365天深度学习训练营-第P5周:运动鞋识别

  • 本文为365天深度学习训练营 中的学习记录博客
  • 参考文章:Pytorch实战 | 第P5周:运动鞋识别
  • 原作者:K同学啊|接辅导、项目定制

目录

  • 一、课题背景和开发环境
    • 开发环境
  • 二、前期准备
    • 1.设置GPU
    • 2.导入数据并划分数据集
    • 3.加载数据
  • 三、构建简单的CNN网络
  • 四、训练模型
    • 1.编写训练函数
    • 2.编写测试函数
    • 3.设置动态学习率
    • 4.正式训练&保存最优模型
  • 五、结果可视化
  • 六、加载模型&指定图片进行预测
  • 七、总结

一、课题背景和开发环境

第P5周:运动鞋识别

  • 难度:新手入门⭐
  • 语言:Python3、Pytorch

要求:

  1. 了解如何设置动态学习率(重点)
  2. 调整代码使测试集accuracy到达84%。

拔高(可选):

  1. 保存训练过程中的最佳模型权重
  2. 调整代码使测试集accuracy到达86%。

开发环境

  • 电脑系统:Windows 10
  • 语言环境:Python 3.8.2
  • 编译器:无(直接在cmd.exe内运行)
  • 深度学习环境:Pytorch
  • 显卡及显存:NVIDIA GeForce GTX 1660 Ti 12G
  • CUDA版本:Release 10.0, V10.0.130(cmd输入nvcc -Vnvcc --version指令可查看)
  • 数据:K同学啊的百度网盘

二、前期准备

1.设置GPU

如果设备上支持GPU就使用GPU,否则使用CPU

import torch
import torchvision

if __name__=='__main__':
    ''' 设置GPU '''
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print("Using {} device\n".format(device))
Using cuda device

2.导入数据并划分数据集

import os
import PIL
import random
import pathlib
import warnings
import numpy as np
import matplotlib.pyplot as plt


''' 读取本地已划分好的训练集与测试集 '''
def localDataset(data_dir):
    data_dir = pathlib.Path(data_dir)
    
    # 读取本地数据集
    data_paths = list(data_dir.glob('*'))
    
    # 关于transforms.Compose的更多介绍可以参考:https://blog.csdn.net/qq_38251616/article/details/124878863
    train_transforms = torchvision.transforms.Compose([
        torchvision.transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
        # torchvision.transforms.RandomHorizontalFlip(), # 随机水平翻转
        torchvision.transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
        torchvision.transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
            mean=[0.485, 0.456, 0.406], 
            std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
    ])
    test_transform = torchvision.transforms.Compose([
        torchvision.transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
        torchvision.transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
        torchvision.transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
            mean=[0.485, 0.456, 0.406], 
            std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
    ])
    
    train_dataset = datasets.ImageFolder(os.path.join(data_dir, 'train'),transform=train_transforms)
    test_dataset  = datasets.ImageFolder(os.path.join(data_dir, 'test'),transform=test_transform)
    classeNames = train_dataset.class_to_idx
    
    print(train_dataset, '\n')
    print(train_dataset.class_to_idx, '\n')
    
    return classeNames, train_dataset, test_dataset


root = 'data'
data_dir = root
classeNames, train_ds, test_ds = localDataset(data_dir)
Dataset ImageFolder
    Number of datapoints: 502
    Root location: data\train
    StandardTransform
Transform: Compose(
               Resize(size=[224, 224], interpolation=bilinear)
               ToTensor()
               Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
           )

{'adidas': 0, 'nike': 1}

num_classes 2
Shape of X [N, C, H, W]:  torch.Size([32, 3, 224, 224])
Shape of y:  torch.Size([32]) torch.int64

Image shape:  torch.Size([32, 3, 224, 224])

3.加载数据

