当前位置:   article > 正文

2.deeplabv3+的主干网络(mobilenet网络)

deeplabv3+的主干网络

        deeplabv3的论文中用了resnet网络,在这里用轻量级网络mobilenet替换resnet,下面分别是两个网络的代码。

1.mobilenet网络

代码如下:

  1. import math
  2. import os
  3. import cv2
  4. import numpy as np
  5. import torch
  6. import torch.nn as nn
  7. import torch.utils.model_zoo as model_zoo
  8. BatchNorm2d = nn.BatchNorm2d
  9. def conv_bn(inp, oup, stride):
  10. return nn.Sequential(
  11. nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
  12. BatchNorm2d(oup),
  13. nn.ReLU6(inplace=True)
  14. )
  15. def conv_1x1_bn(inp, oup):
  16. return nn.Sequential(
  17. nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
  18. BatchNorm2d(oup),
  19. nn.ReLU6(inplace=True)
  20. )
  21. class InvertedResidual(nn.Module):
  22. def __init__(self, inp, oup, stride, expand_ratio):
  23. super(InvertedResidual, self).__init__()
  24. self.stride = stride
  25. assert stride in [1, 2]
  26. hidden_dim = round(inp * expand_ratio)
  27. self.use_res_connect = self.stride == 1 and inp == oup
  28. if expand_ratio == 1:
  29. self.conv = nn.Sequential(
  30. #--------------------------------------------#
  31. # 进行3x3的逐层卷积,进行跨特征点的特征提取
  32. #--------------------------------------------#
  33. nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False),
  34. BatchNorm2d(hidden_dim),
  35. nn.ReLU6(inplace=True),
  36. #-----------------------------------#
  37. # 利用1x1卷积进行通道数的调整
  38. #-----------------------------------#
  39. nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
  40. BatchNorm2d(oup),
  41. )
  42. else:
  43. self.conv = nn.Sequential(
  44. #-----------------------------------#
  45. # 利用1x1卷积进行通道数的上升
  46. #-----------------------------------#
  47. nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),
  48. BatchNorm2d(hidden_dim),
  49. nn.ReLU6(inplace=True),
  50. #--------------------------------------------#
  51. # 进行3x3的逐层卷积,进行跨特征点的特征提取
  52. #--------------------------------------------#
  53. nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False),
  54. BatchNorm2d(hidden_dim),
  55. nn.ReLU6(inplace=True),
  56. #-----------------------------------#
  57. # 利用1x1卷积进行通道数的下降
  58. #-----------------------------------#
  59. nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
  60. BatchNorm2d(oup),
  61. )
  62. def forward(self, x):
  63. if self.use_res_connect:#使用残差边
  64. return x + self.conv(x)
  65. else:
  66. return self.conv(x)#不使用残差边
  67. class MobileNetV2(nn.Module):
  68. def __init__(self, n_class=1000, input_size=224, width_mult=1.):
  69. super(MobileNetV2, self).__init__()
  70. block = InvertedResidual
  71. input_channel = 32
  72. last_channel = 1280
  73. interverted_residual_setting = [
  74. # t, c, n, s
  75. [1, 16, 1, 1], # 256, 256, 32 -> 256, 256, 16
  76. [6, 24, 2, 2], # 256, 256, 16 -> 128, 128, 24 2
  77. [6, 32, 3, 2], # 128, 128, 24 -> 64, 64, 32 4
  78. [6, 64, 4, 2], # 64, 64, 32 -> 32, 32, 64 7
  79. [6, 96, 3, 1], # 32, 32, 64 -> 32, 32, 96
  80. [6, 160, 3, 2], # 32, 32, 96 -> 16, 16, 160 14
  81. [6, 320, 1, 1], # 16, 16, 160 -> 16, 16, 320
  82. ]
  83. assert input_size % 32 == 0
  84. input_channel = int(input_channel * width_mult)
  85. self.last_channel = int(last_channel * width_mult) if width_mult > 1.0 else last_channel
  86. # 512, 512, 3 -> 256, 256, 32
  87. self.features = [conv_bn(3, input_channel, 2)]
  88. for t, c, n, s in interverted_residual_setting:
  89. output_channel = int(c * width_mult)
  90. for i in range(n):
  91. if i == 0:
  92. self.features.append(block(input_channel, output_channel, s, expand_ratio=t))
  93. else:
  94. self.features.append(block(input_channel, output_channel, 1, expand_ratio=t))
  95. input_channel = output_channel
  96. self.features.append(conv_1x1_bn(input_channel, self.last_channel))
  97. self.features = nn.Sequential(*self.features)
  98. self.classifier = nn.Sequential(
  99. nn.Dropout(0.2),
  100. nn.Linear(self.last_channel, n_class),
  101. )
  102. self._initialize_weights()
  103. def forward(self, x):
  104. x = self.features(x)
  105. x = x.mean(3).mean(2)
  106. x = self.classifier(x)
  107. return x
  108. def _initialize_weights(self):
  109. for m in self.modules():
  110. if isinstance(m, nn.Conv2d):
  111. n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
  112. m.weight.data.normal_(0, math.sqrt(2. / n))
  113. if m.bias is not None:
  114. m.bias.data.zero_()
  115. elif isinstance(m, BatchNorm2d):
  116. m.weight.data.fill_(1)
  117. m.bias.data.zero_()
  118. elif isinstance(m, nn.Linear):
  119. n = m.weight.size(1)
  120. m.weight.data.normal_(0, 0.01)
  121. m.bias.data.zero_()
  122. def load_url(url, model_dir='./model_data', map_location=None):
  123. if not os.path.exists(model_dir):
  124. os.makedirs(model_dir)
  125. filename = url.split('/')[-1]
  126. cached_file = os.path.join(model_dir, filename)
  127. if os.path.exists(cached_file):
  128. return torch.load(cached_file, map_location=map_location)
  129. else:
  130. return model_zoo.load_url(url,model_dir=model_dir)
  131. def mobilenetv2(pretrained=False, **kwargs):
  132. model = MobileNetV2(n_class=1000, **kwargs)
  133. if pretrained:
  134. model.load_state_dict(load_url('https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/mobilenet_v2.pth.tar'), strict=False)
  135. return model
  136. if __name__ == '__main__':
  137. img_paths = r"img\000001.jpg"
  138. img = cv2.imread(img_paths)
  139. img = cv2.resize(img, (512, 512))
  140. images = img.reshape(1, 3, 512, 512)
  141. #images=img.reshape(1,3,1080,1920)
  142. data=torch.tensor(images,dtype=torch.float32)
  143. #print(data.sum())
  144. model=MobileNetV2()
  145. for i,layer in enumerate(model.features):
  146. print(i,layer)
  147. # output=model(data)
  148. # print(output)

