第P6周:VGG-16算法-Pytorch实现人脸识别

>- **🍨 本文为[🔗365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客**
>- **🍖 原作者:[K同学啊](https://mtyjkh.blog.csdn.net/)**

一、前期准备 

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasets
import os,PIL,pathlib,warnings

warnings.filterwarnings("ignore")             #忽略警告信息

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device

二、导入数据

读取数据集中不同类别的名字,在这个案例中我们识别的是17个好莱坞明星的人脸,故有17个类别的图片数据

import os,PIL,random,pathlib

data_dir = './第六周/'
data_dir = pathlib.Path(data_dir)

data_paths  = list(data_dir.glob('*'))
classeNames = [str(path).split("\\")[1] for path in data_paths]
classeNames

三、数据预处理

对数据集进行一个预处理,首先将图片尺寸统一调整为224*224像素,再做随机翻转以提升泛化能力做数据增强处理,接着转换为 PyTorch 的张量 (tensor) 格式,并将像素值归一化到 [0,1] 之间,然后做一个标准化处理,最终创建一个 ImageFolder 数据集对象,我们照例查看了每个类别的索引。

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("./第六周/",transform=train_transforms)
total_data

 

total_data.class_to_idx

下面也是以往的操作,设置批组样本数目为32,设置在每次迭代中打乱数据,子进程数为1. 

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])
train_dataset, test_dataset
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

四、调用官方的VGG-16模型

这次我们不再使用自己构建的简单CNN模型而是直接调用经典的VGG-16模型,它由16个卷积层和3个全连接层组成,因此具有相对较深的网络结构。这种深度有助于网络学习到更加抽象和复杂的特征。

但是我们在调用时冻结了预训练模型的所有参数、修改了模型 classifier 部分的最后一层全连接层,使其输出的类别数等于数据集中的类别数。这些修改的目的是利用预训练模型的特征提取能力,同时只训练模型的最后一层,以适应本次分类任务。

from torchvision.models import vgg16
#加载预训练的 VGG16 模型,并将其移动到指定的设备(CPU 或 GPU)。
#冻结预训练模型的所有参数,即在训练过程中这些参数不更新,只训练修改的最后一层参数。
#修改模型的 classifier 部分的最后一层全连接层,使其输出的类别数等于数据集中的类别数。
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)  
model

五、训练模型

下面设置训练模型,包括训练函数、测试函数以及损失函数和优化器

# 训练循环
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

在设置学习率时,我们仍然使用上一周的动态学习率调度器,进行模型优化。

# 定义损失函数和优化器
learn_rate = 1e-4  # 初始学习率
optimizer = torch.optim.SGD(model.parameters(), lr=learn_rate)

# 定义学习率调度器
lambda1 = lambda epoch: 0.92 ** (epoch // 4)
scheduler = LambdaLR(optimizer, lr_lambda=lambda1)

loss_fn = nn.CrossEntropyLoss()  # 创建损失函数
epochs = 40

train_loss = []
train_acc = []
test_loss = []
test_acc = []

best_acc = 0  # 设置一个最佳准确率,作为最佳模型的判别指标

六、正式训练

设置好上面所有步骤,我们进入正式训练,随着训练的模型复杂化,样本增多,训练时间也会随之增加。

for epoch in range(epochs):
    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)
    
    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 = './best_model.pth'
torch.save(best_model.state_dict(), PATH)
print('Done')

 但是我们发现这个模型的准确率不是很高,在测试集上的预测准确率甚至只有20%左右,我们想进一步改善模型。

七、模型优化

我们从以下三个方面来优化我们的模型:

1.数据增强

# 忽略警告信息
warnings.filterwarnings("ignore")
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['figure.dpi'] = 100

# 数据处理与增强
data_dir = './第六周/'
data_dir = pathlib.Path(data_dir)

