首页
/ llama.cpp 多模态实战:MiniCPM-V 4.5 从 PyTorch 模型转换到 GGUF 推理全流程

llama.cpp 多模态实战:MiniCPM-V 4.5 从 PyTorch 模型转换到 GGUF 推理全流程

2026-09-04 15:54:30作者:蔡丛锟

本文基于仓库文档 MiniCPM-V 4.5 指南 展开,完整讲解在 llama.cpp 中部署 openbmb 的 MiniCPM-V 4.5 视觉语言模型的全流程:从编译 llama.cpp,到使用 surgery 与图像编码器转换脚本拆分 PyTorch 权重、生成 mmproj 文件,再到用 llama-quantize 量化与 llama-mtmd-cli 进行单轮问答和会话式推理。读完本文,你可以独立完成 MiniCPM-V 4.5 的本地多模态推理部署,并理解其 resampler 投影器在 ggml 计算图中的实现原理。

一、背景:llama.cpp 的多模态体系与 MiniCPM-V 4.5 在其中的位置

llama.cpp 的多模态能力由 libmtmd 子库提供。根据 tools/mtmd/README.md 的说明,项目早期为 LLaVA 等模型单独创建了 llava-cli,随着 Qwen2-VL、MiniCPM-V 等支持不断增多,出现了 minicpmv-cli 等模型专属二进制;后来 libmtmd 取代了 llava.cpp,并由统一的 mtmd-cli(构建产物为 llama-mtmd-cli)整合所有多模态模型交互。该 README 同时强调,多模态支持仍处于高强度开发中,可能出现破坏性变更。

运行多模态模型通常需要两个 GGUF 文件:

  1. 语言模型文件-m 参数指定),即标准的 LLM GGUF;
  2. 多模态投影器文件--mmproj 参数指定),负责图像编码与投影,即 mmproj-model-*.gguf

MiniCPM-V 系列属于 README 中列出的 "legacy" 模型:较新的模型(如 MiniCPM-V 4.6)可以直接用 convert_hf_to_gguf.py --mmproj 一步转换,而 MiniCPM-V 4.5 需要走 tools/mtmd/legacy-models/ 目录下的专用转换脚本,这正是本文的核心内容。

二、准备工作:编译 llama.cpp

MiniCPM-V 4.5 指南要求先将 openbmb 的 MiniCPM-V-4_5 PyTorch 模型下载到 MiniCPM-V-4_5 文件夹(在 Hugging Face 的 openbmb 组织页可获取;转换后的 gguf 版本同样由官方提供,可跳过第三节的手动转换)。

llama.cpp 本体使用 CMake 构建:

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

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

构建完成后,后文用到的 llama-mtmd-clillama-quantize 两个可执行文件均位于 build/bin/ 目录。若编译选项与实际环境有差异,应以仓库官方构建文档为准。

三、模型转换:PyTorch 权重到 GGUF 的三步流程

原文档给出的完整转换命令如下,建议按顺序执行(也可直接下载官方已转换好的 gguf 文件):

python ./tools/mtmd/legacy-models/minicpmv-surgery.py -m ../MiniCPM-V-4_5
python ./tools/mtmd/legacy-models/minicpmv-convert-image-encoder-to-gguf.py -m ../MiniCPM-V-4_5 --minicpmv-projector ../MiniCPM-V-4_5/minicpmv.projector --output-dir ../MiniCPM-V-4_5/ --minicpmv_version 6
python ./convert_hf_to_gguf.py ../MiniCPM-V-4_5/model

# quantize int4 version
./build/bin/llama-quantize ../MiniCPM-V-4_5/model/ggml-model-f16.gguf ../MiniCPM-V-4_5/model/ggml-model-Q4_K_M.gguf Q4_K_M

下面逐步拆解每个脚本做了什么。

3.1 第一步:minicpmv-surgery.py 拆分多模态权重

surgery 脚本trust_remote_code=True 加载完整的 MiniCPM-V PyTorch 模型后,做三件关键的事:

  1. 抽取投影器权重:把 checkpoint 中所有以 resampler 开头的张量(即图像到文本空间的投影器)另存为 {模型目录}/minicpmv.projector。若模型配置中存在 scale_emb(MiniCPM 系列的嵌入缩放因子),resampler.proj 会先除以该值再保存,以补偿 LLM 侧嵌入缩放带来的量纲差异——因为转换后 LLM 会独立加载,投影器必须与之匹配。
  2. 抽取视觉编码器权重:把 vpm 开头的张量(SigLIP 视觉编码器,去掉 vpm. 前缀)存为 {模型目录}/minicpmv.clip;同时清空 added_tokens.json,去掉多模态新增 token,使 LLM 部分可以被当作常规模型转换。
  3. 导出纯语言模型:重写 config.jsonauto_map,把 AutoConfig/AutoModelForCausalLM 等指向 configuration_minicpm.MiniCPMConfigmodeling_minicpm.MiniCPMForCausalLM,然后把 model.llm 与对应的 tokenizer 一起保存到 {模型目录}/model 子目录。

