五子棋AI教练:用神经网络教孩子建立大局观
陪闺女下五子棋的时候我发现十岁小孩有一种近乎本能的“局部偏执”棋盘右下角明明已经挤成菜市场她还在死磕那个叉叉位棋盘左上角人家连成双活二她觉得“离我太远了跟我没关系”。下完棋复盘问她她说“我每一步都很认真啊”。问题确实不在认真而在“大局观”——但这三个字对小孩来说太抽象了你说一百遍“你要看全局”不如让她亲眼看见自己走完一步棋之后整盘棋的天平往哪边倾了一下。这篇是“好玩系列”第二篇。我不做那种直接替小孩算必胜步的“金牌代打”我训练一个神经网络模型让它坐在旁边当“大局观教练”小孩每走一步棋模型不是告诉她“你应该下哪”而是告诉她“你刚这步棋把胜率从52%拉到41%原因是你在东北角漏掉了一个关键连接点”。这个效果听着玄其实用一套很普通的神经网络组合就能做出来——局面评估网络负责给棋局打分搜索树负责在打分基础上多想几步两者合起来就是一个简化版的自我对弈强化学习雏形。整个项目用家里闲置的电脑就能跑训练数据不需要人工标注磨合一个晚上基本出结果。1. “教练”和“代打”之间隔着一整个产品设计思路1.1 小孩缺的不是答案是反馈方式先聊一个很核心的产品定位问题为什么我坚持不做一个“直接告诉你下一步下哪”的AI因为我试过。第一版原型里我确实给闺女装了一个带胜率提示的五子棋小助手赢棋率立刻上去了但玩了三天我发现她形成了一种“伸手党”习惯先看AI标出的最佳位置在哪然后闭眼下那儿。问她为什么选这步她说“AI说的”。这完全违背了我的初衷——我不是要训练一个听AI话的机器人而是要训练她自己去理解局势的能力。后来我调整了思路把产品定位从“代打”改成“教练”。教练跟代打最大的区别是教练不给答案给反馈。你下完一步棋教练告诉你“这一步造成了什么后果”然后让你自己决定下一步怎么办。这样小孩的大脑必须主动参与必须在反馈中建立“局面感”。这里面有个很微妙的产品设计点教练的反馈要“足够及时”但不能“足够直接”。太及时了小孩来不及思考太直接了小孩又会变成机械执行。所以我的方案是孩子可以随便落子但每落一子界面上立刻更新一个胜率条并用一句话点评这一步棋的“大局观得分”。她不一定要听AI的但她能立刻看到自己每一步棋背后的“局势账”。1.2 大局观如何变成可计算的指标“大局观”听起来很哲学但落到工程上其实就是一个值当前局面下先手/后手获胜概率的变化量。一盘五子棋下到某个时刻局面总有一个真实的优劣状态。你能客观算出“这个局面是黑棋好还是白棋好好多少”你就有了针对大局观的量化评估。孩子走一步棋之后局面变了胜率变了这个差值就是她这一步棋的“大局观成绩单”。问题在于五子棋的局面价值函数不像下棋规则那么容易手工写。你可以写几条经验公式比如“活三加20分、冲四加50分、双三加100分”但这种打分很粗糙而且容易顾此失彼——某些局面下明明整体占优但局部一个低级失误就能把优势全送回去。经验公式很难覆盖这种全局联动。神经网络解决的就是这个问题。让模型在大量棋谱中自己学到“什么样的局面配置更容易赢”然后对任意局面输出一个劣势分。模型的输出天然比手工规则更平滑、更擅长处理“说不清楚但感觉就是不对劲”的复杂局面。这就是我选择训练一个神经网络来做“教练大脑”的根本原因。2. 大脑选型把AlphaGo的双头网络缩小到五子棋够用2.1 为什么不上强化学习用监督学习就够了做棋盘AI很多人的第一反应是上强化学习让AI自己跟自己下几百万盘棋。AlphaGo确实这么干但那是谷歌的算力资源家用电脑想复刻训练周期会感人到让人怀疑人生。我的方案是“监督学习为主搜索为辅”。具体分两步第一步用一套规则AI自己跟自己对弈生成大量棋谱。棋谱里记录每一步落子和最终胜负。第二步让神经网络去学习这些棋谱——给定一个中间局面预测“规则AI在这个局面下会下哪里”策略头同时预测“这个局面按最终结果来看谁赢”价值头。这里的关键洞察是我不需要AI下棋水平有多高我只需要它对局面的判断足够稳定。规则AI本身可能只有“初段”水平但它每盘棋的胜负结果是客观的大量对局产生的胜负统计足以训练出一个对局势敏感的价值网络。经验证这个价值网络对局面的判断能力会超过生成数据的规则AI——因为神经网络在泛化时会把众多棋局中的规律抽取出来而不是死记硬背某几步。2.2 双头网络的结构与PyTorch实现这个模型参考的是AlphaGo论文里“一个网络、两个输出头”的设计只不过规模缩小到家庭电脑能跑的级别。网络结构其实特别朴素import torch import torch.nn as nn import torch.nn.functional as F class DualNet(nn.Module): def __init__(self, board_size15, in_channels6): super(DualNet, self).__init__() self.board_size board_size # 共享的卷积特征提取层 self.conv_block nn.Sequential( nn.Conv2d(in_channels, 32, 3, padding1), nn.BatchNorm2d(32), nn.ReLU(), nn.Conv2d(32, 64, 3, padding1), nn.BatchNorm2d(64), nn.ReLU(), nn.Conv2d(64, 128, 3, padding1), nn.BatchNorm2d(128), nn.ReLU(), ) # 价值头输出一个标量表示当前局面谁占优1黑优-1白优 self.value_head nn.Sequential( nn.Conv2d(128, 1, 1), nn.Flatten(), nn.Linear(board_size * board_size, 128), nn.ReLU(), nn.Linear(128, 1), nn.Tanh() # 压缩到[-1, 1] ) # 策略头输出棋盘上每个位置的落子概率 self.policy_head nn.Sequential( nn.Conv2d(128, 32, 1), nn.Flatten(), nn.Linear(32 * board_size * board_size, board_size * board_size), nn.LogSoftmax(dim1) # 用log概率方便后面算交叉熵 ) def forward(self, x): features self.conv_block(x) value self.value_head(features) policy self.policy_head(features) return value, policy这样设计的原因有几个共享卷积层让网络先在棋盘上提取“局部棋形特征”比如活三、冲四、双三这些模式然后两个输出头各自从这些特征中提取自己需要的信息。价值头关心“这些棋形组合起来整体对谁有利”策略头关心“下一步落在哪个点对局面最有利”。使用Tanh把价值输出压缩到-1到1之间正好和我的标签定义对齐1表示当前执黑一方优势巨大-1表示白方优势巨大。策略头没用Softmax而用LogSoftmax因为训练时要配合NLLLoss计算交叉熵数值上更稳定。整个网络参数量大约几百万显存占用不到2GB普通家用显卡甚至CPU都能跑。2.3 把棋盘转成能“看”的输入特征神经网络不认识“棋盘”它只认识张量。所以要把每一步棋的局势编码成多通道的二维矩阵类似图像的RGB通道。我用6个通道通道内容0当前黑棋的位置黑子处为1其余为01当前白棋的位置2上一手落子的位置用于让模型知道“刚发生了什么”3最近三手棋的落子位置轨迹4当前轮次信息轮到黑棋全1轮到白棋全05自由落子数计数用于提示模型棋盘上还剩多少空间通道2和通道3是我额外加的原版AlphaGo没有这些通道但我实测发现加上它们后模型对“刚发生的局部冲突”更敏感训练收敛也更快。原因很简单五子棋里的很多关键胜负手都是围绕“上一手棋在哪落下”展开的把这层信息显式喂给模型相当于给它开了一盏聚光灯。3. 训练数据不用手工标注规则AI自对弈批量产棋谱3.1 一个评分式规则AI如何工作训练神经网络最头疼的就是数据标注。“指导小孩下五子棋”这个场景总不能真找一堆高手来一局一局告诉我们哪个局面好。所以我的方案是先写一个评分式规则AI让它自己跟自己下棋自动生成海量带胜负标注的棋谱。这个规则AI的核心是棋型评分函数。五子棋的棋型可以分为若干种基本单元活四、冲四、活三、眠三、活二、眠二。每种子型对胜率贡献不同我给它分配一个大致的经验分值SHAPE_SCORES { FOUR_OPEN: 100000, # 活四已经必赢 FOUR_SEMI: 10000, # 冲四需要堵 THREE_OPEN: 5000, # 活三威胁持续累积 THREE_SEMI: 500, # 眠三有潜在威胁 TWO_OPEN: 200, # 活二布局阶段的棋子 TWO_SEMI: 20, # 眠二低配版活二 }下棋时规则AI遍历棋盘所有空位对每个空位假设自己落子计算这一步之后己方的攻击分数和对方的防守分数。最终评分 己方攻击分 对方防守分 × 0.9取分最高的位置落子。为了让自对弈棋谱有足够多样性我会对每个位置的最终分数加一个高斯噪声标准差约15%让AI偶尔不那么“死板”。如果用纯贪心规则3万盘棋会下出大量重复变化模型学到的模式会偏窄。3.2 从棋谱到训练样本的转换过程规则AI下完一盘棋后棋谱是一长串落子序列。要转成神经网络训练样本做法是按步数切片假设一局棋下了40手那么每一步棋对应的局面都能成为一条训练样本。以第t手为例输入特征读完前t-1手的棋盘状态编码成6通道张量策略标签第t手落到哪个位置一个225维的one-hot向量价值标签终局时黑棋赢标1白棋赢标-1整个棋谱能产生约40条训练样本。我批量跑了5万盘自对弈得到约200万条样本。有个细节值得注意价值标签用终局结果而不是用“某一步当时软件评估的胜率”。这是AlphaGo论文里的经典做法逻辑是终局是客观事实没有主观猜测成分。神经网络会在训练中自动学会一个“中庸”的评估——它看过的局面如果最终大多以黑胜告终那它就会倾向于认为类似的局面黑棋占优。3.3 数据量和样本平衡的实际体会第一轮训练我只生成了5千盘棋谱大概20万样本训练出来的模型对局面的判断经常“飘”胜率曲线忽高忽低。后来把数据量扩到5万盘才稳定下来。但这也带来了新问题因为规则AI有轻微的先手偏好黑棋胜率明显高于白棋价值网络会倾向于“无脑吹黑”。解决方法有两种在自对弈时让黑棋和白棋都随机加噪声弱化先手优势训练时对样本做镜面翻转增强——把棋盘上下/左右镜像同时交换黑白颜色相当于把白方样本变成黑方样本我两个都用了。镜像增强之后数据量膨胀4倍而且模型对黑白双方的评价明显更对称了。这里有个小经验在下棋类AI项目里棋盘对称性增强是性价比最高的数据增强手段没有之一。4. 训练实战损失设计、超参与三天三夜的踩坑4.1 价值头和策略头为什么必须一起训练我的模型有两个输出头训练时两个头的损失要加起来一起反向传播def compute_loss(value_pred, policy_pred, value_label, policy_label): value_loss F.mse_loss(value_pred.squeeze(), value_label) policy_loss F.nll_loss(policy_pred, policy_label) total_loss value_loss policy_loss return total_loss, value_loss.item(), policy_loss.item()一开始我贪图省事只训练价值头因为“教练”主要靠胜率变化说话策略头完全没训。结果模型对局面的判断极其迟钝它学会了“棋盘上子多的一方一般占优”这种粗浅规律但对局部棋形完全不感冒。后来我才意识到策略头对价值头来说是一种“自我监督”。策略头输出的落子概率相当于在说“下一步最可能发生在哪些位置”这些位置通常是攻防焦点。当策略头在学这些焦点时特征提取层被迫去关注“哪个地方正在形成威胁”而价值头恰好需要这些威胁信息来做评估。两个头一起训练等于强迫网络把棋盘的“注意力”放在关键位置价值判断自然就更精准。4.2 训练中的三个“反直觉”现象首先是loss下降曲线会骗人。价值头的MSE loss在前几个epoch快速下降看着很开心但模型实际评估能力很弱——因为大部分局面的价值本来就接近0.5五五开模型只要输出一个“平庸的0”就能把loss压得很低。真正要关注的是终局判定准确率随机抽一批棋局到中盘的程度让模型判断黑棋会赢还是白棋会赢统计正确率。我的模型在中盘正确率大概78%这个指标比loss值有意义得多。其次是batch size不能太大。我一开始用batch_size2048想着加速收敛结果模型学习出来的策略头“过于平均”——每个位置的概率都差不多模型根本分辨不出好在哪。降到256之后策略头才开始出现尖锐的概率峰。原因并不难理解batch太大时梯度被平均得太光滑模型学不到局部的尖锐特征。第三是学习率要分阶段降。我用Adam优化器初始学习率5e-3每10个epoch降一半。第一版训练我全程用5e-4训了50个epoch模型策略头基本废了——因为学习率太小前期没有快速跳出随机初始化的泥潭。后来改成分段衰减效果立刻提升一个档次。4.3 模型效果怎么看光看loss不够训练完成后我写了一段测试代码把模型“请到”棋盘边给它随机摆出一系列真实的棋局片段让它输出胜率再用人工认可的常识去判断它说得有没有道理。有个典型case最有说服力我把一个“黑棋双三成型”的局面喂给模型模型输出黑方胜率0.87踩着赢棋阈值旁边另一个局面黑棋只有零星几子白棋却已经四三连珠模型输出黑方胜率0.08。这种直观的对照让我确信模型学到了真实的大局观信号。另外一个观察是模型对“无子区域”的评估出人意料地准。测试时我摆了一个“双方各自布局”的早期局面模型判断为势均力敌但其实棋盘左上角白棋有一个隐蔽的“活二”人力一眼瞅不出来的那种微弱优势模型也给判出来了。事后我拿清盘的MCTS复盘证实模型是对的。这说明它确实捕捉到了跨越多步的隐藏威胁。5. 把模型包装成孩子听得懂的“教练话术”5.1 胜率差分解读一手棋的功过怎么写技术上讲模型输出的是一个-1到1之间的局面分但小孩不可能直接看一个浮点数就懂自己这步下得好不好。我加了一个“胜率条”任意局面下将模型输出映射成0%~100%的胜率。孩子落子前后各算一次胜率差值就是这一步的大局观得分。这部分我设计成几句模板话术def coach_feedback(win_rate_before, win_rate_after, hot_spot): delta win_rate_after - win_rate_before if delta 0.05: comment 这步棋让我们的赢面提高了你注意到大局上最关键的连接点进步很大 elif delta -0.03: comment 这步棋比较平稳没有改变局面太多。不过你可以看看{}那里的机会更大。.format(hot_spot) elif delta -0.10: comment 这步棋让局面稍微变差了你的注意力被局部战斗拉走了全局还有更好的选点。 else: comment 这步棋把大局优势送出去不少先别急着厮杀停下来看看全盘还有什么更重要的事。 return comment这个话术系统非常关键它决定小孩是“越玩越投入”还是“被伤到自尊”。我的体会是批评的话要用“评价棋局”的方式说而不是评价人的方式。要避开“你这步很臭”这种人身评价只描述“这步棋把胜率拉低了原因可能是”这个客观事实。5.2 热点区域图让孩子看见全局的重心文字反馈之外的另一个界面设计是在棋盘上画一个“热点区域图”——用不同颜色的半透明图层标出模型认为当前最重要的攻防区域。落子概率越高的位置颜色越深越红概率低的位置透明。这里的核心思想是模型说一百句“你要看全局”不如让它用热力图直接把全局的注意力可视化出来。孩子看到棋盘右上角红得发烫而自己一直在左下角死磕时不需要任何人解释她自然能建立对“全局重心”的直观印象。热点区域图用策略头的输出实现正巧策略头输出的225维概率向量天然构成了15×15的概率热图直接绘制就能用def render_heatmap(policy_probs, board_size15): # policy_probs: (225,) tensor, 已经过softmax归一化 heatmap policy_probs.reshape(board_size, board_size) # 可选用高斯核做一次平滑让热区更好看 # 实际界面中把heatmap叠加到棋盘图上透明度设为0.4 return heatmap实际使用中我会做一点后处理把概率低于1/225均匀随机水平的位置全部置零避免棋盘上到处都是噪点。5.3 教练模式的交互设计既要提点又不能喧宾夺主产品层面我花了很大精力思考一个矛盾教练提示会给太多会不会让小孩产生依赖最终的设计是分档提示提示关闭模式完全不给任何AI反馈让孩子自己下锻炼独立的局势判断落子后复盘模式孩子落子后AI才给出反馈但落子前不主动给建议实时热力提示模式AI持续画出热点区域图适合孩子刚开始建立大局观时用我的建议是前20盘用模式三让孩子理解“什么是全局视角”之后切到模式二强化自己的判断有重大对局需要校准认知时再开模式三五分钟。界面实现我用的是pygame大概200行代码能搞定一个15×15棋盘交互。孩子用鼠标点格子落子AI立刻异步计算模型输出刷新胜率条和热力图。整个推理过程在CPU上不到50毫秒完全感觉不到卡顿。6. 实测记录与下一步想做的事6.1 10盘试玩后的反应我家闺女从“提示关闭”模式开局下了一盘开局十分钟死磕右下角被AI在左上角偷到一个关键连接点输了。她第一时间问“为什么我感觉我每一步都在赢却一直输”。这正是我想看到的瞬间——我让她打开“落子后复盘模式”接着下第二盘她第一次看到自己落子时胜率条从49%直接掉到71%因为模型判她误入陷阱区她的表情瞬间就凝重了然后会开始主动问“如果我不下这儿那哪儿比较好”。又下到第五盘她的主观策略明显变了不再追着一个局部区域穷追猛打而是会在布局阶段可以分散落子保持多个方向的威胁。虽然下法还很幼稚但她已经可以自己说出“这盘我不能只守一个角”这种具有全局认知的话——这比赢棋重要多了。6.2 当前模型被诟病最多的地方这个项目当然远未成熟有几个问题是客观存在的局部死活识别仍然偏弱。一些复杂的“四三连珠陷阱”模型经常给出错误的高胜率评估因为训练数据里这种高段位对局太少。规则AI是训练数据的源头如果规则AI本身没见过的棋形神经网络也不可能凭空学会。这导致模型面对某些冷门开局时会“胡言乱语”。热点区域图偶尔会让人费解明明一个空位概率特别高但真按那个点下下去胜率并没有显著提升。这是因为策略头在模仿规则AI的下棋偏好而规则AI的偏好并不完全等价于最优。6.3 可以继续玩的方向后续有几个我已经在规划的方向第一用训练好的策略头替代规则AI去自对弈生成更高质量的棋谱再蒸馏回一个新模型相当于一个弱化版的AlphaGo Zero迭代第二在价值头之外加一个“局部死活诊断模块”专门处理那种线型差一点的陷阱棋第三把棋盘从五子棋扩展到更复杂的棋类游戏验证同一套教练框架的迁移能力。这个项目最让我有成就感的不是模型训练本身而是验证了一件事神经网络摆脱“黑盒玩具”的定位真的可以变成一个家庭里的认知工具。它没有替孩子做任何决策但让孩子第一次自发地开始思考“全盘”这个维度。小朋友从“我这步想干嘛”到“我这步下来全盘会变成什么样”这个思维习惯的转变比任何AI棋力指标都值钱。最后顺手分享一个小经验想做这类“家庭AI项目”与其研究很复杂的大模型不如选一个规则清晰、规模可控的小游戏先跑通整个“数据生成—模型训练—交互反馈”闭环。五子棋就是绝佳的实验田一套几十行规则的AI加上双头卷积网络一台普通电脑就能handle。基本功打好了后面迁移到更复杂的场景也就是换换棋盘尺寸和通道数的问题。