Skip to content

fix(spec-decode): load draft token embedding from checkpoint under PP - #50

Open
Karl0007 wants to merge 1 commit into
wtdcode:masterfrom
Karl0007:pr/mtp-embed-from-checkpoint
Open

fix(spec-decode): load draft token embedding from checkpoint under PP#50
Karl0007 wants to merge 1 commit into
wtdcode:masterfrom
Karl0007:pr/mtp-embed-from-checkpoint

Conversation

@Karl0007

Copy link
Copy Markdown

Problem

Under pipeline parallelism the spec-decode drafter runs on the last PP rank while the target's embed_tokens lives on the first rank — so there is nothing local to alias into the draft. Compounding this, MTP's load_weights() filters out every non-spec-layer key (including the top-level embed_tokens), so the draft's own embedding stays uninitialised GPU memory. The draft then emits near-constant garbage tokens: acceptance ≈ 1, and MTP+PP silently degrades to pure target decode (or worse).

Current code skips embedding sharing wholesale whenever pp world_size != 1, so this configuration has no correct path.

Fix

  • Share the target embedding when it is actually materialised on this rank. A _has_real_weight guard prevents aliasing a PPMissingLayer (which has no .weight and would silently become a no-op layer).
  • Otherwise (PP, drafter on last rank): populate the draft's own embedding by reading the single tensor straight from the checkpoint via model.safetensors.index.json (candidate keys cover GLM5-Next's language-model prefix). No cross-rank collective needed; one-time load cost.

Verified on 4× sm_80 64 GB (PCIe, no P2P), GLM-5.3-Flash AWQ W4A16, PP4 14,12,12,7 + --speculative-config {"method":"mtp","num_speculative_tokens":5}:

before after
draft acceptance ~1 token mean length 3.3–3.8
acceptance rate ≈0% 46–56%
correctness battery (temp 0) 9/9

Same shape also validated with DSpark (PP4 + DSpark5: acceptance length 3.11, 42.3%), so the fix generalises beyond EAGLE-style MTP.

Under pipeline parallelism the drafter runs on the LAST rank while the
target's embed_tokens lives on the FIRST, so there is nothing local to
alias. MTP's load_weights() filters out every non-spec-layer key, so the
draft's own embed_tokens stayed uninitialised memory and the draft emitted
constant garbage tokens (acceptance ~= 1).

Previously sharing was skipped wholesale whenever PP world size != 1.
Now: share when the target embedding is materialised on this rank
(_has_real_weight guards against aliasing a PPMissingLayer, which would
silently produce a no-op layer), otherwise populate the draft's own
embedding by reading the one tensor off the checkpoint via
model.safetensors.index.json - no cross-rank collective needed.

Verified on 4x sm80 64GB (GLM-5.3-Flash AWQ W4A16, PP4 14,12,12,7 +
MTP num_speculative_tokens=5): draft acceptance went from ~1 to 46-56%
(mean acceptance length 3.3-3.8), correctness battery 9/9.

Signed-off-by: kk <kk@cmp170hx>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant