YOLOv5之Focus与6×6卷积的理解

YOLOv5之Focus与6×6卷积的理解最近正在学习 yolov5 算法 以 yolov5s 模型为例 其中的 foucs 模块有何作用 之后被改成了一个 k 6 的卷积层 为什么较大的卷积核进行卷积比 focus 模块更有效呢 focus 模块

大家好,欢迎来到IT知识分享网。

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

最近正在学习yolov5算法,以yolov5s模型为例,其中的focus模块在被改成了一个kernel_size=6的卷积层,为什么较大的卷积核进行卷积比focus模块更有效呢?

一、Foucs

1.Focus简介

Foucs模块使将原本的feature map按下图方式分成四份,再concat到一起进行一次卷积.
Foucs
这里是Focus模块的源码:

class Focus(nn.Module): # Focus wh information into c-space def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups super().__init__() self.conv = Conv(c1 * 4, c2, k, s, p, g, act) def forward(self, x): # x(b,c,w,h) -> y(b,4c,h/2,w/2) return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1)) 

为了理解torch.cat()中拼接部分具体操作,我们需要自己做个小实验验证一下:

import torch import numpy as np import torch.nn as nn # foucs 结构 a = torch.tensor([[[[0,1],[2,3]], [[0,1],[2,3]], [[0,1],[2,3]], ]]) print(a.size()) # torch.Size([1, 3, 2, 2]) (N,C,H,W) print(a[...].size()) # ...第0维上所有都取 torch.Size([1, 3, 2, 2]) print(a[...,:2,:2].size()) # :2 取该维度上前两位 torch.Size([1, 3, 2, 2]) print(a[...,::2,::2].size()) # ::2 取该维度上总数/2 torch.Size([1, 3, 1, 1]) print(a[...,::2,::2].size(), '\n', a[...,::2,::2]) # torch.Size([1, 3, 1, 1]) 取0 print(a[...,1::2,::2].size(), '\n', a[...,1::2,::2]) # torch.Size([1, 3, 1, 1]) 取2 print(a[...,::2,1::2].size(), '\n', a[...,::2,1::2]) # torch.Size([1, 3, 1, 1]) 取1 print(a[...,1::2,1::2].size(), '\n', a[...,1::2,1::2]) # torch.Size([1, 3, 1, 1]) 取3 b = torch.cat([a[...,::2,::2], a[...,1::2,::2] ,a[...,::2,1::2], a[...,1::2,1::2]],1) # 最后的1代表在通道数上拼接 print(b.size())# torch.Size([1, 12, 1, 1]) print(b) # tensor([[[[0]], [[0]],[[0]],[[2]],[[2]], [[2]],[[1]],[[1]],[[1]],[[3]],[[3]],[[3]]]]) 

a.shape[1, 3, 2, 2]变至[1, 12, 1, 1],通道数增大四倍,w与h缩小两倍.
a[...,::2,::2] 取左上角
a[...,1::2,::2]取左下角
a[...,::2,1::2]取右上角
a[...,1::2,1::2]取右下角
再通过torch.cat()其在第二个维度上堆叠起来,在对其进行kernel_size=3,stride=1卷积特征提取.




2.对Focus的疑问

