Python para PNL: modelado de temas

Este es el sexto artículo de mi serie de artículos sobre Python para PNL. En mi artículo anterior, hablé sobre cómo realizar un análisis de sentimiento de los datos de Twitter u...

Este es el sexto artículo de mi serie de artículos sobre Python para PNL. En mi Artículo anterior, hablé sobre cómo realizar un análisis de sentimiento de los datos de Twitter utilizando la biblioteca Scikit-Learn de Python. . En este artículo, estudiaremos el modelado de temas, que es otra aplicación muy importante de la PNL. Veremos cómo hacer un modelado de temas con Python.

¿Qué es el modelado de temas?

El modelado de temas es una técnica no supervisada que pretende analizar grandes volúmenes de datos de texto agrupando los documentos en grupos. En el caso del modelado de temas, los datos de texto no tienen etiquetas adjuntas. Más bien, el modelado de temas intenta agrupar los documentos en grupos basados ​​en características similares.

Un ejemplo típico de modelado de temas es agrupar una gran cantidad de artículos de periódicos que pertenecen a la misma categoría. En otras palabras, agrupe documentos que tengan el mismo tema. Es importante mencionar aquí que es extremadamente difícil evaluar el desempeño del modelado de temas ya que no hay respuestas correctas. Depende del usuario encontrar características similares entre los documentos de un grupo y asignarle una etiqueta o tema apropiado.

