LLM 엔지니어링6장 · RAG 의 원리와 로컬 모델로 옮기기15 / 23← 이전목차다음 →MetaContents

6장. RAG 의 원리와 로컬 모델로 옮기기

실습 1 — 모델이 모르는 질문 해 보기

3장부터 쓴 Qwen/Qwen2.5-0.5B-Instruct 에 가상 쇼핑몰의 정책을 묻고, 문서 없이 얼마나 답하는지 봅니다. 질문은 shop_policy_qa.jsonl 의 30개(문서에 있는 24개, 없는 6개)이고, 쇼핑몰과 정책은 모두 연습용으로 지어낸 것입니다. GPU 가 있으면 GPU 로, 없으면 CPU 로 돕니다.

1단계. 모델 불러오기

Python
import json
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

name = "Qwen/Qwen2.5-0.5B-Instruct"
# GPU 가 있으면 fp16 으로 GPU 에, 없으면 float32 로 CPU 에 올린다
device = "cuda" if torch.cuda.is_available() else "cpu"
dtype = torch.float16 if device == "cuda" else torch.float32
tok = AutoTokenizer.from_pretrained(name)
model = AutoModelForCausalLM.from_pretrained(name, dtype=dtype).to(device)

def chat(messages, max_new_tokens=100):
    """메시지 목록을 받아 모델의 답(글)을 돌려준다. do_sample=False 라 다시 돌려도 같은 답이다."""
    enc = tok.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt", return_dict=True).to(device)
    with torch.no_grad():
        out = model.generate(**enc, max_new_tokens=max_new_tokens, do_sample=False)
    # 프롬프트 뒤에 새로 만든 토큰만 글로 바꾼다
    return tok.decode(out[0][enc["input_ids"].shape[1]:], skip_special_tokens=True).strip()

print("장치:", device)
출력
장치: cuda

2단계. 문서 없이 물어보기

Python
# 질문 30개를 읽는다. 근거조항이 0 이면 문서에 없는 질문이다
qa = [json.loads(line) for line in open("shop_policy_qa.jsonl", encoding="utf-8")]
print("질문 수:", len(qa), "· 문서에 없는 질문:", sum(r["근거조항"] == 0 for r in qa))

NO_DOC = [{"role": "system", "content": "당신은 쇼핑몰 상담원입니다. 짧게 답하세요."}]
# 문서에 있는 질문 둘, 없는 질문 하나를 골라 본다
for q in ["휴대폰으로 결제하면 한도가 있나요?", "골드 등급이 되려면 얼마나 사야 해요?", "새벽 배송도 해 주나요?"]:
    answer = chat(NO_DOC + [{"role": "user", "content": q}])
    print("질문:", q)
    print("답  :", answer.replace("\n", " ")[:110])
출력
질문 수: 30 · 문서에 없는 질문: 6
질문: 휴대폰으로 결제하면 한도가 있나요?
답  : 결제 금액에 대한 한도는 상점마다 다르지만, 일반적으로 10만원 이상의 결제를 원하는 경우가 많습니다. 이 금액을 기준으로 상점에서 할인이나 특별한 혜택을 제공할 수 있습니다.
질문: 골드 등급이 되려면 얼마나 사야 해요?
답  : 골드 등급을 구할 때는 몇 가지 요소가 중요합니다:  1. 가격: 골드의 가격에 따라 가치가 달라집니다.  2. 브랜드와 제조사: 브랜드와 제조사를 고려해야 합니다.  3. 판매량: 현재 판매량과
질문: 새벽 배송도 해 주나요?
답  : 네, 새벽 배송을 지원합니다. 저희는 고객님의 주문에 따라 최선을 다해 배송을 진행하겠습니다. 언제든지 도움이 필요하시면 말씀해주세요!

3단계. 30개 채점하기

Python
from collections import Counter

REFUSE = ["찾지 못", "찾을 수 없", "나와 있지 않", "제공되지 않"]

def grade(key, answer):
    """본문 "LLM 이 모르는 것"의 채점 함수. 결과: 정답, 오답, 거절, 지어냄, 모름 맞음"""
    refused = any(w in answer for w in REFUSE)
    if key == "":
        return "모름 맞음" if refused else "지어냄"
    if any(k in answer for k in key.split("|")):
        return "정답"
    return "거절" if refused else "오답"

