☰
Ghost-master实战:基于Minigo的幻影围棋Python项目全解析
2026/10/8 19:09:42 网站建设 项目流程

简介:基于Minigo的幻影围棋项目Ghost-master,面向围棋AI学习者和Python开发者,用于实现人机、机机两种对弈模式。压缩包共18个文件,总量1.47MB,以14个py源码为核心,覆盖棋盘表示、特征提取、对弈策略、蒙特卡洛树搜索及可视化界面等模块;同时附带模型权重索引文件与说明文档,便于快速运行和二次开发。目前已有926人学习下载。项目通过referee、ghost_vs_human、ghost_vs_ghost等脚本清晰区分裁判、人机对战和AI自弈流程;dual_net等文件展示了Minigo神经网络结构,utils、symmetries等工具则提供数据增强与通用函数支持。从零搭建这套围棋AI,可深入理解MCTS+CNN的混合决策机制,并借助机机对弈持续优化棋力,是实践深度学习与博弈算法的轻量级好素材。

1. Ghost-master 幻影围棋:一份能直接落地的 Minigo Python 项目

Ghost-master 是一份用 Python 写好的幻影围棋项目,底层走的是 DeepMind 开源的 Minigo 框架,压缩包里模型权重、双头网络、蒙特卡洛树搜索和三个对战入口脚本一次配齐,装好依赖就能看到 AI 在 19 路棋盘上落子。它把 AlphaGo Zero 那套“特征编码 → 策略网络 → 价值网络 → MCTS 搜索”完整折叠进了可复现的代码里,比对着论文空想直观得多。适合两类人:围棋爱好者想本地体验 AI 对弈,AI 学习者想读懂完整决策链路。我拆完这个包最大的体会是:难点不在单个算法,而在几个 Python 模块之间怎么衔接,所以下面按“认清结构 → 跑通对战 → 拆核心机制 → 避开坑”的顺序讲。

2. 项目结构与模块职责:先看懂这些 .py 文件的分工

拿到压缩包解压后,Ghost-master 根目录下躺着十几个 Python 文件和一个 models 目录。很多人上来就双击 ghost_vs_human.py,结果各种报错,原因就是没搞清楚模块边界。这个项目的文件整理成下表,你会发现它其实是标准 Minigo 工程的分层:围棋规则层、特征层、网络层、搜索层、界面层。

文件职责
go.py棋盘状态、落子合法性、提子、打劫、胜负判定
coords.py棋盘坐标与落子坐标之间的转换
features.py把当前局面编码成 CNN 需要的特征平面
dual_net.py定义策略 + 价值双头卷积网络
symmetries.py棋盘的旋转/翻转 8 种对称变换
Ghost.pyAI 玩家主逻辑,封装 MCTS 搜索
Ghost1.py备用的 AI 封装入口,通常搜索次数更低
Referee.py对局裁判,负责贴目与终局数子
utils.py公共工具:日志、计时、模型路径等
ghost_vs_human.py人机对战入口
ghost_vs_ghost.pyAI vs AI 机机博弈入口
ghost_vs_randomPlayer.pyAI vs 随机落子对手,用于环境自检
AI_GUI.pypygame 图形界面入口
models/000496第 496 步训练保存的模型权重
note.txt作者留下的运行说明
z_trashBin.py废弃代码,不要引入主流程

里头的核心链路是这样走的:go.py 维护棋盘,features.py 把棋盘编码成 17 通道特征图,dual_net.py 推理出策略先验和胜率估计,Ghost.py 拿这两个输出跑 MCTS 搜索,最终选一个落子点。

2.1 go.py 与 coords.py:对弈规则底座

go.py 是整盘棋的地基。它负责记录当前局面、判断某步是否合法、处理提子和打劫,以及终局时计算地盘。Minigo 的 go.py 里核心数据结构是 Position,每个 Position 快照包含棋盘状态、上一次落子、是否 pass 等信息。MCTS 每展开一个节点都要调用它,所以这块的性能直接影响搜索速度。

coords.py 解决的是坐标转换问题。围棋里有三种常见坐标表示:GTP 协议的字母坐标(如 D4)、SGF 棋谱坐标、还有程序内部用的 0-18 索引。人机对战的时候用户在终端输入的是 D4 这种,程序内部计算用的是数字索引,中间就是 coords.py 在做翻译。你改界面或者接 GTP 协议时,最先碰到的就是这里。

