Skip to content

Commit c6851b8

Browse files
ordabayevyfritzo
andauthored
PyTorch Lightning example (#3189)
* Bump to version 1.5.2 (#2755) * PyTorch Lightning example * fixes * fix test * update comments * fix pip install pyro-ppl * address comments * add svi_lightning to toctree --------- Co-authored-by: Fritz Obermeyer <fritz.obermeyer@gmail.com>
1 parent 9afb089 commit c6851b8

7 files changed

Lines changed: 153 additions & 1 deletion

File tree

examples/svi_horovod.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# https://horovod.readthedocs.io/en/stable
1313
#
1414
# This assumes you have installed horovod, e.g. via
15-
# pip install pyro[horovod]
15+
# pip install pyro-ppl[horovod]
1616
# For detailed instructions see
1717
# https://horovod.readthedocs.io/en/stable/install.html
1818
# On my mac laptop I was able to install horovod with

examples/svi_lightning.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Copyright Contributors to the Pyro project.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Distributed training via Pytorch Lightning.
5+
#
6+
# This tutorial demonstrates how to distribute SVI training across multiple
7+
# machines (or multiple GPUs on one or more machines) using the PyTorch Lightning
8+
# library. PyTorch Lightning enables data-parallel training by aggregating stochastic
9+
# gradients at each step of training. We focus on integration between PyTorch Lightning and Pyro.
10+
# For further details on distributed computing with PyTorch Lightning, see
11+
# https://lightning.ai/docs/pytorch/latest
12+
#
13+
# This assumes you have installed pytorch lightning, e.g. via
14+
# pip install pyro-ppl[lightning]
15+
16+
import argparse
17+
18+
import pytorch_lightning as pl
19+
import torch
20+
21+
import pyro
22+
import pyro.distributions as dist
23+
from pyro.infer import Trace_ELBO
24+
from pyro.infer.autoguide import AutoNormal
25+
from pyro.nn import PyroModule
26+
27+
28+
# We define a model as usual, with no reference to Pytorch Lightning.
29+
# This model is data parallel and supports subsampling.
30+
class Model(PyroModule):
31+
def __init__(self, size):
32+
super().__init__()
33+
self.size = size
34+
35+
def forward(self, covariates, data=None):
36+
coeff = pyro.sample("coeff", dist.Normal(0, 1))
37+
bias = pyro.sample("bias", dist.Normal(0, 1))
38+
scale = pyro.sample("scale", dist.LogNormal(0, 1))
39+
40+
# Since we'll use a distributed dataloader during training, we need to
41+
# manually pass minibatches of (covariates,data) that are smaller than
42+
# the full self.size. In particular we cannot rely on pyro.plate to
43+
# automatically subsample, since that would lead to all workers drawing
44+
# identical subsamples.
45+
with pyro.plate("data", self.size, len(covariates)):
46+
loc = bias + coeff * covariates
47+
return pyro.sample("obs", dist.Normal(loc, scale), obs=data)
48+
49+
50+
# We define an ELBO loss, a PyTorch optimizer, and a training step in our PyroLightningModule.
51+
# Note that we are using a PyTorch optimizer instead of a Pyro optimizer and
52+
# we are using ``training_step`` instead of Pyro's SVI machinery.
53+
class PyroLightningModule(pl.LightningModule):
54+
def __init__(self, loss_fn: pyro.infer.elbo.ELBOModule, lr: float):
55+
super().__init__()
56+
self.loss_fn = loss_fn
57+
self.model = loss_fn.model
58+
self.guide = loss_fn.guide
59+
self.lr = lr
60+
self.predictive = pyro.infer.Predictive(
61+
self.model, guide=self.guide, num_samples=1
62+
)
63+
64+
def forward(self, *args):
65+
return self.predictive(*args)
66+
67+
def training_step(self, batch, batch_idx):
68+
"""Training step for Pyro training."""
69+
loss = self.loss_fn(*batch)
70+
# Logging to TensorBoard by default
71+
self.log("train_loss", loss)
72+
return loss
73+
74+
def configure_optimizers(self):
75+
"""Configure an optimizer."""
76+
return torch.optim.Adam(self.loss_fn.parameters(), lr=self.lr)
77+
78+
79+
def main(args):
80+
# Create a model, synthetic data, a guide, and a lightning module.
81+
pyro.set_rng_seed(args.seed)
82+
pyro.settings.set(module_local_params=True)
83+
model = Model(args.size)
84+
covariates = torch.randn(args.size)
85+
data = model(covariates)
86+
guide = AutoNormal(model)
87+
loss_fn = Trace_ELBO()(model, guide)
88+
training_plan = PyroLightningModule(loss_fn, args.learning_rate)
89+
90+
# Create a dataloader.
91+
dataset = torch.utils.data.TensorDataset(covariates, data)
92+
dataloader = torch.utils.data.DataLoader(dataset, batch_size=args.batch_size)
93+
94+
# All relevant parameters need to be initialized before ``configure_optimizer`` is called.
95+
# Since we used AutoNormal guide our parameters have not be initialized yet.
96+
# Therefore we initialize the model and guide by running one mini-batch through the loss.
97+
mini_batch = dataset[: args.batch_size]
98+
loss_fn(*mini_batch)
99+
100+
# Run stochastic variational inference using PyTorch Lightning Trainer.
101+
trainer = pl.Trainer.from_argparse_args(args)
102+
trainer.fit(training_plan, train_dataloaders=dataloader)
103+
104+
105+
if __name__ == "__main__":
106+
assert pyro.__version__.startswith("1.8.4")
107+
parser = argparse.ArgumentParser(
108+
description="Distributed training via PyTorch Lightning"
109+
)
110+
parser.add_argument("--size", default=1000000, type=int)
111+
parser.add_argument("--batch_size", default=100, type=int)
112+
parser.add_argument("--learning_rate", default=0.01, type=float)
113+
parser.add_argument("--seed", default=20200723, type=int)
114+
parser = pl.Trainer.add_argparse_args(parser)
115+
args = parser.parse_args()
116+
main(args)

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
"yapf",
138138
],
139139
"horovod": ["horovod[pytorch]>=0.19"],
140+
"lightning": ["pytorch_lightning"],
140141
"funsor": [
141142
# This must be a released version when Pyro is released.
142143
# "funsor[torch] @ git+git://github.com/pyro-ppl/funsor.git@7bb52d0eae3046d08a20d1b288544e1a21b4f461",

tests/common.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ def wrapper(*args, **kwargs):
6868
horovod is None, reason="horovod is not available"
6969
)
7070

71+
try:
72+
import pytorch_lightning
73+
except ImportError:
74+
pytorch_lightning = None
75+
requires_lightning = pytest.mark.skipif(
76+
pytorch_lightning is None, reason="pytorch lightning is not available"
77+
)
78+
7179
try:
7280
import funsor
7381
except ImportError:

tests/test_examples.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
requires_cuda,
1515
requires_funsor,
1616
requires_horovod,
17+
requires_lightning,
1718
xfail_param,
1819
)
1920

