Introducción al topic modeling con Gensim (I): fundamentos y preprocesamiento de textos

Mar 18, 2021 | Machine Learning, NLP | 0 Comentarios

En esta serie de artículos que comenzamos hoy aprenderemos como identificar las temáticas de documentos de textos mediante una técnica de procesamiento del lenguaje natural (NLP del inglés natural language processing) denominada topic modeling. Además, aplicaremos este modelo en un ejemplo práctico en el que obtendremos los tópicos de un conjunto de noticias extraídas de diferentes periódicos digitales, usando para ello la librería de Python Gensim.

Dedicaremos esta primera publicación de la serie a explicar esta técnica, centrándonos en el modelo LDA, y a realizar el preprocesamento necesario a los textos para un buen funcionamiento del topic modeling que incluye la tokenización de los textos, la eliminación de las stopwords y el proceso de stemming.

Pero antes de meternos manos a la obra con el código, debemos explicar los conceptos fundamentales del topic modeling. ¡Empezemos!

 

¿Qué es topic modeling y para que se utiliza?

El topic modeling es una técnica no supervisada de NLP, capaz de detectar y extraer de manera automática relaciones semánticas latentes de grandes volúmenes de información.

Estas relaciones son los llamados tópicos, que son un conjunto de palabras que suelen aparecer juntas en los mismos contextos y nos permiten observar relaciones que seríamos incapaces de observar a simple vista.

Existen diversas técnicas que pueden ser usadas para obtener estos tópicos. El principal algoritmo y que además será el que utilizaremos en esta publicación, es el modelo latent dirichlet allocation (LDA), propuesto por David Blei en 2011, que nos devuelve por un lado los diferentes tópicos que componen la colección de documentos y por otro lado cuánto de cada tópico está presente en cada documento.

Los tópicos consisten en una distribución de probabilidades de aparición de las distintas palabras del vocabulario.

Si quieres saber más sobre el modelo LDA y su funcionamiento, recomendamos leer este artículo de Blei.

A modo de ejemplo ilustrativo y para entender mejor este modelo, imaginemos a un periodista que tiene disponible una gran colección de noticias, pero está interesado únicamente en las noticias relacionadas con la violencia de género.

Utilizando el modelo LDA con el conjunto de noticias disponibles nos podría producir los siguientes resultados:

 

ejemplo-lda

Ejemplo de resultados en modelo LDA

 

Vemos que, en el caso de los tópicos, se ha generado el Tópico 0, que contiene la palabra violencia en un 5.7%, la palabra género en un 4.5% y la palabra mujer en un 3.7%. Estas son las palabras que más contribuyen al tópico, por lo que el periodista podría interpretar que este tópico tiene relación con la violencia de género.

Por otro lado, en el caso de los documentos, el algoritmo nos dice que la noticia con el titular Sevilla registra 300 denuncias por violencia de género está compuesta en un 43.34% del Tópico 0. Por lo tanto, al contener este tópico en tal alto porcentaje, el periodista vinculará el artículo a la violencia de género.

Para el ejemplo práctico en Python que viene a continuación, utilizaremos como corpus un conjunto de noticas en castellano al igual que en el ejemplo anterior del periodista.

 

Obteniendo las noticias con un rastreador web

En este tutorial utilizaremos como corpus 5665 noticias extraídas de distintos periódicos digitales españoles como El Diario o El Mundo. Para este ejemplo y para que podáis ver el poder de esta técnica, esas 5665 noticias nos serán más que suficientes, pero si queréis aplicarlo en un problema real, cuantos más documentos le metamos al modelo, mejor.

Para obtener estas noticas hemos implementado un rastreador web usando el framework Scrapy de Python que se encarga de extraer y almacenar las noticias de las páginas webs que se le indican y que en este caso son algunos de los periódicos más conocidos en España.

Posteriormente hemos exportado esa información como un fichero .txt con el titular de la noticia y su texto. Podéis descargar el conjunto de datos aquí.