''' 加载数据,并设置batch_size '''
def loadData(train_ds, test_ds, batch_size=32, root='', show_flag=False):
    # 从 train_ds 加载训练集
    train_dl = torch.utils.data.DataLoader(train_ds,
                                           batch_size=batch_size,
                                           shuffle=True,
                                           num_workers=1)
    # 从 test_ds 加载测试集
    test_dl  = torch.utils.data.DataLoader(test_ds,
                                           batch_size=batch_size,
                                           shuffle=True,
                                           num_workers=1)
    
    # 取一个批次查看数据格式
    # 数据的shape为:[batch_size, channel, height, weight]
    # 其中batch_size为自己设定,channel,height和weight分别是图片的通道数,高度和宽度。
    for X, y in test_dl:
        print('Shape of X [N, C, H, W]: ', X.shape)
        print('Shape of y: ', y.shape, y.dtype, '\n')
        break
    
    imgs, labels = next(iter(train_dl))
    print('Image shape: ', imgs.shape, '\n')
    # torch.Size([32, 3, 224, 224])  # 所有数据集中的图像都是224*224的RGB图
    displayData(imgs, root, show_flag)
    return train_dl, test_dl


''' 数据可视化 '''
def displayData(imgs, root='', flag=False):
    # 指定图片大小,图像大小为20宽、5高的绘图(单位为英寸inch)
    plt.figure('Data Visualization', figsize=(20, 5)) 
    for i, imgs in enumerate(imgs[:20]):
        # 维度顺序调整 [3, 224, 224]->[224, 224, 3]
        npimg = imgs.numpy().transpose((1, 2, 0))
        # 将整个figure分成2行10列,绘制第i+1个子图。
        plt.subplot(2, 10, i+1)
        plt.imshow(npimg)  # cmap=plt.cm.binary
        plt.axis('off')
    plt.savefig(os.path.join(root, 'DatasetDisplay.png'))
    if flag:
        plt.show()
    else:
        plt.close('all')


batch_size = 32
train_dl, test_dl = loadData(train_ds, test_ds, batch_size, root, True)
Shape of X [N, C, H, W]:  torch.Size([32, 3, 224, 224])
Shape of y:  torch.Size([32]) torch.int64

Image shape:  torch.Size([32, 3, 224, 224])

365天深度学习训练营-第P5周:运动鞋识别_第1张图片


三、构建简单的CNN网络

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.conv1=nn.Sequential(
            nn.Conv2d(3, 12, kernel_size=5, padding=0), # 12*220*220
            nn.BatchNorm2d(12),
            nn.ReLU()
        )
        self.conv2=nn.Sequential(
            nn.Conv2d(12, 12, kernel_size=5, padding=0), # 12*216*216
            nn.BatchNorm2d(12),
            nn.ReLU()
        )
        self.pool3=nn.Sequential(
            nn.MaxPool2d(2),                             # 12*108*108
            nn.Dropout(0.15)
        )
        self.conv4=nn.Sequential(
            nn.Conv2d(12, 24, kernel_size=5, padding=0), # 24*104*104
            nn.BatchNorm2d(24),
            nn.ReLU()
        )
        self.conv5=nn.Sequential(
            nn.Conv2d(24, 24, kernel_size=5, padding=0), # 24*100*100
            nn.BatchNorm2d(24),
            nn.ReLU()
        )
        self.pool6=nn.Sequential(
            nn.MaxPool2d(2),                             # 24*50*50
            nn.Dropout(0.15)
        )
        self.fc=nn.Sequential(
            nn.Linear(24*50*50, num_classes)
        )
    
    def forward(self, x):
       batch_size = x.size(0)
       x = self.conv1(x)  # 卷积-BN-激活
       x = self.conv2(x)  # 卷积-BN-激活
       x = self.pool3(x)  # 池化-Drop
       x = self.conv4(x)  # 卷积-BN-激活
       x = self.conv5(x)  # 卷积-BN-激活
       x = self.pool6(x)  # 池化-Drop
       x = x.view(batch_size, -1)  # flatten 变成全连接网络需要的输入 (batch, 24*50*50) ==> (batch, -1), -1 此处自动算出的是21168
       x = self.fc(x)
       
       return x


if __name__=='__main__':
    ''' 设置GPU '''
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print("Using {} device\n".format(device))
    
    ''' 调用并将模型转移到GPU中(我们模型运行均在GPU中进行) '''
    model = Model().to(device)
    ''' 显示网络结构 '''
    summary(model)
    print(model)
Using cuda device

