20장. LLM 평가와 모니터링
RAG 는 나눠서 잰다
찌개 맛이 이상하면 재료가 상했는지, 요리사가 간을 잘못했는지부터 가립니다. RAG 의 틀린 답도 검색이 맞는 자료를 못 찾았는지, 자료를 찾았는데 LLM 이 잘못 썼는지 나눠 봅니다.
그림의 왼쪽 카드는 검색만 재는 hit@k 입니다. 가운데는 답이 참조 답과 맞는지 재는 답 정확도입니다. 오른쪽은 답의 내용이 검색한 자료에 있는지 보는 근거 충실도입니다. 재료 검사가 검색 평가, 간 보기가 답 평가입니다.
실패를 네 칸으로 나누기
| 답이 맞음 | 답이 틀림 | |
|---|---|---|
| 검색이 맞음 | 잘 됨 | 생성 실패: 프롬프트, 모델을 고친다 |
| 검색이 틀림 | 운이 좋았거나 모델이 원래 알았다 | 검색 실패: 조각, 임베딩, 문서를 고친다 |
RAGAS 같은 오픈소스 도구는 근거 충실도, 답 관련성 같은 지표를 LLM 채점자로 계산해 줍니다. 이 장에서는 도구를 쓰지 않고 같은 생각을 직접 코드로 짭니다.
네 칸으로 세기
from collections import Counter
# (검색이 맞았나, 답이 맞았나) 의 예시 결과
results = [(True, True), (True, True), (True, False), (False, False), (True, True), (False, True)]
names = {(True, True): "잘 됨", (True, False): "생성 실패", (False, False): "검색 실패", (False, True): "검색 틀림·답 맞음"}
# 결과: 칸마다의 수. 고칠 곳의 차례를 정할 때 쓴다
print(Counter(names[r] for r in results))코드에 나오는 함수
| 코드 | 쉬운 뜻 |
|---|---|
from collections import Counter | 같은 값이 몇 번 나왔는지 세어 주는 Counter(세는 것)를 불러온다 |
(True, False) | (검색이 맞았나, 답이 맞았나) 두 값을 묶은 튜플이다. 사전의 키로도 쓸 수 있다 |
names[r] | 튜플 r 에 붙인 칸 이름을 꺼낸다 |
Counter(...) | 칸 이름마다 몇 번 나왔는지 센다. 찍으면 많은 것부터 늘어놓는다 |
이 코드가 뜻하는 것
택배가 잘못 갔을 때 사고 카드를 네 칸짜리 상자에 나눠 넣는 일과 같습니다. 창고에서 물건을 바르게 집었는지, 기사가 바르게 배달했는지로 칸을 정합니다. 여기서 창고에서 집기가 검색, 배달이 답 만들기입니다. 비유가 맞지 않는 칸도 있습니다. "검색 틀림·답 맞음"은 잘못 집은 물건이 우연히 맞은 셈인데, RAG 에서는 모델이 원래 알던 내용으로 답할 때 생깁니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
results = [...] | 질문 여섯 개의 (검색, 답) 결과를 담는다 | 사고 카드 6장 |
names = {...} | 네 가지 짝에 칸 이름을 붙인다 | 상자 네 칸의 이름표 |
names[r] for r in results | 결과마다 칸 이름으로 바꾼다 | 카드를 맞는 칸에 넣기 |
Counter(...) | 칸마다 수를 센다 | 칸마다 카드 세기 |
결과는 잘 됨 3, 생성 실패 1, 검색 실패 1, 검색 틀림·답 맞음 1 입니다. 실제 평가에서 생성 실패가 많으면 프롬프트와 모델을, 검색 실패가 많으면 조각과 임베딩을 먼저 고칩니다.
주의할 점
- 답이 없는 질문(모름)은 검색 평가에서 뺍니다. 맞는 자료가 없으니 hit@k 를 잴 수 없습니다.
- 근거 충실도가 높아도 자료 자체가 옛 판이면 틀린 답입니다. 자료의 판은 19장의 갱신 작업이 지킵니다.