로컬이나 사내 GPU에서 LLM을 서빙하다 보면 지연 시간에 먼저 발목이 잡힌다. 모델을 더 키우긴 어렵고 GPU를 더 붙이자니 비용이 걸린다. 이럴 때 손대볼 만한 게 추측 디코딩(speculative decoding)이고, vLLM은 EAGLE3를 포함해 몇 가지 방식을 설정 한 덩어리로 켤 수 있게 해준다.
결론부터 말하자면, speculative_config에 draft 모델과 num_speculative_tokens만 넣으면 추측 디코딩이 켜진다. 공개된 벤치마크에서 EAGLE3는 출력이 긴 작업에서 환경에 따라 다르지만 대략 2배 안팎의 처리량 이득을 보이고, 블록 병렬 방식인 DFlash와 DSpark는 동기 요청에서 그보다 더 큰 이득을 보고한다. 출력 분포는 바뀌지 않으므로 응답 품질은 그대로다.
1. 작은 모델이 먼저 쓰고 큰 모델이 검수한다
보통의 자기회귀 디코딩은 토큰 한 개를 만들 때마다 큰 모델의 forward를 한 번씩 돈다. 토큰 100개면 forward 100번이고, 이게 지연의 대부분을 차지한다.
추측 디코딩은 작고 빠른 draft 모델이 다음 토큰 여러 개를 미리 제안하게 한다. 그러면 큰 타깃 모델은 그 제안 토큰들을 한 번의 forward로 한꺼번에 검증한다. 검증을 통과한 토큰까지는 그대로 채택하고, 처음 틀린 지점부터 다시 제안받는다. 검증 단계가 원래 모델의 확률 분포를 그대로 따르기 때문에 출력 품질 손실은 없다.
EAGLE3(Extrapolation Algorithm for Greater Language-model Efficiency, 버전 3)는 draft로 별도의 작은 LLM을 두는 대신, 타깃 모델의 여러 중간 레이어에서 뽑은 hidden state를 입력받는 경량 헤드를 쓴다. 최종 레이어 하나만 쓰던 이전 세대보다 수용률(제안 토큰이 받아들여지는 비율)이 높아서, 같은 제안 길이에서도 더 많은 토큰을 한 번에 확정한다.
2. speculative_config로 EAGLE3 켜기
최근 vLLM(V1 엔진)이면 설치는 평소와 같다.
pip install vllm
서빙으로 띄울 때는 --speculative-config에 JSON을 넘긴다. method는 eagle3, model은 타깃에 맞춰 학습된 EAGLE3 체크포인트다.
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--speculative-config '{"method": "eagle3", "model": "yuhuili/EAGLE3-LLaMA3.1-Instruct-8B", "num_speculative_tokens": 3}'
오프라인 추론이면 파이썬에서 같은 내용을 딕셔너리로 넘긴다.
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
speculative_config={
"method": "eagle3",
"model": "yuhuili/EAGLE3-LLaMA3.1-Instruct-8B",
"num_speculative_tokens": 3,
},
)
out = llm.generate("파이썬으로 피보나치 함수를 작성해줘", SamplingParams(max_tokens=256))
print(out[0].outputs[0].text)
EAGLE3 체크포인트는 타깃 모델별로 따로 있다. yuhuili/EAGLE3-LLaMA3.1-Instruct-8B는 Llama 3.1 8B Instruct에 맞춰진 것이라, 다른 타깃에는 그 타깃용 체크포인트를 써야 한다. draft 헤드는 작지만 GPU 메모리를 추가로 차지하므로, 메모리가 빡빡하면 gpu_memory_utilization을 함께 조정한다.
처음 띄울 때 draft 모델의 아키텍처가 타깃과 맞지 않으면 로드 단계에서 바로 에러가 난다. 체크포인트 이름의 타깃 모델명이 --model과 같은 계열인지 먼저 확인하면 이 문제는 피한다.
3. num_speculative_tokens 값을 정하는 기준
num_speculative_tokens는 draft가 한 스텝에 제안하는 토큰 개수다. 값을 키우면 타깃 forward 한 번으로 확정할 수 있는 토큰이 늘어나 이론상 더 빠르지만, 뒤쪽 토큰일수록 수용률이 떨어진다. 제안했다가 버려지는 토큰이 많아지면 검증에 쓴 계산이 낭비된다.
3에서 5 사이로 시작해서 올리고 내리며 맞추는 쪽을 권한다. 8을 넘기면 이득을 보는 경우가 드물다. 코드 생성처럼 다음 토큰이 잘 예측되는 작업은 값을 조금 높여도 수용률이 버티고, 자유 서술형처럼 예측이 어려운 작업은 낮게 두는 편이 유리하다.
한 가지 더, 배치가 커져서 GPU가 이미 처리량 한계에 가까우면 추측 디코딩의 이득은 줄어든다. 추측 디코딩은 요청이 많지 않고 지연을 줄이는 게 목표인 상황에서 효과가 가장 크다.
4. EAGLE3와 DFlash, DSpark 중에 고르기
vLLM은 speculators 프로젝트를 통해 EAGLE3 말고도 블록 병렬 방식의 draft를 지원한다. 이름은 dflash와 dspark이고, method만 바꿔 끼우면 된다.
EAGLE3는 draft가 토큰을 하나씩 자기회귀로 제안한다. 공개 체크포인트가 비교적 많아 적용할 수 있는 타깃 모델 폭이 넓다. 쓸 수 있는 draft가 이미 있는 범용 선택지다.
DFlash는 작은 디퓨전 기반 draft가 비인과(non-causal) 어텐션으로 블록 전체를 한 번의 forward에 제안한다. 토큰을 순차로 뽑지 않아, 동기(저QPS) 요청에서 EAGLE3보다 2배에서 3배 큰 이득을 speculators 문서가 보고한다. 대신 전용 speculator 체크포인트가 필요하고 아직 지원 모델이 적다.
DSpark는 DFlash 위에 두 개의 헤드를 더 올린 방식이다. 블록 안 토큰 사이에 의존성을 되살리는 저랭크 Markov 헤드와, 각 위치의 수용 확률을 예측하는 confidence 헤드로 블록 후반부의 수용률 하락을 보강한다. 블록을 어디까지 검증할지 판단하는 데 이 신호를 쓴다.
정리하면, 타깃 모델에 맞는 DFlash나 DSpark 체크포인트가 있으면 저지연 서빙에서는 그쪽을 먼저 시도하고, 쓸 수 있는 전용 체크포인트가 없으면 체크포인트 선택지가 넓은 EAGLE3로 가는 게 현실적이다.
5. 토큰 생성 속도 재기
켜고 나면 숫자로 확인한다. 추측 디코딩을 끈 서버와 켠 서버를 각각 띄워 같은 벤치마크를 돌리고 출력 토큰 처리량을 비교하면 된다.
vllm bench serve \ --model meta-llama/Llama-3.1-8B-Instruct \ --dataset-name random \ --num-prompts 200
실행이 끝나면 결과에 Output token throughput (tok/s)가 찍힌다. 베이스라인 대비 이 값이 몇 배인지가 실제 이득이다. EAGLE3는 공개 벤치마크에서 출력이 긴 작업 기준 대략 2배에서 2.3배 수준을 보였고, 수치는 모델, 데이터셋, 배치 크기에 따라 달라진다. 짧은 응답이나 예측이 어려운 작업에서는 이득이 거의 없거나 오히려 느려질 수도 있어, 실제 트래픽과 닮은 데이터로 재는 게 중요하다.
수용률은 서버의 Prometheus 지표(/metrics)에 누적 수용 토큰 수로 나온다. 제안 대비 수용 비율이 낮게 나오면 num_speculative_tokens를 줄이거나 작업에 맞는 draft 체크포인트를 다시 고를 신호다.
먼저 EAGLE3로 베이스라인 대비 배수를 뽑아보고, 그 타깃에 DFlash나 DSpark 체크포인트가 있는지 speculators 쪽을 확인하면 다음 튜닝 지점이 잡힌다.