+ 作る
図を読み込んでいます…
1 / 24

LLMの生成を5倍速くする「D3D」を図で読む:下書きと検証を同時に走らせる

発行
かえで
版
第6版
改訂
分量
24コマ・10,763字

AIの文章生成を、中身を変えずに約5倍速くする新手法「D3D」(arXiv:2611.06425)の図解。下書き係と検証係を同時に走らせる仕組みを、予備知識なしで読めるように動く図で追います。

この記事は、論文「D3D: Decoupled Diffusion Drafting for Asynchronous Speculative Decoding」(Baoyou Chen ほか、2026年11月、arXiv:2611.06425)を、予備知識なしで読めるように図解したものです。ひとことで言うと 「AIの文章生成を、中身を変えずに約5倍速くする方法」 の話です。

LLMは「1トークンずつ」しか書けない

文章生成AI(LLM = 大規模言語モデル。大量の文章から「次に来る言葉」を学んだAI)は、文章を一気に書いているように見えて、実はトークン(単語や文字のかけら。AIが文章を扱う最小単位)を1つずつ順番に出しています。

図の左の箱が、答えを書く本番のモデルです。この記事ではターゲットモデルと呼びます。

1トークン出すたびに、巨大なモデル全体を1回計算します。この「前から1つずつ」の書き方を自己回帰(AR)と言います。

次のページで、続きのトークンが出てくる様子を見てみましょう。

  • ふつうのLLM:1トークンずつ順番に書く
  • 考える
  • 計算1回 → 1トークン
  • ターゲットモデル
  • は
  • いい
  • 天気
  • です
  • ね
  • 今日
  • 本番用の大きなLLM
  • 1回の計算で出てくるのは、たった1トークン

長い文章ほど、待ち時間が積み上がる

6トークン書くには6回、1000トークンなら1000回の計算が必要です。しかも「前のトークンが決まらないと次が書けない」ので、計算を横に並べて同時に走らせることもできません。

モデルが大きいほど、1回の計算は重くなります。賢いモデルほど返事が遅いのはこのためです。

この論文のゴールは、出てくる文章を一切変えずに、この待ち時間を数分の1にすることです。

  • ふつうのLLM:1トークンずつ順番に書く
  • 考える
  • 計算6回 → 6トークン
  • ターゲットモデル
  • 今日
  • は
  • いい
  • 天気
  • です
  • ね
  • 本番用の大きなLLM
  • 6トークン書くには、大きなモデルを6回動かす
  • は
  • いい
  • 天気
  • だ
  • よ

投機的デコーディング:先に下書き、あとで確認

ここで使われるのが投機的デコーディング(speculative decoding。「たぶんこう続くだろう」と先読みして、あとから答え合わせをする高速化の手法)です。

登場人物が1人増えます。

  • ドラフター:下書き係。数トークン先までまとめて予想する
  • ターゲットモデル:本番のモデル。下書きが正しいかを確かめる

図では、ドラフターが「今日」の続きを5トークン分まとめて予想しました。この時点ではまだ予想で、正式な出力ではありません。

  • 投機的デコーディング:小さいモデルに下書きさせる
  • 考える
  • 確定済み
  • ターゲットモデル
  • です
  • 今日
  • 本番用の大きなLLM
  • 電気
  • 下書き:5トークン先までまとめて予想
  • ドラフター
  • は
  • いい
  • 天気
  • だ
  • よ
  • 小さくて速い下書き係
  • 下書きはまだ「予想」。正しいかはターゲットが確かめる

検証のしくみ①:すべての位置で「次の1個」が出ている

ここが投機的デコーディングのいちばん大事なところなので、3ページかけてゆっくり見ます。

実は、LLMに文を入力すると、最後の位置だけでなく、すべての位置で「この次に来るトークン」の答えが同時に出てきます。 ふだんの生成では、いちばん右の答え1個しか使っていないだけです。

そこで検証では、確定済みの「今日」に下書きの5個をつなげて、6トークンをまとめてターゲットに渡します。計算は1回です。すると図の下の段のように、位置ごとの答えが並んで出てきます。

  • 「今日」の位置 → は
  • 「は」の位置 → いい
  • 「いい」の位置 → 天気
  • 「天気」の位置 → です
  • 「だ」の位置 → ね

(最後の「よ」の位置にも答えは出ます。下書きが全部当たったときに使う、おまけの1個です。次のページからは省きます)

この時点では、まだ答えを並べただけです。下書きと合っているかどうかは、まだ見ていません。

  • 検証のしくみ①:全部の位置で「次の1個」が出ている
  • 入力:確定済みの「今日」+ 下書き5個を、まとめて渡す
  • 今日
  • は
  • いい
  • 天気
  • だ
  • よ
  • 考える
  • ターゲットモデル
  • 1回の計算で、すべての位置を同時に処理
  • 計算は1回だけ
  • は
  • いい
  • 天気
  • です
  • ね
  • …
  • ターゲットの答え:それぞれの位置で「次はこれ」
  • ふだんの生成では、いちばん右の答え1個しか使っていない

検証のしくみ②:各位置は、自分より左だけを見ている

なぜ1回の計算で、位置ごとに別々の答えが出せるのでしょうか。

LLMには、各位置は自分より左(前)のトークンしか見てはいけないという決まりが組み込まれています(因果マスクと呼ばれる目隠し。学習のときに、答えを先に見てしまうカンニングを防ぐための仕組みです)。

だから、6トークンをまとめて入力しても、中で起きているのは次の5つの計算です。

見ている範囲出てくる答え
今日は
今日 はいい
今日 は いい天気
今日 は いい 天気です
今日 は いい 天気 だね

図の水色の帯が「見ている範囲」、右端の青いトークンがその行の答えです。前のページで下に並んでいた答えが、それぞれの行の右端に移動しました。

ポイントは、どの行も「左にあるトークンが正しいとしたら、次は何か」という条件つきの答えだということです。

  • 1行目は、確定済みの「今日」しか見ていないので、無条件に正しい
  • 2行目は、「は」が正しければ正しい
  • 3行目は、「は」「いい」が正しければ正しい

1トークンずつ5回計算するのと、中身はまったく同じです。同じ計算を、1回にまとめてやっているだけです。

  • 検証のしくみ②:各位置は、自分より左だけを見ている
  • 入力:確定済みの「今日」+ 下書き5個を、まとめて渡す
  • 今日
  • は
  • いい
  • 天気
  • だ
  • よ
  • 今日
  • →
  • は
  • 考える
  • 今日 は
  • →
  • いい
  • ターゲットモデル
  • 今日 は いい
  • →
  • 天気
  • この5行を 1回で同時に計算
  • 今日 は いい 天気
  • →
  • です
  • 今日 は いい 天気 だ
  • →
  • ね
  • 水色の帯=見ている範囲、右端の青いトークン=その行の答え
  • 5行は別々の計算ではなく、1回の計算の中で同時に出てくる

検証のしくみ③:真上の下書きと見比べる

答えが出そろったら、あとは見比べるだけです。図では、各行の答え(右端)が、対応する下書き(上の段)のちょうど真下に来ています。

上の行から順に見ていきます。

  1. 「今日」の次:ターゲットの答え「は」= 下書き「は」→ 一致。これで「は」は確定
  2. 「今日 は」の次:「いい」=「いい」→ 一致。2行目は「は」が正しい前提の答えでしたが、その前提はいま確定したので、この答えも本物です
  3. 「今日 は いい」の次:「天気」=「天気」→ 一致
  4. 「今日 は いい 天気」の次:ターゲットは「です」、下書きは「だ」→ 不一致

4行目の「です」は捨てません。この行が見ている「今日 は いい 天気」はすべて確定済みなので、「です」はターゲットの正式な答えです。これを「だ」の代わりに置きます。

5行目は違います。この行は、間違いだった「だ」を見て出した答えです。前提が崩れているので使えません。最初に外れた位置から後ろを全部捨てるのは、このためです。

結果、一致した3個とターゲットの「です」で、計算1回で4トークン進みました。

下書きがどこまで当たるかは、見比べるまで分かりません。当たった分だけ得をする、という仕組みです。

  • 検証のしくみ③:真上の下書きと見比べる
  • 下書き(上の段)と、ターゲットの答え(各行の右端)を縦に見比べる
  • 今日
  • は
  • いい
  • 天気
  • だ
  • よ
  • 今日
  • →
  • は
  • チェック
  • 考える
  • 今日 は
  • →
  • いい
  • チェック
  • ターゲットモデル
  • 今日 は いい
  • →
  • 天気
  • チェック
  • この5行を 1回で同時に計算
  • 今日 は いい 天気
  • →
  • です
  • 不一致 「です」を採用
  • 今日 は いい 天気 だ
  • →
  • ね
  • 5行目は、間違った「だ」を見て出した答え → 使えないので捨てる
  • 一致した3個 + ターゲットの「です」= 計算1回で4トークン進む

検証のしくみ④:計算の量は増えるのに、なぜ時間は増えない?

「5個ぶんをまとめて計算するなら、結局5倍の手間では?」と思った人は鋭いです。掛け算の総量は、たしかに5個ぶんに増えます。 増えないのは「かかる時間」のほうです。

図は、横幅が「かかる時間」、面積が「計算の量」を表しています。前のページの5行が、上の段では横に5回並び、下の段では1つの箱に縦に積まれています。

理由1:GPUは、並んだ計算を同時にこなせる

LLMの計算の正体は、巨大な数の表(行列)どうしの掛け算です。入力は「トークンの数 × 数千個の数値」の表になっていて、モデルの重み(学習で身につけた数値のかたまり)を全トークンにまとめて掛けます。5トークン入れれば、5個ぶんの答えが一度に出ます。GPUは、こういう並んだ計算を同時に処理するのが得意な部品です。

1個ずつ書くときにこれができないのは、2個目の入力が1個目の答えで決まるからです。順番に待つしかありません。検証では、下書きのおかげで入力が最初から全部そろっているので、並べて一気に計算できます。

理由2:時間の多くは「重みを読む」ことに使われている

ターゲットの重みは数十億個の数値で、データにすると十数GB以上あります。1回の計算のたびに、これを全部メモリから読み出します。1トークンだけ計算するとき、GPUの計算能力は余っていて、時間を決めているのは主にこの読み出しだと言われています(実際には、ほかの細かい待ち時間も混ざります)。

  • 1個ずつ5回書く → 重み全体を5回読む
  • 5個まとめて検証 → 重み全体を読むのは1回。そこに5個ぶんの計算を載せる

エレベーターにたとえると、1人ずつ5往復するか、5人まとめて1往復するかの違いです。

ただし、載せすぎると遅くなる

GPUの余力には限りがあります。あとのページに出てくる「ブロックを長くしすぎると、かえって遅くなる」「同時に処理する依頼が増えると、速度向上が小さくなる」は、この限界が見えている例です。

(このページは論文の外の一般的な補足です。参考:SARATHI の3章に、1トークンずつの生成ではGPUの計算能力が余ることの測定があります)

  • 検証のしくみ④:計算は5個ぶん、でも時間は1回ぶん
  • ターゲットだけで1個ずつ書く場合:重み全体を5回読む
  • は
  • いい
  • 天気
  • です
  • ね
  • 重みを全部読む
  • 重みを全部読む
  • 重みを全部読む
  • 重みを全部読む
  • 重みを全部読む
  • かかる時間:計算5回ぶん
  • 下書きを検証する場合:重み全体を読むのは1回だけ
  • は
  • 横幅 = かかる時間 / 面積 = 計算の量
  • いい
  • 計算の量は、5個ぶんに増える
  • 天気
  • でも重みを読むのは1回。GPUが5個を同時にさばく
  • だ
  • よ
  • → かかる時間は、ほぼ1回ぶんのまま
  • 重みを全部読む
  • かかる時間:ほぼ1回ぶん

検証のまとめ:計算1回で、4トークン進んだ

ここまでの答え合わせを検証と呼びます。ルールを整理すると3つです。

  1. 前から順に見て、ターゲットの答えと下書きが合っていれば受理
  2. 最初に外れた位置から後ろは、すべて捨てる
  3. 外れた位置には、ターゲット自身の答えを置く

「は」「いい」「天気」が受理、「だ」が外れて「です」に差し替えられました。ターゲットの計算1回で4トークン進んでいます。1ラウンドで進んだトークン数を受理長(記号は τ、タウ)と呼びます。

大事なのは、最終的な文章がターゲットだけで書いた場合と変わらないことです(確率的に言葉を選ぶ設定でも、出力の確率分布が変わらないことが保証されています)。この性質をロスレス(品質を落とさない)と言います。下書きが外れても、損をするのは時間だけです。

速さを決めるのは3つ。受理長・下書きにかかる時間・検証にかかる時間です。この3つが、このあと何度も出てきます。

  • 検証のまとめ:1回の計算で、4トークン進んだ
  • 考える
  • 確定済み
  • 下書きが当たり(受理)
  • ターゲットが修正
  • ターゲットモデル
  • 今日
  • は
  • いい
  • 天気
  • です
  • 本番用の大きなLLM
  • この1ラウンドで4トークン進んだ(受理長 τ = 4)
  • だ
  • よ
  • 最初に外れた所から後ろは捨てる
  • 電気
  • ドラフター
  • 小さくて速い下書き係
  • ターゲットの計算は1回だけ。なのに4トークン進む

従来の拡散ドラフター:一気に書けるが、順番待ちが残る

下書きの作り方にも種類があります。

  • 自己回帰型のドラフター:小さいモデルが1トークンずつ書く。下書きを長くするほど時間がかかる。既存の小さいLLMをそのまま使える
  • 拡散型のドラフター:伏せ字だらけのブロックを、1回の計算で全部埋める。長さに関係なく1回で済む。ただし、そのための専用の学習が必要(理由はあとのページで説明します)

(拡散モデル = ノイズや伏せ字で隠したデータを元に戻す練習をしたモデル。画像生成でおなじみの仕組みを文章に応用したものです)

拡散型の代表が DFlash です。DFlashは下書きの精度を上げるために、ターゲットモデルの隠れ状態(モデルが文を読みながら内部に作る数値のメモ。文脈の理解が詰まっている)を借りています。

ここに落とし穴があります。新しく確定したトークンの隠れ状態は、検証が終わるまで手に入りません。 そのため

下書き → 検証 → (隠れ状態が届く) → 下書き → 検証 → …

