李沐--动手学深度学习 ResNet

1.理论

2.残差块

python 复制代码
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

#ResNet沿用了VGG完整的3*3卷积层设计.残差块的实现如下:
#此代码生成两种类型的网络:
#一种是当use_1x1conv=False时,应用ReLU非线性函数之前,将输入添加到输出。
#另一种是当use_1x1conv=True时,添加通过1*1卷积调整通道和分辨率。
class Residual(nn.Module):
    def __init__(self,input_channels,num_channels,
                 use_1x1conv = False, strides = 1):
        super().__init__()
        self.conv1 = nn.Conv2d(input_channels,num_channels,
                               kernel_size=3,padding=1,stride=strides)
        self.conv2 = nn.Conv2d(num_channels,num_channels,
                               kernel_size=3,padding=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)

    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)

#下面来查看输入和输出形状一致的情况。
b1k = Residual(3,3)
X = torch.rand(4,3,6,6)
Y = b1k(X)
print(Y.shape)
#也可以在增加输出通道数的同时,减半输出的高和宽
b1k = Residual(3,6,use_1x1conv=True,strides=2)
print(b1k(X).shape)

3.ResNet模型

python 复制代码
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

#ResNet沿用了VGG完整的3*3卷积层设计.残差块的实现如下:
#此代码生成两种类型的网络:
#一种是当use_1x1conv=False时,应用ReLU非线性函数之前,将输入添加到输出。
#另一种是当use_1x1conv=True时,添加通过1*1卷积调整通道和分辨率。
class Residual(nn.Module):
    def __init__(self,input_channels,num_channels,
                 use_1x1conv = False, strides = 1):
        super().__init__()
        self.conv1 = nn.Conv2d(input_channels,num_channels,
                               kernel_size=3,padding=1,stride=strides)
        self.conv2 = nn.Conv2d(num_channels,num_channels,
                               kernel_size=3,padding=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)

    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)

'''
#下面来查看输入和输出形状一致的情况。
b1k = Residual(3,3)
X = torch.rand(4,3,6,6)
Y = b1k(X)
print(Y.shape)
#也可以在增加输出通道数的同时,减半输出的高和宽
b1k = Residual(3,6,use_1x1conv=True,strides=2)
print(b1k(X).shape)
'''

#ResNet的前两层跟之前介绍的GoogLeNet中的一样,在输出通道数为64、步幅为2的7*7卷积层后,接步幅为2的3*3的最大汇聚层
#不同之处在于ResNet每个卷积层后增加了批量规范化层。
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))
#GoogLeNet在后面接了4个由Inception块组成的模块。
#ResNet则使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。
#第一个模块的通道数同输入通道数一致。 由于之前已经使用了步幅为2的最大汇聚层,所以无须减小高和宽。
#之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并将高和宽减半。
def resnet_block(input_channels,num_channels,num_residuals,
                 first_block = False):
    b1k = []
    for i in range(num_residuals):
       if i == 0 and not first_block:
           b1k.append(Residual(input_channels,num_channels,
                               use_1x1conv=True,strides=2))
       else:
           b1k.append(Residual(num_channels,num_channels))
    return b1k
#接着在ResNet加入所有残差块,这里每个模块使用2个残差块。
b2 = nn.Sequential(*resnet_block(64,64,2,first_block=True))
b3 = nn.Sequential(*resnet_block(64,128,2))
b4 = nn.Sequential(*resnet_block(128,256,2))
b5 = nn.Sequential(*resnet_block(256,512,2))
#最后,与GoogLeNet一样,在ResNet中加入全局平均汇聚层,以及全连接层输出。
net = nn.Sequential(b1,b2,b3,b4,b5,
                    nn.AdaptiveAvgPool2d((1,1)),
                    nn.Flatten(),nn.Linear(512,10))


#在训练ResNet之前,让我们观察一下ResNet中不同模块的输入形状是如何变化的。
#在之前所有架构中,分辨率降低,通道数量增加,直到全局平均汇聚层聚集所有特征。
X = torch.rand(size=(1,1,224,224))
for layer in net:
    X = layer(X)
    print(layer.__class__.__name__,'output shape:\t',X.shape)

4.ResNet模型训练

python 复制代码
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

