Pontificia Universidad Católica de Chile
Departamento de Ciencia de la Computación
2025 - Bimestre 5

Procesamiento de Lenguaje Natural

Ayudantía 2: Word2Vec

Profesor: Marcelo Mendoza
Ayudante: Miguel Fernández

Instalar librerías¶

Para trabajar con el modelo word2vec, es necesario instalar la siguiente librería.

In [ ]:
!pip install gensim
Collecting gensim
  Downloading gensim-4.4.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.metadata (8.4 kB)
Requirement already satisfied: numpy>=1.18.5 in /usr/local/lib/python3.12/dist-packages (from gensim) (2.0.2)
Requirement already satisfied: scipy>=1.7.0 in /usr/local/lib/python3.12/dist-packages (from gensim) (1.16.3)
Requirement already satisfied: smart_open>=1.8.1 in /usr/local/lib/python3.12/dist-packages (from gensim) (7.5.0)
Requirement already satisfied: wrapt in /usr/local/lib/python3.12/dist-packages (from smart_open>=1.8.1->gensim) (2.0.1)
Downloading gensim-4.4.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (27.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 27.9/27.9 MB 13.2 MB/s eta 0:00:00
Installing collected packages: gensim
Successfully installed gensim-4.4.0

Cargar dataset¶

En primer lugar, se debe cargar el conjunto de datos.

In [ ]:
import pandas as pd
df = pd.read_csv("/content/film_affinity.csv")
In [ ]:
df
Out[ ]:
critica nota url
0 Bueno, bajo mi gusto, otro fracaso más de DC. ... 3 https://www.filmaffinity.com/es/reviews/1/4208...
1 Es tan terrible que podría funcionar como paro... 1 https://www.filmaffinity.com/es/reviews/1/4208...
2 Tengo una tradición desde hace más de 5 años. ... 2 https://www.filmaffinity.com/es/reviews/1/4208...
3 No entiendo como nadie tiene la cara de presen... 1 https://www.filmaffinity.com/es/reviews/1/4208...
4 La primera entrega de Wonder Woman (2017) no m... 4 https://www.filmaffinity.com/es/reviews/1/4208...
... ... ... ...
4795 "Shrek" es sin lugar a dudas una de las mejore... 9 https://www.filmaffinity.com/es/reviews/6/4945...
4796 Muy buena e incluso diría, inteligente comedia... 8 https://www.filmaffinity.com/es/reviews/3/9420...
4797 Cuando una película consigue hacer que algo ta... 7 https://www.filmaffinity.com/es/reviews/3/9420...
4798 Una gran comedia estupida que cumple su funció... 8 https://www.filmaffinity.com/es/reviews/3/9420...
4799 Primer "peplum" moderno que sorprendió a la ma... 10 https://www.filmaffinity.com/es/reviews/3/3920...

4800 rows × 3 columns

A continuación, se presenta un gráfico con la cantidad de ejemplos por clase.

In [ ]:
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('ggplot')
ax = sns.countplot(data=df, x="nota",color="tab:blue")
ax.set_title("Distribución notas asignadas")
ax.set_ylabel("Frecuencia")
ax.set_xlabel("Nota")
ax.bar_label(ax.containers[0])
plt.show()
No description has been provided for this image

Dado que el texto está en español, se debe trabajar con la versión de spacy es_core_news_sm.

!python3 -m spacy download es_core_news_sm

In [ ]:
!python3 -m spacy download es_core_news_sm
Collecting es-core-news-sm==3.8.0
  Downloading https://github.com/explosion/spacy-models/releases/download/es_core_news_sm-3.8.0/es_core_news_sm-3.8.0-py3-none-any.whl (12.9 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.9/12.9 MB 31.9 MB/s eta 0:00:00
Installing collected packages: es-core-news-sm
Successfully installed es-core-news-sm-3.8.0
✔ Download and installation successful
You can now load the package via spacy.load('es_core_news_sm')
⚠ Restart to reload dependencies
If you are in a Jupyter or Colab notebook, you may need to restart Python in
order to load all the package's dependencies. You can do this by selecting the
'Restart kernel' or 'Restart runtime' option.

Preprocesamiento¶

En la siguiente celda se define una función para preprocesar el texto.

In [ ]:
import string
import re
import spacy
from spacy.lang.es.stop_words import STOP_WORDS

nlp = spacy.load("es_core_news_sm") # instanciar el modelo en español de spacy
REGX_USERNAME = r"@[A-Za-z0-9$-_@.&+]+"

def preprocessing(text):
  text = text.lower() #llevar todo a minúscula
  text = re.sub(REGX_USERNAME, ' ', text) #procesar caracteres especiales
  tokens = [token.text for token in nlp(text)]
  tokens = [t for t in tokens if t not in STOP_WORDS and t not in string.punctuation and len(t) > 2] #filtrar casos con menos de una palabra, eliminar stopwords
  tokens = [t for t in tokens if not t.isdigit()]

  return " ".join(tokens)

Se aplica la función preprocessing a la variable "critica" y se almacena en text_clean (demora 5 minutos).

In [ ]:
df["text_clean"] = df["critica"].apply(preprocessing)

Se crea la variable binaria sentiment considerando la nota asignada. Esta columna se incorpora al dataframe.

In [ ]:
df["sentiment"] = df.apply(lambda x: 0 if int(x["nota"]) < 5  else 1, axis=1)
In [ ]:
df.head()
Out[ ]:
critica nota url text_clean sentiment
0 Bueno, bajo mi gusto, otro fracaso más de DC. ... 3 https://www.filmaffinity.com/es/reviews/1/4208... gusto fracaso empezó año aves presa acaba año ... 0
1 Es tan terrible que podría funcionar como paro... 1 https://www.filmaffinity.com/es/reviews/1/4208... terrible funcionar parodia sobreactuada record... 0
2 Tengo una tradición desde hace más de 5 años. ... 2 https://www.filmaffinity.com/es/reviews/1/4208... tradición años diciembre cine blockbuster peli... 0
3 No entiendo como nadie tiene la cara de presen... 1 https://www.filmaffinity.com/es/reviews/1/4208... entiendo cara presentar película entiendo crít... 0
4 La primera entrega de Wonder Woman (2017) no m... 4 https://www.filmaffinity.com/es/reviews/1/4208... entrega wonder woman pareció maravilla contrar... 0

A continuación, se verifica que la clasificación es correcta.

In [ ]:
df[df["sentiment"]==0].nota.value_counts()
Out[ ]:
count
nota
1 674
3 609
4 576
2 541

In [ ]:
df[df["sentiment"]==1].nota.value_counts()
Out[ ]:
count
nota
7 717
8 648
10 561
9 474

In [ ]:
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('ggplot')
ax = sns.countplot(data=df, x="sentiment",color="tab:blue")
ax.set_title("Distribución de clases")
ax.set_ylabel("Frecuencia")
ax.set_xlabel("Categoría Nota")
ax.bar_label(ax.containers[0])
plt.show()
No description has been provided for this image

Creación train-test-val¶

Se crean 3 conjuntos de datos:

  • train (80%)
  • val (10%)
  • test (10%)
In [ ]:
df.shape
Out[ ]:
(4800, 5)
In [ ]:
from sklearn.model_selection import train_test_split
X = df["text_clean"]
y = df["sentiment"]
In [ ]:
#Separación entre train (90%) y test (10%) desde el conjunto completo
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=42)

