Add D-PACE loss implementation for D-Flash training#736
Conversation
📝 WalkthroughWalkthroughAdds D-PACE (per-position confidence-based) loss weighting as an alternative to fixed-exponential decay. Introduces CLI flags, extends DFlashDraftModel's forward and get_trainer_kwargs to propagate the new configuration, adds branching logic in compute_metrics, and adds a new dpace_loss_weight helper plus optional precomputed CE support in compound_loss and loss_function. ChangesD-PACE loss weighting
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/speculators/models/metrics.py (2)
281-320: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winValidate
alpharange for numerical stability.Confirmed against the D-PACE paper (arXiv:2605.18810) that this implementation matches the published smoothing/prefix-product/suffix-sum weighting scheme. However, the paper notes the smoothing parameter is only meaningful for a lower bound preventing the cumulative product in the weight from vanishing, and specifically that at α = 0, where the cumulative product vanishes and τ stalls near 1.0. There's no validation here that
alphastays within[0, 1]; an out-of-range value (e.g., negative or >1, or the degeneratealpha=0) would silently produce meaningless/unstable weights instead of failing fast.🛡️ Proposed fix
with torch.no_grad(): + if not 0.0 < alpha <= 1.0: + raise ValueError(f"alpha must be in (0, 1], got {alpha}") # convert CE to per-position confidence q = torch.exp(-neg_log_q).float()🤖 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 `@src/speculators/models/metrics.py` around lines 281 - 320, Validate the smoothing parameter in dpace_loss_weight before computing the prefix product: add an explicit check that alpha is within the supported range, rejecting out-of-range values and the degenerate zero case with a clear failure. Keep the existing weighting logic unchanged, but ensure the function fails fast for invalid alpha so the numerical stability of the D-PACE weighting is preserved.
300-302: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueReshape relies on exact block-size divisibility.
num_anchors = q.shape[1] // block_sizeuses floor division; ifq.shape[1]isn't an exact multiple ofblock_size, the subsequent.reshape(num_anchors, block_size)calls will raise a runtime error rather than a clear, actionable message. In the current call path (compute_metricsindflash/metrics.py),seq_lenis alwaysnum_anchors * block_sizeby construction, so this shouldn't trigger today, but a defensive assert would make future misuse fail with a clearer error.🤖 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 `@src/speculators/models/metrics.py` around lines 300 - 302, The reshape logic in the metrics path assumes q.shape[1] is exactly divisible by block_size, so add a defensive validation before computing num_anchors in the relevant reshape block. Update the code around the q.reshape and loss_mask.reshape operations to assert or explicitly check divisibility and raise a clear error message if the sequence length is not compatible with block_size, using the existing compute_metrics / metrics reshape flow as the location to safeguard.scripts/train.py (1)
964-976: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winConsider validating
--dpace-alphaat parse time.Per the D-PACE paper, alpha is only meaningful in
[0, 1], withalpha=0causing the weighting to degenerate. Argparse currently accepts any float unchecked. Adding a lightweight validator here (or indpace_loss_weight, see comment inmetrics.py) would catch a misconfigured training run early rather than after silently training with a degenerate loss weighting.As per path instructions, "Check that scripts handle argument parsing robustly."
🤖 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 `@scripts/train.py` around lines 964 - 976, Add parse-time validation for the D-PACE smoothing argument so invalid values are rejected early. In the argument setup around parser.add_argument for --dpace-alpha, enforce that the value is within the valid [0, 1] range and disallow degenerate settings such as 0, either via an argparse type/validator or a small reusable check used by dpace_loss_weight. Keep the validation close to the existing D-Pace-specific arguments so misconfigured runs fail immediately with a clear message.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.
Inline comments:
In `@src/speculators/models/dflash/metrics.py`:
- Around line 55-64: Replace the inline lambda assigned to decay_fn in the
dflash metrics logic with a named nested function to satisfy Ruff E731 and
improve readability. In the per_position_loss_weight == "dpace" branch, define a
def that returns decay_mult instead of using lambda, and keep the existing
partial(dflash_loss_decay, gamma=gamma) path unchanged. Use a distinct,
descriptive function name near ce_loss, dpace_loss_weight, and decay_fn so the
intent is clear and no parameter name conflicts with the outer pos_idx.
---
Nitpick comments:
In `@scripts/train.py`:
- Around line 964-976: Add parse-time validation for the D-PACE smoothing
argument so invalid values are rejected early. In the argument setup around
parser.add_argument for --dpace-alpha, enforce that the value is within the
valid [0, 1] range and disallow degenerate settings such as 0, either via an
argparse type/validator or a small reusable check used by dpace_loss_weight.
Keep the validation close to the existing D-Pace-specific arguments so
misconfigured runs fail immediately with a clear message.
In `@src/speculators/models/metrics.py`:
- Around line 281-320: Validate the smoothing parameter in dpace_loss_weight
before computing the prefix product: add an explicit check that alpha is within
the supported range, rejecting out-of-range values and the degenerate zero case
with a clear failure. Keep the existing weighting logic unchanged, but ensure
the function fails fast for invalid alpha so the numerical stability of the
D-PACE weighting is preserved.
- Around line 300-302: The reshape logic in the metrics path assumes q.shape[1]
is exactly divisible by block_size, so add a defensive validation before
computing num_anchors in the relevant reshape block. Update the code around the
q.reshape and loss_mask.reshape operations to assert or explicitly check
divisibility and raise a clear error message if the sequence length is not
compatible with block_size, using the existing compute_metrics / metrics reshape
flow as the location to safeguard.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d76c259c-9738-4941-87eb-0fba2af483e2
📒 Files selected for processing (4)
scripts/train.pysrc/speculators/models/dflash/core.pysrc/speculators/models/dflash/metrics.pysrc/speculators/models/metrics.py
|
The quality checks have failed. Please run |
Signed-off-by: Weifan Jiang <weijiang@redhat.com>
Signed-off-by: Weifan Jiang <weijiang@redhat.com>
Signed-off-by: Weifan Jiang <weijiang@redhat.com>
fynnsu
left a comment
There was a problem hiding this comment.
Hi @weifanjiang thanks for the pr! I think we need to change how we're piping some things through speculators so that it fits within the existing abstractions.
Signed-off-by: Weifan Jiang <weijiang@redhat.com>
Signed-off-by: Weifan Jiang <weijiang@redhat.com>
…c schema Make scripts/train.py config-file-first without changing what any run does and without touching the model layer. - New src/speculators/train/config.py is the single source of truth: ~75 tunables as typed fields on cohesive pydantic-settings groups (verifier/draft/data/ generation/loss/optimizer/scheduler/trainer/logging + per-algo dflash/dspark/ peagle/mtp). The argparse CLI is GENERATED from these fields, so adding an argument is one typed field -- train.py has no per-flag add_argument. - --config run.yaml supplies any argument; precedence is CLI > YAML > coded defaults; --config is optional (no file == the old pure-CLI behaviour). - --dump-config prints the fully-resolved grouped YAML (a valid --config file); the resolved run.yaml is also saved next to each checkpoint for reproducibility. - Provenance = the argparse.SUPPRESS'd namespace (not model_fields_set, which the nested partial-update machinery pollutes). A YAML leaf counts as provided only under its owning group block; a misplaced key is reported as unrecognised and kept out of provenance so it can't mis-fire the draft-init conflict check. - ValidationError is routed through parser.error (argparse usage, exit code 2). Phase 1: the resolved config is flattened back to the vars(args)-shaped dict the five SpeculatorModel classes consume via **kwargs, so the model layer and Trainer are untouched; only main()'s TrainerConfig reads the typed groups. Pushing typed configs into the model layer is deferred to a follow-up PR. Includes the D-PACE loss knobs from #736 (--per-position-loss-weight, --dpace-alpha) as typed DFlash fields, with the "dpace requires ce loss / alpha in (0,1]" check ported to a cross-group model_validator. Equivalence is guarded by tests/unit/train/test_golden_cli_equivalence.py: the refactored flat working-dict is asserted byte-value-identical to the pre-refactor parser across a 15-invocation matrix. Expected values live in one golden_flat_dict.json (a _baseline all-defaults dict + per-invocation deltas). Removes src/speculators/utils/argparse_utils.py and its test: train.py was the only caller of explicitly_provided_dests, which the SUPPRESS'd-namespace provenance replaces. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Rahul-Tuli <rtuli@redhat.com>
…c schema Make scripts/train.py config-file-first without changing what any run does and without touching the model layer. - New src/speculators/train/config.py is the single source of truth: ~75 tunables as typed fields on cohesive pydantic-settings groups (verifier/draft/data/ generation/loss/optimizer/scheduler/trainer/logging + per-algo dflash/dspark/ peagle/mtp). The argparse CLI is GENERATED from these fields, so adding an argument is one typed field -- train.py has no per-flag add_argument. - --config run.yaml supplies any argument; precedence is CLI > YAML > coded defaults; --config is optional (no file == the old pure-CLI behaviour). - --dump-config prints the fully-resolved grouped YAML (a valid --config file); the resolved run.yaml is also saved next to each checkpoint for reproducibility. - Provenance = the argparse.SUPPRESS'd namespace (not model_fields_set, which the nested partial-update machinery pollutes). A YAML leaf counts as provided only under its owning group block; a misplaced key is reported as unrecognised and kept out of provenance so it can't mis-fire the draft-init conflict check. - ValidationError is routed through parser.error (argparse usage, exit code 2). Phase 1: the resolved config is flattened back to the vars(args)-shaped dict the five SpeculatorModel classes consume via **kwargs, so the model layer and Trainer are untouched; only main()'s TrainerConfig reads the typed groups. Pushing typed configs into the model layer is deferred to a follow-up PR. Includes the D-PACE loss knobs from #736 (--per-position-loss-weight, --dpace-alpha) as typed DFlash fields, with the "dpace requires ce loss / alpha in (0,1]" check ported to a cross-group model_validator. Equivalence is guarded by tests/unit/train/test_golden_cli_equivalence.py: the refactored flat working-dict is asserted byte-value-identical to the pre-refactor parser across a 15-invocation matrix. Expected values live in one golden_flat_dict.json (a _baseline all-defaults dict + per-invocation deltas). Removes src/speculators/utils/argparse_utils.py and its test: train.py was the only caller of explicitly_provided_dests, which the SUPPRESS'd-namespace provenance replaces. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Rahul-Tuli <rtuli@redhat.com>
…c schema Make scripts/train.py config-file-first without changing what any run does and without touching the model layer. - New src/speculators/train/config.py is the single source of truth: ~75 tunables as typed fields on cohesive pydantic-settings groups (verifier/draft/data/ generation/loss/optimizer/scheduler/trainer/logging + per-algo dflash/dspark/ peagle/mtp). The argparse CLI is GENERATED from these fields, so adding an argument is one typed field -- train.py has no per-flag add_argument. - --config run.yaml supplies any argument; precedence is CLI > YAML > coded defaults; --config is optional (no file == the old pure-CLI behaviour). - --dump-config prints the fully-resolved grouped YAML (a valid --config file); the resolved run.yaml is also saved next to each checkpoint for reproducibility. It runs the draft-init contract first, so a scaffold is never emitted for a config that would fail to load. - Provenance = the argparse.SUPPRESS'd namespace (not model_fields_set, which the nested partial-update machinery pollutes). A YAML leaf counts as provided only under its owning group block; a misplaced key is reported as unrecognised and kept out of provenance so it can't mis-fire the draft-init conflict check. - Every bad input is a clean argparse error (exit code 2, no traceback): schema ValidationError and a broken --config file (missing/unreadable/malformed/ non-mapping) alike; a missing required field names the flag to set. Phase 1: the resolved config is flattened back to the vars(args)-shaped dict the five SpeculatorModel classes consume via **kwargs, so the model layer and Trainer are untouched; only main()'s TrainerConfig reads the typed groups. Pushing typed configs into the model layer is deferred to a follow-up PR. Rebased onto current main. Absorbs the flags added since the branch point and ports them into the schema so behaviour is unchanged: - #736 D-PACE: --per-position-loss-weight, --dpace-alpha as typed DFlash fields, with the "dpace requires ce loss / alpha in (0,1]" check as a cross-group model_validator. - #651 scheduler: --scheduler-warmup-ratio, and --scheduler-type as a Literal (linear/cosine/none), threaded through the typed TrainerConfig build. - #749 default sliding-window attention: --sliding-window-indices renamed to --full-attention-indices (inverted semantics), matching upstream. The decoder-shaping flag set (used by the draft-init conflict check) is derived from the schema via a field tag, so it is not a second place to edit when a shaping knob is added. Equivalence is guarded by tests/unit/train/test_golden_cli_equivalence.py: the refactored flat working-dict is asserted byte-value-identical to the pre-refactor parser across a 17-invocation matrix (expected values in one golden_flat_dict.json -- a _baseline all-defaults dict + per-invocation deltas), and the generated CLI's option-string set is frozen against the pre-refactor parser (golden_cli_flags.json) so no flag can be silently renamed or dropped. Removes src/speculators/utils/argparse_utils.py and its test: train.py was the only caller of explicitly_provided_dests, which the SUPPRESS'd-namespace provenance replaces. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Rahul-Tuli <rtuli@redhat.com>
<!-- markdownlint-disable --> <!-- PLEASE FILL IN THE PR DESCRIPTION HERE ENSURING ALL CHECKLIST ITEMS (AT THE BOTTOM) HAVE BEEN CONSIDERED. --> ## Purpose This PR implements [D-PACE: Dynamic Position-Aware Cross-Entropy for Parallel Speculative Drafting](https://arxiv.org/abs/2605.18810). D-PACE proposes an alternative per-position loss weighting for training block-parallel draft models. Instead of applying a fixed exponential decay over block positions, D-PACE dynamically adjusts the weights as training progresses. This is based on the intuition that although earlier positions are naturally more important (i.e., an early rejection forces all subsequent draft positions to be discarded), when they are sufficiently stabilized, the training priority should shift to later positions to improve overall acceptance rate. Two arguments are added to `train.py` to support D-PACE loss: - `--per-position-loss-weight ["fixed-exp-decay", "dpace"]` (default `fixed-exp-decay`). This flag enables D-PACE loss; while missing, it defaults to the original fixed exponential decay. - `--dpace-alpha` (default `0.5`). This flag is only effective when D-PACE is enabled. It smoothes per-position confidence according to equation 7 of the paper. The paper uses 0.5 as the default value for their evaluation and justifies this choice in sensitivity analysis. Note that D-PACE requires cross-entropy as the training loss (`--loss-fn ce`), allowing per-position confidence score computation to be piggybacked on the loss computation. Applying D-PACE to other losses are not supported and will result in `ValueError`. D-PACE is currently enabled for D-Flash and D-Spark draft models. Theoretically, it is compatible with all block-parallel drafters. ## Tests **D-Flash + D-PACE evaluation.** Multiple D-Flash checkpoints are trained with different loss function configurations: - `--loss-fn kld`: KL Divergence (current default) - `--loss-fn ce`: Cross Entropy with fixed exponential decay - `--per-position-loss-weight dpace --dpace-alpha ALPHA`: D-PACE with `ALPHA` = 0.3, 0.5, 0.7. The training set is 100K random samples from Magpie+Ultrachat. The verifier model is Qwen3-8B. Each checkpoint uses 1 A100 for hidden state generation and 7 A100s for training. Training configurations are identical for all checkpoints: ``` --target-layer-ids 1 9 17 25 34 --num-layers 5 --draft-vocab-size 32000 --max-anchors 3072 --block-size 8 --sliding-window 2048 --sliding-window-indices 0 1 2 3 4 --scheduler-type cosine --lr 0.0006 --epochs 1 --noise-std 0.0 ``` Metric: since D-PACE is a pure training-time optimization and does not change draft model's forward pass complexity, its gains are captured entirely by acceptance rates. Evaluation results: Mean-Accepted-Length (MAL) on [Speculator Benchmarks](https://huggingface.co/datasets/RedHatAI/speculator_benchmarks): | Dataset | KL Divergence | CE | D-PACE $\alpha=0.3$ | D-PACE $\alpha=0.5$| D-PACE $\alpha=0.7$ | |---|---:|---:|---:|---:|---:| | HumanEval | 2.977 | 2.956 | 3.047 | **3.169** | 3.139 | | math_reasoning | 3.275 | 3.322 | **3.541** | 3.532 | 3.531 | | qa | 2.279 | 2.343 | 2.455 | **2.495** | 2.431 | | question | 2.585 | 2.567 | 2.685 | 2.680 | **2.708** | | rag | 2.302 | 2.269 | **2.393** | 2.372 | 2.336 | | summarization | 1.906 | 1.918 | 1.981 | 1.980 | **1.991** | | tool_call | 2.372 | 2.327 | **2.480** | 2.454 | 2.467 | | translation | 2.096 | 2.072 | 2.176 | 2.183 | **2.191** | | writing | 2.565 | 2.586 | 2.683 | 2.684 | **2.718** | | Mean | 2.484 | 2.485 | 2.605 | **2.617** | 2.612 | **D-Spark + D-PACE evaluation.** D-Spark checkpoints use Markov rank 256 and vanilla Markov head type. The confidence head is disabled. Remaining training configurations match above D-Flash experiments. - `--loss-fn ce`: Vanilla fixed-decay cross-entropy - `--position-loss-weight dpace --dpace-alpha 0.5`: D-PACE with $\alpha=0.5$ MALs: | Dataset | CE | D-PACE $\alpha=0.5$ | |---|---:|---:| | HumanEval | 3.145 | **3.302** | | math_reasoning | 3.457 | **3.615** | | qa | 2.352 | **2.492** | | question | 2.671 | **2.770** | | rag | 2.276 | **2.384** | | summarization | 1.916 | **1.996** | | tool_call | 2.451 | **2.538** | | translation | 2.082 | **2.202** | | writing | 2.686 | **2.776** | | Mean | 2.560 | **2.675** | D-PACE improves upon fixed-decay loss for both D-Flash and D-Spark drafters. ## Checklist I have filled in: - [x] The purpose of the PR, such as "Fix some issue (link existing issues this PR will resolve)". - [x] The test plan/results, such as providing test command and pasting the results. - [ ] (Optional) The necessary documentation update. - [x] I (a human) have written or reviewed the code in this pr to the best of my ability. --------- Signed-off-by: Weifan Jiang <weijiang@redhat.com> Co-authored-by: Weifan Jiang <weifanjiang@a100-08.nemg-001.lab.rdu2.dc.redhat.com> Co-authored-by: Weifan Jiang <weijiang@redhat.com> Signed-off-by: Roderick Wu <Roderick-Wu@a100-04.nemg-001.lab.rdu2.dc.redhat.com>
…c schema Make scripts/train.py config-file-first without changing what any run does and without touching the model layer. - New src/speculators/train/config.py is the single source of truth: ~75 tunables as typed fields on cohesive pydantic-settings groups (verifier/draft/data/ generation/loss/optimizer/scheduler/trainer/logging + per-algo dflash/dspark/ peagle/mtp). The argparse CLI is GENERATED from these fields, so adding an argument is one typed field -- train.py has no per-flag add_argument. - --config run.yaml supplies any argument; precedence is CLI > YAML > coded defaults; --config is optional (no file == the old pure-CLI behaviour). - --dump-config prints the fully-resolved grouped YAML (a valid --config file); the resolved run.yaml is also saved next to each checkpoint for reproducibility. It runs the draft-init contract first, so a scaffold is never emitted for a config that would fail to load. - Provenance = the argparse.SUPPRESS'd namespace (not model_fields_set, which the nested partial-update machinery pollutes). A YAML leaf counts as provided only under its owning group block; a misplaced key is reported as unrecognised and kept out of provenance so it can't mis-fire the draft-init conflict check. - Every bad input is a clean argparse error (exit code 2, no traceback): schema ValidationError and a broken --config file (missing/unreadable/malformed/ non-mapping) alike; a missing required field names the flag to set. Phase 1: the resolved config is flattened back to the vars(args)-shaped dict the five SpeculatorModel classes consume via **kwargs, so the model layer and Trainer are untouched; only main()'s TrainerConfig reads the typed groups. Pushing typed configs into the model layer is deferred to a follow-up PR. Rebased onto current main. Absorbs the flags added since the branch point and ports them into the schema so behaviour is unchanged: - #736 D-PACE: --per-position-loss-weight, --dpace-alpha as typed DFlash fields, with the "dpace requires ce loss / alpha in (0,1]" check as a cross-group model_validator. - #651 scheduler: --scheduler-warmup-ratio, and --scheduler-type as a Literal (linear/cosine/none), threaded through the typed TrainerConfig build. - #749 default sliding-window attention: --sliding-window-indices renamed to --full-attention-indices (inverted semantics), matching upstream. The decoder-shaping flag set (used by the draft-init conflict check) is derived from the schema via a field tag, so it is not a second place to edit when a shaping knob is added. Equivalence is guarded by tests/unit/train/test_golden_cli_equivalence.py: the refactored flat working-dict is asserted byte-value-identical to the pre-refactor parser across a 17-invocation matrix (expected values in one golden_flat_dict.json -- a _baseline all-defaults dict + per-invocation deltas), and the generated CLI's option-string set is frozen against the pre-refactor parser (golden_cli_flags.json) so no flag can be silently renamed or dropped. Removes src/speculators/utils/argparse_utils.py and its test: train.py was the only caller of explicitly_provided_dests, which the SUPPRESS'd-namespace provenance replaces. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Rahul-Tuli <rtuli@redhat.com>
Purpose
This PR implements D-PACE: Dynamic Position-Aware Cross-Entropy for Parallel Speculative Drafting. D-PACE proposes an alternative per-position loss weighting for training block-parallel draft models. Instead of applying a fixed exponential decay over block positions, D-PACE dynamically adjusts the weights as training progresses. This is based on the intuition that although earlier positions are naturally more important (i.e., an early rejection forces all subsequent draft positions to be discarded), when they are sufficiently stabilized, the training priority should shift to later positions to improve overall acceptance rate.
Two arguments are added to
train.pyto support D-PACE loss:--per-position-loss-weight ["fixed-exp-decay", "dpace"](defaultfixed-exp-decay). This flag enables D-PACE loss; while missing, it defaults to the original fixed exponential decay.--dpace-alpha(default0.5). This flag is only effective when D-PACE is enabled. It smoothes per-position confidence according to equation 7 of the paper. The paper uses 0.5 as the default value for their evaluation and justifies this choice in sensitivity analysis.Note that D-PACE requires cross-entropy as the training loss (
--loss-fn ce), allowing per-position confidence score computation to be piggybacked on the loss computation. Applying D-PACE to other losses is not supported and will result inValueError.D-PACE is currently enabled for D-Flash and D-Spark draft models. Theoretically, it is compatible with all block-parallel drafters.
Tests
D-Flash + D-PACE evaluation. Multiple D-Flash checkpoints are trained with different loss function configurations:
--loss-fn kld: KL Divergence (current default)--loss-fn ce: Cross Entropy with fixed exponential decay--per-position-loss-weight dpace --dpace-alpha ALPHA: D-PACE withALPHA= 0.3, 0.5, 0.7.The training set is 100K random samples from Magpie+Ultrachat. The verifier model is Qwen3-8B. Each checkpoint uses 1 A100 for hidden state generation and 7 A100s for training. Training configurations are identical for all checkpoints:
Metric: since D-PACE is a pure training-time optimization and does not change draft model's forward pass complexity, its gains are captured entirely by acceptance rates.
Evaluation results: Mean-Accepted-Length (MAL) on Speculator Benchmarks:
D-Spark + D-PACE evaluation. D-Spark checkpoints use Markov rank 256 and vanilla Markov head type. The confidence head is disabled. Remaining training configurations match above D-Flash experiments.
--loss-fn ce: Vanilla fixed-decay cross-entropy--position-loss-weight dpace --dpace-alpha 0.5: D-PACE withMALs:
D-PACE improves upon fixed-decay loss for both D-Flash and D-Spark drafters.
Checklist
I have filled in: