こんにちは、パレイド技術部の夏目です。
次世代 Qwen4 アーキテクチャの先行版として、Qwen3.8-Flash-Next が 8/26 に公開されました。MoE(Mixture of Experts、入力ごとに一部の専門家サブネットだけを動かす構造)ですが、公称パラメータ数が情報源によって 125B と 180B に割れています。その差は 55B。最小の量子化ファイルでも 72.5GB あり、32GB の MacBook Air M5 には素直に考えて載りません。それでも動きました。毎秒 2.5 トークンで。この記事は、なぜ載ったのかと、載ったところで何ができないのかの記録です。
本記事はローカル LLM による自動執筆パイプラインで生成されました。現段階ではクラウド AI(Claude 等)の補助や人間の編集が介在していますが、pareido.jp では最終的に AI が自律的にコンテンツを制作できる仕組みの構築を目指しています。
Ollama には降りてこない
前回 Qwen3.8-27B で踏んだ壁を、今回も同じ場所で踏みました。
# 手元の Ollama 0.32.15 で pull を叩く
ollama pull qwen3.8-flash-next:125b-a6b-nvfp4
# Error: pull model manifest: 412:
# The model you are attempting to pull requires a newer version of Ollama.
対応は v0.33.1(8/26)で入っていますが、リリースノートの記述は「MLX: Qwen3.8 Flash Next support」。MLX バックエンドだけの対応です。実際、Ollama のライブラリに並ぶタグは 125b-a6b-mlx-bf16(360GB)、125b-a6b-nvfp4(113GB)、125b-mlx(113GB)の 3 つで、GGUF はありません。最小の 113GB でも 32GB 機には無理なので、Ollama を最新にしても行き止まりです。
llama.cpp 側も追いついていません。master の対応アーキテクチャ一覧は qwen3next / qwen35 / qwen35moe までで、qwen4exp がありません。対応 PR #27742 は 8/28 時点で open のドラフトのままです。unsloth の GGUF 配布ページ自身が「動かすならこの PR を使え」と案内しているので、素直に従いました。
# unsloth 側のブランチを取得して Metal 付きでビルド(55 秒)
git clone --depth 1 -b qwen4exp/qwen3.8-flash-next \
https://github.com/unslothai/llama.cpp.git
cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_METAL=ON
cmake --build build -j 8
| 項目 | 内容 |
|---|---|
| 検証機 | MacBook Air (M5 / 10 コア) |
| メモリ | 32GB ユニファイドメモリ |
| OS | macOS 26.4.1 (25E253) |
| 推論エンジン | llama.cpp b1-250b614(PR #27742 ブランチ) |
| Metal working set | 25,559 MiB |
| 対象モデル | Qwen3.8-Flash-Next UD-IQ1_S(72.5GB / 3 分割) |
| 比較対象 | qwen3.8:27b(Q4_K_M / 17GB、前回計測値) |
| 計測時の load average | 3〜4(1 分平均) |
ダウンロードは 2,374 秒(39 分 24 秒)、平均 30MB/s。ディスクの空きは 132GiB から 48GiB になりました。unsloth が用意している量子化は UD-IQ1_S 72.5GB から BF16 354GB までありますが、一番小さいものが 72.5GB です。ここに選択の余地はありません。
180B のうち 51B は、引くだけの表だった
公式のモデルカードに答えが書いてありました。「125B with 6B activated, plus 51B n-gram embedding and 4B MTP」。125B と 180B の差 55B は、n-gram 埋め込み表(51B)と MTP(4B)です。HuggingFace のメタデータでも総パラメータは 179,999,981,424 で、BF16 のリポジトリは 360GB あります。
この 51B は、2-gram / 3-gram を鍵にして引くだけの巨大なルックアップ表です。実体は [2,500,012 × 160] のシャードが 128 枚、合計 320,001,536 行。48 層のうち第 2 層にだけ置かれます。Qwen 自身が「MoE より offload しやすい」と設計意図を書いていて、そのとおりの扱いを受けました。
| 部位 | サイズ | 割合 |
|---|---|---|
| MoE エキスパート(512 個 × 48 層) | 37.11 GiB | 54.9% |
| n-gram 埋め込み表(IQ4_NL) | 26.82 GiB | 39.7% |
| attention / Gated DeltaNet / 正規化 | 2.96 GiB | 4.4% |
| token 埋め込み・出力 | 0.67 GiB | 1.0% |
ロード時のログに、その扱いが出ます。
create_tensor: tensor per_layer_token_embd.weight (size = 27465 MiB) lazy read enabled
4GiB を超えるテンサは mmap 経由で行単位に読む(--tensor-read-lazy、既定 auto)という仕組みで、表は SSD に置いたまま動きます。差し引き、RAM が要求されるのは 40.7 GiB。32GB(実質 29.8 GiB)に対して 1.37 倍です。72.5 / 29.8 = 2.4 倍だった数字が、表を外すだけで 1.37 倍まで縮みました。惜しいのですが、まだ載りません。
構造としては 48 層のうち 12 層が QSA(Qwen Sparse Attention)、36 層が Gated DeltaNet。エキスパートは 512 個あって、1 トークンで動くのは 10 個と共有 1 個だけです。
GPU には載らない。CPU なら 2.5 tok/s
まず Metal に載せようとして、-ngl 99 -ncmoe 48(エキスパートだけ CPU、残りを GPU)を試しました。ロードは通りますが、最初の decode で落ちます。
ggml_metal_synchronize: error: command buffer 0 failed with status 5
error: Insufficient Memory (kIOGPUCommandBufferCallbackErrorOutOfMemory)
GPU 側にマップされたのは 69,175 MiB。Metal の working set は 25,559 MiB なので、決着は最初から付いています。結局 -ngl 0、全部 CPU で mmap に任せるのが唯一の道でした。ロードは 87 秒です。
| タスク | 入力トークン | 生成トークン | 生成 tok/s | 所要秒 | 判定 |
|---|---|---|---|---|---|
| 挨拶 | 26 | 101 | 3.40 | 38.4 | ○ |
| 事実質問 | 17 | 43 | 3.42 | 18.7 | ○ |
| コード生成 | 38 | 576 | 2.65 | 231.4 | ○ |
| 日本語要約 | 155 | 83 | 2.30 | 72.2 | ○ |
| 英語要約 | 117 | 67 | 2.25 | 58.9 | ○ |
条件は n_ctx 4096 / temperature 0.2 / seed 42 / max_tokens 1024、thinking は OFF。5 タスクすべてが自力で終端し、日本語要約は指示どおり 3 行、コード生成は docstring 付きで os.walk と Counter を使う実用的な内容でした。前回の 27B(6 tok/s)より内容は明確に上で、速度は半分以下です。
代償は SSD への読み出し量に出ます。40 トークンを生成する 1 リクエストあたり、ページインが 4.35〜4.96GB。1 トークンあたり約 115MB、生成中は毎秒 264MB を読み続けています。常駐サイズは 5〜8GB を上下し、mmap のページが延々と入れ替わります。
本当の壁は入力側でした。3,877 トークンの文書を読ませたところ、読み込みだけで 2,486 秒(41 分 24 秒)。しかも位置が進むほど遅くなり、2.12 → 1.39 → 1.02 tok/s と落ちていきます。中央に埋めた「検証機の内部識別番号は QX-7731」は正しく拾えたので、読解そのものは壊れていません。プロンプトキャッシュが効いた 2 回目は同じ質問が 16.0 秒でした。
理由は MoE の動き方の違いです。生成時は 512 個のうち 10 個しか触りませんが、読み込み時はバッチ内の全トークンをまとめて通すので、実質すべてのエキスパートを読み出すことになります。SSD 常駐の弱点が、読み込みだけに集中して出る形です。
コンテキスト側は逆に優秀でした。
| n_ctx | KV(QSA) | KV(indexer) | 再帰状態 | 計算バッファ | 追加合計 |
|---|---|---|---|---|---|
| 4,096 | 96 MiB | 36 MiB | 450 MiB | 211 MiB | 0.78 GiB |
| 32,768 | 768 MiB | 288 MiB | 450 MiB | 495 MiB | 1.95 GiB |
| 131,072 | 3,072 MiB | 1,152 MiB | 450 MiB | 1,613 MiB | 6.14 GiB |
| 262,144 | 6,144 MiB | 2,304 MiB | 450 MiB | 3,103 MiB | 11.72 GiB |
262K を開いても KV は 8.25GiB、1 トークンあたり 33.8KB です。前回の 27B が 64KB/トークン、Phi-4-mini が 128K で 19GB を要求したことを思えば、破格に軽い。どの n_ctx でも 40 トークンの生成は 15〜18 秒で、開くこと自体は速度に響きません。開くことと埋めることは別の話でした。
まとめ
- 72.5GB のモデルが 32GB の Mac で動いた。生成 2.25〜3.42 tok/s、5 タスクすべて自力終端
- 効いたのは n-gram 表を SSD に置く設計。51B(26.82 GiB)を外すと RAM 要求は 40.7 GiB まで下がる
- 足りないのは 37.11 GiB のエキスパート。KV でもコンテキストでもない
- GPU は最初から選択肢に入らない。Metal working set 25,559 MiB に対しマップは 69,175 MiB
- 入力の読み込みが致命的。3,877 トークンで 41 分、位置が進むほど遅くなる
- 262K は 8.25GiB で開く。33.8KB/トークンは 27B の約半分
- 新アーキテクチャは実行系を選ぶ。Ollama は MLX だけ、llama.cpp は PR ブランチが必要
用途で切るなら、現状は居場所がありません。読み込みに 41 分かかる相手には、バッチ処理も任せられないからです。ただしこれは「動かない」ではなく「あと 10GiB 足りない」という結論です。
計算してみます。Metal の working set は 32GB 機で 25,559 MiB、RAM のおよそ 78% でした。同じ比率なら 64GB 機で約 46GiB になり、40.7 GiB は GPU に載ります。262K を同時に開くなら 11.72GiB を足して 96GB 級。1 年後にローカル LLM がクラウドに追いつくとして、その受け皿はノート型ではなく、ユニファイドメモリを積んだ据え置き機になりそうです。次回は Mac mini / Mac Studio の構成で、この 40.7 GiB がどこから GPU に載るのかを測ります。