6-1편. Speculative Decoding

Gyullbb·약 15시간 전

이전 편에서 확인한 기법들은 한 번의 forward pass를 얼마나 효율적으로 돌릴 것인가에 집중된 기법이었다.
그런데 LLM 생성은 근본적으로 토큰을 한 개씩 순차적으로 만들어야 하는(autoregressive) 구조라서, 한 번의 pass를 아무리 최적화해도 "다음 토큰을 만들려면 방금 만든 토큰을 알아야 한다"는 순서 제약 자체는 사라지지 않는다.
게다가 대규모 모델의 추론은 산술 연산량보다 GPU 메모리 대역폭에 더 크게 좌우된다. 토큰 하나를 만들 때마다 거대한 가중치 전체를 메모리에서 새로 읽어와야 하는데, 이 시간이 병목이 되어 정작 GPU의 연산 능력은 상당 부분 놀고 있는 상태가 된다.
즉 지연시간을 한 단계 더 줄이려면 "한 토큰씩 순차적으로 만드는 구조 자체"를 깨야 하는데, 이때 필요한 것이 추측 디코딩(Speculative Decoding)이다.

1) Speculative Decoding

  • 작고 빠른 드래프트 모델이 먼저 여러 개의 다음 토큰 후보를 순차적으로 미리 만들어 둔다.
  • 크고 정확한 타깃 모델이 이 후보들을 한 번의 forward pass로 병렬 검증한다.
  • 드래프트가 맞으면 그대로 채택하고, 틀리면 확률적으로 걸러낸 뒤 보정된 분포에서 다시 뽑는다. 이 보정 덕분에 최종 출력 분포는 타깃 모델 혼자 생성했을 때와 수학적으로 완전히 동일하게 보장된다. 즉, 속도를 얻기 위해 품질을 희생하지 않는다.

이렇게만 보면 대략적으로 어떤 기법인지에 대해 파악은 되나, 정확한 원리를 파악하기엔 부족하다. "작은 모델이 미리 만들고 큰 모델이 검증한다"는 구조가 왜 하필 필요한지, 그리고 왜 이게 실제로 빨라지는지를 논문을 통해 구체적으로 확인해보자.

1-1) 논문 표기 (1): Mp와 Mq

논문에서 쓰는 표기를 미리 익혀두면 이후 내용을 따라가기 편하다.

  • 크고 느린 타겟 모델 Mp: 최종적으로 원하는 출력 분포 p(x)를 보장하는 모델
  • 작고 빠른 근사(드래프트) 모델 Mq: Mp를 흉내 내며 분포 q(x)를 만드는 모델

Mq가 먼저 γ개의 토큰을 순차적으로 "추측"해서 만들어 놓으면, 이 추측된 시퀀스는 이제 Mp 입장에서 "이미 정해진 시퀀스"가 된다.
그러면 학습 때처럼 이 시퀀스 전체를 Mp에 한 번에 넣어서, 단 한 번의 forward pass로 γ+1개 자리의 확률분포를 동시에 뽑아낼 수 있다.
Mp의 무거운 가중치를 딱 한 번만 읽으면서 여러 토큰 분량의 검증을 끝내는 것이다.

여기서 "Mq를 γ번, Mp를 1번 돌리니 결국 호출 횟수는 비슷한 거 아닌가"라는 의문이 들 수 있는데, 핵심은 읽어오는 양(모델 크기)이 다르다는 데 있다.

  • Mq는 Mp보다 훨씬 작은 모델. T_q ≪ T_p
  • Mp만으로 추론을 한다면: (γ+1)×T_p
  • 추론적 디코딩을 한다면: γ×T_q + T_p

원래 Mp를 γ번 더 읽어야 했던 비용을 Mq를 γ번 읽는 훨씬 싼 비용으로 바꿔치기하게 된다.

Mq의 추측이 항상 맞을 리는 없다. 그래서 Mp가 검증하면서 확률적으로 수락·거부하고, 거부된 자리부터는 보정된 분포에서 다시 샘플링한다. 이 보정 과정 덕분에 최종 출력의 분포가 Mp 혼자 돌렸을 때와 수학적으로 완전히 동일하다는 게 이 방법의 핵심이다. — 이 증명은 논문을 따라가며 다음 절에서 자세히 확인한다.

1-2) 논문 표기 (2): 확률 분포

