恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

YOLOv5模型ncnn量化部署全攻略:从ONNX到int8

  • 首页
  • 资讯中心
  • /
  • YOLOv5模型ncnn量化部署全攻略:从ONNX到int8

相关资讯

Flask+Bootstrap博客系统:Python全栈入门最佳实践 2026/9/12 21:25:30
Android四合一应用源码解析:闹钟、秒表与自定义滚轮实现 2026/9/12 21:25:30
Java开发者转型Agent开发与大模型应用指南 2026/9/12 21:25:30

最新资讯

PaddleInference OCR验证码识别部署实战
WezTerm Lua API 实战:用 `wezterm.procinfo.current_working_dir_for_pid()` 查询任意进程的工作目录
PolarDB-X与自建数据库三年TCO实测对比
群晖NAS迅雷套件下载路径修改与优化指南
Codex与GPT-6 Astra的本质差异:从代码编译器到任务导航星图
用Res2Net改进UNet实现舌头图像语义分割实战

今日推荐

MATLAB仿生优化框架:长鼻浣熊算法多策略融合实现
【JAVA毕设源码分享】基于 JavaWeb 的校园一卡通管理系统的设计与实现 基于 JavaWeb 的校园卡业务管理系统(程序+文档+代码讲解+一条龙定制)
【JAVA毕设源码分享】基于 Java 的图书馆借阅管理平台的搭建与实现 基于 Java 的图书馆综合管理系统(程序+文档+代码讲解+一条龙定制)

本周热门

超人会飞不算本事:系统稳定依赖清晰规则与边界设计
超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论
基于CNN的调制信号识别:MATLAB实现时频图分类实战

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

YOLOv5模型ncnn量化部署全攻略:从ONNX到int8

