개인정보처리방침© 2026 DEV BAK - 기술블로그. All rights reserved.
DEV BAK - 기술블로그
AI

소비자 GPU 한 장에서 QLoRA로 도메인 특화 SLM을 파인튜닝하는 방법

GPT-4o API 비용이 쌓이기 시작하는 순간, 자연스럽게 "이걸 우리 도메인에 맞게 직접 학습시킬 수 없을까?"라는 생각이 듭니다. 저도 그랬습니다. 의료 기록 요약 파이프라인에서 GPT-4를 쓰다가 월말 청구서를 보고 식겁한 게 계기였는데, 그러다 파인튜닝된 Qwen3-4B 하나가 특정 도메인 태스크에서 훨씬 큰 범용 모델을 넘기는 벤치마크 결과를 보고 진지하게 파고들기 시작했습니다.

잘 파인튜닝된 SLM은 특정 도메인 태스크에서 수십 배 큰 범용 LLM을 능가할 수 있으며, 추론 비용은 1/10~1/100 수준입니다. 2026년 기준으로 RTX 4070 Ti 한 장과 반나절이면 7B 모델을 도메인 전문가로 탈바꿈시킬 수 있습니다. 이 글에서는 핵심 개념부터 실제로 실행 가능한 코드, 그리고 직접 겪어온 시행착오까지 풀어봅니다. 글을 읽고 나면 Unsloth + QLoRA 조합으로 첫 번째 도메인 특화 SLM을 직접 학습시킬 수 있는 기반이 생길 겁니다.


핵심 개념

SLM이란 무엇이고, 왜 지금인가

SLM(Small Language Model)은 일반적으로 1B~13B 파라미터 수준의 소형 언어 모델을 가리킵니다. 이 정의 자체는 단순하지만, 중요한 건 "작다"는 게 더 이상 "못하다"를 뜻하지 않는다는 점입니다.

2026년 현재 SLM 파인튜닝 생태계가 흥미로운 이유는 세 가지입니다. 하드웨어 문턱이 급격히 낮아졌고(RTX 4070 Ti 한 장이면 충분합니다), 베이스 모델 품질이 크게 올라왔으며, 프레임워크 성숙도가 실무 사용에 충분한 수준에 도달했습니다. GlobalData는 2030년까지 SLM 시장이 207억 달러 규모, 연평균 15.1% 성장할 것으로 전망합니다.

SLM vs LLM: 규모 차이만이 아닌 사용 전략의 차이입니다. LLM은 범용 추론에 강하고, 파인튜닝된 SLM은 특정 도메인 태스크에서 LLM을 이깁니다. 도구를 선택하는 것이지, 우열을 가리는 게 아닙니다.

파인튜닝 방법론 3가지: 뭘 골라야 할까

처음 파인튜닝을 알아볼 때 SFT, LoRA, QLoRA가 동시에 등장해서 저도 한동안 헷갈렸습니다. 핵심은 메모리 제약과 허용 가능한 성능 트레이드오프에 따라 선택한다는 것입니다.

방법 작동 원리 VRAM 요구 파국적 망각 위험
SFT (Supervised Fine-Tuning) 전체 가중치 업데이트 매우 높음 (40GB+) 높음
LoRA 핵심 레이어에 소형 학습 행렬 삽입, 원본 동결 중간 (16~24GB) 낮음
QLoRA LoRA + 4-bit 양자화 낮음 (8~12GB) 낮음

여기서 양자화(Quantization) 란, 모델 가중치를 기존 32-bit나 16-bit 부동소수점 대신 4-bit 정수로 압축해 저장하는 기법입니다. 정보 손실이 일부 발생하지만, VRAM 사용량이 극적으로 줄어들어 소비자 GPU에서도 7B 모델을 올릴 수 있게 됩니다. QLoRA는 이 양자화 위에 LoRA를 얹은 조합으로, 현실적인 온프레미스 환경에서의 최선의 출발점입니다.

