꺼내먹는지식 준

Multi_GPU, 분산 학습, 데이터 병렬화, 파라미터서버 본문

AI/딥러닝 기초

Multi_GPU, 분산 학습, 데이터 병렬화, 파라미터서버

알 수 없는 사용자 2022. 1. 28. 12:02

오늘날의 딥러닝은 큰 데이터가 성능을 결정짓는다. 

연구가 장비빨이라는 말도 나온다. 

 

즉, Multi-GPU 를 다룰 줄 아는 것은 필수이다. 

 

 

Node

: Node는 주로 system, 컴퓨터를 뜻한다. 

 

ex)

Single Node Single GPU 

한대 컴퓨터 안의 한대 컴퓨터 

Signle Node Multi GPU 

한대 컴퓨터 여러대 GPU 

Multi Node Multi GPU 

여러대 컴퓨터 여러대 GPU 

 

 

TensorRT (Nvidia 제공, GPU 쉽게 사용할 수 있는 module)

 

Model, Data Parallel 

다중 GPU에 합습을 분산하는 방법 

 

1) 모델 병렬화 (Model)

모델을 나눠서 GPU 에서 모델을 병렬로 처리 

모델 병렬화는 난이도가 높아서 흔히 사용되지는 않는다. 

ex) AlexNet

 

2) 데이터 나누기 (Data)

데이터를 나눠서, 각 GPU 에서 데이터를 돌리고, 각각 GPU에서 나오는 loss 값들을 미분한 후, 평균 값을 가지고 전체 미분 값을 구함 

 

출처 https://xiandong79.github.io

 

 

1) 모델 병렬화 

 

모델 병렬화의 문제점

사진 위 장면은 병렬화의 실패 장면

파이프라인 구조를 잘 짜는게 굉장히 중요 

 

참고자료

https://tutorials.pytorch.kr/intermediate/model_parallel_tutorial.html

 

단일 머신을 사용한 모델 병렬화 모범 사례 — PyTorch Tutorials 1.10.1+cu113 documentation

Note Click here to download the full example code

tutorials.pytorch.kr

예를 들어보자. 모델 병렬화를 하면, 10개의 층으로 구성된 m 신경망 모델에 대해서 데이터 병렬 처리 방법은 10개의 층을 전부 복제하여 각 GPU에 할당하여 처리하지만, 이와 반대로 2개의 GPU에 모델을 병렬 처리한다면, 각 GPU에 5개의 층씩 각각 할당하여 호스팅할 수 있다.

 

각 sequence를 다른 GPU cuda 0, cuda 1 에서 처리를 해주는 것을 볼 수 있다. 

cuda 0 에서 seq1 처리 후, seq2 로 보냄 

하지만 이 방법은 두 개의 GPU가 동시에 계산하는 것이 아니라 1개의 GPU는 계산하지 않고 대기하고 있기 때문에 단일 GPU를 이용할 때보다 학습 과정이 오래걸리며 두 번째 층 (layer2)이 할당된 첫 번째 GPU에서 계산된 결과를 세 번째 층 (layer3)이 할당된 두 번째 GPU로 텐서값을 복사하기 때문에 계산 과정이 더 길어지게 된다. 

 

입력 텐서값을 분할하는 파이프라인을 설계하여 학습 시간을 단축하는 방법에 대한 예제

아래에 있는 실험은, 120개의 이미지로 구성된 1개의 미니 배치 데이터를 20개씩 나누어 진행하는 과정이다. 아래의 과정을 실행할 때, PyTorch가 CUDA 연산을 비동기적으로 이용하기 때문에, 프로세스를 실행하는 스레드를 여러개 생성할 필요가 없다.

 

class PipelineParallelResNet50(ModelParallelResNet50):
    def __init__(self, split_size=20, *args, **kwargs):
        super(PipelineParallelResNet50, self).__init__(*args, **kwargs)
        self.split_size = split_size

    def forward(self, x):
        splits = iter(x.split(self.split_size, dim=0))
        s_next = next(splits)
        s_prev = self.seq1(s_next).to('cuda:1')
        ret = []

        for s_next in splits:
            # A. s_prev는 두 번째 GPU에서 실행됩니다.
            s_prev = self.seq2(s_prev)
            ret.append(self.fc(s_prev.view(s_prev.size(0), -1)))

            # B. s_next는 A.와 동시에 진행되면서 첫 번째 GPU에서 실행됩니다.
            s_prev = self.seq1(s_next).to('cuda:1')

        s_prev = self.seq2(s_prev)
        ret.append(self.fc(s_prev.view(s_prev.size(0), -1)))

        return torch.cat(ret)


