Abrir en Google Colab Descargar notebook

Modelado clásico de lenguaje natural

Creando un pipeline de preprocesamiento de texto

A pesar de que los métodos anteriores son no supervisados, son de utilidad para el modelado de de problemas no supervisados como supervisados. Para llevar estos métodos a un entorno práctico normalmente se construyen flujos de procesamiento como el que se muestra más abajo. Estos flujos se los llama Pipeline:

225cb804efde4bb6a0bdbe64edfa8b7e

A modo de ejemplo, vamos a utilizar la API de Scikit-Learn para generar cada uno de estos pasos y así construir un modelo que resuelva un problema de negocio de punta a punta.

¿Que es lo que vamos a hacer? Intentaremos construir un pipeline de machine learning donde como entrada recibamos texto, ejecutemos todos los pasos que vimos en este notebook incluyendo:

  • Eliminación de stopwords

  • Tokenización

  • Stemming y Lemmatization

  • Procesamiento especico del tema

  • Creación de features utilizando algun metodo de reducción de dimensionalidad, SVD, LSI, LDA

, para luego utilizar estas features para entrenar un modelo que nos permita predecir alguna propiedad interesante del set de datos. En este caso en particular, donde estamos viendo tweets, algunos casos interesantes podrían ser:

  • Predecir el sector al que pertenece el tweet: Alimentación, Bebidas, etc.

  • Predecir el paso en el Marketing Funel al que pertece

Para ejecutar este notebook

Para ejecutar este notebook, instale las siguientes librerias:

[42]:
!wget https://raw.githubusercontent.com/santiagxf/M72109/master/NLP/Datasets/mascorpus/tweets_marketing.csv \
    --quiet --no-clobber --directory-prefix ./Datasets/mascorpus/
!wget https://raw.githubusercontent.com/santiagxf/M72109/master/m72109/nlp/normalization.py \
    --quiet --no-clobber --directory-prefix ./m72109/nlp/
!wget https://raw.githubusercontent.com/santiagxf/M72109/master/m72109/nlp/transformation.py \
    --quiet --no-clobber --directory-prefix ./m72109/nlp/
!wget https://raw.githubusercontent.com/santiagxf/M72109/master/docs/nlp/classic/classic-modeling.txt \
    --quiet --no-clobber
!pip install -r classic-modeling.txt --quiet
[2]:
!python -m spacy download es_core_news_sm 1> /dev/null

Primero importaremos algunas librerias necesarias

[3]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm

Sobre el set de datos con el que vamos a trabajar

Utilizaremos como ejemplo un set de datos en español que contiene tweets que diferentes usuarios han publicado en relación a diferentes marcas de productos u empresas en el rubro de alimentación, construcción, automoviles, etc. Estos tweets, a su vez, están asociados a una de las diferentes fases en el proceso de ventas (también conocido como Marketing Funel) y por eso están tagueados con las fases de:

  • Awareness – el cliente es conciente de la existencia de un producto o servicio

  • Interest – activamente expresa el interes de un producto o servicio

  • Evaluation – aspira una marca o producto en particular

  • Purchase – toma el siguiente paso necesario para comprar el producto o servicio

  • Postpurchase - realización del proceso de compra. El cliente compara la diferencia entre lo que deseaba y lo que obtuvo

Referencia: Spanish Corpus of Tweets for Marketing

Nota: La version de este conjunto de datos que utilizaremos aqui es una versión preprocesada del original.

[4]:
tweets = pd.read_csv('Datasets/mascorpus/tweets_marketing.csv')

Inspeccionamos el set de datos

[5]:
tweets.head(5)
[5]:
TEXTO SECTOR MARCA CANAL AWARENESS EVALUATION PURCHASE POSTPURCHASE NC2
0 #tablondeanuncios Funda nordica ikea #madrid h... RETAIL IKEA Microblog 0 0 0.0 0 1.0
1 #tr Me ofrezco para montar muebles de Ikea - H... RETAIL IKEA Microblog 0 0 0.0 0 1.0
2 #VozPópuli Vozpópuli @voz_populi - #LoMásLeido... RETAIL ALCAMPO Microblog 0 0 0.0 0 1.0
3 #ZonaTecno Destacado: Todo lo que hay que sabe... RETAIL CARREFOUR Microblog 0 0 0.0 0 1.0
4 $Carrefour retira pez #Panga. OCU y grupos x #... RETAIL CARREFOUR Microblog 0 0 0.0 0 1.0
[6]:
tweets.groupby('SECTOR').head(1)[['TEXTO', 'SECTOR']]
[6]:
TEXTO SECTOR
0 #tablondeanuncios Funda nordica ikea #madrid h... RETAIL
725 "Ilcinsisti lis MB dispiniblis" te odeeeeeo Mo... TELCO
964 #CarlosSlim y Bimbo lanzarán un vehículo eléct... ALIMENTACION
1298 ‼🏎Toyota #Day, 4ruedas ,1/4 milla, 1 #pasión, ... AUTOMOCION
1748 "- Tú qué.\n- Yo na."\nConversaciones banco sa... BANCA
2348 - Cariño, te juro que sólo tenían Cruzcampo en... BEBIDAS
3023 #adidas #hockey Amenabar 2080 CABA https://t.c... DEPORTES

