# 真实双模型 DIVERSED 演示说明

这个目录由早期的 `T3/run_real_demo/` 移动并整理得到，不是 `T3` 正式考试评测的一部分，而是一个“真实加载 `Qwen3-8B + Qwen3-0.6B`”的演示实验。

它的定位是“纯 Python 机制复现 + 小型 Demo”，不是论文仓库那种做过底层内核优化的正式实现，因此这里的 benchmark 更适合拿来验证方法趋势，而不能直接当成“已经真正实现了生产级加速”。

当前这套演示也已经统一使用 Qwen3 的 `messages + apply_chat_template`，并强制要求走 Qwen 官方 chat template；如果 tokenizer 不支持该模板，脚本会直接报错，而不是退回到手写 prompt 拼接。

## 目录结构

- `run_real_qwen_diversed.py`
  真实实验主脚本，支持 `train / generate / benchmark`
- `run_qwen_diversed_benchmark.sh`
  一键启动脚本
- `diversed_train_small.jsonl`
  用于训练 dynamic ensemble head 的小样本
- `diversed_benchmark_small.jsonl`
  用于比较 `sd / diversed` 的小基准集

## 这套演示在做什么

目标不是复现论文里的完整训练流水线，而是回答一个更直接的问题：

1. `Qwen3-0.6B + Qwen3-8B` 的普通 speculative decoding 表现如何
2. 在同一对模型上，加入一个训练后的 dynamic ensemble head，`DIVERSED` 是否还能进一步改善时延与吞吐，同时尽量维持输出质量

这里的“改善”应理解为：在同一个纯 Python Demo 里，`diversed` 相比普通 `sd` 的相对变化，而不是对论文最终加速结论的严格复现。

## 为什么只有 DIVERSED 需要先训练

这套演示里：

1. `sd`
   直接使用 `Qwen3-0.6B` 做 draft、`Qwen3-8B` 做 target 的普通 speculative decoding，不需要训练
2. `diversed`
   需要一个额外的 `ensemble head` 来根据当前上下文动态输出论文风格的 `w_t`，因此必须先训练这个小头

这里训练的不是 `Qwen3-8B` 或 `Qwen3-0.6B` 本体，而只是一个很小的线性层。基础模型在训练和推理中都保持冻结。

## 数据格式

`diversed_train_small.jsonl` 和 `diversed_benchmark_small.jsonl` 都是 JSONL，每行格式一致：

```json
{"prompt": "...多句运行记录...", "reference": "...一句正式摘要..."}
```

这里故意把任务定义成：

1. 输入是 3 到 4 句运行记录
2. 输出是一句明显更短的正式摘要
3. 允许改写语气，但必须保留关键事件
4. `reference` 只保留摘要正文，不带 `摘要：`、`值班通报：`、`正式通知：` 这类标签；如果需要约束格式，应写在 prompt 中
5. 当前小样本已经统一使用同一条 user prompt：`请将下面记录压缩成一句正式中文摘要，只输出正文，不要标签或解释。`

也就是说，这里演示的是“真正压缩信息”的摘要任务，而不是简单换个正式说法。

## 训练流程

执行 `train` 时，脚本会：

1. 加载 `Qwen3-8B` 和 `Qwen3-0.6B`
2. 初始化或读取现有的 `ensemble head`
3. 对每条训练样本，先用当前 `diversed` 头真实生成一句摘要
4. 用 `reference` 计算奖励，当前默认是 `ROUGE-L`
5. 用简化版 REINFORCE 风格目标更新 `ensemble head`
6. 同时加入 acceptance regularization 和 entropy regularization

保存产物在：

```text
T3_real_demo/T3_artifacts/diversed_qwen_head/
```

其中最重要的是：

- `ensemble_head.bin`
- `ensemble_config.json`
- `training_history.json`

## Benchmark 在比什么

启动脚本会默认比较两种方法：

1. `sd`
2. `diversed`

如果设置 `INCLUDE_STATIC=1`，还会加上：

3. `static`

benchmark 主流程会先把每条样本的中间输出保存下来，再基于参考答案计算奖励指标并汇总报告。

输出指标包括：

1. `avg_time_s`
   平均单条样本生成耗时
2. `tokens_per_s`
   生成吞吐
3. `acceptance_rate`
   draft token 被接受的比例
4. `avg_reward`
   基于参考答案计算的平均奖励，默认是 `ROUGE-L`
5. `peak_mem_mb`
   生成阶段峰值显存 / NPU allocator memory

当前默认不再启用大模型逐条质量评审。benchmark 会直接使用参考答案计算奖励指标，并在终端与 `report.md` 中输出中文自动分析。其中 `speedup` 的基线固定是普通 `sd`，不是 `auto`。