=================================================================
Layer (type:depth-idx)                   Param #
=================================================================
Model                                    --
├─Sequential: 1-1                        --
│    └─Conv2d: 2-1                       912
│    └─BatchNorm2d: 2-2                  24
│    └─ReLU: 2-3                         --
├─Sequential: 1-2                        --
│    └─Conv2d: 2-4                       3,612
│    └─BatchNorm2d: 2-5                  24
│    └─ReLU: 2-6                         --
├─Sequential: 1-3                        --
│    └─MaxPool2d: 2-7                    --
│    └─Dropout: 2-8                      --
├─Sequential: 1-4                        --
│    └─Conv2d: 2-9                       7,224
│    └─BatchNorm2d: 2-10                 48
│    └─ReLU: 2-11                        --
├─Sequential: 1-5                        --
│    └─Conv2d: 2-12                      14,424
│    └─BatchNorm2d: 2-13                 48
│    └─ReLU: 2-14                        --
├─Sequential: 1-6                        --
│    └─MaxPool2d: 2-15                   --
│    └─Dropout: 2-16                     --
├─Sequential: 1-7                        --
│    └─Linear: 2-17                      120,002
=================================================================
Total params: 146,318
Trainable params: 146,318
Non-trainable params: 0
=================================================================
Model(
  (conv1): Sequential(
    (0): Conv2d(3, 12, kernel_size=(5, 5), stride=(1, 1))
    (1): BatchNorm2d(12, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
  )
  (conv2): Sequential(
    (0): Conv2d(12, 12, kernel_size=(5, 5), stride=(1, 1))
    (1): BatchNorm2d(12, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
  )
  (pool3): Sequential(
    (0): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (1): Dropout(p=0.15, inplace=False)
  )
  (conv4): Sequential(
    (0): Conv2d(12, 24, kernel_size=(5, 5), stride=(1, 1))
    (1): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
  )
  (conv5): Sequential(
    (0): Conv2d(24, 24, kernel_size=(5, 5), stride=(1, 1))
    (1): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
  )
  (pool6): Sequential(
    (0): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (1): Dropout(p=0.15, inplace=False)
  )
  (fc): Sequential(
    (0): Linear(in_features=60000, out_features=2, bias=True)
  )
)

四、训练模型

1.编写训练函数

optimizer.zero_grad()
loss.backward()
optimizer.step()
关于以上三个函数,我在之前的文章中有做说明,这里不再赘述

# 训练循环
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)  # 训练集的大小
    num_batches = len(dataloader)   # 批次数目

    train_loss, train_acc = 0, 0  # 初始化训练损失和正确率
    
    for X, y in dataloader:  # 获取图片及其标签
        X, y = X.to(device), y.to(device)
        
        # 计算预测误差
        pred = model(X)          # 网络输出
        loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
        
        # 反向传播
        optimizer.zero_grad()  # grad属性归零
        loss.backward()        # 反向传播
        optimizer.step()       # 每一步自动更新
        
        # 记录acc与loss
        train_acc  += (pred.argmax(1) == y).type(torch.float).sum().item()
        train_loss += loss.item()
            
    train_acc  /= size
    train_loss /= num_batches

    return train_acc, train_loss

2.编写测试函数

测试函数和训练函数大致相同,但是由于不进行梯度下降对网络权重进行更新,所以不需要传入优化器

def test (dataloader, model, loss_fn):
    size        = len(dataloader.dataset)  # 测试集的大小
    num_batches = len(dataloader)          # 批次数目,(size/batch_size,向上取整)
    test_loss, test_acc = 0, 0
    
    # 当不进行训练时,停止梯度更新,节省计算内存消耗
    with torch.no_grad():
        for imgs, target in dataloader:
            imgs, target = imgs.to(device), target.to(device)
            
            # 计算loss
            target_pred = model(imgs)
            loss        = loss_fn(target_pred, target)
            
            test_loss += loss.item()
            test_acc  += (target_pred.argmax(1) == target).type(torch.float).sum().item()

    test_acc  /= size
    test_loss /= num_batches

    return test_acc, test_loss

3.设置动态学习率

torch.optim.lr_scheduler.StepLR详解

等间隔动态调整方法,每经过step_size个epoch,做一次学习率decay,以gamma值为缩小倍数。

函数原型:

torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)

参数说明:

  • optimizer(Optimizer):是之前定义好的需要优化的优化器的实例名
  • step_size(int):是学习率衰减的周期,每经过每个epoch,做一次学习率decay
  • gamma(float):学习率衰减的乘法因子。Default:0.1

