自学内容网 自学内容网

PyTorch面部表情识别项目实战

新书速览|PyTorch深度学习与企业级项目实战-CSDN博客

本书案例比较丰富、比较完整,可以用于课题研究、毕业论文素材,值得大家收藏。

人脸表情是人类信息交流的重要方式,它所包含的人体行为信息与人的情感状态、精神状态、健康状态等有着极为密切的关联。因此,通过对人脸表情的识别可以获得很多有价值的信息,从而分析人类的心理活动和精神状态,并为各种机器视觉和人工智能控制系统的应用提供解决方案。 所以本项目在研究人脸面部表情识别的过程中,借助人工智能算法的优势开展基于深度神经网络的图像分类实验。

借助MobileNetv3模型进行迁移学习,经过足够多次的迭代,分类准确率可以达到90%。这里使用的MMAFEDB数据集包含128 000MMA面部表情图像数据集 MMAFEDB数据集包含用于、验证和测试的目录。 每个目录包含对应7个面部表情类别的7个子目录。

MMAFEDB数据集数据说明如下:

  1. Angry:愤怒。
  2. Disgust:厌恶。
  3. Fear:恐惧。
  4. Happy:快乐。
  5. Neutral:中性。
  6. Sad:悲伤。
  7. Surprise:惊讶。

MMAFEDB数据集数据来源如下:

https://www.kaggle.com/mahmoudima/mma-facial-expression?select=MMAFEDB

相对重量级网络而言,轻量级网络的特点是参数少、计算量小、推理时间短,更适用于存储空间和功耗受限的场景,例如移动端嵌入式设备等边缘计算设备。因此,轻量级网络受到了广泛的关注,其中MobileNet可谓是其中的佼佼者。MobileNetV3经过了V1和V2前两代的积累,性能和速度都表现优异,受到学术界和工业界的追捧,无疑是轻量级网络的“抗把子”。

本项目这里加载预训练的MobileNetv3模型,由于预训练的模型与我们的任务需要不一样,因此需要修改最后的全连接层,将输出维度修改为我们的任务要求中的7个分类(7种面部表情)。 但是需要注意冻结其他层的参数,防止训练过程中将其改动,然后训练微调最后一层即可。

由于我们的任务为多分类问题,因此损失函数需要使用交叉熵损失函数(Cross Entropy),但是这里没有采用框架自带的损失函数,而是自己实现了一个损失函数。虽说大多数情况下,框架自带的损失函数就能够满足我们的需求,但是对于一些特定任务是无法满足的,需要我们进行自定义。自定义函数需要继承 nn.Module 子类,然后定义好参数和所需的变量,在forward方法中编写计算损失函数的过程,然后PyTorch会自动计算反向传播需要的梯度,不需要我们自己进行计算。

###########expression_on_face.py#####################
import torchvision
from torch import nn
import numpy as np
import os
import pickle
import torch

from torchvision import transforms, datasets
import torchvision.models as models
from tqdm import tqdm
from PIL import Image
import matplotlib.pyplot as plt

epochs = 10
lr = 0.03
batch_size = 32
image_path = './The_expression_on_his_face/train'
save_path = './chk/expression_model.pkl'

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

# 1.数据转换
data_transform = {
    # 训练中的数据增强和归一化
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),  # 随机裁剪
        transforms.RandomHorizontalFlip(),  # 左右翻转
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  # 均值方差归一化
    ])
}

# 2.形成训练集
train_dataset = datasets.ImageFolder(root=os.path.join(image_path),
                                     transform=data_transform['train'])

# 3.形成迭代器
train_loader = torch.utils.data.DataLoader(train_dataset,
                                           batch_size,
                                           True)

print('using {} images for training.'.format(len(train_dataset)))

# 4.建立分类标签与索引的关系
cloth_list = train_dataset.class_to_idx
class_dict = {}
for key, val in cloth_list.items():
    class_dict[val] = key
with open('class_dict.pk', 'wb') as f:
    pickle.dump(class_dict, f)

print(class_dict.values())

# 自定义损失函数,需要在forward中定义过程
class MyLoss(nn.Module):
    def __init__(self):
        super(MyLoss, self).__init__()

    # 参数为传入的预测值和真实值,返回所有样本的损失值
    # 自己只需定义计算过程,反向传播PyTroch会自动记录
    def forward(self, pred, label):
        # pred:[32, 4] label:[32, 1] 第一维度是样本数

        exp = torch.exp(pred)
        tmp1 = exp.gather(1, label.unsqueeze(-1)).squeeze()
        tmp2 = exp.sum(1)
        softmax = tmp1 / tmp2
        log = -torch.log(softmax)
        return log.mean()

# 5.加载MobileNetv3模型
#加载预训练好的MobileNetv3模型
model = torchvision.models.mobilenet_v3_small(
    weights=models.MobileNet_V3_Small_Weights.DEFAULT)
# 冻结模型参数
for param in model.parameters():
    param.requires_grad = False

# 修改最后一层的全连接层
model.classifier[3] = nn.Linear(model.classifier[3].in_features, 7)

# 将模型加载到cpu中
model = model.to('cpu')