2.2 models/000496:这份模型权重到底代表什么

models 目录下只有一个 000496,这个编号我理解是强化学习自对弈训练到第 496 次保存的权重快照。Minigo 的做法是让当前模型和旧模型不断对弈,赢到一定比例才更新,所以编号越大的权重通常棋力越强、棋风也越稳。000496 在训练曲线上属于中前期,特征很明显:会走常见定式和基本攻防,但大局观偏弱,特别喜欢主动引发战斗。

加载模型的逻辑一般写在 Ghost.py 顶部,常见写法是定义一个 MODEL_DIR 或 LOAD_PATH 常量指向 models/000496。如果你手头有更强的新权重,只需要替换这个目录再改指向就行。注意这里不是只换一个文件,而是换整个目录,因为 Minigo 的保存格式里除了权重本身还有图结构和训练配置。

2.3 三个入口脚本:人机、机机、随机验证怎么选

ghost_vs_human.py 是主入口,跑起来后 AI 加载模型下黑棋或者白棋,用户通过命令行输入坐标。ghost_vs_ghost.py 是给两个 AI 对弈用的,你可以指定不同的权重目录让它们自我博弈,这是最强的验证方式——两个差不多棋力的 AI 对局,结果基本能反映权重好坏。ghost_vs_randomPlayer.py 则是我建议你第一次跑通的脚本,它让 AI 对一个随机落子的对手,程序稳定性和环境问题一眼就能暴露出来。

这三个脚本的职责划分很重要。很多人一上来就试图用 GUI 跑对局,结果模型加载成功但界面崩了,反而分不清是模型问题还是界面问题。我的习惯是先跑 ghost_vs_randomPlayer.py 确认推理链路通,再跑 ghost_vs_human.py 体验对战,最后才碰 AI_GUI.py。

3. 环境搭建与启动:从 pip 装依赖到人机对战跑通

这一章讲实际操作。Ghost-master 是 2019 年前后那个 Minigo 时期的项目,依赖栈和现在主流环境差得比较多,装环境的坑比代码本身还多。核心准则是:Python 版本别用太高,TensorFlow 别用 2.x,numpy 别用新版。我给出的组合是 Python 3.7 + TensorFlow 1.15 + numpy 1.19。

3.1 创建虚拟环境并安装依赖

先用 conda 建一个干净的环境,避免污染系统 Python。

conda create -n ghost python=3.7 -y conda activate ghost pip install tensorflow==1.15.0 pip install numpy==1.19.5 pip install pygame sgf

逻辑说明:conda 创建独立环境是为了把 TensorFlow 1.15 和系统里其他 Python 项目隔开,这个版本非常老,装错地方会引发连锁冲突。tensorflow==1.15.0 是这台代码能正常 import 的前提,Minigo 的 dual_net.py 里大量使用 session、placeholder 这类 TF1 专用 API,TF2 默认 eager 模式会直接报错。numpy 锁在 1.19.5 是为了避开 1.24 以上版本移除 np.float / np.int 别名的问题。pygame 是 AI_GUI 要用的,sgf 库用来解析棋谱和记录对局。

参数说明:如果装 tensorflow 时提示找不到对应版本,多半是 Python 版本太高,退回 3.7 即可。如果你用的是 Apple Silicon 或者比较新的 macOS,pip 装不了 TF1.15 的轮子,我一般直接拉镜像跑:docker 里放一个 tensorflow/tensorflow:1.15.5-py3 环境,再把项目挂载进去。

3.2 检查并修改模型路径与核心参数

解压后第一步不是直接运行,而是打开 Ghost.py 或 utils.py 看一眼模型路径。常见写法是顶部定义常量,我按最常见的配置写下示意:

# utils.py 或 Ghost.py 顶部的配置区 MODEL_DIR = os.path.join(os.path.dirname(__file__), 'models', '000496') BOARD_SIZE = 19 NUM_READOUTS = 800 RESIGN_THRESHOLD = -0.9

逻辑说明:MODEL_DIR 指向模型权重目录,如果脚本用相对路径,建议你改成绝对路径,防止从别的目录启动时报找不到文件。BOARD_SIZE 决定 dual_net 输入形状,Minigo 默认 19 路,也就是特征图是 19×19。NUM_READOUTS 是 MCTS 每步搜索的模拟次数,800 是 Minigo 默认值。RESIGN_THRESHOLD 是认输阈值,代表胜率估计跌到多少时自动认输。

