当前位置:   article > 正文

YOLOV5目标检测换多种主干的方法(含shufflenet,ghost net,mobilenet,resnet等)_yolov5骨干和检测头更换

yolov5骨干和检测头更换

一,YOLOv5主干换为ghostnet

其实换主干是一个较为简单的操作方法,但是很多新手可能不太会,或者其中可能会出现一些问题,那我对这些问题归纳出三步走换主干的方法,以便和大家分享交流。我们这里以yolov5s为例。

1,找到想要替换的新的主干网络的基本结构组成,并将其复制到models/common.py文件中。与yolov5拥有C3,Conv这些基本组成结构相似,ghostnet也有类似的基本结构,它们分别是Ghostconv,Ghostbottleneck和C3Ghost,实际上他们对应的就是主干的不同级别的单位组成。将它们放置在models/common.py中的150行以后,找个位置插入即可。

  1. class GhostConv(nn.Module):
  2. def __init__(self, c1, c2, k=1, s=1, g=1, act=True):
  3. super(GhostConv, self).__init__()
  4. c_ = c2 // 2
  5. self.cv1 = Conv(c1, c_, k, s, None, g, act)
  6. self.cv2 = Conv(c_, c_, 5, 1, None, c_, act)
  7. def forward(self, x):
  8. y = self.cv1(x)
  9. return torch.cat([y, self.cv2(y)], 1)
  10. class GhostBottleneck(nn.Module):
  11. def __init__(self, c1, c2, k=3, s=1):
  12. super().__init__()
  13. c_ = c2 // 2
  14. self.conv = nn.Sequential(
  15. GhostConv(c1, c_, 1, 1),
  16. DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(),
  17. GhostConv(c_, c2, 1, 1, act=False))
  18. self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False), Conv(c1, c2, 1, 1,
  19. act=False)) if s == 2 else nn.Identity()
  20. def forward(self, x):
  21. return self.conv(x) + self.shortcut(x)
  22. class C3Ghost(C3):
  23. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
  24. super().__init__(c1, c2, n, shortcut, g, e)
  25. c_ = int(c2 * e)
  26. self.m = nn.Sequential(*(GhostBottleneck(c_, c_) for _ in range(n)))

2,在结构搭建脚本models/yolo.py添加上我们新增加的模块名称,以便搭建网络时可以直接调用。不同的版本这一块的内容可能不完全相同,我们要做的就是把Ghostconv,Ghostbottleneck和C3Ghost补全进去即可。

3,接下来就是核心步骤了,大家可以直接在models/yolov5s.yaml里面进行修改,或者重命名一个文件,但重命名的话记得在train.py里面改一下路径,这里就简单的直接进行修改。因为我们只修改主干,因此只需要改动backbone这里的内容即可。实际上来讲,这里可操作的空间很大,大家只要保证P3,P4,P5层的下采样倍数和通道数是没问题的就行,其他中间具体的结构其实改了也不会报错,所以有兴趣的小伙伴不如炼丹试试,下面我分享自己进行改进的内容:

之后直接去运行train.py即可。

二,yolov5主干换为shufflenet

