File size: 8,839 Bytes
08ad55b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | # Draft Model Speculative Decoding — 执行规格 v1
**目标模型**:Qwen3-0.6B(draft)+ Qwen3-235B-A22B(verifier,当前主模型)
**平台**:Ascend 910 × 16 NPU
**预期收益**:全 prompt 类型 +50~100% TG(目标 40-50 t/s 稳定)
**工程量**:1-2 周
---
## 0 · 为什么换 draft model
| 指标 | PLD (n-gram) | Draft Model (0.6B) |
|---|---|---|
| Accept rate 均值 | 0.3-1.5 (低) | 3-5 (预期) |
| 事实/代码 prompt | 需 guard 丢弃大量 draft → 几乎失效 | 语义理解 → 稳定 accept |
| 正反馈循环风险 | 高(n-gram 放大重复) | 低(小模型有自己的分布,不会完美复制主模型重复) |
| 内存占用 | ~0 | ~1.2GB BF16 |
| 计算成本 | ~0 | 每 draft step ~10-15ms |
n-gram PLD 已到方法论天花板;accept=0.3-1.5 意味着平均每次 verify 只赚 1-2 个 token,
且 guard 在事实/代码场景丢弃几乎所有 draft。
---
## 1 · 硬件 / 资源规划
### 1.1 NPU 分配决策
**选项 A**(推荐):主模型 TP=16 不变,draft 模型**时分复用 device 0**
- 优点:无需改动主模型 sharding
- draft decode ~10ms,主模型 decode ~35ms → 串行总 45ms 仍然值得(vs pure decode 35ms)
因为每次 draft 后的 batch verify 摊销到 3-5 token → 50/5 = 10ms/token
**选项 B**:主模型 TP=15,draft 独占 device 15
- 优点:draft / 主完全并行
- 缺点:主模型所有 sharding 常量需重算 `64/15` 不整除问题严重
**选项 C**:主模型 TP=8(仅用 device 0-7),draft 独占 device 8
- 优点:配置简洁
- 缺点:主模型从 TP=16 降到 TP=8,每 rank 权重加倍,可能 OOM(Qwen3-235B BF16 约 470GB / 8 = 58GB/rank,910 单卡 64GB 勉强够)
**→ 选 A**。draft decode 可重叠的时机有限,但工程风险最低。
### 1.2 权重下载 / 存储
- HF:`Qwen/Qwen3-0.6B`(约 1.2GB BF16)
- 位置:`/run/modelscope/Qwen3-0.6B-BF16/`(与主模型同目录结构)
- tokenizer **完全相同**(共享 Qwen3 vocab.bin)—— 这是选 0.6B 的关键优势
---
## 2 · 架构设计
### 2.1 模块
```
include/
├── draft_runner.h ← 新增:TP=1 Runner 特化,device 独占
├── draft_engine.h ← 新增:Qwen3 28 层 attn+MLP forward (非 MoE)
├── draft_model_config.h ← 新增:Qwen3-0.6B hparams
src/
├── draft_runner.cpp ← 简化 runner.cpp,去 TP/HCCL
├── main_cli.cpp ← 新增 --draft-model-dir flag + spec decode loop
```
### 2.2 主循环流程(替换现有 PLD 分支)
```
while (!eos && decoded < n_predict) {
// Phase 1: draft 模型连续 decode K 步
draft_tokens = []
draft_runner.set_cursor(main_runner.past_len + 1) // 同步位置
cur = next_id
for k in 0..K-1:
d_logits = draft_runner.decode(cur) // ~10 ms (device 0 独占)
cur = argmax(d_logits)
draft_tokens.append(cur)
if is_eos(cur): break
// Phase 2: 主模型 batch verify (K+1 位置)
batch_input = [next_id] + draft_tokens
batch_logits = main_runner.decode_batch(batch_input) // ~42 ms
// Phase 3: 接受最长前缀 + 1 bonus
accept = 0
for i in 0..K-1:
if argmax(batch_logits[i]) == draft_tokens[i]:
accept++
else:
next_id = argmax(batch_logits[i]); break
if accept == K:
next_id = argmax(batch_logits[K]) // bonus
// Phase 4: rewind 两个模型的 KV cache
main_runner.rewind(K - accept)
draft_runner.rewind(K - accept + (accept == K ? 0 : 1)) // draft 多走了一步
// Phase 5: emit accepted + bonus
for tok in draft_tokens[:accept] + [next_id]:
emit(tok); hist.append(tok)
}
```
### 2.3 关键 invariant
| invariant | 保障 |
|---|---|
| draft 与 main 看同一 token 序列 | 每次 decode 前 `draft.position = main.past_len`,rewind 同步 |
| device 0 时序:draft decode 不冲突主模型 | 主模型 rank 0 在 HCCL 等待期间 device 0 空闲 → draft 用这段时间 |
| KV cache 独立 | draft 与 main 各有自己的 KV cache,大小分别按 0.6B / 235B |
### 2.4 device 0 争用分析
主模型每 decode step ~35ms,其中:
- rank 0 的 local compute ~10ms
- HCCL AllReduce 等待 ~22ms(device idle from rank 0 POV)
- final logits ~3ms
**理论上 draft decode 可在 HCCL 等待期间占用 device 0**。但实际:
- HCCL 等待期间 rank 0 的 stream 是被 HCCL 占用的(不是 idle)
- draft 需要独立的 stream 才能真正并行
- 若用 `aclrtCreateStream` 分出第二条 stream,draft 和主的 HCCL 共享 AI Core 资源 → 可能互相干扰
**保守估算**:串行执行 draft (K=4 步约 40ms) + verify (42ms) = 82ms per spec cycle,产出 3-5 token → amortized 16-27 ms/token → 37-60 t/s(对比当前 ~27 t/s 单 decode)。
---
## 3 · 实施步骤(按依赖排序)
### M1 · draft_model 独立推理(3 天)
- [ ] `draft_model_config.h`:Qwen3-0.6B hparams(28 层,16 Q head,8 KV head,hidden=1024, ...)
- [ ] `draft_engine.h`:simplified attention + MLP(SwiGLU),无 MoE
- [ ] `draft_runner.cpp`:单 device,无 HCCL,复用 RoPE cache / workspace pool
- [ ] `test_draft_standalone.cpp`:与 HF Transformers 参考比对 rel ≤ 1e-3
- [ ] `test_draft_consistency.cpp`:同 prompt 多次 argmax 确定(greedy 应稳定)
**验证点**:draft 单模型 decode ≥ 80 t/s(TP=1,0.6B 应该很快)
### M2 · 协同控制(3 天)
- [ ] `main_cli.cpp` 新增 `--draft-model-dir` flag
- [ ] 主/draft cursor 同步 wrapper
- [ ] draft.rewind() 实现(沿用 main 的 rewind_cache 模式)
- [ ] 串行版 spec decode loop(不追求重叠)
- [ ] **正确性验证**:--draft 路径与 --pld 关闭路径对相同 prompt 应产生**完全相同**的 token 序列(greedy temp=0)
- 这是黄金标准:任何偏差都是 bug
- 用 `test_spec_correctness.sh` 自动回归
**验证点**:5 个 prompt(故事/代码/事实/数学/对话)输出与 baseline 逐 token 一致
### M3 · 性能测量与调优(3 天)
- [ ] K sweep:K ∈ {2, 4, 6, 8},找 best accept × throughput
- [ ] 与 PLD+guard 对比:同 prompt 3 runs
- [ ] 文档更新:诚实对比表
**验证点**:
- 所有 prompt OK verdict(bench_pld_safe.sh 分类器不报 degraded)
- mean TG 比 base +40% 以上
### M4(可选)· 并行化 / 异步 draft(2-3 天)
- [ ] 第二条 aclrtStream 用于 draft
- [ ] 尝试 draft decode 与主 verify 的下一轮重叠
- [ ] 若测不出显著增益则砍掉,保持串行
---
## 4 · 正确性测试计划
**绝对底线**:greedy (temp=0) 下,启用 draft 的输出必须与不启用**逐 token bit-identical**。
理由:spec decoding 的正确性定义就是 "输出等价于 verifier 单独贪心解码"。
### 4.1 回归测试套件
```bash
# test_spec_correctness.sh
for prompt in "Paris" "Python function" "Once upon" "2+2" "长句测试":
out_base = run --no-pld --temperature 0
out_draft = run --draft-model-dir --temperature 0
diff out_base out_draft # must be identical
```
### 4.2 压力测试
- 长 prompt (>1K token) 稳定性
- 多轮对话下 cache rewind 正确
- EOS 在 draft 和 main 不一致时的处理
### 4.3 benchmark 诚实性
- 继续用 `bench_pld_safe.sh` + 新增 `--draft` 模式列
- OK/degraded 分组统计;degraded = fatal
---
## 5 · 风险与兜底
| 风险 | 概率 | 兜底 |
|---|---|---|
| device 0 独占争用让 draft 延迟 >15ms | 中 | 保持串行执行,不追求重叠 |
| draft accept rate 不如预期(<3) | 中 | K 调小至 2-3,仍有 +20-30% 收益 |
| rewind 导致 KV cache 污染 | 高 | 严格测试 rewind 正确性;失败就回滚到"不 rewind,允许小概率错" |
| 910 初代 0.6B 权重加载失败 | 低 | 已验证 Qwen3-235B BF16 可加载,0.6B 结构子集 |
| 与 PLD guard 机制冲突 | 低 | draft 替代 PLD,同时不开启两个路径 |
---
## 6 · 时间表(单人 1 周密集 / 2 周分散)
| 天 | 任务 | 产出 |
|---|---|---|
| D1-D3 | M1 draft_standalone | test_draft_standalone PASS |
| D4-D6 | M2 协同 + 正确性 | test_spec_correctness 5/5 PASS |
| D7-D8 | M3 性能调优 | K sweep + 对比表 |
| D9-D10 | M4 可选重叠 + 文档 | 最终报告 |
---
## 7 · 决策 checkpoint
**第 3 天末**:若 draft 单模型 decode < 60 t/s(远不如预期),暂停,重新评估 0.6B 是否太慢 / 切换到 Qwen3-1.7B / 或放弃路径。
**第 6 天末**:若 5/5 prompt 正确性测试过不了,停下查 bug,不急于测性能。
**第 8 天末**:若 mean TG 提升 < 20% vs 当前 PLD+guard,判 ROI 不足,回滚保留 PLD+guard 为主路径。
---
*此规格服务于下一轮会话,作为单人专职工作的路线图。*
|