加载中…
加载中…
XHToken/Spark-X2.6-4BA one-layer EAGLE-3 draft head (218M parameters) for XHToken/Spark-X2.6-4B. It proposes tokens that the 4B model verifies, so speculative decoding runs faster while the output stays exactly what the target model would have produced.
There was no public draft head for Spark, so this is a from-scratch one. It is provided so you can combine it with whatever target GGUF you already use.
This is v8, the current release: trained on a mixed code + prose + conversation corpus with verifier-argmax distillation and the EAGLE-3 training-time test.
llama.cpp's EAGLE-3 draft consumes the input to three target decoder layers,
exposed per architecture as res->t_layer_inp[il]. Spark's arch (spark2_5) does
not set it, and the runtime aborts with:
GGML_ASSERT(t_layer_inp[il] != nullptr)
Apply llama.cpp-spark2_5-layer-inputs.patch (included here) to
src/models/spark2-5.cpp and rebuild:
for (int il = 0; il < n_layer; ++il) {
+ res->t_layer_inp[il] = inpL;
+
ggml_tensor * inpSA = inpL;
Tested against llama.cpp 0.7.0-dev (commit f5027fb).
llama-server \
--model Spark-X2.6-4B-Q8_0.gguf \
--draft spark-eagle3-v8.gguf --spec-type draft-eagle3 \
--spec-draft-n-max 3 \
-ngl 99 --ngld 99
Where the 4B target does not fit in VRAM, keep it on the CPU and put only the head on the GPU — the head costs ~0.5 GiB including a 4k context:
llama-server \
--model Spark-X2.6-4B-Q8_0.gguf \
--draft spark-eagle3-v8.gguf --spec-type draft-eagle3 \
--spec-draft-n-max 3 \
--ngl 0 --ngld 99 --threads 4 -c 4096 -np 1
--spec-draft-n-max: 3 for prose, 2 never, 1 for code. Prose depth-1
acceptance is 0.75, so a pass returning ~3.2 tokens beats verifying four; code
depth-1 is ~0.15, so only n=1 helps there.
Avoid 2 when the target is on the CPU. It is not a middle setting: in
llama.cpp 0.7.0-dev a pass at n_max 2 costs ~350 ms against ~182 ms at 1 and
~217 ms at 3, so it lands below both (prose 1.28x vs 1.84x/2.57x). The same value
behaves normally with the target in VRAM, so this is a CPU-side code-path artifact.
| Architecture | EAGLE-3, 1 decoder layer |
| Parameters | 218M (32k draft vocab dominates) |
| Base model | XHToken/Spark-X2.6-4B (Apache-2.0) |
| Target layers fused | 2, 18, 33 |
| Hidden size | 2560 |
| Attention | 16 heads / 4 KV, head_dim 256, RoPE theta 1e6 |
| Draft vocab | 32,000 with d2t / t2d maps |
| Weight file | 422 MB bf16 GGUF, 834 MB fp32 safetensors |
Note for anyone loading the safetensors by hand: d2t is stored as deltas
(convert_hf_to_gguf.py adds arange to recover absolute ids). Exporting absolute
ids fails with EAGLE-3 d2t target ids out of range for target vocab size 131072.
| Corpus | 21.96M tokens: 10M Python repo code (codeparrot-clean), 3M instruction→code (Magicoder), 2M prose (WikiText-103), 7M conversation (UltraChat-200k, 32% of segments) |
| Steps | 5,362 (one epoch, batch 4 × 1024) |
| Objective | CE against the target's own argmax, plus 0.5 · MSE on the fed-back prenorm state, plus a depth-1 CE from the training-time test |
| Hardware | single RTX 3090 at 300 W, target frozen in bf16 |
Three choices carried the gain, each measured against the previous version:
Label on the verifier, not the corpus. Acceptance is
draft argmax == target argmax, so wherever the corpus token is not what Spark
would emit, ordinary cross-entropy teaches a position the draft can never win.
Relabelling lifted prose acceptance 0.49 → 0.81 at identical cost.
Training-time test. The head is also fed its own prenorm output back as the next
step's g, with a second loss there, so it learns to recover from its own drafts.
This raised prose depth-1 acceptance 0.50 → 0.70 and is what makes n_max > 1 pay.
Conversational data. Instruct prose rendered through Spark's own template lifted chat-format acceptance 0.299 → 0.325 (0.377 with thinking disabled). Templated code in the same quantity moved it 0.001, so the win is the register, not the markers.
Depth-0 acceptance, greedy, 96-token generations, target in VRAM:
| prompt form | p0 |
|---|---|
| WikiText prose (in-domain) | 0.920 |
| 12 mixed prompts, raw text | 0.464 |
| 12 mixed prompts, chat template (thinking on) | 0.325 |
| 3 Python code prompts | 0.415 |
Throughput, single RTX 3090 + i7-6700K (n=3 unless noted):
| placement | prose | mixed | chat format | code |
|---|---|---|---|---|
| Q8 target on CPU (5.79 tok/s), head on GPU | 2.57x | 1.48x (n=1) | 1.24x (n=1) | – |
| Q8 target in VRAM (~140 tok/s), head on GPU | 2.04x | 1.18x (n=1) | 1.06x (n=1) | 1.08x (n=1) |
At n=1 the CPU placement gives 1.88x on prose and 1.48x on mixed prompts. The gain scales with how slow the verifier is: with the target in VRAM a token costs ~7 ms, so the draft's fixed per-pass cost stops being negligible.
n_max 1 is useful. Code
p0 is 0.415 here against 0.437 for the previous release; that difference is
within the ±0.03 noise of a 3-prompt evaluation and coincided with cutting the
code share from 12M to 10M tokens, so treat it as unresolved rather than real.convert_hf_to_gguf.py only detects
EAGLE-3 at num_hidden_layers == 1, and llama.cpp's eagle3 graph builds one
layer, so a deeper head needs patches on both sides.| file | purpose |
|---|---|
spark-eagle3-v8.gguf |
current release, --spec-type draft-eagle3 |
v8/model.safetensors, v8/config.json |
v8 fp32 HF-format checkpoint |
v8/train_log.json |
v8 training curve |
llama.cpp-spark2_5-layer-inputs.patch |
required for the draft to load at all |
spark-eagle3-v6.gguf, v6/… |
previous release, kept for existing links |
spark-eagle3-v5.gguf, root model.safetensors/config.json |
first release, kept |
To rebuild the GGUF from the safetensors (needs a local copy of the Spark config and tokenizer, which the converter reads but does not instantiate):
python3 convert_hf_to_gguf.py v8 --target-model-dir /path/to/Spark-X2.6-4B \
--outfile spark-eagle3-v8.gguf --outtype bf16
Apache-2.0, following the base model. Training data: codeparrot-clean (permissively licensed GitHub Python), Magicoder OSS-Instruct (MIT), UltraChat-200k (MIT), WikiText-103 (CC BY-SA 3.0) — the last is share-alike, which is worth knowing if you redistribute this commercially.
If this is useful, cite the EAGLE-3 paper for the architecture (Li et al., 2025) and link back here for the weights.