22.03.15 CSE 콜로퀴엄 수업 대규모 자연어 처리 모델 분산학습 최적화 최신 동향(서지원 교수님)

2022. 3. 16. 18:37작업/RNN

x축 : 모델의 파라미터 수, y축 : 모델 종류

parameter + optimizer status라는 메타데이터까지 저장해야 한다. 

forward 학습도 하면서 각 레이어의 output들도 메모리에 가지고 있어야 한다.

 -> 요새는 메모리가 많이 필요하다. 이건 메모리 space에 대한 얘기

 

필요한 총 계산량 : V100 GPU를 100대를 가지고 계산했을 때 1년이 걸릴 정도로 큰 모델이다.

이렇게 큰 모델일 경우에는 32bit 계산을 하는 게 아닌 16+32bit를 섞어서 계산하는 mixed precision 방식을 사용한다.

그래서 half precision인 16bit로 대부분 연산을 진행하게 된다. (FP16)

그렇게 16bit로 줄이게 되면 1년 -> 1개월로 줄일 수 있다.(여기서 V100을 A100으로 바뀐 그래프로 보여준다. A100도 위에서는 4년인가 1년인가 걸림)

 

그러나 실제로는 GPU를 100대 사용하는 건 불가능하므로 FP16도 한 6개월정도 걸린다.

따라서 분산학습을 하는 것이 필수적이다.

 

분산 학습은 두 가지 방법이 있다.

Data Parallel : 파라미터를 같은 값으로 동기화하면서 나눠서 진행?

 

Model Parallel : Pipeline Parallel - 레이어 단위로 레이어 여러개를 한 GPU가 맡아서

 Tensor Parallel - 레이어 하나를 여러 GPU에서 나눠서 학습

 

각 방식에서 bottleneck이 되는 부분이 좀 다르다.

 

Data Parallel Training

Worker : 계산을 하는 GPU를 의미

PS : Parameter server

x축: 시간

Forward를 먼저 게산하고 Backward를 계산하는 순서로 진행되는데, 그 학습된 파라미터들을 PS(Parameter Sync)에서 분산처리된 GPU들에게 동기화를 해줘야하는데 GPU IDLE(놀고있는 GPU)가 생기는 것이 문제이다.

 

Pipeline Parallel Training 

BERT-Base : FC가 모여있는 어떤 transformer라는 모델.

이 모델이 워낙 크다보니 (12개의 transformer)를 GPU 4개에 3개씩 나눠서 학습을 시킨다.

얘도 한 GPU가 학습이 끝나고 나서 다음 GPU에게 보내줄 때(GPU0->GPU1) GPU가 노는 시간이 생긴다.

 

구글에서 만든 시스템 - GPipe:

위쪽은 미니배치로 받아서 순서대로 GPU0->GPU1로 넘겨주는데

아래쪽 모델은 더 작게, 마이크로배치로 쪼개서 GPU0이 계산하는 동안에 GPU1이 조그만 배치를 계산중이도록 만든다.

저 검은 부분(노는 시간)이 줄어드니까 좋긴 한데, 미니배치를 영원히 아아아주 작게 나눌수만은 없다.

왜냐하면 GPU에서 실행하게 되는 task 하나의 크기가 매우 작아지게 되고, task 하나에서 필요한 연산이 적기 때문에

GPU의 하드웨어 resource를 다 쓸 필요가 없다. 

따라서 미니배치를 4개의 마이크로배치로 잘랐을 때 사실은 미니배치 1개 = 마이크로배치 4개여야되는데 실제로는 미니배치 1개 < 마이크로배치 4개가 되어 bottleneck 문제도 있다.

 

Tensor Parallel Training : Output neuron의 갯수를 GPU가 나눠서 계산한다. 

한 레이어가 끝나고 나면 GPU끼리 sync를 해서 copy를 해서 공유하는듯? 하다. 그 후 그다음 레이어로 넘어간다.

그래서 보통은 아래처럼 한다.

