MiniCPM-V 4.7
MiniCPM-V Multimodal Series
从一张图片和一个问题开始,跑通首次对话,再尝试多图比较、视频理解、OCR、PDF 解析与视觉定位。选择与你任务相符的示例,逐步搭建自己的多模态应用。
MiniCPM-V 4.7 已在 transformers >= 5.18.0 中注册为独立架构 MiniCPMV4_7ForConditionalGeneration,标准 HuggingFace Processor + model.generate 流程即可使用,无需 trust_remote_code。
MiniCPM-V 4.7 有两个尺寸,改 model_path 即可切换:
model_path | 尺寸 |
|---|---|
openbmb/MiniCPM-V-4.7-1B | dense 1B(端侧) |
openbmb/MiniCPM-V-4.7-35B-A3B | MoE 35B-A3B(高吞吐) |
安装
pip install "transformers[torch]>=5.18.0" torchvision torchcodec
torchcodec(视频解码)对 CUDA 版本较敏感。若遇到 RuntimeError: Could not load libtorchcodec,可改用 PyAV:
pip install "transformers[torch]>=5.18.0" torchvision av
或在装 torch 时指定匹配的 CUDA(如 CUDA 12.8):
pip install "transformers>=5.18.0" torchvision torchcodec --index-url https://download.pytorch.org/whl/cu128
初始化模型
下面各示例共用这段初始化代码。
import torch
from PIL import Image
from transformers import AutoProcessor, AutoModelForImageTextToText
model_path = "openbmb/MiniCPM-V-4.7-1B" # 或 "openbmb/MiniCPM-V-4.7-35B-A3B"
processor = AutoProcessor.from_pretrained(model_path)
model = AutoModelForImageTextToText.from_pretrained(
model_path,
dtype="auto",
device_map="auto",
attn_implementation="sdpa", # 或 "flash_attention_2"
).eval()
思考模式
MiniCPM-V 4.7 每个尺寸只有一个 checkpoint:对话模板默认带 <think> 块,除非传入 chat_template_kwargs={"enable_thinking": False}。
- 重 OCR / 推理类任务可保留思考,模型会先审视版面再作答。
- 普通问答建议关闭,以获得更快的直答。
单图问答
image = Image.open("./assets/single.png").convert("RGB")
# 第一轮
question = "图中是什么地貌?"
messages = [{
"role": "user",
"content": [
{"type": "image", "image": image},
{"type": "text", "text": question},
],
}]
inputs = processor.apply_chat_template(
messages,
add_generation_prompt=True,
tokenize=True,
return_dict=True,
return_tensors="pt",
chat_template_kwargs={"enable_thinking": False},
).to(model.device)
out_ids = model.generate(**inputs, max_new_tokens=512)
answer = processor.decode(
out_ids[0][inputs["input_ids"].shape[-1]:],
skip_special_tokens=True,
)
print(answer)
多轮对话
把上一轮回答追加回 messages,即可接着提问:
messages.append({"role": "assistant", "content": [{"type": "text", "text": answer}]})
messages.append({
"role": "user",
"content": [{"type": "text", "text": "去这里旅行需要注意什么?"}],
})
inputs = processor.apply_chat_template(
messages,
add_generation_prompt=True,
tokenize=True,
return_dict=True,
return_tensors="pt",
chat_template_kwargs={"enable_thinking": False},
).to(model.device)
out_ids = model.generate(**inputs, max_new_tokens=512)
answer = processor.decode(
out_ids[0][inputs["input_ids"].shape[-1]:],
skip_special_tokens=True,
)
print(answer)
多图问答
在 content 里按顺序放多个 image 条目即可:
image1 = Image.open("assets/multi1.png").convert("RGB")
image2 = Image.open("assets/multi2.png").convert("RGB")
messages = [{
"role": "user",
"content": [
{"type": "image", "image": image1},
{"type": "image", "image": image2},
{"type": "text", "text": "比较这两张图片,说明它们之间的差异。"},
],
}]
inputs = processor.apply_chat_template(
messages,
add_generation_prompt=True,
tokenize=True,
return_dict=True,
return_tensors="pt",
chat_template_kwargs={"enable_thinking": False},
).to(model.device)
out_ids = model.generate(**inputs, max_new_tokens=512)
answer = processor.decode(
out_ids[0][inputs["input_ids"].shape[-1]:],
skip_special_tokens=True,
)
print(answer)
视频理解
视频走标准 message 流程:在 content 里放一个 {"type": "video", ...} 条目,由 processor 自动解码 / 采样帧。帧预算与采样由 processor 参数控制 —— 无需手动解帧,也不需要特殊的视频后端。
视频场景建议用 Flash Attention 2,加速并省显存:初始化时传 attn_implementation="flash_attention_2" 与 torch_dtype=torch.bfloat16。
messages = [{
"role": "user",
"content": [
{"type": "video", "url": "https://huggingface.co/datasets/openbmb/DemoCase/resolve/main/football.mp4"},
{"type": "text", "text": "详细描述这段视频。按时间线展开,重点关注画面文字、界面变化、主要动作与场景切换。"},
],
}]
downsample_mode = "16x" # 需要更精细的细节时用 "4x"
inputs = processor.apply_chat_template(
messages, tokenize=True, add_generation_prompt=True,
return_dict=True, return_tensors="pt",
downsample_mode=downsample_mode,
max_num_frames=128,
stack_frames=1,
max_slice_nums=1,
use_image_id=False,
).to(model.device)
generated_ids = model.generate(**inputs, downsample_mode=downsample_mode, max_new_tokens=2048)
generated_ids_trimmed = [
out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_text = processor.batch_decode(
generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
)
print(output_text[0])
使用本地文件时直接传路径:
{"type": "video", "url": "./assets/badminton.mp4"}
高级参数
传给 apply_chat_template(标注了 generate 的也需传给 generate