8,027
社区成员
发帖
与我相关
我的任务
分享实测日期:2026 年 11 月 10 日。
把一个模型在开发板上跑起来之后,我更想知道:换一条推理路径,输出会不会变化?如果主机和开发板都识别出了同一种动物,是否就可以认为部署正确?
这次,我用 Qualcomm AI Hub Models 的同一个 MobileNet-v2 量化产物,以及完全相同的一份图片输入,比较了三条路径:主机 CPU reference、犀牛派 X1 本地 CPU、X1 QNN external delegate。
三路都把图片识别为萨摩耶,top-5 顺序也相同,但 1000 类的完整输出存在数值差异。继续加入板端 CPU 对照后,一个重要观察是:X1 本地 CPU 与主机 reference 本身也有差异,当前证据不能把全部主机/QNN 差异归因于 QNN。
模型取自 AI Hub Models v0.65.0 对应的 MobileNet-v2 TFLite w8a8 预导出包。官方发布清单同时列出 float、w8a8 等产物;这次使用预导出文件,没有为本轮本地对照提交新的云端编译作业。固定版本的官方发布清单
| 项目 | 本次固定内容 |
|---|---|
| 模型 | MobileNet-v2,TFLite w8a8 |
| 输入 | image_tensor,UINT8 NHWC [1,224,224,3] |
| 输出 | class_logits,UINT8 [1,1000] |
| 图片 | 官方分类器测试样例;预期类别 258,Samoyed |
| 主机路径 | x86_64,LiteRT 2.3.0,BUILTIN_REF,单线程 |
| X1 环境 | aarch64,Ubuntu 22.04.2,既有 vendor TFLite 与 QNN 库 |
| 板端工具 | 同一个 benchmark_model,普通账号、单线程 |
| 板端调用条件 | 每条路径零预热、一次正式调用,开启图状态输出与 op profiling |
为了确保比较过程中没有换模型或换输入,我记录了文件哈希:
model_sha256:
ba4c39fdda9a3a5b0a6f69a6c7542aba57fbabfced91fb37086298461fdeaf06
input_uint8_sha256:
c9b68ebb45a6d0cda70e0834ae433222fe6021085d138738c07b74b665623e85
模型、输入和板端运行库都在调用前后核对身份。输入共 150,528 字节,主机量化模型、X1 CPU 和 X1 QNN 使用的内容逐字节一致。
此前的合成输入可以检查接口和数值路径,但没有对应的正确类别。这一轮从官方测试代码出发,找到测试图片和 MobileNet 的参考概率文件。分类器测试模板明确给出预期类别 258,标签为 Samoyed。官方分类器测试模板
图像先按 RGB 解码,短边缩放到 256,再中心裁剪到 224,转换成 [0,1] 范围的浮点数据。主机侧固定 Pillow 10.2.0、NumPy 1.26.4,采用 PIL 双线性缩放,记录裁剪位置;同一次预处理同时生成浮点与量化输入。官方图像处理实现
这里有两处容易混淆。
第一,参考文件保存的是概率,模型输出是 logits,需要先做 softmax 再比较。第二,模型的归一化位置需要查对应包装器,外部不能再重复执行 ImageNet mean/std 归一化。MobileNet 的源实现和权重加载路径也要按固定版本核对,不能直接用任意 torchvision 默认权重代替。固定版本的 MobileNet 实现
我先运行同版本的官方 float TFLite 候选:萨摩耶概率为 0.74959024,官方源模型参考为 0.74959123,1000 项概率的最大绝对差约 9.88×10⁻⁷。
这为当前图片的解码、预处理和浮点参考建立了一个有用基线。它仍然只是单张图片的对照,没有证明 float 与 w8a8 两个产物的完整权重构建关系,也没有评估整个数据集。
量化输入不能只看 dtype。这个产物的输入 scale 为 0.003918204456567764,zero-point 为 0。本次按下式生成量化输入:
q_input = clip(round((rgb / 255) / input_scale + input_zero_point), 0, 255)
输入量化使用 float64 nearest/ties-to-even 舍入后裁剪,并冻结生成结果。输出的参数则不同:scale 为 0.13067921996116638,zero-point 为 76。
logit = (q_output - 76) * 0.13067921996116638
probability = softmax(logits)
实际输出反量化按 float32 计算,三路统一使用稳定 float64 softmax 做后处理。这样可以比较同一种数值表示,并保留最初的 UINT8 字节。
主机量化模型的萨摩耶概率为 0.66138804,比浮点结果低约 8.82 个百分点。为了观察输入量化表示的影响,我又让相同浮点模型读取量化输入反量化后的实数,得到 0.75023698。
只改变输入时,浮点模型的该类概率增加约 0.065 个百分点,变化较小且方向相反。因此,在这张图片上,输入量化重构无法解释量化模型概率下降的主要部分。其余产物、转换、量化及数值 kernel 差异仍需分别研究,不能把这次观察直接称为某种量化算法的误差证明。
在有了主机量化输出后,我将完全相同的模型和输入放到 X1,先请求 QNN HTP 路径,再执行板端 builtin CPU 路径。
| 路径 | 萨摩耶概率 | 唯一 top-1 |
|---|---|---|
| 主机 CPU reference | 66.1388% | 258 / Samoyed |
| X1 本地 CPU | 62.1325% | 258 / Samoyed |
| X1 QNN 请求路径 | 67.0617% | 258 / Samoyed |
三路 top-5 顺序均为:Samoyed、Arctic fox、Pomeranian、keeshond、white wolf,对应索引 258,279,259,261,270。
完整输出的比较,比只看 top-1 更能说明问题:
| 路径对比 | 不同 UINT8 项数 | 最大绝对量化差 | 平均绝对量化差 | 最大概率绝对差 |
|---|---|---|---|---|
| X1 CPU / 主机 reference | 713/1000 | 4 | 1.023 | 0.04006330 |
| X1 CPU / X1 QNN | 804/1000 | 7 | 1.593 | 0.06055563 |
| X1 QNN / 主机 reference | 812/1000 | 7 | 1.504 | 0.04946904 |
本轮在运行前确定了这些比较指标,但没有制定量化数值通过容差。因此,表格记录的是差异,不能事后挑一条误差线宣布通过。三路类别相符,只能说明这个样例的预期标签得到满足;萨摩耶概率更高,也不代表那条路径的总体准确率更高。
两条板端路径使用相同的工具、模型、输入、库搜索设置和 profiling 配置,主要变化是外部 delegate 的选择。
QNN 路径设置既有 external delegate,配置请求 HTP。日志显示原始 108 个 TFLite 节点进入一个 TfLiteQnnDelegate 分区,执行计划为 [108],TFLite 层没有剩余的独立 builtin 节点。
CPU 路径显式清空 external_delegate_path 和 external_delegate_options,设置 use_gpu=false、use_xnnpack=false。实际执行计划为 [0,1,...,107]:108 个 builtin 节点直接执行,没有 delegate 分区。pre/post 图状态一致,108 行 profile 各调用一次。
两轮日志都记录零预热、一次正式调用,进程正常退出,模型和库文件哈希保持一致。
图层委派证据说明 TFLite 把哪些节点交给了 QNN 分区。当前没有 QNN 内部逐算子的实际设备事件,因而不能进一步声称分区内部全部计算均在 HTP 上完成,或者完全没有 CPU 辅助工作。
如果只比较主机和 X1 QNN,可能很容易把差异都归到加速后端。加入 X1 CPU 后可以看到:关闭外部 delegate 时,板端输出仍与主机 reference 不同。
主机与板端在架构、运行库版本和 kernel 实现上都有差别。X1 CPU 与 QNN 两路也会改变数值执行实现,并且采集时间不同。这次没有重复性样本,所以还不能把差异唯一归因于某个 kernel、版本或设备。
三方对照提供了更清晰的研究范围:主机与板端 CPU 的差异需要单独看,同板卡 CPU 与 QNN 的差异也需要单独看。此前 float/w8a8 两个产物之间的差异,则属于另一组问题。
板端 CPU 的正式调用记录为 31.879 ms,QNN 路径为 1.370 ms。它们是开启图状态和 profiling、零预热、各一次的诊断观察值,没有形成稳定的性能统计,也不包含完整图像处理和模型加载过程。
本轮没有据此计算稳定加速比、P95 延迟或端到端帧率。若后续要回答这些性能问题,需要另外固定预热、重复次数、计时范围和环境条件。
从官方模型和有预期类别的图片出发,我保存了预处理参数、模型与输入身份、原始输出、调用计数、执行图和逐类差异。三方结果又通过独立反量化、softmax 与差异复算核对。
这已经构成一次有明确边界的 X1 部署实测:模型能够在指定环境下调用,本样例的类别符合预期,执行路径有记录,数值差异也被完整保留下来。多样本准确率、量化容差、重复稳定性和稳态性能仍需要独立验证;机器人策略模型和 Runtime 后端接入继续按各自的开发与验收安排推进。
本文表格为作者本次实测摘录,官方链接用于核对模型来源和上游测试实现。本次完整实验归档尚未公开提供下载。