논문에서 계속 등장하는 p(x), q(x)는 둘 다 "지금까지 나온 문맥이 주어졌을 때, 다음 토큰이 무엇일지"에 대한 확률분포다.
모델은 어휘 사전에 있는 모든 후보 토큰에 대해 확률을 매기는데, 이걸 시각화하면 x축에 어휘 사전의 토큰들을 쭉 나열하고 y축에 각 토큰이 다음 단어로 뽑힐 확률을 그린 막대그래프가 된다.
타겟 모델 Mp가 그린 막대그래프와 근사 모델 Mq가 그린 막대그래프는 같은 문맥을 보고 그렸지만 모델 성능 차이 때문에 막대 높이가 서로 다르다.

이 논문에서 "q(x)가 p(x)보다 크다/작다"라고 할 때는 이 그래프 전체를 비교하는 게 아니라, 특정 토큰 하나에 대해 두 모델이 매긴 확률값을 비교하는 것이다.
즉 Mq가 방금 draft한 토큰 x 하나에 대해, 근사 모델은 q(x)만큼, 타겟 모델은 같은 자리에서 p(x)만큼의 확률을 줬다는 걸 견주는 것이다.

  • q(x) > p(x)라는 건 "근사 모델이 이 토큰을 타겟 모델보다 더 자신 있게(과대평가해서) 뽑았다"는 뜻이고
  • q(x) ≤ p(x)는 "근사 모델의 자신감이 타겟 모델을 넘어서지 않았다"는 뜻이다.

2) 논문 따라가기

알고리즘 4단계

  1. 근사 모델 Mq로 γ개의 토큰을 순차적으로(autoregressive) 생성한다.
  2. 타겟 모델 Mp로 이 γ개 추측과 각각의 확률 p(x)를 병렬로(한 번의 forward pass로) 평가한다.
  3. q(x) ≤ p(x)면 그 추측을 그대로 수락한다. q(x) > p(x)면 1 - p(x)/q(x)의 확률로 거부한다.
  4. 거부된 첫 토큰 자리에서는 보정된 분포 p'(x) = norm(max(0, p(x) - q(x)))에서 새 토큰을 뽑는다. 만약 γ개 전부 수락됐다면, Mp가 이미 계산해 둔 그다음 자리의 분포에서 보너스 토큰을 하나 더 뽑는다.

2-1) 추측 수락/거부 규칙

추측 수락/거부 규칙을 하나의 수식으로 표현하면 아래와 같다.

min(1,p(x)q(x))\min\left(1, \dfrac{p(x)}{q(x)}\right)

p(x)는 큰 모델이 이 토큰에 준 확률이고, q(x)는 작은 모델이 이 토큰에 준 확률이다.

1) q(x) ≤ p(x)

작은 모델이 추론한 확률보다 큰 모델이 추론한 확률이 더 크다면, 작은 모델은 적절하게 추론한 셈이다. 그렇기 때문에 이 경우에는 무조건 수락 즉, 확률이 1이 된다.

2) q(x) > p(x)

반대로 작은 모델이 추론한 확률이 큰 모델이 추론한 확률보다 더 크다면, 작은 모델은 과도하게 추론을 해버린 것이다.
이 토큰을 그대로 받아버린다면 실제 보다 이 토큰이 더 자주 나오는 현상이 발생한다.
적절하게 이 토큰을 받아 들이기 위해서 정한 것이 바로 p(x)/q(x) 비율이다.

이 두 가지 경우를 하나의 식으로 압축한 게 min(1,p(x)q(x))\min\left(1, \dfrac{p(x)}{q(x)}\right)이다.

  • q(x) ≤ p(x)의 경우: p(x)q(x)1\dfrac{p(x)}{q(x)} \ge 1이므로 min(1,p(x)q(x))=1\min\left(1, \dfrac{p(x)}{q(x)}\right) = 1
  • q(x) > p(x)의 경우: p(x)q(x)<1\dfrac{p(x)}{q(x)} < 1이므로 min(1,p(x)q(x))=p(x)q(x)\min\left(1, \dfrac{p(x)}{q(x)}\right) = \dfrac{p(x)}{q(x)}

왜 하필 p(x)/q(x)인가: Rejection Sampling

