前言
在目标检测任务中,有两大系列的模型是咱们经常会碰见的;一类是 the one-stage,另一类是 the two-stage。在the one-stage中 检测速率较the two-stage有大的提升,但是在精度上却不如the two-stage。工程中寻求能够在边缘设备上能够保持较快的检测出来的一起精度也能够得到确保。
与此一起,GSConv应运而生,能够很好的满意上述的需求。在GSConv中引入了一种新的轻量级卷积技术GSConv,在保持模型精度的前提下,对模型进行了轻量化,满意了模型的准确性和速度之间完成了极好的平衡
GSConv简述
在诸多网络中单看精度的话,能够发现随着精度的增加,网络的层数会随着精度的增加而增加,但带来的明显影响就是速率的下降。随着Xception、MobileNets和ShuffleNets 这类轻量化模型中经过DSC操作大大提高了检测器的速度,但是在精度上却有较大的缺陷。
为了满意精度和速率的一起统筹,选用SC、DSC和shuffle混合卷积进行组合起来构建为一个新的卷积层,咱们能够直接看成果:
- 该办法使卷积核算的输出尽可能挨近SC,下降了核算量;
- 具有标准骨干的细长颈部;
无论是the one-stage 还是the two-stage,它们的中心基础的为CNN根本结构。关于轻量级模型,在建立网络结构的时候能够直接用GSConv层替换原始的卷积层,无需额外操作即可取得明显的精度增益。
GSConv结构
import torch
import torch.nn as nn
class SiLU(nn.Module):
@staticmethod
def forward(x):
return x * torch.sigmoid(x)
def autopad(k, p=None): # kernel, padding
# Pad to 'same'
if p is None:
p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
return p
class Conv(nn.Module):
# Standard convolution
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 = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
self.bn = nn.BatchNorm2d(c2)
self.act = SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
def forward(self, x):
return self.act(self.bn(self.conv(x)))
def forward_fuse(self, x):
return self.act(self.conv(x))
class GSConv(nn.Module):
# GSConv https://github.com/AlanLi1997/slim-neck-by-gsconv
def __init__(self, c1, c2, k=1, s=1, g=1, act=True):
super().__init__()
c_ = c2 // 2
self.cv1 = Conv(c1, c_, k, s, None, g, act)
self.cv2 = Conv(c_, c_, 5, 1, None, c_, act)
def forward(self, x):
x1 = self.cv1(x)
x2 = torch.cat((x1, self.cv2(x1)), 1)
# shuffle
# y = x2.reshape(x2.shape[0], 2, x2.shape[1] // 2, x2.shape[2], x2.shape[3])
# y = y.permute(0, 2, 1, 3, 4)
# return y.reshape(y.shape[0], -1, y.shape[3], y.shape[4])
b, n, h, w = x2.data.size()
b_n = b * n // 2
y = x2.reshape(b_n, 2, h * w)
y = y.permute(1, 0, 2)
y = y.reshape(2, -1, n // 2, h, w)
return torch.cat((y[0], y[1]), 1)
AlexNet-GSConv结构与试验
这里咱们选用数据集为鲜花数据集的改版进行试验,数据集的信息如下表格中所示。关于训练网络部分的代码,大家能够自行参阅# 【基础实操】借用torch自带网络进行训练自己的图画数据
参数 | 训练集 | 测试集 |
---|---|---|
nc | 5 | 5 |
size | 224 x 224 | 224 x 224 |
channel | 3 | 3 |
num | 300 | 210 |
网络结构
import torch
import torch.nn as nn
from nets import GSConv
class AlexNet(nn.Module):
def __init__(self, num_classes: int = 1000) -> None:
super(AlexNet, self).__init__()
self.features = nn.Sequential(
# nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
GSConv(c1=3, c2=64, k=11, s=4),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
# nn.Conv2d(64, 192, kernel_size=5, padding=2),
GSConv(c1=64, c2=192, k=5),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
# nn.Conv2d(192, 384, kernel_size=3, padding=1),
GSConv(c1=192, c2=384, k=3),
nn.ReLU(inplace=True),
# nn.Conv2d(384, 256, kernel_size=3, padding=1),
GSConv(c1=384, c2=256, k=3),
nn.ReLU(inplace=True),
# nn.Conv2d(256, 256, kernel_size=3, padding=1),
GSConv(c1=256, c2=256, k=3),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(256 * 6 * 6, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
结尾
值得注意的是,随着渠道核算能力的增长,GSConv的优势变得不那么明显。GSConv由于核算耗费和内存占用较小,更适合于边缘核算设备。总之,如果一起考虑模型的本钱和功能,选用GSConv将是一个正确的挑选。
参阅
[1] github.com/alanli1997/…
[2] arxiv.org/abs/2206.02…