En este articulo no se explicará como implementar el rastreador web, pero tranquilos, en este blog ya mostramos en una publicación anterior como extraer paso a paso con Scrapy los datos de un sitio web. Además, en un segundo tutorial explicamos como extraer los datos de manera recursiva de todas las URLs de un dominio.

 

Preparando los textos para el topic modeling

Es hora de adentrarnos en el topic modeling. Una vez ya tenemos a nuestra disposición los 5665 textos con los que vamos a entrenar nuestro modelo LDA, hay que limpiarlos y preprocesarlos, y para ello vamos a utilizamos la librería NLTK de Python. Este paso es muy importante para asegurarnos que el modelo identifique correctamente las palabras adecuadas y pueda evitar el ruido.

Pero antes de nada, importamos las librerías que vamos a necesitar como las de Gensim y NLTK.

In [1]:
import json, re
import pandas as pd 
from nltk.corpus import stopwords
from nltk.stem import SnowballStemmer
from nltk.tokenize import ToktokTokenizer

El siguiente paso será cargar el fichero de texto creado en el anterior paso y convertirlo en un objeto DataFrame, que nos proporciona la librería pandas, con dos columnas para los titulares y los contenidos de cada noticia.

Una vez hemos construido nuestro DataFrame, mostramos las primeras cinco noticias mediante la función head() para asegurarnos de que se han cargado los datos de manera correcta.

In [2]:
with open('noticias.txt') as json_file:
    datos = json.load(json_file)
tuplas = list(zip([noticia.get("titular") for noticia in datos],
                  [noticia.get("texto") for noticia in datos]))
df = pd.DataFrame(tuplas, columns =['Titular', 'Noticia'])
print(df.shape)
df.head()
(5665, 2)
Out[2]:
TitularNoticia
0Un estudio impulsado por la Universidad de San...El virus SARS-Cov-2 entró en España por la ciu...
1Las claves: qué es Montai y quién está detrás¿Qué es Montai? ¿Qué relación guarda con las o...
2Robots entregan domicilios en Medellín durante...Unos 15 robots recorren las calles de Medellín...
3Grazón insiste en que un nuevo estado de alarm...En una entrevista en Radio Euskadi, recogida p...
4Vox se sube a la ola de la extrema derecha eur..."España ha dejado de ser católica", decía Manu...

Una vez tenemos el DataFrame, es hora de realizar una limpieza inicial de los textos de cada noticia, paso imprescindible antes de cualquier tarea de minería de textos, donde eliminamos los caracteres especiales como ¿ o ¡, las palabras con un solo carácter que normalmente no contienen información útil y convertimos todo a minúsculas entre otras tareas.

En el siguiente código implementamos un método que se encarga de realizar esta limpieza inicial de los textos:

In [3]:
def limpiar_texto(texto):
    """
    Función para realizar la limpieza de un texto dado.
    """
    # Eliminamos los caracteres especiales
    texto = re.sub(r'\W', ' ', str(texto))
    # Eliminado las palabras que tengo un solo caracter
    texto = re.sub(r'\s+[a-zA-Z]\s+', ' ', texto)
    # Sustituir los espacios en blanco en uno solo
    texto = re.sub(r'\s+', ' ', texto, flags=re.I)
    # Convertimos textos a minusculas
    texto = texto.lower()
    return texto

Seguidamente aplicamos la función a cada noticia contenida en nuestro objeto y mostramos de nuevo las 5 primeras:

