0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

llama.cpp で量子化版の拡散モデル「DiffusionGemma」を動かしてみたら、長けた点と課題が見えた(Colab L4)

0
Last updated at Posted at 2026-06-14

Google DeepMind が 2026/6/10 に公開した DiffusionGemma(拡散でテキストを生成する実験的オープンモデル)を、llama.cpp + 4bit量子化(GGUF)で Colab の L4 上で実際に動かしてみました。

「動かせる」こと自体は Unsloth が手順を公開しています。本記事では実際に Colab L4 で動かし、速度と品質を測って 長けた点と課題 をまとめます。

先に結論を言うと——長所は「早く確信に達するタスクほど速い、という拡散ならではの動的な速さ」、課題は「(量子化+llama.cppでは)未マージで自前ビルドが要る運用の重さ」と「試した言葉遊びでの日本語の取りこぼし」でした。

ハイライト

長けた点(現状はほぼ速度)

  • 早く確信に達しやすいタスクほど速い(拡散の適応停止=確信したら早期打ち切り)。L4/Q4 で実効 62〜132 tok/s(出力長を伸ばせば最大154)、事実質問(P1)132・数列(P5)122 が爆速。今回の計測では速度は出力長より denoising ステップ数に強く依存。
  • 固定48ステップ(25.8 tok/s)比で最大5倍。今回の計測で速さに最も効いていたのは、256並列キャンバスより適応停止(早期打ち切り)でした。
  • 同条件の自己回帰版 Gemma 4(約56 tok/s)と直接比較: 確信タスクで約2.4倍速、難タスクで拮抗。生成方式だけを変えたフェアな対照(同 P1-P6 / 同 Q4_K_M / 同 L4 / 同 llama.cpp)。

