도우미가 교육비 한도를 20만 원이라고 답했다. 문서에는 30만 원이라고 적혀 있다. 모델을 더 좋은 것으로 바꾸면 고쳐질까. 모델이 문서를 읽고도 틀렸다면 그렇다. 하지만 검색이 개정 전 문서를 골라 넘겼다면 어떤 모델을 써도 20만 원이라고 답한다. 그래서 모델을 고르기 전에 모델이 하는 일과 우리가 만들 일을 나눈다.
다음 토큰 예측
언어 모델은 지금까지의 토큰을 보고 다음 토큰 하나의 확률을 계산한다. 토큰은 모델이 글을 다루는 단위인데, 한 단어나 한 글자와 같지 않고 토크나이저마다 다르게 쪼갠다. 한국어 문장이 토큰 몇 개인지는 쓰려는 모델의 토크나이저로 확인해야 한다.
고른 토큰을 뒤에 붙이고 같은 계산을 반복하니, 앞에서 다른 토큰이 뽑히면 뒤의 문장도 달라진다. 같은 질문에 표현이 매번 조금씩 다른 이유다.
학습은 데이터로 모델의 매개변수를 조정하는 과정이고, 추론은 그 매개변수로 입력을 처리하는 과정이다. 문서를 프롬프트에 붙여 질문하는 우리의 호출은 추론이다. 그 문서가 모델 안에 남지 않으니, 다음 요청에도 쓰려면 애플리케이션이 다시 넘겨야 한다. 프롬프트 안의 예시만으로 매개변수 갱신 없이 여러 작업을 처리한 대표 연구가 GPT-3다. Language Models are Few-Shot Learners
모델의 내부 구조인 Transformer는 어텐션으로 토큰의 표현을 주변 문맥에 따라 바꾼다. “승인 후 구매”와 “구매 후 승인”은 같은 단어로 됐지만 관계가 다르고, 모델은 단어의 유무를 넘어 이 관계를 다룬다. 관계를 항상 지키지는 못하므로 업무 절차의 순서는 평가 사례에 넣는다. 행렬 계산까지 알 필요는 없고, 긴 입력과 긴 출력이 계산 자원을 더 쓴다는 점만 알면 된다. Attention Is All You Need
학습된 지식과 문맥
모델이 교육비 제도를 일반적으로 잘 설명하더라도 한울연구소의 현재 한도를 알고 있을 리 없다. 반대로 사내 정책을 본 적 없는 모델도 문서를 받으면 답할 수 있다. 무엇을 아는가와 주어진 자료를 얼마나 잘 쓰는가는 따로 평가한다.
첫 조건에서 금액을 맞혔다면 우연이고, 둘째부터 넷째까지가 실제로 재려는 능력이다. 넷을 합쳐야 수치를 잘 생성하는 모델과 근거에 따라 수치를 고르는 모델이 갈린다.
컨텍스트 창은 한 번의 처리에서 다룰 수 있는 토큰 범위다. 입력·출력·별도 처리 토큰이 한도에 어떻게 들어가는지는 제공자 문서로 확인한다. 창이 크다고 긴 문서의 모든 정보를 같은 품질로 쓰지는 못한다. Lost in the Middle은 관련 정보의 위치를 바꾸면 성능이 달라지는 현상을 보고했다. 우리 실험에서는 같은 정책을 짧은 문맥 앞에 둔 경우와 긴 문맥 가운데에 둔 경우를 비교하되, 위치와 주변 문서 양을 하나씩만 바꾼다. Lost in the Middle
문서가 적고 권한이 단순하면 전체를 넘기는 쪽이 좋은 기준선이다. 문서가 많고 자주 바뀌고 사용자마다 읽을 범위가 다르면 필요한 부분을 고르는 검색이 필요하다. 긴 문맥과 RAG 중 어느 쪽이 낫냐가 아니라, 같은 질문 집합에서 품질·지연·비용·권한 처리를 비교해 고른다.
모델·제공자·실행 환경
모델 이름만 기록하면 실험을 재현할 수 없다. 같은 계열이라도 버전과 튜닝이 다르고, 로컬에서는 양자화와 추론 엔진이, 호스팅 API에서는 요청 형식과 지원 기능이 다르다. 고르는 대상은 모델과 실행 조건의 조합이다.
| 구분 | 질문 | 기록 |
|---|---|---|
| 모델 | 어떤 작업을 처리하는가 | 식별자·버전·입력 매체 |
| 제공자 | 어떤 API로 접근하는가 | 엔드포인트 종류·지원 기능 |
| 실행 환경 | 어디서 어떤 자원으로 계산하는가 | 호스팅 또는 장비·추론 엔진 |
| 생성 설정 | 어떤 제약으로 응답을 만드는가 | 출력 한도·샘플링 설정 |
| 데이터 경로 | 어떤 정보가 어디로 가는가 | 요청·저장·로그·삭제 범위 |
가중치를 내려받을 수 있다고 사용·수정·재배포가 다 허용되지는 않는다. 모델 카드와 사용 조건을 확인해야 하고, 그래서 ‘오픈소스’와 ‘공개 가중치’를 같은 말로 쓰지 않는다. 호스팅 API는 실행 환경을 꾸리는 부담이 없지만 네트워크와 외부 서비스의 제한을 받고, 자체 호스팅은 환경을 직접 제어하지만 장비·용량·업데이트·장애 대응을 맡는다. 어느 쪽이 싼지는 트래픽과 가동률, 운영 인력에 따라 다르다.
비교 실험의 순서
한국어 출력과 문서 근거 준수는 첫 기능부터 필요하고, 영상 입력은 아직 필요 없다. 필수 조건을 점수에 섞으면 자연스러운 문장이 필수 기능의 부재를 덮는다. Q01~Q05처럼 기대 결과가 분명한 항목부터 확인하고, 문체의 자연스러움은 별도 항목으로 둔다. 질문마다 세 번씩 돌리는 것은 한 번 잘 나온 답이 우연인지 보려는 최소치라서, 중요한 실패가 간헐적으로 보이면 반복을 늘린다.
모델마다 권장 입력 형식이 달라 후보별 조정을 허용할 수 있지만, 공통 조건 결과와 조정 후 결과는 나눠 기록한다. 한 후보만 오래 튜닝한 결과를 같은 조건의 비교라고 부르면 안 된다. 어떤 후보가 지원하지 않는 설정은 억지로 넘기지 말고, 통제하지 못한 차이로 남긴다.
| 항목 | 후보 A | 후보 B |
|---|---|---|
| 모델 식별자·버전 | 실행 시 기록 | 실행 시 기록 |
| 문서 수치·조건 일치 | 통과 / 전체 | 통과 / 전체 |
| 근거 없는 질문 처리 | 통과 / 전체 | 통과 / 전체 |
| 근거 식별자 유효성 | 통과 / 전체 | 통과 / 전체 |
| 형식 실패 | 실패 / 전체 | 실패 / 전체 |
| 전체 응답 시간 · 첫 토큰 시간 | 관측값과 요약 | 관측값과 요약 |
| 비용 | 총액 · 산정 근거 · 가격 확인 시점 | 총액 · 산정 근거 · 가격 확인 시점 |
| 미지원 설정 | 명시 | 명시 |
응답 시간은 요청 시작부터 응답을 다 받을 때까지 재고, 스트리밍이면 첫 토큰까지의 시간을 따로 잰다. 첫 토큰이 빨리 나와도 업무는 늦게 끝날 수 있다. 비용은 토큰당 가격에 실제 입력·출력 토큰 수와 재시도 횟수를 곱해서 내고, 가격을 확인한 시점을 함께 적는다.
가상의 후보 A는 문장이 자연스럽지만 근거 없는 질문에도 금액을 답하고, B는 간결하지만 근거 부족을 표시한다고 하자. 사용자가 원하는 것이 정책 조회라면 A를 고를 수 없다. 결정 기록에는 다시 비교할 조건까지 적는다. 이미지 입력이 필요해지거나 요청량이 크게 바뀌는 때가 그런 조건이다.
환각의 네 갈래
모델 소개의 지식 시점은 학습 데이터가 거기까지라는 뜻이고, 그 이전의 사실을 다 안다는 뜻이 아니다. 한울연구소의 비공개 문서가 들어 있을 리 없으니 필요한 정보의 출처와 시점은 우리가 관리한다.
이 책에서 환각은 사실이나 제공한 근거로 뒷받침되지 않는 출력을 가리킨다. 오류를 발견하면 한 단어로 묶지 않고 넷으로 나눠 기록한다. 고칠 자리가 다르기 때문이다.
| 갈래 | 예 | 볼 곳 |
|---|---|---|
| 문서에 없는 사실을 추가 | 없는 예외 조항을 설명한다 | 프롬프트·근거 검증 |
| 문서의 수치를 바꿈 | 30만 원을 20만 원으로 답한다 | 문맥 구성·모델 |
| 없는 출처를 붙임 | 존재하지 않는 문서 번호를 인용한다 | 인용 검증 |
| 도구 실패를 성공으로 표현 | 시간 초과인데 “신청 없음”이라고 답한다 | 도구 결과 반영 |
모델이 “확실하다”고 말하는 정도를 신뢰도로 쓰지 않는다. 그 표현과 실제 정답률의 관계를 재 본 적이 없으면 문체일 뿐이고, 토큰 확률도 문장이 사실일 확률과 다르다. 근거가 없을 때는 “모르겠다”로 끝내지 말고 어떤 조건이 빠졌는지, 어떤 자료로는 확인할 수 없었는지, 사람이 확인할 경로가 있는지를 알린다. 이때도 권한이 없는 문서의 제목이나 존재는 말하지 않는다.
샘플링 설정
모델이 다음 토큰 후보에 매긴 점수를 로짓이라고 한다. 로짓은 확률이 아니고, 온도 를 적용한 소프트맥스가 후보 의 확률을 만든다.
를 낮추면 큰 로짓의 비중이 커지고, 높이면 분포가 평평해진다. 은 나눗셈 때문에 이 식에 넣을 수 없어서, API가 온도 0을 받으면 그 구현이 무엇을 하는지 문서로 확인한다.
위 그림의 값을 계산하는 코드다. 표준 라이브러리만 쓴다.
import math
def probabilities(logits: list[float], temperature: float) -> list[float]:
if not logits or not all(math.isfinite(x) for x in logits):
raise ValueError("유한한 로짓을 하나 이상 입력해야 한다")
if not math.isfinite(temperature) or temperature <= 0:
raise ValueError("온도는 유한한 양수여야 한다")
peak = max(logits)
weights = [math.exp((x - peak) / temperature) for x in logits]
total = sum(weights)
return [weight / total for weight in weights]
for temperature in (0.5, 1.0, 2.0):
result = probabilities([2.0, 1.0, 0.0], temperature)
print(temperature, [round(p, 3) for p in result])
0.5 [0.867, 0.117, 0.016]
1.0 [0.665, 0.245, 0.09]
2.0 [0.506, 0.307, 0.186]
최댓값을 뺀 것은 지수 계산에서 큰 수를 만들지 않기 위해서고, 모든 로짓에 같은 값을 빼도 확률은 같다. 온도는 사실 확인 기능이 아니다. 근거 없는 후보가 가장 높은 점수를 받았다면 낮은 온도는 그 후보를 더 자주 고른다.
top-k는 확률 높은 후보를 k개만 남기고, top-p는 확률을 높은 순서로 더해 p를 넘기는 최소 집합을 남긴 뒤 다시 정규화한다. 온도 1.0에서 top-k 2는 앞의 둘을 남기고, top-p 0.8도 첫 후보(0.665)만으로는 0.8에 못 미쳐 둘째까지 남긴다. top-p 0.6이면 첫 후보만 남는다. 핵 샘플링을 제안한 연구가 고정 개수 대신 분포에 맞춰 집합을 바꾸는 이유를 다룬다. The Curious Case of Neural Text Degeneration
반복 제어는 이미 나온 토큰이 다시 뽑히는 경향을 줄인다. 문서 식별자나 정확한 용어는 여러 번 나와야 하므로 줄이는 게 항상 좋지는 않고, 같은 이름의 설정이라도 제공자마다 계산이 달라 수치를 그대로 복사하지 않는다. 가장 높은 후보만 고르는 탐욕적 선택도 서비스 전체의 재현성을 보장하지는 않는다. 문맥, 모델 버전, 검색 결과, 서버 환경이 바뀌면 답도 바뀐다. Transformers 생성 설정 문서
실습: 모델 선택 기록 준비하기
- 위 코드를
sampling.py로 저장하고 실행한다. 온도 0과 빈 목록은 오류가 나야 하고, 같은 로짓 셋은 각각 3분의 1이 나와야 하고, 모든 로짓에 100을 더해도 확률은 같아야 한다. - 교육비 문서로 네 조건을 만들고, 조건마다 기대 답을 모델 실행 전에 써 둔다. 결과를 본 뒤 기대 답을 바꾸면 시험을 모델에 맞추게 된다.
- 후보별 결정 기록을 만든다. 필수 조건, 질문 집합, 실행 횟수, 품질 기준, 시간 측정 범위, 비용 산정 방식, 다시 비교할 조건을 적고 결과 칸은 비워 둔다.
이 장의 결과물은 순위가 아니라, 어떤 조건에서 어떤 증거로 모델을 고를지 정한 실험 명세와 생성 설정을 읽을 수 있는 작은 계산이다.