)
残差网络Residual NetworkResNet是一种深度卷积神经网络架构由何恺明等人于2015年提出。其核心思想是通过引入“残差块”Residual Block解决深度网络中的梯度消失或爆炸问题使得网络能够训练更深的层次如超过100层。残差网络的主要贡献是发现了“退化现象Degradation”并针对退化现象发明了 “直连边/短连接Shortcut connection”极大的消除了深度过大的神经网络训练困难问题。论文Deep Residual Learning for Image Recognition一.残差连接随着神经网络的发展人们发现增加神经网络的层数可以提高训练精度但是如果只增加网络的深度可能会出现“梯度消失”和“梯度爆炸”等问题。梯度消失就像传话游戏中声音越来越小最终听不清一样。梯度在反向传播过程中逐渐变小最终接近于零。这导致网络早期层的参数几乎无法更新模型学习停滞。梯度爆炸相反的情况是梯度变得异常大就像传话游戏中某个人突然大喊导致后续信息完全失真。这会使参数更新幅度过大训练过程变得不稳定甚至发散。这两种问题都源于链式法则的累积效应。简单来说每一层的梯度都是前一层梯度与当前层激活函数导数的乘积。当激活函数导数小于1时如Sigmoid或Tanh多次相乘会导致梯度指数级衰减如果导数大于1则可能导致梯度爆炸。即使解决了梯度问题增加网络深度也不一定带来性能提升。当网络深度增加到一定程度时训练准确率反而开始下降这被称为退化问题。传统前馈神经网络就像一条单行道信息只能按顺序通过每一层这种结构使得网络难以学习到有效的恒等映射。随着深度增加优化难度急剧上升网络忘记了如何保持信息不变。残差连接的核心思想非常简单却极其有效与其让网络直接学习目标映射H(x)不如让它学习残差映射F(x) H(x) - x然后通过公式y F(x) x得到最终结果。残差连接通过创建快捷路径shortcut path让输入能够绕过中间层直接参与后续计算。假设你要从A点到达B点直接规划路线可能很复杂。但如果你先确定从A到B的差值比如向东5公里向北3公里然后再从A点出发加上这个差值问题就简单多了。残差连接之所以叫残差正是因为它关注的是输入与期望输出之间的差值而不是完整的映射。这种设计让网络能够轻松学习恒等映射——只需将F(x)学习为零即可。二.残差网络概念使用传统的解决方法例如权重的初始化和批标准化虽然能够解决梯度问题但是由于深度加深出现了网络性能的退化现象进而丢失网络前面获取的特征。也就是随着训练轮数epoch的增加精度到达一定程度后反而开始迅速下降。而ResNet基于使用直接映射来连接网络不同层的思想保证了 l1 层的网络一定比 l 层包含更多的图像信息。ResNet的核心思想是每个附加层都应该更容易地包含原始函数作为其元素之一。也就是引入了残差块Residual Block使用跳跃连接Skip Connection让梯度可以直接传递。残差网络是由残差块堆叠而成的深度网络数学上对于第l层的输入xl在残差网络中有那么梯度计算变为这个1就是重点。即使很小接近0梯度仍然可以通过1直接传递就像有一条专门的通道确保信息不会丢失。残差网络的优点缓解梯度消失跳跃连接允许梯度直接反向传播避免深层网络训练困难。易于优化残差学习使得网络只需拟合输入与输出的差值残差降低了学习难度。性能提升在ImageNet等数据集上ResNet实现了更低的错误率和更高的准确率。三.残差块残差块通过跳跃连接将输入直接传递到输出与经过卷积层处理后的特征相加。一个残差块可以用表示为yF(x,W)x其中x残差块的输入F(x,W)由卷积、BN、激活等组成的残差映射学习输入与输出的差异y残差块输出由此可以看出残差块的输出是原始输入与残差函数输出的和。当网络发现不需要改变输入时它可以让F(x)接近于零这样输出就等于输入实现了恒等映射保证深层网络性能不退化。若输入与输出的维度不一致可通过1x1卷积调整维度公式调整为yF(x,W)其中表示1×1 卷积层或全连接层的参数矩阵用于将 x 投影到与 F(x)相同的维度但是实验结果表明1x1卷积对模型性能提升有限所以一般是在升维或者降维时才会使用。残差块的实现如下当前实现无可视化结果仅为残差块的代码import torch from torch import nn from torch.nn import functional as F class Residual(nn.Module): def __init__(self, input_channels, num_channels, use_1x1convFalse, strides1): super().__init__() # 第一个卷积层负责通道数变换/步幅下采样 self.conv1 nn.Conv2d(input_channels, num_channels, kernel_size3, padding1, stridestrides) # 第二个卷积层通道数不变仅提取特征 self.conv2 nn.Conv2d(num_channels, num_channels, kernel_size3, padding1) # 可选的1×1卷积用于匹配输入X和卷积输出Y的维度通道/尺寸 if use_1x1conv: self.conv3 nn.Conv2d(input_channels, num_channels, kernel_size1, stridestrides) else: self.conv3 None # 批量归一化层对应两个卷积层的输出 self.bn1 nn.BatchNorm2d(num_channels) self.bn2 nn.BatchNorm2d(num_channels) def forward(self, X): Y F.relu(self.bn1(self.conv1(X))) Y self.bn2(self.conv2(Y)) if self.conv3: X self.conv3(X) Y X return F.relu(Y)如果想要进行测试看到输出形状import torch from torch import nn from torch.nn import functional as F class Residual(nn.Module): def __init__(self, input_channels, num_channels, use_1x1convFalse, strides1): super().__init__() # 第一个卷积层3x3卷积 self.conv1 nn.Conv2d( input_channels, num_channels, kernel_size3, padding1, stridestrides ) # 第二个卷积层3x3卷积 self.conv2 nn.Conv2d( num_channels, num_channels, kernel_size3, padding1 ) # 1x1卷积层可选用于调整输入X的通道数和尺寸使与输出Y维度匹配 if use_1x1conv: self.conv3 nn.Conv2d( input_channels, num_channels, kernel_size1, stridestrides # 步长与conv1一致确保尺寸同步调整 ) else: self.conv3 None # 不使用时为None # 批量规范化层BN层每个卷积层后都接BN层稳定训练 self.bn1 nn.BatchNorm2d(num_channels) # 对应conv1的输出 self.bn2 nn.BatchNorm2d(num_channels) # 对应conv2的输出 def forward(self, X): 参数: X: 输入特征图形状为(批量大小, input_channels, 高, 宽) 返回: 残差块的输出特征图形状为(批量大小, num_channels, 高, 宽) # 第一步conv1 BN ReLU残差函数F(X)的第一部分 Y F.relu(self.bn1(self.conv1(X))) # Y形状(批量大小, num_channels, 高1, 宽1) # 第二步conv2 BN残差函数F(X)的第二部分暂不激活 Y self.bn2(self.conv2(Y)) # Y形状(批量大小, num_channels, 高2, 宽2) # 若使用1x1卷积调整输入X的维度通道数和尺寸以匹配Y if self.conv3: X self.conv3(X) # X形状变为(批量大小, num_channels, 高2, 宽2) # 残差连接,将输入X与残差函数F(X)相加 Y X # 此时Y和X维度必须完全一致通道数、高、宽均相同 # 最后通过ReLU激活函数输出 return F.relu(Y) if __name__ __main__: # 测试1输入输出通道数相同3→3不使用1x1卷积步长为1尺寸不变 blk Residual(input_channels3, num_channels3) X torch.rand(4, 3, 6, 6) # 随机输入4个样本3通道6x6尺寸 Y blk(X) print(测试1输出形状, Y.shape) # 输出(4, 3, 6, 6)通道和尺寸均不变 # 测试2输入输出通道数不同3→6使用1x1卷积调整维度步长为2尺寸减半 blk Residual(input_channels3, num_channels6, use_1x1convTrue, strides2) print(测试2输出形状, blk(X).shape) # 输出(4, 6, 3, 3)通道加倍尺寸减半四.ResNet的整体设计一个典型的ResNet包含以下几个主要部分初始处理层7×7大卷积核后接最大池化快速提取基础特征。四个残差阶段每个阶段包含多个残差块特征图尺寸逐阶段减半通道数逐阶段翻倍。全局池化与分类层将特征图压缩为向量并进行分类。ResNet的不同版本如ResNet-18、ResNet-50主要区别在于每个阶段中残差块的数量和类型。较深的版本如ResNet-50以上使用瓶颈残差块包含1×1-3×3-1×1的卷积序列以减少计算量。下图是ResNet-18的架构图16个3x3卷积层组织成8个残差块每个块包含2个卷积层不包括恒等映射的1*1卷积层加上第一个7*7卷积层和最后一个全连接层共有18层。