소비자 GPU 한 장으로, 내 도메인에 맞는 12B 모델을 직접 학습시킵니다.
✅ 핵심 요약
→ Unsloth는 FA2 대비 ~1.5배 빠르고 ~60% 적은 VRAM으로 Gemma 4를 학습시킵니다 → 12B QLoRA는 RTX 4090(24GB) 권장 — RTX 3060(12GB)에서도 가능하나 배치 크기 줄여야 함 → Encoder-Free 아키텍처 덕분에 멀티모달(이미지·오디오·텍스트) 어댑터가 단일 pass로 학습됩니다 → 기존 인코더 기반 모델처럼 인코더를 freeze할 필요가 없습니다 → use_gradient_checkpointing="unsloth" 필수 — 없으면 VRAM 30% 더 잡아먹습니다 → 26B-A4B MoE는 QLoRA 대신 16-bit LoRA 사용 권장 (MoE routing + 4-bit 충돌) → Thinking mode 보존하려면 학습 데이터에 reasoning 예시 최소 75% 유지 필요 → 학습 완료 후 GGUF로 변환하면 vLLM / SGLang / Ollama에 그대로 배포 가능
파인튜닝이 필요한 시점
모델이 좋아도 "내 도메인"에서는 삐걱거리는 순간이 있습니다. 아래 기준으로 판단하세요.
상황 접근법
| 특정 스타일·톤으로 항상 답하게 하고 싶다 | 파인튜닝 |
| 내부 전문 용어·도메인 지식이 필요하다 | 파인튜닝 |
| 특정 출력 포맷(JSON, 특수 구조)을 강제하고 싶다 | 파인튜닝 |
| 최신 외부 데이터를 참조해야 한다 | RAG가 더 적합 |
| 프롬프트 엔지니어링으로 해결 가능한 수준 | 파인튜닝 불필요 |
VRAM 요구량 — 모델 크기별
모델 방법 VRAM
| E2B | LoRA | 8~10GB |
| E4B | LoRA | ~17GB |
| 12B | QLoRA (4-bit) | ~16~20GB |
| 26B-A4B | 16-bit LoRA (QLoRA 비권장) | 40GB+ |
| 31B | QLoRA (4-bit) | ~22GB |
💡 12B QLoRA 기준으로 RTX 4090(24GB)이 편하게 돌아갑니다. RTX 3060(12GB)에서도 per_device_train_batch_size=1, gradient_accumulation_steps=8로 줄이면 가능합니다. 훈련 시간은 10K 샘플 기준 RTX 3060에서 약 3시간, RTX 4090에서 30~45분입니다.
Step 1. 설치
pip install unsloth
pip install --upgrade trl transformers datasets
Step 2. 모델 로드 (QLoRA)
from unsloth import FastLanguageModel
import torch
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="google/gemma-4-12b-it",
max_seq_length=4096, # 학습에 쓸 최대 시퀀스 길이
load_in_4bit=True, # QLoRA — 4-bit NF4 양자화
dtype=None, # 자동 감지 (BF16 권장)
)
⚠️ 26B-A4B는 예외 — MoE routing과 4-bit 양자화가 충돌합니다. 26B는 load_in_4bit=False, load_in_16bit=True로 16-bit LoRA를 써야 합니다.
Step 3. LoRA 어댑터 붙이기
model = FastLanguageModel.get_peft_model(
model,
r=16, # LoRA rank — 복잡한 태스크면 32, 간단하면 8
lora_alpha=16, # 보통 r과 동일하게 설정
lora_dropout=0, # Unsloth 최적화 — 0으로 유지
bias="none",
target_modules=[ # 어텐션 + FFN 레이어 모두 타겟
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",
],
use_gradient_checkpointing="unsloth", # VRAM 30% 절약 — 필수
random_state=42,
max_seq_length=4096,
)
💡 LoRA rank 선택 가이드
- r=8: 스타일·톤 조정처럼 단순한 태스크
- r=16: 범용 기본값, 대부분 여기서 시작
- r=32: 도메인 전문 지식, 복잡한 출력 포맷
- r=64: VRAM 여유 있을 때만, 과적합 주의
Step 4. 학습 데이터 준비
Gemma 4는 standard chat format을 씁니다. conversations 키에 role/content 리스트를 넣습니다.
데이터 포맷 (JSONL)
{"conversations": [{"role": "user", "content": "FastAPI 헬스체크 엔드포인트 짜줘"}, {"role": "assistant", "content": "from fastapi import FastAPI\n\napp = FastAPI()\n\n@app.get('/health')\ndef health_check():\n return {'status': 'ok'}"}]}
{"conversations": [{"role": "user", "content": "Redis pub/sub 파이썬 예제"}, {"role": "assistant", "content": "..."}]}
Thinking mode 보존할 때
추론 능력을 유지하려면 학습 데이터의 최소 75%에 reasoning 예시를 포함해야 합니다.
{
"conversations": [
{"role": "user", "content": "이 SQL 쿼리 최적화해줘: SELECT * FROM orders WHERE ..."},
{"role": "assistant", "content": "<|channel>thought\n인덱스가 없는 컬럼에 WHERE 절이 걸려있다. EXPLAIN을 먼저 보면...\n<channel|>\n\n인덱스 추가와 SELECT * 대신 필요한 컬럼만 지정하면 됩니다:\n\nCREATE INDEX idx_orders_status ON orders(status);\nSELECT id, user_id, total FROM orders WHERE status = 'pending';"}
]
}
데이터셋 로드
from datasets import load_dataset
dataset = load_dataset(
"json",
data_files={"train": "my_data.jsonl"},
split="train"
)
💡 데이터 양 가이드
- 스타일·톤 조정: 500~1,000개
- 태스크 특화 적응: 1,000~5,000개
- 도메인 지식 주입: 10,000~50,000개
양보다 질이 중요합니다. 노이즈 많은 10,000개보다 깨끗한 1,000개가 낫습니다.
Step 5. 학습 실행
from trl import SFTTrainer, SFTConfig
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset,
args=SFTConfig(
output_dir="./gemma4-12b-finetuned",
per_device_train_batch_size=2, # VRAM 빡빡하면 1로
gradient_accumulation_steps=4, # effective batch = 2 * 4 = 8
num_train_epochs=3,
learning_rate=2e-4,
warmup_ratio=0.1,
lr_scheduler_type="cosine",
bf16=True, # BF16 필수
logging_steps=10,
save_strategy="epoch",
max_seq_length=4096,
dataset_text_field="conversations",
# Thinking mode 쓸 경우 chat template 설정
# tokenizer.chat_template = "gemma-4-thinking"
),
)
trainer.train()
학습 중 loss 이상 여부
정상 범위:
- 12B: 초기 loss 2.0~3.5, 수렴 시 0.8~1.5
- E2B / E4B: 초기 loss 13~15 → 정상 (멀티모달 모델 특성)
← 이 숫자에 놀라지 마세요, 수렴은 정상적으로 됩니다
Step 6. 어댑터 저장 및 병합
어댑터만 저장 (빠른 실험용)
# 어댑터 파일만 저장 (~50MB)
model.save_pretrained("./gemma4-12b-adapter")
tokenizer.save_pretrained("./gemma4-12b-adapter")
베이스 모델과 병합 (배포용)
# LoRA 병합 후 저장
merged_model = model.merge_and_unload()
merged_model.save_pretrained("./gemma4-12b-merged")
tokenizer.save_pretrained("./gemma4-12b-merged")
Step 7. GGUF 변환 → 배포
병합된 모델을 GGUF로 변환하면 vLLM / SGLang / Ollama에 바로 올릴 수 있습니다.
# llama.cpp 변환 스크립트
python llama.cpp/convert_hf_to_gguf.py ./gemma4-12b-merged \
--outfile gemma4-12b-finetuned.gguf \
--outtype q4_k_m
Ollama에 올리기
# Modelfile
FROM ./gemma4-12b-finetuned.gguf
PARAMETER num_ctx 32768
PARAMETER temperature 1.0
PARAMETER top_p 0.95
PARAMETER top_k 64
ollama create my-gemma4 -f Modelfile
ollama run my-gemma4
자주 만나는 에러와 해결법
에러 원인 해결
| CUDA OOM | 배치 크기 과다 | per_device_train_batch_size=1, gradient_accumulation_steps 늘리기 |
| loss가 수렴 안 함 | lr 너무 낮음 | learning_rate=5e-4로 올려보기 |
| E2B/E4B loss 13~15 | 멀티모달 정상 동작 | 정상 — 무시하고 계속 |
| fp16 overflow on T4 | attention logits -1e9 overflow | Unsloth 최신 버전으로 업데이트 (버그 픽스 완료) |
| 추론 능력 저하 | thinking 예시 부족 | 학습 데이터 75% 이상 reasoning 포함 |
✅ 전체 파이프라인 요약
1. pip install unsloth
2. FastLanguageModel.from_pretrained() ← load_in_4bit=True
3. FastLanguageModel.get_peft_model() ← r=16, gradient_checkpointing="unsloth"
4. JSONL 데이터셋 준비 ← conversations 포맷
5. SFTTrainer.train()
6. merge_and_unload() → save_pretrained()
7. convert_hf_to_gguf.py → Q4_K_M
8. vLLM / SGLang / Ollama 배포
관련 글
- Gemma 4 12B 완전분석 1편 — 아키텍처·벤치마크·E4B 비교
- Gemma 4 12B 완전분석 2편 — vLLM / SGLang / Ollama 서빙 가이드
- Gemma 4 12B 완전분석 3편 — Unsloth 파인튜닝 실전
'Gemini' 카테고리의 다른 글
| Gemma 4 12B 완전분석 2편: vLLM / SGLang / Ollama 서빙 실전 세팅 (0) | 2026.06.05 |
|---|---|
| Gemma 4 12B 완전분석 1편 — 16GB 노트북에서 돌아가는 멀티모달 오픈소스 AI의 새 기준 (0) | 2026.06.05 |
| AI가 내 구글 검색을 대신한다? Gemini AI Mode 사용법 3단계 (0) | 2026.06.05 |
| Gemini 3.5 Flash Thought Preservation 완전분석 — 멀티턴 추론이 자동으로 이어지는 것, 비용은 어떻게 올라가나 (0) | 2026.05.29 |
| Gemini Omni vs Veo 3.1 — Google이 비디오 모델을 두 개 운영하는 이유 (0) | 2026.05.28 |