当前位置:   article > 正文

多种轻量化算法改进YOLOv5模型_yolov5改进

yolov5改进

改进主要分为三步:

1.将主代码添加至common文件中;

2.将主代码中的函数写入yolo.py文件中的parse_model函数;

3.重新配置相关.yaml文件

1.MobileNetv3

1.1主代码

  1. ############################################### MobileNet V3 #########################################################
  2. # ---------------------------- MobileBlock start -------------------------------
  3. class h_sigmoid(nn.Module):
  4. def __init__(self, inplace=True):
  5. super(h_sigmoid, self).__init__()
  6. self.relu = nn.ReLU6(inplace=inplace)
  7. def forward(self, x):
  8. return self.relu(x + 3) / 6
  9. class h_swish(nn.Module):
  10. def __init__(self, inplace=True):
  11. super(h_swish, self).__init__()
  12. self.sigmoid = h_sigmoid(inplace=inplace)
  13. def forward(self, x):
  14. return x * self.sigmoid(x)
  15. class SELayer(nn.Module):
  16. def __init__(self, channel, reduction=4):
  17. super(SELayer, self).__init__()
  18. # Squeeze操作
  19. self.avg_pool = nn.AdaptiveAvgPool2d(1)
  20. # Excitation操作(FC+ReLU+FC+Sigmoid)
  21. self.fc = nn.Sequential(
  22. nn.Linear(channel, channel // reduction),
  23. nn.ReLU(inplace=True),
  24. nn.Linear(channel // reduction, channel),
  25. h_sigmoid()
  26. )
  27. def forward(self, x):
  28. b, c, _, _ = x.size()
  29. y = self.avg_pool(x)
  30. y = y.view(b, c)
  31. y = self.fc(y).view(b, c, 1, 1) # 学习到的每一channel的权重
  32. return x * y
  33. class conv_bn_hswish(nn.Module):
  34. """
  35. This equals to
  36. def conv_3x3_bn(inp, oup, stride):
  37. return nn.Sequential(
  38. nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
  39. nn.BatchNorm2d(oup),
  40. h_swish()
  41. )
  42. """
  43. def __init__(self, c1, c2, stride):
  44. super(conv_bn_hswish, self).__init__()
  45. self.conv = nn.Conv2d(c1, c2, 3, stride, 1, bias=False)
  46. self.bn = nn.BatchNorm2d(c2)
  47. self.act = h_swish()
  48. def forward(self, x):
  49. return self.act(self.bn(self.conv(x)))
  50. def fuseforward(self, x):
  51. return self.act(self.conv(x))
  52. class MobileNet_Block(nn.Module):
  53. def __init__(self, inp, oup, hidden_dim, kernel_size, stride, use_se, use_hs):
  54. super(MobileNet_Block, self).__init__()
  55. assert stride in [1, 2]
  56. self.identity = stride == 1 and inp == oup
  57. # 输入通道数=扩张通道数 则不进行通道扩张
  58. if inp == hidden_dim:
  59. self.conv = nn.Sequential(
  60. # dw
  61. nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
  62. bias=False),
  63. nn.BatchNorm2d(hidden_dim),
  64. h_swish() if use_hs else nn.ReLU(inplace=True),
  65. # Squeeze-and-Excite
  66. SELayer(hidden_dim) if use_se else nn.Sequential(),
  67. # pw-linear
  68. nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
  69. nn.BatchNorm2d(oup),
  70. )
  71. else:
  72. # 否则 先进行通道扩张
  73. self.conv = nn.Sequential(
  74. # pw
  75. nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),
  76. nn.BatchNorm2d(hidden_dim),
  77. h_swish() if use_hs else nn.ReLU(inplace=True),
  78. # dw
  79. nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
  80. bias=False),
  81. nn.BatchNorm2d(hidden_dim),
  82. # Squeeze-and-Excite
  83. SELayer(hidden_dim) if use_se else nn.Sequential(),
  84. h_swish() if use_hs else nn.ReLU(inplace=True),
  85. # pw-linear
  86. nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
  87. nn.BatchNorm2d(oup),
  88. )
  89. def forward(self, x):
  90. y = self.conv(x)
  91. if self.identity:
  92. return x + y
  93. else:
  94. return y
  95. # ---------------------------- MobileBlock end ---------------------------------