参数说明:如果你是第一次跑且用的是 CPU,建议先把 NUM_READOUTS 降到 200,不然每步要等十几秒,体验很差。核显或没有 GPU 的机器上,800 次搜索一步棋可能要二十秒以上。跑通全流程后再逐步调高到 400、1600 对比棋力变化。

3.3 跑通人机对战

环境弄好后,先跑最简单的随机对手脚本验证推理链路:

python ghost_vs_randomPlayer.py

逻辑说明:这个脚本让 AI 对随机落子的对手,如果能在几十手内看到 AI 正常落子并最终获胜,说明模型加载、特征编码、MCTS 搜索整个链路是通的。常见的失败点是前向推理时报形状错误,说到底是 BOARD_SIZE 和模型训练尺寸不一致,回到 3.2 检查配置。

验证通过后再跑人机对战:

python ghost_vs_human.py

启动后终端会提示你输入落子坐标,比如 D4、K10 这种。输入格式走 GTP 坐标:字母从 A 到 T 跳过 I,数字从 1 开始,A1 对应棋盘左下角。你每落一子,AI 开始思考,控制台会打印它的选点和胜率估计。至于是执黑先行还是执白,启动时通常有提示,或者进脚本里改一个常量就行。

3.4 一个值得养成的验证习惯

每次改完环境或者换完模型,我都习惯先跑 ghost_vs_randomPlayer.py 两局再碰正式对战。这个脚本比人机对战快很多,因为随机对手的落子不需要搜索,几乎瞬间响应。它能快速区分“环境有问题”和“模型棋力不行”这两种完全不同的状况,避免你在一个坏环境里调一个晚上的参数。

4. Minigo 核心机制拆解:dual_net、features、symmetries 与 MCTS 决策链路

这章把 Ghost-master 内部最关键的四个模块讲透。MCTS 对很多人是黑匣子,其实拆开看就三件事:拿神经网络给出的先验概率和胜率估计去搜索,搜索到足够次数后按访问次数落子。理解了这个,你就知道为什么有时候搜索次数越高棋力越强。

4.1 dual_net.py:策略与价值双头卷积网络

dual_net.py 定义的是 AlphaGo Zero 论文里的双头结构:输入一个 19×19×17 的特征张量,共享一组残差卷积层,然后分两个头输出。策略头输出 19×19=361 个落子点的概率分布再加 pass 的概率,价值头输出一个标量胜率估计,范围取 tanh 压缩到 -1 到 1 之间。

部件输入输出作用
共享残差块19×19×1719×19×256提取局面特征
策略头19×19×256361+1 个 logits每个落子点的先验概率
价值头19×19×2561 个标量当前局面胜率估计

结构上有一点和普通分类网络不同:落子概率要先做 mask,把不合法的点(已经有子的位置、打劫禁止点)排除掉,再 softmax。这个 mask 操作在 Ghost.py 里完成。如果你换权重后棋力异常,先检查 mask 是不是生效,这是最容易出玄学问题的地方。

4.2 features.py:17 通道特征是怎么拼出来的

Minigo 的输入特征沿用了 AlphaGo Zero 论文的 17 平面设计,这是整个网络输入的核心。简单说,它把最近 8 步的棋盘历史全部编码进去,让网络看到棋局的动态演变,而不仅仅当前这一步。我按惯例写一个构造特征面的示意代码,方便你理解概念:

def build_features(history, next_player): feats = np.zeros((17, 19, 19), dtype=np.float32) for step in range(8): if step < len(history): board = history[-(step + 1)]['board'] own = (board == next_player).astype(np.float32) opp = (board == (1 - next_player)).astype(np.float32) else: own = np.zeros((19, 19), dtype=np.float32) opp = np.zeros((19, 19), dtype=np.float32) feats[step * 2] = own feats[step * 2 + 1] = opp feats[16] = next_player return feats

逻辑说明:history 里保存的是最近若干步的棋盘快照,next_player 是当前轮到的棋手。循环从最远的快照取到最近一步,每步拆成两个平面,分别标记当前棋手和对手的棋子位置。第 17 个平面全部用 next_player 填充,表示这步轮到谁走。这样网络看到的不只是一个静止局面,还包括最近 8 步双方的动作轨迹。

