【2026腾讯犀牛鸟】Penguin-VL ncnn 移植与多平台部署 #6868
AiChiTuDouPian
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Penguin-VL → ncnn 转换与端到端推理 技术报告
1. 项目概述
tencent/Penguin-VL(Qwen3 底座 VLM)nihui/ncnn_llm的 LLM 解码范式)penguin_vl_run.exe --model . --image x.png --prompt "..."直接出文本,无 Python 胶水、无中间文件2. 模型架构与 ncnn 映射
2.1 结构参数
2.2 关键 token id
<image>= 151669;<|image_pad|>= 151655;<|vision_start|>= 151652;<|vision_end|>= 1516532.3 图像与视觉 RoPE
num_patches = 495(= 9×55);pixel_valuesshape =[N, 588](588 = 14×42)merge_size = 1(PyTorch 官方不合并)[64, 64]切两段,第 063 维用 h 流 cos/sin、第 64127 维用 w 流 cos/sin;base=1e6;merge_size=1 退化为行优先位置2.4
offset = x + y*w + z*w*h(w 最内层);numpy 正确视图为[h, w](2D)或[c, h, w](3D)。保存 bin 时save_bin须把 C-order 数组与显式头(w, h, c)配对。axis=0→切 c 维、axis=1→切 h 维、axis=2→切 w 维。视觉 RoPE 用Slice 1=2(沿 w=head_dim=128 切 [64,64])、Concat 0=2(沿 w 拼)。[h=in, w=out]即 Wᵀ 形式存储。(128, 1, N)即可。3. 导出与工程实现
export/export_vision.py等)处理:[1024,1024](8 KV 头)展开为[2048,1024](16 头),wk.reshape(NKV,HD,HID).repeat_interleave(2,0).reshape(NH*HD,HID)。cpp/)实现:BPE 分词器 + 图像预处理 + ncnn 推理 + 贪婪解码,参考HunyuanOCR的自包含形态。模型资产位于
assets/penguin_vl/:decoder.ncnn.{param,bin}、embed.ncnn.{param,bin}、lm_head.ncnn.{param,bin}vision_embed_patch.ncnn.*、vision_encoder.ncnn.*、vision_projector.ncnn.*vocab.txt、merges.txt、model.json4. 关键问题与修复
4.1 自回归生成退化成
'!'(id 0)decoder/lm_head数值均正确(top1 与 PyTorch 第二 token 一致),但自回归每一步都选 id 0 输出!。<|image|>不在vocab.txt(<|image_pad|>在),AddAdditionalSpecialToken("<|image|>")把它追加成 index 151936,使vocab_size = 151937 > logits 实际维数 total = 151936,llm_select_next_token因vocab_size > total早退return 0。llm_select_next_token把vocab_size钳制到total(vocab_size = (cfg.vocab_size>0 && <=total) ? cfg.vocab_size : total)。PY_IDS逐 token 一致。4.2 vision encoder NaN
vision_encoder在 L2 残差突然全 NaN;而 L0(max 685)/L1(max 1283) 有限。1,996,750,848= 309 个权重 blob 按 param 顺序连续存放)。k_proj只是最先暴露的一处,整份 bin 都不可信。export/rewrite_vision_bin.py整体从 safetensors 重生成vision_encoder.ncnn.bin(fp32,k/v 做 GQA 8→16 头展开),不依赖旧损坏数据。所有 309 个 blob 与 safetensors cos=1.0(自校)。旧损坏 bin →vision_encoder.ncnn.bin.corrupted_bak。MatMul name bc tc b1 b2 top→ 权重输入在t[4], t[5](此前误用t[3], t[4]);t[4];RMSNorm 的 dim 在t[6](此前误用t[5])。(idx//7, TYPES[idx%7]),RMSNorm 顺序为[input, q_norm, k_norm, post] × 28 + final。5. PyTorch 一致性验证
本项目对所有可比对环节都做了 ncnn 输出 vs PyTorch 原版真值 的一致性验证,层次从"单个权重 blob"逐级上升到"端到端生成 token"。验证遵循"能精确 Match 就不止步于 cos≈1"的原则:文本生成要求 token 级逐位一致,张量要求 cosine ≥ 0.99 且 nan = 0。
5.1 验证方法论
export/reference_vision.py提供与 PyTorch 原模型逐算子对齐的参考函数(rmsnorm/rotate_half/vision_rope/attention/decoder_layer/vision_forward),作为所有数值比对真值来源;LLM 部分以 PyTorchverify_greedy.py的PY_IDS真值序列为准。cos(x, y) = Σ(x·y) / (‖x‖·‖y‖ + 1e-12)(向量夹角余弦,衡量方向一致性)nan_count(NaN 计数,修复前为整图 NaN,修复后须为 0)max_abs = max|x − y|(绝对误差幅值,用于评估 fp32 累积漂移)5.2 权重层一致性(safetensors ↔ ncnn bin)
修复脚本
export/rewrite_vision_bin.py在写出每个 blob 时即做 self-check,确保与 safetensors 完全一致:q_proj权重(ncnn Wᵀ 布局)vs safetensorsq_proj.weightk_proj权重(含 GQA 8→16 头展开,Wᵀ 布局)vs safetensors 展开后vision_projector.ncnn.bin4 blob vs safetensors5.3 纯文本 decoder 一致性
纯文本路径不依赖视觉,验证 C++ 解码器正确性。
verify_greedy.py的PY_IDS。5.4 视觉编码器逐层一致性(layer-by-layer)
为定位 4.2 的 NaN 根因,做了逐层中间量比对,确认每层图计算与 PyTorch 一致:
verify_layer0.py:复算rmsnorm/rotate_half/SDPA等compare_layers.py:用reference_vision.decoder_layer跑全链,打印每层 max/min/nanverify_layerN.py:先复算 layer N−1 输出再比 layer N 中间量k_proj(blob 241)已含 990 个 NaN → 锁定权重损坏根因5.5 视觉编码器整体输出一致性
compare_enc.py将 ncnnvision_encoder的out0与 PyTorchvision_forward(28 层 + final norm,merge 之前)逐元素比对。5.6 全视觉链路一致性(encoder + merge + projector)
verify_full_vision.py比对 ncnn 整条视觉链路输出与 PyTorch 参考features_ref.npy(encoder 输出经位置 merge 再经 2 层 MLP projector 后的特征)。5.7 端到端图文一致性(C++ 自包含推理 vs 真值语义)
5.8 一致性验证汇总
PY_IDSout0vs PyTorch 28 层 + final normfeatures_ref.npyvision_projector.ncnn.bin4 blob vs safetensors提示词:请描述这张图片
output:这张图片是一段文字,内容如下:
"Compressed and interpretable light-weight model ready for edge deployment."
这段文字描述了一个轻量级、可解释的模型,已经压缩好,准备用于边缘部署。
提示词:请描述这张图片
output:这张图片展示了一个紧张的场景,看起来像是电影或电视剧中的一个片段。画面中,一个穿着深色衣服的男子正与另一个坐在椅子上的男子进行对话。坐在椅子上的男子穿着浅色T恤,表情严肃,似乎在认真倾听或回应对方。背景中可以看到其他几个人,他们似乎在观看或参与这个对话。整个场景的光线较暗,给人一种紧张和严肃的氛围。图片下方有文字:“对不起,我是诺曼·斯佩尔曼”,这表明对话的主角之一是诺曼·斯佩尔曼,而坐在椅子上的男子可能是他的对手或对手方。
6. 模型发布(Hugging Face)和代码仓库
https://huggingface.co/xxzigou/plugin-VL-ncnnhttps://github.com/AiChiTuDouPian/plugin-VL-ncnn*.ncnn.{param,bin}(decoder / embed / lm_head / vision_embed_patch / vision_encoder / vision_projector)vocab.txt、merges.txt、model.json、README.md7. 结论与后续
All reactions