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 mapean palabras a sus correspondientes identificadores numéricos. Además, discutimos cómo crear un 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 hacer un modelado de temas usando la biblioteca Scikit-Learn de Python, en mi artÃculo anterior. En ese artÃculo, expliqué cómo Asignación de Dirichlet latente (LDA) y Factorización de matriz no negativa (NMF) se puede utilizar para modelar temas.
En este artÃculo, usaremos la biblioteca Gensim para modelar temas. Los enfoques empleados para el modelado de temas serán LDA y LSI (Indexación latente de Semantim).
Instalación de bibliotecas necesarias
Contenido
Realizaremos el modelado de temas sobre el texto obtenido de los artÃculos de Wikipedia. Para raspar los artÃculos de Wikipedia, usaremos la API de Wikipedia. Para descargar la biblioteca API de Wikipedia, ejecute el siguiente comando:
$ pip install wikipedia
De lo contrario, si usa la distribución Anaconda de Python, puede usar uno de los siguientes comandos:
$ conda install -c conda-forge wikipedia
$ conda install -c conda-forge/label/cf201901 wikipedia
Para visualizar nuestro modelo de tema, usaremos el pyLDAvis
biblioteca. Para descargar la biblioteca, ejecute el siguiente comando pip:
$ pip install pyLDAvis
Nuevamente, si usa la distribución Anaconda en su lugar, puede ejecutar uno de los siguientes comandos:
$ 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, procesaremos previamente los artÃculos, seguidos del paso de modelado de temas. Finalmente, veremos cómo podemos visualizar el modelo LDA.
Raspado de artÃculos de Wikipedia
Ejecute el siguiente script:
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 el wikipedia
y nltk
Bibliotecas. También descargamos el inglés nltk
Para las palabras. Usaremos estas palabras vacÃas más adelante.
A continuación, descargamos el artÃculo de Wikipedia especificando el tema al page
objeto de la wikipedia
biblioteca. El objeto devuelto contiene información sobre la página descargada.
Para recuperar el contenido de la página web, podemos utilizar el content
atributo. El contenido de los cuatro artÃculos se almacena en la lista denominada corpus
.
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. Por el último artÃculo (vinculado arriba), sabemos que para crear un diccionario y un corpus de palabras necesitamos datos en forma de tokens.
Además, debemos eliminar elementos como los signos de 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:
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'^bs+', '', 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 está sucediendo en la función anterior:
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 caracteres individuales sin significado en el texto. Por ejemplo, cuando reemplaza la puntuación en el texto Eiffel's
, las palabras Eiffel
y s
Aparecer. Aquà el s
no tiene significado, por lo tanto, necesitamos reemplazarlo por el espacio. El siguiente script hace eso:
document = re.sub(r's+[a-zA-Z]s+', ' ', document)
La secuencia de comandos anterior elimina los caracteres individuales dentro del texto solamente. Para eliminar un solo carácter al principio del texto, se utiliza el siguiente código.
document = re.sub(r'^[a-zA-Z]s+', ' ', document)
Cuando elimina espacios simples dentro del texto, pueden aparecer varios espacios vacÃos. El siguiente código reemplaza varios espacios vacÃos por un solo espacio:
document = re.sub(r's+', ' ', document, flags=re.I)
Cuando raspa un documento en lÃnea, una cadena b
a menudo se adjunta al documento, lo que significa que el documento es binario. Para eliminar el prefijo b
, se utiliza el siguiente script:
document = re.sub(r'^bs+', '', document)
El resto del método se explica por sà mismo. El documento se convierte en minúsculas y luego se divide en tokens. Los tokens se lematizan y las palabras vacÃas se eliminan. Finalmente, se ignoran todos los tokens que tienen menos de cinco caracteres. El resto de los tokens se devuelven a la función de llamada.
Temas de modelado
Esta sección es el meollo del artÃculo. Aquà veremos cómo se puede usar la función incorporada de la biblioteca Gensim para modelar 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:
processed_data = [];
for doc in corpus:
tokens = preprocess_text(doc)
processed_data.append(tokens)
El guión de arriba es sencillo. Repetimos a través del corpus
lista que contiene los cuatro artÃculos de Wikipedia en forma de cadenas. En cada iteración, pasamos el documento al preprocess_text
método que creamos anteriormente. El método devuelve tokens para ese documento en particular. Los tokens se almacenan en el processed_data
lista.
Al final de for
bucle todos los tokens de los cuatro artÃculos se almacenarán en el processed_data
lista. Ahora podemos usar esta lista para crear un diccionario y el corpus correspondiente de bolsa de palabras. El siguiente script hace eso:
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 el corpus de la bolsa de palabras usando pickle. Usaremos el diccionario guardado más adelante para hacer predicciones sobre los nuevos datos.
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 el LdaModel
clase de la gensim.models.ldamodel
módulo para crear el modelo LDA. Necesitamos pasar el corpus de bolsa de palabras que creamos anteriormente como primer parámetro al LdaModel
constructor, seguido del número de temas, el diccionario que creamos anteriormente y el número de pasadas (número de iteraciones para el modelo).
Ejecute el siguiente script:
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 print_topics
método. Ejecute el siguiente script:
topics = lda_model.print_topics(num_words=10)
for topic in topics:
print(topic)
La salida se ve asÃ:
(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 painting
, louvre
, portrait
, french
museum
, 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 intelligence
, machine
, research
, etc. Podemos suponer que estas palabras pertenecen al tema relacionado con la Inteligencia Artificial.
De manera similar, las palabras del tercer y cuarto tópico 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 del 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.
Ahora creemos 8 temas usando nuestro conjunto de datos. Imprimiremos 5 palabras por tema:
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Ã:
(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"')
Nuevamente, la cantidad de temas que desea crear depende de usted. Siga probando con diferentes números hasta que encuentre los 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:
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 para los parámetros LDA se eligen al azar. Los resultados esta vez son los siguientes:
(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 están relacionadas principalmente con el calentamiento global, mientras que el segundo tema contiene palabras relacionadas con la torre Eiffel.
Evaluación del modelo LDA
Como dije anteriormente, 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:
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 el corpus de bolsa de palabras. La representación de la bolsa de palabras se pasa luego al get_document_topics
método. La salida se ve asÃ:
[(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). Asimismo, existe un 74,4% de posibilidades de que este documento pertenezca al segundo tema. Si miramos el segundo tema, contiene palabras relacionadas con la Torre Eiffel. Nuestro documento de prueba también contiene palabras relacionadas con estructuras y edificios. Por 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.
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 un CoherenceModel
clase que se puede utilizar para encontrar la coherencia del modelo LDA. Por perplejidad, el LdaModel
el objeto contiene log_perplexity
método que toma un corpus de palabras como parámetro y devuelve la perplejidad correspondiente.
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)
los CoherenceModel
La clase toma el modelo LDA, el texto tokenizado, el diccionario y el diccionario como parámetros. Para obtener la puntuación de coherencia, get_coherence
se utiliza el método. La salida se ve asÃ:
Perplexity: -7.492867099178969
Coherence Score: 0.718387005948207
Visualizando el LDA
Para visualizar nuestros datos, podemos utilizar el pyLDAvis
biblioteca que descargamos al principio del artÃculo. La biblioteca contiene un módulo para el modelo Gensim LDA. Primero debemos preparar la visualización pasando el diccionario, un corpus de bolsa de palabras y el modelo LDA al prepare
método. A continuación, debemos llamar al display
sobre el gensim
módulo de la pyLDAvis
biblioteca, como se muestra a continuación:
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:
Cada cÃrculo de la imagen de arriba corresponde a un tema. A partir de la salida del modelo LDA utilizando 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 la Mona Lisa, mientras que el cuarto tema está relacionado con la Artificial. Inteligencia.
La distancia entre cÃrculos muestra cuán diferentes son los temas entre sÃ. Puede 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 de la derecha, solo verá el cÃrculo del tema que contiene la palabra. Por ejemplo, si coloca 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:
De manera similar, si mantiene 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 coloca el cursor sobre el cÃrculo 2, que corresponde al tema “Torre Eiffel”, verá los siguientes resultados:
En el resultado, puede ver que se ha seleccionado el cÃrculo para el segundo tema, es decir, “Torre Eiffel”. En la lista de la derecha, puede ver los términos más frecuentes para el tema. El término “eiffel” está en la parte superior. Además, es evidente que el término “eiffel” se produjo principalmente dentro de este tema.
Por otro lado, si observa el término “francés”, puede ver claramente que alrededor de la mitad de las ocurrencias del término están 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 tiene que hacer es usar el LsiModel
clase. El resto del proceso sigue siendo absolutamente similar a lo que seguimos antes con LDA.
Mira el siguiente guión:
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Ã:
(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 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 hacer un modelado de temas a través de la biblioteca Gensim en Python usando los enfoques LDA y LSI. También vimos cómo visualizar los resultados de nuestro modelo LDA.