这一步结束后,MiniCPM-V-4_5 目录下就有了三类产物:minicpmv.projector(resampler 权重)、minicpmv.clip(视觉编码器权重)和 model/(纯文本 LLM)。

3.2 第二步:图像编码器转成 mmproj GGUF

minicpmv-convert-image-encoder-to-gguf.py 负责把视觉编码器与投影器打包成一个 mmproj-model-f16.gguf。它的关键参数:

参数 说明
-m / --model-dir 模型目录(必填),脚本从中读取 minicpmv.clipconfig.json
--minicpmv-projector 上一步生成的 minicpmv.projector 文件路径;若不指定但目录下存在该文件则自动采用
-o / --output-dir GGUF 输出目录,默认为模型目录
--minicpmv_version 架构版本号。脚本帮助文本给出的映射为:MiniCPM-V-2 用 1、MiniCPM-V-2.5 用 2、MiniCPM-V-2.6 用 3、MiniCPM-o-2.6 用 4、MiniCPM-V 4.0 用 5、MiniCPM-o-4.0 用 6、MiniCPM-o-4.5 用 100045
--use-f32 权重以 f32 保存(默认 f16)

关于版本号的取值要注意:MiniCPM-V 4.5 指南明确要求传 --minicpmv_version 6。这与脚本帮助文本中"6 对应 MiniCPM-o-4.0"的描述不完全一致;从 C++ 侧运行时看,版本号 6 实际被识别为 MiniCPM-V 4.5——clip.cpp 中 resampler 输出 token 数的回退逻辑 明确注释 minicpmv_version == 6 // MiniCPM-V 4.5(对应 n_patches = 64),而 100045 才标注为 MiniCPM-o 4.5。因此 MiniCPM-V 4.5 以 6 为准,不要照抄帮助文本的旧映射。

指定 --minicpmv-projector 后,脚本会在输出的 GGUF 元数据中写入:

  • clip.has_minicpmv_projector = trueclip.projector_type = "resampler"
  • clip.minicpmv_version(即上面传入的版本号);
  • clip.minicpmv_query_num(来自 config.jsonquery_num,resampler 的可学习查询向量数量);
  • 视觉编码器的 image_sizepatch_size、hidden/ffn 维度、attention 头数、block 数,以及图像归一化的 image_mean / image_std(默认各为 0.5)。

同时,resampler 权重会经历一次改名与拆分(见脚本中的 _replace_name_resampler):resampler.attn.in_proj_* 被拆为独立的 q/k/v 权重,resampler.proj 被转置并补上一份 70×70 网格的二维正弦位置嵌入 pos_embed_k。这些改名后的张量名(如 resampler.queryresampler.kv.weight)与 C++ 侧加载时查找的键名 TN_MINICPMV_* 宏 一一对应。

3.3 第三步:LLM 转 GGUF 并量化

convert_hf_to_gguf.py 消费 3.1 步导出的 model/ 目录(标准 MiniCPM 文本模型),生成 ggml-model-f16.gguf;随后 llama-quantize 将其压到 4-bit 的 Q4_K_M,得到 ggml-model-Q4_K_M.gguf。量化只作用于语言模型,mmproj 保持 f16 不动——这也是官方推理命令中 LLM 与 mmproj 可以分别选不同精度的原因。

四、推理:llama-mtmd-cli 单轮与对话模式

转换完成后,在 Linux 或 Mac 上即可运行(原文档给出的两条命令,可直接复制):

# 单轮问答模式:指定一张图片与提示词
./build/bin/llama-mtmd-cli -m ../MiniCPM-V-4_5/model/ggml-model-f16.gguf \
  --mmproj ../MiniCPM-V-4_5/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?"

# 会话模式:交互式多轮对话
./build/bin/llama-mtmd-cli -m ../MiniCPM-V-4_5/model/ggml-model-Q4_K_M.gguf \
  --mmproj ../MiniCPM-V-4_5/mmproj-model-f16.gguf

各参数含义:

参数 说明
-m 语言模型 GGUF(f16 或量化后的 Q4_K_M 均可)
--mmproj 图像编码器/投影器 GGUF
-c 4096 上下文长度 4096 token。多模态输入会消耗大量 token(见下文切片机制),上下文偏小时图片可能放不进去
--temp 0.7 / --top-p 0.8 / --top-k 100 官方推荐的采样参数组合
--repeat-penalty 1.05 轻微重复惩罚,抑制视觉问答中的复读
--image xx.jpg 要描述的图片,替换为你的图片路径
-p "..." 单轮模式的用户提示词;会话模式下省略 -p 即可进入交互界面

图片如何在 token 流中表达

mtmd.cpp 的 init_vision() 初始化逻辑 可以看到,MiniCPM-V 4.5(minicpmv_version = 6)与 2.6/4.0 使用同一套切片模板 MTMD_SLICE_TMPL_MINICPMV_2_6,图像 token 流被组织为:

