frontier AI
transformersがGGUFをggmlカーネルで実行:従来との違いと制約
- transformers
- ローカル推論
- 量子化
- GGUF
- ggml
- llama.cpp
- vLLM
- kernels
- Apple Silicon
- Qwen3.5
- PyTorch
- カーネル
- 展開
- 推論エンジン
- モデル定義
- 保存形式
- 量子化の型
- 精度低下
- Hugging Face
- CUDA
- Metal
- fp32
- from_pretrained
- vllm-gguf-plugin
- 微調整
- Q4_K_M
- アーキテクチャ
目次
オープンなLLMを手元のノートパソコンで動かすときは、多くの場合、量子化したモデルを使う。量子化は、モデルの重みを元より少ないビット数で表し、ファイルとメモリを小さくする処理である。量子化したモデルはその形式のまま配られ、推論エンジンがそれを読んで動かす。推論エンジンは、学習済みのモデルに文章を生成させる処理を速く行うためのソフトウェアである。
量子化したモデルを使う人は、文章を生成させるだけでなく、モデルの内部を調べたいこともある。途中の層の出力を見る、手元の課題で精度を評価する、といった作業である。こうした作業には、モデルの各層の計算をコードとして書いたライブラリ(モデル定義のライブラリ)が使われる。
困るのは、モデル定義のライブラリで量子化したファイルを読むと、量子化で減らしたメモリの使用量が増えることがある点である。読み込むときに重みをより多いビット数の形式へ書き直すことがあるからで、この書き直しを展開と呼ぶ(英語ではdequantize)。展開した重みは、量子化したままの重みより多くのメモリを使う。そのため、量子化したままの大きさで動かすには推論エンジンを、内部を調べるにはモデル定義のライブラリを使い、同じモデルを二つの形で扱うことになる。展開せずに計算するには、量子化した保存形式をそのまま読んで演算する小さなプログラムが要る。GPUの上で一つの演算を行うこうしたプログラムを、カーネルと呼ぶ。
本稿は、Hugging Faceのtransformersと、量子化したモデルを配る形式GGUFを例に取る。transformersは、モデルの各層の計算をPyTorchのコードとして書いたライブラリである。GGUFは、ggml-orgの推論エンジンであるllama.cppの開発チームが作った1。transformersは2024年からGGUFを読めたが、当初は、読み込むときに重みを32ビットの浮動小数点数(fp32)の形式へ展開していた2。2026年9月22日、Hugging Faceは、transformersがGGUFの重みを展開せずに計算する経路を加えたと発表した。この経路は、llama.cppが下層で使うggmlのカーネルを取り込んで使う1。
transformersは2024年からGGUFのファイルを読めたのに、なぜggmlのカーネルまで取り込んだのか。
ファイルを読めるだけでは重みを展開することになり、量子化したままの重みで計算するには、GGUFの保存形式を直接読むカーネルが要るからである1。 そのカーネルは、GGUFを作ったllama.cppが下層で使うggmlにあり、作り手はllama.cppに性能を近づけるためにこれを再利用したと書く1。置き換わるのは個々の演算なので、各層を定義するコードはtransformersのまま残り、途中の層の出力を取り出す仕組みや評価のツールがそのまま使える。代償は、使える環境がApple SiliconのGPUとQwen3.5系のモデルに限られること、量子化による精度の低下を自分の課題で確かめる手間が要ること、保存形式を直接読むカーネルがあっても、それだけでは速さと安定が決まらないことである。ggmlのCUDA向けのカーネルを複写して使うvLLMでは、GGUF対応はまだ実験的な扱いである3。
重み40億個のモデルで、メモリと展開の量を数える
※ この節の数値は説明のための仮定で、測定値ではありません。
重みが40億個あるモデルを考える。重みは、学習で決まった数値で、モデルは各層でこの数値を行列演算に使う。量子化する前は、重み1個を16ビットで表すとする。16ビットは2バイトなので、重み全体は 40億 × 2 = 80億バイト、つまり8 GBになる(1 GBを10億バイトとして数える)。
このモデルを量子化する。量子化は、重みを元より少ないビット数で表し直す処理で、ここでは重み1個を4ビットにする。4ビットは0.5バイトなので、重み全体は 40億 × 0.5 = 20億バイト、つまり2 GBになる。量子化したモデルはGGUFという形式のファイルで配られ、ファイルにはこの2 GBの重みが入っている。実際のGGUFは、重みを小さな区画に分け、重みのほかに区画ごとの倍率なども持つので、4ビットの型でも重み1個あたりは4ビットを少し超える(Q4_Kという型では4.5ビット)4。ここではこの分を数えない。
モデルは、文章をトークン(単語やその一部に当たる単位)に分けて扱い、トークンを1個ずつ生成する。ここでは、トークンを1個生成するたびに、40億個の重みをすべて演算に使うとする。
2 GBのファイルを読んで計算する方法は、三つある。
| 重みの | 重みが |
|---|---|
| ① 読み込むときに | 8 GB。 |
| ② 4ビットのまま | 2 GBと、 |
| ③ 4ビットのまま | 2 GB。 |
①は、読み込むときに40億個の重みをすべて16ビットの形式へ書き直す。この書き直しを展開と呼ぶ。その後の演算は16ビットの値を読むので、PyTorchが備える汎用の演算をそのまま使える。重みが使うメモリは8 GBで、量子化する前と同じである。
②は、重みを4ビットのままメモリに置く。汎用の演算は4ビットの保存形式を読めないので、演算の直前に、使う重みを16ビットへ展開する。常に置いておく重みは2 GBで済むが、トークンを1個生成するたびに、40億個分の展開が入る。
③は、重みを4ビットのまま置き、演算が保存形式を直接読む。演算の中で4ビットの値から計算に使う値を作るが、16ビットの重みとしてメモリに書き出すことはしないので、①や②の意味での展開は起きない。その代わり、保存形式を読める専用の演算が要る。GPUの上で一つの演算を行う小さなプログラムをカーネルと呼ぶので、③に要るのは専用のカーネルである。
三つの方法で、各層をどの順に計算するかは変わらない。変わるのは、重みをメモリにどう置くかと、個々の演算をどのプログラムが行うかである。
計算に使う値も、三つの方法で変わらない。展開はビット数を戻す処理で、量子化で丸めた値は元に戻らない。どの方法でも、計算に使うのは4ビットに量子化した後の値である。
①から③を比べると、重みの扱い方について三つの性質が言える。
- ①で重みが使うメモリは、重みの個数と展開後のビット数で決まる。量子化を4ビットから2ビットへ強めても、①のメモリは8 GBのままである。
- メモリを2 GBに保ち、展開も0個にできるのは③だけである。③は、保存形式を直接読む演算がある環境でしか使えない。その演算が無ければ、①か②になる。
- 三つの方法で変わるのは、重みの置き方と個々の演算だけである。各層の計算を定義するコードは、どの方法でも同じものを使える。
2024年の読み込みは展開し、今回の経路はggmlのカーネルが直接読む
transformersはHugging Faceのライブラリで、作り手は自らを「モデル定義の土台」と位置づける1。llama.cppはggml-orgの推論エンジンで、READMEは主な目的を、LLMの推論を、最小限のセットアップと最先端の性能で、幅広いハードウェアの上で行えるようにすることだと書く。対象はローカルとクラウドの両方である5。GGUFを作ったのはllama.cppのチームで、GGMLとllama.cppはHugging Faceに加わっている1。
作り手は対応の背景として、手元の機械で行う推論(ローカル推論)にGGUFが広く使われていることを挙げる。llama.cppの推論エンジンは「Ollama、LM Studio、JanといったローカルAIのツールを動かして」おり、「GGUFモデルは何百万回もダウンロードされてきた」1。
2024年5月のv4.41.0から、transformersはGGUFの重みを読み込み、通常のPyTorchのモデルへ変換していた2。読み込むときに重みをすべて展開する方法で、その後の演算は展開した値を読むので、PyTorchの汎用の演算をそのまま使える。重みが使うメモリは重みの個数と展開後のビット数で決まり、量子化を強めても減らない。同じ版のドキュメントは、展開する先をfp32と書いている2。32ビットは16ビットの倍なので、16ビットで持つ量子化前のモデルより多くのメモリを使う。いまの展開の経路では、展開する先の精度を使う人が指定する6。展開して読む経路は、今回の対応の後も別の選択肢として残る1。
量子化でファイルがどれだけ小さくなるかは、作り手の記事に実例がある。UnslothのQwen3.5-4Bは、量子化しない版のファイルが8.42 GBで、大半の重みを4ビットにした版(Q4_K_M)が2.74 GBである1。4ビットの型でも重み1個あたりは4ビットを超える。この版は量子化に敏感な一部の重みを高い精度で残し1、GGUFの型は重みを小さな区画に分けて区画ごとの倍率なども持つからである4。量子化を強めれば、より大きなモデルもメモリに収めやすくなるが、品質との引き換えはモデルと課題で変わる1。
今回の経路で量子化したままの重みを読むのは、ggml-quantizationというカーネルである。このカーネルは、量子化したままの重みを行列演算の中で直接読み、トークンを一つ生成するたびに重み行列全体を展開することはしない1。重みを量子化したまま置いて演算の直前に展開する方法なら、常に置く重みは小さく保てるが、トークンを生成するたびに展開が入る。作り手が避けたと書くのは、この動作である。
ggmlのカーネルをtransformersから呼べるようにするのが、kernelsライブラリである。kernelsは、Apple SiliconのGPU向けのggmlのカーネルを、互換のビルドにしてHugging Face Hubで配り、transformersがそれを呼び出す1。使い方は2024年からの読み込みと同じで、from_pretrainedにgguf_fileを渡す。重みがApple SiliconのGPUの上に量子化したまま置かれると、transformersは互換のカーネルを自動で読み込む1。互換のカーネルが無いとき、ローダーは展開に切り替わり、より多くのメモリを使う1。
作り手がggmlのカーネルを再利用した目的は、性能をllama.cppに近づけることである1。測定の結果について、作り手は、三つのGGUFチェックポイントのいずれでもtransformersがllama.cppに近かったと書く1。ただし作り手が明記するとおり、測り方は揃っていない。llama.cpp側は、プロンプトの処理を除いて128トークンを生成する速さを3回測り、平均した値である。transformers側は、12トークンのプロンプトの処理を含めて同じ128トークンを生成する速さを3回測り、最良を取った値である1。この比較を、そのまま推論エンジンどうしの優劣としては読めない。
速さに効いている変更は、重みを読むカーネルだけではない。作り手は、重みを読む演算のほかにもggmlのカーネルを使い、生成の処理でCPUがGPUの結果を待つ回数も減らした1。生成の処理の変更は、GGUFに限らずtransformersの他のモデルにも効くが、作り手が対象とするのは対応済みの経路と、詰め物(パディング)の無い入力である1。
各層を定義するコードが残るので、内部を調べるツールが使える
置き換わるのが個々の演算であることは、kernelsライブラリの効果についての作り手の説明に出ている。作り手は、モデルを別の推論エンジンで置き換えることなく、ggmlの成果をPyTorchのモデルに持ち込めると書く1。transformersが取り込んだのはカーネルで、llama.cppの推論エンジン全体は取り込んでいない。各層の計算を定義するコードは、transformersのものがそのまま動く。ただし、どのモデルでも量子化したまま計算できるわけではない。置き換える演算はアーキテクチャごとに統合と検証が要り、いま済んでいるのはQwen3.5系だけである1。
そのため、transformersの上で動くツールを、量子化したままのモデルに使える。作り手は用途を五つ挙げる。途中の層の出力を取り出す仕組み(hook)を使った実験、GGUFモデルの評価、GGUFへの変換の検証、生成の手順を変える試行、微調整である1。五つのうち微調整は、重みを展開してから行う1。そのため微調整では、重みが使うメモリは展開後のビット数で決まる。
作り手は、二つのツールの役割を分けている。「llama.cppはローカル推論の土台を、transformersはモデル定義の土台を提供する」とし、「効率的なローカル推論が優先なら、llama.cppが引き続き推奨するエンジンである」と明記する1。
演算だけを置き換える設計は、GGUFのファイルの外にも及ぶ可能性がある。作り手は、カーネルが作用する相手は個々のテンソル(数値の配列)で、モデル全体がGGUFのファイルから来ている必要はないと書く1。作り手はその先に、llama.cppに専用の実装が無いアーキテクチャ(モデルの層の組み方)でも、transformersのモデル定義の上でggmlの演算を使えるようにしていくことを見込み、これを「より大きな機会」と呼ぶ1。ただしその場合も、アーキテクチャごとの統合と検証は要る。
代償は対応範囲、量子化の損失、推論エンジンごとの差
量子化したまま計算する経路が使える範囲は、デバイスでも、アーキテクチャでも、量子化の型でも狭い6。作り手によると、この経路は当面Apple SiliconのGPUの上でしか動かない1。対応するアーキテクチャは、Qwen3.5の密なモデルとMoEのモデル、互換のQwen3.8のチェックポイントである1。複数の入力をまとめて生成する処理にも、まだ改善が要ると作り手は書く1。作り手の記事は、この経路にtransformersのmainブランチを指定し、次のリリースまでの措置とした1。Qwen3.5を量子化したまま計算する経路そのものは、2026年9月9日公開のv5.17.0にすでに入っていた67。MoEのモデルとQwen3.8への対応は、9月30日公開のv5.18.0で加わった8。
範囲の外では、読み込むときに展開する方法に戻る。Apple Silicon以外の環境や、Qwen3.5系以外のモデルでは、transformersの従来のローダーが対応するアーキテクチャに限って、重みを展開して読む6。展開して読むと、重みが使うメモリは展開後のビット数で決まる大きさになる。内部は調べられるが、量子化で減らしたメモリは増える。
デバイスとアーキテクチャの二つの制約は、見通しが違う。作り手が広げると書いているのは、対応するアーキテクチャと、Apple SiliconのGPUの上で複数の入力をまとめて生成する処理である。ほかのデバイスについては「当面」と書くだけで、予定は示していない1。本稿は、アーキテクチャの狭さは一時的だが、デバイスの制約はいつ解けるか見通せないと判定する。
量子化による精度低下の可能性は、展開して読む経路でも、量子化したまま計算する経路でも残る。展開はビット数を戻す処理で、量子化で丸めた値は元に戻らないので、どちらの経路でも計算に使うのは量子化した後の値だからである。llama.cppのquantizeのREADMEは、量子化が「いくらかの精度低下を招きうる」と書く。量子化のときに適切な補助のファイルを使えば、低下を最小化できるとも書く9。
低下が出るか、どれだけ出るかは課題とモデルで変わり、Hugging Faceの記事も「実際にモデルにさせたい作業で評価せよ」と書く1。本稿はこの手間を恒久的な代償と判定する。形式や推論エンジンを替えても、量子化した重みを使う限りこの確認は要る。
保存形式を直接読むカーネルがあるかどうかで決まるのは、メモリと展開の量までである。速さと安定は、それだけでは決まらない。比較のため、同じGGUFを受け入れたもう一つの推論エンジンとしてvLLMを見る。vLLMも、GGUFの計算にllama.cppのggmlカーネルを使っている。ただし使うのは、2024年5月のllama.cppのビルドからCUDA向けのカーネルを複写して自前で持つもので10、transformersがHubから受け取るApple SiliconのGPU(Metal)向けのカーネルとは別物である1。そのため、vLLMとtransformersの速さと安定の違いが推論エンジンの差なのかカーネルの差なのかは、この比較からは分けられない。
vLLMのドキュメントは、現時点ではGGUFをメモリ使用量を減らす手段として使えると書く。速さと安定については、GGUF対応を「非常に実験的で最適化が不十分であり、他の機能と両立しないことがある」と書く3。
vLLMは、GGUFの対応を本体から外し、別のプラグインへ移した3。元の提案は対応の廃止で、利用が保守の負担に比べて非常に少ないこと、手元のGPUではllama.cppで動かすほうが速いという利用者の声を理由に挙げていた。利用の少なさは、提案者自身の見立てによる見積もりである。プラグインへ移す形は議論の中で出て、提案の題が改められた11。本稿は、vLLMとtransformersの速さと安定の差を、恒久とも一時的とも判定できないと見る。
出典11件
-
Hugging Face「Transformers now runs llama.cpp quants」Hugging Face Blog, 2026. https://huggingface.co/blog/transformers-llama-cpp-quants — 2026年9月22日公開。GGUFを量子化したままggmlのカーネルで計算する経路の発表。対応はApple SiliconのGPUとQwen3.5系に限られると明記。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17 ↩18 ↩19 ↩20 ↩21 ↩22 ↩23 ↩24 ↩25 ↩26 ↩27 ↩28 ↩29 ↩30 ↩31 ↩32 ↩33 ↩34
-
Hugging Face「transformers v4.41.0」GitHub Releases, 2024. https://github.com/huggingface/transformers/releases/tag/v4.41.0 https://raw.githubusercontent.com/huggingface/transformers/v4.41.0/docs/source/en/gguf.md — 2024年5月17日公開。GGUFの量子化の大半をfrom_pretrainedで読み、通常のPyTorchのモデルへ変換できるようにした版。同梱のドキュメントは、読み込み時にfp32へ展開すると書く。 ↩ ↩2 ↩3
-
vLLM docs「GGUF」(2026-09-25 取得). https://docs.vllm.ai/en/latest/features/quantization/gguf.html — GGUF対応は非常に実験的で最適化が不十分と注意し、メモリを減らす手段として使えると書く。対応は本体外のvllm-gguf-pluginへ移った。元のモデルのトークナイザを使うよう勧める。 ↩ ↩2 ↩3
-
Hugging Face Hub docs「GGUF」(2026-09-30 取得). https://huggingface.co/docs/hub/gguf — 量子化の型の表で、Q4_Kを8個のブロック(各32個の重み)からなるスーパーブロックとし、ブロックごとの倍率と最小値を持って重み1個あたり4.5ビットになると書く。 ↩ ↩2
-
ggml-org「llama.cpp」README(2026-09-25 取得). https://github.com/ggml-org/llama.cpp — 主な目的を、最小限のセットアップと最先端の性能で、幅広いハードウェアの上で、ローカルでもクラウドでもLLMの推論を行えるようにすることと書く。 ↩
-
Hugging Face transformers docs「GGUF」(v5.17.0の版、2026-10-09 取得). https://huggingface.co/docs/transformers/v5.17.0/gguf — 対応外のデバイスや量子化の型では読み込み時に展開し、未対応のアーキテクチャは従来のローダー(Llama・Mistral・Qwen2など)を通ると書く。展開の先は渡したdtypeになる。 ↩ ↩2 ↩3 ↩4
-
Hugging Face「transformers v5.17.0」GitHub Releases, 2026. https://github.com/huggingface/transformers/releases/tag/v5.17.0 — 2026年9月9日(UTC)公開。作り手の記事より前のリリース。 ↩
-
Hugging Face「transformers v5.18.0」GitHub Releases, 2026. https://github.com/huggingface/transformers/releases/tag/v5.18.0 — 2026年9月30日(UTC)公開。変更点に、GGUFの統合でのMoE対応とQwen3.8のGGUF対応を挙げる。 ↩
-
llama.cpp「quantize」README(2026-09-25 取得). https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md — 量子化はいくらかの精度低下を招きうるとし、perplexityとKLダイバージェンスで測ること、適切なimatrixファイルで低下を最小化できることを書く。 ↩
-
vLLM project「vllm-gguf-plugin」(2026-09-26 取得). https://github.com/vllm-project/vllm-gguf-plugin — 行列演算カーネルmmq.cuhは、冒頭のコメントでllama.cppのビルドb2899(2024年5月16日のコミット)のggml-cuda/mmq.cuからの複写と記す。導入の前提はCUDAかROCm。 ↩
-
vLLM「[RFC]: Migrate bitsandbytes and GGUF quantization support to OOT plugin」GitHub issue #39583, 2026. https://github.com/vllm-project/vllm/issues/39583 — 当初の題は非推奨化で、2026年5月20日に改題。利用の少なさと、llama.cppが速いという声を理由に挙げた。 ↩
この記事はAIを使用して執筆されました。内容には誤りが含まれる可能性があります。事実や数字の出典は脚注から確かめられます。