PASO 2b · COMPLEMENTO

¿Cómo se convierte exactamente "gato" → 2103 en un vector?

La pregunta correcta. Aquí están las matemáticas, sin saltarse ningún paso.

La respuesta corta (que sorprende a todo el mundo)

No hay "matemáticas" en el sentido tradicional. El ID 2103 se convierte en un vector con una operación de búsqueda en una tabla. Literalmente tabla[2103]. No hay multiplicación, ni suma, ni nada que calcular. Es un lookup de array.

Esto suele confundir porque a la gente le dicen "embedding layer", "capa de embeddings", y suena a que hay una transformación complicada. Pero no la hay. Es indexar un array.

PERO — y aquí viene lo interesante — existe una segunda forma de explicar lo mismo, usando multiplicación de matrices. Esta segunda vista es matemáticamente equivalente, y es la que usan los papers académicos. Vamos a ver ambas.

Setup: un ejemplo concreto y chiquito

Para no perdernos, vamos a usar un vocabulario de solo 6 palabras, y embeddings de solo 4 dimensiones.

📚 El vocabulario completo (6 palabras)

IDPalabra
0"el"
1"perro"
2"gato"
3"come"
4"agua"
5"corre"

Para este ejemplo, "gato" tiene id = 2 (no 2103 como antes, pero la mecánica es idéntica — solo que con un vocab más chico se ve mejor).

📊 La matriz de embeddings E (6 filas × 4 columnas)

Esto es lo que vive en memoria. Cada fila corresponde a una palabra. Los valores fueron inicializados aleatoriamente y luego "aprendidos" durante el entrenamiento.

IDpalabradim 0dim 1dim 2dim 3
0"el" 0.21-0.550.080.43
1"perro" -0.340.610.77-0.12
2"gato" -0.300.580.82-0.15
3"come" 0.45-0.22-0.300.91
4"agua" 0.670.18-0.400.50
5"corre" 0.52-0.19-0.280.88

Fíjate que la fila de "gato" (id=2) y la de "perro" (id=1) son parecidas. Ese es el resultado de un entrenamiento bien hecho.

Vista 1: el lookup directo vista práctica

Así se implementa en la realidad. Es lo que hace PyTorch o cualquier framework por debajo.

🎮 Sigue la operación paso a paso

1. Input: id=2
2. Ir a la fila 2 de la matriz E
3. Copiar esa fila
4. ¡Listo! Vector listo
Presiona "Siguiente paso" abajo para empezar
Vamos a tomar el id=2 (que es "gato") y obtener su vector.
INPUT: token id
2
OUTPUT: vector
— se llenará al avanzar —

📜 El código completo

// La matriz E vive en memoria como un array 2D:
const E = [
  [0.21, -0.55,  0.08,  0.43], // fila 0 → "el"
  [-0.34,  0.61,  0.77, -0.12], // fila 1 → "perro"
  [-0.30,  0.58,  0.82, -0.15], // fila 2 → "gato"
  [ 0.45, -0.22, -0.30,  0.91], // fila 3 → "come"
  [ 0.67,  0.18, -0.40,  0.50], // fila 4 → "agua"
  [ 0.52, -0.19, -0.28,  0.88], // fila 5 → "corre"
];

function embed(tokenId) {
  return E[tokenId];   // ¡ESO ES TODO!
}

const vectorGato = embed(2);
// → [-0.30, 0.58, 0.82, -0.15]
Eso es literalmente todo. No hay multiplicación, no hay activación, no hay nada que "calcular" en el sentido tradicional. Solo array[index]. Si esto te parece anticlimático, es porque LO ES. La complejidad de los embeddings no está en CÓMO se busca el vector, sino en CÓMO se llegó a tener esos valores en la matriz (entrenamiento — paso 10).

📐 Repaso matemático rápido (asumiendo que olvidaste todo)

Antes de ver la Vista 2 (que usa multiplicación de matrices), vamos a repasar los conceptos matemáticos básicos. Olvídate de lo que recuerdas o no del colegio — vamos a construir todo de cero, con números concretos y explicando por qué existe cada operación.

1. ¿Qué es un vector?

Un vector es simplemente una lista ordenada de números. Eso es todo. No te dejes intimidar por el nombre.

