首页
/ llama.cpp 多模态实战:MiniCPM-V 4.6 的 GGUF 转换、量化与 llama-mtmd-cli 推理全流程

llama.cpp 多模态实战:MiniCPM-V 4.6 的 GGUF 转换、量化与 llama-mtmd-cli 推理全流程

2026-09-04 22:31:50作者:殷蕙予

本文以 llama.cpp 仓库中的 MiniCPM-V 4.6 多模态指南 为主体,完整覆盖从 PyTorch 检查点准备、convert_hf_to_gguf.py 双通道转换(语言模型 + 多模态投影器)、Q4_K_M 量化,到 llama-mtmd-cli 单轮/对话式推理的全流程操作;并结合 conversion/minicpm.pytools/mtmd/models/minicpmv.cpp 等源码,解释 SigLIP 视觉塔、窗口注意力 vit_merger 与 DownsampleMLP 最终融合器在 GGUF 中如何落图与执行。读完你可以独立完成 MiniCPM-V 4.6 在 llama.cpp 中的本地化部署,并理解其视觉编码链路的底层原理。

一、模型与检查点要求

MiniCPM-V 4.6 是 OpenBMB 的多模态大模型,在 llama.cpp 中属于 "mtmd"(multimodal)工具链支持的模型系列,tools/mtmd/README.md 也将其列为受支持模型并指向本文档。

按照 docs/multimodal/minicpmv4.6.md 的要求,准备阶段需要确认两点:

  1. 获取 PyTorch 检查点:从 Hugging Face 的 openbmb/MiniCPM-V-4_6 仓库下载模型,放到本地 MiniCPM-V-4_6 目录。
  2. 检查点格式硬性要求:必须使用标准 transformers v5.7.0+ 的检查点,不需要 trust_remote_codeconfig.json 中的架构(arch)应为 MiniCPMV4_6ForConditionalGeneration,其内部由 qwen3_5_text 文本模型、基于 SigLIP 的视觉塔,以及一个带窗口注意力的 vit_merger 组成。

这个架构描述可以从转换源码得到印证。conversion/minicpm.py 中对 MiniCPMV4_6ForConditionalGeneration 注册了两个类,分别对应两种转换模式:

@ModelBase.register("MiniCPMV4_6ForConditionalGeneration")
@ModelBase.example("openbmb/MiniCPM-V-4_6")
class MiniCPMV4_6TextModel(Qwen3_5TextModel):   # 文本模式:导出语言模型 GGUF
    model_arch = gguf.MODEL_ARCH.QWEN35
  • MiniCPMV4_6TextModel 继承自 Qwen3_5TextModel,GGUF 架构标记为 qwen35(见 gguf-py/gguf/constants.pyMODEL_ARCH.QWEN35 的定义),并在 filter_tensors 中剔除 model.merger.*(视觉融合器权重,属于 mmproj 文件)与 mtp(多 token 预测张量,推理暂不使用,行为对齐 Qwen3Next)。
  • MiniCPMV4_6VisionModel 继承自 MmprojModel,负责导出视觉塔 + 融合器的 mmproj GGUF,它剔除了 lm_head.*mtp 张量(这些属于语言模型文件)。

从源码结构看,文本侧的 Qwen3.5 采用线性注意力(gated delta net)与全注意力混合的混合架构:src/models/qwen35.cpp 在加载超参数时读取 ssm_d_convssm_d_innerssm_d_statessm_dt_rankssm_n_group 等线性注意力参数,并按 full_attention_interval(默认 4)标记哪些层走循环/线性实现(is_recr_impl),即每隔 4 层有一层完整注意力。这与文档中 "qwen3_5_text 文本模型" 的说法一致,也解释了为何该模型推理时对长上下文的 KV 压力与常规稠密 Transformer 不同。

二、构建 llama.cpp

若构建方式与你的平台有差异,可参考仓库官方构建文档(docs/ 目录下的 build 说明)。按照 docs/multimodal/minicpmv4.6.md 的标准流程:

git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp

使用 CMake 构建:

cmake -B build
cmake --build build --config Release

构建完成后,产物位于 build/bin/ 目录,本文后续涉及的 llama-mtmd-clillama-quantize 两个二进制都来自该目录。

