本文档记录 openbmb/MiniCPM-V-4.6(端侧多模态视觉语言模型,image-text-to-text)在华为昇腾 Ascend910(npu:0)上通过 torch_npu 完成的真机适配与验证结果。
SigLIP2-400M 视觉塔(27 层,hidden 1152,patch 14)+ Qwen3.5-0.8B 文本解码器(qwen3_5_text,24 层混合结构:18 层线性注意力 [门控 delta / 短卷积 SSM] + 6 层全注意力,hidden 1024,vocab 248094),支持混合 4x/16x 视觉 token 压缩。1.30B(1300.4M),权重单文件 model.safetensors 约 2.6 GB。MiniCPMV4_6ForConditionalGeneration,model_type=minicpmv4_6。float32 加载。适配要点:
trust_remote_code:仓库不含任何建模 .py,是原生 transformers 权重(由 transformers 5.7.0 序列化)。minicpmv4_6 / qwen3_5_text 两个架构在 pod 自带的 transformers 4.57.6 中不存在。inference.py 会把 transformers>=5.7.0 + huggingface_hub>=1.5 以 --no-deps 私有沙箱(~/.cache/models/MiniCPM-V-4.6/_tf_vendor)装入并仅对本进程 sys.path 前插,不升级共享环境、不触碰 torch/torch_npu。详见 §9。AutoModelForImageTextToText + AutoProcessor,attn_implementation="sdpa"。flash-linear-attention / causal-conv1d)在昇腾上不存在,transformers 自动回落到纯 torch 参考实现,数值等价、可在 NPU 上正确运行(Gate 1 保持 0/0/0)。.to("npu:0") 即可推理,无需改写建模代码。相关获取地址:
| 组件 | 版本 |
|---|---|
CANN | 8.5.1 |
torch | 2.9.0+cpu |
torch-npu | 2.9.0.post1+gitee7ba04 |
transformers | 5.7.0(私有沙箱,见 §9;pod 系统为 4.57.6) |
huggingface_hub | 1.28.0(私有沙箱) |
tokenizers | 0.22.2 |
Python | 3.11.14 |
Ascend910(npu-smi 型号 Ascend910_9362),2 逻辑卡,单卡 HBM 65536 MB,本次用 npu:0。torch_npu(原生 transformers minicpmv4_6)。~/.cache/models/MiniCPM-V-4.6(约 2.6 GB)。image-text-to-text(多模态视觉问答 / 图像描述)。export PATH=/usr/local/python3.11.14/bin:$PATH # 确保用带 torch/torch_npu 的 3.11 解释器
# 前置检查
python -c "import torch, torch_npu; assert torch.npu.is_available(); print('NPU OK', torch.__version__)"
npu-smi info # 确认昇腾卡在位inference.py 首次运行会自动完成三件事,无需手动干预:
transformers==5.7.0 + huggingface_hub>=1.5 以 pip install --no-deps --target <vendor_dir> 装入私有沙箱(默认 ~/.cache/models/MiniCPM-V-4.6/_tf_vendor),torch/torch_npu 不受影响;HF_ENDPOINT=https://hf-mirror.com,Xet CDN 直连)下载权重到 ~/.cache/models/MiniCPM-V-4.6;~/.cache/models/MiniCPM-V-4.6/demo/。如需手动预置沙箱(--no-deps 不拖动 torch):
pip install --no-deps --target ~/.cache/models/MiniCPM-V-4.6/_tf_vendor \
"transformers==5.7.0" "huggingface_hub>=1.5,<2.0"已验证通过的命令(卡锁 + 物理卡固定 + 编译缓存进 /tmp):
flock -w 7200 /tmp/npu_card0.lock bash -c '
export PATH=/usr/local/python3.11.14/bin:$PATH
ASCEND_RT_VISIBLE_DEVICES=0 ASCEND_CACHE_PATH=/tmp/ascend_cache_MiniCPM-V-4.6 \
ASCEND_PROCESS_LOG_PATH=/tmp/ascend_log ASCEND_WORK_PATH=/tmp/ascend_work \
python inference.py --device npu'其他用法:
python inference.py --device npu --skip-perf # 仅门禁 + demo
python inference.py --device npu --image my.jpg --question "..." # 自定义单图问答注意:
ASCEND_CACHE_PATH目录必须存在(脚本 import 时已预建);否则昇腾会以error 500001/HF32报错伪装“目录不存在”问题(见 §9)。
python inference.py --device npu 在昇腾 NPU 上的真实输出(节选,已剔除权重加载进度条与 CANN 告警):
[vendor] transformers 5.7.0 (from .../_tf_vendor/transformers/__init__.py)
[weights] model dir ready: /opt/atomgit/.cache/models/MiniCPM-V-4.6
[data] demo image ready: /opt/atomgit/.cache/models/MiniCPM-V-4.6/demo/cats_image.jpeg
[npu] HF32 disabled for conv + matmul (full fp32)
[npu] using device npu:0 (Ascend910_9362)
[load] processor: MiniCPMV4_6Processor (image_proc=MiniCPMV4_6ImageProcessor, tok=TokenizersBackend)
[load] MiniCPMV4_6ForConditionalGeneration on npu:0, fp32, 1300.4M params
[gate1] missing=0 unexpected=0 mismatched=0
[gate1] PASS -- strict load 0/0/0
[gate2] PASS -- all 26 checked repo config.json keys landed on model.config (incl. hybrid layer_types 18x linear + 6x full_attention, rope_theta 1e7)
[demo] Q: What is in this image? How many animals?
[demo] A: 'The image shows two cats resting on a pink blanket or surface. Both appear to be sleeping or relaxing. One is on the left, stretched out, and the other is on the right, curled up. There are also remote controls visible, but those are not animals.\n\nSo, how many animals? \n✅ There'
[demo] image-keyword('cat', 'cats', 'kitten') hit=True count-keyword('two', '2') hit=True new_tokens=64 distinct_ratio=0.75
[demo] determinism (greedy x2 identical): True
[gate3] image-content keyword hits: pretrained 2/2 vs random 0/2 -> DECISIVE
[gate3] PASS
[hf32] max |logit delta| HF32 on vs off: 1.073503e+00 (runs use HF32 OFF)
[perf] prompt 235 tokens (incl. image tokens) + 48 new tokens, warmup=5, timed rounds=20
[perf] avg=2245.65 ms min=2117.55 max=2789.02 p50=2199.93 p90=2261.02 p95=2527.13 ms
[perf] end-to-end throughput 21.37 tok/s (48 new tokens incl. prefill) peak HBM 5241.8 MB
[done] results written to /tmp/MiniCPM-V-4.6_npu_results.json验证结果:
npu:0 上以 fp32 加载成功,1300.4M 参数,严格加载 0/0/0。deterministic=True)。测试条件:单图 + 定长生成 48 个新 token(min_new_tokens=max_new_tokens=48,贪心),prompt 235 token(含视觉 token),warmup=5、正式 20 轮,每轮前后 torch.npu.synchronize();端到端计时(含视觉编码 prefill + decode)。
| 指标 | 数值 |
|---|---|
avg_ms | 2245.65 ms |
min_ms / max_ms | 2117.55 / 2789.02 ms |
p50_ms / p90_ms / p95_ms | 2199.93 / 2261.02 / 2527.13 ms |
throughput | 21.37 tok/s(端到端,含 prefill) |
peak_hbm_mb | 5241.8 MB |
p50=2199.93 ms / p90=2261.02 ms;p95/max 尖刺来自 CANN 首次算子编译与 ArgSort int64 回落 AiCPU(见 §9),非稳态代表值。本模型为多模态生成式 VLM,无分类标签级 ground-truth,采用“图像内容关键词命中 + 随机初始化对照 + 决定性判定”三证,并结合贪心决定性复现。
| 指标 | 数值 |
|---|---|
| 数据集 | COCO 两猫图(huggingface/cats-image,640x480) |
| 样本 / 问题 | 1 图,"What is in this image? How many animals?" |
| Gate 1(严格加载) | missing/unexpected/mismatched = 0/0/0 |
| Gate 2(config 落地) | 26 个关键 key 全部落到 model.config(含混合 18 线性 + 6 全注意力、rope_theta=1e7) |
| Gate 3(预训练 vs 随机) | 图像内容关键词命中:预训练 2/2 vs 随机 0/2 → 决定性胜出 |
| 决定性 | 贪心解码两次输出逐字符一致 |
seed=1234)对同一图像/问题只产生乱码(如 "agnesনী.YEARbelief俏 fishermen carri entrambi ...",图像关键词与数量关键词均 0 命中,distinct-ratio 1.0);预训练权重则准确描述图像内容并正确数出“两只猫”,双关键词命中 2/2。预训练相对随机初始化决定性胜出,证明加载的是有效预训练权重且在 NPU 上语义正确。