用法用例

optimizer = torch.optim.SGD(net.parameters(), lr=0.001 )
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1)

torch.optim.lr_scheduler.LambdaLR详解

根据自己定义的函数更新学习率。

函数原型:

torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1, verbose=False)

参数说明:

  • optimizer(Optimizer):是之前定义好的需要优化的优化器的实例名
  • lr_lambda(function):更新学习率的函数

用法用例

lambda1 = lambda epoch: (0.92 ** (epoch // 2) # 第二组参数的调整方法
optimizer = torch.optim.SGD(model.parameters(), lr=learn_rate)
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda1) #选定调整方法

torch.optim.lr_scheduler.MultiStepLR详解

在特定的 epoch 中调整学习率

函数原型:

torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1, verbose=False)

参数说明:

  • optimizer(Optimizer):是之前定义好的需要优化的优化器的实例名
  • milestones(list):是一个关于epoch数值的list,表示在达到哪个epoch范围内开始变化,必须是升序排列
  • gamma(float):学习率衰减的乘法因子。Default:0.1

用法用例

optimizer = torch.optim.SGD(net.parameters(), lr=0.001 )
scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, 
                                                 milestones=[2,6,15], #调整学习率的epoch数
                                                 gamma=0.1)

更多的官方动态学习率设置方式可参考:https://pytorch.org/docs/stable/optim.html

下面的是这次使用的代码:

''' 自定义设置动态学习率 '''
def adjust_learning_rate(optimizer, epoch, start_lr):
    # 每 2 个epoch衰减到原来的 0.92
    lr = start_lr * (0.92 ** (epoch // 2))
    for param_group in optimizer.param_groups:
        param_group['lr'] = lr


learn_rate = 2e-4 # 初始学习率
optimizer  = torch.optim.SGD(model.parameters(), lr=learn_rate)
# 调用官方动态学习率接口时使用
learn_rate = 2e-4 # 初始学习率
lambda1 = lambda epoch: (0.92 ** (epoch // 2)
optimizer = torch.optim.SGD(model.parameters(), lr=learn_rate)
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda1) #选定调整方法

以上两种方式是等价的


4.正式训练&保存最优模型

model.train()
model.eval()

关于以上两个个函数,我在之前的文章中有做说明,这里不再赘述

import time

''' 设置超参数 '''
start_epoch = 0
epochs      = 50
learn_rate  = 2e-4 # 初始学习率
loss_fn     = nn.CrossEntropyLoss()  # 创建损失函数
optimizer   = torch.optim.SGD(model.parameters(), lr=learn_rate)
#optimizer   = torch.optim.Adam(model.parameters(),lr=learn_rate)
train_loss  = []
train_acc   = []
test_loss   = []
test_acc    = []


''' 加载之前保存的模型 '''
if not os.path.exists(output) or not os.path.isdir(output):
    os.makedirs(output)
if start_epoch > 0:
    resumeFile = os.path.join(output, 'epoch'+str(start_epoch)+'.pkl')
    if not os.path.exists(resumeFile) or not os.path.isfile(resumeFile):
        start_epoch = 0
    else:
        model.load_state_dict(torch.load(resumeFile))  # 加载模型参数

''' 开始训练模型 '''
epoch_best_acc = 0
print('\nStart training...')
for epoch in range(start_epoch, epochs):
    # 更新学习率(使用自定义学习率时使用)
    adjust_learning_rate(optimizer, epoch, learn_rate)
    
    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)
    # scheduler.step() # 更新学习率(调用官方动态学习率接口时使用)
    
    model.eval()
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    # 获取当前的学习率
    lr = optimizer.state_dict()['param_groups'][0]['lr']
    
    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E}')
    print(time.strftime('[%Y-%m-%d %H:%M:%S]'), template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss, lr))
    if epoch_test_acc>epoch_best_acc:
        epoch_best_acc = epoch_test_acc
        print(('acc = {:.1f}%, saving model to best.pkl').format(epoch_best_acc*100))
        saveFile = os.path.join(output, 'best.pkl')
        torch.save(model.state_dict(), saveFile)
