fix(eagle3,peagle): default d2t to the offset identity instead of arange - #67
Conversation
d2t holds offsets, not absolute ids: the target id of draft id i is i + d2t[i]. That is what preprocessing.process_token_dict_to_mappings emits (used_tokens[i] - i) and what the serving engines apply (vLLM's EAGLE-3 draft computes targets = arange(draft_vocab_size) + draft_id_to_target_id, and initializes its own parameter to zeros). Both draft models default the buffer to torch.arange(draft_vocab_size), which under that convention resolves to target_id = 2 * draft_id rather than the intended identity. Nothing internal reads d2t (the backend only validates its shape), but _get_full_export_state_dict deliberately keeps vocab-mapping buffers, so every exported checkpoint carries the wrong values. Neither load_vocab_mapping nor generate_vocab_mapping_file has a caller in-tree, so a freshly trained drafter always exports the constructor default. Default to zeros and pin the convention with round-trip tests against the repo's own mapping generator. Checkpoints already exported with the arange buffer keep it on reload; they need their d2t rewritten (all zeros for a full-vocabulary draft). Signed-off-by: khazic <khazzz1c@gmail.com>
|
The
#68 and #70 are the clearest: neither touches the dspark or the dflash code path, yet those are the jobs that failed. Within a single run the three example jobs are serialized (on #63 they ran 11:25:05 to 11:42:21, 11:42:46 to 12:01:01, 12:01:27 to 12:19:55), so the contention comes from runs of different PRs overlapping. The failures cluster in the window where three PRs had example jobs in flight at once, and every job that started after that queue drained passed. The failed jobs also died in about 12 minutes against roughly 17 for a successful one, consistent with dying at engine init rather than during real work.
Could a maintainer re-run the failed job? I do not have the permission to ( |
Problem
d2tholds offsets, not absolute ids: the target id of draft idiisi + d2t[i].That is what this repo's own mapping generator emits:
and it is what the serving engine applies:
vLLM even initializes its own copy of the buffer to
torch.zeros, which is the identity under this convention.Both draft models here default it to
torch.arangeinstead:Under the offset convention that resolves draft id
ito target id2 * i. For a full-vocabulary draft the top of the range lands outside the target vocabulary entirely, so the engine'slogits_new[:, targets] = logitsindexes out of bounds.Scope
Nothing inside this repo reads
d2t; the backend only validates its shape, and the loss usest2d. The exposure is entirely on the export path:_get_trainable_state_dictdrops non-floating tensors, sod2tnever goes over the wire and the engine keeps its own zero-initialized copy._get_full_export_state_dictdeliberately keeps "persistent buffers such as vocab mappings", so every checkpoint written to disk carries the value.Neither
load_vocab_mappingnorgenerate_vocab_mapping_filehas a caller in-tree, so a freshly trained drafter always exports the constructor default rather than a frequency-derived mapping.Fix
Default the buffer to
torch.zerosin both draft models and state the convention in a comment next to it.Checkpoints already exported with the
arangebuffer keep it on reload, sincefrom_pretrainedoverwrites the default. They need theird2trewritten (all zeros for a full-vocabulary draft).Validation
Ran a before/after repro on both
mainand this branch. It shows what the repo's own generator emits, then applies vLLM's exact formula to the default buffer of both drafts.Before/after repro output
max target id = 62againstvocab_size = 32is the out-of-bounds write described above.Tests
New file
tests/integration/test_draft_vocab_mapping_contract.py, five cases:test_default_d2t_is_the_offset_identityfor both drafts, asserting the engine formula recoversarange.test_default_d2t_stays_inside_the_target_vocabularyfor both drafts with a reduced draft vocabulary.test_generated_mapping_round_trips_through_the_offset_conventionpins the convention againstprocess_token_dict_to_mappings, so the two producers cannot drift apart again.Full CPU suite (
tests/integration tests/compat tests/config tests/examples) run on both sides:Test suite before/after
The same 9 tests fail on
mainand on this branch. They need optional dependencies (VeOmni, the NPU vLLM stack) that are absent in this environment, so they are pre-existing and unrelated. The+5on this branch are the new tests above.