Antes de construir una red neuronal, vamos a resolver el problema de la manera más ingenua posible: contando. Esto es train0.py de Karpathy.
Imagina un camarero que trabaja en el mismo bar hace 10 años. Nunca tomó cursos de psicología ni leyó libros de patrones de consumo. Pero ha atendido miles de pedidos. Cuando escucha "quiero un café con...", automáticamente dice "¿leche?" porque el 80% de las veces que alguien dijo "café con" en su bar, la siguiente palabra fue "leche".
No aprendió una regla. Solo acumuló conteos. Eso es exactamente el modelo bigram.
Una tabla bigram es una matriz de 27×27. Cada posición tabla[i][j] contiene un número: cuántas veces el token j apareció inmediatamente después del token i en el dataset.
El nombre "emma" con tokens BOS: [BOS] → e → m → m → a → [BOS] [26] → [4]→[12]→[12]→[0]→[26] Cada par (actual → siguiente) actualiza la tabla: tabla[26][4] += 1 ← [BOS]→e: empezar con 'e' tabla[4][12] += 1 ← e→m tabla[12][12] += 1 ← m→m tabla[12][0] += 1 ← m→a tabla[0][26] += 1 ← a→[BOS]: terminar con 'a' Hacemos esto para los 32.000 nombres del dataset. Al final, tabla[i][j] contiene el número de veces que el carácter j siguió al carácter i en todo el dataset.
Más brillante = más frecuente ese par en el dataset de nombres. La fila [BOS] muestra qué letras inician los nombres más frecuentemente.
Una vez que tienes la tabla de conteos, convertirla en probabilidades es dividir cada fila por la suma de esa fila. Si después de "a" apareció "n" 500 veces, "l" 300 veces y otras letras 200 veces (total 1000), entonces:
Conteos después de 'a' (simplificado): a→n: 500 a→l: 300 a→r: 120 a→[BOS]: 80 Total: 500 + 300 + 120 + 80 = 1000 Probabilidades: P(n|a) = 500/1000 = 0.50 (50%) P(l|a) = 300/1000 = 0.30 (30%) P(r|a) = 120/1000 = 0.12 (12%) P([BOS]|a) = 80/1000 = 0.08 (8%) ← termina aquí Suma: 0.50 + 0.30 + 0.12 + 0.08 = 1.00 ✅ Para generar el siguiente carácter desde 'a': → Lanzamos un dado ponderado con estas probabilidades. La mayoría de las veces saldrá 'n', pero a veces 'l', etc.
Con la tabla lista, generar un nombre es un proceso simple de 4 pasos que se repite:
El proceso se repite hasta generar el token [BOS], que marca el fin del nombre.
Esta demo usa una tabla bigram simplificada basada en frecuencias reales de nombres. Mueve el slider de temperatura y presiona "Generar" para ver nombres nuevos.
Temperatura 0.5 = conservador (nombres más comunes). Temperatura 2.0 = creativo (nombres raros).
El modelo bigram es simple y funciona hasta cierto punto. Pero tiene una limitación fundamental:
| Característica | Bigram (train0) | GPT completo (train5) |
|---|---|---|
| Contexto que considera | Solo el carácter anterior | Todos los anteriores (hasta 16) |
| Parámetros que aprende | 0 (solo cuenta) | 3.808 parámetros |
| Puede generalizar | No — solo lo que vio | Sí — interpola patrones |
| Complejidad | Una tabla | Tokenizador + atención + MLP + Adam |
train0.py) porque demuestra el problema en su forma más simple.
Cada lección siguiente agrega una pieza que resuelve una limitación del anterior.