24.8.10学习笔记

深度剖析 猫狗分类CNN代码:

import torch
from torch import nn
from catdog import MyAlexNet
import numpy as np
from torch.optim import  lr_scheduler
import os
from torchvision import  transforms
from torchvision.datasets import  ImageFolder
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False

ROOT_TRAIN=r'C:/Users/kk/PycharmProjects/pythonProject2/dataset/train/catdog1/data/train'
# 定义训练数据的根目录路径
ROOT_TEST=r'C:/Users/kk/PycharmProjects/pythonProject2/dataset/train/catdog1/data/val'
# 定义测试数据的根目录路径

normalize=transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
# 创建一个数据归一化的操作,对每个通道的均值为[0.5, 0.5, 0.5],标准差为[0.5, 0.5, 0.5]

train_transform=transforms.Compose([
    transforms.Resize((224,224)),
    # 对训练数据进行尺寸调整,将图像大小调整为 224x224
    transforms.RandomVerticalFlip(),
    # 对训练数据进行随机垂直翻转操作,增加数据的多样性
    transforms.ToTensor(),
    # 将图像转换为张量(Tensor)格式
    normalize
    # 应用前面定义的归一化操作
])

val_transform=transforms.Compose([
    transforms.Resize((224,224)),
    # 对验证数据进行尺寸调整,将图像大小调整为 224x224
    transforms.ToTensor(),
    # 将图像转换为张量(Tensor)格式
    normalize
    # 应用前面定义的归一化操作
])
train_dataset = ImageFolder(ROOT_TRAIN, transform=train_transform)
# 使用指定的根目录(ROOT_TRAIN)和训练数据的转换操作(train_transform)创建一个训练数据集对象

val_dataset = ImageFolder(ROOT_TEST, transform=val_transform)
# 使用指定的根目录(ROOT_TEST)和验证数据的转换操作(val_transform)创建一个验证数据集对象

train_dataloader = DataLoader(train_dataset, batch_size=32, shuffle=True)
# 为训练数据集创建一个数据加载器,批量大小为 32,并且在每个 epoch 中打乱数据顺序

val_dataloader = DataLoader(val_dataset, batch_size=32, shuffle=True)
# 为验证数据集创建一个数据加载器,批量大小为 32,并且在每个 epoch 中打乱数据顺序

device = 'cuda'
# 指定计算设备为 CUDA(如果可用)

model = MyAlexNet().to(device)
# 创建自定义的 MyAlexNet 模型,并将其移动到指定的设备(CUDA)

loss_fn = nn.CrossEntropyLoss()
# 定义交叉熵损失函数

optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# 使用随机梯度下降(SGD)优化器来优化模型的参数,学习率为 0.01,动量为 0.9

lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)
# 创建一个学习率调整策略,每 10 个 epoch 学习率乘以 0.5

这段代码是构建了一个用于图像分类任务的深度学习训练流程,主要分为以下几个步骤:

  1. 设置数据路径:定义了训练和测试数据的根目录路径,这些路径指向存放图像的文件夹。

  2. 定义归一化操作:创建了一个transforms.Normalize对象,用于将图像数据的每个通道进行归一化处理,使其均值变为0.5,标准差也为0.5。

  3. 定义训练和验证数据的转换操作:使用transforms.Compose组合了多个图像预处理步骤,包括调整图像大小、随机垂直翻转(仅训练数据)、转换为张量以及应用归一化。

  4. 创建数据集对象:利用定义好的根目录和转换操作,创建了训练和验证的数据集对象。

  5. 创建数据加载器:使用DataLoader为训练和验证数据集创建了数据加载器,设置了批量大小为32,并在每个epoch中打乱数据顺序,以提高模型训练的泛化能力。

  6. 指定计算设备:将计算设备设置为CUDA,意味着如果可用,训练过程将在GPU上进行。

  7. 创建模型:实例化了一个自定义的MyAlexNet模型,并将其移动到指定的CUDA设备上。

  8. 定义损失函数:选择了交叉熵损失函数nn.CrossEntropyLoss作为模型训练的损失函数。

  9. 设置优化器:使用随机梯度下降(SGD)作为优化算法,设置了学习率和动量参数:

    model.parameters() 的作用是告诉优化器 torch.optim.SGD 哪些参数应该被更新。

  10. 设置学习率调整策略:创建了一个学习率调整器,每10个epoch将学习率乘以0.5,这是一种常见的学习率衰减策略,用于在训练过程中逐渐减小学习率,以帮助模型收敛。

简而言之,这段代码完成了一个深度学习模型训练流程的准备工作,包括数据预处理、数据加载、模型定义、损失函数选择、优化器设置以及学习率调整策略的配置。接下来,就可以使用这些设置来训练模型,并在训练过程中监控其性能。

def train(dataloader, model, loss_fn, optimizer):
    # 初始化损失和准确率累加器
    loss, current, n = 0.0, 0.0, 0
    # 遍历数据加载器中的批次
    for batch, (x, y) in enumerate(dataloader):
        # 将图像和标签移动到指定的设备(如GPU)
        image, y = x.to(device), y.to(device)
        # 模型前向传播得到输出
        output = model(image)
        # 计算当前批次的损失
        cur_loss = loss_fn(output, y)
        # 获取预测结果(通过找到输出的最大值索引)
        _, pred = torch.max(output, axis=1)
        # 计算当前批次的准确率
        cur_acc = torch.sum(y == pred) / output.shape[0]

        # 清除之前的梯度
        optimizer.zero_grad()
        # 反向传播计算梯度
        cur_loss.backward()
        # 更新模型参数
        optimizer.step()
        # 累加损失和准确率
        loss += cur_loss.item()
        current += cur_acc.item()
        n = n + 1

    # 计算整个epoch的平均损失和准确率
    train_loss = loss / n
    train_acc = current / n
    # 打印训练损失和准确率
    print('train_loss ' + str(train_loss))
    print('train_acc ' + str(train_acc))
    # 返回训练损失和准确率
    return train_loss, train_acc


def val(dataloader, model, loss_fn):
    # 初始化用于累加的变量,分别用于存储损失和准确率
    loss, current, n = 0.0, 0.0, 0

    # 遍历数据加载器(dataloader)中的批次
    for batch, (x, y) in enumerate(dataloader):
        # 将批次中的图像(x)和标签(y)移动到指定的设备,例如GPU
        image, y = x.to(device), y.to(device)

        # 调用模型进行前向传播,计算给定图像的输出
        output = model(image)

        # 计算当前批次的损失,使用损失函数(loss_fn)对模型的输出和真实标签进行评估
        cur_loss = loss_fn(output, y)

        # 从模型输出中获取预测结果,通过找到概率最高的类别索引
        _, pred = torch.max(output, axis=1)

        # 计算当前批次的准确率,通过比较预测结果(pred)和真实标签(y)是否一致
        cur_acc = torch.sum(y == pred) / output.shape[0]

        # 累加批次数量,用于在最后计算平均值
        n = n + 1

        # 累加当前批次的损失和准确率到总损失和总准确率
        loss += cur_loss.item()  # 使用.item()将张量转换为Python数值
        current += cur_acc

    # 计算整个验证过程(epoch)的平均损失和准确率
    val_loss = loss / n
    val_acc = current / n

    # 打印出训练过程中的损失和准确率,注意这里的'train_loss'和'train_acc'可能是'val_loss'和'val_acc'的误写
    print('val_loss ' + str(val_loss))
    print('val_acc ' + str(val_acc))

    # 函数返回计算得到的平均损失和准确率
    return val_loss, val_acc

训练函数 train

  1. 初始化统计变量:

    • 初始化累加器 losscurrent 和 n 分别用于累加损失、准确率和批次数量。
  2. 遍历数据集:

    • 使用 for 循环遍历数据加载器中的每个批次。
    • 每个批次包含输入图像 x 和对应的标签 y
  3. 数据准备:

    • 将输入图像 x 和标签 y 移动到指定的设备(例如 GPU)。
  4. 前向传播:

    • 使用模型 model 对输入图像进行前向传播,得到输出 output
  5. 计算损失:

    • 使用损失函数 loss_fn 计算模型输出 output 与真实标签 y 之间的损失 cur_loss
  6. 获取预测结果:

    • 通过找到输出 output 中每行的最大值索引,得到预测结果 pred
  7. 计算准确率:

    • 比较预测结果 pred 和真实标签 y,计算预测正确的样本数,并计算当前批次的准确率 cur_acc
  8. 反向传播和优化:

    • 清除之前的梯度。
    • 使用当前批次的损失 cur_loss 进行反向传播。
    • 使用优化器 optimizer 更新模型参数。
  9. 累加统计量:

    • 累加当前批次的损失和准确率。
    • 增加批次计数。
  10. 计算平均值:

    • 计算整个 epoch 的平均损失 train_loss 和平均准确率 train_acc
  11. 输出和返回:

    • 打印训练损失和准确率。
    • 返回训练损失和准确率。

val 函数中,我们没有进行梯度清零和参数更新,因为验证过程的目的是评估模型性能,而不是进行训练。

后续代码:

def matplot_loss(train_loss,val_loss):
    plt.plot(train_loss,label='train_loss')
    plt.plot(val_loss,label='val_loss')
    plt.legend(loc='best')
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.title('训练集和验证集的loss值对比图')

def matplot_acc(train_acc,val_acc):
    plt.plot(train_acc,label='train_acc')
    plt.plot(val_acc,label='val_acc')
    plt.legend(loc='best')
    plt.ylabel('acc')
    plt.xlabel('epoch')
    plt.title('训练集和验证集的acc值对比图')


loss_train=[]
acc_train=[]
loss_val=[]
acc_val=[]
# loss_train 和 acc_train 用于存储每个 epoch 的训练损失和准确率。
# loss_val 和 acc_val 用于存储每个 epoch 的验证损失和准确率。

epoch=20
min_acc=0
for t in range(epoch):
    lr_scheduler.step() # 使用学习率调度器 lr_scheduler 调整优化器的学习率。基于 epoch 数自动调整学习率。
    print(f'epoch{t+1}\n---------')
    train_loss,train_acc=train(train_dataloader,model,loss_fn,optimizer) # 训练,得到loss和acc
    val_loss,val_acc=val(val_dataloader,model,loss_fn) # 验证,得到loss和acc

    loss_train.append(train_loss) # 全部存入初始化的空列表,一个epoch存一个
    acc_train.append(train_acc)
    loss_val.append(val_loss)
    acc_val.append(val_acc)


    if val_acc>min_acc:
        # 如果后续轮次中有验证准确率低于前面的,则取前面的为最好模型。
        folder='save_model'
        if not os.path.exists(folder):
            os.mkdir('save_model')
        min_acc=val_acc
        print(f'save best model,第{t+1}轮') # 打印最好模型的轮次
        torch.save(model.state_dict(),'save_model/best_model.pth')

    if t==epoch-1:
        torch.save(model.state_dict(), 'save_model/last_model.pth')

print(f'done')
  1. 定义绘图函数:首先定义了两个函数 matplot_lossmatplot_acc,它们使用Matplotlib库来绘制训练和验证过程中损失值和准确率的变化趋势图。

  2. 初始化存储结构:创建了四个列表 loss_trainacc_trainloss_valacc_val,用于存储每个epoch的训练损失、训练准确率、验证损失和验证准确率。

  3. 设置训练参数:设置了训练的总轮数(epoch)为20,并初始化了 min_acc 变量来记录最佳验证准确率。

  4. 训练和验证循环:通过一个for循环进行多次训练和验证过程:

    • 每次开始前,调用 lr_scheduler.step() 来更新学习率。
    • 打印当前epoch的编号。
    • 调用 train 函数进行模型训练,获取当前epoch的训练损失和准确率,并添加到相应的列表中。
    • 调用 val 函数进行模型验证,获取当前epoch的验证损失和准确率,并同样添加到列表中。
  5. 模型保存逻辑:在每个epoch结束后,如果当前的验证准确率 val_acc 高于之前记录的 min_acc,则认为当前模型是最佳模型,并执行以下操作:

    • 创建一个名为 save_model 的文件夹(如果它不存在)。
    • 更新 min_acc 为当前的 val_acc
    • 打印保存最佳模型的信息,并保存当前模型的参数到文件 save_model/best_model.pth
  6. 保存最后一轮模型:无论模型性能如何,训练的最后一轮结束后,都会将模型参数保存到 save_model/last_model.pth 文件中。

  7. 训练结束:当所有epoch完成后,打印 "done" 表示训练过程全部结束。

总的来说,这段代码实现了深度学习模型的标准训练流程,包括学习率调整、训练与验证、性能监控、最佳模型保存以及最终模型的保存。通过这种方式,可以确保在训练过程中跟踪模型的性能,并保存下表现最佳的模型参数。9

你可能感兴趣的:(学习,笔记)