23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
지난 1편에서는 배치 크기(Batch size)와 시퀀스 길이(Sequence length)를 조정하는 방법부터, 학습 과정에서 메모리를 아껴 쓰는 최적화 기법까지 다양한 기법을 살펴보았습니다.
요약하자면 비싼 GPU 메모리를 어떻게 효율적으로 활용해 적은 자원으로도 최적의 학습을 할 수 있을지를 고민해봤습니다.
이번에는 학습 알고리즘 최적화에 대해 이야기해보려고 합니다. 전편보다 조금 더 전문적인 내용을 다뤄야 할 것 같은데, 개발자의 시선으로 쉽게 풀어보려고 합니다.
이번 글에서는 다음과 같은 주제를 다루고 있습니다.
Optimizer 선택 기준
Learning Rate가 모델 학습에 미치는 영향
Learning Rate 스케줄링이 필요한 이유
연산 정밀도(Precision)에 따른 영향
Part1 편은 아래 링크를 참고하세요.
이번 포스팅에서 사용된 코드는 아래 GitHub 링크에서 다운로드할 수 있습니다.
모델 학습에는 HuggingFace Transformers 라이브러리를 활용했으며, 관련 Training Arguments에 대한 내용을 본문의 각 세션에 표기 했습니다.
실험 환경은 별도 언급이 없는 경우, 다음과 같은 환경에서 진행되었습니다.
Base 모델: Gemma-2b-it, Llama-3.2-3B-Instruct, Qwen2.5-3B-Instruct
파인튜닝 방식: Full, LoRA
Dataset : beomi/KoAlpaca-v1.1a (21,200 샘플)
GPU: NVIDIA A100 80G (단일)
딥러닝 모델을 학습할 때, 우리는 모델이 예측한 값과 실제 정답 간의 차이를 측정하기 위해 손실 함수(Loss Function)를 정의합니다.
모델을 학습한다는 것은 이 손실 값을 최소화하는 과정이며, 이것이 바로 최적화(Optimization)입니다.
Optimizer란 이러한 최적화를 수행하는 알고리즘입니다. 쉽게 말해 어떻게 하면 손실 함수의 최소값을 빨리 찾아 낼 방법을 결정하는 역할을 하는 것이죠
딥러닝 기법이 본격적으로 사용된 이래로 현재까지 많은 옵티마이저들이 제안되었지만, 현재 LLM 파인튜닝에서 가장 일반적으로 사용되는 옵티마이저는 Adam의 변형인 AdamW 입니다.
[출처 : https://www.slideshare.net/slideshow/ss-79607172/79607172 ]
optim="adamw_torch"
optim="adamw_8bit"
optim="paged_adamw_32bit"
optim="paged_adamw_8bit"
optim="adafactor"그럼 Adam 계열의 옵티마이저를 간단히 살펴보고, 메모리 효율화를 위해 개선된 형태도 살펴보겠습니다.
Adam은 마치 운전할 때 가속과 브레이크를 자동으로 조절하는 ‘스마트 크루즈 컨트롤’과 같은 역할을 합니다.
일반적인 경사 하강법(SGD)이 고정된 속도로 움직이는 것이라면, Adam은 1차 모멘트(평균)와 2차 모멘트(그래디언트 제곱값의 이동 평균)를 각각 추적하여 학습률을 자동으로 조정합니다.
1차 모멘트는 현재까지의 그래디언트 평균을 계산하여 방향과 속도를 결정하는 역할(가속 페달)을 하고, 2차 모멘트는 그래디언트의 변화량을 추적하여 급격한 변화를 방지하는 역할(브레이크)을 합니다.
즉, 변화가 크면 학습률을 줄이고, 변화가 적으면 학습률을 늘리는 방식으로 빠르고 안정적인 학습이 가능합니다.
단점으로는 두 개의 모멘트 값을 저장해야 하므로 메모리 사용량이 증가한다는 문제가 있습니다.
Adam이 가속과 브레이크를 조절하며 최적의 속도를 찾는다면, AdamW는 여기에 불필요한 연료 소모(가중치 증가)를 방지하는 연비 최적화 기능(Weight Decay)을 추가한 "연비 개선 스마트 크루즈 컨트롤"이라고 볼 수 있습니다.
기존 Adam은 Weight Decay를 손실 함수에 포함해 적용했지만, 이는 적응형 학습률과 충돌하여 정규화 효과가 약해지는 문제가 있었습니다.
AdamW는 Weight Decay를 그래디언트 업데이트 과정과 분리하여 적용함으로써, 불필요한 가중치 증가를 방지하고 더 강력한 정규화 효과를 제공하여 과적합을 줄이고 모델의 일반화 성능을 높입니다.
하지만 역시나 Adam과 마찬가지로 메모리 사용량이 많아지는 단점이 있습니다.
우리가 지금까지 학습했던 Gemma 2B 모델을 Adam 옵티마이저로 파인 튜닝할 경우, 얼마나 많은 메모리 공간이 필요할까요?
우선, 모델의 파라미터를 저장하는 데 8GB의 메모리가 필요합니다. (float32 기준, 파라미터당 4바이트)
여기에 Adam 옵티마이저가 사용하는 1차 모멘트와 2차 모멘트까지 고려하면, 추가적으로 2배의 공간이 필요하므로 총 24GB의 메모리를 소비하게 됩니다.
모델 크기가 2B에서 9B, 30B 등으로 커질수록 옵티마이저가 차지하는 메모리 역시 2n 배로 급격히 증가합니다.
금처럼 비싼 GPU 메모리를 감안하면, 보다 효율적인 메모리 관리 방법이 필수적입니다.
이를 해결하기 위해 메모리 사용량을 줄이는 다양한 옵티마이저 개선 기법이 연구되었습니다.
대표적인 방법으로는 알고리즘 자체를 최적화한 Adafactor와 같은 기법이 있으며, GPU 메모리를 보다 효과적으로 활용하기 위해 옵티마이저의 저장 공간을 양자화(Quantization)하거나, CPU의 메인 메모리를 활용하는 방식(Paging) 등이 제안되었습니다.
Adafactor는 Adam의 효율성을 유지하면서도 메모리 사용량을 대폭 줄인 최적화 알고리즘입니다.
일반적인 Adam은 모든 가중치마다 2차 모멘트(그래디언트 제곱값의 이동 평균)를 저장해야 하지만, Adafactor는 이를 행(row)과 열(column) 방향의 통계량만 유지하는 방식(factorization)을 사용하여 메모리 요구량을 감소시킵니다.
덕분에 대형 모델도 적은 자원으로 학습할 수 있으며, relative_step=True 옵션을 사용하면 학습률을 자동 조정할 수 있어 스케줄러와 웜업 같은 튜닝 부담도 줄일 수 있습니다.
다만 Adam에 비해 학습 성능이 다소 낮을 수 있으며, 학습이 불안정한 경우가 있습니다.
AdamW_8bit은 AdamW의 학습 성능을 유지하면서도 메모리 사용량을 줄이기 위해 8비트 정밀도를 사용하는 Optimizer입니다.
기존 AdamW는 32비트(fp32) 또는 16비트(fp16, bf16) 정밀도로 1차 및 2차 모멘트를 저장하지만, AdamW_8bit은 이를 8비트로 변환하여 저장하므로 메모리 사용량을 획기적으로 줄일 수 있습니다.
그러나, 8비트 정밀도로 Optimizer의 모멘트 값을 변환하면, 정밀도 손실로 인해 학습이 불안정해질 가능성이 있으며, 모델의 성능 저하가 발생할 수 있습니다.
Paged_adamW는 GPU 메모리 사용량을 줄이기 위해 CPU 메모리를 확장하여 활용하는 기술입니다.
일반적인 AdamW 옵티마이저를 사용할 경우, 1차 및 2차 모멘트 값을 포함한 모든 옵티마이저 상태가 GPU 메모리에 저장되므로, 대형 모델에서는 GPU 메모리 사용량이 급격히 증가하는 문제가 발생합니다.
반면, Paged_adamW는 모델 가중치 및 옵티마이저 상태를 연산이 필요한 순간에만 GPU로 로드하고, 일부를 CPU 메모리에 유지하는 방식을 사용하여 메모리 부담을 줄입니다.
그러나 CPU 메모리를 활용하는 특성상, CPU-GPU 간 I/O 병목이 발생할 수밖에 없는 근본적인 제약도 존재합니다.
만약 옵티마이저의 메모리 사용량을 극한까지 줄이고 싶다면, 앞서 살펴본 AdamW_8bit 양자화 기법에 페이징 기법까지 적용된 paged_adamw_8bit 옵티마이저를 활용한다면 사용량을 최소화 할 수 있습니다.
Gemma-2-2B-it 모델에 대해 동일한 학습 설정으로 Optimizer만 변경하며 실험을 진행했습니다.
사용한 Optimizer는 adamw_torch, adamw_8bit, paged_adamw_32bit, paged_adamw_8bit입니다.
그림1. Optimizer에 따른 학습 결과
Eval 결과 미세한 차이가 있긴 하지만, 전체적으로 거의 동일한 Loss 추이를 보이고 있습니다.
AdamW 에 양자화와 페이징 기법을 적용했지만 비교적 안정적으로 학습이 진행됨을 확인할 수 있네요.
하지만 메모리 사용에 있어서는 매우 큰 차이를 보입니다.
표 1의 결과를 보면, 양자화 및 페이징 기법을 적용한 Optimizer가 훨씬 더 적은 메모리를 사용한다는 것을 볼 수 있습니다.
이는 모델 크기가 커질수록 커질 수록 메모리 사용량에 대한 차이가 더욱 커질 것이라는 것을 짐작 할 수 있습니다.
Optimizer | Memory(GB) | Runtime(Sec) |
|---|---|---|
adamw_torch | 27.5 | 1,063 |
adamw_8bit | 19.1 | 1,091 |
paged_adamw_32bit | 15.2 | 1,126 |
paged_adamw_8bit | 15.2 | 1,092 |
adafactor | 23.9 | 1107 |
표 1. Optimizer 에 따른 메모리 사용
동일한 실험 환경에서 모델을 좀 더 큰 gemma-9b로 교체하고 Full 파인튜닝을 진행했습니다.
모델을 메모리에 로딩하고 학습을 시작하려며는 순간 아래와 같이 메모리 부족으로 학습이 중단됩니다.
Optimizer가 할당할 메모리가 부족한 것이죠.이때 메모리를 훨씬 적게 사용하는 adamw_8bit로 변경하면 여유롭게 학습을 진행 할 수 있습니다.
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 98.00 MiB. GPU 0 has a total capacity of 79.15 GiB of which 31.62 MiB is free. Including non-PyTorch memory, this process has 79.11 GiB memory in use. Of the allocated memory 78.42 GiB is allocated by PyTorch, and 196.49 MiB is reserved by PyTorch but unallocated.이번에는 adafactor의 결과를 살펴보겠습니다.
그림2. adafactor Optimizer 학습 결과
grad_norm 그래프를 보면, 학습 초기에 큰 변동성을 보입니다.
grad_norm이 갑자기 커진다는 것은 기울기(Gradient)가 불안정하다는 것을 의미하며, 학습 과정에서 급격한 변화가 발생했음을 나타냅니다.
초기 변동 이후에는 학습이 점차 안정화되어 잘 수렴되었지만, 만약 grad_norm이 지속적으로 불안정한 상태에서 증가한다면, 학습 수렴에 문제를 일으킬 수 있습니다.
또한, 메모리 절감 측면에서도 AdamW_8bit 등의 양자화 기법과 비교했을 때 큰 메리트가 없기도 합니다.
메모리 제약이 없다면 AdamW가 가장 안정적이고 효과적인 선택입니다.
메모리 제약이 있다면, 먼저 양자화 기법인 adamw_8bit를 적용해보고, 추가적인 메모리 확보가 필요하다면 paged_adamw_8bit를 적용합니다.
adafactor는 메모리 효율성이 뛰어나지만, 학습이 불안정해질 가능성이 있습니다.
adamw_8bit도 adafactor만큼 메모리를 절감할 수 있으므로, 학습 안정성을 고려한다면 AdamW 계열이 더 적합합니다.
학습 시 양자화 기법을 적용하면, 정밀도가 낮아져 성능에 영향을 줄 수 있으며, Loss를 모니터링하는 것뿐만 아니라 학습 후 모델 평가를 통해 출력 결과의 품질을 확인하는 것이 중요합니다.
LoRA 학습시 Optimizer 선택
예제로 제시된 실험은 모두 Full 파인튜닝 결과입니다. 즉, 모델의 모든 파라미터를 업데이트하는 방식입니다.
그러나, LoRA와 같은 PEFT 방식에서는 학습해야 할 파라미터 개수가 현저히 줄어들어 Optimizer가 사용하는 메모리도 함께 감소합니다.
따라서, LoRA 방식으로 파인튜닝할 경우, 굳이 양자화나 페이징 기법을 적용한 Optimizer를 사용할 필요 없이 AdamW를 그대로 사용하는 것을 권장합니다.
다만, LoRA를 사용하더라도 메모리가 부족한 경우에는 양자화나 페이징 기법을 적용한 Optimizer 사용을 고려할 수 있습니다.
LLM 파인튜닝에서 학습률(Learning Rate)은 Batch 크기와 함께 모델 학습에 가장 큰 영향을 미치는 하이퍼파라미터 중 하나입니다.
딥러닝을 공부해보신 분들이라면 손실 함수의 최소값을 찾아가는 과정에서 학습률이 어떻게 작용하는지 보여주는 아래와 같은 그래프를 많이 보셨을 것입니다.
그림3. learning rate
학습률은 최적의 손실값을 찾기 위해 한 번에 이동하는 거리를 결정합니다.
학습률이 너무 작으면 최소값을 찾는 데 시간이 오래 걸릴 뿐만 아니라, 국소 최적점(local minima)에 갇혀 더 좋은 최적점을 찾지 못할 수 있습니다.
반대로 학습률이 너무 크면 최소값을 지나쳐버려 손실이 수렴하지 않고 발산할 수 있죠.
따라서 적절한 학습률을 찾는 것이 매우 중요하며, 일반적으로 여러 번의 실험을 통해 최적값을 찾아내는 과정이 필요합니다.
learning_rate=1e-04LLM을 파인튜닝할 때 적절한 학습률은 일반적으로 1e-3 ~ 1e-6 사이 어딘가에 위치합니다.
학습률을 찾는 가장 효과적인 방법은 이 범위의 중간값으로 실험을 시작한 후, 학습 결과를 바탕으로 조정해 나가는 것입니다.
예를 들어, 1e-4로 학습했을 때 성능이 5e-4보다 더 좋았다면, 5e-4보다 높은 학습률은 성능이 더 떨어질 가능성이 큽니다.
따라서 1e-4 이하의 범위에서 더 적절한 학습률을 탐색하는 것이 효과적일 수 있습니다.
아래는 학습률 변화에 따른 학습 성능을 비교하기 위해 5e-3부터 1e-6까지 다양한 학습률로 실험을 진행한 결과입니다.
그림4. learning rate 실험
실험 결과를 살펴 볼까요?
5e-3: 손실값(Loss)이 발산하여 학습이 제대로 진행되지 않음
1e-4, 5e-5: 가장 낮은 Loss를 기록했으며, 그중 1e-4가 좀 더 낮은 최종 Loss를 보임
1e-5, 1e-6: Loss의 수렴 속도가 상대적으로 느리고 진행을 볼 때 최적값에 도달하지 못할 가능성이 있고, 도달하더라도 상당한 시간이 필요할 것으로 보임
즉, 1e-4 근처의 학습률이 가장 적절한 선택으로 보이며, 지나치게 낮은 학습률(1e-5 이하)은 학습이 비효율적일 가능성이 높습니다.
이번에는 gemma에서 가장 좋은 성능을 보인 학습률 1e-4를 동일한 학습 설정으로 Llama-3.2-3B와 Qwen2.5-3B에도 적용하여 학습을 진행 했습니다.
그림5. 여러 모델간 earning rate 실험
Loss의 수렴 패턴은 전반적으로 비슷하지만, 수렴 속도와 최종 Loss에 차이가 발생 합니다.
이는 모델 아키텍처에 따라 학습률이 다르게 작용할 수 있음을 의미하며, 모델 구조, 데이터셋, 하이퍼파라미터 등의 변경이 있을 경우 학습률을 다시 검증하는 과정이 필요하다는 것을 보여줍니다.
1e-4를 기준으로 학습을 시작한 후, 더 높은 학습률과 더 낮은 학습률을 각각 실험하며 성능이 개선되는 방향으로 탐색하여 최적의 학습률을 찾습니다.
모델 아키텍처, 데이터셋, 하이퍼파라미터 설정에 따라 최적의 학습률은 달라질 수 있으므로, 사전 검증 과정을 거치는 것이 중요합니다.
보다 안정적인 학습을 위해 학습률 스케줄링 기법인 Warmup 적용을 고려 합니다.
앞서 학습률이 모델 학습 성능에 큰 영향을 미친다는 점을 확인했습니다.
그렇다면 우리가 찾은 최적의 학습률 1e-4를 학습 시작부터 종료 시점까지 동일하게 유지하는 것이 좋을까요?
실험적으로 입증된 바에 따르면, 동일한 학습률을 유지하는 것보다 학습률을 동적으로 조정하면서 학습을 진행하는 것이 더 효과적입니다.
lr_scheduler_type="linear"
lr_scheduler_type="cosine"
lr_scheduler_type="constant "이전 섹션에서 살펴본 것처럼, 학습률이 너무 낮으면 학습 속도가 느려지고, 반대로 너무 높으면 발산할 위험이 있습니다.
하지만 전체 학습 과정에서 동일한 학습률을 유지하는 것이 최선의 선택이 아닐 수 있습니다.
일반적으로 학습 초기에는 빠른 수렴을 위해 비교적 높은 학습률이 필요하지만, 후반부에는 더 정밀한 최적화를 위해 학습률을 점진적으로 낮추는 것이 효과적입니다.
쉽게 말해, 학습 초반에는 큰 걸음으로 멀리 내다보며 나아가다가, 학습이 진행될수록 보폭을 줄이며 세밀하게 조정해 나가는 것이 필요하다는 것이죠.
이처럼 학습률을 동적으로 조정해주는 기법을 학습률 스케줄러라고 합니다.
학습률 스케줄러에는 대표적으로 Cosine, Linear, Constant 방식을 꼽을 수 있습니다.
Cosine: 학습률을 코사인 함수 곡선을 따라 감소시키는 방식
Linear: 일정한 비율로 학습률을 선형적으로 감소시키는 방식
Constant: 학습률을 일정하게 유지하는 방식
아래 실험은 학습률 스케줄러를 변경하며 학습한 결과로, step에 따른 학습률 변화 양상을 보여줍니다.
그림6. learning rate scheduler에 따른 학습률 변화
결과를 보면 각 스케줄러별로 학습률이 조정되는 방식의 차이를 직관적으로 확인할 수 있습니다.
일반적으로 Cosine과 Linear 방식은 학습 성능에서 큰 차이를 보이지 않습니다만, 특정 상황에서는 성능 차이가 발생할 수 있습니다.
따라서 학습률과 마찬가지로 실험을 통해 적절한 스케줄러를 선택하는 것이 바람직합니다.
Constant 방식의 경우 학습률을 고정하는 방식으로, 동적인 조정이 필요하지 않은 특수한 경우에만 사용됩니다.
cosine과 linear 방식은 대부분의 경우 성능 차이가 크지 않지만, 실험을 통해 해당 모델과 데이터에 더 적합한 방식을 선택하는 것이 좋습니다.
constant 스케줄러는 일반적인 LLM 파인튜닝에서는 권장 되지 않습니다.
Warmup은 학습률 스케줄러와 함께 사용되는 기법입니다.
앞서 학습률 스케줄러가 초기에 높은 학습률을 사용하고 이후 점진적으로 낮추는 방식으로 동작한다고 설명했는데, 웜업은 이와 반대로 학습 초기에 낮은 학습률에서 시작하여 설정된 학습률까지 점진적으로 높여가며 학습하는 기법입니다.
warmup_ratio=0.1
warmup_steps=100학습을 시작할 때 너무 높은 학습률을 적용하면, 학습이 급격히 발산하거나 최적의 방향을 찾지 못할 위험이 있습니다.
이를 방지하기 위해 초기 일정 스텝 동안 학습률을 점진적으로 증가시키는 Warmup 기법을 사용합니다.
즉, 학습 초반에는 낮은 학습률로 시작한 후 이를 점진적으로 증가시키고, 이후에는 스케줄러에 따라 학습률을 조정하는 방식입니다.
Warmup을 적용하는 방식에는 두 가지 방법이 있습니다.
Warmup Step
학습률을 점진적으로 증가시키는 기간을 step 수로 지정하는 방식
warmup_steps=1000 : 1000 step 동안 학습률을 증가시킴
Warmup Ratio:
전체 학습 과정에서 Warmup이 차지하는 비율을 지정하는 방식
warmup_ratio=0.1 : 전체 step의 10% 동안 학습률을 증가시킴
두 방식 모두 사용 가능하지만, 일반적으로 총 step 수를 고려해야 하는 Warmup Step 방식보다는 설정이 간편한 Warmup Ratio 방식을 더 선호합니다.
이번에는 웜업 적용 여부에 따른 실험을 진행했습니다.
그림 7. Warmup 적용
실험 결과를 살펴보면,
웜업을 적용하지 않은 경우에는 초기 학습률이 5e-4로 고정된 상태에서 학습이 시작되고, 이후 linear 스케줄러에 의해 점진적으로 감소합니다.
이때 초기 학습이 불안정해지고 loss가 급격히 변동하는 현상이 발생합니다.
시간이 지나면서 점차 안정화되지만, 최종 성능은 웜업을 적용한 경우보다 좋지 않았습니다.
반면, 웜업을 적용한 경우에는 초기 학습률이 0에서 시작하여 warmup 단계 동안 5e-4 까지 점진적으로 증가한 후, 이후 스케줄러에 따라 점진적으로 감소합니다.
초기 학습이 더 안정적으로 진행되고 loss도 더 완만하게 감소하여 더 나은 최종 성능을 얻을 수 있었습니다.
작은 모델 : warmup_ratio = 0.05, 학습 초기 안정성이 높아 짧은 웜업으로도 잘 동작하는 편입니다
큰 모델 : warmup_ration = 0.1, 초기 가중치 변화가 크므로 안정적인 학습을 위해 좀 더 긴 웝업이 필요합니다.
큰 데이터셋 : 데이터셋이 클수록 더 긴 웜업 단계가 필요할 수 있습니다
작은 데이터셋 : 데이터셋이 작은 경우 학습 초기에 빠른 수렴이 필요하므로 웜업을 줄이는 것이 효과적입니다
모델에서 사용하는 FP32, FP16, BF16 등의 정밀도는 숫자를 표현하는 방식(부동소수점 형식)과 그에 따른 연산의 정확도를 의미합니다.
이는 데이터를 얼마나 세밀하게 표현할 수 있는지를 나타내며, 정밀도가 높을수록 더 넓은 범위의 숫자를 더 정확하게 표현할 수 있습니다.
다만 높은 정밀도는 더 많은 메모리와 계산 자원을 필요로 하므로, 모델의 성능과 효율성을 고려하여 적절한 정밀도를 선택해야 합니다.
bf16=True아래 표는 llama-3-8b 모델을 2,048 시퀀스로 8 배치로 학습할 때 필요한 메모리 요구량입니다.
이는 메모리 최적화 기법이나 Optimizer 양자화 등이 적용되지 않은 순수 요구량이므로, 생각 했던 보다 훨씬 더 높게 보이네요.
여기에 메모리 최적화 기법이 적용되면 FP32와 FP16, BF16 간의 메모리 요구량 차이는 더욱 커지게 될 것입니다.
이것이 우리가 FP16 이나 BF16을 선택할 수밖에 없는 이유입니다.
Precision | Parameters | KV Cache | Activation | Optimizer | Gradient | Total |
|---|---|---|---|---|---|---|
FP32 | 29.8 | 16 | 28.5 | 59.6 | 29.8 | 163.7 GB |
FP16 | 14.9 | 8 | 28.5 | 59.6 | 29.8 | 140.8 GB |
BF16 | 14.9 | 8 | 28.5 | 59.6 | 29.8 | 140.8 GB |
표2. 계산출처 : https://llm-dev-tools.streamlit.app/Memory
FP16과 BF16은 모두 16비트 부동소수점 형식으로 메모리 효율을 높이고 연산 속도를 개선하는 역할을 합니다.
하지만 두 방식은 정밀도와 안정성에서 차이가 있으며, 특히 파라미터 크기가 큰 LLM 모델 학습에서는 BF16이 더 유리합니다.
FP32 vs FP16
FP16은 FP32의 절반의 메모리만을 사용하면서 연산 속도를 향상시킬 수 있어 효율적입니다.
하지만 표현 가능한 숫자의 범위가 줄어들어 매우 크거나 작은 값을 정확하게 저장하지 못할 수 있습니다.
이로 인해 연산 과정에서 오버플로우(값이 너무 커짐)나 언더플로우(값이 너무 작아짐) 문제가 발생할 수 있으며, 이를 방지하기 위해 손실 스케일링(loss scaling) 기법이 필요합니다.
손실 스케일링은 숫자의 크기를 조정해 안정적인 연산을 가능하게 하지만, 적절한 값을 찾지 못하면 학습이 불안정해질 수 있습니다.
FP16 vs BF16
BF16은 FP16과 동일한 16비트 형식이지만, FP32와 같은 8비트 지수를 유지하여 보다 넓은 숫자 범위를 표현할 수 있습니다.
이로 인해 FP16에서 발생할 수 있는 오버플로우, 언더플로우 문제를 줄일 수 있어, 큰 모델일 수 록 더 안정적인 학습이 가능합니다.
또한, FP16은 숫자 표현 한계로 인해 연산 중 손실 스케일링(loss scaling) 과정을 거쳐야 하지만, BF16은 이러한 추가 조정 없이도 안정적으로 학습할 수 있어 학습 과정이 단순하고 효율적입니다.
LLM 모델 학습에서 BF16은 FP16에 비교해서 더 안정적이고 효율적 입니다.
특히, 하드웨어(NVIDIA Ampere GPU, Google TPU)에서 직접 사용할 수 있도록 최적화되어 있어, NVIDIA Ampere 계열 이상의 GPU를 사용한다면 기본 사용을 권장 합니다.
높은 정밀도를 요구하지 않는 LLM 파인튜닝시 BF16의 정밀도를 사용하는 것이 메모리와 학습 성능에 유리 합니다.
NVIDIA Ampere 계열 이상의 GPU에서 BF16 지원이 최적화되어 있으므로 이를 사용하는 것이 좋습니다.
Part 1과 Part 2를 통해 LLM 파인 튜닝의 핵심 요소들과 하이퍼파라미터 최적화 방법에 대해 알아보았습니다.
물론, 이번 글에서 다루지 못한 세부적인 튜닝 요소들이 더 있지만, 지금까지 소개한 기법들만 잘 조정하더라도 안정적인 학습을 진행할 수 있을 것으로 생각 합니다.
한가지 더, 이번 포스팅에서 다루지 못했지만 파인튜닝에 있어 가장 중요한 요소가 바로 데이터셋의 품질입니다.
아무리 학습을 최적화 했다고 해도, 데이터셋의 품질이 뒷받침되지 않으면 원하는 성능을 얻기 어렵습니다.
따라서 기대한 성능이 나오지 않는다면, 데이터셋의 크기, 품질, 다양성 등을 우선적으로 점검하는 것이 중요합니다.
비록 이번 실험은 NVIDIA A100으로 진행했지만, 최적화 기법들을 잘 활용하면 작은 크기의 모델의 경우 RTX GPU에서도 충분히 파인튜닝이 가능합니다.
중요한 것은 실제로 시도해보는 것이니까요!
소개된 기법들이 파인튜닝에 도움이 되길 바라면서 글을 마무리 합니다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.