利用VGG16进行人脸鞋识别(pytorch版)
🍨 本文为🔗365天深度学习训练营 中的学习记录博客
🍖 原作者:K同学啊 | 接辅导、项目定制
一、代码
import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasets
import os,PIL,pathlib,warnings
import os,PIL,random,pathlib
from torchvision.models import vgg16
warnings.filterwarnings("ignore") #忽略警告信息
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
data_dir = '/home/kaijiang/zlf/CSDN task/the six week/48-data/'
data_dir = pathlib.Path(data_dir)
data_paths = list(data_dir.glob('*'))
classeNames = [str(path).split("\\")[0] for path in data_paths]
train_transforms = transforms.Compose([
transforms.Resize([224, 224]), # 将输入图片resize成统一尺寸
# transforms.RandomHorizontalFlip(), # 随机水平翻转
transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
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] 从数据集中随机抽样计算得到的。
])
total_data = datasets.ImageFolder("/home/kaijiang/zlf/CSDN task/the six week/48-data/",transform=train_transforms)
train_size = int(0.8 * len(total_data))
test_size = len(total_data) - train_size
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
batch_size = 32
train_dl = torch.utils.data.DataLoader(train_dataset,
batch_size=batch_size,
shuffle=True,
num_workers=1)
test_dl = torch.utils.data.DataLoader(test_dataset,
batch_size=batch_size,
shuffle=True,
num_workers=1)
for X, y in test_dl:
print("Shape of X [N, C, H, W]: ", X.shape)
print("Shape of y: ", y.shape, y.dtype)
break
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))
# 加载预训练模型,并且对模型进行微调
model = vgg16(pretrained = True).to(device) # 加载预训练的vgg16模型
for param in model.parameters():
param.requires_grad = False # 冻结模型的参数,这样子在训练的时候只训练最后一层的参数
# 修改classifier模块的第6层(即:(6): Linear(in_features=4096, out_features=2, bias=True))
# 注意查看我们下方打印出来的模型
model.classifier._modules['6'] = nn.Linear(4096,len(classeNames)) # 修改vgg16模型中最后一层全连接层,输出目标类别个数
model.to(device)
def train(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset) # 训练集的大小
num_batches = len(dataloader) # 批次数目, (size/batch_size,向上取整)
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
def adjust_learning_rate(optimizer, epoch, start_lr):
# 每 2 个epoch衰减到原来的 0.98
lr = start_lr * (0.92 ** (epoch // 2))
for param_group in optimizer.param_groups:
param_group['lr'] = lr
learn_rate = 1e-3 # 初始学习率
optimizer = torch.optim.SGD(model.parameters(), lr=learn_rate)
import copy
loss_fn = nn.CrossEntropyLoss() # 创建损失函数
epochs = 40
train_loss = []
train_acc = []
test_loss = []
test_acc = []
best_acc = 0 # 设置一个最佳准确率,作为最佳模型的判别指标
for epoch in range(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)
# 保存最佳模型到 best_model
if epoch_test_acc > best_acc:
best_acc = epoch_test_acc
best_model = copy.deepcopy(model)
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(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss,
epoch_test_acc*100, epoch_test_loss, lr))
# 保存最佳模型到文件中
PATH = '/home/kaijiang/zlf/CSDN task/the six week/best_model.pth' # 保存的参数文件名
torch.save(model.state_dict(), PATH)
print('Done')
二、模型评估
1、恒定learn_rate = 1e-4,epoch=40
2、动态 learn_rate = 1e-4,epoch=40
3、恒定学习率 learn_rate = 1e-3,epoch=40
4、 动态 learn_rate = 1e-3,epoch=40
5、动态 learn_rate = 1e-2,epoch=30
6、恒定 learn_rate = 1e-2,epoch=40
三、理论基础
VGG是牛津大学视觉组(Visual Geometry Group)在2014年提出来的,并取得了 ImageNet 2014比赛分类组的第二名和定位任务的第一名。
VGG的改进
1.采用3*3的小尺寸的卷积核,步长为1,参数量更少;
2.去掉了LRN层
LRN是Local Response Normalization的缩写,即局部响应归一化。
LRN层是一种规范化技术,用于增强深度神经网络模型的泛化性能。它在卷积层之后被应用,旨在提高网络对于不同位置的特征响应强度的鲁棒性。
局部响应归一化通过对每个神经元及其相邻神经元的输出进行归一化来实现。具体而言,对于给定的神经元,它将其输出除以一个归一化参数,该归一化参数由该神经元的邻近神经元输出的平方和加上一个较小的常数来计算。
LRN层的目的是提供一种竞争机制,使得输出较大的神经元能够在邻近神经元的竞争中更为突出。这样可以增强网络对于局部特征的感知能力,并提高网络的鲁棒性。
需要注意的是,在后续的卷积神经网络的研究和实践中,LRN层并不常见,有时会被其他正则化技术(如批标准化)所取代。
3.VGG还 有一个结构特点是重复使用简单的卷积块(Convolution Block)来堆叠得到模型
四、训练结论
1、为什么初始学习率增大以后,VGG测试集的准确率提高了20个百分点?
始学习率增大后,VGG测试集准确率提高20个百分点可能是由于以下原因之一或者几个因素的结合:
(1)收敛速度更快: 增大初始学习率可能会加快模型的收敛速度,使模型在相同的训练轮次内达到更高的准确率。
(2)学习步长更大: 增大初始学习率会使每次参数更新的步长增加,这有助于跳出局部最优解并更快地找到全局最优解。这可能导致模型更好地捕捉数据集的特征,从而提高测试集的准确率。
(3)泛化能力更好: 一些情况下,增大初始学习率可能会帮助模型更好地泛化到测试集数据。通过在更大的学习率下训练,模型可能更能够适应数据的多样性,从而提高测试集的准确率。
(4)减少过拟合: 在某些情况下,增大初始学习率可能有助于减少模型的过拟合,尤其是在正则化不足的情况下。更大的学习率可能会导致模型更快地达到平滑的参数值,从而减少过拟合的可能性,提高测试集的准确率。
但是增大初始学习率可能也会带来一些负面影响,如训练过程中的不稳定性、易于跳过最优解等。因此,在调整学习率时需要进行适当的实验和验证,以确保获得最佳的模型性能。
2、为什么在训练VGG16的时候,将学习率从1e-3变为1e-4以后,训练时间变长?
(1)学习率影响收敛速度: 学习率的大小直接影响模型参数在训练过程中的调整步长。较高的学习率(如1e-3)会导致参数在每次更新时发生较大的变化,模型可能更快地收敛到局部最优点或者陷入局部最优点附近的震荡状态。而较低的学习率(如1e-4)则会使参数调整变得更加缓慢,需要更多的训练迭代才能达到相同的收敛程度。
(2)学习率影响训练稳定性: 过高的学习率可能导致训练过程中出现训练不稳定的情况,如发散或者在局部最优点附近震荡。通过降低学习率,训练过程可能更加稳定,但相应地需要更多的迭代次数才能达到较好的训练效果。
(3)调整学习率需要更多迭代: 降低学习率通常需要更多的训练迭代次数才能达到相同的收敛效果。因此,当你将学习率从1e-3降低到1e-4时,模型可能需要更长的训练时间才能达到相同的性能水平。
(4)模型参数调整速度减慢: 较低的学习率意味着模型参数在训练过程中的调整速度减慢,因此需要更多的迭代次数才能达到最优的参数设置。