모델 운영

MLOPS / 82번째 글

학습 중 평가 — 손실 곡선만 보고 체크포인트를 고를 수 없다

검증 손실이 가장 낮은 체크포인트가 가장 좋은 체크포인트인 경우는 생각보다 드뭅니다. 왜 두 지표가 어긋나는지, 평가를 어떤 층으로 나눠 돌려야 학습이 느려지지 않는지 정리합니다.

PALDYN Team12 MIN READ

지난 글에서 학습 중에 보존 세트를 함께 재야 한다고 적었다. 그러면 바로 다음 질문이 온다. 얼마나 자주 재야 하고, 그 결과로 무엇을 결정하는가.

학습 스크립트가 기본으로 내주는 것은 검증 손실 하나다. 그리고 대부분의 라이브러리가 「검증 손실이 가장 낮은 체크포인트를 저장」을 기본값으로 들고 있다. 이 기본값이 맞는 경우는 생각보다 드물다.

검증 손실이 실제로 재는 것

검증 손실은 이런 값이다.

Lval=−1N∑i=1Nlog⁡pθ(yi∣xi,y<i)\mathcal{L}_{\text{val}} = -\frac{1}{N}\sum_{i=1}^{N} \log p_\theta(y_i \mid x_i, y_{<i})

마지막 조건 y<iy_{<i}가 핵심이다. 각 토큰을 예측할 때 그 앞의 토큰들은 정답이 그대로 주어진다. 이것을 교사 강요(teacher forcing)라 부른다 — 모델은 매 자리에서 「정답까지 다 맞은 상태에서 다음 한 글자」만 맞히면 된다.

그런데 서비스에서 이 모델이 하는 일은 그것이 아니다. 첫 토큰을 자기가 고르고, 그 위에 두 번째를 고르고, 자기가 만든 문맥 위에서 계속 이어 간다. 100번째 토큰을 낼 때 앞의 99개는 정답이 아니라 자기 출력이다.

두 상황이 다르니 두 숫자가 다른 방향으로 움직일 수 있다. 실제로 자주 벌어지는 어긋남은 이런 것들이다.

  • 손실은 내려가는데 긴 출력이 무너진다. 짧은 문맥에서의 다음 토큰 예측은 정확해졌지만, 자기 출력을 쌓아 올릴 때의 안정성은 손실이 재지 않는다
  • 손실은 내려가는데 형식이 깨진다. JSON 중괄호 하나를 틀리면 그 응답은 못 쓰는데, 손실에서는 토큰 하나의 작은 벌점일 뿐이다
  • 손실은 그대로인데 점수가 오른다. 정답과 다른 표현으로 맞게 답하는 경우가 늘면 손실은 안 내려간다

손실이 가장 낮은 체크포인트가 가장 좋은 체크포인트는 아니다

무엇으로 고를 것인가

원칙은 하나로 정리된다. 체크포인트를 고르는 지표는 배포를 결정하는 지표와 같아야 한다.

배포 결정을 골든셋 점수와 회귀 건수로 한다면, 체크포인트도 그것으로 골라야 한다. 손실로 고른 뒤에 골든셋으로 검사하는 순서는 순서가 뒤집힌 것이다 — 손실이 이미 후보를 좁혀 버렸기 때문에, 골든셋 점수가 더 좋은 체크포인트가 후보에 없을 수 있다.

실무에서 쓸 만한 선택 규칙은 대체로 이 모양이다.

규칙 내용 언제
단일 지표 최대 골든셋 점수가 가장 높은 체크포인트 목표 과제가 하나이고 보존 위험이 낮을 때
문턱 + 최대 보존 점수가 기준 이상인 것 중에서 골든셋 최고 대부분의 경우
가중합 목표와 보존에 가중치를 주고 합산 두 축의 상대 가치를 숫자로 적을 수 있을 때

가운데 것이 기본값으로 좋다. 「보존 점수가 베이스 대비 2점 이상 떨어지지 않은 체크포인트 중에서 골든셋 최고」처럼 적으면, 망각을 허용 범위로 묶어 두고 그 안에서 최대를 찾는다. 가중합은 가중치를 정할 근거가 대개 없어서 논쟁만 늘어난다.

그리고 고른 체크포인트가 마지막 것이면 한 번 의심한다. 학습이 아직 안 끝난 것일 수도 있고, 평가가 개선을 못 잡고 있는 것일 수도 있다.

평가를 층으로 나눈다

골든셋 100문제를 매 스텝 돌릴 수는 없다. 생성이 필요하고, 생성은 학습 한 걸음보다 훨씬 비싸다. 그렇다고 학습이 끝난 뒤에 한 번만 재면 중간에 무슨 일이 있었는지 알 수 없다.

답은 하나를 고르는 것이 아니라 층을 나누는 것이다.

평가를 한 종류만 두면 자주 못 돌거나 아무것도 못 잡는다

층 주기 비용 무엇을 잡나
검증 손실 매 50~100스텝 1초 미만 학습이 망가졌는지 — 발산, NaN, 과적합 시작
골든셋 소량 매 500스텝 2~4분 실제 생성 품질의 추세, 형식 붕괴
전체 스위트 에폭 끝 30분 이상 배포 판단 — 보존·안전·사람 검수

맨 위 층은 거르는 용도이지 고르는 용도가 아니다. 손실이 튀거나 NaN이 나오면 학습을 즉시 죽여 GPU 시간을 아낀다. 체크포인트 선택은 아래 두 층이 한다.

가운데 층에서는 골든셋 전체가 아니라 고정된 부분집합을 쓴다. 30~50문제면 추세를 보기에 충분하고, 중요한 것은 매번 같은 문제를 쓰는 것이다. 매번 다른 표본을 뽑으면 점수의 오르내림 중 어디까지가 모델 변화이고 어디까지가 표본 교체인지 알 수 없다.

