From 64f08a8963398ef9e669fc29294f42cc53043753 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Mon, 3 Jun 2024 15:37:57 -0300 Subject: [PATCH] multigpu training support v0.0.5 --- README.md | 113 +++++++++++++++++- build/cuda.cu.o | Bin 220752 -> 220752 bytes build/distributed.o | Bin 188432 -> 187696 bytes examples/train_multigpu.py | 93 ++++++++++++++ .../{train.ipynb => train_nn_mnist.ipynb} | 0 examples/train_singlegpu.py | 71 +++++++++++ norch/__init__.py | 2 +- norch/csrc/distributed.cpp | 13 -- norch/distributed/distributed.py | 7 -- norch/libtensor.so | Bin 401240 -> 401192 bytes .../optimizers/__pycache__/sgd.cpython-38.pyc | Bin 1274 -> 1270 bytes setup.py | 2 +- 12 files changed, 274 insertions(+), 27 deletions(-) create mode 100644 examples/train_multigpu.py rename examples/{train.ipynb => train_nn_mnist.ipynb} (100%) create mode 100644 examples/train_singlegpu.py diff --git a/README.md b/README.md index a6491be..d6eb1bb 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # PyNorch -Recreating PyTorch from scratch (C/C++, CUDA and Python, with GPU support and automatic differentiation!) +Recreating PyTorch from scratch (C/C++, CUDA and Python, with multi-GPU support and automatic differentiation!) Project details explanations can also be found on [medium](https://towardsdatascience.com/recreating-pytorch-from-scratch-with-gpu-support-and-automatic-differentiation-8f565122a3cc). # 1 - About -**PyNorch** is a deep learning framework constructed using C/C++, CUDA and Python. This is a personal project with educational purpose only! `Norch` means **NOT** PyTorch, and we have **NO** claims to rivaling the already established PyTorch. The main objective of **PyNorch** was to give a brief understanding of how a deep learning framework works internally. It implements the Tensor object, GPU support and an automatic differentiation system. +**PyNorch** is a deep learning framework constructed using C/C++, CUDA and Python. This is a personal project with educational purpose only! `Norch` means **NOT** PyTorch, and we have **NO** claims to rivaling the already established PyTorch. The main objective of **PyNorch** was to give a brief understanding of how a deep learning framework works internally. It implements the Tensor object, multi-GPU support and an automatic differentiation system. # 2 - Installation -Install this package from PyPi (you can test on Colab!) +Install this package from PyPi (you can test on Colab! Also tested on AWS g4dn.12xlarge instance with image ami-061debf863768593d) ```css $ pip install norch @@ -60,8 +60,10 @@ class MyModel(nn.Module): return out ``` -### 3.3 - Example training +### 3.3 - Example single GPU training ```python +# examples/train_singlegpu.py + import norch from norch.utils.data.dataloader import DataLoader from norch.norchvision import transforms as T @@ -135,6 +137,107 @@ for epoch in range(epochs): ``` +### 3.4 - Example multi-GPU training +First create a file .py as the example below + +```python +# examples/train_multigpu.py + +import os +import norch +import norch.distributed as dist +import norch.distributed +import norch.nn as nn +import norch.optim as optim +from norch.nn.parallel import DistributedDataParallel +from norch.utils.data.distributed import DistributedSampler +from norch.norchvision import transforms as T +import random +random.seed(1) + +local_rank = int(os.getenv('OMPI_COMM_WORLD_LOCAL_RANK', -1)) +rank = int(os.getenv('OMPI_COMM_WORLD_RANK', -1)) +world_size = int(os.getenv('OMPI_COMM_WORLD_SIZE', -1)) + +dist.init_process_group( + rank, + world_size +) + +BATCH_SIZE = 32 +device = local_rank +epochs = 10 + +transform = T.Compose( + [ + T.ToTensor(), + T.Reshape([-1, 784, 1]) + ] +) + +target_transform = T.Compose( + [ + T.ToTensor() + ] +) + +train_data, test_data = norch.norchvision.datasets.MNIST.splits(transform=transform, target_transform=target_transform) +distributed_sampler = DistributedSampler(dataset=train_data, num_replicas=world_size, rank=rank) +train_loader = norch.utils.data.DataLoader(train_data, batch_size=BATCH_SIZE, sampler=distributed_sampler) + +class MyModel(nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.fc1 = nn.Linear(784, 30) + self.sigmoid1 = nn.Sigmoid() + self.fc2 = nn.Linear(30, 10) + self.sigmoid2 = nn.Sigmoid() + + def forward(self, x): + out = self.fc1(x) + out = self.sigmoid1(out) + out = self.fc2(out) + out = self.sigmoid2(out) + + return out + +model = MyModel().to(device) +model = DistributedDataParallel(model) +criterion = nn.CrossEntropyLoss() +optimizer = optim.SGD(model.parameters(), lr=0.01) +loss_list = [] + +print(f"Starting training on Rank {rank}/{world_size}\n\n") + +for epoch in range(epochs): + for idx, batch in enumerate(train_loader): + + inputs, target = batch + + inputs = inputs.to(device) + target = target.to(device) + + outputs = model(inputs) + + loss = criterion(outputs, target) + + optimizer.zero_grad() + + loss.backward() + + optimizer.step() + + if rank == 0: + print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss[0]:.4f}') + loss_list.append(loss[0]) +``` +Then you can run using + +```css +$ python3 -m norch.distributed.run --nproc_per_node 4 examples/train_multigpu.py +``` + + # 4 - Progress @@ -144,4 +247,4 @@ for epoch in range(epochs): | Loss | in progress | | | Data | in progress | | | Convolutional Neural Network | in progress | | -| Distributed | in progress | | +| Distributed | in progress | | diff --git a/build/cuda.cu.o b/build/cuda.cu.o index 1d8c326abdd0622a8d7c1024c4c6e6b32801a8c7..427e573c3e290f3050bf27a660fc26864ce2c0e1 100644 GIT binary patch delta 24 gcmcbxhxfuB-i8*&Ele_(SS-xV(zYvHVlwCf0Dt-kQUCw| delta 24 gcmcbxhxfuB-i8*&Ele_(SW*mAEw(FMVlwCf0D-azc>n+a diff --git a/build/distributed.o b/build/distributed.o index 58e0ef2fc765a84fc1bcb9cc82363ac3a6823f2f..caf2c5fa5c36e95041bc484c0aad32849d41eb46 100644 GIT binary patch delta 8547 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 CF%oJ3 delta 9104 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 diff --git a/examples/train_multigpu.py b/examples/train_multigpu.py new file mode 100644 index 0000000..8cbaaff --- /dev/null +++ b/examples/train_multigpu.py @@ -0,0 +1,93 @@ +import os +import norch +import norch.distributed as dist +import norch.distributed +import norch.nn as nn +import norch.optim as optim +from norch.nn.parallel import DistributedDataParallel +from norch.utils.data.distributed import DistributedSampler +from norch.norchvision import transforms as T +import random +random.seed(1) + +def main(): + + local_rank = int(os.getenv('OMPI_COMM_WORLD_LOCAL_RANK', -1)) + rank = int(os.getenv('OMPI_COMM_WORLD_RANK', -1)) + world_size = int(os.getenv('OMPI_COMM_WORLD_SIZE', -1)) + + dist.init_process_group( + rank, + world_size + ) + + BATCH_SIZE = 32 + device = local_rank + epochs = 10 + + transform = T.Compose( + [ + T.ToTensor(), + T.Reshape([-1, 784, 1]) + ] + ) + + target_transform = T.Compose( + [ + T.ToTensor() + ] + ) + + train_data, test_data = norch.norchvision.datasets.MNIST.splits(transform=transform, target_transform=target_transform) + distributed_sampler = DistributedSampler(dataset=train_data, num_replicas=world_size, rank=rank) + train_loader = norch.utils.data.DataLoader(train_data, batch_size=BATCH_SIZE, sampler=distributed_sampler) + + class MyModel(nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.fc1 = nn.Linear(784, 30) + self.sigmoid1 = nn.Sigmoid() + self.fc2 = nn.Linear(30, 10) + self.sigmoid2 = nn.Sigmoid() + + def forward(self, x): + out = self.fc1(x) + out = self.sigmoid1(out) + out = self.fc2(out) + out = self.sigmoid2(out) + + return out + + model = MyModel().to(device) + model = DistributedDataParallel(model) + criterion = nn.CrossEntropyLoss() + optimizer = optim.SGD(model.parameters(), lr=0.01) + loss_list = [] + + print(f"Starting training on Rank {rank}/{world_size}\n\n") + + for epoch in range(epochs): + for idx, batch in enumerate(train_loader): + + inputs, target = batch + + inputs = inputs.to(device) + target = target.to(device) + + outputs = model(inputs) + + loss = criterion(outputs, target) + + optimizer.zero_grad() + + loss.backward() + + optimizer.step() + + if rank == 0: + print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss[0]:.4f}') + loss_list.append(loss[0]) + +if __name__ == "__main__": + main() + diff --git a/examples/train.ipynb b/examples/train_nn_mnist.ipynb similarity index 100% rename from examples/train.ipynb rename to examples/train_nn_mnist.ipynb diff --git a/examples/train_singlegpu.py b/examples/train_singlegpu.py new file mode 100644 index 0000000..8c58ed6 --- /dev/null +++ b/examples/train_singlegpu.py @@ -0,0 +1,71 @@ +import norch +import norch.nn as nn +import norch.optim as optim +from norch.norchvision import transforms as T +import random +random.seed(1) + +def main(): + + BATCH_SIZE = 32 + device = "cuda" + epochs = 10 + + transform = T.Compose( + [ + T.ToTensor(), + T.Reshape([-1, 784, 1]) + ] + ) + + target_transform = T.Compose( + [ + T.ToTensor() + ] + ) + + train_data, test_data = norch.norchvision.datasets.MNIST.splits(transform=transform, target_transform=target_transform) + train_loader = norch.utils.data.DataLoader(train_data, batch_size=BATCH_SIZE) + + class MyModel(nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.fc1 = nn.Linear(784, 30) + self.sigmoid1 = nn.Sigmoid() + self.fc2 = nn.Linear(30, 10) + self.sigmoid2 = nn.Sigmoid() + + def forward(self, x): + out = self.fc1(x) + out = self.sigmoid1(out) + out = self.fc2(out) + out = self.sigmoid2(out) + + return out + + model = MyModel().to(device) + criterion = nn.CrossEntropyLoss() + optimizer = optim.SGD(model.parameters(), lr=0.01) + loss_list = [] + + for epoch in range(epochs): + for idx, batch in enumerate(train_loader): + + inputs, target = batch + + inputs = inputs.to(device) + target = target.to(device) + + outputs = model(inputs) + loss = criterion(outputs, target) + + optimizer.zero_grad() + + loss.backward() + + optimizer.step() + + +if __name__ == "__main__": + main() + diff --git a/norch/__init__.py b/norch/__init__.py index 2aae8f0..355e312 100644 --- a/norch/__init__.py +++ b/norch/__init__.py @@ -5,6 +5,6 @@ from .utils import * from .norchvision import * from .utils import * -__version__ = "0.0.4" +__version__ = "0.0.5" __author__ = 'Lucas de Lima Nogueira' __credits__ = 'Lucas de Lima Nogueira' \ No newline at end of file diff --git a/norch/csrc/distributed.cpp b/norch/csrc/distributed.cpp index 0a1cc69..bc993fa 100644 --- a/norch/csrc/distributed.cpp +++ b/norch/csrc/distributed.cpp @@ -58,19 +58,6 @@ void allreduce_sum_tensor(Tensor* tensor) { cudaStreamDestroy(stream); } -void allreduce_mean_tensor(Tensor* tensor) { - cudaStream_t stream; - cudaStreamCreate(&stream); - - // Perform NCCL AllReduce operation to calculate the mean of all tensors across all processes - NCCL_CHECK(ncclAllReduce(tensor->data, tensor->data, tensor->size, ncclFloat, ncclSum, nccl_comm, stream)); - - tensor_div_scalar_cuda(tensor, world_size, tensor->data); - - cudaStreamSynchronize(stream); - cudaStreamDestroy(stream); -} - void end_process_group() { MPI_CHECK(MPI_Finalize()); NCCL_CHECK(ncclCommDestroy(nccl_comm)); diff --git a/norch/distributed/distributed.py b/norch/distributed/distributed.py index d534ec4..7d41dec 100644 --- a/norch/distributed/distributed.py +++ b/norch/distributed/distributed.py @@ -28,10 +28,3 @@ def allreduce_sum_tensor(tensor): Tensor._C.allreduce_sum_tensor.restype = None Tensor._C.allreduce_sum_tensor(tensor.tensor) - -def allreduce_mean_tensor(tensor): - - Tensor._C.allreduce_mean_tensor.argtypes = [ctypes.POINTER(CTensor)] - Tensor._C.allreduce_mean_tensor.restype = None - - Tensor._C.allreduce_mean_tensor(tensor.tensor) diff --git a/norch/libtensor.so b/norch/libtensor.so index bc35ee296b4615736fe0e00bbe00296f7b697df0..68eb2f24f9e31ab50b78d6873023647752adce18 100755 GIT binary patch delta 45628 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 NeDw>W$U*M={tc@g>p1`b delta 45344 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 P&~ZEljijJ^z4!kwDHNN6 diff --git a/norch/optim/optimizers/__pycache__/sgd.cpython-38.pyc b/norch/optim/optimizers/__pycache__/sgd.cpython-38.pyc index 0ee226c0b3d139a685b589b2150df2735cacdb97..0daa1fffe3da3da9f2ccc8404191042b42839949 100644 GIT binary patch delta 74 zcmeyx`Hhn|l$V!_0SG>`$E9U&