hyunsu blog

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

LAYA 학습 데이터 만들기 — CSV·JSONL 형식과 dry-run 으로 미리 보기

목차 · 6
  1. 01무엇을 가르치나 — 우리 회사의 규칙
  2. 02데이터는 세 무더기로 만든다
  3. 03만들어 보기
  4. 04형식 1 — CSV(질문 하나)
  5. 05형식 2 — JSONL(질문 여럿)
  6. 06실습 — 내 데이터로 옮길 때

LAYA 는 문장과 정답이 짝지어진 데이터로 학습한다. 가장 쉬운 형식은 문장·정답 두 칸의 CSV 이고, 질문 여럿과 정답을 함께 담을 때는 JSONL 을 쓴다. 이 편에서는 고객 문의 데이터를 학습용·운영용·평가용으로 나눠 만들고, laya-train --dry-run 으로 학습 전에 확인한다.

01 무엇을 가르치나 — 우리 회사의 규칙

5편에서 기본 모델은 평가 문의 200건 가운데 55% 만 맞혔다. 틀린 것은 대부분 회사마다 다른 규칙 때문이었다. 이 쇼핑몰의 규칙은 이렇다.

python
TEAMS = {
    "billing": "결제·환불·쿠폰·영수증",
    "delivery": "배송·교환·반품",
    "account": "로그인·비밀번호·회원·포인트",
    "technical": "앱·웹사이트 오류, 결제창 오류 포함",
}

"교환·반품은 배송팀", "포인트는 계정팀", "결제창 오류는 결제팀이 아니라 기술팀" 같은 것이 일반 상식과 조금씩 어긋난다. 설명 문장에 써 두기는 했지만, 기본 모델은 이것을 몇 줄 설명만으로 따르지 못했다. 이것을 예시로 가르치는 것이 학습이다.

02 데이터는 세 무더기로 만든다

실제 문의를 쓸 수 없어서, 이 강좌는 문장 틀을 채워 문의를 만드는 합성 데이터를 쓴다. 팀마다 문장 틀을 세 무더기로 나눈 것이 핵심이다. 결제팀의 틀을 예로 보면 이렇다.

python
"billing": {
    "train": [
        "{item} 결제가 두 번 됐어요{tail}",
        "{when} 주문한 {item} 환불은 언제 들어오나요{tail}",
        "쿠폰을 썼는데 할인이 안 됐어요{tail}",
        "{item} 영수증을 다시 받을 수 있을까요{tail}",
        "카드값이 주문 금액보다 많이 나갔어요{tail}",
        "무통장 입금했는데 확인이 안 돼요{tail}",
    ],
    "eval": [
        "{item} 값이 중복으로 빠져나갔습니다{tail}",
        "취소한 {item} 돈을 아직 못 돌려받았어요{tail}",
        "할인 코드가 적용이 안 된 채로 결제됐어요{tail}",
        "세금계산서 발행 부탁드려요{tail}",
    ],
    "ops": [
        "{item} 결제 취소했는데 카드 승인이 그대로예요{tail}",
        "포인트 말고 카드로 환불받고 싶어요{tail}",
        "같은 주문이 결제 완료로 두 개 떠요{tail}",
        "{item} 사고 나서 가격이 내렸는데 차액 돌려주나요{tail}",
    ],
},
  • train — 처음 학습에 쓰는 표현.
  • ops — 운영을 시작한 뒤에 새로 들어오는 표현(9편에서 쓴다). 실제 서비스에서도 처음 모은 데이터에 없던 말투가 계속 들어온다.
  • eval — 평가에만 쓰는 표현. 학습에서 한 번도 보지 않은 문장으로 재야, 외운 것이 아니라 익힌 것을 잴 수 있다.

{item}·{when}·{tail} 자리에 상품 이름·때·말끝("빨리 확인 부탁드려요." 같은 것)을 채워 문장을 만든다. 합성 데이터라 실제 문의보다 쉽고 반복이 많다. 이 강좌의 숫자는 방법을 보여 주는 숫자로 읽고, 실제 정확도는 자기 데이터로 다시 재야 한다.

질문은 팀(choice)과 서두름(noul) 둘이다.

python
QUESTIONS = {
    "team": {"type": "choice", "instructions": "이 문의를 맡을 팀은?", "criteria": TEAMS},
    "urgent": {"type": "noul", "instructions": "고객이 서두르거나 불편을 드러내는가?"},
}

03 만들어 보기

bash
python make_data.py
text
train 600 · eval 200
  billing   train 206 · eval  63
  delivery  train 241 · eval  45
  account   train  54 · eval  45
  technical train  99 · eval  47
