
딥러닝 모델을 학습할 때 가장 현실적인 문제는 vram 부족이다. 필요한 vram이 물리적인 크기를 초과할 경우 OOM 오류가 뜨거나 블루스크린(MEMORY MANAGEMENT ERROR)가 뜨기도 한다. 이런 상황에서, vram 사용량에 크게 영향을 주는 배치사이즈를 직접적으로 늘리지 않더라도 메모리 사용량을 최적화하여 모델을 학습하는 방법에 대해 공부하고 정리한다.
문제인식
현재 오픈소스 LLM을 LoRA 파인튜닝 하여 수능과 같은 수리논술문제 풀이에 최적화된 모델을 만드는 프로젝트를 진행하고 있다. 프로젝트와 데이터에 따라 크게 차이가 나지만 현재 진행중인 프로젝트에서 RTX 3090으로 파라미터 7~8B의 모델을 돌리면 vram 24GB 이상을 요구한다. 4비트로 양자화 하여 모델을 학습시켜도 파라미터 14B정도가 마지노선이다. 파라미터크기가 30B이 넘어가는 모델은 vram 24GB로는 학습자체가 불가능하며, 모델파라미터와 데이터 길이로 인해 배치사이즈는 1이상 키우기 힘든 상황이다. 따라서 허깅페이스 문서를 참고하여 한정된 single GPU vram에서 학습을 최적화 할 수 있는 방법론들을 정리하고, 적용해본 결과를 정리해보고자 한다.
이론 학습
model training on single gpu
대규모 모델 훈련 시에는 다음 두 가지 측면을 동시에 고려해야 한다.
1. 데이터 처리량/훈련 시간
2. 모델 성능
초당 학습 샘플 개수로 측정되는 throughput을 극대화하는 것은 학습 비용을 낮추는 것으로 연결된다.
일반적으로 GPU를 최대한 많이 사용하고, GPU 메모리를 그 한계까지 사용함으로써 이를 달성할 수 있다.
만약 배치사이즈가 GPU 메모리를 초과하면, Gradient Accumulation과 같은 메모리 최적화 기술이 도움이 될 수 있다.
그러나 선호하는 배치 크기가 메모리에 맞는 경우, 이 기술들이 훈련 속도를 늦출 수 있기 때문에 메모리 최적화 기술을 적용할 이유가 없다. 큰 배치 크기를 사용할 수 있다고 해서 반드시 사용해야 하는 것은 아니다.
하이퍼파라미터 튜닝의 일환으로, 어떤 배치 크기가 가장 좋은 결과를 가져오는지 확인하는 것이 우선이며 원하는 배치사이즈와 학습률을 설정하기에 GPU자원이 부족할 때 아래와 같은 방법론들을 시도해볼 수 있다.
| Method/Tool | Improves Training Speed | Optimizes Memory Utilization |
|---|---|---|
| Batch size choice | Yes | Yes |
| Gradient accumulation | No | Yes |
| Gradient checkpointing | No | Yes |
| Mixed precision training | Yes | Maybe* |
| torch_empty_cache_steps | No | Yes |
| Optimizer choice | Yes | Yes |
| Data preloading | Yes | No |
| DeepSpeed Zero | No | Yes |
| torch.compile | Yes | No |
| Parameter-Efficient Fine Tuning (PEFT) | No | Yes |
참고: 작은 모델과 큰 배치 크기를 사용할 때는 혼합 정밀도(mixed precision)로 메모리 절감이 일부 있을 수 있지만, 큰 모델과 작은 배치 크기를 사용할 때는 메모리 사용량이 더 클 수 있다.
위에서 언급한 방법들을 결합하여 누적 효과를 얻을 수 있다. 이러한 기술들은 Trainer를 사용하여 모델을 학습하거나 순수 PyTorch 루프를 작성할 때 모두 사용할 수 있고, 이 경우 Accelerate를 사용하여 이러한 최적화를 구성할 수 있다.
배치 사이즈 선택
모델 학습 속도와 메모리 활용의 효율성을 증대하기 위한 최적화 기법을 이해하기 위해, 최적의 성능을 얻기 위해 먼저 적절한 배치 크기를 식별해야 한다. 배치 크기와 입력/출력 뉴런 수는 크기가 2의 거듭제곱(2^N)의 크기를 사용하는 것이 권장된다. 8의 배수를 일반적으로 사용할 수 있지만, 사용 중인 하드웨어와 모델의 dtype에 따라 더 높은 숫자를 선택하는 것이 가능하다.
General Matrix Multiplication(GEMM)에 포함되어 있는 fully connected layer에 대한 입력/출력 뉴런 수 및 배치 사이즈에 대한 해서는 NVIDIA의 권장 사항을 확인해보면 좋다.
Tensor Core Requirements는 데이터 유형(dtype)과 하드웨어에 따라 배수를 정의한다. 예를 들어, fp16 데이터 유형에서는 8의 배수를 사용하는 것이 권장되며, A100 GPU의 경우에는 64의 배수를 사용하는 것이 좋다.
작은 파라미터의 경우 Dimension Qualtization Effects를 고려할 수도 있다. 이 과정에서 타일링(tiling)이 발생하며, 적절한 배수를 사용하는 것이 큰 속도 향상을 가져올 수 있다.
Gradient Accumulation
Gradient Accumulation 방법은 전체 배치에 대해 한 번에 기울기를 계산하는 대신, 더 작은 단위로 나누어 기울기를 계산하는 것을 목표로 한다. 이 접근법은 모델을 통해 더 작은 배치로 순방향(forward) 및 역방향(backward) 패스를 수행하면서 기울기를 점진적으로 계산하고, 이 기울기를 누적하는 방식이다. 충분한 양의 기울기가 누적되면 모델의 최적화 단계가 실행된다. Gradient Accumulation을 사용하면 GPU 메모리 용량의 제한을 넘어서는 효과적인 배치 크기를 늘릴 수 있게 된다.
그러나 Gradient Accumulation으로 인한 추가적인 순방향 및 역방향 패스로 인해 학습 속도가 느려질 수 있다는 점을 유의해야 한다.
Gradient Accumulation은 TrainingArguments에 gradient_accumulation_steps 인자를 추가하여 활성화할 수 있다.
training_args = TrainingArguments(per_device_train_batch_size=1,
gradient_accumulation_steps=4,
**default_args)
위 코드에서 유효 배치 사이즈는 4가 된다.
가능한 한 GPU 사용률을 최대한 활용하는 것이 권장되지만, Gradient Accumulation 단계가 많아질수록 학습 속도 저하가 더 두드러질 수 있다.
다음 예를 고려해보자. per_device_train_batch_size=4인 경우 Gradient Accumulation 없이도 GPU 한계에 도달했다고 가정해보자. 배치 크기 64로 학습하고 싶다면, per_device_train_batch_size를 1로 설정하고 gradient_accumulation_steps를 64로 설정하는 것은 권장되지 않는다. 대신 per_device_train_batch_size=4로 유지하고 gradient_accumulation_steps=16으로 설정하는 것이 좋다. 이렇게 하면 동일한 효과적인 배치 크기를 가지면서 GPU 리소스를 더 잘 활용할 수 있게 된다.
허깅페이스 transformer 라이브러리에서 RTX-3090 및 A100에 대한 배치 크기 벤치마크를 참고할 수 있다.
Gradient Checkpointing
일부 대형 모델은 배치 크기를 1로 설정하고 Gradient Accumulation을 사용해도 여전히 메모리 문제를 겪을 수 있다. 이는 배치 크기 외에도 메모리 저장이 필요한 다른 구성 요소들이 있기 때문이다.
기울기를 계산하기 위해 backward pass에서 모든 활성화값(activation)을 저장하는 것은 상당한 메모리 오버헤드를 초래할 수 있다.
그렇다고 대안으로 활성화값을 폐기하고, 필요할 때 역방향 패스에서 다시 계산할 수 있지만, 이는 상당한 계산 오버헤드를 일으켜 학습 속도를 늦춘다.
Gradient Checkpointing은 이러한 두 가지 접근법 사이에서 절점을 제공한다. 계산 그래프 전반에 걸쳐 전략적으로 선택된 활성화를 저장하므로 activation의 일부만 그래디언트에 대해 다시 계산하면 된다.
Trainer에서 Gradient Checkpointing을 활성화하려면 TrainingArguments에 해당 플래그를 전달하면 된다
training_args = TrainingArguments(
per_device_train_batch_size=1, gradient_accumulation_steps=4, gradient_checkpointing=True, **default_args
)
Gradient Checkpointing은 메모리 효율을 개선할 수 있지만, 학습 속도를 약 20% 정도 늦춘다는 점을 염두에 두어야 한다.
Mixed Precision Training
Mixed Precision Training은 일부 변수에 대해 낮은 정밀도의 수치 형식을 사용함으로써 모델 학습의 계산 효율을 최적화하는 기법이다. 전통적으로 대부분의 모델은 변수들을 표현하고 처리하기 위해 32비트 부동 소수점 정밀도(fp32 또는 float32)를 사용한다. 하지만 모든 변수에서 높은 정밀도가 필요한 것은 아니다. 일부 변수의 정밀도를 16비트 부동 소수점(fp16 또는 float16)과 같은 낮은 수치 형식으로 줄임으로써 계산 속도를 높일 수 있다. 이 접근법에서는 일부 계산은 반 정밀도(half precision)로, 일부는 여전히 전체 정밀도(full precision)로 수행되기 때문에 Mixed Precision Training이라고 불린다.
Mixed Precision Training은 보통 fp16(float16) 데이터 유형을 사용하여 이루어진다. 하지만 일부 GPU 아키텍처(예: Ampere 아키텍처)에서는 bf16 및 tf32(CUDA 내부 데이터 유형)와 같은 데이터 유형도 제공한다. 이러한 유형의 차이점은 NVIDIA Blog를 통해 확인할 수 있으며 아래 사용법을 제공한다.
fp16
Mixed Precision Training의 주요 장점은 활성화값을 반 정밀도(fp16)로 저장함으로써 얻을 수 있다. 기울기 역시 반 정밀도로 계산되지만, 최적화 단계에서는 다시 전체 정밀도로 변환되므로 여기서 메모리 절감 효과는 없다. fp16을 사용하는 이유는 메모리 효율이 아닌 계산 속도에 있다.
그 이유는 현대의 GPU(특히 NVIDIA의 Ampere 아키텍처 등)는 fp16 연산을 처리하는 Tensor Cores와 같은 특수 하드웨어를 가지고 있기 때문이다. 이러한 하드웨어는 낮은 정밀도인 fp16 형식을 사용함으로써 계산 속도를 크게 높일 수 있다. 이로 인해 학습 시간도 줄어들게 된다.
즉, fp16은 메모리를 아끼기 위한 것이 아니라, 연산 속도 향상을 위해 사용되는 것이다. 모델을 더 빠르게 훈련시키기 위해서는 GPU의 연산 능력을 최대한 활용하는 것이 중요한데, fp16을 사용하면 이러한 목적을 달성할 수 있다.
다만 fp16을 사용할 때 모든 계산을 반 정밀도로 처리하는 것이 아니라 중요한 연산은 여전히 fp32를 유지하여 모델의 정확도를 유지하면서도 속도를 높이는 것이다. 이 혼합된 접근 방식이 바로 Mixed Precision Training이며, 이 때문에 fp16을 사용해도 메모리에서 이점은 없더라도 계산 효율 면에서 중요한 이점이 있다.
Mixed Precision Training은 더 빠른 계산을 가능하게 하지만, 특히 작은 배치 크기의 경우 더 많은 GPU 메모리가 사용될 수 있다. 이는 모델이 GPU에 16비트와 32비트 정밀도로 모두 존재하기 때문으로, 결과적으로 GPU 상에서 원래 모델의 약 1.5배 크기를 차지하게 된다.
Mixed Precision Training을 활성화하려면 fp16 플래그를 True로 설정하면 된다.
training_args = TrainingArguments(per_device_train_batch_size=4, fp16=True, **default_args)
BF16
Ampere 또는 그 이상의 하드웨어(RTX 3090, A100이상) 를 사용할 수 있다면, BF16을 사용하여 Mixed Precision Training과 평가를 수행할 수 있다. BF16은 FP16보다 정밀도는 낮지만, 훨씬 더 큰 동적 범위를 가지고 있다. FP16에서 표현할 수 있는 가장 큰 수는 65504이며, 이를 초과하는 수는 오버플로우가 발생한다. 반면 BF16은 최대 3.39e+38의 값을 가질 수 있으며, 이는 FP32와 거의 동일하다. 그 이유는 둘 다 수치 범위를 나타내는 데 8비트를 사용하기 때문이다.
Trainer에서 BF16을 활성화하려면 다음과 같이 설정하면 된다.
training_args = TrainingArguments(bf16=True, **default_args)
BF16은 더 큰 동적 범위를 가지고 있기 때문에, 오버플로우 문제를 줄이면서도 Mixed Precision Training의 이점을 취할 수 있다. Ampere와 같은 최신 하드웨어에서는 BF16을 사용하여 모델 학습을 최적화하는 것이 좋은 선택일 수 있다.
TF32
Ampere 하드웨어(RTX 3090, A100이상)는 TF32를 지원한다. TF32는 FP32와 동일한 수치 범위(8비트)를 가지지만, 정밀도는 23비트 대신 10비트(FP16과 동일)만 사용하며 전체적으로 19비트만 사용한다.
TF32가 "마법 같다"고 불리는 이유는 FP32를 사용한 일반적인 학습 및 추론 코드에서 TF32 지원을 활성화하면 최대 3배의 처리량 향상을 얻을 수 있기 때문이다. 이를 위해 다음과 같은 코드를 추가하면 된다.
import torch
torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = True
위 설정을 추가하면, 사용 중인 GPU가 Ampere 시리즈인 경우 CUDA가 가능한 한 FP32 대신 TF32를 자동으로 사용하게 된다. NVIDIA의 연구에 따르면 대부분의 머신러닝 학습 작업에서 TF32 학습을 사용할 때 FP32와 동일한 퍼플렉서티(perplexity)와 수렴(convergence)을 보인다고 한다. 이미 FP16 또는 BF16 혼합 정밀도를 사용 중이라면 TF32가 처리량을 향상하는 데 도움이 될 수 있다.
Trainer에서 TF32 모드를 활성화하려면 다음과 같이 설정할 수 있다.
training_args = TrainingArguments(tf32=True, **default_args)
TF32는 tensor.to(dtype=torch.tf32)로 직접 접근할 수 없다. 이는 CUDA의 내부 데이터 유형이기 때문이다. TF32 데이터 유형을 사용하려면 torch>=1.7이 필요하다.
TF32와 다른 정밀도의 차이에 대한 추가 정보는 RTX-3090 및 A100에 대한 벤치마크를 참조할 수 있다.
FP32, TF32, BF32, fp16에 대한 정리
| 특성 | fp16 (Float16) | BF16 (Bfloat16) | TF32 | FP32 (Float32) |
|---|---|---|---|---|
| 비트 수 | 16비트 | 16비트 | 19비트 | 32비트 |
| 정밀도 (Mantissa) | 10비트 | 7비트 | 10비트 | 23비트 |
| 지수 (Exponent) | 5비트 | 8비트 | 8비트 | 8비트 |
| 동적 범위 | 최대 65504 | 최대 3.39e+38 | 최대 3.39e+38 | 최대 3.39e+38 |
| 정밀도 수준 | 낮음 | 낮음 | 낮음 | 높음 |
| 사용 대상 | 속도 최적화, *메모리 절감 | 안정성 개선, 동적 범위 넓음 | 처리량 최적화 (Ampere) | 높은 정밀도 요구 작업 |
| 주요 장점 | 메모리 절감, 속도 향상 | 동적 범위 넓어 안정성 높음 | FP32와 유사한 범위, 높은 처리량 | 높은 정밀도, 안정적 학습 |
| 지원 하드웨어 | 대부분의 GPU | Ampere 아키텍처 (RTX 3090, A100)이상 |
Ampere 아키텍처 (RTX 3090, A100)이상 |
대부분의 GPU |
| 활용 사례 | 혼합 정밀도 학습, 속도향상 |
큰 모델의 안정적 학습 | FP32 대체, 성능 최적화 | 기본 수치 계산 |
| 학습 속도 | 매우 빠름 | FP16보다는 느림 | FP32 대비 최대 3배 향상 | 상대적으로 느림 |
*fp16을 쓰더라도 실제로는 불안정성으로 인해 중요한 연산은 여전히 fp32를 유지하여 메모리를 실제로는 1.5배까지 사용할 수 있다.
FP32와 TF32는 정밀도 높은 계산의 처리량 최적화 측면에서 비교되며, FP16과 BF16은 메모리 절감 및 연산 효율성 측면에서 비교될 수 있다.
Flash Attention 2
Flash Attention 2를 사용하여 transformers와 통합하면 학습 처리량을 가속할 수 있다. Flash Attention 2 모듈을 사용하여 모델을 로드하는 방법에 대해서는 단일 GPU 섹션의 적절한 부분을 참고하자.
Optimizer 선택
Transformer 모델을 학습할 때 가장 일반적으로 사용하는 옵티마이저는 Adam 또는 AdamW(weight decay가 포함된 Adam)이다. Adam은 이전 기울기의 이동 평균을 저장하여 좋은 수렴성을 보장하지만, 모델 파라미터 수에 비례하는 추가 메모리 사용량이 발생한다.
이를 해결하기 위해 대안적인 옵티마이저를 사용할 수 있다. 예를 들어, NVIDIA GPU의 경우 NVIDIA/apex를 설치하거나 AMD GPU의 경우 ROCmSoftwarePlatform/apex를 설치하여 adamw_apex_fused를 사용하면 지원되는 모든 AdamW 옵티마이저 중 가장 빠른 학습을 보여준다.
허깅페이스 Trainer은 즉시 사용할 수 있는 다양한 옵티마이저를 제공한다. (adamw_hf, adamw_torch, adamw_torch_fused, adamw_apex_fused, adamw_anyprecision, adafactor, adamw_bnb_8bit)
그중에서 adafactor, adamw_bnb_8bit 옵티마이저를 3B-파라미터 모델(예: "google-t5/t5-3b")를 통해 비교해보자.
- 표준 AdamW 옵티마이저는 각 파라미터에 8바이트를 사용하기 때문에 24GB의 GPU 메모리가 필요하다 (8*3 => 24GB).
- Adafactor 옵티마이저는 12GB 이상이 필요하다. 각 파라미터에 대해 약간 더 많은 4바이트를 사용하기 때문에 대략 4*3 그리고 약간의 추가 메모리가 필요하다.
- 8비트 BNB 양자화 옵티마이저는 모든 옵티마이저 상태가 양자화된 경우 (2*3) 6GB만 사용한다.
Adafactor
Adafactor는 가중치 행렬의 각 요소에 대해 이동 평균을 저장하지 않는다. 대신, 행 및 열별로 이동 평균의 합을 유지하며, 이를 통해 메모리 사용량을 크게 줄일 수 있다. 하지만 Adam과 비교했을 때 일부 경우에는 수렴 속도가 느릴 수 있다.
Adafactor로 전환하려면 TrainingArguments에서 optim="adafactor"로 설정하면 된다.
training_args = TrainingArguments(per_device_train_batch_size=4, optim="adafactor", **default_args)
다른 접근법들(Gradient Accumulation, Gradient Checkpointing, Mixed Precision Training)과 결합하면, 처리량을 유지하면서 최대 3배의 개선을 확인할 수 있다.
하지만 앞서 언급했듯이, Adafactor의 수렴은 Adam보다 느릴 수 있다는 점을 유의해야 한다.
8-bit Adam
Adafactor처럼 옵티마이저 상태를 집계하는 대신, 8-bit Adam은 전체 상태를 유지하면서 이를 양자화한다. 양자화(Quantization)는 상태를 더 낮은 정밀도로 저장하고, 최적화를 위해서만 이를 복원(dequantize)하는 것을 의미한다. 이는 혼합 정밀도 학습(Mixed Precision Training) 뒤에 있는 아이디어와 유사하다.
adamw_bnb_8bit을 사용하려면 간단히 TrainingArguments에서 optim="adamw_bnb_8bit"로 설정하면 된다.
training_args = TrainingArguments(per_device_train_batch_size=4, optim="adamw_bnb_8bit", **default_args)
정식 지원을 통해 간단히 사용할 수 있지만, 더 높은 제어와 세밀한 커스터마이제이션을 원하거나, 연구 목적으로 다양한 실험을 시도해보고 싶은 경우에는 원문내용과 GitHub 저장소에 있는 설치 가이드를 따라 bitsandbytes 라이브러리를 설치하여 8비트 Adam 옵티마이저를 구현할 수 있다.
이 역시 다른 접근법들(Gradient Accumulation, Gradient Checkpointing, Mixed Precision Training)과 결합하면, 처리량을 유지하면서 최대 3배의 개선을 확인할 수 있다.
multi_tensor
pytorch-nightly는 torch.optim._multi_tensor를 도입했다. 이는 많은 수의 작은 특징 텐서(feature tensor)를 가진 상황에서 옵티마이저의 속도를 크게 향상시킬 수 있다. 이 기능은 defalut로 작동할 예정이며, 이슈를 통해 확인 및 실험해볼 수 있다.
multi_tensor는 작은 텐서를 다룰 때 처리 속도를 높여주기 때문에, 많은 파라미터 그룹을 가진 모델에서 효율적인 학습을 가능하게 한다. 이 기능을 통해 옵티마이저의 성능을 개선할 수 있다.
Data Preloading
우수한 학습 속도를 달성하기 위한 중요한 요구 사항 중 하나는 GPU에 데이터를 가능한 최대 속도로 공급할 수 있는 능력이다. 기본적으로 모든 처리는 메인 프로세스에서 이루어지며, 이 경우 디스크에서 데이터를 충분히 빠르게 읽어오지 못해 병목 현상이 발생할 수 있다. 이로 인해 GPU 사용률이 떨어질 수 있기 때문에 이러한 병목을 줄이기 위해 다음과 같은 인자를 설정해야 한다.
- DataLoader(pin_memory=True, ...) - 데이터를 CPU의 고정 메모리(pinned memory)로 미리 로드하여 CPU에서 GPU로의 데이터 전송을 훨씬 빠르게 할 수 있다.
- DataLoader(num_workers=4, ...) - 여러 작업자(workers)를 생성하여 데이터를 더 빠르게 미리 로드한다. 학습 중 GPU 사용률 통계를 확인하면서, 사용률이 100%에 미치지 못하면 작업자 수를 늘려보는 실험을 할 수 있다. 물론 문제의 원인이 다른 곳에 있을 수도 있기 때문에 작업자 수를 많이 늘린다고 해서 항상 더 나은 성능으로 이어지는 것은 아니다.
Trainer를 사용할 때의 해당 TrainingArguments는 dataloader_pin_memory(defaults to True)와 dataloader_num_workers(defaults to 0)이다.
DeepSpeed ZeRO
DeepSpeed는 huggingface Transformers 및 Accelerate와 통합된 오픈 소스 딥러닝 최적화 라이브러리이다. 대규모 딥러닝 학습의 효율성과 확장성을 향상시키기 위해 다양한 기능과 최적화를 제공한다. 모델이 단일 GPU에 맞지 않거나 작은 배치 크기를 적용할 수 없는 경우, DeepSpeed ZeRO + CPU 오프로딩 또는 NVMe 오프로딩을 사용하여 훨씬 더 큰 모델을 처리할 수 있다.
단점은 DeepSpeed 설정 파일을 작성해야 하고, 이를 GPU 환경에 맞게 조정해야 한다. 특히 ZeRO-3와 같은 고급 기능을 사용할 때는 더 많은 조정이 필요할 수 있다.
또, 파라미터와 기울기를 여러 GPU에 나누는 과정에서 통신 오버헤드가 발생할 수 있다. 이는 GPU 간의 통신 속도에 따라 학습 속도에 영향을 미칠 수 있다. 그렇기 때문에 GPU 메모리가 충분하다면 ZeRO를 사용하지 않는 것이 더 빠를 수 있다.
이 경우 라이브러리를 별도로 설치한 후, 가이드 중 하나를 따라 설정 파일을 생성하고 DeepSpeed를 실행해야 한다.
Using torch.compile
PyTorch 2.0은 새로운 compile 함수를 도입했다. 기존 PyTorch 코드를 수정할 필요 없이 단 한 줄의 코드로 모델을 최적화할 수 있다
model = torch.compile(model)
Trainer를 사용할 경우, TrainingArguments에서 torch_compile 옵션을 설정하기만 하면 된다
training_args = TrainingArguments(torch_compile=True, **default_args)
torch.compile은 Python의 프레임 평가 API를 사용하여 기존 PyTorch 프로그램에서 자동으로 그래프를 생성한다. 그래프를 캡처한 후, 다양한 백엔드를 사용하여 그래프를 최적화된 엔진으로 변환할 수 있다. 자세한 정보와 벤치마크는 PyTorch 문서에서 확인할 수 있다.
torch.compile에는 점점 더 많은 백엔드가 추가되고 있으며, torchdynamo.list_backends()를 호출하여 백엔드 목록을 확인할 수 있다.(각각의 백엔드에는 선택적 의존성이 있다.)
TrainingArguments에서 torch_compile_backend를 지정하여 사용할 백엔드를 선택할 수 있으며, 가장 많이 사용되는 백엔드는 원문 페이지에서 확인 가능하다.
PyTorch 2.0의 최신 기능을 사용해 BERT 모델을 텍스트 분류로 미세 조정하는 블로그 게시물을 참고하여 Transformers에서 torch.compile을 사용하는 예시를 볼 수 있다.
Using PEFT
Parameter-Efficient Fine Tuning (PEFT) 방법은 파인튜닝 과정에서 사전 학습된 모델 파라미터를 고정(freeze)하고, 그 위에 소수의 학습 가능한 파라미터(어댑터)를 추가한다.
이 결과로, 옵티마이저 상태와 기울기에 관련된 메모리 사용량이 크게 줄어든다. 예를 들어, 일반적인 AdamW의 경우, 옵티마이저 상태에 필요한 메모리는 아래와 같다.
FP32 파라미터 복사본: 4 bytes/파라미터
모멘텀(Momentum): 4 bytes/파라미터
분산(Variance): 4 bytes/파라미터
파라미터가 70억 개(7B)인 모델과 LoRA 어댑터(Low Rank Adapters)로 추가된 2억 개의 파라미터가 있다고 가정해 보자. 단순 모델의 옵티마이저 상태에 필요한 메모리 요구량은 12 * 7 = 84GB이다 (70억 개의 학습 가능한 파라미터를 가정할 경우).
Lora를 추가하면 모델 가중치와 관련된 메모리 요구량이 약간 증가하지만, 옵티마이저 상태에 필요한 메모리는 크게 줄어들어 12 * 0.2 = 2.4GB로 감소한다.
PEFT는 학습 효율성을 극대화하고 메모리 사용량을 줄여 더 큰 모델을 다룰 때 매우 유용한 방법이다.
Using HF Accelerate
TBD
Efficient Software Prebuilds
TBD
Mixture of Experts
TBD
Using PyTorch native attention and Flash Attention
TBD

'DATA, AI' 카테고리의 다른 글
| Reinforcement Learning : Understanding Fundamentals – From Key Concepts to Policy Gradient Methods (0) | 2025.03.01 |
|---|---|
| 우분투 환경에서 deepseek-r1 로컬 설치하기(open-webui, docker) (2) | 2025.01.30 |
| huggingface로 협업하기 (2) | 2024.10.29 |
| 입출력 형태에 따른 자연어 처리 Task의 이해 (2) | 2024.10.02 |
| NLP 논문 리스트 (3) | 2024.09.13 |
개발새발라이프
hi there🙌