22

El transformer completo

¿Qué hay dentro de un LLM, bloque por bloque?

12 min

El transformer completo

Con voz. Puedes pausar y saltar a cualquier paso.

Lección completa

Subtítulos

Ya conoces las piezas: tokens, embeddings, atención y la predicción de la siguiente palabra. Hoy las armamos en una sola máquina: el transformer, la que está dentro de GPT.

Voz

Sin voz: la lección avanza sola con subtítulos.

La voz de esta lección está en preparación: por ahora avanza sola con subtítulos.

0:00 / 0:00

Qué lograrás

  • Seguir el camino completo de una frase dentro de un transformer: tokens, números, embeddings, bloques, puntajes, softmax y la siguiente palabra.
  • Explicar qué hace cada pieza de un bloque (atención, red, conexión residual y normalización de capa) y por qué hacen falta la codificación de posición y la máscara causal.
  • Calcular cuántos parámetros tiene cada parte de un transformer pequeño y reconocer dónde está casi todo el tamaño de un modelo real como GPT-3.

Ideas clave

  1. Un transformer recorre siempre el mismo camino: tokens, números, embeddings más posición, una pila de bloques y, con la lista del último token, doce puntajes (uno por palabra) que softmax vuelve probabilidades.
  2. Cada bloque tiene dos partes: la atención, el único camino por el que un token recibe información de los demás, y una red que procesa cada token por separado.
  3. Ninguna parte reemplaza la lista de un token: le suma su aporte (conexión residual). Antes de cada parte, la normalización de capa le resta su promedio y la divide entre su desviación estándar.
  4. La atención sola no sabe el orden: por eso se suma la codificación de posición. La máscara causal impide mirar el futuro; sin ella, el modelo aprende a copiar y no sirve para escribir.
  5. Los parámetros se cuentan por tablas. En un modelo grande casi todos están en la atención y en la red de los bloques: en GPT-3, el embedding es menos del uno por ciento.

Lo que mucha gente cree

Creencia común Un transformer lee el texto de izquierda a derecha, palabra por palabra, como una persona.

Lo correcto Todos los tokens entran a la vez y cada bloque los procesa en paralelo. El orden se lo da la codificación de posición, y es la máscara causal la que impide que cada token mire hacia delante. Lo que sí va palabra por palabra es la escritura: un pase completo por cada token nuevo.

Compruébalo

Cuatro preguntas. Sin nota ni cuenta: sirven para ver si la idea quedó.

Concepto¿Para qué sirve la máscara causal en un transformer que escribe texto?
ConceptoEn un bloque, ¿qué parte procesa cada token por separado, sin mirar a los demás?
CálculoEn un bloque con listas de diez números, la atención tiene cuatro tablas de diez por diez (consultas, claves, valores y salida). Sin contar los sesgos, ¿cuántos parámetros suman esas tablas?
AplicaciónUn equipo arma un modelo con atención sin máscara para clasificar reseñas y, por error, olvida la codificación de posición. ¿Qué problema es de esperar?

Por dentroPara quien quiere las fórmulas y el código. No hace falta para seguir el curso.

El pase completo, ecuación por ecuación

Para una secuencia de TT tokens con números t1,…,tTt_1,\dots,t_T, una tabla de embeddings EE (una fila de dd números por palabra del vocabulario) y LL bloques, en la variante «con normalización previa» de GPT-2, que es la de nuestro juguete (d=8d=8, L=2L=2):

xi←E[ti]+PEix_i \leftarrow E[t_i] + \mathrm{PE}_i

y, en cada bloque, con LN\mathrm{LN} la normalización de capa:

x←x+Atencioˊn(LN1(x))x←x+MLP(LN2(x))x \leftarrow x + \mathrm{Atenci\acute{o}n}\big(\mathrm{LN}_1(x)\big) \qquad x \leftarrow x + \mathrm{MLP}\big(\mathrm{LN}_2(x)\big)

Esas dos sumas son las conexiones residuales: cada parte suma su aporte a xx. Al salir del último bloque, la lista del último token da los puntajes (en inglés, logits) y softmax las probabilidades:

z=LNf(xT) E⊤p=softmax(z)z = \mathrm{LN}_f(x_T)\,E^\top \qquad p = \mathrm{softmax}(z)

«Comparar con cada palabra del vocabulario» es exactamente esto: un producto punto con cada fila de EE, la misma tabla de la entrada. Reutilizarla ahorra V⋅dV\cdot d parámetros; ya lo hacía el artículo de 2017 (sección 3.4) y la técnica se estudia en Press y Wolf (2017). Al entrenar se usan las TT posiciones a la vez: cada una predice el token siguiente.

La atención con máscara