#Separación entre train y val desde el conjunto train definido anteriormente
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=480, random_state=42)
In [ ]:
print(f"X_train: {X_train.shape[0]}")
print(f"y_train: {y_train.shape[0]} \n")
print(f"X_val: {X_val.shape[0]}")
print(f"y_val: {y_val.shape[0]}\n")
print(f"X_test: {X_test.shape[0]}")
print(f"y_test: {y_test.shape[0]}")
X_train: 3840
y_train: 3840 

X_val: 480
y_val: 480

X_test: 480
y_test: 480

Para usar el tokenizador se debe intalar nltk

In [ ]:
import nltk
nltk.download('punkt_tab')
[nltk_data] Downloading package punkt_tab to /root/nltk_data...
[nltk_data]   Unzipping tokenizers/punkt_tab.zip.
Out[ ]:
True

La siguiente función permite tokenizar cada sentencia del dataset.

In [ ]:
from nltk.tokenize import RegexpTokenizer, sent_tokenize

tokenizer = RegexpTokenizer('[\'a-zA-Z]+')
def tokenize(document):
    words = []
    for sentence in sent_tokenize(document):
        tokens = [t.lower() for t in tokenizer.tokenize(sentence) if len(t) > 2]
        words += tokens
    return words
In [ ]:
corpus_words = [] #Lista de listas de palabras que componen una sentencia. Esta estructura espera el modelo de word2vec

for raw_text in X_train.to_list():
    words = tokenize(raw_text)
    corpus_words.append(words)
In [ ]:
df.critica[0]
Out[ ]:
'Bueno, bajo mi gusto, otro fracaso más de DC. Empezó el año con Aves de Presa y acaba el año con Wonder Woman. Dos películas malas con ganas. \r\n \r\nEn esta película, destacan los 37 slow motions que le han metido, junto con unos efectos especiales no de segunda, sino de tercera categoría. Te ves las de Spiderman de Sam Reimi que son de hace 15 años, y piensas que son mucho más modernas que éstas. \r\n \r\nMención aparte, los minutos que sale Wonder Woman como tal. Si hubiera tenido cronómetro en mano, yo creo que no llegan a los 15 minutos totales en pantalla. No olvidemos que semejante película dura nada más y nada menos que 150 minutos. \r\n \r\nQue está ambientada en los 80, te lo tienes que creer porque lo pone en el título y porque te dan 4 pinceladas de ello. Por lo demás, 0 relevancia tiene en la película, no se nota que esté en los 80 como tal. Más bien parece una acción totalmente comercial con el tirón que existe ahora. \r\n \r\nRespecto al argumento, poco que decir. Poco se puede decir sin spoiler, pero vamos, es que parece que hacen lo primero que se les ocurre en la cabeza sin pensarlo dos veces. La forma en la que resuelven la película es cuanto menos, nefasta. \r\n \r\nEscenas de acción, yo he contado 3 en total. La batalla final que pensaba que después de 2 horas de películas iba a levantar algo el ánimo, la hacen de noche. Dos sombras peleando sobre la noche en la que se distingue más bien poco, y la villana apenas luce, básicamente porque no se la distingue. \r\n \r\nLo demás, en spoilers, que hay para rato.'
In [ ]:
corpus_words[:2]
Out[ ]:
[['mundo',
  'destaca',
  'bondades',
  'padrino',
  'har',
  'lista',
  'malo',
  'nico',
  'malo',
  'pel',
  'cula',
  'veas',
  'estar',
  'condenado',
  'vida',
  'revisitarla',
  'frecuentemente',
  'pilles',
  'pping',
  'alguien',
  'diga',
  'apetece',
  'padrino',
  'quedar',
  'pegado',
  'butaca',
  'horas',
  'remedio',
  'cosa',
  'mala',
  'quedas',
  'ganas',
  'cosas',
  'sonny',
  'clemenza',
  'tessio',
  'fredo',
  'barzini',
  'neri',
  'luca',
  'brasi',
  'gigantescos',
  'carism',
  'ticos',
  'personajes',
  'secundarios',
  'pueblan',
  'trama',
  'pel',
  'cula',
  'excusas',
  'verla',
  'auna',
  'cine',
  'calidad',
  'entretenimiento',
  'forma',
  'igualada',
  'empujar',
  'resto',
  'trilog',
  'excelentes',
  'pel',
  'cula',
  'tiempos'],
 ['serie',
  'feeling',
  'adolescentes',
  'mundo',
  'deber',
  'serie',
  'idea',
  'abierta',
  'sexualidad',
  'colegios',
  'fallan',
  'darles',
  'educaci',
  'personajes',
  'perfectamente',
  'amables',
  'especial',
  'protagonista',
  'rezuma',
  'mica',
  'maeve',
  'resto',
  'personajes',
  'terminas',
  'cogiendoles',
  'cari',
  'especial',
  'desarrollo',
  'imprescindible']]

Aplicación de Word2Vec¶