1.2  .yaml配置文件

  1. # YOLOv5 v6.0 backbone
  2. backbone:
  3. # MobileNetV3-small 11层
  4. # [from, number, module, args]
  5. # MobileNet_Block: [out_ch, hidden_ch, kernel_size, stride, use_se, use_hs]
  6. # hidden_ch表示在Inverted residuals中的扩张通道数
  7. # use_se 表示是否使用 SELayer, use_hs 表示使用 h_swish 还是 ReLU
  8. [[-1, 1, conv_bn_hswish, [16, 2]], # 0-p1/2
  9. [-1, 1, MobileNet_Block, [16, 16, 3, 2, 1, 0]], # 1-p2/4
  10. [-1, 1, MobileNet_Block, [24, 72, 3, 2, 0, 0]], # 2-p3/8
  11. [-1, 1, MobileNet_Block, [24, 88, 3, 1, 0, 0]], # 3-p3/8
  12. [-1, 1, MobileNet_Block, [40, 96, 5, 2, 1, 1]], # 4-p4/16
  13. [-1, 1, MobileNet_Block, [40, 240, 5, 1, 1, 1]], # 5-p4/16
  14. [-1, 1, MobileNet_Block, [40, 240, 5, 1, 1, 1]], # 6-p4/16
  15. [-1, 1, MobileNet_Block, [48, 120, 5, 1, 1, 1]], # 7-p4/16
  16. [-1, 1, MobileNet_Block, [48, 144, 5, 1, 1, 1]], # 8-p4/16
  17. [-1, 1, MobileNet_Block, [96, 288, 5, 2, 1, 1]], # 9-p5/32
  18. [-1, 1, MobileNet_Block, [96, 576, 5, 1, 1, 1]], # 10-p5/32
  19. [-1, 1, MobileNet_Block, [96, 576, 5, 1, 1, 1]], # 11-p5/32
  20. ]
  21. # YOLOv5 v6.0 head

2.ShuffleNetV2