三、用 convert_hf_to_gguf.py 转换出两个 GGUF 文件

这是 MiniCPM-V 4.6 与旧版 MiniCPM-V 变体在流程上的关键区别:旧版本依赖社区维护的 hf2gguf 独立转换仓库,而 4.6 直接通过 llama.cpp 内置的 convert_hf_to_gguf.py 转换。同一个脚本在原始 Hugging Face 目录上调用两次:第一次产出语言模型 GGUF,第二次加 --mmproj 产出多模态投影器 GGUF。

# 语言模型
python ./convert_hf_to_gguf.py ../MiniCPM-V-4_6 --outfile ../MiniCPM-V-4_6/ggml-model-f16.gguf

# 多模态投影器(视觉塔 + 窗口注意力 vit_merger + DownsampleMLP 融合器)
python ./convert_hf_to_gguf.py ../MiniCPM-V-4_6 --mmproj --outfile ../MiniCPM-V-4_6/mmproj-model-f16.gguf

# 可选:量化为 Q4_K_M
./build/bin/llama-quantize ../MiniCPM-V-4_6/ggml-model-f16.gguf ../MiniCPM-V-4_6/ggml-model-Q4_K_M.gguf Q4_K_M

三条命令的要点:

命令 作用 输出文件
--mmproj MiniCPMV4_6TextModel 分支,导出 Qwen3.5 文本塔 ggml-model-f16.gguf
--mmproj MiniCPMV4_6VisionModel 分支,导出 SigLIP 视觉塔 + 两级融合器 mmproj-model-f16.gguf
llama-quantize ... Q4_K_M 仅对语言模型做量化压缩(mmproj 通常保持 f16) ggml-model-Q4_K_M.gguf

mmproj 文件里写入了哪些关键元数据

结合 conversion/minicpm.pyMiniCPMV4_6VisionModel 实现,转换时向 GGUF 写入了若干决定推理行为的字段,这些正是 tools/mtmd/clip.cpp 在加载模型时读取的内容:

  • 投影器类型add_clip_projector_type(gguf.VisionProjectorType.MINICPMV4_6),常量值 "minicpmv4_6"(见 gguf-py/gguf/constants.py),推理端据此把图构建器路由到 clip_graph_minicpmv4_6tools/mtmd/clip.cpp)。
  • 下采样模式 downsample_mode:取自 preprocessor_config,只接受 "4x""16x" 两种取值,并据此写入 vision_projector_scale_factor(4x 模式为 2,16x 模式为 4)。4x 模式下还会从张量集合中剔除 *.vit_merger.*——因为该模式的检查点本就不含 vit_merger 权重。
  • 窗口注意力插入层 insert_layer_id:借用 GGUF 的 wa_layer_indexes 字段保存 vit_merger 的插入位置(默认 6,即 vit_merger 插在 ViT 第 6 层之后)。
  • 图像处理尺寸的特殊处理vision_config.image_size(980)只是 SigLIP 位置编码分桶网格(70×70),真正逐切片处理分辨率是预处理器的 scale_resolution(通常为 448)。转换代码会把 clip.vision.image_size 改写为 scale_resolution,使 tools/mtmd/clip.cpp 的切片逻辑与上游 MiniCPMV4_6ImageProcessorPil 的分片规则保持一致。
  • 激活函数:写入 vision_use_gelu = True,因为 SigLIP 视觉主干使用 gelu_pytorch_tanh,与 ggml 的 ggml_gelu(tanh 近似)一致。

四、视觉编码链路的图构建:窗口注意力 vit_merger 与两级下采样

推理时 mmproj 的计算图由 tools/mtmd/models/minicpmv.cpp 中的 clip_graph_minicpmv4_6::build() 构建,整条链路可概括为:SigLIP ViT → (16x 模式才有)vit_merger 窗口注意力 + 2×2 下采样 → 继续 ViT → DownsampleMLP 最终融合