課題

  • 量子化+llama.cpp 経路が未成熟: llama.cpp の diffusion 対応は未マージ(PR #24423)。pip wheel・Ollama になく、専用 llama-diffusion-cli自前ビルド必須(筆者環境で約11分)。※大容量GPUがあれば vLLM/Transformers は公式に動く。
  • 今回試した日本語の言葉遊び(P2)では自己回帰版より明確に弱い: 「すもも/もも/係助詞"も"の区別」を拡散はほぼ無回答、自己回帰版は正しく分解(P2の1タスクなので日本語全般の結論ではない)。品質は全般に本家 Gemma 4 より下。
  • ※ 英語思考・冗長thinking・thinkingが予算を食う点は拡散固有ではなく Gemma 4 共通の癖(自己回帰版でも同じ)。同条件の対照実験で切り分け。

DiffusionGemma とは

ふつうの LLM はトークンを1個ずつ左から生成します(自己回帰)。DiffusionGemma は違って、256 トークンの「キャンバス」をノイズから一気に並列で denoise(ノイズ除去)して作る「離散拡散」方式です。

正確には ブロック拡散(block diffusion) と呼ばれる、拡散と自己回帰のハイブリッドです。文章を 256 トークンの「キャンバス(ブロック)」に区切り、各ブロックの中身は拡散で並列生成ブロックの並べ方は自己回帰で進めます。画像生成のような“全体を一気に拡散”する純粋拡散とは違い、長文の安定性のために自己回帰を組み合わせた方式です。

  • アーキは Gemma 4 の 26B A4B MoE(総 25.2B / アクティブ 3.8B、128エキスパート中8active+shared1)
  • キャンバス長 256、コンテキスト最大 256K、テキスト+画像入力
  • 1 forward pass で 15〜20 トークン生成、H100/FP8 では 1100 tok/s 超を主張(※L4ではそこまで出ません。後述)
  • 品質は本家 Gemma 4 より低め(速度特化)

DiffusionGemma は encoder-decoder 構成です。プロンプトは自己回帰エンコーダが(ふつうの LLM と同じ因果的な読み方で)処理して KV キャッシュ化し、デコーダがキャンバスを 双方向 attention(前後を見渡す)でノイズ除去します。「左から1個ずつ」ではなく前後を見渡して一括で直せるのが、並列生成できる理由です。ちなみに“ノイズ”は [MASK] ではなく語彙からのランダムなトークンです。

生成の流れは、256トークンのキャンバスを1ブロックとして仕上げ、それを文脈(KVキャッシュ)に積んで次のブロックへ——と、ブロック単位で順に進みます。

そして1つのキャンバスは一発で決まるのではなく、ノイズ除去(denoise)を数回くり返して仕上げます。この反復回数が「denoising ステップ数」。十分に確信できたら最後まで回さず途中で打ち切るのが「適応停止」で、これが後で速度を大きく左右します。


環境構築でハマった点(ここが一番の山)

前提として、「llama.cpp + 量子化で動かせる」こと自体は Unsloth の公式ドキュメント が手順を公開しており、既知です。ただし PR #24423未マージで、公式リリース・pip wheel・Ollama(対応要望Issueがopen)には入っておらず、専用 llama-diffusion-cli自前ビルドが必要な bleeding edge 段階です。以下、実際に動かして詰まった点をまとめます。

「60GB必須」の抜け道

Google の推論ノートブック(inference-diffusiongemma-with-hf)には「60GB超のメモリを持つ GPU が必要(NVIDIA G4またはNVIDIA H100)」という注記があり(原文: "requires a GPU which has more than 60GB of memory")、最初は「Colab Pro の A100(40GB) でも無理では」と思いました。でもこれは BF16(52GB) の話。量子化版を見ると要件はガラッと変わります。

補足: この「60GB超」は HF のモデルカード(モデルページ)には書かれておらず、推論ノートブック側の注記です。モデルカードは GPU メモリ要件を明記していません。

量子化 必要メモリ L4(22.5GB)
4-bit 18GB ✅ 余裕
5-bit 20GB
6-bit 24GB ❌(A100)
8-bit 28GB ❌(A100)
BF16 52GB

4bitなら安いL4で動く。A100が不要でした。

(この必要メモリの目安は Unsloth のドキュメント より。表の「L4」は公称 24GB・実利用は約 22.5GB 枠で判定。)

llama.cpp の diffusion 対応は「未マージ+専用バイナリ」

ここが本番。普段 llama.cpp は pip install llama-cpp-python のプリビルト wheel で済ませていますが、今回はそれが一切使えません

  1. diffusion 対応は llama.cpp 本体に未マージ(PR #24423、執筆時点レビュー中)→ どの wheel にも入っていない
  2. 通常の llama-cli / llama-server では動かず、専用の llama-diffusion-cli が要る → llama-cpp-python の Python API にも存在しない
  3. 見つかったコミュニティ製のプレビルト配布は sm_86(RTX30系)+glibc≥2.39 専用で、Colab(L4 sm_89 / Ubuntu22.04)では動かなかった

結局 Colab上でソースビルドするしかありません。ただし1ターゲットだけ+アーキを実機に絞れば、筆者環境のL4で約11分で済みました。

# PR #24423 を取得し、llama-diffusion-cli だけを L4(sm89) 向けにビルド
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
git fetch origin pull/24423/head:diff && git checkout diff
cmake -B build -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=89 -DLLAMA_CURL=OFF
cmake --build build -j --config Release --target llama-diffusion-cli

ビルド済みバイナリ(llama-diffusion-cli + *.so)を Google Drive に保存しておけば、次のセッションは数秒で復元・再ビルド不要です。「1セッション1条件で回してセッションを消す」運用と相性が良い。

地味にハマった: libcuda.so.1 cannot open

CLI を Python の subprocess で叩くとき、LD_LIBRARY_PATH をバイナリのディレクトリで丸ごと上書きしたら、Colab 既定の CUDA ドライバの場所(/usr/lib64-nvidia)が消えて error while loading shared libraries: libcuda.so.1 で落ちました。上書きせず追記で解決:

env = dict(os.environ)
env['LD_LIBRARY_PATH'] = ':'.join(p for p in [BIN_DIR, env.get('LD_LIBRARY_PATH',''), '/usr/lib64-nvidia'] if p)

計測方法

  • llama-diffusion-cli を subprocess で起動し、出力末尾のスループット行をパース
  • クロック固定nvidia-smi -lgc/-lmc で最大に)→ 共有VMのガバナー変動を抑制。結果、cv 0.1〜0.3% と非常に安定
  • 各条件 n=3〜5 試行の中央値、結果は Drive の CSV に追記
  • モデルロードは毎回約16秒だが、計測する eff_tok_s生成のみのスループットなので影響なし

スループットには2種類ある(混同注意)

実機の出力はこうです:

throughput: 65.9 tok/s (256 tok in 3882.76ms),
            in-step parallel 1187 tok/s (256-tok canvas x 18.0 steps/block)
18 steps over 1 block, time per step: 215.71ms
指標 値(L4/Q4) 意味
実効スループット 65.9 tok/s 体感する「実際に出てくる速度」。本記事で使うのはこちら
in-step parallel 1187 tok/s 256位置×18ステップを並列処理した「計算量ベース」の数字

Google の「H100/FP8 で 1100 tok/s 超」は実効値の主張。L4 の in-step parallel(1187) がたまたま近いだけで別物です。L4 の実効は数十〜150 tok/s 台(本記事の測定範囲)。


使ったプロンプト(P1〜P6)

計測には、この検証シリーズで共通利用しているテスト用プロンプト(prompts.json)を使いました。以降の表・本文に出てくる P1〜P6 は次の通りです。

ID 内容 ねらい
P1 「日本の首都はどこですか。一言で。」 短い事実質問
P2 「すもももももももものうち」に果物名はいくつ? 日本語のひっかけ(言葉遊び)
P3 早口言葉「woodchuck…」に果物名はいくつ?(英語) 英語のひっかけ
P4 長文(4k/8kトークン)の日本語要約 長い入力の処理(※速度比較では未使用)
P5 「1〜200をカンマ区切りで」 長く・内容が決まりきった出力
P6 天気取得APIの関数呼び出し 構造化出力(tool use)

結果A: 速度を決めるのは「出力長」ではなく「ステップ数」

上記のうち短い P1/P2/P3/P5/P6 を、同じ -n 256-n は生成トークンの上限。256=1キャンバス分)で回しました。

