[动手学深度学习]29. 残差网络

"加入在卷积神经网络里面只了解一个神经网络的话,了解resnet就可以了"

统计学习里面存在模型偏差:

比如这样,左边随着迭代可能会远离目标点(f3比f6更近)

残差块

之前是一个一个层串起来,如果想要加层然后不会使模型变复杂的话,可以在里面再做一个加法

这样可以保证至少结果不会变差:g(x)=0那么结果f(x)还是和以前x一样的,(文字里的g(x)即图中的f(x),g(x)=0也即没有学到任何东西)

  • 有两种实现:

    1. x直接加在BN上
    2. 如果需要修改通道,x先经过1*1的卷积再加到BN上
  • 也可以使用不同的残差块

  • 有2中ResNet块

    1. 高宽减半ResNet块(步幅2)
    2. 后接多个高宽不变ResNet块

ResNet架构

类似VGG和GoogLeNet的总体架构

但替换成了ResNet块

前面的层一般都是77卷积和33pooling,后面可以替换成自己想要的样子

  • ResNet152

    ResNet152效果很好,一般用来刷分,因为太贵了(指的是卷积层的层数)
    一般可以用101、50、34,一般34用的比较多,效果不好可以上50

总结

  • 残差块使得很深的网络更加容易训练
    甚至可以训练1k层的网络
  • 残差网络对随后的深层神经网络设计产生了深远影响,无论是卷积类网络还是全连接类网络
python 复制代码
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

class Residual(nn.Module):
    def __init__(self, input_channels, num_channels, use_1x1conv=False, strides=1): # 本类的初始化函数, num_channels是输出通道数
        super().__init__()
        self.conv1 = nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1, stride=strides) # 3x3卷积层
        self.conv2 = nn.Conv2d(num_channels, num_channels, kernel_size=3, padding=1) # 使用nn.Conv2d默认的strides=1
        if use_1x1conv:
            self.conv3 = nn.Conv2d(input_channels, num_channels, kernel_size=1, stride=strides)
        else:
            self.conv3 = None
        self.bn1 = nn.BatchNorm2d(num_channels)
        self.bn2 = nn.BatchNorm2d(num_channels)
        self.relu = nn.ReLU(inplace=True) # inplace=True表示直接在原变量上进行操作, 不产生新的变量, 节省内存
    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)   # 这里是对X进行操作不是对Y
        Y += X
        return F.relu(Y)

# 输出输出形状一样
blk = Residual(3, 3)    # 输入3 输出3
X = torch.rand((4, 3, 6, 6))
Y = blk(X)
Y.shape

# 增加输出通道数的同时,减半输出的高和宽
blk = Residual(3, 6, use_1x1conv=True, strides=2) # 由3变为6.通道数增加
blk(X).shape

# 有这些就可以实现一个stage了

# ResNet的第一个stage, 和GoogleNet的第一个stage一样,都是卷积+池化
b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
                   nn.BatchNorm2d(64), nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

# class Residual为小block, resnet_block为大block, 为Resnet网络的一个stage
def resnet_block(input_channels, num_channels, num_residuals, first_block=False):
    blk = []
    for i in range(num_residuals):
        if i==0 and not first_block: # stage中不是第一个block则高宽减半
            blk.append(Residual(input_channels, num_channels, use_1x1conv=True, strides=2))
        else:
            blk.append(Residual(num_channels, num_channels))

    return blk

# 因为b1做了2次池化,所以高宽减半,所以输入通道数变为64,所以b2中的首次就不减半了
b2 = nn.Sequential(*resnet_block(64, 64, 2, first_block=True))
b3 = nn.Sequential(*resnet_block(64, 128, 2)) # b3 b4 b5的首次卷积层都减半
b4 = nn.Sequential(*resnet_block(128, 256, 2))
b5 = nn.Sequential(*resnet_block(256, 512, 2))

net = nn.Sequential(b1, b2, b3, b4, b5, nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten(), nn.Linear(512, 10))

查看网络中不同模块的输入形状是如何变化的

python 复制代码
# 网络中不同模块输入形状的变换
X = torch.rand((1, 1, 224, 224))
for layer in net:
    X = layer(X)
    print(layer.__class__.__name__, 'output shape:\t', X.shape)

训练模型

python 复制代码
# 训练模型
lr, num_epochs, batch_size = 0.05, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
相关推荐
Wuhan87827211m1 分钟前
微生物细胞检测与识别 大肠杆菌E.coli和其他细菌细胞自动检测与分类 RetinaNet+RegNet模型实现
人工智能·分类·数据挖掘
idkmn_1 分钟前
Agentic AI 基础概念
人工智能·python·深度学习·chatgpt·langchain
Lun3866buzha1 分钟前
【深度学习】Mask R-CNN在温室番茄成熟度检测中的应用——基于ResNet18与FPN的多级特征融合分类系统
深度学习·r语言·cnn
AI营销资讯站3 分钟前
原圈科技AI营销内容生产系统:企业降本增效的全流程智能方案
大数据·人工智能
图学习小组5 分钟前
PaCon:一种用于识别编程提交中问题求解策略的符号分析方法
人工智能·算法·机器学习
墨染倾城殇6 分钟前
蓝牙模块场景化应用与选型:高效连接,精准适配
网络·人工智能·蓝牙模块·低功耗蓝牙模块·飞易通科技·蓝牙模块推荐
码农阿豪6 分钟前
POP到店模式(LOC)业务规则深度解析:从配置到结算的全链路指南
大数据·网络·人工智能
日更嵌入式的打工仔6 分钟前
SSC Tools配置项中文详解
网络·笔记·信息与通信·ethercat
于是我说7 分钟前
如何判断一个视频到底是真实 MP4 直链,还是流媒体M3U8
网络·音视频
江上鹤.1488 分钟前
Day 41 早停策略和模型权重的保存
人工智能·深度学习·机器学习