13. 交接:任务、猜想、教训(压缩上下文前的存档)

★ 5.1k 字 · 源文件 PROJECT.md 第 2745 行起 · ruler optim read

#13.1 任务状态

#任务状态证据/位置
1视觉前端可读性(英文/汉字 → 字号×下采样)✅ 完成§12.1 §12.2
2字符集内在可分辨性门禁✅ 完成§12.10 diag_charset_gate.py
3认字环境 read_screen(屏幕给字→打拼音)✅ 完成 42 项自检§12.13
4视觉预热 + 形变增广✅ 完成§12.9 留出字体 90.5%
5exp31 抄写任务(老师 β:1→0 + RL)✅ 能跑§12.16
6判据 1/2/3/6(正确率/认字/空白屏/emit 方差)✅ 通过空白屏 0.0% 是铁证
7判据 5(撤掉老师后 ≥50%)⚠️ 44.7%,未过但已 3.2× 改善§13.2
8判据 4(留出字体,策略端)⚠️ 弱(8~30%),编码器端已 90.5%
9★★ 同声旁结构迁移⚠️ A/B = 7.8× 仍然成立,但机制未识别(见 §12.19,原判读已改)§12.18 §12.19
10形近异音第三控制(C′)⚠️ 跑完了,但发现它没有分辨力(满分线 0%)§12.19
11D 内驱力 + E DAgger 解耦✅ 实现并跑完,3.2× 改善§13.2
12接地线(指令→执行)⬜ 未开始§13.4
13工作台(全局广播)⬜ 未开始§13.4
14记忆地平线✅ 完成,假设被否掉§12.17
15★ 留出集零学习基线(查最近邻满分线)✅ 完成,救了两个错结论§12.19 diag_nn_baseline.py
16★ 留出集逐字预测串倒出✅ 完成(C′ 上 41.7% 输出孪生读音)§12.19 diag_transfer_strings.py
172×2 混淆矩阵(视觉×读音)⚠️ 候选池塌了,未建成(记下原因)§12.19

#13.2 ★★ D+E 的最终读数(results/logs/exp31_DE.log)

之前(无 D/E)DE(有 D/E)变化
β=0 时整串正确率4.2%13.5%3.2×
β=0 时认字9.4%36.5%3.9×
emit 均值0.10~0.200.85门从关死变常开
门(该开/该关)0.10 / 0.270.87 / 0.94⚠️ 见下

判据 5 的分母是 30.2%(纯模仿),13.5% / 30.2% = 44.7% —— 未过 50%, 但比之前的 4.2% 好了 3.2 倍。

★ 两个新发现:

  1. D(内驱力)确实修好了"该动不动":emit 从 0.10 → 0.85。
  2. ⚠️ 但门现在"常开",矫正过头了:该关时也是 0.94(应 <0.5)。 → DRIVE_EMIT=1.0 偏大,或者缺少"说完该停"的约束。下一步要调。

#13.3 猜想清单(验证过的 / 被否掉的 / 待验证的)

猜想结论证据
用 raw 单通道就能认字❌ 否2.1%(=瞎猜);edge 层才是关键,四通道 84.6%
降低下采样对英文有用❌ 否12×12 反而 85.7%
汉字和英文对分辨率的需求一样❌ 否16px 汉字 4 次下采样 0.5%,3 次 99.8%
4 次下采样 = 信息上限("6×6 不可分")❌ 否,是我错了加形变增广后 c24/d4 从 0.4% → 91.6%
多字体训练能救跨字体泛化✅ 是30.9% → 81.8%;加增广 → 90.5%
纯 RL 能从零认字❌ 否冻结特征线性探针只有 2~19%
目标字一直显示 → 记忆不必要 → 长不出记忆❌ 否(我错了)字消失 20 刻准确率一点不掉(§12.17)
真瓶颈是"每字 65%、误差乘性"✅ 是31.2% ≈ 0.65³,0.65⁶≈7.5%
它只是查表器⚠️ 分不开(原判"否"已撤回)A 的"查最近邻满分线"是 93.8% → 查表也能考这么高(§12.19)
A 的机制是"读声旁"⚠️ 未识别C′ 满分线 0% → 它没有分辨力;实测 2.0% 在噪声里(§12.19)
C′ 能分辨"视觉最近邻 vs 更细结构"❌ 否(是我的设计错)C′ 的最近邻读音全不同音 → 两个假说都预测"低"(§12.19)
给没教过的字,它会输出孪生字的读音✅ 是C′ 上 41.7%,逐字预测串实测(§12.19)
放手后崩 = RL 破坏模仿(不是池变难)✅ 是C(β=1) 30.2% vs 放手 4.2%
内驱力能修 emit 门✅ 是0.10 → 0.85
DAgger 解耦能修放手后崩⚠️ 部分3.2× 改善但仍未过 50%
少样本/结构迁移 = 0⚠️ 降级为"未定"原判"否"依赖 A 的判读,而 A 的机制未识别(§12.19)

