Lección 6 de 10 · Resultados y límites · 17 min
Cómo se evaluó
Win rate con su intervalo, veracidad y toxicidad: los números del paper y cómo se calculan
Notebook del episodio
El código de esta lección, para correrlo y modificarlo en Google Colab, gratis y con tu cuenta. Para guardar tus cambios: Archivo → Guardar una copia en Drive.
InstructGPT. Episodio seis: cómo se evaluó. Ya tenemos los tres pasos: SFT, supervised fine-tuning; el reward model; y PPO, proximal policy optimization, con su mezcla de preentrenamiento. Hoy, la pregunta que todo paper tiene que contestar: ¿cómo sabés que salió mejor? Vamos a ver qué midieron, cómo se calcula cada número, y cuánto ruido trae.
Tres palabras, tres mediciones
El paper define alinear con tres palabras: útil, honesto e inofensivo. Útil se mide con preferencias: una persona ve dos respuestas al mismo prompt y elige la mejor. Los prompts son de clientes de la API, application programming interface, la puerta por la que se usa el modelo, de clientes que no aportaron datos de entrenamiento. Honesto no se puede medir directo, porque no vemos qué cree el modelo; miden veracidad. E inofensivo, con toxicidad y sesgo sobre datasets públicos.
| criterio | cómo lo midieron | con qué datos |
|---|---|---|
| útil (helpful) | preferencia de quien etiqueta: win rate | prompts de la API |
| honesto (honest) | veracidad y alucinaciones | TruthfulQA y la API |
| inofensivo (harmless) | toxicidad y sesgo | RealToxicityPrompts y otros |
Parte 1 · Un juicio por prompt
La métrica principal no es una pérdida ni un benchmark: es contar cuántas veces una persona prefirió tu modelo.
Win rate: contar preferencias
Simulamos una evaluación. Cuatrocientos prompts de test, un juicio por prompt. Cada juicio es un uno si quien etiqueta eligió la respuesta del modelo A, y un cero si eligió la del baseline B. Acá A le gusta de verdad un sesenta y seis por ciento de las veces, pero eso no lo vemos. El win rate es el promedio de esos unos y ceros. Y el baseline contra sí mismo. A ganó doscientos cincuenta y cinco de cuatrocientos: cero coma seis tres siete, no cero coma sesenta y seis. Esa diferencia es ruido de muestreo. El baseline contra sí mismo da cero coma cinco, más o menos.
n = 400 # prompts de test: un juicio por prompt
p_oculta = 0.66 # cuánto gusta A de verdad (no se ve)
prefiere_A = rng.random(n) < p_oculta # True: quien etiqueta eligió A sobre B
print("primeros 12:", prefiere_A[:12].astype(int))
win_rate = prefiere_A.mean() # promedio de unos y ceros
print(f"A gana {prefiere_A.sum()} de {n}: win rate {win_rate:.3f}")
contra_si = rng.random(n) < 0.5 # el baseline contra sí mismo
print(f"SFT contra SFT: {contra_si.mean():.3f}")
primeros 12: [1 1 1 1 0 1 0 1 0 1 1 0]
A gana 255 de 400: win rate 0.637
SFT contra SFT: 0.502
El win rate, en fórmulas
: 1 si se cumple, 0 si no · : «preferida a»
el baseline contra sí mismo: la línea de referencia
La fórmula que vimos en el episodio uno, ahora con cada pieza. n es la cantidad de prompts. Para cada prompt i, la respuesta A sub i del modelo y la B sub i del baseline. El símbolo parecido a un mayor es «preferida a». El uno en negrita es una indicadora: uno si lo de adentro es cierto, cero si no. w sombrero es su promedio; el sombrero dice que es una estimación. En el dibujo, ocho juicios y su promedio. El paper compara todo contra el SFT de ciento setenta y cinco mil millones de parámetros, porque queda en el medio. Contra sí mismo da cero coma cinco: arriba de esa línea, la gente prefiere tu modelo. En el dibujo, los cinco modelos grandes.
La figura 1 del paper, redibujada
La figura principal del paper, con valores leídos del gráfico, no de una tabla. GPT tres a secas: de cero coma uno cuatro a cero coma dos cuatro. GPT tres con un prompt few-shot bien elegido: sube. El SFT: de cero coma tres cinco a cero coma cinco. PPO, sin mezcla de preentrenamiento. PPO-ptx, que es lo que el paper llama InstructGPT: cero coma cinco tres en el más chico, cero coma sesenta y seis en el más grande. La línea del cero coma cinco. El dato del abstract: el PPO-ptx de uno coma tres mil millones queda arriba del GPT tres de ciento setenta y cinco mil millones.
valores leídos del gráfico vectorial de la figura 1 (±0,01); barras de error de ±0,03 a ±0,05
Ouyang et al., 2022 — Training language models to follow instructions with human feedback · arXiv:2203.02155
85 ± 3 % · «175B InstructGPT outputs are preferred to GPT-3 outputs 85 ± 3% of the time, and preferred 71 ± 4% of the time to few-shot GPT-3.»
Los números del episodio uno. Comparando directo, sin pasar por el baseline: El InstructGPT de ciento setenta y cinco mil millones le gana a GPT tres el ochenta y cinco por ciento de las veces, más o menos tres. Y al GPT tres con prompt few-shot, el setenta y uno, más o menos cuatro. Ese más o menos es lo que sigue.
Parte 2 · Cuánto ruido tiene un win rate
Con cuatrocientos juicios salió cero coma seis tres siete en vez de cero coma sesenta y seis. ¿Cuánto se puede mover?
El intervalo, a mano y por bootstrap
Los mismos cuatrocientos juicios. El win rate. Su error estándar: la raíz de p por uno menos p, sobre n. El intervalo del noventa y cinco por ciento: uno coma noventa y seis errores estándar para cada lado. Y la otra forma, el bootstrap que usamos en Alineación, episodio diez: sortear cinco mil veces cuatrocientos juicios con reposición entre los que tenemos. Y quedarnos con el percentil dos coma cinco y el noventa y siete coma cinco. Error estándar cero coma cero dos cuatro. Intervalo: de cero coma cinco nueve a cero coma seis ocho cinco, más o menos cero coma cero cuatro siete. El bootstrap da casi lo mismo. Y el sesenta y seis verdadero cae adentro.
p = prefiere_A.mean()
se = np.sqrt(p * (1 - p) / n) # error estándar de un promedio de 0 y 1
print(f"p = {p:.3f} se = {se:.4f}")
print(f"fórmula: {p - 1.96*se:.3f} a {p + 1.96*se:.3f} (± {1.96*se:.3f})")
remuestras = rng.choice(prefiere_A, size=(5000, n)) # bootstrap
medias = remuestras.mean(1)
lo, hi = np.percentile(medias, [2.5, 97.5])
print(f"bootstrap: {lo:.3f} a {hi:.3f}")
p = 0.637 se = 0.0240
fórmula: 0.590 a 0.685 (± 0.047)
bootstrap: 0.588 a 0.685
De dónde sale el ±
se: error estándar ·
el 95 % de una normal cae a ±1,96 se
La cuenta. Cada juicio es una moneda cargada, con varianza w por uno menos w. El promedio de n monedas tiene esa varianza dividida por n; su raíz es el error estándar, se. Con nuestros números: cero coma seis tres siete por cero coma tres seis tres, sobre cuatrocientos, raíz: cero coma cero dos cuatro. En el dibujo, cada factor. Con n grande, el promedio se distribuye casi como una campana normal, y el noventa y cinco por ciento de una normal cae a uno coma noventa y seis errores estándar del centro. En el dibujo, los dos bordes.
Cuántos juicios hay detrás de cada ±
El paper da los más o menos, pero no dice cuántas comparaciones hizo ni con qué método. Podemos dar vuelta la fórmula. El margen m es z por el error estándar. Elevamos al cuadrado los dos lados y despejamos n. Para el ochenta y cinco más o menos tres. Unos quinientos cuarenta. Para el setenta y uno más o menos cuatro, unos cuatrocientos noventa; para la figura uno, unos cuatrocientos. Es una inferencia nuestra, suponiendo esta fórmula. Y para tener un punto de precisión hacen falta casi diez mil. Diferencias de dos o tres puntos en la figura uno son ruido.
Cada línea, su término:
def n_para(p, margen, z=1.96)→ — : el margen, lo que va después del ±return z**2 * p * (1 - p) / margen**2→ — elevar al cuadrado y despejar n"175B vs GPT-3, 85 ± 3"→ — nuestra inferencia, no un dato del papern_para(0.5, 0.01)→ — un punto de precisión: diez mil juicios
def n_para(p, margen, z=1.96): # despejar n de margen = z·√(p(1−p)/n)
return z**2 * p * (1 - p) / margen**2
casos = {"175B vs GPT-3, 85 ± 3": (0.85, 0.03),
"175B vs few-shot, 71 ± 4": (0.71, 0.04),
"figura 1, 0,66 ± 0,045": (0.66, 0.045)}
for nombre, (p, m) in casos.items():
print(f"{nombre:26} → n ≈ {n_para(p, m):4.0f}")
print(f"y para ± 1 punto con p = 0,5: n ≈ {n_para(0.5, 0.01):5.0f}")
175B vs GPT-3, 85 ± 3 → n ≈ 544
175B vs few-shot, 71 ± 4 → n ≈ 494
figura 1, 0,66 ± 0,045 → n ≈ 426
y para ± 1 punto con p = 0,5: n ≈ 9604
Parte 3 · Veracidad
Honesto no se mide: no sabemos qué cree el modelo. Lo que sí se mide es si lo que dice es verdad.
TruthfulQA: dos tasas, no una
TruthfulQA es un dataset de preguntas armadas para que un modelo repita mitos. Una persona juzga cada respuesta dos veces: si es verdadera, y si informa algo. El modelo A responde cuatro preguntas de ejemplo, inventadas: dos bien, un mito, y una abstención. El modelo B siempre se abstiene: «I have no comment». La primera tasa cuenta verdaderas. La segunda, verdaderas y además informativas. A: cero coma setenta y cinco y cero coma cinco. B: uno y cero. Callarse siempre es verdad, pero no sirve: por eso se reportan las dos.
# cada respuesta, juzgada por una persona: ¿es verdadera? ¿informa algo?
modelo_a = [("Nada en particular: es un mito.", True, True),
("Se rompe en pedazos.", True, True),
("Te da siete años de mala suerte.", False, True),
("I have no comment.", True, False)]
modelo_b = [("I have no comment.", True, False)] * 4 # siempre se abstiene
def tasas(respuestas):
n = len(respuestas)
verdad = sum(v for _, v, _ in respuestas) / n
verdad_e_info = sum(v and i for _, v, i in respuestas) / n
return verdad, verdad_e_info
for nombre, r in [("A", modelo_a), ("B", modelo_b)]:
v, vi = tasas(r)
print(f"modelo {nombre}: true {v:.2f} true + info {vi:.2f}")
modelo A: true 0.75 true + info 0.50
modelo B: true 1.00 true + info 0.00
Las dos tasas
: la respuesta es verdadera
: informa algo · : «y»
Tabla 14 del paper: true contra true + info
En fórmulas. v sub i vale verdadero si la respuesta i es cierta. La tasa true es el promedio de la indicadora. En el dibujo, las cuatro respuestas del modelo A: tres unos. f sub i dice si informa algo, y el símbolo de cuña es «y». Ahora la abstención es verdadera pero no informativa: cuenta cero. Pasa de verdad. En la Tabla catorce, el PPO de seis mil millones, con una instrucción que le permite contestar «I have no comment»: cero coma nueve dos ocho de verdaderas, pero cero coma uno ocho siete de verdaderas e informativas.
TruthfulQA en 175B: verdaderas e informativas
Los números reales, de la Tabla catorce: los modelos de ciento setenta y cinco mil millones, con el prompt de preguntas y respuestas, contando verdaderas e informativas. GPT tres: cero coma dos cinco uno. El SFT: apenas más. PPO: cero coma siete cinco dos. PPO-ptx: cero coma seis ocho nueve. Ese es el «más o menos el doble» del abstract. Y sin pedirle que diga la verdad.
- GPT: 0,251
- SFT: 0,271
- PPO: 0,752
- PPO-ptx: 0,689
Alucinar en una tarea cerrada
La otra medición usa tareas cerradas, como resumir: la salida no debería traer nada que no esté en la entrada. En el paper lo marca una persona, con un sí o no por respuesta. Acá, una regla de juguete. Un texto fuente, inventado. Cuatro resúmenes. Una palabra que no está en la fuente cuenta como alucinación. Y la tasa. El segundo dice que abre todos los días: falso. El cuarto inventa el wifi. La mitad alucina. La regla es burda: un sinónimo la engaña. Por eso el paper usa personas.
fuente = ("La biblioteca abre de lunes a viernes de 9 a 18. "
"Los sábados abre de 10 a 14. Los domingos está cerrada.")
resumenes = ["Abre de lunes a viernes de 9 a 18 y los sábados de 10 a 14.",
"Abre todos los días de 9 a 18.",
"Los domingos está cerrada; los sábados abre de 10 a 14.",
"Abre de lunes a viernes y tiene wifi gratis."]
vacias = {"y", "de", "a", "la", "los", "el"} # no cuentan
palabras = lambda t: set(re.findall(r"\w+", t.lower())) - vacias
vocab = palabras(fuente)
for r in resumenes:
nuevas = palabras(r) - vocab # lo que no estaba en la entrada
print(f"{'ALUCINA' if nuevas else 'ok '} {sorted(nuevas)}")
tasa = sum(bool(palabras(r) - vocab) for r in resumenes) / len(resumenes)
print(f"tasa de alucinación: {tasa:.2f}")
ok []
ALUCINA ['días', 'todos']
ok []
ALUCINA ['gratis', 'tiene', 'wifi']
tasa de alucinación: 0.50
Ouyang et al., 2022 — Training language models to follow instructions with human feedback · arXiv:2203.02155
21 % vs 41 % · «InstructGPT models make up information not present in the input about half as often as GPT-3 (a 21% vs. 41% hallucination rate, respectively).»
El resultado del paper con prompts reales de la API. En tareas cerradas, InstructGPT inventa información que no estaba en la entrada la mitad de las veces que GPT tres: veintiuno contra cuarenta y uno por ciento.
Parte 4 · Toxicidad
RealToxicityPrompts: comienzos de frase sacados de la web. El modelo completa, y un clasificador puntúa de cero a uno.
Cómo elegís los prompts cambia el número
Partimos los prompts en cuatro tramos según qué tan tóxicos son, y suponemos, con números inventados, cuánta toxicidad promedio produce el modelo en cada tramo. En el dataset, la mayoría de los prompts son poco tóxicos. El paper eligió prompts en partes iguales por tramo, para ver qué pasa con los más tóxicos. Sorteamos y promediamos. Con muestreo natural, cero coma cero nueve tres. Uniforme: cero coma dos dos. El mismo modelo, más del doble. El paper lo dice: sus números absolutos están inflados. Sirven para comparar modelos entre sí.
# toxicidad media de la continuación según qué tan tóxico es el prompt
media_por_tramo = np.array([0.05, 0.12, 0.25, 0.45]) # tramos: 0–25 %, …, 75–100 %
natural = np.array([0.70, 0.20, 0.07, 0.03]) # cómo vienen en el dataset
uniforme = np.array([0.25, 0.25, 0.25, 0.25]) # lo que hizo el paper
def medir(pesos, n=4000):
tramo = rng.choice(4, size=n, p=pesos) # elegir prompts
tox = rng.normal(media_por_tramo[tramo], 0.05).clip(0, 1)
return tox.mean()
print(f"muestreo natural: {medir(natural):.3f}")
print(f"muestreo uniforme: {medir(uniforme):.3f}")
muestreo natural: 0.093
muestreo uniforme: 0.220
Un promedio pesado
: fracción de prompts del tramo · : toxicidad media
lo que hizo el paper: partes iguales
Lo mismo en fórmula. t barra es la toxicidad promedio que reportás. Mu sub k es la toxicidad media del modelo en el tramo k, y w sub k es qué fracción de los prompts sale de ese tramo; los cuatro w suman uno. En el dibujo, las cuatro columnas pesadas: casi todo el peso en el tramo menos tóxico. Con pesos iguales, un cuarto cada uno, t barra es el promedio simple de los mu. Los tramos tóxicos pesan mucho más, y el número sube, sin que el modelo cambie.
Toxicidad en 175B según la instrucción
Los números de la Tabla catorce, puntuados con Perspective API, un clasificador de toxicidad. Sin instrucción, los tres modelos de ciento setenta y cinco mil millones dan casi lo mismo. Si el prompt pide ser respetuoso, el PPO-ptx baja a cero coma uno nueve seis; GPT tres no se entera. El paper reporta que, con personas puntuando, InstructGPT genera cerca de un veinticinco por ciento menos de salidas tóxicas en ese caso. Pero si el prompt pide ser sesgado, PPO-ptx sube a cero coma cuatro: obedece. Sigue también las malas instrucciones.
| prompt | GPT | SFT | PPO-ptx |
|---|---|---|---|
| sin instrucción | 0,231 | 0,211 | 0,234 |
| «sé respetuoso» | 0,233 | 0,199 | 0,196 |
| «sé sesgado» | 0,285 | 0,256 | 0,400 |
Tabla 14, RealToxicityPrompts, Perspective API (0 a 1)
Lo que esta evaluación no mide
- Quien juzga no es quien escribió el prompt
- Los prompts favorecen a InstructGPT: hay control con prompts de GPT-3
- Sesgo (Winogender, CrowS-Pairs): sin mejora
- Un número por modelo no muestra cuándo falla
Los límites que el propio paper reconoce. La persona que juzga lee el prompt pero no lo escribió: puede malinterpretar la intención. Los prompts de test vienen de gente que usaba InstructGPT, y eso le juega en contra a GPT tres; por eso repitieron la evaluación con prompts enviados a GPT tres, y el resultado no cambió mucho. En sesgo, con dos datasets de pares de frases, no hubo mejora. Y un win rate no te dice en qué prompts pierde. Eso lo miramos en el episodio ocho.
Resumiendo
- Win rate: promedio de juicios 0/1 contra un baseline (SFT 175B )
- Figura 1: PPO-ptx 1,3B ≈ 0,53, por encima de GPT-3 175B ≈ 0,24
- : ; ±3 puntos ≈ 500 juicios
- TruthfulQA: true y true + info; abstenerse infla la primera
- Alucinación 21 % vs 41 %; toxicidad baja solo si se la pedís
- Próximo: el impuesto de alineación
Papers del episodio
¿Qué tal esta lección?
Sé el primero en calificarla
¿Terminaste la lección?
Márcala para sumar +50 XP y mantener tu racha.
Tutor IA
Contexto: Cómo se evaluó
Conectando con el tutor…