홈강좌LAYA 결정 모델 24편 중 3편째
LAYA 첫 판단 — choice·score·noul 질문을 한 번에 묻고 결과 읽기
LAYA 에 문장 하나와 질문 셋(팀 고르기·급한 정도·화남 여부)을 한 번에 물으면, 질문마다 선택·확률·확신이 돌아온다. 이 편은 결과 JSON 을 한 칸씩 읽고, 학습하지 않은 기본 모델이 쉬운 문장에서도 틀리는 경우와 한 건에 걸리는 시간을 본다.
01 질문 셋을 한 번에
"""질문 셋(choice·score·noul)을 한 번에 묻고 결과를 통째로 본다."""
import json
from laya import Router
router = Router()
questions = {
"team": {
"type": "choice",
"instructions": "이 문의를 맡을 팀은?",
"criteria": {"billing": "결제·환불", "delivery": "배송·교환", "technical": "앱 오류"},
},
"urgency": {
"type": "score",
"instructions": "얼마나 급한가?",
"criteria": ["여유 있음", "오늘 안에", "지금 당장"],
},
"angry": {"type": "noul", "instructions": "고객이 화가 났는가?"},
}
result = router.predict("어제 주문한 운동화 결제가 두 번 됐어요. 오늘 안에 하나 취소해 주세요!", questions)
print(json.dumps(result["answers"], ensure_ascii=False, indent=2))
print("routing:", result["routing"]["model"], "-", result["routing"]["reason"])Router()는 문장의 언어를 보고 알맞은 체크포인트를 골라 준다. 처음 쓸 때 체크포인트를 내려받는다.- 질문은 이름(
team·urgency·angry)을 붙여 사전으로 넘긴다. 이름은 결과에서 답을 찾는 열쇠다. criteria는 질문 종류마다 모양이 다르다. choice 는{선택지: 설명}, score 는 낮은 단계부터 높은 단계까지의 목록, noul 은 없어도 된다.
python first.py{
"team": {
"type": "choice",
"choice": "billing",
"probabilities": {
"billing": 0.9986,
"delivery": 0.0005,
"technical": 0.0009
},
"confidence": 0.9897,
"answer_confidence": 0.9986,
"action": {
"act_probability": 1.0
}
},
"urgency": {
"type": "score",
"score": 1.7616,
"legend": {
"0": "여유 있음",
"1": "오늘 안에",
"2": "지금 당장"
},
"probabilities": {
"0": 0.0323,
"1": 0.1737,
"2": 0.7939
},
"confidence": 0.4554,
"answer_confidence": 0.7939,
"action": {
"act_probability": 1.0
}
},
"angry": {
"type": "noul",
"noul": 0.2182,
"confidence": 0.7818,
"answer_confidence": 0.7818,
"action": {
"act_probability": 1.0
}
}
}
routing: multilingual - non-Latin script (hangul, 100% of letters); the English checkpoint cannot read it02 결과 읽기
choice — 팀
choice: billing— 고른 답.probabilities— 선택지마다 확률. 합이 1 이다. billing 0.9986 이라 거의 확실하다.confidence0.9897 — 분포 전체가 한쪽으로 쏠린 정도(1 - 정규화한 엔트로피).answer_confidence0.9986 은 고른 답의 확률이다. 문턱을 정할 때는 보통 고른 답의 확률을 쓴다.action.act_probability— 공식 모델 카드가 "아직 쓸 만한 신호가 없다" 고 적은 칸이다. 지금은 보지 않는다.
score — 급한 정도
score: 1.7616— 단계 번호의 기댓값이다.legend를 보면 1 은 "오늘 안에", 2 는 "지금 당장" 이니 그 사이, "지금 당장" 쪽에 가깝다. 문장에 "오늘 안에" 라고 썼는데도 "!" 와 결제 두 번이라는 내용 때문에 더 급하게 읽었다.- 단계마다 확률도 있다. 하나를 골라야 하면 확률이 가장 큰 단계(2)를 쓴다.
noul — 화남
noul: 0.2182— "예" 일 확률이다. 화가 났다고 보기는 어렵다고 판단했다.
질문 셋이 계산 한 번에 함께 나왔다. 모델 카드도 한 번 부를 때의 질문이 모두 한 번의 계산(forward pass)으로 처리된다고 적고 있다.
routing
마지막 줄은 어느 체크포인트가 답했는지다. 한글이 100% 라 영어 체크포인트는 읽을 수 없다며 다국어 체크포인트(multilingual)를 골랐다.
03 여러 문장, 그리고 시간
"""여러 문장을 판단해 보고, 한 번에 걸리는 시간을 잰다."""
import time
from laya import Router
router = Router()
questions = {
"team": {
"type": "choice",
"instructions": "이 문의를 맡을 팀은?",
"criteria": {"billing": "결제·환불", "delivery": "배송·교환", "technical": "앱 오류"},
},
}
texts = [
"카드가 두 번 긁혔어요.",
"택배가 일주일째 안 와요.",
"앱을 켜면 바로 꺼져요.",
"결제 버튼을 눌러도 반응이 없어요.",
"환불 받은 돈이 아직 안 들어왔어요.",
]
router.predict(texts[0], questions) # 첫 판단은 모델을 올리느라 느리다 — 재기 전에 한 번 데운다
for text in texts:
start = time.time()
answer = router.predict(text, questions)["answers"]["team"]
ms = (time.time() - start) * 1000
top = answer["probabilities"][answer["choice"]]
print(f"{answer['choice']:<9} {top:.2f} {ms:4.0f} ms {text}")python many.pytechnical 0.65 20 ms 카드가 두 번 긁혔어요.
delivery 1.00 19 ms 택배가 일주일째 안 와요.
technical 1.00 20 ms 앱을 켜면 바로 꺼져요.
technical 0.59 19 ms 결제 버튼을 눌러도 반응이 없어요.
billing 0.88 19 ms 환불 받은 돈이 아직 안 들어왔어요.- 한 건에 약 20ms 다. 첫 판단은 모델을 올리느라 느려서, 재기 전에 한 번 돌려 데웠다.
- 다섯 가운데 하나가 틀렸다. "카드가 두 번 긁혔어요" 는 결제팀인데 기술팀(0.65)으로 갔다. "결제 버튼을 눌러도 반응이 없어요" 는 기술팀이 맞지만 확신이 0.59 로 낮다. 학습하지 않은 모델은 일반 상식으로만 고르니 "결제" 와 "오류" 가 섞인 문장에서 흔들린다.
- 이 다섯 문장에서는 흔들린 둘의 확신이 0.65·0.59 로 낮았고, 나머지는 0.88~1.00 이었다. 확신이 쓸모 있는 신호처럼 보인다. 하지만 5편에서 200건으로 재 보면 기본 모델은 틀릴 때도 높은 확신을 자주 붙인다.
04 실습
first.py의 문장을 바꿔 보며 score·noul 값이 어떻게 움직이는지 본다. "환불해 주세요" 와 "당장 환불 안 하면 신고합니다" 의angry차이를 비교해 본다.many.py에 자기 서비스의 실제 문장 몇 개를 넣어 본다. 몇 개나 맞히는지, 틀린 것의 확신은 몇인지 적어 둔다. 5편에서 그것을 숫자로 잰다.
다음 편은 질문 설계다. 선택지 설명을 어떻게 쓰느냐에 따라 같은 모델의 정확도가 크게 달라진다.