저기 보면 GPU1이 가진 2개 노드를 거치면 그 아래 2개의 노드가 sync가 하지말고, GPU2는 위쪽 2개 노드가 하지 않고 계산을 다 한다. 그래서 sync를 나중에 한번에 해준다.

 

Megatron : 미들웨어 (버트 모델같이 큰 모델을 분산학습하도록 개발한 모델)

Transformer : 나는 영화를 좋아합니다. 이라는 문장이 있으면 단어끼리 서로의 상관관계를 계산(self attention)

하고 그 결과에 대해 FC로 계산하는 것?

Megatron에는 Tensor Parallel이 좋은데, 왜냐면 self attention에서 각각이 독립적으로 계산하기 때문이다. 끝날떄까지 동기화를 할 필요가 없다. transformer 하나당 커뮤니케이션(sync)가 두 번밖에 없기 떄문에 오버헤드가 크지는 않아서 적합하다.

이중에서 Data Parallel과 Pipeline Parallel이 오버헤드가 크기에 연구가 많이 일어났다.

BytePS : Byte라는 회사? 가 만든 시스템 OSDI라는 최고학회에서 나온 내용

GPipe는 구글에서 만듦.

 

Data Parallel : Backpropagation을 하면서 기울기를 계산하고 그걸 다른 worker에게 전달하고, 모든 worker들의 파라미터도 합쳐서 weight를 계산해야 한다.

Wt+1 = Wt + 이전의 Weight들의 합?

레이어 5개짜리 모델의 Weight 계산 순서의 기존의 그림이다.

우리가 계산해야 하는 건 두개이다.

1. Weight Parameter에 대한 계산

2. Neuron에 대한 계산(output gradient?)

 

Backpropagation을 해야 Forward 계산을 할 수 있다. 

 

그런데!

Byte Scheduler를 쓰면? 아래 그림처럼 된다.

위쪽이 기존이고 아래쪽이 Byte인데, Byte의 원리는 3번 레이어의 계산 델타W3를 하다가도 5번 레이어 계산이 완료되면 미리 시작한다? 그래서 speed up이 될 수 있다!

즉, 동기화를 우선순위를 줘서 하자!

 

Pipeline Parallel Training : PipeDream

PipeDream : 원래는 끝나고 나서 해야하는 부분을 미리 가져와서 하기 때문에 시간이 일찍 끝나서 GPU 효율은 좋으나,

학습에 따른 version이 여러버전이 필요하다. 수정1,수정2 등등...ㅋㅋ

 

GPU에 있는 하드웨어를 100% 쓸 수 있는가?에 대한 문제가 아직 안풀리고 있다(GPU Utilization)

즉 GPU의 일부가 놀고 있다!

저 빨간 박스 친 부분이 GPU 를 44%밖에 못 쓴다는 뜻이다. 

그런데! 이게 7~80년대 CPU Utilization 문제와 비슷하다!

컴퓨터가 실행될 떄 Out of Order? Execution : 내가 코드를 짠 대로, 실행한 순서로 가는 게 아니라 미리 끝난 건 먼저? 해주는 그런 7~80년대 했던 내용이다.

여기서 힌트를 얻어서 GPU도 해결해보자.

 

Neural Network Dependency : 먼저 처리되어야하는? 의존도 표시 compute graph로 표시

저 F는 Forward, 초록색 미분된부분은 Backpropagation

그리고 저 네모 두개를 묶은 건 한 노드로 묶은 것

예를 들어 두 레이어의 계산 순서를 보자.

3번레이어 먼저 계산 - U3(Sync) - 2번레이어 계산 - U2(sync)

그러나 실제로는, 더 정확하게 표현하면 이 그림이 맞다.

뉴런들간에는 dependency가 있지만 W끼리는 dependency가 없다. 즉 계산을 해도, 이전 W를 다음에서 사용하진 않ㅇ는다.

저 execution이 흐름에 따라 화살표가 오른쪽으로 가게끔만 scheduling 하면 된다.

꼭 저 노드를 묶어서 볼 필요가 없이 즉,

꼭 레이어 역순서로 계산할 필요가 없다.

이게 우리 연구실에서 한 내용이다.

