import string
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import nltk # herramienta que permite trabajar con dialogos y texto
from nltk.corpus import stopwords
from unidecode import unidecode
# Descargar información necesaria para el preprocesamiento
nltk.download('stopwords')
# Una vez descargado, se comenta porque ya queda instalado
dialogs = pd.read_csv('dialogos.csv', index_col=0)
dialogs['speaker'] = np.where(dialogs['speaker'] == 'amlo', 'politico', 'medico')
dialogs.sample(10)
# Se busca hacer una clasificación binaria, donde se busca saber quién dijo dicha frase, si un político o un médico
Análisis Exploratorio de Datos
fig, (ax1,ax2) = plt.subplots(2,1,dpi = 100,figsize = (10,5))
sns.violinplot(y = 'speaker', x = 'length', data=dialogs, ax=ax1)
sns.boxplot(y='speaker', x = 'length', data=dialogs, ax = ax2)
# Se muestra que los diálogos son de la misma longitud en ambos perfiles
# No son una distribución normal
fig = plt.figure(dpi=100,figsize=(12,3))
ax = fig.gca()
for label, color in [('politico','green'),('medico','red')]:
lengths = dialogs[dialogs['speaker'] == label]
values, bins = np.histogram(lengths['length'], range = (0,1200), bins = 50)
y = (bins[1:] + bins[:-1])/2
sns.barplot(x=y, y=values/len(lengths), label = label, color=color, ax = ax, alpha = 0.5)
ax.set_xticklabels([f'{int(_y)} - {int(_x)}' for _x,_y in zip(bins[1:], bins[:-1])], rotation = 90, fontsize = 9)
ax.set_yticklabels([])
ax.set_xlabel('Rango')
ax.legend()
# Distribución de los diálogos
# La mayoría de los diálogos se presentan en dos grupos predominantes
Dividir el Dataset
from sklearn.model_selection import train_test_split
# Se dividirá el data set en dos secciones
# una de las cuales será la de pruebas
rest, test = train_test_split(dialogs, test_size = 0.2, stratify=dialogs['speaker'])
# stratify le inidica que distribuya el dataset en función de speaker para sea más equitativo
# ya que los diálogos del político son muchos más
# El resto se dividirá entre el entrenamiento y la validación
train, val = train_test_split(rest, test_size = 0.2, stratify = rest['speaker'])
len(train), len(val), len(test)
# Guardar los valores en diferentes variables
dialogs_train = train['dialog']
dialogs_val = val['dialog']
dialogs_test = test['dialog']
target_train = train['speaker']
target_val = val['speaker']
target_test = test['speaker']
Feature Engineering
Etiquetas
# Los datos deben transformarse a información que el modelo entienda
# por lo general, estos son números.
# Etiqueta
train_y = np.where(target_train == 'politico', 1, 0)
val_y = np.where(target_val == 'politico', 1, 0)
test_y = np.where(target_test == 'politico', 1, 0)
Texto
dialogs_train.sample(10, random_state=132).values
# Trabajar un sólo texto
example_sentence = dialogs_train.iloc[80561]
print(example_sentence)
Tokenización
from nltk.tokenize.toktok import ToktokTokenizer
# Existen librerías para redes sociales que identifica emojis
tk_tokenizer = ToktokTokenizer()
# Separar el diálogo y los signos de puntuación en tokens
tokens = tk_tokenizer.tokenize(example_sentence)
print(' # '.join(tokens))
# Las palabras que siempre se utilizan, no importar el tema: STOPWORDS
# artículos, preposiciones, conectores, etc
# Símbolos de puntuacióñ
# acentos
# Todos esos tokens se pueden eliminar para poder quedarnos con las importantes
# Se llama a las stopwords del español
sp_stopwords = stopwords.words('spanish')
# Eliminar los signos de puntuación
# Se deben agregar los signos de apertura de interrogación y exclamación
# Se llama a todos los signos de puntuación agregando los mencionados
sp_punctuation = string.punctuation + '¿¡'
# Combinar los dos grupos de no deseados
not_wanted = set((unidecode(word) for word in sp_stopwords)) | set(sp_punctuation)
not_wanted
#tokanizador
tk_tokenizer = ToktokTokenizer()
# Guardar los tokens que si queremos que se conserven
def tokenise(sentence):
limpia = []
clean_sentence = unidecode(sentence)
for token_ in tk_tokenizer.tokenize(clean_sentence):
token = token_.lower()
if token in not_wanted:
continue
limpia.append(token)
return limpia
print(tokenise(example_sentence))
Vectorización
One-Hot Encoding
from sklearn.feature_extraction.text import CountVectorizer
vectorizador_ejemplo = CountVectorizer(binary=True, analyzer = tokenise, max_features=4000)
# Binary inidica si utilizaremos binarios (sólo si existe o no)
# o si queremos ocurrencias (cuantas veces existe)
ejemplos = [
'viva mexico paisanos en septiembre',
'en mexico hay inundaciones de viva voz'
]
vectors = vectorizador_ejemplo.fit_transform(ejemplos)
vocabulary = vectorizador_ejemplo.vocabulary_
columns = [token for token, _ in sorted(vocabulary.items(), key = lambda item: item[1])]
pd.DataFrame(vectors.todense(), columns = columns, index = [1,2])
# Crear un vectorizador
# Toma los mil tokens más frecuentes
vectorizador_real = CountVectorizer(binary=True, analyzer=tokenise, max_features=4000)
Convierte nuestros textos a vectores
# Se entrena al modelo
vectorizador_real.fit(dialogs_train)
train_x = vectorizador_real.transform(dialogs_train)
val_x = vectorizador_real.transform(dialogs_val)
test_x = vectorizador_real.transform(dialogs_test)
Modelado
from sklearn.linear_model import LogisticRegression
# Regresión logística
lr = LogisticRegression()
# Entrenado al modelo con los datos
lr.fit(train_x, train_y)
# Revisar los valores
train_pred = lr.predict(train_x) # Para diagnosticar Overfitting
val_pred = lr.predict(val_x) # Para decidir cambios sobre el modelo
val_pred
# Accuracy
from sklearn.metrics import accuracy_score, classification_report
accuracy_score(train_y, train_pred)
# Se descarta el overfitting porque el valor del accuracy es mayor a 60%
print(classification_report(train_y, train_pred))
print(accuracy_score(val_pred, val_y))
# El valor de entrenamiento sería mucho más alto que el de validación en el caso del overfitting
Test Dataset
# Revisar el conjunto de prueba
test_pred = lr.predict(test_x)
test_accuracy = accuracy_score(test_y,test_pred)
print(f'Test accuracy: {test_accuracy:0.2%}')
# Este es el valor que se muestra
# Ejemplo propio
oracion = 'Quedate en casa, la curva epidémica no se ha reducido'
own_x = vectorizador_real.transform([oracion])
result = lr.predict(own_x)
result
medico, politico = lr.predict_proba(own_x).squeeze()*100
# Probabilidad de qué sea uno u otro
medico,politico