【北京大学】15 TensorFlow1.x的项目实战之人脸表情识别

1 概述

任务目标:人脸表情识别
数据来源:kaggle
模型设计:Lenet7和CliqueNet
度量标准:准确率
实验分类:8类(愤怒 恶心 害怕 快乐 悲伤 惊讶 蔑视 面无表情)

2 数据集

2.1 数据集下载

由Microsoft对kaggle提出的数据集进行重新标注的数据集
数据集下载

2.2 数据规模

训练集:测试集:验证集 = 8:1:1
1P = 48 * 48 = 2304像素
在这里插入图片描述

2.3 数据格式

-csv -3个属性:Label Pixels Usage
实际内容:人脸(灰度)
8中表情:愤怒 恶心 害怕 快乐 悲伤 惊讶 蔑视 面无表情(No Expression)
面部基本居中,大小相似
预处理数据下载
在这里插入图片描述

3 模型说明

3.1 Lenet7

在这里插入图片描述

Lenet7原来的网络使用两层卷积层和两层池化层,在实践中增大了模型深度,更好的适应当前数据特征
增加一个卷积和池化层
在这里插入图片描述

增加一层全连接层。
在这里插入图片描述

作者虽然实现了LEnet7的网络进行了测试,对比CliqueNet后,后者的效果更好,这里就没有详细介绍Lenet5以及它的源码。以下都是介绍CliqueNet的。

3.2 CliqueNet

(1)简介
由DenseNet启发
各Layer间双向连接
stage1: 浅层–>高层特征
stage2: 近层更新远层
在这里插入图片描述

(2)网络结构
本项目中由于数据集复杂程度不高,使用l三个block,每个block提取特征进行预测。浅层提取到了细节特征,深层提取到了全局信息。
此外在网络中使用Transition模块,使用channel-wise attention来给不同的channel赋予不同的权重。
在这里插入图片描述

4 代码分析

以下只分析关cliquenet.py文件中关键代码,其余代码请下载完整的参考。

4.1 cliquenet.py中forward函数

其中block模块主要对一维和高维图像特征进行提取
transition模块主要通过channel-wise attention机制对cannel的结构进行优化,以使得channel都获得所有维度的特征信息,有益于后面过程的学习。

