跳转至

训练闭环

闭环由哪些环节组成

这个项目的训练闭环可以按下面顺序理解:

  1. Rust 根据当前规则和模型推理能力执行 self-play。
  2. self-play 过程中生成棋谱、统计字段和索引信息。
  3. Python 读取这些数据,构造训练 batch。
  4. 模型训练后写出新的 checkpoint 和 metrics。
  5. 后续 self-play 继续使用新的模型能力。

这样设计的关键点不是“环节多”,而是每个环节都要留下可复用输出,后一个环节不能依赖前一个环节的内存状态。

如果你想看一个最小例子,可以把一次闭环想成这样:

  1. 当前模型先参与 100 盘 self-play。
  2. 这 100 盘的每一步行动、分数变化和终局原因被保存到 run 目录。
  3. Python 读取这些记录,整理成训练 batch。
  4. 模型更新一次或几次后,写出新的 checkpoint。
  5. 下一批 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>/

作为一次运行的统一输入输出容器。

读者怎样判断闭环是否正常

至少看四件事:

  1. self-play 能稳定结束,而不是卡在非法状态。
  2. 棋谱、索引和 metrics 会持续落盘。
  3. checkpoint 能被下一轮训练重新加载。
  4. 中断后恢复时,系统能接着已有状态继续,而不是悄悄重置。

如果你打开一次正常运行后的 data/runs/<run_id>/,至少应该能看到三类东西:

  • checkpoint 文件
  • 对局记录或对局索引
  • metrics 文件

如果只看到图片、零散日志,或者只看到模型文件却找不到对局数据,这条闭环就不算完整。