Skip to content
Published on

Diffusion LMの台頭 — 自己回帰の代替になり得るか

Authors

はじめに — 拡散モデルがテキストに渡ってきた瞬間

2026年6月、GoogleがDiffusionGemmaを公開しました。26BパラメータのMoE構造、Apache 2.0ライセンス、そして既存の自己回帰(autoregressive)モデル比で最大4倍速いテキスト生成という主張とともにです。GeekNewsに上がった紹介記事には急速にコメントが集まり、Hacker Newsでも「ついに自己回帰の代替が現れるのか」という論争が巻き起こりました。

画像生成では拡散(diffusion)モデルがすでに標準です。しかしテキストは事情が違いました。トークンはピクセルと違って離散的(discrete)であり、言語は順序が意味を作るからです。それでもテキスト拡散モデルが今注目される理由は明確です。自己回帰生成の構造的限界、特に「一度にトークン1つ」という逐次性が、推論コストとレイテンシの根本原因として指摘されているからです。

本記事では、自己回帰の限界からテキスト拡散の動作原理、DiffusionGemmaの分析、そしてベンチマークの主張をどこまで信じるべきかまで、順に見ていきます。

自己回帰生成の構造的限界

現在のほぼすべてのLLMは自己回帰方式です。次のトークンの確率分布を予測し、1つサンプリングし、それを入力に付け足して再び予測する、という過程を繰り返します。

自己回帰生成(逐次的):

入力: "今日の天気は"
  ステップ1: [今日の天気は] -> "とても"
  ステップ2: [今日の天気は とても] -> "良い"
  ステップ3: [今日の天気は とても 良い] -> "ですね"
  ...
  トークンN個 = モデルforward N回(直列、並列化不可)

この構造には3つの本質的な限界があります。

  1. 逐次性:トークンをN個作るにはforwardパスがN回、直列に必要です。どれだけGPUが大きくても、単一シーケンスの生成レイテンシは縮めにくいのです。
  2. メモリ帯域幅のボトルネック:デコーディング段階はバッチが小さいとき、演算ではなく重みとKV cacheを読み込むメモリ帯域幅に縛られます。GPUの演算ユニットはほとんど遊んでいます。
  3. やり直しがきかない:一度出したトークンは修正できません。文の前半で間違った方向に進むと、後から正す構造的手段がありません。

投機的デコーディング(speculative decoding)やマルチトークン予測のような補完策は登場しましたが、すべて自己回帰という枠の中の最適化です。拡散モデルは枠そのものを変えようという提案です。

テキスト拡散モデルの原理 — マスキングとデノイジング

画像拡散モデルはピクセルにガウシアンノイズを加え、それを逆転させる過程を学習します。テキストは離散的なのでノイズを加えられないため、ほとんどのテキスト拡散モデルはマスキングをノイズとして使います。これをマスクド拡散(masked diffusion)あるいは離散拡散(discrete diffusion)と呼びます。

学習(forward process): 段階的にマスキング
  "猫が ソファの 上で 眠って いる"
   -> "猫が [M] 上で 眠って [M]"
   -> "[M] [M] 上で [M] [M]"
   -> "[M] [M] [M] [M] [M]"

生成(reverse process): 段階的に復元
  "[M] [M] [M] [M] [M]"
   -> "[M] [M] 上で [M] いる"     (確信度の高いトークンから)
   -> "猫が [M] 上で 眠って いる"
   -> "猫が ソファの 上で 眠って いる"

核心は、生成段階で複数のトークンを同時に復元できる点です。自己回帰がN個のトークンにN回のforwardを必要とするのに対し、拡散モデルはデノイジングのステップ数T回で済みます。TがNよりはるかに小さければ、その分だけ速くなります。

サンプリングループの骨格を疑似コードで表すと次の通りです。

