hyunsu blog

홈강좌LAYA 결정 모델 24편 중 16편째

LAYA 돌아보기 — 내 모델과 지속 학습은 정답률을 올렸나, 결과 한 장과 권장 순서

목차 · 5
  1. 01한자리에서 다시 잰 결과
  2. 02지속 학습은 도움이 됐나
  3. 03오르지 않는 곳
  4. 04권장 — 어떻게 학습하고 이어 갈까
  5. 05실습

6~11편에서 문의 분류 모델을 만들고 고쳐 왔다. 숫자가 편마다 흩어져 있어, 이 편에서 판 여섯 개를 같은 날 같은 코드로 다시 잰다. 결론부터 말하면 지속 학습은 정답률을 올렸다. 학습 전 0.550 이 처음 150건으로 0.750, 운영에서 모은 정답 90건을 더해 바탕 모델에서 처음부터 다시 학습한 v2 가 0.815 다. 같은 정답을 v1 에 이어서 학습한 판들은 오르지 않았다.

01 한자리에서 다시 잰 결과

판마다 사람이 정답을 붙인 건수도 함께 적었다. 정확도만 보면 비용이 안 보인다.

python
"""문의 분류 사례의 판을 한자리에서 다시 잰다 — 같은 평가 세트(200건), 같은 날, 같은 코드.

판마다 사람이 정답을 붙인 건수도 함께 적는다. 정확도 하나만 보면 비용이 안 보인다.
"""
import json

import numpy as np
import laya
from laya.common import ece_score

STAGES = [   # (이름, 경로, 사람이 붙인 정답 수, 설명)
    ("base", None, 0, "학습 전 다국어 체크포인트"),
    ("v1", "checkpoints/v1", 150, "처음 150건으로 학습(7편)"),
    ("v2", "checkpoints/v2", 240, "기본 모델에서 누적 240건 다시 학습, 승격(11편)"),
    ("v2a", "checkpoints/v2a", 240, "v1 에 새 90건만 이어 학습(10편)"),
    ("v2b", "checkpoints/v2b", 240, "v1 에 누적 240건을 이어 학습, 거부(11편)"),
    ("more-600", "checkpoints/more-600", 600, "사람이 600건을 붙였다면(10편)"),
]
cases = [json.loads(line) for line in open("tickets-eval.jsonl", encoding="utf-8")]

print(f"{'stage':<10}{'labels':>7}{'accuracy':>10}{'ECE':>7}{'worst team':>18}  note")
for name, path, labels, note in STAGES:
    agent = laya.load("convaiinnovations/laya", subfolder="multilingual") if path is None else laya.load(path)
    correct, conf, per_team = [], [], {}
    for c in cases:
        a = agent.predict(c["state"], c["questions"])["answers"]["team"]
        ok = a["choice"] == c["expected"]["team"]
        correct.append(ok)
        conf.append(a["probabilities"][a["choice"]])
        per_team.setdefault(c["expected"]["team"], []).append(ok)
    correct = np.array(correct, dtype=float)
    worst = min(per_team, key=lambda t: sum(per_team[t]) / len(per_team[t]))
    worst_acc = sum(per_team[worst]) / len(per_team[worst])
    print(f"{name:<10}{labels:>7}{correct.mean():>10.3f}{ece_score(np.array(conf), correct, bins=10):>7.3f}"
          f"{worst:>11} {worst_acc:.3f}  {note}")
bash
python journey.py
text
stage      labels  accuracy    ECE        worst team  note
base            0     0.550  0.297    account 0.222  학습 전 다국어 체크포인트
v1            150     0.750  0.207   delivery 0.600  처음 150건으로 학습(7편)
v2            240     0.815  0.136   delivery 0.622  기본 모델에서 누적 240건 다시 학습, 승격(11편)
v2a           240     0.730  0.249   delivery 0.622  v1 에 새 90건만 이어 학습(10편)
v2b           240     0.735  0.237  technical 0.468  v1 에 누적 240건을 이어 학습, 거부(11편)
more-600      600     0.800  0.183   delivery 0.622  사람이 600건을 붙였다면(10편)
그림 1.판별 정확도 — 같은 평가 데이터 200건
0.00.20.40.60.81.0학습 전v1v2a 이어v2b 이어v2 다시600건학습 전: 0.550v1: 0.750v2a 이어: 0.730v2b 이어: 0.735v2 다시: 0.815600건: 0.8000.5500.7500.7300.7350.8150.8000.00.20.40.60.81.0학습 전v1v2a 이어v2b 이어v2 다시600건학습 전: 0.550v1: 0.750v2a 이어: 0.730v2b 이어: 0.735v2 다시: 0.815600건: 0.8000.5500.7500.7300.7350.8150.800

v2 가 운영에 올린 판. v2a·v2b 는 같은 정답을 v1 에 이어 학습했다

숫자로 보기
항목팀 정확도
학습 전0.550
v10.750
v2a 이어0.730
v2b 이어0.735
v2 다시0.815
600건0.800
  • base → v1: 내 데이터 150건으로 +0.20. 이 사례에서 가장 큰 한 걸음이다(7·8편).
  • v1 → v2: 운영 정답 90건을 더해 처음부터 다시 학습하니 +0.065. 11편의 승격 기준을 넘었다.
  • v2a·v2b: 같은 90건을 v1 에 이어 학습한 판이다. v2a 는 v1 보다 낮고, v2b 는 기술팀이 무너졌다.
  • more-600: 처음 150건과 같은 문장 틀로 600건을 붙이면 0.800 이다. 새 표현이 든 240건(v2)과 비슷하거나 낮다.

이 표의 숫자는 판마다 한 번 학습한 결과다. 맥 GPU 학습은 같은 명령도 씨앗에 따라 ±0.1 쯤 흔들려서(8·10편), 방법끼리는 세 번씩 돌린 평균으로 비교했다.