と、2つのモデルが交互に相手を待つことになります。図の紫の矢印がその「待ち」です。1ブロックの中は並列に書けても、ラウンド同士は一列に並んだままです。

  • 従来の拡散ドラフター:速いけれど「待ち」が残る
  • 考える
  • 検証が終わる → 隠れ状態が届く → やっと次の下書き(順番待ち)
  • ターゲットモデル
  • 検証1
  • 検証2
  • 検証3
  • 検証4
  • 検証する側
  • 隠れ状態 (頭の中のメモ)
  • 電気
  • ドラフター
  • 下書1
  • 下書2
  • 下書3
  • 下書4
  • 下書きする側
  • ここで完了
  • 時間の流れ →

D3Dのアイデア:依存を切って、同時に走らせる

D3D(Decoupled Diffusion Drafting = 切り離された拡散ドラフティング)は、この紫の矢印をなくします。

D3Dのドラフターが読むのは確定済みのトークン列だけです。ターゲットの隠れ状態は一切使わず、文脈の理解は自分でやります。こういう自立したドラフターを、論文はスタンドアロンと呼んでいます。

すると、ターゲットが検証している間に、ドラフターは次のブロックの下書きを進められます。図のオレンジの棒が青い棒の真下にもぐり込み、全体が左に詰まりました。この「相手を待たずに並行して進める」方式が非同期(asynchronous)です。

下書きの時間が検証の時間より短ければ、下書きのコストは検証の裏に完全に隠れます。

でも、疑問が残ります。検証の結果がまだ分からないのに、どこから次を書けばいいのでしょうか?

  • D3D:ドラフターを切り離して、検証の裏で下書きする
  • 考える
  • 検証している間に、次の下書きを済ませておく(非同期)
  • ターゲットモデル
  • 検証1
  • 検証2
  • 検証3
  • 検証4
  • 検証する側
  • NG
  • 隠れ状態は もう使わない
  • 電気
  • ドラフター
  • 下書1
  • 下書2
  • 下書3
  • 下書4
  • 下書きする側
  • ここで完了
  • 従来の完了
  • 時間の流れ →

答え:ありうる結果すべてに、先回りして下書きする

検証の結果は「何個目まで受理されたか」と「外れた位置にターゲットが何を置いたか」で決まります。パターンは限られているので、全部に対して続きを用意してしまおうというのがD3Dの作戦です(検証結果を先読みして下書きを準備しておく、という先行研究 SSD の考え方を受け継いでいます)。

1ブロックは16トークンです。先頭の1つはアンカー(すでに確定している、足がかりのトークン)で、残り15個が下書きです。

用意する続きは次のとおりです。

  • 途中で外れた場合:1〜11個目のそれぞれについて、「ここが外れたなら、ターゲットはこれを置くだろう」という候補を2つずつ選ぶ(ドラフター自身の第2・第3候補)。その候補から先の続きも下書きする → 11か所 × 2案 = 22ブロック
  • 全部受理された場合:さらにその先の続き → 1ブロック

図では、外れる位置ごとに2本ずつのバーが階段のように並んでいます。

  • 検証を待たない:ありうる結果すべてに下書きを用意
  • アンカー
  • 下書き15トークン(いまターゲットが検証中)
  • 下書きが途中で外れた場合の続き
  • 位置ごとに2案ずつ用意
  • (11か所 × 2案 = 22ブロック)
  • 全部当たりだった場合の続き →

パック:24ブロックを1回の計算で

続きの22+1ブロックに、いま検証中のブロックを加えて、合計 24ブロック × 16 = 384トークン。これを1本の入力に詰め込み(パック)、ドラフターは1回の計算でまとめて処理します。

なぜ1回で済むのでしょうか。

  • 共通部分(プロンプトと、確定済みの回答)は1度だけ読んで、全ブロックで使い回す
  • 各ブロックが見るのは「自分より前の文」と「自分自身」だけ。ほかのブロックは見ないので、互いに混ざらない

(プロンプト = ユーザーがAIに渡した質問や指示の文)

階段状だったバーが、1つの束にまとまりました。ターゲットが左で検証している、まさにその間に、ドラフターは右でこの束を処理します。

  • パック:24通りの下書きを、1回の計算でまとめて作る
  • 更新
  • 電気
  • ターゲットモデル
  • ドラフター
  • いまのブロックを 検証している最中
  • 同じ時間に、1回の 計算で全部下書き
  • 24ブロック × 16 = 384トークン
  • これを1本の入力に「パック」する

ヒット:待ち時間ゼロで次のラウンドへ

検証が終わりました。例として「5個受理、6個目で外れ」だったとします。

ターゲットが6個目に置いたトークンが、用意しておいた候補のどちらかと一致すればヒットです。「6個目で外れた場合」の下書きがすでに手元にあるので、ドラフターを待たずに、すぐ次の検証を始められます。

