FER2013人脸表情识别从零实现(Pytorch,FNN,CNN)

这篇具有很好参考价值的文章主要介绍了FER2013人脸表情识别从零实现(Pytorch,FNN,CNN)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

学完了深度学习的理论和框架,接下来就是实践啦!相信大家在实践的时候都会去找各种的项目,本文是kaggle上面的一个深度学习小项目,目的是实现人脸表情的分类。接下来我将具体讲解实现过程,相信你只要学了深度学习的理论知识,对pytorch框架有所了解,就能够看得懂。话不多说,开讲!

深度学习首先要做的就是准备数据集,本项目的数据集可以直接从kaggle上面下载,附上链接(FER-2013数据集)。数据集由训练集和测试集组成,训练集包含28709张人脸图片,按照不同表情分为愤怒,厌恶,恐惧,快乐,悲伤,惊讶,中性七个类别,放在不同的文件夹中。测试集包含3589图片,也一样的分好类放在不同的文件夹中,其中每张图片都是48*48像素的灰度图像。

FER2013人脸表情识别从零实现(Pytorch,FNN,CNN)

数据集准备好之后,接下来开始搭建模型。众所周知,深度学习分为模型,学习准则,优化算法三个模块,后面我会一一介绍。本文分别用FNN(前馈神经网络),CNN(卷积神经网络)进行了项目的实现,先介绍FNN。

第一步,导包,这里我们要用到四个包,后面用的时候再详细介绍。

import torch  # 导入pytorch
from torch.utils.data import DataLoader  # 加载数据集的包
from torchvision import transforms  # 对数据进行处理
from torchvision.datasets import ImageFolder  # 按文件夹对自动给图片打标签

深度学习在实现过程中,无非就是四个步骤,导入数据集、搭建训练模型、训练、测试。首先讲第一步:前面已经下载好了数据集,我们要把train和test数据集放在pycharm创建的project同一个文件夹里面,方便训练的时候导入数据。用transforms准备两个数据处理器,一个用来处理训练集,一个用来处理测试集,两个处理器的配置不一样。transforms在处理图像的时候,可以对图像进行翻转,旋转,缩放,裁剪等操作,所以可以用来对训练集进行数据增强,防止过拟合,而测试集不需要做数据增强,所以对两个数据集的处理器配置要不一样。对训练集本人加了翻转和旋转操作,当然读者也可以做别的数据增强,具体的可以去看transforms的使用。

TrainTransforms = transforms.Compose([transforms.RandomHorizontalFlip(p=0.5),  # 以0.5的概率随机翻转
                                      transforms.RandomRotation((-10, 10)),  # 在(-10,10)度的范围内旋转
                                      transforms.Grayscale(num_output_channels=1),  # 参数为1表示转换为灰度图像
                                      transforms.ToTensor(),  # 把图像转化为张量
                                      transforms.Normalize(0.5, 0.5)])  # 归一化,参数为均值和方差

TestTransforms = transforms.Compose([transforms.Grayscale(num_output_channels=1),  
                                     transforms.ToTensor(),
                                     transforms.Normalize(0.5, 0.5)])

配置好了数据处理器之后,接下来就是要加载数据集,这里我们要用到DataLoader模块。但是在加载数据集之前,我们还有一个工作,我们下载下来的图片都是没有标签的,不像手写数字集MINST那样每张图片都进行了标注,这里只是按照不同的类别将人脸表情图像放在了不同的文件夹中而已。所以,我们在加载数据集之前必须要对图片进行标注。这里有一个很方便的模块叫做ImageFolder,可以按照文件夹顺序给文件夹中的每张图片进行标注,例如angry中的每张图片都标注为0,disgust中的图片都为1。

给数据打好标签之后,我们把它放入DataLoader,训练过程中,我们采用小批量随机梯度下降算法,将batch_size设置为64,shuffle=True,每次随机抽取64张图片参与训练。

train_dir = './train'  # “.”表示当前目录
test_dir = './test'

TrainData = ImageFolder(train_dir, transform=TrainTransforms)  # ImageFolder把图片按照文件夹分类,可以自动打标签
TestData = ImageFolder(test_dir, transform=TestTransforms)

