La Revolución Vectorial

S2: GloVe y FastText — Embeddings de Subpalabras

Prof. Francisco Suárez

Universidad Católica Boliviana

2026-03-04

Agenda de Hoy

Primera Parte

  1. 🔄 Repaso: Word2Vec y sus limitaciones
  2. 🌐 GloVe: Lo mejor de dos mundos
  3. 📐 La matemática detrás de GloVe

Segunda Parte

  1. 🧩 FastText: Embeddings de subpalabras
  2. 🆚 Comparación: Word2Vec vs. GloVe vs. FastText
  3. 🛠️ Implementación práctica

Bloque 1: Repaso y Motivación

¿Dónde Quedamos?

En la sesión anterior aprendimos Word2Vec:

Lo que logramos

  • ✅ Vectores densos de baja dimensión
  • ✅ Palabras similares → vectores cercanos
  • ✅ Aritmética de palabras (rey - hombre + mujer ≈ reina)
  • ✅ Entrenamiento eficiente con Negative Sampling

Lo que nos falta resolver

  • ❌ Word2Vec usa ventanas locales — no aprovecha estadísticas globales del corpus
  • Una palabra = un vector — no maneja polisemia
  • Palabras fuera del vocabulario (OOV) — si no vio la palabra, no tiene vector

Hoy veremos dos modelos que atacan estos problemas

  • GloVe → Aprovecha estadísticas globales de co-ocurrencia
  • FastText → Resuelve el problema de OOV usando subpalabras

Dos Filosofías para Aprender Embeddings

Métodos basados en conteo

  • Construir una matriz de co-ocurrencia global
  • Aplicar factorización (SVD, NMF)
  • Ejemplo: LSA (Latent Semantic Analysis)

Ventaja: Captura estadísticas globales

Desventaja: Las matrices son enormes y dispersas

Métodos basados en predicción

  • Entrenar una red neuronal para predecir palabras
  • El embedding es un subproducto del entrenamiento
  • Ejemplo: Word2Vec

Ventaja: Captura patrones locales, analogías

Desventaja: Solo ve ventanas locales, no todo el corpus

La pregunta clave

¿Podemos combinar lo mejor de ambos? → ¡Sí! Eso es GloVe.

Bloque 2: GloVe — Global Vectors

GloVe (Pennington et al., 2014)

GloVe = Global Vectors for Word Representation

La idea central

GloVe aprende embeddings que codifican directamente la información de la matriz de co-ocurrencia global.

En vez de predecir palabras vecinas (como Word2Vec), GloVe se pregunta:

¿Puedo aprender vectores tales que su producto punto sea igual al logaritmo de su co-ocurrencia?

Publicación

  • Autores: Jeffrey Pennington, Richard Socher, Christopher Manning (Stanford)
  • Año: 2014
  • Paper: GloVe: Global Vectors for Word Representation (EMNLP 2014)
  • Insight clave: Las razones de co-ocurrencia son más informativas que los conteos brutos

Matriz de Co-ocurrencia

Paso 1: Construir la matriz \(X\)

\(X_{ij}\) = número de veces que la palabra \(j\) aparece en el contexto de la palabra \(i\) (en todo el corpus).

. . .

Ejemplo con ventana \(c = 1\):

Corpus: “el gato come . el perro come . el gato duerme .”

el gato perro come duerme .
el 0 2 1 0 0 0
gato 2 0 0 1 1 0
perro 1 0 0 1 0 0
come 0 1 1 0 0 2
duerme 0 1 0 0 0 1
. 0 0 0 2 1 0

. . .

Note

La matriz es simétrica (\(X_{ij} = X_{ji}\)) si usamos una ventana simétrica.

El Insight Clave: Razones de Co-ocurrencia

Lo que hace a GloVe diferente es que se enfoca en las razones entre probabilidades de co-ocurrencia:

Definimos: \(P_{ij} = P(j \mid i) = \frac{X_{ij}}{X_i}\) donde \(X_i = \sum_k X_{ik}\)

Ejemplo (datos reales de un corpus grande)

Consideremos las palabras hielo y vapor:

Palabra \(k\) \(P(k \mid \text{hielo})\) \(P(k \mid \text{vapor})\) Razón
sólido \(1.9 \times 10^{-4}\) \(2.2 \times 10^{-5}\) 8.9
gas \(6.6 \times 10^{-5}\) \(7.8 \times 10^{-4}\) 0.085
agua \(3.0 \times 10^{-3}\) \(2.2 \times 10^{-3}\) 1.36
moda \(1.7 \times 10^{-5}\) \(1.8 \times 10^{-5}\) 0.96

