残差网络的学习

1为什么出现:

并非网络结构越深,效果越好。随着网络结构的加深,模型的性能会逐渐提升,到某个最优点后,反而会下降。

重要的是,这并不仅仅是过拟合,因为即使在训练集上,更深の网络的误差也更高。这意味着更深的网络甚至无法很好地学*。

image
如果模型最优点是星星的话,像图1来说,最后会离最优点越来越远。
所以只要实现像图2一样,随着模型层数的加深,但是后面的层总是包含着前面的层,就算最后并没有达到最优解,起码也不会丢失曾经到达过的最优点。

2核心思想

也就是随着层的加深,有些层没用,所以有些层的输出不如是H(x)=x 就是直接把参数传递过来。
但是让模型训练一个恒等映射都非常难(即 H(x) = x) 可以让模型训练一个残差值F(x)
其中
image
那么原始的理想映射就变成
image

3为什么其有用

如果某一层的最佳映射就是一个恒等变换(即输出等于输入,H(x) = x),那么对于传统的网络,它需要费力地调整参数来拟合这个恒等函数。

但对于ResNet,它只需要将残差 F(x) 的权重学*到趋*于0即可。让权重变为0,远比拟合一个恒等变换要容易得多。这大大降低了深层网络学*的难度,使得梯度可以更好地在层与层之间流动,从而解决了网络退化问题,让构建非常深的网络成为可能。

4维度匹配问题

x要能和fx做加法运算,需要二者形状相同。
通常来说,都是fx的得到,都是对x做卷积运算,尺寸变小。
所以x要另外单独做一次卷积 才能实现x+fx的运算。

这引出了两种基本类型的残差块:

  • Identity Block (身份块):输入和输出维度相同时使用,跳跃连接直接将 x 相加。

  • Convolutional Block (卷积块/投影块):输入和输出维度不同时使用,跳跃连接需要通过一个卷积层来变换 x 的维度。

5残差块 ResidualBlock

核心思想

image

通常是上图这种架构的
weight layer代表卷积层 Activation function 代表激活函数

具体细节如下图:
image

残差块 code实现:

image
是实现上图这个架构,所以在初始化要定义两个Conv2d层,两个BN层。

初始化

def __init__(self, in_channels, out_channels,stride=1):
初始化传入输入通道数,输出通道数,步长。

然后创建两个卷积层,两个BN层。

self.conv1 = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=3, stride=stride, padding=1,bias=False)  
self.bn1 = nn.BatchNorm2d(out_channels)  
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)  
self.bn2 = nn.BatchNorm2d(out_channels)

初始化卷积层层

首先两个层都是3x3的卷积层 都使用pad=1的填充 kernal_size=3 但是最关键的就是步长stride的设置

首先对于卷积层来说,输出特征图的尺寸公式为:
image
所以当步长Sh=1时候,只要2*pad-kernal_size+1=0就可以保证图像尺寸不变。

  • 第一个卷积核采用的步长是残差块初始化输入的参数stride
    • 当stride=1时候,图像尺寸不发生改变 因为pad=1 kernal_size=3 满足上面的保证图像尺寸不变的公式。
    • 当stride!=1时候,此时图像尺寸一定会变小,其实就是在做下采样:使特征图的尺寸减小。
  • 第二个卷积核采用的是步长固定的为1的硬编码 所以一定满足上面保证图像尺寸不变的公式。

而BN层不改变图像尺寸,输出通道数。所以只有第一个卷积层会改变图像的尺寸已经通道数。

初始化快捷通道

快捷通道就是最后输出H(x)=F(x)+x的 x通道。
由于要做加法运算,所以形状必须匹配。

研究输入x的形状 为[in_channels,h,w]
经过卷积块输出F(x)的形状为[out_channels,oh,ow]

所以需要把快捷通道x的形状修正为F(x)

先初始化快捷通道
self.shortcut = nn.Sequential() #先初始化 参考[[nn.Sequential]]

然后判断形状或者通道数有没有改变

通道数的判断依据就是in_channels==out_channels
形状的改变就是 初始化的步长是否等于1 stride==1

如果不满足这两个条件其中的一个,就把输入x经过一个卷积层,修正形状。

if in_channels != out_channels or stride!= 1:  
    self.shortcut = nn.Sequential(  
        nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride),#pad默认为0  
        nn.BatchNorm2d(out_channels)  
    )

说明发生了下采样或者通道数改变了,就使用一个卷积层修改通道数和特征图大小。

这个卷积层 是大小1x1的。由于pad默认等于0 根据卷积核输出特征图的公式
他的分子为h-1+2*0 实际上和前面的第一个卷积层分子h-3+2*1的分子一样,由于前面的第二个卷层不改变图像,所以只要和第一个卷积层一样就行了,分子已经一样了,所以分母也要一样,所以该卷积核的步长也设置为卷积块初始化的stride。
这样就保证了快捷通道和输出F(x)尺寸对齐。

前向传播

就按照架构图,加入激活函数实现。
这里就是注意 H(x)=F(x)+x之后,对H(x)也要激活函数激活一下。

def forward(self, x):  
    # F(x)+x  
    out = F.relu(self.bn1(self.conv1(x)))  
    out = self.bn2(self.conv2(out))  
    out += self.shortcut(x)  
    out = F.relu(out)   
    return out

残差网络

残差网络(通常简称为 ResNet)就是将许许多多的残差块串联(堆叠)起来形成的一个完整的、非常深的深度学*模型

一个典型的残差网络(如 ResNet-34, ResNet-50)的结构可以简化为:

输入 -> 初始卷积/池化层 -> [残差块] -> [残差块] -> ... -> [残差块] -> 全局平均池化层 -> 全连接分类层 -> 输出

posted @ 2025-09-22 21:28  朱朱成  阅读(25)  评论(0)    收藏  举报