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억 개의 숫자는 어떻게 만들어진 걸까? 다음 편에서 학습 과정을 다룬다.
이 글이 어떠셨나요?
관련 포스트
1. LLM은 텍스트를 어떻게 처리하는가
LLM이 텍스트 한 줄을 받아서 다음 토큰을 예측하기까지의 전체 과정을 4단계로 따라간다. 토크나이저, 임베딩, 트랜스포머 레이어, 출력까지 "OOM killer가 nginx를" 한 문장이 모델 안에서 겪는 여행.
2026. 03. 03. 오후 10:00DevOps5. 조립: 토큰이 쌓이고 레이어가 깊어지는 과정
어텐션과 FFN을 조립하는 과정
2026. 03. 31. 오후 10:00DevOps3. 어텐션: 새 토큰이 문맥을 얻는 과정
LLM 어텐션의 실제 계산 과정을 숫자로 추적한다. Q·K 내적이 주목도를 만들고, V의 가중합이 문맥을 만드는 과정. 멀티 헤드가 필요한 이유, 스케일링(√d), 잔차 연결까지.
2026. 03. 17. 오후 10:00뉴스레터 구독
새 글이 올라오면 이메일로 알려드려요.