generate()에 assistant_model 하나만 넘기면 투기적 디코딩이 켜지는데, 막상 어떤 작은 모델을 붙여야 할지, 미리 쓸 토큰 수 γ는 몇으로 둘지에서 막히는 경우가 많아요. 결론부터 말하면 초안은 목표 모델과 토크나이저가 같고 파라미터가 1/10 이하인 같은 계열 모델에서 고르고, γ는 감으로 정하지 말고 실제 프롬프트로 잰 수락률 α와 비용 비율 c로 계산해서 정하면 돼요. 계산이 번거로우면 Transformers 4.45.0부터 기본값이 된 동적 γ를 그대로 두는 편이 대부분 고정값보다 나아요.
이 글은 가장 기본인 별도 초안 모델 방식 하나를 골라 쓰는 법에 집중해요. 원 논문 실측값과 공식 식으로 초안 크기·γ·메모리·GPU 비용을 직접 계산하고, 두 숫자를 내 환경에서 재는 코드까지 담았어요. EAGLE·MTP·n-gram 같은 방식 비교와 배치별 손익은 투기적 디코딩 방식 고르기, EAGLE·MTP·n-gram 속도와 GPU 비용 비교에 따로 정리해 두었어요.
초안 모델 속도를 정하는 세 숫자 α·c·γ
투기적 디코딩 한 라운드는 초안 모델이 γ개 토큰을 차례로 쓰고, 목표 모델이 그 γ개를 순전파 한 번으로 채점하는 구조예요. 여기서 고르는 사람이 손댈 수 있는 숫자는 셋이에요.
- α (수락률): 초안 토큰 하나가 목표 모델 검증을 통과할 확률이에요. 모델 짝, 작업 종류, 샘플링 온도가 정해요.
- c (비용 비율): 초안을 한 번 돌리는 시간 ÷ 목표를 한 번 돌리는 시간이에요. Leviathan 외(2022) 원 논문은 α가 모델과 작업의 성질인 것과 달리 c는 하드웨어와 구현에 따라 달라진다고 적었어요.
- γ (초안 길이): 한 라운드에 초안이 미리 쓰는 토큰 수예요. 세 숫자 중 유일하게 설정으로 바꾸는 값이에요.
논문의 정리 3.8이 셋을 하나로 묶어요. 속도 향상 = (1 − α^(γ+1)) ÷ ((1 − α)(γc + 1)) 예요. 분자는 검증 한 번에 평균 몇 토큰을 얻는지, 분모는 그 대가로 초안을 γ번 돌린 시간까지 더한 비용이에요. γ = 1일 때 식이 (1 + α) ÷ (1 + c)가 되므로 α가 c보다 커야 조금이라도 빨라져요. 논문 따름정리 3.9도 같은 조건을 밝혀요.
토큰마다 수락 확률이 같다고 가정한 근사지만, 논문 부록 표 4에서 예측과 실측이 대체로 맞았어요(T5-small 예측 3.2배, 실측 3.4배).
초안이 클수록 느려지는 이유 — 원 논문 실측
초안 모델을 고를 때 가장 흔한 착각은 "목표 모델과 비슷하게 말할수록 좋다"예요. 원 논문 표 2와 부록 표 4는 이걸 정면으로 반박해요. 목표는 T5-XXL(11B), 영어→독일어 번역, TPU-v4 한 장, 배치 1, 온도 0이에요.
| 초안 모델 | 파라미터 비율 | 실측 c | α | γ | 속도 |
|---|---|---|---|---|---|
| T5-small (77M) | 0.70% | 0.02 | 0.75 | 7 | 3.4배 |
| T5-base (250M) | 2.27% | 0.04 | 0.80 | 7 | 2.8배 |
| T5-large (800M) | 7.27% | 0.11 | 0.82 | 7 | 1.7배 |
파라미터 비율은 초안 파라미터 ÷ 11B로 직접 계산했고, c는 논문이 프로파일러 추적으로 잰 값이에요. 초안을 10배 키워 α를 0.07 올렸지만 c가 5.5배 커져서 속도가 절반이 됐어요.
여기서 규칙이 하나 나와요. c는 파라미터 비율보다 크게 나와요. 작은 모델도 층마다 커널을 차례로 실행하는 고정 비용이 있어서 크기만큼 시간이 줄지 않기 때문이에요. 그래서 층 수도 봐야 해요. 예를 들어 SmolLM-135M은 30층으로, 목표인 SmolLM-1.7B(24층)보다 층이 많아요(Hugging Face 모델 설정 파일 기준). 이런 깊고 좁은 초안은 c가 기대보다 크게 나올 수 있어요.
논문의 결론도 이 균형을 가리켜요. 같은 구조의 기성 모델을 초안으로 썼을 때 목표보다 두 자릿수(약 100배) 작은 초안이 대체로 가장 좋았고, 그 정도 크기에서 c는 늘 0.05 미만이었다고 적었어요. 그런 초안의 α는 0.5~0.9 사이였어요. Hugging Face의 Assisted Generation 블로그 글은 조금 느슨하게 "적어도 한 자릿수(10배) 작게, 차이가 클수록 좋다"고 권해요.
최적 γ 계산표 — 수락률과 비용 비율로 정하기
γ를 늘리면 검증 한 번에 얻을 수 있는 토큰이 늘지만, 그 증가분은 α^γ로 빠르게 줄어요. 반면 초안 비용 γc는 γ에 비례해 계속 커져요. 그래서 α·c마다 속도가 가장 높은 γ가 하나 있어요. 위 식에 γ = 1~20을 넣어 가장 빠른 값을 찾았어요.
| α \ c | c = 0.02 | c = 0.05 | c = 0.1 | c = 0.2 |
|---|---|---|---|---|
| 0.5 | γ 4 → 1.79배 | γ 3 → 1.63배 | γ 2 → 1.46배 | γ 1 → 1.25배 |
| 0.6 | γ 6 → 2.17배 | γ 4 → 1.92배 | γ 3 → 1.67배 | γ 2 → 1.40배 |
| 0.7 | γ 8 → 2.76배 | γ 6 → 2.35배 | γ 4 → 1.98배 | γ 3 → 1.58배 |
| 0.8 | γ 11 → 3.82배 | γ 8 → 3.09배 | γ 6 → 2.47배 | γ 4 → 1.87배 |
| 0.9 | γ 19 → 6.37배 | γ 13 → 4.67배 | γ 10 → 3.43배 | γ 7 → 2.37배 |
초안이 무거울수록(오른쪽) 최적 γ가 줄고, 잘 맞을수록(아래) 늘어요. 꼭대기 근처는 평평해서 α 0.7·c 0.02에서 γ 5는 2.67배로 최적(2.76배)과 3% 차이예요. α 0.7·c 0.02에서 γ 5는 2.67배로 최적(2.76배)과 3% 차이예요.
위험한 쪽은 반대 방향이에요. c가 큰 초안에 γ를 크게 주면 속도가 1배 아래로 떨어질 수 있어요.
c 0.2에서 γ 12는 0.97배로 끄는 편이 빨라요. 같은 γ라도 초안 무게에 따라 결과가 갈려요.
표로 보기
| 구분 | c 0.02 | c 0.1 | c 0.2 |
|---|---|---|---|
| 1 | 1.7 | 1.6 | 1.4 |
| 2 | 2.1 | 1.8 | 1.6 |
| 3 | 2.4 | 1.9 | 1.6 |
| 4 | 2.6 | 2 | 1.5 |
| 5 | 2.7 | 2 | 1.5 |
| 6 | 2.7 | 1.9 | 1.4 |
| 7 | 2.8 | 1.9 | 1.3 |
| 8 | 2.8 | 1.8 | 1.2 |
| 9 | 2.8 | 1.7 | 1.2 |
| 10 | 2.7 | 1.6 | 1.1 |
| 11 | 2.7 | 1.6 | 1 |
| 12 | 2.7 | 1.5 | 0.97 |
목표 모델이 8B이고 초안이 1B급이면 파라미터 비율만 15%예요(Llama 3.1 8B 약 8.03B, Llama 3.2 1B 약 1.24B — 공개 설정 파일로 계산). 위 T5 사례처럼 c가 파라미터 비율보다 크게 나온다면 c는 0.15를 넘는다고 보는 편이 안전하고, 그러면 표의 오른쪽 끝 열에 해당해요. 1B급 초안이 8B 목표에서 큰 이득을 못 내는 경우가 많은 이유가 이 계산에 그대로 드러나요.
고정 γ 대신 동적 γ — Transformers 기본값
고정 γ는 평균값일 뿐, 실제 문장에는 쉬운 구간과 어려운 구간이 섞여 있어요. 원 논문도 라운드마다 γ를 맞히는 오라클이 있다면 고정 γ보다 최대 약 60% 빨라질 수 있다고 계산했어요.
Transformers는 이 아이디어를 기본값으로 넣었어요. Intel·Hugging Face의 동적 투기적 디코딩 글에 따르면 4.45.0부터 초안이 토큰을 하나 쓸 때마다 자기 확신도(softmax 확률)를 보고, 그 값이 assistant_confidence_threshold(기본 0.4) 아래로 내려가면 num_assistant_tokens(기본 최대 20)에 닿기 전이라도 멈추고 바로 검증을 맡겨요. 같은 글에 실린 RTX 4090, 온도 0 측정값이에요.
c가 작은 짝(OPT 6.7B + 125M, 파라미터 비율 약 2%)은 어느 방식이든 이득이 컸지만, Llama 8B + 1B처럼 무거운 짝은 휴리스틱으로 1.00배였다가 동적으로 바꿔야 1.15~1.52배가 나왔어요. 초안이 무거울수록 γ를 똑똑하게 정하는 가치가 커져요.
고정값으로 돌아가고 싶으면 블로그 글이 안내한 대로 초안 모델의 생성 설정을 바꿔요.
# 동적 γ(기본): 확신도 0.4 아래면 멈춤, 최대 20토큰
assistant_model.generation_config.assistant_confidence_threshold = 0.4
assistant_model.generation_config.num_assistant_tokens = 20
# 계산표에서 고른 고정 γ로 비교할 때
assistant_model.generation_config.num_assistant_tokens_schedule = "constant"
assistant_model.generation_config.assistant_confidence_threshold = 0
assistant_model.generation_config.num_assistant_tokens = 4
Transformers 생성 설정 문서에 따르면 heuristic은 초안이 모두 맞으면 γ를 2 늘리고 틀리면 1 줄이며, 바뀐 값이 다음 generate() 호출로 이어져요. 요청마다 처음 값으로 되돌리려면 heuristic_transient를 써요.
내 환경에서 α와 c를 재는 코드
표를 쓰려면 내 GPU와 내 프롬프트에서 α와 c를 알아야 해요. 아래 코드는 그리디(온도 0) 기준으로 두 숫자를 재고 최적 γ까지 계산해요. 그리디에서는 초안과 목표의 확률 분포가 각자 가장 높은 토큰 하나로 모이므로, α는 "목표가 실제로 고른 토큰을 초안도 1순위로 골랐는가"의 비율과 같아요.
import time, torch
from transformers import AutoModelForCausalLM, AutoTokenizer
TARGET, DRAFT = "HuggingFaceTB/SmolLM-1.7B", "HuggingFaceTB/SmolLM-135M"
tok = AutoTokenizer.from_pretrained(TARGET)
assert tok.get_vocab() == AutoTokenizer.from_pretrained(DRAFT).get_vocab(), "어휘가 달라요 → UAD 경로 필요"
target = AutoModelForCausalLM.from_pretrained(TARGET, dtype="auto", device_map="auto")
draft = AutoModelForCausalLM.from_pretrained(DRAFT, dtype="auto", device_map="auto")
def sync(t):
if t.is_cuda:
torch.cuda.synchronize()
@torch.no_grad()
def step_time(model, ids, n=32): # KV 캐시를 쓴 1토큰 순전파 평균 시간
out = model(ids, use_cache=True)
past, nxt = out.past_key_values, out.logits[:, -1:].argmax(-1)
sync(ids); t = time.perf_counter()
for _ in range(n):
out = model(nxt, past_key_values=past, use_cache=True)
past, nxt = out.past_key_values, out.logits[:, -1:].argmax(-1)
sync(ids)
return (time.perf_counter() - t) / n
@torch.no_grad()
def greedy_alpha(prompt, max_new=128): # 목표가 쓴 답을 초안도 1순위로 맞힌 비율
ids = tok(prompt, return_tensors="pt").input_ids.to(target.device)
seq = target.generate(ids, max_new_tokens=max_new, do_sample=False)
n = ids.shape[1]
t_pred = target(seq).logits[0, n - 1:-1].argmax(-1)
d_pred = draft(seq.to(draft.device)).logits[0, n - 1:-1].argmax(-1).to(t_pred.device)
return (t_pred == d_pred).float().mean().item()
prompts = ["def parse_config(path):", "다음 회의록을 세 줄로 요약해 줘: ..."] # 실제 트래픽에서 뽑기
alpha = sum(greedy_alpha(p) for p in prompts) / len(prompts)
ids = tok(prompts[0], return_tensors="pt").input_ids.to(target.device)
c = step_time(draft, ids.to(draft.device)) / step_time(target, ids)
speed = lambda g: (1 - alpha ** (g + 1)) / ((1 - alpha) * (g * c + 1))
best = max(range(1, 21), key=speed)
print(f"alpha={alpha:.2f} c={c:.3f} best gamma={best} 예상 {speed(best):.2f}배")
잴 때 지킬 점은 세 가지예요.
- 프롬프트는 실제 트래픽에서 뽑아요. 원 논문에서도 같은 T5-small·온도 0 조건으로 번역은 α 0.75, 요약은 0.65였어요.
- c는 1토큰 디코딩 시간으로 재요. 프롬프트 전체를 넣는 첫 순전파(프리필)는 빼야 해서 위 코드도 첫 호출을 시간에서 제외했어요.
- 서비스 온도로 다시 확인해요. T5-small의 α는 온도 0에서 0.75, 온도 1에서 0.62였어요. 위 코드는 그리디 전용이라, 샘플링이면
do_sample=True로 켠 것과 끈 것의 속도를 직접 비교하세요.
초안 모델의 메모리와 GPU 비용 계산
초안 모델은 가중치와 KV 캐시를 한 벌 더 올려요. bf16 기준(파라미터 1개에 2바이트)으로 공개 설정 파일의 층 수·KV 헤드 수·헤드 차원을 넣어 계산했어요. KV 캐시는 토큰 하나에 2(K·V) × 층 수 × KV 헤드 수 × 헤드 차원 × 2바이트예요.
| 모델 | 파라미터 | bf16 가중치 | 토큰당 KV | 4,096토큰 KV |
|---|---|---|---|---|
| Llama 3.1 8B (목표) | 약 8.03B | 약 16.06GB | 128KB | 512MiB |
| Llama 3.2 1B (초안) | 약 1.24B | 약 2.47GB | 32KB | 128MiB |
| SmolLM-1.7B (목표) | 약 1.71B | 약 3.42GB | 192KB | 768MiB |
| SmolLM-135M (초안) | 약 0.135B | 약 0.27GB | 22.5KB | 90MiB |
Llama 짝은 초안이 가중치로 15%, KV 캐시로 25%를 더 써요. 24GB GPU에 8B 목표와 1B 초안을 함께 올리면 약 18.5GB라, KV 캐시와 실행 공간에 남는 몫이 그만큼 줄어 동시 요청 수가 줄어요. KV 캐시 용량과 동시 요청 수의 관계는 vLLM KV 캐시 용량 계산에서 자세히 다뤘어요.
비용은 속도 향상의 역수로 바뀌어요. 배치 1에 가까운 지연 중심 서비스라면, 같은 트래픽을 처리하는 데 필요한 GPU 시간이 1 ÷ 속도로 줄어요. Lambda GPU 클라우드 가격표의 1장 인스턴스 요금(2026-11-05 확인, 세금 별도)으로 한 달(720시간) 비용을 계산했어요. A10 24GB는 시간당 1.29달러로 월 928.8달러, H100 SXM 80GB는 시간당 4.29달러로 월 3,088.8달러예요.
| 속도 향상 (위 동적 γ 실측) | GPU 시간 절감 | A10 1장 월 절감 | H100 1장 월 절감 |
|---|---|---|---|
| 1.52배 (Llama 8B 요약) | 34.2% | 약 317.7달러 | 약 1,056.7달러 |
| 1.18배 (Llama 8B 자유생성) | 15.3% | 약 141.7달러 | 약 471.2달러 |
| 1.15배 (Llama 8B 코드) | 13.0% | 약 121.1달러 | 약 402.9달러 |
이 표는 RTX 4090에서 잰 배수를 다른 GPU 요금에 곱한 예시 계산이에요. GPU가 바뀌면 c와 배수도 바뀌고, 배치가 커질 때 이득이 줄어드는 점도 빠져 있어요. 절감액은 앞 절의 코드로 내 GPU에서 잰 배수로 다시 계산하세요.
토크나이저가 다를 때와 초안이 마땅치 않을 때
기본 방식은 두 모델의 어휘가 같아야 해요. 같은 토큰 번호 x에 대해 목표 확률 p(x)와 초안 확률 q(x)를 비교하기 때문이에요. 위 코드 첫머리의 assert처럼 get_vocab()을 비교하면 바로 확인돼요. 이름이 같은 계열이어도 세대가 다르면 어휘가 바뀌는 경우가 있으니 꼭 확인하세요.
어휘가 다르면 선택지는 셋이에요.
- UAD(Universal assisted decoding): Transformers의 Assisted decoding 문서가 소개하는 경로예요.
tokenizer와assistant_tokenizer를 함께 넘기면 초안 토큰을 텍스트로 풀어 목표 토크나이저로 다시 인코딩하고, 최장 공통 부분 수열로 위치를 맞춰요. 재인코딩 비용만큼 c가 늘어나요(정렬 보정은assistant_lookbehind·target_lookbehind). - 프롬프트 룩업: 초안 모델 없이 프롬프트 안에서 겹치는 n-gram을 찾아 후보로 써요.
prompt_lookup_num_tokens로 복사할 토큰 수를,max_matching_ngram_size(기본 2)로 찾을 구절 길이를 정해요. c가 사실상 0이라 요약·번역·코드 수정처럼 입력을 베끼는 작업에서 효율이 좋아요. - 초안을 직접 만들기: 맞는 작은 모델이 없으면 목표 모델 출력으로 작은 모델을 증류해 α를 올릴 수 있어요. 학습 비용은 지식 증류 방식 고르기로 따져 보세요.
초안 모델 상황별 추천과 고르는 순서
| 상황 | 초안 선택 | γ 설정 |
|---|---|---|
| 같은 계열에 1/50~1/100 크기 모델이 있음 | 그 모델 | 계산표 기준 6~10, 또는 동적 기본값 |
| 같은 계열 최소 모델이 1/5~1/10 크기 | 써 보되 c를 꼭 측정 | 3 이하 고정 또는 동적 기본값 |
| 요약·번역·코드 수정 위주 | 프롬프트 룩업부터 | prompt_lookup_num_tokens 5 안팎 |
| 높은 온도의 창작·대화 | 초안 효과가 작음, 측정 후 결정 | 작게(2~3) |
| 어휘가 맞는 작은 모델이 없음 | UAD 또는 증류 초안 | 측정한 α·c로 계산 |
| 24GB급 GPU에 목표가 꽉 참 | 더 작은 초안 또는 n-gram | — |
순서도의 네 질문을 위에서부터 확인한 뒤, 계산표로 고른 고정 γ와 동적 기본값을 실제 프롬프트로 나란히 재서 빠른 쪽을 쓰면 돼요.
자주 묻는 질문
초안 모델은 목표 모델보다 몇 배 작아야 하나요?
원 논문은 두 자릿수(약 100배), Hugging Face 블로그는 적어도 한 자릿수(10배) 작게 고르라고 해요. 층 수가 비슷하면 10배 작아도 c가 크게 나올 수 있으니 직접 재서 정하세요.
수락률이 높은 큰 초안이 더 빠르지 않나요?
아니에요. T5-XXL 실험에서 초안을 77M에서 800M으로 키우자 α는 0.75에서 0.82로 올랐지만 속도는 3.4배에서 1.7배로 떨어졌어요. 초안을 γ번 돌리는 비용이 더 크게 늘었기 때문이에요.
num_assistant_tokens를 몇으로 두면 되나요?
Transformers 4.45.0 이후 기본값(동적, 확신도 0.4, 최대 20)은 대부분 고정값보다 나았어요. 고정하고 싶다면 α와 c를 재서 이 글의 계산표로 정하세요. 예를 들어 α 0.7이면 c 0.02에서 7~8, c 0.1에서 4, c 0.2에서 3이 가장 빨라요.
투기적 디코딩을 켜면 결과가 달라지나요?
표준 검증 규칙을 쓰면 출력 분포는 목표 모델 혼자 만든 것과 같아요. 다만 여러 토큰을 한 번에 처리하는 계산은 부동소수점 누적 순서가 달라 그리디에서도 드물게 다른 토큰이 나올 수 있어요. assistant_ensemble_weight 같은 느슨한 검증 옵션은 분포 자체가 바뀌어요.
토크나이저가 다른 작은 모델도 초안으로 쓸 수 있나요?
Transformers의 UAD(tokenizer·assistant_tokenizer를 함께 넘김)로 쓸 수 있지만 재인코딩만큼 c가 늘어요. 같은 어휘의 작은 모델이 있다면 그쪽이 먼저예요.
정리
- 초안 모델의 이득은 α(수락률)·c(비용 비율)·γ(초안 길이)가 정하고, α > c여야 빨라져요. c는 파라미터 비율보다 크게 나오니 층 수까지 보고 직접 재요.
- 초안은 같은 어휘의 같은 계열 모델 중 목표의 1/10 이하, 가능하면 1/100 근처에서 골라요. 더 크고 정확한 초안이 더 빠르지는 않아요.
- γ는 α·c로 계산하거나 Transformers의 동적 기본값을 써요. 초안이 무거운 짝(8B + 1B)일수록 동적 γ의 효과가 컸어요(요약 1.00배 → 1.52배).
- 초안은 가중치와 KV 캐시를 더 써요(Llama 8B + 1B 기준 가중치 15%, KV 25%). 절감액은 내 GPU에서 잰 속도 배수로 계산하세요.
- 다른 방식과 배치별 손익은 투기적 디코딩 방식 고르기에서 이어서 볼 수 있어요.
더 깊이 읽기
이 글은 실무에서 판단할 때 필요한 것 위주로 정리했어요. 같은 주제의 내부 동작 원리는 devboi 기술 블로그의 투기적 디코딩 원리 — 초안 모델·수락 규칙·EAGLE에서 도식과 코드로 단계별로 풀었어요.