Pytorch之ResNet图像分类( 三 )


代表着我们计算的 map每个维度()的方差,注意是一个向量不是一个值,向量的每一个元素代表着一个维度()的方差,然后根据

计算标准化处理后得到的值 。示例如下所示:
在原论文公式中还有

两个参数,
是用来调整数值分布的方差大小,
是用来调节数值均值的位置 。这两个参数是在反向传播过程中学习得到的,
的默认值是1,
的默认值是0 。
使用BN需要注意:
1.训练时要将参数设置为True,在验证时将参数设置为False 。在中可通过创建模型的model.train()和model.eval()方法控制 。
2.batch size尽可能设置大点,设置小后表现可能很糟糕,设置的越大求的均值和方差越接近整个训练集的均值和方差 。
3.一般将BN层放在卷积层(Conv) 和激活层(Relu) 之间,且卷积层不要使用偏置bias 。在有无偏置时推导出来的结果一样,使用反而增加运算效率 。

Pytorch之ResNet图像分类

文章插图
二、网络结构
( Next)是一种深度神经网络架构,它是对残差网络(,通常简称为)的扩展和改进 。的设计目标是提高网络的性能和效率,特别是在大规模图像分类任务上表现出色 。
网络的一些关键特点:
1.基于残差连接: 仍然基于残差连接,这是的核心思想之一 。残差连接允许信息在网络中跳跃传递,有助于解决梯度消失问题,使得更深的网络可以更容易地训练 。
2.分组卷积: 引入了分组卷积( )的概念,这是与传统卷积不同的一种卷积操作 。在分组卷积中,卷积核被分为多个组(),每个组对输入数据执行卷积操作 。这种结构允许网络在不增加参数数量的情况下增加模型的宽度,从而提高了性能 。
3.(基数): 中引入了一个称为 "" 的参数,用于指定分组卷积中的组数 。通过调整基数,可以控制模型的宽度,从而平衡模型的性能和计算成本 。通常,较大的基数可以提高性能,但也增加了计算成本 。
分组卷积(Group ):
假设输入特征矩阵等于4,分为两个组,对每个组分别进行卷积操作,假设对每个Group使用n/2个卷积核,通过第每个Group的卷积可以得到对应的是n/2的特征矩阵,再对两组进行拼接,那么最终特征矩阵得到的是n 。
若假设输入矩阵的等于cin,对输入特征矩阵分为g个组,那么对于每个group而言,每个group采用卷积核的参数是(k×k×cin/g×n/g) 。当g=cin,n=cin,这就相当于对输入特征矩阵的每一个分配了一个为1的卷积核进行卷积 。
中的残差模块(右图),其等价表示如下图三种形式 。
a:第三层对每个分支: 先通过1*1的卷积,然后在进行相加(和b的第三层先通过拼接,在进行1*1卷积等价)
b:第一层有32个分支(32*4=128,与c第一层等价),第二层和c的group卷积一样,对于每个分支可理解为一个Group
c:先通过1*1卷积层进行降维处理(128),再通过group对它进行处理(group数32,大小3*3,输出),最后通过一个1*1的卷积升维
网络结构:将网络中的残差模块替换为模块中的残差模块即可 。
C()=32是Group数,4对应每个组卷积核的个数 。注意,只有block层数大于等于3的时,才能构建出一个比较有意义的block,对之前的浅层block而言,还是使用的block 。
三、网络实现 1.构建网络
根据 block的两种结构搭建残差结构 。
# resnet18/34 blockclass BasicBlock(nn.Module):expansion = 1# 主分支上卷积核的个数是否相同,BasicBlock第一层和第二层相同,Bottleneck第三层特征矩阵的维度是第一层的4倍def __init__(self, in_channel, out_channel, stride=1, downsample=None, **kwargs):super(BasicBlock, self).__init__()# 实线残差结构:stride=1;虚线残差结构:stride = 2(特征图减半)self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel, kernel_size=3, stride=stride, padding=1, bias=False)# 在使用BN层使,无biasself.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# 虚线残差结构:shortcut分支的下采样def forward(self, x):identity = x# 虚线残差结构:输入特征矩阵和输出特征矩阵不能直接相加,输入特征矩阵需要经过shortcut分支上的1×1的卷积核进行了维度处理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 += identityout = self.relu(out)return out# resnet50/101/152 blockclass Bottleneck(nn.Module):"""注意:原论文中,在虚线残差结构的主分支上,第一个1x1卷积层的步距是2,第二个3x3卷积层步距是1 。但在pytorch官方实现过程中是第一个1x1卷积层的步距是1,第二个3x3卷积层步距是2,这么做的好处是能够在top1上提升大概0.5%的准确率 。可参考Resnet v1.5 https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch"""# 主分支上卷积核的个数是否相同,BasicBlock第一层和第二层相同,Bottleneck第三层特征矩阵的维度是第一层的4倍expansion = 4# in_channel:传入Bottleneck的输入通道数,out_channel:中间3x3所使用卷积核的个数def __init__(self, in_channel, out_channel, stride=1, downsample=None, groups=1, width_per_group=64):super(Bottleneck, self).__init__()# ResNet网络不使用Grouped Convolution,groups、width_per_group使用默认参数width = out_channel# ResNeXt网络使用Grouped Convolution,groups=32、width_per_group=4使用默认参数width = 2*out_channel# ResNeXt50的输出特征矩阵通道数ResNet50的2倍width = int(out_channel * (width_per_group / 64.)) * groupsself.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=width, kernel_size=1, stride=1, bias=False)# squeeze channelsself.bn1 = nn.BatchNorm2d(width)# 第二层:实线残差结构:stride=1;虚线残差结构:stride = 2(特征图减半)self.conv2 = nn.Conv2d(in_channels=width, out_channels=width, groups=groups, kernel_size=3, stride=stride, bias=False, padding=1)self.bn2 = nn.BatchNorm2d(width)# 第三层的输出特征是第一层的特征输出的4(expansion)倍self.conv3 = nn.Conv2d(in_channels=width, out_channels=out_channel*self.expansion, kernel_size=1, stride=1, bias=False)# unsqueeze channelsself.bn3 = nn.BatchNorm2d(out_channel*self.expansion)self.relu = nn.ReLU(inplace=True)self.downsample = downsampledef forward(self, x):identity = xif 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 += identityout = self.relu(out)return out