Con QQ, KK y VV como en la lección 21 y la máscara Mij=0M_{ij}=0 si j≤ij\le i y −∞-\infty si j>ij>i:

Atencioˊn(Q,K,V)=softmax ⁣(QK⊤dh+M)V\mathrm{Atenci\acute{o}n}(Q,K,V)=\mathrm{softmax}\!\Big(\frac{QK^\top}{\sqrt{d_h}}+M\Big)V

Como e−∞=0e^{-\infty}=0, el futuro recibe peso cero. Así lo describe el artículo original para el decodificador: se pone en −∞-\infty toda entrada de softmax que corresponda a una conexión «ilegal», para preservar la propiedad autorregresiva (sección 3.2.3). Nuestro juguete tiene dos cabezas de dh=4d_h=4 números por bloque; la tabla de la escena es una cabeza del segundo bloque.

El experimento. Entrenamos dos modelos idénticos (misma semilla, mismos datos, 4 000 pasos) y a uno le quitamos la máscara. Con frases nuevas, el modelo con máscara tiene una pérdida de 0.527 nats por palabra, casi la entropía de la gramática (0.526, el mínimo posible). El modelo sin máscara baja su pérdida de entrenamiento a 0.059 copiando, pero usado como se usa al escribir (con máscara, sin futuro) da 3.088, peor que repartir por igual entre doce palabras: ln⁡12=2.485\ln 12 = 2.485.

La red de cada bloque

MLP(x)=W2 GELU(W1x+b1)+b2\mathrm{MLP}(x)=W_2\,\mathrm{GELU}(W_1x+b_1)+b_2, con W1W_1 de d×4dd\times 4d y W2W_2 de 4d×d4d\times d, la misma para todas las posiciones del bloque («aplicada a cada posición por separado y de forma idéntica», sección 3.3 del artículo). El artículo original usaba ReLU; GPT-1 y GPT-2 usan GELU (Hendrycks y Gimpel, 2016), una ReLU suave: GELU(x)=x Φ(x)\mathrm{GELU}(x)=x\,\Phi(x), con Φ\Phi la distribución normal acumulada.

Normalización de capa

Sobre los dd números de una lista (Ba, Kiros y Hinton, 2016):

LN(x)=γ⊙x−μσ2+ε+β,μ=1d∑jxj,σ2=1d∑j(xj−μ)2\mathrm{LN}(x)=\gamma\odot\frac{x-\mu}{\sqrt{\sigma^2+\varepsilon}}+\beta,\qquad \mu=\tfrac1d\sum_j x_j,\quad \sigma^2=\tfrac1d\sum_j (x_j-\mu)^2

γ\gamma y β\beta son los «dos ajustes que también se aprenden» (ganancia y sesgo, 2d2d parámetros por normalización). El artículo de 2017 normalizaba después de sumar, LN(x+Subcapa(x))\mathrm{LN}(x+\mathrm{Subcapa}(x)); GPT-2 la movió a la entrada de cada parte y añadió una más tras el último bloque. Xiong y colegas (2020) muestran que, con la normalización previa, los gradientes están bien comportados desde el inicio y se puede entrenar sin la fase de calentamiento de la tasa de aprendizaje. En la escena, la lista de «come» al entrar al segundo bloque tiene promedio 1.10 y desviación estándar 0.78.

Codificación de posición

La del artículo de 2017 (sección 3.5), con pospos el lugar e ii el índice del par de números:

PEpos,2i=sin⁡ ⁣(pos/100002i/d)PEpos,2i+1=cos⁡ ⁣(pos/100002i/d)\mathrm{PE}_{pos,2i}=\sin\!\big(pos/10000^{2i/d}\big)\qquad \mathrm{PE}_{pos,2i+1}=\cos\!\big(pos/10000^{2i/d}\big)

Las longitudes de onda van de 2π2\pi a 10000⋅2π10000\cdot2\pi. Los autores probaron también posiciones aprendidas y obtuvieron resultados casi idénticos; GPT-1 aprende las suyas. «La atención sola no sabe el orden» es exacto para la atención sin máscara: si se permutan los tokens, sus salidas se permutan igual. Con máscara causal la cosa es más sutil: Haviv y colegas (2022) muestran que un modelo de lenguaje causal sin codificación de posición aprende una noción implícita de posición, gracias a la propia máscara. Aun así, casi todos los modelos usan alguna. En la escena, el cálculo «sin posición» usa una cabeza real de nuestro modelo sobre los embeddings solos y sin máscara.

Contar parámetros

Con VV palabras, dd números por lista, LL bloques, ctxctx posiciones aprendidas y una red de d→4d→dd\to4d\to d:

