fix(dspark): loss mask off-by-one#865
Conversation
📝 WalkthroughWalkthroughChangesDFlash loss-mask alignment
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Merge Protections🔴 1 of 1 protections blocking · waiting on 👀 reviews
🔴 Require approval from approved reviewers listWaiting for any of
This rule is failing.All pull requests must have at least one approving review from a member of the approved reviewers list before merging.
|
3fa5a41 to
451a883
Compare
With `sample_from_anchor=True` -- DSpark's default -- slot j of anchor a is supervised by the verifier's distribution over token a+j+1, but the loss mask was read at a+j. The two indices only disagree where the mask changes, so the effect is confined to turn boundaries: the slot past a turn's last content token was trained on the separator that follows it. Both SpecForge and TorchSpec gather the mask at the shifted index. The code lives in the DFlash backbone that DSpark inherits, so DFlash is fixed too when run with the flag set; its default of False was already correct and is unchanged. The same mask feeds full_acc, position_k_acc, eal, accept_rate and the confidence-head target, so reported acceptance was slightly flattered -- expect a small shift in those numbers, not a regression. Signed-off-by: Ranran Haoran Zhang <ranzhang@redhat.com>
451a883 to
9e499cc
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/integration/conftest.py (1)
138-150: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winKeep the proposal count aligned with
sample_from_anchor.This helper forwards
sample_from_anchor=True, but the proposal configuration below still hard-codesspeculative_tokens=block_size - 1. Production configuration usesblock_sizewhen sampling from the anchor, so tests can exercise inconsistent proposal-length metadata.As per path instructions, the integration helper should propagate the new code path consistently across its configuration.
Suggested fix
proposal_methods=[ - GreedyTokenProposalConfig(speculative_tokens=block_size - 1) + GreedyTokenProposalConfig( + speculative_tokens=( + block_size if sample_from_anchor else block_size - 1 + ) + ) ],🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/integration/conftest.py` around lines 138 - 150, Update the DFlashSpeculatorConfig setup in the integration model helper so speculative_tokens is derived from sample_from_anchor: use block_size when sampling from the anchor and block_size - 1 otherwise. Keep the helper’s proposal-length metadata consistent with production configuration.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@tests/integration/conftest.py`:
- Around line 138-150: Update the DFlashSpeculatorConfig setup in the
integration model helper so speculative_tokens is derived from
sample_from_anchor: use block_size when sampling from the anchor and block_size
- 1 otherwise. Keep the helper’s proposal-length metadata consistent with
production configuration.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 536716cb-7006-4b7b-87c4-caff5a67e70a
📒 Files selected for processing (3)
src/speculators/models/dflash/core.pytests/integration/conftest.pytests/integration/models/test_model_forward.py
Purpose
Affects
sample_from_anchor=True, DSpark's default. DFlash defaults to False and is unchanged.A draft slot was gated by the loss mask at its own position instead of at the token it predicts. A hidden state at position i yields the distribution over token i+1, so the mask has to be read one position ahead.
Assistant turn at positions 8-39,
block_size=4, anchora=37:The two indices only disagree where the mask changes, so this is confined to turn boundaries — 0.47% of supervised slots at 200-token turns, 5.7% at 20-token turns, always over-supervision.
Tests
One test: every slot the model marks trainable must be predicting a supervised token. Red with the fix reverted, green with it — the failure is the bug stated directly,
loss_masksampled at each trained slot's predicted token, which should be all ones.ruff checkandruff format --checkpass.Note for reviewers: this mask also feeds
full_acc,eal,accept_rateand the confidence-head target, so acceptance numbers will shift slightly. That is the correction, not a regression.Checklist
I have filled in: