Preferencias y el modelo de recompensa
35 min de lectura
Con el ajuste supervisado, el mini-GPT contesta en lugar de continuar el documento, y la forma de obedecer queda resuelta. El contenido no: un modelo ajustado llega hasta donde llegan los pares con que se ajustó, ni un punto más arriba. Mejorar lo que responde pide otro tipo de dato, y esta lección trabaja con comparaciones: un par de respuestas a la misma pregunta y la que alguien prefiere de las dos.
Los anotadores de InstructGPT hacían eso a escala. Por cada instrucción ordenaban de cuatro a nueve respuestas del modelo, y aun así dos de ellos coincidían en una comparación algo menos de tres veces de cada cuatro. Esta lección convierte datos así, ruidosos y sin una escala común, en un número por respuesta, la recompensa, y entrena una red que lo calcula. Con ese número, el bloque tendrá por fin algo que maximizar.
Pedir notas parece más directo, y tiene un defecto que ninguna cantidad de notas arregla. Una anotadora generosa le pone un 9 a una explicación de por qué el cielo es azul y un anotador severo le pone un 6 a otra; los dos podrían preferir la segunda. Cada nota se lee en la escala de quien la pone, y esa escala se mueve también a lo largo de una tarde de trabajo. Si una anotadora puntúa unas respuestas y otro anotador otras, juntar sus notas junta sus escalas.
Una comparación tira la escala y se queda con el orden. Entre dos explicaciones, la generosa y el severo dicen cuál prefieren, y lo generosos que sean no entra en la respuesta. Lo que se pierde es el tamaño de la diferencia, y se recupera contando: si tres de cada cuatro anotadores prefieren una, la ventaja es pequeña; si la prefieren todos, grande.
El ajedrez resolvió así el mismo problema. Nadie le pone nota a un jugador: juega partidas, gana o pierde, y su Elo sale de los resultados. El Elo es un número por jugador, y la probabilidad de que uno gane a otro depende sólo de la diferencia entre los dos: con 400 puntos más que su rival, se espera que un jugador gane diez partidas por cada una que pierda. Chatbot Arena clasifica hoy los modelos de lenguaje igual, con millones de votos entre dos respuestas anónimas. Con respuestas en lugar de jugadores, el número es la recompensa.
Una recompensa que reproduce las comparaciones
Fijemos los datos. Una comparación es una terna : una instrucción, la respuesta que un anotador prefirió y la que rechazó, y se escribe . Los subíndices vienen del inglés, win y lose. Un anotador (labeler) es la persona que compara, y su elección tiene algo de azar: los anotadores no siempre coinciden, y uno solo tampoco consigo mismo de un día a otro.
El modelo de Bradley–Terry (1952) le da a cada respuesta un número, su recompensa , y dice cómo salen las comparaciones. Para dos respuestas e a la misma instrucción, antes de que nadie elija, la probabilidad de que el anotador prefiera es la parte de en la suma de las dos. Dividiendo arriba y abajo por ese término,
Es el softmax de las dos recompensas, el de la lección sobre funciones de pérdida del curso anterior, y con dos entradas el softmax es una sigmoide de la diferencia. Llamemos margen a esa diferencia, . Despejando, : el margen es el logaritmo de la ventaja. Uno de quiere decir que tres de cada cuatro anotadores prefieren ; uno de , nueve de cada diez. El Elo es esta misma curva con otra unidad, y sus 400 puntos son un margen de .
Un número por respuesta es una afirmación fuerte, y tiene consecuencias que se pueden poner a prueba. Los márgenes se suman a lo largo de una cadena: si gana a tres a uno y a tres a uno, el modelo dice que gana a nueve a uno, sin que nadie las haya comparado. Y no admite ciclos. Si los anotadores prefieren a , a e a , ninguna recompensa lo reproduce, y con las tres ventajas iguales el mejor ajuste es un empate. Es la concesión de Bradley–Terry: supone que detrás de cada elección hay un único eje de «calidad».
Lo que las comparaciones no fijan
Sumemos a todas las recompensas de una instrucción un mismo número, que puede ser distinto para cada instrucción, y llamémoslo . Ninguna probabilidad cambia, porque el margen lo resta:
Las comparaciones enfrentan siempre dos respuestas a la misma instrucción, así que fijan los márgenes dentro de cada instrucción y nada más. La recompensa queda definida salvo una constante por instrucción, y eso tiene dos consecuencias. Comparar recompensas de instrucciones distintas no significa nada: que una respuesta sobre la nieve tenga más recompensa que otra sobre el mar no lo ha dicho ninguna comparación. Y quien aprende una recompensa de comparaciones tiene que fijar el nivel por su cuenta, o dejarlo donde caiga. InstructGPT, antes de usar su modelo de recompensa, le ajustaba a mano un sesgo para que las respuestas escritas por sus anotadores tuvieran media cero, y daba esta misma razón.
Lo que las comparaciones sí fijan es la escala. Multiplicar las recompensas por 2 dobla cada margen y cambia cada probabilidad. La unidad de la recompensa no es la de ningún anotador: es la frecuencia con que coinciden. Esta libertad de una constante por instrucción parece un defecto del modelo, y más adelante el bloque la aprovechará.
Aprender la recompensa de las comparaciones
Un modelo de recompensa (reward model) es una red , con sus parámetros , que lee una instrucción y una respuesta y devuelve un número. Se entrena como todo en este bloque, maximizando la verosimilitud de sus datos. Llamemos a la distribución que saca una comparación al azar del conjunto. Lo que el modelo dice de una comparación es la probabilidad de que el anotador eligiera la que eligió, y la pérdida es menos su logaritmo, en media:
Es la entropía cruzada binaria de aquella lección sobre funciones de pérdida, con el margen en el lugar de y la etiqueta siempre 1, porque cada comparación se escribe con la preferida delante. Su óptimo sale con el argumento de siempre. Si la red puede dar cualquier recompensa, agrupemos las comparaciones de un mismo par de respuestas: cada grupo es una entropía cruzada entre la proporción de veces que ganó cada una y la sigmoide del margen, y la desigualdad de la lección sobre la perplejidad la hace mínima cuando son iguales. Un margen aprendido es una proporción de acuerdo escrita como logaritmo de una ventaja: tres anotadores de cuatro, .
El gradiente es una línea de aquella lección y la regla de la cadena. La derivada de la entropía cruzada binaria respecto de era ; con e , es . Así que
Un paso de descenso sube la recompensa de la preferida y baja la de la rechazada, cada una en la dirección de su gradiente. El factor que lo multiplica, , es la probabilidad que el modelo le da todavía al orden contrario. Una comparación que ya ordena con un margen de 4 pesa ; una que tiene al revés por 1, . El modelo aprende de lo que aún tiene mal, y lo que tiene bien sigue empujando, cada vez menos.
En la práctica, es el modelo ajustado con su última capa cambiada: en lugar de la que da un logit por entrada del vocabulario, una capa lineal que da un solo número. InstructGPT lo hizo con 6 000 millones de parámetros. Aquí lo haremos con el mini-GPT congelado y una capa lineal encima. Sea la media de los vectores que el último bloque deja en las posiciones de los tokens de :
con por igual sobre los tokens de , incluido, y : 65 números. Es la neurona del curso anterior sin activación, con para el peso porque ya marca la preferida.
El gradiente de es respecto de y 1 respecto de , el mismo 1 en las dos respuestas. En la ecuación del gradiente, lo que las dos tienen en común se va en la resta, y el de es . El sesgo no aprende nunca: es un como el de antes, el mismo para todas las instrucciones, hecho parámetro.
Un modelo de recompensa sobre el mini-GPT
Las dos celdas corren sobre el mini-GPT, tu mini-GPT del bloque 1 cargado desde el checkpoint con las cuatro filas de la plantilla, y no lo entrenan: lo congelan y entrenan encima los 65 números de . He partido del modelo preentrenado y no del ajustado de la lección anterior, que olvidó la novela por aprender diez frases.
El anotador es un programa que sabe de qué color es cada cosa. Hay 36 preguntas como las del ajuste, seis cosas de cada color, y cada una tiene seis respuestas con la forma de los pares, una por color. El anotador prefiere la correcta, y cada pregunta da cinco comparaciones. En 24 de las preguntas, tres de sus comparaciones entran en el entrenamiento y las otras dos se guardan: son los rivales nuevos, respuestas que el modelo nunca ha visto enfrentadas a la correcta. Las 12 preguntas restantes, dos por color, son de cosas que la novela no nombra nunca, y ninguna de sus comparaciones entra. La primera celda calcula de las 216 respuestas en una sola ida.
from pyodide.http import open_url
exec(open_url("/courses/llm-agents/modelo.py").read()) # red, F, codificar
# La plantilla de chat, con sus cuatro filas nuevas: la media de E.
ID = {e: 512 + i for i, e in enumerate(["<|sistema|>", "<|usuario|>", "<|asistente|>", "<|fin|>"])}
red.p["E"] = np.vstack([red.p["E"], np.tile(red.p["E"].mean(axis=0), (4, 1))])
red.cfg["n_v"] = 516
# Seis cosas de cada color. Las dos últimas de cada uno no entran en el entrenamiento.
CASOS = {"blanco": "la nieve, la leche, la nube, el azúcar, la sal, el algodón",
"negro": "el carbón, la noche, la tinta, la pizarra, el cuervo, el petróleo",
"rojo": "la sangre, el fuego, la rosa, la fresa, el tomate, la cereza",
"verde": "la hierba, la hoja, el pino, la esmeralda, la lechuga, el pepino",
"azul": "el cielo, el mar, el río, el lago, el zafiro, el arándano",
"amarillo": "el sol, el oro, el limón, el trigo, el plátano, el maíz"}
COLORES, FEM = list(CASOS), {"blanco": "blanca", "negro": "negra", "rojo": "roja", "amarillo": "amarilla"}
cosas = [(cosa, color, i >= 4) for color in COLORES for i, cosa in enumerate(CASOS[color].split(", "))]
def frase(cosa, color): # «La nieve es blanca.»
art, nombre = cosa.split(" ")
return "%s %s es %s." % (art.capitalize(), nombre, FEM.get(color, color) if art == "la" else color)
# Las 36 preguntas con sus seis respuestas: 216 conversaciones en un solo batch.
convs, tramos = [], []
for cosa, _, _ in cosas:
x = [ID["<|usuario|>"]] + codificar("¿De qué color es %s?" % cosa, F) + [ID["<|fin|>"], ID["<|asistente|>"]]
for color in COLORES:
y = codificar(frase(cosa, color), F) + [ID["<|fin|>"]]
convs.append(x + y)
tramos.append((len(x), len(x) + len(y))) # las posiciones de los tokens de y
T = max(map(len, convs))
X = np.array([ids + [0] * (T - len(ids)) for ids in convs]) # relleno detrás: la máscara causal lo ignora
Hn = red.adelante(X)[1][3] # el vector h de cada posición
H = np.array([Hn[b, i:j].mean(axis=0) for b, (i, j) in enumerate(tramos)]).reshape(36, 6, 64)
# Las comparaciones (pregunta, preferida, rechazada): la correcta contra cada una de las otras cinco.
entreno, rivales_nuevos, preguntas_nuevas = [], [], []
for q, (cosa, color, reservada) in enumerate(cosas):
w = COLORES.index(color)
for k, l in enumerate([l for l in range(6) if l != w]):
(preguntas_nuevas if reservada else entreno if (k + q) % 5 < 3 else rivales_nuevos).append((q, w, l))
print("h̄ de cada respuesta:", H.shape)
print("comparaciones: %d de entrenamiento, %d con rivales nuevos, %d de preguntas nuevas"
% (len(entreno), len(rivales_nuevos), len(preguntas_nuevas)))
La primera ejecución descarga el intérprete de Python (~15 MB). Después queda en la caché del navegador y se reutiliza en todas las lecciones.
Cada una de las 216 respuestas queda en un vector de 64 números, la media de los en sus posiciones, y nada más del mini-GPT se usa en adelante. Las comparaciones son 72 de entrenamiento, 48 con rivales nuevos y 60 de preguntas nuevas. La segunda celda entrena con trescientos pasos de descenso por gradiente sobre y mide el acierto en los tres grupos: la proporción de comparaciones en que la preferida recibe más recompensa. Después enseña las seis recompensas de una pregunta del entrenamiento y de una nueva.
sigma = lambda z: 1 / (1 + np.exp(-z))
H1 = np.concatenate([H, np.ones((36, 6, 1))], axis=2) # (h̄, 1): la última coordenada es la de b
r = lambda phi, q, k: H1[q, k] @ phi # r(x, y) = u·h̄(x, y) + b
def perdida(phi, comparaciones):
"""La pérdida de Bradley-Terry y su gradiente respecto de phi = (u, b)."""
q, w, l = np.array(comparaciones).T
margen = r(phi, q, w) - r(phi, q, l)
peso = sigma(-margen) # la probabilidad del orden contrario
L = np.logaddexp(0, -margen).mean() # -log sigma(margen), sin desbordar
return L, (peso[:, None] * (H1[q, l] - H1[q, w])).mean(axis=0)
phi = np.zeros(65)
for s in range(1, 301):
L, g = perdida(phi, entreno)
phi -= 2.0 * g # descenso por gradiente
if s in (1, 10, 100, 300):
print("paso %3d pérdida %.3f gradiente respecto de b: %.1f" % (s, L, g[-1]))
acierto = lambda comps: np.mean([r(phi, q, w) > r(phi, q, l) for q, w, l in comps])
print("\nacierto: %.2f en entrenamiento, %.2f con rivales nuevos, %.2f en preguntas nuevas"
% (acierto(entreno), acierto(rivales_nuevos), acierto(preguntas_nuevas)))
for q in (0, 34): # la nieve, entrenada; el plátano, nueva
rq = r(phi, q, np.arange(6))
print("\n¿De qué color es %s?" % cosas[q][0])
print(" ".join("%s %.1f" % (COLORES[k], rq[k]) for k in np.argsort(-rq)))
La primera ejecución descarga el intérprete de Python (~15 MB). Después queda en la caché del navegador y se reutiliza en todas las lecciones.
La pérdida arranca en : con todas las recompensas son iguales y cada comparación es una moneda al aire. Baja a en trescientos pasos, y el gradiente respecto de es cero en todos ellos, como decía la derivación. El modelo acierta 71 de sus 72 comparaciones de entrenamiento y el 85 % de las de rivales nuevos. Nunca vio La nieve es blanca. frente a La nieve es amarilla. y las ordena bien, porque no ha aprendido comparaciones sueltas sino un número por respuesta, y el de la correcta subió con cada comparación que ganó.
En las preguntas nuevas acierta el 52 %, lo que acertaría una moneda. A ¿De qué color es el plátano?, la respuesta El plátano es amarillo. recibe la menor recompensa de las seis. La capa lineal sólo puede pesar lo que trae, y el mini-GPT ha leído una novela en la que no sale ningún plátano: las comparaciones le enseñaron el color de 24 cosas, una a una. Un modelo de recompensa juzga con lo que su red sabe, y por eso los de verdad parten de un modelo grande. El de InstructGPT acertaba un 70 % de las comparaciones de anotadores que no había visto, cerca del 73 % en que los anotadores coincidían entre sí.
Mira también los niveles. La peor respuesta sobre la nieve tiene más recompensa, 18.1, que la mejor sobre el plátano, 14.2, y eso no significa nada: ninguna comparación ha puesto nunca una frente a la otra. Es la constante por instrucción, que aquí pone aplicado a lo que las seis respuestas de cada pregunta tienen en común.
Comprueba tu intuición
Cinco preguntas y un desafío: las recompensas de Bradley–Terry a partir de una tabla de votos, sin red.
Dos anotadores puntúan de 1 a 10 respuestas a la misma instrucción. Lucía le pone un 8 a la respuesta P y Andrés un 5 a la respuesta Q; ninguno ha visto la del otro. ¿Qué se puede concluir de P frente a Q?
Tres de cada cuatro anotadores prefieren la respuesta A a la B, y tres de cada cuatro prefieren la B a la C. Si las comparaciones siguen el modelo de Bradley–Terry, ¿con qué probabilidad prefiere un anotador A a C?
Se acepta un margen de ±0.002.
Un modelo de recompensa da a cada respuesta. Marca los cambios que dejan intacta la probabilidad que asigna a cada comparación.
Marca todas las opciones correctas. Se corrige todo o nada: no hay puntuación parcial.
En un paso de descenso hay dos comparaciones. En la primera, el modelo de recompensa ya da a la preferida 4 puntos más que a la rechazada; en la segunda, le da 1 punto menos. ¿Cómo se comparan los factores que multiplican la dirección de cada una en el gradiente?
En la lección, el modelo de recompensa acierta el 99 % de sus comparaciones de entrenamiento y el 52 % de las de preguntas nuevas. ¿Qué lo explica?
Escribe recompensas(votos): las recompensas de Bradley–Terry de varias respuestas a una
misma instrucción, sin red, una recompensa libre por respuesta. votos[i][j] es cuántas veces
un anotador prefirió la respuesta i a la j. Devuelve un array con una recompensa por
respuesta, que maximice la verosimilitud de los votos y tenga media cero: los votos no deciden
el nivel, y alguien tiene que fijarlo. Un ascenso por gradiente con la derivada de la lección converge en todas las
pruebas si da unos miles de pasos, cada uno dividido entre las comparaciones de su respuesta.
La primera comprobación descarga el intérprete de Python (~15 MB); después queda en la caché del navegador. Este desafío se resuelve mejor con un teclado físico: en el móvil puedes leerlo y volver luego.
Un modelo de recompensa da un número a cualquier respuesta, también a las que nadie ha comparado, y eso es lo que hacía falta para mejorar la política: cambiar para que sus respuestas reciban más. La celda dice también dónde se puede confiar en ese número. Cerca de lo que el modelo aprendió, ordena bien; lejos, adivina, y una política que lo maximice sin freno irá a buscar justo las respuestas en que se equivoca.
La lección siguiente, sobre el aprendizaje por refuerzo con retroalimentación humana (reinforcement learning from human feedback, RLHF), escribe ese freno. Maximiza la recompensa media de las respuestas de la política, menos lo que ésta se aleja del modelo ajustado, y deriva cómo se sube esa media con un gradiente aunque las respuestas salgan de un sorteo.
Para profundizar5 fuentes · 5 papers
De dónde sale lo de esta lección, y dónde seguir si quieres más. Nada de aquí hace falta para continuar el curso.
- Rank Analysis of Incomplete Block Designs: I. The Method of Paired Comparisons
El artículo que da nombre al modelo: un número por cada cosa comparada y la verosimilitud de los resultados, setenta años antes de que las cosas comparadas fueran respuestas de un modelo de lenguaje.
- Deep reinforcement learning from human preferences
Donde empieza la idea: personas que comparan dos vídeos cortos de un agente, una recompensa ajustada con la pérdida de esta lección y un robot simulado que aprende una voltereta que nadie sabría escribir como fórmula.
- Learning to summarize from human feedback
Un modelo de recompensa entrenado con comparaciones de resúmenes, y un modelo optimizado contra él cuyos resúmenes gustan más que los escritos por personas: lo que el ajuste supervisado, que imita, no puede dar.
- Training language models to follow instructions with human feedback
Su sección 3.5 es esta lección a escala: de cuatro a nueve respuestas ordenadas por instrucción, un modelo de recompensa de 6 000 millones de parámetros y un sesgo fijado a mano porque la pérdida no lo fija.
- Chatbot Arena: An Open Platform for Evaluating LLMs by Human Preference
Bradley–Terry para clasificar modelos en lugar de respuestas: votos de usuarios entre dos respuestas anónimas, y una recompensa por modelo con su intervalo de confianza.