평가가 학습을 잡아먹지 않게

학습 중 평가에서 가장 흔한 실수는 학습 프로세스 안에서 생성을 돌리는 것이다. 그 몇 분 동안 GPU는 학습을 멈추고 있고, 그것이 500스텝마다 반복된다.

비용을 실제로 세어 보면 결정이 쉬워진다. 500스텝이 15분이고 평가가 4분이면 학습 시간의 21%가 평가에 간다. 8시간 학습이 10시간이 된다.

줄이는 방법은 셋이다.

  • 체크포인트만 저장하고 평가는 다른 프로세스에 맡긴다. 저장된 어댑터를 별도 GPU의 추론 서버가 집어 가서 평가하고 결과만 학습 로그에 되돌려 준다. 학습은 멈추지 않는다
  • 평가용 생성을 배치로 돌린다. 50문제를 하나씩 생성하면 대부분의 시간이 GPU를 놀리는 데 쓰인다. 한 번에 묶어 넣으면 몇 배 빨라진다
  • 최대 생성 길이를 자른다. 형식과 방향을 보는 데는 256토큰이면 대개 충분하다. 학습 중 평가에서 2,048토큰까지 뽑을 이유가 없다

첫 번째가 구조적으로 가장 낫지만 GPU가 한 장뿐이면 쓸 수 없다. 그럴 때는 나머지 둘로 4분을 1분 근처까지 줄이고 주기를 늘린다.

자주 새는 곳

학습 중 평가는 조용히 틀리기 쉽다. 결과가 그럴듯한 숫자로 나오기 때문이다.

  • 검증셋이 학습셋과 겹친다. 같은 원본에서 뽑은 데이터를 무작위로 나누면 거의 같은 문장이 양쪽에 들어간다. 문서 단위·고객 단위로 나눠야 한다(오염 글에서 다룬 문제가 그대로 재현된다)
  • 평가 프롬프트가 서비스 프롬프트와 다르다. 시스템 프롬프트나 채팅 템플릿이 한 글자만 달라도 점수가 몇 점씩 움직인다. 학습 중 평가일수록 실제 호출 경로와 같은 템플릿을 쓴다
  • 샘플링이 켜져 있다. 온도가 0이 아니면 같은 체크포인트를 두 번 재도 점수가 다르다. 학습 중 비교에는 탐욕 디코딩으로 고정한다
  • 표본이 너무 작다. 30문제에서 2점 차이는 아무 의미가 없다. 추세로만 읽고, 최종 선택은 표본과 유의성을 따진 뒤에 한다

조기 종료는 어디에 거는가

조기 종료(early stopping)는 개선이 멈추면 학습을 끊는 장치다. 여기서도 같은 원칙이 적용된다 — 손실이 아니라 고를 때 쓰는 지표에 건다.

# 손실이 아니라 골든셋 점수에 건다. 방향도 함께 뒤집는다.
TrainingArguments(
    eval_strategy="steps",
    eval_steps=500,
    load_best_model_at_end=True,
    metric_for_best_model="golden_score",
    greater_is_better=True,
)

인내 구간(patience)은 넉넉하게 잡는다. 생성 기반 점수는 손실보다 훨씬 울퉁불퉁해서, 한 번 떨어졌다가 다음 평가에서 회복하는 일이 흔하다. 평가 3~5회 연속 개선이 없을 때 끊는 정도가 무난하다.

그리고 끊긴 지점이 아니라 가장 좋았던 지점을 저장한다. 인내 구간 동안 계속 저장하고 있어야 되돌아갈 수 있다.

정리

학습 중 평가는 「돌아가는지 보는 것」이 아니라 무엇을 배포할지 고르는 절차다. 그렇게 보면 설계가 정해진다.

  • 검증 손실은 교사 강요 아래의 다음 토큰 예측을 재고, 서비스는 자기 출력 위에서 생성한다. 두 숫자가 어긋나는 것이 정상이다
  • 체크포인트를 고르는 지표는 배포를 결정하는 지표와 같아야 한다. 보존 점수에 문턱을 두고 그 안에서 목표 점수 최대가 무난한 기본값이다
  • 평가는 한 종류가 아니라 세 층이다 — 매 스텝 거르기, 매 500스텝 추세, 에폭 끝 판단
  • 평가로 GPU를 멈추지 않는다. 가능하면 다른 프로세스로 넘기고, 안 되면 배치와 길이를 줄인다

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

LATEST

모델 운영의 최신 글

모델 운영2026.09.04

KV 캐시 양자화 — 가중치보다 이쪽이 먼저 넘친다

긴 문맥에서 GPU 메모리를 실제로 잡아먹는 것은 가중치가 아니라 KV 캐시입니다. 캐시 크기를 계산하는 법, K와 V를 다르게 다뤄야 하는 이유, 어디까지 줄여도 되는지를 정리합니다.

16 MIN
모델 운영2026.09.04

양자화 보정 — 데이터 128개가 모델 품질을 정한다

양자화에서 스케일을 정하는 절차가 보정입니다. 무엇을 재는지, 데이터를 어디서 몇 개 뽑아야 하는지, 자르는 지점을 어떻게 고르는지, 그리고 잘못된 보정이 어떤 모양으로 드러나는지를 정리합니다.

21 MIN
모델 운영2026.09.03

과제 특화 증류 — 큰 모델의 답을 작은 모델에 옮긴다

범용 성능이 아니라 우리 과제 하나만 잘하는 작은 모델을 만드는 방법입니다. 교사에게 무엇을 받아야 하는지, 데이터를 어떻게 모으고 거르는지, 손익 분기가 어디인지를 정리합니다.

15 MIN