TrainLoad = DataLoader(TrainData, batch_size=64, shuffle=True, pin_memory=True)  # pin_memory:如果设置为True,
                                                                                 # 那么data loader将会在返回它们之前,将tensors拷贝到CUDA中的固定内存
TestLoad = DataLoader(TestData, batch_size=64, shuffle=True, pin_memory=True)

数据准备工作就绪,接下来开始搭建模型。首先是FNN前馈神经网络,也就是多隐层全连接神经网络。输入数据是64X(48*48)的二维张量,所以在设置模型超参数时,维度要对应起来。在设置超参数的时候,除了第一个隐藏层的输入维度要设置为48*48,最后一个隐藏层的输出要设置维7(七个类别)之外,其他的超参数都可以自由设置,读者也可以自己更改网络层数。

class Model(torch.nn.Module):
    def __init__(self):
        super(Model, self).__init__()  # 所有继承自torch.nn.Module的类都需要写这个初始化方法
        self.Linear1 = torch.nn.Linear(48 * 48, 512)  # 注意这个L是大写
        self.Linear2 = torch.nn.Linear(512, 64)
        self.Linear3 = torch.nn.Linear(64, 7)
        self.Activate = torch.nn.ReLU()  # ReLu在神经网络中相当于一个通用的激活函数

    def forward(self, x):  # 前馈神经网络中方法名必须为forward,不能更改
        x = x.view(x.size(0), -1)  # 把输入数据转化为64行,自动获取列数,因为x.size的第0维是64
        x = self.Activate(self.Linear1(x))
        x = self.Activate(self.Linear2(x))
        x = self.Linear3(x)
        return x  # 用交叉熵损失函数就不需要softmax了,因为交叉熵会自动做softmax处理

下一步工作就是学习准则和优化算法的选择了,学习准则这里采用经验风险最小化准则,优化算法为小批量随机梯度下降算法,这里我们选用pytorch中的Adam优化器。在进行训练之前,我们把数据迁移至GPU进行加速训练。

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = Model().to(device)  # 把模型迁移至GPU
criterion = torch.nn.CrossEntropyLoss()  # 多分类用交叉熵损失函数
optimizer = torch.optim.Adam(model.parameters())  # 也可以用别的优化器对比效果

一切准备就绪,开始训练。通过DataLoader返回的数据是一个包含两个数据的列表,第一个数据是图片张量,第二个数据是标签,所以可以直接用image和label获取数据。

def Train():
    run_loss = 0
    total = 0
    for image, label in TrainLoad:
        image = image.to(device)  # 迁移至GPU
        label = label.to(device)
        optimizer.zero_grad()  # 梯度清零
        pred = model(image)  # 预测
        loss = criterion(pred, label)  # 计算损失
        run_loss += loss.item()  # loss是一个张量。取值要用item
        total += label.size(0)
        loss.backward()  # 反向传播
        optimizer.step()  # 更新梯度
    print(run_loss/total)  # 输出每个样本上的损失值

每训练一轮,我们在测试集上进行一次测试。测试过程中不需要使用梯度,所以要设置no_grad()。

def Test():
    total = 0
    correct = 0
    with torch.no_grad():
        for image, label in TestLoad:
            image = image.to(device)
            label = label.to(device)
            pred = model(image)
            _, predict = torch.max(pred.data, dim=1)  # torch.max返回张量中的最大值和索引,dim=1按照行计算,不需要使用的值用下划线储存
            total += label.size(0)  # size返回label的行数和列数,0表示行数
            number = (predict == label).sum().item()  # sum对64维的布尔型张量求值得到一个只有一个值的张量,item把这个张量转换为int
    print("accuracy on test is %d %%" % (100*correct/total))

最后添加程序入口,设置训练轮数,项目完成!

if __name__ == '__main__':
    for i in range(10):  # 训练10轮
        Train()
        Test()

下面是CNN(卷积神经网络)的代码,直接附上。CNN的优势在于可以保存图像的空间信息,其关键在于要会计算每一次卷积和池化之后的维度。如果不会计算,那你或许需要再去巩固一下CNN的理论知识,也可以去B站看一下Pytorch框架的视频,这里推荐刘二大人,直接看卷积神经网络。