難易度スイープ

プロンプト 実効 tok/s denoising steps
P1 日本の首都は?(一言で) 132.0 8
P5 1〜200を順に書く 122.1 9
P3 早口言葉に果物名は何個(英語) 81.0 14
P6 関数呼び出し(tool use) 77.1 14
P2 「すもももももももものうち」の果物名は何個 61.7 19

全部 n=256 で出力長は同じなのに、速度が約2倍違う。理由はシンプルで:

  • 1ステップの時間はほぼ一定(〜225ms = 256キャンバスを1回 denoise するコスト)
  • 実効 tok/s ≒ 256 ÷ (steps × 0.225s) → ステップ数に反比例
  • このEBサンプラー設定では、ステップ数は entropy/confidence 条件による早期停止に大きく左右される(適応停止)

事実質問(P1)や単純な数列(P5)は答えが確定的でエントロピーが低く、すぐ収束(8-9ステップ)。逆に P2「すもも」のような“ひっかけ”は最遅(19ステップ) —— モデルが確信に達しにくいほどステップが増えます。今回の測定では、速度は出力長よりも“モデルがどれだけ早く確信に達するか”に強く左右されました。自己回帰では(内容に依らず一定なので)見られない挙動です。


結果B: 出力長に対しては「山なり」

P5(1〜200をカンマ区切りで=決定的に長い出力)で -n を変えました。

出力長スイープ

-n tok/s steps blocks n_tok
256 122.2 9 1 256
512 154.3 14 2 512
1024 138.6 27 4 1024
  • 512でピーク(154 tok/s)→ 1024で低下(139)。1ステップあたり確定トークン数は 28→37 と増えており、1ブロック目の固定オーバーヘッドが2ブロックで薄まるのが効いていそう。
  • 長くなると1ステップ時間が伸びる(233→237→274ms)。ブロックを順に積み上げる方式なので、後ろのブロックほど長い文脈を見るためと思われる(attention コストは直接は未計測)。ただし上昇は緩やか(4ブロックで約17%)。
  • P5 は数列なのでエントロピーが低く、1ブロックあたり 5〜9ステップと少ない(A の P2=19ステップと対照的)。長い出力でも“内容が確定的”なら高速を保てる好例。

結果C: 今回の計測で速さに最も効いていたのは「適応停止」

P2(すもも)で早期停止を無効化(--diffusion-eb-confidence 0.0)し、常に48ステップ回す設定と比較しました。

適応停止の効果

tok/s steps
adaptive(早期停止あり) 61.7 19
fixed 48(早期停止なし) 25.8 48

2.4倍速。1ステップ時間は同じ(〜210ms)なので、差は純粋にステップ数だけです。つまり 固定ステップにすると DiffusionGemma は 25.8 tok/s まで落ちる(後述の結果Eのとおり、同じ Gemma 4 の自己回帰版56 tok/s よりむしろ遅い)。「拡散だから速い」というより、今回の計測では“確信したら途中で止める”適応停止の寄与が最も大きく見えました(Google は並列デコードやブロック拡散自体も高速化の要因として挙げています。本記事で切り分けられたのは適応停止の効果まで)。

1ステップ時間が内容に依存しない(〜210ms一定)ので、固定48は全タスクで約25.8 tok/s になります。するとAの各タスクが適応停止で得ている倍率が出せます:

タスク 適応 tok/s 固定48比
P1 日本の首都 132.0 5.1倍
P5 1〜200 122.1 4.7倍
P3 早口言葉(英語) 81.0 3.1倍
P6 関数呼び出し 77.1 3.0倍
P2 すもも 61.7 2.4倍

