hyunsu blog

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

LAYA 첫 판단 — choice·score·noul 질문을 한 번에 묻고 결과 읽기

목차 · 4
  1. 01질문 셋을 한 번에
  2. 02결과 읽기
  3. 03여러 문장, 그리고 시간
  4. 04실습

LAYA 에 문장 하나와 질문 셋(팀 고르기·급한 정도·화남 여부)을 한 번에 물으면, 질문마다 선택·확률·확신이 돌아온다. 이 편은 결과 JSON 을 한 칸씩 읽고, 학습하지 않은 기본 모델이 쉬운 문장에서도 틀리는 경우와 한 건에 걸리는 시간을 본다.

01 질문 셋을 한 번에

python
"""질문 셋(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 은 없어도 된다.
bash
python first.py
text
{
  "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 it

02 결과 읽기

choice — 팀

  • choice: billing — 고른 답.
  • probabilities — 선택지마다 확률. 합이 1 이다. billing 0.9986 이라 거의 확실하다.
  • confidence 0.9897 — 분포 전체가 한쪽으로 쏠린 정도(1 - 정규화한 엔트로피). answer_confidence 0.9986 은 고른 답의 확률이다. 문턱을 정할 때는 보통 고른 답의 확률을 쓴다.
  • action.act_probability — 공식 모델 카드가 "아직 쓸 만한 신호가 없다" 고 적은 칸이다. 지금은 보지 않는다.

score — 급한 정도

  • score: 1.7616 — 단계 번호의 기댓값이다. legend 를 보면 1 은 "오늘 안에", 2 는 "지금 당장" 이니 그 사이, "지금 당장" 쪽에 가깝다. 문장에 "오늘 안에" 라고 썼는데도 "!" 와 결제 두 번이라는 내용 때문에 더 급하게 읽었다.
  • 단계마다 확률도 있다. 하나를 골라야 하면 확률이 가장 큰 단계(2)를 쓴다.

noul — 화남

  • noul: 0.2182 — "예" 일 확률이다. 화가 났다고 보기는 어렵다고 판단했다.

질문 셋이 계산 한 번에 함께 나왔다. 모델 카드도 한 번 부를 때의 질문이 모두 한 번의 계산(forward pass)으로 처리된다고 적고 있다.

routing

마지막 줄은 어느 체크포인트가 답했는지다. 한글이 100% 라 영어 체크포인트는 읽을 수 없다며 다국어 체크포인트(multilingual)를 골랐다.

03 여러 문장, 그리고 시간

python
"""여러 문장을 판단해 보고, 한 번에 걸리는 시간을 잰다."""
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}")
bash
python many.py
text
technical 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 실습

  1. first.py 의 문장을 바꿔 보며 score·noul 값이 어떻게 움직이는지 본다. "환불해 주세요" 와 "당장 환불 안 하면 신고합니다" 의 angry 차이를 비교해 본다.
  2. many.py 에 자기 서비스의 실제 문장 몇 개를 넣어 본다. 몇 개나 맞히는지, 틀린 것의 확신은 몇인지 적어 둔다. 5편에서 그것을 숫자로 잰다.

다음 편은 질문 설계다. 선택지 설명을 어떻게 쓰느냐에 따라 같은 모델의 정확도가 크게 달라진다.