Python para NLP: etiquetado de partes del discurso y reconocimiento de entidades con nombre

Este es el cuarto artículo de mi serie de artículos sobre Python para PNL. En mi artículo anterior, expliqué cómo se puede usar la biblioteca spaCy para realizar tareas como...

Este es el cuarto artículo de mi serie de artículos sobre Python para PNL. En mi Artículo anterior, expliqué cómo se puede usar la biblioteca spacey para realizar tareas como el vocabulario y la combinación de frases.

En este artículo, estudiaremos en detalle el etiquetado de partes del discurso y el reconocimiento de entidades nombradas. Veremos cómo se puede utilizar la biblioteca spaCy para realizar estas dos tareas.

Etiquetado de partes del habla (POS)

El etiquetado de partes del discurso simplemente se refiere a la asignación de partes del discurso a palabras individuales en una oración, lo que significa que, a diferencia de la coincidencia de frases, que se realiza a nivel de oración o de varias palabras, el etiquetado de partes del discurso se realiza a nivel de token.

Tomemos un ejemplo muy simple de etiquetado de partes del discurso.

1
2
import spacy
sp = spacy.load('en_core_web_sm')

Como de costumbre, en la secuencia de comandos anterior importamos el modelo básico en inglés spaCy. A continuación, debemos crear un documento spaCy que usaremos para realizar el etiquetado de partes del discurso.

1
sen = sp(u"I like to play football. I hated it in my childhood though")

El objeto de documento spaCy tiene varios atributos que se pueden usar para realizar una variedad de tareas. Por ejemplo, para imprimir el texto del documento, se utiliza el atributo texto. De manera similar, el atributo pos_ devuelve la etiqueta POS de grano grueso. Para obtener etiquetas POS detalladas, podríamos usar el atributo tag_. Y finalmente, para obtener la explicación de una etiqueta, podemos usar el método spacy.explain() y pasarle el nombre de la etiqueta.

Veamos esto en acción:

1
print(sen.text)

El script anterior simplemente imprime el texto de la oración. La salida se ve así:

1
I like to play football. I hated it in my childhood though

A continuación, veamos el atributo pos_. Imprimiremos la etiqueta POS de la palabra "hated", que en realidad es el séptimo token en la oración.

1
print(sen[7].pos_)

Producción:

1
VERB

Puede ver que la etiqueta POS devuelta para "hated" es un "VERB" ya que "hated" es un verbo.

Ahora imprimamos la etiqueta POS detallada para la palabra "odiado".

1
print(sen[7].tag_)

Producción:

1
VBD

Para ver qué significa VBD, podemos usar el método spacy.explain() como se muestra a continuación:

1
print(spacy.explain(sen[7].tag_))

Producción:

1
verb, past tense

El resultado muestra que VBD es un verbo en tiempo pasado.

Imprimamos el texto, las etiquetas POS de grano grueso, las etiquetas POS de grano fino y la explicación de las etiquetas para todas las palabras de la oración.

1
2
for word in sen:
    print(f'{word.text:{12}} {word.pos_:{10}} {word.tag_:{8}} {spacy.explain(word.tag_)}')

En la secuencia de comandos anterior, mejoramos la legibilidad y el formato al agregar 12 espacios entre el texto y la etiqueta de POS de grano grueso y luego otros 10 espacios entre las etiquetas de POS de grano grueso y las etiquetas de POS de grano fino.

Producción:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
I            PRON       PRP      pronoun, personal
like         VERB       VBP      verb, non-3rd person singular present
to           PART       TO       infinitival to
play         VERB       VB       verb, base form
football     NOUN       NN       noun, singular or mass
.            PUNCT      .        punctuation mark, sentence closer
I            PRON       PRP      pronoun, personal
hated        VERB       VBD      verb, past tense
it           PRON       PRP      pronoun, personal
in           ADP        IN       conjunction, subordinating or preposition
my           ADJ        PRP$     pronoun, possessive
childhood    NOUN       NN       noun, singular or mass
though       ADP        IN       conjunction, subordinating or preposition

Una lista completa de etiquetas para las partes del discurso y las etiquetas detalladas, junto con su explicación, está disponible en la documentación oficial de spaCy.

