Field Log · Entry
LLM 추론 입문: Prefill·Decode·KV Cache·Temperature 이해하기 (9/10)
오늘의 결론
- LLM 요청은 긴 입력을 처리하는 prefill과 새 token을 한 개씩 만드는 decode로 나뉩니다.
- KV cache는 이전 token의 attention key·value를 layer별로 저장해 decode 때 재계산을 줄입니다.
- temperature와 top-p는 모델 지식을 바꾸지 않고 logits에서 token을 고르는 분포를 바꿉니다.
- RAG가 넣는 문서가 길수록 prefill 시간과 KV cache 메모리가 커집니다. 관련 없는 context는 품질뿐 아니라 시스템 비용도 만듭니다.
API 응답 시간이 느릴 때 “모델이 느리다”만으로는 해결하기 어렵습니다. 검색이 오래 걸린 것인지, 첫 token 전의 prefill이 긴 것인지, token 생성 속도가 느린 것인지 분리해야 합니다.
LLM inference는 크게 두 계산 성격을 가집니다. prompt 전체를 한꺼번에 읽는 prefill과, 그 결과를 cache에 두고 다음 token을 순차 생성하는 decode입니다.
그림 1. prefill은 입력 token 전체의 K·V를 만들고, decode는 cache를 재사용하면서 새 token의 K·V만 추가한다.
요청이 모델에 도달하기 전
채팅 요청은 보통 다음 단계를 거칩니다.
system message
+ conversation history
+ user question
+ retrieved documents
+ tool results
→ chat template
→ tokenizer
→ input token IDs
→ model
API에 보낸 JSON 구조가 그대로 Transformer에 들어가는 것이 아닙니다. model별 chat template이 역할 구분 token과 control token을 추가하고 tokenizer가 ID 배열로 바꿉니다.
그래서 같은 보이는 문장이라도 model과 template에 따라 input token 수가 달라질 수 있습니다.
Prefill: Prompt 전체를 읽는 단계
prefill은 입력 sequence의 모든 token을 Transformer에 통과시킵니다.
input tokens: [system, question, evidence 1, evidence 2, ...]
└──────────── prefill ────────────┘
causal mask를 쓰지만 학습 때처럼 여러 prompt 위치의 행렬 연산을 GPU에서 병렬로 처리할 수 있습니다. 각 layer에서 모든 입력 token의 key와 value가 만들어지고 KV cache에 저장됩니다.
prefill은 대체로 연산량이 많은 compute-bound 성격을 보입니다. prompt가 길면 첫 token이 나오기 전 시간이 늘어납니다.
Decode: 한 Token씩 생성하는 단계
prefill이 끝나면 마지막 위치의 logits에서 첫 output token을 고릅니다. 그 token을 입력에 추가해 다음 token을 계산하고 반복합니다.
prompt → token₁ → token₂ → token₃ → ... → EOS
decode step마다 한 token
각 step은 이전 token 선택이 끝나야 시작할 수 있어 순차적입니다. 한 번에 처리하는 새 token 수는 작지만 model weight와 KV cache를 계속 읽어야 하므로 memory bandwidth의 영향을 크게 받을 수 있습니다.
TTFT와 TPOT를 나눠 보자
사용자 체감 latency를 두 부분으로 나눌 수 있습니다.
| 지표 | 뜻 | 크게 영향을 주는 것 |
|---|---|---|
| TTFT | Time To First Token | retrieval, queue, prompt 길이, prefill |
| TPOT | Time Per Output Token | model 크기, batch, cache, serving engine |
| total latency | 요청부터 마지막 token까지 | TTFT + output token 수 × TPOT |
RAG에서 검색 시간이 500ms이고 prefill이 800ms라면 첫 token까지 최소 1.3초 이상 걸립니다. 반면 답을 500 tokens 생성하면 TPOT의 작은 차이가 전체 latency를 크게 벌릴 수 있습니다.
streaming은 첫 token 이후 내용을 바로 보여 주어 체감 시간을 줄이지만 총 compute를 없애지는 않습니다.
KV Cache는 무엇을 저장하는가
attention에서 이전 token들의 K와 V는 다음 decode step에서도 같습니다. 매번 다시 계산하지 않고 저장해 둡니다.
step 1 cache: K,V(prompt tokens)
step 2 cache: K,V(prompt tokens + token₁)
step 3 cache: K,V(prompt tokens + token₁ + token₂)
새 step에서는 새 token의 query를 만들고, cache의 모든 이전 key와 score를 계산해 value를 읽습니다. 새 token의 K·V만 cache 뒤에 추가합니다.
KV cache는 다음 항목에 대략 비례해 커집니다.
batch × layers × cached tokens × KV heads × head dimension × bytes
정확한 크기는 multi-query/grouped-query attention, precision, sliding window 같은 architecture에 따라 달라집니다.
KV Cache는 대화 기억이 아니다
이름 때문에 application memory와 혼동하기 쉽습니다.
- KV cache: 현재 inference의 attention 연산 재사용을 위한 tensor
- conversation history: 이전 메시지를 다시 prompt로 넣은 token
- agent memory: DB나 파일에 저장한 장기 상태
- model parameter: training으로 학습해 checkpoint에 저장한 weight
요청이 끝나 cache를 버리면 모델이 대화를 영구 기억하는 것이 아닙니다. 같은 대화를 이어가려면 history를 다시 보내거나 serving system이 해당 prefix cache를 유지해야 합니다.
Prefill과 Decode에서 Cache가 주는 이점
cache가 없다면 output token을 만들 때마다 prompt부터 현재 sequence 전체의 K·V를 반복 계산해야 합니다.
cache 없음:
step 1 → 길이 N 전체 계산
step 2 → 길이 N+1 전체 계산
step 3 → 길이 N+2 전체 계산
cache 있음:
prefill → 길이 N 계산·저장
step 1 → 새 위치만 계산
step 2 → 새 위치만 계산
다만 cache가 이전 token과의 attention score 계산까지 없애는 것은 아닙니다. 새 query는 여전히 이전 key들과 비교해야 합니다. cache는 이전 token의 K·V projection과 layer 계산을 재사용합니다.
Temperature는 Logit을 나눈다
temperature T는 softmax 전에 logits를 나눕니다.
P(token_i) = softmax(logit_i / T)
T < 1: 큰 logit의 우위를 확대, 분포가 뾰족T = 1: 원래 softmaxT > 1: 분포가 평평, 다양한 token 가능성 증가
예를 들어 logits가 [2, 1, 0]이면:
| temperature | 대략적인 분포 | 성격 |
|---|---|---|
| 0.5 | [0.87, 0.12, 0.02] | 매우 집중 |
| 1.0 | [0.67, 0.24, 0.09] | 기본 |
| 2.0 | [0.51, 0.31, 0.19] | 더 평평 |
temperature는 model parameter나 지식을 바꾸지 않습니다. 현재 step의 선택 분포만 조절합니다.
Greedy, Top-k, Top-p
Greedy
가장 큰 logit token을 고릅니다. sampling random이 없지만, backend와 병렬 연산의 비결정성까지 포함해 모든 환경에서 byte-for-byte 동일하다고 보장되는 것은 별도 문제입니다.
Top-k
확률이 높은 k개 token만 남기고 그 안에서 sampling합니다. 후보 수가 고정입니다.
Top-p, Nucleus sampling
확률을 높은 순서로 더해 누적 합이 p 이상이 되는 최소 후보 집합을 남깁니다.
probabilities: [0.50, 0.25, 0.12, 0.08, 0.05]
top_p = 0.80
→ [0.50, 0.25, 0.12]까지 유지 후 재정규화
분포가 확실하면 후보가 적고, 불확실하면 후보가 많아지는 동적 방식입니다. temperature와 top-p를 함께 쓰면 둘 다 분포를 바꾸므로 한 번에 여러 값을 크게 조정하지 말고 task set으로 비교해야 합니다.
RAG 답변에서는 Sampling을 어떻게 볼까
사실 기반 QA는 창작보다 안정성과 재현성이 중요합니다. 하지만 “temperature 0이면 factual”이라는 공식은 없습니다.
- 잘못된 evidence가 들어오면 greedy도 틀림
- 낮은 temperature는 표현 다양성을 줄일 뿐 근거 검증을 하지 않음
- structured output은 schema enforcement와 validation이 별도 필요
- 답변 품질은 retrieval, prompt, model, decoding의 결합
운영에서는 설정을 고정하고 동일한 evaluation set으로 변경 전후를 비교합니다.
RAG Context가 Inference 비용이 되는 방식
검색 문서 10개를 모두 넣으면 model은 그 token 전체를 prefill해야 합니다.
retrieved context 증가
→ input tokens 증가
→ prefill 연산 증가
→ KV cache 증가
→ TTFT 증가 가능
→ 중요 evidence가 묻힐 가능성
따라서 top-k를 크게 하는 것은 공짜 recall 개선이 아닙니다. retrieval recall, reranker precision, context token budget, TTFT와 answer quality를 함께 봐야 합니다.
중복 chunk 제거와 reranking은 생성 품질뿐 아니라 serving 비용 최적화이기도 합니다.
Batching과 Throughput
GPU는 여러 요청을 함께 처리할 때 utilization이 좋아질 수 있습니다. 하지만 sequence 길이와 도착 시간이 다르므로 고정 batch만으로는 낭비가 생깁니다.
LLM serving engine은 continuous batching, paged KV cache 관리 등을 사용해 진행 중인 요청에 새 요청을 동적으로 합칩니다. vLLM의 PagedAttention은 운영체제의 paging에서 영감을 받아 동적으로 자라는 KV cache의 fragmentation과 낭비를 줄이는 방향을 제시했습니다.
여기서 두 목표를 구분합니다.
- Latency: 사용자 한 요청이 얼마나 빨리 끝나는가
- Throughput: 단위 시간에 전체 token/요청을 얼마나 처리하는가
batch를 키워 throughput이 좋아져도 queue 때문에 개별 latency가 나빠질 수 있습니다.
10분 실습: RAG 요청 Token 예산 기록하기
API가 usage를 반환한다면 query별로 다음을 로그에 남깁니다.
{
"retrieval_ms": 142,
"rerank_ms": 87,
"input_tokens": 4820,
"output_tokens": 312,
"ttft_ms": 940,
"total_ms": 4210,
"retrieved_chunks": 8,
"used_chunks": 4
}
그다음 input tokens와 TTFT, output tokens와 total latency의 관계를 그려 보세요. “느리다”가 retrieval 병목인지 prefill 병목인지 decode 병목인지 훨씬 선명해집니다.
Hugging Face 계열의 작은 로컬 모델에서는 generation 설정을 다음처럼 실험할 수 있습니다.
output = model.generate(
**inputs,
max_new_tokens=128,
do_sample=True,
temperature=0.7,
top_p=0.9,
use_cache=True,
)
같은 prompt를 여러 번 생성해 답의 사실성, 형식 준수, 변동성을 함께 기록합니다.
자주 하는 오해
“KV cache가 있으면 context 길이 비용이 사라진다.”
prefill은 여전히 전체 prompt를 처리하고 cache 메모리는 token 수에 따라 증가합니다. decode의 반복 계산을 줄이는 장치입니다.
“temperature가 낮으면 model이 더 많이 안다.”
지식과 parameter는 그대로입니다. 후보 분포에서 더 보수적으로 선택할 뿐입니다.
“첫 token이 빨리 나오면 전체 inference도 빠르다.”
TTFT와 TPOT, output 길이가 다릅니다. streaming UI만 보고 전체 compute와 비용을 판단하면 안 됩니다.
스스로 확인하기
- prefill과 decode는 각각 어떤 token들을 한 번에 처리하는가?
- KV cache에는 무엇이 저장되고 무엇이 저장되지 않는가?
- temperature 0.5와 2.0은 같은 logits 분포를 어떻게 바꾸는가?
- RAG top-k를 늘릴 때 품질 외에 어떤 시스템 비용이 증가하는가?
다음 글에서는 지금까지의 부품을 연결해 문서 수집부터 답변과 평가까지 첫 RAG pipeline을 구현합니다.