"다음 단어 예측"만으로는 부족하다 — LLM 추론 전 과정
← 전체 목차 · 이전: QKV를 1분 만에 이해하기 · 다음: 메모리를 삼키는 주범 — KV Cache
"대규모 모델이 추론 단계에서 무엇을 하는가"라는 질문에 "다음 단어를 예측한다"만 답하면 핵심 점수를 받지 못한다. 그것은 결론이지 답이 아니다. 요구되는 것은 과정이다 — 입력이 어떻게 행렬이 되고, 어떻게 token 단위로 순환하며, 어떻게 최적화하고, 어떻게 단어를 고르는가.
전처리 1: 분절(Tokenization)
모델은 한글도 한자도 이해하지 못한다. 그 세계에는 차가운 숫자 뭉치만 있다. 첫 단계는 "번역"이다 — 일상 언어를 작은 조각들로 자르는 것.
예: 안녕하세요, 대규모 모델 → 안녕하세요 / , / 대규모 / 모델 4개의 token. 구두점도 별도의 token을 차지한다.
전처리 2: 임베딩(Embedding)
잘라낸 조각들을 긴 숫자 벡터로 변환한다.
| Token | 벡터(앞 3차원만 표시) |
|---|
| 안녕하세요 | [0.2, -0.1, 0.9, ...] |
| , | [-0.5, 0.3, 0.1, ...] |
| 대규모 | [0.8, 0.4, -0.2, ...] |
| 모델 | [-0.1, 0.9, 0.5, ...] |
분절 + 임베딩 두 단계를 마치면 입력 전체가 거대한 수학 행렬이 되고, 그제서야 모델이 진짜로 "머리를 쓰기" 시작한다.
자기회귀 생성: 본질은 "끝말잇기"
한 문장을 한 번에 다 쓰는 것이 아니라 한 글자씩 밖으로 내보낸다.
프롬프트
└→ 층층이 순전파 → 다음 글자로 가장 가능성 높은 것을 추측
└→ "오"를 산출
└→ 프롬프트 + "오" → 다시 모델에 투입
└→ 다음 글자가 "늘"인지 추측
└→ 프롬프트 + "오늘" → …(계속 순환)
└→ 특정 【종료 토큰】을 생성하면 전체 과정 종료
- 핵심: 매 라운드의 입력 = 원래 프롬프트 + 이미 생성된 전부이므로 시퀀스는 점점 길어진다.
- 종료 조건은 모델 스스로 "이제 멈춰야 한다"고 판단하는 것, 즉 종료 토큰(EOS) 생성이지 외부에서 멈추라고 외치는 것이 아니다.
추론 최적화: KV Cache
자연히 따라오는 의문 — 한 글자 추측할 때마다 앞의 모든 내용을 다시 봐야 한다면 끔찍하게 느리지 않은가? 실제로 그렇다.
비유: 소설을 읽다가 두 번째 페이지에 왔을 때 첫 페이지를 처음부터 한 글자씩 다시 읽지는 않는다. 앞의 줄거리가 머릿속에 남아 있기 때문이다 — 처음부터 다시 읽을 필요가 없다.
메커니즘: 앞서 이미 계산한 주의력 행렬을 반복 계산하지 않도록, 계산해 둔 중간 데이터를 GPU 메모리에 저장한다.
본질: 전형적인 공간-시간 교환.
새 글자의 주의력 + 캐싱된 과거 데이터 → 다음 글자 출력
디코딩과 샘플링 전략
모델이 매 단계에서 실제로 산출하는 것은 수만 개 단어의 확률 분포다. 거대한 추첨 상자가 눈앞에 놓인 것과 같으며, 어떤 단어를 출력으로 고를지는 디코딩 전략이 결정한다.
| 디코딩 전략 | 방식 | 특징 |
|---|
| 그리디 서치(Greedy Search) | 항상 확률이 가장 큰 단어를 선택 | 경직됨, 앵무새 같음, 다양성 부족 |
| 샘플링(Sampling) | 확률에 따라 무작위 추출 | 더 자연스럽고 다양하며 반복을 피함 |
온도 파라미터(Temperature)
| 온도 | 표현 |
|---|
| 낮음 | 더 엄밀하고 결정성이 높음 |
| 높음 | 더 발산적이고 창의적이며, 동시에 정색하고 헛소리할 가능성도 커짐 |
핵심 통찰
- "다음 단어 예측"은 결론이지 답이 아니다 — 요구되는 것은 과정이다. 입력이 어떻게 행렬이 되고, 어떻게 token 단위로 순환하고, 어떻게 최적화하고, 어떻게 단어를 고르는가. 한 줄 결론을 네 단락의 파이프라인으로 펼쳐내는 것이 점수를 받는 자세다.
- 추론 단계는 한 번의 순전파가 아니라 상태를 가진 순환이다 — 매 라운드의 입력이 "원래 프롬프트 + 이미 생성된 내용"이므로 시퀀스는 단조 증가한다. 이 점을 이해하면 이후의 모든 성능 문제(길이에 따른 지연 증가, 길이에 따른 메모리 증가)를 스스로 도출할 수 있다.
- KV Cache의 본질은 "공간으로 시간을 사는" 고전적 엔지니어링 트레이드오프다 — 수학적 결과를 바꾸지 않고, 이미 계산한 K/V 중간량을 캐싱해 매 단계의 중복 계산을 "전체 시퀀스"에서 "새 token 하나"로 낮춘다. 최적화를 물으면 먼저 재사용 가능한 중간 결과가 있는지 생각할 것.
- "공간을 바꾼" 이상 공간이 새로운 병목이 된다 — 입력이 100만 token이면 KV Cache는 「시퀀스 길이 × 층수 × 주의력 헤드 수」에 비례해 팽창하고 메모리가 먼저 버티지 못한다. 장점에서 대응되는 대가를 스스로 도출할 수 있는지가 초급과 중급 답변의 분수령이다.
- 생성 품질 문제는 모델이 아니라 디코딩 전략에 있는 경우가 많다 — 모델이 출력하는 것은 확률 분포이며, "앵무새"나 "경직됨"은 말을 못 해서가 아니라 그리디 서치가 언제나 argmax를 취하기 때문이다. 문제를 진단할 때 **"분포 자체가 나쁜지" vs "분포에서 잘못 고르는지"**를 먼저 구분할 것.
- 온도는 "결정성 ↔ 창의성"의 다이얼이지 "똑똑함 ↔ 멍청함"의 다이얼이 아니다 — 온도가 높으면 창의성과 함께 환각 위험도 필연적으로 따라온다. 둘은 같은 뿌리다. 엄밀한 작업(추출, 분류, 코드)은 온도를 낮추고, 창작 작업에서만 높인다.
- 좋은 기술 표현은 비유에 기댄다 — "끝말잇기"로 자기회귀를, "소설 둘째 페이지에서 첫 페이지를 다시 읽지 않는다"로 KV Cache를, "거대한 추첨 상자"로 확률 분포를 설명한다. 현장에서 정확한 비유를 낼 수 있다는 것은 외운 것이 아니라 이해했다는 증거다.
엔지니어링 실전 Tips
- 추가 질문의 인터페이스를 스스로 건네줄 것: KV Cache를 설명하고 나서 "대가는 메모리다"를 먼저 보태면, 대화를 자신이 준비해 둔 메모리 병목·처리량 최적화 영역으로 끌어올 수 있다.
- 두 종류의 성능 지표를 구분할 것: 첫 token 지연(입력 전체의 계산량이 지배하는 prefill 단계)과 이후 token당 지연(메모리 대역폭이 지배하는 decode 단계). KV Cache가 주로 최적화하는 것은 후자다.
- 롱컨텍스트 시나리오에서는 메모리 계산부터:
KV Cache 크기 ≈ 시퀀스 길이 × 층수 × KV 헤드 수 × head_dim × 2(K와 V) × 정밀도 바이트 수. 시퀀스가 길어지면 모델 가중치 자체보다 커진다.
- 프로덕션 서비스의 기본 파라미터: 결정성 작업(정보 추출, SQL·코드 생성, 분류·태깅)은 낮은 온도 또는 그리디로, 카피·브레인스토밍은 높은 온도 + 샘플링으로. 같은 모델도 온도를 바꾸면 두 가지 인격이 된다.
- 모델이 "반복"하면 먼저 모델을 탓하지 말 것: 디코딩 설정이 순수 그리디이거나 온도가 지나치게 낮게 잡혀 있는지 먼저 확인하고, 그다음에 반복 페널티 같은 파라미터를 고려할 것.
- 분절 단계도 비용이다: 구두점 하나도 token 하나를 차지한다. 프롬프트의 체감 길이와 실제 token 수는 자주 어긋난다.