使わなかった残りの下書きは捨てます。もったいなく見えますが、ターゲットが検証している間の「空き時間」に、別のGPUで作ったものです。全体の時間は延びません。

(GPU = AIの計算を担当する部品。D3DはターゲットとドラフターをGPU 1枚ずつに載せ、計2枚で同時に動かします)

  • ヒット:用意しておいた下書きが、そのまま次に使える
  • アンカー
  • 検証の結果:5個は当たり、6個目で外れ
  • ターゲットが置いた1トークン = 用意していた候補と一致 →
  • 6個目で外れた場合の下書き(用意済み)
  • ドラフターを待たずに、すぐ次の検証を始められる
  • 使わなかった下書きは 捨てる

ミス:外れた回だけ、従来どおり待つ

用意が外れることもあります。

  • ターゲットが置いたトークンが、候補2つのどちらでもなかった
  • 12個目以降で外れた(そこまでは用意していない)

この場合はミスです。ドラフターが、確定した文から改めてブロックを書くのを待ちます。その回だけ、従来の「下書き → 検証」の順番待ちに戻るわけです。

論文の設定(外れ位置11か所 × 候補2つ)では、ヒット率は 71.3% でした(数学の難問を集めたベンチマーク AIME25 での測定。ベンチマーク = 性能を比べるための共通のテスト問題集)。

用意を増やせばいい、わけではない

設定用意するトークンヒット率速度
外れ位置11か所 × 候補2つ38471.3%約5.0倍(最良)
外れ位置15か所 × 候補2つ51278.3%下がる
外れ位置11か所 × 候補5つ91281.8%下がる

用意を増やすとヒット率は上がります。でもパックが大きくなり、下書きそのものが遅くなって、検証の裏に隠れきれなくなります。「当たる確率」と「下書きの速さ」の綱引きです。

  • ミス:用意が外れたら、その回だけ書き直しを待つ
  • アンカー
  • 検証の結果:5個は当たり、6個目で外れ
  • ターゲットが置いた1トークン ≠ 用意していた候補 →
  • 用意がない → いまから下書きし直す
  • この回だけ、従来と同じ「下書き待ち」が発生する
  • 1ラウンドごとの当たり外れ(AIME25)
  • ヒット 71.3%
  • ミス
  • 使わなかった下書きは 捨てる

なぜ学習が必要?①:検証は「入力がそろっている」

ここまで読んで、「まとめて計算できるなら、下書きもコードを書き換えて何行か同時に計算すれば済むのでは?」と思った人がいるかもしれません。

結論から言うと、下書きのほうは、それでは動きません。専用の学習が必要です。 3ページかけて理由を見ます。

まず、検証のときの計算をもう一度見てください(図)。

どの行も、矢印の左側の入力がすべて埋まっています。オレンジの「は」「いい」「天気」は、ドラフターが先に書いてくれたものです。入力がそろっているので、4行を並べて同時に計算できます。

これは計算の並べ方を工夫しているだけです。ターゲットモデルは、ふだんと同じ「左を見て、次の1個を当てる」仕事をしています。だから検証する側のモデルには、学習し直しは一切要りません。

  • なぜ学習が必要?①:検証は、入力がそろっている
  • 検証:ターゲットは、下書きを入力にして4行を同時に計算する
  • 今日
  • →
  • は
  • 考える
  • 今日
  • は
  • →
  • いい
  • ターゲットモデル
  • 検証する側
  • 今日
  • は
  • いい
  • →
  • 天気
  • 今日
  • は
  • いい
  • 天気
  • →
  • です
  • 入力はぜんぶ手元にある(下書きがくれた)
  • → 並べて同時に計算するだけ。学習し直しは不要

なぜ学習が必要?②:下書きは「入力がまだない」

同じことを、下書きを作る側でやろうとするとどうなるでしょうか。

手元にあるのは「今日」だけです。1行目は問題ありません。「今日」を見て「は」を出せます。

困るのは2行目からです。2行目の入力は「今日 は」のはずですが、その「は」は、いままさに1行目で計算している最中です。同時に計算したいのに、入力がまだ存在しません。

仕方がないので、空いている場所には伏せ字(「ここはまだ分からない」という印の特別なトークン。図の灰色の「?」)を入れて計算することになります。

ところが、ふつうのLLMは「本物のトークンが左に並んでいる状態で、次の1個を当てる」練習しかしていません。伏せ字が並んだ入力は一度も見たことがないので、コードだけ書き換えて同時に計算しても、2個目以降はでたらめが出ます。

足りないのは計算の速さではありません。「伏せ字の並びを見て、中身を当てる」という能力そのものです。

  • なぜ学習が必要?②:下書きは、入力がまだない
  • 下書き:まだ「今日」しかないのに、4個先まで一度に出したい
  • 今日
  • →
  • は
  • 電気
  • 今日
  • ?
  • →
  • ×
  • ドラフター
  • 下書きする側
  • 今日
  • ?
  • ?
  • →
  • ×
  • 今日
  • ?
  • ?
  • ?
  • →
  • ×
  • 2行目からは、入力がまだ存在しない(伏せ字「?」を入れるしかない)
  • → 伏せ字を見たことがないLLMでは、でたらめが出る