最容易踩的一条:架构不在 4.57.6,需私有沙箱化 transformers 5.7.0(且不能升级共享环境)。
transformers 4.57.6 加载报 KeyError/无法识别 minicpmv4_6;AutoConfig/AutoModel 找不到该架构。minicpmv4_6 in CONFIG_MAPPING: False、qwen3_5_text in CONFIG_MAPPING: False。transformers 5.7.0 序列化(config.json 中 transformers_version: 5.7.0),其 minicpmv4_6 视觉-语言架构与 qwen3_5_text(带线性注意力 SSM 的 Qwen3.5)文本子架构均是 transformers 5.x 才引入的;仓库不带建模 .py,trust_remote_code 无从谈起——必须靠新版 transformers 本体。排除了“缺 remote code”“缺 flash_attn”等误判方向。inference.py 用 pip install --no-deps --target <vendor_dir> "transformers==5.7.0" "huggingface_hub>=1.5,<2.0" 将新版 transformers 装入私有目录,仅对本进程 sys.path.insert(0, vendor_dir);因 --no-deps,torch/torch_npu/numpy 不被改动,共享的 4.57.6 也不受污染。已验证该沙箱下 torch_npu 正常、torch.npu.is_available()=True。其余问题:
error code 500001 伪装成 HF32/ACL 错误:现象是 AclSetCompileopt(ACL_ALLOW_HF32) error 500001 + GEInitialize failed。根因其实是 ASCEND_CACHE_PATH 指向的目录不存在(日志尾部 Directory ... does not exist)。处理:脚本 import 阶段就 os.makedirs(ASCEND_CACHE_PATH, exist_ok=True),并在设置 HF32 开关前先 torch.ones(1).to("npu:0") 触发 lazy-init。The fast path is not available ... Falling back to torch implementation(缺 flash-linear-attention/causal-conv1d)及 kernel [ArgSort] ... running on AiCpu(int64 排序回落 CPU)。这些不改变数值正确性,但抬高 host 侧耗时与尾延迟,故 p95/max 有尖刺;如追求性能可将相关张量转 float32 或补装 CUDA-only 内核(昇腾不适用)。max |logit delta| HF32 on vs off = 1.07。为保证 fp32 正确性对照,运行时通过 torch.npu.conv.allow_hf32=False / torch.npu.matmul.allow_hf32=False 关闭 HF32;若开启 HF32,logits 会有约 1.0 量级偏移,不建议用于精度对照。apply_chat_template 的 downsample_mode 必须同时传给 generate:否则视觉 token 占位数与视觉编码器不一致。本脚本 image 场景用 downsample_mode="16x"、max_slice_nums=9。#NPU #Ascend #torch_npu #image-text-to-text #MiniCPM-V #multimodal