# DeepSeek-V2-Lite 路由的官方模板敏感性审计 > 状态:真实官方 BF16 权重、本机 RTX 5090、layer 0–6 连续 forward。 > 固定模型 revision:`604d5664dddd88a0433dbae533b7fe9472482de0`。 > 研究单位:同一批 128 条公开 source prompt 的三种输入协议。 > 结论身份:**协议敏感性探针**,不是能力评测、训练分布、线上流量或专家语义。 ## 1. 这轮究竟要排除什么混杂因素 上一轮已经证明:自然长度不同会改变路由集中度,因此我们不能把所有域间差异都 解释成“内容语义”。 但即使内容和长度都固定,输入模型之前还有一层协议: ```text 纯文本 ↓ tokenizer BOS + content 单轮对话 ↓ official chat template + tokenizer BOS + "User: " + content + "\n\n" + optional "Assistant:" ``` 角色前缀会: 1. 增加 wrapper token; 2. 改变内容 token 的绝对位置; 3. 为每个内容 token 增加可见的左侧上下文; 4. 可能让第一个内容 token 在 BPE 边界处重新切分; 5. 改变“整段输入统计”的分母。 所以本轮不是问“chat 模型强不强”,而是问: > 同一段内容送入同一个 base checkpoint,仅改变官方输入包装时,前六个 MoE > 层的路由统计会怎样变化? ## 2. 官方模板不是自行编写的教学字符串 固定 revision 的 `tokenizer_config.json` 给出: ```jinja {{ bos_token }} {% for message in messages %} user -> "User: " + content + "\n\n" assistant -> "Assistant: " + content + eos_token system -> content + "\n\n" {% endfor %} {% if add_generation_prompt %}"Assistant:"{% endif %} ``` 完整模板字符串 SHA-256: ```text 8aeba567270fa9a8d5372caf4b04affea947c4421d96adc01e3ff94021b0ae8e ``` 特殊 token: | 角色 | 文本 | ID | | --- | --- | ---: | | BOS | `<|begin▁of▁sentence|>` | 100000 | | EOS / PAD | `<|end▁of▁sentence|>` | 100001 | 实验通过 Transformers 4.41.2 的 `apply_chat_template` 生成字符串和 token IDs; 随后再次对渲染字符串编码,并要求两条路径的 token IDs 完全一致。 ## 3. 三个条件如何构成“实验 + 负对照” 每条 source prompt 都渲染三遍: ```text RAW [BOS] [content] USER [BOS] [User:] [content] [\n\n] GENERATION [BOS] [User:] [content] [\n\n] [Assistant:] ``` 比较分两种: ### 3.1 `RAW → USER` 这是主要敏感性实验。 - 内容字符前缀相同; - checkpoint、权重、batch、层、dtype 相同; - 改变的是左侧角色前缀和尾部换行; - 尾部换行不能反向影响更早的内容 token; - `User:` 前缀可以通过因果注意力影响后续内容 token。 它能描述“输入协议敏感性”,但不能单独证明训练中的角色语义或能力变化。 ### 3.2 `USER → GENERATION` 这是因果负对照。 `GENERATION` 只在 `USER` 末尾追加 `Assistant:`。在严格因果 attention 中, 未来 suffix 不应改变此前共享前缀的 hidden state 或路由。 因此它同时验证: - causal mask; - batching; - token prefix; - route 捕获; - 分析管线。 若这个负对照失败,`RAW → USER` 的任何解释都不可信。 ## 4. 固定 cohort 与内容长度合同 四个公开域仍为: | 域 | 来源 | 使用字段 | 数量 | | --- | --- | --- | ---: | | 英文百科 | WikiText-2 raw validation | `text` | 32 | | 中文新闻 | CLUE TNEWS public test | `sentence` | 32 | | Python 代码 | OpenAI HumanEval | `prompt` | 32 | | 小学数学 | OpenAI GSM8K test | `question` | 32 | 答案不输入,HumanEval 代码不执行。 ### 4.1 为什么固定 23 个 content tokens 上一轮 24-token matched cohort 的总长包括 BOS: ```text 1 BOS + 23 source-content tokens = 24 raw input tokens ``` 本轮直接把内容合同写清楚为 **23 个 regular-tokenizer content tokens**。 这样 RAW 条件固定为 24 tokens,同时保留足够多的 TNEWS 候选。 ### 4.2 字符前缀如何重建 BPE 对“完整字符串”和“截断后的字符串”可能在最后一个 token 发生不同合并。 因此不能粗暴地取完整编码的前 23 个 IDs 再假设可逆。 脚本采用: 1. 读取 fast tokenizer offset mapping; 2. 在目标 token 边界附近搜索字符终点; 3. 重新编码字符前缀; 4. 只接受恰好得到 23 tokens 的最长前缀。 选样排序固定为: ```text ascending SHA256( "llm-atlas-deepseek-routing-template-control-v1" | domain | source_id ) ``` 候选与选中来源长度: | 域 | 可用候选 | 选中 | source tokens min / mean / max | | --- | ---: | ---: | ---: | | 英文百科 | 1,655 | 32 | 29 / 142.84 / 280 | | 中文新闻 | 1,609 | 32 | 23 / 24.66 / 30 | | Python 代码 | 164 | 32 | 42 / 141.78 / 352 | | 小学数学 | 1,319 | 32 | 31 / 59.88 / 131 | TNEWS 在完成选中前有 1 条无法构造精确 23-token 字符前缀,脚本显式记录并继续按 哈希顺序选择下一条;其余域为 0。 ## 5. 为什么必须分“整段输入”和“对齐内容” 若直接把 wrapper token 与 content token 全部求和,得到的是部署协议问题: > 这一整段实际输入会把 token 路由到哪里? 若只研究内容,需要保证比较的是同一 token,而不是“都在内容字符范围内”就算相同。 本轮为三种条件中的每个 token 记录: ```text (相对内容字符起点, 相对内容字符终点, token ID) ``` 然后只保留三个条件的精确交集。 ### 5.1 Token 账 | 条件 | 输入 tokens | 对齐内容 tokens | 六层真实 top-6 routes | | --- | ---: | ---: | ---: | | RAW | 3,072 | 2,874 | 110,592 | | USER | 3,642 | 2,874 | 131,112 | | GENERATION | 3,898 | 2,874 | 140,328 | | 合计 | 10,612 | — | **382,032** | RAW 原始内容范围共有 2,944 tokens;由于 `User: ` 后的 BPE 边界,70 个首 token 不能以相同 `(span, token ID)` 对齐,因此从三个条件中同时剔除。 按域的对齐覆盖: | 域 | aligned / RAW content | 覆盖率 | | --- | ---: | ---: | | 英文百科 | 706 / 736 | 95.9% | | 中文新闻 | 735 / 736 | 99.9% | | Python 代码 | 733 / 736 | 99.6% | | 小学数学 | 700 / 736 | 95.1% | 这意味着 `content_only` 比较中的每一条 route 都对应相同字符段与相同 token ID。 ## 6. 统计合同 与长度实验相同,抽样单位仍是 source prompt,而不是 token。 每个 layer × scope × mode × domain: 1. 32 条 prompt; 2. 有放回重采样 2,000 次; 3. before / after 使用完全相同的 prompt indices; 4. 固定 seed `20260729`; 5. 报告 percentile 95% 区间; 6. 不进行假设检验; 7. 不做多重比较校正。 聚合口径: - `prompt_balanced`:每条 prompt 先归一,再让 32 条 prompt 等权; - `token_weighted`:先汇总选中 token routes,再整体归一。 主要正文使用 `prompt_balanced`,避免少量 tokenization 长度差异改变 prompt 权重。 ## 7. 负对照:21,852 个共享前缀 token 全部 exact 每个 MoE 层: | Layer | USER 共享前缀 tokens | ordered top-6 exact | 违规 prompt | | --- | ---: | ---: | ---: | | L1 | 3,642 | 3,642 | 0 | | L2 | 3,642 | 3,642 | 0 | | L3 | 3,642 | 3,642 | 0 | | L4 | 3,642 | 3,642 | 0 | | L5 | 3,642 | 3,642 | 0 | | L6 | 3,642 | 3,642 | 0 | | 合计 | **21,852** | **21,852** | **0** | `content_only` 的 USER→GENERATION 在 6 层 × 4 域共 24 个比较中: ```text CV Δ = 0 total variation = 0 JSD = 0 ``` 但若看 `full_input`,因为 GENERATION 确实新增了 `Assistant:` token,prompt-balanced total variation 为 0.037–0.051,平均 0.0445。 这组结果正好说明: > suffix 没有改写过去;整段统计改变,是因为统计对象加入了新 token。 ## 8. 主结果:同一个 `User:` 前缀在不同深度方向不同 以下使用: - scope:三个条件精确对齐的 content tokens; - mode:prompt-balanced; - Δ:`CV(USER) − CV(RAW)`。 | Layer | English | Chinese | Code | Math | |---|---:|---:|---:|---:| | L1 | 0.633→0.593, **−0.040** [−0.063, −0.017] | 0.670→0.621, **−0.049** [−0.068, −0.028] | 0.901→0.882, −0.019 [−0.039, 0.001] | 0.817→0.796, −0.021 [−0.049, 0.006] | | L2 | 0.404→0.405, 0.001 [−0.014, 0.016] | 0.383→0.353, **−0.030** [−0.041, −0.016] | 0.684→0.617, **−0.067** [−0.084, −0.047] | 0.453→0.455, 0.002 [−0.016, 0.016] | | L3 | 0.405→0.422, 0.017 [−0.002, 0.033] | 0.415→0.471, **0.057** [0.033, 0.075] | 0.824→0.812, −0.011 [−0.023, 0.001] | 0.541→0.547, 0.006 [−0.011, 0.023] | | L4 | 0.530→0.555, 0.025 [−0.002, 0.049] | 0.897→0.848, **−0.048** [−0.067, −0.030] | 1.064→1.038, **−0.027** [−0.039, −0.016] | 0.668→0.649, −0.020 [−0.040, 0.000] | | L5 | 0.446→0.451, 0.005 [−0.015, 0.021] | 0.478→0.504, **0.026** [0.006, 0.042] | 0.808→0.850, **0.043** [0.032, 0.055] | 0.770→0.779, 0.008 [−0.005, 0.021] | | L6 | 0.502→0.562, **0.060** [0.036, 0.076] | 0.509→0.539, **0.030** [0.009, 0.043] | 0.851→0.909, **0.058** [0.046, 0.068] | 0.738→0.775, **0.037** [0.013, 0.058] | ### 8.1 初学者应该怎样读 CV 越高,64 个 routed experts 的份额越不平。 - L1:英文、中文的对齐内容变得更平; - L2:中文、代码变得更平; - L3:中文反而更集中; - L4:中文、代码又变平; - L5:中文、代码更集中; - L6:四域全部更集中。 因此不应写成: > Chat template 会让路由更均衡。 也不应写成: > Chat template 会让路由更集中。 更准确的说法是: > 在这个 23-token 固定探针中,角色前缀对路由集中度的影响随层和域改变方向; > 低层与高层不能用一个单调结论概括。 ## 9. “整段输入”会给出另一幅图 若把 BOS、`User:`、换行与 content 全部计算,Δ CV 为: | Layer | English | Chinese | Code | Math | |---|---:|---:|---:|---:| | L1 | 0.061 [0.041, 0.077] | 0.093 [0.062, 0.112] | 0.014 [−0.009, 0.035] | 0.058 [0.029, 0.083] | | L2 | 0.093 [0.069, 0.106] | 0.138 [0.106, 0.151] | 0.038 [0.020, 0.052] | 0.098 [0.065, 0.122] | | L3 | 0.100 [0.072, 0.110] | 0.140 [0.096, 0.159] | 0.078 [0.058, 0.092] | 0.058 [0.031, 0.076] | | L4 | 0.131 [0.093, 0.152] | −0.012 [−0.033, 0.003] | 0.051 [0.035, 0.063] | 0.066 [0.042, 0.083] | | L5 | 0.083 [0.050, 0.094] | 0.036 [−0.003, 0.054] | 0.092 [0.070, 0.112] | −0.053 [−0.075, −0.036] | | L6 | 0.134 [0.090, 0.155] | 0.046 [0.000, 0.069] | 0.103 [0.079, 0.118] | 0.027 [−0.004, 0.047] | 多数格子是正值,与对齐内容的混合方向明显不同。 原因不是统计错误,而是问题不同: - `full_input` 包含重复出现的 wrapper token; - `content_only` 只比较相同字符段和 token ID; - wrapper 自己可能有很强、很稳定的路由偏好; - wrapper 数量占短输入的比例不小。 这也是为什么评测协议、chat template 和 tokenizer 必须进入实验账本,而不能只写模型名。 ## 10. 逐 token top-6 有多稳定 对 RAW 与 USER 中能按 `(relative span, token ID)` 对齐的内容 token,报告: - `set`:top-6 专家集合完全相同的比例; - `J`:top-6 专家集合的平均 Jaccard。 | Layer | English | Chinese | Code | Math | |---|---:|---:|---:|---:| | L1 | set 0.552 · J 0.854 | set 0.600 · J 0.867 | set 0.625 · J 0.867 | set 0.459 · J 0.822 | | L2 | set 0.606 · J 0.874 | set 0.604 · J 0.869 | set 0.562 · J 0.846 | set 0.577 · J 0.860 | | L3 | set 0.625 · J 0.881 | set 0.634 · J 0.875 | set 0.570 · J 0.864 | set 0.644 · J 0.890 | | L4 | set 0.572 · J 0.865 | set 0.590 · J 0.869 | set 0.562 · J 0.831 | set 0.593 · J 0.873 | | L5 | set 0.540 · J 0.849 | set 0.550 · J 0.841 | set 0.551 · J 0.824 | set 0.610 · J 0.872 | | L6 | set 0.581 · J 0.859 | set 0.558 · J 0.839 | set 0.529 · J 0.814 | set 0.544 · J 0.846 | 两个数字可以同时成立: - 只有约 46%–64% 的 token 保持完全相同的 top-6 集合; - 但平均 Jaccard 仍为 0.814–0.890。 直觉上,这说明很多变化不是“六个专家全部换掉”,而是 top-6 边缘的一两个专家发生替换。 它仍不能告诉我们专家“负责什么语义”。 ## 11. 分布距离不大,不等于逐 token 没变化 RAW→USER 的对齐内容、prompt-balanced: - total variation 点估计约为 0.024–0.067; - JSD 点估计约为 0.001–0.008 nats。 聚合分布看起来接近,但上一节仍有大量 token 的 top-6 集合不完全相同。 这是一个重要的测量层级区别: ```text aggregate distribution close ≠ every token took the same route ``` 相反,也不能从 token route 变化推出输出答案或任务分数必然变化;本实验没有执行完整模型生成。 ## 12. 独立复跑 正式输出: ```text src/data/deepseek-v2-lite-routing-template.json src/data/deepseek-v2-lite-routing-template-repro.json ``` 两份完整 JSON: ```text SHA-256 da1f10333b2fa269e64f9716a0ca6c1a656d23d3e70b8a25d6e59f8a5b3bc1b9 ``` `cmp` byte-exact。 复现命令: ```bash PYTHONPATH=/path/to/transformers-4.41.2-deps:/usr/lib/python3/dist-packages \ python -B experiments/deepseek/v2_lite_routing_template_probe.py \ --artifact-dir /path/to/deepseek-v2-lite \ --human-eval /path/to/HumanEval.jsonl.gz \ --gsm8k /path/to/gsm8k/test.jsonl \ --tnews /path/to/tnews/test.json \ --tnews-archive /path/to/tnews_public.zip \ --wikitext /path/to/wikitext-validation.parquet \ --output /path/to/template-probe.json \ --per-domain 32 \ --content-tokens 23 \ --batch-prompts 8 \ --layers 7 \ --bootstrap 2000 \ --seed 20260729 \ --captured-at 2026-07-29T08:30:00+00:00 ``` Jinja2 是 `apply_chat_template` 的运行依赖;正式环境记录为 3.1.6。 ## 13. 可以说什么,不能说什么 ### 可以说 - 固定官方 revision 的 chat template 会改变输入 token 序列; - 相同字符段、相同 token ID 的内容路由对角色前缀敏感; - 敏感性随层和域改变方向; - 追加在未来的 `Assistant:` 不改变此前共享前缀,六层 21,852 / 21,852 exact; - wrapper-inclusive 与 aligned-content 统计回答不同问题; - 正式运行与独立复跑 byte-exact。 ### 不能说 - `User:` 专门激活某类“对话专家”; - top-6 ID 的变化等于专家语义变化; - 路由更平或更集中等于模型能力更强; - 这个 base checkpoint 的 route probe 等于 chat model 的线上流量; - 前六层等于完整 27 层; - 23-token 前缀代表长对话; - 不生成答案的实验可以给出任务准确率结论; - 24 个 layer×domain 区间是已校正的显著性检验。 ## 14. 下一道闸门 1. 下载并执行完整 layer 0–26,检查“低层变平、高层变集中”的模式是否延续; 2. 加入 system role、few-shot turn 与换行边界的正交扰动; 3. 把模板敏感性接到完整生成与输出指标,仍保持 route / output 两张账; 4. 在支持环境执行 FlashMLA optimized kernel; 5. 继续 FP8 / pipeline traces 与 R1-like 小模型复现。