En esta sección veremos tareas sencillas de reconocimiento de entidades. Para ello usaremos tres librerías nuevas, una de las cuales (openNLP
) es una interfaz a una librería en Java. Usaremos además modelos pre-entrenados que no están disponibles en CRAN (depbido debido a la política del repositorio):
library(NLP)
library(openNLP)
# install.packages("openNLPmodels.en",
# repos="http://datacube.wu.ac.at/",
# type="source")
Vamos a hacer una tarea sencilla de separar un texto en oraciones y en palabras. En concreto empezaremos con uno de los textos que estudiaremos en la siguiente sección
text <- readLines("./dta/senate-releases/Biden/01Mar2006Biden9.txt")
y lo transformaremos en un formato más adecuado para trabajar con NLP
:
text <- as.String(text)
Las funciones en openNLP
funcionan siempre del mismo modo. En primer lugar, inicializamos un anotador y después aplicamos ese anotador a un texto. Por ejemplo, podemos empezar por anotar palabras y oraciones utilizando las funciones:
word_ann <- Maxent_Word_Token_Annotator()
sent_ann <- Maxent_Sent_Token_Annotator()
y a continuación anotamos el texto aplicando estos dos objetos al texto que queremos analizar:
annotated_text <- annotate(text, list(sent_ann, word_ann))
Lo que obtenemos es un objeto de clase
class(annotated_text)
que en realidad se parece mucho a un data.frame
en el que tenemos la lista de oraciones y palabras identificadas por la posición en la que empiezan y en la que acaban. Podemos, por ejemplo, recuperar el total de oraciones:
head(subset(annotated_text, type=="sentence"))
y podemos comprobar el contenido de esta oración
subset(annotated_text, type=='sentence')[[4]]$features
text[subset(annotated_text, type=='sentence')[[4]]]
También podríamos recuperar las palabras que constituyen el texto de modo similar
head(subset(annotated_text, type=="word"))
Quizás sea más fácil explorar los resultados utilizando la función AnnotatedPlainTextDocument
:
adoc <- AnnotatedPlainTextDocument(text, annotated_text)
head(sents(adoc))
head(words(adoc))
Podemos utilizar el mismo proceso para anotar entidades dentro de un texto. Por ejemplo, podemos anotar personas, localizaciones, organizaciones y fechas dentro del documento que ya hemos anotado anteriormente.
person_ann <- Maxent_Entity_Annotator(kind="person")
loc_ann <- Maxent_Entity_Annotator(kind="location")
org_ann <- Maxent_Entity_Annotator(kind="organization")
date_ann <- Maxent_Entity_Annotator(kind="date")
y ahora aplicamos estos nuevos anotadores al texto que ya tokenizamos previamente:
annotated_text <- annotate(text,
list(person_ann, loc_ann, org_ann, date_ann),
annotated_text)
podemos ver que, a la lista de tokens, se han añadido las entidades que el clasificador ha logrado identificar.
subset(annotated_text, type=="entity")$features
y ahora podemos ver las entidades que hemos recuperado
sel <- subset(annotated_text, type=="entity")
cbind(unlist(sel$features), text[sel])
La anotación de POS sigue la misma lógica. Ahora generaremos un nuevo objeto en el que inicializamos el anotador de POS y aplicamos este nuevo anotador a nuestro texto:
pos_ann <- Maxent_POS_Tag_Annotator()
pos_annotated_text <- annotate(text, pos_ann, annotated_text)
Ahora lo que tenemos en la columna de features
es una indiación de la POS de cada una de las palabras
head(subset(pos_annotated_text, type=="word"))
En este caso vemos que, por ejemplo,
text[subset(pos_annotated_text, type=="word")[[1]]]
está etiquetado como JJ
que se corresponde como adjetivo y
text[subset(pos_annotated_text, type=="word")[[2]]]
está etiquetado como NN
que es un sustantivo en singular. La lista completa de abreviaturas es:
Símbolo | Significado |
---|---|
CC | Coordinating conjunction |
CD | Cardinal number |
DT | Determiner |
EX | Existential there |
FW | Foreign word |
IN | Preposition or subordinating conjunction |
JJ | Adjective |
JJR | Adjective, comparative |
JJS | Adjective, superlative |
LS | List item marker |
MD | Modal |
NN | Noun, singular or mass |
NNS | Noun, plural |
NNP | Proper noun, singular |
NNPS | Proper noun, plural |
PDT | Predeterminer |
POS | Possessive ending |
PRP | Personal pronoun |
PRP$ | Possessive pronoun |
RB | Adverb |
RBR | Adverb, comparative |
RBS | Adverb, superlative |
RP | Particle |
SYM | Symbol |
TO | to |
UH | Interjection |
VB | Verb, base form |
VBD | Verb, past tense |
VBG | Verb, gerund or present participle |
VBN | Verb, past participle |
VBP | Verb, non3rd person singular present |
VBZ | Verb, 3rd person singular present |
WDT | Whdeterminer |
WP | Whpronoun |
WP$ | Possessive whpronoun |
WRB | Whadverb |
Hemos visto que las etiquetaciones son en realidad el resultado de un modelo estadístico. Podemos pedirle al anotador que nos devuelva las probabilidades asociadas a la etiqueta escogida con el fin de tener información acerca de la incertidumbre.
pos_ann <- Maxent_POS_Tag_Annotator(probs=TRUE)
pos_annotated_text <- annotate(text, pos_ann, annotated_text)
head(subset(pos_annotated_text, type=="word"))
y así comprobamos que la asignación de rushed como adjetivo en realidad tiene una enorme incertidumbre.