なぜ学習が必要?③:解決策は2つある

① 学習なし:小さいモデルが1個ずつ書く

入力がないなら順番に作ればいい、という素直な方法です。小さいモデルが1個書き、それを入力に足して次の1個を書く。これを繰り返します。

  • 既存の小さいLLMをそのままドラフターに使える。学習は不要
  • 1回の計算は軽いが、下書きを長くするほど回数が増える

「ふつうの投機的デコーディング」はこの形です。大きいモデルと、同じ系列の小さいモデルが手元にあれば、すぐに試せます。

② 学習あり:伏せ字を1回で全部埋める

伏せ字から中身を当てる練習を、専用にさせる方法です。これが拡散ドラフターで、D3Dもこちらです。

  • 何個先まで書いても計算は1回。下書きを長くできる(D3Dは15個)
  • そのかわり専用の学習が必要。既存のモデルを持ってきて、そのままD3Dのドラフターにすることはできない

論文では、メインのドラフターの学習に約142万件の教材と、360GPU時間(高性能GPU 2枚)をかけています。「D3Dで5倍速くなる」のは、この学習を済ませたドラフターが手元にある場合の話です。

次のページから、その練習の中身(学習①・②)を見ていきます。

  • なぜ学習が必要?③:解決策は2つある
  • ① 学習なし:小さいモデルが、1個ずつ順番に書く
  • は
  • いい
  • 天気
  • だ
  • → たまったら、まとめて検証へ
  • かかる時間:小さいモデルの計算4回ぶん(1回が軽いので、合計でも短い)
  • ふつうの投機的デコーディング。今あるモデルのまま、すぐ試せる
  • ② 学習あり:伏せ字を、1回の計算でぜんぶ埋める(拡散ドラフター)
  • は
  • 伏せ字から中身を当てる「練習」をさせる
  • いい
  • 何個先まで書いても計算は1回。下書きを長くできる
  • 天気
  • ただし専用の学習が必要。既存のモデルをそのままは使えない
  • だ
  • → D3Dはこちら。次のページから、その練習の中身
  • かかる時間:計算1回ぶん

学習①:伏せ字を1回で全部当てる練習

隠れ状態を借りないぶん、ドラフター自身が賢くなければいけません。そこでD3Dは、ターゲットモデルが実際に書いた回答(約142万件の質問に対するもの)を教材にして、ドラフターを鍛えます。

練習問題はこう作ります。

  1. 回答の中からランダムな位置を選び、そこをアンカーにする
  2. アンカーに続く15トークンをマスク(伏せ字を表す特別なトークン)で隠す
  3. ドラフターは「それより前の文」と「ブロック自身」を見て、15個を1回の計算で同時に当てる

ブロックの中では、前後どちらの位置を見てもかまいません(双方向の注意。注意 = アテンション = どの位置の情報を参考にするかを決める仕組み)。

1つの回答からアンカーを128か所とり、まとめて練習します。本番の「パック」と同じ形です。

もう1つの工夫は出発点です。ドラフターをゼロから育てるのではなく、すでに言葉を知っている学習済みモデル(Qwen3)から始めます。ゼロから始めた場合と比べて、受理長は約4.5 → 約9.8と2倍以上になりました(算数の文章題ベンチマーク GSM8K での比較)。

  • 学習①:アンカー以外を隠して、1回で全部当てさせる
  • これまでの文(プロンプト + 確定済みの回答)
  • アンカー
  • マスク(伏せ字にした15トークン)= ここを一度に予想する
  • ブロックの中では、前も後ろも見てよい(双方向)
  • 穴埋めを何段階もくり返さず、1回の計算で15個すべてを出す

学習②:前のトークンほど重く採点する

検証のルールを思い出してください。最初に外れた位置から後ろは、全部捨てられます。 つまり1個目を外すと、2〜15個目がどれだけ正確でも無意味です。

そこでD3Dは、採点(損失 = 予想がどれだけ外れたかを表す点数。これが小さくなるように学習する)に、位置ごとの重みをつけます。

w_j = \exp\left(-\frac{j-1}{\gamma}\right)

j は何個目の下書きか、\gamma は減り方のゆるさを決める定数です。図の紫の棒がその重みで、前ほど高く、後ろへ行くほど低くなります(棒の高さはイメージです)。

補助の練習:ふつうの「次の1トークン当て」

もう1つ、伏せ字にしていない回答に対して、ふつうのLLMと同じ「次のトークンを当てる」練習(AR損失)も同時に行います。

\mathcal{L}_{\mathrm{train}} = \mathcal{L}_{\mathrm{diff}} + \lambda\,\mathcal{L}_{\mathrm{AR}}

