llama.cppのマルチモーダルを試す
たまたま見かけたらserverでマルチモーダル対応していた
以前もチラホラとマルチモーダルには対応していたようなのだけど、それまではモデルアーキテクチャごとに別コマンドに別れていたのが1つのコマンドというかライブラリに集約されて、それを使ってサーバも・・・ということになったみたい。
マルチモーダルについてのドキュメントは以下の2つを読めば良さそう
マルチモーダル
llama.cpp は
libmtmd
を介してマルチモーダル入力をサポートします。現在、この機能をサポートするツールは 2 つあります:
- llama-mtmd-cli
- llama-server (OpenAI 互換の
/chat/completions
API 経由)次の 2 つの方法のいずれかで有効化できます:
- 対応モデルを指定して
-hf
オプションを使用する(事前量子化モデルの一覧は後述)
- マルチモーダルを無効にして
-hf
でモデルを読み込むには--no-mmproj
を使用- 独自の mmproj ファイルを使用して
-hf
でモデルを読み込むには--mmproj local_file.gguf
を使用-m model.gguf
オプションと--mmproj file.gguf
を併用して、テキストモデルとマルチモーダルプロジェクタをそれぞれ指定する既定では、マルチモーダルプロジェクタは GPU にオフロードされます。これを無効にするには
--no-mmproj-offload
を追加してください。例:
# CLI での簡単な使用例 llama-mtmd-cli -hf ggml-org/gemma-3-4b-it-GGUF # サーバでの簡単な使用例 llama-server -hf ggml-org/gemma-3-4b-it-GGUF # ローカルファイルを使う場合 llama-server -m gemma-3-4b-it-Q4_K_M.gguf --mmproj mmproj-gemma-3-4b-it-Q4_K_M.gguf # GPU オフロードなし llama-server -hf ggml-org/gemma-3-4b-it-GGUF --no-mmproj-offload
事前量子化済みモデル
これらはすぐに使用できるモデルで、大半は既定で
Q4_K_M
量子化が施されています。Hugging Face の ggml-org ページ(https://huggingface.co/ggml-org)にあります。
(tool_name)
は使用するバイナリ名に置き換えてください。例:llama-mtmd-cli
またはllama-server
注意: 一部のモデルでは大きなコンテキストウィンドウが必要になる場合があります。例:
-c 8192
# Gemma 3 (tool_name) -hf ggml-org/gemma-3-4b-it-GGUF (tool_name) -hf ggml-org/gemma-3-12b-it-GGUF (tool_name) -hf ggml-org/gemma-3-27b-it-GGUF # SmolVLM (tool_name) -hf ggml-org/SmolVLM-Instruct-GGUF (tool_name) -hf ggml-org/SmolVLM-256M-Instruct-GGUF (tool_name) -hf ggml-org/SmolVLM-500M-Instruct-GGUF (tool_name) -hf ggml-org/SmolVLM2-2.2B-Instruct-GGUF (tool_name) -hf ggml-org/SmolVLM2-256M-Video-Instruct-GGUF (tool_name) -hf ggml-org/SmolVLM2-500M-Video-Instruct-GGUF # Pixtral 12B (tool_name) -hf ggml-org/pixtral-12b-GGUF # Qwen 2 VL (tool_name) -hf ggml-org/Qwen2-VL-2B-Instruct-GGUF (tool_name) -hf ggml-org/Qwen2-VL-7B-Instruct-GGUF # Qwen 2.5 VL (tool_name) -hf ggml-org/Qwen2.5-VL-3B-Instruct-GGUF (tool_name) -hf ggml-org/Qwen2.5-VL-7B-Instruct-GGUF (tool_name) -hf ggml-org/Qwen2.5-VL-32B-Instruct-GGUF (tool_name) -hf ggml-org/Qwen2.5-VL-72B-Instruct-GGUF # Mistral Small 3.1 24B (IQ2_M 量子化) (tool_name) -hf ggml-org/Mistral-Small-3.1-24B-Instruct-2503-GGUF # InternVL 2.5 および 3 (tool_name) -hf ggml-org/InternVL2_5-1B-GGUF (tool_name) -hf ggml-org/InternVL2_5-4B-GGUF (tool_name) -hf ggml-org/InternVL3-1B-Instruct-GGUF (tool_name) -hf ggml-org/InternVL3-2B-Instruct-GGUF (tool_name) -hf ggml-org/InternVL3-8B-Instruct-GGUF (tool_name) -hf ggml-org/InternVL3-14B-Instruct-GGUF
llama.cpp におけるマルチモーダル対応
このディレクトリは
llama.cpp
にマルチモーダル機能を提供します。当初は LLaVA モデルを実行するためのショーケースとして意図されていましたが、時を経てさまざまな視覚モデルを含むまでに範囲が大きく拡張されました。その結果、LLaVA はもはや唯一の対応マルチモーダルアーキテクチャではありません。重要
マルチモーダル対応は
llama.cpp
内のサブプロジェクトと見なすことができます。開発が非常に活発であり、後方互換性のない変更が入る可能性があります。マルチモーダル対応に関する命名と構造は進化を続けており、混乱を招く場合があります。以下の簡単なタイムラインが経緯を示します:
- #3436: LLaVA 1.5 の初期サポートが追加され、
llava.cpp
とclip.cpp
が導入されました。モデルと対話するためのllava-cli
バイナリが作成されました。- #4954: MobileVLM のサポートが追加され、2 番目の視覚モデルとなりました。既存の
llava.cpp
、clip.cpp
、llava-cli
基盤を活用しています。- 拡張と分裂: その後多くの新しいモデルが追加されました(例: #7599、#10361、#12344 など)。しかし
llava-cli
はこれらのモデルが必要とする複雑化するチャットテンプレートをサポートできず、qwen2vl-cli
、minicpmv-cli
、gemma3-cli
などモデル固有バイナリが乱立しました。機能的ではあるものの、CLI の増殖はユーザーを混乱させました。- #12849:
libmtmd
が導入され、llava.cpp
の置き換えとして登場しました。その目的は、単一で統一されたコマンドラインインターフェースを提供し、ユーザー/開発者体験(UX/DX)を向上させ、音声と画像の両入力に対応することです。- #13012:
mtmd-cli
が追加され、libmtmd
を基盤としてモデル固有 CLI を 1 つのツールに統合しました。事前量子化済みモデル
事前量子化モデルの一覧はこちらをご覧ください。
仕組みと
mmproj
とは?
llama.cpp
のマルチモーダル対応は、画像を別個のモデルコンポーネントで埋め込みにエンコードし、これを言語モデルに入力することで機能します。このアプローチにより、マルチモーダル要素をコアの
libllama
から分離できます。分離することで、迅速かつ独立した開発サイクルが可能になります。多くの最新視覚モデルは Vision Transformer(ViT)を基盤としていますが、前処理と投影の手順は大きく異なる場合があります。こうした多様な複雑性を直接libllama
に統合するのは現状では困難です。そのため、マルチモーダルモデルを実行するには通常 2 つの GGUF ファイルが必要です:
- 標準の言語モデルファイル
- 画像のエンコードと投影を行う マルチモーダルプロジェクタ(
mmproj
) ファイル
libmtmd
とは?前述の経緯のとおり、
libmtmd
はマルチモーダル入力を扱うための、元のllava.cpp
実装に代わる最新ライブラリです。
llava.cpp
と同様にclip.cpp
を基盤として構築され、以下の利点があります:
- 統一インターフェース: さまざまなマルチモーダルモデルとの対話を一元化することを目指します。
- UX/DX 向上: Hugging Face の
transformers
ライブラリのProcessor
クラスに着想を得た、より直感的な API を提供します。- 柔軟性: 多様なチャットテンプレートを尊重しつつ、テキスト・音声・画像など複数の入力タイプをサポートするよう設計されています。
mmproj
の入手方法
mmproj
(マルチモーダルプロジェクタ)ファイルはモデルアーキテクチャごとに固有です。以下のモデルについては、
convert_hf_to_gguf.py
を--mmproj
フラグ付きで使用してmmproj
ファイルを取得できます:
- Gemma 3(ガイドはこちら)※1B 版はビジョン非対応
- SmolVLM(HuggingFaceTB より)
- SmolVLM2(HuggingFaceTB より)
- Pixtral 12B —
transformers
互換チェックポイントのみ対応- Qwen 2 VL と Qwen 2.5 VL(Qwen より)
- Mistral Small 3.1 24B
- InternVL 2.5 と InternVL 3(OpenGVLab より)
※InternVL3-*-hf
モデルの変換は未対応、非 HF 版のみサポート
※InternLM2Model
テキスト モデルは未対応旧モデルについては、該当するガイドを参照して取得または作成してください:
注意: 変換スクリプトは
tools/mtmd/legacy-models
にあります
今回はお手軽にやりたいのでローカルのMac(M2 Pro)でやる。
レポジトリクローン
git clone https://github.com/ggml-org/llama.cpp && cd llama.cpp
ビルド
cmake -B build
cmake --build build --config Release -j 8
llama-server と llama-mtmd-cli がビルドされている
ls -lt build/bin/{llama-server,llama-mtmd-cli}
-rwxr-xr-x@ 1 kun432 staff 4448032 5 14 22:31 build/bin/llama-server
-rwxr-xr-x@ 1 kun432 staff 2175280 5 14 22:31 build/bin/llama-mtmd-cli
ではまず llama-mtmd-cli を使ってみる。モデルは事前量子化されたgemma-3-4b-itを使う。
./build/bin/llama-mtmd-cli -hf ggml-org/gemma-3-4b-it-GGUF
モデルのダウンロードが行われるのでしばらく待つ。以下のように表示されればOK。
(snip)
main: loading model: /Users/kun432/Library/Caches/llama.cpp/ggml-org_gemma-3-4b-it-GGUF_gemma-3-4b-it-Q4_K_M.gguf
Running in chat mode, available commands:
/image <path> load an image
/clear clear the chat history
/quit or /exit exit the program
>
ということで画像をロード。以下の画像を使う。
> /image ./kobe.jpg
Image ./kobe.jpg loaded
チャットしてみる
> この画像について簡潔に説明して。
encoding image or slice...
image/slice encoded in 15061 ms
decoding image batch 1/1, n_tokens_batch = 256
image decoded (batch 1/1) in 474 ms
この画像は、日本の瀬戸内海の街、岡山港の風景です。
特徴的なのは、赤い塔が印象的な「岡山観覧車」と、背景にそびえる高層ビル群です。穏やかな海面と青空が、この景観をより一層美しくしています。また、港には船が停泊しており、活気のある様子が伝わってきます。
知識は足りないが、画像の内容は理解しているね。
ワンライナーでも
./build/bin/llama-mtmd-cli \
-hf ggml-org/gemma-3-4b-it-GGUF \
--image kobe.jpg \
-p "この画像について説明して"
(snip)
この画像は、日本、瀬戸内海の街、岡山(Okayama)の港風景です。
主な特徴は以下の通りです。
* **岡山ドーム:** 鮮やかな赤色の巨大なドーム状の建造物で、岡山ドームのシンボルです。
* **岡山駅ビル:** ドームの左側に位置する高層ビルです。
* **港:** ドームと駅ビルの間に広がる港には、船や遊覧船が停泊しています。
* **海:** 港の背景には、青い海が広がっています。
* **空:** 青空の下、太陽が輝いています。
全体として、岡山港の美しい風景と、岡山ドームの存在感が印象的な写真です。
llama_perf_context_print: load time = 394.28 ms
llama_perf_context_print: prompt eval time = 15805.24 ms / 273 tokens ( 57.89 ms per token, 17.27 tokens per second)
llama_perf_context_print: eval time = 3688.90 ms / 165 runs ( 22.36 ms per token, 44.73 tokens per second)
llama_perf_context_print: total time = 20184.48 ms / 438 tokens
次に llama-server を使ってみる
./build/bin/llama-server -hf ggml-org/gemma-3-4b-it-GGUF
以下のように表示されればOK
(snip)
main: server is listening on http://127.0.0.1:8080 - starting the main loop
srv update_slots: all slots are idle
サーバへのアクセスは
- GUI
- OpenAI互換API
の2種類ある。というか、GUIあるのずっと知らなかったよ・・・
まずGUI。ブラウザで上記のURLにアクセスするとこんな画面になる。
画像を添付して質問するとこんな感じ。
次にAPI。こちらはOpenAI互換APIになっている。
curlでアクセス。
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "ggml-org_gemma-3-4b-it-GGUF_gemma-3-4b-it-Q4_K_M.gguf",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "この画像について詳しく説明して"
},
{
"type": "image_url",
"image_url": {
"url": "https://storage.googleapis.com/zenn-user-upload/82968d23b6c5-20250228.jpg"
}
}
]
}
],
"max_tokens": 1024
}' | jq -r .
{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"role": "assistant",
"content": "この画像は、日本の瀬戸内海に面した街並みを捉えた写真です。以下に詳細な説明をします。\n\n**主な要素:**\n\n* **観覧車 (マササゴ観覧車):** 画面の中央に大きく見えます。これは、岡山県岡山市の中心部に位置する、特徴的な赤い観覧車です。そのユニークな形状と、街のランドマークとして知られています。\n* **高層ビル群:** 観覧車を中心に、様々なデザインの高層ビルが並んでいます。これらのビルは、現代的な都市の景観を形成しています。\n* **港と水面:** 画面下部には、穏やかな海面が広がっています。海面には、太陽光を反射した水面の波紋や、船の影が映り込み、奥行きと動きを加えています。\n* **船:** 画面右側に、クルーズ船や小型の船が停泊しています。\n* **背景:** 背景には、緑豊かな山々が連なり、自然との調和を感じさせます。\n\n**場所:**\n\nこの画像は、岡山県の岡山市の中心部にある、岡山港の風景を撮影したものと思われます。観覧車は、岡山港のシンボルとして、観光客に人気のスポットです。\n\n**雰囲気:**\n\n全体的に、晴天で穏やかな雰囲気の画像です。高層ビルと観覧車が織りなす景色は、現代的な都市の魅力を伝えます。\n\n**補足:**\n\n* 撮影角度は、港から陸地に向かって撮影されたものと思われます。\n* 写真の構図は、観覧車を強調し、都市の景観全体を捉えるように工夫されています。\n\nこの説明で、この画像についてより深く理解できることを願っています。"
}
}
],
"created": 1747234286,
"model": "ggml-org_gemma-3-4b-it-GGUF_gemma-3-4b-it-Q4_K_M.gguf",
"system_fingerprint": "b5381-5e7d95e2",
"object": "chat.completion",
"usage": {
"completion_tokens": 384,
"prompt_tokens": 274,
"total_tokens": 658
},
"id": "chatcmpl-5tOwxMi3h4hGBZ7F6voCfSewFycrExW2",
"timings": {
"prompt_n": 1,
"prompt_ms": 118.884,
"prompt_per_token_ms": 118.884,
"prompt_per_second": 8.411560849231183,
"predicted_n": 384,
"predicted_ms": 8035.073,
"predicted_per_token_ms": 20.924669270833334,
"predicted_per_second": 47.790480559417446
}
}
Pythonで。
from openai import OpenAI
client = OpenAI(
api_key="dummy",
base_url="http://localhost:8080/v1",
)
response = client.chat.completions.create(
model="ggml-org_gemma-3-4b-it-GGUF_gemma-3-4b-it-Q4_K_M.gguf",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "この画像について詳しく説明して。"},
{
"type": "image_url",
"image_url": {
"url": "https://storage.googleapis.com/zenn-user-upload/82968d23b6c5-20250228.jpg",
}
},
],
}
],
max_tokens=1024,
)
print(response.choices[0].message.content)
この画像は、日本の瀬戸内海の街、岡山(Okayama)のハーバーランドの風景です。以下に詳細を説明します。
**主な要素:**
* **岡山港:** 海面に映る波の模様が特徴的な岡山港が前景に広がっています。
* **岡山港ターミナル:** 港の左側に、モダンなデザインのターミナルビルが並んでいます。
* **岡山港タワー:** 画像の中心にそびえ立つのは、岡山港タワーです。特徴的なデザインで、赤と白のラインが印象的です。
* **岡山駅ビル:** 岡山港タワーの近くには、岡山駅ビルの白い建物が見えます。
* **船:** 港に停泊しているクルーズ船や客船が、風景の一部として入っています。
* **背景の山:** 岡山港の背後には、緑豊かな山々が連なっています。
* **空:** 青空が広がり、晴れた日中の風景を写しています。
**場所:**
この写真は、岡山港のハーバーランド地区を撮影したものです。ハーバーランドは、岡山港を背景にした、ショッピングやレジャーが楽しめるエリアです。
**全体的な印象:**
全体的に、岡山港の活気と、近未来的な建築物との調和が感じられる写真です。晴れた日中の明るい空と、海面を照らす光が、この風景をより魅力的にしています。
もし、この写真について何か特定の質問があれば、遠慮なく聞いてください。
ローカルのファイルをBASE64に変換して送信することもできる。
from openai import OpenAI
import base64
def encode_image(image_path):
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode("utf-8")
image_path = "kobe.jpg"
base64_image = encode_image(image_path)
client = OpenAI(
api_key="dummy",
base_url="http://localhost:8080/v1",
)
response = client.chat.completions.create(
model="ggml-org_gemma-3-4b-it-GGUF_gemma-3-4b-it-Q4_K_M.gguf",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "この画像について詳しく説明して。"},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{base64_image}",
}
},
],
}
],
max_tokens=1024,
)
print(response.choices[0].message.content)
でここまではggml-org公式の事前量子化済+mmproj作成済のモデルを使用していた。自分でこれを作るにはどうすればいいか?
以下のモデルについては、convert_hf_to_gguf.py を --mmproj フラグ付きで使用して mmproj ファイルを取得できます:
ということでllama.cpp側でも対応済みである必要があるのだが、mmprojファイル付きのGGUF化を試してみる。例としてgemma-3-4b-itを使う。
まあこれはドキュメントが用意されているのだけどね。一応写経ということで。
llama.cppのクローンしたレポジトリの直下で作業を続ける。
まずHuggingFaceからモデルをダウンロード
git lfs install
git clone https://huggingface.co/google/gemma-3-4b-it
GGUF変換を行うスクリプトはPython環境が必要なので、仮想環境を用意して、パッケージをインストールする。
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
convert_hf_to_gguf.py
を使って、モデルをGGUFに変換する。この時--mmproj
を指定する
cd gemma-3-4b-it
python ../convert_hf_to_gguf.py --outfile gemma-3-4b-it-f16.gguf --outtype f16 --mmproj .
INFO:hf-to-gguf:Model successfully exported to mmproj-gemma-3-4b-it-f16.gguf
mmprojファイルが生成されている
ls -lt *.gguf
-rw-r--r--@ 1 kun432 staff 851251552 5 15 08:29 mmproj-gemma-3-4b-it-f16.gguf
ただしモデルの本体はまだGGUF化されていない。一緒にやってくれるのかなと思ったんだけどね。とりあえずこちらもGGUF作成する。--mmproj
を外して再度実行。
python ../convert_hf_to_gguf.py --outfile gemma-3-4b-it-f16.gguf --outtype f16 .
これでモデル本体のGGUFファイルと、mmprojファイルができた。
ls -lt *.gguf
-rw-r--r--@ 1 kun432 staff 7767474208 5 15 08:31 gemma-3-4b-it-f16.gguf
-rw-r--r--@ 1 kun432 staff 851251552 5 15 08:29 mmproj-gemma-3-4b-it-f16.gguf
これがあれば推論できるはずなんだけど、一旦量子化する。
../build/bin/llama-quantize gemma-3-4b-it-f16.gguf gemma-3-4b-it-Q4_K_M.gguf Q4_K_M
では推論
../build/bin/llama-mtmd-cli \
-m gemma-3-4b-it-Q4_K_M.gguf \
--mmproj mmproj-gemma-3-4b-it-f16.gguf \
--image ../kobe.jpg \
-p "この画像について説明して。"
エラー・・・
(snip)
main: loading model: gemma-3-4b-it-Q4_K_M.gguf
encoding image or slice...
ggml_metal_graph_compute: command buffer 1 failed with status 5
error: Internal Error (0000000e:Internal Error)
clip_image_batch_encode: ggml_backend_sched_graph_compute failed with error -1
failed to encode image
failed to eval chunk 1
Unable to eval prompt
うーん、ちょっとよくわからない・・・ちなみに量子化しないままでも試してみたんだけど、同じエラーになった。まあまだEXPERIMENTALだし一旦寝かしておくか。
追記20250515
Linux(Ubuntu-22.04)上で、上と全く同じ手順でやってみたら上手く行った。んー、Macだと上手くいかないのかな?
(snip)
main: loading model: gemma-3-4b-it-Q4_K_M.gguf
encoding image or slice...
image/slice encoded in 411 ms
decoding image batch 1/1, n_tokens_batch = 256
image decoded (batch 1/1) in 442 ms
この画像は、日本の瀬戸内海の街、岡山(Okayama)のハーバーランドの風景です。
主な特徴は以下の通りです。
* **岡山ドーム:** 画面中央にそびえ立つ、特徴的な赤いドーム型の建造物です。
* **ハーバーランド:** 岡山ドームの周辺には、商業施設やホテルなど、現代的な建物が並んでいます。
* **海と船:** 水面に映る太陽の反射が美しく、背景には海と停泊している船が見えます。
* **晴天:** 青空の下、晴れた日差しが印象的です。
このハーバーランドは、岡山市を活性化させるプロジェクトの一環として開発された複合施設で、観光や商業の中心地となっています。
llama_perf_context_print: load time = 490.27 ms
llama_perf_context_print: prompt eval time = 1113.12 ms / 274 tokens ( 4.06 ms per token, 246.15 tokens per second)
llama_perf_context_print: eval time = 8169.85 ms / 168 runs ( 48.63 ms per token, 20.56 tokens per second)
llama_perf_context_print: total time = 9589.03 ms / 442 tokens
とりあえずマルチモーダルもお手軽に使えるようになってきたということで、今後に期待。
個人的にはOvis2が使えるようになると嬉しいなぁ・・・・
前に見たときよりも対応モデル増えてる
- Llama-4-Scout-17B-16E-Instruct
- moondream2-20250414
- ultravox-v0_5-llama-3_2-1b
- ultravox-v0_5-llama-3_1-8b
- Qwen2.5-Omni-3B
- Qwen2.5-Omni-7B
オーディオモデルとか複数モダリティ対応とか