거부 확률이 1 - p(x)/q(x)가 되는 건 통계학의 rejection sampling 기법을 가져온 것이다.
우리가 원하는 진짜 분포는 p인데 실제 샘플은 q에서 뽑는다면, p(x)/q(x) 비율만큼만 그 샘플을 인정해준다. q가 어떤 토큰을 과대평가한 만큼, 그 초과분에 비례해서 걸러내는 것이다.
인정된 샘플들의 최종 분포를 정확히 p로 맞출 수 있다는 게 수학적으로 증명되어 있다.

2-2) 보정 분포 p'(x)

p'(x) = norm(max(0, p(x) - q(x)))에서 프라임(')은 미분(도함수)을 뜻하는 게 아니라, 그냥 "보정된 버전의 p"라는 표기이다.
실제 연산은 뺄셈(p(x)-q(x))과 음수 제거(max(0, ·)), 정규화(norm)다.

거부가 일어났다는 건 이번 자리에서 q가 어떤 토큰들엔 확률을 과하게 줬다는 뜻이다.
확률의 총합은 1이므로, q가 특정 토큰에 확률을 과하게 몰아줬다면 다른 토큰들에는 p 기준으로 부족하게 줬을 것이다.
p(x)-q(x)가 양수인 토큰들이 바로 이렇게 "덜 챙겨받은" 토큰들이고, 이 부족분만 모아서 정규화한 게 p'이다.
q(x)-p(x)가 아니라 p(x)-q(x)를 쓰는 이유는, 우리가 최종적으로 맞추고 싶은 목표가 p이기 때문이다.

norm(정규화)은 "각 항목을 전체 합으로 나눠서 총합이 1이 되게 만드는 것"이다.

이후 이 보정 분포 p'(x)에서 샘플링을 하여 토큰을 하나 뽑게된다.

샘플링이란

샘플링은 확률에 "비례해서" 무작위로 뽑는 것이다 — 확률 0.6, 0.3, 0.1을 가진 세 토큰이 있다면 각각 60%, 30%, 10%의 기회로 뽑히게 된다.
앞서 계산한 p(x), q(x)를 활용한 보정 분포를 통해 적절한 확률로 난수를 뽑는 것이다.

2-3) 추론적 디코딩으로 뽑은 토큰 분포 = Mp를 통한 분포

이 논문이 증명하려는 핵심 주장은 추론적 디코딩으로 뽑은 토큰의 분포가 Mp 혼자 정직하게 샘플링했을 때의 분포 p(x)와 정확히 같다는 것이다.

이를 수학적 공식으로 따라 가면 아래와 같다.

1) Mq로 뽑은 샘플을 유지할 확률 β

β=Exq(x)[min(1,p(x)q(x))]=xmin(p(x),q(x))\beta = \mathbb{E}_{x \sim q(x)}\left[\min\left(1, \frac{p(x)}{q(x)}\right)\right] = \sum_x \min(p(x), q(x))

앞서 추측 수락/거부 규칙을 하나의 수식으로 표현하면 min(1,p(x)q(x))\min\left(1, \dfrac{p(x)}{q(x)}\right) 라는 것을 알아보았다.

여기서 기댓값이라는 정의가 나온다.

기댓값(Expectation)이란

기댓값 E[f(x)] =x를 어떤 확률분포에서 뽑았을 때 f(x)의 평균

E[f(x)]=xP(x)f(x)E[f(x)] = \sum_x P(x) \cdot f(x)

x가 나올 수 있는 모든 경우를 하나씩 살펴보면서, 그 경우가 일어날 확률 P(x)만큼 가중치를 줘서 f(x) 값을 더한다.

이 정의를 위 함수에 그대로 적용하면, β는 x를 q(x)라는 분포에서 뽑았을 때 min(1, p(x)/q(x))의 평균이므로 아래와 같이 표현된다.

β=Exq(x)[min(1,p(x)q(x))]=xq(x)min(1,p(x)q(x))\beta = \mathbb{E}_{x \sim q(x)}\left[\min\left(1, \frac{p(x)}{q(x)}\right)\right] = \sum_x q(x)\cdot \min\left(1,\frac{p(x)}{q(x)}\right)

이 곱셈을 풀어보자.

  • q(x) > p(x)인 경우 min(1, p(x)/q(x)) = p(x)/q(x)이므로,
q(x)×p(x)q(x)=p(x)q(x) \times \frac{p(x)}{q(x)} = p(x)
  • q(x) ≤ p(x)인 경우 min(1, p(x)/q(x)) = 1이므로,
q(x)×1=q(x)q(x) \times 1 = q(x)

