RAG를 넘어: 파인튜닝이 필요한 순간
1편에서 RAG로 신뢰도 높은 사내 챗봇을 만드는 방법을 다뤘습니다. RAG는 빠르고 유연하지만, 모델이 특정 도메인의 언어 스타일과 패턴을 깊이 내면화해야 할 때는 파인튜닝이 답입니다.
RAG vs 파인튜닝: 선택 기준
| 상황 | 추천 방법 |
|---|---|
| 최신 정보 지속 업데이트 | RAG |
| 특정 도메인 전문 용어 습득 | 파인튜닝 |
| 응답 형식/스타일 일관성 | 파인튜닝 |
| 빠른 프로토타이핑 | RAG |
| 추론 비용 최소화 | 파인튜닝 + 경량 모델 |
LoRA: 효율적인 파인튜닝의 핵심
LoRA(Low-Rank Adaptation)는 원본 가중치를 고정하고 저차원 분해 행렬만 학습합니다. 파라미터 수를 100배 이상 줄이면서 성능은 풀 파인튜닝에 근접합니다.
from peft import LoraConfig, get_peft_model, TaskType
lora_config = LoraConfig(
r=16, # 랭크: 낮을수록 빠름·작음, 높을수록 표현력 강함
lora_alpha=32, # 스케일링 팩터 (보통 r의 2배)
target_modules=["q_proj", "v_proj"], # 어텐션 레이어만 학습
lora_dropout=0.1,
bias="none",
task_type=TaskType.CAUSAL_LM,
)
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3-8B")
peft_model = get_peft_model(model, lora_config)
peft_model.print_trainable_parameters()
# trainable params: 4,194,304 || all params: 8,030,261,248 || 0.052%QLoRA: 소비자 GPU에서 파인튜닝하기
QLoRA는 4-bit 양자화 + LoRA를 결합해 24GB VRAM으로 70B 모델을 파인튜닝할 수 있게 합니다.
from transformers import BitsAndBytesConfig
import torch
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # NormalFloat4: 가장 정확한 4bit 타입
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True, # 양자화 상수도 양자화
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B",
quantization_config=bnb_config,
device_map="auto",
)데이터셋 준비: 품질이 양보다 중요
파인튜닝에서 가장 중요한 것은 데이터 품질입니다.
# 고품질 instruction-response 쌍 형식
dataset_example = {
"instruction": "다음 고객 문의에 공감하며 간결하게 답변하세요.",
"input": "주문한 지 이틀이 지났는데 배송 추적이 안 됩니다.",
"output": "불편을 드려 죄송합니다. 배송사에 직접 확인해보니 물류센터에서 처리 중입니다. 오늘 중으로 출고 예정이며, 출고 시 문자로 안내해드리겠습니다."
}
# 데이터 정제 체크리스트
checklist = [
"100~500자 사이의 적절한 응답 길이",
"일관된 어조와 문체",
"도메인 전문 용어 정확 사용",
"개인정보 제거 완료",
"사람 검수 통과",
]데이터 수량 가이드:
- 스타일 미세 조정: 100~500개
- 도메인 특화: 1,000~5,000개
- 새로운 태스크 학습: 10,000개 이상
학습 설정
from transformers import TrainingArguments
from trl import SFTTrainer
training_args = TrainingArguments(
output_dir="./results",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4, # effective batch = 16
warmup_ratio=0.03,
learning_rate=2e-4,
fp16=True,
logging_steps=10,
save_strategy="epoch",
evaluation_strategy="epoch",
load_best_model_at_end=True,
)
trainer = SFTTrainer(
model=peft_model,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
args=training_args,
dataset_text_field="text",
max_seq_length=2048,
)
trainer.train()학습 후 평가
# BLEU, ROUGE만으론 부족 — LLM 평가 추가
from evaluate import load
rouge = load("rouge")
scores = rouge.compute(predictions=model_outputs, references=reference_outputs)
print(f"ROUGE-L: {scores['rougeL']:.3f}")
# 도메인 특화 평가 (예: 법률 용어 정확도)
def domain_accuracy(outputs, references, domain_terms):
hits = sum(any(t in o for t in domain_terms) for o in outputs)
return hits / len(outputs)3편에서는 여러 LLM을 라우팅·앙상블해 정확도와 비용을 동시에 최적화하는 전략을 다룹니다.
학습 실패 증상별 분기표
| 증상 | 유력 원인 | 우선 조치 |
|---|---|---|
| loss가 발산 | 학습률 과다 | 학습률 1/10로, warmup 스텝 추가 |
| loss는 내려가는데 실제 품질 저하 | 데이터셋 품질·과적합 | 에폭 축소(1~3), 검증 세트로 조기 종료, 데이터 중복 제거 |
| 학습 후 일반 능력 퇴화(catastrophic forgetting) | 도메인 데이터만 학습 | 일반 instruction 데이터 10~20% 혼합 |
| OOM (메모리 부족) | 배치·시퀀스 길이 과다 | QLoRA(4bit) 전환, gradient accumulation으로 유효 배치 유지, 시퀀스 길이 축소 |
| 특정 형식만 반복 출력 | 데이터 형식 편중 | 출력 형식 다양화, 시스템 프롬프트 변형 추가 |
학습 전 체크리스트
- 이 문제가 정말 파인튜닝 대상인가 — 프롬프트/RAG로 해결 불가함을 사례로 문서화했는가
- 평가 기준 선정의 — 학습 전 베이스라인 점수를 같은 평가 세트로 기록
- 데이터 라이선스·개인정보 — 학습 데이터에 고객 식별 정보가 없는지 검수
- 실험 추적 — 하이퍼파라미터·데이터 버전·adapter 가중치를 함께 기록 (재현 가능성)
자주 묻는 질문 (FAQ)
Q. LoRA 파인튜닝과 RAG, 뭘 써야 하나요? A. 목적이 다릅니다. RAG는 '최신·외부 지식을 검색해 주입'(사실·문서 기반)이고, LoRA는 '모델의 말투·형식·도메인 표현을 학습'(스타일·태스크 적응)입니다. 최신 정보가 필요하면 RAG, 특정 형식·톤·분류 정확도가 필요하면 LoRA입니다.
Q. LoRA와 RAG를 함께 쓸 수 있나요? A. 흔한 조합입니다. LoRA로 도메인 말투·출력 형식을 고정하고 RAG로 실시간 근거를 주입하면 '우리 회사 톤 + 최신 사실'을 동시에 얻습니다. 단 LoRA 학습 데이터와 RAG 문서의 도메인이 어긋나지 않도록 관리해야 합니다.
이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
댓글
첫 번째 댓글을 남겨보세요.