A continuación, se crea el objeto model para crear embeddings en función del corpus creado anteriormente.

In [ ]:
from gensim.models import Word2Vec

model = Word2Vec(sentences=corpus_words,
                 vector_size=50, #Tamaño del embedding
                 window=4, #ventana considerada
                 min_count=2, #Ignora todas las palabras con un número menor a 2
                 sg=1) #con 1 se trabaja con skip-gram

En la siguiente celda se presentán palabras similares a excelente.

In [ ]:
model.wv.similar_by_word('excelente')
Out[ ]:
[('excepcional', 0.9518408179283142),
 ('impresionante', 0.9517847895622253),
 ('soberbia', 0.9479108452796936),
 ('espectacular', 0.947566568851471),
 ('compuesta', 0.946370005607605),
 ('bso', 0.9418780207633972),
 ('destaca', 0.9416521787643433),
 ('estupenda', 0.9385371208190918),
 ('brillante', 0.9384781122207642),
 ('memorable', 0.9345085024833679)]

Keras¶

A continuación, se entrenará un perceptrón multicapa (MLP por sus siglas en inglés) para realizar la clasificación. Para esto, se trabajará con el framework keras.

Observación

En general, el entrenamiento de redes neuronales requiere GPUs. Sin embargo, este ejemplo está diseñado para trabajar solo con CPU. Considerar que el uso de GPU es por tiempo limitado.

Más información en: https://keras.io/

In [ ]:
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

Para el correcto funcionamiento de la red es necesario asegurar que el largo de las oraciones es el mismo. Esto se realiza mediante el proceso de padding.

Como ejemplo, supongamos que se limita el largo a 6 palabras:

  • quiero aprender inteligencia artificial [PAD] [PAD]
  • quiero aprender inteligencia artificial para entrenar modelos de lenguaje

Padding¶

In [ ]:
max_tokens = 50 #Máximo de tokens por secuencia
tokenizer = Tokenizer()
tokenizer.fit_on_texts(X_train)

X_train_vect = pad_sequences(tokenizer.texts_to_sequences(X_train.to_list()), maxlen=max_tokens, padding="post", truncating="post")
X_val_vect  = pad_sequences(tokenizer.texts_to_sequences(X_val.to_list()), maxlen=max_tokens, padding="post", truncating="post")
X_test_vect  = pad_sequences(tokenizer.texts_to_sequences(X_test.to_list()), maxlen=max_tokens, padding="post", truncating="post")

print(f"train: {X_train_vect.shape} \n val: {X_val_vect.shape} \n test: {X_test_vect.shape}")
train: (3840, 50) 
 val: (480, 50) 
 test: (480, 50)

A continuación, se observa un ejemplo de lo que hace el tokenizador.

In [ ]:
tokenizer.texts_to_sequences(["mundo destaca bondades padrino haré lista"])
Out[ ]:
[[9, 654, 12978, 582, 5631, 865]]

A continuación, se presenta la asignación de valores enteros a cada palabra.