어느 경우든 결과는 p(x)와 q(x) 중 더 작은 값이다. 따라서 위에서 봤던 식이 성립하게 된다.

β=xq(x)min(1,p(x)q(x))=xmin(p(x),q(x))\beta = \sum_x q(x)\cdot \min\left(1,\frac{p(x)}{q(x)}\right) = \sum_x \min(p(x), q(x))

2) 보정된 분포 p'(x)

p(x)=norm(max(0,p(x)q(x)))=norm(p(x)min(q(x),p(x)))=p(x)min(q(x),p(x))x(p(x)min(q(x),p(x)))=p(x)min(q(x),p(x))1β\begin{aligned} p'(x) &= \text{norm}\big(\max(0, p(x)-q(x))\big) \\ &= \text{norm}\big(p(x)-\min(q(x),p(x))\big) \\ &= \frac{p(x)-\min(q(x),p(x))}{\sum_{x'} \big(p(x')-\min(q(x'),p(x'))\big)} \\ &= \frac{p(x)-\min(q(x),p(x))}{1-\beta} \end{aligned}

위에서 보정 분포 p'(x)의 개념에 대해서 알아보았다. 이를 수식으로 표현하면 norm(max(0,p(x)q(x)))\text{norm}\big(max(0, p(x)-q(x))\big)이다.

max(0,p(x)q(x))\max(0, p(x)-q(x))는 사실 p(x)min(q(x),p(x))p(x)-\min(q(x),p(x))와 같은 값이다.

  • q(x) <= p(x)인 경우
    max(0,p(x)q(x))=p(x)q(x)=p(x)min(q(x),p(x))\max(0, p(x)-q(x)) = p(x)-q(x) = p(x)-\min(q(x),p(x))

  • q(x) > p(x) 인 경우
    max(0,p(x)q(x))=0=p(x)p(x)=p(x)min(q(x),p(x))\max(0, p(x)-q(x)) = 0 = p(x)-p(x) = p(x)-\min(q(x),p(x))

정규화 분포의 분모를 살펴보면 앞서 정의한 β\beta 로 인해

xp(x)xmin(q(x),p(x))=1β\sum_{x'} p(x') - \sum_{x'} \min(q(x'),p(x')) = 1-\beta

가 된다. (xp(x)=1\sum_{x'} p(x')=1이고 xmin(q(x),p(x))=β\sum_{x'}\min(q(x'),p(x'))=\beta이므로)

3) P(x=x)=p(x)P(x=x') = p(x')

이제 마지막으로 추론적 디코딩으로 뽑은 토큰의 분포가 Mp 혼자 정직하게 샘플링했을 때의 분포 p(x)와 정확히 같은지 확인해보자.

추론적 디코딩으로 토큰이 뽑히는 것 즉, P(x=x)P(x=x')는 두 경로의 합이다.

P(x=x)=P(수락되고 x=x)+P(거부되고 x=x)P(x=x') = P(\text{수락되고 } x=x') + P(\text{거부되고 } x=x')

첫째 항: "xx'가 draft되고(확률 q(x)q(x')) 그게 수락되는(확률 min(1,p(x)/q(x))\min(1,p(x')/q(x'))) 확률"

q(x)min(1,p(x)q(x))=min(q(x),p(x))q(x')\cdot \min\left(1,\frac{p(x')}{q(x')}\right) = \min(q(x'),p(x'))

둘째 항: "거부가 일어나고(확률 1β1-\beta) 재샘플링 결과가 xx'인(확률 p(x)p'(x')) 확률"

(1β)p(x)(1-\beta)\cdot p'(x')

여기서 위에서 구한 p(x)p'(x) 정의를 그대로 대입하면 아래와 같다.

(1β)p(x)=(1β)×p(x)min(q(x),p(x))1β=p(x)min(q(x),p(x))(1-\beta)\cdot p'(x') = (1-\beta)\times \frac{p(x')-\min(q(x'),p(x'))}{1-\beta} = p(x')-\min(q(x'),p(x'))

결국 첫째 항의 결과 min(q(x),p(x))\min(q(x'),p(x'))와 둘째 항의 결과 p(x)min(q(x),p(x))p(x')-\min(q(x'),p(x'))를 더하면 p(x)p(x')만 남게 된다.

P(x=x)=P(guess accepted,x=x)+P(guess rejected,x=x)=q(x)min ⁣(1,p(x)q(x))+(1β)p(x)=min(q(x),p(x))+p(x)min(q(x),p(x))=p(x)\begin{aligned} P(x=x') &= P(\text{guess accepted}, x=x') + P(\text{guess rejected}, x=x') \\ &= q(x')\min\!\left(1, \frac{p(x')}{q(x')}\right) + (1-\beta)\,p'(x') \\ &= \min(q(x'),p(x')) + p(x') - \min(q(x'),p(x')) \\ &= p(x') \end{aligned}

추론적 디코딩으로 뽑은 토큰의 분포가 Mp 혼자 정직하게 샘플링했을 때의 분포 p(x)와 정확히 같은 것이 증명된 것이다.

이를 통해 추론적 디코딩이 출력 품질을 조금도 희생하지 않으면서 순수하게 속도만 끌어올리는 무손실 효율화 기법임을 알 수 있다.

3. 성능 분석: 얼마나 빨라지고, 얼마나 더 계산하는가

3-1. 생성 토큰 개수 기댓값

각 자리의 수락 확률(β)이 서로 독립이고 동일한 분포를 따른다고 가정하고, 그 평균을 α라 하자.

α=E(β)\alpha = E(\beta)

한 라운드에서 몇 번째 토큰까지 살아남는지를 생각해보면, 첫 번째 토큰은 항상 만들어지므로 그 확률은 1이다. 두 번째 토큰까지 살아남으려면 첫 번째가 수락돼야 하므로 확률은 α, 세 번째까지 살아남으려면 앞의 두 개가 모두 수락돼야 하므로 α² — 이런 식으로 "k번째 토큰까지 생성될 확률"은 α^(k-1)이 된다.

P(Nk)=αk1(k=1,2,,γ+1)P(N \ge k) = \alpha^{k-1} \qquad (k = 1, 2, \dots, \gamma+1)

기댓값은 "적어도 k개 나올 확률"을 전부 더해서 구할 수 있으므로,

E[N]=k=1γ+1P(Nk)=1+α+α2++αγ=1αγ+11αE[N] = \sum_{k=1}^{\gamma+1} P(N\ge k) = 1+\alpha+\alpha^2+\cdots+\alpha^{\gamma} = \frac{1-\alpha^{\gamma+1}}{1-\alpha}

α가 1에 가까울수록(Mq가 Mp를 잘 근사할수록) 이 값은 커진다. 반대로 α가 낮으면 뒤쪽 항들(α², α³, ...)이 금방 0에 가까워지므로, γ를 아무리 늘려도 얻는 게 별로 없다.

3-2. Walltime 개선

c를 "Mq 1회 실행 시간 / Mp 1회 실행 시간"의 비율이라 하자. 한 라운드는 Mp 1회 시간을 기준으로

γc+1\gamma c + 1

배가 걸리고, 그 라운드에서 평균 E[N]=1αγ+11αE[N] = \dfrac{1-\alpha^{\gamma+1}}{1-\alpha}개의 토큰을 얻는다. "토큰 1개당 걸리는 시간"을 순수 Mp 방식과 비교하면 속도 개선 배수는 다음과 같다.

속도 개선=1αγ+1(1α)(γc+1)\text{속도 개선} = \frac{1-\alpha^{\gamma+1}}{(1-\alpha)(\gamma c+1)}

γ=1을 대입하면 어떻게 단순화되는지 직접 풀어보자.

1α2(1α)(c+1)=(1α)(1+α)(1α)(c+1)=1+α1+c\frac{1-\alpha^{2}}{(1-\alpha)(c+1)} = \frac{(1-\alpha)(1+\alpha)}{(1-\alpha)(c+1)} = \frac{1+\alpha}{1+c}

1α21-\alpha^2(1α)(1+α)(1-\alpha)(1+\alpha)로 인수분해하면 분모의 (1α)(1-\alpha)와 정확히 약분된다.
여기서 α > c이기만 하면 항상 속도가 개선되는 γ가 존재한다는 기준이 나온다.
드래프트 모델을 고를 때는 "얼마나 빠른가(c)"만 볼 게 아니라, "속도 대비 정확도(α)가 그 속도 이득을 넘어서는가"를 봐야 한다는 뜻이다.

3-3. 산술 연산 횟수는 오히려 증가한다

c^\hat c를 "Mq 토큰당 연산량 / Mp 토큰당 연산량"의 비율이라 하자.

c와 다른 점은, Mp의 지연시간은 메모리 대역폭에 좌우되지만 순수 연산량 자체는 처리하는 자리 수에 정직하게 비례한다는 것이다. 그래서 이 둘을 구분해야 한다.

한 라운드의 총 연산량은 (Mp 토큰당 연산량 기준으로) 다음과 같다.

γc^+γ+1\gamma\hat c + \gamma + 1

이를 순수 Mp 방식과 비교한 연산량 배수는

연산량 배수=(1α)(γc^+γ+1)1αγ+1\text{연산량 배수} = \frac{(1-\alpha)(\gamma\hat c+\gamma+1)}{1-\alpha^{\gamma+1}}

이 값은 항상 1보다 크다. 드래프트가 거부되면 그 계산은 그냥 버려지기 때문이다. α가 낮을수록(Mq 성능이 나쁠수록) 이 증가율은 더 심해진다.

예시로 한눈에 보기

지금까지 나온 세 지표를 α=0.8, c=c^\hat c=0.1, γ=4로 직접 계산해보면 다음과 같다.

E[N]=10.8510.8=0.67230.23.36E[N] = \frac{1-0.8^{5}}{1-0.8} = \frac{0.6723}{0.2} \approx 3.36
속도 개선=3.364×0.1+1=3.361.42.40\text{속도 개선} = \frac{3.36}{4\times0.1+1} = \frac{3.36}{1.4} \approx 2.40\text{배}
연산량 배수=0.2×(4×0.1+4+1)0.6723=0.2×5.40.67231.61\text{연산량 배수} = \frac{0.2\times(4\times0.1+4+1)}{0.6723} = \frac{0.2\times5.4}{0.6723} \approx 1.61\text{배}
지표
평균 생성 토큰 수 E[N]E[N]약 3.36개 (최대 5개 중)
Walltime 개선 배수약 2.4배
총 연산량 배수약 1.61배

시간은 2.4배 빨라졌지만, 연산은 61% 더 쓴 셈이다. 시간과 연산량이 반대로 움직인다는 게 이 표의 핵심이다.

3-4. 핵심: 메모리 접근은 줄어든다

연산량은 늘어도, 타겟 모델의 가중치와 KV cache를 읽는 메모리 접근 횟수는 한 라운드에 딱 한 번뿐이다. 그래서 메모리 접근 횟수는 정확히

1αγ+11α\frac{1-\alpha^{\gamma+1}}{1-\alpha}

배만큼 줄어든다. 원래 (γ+1)(\gamma+1)번 읽어야 했던 Mp의 무거운 가중치를 1번만 읽고, 나머지는 훨씬 가벼운 Mq의 가중치 읽기로 대체한 것이다.
남는 연산력을 써서 부족한 메모리 대역폭을 아끼는 자원 교환이 추론적 디코딩의 핵심이다.

3-5. 최적의 γ 고르기

c와 α가 정해지고 컴퓨팅 자원이 충분하다면, 최적의 γ는 3-2의 속도 개선 식

1αγ+1(1α)(γc+1)\frac{1-\alpha^{\gamma+1}}{(1-\alpha)(\gamma c+1)}

을 최대화하는 정수를 찾으면 된다. γ가 정수이므로 미분 없이 γ=1,2,3,...을 하나씩 대입해보는 것으로 충분하다. Mq가 정확하고(α↑) 저렴할수록(c↓) γ를 크게, Mq가 부정확하거나 상대적으로 비쌀수록 γ를 작게 잡는 게 유리하다.

3-6. 추론적 디코딩의 한계

  • 디코드 단계에만 도움이 된다. 추론적 디코딩은 "메모리 대역폭이 병목인 상황"에서는 효율적이다.
    prefill처럼 입력 컨텍스트가 길어서 이미 연산(FLOPS)에 병목이 걸린 compute-bound 상황이라면, 애초에 남는 연산력이 없다. 남는 연산력이 없다는 건 γ개 자리를 동시에 계산해도 시간이 거의 그대로라는 추론적 디코딩의 이점이 사라지기 때문에, 속도 이득이 사라진다.
  • 수락률이 낮으면 오히려 손해다. 위에서 보았듯, 총 연산량은 항상 증가하고, 수락률(α)가 낮을수록 그 증가율이 더 커진다. compute-bound 상황과 낮은 수락률이 겹치면, 늘어난 연산량이 그대로 지연시간 증가로 이어질 수 있다.
  • 배치가 커지면 병목 자체가 바뀐다. 큰 배치를 처리하면 GPU 활용률이 올라가면서 memory-bound였던 병목이 compute-bound로 옮겨간다. 이 경우 지연시간에는 여전히 도움이 되지만, 늘어난 연산량이 처리량의 병목이 된다.

4. 그 이후 등장한 아이디어들: Mq 모델

Mq를 준비하는 방법은 아래 스펙트럼처럼 발전해 왔다. 별도 모델에 대한 의존도가 점점 줄어드는 방향이다.

양자화(Quantization) — 원래 모델의 가중치는 FP32나 FP16 같은 고정밀도 숫자로 저장되는데, 이를 INT8, INT4처럼 더 적은 비트로 표현하는 기법이다.
정밀도를 조금 희생하는 대신 모델 크기가 작아지고 메모리에서 읽어와야 할 데이터양이 줄어드니, 메모리 대역폭 병목을 직접 완화한다. Mq의 정확도가 좀 떨어져도 acceptance rate(β)만 낮아질 뿐, 최종 출력 품질은 앞서 검증한 수식으로 인해 나빠지지 않는다.

증류(Distillation) — 원본 데이터로 작은 모델을 처음부터 학습시키는 대신, 크고 성능 좋은 Mp의 출력 확률분포 자체를 정답처럼 삼아 Mq가 그 분포를 모방하도록 학습시키는 기법이다. q(x)가 p(x)를 최대한 잘 근사하는 것을 목표로, Mp로부터 증류한 Mq는 처음부터 Mp를 흉내내도록 학습된 셈이라 acceptance rate α가 크게 올라가고, 성능 지표(속도 개선, 연산량 배수)가 함께 개선된다.

Self-drafting — 아예 별도 모델 없이 타겟 모델 자기 자신이 드래프트를 만드는 방식이다. 타겟 모델의 중간 레이어까지만 실행해서 빠르게 대략적인 예측을 내놓는 early exit이나, 원래 모델에 여러 미래 토큰을 동시에 예측하는 추가 헤드를 붙이는 Medusa 같은 방법이 여기 속한다.
별도 모델을 학습·저장·서빙할 필요가 없다는 게 큰 장점이다.

N-gram — 신경망 모델을 아예 쓰지 않는다. 최근에 나온 n개의 연속 토큰이 이전 문맥이나 외부 텍스트에 등장한 적이 있는지 찾아보고, 그 뒤에 이어졌던 토큰을 그대로 추측으로 재활용한다. 코드 생성(변수명·구조 반복)이나 문서 요약·인용(입력 문구를 그대로 베끼는 경우)처럼 반복이 잦은 텍스트에서는, 모델이 예전에 나온 문구를 그대로 반복할 확률이 높아서 n-gram 매칭만으로도 상당히 정확한 추측을 연산 비용 거의 없이 만들어낼 수 있다.

5. 정리하며

지금까지 추론적 디코딩(Speculative Decoding)에 대해 알아봤다. 작은 드래프트 모델(Mq)로 후보 토큰을 미리 만들고, 큰 타겟 모델(Mp)이 이를 병렬로 검증함으로써 출력 품질은 그대로 유지하면서 디코딩 속도를 끌어올리는 기법이다. 그리고 이 Mq를 어떻게 준비할 것인지에 대한 다양한 방법들(양자화, 증류, self-drafting, n-gram)도 간략하게 살펴보았다.

정리하면, 가장 큰 장점은 출력 분포를 전혀 희생하지 않으면서 속도만 얻을 수 있고 재학습 없이 기존 시스템에 얹을 수 있다는 점이다. 다만 이 이득은 메모리 대역폭이 병목인 상황에서만 통하며, 연산량(FLOPs)은 항상 늘어나고 acceptance rate에 따라 실제 효과가 크게 갈린다는 한계도 함께 가진다.

그런데 추측 디코딩은 어디까지나 모델이 이미 GPU에 올라가 있다는 것을 전제로, 디코딩 단계의 속도만 높이는 기법이다. 모델 자체가 하나의 GPU 메모리에 다 올라가지 않을 만큼 커진다면, 속도를 최적화하기 이전에 애초에 여러 GPU와 노드에 모델을 나눠 서빙하는 방법부터 필요해진다. 다음은 Multi-GPU and Multi-Node Inferencing에 대해 알아본다.

0개의 댓글