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

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

数据集准备好之后,接下来开始搭建模型。众所周知,深度学习分为模型,学习准则,优化算法三个模块,后面我会一一介绍。本文分别用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维是64x = 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 = 0total = 0for image, label in TrainLoad:image = image.to(device)  # 迁移至GPUlabel = label.to(device)optimizer.zero_grad()  # 梯度清零pred = model(image)  # 预测loss = criterion(pred, label)  # 计算损失run_loss += loss.item()  # loss是一个张量。取值要用itemtotal += label.size(0)loss.backward()  # 反向传播optimizer.step()  # 更新梯度print(run_loss/total)  # 输出每个样本上的损失值

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

def Test():total = 0correct = 0with 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把这个张量转换为intprint("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 transformstrain_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*250x = 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 = 0count_labels = 0for 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 = 0correct = 0with 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 Ftrain_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=1class 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*250x = 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 = 0count_labels = 0for 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 = 0correct = 0with 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进行开源,敬请关注)

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

  1. Kaggle ICML2013 fer2013人脸表情识别/面部表情识别:训练、调优、调试与踩坑

    目录 概要: 问题来源: 论文对此比赛的说明: 选择原因: 实现与优化思路: 前置: 数据处理: 原csv数据的读取与分割: csv数据转图片和tfrecord的存取: tfrecord接生产队列供模 ...

  2. 【项目实战课】AI零基础,人人免费可学!基于Pytorch的SimpleNet人脸表情识别实战...

    欢迎大家来到我们的项目实战课,本期内容是<基于SimpleNet的人脸表情识别实战>,本次内容面向任意AI基础的读者,人人皆可免费学习. 所谓项目实战课,就是以简单的原理回顾+详细的项目实 ...

  3. Fer2013 数据集人脸表情识别 详细代码

    Fer2013 数据集人脸表情识别 详细代码 本文将从数据集.模型训练.模型实践应用(AI模型落地场景实际应用)几个部分完整讲解基于Fer2013 数据集的人脸表情识别项目, 最终项目实现效果: 通过 ...

  4. 人脸表情识别 (1) 下载fer2013数据集和数据的处理

    最近做了有关人脸表情识别的实验,还是挺有意思的,所以想在博客上记录这个过程,既是给自己做个记录,也想把这个过程分享给大家,如果能对你有一些帮助我会感到挺开心的,模型,数据集之类的我都会陆续上传,有需要 ...

  5. 人脸表情识别系统(VGG_16和Xception模型)配置GPU加速,Fer2013数据集和CK+数据集,tensorboard

    编译器 python3.8 开发平台 Pycharm PyQT5 系统界面 (可视化开发工具designer ) 模型训练基于深度学习tensorflow框架 opencv haar cascade ...

  6. CNN表情识别系统制作(1)----fer2013人脸表情数据集简介

    fer2013人脸表情数据集简介 fer2013人脸表情数据集由35886张人脸表情图片组成,其中,测试图(Training)28708张,公共验证图(PublicTest)和私有验证图(Privat ...

  7. 高精度人脸表情识别(附GitHub地址)

    编者按:本文原作者吴捷,目前于中山大学就读研究生.研究领域为计算机视觉与自然语言处理.本文原载于知乎,经作者授权发布.欢迎去GitHub给大佬加星. 先放出GitHub地址: https://gith ...

  8. 基于堆栈二值化自动编码器和二值化神经的无约束人脸表情识别算法(An efficient unconstrained FERa based on BAEs and BNN)

    摘要(abstract) 虽然深度学习在许多模式识别任务中都取得了良好的效果,但对于含有大量参数集.标记数据有限的深度网络,过拟合问题仍然是一个严重的问题.在这项工作中,二进制自动编码器(BAEs)和 ...

  9. 【技术综述】人脸表情识别研究

    李振东 北京邮电大学硕士在读,计算机视觉方向 言有三 毕业于中国科学院,计算机视觉方向从业者,有三工作室等创始人 作者 | 李振东/言有三 编辑 | 言有三 随着机器学习和深度神经网络两个领域的迅速发 ...

最新文章

  1. Nature癌症“牵线木偶”理论:科学家找到了不易误伤健康细胞的“剪刀”
  2. Redis系列-远程连接redis并给redis加锁
  3. 概率论-3.2 边际分布与随机变量的独立性
  4. springboot 问题Change project compliance and JRE to 1.5
  5. entity framework5 sqlserver2005 事务(TransactionScope)报未启用MSDTC错误解决办法
  6. shell实战之tomcat看门狗
  7. xshell如何运行java_利用Xshell往应用服务器上部署项目
  8. Office Open XML学习(1)-创建excel文档,并向单元格中插入字符串
  9. jQuery中 trigger() 使用心得
  10. 【MySql】Navicat Premium 15 无限试用脚本
  11. lowB三人组---冒泡排序原理和实现
  12. Python 之CV2详解
  13. 8.字符串转整数(String to Integer)
  14. SIFT Flow 笔记
  15. python实现向图像随机添加高斯白噪声,并修改尺寸
  16. JAVA C~K的班级
  17. 智能运维案例系列 | 袋鼠云日志助力云南某金融机构日志平台建设,实现核心业务系统运维智能化...
  18. [智慧城市]Python实时城市路面积水面积检测(完整源码&UI界面&视频教程)
  19. python操作xlsx文件的资料
  20. 全国计算机一级上机考试试题,全国计算机等级考试一级上机WORD题(六)

热门文章

  1. excel 公式复制学习
  2. node.js域名解析
  3. 格式化的计算机数据就找不回来了,电脑被格式化了,硬盘文件恢复的回来吗?...
  4. 股票当日委托未成交第二天还会有效吗?
  5. 不一样的古典帅,高伟光喜获29届华鼎奖中国古装题材电视剧最佳男演员
  6. Element 日期控件禁用今天之前的日期(适用ele各种类型组件)
  7. ubuntu上的wife设置
  8. Docker打包应用部署
  9. html和css进阶小练习
  10. OfficeExcel(6)