当前位置:   article > 正文

使用PyTorch实现的ResNet-50模型_resnet50 pytorch

resnet50 pytorch

ResNet(残差网络)是一种深度卷积神经网络结构,通过引入残差块(Residual Block)解决了深度网络中的梯度消失和梯度爆炸问题,使得可以训练非常深的网络。

使用PyTorch实现的ResNet-50模型

FixedBatchNorm类

定义FixedBatchNorm类,继承自nn.BatchNorm2d,并在forward方法中使用F.batch_norm函数,这是为了固定批量归一化的行为。

  1. class FixedBatchNorm(nn.BatchNorm2d):
  2. def forward(self, input):
  3. output = F.batch_norm(input, # 输入数据,即卷积层的输出
  4. self.running_mean, # 训练过程中累积的样本均值,训练过程中被动更新,推理阶段用于标准化数据。
  5. self.running_var, # 训练过程中累积的样本方差,训练过程中被动更新,推理阶段用于标准化数据。
  6. self.weight,
  7. self.bias,
  8. training=False, # 推理阶段进行批量归一化,因此不需要计算新的均值和方差,而是使用之前训练时计算得到的self.running_mean和self.running_var。
  9. eps=self.eps # eps是为了数值稳定性而添加到方差的小常量。这可以防止除以接近于零的方差,避免数值不稳定性的问题。
  10. )
  11. return output

计算标准化的值(normalized value):

        normalized_value = \frac{input - mean}{\sqrt (var+eps)}

        output = normaiized_value * weights + bias

F.batch_norm操作在模型的卷积层之后,常常与激活函数一起使用,以促使模型更快地学习和更好地泛化。

About BatchNorm Study

Bottleneck类

定义Bottleneck类,继承自nn.Module,表示ResNet中的残差块。

ResNet 中的基本构建块,它定义了残差网络中的瓶颈结构。瓶颈结构的主要目标是减少计算成本,同时提高网络的表示能力

具体而言,Bottleneck 块在每个阶段(stage)的每个残差块中被重复使用。

  1. class Bottleneck(nn.Module):
  2. # ...
  1. class Bottleneck(nn.Module):
  2. expansion = 4
  3. def __init__(self, inplanes, planes, stride=1, downsample=None, dilation=1):
  4. super(Bottleneck, self).__init__()
  5. self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) # 1x1 卷积,用于减少输入特征图的维度(降维),其输出通道数为 planes
  6. self.bn1 = FixedBatchNorm(planes) # Batch Normalization, 对 1x1 卷积的输出进行批量归一化。
  7. self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
  8. padding=dilation, bias=False, dilation=dilation) # 3x3 卷积,用于学习特征,而且由于通道数相对较小,计算成本相对较低,这也是为什么称之为“瓶颈”的原因。
  9. self.bn2 = FixedBatchNorm(planes)
  10. self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) # 1x1 卷积,用于恢复输入特征图的维度(升维),其输出通道数为 planes * 4。
  11. self.bn3 = FixedBatchNorm(planes * 4)
  12. self.relu = nn.ReLU(inplace=True) # ReLU 激活函数:对归一化后的数据进行激活。
  13. self.downsample = downsample #
  14. self.stride = stride
  15. self.dilation = dilation

主要作用是引入残差连接(residual connection),允许梯度更容易地通过网络传播。残差连接通过将输入直接加到块的输出中,有助于缓解梯度消失问题,并允许训练非常深的神经网络。 

最终的output是将残差连接的结果传递给 ReLU 激活函数,这是为了引入非线性。在整个 Bottleneck 类中,expansion 参数设置为 4,表示输出通道数是输入通道数的 4 倍。

ResNet类

定义了整个 Residual Network(ResNet)的架构。ResNet 是一种深度神经网络架构,引入了残差块(Residual Block),旨在解决深层网络训练过程中的梯度消失和梯度爆炸问题。

定义ResNet类,继承自nn.Module,包含了ResNet的整体结构,包括多个残差块的堆叠。