setup = "model = PipelineParallelResNet50()"
pp_run_times = timeit.repeat(
    stmt, setup, number=1, repeat=num_repeat, globals=globals())
pp_mean, pp_std = np.mean(pp_run_times), np.std(pp_run_times)

plot([mp_mean, rn_mean, pp_mean],
     [mp_std, rn_std, pp_std],
     ['Model Parallel', 'Single GPU', 'Pipelining Model Parallel'],
     'mp_vs_rn_vs_pp.png')

 

 

2) 데이터 나누기 

https://wooono.tistory.com/331

 

[DL] Distributed Training (분산 학습) 이란?

Distributed Training (분산 학습) 이란? 딥러닝 모델 설계 과정에는 많은 시간이 소요됩니다. 따라서, 모델의 학습 과정을 가속화하는 것은 매우 중요합니다. 분산 학습은 이러한 딥러닝 모델의 학습

wooono.tistory.com

출처: https://wooono.tistory.com/331

 

파라미터 서버 기반 분산 딥 러닝 시스템을 위한 파라미터 샤딩 기술 성능 평가

dbpia.co.kr/journal/articleDetail?nodeId=NODE08763430#

 

데이터를 여러 GPU에 분산하여 처리하는 방법

모델 학습은 다음과 같은 순서로 진행 

1) Worker(GPU)는 동일한 각 모델, 각기 다른 입력 데이터를 받아 모델을 학습 

2) 모델 학습 시, 순전파(loss계산할 때)와 역전파를 통해, 모델의 파라미터가 결과값에 미치는 영향(Gradient)을 계산, 파라미터 서버 노드로 전송 

3) 파라미터 노드는 각 모델로부터 나온 Gradient의 평균 계산, 전역 파라미터 업데이트

4)  업데이트 된 모델의 전역 파라미터를 Worker에게 전송 

5) 2 ~ 4 과정 반복 

 

Parameter 동기화 방식

 

출처: 파라미터 서버 기반 분산 딥 러닝 시스템을 위한 파라미터 샤딩 기술 성능 평가

1) Synchronous replication(Parallel)

분산 처리가 모두 끝났을 때, Gradient 평균을 사용해 Parameter 업데이트 

수렴이 빠르다. 

 

2) Asynchronous replication(Parallel)

파라미터 서버 노드는 각 Worker의 학습 결과를 개별적으로 처리하여 전역 파라미터를 업데이트 하고 해당 Worker에만 전송

(평균 내기위해 기다리는 과정 생략)

각 Worker는 파라미터 서버 노드에게 파라미터를 받아 자신의 파라미터를 업데이트 후 다음 학습 진행 

수렴이 느릴 수 있다. 

 

Sync 방식은 모든 Worker(GPU)의 job이 끝날 때 까지 대기하다가 Parameter를 업데이트 하므로, 분산되어 있는 GPU가 많을 수록 Async 방식이 효율적 그 외에도 각 방법의 장점만을 차용한 State Synchronous Parallel 기법등이 있다. 

 

Gradient 취합 방식 

1) All-Reduce (Parameter Server)

Parameter Server가 모든 Gradient 를 취합하여 재분배 

 

2)모든 GPU를 Ring 형태로 구성한 뒤, Graident 전달을 통해 공유 

 

출처: wooono.tistroy.com/331

 

All-Reduce

All Reduce는 단순하게 Parameter Server가 모든 Worker(GPU)로 부터 계산 된 Gradient 를 취합한 뒤, Worker(GPU)

에게 재 분배하는 방식

 

장점: 단순한 구조 

단점: Worker의 수에 따라, Parameter Server의 메모리 사용량 및 네트워크 부하가 비례하여 증가하기 때문에 병목 현상이 발생할 수 있다. 

 

Ring-AllReduce

AllReduce의 병목 현상을 제거한, 빠르고 확장 가능한 알고리즘 

1) 모든 GPU를 Ring 형태로 구성 

2) GPU에서 계산 된 Gradient 를, 총 GPU 개수만큼 분리 

3) 각각의 GPU는 분리된 Gradient의 일부를 옆 GPU 에게 전달 

4) Gradient를 넘겨받은 GPU는 본인의 Gradient와 전달받은 Gradient를 합산 한 뒤, 다시 옆 GPU에게 Gradient를 전달 

