一、前言

主要是遇见几个问题,赶紧记录一下,免得后面兄弟们吃同样的亏,也帮助自己记忆。附上我的 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相关推荐

  1. 【华为出品】物联网全栈开发实战营第2期来啦!送海思开发板

    物联网一直在改变我们生活的方方面面.可穿戴设备有助于监控我们的偏好.习惯和健康状况.智能家居设备可提高家居的舒适度.安全性和便利性.城市利用连接的数字设备收集的海量数据(数十亿个)来改善城市规划.制造 ...

  2. 海思开发板海图科技测评,海图海思开发板怎么样

    关于海思开发板海图科技的测评,相关关键字:hi3516,hi3519,hi3531,hi3535,hi3536,hi3559,hi3403 最近网络上关于海图的海思开发板宣传的沸沸扬扬,很多人都想要一 ...

  3. 开源海思开发板(HIVIEW开发板)

    大家好,今天给大家分享一个有料的海思开发板,可用于快速构建产品样机. HIVIEW简介: HIVIEW是个小而专业的设计公司(海思软/硬件设计能力13年以上)     我们的目标 **以开放的软/硬件 ...

  4. 海思开发板FFmpeg+Nginx,推流RTMP播放(优秀教程收集+实操整理)

    海思开发板FFmpeg+Nginx推流RTSP播放(优秀教程收集+实操整理) 安装FFmpeg及移植 FFmpeg编译问题收录: static declaration of 'cbrt' follow ...

  5. 海思开发板上添加ftp功能

    海思开发板上添加ftp功能 前言: 本博客是基于海思3520Dv100开发,编译工具链为:arm-hisiv100nptl-linux-gcc 1.下载stupid-ftpd-1.5beta.tar. ...

  6. mysql移植海思_live555 交叉编译移植到海思开发板

    1.首先到它的主页下载一个源码包: http://www.live555.com/liveMedia/public/ 我下载的是latest的,具体什么版本还真不清楚 2.放到linux目录下解压: ...

  7. vmware虚拟机中ubuntu系统里设置USB串口连接海思开发板

    vmware虚拟机中ubuntu系统里设置USB串口连接海思开发板 作为海思平台的一个新手,在学习海思开发板时,会因为一个很小的问题折腾半天,但是当解决一个小问题时还是很兴奋的,记录下自己学习过程. ...

  8. 海思开发板实用技巧集

    1. 海思开发板默认状态下自己不能ping通自己,需要 ifconfig lo up,之后就可以自己ping通自己了.如下所示: /app/komod # ping 192.168.1.21 PING ...

  9. 为什么你们说好的程序在我的海思开发板上就是不行呢,难道真的是人品有问题

    就下面的程序,一个利用libpcap做的抓包程序,一个用raw socket 做的抓包程序 海思开发板 是通过映射过来的,被映射的是宝利通mcu rmx1800,终端目前用的是软终端,硬件终端还在路上 ...

最新文章

  1. 数据库字段名不统一的教训
  2. Activity查看数据与文本框反回数据
  3. RV1108调试串口参数设置
  4. Java如何实现跨平台?原理是怎样的?
  5. SAP 电商云 Spartacus UI 修改代码后,重新构建基于 SSR 版本的程序报错
  6. Redis源码解析:21sentinel(二)定期发送消息、检测主观下线
  7. oracle 导入sql文件 汉字乱码_将现有的sql脚本导入 Oracle 数据库,中文乱码问题...
  8. 看FusionInsight Spark如何支持JDBCServer的多实例特性
  9. PowerDesigner--创建概念数据模型;并生成逻辑数据模型/物理数据模型/数据库SQL脚本
  10. 百度地图之离线下载功能
  11. python数据透视表怎么存下来_大数据分析如何利用Python创建数据透视表?
  12. 连接服务器失败请检查配置文件,连接服务器失败请检查网络
  13. IC在測試生產過程的靜電放電(ESD)擊傷及電性過壓(EOS)現象
  14. 会员营销中,沉寂会员的三种运营策略
  15. 身份证提取生日和性别
  16. Python Selenium 字节校招实习岗筛选
  17. 智慧物流wms仓储管理系统
  18. Vue_01_组件的使用
  19. numpy创建空数组
  20. 广和通5G模组再添新翼,FG650-EAU蓄势扬帆出海

热门文章

  1. python爬app西瓜视频_Python爬虫工程师面试题,采集头条西瓜视频
  2. codeforces 868C - Qualification Rounds
  3. 「GoTeam 招聘时间」ANKER Golang 开发工程师(深圳)
  4. Anker 拓展坞连接网线经常断网问题
  5. 百度搜索引擎结果网址参数 搜索框提示次数(rsv_sug1)
  6. 线上连锁线下整合 连锁电商建设方案
  7. NFT交易平台2.0来了,源代码,智能合约整套
  8. 百度网盘mac损害计算机,Mac 电脑百度网盘一直闪退怎么办?解决办法
  9. 打叉图标html,SVG 勾号和叉号图标
  10. 读《拆掉思维的墙》小记