# criterion = nn.CrossEntropyLoss() # 损失函数
criterion = MyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)  # 优化器

# 6.模型训练
best_acc = 0  # 最优精确率
best_model = None  # 最优模型参数

for epoch in range(epochs):
    model.train()
    running_loss = 0  # 损失
    epoch_acc = 0  # 每个epoch的准确率
    epoch_acc_count = 0 # 每个epoch训练的样本数
    train_count = 0  # 用于计算总的样本数,方便求准确率
    train_bar = tqdm(train_loader)
    for data in train_bar:
        images, labels = data
        optimizer.zero_grad()
        output = model(images.to(device))
        loss = criterion(output, labels.to(device))
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                 epochs,
                                                                 loss)
        # 计算每个epoch正确的个数
        epoch_acc_count += (output.argmax(axis=1) == labels.view(-1)).sum()
        train_count += len(images)

    # 每个epoch对应的准确率
    epoch_acc = epoch_acc_count / train_count

    # 打印信息
    print("【EPOCH: 】%s" % str(epoch + 1))
    print("训练损失为%s" % str(running_loss))
    print("训练精度为%s" % (str(epoch_acc.item() * 100)[:5]) + '%')

    if epoch_acc > best_acc:
        best_acc = epoch_acc
        best_model = model.state_dict()

    # 在训练结束保存最优的模型参数
    if epoch == epochs - 1:
        # 保存模型
        torch.save(best_model, save_path)

print('Finished Training')

# 加载索引与标签映射字典
with open('class_dict.pk', 'rb') as f:
    class_dict = pickle.load(f)

# 数据变换
data_transform = transforms.Compose(
    [transforms.Resize(256),
     transforms.CenterCrop(224),
     transforms.ToTensor()])

# 图片路径
img_path = r'./The_expression_on_his_face/test.jpg'

# 打开图像
#为了避免通道数不匹配,使用灰度图像(1通道),使用RGB图像(3通道)
#解决方式:加载图像时,做一下转换
img = Image.open(img_path).convert('RGB')

# 对图像进行变换
img = data_transform(img)

plt.imshow(img.permute(1, 2, 0))
plt.show()

# 将图像升维,增加batch_size维度
img = torch.unsqueeze(img, dim=0)

# 获取预测结果
pred = class_dict[model(img).argmax(axis=1).item()]
print('【预测结果分类】:%s' % pred)

代码运行结果如下:

using 851 images for training.
dict_values(['angry', 'disgust', 'fear', 'happy', 'neutral', 'sad', 'surprise'])
train epoch[1/10] loss:1.943: 100%|██████████| 27/27 [00:10<00:00,  2.65it/s]
  0%|          | 0/27 [00:00<?, ?it/s]【EPOCH: 】1
训练损失为54.592151284217834
训练精度为23.14%
train epoch[2/10] loss:1.906: 100%|██████████| 27/27 [00:10<00:00,  2.50it/s]
  0%|          | 0/27 [00:00<?, ?it/s]【EPOCH: 】2
训练损失为51.95514786243439
训练精度为27.96%
train epoch[3/10] loss:1.873: 100%|██████████| 27/27 [00:10<00:00,  2.68it/s]
【EPOCH: 】3
训练损失为54.413649916648865
训练精度为29.96%
train epoch[4/10] loss:1.508: 100%|██████████| 27/27 [00:10<00:00,  2.60it/s]
【EPOCH: 】4
训练损失为51.14111852645874
训练精度为30.66%
train epoch[5/10] loss:1.816: 100%|██████████| 27/27 [00:13<00:00,  2.05it/s]
【EPOCH: 】5
训练损失为52.17003357410431
训练精度为32.07%
train epoch[6/10] loss:1.833: 100%|██████████| 27/27 [00:11<00:00,  2.31it/s]
  0%|          | 0/27 [00:00<?, ?it/s]【EPOCH: 】6
训练损失为51.988134145736694
训练精度为31.37%
train epoch[7/10] loss:1.907: 100%|██████████| 27/27 [00:10<00:00,  2.49it/s]
【EPOCH: 】7
训练损失为51.65321123600006
训练精度为32.54%
train epoch[8/10] loss:1.993: 100%|██████████| 27/27 [00:11<00:00,  2.40it/s]
【EPOCH: 】8
训练损失为51.17294144630432
训练精度为33.72%
train epoch[9/10] loss:1.682: 100%|██████████| 27/27 [00:13<00:00,  2.02it/s]
【EPOCH: 】9
训练损失为52.21281313896179
训练精度为29.49%
train epoch[10/10] loss:1.926: 100%|██████████| 27/27 [00:15<00:00,  1.75it/s]
【EPOCH: 】10
训练损失为50.530142426490784
训练精度为32.43%
Finished Training
【预测结果分类】:angry

注意,这里只是列出计算10个周期,如果需要提高训练精度,需要增加训练周期100个以及扩大训练样本量。读者可以自行尝试。

《PyTorch深度学习与企业级项目实战(人工智能技术丛书)》(宋立桓,宋立林)【摘要 书评 试读】- 京东图书 (jd.com)


原文地址:https://blog.csdn.net/brucexia/article/details/140484580

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!