LLM·트랜스포머

LLM / 51번째 글

작은 모델을 우리 일에 맞추는 법

파인튜닝이 실제로 고치는 것은 지식이 아니라 행동입니다. 데이터 몇 건이 필요한지, LoRA가 무엇을 바꾸는지, 학습 전에 무엇을 먼저 만들어야 하는지를 정리합니다.

PALDYN Team15 MIN READ

지난 글에서 작은 모델로 내려도 되는지를 재는 방법을 봤다. 재 봤더니 격차가 조금 남았고, 그 조금 때문에 못 내리고 있다면 다음 수단이 파인튜닝이다.

그런데 파인튜닝은 "모델을 똑똑하게 만드는 일"이 아니다. 이걸 오해한 채로 시작하면 데이터를 몇 천 건 모으고 GPU를 며칠 돌린 뒤에 "왜 여전히 우리 회사 제품 이름을 모르지"라는 질문에 도착한다. 무엇이 고쳐지고 무엇이 안 고쳐지는지부터 갈라 놓는 편이 빠르다.

고치는 것은 지식이 아니라 행동이다

파인튜닝이 잘 고치는 것은 모델이 매번 어떻게 답하는가다. 출력 형식을 흔들림 없이 지키게 만들기, 우리 분류 체계의 15개 라벨 중 하나만 뱉게 만들기, 사내 문서의 말투를 따르게 만들기, 필요 없는 서론을 빼게 만들기. 이런 것들은 예시를 몇 백 건 보여 주면 눈에 띄게 잡힌다.

반대로 잘 안 고쳐지는 것은 모델이 무엇을 아는가다. 새 사실을 학습 데이터에 섞어 넣어도 모델은 그 사실을 외우기 전에 그 문장의 형식을 먼저 외운다. 결과는 "그럴듯한 말투로 틀린 답을 하는 모델"이고, 이건 원래 상태보다 나쁘다. 틀렸다는 것을 알아채기 더 어려워졌기 때문이다.

파인튜닝은 첫 계단이 아니라 마지막 계단이다

그래서 순서가 있다. 프롬프트를 다듬고, 까다로운 사례 몇 개를 예시로 붙이고, 지식이 문제라면 검색을 붙인다. 여기까지 해도 남는 것 — 형식이 가끔 깨지고, 말투가 안 잡히고, 프롬프트가 너무 길어져 지연이 늘어난 것 — 이 파인튜닝의 몫이다.

증상 먼저 해볼 것 파인튜닝이 답인가
JSON이 열 번에 한 번 깨진다 스키마 강제 디코딩 그래도 남으면 예
최신 정책을 모른다 검색 붙이기 아니오
우리 분류 라벨을 안 지킨다 라벨 목록을 프롬프트에 예
답이 장황하다 지시 한 줄 예
추론이 얕다 큰 모델 대개 아니오
프롬프트가 3,000토큰이다 — 예, 이게 가장 확실한 이득이다

마지막 줄이 실무에서 가장 자주 본전을 뽑는 자리다. 규칙과 예시를 프롬프트에 잔뜩 넣어 겨우 동작하게 만들어 뒀다면, 그 규칙을 가중치로 옮기는 것만으로 요청마다 붙던 수천 토큰이 사라진다. 품질이 그대로여도 비용과 지연이 함께 내려간다.

데이터는 500건부터 시작한다

"몇 건이 필요한가"에 대한 답은 작업의 좁기에 달렸다. 라벨 15개짜리 분류처럼 좁은 작업이면 라벨당 3050건, 그러니까 500건 남짓에서 이미 눈에 띄는 변화가 나온다. 형식과 말투까지 잡으려면 1,0003,000건 구간이 흔하다. 여기서 더 늘리는 것보다 틀린 예시를 걷어내는 쪽이 거의 항상 이득이 크다 — 잘못된 예시 하나는 올바른 예시 열 개를 상쇄한다.

데이터를 만드는 방법은 셋이다.

  • 사람이 쓴 기존 기록 — 상담 로그, 처리된 티켓, 승인된 문서. 가장 좋지만 그대로는 못 쓴다. 입력·출력 쌍으로 자르고 개인정보를 지우는 작업이 실제 비용의 대부분이다.
  • 큰 모델로 만든 데이터 — 빠르고 싸다. 다만 큰 모델의 버릇과 실수를 그대로 물려받으므로, 사람이 훑어 걸러내는 단계를 반드시 끼운다. 그리고 쓰려는 모델의 이용 약관이 이런 용도를 허용하는지 확인한다.
  • 운영 중인 큰 모델의 출력 — 이미 큰 모델로 서비스하고 있다면 그 입출력이 곧 학습 데이터다. 사용자가 고쳐 쓴 흔적이 있는 답은 특히 값지다. 어느 쪽이 채택됐는지가 라벨이 된다.

