Aprender a adivinar la letra siguiente
Un modelo de lenguaje se entrena con la predicción del token siguiente. Adivina el próximo token, ve el correcto y ajusta sus números para adivinar mejor. Ese único bucle, repetido sobre muchísimo texto, es todo el entrenamiento.
Aquí entrenas un modelo muy pequeño con una frase corta en pocos segundos, y ves cómo mejoran sus apuestas.
Al final de esta lección
Nota
Puedo entrenar un modelo muy pequeño para predecir el carácter siguiente y leer su pérdida.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Predicción del token siguiente | adivinar el token que viene después | |
| Objetivo | el token que de verdad viene después | i tras m |
| Probabilidad | la parte que un modelo da a un token, de 0 a 1 | |
| Pérdida | la sorpresa del modelo ante el objetivo | |
| Paso de entrenamiento | un pequeño empujón a cada puntuación | |
| Tasa de aprendizaje | el tamaño de cada empujón | 2.0 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Cada lugar de un texto es una pregunta
Toma la frase «mi mami me mima». En cada lugar, la pregunta es: ¿qué viene después? La respuesta, el objetivo, es el carácter siguiente del texto.
Así, 15 caracteres dan 14 preguntas, sin ninguna etiqueta puesta a mano. Por eso un modelo puede aprender de cualquier texto.
Ejemplo resuelto: las seis primeras preguntas
| Lugar | Este token | Objetivo |
|---|---|---|
| 0 | m | i |
| 1 | i | espacio |
| 2 | espacio | m |
| 3 | m | a |
| 4 | a | m |
| 5 | m | i |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Nuestro modelo mira un token atrás
Nuestro modelo muy pequeño es una tabla de puntuaciones, con una fila por token y una columna por cada posible token siguiente. El softmax convierte una fila en probabilidades. Cinco caracteres dan una tabla de 25 puntuaciones.
Este modelo solo ve el token justo anterior. Un transformer ve todos los tokens anteriores gracias a la atención enmascarada. La forma de entrenarlo es la misma.
La pérdida mide la sorpresa
La pérdida de una pregunta vale menos el log de la probabilidad dada al objetivo. Una respuesta correcta y segura casi no cuesta nada. Una probabilidad pequeña en el objetivo cuesta mucho.
Al principio, cada puntuación vale 0, así que cada uno de los 5 caracteres recibe un quinto, 0,2. La pérdida vale entonces 1,61 en cada pregunta.
Ejemplo resuelto: probabilidad en el objetivo y pérdida
| Probabilidad en el objetivo | Pérdida |
|---|---|
| 1,0 | 0 |
| 0,9 | 0,11 |
| 0,5 | 0,69 |
| un quinto, 0,2 | 1,61 |
| 0,1 | 2,30 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un paso de entrenamiento empuja las puntuaciones
Para cada pregunta, mira la fila de este token. Cada puntuación de esa fila baja tanto como su probabilidad. Además, la puntuación del objetivo sube 1.
Suma esos empujones de las 14 preguntas y divide por 14. Multiplica por la tasa de aprendizaje y aplícalos. Eso es un paso de entrenamiento. Repítelo, y la pérdida baja.
Entrenar el modelo pequeño 50 pasos
import math
text = "mi mami me mima"
chars = sorted(set(text)) # el vocabulario: espacio, a, e, i, m
n = len(chars)
pairs = len(text) - 1 # 14 pares de este token y el siguiente
table = [[0.0] * n for i in range(n)] # cada puntuación empieza en 0
rate = 2.0
for step in range(51):
push = [[0.0] * n for i in range(n)]
loss = 0
for k in range(pairs):
a = chars.index(text[k]) # este token
b = chars.index(text[k + 1]) # el token siguiente correcto
powers = [math.exp(s) for s in table[a]]
total = sum(powers)
loss = loss - math.log(powers[b] / total)
for j in range(n):
push[a][j] += powers[j] / total - (1 if j == b else 0)
if step % 10 == 0:
print("paso", step, "pérdida", round(loss / pairs, 3))
for a in range(n):
for j in range(n):
table[a][j] -= rate * push[a][j] / pairs
powers = [math.exp(s) for s in table[chars.index("m")]]
print("tras m:", [(chars[j], round(powers[j] / sum(powers), 2)) for j in range(n)])
# Pon rate a 0.5 y pulsa Ejecutar. ¿La pérdida baja más rápido o más despacio?
Ejemplo resuelto: la pérdida durante el entrenamiento
| Paso | 0 | 10 | 20 | 30 | 40 | 50 |
|---|---|---|---|---|---|---|
| Pérdida | 1,609 | 0,848 | 0,717 | 0,667 | 0,641 | 0,626 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error frecuente
Advertencia
Mucha gente piensa que un modelo guarda las frases que lee y las busca después. El nuestro guarda 25 puntuaciones y ningún texto. Ha aprendido que tras una m suele venir una i. Un modelo guarda regularidades en números, y por eso también puede equivocarse con seguridad.
Escribir el script de entrenamiento en el laboratorio
docker exec jupyter sh -c "printf '%s\n' 'import numpy as np' 'text = \"mi mami me mima\"' 'chars = sorted(set(text))' 'ids = np.array([chars.index(c) for c in text])' 'x, y = ids[:-1], ids[1:]' 'W = np.random.default_rng(0).normal(0, 0.1, (5, 5))' 'for step in range(101):' ' p = np.exp(W[x]) / np.exp(W[x]).sum(axis=1, keepdims=True)' ' if step % 25 == 0:' ' print(\"paso\", step, \"perdida\", round(-np.log(p[np.arange(14), y]).mean(), 3))' ' p[np.arange(14), y] -= 1' ' np.add.at(W, x, -2.0 * p / 14)' 'p = np.exp(W[4]) / np.exp(W[4]).sum()' 'print(\"tras m:\", dict(zip(chars, p.round(2).tolist())))' > steps/tiny.py"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Entrenar desde puntuaciones al azar
docker exec jupyter python3 steps/tiny.pySe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.