【RKNN】YOLO V5中pytorch2onnx,pytorch和onnx模型输出不一致,精度降低

news2025/4/26 7:14:30

yolo v5训练的模型,转onnx,再转rknn后,测试发现:

  1. rknn模型,量化与非量化,相较于pytorch模型,测试精度都有降低
  2. onnx模型,相较于pytorch模型,测试精度也有降低,且与rknn模型的精度更接近

于是,根据这种测试情况,rknn模型的上游,就是onnx。onnx这里发现不对劲,肯定是这步就出现了问题。于是就查pytorch转onnx阶段,就存在转化的精度降低了。

本篇就是记录这样一个过程,也请各位针对本文的问题,给一些建议,毕竟目前是发现了问题,同时还存在一些问题在。

一、pytorch转onnx:torch.onnx.export

yolo v5 export.py: def export_onnx()中,添加下面代码,检查转储的onnx模型,与pytorch模型的输出结果是否一致。代码如下:

torch.onnx.export(
    model.cpu() if dynamic else model,  # --dynamic only compatible with cpu
    im.cpu() if dynamic else im,
    f,
    verbose=False,
    opset_version=opset,
    export_params=True, # 将训练好的权重保存到模型文件中
    do_constant_folding=True,  # 执行常数折叠进行优化
    input_names=['images'],
    output_names=output_names,
    dynamic_axes={
        "image": {0: "batch_size"},  # variable length axes
        "output": {0: "batch_size"},
    }
)

# Checks
model_onnx = onnx.load(f)  # load onnx model
onnx.checker.check_model(model_onnx)  # check onnx model
    
import onnxruntime
import numpy as np
print('onnxruntime run start', f)
sess = onnxruntime.InferenceSession('best.onnx')
print('sess run start')
output = sess.run(['output0'], {'images': im.detach().numpy()})[0]
print('pytorch model inference start')


pytorch_result = model(im)[0].detach().numpy()
print(' allclose start')
print('output:', output)
print('pytorch_result:', pytorch_result)
assert np.allclose(output, pytorch_result), 'the output is different between pytorch and onnx !!!'

对其中的输出结果进行了打印,将差异性比较明显的地方进行了标记,如下所示:

在这里插入图片描述
也可以直接使用我下面这个版本,在转完onnx后,进行评测,转好的onnx和pt文件之间的差异性。如下:

参考pytorch官方:(OPTIONAL) EXPORTING A MODEL FROM PYTORCH TO ONNX AND RUNNING IT USING ONNX RUNTIME

import os
import platform
import sys
import warnings
from pathlib import Path
import torch

FILE = Path(__file__).resolve()
ROOT = FILE.parents[0]  # YOLOv5 root directory
if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT))  # add ROOT to PATH
if platform.system() != 'Windows':
    ROOT = Path(os.path.relpath(ROOT, Path.cwd()))  # relative

from models.experimental import attempt_load
from models.yolo import ClassificationModel, Detect, DetectionModel, SegmentationModel
from utils.dataloaders import LoadImages
from utils.general import (LOGGER, Profile, check_dataset, check_img_size, check_requirements, check_version,
                           check_yaml, colorstr, file_size, get_default_args, print_args, url2file, yaml_save)
from utils.torch_utils import select_device, smart_inference_mode


import numpy as np
def cosine_distance(arr1, arr2):
    # flatten the arrays to shape (16128, 7)
    arr1_flat = arr1.reshape(-1, 7)
    arr2_flat = arr2.reshape(-1, 7)

    # calculate the cosine distance
    cosine_distance = np.dot(arr1_flat.T, arr2_flat) / (np.linalg.norm(arr1_flat) * np.linalg.norm(arr2_flat))

    return cosine_distance.mean()