In [ ]:
tokenizer.index_word
Out[ ]:
{1: '\r',
 2: 'película',
 3: 'historia',
 4: 'cine',
 5: 'personajes',
 6: 'películas',
 7: 'vida',
 8: 'serie',
 9: 'mundo',
 10: 'años',
 11: 'film',
 12: 'obra',
 13: 'forma',
 14: 'personaje',
 15: 'visto',
 16: 'director',
 17: 'tiempo',
 18: 'guión',
 19: 'escenas',
 20: 'momento',
 21: 'espectador',
 22: 'cosas',
 23: 'actores',
 24: 'cinta',
 25: 'escena',
 26: 'tipo',
 27: 'gente',
 28: 'protagonista',
 29: 'crítica',
 30: 'acción',
 31: 'momentos',
 32: 'trama',
 33: 'humor',
 34: 'sentido',
 35: 'cosa',
 36: 'minutos',
 37: 'original',
 38: 'amor',
 39: 'the',
 40: 'caso',
 41: 'mala',
 42: 'peli',
 43: 'punto',
 44: 'música',
 45: 'deja',
 46: 'efectos',
 47: 'lugar',
 48: 'terror',
 49: 'trabajo',
 50: 'realidad',
 51: 'comedia',
 52: 'queda',
 53: 'resulta',
 54: 'único',
 55: 'verla',
 56: 'idea',
 57: 'realmente',
 58: 'papel',
 59: 'falta',
 60: 'banda',
 61: 'hombre',
 62: 'interesante',
 63: 'argumento',
 64: 'protagonistas',
 65: 'sonora',
 66: 'hora',
 67: 'mujer',
 68: 'arte',
 69: 'género',
 70: 'duda',
 71: 'saga',
 72: 'pasa',
 73: 'mejores',
 74: 'público',
 75: 'diálogos',
 76: 'gusta',
 77: 'pantalla',
 78: 'gracias',
 79: 'familia',
 80: 'cámara',
 81: 'ritmo',
 82: 'nota',
 83: 'disney',
 84: 'alguien',
 85: 'casa',
 86: 'niños',
 87: 'fotografía',
 88: 'personas',
 89: 'resto',
 90: 'madre',
 91: 'llega',
 92: 'l',
 93: 'principio',
 94: 'estilo',
 95: 'actor',
 96: 'gracia',
 97: 'ejemplo',
 98: 'pena',
 99: 'muestra',
 100: 'animación',
 101: 'situaciones',
 102: 'viendo',
 103: 'pasar',
 104: 'especiales',
 105: 'allá',
 106: 'dirección',
 107: 'tema',
 108: 'planos',
 109: 'vista',
 110: 'historias',
 111: 'sociedad',
 112: 'cabeza',
 113: 'llegar',
 114: 'miedo',
 115: 'calidad',
 116: 'malo',
 117: 'maestra',
 118: 'padre',
 119: 'media',
 120: 'nivel',
 121: 'simplemente',
 122: 'año',
 123: 'horas',
 124: 'totalmente',
 125: 'críticas',
 126: 'muerte',
 127: 'imágenes',
 128: 'spoiler',
 129: 'general',
 130: 'resultado',
 131: 'paso',
 132: 'ficción',
 133: 'niño',
 134: 'pensar',
 135: 'etc',
 136: 'real',
 137: 'persona',
 138: 'reparto',
 139: 'guion',
 140: 'interpretación',
 141: 'interpretaciones',
 142: 'humano',
 143: 'drama',
 144: 'difícil',
 145: 'época',
 146: 'mensaje',
 147: 'siquiera',
 148: 'gusto',
 149: 'merece',
 150: 'especial',
 151: 'sensación',
 152: 'amigos',
 153: 'éxito',
 154: 'cara',
 155: 'producto',
 156: 'fondo',
 157: 'hubiera',
 158: 'actuaciones',
 159: 'filme',
 160: 'entrega',
 161: 'opinión',
 162: 'fácil',
 163: 'principal',
 164: 'contar',
 165: 'entretenida',
 166: 'violencia',
 167: 'mierda',
 168: 'digo',
 169: 'problema',
 170: 'veo',
 171: 'simple',
 172: 'libro',
 173: 'joven',
 174: 'negro',
 175: 'mayoría',
 176: 'dios',
 177: 'importante',
 178: 'hablar',
 179: 'excelente',
 180: 'imposible',
 181: 'vuelve',
 182: 'partes',
 183: 'ciencia',
 184: 'desarrollo',
 185: 'ganas',
 186: 'pone',
 187: 'hijo',
 188: 'capaz',
 189: 'visual',
 190: 'guerra',
 191: 'metraje',
 192: 'plano',
 193: 'única',
 194: 'definitiva',
 195: 'entender',
 196: 'belleza',
 197: 'relación',
 198: 'mano',
 199: 'producción',
 200: 'universo',
 201: 'par',
 202: 'dinero',
 203: 'genial',
 204: 'dejar',
 205: 'elementos',
 206: 'seguir',
 207: 'quiero',
 208: 'juego',
 209: 'acaba',
 210: 'señor',
 211: 'ojos',
 212: 'título',
 213: 'camino',
 214: 'rato',
 215: 'disfrutar',
 216: 'interés',
 217: 'mente',
 218: 'debería',
 219: 'temporada',
 220: 'palabras',
 221: 'fuerza',
 222: 'cuento',
 223: 'seguro',
 224: 'serio',
 225: 'viaje',
 226: 'viene',
 227: 'grupo',
 228: 'vemos',
 229: 'pese',
 230: 'absurdo',
 231: 'aburrida',
 232: 'jamás',
 233: 'james',
 234: 'vale',
 235: 'tensión',
 236: 'contrario',
 237: 'luz',
 238: 'versión',
 239: 'tiempos',
 240: 'temas',
 241: 'hija',
 242: 'situación',
 243: 'busca',
 244: 'obras',
 245: 'atención',
 246: 'musical',
 247: 'funciona',
 248: 'sale',
 249: 'sexo',
 250: 'series',
 251: 'absolutamente',
 252: 'recuerdo',
 253: 'crear',
 254: 'alma',
 255: 'favor',
 256: 'montaje',
 257: 'clásico',
 258: 'amigo',
 259: 'entretenimiento',
 260: 'episodio',
 261: 'michael',
 262: 'malos',
 263: 'logra',
 264: 'chica',
 265: 'carrera',
 266: 'salir',
 267: 'conclusión',
 268: 'documental',
 269: 'país',
 270: 'tono',
 271: 'personal',
 272: 'divertida',
 273: 'imagen',
 274: 'mitad',
 275: 'relato',
 276: 'vivir',
 277: 'actriz',
 278: 'presenta',
 279: 'sala',
 280: 'ofrece',
 281: 'llena',
 282: 'razón',
 283: 'intenta',
 284: 'parecido',
 285: 'completamente',
 286: 'social',
 287: 'secuencias',
 288: 'iba',
 289: 'aventuras',
 290: 'puntos',
 291: 'david',
 292: 'empieza',
 293: 'detalles',
 294: 'sangre',
 295: 'chistes',
 296: 'ocurre',
 297: 'ocasiones',
 298: 'aspectos',
 299: 'puro',
 300: 'adaptación',
 301: 'noche',
 302: 'perfectamente',
 303: 'tierra',
 304: 'aparece',
 305: 'capítulo',
 306: 'puedes',
 307: 'podía',
 308: 'pareja',
 309: 'lejos',
 310: 'escrito',
 311: 'tanta',
 312: 'inteligente',
 313: 'encontrar',
 314: 'novela',
 315: 'corazón',
 316: 'parecen',
 317: 'voz',
 318: 'actuación',
 319: 'anteriores',
 320: 'convierte',
 321: 'basura',
 322: 'especie',
 323: 'ideas',
 324: 'destacar',
 325: 'espacio',
 326: 'base',
 327: 'mezcla',
 328: 'talento',
 329: 'espectadores',
 330: 'infantil',
 331: 'volver',
 332: 'hollywood',
 333: 'humana',
 334: 'mujeres',
 335: 'entiendo',
 336: 'sentir',
 337: 'parecer',
 338: 'cambio',
 339: 'canciones',
 340: 'experiencia',
 341: 'gustado',
 342: 'visión',
 343: 'sueño',
 344: 'blanco',
 345: 'clase',
 346: 'problemas',
 347: 'puesto',
 348: 'tienes',
 349: 'absoluto',
 350: 'hechos',
 351: 'recomiendo',
 352: 'dura',
 353: 'hombres',
 354: 'star',
 355: 'perfecto',
 356: 'sorpresa',
 357: 'actual',
 358: 'mínimo',
 359: 'adolescentes',
 360: 'darle',
 361: 'cuerpo',
 362: 'esperaba',
 363: 'cualquiera',
 364: 'intento',
 365: 'risa',
 366: 'ciudad',
 367: 'john',
 368: 'propuesta',
 369: 'recomendable',
 370: 'medida',
 371: 'pequeño',
 372: 'empezar',
 373: 'objetivo',
 374: 'humanos',
 375: 'capítulos',
 376: 'pretende',
 377: 'nombre',
 378: 'secuela',
 379: 'pelis',
 380: 'pobre',
 381: 'normal',
 382: 'dando',
 383: 'cinematográfico',
 384: 'episodios',
 385: 'aspecto',
 386: 'supone',
 387: 'especialmente',
 388: 'precisamente',
 389: 'puesta',
 390: 'presencia',
 391: 'estética',
 392: 'habría',
 393: 'movie',
 394: 'videojuego',
 395: 'hablando',
 396: 'perfecta',
 397: 'futuro',
 398: 'millones',
 399: 'aparte',
 400: 'secuencia',
 401: 'importa',
 402: 'larga',
 403: 'ambientación',
 404: 'guionista',
 405: 'aventura',
 406: 'manos',
 407: 'malas',
 408: 'fans',
 409: 'ocasión',
 410: 'naturaleza',
 411: 'narrativa',
 412: 'hecha',
 413: 'culto',
 414: 'tantos',
 415: 'supongo',
 416: 'lynch',
 417: 'pasando',
 418: 'humanidad',
 419: 'jóvenes',
 420: 'brillante',
 421: 'absurda',
 422: 'com',
 423: 'parodia',
 424: 'festival',
 425: 'lucha',
 426: 'capacidad',
 427: 'lleno',
 428: 'boca',
 429: 'cinematográfica',
 430: 'familiar',
 431: 'infancia',
 432: 'pareció',
 433: 'has',
 434: 'secundarios',
 435: 'frente',
 436: 'mostrar',
 437: 'perder',
 438: 'presupuesto',
 439: 'presente',
 440: 'trilogía',
 441: 'televisión',
 442: 'hijos',
 443: 'libertad',
 444: 'termina',
 445: 'siglo',
 446: 'necesario',
 447: 'sentimientos',
 448: 'edad',
 449: 'genio',
 450: 'leído',
 451: 'pequeños',
 452: 'diferencia',
 453: 'visionado',
 454: 'directamente',
 455: 'autor',
 456: 'thriller',
 457: 'conjunto',
 458: 'cantidad',
 459: 'personalidad',
 460: 'mirada',
 461: 'cabo',
 462: 'vive',
 463: 'padres',
 464: 'dolor',
 465: 'resumen',
 466: 'directores',
 467: 'siento',
 468: 'mil',
 469: 'espectáculo',
 470: 'emociones',
 471: 'narración',
 472: 'risas',
 473: 'aparecen',
 474: 'espectacular',
 475: 'seres',
 476: 'constante',
 477: 'altura',
 478: 'wars',
 479: 'interesantes',
 480: 'vidas',
 481: 'duración',
 482: 'atmósfera',
 483: 'conseguido',
 484: 'vergüenza',
 485: 'quieres',
 486: 'encuentro',
 487: 'mira',
 488: 'pelo',
 489: 'suele',
 490: 'fuerte',
 491: 'bodrio',
 492: 'toma',
 493: 'cabe',
 494: 'haga',
 495: 'finalmente',
 496: 'oscar',
 497: 'pasan',
 498: 'mantiene',
 499: 'contenido',
 500: 'pequeña',
 501: 'magia',
 502: 'ayuda',
 503: 'estreno',
 504: 'suficiente',
 505: 'acerca',
 506: 'ridículo',
 507: 'increíble',
 508: 'oportunidad',
 509: 'recuerda',
 510: 'coppola',
 511: 'seguramente',
 512: 'absoluta',
 513: 'teniendo',
 514: 'justo',
 515: 'suerte',
 516: 'venganza',
 517: 'agua',
 518: 'semejante',
 519: 'adolescente',
 520: 'línea',
 521: 'creer',
 522: 'esencia',
 523: 'color',
 524: 'colores',
 525: 'basada',
 526: 'leer',
 527: 'elenco',
 528: 'reflexión',
 529: 'impresionante',
 530: 'sonido',
 531: 'canción',
 532: 'espera',
 533: 'cronenberg',
 534: 'kubrick',
 535: 'enorme',
 536: 'maravillosa',
 537: 'intentar',
 538: 'valor',
 539: 'siente',
 540: 'maravilla',
 541: 'cuestión',
 542: 'gustan',
 543: 'espero',
 544: 'sucede',
 545: 'dirigida',
 546: 'diseño',
 547: 'detalle',
 548: 'comienzo',
 549: 'tío',
 550: 'inteligencia',
 551: 'perdido',
 552: 'muchísimo',
 553: 'prácticamente',
 554: 'probablemente',
 555: 'hacía',
 556: 'tantas',
 557: 'interior',
 558: 'comercial',
 559: 'sinceramente',
 560: 'cuya',
 561: 'reales',
 562: 'notable',
 563: 'perfección',
 564: 'pueblo',
 565: 'palabra',
 566: 'magnífica',
 567: 'valores',
 568: 'magistral',
 569: 'vas',
 570: 'misterio',
 571: 'comienza',
 572: 'drogas',
 573: 'bella',
 574: 'llamado',
 575: 'pelicula',
 576: 'niña',
 577: 'argumental',
 578: 'ves',
 579: 'atrás',
 580: 'cuyo',
 581: 'anderson',
 582: 'padrino',
 583: 'finales',
 584: 'españa',
 585: 'chico',
 586: 'esperar',
 587: 'ven',
 588: 'paul',
 589: 'reír',
 590: 'americano',
 591: 'extraña',
 592: 'adultos',
 593: 'estás',
 594: 'entrar',
 595: 'gore',
 596: 'casos',
 597: 'proyecto',
 598: 'encontramos',
 599: 'respeto',
 600: 'originalidad',
 601: 'oscuro',
 602: 'moda',
 603: 'inicio',
 604: 'pierde',
 605: 'sirve',
 606: 'dudas',
 607: 'sueños',
 608: 'llegado',
 609: 'gags',
 610: 'tom',
 611: 'completo',
 612: 'referencias',
 613: 'sexual',
 614: 'hubiese',
 615: 'acierto',
 616: 'amistad',
 617: 'sacar',
 618: 'movimiento',
 619: 'aire',
 620: 'sistema',
 621: 'mayores',
 622: 'moral',
 623: 'primeras',
 624: 'espíritu',
 625: 'esté',
 626: 'habitual',
 627: 'toque',
 628: 'frases',
 629: 'alto',
 630: 'pasión',
 631: 'desenlace',
 632: 'emoción',
 633: 'carne',
 634: 'caer',
 635: 'pregunta',
 636: 'fan',
 637: 'salvar',
 638: 'tarkovsky',
 639: 'error',
 640: 'esperanza',
 641: 'pura',
 642: 'importancia',
 643: 'ojo',
 644: 'quieren',
 645: 'acabar',
 646: 'común',
 647: 'ambas',
 648: 'aporta',
 649: 'necesita',
 650: 'lenta',
 651: 'narrativo',
 652: 'produce',
 653: 'cultura',
 654: 'destaca',
 655: 'podido',
 656: 'pienso',
 657: 'formas',
 658: 'necesidad',
 659: 'brutal',
 660: 'largometraje',
 661: 'particular',
 662: 'querer',
 663: 'dejan',
 664: 'policía',
 665: 'sorprendente',
 666: 'pixar',
 667: 'español',
 668: 'clara',
 669: 'auténtica',
 670: 'sigo',
 671: 'intención',
 672: 'cambiar',
 673: 'creíble',
 674: 'profundidad',
 675: 'maestro',
 676: 'títulos',
 677: 'libros',
 678: 'quedado',
 679: 'minuto',
 680: 'cumple',
 681: 'smith',
 682: 'desarrolla',
 683: 'monstruos',
 684: 'existencia',
 685: 'divertido',
 686: 'principales',
 687: 'papeles',
 688: 'ambiente',
 689: 'previsible',
 690: 'jones',
 691: 'relaciones',
 692: 'fantasía',
 693: 'quiera',
 694: 'extraño',
 695: 'films',
 696: 'permite',
 697: 'mental',
 698: 'buscar',
 699: 'quedan',
 700: 'guste',
 701: 'peores',
 702: 'artística',
 703: 'porqué',
 704: 'crea',
 705: 'premisa',
 706: 'a',
 707: 'imaginación',
 708: 'considero',
 709: 'duro',
 710: 'horror',
 711: 'diría',
 712: 'hará',
 713: 'gana',
 714: 'efecto',
 715: 'montón',
 716: 'terminar',
 717: 'siguen',
 718: 'cineasta',
 719: 'decide',
 720: 'entorno',
 721: 'sentidos',
 722: 'típico',
 723: 'vacío',
 724: 'guionistas',
 725: 'tópicos',
 726: 'gustó',
 727: 'escribir',
 728: 'spielberg',
 729: 'hermano',
 730: 'claramente',
 731: 'warren',
 732: 'escenario',
 733: 'clásicos',
 734: 'robert',
 735: 'técnica',
 736: 'salen',
 737: 'sucesión',
 738: 'mantener',
 739: 'corto',
 740: 'material',
 741: 'vuelta',
 742: 'obstante',
 743: 'tomar',
 744: 'auténtico',
 745: 'sorprende',
 746: 'dejando',
 747: 'comedias',
 748: 'realista',
 749: 'personalmente',
 750: 'generación',
 751: 'profesor',
 752: 'carisma',
 753: 'monstruo',
 754: 'intriga',
 755: 'doy',
 756: 'puta',
 757: 'críticos',
 758: 'musicales',
 759: 'fiel',
 760: 'equipo',
 761: 'demuestra',
 762: 'plan',
 763: 'esfuerzo',
 764: 'concepto',
 765: 'mundial',
 766: 'emocional',
 767: 'héroe',
 768: 'cuesta',
 769: 'curioso',
 770: 'piel',
 771: 'parecía',
 772: 'artista',
 773: 'filmaffinity',
 774: 'sabor',
 775: 'consecuencias',
 776: 'origen',
 777: 'triste',
 778: 'figura',
 779: 'quería',
 780: 'amantes',
 781: 'sustos',
 782: 'profunda',
 783: 'vestuario',
 784: 'hayan',
 785: 'memoria',
 786: 'encanta',
 787: 'cutre',
 788: 'mirar',
 789: 'estructura',
 790: 'contexto',
 791: 'animales',
 792: 'culo',
 793: 'off',
 794: 'evolución',
 795: 'dosis',
 796: 'libre',
 797: 'productos',
 798: 'similar',
 799: 'cae',
 800: 'escenarios',
 801: 'estudio',
 802: 'silencio',
 803: 'conoce',
 804: 'suspense',
 805: 'reconocer',
 806: 'debo',
 807: 'causa',
 808: 'aparición',
 809: 'taquilla',
 810: 'búsqueda',
 811: 'explicar',
 812: 'agradable',
 813: 'hilo',
 814: 'basa',
 815: 'bonita',
 816: 'pensando',
 817: 'sencillamente',
 818: 'destino',
 819: 'recursos',
 820: 'planeta',
 821: 'retrato',
 822: 'zona',
 823: 'resultar',
 824: 'olvidar',
 825: 'carácter',
 826: 'culpa',
 827: 'peso',
 828: 'evil',
 829: 'marvel',
 830: 'pasó',
 831: 'ajena',
 832: 'fuese',
 833: 'directora',
 834: 'política',
 835: 'doble',
 836: 'miles',
 837: 'cintas',
 838: 'conocido',
 839: 'llevan',
 840: 'spoilers',
 841: 'segura',
 842: 'giros',
 843: 'entra',
 844: 'ponen',
 845: 'cruella',
 846: 'azul',
 847: 'fascinante',
 848: 'sensaciones',
 849: 'ausencia',
 850: 'frase',
 851: 'pie',
 852: 'recuerdos',
 853: 'franquicia',
 854: 'evidente',
 855: 'reina',
 856: 'hermanos',
 857: 'respuesta',
 858: 'verano',
 859: 'chicas',
 860: 'tontos',
 861: 'razones',
 862: 'mérito',
 863: 'década',
 864: 'géneros',
 865: 'lista',
 866: 'sabía',
 867: 'aburrimiento',
 868: 'expectativas',
 869: 'transmitir',
 870: 'romántica',
 871: 'interpreta',
 872: 'sociales',
 873: 'llama',
 874: 'muertos',
 875: 'homenaje',
 876: 'gustar',
 877: 'motivo',
 878: 'torrente',
 879: 'elemento',
 880: 'luces',
 881: 'hacerse',
 882: 'justicia',
 883: 'convencional',
 884: 'deseo',
 885: 'recordar',
 886: 'dejado',
 887: 'acto',
 888: 'campo',
 889: 'segundos',
 890: 'menor',
 891: 'sonrisa',
 892: 'típica',
 893: 'villano',
 894: 'teatro',
 895: 'natural',
 896: 'poesía',
 897: 'mañana',
 898: 'fantástico',
 899: 'lucas',
 900: 'espejo',
 901: 'originales',
 902: 'locura',
 903: 'henry',
 904: 'producciones',
 905: 'trabajos',
 906: 'pongo',
 907: 'evitar',
 908: 'parezca',
 909: 'créditos',
 910: 'entiende',
 911: 'tonto',
 912: 'impresión',
 913: 'bill',
 914: 'técnico',
 915: 'asco',
 916: 'convertido',
 917: 'jackson',
 918: 'identidad',
 919: 'podríamos',
 920: 'sentimiento',
 921: 'corta',
 922: 'discurso',
 923: 'sencilla',
 924: 'superior',
 925: 'importantes',
 926: 'crisis',
 927: 'ido',
 928: 'creado',
 929: 'distintos',
 930: 'www',
 931: 'marido',
 932: 'estrella',
 933: 'predecible',
 934: 'errores',
 935: 'cuarta',
 936: 'comprender',
 937: 'imprescindible',
 938: 'jurado',
 939: 'pesadilla',
 940: 'bazofia',
 941: 'convertirse',
 942: 'doblaje',
 943: 'alta',
 944: 'plantea',
 945: 'rey',
 946: 'unidos',
 947: 'visuales',
 948: 'lógica',
 949: 'wan',
 950: 'pensé',
 951: 'resident',
 952: 'sobretodo',
 953: 'transmite',
 954: 'sublime',
 955: 'ejercicio',
 956: 'oro',
 957: 'entretiene',
 958: 'clave',
 959: 'avanza',
 960: 'dibujos',
 961: 'bosque',
 962: 'secuelas',
 963: 'únicamente',
 964: 'basta',
 965: 'mención',
 966: 'llamada',
 967: 'básicamente',
 968: 'suena',
 969: 'jugar',
 970: 'don',
 971: 'rollo',
 972: 'vueltas',
 973: 'llegan',
 974: 'aronofsky',
 975: 'personales',
 976: 'virtudes',
 977: 'tiburones',
 978: 'carga',
 979: 'acaso',
 980: 'mediocre',
 981: 'comer',
 982: 'narra',
 983: 'entretenido',
 984: 'conciencia',
 985: 'abajo',
 986: 'lee',
 987: 'exactamente',
 988: 'creación',
 989: 'hermosa',
 990: 'capaces',
 991: 'cree',
 992: 'expresión',
 993: 'coche',
 994: 'raro',
 995: 'soledad',
 996: 'negra',
 997: 'experimento',
 998: 'pies',
 999: 'lugares',
 1000: 'tarantino',
 ...}