print('Done\n')
Start training...
[2022-10-27 13:00:03] Epoch: 1, Train_acc:53.2%, Train_loss:0.876, Test_acc:56.6%, Test_loss:0.665, Lr:2.00E-04
acc = 56.6%, saving model to best.pkl
[2022-10-27 13:00:11] Epoch: 2, Train_acc:54.6%, Train_loss:1.042, Test_acc:63.2%, Test_loss:0.597, Lr:2.00E-04
acc = 63.2%, saving model to best.pkl
[2022-10-27 13:00:18] Epoch: 3, Train_acc:64.9%, Train_loss:0.730, Test_acc:64.5%, Test_loss:0.644, Lr:1.84E-04
acc = 64.5%, saving model to best.pkl
[2022-10-27 13:00:25] Epoch: 4, Train_acc:68.9%, Train_loss:0.620, Test_acc:76.3%, Test_loss:0.494, Lr:1.84E-04
acc = 76.3%, saving model to best.pkl
[2022-10-27 13:00:33] Epoch: 5, Train_acc:71.3%, Train_loss:0.534, Test_acc:77.6%, Test_loss:0.503, Lr:1.69E-04
acc = 77.6%, saving model to best.pkl
[2022-10-27 13:00:41] Epoch: 6, Train_acc:80.7%, Train_loss:0.447, Test_acc:69.7%, Test_loss:0.562, Lr:1.69E-04
[2022-10-27 13:00:48] Epoch: 7, Train_acc:79.1%, Train_loss:0.474, Test_acc:68.4%, Test_loss:0.542, Lr:1.56E-04
[2022-10-27 13:00:55] Epoch: 8, Train_acc:78.3%, Train_loss:0.444, Test_acc:82.9%, Test_loss:0.518, Lr:1.56E-04
acc = 82.9%, saving model to best.pkl
[2022-10-27 13:01:02] Epoch: 9, Train_acc:84.1%, Train_loss:0.403, Test_acc:73.7%, Test_loss:0.513, Lr:1.43E-04
[2022-10-27 13:01:10] Epoch:10, Train_acc:84.3%, Train_loss:0.399, Test_acc:80.3%, Test_loss:0.428, Lr:1.43E-04
[2022-10-27 13:01:17] Epoch:11, Train_acc:89.0%, Train_loss:0.344, Test_acc:76.3%, Test_loss:0.491, Lr:1.32E-04
[2022-10-27 13:01:24] Epoch:12, Train_acc:84.9%, Train_loss:0.357, Test_acc:80.3%, Test_loss:0.460, Lr:1.32E-04
[2022-10-27 13:01:31] Epoch:13, Train_acc:90.4%, Train_loss:0.331, Test_acc:80.3%, Test_loss:0.447, Lr:1.21E-04
[2022-10-27 13:01:38] Epoch:14, Train_acc:89.6%, Train_loss:0.323, Test_acc:76.3%, Test_loss:0.511, Lr:1.21E-04
[2022-10-27 13:01:46] Epoch:15, Train_acc:89.6%, Train_loss:0.311, Test_acc:82.9%, Test_loss:0.414, Lr:1.12E-04
[2022-10-27 13:01:53] Epoch:16, Train_acc:91.8%, Train_loss:0.294, Test_acc:78.9%, Test_loss:0.430, Lr:1.12E-04
[2022-10-27 13:02:00] Epoch:17, Train_acc:92.0%, Train_loss:0.291, Test_acc:80.3%, Test_loss:0.410, Lr:1.03E-04
[2022-10-27 13:02:07] Epoch:18, Train_acc:91.2%, Train_loss:0.293, Test_acc:78.9%, Test_loss:0.414, Lr:1.03E-04
[2022-10-27 13:02:14] Epoch:19, Train_acc:92.4%, Train_loss:0.274, Test_acc:80.3%, Test_loss:0.450, Lr:9.44E-05
[2022-10-27 13:02:21] Epoch:20, Train_acc:93.2%, Train_loss:0.269, Test_acc:80.3%, Test_loss:0.404, Lr:9.44E-05
[2022-10-27 13:02:28] Epoch:21, Train_acc:94.0%, Train_loss:0.262, Test_acc:78.9%, Test_loss:0.424, Lr:8.69E-05
[2022-10-27 13:02:35] Epoch:22, Train_acc:92.2%, Train_loss:0.265, Test_acc:84.2%, Test_loss:0.445, Lr:8.69E-05
acc = 84.2%, saving model to best.pkl
[2022-10-27 13:02:42] Epoch:23, Train_acc:94.8%, Train_loss:0.242, Test_acc:78.9%, Test_loss:0.409, Lr:7.99E-05
[2022-10-27 13:02:50] Epoch:24, Train_acc:94.0%, Train_loss:0.262, Test_acc:80.3%, Test_loss:0.417, Lr:7.99E-05
[2022-10-27 13:02:57] Epoch:25, Train_acc:93.6%, Train_loss:0.245, Test_acc:81.6%, Test_loss:0.428, Lr:7.35E-05
[2022-10-27 13:03:04] Epoch:26, Train_acc:95.2%, Train_loss:0.243, Test_acc:78.9%, Test_loss:0.479, Lr:7.35E-05
[2022-10-27 13:03:12] Epoch:27, Train_acc:94.8%, Train_loss:0.234, Test_acc:77.6%, Test_loss:0.439, Lr:6.77E-05
[2022-10-27 13:03:20] Epoch:28, Train_acc:96.2%, Train_loss:0.221, Test_acc:80.3%, Test_loss:0.445, Lr:6.77E-05
[2022-10-27 13:03:27] Epoch:29, Train_acc:96.0%, Train_loss:0.224, Test_acc:80.3%, Test_loss:0.441, Lr:6.22E-05
[2022-10-27 13:03:34] Epoch:30, Train_acc:96.4%, Train_loss:0.225, Test_acc:77.6%, Test_loss:0.476, Lr:6.22E-05
[2022-10-27 13:03:41] Epoch:31, Train_acc:96.2%, Train_loss:0.216, Test_acc:78.9%, Test_loss:0.453, Lr:5.73E-05
[2022-10-27 13:03:48] Epoch:32, Train_acc:96.6%, Train_loss:0.217, Test_acc:78.9%, Test_loss:0.427, Lr:5.73E-05
[2022-10-27 13:03:55] Epoch:33, Train_acc:96.6%, Train_loss:0.216, Test_acc:78.9%, Test_loss:0.428, Lr:5.27E-05
[2022-10-27 13:04:03] Epoch:34, Train_acc:95.2%, Train_loss:0.216, Test_acc:81.6%, Test_loss:0.420, Lr:5.27E-05
[2022-10-27 13:04:10] Epoch:35, Train_acc:94.8%, Train_loss:0.212, Test_acc:78.9%, Test_loss:0.408, Lr:4.85E-05
[2022-10-27 13:04:17] Epoch:36, Train_acc:96.2%, Train_loss:0.216, Test_acc:80.3%, Test_loss:0.417, Lr:4.85E-05
[2022-10-27 13:04:25] Epoch:37, Train_acc:95.2%, Train_loss:0.214, Test_acc:78.9%, Test_loss:0.413, Lr:4.46E-05
[2022-10-27 13:04:32] Epoch:38, Train_acc:94.4%, Train_loss:0.211, Test_acc:80.3%, Test_loss:0.413, Lr:4.46E-05
[2022-10-27 13:04:39] Epoch:39, Train_acc:95.2%, Train_loss:0.210, Test_acc:78.9%, Test_loss:0.409, Lr:4.10E-05
[2022-10-27 13:04:47] Epoch:40, Train_acc:96.8%, Train_loss:0.201, Test_acc:78.9%, Test_loss:0.433, Lr:4.10E-05
[2022-10-27 13:04:54] Epoch:41, Train_acc:97.6%, Train_loss:0.199, Test_acc:78.9%, Test_loss:0.395, Lr:3.77E-05
[2022-10-27 13:05:01] Epoch:42, Train_acc:96.0%, Train_loss:0.209, Test_acc:80.3%, Test_loss:0.410, Lr:3.77E-05
[2022-10-27 13:05:08] Epoch:43, Train_acc:96.8%, Train_loss:0.198, Test_acc:77.6%, Test_loss:0.479, Lr:3.47E-05
[2022-10-27 13:05:15] Epoch:44, Train_acc:96.8%, Train_loss:0.199, Test_acc:80.3%, Test_loss:0.426, Lr:3.47E-05
[2022-10-27 13:05:23] Epoch:45, Train_acc:96.8%, Train_loss:0.205, Test_acc:80.3%, Test_loss:0.454, Lr:3.19E-05
[2022-10-27 13:05:30] Epoch:46, Train_acc:96.0%, Train_loss:0.200, Test_acc:80.3%, Test_loss:0.409, Lr:3.19E-05
[2022-10-27 13:05:38] Epoch:47, Train_acc:97.4%, Train_loss:0.196, Test_acc:78.9%, Test_loss:0.449, Lr:2.94E-05
[2022-10-27 13:05:45] Epoch:48, Train_acc:97.4%, Train_loss:0.190, Test_acc:80.3%, Test_loss:0.449, Lr:2.94E-05
[2022-10-27 13:05:52] Epoch:49, Train_acc:96.6%, Train_loss:0.191, Test_acc:80.3%, Test_loss:0.406, Lr:2.70E-05
[2022-10-27 13:05:59] Epoch:50, Train_acc:96.6%, Train_loss:0.193, Test_acc:80.3%, Test_loss:0.424, Lr:2.70E-05
Done

