Guías
Tutorial: evaluar una política de aprendizaje por refuerzo
Un recorrido ejecutable para dos tipos de preguntas de aprendizaje por refuerzo: qué tal funciona a lo largo de los episodios una política que usted puede ejecutar, y qué tal habría funcionado una política que no puede probar en el mundo real, juzgada a partir del registro de la política a la que reemplazaría. Ambos ejemplos se ejecutan en local sin credenciales de proveedor.
La referencia de cada campo y evaluador es Aprendizaje por refuerzo; los términos caso, evaluador, métrica, intervalo y gate están en Conceptos básicos. Esta página es el recorrido práctico por ellos.
Lo que Oloproof hace y no hace aquí
Oloproof no ejecuta su entorno, no aloja un simulador ni entrena una política. Su sistema ejecuta cada episodio en su propio entorno y lo registra como un artefacto rl_episode/v1, y cada métrica de episodio se lee de ese registro. En la segunda parte no se ejecuta nada en absoluto: la política evaluada solo indica la probabilidad que daría a cada acción.
Mantenga separadas las dos preguntas:
| Pregunta | Lo que tiene | Parte |
|---|---|---|
| ¿Qué tal funciona esta política a lo largo de los episodios? | Una política y un entorno que puede ejecutar | 1 |
| ¿Qué tal habría funcionado esta política en producción? | Un registro de las decisiones que tomó una política anterior, con las probabilidades que usó | 2 |
Requisitos previos
- Python 3.11 o posterior, y Oloproof instalado (pip install oloproof).
- El proyecto de ejemplo, que viene con el paquete. Cópielo en un directorio nuevo:
oloproof init --example rl my-rlContiene dos proyectos, episodes/ y off_policy/. Cada comando de abajo se ejecuta desde dentro de uno de ellos, y la evidencia se almacena en su directorio .oloproof/.
Parte 1: una política que puede ejecutar
cd my-rl/episodesLos archivos
| Archivo | Qué es |
|---|---|
| env.py | Un mundo de cuadrícula, dos políticas (wander y plan) y los sistemas que las ejecutan |
| data/tasks.jsonl | 24 cuadrículas, cada una una tarea: muros, peligros, un inicio y un objetivo |
| oloproof.yaml | La suite para la política de línea base, wander |
| candidate.yaml | La misma suite para la política candidata, plan |
| release.yaml | La política de publicación |
| comparison.yaml | Las reglas que un candidato debe cumplir frente a la línea base |
Un episodio avanza una celda por paso hasta que alcanza el objetivo o se queda sin pasos. Cada paso vale -1 y alcanzar el objetivo vale +10, así que un retorno está entre -30 (30 pasos, sin objetivo) y 10. Entrar en una casilla de peligro cuesta 1: esa es la restricción de seguridad. Uno de cada diez movimientos resbala en una dirección aleatoria, sea cual sea la elección de la política, así que la misma política puede obtener resultados distintos en la misma cuadrícula.
Registrar un episodio
El sistema ejecuta el episodio por sí mismo y entrega el registro al registrador del caso:
@system(name="grid-agent", version="wander-v1", records=("rl_episode/v1",))
def run_baseline(task):
return _run(task, wander)
def _run(task, policy):
recorder = current_case()
record = episode(task, policy, seed=task["seed"] * 1000 + recorder.replicate)
recorder.rl_episode(record)
return {"success": record["success"], "steps": len(record["steps"])}El registro enumera la recompensa y el coste de cada paso, si el episodio alcanzó el objetivo (success) y si el límite de pasos lo terminó (truncated).
Varios episodios por tarea
oloproof.yaml declara replicates: 5, así que cada tarea ejecuta cinco episodios. current_case().replicate indica cuál (de 0 a 4), y el entorno fija la semilla del episodio a partir de él: el mismo episodio de la misma tarea siempre se reproduce igual, y una segunda ejecución reutiliza los episodios almacenados en lugar de volver a ejecutarlos.
La tarea sigue siendo la unidad. Cada criterio es la media, sobre las 24 tareas, de la media propia de cada tarea sobre sus cinco episodios, así que el intervalo cuenta 24 tareas y no 120 episodios. Contar episodios sería fingir que cinco episodios de una cuadrícula son cinco cuadrículas independientes.
Los evaluadores:
evaluators:
- {type: episode_return, return_range: [-30, 10]}
- {type: episode_success}
- {type: episode_cost_within, budget: 0, criterion: hazard_free}
metrics:
- {id: solved_every_time, type: every_replicate, criterion: episode_success}return_range es obligatorio: el intervalo de un retorno medio solo es válido dentro de un rango en el que se encuentra todo retorno, y un retorno fuera de él detiene la ejecución. solved_every_time cuenta una tarea solo cuando sus cinco episodios alcanzaron el objetivo.
Ejecutar la línea base
oloproof runGate: BLOCK (exit 3)
Decisions
┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Rule ┃ Metric ┃ State ┃ Reasons ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ success-floor │ episode_success │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ safety-floor │ hazard_free │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
└───────────────┴─────────────────┴───────────────────────┴─────────────────────────────┘
Metrics
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Metric ┃ Estimate ┃ Interval ┃ N ┃
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ episode_return │ -10.51 │ [-16.44, -5.11] │ mean of 24 observed · 0 missing · 0 excluded │
│ episode_success │ 76.7% │ [55.4%, 89.6%] │ 24 cases observed · 0 missing · 0 excluded · 5 replicates each · 14 flaky │
│ hazard_free │ 29.2% │ [10.3%, 54.0%] │ 24 cases observed · 0 missing · 0 excluded · 5 replicates each · 9 flaky │
│ solved_every_time │ 37.5% │ [18.7%, 59.5%] │ 9 / 24 observed · 0 missing · 0 excluded · 5 replicates each · 14 flaky │
└───────────────────┴──────────┴─────────────────┴───────────────────────────────────────────────────────────────────────────┘
Cache: execution 0 hit/120 miss; judgment 0 hit/360 missLéalo fila por fila:
- La política errante alcanza el objetivo en el 76.7% de los episodios, pero el intervalo va del 55.4% al 89.6%, así que el mínimo del 75% ni se supera ni se incumple: INSUFFICIENT_EVIDENCE.
- Solo resuelve 9 de las 24 tareas en cada uno de sus cinco episodios. "Suele funcionar" y "funciona siempre" son afirmaciones distintas, y un agente que vaya a funcionar sin supervisión necesita la segunda.
- 14 tareas son inestables: su éxito cambió entre episodios idénticos. Un episodio por tarea no habría podido mostrarlo.
- Evita los peligros en el 29.2% de los episodios. El extremo superior del intervalo, 54.0%, está por encima del mínimo del 50%, así que ni siquiera este mal historial demuestra todavía que la política sea insegura.
Mire una tarea:
oloproof inspect RUN_ID --case grid_03output: {
"success": false,
"steps": 30
}
judgments:
episode_return: score -30
episode_success: failed
hazard_free: passedEl primer episodio de grid_03 se quedó sin pasos: retorno -30, sin objetivo, sin entrar en ningún peligro.
Ejecutar el candidato
oloproof run --config candidate.yaml│ success-floor │ episode_success │ PASS │ lower_bound_meets_minimum │
│ safety-floor │ hazard_free │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold ││ episode_return │ 0.42 │ [-5.78, 2.59] │ mean of 24 observed · 0 missing · 0 excluded │
│ episode_success │ 100.0% │ [79.9%, 100.0%] │ 24 cases observed · 0 missing · 0 excluded · 5 replicates each │
│ hazard_free │ 57.5% │ [29.3%, 81.5%] │ 24 cases observed · 0 missing · 0 excluded · 5 replicates each · 6 flaky │
│ solved_every_time │ 100.0% │ [85.7%, 100.0%] │ 24 / 24 observed · 0 missing · 0 excluded · 5 replicates each │El planificador alcanza el objetivo en todos los episodios, así que el mínimo de éxito aprueba. Aun así entra en peligros: algunas cuadrículas no tienen un camino seguro, y el suelo resbaladizo lo empuja hacia otros. Su tasa de seguridad del 57.5% está por encima del mínimo, pero 24 tareas todavía no pueden mostrar que esté por encima del 50%.
Comparar las dos políticas
oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy comparison.yamlepisode_return: +10.92 [+0.21, +17.74] · 24 paired · 0 missing · 0 excluded
episode_success: +23.3 points [-3.2, +44.6] · 24 paired · 0 missing · 0 excluded
hazard_free: +28.3 points [-2.4, +54.7] · 24 paired · 0 missing · 0 excluded
solved_every_time: +62.5 points [+24.3, +87.7] · 24 paired · 0 missing · 0 excluded
Decisions
return-better episode_return superiority PASS difference_above_zero
safety-no-worse hazard_free non-inferiority, margin 5.0 points PASS lower_bound_above_margin
Gate: ALLOW (exit 0)La comparación empareja cada tarea consigo misma, y cada lado vale su media sobre cinco episodios. La diferencia de retorno está en la propia unidad del retorno: el planificador obtiene unos 10.9 más por episodio, y al menos 0.21 más. Emparejado sobre las mismas cuadrículas, un margen de 5 puntos basta para mostrar que el planificador no es menos seguro, aunque ninguna de las dos ejecuciones por sí sola pudo resolver su propio mínimo de seguridad.
Parte 2: una política juzgada a partir de un registro
cd ../off_policyUn servicio de soporte envía cada ticket a un modelo pequeño o a uno grande. El enrutador actual elige el modelo pequeño el 70% de las veces y el grande el 30%, al azar. Usted ha escrito una regla nueva: los tickets técnicos, y los tickets de facturación largos, van al modelo grande. Todavía no puede probarla con tickets reales, pero el registro del enrutador antiguo puede responder por ella. Eso es la evaluación fuera de política.
El registro y sus archivos
| Archivo | Qué es |
|---|---|
| data/decisions.jsonl | 600 decisiones que tomó el enrutador actual |
| make_log.py | La simulación que escribió ese registro, para que pueda ver cómo se hizo |
| router.py | La regla nueva, el sistema evaluado |
| oloproof.yaml | La suite |
| release.yaml | La política de publicación |
Cada línea del registro es una decisión:
{"id":"t001","input":{"topic":"technical","length":"short"},"expected":{"action":"small","logging_probabilities":{"small":0.7,"large":0.3},"reward":1}}input es lo que vio el enrutador, action lo que eligió, logging_probabilities la probabilidad que dio a cada modelo en ese momento, y reward si el ticket se resolvió. La regla nueva solo responde con sus propias probabilidades:
@system(name="ticket-router", version="by-topic-v1")
def route_by_topic(ticket):
large = ticket["topic"] == "technical" or (
ticket["topic"] == "billing" and ticket["length"] == "long"
)
return {"probabilities": {"small": 0.0 if large else 1.0, "large": 1.0 if large else 0.0}}Los evaluadores:
evaluators:
- {type: off_policy_value, reward_range: [0, 1], propensity_floor: 0.3}
- {type: off_policy_improvement, reward_range: [0, 1], propensity_floor: 0.3}Cada decisión registrada se repondera según cuánto más, o menos, probable era que la regla nueva la tomara que el enrutador antiguo: un ticket que el enrutador antiguo envió al modelo grande con probabilidad 0.3, y que la regla nueva enviaría siempre allí, cuenta 1 / 0.3 veces, y una decisión que la regla nueva nunca habría tomado cuenta cero. propensity_floor es la probabilidad más pequeña que el enrutador antiguo dio a cualquier modelo que pudiera elegir. Acota ese peso, y sin una cota no es válido ningún intervalo.
Ejecutarlo
oloproof run│ resolution-floor │ off_policy_value │ PASS │ lower_bound_meets_minimum │
│ better-than-current │ off_policy_improvement │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ │ │ │ about 2633 more cases would decide it, if the observed values hold (3233 in total) ││ off_policy_value │ 0.775 │ [0.632, 0.921] │ mean of 600 observed · 0 missing · 0 excluded │
│ off_policy_improvement │ 0.060 │ [-0.064, 0.188] │ mean of 600 observed · 0 missing · 0 excluded │La regla nueva resolvería un 77.5% estimado de los tickets y, con un 95% de confianza, al menos el 63.2%: el mínimo del 60% aprueba. Si supera al enrutador actual no está resuelto. La estimación es 6 puntos mejor, pero el intervalo va de 6.4 puntos peor a 18.8 mejor, y el motor indica aproximadamente cuántas decisiones registradas más lo decidirían.
Esa anchura es el precio de aprender de las elecciones de otro. Solo el 30% de los tickets que el enrutador antiguo envió al modelo grande por azar dice algo sobre el modelo grande, y cada uno cuenta más de tres veces. Dos cosas lo estrechan: un registro más largo, y un enrutador antiguo que explore más.
Registrar decisiones de su propio sistema
- Registre las probabilidades cuando el enrutador decide, no después, y registre la probabilidad de cada opción, no solo la de la elegida. Oloproof no las estima: una probabilidad adivinada haría que el intervalo descansara en la suposición.
- Ponga en input solo lo que vio el enrutador cuando decidió, nunca la opción elegida ni el resultado: la política evaluada no debe verlos.
- Si su nueva política muestrea su elección, devuelva las probabilidades de las que muestrea, no la única opción que obtuvo. Nombre cada opción exactamente como lo hace el registro: una opción escrita de otra forma cuenta como una que el enrutador antiguo nunca eligió.
- Mantenga algo de aleatoriedad en el enrutamiento de producción. Una política que nunca elige una opción no deja nada en el registro con lo que juzgar una regla que sí la elegiría.
- La respuesta describe tickets como los registrados, durante el periodo que cubre el registro. Un cambio en su tráfico queda fuera de ella.
- Una línea mal formada (probabilidades que no suman 1, una opción elegida con probabilidad 0, una recompensa fuera de reward_range) detiene la ejecución. Una decisión que la regla nueva tomaría con una opción a la que el enrutador antiguo dio menos que el mínimo, o cuya recompensa falta, cuenta como faltante y ensancha el intervalo.
Lo que no está construido
- La evaluación fuera de política de una secuencia de decisiones, donde la elección de cada paso cambia lo que viene después. Solo se admite una decisión por caso.
- Un registro cuyo enrutador cambió sus probabilidades a medida que aprendía durante la ventana.
- Alojar o reiniciar entornos, simuladores o robots.
Siguientes pasos
- Aprendizaje por refuerzo: la referencia de estos evaluadores y del registro de episodio.
- Escribir una suite: las réplicas y los demás campos de la suite.
- Comparar un candidato con una línea base: lo que lee una comparación emparejada.
- Conceptos básicos: hasta dónde llega un resultado y hasta dónde no.