Matriz embeddings¶

A continuación, se crea la matriz de embeddings.

In [ ]:
import numpy as np
vectors_w2v = np.asarray(model.wv.vectors)
labels_w2v = np.asarray(model.wv.index_to_key)
In [ ]:
#esta celda demora 1 minuto

embed_len = 50 #Dimensión de embedding

w2v_embeddings = np.zeros((len(tokenizer.index_word)+1, embed_len)) # Notar que se suma 1 por un token especial que necesita keras para asignar a las palabras desconocidas.

for idx, word in tokenizer.index_word.items():
    if word in labels_w2v:
        w2v_embeddings[idx] = vectors_w2v[int(np.where(labels_w2v == word)[0][0])]
In [ ]:
w2v_embeddings.shape
Out[ ]:
(51106, 50)

Cada fila de esta matriz corresponde a una palabra del vocabulario, la cual tiene una representación vectorial de dimensión 50. Recordar que la dimensionalidad del embedding es un hiperparámetro definido por el modelador.

image.png

Entrenar MLP¶

In [ ]:
# import tensorflow
In [ ]:
from tensorflow.keras.layers import Input, Embedding, Dense, GlobalAveragePooling1D
from tensorflow.keras.models import Model

inputs = Input(shape=(max_tokens,))

embeddings_layer = Embedding(
    input_dim=len(tokenizer.index_word)+1,
    output_dim=embed_len,
    trainable=False,
    weights=[w2v_embeddings]
)