만들 때 한 가지만 지키면 된다. 학습 데이터의 입력 형식과 실제 요청의 입력 형식이 같아야 한다. 학습할 때는 문서를 통째로 넣었는데 운영에서는 요약본을 넣는다면, 모델은 배운 적 없는 상황을 만난다. 사소해 보이지만 파인튜닝이 "왜인지 실제로는 별로"인 이유의 절반이 여기다.

LoRA는 원본을 그대로 두고 옆길을 낸다

70억 파라미터 모델의 모든 가중치를 갱신하려면 가중치 자체보다 옵티마이저 상태가 더 무겁다. fp16 가중치가 14GB인데 Adam이 들고 있어야 할 1차·2차 모멘트가 fp32로 56GB, 기울기가 14GB, 합치면 80GB를 넘는다. 그래서 실무의 기본값은 원본을 얼려 두고 작은 행렬 둘만 학습하는 LoRA다.

W′=W+αrBAW' = W + \frac{\alpha}{r} BA

WW는 d×dd \times d 원본이고 AA는 d×rd \times r, BB는 r×dr \times d다. rr이 16이고 dd가 4,096이면 원본은 약 1,678만 개, 어댑터는 2×4096×16=13.12 \times 4096 \times 16 = 13.1만 개다. 0.78%만 학습한다는 뜻이다.

LoRA는 원본을 건드리지 않고 옆길을 하나 낸다

α/r\alpha/r은 옆길의 세기를 정하는 배율이다. 관례는 α=2r\alpha = 2r이고, 이 값을 유지하면 rr을 바꿔도 초반 학습 속도가 크게 안 흔들린다. rr은 8이나 16에서 시작한다. 올려서 좋아지는 경우는 생각보다 드물고, 올렸는데 좋아졌다면 대개 데이터가 어려운 것이 아니라 많은 것이다.

붙일 자리도 정해야 한다. 어텐션의 질의·값 투영에만 붙이는 것이 고전적인 최소 구성이고, 요즘은 선형 층 전부에 붙이는 쪽이 기본값에 가깝다. 좁은 분류 작업이면 최소 구성으로 충분하고, 말투나 형식처럼 전반적인 행동을 바꾸려면 전부에 붙이는 편이 잘 듣는다.

from peft import LoraConfig, get_peft_model

config = LoraConfig(
    r=16,
    lora_alpha=32,          # 관례대로 2r
    lora_dropout=0.05,
    target_modules="all-linear",
    task_type="CAUSAL_LM",
)
model = get_peft_model(base_model, config)
model.print_trainable_parameters()
# trainable: 20,971,520 / 7,262,703,616 (0.29%)
방식 7B 기준 필요 메모리 언제 쓰나
풀 파인튜닝 80GB 이상 도메인 자체가 다를 때, 데이터가 수십만 건일 때
LoRA (fp16 원본) 18~24GB 기본값
QLoRA (4비트 원본) 8~12GB GPU 한 장으로 끝내야 할 때

QLoRA는 원본을 4비트로 눌러 얼려 두고 어댑터만 fp16으로 학습한다. 소비자용 GPU 한 장에 7B가 들어가는 것이 이 방식의 존재 이유다. 다만 학습 속도는 LoRA보다 느리고, 눌러 둔 원본 위에서 학습하므로 나중에 fp16 원본에 어댑터를 합칠 때 결과가 미세하게 달라진다. 학습할 때 쓴 양자화 설정으로 서빙하는 편이 안전하다.

손댈 하이퍼파라미터는 셋뿐이다

돌려 볼 값이 스무 개처럼 보이지만 실제로 결과를 흔드는 것은 셋이다.

학습률은 LoRA 기준 1e-4에서 2e-4가 출발점이다. 풀 파인튜닝(1e-5 근처)보다 한 자릿수 크다 — 갱신하는 파라미터가 적으니 더 세게 밀어야 움직인다. 손실이 초반에 널뛰면 절반으로 줄인다.

에폭은 1에서 3이다. 데이터가 1,000건 수준이면 3, 수만 건이면 1로 시작한다. 4를 넘겨서 좋아지는 경우는 거의 못 봤고, 대신 학습 데이터에만 있던 문구를 그대로 뱉기 시작한다.