1,这里依旧是三步走的步骤,对common文件进行模块内容的添加。

  1. def channel_shuffle(x, groups):
  2. batchsize, num_channels, height, width = x.data.size()
  3. channels_per_group = num_channels // groups
  4. x = x.view(batchsize, groups, channels_per_group, height, width)
  5. x = torch.transpose(x, 1, 2).contiguous()
  6. x = x.view(batchsize, -1, height, width)
  7. return x
  8. class stem(nn.Module):
  9. def __init__(self, c1, c2):
  10. super(stem, self).__init__()
  11. self.conv = nn.Sequential(
  12. nn.Conv2d(c1, c2, kernel_size=3, stride=2, padding=1, bias=False),
  13. nn.BatchNorm2d(c2),
  14. nn.ReLU(inplace=True),
  15. )
  16. self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
  17. def forward(self, x):
  18. return self.maxpool(self.conv(x))
  19. class Shuffle_Block(nn.Module):
  20. def __init__(self, ch_in, ch_out, stride):
  21. super(Shuffle_Block, self).__init__()
  22. if not (1 <= stride <= 2):
  23. raise ValueError('illegal stride value')
  24. self.stride = stride
  25. branch_features = ch_out // 2
  26. assert (self.stride != 1) or (ch_in == branch_features << 1)
  27. if self.stride > 1:
  28. self.branch1 = nn.Sequential(
  29. self.depthwise_conv(ch_in, ch_in, kernel_size=3, stride=self.stride, padding=1),
  30. nn.BatchNorm2d(ch_in),
  31. nn.Conv2d(ch_in, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
  32. nn.BatchNorm2d(branch_features),
  33. nn.ReLU(inplace=True),
  34. )
  35. self.branch2 = nn.Sequential(
  36. nn.Conv2d(ch_in if (self.stride > 1) else branch_features,
  37. branch_features, kernel_size=1, stride=1, padding=0, bias=False),
  38. nn.BatchNorm2d(branch_features),
  39. nn.ReLU(inplace=True),
  40. self.depthwise_conv(branch_features, branch_features, kernel_size=3, stride=self.stride, padding=1),
  41. nn.BatchNorm2d(branch_features),
  42. nn.Conv2d(branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
  43. nn.BatchNorm2d(branch_features),
  44. nn.ReLU(inplace=True),
  45. )
  46. @staticmethod
  47. def depthwise_conv(i, o, kernel_size, stride=1, padding=0, bias=False):
  48. return nn.Conv2d(i, o, kernel_size, stride, padding, bias=bias, groups=i)
  49. def forward(self, x):
  50. if self.stride == 1:
  51. x1, x2 = x.chunk(2, dim=1)
  52. out = torch.cat((x1, self.branch2(x2)), dim=1)
  53. else:
  54. out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)
  55. out = channel_shuffle(out, 2)
  56. return out

2,对models/yolo.py进行添加这几个模块名称。

3,修改models/yolov5s.yaml文件内容

这就完成了。

三,yolov5主干改为resnet

1,仍然是添加新的模块进入models/common.py,你们用的时候尽量直接复制,尽量不要修改。

  1. class BasicBlock(nn.Module):
  2. expansion = 1
  3. def __init__(self, in_channel, out_channel, stride=1, downsample=None, **kwargs):
  4. super(BasicBlock, self).__init__()
  5. self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel,
  6. kernel_size=3, stride=stride, padding=1, bias=False)
  7. self.bn1 = nn.BatchNorm2d(out_channel)
  8. self.relu = nn.ReLU()
  9. self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel,
  10. kernel_size=3, stride=1, padding=1, bias=False)
  11. self.bn2 = nn.BatchNorm2d(out_channel)
  12. self.downsample = nn.Conv2d(in_channels=in_channel, out_channels=out_channel,
  13. kernel_size=3, stride=2, padding=1, bias=False)
  14. def forward(self, x):
  15. identity = x
  16. if self.downsample is not None:
  17. identity = self.downsample(x)
  18. out = self.conv1(x)
  19. out = self.bn1(out)
  20. out = self.relu(out)
  21. out = self.conv2(out)
  22. out = self.bn2(out)
  23. out += identity
  24. out = self.relu(out)
  25. return out
  26. class Bottleneck1(nn.Module):
  27. """
  28. 注意:原论文中,在虚线残差结构的主分支上,第一个1x1卷积层的步距是2,第二个3x3卷积层步距是1。
  29. 但在pytorch官方实现过程中是第一个1x1卷积层的步距是1,第二个3x3卷积层步距是2,
  30. 这么做的好处是能够在top1上提升大概0.5%的准确率。
  31. 可参考Resnet v1.5 https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch
  32. """
  33. expansion = 1
  34. def __init__(self, in_channel, out_channel, stride=1, downsample=None,
  35. groups=1, width_per_group=64):
  36. super(Bottleneck1, self).__init__()
  37. width = int(out_channel * (width_per_group / 64.)) * groups
  38. self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=width,
  39. kernel_size=1, stride=1, bias=False) # squeeze channels
  40. self.bn1 = nn.BatchNorm2d(width)
  41. # -----------------------------------------
  42. self.conv2 = nn.Conv2d(in_channels=width, out_channels=width, groups=groups,
  43. kernel_size=3, stride=stride, bias=False, padding=1)
  44. self.bn2 = nn.BatchNorm2d(width)
  45. # -----------------------------------------
  46. self.conv3 = nn.Conv2d(in_channels=width, out_channels=out_channel*self.expansion,
  47. kernel_size=1, stride=1, bias=False) # unsqueeze channels
  48. self.bn3 = nn.BatchNorm2d(out_channel*self.expansion)
  49. self.relu = nn.ReLU(inplace=True)
  50. self.downsample = downsample
  51. def forward(self, x):
  52. identity = x
  53. if self.downsample is not None:
  54. identity = self.downsample(x)
  55. out = self.conv1(x)
  56. out = self.bn1(out)
  57. out = self.relu(out)
  58. out = self.conv2(out)
  59. out = self.bn2(out)
  60. out = self.relu(out)
  61. out = self.conv3(out)
  62. out = self.bn3(out)
  63. out += identity
  64. out = self.relu(out)
  65. return out

2,添加模块名称,你们注意这里是Bottleneck1,不要忘了后面有1.

3,修改配置文件,我并没有严格按照resnet的层数堆,如果想要很标准的话,可以自己查一下每个模块堆叠多少次。

最后,新人求关注,才开始搞这些,没什么经验,求大家多多关注多多收藏,以后会做质量更高的,谢谢大家

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/170960
推荐阅读
相关标签
  

闽ICP备14008679号