요즘 챗GPT 같은 대규모 언어 모델(LLM)의 능력을 보면 똑똑하다는 생각이 들다가도, 가끔 답변이 너무 느리게 나와 답답할 때가 많다. LLM, 성능은 좋은데 왜 이렇게 느린 걸까? 이 속도로는 실시간 서비스는 어림도 없겠다는 생각이 든다.
이런 고민을 해결해 줄 기술이 바로 Speculative Decoding이다. 큰 LLM의 성능은 그대로 유지하면서 추론 속도만 빠르게 만드는 방법이다. 심지어 모델을 추가로 학습시킬 필요도 없다. 이번 포스팅에서는 Speculative Decoding이 무엇인지, 어떤 원리로 속도 문제를 해결하는지 찾아본 내용들을 정리해보려 한다.
문제점 1: LLM은 왜 느릴까? - 한 글자 한 글자 생성하는 구조
LLM이 텍스트를 생성하는 방식을 보면 그 이유를 알 수 있다. 대부분의 LLM은 AutoRegressive(자기회귀적) Transformer라는 구조를 기반으로 한다. AutoRegressive는 쉽게 말해 "이전 단어들을 보고 다음 단어를 예측"하는 방식이다.
"The quick brown fox jumps" 라는 문장이 있다면,
- "The" 다음에 "quick"을 예측하고,
- "The quick" 다음에 "brown"을 예측하고,
- "The quick brown" 다음에 "fox"를 예측하고...
이런 식으로 한 토큰(단어 또는 글자)씩 순차적으로 생성한다. 이 때문에 병렬 처리가 어려워 추론 속도가 느려지는 근본적인 원인이 된다.
문제점 2: LLM은 왜 느릴까? - 쉬운 건데 굳이?
LLM이 생성하는 모든 토큰이 다 어려운 건 아니다. 어떤 토큰은 쉽게 예측할 수 있고, 어떤 토큰은 좀 더 깊은 고민이 필요하다.
예를 들어,
"What is the square root of 7? The square root of 7 is 2.646"
여기서 "The square root of 7 is" 다음에 오는 "2.646"을 예측하는 건 난이도가 좀 있다. 앞 문맥에서 반복되는 패턴도 없고, 실제로 계산을 해야 하기 때문. 이런 건 거대한 LLM이 실력 발휘를 해야 한다. (난이도: 상)
하지만, "The square root of 7"과 같이 문맥에서 반복되는 패턴이 있는 부분은 어떨까? 굳이 똑똑한 거대 LLM이 아니더라도, 좀 더 가벼운 모델도 충분히 잘 맞출 수 있다. (난이도: 하)
이런 쉬운 토큰까지 거대 LLM이 하나하나 다 처리하려니, 비효율이 발생하는 것.
문제점 3: LLM은 왜 느릴까? - 메모리 대역폭 병목 현상
LLM의 추론 속도를 제한하는 또 다른 주범은 바로 메모리 대역폭 병목 현상 (Memory Bandwidth Bottleneck) 이다.
- 메모리 대역폭이란? 초당 VRAM에서 GPU로 전송할 수 있는 데이터의 양을 의미한다.
- LLM의 거대한 가중치(모델 파라미터)는 VRAM에 저장된다.
- LLM이 한 토큰을 생성할 때마다, 이 가중치 전체를 VRAM에서 GPU로 읽어와야 한다. 모델 크기가 1TB라면, 매 토큰 생성 시 1TB의 데이터를 읽는 셈. (로딩과는 다른 개념. 이미 로딩된 데이터를 계속 읽어오는 과정.)
GPU의 연산 능력은 엄청나지만, Transformer 추론 시에는 1바이트당 고작 10번 정도의 연산만 수행한다. 즉, GPU는 데이터를 더 달라고 하지만 메모리에서 데이터를 가져오는 속도가 느려서 GPU가 놀게 되는 현상이 발생한다.
결국 LLM 추론이 느린 건 GPU 연산 능력 부족이 아니라, 메모리에서 가중치를 읽어오는 속도 때문인 경우가 많다. 메모리 문제만 해결하면 병렬 처리로 속도를 올릴 수 있다는 뜻이다. Speculative Decoding은 바로 이 지점을 파고든다.
Speculative Decoding이란 무엇인가?
Speculative Decoding은 마치 "똑똑하지만 조금 느린 교수님(큰 모델)"과 "엄청 빠르지만 가끔 실수하는 조교(작은 모델)"가 함께 일하는 방식과 같다.
- 조교(작은 모델)의 활약: 가볍고 빠른 작은 모델(근사 모델, Mq)이 먼저 다음 내용을 추측하여 여러 개의 후보 토큰(예: 5개)을 빠르게 생성한다. (AutoRegressive 방식이지만 모델이 작아서 매우 빠름)
- 교수님(큰 모델)의 검토: 똑똑한 큰 모델(타겟 모델, Mp)은 조교가 가져온 여러 개의 후보 토큰들을 한 번에 병렬적으로 검토한다. "음, 첫 번째, 두 번째, 세 번째는 맞는데, 네 번째는 틀렸군!"
- 결과 반영:
- 조교의 예측이 맞은 부분까지는 그대로 사용. (시간 절약)
- 틀린 부분부터는 교수님이 직접 "이게 정답이다." 하고 올바른 토큰을 생성.
이렇게 하면 어떤 장점이 있을까?
- 큰 모델 호출 횟수 대폭 감소: 예전에는 매 토큰마다 큰 모델을 불러야 했지만, 이제는 여러 토큰을 한 번에 처리하니 메모리 부담이 줄어든다. (메모리 대역폭 병목 완화)
- GPU 활용도가 높아진다: 큰 모델이 한 번 호출될 때 여러 토큰을 병렬로 검증하면서 더 많은 연산을 수행하게 된다. 즉, 메모리 읽기 시간 대비 연산량이 늘어나 GPU가 더 효율적으로 일하게 된다.
- 작은 모델은 원래 빠름: 작은 모델은 가중치 크기도 작고 계산도 빨라서 부담이 훨씬 적다.
결론적으로, 큰 모델의 정확도는 유지하면서, 작은 모델의 속도를 활용해 전체 추론 시간을 단축하는 것이 Speculative Decoding의 핵심!
Speculative Decoding 상세 과정
용어를 정리하자면, 다음과 같다.
- 큰 모델 (Mp, 타겟 모델): 우리가 원하는 고품질의 결과를 내는, 하지만 느린 메인 모델.
- 작은 모델 (Mq, 근사 모델): 크기가 작고 빨라서 추측을 담당하는 보조 모델.
- 감마 (γ): 작은 모델(Mq)이 한 번에 생성하는 예측 토큰의 개수.
프로세스 예시