first train rows:
  {'text': '텀블러 상자가 찌그러져서 왔어요, 바꿔 주세요.', 'team': 'delivery', 'urgent': False}
  {'text': '장바구니에 담기가 안 돼요 답답하네요.', 'team': 'technical', 'urgent': True}
  {'text': '청바지 영수증을 다시 받을 수 있을까요', 'team': 'billing', 'urgent': False}

학습용 600건과 평가용 200건이 생겼다. 팀별 건수가 고르지 않다. 계정팀(account)은 학습용이 54건뿐이다. 계정 문의의 문장 틀에는 상품 이름 자리가 없어서 서로 다른 문장이 적게 나온 탓이다. 실제 데이터도 대개 이렇게 기운다. 적은 쪽이 학습 뒤에 어떻게 되는지는 8편에서 본다.

04 형식 1 — CSV(질문 하나)

정답이 하나뿐이면 CSV 가 가장 쉽다. 엑셀에서 만들어도 된다.

text
text,team
"텀블러 상자가 찌그러져서 왔어요, 바꿔 주세요.",delivery
장바구니에 담기가 안 돼요 답답하네요.,technical
청바지 영수증을 다시 받을 수 있을까요,billing

선택지는 정답 칸에 나온 값들이 그대로 된다. 실제로 학습하기 전에 --dry-run 으로 확인한다. 모델을 내려받지도, 학습하지도 않고 데이터만 읽어 본다.

bash
laya-train --data tickets-train.csv --text-column text --label-column team \
  --question-id team --instructions "이 문의를 맡을 팀은?" --base multilingual --dry-run
text
dry-run: 600 rows read, 600 valid items, skipped: {}
dry-run: optimizer budget: 540 train item(s), effective batch up to 64 (micro_batch 8 x grad_accum 8), 36 optimizer updates over 4 epoch(s)

600줄을 읽어 600건이 학습 항목이 됐고, 건너뛴 것(skipped)이 없다. 빈 문장·빈 정답이 있으면 여기에 이유와 함께 센다. 둘째 줄은 학습 계획이다. 60건(10%)은 보정용으로 떼어 두고 540건으로 학습하며, 8건씩 8번 모아 한 번 고치니(유효 배치 64) 4번 돌면 36번 고친다.

05 형식 2 — JSONL(질문 여럿)

질문이 여럿이면 JSONL 을 쓴다. 한 줄이 한 건이다. 같은 내용을 들여 써서 보면 이렇다.

json
{
  "state": "텀블러 상자가 찌그러져서 왔어요, 바꿔 주세요.",
  "questions": {
    "team": {
      "type": "choice",
      "instructions": "이 문의를 맡을 팀은?",
      "criteria": {
        "billing": "결제·환불·쿠폰·영수증",
        "delivery": "배송·교환·반품",
        "account": "로그인·비밀번호·회원·포인트",
        "technical": "앱·웹사이트 오류, 결제창 오류 포함"
      }
    },
    "urgent": {
      "type": "noul",
      "instructions": "고객이 서두르거나 불편을 드러내는가?"
    }
  },
  "expected": {
    "team": "delivery",
    "urgent": false
  }
}
  • state 는 판단할 문장이다.
  • questions 는 3편에서 쓴 질문 모양 그대로다. 학습 때 쓴 질문 문장과 선택지를 서비스에서도 똑같이 써야 한다. 학습이 끝나면 체크포인트 안에 questions.json 으로 함께 저장된다.
  • expected 는 질문마다 정답이다. choice 는 선택지 이름, noul 은 true·false, score 는 단계 번호다.
bash
laya-train --data tickets-train.jsonl --base multilingual --dry-run
text
dry-run: 600 rows read, 1200 valid items, skipped: {}
dry-run: optimizer budget: 1080 train item(s), effective batch up to 64 (micro_batch 8 x grad_accum 8), 68 optimizer updates over 4 epoch(s)

같은 600줄인데 학습 항목이 1,200건이다. 문장마다 질문이 둘이라 항목이 둘씩 생긴 것이다. 그만큼 한 번 돌 때 일이 두 배가 되고, 고치는 횟수도 68번으로 늘었다.

06 실습 — 내 데이터로 옮길 때

  1. 실제 문의 가운데 사람이 이미 분류해 둔 것부터 모은다. 상담 도구의 처리 기록·태그가 그대로 정답이 된다.
  2. 평가용은 학습용과 겹치지 않게 먼저 떼어 둔다. 가능하면 학습 데이터보다 나중 기간의 문의로 둔다(미래의 문의를 잘 맞혀야 하니까).
  3. 팀마다 적어도 수십 건은 있게 한다. 한 팀이 너무 적으면 그 팀부터 틀린다.
  4. --dry-run 으로 건너뛴 것이 없는지, 학습 횟수가 너무 적지 않은지 본다.

다음 편에서 처음 라벨을 붙인 150건으로 맥북에서 실제로 학습한다.