def check_onnx(model, im):

    import onnxruntime
    import numpy as np
    print('onnxruntime run start')
    sess = onnxruntime.InferenceSession('best.onnx')
    print('sess run start')
    output = sess.run(['output0'], {'images': im.detach().numpy()})[0]
    print('pytorch model inference start')

    with torch.no_grad():
        pytorch_result = model(im)[0].detach().numpy()
    print(' allclose start')
    print('output:', output, output.shape)
    print('pytorch_result:', pytorch_result, pytorch_result.shape)
    cosine_dis = cosine_distance(output, pytorch_result)
    print('cosine_dis:', cosine_dis)

    # 判断小数点后几位(4),是否相等,不相等就报错
    # np.testing.assert_almost_equal(pytorch_result, output, decimal=4)

    # compare ONNX Runtime and PyTorch results
    np.testing.assert_allclose(pytorch_result, output, rtol=1e-03, atol=1e-05)

    # assert np.allclose(output, pytorch_result), 'the output is different between pytorch and onnx !!!'

import cv2
from utils.augmentations import letterbox
def preprocess(img, device):
    img = cv2.resize(img, (512, 512))

    img = img.transpose((2, 0, 1))[::-1]
    img = np.ascontiguousarray(img)
    img = torch.from_numpy(img).to(device)
    img = img.float()
    img /= 255
    if len(img.shape) == 3:
        img = img[None]
    return img
def main(
        weights=ROOT / 'weights/best.pt',  # weights path
        imgsz=(512, 512),  # image (height, width)
        batch_size=1,  # batch size
        device='cpu',  # cuda device, i.e. 0 or 0,1,2,3 or cpu
        inplace=False,  # set YOLOv5 Detect() inplace=True
        dynamic=False,  # ONNX/TF/TensorRT: dynamic axes

):
    # Load PyTorch model
    device = select_device(device)
    model = attempt_load(weights, device=device, inplace=True, fuse=True)  # load FP32 model

    # Checks
    imgsz *= 2 if len(imgsz) == 1 else 1  # expand

    # Input
    gs = int(max(model.stride))  # grid size (max stride)
    imgsz = [check_img_size(x, gs) for x in imgsz]  # verify img_size are gs-multiples
    im = torch.zeros(batch_size, 3, *imgsz).to(device)  # image size(1,3,320,192) BCHW iDetection
    # im = cv2.imread(r'F:\tmp\yolov5_multiDR\data\0000005_20200929_M_063Y16640.jpeg')
    # im = preprocess(im, device)

    print(im.shape)
    # Update model
    model.eval()
    for k, m in model.named_modules():
        if isinstance(m, Detect):
            m.inplace = inplace
            m.dynamic = dynamic
            m.export = True

    warnings.filterwarnings(action='ignore', category=torch.jit.TracerWarning)  # suppress TracerWarning
    check_onnx(model, im)

if __name__ == "__main__":
    main()

测试1:图像是一个全0的数组,一致性检查如下:

Mismatched elements: 76 / 112896 (0.0673%)
Max absolute difference:  0.00053406
Max relative difference:      2.2101

output: [[[     3.1054       3.965      8.9553 ...  6.8545e-07     0.36458     0.53113]
  [     9.0205      2.5498       13.39 ...  6.2585e-07     0.18449     0.70698]
  [     20.786      2.2233      13.489 ...  2.3842e-06    0.033101     0.95657]
  ...
  [     419.42      493.04      106.14 ...  8.4937e-06     0.24135     0.60916]
  [     485.68      500.22      46.923 ...  1.1176e-05     0.33573     0.48875]
  [     488.37      503.87      68.881 ...  5.9605e-08  0.00030029     0.99639]]] (1, 16128, 7)
pytorch_result: [[[     3.1054       3.965      8.9553 ...  7.0523e-07     0.36458     0.53113]
  [     9.0205      2.5498       13.39 ...  6.0181e-07     0.18449     0.70698]
  [     20.786      2.2233      13.489 ...  2.4172e-06    0.033101     0.95657]
  ...
  [     419.42      493.04      106.14 ...  8.5151e-06     0.24135     0.60916]
  [     485.68      500.22      46.923 ...  1.1174e-05     0.33573     0.48875]
  [     488.37      503.87      68.881 ...  9.3094e-08   0.0003003     0.99639]]] (1, 16128, 7)
cosine_dis: 0.04229331

测试2:图像是加载的本地图像,一致性检查如下:

