MiniCPM-V 1.0 技术解析与端侧部署实战:基于 Perceiver Resampler 的 64 Token 高效多模态大模型
【免费下载链接】MiniCPM-VA Pocket-Sized MLLM for Ultra-Efficient Image and Video Understanding on Your Phone项目地址: https://gitcode.com/GitHub_Trending/mi/MiniCPM-V
MiniCPM-V 1.0 是 MiniCPM-V 系列中面向端侧部署的高效视觉语言模型版本(仓库归档于 2024-05-19,对应文档见 docs/minicpm_v1.md),它由 SigLIP-400M 视觉编码器与 MiniCPM-2.4B 语言模型通过 perceiver resampler 连接而成,将整张图片的视觉表示压缩为仅 64 个 token。本文将以该文档为核心,结合当前仓库源码(chat.py、omnilmm/model/resampler.py 等)逐层拆解其架构设计、评测表现、环境安装、多轮对话推理、Mac(MPS)与手机端部署方法,帮助你完整掌握这一「口袋级」多模态模型的原理与实战用法。
模型总览:SigLIP-400M + MiniCPM-2.4B + Perceiver Resampler
MiniCPM-V 1.0 采用经典的「视觉编码器 + 连接器 + 语言模型」三段式架构:
- 视觉编码器:SigLIP-400M,负责将输入图片编码为视觉特征序列;
- 语言模型:MiniCPM-2.4B,作为多模态对话的推理底座;
- 连接器:perceiver resampler,将视觉特征压缩为固定数量的 query 向量后送入语言模型。
当前仓库的omnilmm/子模块中保留了同源的 perceiver resampler 实现(omnilmm/model/resampler.py),其核心定义如下:
class Resampler(nn.Module): """ A 2D perceiver-resampler network with one cross attention layers by (grid_size**2) learnable queries and 2d sincos pos_emb Outputs: A tensor with the shape of (grid_size**2, embed_dim) """从实现可以看到 resampler 的工作机制:通过grid_size**2个可学习的 query 与视觉特征做单层交叉注意力(cross attention),最终输出形状为(grid_size**2, embed_dim)的固定长度特征。其中 query 使用 2D sincos 位置编码注入空间信息(get_2d_sincos_pos_embed),并通过kv_proj将视觉特征维度对齐到语言模型的隐藏维度。在 omnilmm/model/omnilmm.py 的create_vision_module中,resampler 的 query 数量由配置项num_query决定:
resampler = Resampler( grid_size=int(math.sqrt(config.num_query)), embed_dim=embed_dim, num_heads=embed_dim // 128, kv_dim=vision_tower.embed_dim, )对于 MiniCPM-V 1.0,num_query为 64(即 8×8 网格),这正是文档中所说「将图片压缩为 64 个视觉 token」的由来。
核心特性
⚡️ 高效率:64 Token 带来的推理开销优势
在视觉编码阶段,MiniCPM-V 1.0 通过 perceiver resampler 将图像表示压缩为64 个 token,这一数量显著低于基于 MLP 架构的其他多模态大模型(通常超过 512 个 token)。更少的视觉 token 意味着:
- 语言模型自回归解码时的 KV-Cache 与注意力计算开销更小;
- 整体显存占用更低、推理速度更快;
- 因此可以高效部署在绝大多数 GPU 显卡、个人电脑,甚至手机等端侧设备上。
从当前仓库 chat.py 中可见,MiniCPM-V 1.0 对应的封装类MiniCPMV仅需AutoModel.from_pretrained(model_path, trust_remote_code=True)配合tokenizer即可完成加载与对话,说明其在工程上是按轻量级端侧模型设计的:
class MiniCPMV: def __init__(self, model_path) -> None: self.model = AutoModel.from_pretrained(model_path, trust_remote_code=True).to(dtype=torch.bfloat16) self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) self.model.eval().cuda() def chat(self, input): image = Image.open(io.BytesIO(base64.b64decode(input['image']))).convert('RGB') msgs = json.loads(input['question']) answer, context, _ = self.model.chat( image=image, msgs=msgs, context=None, tokenizer=self.tokenizer, sampling=True, temperature=0.7) return answer🔥 性能:同参数量级中的突出表现
MiniCPM-V 1.0 在 MMMU、MME、MMBench 等多个基准上取得了同规模模型中的领先成绩,超越基于 Phi-2 构建的既有多模态模型,甚至达到或超过9.6B 的 Qwen-VL-Chat的水平。详细评测数据见下一节「评测结果」。
🙌 中英双语多模态交互
MiniCPM-V 1.0 是首个支持中英双语多模态交互的端侧可部署多模态模型。这一能力来源于将多模态能力跨语言泛化的技术(对应 ICLR 2024 spotlight 论文 VisCPM 系列工作),使得同一个端侧模型既能理解中文图片内容、也能以中文/英文进行对话。
评测结果
文档给出了 MiniCPM-V 1.0 与同期主流多模态模型(LLaVA-Phi、MobileVLM、Imp-v1、Qwen-VL-Chat、CogVLM)在 MME、MMB、MMMU、CMMMU 等基准上的对比结果,完整数据如下:
| 模型 | 参数量 | Visual Tokens | MME | MMB dev (en) | MMB dev (zh) | MMMU val | CMMMU val |
|---|---|---|---|---|---|---|---|
| LLaVA-Phi | 3B | 576 | 1335 | 59.8 | - | - | - |
| MobileVLM | 3B | 144 | 1289 | 59.6 | - | - | - |
| Imp-v1 | 3B | 576 | 1434 | 66.5 | - | - | - |
| Qwen-VL-Chat | 9.6B | 256 | 1487 | 60.6 | 56.7 | 35.9 | 30.7 |
| CogVLM | 17.4B | 1225 | 1438 | 63.7 | 53.8 | 32.1 | - |
| MiniCPM-V 1.0 | 3B | 64 | 1452 | 67.9 | 65.3 | 37.2 | 32.1 |
几个值得注意的要点:
- Visual Tokens 只有 64 个,仅为 LLaVA-Phi / Imp-v1(576 个)的约九分之一、MobileVLM(144 个)的不到一半,这正是其端侧高效的关键;
- 在 MMB dev(中英双语)上以 67.9 / 65.3 领先于表中所有对比模型,包括参数量 17.4B 的 CogVLM;
- 在 MMMU val / CMMMU val 上分别达到 37.2 / 32.1,超过 9.6B 的 Qwen-VL-Chat(35.9 / 30.7)。
端侧部署示例
MiniCPM-V 1.0 已在真实端侧设备上完成部署验证:演示视频为一加 9R 手机上的原始屏幕录制,未做任何剪辑处理(见本文开头的两段 GIF:中文「蛇」场景与英文「蘑菇」场景)。这表明该模型可以脱离 GPU 服务器,直接在手机等端侧设备上完成实时图像理解与问答。
环境安装
文档给出的安装流程如下,适用于 Python 3.10 环境:
- 克隆仓库并进入源码目录
git clone https://gitcode.com/GitHub_Trending/mi/MiniCPM-V cd MiniCPM-V- 创建 conda 环境
conda create -n minicpm-v python=3.10 -y conda activate minicpm-v- 安装依赖
pip install -r requirements.txt当前仓库根目录下的 requirements.txt 中已声明模型加载与推理所需的依赖(transformers、torch、accelerate 等);若需复现 chat.py 中的完整推理链路,还依赖omnilmm子模块及其模型实现(omnilmm/model/omnilmm.py)。
注意:本文所依据的 docs/minicpm_v1.md 为 2024-05-19 归档版本,其中的
OmniLMMChat类来自当时的 OmniLMM 代码库;当前仓库 chat.py 已演进为统一的MiniCPMVChat入口(见下文「多轮对话」),调用方式略有差异但核心接口保持一致。
推理实战
Model Zoo
| 模型 | 说明 | 下载 |
|---|---|---|
| MiniCPM-V 1.0 | 面向端侧部署的高效版本 | Hugging Faceopenbmb/MiniCPM-V;ModelScopeOpenBMB/MiniCPM-V |
多轮对话
方式一:按归档文档使用OmniLMMChat(历史接口)
from chat import OmniLMMChat, img2base64 chat_model = OmniLMMChat('openbmb/MiniCPM-V') im_64 = img2base64('./assets/worldmap_ck.jpg') # First round chat msgs = [{"role": "user", "content": "What is interesting about this image?"}] inputs = {"image": im_64, "question": json.dumps(msgs)} answer = chat_model.chat(inputs) print(answer) # Second round chat # pass history context of multi-turn conversation msgs.append({"role": "assistant", "content": answer}) msgs.append({"role": "user", "content": "Where is China in the image"}) inputs = {"image": im_64, "question": json.dumps(msgs)} answer = chat_model.chat(inputs) print(answer)输入图片为仓库内的 assets/worldmap_ck.jpg:
这段代码体现了多轮对话的两个关键约定:
- 图片以 base64 字符串传输:
img2base64读取图片字节并做 Base64 编码(对应 chat.py 中img2base64的实现),服务端再以Image.open(io.BytesIO(base64.b64decode(...)))还原为 PIL 图像; - 对话历史以 JSON 数组传递:
msgs中按{"role": "user"/"assistant", "content": ...}交替追加,第二轮对话时将第一轮的answer追加进历史,从而保持上下文连贯。
方式二:当前仓库的MiniCPMVChat统一入口(推荐)
当前仓库 chat.py 提供的统一入口为MiniCPMVChat,它会根据模型路径自动路由到对应实现:
class MiniCPMVChat: def __init__(self, model_path, multi_gpus=False) -> None: if '12B' in model_path: self.model = OmniLMM12B(model_path) elif 'MiniCPM-Llama3-V' in model_path: self.model = MiniCPMV2_5(model_path) elif 'MiniCPM-V-2_6' in model_path: self.model = MiniCPMV2_6(model_path, multi_gpus) else: self.model = MiniCPMV(model_path) # openbmb/MiniCPM-V 1.0 走这里 def chat(self, input): return self.model.chat(input)即传入openbmb/MiniCPM-V时自动落到MiniCPMV类(chat.py),采用trust_remote_code=True加载模型权重,并在对话时以sampling=True, temperature=0.7采样生成。chat.py底部的__main__分支还给出了一个可直接运行的最小多轮对话示例(模型路径为openbmb/OmniLMM-12B时走 OmniLMM 分支,改传openbmb/MiniCPM-V即可用于 1.0 版本)。
在 Mac 上推理(MPS)
MiniCPM-V 1.0 可以在带 MPS(Apple Silicon 或 AMD GPU)的 Mac 上运行。将以下内容保存为test.py:
# test.py import torch from PIL import Image from transformers import AutoModel, AutoTokenizer model = AutoModel.from_pretrained('openbmb/MiniCPM-V', trust_remote_code=True, torch_dtype=torch.bfloat16) model = model.to(device='mps', dtype=torch.float16) tokenizer = AutoTokenizer.from_pretrained('openbmb/MiniCPM-V', trust_remote_code=True) model.eval() image = Image.open('./assets/worldmap_ck.jpg').convert('RGB') question = 'What is interesting about this image?' msgs = [{'role': 'user', 'content': question}] answer, context, _ = model.chat( image=image, msgs=msgs, context=None, tokenizer=tokenizer, sampling=True ) print(answer)运行命令:
PYTORCH_ENABLE_MPS_FALLBACK=1 python test.py注意两点:
- 加载后需将模型显式迁移到
mps设备并转为float16(model.to(device='mps', dtype=torch.float16)),因为 MPS 对bfloat16支持有限; PYTORCH_ENABLE_MPS_FALLBACK=1用于让 MPS 上不支持的算子自动回退到 CPU 实现,保证推理链路完整可跑通。
手机端部署(Android / Harmony)
MiniCPM-V 1.0 支持部署在Android 与 Harmony(鸿蒙)操作系统的手机上。官方通过 mlc-MiniCPM 项目提供手机端 APK 与部署方案(可结合 docs/minicpm_v2.md 中「MiniCPM-V 1.0:GPU 约 7 GB 显存,最轻量、推理最快」的定位选择部署目标设备)。这是 MiniCPM-V 系列「端侧可部署」定位的最直接体现——模型先做视觉 token 压缩,再由轻量语言模型解码,从而让多模态问答真正运行在随身设备上。
从源码理解 64 Token 压缩机制
视觉编码与 Resampler 的完整数据流
结合 omnilmm/model/omnilmm.py 的get_vision_embedding,可以还原视觉特征进入语言模型前的完整链路:
def get_vision_embedding(self, pixel_values): vision_embedding = vision_tower.forward_features(pixel_values.type(dtype)) if hasattr(vision_tower, 'num_prefix_tokens') and vision_tower.num_prefix_tokens > 0: vision_embedding = vision_embedding[:, vision_tower.num_prefix_tokens:] res = self.resampler(vision_embedding) return res即:图片 → 视觉编码器提取 patch 特征(并去除 cls/prefix token)→ resampler 交叉注意力压缩为grid_size**2个 token → 拼接到文本 token 的 embedding 序列中参与语言模型自回归生成。
Resampler 的内部结构
从 omnilmm/model/resampler.py 可以看到其关键组件:
self.pos_embed = nn.Parameter(torch.from_numpy(get_2d_sincos_pos_embed(embed_dim, grid_size)).float()).requires_grad_(False) self.query = nn.Parameter(torch.zeros(self.num_queries, embed_dim)) trunc_normal_(self.query, std=.02) if kv_dim is not None and kv_dim != embed_dim: self.kv_proj = nn.Linear(kv_dim, embed_dim, bias=False) else: self.kv_proj = nn.Identity() self.attn = nn.MultiheadAttention(embed_dim, num_heads) self.ln_q = norm_layer(embed_dim) self.ln_kv = norm_layer(embed_dim) self.ln_post = norm_layer(embed_dim) self.proj = nn.Parameter((embed_dim ** -0.5) * torch.randn(embed_dim, embed_dim))- 可学习 query:
grid_size**2个可学习向量(1.0 中为 64 个),采用截断正态初始化(std=0.02); - 2D sincos 位置编码:
pos_embed由get_2d_sincos_pos_embed生成并冻结(requires_grad_(False)),为 query 和视觉特征注入二维空间位置信息;输入分辨率变化时通过get_abs_pos做双三次插值适配; - KV 投影:当视觉维度与语言模型隐藏维度不一致时,用
kv_proj线性投影对齐; - 单层多头交叉注意力:
nn.MultiheadAttention(embed_dim, num_heads),其中num_heads = embed_dim // 128; - 输出投影:经
ln_post后再与可学习的proj矩阵相乘得到最终特征。
forward 中的核心交叉注意力计算为:
out = self.attn( self._repeat(q, N) + self.pos_embed.unsqueeze(1), x + pos_embed.unsqueeze(1), x, attn_mask=attn_mask)[0]query 与视觉特征均叠加位置编码后参与注意力,最终输出(num_queries, embed_dim)的定长表示——这就是「64 个视觉 token」的产生源头。
图像预处理与特殊 Token 展开
视觉侧与文本侧的衔接同样关键:
- 图像预处理:推理时采用
build_transform(is_train=False, input_size=config.image_size, std_mode='OPENAI_CLIP')(见 omnilmm/model/utils.py),即 resize 到固定输入尺寸、转 Tensor 并按 OpenAI CLIP 的 mean/std(0.48145466, 0.4578275, 0.40821073/0.26862954, 0.26130258, 0.27577711)归一化; - 特殊 token 展开:chat.py 中的
expand_question_into_multimodal会把问题文本中的<image>占位符替换为im_start + im_patch × image_token_len + im_end序列,其中image_token_len取自模型配置的num_query(即 64)。也就是说,64 个视觉 token 会以 64 个<im_patch>token 的形式占位在输入序列中,模型前向时再被 resampler 输出的真实视觉特征逐位替换(对应 omnilmm/model/omnilmm.py 中get_vllm_embedding的 embedding 拼接逻辑)。
解码参数参考
在 chat.py 的OmniLMM12B.decode中可以看到系列模型常用的采样参数(同源设计可参考):
temperature=0.6, max_new_tokens=1024, do_sample=True, repetition_penalty=1.1, top_k=30, top_p=0.9,而 MiniCPM-V 1.0 的MiniCPMV封装在对话时采用sampling=True, temperature=0.7(chat.py)。实际使用时可根据任务场景调整 temperature(越高越发散)与 repetition_penalty(抑制重复)。
使用注意事项
- 归档版本说明:本文档对应的模型能力与评测数据归档于 2024-05-19;当前仓库已演进到 MiniCPM-V 2.x / 4.x 系列(见 README.md),MiniCPM-V 1.0 仍保留
openbmb/MiniCPM-V权重标识可供加载使用。 - 显存需求:结合 docs/minicpm_v2.md 的 Model Zoo 信息,MiniCPM-V 1.0 为系列中最轻量版本,GPU 推理约需 7 GB 显存,适合多数消费级显卡与个人电脑。
- 精度选择:NVIDIA GPU 上默认以
bfloat16加载(chat.py 中MiniCPMV使用torch.bfloat16);若显卡不支持 bf16(如 V100、T4、RTX 2080),可参考 web_demos/web_demo.py 中的做法切换为fp16;Mac MPS 上则统一使用fp16并配合PYTORCH_ENABLE_MPS_FALLBACK=1。 - 多轮上下文:多轮对话时务必把上一轮的
answer以assistant角色追加回msgs,否则模型无法感知历史对话内容。
【免费下载链接】MiniCPM-VA Pocket-Sized MLLM for Ultra-Efficient Image and Video Understanding on Your Phone项目地址: https://gitcode.com/GitHub_Trending/mi/MiniCPM-V
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考