La pieza que repite un modelo de lenguaje
Un bloque transformer es la unidad con la que se construye un modelo de lenguaje, y un modelo apila ese mismo bloque muchas veces. Contiene una atención enmascarada con varias cabezas y luego una pequeña red para cada token. Unos atajos y un reajuste de escala rodean las dos partes.
Quien entiende un bloque entiende la forma del modelo entero.
Al final de esta lección
Nota
Puedo seguir un token a través de los pasos de un bloque transformer.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Máscara causal | impide que un token mire a los tokens siguientes |
| Cabeza | un paso de atención con sus propias tablas de consulta, clave y valor |
| Etapa densa | una pequeña red aplicada a cada token por separado |
| Conexión residual | suma la entrada de un paso a su salida |
| Normalización | reajusta un vector para que sus números tengan un tamaño estable |
| Bloque | atención y etapa densa con sus residuos, repetidos para formar un modelo |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Una máscara causal oculta el futuro
Un modelo de lenguaje aprende a adivinar el token siguiente. Si un token pudiera mirar a los tokens de después, vería la respuesta.
Por eso una máscara causal deja que cada token mire solo a sí mismo y a los tokens anteriores. Un token posterior recibe un peso de 0. En la práctica, su puntuación pasa a menos infinito antes del softmax, porque e elevado a menos infinito vale 0.
Ejemplo resuelto: los pesos con el futuro enmascarado
| Token | Puntuaciones | Puede mirar | Pesos |
|---|---|---|---|
| Primero | 1, 2, 0 | solo el primero | 1, 0, 0 |
| Segundo | 2, 1, 1 | el primero y el segundo | 0,731, 0,269, 0 |
| Tercero | 0, 1, 3 | los tres | 0,042, 0,114, 0,844 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Enmascarar el futuro en Python
import math
# scores[i][j]: cuánto encaja la consulta del token i con la clave del token j.
scores = [[1, 2, 0], [2, 1, 1], [0, 1, 3]]
for i in range(3):
powers = []
for j in range(3):
if j <= i:
powers.append(math.exp(scores[i][j]))
else:
# Un token posterior: enmascarado, no recibe ningún peso.
powers.append(0)
total = sum(powers)
weights = []
for p in powers:
weights.append(round(p / total, 3))
print("token", i, "pesos", weights)
# Cambia j <= i por True, para no enmascarar nada, y pulsa Ejecutar.
# ¿Cómo cambia la fila del primer token, y por qué es hacer trampa?
La misma máscara con NumPy
docker exec jupyter python3 -c "import numpy as np; S = np.array([[1, 2, 0], [2, 1, 1], [0, 1, 3]], dtype=float); S[np.triu(np.ones((3, 3), dtype=bool), 1)] = -np.inf; W = np.exp(S) / np.exp(S).sum(axis=1, keepdims=True); print(W.round(3).tolist())"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Varias cabezas buscan cosas distintas
Un paso de atención solo mezcla de una manera. Por eso un bloque ejecuta varias cabezas en paralelo, cada una con sus propias tablas de consulta, clave y valor.
Una cabeza puede unir una palabra con la palabra anterior. Otra puede unir un adjetivo con el nombre que describe. Sus salidas se ponen una detrás de otra, y una última tabla aprendida las devuelve a la longitud habitual.
Ejemplo resuelto: juntar dos cabezas
| Paso | Para el token lleno |
|---|---|
| Salida de la cabeza 1 | 7,88, 2,12 |
| Salida de la cabeza 2 | 1, 3 |
| Una detrás de otra | 7,88, 2,12, 1, 3 |
| Tras la tabla de mezcla | vuelta a la longitud habitual |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Etapa densa, residuos y normalización
Después de la atención, cada token pasa solo por una pequeña etapa densa. Son dos capas con ReLU entre ellas, como en una red neuronal sencilla.
Una conexión residual suma la entrada de cada paso a su salida, así que no se pierde nada de lo ya sabido. La normalización reajusta cada vector: sus números quedan con media 0 y una dispersión estable. Las dos cosas mantienen entrenable una pila profunda.
Ejemplo resuelto: un residuo y luego la normalización
| Paso | Números |
|---|---|
| Entrada de lleno | 1, 2 |
| Salida de la atención | 7,88, 2,12 |
| Residuo: sumarlas | 8,88, 4,12 |
| Normalizado: media 0 | 1, -1 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Normalizar un vector con NumPy
docker exec jupyter python3 -c "import numpy as np; x = np.array([2.0, 4.0, 6.0]); print(x.mean(), ((x - x.mean()) / x.std()).round(2).tolist())"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error frecuente
Advertencia
Mucha gente piensa que la etapa densa hace que los tokens hablen entre sí. Trabaja con cada token por separado. Los tokens solo comparten información en la atención, así que un dato que viene de otro token tiene que pasar antes por ahí.