適応停止は、確信が持てるタスクほど効く(P1で5.1倍、迷うP2で2.4倍)。


結果D: 出力品質 —「Gemma 4 共通の癖」と「今回のタスクで見えた拡散側の弱点」を切り分ける

速度だけでなく実際の生成テキストも P1-P6 で取り、自己回帰版 Gemma 4 と同一プロンプトで見比べました。すると、当初「拡散の弱点」に見えたものの多くが、実は Gemma 4 ファミリー共通の癖だと分かりました。対照実験をやった甲斐がここに出ます。

Gemma 4 共通(自己回帰版でも同じ)

  • 思考(thinking)が日本語プロンプトでも英語。拡散も自己回帰も英語で考え、最後だけ日本語に訳す:
    [拡散]    <|channel>thought  * Question: "日本の首都..." ... <channel|>東京です
    [自己回帰] * Question: "日本の首都..." ... [End thinking] 東京
    
  • thinking が長くトークン予算を食う。両者とも P2/P3 では思考の再確認に -n を使い切りがち(自己回帰版も 512 トークンで最終回答に届かないことがある)。

→ つまり「思考が英語」「冗長な思考」は拡散固有ではなく Gemma 4 全体の特性

今回のタスクで見えた、拡散側の弱点(P2)

  • 難しい日本語のひっかけ(P2「すもももももももものうち」)で拡散はほぼ無出力* Input sentence: 『す で停止)。一方、自己回帰版は すもも=Plum / もも=Peach と正しく分解できる:
    [拡散]    * Input sentence: 『す        ← ほぼ何も出ない
    [自己回帰] すもも(Sumomo)=Plum / も=particle / もも(momo)=Peach / ...  ← 正しく解析
    
    生成方式の違いが品質に出た一例です。ただし これは P2 という1タスクの観察で、「日本語全般が弱い」と一般化できる根拠(JA-MMLU 等のベンチ)は測っていません。

構造化・短答は拡散も問題なし

  • P1(東京)✅ / P6(正しい JSON 関数呼び出し)✅ / P3(英語の「0個」正解)✅ / P5(数列生成)✅。

→ まとめると、英語思考・冗長thinkingは Gemma 4 共通で拡散の弱点ではなく、今回の言葉遊び(P2)では拡散側だけが取りこぼした(自己回帰版は解けた)。本家 Gemma 4 の方が品質は上、という公式の位置づけとも整合します。用途を選べば(短答・構造化・英語)拡散も十分実用、というのが実機の感触です。


結果E: 自己回帰版 Gemma 4 26B A4B との直接対決

いちばん知りたかった比較です。DiffusionGemma の土台になった自己回帰版 Gemma 4 26B A4Bunsloth GGUF)を、同じ Q4_K_M・同じ L4・同じ llama.cpp ビルドで測りました(生成方式だけが違う対照実験)。

拡散 vs 自己回帰

自己回帰版の decode は 約56 tok/s(同一P1-P6で 55.5〜56.8 とほぼ一定=内容に依らない)。prefill は速く、llama-bench の pp512 で 2509 tok/s。拡散と並べると:

タスク 拡散 tok/s 自己回帰(約56 tok/s)比
P1 首都 132 2.4倍速
P5 1〜200 122 2.2倍速
P3 早口言葉(英) 81 1.4倍速
P6 関数呼び出し 77 1.4倍速
P2 すもも(難) 62 1.1倍(ほぼ拮抗)
拡散・固定48ステップ 26 0.46倍(逆に遅い)

結論は明快です:

  • 確信できるタスク(事実・数列)では拡散が自己回帰の2倍以上速い(P1で2.4倍)。
  • 難しいタスク(P2)では拮抗(62 vs 56)。迷うほど拡散はステップが増え、優位が消える。
  • 適応停止を切ると拡散は自己回帰より遅い(26 vs 56)。

つまり 拡散の速さは「適応停止が効く場面」限定。タスクを選べば確かに速いが、万能の高速化ではない——というのが同条件比較の答えでした。なお prefill(長い入力の読み込み)は自己回帰が 2509 tok/s と非常に速く、長文入力用途では自己回帰が依然強い領域です。


特徴と問題点(整理)

実機で触ってみて分かった、DiffusionGemma の特徴と問題点を整理します。

特徴(強み)

