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 MendozaAyudantía 2: Word2Vec
Ayudante: Miguel Fernández
Instalar librerías¶
Para trabajar con el modelo word2vec, es necesario instalar la siguiente librería.
!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.
import pandas as pd
df = pd.read_csv("/content/film_affinity.csv")
df
| 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.
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()
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
!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.
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).
df["text_clean"] = df["critica"].apply(preprocessing)
Se crea la variable binaria sentiment considerando la nota asignada. Esta columna se incorpora al dataframe.
df["sentiment"] = df.apply(lambda x: 0 if int(x["nota"]) < 5 else 1, axis=1)
df.head()
| 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.
df[df["sentiment"]==0].nota.value_counts()
| count | |
|---|---|
| nota | |
| 1 | 674 |
| 3 | 609 |
| 4 | 576 |
| 2 | 541 |
df[df["sentiment"]==1].nota.value_counts()
| count | |
|---|---|
| nota | |
| 7 | 717 |
| 8 | 648 |
| 10 | 561 |
| 9 | 474 |
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()
Creación train-test-val¶
Se crean 3 conjuntos de datos:
- train (80%)
- val (10%)
- test (10%)
df.shape
(4800, 5)
from sklearn.model_selection import train_test_split
X = df["text_clean"]
y = df["sentiment"]
#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)
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
import nltk
nltk.download('punkt_tab')
[nltk_data] Downloading package punkt_tab to /root/nltk_data... [nltk_data] Unzipping tokenizers/punkt_tab.zip.
True
La siguiente función permite tokenizar cada sentencia del dataset.
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
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)
df.critica[0]
'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.'
corpus_words[:2]
[['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.
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.
model.wv.similar_by_word('excelente')
[('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/
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¶
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.
tokenizer.texts_to_sequences(["mundo destaca bondades padrino haré lista"])
[[9, 654, 12978, 582, 5631, 865]]
A continuación, se presenta la asignación de valores enteros a cada palabra.
tokenizer.index_word
{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.
import numpy as np
vectors_w2v = np.asarray(model.wv.vectors)
labels_w2v = np.asarray(model.wv.index_to_key)
#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])]
w2v_embeddings.shape
(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.
Entrenar MLP¶
# import tensorflow
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.
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.
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
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
print(history.history.keys())
dict_keys(['accuracy', 'loss', 'val_accuracy', 'val_loss'])
# 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()
Métricas de evaluación¶
Se obtienen las predicciones.
y_test = np.asarray(y_test)
y_pred = model.predict(X_test_vect).argmax(axis=-1)
15/15 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
y_test
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]) y_pred
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.
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¶
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
glove_wiki_50 = gensim.downloader.load('glove-wiki-gigaword-50') #tiene un tamaño de 66 MB.
[==================================================] 100.0% 66.0/66.0MB downloaded
glove_wiki_50.similar_by_word('peace')
[('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)] import numpy as np
vectors_glove_50 = np.asarray(glove_wiki_50.vectors)
labels_glove_50 = np.asarray(glove_wiki_50.index_to_key)
labels_glove_50.shape
(400000,)
vectors_glove_50.shape
(400000, 50)