ResNet网络结构,BN以及迁移学习的详解

参考大佬

ResNet详解

论文亮点:

  • 超深的网络结构(突破1000层)

  • 提出residual模型

  • 使用BN加速训练,丢弃(Dropout)
    残差结构解决的问题

  • 梯度消失或梯度爆炸

  • 网络退化的问题

残差结构

在这里插入图片描述
左边的图是针对于网络层数较少的残差结构,ResNet-34
右边的图是针对网络层数比较神的残差结构,ResNet-50/101/152
残差结构是通过主线的残差结构,加上短接线的输出结构,经过激活函数,这里值得注意的是主分支和shortcut的输出特征矩阵的shape必须是相同的。还有就是这里是在相同的维度上相加,不是GoolNet中的深度拼接。右边的残差结构前后都有一个11的卷积层,通过11的卷积核来进行降维和升维(改变深度)。
假设以256深度作为例子
左图:33256256+33256256=1,179,648
右图:1125664+336464+1164*256=69,632
右图的参数量相比于左图少很多
在这里插入图片描述
在这里插入图片描述
实线和虚线的区别
实线是输入和输出的特征矩阵的shape是一样的
虚线是输入和输出是不一样的,通过改变步距,使得主分支和捷径分支输出的shape是一样的
同样对于三层的网络结构,虚线原理是一样的
对于深层的结构,在残差结构中一般设置第一层的步距为2,这样的目的是为了对网络层进行下采样,在原论文中只在Conv3/Conv4/Conv5存在虚线,也就是存在下采样。

BN的详解

BN的目的是使一批(Batch)的特征图满足均值为0,方差为1的分布规律
在图像传入第一个卷积层之前,需要对传入的图像数据做预处理,使它满足一定的规律分布,但是在经过卷积层后,数据的分布就不一定满足一定的规律了,BN就是为了使特征图满足均值为0,方差为1的分布规律。
在原文中讲道,对于拥有D维的输入,要对每一维进行标准化处理。假设我们的输入图像是RGB是三通道的彩色图像,标准化处理分别对RGB三个通道进行处理。
使用BN所需要注意的问题

  • 训练时将training=True,在验证时将traing=False,在Pytorch中可通过创建模型model.train()和model.evl()方法控制
  • batch size尽可能设置的大点,设置小后表现的可能很糟糕,设置的越大求得均值和方差就越接近整个训练集的均值和方差
    -建议放在卷积层和激活层之间,卷积层不用设置偏置,因为没有用,设置偏置得到的结果也是一样的。

迁移学习

迁移学习的优势

  • 能够快速的训练出一个理想的结果
  • 当数据集比较少的时候,也能训练出理想的效果
  • 使用别人的预训练模型参数时,要注意别人的预处理方式

浅层的网络参数可以将学好的参数应用到新的网络模型中,因为浅层的网络结可能学到的信息是相同的。
常见的迁移学习的方式

  • 载入权重后训练所有的参数
  • 载入权重后只训练最后几层的参数
  • 载入权重后在原网络的基础上再添加一层的全连接层,仅训练最后一个全连接层(因为分类的类别是不同的,如果想用原模型的所有参数,需要再增加一层新的全连接层)
### ResNet50 架构详解 ResNet50 是一种深度卷积神经网络架构,拥有 50 层残差块。这种设计使得模型能够更有效地训练更深的网络层次,从而提高性能和鲁棒性[^1]。 #### 主要组成部分 - **输入层**:接受图像数据作为输入。 - **初始卷积层**:通常是一个 $7 \times 7$ 的大尺寸卷积核,步幅为 2,用于提取基础特征。 - **最大池化层**:紧随初始卷积之后,进一步减少空间维度。 - **四个阶段的残差模块**: - 每个阶段包含若干个残差单元(residual blocks)。这些单元内部采用短连接机制来缓解梯度消失问题。 - **全局平均池化层**:位于最后一个阶段之后,负责将特征图转换成固定长度向量表示。 - **全连接分类器**:最终输出类别预测概率分布。 ```python import torch.nn as nn class Bottleneck(nn.Module): expansion = 4 def __init__(self, inplanes, planes, stride=1, downsample=None): super(Bottleneck, self).__init__() self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) self.bn1 = nn.BatchNorm2d(planes) self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(planes) self.conv3 = nn.Conv2d(planes, planes * self.expansion, kernel_size=1, bias=False) self.bn3 = nn.BatchNorm2d(planes * self.expansion) self.relu = nn.ReLU(inplace=True) self.downsample = downsample self.stride = stride def forward(self, x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) out = self.relu(out) out = self.conv3(out) out = self.bn3(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out def make_layer(block, planes, blocks, stride=1): layers = [] downsample = None if stride != 1 or inplanes != block.expansion * planes: downsample = nn.Sequential( nn.Conv2d(inplanes, block.expansion*planes, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(block.expansion*planes)) layers.append(block(inplanes, planes, stride, downsample)) inplanes = block.expansion * planes for i in range(1, blocks): layers.append(block(inplanes, planes)) return nn.Sequential(*layers) class ResNet(nn.Module): def __init__(self, block=Bottleneck, num_classes=1000): global inplanes inplanes = 64 super(ResNet, self).__init__() self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,bias=False) self.bn1 = nn.BatchNorm2d(64) self.relu = nn.ReLU(inplace=True) self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) self.layer1 = make_layer(block, 64, 3) self.layer2 = make_layer(block, 128, 4, stride=2) self.layer3 = make_layer(block, 256, 6, stride=2) self.layer4 = make_layer(block, 512, 3, stride=2) self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.fc = nn.Linear(512 * block.expansion, num_classes) # 初始化权重... def _forward_impl(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.maxpool(x) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) x = self.layer4(x) x = self.avgpool(x) x = torch.flatten(x, 1) x = self.fc(x) return x model = ResNet() print(model) ``` ### 微调方法 对于特定任务的数据集较小的情况,可以通过迁移学习的方式利用预训练好的 ResNet50 来加速收敛并提升泛化能力: - 替换最后一层全连接层以适应新的分类数目; - 冻结前面大部分层的参数不变,只更新新增加部分以及少量最后几层的权值; - 使用较低的学习率重新训练整个网络; 具体实现如下所示: ```python from torchvision import models # 加载预训练模型 pretrained_model = models.resnet50(pretrained=True) for param in pretrained_model.parameters(): param.requires_grad_(False) # 冻结所有参数 num_ftrs = pretrained_model.fc.in_features pretrained_model.fc = nn.Linear(num_ftrs, new_num_classes) # 修改输出节点数 criterion = nn.CrossEntropyLoss() # 定义损失函数 optimizer = optim.Adam(filter(lambda p: p.requires_grad, pretrained_model.parameters()), lr=learning_rate) # 只优化可训练参数 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值