Ejemplo de un vector de 4 dimensiones:

v = [ 3,  -1,  7,  2 ]

Dimensiones = cuántos números tiene la lista. Este vector tiene 4 dimensiones porque tiene 4 números.

Para un programador: es exactamente un array:

const v = [3, -1, 7, 2];
v.length  // → 4 (la dimensión del vector)
v[0]      // → 3
v[2]      // → 7
¿Por qué los usamos? Porque queremos representar "cosas con varios atributos a la vez". Un punto en un plano necesita 2 números (x, y) → vector 2D. Un punto en el espacio necesita 3 (x, y, z) → vector 3D. El "significado" de una palabra necesita muchos atributos abstractos → vector de N dimensiones.

2. ¿Qué es una matriz?

Una matriz es una tabla rectangular de números. O equivalentemente, una lista de vectores apilados. También algo que ya conoces: un array bidimensional.

Ejemplo de una matriz 3×4 (3 filas, 4 columnas):

3-172
05-28
416-3

Notación de shape: cuando decimos "matriz [3×4]" significa 3 filas × 4 columnas. ¡SIEMPRE primero filas, después columnas! Esto importa muchísimo más adelante.

Para un programador: es un array de arrays:

const M = [
  [3, -1, 7, 2],   // fila 0 (un vector de 4 dim)
  [0, 5, -2, 8],   // fila 1
  [4, 1, 6, -3],   // fila 2
];
M.length     // → 3 (cantidad de filas)
M[0].length  // → 4 (cantidad de columnas)
M[1][2]     // → -2 (fila 1, columna 2)
¿Por qué las usamos? Porque a veces necesitamos guardar "muchos vectores a la vez" de forma organizada. La matriz de embeddings E es justamente eso: un vector por cada palabra del vocabulario, apilados uno sobre otro.

3. ¿Qué es el producto punto?

El producto punto (o dot product) es la operación más importante en deep learning. Toda la red neuronal está hecha de productos punto encadenados.

Receta: dados dos vectores del mismo tamaño:

  1. Multiplica los números en la misma posición: pos 0 con pos 0, pos 1 con pos 1, etc.
  2. Suma todos esos productos.
  3. El resultado es un solo número (un escalar, no un vector).

Ejemplo con dos vectores de 4 dimensiones:

a = [2, 3, -1, 4]
b = [5, 1, 2, -3]

a · b
= (2 × 5) + (3 × 1) + (-1 × 2) + (4 × -3)
= 10 + 3 + (-2) + (-12)
= -1

function dotProduct(a, b) {
  let sum = 0;
  for (let i = 0; i < a.length; i++) {
    sum += a[i] * b[i];
  }
  return sum;
}

dotProduct([2, 3, -1, 4],
           [5, 1, 2, -3]);
// → -1

🎮 Calculadora interactiva de producto punto

Modifica los valores y observa el cálculo en vivo:

a[0]
a[1]
a[2]
a[3]
b[0]
b[1]
b[2]
b[3]

¿Por qué existe esta operación? ¿Para qué sirve?

Buena pregunta — porque "multiplicar y sumar" suena arbitrario. Pero el producto punto tiene un significado geométrico profundo:

  • Mide qué tan "alineados" están dos vectores. Si los vectores apuntan en la misma dirección, el producto punto es positivo y grande.
  • Si apuntan en direcciones opuestas, es negativo.
  • Si son perpendiculares (no tienen nada que ver), es cero.
Para qué se usa en un LLM: en self-attention (paso 4), cuando el modelo decide "qué tan importante es la palabra A para entender la palabra B", lo hace haciendo el producto punto entre los vectores de A y B. Si el resultado es alto, hay relación. Si es bajo, no la hay. Por eso el producto punto es el corazón del Transformer.

4. ¿Qué es multiplicar matrices?

Spoiler: multiplicar matrices NO es lo que parece. NO es multiplicar celda por celda. Es algo más interesante: es hacer muchos productos punto de forma organizada.

La receta

Para calcular C = A × B:

  1. Cada celda C[i][j] del resultado se calcula como el producto punto entre la fila i de A y la columna j de B.
  2. Repite para cada combinación de fila y columna posible.

Reglas de "shape" (las dimensiones deben encajar)

