Pytorch进阶教学——训练一个图像分类模型(GPU)

目录

1、前言

2、数据集介绍

3、获取数据

4、创建网络

5、训练模型

6、测试模型

6.1、测试整个模型准确率

6.2、测试单张图片


1、前言

  • 编写一个可以分类蚂蚁和蜜蜂图片的模型,使用数据集对卷积神经网络进行训练。训练后的模型可以对蚂蚁或蜜蜂的图片进行检测。
  • 使用anaconda新建一个虚拟环境,安装好pytorch。后续缺什么包就安装什么包即可。
  • 使用pycharm新建一个项目,配置好环境。

2、数据集介绍

  • 使用的数据集为蚂蚁和蜜蜂 的图片,分为训练集和测试集
  • 【注】数据集下载地址

3、获取数据

  • 代码中获取数据集使用的是txt文件,所以首先需要提取全部图片的地址和标签放入txt文件中。

  • 下述代码为python提取全部图片地址和标签导出为txt文件的脚本。(自行修改)
    *

    python 复制代码
    import os  # 导入os模块,用于操作文件路径等操作系统相关功能。
    
    
    def get_file_name(file_path, output_file, type):  # 绝对路径
        path_list = os.listdir(file_path)  # 列出指定路径下的所有文件和文件夹,并将结果存储在path_list中
        with open(output_file, 'a') as file:
            for filename in path_list:
                all_file_path = os.path.join(file_path, filename)  # 拼接路径
                file.write(all_file_path + ' ' + type + '\n')
    
    
    if __name__ == '__main__':
        ants_file_path = r"D:\BaiduNetdiskWorkspace\PyTorch\image_recognition\hymenoptera_data\train\ants"
        bees_file_path = r"D:\BaiduNetdiskWorkspace\PyTorch\image_recognition\hymenoptera_data\train\bees"
        output_file = r"D:\BaiduNetdiskWorkspace\PyTorch\image_recognition\hymenoptera_data\train.txt"
        get_file_name(ants_file_path, output_file, 'ants')
        get_file_name(bees_file_path, output_file, 'bees')
  • 将全部地址修改为相对地址。

    • 使用替换操作实现。例如:
  • 最后txt文件的内容如下:

  • 新建一个dataset.py文件。
    *

    python 复制代码
    # 读取数据
    import torch
    import torchvision.transforms as transforms
    from PIL import Image
    
    
    # 读取数据类
    class MyDataset(torch.utils.data.Dataset):  # 继承构建自定义数据集的基类
        def __init__(self, datatxt, datatransform):
            datas = open(datatxt, 'r').readlines()  # 按行读取,每行包含图像路径和标签
            self.images = []
            self.labels = []
            self.transform = datatransform
            for data in datas:
                item = data.strip().split(' ')  # 去除首尾空格并按空格分割
                # 分别将图像路径和标签添加到self.images和self.labels列表中
                self.images.append(item[0])  # 路径
                self.labels.append(item[1])  # 标签
            return
    
        def __len__(self):
            return len(self.images)
    
        # 获取数据集中的一个样本。接收一个索引item,根据索引获取对应的图像路径和标签
        def __getitem__(self, item):
            imagepath, label = self.images[item], self.labels[item]
            image = Image.open(imagepath)  # 打开图片
            return self.transform(image), label  # 返回转换后的图像和对应的标签
    
    
    # 用于测试
    if __name__ == '__main__':
        # 利用txt文件读取图片信息,txt文件包括图片路径和标签
        traintxt = './hymenoptera_data/train.txt'
        valtxt = './hymenoptera_data/val.txt'
        # 图片转换形式
        traindata_transfomer = transforms.Compose([
            transforms.ToTensor(),  # 转为Tensor格式
            transforms.Resize(60),  # 调整图像大小,调整为高度或宽度为60像素,另一边按比例调整
            transforms.RandomCrop(48),  # 裁剪图片,随机裁剪成高度和宽度均为48像素的部分
            transforms.RandomHorizontalFlip(),  # 随机水平翻转
            transforms.RandomRotation(10),  # 随机旋转
            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 对图像进行归一化处理。对每个通道执行了均值为0.5、标准差为0.5的归一化操作
        ])
        valdata_transfomer = transforms.Compose([
            transforms.ToTensor(),  # 转为Tensor格
            transforms.Resize(48),  # 调整图像大小,调整为高度或宽度为48像素,另一边按比例调整
            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
        ])
        # 加载数据
        traindataset = MyDataset(traintxt, traindata_transfomer)
        valdataset = MyDataset(valtxt, valdata_transfomer)
        print("测试集:" + str(traindataset.__len__()))
        print("训练集:" + str(valdataset.__len__()))
  • 单独运行结果:(只用于测试)