Interpretación

\[\frac{P(k \mid \text{hielo})}{P(k \mid \text{vapor})}\]

  • Grande (≫ 1): \(k\) está más asociada con “hielo”
  • Pequeña (≪ 1): \(k\) está más asociada con “vapor”
  • Cercana a 1: \(k\) es igualmente relevante (o irrelevante) para ambas

La clave

Las razones de co-ocurrencia discriminan mejor que las probabilidades simples. GloVe aprende vectores que codifican estas razones.

La Función Objetivo de GloVe

GloVe quiere que los vectores aprendidos satisfagan:

\[\vec{w}_i^T \vec{\tilde{w}}_j + b_i + \tilde{b}_j = \log(X_{ij})\]

donde:

  • \(\vec{w}_i\) — vector de la palabra \(i\) (embedding principal)
  • \(\vec{\tilde{w}}_j\) — vector de la palabra \(j\) (embedding de contexto)
  • \(b_i, \tilde{b}_j\) — sesgos (bias) escalares
  • \(X_{ij}\) — conteo de co-ocurrencia

Función de pérdida completa

\[J = \sum_{i,j=1}^{|V|} f(X_{ij}) \left(\vec{w}_i^T \vec{\tilde{w}}_j + b_i + \tilde{b}_j - \log X_{ij}\right)^2\]

. . .

Notación

\(f(X_{ij})\) es una función de ponderación que controla la importancia de cada par.

La Función de Ponderación \(f(x)\)

No todas las co-ocurrencias son igualmente importantes:

Definición

\[f(x) = \begin{cases} (x / x_{\max})^\alpha & \text{si } x < x_{\max} \\ 1 & \text{si } x \geq x_{\max} \end{cases}\]

Con valores típicos: \(x_{\max} = 100\), \(\alpha = 3/4\).

¿Por qué?

  • Co-ocurrencias raras (\(X_{ij}\) pequeño): peso bajo — podrían ser ruido
  • Co-ocurrencias muy frecuentes (\(X_{ij}\) grande): peso limitado a 1 — no deben dominar
  • Co-ocurrencias cero (\(X_{ij} = 0\)): \(f(0) = 0\) — se ignoran completamente (¡y \(\log 0\) no se calcula!)
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 150, 300)
x_max = 100
alpha = 3/4

f_x = np.where(x < x_max, (x / x_max) ** alpha, 1.0)

plt.figure(figsize=(8, 3))
plt.plot(x, f_x, linewidth=2, color='#0077b6')
plt.axvline(x=x_max, color='gray', linestyle='--', alpha=0.7, label=f'$x_{{max}} = {x_max}$')
plt.xlabel('$X_{ij}$', fontsize=12)
plt.ylabel('$f(X_{ij})$', fontsize=12)
plt.title('Función de ponderación de GloVe', fontsize=13)
plt.legend(fontsize=11)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

GloVe vs. Word2Vec: Resumen

Word2Vec

Aspecto Detalle
Tipo Predictivo (red neuronal)
Datos Ventanas locales
Objetivo Predecir palabras vecinas
Entrenamiento SGD sobre pares (w, contexto)
Estadísticas Locales

GloVe

Aspecto Detalle
Tipo Basado en conteo + optimización
Datos Matriz de co-ocurrencia global
Objetivo \(\vec{w}_i^T \vec{\tilde{w}}_j \approx \log X_{ij}\)
Entrenamiento Mínimos cuadrados ponderados
Estadísticas Globales ✅

En la práctica

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”
  • Morfología rica: especialmente en español
    • caminar → caminando, caminé, caminaremos, caminable…
  • Dominios especializados: terminología técnica nueva

. . .

La observación clave

desconfigurar” contiene “des-” + “configurar”. Si el modelo entendiera los componentes, podría inferir el significado. 💡

FastText (Bojanowski et al., 2017)

Idea central: Representar cada palabra como la suma de sus n-gramas de caracteres.

Publicación

  • Autores: Piotr Bojanowski, Edouard Grave, Armand Joulin, Tomáš Mikolov (Facebook AI Research)
  • Año: 2017
  • Paper: Enriching Word Vectors with Subword Information
  • Basado en: Skip-gram de Word2Vec

La innovación

En vez de aprender un vector por palabra, FastText aprende vectores para n-gramas de caracteres y compone el embedding de la palabra como su suma.

El cambio fundamental

Word2Vec/GloVe: la unidad mínima es la palabra

FastText: la unidad mínima es el n-grama de caracteres (subpalabra)