Parte Parámetros Juguete (V=12V{=}12, d=8d{=}8, L=2L{=}2)
Embeddings (y posiciones aprendidas) Vd+ctx dV d + ctx\,d 96 (posición con ondas: 0)
Atención L (4d2+4d)L\,(4d^2+4d) 576
Red L (8d2+5d)L\,(8d^2+5d) 1 104
Normalizaciones (4L+2) d(4L+2)\,d 80
Total ≈12 L d2\approx 12\,L\,d^2 si dd es grande 1 856

La aproximación N≈12 L d2N\approx 12\,L\,d^2 es la de Kaplan y colegas (2020, ecuación 2.1), sin contar embeddings. Con el vocabulario de GPT-2 (50 257) y ctx=2048ctx=2048, la tabla da 125 226 240 para 12 bloques con d=768d=768 y 174 604 259 328 para 96 bloques con d=12 288d=12\,288; el artículo de GPT-3 (tabla 2.1) lista 125 millones para «GPT-3 Small» y 175.0 mil millones para GPT-3. El reparto por partes de la escena es nuestro cálculo con esta tabla. Para GPT-2 pequeño (contexto 1 024) da 124 439 808, lo mismo que cuenta la biblioteca Hugging Face Transformers (comprobado en train_tiny.py); el artículo de GPT-2 decía 117 millones.

En código

import numpy as np

def softmax(z):
    e = np.exp(z - z.max(-1, keepdims=True))
    return e / e.sum(-1, keepdims=True)

def causal_attention(Q, K, V):
    T, d = Q.shape
    scores = Q @ K.T / np.sqrt(d)
    scores[np.triu_indices(T, k=1)] = -np.inf      # máscara causal: nada de mirar al futuro
    w = softmax(scores)
    return w, w @ V

def layer_norm(x, g, b, eps=1e-5):
    return g * (x - x.mean(-1, keepdims=True)) / np.sqrt(x.var(-1, keepdims=True) + eps) + b

def n_params(d, L, V, ctx, ff=None):
    ff = ff or 4 * d
    return V*d + ctx*d + L*(4*d*d + 4*d) + L*(2*d*ff + ff + d) + (4*L + 2)*d

rng = np.random.default_rng(0)
Q = K = V = rng.normal(size=(3, 4))
w, out = causal_attention(Q, K, V)
print(np.round(w, 2))
x = rng.normal(2.0, 3.0, size=8)
y = layer_norm(x, np.ones(8), np.zeros(8))
print(round(y.mean(), 3), round(y.std(), 3))
print(n_params(8, 2, 12, 0), n_params(12288, 96, 50257, 2048))
# [[1.   0.   0.  ]
#  [0.25 0.75 0.  ]
#  [0.17 0.13 0.7 ]]
# 0.0 1.0
# 1856 174604259328

El modelo de la lección está completo en model.ts y se entrena con train_tiny.py (PyTorch, en CPU, menos de un minuto): ambos viven junto a esta lección, y verify-forward.mts comprueba que el pase en TypeScript coincide con PyTorch (diferencia máxima de 7·10⁻⁶).

Lo que viene

Un modelo con esta arquitectura empieza con parámetros al azar. La lección 23 muestra qué aprende al entrenarlo para predecir la siguiente palabra con texto a escala de internet, y la 24, cuánto cuesta. Cómo se sirve (la caché KV, que evita recalcular los tokens anteriores) llega en la lección 26.

Lente de decisión

Qué significa esta idea si tu trabajo es decidir, no programar.

  • Casi todos los modelos de lenguaje de uso general comparten este esqueleto. Decir «usamos un transformer» casi no informa: pregunta por el tamaño (número de bloques y largo de las listas), por cuánto texto puede leer de una vez y por los datos con que se entrenó.
  • El tamaño crece con el cuadrado del largo de las listas y con el número de bloques: duplicar el largo cuadruplica los parámetros de cada bloque. Compara los dos números, no solo el total.
  • Cada token que el modelo escribe exige un pase por todos los bloques. Las respuestas largas cuestan más tiempo y dinero, y un modelo más profundo tarda más por token.
  • Hay modelos que escriben (solo decodificador, como GPT) y modelos que solo leen (solo codificador, como BERT). Para clasificar o buscar documentos no siempre hace falta uno que escriba: puede ser más barato y más fácil de evaluar.

Términos de la lección

Transcripción completa
ComentariosConversación pública de esta lección: dudas, datos por revisar, ideas.

Los comentarios viven en GitHub Discussions, en el repositorio público del curso; para escribir se necesita una cuenta de GitHub. Se cargan desde giscus.app al abrir esta sección. Las personas que revisan el curso los leen, y si un cambio se acepta, se aplica en los dos idiomas. Abrir el hilo en GitHub

Fuentes