DevOps··2분 읽기·0

7. 출력 제어: 확률에서 토큰을 고르는 방법

LLM이 확률 분포에서 토큰을 고르는 방법. temperature, top_p, top_k가 각각 뭐고 언제 쓰는지.

글꼴

6편에서 "종료시켰다"가 23%로 가장 높은 확률을 받았다. 근데 항상 가장 높은 확률을 고르는 건가? 그러면 같은 프롬프트에 매번 같은 답이 나와야 하는데, 실제로는 그렇지 않다. 같은 질문을 두 번 하면 대답이 조금씩 다르다.

이번 편에서는 확률 분포에서 실제로 토큰을 어떻게 고르는지, 그리고 모델이 언제 출력을 멈추는지를 다룬다.


temperature — 확률 분포의 날카로움을 조절한다

같은 모델인데 매번 답이 다른 이유가 이거다.

모델 출력 확률 분포:
  "종료시켰다"  23%
  "죽였다"      15%
  "메모리"      11%
  "해당"         8%
  ...
 
-- temperature --
확률 분포의 날카로움을 조절.
 
  temperature=0      temperature=0.7      temperature=1.5
  종료시켰다 ████████  종료시켰다 ██████    종료시켰다 ████
  죽였다     ░░░░░░  죽였다     ████      죽였다     ███
  메모리     ░░░░░░  메모리     ███       메모리     ███
  해당       ░░░░░░  해당       ██        해당       ██
 
  0:    가장 높은 확률만 선택 (결정적, 매번 같은 답)
  0.7:  기본값. 약간의 랜덤성 (자연스러움)
  1.5:  평탄하게 -> 의외의 토큰도 선택 (창의적, 횡설수설 위험)

temperature가 0이면 매번 "종료시켰다"만 나온다. 0.7이면 대부분 "종료시켰다"가 나오지만 가끔 "죽였다"나 "메모리"가 나올 수도 있다. 1.5면 거의 랜덤에 가깝다.


top_p와 top_k — 후보를 제한한다

temperature만으로는 부족할 때가 있다. 확률이 0.001%인 완전 엉뚱한 토큰이 선택되는 걸 막고 싶을 때 쓴다.

top_p (nucleus sampling): 확률 상위 몇 %까지만 후보로 남기고 나머지를 제거한다. top_p=0.9면 확률 누적 90%까지만 후보다. top_p=0.5면 상위 50%만 남긴다. 안전하지만 다양성이 줄어든다.

top_k: 상위 k개 토큰만 후보로 남긴다. top_k=50이면 상위 50개 토큰에서만 샘플링한다. top_k=1이면 temperature 0과 같다(greedy decoding).

실무에서 이걸 어떻게 쓰는지 정리하면 이렇다.

태스크              temperature   top_p    비고
----------------   ----------   ------   --------------
로그 분석            0 ~ 0.1     0.9      정확성 최우선
코드 생성            0 ~ 0.2     0.95     정확성 + 약간의 유연성
블로그 글 작성       0.7 ~ 0.8   0.95     자연스러움 + 창의성
브레인스토밍         0.9 ~ 1.2   0.95     다양한 아이디어

로그 분석처럼 정확성이 중요한 태스크에서는 temperature를 0에 가깝게 쓴다. 매번 같은 답이 나오더라도 정확한 게 낫다. 블로그 글처럼 자연스러움이 중요하면 0.7~0.8 정도가 적당하다.


정지 조건 — 모델이 언제 출력을 멈추는가

토큰 생성은 루프다. 언제 멈출까?

생성 루프:
 
  토큰 생성 -> 다음 토큰 생성 -> ... -> 언제 멈춤?
 
  [1] EOS 토큰:
      모델이 <|im_end|> 또는 <eos> 토큰을 생성하면 종료.
      학습 때 문장 끝에 이 토큰이 있었으므로 "여기서 끝"을 학습.
 
  [2] max_tokens:
      미리 정한 최대 생성 수에 도달 (예: 2048).
      안전장치. 없으면 무한 생성 가능.
 
  [3] stop sequence:
      특정 문자열이 나오면 강제 종료.
      예: "###", "Human:" 등.

EOS 토큰이 가장 자연스러운 종료 방식이다. 모델이 "할 말 다 했다"고 판단하면 EOS의 확률이 올라간다. max_tokens는 안전장치다. 모델이 EOS를 생성하지 않고 계속 이어쓰는 경우를 방지한다.


대화 형식 — 실제로 모델에 들어가는 토큰

ollama에서 대화할 때, 사용자가 보는 건 채팅 UI지만 모델이 실제로 보는 건 하나의 긴 토큰 시퀀스다.

ollama에서 대화할 때 실제 모델이 보는 것 (Qwen2.5 기준):
 
<|im_start|>system
너는 시스템 엔지니어 전문가야.<|im_end|>
<|im_start|>user
이 로그에서 이상한 점을 찾아줘: [ERROR] OOM killer...<|im_end|>
<|im_start|>assistant
                                <- 여기부터 모델이 생성 시작
 
이 전체가 하나의 토큰 시퀀스.
<|im_start|>, <|im_end|>는 특수 토큰 (토크나이저에 등록).
모델은 "assistant" 뒤의 다음 토큰부터 생성.
<|im_end|>가 생성되면 답변 종료.

대화가 여러 턴 이어지면 이 시퀀스가 점점 길어진다. 이전 턴의 대화 내용이 전부 입력에 포함된다. 그래서 대화가 길어지면 토큰 수가 늘어나고, KV 캐시가 커지고, VRAM을 더 많이 먹고, 결국 느려진다.

여기까지가 "모델이 확률 분포에서 토큰을 고르고, 언제 멈추는지"에 대한 이야기다. 지금까지 1~7편에서 입력부터 출력까지의 전체 추론 과정을 다뤘다. 그런데 이 모든 과정을 가능하게 하는 파라미터, 그 30억 개의 숫자는 어떻게 만들어진 걸까? 다음 편에서 학습 과정을 다룬다.

이 글이 어떠셨나요?

이 글이 도움이 되셨나요?
공유:

관련 포스트

뉴스레터 구독

새 글이 올라오면 이메일로 알려드려요.

댓글

댓글을 불러오는 중...