LeNet-5卷积神经网络。

import torch
from torch.utils.data import DataLoader
from torchvision.datasets import ImageFolder
from torchvision import transforms

train_dir = './train'
test_dir = './test'

TrainTransforms = transforms.Compose([transforms.RandomHorizontalFlip(p=0.5),
                                      transforms.RandomRotation(10),  # 旋转角度为(-10,10)
                                      transforms.Grayscale(num_output_channels=1),  # 转化为灰度图像
                                      transforms.ToTensor(),
                                      transforms.Normalize(0.5, 0.5)])

TestTransforms = transforms.Compose([transforms.Grayscale(num_output_channels=1),
                                     transforms.ToTensor(),
                                     transforms.Normalize(0.5, 0.5)])

TrainData = ImageFolder(train_dir, transform=TrainTransforms)
TestData = ImageFolder(test_dir, transform=TestTransforms)

TrainLoader = DataLoader(TrainData, batch_size=64, shuffle=True, pin_memory=True)
TestLoader = DataLoader(TestData, batch_size=64, shuffle=True, pin_memory=True)


class Model(torch.nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.Con1 = torch.nn.Conv2d(1, 10, kernel_size=5, stride=1)  # Conv2d是指定大小的二维卷积核,Conv1d是一维
        self.Con2 = torch.nn.Conv2d(10, 20, kernel_size=5, stride=1)
        self.Con3 = torch.nn.Conv2d(20, 10, kernel_size=5, stride=1)
        self.Pooling = torch.nn.MaxPool2d(2)  # Maxpool2d(二维池化),Maxpool1d(一维池化)
        self.Activate = torch.nn.ReLU()
        self.fc = torch.nn.Linear(250, 7)

    def forward(self, x):
        batch_size = x.size(0)
        x = self.Activate(self.Pooling(self.Con1(x)))
        x = self.Activate(self.Pooling(self.Con2(x)))
        x = self.Activate(self.Con3(x))  # 注意这里的维度变成了64*250
        x = x.view(batch_size, -1)  # 注意全连接层的维度
        return self.fc(x)


device = ('cuda' if torch.cuda.is_available() else 'cpu')
model = Model().to(device)  # 把模型迁移到GPU
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)


def Train():
    run_loss = 0
    count_labels = 0
    for images, labels in TrainLoader:
        images = images.to(device)
        labels = labels.to(device)
        predict = model(images)
        loss = criterion(predict, labels)
        count_labels += labels.size(0)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        run_loss += loss.item()
    print(run_loss/count_labels)


def Test():
    total = 0
    correct = 0
    with torch.no_grad():  # 不要漏掉这里的括号
        for images, labels in TestLoader:
            images = images.to(device)
            labels = labels.to(device)
            pred = model(images)
            _, predict = torch.max(pred.data, dim=1)
            correct += (predict == labels).sum().item()
            total += labels.size(0)  # size(0)是什么意思
    print('accuracy on test is %d %%' % (100*correct/total))


if __name__ == '__main__':
    for epoch in range(20):
        Train()
        Test()

Inception卷积神经网络。

import torch
from torch.utils.data import DataLoader
from torchvision.datasets import ImageFolder
from torchvision import transforms
import torch.nn.functional as F

train_dir = './train'
test_dir = './test'

TrainTransforms = transforms.Compose([transforms.RandomHorizontalFlip(p=0.5),
                                      transforms.RandomRotation(10),  # 旋转角度为(-10,10)
                                      transforms.Grayscale(num_output_channels=1),  # 转化为灰度图像
                                      transforms.ToTensor(),
                                      transforms.Normalize(0.5, 0.5)])

TestTransforms = transforms.Compose([transforms.Grayscale(num_output_channels=1),
                                     transforms.ToTensor(),
                                     transforms.Normalize(0.5, 0.5)])

TrainData = ImageFolder(train_dir, transform=TrainTransforms)
TestData = ImageFolder(test_dir, transform=TestTransforms)

TrainLoader = DataLoader(TrainData, batch_size=64, shuffle=True, pin_memory=True)
TestLoader = DataLoader(TestData, batch_size=64, shuffle=True, pin_memory=True)


