Files
natural-memory-nm21/REALISTIC_V2_FINDINGS.md
T
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

132 lines
7.9 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.
# 真实工作泛化能力:用真实形态数据重训路由器的完整结论
> ## ⚠️ 本文的端到端数字已被证伪,读之前先看 `MEASUREMENT_FIXES.md`
>
> 2026-09-15 复核发现,本文第 3 节起引用的端到端正确率(**72.57% / 63.50% / 64.00%**)
> 由两个评分器缺陷造成,**不是模型的真实水平**:
>
> 1. 评分器对空白敏感 —— 期望锚点 `值班人-5259`、模型答 `值班人 -5259`(多一个空格)判错,
> **19 条多跳用例全部因此被判错**(多跳真实是 **100.00%**,不是 24.00%);
> 2. 拒答检测只认固定关键词,漏掉模型实际使用的措辞(「未包含相关信息,无法回答」),
> 把 **52.00%** 的未知拒答率报成 **0.00%**。
>
> 修正后,同一份权重、同一份语料的真实水平是
> **总体 82.00% / 可回答 86.29% / 未知拒答 52.00% / 已知被误拒 2.29%**。
>
> **本文仍然成立的结论**:路由器在未见属性族上确实能练出泛化能力(Top-1 49.14% → 94.86%),
> 而且该增益不传递到端到端答案 —— 这一条与空白缺陷无关,仍然有效。
> **本文不再成立的结论**:所有以 72.57%/63.50% 为基线的「差值」与「瓶颈排序」,
> 包括「多跳是瓶颈」和「未知拒答率 4.00%/0.00%」。
## 1. 做了什么
| 步骤 | 结果 |
|---|---|
| 真实形态**训练集** | `data/realistic_v2/train.jsonl`,3200 条 / 8 类别(多实体、别名、更新冲突、多跳、近邻干扰、噪声、长事实、未知) |
| 真实形态**评测集** | `data/realistic_v2/eval.jsonl`,200 条 / 8 类别 |
| **属性族不相交** | 36 个属性族按字典序三分:训练 24 / 评测 12,**overlap = []**(评测用的属性名与句式训练时从未出现) |
| 特征编码 | 10,034 文本 / 60 秒(流式+8 线程) |
| 训练 | V2-128(drop-in 几何),从现有交付件出发,12,000 步 / 1e-4 / need+hop 正常权重 |
## 2. 路由器级:真实泛化能力被练出来了(强结果)
在**未见属性族**上:
| 检查点 | Top-1 | Recall@3 | MRR | need 特异度 | hop |
|---|---:|---:|---:|---:|---:|
| step 0(现有交付件) | **49.14%** | 78.29% | 66.87% | 0.00 | 0.68 |
| step 1000 | **94.86%** | **100.00%** | 96.95% | 0.20 | 0.785 |
| step 12000 | 93.71% | 100.00% | 96.57% | 0.32 | 0.765 |
**Top-1 +45.72pp、Recall@3 到 100.00%**,而且是在训练时从未见过的属性措辞上 —— 这一个数字本身说明:
**"路由器缺的是真实形态训练数据"这个判断是对的。**
## 3. 但端到端**一动不动**
同一条 200 条评测集(属性不相交),同一份运行时,只换路由器:
| | 总体 | 可回答(175) | 未知拒答(25) | 已知被误拒 |
|---|---:|---:|---:|---:|
| 原版 NM2 | 64.00% | **72.57%** | 4.00% | 2.86% |
| 真实语料重训的路由器 | 63.50% | **72.57%** | 0.00% | 1.71% |
**Top-1 提升 45.72pp → 端到端可回答正确率变化 0.00pp。** 逐类别:
| 类别 | 原版 NM2 | 重训 | 差值 |
|---|---:|---:|---:|
| alias_paraphrase | 96.00% | **100.00%** | +4.00pp |
| long_fact | 64.00% | **72.00%** | +8.00pp |
| multi_entity | 96.00% | 96.00% | 0 |
| **multi_hop** | **48.00%** | **24.00%** | **−24.00pp** |
| near_miss | 88.00% | **100.00%** | +12.00pp |
| noise_context | 68.00% | **72.00%** | +4.00pp |
| unknown_attribute | 4.00% | 0.00% | −4.00pp |
| update_conflict | 48.00% | 44.00% | −4.00pp |
| **合计** | **64.00%** | **63.50%** | **−0.50pp** |
8 类里 4 类改善(+4~+12pp)、3 类退步(−4~−24pp),**净 −0.50pp**。每类只有 25 条、1 例 = 4.00pp,
所以 ±8pp 都在噪声量级;**唯一可能真实的是 multi_hop 的 −24.00pp(6 个用例)**。
## 4. 逐个被证伪的假设(这一步比结论更重要)
| 假设 | 测量结果 | 判定 |
|---|---|---|
| 记录没写进库 | 写入存活 **92.00%**、**目标事实存活 100.00%(35/35)** | **否** |
| 排序被先验压住 | 先验置 0 + 纯路由器排序:可回答 **77.14%** vs 现状 76.57%(1 个用例) | **否** |
| 先验权重要调 | 剂量曲线 1.00/0.50/0.25/0.00 = 65.00%/47.50%/42.50%/40.00%(模板域) | **否,调小更差** |
| 注入记录太多,模型被混淆 | top_k = 4/2/1 → 可回答 72.57% / 66.29% / **52.57%** | **否,减少更差** |
| 路由器分数没被用上 | 让路由器全权排序只动 1 个用例(见上) | **部分成立但不是主因** |
**结论:在当前架构下,路由器级排序能力不是端到端准确率的约束条件。** 这一点现在有两条独立证据链
(模板域的先验/混合实验 + 真实域的"Top-1 +45.72pp 而端到端 0.00pp")。
## 5. 残差到底落在哪(可辩护的逐类分类,200 条)
| 类别 | 用例 | 正确 | 答了但不对 |
|---|---:|---:|---:|
| alias_paraphrase | 25 | **25(100.00%)** | 0 |
| near_miss | 25 | **25(100.00%)** | 0 |
| multi_entity | 25 | 24 | 1 |
| long_fact | 25 | 18 | 7 |
| noise_context | 25 | 18 | 6 |
| **update_conflict** | 25 | **11** | **13** |
| **multi_hop** | 25 | **6** | **19** |
| **unknown_attribute** | 25 | **0** | **25** |
| 合计 | 200 | 127(63.50%) | 71(35.50%)(拒答 2、空回复 0) |
**残差集中在这三处,而不是"全面不准":**
1. **未知属性 0/25** —— 全部照答。覆盖机制是固定 24 属性闭集,在真实实体前缀属性上自动旁路(设计如此)。
2. **多跳 6/25(24.00%)** —— 链条没被跟上,而且比原版更差,说明重训在这类上过拟合了训练里的链条形态。
3. **更新冲突 11/25(44.00%)** —— 模型答出**被取代的旧值**。
第 3 点值得单独查:语料里我把"旧值/新值"写成**两个不同句式**(`frames[0]` / `frames[-1]`)。
若新值那句解析不出同一属性,`write()` 的同属性取代就不会触发,**旧记录仍是 active** → 两条都进证据。
这既是我的语料细节,**也对应真实产品缺陷:用户换一种说法更新时,旧值可能不被取代**。
## 6. 结论
1. **"真实形态训练数据"是路由器泛化的正确杠杆**:+45.72pp Top-1(未见过属性族),证据干净。
2. **但它不是端到端体验的杠杆**:端到端净变化 −0.50pp,8 类里有升有降。
3. **端到端剩下的 27.43% 失败集中在三处**(未知 25/25 错、多跳 19/25 错、更新冲突 13/25 错),
全部属于**运行时逻辑**(覆盖判断、多跳执行、冲突取代),不在路由器权重里。
4. 因此下一步该做的不是继续训路由器,而是:
* **冲突取代**:让"换一种说法的更新"也能取代旧记录(结构化键 + 语义匹配双条件),目标 更新冲突 ≥ 80%;
* **多跳执行**:核对 read 路径是否真的按 hop=2 串联两条记录,目标 多跳 ≥ 70%;
* **动态属性词表**:覆盖判断要能处理实体前缀属性,目标 未知拒答 ≥ 80%。
路线图建议:**多跳与冲突取代优先**(各约 20 条错例,且都是逻辑问题、不需要新数据)。
## 7. 产物
| 文件 | 内容 |
|---|---|
| `make_realistic_memory_data.py` | 训练/评测语料生成器(36 属性族、按族三分切分、含构造 bug 修复注释) |
| `data/realistic_v2/{train,eval}.jsonl` + `.manifest.json` | 3200 / 200 条,含 sha256 与**不相交证明**(`overlap: []`) |
| `H:\Memory\nm_cache\nm_realistic_v2\feature_cache` | 10,034 行特征库(流式编码,60 秒) |
| `checkpoints/router_realistic_v2_128/` | 真实语料重训的路由器(V2-128,drop-in 几何) |
| `rv2_{orig,new}_e2e.{json,md}` | 第 3 节对照(原版 vs 重训) |
| `rv2_topk{1,2}_e2e.{json,md}` | 第 4 节注入纪律实验 |
| `rv2_order_{current,routeronly}.{json,md}` | 第 4 节排序实验 |
| `write_survival.json` | 第 4 节写入存活诊断 |
| `diagnose_write_survival.py` | 写入存活诊断脚本 |
| `REALISTIC_CORPUS_FINDINGS.md` | 第一版语料(含我 3 个构造 bug 的复盘) |