\mathcal{L}_{\mathrm{diff}} が伏せ字当ての損失、\mathcal{L}_{\mathrm{AR}} が次トークン当ての損失、\lambda は2つのバランスです。ターゲットの隠れ状態に頼らず、自分の力で文脈を読み取る力を保つための練習です。

λ受理長(GSM8K)受理長(HumanEval)
0(補助なし)7.895.86
0.58.056.02
1.0(採用)8.296.25
1.58.256.19

(小さめのドラフターで試した比較実験の値です)

  • 学習②:前のトークンほど重く採点する
  • 1個目を外すと、残りは全部ムダになる
  • → 前の位置ほど、採点の重みを大きくする
  • w_j = e^{-(j-1)/\gamma}
  • + ふつうの「次の1トークン当て」も同時に練習 (AR損失、バランス λ = 1)
  • アンカー
  • 下書きの1個目 → 15個目

残る課題:ドラフターが重い

D3Dのメインのドラフターは、約82億パラメータ(パラメータ = モデルが学習で調整する数値。多いほど賢いが、計算は重い)、36層(層 = 計算の段。入力はこの段を順番に通って処理される)。下書き係としてはかなり大きなモデルです。

大きいドラフターほど、下書きはよく当たります。

ドラフターパラメータ受理長(MATH-500)
DFlash(従来)23億6.87
D3D-1.7B20億8.16
D3D-4B44億9.45
D3D-8B82億9.89

ふつう、大きいドラフターは遅くて使い物になりません。でも非同期なら、下書きが検証より先に終わりさえすれば、その重さは表に出ません。

裏を返すと、384トークンのパックを処理する時間が検証の時間を超えると、ヒットしても待ちが出てしまいます(図の下の棒。長さはイメージです)。

そこで最後の工夫です。当たりやすさをほとんど落とさずに、ドラフターを軽くします。

  • 残る課題:下書きが検証より遅いと、裏に隠れきれない
  • ドラフターの中身:36層(計算の段)を順番に通る
  • 検証の時間
  • 下書きの時間
  • はみ出した分だけ、待ちが出る
  • ※棒の長さはイメージ
  • ヒットしても、下書きが間に合わなければ効果が薄れる

層の枝刈り:効いていない層から抜く

枝刈り(プルーニング = モデルの一部を取り除いて軽くすること)の手順はこうです。

  1. 残っている層を1つずつ試しに抜いて、受理長がどれだけ落ちるかを測る
  2. 落ち方がいちばん小さい層を、本当に抜く
  3. 回復学習:抜いた後のモデルを1万ステップ追加で学習させる。このとき、抜く前の元のドラフターを先生にして、その出力を真似させる(蒸留 = 大きいモデルの振る舞いを小さいモデルに写し取る学習法)
  4. 回復したモデルで、もう一度1から測り直す

先生が間違えている位置では、正解のほうが必ず少しだけ優勢になるように先生の答えを補正する、という細かい工夫も入っています。

結果

  • 36層 → 31層(深さを13.9%削減)
  • 受理長は元の99%以上を維持
  • 速度向上は 4.44倍 → 4.69倍(3つのベンチマークの平均)
  • 31層より減らすと、受理長も速度も落ち始める

一般的な「精度」ではなく、速さに直結する「受理長」を見ながら削るのがポイントです。

  • 層の枝刈り:効いていない層を抜いて、下書きを速く
  • 5層を抜いて 36層 → 31層(抜く層は実測で決める。図の位置はイメージ)
  • 検証の時間
  • 下書きの時間
  • 検証の裏に、すっぽり隠れる
  • ※棒の長さはイメージ
  • 速度向上は4.44倍 → 4.69倍。受理長は99%以上を維持

結果①:Qwen3-8B を相手に、平均5.00倍

いよいよ成績です。棒の高さは「下書きなしで1トークンずつ書いた場合の、何倍速いか」。7種類のベンチマーク(数学3つ、プログラミング3つ、雑談1つ)の平均です。

手法使うGPU平均の速度向上平均の受理長
SSD2枚1.84倍3.25
EAGLE-31枚2.88倍4.56
DFlash1枚4.03倍5.96
PARD-22枚4.08倍5.62
DSpark1枚4.44倍5.59
D3D2枚5.00倍7.88

GPU 1枚の手法は「下書き → 検証」を交互に行う方式、2枚の手法はターゲットとドラフターを別々のGPUに載せる方式です。

受理長7.88は、「ターゲットの計算1回で、平均8トークン近く進む」という意味です。

D3Dのベンチマーク別の成績

ベンチマーク種類速度向上受理長
GSM8K数学6.71倍9.87
MATH-500数学6.21倍9.84
AIME25数学5.11倍7.98
HumanEvalコード5.19倍7.79
MBPPコード4.70倍7.31
LiveCodeBench-v6コード4.44倍6.68
MT-Bench雑談2.68倍5.71

