feat: top-p/top-k train sampling with native sampling replay - #3431
Conversation
14a0bbf to
77c9264
Compare
Truncated train sampling (top_p < 1, top_k) renormalizes the rollout distribution over the surviving kept set; rollout logprobs reflect that (processed_logprobs) while the trainer normalizes over the full vocab, biasing every importance ratio. Record the kept set at sampling time and renormalize trainer logprobs over the same set (DeepSeek V3.2's Keep Sampling Mask, arXiv:2512.02556 3.1). Same user API as #3235: [orchestrator.train.sampling] top_p/top_k, no replay flags. Truncating policy sampling auto-enables inference.enable_return_sampling_mask, bounds top_k to 512 (trainer mask tensors pad to the largest kept set), and rejects opd/opsd and temperature 0. Unlike #3235 the capture is vLLM's native --return-sampling-mask (>= 0.28, V2 model runner) instead of custom engine patches: the /generate response carries sampling_mask natively, renderers parse it (PrimeIntellect-ai/renderers#144) and verifiers carry it as KeptTokens arrays (PrimeIntellect-ai/verifiers#2460). Capture is engine-wide: vLLM rejects requests with temperature <= 0 or top_k <= 0 while it is on, and it is incompatible with router replay (V1-only).
a34da34 to
5a657f6
Compare
…replay # Conflicts: # deps/renderers # deps/verifiers
…replay # Conflicts: # deps/verifiers
|
From Sami — written by Codex: Sampling replay currently breaks for multimodal batches with context parallelism. In the deferred VLM path, The sampling mask should be sharded alongside |
|
Fixed the multimodal context-parallel sampling-mask mismatch in bd24f08. Sampling masks now shard with labels even when VLM input sharding is deferred. A pre-forward assertion also verifies mask-label alignment. Focused LM-head and loss tests pass: 19 passed, 1 skipped. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit bd24f08. Configure here.

Summary
top_k = 512when truncation is enabled.SamplingMaskandsampling_maskacross Renderers, Verifiers, and prime-rl.0.1.12.dev2build.mainafter its sampling-mask merge.This builds on vLLM 0.28.0 from #3430. It supersedes #3235. The capture side uses vLLM's native sampling-mask support from vllm#49577.
Usage
There are no replay flags for sampling replay. Truncated policy sampling enables mask capture and trainer replay automatically.
Set
trainer.enable_router_replay = trueto combine router replay with sampling replay. Standard deployments use Model Runner V2 for both captures. Disaggregated NIXL deployments keep router replay on V1 and cannot combine both replay modes.Behavior
top_p < 1ortop_kgets a bounded sampling mask.top_kdefaults to 512.opdandopsdreject truncated policy sampling because their reference scores use the full vocabulary.temperature <= 0or without an effectivetop_k > 0.Runs
GLM-4.5-Air on
scalesweon 4 nodesglm-air-v1-baselineglm-air-v2-baselineglm-air-v2-router-replayglm-air-v2-top-p-0.95glm-air-v2-router-replay-top-p-0.95Sampling replay leads to
Verification
uv run pytest -q tests/unit/test_configs.py -k 'policy_sources': 2 passed.uv run pytest -q tests/unit/test_configs.py -k 'not test_load_configs': 69 passed and 67 deselected.Pre-commit checks passed for the changed config and test files.
uv run pytest tests/unit/train/rl/test_fused_lm_head.py tests/unit/train/rl/test_loss.py -q: 19 passed and 1 skipped after the context-parallel fix.uv run pytest tests/unit/test_configs.py::test_combined_replay_uses_v2_runner -q: passed after the documentation review.uv sync --all-extras: passed with merged Renderers and Verifiers dependencies.uv run pytest tests/unit/orchestrator tests/unit/inference tests/unit/train/rl/test_loss.py tests/unit/train/rl/test_fused_lm_head.py --ignore=tests/unit/orchestrator/test_qwen3_vl_e2e.py -q: 108 passed and 1 skipped.The excluded Qwen3-VL test has an existing fake-response fixture mismatch.
uv run rl @ examples/basic/reverse-text/rl.toml --max-steps 5 --orchestrator.train.sampling.top-p 0.95: completed five trainer and orchestrator steps on the final dependency chain.The end-to-end run used Model Runner V2, effective
top_k = 512, and sampling-mask capture. Final mismatch KL was 0.0035 with no rollout errors.Renderers:
uv run pytest tests/test_client.py -q: 21 passed and 5 skipped. Ruff and format checks pass.Latest Verifiers main:
uv run pytest deps/verifiers/tests/v1/test_graph.py deps/verifiers/tests/v1/test_trace.py -q: 21 passed. Ruff, format, type, and pre-commit checks pass.Reverse-text baseline, top-p 0.95, and top-p 0.95 plus top-k 20 runs completed with 100% sampling-mask coverage.
W&B:
reverse-text/reverse-text-native-replay-{baseline,topp095,topp095-topk20}.🤖 Generated with Claude Code
Note
High Risk
Changes core RL logprob/importance-ratio math and vLLM runner selection; misconfiguration or missing masks can bias training or fail at runtime, though validation and runtime checks aim to catch incompatible modes.
Overview
Adds sampling replay so policy rollouts with
top_p < 1ortop_kstay aligned with trainer importance ratios: vLLM 0.28 returns per-token sampling masks (enable_return_sampling_mask/--return-sampling-mask), and the trainer renormalizes logprobs over the same mask instead of the full vocabulary.Config & orchestration:
TrainSamplingConfiggains typedtop_pandtop_k(truncation viaextra_bodyis rejected). Therlentrypoint auto-enables mask capture when policy sampling truncates; unbounded truncation defaultstop_k = 512, values above 512 are rejected,temperature = 0andopd/opsdare blocked. Policy train sources must agree on top-k capture mode (engine-wide). Inference: newenable_return_sampling_mask; vLLM env setup prefers V2 for sampling capture and for router replay on standard deployments, while disaggregated NIXL P/D keeps routed-expert capture on V1 and rejects router + sampling replay together.Data path:
SamplingMaskonTrainingSample/MicroBatch, encoding in trajectories, packing/padding in the trainer batch builder, and a hard error inTrainSinkif truncated rollouts lack masks. Trainer: masks flow through CP sharding; fusedlm_headand vanilla-pathselective_log_softmax_with_sampling_maskcompute mask-renormalized logprobs (Gemma softcap heads explicitly unsupported). Docs cover sampling replay;renderersis bumped for mask support in the rollout stack.Reviewed by Cursor Bugbot for commit cee517e. Bugbot is set up for automated code reviews on this repo. Configure here.