그러나 이렇게 하면 문제가 있을 수 있다. 바로 Memory 오버헤드가 있을 수 있다.

아무튼 메모리 효율도 잘 따져봐야 한다.

 

그래서 우리가 OOO BackProp을 사용해서 스케쥴링 알고리즘을 만들었다.

밑에 그림을 보자. 위 두개는 기존의 방식(원조, Byte 방식)이다

저 기존의 방식 둘다 W1(첫번쨰 레이어에 대한 BackProp계싼)을 마쳐야 Sync를 할 수 있었다.

그런데 우리는 1번을 오히려 먼저, 3-2-1순이 아닌 1-2-3으로 계산하는 것이다. 왜냐하면 어차피 동기화를 F(Forward)랑 동시에 하면 되고, F1을 더 먼저해야 하니까 1-2-3으로 scheduling해서 하면 먼저 끝난다.

-> List Scheduling 기법 ( 우선순위 스케줄링 ) 

우선순위가 높은 작업을 먼저 스케줄링 해주는 것이다. 우선순위 스코어를 그러면 어떻게 책정을 할 것이냐

1. 얼마나 다음 iteration에서 급하게(먼저) 사용되는지

2. network bandwidth를 꾸준히 잘 사용할 수 있게 하는?

 -> 이게 중요하다. 왜냐하면 network bandwidth를 놀고있게 할 수도 있기 때문이다.

 예를들어 이렇게 순서를 짜면 다 보내서 보낼 애가 없다. (저 빨간점선 빈 부분)

 

저 두 가지를 잡아서 우선순위 스코어를 짜면 된다.

그런데 또 문제가 있다.

 

바로 동기화하는데 걸리는 시간을 알아야 한다. 그래야 GPU가 언제 노는지 알 수 있다.

 

그래서 Reverse k scheduling

앞쪽 k-1개까지는 순차적으로, k부터는 역순으로 스케쥴링하면 우선순위를 잘 지키면서도 최대한 덜 놀게? 수 있다.

적절한 k값을 찾아야하긴 하지만, 이 k파라미터만 찾아가면서 training을 시켜본다면 괜찮다.

우리의 Reverse k execution scheduling 방법을 쓰면 기존의(초록색) 보다 우리 k scheduling이 메모리 사용량이 높다.