A: [M × K] × B: [K × N] = C: [M × N]

Léelo así: las dimensiones del medio (la K) tienen que ser iguales. El resultado toma la primera dimensión de A y la última de B.

Ejemplos:

  • [3×4] × [4×2] = [3×2] ✓ (los 4 del medio coinciden)
  • [1×6] × [6×4] = [1×4] ✓ ← este es nuestro caso del one-hot!
  • [3×4] × [3×4] ❌ no se puede (el medio sería 4 vs 3)

Ejemplo concreto: [2×3] × [3×2] = [2×2]

Vamos a multiplicar dos matrices chiquitas y ver paso a paso cómo se llena el resultado.

Matriz A [2×3]
fila 0123
fila 1456
Matriz B [3×2]
fila 078
fila 1910
fila 21112
Presiona "Calcular siguiente celda" para empezar
Vamos a calcular las 4 celdas del resultado, una por una, mostrando el producto punto que da cada una.
Matriz resultado C [2×2]
fila 0 ? ?
fila 1 ? ?
Observación clave: multiplicar matrices se ve "complicado" porque hay muchas operaciones, pero cada celda del resultado es un solo producto punto. Si entiendes producto punto, entiendes multiplicación de matrices. No hay magia extra — solo bookkeeping (organizar dónde va cada resultado).

¿Por qué existe esta operación?

La multiplicación de matrices es la operación más usada en deep learning. La razón principal: representa una transformación lineal de un vector. Cuando multiplicas una matriz W por un vector x, obtienes un nuevo vector y que es x transformado de cierta manera. La matriz W contiene los parámetros que el modelo aprende para hacer esa transformación útil. Lo veremos a fondo en self-attention (paso 4).

5. ¿Por qué un vector one-hot funciona como un "selector"?

Ahora tenemos todo lo necesario para entender el truco del one-hot. Recuerda:

  • One-hot del id=2: [0, 0, 1, 0, 0, 0] ← un solo "1", todo lo demás cero.
  • La matriz E tiene 6 filas y 4 columnas (en nuestro ejemplo).
  • Shape: [1 × 6] × [6 × 4] = [1 × 4] — el resultado es un vector de 4 dimensiones (¡exactamente lo que queremos!).

Cuando hacemos one_hot × E:

  • El resultado tiene 4 valores (uno por cada columna de E).
  • Cada valor del resultado = producto punto entre el one-hot y una columna de E.

Y aquí viene la magia. Calculemos el primer valor del resultado: producto punto entre [0, 0, 1, 0, 0, 0] y la primera columna de E (que es [0.21, -0.34, -0.30, 0.45, 0.67, 0.52]):

= (0 × 0.21) + (0 × -0.34) + (1 × -0.30) + (0 × 0.45) + (0 × 0.67) + (0 × 0.52)
= 0 + 0 + -0.30 + 0 + 0 + 0
= -0.30

Fíjate qué pasó: todos los productos donde el one-hot tenía un 0 se anularon (porque 0 por lo que sea es 0). Solo sobrevivió el término donde el one-hot tenía un 1, que justamente está en la posición 2. Ese término "selecciona" el valor de la fila 2 de E.

El primer valor del resultado es E[2][0] = -0.30 (la fila 2, columna 0 de E). Si hacemos lo mismo para las otras 3 columnas, obtenemos exactamente la fila 2 completa de E: [-0.30, 0.58, 0.82, -0.15].

Por eso el one-hot funciona como un "selector": al multiplicarlo por una matriz, todos los ceros aniquilan filas que no nos interesan, y el único 1 deja pasar exactamente la fila que queremos. Matemáticamente es una multiplicación; conceptualmente es indexar.
✅ Si llegaste hasta aquí, ya tienes todas las matemáticas necesarias para entender la Vista 2 que viene a continuación. Verás la misma operación de antes pero ahora podrás seguir cada producto punto y entender por qué da exactamente lo que da.

Vista 2: la formulación matemática formal vista académica

Esta es la forma en que los papers escriben lo mismo. Es matemáticamente equivalente al lookup, pero formulada como una multiplicación de matrices. ¿Por qué? Porque permite escribir todo el Transformer como una secuencia de matrices que se multiplican entre sí, lo cual es elegante en notación y permite que las derivadas (necesarias para entrenar) salgan limpias.