5) 해당 과정을 2(N-1) 번 진행 (N: GPU 개수)

 

위 사진을 참고하면, Start 그림 GPU0 a0 + b0 + c0 + d0 + e0 은, GPU0 에서 계산된 Gradient 를 의미

목표는 GPU0 ~ GPU4 가 가진 모든 Gradient 를 Goal 사진처럼 취합하는 것 

 

출처: https://wooono.tistory.com/331

 

파라미터 서버 기반 분산 딥 러닝 방안은 워커 노드의 수가 많아질수록 파라미터 서버 노드가 합산해야 할 학습 결과가 많아진다. 즉, 파라미터 서버 노드의 통신 및 연산이 전체 학습 과정의 병목이 될 수 있다. 이러한 문제를 해결하기 위해 파라미터 샤딩 기술을 사용하기도 한다. 

 

 

파라미터 샤딩 기술 

모델 파라미터를 다수의 파라미터 샤드로 분할하고 각 샤드를 서로 다른 파라미터 서버 노드가 처리하는 기술

1) 하나의 파라미터 서버 노드로 집중되었던 네트워크 대역이 여러 노드로 분산 

2) 전역 파라미터를 여러 파라미터 서버 노드가 나누어 병력적으로 처리할 수 있다. 

 

파라미터 서버 노드의 수가 증가할수록 성능이 증가, 그러나 워커 노드의 수에 비해 파라미터 서버노드의 수가 필요 이상으로 많을 경우 오히려 성능이 감소 

 

*Parameter Server Node

 

파라미터 서버 노드는 학습 모델의 파라미터를 관리하는 노드로 워커 노드들의 학습 결과를 합산하는 역할을 한다.

 

*Worker Node

워커 노드는 전체 학습 데이터를 분할하여 저장하고 해당 데이터를 학습하여 파라미터 서버에게 전송하는 역할을 한다

 

 

 

Pytorch Data Parallel 

하나의 GPU 가 과도하게 일을 처리하고 있을 때 분배해주는 것이 중요

DataParallel은 단순히 데이터를 분배한 후 평균을 취한다. 

즉, 이로 인해 GPU 사용 불균형 문제가 발생할 수 있고, Batch 사이즈가 감소한다. (한 GPU가 병목), GIL 

 

DistributedDataParallel 

각 CPU마다 process 생성하여 개별 GPU에 할당 

기본적으로 DataParallel로 하나 개별적으로 연산의 평균을 낸다. (모으는 작업이 없다.)

DistributedDataParallel

https://cvml.tistory.com/24

 

num_workers & pin_memory in DataLoader

pytorch를 이용해 딥러닝 모델을 학습시킬 때 custom dataset을 이용할 경우 torch.utils.data.Dataset으로 데이터셋을 정의하고(input data type, augmentation 등) torch.utils.data.DataLoader로 어떻게 데이터..

cvml.tistory.com

핀메모리: 

데이터를 CPU로 읽어들인 다음 GPU로 보내기 위해서는 GPU와 통신하기 위한 CPU의 메모리 공간이 필요하다. 이 때, 메모리를 할당시키는 기법을 memory pinning이라고 한다. memory pinning을 하는 데 사용되는 메모리의 종류는 pageable memory와 pinned memory가 있는데 pinned memory가 더 빠르다고 한다.

 

 

즉, pinned memory는 위 그림처럼 GPU에서 호스트에서 디바이스로 전송을 위한 staging area이고 pinned data transfer는 pinned memory와 pageable memory의 전송 비용을 줄이기 위해 데이터를 pin memory에 고정시켜 전송하는 방법이다. 우리가 pin_memory = True로 하게된다면 입력 데이터를 바로 pinned memory에 로드하여 빠르게 데이터 복사를 해 CUDA 연산을 효율적으로 할 수 있게 해준다. 따라서 시스템 메모리가 넉넉하다면 pin_memory = True로 하고 학습을 수행한다면 병목을 개선시킬 수 있다.

num_workers(GPU $\times$ 4)

 

'AI > 딥러닝 기초' 카테고리의 다른 글

Neural Net, Multi-layer Perceptron  (0) 2022.02.07
딥러닝 역사, 기본 및 용어  (0) 2022.02.07
딥러닝 학습모델 간단 정리  (0) 2022.01.23
경사하강법 간단정리  (0) 2022.01.20
행렬 간단 정리  (0) 2022.01.19
Comments