홈강좌LAYA 결정 모델 24편 중 8편째
LAYA 학습 결과 읽기 — 좋아질 때, 그대로일 때, 속을 때
학습한 LAYA 를 평가 데이터로 다시 재면 팀 정확도가 0.55 에서 0.75 로 올랐다. 이 편은 그 결과를 읽고, 같은 방법으로 학습이 잘 안 되는 경우를 실제로 돌려 본다. 데이터가 적을 때, 라벨이 틀렸을 때, 끝단만 학습할 때, 너무 오래 학습할 때다. 학습 로그가 1.000 을 찍었는데 실제로는 0.6 대인 경우가 나오고, 같은 명령이 돌릴 때마다 0.1 씩 달라지는 것도 본다.
01 v1 을 팀 질문만 따로 잰다
7편의 표는 팀과 서두름을 합친 숫자였다. 5편의 evaluate.py 로 팀만 잰다.
python evaluate.py checkpoints/v1model: checkpoints/v1
accuracy: 0.750 (150/200)
account 0.800 (36/45)
billing 0.746 (47/63)
delivery 0.600 (27/45)
technical 0.851 (40/47)
mean confidence: 0.954
ECE: 0.207
most common mistakes: billing -> technical ×15, delivery -> technical ×10, account -> billing ×8
time: 40 ms per case| 학습 전(5편) | v1 | |
|---|---|---|
| 정확도 | 0.550 | 0.750 |
| account | 0.222 | 0.800 |
| billing | 0.397 | 0.746 |
| delivery | 0.622 | 0.600 |
| technical | 1.000 | 0.851 |
| 평균 확신 | 0.846 | 0.954 |
| ECE | 0.297 | 0.207 |
- 학습 전에는 거의 모든 문의를 기술팀으로 보냈다(technical 1.000 은 실력이 아니라 쏠림이었다). v1 은 그 쏠림이 풀려 계정·결제를 제대로 나누기 시작했다.
- 대신 기술팀은 0.851 로 내려갔고, 배송팀은 그대로다. 전체가 올라도 팀마다 오르내림이 다르다. 11편의 승격 기준에서 팀별 숫자를 따로 보는 이유다.
- 평균 확신 0.954 인데 정확도는 0.750 이다. 여전히 지나치게 확신한다(ECE 0.207). 7편의 보정 경고 그대로다.
02 결과 가능성 — 다섯 가지를 실제로 돌렸다
같은 바탕 모델(multilingual)에서 조건 하나씩만 바꿔 학습하고, 같은 평가 데이터로 쟀다. 7편의 v1 과 달리 아래 넷은 --eval 없이 돌렸다. 그러면 학습 로그는 학습용에서 떼어 낸 보정용 몇십 건으로 "전후" 를 잰다.
1. 데이터가 너무 적다 — 40건
laya-train --data first-40.jsonl --base multilingual --out checkpoints/few-40
python evaluate.py checkpoints/few-40학습 로그의 전후 표는 이렇다.
=== Calibration Evidence (in-sample calibration slice, 8 items; not held-out) ===
Metric Before After Delta
-----------------------------------------------------------------
Accuracy 0.7500 1.0000 +0.2500
Loss 0.9659 0.0924 -0.8735
ECE 0.2359 0.0834 -0.1525
Brier (multi-class) 0.4789 0.0300 -0.4489
Brier (top-1) 0.2394 0.0152 -0.2242
Mean Confidence 0.9859 0.9166 -0.0693 정확도 1.0000. 그런데 제목 줄이 스스로 말한다. 보정용 8건으로 잰 것이고(8 items), 학습 데이터와 같은 곳에서 나온 것이라 따로 떼어 둔 평가가 아니다(not held-out). 평가 데이터로 다시 재면 이렇다.
model: checkpoints/few-40
accuracy: 0.625 (125/200)
account 0.400 (18/45)
billing 0.952 (60/63)
delivery 0.644 (29/45)
technical 0.383 (18/47)
mean confidence: 0.904
ECE: 0.285
most common mistakes: technical -> billing ×17, account -> delivery ×14, account -> billing ×9
time: 40 ms per case실제는 0.625. 결제팀은 0.952 로 잘 맞히지만 기술팀은 0.383 이다. 40건 가운데 결제 문의가 많았던 쪽으로 쏠렸다. 적은 데이터는 "어느 팀이 많았나" 를 배운다.
2. 라벨이 틀렸다 — 150건 중 30% 를 아무 팀으로
한 건마다 30% 확률로 정답 팀을 아무 팀으로 바꿨다. 원래 팀이 다시 뽑히기도 하니, 실제로 틀린 라벨이 된 것은 41건(27%)이다(출력 첫 줄).
python make_noisy.py
laya-train --data noisy-150.jsonl --base multilingual --out checkpoints/noisy-150
python evaluate.py checkpoints/noisy-150labels changed: 41/150epoch 1/4 mean loss 1.0897
epoch 2/4 mean loss 0.4150
epoch 3/4 mean loss 0.3039
epoch 4/4 mean loss 0.4188손실이 1.0897 에서 0.3~0.4 대에서 더 내려가지 않는다. 서로 어긋나는 정답을 동시에 맞힐 수는 없기 때문이다. 손실이 일찍 멈추면 라벨부터 의심한다.
model: checkpoints/noisy-150
accuracy: 0.545 (109/200)
account 0.400 (18/45)
billing 0.619 (39/63)
delivery 0.556 (25/45)
technical 0.574 (27/47)
mean confidence: 0.529
ECE: 0.063
most common mistakes: billing -> technical ×18, technical -> account ×18, delivery -> technical ×17
time: 37 ms per case정확도 0.545, 학습 전(0.550)과 같다. 흥미로운 것은 평균 확신이 0.529 로 떨어지고 ECE 가 0.063 으로 이 편에서 가장 좋다는 점이다. 모델이 "잘 모르겠다" 를 정직하게 말하게 됐지만, 쓸모는 없다. 보정 지표만 좋아졌다고 좋은 모델은 아니다.
3. 끝단만 학습 — --freeze-encoder
문장을 읽는 본체(인코더)는 그대로 두고 맨 끝의 판단 부분만 학습한다. 가볍고 빠르지만 효과가 작다고 공식 문서가 적고 있다.
laya-train --data first-150.jsonl --base multilingual --freeze-encoder --out checkpoints/frozen-150epoch 1/4 mean loss 0.7263
epoch 2/4 mean loss 0.6337
epoch 3/4 mean loss 0.6762
epoch 4/4 mean loss 0.6723model: checkpoints/frozen-150
accuracy: 0.560 (112/200)
account 0.244 (11/45)
billing 0.413 (26/63)
delivery 0.622 (28/45)
technical 1.000 (47/47)
mean confidence: 0.727
ECE: 0.187
most common mistakes: billing -> technical ×33, account -> technical ×23, delivery -> technical ×17
time: 39 ms per case손실이 0.6~0.7 에서 거의 움직이지 않았고, 정확도 0.560·팀별 숫자·틀린 방향(기술팀 쏠림)까지 학습 전과 거의 같다. "결제창 오류는 기술팀" 같은 규칙은 문장을 다르게 읽어야 배울 수 있는데, 읽는 부분을 얼려 두었기 때문이다. 메모리가 아주 작은 기계가 아니라면 맥북에서는 전체 학습이 낫다.
4. 오래 학습 — 12에폭
v1 은 4에폭이었다. 세 배로 늘려 본다.
laya-train --data first-150.jsonl --base multilingual --epochs 12 --out checkpoints/long-150
python evaluate.py checkpoints/long-150epoch 1/12 mean loss 0.3569
epoch 2/12 mean loss -0.0588
epoch 3/12 mean loss 0.0361
epoch 4/12 mean loss -0.0092
epoch 5/12 mean loss 0.0095
epoch 6/12 mean loss 0.0000
epoch 7/12 mean loss 0.0000
epoch 8/12 mean loss 0.0000
epoch 9/12 mean loss 0.0000
epoch 10/12 mean loss 0.0000
epoch 11/12 mean loss 0.0000
epoch 12/12 mean loss 0.00006에폭부터 0 — 학습 데이터를 다 외웠다
숫자로 보기
| 항목 | mean loss |
|---|---|
| 1 | 0.3569 |
| 2 | -0.0588 |
| 3 | 0.0361 |
| 4 | -0.0092 |
| 5 | 0.0095 |
| 6 | 0.0000 |
| 7 | 0.0000 |
| 8 | 0.0000 |
| 9 | 0.0000 |
| 10 | 0.0000 |
| 11 | 0.0000 |
| 12 | 0.0000 |
6에폭부터 손실이 0.0000 이다(중간의 음수는 7편에서 본 RLCD 의 특성이다). 학습 데이터를 완전히 외웠다. 학습 로그의 전후 표도 정확도 1.0000·ECE 0.0000 으로 완벽해 보인다.
Accuracy 0.8000 1.0000 +0.2000
Loss 0.7643 0.0000 -0.7643
ECE 0.2474 0.0000 -0.2474 평가 데이터로 재면 이렇다.
model: checkpoints/long-150
accuracy: 0.785 (157/200)
account 0.756 (34/45)
billing 0.952 (60/63)
delivery 0.622 (28/45)
technical 0.745 (35/47)
mean confidence: 0.978
ECE: 0.193
most common mistakes: technical -> account ×9, delivery -> technical ×9, delivery -> account ×8
time: 37 ms per case0.785 로 4에폭의 v1(0.750)보다 오히려 높다. 평균 확신은 0.978 로 v1(0.954)보다 더 확신하지만, ECE 는 0.193 으로 v1(0.207)과 비슷하다. 그럼 12에폭이 더 좋은 걸까.
5. 한 번 잰 숫자는 흔들린다 — 같은 학습을 세 번씩
이 글을 쓰며 처음 12에폭을 돌렸을 때는 0.660 이 나왔다. "오래 학습해 나빠졌다" 고 쓸 뻔했는데, 같은 명령을 다시 돌리니 위의 0.785 였다. 맥 GPU(MPS)의 학습은 매번 완전히 같은 결과를 보장하지 않고, 150건뿐인 학습에서는 그 작은 차이가 정확도 몇 점으로 커진다. 그래서 4에폭과 12에폭을 씨앗(seed)만 바꿔 세 번씩 학습했다.
"""4에폭과 12에폭을 씨앗만 바꿔 세 번씩 학습해 비교한다 — 한 번 돌린 숫자로 '과적합' 을 말할 수 있나."""
import json
import shutil
import statistics
import subprocess
import laya
cases = [json.loads(line) for line in open("tickets-eval.jsonl", encoding="utf-8")]
def team_accuracy(path):
agent = laya.load(path)
return sum(agent.predict(c["state"], c["questions"])["answers"]["team"]["choice"] == c["expected"]["team"]
for c in cases) / len(cases)
print(f"{'epochs':<10}{'seed 1':>8}{'seed 2':>8}{'seed 3':>8}{'mean':>8}{'range':>8}")
for epochs in [4, 12]:
scores = []
for seed in [1, 2, 3]:
out = f"checkpoints/epochs-{epochs}-{seed}"
subprocess.run(["laya-train", "--data", "first-150.jsonl", "--base", "multilingual", "--epochs", str(epochs),
"--seed", str(seed), "--out", out], check=True, capture_output=True)
scores.append(team_accuracy(out))
shutil.rmtree(out)
cells = "".join(f"{s:>8.3f}" for s in scores)
print(f"{epochs:<10}{cells}{statistics.mean(scores):>8.3f}{max(scores) - min(scores):>8.3f}", flush=True)python repeat_epochs.py칸은 차례로 에폭, 씨앗 1·2·3 의 팀 정확도, 평균, 폭(가장 높은 것 - 가장 낮은 것)이다.
epochs seed 1 seed 2 seed 3 mean range
4 0.800 0.790 0.705 0.765 0.095
12 0.765 0.730 0.845 0.780 0.115- 씨앗 1·2·3
- 평균
- 최소~최대
평균 차이 0.015 가 같은 에폭 안의 폭(0.095·0.115)보다 작다
숫자로 보기
| 항목 | 측정값 | 평균 |
|---|---|---|
| 4에폭 | 0.800, 0.790, 0.705 | 0.765 |
| 12에폭 | 0.765, 0.730, 0.845 | 0.780 |
- 같은 4에폭이 0.705 에서 0.800 까지 나왔다. 7편의 v1(0.750)도 이 범위 안의 한 번이다.
- 평균은 4에폭 0.765, 12에폭 0.780 으로 차이가 0.015 다. 같은 에폭 안의 폭(0.095·0.115)보다 훨씬 작다. 이 데이터에서는 12에폭이 낫다고도, 나쁘다고도 말할 수 없다. 확실한 것은 학습 시간이 세 배라는 것뿐이다.
학습 데이터를 외워 새 데이터에서 나빠지는 것을 과적합(overfitting) 이라 한다. 데이터가 더 적거나 에폭이 훨씬 길면 실제로 생긴다. 하지만 손실 0 과 학습 로그의 1.000 은 과적합의 신호일 뿐이고, 판단은 여러 번 재서 평균이 떨어질 때 한다. 한 번 잰 0.660 이나 0.785 로 결론을 냈다면 둘 다 틀렸다. 이 시리즈는 에폭을 기본값 4 로 두고, 판을 비교할 때는 여러 번 재서 평균과 폭을 본다(10편).
03 정리 — 결과별로 읽는 법
| 결과 | 로그에서 보이는 것 | 평가에서 보이는 것 | 다음 수 |
|---|---|---|---|
| 좋아짐(v1) | 손실이 크게 준다 | 정확도가 오르고 팀 쏠림이 풀린다 | 지속 학습으로 간다(9편) |
| 데이터 부족 | 보정 건수 경고, 학습 로그 1.000 | 많은 팀으로 쏠린다 | 적은 팀의 데이터를 더 모은다 |
| 라벨 오류 | 손실이 일찍 멈춘다 | 학습 전과 같고 확신만 낮다 | 라벨을 다시 본다 |
| 끝단만 학습 | 손실이 거의 그대로 | 학습 전과 같다 | 전체 학습으로 |
| 오래 학습 | 손실 0, 학습 로그 1.000 | 한 번은 높게, 한 번은 낮게 나온다 | 세 번 재서 평균으로 본다 |
| 결과 흔들림 | 같은 명령, 다른 손실 | 같은 설정이 0.1 씩 달라진다 | 씨앗을 바꿔 여러 번 잰다 |
모든 경우에 학습 로그의 숫자는 평가 데이터의 숫자와 달랐다. 학습 로그의 전후 표는 학습용에서 떼어 낸 수십 건으로 잰 것이라 믿을 수 없다. 학습용과 겹치지 않는, 고정된 평가 데이터로 재는 것이 유일한 판단 근거다. 이 평가 데이터는 다음 편부터 판을 바꿀 때마다 같은 것을 쓴다.
04 실습 — 내 데이터에서 해 볼 것
- 평가 데이터를 먼저 떼어 두고, 학습할 때마다
--eval을 붙인다. - 데이터 양을 반으로 줄여 한 번 더 학습해 본다. 정확도가 크게 떨어지면 데이터를 더 모을수록 좋아질 여지가 크다는 뜻이다.
- 에폭을 2·4·8 로 바꿔 평가 정확도가 가장 높은 곳을 찾는다. 설정마다 씨앗을 바꿔 세 번씩 재고, 평균의 차이가 폭보다 클 때만 "낫다" 고 본다.
v1 은 첫 판으로 쓸 만하지만 여전히 지나치게 확신한다. 다음 편에서 v1 을 운영에 넣고, 사람이 고친 판단을 모아 다음 판의 재료로 쓴다.