当前位置:   article > 正文

带有SE注意力机制的ResNet残差网络构建_resnet+se注意力机制

resnet+se注意力机制

残差网络ResNet和SE注意力机制自从被提出以后就得到了广泛的应用,在模型搭建过程中,有很多将两者进行结合使用的方法被提出,以是的模型具有更好的性能。

下面是将SE注意力机制应用在不同的ResNet残差网络中的方法。

 SE注意力机制模型图如下:

 基本的残差单元:

 网络输入x,希望输出H(x)。加入残差连接之后,有H(x)=F(x)+x,网络就只需要学习输出一个残差F(x)=H(x)-x,学习残差F(x)=H(x)-x会比直接学习原始特征H(x)简单的多。

不同的ResNet基本组成:

1.SE注意力机制+ResNet18

  1. import torch.nn as nn
  2. import math
  3. import torch.utils.model_zoo as model_zoo
  4. __all__ = ['SENet', 'se_resnet_18', 'se_resnet_34', 'se_resnet_50', 'se_resnet_101',
  5. 'se_resnet_152']
  6. def conv3x3(in_planes, out_planes, stride=1):
  7. # 定义3x3卷积,并且填充数为1
  8. """3x3 convolution with padding"""
  9. return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
  10. padding=1, bias=False)
  11. class BasicBlock(nn.Module):
  12. expansion = 1 # 扩展倍数的属性
  13. # 用于在 ResNet 中确定每个 BasicBlock 层的输入通道和输出通道之间的倍数关系。通过将输入通道数乘以 expansion,可以得到输出通道数
  14. def __init__(self, inplanes, planes, stride=1, downsample=None):
  15. super(BasicBlock, self).__init__()
  16. self.conv1 = conv3x3(inplanes, planes, stride) # 3x3卷积
  17. self.bn1 = nn.BatchNorm2d(planes) # 归一化
  18. self.relu = nn.ReLU(inplace=True)
  19. self.conv2 = conv3x3(planes, planes) # 3x3卷积
  20. self.bn2 = nn.BatchNorm2d(planes)
  21. self.downsample = downsample # 传入的下采样方法保存为一个属性
  22. self.stride = stride # 步长
  23. if planes == 64: # 输出特征图的通道数
  24. self.globalAvgPool = nn.AvgPool2d(56, stride=1) # 全局平均池化
  25. elif planes == 128:
  26. self.globalAvgPool = nn.AvgPool2d(28, stride=1)
  27. elif planes == 256:
  28. self.globalAvgPool = nn.AvgPool2d(14, stride=1)
  29. elif planes == 512:
  30. self.globalAvgPool = nn.AvgPool2d(7, stride=1)
  31. self.fc1 = nn.Linear(in_features=planes, out_features=round(planes / 16)) # 全连接
  32. self.fc2 = nn.Linear(in_features=round(planes / 16), out_features=planes) # 全连接
  33. self.sigmoid = nn.Sigmoid()
  34. def forward(self, x):
  35. residual = x
  36. out = self.conv1(x) # 3x3conv,s=1
  37. out = self.bn1(out)
  38. out = self.relu(out)
  39. out = self.conv2(out) # 3x3conv,s=0
  40. out = self.bn2(out)
  41. if self.downsample is not None:
  42. residual = self.downsample(x)
  43. original_out = out
  44. out = self.globalAvgPool(out) # # 全局平均池化
  45. out = out.view(out.size(0), -1)
  46. # out.size(0) 表示第一个维度的大小保持不变,而 -1 表示在保持其他维度的前提下,自动调整第二个维度的大小
  47. out = self.fc1(out)
  48. out = self.relu(out)
  49. out = self.fc2(out)
  50. out = self.sigmoid(out)
  51. out = out.view(out.size(0), out.size(1), 1, 1) # 新张量的形状为 (out.size(0), out.size(1), 1, 1)
  52. out = out * original_out
  53. out += residual # 残差连接
  54. out = self.relu(out)
  55. return out
  56. class Bottleneck(nn.Module):
  57. expansion = 4 # 扩展倍数的属性
  58. # 用于在 ResNet 中确定每个 BasicBlock 层的输入通道和输出通道之间的倍数关系。通过将输入通道数乘以 expansion,可以得到输出通道数
  59. def __init__(self, inplanes, planes, stride=1, downsample=None):
  60. super(Bottleneck, self).__init__()
  61. self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) # 1x1conv
  62. self.bn1 = nn.BatchNorm2d(planes)
  63. self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, # 3x3conv
  64. padding=1, bias=False)
  65. self.bn2 = nn.BatchNorm2d(planes)
  66. self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) # 1x1conv,输出通道是输入通道的4倍
  67. self.bn3 = nn.BatchNorm2d(planes * 4)
  68. self.relu = nn.ReLU(inplace=True)
  69. if planes == 64:
  70. self.globalAvgPool = nn.AvgPool2d(56, stride=1) # 平均池化
  71. elif planes == 128:
  72. self.globalAvgPool = nn.AvgPool2d(28, stride=1)
  73. elif planes == 256:
  74. self.globalAvgPool = nn.AvgPool2d(14, stride=1)
  75. elif planes == 512:
  76. self.globalAvgPool = nn.AvgPool2d(7, stride=1)
  77. self.fc1 = nn.Linear(in_features=planes * 4, out_features=round(planes / 4)) # 除 4 取整
  78. self.fc2 = nn.Linear(in_features=round(planes / 4), out_features=planes * 4)
  79. self.sigmoid = nn.Sigmoid()
  80. self.downsample = downsample
  81. self.stride = stride
  82. def forward(self, x):
  83. residual = x
  84. out = self.conv1(x)
  85. out = self.bn1(out)
  86. out = self.relu(out)
  87. out = self.conv2(out)
  88. out = self.bn2(out)
  89. out = self.relu(out)
  90. out = self.conv3(out)
  91. out = self.bn3(out)
  92. if self.downsample is not None:
  93. residual = self.downsample(x)
  94. original_out = out
  95. out = self.globalAvgPool(out)
  96. out = out.view(out.size(0), -1)
  97. out = self.fc1(out)
  98. out = self.relu(out)
  99. out = self.fc2(out)
  100. out = self.sigmoid(out)
  101. out = out.view(out.size(0),out.size(1),1,1)
  102. out = out * original_out
  103. out += residual
  104. out = self.relu(out)
  105. return out
  106. class SENet(nn.Module):
  107. # SE注意力机制
  108. def __init__(self, block, layers, num_classes=2):
  109. self.inplanes = 64
  110. super(SENet, self).__init__()
  111. self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,
  112. bias=False)
  113. self.bn1 = nn.BatchNorm2d(64)
  114. self.relu = nn.ReLU(inplace=True)
  115. self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) # 最大池化
  116. self.layer1 = self._make_layer(block, 64, layers[0])
  117. self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
  118. self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
  119. self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
  120. # block:指定使用的基本块类型,可以是残差块或其他类型的块
  121. # 64、128、256、512:指定每个卷积层组中的通道数(即输出特征图的通道数)
  122. # layers[0]、layers[1]、layers[2]、layers[3]:指定每个卷积层组中的基本块数量
  123. # stride=2:指定每个卷积层组中的卷积层的步长(stride),默认为 2
  124. self.avgpool = nn.AvgPool2d(7, stride=1) # 平均池化
  125. self.fc = nn.Linear(512 * block.expansion, num_classes) # 全连接
  126. for m in self.modules(): # 遍历模型的所有子模块
  127. if isinstance(m, nn.Conv2d): # 如果m模块时卷积模块
  128. n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels # 计算卷积核的参数个数 n
  129. m.weight.data.normal_(0, math.sqrt(2. / n))
  130. # 对卷积核的权重进行初始化。这里使用了正态分布来初始化权重,均值为 0,标准差为 math.sqrt(2. / n)。这种初始化方法可以帮助模型更好地收敛
  131. elif isinstance(m, nn.BatchNorm2d): # 如果m模块是归一化模块
  132. m.weight.data.fill_(1) # 归一化层的权重进行初始化,将所有权重设置为 1
  133. m.bias.data.zero_() # 归一化层的偏移量进行初始化,将所有偏移量设置为 0
  134. def _make_layer(self, block, planes, blocks, stride=1): # 用于创建一个层
  135. # block是一个模型中的基本单元,planes是层中的通道数,blocks是层中重复的次数,stride是步长,默认值为1
  136. downsample = None # 下采样层的目的是降低输出特征图的尺寸和增加通道数,以便将输入的特征图与输出的特征图进行匹配
  137. if stride != 1 or self.inplanes != planes * block.expansion:
  138. downsample = nn.Sequential(
  139. nn.Conv2d(self.inplanes, planes * block.expansion,
  140. kernel_size=1, stride=stride, bias=False), # 下采样快
  141. nn.BatchNorm2d(planes * block.expansion), # 归一化
  142. )
  143. layers = []
  144. layers.append(block(self.inplanes, planes, stride, downsample))
  145. self.inplanes = planes * block.expansion
  146. for i in range(1, blocks):
  147. layers.append(block(self.inplanes, planes))
  148. # block函数的目的是创建一个新的模块,该模块由一个卷积层和一个批归一化层组成,同时也可能包含一个下采样层
  149. return nn.Sequential(*layers) # 构建了一个顺序网络容器,将 layers 列表中的模块组合在一起,并将其作为方法的返回值
  150. def forward(self, x):
  151. x = self.conv1(x)
  152. x = self.bn1(x)
  153. x = self.relu(x)
  154. x = self.maxpool(x)
  155. x = self.layer1(x)
  156. x = self.layer2(x)
  157. x = self.layer3(x)
  158. x = self.layer4(x) # 特征提取和降维
  159. x = self.avgpool(x) # 将特征降维为一维
  160. x = x.view(x.size(0), -1)
  161. x = self.fc(x) # 全连接层进行分类
  162. print(x.shape)
  163. return x
  164. def se_resnet_18(pretrained=False, **kwargs):
  165. """Constructs a ResNet-18 model.
  166. Args:
  167. pretrained (bool): If True, returns a model pre-trained on ImageNet
  168. pretrained 表示是否使用在 ImageNet 数据集上预训练的模型
  169. """
  170. # 使用了一个名为 SENet 的类来构建模型,同时也使用了 BasicBlock 类作为 ResNet 中的基本块
  171. # [2, 2, 2, 2] 是一个列表,表示模型中每个阶段(stage)中重复 BasicBlock 的次数
  172. model = SENet(BasicBlock, [2, 2, 2, 2], **kwargs) # 创建含有SE注意机制的残差模块
  173. return model

