- 本文为365天深度学习训练营 中的学习记录博客
- 参考文章:Pytorch实战 | 第P5周:运动鞋识别
- 原作者:K同学啊|接辅导、项目定制
第P5周:运动鞋识别
要求:
拔高(可选):
cmd
输入nvcc -V
或nvcc --version
指令可查看)如果设备上支持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
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])
''' 加载数据,并设置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])
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)
)
)
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
测试函数和训练函数大致相同,但是由于不进行梯度下降对网络权重进行更新,所以不需要传入优化器
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
⭐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) #选定调整方法
以上两种方式是等价的
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)
''' 预测函数 '''
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同学啊给的范例模型,删减了最后三层的卷积以及池化层,并在前面的每次池化层后面又增加了 Dropout。Dropout 层的丢弃率设置为0.11,初始学习率设置为2e-4。50轮后,训练集准确率达到了97%,测试集准确率只达到了80%。
第二次优化,Dropout 层的丢弃率设置为0.15,初始学习率设置为2e-4。50轮后,训练集准确率达到了92%,测试集准确率达到了84%。