Mismatched elements: 158 / 112896 (0.14%)
Max absolute difference:   0.0016251
Max relative difference:      1.2584

output: [[[     3.0569      2.4338      10.758 ...  2.0862e-07     0.16333     0.78551]
  [     11.028      2.0251      13.407 ...  3.5763e-07    0.090503     0.88087]
  [     19.447      1.8957      13.431 ...  6.8545e-07    0.047358     0.95029]
  ...
  [     418.66       487.8      80.157 ...  1.4573e-05     0.65453     0.23448]
  [     472.99      491.78      79.313 ...  1.3232e-05     0.79356     0.15061]
  [     496.41      488.49      44.447 ...  2.6256e-05     0.89966     0.08772]]] (1, 16128, 7)
pytorch_result: [[[     3.0569      2.4338      10.758 ...  2.5371e-07     0.16333     0.78551]
  [     11.028      2.0251      13.407 ...  3.3069e-07    0.090503     0.88087]
  [     19.447      1.8957      13.431 ...  6.6051e-07    0.047358     0.95029]
  ...
  [     418.66       487.8      80.157 ...  1.4618e-05     0.65453     0.23448]
  [     472.99      491.78      79.313 ...  1.3215e-05     0.79356     0.15061]
  [     496.41      488.49      44.447 ...  2.6262e-05     0.89966     0.08772]]] (1, 16128, 7)
cosine_dis: 0.04071107

发现,输出结果中,差异的数据点还是挺多的,那么就说明在模型中,有些部分的参数是有差异的,这才导致相同的输入,在最后的输出结果中存在差异。

但是在一定的误差内,结果是一致的。比如我验证了小数点后3位,都是一样的,但是到第4位的时候,就开始出现了差异性。

那么,如何降低,甚至没有这种差异,该怎么办呢?不知道你们有没有这方面的知识储备或经验,欢迎评论区给出指导,感谢。

二、新的pytorch转onnx:torch.onnx.dynamo_export

在参考pytorch官方,关于torch.onnx.export的模型转换,相关文档中:(OPTIONAL) EXPORTING A MODEL FROM PYTORCH TO ONNX AND RUNNING IT USING ONNX RUNTIME

1
上述案例,是pytorch官方给出评测pytorch和onnx转出模型,在相同输入的情况下,输出结果一致性对比的评测代码。对比这里:

testing.assert_allclose(actual, desired, rtol=1e-07, atol=0, equal_nan=True, err_msg='', verbose=True)

其中:

  • rtol:相对tolerance(容忍度,公差,容许偏差)
  • atol:绝对tolerance
  • 要求 actualdesired 值的差别不超过 atol + rtol * abs(desired),否则弹出错误提示

可以看出,这是在误差允许的范围内,进行的评测。只要满足一定的误差要求,还是满足的。并且在本测试案例中,也确实通过了上述设定值的误差要求。

但是,峰回路转,有个提示,如下:
2
于是,就转到torch.onnx.dynamo_export链接,点击这里直达:EXPORT A PYTORCH MODEL TO ONNX

同样的流程,导出模型,然后进行一致性评价,发现官方竟然没有采用允许误差的评测,而是下面这样:
在这里插入图片描述输出完全一致,这是一个大好消息。至此,开始验证

2.1、验证结果

与此同时,发现yolo v5更新到了v7.0.0的版本,于是就想着把yolo 进行升级,同时将pytorch版本也更新到最新的2.1.0,这样就可以采用torch.onnx.dynamo_export 进行转onnx模型的操作尝试了。

当一起就绪后,采用下面的代码转出onnx模型的时候,却出现了错误提示。

export_output = torch.onnx.dynamo_export(model.cpu() if dynamic else model,
                                             im.cpu() if dynamic else im)
export_output.save("my_image_classifier.onnx")

2.2、转出失败

在这里插入图片描述

给出失败的的提示:torch.onnx.OnnxExporterError,转出onnx模型失败,产生了一个SARIF的文件。然后介绍了什么是SARIF文件,可以通过VS Code SARIF,也可以 SARIF web查看。最后说吧这个错误,报告给pytorchGitHubissue地方。

