-
-
Notifications
You must be signed in to change notification settings - Fork 21.1k
[Feature] Mask Replay #49577
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
[Feature] Mask Replay #49577
Changes from 71 commits
Commits
Show all changes
132 commits
Select commit
Hold shift + click to select a range
1a320a7
Implement Mask Replay
vx120 a444039
Optimize sampling mask storage
vx120 9c6db21
Streamlined the code and removed unnecessary parts.
vx120 95d453f
Merge branch 'vllm-project:main' into mask_replay
vx120 da36481
Require final-only output for sampling replay
vx120 a37a134
add pre-commit code
vx120 2129592
Merge branch 'vllm-project:main' into mask_replay
vx120 e3b8d31
Clean up formatting in scheduler.py
vx120 bbffe02
Require bounded top-k for sampling replay
vx120 fee020e
Improve error message for top_k validation
vx120 145da58
Merge branch 'vllm-project:main' into mask_replay
vx120 d84da9d
pre-commit change
vx120 8c4144f
Merge branch 'vllm-project:main' into mask_replay
vx120 045c413
Validate sampling replay output mode early
aoshen02 2951912
Gate sampling masks in scheduler
aoshen02 754a737
Merge pull request #3 from aoshen02/codex/fix-sampling-replay-validation
aoshen02 8cdca91
Compact sampling support before async output
aoshen02 4bce273
Merge pull request #9 from aoshen02/codex/compact-sampling-support-ou…
aoshen02 26199bf
Merge pull request #6 from aoshen02/codex/sampling-mask-scheduler-flag
aoshen02 fdae7cc
Remove FlashInfer sampling mask log
aoshen02 0acee62
Merge pull request #10 from aoshen02/codex/remove-flashinfer-sampling…
aoshen02 41f18c6
Disable FlashInfer sampling for sampling masks
aoshen02 a5591cc
Merge pull request #11 from aoshen02/codex/disable-flashinfer-samplin…
aoshen02 ba8aa3a
Keep sampling mask tensors local to sampler
aoshen02 f038732
Merge pull request #12 from aoshen02/codex/keep-sampling-mask-local
aoshen02 880788b
Move sampling mask to its own module
aoshen02 8622dbd
Merge pull request #14 from aoshen02/codex/extract-sampling-mask-output
aoshen02 00c2ed2
Restore sampler spacing
aoshen02 0ac290f
Merge pull request #16 from aoshen02/codex/restore-processed-logprobs
aoshen02 209a885
refactor: align sampling mask tensor conversion
aoshen02 cef55ab
Merge pull request #17 from aoshen02/codex/python-sampling-mask-conve…
aoshen02 b25bed1
Refactor sampling mask compaction
aoshen02 fac80d4
Merge pull request #18 from aoshen02/codex/encapsulate-sampling-mask-…
aoshen02 78b54ac
Merge branch 'main' into mask_replay
aoshen02 3c5e664
Align sampling mask output with Rust protocol
vx120 cf2ad7e
Merge branch 'vllm-project:main' into mask_replay
vx120 1c7b816
[Feature] Expose sampling mask via HTTP endpoints as list[list[int]]
aoshen02 a9ae6e5
Remove sampling_mask from OpenAI-compat endpoints
aoshen02 c3a2a38
Merge pull request #19 from aoshen02/worktree-sampling-mask-endpoint
aoshen02 2d947dd
Sync sampling mask protocol fixtures
vx120 8905ebb
Enforce processed_logprobs mode for sampling mask
aoshen02 1fccd75
Merge pull request #20 from aoshen02/fix-logprobs-mode-validation
aoshen02 1e71000
Merge branch 'main' into mask_replay
vx120 03fae73
Fix sampling mask scheduler test setup
vx120 3963b7e
Merge branch 'vllm-project:main' into mask_replay
vx120 d886296
Move new_sampling_mask to end of EngineCoreOutput, revert Rust changes
aoshen02 95f6a6e
Merge pull request #21 from aoshen02/fix-sampling-mask-field-order
aoshen02 ac6602f
Fix python_compat.py: move new_sampling_mask to end, remove wire Nil
aoshen02 97a96a0
Update output.rs
aoshen02 26b767b
Merge pull request #22 from aoshen02/fix-python-compat-field-order
aoshen02 e0d43ad
Remove stale sampling_mask wire Nil from client.rs test fixture
aoshen02 19c3ff9
Merge pull request #23 from aoshen02/fix-client-rs-wire-nil
aoshen02 f8427ac
Remove FINAL_ONLY requirement for sampling mask
aoshen02 bbaa49c
Merge pull request #24 from aoshen02/remove-final-only-requirement
aoshen02 6d3836e
Merge branch 'main' into mask_replay
vx120 e4e0ae1
add validation of sampling replay params
vx120 fb14bec
Merge branch 'main' into mask_replay
vx120 a74b537
Add sampling mask feature documentation
aoshen02 278a7b2
Merge pull request #25 from aoshen02/add-sampling-mask-docs
aoshen02 6e7747a
fix: markdownlint MD060 table-column-style in sampling_mask.md
aoshen02 695ca45
Merge pull request #26 from vx120/fix/sampling-mask-md-lint-49577
aoshen02 7cf69ba
Remove logprobs check in input_processor.py
vx120 bf2865d
Merge branch 'main' into mask_replay
vx120 a49d465
[Feature] Expose sampling mask via HTTP endpoints as list[list[int]]
aoshen02 7f90a9f
Remove sampling_mask from OpenAI-compat endpoints
aoshen02 4421bd4
Merge pull request #19 from aoshen02/worktree-sampling-mask-endpoint
aoshen02 93dc02e
Enforce processed_logprobs mode for sampling mask
aoshen02 ca15b4c
Sync sampling mask protocol fixtures
vx120 982ceb9
Merge pull request #20 from aoshen02/fix-logprobs-mode-validation
aoshen02 b799148
Merge branch 'main' into mask_replay
vx120 1733f82
Move new_sampling_mask to end of EngineCoreOutput, revert Rust changes
aoshen02 4dcc2cc
Fix sampling mask scheduler test setup
vx120 b8fcc29
Merge branch 'vllm-project:main' into mask_replay
vx120 e77d1d9
Merge pull request #21 from aoshen02/fix-sampling-mask-field-order
aoshen02 cb8c60a
Fix python_compat.py: move new_sampling_mask to end, remove wire Nil
aoshen02 fcd283a
Update output.rs
aoshen02 f332d61
Remove stale sampling_mask wire Nil from client.rs test fixture
aoshen02 87fd402
Merge pull request #22 from aoshen02/fix-python-compat-field-order
aoshen02 a69ae8c
Remove FINAL_ONLY requirement for sampling mask
aoshen02 ee4f6fb
Merge pull request #23 from aoshen02/fix-client-rs-wire-nil
aoshen02 73f7c71
Merge pull request #24 from aoshen02/remove-final-only-requirement
aoshen02 8987b3d
Merge branch 'main' into mask_replay
vx120 e2d4fa0
add validation of sampling replay params
vx120 55d701b
Add sampling mask feature documentation
aoshen02 7c9722d
Merge branch 'main' into mask_replay
vx120 818b022
Merge pull request #25 from aoshen02/add-sampling-mask-docs
aoshen02 402766a
fix: markdownlint MD060 table-column-style in sampling_mask.md
aoshen02 decfccb
Merge pull request #26 from vx120/fix/sampling-mask-md-lint-49577
aoshen02 7f10866
fix(rust): add new_sampling_mask slot to EngineCoreOutput
aoshen02 911496d
Remove logprobs check in input_processor.py
vx120 8235f6b
Merge branch 'main' into mask_replay
vx120 90d17ea
Merge pull request #27 from vx120/fix/sampling-mask-md-lint-49577
aoshen02 6dae808
Merge branch 'main' into mask_replay
vx120 bb08e6f
Merge branch 'main' into mask_replay
vx120 16e1c80
Merge branch 'main' into mask_replay
vx120 6d7edbd
Merge branch 'main' into mask_replay
vx120 79c3986
Merge branch 'main' into mask_replay
vx120 d8fcd2c
Merge branch 'main' into mask_replay
vx120 f0e800d
Merge branch 'mask_replay' of https://github.com/vx120/vllm into mask…
vx120 de61e3c
Merge branch 'main' into mask_replay
vx120 8758c7a
Merge branch 'mask_replay' of https://github.com/vx120/vllm into mask…
vx120 966e96c
Avoid GPU sync when collecting sampling masks
vx120 95cecfe
Merge branch 'main' into mask_replay
vx120 cd4eaf1
Merge branch 'main' into mask_replay
vx120 e69e436
Merge branch 'main' into mask_replay
vx120 ca46f77
add pre-commit code
vx120 19c6c86
Merge branch 'main' into mask_replay
vx120 997eb90
Merge branch 'main' into mask_replay
vx120 d49e7e4
Merge upstream main into mask_replay
vx120 2ec706d
Merge branch 'main' into mask_replay
vx120 9c793ef
Fix pre-commit import ordering
vx120 4d638b1
Merge remote-tracking branch 'origin/mask_replay' into mask_replay
vx120 e97181d
Merge branch 'main' into mask_replay
vx120 38fc120
Merge branch 'main' into mask_replay
aoshen02 572fdbd
Fix sampling mask tensor test assertions
aoshen02 442d302
Merge pull request #28 from aoshen02/codex/fix-sampling-mask-tests
aoshen02 13e9e1f
Merge branch 'main' into mask_replay
aoshen02 a23eeb6
Remove sampling mask CPU fallback
aoshen02 ffeac59
Merge pull request #29 from aoshen02/codex/remove-sampling-mask-cpu-f…
aoshen02 f90415f
Merge branch 'main' into mask_replay
aoshen02 9e08019
Merge branch 'main' into mask_replay
vx120 59c64b9
Address sampling mask review feedback
aoshen02 c992544
Add sampling mask endpoint coverage
aoshen02 63f97c6
Merge pull request #30 from aoshen02/codex/address-mask-replay-review…
aoshen02 c937874
Merge branch 'main' into mask_replay
aoshen02 bbe9552
Fix sampling mask support assertions
aoshen02 2eae885
Merge pull request #31 from aoshen02/codex/fix-sampling-mask-support-…
aoshen02 ab0c94b
Fix sampling mask Rust wire compatibility
aoshen02 0276e94
Merge pull request #32 from aoshen02/codex/fix-sampling-mask-rust-wire
aoshen02 b53b2a9
Merge branch 'main' into mask_replay
aoshen02 5b15d36
Fix Rust sampling mask test formatting
aoshen02 ab01a11
Merge pull request #33 from aoshen02/codex/fix-sampling-mask-rustfmt
aoshen02 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| # Sampling Mask (Distribution Replay) | ||
|
|
||
| When using top-k/top-p sampling for RL rollouts (e.g. GRPO), there is a | ||
| systematic mismatch between the truncated distribution the sampler actually | ||
| drew from and the full-vocabulary softmax used to compute log-probabilities | ||
| during training. The **sampling mask** feature closes this gap by returning | ||
| the exact set of token IDs that survived top-k/top-p/min-p filtering at each | ||
| generation step, so the training side can normalize over the same support. | ||
|
|
||
| ## Background | ||
|
|
||
| This feature implements the **Keep Sampling Mask** strategy described in the | ||
| [DeepSeek-V3.2 technical report](https://huggingface.co/deepseek-ai/DeepSeek-V3.2/blob/main/assets/paper.pdf) | ||
| (Section 3.3). The key insight: top-k/top-p truncation during rollout sampling | ||
| introduces a mismatch between the action spaces of `π_old` and `π_θ`, which | ||
| violates the principles of importance sampling and destabilizes training. By | ||
| preserving the truncation masks from `π_old` and applying them to `π_θ` during | ||
| training, both policies share identical action subspaces. DeepSeek reports that | ||
| combining top-p sampling with the Keep Sampling Mask strategy effectively | ||
| preserves language consistency during RL training. | ||
|
|
||
| ## Quick start | ||
|
|
||
| ```bash | ||
| vllm serve <model> \ | ||
| --enable-return-sampling-mask \ | ||
| --logprobs-mode processed_logprobs | ||
| ``` | ||
|
|
||
| ```python | ||
| from vllm import LLM, SamplingParams | ||
|
|
||
| llm = LLM(model, enable_return_sampling_mask=True, | ||
| logprobs_mode="processed_logprobs") | ||
| output = llm.generate( | ||
| "The capital of France is", | ||
| SamplingParams(temperature=1.0, top_k=50, top_p=0.95, logprobs=1), | ||
| ) | ||
| mask = output[0].outputs[0].sampling_mask | ||
| # mask.token_ids: [[187, 326, 512], [42, 88], ...] | ||
| # mask.token_ids[i] = token IDs in the sampling support for generated token i | ||
| ``` | ||
|
|
||
| The mask is also available via the `/inference/v1/generate` HTTP endpoint: | ||
|
|
||
| ```json | ||
| { | ||
| "choices": [{ | ||
| "token_ids": [187, 42, 303], | ||
| "sampling_mask": [[187, 326, 512], [42, 88], [303, 11, 22]], | ||
| "finish_reason": "stop" | ||
| }] | ||
| } | ||
| ``` | ||
|
|
||
| ## Requirements | ||
|
|
||
| | Requirement | Reason | | ||
| | --- | --- | | ||
| | `--enable-return-sampling-mask` | Engine-level opt-in (disables FlashInfer sampler) | | ||
| | `--logprobs-mode processed_logprobs` | Returned logprobs are normalized over the nucleus, not full vocab | | ||
| | `temperature > 0` | Greedy has no truncated distribution | | ||
| | `top_k > 0` | Bounds mask size; pure top-p can produce vocab-sized masks | | ||
| | Model Runner V2 | Required by the async D2H copy pipeline | | ||
|
|
||
| The engine rejects unsupported combinations at startup or request time: | ||
|
|
||
| - Speculative decoding | ||
| - Diffusion models | ||
| - Custom logits processors (engine-level `--logits-processors`) | ||
|
|
||
| ## How it works | ||
|
|
||
| 1. The sampler applies all logit processors (penalties, logit bias, bad words, | ||
| temperature, min-p) and then top-k/top-p filtering, which sets excluded | ||
| logits to `-inf`. | ||
| 2. After sampling, `torch.isfinite(processed_logits)` identifies the surviving | ||
| token IDs — this is the sampling mask. | ||
| 3. The mask is transferred GPU → CPU asynchronously alongside sampled tokens. | ||
| 4. On request completion, per-step masks are merged and converted to | ||
| `list[list[int]]` for the response. | ||
|
|
||
| ## RL training usage | ||
|
|
||
| The training side needs two things for the importance ratio `π_θ/π_old`: | ||
|
|
||
| **`π_old(a|s)` — old policy's nucleus-normalized logprob:** | ||
| Already returned by vLLM when `--logprobs-mode processed_logprobs` is set. | ||
| The `log_softmax` is computed over processed logits (where filtered tokens | ||
| are `-inf`), so the denominator only includes the nucleus. | ||
|
|
||
| **`π_θ(a|s)` — current policy's nucleus-normalized logprob:** | ||
| Computed by the training framework using the mask: | ||
|
|
||
| ```python | ||
| # mask_ids: list[int], the sampling support for this token | ||
| # logits: the training model's raw logits for this position | ||
| keep = torch.zeros(vocab_size, dtype=torch.bool) | ||
| keep[mask_ids] = True | ||
| masked_logits = logits.masked_fill(~keep, float("-inf")) | ||
| log_prob = log_softmax(masked_logits)[sampled_token_id] | ||
| ``` | ||
|
|
||
| Both sides normalize over the same token set, so the importance ratio is | ||
| consistent. | ||
|
|
||
| ## Limitations | ||
|
|
||
| - **Engine-level flag:** `--enable-return-sampling-mask` globally disables the | ||
| FlashInfer fused sampler. All requests pay the cost of the PyTorch sampling | ||
| path, even if they don't need the mask. | ||
| - **No streaming support:** The mask is returned only in the final response, | ||
| not in intermediate streaming chunks. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will it be a follow up pr to support spec?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We’ll evaluate the need after the current phase is complete
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I would strongly recommend doing this.