이번 튜토리얼은 선택적으로 하면 된다. 여러 GPU를 이용한 병렬 처리에 관한 것이기에 GPU 여러 개를 쓸 수 있는 환경이라면 직접 실습해보아도 좋고, 아니더라도 잠시 훑고 가는 것도 좋을 것 같다.
optional: data parallelism
이 튜토리얼에서, 어떻게 DataParallel을 사용해 여러 GPU를 다룰 수 있는 지 알아볼 것이다.
Pytorch로 GPU를 다루기는 매우 쉽다. GPU에 모델을 얹을 수 있다.
device = torch.device("cuda:0")
model.to(device)
그리고 모든 tensor들을 GPU에 복사할 수 있다.
mytensor = my_tensor.to(device)
my_tensor.to(device)만 호출하는 것은 my_tensor를 다시 쓰는 게 아니라 my_tensor의 새로운 복사본을 GPU에 반환하게 되는 걸 명심하자.
여러 GPU에서 순전파/역전파를 수행하는 것은 일반적이다. 그러나, Pytorch에서는 하나의 GPU를 사용하는 것을 기본값으로 한다. DataParallel을 이용해 모델을 병렬적으로 여러 GPU에서 돌릴 수 있다.
model = nn.DataParallel(model)
Imports와 parameters
Pytorch를 import하고 parameter를 정의한다.
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
# Parameters and DataLoaders
input_size = 5
output_size = 2
batch_size = 30
data_size = 100
device 객체 받기(GPU에)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
Dummy Dataset
임의의 데이터셋을 생성한다. getitem을 실행시키면 된다.
class RandomDataset(Dataset):
def __init__(self, size, length):
self.len = length
self.data = torch.randn(length, size)
def __getitem__(self, index):
return self.data[index]
def __len__(self):
return self.len
rand_loader = DataLoader(dataset=RandomDataset(input_size, data_size),
batch_size=batch_size, shuffle=True)
간단한 모델
demo를 위해서, input을 받고 선형적인 연산을 시행 후 output을 출력하도록 한다. 그러나, DataParallel은 어떤 모델이던 사용할 수 있다.(CNN, RNN, Capsule Net 등)
모델 중간에 print문을 넣었는데, 이는 모델에서 input과 output tensor의 size를 알아보기 위함이다. batch rank 0에서 어떤 게 print 되었는지 주의 깊게 살펴보자
class Model(nn.Module):
# Our model
def __init__(self, input_size, output_size):
super(Model, self).__init__()
self.fc = nn.Linear(input_size, output_size)
def forward(self, input):
output = self.fc(input)
print("\tIn Model: input size", input.size(),
"output size", output.size())
return output
모델 생성 및 DataParallel
이 튜토리얼의 핵심적인 부분이다. 첫째로, 모델 instance를 생성하고, multiple GPU가 있는지 확인한다. Multiple GPU가 있다면, nn.DataParallel을 이용해 모델을 묶을 수 있다. 그 다음 GPU들에 model.to(device)를 통해 모델을 올릴 수 있다.
model = Model(input_size, output_size)
if torch.cuda.device_count() > 1:
print("Let's use", torch.cuda.device_count(), "GPUs!")
# dim = 0 [30, xxx] -> [10, ...], [10, ...], [10, ...] on 3 GPUs
model = nn.DataParallel(model)
model.to(device)
Results
만일 GPU가 없거나 한 개 뿐이라면, batch로 30개의 input과 30 output을 할당했을 때, 30개의 input과 30 output이 나올 것이다. 그러나 여러 GPU를 갖고 있다면, 다음과 같은 결과가 나올 것이다.
GPU의 개수에 따라 하나의 batch에 대해 GPU개수 만큼 나눠진 후 연산 그 후에 합쳐지는 걸 살펴볼 수 있다.
2개 GPU
# on 2 GPUs
Let's use 2 GPUs!
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])
In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
3개 GPU
Let's use 3 GPUs!
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
8개 GPU
Let's use 8 GPUs!
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
요약
DataParallel은 데이터를 자동적으로 나누고 여러 GPU에 여러 모델들에 대한 처리 순서를 보낸다. 각 모델이 할 일을 끝내면, DataParallel은 최종 결과 반환 전에 그 결과들을 모으고 종합해 준다.
더 많은 정보는 다음을 확인해보자.
https://pytorch.org/tutorials/beginner/former_torchies/parallelism_tutorial.html
'컴퓨터' 카테고리의 다른 글
positional encoding이란 무엇인가 (5) | 2020.02.20 |
---|---|
[pytorch] 언어별 이름(성씨) 분류 (0) | 2020.02.13 |
[pytorch] 파이토치 튜토리얼 4 (0) | 2020.02.10 |
[pytorch] 파이토치 튜토리얼3 (0) | 2020.02.10 |
[pytorch] 파이토치 튜토리얼2 (0) | 2020.02.07 |
댓글