뉴비에욤

PyTorch 튜토리얼 5 - 데이터 병렬 처리 본문

Machine Learning/PyTorch

PyTorch 튜토리얼 5 - 데이터 병렬 처리

초보에욤 2018. 5. 1. 20:26

2017/07/13 - [Machine Learning/PyTorch] - 윈도우 10 PyTorch 환경 구성 - 설치

2018/04/30 - [Machine Learning/PyTorch] - PyTorch 튜토리얼 1 - PyTorch란?

2018/04/30 - [Machine Learning/PyTorch] - PyTorch 튜토리얼 2 - Autograd : 자동 미분

2018/05/01 - [Machine Learning/PyTorch] - PyTorch 튜토리얼 3 - 뉴럴 네트워크(신경망)

2018/05/01 - [Machine Learning/PyTorch] - PyTorch 튜토리얼 4 - Classifier 학습



원문 : http://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html


* 17년도 07월에 초번 번역 후, 파이토치 최신 버전인 0.4.0에 맞추어 갱신 된 번역본

* 최근 갱신일 : 2018-05-01



목 차 ( 초보자 튜토리얼 )

A. 파이토치와 함께하는 딥러닝 : 60분만에 끝장내기!

5. 데이터 병렬 처리

임포트와 파라미터

더미 데이터셋

단순 모델

모델 및 데이터 병렬 생성

모델 실행

- 결과

2개의 GPU

3개의 GPU

8개의 GPU

요약





데이터 병렬 처리 ( Optional: Data Parallelism )


이번 튜토리얼에서는 DataParallel을 이용하여 여러 개의 GPU를 사용하는 법을 배울 것이다.


파이토치에서 여러 개의 GPU를 사용하는 것은 매우 쉬운데, GPU에 다음과 같이 모델을 등록시킬 수도 있다.

- 소스

device = torch.device("cuda:0")
model.to(device)


그 다음 모든 텐서를 GPU에 복사한다.

- 소스

mytensor = my_tensor.to(device)


mytensor.to(devices)를 그냥 호출해버리면 mytensor를 다시 작성하는것이 아니라, GPU에 mytensor의 새로운 복사본을 생성하여 리턴하는 것이며 사용자는 새로 생성 된 복사본을 새로운 변수에 할당하고, 할당 된 텐서를 GPU에서 사용해야 한다.



여러 개의 GPU에서 사용자의 순방향(forward), 역방향(backwawrd) 전파(propagations)를 실행하는 것은 당연한 일이다. 그러나 파이토치는 기본적으로 한개의 GPU만 사용한다. 사용자는 DataParallel을 이용하여 모델을 병렬로 실행함으로써 여러 개의 GPU를 이용한 작업을 손쉽게 수행할 수 있다.

- 소스

model = nn.DataParallel(model)


지금까지 언급한 것이 바로 이번 튜토리얼의 숨겨진 핵심이라 볼 수 있다. 지금부터는 좀 더 상세하게 살펴보고자 한다.




임포트와 파라미터 ( Imports and parameters )


파이토치 모듈을 임포트하고 파라미터들을 정의한다.

- 소스

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 = 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, 100),
                         batch_size=batch_size, shuffle=True)




단순 모델 ( Simple Model )


- 소스

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


시연(demo)의 경우, 우리의 모델을 입력을 받고, 선형 연산(linear operation)을 수행한 다음, 출력을 제공한다. 그러나 사용자는 모델(CNN, RNN, Capsule Net, 기타 등등)에 상관 없이 DataParallel을 사용할 수 있다.


- 우리는 모델 내부에 print 문을 작성하였는데, 이는 입력/출력 텐서의 크기를 알아보기 위함이다. 배치 랭크 '0'에 어떤 값이 출력 되는지 유의 깊게 살펴보기 바란다.




모델 및 데이터 병렬 생성 ( Create Model and DataParallel )


이번 섹션은 본 튜토리얼에서 가장 중요한 부분이다. 우선, 모델 인스턴스를 생성하고 여러 개의 GPU가 있는지 확인할 필요가 있다. 만약 여러 개의 GPU가 있다면, nn.DataParallel을 이용하여 우리의 모델을 래필할 수 있다. 그 다음, model.to(device)를 이용하여 모델을 여러 개의 GPU에 올릴 수 있다.

- 소스

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)




모델 실행 ( Run the Model )


이제 우리는 입력/출력 텐서들의 크기를 볼 수 있다.

- 소스

for data in rand_loader:
    input = data.to(device)
    output = model(input)
    print("Outside: input size", input.size(),
          "output_size", output.size()) 

- 결과

In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([30, 5]) output size torch.Size([30, 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])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])




결과 ( Results )


30개의 입력과 30개의 출력을 배치할 때, 모델은 30개을 얻고 예상대로 30개를 출력한다. 그러나 여러 개의 GPU가 있는 경우라면 다음과 같은 결과를 얻을 수 있다.



2개의 GPU ( 2 GPUs )


만약 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 ( 3 GPUs )


만약 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 ( 8 GPUs )


만약 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])




요약 ( Summary )


DaataParallel은 자동으로 데이터를 분할하고 작업 주문/순서(job orders)를 여러 개의 GPU에서 돌아가는 여러 모델로 전송한다. 각 모델이 스스로의 작업을 끝내면, DataParallel은 결과를 수집하고 합친 다음 사용자에게 리턴한다.


좀 더 많은 정보를 알고 싶다면, 다음 링크를 참고하기 바란다.

http://pytorch.org/tutorials/beginner/former_torchies/parallelism_tutorial.html




Comments