現状はっきり言える強みは、ほぼ速度に集約されます。

  • 確信できるタスクでの動的な速さ: 適応停止により、モデルが早く確信に達するタスクほど少ステップで高速。同条件の自己回帰版 Gemma 4 比で最大 約2.4倍(P1、L4/Q4 で実効 62〜154 tok/s、公称は H100/FP8 で 1100+)。難しいタスクでは拮抗。
     

問題点(弱み・注意)

  • 量子化+llama.cpp 経路が未成熟: llama.cpp の diffusion 対応は未マージ(PR #24423)。pip wheel・Ollama になく、専用 llama-diffusion-cli自前ビルドが必須(筆者環境のL4で約11分)。通常の llama-cli/llama-server/llama-cpp-python では動かない。※ 大容量GPUがあれば vLLM や Transformers は公式に動きます公式デベロッパーガイド)。未成熟なのは「量子化してコンシューマGPUに載せる llama.cpp 経路」の話です。
  • 今回試した日本語の言葉遊び(P2)で取りこぼし: 「すもも」を拡散はほぼ無回答、自己回帰版 Gemma 4 は正しく分解。生成方式差が出た一例(ただし P2 の1タスクで、日本語全般の結論ではない)。
  • 品質は本家 Gemma 4 より低い: Google 自身が「最高品質には自己回帰 Gemma 4 を推奨」と位置づけ。速度とのトレードオフ。
  • (Gemma 4 共通の癖・拡散固有ではない): 思考が英語で行われる/thinking が長く -n を大きく取らないと最終回答に届かない、は自己回帰版でも同様。対照実験で切り分け済み。
  • 最適化の余地: 計測時点の PR #24423 では Flash Attention が有効になっていなかった(自動で CPU 割当→disabled)。有効化できれば速度が伸びる可能性。
  • 環境の落とし穴が多い(bleeding edge): libcuda.so.1 の参照、versioned soname(.so.0)の取りこぼし、nvidia-smi -lgc の引数仕様など、動かすまでの罠が多い。
  • BF16 は 52GB 必要: フル精度は A100 80GB / H100 クラス前提。実質、量子化が前提。

まとめ

  • DiffusionGemma は Colab L4 + 4bit で動く。ただし量子化+llama.cpp で動かすなら diffusion 対応が未マージで、専用 llama-diffusion-cli のソースビルド(筆者環境で約11分)+Driveキャッシュが要る(大容量GPUなら vLLM/Transformers が公式に動く)。
  • 今回の計測では、速度は出力長より denoising ステップ数に強く左右され、ステップ数は entropy/confidence 条件による早期停止で大きく変わった。事実質問(P1)132 tok/s vs ひっかけ(P2)62 tok/s。
  • 今回の計測で速さに最も効いたのは適応停止(Google は並列デコードやブロック拡散も要因に挙げる)。固定48ステップなら25.8 tok/s(同 Gemma 4 の自己回帰版56より遅い)。適応で最大5倍。
  • 同条件の自己回帰版 Gemma 4(約56 tok/s)と直接比較すると、確信タスクで約2.4倍速・難タスクで拮抗・適応停止を切ると逆に遅い。拡散の速さは「適応停止が効く場面」限定で、万能の高速化ではない。
  • 出力長(P5)に対しては512付近がピーク。長くなるほど1ステップが少し重くなるが、内容が確定的なら長文でも高速を保つ。
  • 品質: 英語思考・冗長thinkingは Gemma 4 共通の癖(自己回帰版も同じ)。今回の言葉遊び(P2)では拡散側が取りこぼし(自己回帰版は正解)——ただし P2 の1例で、日本語全般の結論ではない。短答・構造化・英語なら拡散も実用十分。

計測条件: Colab Pro / NVIDIA L4(24GB, sm89) / DiffusionGemma 26B A4B-it / unsloth GGUF Q4_K_M / llama-diffusion-cli(llama.cpp PR #24423, commitは取得時点の最新)/ プロンプトは本シリーズ共通の prompts.json(P1-P6)/ EB sampler max_steps=48・entropy_bound=0.1・confidence=0.005(C のみ confidence=0.0)/ クロック固定(sm/mem最大)/ n=3〜5 中央値。

※ 再現性: セッション内 cv は 0.1〜0.3%。別ランタイム(新セッション・trials=7・SMクロック固定)で主要点を測り直しても初回と数%以内で再現。L4 の自動クロックがこの負荷で既に最大付近に張り付いているため、クロック固定の有無で結果はほぼ変わらない。

※ H100/FP8 公称の 1100 tok/s 超は L4 では再現しない(ハード差)。また計測時点の PR #24423 では Flash Attention が有効になっていなかった(自動でCPU割当→disabled)ため、有効化できれば各数値はさらに伸びる余地がある。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?