## 这些指标该怎么解读

建议按这个优先级看：

1. 先看 `avg_time_s`
2. 再看 `tokens_per_s`
3. 再看 `avg_reward`
4. 然后用 `acceptance_rate` 解释为什么更快
5. 最后再看 `peak_mem_mb`

原因是：

1. 对论文方法是否“有效”，主问题是速度和质量代理指标的平衡
2. `acceptance_rate` 是中间机制指标，不是最终业务指标
3. `peak_mem_mb` 更适合当 `sd` 与 `diversed` 的代价侧参考
4. 训练阶段和 benchmark 阶段都可以使用同一个奖励指标，因此结果更直接，也更容易复现

自动分析默认按下面的逻辑给结论：

1. 如果 `diversed` 的平均耗时更短，且奖励没有下降，则判定为“整体优于传统投机采样”
2. 如果 `diversed` 更快但奖励下降，则判定为“速度更好，但质量下降”
3. 如果 `diversed` 奖励更高但平均耗时没有更快，则判定为“质量更好，但速度没有占优”
4. 如果只是吞吐或接受率更好，但平均耗时没有占优，则判定为“存在局部收益，但不能认定端到端整体更优”

## Warmup 有什么用

`warmup` 的作用是在正式计时前，先让每种方法跑几轮不计分的预热，尽量消化以下一次性开销：

1. 首次前向时的图构建、缓存建立和 lazy 初始化
2. tokenizer / 模型对象第一次真正进入工作状态时的抖动
3. NPU allocator、框架调度和 Python 侧对象初始化造成的额外延迟

如果你想要更稳定的时间对比，可以保留 `WARMUP_RUNS=1` 或更高；如果你只是想快速验通流程，可以把它设为 `0`。

## 如何运行

在考试包根目录中执行：

```bash
cd T3_real_demo
FORCE_TRAIN=1 bash ./run_qwen_diversed_benchmark.sh
```

当前脚本默认配置为：

1. `BENCH_RUNS=1`
2. `WARMUP_RUNS=0`
3. `MAX_NEW_TOKENS=128`

脚本会优先使用 `PYTHON_BIN` 指定的 Python；未指定时，会从 `CONDA_PREFIX`、`$HOME/miniconda3`、以及当前 `PATH` 中查找能导入 `torch` 和 `transformers` 的 Python。如果自动探测失败，请显式指定：

```bash
PYTHON_BIN=/path/to/python bash ./run_qwen_diversed_benchmark.sh
```

模型路径优先读取 `TARGET_MODEL / DRAFT_MODEL`；如果没有分别指定，则读取 `QWEN_ROOT/Qwen3-8B` 和 `QWEN_ROOT/Qwen3-0.6B`。若 `QWEN_ROOT` 也未设置，脚本会尝试从当前目录相对查找 `../../../Qwen`、`../../Qwen` 或 `../Qwen`。

直接运行 `run_real_qwen_diversed.py` 时也遵循同一套规则：优先使用命令行参数，其次使用 `TARGET_MODEL / DRAFT_MODEL`，再尝试 `QWEN_ROOT` 和相对路径候选。

如果只想先检查 Python 与模型路径是否解析正确，可以运行：

```bash
DRY_RUN=1 PYTHON_BIN=/path/to/python bash ./run_qwen_diversed_benchmark.sh
```

如果你还想把 `static ensemble` 放进来一起看：

```bash
INCLUDE_STATIC=1 FORCE_TRAIN=1 bash ./run_qwen_diversed_benchmark.sh
```

最终结果会落在：

```text
T3_real_demo/T3_artifacts/benchmark_时间戳/sample_outputs.jsonl
T3_real_demo/T3_artifacts/benchmark_时间戳/report.json
T3_real_demo/T3_artifacts/benchmark_时间戳/report.md
```

## 边界

这仍然只是一个“小型真实演示”，不是论文级别的严格复现。它的价值在于：

1. 真正加载了两套模型
2. 真正跑了普通 speculative decoding
3. 真正训练了一个 dynamic ensemble head
4. 真正输出了 `sd` 与 `diversed` 可对比的速度、吞吐、奖励指标和 acceptance 指标
5. 真正把 `speedup` 口径固定成了“相对普通 speculative decoding (`sd`)”

但它还没有覆盖：

1. 更大规模训练集
2. 更复杂任务分布
3. 更严格的多轮 benchmark
4. 论文仓库里的完整训练配置与底层优化
5. 生产级 speculative decoding 所需的高效 KV cache / kernel / runtime 级加速路径
