Todo es posible: resolver problemas de PNL con espacio





El procesamiento del lenguaje natural es ahora omnipresente: las interfaces de voz y los chatbots se están desarrollando rápidamente, se están desarrollando modelos para procesar grandes datos de texto y la traducción automática continúa desarrollándose.



En este artículo, veremos la biblioteca SpaCy relativamente nueva, que actualmente es una de las soluciones más populares y convenientes para el procesamiento de texto en Python. Su funcionalidad le permite resolver una amplia gama de tareas: desde identificar partes del discurso y extraer entidades nombradas hasta crear sus propios modelos para el análisis.



Para empezar, echemos un vistazo a cómo se procesan los datos en SpaCy. El texto cargado para su procesamiento pasa secuencialmente a través de varios componentes de procesamiento y se guarda como una instancia del objeto Doc:







Doc es la estructura de datos central en SpaCy, es en ella donde se almacenan las secuencias de palabras o, como también se les llama, tokens. Dentro del objeto Doc, se pueden distinguir otros dos tipos de objetos: Token y Span. Token es un enlace a palabras individuales de un documento, y Span es un enlace a una secuencia de varias palabras (puede crearlas usted mismo):







Otra estructura de datos importante es el objeto Vocab, que almacena un conjunto de tablas de búsqueda comunes a todos los documentos. Esto ahorra memoria y proporciona una única fuente de información para todos los documentos procesados.



Los tokens de documento se conectan al objeto Vocab a través de un hash, mediante el cual puede obtener las formas iniciales de las palabras u otros atributos léxicos de los tokens:







ahora sabemos cómo se organiza el almacenamiento y procesamiento de datos en la biblioteca SpaCy. ¿Cómo aprovechar las oportunidades que brinda? Echemos un vistazo a las operaciones que se pueden utilizar para procesar texto en secuencia.



1. Operaciones básicas



Antes de comenzar a trabajar con texto, debe importar el modelo de idioma. Para el idioma ruso, existe un modelo oficial de SpaCy que admite la tokenización (división de texto en tokens separados) y una serie de otras operaciones básicas:



from spacy.lang.ru import Russian
      
      





Después de importar y crear una instancia del modelo de idioma, puede comenzar a procesar el texto. Para hacer esto, solo necesita pasar el texto a la instancia creada:



nlp = Russian()
doc = nlp("     ,   .")
      
      





Trabajar con el objeto Doc resultante es muy similar a trabajar con listas: puede acceder al token deseado por índice o hacer cortes de varios tokens. Y para obtener el texto de un token o segmento, puede usar el atributo de texto:



token = doc[0]
print(token.text)

span = doc[3:6]
print(span.text)


  
      
      





Para obtener más información sobre el tipo de información que contiene el token, se pueden utilizar los siguientes atributos:



  1. is_alpha: comprueba si el token contiene solo caracteres alfabéticos
  2. is_punct: comprueba si el token es un signo de puntuación
  3. like_num: comprueba si un token es un número


print("is_alpha:    ", [token.is_alpha for token in doc])
print("is_punct:    ", [token.is_punct for token in doc])
print("like_num:    ", [token.like_num for token in doc])

      
      





Consideremos otro ejemplo, donde todos los tokens que preceden al punto se muestran en la pantalla. Para obtener este resultado, al iterar sobre tokens, verifique el siguiente token usando el atributo token.i:



for token in doc:
    if token.i+1 < len(doc):
        next_token = doc[token.i+1]
        if next_token.text == ".":
            print(token.text)


      
      





2. Operaciones con sintaxis



Para operaciones de procesamiento de texto más complejas, se utilizan otros modelos. Están especialmente capacitados para tareas relacionadas con la sintaxis, extracción de entidades con nombre y trabajo con significados de palabras. Por ejemplo, para el inglés, hay 3 modelos oficiales que difieren en tamaño. Para el idioma ruso, por el momento, el modelo oficial aún no ha sido entrenado, pero ya existe un modelo ru2 de fuentes de terceros que puede trabajar con la sintaxis.



