Python para PNL: creación de un modelo de bolsa de palabras desde cero

Este es el artículo número 13 de mi serie de artículos sobre Python para PNL. En el artículo anterior, vimos cómo crear un chatbot simple basado en reglas que usa coseno...

Este es el artículo número 13 de mi serie de artículos sobre Python para PNL. En el Artículo anterior, vimos cómo crear un chatbot simple basado en reglas que usa [similitud de coseno](https:/ Cosine_similarity between the [TF-FDI] (https://en.wikipedia.org/wiki/Tf%E2%80%93idf) vectores de las palabras en el corpus y la entrada del usuario, para generar una respuesta. El modelo TF-IDF se utilizó básicamente para convertir palabras en números.

En este artículo, estudiaremos otro modelo muy útil que convierte texto en números, es decir, la bolsa de palabras (BOW).

Dado que la mayoría de los algoritmos estadísticos, por ejemplo, el aprendizaje automático y las técnicas de aprendizaje profundo, funcionan con datos numéricos, tenemos que convertir el texto en números. Existen varios enfoques al respecto. Sin embargo, los más famosos son Bag of Words, TF-IDF y word2vec. Aunque existen varias bibliotecas, como Scikit-Learn y NLTK, que pueden implementar estas técnicas en una línea de código, es importante comprender el principio de funcionamiento detrás de estas técnicas de incrustación de palabras. La mejor manera de hacerlo es implementar estas técnicas desde cero en Python y esto es lo que vamos a hacer hoy.

En este artículo, veremos cómo implementar el enfoque Bag of Words desde cero en Python. En el próximo artículo, veremos cómo implementar el enfoque TF-IDF desde cero en Python.

Antes de codificar, primero veamos la teoría detrás del enfoque de la bolsa de palabras.

Teoría detrás del enfoque de la bolsa de palabras {#teoría detrás del enfoque de la bolsa de palabras}

Para comprender el enfoque de la bolsa de palabras, primero comencemos con la ayuda de un ejemplo.

Supongamos que tenemos un corpus con tres oraciones:

  • "Me gusta jugar al fútbol"
  • "Saliste a jugar al tenis"
  • "Juan y yo jugamos al tenis"

Ahora bien, si tenemos que realizar una clasificación de texto, o cualquier otra tarea, sobre los datos anteriores utilizando técnicas estadísticas, no podemos hacerlo ya que las técnicas estadísticas funcionan solo con números. Por lo tanto, necesitamos convertir estas oraciones en números.

Paso 1: tokenice las oraciones

El primer paso en este sentido es convertir las oraciones de nuestro corpus en tokens o palabras individuales. Mira la tabla a continuación:


Oración 1 Oración 2 Oración 3 yo hice juan como tu y ir yo jugar afuera jugar fútbol a tenis play
tennis


Paso 2: Cree un diccionario de frecuencia de palabras

El siguiente paso es crear un diccionario que contenga todas las palabras de nuestro corpus como claves y la frecuencia de aparición de las palabras como valores. En otras palabras, necesitamos crear un histograma de las palabras en nuestro corpus. Mira la siguiente tabla:


Frecuencia de palabra yo 2 como 1 a 2 jugar 3 fútbol 1 Hizo 1 tu 1 ir 1 afuera 1 tenis 2 Juan 1 y 1


En la tabla anterior, puede ver cada palabra de nuestro corpus junto con su frecuencia de aparición. Por ejemplo, puedes ver que dado que la palabra jugar aparece tres veces en el corpus (una vez en cada oración), su frecuencia es 3.

En nuestro corpus, solo teníamos tres oraciones, por lo tanto, es fácil para nosotros crear un diccionario que contenga todas las palabras. En los escenarios del mundo real, habrá millones de palabras en el diccionario. Algunas de las palabras tendrán una frecuencia muy pequeña. Las palabras con una frecuencia muy pequeña no son muy útiles, por lo que se eliminan. Una forma de eliminar las palabras con menos frecuencia es ordenar el diccionario de frecuencia de palabras en orden decreciente de frecuencia y luego filtrar las palabras que tienen una frecuencia superior a un cierto umbral.

Ordenemos nuestro diccionario de frecuencia de palabras:


Frecuencia de palabra jugar 3 tenis 2 a 2 yo 2 fútbol 1 Hizo 1 tu 1 ir 1 afuera 1 como 1 Juan 1 y 1


Paso 3: creación del modelo de bolsa de palabras

Para crear el modelo de bolsa de palabras, necesitamos crear una matriz donde las columnas correspondan a las palabras más frecuentes en nuestro diccionario donde las filas correspondan al documento oa las oraciones.

Supongamos que filtramos las 8 palabras más frecuentes de nuestro diccionario. Entonces la matriz de frecuencia del documento se verá así:


           Play   Tennis   To   I   Football   Did   You   go

Oración 1 1 0 1 1 1 0 0 0 Oración 2 1 1 1 0 0 1 1 1 Oración 3 1 1 0 1 0 0 0 0


Es importante entender cómo se crea la matriz anterior. En la matriz anterior, la primera fila corresponde a la primera oración. En la primera, la palabra "jugar" aparece una vez, por lo que agregamos 1 en la primera columna. La palabra en la segunda columna es "Tenis", no aparece en la primera oración, por lo que agregamos un 0 en la segunda columna para la oración 1. De manera similar, en la segunda oración, ambas palabras "Jugar " y "Tenis" ocurren una vez, por lo que agregamos 1 en las dos primeras columnas. Sin embargo, en la quinta columna, agregamos un 0, ya que la palabra "Fútbol" no aparece en la segunda oración. De esta forma, todas las celdas de la matriz anterior se rellenan con 0 o 1, según la aparición de la palabra. La matriz final corresponde al modelo de la bolsa de palabras.

En cada fila, puede ver la representación numérica de la oración correspondiente. Por ejemplo, la primera fila muestra la representación numérica de Oración 1. Esta representación numérica ahora se puede utilizar como entrada para los modelos estadísticos.

Basta de teoría, implementemos nuestro propio modelo de bolsa de palabras desde cero.

Modelo de bolsa de palabras en Python

Lo primero que necesitamos para crear nuestro modelo Bag of Words es un conjunto de datos. En la sección anterior, creamos manualmente un modelo de bolsa de palabras con tres oraciones. Sin embargo, los conjuntos de datos del mundo real son enormes, con millones de palabras. La mejor manera de encontrar un corpus aleatorio es Wikipedia.

En el primer paso, rasparemos el artículo de Wikipedia sobre [Procesamiento del lenguaje natural] (https://en.wikipedia.org/wiki/Natural_language_processing). Pero primero, importemos las bibliotecas requeridas:

1
2
3
4
5
6
7
8
import nltk  
import numpy as np  
import random  
import string

import bs4 as bs  
import urllib.request  
import re  

Como hicimos en el artículo anterior, usaremos la biblioteca Hermosasopa4 para analizar los datos de Wikipedia. Además, se utilizará la Biblioteca de expresiones regulares de Python, re, para algunas tareas de preprocesamiento sobre el texto.

A continuación, debemos raspar el artículo de Wikipedia sobre el procesamiento del lenguaje natural.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
raw_html = urllib.request.urlopen('https://en.wikipedia.org/wiki/Natural_language_processing')  
raw_html = raw_html.read()

article_html = bs.BeautifulSoup(raw_html, 'lxml')

article_paragraphs = article_html.find_all('p')

article_text = ''

for para in article_paragraphs:  
    article_text += para.text

En el script anterior, importamos el código HTML sin formato para el artículo de Wikipedia. Desde el HTML sin formato, filtramos el texto dentro del texto del párrafo. Finalmente, creamos un corpus completo concatenando todos los párrafos.

El siguiente paso es dividir el corpus en oraciones individuales. Para ello, utilizaremos la función sent_tokenize de la biblioteca NLTK.

1
corpus = nltk.sent_tokenize(article_text)

Nuestro texto contiene signos de puntuación. No queremos que las puntuaciones formen parte de nuestro diccionario de frecuencia de palabras. En el siguiente script, primero convertimos nuestro texto a minúsculas y luego eliminamos la puntuación de nuestro texto. Quitar la puntuación puede resultar en múltiples espacios vacíos. Eliminaremos los espacios vacíos del texto usando expresiones regulares.

Mira el siguiente guión:

1
2
3
4
for i in range(len(corpus )):
    corpus [i] = corpus [i].lower()
    corpus [i] = re.sub(r'\W',' ',corpus [i])
    corpus [i] = re.sub(r'\s+',' ',corpus [i])

En el script anterior, iteramos a través de cada oración en el corpus, convertimos la oración a minúsculas y luego eliminamos la puntuación y los espacios vacíos del texto.

Averigüemos el número de oraciones en nuestro corpus.

1
print(len(corpus))

La salida muestra 49.

Imprimamos una oración de nuestro corpus:

1
print(corpus[30])

Producción:

1
in the 2010s representation learning and deep neural network style machine learning methods became widespread in natural language processing due in part to a flurry of results showing that such techniques 4 5 can achieve state of the art results in many natural language tasks for example in language modeling 6 parsing 7 8 and many others 

Puede ver que el texto no contiene ningún carácter especial o múltiples espacios vacíos.

Ahora tenemos nuestro propio corpus. El siguiente paso es tokenizar las oraciones del corpus y crear un diccionario que contenga palabras y sus correspondientes frecuencias en el corpus. Mira el siguiente guión:

1
2
3
4
5
6
7
8
wordfreq = {}
for sentence in corpus:
    tokens = nltk.word_tokenize(sentence)
    for token in tokens:
        if token not in wordfreq.keys():
            wordfreq[token] = 1
        else:
            wordfreq[token] += 1

En el script anterior creamos un diccionario llamado wordfreq. A continuación, iteramos a través de cada oración en el corpus. La oración se tokeniza en palabras. A continuación, iteramos a través de cada palabra de la oración. Si la palabra no existe en el diccionario wordfreq, agregaremos la palabra como clave y estableceremos el valor de la palabra en 1. De lo contrario, si la palabra ya existe en el diccionario, simplemente incrementaremos el conteo de llaves por 1.

Si está ejecutando lo anterior en el editor espía como yo, puede ir al explorador de variables a la derecha y hacer clic en la variable wordfreq. Debería ver un diccionario como este:

{.img-responsive}

Puede ver las palabras en la columna "Clave" y su frecuencia de aparición en la columna "Valor".

Como dije en la sección de teoría, dependiendo de la tarea en cuestión, no todas las palabras son útiles. En grandes corpus, puedes tener millones de palabras. Podemos filtrar las palabras que aparecen con más frecuencia. Nuestro corpus tiene 535 palabras en total. Filtremos hasta las 200 palabras más frecuentes. Para hacerlo, podemos hacer uso de la biblioteca heap de Python.

Mira el siguiente guión:

1
2
import heapq
most_freq = heapq.nlargest(200, wordfreq, key=wordfreq.get)

Ahora nuestra lista most_freq contiene las 200 palabras más frecuentes junto con su frecuencia de ocurrencia.

El paso final es convertir las oraciones de nuestro corpus en su representación vectorial correspondiente. La idea es sencilla, para cada palabra en el diccionario most_freq si la palabra existe en la oración, se agregará un 1 para la palabra, de lo contrario se agregará 0.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
sentence_vectors = []
for sentence in corpus:
    sentence_tokens = nltk.word_tokenize(sentence)
    sent_vec = []
    for token in most_freq:
        if token in sentence_tokens:
            sent_vec.append(1)
        else:
            sent_vec.append(0)
    sentence_vectors.append(sent_vec)

En el script anterior, creamos una lista vacía sentence_vectors que almacenará vectores para todas las oraciones en el corpus. A continuación, iteramos a través de cada oración en el corpus y creamos una lista vacía sent_vec para las oraciones individuales. Del mismo modo, también tokenizamos la oración. A continuación, iteramos a través de cada palabra en la lista most_freq y verificamos si la palabra existe en los tokens de la oración. Si la palabra es parte de la oración, se agrega 1 al vector de oración individual sent_vec, de lo contrario, se agrega 0. Finalmente, el vector de oración se agrega a la lista sentence_vectors que contiene vectores para todas las oraciones. Básicamente, este sentence_vectors es nuestro modelo de bolsa de palabras.

Sin embargo, el modelo de la bolsa de palabras que vimos en la sección de teoría tenía la forma de una matriz. Nuestro modelo tiene la forma de una lista de listas. Podemos convertir nuestro modelo en forma de matriz usando este script:

1
sentence_vectors = np.asarray(sentence_vectors)

Básicamente, en el siguiente script, convertimos nuestra lista en una matriz numpy bidimensional usando la función asarray. Ahora, si abre la variable sentence_vectors en el explorador de variables del editor de Spyder, debería ver la siguiente matriz:

{.img-responsive}

Puede ver el modelo Bolsa de palabras que contiene 0 y 1.

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 modelo Bag of Words es uno de los tres enfoques de incrustación de palabras más utilizados, siendo TF-IDF y Word2Vec los otros dos.

En este artículo, vimos cómo implementar el enfoque Bag of Words desde cero en Python. La teoría del enfoque se ha explicado junto con el código práctico para implementar el enfoque. En el próximo artículo, veremos cómo implementar el enfoque TF-IDF desde cero en Python.