最终结果,最优模型(Epoch:22的结果)的训练集准确率达到92.2%,测试集准确率达到84.2%


五、结果可视化

''' 结果可视化 '''
def displayResult(train_acc, test_acc, train_loss, test_loss, start_epoch, epochs, output=''):
    # 隐藏警告
    warnings.filterwarnings("ignore")                # 忽略警告信息
    plt.rcParams['font.sans-serif']    = ['SimHei']  # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False       # 用来正常显示负号
    plt.rcParams['figure.dpi']         = 100         # 分辨率
    
    epochs_range = range(start_epoch, epochs)
    
    plt.figure('Result Visualization', figsize=(12, 3))
    plt.subplot(1, 2, 1)
    
    plt.plot(epochs_range, train_acc, label='Training Accuracy')
    plt.plot(epochs_range, test_acc, label='Test Accuracy')
    plt.legend(loc='lower right')
    plt.title('Training and Validation Accuracy')
    
    plt.subplot(1, 2, 2)
    plt.plot(epochs_range, train_loss, label='Training Loss')
    plt.plot(epochs_range, test_loss, label='Test Loss')
    plt.legend(loc='upper right')
    plt.title('Training and Validation Loss')
    plt.savefig(os.path.join(output, 'AccuracyLoss.png'))
    plt.show()