发布时间:2026/9/12 21:25:30
YOLOv5模型ncnn量化部署全攻略:从ONNX到int8 简介基于ncnn部署yolov5及量化的完整工程包面向移动端与嵌入式视觉开发者聚焦如何将PyTorch版YOLOv5转换为ncnn格式并通过INT8量化压缩模型体积、提升推理速度解决资源受限设备上的实时目标检测部署难题。压缩包共257个文件约205MB包含141个C头文件、onnx/pt原始与中间权重、ncnn的param/bin优化模型、proto协议文件、CMake构建脚本、lib静态库、exe工具以及Visual Studio完整解决方案sln/vcxproj与编译中间产物目录结构完整既可用于学习转换流程也可直接承接二次开发。源码覆盖模型加载、预处理、推理、后处理NMS和CPU多核/OpenMP加速等关键环节并含有优化前后的模型文件对比量化方面给出离线转换与参数优化后的产物配合依赖库和VS工程配置能显著降低环境搭建与排错成本。资源已有486人学习适合有深度学习基础、正在将检测算法落地到移动端的工程师参考复用也可作为团队内部技术预研的起点。1. 用ncnn量化部署yolov5先想清楚整条链路在边缘端跑目标检测很多人踩的第一个坑不是模型精度不够而是训练好的yolov5权重没法直接被CPU推理库读取。PyTorch的.pt文件绑定了Python运行时和算子逻辑要落到Android、嵌入式Linux或国产CPU上通常先把模型导出成ONNX再转成ncnn的.param/.bin格式最后做fp16或int8量化。这条链路解决的是“推理框架选型、模型体积压缩、精度损失可控”三个问题ncnn不依赖CUDA和第三方运行时int8量化后模型体积能压到原始fp32的四分之一单核CPU也能跑到可用帧率。适合做边缘计算盒、工业检测终端或者只是想把几十MB的模型瘦身上线的人。2. yolov5转ncnnONNX导出与量化选型2.1 导出yolov5的ONNX时关掉哪些开关用ncnn跑yolov5第一步是把.pt转成ONNX。官方仓库的export.py封装了导出逻辑但有几个参数直接影响后续onnx2ncnn是否顺利。python export.py --weights yolov5s.pt --img 640 --batch 1 \ --include onnx --opset 12 --simplify这里--img 640让输入是固定尺寸--batch 1锁定动态batch--opset 12是ncnn兼容性最好的算子集版本。--simplify需要先安装onnx-simplifier它会折叠掉一些冗余的Reshape和Transpose让计算图更干净。导出后建议用onnx.checker或onnxruntime跑一次推理确认没有NaN输出。常见问题是漏加--simplify导致图里有一长串Identity节点或者用了--dynamic导出动态尺寸虽然ncnn后来也支持动态shape但动态维度会把量化校准表弄复杂。我一般固定640输入客户端再自己做letterbox。yolov5的Detect头在导出时会被特殊处理输出是三个尺度的检测结果拼在一起ONNX末尾是一个Concat和Reshape最终输出维度是[1, 25200, 85]coco 80类。ncnn对这类图很熟悉只要没引入自定义算子转换通常一遍过。2.2 ONNX算子兼容性onnx2ncnn最常见失败点onnx2ncnn失败往往不是框架问题而是onnx里出现ncnn尚未支持的op。yolov5常见兼容性风险如下算子/行为风险处理方式opset 13的Splitncnn旧版本不认num_outputs属性固定用--opset 12导出DynamicQuantizeLinear量化算子转换麻烦导出时不要启动--int8ncnn侧自己做量化Swish/SiLUncnn已支持但某些源码版本未注册升级ncnn到最新release自定义NMS层onnx不支持NMS导出把NMS放到后处理C代码里输入名不是images后续写代码容易搞混导出后用onnx.reshape固定输入名遇到转换报错别急着改模型结构先看ncnn源码的onnx2ncnn.cpp里有没有对应op实现。yolov5大部分层不是难点真正容易卡的是opset版本和Split的num_outputs。把opset降到12绝大多数兼容性问题都能绕开。2.3 fp16还是int8量化粒度与精度、速度的矛盾ncnn的“量化”通常分两级。第一级是fp16存储把权重从32位浮点改成16位浮点模型体积减半精度几乎没有损失第二级是int8存储用校准数据集统计每层输入的数值范围把浮点权重和激活映射到8位整数体积再减一半同时利用ARM的int8指令如dotprod获得明显加速。两者的选择要看硬件和场景。下表是我常用的选型参考指标fp16int8模型体积约为fp32的50%约为fp32的25%精度损失基本无损mAP下降0.1%通常下降0.5%~2.0%之间速度提升CPU上有限GPU/Vulkan上明显ARMv8.2平台有较大提升是否需要校准集不需要需要100~1000张有代表性图片部署难度低中需处理输出层量化偏差如果你的目标设备是带Vulkan的GPUfp16已经能拿到很大收益。如果设备是ARM CPU尤其支持int8点积指令int8才是真正的性能关键。yolov5s转int8后在RK3588这类平台上延迟能比fp16再降30%以上代价是mAP可能掉1个点左右。量化工作大多集中在“校准集怎么选”和“哪些层不量化”这两个问题上。3. 在Ubuntu下生成onnx2ncnn并完成yolov5量化3.1 编译ncnn工具链依赖与最小cmakeonnx2ncnn和量化工具都在ncnn源码里先克隆再编译git clone https://github.com/Tencent/ncnn.git cd ncnn mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease \ -DNCNN_BUILD_TOOLSON \ -DNCNN_BUILD_EXAMPLESON \ -DNCNN_VULKANON \ -DNCNN_BUILD_BENCHMARKON .. make -j$(nproc)重点说两个选项-DNCNN_BUILD_TOOLSON会编译出onnx2ncnn、ncnnoptimize、ncnn2table、ncnnint8这些可执行文件-DNCNN_VULKANON让ncnn在支持Vulkan的机器上能调用GPU量化时也更快。如果目标设备没有Vulkan这里可以关掉但后续C工程里也要保持和编译时一致。编译依赖需要protobuf、opencv和glslang。Ubuntu下直接apt install libprotobuf-dev protobuf-compiler libopencv-dev就够。不建议用系统自带的老protobuf首选protobuf 3.x版本。编译完成后检查build/tools/onnx/onnx2ncnn是否存在。这一步很关键很多人下载了别人编译好的ncnn换机器跑onnx2ncnn报缺少.so自己编译一次能省掉很多环境问题。3.2 onnx2ncnn把yolov5s.onnx转成fp16 param/bin./build/tools/onnx/onnx2ncnn yolov5s.onnx yolov5s.param yolov5s.bin命令格式是“输入onnx 输出param 输出bin”。转出来的param是文本格式的图结构bin是二进制的权重数据。yolov5s转出来大概几十MB这时的bin还是fp32原始权重。转完先看param尾部通常会有Reshape和Concat组成的Detect层结构。如果中间有MemoryData类型的节点数量远多于常规CNN说明ONNX里有大量常量折叠没做干净回到export.py加上--simplify重新导出。3.3 ncnnoptimize图优化fp16存储的关键一步./build/tools/optimize/ncnnoptimize yolov5s.param yolov5s.bin yolov5s-opt.param yolov5s-opt.bin 1最后一个参数1表示开启fp16存储。这个工具会先做一次常量折叠和算子融合再把能转fp16的权重转成半精度。只看体积的话转完的bin应当比原始bin小一半左右。注意ncnnoptimize也会把一些层融合成Gemm、ConvolutionDepthWise这类ncnn自研实现后续表格文件是基于优化后的模型生成的。所以流程顺序一定是先ncnnoptimize再拿优化后的param/bin去量化不要在原始ONNX转出来的模型上直接跑int8。3.4 int8校准集从yolov5训练集里抽多少张合适int8量化需要统计激活值的真实分布校准集要有代表性。不要用测试集也不要只拿一类目标。常见做法是从训练集里随机抽200~2000张覆盖不同光照、不同目标尺寸和不同背景。配一个文本文件每行一张图片的绝对路径/home/user/calib/0001.jpg /home/user/calib/0002.jpg /home/user/calib/0003.jpg图片分辨率不一定要和训练尺寸一致ncnn2table在处理时会先做一次resize到目标shape。但尽量选接近原始训练分布的数据。如果目标是检测小物体校准图里不能全是大目标场景否则量化会把小目标对应的激活值截断掉。3.5 跑ncnn2table和ncnnint8的完整命令与参数./build/tools/quantize/ncnn2table yolov5s-opt.param yolov5s-opt.bin \ calib_list.txt yolov5s.table \ mean0,0,0 norm0.003921,0.003921,0.003921 shape640,640,3 pixel0 thread8 methodkl参数含义如下mean和norm表示预处理参数这里对应yolov5做归一化时除以255mean填0。shape是输入宽高和通道顺序是W,H,C。如果yolov5输入是640x640就写640,640,3。pixel0表示输入按BGR顺序这一点和PyTorch训练时保持一致。thread8用8线程加速校准数据前向数据集大时能明显省时间。methodkl是校准算法。kl对yolov5这类检测模型通常更稳实测中比min/max损失小但数据分布极端时也可以试aciq。生成yolov5s.table后再执行./build/tools/quantize/ncnnint8 yolov5s-opt.param yolov5s-opt.bin \ yolov5s-int8.param yolov5s-int8.bin yolov5s.table这一步把fp16的model计算成int8权重输出新的param和bin。拿到yolov5s-int8.param后先grep一下Quantize和Dequantize节点数量如果detect层前没有Dequantize说明输出层被强行量化了后续做后处理时大概率会出现类型不符。此时要用编辑器把param里对应输出层的flag从1改成0或者从原始模型copy一个未量化的检测头拼接进去。4. 用C推理代码调用量化后的yolov54.1 Net加载int8模型并设置线程和低功耗ncnn不是像OpenCV那样调一个函数就完事它要求你先加载结构文件param再加载权重bin之后通过Extractor做推理。#include net.h ncnn::Net net; net.opt.use_vulkan_compute true; net.opt.num_threads 4; net.opt.use_fp16_packed true; net.opt.use_fp16_storage true; net.opt.use_bf16_storage false; net.load_param(yolov5s-int8.param); net.load_model(yolov5s-int8.bin);load_param和load_model的顺序不能颠倒。use_vulkan_compute要和你编译ncnn时开不开Vulkan对应设备不支持时打开只会在运行时提示vulkan not supported不会崩溃。use_fp16_packed和use_fp16_storage对int8模型影响不大但对fp16模型非常关键。4.2 预处理letterbox保持宽高比并填成640x640直接resize会破坏目标宽高比导致检测框偏移。yolov5官方用了letterbox也就是等比缩放后填充灰色边缘。#include mat.h int letterbox(const cv::Mat src, cv::Mat dst, int target_w, int target_h) { float ratio std::min(1.0f * target_w / src.cols, 1.0f * target_h / src.rows); int new_w std::round(src.cols * ratio); int new_h std::round(src.rows * ratio); cv::resize(src, dst, cv::Size(new_w, new_h)); int pad_w (target_w - new_w) / 2; int pad_h (target_h - new_h) / 2; cv::copyMakeBorder(dst, dst, pad_h, target_h - new_h - pad_h, pad_w, target_w - new_w - pad_w, cv::BORDER_CONSTANT, cv::Scalar(114, 114, 114)); return 0; } // 调用 cv::Mat rgb; cv::cvtColor(bgr_img, rgb, cv::COLOR_BGR2RGB); cv::Mat letterboxed; letterbox(rgb, letterboxed, 640, 640); ncnn::Mat in ncnn::Mat::from_pixels(letterboxed.data, ncnn::Mat::PIXEL_RGB, letterboxed.cols, letterboxed.rows); const float mean[3] {0.f, 0.f, 0.f}; const float norm[3] {1 / 255.f, 1 / 255.f, 1 / 255.f}; in.substract_mean_normalize(mean, norm);这里有个容易错的地方ncnn的from_pixels顺序有PIXEL_RGB和PIXEL_BGR两种。如果训练时用了RGB顺序且量化table里写pixel0推理代码就要用PIXEL_BGR或pixel1保持一致。我通常会统一成RGB避免在调试时纠结颜色通道反了导致检测置信度不稳。4.3 后处理从输出Mat到NMSyolov5的ONNX输出是一个[1, 25200, 85]的二维展开张量每行是cx,cy,w,h,obj,cls0,cls1,...。ncnn读取时通常把它当成三维Mat但实际数据是连续排布的直接按out.channel(0)取指针就够。#include layer.h std::vectorcv::Rect boxes; std::vectorfloat scores; std::vectorint class_ids; ncnn::Mat out; ex.extract(output, out); int num_boxes out.h; // 25200 int num_classes out.c - 5; for (int i 0; i num_boxes; i) { const float* ptr out.row(i); float obj_conf ptr[4]; if (obj_conf 0.25) continue; for (int j 0; j num_classes; j) { float cls_conf ptr[5 j]; float final_conf obj_conf * cls_conf; if (final_conf 0.25) { float cx ptr[0]; float cy ptr[1]; float w ptr[2]; float h ptr[3]; // 记录框和分数等NMS } } } // 用标准NMS过滤重叠框 ncnn::nms(boxes, scores, class_ids, 0.45, ncnn::NMS_SUPPRESS);out.h不一定等于25200取决于param里的Reshape排列。如果发现out.c是25200而out.h是85就把行列互换排查方法是在一个已知图片上打印out.w, out.h, out.c和第一个值的关系。5. 量化后的精度验证、敏感度分析与混合量化技巧5.1 用验证集和mAP0.5判断量化损失int8量化不是转完就能上线先做一次精度对比。用同一批验证图片原fp16模型和int8模型分别跑推理统计mAP0.5。不想写完整mAP脚本可以直接对比每个类别的召回率或前100张图的平均置信度差异。yolov5官方val.py只能测PyTorch模型。ncnn模型通常我会写一个轻量脚本读入图片通过ncnn推理输出框再和你训练时的.txt标注计算IoU。量化损失超过2个mAP点就说明校准集或参数不对不要继续往下做部署。5.2 逐层敏感度分析与混合量化有时候不是所有层都适合int8。比如第一个卷积直接吃原始像素量化误差容易放大Detect层前面的卷积决定最终输出也常出现漂移。ncnnint8生成的param里每一层后面会带一个整型flag1表示该层被量化为int8。把敏感层的flag改为0后保留该层为fp16计算这就是ncnn的混合量化。修改param后重新加载bin不用换。实际操作时我习惯先量化全部然后逐层改回fp16看mAP变化找前三名最敏感层。这个过程虽然手动但效果通常比强行全int8好而且计算量不大因为只需要改文本文件。5.3 校准集和超参数的几个坑校准集数量不是越大越好。我用过2000张和300张后者经常出更好的mAP因为校准集太杂会把激活值分布拉宽导致量化步长过大。校准算法选择上kl适合正态分布的数据aciq在低比特量化时更稳。如果目标设备是ARMv8.2且支持int8点积注意打开net.opt.use_bf16_storage false否则某些层可能悄悄回退到bf16。另一个容易忽略的是输出层前后的Dequantize节点要确认Detect前是fp32输出否则后处理里的阈值会变得不可理喻。遇到这种问题直接在param里把Detect前的卷积flag改成0让检测头保持fp16精度。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号