파인튜닝이란 무엇이고, LoRA 는 왜 표준이 됐나

“우리 일에 맞는 AI 모델을 하나 만들면 어떨까.” 앱을 만들다 보면 이 생각을 한 번은 하게 된다. 같은 형식의 요청을 하루에 수천 번 API 로 보내고 있다면 더 그렇다.
그런데 모델을 처음부터 만드는 건 자릿수가 다른 이야기다. 수조 개의 토큰과 GPU 수천 장이 필요하다. 현실적인 선택지는 이미 공개된 모델을 가져와 내 일에 맞게 조금 고쳐 쓰는 것, 즉 파인튜닝이다. 이 글은 파인튜닝이 무엇인지, 그리고 지금 사실상 표준이 된 LoRA 가 무엇인지 정리한 것이다.
파인튜닝이란
언어 모델이 만들어지는 과정은 두 단계로 나눌 수 있다.
- 사전학습(pre-training) — 인터넷 규모의 글을 읽으며 언어와 세상에 대한 일반 지식을 익힌다. 대학을 갓 졸업한 신입사원과 비슷하다.
- 파인튜닝(fine-tuning) — 그 모델에 내 데이터로 추가 학습을 시켜 가중치를 특정 용도에 맞게 조금 옮긴다. 회사에 들어와 업무 교육을 받는 단계다. 우리 회사의 양식, 말투, 판단 기준을 익힌다.
중요한 건 파인튜닝이 “지식을 넣는” 도구라기보다 “행동을 바꾸는” 도구라는 점이다. 출력 형식을 고정하거나, 특정 분류 기준을 몸에 배게 하거나, 말투를 바꾸는 데는 탁월하다. 반대로 최신 사실이나 사내 문서 내용을 외우게 하는 데는 잘 맞지 않는다. 그건 검색해서 붙여 주는 편(RAG)이 낫다.
| 방법 | 무엇을 바꾸나 | 잘 맞는 일 |
|---|---|---|
| 프롬프트·예시 몇 개 | 입력만 | 대부분의 일. 먼저 여기서 시작한다 |
| RAG (검색 후 붙이기) | 입력에 문서를 덧붙임 | 자주 바뀌는 사실, 사내 문서 |
| 파인튜닝 | 모델 가중치 | 고정된 형식·분류·말투, 같은 일을 대량으로 반복할 때 |
프롬프트로 이미 잘 되는 일이라면 파인튜닝할 이유가 없다. 파인튜닝은 “같은 일을 아주 많이 하는데, 더 작은 모델로 싸게 하고 싶다” 일 때 빛난다.
풀 파인튜닝은 왜 그렇게 비싼가
가장 단순한 방법은 모델의 모든 가중치를 다시 학습하는 것이다. 이걸 풀 파인튜닝이라고 한다. 성능은 가장 좋다. 문제는 메모리다.
요즘 표준 설정(bf16 혼합정밀도 + AdamW 옵티마이저)으로 학습하면, 파라미터 하나당 이만큼의 메모리가 필요하다.
| 항목 | 파라미터당 바이트 |
|---|---|
| 모델 가중치 (bf16) | 2 |
| 기울기 (bf16) | 2 |
| fp32 마스터 가중치 | 4 |
| Adam 1차·2차 모멘트 (fp32) | 8 |
| 합계 | 16 |
여기에 순전파 중간값(활성값)이 배치 크기와 문장 길이에 따라 더해진다. 그래서 기억하기 쉬운 공식은 이렇다.
풀 파인튜닝 메모리 ≈ 파라미터 수 × 16바이트 + α
7B 모델이면 112GB, 40B 모델이면 640GB 다. 80GB 짜리 H100 을 8장 꽂은 서버 한 대가 640GB 니, 40B 모델 하나를 풀 파인튜닝하려면 그 서버로도 빠듯하다. 실제로는 여러 장에 나눠 담는 샤딩(ZeRO, FSDP)이나 CPU 로 덜어내는 오프로딩까지 써야 한다.
LoRA — 바뀌는 양만 작게 학습한다
LoRA(Low-Rank Adaptation)는 2021년 Microsoft 연구진이 낸 방법이다. 아이디어는 한 줄이다.
원래 가중치 W 는 얼려 두고, 바뀌는 양 ΔW 만 작은 행렬 두 개의 곱으로 학습한다.
W' = W + ΔW = W + B·A
W 가 4096×4096 행렬이라고 하자. 통째로 학습하면 약 1,670만 개의 숫자를 바꿔야 한다. LoRA 는 ΔW 를 4096×r 행렬 B 와 r×4096 행렬 A 의 곱으로 표현한다. r(rank)을 16 으로 잡으면 학습할 숫자는 16×(4096+4096) = 약 13만 개, 원래의 0.8% 다.
“파인튜닝으로 생기는 변화는 생각보다 단순하다(저랭크다)” 는 관찰이 이 방법의 근거다. 실제로 이 정도로 줄여도 대부분의 태스크에서 풀 파인튜닝과 성능 차이가 크지 않다. 원 논문은 GPT-3 175B 에서 학습할 파라미터를 약 1만 분의 1, GPU 메모리를 약 3분의 1 로 줄였다고 보고했다.
이게 왜 좋은가.
- 메모리가 확 준다. 원래 가중치는 학습하지 않으니 기울기도, 옵티마이저 상태도 필요 없다. 들고 있어야 하는 건 사실상 모델 가중치 하나(파라미터당 2바이트) 와 작은 어댑터뿐이다.
- 결과물이 작다. 수십 GB 짜리 모델 대신 수십 MB 짜리 어댑터 파일이 나온다. 기본 모델 하나에 용도별 어댑터 여러 개를 갈아 끼울 수 있다.
- 추론이 느려지지 않는다. 학습이 끝나면 B·A 를 W 에 더해 합쳐 버리면 된다. 구조가 원래 모델과 똑같아진다.
QLoRA — 기본 모델까지 4bit 로
2023년에 나온 QLoRA 는 한 걸음 더 간다. 얼려 둔 기본 모델을 4bit 로 양자화해서 올려 두고, 그 위에서 LoRA 를 학습한다. 파라미터당 0.5바이트 수준이니 7B 모델이 24GB 소비자용 GPU 한 장(RTX 3090·4090)에 들어간다. 논문은 65B 모델을 48GB GPU 한 장으로 학습했다. 처음 해 본다면 QLoRA 부터 시작하는 게 보통이다.
실제 설정은 이 정도다
Hugging Face 의 peft 라이브러리로 쓰면 설정은 몇 줄이다.
from peft import LoraConfig, get_peft_model
lora_config = LoraConfig(
r=16, # rank. 8~64. 클수록 표현력↑, 크기↑
lora_alpha=32, # 스케일. 보통 r 의 2배
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"], # 어댑터를 붙일 층
lora_dropout=0.05,
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters() # 전체의 0.1~1% 정도가 찍힌다
target_modules 는 보통 어텐션의 q·k·v·o 에서 시작하고, 부족하면 MLP 층까지 넓힌다.
LoRA 말고는 없나
있다. 다만 두 가지 질문을 섞지 않는 게 중요하다. “어느 파라미터를 바꾸나” 와 “무엇을 목표로 학습하나” 는 서로 다른 축이다. LoRA 는 앞의 축에 속하고, 뒤의 축과 조합해서 쓴다.
어느 파라미터를 바꾸나
| 방식 | 한 줄 설명 |
|---|---|
| 풀 파인튜닝 | 전부 학습. 최고 성능, 최대 비용 |
| 일부 층만 학습 | 위쪽 몇 층만 풀어 준다. 가장 오래된 방법 |
| Adapter | 층 사이에 작은 모듈을 끼운다. LoRA 이전의 표준 |
| Prefix·Prompt Tuning | 입력 앞에 학습 가능한 가상 토큰을 붙인다 |
| LoRA 계열 | QLoRA, DoRA, AdaLoRA 등 변형이 많다 |
| GaLore | 전체를 학습하되 기울기를 압축해 메모리를 아낀다 |
무엇을 목표로 학습하나
| 방식 | 필요한 데이터 | 용도 |
|---|---|---|
| SFT (지도 미세조정) | 질문 + 모범 답 | 형식·태스크 학습. 가장 기본 |
| 증류 | 큰 모델이 만든 답 | 큰 모델의 능력을 작은 모델로 옮긴다 |
| DPO 등 선호 학습 | 좋은 답 / 나쁜 답 쌍 | 말투·품질 다듬기 |
| GRPO 등 강화학습 | 채점기 | 정답을 판정할 수 있는 일(수학·코드·분류) |
흔한 조합은 “SFT 를 LoRA 로”, 그다음 필요하면 “DPO 를 LoRA 로” 다.
하나 덧붙이면, 증류용 데이터를 상용 API 모델로 만들면 안 된다. 주요 상용 모델의 이용약관은 그 출력으로 경쟁 모델을 학습시키는 걸 금지한다. 큰 모델의 답을 학습 데이터로 쓰려면 오픈 웨이트 모델을 쓰는 게 안전하다.
숫자로 보기 — 40B 와 180B
같은 공식으로 두 모델을 계산해 보자.
40B dense 모델
| 방식 | 메모리 | 대략의 장비 |
|---|---|---|
| 풀 파인튜닝 | 40B × 16 = 640GB + 활성값 | H100 8장으로 빠듯, 샤딩·오프로딩 필요 |
| LoRA (bf16) | 40B × 2 + α ≈ 90~110GB | H100 2장 |
| QLoRA (4bit) | 40B × 0.5 + α ≈ 30~45GB | 48GB GPU 1장 |
같은 모델인데 방법에 따라 스무 배 차이가 난다.
Qwen3.8-Flash-Next (MoE)
요즘 공개 모델은 MoE(Mixture of Experts) 구조가 많다. 전문가 여러 개 중 토큰마다 몇 개만 골라 계산하는 구조다. 공개된 자료 기준으로 이 모델은 총 파라미터가 약 180B(본체 125B + n-gram 임베딩 51B + MTP 4B), 토큰당 실제로 계산에 쓰이는 활성 파라미터는 약 6B 다.
여기서 흔한 착각이 있다. “활성이 6B 니 6B 모델처럼 다룰 수 있겠지.” 계산량은 그렇지만 메모리는 아니다. 한 번에 일부 전문가만 쓰더라도 학습할 때는 모든 전문가의 가중치·기울기·옵티마이저 상태를 들고 있어야 한다.
| 방식 | 메모리 | 대략의 장비 |
|---|---|---|
| 풀 파인튜닝 | 180B × 16 ≈ 2.9TB (n-gram 테이블을 얼리면 약 2.2TB) | H100 30~40장, 여러 대 |
| LoRA (bf16) | 180B × 2 + α ≈ 380~420GB | H100 8장 서버 1대 |
| QLoRA (4bit) | 180B × 0.5 + α ≈ 110~130GB | H200 1장 또는 H100 2장 |
반면 계산량은 활성 파라미터 기준이라 오히려 가볍다. 학습 연산량은 대략 6 × 파라미터 × 토큰 수 인데, 1억 토큰이면 40B dense 는 2.4×10¹⁹, 이 MoE 모델은 3.6×10¹⁸ 으로 여섯 배 이상 적다. MoE 는 메모리가 비싸고 계산이 싸다.
우리 사무실에는 VRAM 96GB 짜리 로컬 AI 박스가 있고, 이 모델을 2bit 양자화로 줄여 추론용으로 돌리고 있다. 그것만으로 86GB 를 다 쓴다. 위 표대로라면 같은 장비에서 이 모델을 학습하는 건 QLoRA 로도 불가능하다. 추론과 학습은 필요한 메모리가 이 정도로 다르다.
MoE 모델에 QLoRA 를 걸 때는 하나 더 조심할 게 있다. 전문가 가중치가 하나로 합쳐진(fused) 형태인 경우가 많아 4bit 양자화 라이브러리가 잘 안 붙는다. 보통은 어텐션 층에만 LoRA 를 걸고 라우터는 얼려 둔다.
사면 수억, 빌리면 수십만 원
H100 8장 서버를 사려면 수억 원이다. 표만 보면 “파인튜닝은 대기업 일” 같지만, 학습은 몇 시간짜리 일이라 살 이유가 없다. 클라우드 GPU 는 시간 단위로 빌린다. 시세는 업체와 시기에 따라 다르지만 H100 한 장에 시간당 몇 달러 수준이고, 8장 서버도 시간당 수십 달러다.
180B MoE 모델에 LoRA 를 건다고 해도 8장 서버를 반나절 빌리면 되고, 8B 모델에 QLoRA 라면 GPU 한 장으로 몇 시간이면 끝난다. 실패해도 잃는 돈이 치킨 몇 마리 값이다. 진짜 비용은 GPU 가 아니라 좋은 학습 데이터 수천 건을 만드는 일에 든다.
이미지 모델에서의 LoRA
LoRA 가 대중적으로 가장 많이 알려진 곳은 사실 이미지 생성 쪽이다. Stable Diffusion·Flux 같은 모델에 특정 화풍, 캐릭터, 물건을 덧입히는 용도로 쓴다. 이미지 수십 장으로 학습한 수십~수백 MB 파일 하나를 받아서, 기본 모델 위에 “강도 0.7” 처럼 섞어 쓰는 식이다. 원리는 언어 모델과 똑같다. 원래 가중치는 그대로 두고 바뀌는 양만 작게 얹는다.
정리 — 고르는 순서
- 프롬프트로 되는지 먼저 본다. 대부분 여기서 끝난다.
- 지식이 필요하면 RAG. 파인튜닝으로 사실을 외우게 하지 않는다.
- 형식·분류·말투를 굳히고 싶으면 SFT + LoRA (또는 QLoRA). 처음이면 QLoRA, 작은 모델(7~8B)부터.
- 품질을 더 다듬으려면 DPO, 정답을 채점할 수 있으면 GRPO 를 LoRA 위에 얹는다.
- 풀 파인튜닝은 LoRA 로 해 봤는데 부족할 때만. 그리고 장비는 빌린다.
파인튜닝은 “더 큰 모델” 을 만드는 일이 아니라 “더 작은 모델이 내 일 하나만큼은 큰 모델처럼 하게” 만드는 일이다. 그렇게 보면 LoRA 가 왜 표준이 됐는지도 자연스럽다. 그 목적에 필요한 만큼만, 아주 싸게 고치기 때문이다.
댓글