¿Por qué es útil el etiquetado de POS? {#por qué el postetiquetado es útil}

El etiquetado de POS puede ser realmente útil, especialmente si tiene palabras o tokens que pueden tener varias etiquetas de POS. Por ejemplo, la palabra "google" se puede usar como sustantivo y como verbo, según el contexto. Al procesar el lenguaje natural, es importante identificar esta diferencia. Afortunadamente, la biblioteca spaCy viene preconstruida con algoritmos de aprendizaje automático que, según el contexto (las palabras circundantes), es capaz de devolver la etiqueta POS correcta para la palabra.

Veamos esto en acción. Ejecute el siguiente script:

1
2
3
4
sen = sp(u'Can you google it?')
word = sen[2]

print(f'{word.text:{12}} {word.pos_:{10}} {word.tag_:{8}} {spacy.explain(word.tag_)}')

En el script anterior, creamos un documento spaCy con el texto "¿Puedes buscarlo en Google?". Aquí, la palabra "google" se usa como verbo. A continuación, imprimimos la etiqueta POS para la palabra "google" junto con la explicación de la etiqueta. La salida se ve así:

1
google       VERB       VB       verb, base form

En el resultado, puede ver que la palabra "google" se identificó correctamente como verbo.

Veamos ahora otro ejemplo:

1
2
3
4
sen = sp(u'Can you search it on google?')
word = sen[5]

print(f'{word.text:{12}} {word.pos_:{10}} {word.tag_:{8}} {spacy.explain(word.tag_)}')

Aquí, en el script anterior, la palabra "google" se usa como sustantivo, como se muestra en el resultado:

1
google       PROPN      NNP      noun, proper singular

Encontrar el número de etiquetas POS

Puede encontrar el número de ocurrencias de cada etiqueta POS llamando a count_by en el objeto del documento spaCy. El método toma spacy.attrs.POS como valor de parámetro.

1
2
3
4
sen = sp(u"I like to play football. I hated it in my childhood though")

num_pos = sen.count_by(spacy.attrs.POS)
num_pos

Producción:

1
{96: 1, 99: 3, 84: 2, 83: 1, 91: 2, 93: 1, 94: 3}

En la salida, puede ver la identificación de las etiquetas POS junto con sus frecuencias de aparición. El texto de la etiqueta POS se puede mostrar pasando la ID de la etiqueta al vocabulario del documento spaCy real.

1
2
for k,v in sorted(num_pos.items()):
    print(f'{k}. {sen.vocab[k].text:{8}}: {v}')

Ahora, en la salida, verá la ID, el texto y la frecuencia de cada etiqueta como se muestra a continuación:

1
2
3
4
5
6
7
83. ADJ     : 1
84. ADP     : 2
91. NOUN    : 2
93. PART    : 1
94. PRON    : 3
96. PUNCT   : 1
99. VERB    : 3

Visualización de etiquetas de partes del habla

Visualizar etiquetas POS de forma gráfica es extremadamente fácil. El módulo displacy de la biblioteca spacy se usa para este propósito. Para visualizar las etiquetas POS dentro del cuaderno Jupyter, debe llamar al método render desde el módulo displacy y pasarle el documento espacioso, el estilo de la visualización y establecer el atributo jupyter en True como mostrado a continuación:

1
2
3
4
from spacy import displacy

sen = sp(u"I like to play football. I hated it in my childhood though")
displacy.render(sen, style='dep', jupyter=True, options={'distance': 85})

En el resultado, debería ver el siguiente árbol de dependencias para las etiquetas de POS.

{.img-responsive}

Puede ver claramente la dependencia de cada token en otro junto con la etiqueta POS.

Si desea visualizar las etiquetas POS fuera del cuaderno Jupyter, debe llamar al método servir. El gráfico de las etiquetas POS se imprimirá en formato HTML dentro de su navegador predeterminado. Ejecute el siguiente script:

1
displacy.serve(sen, style='dep', options={'distance': 120})

Una vez que ejecute el script anterior, verá el siguiente mensaje:

1
2
Serving on port 5000...
Using the 'dep' visualizer

Para ver el árbol de dependencias, escriba la siguiente dirección en su navegador: http://127.0.0.1:5000/. Verá el siguiente árbol de dependencia:

{.img-responsive}

Reconocimiento de entidad nombrada

El reconocimiento de entidades nombradas se refiere a la identificación de palabras en una oración como una entidad, p. el nombre de una persona, lugar, organización, etc. Veamos cómo la biblioteca spaCy realiza el reconocimiento de entidades nombradas. Mira el siguiente guión:

1
2
3
4
import spacy
sp = spacy.load('en_core_web_sm')

sen = sp(u'Manchester United is looking to sign Harry Kane for $90 million')

En el script anterior, creamos un documento spaCy simple con algo de texto. Para encontrar la entidad nombrada podemos usar el atributo ents, que devuelve la lista de todas las entidades nombradas en el documento.

1
print(sen.ents)

Producción:

1
(Manchester United, Harry Kane, $90 million)

Puede ver que se identificaron tres entidades nombradas. Para ver los detalles de cada entidad nombrada, puede usar el método text, label y spacy.explain que toma el objeto de la entidad como un parámetro.

1
2
for entity in sen.ents:
    print(entity.text + ' - ' + entity.label_ + ' - ' + str(spacy.explain(entity.label_)))

En el resultado, verá el nombre de la entidad junto con el tipo de entidad y una pequeña descripción de la entidad como se muestra a continuación:

1
2
3
Manchester United - ORG - Companies, agencies, institutions, etc.
Harry Kane - PERSON - People, including fictional
$90 million - MONEY - Monetary values, including unit

Puede ver que "Manchester United" se ha identificado correctamente como una organización, empresa, etc. De manera similar, "Harry Kane" se ha identificado como una persona y, finalmente, "$90 millones" se ha identificado correctamente. como una entidad de tipo Dinero.

Adición de nuevas entidades

También puede agregar nuevas entidades a un documento existente. Por ejemplo, en el siguiente ejemplo, la biblioteca spaCy no identifica a "Nesfruita" como una empresa.

1
2
3
sen = sp(u'Nesfruita is setting up a new company in India')
for entity in sen.ents:
    print(entity.text + ' - ' + entity.label_ + ' - ' + str(spacy.explain(entity.label_)))

Producción:

1
India - GPE - Countries, cities, states

A partir de la salida, puede ver que solo India ha sido identificada como una entidad.

Ahora para agregar "Nesfruita" como una entidad de tipo "ORG" a nuestro documento, necesitamos ejecutar los siguientes pasos:

1
2
3
4
5
from spacy.tokens import Span

ORG = sen.vocab.strings[u'ORG']
new_entity = Span(sen, 0, 1, label=ORG)
sen.ents = list(sen.ents) + [new_entity]

Primero, necesitamos importar la clase Span desde el módulo spacy.tokens. A continuación, necesitamos obtener el valor hash del tipo de entidad ORG de nuestro documento. Después de eso, debemos asignar el valor hash de ORG al lapso. Dado que "Nesfruita" es la primera palabra del documento, el intervalo es 0-1. Finalmente, necesitamos agregar el nuevo intervalo de entidades a la lista de entidades. Ahora, si ejecuta el siguiente script, verá "Nesfruita" en la lista de entidades.

1
2
for entity in sen.ents:
    print(entity.text + ' - ' + entity.label_ + ' - ' + str(spacy.explain(entity.label_)))

La salida del script anterior se ve así:

1
2
Nesfruita - ORG - Companies, agencies, institutions, etc.
India - GPE - Countries, cities, states

Entidades de conteo

En el caso de las etiquetas POS, podríamos contar la frecuencia de cada etiqueta POS en un documento usando un método especial sen.count_by. Sin embargo, para las entidades nombradas, no existe tal método. Podemos contar manualmente la frecuencia de cada tipo de entidad. Supongamos que tenemos el siguiente documento junto con sus entidades:

1
2
3
sen = sp(u'Manchester United is looking to sign Harry Kane for $90 million. David demand 100 Million Dollars')
for entity in sen.ents:
    print(entity.text + ' - ' + entity.label_ + ' - ' + str(spacy.explain(entity.label_)))

Producción:

1
2
3
4
5
Manchester United - ORG - Companies, agencies, institutions, etc.
Harry Kane - PERSON - People, including fictional
$90 million - MONEY - Monetary values, including unit
David - PERSON - People, including fictional
100 Million Dollars - MONEY - Monetary values, including unit

Para contar las entidades de tipo persona en el documento anterior, podemos usar el siguiente script:

1
len([ent for ent in sen.ents if ent.label_=='PERSON'])

En la salida, verá 2 ya que hay 2 entidades de tipo PERSONA en el documento.

Visualización de entidades con nombre

Al igual que las etiquetas POS, también podemos ver entidades con nombre dentro del cuaderno Jupyter, así como en el navegador.

Para ello, utilizaremos de nuevo el objeto displacy. Mira el siguiente ejemplo:

1
2
3
4
from spacy import displacy

sen = sp(u'Manchester United is looking to sign Harry Kane for $90 million. David demand 100 Million Dollars')
displacy.render(sen, style='ent', jupyter=True)

Puede ver que la única diferencia entre visualizar entidades con nombre y etiquetas POS es que aquí, en el caso de entidades con nombre, pasamos ent como valor para el parámetro style. La salida del script anterior se ve así:

{.img-responsive}

Puede ver en la salida que las entidades nombradas se han resaltado en diferentes colores junto con sus tipos de entidad.

También puede filtrar qué tipos de entidades mostrar. Para hacerlo, debe pasar el tipo de las entidades para que se muestren en una lista, que luego se pasa como un valor a la clave ents de un diccionario. Luego, el diccionario se pasa al parámetro options del método render del módulo displacy como se muestra a continuación:

1
2
filter = {'ents': ['ORG']}
displacy.render(sen, style='ent', jupyter=True, options=filter)

En el script anterior, especificamos que solo las entidades de tipo ORG deben mostrarse en la salida. La salida del script anterior se ve así:

{.img-responsive}

Finalmente, también puede mostrar entidades con nombre fuera del cuaderno de Jupyter Notebook. El siguiente script mostrará las entidades nombradas en su navegador predeterminado. Ejecute el siguiente script:

1
displacy.serve(sen, style='ent')

Ahora, si va a la dirección http://127.0.0.1:5000/ en su navegador, debería ver las entidades nombradas.

Yendo más lejos: proyecto de extremo a extremo portátil

¿Tu naturaleza inquisitiva te hace querer ir más allá? Recomendamos consultar nuestro Proyecto guiado: ["Subtítulos de imágenes con CNN y Transformers con Keras"](https://wikihtp.com/courses/image-captioning-with-cnns-and -transformadores-con-keras/#cta){target="_blank"}.

En este proyecto guiado, aprenderá a crear un modelo de subtítulos de imágenes, que acepta una imagen como entrada y produce un subtítulo de texto como salida.

Aprenderás a:

  • Preprocesar texto
  • Vectorizar la entrada de texto fácilmente
  • Trabaje con la API tf.data y cree conjuntos de datos de alto rendimiento
  • Cree Transformers desde cero con TensorFlow/Keras y KerasNLP: la adición horizontal oficial a Keras para crear modelos NLP de última generación
  • Cree arquitecturas híbridas donde la salida de una red se codifica para otra

¿Cómo enmarcamos los subtítulos de las imágenes? La mayoría lo considera un ejemplo de aprendizaje profundo generativo, porque estamos enseñando a una red a generar descripciones. Sin embargo, me gusta verlo como una instancia de traducción automática neuronal: estamos traduciendo las características visuales de una imagen en palabras. A través de la traducción, estamos generando una nueva representación de esa imagen, en lugar de simplemente generar un nuevo significado. Verlo como traducción, y solo por generación de extensión, enfoca la tarea bajo una luz diferente y la hace un poco más intuitiva.

Enmarcar el problema como uno de traducción hace que sea más fácil averiguar qué arquitectura querremos usar. Los transformadores solo de codificador son excelentes para comprender el texto (análisis de opinión, clasificación, etc.) porque los codificadores codifican representaciones significativas. Los modelos de solo decodificador son excelentes para la generación (como GPT-3), ya que los decodificadores pueden inferir representaciones significativas en otra secuencia con el mismo significado. La traducción generalmente se realiza mediante una arquitectura de codificador-decodificador, donde los codificadores codifican una representación significativa de una oración (o imagen, en nuestro caso) y los decodificadores aprenden a convertir esta secuencia en otra representación significativa que es más interpretable para nosotros (como una oración).

Conclusión

El etiquetado de partes del discurso y el reconocimiento de entidades nombradas son cruciales para el éxito de cualquier tarea de NLP. En este artículo, vimos cómo se puede usar la biblioteca spaCy de Python para realizar el etiquetado de POS y el reconocimiento de entidades nombradas con la ayuda de diferentes ejemplos.