파국적 망각(Catastrophic Forgetting): 도메인 특화 데이터로 파인튜닝할 때 모델이 기존에 갖고 있던 범용 추론 능력을 잃어버리는 현상입니다. LoRA는 원본 가중치를 동결하고 소형 행렬만 학습하기 때문에 full SFT보다 이 현상이 훨씬 덜 발생합니다.

LoRA의 수학적 직관

복잡하게 생각할 필요 없습니다. 원본 가중치 행렬 W를 직접 업데이트하는 대신, 훨씬 작은 두 행렬 A와 B의 곱으로 변화량을 근사합니다. 여기서 r이 바로 rank입니다.

W_new = W_original + (B × A) × α/r

이 수식이 무섭게 보여도 의미는 단순합니다: "원본 가중치는 건드리지 말고, 작은 행렬 두 개만 학습하자." rank가 작을수록 학습 파라미터가 줄고 VRAM이 절약되지만, 표현력도 줄어듭니다. 실제로 rank를 4로 낮췄더니 수렴이 불안정해지고, 32로 올렸더니 RTX 4070 Ti에서 VRAM이 모자랐습니다. 도메인 적응에는 r=16이 실질적인 시작점입니다.

lora_alpha도 짚고 넘어갈 부분입니다. alpha=16, r=16으로 설정하면 alpha/r = 1이 되어 LoRA 어댑터의 스케일링 팩터가 1로 고정됩니다. alpha와 r을 같은 값으로 맞추면 학습률과 LoRA의 실효 영향력이 직관적으로 일치하게 되어, 하이퍼파라미터 탐색 시 시작점을 단순하게 유지할 수 있습니다.


실전 적용

예시 1: QLoRA로 금융 도메인 SLM 학습시키기

Qwen3-4B를 금융 QA 태스크에 맞게 파인튜닝하는 기본 세팅입니다. Unsloth를 사용하면 동일한 작업을 HuggingFace Transformers만 쓸 때보다 약 2배 빠르고 VRAM을 70% 절감할 수 있습니다.

한 가지 짚어둘 함정이 있습니다. Qwen3-Instruct처럼 채팅 템플릿이 있는 모델을 파인튜닝할 때 데이터 형식이 결과에 큰 영향을 미칩니다. {"text": "질문\n답변"} 같은 단순 텍스트 형식은 모델이 기대하는 입력 구조와 달라 파인튜닝 효과가 크게 떨어집니다. tokenizer의 apply_chat_template을 활용해 모델에 맞는 형식으로 변환하는 것을 권장합니다.

python
from unsloth import FastLanguageModel
from trl import SFTTrainer
from transformers import TrainingArguments
from datasets import load_dataset
 
# 1. 모델 로드 (4-bit 양자화)
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="Qwen/Qwen3-4B-Instruct",
    max_seq_length=2048,
    dtype=None,         # 자동 감지
    load_in_4bit=True,  # 4-bit 양자화(QLoRA) 활성화
)
 
# 2. LoRA 어댑터 설정
model = FastLanguageModel.get_peft_model(
    model,
    r=16,           # rank: 표현력과 VRAM 절감의 균형점
    target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
    lora_alpha=16,  # alpha/r = 1로 스케일링 팩터 중립화
    lora_dropout=0.05,
    bias="none",
    use_gradient_checkpointing="unsloth",
)
 
# 3. 데이터셋 — Qwen3-Instruct 채팅 템플릿으로 변환
raw_dataset = load_dataset("json", data_files="finance_qa.jsonl")["train"]
 
def format_with_chat_template(example):
    messages = [
        {"role": "user", "content": example["question"]},
        {"role": "assistant", "content": example["answer"]},
    ]
    return {"text": tokenizer.apply_chat_template(messages, tokenize=False)}
 
dataset = raw_dataset.map(format_with_chat_template)
 
