百度联盟网站备案信息,wordpress初学,网站运营与建设方案,宜昌 网站建设 公司Pytorch | 从零构建ResNet对CIFAR10进行分类 CIFAR10数据集ResNet核心思想网络结构创新点优点应用 ResNet结构代码详解结构代码代码详解BasicBlock 类ResNet 类ResNet18、ResNet34、ResNet50、ResNet101、ResNet152函数 训练过程和测试结果代码汇总resnet.pytrain.pytest.py 前… Pytorch | 从零构建ResNet对CIFAR10进行分类 CIFAR10数据集ResNet核心思想网络结构创新点优点应用 ResNet结构代码详解结构代码代码详解BasicBlock 类ResNet 类ResNet18、ResNet34、ResNet50、ResNet101、ResNet152函数 训练过程和测试结果代码汇总resnet.pytrain.pytest.py 前面文章我们构建了AlexNet、Vgg、GoogleNet对CIFAR10进行分类 Pytorch | 从零构建AlexNet对CIFAR10进行分类 Pytorch | 从零构建Vgg对CIFAR10进行分类 Pytorch | 从零构建GoogleNet对CIFAR10进行分类 这篇文章我们来构建ResNet.
CIFAR10数据集
CIFAR-10数据集是由加拿大高级研究所CIFAR收集整理的用于图像识别研究的常用数据集基本信息如下
数据规模该数据集包含60,000张彩色图像分为10个不同的类别每个类别有6,000张图像。通常将其中50,000张作为训练集用于模型的训练10,000张作为测试集用于评估模型的性能。图像尺寸所有图像的尺寸均为32×32像素这相对较小的尺寸使得模型在处理该数据集时能够相对快速地进行训练和推理但也增加了图像分类的难度。类别内容涵盖了飞机plane、汽车car、鸟bird、猫cat、鹿deer、狗dog、青蛙frog、马horse、船ship、卡车truck这10个不同的类别这些类别都是现实世界中常见的物体具有一定的代表性。
下面是一些示例样本
ResNet
ResNetResidual Network即残差网络是由微软研究院的何恺明等人在2015年提出的一种深度卷积神经网络架构它在ILSVRC 2015图像识别挑战赛中取得了优异成绩在图像分类、目标检测、语义分割等计算机视觉任务中具有广泛应用。以下是对ResNet的详细介绍
核心思想
解决梯度消失和退化问题随着神经网络层数的增加会出现梯度消失或梯度爆炸问题导致模型难以训练。同时还会出现网络退化现象即增加网络层数后准确率反而下降。ResNet的核心思想是引入残差连接Residual Connection通过跨层的shortcut连接将输入直接传递到后面的层使得后面的层可以学习到输入的残差从而缓解了梯度消失和网络退化问题。
网络结构
基本残差块ResNet的基本组成单元是残差块Residual Block。一个典型的残差块包含两个3×3卷积层中间有一个ReLU激活函数并且在第二个卷积层之后也有一个ReLU激活函数。输入通过一个shortcut连接直接与残差块的输出相加形成残差学习。不同层数的架构ResNet有多种不同层数的架构如ResNet-18、ResNet-34、ResNet-50、ResNet-101和ResNet-152等。其中数字表示网络中卷积层和全连接层的总层数。层数越深模型的表示能力越强但计算成本也越高。
创新点
瓶颈结构在ResNet-50及更深的网络中采用了瓶颈结构Bottleneck的残差块。这种结构先使用1×1卷积层进行降维然后使用3×3卷积层进行特征提取最后再使用1×1卷积层进行升维这样可以在减少计算量的同时增加网络的深度和宽度提高模型的性能。全局平均池化在网络的最后一层ResNet采用了全局平均池化Global Average Pooling代替传统的全连接层进行分类。全局平均池化可以将每个特征图的空间维度压缩为一个值得到一个固定长度的特征向量然后直接输入到分类器中进行分类。
优点
训练深度网络更容易残差连接使得梯度能够更有效地在网络中传播大大降低了训练深度网络的难度使得可以成功训练上百层甚至上千层的网络。性能出色在各种图像识别任务中ResNet都取得了非常出色的性能相比之前的网络结构具有更高的准确率和更好的泛化能力。模型可扩展性强可以方便地通过增加残差块的数量来扩展网络的深度以适应不同的任务和数据集需求。
应用
图像分类ResNet在图像分类任务中取得了巨大成功如在ImageNet数据集上达到了很高的准确率成为了图像分类领域的主流模型之一。目标检测与其他目标检测算法结合如Faster R-CNN、YOLO等通过提取图像的特征提高目标检测的准确率和召回率。语义分割用于对图像进行像素级的分类将图像中的每个像素分配到不同的类别中在城市景观分割、医学图像分割等领域有广泛应用。
ResNet结构代码详解
结构代码
import torch
import torch.nn as nnclass BasicBlock(nn.Module):expansion 1def __init__(self, in_channels, out_channels, stride1):super(BasicBlock, self).__init__()self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse)self.bn1 nn.BatchNorm2d(out_channels)self.relu nn.ReLU(inplaceTrue)self.conv2 nn.Conv2d(out_channels, out_channels * BasicBlock.expansion, kernel_size3, padding1, biasFalse)self.bn2 nn.BatchNorm2d(out_channels * BasicBlock.expansion)self.shortcut nn.Sequential()if stride ! 1 or in_channels ! out_channels * BasicBlock.expansion:self.shortcut nn.Sequential(nn.Conv2d(in_channels, out_channels * BasicBlock.expansion, kernel_size1, stridestride, biasFalse),nn.BatchNorm2d(out_channels * BasicBlock.expansion))def forward(self, x):out self.conv1(x)out self.bn1(out)out self.relu(out)out self.conv2(out)out self.bn2(out)out self.shortcut(x)out self.relu(out)return outclass ResNet(nn.Module):def __init__(self, block, num_blocks, num_classes):super(ResNet, self).__init__()self.in_channels 64self.conv1 nn.Conv2d(3, 64, kernel_size3, padding1, biasFalse)self.bn1 nn.BatchNorm2d(64)self.relu nn.ReLU(inplaceTrue)self.maxpool nn.MaxPool2d(kernel_size3, stride1, padding1)self.layer1 self._make_layer(block, 64, num_blocks[0], 1)self.layer2 self._make_layer(block, 128, num_blocks[1], 2)self.layer3 self._make_layer(block, 256, num_blocks[2], 2)self.layer4 self._make_layer(block, 512, num_blocks[3], 2)self.avgpool nn.AdaptiveAvgPool2d((1, 1))self.fc nn.Linear(512 * block.expansion, num_classes)def _make_layer(self, block, out_channels, num_blocks, stride1):strides [stride] [1] * (num_blocks - 1)layers []for stride in strides:layers.append(block(self.in_channels, out_channels, stride))self.in_channels out_channels * block.expansionreturn nn.Sequential(*layers)def forward(self, x):out self.conv1(x)out self.bn1(out)out self.relu(out)out self.maxpool(out)out self.layer1(out)out self.layer2(out)out self.layer3(out)out self.layer4(out)out self.avgpool(out)out out.view(out.size(0), -1)out self.fc(out)return out# ResNet18, ResNet34
def ResNet18(num_classes):return ResNet(BasicBlock, [2, 2, 2, 2], num_classes)def ResNet34(num_classes):return ResNet(BasicBlock, [3, 4, 6, 3], num_classes)# ResNet50, ResNet101, ResNet152 需要 BottleNeck
class Bottleneck(nn.Module):expansion 4def __init__(self, in_channels, out_channels, stride1):super(Bottleneck, self).__init__()self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size1, biasFalse)self.bn1 nn.BatchNorm2d(out_channels)self.conv2 nn.Conv2d(out_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse)self.bn2 nn.BatchNorm2d(out_channels)self.conv3 nn.Conv2d(out_channels, out_channels * self.expansion, kernel_size1, biasFalse)self.bn3 nn.BatchNorm2d(out_channels * self.expansion)self.relu nn.ReLU(inplaceTrue)self.shortcut nn.Sequential()if stride ! 1 or in_channels ! out_channels * self.expansion:self.shortcut nn.Sequential(nn.Conv2d(in_channels, out_channels * self.expansion, kernel_size1, stridestride, biasFalse),nn.BatchNorm2d(out_channels * self.expansion))def forward(self, x):out self.conv1(x)out self.bn1(out)out self.relu(out)out self.conv2(out)out self.bn2(out)out self.relu(out)out self.conv3(out)out self.bn3(out)out self.shortcut(x)out self.relu(out)return outdef ResNet50(num_classes):return ResNet(Bottleneck, [3, 4, 6, 3], num_classes)def ResNet101(num_classes):return ResNet(Bottleneck, [3, 4, 23, 3], num_classes)def ResNet152(num_classes):return ResNet(Bottleneck, [3, 8, 36, 3], num_classes)代码详解
以下是对上述提供的PyTorch代码的详细解释这段代码实现了经典的ResNet残差网络系列模型包括ResNet-18、ResNet-34、ResNet-50、ResNet-101和ResNet-152等不同深度的网络架构
BasicBlock 类
class BasicBlock(nn.Module):expansion 1def __init__(self, in_channels, out_channels, stride1):super(BasicBlock, self).__init__()self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse)self.bn1 nn.BatchNorm2d(out_channels)self.relu nn.ReLU(inplaceTrue)self.conv2 nn.Conv2d(out_channels, out_channels * BasicBlock.expansion, kernel_size3, padding1, biasFalse)self.bn2 nn.BatchNorm2d(out_channels * BasicBlock.expansion)self.shortcut nn.Sequential()if stride! 1 or in_channels! out_channels * BasicBlock.expansion:self.shortcut nn.Sequential(nn.Conv2d(in_channels, out_channels * BasicBlock.expansion, kernel_size1, stridestride, biasFalse),nn.BatchNorm2d(out_channels * BasicBlock.expansion))类定义与属性 定义了一个名为BasicBlock的类继承自nn.Module这是PyTorch中定义神经网络模块的基类。expansion属性被设置为1用于表示该基本块在通道维度上的扩展倍数在BasicBlock中通道数不会进行额外的扩展后续的Bottleneck块会有不同的扩展倍数。 初始化方法__init__ 首先调用父类nn.Module的初始化方法super(BasicBlock, self).__init__()确保模块正确初始化。定义了两个卷积层conv1和conv2 conv1输入通道数为in_channels输出通道数为out_channels卷积核大小为3×3步长为stride填充为1并且不使用偏置biasFalse这是遵循ResNet论文中的实现方式通常配合后续的BatchNorm使用。conv2输入通道数为out_channels输出通道数为out_channels * BasicBlock.expansion实际就是out_channels因为expansion为1卷积核大小同样是3×3填充为1无偏置。 定义了两个BatchNorm2d层bn1和bn2分别对应两个卷积层之后用于对卷积后的特征进行归一化处理有助于加速训练和提高模型的稳定性。定义了一个ReLU激活函数relu并且设置inplaceTrue表示直接在原张量上进行激活操作节省内存空间但要注意使用不当可能导致梯度计算问题如前面提到的错误情况。定义了shortcut初始化为一个空的nn.Sequential序列。当输入和输出的通道数不一致或者步长不为1时意味着尺寸或通道数有变化会重新构建shortcut使其包含一个1×1卷积层用于调整通道数和一个BatchNorm2d层以保证shortcut连接的特征维度能与主分支的输出特征维度相匹配便于后续进行相加操作。 def forward(self, x):out self.conv1(x)out self.bn1(out)out self.relu(out)out self.conv2(out)out self.bn2(out)out self.shortcut(x)out self.relu(out)return out前向传播方法forward 首先将输入x经过conv1卷积、bn1归一化后再通过relu激活函数得到中间特征。接着将该中间特征再经过conv2卷积和bn2归一化。然后将主分支得到的特征out与shortcut分支直接连接输入x经过调整后的特征进行逐元素相加实现残差连接的操作。最后再经过一次relu激活函数后返回结果作为该基本块的输出。
ResNet 类
class ResNet(nn.Module):def __init__(self, block, num_blocks, num_classes):super(ResNet, self).__init__()self.in_channels 64self.conv1 nn.Conv2d(3, 64, kernel_size3, padding1, biasFalse)self.bn1 nn.BatchNorm2d(64)self.relu nn.ReLU(inplaceTrue)self.maxpool nn.MaxPool2d(kernel_size3, stride1, padding1)self.layer1 self._make_layer(block, 64, num_blocks[0], 1)self.layer2 self._make_layer(block, 128, num_blocks[1], 2)self.layer3 self._make_layer(block, 256, num_blocks[2], 2)self.layer4 self._make_layer(block, 512, num_blocks[3], 2)self.avgpool nn.AdaptiveAvgPool2d((1, 1))self.fc nn.Linear(512 * block.expansion, num_classes)类定义与属性 定义了ResNet类同样继承自nn.Module用于构建完整的ResNet网络架构。初始化了一个属性in_channels为64用于记录当前层的输入通道数后续会动态更新。定义了网络的起始层包括一个3×3卷积层conv1输入通道为3对应彩色图像的RGB三个通道输出通道为64一个BatchNorm2d层bn1用于归一化一个ReLU激活函数relu以及一个最大池化层maxpool其参数设置按照常规的ResNet结构配置。分别定义了layer1、layer2、layer3、layer4这四层网络结构它们通过调用_make_layer方法来构建每层的输出通道数以及重复的块数量由传入的参数决定并且随着层数加深步长会相应改变从第二层开始步长为2用于逐步减小特征图尺寸。定义了一个自适应平均池化层avgpool它能将输入的特征图尺寸自适应地变为(1, 1)大小无论输入特征图的尺寸原本是多少便于后续全连接层处理。最后定义了一个全连接层fc用于将池化后的特征映射到指定的类别数num_classes上进行分类。 def _make_layer(self, block, out_channels, num_blocks, stride1):strides [stride] [1] * (num_blocks - 1)layers []for stride in strides:layers.append(block(self.in_channels, out_channels, stride))self.in_channels out_channels * block.expansionreturn nn.Sequential(*layers)_make_layer方法 这个方法用于构建ResNet中的每一层网络结构由多个基本块组成。首先根据传入的stride和num_blocks生成一个步长列表strides例如如果传入stride2和num_blocks3那么strides会是[2, 1, 1]意味着第一个基本块可能会改变特征图的尺寸和通道数后面的基本块保持步长为1。然后循环遍历strides列表每次创建一个指定的block可以是BasicBlock或者后续定义的Bottleneck块并传入当前的输入通道数、输出通道数以及对应的步长将创建好的块添加到layers列表中。同时更新self.in_channels为当前块输出的通道数考虑了块的扩展倍数。最后将layers列表中的所有块组合成一个nn.Sequential序列并返回形成一层完整的网络结构。 def forward(self, x):out self.conv1(x)out self.bn1(out)out self.relu(out)out self.maxpool(out)out self.layer1(out)out self.layer2(out)out self.layer3(out)out self.layer4(out)out self.avgpool(out)out out.view(out.size(0), -1)out self.fc(out)return out前向传播方法forward 首先将输入x依次经过网络起始层的卷积、归一化、激活和池化操作得到初步的特征表示。然后将该特征依次通过layer1、layer2、layer3、layer4这四层网络结构不断提取和融合特征每一层都会进一步加深特征的抽象程度并且改变特征图的尺寸和通道数。接着经过自适应平均池化层avgpool将特征图变为(1, 1)大小的特征向量。通过out.view(out.size(0), -1)操作将特征向量展平为一维向量使其能输入到全连接层fc中。最后将全连接层的输出作为整个网络的最终输出返回分类结果。
ResNet18、ResNet34、ResNet50、ResNet101、ResNet152函数
# ResNet18, ResNet34
def ResNet18(num_classes):return ResNet(BasicBlock, [2, 2, 2, 2], num_classes)def ResNet34(num_classes):return ResNet(BasicBlock, [3, 4, 6, 3], num_classes)这两个函数分别用于创建ResNet-18和ResNet-34网络模型。它们通过调用ResNet类的构造函数传入BasicBlock作为构建块类型以及对应不同层数的重复块数量列表如ResNet-18中每层分别重复2个基本块还有指定的类别数num_classes最终返回构建好的相应深度的ResNet模型实例用于图像分类等任务。
# ResNet50, ResNet101, ResNet152 需要 BottleNeck
class Bottleneck(nn.Module):expansion 4def __init__(self, in_channels, out_channels, stride1):super(Bottleneck, self).__init__()self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size1, biasFalse)self.bn1 nn.BatchNorm2d(out_channels)self.conv2 nn.Conv2d(out_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse)self.bn2 nn.BatchNorm2d(out_channels)self.conv3 nn.Conv2d(out_channels, out_channels * self.expansion, kernel_size1, biasFalse)self.bn3 nn.BatchNorm2d(out_channels * self.expansion)self.relu nn.ReLU(inplaceTrue)self.shortcut nn.Sequential()if stride! 1 or in_channels! out_channels * self.expansion:self.shortcut nn.Sequential(nn.Conv2d(in_channels, out_channels * self.expansion, kernel_size1, stridestride, biasFalse),nn.BatchNorm2d(out_channels * self.expansion))Bottleneck类定义与初始化 定义了Bottleneck类同样继承自nn.Module用于构建更深层的ResNet网络如ResNet-50及以上中的基本块。expansion属性被设置为4意味着该块在经过一系列操作后输出通道数会是输入通道数的4倍通过这种方式在增加网络深度的同时控制计算量。在初始化方法中定义了三个卷积层conv1、conv2、conv3分别是1×1卷积用于降维、3×3卷积进行主要的特征提取、1×1卷积用于升维并且每个卷积层后都有对应的BatchNorm2d层进行归一化还有ReLU激活函数用于激活中间特征。同样定义了shortcut其构建逻辑和BasicBlock中类似根据输入输出通道数和步长情况来决定是否需要构建包含1×1卷积和BatchNorm2d层的调整结构以保证残差连接的维度匹配。 def forward(self, x):out self.conv1(x)out self.bn1(out)out self.relu(out)out self.conv2(out)out self.bn2(out)out self.relu(out)out self.conv3(out)out self.bn3(out)out self.shortcut(x)out self.relu(out)return outBottleneck块的前向传播方法 前向传播过程与BasicBlock类似只是中间经过了三个卷积层及对应的归一化和激活操作最后同样是将主分支特征与shortcut分支特征相加后再经过ReLU激活函数输出实现残差学习。
def ResNet50(num_classes):return ResNet(Bottleneck, [3, 4, 6, 3], num_classes)def ResNet101(num_classes):return ResNet(Bottleneck, [3, 4, 23, 3], num_classes)def ResNet152(num_classes):return ResNet(Bottleneck, [3, 8, 36, 3], num_classes)这几个函数分别用于创建ResNet-50、ResNet-101和ResNet-152网络模型它们与创建ResNet-18、ResNet-34的函数类似只是传入的构建块类型变为Bottleneck以及对应不同层数的重复Bottleneck块数量列表还有指定的类别数num_classes最终返回相应深度的ResNet模型实例用于更复杂的图像分类等任务这些更深层的网络结构在处理大规模图像数据集时往往能取得更好的性能表现。
训练过程和测试结果
训练过程损失函数变化曲线
训练过程准确率变化曲线 测试结果
代码汇总
项目github地址 项目结构
|--data
|--models|--__init__.py|-resnet.py|--...
|--results
|--weights
|--train.py
|--test.pyresnet.py
import torch
import torch.nn as nnclass BasicBlock(nn.Module):expansion 1def __init__(self, in_channels, out_channels, stride1):super(BasicBlock, self).__init__()self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse)self.bn1 nn.BatchNorm2d(out_channels)self.relu nn.ReLU(inplaceTrue)self.conv2 nn.Conv2d(out_channels, out_channels * BasicBlock.expansion, kernel_size3, padding1, biasFalse)self.bn2 nn.BatchNorm2d(out_channels * BasicBlock.expansion)self.shortcut nn.Sequential()if stride ! 1 or in_channels ! out_channels * BasicBlock.expansion:self.shortcut nn.Sequential(nn.Conv2d(in_channels, out_channels * BasicBlock.expansion, kernel_size1, stridestride, biasFalse),nn.BatchNorm2d(out_channels * BasicBlock.expansion))def forward(self, x):out self.conv1(x)out self.bn1(out)out self.relu(out)out self.conv2(out)out self.bn2(out)out self.shortcut(x)out self.relu(out)return outclass ResNet(nn.Module):def __init__(self, block, num_blocks, num_classes):super(ResNet, self).__init__()self.in_channels 64self.conv1 nn.Conv2d(3, 64, kernel_size3, padding1, biasFalse)self.bn1 nn.BatchNorm2d(64)self.relu nn.ReLU(inplaceTrue)self.maxpool nn.MaxPool2d(kernel_size3, stride1, padding1)self.layer1 self._make_layer(block, 64, num_blocks[0], 1)self.layer2 self._make_layer(block, 128, num_blocks[1], 2)self.layer3 self._make_layer(block, 256, num_blocks[2], 2)self.layer4 self._make_layer(block, 512, num_blocks[3], 2)self.avgpool nn.AdaptiveAvgPool2d((1, 1))self.fc nn.Linear(512 * block.expansion, num_classes)def _make_layer(self, block, out_channels, num_blocks, stride1):strides [stride] [1] * (num_blocks - 1)layers []for stride in strides:layers.append(block(self.in_channels, out_channels, stride))self.in_channels out_channels * block.expansionreturn nn.Sequential(*layers)def forward(self, x):out self.conv1(x)out self.bn1(out)out self.relu(out)out self.maxpool(out)out self.layer1(out)out self.layer2(out)out self.layer3(out)out self.layer4(out)out self.avgpool(out)out out.view(out.size(0), -1)out self.fc(out)return out# ResNet18, ResNet34
def ResNet18(num_classes):return ResNet(BasicBlock, [2, 2, 2, 2], num_classes)def ResNet34(num_classes):return ResNet(BasicBlock, [3, 4, 6, 3], num_classes)# ResNet50, ResNet101, ResNet152 需要 BottleNeck
class Bottleneck(nn.Module):expansion 4def __init__(self, in_channels, out_channels, stride1):super(Bottleneck, self).__init__()self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size1, biasFalse)self.bn1 nn.BatchNorm2d(out_channels)self.conv2 nn.Conv2d(out_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse)self.bn2 nn.BatchNorm2d(out_channels)self.conv3 nn.Conv2d(out_channels, out_channels * self.expansion, kernel_size1, biasFalse)self.bn3 nn.BatchNorm2d(out_channels * self.expansion)self.relu nn.ReLU(inplaceTrue)self.shortcut nn.Sequential()if stride ! 1 or in_channels ! out_channels * self.expansion:self.shortcut nn.Sequential(nn.Conv2d(in_channels, out_channels * self.expansion, kernel_size1, stridestride, biasFalse),nn.BatchNorm2d(out_channels * self.expansion))def forward(self, x):out self.conv1(x)out self.bn1(out)out self.relu(out)out self.conv2(out)out self.bn2(out)out self.relu(out)out self.conv3(out)out self.bn3(out)out self.shortcut(x)out self.relu(out)return outdef ResNet50(num_classes):return ResNet(Bottleneck, [3, 4, 6, 3], num_classes)def ResNet101(num_classes):return ResNet(Bottleneck, [3, 4, 23, 3], num_classes)def ResNet152(num_classes):return ResNet(Bottleneck, [3, 8, 36, 3], num_classes)train.py
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from models import *
import matplotlib.pyplot as pltimport ssl
ssl._create_default_https_context ssl._create_unverified_context# 定义数据预处理操作
transform transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.491, 0.482, 0.446), (0.247, 0.243, 0.261))])# 加载CIFAR10训练集
trainset torchvision.datasets.CIFAR10(root./data, trainTrue,downloadFalse, transformtransform)
trainloader torch.utils.data.DataLoader(trainset, batch_size128,shuffleTrue, num_workers2)# 定义设备GPU优先若可用
device torch.device(cuda if torch.cuda.is_available() else cpu)# 实例化模型
model_name ResNet18
if model_name AlexNet:model AlexNet(num_classes10).to(device)
elif model_name Vgg_A:model Vgg(cfg_vggA, num_classes10).to(device)
elif model_name Vgg_A-LRN:model Vgg(cfg_vggA-LRN, num_classes10).to(device)
elif model_name Vgg_B:model Vgg(cfg_vggB, num_classes10).to(device)
elif model_name Vgg_C:model Vgg(cfg_vggC, num_classes10).to(device)
elif model_name Vgg_D:model Vgg(cfg_vggD, num_classes10).to(device)
elif model_name Vgg_E:model Vgg(cfg_vggE, num_classes10).to(device)
elif model_name GoogleNet:model GoogleNet(num_classes10).to(device)
elif model_name ResNet18:model ResNet18(num_classes10).to(device)
elif model_name ResNet34:model ResNet34(num_classes10).to(device)
elif model_name ResNet50:model ResNet50(num_classes10).to(device)
elif model_name ResNet101:model ResNet101(num_classes10).to(device)
elif model_name ResNet152:model ResNet152(num_classes10).to(device)criterion nn.CrossEntropyLoss()
optimizer optim.Adam(model.parameters(), lr0.001)# 训练轮次
epochs 15def train(model, trainloader, criterion, optimizer, device):model.train()running_loss 0.0correct 0total 0for i, data in enumerate(trainloader, 0):inputs, labels data[0].to(device), data[1].to(device)optimizer.zero_grad()outputs model(inputs)loss criterion(outputs, labels)loss.backward()optimizer.step()running_loss loss.item()_, predicted outputs.max(1)total labels.size(0)correct predicted.eq(labels).sum().item()epoch_loss running_loss / len(trainloader)epoch_acc 100. * correct / totalreturn epoch_loss, epoch_accif __name__ __main__:loss_history, acc_history [], []for epoch in range(epochs):train_loss, train_acc train(model, trainloader, criterion, optimizer, device)print(fEpoch {epoch 1}: Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%)loss_history.append(train_loss)acc_history.append(train_acc)# 保存模型权重每5轮次保存到weights文件夹下if (epoch 1) % 5 0:torch.save(model.state_dict(), fweights/{model_name}_epoch_{epoch 1}.pth)# 绘制损失曲线plt.plot(range(1, epochs1), loss_history, labelLoss, markero)plt.xlabel(Epoch)plt.ylabel(Loss)plt.title(Training Loss Curve)plt.legend()plt.savefig(fresults\\{model_name}_train_loss_curve.png)plt.close()# 绘制准确率曲线plt.plot(range(1, epochs1), acc_history, labelAccuracy, markero)plt.xlabel(Epoch)plt.ylabel(Accuracy (%))plt.title(Training Accuracy Curve)plt.legend()plt.savefig(fresults\\{model_name}_train_acc_curve.png)plt.close()test.py
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
from models import *import ssl
ssl._create_default_https_context ssl._create_unverified_context
# 定义数据预处理操作
transform transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.491, 0.482, 0.446), (0.247, 0.243, 0.261))])# 加载CIFAR10测试集
testset torchvision.datasets.CIFAR10(root./data, trainFalse,downloadFalse, transformtransform)
testloader torch.utils.data.DataLoader(testset, batch_size128,shuffleFalse, num_workers2)# 定义设备GPU优先若可用
device torch.device(cuda if torch.cuda.is_available() else cpu)# 实例化模型
model_name ResNet18
if model_name AlexNet:model AlexNet(num_classes10).to(device)
elif model_name Vgg_A:model Vgg(cfg_vggA, num_classes10).to(device)
elif model_name Vgg_A-LRN:model Vgg(cfg_vggA-LRN, num_classes10).to(device)
elif model_name Vgg_B:model Vgg(cfg_vggB, num_classes10).to(device)
elif model_name Vgg_C:model Vgg(cfg_vggC, num_classes10).to(device)
elif model_name Vgg_D:model Vgg(cfg_vggD, num_classes10).to(device)
elif model_name Vgg_E:model Vgg(cfg_vggE, num_classes10).to(device)
elif model_name GoogleNet:model GoogleNet(num_classes10).to(device)
elif model_name ResNet18:model ResNet18(num_classes10).to(device)
elif model_name ResNet34:model ResNet34(num_classes10).to(device)
elif model_name ResNet50:model ResNet50(num_classes10).to(device)
elif model_name ResNet101:model ResNet101(num_classes10).to(device)
elif model_name ResNet152:model ResNet152(num_classes10).to(device)criterion nn.CrossEntropyLoss()# 加载模型权重
weights_path fweights/{model_name}_epoch_15.pth
model.load_state_dict(torch.load(weights_path, map_locationdevice))def test(model, testloader, criterion, device):model.eval()running_loss 0.0correct 0total 0with torch.no_grad():for data in testloader:inputs, labels data[0].to(device), data[1].to(device)outputs model(inputs)loss criterion(outputs, labels)running_loss loss.item()_, predicted outputs.max(1)total labels.size(0)correct predicted.eq(labels).sum().item()epoch_loss running_loss / len(testloader)epoch_acc 100. * correct / totalreturn epoch_loss, epoch_accif __name__ __main__:test_loss, test_acc test(model, testloader, criterion, device)print(f{model_name} Test)print(fLoad Model Weights From: {weights_path})print(fTest Loss: {test_loss:.4f}, Test Acc: {test_acc:.2f}%)