在ResNet类的构造函数中,定义了模型的各个组成部分,包括卷积层、批量归一化层、激活函数等。 

  1. class ResNet(nn.Module):
  2. def __init__(self, block, layers, strides=(2, 2, 2, 2), dilations=(1, 1, 1, 1)):
  3. self.inplanes = 64 # 初始通道数=64
  4. super(ResNet, self).__init__()
  5. '''初始卷积层'''
  6. self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size, stride=strides[0], padding,
  7. bias=False)
  8. self.bn1 = FixedBatchNorm()
  9. self.relu = nn.ReLU(inplace=True)
  10. '''池化层'''
  11. self.maxpool = nn.MaxPool2d(kernel_size, stride=2, padding=1)
  12. '''四个阶段,每个阶段由多个 Bottleneck 块组成。Bottleneck 块的数量由 layers 参数决定。输入通道数和输出通道数随着阶段的增加而增加。'''
  13. # 主要用于捕捉输入数据中的低级特征,例如边缘、颜色等。由于这个阶段位于网络的起始部分,对图像的原始特征进行初步处理和提取。
  14. self.layer1 = self._make_layer(block, planes, layers[0], stride=1, dilation=dilations[0])
  15. # 在第一阶段的基础上,进一步学习中级特征,如纹理、形状等。引入更多的Bottleneck块,逐渐增加网络的深度和复杂性,提高特征表示的抽象程度。
  16. self.layer2 = self._make_layer(block, planes, layers[1], stride=strides[1], dilation=dilations[1])
  17. # 学习更高级的语义特征,例如物体部分和局部特征。增加的深度有助于网络理解图像中更复杂的模式,对物体的更深层次的特征有更好的捕捉。
  18. self.layer3 = self._make_layer(block, planes, layers[2], stride=strides[2], dilation=dilations[2])
  19. # 在网络的最深层次上进行全局特征建模,包括整体物体的语义信息。通过更深的层次,网络能够捕捉图像中更全局、更高级的特征,从而提高对整个图像内容的理解。
  20. self.layer4 = self._make_layer(block, planes, layers[3], stride=strides[3], dilation=dilations[3])
  21. self.inplanes = 1024

MaxPool2d study

定义_make_layer方法,用于构建每个阶段的残差块。

  1. def _make_layer(self, block, planes, blocks, stride=1, dilation=1):
  2. ''' 如果stride不为1,或者self.inplanes不等于planes*block.expansion则创建一个下采样'''
  3. downsample = None
  4. if stride != 1 or self.inplanes != planes * block.expansion:
  5. downsample = nn.Sequential(
  6. nn.Conv2d(self.inplanes, planes * block.expansion,
  7. kernel_size=1, stride=stride, bias=False), # 1*1卷积
  8. FixedBatchNorm(planes * block.expansion), # 批量归一化
  9. )
  10. layers = [block(self.inplanes, planes, stride, downsample, dilation=1)]
  11. '''更新self.inplanes以匹配当前阶段的输出通道数'''
  12. self.inplanes = planes * block.expansion
  13. for i in range(1, blocks):
  14. layers.append(block(self.inplanes, planes, dilation=dilation))
  15. '''将列表中的块组成一个序列,并返回这个序列作为阶段的输出'''
  16. out_layer = nn.Sequential(*layers)
  17. return out_layer

 为了保持输入和输出的维度一致,当 stride 不等于 1 或者当前层的输入通道数不等于 planes * block.expansion 时,会引入下采样(downsample)操作。

        具体来说,当 stride 不为 1 时,卷积操作会导致特征图的尺寸减小,为了保持尺寸一致,需要引入下采样。同时,如果输入通道数与输出通道数不一致,也需要引入下采样,以便将输入的通道数调整到与输出通道数一致,从而能够正确地进行残差连接

        下采样的引入有助于保持网络中特征图尺寸的一致性,使得残差块能够正确地进行残差连接,从而更好地传播梯度并促进模型训练。这种设计也是 ResNet 架构成功的一个关键因素,有助于训练非常深层的神经网络。

定义前向传播方法forward,描述了数据在模型中的流动。

  1. def forward(self, x):
  2. # ···

 resnet50函数

定义resnet50函数,用于创建ResNet-50模型实例。如果pretrained为True,还会加载预训练权重。

  1. def resnet50(pretrained=True, **kwargs):
  2. model = ResNet(Bottleneck, layers, **kwargs)
  3. # 预训练模型
  4. if pretrained
  5. state_dict = torch.load(model_path)
  6. # 删除不需要的全连接层权重
  7. state_dict.pop('fc.weight', None)
  8. state_dict.pop('fc.bias', None)
  9. model.load_state_dict(state_dict)
  10. return model

这样设计的 ResNet-50 模型是由论文"Deep Residual Learning for Image Recognition"中提出的,。这个结构的设计旨在在保持模型深度的同时,有效地捕捉和学习图像中的抽象特征。

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

闽ICP备14008679号