개요

LLM을 도메인에 특화시키는 대표적인 두 가지 접근법은 Fine-tuning(미세조정)RAG(Retrieval-Augmented Generation)입니다. 많은 팀이 "우리 데이터로 AI를 학습시키자"고 할 때 바로 Fine-tuning을 떠올리지만, 실제로는 RAG가 더 적합한 경우가 대부분입니다.

이 글에서는 두 접근법의 원리, 비용, 성능을 객관적으로 비교하고, 실무에서 올바른 선택을 내리기 위한 의사결정 프레임워크를 제시합니다.

핵심 개념: 두 접근법의 원리

  • Fine-tuning: 사전학습된 모델의 가중치를 도메인 데이터로 추가 학습. 모델 내부에 지식이 "각인"됩니다. 출력 스타일, 포맷, 특정 도메인 어휘를 학습시키는 데 효과적입니다.
  • RAG: 모델 가중치는 그대로 두고, 추론 시점에 관련 문서를 검색하여 컨텍스트로 제공. 외부 지식 저장소를 "참고 자료"로 활용합니다. 사실 기반 응답과 최신 정보 반영에 효과적입니다.

실전 예제: 비용과 성능 비교

1. Fine-tuning 파이프라인

# OpenAI Fine-tuning 예시
import openai
import json

# 1. 학습 데이터 준비 (JSONL 형식)
training_data = [
    {
        "messages": [
            {"role": "system", "content": "당신은 의료 전문 AI입니다."},
            {"role": "user", "content": "고혈압 환자의 1차 치료약은?"},
            {"role": "assistant", "content": "고혈압 1차 치료약으로는 ACE 억제제(에날라프릴), ARB(발사르탄), CCB(암로디핀), 티아지드계 이뇨제가 권장됩니다. JNC-8 가이드라인에 따르면..."}
        ]
    },
    # ... 수백~수천 개의 학습 예시
]

with open("training.jsonl", "w") as f:
    for item in training_data:
        f.write(json.dumps(item, ensure_ascii=False) + "\n")

# 2. 파일 업로드
file = openai.files.create(
    file=open("training.jsonl", "rb"),
    purpose="fine-tune"
)

# 3. Fine-tuning 작업 시작
job = openai.fine_tuning.jobs.create(
    training_file=file.id,
    model="gpt-4o-mini-2024-07-18",
    hyperparameters={
        "n_epochs": 3,
        "learning_rate_multiplier": 1.8
    }
)

# 4. 학습 완료 후 사용
response = openai.chat.completions.create(
    model=f"ft:{job.fine_tuned_model}",
    messages=[{"role": "user", "content": "당뇨병 진단 기준은?"}]
)

2. 동일 도메인의 RAG 구현

# RAG로 동일한 의료 Q&A 구현
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA

# 의료 가이드라인 문서를 벡터DB에 색인
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
    documents=medical_guidelines,  # 의료 가이드라인 PDF 파싱 결과
    embedding=embeddings
)

# RAG 체인 구성
qa = RetrievalQA.from_chain_type(
    llm=ChatOpenAI(model="gpt-4o", temperature=0),
    retriever=vectorstore.as_retriever(search_kwargs={"k": 5}),
    return_source_documents=True
)

result = qa.invoke({"query": "당뇨병 진단 기준은?"})
print(result["result"])
print("참고 문헌:")
for doc in result["source_documents"]:
    print(f"  - {doc.metadata['source']}, p.{doc.metadata.get('page', 'N/A')}")

3. 비용 비교표

+------------------+-------------------+-------------------+
|     항목          |   Fine-tuning     |      RAG          |
+------------------+-------------------+-------------------+
| 초기 비용         | $50~$500+         | $5~$50            |
|                   | (학습 컴퓨팅)      | (임베딩 생성)      |
+------------------+-------------------+-------------------+
| 추론 비용/건      | 기본 모델 대비      | 임베딩 + 검색      |
|                   | 동일 또는 저렴     | + LLM 비용        |
+------------------+-------------------+-------------------+
| 데이터 업데이트    | 재학습 필요        | 문서 추가만        |
|                   | (수 시간~수 일)     | (수 분)            |
+------------------+-------------------+-------------------+
| 필요 데이터량      | 수백~수천 예시     | 원본 문서 그대로    |
+------------------+-------------------+-------------------+
| 할루시네이션       | 여전히 발생 가능    | 출처 기반으로 감소  |
+------------------+-------------------+-------------------+
| 구현 복잡도       | 중간               | 낮음~중간          |
+------------------+-------------------+-------------------+

활용 팁

  • RAG 먼저 시도하세요: 대부분의 사용 사례에서 RAG가 더 빠르고 저렴하며 유지보수가 쉽습니다. Fine-tuning은 RAG로 해결되지 않는 경우에만 고려하세요.
  • Fine-tuning이 필요한 경우: 특정 출력 형식/톤 강제, 모델 크기 축소(소형 모델을 대형 모델 수준으로), 지연 시간 최소화가 필요할 때입니다.
  • 하이브리드 접근: Fine-tuned 모델 + RAG를 결합하면 최고의 성능을 얻을 수 있습니다. 모델은 도메인 스타일을, RAG는 최신 사실을 담당합니다.
  • 평가 기준 먼저 설정: 어떤 접근법을 선택하든, 성능을 측정할 평가 데이터셋을 먼저 만드세요. 정량적 비교 없이는 올바른 판단이 불가능합니다.
  • 데이터 품질이 핵심: Fine-tuning은 학습 데이터의 품질에, RAG는 검색 문서의 품질에 결과가 직결됩니다. 양보다 질을 우선하세요.

마무리

Fine-tuning과 RAG는 경쟁 관계가 아닌 보완 관계입니다. "RAG 먼저, Fine-tuning은 필요할 때"가 가장 실용적인 전략입니다. 빠르게 변하는 정보에는 RAG, 일관된 출력 스타일에는 Fine-tuning, 최고 성능이 필요하면 두 가지를 결합하세요. 올바른 선택은 기술적 우열이 아니라 비즈니스 요구사항에 달려 있습니다.