YOLOv5实战指南:从ONNX模型到Android端高效部署
1. YOLOv5模型转换全流程解析第一次把YOLOv5模型部署到Android端时我对着各种格式转换工具发懵——PT、ONNX、NCNN这些名词看得人眼花缭乱。后来踩过几次坑才发现其实只要掌握关键步骤整个过程就像组装乐高积木一样有章可循。先说说为什么要用ONNX这个中间格式。想象你有个Python写的模型现在要让它跑在Java开发的Android应用里就像让说中文的人和说英文的人直接对话双方都听不懂。ONNX就像个万能翻译官先把PyTorch的.pt模型转换成通用语言再让NCNN这个本地翻译转成Android能理解的格式。这种标准化流程比直接硬啃跨平台兼容性要高效得多。实际操作时模型转换最容易卡在三个地方PT转ONNX时忘记加--train参数导致后处理节点残留没做ONNX简化就直接转换遇到莫名其妙的节点报错param文件末尾参数没改成-1结果检测框多到看不清图片我建议在转换完成后立即用Netron检查模型结构。这个可视化工具能直观显示网络层就像X光机一样帮你确认转换是否成功。最近还发现个偷懒技巧用convertmodel.com在线转换特别适合不想折腾环境配置的时候。不过要注意敏感模型别随便上传毕竟数据安全最重要。2. ONNX模型优化实战技巧很多人以为PT转ONNX就是执行个命令行的事其实里面的门道不少。最近帮同事处理一个工业质检模型时发现同样的转换命令在YOLOv5 6.2版本上就比老版本少了很多麻烦——主要因为官方去掉了Focus层这个历史包袱。模型简化是另一个容易翻车的环节。有次我偷懒没做简化结果ONNX转NCNN时报了一堆Unsupported slice steps错误。后来发现用这个命令能解决90%的问题python -m onnxsim input.onnx output-sim.onnx --dynamic-input-shape加--dynamic-input-shape特别重要它让模型能适应不同尺寸的输入图像。就像给衣服加上松紧带不管手机摄像头拍出什么比例的图片都能处理。还有个隐藏技巧转换前先在Python里验证ONNX模型import onnxruntime as ort sess ort.InferenceSession(model.onnx) outputs sess.run(None, {images: dummy_input})这步能提前发现runtime错误比等到移动端才调试省时得多。记得检查输出维度是否和原模型一致我遇到过转完ONNX后输出shape莫名其妙少一维的情况。3. Android环境配置避坑指南第一次装Android Studio的经历堪称噩梦——SDK版本冲突、Gradle下载卡住、模拟器启动失败...后来总结出最稳的安装流程JDK版本要选8或11新版可能不兼容安装时勾选Android SDK Command-line Tools后面编译NCNN要用创建项目时选**Native C**模板省去JNI配置的麻烦测试发现用真机调试比模拟器快不止一个量级。建议在手机的开发者选项里开启USB调试和保持唤醒状态不然训练到一半锁屏就前功尽弃了。遇到最奇葩的问题是同样的APK在华为手机上正常到小米就崩溃。最后发现是OpenMP库冲突解决方法是在build.gradle里加上packagingOptions { pickFirst **/libomp.so }这个坑足足浪费我两天时间现在看到小米手机都条件反射地加这行配置。4. 模型适配与性能调优把官方demo跑通只是第一步要让自定义模型高效运行还得下功夫。有次部署一个安全帽检测模型发现帧率只有3FPS根本没法用。后来通过这四招提升到18FPS量化模型把FP32转成INT8体积缩小4倍./ncnnoptimize yolov5.param yolov5.bin yolov5-opt.param yolov5-opt.bin 65536调整线程数根据CPU核心数设置ncnn::set_cpu_num_threads()启用Vulkan在支持GPU的设备上能提速30%预处理优化把RGB转换放到GLSL着色器里anchors设置是另一个关键点。有次直接用了COCO数据集的默认值结果小目标检测完全失效。后来学会用这个脚本提取训练时的anchorfrom utils.autoanchor import check_anchors check_anchors(dataset, modelmodel, thr4.0)建议在训练日志里就保存好这些参数免得部署时又要翻旧账。5. 部署后的实战调试你以为模型转好、APK装上就万事大吉真正的挑战才刚刚开始。在荣耀X10上测试时发现检测框总是偏移10个像素查了半天才发现是分辨率适配问题——模型训练用640x640但手机摄像头输出是720p。解决方法是在YOLOv5ncnn.cpp里修改预处理ncnn::Mat in ncnn::Mat::from_pixels_resize( rgb.data, ncnn::Mat::PIXEL_RGB, src_w, src_h, target_w, target_h );还要注意内存泄漏问题。有次APP跑着跑着就卡死发现是每次检测都new对象没释放。现在都会在JNI函数开头加ncnn::create_gpu_instance();结尾加ncnn::destroy_gpu_instance();最近还发现个取巧的办法用TFLite代替NCNN部署。虽然性能稍差但Android官方支持更好。适合对帧率要求不高(15FPS)的场景能省去很多底层适配工作。