2.SE注意力机制+ResNet34

只需要将SE注意力机制+ResNet18部分的代码中的def se_resnet_18部分换成如下即可:

  1. def se_resnet_34(pretrained=False, **kwargs):
  2. """Constructs a ResNet-34 model.
  3. Args:
  4. pretrained (bool): If True, returns a model pre-trained on ImageNet
  5. pretrained 表示是否使用在 ImageNet 数据集上预训练的模型
  6. """
  7. # 使用了一个名为 SENet 的类来构建模型,同时也使用了 BasicBlock 类作为 ResNet 中的基本块
  8. # 模型中每个阶段重复 BasicBlock 的次数为 [3, 4, 6, 3]
  9. model = SENet(BasicBlock, [3, 4, 6, 3], **kwargs)
  10. return model

3.SE注意力机制+ResNet50

只需要将SE注意力机制+ResNet18部分的代码中的def se_resnet_18部分换成如下即可:

  1. def se_resnet_50(pretrained=False, **kwargs):
  2. """Constructs a ResNet-50 model.
  3. Args:
  4. pretrained (bool): If True, returns a model pre-trained on ImageNet
  5. pretrained 表示是否使用在 ImageNet 数据集上预训练的模型
  6. """
  7. # 使用了一个名为 SENet 的类来构建模型,同时也使用了 Bottleneck 类作为 ResNet 中的基本块
  8. # [3, 4, 6, 3] 是一个列表,表示模型中每个阶段(stage)中重复 BasicBlock 的次数
  9. model = SENet(Bottleneck, [3, 4, 6, 3], **kwargs)
  10. return model

