為什麼 llama.cpp 跑 Vision Model 還要另外載入 mmproj?
全文為AI參考實際開發狀況進行記錄並撰寫。
很多人第一次用 llama.cpp 跑 vision model 時,會有一個很直覺的疑問:
這個模型不是本來就是 vision model 嗎? 為什麼還要另外載入一個
mmproj檔案?
答案是:在 llama.cpp 裡,vision model 通常不是單一檔案完成所有事情,而是被拆成「語言模型」和「視覺轉接器」兩個部分。
一張圖看懂
使用者丟一張圖片
│
▼
┌────────────────┐
│ 圖片 pixels │
│ 照片 / 截圖 / 圖表 │
└───────┬────────┘
│
▼
┌────────────────┐
│ mmproj │
│ 視覺轉接器 │
└───────┬────────┘
│
▼
┌────────────────┐
│ LLM .gguf │
│ 語言模型大腦 │
└───────┬────────┘
│
▼
產生文字回答
mmproj 的角色,就是把圖片轉成語言模型看得懂的格式。
如果沒有 mmproj,流程會變成這樣:
圖片 pixels
│
▼
┌────────────────┐
│ LLM .gguf │
│ 語言模型大腦 │
└────────────────┘
▲
│
└── 看不懂原始圖片資料
語言模型本身主要吃的是 token,也就是文字或已經轉換好的向量表示。 圖片的 pixel 資料不能直接丟給它。
「Vision Model」不等於「單一 GGUF 檔就能看圖」
在 Hugging Face 上,一個 vision model 可能看起來是一個完整模型。
但轉成 llama.cpp / GGUF 使用時,常常會拆成兩個檔案:
主模型:
qwen3.6-35b-a3b.gguf
gemma-4-E4B-it-Q4_K_M.gguf
視覺 projector:
mmproj-Qwen3.6-35B-A3B-F16.gguf
mmproj-gemma-4-E4B-it-Q8_0.gguf
主模型負責語言推理。
mmproj 負責把圖片轉成主模型可以理解的 embedding。
可以把它想成:
眼睛看到東西
│
▼
視神經把訊號轉成大腦看得懂的格式
│
▼
大腦理解並回答
在這個比喻裡:
圖片 = 外界畫面
mmproj = 視神經 / 轉接器
LLM .gguf = 大腦
llama.cpp 為什麼要這樣設計?
原因是不同 vision model 的圖片處理方式差很多。
有些模型用不同的 vision encoder。 有些模型有不同的 image preprocessing。 有些模型的 projector 架構也不同。
如果 llama.cpp 把所有 multimodal 邏輯都硬塞進核心 LLM runtime,維護成本會很高,也很容易壞。
所以 llama.cpp 把 multimodal 支援拆成比較獨立的部分:
┌────────────────────────────┐
│ llama.cpp core │
│ 負責跑語言模型 │
└────────────────────────────┘
┌────────────────────────────┐
│ multimodal / mmproj │
│ 負責圖片、音訊等額外輸入 │
└────────────────────────────┘
這樣做的好處是:
- 語言模型核心保持乾淨。
- vision/audio 支援可以獨立演進。
- 不同 multimodal 架構可以各自處理自己的轉換邏輯。
代價是:使用本機 .gguf 檔時,你需要明確指定對應的 mmproj。
實際啟動差異
只載入主模型:
llama-server \
-m gemma-4-E4B-it-Q4_K_M.gguf
這時候它主要是文字模型。 即使這個模型架構支援 vision,llama.cpp 也沒有載入圖片轉換元件。
正確載入 vision 能力:
llama-server \
-m gemma-4-E4B-it-Q4_K_M.gguf \
--mmproj mmproj-gemma-4-E4B-it-Q8_0.gguf
這樣 llama.cpp 才知道:
收到圖片
→ 交給 mmproj 處理
→ 轉成模型可理解的表示
→ 餵給主模型回答
-hf 和 -m 的差別
llama.cpp 有兩種常見用法。
用 Hugging Face repo:
llama-server -hf ggml-org/gemma-4-E4B-it-GGUF
這種方式有時候 llama.cpp 可以從 repo 結構裡找到對應的 multimodal projector。
但如果你是用本機模型檔:
llama-server -m /path/to/model.gguf
那你通常就要自己指定:
--mmproj /path/to/mmproj.gguf
因為 llama.cpp 不會憑空知道哪個 projector 才是這個模型的正確搭配。
還有一層:OpenCode 也要知道模型支援圖片
在我們這次的情境裡,不只是 llama.cpp 要載入 mmproj。
OpenCode 這一層也要知道這個 provider model 可以接受 image input。
所以設定裡還需要像這樣:
"modalities": {
"input": ["text", "image"],
"output": ["text"]
}
這兩層解決的是不同問題:
OpenCode modalities
= 告訴 OpenCode:這個模型可以收圖片,不要擋掉
llama.cpp --mmproj
= 告訴 llama.cpp:真的載入圖片處理元件
也就是:
使用者上傳圖片
│
▼
OpenCode 檢查模型是否允許 image
│
▼
llama.cpp 用 mmproj 處理圖片
│
▼
LLM 產生回答
如果少了 OpenCode 的 modalities,圖片可能還沒到 llama.cpp 就被擋掉。
如果少了 llama.cpp 的 --mmproj,圖片到了後端也沒有真正的 vision pipeline 可以處理。
小結
「這是 vision model」這句話,代表模型架構或原始 checkpoint 具備看圖能力。
但在 llama.cpp 實際執行時,通常需要同時具備:
1. 主模型 .gguf
2. 對應的 mmproj .gguf
3. 啟動時加上 --mmproj
4. 上層工具標記這個 model 支援 image input
所以問題的答案不是「它到底是不是 vision model」。
比較精準的說法是:
它是 vision-capable model,但 llama.cpp 需要額外載入對應的 multimodal projector,才能把圖片轉成主模型真的看得懂的輸入。