EzTrain
A small, framework-agnostic training-loop library. The trainer companion to EzConfy.
Every ML project rewrites the same trainer: a loop over epochs or updates,
periodic evaluation, callbacks, early stopping, checkpoint scheduling, metric
logging, graceful Ctrl+C. EzTrain extracts exactly that skeleton and
nothing else:
- Core never imports torch or jax. It abstracts the iteration, not the
tensor: an iteration is anything that returns a
Mapping[str, Any]of metrics — an epoch over a dataloader, an RL collect→update cycle, a world-model phase. - Run lifecycle built in. A run id (
<name>_<timestamp>) is shared between checkpoints and the experiment tracker, with three resume modes: FRESH (from scratch), CONTINUE (same run: weights + optimizer + tracker), FORK (new run seeded with old weights). - Composition over inheritance where it matters:
Logger,MetricandCheckpointerare Protocols you inject;Callbackhooks are dispatched dynamically by name so trainers can invent their own hooks. - No hidden defaults. No optimizer/scheduler construction, no concrete metrics, no config system (that's EzConfy's job), no distributed magic.
Core dependencies: tqdm, loguru. That's it.
Install
uv add eztrain # core
uv add "eztrain[wandb]" # + Weights & Biases logger
uv add "eztrain[tensorboard]" # + TensorBoard logger
Supervised (epoch-based)
from eztrain import EpochTrainer, EarlyStopping, MetricCollection, WandbLogger
class MyTrainer(EpochTrainer):
def __init__(self, *, model, optimizer, **kwargs):
super().__init__(**kwargs)
self.model, self.optimizer = model, optimizer
def train_step(self, batch):
loss = ... # your forward/backward/step
return {"loss": loss.item()} # averaged over the epoch -> "train/loss"
def eval_step(self, batch):
preds, loss = ...
self.metrics.update(preds, batch.y) # your metrics, your signature
return {"loss": loss.item()} # -> "val/loss"
trainer = MyTrainer(
model=model,
optimizer=optimizer,
train_loader=train_loader,
val_loader=val_loader,
metrics=MetricCollection([MyAccuracy()]),
max_iterations=100, # epochs
eval_freq=1,
callbacks=[EarlyStopping(monitor="val/loss", patience=10)],
logger=WandbLogger(project="my-project"),
run_name="baseline",
)
trainer.fit()
RL (update-based)
Subclass Trainer directly — one iteration is one update:
from eztrain import Trainer
class PPOTrainer(Trainer):
def __init__(self, *, env, agent, num_steps, **kwargs):
super().__init__(unit="update", **kwargs)
self.env, self.agent, self.num_steps = env, agent, num_steps
self.obs = env.reset()
def train_iteration(self, update):
segment, self.obs = collect_rollouts(self.env, self.agent, self.num_steps, self.obs)
advantages, returns = compute_gae(segment)
return self.agent.learn_from(segment, advantages, returns)
def evaluate(self):
return {"eval/reward": evaluate(self.agent)}
def log_step(self): # log by env steps, not updates
return self.iteration * self.num_steps * self.env.num_envs
Resume and fork
Trainer(run_name="exp-1") # FRESH
Trainer(run_name="exp-1", resume_from="exp-1_20260530_051406") # CONTINUE
Trainer(run_name="exp-2", resume_from="exp-1_20260530_051406") # FORK
Pass resume_from to start from an existing run's checkpoint:
- Same
run_name-> the run continues; - a new
run_name-> it forks into a new run.
Callbacks
from eztrain import Callback
class MyCallback(Callback):
def on_train_start(self, trainer): ...
def on_iteration_end(self, trainer, iteration): ...
def on_eval_end(self, trainer): ... # trainer.history has fresh metrics
def on_train_end(self, trainer): ... # always runs, even on Ctrl+C
The stable surface callbacks can rely on: trainer.run, trainer.iteration,
trainer.start_iteration (writable), trainer.history,
trainer.should_stop (writable), trainer.checkpointables,
trainer.logger, trainer.call_hook. Custom hooks compose freely:
self.call_hook("on_rollout_end", num_steps=n) inside your trainer reaches
any callback that defines it.
With EzConfy
Every public class takes plain keyword arguments, so it can be instantiated straight from YAML:
# schema.yaml
types:
Callback: eztrain.callbacks:Callback
Logger: eztrain.loggers:Logger
schema:
trainer:
callbacks: list[Callback]
logger: Logger
# config.yaml
trainer:
logger:
_target_type_: eztrain.loggers:WandbLogger
_init_args_: { project: my-project, entity: me }
callbacks:
- _target_type_: eztrain.callbacks:EarlyStopping
_init_args_: { monitor: val/loss, mode: min, patience: 10 }
What's deliberately not here
Concrete train steps, models, losses, optimizers/schedulers (inject your own — a library default becomes a cage), metrics implementations, config loading, distributed training. Keep those in your project; eztrain only owns the loop around them.
Metadata
Release files for eztrain 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| eztrain-0.1.2.tar.gz | 130.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| eztrain-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 145.2 kB
Release files / eztrain-0.1.2.tar.gz
| Download URL | eztrain-0.1.2.tar.gz |
|---|---|
| Size | 130.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5d5f995b45b8f06781c190a9e2118b86ba7711bb5b9e30ceb16891459e970c44
|
|
BLAKE2b-256 checksum How to use checksums |
3490e74c7beded1227665dd2ec562426e6021710e34f94d82246e922b608c4a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logRelease files / eztrain-0.1.2-py3-none-any.whl
| Download URL | eztrain-0.1.2-py3-none-any.whl |
|---|---|
| Size | 14.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8af9f0337bec183ed287bf2c5a9c8dd34f01519fe6450442aa1babd99aef9c2c
|
|
BLAKE2b-256 checksum How to use checksums |
8964495d9ed405431f8d2bb51f9f67460747dcfc27a4bef57717d3f168f959ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency log