深度学习入门代码详细注释-ResNet18分类蚂蚁蜜蜂

本项目将基于PyTorch平台迁移ResNet18模型。该模型原采用ImageNet数据集(含1000个图像类别)进行训练。我们将尝试运用该模型对蚂蚁和蜜蜂进行分类(这两个类别未包含在原训练数据集中)。

本文的原始代码参考于博客深度学习入门项目------附代码(持续更新) - 知乎,但是这位博主只给出了代码,而没有对代码进行一些必要的注释,这对刚入门的菜鸟新手来说不太友好,所以在这里,我对该代码做了一些详细的注释,希望能够帮助到和我一样新入门的菜鸟。也同时感谢原作者对代码整理所付出的劳动!

python 复制代码
#加载所需要的库
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torch.optim.lr_scheduler import StepLR

import torchvision
from torchvision import datasets
from torchvision import models
from torchvision import transforms

import numpy as np

from io import BytesIO
from urllib.request import urlopen
from zipfile import ZipFile

import matplotlib.pyplot as plt

#调用urllib.request.urlopen从下载网址https://pytorch.tips/bee-zip中下载数据,并生成对象#zipresp;
#用IO流来进行操作,无需将zip文件下载到本地磁盘上
zipurl='https://pytorch.tips/bee-zip'
with urlopen(zipurl) as zipresp:
    with ZipFile(BytesIO(zipresp.read())) as zfile:
                 zfile.extractall('./data')


#定义训练集的变换
train_transforms = transforms.Compose([       #transforms.Compose 是一个工具,用于将多个预
#处理操作组合成一个完整的流程。它接受一个列表,列表中的每个元素是一个预处理操作。
    # 随机裁剪并调整大小到 224x224,比如原图像大小为512x512,是一个随机操作,每次裁剪的区域可
#能不同,裁剪出224x224
    transforms.RandomResizedCrop(224),         # 用于训练集,增加数据的多样性,帮助模型学习
#到更多的特征。
    # 随机水平翻转,概率为 0.5
    transforms.RandomHorizontalFlip(),     #它通过0.5的概率随机水平翻转图像,增加了数据的多样性,有助于提高模型的泛化能力和减少过拟合。一张猫的图像无论向左看还是向右看,都是#猫通过随机水平翻转,模型可以学习到更多样的图像特征。
    # 将图片转换为张量
    transforms.ToTensor(),
    # 标准化处理,使用预定义的均值和标准差
    transforms.Normalize(
        mean=[0.485, 0.456, 0.406],  # ImageNet  RGB三个通道的均值
        std=[0.229, 0.224, 0.225]        # ImageNet  RGB三个通道的标准差
    )])
#定义验证集的变换
val_transforms = transforms.Compose([
    # 将图片调整为 256x256
    transforms.Resize(256),               #是一个确定性操作,每次调整大小的结果是相同的。这
#确保了验证集的图像在每次运行时都保持一致,从而保证验证结果的稳定性和可重复性。
   # 从中心裁剪出 224x224 的区域
    transforms.CenterCrop(224),    #这种组合操作确保了验证集的图像在每次运行时都保持一致,同
#时也能保证输入到模型的图像大小一致。
   # 将图片转换为张量
    transforms.ToTensor(),    #将图像从 PIL 图像或 NumPy 数组转换为 PyTorch 张量。转换后的张
#量形状为 (C, H, W),其中 C 是通道数,H 是高度,W 是宽度。像素值会被归一化到 [0, 1] 范围。
   # 标准化处理,使用预定义的均值和标准差
    transforms.Normalize(
        mean=[0.485, 0.456, 0.406],    # ImageNet RGB三个通道的均值
        std=[0.229, 0.224, 0.225]         # ImageNet RGB三个通道的标准差
    )])
#加载数据
#数据集
train_dataset = datasets.ImageFolder(
    root = './data/hymenoptera_data/train',
    transform = train_transforms)

val_dataset = datasets.ImageFolder(
    root = './data/hymenoptera_data/val',
    transform = val_transforms)

