简而言之

  • 一份针对 Mistboard 揭棋机器人背后引擎的外部审查,发现了一个真实的评估函数缺陷:其中一方的暗子从未被计入。
  • 修复后对原版引擎得分 0.475;围绕修复重新调校相关的 43 项参数,得分 0.507;替换审查所批评的身份平均,得分 0.370。
  • 机器人维持原样。手工调校的评估函数会吸收自身的缺陷,200 局的门槛看不出 +10 Elo,而任何从这个引擎派生出来的东西都赢不了它。

iwestlin/jieqi-ai 收录了 Pikafish 的 jieqi_old 分支,也就是 Mistboard 上揭棋机器人背后的引擎,并附带一份源码审查 ai分析.md。它的五项发现中,两项是已知限制,一项误读了规则,另有两项值得打一场对抗赛:评估函数中的一个真实缺陷,以及「在搜索中对隐藏身份取平均并不可靠」的说法。

jieqi_old 是 alpha-beta 搜索加手写的评估函数,没有神经网络。它此前已经顶住了十个从它自身评估值蒸馏出来的神经网络(最好的得分 0.43),也顶住了在一局被人类赢下的棋上给它十五倍的搜索时间(着法完全相同)。下面还有三项测试,做法都一样:改动后的引擎对原版引擎,轮流执红执黑,每着用时相同,由裁判程序发放身份,两个引擎都看不到暗子。

缺陷:一方的暗子从未被计入

Evaluation::initialize<Us>() 会为白方和黑方各运行一次。每次调用都会清空整个 DarkPieces 数组,只重新填入自己一方,所以等到威胁项读取它时,白方的暗子已经没了:

memset(DarkPieces, 0, sizeof DarkPieces);   // both colours, every call
for (PieceType i = ROOK; i <= BISHOP; ++i) {
    Bitboard b = pos.pieces(Us, i);         // only Us is refilled
    ...

引擎的 eval 跟踪输出证实了这一点:黑卒攻击红方暗子时,修复前 Threats 一行双方完全相同,修复后则与红攻黑的情形互为镜像。红方攻击暗子会得分,黑方从来不会。

测试一:修复它

黑方一直少了一项红方拥有的评估项,所以把它补给黑方应该有帮助。

每着 500 毫秒,200 局:91-101-8,得分 0.475 ± 0.068。 执红 0.525,执黑 0.425。

新获得这项加分的一方反而下得更差,所以这些权重不只是算错了,本身也是错的。源码里那些奇怪的数值(S(-11, 2)、S(39, -26))说明它们曾经靠实战调校过一次,而当时缺陷就在里面。公式正确、权重照旧,就是另一个没调过参的引擎。

测试二:围绕修复重新调参

如果这些权重是为有缺陷的公式调出来的,那么为修复后的公式重新调参,应该能找回测试一损失的部分,并发现缺陷所掩盖的东西。

缺陷涉及的 43 项参数,通过 Pikafish 的 TUNE 功能暴露为 UCI 选项,再在笔记本电脑上跑 fishtest 式的 SPSA:10,000 对对局,每着 100 毫秒,六小时。43 项中有 41 项发生了变动,变动最大的是暗子着法分数的上限(2862 到 2745)。调参版对原版,每着 500 毫秒,200 局:96-93-11,得分 0.507 ± 0.067。

仍在持平的误差范围内,只是这次落在另一边。只修复得 0.475,修复加调参得 0.507,说明这些参数怎么调都没多少棋力,而不是调得不够。在租用的 CPU 上跑 50,000 对只需几美元,但这两个结果并不支持这么做。

测试三:替换身份平均

搜索走动暗子时,会对该方子池中剩余的每种身份各分支一次,再把结果合并:一个带截断的加权平均,当平均值远高于最坏情况时就退回最坏情况。审查说得对,这不是信息集搜索;我自己也有怀疑它的理由:7 月时,这个公式选了一步真实期望胜率为 64% 的暗子着法,而不是一步胜率 97% 的稳妥着法。

如果这个公式给赌博定价有误,那么正确定价应该能选出更好的着法:当引擎的首选是走动自己的暗子时,取它的前四个候选,对每个赌博的每种身份分别搜索,平均胜率,走平均值最高的一步。

以包装程序实现。根节点用时 400 毫秒、每种身份 100 毫秒,100 局:30-56-14,得分 0.370,用时是原版引擎的 2.2 倍。每种身份都给满 400 毫秒,50 局:17-26-7,得分 0.410,用时是七倍。

它在每种预算下都输,所以是方法错了,而不是资源不够。各自独立的搜索之间的分歧,比同一棵树内部的分歧更大;审查所反对的最坏情况退回,其实在发挥作用。7 月那个局面确实是个错误;这个疗法制造的错误比它消除的还多。

手工调校的评估函数为何难以逐项修补

评估函数是作为一个整体调校的。每个权重都是在与其他所有权重(包括那个缺陷)的对弈中定下来的,所以修复一项会退步,重调一组也只能找回修复所损失的部分。要改进它,就得把全部参数重调一遍,也就是原作者当初做过一次的工作。

门槛太粗。200 局的对抗赛在 95% 置信度下误差约 ± 0.07,大约 ± 25 Elo:价值 +10 的改动看不出来,价值 +30 的也跟掷硬币差不多。更细的门槛需要 1,000 局,在笔记本电脑上每个候选要跑一天。

没有外部标尺。暗棋有开源引擎可以对照,揭棋没有,所以任何从原版引擎蒸馏或对着它调出来的东西,按构造就会落在原版引擎的水平。老师所缺的信号是有深度的自我对弈,而这正是为 jieqi 分支训练神经网络所需要的。

会有什么改变

已上线的机器人不做任何改动。改动它的固定版本意味着新的网页浏览器版本、新的引擎 ID,以及重新计算所有缓存的揭棋复盘,而这里没有任何结果值得这样做。这个缺陷修复要等到它所在的评估函数被替换时再说。

固定的版本现在附带 Linux 二进制文件和网页浏览器版本,上面每个数字背后的训练器、特征集、裁判程序、对抗赛框架和调参器都已公开。一个对已上线机器人 200 局得分 0.55 的神经网络,就会成为新的机器人。