Creando un pipeline

Creando un paso de Pipeline para procesamiento de texto

El paso más complejo que tenemos para crear es quizas el preprocesamiento del texto. Esto lo podemos encapsular en un modulo de Scikit-Learn. Esta libreria tiene 2 tipos de modulos:

  • Transformers

  • Estimators

Los transformers toman un set de features y devuelven otro set de features, por eso es que reciben el nombre de «trasformers», porque basicamente transforman vectores. Los estimators, por el contrario, reciben un set de features y producen un podelo que aproxima, o estima, una variable target. Por este motivo, estos modulos reciben el nombre de «estimators».

Para simplicidad, en este curso disponemos de un TweetTextNormalizer ya implementado que basicamente utiliza el mismo código que vimos en la lección de procesamiento de texto.

Tip: Recomendamos que revise todos los parametros que recibe esta clase.

Instanciamos nuestro preprocesamiento de texto

[47]:
from m72109.nlp.normalization import TweetTextNormalizer

normalizer = TweetTextNormalizer(
    language='spanish',
    lemmatize=True,
    stem=False,
    reduce_len=True,
    strip_handles=True,
    strip_stopwords=True,
    strip_urls=True,
    strip_accents=True,
    token_min_len=4,
    preserve_case=False
  )

Podemos ver como funciona nuestro modulo de preprocesamiento de texto al llamarlo con la función transform:

[48]:
tweet = tweets['TEXTO'][5]
print(tweet)
. @PoliciadeBurgos @PCivilBurgos @Aytoburgos Mismo peligro c/ Rio Viejo junto Mercadona Villimar
[49]:
normalizer.transform([tweet])
100%|██████████| 1/1 [00:00<00:00, 13.51it/s]
[49]:
array(['mismo peligro viejo junto mercadona villimar '], dtype=object)

Creando pasos de pipeline para la vectorizacion y ingeniería de features

Importamos algunas librerias que necesitaremos

[50]:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import LatentDirichletAllocation
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report

Instanciamos nuestro vectorizador, en este caso usando el método TF-IDF

[51]:
vectorizer = TfidfVectorizer(use_idf=True, sublinear_tf=True, norm='l2')

Instanciamos nuestro generador de features, que en este caso son los tópicos que LDA genere

[52]:
featurizer = LatentDirichletAllocation(n_components=7)

Creando un paso de pipeline para clasificar

Instanciamos nuestro clasificador que utilizará las features generadas hasta este momento

[53]:
estimator = LogisticRegression(max_iter=10000, multi_class='multinomial')

Ensamblando el pipeline

Creamos un pipeline que ejecute todos los pasos en secuencia

[54]:
pipeline = Pipeline(steps=[('normalizer', normalizer),
                           ('vectorizer', vectorizer),
                           ('featurizer', featurizer),
                           ('estimator', estimator)])

En este caso intentaremos predecir el sector al que pertenece un tweet en particular. Para ello, como en todo proceso de machine learning separaremos nuestros datos en training y testing, para poder evaluar los resultados:

[55]:
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(tweets['TEXTO'], tweets['SECTOR'],
                                                    test_size=0.33, stratify=tweets['SECTOR'])

El método fit intrenará nuestro modelo de punta a punta. Tomará unos minutos

[56]:
model = pipeline.fit(X=X_train, y=y_train)
100%|██████████| 2521/2521 [01:47<00:00, 23.56it/s]

Es hora de ver que tan bien le fué a nuestro modelo en esta tarea

[57]:
predictions = model.predict(X_test)
100%|██████████| 1242/1242 [00:53<00:00, 23.02it/s]
[60]:
print(classification_report(y_test, predictions, zero_division=0))
              precision    recall  f1-score   support

ALIMENTACION       0.00      0.00      0.00       110
  AUTOMOCION       0.00      0.00      0.00       148
       BANCA       0.50      0.01      0.02       198
     BEBIDAS       0.34      0.39      0.36       223
    DEPORTES       0.28      0.32      0.30       216
      RETAIL       0.25      0.69      0.37       268
       TELCO       0.00      0.00      0.00        79

    accuracy                           0.28      1242
   macro avg       0.20      0.20      0.15      1242
weighted avg       0.24      0.28      0.20      1242

¿Les parece que estás métricas son buenas? ¿Se les ocurre como mejorarlo? Algunas ideas:

  • ¿Quien funcionará mejor? ¿Stemmer o Lemmatization?

  • ¿Qué será mejor hacer con los hashtags? ¿Quitarlos?

  • ¿Que cantidad de factores latentes funcionará mejor? ¿7, 10, 200, 300?

  • ¿Es Logistic Regression el mejor clasificador que podemos probar? ¿Si subimos la cantidad de tópicos que me sería mejor utilizar?