关键实现细节:

  1. 模式判定const bool is_4x = hparams.n_merge == 2;n_mergetools/mtmd/clip.cpp 在加载 mmproj 时确定:默认 4(16x 模式),再尝试从 clip.vision.projector_scale_factor 覆盖,并断言只能取 2 或 4。同时该分支把 wa_layer_indexes 的首元素读为 insert_layer_id,与第三节的写入逻辑闭环。
  2. 16x 模式的窗口注意力 vit_merger!is_4x 分支):
    • 先把 ViT 前 insert_layer_id 层跑完,然后在插入点执行 vit_merger;
    • 通过 vit_merger_window_idx 把 token 重排为窗口主序(每个 4 token 窗口内连续),并用 vit_merger_window_mask 施加块对角掩码(-inf 屏蔽,仅对角 4×4 块内可互注意)——源码注释明确指出该布局"镜像 qwen2vl 的窗口注意力模式",以便 build_attn() 命中 flash-attention 路径;
    • 注意力后逆重排(vit_merger_inv_window_idx)并加残差;
    • 随后做 2×2 空间下采样:四邻域 token 取均值作为残差(ggml_scale(mean_res, 0.25f)),四 token 拼接后经 LayerNorm + MLP(FFN_GELU,即 tanh 近似 GELU)得到融合特征,这与上游 ViTWindowAttentionMerger 的 downsample MLP 逐一对应。
  3. DownsampleMLP 最终融合器:所有 ViT 层跑完后,再做一次 2×2 空间合并——四 token 拼接、LayerNorm、MLP,激活为 FFN_GELU_ERF(对应 PyTorch 的 nn.GELU(),erf 实现),输出即送入语言模型的视觉 token 嵌入。
  4. token 数量:最终视觉 token 数在 tools/mtmd/clip.cpp 中按 n_patches /= n_merge * n_merge 计算,即 16x 模式下每 16×16 个 patch 位置最终折成一个语言模型输入 token。

上述窗口重排、掩码与下采样索引张量(vit_merger_window_idxvit_merger_ds_idx_*merger_ds_idx_* 等)在运行期由 mtmd 前端按图像切片几何动态填充,属于图构建时声明的 ggml_set_input 输入,这也是该模型实现中"数据依赖索引"与"权重"分离处理的典型做法。

五、使用 llama-mtmd-cli 推理

模型准备完成后,在 Linux 或 macOS 上通过 llama-mtmd-cli 运行推理。以下命令完整继承自 docs/multimodal/minicpmv4.6.md

# 单轮模式(single-turn mode)
./build/bin/llama-mtmd-cli -m ../MiniCPM-V-4_6/ggml-model-f16.gguf \
    --mmproj ../MiniCPM-V-4_6/mmproj-model-f16.gguf \
    -c 4096 --temp 0.7 --top-p 0.8 --top-k 100 --repeat-penalty 1.05 \
    --image xx.jpg -p "What is in the image?"

# 对话模式(conversation mode)
./build/bin/llama-mtmd-cli -m ../MiniCPM-V-4_6/ggml-model-Q4_K_M.gguf \
    --mmproj ../MiniCPM-V-4_6/mmproj-model-f16.gguf

关键参数说明:

参数 说明
-m 语言模型 GGUF(f16 或量化后的 Q4_K_M 均可)
--mmproj 多模态投影器 GGUF,必须与语言模型配套,不可省略
-c 4096 上下文窗口长度,需为 16 的倍数以便对齐注意力块
--temp / --top-p / --top-k 采样温度 0.7、nucleus 采样 0.8、top-k 100,为文档推荐的解码配置
--repeat-penalty 1.05 轻度重复惩罚,抑制多模态场景常见的重复输出
--image xx.jpg -p "..." 单轮模式:指定图片与提示词,回答一次后退出
不带 --image/-p 进入交互式对话模式,可在会话中反复插入图片与文本

两种模式的选择建议:批量验证、脚本化调用用单轮模式;多轮图文交替问答(如"先描述图片、再基于描述追问")用对话模式,后者也便于体验 Qwen3.5 混合注意力下的长上下文表现。

六、相关源码与文档索引

便于继续深入仓库的入口:

需要注意的适用前提:检查点必须是标准 transformers v5.7.0+ 架构(无 trust_remote_code),downsample_mode 仅支持 4x/16xn_merge 推理端只接受 2 或 4;若上游检查点结构发生变化,需以仓库内转换代码的断言与错误提示为准进行核对。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341