results = [grade(r["정답열쇠"], chat(NO_DOC + [{"role": "user", "content": r["질문"]}])) for r in qa]
# 문서에 있는 질문(24개)과 없는 질문(6개)을 따로 센다
print("문서에 있는 질문:", Counter(x for x, r in zip(results, qa) if r["근거조항"]))
print("문서에 없는 질문:", Counter(x for x, r in zip(results, qa) if not r["근거조항"]))
출력
문서에 있는 질문: Counter({'오답': 22, '정답': 2})
문서에 없는 질문: Counter({'지어냄': 6})

이 실습이 뜻하는 것

이 실습은 우리 가게 안내서를 한 번도 본 적 없는 새 아르바이트생에게 손님 질문을 던져 보는 것과 같습니다. 아르바이트생을 불러 자리에 앉히고(1단계), 질문 세 개를 먼저 던져 어떻게 답하는지 듣고(2단계), 질문 30개를 모두 물어 정답 낱말이 들어 있는지로 채점합니다(3단계). 여기서 아르바이트생이 Qwen2.5-0.5B-Instruct 모델, 안내서가 shop_policy.txt 정책 문서, 채점표가 grade() 함수입니다. 비유와 다른 점은, 사람은 모르면 머뭇거리기라도 하지만 모델은 모르는 것도 망설임 없이 그럴듯하게 답한다는 것입니다.

단계출력뜻
1. 모델 불러오기장치: cuda모델이 GPU(Graphics Processing Unit, 그래픽 처리 장치)에 올라갔습니다. GPU 가 없으면 cpu 로 나오고 더 느리게 돕니다
2. 질문 수질문 수: 30 · 문서에 없는 질문: 6시험 문제는 30개이고, 그 가운데 6개는 정책 문서에도 답이 없는 질문입니다
2. 휴대폰 결제"일반적으로 10만원 이상…"문서의 답은 "한 달에 50만원까지"인데, 모델은 흐릿한 일반론을 지어냈습니다
2. 골드 등급"골드의 가격에 따라…"쇼핑몰의 회원 등급을 금(골드)을 사는 이야기로 잘못 알아들었습니다
2. 새벽 배송"네, 새벽 배송을 지원합니다"문서에 없는 서비스를 있다고 자신 있게 답했습니다. 이렇게 사실이 아닌 것을 지어내는 것이 환각입니다
3. 문서에 있는 질문'오답': 22, '정답': 2답이 문서에 있는 24문제 가운데 2개만 맞혔습니다. 그 2개도 일반론에 열쇠 낱말이 우연히 섞인 것입니다
3. 문서에 없는 질문'지어냄': 6답이 없는 6문제에 한 번도 모른다고 하지 않고 모두 지어냈습니다

한 줄로 줄이면, 모델은 우리 쇼핑몰 정책을 모르고, 모를 때도 모른다고 하지 않는다는 것을 숫자(24문제 중 정답 2개, 없는 6문제 모두 지어냄)로 확인한 실습입니다. 이 점수가 뒤 실습의 출발선입니다. 실습 4에서 같은 30문제를 문서를 붙여 다시 풀고 견줍니다. 채점은 열쇠 낱말이 들어 있는지만 보므로, 정답 2개가 "알고 맞혔다"는 뜻은 아닙니다.

코드 설명

  • apply_chat_template(..., return_dict=True) 는 3장처럼 메시지 목록을 모델이 학습한 대화 꼴의 토큰으로 바꿉니다. chat() 은 이 장 내내 "메시지를 넣으면 답 글이 나오는" 함수로 씁니다.
  • 문서 없이 물으니 휴대폰 결제 한도는 "일반적으로 10만원 이상"처럼 흐리게 지어내고, 골드 등급은 금(골드)을 사는 이야기로 알아듣고, 문서에 없는 새벽 배송은 "네, 새벽 배송을 지원합니다"라고 자신 있게 답합니다. 모두 문서와 다른 환각입니다.
  • 문서에 있는 24개 가운데 정답은 2개, 오답 22개이고, 문서에 없는 6개는 모두 지어냈습니다. 정답 2개도 "반품은 일반적으로 불가능합니다", "대부분 10% 이하"처럼 일반론에 열쇠 낱말이 우연히 들어간 것입니다.
  • 정답 열쇠는 "50만원"처럼 짧은 낱말이라, 운 좋게 겹치는 낱말을 말하면 맞힌 것으로 셉니다. 숫자가 몇 개 맞았다고 모델이 정책을 아는 것은 아닙니다.
← 이전 절6장 · 실습 지도다음 절 →6장 · 실습 2 — 정책 문서를 조각으로 나누기