导 论 :Hello 各位小伙伴,今天给大家带来零基础入门深度学习第一个小项目—情绪识别。本订阅号作为科普类的公众号,目的就是科普人工智能,无论什么专业只需要掌握了高中数学基础与简单编程就能入门人工智能。好了,接下来开始我们今天的第一个小项目—计算机视觉之情绪检测。
本小项目是非常经典的,会了这个项目也就掌握了卷积神经网络的使用。之后自己举一反三也就会人脸口罩识别、性别识别、动物识别、手写数字识别、手写字母识别、车牌识别等等各种图片的识别。所以本期第一个小项目是非常重要的,后期还会给大家介绍生成对抗网络、时间序列网络、强化学习等各种有趣的项目。
本期需要预先掌握的知识有Python程序设计、Pytorch深度学习框架基础。电脑需要安装Python3.6/3.7、pytorch。电脑有NVIDIA显卡最好,没有的话也可以使用CPU进行训练。
目前的流行的人工智能实现基本上都是基于深度学习的模型,往往是模型越大(参数就越多)其能力越大,例如自然语言处理领域的GPT-3模型多达1750亿个参数。这类超大模型只可远观不可亵玩焉,甚至我们把训练好的GPT-3(350多G)下载下来也无法运行。这类模型往往功能非常强大,例如可以作词作诗、翻译、对对联等等。但是这些大模型也同样经不起图灵测试(人类和它交互很容易发现它就是机器人),有兴趣的同学可以去试玩百度的文心大模型(https://wenxin .baidu.com/wenxin/ernie-vilg),该模型参数超过2000亿,如下面图1是通过诗句机器自动作画的应用。
其实实现一些功能并不需要如此庞大的神经网络,一些具体的问题其实只需要很小的网络就可以完成。什么是人工神经网络?这里读者需要知道神经网络的概念。其实神经网络就是一个能够拟合任何函数的一个函数,神经网络就是一个function。那么我们需要做的就是通过训练集找到能够识别情绪图片的神经网络参数。目前如果对神经网络及其优化还不了解的小伙伴可以将神经网络理解为一个魔法盒,我们只需要告诉盒子哪些图片是悲伤的,哪些图片是快乐的,哪些图片是惊讶的,哪些图片是愤怒的就可以。这些图片我们称之为训练集,当网络模型学完训练集后就基本知道快乐、悲伤等的表情大概具有什么样的特征。之后我们给一张网络模型从未见过的情绪图片给它,它依然可以做到准确识别。如果想具体了解理论的小伙伴可以等我后期出关于理论部分的介绍后再阅读。
情绪检测
情绪检测或表情分类在深度学习领域中有着广泛的研究。使用电脑摄像头和一些简单的代码我们就可以对情绪进行实时分类。 需要预先掌握的知识:
• Python基础
• OpenCV基础
• 卷积神经网络(CNN)
• pytorch基础
训练集: 本文共使用28000张人脸情绪图片作为训练样本,类别分为愤怒、害怕、高兴、正常、悲伤、惊讶6个类。
测试集: 本文共使用3500张人脸情绪图片作为测试样本,用来测试神经网络模型的效果。
该数据集属于公开数据集,约3w张分好类的人脸灰度图。其数据类别分布不均衡,所以对于某些类别的预测不准确。数据集公众号回复:emotion_data 即可下载
定义网络模型
本文使用卷积神经网络,代码如下:
# 定义卷积神经网络,这里使用5层的神经网络
import torch
import torch.nn as nn
class CNN5(nn.Module):
def __init__(self):
super(CNN5, self).__init__()
self.conv1 = nn.Conv2d(1, 64, kernel_size=(3, 3),padding=1)
self.relu = nn.ReLU()
self.maxpool1 = nn.MaxPool2d(kernel_size=(2, 2) )
self.conv2 = nn.Conv2d(64, 256, kernel_size=(3, 3))
self.maxpool2 = nn.MaxPool2d(kernel_size=(2, 2) )
self.fc1 = nn.Linear(256*7*7, 4096)
self.fc2 = nn.Linear(4096, 1024)
self.fc3 = nn.Linear(1024, 6)
def forward(self, img):
output = self.conv1(img)
output = self.relu(output)
output = self.maxpool1(output)
output = self.conv2(output)
output = self.relu(output)
output = self.maxpool2(output)
feature = output.view(-1, 256*7*7)
output = self.fc1(feature)
output = self.relu(output)
output = self.fc2(output)
output = self.relu(output)
output = self.fc3(output)
return output
读取数据集
定义好了卷积网络接着需要读取文件夹中的训练数据集,并进行数据增强、归一化等操作。代码如下:
def get_emotion_data(args):
#args参数封装,理解为参数字典
train_loader = torch.utils.data.DataLoader(
datasets.ImageFolder(args.data_root+'train',
transform=transforms.Compose([
transforms.Resize((32, 32)),
transforms.ToTensor(),
transforms.Grayscale(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=args.batch_size, shuffle=True, num_workers=2)
test_loader = torch.utils.data.DataLoader(
datasets..ImageFolder(args.data_root+'validation',
transform=transforms.Compose([
transforms.Resize((32, 32)),
transforms.ToTensor(),
transforms.Grayscale(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=args.batch_size, shuffle=True, num_workers=2)
return train_loader , test_loader
训练方法
def my_train(args, model, device, train_loader, optimizer, epoch):
model.train()
for idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.cross_entropy(output, target)
loss.backward()
optimizer.step()
if (idx+1) % 20 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, idx * len(data), len(train_loader.dataset),
100. * idx / len(train_loader), loss.item()))
测试方法
def my_test(args, model, device, test_loader, cur_epoch):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.cross_entropy(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nEpoch {} Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.4f}%)'.format(
cur_epoch, test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return correct/len(test_loader.dataset)
main方法
if __name__ == '__main__':
#封装参数
parser = argparse.ArgumentParser()
parser.add_argument('--data_root', type=str, default='Q:\cache\emotion\\')
parser.add_argument('--batch_size', type=int, default=128, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--lr', type=float, default=0.1, metavar='LR',
help='learning rate (default: 0.1)')
parser.add_argument('--weight_decay', type=float, default=5e-4)
parser.add_argument('--epochs',type=int,default=30)
parser.add_argument('--momentum', type=float, default=0.9, metavar='M',
help='SGD momentum (default: 0.9)')
args = parser.parse_args()
device = "cuda" if torch.cuda.is_available() else "cpu"
print('运行在{}上...'.format(device))
#实例化模型
model = LeNet5().to(device)
#定义优化器
optimizer = optim.SGD(model.parameters(), lr=args.lr, weight_decay=args.weight_decay, momentum=args.momentum)
#加载数据
train_loader,test_loader = get_emotion_data(args)
best_acc=0
for epoch in range(1, args.epochs + 1):
my_train(args, model, device, train_loader, optimizer, epoch)
acc = my_test(args, model, device, test_loader, epoch)
if acc>best_acc:
best_acc = acc
torch.save(model.state_dict(),"models/emotion.ckpt")
print("Best Acc=%.6f"%best_acc)
本文默认运行30个epoch,最后保存准确率最高为52.8%的模型。完整代码关注本公众号回复:"情绪检测代码"即可获得完整源码。这个准确率其实一言难尽,目前使用ResNet网络来训练准确率也只有60%左右,因为这个数据集的自身分类误差就很大,加之数据类别不均衡等原因导致识别难度大。这依旧是具有挑战性的研究,在CVPR、Kaggle等比赛中,人脸情绪识别依旧很火热。本文作为入门实验在这里不做深入探究。
使用表情检测模型
当我们训练完识别模型后我们可以用来检测视频中的人物表情或者摄像头拍摄到的人物表情,这里基于OpenCV来实现。其实我们用来训练的是人脸,所以首先我们需要检测出人脸,我们当然可以训练一个神经网络用来检测人脸,但是本期介绍主要介绍图片分类,所以检测人脸我们直接调用OpenCV的模型。OpenCV自带的人脸检测表现的非常糟糕,有兴趣的小伙伴有手就行可以轻松训练人脸检测的网络轻轻松松超越OpenCV自带的模型。该部分代码获取公众号回复"情绪检测"即可获取!
公众号:矩阵科学
版权归原作者 cheney-pro 所有, 如有侵权,请联系我们删除。