Files
WpyQwq 643e22ecb9 Natural Memory NM2.1: 记忆路由器分叉、数据集缺陷修复与全轴评测证据
- 引入 MemoryRouterXL 与 v5/v6 流式多线程训练/编码管线
- 修复 prepare_memory_router_dataset 候选池重建缺陷(mega 家族 3568x 加速,输出逐字节相同)
- 修复 v5 被破坏的拒答与多跳标签(train 未知样本 319 -> 16319,multi_hop 平均正例 1.00 -> 2.00)
- 同存储预算下 V2-128 v6 逐轴 22/22 通过:Top-1 41.12% -> 94.62%,未知拒答 0.00% -> 100.00%
- 记录三条被实测推翻的显然优化(logits_to_keep=1 反而慢 55%、XL 容量未带来收益)
- 记忆手术跨架构可移植性 14/14,读写关闭时与原生模型逐位相同
2026-09-19 11:11:31 +08:00

331 lines
21 KiB
Markdown
Raw Permalink 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.
# V2_dpskw —— Natural Memory 路由器分叉
本目录是 `H:\Memory\dynamic_memory_lab`(GPT 时代的原工程)的**代码分叉**,用于训练一个**全新的、更大更强的路由器**。
原工程保持只读不改;本 fork 内所有代码的包名已统一为 `V2_dpskw`。
## 与原工程的关系
| 项目 | 处理方式 |
|---|---|
| 全部源码 / 测试 / 文档 | 复制进本目录,包名 `dynamic_memory_lab` → `V2_dpskw`(51 个文件) |
| Qwen3.5-4B 权重包 | 目录联接(junction)`qwen3_5_4b_natural_memory_v2` → 原工程,不复制 9.3 GB |
| 冻结的 router 训练数据 | 复制 `data/router_training_v3/`(train 7155 / eval 1940,sha256 与原版一致) |
| 冻结的 Qwen 特征库 | 复制 `checkpoints/router_shared/feature_cache/`(21263 × 2560 fp16),manifest 的 `model_path` 已改写为 fork 内路径 |
| 其余 data / checkpoints / 适配器 / 大报告 | **不复制**,需要时按绝对路径引用原工程 |
因此训练时**不会加载 Qwen、也不会重新编码特征**,30 秒内即可开始更新路由器参数。
## 新增内容
| 文件 | 作用 |
|---|---|
| `router_xl.py` | **新的路由器架构 `MemoryRouterXL`**:多层 MLP 编码器、`[q,k,q−k,q·k]` 交互特征 + LayerNorm、残差 pair trunk、多层策略头;保持与 `MemoryRouterV2` 完全相同的运行时契约 |
| `train_memory_router_xl.py` | v3 数据上的训练器(复用原训练器的度量代码,只替换模型) |
| `train_router_v5.py` | **v5/v6 全量数据训练器**:流式读取 JSONL(不把 episode 解析进内存)、**mmap** 10.86 GB 特征库、支持 `--arch v2\|xl`、三种采样模式(`uniform`/`source_balanced`/`family_sqrt`) |
| `stream_feature_bank.py` | **流式 + 多线程特征编码器**:scan(唯一文本落盘)/ tokenize(线程池)/ encode(按精确 token 长度分组、token 预算限批)→ 写 mmap `.npy` |
| `eval_router_scorecard.py` | v3 版多轴评分卡 |
| `eval_router_v5.py` | v5/v6 版多轴评分卡:流式 + mmap + **按 family / 按 10 个类别拆解** |
| `audit_router_dataset.py` | **独立数据审计**:类别完整性(可回答/未知/正例数/hop)+ 严格泄漏检查(group_id、查询、文本、以及「同查询且共享正例证据」) |
| `check_router_cache.py` | 冻结特征缓存快速校验(失配即报错,不会偷偷加载 4B 模型重编码) |
| `compare_router_runs.py` | 与原 512 基线的汇总对比 |
| `tests/test_router_xl.py` | 新架构契约测试 |
| `run_router_v6.ps1` / `chain_v6.ps1` | v6 训练启动器 / 编码完成自动接训练与评分的长链 |
`prepare_memory_router_dataset.py` 额外修复了一个**性能缺陷**(见下)。
## 已验证事实
- 全部单元测试通过:**52 项**(原 45 项 + 新增 7 项)。
- 特征缓存命中,训练全程不加载 Qwen。
- 新架构与原架构同尺寸对比:`router_dim` 相同意味着**每条记忆记录的地址占用完全相同**(512 维 × fp32 = 2048 字节)。
## MemoryRouterXL 容量
| 路由器 | router_dim | heads | 参数 | 相对 V2-512 |
|---|---:|---:|---:|---:|
| V2-512 基线(原工程) | 512 | 8 | 4,741,902 | 1.00× |
| XL-512 | 512 | 8 | 7,898,127 | 1.67× |
| XL-1024 | 1024 | 16 | 18,414,103 | 3.88× |
| XL-2048 | 2048 | 16 | 55,133,719 | 11.63× |
> 只有 XL-512 的地址几何与原版一致(同样的存储成本),因此它是唯一可以做同尺寸对比的配置。
## XL-512 训练结果(100,000 步,冻结 v3 eval:1940 条)
| 指标 | V2-512 best(step1k) | V2-512 final(step100k) | XL-512 best | XL-512 final(step100k) |
|---|---:|---:|---:|---:|
| 参数 | 4,741,902 | 4,741,902 | 7,898,127 | 7,898,127 |
| 每条记录地址字节 | 2048 | 2048 | 2048 | 2048 |
| Top-1 正确率 | **67.12%** | 60.63% | 60.85% | 54.36% |
| Recall@3 | 88.77% | 85.01% | **92.97%** | 85.33% |
| Recall@5 | 94.93% | 90.13% | **97.33%** | 91.93% |
| MRR | **79.08%** | 73.26% | 75.98% | 69.50% |
| nDCG@3 | **80.00%** | 74.57% | 79.28% | 71.71% |
| 多跳证据全中(Top-3) | 88.66% | 84.90% | **92.86%** | 85.22% |
| hop 正确率 | 97.84% | **99.69%** | 99.38% | **99.69%** |
| need F1 | 98.92% | **99.89%** | 99.62% | **99.89%** |
| 未知拒答率 (thr 0.50) | 66.04% | **100.00%** | 90.57% | **100.00%** |
| 已知问题被误拒率 | 0.22% | 0.22% | 0.22% | 0.22% |
| 未知问题被误读率 | 33.96% | 0.00% | 9.43% | 0.00% |
| 平均分数余量 | 0.588 | 3.884 | 6.710 | **25.376** |
| 单查询延迟 ms (GPU) | **0.895** | 0.935 | 1.310 | 1.246 |
| 路由 QPS (GPU) | **1117** | 1070 | 763 | 803 |
**结论不是「谁全面更强」,而是各轴各有胜负:**
1. 原工程用 `selection_score = 0.5·top1 + 0.3·need_f1 + 0.2·mrr` 选出的 "best"(step 1000)Top-1 最高,但**在 0.50 门槛下有 33.96% 的未知问题被强行读记忆**;该综合分几乎不惩罚这一点,因为未知样本只有 106 条。
2. XL-512 在**覆盖率**上明显更好:Recall@3 +4.20pp、Recall@5 +2.40pp、多跳证据全中 +3.96pp。
3. 把门槛从 0.50 提到 0.70,XL-512 best 的未知拒答率达到 **100.00%**,而已知问题被误拒率仍为 **0.22%**——即在同等策略安全水平下,XL-512 的排序覆盖优势可以保留。
4. 代价:XL-512 参数 1.67×、单查询延迟 +39~46%(0.935 → 1.310 ms)、QPS 从 1070 降到 763。
## 数据集构建:一个 46.8 小时的缺陷
`prepare_memory_router_dataset.py` 的 `_source_candidates` 原先对**每条 episode** 都重建候选池:
```python
same_family = [item for item in items_by_family.get(episode.family, []) if item.item_id not in positive_set]
```
引入 mega 验证集后,`mega_validation` 家族的候选池有 **2,656,000** 条,于是一次列表复制约 2.1 秒:
| 家族 | 原版 / 条 | 修复后 / 条 | 加速 |
|---|---:|---:|---:|
| mega_validation | 2105.42 ms | **0.59 ms** | **3568×** |
| memory_policy | 16.61 ms | 2.31 ms | 7.2× |
| native_memory | 7.51 ms | 0.68 ms | 11× |
80,000 条 mega episode 原本需要 **46.8 小时**(`H:\Memory\dynamic_memory_lab\data\router_training_v4` 那次运行了 19.5 小时只写出 91 MB 残缺文件,原因即此)。
修复方式(`_ExcludedPool` + `PoolPositionIndex`):不复制候选池,而是提供一个「排除了少量已知位置」的惰性序列视图,`len()` 与 `__getitem__` 与原列表**逐元素一致**,因此 RNG 抽样与最终数据集**逐字节相同**。
等价性已验证(A/B,同样输入、同样 seed):
```
ORIGINAL train_sha=D9C2774955DF7066 eval_sha=169B026DF2A85C35 93.5s
V2_dpskw train_sha=D9C2774955DF7066 eval_sha=169B026DF2A85C35 42.1s
```
全量数据集 `data/router_training_v5`(144 秒建成,原需约 47 小时):
| 文件 | episodes | 类别 |
|---|---:|---|
| `train.jsonl` (1148 MB) | **87,155** | benchmark 128 / native 512 / policy 6,515 / mega 80,000 |
| `eval.jsonl` (288 MB) | **21,920** | 含 mega 20,000,覆盖全部 10 个类别(每类 2,000) |
`group_overlap = 0`,冲突策略与防泄漏检查与原协议一致。
## 当前阻塞:特征库放不进内存
训练复用 Qwen 冻结特征,所以每条唯一文本都要编码一次:
| 文件 | 唯一文本 | 特征库 (fp16) |
|---|---:|---:|
| v5 train | 1,695,797 | 8.68 GB |
| v5 eval | 425,115 | 2.18 GB |
| 合计 | 2,120,912 | **10.86 GB** |
本机内存 31.2 GB、空闲 12.7 GB,还要容纳解析后的 episode 对象,因此**全量 87k 无法在本机编码**。
可选方案:按类别均衡抽样(约 25–32k episode,特征库 ~5 GB)、先压缩 mega 源、或换更大内存的机器。
**实际落地方式**(已完成):全量 2,124,552 条唯一文本、10.86 GB 特征库写成 mmap 并用完整扫描验证,训练器流式读取,因此「内存放不下」不再是阻塞项。
## v6 结果:准确率与拒答策略大幅超越,成本轴未超越
训练:V2-512 对照与 XL-512 候选均 100,000 步、batch 64、`family_sqrt` 采样、同一冻结特征库与协议。
评测:冻结 v6 eval 21,920 条(10 类别 × 2,000 + policy 1,920),7 个路由器同题同特征。
| 指标 | 生产包 128 维(v3) | V2-512 v3 best | **V2-512 v6 best** | **XL-512 v6 best** |
|---|---:|---:|---:|---:|
| Top-1 正确率 | 41.12% | 80.75% | 95.05% | **95.78%** |
| Recall@3 | 46.73% | 95.02% | **98.84%** | 97.59% |
| Recall@5 | 48.91% | 97.31% | **99.35%** | 98.34% |
| MRR | 47.69% | 88.46% | 97.12% | **97.24%** |
| nDCG@3 | 44.22% | 89.53% | **97.24%** | 96.64% |
| 多跳证据全中(Top-3) | 43.43% | 94.19% | **98.64%** | 96.86% |
| hop 正确率 | 73.23% | 81.59% | 100.00% | 100.00% |
| **未知拒答率** | 0.00% | 50.41% | 99.81% | **100.00%** |
| **已知问题被误拒率** | 0.18% | 11.23% | **0.00%** | **0.00%** |
| 单查询延迟 (GPU) | **0.844 ms** | 0.879 ms | 0.875 ms | 1.216 ms |
| 批量 QPS (batch=256) | **264,813** | 167,235 | 161,941 | 96,747 |
| 地址字节/记录 | **512** | 2048 | 2048 | 2048 |
判定(`router_verdict_v6.md`,逐轴对比最强旧基线):**66 项通过 / 22 项未通过 → 全方位超越:否**。
22 项未通过**全部集中在成本轴**(单查询延迟、单查询/批量 QPS、每条记录地址字节)、2 项平均分数余量、以及 XL-final 的多跳证据全中(78.85% vs 84.90%)。所有准确率与拒答策略轴全部通过,且多数是大幅通过:Top-1 +15.03pp、MRR +8.79pp、hop +18.25pp、未知拒答率 +48.71pp、已知被误拒率 −0.11pp。
### 同数据架构对照:XL 相对 V2 并没有赢
在**完全相同**的数据、特征、采样与步数下(best 检查点):
| 指标 | V2-512 v6 | XL-512 v6 | XL−V2 |
|---|---:|---:|---:|
| Top-1 | 95.05% | 95.78% | **+0.73pp** |
| MRR | 97.12% | 97.24% | +0.12pp |
| 未知拒答率 | 99.81% | 100.00% | +0.19pp |
| Recall@3 | 98.84% | 97.59% | −1.25pp |
| Recall@5 | 99.35% | 98.34% | −1.01pp |
| nDCG@3 | 97.24% | 96.64% | −0.60pp |
| 多跳证据全中 | 98.64% | 96.86% | −1.78pp |
| 单查询延迟 | 0.875 ms | 1.216 ms | **+39% 更慢** |
结论:本次最大的提升来自**数据与标签**(v3 → v6:修正被破坏的拒答/多跳标签、12 倍数据量、10 类别均衡评测),而不是新架构。XL 在 512 维上只换来 Top-1 +0.73pp、MRR +0.12pp,代价是 1.4 倍延迟与 1.67 倍参数,并在召回与多跳证据完整度上略逊于同数据的 V2。若目标是「同尺寸全面超越」,应继续投入数据与训练配方,而不是加大路由器容量。
## 同存储预算的判定:128 维 V2 达到全方位超越
把 512 维路由器与 128 维的生产路由器比「每条记录地址字节」是**不同存储预算的错位比较**。因此在生产几何(128 维 = 512 字节/记录、2,037,774 参数)上重训后重新判定:
| 路由器 | 参数 | 地址字节 | Top-1 | 未知拒答率 | 单查询延迟(中位) | batch256 QPS | 逐候选判定 |
|---|---:|---:|---:|---:|---:|---:|---|
| V2-128 生产(v3) | 2,037,774 | 512 | 41.12% | 0.00% | 1.1558 ms | 199,554 | 基线 |
| **V2-128 v6 best** | 2,037,774 | 512 | **94.62%** | **100.00%** | 1.1679 ms | 196,829 | **22/22 通过 → 是** |
| **V2-128 v6 final** | 2,037,774 | 512 | 94.37% | **100.00%** | 1.1653 ms | 198,026 | **22/22 通过 → 是** |
| XL-128 v6 best | 6,714,639 | 512 | 95.66% | 99.59% | 1.5783 ms | 141,501 | 18/22(吞吐 4 轴)|
| V2-512 v6 best | 4,741,902 | 2048 | 95.05% | 99.81% | 1.1623 ms | 154,085 | 20/22(batch256、地址字节)|
| XL-512 v6 best | 7,898,127 | 2048 | 95.78% | 100.00% | 1.5855 ms | 94,647 | 17/22 |
速度轴以**交替轮流测量**(`bench_router_latency.py`,5 轮,模型轮转以抵消漂移)为准,实测波动带 2.8–5.3%,故速度轴采用 3% 相对容差;质量轴仍为严格容差。此前「V2-128 v6 慢 4.6%」是单次顺序测量的漂移,交替测量下两者差异 **≤1.4%**(单查询 min 甚至更快 0.5%)。
**结论**:V2-128 v6 在参数、地址字节、延迟、吞吐**全部不劣**(差异在测量噪声带内)的前提下,把 Top-1 从 41.12% 提到 94.62%(**+53.49pp**)、未知拒答率从 0.00% 提到 100.00%、hop 从 73.23% 提到 100.00% —— 这是相对已交付生产路由器的**同预算全方位超越**。
**但不是新架构的胜利**:XL-128/XL-512 准确率最高(Top-1 95.66%/95.78%)却真实地慢 26–52%(3.3–3.9 倍参数)。本次提升来自数据与标签,而非路由器容量。
## 常用命令
```powershell
$py = 'C:\Users\Administrator\miniconda3\envs\LLM\python.exe'
$env:PYTHONPATH = 'H:\Memory'
Set-Location 'H:\Memory\V2_dpskw'
# 单元测试
& $py -m unittest discover -s tests
# 特征缓存校验(v3 数据)
& $py -m V2_dpskw.check_router_cache
# 构建数据集(已修复拒答/多跳标签;全量约 2 分钟)
& $py -m V2_dpskw.prepare_memory_router_dataset --output-dir data/router_training_v6 `
--train-source <abs>\benchmark_train.jsonl ... --candidate-count 32 --conflict-aware --seed 20260909
# 独立审计:类别完整性 + 严格泄漏
& $py -m V2_dpskw.audit_router_dataset `
--train-file data/router_training_v6/train.jsonl --eval-file data/router_training_v6/eval.jsonl `
--output router_dataset_audit_v6.json
# 流式多线程特征编码(212 万条唯一文本 → mmap 特征库,放 NVMe)
& $py -m V2_dpskw.stream_feature_bank `
--train-file data/router_training_v6/train.jsonl --eval-file data/router_training_v6/eval.jsonl `
--model-path qwen3_5_4b_natural_memory_v2 --output-dir H:\Memory\nm_cache\nm_router_v6\feature_cache `
--tokenizer-threads 8 --max-batch 192 --token-budget 12288 --gpu-memory-gb 10
# 训练(V2 对照 + XL 候选,同数据同协议)
pwsh -File .\run_router_v6.ps1 -Configs v2_512_v6,xl512_v6
# 全类别百分比评分卡
& $py -m V2_dpskw.eval_router_v5 --feature-cache H:\Memory\nm_cache\nm_router_v6\feature_cache `
--train-file data/router_training_v6/train.jsonl --eval-file data/router_training_v6/eval.jsonl `
--run "XL-512 v6=checkpoints\router_v6_xl512\router_best.pt" `
--output router_scorecard_v6.json --markdown router_scorecard_v6.md
```
## 数据管线(流式 + 多线程)实测
| 阶段 | 结果 |
|---|---|
| scan:去重并落盘唯一文本 | 2,124,552 条 / **15.1 秒**(只保留 `sha1→行号` 字典,不把 episode 解析进内存) |
| tokenize:8 线程池(HF fast tokenizer 释放 GIL) | 2,124,552 条 / **41.8 秒**(约 57,000 texts/s) |
| encode:按精确 token 长度分组 + token 预算限批 | 约 283 texts/s @32 token(batch 192),GPU 100% |
| 特征库 | `features.f16.npy` mmap,10.86 GB,训练时按需读页 |
| 断点续跑 | `--resume` + `progress.json`:记录已完成的长度排序行数,被中断后不重编已完成部分 |
**表示保真度**:与旧冻结缓存(v3)共有文本对比,编码结果 **余弦最低 0.999935 / 平均 0.999983**。因此旧路由器可以直接在新特征库上评分,而不是被换到一套不同表示上再比较。
### 一个被测量推翻的「显然优化」
`QwenDynamicMemoryModel._encode_model_key` 调用完整条件生成模型,而 `logits_to_keep` 默认 0(= 全部 token),于是每次前向都算出 `[B, L, 248320]` 的 logits:对 192×32 的批是 **3.05 GB 中间张量 + 约 8.5 TFLOP**,全部丢弃。看起来显然该改成 `logits_to_keep=1`。实测(交替重复 7 轮取最小值;两条路径特征一致性 cosine **0.9999996**):
| 编码路径 | 192×32 批耗时 | 吞吐 | 显存峰值 |
|---|---:|---:|---:|
| 全量 logits(默认,保留) | **679 ms** | **283 texts/s** | 5.22 GiB |
| `logits_to_keep=1` | 1050 ms | 183 texts/s | 4.13 GiB |
| batch 512(全量 logits) | — | 287 texts/s | — |
原因:`slice(-1, None)` 产生**非连续视图**,bitsandbytes 的 4-bit matmul 在这个布局上掉进慢路径(多出约 500 ms),而全量 logits 是规整的连续 GEMM,本身只约 130 ms。结论:**不要启用 `--skip-lm-head`**(默认已关,代码与结论一并保留以便复核)。batch 从 192 提到 512 也无收益,说明该阶段已接近这台 GPU 的算力上限(约 52 TFLOPS,约为 5070 bf16 峰值的 84%)。
## v5 → v6:修正被破坏的拒答与多跳标签
`_add_mega_row` 有两个由 GPT 时代沿用下来的缺陷,正好废掉了本次评测最需要的两个轴:
1. `unknown_abstention` / `forget_correction` 的源数据带 `metadata.answerable = false`,其 `acceptable` 装的是**拒答话术**("不知道"/"没有记录")。旧代码用 `answerable = bool(acceptable)` 判成可回答,又没有事实文本包含这些话术,于是走进兜底分支 `positive_ids = ids[:]`,**把全部事实标成正例**——2000 条拒答样本全部变成可回答样本。
2. `multi_hop` 的真实跳数在 `metadata.hop_count = 2`,旧代码用 `len(positive_ids)` 计算,且只标注了含最终答案的那条事实,**中间推理链事实没有标成证据**。
修正:尊重 `metadata.answerable`;用 `metadata.hop_count` 作为 hop 标签;沿「实体→值」链扩展证据(有界,最多 3 跳)。修正前后对比:
| 指标 | v5(有 bug) | **v6(已修正)** |
|---|---:|---:|
| train 未知样本 | 319 | **16,319** |
| eval 未知样本 | 106 | **4,106** |
| `unknown_abstention` 可回答/未知 | 2000 / **0** | **0 / 2000** |
| `forget_correction` 可回答/未知 | 2000 / **0** | **0 / 2000** |
| `multi_hop` 平均正例数 / hop | 1.00 / **1** | **2.00 / 2** |
数据集重建耗时 **114.9 秒**(未修复前同一构建需要约 47 小时)。
### v6 独立审计结果
| 检查 | 结果 |
|---|---|
| `group_id` 重叠 | **0** |
| 查询文本重叠 | 80 / 4,296 = **1.86%** |
| **同查询且共享同一正例证据** | **0**(80 条均为模板化问句配不同事实,无泄漏) |
| 候选文本重叠 | 3 / 421,473 = **0.0007%** |
| 类别 | episodes | 可回答 | 未知 | 平均正例 | hop |
|---|---:|---:|---:|---:|---|
| unknown_abstention | 2000 | 0 | **2000** | 0.00 | 0 |
| forget_correction | 2000 | 0 | **2000** | 0.00 | 0 |
| multi_hop | 2000 | 2000 | 0 | **2.00** | **2** |
| 其余 8 类 | 各 2000 | 2000 | 0 | 1.00–2.00 | 1–2 |
## NM2 跨架构泛化(实测矩阵)
原集成的记忆手术只适配 Qwen3.5 一族的解码层。`probe_nm2_portability.py` 对 14 个架构真机施加手术,
并做**最强保能力检验**:记忆读写关闭时,包装后的模型必须与原生模型的 logits **逐位相同**。
修复前:只有 `position_embeddings` 在第 2 位置的架构(gemma2 / gemma3 / mixtral)能通过,其余全部
`TypeError: ... got multiple values for argument 'attention_mask'` 或找不到层容器。
修复后(**零补丁、全部逐位相同**):
| 架构 | 层容器 | 第 2 位置参数 | 恒等性 |
|---|---|---|---|
| llama / mistral / qwen2 / qwen3 / starcoder2 / cohere / phi | `model.layers` | `attention_mask` | identical |
| olmoe / granitemoe | `model.layers` | `attention_mask` | identical |
| gemma2 / gemma3 / mixtral | `model.layers` | `position_embeddings` | identical |
| opt / bart | `model.decoder.layers` | — | identical |
为达到这一点只改了 4 处,**记忆核心与路由器一行未改**(它们只依赖 `hidden_size`):
| 改动 | 解决的假设 |
|---|---|
| `resolve_text_config()` | 纯文本模型没有 `config.text_config` |
| `resolve_decoder_layers()` | 层容器固定为 `model.language_model.layers`(现支持 8 条候选路径) |
| `MemoryLayerAdapter` 约定检测 + **按声明参数过滤** | `position_embeddings` 被按位置硬传;参数**名称**也不同(Ling 用 `past_key_value` 单数,且未必有 `**kwargs`) |
| `load_qwen_base()` 兜底 `AutoModelForCausalLM` | 只有 image-text-to-text 加载器 |
对 **Qwen3.5 生产路径零行为变化**(已证明):真实 `Qwen3_5DecoderLayer.forward` 的绑定签名里
`position_embeddings` 位于索引 1 → adapter 走 `positional` 分支 → 与修复前完全相同的调用形式;
同族的 gemma2/gemma3/mixtral 在修复后仍为 `identical`,52 项单元测试保持全绿。
### 第二类兼容性:远程代码 vs transformers 版本
`inclusionAI/Ling-3.0-tiny`(`bailing_hybrid` / `BailingMoeV3ForCausalLM`,hidden 1536 / 24 层 /
128 专家 top-8 / 活跃约 1.2B / MLA + KimiDeltaAttention 混合)**架构上可接**(骨架以关键字传
`position_embeddings`,正好落在新分支;`past_key_value` 单数命名由参数过滤处理),但它自带的
`modeling_bailing_moe_v3.py` 是按**旧版 transformers API** 写的:
- `from transformers.utils.import_utils import is_torch_fx_available` → 5.9 已移除该符号;
- `config.rope_scaling["factor"]` → 5.9 会把 `rope_scaling` 规范化掉,运行期 KeyError。
也就是说「嵌进任意模型」有两层前提:**架构层(已解决,14/14)** 与 **栈层(需兼容 shim 或独立 venv)**。