4、创建网络

  • 新建一个net.py文件。

    • 其中创建了一个简单的三层卷积神经网络。
    python 复制代码
    # 三层卷积神经网络
    import torch
    
    
    # 卷积神经网络类
    class SimpleConv3(torch.nn.Module):  # 继承创建神经网络的基类
        def __init__(self, classes):
            super(SimpleConv3, self).__init__()
            # 卷积层
            self.conv1 = torch.nn.Conv2d(3, 16, 3, 2, 1)  # 输入通道3,输出通道16,3*3的卷积核,步长2,边缘填充1
            self.conv2 = torch.nn.Conv2d(16, 32, 3, 2, 1)  # 输入通道16,输出通道32,3*3的卷积核,步长2,边缘填充1
            self.conv3 = torch.nn.Conv2d(32, 64, 3, 2, 1)  # 输入通道32,输出通道64,3*3的卷积核,步长2,边缘填充1
            # 全连接层
            self.fc1 = torch.nn.Linear(2304, 100)
            self.fc2 = torch.nn.Linear(100, classes)
    
        def forward(self, x):
            # 第一次卷积
            x = torch.nn.functional.relu(self.conv1(x))  # relu为激活函数
            # 第二次卷积
            x = torch.nn.functional.relu(self.conv2(x))
            # 第三次卷积
            x = torch.nn.functional.relu(self.conv3(x))
            # 展开成一维向量
            x = x.view(x.size(0), -1)
            x = torch.nn.functional.relu(self.fc1(x))
            x = self.fc2(x)
            return x
    
    
    # 用于测试
    if __name__ == '__main__':
        inputs = torch.rand((1, 3, 48, 48))  # 生成一个随机的3通道、48x48大小的张量作为输入
        net = SimpleConv3(2)  # 二分类
        output = net(inputs)
        print(output)
  • 单独运行结果:(只用于测试)