dense1 = Dense(256, activation="relu")
dense2 = Dense(64, activation="relu")
dense3 = Dense(256, activation="relu") #no se utiliza
dense4 = Dense(2, activation="softmax") # depende de los posibles valores de la variable de respuesta. En este caso es una tarea binaria.

x = embeddings_layer(inputs)
x = GlobalAveragePooling1D()(x)  # Esta función permite agregar las representaciones vectoriales de cada palabra que compone una frase
x = dense1(x)
x = dense2(x)
z = dense3(x) #estas capas se puede incluir o no en la arquitectura
outputs = dense4(x) # notar que esto la salida de "dense 2"

model = Model(inputs=inputs, outputs=outputs)

A continuación, se puede observar el detalle de los parámetros de la red creada.

In [ ]:
model.summary()
Model: "functional_2"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ input_layer_2 (InputLayer)      │ (None, 50)             │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ embedding_2 (Embedding)         │ (None, 50, 50)         │     2,555,300 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ global_average_pooling1d_2      │ (None, 50)             │             0 │
│ (GlobalAveragePooling1D)        │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_8 (Dense)                 │ (None, 256)            │        13,056 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_9 (Dense)                 │ (None, 64)             │        16,448 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_11 (Dense)                │ (None, 2)              │           130 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 2,584,934 (9.86 MB)
 Trainable params: 29,634 (115.76 KB)
 Non-trainable params: 2,555,300 (9.75 MB)