#数据加载器
train_loader = DataLoader(
    train_dataset,
    batch_size=4,     #每个批次加载 4 个样本。这意味着每次迭代会返回 4 ##个样本及其对应的标签
    shuffle=True,    #每个 epoch 开始时随机打乱数据。虽然验证集通常不需要打乱,但在某些情况下#打乱数据可以避免验证结果的偏差。
    num_workers=4                    #如果只用一个进程加载数据,GPU每次只能处理一张图像,处
#理完一张后再处理下一张。而使用 4 个子进程时,可以同时处理 4 张图像,这样可以显著减少数据加#载的#总时间。
)
val_loader = DataLoader(
    val_dataset,
    batch_size=4,
    shuffle=True,
    num_workers=4
)


#生成ResNet18模型
#模型
model = models.resnet18(pretrained = True)#这是 PyTorch 提供的预定义 ResNet18 模型。pretrained=True:表示加载预训练的权重。这些权重是在 ImageNet 数据集上训练得到的,通常 #用于迁#移学习。
print(model.fc)                                                     #fc 是 ResNet18 模型中#的最后一个全连接层。默认情况下,ResNet18 的全连接层有 1000 个输出节点,对应于 ImageNet 数据
#集的 1000 个类别。
model.fc = nn.Linear(model.fc.in_features, 2) #model.fc.in_features:获取原全连接层的输入特#征数量。ResNet18 的全连接层输入特征数量为 512。nn.Linear(model.fc.in_features, 2):创建一
#个新的全连接层,输入特征数量保持不变,输出特征数量改为 2。这通常用于二分类任务。
print(model.fc)

#定义超参数
model = model.to("cuda")#将模型的所有参数和缓冲区移动到 GPU 上。这使得模型可以在 GPU 上进行训#练,从而显著提高训练速度。

Loss = nn.CrossEntropyLoss()#这是 PyTorch 提供的交叉熵损失函数,通常用于多分类任务。它结合了 LogSoftmax 和 NLLLoss,适用于分类任务。

optim = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)# optim.SGD:这是随机梯度下降(SGD)优化器。model.parameters():获取模型的所有可训练参
 #数。model.parameters():lr=0.001:设置学习率为 0.001。momentum=0.9:设置动量为 0.9,动量可#以帮助优化器更快地收敛,并减少振荡。

exp_lr_scheduler = StepLR(optim, step_size=7, gamma=0.1)#StepLR:这是 PyTorch 提供的学习率#调度器,用于在训练过程中调整学习率。step_size=7:每 7 个 epoch,学习率会调整一次。
#gamma=0.1:每次调整学习率时,学习率会乘以 0.1。例如,如果初始学习率为 0.001,那么在第 7 个 #epoch 时,学习率会变为 0.0001。


#可微调
#训练
num_epochs = 25 #定义了训练的总轮数,即模型将完整地遍历训练数据集的次数。在这里,设置为 25轮。
for epoch in range(num_epochs):
    #训练模型
    model.train()                                   #将模型设置为训练模式。这会影响某些层的
#行为,如 Dropout 和 BatchNorm,确保它们在训练时的行为与评估时不同。
    running_loss = 0.0                         #初始化running_loss:用于累计每个 epoch 的总#损失。
    running_corrects = 0                    #初始化running_corrects:用于累计每个 epoch 中
#正确预测的数量。
    for inputs, labels in train_loader: #train_loader:数据加载器,每次迭代返回一个批次的数
#据和标签。
        inputs = inputs.to("cuda")           #将输入数据移动到 GPU 上。
        labels = labels.to("cuda")            #将标签数据移动到 GPU 上。
        outputs = model(inputs) #将输入数据通过模型进行前向传播,得到模型的输出。
        _, preds = torch.max(outputs, 1) #获取模型输出的最大值及其索引。preds 是预测的类别索#引。
        loss = Loss(outputs, labels)          #计算模型输出和真实标签之间的损失值
        loss.backward()                            #反向传播,计算损失值的梯度,并将其传播回#模型的每个参数。
        optim.step()                                #根据计算得到的梯度更新模型的参数。
        optim.zero_grad()                       #清空之前的梯度,避免梯度累积
        
         # 累计损失和正确预测数
        running_loss += loss.item()/inputs.size(0)    #获取损失值的标量值。
        running_corrects += torch.sum(preds == labels.data)/inputs.size(0) #torch.sum(preds == labels.data):计算预测正确的数量。inputs.size(0):获取当前批次的样本数量。
    
    exp_lr_scheduler.step() #根据学习率调度器的设置更新学习率。
    train_epoch_loss = running_loss/len(train_loader) #计算每个 epoch 的平均损失。
    train_epoch_acc = running_corrects/len(train_loader)#计算每个 epoch 的平均准确率。
 #测试模型
    model.eval()
    running_loss = 0.0
    running_corrects = 0
    for inputs, labels in val_loader:
        inputs = inputs.to("cuda")
        labels = labels.to("cuda")
        
        outputs = model(inputs)
        _, preds = torch.max(outputs, 1)
        loss = Loss(outputs, labels)
       
        running_loss += loss.item()/inputs.size(0)
        running_corrects += torch.sum(preds == labels.data)/inputs.size(0)
    
    epoch_loss = running_loss/len(val_loader)
    epoch_acc = running_corrects/len(val_loader)
    
    if((epoch+1)%5==0): #每隔 5 个 epoch,输出当前的训练和验证的损失和准确率。
        print("epoch:{},  "
            "Train_loss:{:.4f} Train_acc:{:.4f},  "
            "Loss:{:.4f}, Acc:{:.4f}".format(epoch+1, train_epoch_loss, train_epoch_acc, epoch_loss, epoch_acc))


#测试可视化 这段代码定义了一个函数 imshow,用于将经过预处理的图像数据可视化,并显示其预测的类
#别。它还展示了如何使用这个函数来可视化验证集中的图像及其预测结果
def imshow(inp, title=None):
    #从 C-H-W 切换回 H-W-C 图像格式
    inp = inp.numpy().transpose((1, 2, 0)) #将输入的 PyTorch 张量转换为 NumPy 数组   从通道#优先格式(C-H-W)转换为高度-宽度-通道格式(H-W-C),以便使用 matplotlib 进行可视化。
    #撤销归一化
    mean = np.array([0.485, 0.456, 0.406]) #均值数组,用于撤销归一化。
    std = np.array([0.229, 0.224, 0.225])  #标准差数组,用于撤销归一化。
    inp = std * inp + mean  #撤销归一化操作,将图像数据恢复到原始范围。
    inp = np.clip(inp, 0, 1) #将图像数据裁剪到 [0, 1] 范围内,确保数据有效。
    plt.imshow(inp) #使用 matplotlib 的 imshow 函数显示图像。
    
    if title is not None:
        plt.title(title)

inputs , classes = next(iter(val_loader))  #从验证集加载器中获取一个批次的数据和标签。
out = torchvision.utils.make_grid(inputs)    #将一个批次的图像拼接成一个网格,便于可视化
class_names = val_dataset.classes  #val_dataset.classes:获取验证集中的类别名称列表
outputs = model(inputs.to("cuda"))           #inputs.to("cuda"):将输入图像移动到 GPU。model(inputs.to("cuda")):将输入图像通过模型进行前向传播,得到模型的输出。

_, preds = torch.max(outputs, 1)   #获取模型输出的最大值及其索引,preds 是预测的类别索引。

imshow(out, title=[class_names[x] for x in preds])  #根据预测的类别索引获取对应的类别名称
相关推荐
Swift社区3 分钟前
日志不再孤立!用 Jaeger + TraceId 实现链路级定位
人工智能·chatgpt
AI扶我青云志2 小时前
BPE(Byte Pair Encoding)分词算法
人工智能·自然语言处理
Web3_Daisy2 小时前
想要抢早期筹码?FourMeme专区批量交易教学
大数据·人工智能·区块链·比特币
东风西巷4 小时前
NealFun安卓版:创意无限,娱乐至上
android·人工智能·智能手机·娱乐·软件需求
肥猪猪爸5 小时前
BP神经网络对时序数据进行分类
人工智能·深度学习·神经网络·算法·机器学习·分类·时序数据
Liudef066 小时前
神经辐射场 (NeRF):重构三维世界的AI新视角
人工智能·重构
音视频牛哥7 小时前
打造实时AI视觉系统:OpenCV结合RTSP|RTMP播放器的工程落地方案
人工智能·opencv·计算机视觉·大牛直播sdk·rtsp播放器·rtmp播放器·android rtmp
归去_来兮8 小时前
生成式对抗网络(GAN)模型原理概述
人工智能·深度学习·生成对抗网络
在努力的韩小豪8 小时前
如何从0开始构建自己的第一个AI应用?(Prompt工程、Agent自定义、Tuning)
人工智能·python·llm·prompt·agent·ai应用·mcp