파인튜닝의 필요성
사전 학습된 LLM을 특정 도메인이나 태스크에 맞게 조정하는 파인튜닝은 모델 성능을 크게 향상시킵니다. 하지만 70B 파라미터 모델의 전체 파인튜닝에는 수백 GB의 VRAM이 필요하여, 효율적인 대안인 LoRA와 QLoRA가 등장했습니다.
Full Fine-tuning
모든 파라미터를 업데이트하는 전통적인 방식입니다. 최고 성능을 낼 수 있지만 메모리와 비용이 가장 많이 듭니다.
# Full Fine-tuning 기본 설정
from transformers import AutoModelForCausalLM, TrainingArguments, Trainer
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
torch_dtype=torch.bfloat16,
)
training_args = TrainingArguments(
output_dir="./full-ft-output",
per_device_train_batch_size=1,
gradient_accumulation_steps=16,
learning_rate=2e-5,
num_train_epochs=3,
bf16=True,
gradient_checkpointing=True, # 메모리 절약
deepspeed="ds_config_zero3.json", # 멀티 GPU 분산
)
# 7B 모델 기준: ~120GB VRAM (BF16 + optimizer states)
LoRA (Low-Rank Adaptation)
LoRA는 원본 가중치를 고정하고, 작은 저차원 행렬만 학습합니다. 원래 가중치 행렬 W에 저랭크 분해 BA를 추가하여 W' = W + BA로 업데이트합니다. 학습 파라미터가 전체의 0.1-1%에 불과하여 메모리를 크게 절약합니다.
from peft import LoraConfig, get_peft_model, TaskType
# LoRA 설정
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16, # 랭크 (보통 8-64)
lora_alpha=32, # 스케일링 팩터
lora_dropout=0.05,
target_modules=[ # 적용 대상 레이어
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"
],
bias="none",
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
torch_dtype=torch.bfloat16,
)
peft_model = get_peft_model(model, lora_config)
peft_model.print_trainable_parameters()
# trainable params: 33,554,432 || all params: 6,771,970,048 || trainable%: 0.4955
# 7B 모델 기준: ~18GB VRAM
QLoRA (Quantized LoRA)
QLoRA는 베이스 모델을 4비트로 양자화한 뒤 LoRA를 적용합니다. NF4 양자화와 이중 양자화를 통해 메모리를 극적으로 줄이면서도 LoRA와 유사한 성능을 달성합니다.
from transformers import BitsAndBytesConfig
# 4비트 양자화 설정
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # NormalFloat4
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True, # 이중 양자화
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
quantization_config=bnb_config,
device_map="auto",
)
# 이후 LoRA 적용은 동일
peft_model = get_peft_model(model, lora_config)
# 7B 모델 기준: ~6GB VRAM (단일 RTX 4090으로 가능)
성능 및 비용 비교
| 항목 | Full FT | LoRA | QLoRA |
|---|---|---|---|
| VRAM (7B) | ~120GB | ~18GB | ~6GB |
| VRAM (70B) | ~800GB | ~160GB | ~36GB |
| 학습 속도 | 기준 | 2-3x 빠름 | LoRA 대비 약간 느림 |
| 성능 (벤치) | 100% | 97-99% | 95-97% |
| 어댑터 크기 | 모델 전체 | 10-100MB | 10-100MB |
| GPU 요구 | 8x A100 | 1-2x A100 | 1x RTX 4090 |
LoRA 랭크(r) 선택 가이드
- r=8: 간단한 스타일/포맷 변경, 리소스 제한 환경
- r=16-32: 일반적인 도메인 적응, 대부분의 사용 사례에 권장
- r=64-128: 복잡한 태스크, 새로운 지식 주입이 필요할 때
- lora_alpha: 보통 r의 2배로 설정하면 안정적
# 학습 완료 후 어댑터 병합
merged_model = peft_model.merge_and_unload()
merged_model.save_pretrained("./merged-model")
# 또는 어댑터만 저장 (추후 동적 로딩)
peft_model.save_pretrained("./lora-adapter")
# 추론 시 로딩
from peft import PeftModel
base = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
model = PeftModel.from_pretrained(base, "./lora-adapter")
대부분의 실무 사용 사례에서 QLoRA는 비용 대비 성능이 가장 우수합니다. 단일 소비자 GPU로도 70B급 모델을 파인튜닝할 수 있어 접근성이 크게 향상되었습니다.
댓글 0