Resnet 18 及34 的代码复现(基于李沐的动手学深度学习)

文章介绍了ResNet18和ResNet34两种经典卷积神经网络模型的实现代码,包括残差块的基本结构和网络主体的构建过程。ResNet通过引入残差学习,解决了深度网络训练时的梯度消失问题。代码展示了如何使用PyTorch构建这两个模型,适用于图像分类任务。
摘要由CSDN通过智能技术生成

 resnet作为一个经典的图像分类模型,下面是对于resnet18及34的复现代码,具体细节请查阅resnet原文:https://arxiv.org/abs/1512.03385

一、残差块

 

 BasicBlock模块有两种模式,一种是输入X以后需要用1x1卷积层来进行下采样,从而升维,将通道数加倍,其中的步幅stride=2,一种是不需要1x1卷积层,直接将x与拟合的残差F(X)相加。basicblock 采用两层3x3的卷积层,每层卷积后经过批量规范化以及Relu函数激活。以下是详细代码。(basicblock)主要是用于resnet18以及resnet34

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_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)

二、resnet18主体

 可以看到conv1由7x7的卷积层组成,输出为64,stride=2

b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
                   #该输入为通道数1,可修改为3,取决于图片
                   nn.BatchNorm2d(64), nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

然后构建resnet18

#resnet18
def resnet18(num_classes,in_channels=1):
    def resnet_block(in_channels,out_channels,num_residuals,
                     first_block=False):
        blk = []
        for i in range(num_residuals):
            if i == 0 and not first_block:
                blk.append(Residual(in_channels, out_channels,
                                    use_1x1conv=True, strides=2))
            else:
                blk.append(Residual(out_channels, out_channels))
        return nn.Sequential(*blk)
    
    net = nn.Sequential(b1)
    net.add_module("resnet_block1", resnet_block(64, 64, 2, first_block=True))
    net.add_module("resnet_block2", resnet_block(64, 128, 2))
    net.add_module("resnet_block3", resnet_block(128, 256, 2))
    net.add_module("resnet_block4", resnet_block(256, 512, 2))
    net.add_module("global_avg_pool", nn.AdaptiveAvgPool2d((1,1)))
    net.add_module("fc", nn.Sequential(nn.Flatten(),
                                       nn.Linear(512, num_classes)))
    return net

上述的num_classes表示需要分类的类别数

resnet18的架构图片如下

其中conv_3,conv_4,conv_5的三个模块中每个模块中的第一个残差块的输入输出通道数分别从64——128,128——256,256——512,并需要进行1x1的卷积将输入x下采样,从而保证输出的x的通道数与两层3X3卷积后的通道数一致。并且通过第一层的步幅为2的3X3卷积以后,通道数加倍,图片的尺寸,长和宽各变为原来的一半,从而减少了参数量。

以下是输出尺寸的计算公式:

一个尺寸 a*a 的特征图,经过 b*b 的卷积层,步幅(stride)=c,填充(padding)=d,输出特征图的尺寸为:[(a-b+2d)/c]+1

三、Resnet34

resnet34的残差块与resnet18类似,只是conv_2,conv_3,conv_4,conv_5中resnet34网络更深一点。下面是代码实现

def resnet34(num_classes,in_channels=1):
    def resnet_block(in_channels,out_channels,num_residuals,
                     first_block=False):
        blk = []
        for i in range(num_residuals):
            if i == 0 and not first_block:
                blk.append(Residual_1(in_channels, out_channels,
                                    use_1x1conv=True, strides=2))
            else:
                blk.append(Residual_1(out_channels, out_channels))
        return nn.Sequential(*blk)
    
    net = nn.Sequential(nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3),
                        nn.BatchNorm2d(64),
                        nn.ReLU(),
                        nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
    
    net.add_module("resnet_block1", resnet_block(64, 64, 3, first_block=True))
    net.add_module("resnet_block2", resnet_block(64, 128, 4))
    net.add_module("resnet_block3", resnet_block(128, 256, 6))
    net.add_module("resnet_block4", resnet_block(256, 512, 3))
    net.add_module("global_avg_pool", nn.AdaptiveAvgPool2d((1,1)))
    net.add_module("fc", nn.Sequential(nn.Flatten(),
                                       nn.Linear(512, num_classes)))
    return net

以下为完整代码

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_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)

b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
                   #该输入为通道数1,可修改为3,取决于图片
                   nn.BatchNorm2d(64), nn.ReLU(),
                   nn.MaxPool2d(kernel_size=3, stride=2, padding=1))


#resnet18
def resnet18(num_classes,in_channels=1):
    def resnet_block(in_channels,out_channels,num_residuals,
                     first_block=False):
        blk = []
        for i in range(num_residuals):
            if i == 0 and not first_block:
                blk.append(Residual(in_channels, out_channels,
                                    use_1x1conv=True, strides=2))
            else:
                blk.append(Residual(out_channels, out_channels))
        return nn.Sequential(*blk)
    
    net = nn.Sequential(b1)
    net.add_module("resnet_block1", resnet_block(64, 64, 2, first_block=True))
    net.add_module("resnet_block2", resnet_block(64, 128, 2))
    net.add_module("resnet_block3", resnet_block(128, 256, 2))
    net.add_module("resnet_block4", resnet_block(256, 512, 2))
    net.add_module("global_avg_pool", nn.AdaptiveAvgPool2d((1,1)))
    net.add_module("fc", nn.Sequential(nn.Flatten(),
                                       nn.Linear(512, num_classes)))
    return net



def resnet34(num_classes,in_channels=1):
    def resnet_block(in_channels,out_channels,num_residuals,
                     first_block=False):
        blk = []
        for i in range(num_residuals):
            if i == 0 and not first_block:
                blk.append(Residual_1(in_channels, out_channels,
                                    use_1x1conv=True, strides=2))
            else:
                blk.append(Residual_1(out_channels, out_channels))
        return nn.Sequential(*blk)
    
    net = nn.Sequential(nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3),
                        nn.BatchNorm2d(64),
                        nn.ReLU(),
                        nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
    
    net.add_module("resnet_block1", resnet_block(64, 64, 3, first_block=True))
    net.add_module("resnet_block2", resnet_block(64, 128, 4))
    net.add_module("resnet_block3", resnet_block(128, 256, 6))
    net.add_module("resnet_block4", resnet_block(256, 512, 3))
    net.add_module("global_avg_pool", nn.AdaptiveAvgPool2d((1,1)))
    net.add_module("fc", nn.Sequential(nn.Flatten(),
                                       nn.Linear(512, num_classes)))
    return net
                

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值