【图像分类】用通俗易懂代码的复现EfficientNetV2,入门的绝佳选择(pytorch)

举报
AI浩 发表于 2021/12/23 00:00:46 2021/12/23
【摘要】 目录 摘要 代码实现 激活函数 SE模块  定义MBConv模块和Fused-MBConv模块 主体模块 完整代码 摘要 上周学习了EfficientNetV2的论文,并对其进行了翻译,如果对论文感兴趣的可以参考我的文章:https://blog.csdn.net/hhhhhhhhhhwwwwwwwwww/...

目录

摘要

代码实现

激活函数

SE模块

 定义MBConv模块和Fused-MBConv模块

主体模块

完整代码


摘要

上周学习了EfficientNetV2的论文,并对其进行了翻译,如果对论文感兴趣的可以参考我的文章:https://blog.csdn.net/hhhhhhhhhhwwwwwwwwww/article/details/117399085

在EfficientNets的第一个版本中存在三个缺点:

(1) 用非常大的图像尺寸训练很慢;而且输入较大的图像必须以较小的批大小训练这些模型,这大大减慢了训练速度,但是精度反而下降了。

(2) 在网络浅层中使用Depthwise convolutions速度会很慢。

(3) 每个阶段都按比例放大是次优的。 

EfficientNetV2针对这三个方面的缺点做了改进:

1、针对图像的大小的问题,作者提出了自适应正则化的渐进式学习的方法,详见论文的4.2节

2、针对Depthwise convolutions在早期层中很慢的问题,作者提出了 Fused-MBConv 模块来代替部分的MBConv。

3、针对每个阶段都按比例放大是次优的问题,作者使用非均匀缩放策略来逐步添加 到后期阶段。

作者对EfficientNetV2做了三方面的总结

          •我们引入了 EfficientNetV2,这是一个新的更小、更快的模型系列。 通过我们的训练感知NAS 和扩展发现,EfficientNetV2 在训练速度和参数效率方面都优于以前的模型。

          • 我们提出了一种改进的渐进式学习方法,它可以根据图像大小自适应地调整正则化。 我们表明它可以加快训练速度,同时提高准确性。

          • 我们在 ImageNet、CIFAR、Cars 和 Flowers 数据集上展示了比现有技术快 11 倍的训练速度和 6.8 倍的参数效率。

总之,一句话,我们的新模型又快又准而且还小,大家赶快用吧!下面我就讲讲如何使用Pytorch实现EfficientNetV2。

代码实现

EfficientNetV2和EfficientNet一样也是一个家族模型,包括:efficientnetv2_s、efficientnetv2_m,、efficientnetv2_l、efficientnetv2_xl。所以我们要实现四个模型。

激活函数

激活函数使用SiLU激活函数,我对激活函数做了总结,感兴趣的可以查看:CNN基础——激活函数_AI浩-CSDN博客


  
  1. # SiLU (Swish) activation function
  2. if hasattr(nn, 'SiLU'):
  3. SiLU = nn.SiLU
  4. else:
  5. # For compatibility with old PyTorch versions
  6. class SiLU(nn.Module):
  7. def forward(self, x):
  8. return x * torch.sigmoid(x)

SE模块