train_transforms = transforms.Compose([
    transforms.RandomResizedCrop(224),                 # 随机裁剪
    transforms.RandomHorizontalFlip(),                 # 随机水平翻转
    transforms.RandomRotation(15),                     # 随机旋转
    transforms.ColorJitter(brightness=0.4, contrast=0.4, saturation=0.4, hue=0.2), # 颜色抖动
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

test_transforms = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

total_data = datasets.ImageFolder(data_dir, 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)

2.解冻部分卷积层

解除模型中所有层的冻结状态,使所有层都参与训练,以便模型可以更好地学习数据特征。

 

# 模型定义与修改
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))

model = models.vgg16(pretrained=True)
model.classifier[6] = nn.Linear(model.classifier[6].in_features, len(total_data.classes))
model = model.to(device)

# 解冻部分卷积层
for param in model.features.parameters():
    param.requires_grad = True

 3.使用Adam优化器和权重衰减

替换原来的SGD优化器为Adam优化器,并添加权重衰减(L2正则化)以防止过拟合。

# 定义损失函数和优化器
learn_rate = 1e-4  # 初始学习率
optimizer = Adam(model.parameters(), lr=learn_rate, weight_decay=1e-4)  # 使用Adam优化器和权重衰减

loss_fn = nn.CrossEntropyLoss()

进行以上三个操作后,我们重新进行训练

# 训练和测试函数
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)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        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)
    test_loss, test_acc = 0, 0
    
    with torch.no_grad():
        for imgs, target in dataloader:
            imgs, target = imgs.to(device), target.to(device)
            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

# 模型训练
epochs = 40
train_loss, train_acc = [], []
test_loss, test_acc = [], []
best_acc = 0

for epoch in range(epochs):
    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)
    
    model.eval()
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    
    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 = './best_model2.pth'
torch.save(best_model.state_dict(), PATH)
print('Done')

可以看到模型在测试集上的准确率可以提升到60%以上,模型得到改进。

八、总结

在样本数量和复杂度经过提升后,我们需要设置更高层次的CNN模型,调用经典的模型是比较轻松且经典模型往往也能在大多数数据集上表现得不错,但仍需根据自己的数据集和分类任务进行一定的改进,才能使得模型在自己的数据集上表现得更好,同时模型的优化方法也是关键的一步,仍需学习更多更全面的优化方法,而不局限于原来单个的sgd方法。

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python是一种常用的编程语言,而PyTorchPython的一个流行的深度学习框架。使用PyTorch可以实现实时人脸检测和识别,以及构建一个基于人脸识别的考勤系统。 首先,我们需要准备一个可以进行实时人脸检测和识别的数据集,其中包括多个人的照片。可以使用已有的数据集,也可以自己收集数据。 接下来,使用PyTorch中的人脸检测算法进行人脸的定位和检测。常用的算法有基于特征的描述子算法和基于深度学习的算法。深度学习算法通常使用卷积神经网络(CNN)进行人脸检测。 在检测到人脸后,使用PyTorch中的人脸识别算法进行人脸的特征提取和识别。特征提取可以使用一些经过预训练的深度学习模型,如VGG、ResNet等。然后,将提取到的特征与之前准备好的人脸数据集进行比对,找到最相似的人脸。 最后,将识别到的人脸与考勤系统结合,记录并统计员工的考勤情况。可以使用数据库或文件来存储员工的信息和考勤记录。 当系统运行时,摄像头实时捕捉图像,并使用实时人脸检测算法定位人脸。然后,使用人脸识别算法提取特征并与之前的数据集进行比对。如果匹配成功,则表示检测到已注册的人脸。根据识别结果记录员工的考勤情况,可以生成考勤报告或其他需要的信息。 总之,使用PythonPyTorch,可以实现实时人脸检测识别与考勤系统。通过逐步定位人脸、提取特征、与数据集匹配等步骤,可以实现基于人脸的考勤系统,并记录员工的考勤情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值