# 4. 학습
trainer = SFTTrainer(
    model=model,
    tokenizer=tokenizer,
    train_dataset=dataset,
    dataset_text_field="text",
    max_seq_length=2048,
    args=TrainingArguments(
        per_device_train_batch_size=2,
        gradient_accumulation_steps=4,  # 실효 배치 크기 = 2 × 4 = 8, VRAM 유지
        warmup_steps=50,                # 초반 학습률을 서서히 올려 불안정 수렴 방지
        num_train_epochs=3,
        learning_rate=2e-4,
        lr_scheduler_type="cosine",
        fp16=True,
        logging_steps=10,
        output_dir="./finance-qwen3-4b",
        report_to="wandb",
    ),
)
 
trainer.train()
 
# 5. LoRA 어댑터와 토크나이저 저장
model.save_pretrained("./finance-qwen3-4b-lora")
tokenizer.save_pretrained("./finance-qwen3-4b-lora")
설정값 이유
r=16 도메인 적응의 표준 시작점, 표현력과 VRAM 절감의 균형
lora_alpha=16 alpha/r = 1로 스케일링 중립화, 학습률 효과를 직관적으로 제어
gradient_accumulation_steps=4 VRAM 유지하면서 실효 배치 크기를 4배로 늘리는 트릭
learning_rate=2e-4 LoRA 학습에 최적화된 범위, full SFT보다 높게 설정 가능
cosine 스케줄러 학습 후반 수렴 안정성 향상
warmup_steps=50 초기 학습률을 서서히 올려 초반 발산 방지

학습이 끝난 후, 저장된 LoRA 어댑터를 불러와 추론에 활용하는 방법은 다음과 같습니다.

python
from unsloth import FastLanguageModel
 
# 저장된 LoRA 어댑터 로드
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="./finance-qwen3-4b-lora",
    max_seq_length=2048,
    dtype=None,
    load_in_4bit=True,
)
FastLanguageModel.for_inference(model)
 
# 채팅 템플릿 적용 후 추론
messages = [{"role": "user", "content": "2025년 4분기 금리 전망은 어떻게 됩니까?"}]
inputs = tokenizer.apply_chat_template(
    messages, return_tensors="pt", add_generation_prompt=True
).to("cuda")
 
outputs = model.generate(inputs, max_new_tokens=256, temperature=0.1)
response = tokenizer.decode(outputs[0][inputs.shape[-1]:], skip_special_tokens=True)
print(response)

예시 2: RAG + SLM 하이브리드 파이프라인

파인튜닝만으로는 한계가 있습니다. 학습 데이터에 없는 최신 정보나 방대한 사내 문서를 모델이 알 수는 없으니까요. 그래서 형식과 전문 용어는 파인튜닝으로 주입하고, 최신 정보나 사내 데이터는 RAG로 보강하는 이중 전략을 팀에서도 선택했습니다. 두 역할을 파인튜닝 하나에 욱여넣으려다 실패하는 케이스를 꽤 많이 봤기 때문입니다.

아래는 LangChain v0.2+ 기준의 LCEL 방식으로 작성한 예시입니다. 이전 버전의 langchain.llms, langchain.vectorstores 경로는 langchain_community로 이동했고, RetrievalQA는 deprecated 상태이므로 아래의 방식을 권장합니다.

python
from langchain_community.llms import HuggingFacePipeline
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from transformers import pipeline
import torch
 
# 파인튜닝된 SLM 로드
slm_pipeline = pipeline(
    "text-generation",
    model="./finance-qwen3-4b-lora",
    torch_dtype=torch.float16,
    device_map="auto",
    max_new_tokens=512,
)
llm = HuggingFacePipeline(pipeline=slm_pipeline)
 
# 사내 문서 벡터 DB (Chroma)
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-m3")
vectorstore = Chroma(
    persist_directory="./company_docs_db",
    embedding_function=embeddings,
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
 
# LCEL 기반 RAG 체인
prompt = ChatPromptTemplate.from_template(
    """다음 컨텍스트를 참고해 질문에 답하세요.
 
컨텍스트:
{context}
 
질문: {question}"""
)
 
chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)
 