@@ -110,6 +111,10 @@
110111
"sparse_gamma_def.py --num-epochs=2 --eval-particles=2 --eval-frequency=1 --guide auto",
111112
"sparse_gamma_def.py --num-epochs=2 --eval-particles=2 --eval-frequency=1 --guide easy",
112113
"svi_horovod.py --num-epochs=2 --size=400 --no-horovod",
114+
pytest.param(
115+
"svi_lightning.py --max_epochs=2 --size=400 --accelerator cpu --devices 1",
116+
marks=[requires_lightning],
117+
),
113118
"toy_mixture_model_discrete_enumeration.py --num-steps=1",
114119
"sparse_regression.py --num-steps=100 --num-data=100 --num-dimensions 11",
115120
"vae/ss_vae_M2.py --num-epochs=1",
@@ -177,6 +182,10 @@
177182
"sir_hmc.py -t=2 -w=2 -n=4 -d=2 -p=10000 --sequential --cuda",
178183
"sir_hmc.py -t=2 -w=2 -n=4 -d=100 -p=10000 --cuda",
179184
"svi_horovod.py --num-epochs=2 --size=400 --cuda --no-horovod",
185+
pytest.param(
186+
"svi_lightning.py --max_epochs=2 --size=400 --accelerator gpu --devices 1",
187+
marks=[requires_lightning],
188+
),
180189
"vae/vae.py --num-epochs=1 --cuda",
181190
"vae/ss_vae_M2.py --num-epochs=1 --cuda",
182191
"vae/ss_vae_M2.py --num-epochs=1 --aux-loss --cuda",

tutorial/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ List of Tutorials
9696
prior_predictive
9797
jit
9898
svi_horovod
99+
svi_lightning
99100

100101
.. toctree::
101102
:maxdepth: 1

tutorial/source/svi_lightning.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Example: distributed training via PyTorch Lightning
2+
===================================================
3+
4+
This script passes argparse arguments to PyTorch Lightning ``Trainer`` automatically_, for example::
5+
6+
$ python examples/svi_lightning.py --accelerator gpu --devices 2 --max_epochs 100 --strategy ddp
7+
8+
.. _automatically: https://pytorch-lightning.readthedocs.io/en/stable/common/trainer.html#trainer-in-python-scripts
9+
10+
`View svi_lightning.py on github`__
11+
12+
.. _github: https://github.com/pyro-ppl/pyro/blob/dev/examples/svi_lightning.py
13+
14+
__ github_
15+
16+
.. literalinclude:: ../../examples/svi_lightning.py
17+
:language: python

0 commit comments

Comments
 (0)