Al final de este artículo, discutiremos cómo crear sus propios modelos o entrenar adicionalmente los existentes para que funcionen mejor para tareas específicas.



Para ilustrar completamente las capacidades de SpaCy, usaremos los modelos en inglés en este artículo. Configuremos un pequeño modelo en_core_web_sm, que es excelente para demostrar las posibilidades. Para instalarlo en la línea de comando, debe escribir:



python -m spacy download en_core_web_sm
      
      





Usando este modelo, podemos obtener para cada uno de los tokens una parte del discurso, un rol en una oración y un token del que depende:



import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("New Apple MacBook set launch tomorrow")

for token in doc:
    token_text = token.text
    token_pos = token.pos_
    token_dep = token.dep_
    token_head = token.head.text
    print(f"{token_text:<12}{token_pos:<10}" \
          f"{token_dep:<10}{token_head:<12}")
      
      





New         PROPN     compound  MacBook     
Apple       PROPN     compound  MacBook     
MacBook     PROPN     nsubj     set         
set         VERB      ROOT      set         
to          PART      aux       launch      
launch      VERB      xcomp     set         
tomorrow    NOUN      npadvmod  launch 
      
      





De lejos, la mejor manera de ver las dependencias no es leer los datos de texto, sino construir un árbol de sintaxis. La función de desplazamiento puede ayudar con esto, que solo necesita para transferir el documento:



from spacy import displacy
displacy.render(doc, style='dep', jupyter=True)
      
      





Como resultado de ejecutar el código, obtenemos un árbol en el que se encuentra toda la información sintáctica sobre la oración:







Para descifrar los nombres de las etiquetas, puede usar las funciones de explicación:



print(spacy.explain("aux"))
print(spacy.explain("PROPN"))
auxiliary
proper noun
      
      





Aquí, las abreviaturas se muestran en la pantalla, de lo cual podemos aprender que aux significa una partícula auxiliar (auxiliar) y PROPN significa un nombre propio.



SpaCy también implementa la capacidad de encontrar la forma inicial de una palabra para cualquiera de los tokens (-PRON- se usa para pronombres):



import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("I saw a movie yesterday")
print(' '.join([token.lemma_ for token in doc]))

'-PRON- see a movie yesterday'
      
      





3. Destacando entidades nombradas



A menudo, para trabajar con texto, es necesario resaltar las entidades mencionadas en el texto. El atributo doc.ents se usa para enumerar las entidades nombradas en el documento, y el atributo ent.label_ se usa para obtener la etiqueta de esta entidad:



import spacy

nlp = spacy.load("en_core_web_sm")
doc = nlp("Apple is looking at buying U.K. startup for 1$ billion")
for ent in doc.ents:
    print(ent.text, ent.label_)


Apple ORG
U.K. GPE
1$ billion MONEY
      
      





También puede utilizar el atributo de explicación aquí para averiguar la decodificación de etiquetas de entidad con nombre:



print(spacy.explain("GPE"))

      
      





Países, ciudades, estados

Y la función de desplazamiento le ayudará a visualizar las listas de entidades directamente en el texto:



desde spacy import desplazar

desplazar.render (doc, style = 'ent', jupyter = True)







4. Cree sus propias plantillas para la búsqueda de texto



El módulo spaCy contiene una herramienta muy útil que le permite crear sus propias plantillas de búsqueda de texto. En particular, puede buscar palabras de una determinada parte del discurso, todas las formas de una palabra por su forma inicial, verificar el tipo de contenido en el token. Aquí hay una lista de los parámetros principales:







Intentemos crear nuestra propia plantilla para reconocer una secuencia de tokens. Digamos que queremos extraer de las líneas de texto sobre las Copas Mundiales de Críquet de la FIFA o ICC con la mención del año:



import spacy
from spacy.matcher import Matcher