result = chain.invoke("2025년 4분기 리스크 관리 기준은?")
print(result)

이 패턴의 핵심은 역할 분리입니다. SLM은 "어떻게 답변하는지(형식, 용어, 톤)"를, RAG는 "무엇을 참조하는지(최신 정보, 사내 데이터)"를 담당합니다.


장단점 분석

장점

항목 내용
비용 효율 추론 비용이 GPT-4o 대비 1/10~1/100 수준, 트래픽이 많을수록 절감 효과 극대화
응답 지연시간 온프레미스 배포 시 API 호출 오버헤드 없음, 실시간 서비스에 유리
데이터 프라이버시 내부 인프라 완결 처리, 금융·의료·법무 등 규제 산업에서 필수
도메인 정확도 특정 도메인 태스크에서 수십 배 큰 범용 모델을 능가 가능
낮은 데이터 임계값 고품질 샘플 1,000~5,000개 수준으로 의미 있는 특화 달성 가능 (도메인·태스크 복잡도에 따라 편차 있음)

단점 및 주의사항

항목 내용
파국적 망각 도메인 학습 시 범용 추론 능력 저하 가능
데이터 품질 의존성 편향되거나 대표성 낮은 데이터 → 오버피팅
평가 파이프라인 구축 도메인 특화 벤치마크 수립이 어려움
하이퍼파라미터 탐색 rank, 타겟 레이어, 학습률 조합이 도메인마다 다름
범용 태스크 성능 저하 파인튜닝 후 일반 QA 능력이 떨어질 수 있음

파국적 망각은 LoRA를 사용하면 원본 가중치가 동결되므로 full SFT보다 훨씬 완화됩니다. 더 적극적으로 대응하고 싶다면, 파인튜닝 전 모델이 스스로 범용 대화 데이터를 생성해 보존용 데이터로 활용하는 SA-SFT 기법도 있습니다. 추가 외부 데이터 없이 적용 가능한 방식으로, 관심 있으시면 arXiv 2025 논문에서 자세히 확인해볼 수 있습니다.

실무에서 가장 흔한 실수

  1. 데이터 양에만 집착하는 것: 5,000개 이상부터는 수익 체감 구간에 들어갑니다. 10,000개를 무작정 모으기보다 1,000개를 꼼꼼하게 정제하는 편이 훨씬 효과적인 경우가 많습니다. 실제로 3,000개짜리 노이즈 많은 데이터셋보다 800개짜리 정제된 데이터셋이 더 나은 결과를 낸 경험이 있습니다.

  2. 평가 기준을 사후에 정하는 것: 학습 전에 "무엇이 개선되면 성공인가"를 정의해두지 않으면, 모델이 좋아진 건지 나빠진 건지 알 방법이 없습니다. ROUGE나 BERTScore 같은 자동 지표와 함께, 실제 출력을 읽어봤을 때의 품질 평가를 사전에 설계해두는 것을 권장합니다.

  3. 베이스 모델을 아무거나 고르는 것: 코딩·수학 태스크라면 Phi-4 Mini, 다국어·일반 NLP라면 Qwen3, 커뮤니티 자료가 중요하다면 Llama 3.x가 각각 유리합니다. 베이스 모델 선택이 파인튜닝 결과에 미치는 영향이 하이퍼파라미터 튜닝보다 크다는 것이 벤치마크에서도 일관되게 나타납니다.


마치며