class InceptionA(torch.nn.Module):
    def __init__(self, in_channels):
        super(InceptionA, self).__init__()
        self.branch1x1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)

        self.branch5x5_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)
        self.branch5x5_2 = torch.nn.Conv2d(16, 24, kernel_size=5, padding=2)

        self.branch3x3_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)
        self.branch3x3_2 = torch.nn.Conv2d(16, 24, kernel_size=3, padding=1)
        self.branch3x3_3 = torch.nn.Conv2d(24, 24, kernel_size=3, padding=1)

        self.branch_pool = torch.nn.Conv2d(in_channels, 24, kernel_size=1)

    def forward(self, x):
        branch1x1 = self.branch1x1(x)

        branch5x5 = self.branch5x5_1(x)
        branch5x5 = self.branch5x5_2(branch5x5)

        branch3x3 = self.branch3x3_1(x)
        branch3x3 = self.branch3x3_2(branch3x3)
        branch3x3 = self.branch3x3_3(branch3x3)

        branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
        branch_pool = self.branch_pool(branch_pool)

        outputs = [branch1x1, branch5x5, branch3x3, branch_pool]
        return torch.cat(outputs, dim=1)  # b,c,w,h  c对应的是dim=1


class Model(torch.nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.Con1 = torch.nn.Conv2d(1, 10, kernel_size=5, stride=1)
        self.Con2 = torch.nn.Conv2d(88, 20, kernel_size=5, stride=1)
        self.Con3 = torch.nn.Conv2d(88, 10, kernel_size=5, stride=1)
        self.incep1 = InceptionA(in_channels=10)
        self.incep2 = InceptionA(in_channels=20)
        self.Pooling = torch.nn.MaxPool2d(2)
        self.Activate = torch.nn.ReLU()
        self.fc = torch.nn.Linear(250, 7)

    def forward(self, x):
        batch_size = x.size(0)
        x = self.Activate(self.Pooling(self.Con1(x)))
        x = self.incep1(x)
        x = self.Activate(self.Pooling(self.Con2(x)))
        x = self.incep2(x)
        x = self.Activate(self.Con3(x))  # 注意这里的维度变成了64*250
        x = x.view(batch_size, -1)  # 注意全连接层的维度
        return self.fc(x)


device = ('cuda' if torch.cuda.is_available() else 'cpu')
model = Model().to(device)  # 把模型迁移到GPU
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)


def Train():
    run_loss = 0
    count_labels = 0
    for images, labels in TrainLoader:
        images = images.to(device)
        labels = labels.to(device)
        predict = model(images)
        loss = criterion(predict, labels)
        count_labels += labels.size(0)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        run_loss += loss.item()
    print(run_loss/count_labels)


def Test():
    total = 0
    correct = 0
    with torch.no_grad():  # 不要漏掉这里的括号
        for images, labels in TestLoader:
            images = images.to(device)
            labels = labels.to(device)
            pred = model(images)
            _, predict = torch.max(pred.data, dim=1)
            correct += (predict == labels).sum().item()
            total += labels.size(0)  # size(0)是什么意思
    print('accuracy on test is %d %%' % (100*correct/total))


if __name__ == '__main__':
    for epoch in range(20):
        Train()
        Test()

最后总结:在训练完成之后,卷积神经网络比前馈神经网络的精度提高了15%以上。说明卷积神经网络在处理图像方面确实比前馈神经网络要好。模型训练之后的总体精度不是很高,分析其原因在于:1.数据集中有很多不同分类的图像,其本身差异不大,导致模型无法正确分类;2.训练样本数据太少,导致模型过拟合,这个问题可以通过数据增强等方法解决。

最后,感谢大家的阅读,如果觉得我讲的明白,还请大家收藏加关注,感激不尽!(代码后续也会在github进行开源,敬请关注)文章来源地址https://www.toymoban.com/news/detail-447815.html