- 시작! "[START]" 토큰을 입력으로 받는다.
- 작은 모델(Mq)의 제안: 작은 모델이 γ=5라고 가정하고, "japan’s benchmark bond"까지 5개의 토큰을 빠르게 생성한다.
[START] japan’s benchmark bond
- 큰 모델(Mp)의 병렬 검증: 큰 모델은 작은 모델이 생성한 각 토큰이 맞는지 병렬적으로 확인한다.
P([START] → japan)?P([START] japan → ’s)?P([START] japan’s → benchmark)?P([START] japan’s benchmark → bond)?P([START] japan’s benchmark bond → yields)? (마지막은 다음 토큰 예측)
- 승인(Accept) / 거부(Reject):
- 만약 작은 모델이 제안한 "japan’s benchmark bond"까지 모두 정답이라면 (초록색 ✅), 큰 모델은 이 4개 토큰을 모두 승인하고, 자신이 예측한 다음 토큰 "yields"까지 추가하여 총 5개의 토큰을 한 번의 호출로 얻게 된다.
- 만약 중간에 틀린 토큰이 있다면 (빨간색 ❌), 그 지점부터는 작은 모델의 제안을 거부한다. 예를 들어 "benchmark"까지는 맞았지만 "bond"가 틀렸다면, "japan’s benchmark"까지 3개는 승인하고, 틀린 "bond" 대신 큰 모델이 직접 올바른 토큰(파란색 🔵)을 생성하기 시작한다. 이 경우, 거부된 토큰 이후의 작은 모델 제안은 모두 버려진다.
여기서 중요한 포인트! 큰 모델은 이미 어떤 토큰들을 검증해야 할지 작은 모델로부터 전달받았기 때문에, 각 토큰에 대한 확률 계산을 병렬적으로 동시에 처리할 수 있다는 점. 이것이 바로 속도 향상의 핵심이다.
확률 분포 보정 (p'(x))
작은 모델의 예측이 거부되었을 때, 큰 모델은 자신의 원래 확률 분포를 그대로 사용하지 않고 수정된 확률 분포 p'(x)를 사용한다.
이는 작은 모델의 예측 실패가 전체 결과 품질을 해치지 않도록 방지하고, 큰 모델의 원래 분포를 최대한 유지하기 위함이다. "작은 모델이 실수한 부분을 감안해서 최종 답을 내라"는 의미다.
- 작은 모델(Mq)의 예측이 γ개만큼 모두 승인되면 (n=γ): 큰 모델(Mp)이 원래 예측했을 다음 토큰의 확률 분포
p'(x) = p_n+1(x)를 그대로 사용합니다. - 작은 모델(Mq)의 예측이 일부만 승인되면 (n<γ): 큰 모델(Mp)의 (n+1)번째 토큰 확률 분포에서 작은 모델(Mq)의 (n+1)번째 토큰 확률 분포를 보정한 값
p'(x) = norm(max(0, p_n+1(x) - q_n+1(x)))을 사용합니다.
그래서, 실제 성능은?
Google 연구에 따르면, Speculative Decoding을 사용했을 때 실제 경과 시간(Wall time)이 눈에 띄게 줄어들었다.

위 그래프에서 가장 아래에 있는 "Base" 막대가 일반적인 순차적 디코딩(AutoRegressive Decoding) 방식을 나타낸다. 초기 노란색 부분(M_p 인코더) 이후, 각 토큰이 생성될 때마다 상대적으로 긴 진한 보라색 블록(M_p 디코더)이 순차적으로 이어져 전체적인 Wall time이 길게 나타난다.
반면, 위 두 개의 Speculative Decoding 막대("γ=3", "γ=7")를 보자.
- 초기 인코더 부분(노란색 M_p 인코더와 짧은 주황색 M_q 인코더) 이후,
- 짧은 하늘색 블록들(M_q 디코더) 이 먼저 나타난다. 이것이 작은 모델(M_q)이 γ개의 후보 토큰을 빠르게 생성하는 부분이다.
- 그 다음, 상대적으로 큰 진한 보라색 블록(M_p 디코더) 이 한 번 나타나는데, 이것이 큰 모델(M_p)이 작은 모델이 제안한 γ개의 토큰을 한꺼번에 병렬적으로 검증하는 부분이다.
이 (하늘색 M_q 디코더 + 진한 보라색 M_p 디코더) 패턴이 반복되면서, "Base" 막대보다 전체 Wall time이 훨씬 짧아진 것을 명확히 볼 수 있다. 특히 γ 값이 클수록(예: γ=7일 때) 한 번의 큰 모델 호출로 더 많은 토큰을 처리할 수 있어 속도 향상 효과가 더 커지게 된다.
일반적으로 2배에서 3배까지의 속도 향상을 기대할 수 있다고 한다. (출처: Google AI Blog)
Speculative Decoding, 핵심 요약
- 문제: LLM은 한 글자씩 생성하는 방식과 메모리 병목 현상 때문에 느리다.
- 해결책: 가볍고 빠른 작은 모델이 먼저 예측하고, 큰 모델이 병렬로 검증한다.
- 효과: 큰 모델 호출 횟수 감소, GPU 효율 증가, 모델 추가 학습 없이 속도 향상, 품질 유지
Speculative Decoding은 LLM의 느린 추론 속도를 해결하는 효율적인 방법이다. 앞으로 더 빠르고 쾌적한 LLM 서비스를 가능하게 할 기술임이 분명하므로, 알아둘 필요가 있다.
참고 자료:
- Leviathan, Y., Meng, M., Fitch, J., Polyak, A., Baevski, A., Adi, Y., & Chen, Y. (2022). Fast Inference from Transformers via Speculative Decoding. arXiv preprint arXiv:2211.17192. (https://arxiv.org/abs/2211.17192)
- Kim, C., Lee, S., Lee, G., Kim, S., & Kim, J. (2023). Accelerating Large Language Model Decoding with Speculative Sampling. arXiv preprint arXiv:2302.01318. (DeepMind, 이 논문은 "Speculative Sampling"으로 약간 다른 변형을 제안하지만, 기본 아이디어는 유사합니다.) (https://arxiv.org/abs/2302.01318)
- Google Research Blog. (2023). Looking back at Speculative Decoding. (https://research.google/blog/looking-back-at-speculative-decoding/)
- Hugging Face Blog. (2023). Assisted Generation: a new way to speed up inference for large language models. (https://huggingface.co/blog/assisted-generation)
- Noting11 Tistory Blog. Speculative Decoding. (https://noting11.tistory.com/6)
'LLM' 카테고리의 다른 글
| llama.cpp 를 활용해 sLLM 테스트 하기 (0) | 2025.12.24 |
|---|