nlp = spacy.load("en_core_web_sm")
matcher = Matcher(nlp.vocab)
pattern = [
    {"IS_DIGIT": True}, 
    {"LOWER": {"REGEX": "(fifa|icc)"}},
    {"LOWER": "cricket", "OP": "?"},
    {"LOWER": "world"},
    {"LOWER": "cup"}
]
matcher.add("fifa_pattern", None, pattern)
doc = nlp("2018 ICC Cricket World Cup: Afghanistan won!")
matches = matcher(doc)
for match_id, start, end in matches:
    matched_span = doc[start:end]
    print(matched_span)
      
      





2018 ICC Cricket World Cup
      
      





Entonces, en este bloque de código, importamos un objeto Matcher especial que nos permite almacenar un conjunto de plantillas personalizadas. Después de inicializarlo, creamos una plantilla donde especificamos la secuencia de tokens. Tenga en cuenta que usamos expresiones regulares para elegir entre ICC y FIFA, y para el token de Cricket, una clave que indica la presencia opcional de este token.



Después de crear una plantilla, debe agregarla al conjunto mediante la función agregar, especificando un ID de plantilla único en los parámetros. Los resultados de la búsqueda se presentan en forma de lista de tuplas. Cada una de las tuplas consta del ID de coincidencia y los índices de inicio y finalización del segmento que se encuentra en el documento.



5. Determinación de la proximidad semántica



Dos palabras pueden tener un significado muy similar, pero ¿cómo se mide su cercanía? En tales tareas, los vectores semánticos pueden acudir al rescate. Si dos palabras o expresiones verbales son similares, entonces sus vectores estarán próximos entre sí.



Calcular la proximidad semántica de los vectores en SpaCy no es difícil si el modelo de lenguaje ha sido entrenado para resolver tales problemas. El resultado depende en gran medida del tamaño del modelo, por lo que para esta tarea tomemos un modelo más grande:



import spacy
      
      





nlp = spacy.load("en_core_web_md")
doc1 = nlp("I like burgers")
doc2 = nlp("I like pizza")
print(doc1.similarity(doc2))
      
      





0.9244169833828932
      
      





El valor puede variar de cero a uno: cuanto más cerca de uno, mayor es la similitud. En el ejemplo anterior, comparamos dos documentos; sin embargo, puede comparar tokens y segmentos individuales de la misma manera.



La evaluación de la proximidad semántica puede resultar útil para resolver muchos problemas. Por ejemplo, puede utilizarlo para configurar un sistema de recomendación para que ofrezca al usuario textos similares en función de lo que ya ha leído.



Es importante recordar que la afinidad semántica es muy subjetiva y siempre depende del contexto de la tarea. Por ejemplo, las frases “Amo a los perros” y “Odio a los perros” son similares, ya que ambas expresan opiniones sobre los perros, pero al mismo tiempo difieren mucho en el estado de ánimo. En algunos casos, tendrá que entrenar modelos de lenguaje adicionales para que los resultados se correlacionen con el contexto de su problema.



6. Creación de sus propios componentes de procesamiento



El módulo SpaCy admite una serie de componentes integrados (tokenizador, resaltado de entidad con nombre), pero también le permite definir sus propios componentes. De hecho, los componentes se denominan funciones secuencialmente que toman un documento como entrada, lo modifican y lo devuelven. Se pueden agregar nuevos componentes usando el atributo add_pipe:



import spacy

def length_component(doc):
    doc_length = len(doc)
    print(f"This document is {doc_length} tokens long.")
    return doc

nlp = spacy.load("en_core_web_sm")
nlp.add_pipe(length_component, first=True)
print(nlp.pipe_names)
doc = nlp("This is a sentence.")
      
      





['length_component', 'tagger', 'parser', 'ner']
This document is 5 tokens long.
      
      





En el ejemplo anterior, creamos y agregamos nuestra propia función que muestra la cantidad de tokens en el documento procesado. Usando el atributo nlp.pipe_names, obtuvimos el orden de ejecución de los componentes: como podemos ver, el componente creado es el primero en la lista. Puede utilizar las siguientes opciones para especificar dónde agregar el nuevo componente: La







