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 鈥嬧媏n 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 *