
2026/05/15 2:21
GGUF は、モデルのウェイトだけでなく、トークナイザーやコンテキスト長制限、アーキテクチャの詳細、量子化パラメータといった設定関連のメタデータも含みます。ただし、現在では動的バッチ処理による最適化への対応、一部の操作に関する ONNX ランタイムとのネイティブ統合、そして外部ラッパーなしで連続的なバッチ処理を標準的にサポートする機能などが未実装となっています。
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
gguf ファイル形式は、llama.cpp で使用されており、safetensors などの複雑な複数リポジトリ構成の単一ファイル代替手段として機能していますが、多様な言語モデルの能力をよりよくサポートするように進化しています。現在、モダンな AI のデプロイメントに不可欠な標準化された機能において課題を抱えています。具体的には、専用の
think_token フィールドが欠如しているため、推論エンジンが思考ブロックと主要出力を区別できず、明示的な機能フラグがないため、画像の取り込みやネイティブツール呼び出しのような機能をプログラムでの検出が困難です。さらに、エンジン間(Python、Rust など)における Jinja2 の実装の違いや多様なツール呼び出し形式は、ハードコードされたパースャーへの依存を強いることがよくあります。最近の改善としては、モデルファイル内で general.sampling.sequence を介してサンプラーチェーンを直接指定する能力や、チャットテンプレートを Jinja2 スクリプトとして格納する機能などがあります。今後の展望として、コミュニティはプロジェクションモデルを単一ファイルにバンドルする問題の解決や、特別トークン(例: <eos>, <bos>, <|turn> など)に対する明確なメタデータ構造の導入に取り組んでいます。これらの詳細を標準化することにより、モデル固有のコードなしで堅牢な機能検出が可能になり、特に独自の制約文法に依存して型安全性を保証する小規模言語モデルにおいて大きく受益することが期待されます。本文
GGUF は、llama.cpp が言語モデルで使用しているファイル形式です。GGUF の最も魅力的な点は、それが単一のファイルで構成されていることです。これに対し、Hugging Face 上にある典型的な
safetensors リポジトリでは必要な JSON ファイルが散在したり、Ollama のモデルのように OCI パッケージの中に layers.json や Go テンプレートなどが含まれたりする場合を考えると、その内容实质は概ね同じであっても、GGUF はこれらすべてを 1 つのファイルにまとめることで、利用の手軽さ(エルゴノミクス)を大幅に向上させています。しかしながら、その中身が具体的に何を指すのか、そしてそれだけでは必要な要素を網羅しているのかという疑問が生まれます。
チャットテンプレート (Chat Templates)
対話型言語モデルは、特定の形式に従ったシーケンスでトレーニングされており、大まかには会話の形を模しています。例えば、Gemma 4 の形式は以下のように見えます:
<|turn>user\nHi there!<|turn|>\n<|turn>model\nHi there, how can I help you today?<|turn|>
一方、LFM2 の形式テンプレートは以下のようなものです:
<s>\n<user Hi there!
これらはあくまで基本的な例に過ぎません。さらに高度な機能を付加するにつれ、処理が大幅に複雑化します。具体的には、推論ブロックの形式化方法やタイミング、ツールの説明や呼び出しとその応答の提示方法、そして画像、音声、ビデオなどのマルチメディアメッセージをどのように符号化するかなどです。これらすべては、Jinja2 テンプレート言語を用いたスクリプトによって「チャットテンプレート」と呼ばれる仕組みが処理します。Gemma 4 に同梱されているチャットテンプレートを参照すると良いでしょう。デフォルトのチャットテンプレートは、GGUF メタデータの
tokenizer.chat_template キーに格納されます。また、モデルによっては複数のチャットテンプレートを用意している場合もあります。例えば、ツール呼び出し機能をサポートするものと、そうでないものの両方が用意されている情况等です。最も一般的なケースとしては、単一の巨大なチャットテンプレートが同梱されており、ツールが指定された場合にのみツール呼び出し関連の処理を行うというように設計されていることが多いですが、一部のモデルではツール固有のチャットテンプレートを個別に探す必要があることもあります。
Jinja2 は疑いもなくプログラミング言語です——ループ、条件分岐、代入、リスト、辞書など多様な機能を持つため——対話型の LLM アプリケーションは、新規メッセージが追加されるたびに、Gemma に同梱されているような約 250 行の Jinja スクリプトを実行できるプログラミング言語インタプリタを常に同梱する必要があります。
Hugging Face の
transformers ライブラリでは classic Python 版の Jinja2 を使用していますが、llama.cpp の llama-server および llama-cli では独自の Jinja 実装を採用しています(これは、ライブラリ libllama に公開されているやや混乱を招く llama_chat_apply_template と区別する必要があります。この関数は C++ で直接いくつかのチャット形式をハードコードしており、本物の Jinja 実装が実装される前のチャーミングなレリクです)、また NobodyWho では、Jinja を元来の作者によって純粋な Rust で再実装した minijinja というライブラリを使用しています(これはかつて llama.cpp で使用されていた minimalist な Jinja ライブラリの minja とは異なります)。これらの Jinja 実装の間には著しいパフォーマンスの差が存在しますが、チャットテンプレート処理そのものがローカル LLM アプリケーションにおけるボトルネックとはならないため、この点で議論を繰り返す価値はありません。
スペシャルトークン (Special Tokens)
言語モデルは、投入された任意のトークンのシーケンスに対して次のトークンを出力し続ける性質を持っているため、どこかで停止させるメカニズムが必要です。一般的にはエンド・オブ・シーケンス(EOS)トークンを用いることでこれを解決します。つまり、モデルが这类のトークンを出力した時点で、推論エンジンが生成を停止するという考え方です。これは「スペシャルトークン」の一例です。スペシャルトークンは、その文字列化されたトークンの意味よりも広範なセマンティクス(意味)を持つことが一般的で、ユーザーには表示すべきではない種類のトークンです(ただし、たいていテキスト表現を持たないので、必要に応じて可視化することは可能です)。以下に Gemma 4 の例を示します:
| トークン ID | テキスト表現 | 用途 |
|---|---|---|
| 1 | | シーケンスの終了。モデルがこのトークンを出力することで生成が停止します。 |
| 2 | | シーケンスの開始。入力に先頭で追加されます。 |
| 46 | | ツール呼び出しの開始をマークします。 |
| 47 | | ツール呼び出しの終了をマークします。 |
| 105 | | 対話ターンの開始をマークします。 |
| 106 | | 対話ターンの終了をマークします。 |
サンプリャー構成 (Sampler Configuration)
言語モデルは、次のトークン確率の分布を出力します。この分布からトークンを選定する作業を「サンプリング」と呼びます。最も簡単な方法は、重み付けされた分布からランダムにトークンを選定することです。しかし、それ以上のことも可能です。事実、確率分布に対して具体的なトランスフォーメーションを施してから具体的なトークンを選定することで、さらなるパフォーマンス向上が得られることが示されています。研究機関が新しいモデルをリリースする際によくあることですが、推奨されるサンプリャー構成を Markdown ファイルなどに記述し、ユーザーがそれをコピー&ペーストしてより良い応答を得ようとするケースもよく見られます。ユーザーのこの手間を省くために、当方は Hugging Face ページ上に厳選された小規模なモデルコレクションをアップロードしており、それらには独自に考案した形式で推奨されるサンプリャー設定をバッチリングしています。これは機能しましたが、モデルが有用になるためには、すべて
NobodyWho 側での変換が必要となったという欠点もありました。幸いなことに、GGUF フォーマットへの最近の追加により、サンプリャークエインを直接モデルファイル内で指定できるようになりました。これにより、当方の独自フォーマットは不要となり——まさに我々が望んだ結果です——そのようにすることができました。
サンプリャーチェーンのシーケンス (Sampler Chain Sequence)
私は、異なる種類のサンプリャーステップがどのような役割を果たすかを直感的に把握するためのこの Web アプリをとても気に入りしています。個々のステップをドラッグ&ドロップして順序を変えると、サンプル最終分布に大きな影響を与えることがすぐにわかります。しかしながら、多くのサンプリャー構成フォーマット(Ollama イメージの JSON ファイルや HF の
generation_config.json を含む)では、サンプリングステップの順序を指定する方法が全く存在しないことに非常に frustrating としています。GGUF スタンダードには general.sampling.sequence フィールドを含み、ここで順序を明示的に指定できる点をとても嬉しく思っています。それでもなお、多くの GGUF モデルはこのフィールドを省略し、「llama.cpp のデフォルトで何を行うか」という暗黙の順序に委ねることを期待しています。よしなさい、暗黙的でありますが、機能するので問題はありません。
まだ不足している要素は何か (What's Still Missing?)
優れた推論エンジンとは異なる言語モデルに対して統一的なインタフェースを提供することを目標としています。GGUF メタデータに含まれる追加情報は多くの部分をカバーしており、それをパースして利用することで、モデル固有のコードパスを回避することが可能です。
未実装:ツール呼び出し形式 (Tool Calling Formats)
推論エンジンでハードコードされた処理パスが存在する要因の一つに、異なるツール呼び出し形式のパースがあります。例えば、Qwen3 のツール呼び出しは以下のように見えます:
<tool_call>{"name": "get_weather", "arguments": {"location": "Copenhagen"}}</tool_call>
一方、Qwen 3.5 のツール呼び出しは以下のように見えます:
<tool_call> <function=get_weather> <parameter=city> Copenhagen</parameter> </function> </tool_call>
そして、Gemma 4 のツール呼び出しは以下のように見えます:
<|tool_call>call:get_weather{city:<|" Cantonese <|"|>}<tool_call|>
現在、多数の異なる推論エンジンは、新しいモデルがリリースされるたびにパサパーサーを実装しようと急いでいます。モデルファイル内に文法を含めることができれば、そこからパサパーサーを導き出すことができるでしょう。当方の NobodyWho では、ツール呼び出しに関してさらに一歩進んだ(やや独自性?)ステップを踏んでおり、指定された特定の実行環境に固有の制約文法を生成します。これにより、ツール呼び出しに対する型安全性を保証することが可能になります。これは特に、時折浮動小数点数が必要な整数を渡してエラーを起こしてしまうなどのミスが生じやすい最小クラスのモデル(1B トークン以下)において非常に有用です。当然ながら、汎用的なツール呼び出しパーサーから導くことができる文法を指定するだけでも有益ですが、NobodyWho では各特定の実行環境に対する文法生成関数を実装する必要があります。特定のツールのための具体的な文法から導き、さらにそこからパサパーサーを生成するためのメタ・文法形式の考え方を検討するのは興味深い課題です。
未実装:思考トークン (Think Tokens)
これは最も簡単な追加要素の一つです。上流にある Hugging Face リポジトリでは
think_token フィールドを含めるようになり始めています。これは、生成された出力の思考セクションを主出力から明確に分離するためには非常に有用です(一般的には削除されるか、異なる方法で表示されるべきだから)。何故か不思議なことに、下流にある GGUF 変換通常はこの要素を含めていないのが実状です。これにより、GGUF ベースの推論エンジンでは、特定のモデルファミリーに対して特別にコードパスを書くことなく思考ストリームを主出力から分離することができません。標準的な GGUF 変換パイプラインに think_token を追加することでこの問題を解決することができると考えます。これは取り組むべき課題です。
未実装:プロジェクションモデル (Projection Models)
マルチモーダル LLM インタラクション(つまり、LLM が画像や音声をテキストとしてではなく、ネイティブな形で認識できるようにする)には、非テキスト入力を処理するための追加モデル、すなわち「プロジェクションモデル」が必要です。一般的には、LLM 本体用の GGUF ファイルと、画像・音声処理用の小型モデルの 2 つの GGUF ファイルを渡すという慣行があります。これは「単一ファイル」というエルゴノミクス原則に反します。理想的には、プロジェクションモデルの重みと設定をメインの GGUF ファイル内でバッチリングできるようにすることにより、単一ファイルのエルゴノミクスを取り戻せるでしょう。プロジェクションモデルは通常約 1GB のサイズがあり、使用されない場合は明らかにそのオーバーヘッドを避けてください。しかしながら、プロジェクションモデル付きと無しで 2 つの変種を提供することは合理的だと考えます。これにより、ダウンロード先の URL を 1 つだけ管理し、ディスク上のキャッシュファイルも 1 つだけで済む状況に戻ることができます。
未実装:サポートされている機能の一覧 (List of Supported Features)
モデルはすべて同じ機能をサポートしているわけではなく、GGUF ファイルから実際にどの機能がサポートされているかを容易には検出できません。一部のモデルは画像の取り込みをサポートしていますが、他のモデルではサポートしていません。現在の最良の対処法としては、プロジェクションモデルが指定されている場合、画像に対応していると仮定することです。一方、ツール呼び出し機能については、チャットテンプレートに対して部分文字列照合を行い、ツール JSON スキーマのリストをレンダリングしようとするかどうかを確認するのが今のところ最適です。これは明らかにハッキィなアプローチです。一部のモデルは思考ブロックを出力しますが、他のモデルでは出力しません。思考タグが GGUF メタデータから通常欠落しているため、モデルから思考ブロックを期待するかどうかを見る良い方法があるのか確信を持って言えません。GGUF コミュニティにモデルファイルに機能フラグを追加することを始めることを強く推奨します。これにより、当方のようなモデルアグノスティックな推論ライブラリが、消費プログラムが無効なツール呼び出しを試行した際などに、より一貫したエラーメッセージや警告を提供できるようになります。
結論
私は GGUF を愛しています。単一のファイルであり、モデルを正しく実行するために必要なすべてを含みながら、多数のモデル固有のコードパスを追加することなく機能するためです。また、GGUF はオープンで拡張性があり、それを取り巻く強力なコミュニティが存在するためにも愛しています。これにより、私たちは協力して標準を強化し、アプリケーション内でモデルを簡単に交換できるようでありながら、開発体験を維持できるという素晴らしい状態を作り出すことができます。この投稿では、すでに GGUF メタデータの優れた点をいくつか取り上げるとともに、改善したいと願うことも多く述べました。今後数週間、当方の Hugging Face ページや llama.cpp の issues ボードにご関心をお寄せください。
本稿はすべて人間によって執筆されました。機械によって捏造された言葉は一切含まれていません。