4.SE注意力机制+ResNet101

只需要将SE注意力机制+ResNet18部分的代码中的def se_resnet_18部分换成如下即可:

  1. def se_resnet_101(pretrained=False, **kwargs):
  2. """Constructs a ResNet-101 model.
  3. Args:
  4. pretrained (bool): If True, returns a model pre-trained on ImageNet
  5. pretrained 表示是否使用在 ImageNet 数据集上预训练的模型
  6. """
  7. # 使用了一个名为 SENet 的类来构建模型,同时也使用了 Bottleneck 类作为 ResNet 中的基本块
  8. # [3, 4, 23, 3] 是一个列表,表示模型中每个阶段(stage)中重复 BasicBlock 的次数
  9. model = SENet(Bottleneck, [3, 4, 23, 3], **kwargs)
  10. return model

5.SE注意力机制+ResNet152

只需要将SE注意力机制+ResNet18部分的代码中的def se_resnet_18部分换成如下即可:

  1. def se_resnet_152(pretrained=False, **kwargs):
  2. """Constructs a ResNet-152 model.
  3. Args:
  4. pretrained (bool): If True, returns a model pre-trained on ImageNet
  5. pretrained 表示是否使用在 ImageNet 数据集上预训练的模型
  6. """
  7. # 使用了一个名为 SENet 的类来构建模型,同时也使用了 Bottleneck 类作为 ResNet 中的基本块
  8. # [3, 4, 36, 3] 是一个列表,表示模型中每个阶段(stage)中重复 BasicBlock 的次数
  9. model = SENet(Bottleneck, [3, 8, 36, 3], **kwargs)
  10. return model

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

闽ICP备14008679号