def diffusion_generate(model, prompt_ids, gen_len, num_steps):
    # 生成区間をすべてマスクトークンで初期化
    x = concat(prompt_ids, [MASK] * gen_len)

    for step in range(num_steps):
        logits = model(x)              # 全シーケンスを一度にforward
        probs = softmax(logits, dim=-1)
        conf, pred = probs.max(dim=-1) # 各位置の確信度と予測

        # まだマスクの位置のうち確信度上位k個だけ確定
        k = unmask_schedule(step, num_steps, gen_len)
        top_positions = topk_masked_positions(conf, x, k)
        x[top_positions] = pred[top_positions]

    return x

ステップごとに「いま最も確信できるトークン」から確定し、曖昧な位置は次のステップに先送りします。すでに確定したトークンの一部を再マスク(remasking)する変種もあり、自己回帰にはない自己修正能力が生まれます。

ブロック単位の並列生成

長いテキストを一度に全部デノイジングすると品質が落ちるため、実用システムはブロック単位生成を使います。シーケンスをブロックに分け、ブロック間は自己回帰のように順番に、ブロック内は拡散で並列に生成する方式です。

ブロック単位のsemi-autoregressive生成:

[プロンプト] -> [ブロック1: 32トークンを並列デノイジング]
            -> [ブロック2: 32トークンを並列デノイジング]
            -> [ブロック3: 32トークンを並列デノイジング]

ブロック間: 逐次(因果性を維持)
ブロック内: 並列(拡散デノイジング、例: 8ステップ)
  -> トークンあたりのforward回数: 32トークン / 8ステップ = 4トークンに1回

この構造のおかげで任意長の生成とKV cacheの再利用が可能になり、自己回帰に対する確かな速度優位を維持できます。

短い系譜 — どうやってここまで来たか

テキスト拡散は突然現れたものではありません。主要なマイルストーンをたどると流れが見えます。

時期マイルストーン意義
2021D3PM離散データに拡散を定義した理論的基礎
2022Diffusion-LM埋め込み空間での連続拡散の試み
2023SEDDスコアベース離散拡散でperplexityの差を縮小
2024MDLM系マスクド拡散の目的関数を単純化、学習を安定化
2025LLaDA 8Bゼロから学習した拡散LMが同級の自己回帰に肉薄
2025Mercury, Gemini Diffusion商用レベルの速度デモ(毎秒千トークン台の主張)
2026DiffusionGemma大型MoE + オープンウェイトでエコシステムを開放

この系譜から読み取るべきことは2つあります。第一に、理論(D3PM)から実用(DiffusionGemma)まで約5年かかりました。第二に、決定的な転換点は「マスキングをノイズとして使えば目的関数が単純になる」という発見と、「既存の自己回帰の重みからアダプテーションすれば学習コストが激減する」という発見でした。

学習の目的関数 — 思ったより単純

マスクド拡散の学習はBERTのマスクド言語モデリングに似ていますが、決定的な違いがあります。マスキング比率を0と1の間でランダムにサンプリングし、比率に応じた重みを損失に掛ける点です。

import torch
import torch.nn.functional as F

def masked_diffusion_loss(model, x0, mask_token_id):
    """単純化したマスクド拡散損失(MDLMスタイル)。
    x0: 元のトークン列 (B, T)
    """
    B, T = x0.shape
    # 1) マスキング比率tを(0, 1]から一様サンプリング
    t = torch.rand(B, 1, device=x0.device).clamp(min=1e-3)

    # 2) 各トークンを確率tでマスクに置換
    mask = torch.rand(B, T, device=x0.device) < t
    xt = torch.where(mask, mask_token_id, x0)

    # 3) マスク位置の元トークンを予測
    logits = model(xt)  # (B, T, vocab)
    loss_tok = F.cross_entropy(
        logits.view(-1, logits.size(-1)),
        x0.view(-1),
        reduction="none",
    ).view(B, T)

    # 4) マスク位置のみ、1/tの重みで集計
    #    (マスキング比率が低いほど1トークンの情報価値が大きい)
    weighted = (loss_tok * mask) / t
    return weighted.sum() / mask.sum().clamp(min=1)

