# 自对弈强化学习的潜伏威胁盲区——value 标签前缀重分配

自对弈强化学习的潜伏威胁盲区——value 标签前缀重分配

适用范围声明:本文讨论的结论适用于MCTS + 价值网络架构的 棋类自对弈强化学习(五子棋、围棋等)。纯传统搜索引擎(如带专门 VCF/VCT 算杀模块的 NNUE 系)有显式算杀器,不存在本文讨论的 标签机制问题;非完美信息博弈同样不适用。

与 sims 数无关:本文结论覆盖所有搜索预算配置。修复前的 对抗测试中,一条累计约 2 万轮、对弈仅用 200 sims 的成熟参考管线 能稳定防杀,而一条已训练 6000 轮、对弈用 3600 sims 的新管线却 反复失守 ——18 倍的搜索预算差距说明:这项能力不生活在搜索里,它生活在 训练标签里。高 sims 只是暂时掩盖 value 头的缺陷,永远替代不了它。

1. 核心论点

自对弈管线的 policy 标签重构(prior + Q 层次重分配)只做一半是不够的——value 标签必须从同一个分层循环里产出。

这不是"效果更好"的优化项,是潜伏威胁嗅觉的唯一来源:value 标签 若继续使用访问量加权的树内 Q,潜伏杀点的信号会被访问占比稀释、 被平均化抹平,value 头永远学不会"看到对手行棋节点就预感危险", 模型对强算杀对手的失守就是结构性的、训练再久也补不回来。

这个约束与轮次数和 sims 数都无关:标签信号被稀释 10 倍以上, 轮次堆上去只能学出弱化版;搜索预算堆上去根本不会往杀点分配模拟。

2. 现象:防杀失灵的两个不对称

实测现象(新管线对强算杀 AI 的对抗测试,3600 sims):

▼
text
复制代码
对手的杀点落子之前: 模型自己下了一步 → 树里完全搜不到对手存在 10~20 步杀 (3600 sims 的搜索预算如同不存在) 对手的杀点落子之后: 模型立刻判断对面胜率 99% (一次网络前向就完成了)

两个不对称背后是两种截然不同的认知任务:

  • 下之前需要的是前瞻推理——沿一条正确路径连续展开约 20 层, 每层覆盖对手所有应点,暴力搜索空间是天文数字。MCTS 能算深杀 只有一个前提:value 头在杀路的早期节点上就给出强烈的负值信号, 引导搜索沿这条路分配模拟。
  • 下之后只是模式识别——威胁从"逻辑上存在"变成"棋盘上的 具体子力结构"(冲四、双威胁点已成型),CNN 感受野内一眼可见, 且自对弈数据里"威胁成型 → 最终获胜"的样本极多。

于是链条在源头断了,两个信号源同时哑火:

▼
text
复制代码
信号源 1:policy 先验 杀点在棋盘上只是"一个普通空点" → policy 先验给得很低 → 3600 sims 按先验分配 → 该点只分到几次甚至 0 次模拟 → 搜索预算无效堆砌 信号源 2:value 头 "对手已有隐形杀路、但威胁点还没落子"的局面 与普通中盘局面在棋盘特征上差别很小 → 单次前向分辨不出 → 搜索在正确路径的入口处得不到引导

最硬的证据是搜索预算的对照:参考管线对弈只用 200 sims 就能 防杀,新管线 3600 sims 反而防不住。这说明参考管线的能力不来自 搜索深度,只能来自 value 头在子节点展开时的单次前向"嗅杀"—— 而嗅杀是训练标签教出来的。

3. 两种 value 标签:访问量加权 Q vs 前缀 max 重分配

3.1 访问量加权树 Q(出问题的那条路)

对每个被收集的内部节点,value 标签直接取树内统计:

▼
text
复制代码
value(N) = Σ_c visits_c × (-val_c) / Σ_c visits_c

即所有已访问子节点价值的访问量加权平均(取反为父节点视角)。

3.2 前缀 max 重分配(正确的路)

