🔌
LoRA — 66M개를 전부 바꾸지 말고, 1M개짜리 어댑터만 끼우자
파인튜닝의 효율적 대안 — 베이스 모델은 고정하고, 작은 행렬 2개만 훈련한다
파인튜닝의 문제
파인튜닝은 66M개 숫자를 전부 조정한다. 이게 문제가 되는 경우:
7B 모델(70억 개 숫자)을 파인튜닝하려면 GPU 메모리가 수십 GB 필요
용도별로 모델을 5개 만들면 저장 공간이 5배
훈련 시간도 오래 걸림
LoRA의 아이디어
"66M개 전부 바꿀 필요 없다. 작은 어댑터만 끼우면 된다."
파인튜닝:
DistilBERT 66M개 전부 조정 → 66M개 저장 (260MB)
LoRA:
DistilBERT 66M개 고정(얼림) + 어댑터 1M개만 훈련 → 1M개만 저장 (4MB)
비유:
파인튜닝 = 스마트폰 자체를 개조
→ 비싸고, 원본으로 못 돌아감
LoRA = 스마트폰에 케이스를 끼움
→ 싸고, 케이스만 바꾸면 됨, 폰은 그대로
작동 원리 — 행렬 분해
모델의 가중치는 큰 행렬이다. 예를 들어 768×768 = 589,824개 숫자.
LoRA는 이 큰 행렬을 직접 바꾸는 대신, 작은 행렬 2개를 옆에 붙인다:
원래: W (768×768) = 589,824개 숫자
LoRA: W(고정) + A(768×8) × B(8×768)
589,824 + 6,144 + 6,144 = 어댑터는 12,288개만
A와 B를 곱하면 768×768 행렬이 나온다. 근데 중간 차원이 8(rank)이라서 숫자가 훨씬 적다. 이 A, B만 훈련하고, 원래 W는 건드리지 않는다.
# 추론 시:
output = W @ input + (A @ B) @ input
↑ 고정 ↑ LoRA 어댑터
코드로 해보기
pip install peft transformers datasets
from transformers import AutoModelForSequenceClassification, AutoTokenizer, Trainer, TrainingArguments
from peft import LoraConfig, get_peft_model, TaskType
from datasets import load_dataset
# 1. 베이스 모델
model = AutoModelForSequenceClassification.from_pretrained("distilbert-base-uncased", num_labels=2)
tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
# 2. LoRA 설정 — 어떤 레이어에 어댑터를 끼울지
lora_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
r=8, # rank (작을수록 가벼움, 보통 4~16)
lora_alpha=16, # 스케일링 팩터
lora_dropout=0.1,
target_modules=["q_lin", "v_lin"] # attention의 Q, V 행렬에만 끼움
)
# 3. LoRA 적용 — 베이스 모델을 얼리고 어댑터를 붙임
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# → trainable params: 296,450 || all params: 67,251,714 || trainable%: 0.44%
# 66M 중 30만 개만 훈련! (0.44%)
# 4. 데이터 + 훈련 (파인튜닝과 동일)
dataset = load_dataset("imdb")
dataset = dataset.map(lambda x: tokenizer(x["text"], truncation=True, padding="max_length", max_length=512), batched=True)
trainer = Trainer(
model=model,
train_dataset=dataset["train"].select(range(1000)),
args=TrainingArguments(output_dir="./lora-model", num_train_epochs=3, per_device_train_batch_size=8)
)
trainer.train()
# 5. 저장 — 어댑터만 저장됨 (수 MB)
model.save_pretrained("./lora-adapter")
# → ./lora-adapter/adapter_model.safetensors (수 MB)
# 베이스 모델 260MB는 따로 저장 안 해도 됨 (Hub에서 다시 받으면 됨)
파인튜닝 vs LoRA 비교
| 파인튜닝 | LoRA | |
|---|---|---|
| 훈련 대상 | 66M개 전부 | 30만 개 (0.44%) |
| 저장 크기 | 260 MB | 수 MB |
| GPU 메모리 | 많이 필요 | 적게 필요 |
| 훈련 속도 | 보통 | 빠름 |
| 품질 | 기준 | 거의 동일 |
| 용도별 전환 | 모델 전체 교체 | 어댑터만 교체 |
핵심
파인튜닝이 "폰 자체를 개조"라면, LoRA는 "케이스만 끼우기"다. 용도별로 케이스(어댑터)만 바꾸면 된다. 감정 분석용 어댑터, 번역용 어댑터, 코드 생성용 어댑터 — 베이스 모델 하나에 어댑터만 교체.
핵심 개념
1
베이스 모델을 얼린다(freeze) — 66M개 숫자를 고정, 변경 불가로 설정
2
작은 행렬 A(768×8), B(8×768)를 각 레이어에 끼운다 — 어댑터
3
A와 B만 훈련한다 — 전체의 0.44%만 조정
4
추론 시: output = W×input + (A×B)×input — 원래 가중치 + 어댑터 보정
5
어댑터만 저장 (수 MB) — 용도별로 어댑터만 교체하면 됨
사용 사례
대형 모델 파인튜닝 — 7B/13B 모델을 GPU 1개로 훈련 가능
다용도 모델 — 베이스 모델 1개 + 어댑터 N개로 여러 태스크 지원
배포 크기 절감 — 260MB 대신 수 MB 어댑터만 배포