参数说明:如果棋盘是 19 路,每个平面是 19×19;代码里 19 出现的地方都要和 dual_net 的输入形状一致。有些改造版把 8 步历史改成 4 步以省显存,但那样棋力会明显下降,不建议少于 6 步。

4.3 symmetries.py:8 种对称变换的作用

围棋棋盘有天然对称性,同一盘棋旋转 90 度、水平翻转、垂直翻转后,本质上还是同一局面。symmetries.py 就是做这个变换的:对 19×19 的棋盘做 8 种组合变换,对应正方形二面体群。这个模块有两个用途。

第一是训练数据增强。自对弈产生的同一局棋,可以变换成 8 个视角分别当作独立样本喂给网络,等于把数据量翻了 8 倍。第二是推理时做稳定性处理:常见做法是把当前局面变换成 8 个视角,分别跑一次网络推理,再把 8 组策略概率逆变换回原方向取平均,最后的价值取平均值。这样能明显减少单次推理的方向偏差,属于能涨棋但不增加搜索代价的小技巧。

4.4 Ghost.py 里的 MCTS:搜索次数是核心参数

MCTS 搜索封装在 Ghost.py 里,决策流程按顺序是四步:选择、扩展、模拟、回传。选择阶段用 UCB 公式平衡“当前最佳点”和“探索少评估的点”;扩展阶段调用 dual_net 得到新节点的先验概率和胜率;模拟阶段沿着叶子节点继续向下评估;回传阶段把收益沿路径加回每个节点。循环重复 NUM_READOUTS 次,最后按根节点的访问次数选点。

很多初学的人盯着 UCB 公式看半天,其实这个项目里真正影响体验的是 NUM_READOUTS 这个参数。它直接决定每步棋要重复多少次搜索循环。数值越大会越强,但耗时也线性增长。我的经验是:CPU 上跑 200 次已经能欺负乱下的玩家,到 800 次棋风明显更稳,想感受完整棋力至少 1600 次且最好有 GPU。

还有一个细节值得注意:MCTS 的随机性来自 Dirichlet 噪声注入,这是为了让开局不走成固定的重复套路。如果你看到同一盘棋 AI 走了不同开局,不要以为是 bug。

5. 避坑指南:五个必踩的坑与排查方法

这一章写我从拆包到跑通遇到的高频问题,每一条都是现场真实翻车记录。我按“现象 → 原因 → 解决”的方式记录,你可以直接对照着排查。

5.1 坑一:TensorFlow 版本冲突,import 直接失败

现象:运行 ghost_vs_human.py 时报错 module 'tensorflow' has no attribute 'contrib',或者报 placeholder 不存在、Session 不存在。终端里一长串红色 traceback,看最后一行通常在 import dual_net 的位置。

原因:Ghost-master 是基于 TensorFlow 1.x 写的,网络定义里大量使用 tf.contrib、tf.Session、tf.placeholder。装上 TF2.x 之后,contrib 整个被移除,Session 也被禁用,旧代码完全跑不起来。

解决:把 Python 固定到 3.7,然后 pip install tensorflow==1.15.0。如果系统里已经装了 TF2,先 pip uninstall tensorflow 再装。实在装不了旧版就上 Docker 的 tensorflow/tensorflow:1.15.5-py3 镜像。我还见过有人在代码开头加 tf.compat.v1.disable_v2_behavior() 硬兼容,但双头网络的 contrib 部分还是救不回来,不推荐新手折腾。

5.2 坑二:numpy 新版导致 np.float 报错

现象:程序跑到胜负判定或特征构造时报 AttributeError: module 'numpy' has no attribute 'float'。有时候是 np.int 报同样错误。

原因:numpy 1.24 以后把 np.float、np.int 这类别名移除了,而旧项目的代码里直接写了 np.float 或者 np.float32 的位置用了 np.float。这种错误很隐蔽,因为在纯 Python 里不会触发,只有跑到数值计算才会炸。

解决:pip install "numpy==1.19.5"。注意装完 TF1.15 之后再装 numpy,pip 可能会帮你把 numpy 升级到新版,所以顺序最好是先装 numpy 再装 tensorflow,装完检查一下 numpy.version确认没有被动更新。