<image> (overview) </image><slice> (slice) </slice><slice> (slice) </slice>\n ...

即先注入一张低分辨率"总览图"的嵌入,再依次注入多张高分辨率"切片"的嵌入。图像预处理使用 mtmd_image_preprocessor_llava_uhd(与 LLaVA-UHD 一致的超高清多尺度预处理),运行时从 mmproj 元数据中的 image_mean/image_std 读取归一化参数,从 clip.minicpmv_query_num 读取 resampler 查询数,缺失时按版本回退——clip.cpp 中的回退表 显示版本 6(MiniCPM-V 4.5)对应 64 个查询向量,版本 2 为 96 个。这解释了为什么 -c 4096 是官方命令的稳妥取值:总览图 96→64 加上全部切片各 64 个投影 token,总消耗与切片数线性相关。

五、源码纵深:resampler 投影器的 ggml 计算图

MiniCPM-V 的"投影器"不是简单 MLP,而是一个 cross-attention resampler(类似 Perceiver Resampler)。其完整实现位于 clip_graph_minicpmv::build(),计算流程为:

  1. ViT 编码:图像 patch 经 SigLIP 视觉编码器(27 层)得到 patch 级嵌入序列,ViT 自身使用可学习位置嵌入(通过 positions 索引表选取);
  2. 正弦位置编码:resampler 对 K 侧追加二维正弦位置嵌入——用基频向量 omega 与归一化后的网格坐标 pos_h/pos_w 做外积,再分别 sin/cos 拼接得到 x、y 两个分量后沿特征维拼接,最后 k = v + pos_embed,使投影器感知图像的空间结构;
  3. 交叉注意力:64 个可学习查询向量(resampler.query,数量由 clip.minicpmv_query_num 控制,回退值见 L75-L101)与 ViT 输出做 cross-attention,头维固定 d_head = 128,注意力缩放为 1/sqrt(128);输出后经 resampler.attn.out 投影;
  4. 后处理resampler.ln_post LayerNorm,再经 resampler.proj 线性层投影到 LLM 嵌入维度,产出的 64 个向量即填入 token 流中 <image>…</image> / <slice>…</slice> 占位位置的视觉嵌入。

这套结构也解释了转换脚本为什么要对 resampler.* 权重做精细的改名与拆分:PyTorch 中 nn.MultiheadAttention 的合并权重 in_proj_weight 必须拆成独立的 Q/K/V 才能匹配 ggml 的 build_attn 算子布局。

六、实操注意事项与排错要点

  • 版本号是正确性的关键:MiniCPM-V 4.5 必须传 --minicpmv_version 6。若误传 5(MiniCPM-V 4.0)或 100045(MiniCPM-o 4.5),mtmd.cpp 虽然对这几者共用同一切片模板不会直接报错,但 mmproj 元数据中的 clip.minicpmv_version 会影响运行时行为,应避免混用不同版本的 mmproj 与 LLM。
  • 先 surgery 再转换minicpmv-convert-image-encoder-to-gguf.py 依赖 surgery 产物 minicpmv.clip(视觉编码器)与 minicpmv.projector(resampler),跳过第一步会直接失败。
  • mmproj 文件名约定:指定了 --minicpmv-projector 时输出前缀为 mmproj-,默认精度 f16,故标准产物名为 mmproj-model-f16.gguf--use-f32 才生成 mmproj-model-f32.gguf
  • 量化策略:官方示例对 LLM 提供 f16 与 Q4_K_M 两种推理路径,mmproj 恒为 f16;转换脚本注释也说明卷积类权重(ViT 的 patch embedding)在 GGML 中始终按 f16 保存,--use-f32 不影响这部分。
  • 官方预转换权重:如不想本地转换,openbmb 已发布 MiniCPM-V-4_5 对应的 gguf 版本(含 LLM 与 mmproj),下载后只需按第四节的命令推理即可。
  • 相关文档:同系列其他版本的部署方式见 MiniCPM-V 4.0 指南MiniCPM-V 4.6 指南,多模态总入口为 docs/multimodal.mdlibmtmd 的设计背景与 mmproj 概念详见 tools/mtmd/README.md

七、小结

MiniCPM-V 4.5 在 llama.cpp 中的落地路径是:minicpmv-surgery.py 拆分 PyTorch 权重(resampler 投影器、SigLIP 视觉编码器、纯文本 LLM 三件套)→ 专用脚本把图像侧打包为带 clip.minicpmv_version=6 元数据的 mmproj GGUF → convert_hf_to_gguf.pyllama-quantize 处理语言侧 → llama-mtmd-cli 通过"总览图 + 多切片"的 token 模板与 64 查询向量的 resampler 完成图像理解推理。整个流程全部由仓库内脚本驱动,理解各步骤的产物与版本号语义后,即可自行复现或排查部署问题。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384