SLM 파인튜닝은 더 이상 ML 연구자의 영역이 아닙니다. 도메인 데이터와 단일 GPU를 가진 개발팀이라면 누구나 접근할 수 있는 선택지입니다. 처음에는 개념이 많아 복잡하게 느껴지지만, 결국 QLoRA + 좋은 데이터 + 사전에 정의된 평가 파이프라인이라는 세 축에서 출발하면 됩니다. "데이터를 많이 모으면 해결된다"는 생각과 "학습 후에 평가 기준을 만들면 된다"는 생각, 이 두 가지를 먼저 내려놓는 것이 실제 성공률을 높이는 가장 빠른 길이었습니다.

지금 바로 시작해볼 수 있는 3단계:

  1. 베이스 모델과 데이터 준비: 목적에 맞는 베이스 모델(Qwen3-4B-Instruct 또는 microsoft/Phi-4-mini-instruct)을 선택하고, 도메인 QA 쌍 1,000개를 {"question": "...", "answer": "..."} 형태의 JSONL 파일로 정제해볼 수 있습니다. 데이터 품질이 양보다 중요하므로, 모호하거나 틀린 답변은 미리 제거하는 것이 좋습니다.

  2. Unsloth + QLoRA로 첫 학습 실행: pip install unsloth trl 후 위의 예시 코드를 실행해볼 수 있습니다. RTX 4070 Ti 이상이면 배치 사이즈 2, gradient accumulation 4 조합으로 약 3~6시간 안에 3 에포크 학습이 완료됩니다. wandb를 함께 연결해두면 학습 곡선을 실시간으로 확인할 수 있습니다.

  3. 도메인 벤치마크로 성능 비교: 학습 전 베이스 모델과 파인튜닝 후 모델을 동일한 20~50개 테스트 셋으로 평가해볼 수 있습니다. ROUGE-L이나 BERTScore 같은 자동 지표와 실제 출력 비교를 병행하면, 파인튜닝이 가져다주는 변화를 직접 확인할 수 있습니다.


참고 자료

필독 (시작점으로 권장)

  • Fine-Tuning Small Language Models for Domain-Specific AI: An Edge AI Perspective | arXiv 2025
  • We Benchmarked 12 SLMs Across 8 Tasks | distil labs
  • EVAL #003: Fine-Tuning in 2026 — Axolotl vs Unsloth vs TRL vs LLaMA-Factory | DEV Community

심화 자료

  • SLM Finetuning for Natural Language to Domain Specific Code Generation in Production | arXiv 2025
  • Building Domain-Specific Small Language Models via Guided Data Generation | arXiv 2024
  • Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide | Effloow
  • Fine-Tuning Small Language Models with Domain-Specific Data On-Premises | SysArt
  • Improved Supervised Fine-Tuning to Mitigate Catastrophic Forgetting | arXiv 2025
  • SFT Doesn't Always Hurt General Capabilities | arXiv 2025
  • 도메인 특화 LLM: Mistral 7B를 활용한 금융 업무분야 파인튜닝 | KCI
  • Introduction to Small Language Models: The Complete Guide for 2026 | MachineLearningMastery
  • How To Fine-Tune LLMs For Domain-Specific Adaptation | Open Source For You 2026
  • A Practical Guide to Fine-Tuning Small Language Models | Omdena
#QLoRA#SLM#LoRA#파인튜닝#Unsloth#RAG#양자화#LangChain#HuggingFace#SFT
공유하기

목차

핵심 개념SLM이란 무엇이고, 왜 지금인가파인튜닝 방법론 3가지: 뭘 골라야 할까LoRA의 수학적 직관실전 적용예시 1: QLoRA로 금융 도메인 SLM 학습시키기예시 2: RAG + SLM 하이브리드 파이프라인장단점 분석장점단점 및 주의사항실무에서 가장 흔한 실수마치며참고 자료

추천 포스트

로컬 LLM TCO 분석: 온프레미스 전환 손익분기점 계산법과 GPU 활용률 최적화 전략
AI

로컬 LLM TCO 분석: 온프레미스 전환 손익분기점 계산법과 GPU 활용률 최적화 전략