In [4]:
df["Tokens"] = df.Noticia.apply(limpiar_texto)
df.head()
Out[4]:
TitularNoticiaTokens
0Un estudio impulsado por la Universidad de San...El virus SARS-Cov-2 entró en España por la ciu...el virus sars cov 2 entró en españa por la ciu...
1Las claves: qué es Montai y quién está detrás¿Qué es Montai? ¿Qué relación guarda con las o...qué es montai qué relación guarda con las otr...
2Robots entregan domicilios en Medellín durante...Unos 15 robots recorren las calles de Medellín...unos 15 robots recorren las calles de medellín...
3Grazón insiste en que un nuevo estado de alarm...En una entrevista en Radio Euskadi, recogida p...en una entrevista en radio euskadi recogida po...
4Vox se sube a la ola de la extrema derecha eur..."España ha dejado de ser católica", decía Manu...españa ha dejado de ser católica decía manuel...
Luego llega el turno del proceso de tokenización, que consiste en dividir los textos en tokens o palabras individuales. Para ello, la librería  NLTK nos proporciona el objeto ToktokTokenizer con la función tokenize() para tokenizar nuestros textos.
In [5]:
tokenizer = ToktokTokenizer() 
df["Tokens"] = df.Tokens.apply(tokenizer.tokenize)
df.head()
Out[5]:
TitularNoticiaTokens
0Un estudio impulsado por la Universidad de San...El virus SARS-Cov-2 entró en España por la ciu...[el, virus, sars, cov, 2, entró, en, españa, p...
1Las claves: qué es Montai y quién está detrás¿Qué es Montai? ¿Qué relación guarda con las o...[qué, es, montai, qué, relación, guarda, con, ...
2Robots entregan domicilios en Medellín durante...Unos 15 robots recorren las calles de Medellín...[unos, 15, robots, recorren, las, calles, de, ...
3Grazón insiste en que un nuevo estado de alarm...En una entrevista en Radio Euskadi, recogida p...[en, una, entrevista, en, radio, euskadi, reco...
4Vox se sube a la ola de la extrema derecha eur..."España ha dejado de ser católica", decía Manu...[españa, ha, dejado, de, ser, católica, decía,...

Bien, a continuación, eliminamos las stopwords que componen los textos, es decir, las palabras comunes que no aportan significado, como yo, el o y. Previamente debemos descargar la lista de stopwords en castellano mediante el comando stopwords.words("spanish").

En el siguiente código vemos como construir y aplicar a nuestros datos una función para filtrar fuera de los textos las stopwords y los dígitos:

In [6]:
STOPWORDS = set(stopwords.words("spanish"))

def filtrar_stopword_digitos(tokens):
    """
    Filtra stopwords y digitos de una lista de tokens.
    """
    return [token for token in tokens if token not in STOPWORDS 
            and not token.isdigit()]

df["Tokens"] = df.Tokens.apply(filtrar_stopword_digitos)
df.head()
Out[6]:
TitularNoticiaTokens
0Un estudio impulsado por la Universidad de San...El virus SARS-Cov-2 entró en España por la ciu...[virus, sars, cov, entró, españa, ciudad, vito...
1Las claves: qué es Montai y quién está detrás¿Qué es Montai? ¿Qué relación guarda con las o...[montai, relación, guarda, empresas, quién, de...
2Robots entregan domicilios en Medellín durante...Unos 15 robots recorren las calles de Medellín...[robots, recorren, calles, medellín, realizar,...
3Grazón insiste en que un nuevo estado de alarm...En una entrevista en Radio Euskadi, recogida p...[entrevista, radio, euskadi, recogida, europa,...
4Vox se sube a la ola de la extrema derecha eur..."España ha dejado de ser católica", decía Manu...[españa, dejado, ser, católica, decía, manuel,...

En la esta fase final reducimos cada palabra a su raíz mediante el proceso de stemming. Por ejemplo, este algoritmo reduciría las palabras escribir, escribiendo y escribió a escrib. Para este ejemplo usaré el algoritmo de Porter incluido en la librería NLTK y compatible con el idioma castellano. Para utilizarlo, simplemente creamos un objeto SnowballStemmer y usamos la función stem() como podemos ver en el siguiente código:

In [7]:
stemmer = SnowballStemmer("spanish")

def stem_palabras(tokens):
    """
    Reduce cada palabra de una lista dada a su raíz.
    """
    return [stemmer.stem(token) for token in tokens]

df["Tokens"] = df.Tokens.apply(stem_palabras)

df.head()
Out[7]:
TitularNoticiaTokens
0Un estudio impulsado por la Universidad de San...El virus SARS-Cov-2 entró en España por la ciu...[virus, sars, cov, entro, españ, ciud, vitori,...
1Las claves: qué es Montai y quién está detrás¿Qué es Montai? ¿Qué relación guarda con las o...[montai, relacion, guard, empres, quien, detra...
2Robots entregan domicilios en Medellín durante...Unos 15 robots recorren las calles de Medellín...[robots, recorr, call, medellin, realiz, entre...
3Grazón insiste en que un nuevo estado de alarm...En una entrevista en Radio Euskadi, recogida p...[entrev, radi, euskadi, recog, europ, press, g...
4Vox se sube a la ola de la extrema derecha eur..."España ha dejado de ser católica", decía Manu...[españ, dej, ser, catol, dec, manuel, azañ, co...

A continuación mostramos las primeras diez palabras procesadas de una noticia de nuestra colección a modo de ejemplo:

In [8]:
print(df.Tokens[0][0:10])
['virus', 'sars', 'cov', 'entro', 'españ', 'ciud', 'vitori', 'torn', 'febrer', 'conclusion']

¡Estupendo! Ya tenemos los datos listos para construir nuestro diccionario y entrenar el modelo LDA.

En la siguiente publicación de esta serie se explicará como entrenar y aplicar un modelo LDA a nuestros textos y se interpretarán los resultados obtenidos.

Esperamos que este tutorial os haya resultado útil y si tenéis cualquier duda no dudéis en escribirlas en los comentarios. Podéis descargar el cuaderno Jupyter Notebook con el código Python desde mi cuenta de Github.

También te puede interesar:

Diferencias entre inferencia y predicción

Entiende las diferencias entre inferencia y predicción, dos conceptos de la estadística y el machine learning que pueden resultar confusos.

Libros recomendados para adentrarse en el machine learning

Lista de cinco libros recomendables para principiantes que quieran aprender machine learning y ciencia de datos.

Introducción al topic modeling con Gensim (II): asignación de tópicos

En esta publicación aprenderás como entrenar un modelo LDA con noticias periodísticas para la asignación de tópicos, usando para ello la librería Gensim de Python.

Introducción al topic modeling con Gensim (III): similitud de textos

En este post mostramos como utilizar la técnica de topic modeling para obtener la similitud entre textos teniendo en cuenta la semántica

Extracción de datos de Twitter con Python (sin consumir la API)

En esta publicación os enseñaremos como poder extraer datos de Twitter en Python mediante la librería Twint. De esta forma, podremos obtener facilmente los últimos tweets que contengan cierta palabra o que pertenezcan a un determinado usuario y aplicar varios filtros.

Sentiment analysis en críticas de películas mediante regresión logística

El sentimiento de análisis es una técnica que nos permite identificar la opinión emocional que hay detrás de un textol. En este artículo mostramos como construir un modelo de predicción capaz de distinguir entre críticas positivas y negativas. Estas críticas han sido descargadas previamente de la web de filmaffinity.

AutoML: creación de un modelo de análisis de sentimiento con Google Cloud AutoML

Descubre en que consiste el AutoML, que nos permite automatizar varias partes del proceso de Machine Learning y aprende a utilizar Google Cloud AutoML para realizar una tarea de sentiment analysis y construir un clasificador capaz de identificar si una crítica de película es positiva o negativa.

Introducción al clustering (I): algoritmo k-means

En este artículo explicamos el algoritmo de clustering k-means, el cual busca instancias centradas en un punto determinado, llamado centroide. Después de explicar su funcionamiento, lo aplicaremos en Python a un conjunto de datos y visualizaremos los resultados obtenidos.