5、训练模型

  • 新建一个train.py文件。

    • 其中可自行设置的参数都有标出。
    python 复制代码
    # 训练模型
    import matplotlib
    
    matplotlib.use('TkAgg')
    import matplotlib.pyplot as plt
    from dataset import MyDataset
    from net import SimpleConv3
    import torch
    import torchvision.transforms as transforms
    from torch.optim import SGD  # 优化相关
    from torch.optim.lr_scheduler import StepLR  # 优化相关
    from sklearn import preprocessing  # 处理label
    
    # 图片转换形式
    traindata_transfomer = transforms.Compose([
        transforms.ToTensor(),  # 转为Tensor格式
        transforms.Resize(60, antialias=True),  # 调整图像大小,调整为高度或宽度为60像素,另一边按比例调整,antialias=True启用了抗锯齿功能
        transforms.RandomCrop(48),  # 裁剪图片,随机裁剪成高度和宽度均为48像素的部分
        transforms.RandomHorizontalFlip(),  # 随机水平翻转
        transforms.RandomRotation(10),  # 随机旋转
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 对图像进行归一化处理。对每个通道执行了均值为0.5、标准差为0.5的归一化操作
    ])
    
    if __name__ == '__main__':
        traintxt = './hymenoptera_data/train.txt'
        valtxt = './hymenoptera_data/val.txt'
    
        # 加载数据
        traindataset = MyDataset(traintxt, traindata_transfomer)
    
        # 创建卷积神经网络
        net = SimpleConv3(2)  # 二分类
        # 使用GPU
        device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
        net.to(device)
        # 测试GPU是否能使用
        # print("The device is gpu later?:", next(net.parameters()).is_cuda)
        # print("The device is gpu,", next(net.parameters()).device)
    
        # 将数据提供给模型使用
        traindataloader = torch.utils.data.DataLoader(traindataset, batch_size=128, shuffle=True,
                                                      num_workers=1)  # batch_size可以自行调节
        # 优化器
        optim = SGD(net.parameters(), lr=0.1, momentum=0.9)  # 使用随机梯度下降(SGD)作为优化器,学习率0.1,动量0.9,加速梯度下降过程,lr可自行调节
        criterion = torch.nn.CrossEntropyLoss()  # 使用交叉熵损失作为损失函数
        lr_step = StepLR(optim, step_size=200, gamma=0.1)  # 学习率调度器,动态调整学习率,每200个epoch调整一次,每次调整缩小为原来的0.1倍,step_size可自行调节
        epochs = 5  # 训练次数
        accs = []
        losss = []
        # 训练循环
        for epoch in range(0, epochs):
            batch = 0
            running_acc = 0.0  # 精度
            running_loss = 0.0  # 损失
            for data in traindataloader:
                batch += 1
                imputs, labels = data
                # 将标签从元组转换为tensor类型
                labels = preprocessing.LabelEncoder().fit_transform(labels)
                labels = torch.as_tensor(labels)
                # 利用GPU训练模型
                imputs = imputs.to(device)
                labels = labels.to(device)
                # 将数据输入至网络
                output = net(imputs)
                # 计算损失
                loss = criterion(output, labels)
                # 平均准确率
                acc = float(torch.sum(labels == torch.argmax(output, 1))) / len(imputs)
                # 累加损失和准确率,后面会除以batch
                running_acc += acc
                running_loss += loss.data.item()
    
                optim.zero_grad()  # 清空梯度
                loss.backward()  # 反向传播
                optim.step()  # 更新参数
    
            lr_step.step()  # 更新优化器的学习率
            # 一次训练的精度和损失
            running_acc = running_acc / batch
            running_loss = running_loss / batch
            accs.append(running_acc)
            losss.append(running_loss)
            print('epoch=' + str(epoch) + ' loss=' + str(running_loss) + ' acc=' + str(running_acc))
    
        # 保存模型
        torch.save(net, 'model.pth')  # 保存模型的权重和结构
        x = torch.randn(1, 3, 48, 48).to(device)  # # 生成一个随机的3通道、48x48大小的张量作为输入,新建的张量也要送到GPU中
        net = torch.load('model.pth')  # 从保存的.pth文件中加载模型
        net.train(False)  # 设置模型为推理模式,意味着不会进行梯度计算或反向传播
        torch.onnx.export(net, x, 'model.onnx')  # 使用ONNX格式导出模型
        # 接受模型net、示例输入x和导出的文件名model.onnx作为参数
    
        # 可视化结果
        fig = plt.figure()
        plot1, = plt.plot(range(len(accs)), accs)  # 创建一个图形对象plot1,绘制accs列表中的数据
        plot2, = plt.plot(range(len(losss)), losss)  # 创建另一个图形对象plot2,绘制losss列表中的数据
        plt.ylabel('epoch')  # 设置y轴的标签为epoch
        plt.legend(handles=[plot1, plot2], labels=['acc', 'loss'])  # 创建图例,指定图表中不同曲线的标签
        plt.show()  # 展示所绘制的图表
  • 【注】本项目使用的是GPU训练模型。如果GPU可以获得,但是无法使用,可能是pytorch的版本不对,需要重新安装。

  • 运行结果:

  • 保存后的模型如下:


6、测试模型