El truco: vectores one-hot

Un vector one-hot es un vector donde todas las posiciones son 0 excepto una, que es 1. La posición del 1 indica "qué palabra es esta".

One-hot del token id=2 ("gato")

posición012345
significa "el" "perro" "gato" "come" "agua" "corre"
valor 0 0 1 0 0 0

El "1" está en la posición 2 porque "gato" tiene id=2. Si quisiéramos representar "perro" sería [0, 1, 0, 0, 0, 0].

Ahora la multiplicación

La operación es:

vector_embedding = one_hot × E

one_hot: shape [1 × 6]  ·  E: shape [6 × 4]  ·  resultado: shape [1 × 4]

🎮 La multiplicación, paso a paso

Para multiplicar un vector fila por una matriz, calculamos el producto punto entre el vector y cada columna de la matriz. Hay 4 columnas → 4 productos punto → 4 números de salida.

dim 0
dim 1
dim 2
dim 3
¡Listo!
Presiona "Calcular siguiente" para ver cómo se obtiene cada número del vector resultado
Vamos a calcular las 4 dimensiones del vector de salida, una por una.

Operación actual

aún no comenzado

Productos punto realizados

Vector resultado en construcción

[ ?, ?, ?, ? ]
El truco mágico: como el one-hot tiene un 1 en la posición 2 y 0 en todas las demás, cada producto punto solo "encuentra" el valor en la fila 2 de E. Todos los demás términos son 0 × algo = 0. ¡El resultado de la multiplicación es exactamente la fila 2 de E!

¿Por qué dos vistas si dan lo mismo?

Vista 1: lookup

  • Eficiente: O(1), acceso directo a memoria.
  • Implementación real: es lo que PyTorch (nn.Embedding) y TensorFlow hacen por debajo.
  • Intuitiva: "tabla → busca la fila".
  • Desventaja: rompe la elegancia de "todo es multiplicación de matrices".

Vista 2: one-hot × matriz

  • Lenta: O(N) — multiplicas muchos ceros inútilmente.
  • Útil en papers: permite escribir todo el Transformer como una cadena uniforme de operaciones matriciales.
  • Útil para entender backprop: las derivadas de embedding salen igualitas a las de una capa lineal cualquiera.
  • Útil pedagógicamente: conecta el embedding con el resto de operaciones del modelo.
Las dos vistas describen exactamente la misma cosa. Lo único que cambia es la forma de explicarlo y la eficiencia de implementarlo. El resultado numérico es idéntico, los gradientes durante el entrenamiento son idénticos, y el modelo no nota la diferencia.

🎯 De dónde salen esos números de la matriz E

Te queda una pregunta legítima: si la matriz E tiene esos números específicos (−0.30, 0.58, 0.82, ...) para "gato", ¿quién los puso ahí?

La respuesta corta: nadie los puso a mano. La matriz E se inicializa con números aleatorios pequeños al inicio del entrenamiento. Luego, durante millones/billones de ejemplos de texto, el algoritmo de gradient descent los va ajustando.

Cada vez que el modelo se equivoca prediciendo la siguiente palabra, el gradiente del error "fluye hacia atrás" hasta llegar a la matriz E, y le dice: "moveme la fila de 'gato' un poquito en esta dirección para equivocarte menos la próxima". Después de muchos ejemplos, las filas convergen a vectores que capturan el significado.

Lo veremos a fondo en el paso 10 (entrenamiento). Por ahora, lo que necesitas creer es: esos números no son mágicos ni diseñados, son el resultado de muchos pequeños ajustes que minimizan el error de predicción.

✅ Resumen

  1. Un token tiene un ID entero (un número, sin significado matemático).
  2. La matriz E es una tabla con una fila por cada palabra del vocabulario, y cada fila tiene N números (N = la dimensión del embedding).
  3. Para convertir un ID en vector, simplemente tomas la fila correspondiente: vector = E[id].
  4. Eso es equivalente matemáticamente a multiplicar un vector one-hot por la matriz E, pero en la práctica nadie hace esa multiplicación porque es 1000× más lento que indexar.
  5. Los números de E se aprenden durante el entrenamiento. No los pone nadie a mano.