홈강좌LAYA 결정 모델 24편 중 9편째
LAYA 지속 학습 1 — 운영에서 사람이 고친 판단을 학습 데이터로 모은다
지속 학습의 재료는 운영에서 사람이 고친 판단이다. 이 편은 v1 이 새 문의 300건을 처리하는 3주를 흉내 낸다. 확신이 낮은 것은 사람이 직접 분류하고, 높은 것은 자동으로 보내되 일부를 사람이 다시 본다. 사람이 본 것은 모두 정답이 붙으니 다음 판의 학습 데이터가 된다. 결과는 생각보다 나빴다. v1 은 확신 0.99 이상에서도 9.4% 를 틀렸다.
01 지속 학습의 고리
- v1 이 문의를 판단한다.
- 확신이 높으면 자동 처리하고, 그중 일부를 사람이 다시 본다(표본 검사).
- 확신이 낮으면 사람이 직접 분류한다(검토 큐).
- 사람이 본 것은 모두 정답이 붙어 학습 데이터(feedback)로 쌓인다.
- 이전 데이터와 합쳐 v2 를 학습한다(10편).
- 승격 판정을 통과하면 배포한다(11~15편). 그리고 다시 1번으로.
사람이 보는 곳은 둘이다.
- 검토 큐 — 확신이 문턱보다 낮은 판단. 모델이 자신 없어 하니 사람이 정한다.
- 표본 검사 — 확신이 높아 자동 처리한 것 가운데 일부를 무작위로 다시 본다. 모델이 자신 있게 틀리는 것은 이렇게만 찾을 수 있다.
02 운영에서 들어오는 문의
6편에서 만든 incoming-300.jsonl 은 처음 150건과 겹치지 않는 문의 300건이다. 70% 는 학습 때 없던 새 표현(ops 문장 틀)이고 30% 는 익숙한 표현이다. 처음 몇 건은 이렇다.
| 문의 | 정답 |
|---|---|
| 노트북 거치대 반품하려는데 박스를 버렸어요 빨리 확인 부탁드려요. | delivery |
| 무선 이어폰 사고 나서 가격이 내렸는데 차액 돌려주나요? | billing |
| 로그인 버튼이 화면 밖으로 밀려나 있어요 어떻게 해야 하나요? | technical |
| 쿠폰 입력 칸이 눌리지 않아요 어떻게 해야 하나요? | technical |
"로그인" 이 들어 있어도 화면이 깨진 것이니 기술팀, "쿠폰" 이 들어 있어도 입력 칸이 안 눌리니 기술팀이다. 처음 데이터에 없던 갈림길이다.
03 흉내 내는 코드
"""운영 3주를 흉내 낸다 — v1 이 새 문의 300건을 판단하고, 사람이 고친 것을 학습 데이터로 모은다.
규칙: 확신이 THRESHOLD 미만이면 사람이 직접 분류한다(검토 큐).
그 이상은 자동 처리하되, 그중 AUDIT_RATE 만큼을 사람이 뽑아 다시 본다(표본 검사).
사람이 본 건은 모두 정답이 붙으므로 학습 데이터로 쌓는다. 정답은 데이터에 미리 들어 있는 expected 로 흉내 낸다.
"""
import json
import random
import sys
import laya
THRESHOLD = float(sys.argv[1]) # 이 확신 미만은 사람이 직접 분류
AUDIT_RATE = float(sys.argv[2]) # 자동 처리분에서 사람이 다시 보는 비율
OUT = sys.argv[3]
agent = laya.load("checkpoints/v1")
rng = random.Random(3)
cases = [json.loads(line) for line in open("incoming-300.jsonl", encoding="utf-8")]
auto, auto_wrong, review, review_wrong, audited, audit_found = 0, 0, 0, 0, 0, 0
feedback = []
for case in cases:
answer = agent.predict(case["state"], case["questions"])["answers"]["team"]
confidence = answer["probabilities"][answer["choice"]]
wrong = answer["choice"] != case["expected"]["team"]
if confidence < THRESHOLD:
review += 1
review_wrong += wrong
feedback.append(case)
else:
auto += 1
auto_wrong += wrong
if rng.random() < AUDIT_RATE:
audited += 1
audit_found += wrong
feedback.append(case)
with open(OUT, "w", encoding="utf-8") as f:
for case in feedback:
f.write(json.dumps(case, ensure_ascii=False) + "\n")
print(f"incoming: {len(cases)}")
print(f"auto (confidence >= {THRESHOLD}): {auto}, wrong {auto_wrong} ({auto_wrong / max(auto, 1):.1%})")
print(f"human review (confidence < {THRESHOLD}): {review}, v1 was wrong on {review_wrong}")
print(f"audit sample of auto: {audited}, mistakes found {audit_found}")
print(f"human work: {review + audited} of {len(cases)} ({(review + audited) / len(cases):.0%})")
print(f"feedback rows saved: {len(feedback)} -> {OUT}")문턱(THRESHOLD)·표본 비율(AUDIT_RATE)·저장할 파일을 인자로 받는다. 사람이 정답을 고르는 일은 데이터에 들어 있는 정답(expected)으로 흉내 냈다.
04 느슨한 규칙 — 문턱 0.90, 표본 10%
python collect.py 0.90 0.10 data/feedback-loose.jsonlincoming: 300
auto (confidence >= 0.9): 294, wrong 33 (11.2%)
human review (confidence < 0.9): 6, v1 was wrong on 1
audit sample of auto: 30, mistakes found 5
human work: 36 of 300 (12%)
feedback rows saved: 36 -> data/feedback-loose.jsonl300건 가운데 294건을 0.90 이상으로 확신해 자동 처리했는데, 그중 33건(11.2%)이 틀렸다. 사람에게 넘어온 것은 6건뿐이다. 표본 검사 30건에서 틀린 것 5건을 찾았다. 사람 손은 12% 만 들었지만, 틀린 33건 가운데 사람이 찾은 것은 6건(검토 1 + 표본 5)이다. 나머지 27건은 틀린 채로 고객에게 갔다.
05 엄격한 규칙 — 문턱 0.99, 표본 30%
python collect.py 0.99 0.30 data/feedback.jsonlincoming: 300
auto (confidence >= 0.99): 286, wrong 27 (9.4%)
human review (confidence < 0.99): 14, v1 was wrong on 7
audit sample of auto: 76, mistakes found 11
human work: 90 of 300 (30%)
feedback rows saved: 90 -> data/feedback.jsonl문턱을 0.99 로 올려도 사람에게 넘어온 것은 14건이다. 0.99 이상으로 확신한 286건 가운데 27건(9.4%)이 틀렸다. 표본을 30% 로 늘려 11건을 더 찾았다. 사람 손은 30%, 모인 정답은 90건이다.
06 왜 문턱이 듣지 않나
1편에서 결정 모델의 값어치는 믿을 수 있는 확신에 있다고 했다. 7편의 보정 경고가 그 문제를 미리 알렸다. 보정 데이터가 14건뿐이라 v1 의 확신은 보정되지 않았고, 실제로 v1 은 틀릴 때도 0.99 를 말한다. 확신을 믿을 수 없으면 문턱은 거의 아무것도 걸러 내지 못한다.
그래서 운영 초기에는 다음처럼 한다.
- 표본 검사를 넉넉히 한다(20~30%). 확신이 아직 거짓말을 하니 무작위로 보는 것이 가장 정직한 측정이다.
- 표본에서 찾은 틀린 비율을 실제 오류율로 기록한다. 이 숫자가 줄어드는지가 지속 학습이 잘 되는지의 첫 지표다.
- 모인 정답이 늘어 보정이 제대로 되면(학습 로그에 보정 경고가 사라지면) 그때 문턱에 기대기 시작한다.
공식 모델 카드도 배포된 체크포인트는 지나치게 확신하니 자기 데이터로 다시 보정하라고 적고 있다. 내 데이터로 학습한 판도 데이터가 적으면 마찬가지다.
07 실습 — 내 서비스에 고리를 놓을 때
- 판단 결과를 남길 때 문장·선택·확신·판 이름을 함께 저장한다. 나중에 어느 판이 무엇을 틀렸는지 다시 볼 수 있다.
- 사람이 고친 정답은 같은 형식(6편의 JSONL)으로 바로 쌓는다. 학습 데이터로 옮길 때 다시 손댈 일이 없다.
- 고친 것뿐 아니라 표본 검사에서 맞았다고 확인한 것도 저장한다. 위의 90건 대부분이 그렇다. 맞았다고 확인된 새 표현도 다음 판에는 좋은 재료다.
다음 편에서 이 90건으로 v2 를 만든다. 이어서 학습하는 방법 셋을 실제로 비교하는데, 가장 자연스러워 보이는 방법이 오히려 점수를 떨어뜨린다.