产生了一个名为:report_dynamo_export.sarif是文件,打开文件,记录的信息如下:

{
 "runs":[
  {
   "tool":{
    "driver":{
     "name":"torch.onnx.dynamo_export",
     "contents":[
      "localizedData",
      "nonLocalizedData"
     ],
     "language":"en-US",
     "rules":[],
     "version":"2.1.0+cu118"
    }
   },
   "language":"en-US",
   "newlineSequences":[
    "\r\n",
    "\n"
   ],
   "results":[]
  }
 ],
 "version":"2.1.0",
 "schemaUri":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cs01/schemas/sarif-schema-2.1.0.json"
}

这更像是一个运行环境收集的一个记录文件。在我对全网进行搜索时候,发现了类似的报错提示,但并没有解决办法。不知道是不是因为这个函数还在内测阶段,并没有很好的适配。

如果你也遇到了同样的问题,欢迎给评论,指导问题出在了哪里?如何解决这个问题。感谢

三、总结

原本想着验证最终转rknn的模型,与原始pytorch模型是否一致的问题,最后发现在转onnx阶段,这种差异性就已经存在了。并且发现rknn的测试结果,与onnx模型的测试结果更加的贴近。无论是量化后的rknn,还是未量化的,均存在这个问题。

同时发现,量化后的rknn模型,在config阶段改变量化的方式,确实会提升模型的性能,且几乎接近于未量化的模型版本。

原本以为采用pytorch新的转出onnx的模型函数,可以解决这个问题。但是,发现还是内测版本,不知道问题是出在了哪里,还需要大神帮助,暂时未跑通。

最后,如果你也遇到了同样的问题,欢迎给评论,指导问题出在了哪里?如何解决这个问题。感谢

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1086967.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

The Foundry Nuke 15视频后期合成和特效制作Mac软件