N-gramas de Caracteres

¿Cómo funciona?

  1. Agregar marcadores de inicio < y fin > a cada palabra
  2. Extraer todos los n-gramas de caracteres (típicamente \(n \in [3, 6]\))
  3. El vector de la palabra = suma de los vectores de sus n-gramas

. . .

Ejemplo: “gatos” con \(n \in [3, 5]\):

\(n\) N-gramas
3 <ga, gat, ato, tos, os>
4 <gat, gato, atos, tos>
5 <gato, gatos, atos>

. . .

Además, la palabra completa <gatos> también tiene su propio vector.

\[\vec{v}_{\text{gatos}} = \vec{v}_{\text{<ga}} + \vec{v}_{\text{gat}} + \vec{v}_{\text{ato}} + \cdots + \vec{v}_{\text{<gatos>}}\]

¿Por Qué Funciona?

Compartir subpalabras

Palabras morfológicamente relacionadas comparten n-gramas:

Palabra Algunos n-gramas
gato <ga, gat, ato, to>
gatos <ga, gat, ato, tos
gata <ga, gat, ata, ta>
gatera <ga, gat, ate, ter

Todas comparten <ga, gat → vectores relacionados automáticamente.

Para palabras OOV

Si aparece “gatuno” (nunca vista en entrenamiento):

N-gramas: <ga, gat, atu, tun, uno, no>

  • <ga, gat → compartidos con “gato”, “gata”
  • El modelo infiere un vector razonable
modelo_ft["gatuno"]  # ¡Funciona! 🎉
# Vector inferido a partir de subpalabras

Ventaja para idiomas morfológicamente ricos

El español, turco, finés, alemán… tienen morfología compleja. FastText captura estas regularidades automáticamente gracias a los n-gramas compartidos.

FastText: La Formalización

Función objetivo

FastText modifica el Skip-gram de Word2Vec. Para cada palabra \(w\), sea \(\mathcal{G}_w\) el conjunto de sus n-gramas:

\[s(w, c) = \sum_{g \in \mathcal{G}_w} \vec{z}_g^T \vec{v}_c\]

. . .

donde:

  • \(\vec{z}_g\) — vector del n-grama \(g\)
  • \(\vec{v}_c\) — vector de la palabra de contexto \(c\)
  • \(s(w, c)\) — score de que \(c\) sea contexto de \(w\)

. . .

Comparación con Skip-gram

Modelo Score \(s(w, c)\)
Skip-gram \(\vec{v}_w^T \vec{u}_c\)
FastText \(\sum_{g \in \mathcal{G}_w} \vec{z}_g^T \vec{v}_c\)

. . .

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 caracteres

def extraer_ngrams(palabra, min_n=3, max_n=6):
    """Extraer n-gramas de caracteres de una palabra."""
    palabra_ext = f"<{palabra}>"
    ngrams = []
    for n in range(min_n, max_n + 1):
        for i in range(len(palabra_ext) - n + 1):
            ngrams.append(palabra_ext[i:i+n])
    return ngrams

# Ejemplo
for palabra in ["gato", "gatos", "gata", "gatuno"]:
    ngrams = extraer_ngrams(palabra, min_n=3, max_n=4)
    print(f"'{palabra}' → {ngrams}")
'gato' → ['<ga', 'gat', 'ato', 'to>', '<gat', 'gato', 'ato>']
'gatos' → ['<ga', 'gat', 'ato', 'tos', 'os>', '<gat', 'gato', 'atos', 'tos>']
'gata' → ['<ga', 'gat', 'ata', 'ta>', '<gat', 'gata', 'ata>']
'gatuno' → ['<ga', 'gat', 'atu', 'tun', 'uno', 'no>', '<gat', 'gatu', 'atun', 'tuno', 'uno>']
# ¿Cuántos n-gramas comparten?
def ngrams_compartidos(w1, w2, min_n=3, max_n=6):
    ng1 = set(extraer_ngrams(w1, min_n, max_n))
    ng2 = set(extraer_ngrams(w2, min_n, max_n))
    compartidos = ng1 & ng2
    jaccard = len(compartidos) / len(ng1 | ng2)
    return compartidos, jaccard

pares = [("gato", "gatos"), ("gato", "gata"), ("gato", "gatuno"), ("gato", "mesa")]

print("\nN-gramas compartidos (n ∈ [3,6]):")
print("-" * 55)
for w1, w2 in pares:
    compartidos, jaccard = ngrams_compartidos(w1, w2)
    print(f"  ({w1:8s}, {w2:8s}): {len(compartidos):2d} compartidos, "
          f"Jaccard = {jaccard:.3f}")