capacidad de agregar componentes personalizados es una herramienta muy poderosa para optimizar el procesamiento de acuerdo con sus necesidades.



7. Modelos de capacitación y actualización



Los modelos estadísticos hacen predicciones basadas en los ejemplos en los que fueron entrenados. Como regla general, la precisión de dichos modelos se puede mejorar capacitándolos adicionalmente con ejemplos específicos de su tarea. La formación adicional en modelos existentes puede ser muy útil (por ejemplo, para el reconocimiento o análisis de entidades con nombre).



Se pueden agregar ejemplos de capacitación adicionales directamente en la interfaz de SpaCy. Los ejemplos en sí deben constar de datos de texto y una lista de etiquetas para este ejemplo en las que se entrenará el modelo.



Como ilustración, considere actualizar el modelo para recuperar entidades con nombre. Para actualizar dicho modelo, debe pasarle muchos ejemplos que contengan texto, una indicación de las entidades y su clase. En los ejemplos, debe utilizar cláusulas completas, porque al extraer entidades, el modelo se basa en gran medida en el contexto de la cláusula. Es muy importante entrenar completamente el modelo para que pueda reconocer tokens que no son entidades.



Por ejemplo:



("What to expect at Apple's 10 November event", {"entities": [(18,23,"COMPANY")]})
("Is that apple pie I smell?", {"entities": []})
      
      





En el primer ejemplo se menciona una empresa: para la capacitación, resaltamos los puestos donde comienza y termina su nombre, y luego colocamos nuestra etiqueta que esta entidad es una empresa. En el segundo ejemplo, estamos hablando de una fruta, por lo que no hay entidades.



Los datos para entrenar el modelo suelen ser marcados por personas, pero este trabajo se puede automatizar ligeramente utilizando las propias plantillas de búsqueda en SpaCy o programas de marcado especializados (por ejemplo, Prodigy ).



Una vez preparados los ejemplos, puede proceder directamente a entrenar el modelo. Para que el modelo se entrene de manera eficaz, debe ejecutar una serie de entrenamientos múltiples. Con cada entrenamiento, el modelo optimizará los pesos de ciertos parámetros. Los modelos en SpaCy utilizan la técnica de descenso de gradiente estocástico, por lo que es una buena idea mezclar los ejemplos con cada entrenamiento y también enviarlos en pequeñas porciones (paquetes). Esto aumentará la confiabilidad de las estimaciones de gradiente.







import spacy
import random
from spacy.lang.en import English

TRAINING_DATA = [
    ("What to expect at Apple's 10 November event", 
    {"entities": [(18,23,"COMPANY")]})
    #  ...
]

nlp = English()

for i in range(10):
    random.shuffle(TRAINING_DATA)
    for batch in spacy.util.minibatch(TRAINING_DATA):
        texts = [text for text, annotation in batch]
        annotations = [annotation for text, annotation in batch]
        nlp.update(texts, annotations)
        
nlp.to_disk("model")
      
      





En el ejemplo anterior, el bucle constaba de 10 entrenamientos. Después de completar el entrenamiento, el modelo se guardó en el disco en la carpeta del modelo.



Para los casos en los que es necesario no solo actualizar, sino crear un nuevo modelo, se requieren una serie de operaciones antes de comenzar el entrenamiento.



Considere el proceso de creación de un nuevo modelo para resaltar entidades con nombre:



nlp = spacy.blank("en")
ner = nlp.create_pipe("ner")
nlp.add_pipe(ner)
ner.add_label("COMPANY")
nlp.begin_training()
      
      





Primero, creamos un modelo vacío usando la función spacy.blank ("en"). El modelo contiene solo datos de idioma y reglas de tokenización. Luego agregamos un componente ner que es responsable de resaltar las entidades nombradas, y usando el atributo add_label, agregamos etiquetas para las entidades. Luego usamos la función nlp.begin_training () para inicializar el modelo para el entrenamiento con una distribución aleatoria de pesos. Bueno, entonces bastará con entrenar el modelo, como se muestra en el ejemplo anterior.



All Articles