Quince cajas, cinco números
24 min read
Descrito, el Transformer ya está entero. La lección anterior, sobre el encoder, el decoder y las máscaras, cerró la última operación que quedaba suelta, y con ella el modelo sabe leer una frase, escribir otra y no mirar lo que todavía no ha escrito. Construido no está. Lo que hay escrito son letras —, , , —, y un programa no reserva memoria para una letra: hasta que cada una no valga un número, no hay ni una matriz que crear.
Los números del artículo son cinco, y ponerlos encima del dibujo es lo que cierra el bloque. Cinco y no quince: fijados ésos, todas las formas del modelo quedan determinadas —incluidas un par que parecen libres y no lo son—, y de ellos sale el recuento entero de parámetros y, con él, el reparto, que no está donde el dibujo sugiere. Antes hay que cerrar la caja que ninguna lección construyó, la que corona la columna del decoder, y su respuesta viene de mucho más atrás que el resto del bloque: es la tabla de embeddings de la lección sobre las representaciones densas, leída al revés.
La figura del artículo está en inglés y este bloque la ha ido construyendo en español, así que lo primero es la correspondencia, rótulo por rótulo:
| Rótulo en la figura | Qué caja es | Dónde se construyó |
|---|---|---|
| Input Embedding, Output Embedding | la tabla , la misma en las dos | la lección sobre las representaciones densas |
| Outputs (shifted right) | lo ya escrito, corrido una posición | la lección anterior, sobre el decoder y su máscara |
| Positional Encoding | , sumada una sola vez abajo del todo | la lección sobre la codificación posicional |
| Multi-Head Attention (izquierda) | la auto-atención del encoder | auto-atención, producto interno escalado y cabezas |
| Masked Multi-Head Attention | la misma, con sumada a las puntuaciones | la lección anterior, sobre las máscaras |
| Multi-Head Attention (centro de la derecha) | la atención encoder-decoder | la lección anterior, sobre la subcapa que junta las dos columnas |
| Add & Norm, cinco veces | la lección sobre los residuales y el layer norm | |
| Feed Forward, dos veces | el perceptrón por posiciones | la lección sobre los residuales y el layer norm |
| Nx | bloques apilados | esta lección |
| Linear, Softmax | la proyección al vocabulario | esta lección |
La caja de arriba lee la tabla del bloque 1 al revés
De lo que sale del último bloque del decoder, la fila es un vector de coordenadas, , y lo que hace falta en esa posición es una probabilidad por cada entrada del vocabulario. El artículo dibuja dos cajas para ese salto: una matriz que lleva de a coordenadas y el reparto de siempre. Esa matriz podría ser suya, y no lo es:
donde es la tabla de embeddings del bloque 1, la misma que abajo del todo cambia cada token por su vector. La coordenada que le toca a la entrada dentro del softmax es : el producto escalar entre la fila de esa entrada y lo que el decoder lleva calculado. La entrada más probable es la que más apunta en la dirección que la posición pide.
Es la tabla leída en la otra dirección. Abajo se entra con una entrada y se sale con su vector; aquí se entra con un vector y se sale con una puntuación por entrada. Las posiciones salen a la vez del mismo producto, como todo en este bloque:
con el softmax por filas. Una fila por posición escrita, una columna por entrada del vocabulario, y ni un parámetro nuevo en toda la caja.
La misma tabla en tres sitios
El artículo va más lejos que eso y usa esa matriz tres veces: en el embedding de entrada, en el de salida y en la proyección al vocabulario. Eso exige un vocabulario común a los dos idiomas, y lo tiene: sus entradas salen de un BPE (byte-pair encoding) entrenado sobre los dos textos a la vez, la técnica de la lección sobre la tokenización. Una tabla de son parámetros, y lo que se compra compartiéndola es no pagarla tres veces.
Queda un factor que la lección sobre la codificación posicional dejó fuera. Allí la entrada de la primera capa se escribió ; el artículo multiplica el primer sumando:
Con los números delante se ve para qué. Todas las filas de miden lo mismo, —es la cuenta de aquella lección—, mientras que una fila de recién inicializada, con coordenadas pequeñas alrededor de cero, mide alrededor de . Sumados así, el contenido queda debajo de la posición; multiplicar por los pone en el mismo orden de magnitud. El artículo escribe la multiplicación y no la justifica: ésta es la razón que se le suele dar, no una que él dé.
Cinco números fijan todas las formas
La tabla 3 del artículo tiene una fila por cada modelo que entrenaron. La primera, la que llaman base, es la que ha ido apareciendo suelta a lo largo del bloque:
| Número | Valor | Qué fija |
|---|---|---|
| cuántos bloques se apilan en cada columna | ||
| la anchura que entra y sale de toda subcapa | ||
| cuántas cabezas tiene cada capa de atención | ||
| la anchura interior del perceptrón por posiciones | ||
| cuántas filas tiene |
Todo lo demás sale de esos cinco. El reparto de la lección sobre la atención multi-head da , y con él , y miden y mide ; el perceptrón por posiciones sube a y vuelve a bajar; cada normalización aporta dos vectores de . Lo que no está en la lista tampoco está en ninguna forma: ni , ni el tamaño del batch. Esa tabla tiene además dos columnas que esta lección no toca, dropout y label smoothing: son del entrenamiento y no de la arquitectura, y quedan fuera del curso.
Un tercio del modelo es una tabla de búsqueda
Un bloque del encoder pesa parámetros —la cuenta de la lección sobre los residuales y el layer norm, con sus sesgos y sus vectores de normalización dentro—. Uno del decoder lleva exactamente una atención y una normalización más:
El modelo entero son seis de cada, más la tabla que las dos columnas comparten:
Ahí está el reparto que el dibujo esconde. Son millones de encoder, de decoder y de tabla: un , un y un por ciento. Lo que más pesa de una sola pieza no es ninguna de las quince cajas, es la tabla de búsqueda, que ella sola supera a las seis capas del encoder juntas — frente a —. Y de ahí que el artículo la comparta: las dos copias que se ahorra suman parámetros, casi tanto como las doce capas.
Esa cuenta es una reconstrucción a partir de la figura, no la contabilidad del artículo, y conviene decir en qué se separan. El artículo declara millones y aquí salen . Los dos que faltan caben enteros en dos detalles que la figura no muestra: el tamaño exacto del vocabulario —con entradas la cifra cuadraría— y dónde pone cada implementación sus sesgos. La misma receta aplicada a la otra fila de la tabla 3, la que llaman big (, , ), da millones frente a los declarados.
El recuento en una función
Toda la sección anterior cabe en ocho líneas de aritmética con números enteros. Mira tres cosas: los tres porcentajes, la diferencia entre la tabla y el encoder entero, y qué le pasa al total si cada caja tuviera su propia tabla.
d_k = d_v = d_model // h # no se eligen: salen de h
atencion = 3 * h * d_model * d_k + h * d_v * d_model # las tres por cabeza, y W^O
ffn = 2 * d_model * d_ff + d_ff + d_model # dos capas y sus dos sesgos
norm = 2 * d_model # gamma y beta
enc = N * (atencion + ffn + 2 * norm) # dos subcapas, dos normalizaciones
dec = N * (2 * atencion + ffn + 3 * norm) # tres y tres
return enc, dec, V * d_model # la tabla, una para tres cajas
enc, dec, tabla = parametros(N=6, d_model=512, h=8, d_ff=2048, V=37000)
total = enc + dec + tabla
for nombre, valor in (("encoder", enc), ("decoder", dec), ("tabla E", tabla)):
print(f"{nombre:>8} {valor:>12,} {100 * valor / total:5.1f} %")
print(f"{'total':>8} {total:>12,}")
print("\nla tabla menos el encoder entero:", tabla - enc)
print("sin compartirla, dos copias mas :", total + 2 * tabla)
grande = parametros(N=6, d_model=1024, h=16, d_ff=4096, V=37000)
print("\nla fila big de la tabla 3 :", sum(grande))
La primera ejecución descarga el intérprete de Python (~15 MB). Después queda en la caché del navegador.
Los tres porcentajes salen , y , y el decoder pesa más que el encoder por lo único en lo que se diferencian: una subcapa de atención más, seis veces. La tabla le saca al encoder parámetros, que sobre dieciocho millones es empatar. Y sin compartirla el modelo se iría a , de millones a por una decisión que no cambia ni una operación del dibujo.
La última línea cambia los cinco argumentos por los de la fila big y da . Lo que no vas a poder cambiar es la longitud del texto, porque no aparece en ninguna línea de la función: no es que se haya quedado fuera de la cuenta, es que no hay ninguna matriz cuya forma dependa de ella.
Comprueba tu intuición
Cinco preguntas: qué forma no fijan los cinco números, qué costaría no compartir la tabla, qué vas a encontrarte al abrir la figura, si un texto más largo pide más parámetros y qué sale por arriba del decoder.
Con los cinco números del artículo puestos —, , , y —, ¿cuál de estas formas sigue sin quedar determinada?
El artículo usa la misma matriz en los tres sitios que la necesitan. Si cada uno tuviera la suya, ¿cuántos parámetros más tendría el modelo base?
A margin of ±0 is accepted.
Abres la figura 1 del artículo. Marca lo que vas a encontrarte.
Select every correct option. This is graded all-or-nothing: there is no partial credit.
Pasas de traducir frases de tokens a traducir textos de . El modelo base necesita más parámetros.
Ésta es la caja de arriba del todo, con la tabla compartida y cuatro posiciones escritas. ¿Qué imprime?
import numpy as np
# E es la tabla compartida; X_dec sale del ultimo bloque
V, d_model, T_y = 37000, 512, 4
rng = np.random.default_rng(0)
E = rng.normal(size=(V, d_model))
X_dec = rng.normal(size=(T_y, d_model))
O = X_dec @ E.T
P = np.exp(O - O.max(axis=1, keepdims=True))
P /= P.sum(axis=1, keepdims=True)
print(P.shape, np.allclose(P.sum(axis=1), 1.0))
Ya no queda nada que describir. Las quince cajas tienen su operación, sus formas y sus números, y eso es justo lo que hace falta para escribir el modelo entero y ver qué contesta. Lo que no cabe es el tamaño: sesenta y tres millones de parámetros no se mueven dentro de un navegador, así que lo que se construye es la arquitectura con anchuras de juguete —las mismas fórmulas, dos capas en lugar de seis, dos cabezas en lugar de ocho— y una frase corta que se pueda seguir a ojo.
Eso es la lección siguiente, el proyecto: un Transformer desde cero. Casi todas las piezas están escritas ya, cada una en el desafío de su lección —el softmax por filas, la atención con su máscara, el layer norm, el perceptrón por posiciones, la codificación posicional—, y lo que queda es ensamblarlas y hacer una pasada hacia delante de principio a fin: de una frase de tokens a una probabilidad por entrada del vocabulario, con los mapas de atención a la vista por el camino. Entrenar de verdad pide una máquina que un navegador no tiene, y eso tiene su sitio más adelante en el bloque.
Further reading2 sources · 2 papers
Where this lesson comes from, and where to go next. None of it is needed to carry on with the course.
- Attention Is All You Need
Su §3.4 comparte la tabla E entre las dos entradas y la salida y la multiplica por √d_model; su tabla 3, fila «base», da tus cinco números. Los 65 millones los reconstruyes tú, y no cuadran al detalle con lo que declara.
- Using the Output Embedding to Improve Language Models
El Transformer lo cita para leer E al revés en la salida. Aquí está el porqué: atar la matriz de entrada con la de salida baja la perplejidad y reduce el modelo a menos de la mitad. Es sobre LSTM; la cuenta de parámetros es tuya.