#13.4 下一步(按优先级)

  1. 读 C′ 形近异音(exp31_Cnear.log)→ 钉死"视觉最近邻 vs 更细结构"
  2. 调 DRIVE_EMIT:门现在常开(该关 0.94),需要"说完该停"的约束
  3. 继续压低判据 5 的差距(44.7% → 50%+):
  4. 候选:自我纠正(Backspace)让误差不再乘性累积
  5. 候选:把 drive 作为输入喂给策略(当前只调制输出,策略"感不到"它)
  6. 候选:更长训练 / 更好编码器
  7. 接地线(指令→执行):让字第一次有后果
  8. 工作台:只在 1–4 之后,判据 = 让"保持"与"迁移"同时改善

#13.5 ★★ 经验与教训(本阶段新增,跨任务通用)

A. 关于"假设"

  1. ★★ 校准锚点必须最先做。§12.17 里 N=0 那个对照(31.2% vs 训练时 30.2%) 救了整个判读:没有它,我会把 31.2% 当成"记忆掉了"的证据去改循环核,方向全错。

  2. ★★ 一个假设被否掉,和它被证实一样有价值 —— 前提是判据能失败。 本阶段有三个我的假设被实测否掉(记忆、下采样上限、任务漏洞),每一个都 把方向纠正到了真正的地方。

  3. ★ 报一个数字时必须同时报它的地板与天花板(§11.15)。"31.2%" 本身 无法解释;"地板 0.4%(随机)、天花板 100%(训练集)"才让它有意义。

B. 关于"对照设计"

  1. ★★ 对照集必须与实验组在「哪些变量被控制住」上对称。 形近异音对照我犯了两次同一个错(v2 随机留出的读音不在训练集; C′ 第一次构造的 5 个字读音也不在训练集)—— 两次都让对照变得不公平。 → 建对照时先列表:哪些变量必须相同?逐个核对。

  2. ★ 两个条件只差一个变量,否则结论无法归因。 v1(训练集 102 vs 3706)就是典型错误。

  3. ★ 一次训练可以同时评估多个留出集(同一个策略)—— 省一半算力, 而且天然保证两个条件在同一条学习轨迹上。

C. 关于"静默失效"(本阶段又抓了 5 个,见 §12.16)

  1. ★★ 不报错、不 NaN、只表现为"怎么练都不动"的 bug 是最贵的。 本阶段五个:grp.obs 没写回(策略永远看黑屏)、评估器绕过了它(第二现场)、 采样与更新不是同一个策略(h 状态没携带)、第 0 刻老师照黑屏示范、 emit 监督 14:2 类别不平衡。

  2. ★★ 分层可量是定位它们的唯一办法:像素差 → Crystal 差 → 编码器差 → 隐状态差 → logits 差。断在哪层一目了然。

  3. ★★ sup 卡在 ln(候选数) 就是"完全没用输入"的指纹 (那是无信息时的正确最优解)。

  4. ★★ "训练好、评估差"先怀疑评估器也看错了输入,别急着说过拟合。
  5. ★ 指标要挑不依赖其它子系统的:first_ok 依赖 emit 门,门塌恒为 0; key_first_ok(直接比 argmax 键)才是干净读数。

  6. ★ 恒真检查 = 没在测东西。本阶段抓到两次:gamma 参数没被用上, 于是"γ<1 时原地不动白得奖励"那条反向验证永远通过; 以及 gamma 版塑形根本没实现。

D. 关于"工具与环境"(血泪)

  1. ★★★ 绝不用 shell 的字符串 cmdlet 往返读写源码(Get-Content/Set-Content)。 一次误操作把 screen_agent.py 全毁,恢复花了十几轮(§12.12)。

  2. ★★ 这个项目现在有 git 了(c5e638d 起)。每完成一件事就提交。
  3. ★ .pyc 当真相基准前先核对它的源文件 mtime/size;它可能比源码旧。 而且 pyc 里的测试自身可能有 bug(本次那个漏了 [1] 的断言)。

  4. ★ PowerShell 会吞掉空字符串参数(--out ""),用 none 哨兵。
  5. ★ 中文字符串里绝不能嵌 ASCII 双引号(会截断 Python 字符串)。 本阶段我犯了 4 次。一律用 「」。

  6. ★ 改文件用 edit 工具或 Python + 显式 encoding="utf-8"; 批量改动写成可复现的补丁脚本(tools/patch_*.py),别手改。

  7. ★ 补丁脚本自己也可能有语法错 → 跑完必须检查它的退出码, 否则会看到"语法 OK / 自检通过"(那验的是没被改过的文件)。

  8. ★ 无头截图加 --virtual-time-budget 会得到假阴性(时钟飞速推进 → 停滞检测立刻触发 → 全黑 25KB)。去掉它才是 295KB 的正确截图。

E. 关于"监控页"

  1. ★ 监控页必须按数据新鲜度停画,且"从未收到数据"要算作陈旧 (初值必须是 Date.now() 而不是 0,否则最该停的场景反而永不停止)。

  2. ★ 同一个页面的不同部分不能有两条更新路径:本次 drawScreen 在轮询回调、 render2D 在渲染循环 → 停画时出现"显示器有内容、面板全是 —"的矛盾画面。


← 返回《PROJECT.md》目录