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

10분 분량조회 –

“우리 일에 맞는 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바이트 + α

풀 파인튜닝은 파라미터당 가중치 2, 기울기 2, 마스터 가중치 4, Adam 모멘트 8 해서 16바이트. LoRA 는 가중치 2바이트와 작은 어댑터, QLoRA 는 0.5바이트와 작은 어댑터
16바이트 중 모델 자체는 2바이트뿐이다. 나머지는 학습하느라 드는 짐이다. 아래 두 줄은 뒤에서 설명할 LoRA·QLoRA.

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
입력 x 가 두 갈래로 나뉜다. 위쪽은 얼려 둔 4096×4096 행렬 W 를 지나고, 아래쪽은 학습하는 작은 행렬 A(16×4096)와 B(4096×16)를 지난다. 두 결과를 더한 것이 출력이다
출력 = W·x + B·A·x. 원래 길(W)은 그대로 두고, 옆에 좁은 샛길(A→B)을 하나 내서 그것만 학습한다.

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 모델처럼 다룰 수 있겠지.” 계산량은 그렇지만 메모리는 아니다. 한 번에 일부 전문가만 쓰더라도 학습할 때는 모든 전문가의 가중치·기울기·옵티마이저 상태를 들고 있어야 한다.

작은 네모 512개가 격자로 놓여 있고, 그중 주황색 10개와 남색 1개만 색칠돼 있다. 토큰 하나는 이 11개 전문가만 계산하지만 메모리에는 512개가 전부 올라가 있어야 한다
어느 토큰이 어느 전문가를 부를지 모르니 전부 대기시켜 둬야 한다.
방식 메모리 대략의 장비
풀 파인튜닝 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장
같은 축에 그린 막대 그래프. 40B 모델은 풀 파인튜닝 640GB, LoRA 약 100GB, QLoRA 약 40GB. 180B MoE 모델은 풀 파인튜닝 약 2.9TB, LoRA 약 400GB, QLoRA 약 120GB. H100 1장(80GB)과 8장 서버(640GB) 기준선이 함께 있다
같은 축으로 그렸다. 방식을 바꾸는 게 모델 크기를 바꾸는 것보다 효과가 크다.

반면 계산량은 활성 파라미터 기준이라 오히려 가볍다. 학습 연산량은 대략 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” 처럼 섞어 쓰는 식이다. 원리는 언어 모델과 똑같다. 원래 가중치는 그대로 두고 바뀌는 양만 작게 얹는다.

정리 — 고르는 순서

  1. 프롬프트로 되는지 먼저 본다. 대부분 여기서 끝난다.
  2. 지식이 필요하면 RAG. 파인튜닝으로 사실을 외우게 하지 않는다.
  3. 형식·분류·말투를 굳히고 싶으면 SFT + LoRA (또는 QLoRA). 처음이면 QLoRA, 작은 모델(7~8B)부터.
  4. 품질을 더 다듬으려면 DPO, 정답을 채점할 수 있으면 GRPO 를 LoRA 위에 얹는다.
  5. 풀 파인튜닝은 LoRA 로 해 봤는데 부족할 때만. 그리고 장비는 빌린다.

파인튜닝은 “더 큰 모델” 을 만드는 일이 아니라 “더 작은 모델이 내 일 하나만큼은 큰 모델처럼 하게” 만드는 일이다. 그렇게 보면 LoRA 가 왜 표준이 됐는지도 자연스럽다. 그 목적에 필요한 만큼만, 아주 싸게 고치기 때문이다.

댓글