返回列表

4th place solution

643. FIDE & Google Efficient Chess AI Challenge | fide-google-efficiency-chess-ai-challenge

开始: 2024-11-18 结束: 2025-03-06 游戏AI AI大模型赛
第四名解决方案 - FIDE Google 效率国际象棋 AI 挑战
作者: nagiss (Grandmaster)
竞赛排名: 第 4 名
发布时间: 2025-02-15
更新时间: 2025-03-30

第四名解决方案

遵循 Kaggle 的惯例,我想分享我的方法。

如果其他团队也能在讨论区分享他们的方法就好了!

我的仓库在 这里。这份撰写内容是机器翻译的,你可以在里面找到 原始的日语版本

编辑 (3/30):添加了 Kaggle Notebooks 部分,并将标题从“我的方法”改为“第四名解决方案”。

概述

我的方法基于 Stockfish 16。通过在 HCE(启发式评估函数)中添加一个小型神经网络(非 NNUE),我将 Elo 等级分提高了约 30 分。

选择基础引擎

在 reviewing 了讨论区和 Discord 上的消息后,我决定使用 HCE 可以获得有竞争力的排名。因此,我选择了 Stockfish 16,这是最后一个支持 HCE 的版本。

我没有考虑 Stockfish 以外的引擎。这是因为在参加 Hungry Geese 竞赛期间,我研究了将棋 AI,了解到它深受 Stockfish 的影响。这激发了我对 Stockfish 的兴趣。

内存使用情况

内存使用分解如下:

兵哈希表 (Pawn Hash Table): 640KiB

我减小了一些成员变量的大小,并将哈希表中的元素数量从原来的 131,072 减少到 8,192。

延续历史表 (Continuation History): 512KiB

我将其替换为包含 262,144 个元素的哈希表。我没有为哈希实现冲突检查。

置换表 (Transposition Table): 1MiB

我保留了原始 Stockfish 允许的最小尺寸。这也适用于兵表和延续历史表,但我根据直觉确定了大小,并没有进行微调。

我还移除了原始 Stockfish 用于大页 (Large Pages) 的额外内存分配。(Kaggle 的环境可能无论如何都不支持大页。)

其他:约 1MiB?

我移除了所有不必要的模块。

车的 Magic Bitboard 消耗了大量内存。然而,我发现 国际象棋编程维基 中描述的 CFish 的传统方法 (Classical Approach) 似乎是一个很好的替代方案,所以我替换了它。

glibc: 约 3MiB?

由于程序链接到 glibc,其常驻集大小 (RSS) 应该需要约 3MB 的内存。但是,glibc 已经加载在 Kaggle 的环境中,所以它似乎不计入 5MB 的限制。

另一方面,libstdc++ 没有预加载。链接到它会增加内存使用量,所以我重写了 Stockfish 中几乎所有依赖 C++ 标准库的部分,以避免链接到 libstdc++。

虽然仍然可以提交链接了 libstdc++ 的代理,但移除依赖减少了超时失败的频率。(我在竞赛最后 17 天部署的代理仍然链接了 libstdc++。)

改进评估函数

通过到目前为止描述的优化,以及高效的引擎使用(如时间管理、启用思考 (pondering)、避免在搜索开始时不必要的表初始化),我能够开发出一个平均排名在金牌范围内的代理。

(正如许多参与者可能同意的那样,)Stockfish 的代码经过多年的完善,已经没有多少可以改进的地方了。唯一看起来可行修改的部分是评估函数,因为 NNUE 被禁用了。

从 Discord 上的讨论来看,顶尖团队似乎在使用 NNUE。构建一个小型 NNUE 将是最直接的选择,但我认为用神经网络扩展 HCE 会更有趣,所以我选择了这种方法。

神经网络

我使用了一个三层 MLP(多层感知机)。

原始的 HCE 将两个评估值——一个用于中局,一个用于残局——打包到一个 32 位变量中并对其进行计算。我通过添加 14 个额外的 16 位值扩展了这一点,使用 256 位寄存器同时计算它们,并将结果作为第一层的输出。(事后看来,我应该将这些计算与 HCE 分开。量化过程变得 unnecessarily 复杂。)

有 99 个输入特征,所以第一层有 1,400 个可训练参数,包括偏差 (99 × 14 + 14 = 1,400)。这些值分别针对白棋和黑棋计算,然后根据回合组合成一个 32 维向量。

该向量通过 Clipped ReLU(如 NNUE 中所用)、一个全连接 32×32 层、另一个 Clipped ReLU,以及最后一个全连接 32×1 层以产生输出。可训练参数总数为 2,489。

对于训练,我将目标值设置为 NNUE 评估与 HCE 评估之间的差异,并使用 MSE 作为损失函数。我还尝试了 QAT,但我不确定它有多有效。

我使用 kaggle-environments 运行了大约 70,000 场游戏,以 1/8,192 的概率保存搜索过程中遇到的位置用于训练。这些游戏中使用的代理包括基于 HCE 评估的代理、基于 NNUE 评估的代理以及我的实验性评估函数。训练和自我对弈完全在 Kaggle Notebooks 中进行。

在搜索期间使用此 NN 进行评估时,简单地将输出添加到 HCE 值会导致不到 10 个 Elo 的提升。然而,仅添加输出的一半会导致比单独使用 HCE 高出 30 个 Elo 的收益。

不幸的是,我只在竞赛的最后一天才意识到这一点,所以我没有时间尝试更大的 NN。

提交策略

由于我不确定基于 NN 的改进是否在我的本地环境之外也能保持有效,我提交了一个 仅 HCE 的代理 和一个 增强 NN 的代理

同比赛其他方案