N-gramas compartidos (n ∈ [3,6]):
-------------------------------------------------------
  (gato    , gatos   ):  6 compartidos, Jaccard = 0.333
  (gato    , gata    ):  3 compartidos, Jaccard = 0.176
  (gato    , gatuno  ):  3 compartidos, Jaccard = 0.120
  (gato    , mesa    ):  0 compartidos, Jaccard = 0.000

Bloque 4: Comparación de los Tres Modelos

Word2Vec vs. GloVe vs. FastText

Aspecto Word2Vec GloVe FastText
Año 2013 2014 2017
Enfoque Predictivo (local) Conteo + optimización (global) Predictivo + subpalabras
Unidad mínima Palabra Palabra N-grama de caracteres
OOV ❌ No soporta ❌ No soporta ✅ Infiere vectores
Morfología ❌ Ignora ❌ Ignora ✅ Captura
Velocidad Rápido Rápido Más lento (más params)
Tamaño modelo Mediano Mediano Grande
Mejor para General Corpus grandes Idiomas ricos / OOV

¿Cuál usar?

  • Necesitas manejar OOV: FastText ✅
  • Corpus muy grande, solo inglés: GloVe o Word2Vec
  • Idioma con morfología rica (español, turco…): FastText ✅
  • Prototipo rápido: El que tenga pre-entrenado disponible

Modelos Pre-entrenados Disponibles

Inglés

Modelo Dimensión Vocabulario
Word2Vec Google News 300 3M
GloVe Wikipedia 50-300 400K
GloVe Common Crawl 300 2.2M
FastText Wiki 300 1M
FastText Crawl 300 2M

Español 🇧🇴🇪🇸

Modelo Dimensión Fuente
FastText Wiki+Crawl 300 fasttext.cc
SBWCE Word2Vec 300 crscardellino.ar
GloVe SBWCE 300 Stanford NLP
FastText Wiki (es) 300 Facebook AI

Recomendación para este curso

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.

Bloque 5: Implementación Práctica

Entrenando FastText con Gensim

from gensim.models import FastText

# Mismo corpus que usamos con Word2Vec
corpus = [
    ["el", "gato", "come", "pescado", "fresco"],
    ["el", "perro", "come", "carne", "roja"],
    ["mi", "gato", "duerme", "en", "el", "sofá"],
    ["mi", "perro", "duerme", "en", "la", "cama"],
    ["el", "gato", "negro", "persigue", "al", "ratón"],
    ["el", "perro", "grande", "persigue", "al", "gato"],
    ["el", "niño", "come", "una", "manzana", "roja"],
    ["la", "niña", "come", "una", "naranja", "fresca"],
    ["el", "gato", "juega", "con", "la", "pelota"],
    ["el", "perro", "juega", "con", "un", "hueso"],
    ["el", "gato", "bebe", "leche", "fresca"],
    ["el", "perro", "bebe", "agua", "fría"],
    ["un", "gato", "pequeño", "maúlla", "fuerte"],
    ["un", "perro", "pequeño", "ladra", "fuerte"],
    ["la", "niña", "acaricia", "al", "gato", "negro"],
    ["el", "niño", "acaricia", "al", "perro", "grande"],
]

# Entrenar FastText
modelo_ft = FastText(
    sentences=corpus,
    vector_size=50,
    window=3,
    min_count=1,
    sg=1,              # Skip-gram
    min_n=3,           # N-grama mínimo
    max_n=6,           # N-grama máximo
    epochs=200,
    seed=42
)

print(f"Vocabulario: {len(modelo_ft.wv)} palabras")
print(f"Dimensión: {modelo_ft.wv.vector_size}")
Vocabulario: 39 palabras
Dimensión: 50

FastText: Magia con Palabras OOV

# Palabras que NO están en el vocabulario de entrenamiento
palabras_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 vocabulario
print("\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}")

Similitud de palabras OOV con 'gato':
  sim(gato, gatos       ) = +0.9778 ███████████████████
  sim(gato, gatito      ) = +0.9305 ██████████████████
  sim(gato, perrito     ) = +0.9655 ███████████████████
  sim(gato, comida      ) = +0.8116 ████████████████

Word2Vec vs. FastText: Comparación Directa

from gensim.models import Word2Vec

# Re-entrenar Word2Vec con el mismo corpus
modelo_w2v = Word2Vec(
    sentences=corpus,
    vector_size=50,
    window=3,
    min_count=1,
    sg=1,
    negative=5,
    epochs=200,
    seed=42
)

