这是 AI 实操第 9 天部署篇:把 day09 训练好的语音指令分类模型,通过 ONNX → SNPE DLC → INT8 量化,最终跑到真机上。记录环境、踩坑过程和实测结果。 本文首发地址 https://h89.cn/archives/676.html 项目地址 https://gitee.com/chenjim/cockpit-ai-from-zero

语音模型在车机芯片上跑,不能只停在训练准确率和 PC 延迟。必须实际走通转换、量化、adb 部署、真机推理,看看模型能不能在目标设备上正常出结果,以及 INT8 量化后精度损失多少。

本文环境:

项目
开发机 x86_64 Linux
SNPE / Qairt SDK 2.48.0.260626120635
手机型号 Xiaomi 24122RKC7C(adb shell getprop ro.product.model
手机 SoC SM8750(骁龙 8 Elite,adb shell getprop ro.soc.model
手机平台 sunadb shell getprop ro.board.platform
连接方式 USB adb

模型来自同一目录的 blog-speech-commands-training.md:Speech Commands v2 35 类分类,残差 CNN + Log-Mel,测试准确率 96.22%。

车载语音指令模型真机部署流程

踩坑记录:脚本里藏了多少“想当然”

convert.sh 最早基本是照着 day07 的 MNIST 部署片段改的,没真正在语音模型 + 当前 SNPE 版本上跑过。一上真机问题全冒出来了。

坑 1:SCRIPT_DIR 被 snpe_setup.sh 覆盖了

convert.sh 开头用 SCRIPT_DIR 定位自己的目录,结果 source ../day06-H-snpe-convert/snpe_setup.sh 时,setup 脚本内部也用了同一个变量名,直接把 SCRIPT_DIR 改成了 day06 目录。后续 ONNX_PATH 就莫名其妙指向了 day06-H-snpe-convert/speech_cmd_cnn.onnx,怎么跑都报模型找不到。

解决办法:source 之前先备份,source 之后恢复。

CONVERT_SCRIPT_DIR="$SCRIPT_DIR"
source "$SCRIPT_DIR/../day06-H-snpe-convert/snpe_setup.sh"
SCRIPT_DIR="$CONVERT_SCRIPT_DIR"

坑 2:Android 库路径多了层不存在的目录

旧脚本里写的是 lib/aarch64-android-clang8.0,但当前 SNPE 2.48 解压出来的 Android 运行时目录只有 lib/aarch64-androidbin/aarch64-android,没有 clang8.0 这层。路径不对,adb push 库时直接失败。

坑 3:忘了 push libSNPE.so

手机端第一次运行 snpe-net-run 时直接报错:

CANNOT LINK EXECUTABLE "./snpe-net-run": cannot locate symbol
"Snpe_Util_IsRuntimeAvailableCheckOptionForDevice"

readelf 才发现 snpe-net-run 直接依赖 libSNPE.so。旧脚本没 push 这个库,而 SNPE 2.48 的 aarch64-android 目录里其实有它(大约 25MB),必须一起推到手机上。

坑 4:CPU 推理不能加 --use_cpu

SNPE 2.48 的 snpe-net-run 默认就是 CPU 后端,不用加 --use_cpu。加了反而会报 Invalid argument passed --use_cpu。这个参数是改 convert.sh 时我自己想当然加进去的,纯属多此一举。

坑 5:校准数据和训练预处理不一致

prep_calibration_sc.py 原本只做了 AmplitudeToDB,但 train_sc.py 里还对每条 Mel 频谱做了 per-sample min-max 归一化,把数值压到 [0,1]。两边预处理不一致,量化出来的 INT8 模型输入分布和训练时偏差很大,精度会掉。补上同样的归一化:

mel_min = mel_feat.amin(dim=(1, 2), keepdim=True)
mel_max = mel_feat.amax(dim=(1, 2), keepdim=True)
range_ = mel_max - mel_min
range_[range_ < 1e-6] = 1.0
mel_feat = (mel_feat - mel_min) / range_

坑 6:SNPE 的 Python 3.10 venv 里没有 torch

snpe_setup.sh 激活的是 Python 3.10 虚拟环境,专门给 SNPE 工具用,里面没装 torch/torchaudio。准备校准数据需要 torchaudio 读取数据集,所以 convert.sh 里先用项目主 venv(Python 3.11)跑 prep_calibration_sc.py,再切到 SNPE 环境跑转换和量化。

转换与量化结果

执行命令:

bash convert.sh 100

先用 100 条校准数据快速跑通,后续正式测试可以换成 500 条。

产物 大小 说明
speech_cmd_cnn.dlc (FP32) 4.82 MB SNPE 专有格式
speech_cmd_cnn_int8.dlc (INT8) 1.32 MB 8bit 量化后
压缩比 3.65x 模型体积明显缩小

snpe-dlc-info 显示模型支持 CPU / DSP / GPU / AIP 四种 runtime,总参数量 1,224,195,和 PyTorch 侧一致。

x86 上先跑个 smoke test

在 PC 上用 snpe-net-run 分别跑 FP32 和 INT8,确认 DLC 本身没问题:

snpe-net-run --container speech_cmd_cnn.dlc \
    --input_list input_list_x86.txt --output_dir output_x86_fp32
snpe-net-run --container speech_cmd_cnn_int8.dlc \
    --input_list input_list_x86.txt --output_dir output_x86_int8

两条都显示 Successfully executed graph speech_cmd_cnn。输出是 35 维 logits,取 top 5 对比:

来源 top5 类别索引 top5 分数
x86 FP32 0, 15, 2, 16, 1 3.18, 0.48, 0.28, -0.78, -1.18
x86 INT8 0, 15, 2, 16, 1 3.18, 0.42, 0.29, -0.79, -1.28

类别排序完全一致,数值差别很小,属于正常量化误差。

真机 CPU 推理:PC 和手机对上了

手机连上 USB 后,convert.sh 自动把库、模型、输入文件推上去,然后跑 FP32 CPU:

adb shell "cd /data/local/tmp/snpe_sc && export LD_LIBRARY_PATH=. \
    && ./snpe-net-run --container speech_cmd_cnn.dlc \
       --input_list input_list.txt --output_dir output_fp32"

输出:

Processing graph : speech_cmd_cnn
Processing DNN input(s): /data/local/tmp/snpe_sc/test_input.raw
Successfully executed graph speech_cmd_cnn

output_fp32/Result_0/logits.raw pull 回本地解析:

来源 top5 类别索引 top5 分数
真机 FP32 CPU 0, 15, 2, 16, 1 3.1826, 0.4755, 0.2811, -0.7797, -1.1840
x86 FP32 CPU 0, 15, 2, 16, 1 3.1826, 0.4755, 0.2811, -0.7797, -1.1840

真机和 PC 输出几乎完全一致(差异在 1e-5 以内),说明从训练到手机端 CPU 部署这条链路已经打通。

INT8 DSP 挂了,但 GPU 能兜底

真机跑 INT8 DSP 时直接失败:

The selected runtime is not available on this platform.
error_code=2008; error_message= QnnBackend_DeviceCreate() failed;
QNN_COMMON_ERROR_INCOMPATIBLE_BINARIES: Loaded libraries are of incompatible versions

这台手机是 SM8750(骁龙 8 Elite),SNPE 2.48 的 QNN backend 版本和它自带的 Hexagon/HTP 驱动二进制不兼容。我试过只推最小 so 集合(libSNPE.so + libQnnSystem.so + libQnnHtp.so + HTP stub),报的还是同样的错,排除 so 漏推的问题。

这属于芯片迭代比 SDK 快的典型情况:新 SoC 出来以后,SDK 可能要晚几个版本才完整支持。不是脚本或模型的问题。

顺手试了一下 GPU,发现 Adreno GPU 可以跑通。于是 convert.sh 里加了 GPU fallback:INT8 DSP 失败时自动切到 GPU 再试一次。

执行结果汇总:

推理方式 状态 top5 类别索引 备注
真机 FP32 CPU 成功 0, 15, 2, 16, 1 与 x86 输出一致
真机 FP32 GPU 成功 0, 15, 2, 16, 1 GPU 后端正常
真机 INT8 DSP 失败 - SM8750 HTP 驱动不兼容
真机 INT8 GPU 成功 0, 15, 2, 16, 1 DSP 失败后的 fallback

GPU 需要额外推这几个库:

libQnnGpu.so
libQnnGpuNetRunExtensions.so
libQairtGpu.so
libQairtGpuBackendExtensions.so

如果想彻底跑通 DSP/HTP,后续可以:

  1. 换一颗 SNPE 2.48 已经支持的 SoC,比如骁龙 8 Gen 3(SM8650)或骁龙 8 Gen 2(SM8550);
  2. 升级 QNN/SNPE SDK 到支持 SM8750 的版本;
  3. 或者直接拿 GPU 当 INT8 推理后端,功能可用,只是功耗比 NPU 高。

实测结论

环节 状态 备注
ONNX → DLC FP32 成功 模型结构和算子都被 SNPE 2.48 支持
FP32 → INT8 量化 成功 压缩比 3.65x,INT8 模型 1.32 MB
x86 CPU 推理 FP32/INT8 成功 输出类别排序一致
真机 FP32 CPU 推理 成功 与 x86 输出几乎一致
真机 FP32 GPU 推理 成功 GPU 后端兼容
真机 INT8 DSP 推理 失败 SM8750 与 SNPE 2.48 不兼容
真机 INT8 GPU 推理 成功 作为 DSP 失败的 fallback

从训练到 PC 端 SNPE 推理、再到手机 CPU/GPU 端推理的完整链路已经跑通。INT8 DSP 推理需要等 SDK 版本匹配或换测试设备,这是端侧部署里常见的“芯片迭代快于 SDK 支持”现象;GPU 至少能作为临时兜底方案,保证功能可用。

下一步计划

  • snpe-throughput-net-run 或循环 snpe-net-run 测量真机 FP32 CPU / INT8 GPU 的单帧延迟,补齐 P50/P99
  • 换一颗 SNPE 2.48 支持的 SoC(如骁龙 8 Gen 3)测试 INT8 DSP/HTP 延迟与功耗
  • 扩展 eval.py 支持直接评测 DLC/INT8 模型,计算完整测试集上的量化精度损失
  • 把 Mel 特征提取也做端侧化,目前仍依赖 Python 预处理,车机部署需要进一步下沉到 DSP/GPU 音频前端

训练调优过程见同目录 blog-speech-commands-training.md


系列文章

本系列「车载端侧 AI 工程化从零上手」共 10 篇,建议按序阅读:

  1. 零基础用 PyTorch 识别手写数字:MNIST 实战入门
  2. 把 PyTorch 模型变成 ONNX:导出、验证和可视化一次学会
  3. MLflow 实验追踪:从入门到上手
  4. 用 MNN 在 CPU 上跑 AI 推理:阿里端侧推理框架上手记
  5. 小白用 TensorRT 给模型加速:ONNX 转 Engine 踩坑实录
  6. 零基础上手:高通 SNPE 模型转换实战(从 ONNX 到 DLC)
  7. 零基础搞懂 SNPE 模型量化:INT8 精度损失 = 0% 的秘密
  8. Python 工程化重构:从 print 脚本到 pytest 项目
  9. 车载语音指令识别:从 44% 到 96% 的调优之路
  10. 车载语音指令识别:SNPE 转换与真机部署实测记录

本文链接:车载语音指令识别:SNPE 转换与真机部署实测记录 - https://h89.cn/archives/676.html

版权声明:原创文章 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文链接和本声明。

标签: 转换, adb, GPU, ONNX, CPU, INT8, SNPE, DLC, 骁龙, 量化, DSP, FP32

🎓 呈言英语 智能英语学习平台
📚单词学习 🎧听说练习 📖阅读理解 ✏️拼写练习 🌟 AI智能推荐 · 科学记忆曲线
🚀 立即开始免费学习

添加新评论