#ResNet沿用了VGG完整的3*3卷积层设计.残差块的实现如下:
#此代码生成两种类型的网络:
#一种是当use_1x1conv=False时,应用ReLU非线性函数之前,将输入添加到输出。
#另一种是当use_1x1conv=True时,添加通过1*1卷积调整通道和分辨率。
class Residual(nn.Module):
    def __init__(self,input_channels,num_channels,
                 use_1x1conv = False, strides = 1):
        super().__init__()
        self.conv1 = nn.Conv2d(input_channels,num_channels,
                               kernel_size=3,padding=1,stride=strides)
        self.conv2 = nn.Conv2d(num_channels,num_channels,
                               kernel_size=3,padding=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)

    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)

'''
#下面来查看输入和输出形状一致的情况。
b1k = Residual(3,3)
X = torch.rand(4,3,6,6)
Y = b1k(X)
print(Y.shape)
#也可以在增加输出通道数的同时,减半输出的高和宽
b1k = Residual(3,6,use_1x1conv=True,strides=2)
print(b1k(X).shape)
'''

#ResNet的前两层跟之前介绍的GoogLeNet中的一样,在输出通道数为64、步幅为2的7*7卷积层后,接步幅为2的3*3的最大汇聚层
#不同之处在于ResNet每个卷积层后增加了批量规范化层。
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))
#GoogLeNet在后面接了4个由Inception块组成的模块。
#ResNet则使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。
#第一个模块的通道数同输入通道数一致。 由于之前已经使用了步幅为2的最大汇聚层,所以无须减小高和宽。
#之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并将高和宽减半。
def resnet_block(input_channels,num_channels,num_residuals,
                 first_block = False):
    b1k = []
    for i in range(num_residuals):
       if i == 0 and not first_block:
           b1k.append(Residual(input_channels,num_channels,
                               use_1x1conv=True,strides=2))
       else:
           b1k.append(Residual(num_channels,num_channels))
    return b1k
#接着在ResNet加入所有残差块,这里每个模块使用2个残差块。
b2 = nn.Sequential(*resnet_block(64,64,2,first_block=True))
b3 = nn.Sequential(*resnet_block(64,128,2))
b4 = nn.Sequential(*resnet_block(128,256,2))
b5 = nn.Sequential(*resnet_block(256,512,2))
#最后,与GoogLeNet一样,在ResNet中加入全局平均汇聚层,以及全连接层输出。
net = nn.Sequential(b1,b2,b3,b4,b5,
                    nn.AdaptiveAvgPool2d((1,1)),
                    nn.Flatten(),nn.Linear(512,10))


'''
#在训练ResNet之前,让我们观察一下ResNet中不同模块的输入形状是如何变化的。
#在之前所有架构中,分辨率降低,通道数量增加,直到全局平均汇聚层聚集所有特征。
X = torch.rand(size=(1,1,224,224))
for layer in net:
    X = layer(X)
    print(layer.__class__.__name__,'output shape:\t',X.shape)
'''

#在Fashion-MNIST数据集上训练ResNet。
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())
d2l.plt.show()
相关推荐
console.log('npc')16 分钟前
第 1 章 项目概述与环境搭建
前端·人工智能·大模型
IT_陈寒25 分钟前
React组件莫名重渲染?你可能忘了这个小细节
前端·人工智能·后端
天远API32 分钟前
零信任架构实战:基于天远公安二要素政务版构建自动化不动产交易与契税核算实名核实网关
人工智能·架构·自动化·php
朋友圈自动点赞工具34 分钟前
浏览器端AI模型实测:五个可用场景
人工智能
承渊政道34 分钟前
【从零开始大模型开发与微调:基于PyTorch与ChatGLM】(从ChatGLM本地化到LoRA、PEFT的高级微调)
android·人工智能·pytorch·chatglm·本地化部署·大模型开发与微调
盟接之桥1 小时前
盟接之桥谈线束数字化-按单入按单出:线束企业库存管理的破局之道
大数据·前端·网络·数据库·人工智能·制造
Summer-Bright1 小时前
浏览器成了AI应用的新入口:智能体接管标签页之后,三本账谁买单
人工智能·chatgpt·agent·浏览器·智能体
xiaohaiAIgeo1 小时前
【2026年】紧急医疗运送送返:保额怎么看、多少够用
人工智能·科普知识
阿基拉de_Akir1 小时前
设计系统负责人:编译工作流中的上下游要求,评审的原材料从哪来,评审的产物给谁用
人工智能
思茂信息1 小时前
电磁仿真学涉及哪些算法?
人工智能·算法·数学建模·matlab·emc·电磁仿真·仿真辐射