Intro

<aside> 🔥

이 내용은 반드시 컴퓨터의 소수점 표현을 이해하고 읽는 것을 추천드립니다.

Body

1. GPU에 올라가는 데이터 살펴보기

  1. 딥러닝 모델에서는 거의 대부분 GPU를 이용합니다.
  2. GPU에 데이터를 올리기 위해서는 GPU에 어떤 데이터를 올리는지 그 이해가 필요합니다.

이제부터 GPU에 어떤 데이터를 올라가는지 차근차근 알아봅시다!

1.3. 메모리 사용량 측정하기

1.3.1) 모델 메모리 측정 방법

<aside> 🔥

간단하게 모델 파라미터 사이즈에 따른 메모리 사용량을 측정하는 방식을 알아봅니다.

</aside>

모델 파라미터 사이즈는 곧 숫자의 갯수를 의미하므로, 파라미터 사이즈를 안다면 모델이 사용하는 GPU를 계산해볼 수 있습니다. Solar 10.7B 모델의 GPU 사용량을 계산해봅시다.

  1. fp16(bf16) 기준의 숫자 자료형을 사용한다고 가정한다면, 16bit를 사용하고, 즉 2byte를 사용합니다. (fp32는 4byte)
  2. $1GB = 1024 * 1MB = 1024^2 * 1KB = 1024^3 * 1byte$ 입니다.
  3. $1024^3$은 1,073,741,824 입니다. 반면에, 1B은 1,000,000,000입니다.
  4. 즉, $1B \approx 1024^3$ 이므로 1B 파라미터 사이즈의 모델은 2byte를 곱하면 약 2GB 조금 안되는 GPU 메모리를 사용하게 됩니다.
  5. 따라서, Solar 10.7B는 약 10.7 x 2 = 21.4GB의 메모리를 차지하게 됩니다.

1.3.2) 학습 메모리 측정 방법

<aside> 🔥

학습에서 사용하는 GPU 메모리를 측정하는 방식을 알아봅니다.

</aside>

학습에서 GPU 메모리가 추가로 들어가는 작업은 다음과 같습니다.

즉, 모델 학습에는 모델 파라미터(N) + forward + backward(optimizer) + gradient = 4N의 메모리가 필요합니다. Solar 10.7B를 학습하기 위해서는 10.7 x 2 x 4 = 85.6GB 이상의 메모리를 필요로 합니다. 또한, 학습 데이터도 gpu 할당이 필요하므로 사실상 학습에 필요한 GPU는 이보다 훨씬 많다고 보시면 됩니다.

2. 단일 GPU 효율적으로 활용하기

GPU를 1개만 사용해서 큰 모델을 학습시키고자 한다면 위에서 알아본 메모리를 사용하는 다양한 부분에서 메모리를 절약하는 방식을 적용해야 합니다. 이제부터 메모리를 어떻게 절약할 수 있을지 알아봅니다.

2.1. Gradient accumulation(그래디언트 누적)

<aside> 🔥

그래디언트 누적은 모델을 학습시킬 때 각 배치마다 모델을 업데이트하지 않고 여러 배치의 학습 데이터를 연산 후 모델을 업데이트해서 마치 더 큰 배치 크기를 사용하는 것 같은 효과를 내는 방법을 말한다.

</aside>

2.2. Gradient checkpointing(그래디언트 체크포인팅)

<aside> 🔥

모델 업데이트를 위해서 optimizer에서 역전파를 계산하는데, 이때 역전파 계산을 하기 위해서는 순전파의 정보를 알고 있어야 합니다. 그래디언트 체크포인팅은 순전파의 정보를 모두 저장하는 방식이 아닌 중간중간 포인트만 저장하는 방식으로 메모리를 효율적으로 사용합니다.

</aside>

3. 다중 GPU 효율적으로 활용하기

GPU 자원이 충분하다면 하나의 GPU에 모델 학습을 진행하는 것이 아닌 여러 GPU에 모델 학습을 진행하는 것이 더 효율적일 것입니다. 데이터 혹은 모델을 여러 GPU에 올리는 방법에 대해 알아봅니다.

3.1. Data Parallelism (데이터 병렬화)

<aside> 🔥

여러 GPU에 각각 모델을 올리고 학습 데이터를 병렬로 처리해 학습 속도를 높이는 방식입니다.

</aside>

3.2. Model Parallelism (모델 병렬화)

<aside> 🔥

여러 GPU에 하나의 모델을 쪼개서 올리는 방식입니다. 주로 큰 모델을 학습시킬때 사용합니다.

</aside>

모델을 vertical하게 쪼개냐 horizontal하게 쪼개냐에 따라서 Pipeline parallelism(파이프라인 병렬화), tensor parallelism(텐서 병렬화)으로 나뉩니다.

3.1.1) Pipeline parallelism

<aside> 🔥

하나의 모델을 각 레이어 단위로 각각의 GPU에 쪼개서 올리는 방식입니다.

</aside>

3.1.2) Tensor parallelism

<aside> 🔥

한층의 레이어도 전부 나눠서 GPU에 올리는 방식입니다.

</aside>

3.3. 데이터 병렬화에서 중복 저장 ZeRO

<aside> 🔥

데이터 병렬화에서 동일 모델을 여러 GPU에 복사해서 올리는 방식을 사용합니다. 이 경우 중복되는 것들이 존재하기 때문에 효율적이지 않습니다. 따라서, 데이터 병렬화를 텐서 병렬화처럼 수행하는 방식을 Zero Redundancy Opimizer(ZeRO)라고 합니다.

</aside>

참고 : https://huggingface.co/docs/accelerate/en/usage_guides/deepspeed

4. 효율적인 학습 방법(PEFT) 사용하기

모델을 전부 업데이트하지 않고 일부만 업데이트 하는 방식인 LoRA에 대해서 알아봅니다.

4.1. Low-Rank Apatation

<aside> 🔥

</aside>

4.2. Page Optimizer(페이지 옵티마이저)

<aside> 🔥

</aside>

4.3. QLoRA

<aside> 🔥

</aside>