Ambos producen embeddings de calidad comparable. GloVe tiende a ser mejor con corpus grandes y Word2Vec con corpus pequeños. La elección suele depender de la disponibilidad de modelos pre-entrenados.
Bloque 3: FastText — Más Allá de las Palabras
El Problema de las Palabras Desconocidas (OOV)
El escenario
Entrenas Word2Vec o GloVe con un corpus. Luego aparece una palabra nueva:
“desconfigurar” — no estaba en el vocabulario
“electrodomésticos” — tampoco
“bolivianísimo” — menos
El resultado
modelo_w2v["desconfigurar"]# KeyError: "word 'desconfigurar' not in vocabulary"
😱 ¡No hay vector!
¿Por qué es grave?
Los idiomas son productivos: siempre hay palabras nuevas
Errores ortográficos: “computdora” vs. “computadora”
El entrenamiento usa Negative Sampling igual que Word2Vec, solo cambia cómo se computa el score.
Ejemplo Práctico: Vocabulario OOV
# Demostración con n-gramas de caracteresdef extraer_ngrams(palabra, min_n=3, max_n=6):"""Extraer n-gramas de caracteres de una palabra.""" palabra_ext =f"<{palabra}>" ngrams = []for n inrange(min_n, max_n +1):for i inrange(len(palabra_ext) - n +1): ngrams.append(palabra_ext[i:i+n])return ngrams# Ejemplofor palabra in ["gato", "gatos", "gata", "gatuno"]: ngrams = extraer_ngrams(palabra, min_n=3, max_n=4)print(f"'{palabra}' → {ngrams}")
Para tareas en español, recomiendo los embeddings de FastText pre-entrenados en Wikipedia + Common Crawl (disponibles en 157 idiomas). Son los más versátiles por su manejo de OOV.
# Palabras que NO están en el vocabulario de entrenamientopalabras_oov = ["gatos", "gatito", "perrito", "comida", "fresquito"]print("Vectores para palabras OOV (inferidos por subpalabras):")print("-"*55)for palabra in palabras_oov: en_vocab = palabra in modelo_ft.wv.key_to_index vec = modelo_ft.wv[palabra] # ¡No da error! norma = np.linalg.norm(vec)print(f" '{palabra:12s}' en vocab: {str(en_vocab):5s} "f"norma = {norma:.3f} primeros 3 dims: {vec[:3].round(3)}")
Vectores para palabras OOV (inferidos por subpalabras):
-------------------------------------------------------
'gatos ' en vocab: False norma = 0.087 primeros 3 dims: [-0.012 0.013 -0.025]
'gatito ' en vocab: False norma = 0.049 primeros 3 dims: [-0.007 0.008 -0.014]
'perrito ' en vocab: False norma = 0.070 primeros 3 dims: [-0.017 0.011 -0.021]
'comida ' en vocab: False norma = 0.032 primeros 3 dims: [-0.007 0.004 -0.01 ]
'fresquito ' en vocab: False norma = 0.037 primeros 3 dims: [-0.005 0.002 -0.009]
# Similitud de palabras OOV con palabras del vocabularioprint("\nSimilitud de palabras OOV con 'gato':")for palabra in ["gatos", "gatito", "perrito", "comida"]: sim = modelo_ft.wv.similarity("gato", palabra) barra ="█"*int(max(0, sim) *20)print(f" sim(gato, {palabra:12s}) = {sim:+.4f}{barra}")
from gensim.models import Word2Vec# Re-entrenar Word2Vec con el mismo corpusmodelo_w2v = Word2Vec( sentences=corpus, vector_size=50, window=3, min_count=1, sg=1, negative=5, epochs=200, seed=42)# Comparar palabras más similaresprint("Top 5 palabras más similares a 'gato':")print(f"\n{'Word2Vec':30s}{'FastText':30s}")print("-"*60)sims_w2v = modelo_w2v.wv.most_similar("gato", topn=5)sims_ft = modelo_ft.wv.most_similar("gato", topn=5)for i inrange(5): w2v_str =f"{sims_w2v[i][0]:12s} ({sims_w2v[i][1]:.3f})" ft_str =f"{sims_ft[i][0]:12s} ({sims_ft[i][1]:.3f})"print(f" {w2v_str:30s}{ft_str:30s}")
Top 5 palabras más similares a 'gato':
Word2Vec FastText
------------------------------------------------------------
perro (0.906) duerme (0.986)
el (0.903) el (0.983)
juega (0.897) perro (0.982)
la (0.889) fresca (0.979)
come (0.881) pequeño (0.978)
Observación
Con un corpus tan pequeño las diferencias son sutiles. En corpus grandes, FastText muestra ventajas claras en palabras raras y morfológicamente complejas.
Cargando Embeddings Pre-entrenados
import gensim.downloader as api# ── GloVe pre-entrenado (inglés) ──# Descarga automática desde gensim-data (~66MB para 100d)glove = api.load("glove-wiki-gigaword-100")print("GloVe: palabras similares a 'king'")for w, s in glove.most_similar("king", topn=5):print(f" {w:15s}{s:.4f}")# Analogía: king - man + woman ≈ ?result = glove.most_similar( positive=["king", "woman"], negative=["man"], topn=3)print(f"\nking - man + woman ≈ {result[0][0]} ({result[0][1]:.4f})")# ── FastText pre-entrenado (español) ──# Descargar desde https://fasttext.cc/docs/en/crawl-vectors.html# Archivo: cc.es.300.bin (~4.5GB)from gensim.models import FastTextmodelo_es = FastText.load_fasttext_format("cc.es.300.bin")print("\nFastText (es): similares a 'computadora'")for w, s in modelo_es.wv.most_similar("computadora", topn=5):print(f" {w:15s}{s:.4f}")# ¡Funciona con errores ortográficos!print(f"\nsim(computadora, computdora) = "f"{modelo_es.wv.similarity('computadora', 'computdora'):.4f}")
Modelos disponibles en gensim.downloader
import gensim.downloader as apiprint(list(api.info()['models'].keys()))# ['fasttext-wiki-news-subwords-300', 'glove-twitter-25',# 'glove-wiki-gigaword-50', 'glove-wiki-gigaword-100', ...]
Bloque 6: Evaluación de Embeddings
¿Cómo Sabemos si los Embeddings son Buenos?
Evaluación intrínseca
Mide la calidad de los vectores directamente:
Analogías de palabras
“rey : reina :: hombre : mujer”
Dataset: Google Analogy Test Set
Similitud de palabras
Correlación con juicios humanos
Datasets: WordSim-353, SimLex-999
Categorización
¿Se agrupan bien las palabras por categoría?
Evaluación extrínseca
Mide el impacto en una tarea downstream:
Clasificación de texto
¿Mejora la F1 al usar estos embeddings?
NER (Named Entity Recognition)
¿Se reconocen mejor las entidades?
Análisis de sentimiento
¿Se clasifica mejor la polaridad?
. . .
En la práctica
La evaluación extrínseca es más importante: ¿los embeddings sirven para tu tarea específica?
Dataset de Analogías
# Evaluación con analogías simples usando nuestro modelo pequeñoanalogias = [# (a, b, c, esperado): a es a b como c es a ? ("gato", "gatos", "perro", "perros"), # Dificil sin "gatos"/"perros" en vocab ("niño", "niña", "perro", "gata"), # género ("come", "gato", "bebe", "perro"), # acción-animal]print("Test de analogías (modelo pequeño):")print("-"*60)for a, b, c, esperado in analogias:try: resultado = modelo_ft.wv.most_similar( positive=[b, c], negative=[a], topn=3 ) predicho = resultado[0][0] correcto ="✅"if predicho == esperado else"❌"print(f" {a:8s} : {b:8s} :: {c:8s} : ?")print(f" Esperado: {esperado:10s} Predicho: {predicho:10s} "f"({resultado[0][1]:.3f}) {correcto}")exceptKeyErroras e:print(f" {a:8s} : {b:8s} :: {c:8s} : ? → Palabra no encontrada: {e}")
Con un corpus de 16 oraciones los resultados no serán perfectos. Con un corpus de millones de palabras (como el de Google News), Word2Vec/GloVe/FastText logran ~60-75% en tests de analogías.