到了这里,关于FER2013人脸表情识别从零实现(Pytorch,FNN,CNN)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Pytorch】基于卷积神经网络实现的面部表情识别

    作者:何翔 学院:计算机学院 学号:04191315 班级:软件1903 转载请标注本文链接: https://blog.csdn.net/HXBest/article/details/121981276 面部表情识别 (Facial Expression Recognition ) 在日常工作和生活中,人们情感的表达方式主要有:语言、声音、肢体行为(如手势)、以及面部表情等。在这

    2024年02月04日
    浏览(78)
  • Pytorch实现基于深度学习的面部表情识别(最新,非常详细)

    基于深度学习的面部表情识别 (Facial-expression Recognition) 数据集cnn_train.csv包含人类面部表情的图片的label和feature。 在这里,面部表情识别相当于一个分类问题,共有7个类别。 其中label包括7种类型表情: 一共有28709个label,即包含28709张表情包。 每一行就是一张表情包4848=2304个

    2024年02月04日
    浏览(81)
  • 【Python+百度API】实现人脸识别和颜值检测系统(包括人脸数量、年龄、颜值评分、性别、种族、表情检测)(超详细 附源码)

    需要源码请点赞关注收藏后评论区留下QQ~~~ 本系统根据已有的模型上改写,添加了在给照片打分的同时可以显示照片,这样显得更加直观和真实 如需要请自行前往官网查询文档相关内容 百度API官网 部分内容如下 接口能力 人脸检测 :检测图片中的人脸并标记出位置信息。

    2024年02月10日
    浏览(45)
  • 表情识别-情感分析-人脸识别(代码+教程)

    面部情绪识别(FER)是指根据面部表情识别和分类人类情绪的过程。通过分析面部特征和模式,机器可以对一个人的情绪状态作出有根据的推断。这个面部识别的子领域高度跨学科,涉及计算机视觉、机器学习和心理学等领域的知识。 以下是一些关键领域,其中这项技术可能

    2024年02月09日
    浏览(42)
  • 【人脸识别】基于facenet_pytorch实现人脸识别

            该代码可以在Pycharm、Jupyter、Python等Python编译器中运行,本文我使用的是Pycharm。 (一)安装pytorch(cpu)         首先在Pycharm中新建一个名为facenet_test的项目,并创建一个名为fp.py的Python文件。         打开PyTorch官网Start Locally | PyTorch,在主页中根据自己的电

    2024年02月04日
    浏览(48)
  • 基于人脸表情的情绪识别

    作为一个在人脸识别领域有一定经验的人,我在过去的项目中涉及了很多与人脸识别相关的任务。我整理了一些项目,但由于学业繁重,整理这些内容一直是断断续续的。最近正值五一假期,我想利用这个时间来整理一篇关于人脸表情识别的文章。这项技术可以识别出人的基

    2024年02月12日
    浏览(47)
  • 人脸表情识别数据集:CK+

    CK+有123的子类(人),每个人提供1~7种表情,每种表情是一组序列(总共有593个图像序列,其中327个序列是有表情标签的):从自然缓慢变化到给定表情 比如上图是人S026做出的某类表情,第一张是自然,缓慢变化到最终是峰值快乐表情 标签 593个序列中只有327个是具有情感序

    2024年02月06日
    浏览(37)
  • CNN实现手写数字识别(Pytorch)

    CNN(卷积神经网络)主要包括卷积层、池化层和全连接层。输入数据经过多个卷积层和池化层提取图片信息后,最后经过若干个全连接层获得最终的输出。 CNN的实现主要包括以下步骤: 数据加载与预处理 模型搭建 定义损失函数、优化器 模型训练 模型测试 以下基于Pytorch框

    2024年02月03日
    浏览(96)
  • 利用PYTHON编写人脸表情识别系统

    最近闲来无事,和一个学妹完成了一个SRT,主要是关于元宇宙什么的,不过我在其中主要的工作是用python写一个人脸识别系统,发到这里和大家分享一下 注:我利用了几个包,包括opencv,dlib,numpy等,所有包都会显示在代码开头import后 第一步,利用PyCharm先做灰度图 想要识别

    2024年02月08日
    浏览(51)
  • 毕业设计-基于深度学习的人脸表情识别

    目录 前言 课题背景和意义 实现技术思路 一、深度学习理论基础  二、AdaBoost 结合 SVM 算法表情识别  三、基于 MTCNN 算法的人脸表情识别 实现效果图样例 最后     📅大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计

    2024年02月01日
    浏览(79)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包