최대 길이는 비용을 정한다. 데이터의 99분위 길이로 자르고 그보다 긴 것은 버린다. 가장 긴 한 건에 맞추면 나머지 전부가 패딩 비용을 낸다.

과적합은 검증 손실이 오르는 것보다 출력이 짧고 단조로워지는 것으로 먼저 나타난다. 학습 데이터에서 자주 나온 문장 하나를 어떤 입력에도 붙이기 시작하면 이미 지났다고 보면 된다.

평가는 학습 전에 만든다

이 순서를 뒤집으면 파인튜닝은 되돌릴 수 없는 도박이 된다. 학습을 끝내고 나서 "좋아진 것 같은데"를 확인하려 하면, 비교할 기준선이 없어서 대개 좋아 보이는 쪽으로 결론이 난다.

학습을 시작하기 전에 셋을 만들어 둔다.

  1. 홀드아웃 묶음 — 학습에 안 쓴 100~300건. 여기 있는 입력이 학습 데이터에 섞이지 않았는지 실제로 확인한다. 같은 원본에서 잘라 냈다면 겹치기 쉽다.
  2. 기준선 숫자 — 파인튜닝 전 작은 모델과 지금 쓰는 큰 모델, 둘 다 이 묶음으로 재 둔다. 목표는 "좋아졌다"가 아니라 "큰 모델과의 격차가 몇 퍼센트포인트에서 몇 퍼센트포인트로 줄었다"다.
  3. 일반 능력 회귀 묶음 — 이 작업과 무관한 질문 50개. 좁은 데이터로 학습하면 그 밖의 능력이 함께 깎이는데, 작업 지표만 보면 이게 안 보인다. 지시를 따르는 능력, 한국어 문장력, 거절해야 할 것을 거절하는 능력이 그대로인지 훑는다.

세 번째를 빠뜨리면 "우리 분류는 완벽한데 사용자가 조금만 다르게 물으면 이상해지는 모델"이 나온다.

배포는 어댑터를 따로 두는 편이 낫다

학습이 끝나면 어댑터를 원본에 합쳐 하나의 모델로 만들 수도 있고, 원본 위에 얹은 채로 서빙할 수도 있다. 합치면 추론이 조금 빠르고, 따로 두면 같은 원본 하나로 여러 어댑터를 갈아 끼울 수 있으며 되돌리기가 파일 하나 바꾸는 일이 된다.

작업이 여럿이라면 후자가 거의 항상 낫다. 요청마다 다른 어댑터를 쓰는 서빙은 지금 흔한 기능이고, GPU 메모리에 원본을 한 번만 올려 둔 채 어댑터 수십 개를 얹을 수 있다. 되돌릴 경로를 파일 하나로 만들어 두는 것 자체가 배포 위험을 크게 줄인다.

재학습 주기는 데이터가 바뀌는 속도가 정한다. 분류 체계가 반년에 한 번 바뀌는 조직이면 반년마다면 되고, 그보다 자주 손대야 한다면 애초에 파인튜닝으로 굳힐 자리가 아니었을 가능성이 높다. 자주 바뀌는 것은 프롬프트나 검색 색인에 두고, 잘 안 바뀌는 것만 가중치에 굳힌다 — 이 경계를 지키는 것이 파인튜닝을 유지 가능한 선택지로 남겨 준다.


읽어주셔서 감사합니다. 😊

LATEST

LLM·트랜스포머의 최신 글

LLM·트랜스포머2026.08.13

작은 모델이 다시 쓸 만해진 이유

10억에서 100억 파라미터 사이의 모델이 다시 실무에 들어오고 있습니다. 무엇이 달라졌는지, 메모리는 어떻게 계산하는지, 무엇을 잘하고 무엇을 못하는지 정리합니다.

10 MIN
LLM·트랜스포머2026.08.06

형태는 맞는데 값이 틀릴 때

스키마를 통과한 출력에서 남는 의미 오류를 잡는 네 층의 검증, 오류를 되먹여 재시도하는 방법과 그 상한, 근거 인용을 스키마에 심어 자동 대조를 가능하게 하는 설계를 정리합니다.

11 MIN
LLM·트랜스포머2026.08.06

문법으로 출력을 제약하기

JSON Schema로는 표현되지 않는 출력 형태를 문맥자유문법으로 강제하는 방법, GBNF 문법을 쓸 때 자주 걸리는 좌재귀·공백·모호성 문제, 그리고 카탈로그에서 문법을 생성하는 실무 형태를 정리합니다.

11 MIN