李沐--动手学深度学习 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()
相关推荐
张人玉1 小时前
人工智能——猴子摘香蕉问题
人工智能
草莓屁屁我不吃1 小时前
Siri因ChatGPT-4o升级:我们的个人信息还安全吗?
人工智能·安全·chatgpt·chatgpt-4o
小言从不摸鱼1 小时前
【AI大模型】ChatGPT模型原理介绍(下)
人工智能·python·深度学习·机器学习·自然语言处理·chatgpt
AI科研视界1 小时前
ChatGPT+2:修订初始AI安全性和超级智能假设
人工智能·chatgpt
霍格沃兹测试开发学社测试人社区1 小时前
人工智能 | 基于ChatGPT开发人工智能服务平台
软件测试·人工智能·测试开发·chatgpt
小R资源2 小时前
3款免费的GPT类工具
人工智能·gpt·chatgpt·ai作画·ai模型·国内免费
artificiali5 小时前
Anaconda配置pytorch的基本操作
人工智能·pytorch·python
酱香编程,风雨兼程5 小时前
深度学习——基础知识
人工智能·深度学习
Lossya5 小时前
【机器学习】参数学习的基本概念以及贝叶斯网络的参数学习和马尔可夫随机场的参数学习
人工智能·学习·机器学习·贝叶斯网络·马尔科夫随机场·参数学习
#include<菜鸡>6 小时前
动手学深度学习(pytorch土堆)-04torchvision中数据集的使用
人工智能·pytorch·深度学习