Python para PNL: análisis de sentimiento con Scikit-Learn

Este es el quinto artículo de la serie de artículos sobre PNL para Python. En mi artículo anterior, expliqué cómo se puede usar la biblioteca spaCy de Python para realizar par...

Este es el quinto artículo de la serie sobre PNL para Python. En mi Artículo anterior, expliqué cómo se puede usar la biblioteca spaCy de Python para realizar el etiquetado de partes del discurso y reconocimiento de entidad nombrada. En este artículo, demostraré cómo hacer un análisis de sentimientos usando datos de Twitter usando la biblioteca Scikit-Learn.

El análisis de sentimientos se refiere al análisis de una opinión o sentimientos sobre algo utilizando datos como texto o imágenes, con respecto a casi cualquier cosa. El análisis de sentimiento ayuda a las empresas en su proceso de toma de decisiones. Por ejemplo, si el sentimiento público hacia un producto no es tan bueno, una empresa puede intentar modificar el producto o detener la producción por completo para evitar pérdidas.

Hay muchas fuentes de opinión pública, p. entrevistas públicas, sondeos de opinión, encuestas, etc. Sin embargo, con más y más personas uniéndose a las plataformas de redes sociales, los sitios web como Facebook y Twitter pueden analizarse en busca de sentimientos públicos.

En este artículo, veremos cómo podemos realizar análisis de sentimiento de datos de texto.

