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 análisis de sentimientos 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 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, agrupar 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 y Factorización de matriz no negativa. En las siguientes secciones, revisaremos brevemente ambos enfoques y veremos cómo se pueden aplicar al modelado de temas en Python.

    Asignación de Dirichlet latente (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 juntas con frecuencia suelen tener el mismo tema.

    Estos supuestos tienen sentido porque los documentos que tienen el mismo tema, por ejemplo, Temas comerciales, tendrán palabras como “economía”, “ganancias”, “mercado de valores”, “pérdidas”, etc. El segundo supuesto establece que si estos las palabras suelen aparecer juntas en varios documentos, esos documentos pueden pertenecer a la misma categoría.

    Matemáticamente, los dos supuestos anteriores se pueden representar como:

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

    LDA para 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 del Kaggle.

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

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

    import pandas as pd
    import numpy as np
    
    reviews_datasets = pd.read_csv(r'E:DatasetsReviews.csv')
    reviews_datasets = reviews_datasets.head(20000)
    reviews_datasets.dropna()
    

    En el script de arriba, importamos el conjunto de datos usando el read_csv método de la biblioteca de pandas. El conjunto de datos original contiene alrededor de 500.000 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 el head() función para inspeccionar nuestros datos:

    reviews_datasets.head()
    

    En la salida, verá los siguientes datos:

    Aplicaremos LDA en la columna “Texto” ya que contiene las revisiones, el resto de las columnas serán ignoradas.

    Veamos la revisión número 350.

    reviews_datasets['Text'][350]
    

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

    '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 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:

    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 de arriba usamos el CountVectorizer clase de la sklearn.feature_extraction.text módulo para crear una matriz documento-plazo. 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 veamos nuestra matriz de términos del documento:

    doc_term_matrix
    

    Salida:

    <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 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:

    from sklearn.decomposition import LatentDirichletAllocation
    
    LDA = LatentDirichletAllocation(n_components=5, random_state=42)
    LDA.fit(doc_term_matrix)
    

    En el script de arriba usamos el LatentDirichletAllocation clase de la sklearn.decomposition biblioteca para realizar LDA en nuestra matriz de documentos y términos. 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 el semilla) se establece en 42 para que obtenga resultados similares a los míos.

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

    El siguiente guión extrae aleatoriamente 10 palabras de nuestro vocabulario:

    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í:

    bribe
    tarragon
    qualifies
    prepare
    hangs
    noted
    churning
    breeds
    zon
    chunkier
    

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

    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 el argsort() función. Una vez ordenadas, las 10 palabras con las mayores probabilidades ahora pertenecerán a los últimos 10 índices de la matriz. El siguiente script devuelve los índices de las 10 palabras con las mayores probabilidades:

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

    Salida:

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

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

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

    En el resultado, debería ver las siguientes palabras:

    water
    great
    just
    drink
    sugar
    good
    flavor
    taste
    like
    tea
    

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

    Imprimamos las 10 palabras con mayores probabilidades para los cinco temas:

    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í:

    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 utilizan 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 LDA.transform() y páselo a nuestra matriz de documentos y términos. Este método asignará la probabilidad de todos los temas a cada documento. Mira el siguiente código:

    topic_values = LDA.transform(doc_term_matrix)
    topic_values.shape
    

    En la salida, 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 de tema con valor máximo, podemos llamar al argmax() y pase 1 como valor para el parámetro del eje.

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

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

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

    reviews_datasets.head()
    

    Salida:

    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 modelar temas. En esta sección, veremos cómo se puede utilizar la factorización matricial no negativa para el modelado de temas.

    La factorización matricial no negativa también es una técnica de aprendizaje supervisado que realiza agrupaciones y reducciones de dimensionalidad. Se puede utilizar 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 matricial no negativa para el modelado de temas.

    NMF para 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:

    import pandas as pd
    import numpy as np
    
    reviews_datasets = pd.read_csv(r'E:DatasetsReviews.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 trabaja con TFIDF. Crearemos una matriz de términos del documento con TFIDF. Mira el siguiente guión:

    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 contiene probabilidades de todas las palabras en el vocabulario para todos los temas. Para hacerlo, podemos usar el NMF clase de la sklearn.decomposition módulo. Mira el siguiente guión:

    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, obtengamos al azar 10 palabras de nuestro vocabulario:

    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:

    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 mayores probabilidades:

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

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

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

    La salida se ve así:

    really
    chocolate
    love
    flavor
    just
    product
    taste
    great
    good
    like
    

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

    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í:

    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 para el tema 1 muestran que este tema contiene reseñas sobre el café. De manera similar, las palabras del tema 2 describen que contiene reseñas sobre refrescos y jugos. El tema 3 nuevamente contiene reseñas sobre bebidas. Por último, el tema 4 puede contener reseñas sobre alimentos para animales, ya que contiene palabras como “gato”, “perro”, “golosina”, etc.

    La siguiente secuencia de comandos agrega los temas al conjunto de datos y muestra las primeras cinco filas:

    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í:

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

    Conclusión

    El modelado de temas es una de las áreas de investigación más buscadas en PNL. Se utiliza para agrupar grandes volúmenes de datos de texto sin etiquetar. En este artículo, se explicaron 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 matrices no negativas se pueden usar para el modelado de temas con la ayuda de bibliotecas de Python.

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *