Cómo cada token (que es solo un número aburrido) se transforma en un vector que captura su significado.
Los embeddings convierten los IDs (números arbitrarios del paso 1) en vectores que capturan significado.
Convertir cada ID de token en un vector de N números que representa el "significado" de esa palabra en un espacio matemático.
Da significado matemático: palabras parecidas terminan con vectores parecidos (cerca en el espacio). Es la "tabla de conocimiento" base del modelo, aprendida durante el entrenamiento.
Un ID es arbitrario: que "gato" sea 2103 no tiene relación numérica con "perro" = 5876. Sin embeddings, el modelo no podría relacionar palabras parecidas ni hacer operaciones significativas.
↓ A continuación, el detalle con ejemplos y visualizaciones ↓
Al final del Paso 1 tenías algo así: "el gato come" → [847, 2103, 1492]. Solo IDs.
Pero esos números no significan nada matemáticamente. El hecho de que "gato" tenga ID 2103 y "perro" tenga ID 5876 no le dice al modelo que son animales parecidos. Tampoco que ID 2104 (si existiera) tenga relación con 2103 — los IDs son arbitrarios, los reparte el tokenizer en el orden que aparecieron al entrenarse.
Solución: convertir cada ID en un vector (una lista de números) llamado embedding. La gracia es que el modelo va a aprender esos vectores durante el entrenamiento, de manera que palabras con significados parecidos terminen con vectores parecidos.
Un embedding es simplemente un array de números reales (por ejemplo, 4 números, 768 números, o 12,288 números, según el modelo). Cada token del vocabulario tiene su propio array.
Para el demo usamos 4 dimensiones. En modelos reales son cientos o miles.
Token: "gato" (id 2103)
Esos 4 números son el "significado" de "gato" para el modelo. Cada dimensión podría representar algún concepto abstracto (¿es animado? ¿es grande? ¿es comestible? — el modelo decide solo).
// La matriz de embeddings: // shape = [vocab_size, embedding_dim] const embeddingMatrix = [ [0.12, -0.45, 0.78, 0.03], // id 0 [0.91, 0.22, -0.11, 0.67], // id 1 // ... miles de filas más ... [-0.34, 0.55, 0.89, -0.21], // id 2103 = "gato" ]; function embed(tokenId) { return embeddingMatrix[tokenId]; // ¡Sí, es literalmente un array[index]! }
array[tokenId] te devuelve el vector. Lo único especial es que los valores de esa tabla se aprenden durante el entrenamiento (en lugar de ser fijos).
Vamos a usar un mini-vocabulario con embeddings que yo armé a mano en 2D (para que podamos visualizarlos). En un modelo real serían cientos de dimensiones aprendidas con backpropagation — pero los principios son idénticos.
Cada palabra es un punto. Las que están cerca tienen significados relacionados. Pasa el mouse sobre cualquier palabra para destacarla.
Eje X (horizontal) ≈ dimensión 1 · Eje Y (vertical) ≈ dimensión 2
En 2D ya empezamos a ver clusters. Pero los embeddings reales tienen cientos o miles de dimensiones. Imposible visualizarlos directamente. Lo más cerca que podemos llegar visualmente es a 3D — arrástralo con el mouse para rotar y ver la estructura desde distintos ángulos.
eje X · eje Y · eje Z — arrastra para rotar
En 2D, varias palabras se "pisan" porque solo tenemos 2 ejes para distinguir todas las propiedades semánticas posibles. Al añadir un 3er eje (eje Z), el modelo puede separar conceptos que en 2D parecían muy cerca.
Por ejemplo, "vino" y "café" pueden estar cerca en X-Y (ambos son bebidas) pero separados en Z (uno es alcohol, otro no). Cuantas más dimensiones, más matices puede capturar el modelo.
En modelos reales, las dimensiones no corresponden a conceptos limpios como "animado" o "género". Son combinaciones aprendidas, raras e interpretables solo a medias. Pero la idea es la misma: cada dimensión extra es un grado de libertad adicional para distinguir significados.
Nuestros ojos viven en 3D. Para "ver" más dimensiones se usan técnicas de reducción de dimensionalidad como PCA, t-SNE o UMAP: comprimen los vectores N-D a 2D/3D tratando de preservar las distancias relativas. Cuando ves esos gráficos de "embeddings visualizados" en papers, es eso: una proyección aproximada — no los vectores reales.
Para medir similitud entre dos embeddings, los LLMs usan similitud coseno. Suena intimidante pero la idea es simple:
function cosineSim(a, b) { // 1. Producto punto: suma de a[i]*b[i] let dot = 0; for (let i = 0; i < a.length; i++) { dot += a[i] * b[i]; } // 2. Magnitud (largo) de cada vector const magA = Math.sqrt(a.reduce((s,x) => s + x*x, 0)); const magB = Math.sqrt(b.reduce((s,x) => s + x*x, 0)); // 3. Coseno del ángulo entre ellos return dot / (magA * magB); } // → 1.0 = idénticos en dirección // → 0.0 = perpendiculares (nada que ver) // → -1.0 = opuestos
Una propiedad alucinante que emerge de los embeddings: puedes hacer matemáticas con conceptos. El ejemplo clásico (de Word2Vec, 2013):
Esto funciona porque los embeddings aprenden direcciones que codifican conceptos. El vector "rey − hombre" captura aproximadamente el concepto de "realeza sin género". Sumarle "mujer" añade el componente femenino, y el resultado cae cerca de "reina".
function analogy(a, b, c) { // 1. Vector resultado = embed(a) - embed(b) + embed(c) const target = a.map((v, i) => v - b[i] + c[i]); // 2. Buscar la palabra del vocab con // vector más parecido (coseno más alto) let bestWord = null, bestSim = -Infinity; for (const [word, vec] of vocab) { const sim = cosineSim(target, vec); if (sim > bestSim) { bestSim = sim; bestWord = word; } } return bestWord; }
Así se ve la "tabla" que el modelo consulta. Cada fila es una palabra; cada columna es una dimensión. En modelos reales esto es enorme (ej: GPT-3 tiene 50,257 filas × 12,288 columnas ≈ 617 millones de parámetros, solo en la capa de embeddings).
Pero hay un problema sutil: estos vectores no contienen información sobre el orden. Si reordenara los tokens a ["come", "gato", "el"], obtendría exactamente los mismos vectores pero en otro orden — y el modelo (en su forma básica) los trataría como un conjunto sin orden.
El Paso 3: Positional Encoding resuelve esto: añade información de "yo soy el primer token", "yo soy el segundo", etc. directamente a los vectores.