ResNet18模型程序实现

1.插入要使用的模块:
    import torch
    import torch.nn as nn
    import torch.nn.functional as F

2.写基本残差块
原理图:


程序:
class BasicBlock(nn.Module):
    def __init__(self,inchannel,outchannel,stride=1):
        super(BasicBlock,self).__init__()
        self.left = nn.Sequential(
            #第一个卷积层,输入通道数为inchannel,输出通道数为outchannel,卷积核的大小为3x3,填充方式为1,步幅大小为stride
            #BN批量归一化层用于对数据进行标准化处理,防止数据异常;
            #ReLU激活函数用于引入非线性变换,增强模型的非线性表达能力。
            nn.Conv2d(inchannel,outchannel,kernel_size=3,stride=stride,padding=1,bias=False),
            nn.BatchNorm2d(outchannel),
            nn.ReLU(inplace=True),
            #第二个卷积层,卷积层的输入通道数为outchannel,输出通道数为outchannel,卷积核的大小为3x3,填充方式为1,步幅大小为1;
            # 批量归一化层用于对数据进行标准化处理,防止数据异常。
            nn.Conv2d(outchannel,outchannel,kernel_size=3,stride=1,padding=1,bias=False),
            nn.BatchNorm2d(outchannel)
        )

        self.shortcut = nn.Sequential()
        if stride != 1 or inchannel != outchannel:
            self.shortcut = nn.Sequential(
                #需要下采样
                #一个 1x1 的卷积层,用于将特征图的通道数从 inchannel 变为 outchannel,同时缩小特征图的宽高尺寸。
                # 一个批量归一化层,用于对数据进行标准化处理,防止数据异常
                #不需要下采样,则 self.shortcut 的定义为空序列即可。
                nn.Conv2d(inchannel,outchannel,kernel_size=1,stride=stride,bias=False),
                nn.BatchNorm2d(outchannel)
            )

    #前向传播函数,用于将输入特征图 x 转换为输出特征图
    def forward(self,x):
        # 将输入特征图 x 作为参数传入基本块左侧部分 self.left 中,执行卷积、批量归一化和激活等操作,得到输出特征图 out。
        # 将输入特征图 x 经过残差连接 self.shortcut 得到残差项,即 out = out + self.shortcut(x)。
        # 对得到的特征图 out 进行 ReLU 激活函数处理。
        # 返回得到的特征图 out 作为基本块的最终输出结果。
        out = self.left(x)              #对应图中F(x)
        out = out + self.shortcut(x)    #对应图中H(x)=F(x)+x
        out = F.relu(out)
        return out

搭建ResNet结构
原理图


