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