LLM 엔지니어링20장 · LLM 평가와 모니터링4 / 22← 이전목차다음 →MetaContents

20장. LLM 평가와 모니터링

평가 묶음 만들기

단골 식당이 맛을 지키려면 매번 같은 메뉴로 시식해야 합니다. 오늘은 김치찌개, 내일은 된장찌개로 시식하면 맛이 변했는지 알 수 없습니다. 평가 묶음은 늘 같은 시식 메뉴입니다.

질문 모으기, 참조 답 적기, 종류 나누기, 판을 고정하기의 네 단계 흐름도

1번에서 실제로 들어올 법한 질문을 모읍니다. 2번에서 질문마다 사람이 바른 답(참조 답)과 근거를 적습니다. 3번에서 답이 있는 질문과, 자료에 없어 모른다고 해야 하는 질문을 나눕니다. 4번에서 묶음에 판 이름을 붙여 고정합니다. 시식 메뉴가 평가 묶음, 메뉴판의 판 번호가 묶음의 판입니다.

평가 묶음의 칸

이 장의 library_eval.jsonl(22줄, 지어낸 연습용 데이터)의 칸입니다.

칸뜻예
질문사용자가 할 법한 질문대출 기간은 며칠이에요?
정답참조 답대출 기간은 14일입니다.
열쇠답에 들어 있어야 할 낱말(세로줄 | 로 여럿)14일
종류답 / 모름답
근거맞는 자료 조각에 든 글대출 기간은 14일

묶음 훑어보기

Python
import json

# 평가 묶음 세 줄의 예(실제 파일은 22줄)
lines = [
    {"질문": "대출 기간은 며칠이에요?", "정답": "대출 기간은 14일입니다.", "열쇠": "14일", "종류": "답"},
    {"질문": "주차는 몇 분까지 무료예요?", "정답": "처음 30분까지 무료입니다.", "열쇠": "30분", "종류": "답"},
    {"질문": "노트북을 빌려 주나요?", "정답": "안내 자료에 없으니 안내 데스크에 문의해야 합니다.", "열쇠": "안내 데스크|문의", "종류": "모름"},
]
# 결과: 종류마다의 질문 수, 그리고 한 줄의 생김새
print({k: sum(l["종류"] == k for l in lines) for k in ["답", "모름"]})
print(json.dumps(lines[2], ensure_ascii=False))

코드에 나오는 함수

코드쉬운 뜻
import jsonJSON(JavaScript Object Notation) 글을 읽고 쓰는 표준 라이브러리를 불러온다
sum(l["종류"] == k for l in lines)비교 결과 True 는 1, False 는 0 으로 더해진다. 그래서 종류가 k 인 줄의 수가 된다
json.dumps(obj)파이썬 값(사전, 목록)을 JSON 글 한 줄로 바꾼다. 끝의 s 는 string(문자열)이다
ensure_ascii=Falseensure ASCII(아스키 글자만 쓰게 하기)를 끈다. 켜 두면(기본값) 한글이 질 같은 번호로 바뀐다. ASCII 는 American Standard Code for Information Interchange 의 머리글자로, 영문과 숫자 위주의 글자 표다
json.loads(line)아래 예시에 나온다. JSON 글 한 줄을 파이썬 사전으로 되돌린다. dumps 와 반대 방향이다

이 코드가 뜻하는 것

도서관의 대출 카드 상자를 떠올려 봅니다. 카드마다 "질문", "정답", "열쇠", "종류" 칸이 똑같이 인쇄되어 있고, 카드 한 장에 질문 하나를 적습니다. 여기서 카드 한 장이 jsonl 파일의 한 줄(사전 하나), 인쇄된 칸 이름이 사전의 키입니다.

줄하는 일쉽게 말하면
lines = [ {...}, {...}, {...} ]평가 묶음 세 줄을 사전 셋으로 담는다카드 세 장
{k: sum(...) for k in ["답", "모름"]}종류마다 줄 수를 센다"답" 카드와 "모름" 카드를 나눠 세기
json.dumps(lines[2], ensure_ascii=False)셋째 줄을 파일에 적힐 꼴로 바꿔 찍는다셋째 카드를 그대로 베껴 보기

{'답': 2, '모름': 1} 은 세 줄 가운데 답이 있는 질문이 2개, 모른다고 해야 하는 질문이 1개라는 뜻입니다. 둘째 줄이 jsonl 파일 안에 실제로 적히는 한 줄의 생김새이고, library_eval.jsonl 은 이런 줄이 22줄입니다.

작게 줄여서 눈으로 보기

ensure_ascii 를 켜고 끌 때의 차이와, jsonl 글을 줄마다 읽는 모습을 봅니다. 질문은 설명하려고 지어낸 것입니다.

Python
import json

row = {"질문": "대출 기간은?", "종류": "답"}
print(json.dumps(row))                        # 기본값: 한글을 \u 꼴로 바꾼다
print(json.dumps(row, ensure_ascii=False))    # 한글을 그대로 둔다

# jsonl 은 한 줄에 JSON 하나다. 줄마다 json.loads 로 읽는다
text = '{"질문": "가", "종류": "답"}\n{"질문": "나", "종류": "모름"}\n'
rows = [json.loads(line) for line in text.splitlines()]
print(len(rows), rows[1]["종류"])
출력
{"\uc9c8\ubb38": "\ub300\ucd9c \uae30\uac04\uc740?", "\uc885\ub958": "\ub2f5"}
{"질문": "대출 기간은?", "종류": "답"}
2 모름
결과읽는 법
첫 줄 {"질문": ...}기본값이면 한글이 \u 로 시작하는 번호로 바뀐다. 컴퓨터는 똑같이 읽지만 사람이 읽기 어렵다
둘째 줄ensure_ascii=False 라 한글이 그대로 남는다
2 모름두 줄짜리 글을 줄마다 json.loads 로 읽어 사전 2개가 되었고, 둘째 사전의 종류는 모름이다

splitlines() 는 글을 줄바꿈마다 잘라 목록으로 돌려줍니다. jsonl(JSON Lines)은 이렇게 한 줄에 JSON 하나를 적는 꼴이라, 파일이 길어도 한 줄씩 읽고 보탤 수 있습니다.

주의할 점

  • 답이 없는 질문을 꼭 넣습니다. 모르는 것을 모른다고 하는지가 RAG 챗봇의 중요한 품질입니다.
  • 운영 중에 틀린 답이 나온 질문은 평가 묶음에 보탭니다. 묶음을 바꾸면 판 이름을 바꿔, 다른 묶음으로 잰 점수끼리 견주지 않게 합니다.
← 이전 절20장 · LLM 평가가 어려운 까닭다음 절 →20장 · 규칙으로 채점하기