Files
llm-atlas/research/DEEPSEEK_V2_LITE_TRACE.md
T
2026-07-29 14:18:45 +08:00

341 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DeepSeek-V2-Lite 真实权重执行审计
> 状态:DeepSeek 专题第三轮工件;2026-07-29
> 目的:把“MLA 会压缩缓存”“MoE 会选择专家”从论文概念推进到固定官方权重、真实 tokenizer、真实 hidden states 和逐 token 路由记录。
> 边界:这不是完整模型 benchmark、训练 trace、生产服务复现、专家语义分析或总体负载估计。
## 0. 一页结论
这次实验固定官方 `deepseek-ai/DeepSeek-V2-Lite` revision
`604d5664dddd88a0433dbae533b7fe9472482de0`,使用官方 tokenizer、配置、模型代码和
BF16 checkpoint 第一分片,在 RTX 5090 上依次执行 embedding 与 decoder layer 0–6。
第 0 层是 dense FFN,第 1–6 层是完整 MoE 层;第 7 层横跨第一、第二分片,因此明确停止。
四条最重要的结果:
1. 4 条人工固定 prompt 共 90 个有效 token,在 6 个 MoE 层产生
`6 layers × 90 tokens × top-6 = 3,240` 次真实 routed-expert 选择。
2. 每个执行层都实际观测到 MLA 的 `[B,T,576]` 压缩投影;其中 512 是
KV latent,64 是 decoupled RoPE key。
3. 官方 Hugging Face eager 实现仍把 cache 物化为
key `[4,16,27,192]` 和 value `[4,16,27,128]`。以 BF16 计,
每 token、每层是 10,240 bytes;按配置保存 latent + RoPE key 只需 1,152 bytes,
相差 `8.8889×`,后者相对前者减少 `88.75%`。
4. 两次独立执行的来源、配置、tokenization、hidden-state hashes、MLA shapes、
aggregate loads 与全部 token routes 共 `31/31` 项完全一致;计时明确不参与一致性检查。
这里最反直觉的发现不是某个“神奇专家”,而是:
- **覆盖不等于均衡。** 90 个 token 在单层可触达 60–64/64 个专家,但各层
CV 为 0.549–0.925,Gini 为 0.303–0.420。
- **路由 ID 不等于语义标签。** `E8` 只表示某一层内部数组的第 8 号专家;跨层的
`E8` 是不同参数,四条 prompt 也不足以赋予“代码专家”等语义。
- **算法压缩不等于实现已经压缩存储。** MLA 的低秩状态合同与 HF eager cache
的实际物化格式是两张账,必须同时报告。
## 1. 证据身份
### 1.1 官方工件
| 工件 | 固定值 |
|---|---|
| Hugging Face model | `deepseek-ai/DeepSeek-V2-Lite` |
| revision | `604d5664dddd88a0433dbae533b7fe9472482de0` |
| checkpoint tensor bytes | `31,412,968,448`,约 29.261 GiB |
| shard 1 bytes | `8,594,887,408`,约 8.005 GiB |
| config SHA-256 | `f346286b0f1c8b044252fd54cb4fa78b9fab6472a6e8bebb9edfe03d414ea03d` |
| modeling code SHA-256 | `7d8e5221095286eea991137760893fd7ba52727c0b4ebf48ec09e8bc56b45b9c` |
| tokenizer SHA-256 | `41f3bf64213da8c012d8bd0871a58a1fdf70463e8f08f110ddbb1082f529f669` |
| index SHA-256 | `d2cdb2f325f6682cf3ad1ad2526a9f979d857390b579380c0331d975136e0acf` |
| shard 1 SHA-256 | `0d7e9f39bde40111a4c0f390b87497dce4565cf578d916395e6b2c7851f1e8da` |
第一分片完整容纳 embedding、layer 0–6 和 layer 7 的一部分。为了不把缺参数的
layer 7 冒充完整执行,本实验把证据边界钉在 layer 6。
原始 checkpoint 分片不进入 Git;仓库只提交:
- 可复现执行脚本;
- 机器可读 trace;
- 独立复跑比较结果;
- 研究审计与网页可视化。
### 1.2 模型配置
| 对象 | 官方配置 |
|---|---:|
| reported total / active | 15.7B / 2.4B |
| decoder layers | 27 |
| hidden size | 2048 |
| attention heads | 16 |
| qk NoPE / RoPE head dim | 128 / 64 |
| value head dim | 128 |
| KV LoRA rank | 512 |
| routed experts / active | 64 / 6 |
| shared experts | 2 |
| expert intermediate | 1408 |
| dense prefix | 1 layer |
| router | softmax + greedy top-k |
| selected top-k normalization | false |
`max_position_embeddings=163840` 与 YaRN 配置属于 checkpoint config;
官方发布表中的上下文口径不应被本实验自行改写成同一个数字。
### 1.3 软件与硬件
| 对象 | 值 |
|---|---|
| GPU | NVIDIA GeForce RTX 5090,32,607 MiB |
| driver | 595.84 |
| Python | 3.10.14 |
| PyTorch | 2.11.0+cu128 |
| CUDA runtime | 12.8 |
| Transformers | 4.41.2 |
| safetensors | 0.8.0 |
| TF32 matmul | disabled |
官方 2024 remote code 在当前 Transformers 5.5 环境中会因已移除的
`is_torch_fx_available` 入口而导入失败。因此实验使用隔离的 Transformers 4.41.2。
模型源码保持只读、不打补丁;runner 只通过本地 package 方式导入官方
`configuration_deepseek.py` 与 `modeling_deepseek.py`。
这条版本边界很重要:**“模型代码公开”不等于“任意未来依赖版本都可直接运行”。**
## 2. 实验设计
### 2.1 为什么选择截断的精确 forward
完整 15.7B BF16 权重与运行时状态虽然可能勉强接近单卡边界,但没有必要为了观察
前六层真实路由而把“全模型能否高效生成”混进同一实验。本 runner:
1. 用官方 tokenizer 一次编码 4 条 prompt;
2. 从第一分片读取官方 embedding;
3. 每次在 meta device 构造一个官方 decoder layer;
4. 为这一层绑定真实 safetensors 参数并传到 GPU;
5. 用上层输出作为下一层输入;
6. 通过 hook 捕获实际 `kv_a_proj_with_mqa` 与实际 MoE gate 输出;
7. 释放当前层,再加载下一层。
因此得到的是**真实权重、真实层类、真实连续 hidden states**,而不是随机初始化、
只读 config 或合成 router;但它仍不是完整 27 层生成。
### 2.2 固定 prompt
| ID | 标签 | token |
|---|---|---:|
| `zh_explanation` | 中文解释 | 26 |
| `en_architecture` | English architecture | 18 |
| `code` | Python code | 19 |
| `math` | 数学推理 | 27 |
batch padding 后形状为 `B=4, T=27`,有效 token 共 90。prompt 是用于建立可复现
探针的人工样本,不是自然语料分布、benchmark 数据集或统计代表样本。
## 3. MLA:一张算法状态账,一张实现物化账
### 3.1 实际观测
每个执行层的 `kv_a_proj_with_mqa` 输出都是:
```text
[batch, sequence, kv_lora_rank + qk_rope_head_dim]
= [4, 27, 512 + 64]
= [4, 27, 576]
```
这 576 个元素可以分为:
- `c_t^KV`:512 维 joint latent;
- `k_t^R`:64 维 decoupled RoPE key。
后者不能因为体积小就从缓存公式中消失。
### 3.2 HF eager 实际保存了什么
本次 `use_cache=True` 的官方 Hugging Face eager 路径实际返回:
```text
key [4, 16, 27, 192]
value [4, 16, 27, 128]
```
所以每 token、每层的缓存元素是:
```text
expanded eager = 16 × (192 + 128) = 5,120 elements
latent contract = 512 + 64 = 576 elements
```
在 BF16 下:
```text
eager bytes = 5,120 × 2 = 10,240
latent bytes = 576 × 2 = 1,152
ratio = 10,240 / 1,152 = 8.8889×
reduction = 1 - 1,152 / 10,240 = 88.75%
```
### 3.3 为什么这不是论文 93.3% 的复跑
V2 报告的 `93.3%` 是完整 V2 相对 DeepSeek 67B、在论文指定配置和比较口径下的
作者报告结果。本实验的 `88.75%` 是 V2-Lite 配置中:
- HF eager 展开 K/V 元素;
- 与 latent + RoPE key 理论存储元素;
之间的逐 token、逐层 BF16 算术比较。模型、维度、基线和实现口径都不同,
二者不能互相替代。
进一步说,低秩架构只定义“可以保存什么最小状态”;生产系统是否真的只保存 latent,
还取决于 attention kernel、权重吸收、RoPE 分叉、paged cache 和服务框架。
## 4. MoE:真实路由显微镜
### 4.1 总量守恒
每个 MoE 层都满足:
```text
90 valid tokens × top-6 = 540 routed selections
```
6 个层合计 `3,240` 次。两条 shared expert 等价路径始终运行,但它们不属于 router
输出的 0–63 routed expert ID,因此不写进 top-6 列表。
### 4.2 六层聚合负载
| layer | selections | used / 64 | zero | CV | Gini | effective experts | top expert |
|---:|---:|---:|---:|---:|---:|---:|---|
| 1 | 540 | 63 | 1 | 0.925 | 0.420 | 46.66 | E8 · 48 |
| 2 | 540 | 64 | 0 | 0.549 | 0.303 | 55.08 | E62 · 23 |
| 3 | 540 | 62 | 2 | 0.569 | 0.322 | 53.51 | E54 · 19 |
| 4 | 540 | 62 | 2 | 0.866 | 0.417 | 47.03 | E48 · 40 |
| 5 | 540 | 60 | 4 | 0.642 | 0.332 | 52.14 | E14 · 32 |
| 6 | 540 | 61 | 3 | 0.765 | 0.378 | 49.48 | E25 · 40 |
三个指标回答不同问题:
- `used experts`:这批 token 是否至少触达某专家一次;
- `CV = std / mean`:计数离散程度;
- `effective experts = exp(entropy)`:若把当前熵换算成均匀分布,相当于多少个专家。
因此 layer 1 触达 63 个专家,却只有 46.66 个 effective experts,且 E8 承担 48/540
次选择。只报“63/64 被使用”会掩盖负载长尾。
### 4.3 prompt 之间看到的差异
同一层内,四条 prompt 的 used-expert set Jaccard 与 CV 会变化。例如:
- layer 1:中文 prompt 使用 54 个专家、CV 0.898;英文架构 prompt 使用 40 个、
CV 1.360;代码 prompt 44 个、CV 1.263;数学 prompt 45 个、CV 1.184。
- layer 4:聚合 top expert 是 E48;英文和代码各把 E48 路由 11 次,
中文 prompt 的 top expert 却是 E29、16 次。
- layer 6:英文 prompt 使用 42 个专家、CV 1.439;中文为 49 个、1.031;
数学为 48 个、0.915。
这些是**描述性 route traces**,说明 token 与上下文确实改变 gate 输出;它们不证明
“E48 是代码专家”,更不允许以四条 prompt 推断训练语料上的领域分工。
### 4.4 top-6 权重没有重新归一
配置 `norm_topk_prob=false`。router 先对 64 个专家做 softmax,再选 top-6;
被选中的 6 个概率不会重新缩放到和为 1。各层选中权重和的均值:
| layer | mean selected top-6 weight sum |
|---:|---:|
| 1 | 0.3949 |
| 2 | 0.4801 |
| 3 | 0.4898 |
| 4 | 0.4749 |
| 5 | 0.4832 |
| 6 | 0.4440 |
所以网页显示某 token 的六个权重和小于 1 是真实配置行为,不是漏算。
## 5. 可复现性闸门
正式 trace:
```text
src/data/deepseek-v2-lite-trace.json
SHA-256 81ff4ab34d20121fe42219c566dd4780f4c7f74252a31b596f90e5280e1f1aef
captured 2026-07-29T05:55:35.599912+00:00
```
第二次独立执行:
```text
SHA-256 b5e2e7567e17765195313473499522a4529b9d37ea96963ba2b9455c3c0b86e6
captured 2026-07-29T05:56:32.527770+00:00
```
比较结果:
```text
31 / 31 exact
timing compared: false
```
精确检查覆盖:
- provenance、configuration、prompt tokenization;
- initial / final hidden hashes;
- layer 0–6 hidden hashes 与 MLA shapes;
- layer 1–6 aggregate loads;
- layer 1–6 全部 token routes。
计时会受首次 kernel、频率、温度、后台进程与同步位置影响,因此没有伪装成 deterministic
evidence。本轮只把计时保留在原始 trace,网站不把它包装成性能 benchmark。
## 6. 绝对禁止的外推
| 本实验能够说 | 本实验不能说 |
|---|---|
| 固定 revision 的前 7 层产生了这些 hidden states | 完整模型会生成什么回答 |
| 四条 prompt 在 6 层产生这些 top-6 routes | 训练集或线上请求的总体专家负载 |
| 某层某 expert ID 被选中多少次 | 该专家具有什么稳定语义 |
| HF eager 物化了展开 K/V | 所有 MLA serving kernel 都这样缓存 |
| V2-Lite 配置可导出 576 元素 latent state | 完整 V2 的 93.3% 已被复跑 |
| 两次相同环境执行 31/31 exact | 跨 GPU、依赖版本和 kernel 仍 bitwise identical |
| layer 0–6 在第一分片内完整 | layer 7 或 layer 8–26 已执行 |
跨层连线也被禁止:layer 1 的 E8 与 layer 2 的 E8 是不同参数对象。可视化只能在同一层内
比较 expert load,不能画一条 “E8 专长” 纵贯 6 层。
## 7. 复现入口
环境与命令见:
- `experiments/deepseek/README.md`
- `experiments/deepseek/v2_lite_trace.py`
- `experiments/deepseek/compare_v2_lite_traces.py`
执行一次:
```bash
python experiments/deepseek/v2_lite_trace.py \
--artifact-dir /path/to/deepseek-v2-lite \
--output /path/to/trace.json
```
比较两次:
```bash
python experiments/deepseek/compare_v2_lite_traces.py \
--first /path/to/trace-1.json \
--second /path/to/trace-2.json \
--output /path/to/repro.json
```
## 8. 下一道研究闸门
本轮已经把 DeepSeek 第三轮从“计划真实 trace”推进到真实 tokenizer、MLA state、
MoE routes 与确定性复跑,但仍有四块未闭合:
1. 用真正只保存 latent 的 MLA kernel 对照 HF eager materialization;
2. 扩大公开语料样本,给 aggregate load 置信区间,并考察 batch / padding / sequence
对 expert parallel 的影响;
3. 复跑 FP8、pipeline 或通信 trace,而不是继续用教学 schedule;
4. 做可审计的 R1-like 小模型训练实验,记录 rollout、reward、KL、长度与梯度统计。
在这些闸门完成之前,DeepSeek 专题保持“第三轮进行中”,不写成完整独立复现。