그림 2.v2 만드는 방법별 — 세 번 평균
0.600.650.700.750.800.850.90v1 다시새 90건 이어240건 이어240건 다시v1 다시: 0.765새 90건 이어: 0.720240건 이어: 0.750240건 다시: 0.8250.7650.7200.7500.8250.60.70.80.9v1 다시새 90건 이어240건 이어240건 다시v1 다시: 0.765새 90건 이어: 0.720240건 이어: 0.750240건 다시: 0.8250.7650.7200.7500.825

씨앗 1·2·3 평균. 이어 학습한 두 방법은 v1 을 다시 학습한 것보다 낮다(10편)

숫자로 보기
항목평균 정확도
v1 다시0.765
새 90건 이어0.720
240건 이어0.750
240건 다시0.825

02 지속 학습은 도움이 됐나

질문답근거
내 데이터로 학습하면 오르나예 — 0.550 → 0.7507·8편
운영 정답을 더하면 오르나예, 처음부터 다시 학습할 때 — 세 번 평균 0.765 → 0.82510편
v1 에 새 정답만 이어 학습하면아니오 — 세 번 평균 0.720, 오히려 내려감10편
v1 에 누적 정답을 이어 학습하면운에 달림 — 0.635 ~ 0.83510편
같은 종류 데이터를 더 붙이면조금 — 600건 0.800, 새 표현 90건을 더한 쪽과 비슷10편
에폭을 늘리면차이 없음 — 4에폭 0.765 · 12에폭 0.780, 흔들림 안8편
정답이 틀리면망가짐 — 라벨 27% 틀림 0.545, 학습 전 수준8편

정리하면 정답률을 올린 것은 새 표현이 든 정확한 정답과 처음부터 다시 하는 학습이다. 데이터 양, 에폭 수, 이어 학습은 그만큼 효과가 없었다.

비용도 같이 본다. v2 의 90건은 운영 문의 300건 가운데 사람이 30% 를 직접 보거나 다시 확인해서 모았다(9편). 문턱을 낮춰 사람 일을 12% 로 줄이면 모인 정답이 36건으로 줄었다. 사람 손을 덜 쓰는 만큼 모델이 배울 재료가 준다.

03 오르지 않는 곳

배송팀은 어느 판에서도 0.6 언저리다. 평가용 배송 문장에 "주소를 잘못 적었어요" 처럼 학습·운영 어디에도 비슷한 표현이 없는 것이 섞여 있다. 정답을 더 모아도 그런 표현이 들어오지 않으면 오르지 않는다. 이런 팀은 두 가지를 본다.

  • 그 팀의 표본 검사를 늘려 그 표현이 실제로 들어오는지 본다.
  • 질문의 criteria 문구가 그 팀을 충분히 설명하는지 본다. "배송·교환·반품" 에 "주소 변경" 이 없으면 모델은 그것을 배송으로 볼 근거가 없다(4편).

04 권장 — 어떻게 학습하고 이어 갈까

이 사례에서 확인한 것을 순서로 정리한다.

  1. 평가 데이터를 먼저 고정한다. 학습에 쓰지 않을 200건 이상을, 학습 데이터와 다른 표현으로 만든다. 승격 기준도 이때 정한다(11편: 전체 +0.02 · 어느 팀도 -0.05 넘게 떨어지지 않기 · ECE 0.25 이하).
  2. 첫 판은 150건 이상, 정확한 정답으로. 40건은 0.625 에 그쳤다. 틀린 정답이 섞이면 학습 전보다 나빠질 수 있으니, 양보다 정답 검수가 먼저다.
  3. 설정은 기본값(4에폭)으로 두고 씨앗을 바꿔 세 번 돌린다. 에폭·학습률을 만지기 전에 흔들림의 폭부터 본다.
  4. 운영에서는 확신 문턱과 표본 검사로 정답을 모은다. 사람이 확인하거나 고친 것만 정답으로 쓴다(9편: 문턱 0.99 · 자동 처리분 30% 표본).
  5. 새 정답이 100건쯤 쌓이면, 바탕 모델에서 누적 전체로 다시 학습한다. 이어 학습은 데이터가 너무 커서 처음부터가 오래 걸릴 때만, 옛 데이터를 섞고 세 번 돌려 폭을 본 뒤에 쓴다.
  6. 고정 평가 데이터로 판정하고, 그림자 비교 뒤에 바꾼다. 앞 판의 이미지를 지우지 않고 두어 되돌릴 수 있게 한다(14·15편).
  7. 오르지 않는 팀은 데이터 대신 질문을 고친다. 질문을 바꾸면 평가 데이터의 질문도 같이 바꾸고, 지금 판을 새 질문으로 다시 재서 기준점을 새로 잡는다.

주기는 "새 정답 100건 또는 한 달 중 먼저 오는 것" 이 무난하다. 이 사례에서 300건 처리에 정답 90건이 모였으니, 하루 100건이 들어오는 서비스라면 열흘쯤이다. LAYA 는 작아서 240건 학습이 맥북에서 몇 분이면 끝나므로, 처음부터 다시 학습해도 부담이 적다.

05 실습

  1. journey.py 에 8편의 long-150(12에폭)과 noisy-150(라벨 27% 틀림)을 더해, 같은 표에서 비교해 본다.
  2. 내 서비스라면 1단계의 평가 데이터 200건을 어디서 구할지 적어 본다. 학습 데이터를 만든 사람과 다른 사람이 만들면 더 좋다.
  3. 다음 편부터는 다른 사례(쇼핑몰 검색어를 필터로 바꾸기)로 같은 과정을 한 번 더 한다. 이 편의 권장 순서가 그 사례에서도 맞는지 본다.