¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Cómo funciona la atención en un transformer

Después de esta lección: Puedo calcular a mano la salida de la atención para un token.

Lección de 65 min2 comandosLaboratorio JupyterLab de ciencia de datos: pandas, Matplotlib y scikit-learn en el navegador

¿Qué palabras anteriores importan para esta?

La atención es el paso de un transformer en el que cada token mira a los demás tokens y toma de ellos lo que necesita. Gracias a ella, un modelo de lenguaje une «él» con el nombre correcto, varias palabras atrás.

En «el tren va lleno», la palabra lleno habla del tren. La atención es la forma en que el modelo lo descubre con números.

Al final de esta lección

Nota

Puedo calcular a mano la salida de la atención para un token.

Palabras clave

PalabraQué significaEjemplo
Consultaun vector que dice qué busca un token (query en inglés)
Claveun vector que dice qué ofrece un token (key en inglés)
Valorun vector que lleva lo que un token transmite (value en inglés)
Puntuacióncuánto encaja una consulta con una clave2
Softmaxconvierte puntuaciones en partes que suman 1
Pesola parte de un valor que entra en la salida0,665

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Consulta, clave y valor para cada token

En álgebra lineal, una tabla de números multiplica un vector. La atención multiplica cada vector de entrada por tres tablas aprendidas, y salen tres vectores nuevos.

La consulta dice qué busca este token. La clave dice qué ofrece a los demás. El valor es lo que transmite si lo eligen. Abajo partimos de los tres vectores ya calculados.

Ejemplo resuelto: puntuar la consulta de lleno

TokenClaveConsulta de lleno por clavePuntuación
tren2, 11x2 + 0x12
va0, 21x0 + 0x20
lleno1, 11x1 + 0x11

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Softmax convierte las puntuaciones en pesos

Las puntuaciones pueden tener cualquier tamaño, incluso por debajo de 0. El softmax las convierte en pesos todos positivos que suman 1.

Primero se calcula e elevado a cada puntuación, la tecla exp de una calculadora. Cada número se vuelve positivo, y una puntuación grande crece mucho más. Luego se divide cada uno por el total. Los modelos reales también dividen antes cada puntuación por un número fijo. Aquí nos saltamos ese paso.

Ejemplo resuelto: softmax de 2, 0 y 1

TokenPuntuacióne elevado a la puntuaciónPeso: dividir por 11,11
tren27,390,665
va01,000,090
lleno12,720,245
Total11,111,000

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Puntuaciones y pesos con NumPy

Comando
docker exec jupyter python3 -c "import numpy as np; K = np.array([[2, 1], [0, 2], [1, 1]]); s = K @ np.array([1, 0]); w = np.exp(s) / np.exp(s).sum(); print(s.tolist(), 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.

La salida mezcla los valores con pesos

El valor de cada token se multiplica por su peso, y luego se suman los resultados. Esa suma es el nuevo vector de lleno.

Lleno toma unos dos tercios (0,665) del valor de tren, así que ahora lleva una parte del sentido de tren. Cada token hace lo mismo con su propia consulta, al mismo tiempo.

Ejemplo resuelto: mezclar los valores

TokenPesoValorPeso por valor
tren0,66510, 06,65, 0
va0,0900, 100, 0,90
lleno0,2455, 51,22, 1,22
Salidaunos 7,88, 2,12

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

La atención para un token en Python

Código: python
import math

keys = [[2, 1], [0, 2], [1, 1]]         # tren, va, lleno
values = [[10, 0], [0, 10], [5, 5]]
query = [1, 0]                          # la consulta de lleno

scores = []
for key in keys:
    scores.append(query[0] * key[0] + query[1] * key[1])

powers = []
for s in scores:
    powers.append(math.exp(s))
total = sum(powers)
weights = []
for p in powers:
    weights.append(p / total)

mix = [0, 0]
for i in range(len(values)):
    for j in range(2):
        mix[j] = mix[j] + weights[i] * values[i][j]

print("puntuaciones:", scores)
print("pesos:", [round(w, 3) for w in weights])
print("salida:", [round(m, 2) for m in mix])
# Cambia la consulta por [0, 1] y pulsa Ejecutar.
# ¿Qué token recibe ahora más peso?

Un error frecuente

Advertencia

Mucha gente piensa que la atención elige un token e ignora los demás. Toma una parte del valor de cada token. Un peso está cerca de 0 solo cuando la clave encaja mal con la consulta, y los pesos siempre suman 1.

La atención de los tres tokens

Comando
docker exec jupyter python3 -c "import numpy as np; Q = np.array([[0, 1], [1, 1], [1, 0]]); K = np.array([[2, 1], [0, 2], [1, 1]]); V = np.array([[10, 0], [0, 10], [5, 5]]); S = Q @ K.T; W = np.exp(S) / np.exp(S).sum(axis=1, keepdims=True); print(W.round(3).tolist()); print((W @ V).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.

Sobre este laboratorio y su código: Laboratorio JupyterLab de ciencia de datos: pandas, Matplotlib y scikit-learn en el navegador

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.