SE模块,我在前面的文章中已经介绍了。现在直接将SE模块拿过来使用。


  
  1. class SELayer(nn.Module):
  2. def __init__(self, inp, oup, reduction=4):
  3. super(SELayer, self).__init__()
  4. self.avg_pool = nn.AdaptiveAvgPool2d(1)
  5. self.fc = nn.Sequential(
  6. nn.Linear(oup, _make_divisible(inp // reduction, 8)),
  7. SiLU(),
  8. nn.Linear(_make_divisible(inp // reduction, 8), oup),
  9. nn.Sigmoid()
  10. )
  11. def forward(self, x):
  12. b, c, _, _ = x.size()
  13. y = self.avg_pool(x).view(b, c)
  14. y = self.fc(y).view(b, c, 1, 1)
  15. return x * y

 定义MBConv模块和Fused-MBConv模块

这两个模块是整个模型实现的核心,模块的详细构造如下图:

我们可以看到MBConv模块,经过1×1的卷积,然后channel放大四倍,再经过depthwise conv3×3的卷积,然后经过SE模块后,再经过1×1的卷积,把channel恢复到输入的大小,最后和上层的输入融合。

Fused-MBConv模块比MBConv模块简单些,先经过3×3的卷积,把channel放大四倍,然后经过SE模块,再经过1×1的卷积,最后和上层的输入融合。下面是实现MBConv模块和Fused-MBConv模块的详细代码:


  
  1. class MBConv(nn.Module):
  2. """
  3. 定义MBConv模块和Fused-MBConv模块,将fused设置为1或True是Fused-MBConv,否则是MBConv
  4. :param inp:输入的channel
  5. :param oup:输出的channel
  6. :param stride:步长,设置为1时图片的大小不变,设置为2时,图片的面积变为原来的四分之一
  7. :param expand_ratio:放大的倍率
  8. :return:
  9. """
  10. def __init__(self, inp, oup, stride, expand_ratio, fused):
  11. super(MBConv, self).__init__()
  12. assert stride in [1, 2]
  13. hidden_dim = round(inp * expand_ratio)
  14. self.identity = stride == 1 and inp == oup
  15. if fused:
  16. self.conv = nn.Sequential(
  17. # fused
  18. nn.Conv2d(inp, hidden_dim, 3, stride, 1, bias=False),
  19. nn.BatchNorm2d(hidden_dim),
  20. SiLU(),
  21. SELayer(inp, hidden_dim),
  22. # pw-linear
  23. nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
  24. nn.BatchNorm2d(oup),
  25. )
  26. else:
  27. self.conv = nn.Sequential(
  28. # pw
  29. nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),
  30. nn.BatchNorm2d(hidden_dim),
  31. SiLU(),
  32. # dw
  33. nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False),
  34. nn.BatchNorm2d(hidden_dim),
  35. SiLU(),
  36. SELayer(inp, hidden_dim),
  37. # pw-linear
  38. nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
  39. nn.BatchNorm2d(oup),
  40. )
  41. def forward(self, x):
  42. if self.identity:
  43. return x + self.conv(x)
  44. else:
  45. return self.conv(x)

主体模块


  
  1. class EfficientNetv2(nn.Module):
  2. def __init__(self, cfgs, num_classes=1000, width_mult=1.):
  3. super(EfficientNetv2, self).__init__()
  4. self.cfgs = cfgs
  5. # building first layer
  6. input_channel = _make_divisible(24 * width_mult, 8)
  7. layers = [conv_3x3_bn(3, input_channel, 2)]
  8. # building inverted residual blocks
  9. block = MBConv
  10. for t, c, n, s, fused in self.cfgs:
  11. output_channel = _make_divisible(c * width_mult, 8)
  12. for i in range(n):
  13. layers.append(block(input_channel, output_channel, s if i == 0 else 1, t, fused))
  14. input_channel = output_channel
  15. self.features = nn.Sequential(*layers)
  16. # building last several layers
  17. output_channel = _make_divisible(1792 * width_mult, 8) if width_mult > 1.0 else 1792
  18. self.conv = conv_1x1_bn(input_channel, output_channel)
  19. self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
  20. self.classifier = nn.Linear(output_channel, num_classes)
  21. self._initialize_weights()
  22. def forward(self, x):
  23. x = self.features(x)
  24. x = self.conv(x)
  25. x = self.avgpool(x)
  26. x = x.view(x.size(0), -1)
  27. x = self.classifier(x)
  28. return x
  29. def _initialize_weights(self):
  30. for m in self.modules():
  31. if isinstance(m, nn.Conv2d):
  32. n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
  33. m.weight.data.normal_(0, math.sqrt(2. / n))
  34. if m.bias is not None:
  35. m.bias.data.zero_()
  36. elif isinstance(m, nn.BatchNorm2d):
  37. m.weight.data.fill_(1)
  38. m.bias.data.zero_()
  39. elif isinstance(m, nn.Linear):
  40. m.weight.data.normal_(0, 0.001)
  41. m.bias.data.zero_()

 理解这段代码,我们还需要了解输入参数cfgs,以efficientnetv2_s为例:


  
  1. def efficientnetv2_s(**kwargs):
  2. """
  3. Constructs a EfficientNetV2-S model
  4. """
  5. cfgs = [
  6. # t, c, n, s, fused
  7. [1, 24, 2, 1, 1],
  8. [4, 48, 4, 2, 1],
  9. [4, 64, 4, 2, 1],
  10. [4, 128, 6, 2, 0],
  11. [6, 160, 9, 1, 0],
  12. [6, 272, 15, 2, 0],
  13. ]
  14. return EfficientNetv2(cfgs, **kwargs)

 第一列“t”指的是MBConv模块和Fused-MBConv模块第一个输入后放大的倍率。

 第二列“c”,channel,指的是输出的channel。

第三列“n”,指定的是MBConv模块和Fused-MBConv模块堆叠的个数。

第四列“s”,指的是卷积的步长,步长为1,图片的大小不变,步长为图片的面积缩小为原来的四分之一,实现降维。

第五列“fused”,选择MBConv模块或Fused-MBConv模块,为1这是Fused-MBConv模块,0则是MBConv模块,对应了前面摘要提过了,在浅层用Fused-MBConv代替MBConv。

完整代码


  
  1. import torch
  2. import torch.nn as nn
  3. import math
  4. __all__ = ['efficientnetv2_s', 'efficientnetv2_m', 'efficientnetv2_l', 'efficientnetv2_xl']
  5. from torchsummary import summary
  6. #这个函数的目的是确保Channel能被8整除。
  7. def _make_divisible(v, divisor, min_value=None):
  8. """
  9. 这个函数的目的是确保Channel能被8整除。
  10. :param v:
  11. :param divisor:
  12. :param min_value:
  13. :return:
  14. """
  15. if min_value is None:
  16. min_value = divisor
  17. new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
  18. # Make sure that round down does not go down by more than 10%.
  19. if new_v < 0.9 * v:
  20. new_v += divisor
  21. return new_v
  22. # SiLU (Swish) activation function
  23. if hasattr(nn, 'SiLU'):
  24. SiLU = nn.SiLU
  25. else:
  26. # For compatibility with old PyTorch versions
  27. class SiLU(nn.Module):
  28. def forward(self, x):
  29. return x * torch.sigmoid(x)
  30. class SELayer(nn.Module):
  31. def __init__(self, inp, oup, reduction=4):
  32. super(SELayer, self).__init__()
  33. self.avg_pool = nn.AdaptiveAvgPool2d(1)
  34. self.fc = nn.Sequential(
  35. nn.Linear(oup, _make_divisible(inp // reduction, 8)),
  36. SiLU(),
  37. nn.Linear(_make_divisible(inp // reduction, 8), oup),
  38. nn.Sigmoid()
  39. )
  40. def forward(self, x):
  41. b, c, _, _ = x.size()
  42. y = self.avg_pool(x).view(b, c)
  43. y = self.fc(y).view(b, c, 1, 1)
  44. return x * y
  45. def conv_3x3_bn(inp, oup, stride):
  46. return nn.Sequential(
  47. nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
  48. nn.BatchNorm2d(oup),
  49. SiLU()
  50. )
  51. def conv_1x1_bn(inp, oup):
  52. return nn.Sequential(
  53. nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
  54. nn.BatchNorm2d(oup),
  55. SiLU()
  56. )
  57. class MBConv(nn.Module):
  58. """
  59. 定义MBConv模块和Fused-MBConv模块,将fused设置为1或True是Fused-MBConv,否则是MBConv
  60. :param inp:输入的channel
  61. :param oup:输出的channel
  62. :param stride:步长,设置为1时图片的大小不变,设置为2时,图片的面积变为原来的四分之一
  63. :param expand_ratio:放大的倍率
  64. :return:
  65. """
  66. def __init__(self, inp, oup, stride, expand_ratio, fused):
  67. super(MBConv, self).__init__()
  68. assert stride in [1, 2]
  69. hidden_dim = round(inp * expand_ratio)
  70. self.identity = stride == 1 and inp == oup
  71. if fused:
  72. self.conv = nn.Sequential(
  73. # fused
  74. nn.Conv2d(inp, hidden_dim, 3, stride, 1, bias=False),
  75. nn.BatchNorm2d(hidden_dim),
  76. SiLU(),
  77. SELayer(inp, hidden_dim),
  78. # pw-linear
  79. nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
  80. nn.BatchNorm2d(oup),
  81. )
  82. else:
  83. self.conv = nn.Sequential(
  84. # pw
  85. nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),
  86. nn.BatchNorm2d(hidden_dim),
  87. SiLU(),
  88. # dw
  89. nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False),
  90. nn.BatchNorm2d(hidden_dim),
  91. SiLU(),
  92. SELayer(inp, hidden_dim),
  93. # pw-linear
  94. nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
  95. nn.BatchNorm2d(oup),
  96. )
  97. def forward(self, x):
  98. if self.identity:
  99. return x + self.conv(x)
  100. else:
  101. return self.conv(x)
  102. class EfficientNetv2(nn.Module):
  103. def __init__(self, cfgs, num_classes=1000, width_mult=1.):
  104. super(EfficientNetv2, self).__init__()
  105. self.cfgs = cfgs
  106. # building first layer
  107. input_channel = _make_divisible(24 * width_mult, 8)
  108. layers = [conv_3x3_bn(3, input_channel, 2)]
  109. # building inverted residual blocks
  110. block = MBConv
  111. for t, c, n, s, fused in self.cfgs:
  112. output_channel = _make_divisible(c * width_mult, 8)
  113. for i in range(n):
  114. layers.append(block(input_channel, output_channel, s if i == 0 else 1, t, fused))
  115. input_channel = output_channel
  116. self.features = nn.Sequential(*layers)
  117. # building last several layers
  118. output_channel = _make_divisible(1792 * width_mult, 8) if width_mult > 1.0 else 1792
  119. self.conv = conv_1x1_bn(input_channel, output_channel)
  120. self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
  121. self.classifier = nn.Linear(output_channel, num_classes)
  122. self._initialize_weights()
  123. def forward(self, x):
  124. x = self.features(x)
  125. x = self.conv(x)
  126. x = self.avgpool(x)
  127. x = x.view(x.size(0), -1)
  128. x = self.classifier(x)
  129. return x
  130. def _initialize_weights(self):
  131. for m in self.modules():
  132. if isinstance(m, nn.Conv2d):
  133. n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
  134. m.weight.data.normal_(0, math.sqrt(2. / n))
  135. if m.bias is not None:
  136. m.bias.data.zero_()
  137. elif isinstance(m, nn.BatchNorm2d):
  138. m.weight.data.fill_(1)
  139. m.bias.data.zero_()
  140. elif isinstance(m, nn.Linear):
  141. m.weight.data.normal_(0, 0.001)
  142. m.bias.data.zero_()
  143. def efficientnetv2_s(**kwargs):
  144. """
  145. Constructs a EfficientNetV2-S model
  146. """
  147. cfgs = [
  148. # t, c, n, s, fused
  149. [1, 24, 2, 1, 1],
  150. [4, 48, 4, 2, 1],
  151. [4, 64, 4, 2, 1],
  152. [4, 128, 6, 2, 0],
  153. [6, 160, 9, 1, 0],
  154. [6, 272, 15, 2, 0],
  155. ]
  156. return EfficientNetv2(cfgs, **kwargs)
  157. def efficientnetv2_m(**kwargs):
  158. """
  159. Constructs a EfficientNetV2-M model
  160. """
  161. cfgs = [
  162. # t, c, n, s, fused
  163. [1, 24, 3, 1, 1],
  164. [4, 48, 5, 2, 1],
  165. [4, 80, 5, 2, 1],
  166. [4, 160, 7, 2, 0],
  167. [6, 176, 14, 1, 0],
  168. [6, 304, 18, 2, 0],
  169. [6, 512, 5, 1, 0],
  170. ]
  171. return EfficientNetv2(cfgs, **kwargs)
  172. def efficientnetv2_l(**kwargs):
  173. """
  174. Constructs a EfficientNetV2-L model
  175. """
  176. cfgs = [
  177. # t, c, n, s, fused
  178. [1, 32, 4, 1, 1],
  179. [4, 64, 7, 2, 1],
  180. [4, 96, 7, 2, 1],
  181. [4, 192, 10, 2, 0],
  182. [6, 224, 19, 1, 0],
  183. [6, 384, 25, 2, 0],
  184. [6, 640, 7, 1, 0],
  185. ]
  186. return EfficientNetv2(cfgs, **kwargs)
  187. def efficientnetv2_xl(**kwargs):
  188. """
  189. Constructs a EfficientNetV2-XL model
  190. """
  191. cfgs = [
  192. # t, c, n, s, fused
  193. [1, 32, 4, 1, 1],
  194. [4, 64, 8, 2, 1],
  195. [4, 96, 8, 2, 1],
  196. [4, 192, 16, 2, 0],
  197. [6, 256, 24, 1, 0],
  198. [6, 512, 32, 2, 0],
  199. [6, 640, 8, 1, 0],
  200. ]
  201. return EfficientNetv2(cfgs, **kwargs)
  202. if __name__ == '__main__':
  203. device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
  204. model = efficientnetv2_s()
  205. model.to(device)
  206. summary(model, (3, 224, 224))

 

文章来源: wanghao.blog.csdn.net,作者:AI浩,版权归原作者所有,如需转载,请联系作者。

原文链接:wanghao.blog.csdn.net/article/details/117441033

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。