Se utilizan principalmente dos enfoques para el modelado de temas: [asignación de Dirichlet latente] (https://en.wikipedia.org/wiki/Latent_Dirichlet_allocation) y [factorización de matriz no negativa] (https://en.wikipedia.org/wiki /Non- matriz_negativa_factorización). En las siguientes secciones, revisaremos brevemente estos dos enfoques y veremos cómo se pueden aplicar al modelado de temas en Python.

Asignación latente de Dirichlet (LDA)

La LDA se basa en dos supuestos generales:

  • Los documentos que tienen palabras similares suelen tener el mismo tema
  • Los documentos que tienen grupos de palabras que aparecen juntos con frecuencia suelen tener el mismo tema.

Estas suposiciones tienen sentido porque los documentos que tienen el mismo tema, por ejemplo, temas de negocios, tendrán palabras como "economía", "ganancia", "la bolsa", "pérdida", etc. El segundo supuesto establece que si estas palabras aparecen juntas con frecuencia en varios documentos, esos documentos pueden pertenecer a la misma categoría.

Matemáticamente, las dos suposiciones anteriores se pueden representar como:

  • Los documentos son distribuciones de probabilidad sobre temas latentes
  • Los temas son distribuciones de probabilidad sobre palabras

LDA para el modelado de temas en Python

En esta sección, veremos cómo se puede usar Python para implementar LDA para el modelado de temas. El conjunto de datos se puede descargar desde Kaggle.

El conjunto de datos contiene reseñas de usuarios para diferentes productos en la categoría de alimentos. Usaremos LDA para agrupar las opiniones de los usuarios en 5 categorías.

El primer paso, como siempre, es importar el conjunto de datos junto con las bibliotecas requeridas. Ejecute el siguiente script para hacerlo:

1
2
3
4
5
6
import pandas as pd
import numpy as np

reviews_datasets = pd.read_csv(r'E:\Datasets\Reviews.csv')
reviews_datasets = reviews_datasets.head(20000)
reviews_datasets.dropna()

En el script de arriba importamos el conjunto de datos usando el método read_csv de la biblioteca de pandas. El conjunto de datos original contiene alrededor de 500k reseñas. Sin embargo, debido a limitaciones de memoria, realizaré LDA solo en los primeros 20k registros. En el script anterior filtramos las primeras 20k filas y luego eliminamos los valores nulos del conjunto de datos.

A continuación, imprimimos las primeras cinco filas del conjunto de datos usando la función head() para inspeccionar nuestros datos:

1
reviews_datasets.head()

En la salida, verá los siguientes datos:

{.img-responsive}

Aplicaremos LDA en la columna "Texto" ya que contiene las reseñas, el resto de las columnas se ignorarán.

Veamos la revisión número 350.

1
reviews_datasets['Text'][350]

En el resultado, verá el siguiente texto de revisión:

1
'These chocolate covered espresso beans are wonderful!  The chocolate is very dark and rich and the "bean" inside is a very delightful blend of flavors with just enough caffine to really give it a zing.'

Antes de que podamos aplicar LDA, necesitamos crear un vocabulario de todas las palabras en nuestros datos. Recuerde del artículo anterior, podríamos hacerlo con la ayuda de un vectorizador de conteo. Mira el siguiente guión:

1
2
3
4
from sklearn.feature_extraction.text import CountVectorizer

count_vect = CountVectorizer(max_df=0.8, min_df=2, stop_words='english')
doc_term_matrix = count_vect.fit_transform(reviews_datasets['Text'].values.astype('U'))

En el script anterior, usamos la clase CountVectorizer del módulo sklearn.feature_extraction.text para crear una matriz de término de documento. Especificamos incluir solo aquellas palabras que aparecen en menos del 80% del documento y aparecen en al menos 2 documentos. También eliminamos todas las palabras vacías, ya que en realidad no contribuyen al modelado de temas.

Ahora echemos un vistazo a la matriz de términos de nuestro documento:

1
doc_term_matrix

Producción:

1
2
<20000x14546 sparse matrix of type '<class 'numpy.int64'>'
with 594703 stored elements in Compressed Sparse Row format>

Cada uno de los 20k documentos se representa como un vector dimensional de 14546, lo que significa que nuestro vocabulario tiene 14546 palabras.

A continuación, usaremos LDA para crear temas junto con la distribución de probabilidad de cada palabra en nuestro vocabulario para cada tema. Ejecute el siguiente script:

1
2
3
4
from sklearn.decomposition import LatentDirichletAllocation

LDA = LatentDirichletAllocation(n_components=5, random_state=42)
LDA.fit(doc_term_matrix)

En el script anterior, usamos la clase LatentDirichletAllocation de la biblioteca sklearn.decomposition para realizar LDA en nuestra matriz de término de documento. El parámetro n_components especifica el número de categorías, o temas, en los que queremos que se divida nuestro texto. El parámetro random_state (también conocido como semilla) se establece en 42 para que obtenga resultados similares a los míos.

Busquemos aleatoriamente palabras de nuestro vocabulario. Sabemos que el vectorizador de conteo contiene todas las palabras de nuestro vocabulario. Podemos usar el método get_feature_names() y pasarle el ID de la palabra que queremos obtener.

El siguiente script extrae aleatoriamente 10 palabras de nuestro vocabulario:

1
2
3
4
5
import random

for i in range(10):
    random_id = random.randint(0,len(count_vect.get_feature_names()))
    print(count_vect.get_feature_names()[random_id])

La salida se ve así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
bribe
tarragon
qualifies
prepare
hangs
noted
churning
breeds
zon
chunkier

Encontremos 10 palabras con la mayor probabilidad para el primer tema. Para obtener el primer tema, puede usar el atributo components_ y pasar un índice 0 como valor:

1
first_topic = LDA.components_[0]

El primer tema contiene las probabilidades de 14546 palabras para el tema 1. Para ordenar los índices según los valores de probabilidad, podemos usar la función argsort(). Una vez ordenadas, las 10 palabras con las probabilidades más altas ahora pertenecerán a los últimos 10 índices de la matriz. El siguiente script devuelve los índices de las 10 palabras con las probabilidades más altas:

1
top_topic_words = first_topic.argsort()[-10:]

Producción:

1
2
array([14106,  5892,  7088,  4290, 12596,  5771,  5187, 12888,  7498,
       12921], dtype=int64)

Estos índices se pueden usar para recuperar el valor de las palabras del objeto count_vect, lo que se puede hacer así:

1
2
for i in top_topic_words:
    print(count_vect.get_feature_names()[i])

En la salida, debería ver las siguientes palabras:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
water
great
just
drink
sugar
good
flavor
taste
like
tea

Las palabras muestran que el primer tema podría ser sobre el té.

Escribamos las 10 palabras con las probabilidades más altas para los cinco temas:

1
2
3
4
for i,topic in enumerate(LDA.components_):
    print(f'Top 10 words for topic #{i}:')
    print([count_vect.get_feature_names()[i] for i in topic.argsort()[-10:]])
    print('\n')

La salida se ve así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Top 10 words for topic #0:
['water', 'great', 'just', 'drink', 'sugar', 'good', 'flavor', 'taste', 'like', 'tea']


Top 10 words for topic #1:
['br', 'chips', 'love', 'flavor', 'chocolate', 'just', 'great', 'taste', 'good', 'like']


Top 10 words for topic #2:
['just', 'drink', 'orange', 'sugar', 'soda', 'water', 'like', 'juice', 'product', 'br']


Top 10 words for topic #3:
['gluten', 'eat', 'free', 'product', 'like', 'dogs', 'treats', 'dog', 'br', 'food']


Top 10 words for topic #4:
['cups', 'price', 'great', 'like', 'amazon', 'good', 'br', 'product', 'cup', 'coffee']

El resultado muestra que el segundo tema podría contener reseñas sobre chocolates, etc. De manera similar, el tercer tema podría contener nuevamente reseñas sobre refrescos o jugos. Puede ver que hay algunas palabras comunes en todas las categorías. Esto se debe a que hay pocas palabras que se usan para casi todos los temas. Por ejemplo, "bueno", "excelente", "me gusta", etc.

Como paso final, agregaremos una columna al marco de datos original que almacenará el tema del texto. Para hacerlo, podemos usar el método LDA.transform() y pasarle nuestra matriz de término de documento. Este método asignará la probabilidad de todos los temas a cada documento. Mira el siguiente código:

1
2
topic_values = LDA.transform(doc_term_matrix)
topic_values.shape

En el resultado, verá (20000, 5), lo que significa que cada uno de los documentos tiene 5 columnas donde cada columna corresponde al valor de probabilidad de un tema en particular. Para encontrar el índice del tema con el valor máximo, podemos llamar al método argmax() y pasar 1 como valor para el parámetro del eje.

El siguiente script agrega una nueva columna para el tema en el marco de datos y asigna el valor del tema a cada fila de la columna:

1
reviews_datasets['Topic'] = topic_values.argmax(axis=1)

Veamos ahora cómo se ve el conjunto de datos:

1
reviews_datasets.head()

Producción:

{.img-responsive}

Puede ver una nueva columna para el tema en la salida.

Factorización de matriz no negativa (NMF)

En la sección anterior, vimos cómo se puede usar LDA para el modelado de temas. En esta sección, veremos cómo se puede utilizar la factorización de matrices no negativas para el modelado de temas.

La factorización de matrices no negativas también es una técnica de aprendizaje supervisado que realiza la agrupación y la reducción de la dimensionalidad. Se puede usar en combinación con el esquema TF-IDF para realizar el modelado de temas. En esta sección, veremos cómo se puede usar Python para realizar una factorización de matriz no negativa para el modelado de temas.

NMF para el modelado de temas en Python

En esta sección, realizaremos el modelado de temas en el mismo conjunto de datos que usamos en la última sección. Verás que los pasos también son bastante similares.

Comenzamos importando el conjunto de datos:

1
2
3
4
5
6
import pandas as pd
import numpy as np

reviews_datasets = pd.read_csv(r'E:\Datasets\Reviews.csv')
reviews_datasets = reviews_datasets.head(20000)
reviews_datasets.dropna()

En la sección anterior usamos el vectorizador de conteo, pero en esta sección usaremos el vectorizador TFIDF ya que NMF funciona con TFIDF. Crearemos una matriz de términos de documentos con TFIDF. Mira el siguiente guión:

1
2
3
4
from sklearn.feature_extraction.text import TfidfVectorizer

tfidf_vect = TfidfVectorizer(max_df=0.8, min_df=2, stop_words='english')
doc_term_matrix = tfidf_vect.fit_transform(reviews_datasets['Text'].values.astype('U'))

Una vez que se genera la matriz de términos del documento, podemos crear una matriz de probabilidad que contenga las probabilidades de todas las palabras del vocabulario para todos los temas. Para hacerlo, podemos usar la clase NMF del módulo sklearn.decomposition. Mira el siguiente guión:

1
2
3
4
from sklearn.decomposition import NMF

nmf = NMF(n_components=5, random_state=42)
nmf.fit(doc_term_matrix )

Como hicimos en la sección anterior, saquemos al azar 10 palabras de nuestro vocabulario:

1
2
3
4
5
import random

for i in range(10):
    random_id = random.randint(0,len(tfidf_vect.get_feature_names()))
    print(tfidf_vect.get_feature_names()[random_id])

En la salida, verá las siguientes palabras:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
safest
pith
ache
formula
fussy
frontier
burps
speaker
responsibility
dive

A continuación, recuperaremos el vector de probabilidad de palabras para el primer tema y recuperaremos los índices de las diez palabras con las probabilidades más altas:

1
2
first_topic = nmf.components_[0]
top_topic_words = first_topic.argsort()[-10:]

Estos índices ahora se pueden pasar al objeto tfidf_vect para recuperar las palabras reales. Mira el siguiente guión:

1
2
for i in top_topic_words:
    print(tfidf_vect.get_feature_names()[i])

La salida se ve así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
really
chocolate
love
flavor
just
product
taste
great
good
like

Las palabras del tema 1 muestran que el tema 1 puede contener reseñas de chocolates. Escribamos ahora las diez palabras con mayores probabilidades para cada uno de los temas:

1
2
3
4
for i,topic in enumerate(nmf.components_):
    print(f'Top 10 words for topic #{i}:')
    print([tfidf_vect.get_feature_names()[i] for i in topic.argsort()[-10:]])
    print('\n')

La salida del script anterior se ve así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Top 10 words for topic #0:
['really', 'chocolate', 'love', 'flavor', 'just', 'product', 'taste', 'great', 'good', 'like']


Top 10 words for topic #1:
['like', 'keurig', 'roast', 'flavor', 'blend', 'bold', 'strong', 'cups', 'cup', 'coffee']


Top 10 words for topic #2:
['com', 'amazon', 'orange', 'switch', 'water', 'drink', 'soda', 'sugar', 'juice', 'br']


Top 10 words for topic #3:
['bags', 'flavor', 'drink', 'iced', 'earl', 'loose', 'grey', 'teas', 'green', 'tea']


Top 10 words for topic #4:
['old', 'love', 'cat', 'eat', 'treat', 'loves', 'dogs', 'food', 'treats', 'dog']

Las palabras del tema 1 muestran que este tema contiene reseñas sobre el café. De manera similar, las palabras del tema 2 muestran que contiene reseñas sobre refrescos y jugos. El tema 3 nuevamente contiene reseñas sobre bebidas. Finalmente, el tema 4 puede contener reseñas sobre alimentación animal ya que contiene palabras como "gato", "perro", "premio", etc.

El siguiente script agrega los temas al conjunto de datos y muestra las primeras cinco filas:

1
2
3
topic_values = nmf.transform(doc_term_matrix)
reviews_datasets['Topic'] = topic_values.argmax(axis=1)
reviews_datasets.head()

La salida del código anterior se ve así:

{.img-responsive}

Como puede ver, se ha asignado un tema a cada revisión, que se generó utilizando el método NMF.

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 modelado de temas es una de las áreas de investigación más solicitadas en PNL. Se utiliza para agrupar grandes volúmenes de datos de texto sin etiquetar. En este artículo, se han explicado dos enfoques para el modelado de temas. En este artículo, vimos cómo la asignación de Dirichlet latente y la factorización de matriz no negativa se pueden usar para el modelado de temas con la ayuda de las bibliotecas de Python. n.