把节点的网络干净先验当作"初始概率预算",在已访问子节点间按 prior 降序逐层差分切分,每层增量 (prior[i] − prior[i+1])×(i+1) 分配给层内(前 i+1 个走法中)Q 最高者,同时把该层的前缀 max 价值 以同一权重累入 value 标签:

▼
text
复制代码
for i in 已访问子节点(按 prior 降序): cnt_i = prior[i] − prior[i+1] # 该层预算(末层保留全部) cur_i = max(前 i+1 个子节点取反 Q) # 层内最优(前缀 max) policy[最优者] += cnt_i × (i+1) # policy 标签 value += cur_i × cnt_i × (i+1) # value 标签(同源权重) value /= Σ已访问 prior # 归一化

3.3 核心差异:极端信号是"平均掉"还是"全额保留"

一个手算例子(30 sims 量级的树,父节点为对手行棋局面):

▼
text
复制代码
子节点 A:prior=0.7(正常回应),Q=+0.2(父视角),访问 7 次 子节点 B:prior=0.2(潜伏杀点),Q=+0.99(父视角),访问 3 次 访问量加权 Q(旧标签): (7×0.2 + 3×0.99) / 10 = 0.437 → 杀点信号被访问占比(0.3)稀释,再被高 prior 的平庸回应平均掉 前缀 max 重分配(新标签): i=0 层:cnt=0.5,前缀{A},cur=0.2,权重 0.5×1 i=1 层:cnt=0.2,前缀{A,B},cur=0.99,权重 0.2×2 value = (0.2×0.5 + 0.99×0.4) / 0.9 ≈ 0.551 → 杀点权重 0.4/0.9 ≈ 0.44,远超其 0.3 的访问占比, 且"max"结构保证极端值不被任何平庸回应平均掉

更尖锐的对比:若杀点只被访问 1 次(30 sims 树里的常态), 访问量加权 Q 里它的权重是 1/30 ≈ 3%,信号基本消失; 前缀 max 里只要它 prior 靠前,包含它的层级的 cur 就全额 等于它的极端价值,权重由 prior 层级决定而非访问次数决定。

这就是"只有半套"的代价:policy 侧做了层次重分配,value 侧仍是 旧 Q——同一份自对弈数据,policy 标签在教"杀点很危险", value 标签却在说"这局面还行"。

4. 为什么自对弈数据本身补不上这块

一个自然的反驳:轮次够多、数据够多,Q 标签里那点稀释信号 总能学出来吧?问题在于自对弈闭环会系统性地饥饿这类样本:

▼
text
复制代码
30 sims 的自对弈里,双方都看不见长杀 ↓ 潜伏威胁节点上的树内 Q 没有信号(谁都不往那搜) ↓ 对局在长杀真正成型之前,就以短杀或意外结束 ↓ "该我走、对手已有隐形杀路、且被正确标注为必败" 的样本在数据里极其稀疏 ↓ value 头对潜伏威胁的学习信号:稀疏样本 × 稀释权重 ↓ 闭环正反馈:模型看不见 → 不生成相关局面 → 更看不见

这与"物化 D4"的闭环偏置是同构的:数据生成端缺失的东西, 训练端的技巧补不齐。前缀 max 重分配之所以有效,恰恰因为它 不需要额外数据——它把树里已经存在但被埋没的信息 (杀点被访问过哪怕一次的极端价值)以正确的权重暴露给训练。

5. 数学保证:telescoping 与凸组合

value 标签的归一化分母不需要单独计算。对任意按 prior 降序排列的 已访问子节点序列,层次差分权重满足恒等式:

▼
text
复制代码
Σ_i cnt_i × (i+1) = Σ_i (prior[i] − prior[i+1]) × (i+1) + prior[n−1] × n = Σ_j prior[j] (telescoping,j 取所有已访问子节点) = sum_used

因此 value = Σ cur_i × w_i / sum_used 中权重和恰为 1,标签是 子节点取反 Q 的凸组合,恒在 [−1, 1]——与网络 tanh 输出域 严格对齐,训练目标永远不会越界。

实现上还有一个与参考实现的已知浮点差异:参考实现在每层先除 sum_used 再累加,本文实现先累加最后除一次——数学等价(分配律), 舍入路径不同,数值对照在 1e-12 相对容差内一致(见 §7)。

