Source:
training/checkpoint.py,training/scheduler.py,training/validation.py,training/benchmark.py,training/data_loader.py
This is a quick-reference companion to training.md, focused on the utility modules. See training.md for the full dual-optimizer / WSD / benchmark narrative.
save_checkpoint(model, muon_opt, adamw_opt, scheduler, step, token_count, best_loss, save_dir, max_keep, tag)
- Creates
save_dir/<tag>/(tag defaults tof"step_{step}"). - Model:
model.state_dict()cast tobfloat16→model.safetensorsvia_save_safetensors(falls back totorch.save(..., .pt)ifsafetensorsis not installed). - Optimizers:
{"muon": muon_opt.state_dict(), "adamw": adamw_opt.state_dict()}→optimizer.ptviatorch.save. - Metadata:
{step, token_count, best_loss, scheduler.state_dict(), model_config}→metadata.json(passed through_make_serializablewhich recursively converts non-JSON-serializable objects tostr). - Calls
_cleanup_old_checkpoints(save_dir, max_keep)to remove oldstep_*directories beyondmax_keep.
- Loads
model.safetensors(or.ptfallback) into the model. - Loads
optimizer.pt(viatorch.load(..., weights_only=True)) intomuon_optandadamw_optif present. - Loads
metadata.jsonand restoresscheduler.load_state_dict(...)if present. - Returns the metadata dict.
Scans subdirectories of save_dir, reads each metadata.json's step,
and returns the directory with the highest step (or None).
Thin wrappers around safetensors.torch.save_file / load_file with a
torch.save/torch.load fallback. Used so the checkpoint code works
without safetensors installed (BF16 is still preserved on the
torch.save path).
Recursively walks a dict/list and converts anything that is not
int|float|str|bool|None to str(...). Used so the metadata JSON can
hold the scheduler state dict (which may contain tensors) without
crashing json.dump.
Sorts step_* directories by step number descending and rmtrees
everything beyond max_keep.
Subclass of torch.optim.lr_scheduler._LRScheduler. See
training.md for the phase
table. Implementation notes:
- Accepts a single optimizer or a list/tuple of optimizers
(
self._optimizers). The primary optimizer is passed to the parent_LRScheduler.__init__; the others are stepped viastep_optimizers()which propagatesget_lr()[0]to their param groups. get_lr()returns[base_lr * factor for base_lr in self.base_lrs]wherefactordepends on the phase (linear warmup, constant stable, linear/cosine decay tomin_lr_ratio, clamped atmin_lr_ratioaftertotal_steps).decayis asserted to be"linear"or"cosine".
generate_synthetic_batch(batch_size, seq_len, vocab_size, device): returns random(tokens, targets)in[0, vocab_size).compute_validation_loss(model, batch_size, seq_len, vocab_size, num_batches, device)(@torch.no_grad): runsnum_batchesforward passes in eval mode, sumsF.cross_entropy(..., reduction="sum"), returns{"loss": avg, "ppl": exp(avg), "n_tokens": total}. Togglesmodel.train()back on at the end.validate_forward_shape(model, batch_size, seq_len, device)(@torch.no_grad): asserts the model output is(batch_size, seq_len, 64000)and logs the shape.
CLI entry point (python training/benchmark.py --steps 100). See
training.md for the full narrative.
Key points:
get_config()returns the A100-optimized config withmtp_depth=0(MTP disabled for the benchmark),micro_batch_size=4,gradient_accumulation_steps=8,use_compile=True,compile_mode="reduce-overhead",use_checkpoint_per_layer=True,wandb_enabled=False.create_mock_data_iter(batch_size, seq_len, vocab_size, device): an infinite generator yielding(tokens, targets)withtargets = tokens.clone().benchmark(steps): builds the model on CUDA (or CPU with a warning), optionallytorch.compiles it, runs 10 warmup steps (graph capture) followed bystepsmeasured steps, and prints total tokens, elapsed seconds, throughput, and estimated training time for 8.31B tokens.- Uses
torch.cuda.amp.autocast(dtype=torch.bfloat16)on CUDA and a disabledtorch.autocaston CPU.
Wraps a base (tokens, targets) iterator. See
training.md for the
narrative. Implementation notes:
__init__storesdata_iter, device, prefetch_factor, num_workers, pin_memory, batch_size, seq_len, vocab_sizeand aNone_prefetch_queue._create_prefetch_iterator()is a generator that pulls fromself.data_iter; ifpin_memoryand the tensor is on CPU, it calls.pin_memory()on both tokens and targets before yielding.__iter__pulls from the prefetch iterator and does.to(device, non_blocking=True)on both tensors.benchmark(num_batches)runsnum_batchesiterations and returns{"batches_per_sec", "tokens_per_sec", "elapsed_sec"}wheretokens_per_sec = num_batches * batch_size * seq_len / elapsed.
Note:
prefetch_factorandnum_workersare stored but not currently used for background workers — the prefetching is the pinned-memory + non-blocking-transfer pattern above. They are kept on the API for future background-thread prefetching.