Poutyne: a Keras-style training loop for PyTorch
A simplified framework and utilities for PyTorch
At a glance
- What is it?
- Poutyne wraps a PyTorch nn.Module in a Model object that owns the training loop, metrics and callbacks. It is a good fit for small to medium projects where you want Keras ergonomics without leaving PyTorch, and a poor fit if you need custom training semantics or do not want LGPL-3.0 in your dependency tree.
- Who is it for?
- Adopt Poutyne if you already write plain PyTorch and keep rewriting the same train/validate/test loop, and if LGPL-3.0 is acceptable for how you ship your code. Do not adopt it if your training step needs custom gradient manipulation, multi-optimizer schedules, or distributed launch semantics that the Model class does not expose.
- Can I use it commercially?
- Yes, with conditions. LGPL-3.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 101 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The boilerplate Poutyne removes from a PyTorch project
Anyone who has written more than a couple of PyTorch projects has written the same loop: iterate the DataLoader, move batches to a device, zero the gradients, run the forward pass, compute the loss, call backward, step the optimizer, then repeat the whole thing for validation with torch.no_grad(). Add metric accumulation, checkpointing and early stopping and the loop grows to a hundred lines that have nothing to do with the model itself. Poutyne's stated purpose is to handle that boilerplating. The README describes it as a simplified framework for PyTorch and says the core data structure is a Model, a way to train your own PyTorch neural networks. The intended audience is people who like PyTorch's module system and autograd but want the training entry point to look like Keras: define the network, hand it to a wrapper, call fit. The README notes Poutyne is compatible with the latest version of PyTorch and requires Python >= 3.10, so the supported surface is current rather than legacy. The project is not archived and the release history shows v1.19.0 and v1.18.0 published one day apart in June 2026, with v1.17.4 more than a year earlier, which suggests maintenance happens in bursts rather than on a fixed cadence.
How the Model class takes over the training loop
The mechanism is a wrapper, not a subclass of anything in torch. You build an ordinary nn.Module, as in the README example where a Sequential of Linear, ReLU and Linear is defined, and pass it as the first argument to poutyne.Model along with string names for the optimizer and loss. The README example passes 'sgd' and 'cross_entropy', plus batch_metrics=['accuracy'] and epoch_metrics=['f1', torchmetrics.AUROC(...)]. That distinction between batch and epoch metrics is worth noting: batch metrics are computed per batch and averaged, epoch metrics are computed over the whole epoch, and the README shows a torchmetrics object being accepted directly in the epoch_metrics list. A device argument takes a torch.device, and the README builds it with torch.device('cuda:%d' % cuda_device if torch.cuda.is_available() else 'cpu'). Calling model.fit(train_x, train_y, validation_data=(valid_x, valid_y), epochs=5, batch_size=32) runs the loop. The README example passes NumPy arrays rather than DataLoaders, so the wrapper is doing dataset conversion as well. Evaluation is symmetric: model.evaluate(test_x, test_y) returns the loss and a tuple of metric values, and model.predict(test_x) returns predictions. The design keeps the network a plain PyTorch object, so you can still inspect parameters, save state dicts or swap the module out; what you give up is control over the order of operations inside the loop.
Callbacks and ModelBundle: where the framework earns its place
Callbacks are the part of Poutyne the README singles out as a strength, describing them as the way to save checkpoints, log training statistics and more. The documented examples include saving the best model and performing early stopping, which are the two pieces of training code people most often reimplement badly. The callback list is not enumerated in the README, so the concrete set of hooks and which epoch events they fire on has to be read from poutyne/callbacks.py in the repository. ModelBundle sits one level above Model. The README example constructs it with ModelBundle.from_network('./saves/my_classification_network', network, optimizer='sgd', task='classif', device=device), then calls model_bundle.train_data(train_x, train_y, validation_data=(valid_x, valid_y), epochs=5) and model_bundle.test_data(test_x, test_y). The path argument is a directory, and the README says everything is saved there, with checkpointing and logging implemented through callbacks under the hood. The task='classif' string tells the bundle which default metrics and outputs to use, which is the main convenience and also the main constraint: you get the bundle's opinion about what a classification experiment looks like. Note that from_network takes an optimizer but no loss argument in the example, so the loss is presumably inferred from task; that inference is not spelled out in the README and should be confirmed in the experiment module before relying on it.
Installing Poutyne and the PyTorch version you must bring
The README is explicit that PyTorch must already be installed, and that it should be the latest version, before Poutyne goes in. The stable install is pip install poutyne, or uv add poutyne if you use uv. The development version comes from the dev branch: pip install -U git+https://github.com/freud14/poutyne.git@dev, or uv add git+https://github.com/freud14/poutyne.git@dev. There is also a container published as ghcr.io/freud14/poutyne:latest for people who want to develop on top of the provided image. Beyond the install commands, the README does not document configuration files or environment variables, because there are none in the examples shown: everything is passed as constructor or fit arguments. The only string keys visible in the material are the optimizer and loss names ('sgd', 'cross_entropy'), the metric names ('accuracy', 'f1'), the task string ('classif') and the directory path given to ModelBundle.from_network. If you need to know the full accepted set of optimizer and loss strings, that is a documentation lookup, not something the README answers.
Where Poutyne stops being the right tool
The wrapper controls the loop, and that is the limitation as much as the feature. If your training step is not loss.backward() followed by optimizer.step(), Poutyne's Model is working against you. Generative adversarial setups that alternate two optimizers, gradient penalty terms that need a second backward pass with retain_graph, gradient accumulation across several batches, or training loops that branch on batch content all require stepping outside the abstraction. The README does not show an override point for the training step, so the practical answer is that you either write a custom callback that hooks into the loop or you drop back to plain PyTorch for that project. A second constraint is dependency surface. Poutyne pulls in torchmetrics for the metric objects shown in the README example, so metric behaviour is torchmetrics' behaviour, not Poutyne's. A third is the release cadence: two releases a day apart in June 2026 after a year-long gap is a pattern that makes pinning a version sensible, since the API surface can move between minors. None of this makes Poutyne badly designed; it makes it a convenience layer with a boundary, and the boundary is wherever your loop stops looking like the standard one.
Poutyne against plain PyTorch and against PyTorch Lightning
The honest alternative for many projects is no framework at all. A hand-written loop of thirty lines gives you total control and zero new dependencies, and the README itself links an introduction notebook that compares Poutyne with bare PyTorch, so the project treats that comparison as the relevant one. The difference is that a hand-written loop must be rewritten for each project, while Poutyne's Model is configured. The other alternative is PyTorch Lightning, which the supplied material does not mention, so any comparison here is structural rather than documented. The visible difference in approach is that Lightning asks you to reorganize your code into a LightningModule with training_step, validation_step and configure_optimizers methods, inverting control so the framework calls your methods. Poutyne keeps your nn.Module untouched and puts the logic in a separate Model object, which means less restructuring of existing code and a smaller conceptual change. The trade-off is that Lightning's method-based structure is where its distributed and precision handling live, and Poutyne's README does not describe an equivalent. If you already have working PyTorch code and want a training entry point, Poutyne is the lighter edit. If you are starting fresh and expect to need multi-GPU launch or mixed precision, check what Poutyne exposes before choosing.
Licence and the cost of staying current
Poutyne is LGPL-3.0, which is a different obligation from the MIT or Apache-2.0 licence most PyTorch tooling carries. LGPL permits use in proprietary applications, but it attaches conditions to modification and to relinking, and those conditions are worth reading in full rather than taking from a summary. If you vendor Poutyne, patch it, or link it in a way your legal team reads as static, the analysis changes. This article is not legal advice; the point is that the licence is a real difference from the alternatives and belongs in the adoption decision, not in a footnote. On maintenance cost, the practical burden is version alignment. Poutyne targets the latest PyTorch, so a PyTorch upgrade is also a Poutyne upgrade question, and the release history shows the project can go a long time between releases and then publish two in two days. Pinning poutyne in your lockfile and reading the release notes before bumping is the low-effort hedge. The repository layout gives you what you need to judge stability yourself: poutyne/framework/model.py for the loop, poutyne/callbacks.py for the hooks, and the examples directory for runnable scripts covering classification, regression and the ModelBundle path.
Editorial conclusion
Adopt Poutyne if you already write plain PyTorch and keep rewriting the same train/validate/test loop, and if LGPL-3.0 is acceptable for how you ship your code. Do not adopt it if your training step needs custom gradient manipulation, multi-optimizer schedules, or distributed launch semantics that the Model class does not expose. Before committing, read poutyne/framework/model.py to see which hooks the loop calls, and check whether the callbacks you need already exist in poutyne/callbacks.py rather than writing them from scratch.
Community notes