数学のように次の展開が読みやすい文章ほどよく当たり、自由な雑談では伸びが小さくなります。7つのうち5つでD3Dが最速でしたが、LiveCodeBench は DSpark と同じ4.44倍、MT-Bench は DSpark(3.17倍)のほうが上でした。

  • 結果:1トークンずつ書く場合の、何倍速いか
  • ターゲット:Qwen3-8B(7つのベンチマークの平均)
  • 5.00倍
  • 4.44倍
  • 4.03倍
  • 4.08倍
  • 2.88倍
  • 1.84倍
  • 1倍
  • SSD
  • EAGLE-3
  • DFlash
  • PARD-2
  • DSpark
  • D3D

結果②:もっと大きい Qwen3-32B では5.71倍

ターゲットを4倍大きい Qwen3-32B に替えると、棒が伸びて差が開きます。

手法平均の速度向上平均の受理長
EAGLE-33.46倍5.08
SSD3.54倍4.22
DFlash3.67倍4.82
PARD-23.88倍5.38
DSpark3.92倍5.48
D3D5.71倍7.73

最高は GSM8K の 7.48倍。こちらは7つのベンチマークすべてでD3Dが最速でした(ただし MT-Bench は3.01倍で、DFlash の3.00倍とほぼ並んでいます)。

ターゲットが大きいほど検証に時間がかかるので、その裏で下書きを済ませる余裕が増える、と読めます(これは筆者の解釈です)。

同時にたくさんの依頼をさばくと?

実際のサービスでは、多数の依頼を同時に処理します。小さめのドラフター(D3D-1.7B)で試した結果です(MATH-500、ターゲットは Qwen3-32B)。

同時に処理する依頼の数1秒あたりのトークン数速度向上
11546.1倍
44704.8倍
87453.9倍
168642.7倍

全体の処理量は増えていきますが、1件あたりの速度向上は小さくなります。

  • 結果:1トークンずつ書く場合の、何倍速いか
  • ターゲット:Qwen3-32B(7つのベンチマークの平均)
  • 5.71倍
  • 3.54倍
  • 3.46倍
  • 3.67倍
  • 3.88倍
  • 3.92倍
  • 1倍
  • SSD
  • EAGLE-3
  • DFlash
  • PARD-2
  • DSpark
  • D3D

まとめ:D3Dが速い3つの理由

  1. 切り離す:ドラフターは確定済みのトークンだけを読む。ターゲットの隠れ状態を待たなくてよい
  2. 先回りする:検証の裏で、ありうる結果24通りの続きを1回の計算で下書きしておく。当たれば(約7割)待ち時間ゼロ
  3. 軽くする:受理長への影響が小さい層を抜いて、下書きを検証の時間内に収める

これらを支えているのが、「前の位置ほど重く採点する」学習と、ふつうの次トークン予測を混ぜる補助の練習でした。

割り引いて読むべき点

論文自身が挙げている制約です。

  • GPUを2枚使う:同じ2枚を別の高速化(1つのモデルを2枚に分けて動かすなど)に使った場合との比較はしていない
  • ミスの回は従来どおり待つ:約3割のラウンドでは下書き待ちが発生する
  • 研究用の環境での測定:実験は Transformers というライブラリ上。実運用向けの推論基盤(vLLM や SGLang)ではまだ試されていない
  • ドラフターの分だけ、メモリと計算が余分にかかる

ブロックの長さも綱引き

ブロックの長さ速度向上(MATH-500)受理長
84.58倍6.26
16(採用)5.55倍9.89
323.16倍12.39

長くするほど1回で進む量は増えますが、パックが重くなり、かえって遅くなります。

おまけ

同じドラフターは、長いプロンプトの中から重要な部分だけを選んでターゲットに読ませる、という使い方もできるそうです(論文の付録)。

---

出典:Baoyou Chen, Ruiqiao Mei, Guancong Lin, Tao Song, Weihao Yuan, Qun Liu, Siyu Zhu「D3D: Decoupled Diffusion Drafting for Asynchronous Speculative Decoding」arXiv:2611.06425(2026年11月)

この記事の図(「今日はいい天気です」の例や棒の長さ)は説明のために作ったもので、論文の図そのものではありません。数値は論文の表から引いています。

  • まとめ:D3Dが速い3つの理由
  • 文章の中身は変えずに、生成を平均5.00〜5.71倍速く
  • 開いた錠
  • ロケット
  • 電気
  • 切り離す
  • 先回りする
  • 軽くする
  • ターゲットの隠れ状態を 使わないドラフター
  • 検証の裏で、ありうる 続きを24通り下書き
  • 効きの小さい層を5つ 抜いて下書きを短縮
  • 注意:GPUを2枚使った結果。雑談では伸びが小さく、 実運用の推論基盤ではまだ試されていない

0 件のコメント

読み込み中…

権利侵害・不適切な内容の連絡: [email protected]