RAG로 외부 문서를 검색하게 만들었는데, 복합적인 질문이 들어오면 매뉴얼을 그대로 읽어주는 데 그친다면? 이건 검색의 한계입니다. 모델 자체가 도메인 지식을 “이해”하는 게 아니라 문서를 “찾아서” 보여주기만 하니까요. 이 한계를 넘는 방법이 바로 LoRA와 QLoRA를 활용한 파인튜닝입니다.
RAG vs 파인튜닝 — 근본적인 차이
RAG와 파인튜닝은 둘 다 AI를 더 똑똑하게 만드는 방법이지만, 작동 방식이 완전히 다릅니다. RAG는 검색입니다. 질문이 들어오면 벡터 데이터베이스에서 관련 문서를 찾아서 모델에게 “이 문서를 읽고 대답해”라고 합니다. 모델의 지식이 늘어나는 게 아니라, 매번 참고서를 펼쳐보는 것이죠. 장점은 즉시 적용 가능하고 문서가 바뀌면 즉시 반영된다는 것이고, 단점은 모델이 문서를 “이해”하는 게 아니라 “요약”하는 수준이라는 것입니다.
파인튜닝은 학습입니다. 모델의 가중치를 직접 수정해서 도메인 지식을 모델 안에 내재화시킵니다. 고객 문의 패턴, 회사 용어, 업계 관행을 모델이 학습해서 문서를 검색하지 않아도 적절한 대답을 합니다. 응답 품질이 올라가고 추론 시간이 빨라지는 장점이 있지만, 학습에 시간과 비용이 들고 데이터가 바뀌면 다시 학습해야 한다는 단점이 있습니다.
구분 기준은 간단합니다. “정보가 자주 바뀌는가?”라고 물어보세요. 제품 가격, 재고, 뉴스처럼 매일 바뀌는 정보는 RAG가 맞습니다. “어떤 유형의 질문에 어떤 톤으로 대답해야 하는가?” 같은 패턴은 파인튜닝이 맞습니다. 그리고 둘을 같이 쓸 수도 있습니다 — 파인튜닝된 모델에 RAG를 얹는 구조가 실제 프로덕션에서 가장 흔합니다.
전체 파인튜닝 vs LoRA vs QLoRA — 비용 차이가 30배
파인튜닝이라고 하면 모델의 모든 파라미터를 다시 학습시킨다고 생각하기 쉽습니다. 70억 개 매개변수를 가진 Llama-3 8B 모델을 전체 파인튜닝하려면 최소 100GB 이상의 GPU 메모리가 필요합니다. A100 GPU 80GB 두 장을 써야 하고, 시간당 약 8달러니까 10시간 학습에 160달러입니다.
LoRA(Low-Rank Adaptation)는 모델의 모든 파라미터를 수정하는 게 아니라 아주 작은 추가 파라미터만 학습시키는 방식입니다. 모델의 가중치 변화를 저랭크 행렬로 근사하면, 원래 파라미터 수의 1% 이하만 학습해도 비슷한 효과를 얻을 수 있습니다. QLoRA는 여기에 한 발 더 나아가 원본 모델을 4비트로 양자화해서 메모리를 극적으로 줄이고, 그 위에 LoRA 어댑터를 16비트로 학습합니다.
결과적으로 QLoRA는 같은 70억 파라미터 모델을 6GB GPU 메모리로 학습합니다. RTX 4090 한 장이면 됩니다. 전력비 포함해도 10시간에 5달러 이하입니다. 전체 파인튜닝 대비 30배 이상 비용 차이가 납니다. 학습하는 파라미터가 전체의 0.1~1%에 불과한데, 놀랍게도 성능은 전체 파인튜닝과 거의 비슷합니다. 도메인 특화 작업에서는 오히려 과적합을 방지하는 효과도 있습니다.
QLoRA 파인튜닝 실습 — HuggingFace + PEFT
필요한 패키지를 설치합니다. transformers는 모델 로드, peft는 LoRA 어댑터 관리, bitsandbytes는 4비트 양자화, accelerate는 GPU 분산, datasets는 학습 데이터 처리, trl은 파인튜닝 루프를 담당합니다.
pip install transformers peft bitsandbytes accelerate datasets trl
학습 데이터는 고객 문의와 적절한 응답 쌍을 JSONL 형식으로 준비합니다. 최소 500개 이상이 필요하며, 시작은 500~2000개가 적당합니다. 데이터 품질이 양보다 훨씬 중요합니다 — 500개의 정제된 데이터가 5000개의 노이즈 데이터보다 낫습니다.
{"instruction": "환불 정책이 어떻게 돼?", "output": "구매 후 14일 이내 미사용 제품은 전액 환불됩니다."}
{"instruction": "배송 언제 와?", "output": "일반 배송은 결제 후 2~3일 소요됩니다."}
이제 모델을 4비트로 로드합니다. nf4(NormalFloat 4비트) 양자화 방식은 QLoRA 논문에서 제안한 방식으로 일반적인 4비트 양자화보다 정확도 손실이 적습니다. double_quant 옵션은 양자화 상수 자체도 한 번 더 양자화해서 메모리를 추가로 절약합니다.
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
import torch
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Meta-Llama-3-8B",
quantization_config=bnb_config,
device_map="auto",
)
LoRA 설정에서 r은 랭크로 8~64 사이가 일반적이고 16이 좋은 시작점입니다. lora_alpha는 보통 r의 2배를 사용합니다. target_modules는 attention 레이어의 프로젝션 행렬을 지정합니다. print_trainable_parameters를 실행하면 “trainable: 13 million out of 8 billion”처럼 나옵니다 — 80억 파라미터 중 0.16%만 학습하는 것입니다.
from peft import LoraConfig, get_peft_model
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
학습은 SFTTrainer로 간단하게 실행합니다. 에포크는 3~5가 적당하고, gradient_accumulation_steps를 조절하면 실제 배치 사이즈를 유지하면서 GPU 메모리를 줄일 수 있습니다. 학습이 끝나면 어댑터를 저장하는데, 파일 크기는 보통 50~100MB 정도입니다. 원본 모델 16GB와 비교하면 아주 작습니다.
자주 발생하는 실수와 해결법
CUDA out of memory가 나면 max_seq_length를 줄이거나 배치 사이즈를 1로 낮추세요. gradient_accumulation_steps를 올리면 실제 배치 사이즈를 유지하면서 메모리를 줄일 수 있습니다. 학습은 되는데 응답 품질이 안 좋으면 데이터 품질을 의심하세요 — 파인튜닝은 데이터 품질이 양보다 훨씬 중요합니다.
모델이 학습 데이터를 앵무새처럼 반복할 때는 에포크를 줄이세요. 3 에포크로 과적합이 나면 1~2 에포크로 줄이는 게 낫습니다. 학습 데이터의 형식이 일관되지 않으면 모델이 혼란스러워합니다. 모든 데이터를 같은 포맷으로 정제하는 데 시간을 투자하세요 — 이게 파인튜닝 성공을 가르는 핵심입니다.
파인튜닝된 모델 vs RAG — 언제 뭘 써야 할까
같은 질문을 RAG에 물어보면 환불 정책 문서를 검색해서 “구매 후 14일 이내, 영수증 필요”라는 원문을 가져옵니다. 하지만 “영수증이 없으면 어떡해?”라는 후속 질문에 문서에 그 케이스가 없으면 대응하지 못합니다. 반면 파인튜닝된 모델은 학습 데이터에 “영수증 분실 케이스”가 있었다면 그 패턴을 학습해서 바로 대답합니다. 응답 속도도 빠릅니다 — RAG는 문서 검색에 1~2초가 걸리지만 파인튜닝 모델은 즉시 응답합니다.
하지만 파인튜닝된 모델은 학습하지 않은 새로운 정책에는 대응하지 못합니다. “이번 주부터 새로운 환불 정책이 적용됐어”라고 하면 RAG는 문서만 바꾸면 즉시 반영되지만 파인튜닝 모델은 다시 학습해야 합니다. 그래서 프로덕션에서는 파인튜닝으로 기본 응답 패턴을 학습시키고 RAG로 최신 정보를 보완하는 조합 구조를 씁니다.
파인튜닝을 해야 하는 신호는 세 가지입니다. RAG 응답이 문서를 그대로 읽어주는 수준에 머물 때, 특정 도메인 용어나 패턴이 반복적으로 나타날 때, 응답 속도가 중요할 때입니다. 반대로 정보가 매일 바뀌거나 데이터가 100개 미만이거나 도메인이 일반적인 경우에는 RAG나 프롬프트 엔지니어링으로 충분합니다. 파인튜닝에 일주일을 투자해서 얻는 성능 향상이 프롬프트 수정 10분으로 얻는 향상보다 크지 않다면, 파인튜닝을 할 이유가 없습니다.
참고 자료
RAG의 기본 개념과 구축 방법이 궁금하다면 ChatGPT가 내 문서를 모른다? 내 서버 AI에게 RAG로 가르쳐라를 함께 읽어보세요. LoRA/QLoRA 구현에 사용한 Hugging Face PEFT 라이브러리의 공식 문서도 참고하세요.



















