0.背景

残差网络(ResNet, Residual Network) 由 何恺明(Kaiming He) 等人于 2015 年 在微软研究院(Microsoft Research)提出。他们在论文 “Deep Residual Learning for Image Recognition”(CVPR 2016)中首次提出 ResNet,并在 ImageNet 图像分类竞赛(ILSVRC 2015) 上取得冠军,证明了超深网络(如 ResNet-152)可以有效训练,并超越传统 CNN 结构。

1.介绍

论文连接:Deep Residual Learning for Image Recognition

在普通深度网络中,随着层数增加,梯度可能会消失或爆炸,使得训练变得困难。

ResNet 引入了 残差块(Residual Block),使用 跳跃连接(Skip Connection) 让梯度可以直接传递:

y=F(x)+x 。其中,x 是输入,F(x) 是卷积层提取的特征 直接将输入 x 加到输出上,形成一个短路路径(Shortcut)残差块介绍

ResNet有这样多个残差块最叠而成,有几个不同规格的网络,最高能到152层,这种残差结构决定了网络可以无限延伸下去而不会出现梯度逐层衰减(梯度消失)或放大(梯度爆炸)。

残差网络结构

论文中给出了不同层网络的结构表格(示意图见附件)

 

论文中给出了不同层网络的训练结果:

 

2.残差网络的好处

(1) 让梯度可以直接传播到前面的层,使得信息不会在深层网络中消失或失真。可以训练 更深的网络,如 ResNet-152,远超传统的 VGG-19。

不同网络训练对比效果

(2) 传统 CNN(如 VGG)通常在 20-30 层后会遇到退化问题(degradation problem),即深度增加反而降低准确率。

(3) 计算效率高,参数量少:VGG-16 约 138M 个参数,而 ResNet-50 仅有 25.5M。由于 ResNet 采用 Bottleneck 结构(1x1 卷积降维 + 3x3 卷积特征提取),减少了计算量。

(4) 提高模型的泛化能力: 浅层网络容易过拟合,而深层 ResNet 能更好地提取复杂特征,提高泛化能力。

3. ResNet优于普通网络的原理

一、在前向传播阶段:

每一层的输出不仅是通过当前层的变换,还加入了原始输入,使得网络能更容易地学习到恒等映射,避免信息在层数过多时丢失。每一层的输出不仅是通过当前层的变换F(x),还加入了原始输入x,使得网络能更容易地学习到恒等映射(identitymapping),避免信息在层数过多时丢失。 二、在反向传播中:

  • 普通网络:梯度传播过程中可能会因为网络层数过深,导致梯度消失或爆炸,使得网络很难训练。

  • ResNet:通过跳跃连接,梯度可以直接从后面的层传递到前面的层,保证了梯度稳定,并且能够有效更新所有层的权重。

正常网络反向传播通过每一层逐渐计算梯度,从最后一层向前传播梯度。由于层数过多,梯度可能会消失(当梯度变得非常小,无法有效更新权重)或者爆炸(当梯度非常大,导致权重更新过快)。

 

ResNet(带残差连接):梯度直接通过残差连接进行传递,使得梯度可以更轻松地传播到前面层。 反向传播时,跳跃连接保证了梯度可以有效传递到早期层,避免了梯度消失

 

4.残差网络代码

import torch
from torch import nn
from torch.nn import functional as F

class Residual_block(nn.Module):
    def __init__(self,input_channels,output_channels,first=False):
        super().__init__()
        self.first=first
        if first==True:
            self.conv1=nn.Conv2d(input_channels,output_channels,stride=2,kernel_size=3,padding=1)
            self.conv3=nn.Conv2d(input_channels,output_channels,kernel_size=1,stride=2)
        else:
            self.conv1=nn.Conv2d(output_channels,output_channels,kernel_size=3,padding=1)
        self.bn1=nn.BatchNorm2d(output_channels)
        self.conv2=nn.Conv2d(output_channels,output_channels,kernel_size=3,padding=1)
        self.bn2=nn.BatchNorm2d(output_channels)
        
    def forward(self,x):
        Y=F.relu(self.bn1(self.conv1(x)))
        Y=self.bn2(self.conv2(Y))
        if self.first==True:
            x=self.conv3(x)
        Y=x+Y
        return F.relu(Y)
def resnet_block(input_channels,output_channels,num_residual_block,special=False):
    blk=[]
    for i in range(num_residual_block):
        if i==0 and special==True:
            blk.append(Residual_block(input_channels,input_channels))
        if i==0 and special==False:
            blk.append(Residual_block(input_channels,output_channels,first=True))
        else:
            blk.append(Residual_block(output_channels,output_channels))
    return blk


b1=nn.Sequential(
    nn.Conv2d(kernel_size=7,in_channels=3,out_channels=64,stride=2,padding=3),
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=3,stride=2,padding=1),
)
R1=Residual_block(64,64)
x=torch.ones(1,3,224,224)
for layer in b1:
    x=layer(x)

b18_2=nn.Sequential(*resnet_block(64,64,2,special=True))
b18_3=nn.Sequential(*resnet_block(64,128,2))
b18_4=nn.Sequential(*resnet_block(128,256,2))
b18_5=nn.Sequential(*resnet_block(256,512,2))

b34_2=nn.Sequential(*resnet_block(64,64,3,special=True))
b34_3=nn.Sequential(*resnet_block(64,128,4))
b34_4=nn.Sequential(*resnet_block(128,256,6))
b34_5=nn.Sequential(*resnet_block(256,512,3))


#Resnet-18
Resnet_18=nn.Sequential(b1,b18_2,b18_3,b18_4,b18_5,nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(),nn.Linear(512,10))
#Resnet-34
Resnet_18=nn.Sequential(b1,b34_2,b34_3,b34_4,b34_5,nn.AdaptiveAvgPool2d((1,1)),nn.Flatten(),nn.Linear(512,10))

5. 附录_全部网络结构

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