보통은 F(Forward에서 메모리 usage가 증가, B(Backward)에서 메모리 감소하는 양상을 보이는데.

 

 

 

Pipeline Parallel Training- 두 가지 기법

(델타W : weight gradient

델타O : output gradient(=neuron gradient) )

기존의 방식과 Pipeline Parallel Trainig을 사용했을 때 

기존의 방식(맨위) 미니배치 4개씩 하면? GPU0 4개 -> GPU1 나머지 4개

(가운데) Fast-Forwarding

(맨아래) Fast-Forwarding + Modulo Allocation

Fast-Forwarding + Modulo Allocation가 좋은 이유, Forward를 자잘하게 나눠서 분할하면 뒤에 델타O와 델타W에서 더 자잘하게 뭉쳐서 계산할 수 있다.

 

Fast-Forwarding + Modulo Allocation를 하면 뒷부분은 이득이 있어도 앞부분은 이득이 없어 보이나 마이크로배치 적용하면 더 이득이 크다. 아래 그림을 보자.

Fast-Forwarding + Modulo Allocation 방법은 GPU하나당 맡는 레이어 수가 많을 수록 성능이 더 크게 향상된다.

 

요기까지가 서지원 교수님 연구실에서 연구한 내용이다. 간단하고 적용하기 쉬운데 파워풀하다. GPU를 어떻게 사용하든 다 적용할 수 있다.

이제 Evaluation을 보자. 우리가 만든 거 vs 여기 나온 애들

아마존 AWS GPU에서 학습했다. 아래와 같은 환경을 구성했다.

이제 아래는 우리의 OOO 알고리즘과 타 알고리즘?의 비교 결과이다. x축이 GPU갯수, y축이 Throughput 이다.

아래는 pipeline parallel training에 대한 Evaluation이다.

Pre training : 사전학습

Fine tuning : 이미 pretrain된 걸 tuning

NVLink : GPU간 통신해주는 것?

노란색 : Fast-Forwarding

빨간색 : Fast-Forwarding + Modulo Allocation(레이어를 GPU에 할당하는 방식(transformer단위로 GPU0, GPu1에 할당했는지, 셀단위로 묶었는지??)

Modulo Allocation : GPU가 다음 GPU에게 정보를 넘겨야 하는 작업이 많아진다. 할당한 만큼

계산하는 시간 분에 커뮤니케이션하는데 걸리는 시간 비교 NVLink, PCIe는 빠른 것, 이더넷은 느린 네트워크

GPU가 더 많은 경우에는 한 머신 내의 GPU끼리의 통신은 NVLink, 머신끼리는 다른 통신?

 

아래는 Pretraining Evaluation결과

Weak Scalining : 보통 머신이나 GPU를 많이 쓰는 이유는 계산을 많이 해야하기 떄문. 그래서 GPU수가 많을 수록 Task 크기를 늘린다(BERT 큰 모델을 training을 시킨다. 12, 24, 36..)

Strong Scaling : Task 크기는 고정, 하드웨에ㅓ를 많이 쓸수록 보통은 Throuput(성능)이 좋아져야 한다. 그러나 GPT-3에서 GPU갯수가 많아져도 Throuput이 증가되지 않는 양상이 있다. 

요약 정리

이건 시스템 최적화 연구

이 랩실에서는 DNN 취약점 분석 및 테스트도 진행한다.

 

취약점 분석 : DNN 모델이 있을 떄, 입력 - 모델 - 추론결과 가 나온다.

비정상적인 추론이 나오는 것에 대한 연구 (Adversarial attack) 사람 눈으로 봤을 때 별로 다르지 않은데, 완전 다른 결과를 내놓는? 왜 그런 추론을 하는지에 대한 입력을 찾는 (보통 이미지)

그림상에선 노이즈 크게 했는데, 사람 눈으로 보면 거의 티가 안난다.

이건 이미 연구가 많이 됬고,

지금은 비가 많이 오거나, 먼지가 많이 끼면 자율주행이 잘 못 찾는다던지..

또는 반증으로 이런 조건이 있으면 잘 찾는다.

 

보통 제일 많이 하는 건 어떤 입력이 있을 떄 오동작을 많이 하는지를 찾는다.

모델의 구조를 보고 오류를 일으키는 입력을 찾는다기보다는 test 입력을 generation한다. test data generation

Out of distribution : training 데이터에서 봤던 데이터가 아닌 데이터를 체크해서, 못 본애면 오동작할 확률이 높으니까 그걸 잘 골라주는거고, 

Data transformation : 데이터가 어떻게 바뀌었을 때 오동작 하는지를 찾는다.

 

OOO 로 하면 accuracy는? 

OOO로 계산한다고 해도 순서만 바꿀 뿐 적용하나 안하나 accuracy는 완전히 동일. 파라미터도 동일하게 업데이트가 되었다. 계산하는 순서만 바꾸기 때문에.

a+b+(3+1) = (a+b)+3+1

floating point 계산 : 유효숫자가 있으니까 에러가 있다.

 

OOO가 시간은 줄어드는데, 메모리가 늘어나는 방법이니까 오히려 자원이 더 써야되는 것 아니냐?

OOO 를 쓰면 Backward에서는 조금 늘어나지만 결국 peak memory는 똑같이 때문에 GPU를 더 쓸 필요는 없다.

그러나 모델에 따라서(VGG) peak memory는 조금 늘긴 했으나, 최신 모델에 대해서는 peak memory가 차이 없었다. 

 

VGG에서 peak memory가 늘은 이유: layer는 작지만, 파라미터가 많아서 그랬다.

k를 적절히 설정하면 된다.

'작업 > RNN' 카테고리의 다른 글

22.03.19 LSTM 과 GRU  (0) 2022.03.19
22.03.15 RNN(Recurrent Neural Network)  (0) 2022.03.19