Day 20: PEFT 라이브러리 실전
PEFT(Parameter-Efficient Fine-Tuning) 라이브러리는 LoRA, QLoRA 등을 간편하게 적용할 수 있게 해주는 Hugging Face 공식 라이브러리입니다. 오늘은 실제 모델에 LoRA를 적용하는 전체 과정을 다룹니다.
LoraConfig 설정하기
# pip install peft transformers bitsandbytes
from peft import LoraConfig, TaskType, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch
# QLoRA를 위한 4bit 양자화 설정
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)
# 기본 모델 로드
model_name = "meta-llama/Llama-3.1-8B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
# LoRA 설정
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM, # 태스크 유형
r=8, # rank (저랭크 차원)
lora_alpha=16, # 스케일링 팩터
lora_dropout=0.05, # 드롭아웃 (과적합 방지)
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"], # 적용할 레이어
bias="none", # 바이어스 학습 여부
)
PEFT 모델 생성과 파라미터 확인
# LoRA 적용
peft_model = get_peft_model(model, lora_config)
# 학습 가능 파라미터 확인
peft_model.print_trainable_parameters()
# 출력 예: trainable params: 6,553,600 || all params: 8,036,098,048 || trainable%: 0.0816
# 어떤 레이어에 LoRA가 적용되었는지 확인
for name, param in peft_model.named_parameters():
if param.requires_grad:
print(f"학습 대상: {name} | shape: {param.shape}")
# 모델 구조에서 LoRA 레이어 확인
print(peft_model)
타겟 모듈 선택 가이드
어떤 레이어에 LoRA를 적용할지가 성능에 영향을 미칩니다. 모델의 Attention 모듈이 기본 대상이며, MLP 레이어까지 포함하면 성능이 더 올라갈 수 있습니다.
# 모델의 모든 Linear 레이어 이름 확인
def find_linear_modules(model):
"""LoRA 적용 가능한 Linear 레이어 목록을 반환"""
linear_modules = set()
for name, module in model.named_modules():
if isinstance(module, torch.nn.Linear):
# 마지막 부분만 추출 (예: model.layers.0.self_attn.q_proj -> q_proj)
layer_name = name.split(".")[-1]
linear_modules.add(layer_name)
return list(linear_modules)
available_modules = find_linear_modules(model)
print(f"사용 가능한 타겟 모듈: {available_modules}")
# 일반적인 선택 가이드
target_guide = {
"최소 (빠른 학습)": ["q_proj", "v_proj"],
"권장 (균형)": ["q_proj", "v_proj", "k_proj", "o_proj"],
"최대 (높은 성능)": ["q_proj", "v_proj", "k_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"],
}
for level, modules in target_guide.items():
print(f"\n[{level}]: {modules}")
모델 저장과 로드
LoRA 어댑터는 원래 모델과 별도로 저장됩니다. 어댑터 크기는 수십 MB에 불과하여 공유와 버전 관리가 쉽습니다.
from peft import PeftModel
# LoRA 어댑터만 저장 (수십 MB)
peft_model.save_pretrained("./my_lora_adapter")
# 저장된 파일 확인
# ./my_lora_adapter/
# adapter_config.json (LoRA 설정)
# adapter_model.safetensors (학습된 가중치)
# 나중에 다시 로드
base_model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map="auto",
)
loaded_model = PeftModel.from_pretrained(base_model, "./my_lora_adapter")
# 추론 모드로 전환
loaded_model.eval()
# 어댑터를 기본 모델에 병합 (선택적 - 추론 속도 향상)
merged_model = loaded_model.merge_and_unload()
merged_model.save_pretrained("./merged_model")
오늘의 연습문제
- 작은 모델(예:
gpt2또는distilgpt2)에 LoRA를 적용하고,print_trainable_parameters()로 학습 가능 파라미터 비율을 확인해보세요. target_modules를 바꿔가며 비교합니다. find_linear_modules()함수를 사용하여 서로 다른 아키텍처의 모델(GPT-2, BERT, T5) 3개의 Linear 레이어 구조를 비교해보세요.- LoRA 어댑터를 저장한 뒤 다시 로드하여, 저장 전과 동일한 출력을 생성하는지 검증해보세요. 같은 입력에 대한 logits를 비교합니다.