Nuke 15 是一款专业的合成软件,主要用于电影、电视和广告制作中的后期合成和特效制作。 Nuke 15 提供了强大的合成工具和功能,可以对多个图像、视频和3D元素进行无缝融合和合成。它支持多通道图像处理,能够处理高动态范围(HDR&…

算法通关村第18关【青铜】| 回溯

回溯算法是一种解决组合优化问题和搜索问题的算法。它通过尝试各种可能的选择来找到问题的解决方案。回溯算法通常用于问题的解空间非常大,而传统的穷举法会导致计算时间爆炸的情况。回溯算法可以帮助限制搜索空间,以提高效率。 回溯算法的核心思想是在…

ARM作业2

.设置按键中断,按键1按下,LED亮,再按一次,灭 按键2按下,蜂鸣器响。再按一次,不响 按键3按下,风扇转,再按一次,风扇停 头文件key_it.h #ifndef __KEY_IT_H__ #define …

没有炫光的台灯有哪些?2023五款优秀护眼台灯

很多家长有时候会说孩子觉得家里的台灯灯光刺眼,看书看久了就不舒服。这不仅要看光线亮度是否柔和,还要考虑台灯是不是有做遮光式设计。没有遮光式设计的台灯,光源外露,灯光会直射孩子头部,孩子视线较低,很…

【小米技术分享】面试题:什么是乐观锁?你是如何设计一个乐观锁

大家好,我是小米。今天我们来聊一下面试中常见的一个问题:“什么是乐观锁?你是如何设计一个乐观锁?”作为一位热爱技术的程序员,对于这个问题,我有着自己独特的理解和实践经验。接下来,我将以通…

flink1.15 savepoint 超时报错 java.util.concurrent.TimeoutException

savepoint命令 flink savepoint e04813d4e7480c526912eb4d32bba510 hdfs://flink/flink/migration/savepoint56650 -Dyarn.application.id=application_1683808492336_1222报错内容 org.apache.flink.util.FlinkException: Triggering a savepoint for the job e04813d4e7480…

通讯网关软件023——利用CommGate X2HTTP实现HTTP访问Modbus TCP

本文介绍利用CommGate X2HTTP实现HTTP访问Modbus TCP。CommGate X2HTTP是宁波科安网信开发的网关软件,软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示,SCADA系统上位机、PLC、设备具备Modbus RTU通讯接口,现在…

【计算机网络】——前言计算机网络发展的历程概述

主页点击直达:个人主页 我的小仓库:代码仓库 C语言偷着笑:C语言专栏 数据结构挨打小记:初阶数据结构专栏 Linux被操作记:Linux专栏 LeetCode刷题掉发记:LeetCode刷题 算法:算法专栏 C头…

Python编程必备:掌握列表遍历的6种神级技巧!

更多资料获取 📚 个人网站:涛哥聊Python 遍历列表是Python中最常见的任务之一,因为列表是一种非常常用的数据结构,它用于存储一组项目。 在编程中,经常需要对这些项目进行操作,例如查找特定元素&#xff…

【LeetCode】2.两数相加

目录 1 题目2 答案2.1 我写的(不对)2.2 更正 3 问题 1 题目 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返…

如何创建自定义前端组件?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

淘宝历史价格数据接口,淘宝商品历史价格接口,淘宝API接口

淘宝历史价格数据接口采集方法如下: 找到适当的淘宝API接口,该接口需要一个淘宝客的授权码才能使用。使用一个开发工具,比如Python编程语言和Requests库来调用该API接口并获取数据。在调用API时需要指定开始时间和结束时间来获取特定商品在时…

快速搭建Linux网站,并利用内网穿透实现宝塔面板的公网访问【内网穿透】

文章目录 前言1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4. 固定http地址5. 配置二级子域名6. 创建一个测试页面 前言 宝塔面板作为简单好用的服务器运维管理面板,它支持Linux/Windows系统,我们可用它来一键配置LAMP/LNMP环境、网站、数据库、FTP等&…

DataGridView-----datagridviewcomboboxcolumn

今天项目中需要实现下拉框展示数据。同时我们需要进行对应的数据绑定和保存。以下是常见的两种方式。 1. 绑定数据库数据源 2. 直接输入的list集合。 3. 出现的问题 问题:使用datagridviewcomboboxcolumn时,默认第一次以及将鼠标点击到当前行时&#…

ipad有必要用手写笔吗?性价比电容笔排行榜

随着技术的进步,各种新型的数字电子产品不断涌现。比如说,智能手机、ipad、电容笔之类的东西。但事实上,要将iPad的功能发挥到极致,我认为,这款电容笔,就必不可少的了。这就好像我们在ipad平板上书写东西&a…

QT运行界面与画布大小不一致问题(一步到位)

QT运行界面与画布大小不一致问题 出现的问题直接设置环境变量main函数中输入以下代码更改系统缩放比好了,看一下运行结果吧 出现的问题 当我们运行程序时,发现运行出来的大小和设计的几面大小有很大的差别,这使我们开发起来就特别的困难&…

【设计模式】七、适配器模式

文章目录 现实生活中的适配器例子基本介绍工作原理举例:类适配器模式类适配器模式介绍类适配器模式应用实例 举例:对象适配器模式基本思路对象适配器模式应用实例 举例:接口适配器模式接口适配器模式应用实例适配器模式在 SpringMVC 框架应用…

GPT-4V的图片识别和分析能力

GPT-4V是OpenAI开发的大型语言模型,是GPT-4的升级版本。GPT-4V在以下几个方面进行了改进: 模型规模更大:GPT-4V的参数量达到了1.37T,是GPT-4的10倍。训练数据更丰富:GPT-4V的训练数据包括了1.56T的文本和代码数据。算…

国有企业合同领域的合规管理

随着中国经济的快速发展和市场经济体制的完善,国有企业在国民经济中的地位和作用日益凸显。作为经济主体之一,国有企业必须积极适应市场环境的变化,加强合同管理,提高合规水平,以实现持续发展目标。本文将围绕国有企业…

pycharm设置pyuic和pyrcc

pyuic设置 适合任何虚拟环境,直接用虚拟环境的python解决一切。。。 E:\anaconda3\envs\qt5\python.exe-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py$FileDir$pyrcc设置 E:\anaconda3\envs\qt5\python.exe-m PyQt5.pyrcc_main $FileName$ -o…