自己回帰の損失と比較すると違いは明確です。

自己回帰:   位置iの正解を位置0..i-1だけ見て予測(因果マスク)
            すべての位置で損失が発生、1シーケンス = 学習信号T個

マスクド拡散: マスク位置の正解を残り全体を見て予測(双方向)
            マスク位置でのみ損失が発生、マスキング比率を変えながら学習

学習信号の密度は自己回帰の方が高いのです。同じデータで同じ損失水準に到達するには拡散の方が多くの計算を使う傾向が報告されており、これが拡散LMの隠れたコストの1つです。アダプテーション戦略が脚光を浴びる理由もここにあります。プリトレーニングの重いコストは自己回帰で払い、拡散はその上に載せるのです。

トイ実装 — 動く最小限の例

原理をコードで固めるため、文字レベルのミニ拡散LMの骨格を作ってみましょう。バックボーンは因果マスクを外したトランスフォーマーで十分です。

import torch
import torch.nn as nn

class TinyDenoiser(nn.Module):
    """双方向アテンションのトランスフォーマー = 拡散LMのバックボーン。"""
    def __init__(self, vocab_size, d_model=256, n_head=4, n_layer=4, max_len=256):
        super().__init__()
        self.tok = nn.Embedding(vocab_size + 1, d_model)  # +1: maskトークン
        self.pos = nn.Embedding(max_len, d_model)
        layer = nn.TransformerEncoderLayer(
            d_model, n_head, dim_feedforward=4 * d_model,
            batch_first=True, norm_first=True,
        )
        # 核心: 因果マスクのないTransformerEncoder(双方向)
        self.blocks = nn.TransformerEncoder(layer, n_layer)
        self.head = nn.Linear(d_model, vocab_size)

    def forward(self, x):
        T = x.size(1)
        pos = torch.arange(T, device=x.device)
        h = self.tok(x) + self.pos(pos)
        h = self.blocks(h)
        return self.head(h)

学習ループは上の損失関数をそのまま使えばよく、生成はconfidenceベースのアンマスキングで実装します。

