首页
/ llama.cpp Gemma 3 Vision 实战:mmproj 转换、llama-mtmd-cli 与 libmtmd 多模态推理

llama.cpp Gemma 3 Vision 实战:mmproj 转换、llama-mtmd-cli 与 libmtmd 多模态推理

2026-09-04 23:17:53作者:廉皓灿Ida

本文聚焦 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/completions API 使用;
  • 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

几点关键说明:

  1. -hf user/repo 选项-hf 会同时下载文本模型和配套的 mmproj 文件,替代 -m--mmproj 两个参数。如果只想加载文本部分而禁用多模态,可加 --no-mmproj;如果想用 -hf 加载文本模型但指定本地自定义的 mmproj 文件,则用 --mmproj local_file.gguf(见 docs/multimodal.md)。
  2. 1B 版本没有视觉能力:Gemma 3 1B 是纯文本模型,只有 4B、12B、27B 的 instruction 版本支持图文推理。
  3. 上下文窗口:多模态模型的图片会占用大量 token,部分模型可能需要显式指定较大上下文,例如 -c 8192
  4. 同样的 -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)则处理 Gemma3ForCausalLMGemma3ForConditionalGeneration 的文本权重:移除 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

由此可以得到几个实操要点:

  1. 单轮问答 vs 交互聊天模式--image--audio-p 都是可选的。若都省略,CLI 进入聊天模式;此时还可以用内置命令动态加载媒体(见 tools/mtmd/mtmd-cli.cpp):
    • /image <path> 加载图片;
    • 类似的 /audio/video 命令加载音频与视频。
  2. 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)。
  3. mmproj 的 GPU offload:多模态投影器默认会被 offload 到 GPU;如果显存紧张或想纯 CPU 运行,加 --no-mmproj-offload 即可(llama-server 同理)。从源码看,mmproj 加载参数包含 mmproj_use_gpummproj_device,以及图片 token 数的上下限 image_min_tokens / image_max_tokens(见 tools/mtmd/mtmd-cli.cpp),后者可以控制高分辨率图片被下采样后至少/最多产生多少个视觉 token。
  4. 缺少 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 视觉模型的接入路径可以概括为三步:

  1. 获取模型:直接用 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。
  2. 运行推理llama-mtmd-cli -m model.gguf --mmproj mmproj.gguf --image your_image.jpg 做单轮图文问答,或省略 -p/--image 进入聊天模式用 /image 命令动态加载图片;服务端场景则用 llama-server -hf ...
  3. 理解底层:mmproj GGUF 中的 GEMMA3 投影器标记驱动 libmtmdclip.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.cpptools/mtmd/clip-impl.h(投影器运行时实现)。

登录后查看全文
热门项目推荐
相关项目推荐