클라우드 LLM API 청구서를 받아들고 한숨 쉬어본 적 있을 것이다. "그냥 우리 서버에 올리면 싸지 않나?"라는 생각, 저도 꽤 오래 해왔다. 그런데 막상 계산해보면 API 요금과 온프레미스 TCO는 전혀 다른 구조를 갖고 있고, 어느 쪽이 유리한지는 의외로 단순한 기준 몇 가지로 ...

2026년 06월 12일읽는 데 22분
AI 코딩 에이전트가 바꾸는 개발팀 구조: 오케스트레이터로 전환하는 법
AI

AI 코딩 에이전트가 바꾸는 개발팀 구조: 오케스트레이터로 전환하는 법

솔직히 말하면, 처음 "코딩 에이전트 도입 후 팀을 재편한다"는 말을 들었을 때 그냥 과장된 마케팅 문구라고 생각했습니다. AI 보조 도구가 코드 완성 속도를 높여준다는 건 체감하고 있었는데, 팀 구조 자체가 바뀐다고? 좀 과했죠. 그런데 2026년 현재, 팀 내에서 그 변화가 실...

2026년 06월 12일읽는 데 25분
AI가 짜고 AI가 검토한다: `/code-review ultra` 멀티에이전트 파이프라인 구축기
AI

AI가 짜고 AI가 검토한다: `/code-review ultra` 멀티에이전트 파이프라인 구축기

솔직히 말하면, 저도 처음 이 개념을 들었을 때 "그게 실제로 돌아가?" 싶었습니다. 에이전트가 혼자 코드를 쓰는 것도 신기한데, 그 코드를 또 다른 에이전트가 검토하고 피드백까지 남긴다니. 그런데 2026년 현재, 이건 이미 실무에서 쓰이는 이야기입니다. 에이전틱 코딩 루프(Ag...

2026년 06월 07일읽는 데 20분
Long-Horizon 에이전트 비용 60~90% 줄이기: 캐싱·압축·라우팅 전략
AI

Long-Horizon 에이전트 비용 60~90% 줄이기: 캐싱·압축·라우팅 전략

AI 에이전트를 프로덕션에 올리고 나서 처음 청구서를 받았을 때의 충격을 아직도 기억한다. 단순 챗봇이라면 예측 가능했을 텐데, 에이전트는 달랐다. 버그 하나 고치는 데 컨텍스트가 수만 토큰씩 쌓이고, 실패하면 그 비용 그대로 날리고 처음부터 다시. 단일 실행이 예상의 열 배를 넘어가는...

2026년 06월 07일읽는 데 24분
Pydantic AI로 LLM 응답을 타입 안전하게 검증하기
AI

Pydantic AI로 LLM 응답을 타입 안전하게 검증하기

LLM을 프로덕션에 붙여본 분이라면 이런 상황을 한 번쯤 겪어봤을 겁니다. GPT한테 JSON으로 응답하라고 시스템 프롬프트까지 꼼꼼히 썼는데, 정작 런타임에 가 터지는 상황. 아니면 가 어떤 날은 고 어떤 날은 인 상황. LLM 출력을 딕셔너리로 받아 쓰는 코드는 언제나 이런 시한폭탄...

2026년 06월 07일읽는 데 22분
사내 REST API를 LLM이 직접 호출하게 만드는 법: TypeScript MCP 서버 구현과 Gateway 패턴
AI

사내 REST API를 LLM이 직접 호출하게 만드는 법: TypeScript MCP 서버 구현과 Gateway 패턴

팀에서 AI 에이전트를 도입하려다 "그래서 우리 내부 API는 어떻게 연결해?"라는 질문에 막혀본 적 있으신가요? 저도 처음엔 LLM에게 사내 API 문서를 통째로 붙여넣거나, 직접 fetch 코드를 프롬프트에 넣어주는 방식을 시도했습니다. 잘 됩니다—딱 한 번만요. 그 다음 요청부터는...

2026년 06월 07일읽는 데 19분