6.1、测试整个模型准确率

  • 利用测试集,测试整个模型的准确率。

  • 新建一个test.py文件。
    *

    python 复制代码
    # 测试整个模型的准确率
    import torch
    import torchvision.transforms as transforms
    from dataset import MyDataset  # 您的数据集类
    from sklearn import preprocessing  # 处理label
    
    # 定义测试集的数据转换形式
    valdata_transfomer = transforms.Compose([
        transforms.ToTensor(),  # 转为Tensor格式
        transforms.Resize(60, antialias=True),  # 调整图像大小,调整为高度或宽度为60像素,另一边按比例调整,antialias=True启用了抗锯齿功能
        transforms.CenterCrop(48),  # 中心裁剪图片,裁剪成高度和宽度均为48像素的部分
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 对图像进行归一化处理。对每个通道执行了均值为0.5、标准差为0.5的归一化操作
    ])
    
    if __name__ == '__main__':
        valtxt = './hymenoptera_data/val.txt'  # 测试集数据路径
    
        # 加载测试集数据
        valdataset = MyDataset(valtxt, valdata_transfomer)
    
        # 加载已训练好的模型,利用GPU进行测试
        device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
        net = torch.load('model.pth').to(device)
        net.eval()  # 将模型设置为评估模式,意味着不会进行梯度计算或反向传播
    
        # 使用 DataLoader 加载测试集数据
        valdataloader = torch.utils.data.DataLoader(valdataset, batch_size=1, shuffle=False)
    
        correct = 0  # 被正确预测的样本数
        total = 0  # 测试样本数
    
        # 测试模型
        with torch.no_grad():
            for data in valdataloader:
                images, labels = data
                # 将标签从元组转换为tensor类型
                labels = preprocessing.LabelEncoder().fit_transform(labels)
                labels = torch.as_tensor(labels)
                # 利用GPU训练模型
                images, labels = images.to(device), labels.to(device)
                outputs = net(images)  # 输入图像并获取模型预测结果
                _, predicted = torch.max(outputs.data, 1)  # 获取预测值中最大概率的索引
                total += labels.size(0)  # 累计测试样本数量
                correct += (predicted == labels).sum().item()  # 计算正确预测的样本数量
    
        # 计算并输出模型在测试集上的准确率
        accuracy = 100 * correct / total
        print('Test Accuracy: {:.2f}%'.format(accuracy))
  • 运行结果:

    • 因为训练模型时只迭代了200次,所以准确率并不高。可以尝试提高训练次数,提高准确率。

6.2、测试单张图片

  • 使用训练后的模型,对单张图片进行预测。

  • 新建一个testone.py文件。
    *

    python 复制代码
    import torch
    from PIL import Image
    import torchvision.transforms as transforms
    
    # 定义图片预处理转换
    image_transforms = transforms.Compose([
        transforms.Resize(60, antialias=True),  # 调整图像大小
        transforms.CenterCrop(48),  # 中心裁剪
        transforms.ToTensor(),  # 转为Tensor格式
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 归一化处理
    ])
    
    # 定义类别映射字典
    class_mapping = {
        0: "ant",
        1: "bee"
    }
    
    # 加载已训练好的模型,利用GPU测试
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    net = torch.load('model.pth').to(device)
    net.eval()  # 将模型设置为评估模式,意味着不会进行梯度计算或反向传播
    
    # 加载要测试的图片
    image_path = './hymenoptera_data/val/bees/26589803_5ba7000313.jpg'  # 图片路径
    input_image = Image.open(image_path)  # 加载图片
    input_tensor = image_transforms(input_image).unsqueeze(0)  # 对图片进行预处理转换,并增加 batch 维度
    
    # 将输入数据移动到GPU上
    input_tensor = input_tensor.to(device)
    
    # 使用模型进行预测
    with torch.no_grad():
        output = net(input_tensor)
        _, predicted = torch.max(output, 1)  # 在张量中沿指定维度找到最大值及其对应的索引
    
    # 输出预测结果
    predicted_class = predicted.item()  # 得到预测的标签
    predicted_label = class_mapping[predicted_class]  # 将标签转换为文字
    print(f"The predicted class for the image is: {predicted_label}")
  • 运行结果:

相关推荐
冷眼看人间恩怨8 分钟前
【话题讨论】AI大模型重塑软件开发:定义、应用、优势与挑战
人工智能·ai编程·软件开发
2401_8830410810 分钟前
新锐品牌电商代运营公司都有哪些?
大数据·人工智能
AI极客菌1 小时前
Controlnet作者新作IC-light V2:基于FLUX训练,支持处理风格化图像,细节远高于SD1.5。
人工智能·计算机视觉·ai作画·stable diffusion·aigc·flux·人工智能作画
阿_旭1 小时前
一文读懂| 自注意力与交叉注意力机制在计算机视觉中作用与基本原理
人工智能·深度学习·计算机视觉·cross-attention·self-attention
王哈哈^_^1 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
Power20246662 小时前
NLP论文速读|LongReward:基于AI反馈来提升长上下文大语言模型
人工智能·深度学习·机器学习·自然语言处理·nlp
数据猎手小k2 小时前
AIDOVECL数据集:包含超过15000张AI生成的车辆图像数据集,目的解决旨在解决眼水平分类和定位问题。
人工智能·分类·数据挖掘
好奇龙猫2 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
沉下心来学鲁班3 小时前
复现LLM:带你从零认识语言模型
人工智能·语言模型
数据猎手小k3 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型