训练闭环¶
闭环由哪些环节组成¶
这个项目的训练闭环可以按下面顺序理解:
- Rust 根据当前规则和模型推理能力执行 self-play。
- self-play 过程中生成棋谱、统计字段和索引信息。
- Python 读取这些数据,构造训练 batch。
- 模型训练后写出新的 checkpoint 和 metrics。
- 后续 self-play 继续使用新的模型能力。
这样设计的关键点不是“环节多”,而是每个环节都要留下可复用输出,后一个环节不能依赖前一个环节的内存状态。
如果你想看一个最小例子,可以把一次闭环想成这样:
- 当前模型先参与 100 盘 self-play。
- 这 100 盘的每一步行动、分数变化和终局原因被保存到 run 目录。
- Python 读取这些记录,整理成训练 batch。
- 模型更新一次或几次后,写出新的 checkpoint。
- 下一批 self-play 改用这个新 checkpoint 对应的模型。
真正运行时规模可能比这个大很多,但数据流方向就是这样。
Rust 在这里负责什么¶
Rust 负责高频、规则敏感、需要稳定状态转移的部分。
主要包括:
- 棋盘表示与 bitboard
- 行动合法性
- 提子与终局判断
- MCTS 搜索
- self-play 执行
- 棋谱压缩和索引相关记录
为什么这样划分: 这些逻辑如果散落到 Python 里,性能和一致性都会更难控制,尤其是规则实现很容易出现两套版本。
输入输出是什么:
- 输入:配置、当前模型提供的推理接口、随机种子、当前运行状态。
- 输出:对局记录、局面转移结果、搜索统计、终局信息。
怎样判断结果是否正常:
- Rust 产出的棋谱能被后续训练和工具读取。
- FFI 边界不会因为 Rust panic 直接把 Python 解释器打崩。
如果你没接触过 FFI,这里它可以简单理解成“Rust 和 Python 互相调用时的接口边界”。
Python 在这里负责什么¶
Python 负责训练和实验控制。
主要包括:
- 配置加载
- 硬件选择
- residual CNN 定义
- checkpoint 轮转
- metrics 写入
- 训练循环
- Rust bridge 调用
为什么这样划分: 模型训练、PyTorch 设备管理和实验调度更适合留在 Python 生态里,同时又不需要复制规则。
输入输出是什么:
- 输入:配置文件、棋谱数据、checkpoint、设备可用性。
- 输出:新的 checkpoint、训练指标、下一轮可用模型。
怎样判断结果是否正常:
- 训练开始前能明确知道使用的是 CPU、MPS 还是 CUDA。
- checkpoint 能按
latest / previous / best轮转更新。 - metrics 不是只在内存里存在,而是持续写入到 run 目录。
这三个 checkpoint 名字也可以先这样理解:
latest:最近一次保存的训练状态previous:上一个可退回的状态best:按项目设定标准保留下来的最好状态
数据为什么必须集中到 run 目录¶
一个训练闭环通常要跑很久。只要数据散落在不同目录里,下面这些事情都会变难:
- 判断某个 checkpoint 对应哪批棋谱。
- 恢复时找不到最新配置快照。
- 画图时不知道该读哪一轮 metrics。
所以本项目要求:
data/runs/<run_id>/
作为一次运行的统一输入输出容器。
读者怎样判断闭环是否正常¶
至少看四件事:
- self-play 能稳定结束,而不是卡在非法状态。
- 棋谱、索引和 metrics 会持续落盘。
- checkpoint 能被下一轮训练重新加载。
- 中断后恢复时,系统能接着已有状态继续,而不是悄悄重置。
如果你打开一次正常运行后的 data/runs/<run_id>/,至少应该能看到三类东西:
- checkpoint 文件
- 对局记录或对局索引
- metrics 文件
如果只看到图片、零散日志,或者只看到模型文件却找不到对局数据,这条闭环就不算完整。