笔记参考视频
【6.1 ResNet网络结构,BN以及迁移学习详解】
https://www.bilibili.com/video/BV1T7411T7wa/?spm_id_from=333.788.videocard.0
【6.2 使用pytorch搭建ResNet并基于迁移学习训练】
https://www.bilibili.com/video/BV14E411H7Uw?t=316
举例:34层网络中,第一层卷积层,然后池化层,有链接线的就是残差结构,最后通过一个平均池化下采样操作,再通过一个全连接层(输出层),基本结构就是堆叠残差结构。
实线部分的输入特征矩阵和输出特征矩阵的shape相同,能直接进行相加,
但是虚线残差结构中不能直接相加,输入结构是[56,56,64],输出结构是[28,28,128],可以通过stride=2将高和宽缩减为原来的一半,128可以将输入的卷积核(深度)从64变化为128
18层和34层的第一层种没有虚线残差结构,因为输入是[56,56,64]已经满足输入要求,但是对于更深层次的50、101和152,想要的输入结构是[56,56,256],所以更深层次的残差网络的第一层CON_2要有虚线结构
BN层,目的是使我们的一批(Batch)的feature map (特征矩阵)每一个channel所对应的维度满足均值为0,方差为1的分布规律
Batch Normalization能够加速网络的收敛并提升准确率,首先将图像数据进行预处理使其满足某一分布规律,进而加速处理
BN层就是使每一层的feature map 满足均值为0方差为1的分布规律
详解链接:https://blog.csdn.net/qq_37541097/article/details/104434557
概念:将已有网络的浅层的网络参数迁移到新的网络当中去,这样新的网络也具有识别底层通用特征的能力,就能更加快速的识别新的数据集的高维特征
优势:能快速训练一个理想的结果;当数据集较小时也能训练出理想的效果
代码地址:https://github.com/WZMIAOMIAO/deep-learning-for-image-processing/blob/master/pytorch_classification/Test5_resnet/model.py
定义一个类:BasicBlock,对应的是18层和34层残差结构
参数expansion=1:表示每层中的卷积核个数有没有发生变化(如18和34中每个残差结构中的2个卷积层大小都一样)
初始化函数init:
in_channel:输入特征矩阵的深度;
out_channel:输出特征矩阵的深度(主分支上卷积核的个数)
stride (步数):默认取1
downsample(下采样参数):默认取None,对应的是虚线的残差结构——捷径中的1*1的卷积层
18层和34层中的con_3、con_4、con_5残差结构的第一层都是虚线结构,起到一个降维的作用
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, in_channel, out_channel, stride=1, downsample=None):
super(BasicBlock, self).__init__()
self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel,kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channel)
self.relu = nn.ReLU()
self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channel)
self.downsample = downsample
# 定义正向传播函数
def forward(self, x):
identity = x
if self.downsample is not None:
identity = self.downsample(x)
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out += identity
out = self.relu(out)
return out
```
以上对于18层和34层的残差结构定义完成
### 多层次残差结构50、101、152
定义一个BasicBlock类,
expansion=4对应残差结构所使用卷积核发生的变化,多层次残差结构中每个结构中第三层的卷积核个数是的第一层和第二层的卷积核的4倍
**定义初始化函数**
```python
class Bottleneck(nn.Module):
expansion = 4
def __init__(self, in_channel, out_channel, stride=1, downsample=None):
super(Bottleneck, self).__init__()
self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel, kernel_size=1, stride=1, bias=False) # squeeze channels
self.bn1 = nn.BatchNorm2d(out_channel)
# -----------------------------------------
self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel,kernel_size=3, stride=stride, bias=False, padding=1)
self.bn2 = nn.BatchNorm2d(out_channel)
# -----------------------------------------
self.conv3 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel*self.expansion, kernel_size=1, stride=1, bias=False) # self.expansion=4,第三层卷积核是第一二层的2倍
self.bn3 = nn.BatchNorm2d(out_channel*self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
# 定义正向传播函数
def forward(self, x):
identity = x
if self.downsample is not None:
identity = self.downsample(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)
out += identity
out = self.relu(out)
return out
```
### ResNet网络框架部分
block就是所对应的残差结构,会根据所定义的不同的结构传入不同的block,例如34层的block_num=[3,4,6,3]是个列表结构,num_classes是训练集的分类个数,include_top是方便在resnet网络上去搭建其他的网络
先定义输入特征矩阵的深度,in_channel=64
定义第一个7*7的卷积层con1,第一个3是输入特征矩阵的深度,也就是RGB图像,kernel_size=7,为了将高和宽缩减为原来的一半,padding=3
定义最大池化下采样层
self_layer1对应con_2中的一系列残差结构
self_layer2对应con_3中的一系列残差结构,以此类推
【自定义make_layer函数】
channel是第一层的卷积核的个数,block_num是指包含了多少个残差结构
这个if语句,18层和34层的第一层会直接跳过,但是多层次的就不会跳过,而且18层和34层的除第一层以外的也不会跳过这个函数,因为符合了stide=2,也就是不等于1的条件
这个for循环没有从0开始是因为第0层是虚线结构,第1层及往后都是实线结构,所以用for循环去实现
*layers中的 ***** 是将列表转化为非关键字参数的形式传入到sequential中
avgpool——平均池化下采样
fc—— 全连接