海思开发:yolo v5s :pytorch->onnx->caffe->nnie
一、前言
主要是遇见几个问题,赶紧记录一下,免得后面兄弟们吃同样的亏,也帮助自己记忆。附上我的 yolo v5 后处理部分 c 语言版本代码:C版 yolo v5s 后处理部分
二、转换报错
1. 报错:
Reshape dimention number shall be 2 or 4
仔细看了一下,和我 reshape 处理的数据维度有关,而转换代码里 reshape 最高支持维度数是 4。而我的数据shape 是 (1, 3, H, W, class_num + 5),这是个五维数组。
本来就想在后面加个条件 : len(shape) == 5,又怕出现新的错误,上网找了篇其他的转换代码,点进去看了下,发现内容基本一致,只不过少了个 len(shape) == 3。看完就明白可能我现在用的demo也是作者自己做了些许改动,而且他遇见的问题恐怕和我一样。于是放心的加上 len(shape) == 5
,再次运行demo。
2. 报错:
Permute dimention number shall be 2 or 4
第二个错误与上面一致,轻车熟路,在后面加个条件。
3. 报错:
Check failed: num_output_ % group_ == 0 (1 vs. 0) Number of output should be multiples of group.
具体报错如上,就是说分组卷积/分组反卷积有个硬性要求是 input_channel 与 output_channel 要是 group 的整数倍,只不过转换原因,使得这里的 output_channel 等于 1 。直接看转换的源码。
框里面的代码原本是 num_output=W.shape[1],而我反卷积是用了分组卷积,w.shape = (512, 1, 2, 2),这样就不对了,output num 应该是 512 才对,进行截图中的改正。再次运行,转换完成!下面跑几张图片具体看看输出值是否一致。
4. caffe 转 wk 模型报错
Permute can only support (0,1,2,3) -> (0,2,3,1) order!
reshape only support C/H/W, not N!
这两个错误不是一起出现的,犯错原因差不多,就是模型的 prototxt 文件格式不符合 nnie 要求。先说说下面一个,即 Permute 层,临时翻海思文档,描述如下:
可以看见 Permute层 局限性蛮大的,据群友们反映,这个op还挺慢的, 结合实际情况,考虑去掉它,即在 prototxt文件里删去它。
至于 reshape 层,在文档里没找见它,只能各种方法都试试,最后发现正确格式是这种。
改为下面这种格式就好了:
之后再进行转换,就没什么问题,静待模型转换完成了。
三、 其他知识点
写这个项目时,遇到的一些问题,觉得有必要和大家讲下,帮助自己记忆,也给大伙一个经验来参考。
1. reshape 的维度与海思输出数据内存分布
先看看源码的输出数据 shape 变化过程,见 yolov5/model/yolo.py 文件:
bs, _, ny, nx = x[i].shape # x(bs, 85 * 3, 20, 20) to x(bs, 3, 20, 20, 85)
# contiguous() : https://zhuanlan.zhihu.com/p/64551412
x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
我的训练数据集是48类,则其中一个层的输出 shape 为 (1, 159, 20, 20),其 shape 变化过程为:
# reshape permute
(1, 159, 20, 20) ---------> (1, 3, 53, 20, 20) ---------> (1, 3, 20, 20, 53)
因为python版输出数据维度过高,海思不支持,且海思 permute op 先天性限制,无法支持更多的操作,所以取消 permute 是在所难免的。但是怎样获得我们需要的 shape ,一开始想着无脑 reshape 成需要的现状,如:
# reshape reshape
(1, 159, 20, 20) ---------> (0, 3, 53, 400) ---------> (0, 3, 400, 53)
# 以上是错误操作,不要用,海思 reshape 要求输出第一维是 0 且最多维度数为 4 .
这样看起来很顺利,不过仔细想想 permute 的机制与 reshape 不一样,为此特意做了个实验:
a = torch.arange(60).view((1, 15, 2, 2)).view((1, 3, 5, 2, 2)).permute(0, 1, 3, 4, 2).contiguous()
print(a.shape)
print(a)
# output:
torch.Size([1, 3, 2, 2, 5]) # shape
# data
tensor([[[[[ 0, 4, 8, 12, 16],[ 1, 5, 9, 13, 17]],[[ 2, 6, 10, 14, 18],[ 3, 7, 11, 15, 19]]],[[[20, 24, 28, 32, 36],[21, 25, 29, 33, 37]],[[22, 26, 30, 34, 38],[23, 27, 31, 35, 39]]],[[[40, 44, 48, 52, 56],[41, 45, 49, 53, 57]],[[42, 46, 50, 54, 58],[43, 47, 51, 55, 59]]]]])
d = torch.arange(60).view((1, 15, 2, 2)).view((1, 3, 2, 2, 5))
print(d.shape)
print(d)
# output
torch.Size([1, 3, 2, 2, 5]) # shape
# data
tensor([[[[[ 0, 1, 2, 3, 4],[ 5, 6, 7, 8, 9]],[[10, 11, 12, 13, 14],[15, 16, 17, 18, 19]]],[[[20, 21, 22, 23, 24],[25, 26, 27, 28, 29]],[[30, 31, 32, 33, 34],[35, 36, 37, 38, 39]]],[[[40, 41, 42, 43, 44],[45, 46, 47, 48, 49]],[[50, 51, 52, 53, 54],[55, 56, 57, 58, 59]]]]])
虽然 shape 一样,但其数值分布完全不一样,且二者之间的数据分布没有规律可循。所以,reshape 代替 permute 的想法是错误。但我们工作总要继续,不能就此放弃。
回到主题,观察发现,python 输出的数据 shape 与海思的输出 shape 最接近的是:
# reshape permute reshape
(1, 159, 20, 20) -----> (1, 3, 53, 20, 20) -----> (1, 3, 20, 20, 53) -----> (1, 3, 400, 53)
# python 版本输出# reshape
(1, 159, 20, 20) -----> (0, 3, 53, 400)
# 海思 版本输出
可以看见,二者已经很相像了,这样能否用呢?做个实验看下:
# 模拟 python 输出
a = torch.arange(60).view((1, 15, 2, 2)).view((1, 3, 5, 2, 2)).permute(0, 1, 3, 4, 2).contiguous().view((1, 3, 4, 5))
print(a.shape)
print(a)
# output
torch.Size([1, 3, 4, 5]) # shape
# data
tensor([[[[ 0, 4, 8, 12, 16], # 一行等于下面数组的一列[ 1, 5, 9, 13, 17],[ 2, 6, 10, 14, 18],[ 3, 7, 11, 15, 19]],
# ---------------------------------- anchor [[20, 24, 28, 32, 36],[21, 25, 29, 33, 37],[22, 26, 30, 34, 38],[23, 27, 31, 35, 39]],
# ---------------------------------- anchor [[40, 44, 48, 52, 56],[41, 45, 49, 53, 57],[42, 46, 50, 54, 58],[43, 47, 51, 55, 59]]]])# 模拟 海思输出
b = torch.arange(60).view((1, 15, 2, 2)).view((1, 3, 5, 4))
print(b.shape)
print(b)
# output
torch.Size([1, 3, 5, 4]) # shape
# data
tensor([[[[ 0, 1, 2, 3], # 一列等于上面数组的一行,而且顺序对应的上[ 4, 5, 6, 7],[ 8, 9, 10, 11],[12, 13, 14, 15],[16, 17, 18, 19]],
# ---------------------------------- anchor [[20, 21, 22, 23],[24, 25, 26, 27],[28, 29, 30, 31],[32, 33, 34, 35],[36, 37, 38, 39]],
# ---------------------------------- anchor [[40, 41, 42, 43],[44, 45, 46, 47],[48, 49, 50, 51],[52, 53, 54, 55],[56, 57, 58, 59]]]])
模拟数据 shape 与实际数据 shape 一 一对应, 3 对应 3,4 对应 400, 5 对应 53 。看上文数据,虽然 shape 不一样,但是在同一个 anchor 中,模拟海思数据的一列等于模拟python数据的一行,这就有意思了。可以看见,同样几个数据,在 python 里面是按行读取,而在海思里需要按列读取。再看看海思中数据存储方式,打开海思的文档,找到内存介绍部分:
海思的模拟数 shape 是 (1, 3, 5, 4),3是通道数,上图中的 Frame0_Chn0, Frame0_Chn1,5 是height,4是 width,可以发现其与打印的数据分布是一致的,这样就可以放心的取数据了。下面是我手绘的我的输出内存分布图,觉得还是有必要说清楚,嫌啰嗦就跳过吧。
以其中一个yolo layer 输出为例,输出 shape 为(0,3, 53, 100)。3 是 anchor 数目,输出结果的内存就是被分成上中下三大部分,如我上图所示。53 就是我们需要的 bbox + confidence + class_confidence 输出,由于没有permute的原因,它们在内存中的排布是上下分布的,这与我们平常按行读取结果的习惯不一致。100是 10x10 那张 feature map 拉直后的长度,即每个 anchor 格子里有100列,每一列代表每个 grid 的数据,而每列数据中又有53行,每行代表当前 grid 中的数据输出。这样的话,再结合我的代码,应该会明白许多。
2. 网络输出处理
yolo v5 与 yolo v3 不同的是,其网络输出不同于v3的那种方式,可以看源码:
y = x[i].sigmoid()
y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i] # xy
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
x[:, 5:] *= x[:, 4:5] # score = obj_conf * cls_conf
对于网络输出结果,先统一做 sigmoid 映射,然后x y w h 再做各自的变化,confidence 与 class_confidence 也是。
3. yolo v5 的 class 输出
因为yolo v5里面对于类别是用了多标签loss训练(不互斥多分类),即每个类别都是经 sigmoid 函数映射后输出,故不用比较大小,最后返回一个最大值对应索引。可看看源码的代码:
# Detections matrix nx6 (xyxy, conf, cls)
if multi_label:
# 注意,yolo v5 不是 softmax分类 ,所以不用遍历出概率最高的那个类别
i, j = (x[:, 5:] > conf_thres).nonzero().t() # 只做一个阈值筛选就好
x = torch.cat((box[i], x[i, j + 5, None], j[:, None].float()), 1)
作者只做了个阈值筛选就返回所需要的类别索引,如果你说这样会不会有两个类别及以上的情况出现,对于这种,我只能说你要么模型没训练好,要么取数据出现失误,一个训练好的模型是不会的。
4. yolo v5 的 nms 处理
还是先看源码, 作者的 nms 实现简单却高效:
# 作者是先拿类别索引乘以一个较大值 max_wh(4096)
c = x[:, 5:6] * (0 if agnostic else max_wh) # classes,5 是类别对应的索引
boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores
i = torchvision.ops.boxes.nms(boxes, scores, iou_thres)
作者针对每个类别都有一个 bbox偏移,在做 nms 之前,先给类别索引乘以一个较大值,再让bbox坐标值加上偏移值,这样意义是什么呢。试想每个类别都加上一个较大值,等同于每个类别都在专属于本类的坐标系里做 nms,这样做nms的时候,不用和以前一样按类别进行nms操作,而是直接做计算就行了。避免了那些 IOU>阈值 但不属于同一类的bbox被删除。
我的 demo 里没有用作者的 nms 方式,如果有兄弟实验后发现作者的更好,请告知我一声。
5. 海思版yolo v5网络修改
yolo v5一开源就看了下其结构,发现有些 op 海思不支持,所以在训练模型之初,就更改了一些,具体如下:
① 将 focus 层换成一个卷积,这个 op 海思上面不用想了(修正,自己试了下focus可以用caffe实现,海思也支持op, yolo v5 focus 层海思部署的可能性 ),欢迎各位朋友讨论;
② 将 leaky relu换成 relu,海思是支持 prelu 的,所以也支持它,不过群友们反映这个 op 很慢,输出还不稳定(这个我没有做实验,真伪性存疑),所以就干脆给它替换了;
③ 上采样层,海思支持的上采样层是 unpooling 方式,而 yolo v5里的上采样方式是最近邻插值(nearest),鉴于各种因素考虑,还是把它换成了分组转置卷积(分组这里要注意,yolo v5 网络其实大部分卷积都是深度卷积 + 逐点卷积,所以转置卷积也分组吧);
④ spp 层的 maxpool ceil mode 都是默认的 false 状态,而海思里的 caffe 只支持 ceil mode 方式,所以要改成 ceil mode = True 。一开始忘记打开使得输出bbox明显偏大(不敢确认是这个的原因),后来特意停止训练修改该参数,再继续训练,后来发现 bbox 正常了。
三、后言
仓促之下写成,如有不好之处,还请指出,谢谢。
四、致歉
在此说声抱歉,由于我的粗心与愚蠢,使得我自己包括其他兄弟的输出结果不正常,给一些兄弟带来很大麻烦。
类似上面这种问题,中途有兄弟也询问过是不是我后处理代码有误,可是在我再一次粗心下,没有发现。在最近一位兄弟的指正下,终于找到了这个错误,gitee仓库的代码已经修改,而且经一位网友验证,修改后模型输出结果能用!
这次给我的教训是:遇事多往自己身上找原因,人总是喜欢把失败归咎于别人,把成功归功于自己。
海思开发:yolo v5s :pytorch->onnx->caffe->nnie相关推荐
- 【华为出品】物联网全栈开发实战营第2期来啦!送海思开发板
物联网一直在改变我们生活的方方面面.可穿戴设备有助于监控我们的偏好.习惯和健康状况.智能家居设备可提高家居的舒适度.安全性和便利性.城市利用连接的数字设备收集的海量数据(数十亿个)来改善城市规划.制造 ...
- 海思开发板海图科技测评,海图海思开发板怎么样
关于海思开发板海图科技的测评,相关关键字:hi3516,hi3519,hi3531,hi3535,hi3536,hi3559,hi3403 最近网络上关于海图的海思开发板宣传的沸沸扬扬,很多人都想要一 ...
- 开源海思开发板(HIVIEW开发板)
大家好,今天给大家分享一个有料的海思开发板,可用于快速构建产品样机. HIVIEW简介: HIVIEW是个小而专业的设计公司(海思软/硬件设计能力13年以上) 我们的目标 **以开放的软/硬件 ...
- 海思开发板FFmpeg+Nginx,推流RTMP播放(优秀教程收集+实操整理)
海思开发板FFmpeg+Nginx推流RTSP播放(优秀教程收集+实操整理) 安装FFmpeg及移植 FFmpeg编译问题收录: static declaration of 'cbrt' follow ...
- 海思开发板上添加ftp功能
海思开发板上添加ftp功能 前言: 本博客是基于海思3520Dv100开发,编译工具链为:arm-hisiv100nptl-linux-gcc 1.下载stupid-ftpd-1.5beta.tar. ...
- mysql移植海思_live555 交叉编译移植到海思开发板
1.首先到它的主页下载一个源码包: http://www.live555.com/liveMedia/public/ 我下载的是latest的,具体什么版本还真不清楚 2.放到linux目录下解压: ...
- vmware虚拟机中ubuntu系统里设置USB串口连接海思开发板
vmware虚拟机中ubuntu系统里设置USB串口连接海思开发板 作为海思平台的一个新手,在学习海思开发板时,会因为一个很小的问题折腾半天,但是当解决一个小问题时还是很兴奋的,记录下自己学习过程. ...
- 海思开发板实用技巧集
1. 海思开发板默认状态下自己不能ping通自己,需要 ifconfig lo up,之后就可以自己ping通自己了.如下所示: /app/komod # ping 192.168.1.21 PING ...
- 为什么你们说好的程序在我的海思开发板上就是不行呢,难道真的是人品有问题
就下面的程序,一个利用libpcap做的抓包程序,一个用raw socket 做的抓包程序 海思开发板 是通过映射过来的,被映射的是宝利通mcu rmx1800,终端目前用的是软终端,硬件终端还在路上 ...
最新文章
- 数据库字段名不统一的教训
- Activity查看数据与文本框反回数据
- RV1108调试串口参数设置
- Java如何实现跨平台?原理是怎样的?
- SAP 电商云 Spartacus UI 修改代码后,重新构建基于 SSR 版本的程序报错
- Redis源码解析:21sentinel(二)定期发送消息、检测主观下线
- oracle 导入sql文件 汉字乱码_将现有的sql脚本导入 Oracle 数据库,中文乱码问题...
- 看FusionInsight Spark如何支持JDBCServer的多实例特性
- PowerDesigner--创建概念数据模型;并生成逻辑数据模型/物理数据模型/数据库SQL脚本
- 百度地图之离线下载功能
- python数据透视表怎么存下来_大数据分析如何利用Python创建数据透视表?
- 连接服务器失败请检查配置文件,连接服务器失败请检查网络
- IC在測試生產過程的靜電放電(ESD)擊傷及電性過壓(EOS)現象
- 会员营销中,沉寂会员的三种运营策略
- 身份证提取生日和性别
- Python Selenium 字节校招实习岗筛选
- 智慧物流wms仓储管理系统
- Vue_01_组件的使用
- numpy创建空数组
- 广和通5G模组再添新翼,FG650-EAU蓄势扬帆出海
热门文章
- python爬app西瓜视频_Python爬虫工程师面试题,采集头条西瓜视频
- codeforces 868C - Qualification Rounds
- 「GoTeam 招聘时间」ANKER Golang 开发工程师(深圳)
- Anker 拓展坞连接网线经常断网问题
- 百度搜索引擎结果网址参数 搜索框提示次数(rsv_sug1)
- 线上连锁线下整合 连锁电商建设方案
- NFT交易平台2.0来了,源代码,智能合约整套
- 百度网盘mac损害计算机,Mac 电脑百度网盘一直闪退怎么办?解决办法
- 打叉图标html,SVG 勾号和叉号图标
- 读《拆掉思维的墙》小记