github上有人问过focus对mAP的影响,原作者是这么回答的
在这里插入图片描述
大意是Foucs()模块是为了减少了FLOPs并增加计算速度设计的,并不会增加mAP;另一方面1个foucs模块代替了3个yolov3/4层.
我们来计算一下focus的参数量与计算量(忽略bias)
F L O P s = ( 3 ∗ 4 ) ∗ 32 ∗ 3 ∗ 3 ∗ 320 ∗ 320 = 353894400 FLOPs = (3*4)*32*3*3*320*320=353894400 FLOPs=(34)3233320320=353894400
P a r a m = 3 ∗ 4 ∗ 32 ∗ 3 ∗ 3 = 3456 Param =3*4*32*3*3=3456 Param=343233=3456
再来计算一下卷积层的参数量与计算量(忽略bias)
F L O P s = 3 ∗ 32 ∗ 3 ∗ 3 ∗ 320 ∗ 320 = 88473600 FLOPs = 3*32*3*3*320*320=88473600 FLOPs=33233320320=88473600
P a r a m = 3 ∗ 32 ∗ 3 ∗ 3 = 864 Param =3*32*3*3=864 Param=33233=864
一个Focus的参数量与计算量大约是conv的4倍,但一个focus结构可以替换3个conv层,所以参数量与计算量都是减少的.
再来计算一下k=6卷积层的参数量与计算量(忽略bias)
F L O P s = 3 ∗ 32 ∗ 6 ∗ 6 ∗ 320 ∗ 320 = 35389440 FLOPs = 3*32*6*6*320*320=35389440 FLOPs=33266320320=35389440
P a r a m = 3 ∗ 32 ∗ 6 ∗ 6 = 3456 Param =3*32*6*6=3456 Param=33266=3456
所以k=6卷积层在理论上等价于focus结构
那么focus虽然降低了计算成本,但是其采样方式会不会破坏图片的空间信息或者混淆GT框的边界坐标呢?
在这里插入图片描述
将空间信息堆叠到通道空间中,可能会减少一个像素的回归信息价值,但对于大多数实例而言回归精度都不会接近一个像素,因此focus的采样方式并对回归精度产生影响极小.另外focus结构的位置问题,在分类网络中放在后面可能会更有用,但是在目标检测模型中对mAP@0.5:0.95的影响更大,mAP@0.5影响较小.
对于focus的采样方式是否会破坏原有图像的空间信息,我们只要将focus的采样的4个模块输出来看一看即可.
















import cv2 import numpy as np img = cv2.imread(r'C:/Users/HP/Desktop/p.jpg') # print(img.shape,"\n",img) # img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) new_img = cv2.resize(img, (320, 320)) cv2.imwrite(r'./0.jpg',new_img) img = img.transpose(2,1,0) img = np.expand_dims(img, 0) print(img.shape) # (1, 3, 640, 640) # cv2.imshow('img',img) # cv2.wimgitKey(0) # print(img[...,::2,::2].size(), '\n', img[...,::2,::2]) # print(img[...,1::2,::2].size(), '\n', img[...,1::2,::2]) # print(img[...,::2,1::2].size(), '\n', img[...,::2,1::2]) # print(img[...,1::2,1::2].size(), '\n', img[...,1::2,1::2]) new_img = img[...,::2,::2] print(new_img.shape) # (1, 3, 160, 160) new_img = new_img[0] print('shape',new_img.shape) # shape (3, 160, 160) new_img = new_img.transpose(2,1,0) # 维度转换 cv2.imwrite(r'./1.jpg',new_img) new_img = img[...,1::2,::2] print(new_img.shape) # (1, 3, 320, 320) new_img = new_img[0] new_img = new_img.transpose(2,1,0) cv2.imwrite(r'./2.jpg',new_img) new_img = img[...,::2,::2] print(new_img.shape) # (1, 3, 320, 320) new_img = new_img[0] new_img = new_img.transpose(2,1,0) cv2.imwrite(r'./3.jpg',new_img) new_img = img[...,1::2,1::2] print(new_img.shape) # (1, 3, 320, 320) new_img = new_img[0] new_img = new_img.transpose(2,1,0) cv2.imwrite(r'./4.jpg',new_img) img1 = cv2.imread(r'./1.jpg') img2 = cv2.imread(r'./2.jpg') img3 = cv2.imread(r'./3.jpg') img4 = cv2.imread(r'./4.jpg') h = np.hstack((img1, img2, img3, img4)) cv2.imwrite(r'./5.jpg',h) 

原图3x640x640:
在这里插入图片描述
focus模块下采样后四张图片3x320x320:
在这里插入图片描述
通过两幅图片对比得知,经过focus下采样得到的特征图并不会丢失特征信息,对语义的影响并不大,且下采样后的每一个像素点的感受野都增大了,与卷积层的下采样方式一样,没有丢失原图的信息.



二.Focus为何又被替换成卷积层

yolov5后期将focus结构又替换成了k=6的卷积层,我猜想可能是因为这样的方式来代替3个conv层的原因是在提高了运算速度前提下,运用较大的卷积核增大了感受野,在性能方面可能会获得了一定的提升.
另一方面,考虑到focus的切片处理,与卷积相比,多出了几次运算concat操作,可能会增加一定的内存开销.

三.Focus是否可以作为一种数据增强的处理方式

Focus可以被看作一种下采样方式,与transforms.Resize相比哪种方式的效果更好,或是可以在调整图片大小时将两种方式混用,在以后的实验中可以一试.

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/104256.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信