train on pytorch lightning - #150
Open
selmanozleyen wants to merge 1 commit into
Open
Conversation
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
Merged
Collaborator
|
I would stack this on top of #149 too :) |
selmanozleyen
force-pushed
the
feat/lightning-trainer
branch
from
August 3, 2026 13:21
870af03 to
3f220fe
Compare
selmanozleyen
force-pushed
the
feat/flatten_backends_module
branch
from
August 3, 2026 13:35
0137697 to
586ffd4
Compare
selmanozleyen
force-pushed
the
feat/lightning-trainer
branch
2 times, most recently
from
August 3, 2026 13:44
4b1d5bb to
55e3ca3
Compare
selmanozleyen
changed the base branch from
feat/flatten_backends_module
to
feat/simplify_methods
August 3, 2026 13:54
selmanozleyen
force-pushed
the
feat/lightning-trainer
branch
from
August 3, 2026 15:23
55e3ca3 to
e4dc8b5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #149 (which is stacked on #148) — review those first. This PR's own commit is
3f220fe; everything below is relative tofeat/simplify_methods.Replaces the hand-rolled training loop with PyTorch Lightning. Net effect on
src/is a washin line count (+662 / -663) — the interesting part is what stopped existing.
What Lightning now owns
Trainer.train()— manual step loop, tqdm bar, log accumulation,valid_freqmodulopl.Trainer(max_steps=…, val_check_interval=…)BaseCallback/ComputationalCallback/LoggingCallback/TrainingCallbackspl.Callback+self.log()WandBLoggerlightning.pytorch.loggers.WandbLoggerOptimizationManagerconfigure_optimizers()set_train_mode,_device_id/_dtype,Model.to_device's optimizer-state walkAnd it brings things that simply did not exist before: checkpointing and resume, AMP, gradient
clipping and accumulation, early stopping, LR monitoring, deterministic seeding, multi-device.
Node-steps
The train sampler used to hand back a tuple of
n_nodesnodes per call, and the loop tookone optimizer step per node inside that tuple. So the gradient schedule was already
per-node; only the counting was per-round.
TrainSampler.__iter__now yields one node at a time, and a node is one batch and oneoptimizer step. Rounds are still drawn
n_nodesat a time soreplace_nodes=Falsekeepsmeaning "distinct nodes within a round". The stream is unbounded and re-iterable —
max_iter_stepsnow defaults toNoneand the trainer'sn_train_stepsgoverns run length.Migration: a run that used
n_train_steps=Nwithn_nodes=know needsn_train_steps=N * k. With the defaultn_nodes=1nothing changes.Samplers are passed to
fit()as plain iterables — Lightning accepts them directly, so thereis no
DataLoaderlayer anddata/stays torch-free.Following scvi-tools, and where it doesn't
scvi-tools wraps its module in a separate
TrainingPlan(LightningModule). Their reason is thatone module gets trained by many plans — 13+ plan classes, selected via
_training_plan_cls,swapped at runtime for TOTALANVI, plus Pyro's SVI which needs a different optimization scheme
entirely. sckitflow has exactly one training recipe and every method is
one-loss-one-optimizer-backward, so
BaseMethodis theLightningModuleand there is noplan layer. If a second recipe ever shows up, extracting a plan is mechanical.
The two scvi layers that do earn their keep are kept:
Trainer(pl.Trainer)— a thin subclass with sckitflow defaults (node-step semantics,check_val_every_n_epoch=None, no checkpointing,log_every_n_steps=1), mirroring theirTrainer.DataFrameLogger— an in-memorypl.Logger, mirroring theirSimpleLogger, soget_train_logs_df()/get_val_logs_df()keep working with zero configuration. Passlogger=to also ship to CSV/W&B; it is composed alongside, not replaced.Two things worth a second look
Frozen dataclasses. Nodes are trees of frozen dataclasses wrapping numpy arrays, and
Lightning's default recursive device transfer refuses to walk them (
ValueError: A frozen dataclass was passed to apply_to_collection).transfer_batch_to_deviceis overridden toleave the node on the host;
extract_step_dataalready does the tensor conversion andplacement per step.
Per-validation-set metric state.
MetricsCallbackgives each validation dataloader its owncopy of every metric. The copies are cloned from the pristine template rather than from another
dataloader's — cloning from an already-updated metric silently pooled the two sets' state.
There is a regression test for it.
Breaking
The method contract renamed in #149 (
compute_loss,infer) is unchanged here. On top of that:pl.Callbacksubclasses._runtime.pylost its backend switch.device_iddefaults toNone(leave in place) instead of eagerly selecting CUDA — thetrainer's accelerator decides. Pass
accelerator=toModel.train.Model.trainno longer forwards*args/**kwargstotrain_step; extra kwargs go to thetrainer. Validation-time prediction arguments move to
val_predict_kwargs(e.g.n_samplesfor noise-generating methods).
Model.saveno longer pickles the trainer, so logs and optimizer state do not survive around trip. Use
ModelCheckpointfor those.OptimConfiglost its unusedplan_kwargs, gainedlr_scheduler_monitor, and now resolvesitself into the mapping
configure_optimizersreturns.lr_scheduler_stepstill accepts"train_step".Verification
.run_notebooks.sh.pre-commit run --all-filesclean.CFMthroughModel.trainwith two validation sets andEnergyDistance/MaximumMeanDiscrepancy: per-step train losses, per-val-id metric frames,and
predictall check out..test_durationsregenerated and remapped onto flatten backends module #148'stests/core/layout; all 51 entriesresolve to files that exist, and
--splits 2verified.New tests cover the parts that carry the semantics: one-node-one-optimizer-step, validation
cadence in node-steps, per-node sampler iteration (lazy, unbounded, re-iterable),
DataFrameLoggertrain/val routing, and the metric-isolation regression.