程序:
class ResNet18(nn.Module):
    # ResidualBlock:基本块(BasicBlock)的类型,可以选择普通基本块或瓶颈基本块。
    # num_classes:最终分类的类别数。在训练过程中需要根据实际情况设定。
    # self.inchannel:输入特征图的通道数。
    # nn.Conv2d(3,64,kernel_size=7,stride=2,padding=1,bias=False):
    # 输入通道数为 3(即 RGB 三通道),输出通道数为 64,卷积核大小为 7x7,填充大小为 1,步幅为 2;
    # bias=False 表示不使用偏置项。
    # nn.BatchNorm2d(64):批量归一化层,用于对数据进行标准化处理,防止数据异常。
    # nn.ReLU(): ReLU 激活函数,用于引入非线性变换,增强模型的非线性表达能力。
    def __init__(self,ResidualBlock,num_classes=10):
        super(ResNet18,self).__init__()
        self.inchannel = 64
        self.conv1 = nn.Sequential(
            nn.Conv2d(3,64,kernel_size=7,stride=2,padding=1,bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(),
        )
        self.layer1 = self.make_layer(ResidualBlock,64,2,stride=1)
        self.layer2 = self.make_layer(ResidualBlock, 128, 2, stride=2)
        self.layer3 = self.make_layer(ResidualBlock, 256, 2, stride=2)
        self.layer4 = self.make_layer(ResidualBlock, 512, 2, stride=2)
        self.fc = nn.Linear(512,num_classes)

    # block:ResNet18 模型中基本块(BasicBlock)的类型,可以选择普通基本块或瓶颈基本块。
    # channels:当前残差模块中基本块的输出通道数。
    # num_blocks:当前残差模块中包含的基本块数量。
    # stride:当前残差模块中第一个基本块的步幅。
    def make_layer(self,block,channels,num_blocks,stride):
        strides = [stride] + [1] * (num_blocks - 1)
        layers = []
        # 将 block(self.inchannel,channels,stride) 添加到 layers 中,
        # 即生成一个输入通道数为 self.inchannel,输出通道数为 channels,步幅为 stride 的基本块,
        # 并将 self.inchannel 更新为 channels
        for stride in strides:
            layers.append(block(self.inchannel,channels,stride))
            self.inchannel = channels
        return nn.Sequential(*layers)

    # 前向传播函数 forward(),用于将输入特征图 x 转换为输出分类结果
    # 将输入特征图x作为参数传入第一个卷积层self.conv1中,执行卷积、批量归一化和激活等操作,得到输出特征图out。
    # 将输出特征图out依次经过四个由基本块组成的残差模块self.layer1、self.layer2、self.layer3和
    # self.layer4,得到更加抽象和丰富的特征表示。
    # 对最后一个残差模块的输出特征图out进行4x4的平均池化,得到一个尺寸为1x1的特征图。
    # 将特征图out展开为向量形式,输入到全连接层self.fc中,进行分类操作。
    # 返回分类结果out。
    def forward(self,x):
        out = self.conv1(x)
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        # 一个 4x4 的平均池化操作,用于将最后一个残差模块输出的特征图进行降维,减少参数数量和计算量
        out = F.avg_pool2d(out,4)
        # 一个reshape操作,将平均池化后的特征图展开成向量形式,输入到全连接层中
        out = out.view(out.size(0),-1)
        # 全连接层,用于将ResNet18模型最后一层输出的特征图转换为分类结果。
        out = self.fc(out)
        return out

完整程序:
import torch
import torch.nn as nn
import torch.nn.functional as F

class BasicBlock(nn.Module):
    def __init__(self,inchannel,outchannel,stride=1):
        super(BasicBlock,self).__init__()
        self.left = nn.Sequential(
            #第一个卷积层,输入通道数为inchannel,输出通道数为outchannel,卷积核的大小为3x3,填充方式为1,步幅大小为stride
            #BN批量归一化层用于对数据进行标准化处理,防止数据异常;
            #ReLU激活函数用于引入非线性变换,增强模型的非线性表达能力。
            nn.Conv2d(inchannel,outchannel,kernel_size=3,stride=stride,padding=1,bias=False),
            nn.BatchNorm2d(outchannel),
            nn.ReLU(inplace=True),
            #第二个卷积层,卷积层的输入通道数为outchannel,输出通道数为outchannel,卷积核的大小为3x3,填充方式为1,步幅大小为1;
            # 批量归一化层用于对数据进行标准化处理,防止数据异常。
            nn.Conv2d(outchannel,outchannel,kernel_size=3,stride=1,padding=1,bias=False),
            nn.BatchNorm2d(outchannel)
        )

        self.shortcut = nn.Sequential()
        if stride != 1 or inchannel != outchannel:
            self.shortcut = nn.Sequential(
                #需要下采样
                #一个 1x1 的卷积层,用于将特征图的通道数从 inchannel 变为 outchannel,同时缩小特征图的宽高尺寸。
                # 一个批量归一化层,用于对数据进行标准化处理,防止数据异常
                #不需要下采样,则 self.shortcut 的定义为空序列即可。
                nn.Conv2d(inchannel,outchannel,kernel_size=1,stride=stride,bias=False),
                nn.BatchNorm2d(outchannel)
            )

    #前向传播函数,用于将输入特征图 x 转换为输出特征图
    def forward(self,x):
        # 将输入特征图 x 作为参数传入基本块左侧部分 self.left 中,执行卷积、批量归一化和激活等操作,得到输出特征图 out。
        # 将输入特征图 x 经过残差连接 self.shortcut 得到残差项,即 out = out + self.shortcut(x)。
        # 对得到的特征图 out 进行 ReLU 激活函数处理。
        # 返回得到的特征图 out 作为基本块的最终输出结果。
        out = self.left(x)
        out = out + self.shortcut(x)
        out = F.relu(out)
        return out

class ResNet18(nn.Module):
    # ResidualBlock:基本块(BasicBlock)的类型,可以选择普通基本块或瓶颈基本块。
    # num_classes:最终分类的类别数。在训练过程中需要根据实际情况设定。
    # self.inchannel:输入特征图的通道数。
    # nn.Conv2d(3,64,kernel_size=3,stride=1,padding=1,bias=False):
    # 输入通道数为 3(即 RGB 三通道),输出通道数为 64,卷积核大小为 3x3,填充大小为 1,步幅为 1;
    # bias=False 表示不使用偏置项。
    # nn.BatchNorm2d(64):批量归一化层,用于对数据进行标准化处理,防止数据异常。
    # nn.ReLU(): ReLU 激活函数,用于引入非线性变换,增强模型的非线性表达能力。
    def __init__(self,ResidualBlock,num_classes=10):
        super(ResNet18,self).__init__()
        self.inchannel = 64
        self.conv1 = nn.Sequential(
            nn.Conv2d(3,64,kernel_size=7,stride=2,padding=1,bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(),
        )
        self.layer1 = self.make_layer(ResidualBlock,64,2,stride=1)
        self.layer2 = self.make_layer(ResidualBlock, 128, 2, stride=2)
        self.layer3 = self.make_layer(ResidualBlock, 256, 2, stride=2)
        self.layer4 = self.make_layer(ResidualBlock, 512, 2, stride=2)
        self.fc = nn.Linear(512,num_classes)

    # block:ResNet18 模型中基本块(BasicBlock)的类型,可以选择普通基本块或瓶颈基本块。
    # channels:当前残差模块中基本块的输出通道数。
    # num_blocks:当前残差模块中包含的基本块数量。
    # stride:当前残差模块中第一个基本块的步幅。
    def make_layer(self,block,channels,num_blocks,stride):
        strides = [stride] + [1] * (num_blocks - 1)
        layers = []
        # 将 block(self.inchannel,channels,stride) 添加到 layers 中,
        # 即生成一个输入通道数为 self.inchannel,输出通道数为 channels,步幅为 stride 的基本块,
        # 并将 self.inchannel 更新为 channels
        for stride in strides:
            layers.append(block(self.inchannel,channels,stride))
            self.inchannel = channels
        return nn.Sequential(*layers)

    # 前向传播函数 forward(),用于将输入特征图 x 转换为输出分类结果
    # 将输入特征图x作为参数传入第一个卷积层self.conv1中,执行卷积、批量归一化和激活等操作,得到输出特征图out。
    # 将输出特征图out依次经过四个由基本块组成的残差模块self.layer1、self.layer2、self.layer3和
    # self.layer4,得到更加抽象和丰富的特征表示。
    # 对最后一个残差模块的输出特征图out进行4x4的平均池化,得到一个尺寸为1x1的特征图。
    # 将特征图out展开为向量形式,输入到全连接层self.fc中,进行分类操作。
    # 返回分类结果out。
    def forward(self,x):
        out = self.conv1(x)
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = self.layer4(out)
        # 一个 4x4 的平均池化操作,用于将最后一个残差模块输出的特征图进行降维,减少参数数量和计算量
        out = F.avg_pool2d(out,4)
        # 一个reshape操作,将平均池化后的特征图展开成向量形式,输入到全连接层中
        out = out.view(out.size(0),-1)
        # 全连接层,用于将ResNet18模型最后一层输出的特征图转换为分类结果。
        out = self.fc(out)
        return out

def ResNet_18():
    return ResNet18(BasicBlock)

res18 = ResNet_18()
print(res18)

运行结果:
ResNet18(
  (conv1): Sequential(
    (0): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(1, 1), bias=False)
    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
  )
  (layer1): Sequential(
    (0): BasicBlock(
      (left): Sequential(
        (0): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        (2): ReLU(inplace=True)
        (3): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
      (shortcut): Sequential()
    )
    (1): BasicBlock(
      (left): Sequential(
        (0): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        (2): ReLU(inplace=True)
        (3): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
      (shortcut): Sequential()
    )
  )
  (layer2): Sequential(
    (0): BasicBlock(
      (left): Sequential(
        (0): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
        (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        (2): ReLU(inplace=True)
        (3): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (4): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
      (shortcut): Sequential(
        (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)
        (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
    )
    (1): BasicBlock(
      (left): Sequential(
        (0): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        (2): ReLU(inplace=True)
        (3): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (4): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
      (shortcut): Sequential()
    )
  )
  (layer3): Sequential(
    (0): BasicBlock(
      (left): Sequential(
        (0): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
        (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        (2): ReLU(inplace=True)
        (3): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (4): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
      (shortcut): Sequential(
        (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)
        (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
    )
    (1): BasicBlock(
      (left): Sequential(
        (0): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        (2): ReLU(inplace=True)
        (3): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (4): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
      (shortcut): Sequential()
    )
  )
  (layer4): Sequential(
    (0): BasicBlock(
      (left): Sequential(
        (0): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
        (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        (2): ReLU(inplace=True)
        (3): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (4): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
      (shortcut): Sequential(
        (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)
        (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
    )
    (1): BasicBlock(
      (left): Sequential(
        (0): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        (2): ReLU(inplace=True)
        (3): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (4): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
      (shortcut): Sequential()
    )
  )
  (fc): Linear(in_features=512, out_features=10, bias=True)
)

进程已结束,退出代码为 0

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彭毓众

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值