# Comparar palabras más similares
print("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 in range(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 FastText
modelo_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 api
print(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:

  1. Analogías de palabras
    • “rey : reina :: hombre : mujer”
    • Dataset: Google Analogy Test Set
  2. Similitud de palabras
    • Correlación con juicios humanos
    • Datasets: WordSim-353, SimLex-999
  3. Categorización
    • ¿Se agrupan bien las palabras por categoría?

Evaluación extrínseca

Mide el impacto en una tarea downstream:

  1. Clasificación de texto
    • ¿Mejora la F1 al usar estos embeddings?
  2. NER (Named Entity Recognition)
    • ¿Se reconocen mejor las entidades?
  3. 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ño
analogias = [
    # (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}")
    except KeyError as e:
        print(f"  {a:8s} : {b:8s} :: {c:8s} : ? → Palabra no encontrada: {e}")
Test de analogías (modelo pequeño):
------------------------------------------------------------
  gato     : gatos    :: perro    : ?
    Esperado: perros      Predicho: acaricia   (0.968) ❌
  niño     : niña     :: perro    : ?
    Esperado: gata        Predicho: gato       (0.963) ❌
  come     : gato     :: bebe     : ?
    Esperado: perro       Predicho: negro      (0.918) ❌

Nota

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.

Bloque 7: Resumen y Conexiones

El Panorama de los Embeddings Estáticos

Lo que aprendimos hoy

  1. GloVe: Combina conteos globales con optimización
    • Función objetivo: \(\vec{w}_i^T \vec{\tilde{w}}_j + b_i + \tilde{b}_j \approx \log X_{ij}\)
    • Razones de co-ocurrencia como señal clave
  2. FastText: Embeddings de subpalabras
    • N-gramas de caracteres como unidad mínima
    • Resuelve el problema OOV
    • Ideal para idiomas con morfología rica

Limitación compartida

Los tres modelos producen embeddings estáticos:

Cada palabra tiene un único vector, sin importar el contexto.

“Fui al banco a depositar dinero”
“Me senté en el banco del parque”

\[\vec{v}_{\text{banco}}^{\text{financiero}} = \vec{v}_{\text{banco}}^{\text{mueble}} \quad \text{😢}\]

. . .

Spoiler: Semanas 9-10

Los Transformers (BERT, GPT) resolverán esto con embeddings contextuales: un vector diferente para cada uso de la misma palabra.

Fórmulas Clave de Hoy

Concepto Fórmula
Co-ocurrencia \(P_{ij} = P(j \mid i) = \frac{X_{ij}}{X_i}\)
GloVe objetivo \(J = \sum_{i,j} f(X_{ij})(\vec{w}_i^T \vec{\tilde{w}}_j + b_i + \tilde{b}_j - \log X_{ij})^2\)
Ponderación GloVe \(f(x) = \min\left((x/x_{\max})^\alpha, 1\right)\)
FastText score \(s(w, c) = \sum_{g \in \mathcal{G}_w} \vec{z}_g^T \vec{v}_c\)
Embedding FastText \(\vec{v}_w = \sum_{g \in \mathcal{G}_w} \vec{z}_g\)

Para Reflexionar 🤔

Preguntas de discusión

  1. ¿Por qué las razones de co-ocurrencia (\(P_{ik}/P_{jk}\)) son más informativas que las probabilidades simples?

  2. Si entrenaras embeddings para español boliviano, ¿esperarías que “cholita” y “mujer” tengan alta similitud? ¿Qué sesgos podrían aparecer?

  3. ¿Cuántos n-gramas de caracteres tiene la palabra “procesamiento” con \(n \in [3,6]\)?

Próxima sesión (S3)

Visualización de datos de alta dimensión

  • ¿Cómo “ver” vectores de 300 dimensiones?
  • PCA (Análisis de Componentes Principales)
  • t-SNE (t-distributed Stochastic Neighbor Embedding)
  • Visualizaciones interactivas de embeddings

Lecturas Recomendadas

Papers originales

  1. Pennington et al. (2014). GloVe: Global Vectors for Word Representation. EMNLP 2014

  2. Bojanowski et al. (2017). Enriching Word Vectors with Subword Information. TACL 2017

Recursos adicionales

  1. Jurafsky & Martin, Ch. 6 — Secciones sobre GloVe

  2. Stanford GloVe Project — Modelos pre-entrenados y código

  3. FastText Official — Modelos en 157 idiomas

  4. Jay Alammar: The Illustrated Word2Vec — Incluye comparación con GloVe