← 返回

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.2760.3040.380
中 → 英翻译0.4490.6010.638
英 → 中翻译0.3250.4800.530
中文摘要(中 → 中)0.3330.4690.479
英文摘要(英 → 中)0.2770.4240.453
资料整理0.4210.5160.532
长上下文0.2980.3950.419
全部0.3440.4600.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 仓库 中的具体说明。

声明

▲