Training loop¶
The loop structure¶
The training loop works in this order:
- Rust runs self-play using the active rules and model guidance.
- Self-play writes game records, statistics, and indexing information.
- Python reads that data and builds training batches.
- Model training writes checkpoints and metrics.
- Later self-play uses the updated model.
The important property is not just that these stages exist. Each stage must leave reusable output behind so the next stage does not depend on hidden in-memory state.
Here is a minimal example of one cycle:
- the current model is used for 100 self-play games
- each move, score update, and end reason is saved into the run directory
- Python reads those records and turns them into training batches
- the model is updated and a new checkpoint is written
- later self-play uses that new checkpoint
Real runs may be much larger, but the data flow is the same.
What Rust handles¶
Rust owns the high-frequency and rules-sensitive parts:
- board representation and bitboards
- move legality
- captures and terminal-state detection
- MCTS search
- self-play execution
- compressed game records and indexing support
Why this split exists: if this logic drifts into Python in a second form, both performance and consistency get worse.
Inputs and outputs:
- input: configuration, model inference interface, random seed, current run state
- output: game records, state transitions, search statistics, and terminal summaries
How to judge a normal result:
- Rust outputs can be read by training and tools
- the FFI boundary does not let a Rust panic crash the Python interpreter
If FFI is new to you, treat it as the interface boundary where Rust and Python call each other.
What Python handles¶
Python owns training and experiment control:
- configuration loading
- hardware selection
- residual CNN definition
- checkpoint rotation
- metrics writing
- training loop control
- Rust bridge integration
Why this split exists: PyTorch training and device management fit Python well, and they do not need a second rules implementation.
Inputs and outputs:
- input: configuration files, game records, checkpoints, device availability
- output: new checkpoints, training metrics, and the next model version
How to judge a normal result:
- the selected device is explicit before training starts
- checkpoints rotate through
latest,previous, andbest - metrics are written to the run directory instead of living only in process memory
Those checkpoint names can be read like this:
latest: the newest saved stateprevious: the earlier fallback statebest: the saved state the project currently considers best
Why the run directory matters¶
Long-running experiments become hard to recover if outputs are scattered.
This repository uses:
data/runs/<run_id>/
as the shared container for one run's inputs and outputs.
How to judge whether the loop is healthy¶
Check at least four things:
- self-play reaches legal terminal states
- records, indexes, and metrics keep being written
- later training can reload the saved checkpoints
- recovery continues from prior state instead of resetting silently
After a healthy run starts producing output, you should expect to find at least:
- checkpoint files
- game records or a game index
- metrics files
If you only have screenshots, loose logs, or model files without game data, the loop is not complete.