代码运行结构如下:

        结果从0到17共18层卷积网络。在该网络结构中,输入的数据大小是1X3X512X512,网络结构共有18层,在这里对下面的部分代码做一些解释:

解释1:  

        以上是网络结构,t表示是否需要升维,1表示不需要升维,6表示需要;c表示该网络结构输出的通道数;n表示该网络结构重复次数;s表示该网络结构的步长。如[6,24,2,2]表示不需要下采样,输出通道数为24,重复两次网络结构,步长是2,该层的网络输出结果如下:

        我们对以上的n的所有值相加得1+2+3+4+3+3+1=17,再加上刚输入进来的一层网络结构刚好等于18层.

解释2:InvertedResidual类

如下图所示,mobilenetv2网络结构的最基本单元(InvertedResidual)就是下图结构:

        上图中用了1X1卷积+3X3卷积+1X1卷积共3层网络构成,第一个1X1卷积起到升维的作用,可以使得获取的信息更丰富,第二个3X3卷积进行跨特征点信息提取,第3个1X1卷积进行降维,是为了减少计算量,也为了获取卷积的主要信息。从上面构建卷积网络的循环代码中可以看到,每个单元网络都是由InvertedResidual构成的,而不是简单的 卷积+BN+激活函数 的结构。

注意,这个类最后返回两种结果:一种是返回残差结果,一种是不使用残差边的结果。

解释3:代码中的self.features

如下图所示:self.features是一个列表变量,里面的conv_bn是代码中最上面的函数,其实就是定义的一个Conv2d+BN+ReLU6的一个卷积层。

        在这里定义了self.features后,它又在第二个图片的for循环里不断的添加(append)新的卷积层,这个for循环添加了17个卷积层,加上定义时的一个卷积层,总共刚好18个卷积层。

        在这里,我们就对mobilenetv2的网络结构解释完了。