A continuación, se compila el modelo definiendo el optimizador, función de pérdida y métrica de evaluación.

In [ ]:
model.compile(optimizer="adam", #optimizador
              loss="sparse_categorical_crossentropy", #función de pérdida que se ajusta al problema de clasificación multiclase.
              metrics=["accuracy"]) #métrica de evaluación
In [ ]:
history = model.fit(X_train_vect, # representación vectorial de entrada
                    y_train, # label de salida
                    batch_size=32, #tamaño de lote que será utilizado en cada step
                    epochs=20, #número de épocas
                    validation_data=(X_val_vect, y_val)) # definir el set para validar el entrenamiento
Epoch 1/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - accuracy: 0.5722 - loss: 0.6724 - val_accuracy: 0.6812 - val_loss: 0.5940
Epoch 2/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.6587 - loss: 0.6160 - val_accuracy: 0.7125 - val_loss: 0.5730
Epoch 3/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.6775 - loss: 0.5995 - val_accuracy: 0.6938 - val_loss: 0.5702
Epoch 4/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.6949 - loss: 0.5871 - val_accuracy: 0.7125 - val_loss: 0.5617
Epoch 5/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - accuracy: 0.6983 - loss: 0.5793 - val_accuracy: 0.7167 - val_loss: 0.5568
Epoch 6/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - accuracy: 0.7065 - loss: 0.5779 - val_accuracy: 0.7125 - val_loss: 0.5521
Epoch 7/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.6946 - loss: 0.5895 - val_accuracy: 0.7063 - val_loss: 0.5615
Epoch 8/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.6956 - loss: 0.5832 - val_accuracy: 0.7167 - val_loss: 0.5465
Epoch 9/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7093 - loss: 0.5733 - val_accuracy: 0.6958 - val_loss: 0.5785
Epoch 10/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7026 - loss: 0.5837 - val_accuracy: 0.7312 - val_loss: 0.5490
Epoch 11/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.6999 - loss: 0.5813 - val_accuracy: 0.7271 - val_loss: 0.5401
Epoch 12/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7124 - loss: 0.5739 - val_accuracy: 0.7312 - val_loss: 0.5371
Epoch 13/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7154 - loss: 0.5660 - val_accuracy: 0.7333 - val_loss: 0.5376
Epoch 14/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7102 - loss: 0.5725 - val_accuracy: 0.7229 - val_loss: 0.5450
Epoch 15/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7218 - loss: 0.5594 - val_accuracy: 0.7312 - val_loss: 0.5405
Epoch 16/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.7290 - loss: 0.5474 - val_accuracy: 0.7375 - val_loss: 0.5352
Epoch 17/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7233 - loss: 0.5605 - val_accuracy: 0.7333 - val_loss: 0.5364
Epoch 18/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7208 - loss: 0.5636 - val_accuracy: 0.7417 - val_loss: 0.5300
Epoch 19/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - accuracy: 0.7079 - loss: 0.5721 - val_accuracy: 0.7396 - val_loss: 0.5377
Epoch 20/20
120/120 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.7264 - loss: 0.5480 - val_accuracy: 0.7292 - val_loss: 0.5425
In [ ]:
print(history.history.keys())
dict_keys(['accuracy', 'loss', 'val_accuracy', 'val_loss'])
In [ ]:
# Para accuracy
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='upper left')
plt.show()
# Para función de pérdida
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='upper left')
plt.show()
No description has been provided for this image
No description has been provided for this image

