BUILD LOG · 2026-10-30
Spark-X2.6-4B EAGLE-3 微调
以目标模型自身生成的中英文文本为训练数据,微调 Spark-X2.6-4B 的 EAGLE-3 草稿头。
ON THIS PAGE
本项目 spark-eagle3-forge 为 Spark-X2.6-4B 重新微调 EAGLE-3 草稿头,让推测解码在真实工作负载(中英翻译、摘要、资料整理、聊天)上能带来正向收益。
公开的 v8 草稿头用英文与代码文本训练,它的 32K 草稿词表只覆盖约一半 ASCII、1.1% 的 CJK;经过 S2a → S2b → S3 三个阶段,同一套 48 题 NLP 矩阵上的平均接受率从 0.344 升到 0.495,48K 双语词表覆盖率 0.979–0.995,实测解码加速 1.15–1.35 倍。与此同时,本项目也为小显存(12 GB)设备本地微调 EAGLE-3 草稿头提供一种切实可行的方案。
当前发布的是 S3(48K,Q8_0):spark-eagle3-s3-48k-q8.gguf(268 MiB),48K d2t 已嵌在 GGUF 内;同目录另有 bf16 版本供 A/B 或重新量化。S2b 与 S2a 保留在模型仓库的 legacy/ 下。
结果简述
| 工作负载 | S2a (32K) | S2b (48K) | S3 (48K) |
|---|---|---|---|
| 对话 | 0.276 | 0.304 | 0.380 |
| 中 → 英翻译 | 0.449 | 0.601 | 0.638 |
| 英 → 中翻译 | 0.325 | 0.480 | 0.530 |
| 中文摘要(中 → 中) | 0.333 | 0.469 | 0.479 |
| 英文摘要(英 → 中) | 0.277 | 0.424 | 0.453 |
| 资料整理 | 0.421 | 0.516 | 0.532 |
| 长上下文 | 0.298 | 0.395 | 0.419 |
| 全部 | 0.344 | 0.460 | 0.495 |
上表为三个 EAGLE-3 草稿模型 S2a / S2b / S3 在各工作负载上的推测接受率 p0(首个草稿词元被目标模型接受的比例)。
协议:48 题 NLP 矩阵、7 个工作负载、目标温度 1.0、草稿头贪心解码、经
llama-server以--spec-draft-n-max 1测量。S2a 使用 32K 词表,S2b 与 S3 共用同一个 48K 词表。S3 沿用 S2b 的词表,所以 0.460 → 0.495 的提升全部来自模型头质量,而不是覆盖率。
项目特点
基于量化 GGUF 训练
训练特征直接从 llama.cpp 实际运行的 Q6_K GGUF 模型中提取,而非传统上使用基础模型的全精度检查点。训练和部署使用的是同一份权重:
- 好处是无需下载 8.2 GB 的 bf16 权重,降低了收集特征时模型推理的显存占用;训练与推理使用同一份权重,或许还能提升 EAGLE-3 草稿头在量化后模型上的接受率(未进行对照实验);
- 代价是可能会牺牲 EAGLE-3 草稿头在其他量化精度或微调模型上的接受率。
本项目为 llama.cpp 打了 layer-input 补丁。这并非魔改框架:EAGLE-3 训练必须拿到推理时的中间层特征,任何适配 EAGLE-3 的框架都需要提供这类接口;llama.cpp 上游已有该接口,只是 Spark 架构还没接上,补丁补上了这处缺失。随后用 C++ shim 把特征读取出来用于训练,没有采用 vLLM 或官方训练框架。
内存缓冲的半离线训练
在线训练在小显存(12 GB)的显卡上难以实现,而离线训练又需要把特征写到硬盘上:三千余条提示在 S3 上生成了约 890 万词元,每词元特征 15,368 字节,光特征就有约 137 GB,规模再往上很容易到数百 GB。为了减少对硬盘的占用与「伤害」,本项目将特征缓存于内存中,每段约 194 万词元(约 30 GB 特征)训练若干步后即保存检查点。
因此在本项目中,目标模型与处于训练状态的草稿头不会同时驻留显存,流程如下:
- 加载目标模型,提取特征并缓存到内存;
- 卸载目标模型、加载草稿头开始训练;
- 训练完成后保存检查点,重新加载目标模型进入下一轮。
提取与训练的耗时远长于模型切换,这部分开销可以忽略。
通过这样的设计,EAGLE-3 草稿头得以在小显存(12 GB)显卡上完成微调,而无需大显存或大容量硬盘。
训练数据由目标模型生成
训练文本不是公开语料本身,而是让目标模型就公开文档生成的中英回答(摘要、翻译、资料整理、对话、长文档整合)。公开语料(中文与英文维基、COIG-CQIA)只作提示,模型自己的输出才是训练目标:草稿头要模仿的正是它在推理时会产生的那段文字。
S3 把这一思路扩到两个通道。S2b 的语料几乎全是思考段——3,520 笔回答里只有 128 笔在预算内结束思考并带上一小段正文,只占响应文本的 0.46%——所以答案段基本没被训练。S3 用关闭思考的同一批任务再生成一遍答案语料,最终 777 万训练行取自目标模型自己生成的 889 万词元(486 万思考段 + 403 万答案段)。
限制
- 32k 上下文仍弱(0.250,S2b 是 0.219)。A/B 只量到 13–14k,那里解码更快;32k 本身没有做 A/B,而且收益随上下文增长而缩小。
- 长上下文的提示与训练文本重叠。 矩阵的长上下文题用本项目的
docs/notes.md当上下文,而long任务的英文那一半也由同一个文件生成,所以那一行偏乐观;独立证据是上下文探针(16k 提升 0.094),它的文本不属于任何一份生成训练文档,续写是合成的,只能用来看方向。 - 离线探针不等于运行时。 它只重放生成的列,而 llama.cpp 的草稿解码器会处理每个已验证的词元(含整个提示),这个缺口随提示变长而变大。S2b 与 S3 在这类探针上可比(两边都少算),运行时数字看线上矩阵。
- 只有一个训练随机种子。 整体提升远大于逐题 ±0.03 的噪声,但没有跑第二个种子。
- 128 词元响应下达不到 1.30 倍(短上下文平均 1.19 倍、全部 48 题 1.11 倍)。解码本身是 1.15–1.35 倍,而预填充是每请求一次的固定成本,会随响应长度摊薄;很长的提示配很短的回复是唯一可能变慢的情形。
- 词表部分在样本内选择。 16K 英文/ASCII id 的排序部分来自同一组 48 道评测题录制的步骤(权重 50),因此 0.979–0.995 的覆盖率偏乐观;模型头没有在这些指令上训练过,但长上下文的上下文有重叠,见上一条。
- 只测了单序列(
--parallel 1);批量推理未调优。 - 只适用于 Spark:
spark2_5架构的 EAGLE-3 模型头,读取第 2/18/33 层。
相关链接
训练代码:GitHub 仓库
微调后 EAGLE-3 模型:Hugging Face 仓库
许可证
代码:BSD 3-Clause,见 LICENSE —— Copyright (c) 2026, 條風。
训练所得的草稿头:Apache-2.0。
详细信息请查阅 GitHub 仓库 中的具体说明。
声明
-
基础模型为 SC117/Spark-X2.6-4B-abliterated-FIT-GGUF 的 Q6_K 量化版本,基于 XHToken/Spark-X2.6-4B-Base。
-
草稿头由 VitreousCut/Spark-X2.6-4B-EAGLE3 v8 检查点热启动重训衍生(v8 → 32K S2a 为重训,32K S2a → 48K S2b 为继续微调,48K S2b → 48K S3 为同词表继续微调)。
-
训练语料:
- S2a(32K 草稿头):m-a-p/COIG-CQIA、YeungNLP/firefly-train-1.1M、wikimedia/wikipedia 的中文分片;
- S2b(48K 草稿头):训练文本由目标模型自身生成(摘要、翻译、资料整理、对话、长文档整合),提示取自 wikimedia/wikipedia 的中文与英文分片以及 COIG-CQIA;
- S3(48K 草稿头):在 S2b 的基础上追加答案通道语料,由目标模型以关闭思考的方式对同一批任务生成。
COIG-CQIA 与 firefly 的卡片没有声明授权,商用再分发衍生权重前请自行评估(详见
THIRD-PARTY-NOTICES.md)。 -
推理框架 ggml-org/llama.cpp。
-
训练框架 pytorch/pytorch 等。