6. 防杀能力的完整闭环

标签修好之后,整条链路是这样的:

▼
text
复制代码
训练时(自对弈,30 sims): 对手行棋节点 N 被收集,杀点作为 N 的高 prior 子节点 被访问过至少一次 → 前缀 max 把杀点的极端价值全额写入 N 的 value 标签 → value 头学会条件反射: "对手行棋 + 危险点在其 policy 高排位" → 直接输出悲观值 对弈时(任何 sims 数): 我方每个候选走法展开后的子节点(对手行棋局面) 被 value 头一次前向评估 → 留杀走法的子节点得分极差 → select 打差 → 自动避开 → 根本不需要搜索"看见"那条 20 步杀路 对手仍硬下出杀点时: 威胁成型 → 模式识别(本就是 value 头的强项)→ 立刻 99%

注意闭环的两端都不需要显式算杀:搜索不用算出整条杀路, value 头也不用——它只需要把"危险点已出现在对手的候选集里" 翻译成一个悲观数。这正是 CNN 单次前向能做到的事。

7. 实现状态与正确性验证

需要先划清一条界线:§2 的现象观察来自修复前的对抗测试(问题 诊断依据),修复本身的效果尚未经过训练验证——§6 的防杀闭环是 基于标签机制的推演,最终是否兑现,以训练后的对抗实测为准。

目前已完成的验证限于实现正确性:

  • 数值对照:标签生成函数与参考管线原版实现做了对照——参照 实现按原版分层循环的语义独立编写,30 组随机树状态(含先验相等、零预算 层、单子节点、未访问子节点)下 policy 与 value 均在 1e-12 容差 内一致;另有 7 个手算期望用例覆盖前缀 max 语义、凸组合区间、 单子节点退化为树内 Q 等边界;
  • 退路完备:重分配关闭时精确退回旧行为(访问分布 + 树内 Q); 无已访问子节点的叶子退回树内 Q;根节点保留蒙特卡洛终局 return——比参考管线的自举根标签更无偏,这一处不退回;
  • 性能:改动位于搜索结束后的数据收集层(每手一次的冷路径), 分层循环本来就在为 policy 标签运行,value 只是同循环多两次 浮点累加,增量开销趋近于零,与搜索热路径(cpp 后端)无关;
  • 轮次背景:参考管线累计约 2 万轮,新管线已训练 6000 轮—— 轮次只差 3 倍余,搜索预算却反向差 18 倍,防杀能力完全倒挂。 如果纯粹是轮次(数据量)问题,新管线 18 倍的搜索深度理应部分 弥补 value 头的不足;实际上搜索对潜伏杀点完全无效(先验低 + value 无信号,模拟根本不往杀点分配)。能同时解释两个方向差距 的变量只有标签机制——潜伏杀点信号被稀释一个数量级,是结构性 问题,也更没有理由让标签继续带病训练。

8. 对后续训练的建议

  1. 现有权重可直接续训,无需重置——标签管线变更不改变网络结构;
  2. 预期初期波动:新标签分布比树内 Q 更极端(前缀 max 保留 极端值),续训最初几轮 val value 可能先升后降,这是标签迁移 而非退化,观察 3~5 轮趋势再下判断;滑动平均的 best 判据会 自然适应新的标签水平;
  3. 效果验收指标:拿新 checkpoint 重测强算杀对手,观察 "对手落杀点之前"的胜率预判是否提前变差——这是 value 头 嗅杀能力的直接观测量,比任何离线 loss 都可靠;
  4. 不要回头堆 sims:3600 sims 防不住的,36000 也防不住—— 搜索预算的分配由 policy 先验驱动,杀点先验低的前提下, 预算是无效堆砌。

9. 一句话总结

潜伏威胁的识别不靠搜索靠标签——访问量加权的树 Q 会把杀点信号 稀释到噪声水平,只有让 value 标签与 policy 标签同源、走同一条 "prior 层级差分 + 前缀 max"的重分配之路,极端价值才能以正确的 权重抵达 value 头,模型才能在对手落子之前就闻到杀气。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
鱼友3743
下载 APP