5.3 坑三:models/000496 加载失败,权重形状对不上

现象:程序启动时能加载模型文件,但一推理就报 shape mismatch,比如 361 和 362 对不上,或者提示 weight 数量不匹配。

原因:Minigo 的策略头输出是 361 个落子点再加 1 个 pass,一共 362 维。如果你下载的权重是旧版 19×19 棋盘训练出来的,而代码里配置改成了 9 路,或者反过来,都会触发形状不匹配。另一个常见原因是加载的不是完整 checkpoint,而是只拷了权重文件没拷图结构,导致变量名对不上。

解决:确认 BOARD_SIZE=19 与模型训练尺寸一致。把 models/000496 整个目录保留完整,不要只搬运单个 .index 或 .data 文件。加载代码一般写在 dual_net.py 的 load 函数里,用 tf.train.Saver 或 tf.keras.models.load_model 时注意看日志里变量数量是否跟你网络定义一致。

5.4 坑四:AI_GUI 黑屏闪退,对局窗口起不来

现象:运行 AI_GUI.py 时窗口一闪而过,或者黑屏卡住不动,命令行没有明显报错。CPU 飙高但界面无响应。

原因:AI_GUI.py 依赖 pygame 做渲染。没装 pygame 时 import 就挂在最前面,窗口直接起不来。另一个原因和 pygame 版本有关,部分新版本对旧式事件循环写法不友好,会卡在刷新循环。

解决:先 pip install pygame 确认 GUI 依赖完整。更稳的做法是先用 ghost_vs_human.py 命令行模式验证核心逻辑通,再跑 GUI。如果你只是想看到最终棋谱,不一定要 GUI,ghost_vs_ghost.py 的输出信息更完整,还不会抢 CPU 资源。

5.5 坑五:机机对战卡死,或出现无限连 pass

现象:跑 ghost_vs_ghost.py 时,有时候看到双方连续 pass 但程序没有正常结束,或者一方明明有棋可下却一直 pass,最后死循环。

原因:Minigo 有认输阈值 RESIGN_THRESHOLD,当价值网络给出的胜率估计低于阈值时 AI 会选择认输。两个 AI 实力悬殊时,弱势一方可能开局没多久就持续 pass,而裁判逻辑如果没有对连续 pass 的次数做上限,就会挂住。

解决:把 RESIGN_THRESHOLD 设得更低,比如 -0.999,让 AI 死扛到底;同时检查 Referee.py 里是否对连续 pass 有终止条件,常见做法是双方连续 pass 两次即终局。在做机机对局测试时,我一般还会加一个最大手数上限,超过比如 800 手强制判和棋,防止极端局面卡死。

6. 进阶玩法:机机自对弈与棋力快速验证

把这套环境跑通只是开始,Ghost-master 真正的价值在机机博弈上。我常用的进阶操作有两个,一个是拿 ghost_vs_ghost.py 做固定参数的自对弈,另一个是低搜索次数快速验证不同权重。

6.1 固定参数自对弈

打开 ghost_vs_ghost.py,通常顶部有几个常量定义两个 AI 各自的模型目录和每步搜索次数:

MODEL_A = 'models/000496' MODEL_B = 'models/000496' NUM_READOUTS_A = 400 NUM_READOUTS_B = 400

把两个模型目录指向不同的权重,或者让 A 保持高搜索次数、B 用低搜索次数,就能观察同一模型在不同搜索强度下的棋力差异。我一般会记录每局胜负、最终目差、手数三个指标,做个 10 局的小比赛,赢多输少的权重就是当前更能打的版本。

6.2 低搜索次数 sanity check

换新权重时,先用 NUM_READOUTS=200 跑 5 局对随机玩家,确认胜率正常后再做正式自对弈。这个习惯帮我过滤掉至少三次坏权重,特征很典型:搜索次数低时该赢的棋赢不下来,但搜索次数越高胜率越稳。如果低搜索局全胜,基本说明权重没废;如果低搜索就翻车,省下了拿高搜索跑几十局的冤枉时间。

从那以后,我每次拿到新训练的棋类 AI 权重,第一件事都是把搜索次数压到 200,和随机玩家跑 5 局做 sanity check,再慢慢往上加搜索量对比。看起来多花十分钟,实际避免的是在坏权重上浪费一晚上的调参。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询