vLLM V1 corrige logprobs y alcanza paridad con V0 | Keryc
vLLM V1 iguala el comportamiento de vLLM V0 en un experimento de RL luego de arreglar cuatro problemas clave: processed_logprobs, defaults de runtime específicos de V1, la ruta de actualizaciones inflight de pesos, y el fp32 lm_head para la proyección final. Antes de tocar el objetivo de RL, el equipo corrigió el backend y luego evaluó cambios a nivel de objetivo.
Qué pasó en pocas palabras
¿Por qué importó tanto este cambio? Porque en sistemas de RL online, los logprobs que vienen del backend son parte directa de la función objetivo. Si esos logprobs no significan lo mismo para el trainer y el rollout backend, la optimización queda sesgada.
En el experimento citado, la referencia usó vLLM 0.8.5 y las pruebas de migración usaron vLLM 0.18.1. La primera corrida con V1 mostró desviaciones claras en métricas como clip rate, kl_new_old, entropy, y reward. El equipo separó las causas en capas y fue resolviendo de abajo hacia arriba: primero semántica, luego path de inferencia, y solo al final la parte de objetivo.
Diagnóstico por capas
Se analizaron tres clases de errores posibles:
Semantic mismatch: el backend devuelve logprobs con un significado distinto al que espera el trainer.
Inference-path mismatch: diferencias en defaults de runtime que hacen que la misma entrada siga caminos de ejecución distintos.
Objective mismatch: la función objetivo necesita correcciones por staleness o comportamiento asincrónico.
Al principio se sospechó la tercera categoría demasiado pronto. La clave fue tratar primero las dos primeras como problemas de backend y descartarlas.
Síntomas observables
La media de la razón de políticas (policy ratio) se desplazaba lejos de 1.0 en la primera V1.
El clip rate mostró rápidamente la discrepancia entre V0 y la V1 inicial.
El trainer veía logprobs y reward moverse lejos del referente desde etapas tempranas del entrenamiento.
Estos síntomas también aparecen en otros esquemas de RL online como PPO o GRPO cuando el rollout usa logprobs que no coinciden con lo que el trainer espera.
Las cuatro correcciones que restauraron paridad
Procesamiento semántico de logprobs
vLLM V1 por defecto entregaba logprobs desde las salidas crudas del modelo, antes de operaciones de post-procesado (temperature, penalties, top-k/top-p). PipelineRL esperaba los logprobs tal como los usa el sampler.
La configuración requerida fue:
logprobs-mode=processed_logprobs
Esto corrigió el offset medio en rollout logprobs y centró la razón de política alrededor de 1.0. Pero aún quedaba diferencia en clip rate, KL, entropy y comportamiento en downstream.
Alineación del path de inferencia y defaults de runtime
La primera corrida mezcló la versión del engine con defaults de runtime de V1 (por ejemplo prefix caching y async scheduling). Para la corrida de paridad las opciones se hicieron explícitas en la configuración:
¿Por qué desactivar prefix caching? Normalmente es una optimización correcta para inferencia con estado fijo, pero en RL online la vida útil y la reutilización de cache en V1 difería de V0. Un cache hit podía reutilizar estado computado antes de una actualización de pesos si la política de cache ignoraba el límite de weight-update. Desactivarlo quitó una diferencia exclusiva de V1.
Coincidencia del flujo de actualización de pesos en vuelo (inflight)
V0 implementaba algo cercano a: bloquear en un boundary del engine, cargar nuevos pesos y reanudar sin invalidar explícitamente estado cacheado. Para reproducir ese comportamiento en V1 se usó:
Los detalles mode="keep" y clear_cache=False replicaron la semántica de inflight updates de V0 y eliminaron lag persistente que apareció en la V1 inicial.
Proyección final en fp32 para los logits
El trainer estaba usando un fp32 lm_head para la proyección final. Pequeñas diferencias numéricas en logits derivan en cambios en logprobs y eso se magnifica en ratios de política, KL y clipping. Incluir el camino del lm_head en fp32 cerró la última brecha numérica.
Este mismo tipo de problema fue reportado en el informe MiniMax-M1 y en la literatura posterior, donde calcular el head en fp32 corrigió discrepancias entre entrenamiento e inferencia.
Qué no funcionó y por qué es importante el orden
Aplicar solo processed_logprobs arregló la semántica, pero no solucionó todas las diferencias. La corrida seguía mostrando lag y discrepancias en clipping.
Tratar la V1 inicial como baseline justo fue un error: tenía varios defaults de V1 activados que confunden la comparación.
Corregir la función objetivo sin antes arreglar el backend habría mezclado dos preguntas distintas y habría ocultado errores de inferencia.
La lección es clara: arregla la corrección del backend primero, luego añade correcciones a nivel objetivo si todavía son necesarias por staleness o asincronía.
Qué sigue y buenas prácticas para RL online
Después de restaurar la paridad de inferencia, las mejoras típicas a nivel de objetivo son razonables. Algunas recomendaciones prácticas:
Conserva los logprobs de la policy de comportamiento en tiempo de rollout.
Recalcula los old-policy logprobs en el trainer al optimizar, si es factible.
Mantén separadas las correcciones por mismatch de backend y las del ratio de política.
Monitorea diagnósticos como ESS (effective sample size) junto a métricas agregadas del trainer.
Si aplicas correcciones del objetivo antes de verificar la equivalencia de inferencia, corres el riesgo de enmascarar un backend roto y perder señales importantes sobre por qué el entrenamiento cambia.
Reflexión final
Este caso es un recordatorio técnico y práctico: en RL online la precisión de la parte de inferencia no es un detalle menor. Pequeñas discrepancias semánticas o numéricas en logprobs y en el manejo de caches y actualizaciones inflight se traducen en grandes diferencias de comportamiento. ¿La moraleja? Comprueba la paridad del backend primero, documenta los defaults de runtime, y solo después evalúa cambios de objetivo.