# 定义了cliquenet的前向传播函数
def forward(x, train=True, regularizer=None):
    # 得到之后进行卷积的卷积核张量,输入channel为1,输出channel为64,kernel size为3
    w = get_weight([3, 3, 1, 64], 0.1, regularizer)
    # 先进行一次卷积,步长为2,使得feature map维度减半,使得模型提取到浅层特征
    x = conv2d(x, w, 2) 
    # 对数据进行批归一化,加快网络训练,减轻梯度消失
    x = bn(x, train)
    # 使用ReLU激活函数使得模型非线性
    x = tf.nn.relu(x)
    # 对数据进行最大池化,使得feature map的维度减半
    x = tf.nn.max_pool(x, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME')
    
    ## block1
    # 将数据送入clique block,block内每一个结点的channel数设为36,一个block内除输入结点外有5个结点
    x, feature1 = clique_block(x, regularizer, 64, 36, 3, 5, train)
    # 对第一个block提取到的特征信息进行global pooling
    feature1 = tf.nn.avg_pool(feature1, ksize=[1, 12, 12, 1], strides=[1, 12, 12, 1], padding='SAME')
    # 将上一层的五个更新后结点作为输入,经过transition调整feature map的维度,并利用attention强化特征信息更优的channel,使得下一个block可以更好地利用前层特征信息
    x = transition(x, regularizer, 180, 180, 12, train)
   
    ## block 2
    # 将数据送入clique block,block内每一个结点的channel数设为36,一个block内除输入结点外有5个结点
    x, feature2 = clique_block(x, regularizer, 180, 36, 3, 5, train)
    # 对第二个block提取到的特征信息进行global pooling
    feature2 = tf.nn.avg_pool(feature2, ksize=[1, 6, 6, 1], strides=[1, 6, 6, 1], padding='SAME')
    # 将上一层的五个更新后结点作为输入,经过transition调整feature map的维度,并利用attention强化特征信息更优的channel,使得下一个block可以更好地利用前层特征信息
    x = transition(x, regularizer, 180, 180, 6, train)
    
    ## block3
    # 将数据送入clique block,block内每一个结点的channel数设为36,一个block内除输入结点外有5个结点
    _, feature3 = clique_block(x, regularizer, 180, 36, 3, 5, train)
    # 对第三个block提取到的特征信息进行global pooling
    feature3 = tf.nn.avg_pool(feature3, ksize=[1, 3, 3, 1], strides=[1, 3, 3, 1], padding='SAME')
    # 将不同层间获取的不同特征进行融合,浅层的特征包含更多的细节信息,而深层特征则包含更多的全局信息
    out = tf.concat([feature1, feature2, feature3], axis=3)
    # 将张量转换为一个长向量,以用于之后的全连接层
    x = tf.reshape(out, [-1, 964])
    # 将向量输入全连接层,从而输出对每一个分类的预测分数
    x = fc(x, 964, 7, 0.01, regularizer)
    # 返回该神经网络的预测结果
    return x

4.2 cliquenet.py中stage1和stage2函数

stage1的公式
代码结构是
layer1 = encoding(input)
layer2 =encoding(layer1,input)
layer3 = encoding(layer2,input)

layer5 = encoding(layer4,input)
因此layer5中存在提取的高维提取特征,并且有很多重复的获取的低维特征。以使得低维和高维的特征共同被利用,加强了information flow,能有效避免梯度消失。

# 定义了stage1阶段
def stage1(x0, w0, w, in_channel, filters, layers=5, train=True):
    # 使用输入结点对每一个之外的结点进行初始化
    for i in range(layers):
        # 如果是第一个结点
        if i == 0:
            # 就取w0中的第一个张量作为连接输入结点与第一个结点的卷积核
            weight = w0[i]
            # 第一个结点在stage1只与输入结点相连接
            data = x0
        # 如果不是第一个结点
        else:
            # 就取w0中对应的结点以及w中对应的结点拼成之后使用的卷积核
            weight = tf.concat([w0[i]] + [w[4 * num + i - 1] for num in range(i)], axis=2)
        # 使用拼出的卷积核与前面的结点生成一个新的结点
        x = conv2d(data, weight)
        # 对数据进行批归一化,加快网络训练,减轻梯度消失
        x = bn(x, train)
        # 使用ReLU激活函数使得模型非线性
        x = tf.nn.relu(x)
        # 使用dropout随机使部分结点归零,从而使得模型不易过拟合
        x = dropout(x, train)
        # 将已经生成的结点concat在一起用于生成下一个结点
        data = tf.concat([data, x], axis=3)
    # 除去输入结点与第一个结点(第一个结点在stage2的第一步即需要被更新,因此不需要传入下一阶段)
    _, x = tf.split(data, [in_channel + filters, filters * (layers - 1)], axis=3)
    # 将得到的后面若干个结点传入stage2
    return x

stage2:迭代更新layers
layer1 = encoding(layer2~5)
layer2 =encoding(layer1,3,4,5)
layer3 = encoding(layer1,2,4,5)

...
layer5 = encoding(layer1,2,3,4)
# 定义了stage2模块
def stage2(x, w, in_channel, filters, layers=5, train=True):
    # 对block内每一个结点进行更新
    for i in range(layers):
        # 取w中对应的结点拼成卷积核
        weight = tf.concat([w[4 * num + i - 1] for num in range(i)] + [w[4 * num + i] for num in range(i + 1, layers)], axis=2)
        # 使用拼出的卷积核与最近更新的结点更新最早的结点
        data = conv2d(x, weight)
        # 对数据进行批归一化,加快网络训练,减轻梯度消失
        data = bn(data, train)
        # 使用ReLU激活函数使得模型非线性
        data = tf.nn.relu(data)
        # 使用dropout随机使部分结点归零,从而使得模型不易过拟合
        data = dropout(data, train)
        # 若不为更新最后一个结点
        if i != layers - 1:
            # 则将更新最早的结点去除
            _, x = tf.split(x, [filters, filters * (layers - 2)], axis=3)
        # 将最新更新的结点同其他结点concat在一起,对于非最后结点相当于替换最早更新的结点
        x = tf.concat([data, x], axis=3)
    # 返回经过stage2后的所有经过更新的结点
    return x

4.3 cliquenet.py中transition函数

将layer在channel层面做attention,使得整个layer都学习到上下文的信息。

# 定义了在多个block之间的转换模块
def transition(x, regularizer, in_channel, out_channel, size, train, use_attention=True):
    # 得到之后进行的1✖1卷积的卷积核张量,输入channel与输出channel为给定值,在该网络中由于较浅,因此暂不使用compression机制,输入channel与输出channel数相等
    w = get_weight([1, 1, in_channel, out_channel], 0.1, regularizer)
    # 根据上述卷积核进行卷积,步长为1,使得feature map维度不变
    x = conv2d(x, w, 1)
    # 对数据进行批归一化,加快网络训练,减轻梯度消失
    x = bn(x, train)
    # 使用ReLU激活函数使得模型非线性
    x = tf.nn.relu(x)
    # 使用dropout随机使部分结点归零,从而使得模型不易过拟合
    x = dropout(x, train)
    # 选择是否使用attention机制,这里采用了channel-wise的attention机制来在转化过程赋予不同channel不同权重,从而使得下一个block的学习效果更好
    if use_attention:
        # 对数据进行global pooling,从而对于对于每一个channel得到一个值
        attention = tf.nn.avg_pool(x, ksize=[1, size, size, 1], strides=[1, size, size, 1], padding='SAME')
        # 将得到的每一个张量转换为一个长度为channel数的长向量
        attention = tf.reshape(attention, [-1, out_channel])
        # 将获得的权重输入一个全连接层,从而对权重进行学习
        attention = fc(attention, out_channel, out_channel // 2, 0.01, regularizer)
        # 使用ReLU激活函数使得attention的学习模块非线性
        attention = tf.nn.relu(attention)
        # 将获得的权重输入一个全连接层,从而对权重进行更深的学习
        attention = fc(attention, out_channel // 2, out_channel, 0.01, regularizer)
        # 使用sigmoid激活函数使得学习到的权重处于0-1的范围内
        attention = tf.sigmoid(attention)
        # 增大张量维度以方便数据与attention的权重的相乘
        attention = tf.expand_dims(attention, 1)
        # 增大张量维度以方便数据与attention的权重的相乘
        attention = tf.expand_dims(attention, 1)
        # 将学习到的channel-wise attention权重分别乘以各自的channel
        x = tf.multiply(x, attention)
    # 对数据进行最大池化,使得feature map的维度减半,使得下一个block可以学习到更深层的信息
    x = tf.nn.avg_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    # 返回经过transition转换后的数据,用于传入下一个block
    return x

5 结果分析

5.1 使用cliqueNet网络优点

增强information flow
减少内存
加速训练
避免网络退化
保证实时性

5.2 参数优化

进行了数据增强
使用了Validation调整优化器与超参数
比较了Adam、Momentum、SGD优化器,其中Momentum收敛较慢,SGD容易梯度消失。最终选择Adam优化器
Dropout = 0.95

5.3 实验环境

操作系统:Ubuntu 16.04LTS
GPU:GTX 1080TI
Python版本: Python3.5
TensorFlow版本:1.11.0

5.4 模型效果

fer2013 Rank最好成绩是0.71。以下是实验结果,最高准确率只有0.653.
在这里插入图片描述

真实数据测试如下,比如周星驰图片中的泪光不够明显,影响了模型的预测结果,得出结论:如何增加标志性特征的对比度是非常重要的。
在这里插入图片描述

以下是表情包的测试,图片中没有一个标准的人脸形状时,模型的准确率也不高。
在这里插入图片描述

5.5 总结

通过预训练的方式来找到较好的收敛特征。fine-tune:增光数据集上学习率较大的coarse预训练
使用图像分割的方式,矫正面部位置

6 源码下载

项目源码下载

相关笔记

以下所有源码以及更详细PDF笔记请在github下载
TensorFolwNotebook-from-Peking-University

  1. 【北京大学】1 TensorFlow1.x中Python基础知识
  2. 【北京大学】2 TensorFlow1.x的张量、计算图、会话
  3. 【北京大学】3 TensorFlow1.x的前向传播推导与实现
  4. 【北京大学】4 TensorFlow1.x的反向传播推导与实现
  5. 【北京大学】5 TensorFlow1.x的损失函数和交叉熵举例讲解及实现
  6. 【北京大学】6 TensorFlow1.x的学习率、滑动平均和正则化实例及实现
  7. 【北京大学】7 TensorFlow1.x的神经网络模块设计思想举例及实现
  8. 【北京大学】8 TensorFlow1.x的Mnist数据集实例实现
  9. 【北京大学】9 TensorFlow1.x的实现自定义Mnist数据集
  10. 【北京大学】10 TensorFlow1.x的卷积神经网络(CNN)相关基础知识
  11. 【北京大学】11 TensorFlow1.x的卷积神经网络模型Lenet5实现
  12. 【北京大学】12 TensorFlow1.x的卷积神经网络模型VGGNet实现
  13. 【北京大学】13 TensorFlow1.x的项目实战之手写英文体识别OCR技术
  14. 【北京大学】14 TensorFlow1.x的二值神经网络实现MNIST数据集手写数字识别
  15. 【北京大学】15 TensorFlow1.x的项目实战之人脸表情识别
  16. 【北京大学】16 TensorFlow1.x的项目实战之图像风格融合与快速迁移
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Better Bench

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值