@torch.no_grad()
def generate(model, prompt, gen_len, num_steps, mask_id):
    device = prompt.device
    x = torch.cat([
        prompt,
        torch.full((1, gen_len), mask_id, device=device),
    ], dim=1)
    prompt_len = prompt.size(1)

    for step in range(num_steps):
        logits = model(x)
        probs = logits.softmax(dim=-1)
        conf, pred = probs.max(dim=-1)

        still_masked = (x == mask_id)
        still_masked[:, :prompt_len] = False
        n_masked = int(still_masked.sum())
        if n_masked == 0:
            break

        # 残りステップに均等配分して今回確定する個数を決定
        k = max(1, n_masked // (num_steps - step))
        conf = conf.masked_fill(~still_masked, -1.0)
        top = conf.view(-1).topk(k).indices
        flat = x.view(-1)
        flat[top] = pred.view(-1)[top]

    return x

この100行余りの骨格で、シェイクスピア規模のデータセットなら十分に実験できます。num_stepsをgen_lenと同じにすればほぼ逐次生成のように動き、8分の1に減らせば品質低下を目で確認できます。品質と速度のダイヤルを自分で回してみる経験が、このテーマの理解の半分です。

画像拡散と何が違うのか

同じ「拡散」という名前を使っていますが、違いは大きいです。

項目画像拡散テキスト拡散(マスクド)
データ空間連続(ピクセル、潜在ベクトル)離散(トークン)
ノイズガウシアンノイズの付加トークンをマスクに置換
復元の目標ノイズ予測または原本予測マスク位置のトークン予測
ステップ数数十ステップが一般的数ステップから数十ステップ
バックボーンU-NetまたはDiT双方向アテンションのトランスフォーマー
評価人の目にもっともらしければOK文法、事実性、一貫性のすべてが必要

特に最後の行が重要です。画像はピクセルが数個ずれても目立ちませんが、テキストはトークン1つの誤りで文が崩れます。テキスト拡散が画像より難しかった根本的な理由です。もう1つ、テキスト拡散モデルのバックボーンは因果マスクのない双方向アテンションを使います。BERTのように前後の文脈をすべて見てマスクを埋める構造が、拡散生成と自然に噛み合うのです。

DiffusionGemmaの分析 — 何が公開されたのか

2026年6月に公開されたDiffusionGemmaの公式発表内容を整理すると次の通りです。

  • 規模:26BパラメータのMoE(Mixture of Experts)。トークンあたりのアクティブパラメータはそれよりはるかに少なく、推論効率を確保
  • ライセンス:Apache 2.0。研究はもちろん商用利用まで開かれている
  • 速度の主張:同級の自己回帰モデル比で最大4倍速い生成。特にコード生成と編集ワークロードで差が大きいと主張
  • 方式:マスクド拡散 + ブロック単位生成。既存のGemma系列からのアダプテーション(adaptation)と伝えられる

注目ポイントは3つです。

第一に、MoEと拡散の組み合わせです。拡散モデルはステップごとに全シーケンスをforwardするため、ステップあたりの演算量は自己回帰より大きくなります。MoEでトークンあたりのアクティブ演算を減らし、このコストを相殺する設計と読めます。

第二に、アダプテーション戦略です。最初から拡散モデルをプリトレーニングする代わりに、既存の自己回帰モデルの重みから出発して拡散の目的関数で追加学習するアプローチは、学習コストを大幅に削減します。自己回帰モデルが蓄積した知識を引き継げるというのが、最近の研究の共通した発見です。

第三に、Apache 2.0での公開という決断です。拡散LMはまだサービングエコシステム(vLLM系の最適化エンジン)が未成熟ですが、重みを完全に開放してコミュニティにインフラを作らせる戦略と見えます。

品質 vs 速度 — 本当のトレードオフ

拡散LMの速度はタダではありません。核心的なトレードオフはデノイジングのステップ数にあります。

デノイジングステップ速度品質の傾向
トークン数と同じ自己回帰と同程度自己回帰級の品質が可能
トークン数の4分の1約4倍速い多くのベンチマークで肉薄
トークン数の16分の1約16倍速い一貫性の低下が顕著、繰り返しと矛盾が増加

つまり「4倍速い」という主張は、「品質低下を許容可能な水準に抑えたステップ数設定で4倍」という意味に読むべきです。ステップ数というダイヤルを回して品質と速度を交換する構造そのものが、拡散LMの本質です。これは弱点でもありますが、ワークロードに応じてダイヤルを調整できるという運用上の柔軟性でもあります。

もう1つのコストはKV cache活用の制約です。双方向アテンションはトークンが確定するたびに文脈が変わるため、自己回帰のような単純なキャッシュ再利用ができません。ブロック単位生成と近似キャッシングで緩和しますが、自己回帰陣営が10年近く積み上げてきたサービング最適化の資産をそのまま使えない点は、現実的なハンディキャップです。

推論コストの観点 — どこで利益が出るのか

GPUの視点で見ると話はより明確になります。

自己回帰デコーディング(バッチ1):
  forward 1回 = トークン1個
  ボトルネック: メモリ帯域幅(重みのストリーミング)
  GPU演算ユニットの稼働率: 低い

拡散デノイジング(バッチ1):
  forward 1回 = ブロック全体(例: 32トークン)を更新
  ボトルネック: 演算(シーケンス全体のアテンション)
  GPU演算ユニットの稼働率: 高い

自己回帰デコーディングは「メモリから重みを読む時間」が支配する仕事なので、GPUの演算能力は余っています。拡散はその余った演算を複数トークンの同時更新に投入します。言い換えれば、拡散LMの速度優位は無駄になっていた演算資源をレイテンシ短縮に転換することから生まれます。

したがって利益が最も大きいのは、バッチが小さくレイテンシが重要な状況(対話型、コード自動補完)です。逆に、大規模バッチ処理でGPUをすでに使い切っているサービング(オフラインのバッチ推論)では、拡散の相対的な優位は縮みます。スループット基準では自己回帰 + 連続バッチング(continuous batching)の組み合わせがすでに非常に効率的だからです。

適したワークロード — どこに使えばよいか

構造的特性上、拡散LMが特によく合うタスクがあります。

ワークロード適合度理由
草稿のドラフティング高い高速な大量生成の後に人が磨く流れと相性が良い
テキスト編集/リライト高い双方向の文脈で中間部分だけを自然に修正可能
コードのインフィリング (FIM)非常に高い前後のコードを両方見て空欄を埋める作業が本質的に一致
構造化出力 (JSONなど)高いスキーマの骨格を固定し値だけをデノイジングする制約生成
長文の推論 (chain of thought)低い段階的な因果展開が必要な作業は逐次生成が有利
精密な長文ドキュメント中間ステップ数を増やせば可能だが速度優位が消える

逆に適合度が低い領域も明確です。数学の証明や多段階の推論のように前のステップの結果が後のステップの前提になる作業は、トークンを並列に確定する拡散の生成方式と根本的に緊張関係にあります。推論特化モデルがいまだに自己回帰を堅持するのには理由があります。

特にコードの穴埋め(fill-in-the-middle)は、自己回帰モデルが特殊トークンでぎこちなく真似していた作業です。双方向アテンションを持つ拡散モデルにとっては本質的に自然な作業であり、コードツール領域から拡散LMの最初のキラーユースケースが出るだろうという見方が多いです。

実務適用ガイド — 評価パイプラインに載せる方法

拡散LMを検討するなら、次の順序をお勧めします。

  1. 自分のワークロードのプロファイル把握:平均出力長、レイテンシ要件、バッチサイズの分布をまず数値化します。短い出力 + 小さいバッチ + 厳しいレイテンシなら拡散に有利な地形です。
  2. ベースラインをきちんと立てる:比較対象の自己回帰モデルに投機的デコーディングと量子化まで適用した状態を基準にしてこそ、公正な比較になります。
  3. 品質ゲートが先、速度測定はその後:自分のドメインの評価セットで品質の下限を通過する最小ステップ数を見つけ、その設定でのレイテンシを測定します。
  4. ブロックサイズとステップ数のグリッド探索:この2つのハイパーパラメータが品質-速度曲線を決めます。曲線全体を描いてこそ運用ポイントを選べます。

レイテンシ測定の骨格は次の通りです。

import time

def benchmark_generation(generate_fn, prompts, n_warmup=3, n_runs=10):
    # ウォームアップ(カーネルコンパイル、キャッシュ効果の除去)
    for p in prompts[:n_warmup]:
        generate_fn(p)

    records = []
    for p in prompts[:n_runs]:
        torch.cuda.synchronize()
        t0 = time.perf_counter()
        out = generate_fn(p)
        torch.cuda.synchronize()
        dt = time.perf_counter() - t0
        n_new = out.size(1) - p.size(1)
        records.append((dt, n_new / dt))

    lat = sorted(r[0] for r in records)
    tps = sorted(r[1] for r in records)
    mid = len(records) // 2
    print(f"median latency: {lat[mid]*1000:.1f} ms")
    print(f"median tokens/sec: {tps[mid]:.1f}")

測定時のよくあるミスを2つ避けてください。第一に、ウォームアップなしで最初の呼び出しを測定に含めると、コンパイルのオーバーヘッドが混ざります。第二に、トークン数基準の比較では2つのモデルのトークナイザーが違うと同じテキストでもトークン数が変わるため、トークン/秒ではなく文字/秒またはタスク完了時間で比較する方が安全です。

導入判断チェックリスト

  • 出力長が短いか中程度か(数百トークン以内)
  • 編集、穴埋め、構造化出力の比重が高いか
  • p95レイテンシがビジネス指標に直結するか
  • 品質評価セットとゲートがすでに整っているか
  • サービングスタックを自分で修正する能力があるか(エコシステム未成熟の補完)

5項目のうち3つ以上にイエスと答えられるなら、パイロットの価値は十分です。

ハイブリッドの展望 — 自己回帰と拡散の結合

業界の大方の見方は「拡散が自己回帰を置き換える」ではなく「2つが結合する」です。すでに複数の方向が実験されています。

  1. ブロック自己回帰 + ブロック内拡散:上で見たsemi-autoregressive構造。因果性と並列性の折衷案として、事実上の標準になりつつあります。
  2. 拡散ドラフター + 自己回帰検証者:投機的デコーディングのドラフトモデルを拡散モデルに交換。拡散が高速に草稿を敷き、自己回帰モデルが検証・修正する構造です。
  3. 推論は自己回帰、本文は拡散:思考過程(reasoning)は逐次生成で正確に、最終回答の長文出力は拡散で高速に作るという役割分担です。
  4. アダプテーションの双方向転換:1つの重みで自己回帰モードと拡散モードの両方をサポートするモデル。ワークロードに応じて生成モードをランタイムに選択します。

DiffusionGemma自体も純粋な拡散というよりブロック単位のハイブリッドに近いという点が、この流れを裏付けています。

批判的視点 — ベンチマークを鵜呑みにしないこと

最後に、公平を期すためにいくつか牽制球を投げておきます。

  • 速度比較のベースライン問題:「4倍速い」の比較対象が何かを常に確認すべきです。投機的デコーディング、量子化、最適化カーネルをすべて適用した自己回帰モデルと比較したのか、素の実装と比較したのかで、結論は完全に変わります。
  • 品質ベンチマークの選択バイアス:拡散LMの発表は並列生成に有利なベンチマーク(短い応答、コードの穴埋め)を前面に出す傾向があります。長文の一貫性、マルチターン対話、精密な指示遵守といった弱点領域の数値は、しばしば付録にあります。
  • 尤度評価の難しさ:拡散LMは正確な尤度計算が難しく、perplexity比較は近似値(ELBO境界)に依存します。自己回帰との直接比較には常に注意が必要です。
  • エコシステムの格差:自己回帰にはvLLM、投機的デコーディング、プレフィックスキャッシングなど巨大な最適化エコシステムがあります。拡散LMの理論的な速度優位が実サービスの差につながるには、同等のサービングインフラが先に整う必要があります。
  • スケール検証の不足:拡散LMがフロンティア級の規模でも自己回帰と同じスケーリング曲線に乗るのかは、公開された証拠がまだ限られています。26B MoEは意味のある規模ですが、結論を出すには早いです。

おわりに

テキスト拡散モデルは「自己回帰の代替品」というより「推論のコスト構造を変える新しいダイヤル」と見るのが正確です。ステップ数で品質と速度を交換でき、双方向の文脈で編集と穴埋めに強く、無駄になっていたGPU演算をレイテンシ短縮に転換します。DiffusionGemmaのApache 2.0公開は、この方向のエコシステム形成を大きく前倒しするでしょう。

技術の歴史において「遅いが確実な方式」と「速いが新しい方式」が競うとき、勝者はたいてい両者の長所を合わせた折衷案でした。テキスト生成でも同じパターンが繰り返される可能性が高いです。

ただし、ベンチマークの華やかな数字は常に条件とセットで読むべきです。自己回帰陣営も止まってはおらず、最も可能性の高い未来はどちらか一方の勝利ではなく、ワークロード別の役割分担とハイブリッドです。コード自動補完やドキュメント編集ツールを作っているなら、今こそ拡散LMを実験パイプラインに載せてみる好機です。

参考資料