記事のサマリー(TL;DR)
- transformers の
from_pretrainedが GGUF ファイルをネイティブロード対応、Qwen3.5 アーキテクチャから開始 - Q4_K_M 量子化で Qwen3.5-4B が 2.74 GB まで圧縮され、MacBook Pro でも実用的な推論速度を達成
- llama.cpp の ggml Metal カーネルを
kernelsライブラリ経由で再利用し、Python/PyTorch のまま高速なローカル推論を実現
Python × PyTorch で LLM のローカル推論を検討している開発者への影響
llama.cpp は Ollama・LM Studio・Jan などローカル AI ツールの推論エンジンとして広く使われており、GGUF 形式のモデルはすでに Hub 上で数百万ダウンロードを記録しています。今回の統合により、GGUF モデルを扱う際に llama.cpp ランタイムを別途セットアップする必要がなくなり、transformers の評価パイプライン・フック・カスタム生成ループをそのまま流用できます。日本国内でも Apple Silicon Mac を開発端末として使うエンジニアは多く、クラウド GPU コストを抑えながらモデルの動作確認や量子化精度の評価を行いたいケースに直結する機能強化です。また、GgufConfig(dequantize=True) を使えば GGUF チェックポイントから通常の fine-tuning ワークフローにそのまま移行できるため、量子化モデルを起点にした追加学習の実験コストも下がります。
詳細
GGUF ファイル形式とは
GGUF は llama.cpp チームが開発したローカル推論向けのファイル形式です。モデルの重みとメタデータ(トークナイザー情報やチャットテンプレートを含む)を 1 ファイルにパッケージし、異なる量子化レベルをサポートしています。量子化によってメモリフットプリントを抑えながら、精度とのトレードオフを選択できます。
Q4_K_M はほとんどのテンソルを 4-bit 重みで保持しつつ、精度に敏感なテンソルを高精度で維持する混合精度バリアントです。Unsloth の Qwen3.5-4B を例にとると、量子化によってファイルサイズは以下のように変化します。
| GGUF バリアント | ファイルサイズ | トレードオフ |
|---|---|---|
| BF16 | 8.42 GB | 量子化なしの基準値 |
| Q6_K | 3.53 GB | 小さいバリアントより高精度 |
| Q5_K_M | 3.14 GB | サイズと精度のバランス |
| Q4_K_M | 2.74 GB | ローカル推論の現実的な出発点 |
まず Q4_K_M から試し、メモリに余裕があれば Q5_K_M や Q6_K を選ぶのが推奨ルートです。より積極的な量子化は大きなモデルをメモリに収める助けになりますが、品質へのトレードオフはモデルとタスクによって異なるため、実際に使いたいタスクで評価することが重要です。
transformers で GGUF をロードする
動作要件:
- Apple Silicon Mac
- サポートされている PyTorch バージョン(通常は最新 2 リリース)
- transformers の最新版(現時点では main ブランチ)と互換性のある
kernels
pip install -U "git+https://github.com/huggingface/transformers.git" kernels
GGUF モデルのロードは、Hub の model_id と filename を gguf_file として from_pretrained に渡すだけです。追加の設定は不要で、重みが Metal 上にパックされたまま維持される場合、transformers は自動的に互換性のある ggml/Metal レイヤーカーネルをロードし、ggml-org/ggml-attn をアテンション実装として使用します。
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "unsloth/Qwen3.5-4B-GGUF"
filename = "Qwen3.5-4B-Q4_K_M.gguf"
tokenizer = AutoTokenizer.from_pretrained(model_id, gguf_file=filename)
model = AutoModelForCausalLM.from_pretrained(
model_id,
gguf_file=filename
)
GGUF 固有のステップはこの部分だけです。それ以降は標準の transformers API をそのまま使えます。
messages = [{"role": "user", "content": "Explain why the sky is blue in a few sentences."}]
inputs = tokenizer.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_dict=True,
return_tensors="pt",
).to(model.device)
with torch.inference_mode():
outputs = model.generate(**inputs, max_new_tokens=256)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
互換性のある量子化カーネルがない場合、ローダーはモデルをデクォンタイズするフォールバック処理を行い、メモリ使用量は増加します。
transformers serve で OpenAI 互換 API として提供する
pip install -U "transformers[serving] @ git+https://github.com/huggingface/transformers.git" kernels
transformers serve "unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf"
モデル引数の書式は <model_id>:<filename>.gguf です。コロン前が Hub リポジトリ(unsloth/Qwen3.5-4B-GGUF)、コロン後がロードするファイル(Qwen3.5-4B-Q4_K_M.gguf)です。Jan や Pi のようなクライアントから、以下の設定でカスタム OpenAI 互換プロバイダーとして接続できます。
| 設定項目 | 値 |
|---|---|
| Base URL | http://localhost:8000/v1 |
| Model ID | unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-Q4_K_M.gguf |
チェーン オブ シンクを持つチャットテンプレートのモデルでは、--reasoning off で思考をスキップ、--reasoning on で有効化できます。デフォルトの --reasoning auto はチャットテンプレートの設定に従います。
llama.cpp とのベンチマーク比較
比較対象は小規模密モデル・大規模密モデル・MoE(Mixture-of-Experts)モデルの 3 つの GGUF チェックポイントです。測定環境は MacBook Pro M2 Max(32 GB ユニファイドメモリ)、macOS 26.6、PyTorch 2.12.1、kernels 0.17.0 です。
llama.cpp 側は llama-bench ツール(build 5f55650a7、release b10200、ggml 0.18.0 の Metal バックエンド)を使用し、-p 0 -n 128 -r 3 で 128 トークン生成速度(tg128)を 3 回の平均として計測しています(プロンプト処理を除外)。transformers 側は 12 トークンのプロンプトから 128 トークンを生成し、3 回のウォームアップ済み実行のベストを採用(プリフィル込み)。
結果として、transformers は 3 つのチェックポイントすべてで llama.cpp に近いパフォーマンスを発揮しています。ただし、transformers の計測にはプリフィルが含まれる一方、llama-bench はデコードのみのスループットを報告しているため、条件が完全に同一ではない点に注意が必要です。
transformers と llama.cpp の役割分担
GGML と llama.cpp が Hugging Face に参加した際に両者の役割が整理されています。llama.cpp はローカル推論の基盤、transformers はモデル定義の基盤、という補完関係です。
効率的なローカル推論を優先する場合は llama.cpp を引き続き推奨します。 専用ランタイム、メモリ管理、幅広いハードウェアサポートがそのゴールのために設計されています。
今回の統合が transformers 上で実現するユースケースは以下の通りです。
- Python/PyTorch での実験: フック・forward パス改造・カスタムレイヤーのプロトタイプ作成
- 評価: 既存の transformers 評価ワークフローで量子化チェックポイントの品質測定
- GGUF 変換の検証: オリジナルと変換後を transformers に読み込んで重みの正確性を確認
- 新しいデコードアイデアの試作: カスタム logits プロセッサ、停止条件の実装、独自の生成ループ記述
- GGUF チェックポイントからの Fine-tuning:
GgufConfig(dequantize=True)を使って重みをデクォンタイズし、標準の training ワークフローに移行
import torch
from transformers import AutoModelForCausalLM, GgufConfig
model = AutoModelForCausalLM.from_pretrained(
"unsloth/Qwen3.5-4B-GGUF",
gguf_file="Qwen3.5-4B-Q4_K_M.gguf",
quantization_config=GgufConfig(dequantize=True),
dtype=torch.bfloat16,
)
GGUF を超えた拡張:ggml カーネルのさらなる活用
より大きな機会は、llama.cpp がサポートしていないモデルに ggml のパフォーマンスをもたらすことです。カーネルはテンソルに対して動作するため、モデル全体が GGUF ファイルから来る必要はありません。同じ構成要素を他の transformers モデルやローディングワークフローに統合できます。
コンピュータビジョン・音声・マルチモーダルモデルも、llama.cpp にフル実装がなくても、互換性のあるアテンション・正規化・行列積カーネルを再利用できる可能性があります。各アーキテクチャには個別の統合と検証が必要ですが、初期の GGUF サポートはテキスト生成から始まっています。
ggml の Metal カーネル再利用の仕組み
kernels ライブラリが ggml の Metal カーネルのビルドを Hub 上で配布し、transformers から呼び出せるようにしています。以下が各カーネルの役割です。
| カーネル | 役割 |
|---|---|
| ggml-quantization | 行列演算用のパック済み量子化重みを読み取る。MoE モデルでは選択エキスパートのみ処理し、重み行列全体の展開を回避 |
| ggml-norm | 正規化演算を融合。Qwen3.5/3.8 が使うゼロセンタリング RMSNorm を含む |
| ggml-attn | プロンプト処理とトークンデコードに ggml の Metal flash attention を提供 |
| ggml-gated-delta-net | Qwen3.5/3.8 ハイブリッドアーキテクチャの線形アテンション層で使う gated delta network を高速化 |
| topk | MoE モデルでの各トークンへのエキスパート選択。softmax と top-k ルーティングを組み合わせた独自の Metal 実装 |
生成ループの最適化
カーネルの高速化と同時に、generate 自体のオーバーヘッド削減も実施されました。これらの改善は GGUF 以外の transformers モデルにも適用されます。
- 不要なアテンションマスクを早期に削除(#48814): パディングなしのデコーダーオンリー入力では、全 1 のパディングマスクを生成開始時に除去。下流のアテンションコードが毎回マスクを検査する必要がなくなります。
- 停止チェックを遅延実行(#47975): サポートされているパスで停止判定を非同期コピーし、次のステップで消費。CPU がスケジューリングを続けながら GPU を動かし続けられます。
現在の制限と今後のロードマップ
- パック推論パスは現時点で MPS のみ対応。デクォンタイズ経由の GGUF インポートは引き続き利用可能
- パディングとバッチ処理はまだ改善が必要。パディングありのバッチはマスク最適化の恩恵を受けられない
- アーキテクチャのカバレッジは現時点で Qwen3.5 の dense と MoE(Qwen3.8 互換チェックポイントを含む)に限定
他のアーキテクチャのサポートは段階的に拡張予定。使いたい GGUF モデルがある場合は、チェックポイントとユースケースを添えて GitHub に Issue を立てることが推奨されています。