mirror of
https://github.com/wassname/pytorch-lightning.git
synced 2026-09-12 12:40:20 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ed5e657e1 | ||
|
|
7da133d91d | ||
|
|
3f76152470 | ||
|
|
d98b9f2f93 | ||
|
|
f6416f737d | ||
|
|
3888825333 | ||
|
|
0479784e7b | ||
|
|
7e053fc731 | ||
|
|
7ac344e43a | ||
|
|
f6b98fe74f | ||
|
|
25f5491ac7 | ||
|
|
df77f5042b | ||
|
|
d273271b4b | ||
|
|
babaa088d7 | ||
|
|
8217ebe029 | ||
|
|
9311812829 |
@@ -3,6 +3,26 @@ Lightning makes multi-gpu training and 16 bit training trivial.
|
|||||||
*Note:*
|
*Note:*
|
||||||
None of the flags below require changing anything about your lightningModel definition.
|
None of the flags below require changing anything about your lightningModel definition.
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Choosing a backend
|
||||||
|
Lightning supports two backends. DataParallel and DistributedDataParallel. Both can be used for single-node multi-GPU training.
|
||||||
|
For multi-node training you must use DistributedDataParallel.
|
||||||
|
|
||||||
|
You can toggle between each mode by setting this flag.
|
||||||
|
``` {.python}
|
||||||
|
# DEFAULT uses DataParallel
|
||||||
|
trainer = Trainer(distributed_backend='dp')
|
||||||
|
|
||||||
|
# change to distributed data parallel
|
||||||
|
trainer = Trainer(distributed_backend='ddp')
|
||||||
|
```
|
||||||
|
|
||||||
|
If you request multiple nodes, the back-end will auto-switch to ddp.
|
||||||
|
We recommend you use DistributedDataparallel even for single-node multi-GPU training. It is MUCH faster than DP but *may*
|
||||||
|
have configuration issues depending on your cluster.
|
||||||
|
|
||||||
|
For a deeper understanding of what lightning is doing, feel free to read [this guide](https://medium.com/@_willfalcon/9-tips-for-training-lightning-fast-neural-networks-in-pytorch-8e63a502f565).
|
||||||
|
|
||||||
---
|
---
|
||||||
#### 16-bit mixed precision
|
#### 16-bit mixed precision
|
||||||
16 bit precision can cut your memory footprint by half. If using volta architecture GPUs it can give a dramatic training speed-up as well.
|
16 bit precision can cut your memory footprint by half. If using volta architecture GPUs it can give a dramatic training speed-up as well.
|
||||||
@@ -37,16 +57,69 @@ Make sure you're on a GPU machine. You can set as many GPUs as you want.
|
|||||||
In this setting, the model will run on all 8 GPUs at once using DataParallel under the hood.
|
In this setting, the model will run on all 8 GPUs at once using DataParallel under the hood.
|
||||||
```python
|
```python
|
||||||
# set these flags
|
# set these flags
|
||||||
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
|
# lightning sets these flags for you automatically
|
||||||
os.environ["CUDA_VISIBLE_DEVICES"] = "0,1,2,3,4,5,6,7"
|
# no need to set yourself
|
||||||
|
# os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
|
||||||
|
# os.environ["CUDA_VISIBLE_DEVICES"] = "0,1,2,3,4,5,6,7"
|
||||||
|
|
||||||
# DEFAULT
|
|
||||||
trainer = Trainer(gpus=[0,1,2,3,4,5,6,7])
|
# to use DataParallel (default)
|
||||||
|
trainer = Trainer(gpus=[0,1,2,3,4,5,6,7], distributed_backend='dp')
|
||||||
|
|
||||||
|
# RECOMMENDED use DistributedDataParallel
|
||||||
|
trainer = Trainer(gpus=[0,1,2,3,4,5,6,7], distributed_backend='ddp')
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
#### Multi-node
|
#### Multi-node
|
||||||
COMING SOON.
|
Multi-node training is easily done by specifying these flags.
|
||||||
|
```python
|
||||||
|
# train on 12*8 GPUs
|
||||||
|
trainer = Trainer(gpus=[0,1,2,3,4,5,6,7], nb_gpu_nodes=12)
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition, make sure to set up your SLURM job correctly via the [SlurmClusterObject](https://williamfalcon.github.io/test-tube/hpc/SlurmCluster/). In particular, specify the number of tasks per node correctly.
|
||||||
|
|
||||||
|
```python
|
||||||
|
cluster = SlurmCluster(
|
||||||
|
hyperparam_optimizer=test_tube.HyperOptArgumentParser(),
|
||||||
|
log_path='/some/path/to/save',
|
||||||
|
)
|
||||||
|
|
||||||
|
# OPTIONAL FLAGS WHICH MAY BE CLUSTER DEPENDENT
|
||||||
|
# which interface your nodes use for communication
|
||||||
|
cluster.add_command('export NCCL_SOCKET_IFNAME=^docker0,lo')
|
||||||
|
|
||||||
|
# see output of the NCCL connection process
|
||||||
|
# NCCL is how the nodes talk to each other
|
||||||
|
cluster.add_command('export NCCL_DEBUG=INFO')
|
||||||
|
|
||||||
|
# setting a master port here is a good idea.
|
||||||
|
cluster.add_command(f'export MASTER_PORT={PORT}')
|
||||||
|
|
||||||
|
# good to load the latest NCCL version
|
||||||
|
cluster.load_modules(['NCCL/2.4.7-1-cuda.10.0'])
|
||||||
|
|
||||||
|
# configure cluster
|
||||||
|
cluster.per_experiment_nb_nodes = 12
|
||||||
|
cluster.per_experiment_nb_gpus = 8
|
||||||
|
|
||||||
|
cluster.add_slurm_cmd(cmd='ntasks-per-node', value=8, comment='1 task per gpu')
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, make sure to add a distributed sampler to your dataset. The distributed sampler copies a
|
||||||
|
portion of your dataset onto each GPU. (World_size = gpus_per_node * nb_nodes).
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ie: this:
|
||||||
|
dataset = myDataset()
|
||||||
|
dataloader = Dataloader(dataset)
|
||||||
|
|
||||||
|
# becomes:
|
||||||
|
dataset = myDataset()
|
||||||
|
dist_sampler = torch.utils.data.distributed.DistributedSampler(dataset)
|
||||||
|
dataloader = Dataloader(dataset, sampler=dist_sampler)
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
#### Self-balancing architecture
|
#### Self-balancing architecture
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ class Trainer(TrainerIO):
|
|||||||
self.use_ddp = False
|
self.use_ddp = False
|
||||||
self.use_dp = False
|
self.use_dp = False
|
||||||
|
|
||||||
|
|
||||||
# gpus come in as a string.
|
# gpus come in as a string.
|
||||||
# if gpus = -1 then use all available devices
|
# if gpus = -1 then use all available devices
|
||||||
# otherwise, split the string using commas
|
# otherwise, split the string using commas
|
||||||
@@ -176,6 +177,15 @@ class Trainer(TrainerIO):
|
|||||||
self.use_dp = distributed_backend == 'dp'
|
self.use_dp = distributed_backend == 'dp'
|
||||||
self.use_ddp = distributed_backend == 'ddp'
|
self.use_ddp = distributed_backend == 'ddp'
|
||||||
|
|
||||||
|
# use ddp automatically if nb_gpu_nodes > 1
|
||||||
|
if nb_gpu_nodes > 1 and self.use_dp:
|
||||||
|
self.use_ddp = True
|
||||||
|
self.use_dp = False
|
||||||
|
w = 'DataParallel does not support nb_gpu_nodes > 1. ' \
|
||||||
|
'Switching to DistributedDataParallel for you. ' \
|
||||||
|
'To silence this warning set distributed_backend=ddp'
|
||||||
|
warnings.warn(w)
|
||||||
|
|
||||||
# process info
|
# process info
|
||||||
self.proc_rank = 0
|
self.proc_rank = 0
|
||||||
|
|
||||||
@@ -383,9 +393,10 @@ class Trainer(TrainerIO):
|
|||||||
|
|
||||||
# whenever we have the correct number of tasks, we let slurm manage processes
|
# whenever we have the correct number of tasks, we let slurm manage processes
|
||||||
# otherwise we launch the required number of processes
|
# otherwise we launch the required number of processes
|
||||||
|
nb_requested_gpus = len(self.data_parallel_device_ids) * self.nb_gpu_nodes
|
||||||
|
nb_slurm_tasks = 0
|
||||||
try:
|
try:
|
||||||
nb_slurm_tasks = int(os.environ['SLURM_NTASKS'])
|
nb_slurm_tasks = int(os.environ['SLURM_NTASKS'])
|
||||||
nb_requested_gpus = len(self.data_parallel_device_ids) * self.nb_gpu_nodes
|
|
||||||
is_slurm_managing_tasks = nb_slurm_tasks == nb_requested_gpus
|
is_slurm_managing_tasks = nb_slurm_tasks == nb_requested_gpus
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# likely not on slurm, so set the slurm managed flag to false
|
# likely not on slurm, so set the slurm managed flag to false
|
||||||
@@ -764,6 +775,11 @@ class Trainer(TrainerIO):
|
|||||||
else:
|
else:
|
||||||
loss.backward()
|
loss.backward()
|
||||||
|
|
||||||
|
# insert after step hook
|
||||||
|
if self.__is_function_implemented('on_after_backward'):
|
||||||
|
model_ref = self.__get_model()
|
||||||
|
response = model_ref.on_after_backward()
|
||||||
|
|
||||||
if self.print_nan_grads:
|
if self.print_nan_grads:
|
||||||
model = self.__get_model()
|
model = self.__get_model()
|
||||||
for param in model.parameters():
|
for param in model.parameters():
|
||||||
@@ -784,6 +800,11 @@ class Trainer(TrainerIO):
|
|||||||
for optimizer in self.optimizers:
|
for optimizer in self.optimizers:
|
||||||
optimizer.step()
|
optimizer.step()
|
||||||
|
|
||||||
|
# insert after step hook
|
||||||
|
if self.__is_function_implemented('on_before_zero_grad'):
|
||||||
|
model_ref = self.__get_model()
|
||||||
|
response = model_ref.on_before_zero_grad(optimizer)
|
||||||
|
|
||||||
# clear gradients
|
# clear gradients
|
||||||
optimizer.zero_grad()
|
optimizer.zero_grad()
|
||||||
|
|
||||||
|
|||||||
@@ -22,3 +22,24 @@ class ModelHooks(torch.nn.Module):
|
|||||||
def on_tng_metrics(self, metrics):
|
def on_tng_metrics(self, metrics):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def on_before_zero_grad(self, optimizer):
|
||||||
|
"""
|
||||||
|
Called after optimizer.step() and before optimizer.zero_grad()
|
||||||
|
|
||||||
|
for optimizer in optimizers:
|
||||||
|
optimizer.step()
|
||||||
|
model.on_before_zero_grad(optimizer) # < ---- called here
|
||||||
|
optimizer.zero_grad
|
||||||
|
|
||||||
|
:param optimizer:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_after_backward(self):
|
||||||
|
"""
|
||||||
|
Called after loss.backward() and before optimizers do anything
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from setuptools import setup, find_packages
|
|||||||
# http://blog.ionelmc.ro/2014/05/25/python-packaging/
|
# http://blog.ionelmc.ro/2014/05/25/python-packaging/
|
||||||
setup(
|
setup(
|
||||||
name="pytorch-lightning",
|
name="pytorch-lightning",
|
||||||
version='0.3',
|
version='0.3.4',
|
||||||
description="The Keras for ML researchers using PyTorch",
|
description="The Keras for ML researchers using PyTorch",
|
||||||
author="William Falcon",
|
author="William Falcon",
|
||||||
author_email="waf2107@columbia.edu",
|
author_email="waf2107@columbia.edu",
|
||||||
|
|||||||
Reference in New Issue
Block a user