Métricas de evaluación¶

Se obtienen las predicciones.

In [ ]:
y_test = np.asarray(y_test)
y_pred = model.predict(X_test_vect).argmax(axis=-1)
15/15 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step 
In [ ]:
y_test
Out[ ]:
array([0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0,
       1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0,
       1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1,
       1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
       0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0,
       0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1,
       0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0,
       0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
       0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0,
       1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1,
       1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0,
       1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
       1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1,
       0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1,
       0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0,
       1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
       1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0,
       1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0,
       0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
       0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0])
In [ ]:
y_pred
Out[ ]:
array([0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0,
       1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0,
       1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
       1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1,
       0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1,
       0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1,
       0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0,
       0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0,
       1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1,
       0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
       1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
       1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0,
       1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0,
       1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1,
       1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1,
       0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1,
       1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
       1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
       1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0,
       1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0,
       0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1,
       1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0])

Se calculan las métricas de evaluación.

In [ ]:
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix

print(classification_report(y_test, y_pred))
              precision    recall  f1-score   support

           0       0.76      0.69      0.72       248
           1       0.70      0.77      0.73       232

    accuracy                           0.73       480
   macro avg       0.73      0.73      0.73       480
weighted avg       0.73      0.73      0.73       480

Trabajar con modelos pre-entrenados¶

Importar usando Gensim¶

Ver sitio: https://radimrehurek.com/gensim/models/word2vec.html

In [ ]:
import gensim.downloader
print(list(gensim.downloader.info()['models'].keys()))
['fasttext-wiki-news-subwords-300', 'conceptnet-numberbatch-17-06-300', 'word2vec-ruscorpora-300', 'word2vec-google-news-300', 'glove-wiki-gigaword-50', 'glove-wiki-gigaword-100', 'glove-wiki-gigaword-200', 'glove-wiki-gigaword-300', 'glove-twitter-25', 'glove-twitter-50', 'glove-twitter-100', 'glove-twitter-200', '__testing_word2vec-matrix-synopsis']

Se trabajará con glove-wiki-gigaword-50

In [ ]:
glove_wiki_50 = gensim.downloader.load('glove-wiki-gigaword-50') #tiene un tamaño de 66 MB.
[==================================================] 100.0% 66.0/66.0MB downloaded
In [ ]:
glove_wiki_50.similar_by_word('peace')
Out[ ]:
[('reconciliation', 0.9170552492141724),
 ('unity', 0.850257396697998),
 ('talks', 0.8138390779495239),
 ('negotiations', 0.8107652068138123),
 ('progress', 0.8098476529121399),
 ('accord', 0.8095232248306274),
 ('ceasefire', 0.8048483729362488),
 ('commitment', 0.7958491444587708),
 ('truce', 0.7929693460464478),
 ('peaceful', 0.7838729619979858)]
In [ ]:
import numpy as np
vectors_glove_50 = np.asarray(glove_wiki_50.vectors)
labels_glove_50 = np.asarray(glove_wiki_50.index_to_key)
In [ ]:
labels_glove_50.shape
Out[ ]:
(400000,)
In [ ]:
vectors_glove_50.shape
Out[ ]:
(400000, 50)