SFT 결과가 이상할 때 — holdout·스트레스·loss로 먼저 가르기

TL;DR

  • SFT 점수가 “안 좋다”만으로는 원인이 안 보인다. 시험지를 둘로 쪼개고, train_loss를 같이 본다.
  • 깨끗 holdout은 스키마·형식 충실도, 스트레스셋(지저분·혼용·영어·수치)은 실사용에 가깝다.
  • 버전마다 1위가 시험지별로 갈리면, 하나를 바로 폐기하지 말고 어떤 분포에서 이긴 건지를 적는다.
  • train_loss가 이례적으로 낮으면(예: 직전 대비 한 자릿수 하락) 과적합을 먼저 의심한다.
  • 원인 모르면 데이터·프롬프트·하이퍼를 동시에 바꾸지 않는다.

들어가며

SFT를 몇 바퀴 돌리면 언젠가 이런 날이 온다.
“이번 버전이 이상하다. 점수가 떨어졌다.”

그런데 “이상하다”는 진단이 아니다.
깨끗하게 쓴 시험지에선 괜찮은데 지저분한 입력에선 무너질 수도 있고, 그 반대일 수도 있다.
이 글은 자유서술 → JSON 구조화 추출 같은 SFT를 개인 실험으로 돌릴 때, 점수가 흔들리면 무엇부터 가르는지 체크리스트다.

(앞선 Few-shot vs SFT, Q-LoRA 시드다음 편에 해당한다.)


1. 시험지를 둘로 쪼개기

채점 세트를 하나만 쓰면, 그 세트의 버릇을 “모델 실력”으로 착각하기 쉽다.

무엇을 보나 함정
깨끗 holdout 스키마 준수, type/role/slot, JSON 유효 학습 분포와 너무 비슷하면 점수 부풀림
스트레스셋 지저분 메모, 약어·언어 혼용, 완전 영어, 수치 임계값 케이스 수가 적으면 방향성만 본다

실사용이 지저분한 입력이라면, 스트레스 1위 ≠ holdout 1위인 상황이 정상일 수 있다.
둘 다 보고 “어느 분포의 챔피언인지”를 적는다.


2. 버전이 시험지마다 1위가 갈릴 때

가상의 숫자 예시다. (숫자는 설명용.)

모델 깨끗 holdout F1 스트레스 내용 F1 해석
vA 0.95 0.66 형식은 강함, 실사용 약함
vB 0.90 0.75 실사용 후보
vC 0.88 0.63 둘 다 애매 — 승격 보류

여기서 vC를 “최신 스키마라서” 바로 올리면 안 된다.
스키마가 맞다 ≠ 일반화가 됐다.


3. train_loss 이상 신호

신호 의심
loss가 직전 대비 급락 (예: 0.15 → 0.004) 데이터가 너무 규칙적 / 외움
train은 좋은데 holdout·스트레스 동시 하락 과적합 가능성 큼
train·holdout만 좋고 스트레스만 나쁨 분포 시프트 (영어 샘플 부재 등)

Loss 하나만으로 확정하지 않는다.
다만 “이례적으로 낮은 loss + 외부 세트 동시 하락”이면, 데이터 더 넣기 전에 과적합·자기증류 천장부터 의심하는 편이 싸다.


4. 해부 체크리스트 (재현용)

  1. 같은 채점기로 후보 버전을 다시 돌렸는가? (구 채점기 숫자와 섞지 않기)
  2. 깨끗 / 스트레스를 표로 나란히 적었는가?
  3. 카테고리별(영어·수치·장문)로 깨지는 축이 보이는가?
  4. train_loss·epoch·데이터 규모를 버전에 붙였는가?
  5. 실패 케이스 3~5개를 입력 / 기대 / 출력으로 펼쳐 봤는가?
  6. 그 실패가 데이터 · 프롬프트 · 채점 계약 · 학습 중 어디에 가까운가? (다음 글 #2, #3)

5. 다음에 손대지 말 것

하지 말 것 이유
원인 모호한 채 데이터+프롬프트+lr 동시 변경 무엇이 먹혔는지 모름
깨끗 점수만 보고 승격 실사용 분포와 어긋날 수 있음
“최신 스키마”만으로 채택 스키마 ≠ 품질
스트레스 소수 케이스로 절대 순위 확정 방향성 지표로만

마치며

SFT 실패 해부의 첫 단계는 모델을 탓하기 전에 시험지와 loss를 가르는 것이다.
다음 글에서는 학습 라벨의 slot 축이 어떻게 추론을 다시 오염시키는지 본다.


시리즈

  1. SFT 결과가 이상할 때 — holdout·스트레스·loss로 먼저 가르기 (이번 글)
  2. 학습 라벨의 slot 축이 추론을 다시 발명한다
  3. 모델은 프롬프트를 지켰는데 점수가 깎일 때

Comments