Definición del problema {#definición del problema}

Dados los tuits sobre seis aerolíneas estadounidenses, la tarea es predecir si un tuit contiene un sentimiento positivo, negativo o neutral sobre la aerolínea. Esta es una tarea típica de aprendizaje supervisado en la que, dada una cadena de texto, tenemos que categorizar la cadena de texto en categorías predefinidas.

Solución

Para resolver este problema, seguiremos la canalización típica de aprendizaje automático. Primero importaremos las bibliotecas requeridas y el conjunto de datos. Luego haremos un análisis exploratorio de datos para ver si podemos encontrar alguna tendencia en el conjunto de datos. A continuación, realizaremos el preprocesamiento de texto para convertir datos textuales en datos numéricos que puedan ser utilizados por un algoritmo de aprendizaje automático. Finalmente, utilizaremos algoritmos de aprendizaje automático para entrenar y probar nuestros modelos de análisis de sentimientos.

Importación de las bibliotecas requeridas

El primer paso, como siempre, es importar las bibliotecas requeridas:

1
2
3
4
5
6
import numpy as np 
import pandas as pd 
import re
import nltk 
import matplotlib.pyplot as plt
%matplotlib inline

Nota: Todos los scripts del artículo se ejecutaron con Jupyter Notebook.

Importación del conjunto de datos

El conjunto de datos que vamos a utilizar para este artículo está disponible gratuitamente en este enlace GitHub .

Para importar el conjunto de datos, utilizaremos la función read_csv de Pandas, como se muestra a continuación:

1
2
data_source_url = "https://raw.githubusercontent.com/kolaveridi/kaggle-Twitter-US-Airline-Sentiment-/master/Tweets.csv"
airline_tweets = pd.read_csv(data_source_url)

Primero veamos cómo se ve el conjunto de datos usando el método head():

1
airline_tweets.head()

La salida se ve así:

{.img-responsive}

Análisis de datos {#análisis de datos}

Exploremos un poco el conjunto de datos para ver si podemos encontrar alguna tendencia. Pero antes de eso, cambiaremos el tamaño de parcela predeterminado para tener una mejor vista de las parcelas. Ejecute el siguiente script:

1
2
3
4
5
6
7
plot_size = plt.rcParams["figure.figsize"] 
print(plot_size[0]) 
print(plot_size[1])

plot_size[0] = 8
plot_size[1] = 6
plt.rcParams["figure.figsize"] = plot_size 

Primero veamos el número de tweets de cada aerolínea. Trazaremos un gráfico circular para eso:

1
airline_tweets.airline.value_counts().plot(kind='pie', autopct='%1.0f%%')

En la salida, puede ver el porcentaje de tweets públicos para cada aerolínea. United Airlines tiene el mayor número de tweets, es decir, un 26 %, seguida de US Airways (20 %).

{.img-responsive}

Veamos ahora la distribución de sentimientos en todos los tweets. Ejecute el siguiente script:

1
airline_tweets.airline_sentiment.value_counts().plot(kind='pie', autopct='%1.0f%%', colors=["red", "yellow", "green"])

El resultado de la secuencia de comandos anterior se parece a esto:

{.img-responsive}

A partir de la salida, puede ver que la mayoría de los tweets son negativos (63 %), seguidos de los tweets neutrales (21 %) y luego los tweets positivos (16 %).

A continuación, veamos la distribución del sentimiento para cada aerolínea individual,

1
2
airline_sentiment = airline_tweets.groupby(['airline', 'airline_sentiment']).airline_sentiment.count().unstack()
airline_sentiment.plot(kind='bar')

La salida se ve así:

{.img-responsive}

Es evidente a partir de la salida que para casi todas las aerolíneas, la mayoría de los tweets son negativos, seguidos de tweets neutrales y positivos. Virgin America es probablemente la única aerolínea donde la proporción de los tres sentimientos es algo similar.

Finalmente, usemos la Biblioteca nacida del mar para ver el nivel de confianza promedio de los tweets pertenecientes a tres sentimiento categorías. Ejecute el siguiente script:

1
2
3
import seaborn as sns

sns.barplot(x='airline_sentiment', y='airline_sentiment_confidence' , data=airline_tweets)

La salida del script anterior se ve así:

{.img-responsive}

A partir del resultado, puede ver que el nivel de confianza para los tweets negativos es más alto en comparación con los tweets positivos y neutrales.

Suficiente del análisis de datos exploratorio, nuestro siguiente paso es realizar un preprocesamiento de los datos y luego convertir los datos numéricos en datos de texto como se muestra a continuación.

Limpieza de datos {#limpieza de datos}

Los tweets contienen muchas palabras de jerga y signos de puntuación. Necesitamos limpiar nuestros tweets antes de que puedan usarse para entrenar el modelo de aprendizaje automático. Sin embargo, antes de limpiar los tweets, dividamos nuestro conjunto de datos en conjuntos de características y etiquetas.

Nuestro conjunto de características consistirá únicamente en tweets. Si miramos nuestro conjunto de datos, la columna 11 contiene el texto del tweet. Tenga en cuenta que el índice de la columna será 10 ya que las columnas de pandas siguen un esquema de indexación basado en cero donde la primera columna se llama columna 0. Nuestro conjunto de etiquetas consistirá en el sentimiento del tweet que tenemos que predecir. El sentimiento del tweet está en la segunda columna (índice 1). Para crear una función y un conjunto de etiquetas, podemos usar el método iloc del marco de datos de pandas.

Ejecute el siguiente script:

1
2
features = airline_tweets.iloc[:, 10].values
labels = airline_tweets.iloc[:, 1].values

Una vez que dividimos los datos en características y conjunto de entrenamiento, podemos preprocesarlos para limpiarlos. Para ello, utilizaremos expresiones regulares. Para obtener más información sobre las expresiones regulares, consulte este artículo sobre expresiones regulares.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
processed_features = []

for sentence in range(0, len(features)):
    # Remove all the special characters
    processed_feature = re.sub(r'\W', ' ', str(features[sentence]))

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

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

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

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

    # Converting to Lowercase
    processed_feature = processed_feature.lower()

    processed_features.append(processed_feature)

En el script anterior, comenzamos eliminando todos los caracteres especiales de los tweets. La expresión regular re.sub(r'\W', ' ', str(features[sentence])) hace eso.

A continuación, eliminamos todos los caracteres individuales que quedaron como resultado de eliminar el carácter especial usando la expresión regular re.sub(r'\s+[a-zA-Z]\s+', ' ',processed_feature). Por ejemplo, si eliminamos el carácter especial ' de Jack's y lo reemplazamos con un espacio, nos queda Jack s. Aquí s no tiene significado, por lo que lo eliminamos reemplazando todos los caracteres individuales con un espacio.

Sin embargo, si reemplazamos todos los caracteres individuales con espacios, se crean múltiples espacios. Por lo tanto, reemplazamos todos los espacios múltiples con espacios simples usando la expresión regular re.sub(r'\s+', '',processed_feature, flags=re.I). Además, si su cadena de texto está en formato de bytes, se agrega un carácter b con la cadena. El script anterior elimina eso usando la expresión regular re.sub(r'^b\s+', '',processed_feature).

Finalmente, el texto se convierte a minúsculas usando la función lower().

Representación de texto en forma numérica

Los algoritmos estadísticos utilizan las matemáticas para entrenar modelos de aprendizaje automático. Sin embargo, las matemáticas solo funcionan con números. Para que los algoritmos estadísticos funcionen con texto, primero tenemos que convertir el texto en números. Para hacerlo, existen tres enfoques principales, es decir, Bag of Words, TF-IDF y Word2Vec. En esta sección, discutiremos la bolsa de palabras y el esquema TF-IDF.

Bolsa de palabras

El esquema de bolsa de palabras es la forma más sencilla de convertir texto en números.

Por ejemplo, tiene tres documentos:

  • Doc1 = "Me gusta jugar al fútbol"
  • Doc2 = "Es un buen juego"
  • Doc3 = "Prefiero el fútbol al rugby"

En el enfoque de la bolsa de palabras, el primer paso es crear un vocabulario de todas las palabras únicas. Para los tres documentos anteriores, nuestro vocabulario será:

1
Vocab = [I, like, to, play, football, it, is, a, good, game, prefer, over, rugby]

El siguiente paso es convertir cada documento en un vector de características utilizando el vocabulario. La longitud de cada vector de características es igual a la longitud del vocabulario. La frecuencia de la palabra en el documento reemplazará la palabra real en el vocabulario. Si una palabra del vocabulario no se encuentra en el documento correspondiente, el vector de características del documento tendrá cero en ese lugar. Por ejemplo, para Doc1, el vector de características se verá así:

1
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
TF-IDF

En el enfoque de la bolsa de palabras, cada palabra tiene el mismo peso. La idea detrás del enfoque TF-IDF es que las palabras que aparecen menos en todos los documentos y más en un documento individual contribuyen más a la clasificación.

TF-IDF es una combinación de dos términos. Frecuencia de Término y Frecuencia de Documento Inverso. Se pueden calcular como:

1
2
3
TF  = (Frequency of a word in the document)/(Total words in the document)

IDF = Log((Total number of docs)/(Number of docs containing the word))
TF-IDF usando la biblioteca Scikit-Learn

Afortunadamente para nosotros, la biblioteca Scikit-Aprender de Python contiene la clase TfidfVectorizer que se puede usar para convertir características de texto en vectores de características TF-IDF. El siguiente script realiza esto:

1
2
3
4
5
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import TfidfVectorizer

vectorizer = TfidfVectorizer (max_features=2500, min_df=7, max_df=0.8, stop_words=stopwords.words('english'))
processed_features = vectorizer.fit_transform(processed_features).toarray()

En el código anterior, definimos que max_features debe ser 2500, lo que significa que solo usa las 2500 palabras que aparecen con mayor frecuencia para crear un vector de características de bolsa de palabras. Las palabras que ocurren con menos frecuencia no son muy útiles para la clasificación.

Del mismo modo, max_df especifica que solo se utilizarán aquellas palabras que aparezcan en un máximo del 80% de los documentos. Las palabras que aparecen en todos los documentos son demasiado comunes y no son muy útiles para la clasificación. De manera similar, min-df se establece en 7, lo que muestra que incluye palabras que aparecen en al menos 7 documentos.

División de datos en conjuntos de entrenamiento y prueba

En la sección anterior, convertimos los datos a la forma numérica. Como último paso antes de entrenar nuestros algoritmos, necesitamos dividir nuestros datos en conjuntos de entrenamiento y prueba. El conjunto de entrenamiento se utilizará para entrenar el algoritmo, mientras que el conjunto de prueba se utilizará para evaluar el rendimiento del modelo de aprendizaje automático.

Ejecuta el siguiente código:

1
2
3
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(processed_features, labels, test_size=0.2, random_state=0)

En el código anterior, usamos la clase train_test_split del módulo sklearn.model_selection para dividir nuestros datos en conjuntos de entrenamiento y prueba. El método toma el conjunto de funciones como primer parámetro, el conjunto de etiquetas como segundo parámetro y un valor para el parámetro test_size. Especificamos un valor de 0.2 para test_size, lo que significa que nuestro conjunto de datos se dividirá en dos conjuntos de 80% y 20% de datos. Usaremos el conjunto de datos del 80 % para el entrenamiento y el conjunto de datos del 20 % para las pruebas.

Entrenando al modelo {#entrenando al modelo}

Una vez que los datos se dividen en conjuntos de prueba y entrenamiento, se pueden usar algoritmos de aprendizaje automático para aprender de los datos de entrenamiento. Puede utilizar cualquier algoritmo de aprendizaje automático. Sin embargo, utilizaremos el Algoritmo de bosque aleatorio, debido a su capacidad de actuar sobre datos no normalizados.

El módulo sklearn.ensemble contiene la clase RandomForestClassifier que se puede usar para entrenar el modelo de aprendizaje automático usando el algoritmo de bosque aleatorio. Para hacerlo, necesitamos llamar al método fit en la clase RandomForestClassifier y pasarle nuestras características y etiquetas de entrenamiento, como parámetros. Mira el siguiente guión:

1
2
3
4
from sklearn.ensemble import RandomForestClassifier

text_classifier = RandomForestClassifier(n_estimators=200, random_state=0)
text_classifier.fit(X_train, y_train)

Realización de predicciones y evaluación del modelo {#realización de predicciones y evaluación del modelo}

Una vez que el modelo ha sido entrenado, el último paso es hacer predicciones sobre el modelo. Para hacerlo, necesitamos llamar al método predecir en el objeto de la clase RandomForestClassifier que usamos para el entrenamiento. Mira el siguiente guión:

1
predictions = text_classifier.predict(X_test)

Finalmente, para evaluar el rendimiento de los modelos de aprendizaje automático, podemos usar métricas de clasificación como métrica de confusión, [medida F1](https://en. wikipedia.org/wiki/F1_score), precisión, etc.

Para encontrar los valores de estas métricas, podemos usar las utilidades classification_report, confusion_matrix y accuracy_score de la biblioteca sklearn.metrics. Mira el siguiente script:

1
2
3
4
5
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score

print(confusion_matrix(y_test,predictions))
print(classification_report(y_test,predictions))
print(accuracy_score(y_test, predictions))

La salida del script anterior se ve así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[[1724  101   45]
 [ 329  237   48]
 [ 142   58  244]]
              precision    recall  f1-score   support

    negative       0.79      0.92      0.85      1870
     neutral       0.60      0.39      0.47       614
    positive       0.72      0.55      0.62       444

   micro avg       0.75      0.75      0.75      2928
   macro avg       0.70      0.62      0.65      2928
weighted avg       0.74      0.75      0.73      2928

0.7530737704918032

A partir de la salida, puede ver que nuestro algoritmo logró una precisión de 75,30.

Yendo más lejos: proyecto de extremo a extremo portátil

¿Tu naturaleza inquisitiva te hace querer ir más allá? Recomendamos consultar nuestro Proyecto guiado: ["Subtítulos de imágenes con CNN y Transformers con Keras"](https://wikihtp.com/courses/image-captioning-with-cnns-and -transformadores-con-keras/#cta){target="_blank"}.

En este proyecto guiado, aprenderá a crear un modelo de subtítulos de imágenes, que acepta una imagen como entrada y produce un subtítulo de texto como salida.

Aprenderás a:

  • Preprocesar texto
  • Vectorizar la entrada de texto fácilmente
  • Trabaje con la API tf.data y cree conjuntos de datos de alto rendimiento
  • Cree Transformers desde cero con TensorFlow/Keras y KerasNLP: la adición horizontal oficial a Keras para crear modelos NLP de última generación
  • Cree arquitecturas híbridas donde la salida de una red se codifica para otra

¿Cómo enmarcamos los subtítulos de las imágenes? La mayoría lo considera un ejemplo de aprendizaje profundo generativo, porque estamos enseñando a una red a generar descripciones. Sin embargo, me gusta verlo como una instancia de traducción automática neuronal: estamos traduciendo las características visuales de una imagen en palabras. A través de la traducción, estamos generando una nueva representación de esa imagen, en lugar de simplemente generar un nuevo significado. Verlo como traducción, y solo por generación de extensión, enfoca la tarea bajo una luz diferente y la hace un poco más intuitiva.

Enmarcar el problema como uno de traducción hace que sea más fácil averiguar qué arquitectura querremos usar. Los transformadores solo de codificador son excelentes para comprender el texto (análisis de opinión, clasificación, etc.) porque los codificadores codifican representaciones significativas. Los modelos de solo decodificador son excelentes para la generación (como GPT-3), ya que los decodificadores pueden inferir representaciones significativas en otra secuencia con el mismo significado. La traducción generalmente se realiza mediante una arquitectura de codificador-decodificador, donde los codificadores codifican una representación significativa de una oración (o imagen, en nuestro caso) y los decodificadores aprenden a convertir esta secuencia en otra representación significativa que es más interpretable para nosotros (como una oración).

Conclusión

El análisis de sentimientos es una de las tareas de PNL más comúnmente realizadas, ya que ayuda a determinar la opinión pública general sobre un tema determinado.

En este artículo, vimos cómo diferentes bibliotecas de Python contribuyen a realizar análisis de sentimientos. Realizamos un análisis de tuits públicos sobre seis aerolíneas estadounidenses y logramos una precisión de alrededor del 75 %. Te recomendaría probar y usar algún otro algoritmo de aprendizaje automático como Regresión logística, [MVS](/implementando-svm-y- kernel-svm-con-pythons-scikit-learn/), o KNN y ver si puedes mejorar resultados.

En el próximo artículo mostraré cómo realizar el modelado de temas con Scikit-Learn, que es una técnica no supervisada para analizar grandes volúmenes de texto datos agrupando los documentos en grupos.