2.1主代码

  1. #############################################ShuffleNet V2############################################
  2. def shuffle_channel(x, num_groups):
  3. """channel shuffle 的常规实现
  4. """
  5. batch_size, num_channels, height, width = x.size()
  6. assert num_channels % num_groups == 0
  7. a=torch.div(num_channels,num_groups, rounding_mode='trunc')
  8. x = x.view(batch_size, num_groups, a, height, width)
  9. x = x.permute(0, 2, 1, 3, 4)
  10. return x.contiguous().view(batch_size, num_channels, height, width)
  11. class CBRM(nn.Module): #conv BN ReLU Maxpool2d
  12. def __init__(self, c1, c2): # ch_in, ch_out
  13. super(CBRM, self).__init__()
  14. self.conv = nn.Sequential(
  15. nn.Conv2d(c1, c2, kernel_size=3, stride=2, padding=1, bias=False),
  16. nn.BatchNorm2d(c2),
  17. nn.ReLU(inplace=True),
  18. )
  19. self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
  20. def forward(self, x):
  21. return self.maxpool(self.conv(x))
  22. class Shuffle_Block(nn.Module):
  23. def __init__(self, ch_in, ch_out, stride):
  24. super(Shuffle_Block, self).__init__()
  25. if not (1 <= stride <= 2):
  26. raise ValueError('illegal stride value')
  27. self.stride = stride
  28. branch_features = ch_out // 2
  29. assert (self.stride != 1) or (ch_in == branch_features << 1)
  30. if self.stride > 1:
  31. self.branch1 = nn.Sequential(
  32. self.depthwise_conv(ch_in, ch_in, kernel_size=3, stride=self.stride, padding=1),
  33. nn.BatchNorm2d(ch_in),
  34. nn.Conv2d(ch_in, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
  35. nn.BatchNorm2d(branch_features),
  36. nn.ReLU(inplace=True),
  37. )
  38. ##DWConv 深度可分离卷积=分离卷积+逐点卷积
  39. ##self.branch1=dwconv+Pointconv
  40. ##self.branch2=dwconv+pointconv
  41. self.branch2 = nn.Sequential(
  42. nn.Conv2d(ch_in if (self.stride > 1) else branch_features,
  43. branch_features, kernel_size=1, stride=1, padding=0, bias=False),
  44. nn.BatchNorm2d(branch_features),
  45. nn.ReLU(inplace=True),
  46. self.depthwise_conv(branch_features, branch_features, kernel_size=3, stride=self.stride, padding=1),
  47. nn.BatchNorm2d(branch_features),
  48. nn.Conv2d(branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
  49. nn.BatchNorm2d(branch_features),
  50. nn.ReLU(inplace=True),
  51. )
  52. @staticmethod
  53. def depthwise_conv(i, o, kernel_size, stride=1, padding=0, bias=False):
  54. return nn.Conv2d(i, o, kernel_size, stride, padding, bias=bias, groups=i) #分离卷积
  55. def forward(self, x):
  56. if self.stride == 1:
  57. x1, x2 = x.chunk(2, dim=1) # 按照维度1进行split
  58. out = torch.cat((x1, self.branch2(x2)), dim=1)
  59. else:
  60. out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)
  61. out = shuffle_channel(out, 2)
  62. return out

2.2  .yaml配置文件

  1. # YOLOv5 v6.0 backbone
  2. backbone:
  3. # [from, number, module, args]
  4. # Shuffle_Block: [out, stride]
  5. [[ -1, 1, CBRM, [ 32 ] ], # 0-P2/4
  6. [ -1, 1, Shuffle_Block, [ 128, 2 ] ], # 1-P3/8
  7. [ -1, 1, Shuffle_Block, [ 128, 1 ] ], # 2
  8. [ -1, 1, Shuffle_Block, [ 256, 2 ] ], # 3-P4/16
  9. [ -1, 1, Shuffle_Block, [ 256, 1 ] ], # 4
  10. [ -1, 1, Shuffle_Block, [ 512, 2 ] ], # 5-P5/32
  11. [ -1, 1, Shuffle_Block, [ 512, 1 ] ], # 6
  12. ]
  13. # YOLOv5 v6.0 head

3.EfficientNetv2

3.1主代码

  1. class stem(nn.Module):
  2. def __init__(self, c1, c2, kernel_size=3, stride=1, groups=1):
  3. super().__init__()
  4. # kernel_size为3时,padding 为1,kernel为1时,padding为0
  5. padding = (kernel_size - 1) // 2
  6. # 由于要加bn层,所以不加偏置
  7. self.conv = nn.Conv2d(c1, c2, kernel_size, stride, padding=padding, groups=groups, bias=False)
  8. self.bn = nn.BatchNorm2d(c2, eps=1e-3, momentum=0.1)
  9. self.act = nn.SiLU(inplace=True)
  10. def forward(self, x):
  11. # print(x.shape)
  12. x = self.conv(x)
  13. x = self.bn(x)
  14. x = self.act(x)
  15. return x
  16. def drop_path(x, drop_prob: float = 0., training: bool = False):
  17. if drop_prob == 0. or not training:
  18. return x
  19. keep_prob = 1 - drop_prob
  20. shape = (x.shape[0],) + (1,) * (x.ndim - 1)
  21. random_tensor = keep_prob + torch.rand(shape, dtype=x.dtype, device=x.device)
  22. random_tensor.floor_() # binarize
  23. output = x.div(keep_prob) * random_tensor
  24. return output
  25. class DropPath(nn.Module):
  26. def __init__(self, drop_prob=None):
  27. super(DropPath, self).__init__()
  28. self.drop_prob = drop_prob
  29. def forward(self, x):
  30. return drop_path(x, self.drop_prob, self.training)
  31. class SqueezeExcite_efficientv2(nn.Module):
  32. def __init__(self, c1, c2, se_ratio=0.25, act_layer=nn.ReLU):
  33. super().__init__()
  34. self.gate_fn = nn.Sigmoid()
  35. reduced_chs = int(c1 * se_ratio)
  36. self.avg_pool = nn.AdaptiveAvgPool2d(1)
  37. self.conv_reduce = nn.Conv2d(c1, reduced_chs, 1, bias=True)
  38. self.act1 = act_layer(inplace=True)
  39. self.conv_expand = nn.Conv2d(reduced_chs, c2, 1, bias=True)
  40. def forward(self, x):
  41. # 先全局平均池化
  42. x_se = self.avg_pool(x)
  43. # 再全连接(这里是用的1x1卷积,效果与全连接一样,但速度快)
  44. x_se = self.conv_reduce(x_se)
  45. # ReLU激活
  46. x_se = self.act1(x_se)
  47. # 再全连接
  48. x_se = self.conv_expand(x_se)
  49. # sigmoid激活
  50. x_se = self.gate_fn(x_se)
  51. # 将x_se 维度扩展为和x一样的维度
  52. x = x * (x_se.expand_as(x))
  53. return x
  54. # Fused-MBConv 将 MBConv 中的 depthwise conv3×3 和扩展 conv1×1 替换为单个常规 conv3×3。
  55. class FusedMBConv(nn.Module):
  56. def __init__(self, c1, c2, k=3, s=1, expansion=1, se_ration=0, dropout_rate=0.2, drop_connect_rate=0.2):
  57. super().__init__()
  58. # shorcut 是指到残差结构 expansion是为了先升维,再卷积,再降维,再残差
  59. self.has_shortcut = (s == 1 and c1 == c2) # 只要是步长为1并且输入输出特征图大小相等,就是True 就可以使用到残差结构连接
  60. self.has_expansion = expansion != 1 # expansion==1 为false expansion不为1时,输出特征图维度就为expansion*c1,k倍的c1,扩展维度
  61. expanded_c = c1 * expansion
  62. if self.has_expansion:
  63. self.expansion_conv = stem(c1, expanded_c, kernel_size=k, stride=s)
  64. self.project_conv = stem(expanded_c, c2, kernel_size=1, stride=1)
  65. else:
  66. self.project_conv = stem(c1, c2, kernel_size=k, stride=s)
  67. self.drop_connect_rate = drop_connect_rate
  68. if self.has_shortcut and drop_connect_rate > 0:
  69. self.dropout = DropPath(drop_connect_rate)
  70. def forward(self, x):
  71. if self.has_expansion:
  72. result = self.expansion_conv(x)
  73. result = self.project_conv(result)
  74. else:
  75. result = self.project_conv(x)
  76. if self.has_shortcut:
  77. if self.drop_connect_rate > 0:
  78. result = self.dropout(result)
  79. result += x
  80. return result
  81. class MBConv(nn.Module):
  82. def __init__(self, c1, c2, k=3, s=1, expansion=1, se_ration=0, dropout_rate=0.2, drop_connect_rate=0.2):
  83. super().__init__()
  84. self.has_shortcut = (s == 1 and c1 == c2)
  85. expanded_c = c1 * expansion
  86. self.expansion_conv = stem(c1, expanded_c, kernel_size=1, stride=1)
  87. self.dw_conv = stem(expanded_c, expanded_c, kernel_size=k, stride=s, groups=expanded_c)
  88. self.se = SqueezeExcite_efficientv2(expanded_c, expanded_c, se_ration) if se_ration > 0 else nn.Identity()
  89. self.project_conv = stem(expanded_c, c2, kernel_size=1, stride=1)
  90. self.drop_connect_rate = drop_connect_rate
  91. if self.has_shortcut and drop_connect_rate > 0:
  92. self.dropout = DropPath(drop_connect_rate)
  93. def forward(self, x):
  94. # 先用1x1的卷积增加升维
  95. result = self.expansion_conv(x)
  96. # 再用一般的卷积特征提取
  97. result = self.dw_conv(result)
  98. # 添加se模块
  99. result = self.se(result)
  100. # 再用1x1的卷积降维
  101. result = self.project_conv(result)
  102. # 如果使用shortcut连接,则加入dropout操作
  103. if self.has_shortcut:
  104. if self.drop_connect_rate > 0:
  105. result = self.dropout(result)
  106. # shortcut就是到残差结构,输入输入的channel大小相等,这样就能相加了
  107. result += x
  108. return result
  109. # ------------------------------Efficientnetv2 end--------------------------------------

 3.2  .yaml配置文件

  1. # YOLOv5 v6.0 backbone
  2. backbone:
  3. [[-1, 1, stem, [24, 3, 2]], # 0-P1/2 efficientnetv2 一开始是Stem = 普通的卷积+bn+激活 640*640*3 --> 320*320*24
  4. # # [out_channel,kernel_size,stride,expansion,se_ration]
  5. [-1, 2, FusedMBConv, [24, 3, 1, 1, 0]], # 1 2个FusedMBConv=3*3conv+se+1*1conv 320*320*24-->320*320*24
  6. [-1, 1, FusedMBConv, [48, 3, 2, 4, 0]], # 2 这里strid2=2,特征图尺寸缩小一半,expansion=4输出特征图的深度变为原来的4倍 320*320*24-->160*160*48
  7. [-1, 3, FusedMBConv, [48, 3, 1, 4, 0]], # 3 三个FusedMBConv
  8. [-1, 1, FusedMBConv, [64, 3, 2, 4, 0]], # 4 160*160*48-->80*80*64
  9. [-1, 3, FusedMBConv, [64, 3, 1, 4, 0]], # 5
  10. [-1, 1, MBConv, [128, 3, 2, 4, 0.25]], # 6 这里strid2=2,特征图尺寸缩小一半, 40*40*128
  11. [-1, 5, MBConv, [128, 3, 1, 4, 0.25]], # 7
  12. [-1, 1, MBConv, [160, 3, 2, 6, 0.25]], # 8 这里 strid2=2,特征图尺寸缩小一半,20*20*160
  13. [-1, 8, MBConv, [160, 3, 1, 6, 0.25]], # 9
  14. [-1, 1, MBConv, [256, 3, 2, 4, 0.25]], # 10 这里strid2=2,特征图尺寸缩小一半,10*10*160
  15. [-1, 14, MBConv, [256, 3, 1, 4, 0.25]], # 11
  16. [-1, 1, SPPF, [1024, 5]], #12
  17. ]
  18. # YOLOv5 v6.0 head

如果喜欢,请给我点赞、关注哦

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

闽ICP备14008679号