Python para PNL: trabajar con la biblioteca Gensim (parte 2)

Este es mi undécimo artículo de la serie de artículos sobre Python para PNL y el segundo artículo sobre la biblioteca Gensim de esta serie. En un artículo anterior, proporcioné un br ...

Este es mi undécimo artículo de la serie de artículos sobre Python para PNL y el segundo artículo sobre la biblioteca Gensim de esta serie. En un Artículo anterior, proporcioné una breve introducción a la biblioteca Gensim de Python. Expliqué cómo podemos crear diccionarios que asignan palabras a sus Id. numéricos correspondientes. También discutimos cómo crear una bolsa de corpus de palabras a partir de diccionarios. En este artículo, estudiaremos cómo podemos realizar el modelado de temas utilizando la biblioteca Gensim.

He explicado cómo modelar temas utilizando la biblioteca Scikit-Learn de Python en mi Artículo anterior. En ese artículo, expliqué cómo [Asignación de Dirichlet latente] (https://en.wikipedia.org/wiki/Latent_Dirichlet_allocation) (LDA) y [Factorización de matriz no negativa] (https://en.wikipedia.org/ wiki/Non -negative_matrix_factorization) (NMF) se puede utilizar para el modelado de temas.

En este artículo, utilizaremos la biblioteca Gensim para el modelado de temas. Los enfoques empleados para el modelado de temas serán LDA y LSI (Latent Semantim Indexación).

Instalación de bibliotecas requeridas

Realizaremos el modelado de temas sobre el texto obtenido de los artículos de Wikipedia. Para extraer artículos de Wikipedia, utilizaremos la API de Wikipedia. Para descargar la biblioteca API de Wikipedia, ejecute el siguiente comando:

1
$ pip install wikipedia

De lo contrario, si usa la distribución Anaconda de Python, puede usar uno de los siguientes comandos:

1
2
$ conda install -c conda-forge wikipedia
$ conda install -c conda-forge/label/cf201901 wikipedia

Para visualizar nuestro modelo de tema, usaremos la biblioteca pyLDAvis. Para descargar la biblioteca, ejecute el siguiente comando pip:

1
$ pip install pyLDAvis

Nuevamente, si usa la distribución Anaconda, puede ejecutar uno de los siguientes comandos:

1
2
3
$ conda install -c conda-forge pyldavis
$ conda install -c conda-forge/label/gcc7 pyldavis
$ conda install -c conda-forge/label/cf201901 pyldavis

Modelado de temas con LDA

En esta sección, realizaremos el modelado de temas de los artículos de Wikipedia utilizando LDA.

Descargaremos cuatro artículos de Wikipedia sobre los temas "Calentamiento global", "Inteligencia artificial", "Torre Eiffel" y "Mona Lisa". A continuación, preprocesaremos los artículos, seguido del paso de modelado de temas. Finalmente, veremos cómo podemos visualizar el modelo LDA.

Scraping de artículos de Wikipedia

Ejecute el siguiente script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import wikipedia
import nltk

nltk.download('stopwords')
en_stop = set(nltk.corpus.stopwords.words('english'))

global_warming = wikipedia.page("Global Warming")
artificial_intelligence = wikipedia.page("Artificial Intelligence")
mona_lisa = wikipedia.page("Mona Lisa")
eiffel_tower = wikipedia.page("Eiffel Tower")

corpus = [global_warming.content, artificial_intelligence.content, mona_lisa.content, eiffel_tower.content]

En el script anterior, primero importamos las bibliotecas wikipedia y nltk. También descargamos las palabras vacías nltk en inglés. Usaremos estas palabras vacías más adelante.

A continuación, descargamos el artículo de Wikipedia especificando el tema en el objeto página de la biblioteca wikipedia. El objeto devuelto contiene información sobre la página descargada.

Para recuperar el contenido de la página web, podemos usar el atributo content. El contenido de los cuatro artículos se almacena en la lista denominada corpus.

Preprocesamiento de datos {#preprocesamiento de datos}

Para realizar el modelado de temas a través de LDA, necesitamos un diccionario de datos y el corpus de la bolsa de palabras. Del último artículo (vinculado arriba), sabemos que para crear un diccionario y una bolsa de corpus de palabras necesitamos datos en forma de tokens.

Además, debemos eliminar elementos como la puntuación y detener las palabras de nuestro conjunto de datos. En aras de la uniformidad, convertiremos todos los tokens a minúsculas y también los lematizaremos. Además, eliminaremos todos los tokens que tengan menos de 5 caracteres.

Mira el siguiente guión:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import re
from nltk.stem import WordNetLemmatizer

stemmer = WordNetLemmatizer()

def preprocess_text(document):
        # Remove all the special characters
        document = re.sub(r'\W', ' ', str(document))

        # remove all single characters
        document = re.sub(r'\s+[a-zA-Z]\s+', ' ', document)

        # Remove single characters from the start
        document = re.sub(r'\^[a-zA-Z]\s+', ' ', document)

        # Substituting multiple spaces with single space
        document = re.sub(r'\s+', ' ', document, flags=re.I)

        # Removing prefixed 'b'
        document = re.sub(r'^b\s+', '', document)

        # Converting to Lowercase
        document = document.lower()

        # Lemmatization
        tokens = document.split()
        tokens = [stemmer.lemmatize(word) for word in tokens]
        tokens = [word for word in tokens if word not in en_stop]
        tokens = [word for word in tokens if len(word)  > 5]

        return tokens

En el script anterior, creamos un método llamado preprocess_text que acepta un documento de texto como parámetro. El método utiliza operaciones de expresiones regulares para realizar una variedad de tareas. Repasemos brevemente lo que sucede en la función anterior:

1
document = re.sub(r'\W', ' ', str(X[sen]))

La línea anterior reemplaza todos los caracteres especiales y números por un espacio. Sin embargo, cuando elimina los signos de puntuación, aparecen en el texto caracteres únicos sin significado. Por ejemplo, cuando reemplaza la puntuación en el texto Eiffel's, aparecen las palabras Eiffel y s. Aquí la ’s’ no tiene significado, por lo tanto necesitamos reemplazarla por un espacio. El siguiente script hace eso:

1
document = re.sub(r'\s+[a-zA-Z]\s+', ' ', document)

La secuencia de comandos anterior elimina caracteres individuales dentro del texto únicamente. Para eliminar un solo carácter al principio del texto, se utiliza el siguiente código.

1
document = re.sub(r'\^[a-zA-Z]\s+', ' ', document)

Cuando elimina espacios individuales dentro del texto, pueden aparecer múltiples espacios vacíos. El siguiente código reemplaza múltiples espacios vacíos por un solo espacio:

1
document = re.sub(r'\s+', ' ', document, flags=re.I)

Cuando raspa un documento en línea, a menudo se agrega una cadena b con el documento, lo que significa que el documento es binario. Para eliminar el prefijo b, se utiliza el siguiente script:

1
document = re.sub(r'^b\s+', '', document)

El resto del método se explica por sí mismo. El documento se convierte en minúsculas y luego se divide en fichas. Los tokens se lematizan y las palabras vacías se eliminan. Finalmente, se ignoran todos los tokens que tengan menos de cinco caracteres. El resto de los tokens se devuelven a la función de llamada.

Temas de modelado

Esta sección es la carne del artículo. Aquí veremos cómo se puede utilizar la función integrada de la biblioteca Gensim para el modelado de temas. Pero antes de eso, necesitamos crear un corpus de todos los tokens (palabras) en los cuatro artículos de Wikipedia que raspamos. Mira el siguiente guión:

1
2
3
4
processed_data = [];
for doc in corpus:
    tokens = preprocess_text(doc)
    processed_data.append(tokens)

El guión anterior es sencillo. Iteramos a través de la lista corpus que contiene los cuatro artículos de Wikipedia en forma de cadenas. En cada iteración, pasamos el documento al método preprocess_text que creamos anteriormente. El método devuelve tokens para ese documento en particular. Los tokens se almacenan en la lista processed_data.

Al final del bucle for, todos los tokens de los cuatro artículos se almacenarán en la lista processed_data. Ahora podemos usar esta lista para crear un diccionario y la correspondiente bolsa de corpus de palabras. El siguiente script hace eso:

1
2
3
4
from gensim import corpora

gensim_dictionary = corpora.Dictionary(processed_data)
gensim_corpus = [gensim_dictionary.doc2bow(token, allow_update=True) for token in processed_data]

A continuación, guardaremos nuestro diccionario así como la bolsa de corpus de palabras usando pepinillo. Usaremos el diccionario guardado más tarde para hacer predicciones sobre los nuevos datos.

1
2
3
4
import pickle

pickle.dump(gensim_corpus, open('gensim_corpus_corpus.pkl', 'wb'))
gensim_dictionary.save('gensim_dictionary.gensim')

Ahora, tenemos todo lo necesario para crear el modelo LDA en Gensim. Usaremos la clase LdaModel del módulo gensim.models.ldamodel para crear el modelo LDA. Necesitamos pasar el corpus de palabras que creamos anteriormente como el primer parámetro al constructor LdaModel, seguido de la cantidad de temas, el diccionario que creamos anteriormente y la cantidad de pases (cantidad de iteraciones para el modelo ).

Ejecute el siguiente script:

1
2
3
4
import gensim

lda_model = gensim.models.ldamodel.LdaModel(gensim_corpus, num_topics=4, id2word=gensim_dictionary, passes=20)
lda_model.save('gensim_model.gensim')

Sí, es así de simple. En el script anterior, creamos el modelo LDA a partir de nuestro conjunto de datos y lo guardamos.

A continuación, imprimamos 10 palabras para cada tema. Para hacerlo, podemos usar el método print_topics. Ejecute el siguiente script:

1
2
3
topics = lda_model.print_topics(num_words=10)
for topic in topics:
    print(topic)

La salida se ve así:

1
2
3
4
5
6
7
(0, '0.036*"painting" + 0.018*"leonardo" + 0.009*"louvre" + 0.009*"portrait" + 0.006*"museum" + 0.006*"century" + 0.006*"french" + 0.005*"giocondo" + 0.005*"original" + 0.004*"picture"')

(1, '0.016*"intelligence" + 0.014*"machine" + 0.012*"artificial" + 0.011*"problem" + 0.010*"learning" + 0.009*"system" + 0.008*"network" + 0.007*"research" + 0.007*"knowledge" + 0.007*"computer"')

(2, '0.026*"eiffel" + 0.008*"second" + 0.006*"french" + 0.006*"structure" + 0.006*"exposition" + 0.005*"tallest" + 0.005*"engineer" + 0.004*"design" + 0.004*"france" + 0.004*"restaurant"')

(3, '0.031*"climate" + 0.026*"change" + 0.024*"warming" + 0.022*"global" + 0.014*"emission" + 0.013*"effect" + 0.012*"greenhouse" + 0.011*"temperature" + 0.007*"carbon" + 0.006*"increase"')

El primer tema contiene palabras como pintura, louvre, retrato, francés museo, etc. Podemos suponer que estas palabras pertenecen a un tema relacionado con una imagen con la conexión francesa.

Del mismo modo, el segundo contiene palabras como ‘inteligencia’, ‘máquina’, ‘investigación’, etc. Podemos suponer que estas palabras pertenecen al tema relacionado con la Inteligencia Artificial.

De manera similar, las palabras del tercer y cuarto tema apuntan al hecho de que estas palabras son parte del tema Torre Eiffel y Calentamiento Global, respectivamente.

Podemos ver claramente que el modelo LDA ha identificado con éxito los cuatro temas en nuestro conjunto de datos.

Es importante mencionar aquí que LDA es un algoritmo de aprendizaje no supervisado y en problemas del mundo real, no conocerá los temas en el conjunto de datos de antemano. Simplemente se le dará un corpus, los temas se crearán usando LDA y luego los nombres de los temas dependerán de usted.

Vamos a crear ahora 8 temas usando nuestro conjunto de datos. Imprimiremos 5 palabras por tema:

1
2
3
4
5
lda_model = gensim.models.ldamodel.LdaModel(gensim_corpus, num_topics=8, id2word=gensim_dictionary, passes=15)
lda_model.save('gensim_model.gensim')
topics = lda_model.print_topics(num_words=5)
for topic in topics:
    print(topic)

La salida se ve así:

1
2
3
4
5
6
7
8
(0, '0.000*"climate" + 0.000*"change" + 0.000*"eiffel" + 0.000*"warming" + 0.000*"global"')
(1, '0.018*"intelligence" + 0.016*"machine" + 0.013*"artificial" + 0.012*"problem" + 0.010*"learning"')
(2, '0.045*"painting" + 0.023*"leonardo" + 0.012*"louvre" + 0.011*"portrait" + 0.008*"museum"')
(3, '0.000*"intelligence" + 0.000*"machine" + 0.000*"problem" + 0.000*"artificial" + 0.000*"system"')
(4, '0.035*"climate" + 0.030*"change" + 0.027*"warming" + 0.026*"global" + 0.015*"emission"')
(5, '0.031*"eiffel" + 0.009*"second" + 0.007*"french" + 0.007*"structure" + 0.007*"exposition"')
(6, '0.000*"painting" + 0.000*"machine" + 0.000*"system" + 0.000*"intelligence" + 0.000*"problem"')
(7, '0.000*"climate" + 0.000*"change" + 0.000*"global" + 0.000*"machine" + 0.000*"intelligence"')

Una vez más, la cantidad de temas que desea crear depende de usted. Siga probando diferentes números hasta que encuentre temas adecuados. Para nuestro conjunto de datos, el número adecuado de temas es 4 ya que sabemos que nuestro corpus contiene palabras de cuatro artículos diferentes. Vuelva a cuatro temas ejecutando el siguiente script:

1
2
3
4
5
lda_model = gensim.models.ldamodel.LdaModel(gensim_corpus, num_topics=4, id2word=gensim_dictionary, passes=20)
lda_model.save('gensim_model.gensim')
topics = lda_model.print_topics(num_words=10)
for topic in topics:
    print(topic)

Esta vez, verá resultados diferentes ya que los valores iniciales de los parámetros LDA se eligen aleatoriamente. Los resultados esta vez son los siguientes:

1
2
3
4
5
6
7
(0, '0.031*"climate" + 0.027*"change" + 0.024*"warming" + 0.023*"global" + 0.014*"emission" + 0.013*"effect" + 0.012*"greenhouse" + 0.011*"temperature" + 0.007*"carbon" + 0.006*"increase"')

(1, '0.026*"eiffel" + 0.008*"second" + 0.006*"french" + 0.006*"structure" + 0.006*"exposition" + 0.005*"tallest" + 0.005*"engineer" + 0.004*"design" + 0.004*"france" + 0.004*"restaurant"')

(2, '0.037*"painting" + 0.019*"leonardo" + 0.009*"louvre" + 0.009*"portrait" + 0.006*"museum" + 0.006*"century" + 0.006*"french" + 0.005*"giocondo" + 0.005*"original" + 0.004*"subject"')

(3, '0.016*"intelligence" + 0.014*"machine" + 0.012*"artificial" + 0.011*"problem" + 0.010*"learning" + 0.009*"system" + 0.008*"network" + 0.007*"knowledge" + 0.007*"research" + 0.007*"computer"')

Puede ver que las palabras del primer tema ahora se relacionan principalmente con el calentamiento global, mientras que el segundo tema contiene palabras relacionadas con la torre Eiffel.

Evaluación del modelo LDA

Como dije antes, los modelos de aprendizaje no supervisados ​​son difíciles de evaluar ya que no existe una verdad concreta contra la cual podamos probar el resultado de nuestro modelo.

Supongamos que tenemos un nuevo documento de texto y queremos encontrar su tema usando el modelo LDA que acabamos de crear, podemos hacerlo usando el siguiente script:

1
2
3
4
5
test_doc = 'Great structures are build to remember an event happened in the history.'
test_doc = preprocess_text(test_doc)
bow_test_doc = gensim_dictionary.doc2bow(test_doc)

print(lda_model.get_document_topics(bow_test_doc))

En el script anterior, creamos una cadena, creamos su representación de diccionario y luego convertimos la cadena en la bolsa de palabras corpus. La representación de la bolsa de palabras se pasa luego al método get_document_topics. La salida se ve así:

1
[(0, 0.08422605), (1, 0.7446843), (2, 0.087012805), (3, 0.08407689)]

El resultado muestra que hay un 8,4% de posibilidades de que el nuevo documento pertenezca al tema 1 (consulte las palabras del tema 1 en el último resultado). De igual forma, existe un 74,4% de posibilidades de que este documento pertenezca al segundo tema. Si nos fijamos en el segundo tema, contiene palabras relacionadas con la Torre Eiffel. Nuestro documento de prueba también contiene palabras relacionadas con estructuras y edificios. Por lo tanto, se le ha asignado el segundo tema.

Otra forma de evaluar el modelo LDA es a través de Perplejidad y [Puntuación de coherencia](https://datascienceplus.com/evalue-of -tema-modelado-tema-coherencia/).

Como regla general para un buen modelo LDA, la puntuación de perplejidad debe ser baja mientras que la coherencia debe ser alta. La biblioteca Gensim tiene una clase CoherenceModel que se puede usar para encontrar la coherencia del modelo LDA. Para la perplejidad, el objeto LdaModel contiene el método log_perplexity que toma un corpus de palabras como parámetro y devuelve la perplejidad correspondiente.

1
2
3
4
5
6
7
8
print('\nPerplexity:', lda_model.log_perplexity(gensim_corpus))

from gensim.models import CoherenceModel

coherence_score_lda = CoherenceModel(model=lda_model, texts=processed_data, dictionary=gensim_dictionary, coherence='c_v')
coherence_score = coherence_score_lda.get_coherence()

print('\nCoherence Score:', coherence_score)

La clase CoherenceModel toma el modelo LDA, el texto tokenizado, el diccionario y el diccionario como parámetros. Para obtener la puntuación de coherencia, se utiliza el método get_coherence. La salida se ve así:

1
2
3
Perplexity: -7.492867099178969

Coherence Score: 0.718387005948207

Visualizando el LDA

Para visualizar nuestros datos, podemos usar la biblioteca pyLDAvis que descargamos al principio del artículo. La biblioteca contiene un módulo para el modelo Gensim LDA. Primero necesitamos preparar la visualización pasando el diccionario, una bolsa de corpus de palabras y el modelo LDA al método preparar. A continuación, debemos llamar a display en el módulo gensim de la biblioteca pyLDAvis, como se muestra a continuación:

1
2
3
4
5
6
7
8
gensim_dictionary = gensim.corpora.Dictionary.load('gensim_dictionary.gensim')
gensim_corpus = pickle.load(open('gensim_corpus_corpus.pkl', 'rb'))
lda_model = gensim.models.ldamodel.LdaModel.load('gensim_model.gensim')

import pyLDAvis.gensim

lda_visualization = pyLDAvis.gensim.prepare(lda_model, gensim_corpus, gensim_dictionary, sort_topics=False)
pyLDAvis.display(lda_visualization)

En la salida, verá la siguiente visualización:

{.img-responsive}

Cada círculo en la imagen de arriba corresponde a un tema. Del resultado del modelo LDA usando 4 temas, sabemos que el primer tema está relacionado con el Calentamiento Global, el segundo tema está relacionado con la Torre Eiffel, el tercer tema está relacionado con Mona Lisa, mientras que el cuarto tema está relacionado con Artificial Inteligencia.

La distancia entre los círculos muestra cuán diferentes son los temas entre sí. Puedes ver que los círculos 2 y 3 se superponen. Esto se debe al hecho de que el tema 2 (Torre Eiffel) y el tema 3 (Mona Lisa) tienen muchas palabras en común, como "francés", "Francia", "Museo", "París". , etc.

Si pasa el cursor sobre cualquier palabra a la derecha, solo verá el círculo del tema que contiene la palabra. Por ejemplo, si pasa el cursor sobre la palabra "clima", verá que los temas 2 y 4 desaparecen porque no contienen la palabra clima. El tamaño del tema 1 aumentará ya que la mayoría de las apariciones de la palabra "clima" están dentro del primer tema. Un porcentaje muy pequeño está en el tema 3, como se muestra en la siguiente imagen:

{.img-responsive}

De manera similar, si pasa el mouse sobre cualquiera de los círculos, aparecerá una lista de los términos más frecuentes para ese tema a la derecha junto con la frecuencia de aparición en ese mismo tema. Por ejemplo, si pasa el cursor sobre el círculo 2, que corresponde al tema "Torre Eiffel", verá los siguientes resultados:

{.img-responsive}

En el resultado, puede ver que se seleccionó el círculo para el segundo tema, es decir, "Torre Eiffel". En la lista de la derecha, puede ver los términos más recurrentes para el tema. El término "eiffel" está en la parte superior. Además, es evidente que el término "eiffel" se produjo mayoritariamente dentro de este tema.

Por otro lado, si observa el término "francés", puede ver claramente que alrededor de la mitad de las apariciones del término se encuentran dentro de este tema. Esto se debe a que el tema 3, es decir, "Mona Lisa", también contiene el término "francés" varias veces. Para verificar esto, haga clic en el círculo del tema 3 y coloque el cursor sobre el término "francés".

Modelado de temas a través de LSI

En la sección anterior, vimos cómo realizar el modelado de temas a través de LDA. Veamos cómo podemos realizar el modelado de temas a través de la indexación semántica latente (LSI).

Para hacerlo, todo lo que tienes que hacer es usar la clase LsiModel. El resto del proceso sigue siendo absolutamente similar al que seguimos antes con LDA.

Mira el siguiente guión:

1
2
3
4
5
6
from gensim.models import LsiModel

lsi_model = LsiModel(gensim_corpus, num_topics=4, id2word=gensim_dictionary)
topics = lsi_model.print_topics(num_words=10)
for topic in topics:
    print(topic)

La salida se ve así:

1
2
3
4
5
6
7
(0, '-0.337*"intelligence" + -0.297*"machine" + -0.250*"artificial" + -0.240*"problem" + -0.208*"system" + -0.200*"learning" + -0.166*"network" + -0.161*"climate" + -0.159*"research" + -0.153*"change"')

(1, '-0.453*"climate" + -0.377*"change" + -0.344*"warming" + -0.326*"global" + -0.196*"emission" + -0.177*"greenhouse" + -0.168*"effect" + 0.162*"intelligence" + -0.158*"temperature" + 0.143*"machine"')

(2, '0.688*"painting" + 0.346*"leonardo" + 0.179*"louvre" + 0.175*"eiffel" + 0.170*"portrait" + 0.147*"french" + 0.127*"museum" + 0.117*"century" + 0.109*"original" + 0.092*"giocondo"')

(3, '-0.656*"eiffel" + 0.259*"painting" + -0.184*"second" + -0.145*"exposition" + -0.145*"structure" + 0.135*"leonardo" + -0.128*"tallest" + -0.116*"engineer" + -0.112*"french" + -0.107*"design"')

Conclusión

El modelado de temas es una tarea importante de la PNL. Existe una variedad de enfoques y bibliotecas que se pueden usar para el modelado de temas en Python. En este artículo, vimos cómo modelar temas a través de la biblioteca Gensim en Python utilizando los enfoques LDA y LSI. También vimos cómo visualizar los resultados de nuestro modelo LDA. modelo LDA.