2.deeplabv3+对mobilenetv2的运用

           在deeplabv3+里,我们首先写入上面的代码,文件名称为mobilenetv2.py,然后再写一个deeplabv3_plus.py文件对先前写的代码进行引用,代码如下:

  1. class MobileNetV2(nn.Module):
  2. def __init__(self, downsample_factor=8, pretrained=True):
  3. super(MobileNetV2, self).__init__()
  4. from functools import partial
  5. model = mobilenetv2(pretrained)
  6. self.features = model.features[:-1]
  7. self.total_idx = len(self.features)
  8. self.down_idx = [2, 4, 7, 14]
  9. if downsample_factor == 8:
  10. for i in range(self.down_idx[-2], self.down_idx[-1]):
  11. self.features[i].apply(
  12. partial(self._nostride_dilate, dilate=2)
  13. )
  14. for i in range(self.down_idx[-1], self.total_idx):
  15. self.features[i].apply(
  16. partial(self._nostride_dilate, dilate=4)
  17. )
  18. elif downsample_factor == 16:
  19. for i in range(self.down_idx[-1], self.total_idx):
  20. self.features[i].apply(
  21. partial(self._nostride_dilate, dilate=2)
  22. )
  23. def _nostride_dilate(self, m, dilate):
  24. classname = m.__class__.__name__
  25. if classname.find('Conv') != -1:
  26. if m.stride == (2, 2):
  27. m.stride = (1, 1)
  28. if m.kernel_size == (3, 3):
  29. m.dilation = (dilate//2, dilate//2)
  30. m.padding = (dilate//2, dilate//2)
  31. else:
  32. if m.kernel_size == (3, 3):
  33. m.dilation = (dilate, dilate)
  34. m.padding = (dilate, dilate)
  35. def forward(self, x):
  36. low_level_features = self.features[:4](x)
  37. x = self.features[4:](low_level_features)
  38. return low_level_features, x

        对于以上代码的主要部分,在这里做部分解释如下:

解释一:self.features = model.features[:-1]

        这个代码就是提取mobilenetv2代码的self.features,那这里为什么加了[:-1]呢?因为mobilenetv2最后加了一层卷积self.features.append(conv_1x1_bn(input_channel, self.last_channel)),这个卷积层其实就是mobilenetv2自身分类用的,而在deeplabv3+里不需要这一层卷积,所以下面的代码调用的是model.features[:-1]。

解释二:代码中的self.down_idx = [2, 4, 7, 14]

这行代码是与mobilenetv2代码的卷积层对应的,mobilenetv2代码有下图的几行代码:

        其实,[2, 4, 7, 14]中的几个数对应的是图片中s=2的卷积层的位置。

  • 图片中的第二行的s=2对应卷积层的第3层(前两层是 1+1)位置(对应的坐标位置就是2),最前面有一层初始定义的卷积层(前面已经解释过);
  • 图片中第3行的2对应第5层(前4层是1+1+2)位置(对应的坐标位置就是4);
  • 图片中第4行的2对应第8层(前7层是1+1+2+3)位置(对应的坐标位置就是7);
  • 图片中第6行的2对应第15层(前14层是1+1+2+3+4+3)位置(对应的坐标位置就是14);

解释3:downsample_factor == 8或者downsample_factor == 16

       这两行代码表示如果downsample_factor == 8,表示我们只需要3次下采用,那么我们需要将[2, 4, 7, 14]的后两次采样的参数做一个修改(对第7和第14层的参数作一个修改),即将步长s修改为1.

        前3次下采样的地方是:第0层到第1层之间做了一次下采样;第2层到第3层之间做了一次下采样;第4层到第5层之间做了一次下采样.

        如果downsample_factor == 16,表示我们只需要4次下采用,那么我们需要将[2, 4, 7, 14]的后一次采样的参数做一个修改(第14层的参数作一个修改),即将步长s修改为1.

        前4次下采样的地方是:第0层到第1层之间做了一次下采样;第2层到第3层之间做了一次下采样;第4层到第5层之间做了一次下采样;第7层到第8层之间做了一次下采样。

        在这里,我们就对deeplabv3+中的mobilenet2模型的运用解释完了!!!

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

闽ICP备14008679号