Kimi-K3 support for speculative-decoding draft training - #2049
Conversation
Two model-specific gaps prevented drafter training on Kimi-K3 from being numerically correct. 1. Loss mask. K3 replaces the K2/K2.5 `<|im_*|>` turn markers with an XTML tag format (`<|open|> message role assistant <|sep|> ... <|close|>`), and ships only a slow tiktoken tokenizer, so `apply_chat_template` cannot emit an assistant mask. Add a `kimi_k3` loss-mask recovery that walks the XTML turns and marks the assistant content span, tracking open/close depth so the nested `think`/`response` sub-tags stay inside the span. K3 also keeps the legacy `<|im_*|>` markers for back-compat, so the existing `kimi` recovery matches a K3 tokenizer and returns an all-zero mask. Make `_kimi_detect` defer when the XTML markers are present. 2. Final norm. `_FINAL_NORM_TYPE_BY_MODEL_TYPE` is keyed on the resolved text config's `model_type`. Kimi-K3 is a VLM whose outer config reports `kimi_k3` but whose text backbone reports `kimi_linear`, so no entry matched and FakeBaseModel silently built no final norm -- the offline and streaming producers then reconstructed logits from an unnormed hidden. Add the `kimi_linear` entry. Both are additive: no behavior change for any other model_type or tokenizer. Signed-off-by: h-guo18 <67671475+h-guo18@users.noreply.github.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2049 +/- ##
==========================================
+ Coverage 66.94% 66.96% +0.02%
==========================================
Files 519 519
Lines 59401 59445 +44
==========================================
+ Hits 39767 39810 +43
- Misses 19634 19635 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Mirrors the existing MiniMax-M3 / Kimi-K2.6 DSpark streaming examples for Kimi-K3, and documents the four K3-specific settings that are otherwise silent failure modes: the XTML loss-mask recovery and the `kimi_linear` final-norm entry added in this PR, the final aux capture id (93 == num_hidden_layers, which needs vllm#50815 on K3's block-residual backbone), and the explicit draft dims. The draft dims are documented as draft-side choices rather than mirrors of the backbone: K3's text config carries no rope_theta, and its FFN is 33792 dense / 3072 MoE, neither of which the draft copies. Signed-off-by: h-guo18 <67671475+h-guo18@users.noreply.github.com>
What does this PR do?
Type of change: Bug fix
Two model-specific gaps prevented speculative-decoding draft training on Kimi-K3 from being numerically correct. Both are additive — no behavior change for any other
model_typeor tokenizer.1. Loss mask — K3's chat format is not recognised
K3 replaces the K2/K2.5
<|im_*|>turn markers with an XTML tag format:where tag names and attribute values (including the role) are ordinary text tokens; only
<|open|>/<|close|>/<|sep|>/<|end_of_msg|>are special. Like the other Kimi models it ships only a slow tiktoken tokenizer, soapply_chat_templatecannot emit an assistant mask and theloss_maskrecovery registry is the only route.This adds a
kimi_k3recovery that walks the XTML turns and marks the assistant content span. It tracks<|open|>/<|close|>depth rather than scanning for the next marker, so the nestedthink/responsesub-tags the model generates stay inside the masked span.There is a second-order problem: K3 keeps the legacy
<|im_*|>markers for back-compat, so the existingkimirecovery also matches a K3 tokenizer — and sinceget_loss_mask_recoveryreturns the first match, K3 would silently get an all-zero mask (no<|im_middle|>ever appears in a K3-rendered sample)._kimi_detectnow defers when the XTML markers are present.2. Final norm — K3 is a VLM and the table is keyed on the text config
_FINAL_NORM_TYPE_BY_MODEL_TYPEis keyed on the resolved text config'smodel_type. Kimi-K3's outer config reportskimi_k3, but its text backbone reportskimi_linear, so nothing matched,_select_final_norm_typereturnedNone, andFakeBaseModelbuilt no final norm at all. The offline/streaming producers then reconstructed target logits from an un-normed hidden state.This adds the
kimi_linearentry. Note the entry deliberately is notkimi_k3— a test pins that, since keying on the outer VLM model_type is the exact mistake this fixes.3. Example — Kimi-K3 DSpark streaming training
Adds
tools/launcher/examples/moonshotai/Kimi-K3/hf_streaming_dspark_multi_node.yaml, mirroring the existing MiniMax-M3 and Kimi-K2.6 DSpark streaming examples. Its header documents the K3 settings that are otherwise silent failure modes — the two fixes above, the final aux capture id (93 == num_hidden_layers, which needs vllm#50815 on K3's block-residual backbone), and the explicit draft dims.One deliberate difference from the sibling examples: the draft dims are described as draft-side choices, not mirrors of the backbone. K3's text config carries no
rope_thetaat all, and its FFN is 33792 dense / 3072 MoE — the draft copies neither, so claiming otherwise (as would be natural by analogy with the M3/K2.6 examples) would be wrong.Usage
No API change. Both paths are selected automatically from the tokenizer / base config:
Testing
Extends the existing unit tests;
27 passedlocally:New cases:
test_k3_recovery_is_registered— a K3 tokenizer selectskimi_k3.test_k3_tokenizer_does_not_match_the_k2_recovery— a tokenizer carrying both marker sets (i.e. real K3) still routes tokimi_k3. This is the regression that produced empty masks.test_k3_mask_marks_only_assistant_content— user turns stay unmasked.test_k3_mask_includes_nested_tags— nestedthinksub-tag stays inside the assistant span.test_k3_mask_accepts_list_input— parity with the existing K2 test.test_select_final_norm_type— addskimi_linear -> "rmsnorm"andkimi_k3 -> None.Beyond unit tests, both changes are exercised by a real Kimi-K3 DSpark drafter training run (streaming hidden-state extraction from vLLM, drafter exported); the loss mask is what selects the trained tokens and the final norm is what makes the reconstructed teacher logits correct.
Before your PR is "Ready for review"
_kimi_detectreturningFalsefor tokenizers that define K3's XTML markers, which no pre-K3 Kimi tokenizer does.CONTRIBUTING.md: ✅ — no new dependencies, no copied code.Additional Information
Marked as draft for one reason: the commit carries the DCO sign-off but is not yet cryptographically signed (
git commit -s -S). I will force-push a signed version before marking ready.