llama.cpp Gemma 3 Vision 实战:mmproj 转换、llama-mtmd-cli 与 libmtmd 多模态推理
本文聚焦 llama.cpp 对 Gemma 3 视觉模型的推理支持:从构建 llama-mtmd-cli 并加载预量化 GGUF 模型,到使用 convert_hf_to_gguf.py 自行生成 mmproj.gguf 视觉投影器,再到结合仓库源码理解 Gemma 3 投影器在 libmtmd 中的加载与推理链路。读完本文,你可以独立完成 Gemma 3 4B/12B/27B 图文推理的完整部署,并理解"文本模型 + mmproj 双文件"架构在 llama.cpp 中的落地方式。
定位:Gemma 3 vision 在 llama.cpp 多模态体系中的位置
llama.cpp 通过 libmtmd 库提供多模态输入支持(图片、音频、视频),官方文档明确标注 Gemma 3 视觉支持目前仍处于高度实验性阶段,仅用于演示目的(见 gemma3.md 开头的 IMPORTANT 提示)。目前有三个工具可以使用该能力:
llama-cli:常规命令行推理,支持-m+--mmproj组合;llama-server:通过 OpenAI 兼容的/chat/completionsAPI 使用;llama-mtmd-cli:面向测试与开发的多模态专用 CLI,Gemma 3 文档给出的正是该工具的用法。
多模态支持的整体架构、libmtmd 的演进历史(从最初的 llava.cpp 到统一的 mtmd-cli)可以参见 tools/mtmd/README.md,多模态模型的总入口文档见 docs/multimodal.md。
其核心机制是:用一个独立的多模态投影器(multimodal projector,简称 mmproj)把图片编码为嵌入向量,再喂给语言模型。这种分离设计让视觉组件与核心 libllama 解耦——不同视觉模型的预处理和投影步骤差异很大,直接塞进 libllama 会很复杂,因此运行一个多模态模型通常需要两个 GGUF 文件:语言模型文件与对应的 mmproj 文件。
快速上手:构建 llama-mtmd-cli 并运行预量化 Gemma 3 模型
Gemma 3 的视觉能力可以通过 ggml-org 在 Hugging Face 上的预量化 GGUF 模型直接获得,无需自行转换。文档给出的最简流程如下:
# build
cmake -B build
cmake --build build --target llama-mtmd-cli
# alternatively, install from brew (MacOS)
brew install llama.cpp
# run it
llama-mtmd-cli -hf ggml-org/gemma-3-4b-it-GGUF
llama-mtmd-cli -hf ggml-org/gemma-3-12b-it-GGUF
llama-mtmd-cli -hf ggml-org/gemma-3-27b-it-GGUF
# note: 1B model does not support vision
几点关键说明:
-hf user/repo选项:-hf会同时下载文本模型和配套的 mmproj 文件,替代-m与--mmproj两个参数。如果只想加载文本部分而禁用多模态,可加--no-mmproj;如果想用-hf加载文本模型但指定本地自定义的 mmproj 文件,则用--mmproj local_file.gguf(见 docs/multimodal.md)。- 1B 版本没有视觉能力:Gemma 3 1B 是纯文本模型,只有 4B、12B、27B 的 instruction 版本支持图文推理。
- 上下文窗口:多模态模型的图片会占用大量 token,部分模型可能需要显式指定较大上下文,例如
-c 8192。 - 同样的
-hf用法同样适用于llama-server,例如llama-server -hf ggml-org/gemma-3-4b-it-GGUF,即可通过 HTTP API 提供图文对话服务。
如何获得 mmproj.gguf:用 convert_hf_to_gguf.py 的 --mmproj 参数
如果不想使用预量化文件,可以从 Gemma 3 的 Hugging Face 官方 checkpoint 自行转换。文档给出的命令是在模型目录内执行:
cd gemma-3-4b-it
python ../llama.cpp/convert_hf_to_gguf.py --outfile model.gguf --outtype f16 --mmproj .
# output file: mmproj-model.gguf
这里 --mmproj . 表示从当前目录(Hugging Face checkpoint 目录)读取视觉部分权重并单独导出一个投影器文件。转换脚本中该参数的定义与行为可以对照 convert_hf_to_gguf.py:
"--mmproj", action="store_true",
help="Export multimodal projector (mmproj) for vision models. This will only work on some vision models. An 'mmproj-' prefix will be added to the output file name.",
也就是说:--mmproj 是布尔开关,只在部分视觉模型上有效;输出文件名会自动加上 mmproj- 前缀。转换入口处会据此将模型类型切换为 ModelType.MMPROJ(见 convert_hf_to_gguf.py),并在导出时把 gguf_type 标记为 MMPROJ,默认输出名形如 mmproj-<模型名>.gguf(见 conversion/base.py)。
源码视角:Gemma 3 的 mmproj 里到底装了什么
Gemma 3 的视觉投影器转换类是 conversion/gemma.py 中的 Gemma3VisionModel,它继承自通用的 MmprojModel,并注册到 Hugging Face 架构名 Gemma3ForConditionalGeneration 下。几个值得注意的实现细节:
- 写入投影器类型标记:
set_gguf_parameters()会调用add_clip_projector_type(gguf.VisionProjectorType.GEMMA3),同时写入视觉注意力 LayerNorm 的 eps(默认1e-6)和vision_use_gelu = True。这个GEMMA3标记就是libmtmd在运行时选择对应投影器图的关键(见下文)。 - proj_scale_factor 的自动推导:代码从
preprocessor_config读取image_seq_length(默认 256),得到n_per_side = 16(16×16 = 256 个视觉 token),再结合image_size / patch_size计算proj_scale_factor,只有非默认值 4 时才会写入 GGUF——这是为 tinygemma3 测试模型留的兼容项。这也解释了为什么一张图片在 Gemma 3 中默认对应 256 个视觉 token。 - 强制量化策略:
tensor_force_quant()强制input_projection相关张量保持 F16,.embeddings.张量保持 F32(见 conversion/gemma.py),避免关键投影层在量化后损失精度。 - 张量过滤:
filter_tensors()只保留multi_modal_projector.、vision_tower.、multimodal_projector.、vision_model.前缀的张量,跳过vision_model.head.等冗余部分——即 mmproj 文件只包含视觉塔(SigLIP)与投影头,不含任何语言模型权重。 - norm 值修正:Gemma 3 的
Gemma3RMSNorm实现为output = output * (1.0 + weight),而 HF 权重存的是weight,所以转换时对soft_emb_norm.weight执行data_torch + 1修正(见 conversion/gemma.py)。注意注释强调只需修正这个投影器内的 norm,视觉塔里 SigLIP 的 norm 值本来就是正确的。
对应的语言模型侧转换类 Gemma3Model(同一文件 conversion/gemma.py)则处理 Gemma3ForCausalLM 与 Gemma3ForConditionalGeneration 的文本权重:移除 OOV 嵌入行、对 norm.weight 做 +1 平移、写入 RoPE 基频 1,000,000 等参数。文本 GGUF 与 mmproj GGUF 必须来自同一个 checkpoint,才能配对使用。
运行图文推理:llama-mtmd-cli 的完整用法
运行 Gemma 3 视觉推理需要三样东西(文档原文要求):
- 文本模型 GGUF(可用
convert_hf_to_gguf.py转换得到); - 上一步生成的 mmproj 文件;
- 一张图片文件。
构建与运行命令:
# build
cmake -B build
cmake --build build --target llama-mtmd-cli
# run it
./build/bin/llama-mtmd-cli -m {text_model}.gguf --mmproj mmproj.gguf --image your_image.jpg
其中 {text_model}.gguf 替换为你的文本模型路径,mmproj.gguf 替换为你的投影器文件名(转换脚本默认会生成带 mmproj- 前缀的文件名)。
从 tools/mtmd/mtmd-cli.cpp 的 usage 文本可以确认该工具的完整参数语义:
Usage: %s [options] -m <model> --mmproj <mmproj> --image <image> --audio <audio> -p <prompt>
-m and --mmproj are required
-hf user/repo can replace both -m and --mmproj in most cases
--image, --audio and -p are optional, if NOT provided, the CLI will run in chat mode
to disable using GPU for mmproj model, add --no-mmproj-offload
由此可以得到几个实操要点:
- 单轮问答 vs 交互聊天模式:
--image、--audio、-p都是可选的。若都省略,CLI 进入聊天模式;此时还可以用内置命令动态加载媒体(见 tools/mtmd/mtmd-cli.cpp):/image <path>加载图片;- 类似的
/audio、/video命令加载音频与视频。
- prompt 中的图片占位:单轮模式下
is_single_turn = !params.prompt.empty() && !params.image.empty()(见 tools/mtmd/mtmd-cli.cpp);多数模型要求在每张图之前插入标记 token,CLI 会在模板中自动处理(同文件 L458 附近:// most models require the marker before each image)。 - mmproj 的 GPU offload:多模态投影器默认会被 offload 到 GPU;如果显存紧张或想纯 CPU 运行,加
--no-mmproj-offload即可(llama-server同理)。从源码看,mmproj 加载参数包含mmproj_use_gpu、mmproj_device,以及图片 token 数的上下限image_min_tokens/image_max_tokens(见 tools/mtmd/mtmd-cli.cpp),后者可以控制高分辨率图片被下采样后至少/最多产生多少个视觉 token。 - 缺少 mmproj 会直接报错:若既没有
--mmproj也没有-hf,程序会输出ERR: Missing --mmproj argument(见 tools/mtmd/mtmd-cli.cpp)。
libmtmd 中的 Gemma 3 投影器:运行链路验证
在 libmtmd 内部,投影器类型是运行时行为分发的核心。tools/mtmd/clip-impl.h 定义了投影器枚举,其中包含:
PROJECTOR_TYPE_GEMMA3,
PROJECTOR_TYPE_GEMMA3NV,
PROJECTOR_TYPE_GEMMA3NA,
...
{ PROJECTOR_TYPE_GEMMA3, "gemma3"},
{ PROJECTOR_TYPE_GEMMA3NV, "gemma3nv"},
{ PROJECTOR_TYPE_GEMMA3NA, "gemma3na"},
字符串名 "gemma3" 与转换脚本写入 GGUF 的 VisionProjectorType.GEMMA3 一一对应,加载 mmproj 时 libmtmd 依据该标记构建对应的计算图。相关的张量名宏也标注了来源(见 tools/mtmd/clip-impl.h):
#define TN_MM_INP_PROJ "mm.input_projection.weight" // gemma3
#define TN_MM_SOFT_EMB_N "mm.soft_emb_norm.weight" // gemma3
这两个张量正是转换脚本中强制 F16(input_projection)与做 norm 修正(soft_emb_norm)的那两组权重。
在 tools/mtmd/clip.cpp 中,PROJECTOR_TYPE_GEMMA3 出现在多处分支:模型参数初始化时读取可选的 vision_projector_scale_factor(默认 4,兼容 tinygemma3 测试模型,见 tools/mtmd/clip.cpp);视觉塔实现则由 tools/mtmd/models/siglip.cpp 承担——Gemma 3 的视觉塔就是 SigLIP,siglip.cpp 中对 proj_type == PROJECTOR_TYPE_GEMMA3 有专门处理。图片解码流程中,Gemma 3 与 Gemma3n 一样产生 16×16 = 256 个视觉 token(Gemma3n 的 MobileNetV5 塔同样产出 256 token,见 tools/mtmd/clip.cpp 的注释)。
常见问题与限制
- 实验性状态:文档开头即声明这是 "very experimental, only used for demo purpose",
tools/mtmd的 README 也强调多模态属于重度开发中的子项目,破坏性变更随时可能发生,参数与二进制名可能随版本调整。 - 1B 无视觉:Gemma 3 1B 不含视觉塔,
-hf也无法为其找到 mmproj,请勿用于图文任务。 - 版本对应:
--mmproj转换与libmtmd的投影器实现必须配套同一份仓库版本;混用不同版本转换出的 mmproj 与旧版libmtmd可能因张量名或类型标记不匹配而失败。 - 旧工具名:历史版本中曾存在
gemma3-cli等按模型划分的二进制(见 tools/mtmd/README.md 的时间线说明),现已统一为llama-mtmd-cli(旧目标如 tools/mtmd/CMakeLists.txt 中的llama-gemma3-cli现在只指向 deprecation warning 程序),新环境请直接使用-hf或-m+--mmproj的标准用法。
小结
llama.cpp 对 Gemma 3 视觉模型的接入路径可以概括为三步:
- 获取模型:直接用
llama-mtmd-cli -hf ggml-org/gemma-3-{4b,12b,27b}-it-GGUF加载 ggml-org 预量化 GGUF;或从 HF checkpoint 用convert_hf_to_gguf.py分别导出文本 GGUF 与--mmproj投影器 GGUF。 - 运行推理:
llama-mtmd-cli -m model.gguf --mmproj mmproj.gguf --image your_image.jpg做单轮图文问答,或省略-p/--image进入聊天模式用/image命令动态加载图片;服务端场景则用llama-server -hf ...。 - 理解底层:mmproj GGUF 中的
GEMMA3投影器标记驱动libmtmd(clip.cpp/siglip.cpp)构建 SigLIP 视觉塔 + 投影头的计算图,每张图默认编码为 256 个视觉 token 嵌入后送入语言模型;GPU offload、token 数上下限等均可通过 CLI 参数调节。
文中涉及的关键源码路径汇总:docs/multimodal/gemma3.md(原始指南)、docs/multimodal.md(多模态总览与预量化模型清单)、tools/mtmd/README.md(libmtmd 架构说明)、convert_hf_to_gguf.py(转换入口)、conversion/gemma.py(Gemma 3 文本/视觉转换实现)、tools/mtmd/mtmd-cli.cpp(CLI 实现)、tools/mtmd/clip.cpp 与 tools/mtmd/clip-impl.h(投影器运行时实现)。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00