''' 绘制准确率&损失率曲线图 '''
displayResult(train_acc, test_acc, train_loss, test_loss, start_epoch, epochs, output)

365天深度学习训练营-第P5周:运动鞋识别_第2张图片


六、加载模型&指定图片进行预测

''' 预测函数 '''
def predict(model, img_path):
    img = Image.open(img_path)
    test_transforms = torchvision.transforms.Compose([
        torchvision.transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
        torchvision.transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
        torchvision.transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
            mean=[0.485, 0.456, 0.406], 
            std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
    ])
    img = test_transforms(img)
    img = img.to(device).unsqueeze(0)
    output = model(img)
    #print(output.argmax(1))
    
    _, indices = torch.max(output, 1)
    percentage = torch.nn.functional.softmax(output, dim=1)[0] * 100
    perc = percentage[int(indices)].item()
    result = classeNames[indices]
    print('predicted:', result, perc)


if __name__=='__main__':
    classeNames = ['adidas', 'nike']
    num_classes = len(classeNames)
    
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print("Using {} device\n".format(device))
    
    model = Model().to(device)
    model.load_state_dict(torch.load(os.path.join('output', 'best.pkl')))
    model.eval()
    
    img_path = 'data/test/nike/21.jpg'
    predict(model, img_path)
Using cuda device

predicted: adidas 81.73172760009766

七、总结

这次课题原本想要沿用前两次课题的网络,但是测试过后发现测试集的准确率最高只到70%左右,后续很难再上升。

第一次优化,参考了K同学啊给的范例模型,删减了最后三层的卷积以及池化层,并在前面的每次池化层后面又增加了 DropoutDropout 层的丢弃率设置为0.11,初始学习率设置为2e-4。50轮后,训练集准确率达到了97%,测试集准确率只达到了80%。

第二次优化,Dropout 层的丢弃率设置为0.15,初始学习率设置为2e-4。50轮后,训练集准确率达到了92%,测试集准确率达到了84%。

你可能感兴趣的:(365天深度学习训练记录,深度学习,pytorch,人工智能)