모델을 우리 서비스에 맞추고 싶은데 프롬프트를 더 다듬을지, LoRA를 학습할지, 아예 전체 파인튜닝을 할지 정하기 어려우시죠? 결론부터 말하면 학습비는 생각보다 작고(7B 모델, 1,500만 토큰 학습이면 수 달러), 실제로 갈리는 건 GPU 메모리와 배포 형태예요. 7B 전체 파인튜닝은 고정 메모리만 약 122GB라 H100 두 장 이상이 필요하지만, LoRA는 약 16GB, QLoRA는 약 6GB로 한 장에 들어가요. 그리고 프롬프트 캐시가 되는 API라면 "긴 지시문 토큰을 아끼려고" LoRA를 하는 건 돈으로는 거의 이득이 없어요.
이 글은 원리보다 도입 결정에 필요한 숫자를 다뤄요. 단가는 Lambda·DeepSeek·Together AI 공식 가격표(2026-10-26 확인), 모델 구조는 Qwen2.5 config.json에서 가져와 직접 계산했어요.
먼저 가를 것 — 학습 없이 끝나는 일인지
파인튜닝을 하면 데이터·평가 세트·모델 버전 관리가 계속 따라와요. 학습 전에 세 가지를 먼저 확인해요.
- 지시문과 예시 몇 개로 원하는 출력이 나오나요? 나오면 프롬프트에서 멈춰요.
- 틀리는 이유가 "모르는 사실" 때문인가요? 가격·규정처럼 자주 바뀌는 정보는 학습하면 바뀔 때마다 다시 해야 하니 RAG가 맞아요.
- 틀리는 이유가 "형식·말투·판단 습관" 때문인가요? JSON 구조가 가끔 어긋나거나 사내 문체를 못 지키는 경우로, LoRA가 잘 맞는 자리예요.
방식별 학습 메모리 계산 — 7B·72B에 GPU 몇 장
학습 중 GPU에 고정으로 올라가는 건 가중치, 기울기, 옵티마이저 상태예요. 혼합 정밀도 AdamW 기준으로 파라미터 하나당 bf16 가중치 2바이트, bf16 기울기 2바이트, fp32 마스터 가중치 4바이트, Adam 모멘트 두 개 8바이트를 합쳐 16바이트로 잡았어요.
- 전체 파인튜닝: 모든 파라미터 × 16바이트
- LoRA: 얼린 베이스는 bf16 가중치 2바이트만, 어댑터 파라미터만 16바이트
- QLoRA: 얼린 선형층을 NF4로 4비트 + 이중 양자화 상수. QLoRA 논문이 계산한 상수 비용이 블록 64개 기준 파라미터당 0.127비트라 합계 4.127비트로 잡았어요. 임베딩·출력층은 bf16 그대로 두는 경우가 많아 그만큼은 2바이트로 셌어요
모델은 공개 모델인 Qwen2.5-7B-Instruct(7.61B, 임베딩 제외 6.53B)와 Qwen2.5-72B-Instruct(72.7B, 임베딩 제외 70.0B)이고, 어댑터는 모든 선형층(all-linear)에 r=16으로 붙였어요. 7B 어댑터는 층당 1,441,792개 × 28층 = 약 4,037만 개예요.
이 숫자를 GPU 장수로 바꾸면 선택지가 확 줄어요(활성값 여유를 두고 잡은 값이에요).
| 모델 · 방식 | 고정 메모리 | 현실적인 최소 구성 |
|---|---|---|
| 7B 전체 파인튜닝 | 약 122GB | H100 80GB 2장 이상 + ZeRO/FSDP 분산 |
| 7B LoRA | 약 15.9GB | 48GB급 1장 (24GB는 짧은 시퀀스만) |
| 7B QLoRA | 약 6.2GB | 24GB급 1장 |
| 72B 전체 파인튜닝 | 약 1,163GB | H100 16장 이상 (8장 노드 2개) |
| 72B LoRA | 약 149GB | H100 4장 정도 |
| 72B QLoRA | 약 45GB | 80GB 1장 (48GB는 빠듯) |
72B 전체 파인튜닝은 노드 두 개를 묶어야 해서 인프라가 더 큰 장벽이에요. QLoRA 논문도 65B 모델의 메모리를 780GB 이상에서 48GB 아래로 줄였다고 적어요. GB와 GiB가 섞여 헷갈리면 데이터 용량 변환기로 맞춰 보면 돼요.
학습비는 얼마나 — 직접 돌릴 때와 관리형 서비스
학습 데이터는 5,000건 × 1,000토큰 = 500만 토큰, 3에폭이면 1,500만 토큰으로 잡았어요. 연산량은 전체 파인튜닝이 토큰당 약 6×파라미터 FLOP이고, LoRA는 얼린 가중치의 기울기 계산이 빠져 약 4×파라미터로 근사했어요. H100의 bf16 밀집 연산은 NVIDIA H100 사양의 1,979TFLOPS가 희소성 기준이라 절반인 약 989TFLOPS로 보고, 실제 활용률은 40%로 가정했어요.
| 구성 | GPU 시간 (H100) | 직접 돌릴 때 비용 | Together AI 관리형 |
|---|---|---|---|
| 7B 전체 파인튜닝 | 약 0.48 | 약 1.9달러 (8장 노드 장당 3.99달러) | Llama 3.1 8B 기준 5.70달러 |
| 7B LoRA | 약 0.32 | 약 1.4달러 (1장 4.29달러) | Llama 3.1 8B 기준 5.10달러 |
| 72B 전체 파인튜닝 | 약 4.59 | 약 18달러 + 노드 2개 구성 | Llama 3.3 70B 기준 33.60달러 |
| 72B LoRA | 약 3.06 | 약 12.5달러 (4장 장당 4.09달러) | Llama 3.3 70B 기준 30.45달러 |
GPU 단가는 Lambda 가격표의 H100 SXM 온디맨드 요금이고, 관리형 단가는 Together AI 가격표의 SFT 100만 토큰당 요금(Llama 3.1 8B LoRA 0.34달러·전체 0.38달러, Llama 3.3 70B LoRA 2.03달러·전체 2.24달러)에 1,500만 토큰을 곱했어요. 같은 모델은 아니니 규모 감만 잡는 용도예요.
표에서 읽을 점은 두 가지예요.
- 학습 한 번의 계산 비용은 수 달러에서 수십 달러예요. 실제 예산은 데이터 정제, 하이퍼파라미터 탐색, 평가에 들어가요.
- 관리형 서비스에선 LoRA와 전체 파인튜닝의 학습 단가 차이가 약 10%에 불과해요. 결정은 서빙 쪽에서 나요.
활용률 40%는 가정이라 실제 시간은 작은 데이터로 재서 늘려 잡아요. 한국어 데이터는 토크나이저마다 토큰 수가 꽤 달라서 학습 토큰도 실제 토크나이저로 세어 봐요.
긴 프롬프트 vs LoRA — 요청당 비용과 손익분기
LoRA를 검토하는 흔한 이유가 "요청마다 붙는 긴 지시문과 예시를 없애고 싶다"예요. 요청당 지시문 3,000토큰 + 사용자 입력 500토큰 + 출력 300토큰으로 계산해 볼게요. 단가는 DeepSeek 가격표의 피크 요금이에요.
| API 모델 · 조건 | 요청당 비용 | 지시문 없을 때 |
|---|---|---|
| deepseek-v4-pro, 캐시 미스 | 0.005808달러 | 0.001848달러 |
| deepseek-v4-pro, 지시문 캐시 적중 | 0.001980달러 | — |
| deepseek-flash, 캐시 미스 | 0.001410달러 | 0.000510달러 |
| deepseek-flash, 지시문 캐시 적중 | 0.000528달러 | — |
flash에서 캐시가 맞으면 3,000토큰 지시문 값은 0.000018달러로, 지시문이 없을 때와 거의 같아요. DeepSeek은 컨텍스트 캐시 안내에 따르면 기본으로 켜져 있고, 앞부분이 완전히 같은 요청이어야 적중해요. 그러니 지시문은 항상 맨 앞에, 바뀌는 내용은 뒤에 두는 것만으로 비용 대부분이 사라져요.
그럼 LoRA를 붙인 7B를 H100 한 장(4.29달러 × 730시간 = 약 3,132달러/월)에 띄우는 것과 비교하면 어떨까요.
자체 호스팅 선은 GPU 한 장이 그 요청량을 다 처리한다는 가정이에요. 넘치면 장수만큼 계단식으로 올라가요.
표로 보기
| 구분 | V4-Pro 캐시 미스 | V4-Pro 캐시 적중 | Flash 캐시 적중 | 7B+LoRA 자체 호스팅 |
|---|---|---|---|---|
| 50만 | 2,904 | 990 | 264 | 3,132 |
| 100만 | 5,808 | 1,980 | 528 | 3,132 |
| 200만 | 11,616 | 3,960 | 1,056 | 3,132 |
| 300만 | 17,424 | 5,940 | 1,584 | 3,132 |
| 500만 | 29,040 | 9,900 | 2,640 | 3,132 |
손익분기(GPU 월세 ÷ 요청당 API 비용)는 V4-Pro 캐시 미스 대비 월 약 54만 요청, 캐시 적중 대비 약 158만 요청, Flash 캐시 적중 대비 약 593만 요청이에요. 7B를 작은 API와 같은 품질로 맞출 수 있다는 전제도 필요해요. 결국 LoRA를 고르는 이유는 토큰 절약보다 품질의 일관성, 데이터 반출 금지, 지연이어야 해요. 같은 계산을 지식 증류 관점에서 한 지식 증류 방식 고르기도 결론이 비슷해요.
자체 호스팅도 vLLM의 자동 프리픽스 캐싱으로 같은 앞부분의 KV 캐시를 재사용하면 긴 지시문의 프리필 계산이 크게 줄어요.
배포 형태 고르기 — 합치기·멀티 어댑터·모델 여러 벌
LoRA의 돈 이득이 분명해지는 건 작업 변형이 여러 개일 때예요. 고객사별·언어별·기능별로 다르게 맞춘 모델이 10개 필요하다고 해 볼게요.
- 전체 파인튜닝 10벌: 7B bf16 한 벌이 약 15.2GB, 10벌이면 152GB라 갈아 끼우기 어려워 모델마다 GPU를 따로 둬요.
- LoRA 어댑터 10개: r=16 어댑터가 약 80.7MB라 10개여도 0.8GB예요. 베이스 한 벌 위에서 요청마다 골라 써요.
vLLM은 --enable-lora로 켜고 --lora-modules 이름=경로로 어댑터를 등록한 뒤, 요청의 model 값으로 어댑터를 고르게 해 줘요(vLLM LoRA 문서).
vllm serve Qwen/Qwen2.5-7B-Instruct \
--enable-lora \
--lora-modules tone-a=./adapters/tone-a support-b=./adapters/support-b \
--max-loras 4 \
--max-lora-rank 16
vLLM 소스의 LoRAConfig를 보면 max_loras(한 배치에 동시에 들어갈 어댑터 수)가 1, max_lora_rank가 16이에요. 어댑터를 여러 개 섞어 받으려면 max_loras를 올려야 하고, r=32·64 어댑터는 max_lora_rank를 그에 맞게 올려야 해요. 문서는 필요 이상으로 크게 잡으면 메모리를 낭비하고 성능이 떨어질 수 있다고 적어요.
작업이 하나뿐이고 지연이 중요하면 반대로 합쳐서 배포해요. PEFT의 merge_and_unload()로 어댑터를 가중치에 더하면 원래 모델과 같은 구조가 되어 추가 계산이 없어요. 이 함수는 제자리 연산이 아니라서 반환값을 받아 저장해야 한다고 PEFT LoRA API 문서에 적혀 있어요. 합친 모델을 서빙할 GPU 용량은 vLLM KV 캐시 용량 계산으로 잡으면 돼요.
품질 쪽 숫자 — LoRA가 덜 배우는 경우
비용이 싸도 품질이 모자라면 소용없어요. 1차 자료가 말하는 경계는 이래요.
- 좁은 작업에선 작은 r로도 충분했어요. LoRA 논문은 GPT-3 175B에서 전체 파인튜닝과 비슷하거나 나은 품질을 보고했고, 같은 예산이면 여러 행렬에 작은 r로 나눠 붙이는 쪽이 나았어요. 체크포인트는 350GB에서 35MB로 줄었어요.
- 새 영역을 깊게 배우는 일에선 크게 뒤질 수 있어요. LoRA Learns Less and Forgets Less는 코딩·수학 영역의 지시 튜닝과 지속 사전학습 모두에서 흔한 저랭크 설정의 LoRA가 전체 파인튜닝보다 상당히 낮았다고 보고해요. 대신 목표 밖 작업의 원래 성능은 더 잘 지켰어요.
- QLoRA는 4비트로 얼려도 16비트 수준을 유지했다고 보고해요. 논문의 Guanaco 모델은 Vicuna 벤치마크에서 ChatGPT 대비 99.3% 수준이었고, 단일 GPU에서 24시간 학습했어요.
정리하면 말투·형식·좁은 분류는 LoRA로 시작하고, 새 언어·도메인 지식을 대량으로 넣는 일은 전체 파인튜닝과 작게 비교 실험한 뒤 정해요. 학습 전후로 범용 벤치마크 몇 개를 돌려 원래 능력을 얼마나 잃는지도 같이 재요.
설정 출발점 — r·alpha·target_modules
PEFT LoraConfig의 기본값은 r=8, lora_alpha=8, lora_dropout=0.0이고, target_modules를 비우면 모델 구조에 맞춰 자동으로 골라요. "all-linear"를 주면 출력층을 뺀 모든 선형층에 붙어요. 어댑터 크기는 붙이는 층과 r에 비례해요.
| 시작 설정 (Qwen2.5-7B) | 어댑터 파라미터 | 전체 대비 | bf16 파일 크기 |
|---|---|---|---|
| q·v만, r=8 | 약 252만 | 0.033% | 약 5.0MB |
| all-linear, r=16 | 약 4,037만 | 0.53% | 약 80.7MB |
| all-linear, r=64 | 약 1억 6,148만 | 2.12% | 약 323MB |
config.json만으로 어댑터 크기와 학습 메모리를 미리 보는 스크립트예요(출력은 위 차트 값과 같아요).
import json, urllib.request
def lora_budget(repo, r=16, total_params=None, non_embed=None):
url = f"https://huggingface.co/{repo}/resolve/main/config.json"
c = json.load(urllib.request.urlopen(url))
d, inter, layers = c["hidden_size"], c["intermediate_size"], c["num_hidden_layers"]
head = c.get("head_dim") or d // c["num_attention_heads"]
kv = c["num_key_value_heads"] * head
per_layer = r * ((d + d) * 2 + (d + kv) * 2 + (d + inter) * 3)
adapter = per_layer * layers
gb = lambda b: round(b / 1e9, 1)
print(f"{repo}: adapter {adapter:,} params, {adapter * 2 / 1e6:.1f} MB (bf16)")
if total_params:
print(" full FT :", gb(total_params * 16), "GB")
print(" LoRA :", gb(total_params * 2 + adapter * 16), "GB")
q = non_embed * 4.127 / 8 + (total_params - non_embed) * 2
print(" QLoRA :", gb(q + adapter * 16), "GB")
lora_budget("Qwen/Qwen2.5-7B-Instruct", r=16, total_params=7.61e9, non_embed=6.53e9)
배율이 α/r이라 r을 바꾸면서 lora_alpha를 그대로 두면 배율도 같이 바뀌어요. 탐색할 땐 r·α·학습률 중 하나씩만 움직여요.
상황별 추천
| 상황 | 추천 | 이유 |
|---|---|---|
| 프롬프트로 품질이 나옴 | 프롬프트 + 캐시 | 관리할 모델이 없어요 |
| 자주 바뀌는 사실 | RAG | 다시 학습할 필요가 없어요 |
| JSON 형식·문체 일관성 | LoRA (합쳐서 배포) | 작은 어댑터로 충분한 경우가 많아요 |
| 고객사·기능별 변형 여럿 | LoRA (멀티 어댑터) | GPU 비용이 변형 수와 무관해요 |
| 24~48GB GPU 한 장뿐 | QLoRA | 7B 약 6GB, 72B 약 45GB |
| 코딩·수학·새 언어를 깊게 | 전체 파인튜닝과 비교 실험 | LoRA가 크게 뒤진 연구가 있어요 |
| 데이터 반출 금지 | LoRA/QLoRA + 자체 호스팅 | 규정이 결정해요 |
도입 체크리스트
자주 묻는 질문
LoRA와 QLoRA 중 무엇으로 시작하나요?
GPU 메모리가 충분하면 LoRA가 단순하고 학습도 빨라요. QLoRA는 매 스텝 4비트 가중치를 풀어 계산하는 비용이 붙어요. 7B를 24GB GPU에서 학습하거나 72B를 한 장에 올려야 할 때처럼 메모리가 막힐 때 QLoRA로 내려가면 돼요.
학습 데이터는 몇 건이 필요한가요?
정해진 숫자는 없고 작업 범위에 따라 달라요. 데이터를 절반·전부로 나눠 학습해 보고, 늘려도 평가 점수가 오르지 않는 지점을 찾는 게 가장 확실해요.
프롬프트 캐시가 있으면 LoRA는 필요 없나요?
비용만 보면 이득이 거의 없어요. 다만 긴 지시문으로도 형식이 가끔 어긋나는 문제는 캐시로 풀리지 않아서, 그럴 땐 LoRA가 여전히 후보예요.
어댑터를 합치면 품질이 달라지나요?
같은 계산이라 부동소수점 오차 수준의 차이만 생겨요. 다만 합친 모델을 다시 양자화하면 차이가 커질 수 있으니 평가 세트를 한 번 더 돌려요.
베이스 모델을 새 버전으로 바꾸면 어댑터를 그대로 쓸 수 있나요?
못 써요. 어댑터는 특정 베이스 가중치 위의 변화량이라 베이스가 바뀌면 의미가 없어져요. 층 이름과 크기가 같아 로드는 되더라도 품질은 보장되지 않으니 같은 데이터로 다시 학습해요.
정리
- 프롬프트 → RAG → LoRA → QLoRA → 전체 파인튜닝 순서로 따지고, 앞 단계에서 기준선을 넘으면 멈춰요.
- 7B 학습 고정 메모리는 전체 약 122GB, LoRA 약 15.9GB, QLoRA 약 6.2GB예요. 학습 계산비는 수 달러라 메모리가 결정해요.
- 캐시가 맞으면 긴 지시문 비용은 거의 없어요. LoRA의 이유는 일관성·데이터 규정·지연, 그리고 변형이 여럿일 때의 멀티 어댑터 서빙이에요.
- 새 영역을 깊게 배우는 작업은 전체 파인튜닝과 작게 비교해요. 관련 계산은 LLM 양자화 형식 고르기와 vLLM KV 캐시 용량 계산에 이어져 있어요.
더 깊이 읽기
이 글은 실무에서 판단할 때 필요한 것 위주로 정리했어요. 같은 주제의 내부 동작 원리는 devboi 기술 블로그의 LoRA 원리 — 파인튜닝·프롬프트, 언제 무엇을 쓰나에서 도식과 코드로 단계별로 풀었어요.