CNN for Face Alignment 深度神经网络的初次尝试

来源:互联网 发布:mac 查看php版本 编辑:程序博客网 时间:2024/06/18 00:56

在Face Alignment中,传统方法其实能够取得不错的效果。包括AAM,ASM,CLM还有之前说的基于全局显式回归的ESR,3000FPS等。

但是!传统算法在大姿态、极端表情上基本无能为力。在我的理解中,他们都是对于训练集的某些特征进行降维后记忆回归的结果(理解有错请指出)。嘻嘻,而且不去接触神经网络都要Out了!

背景
基于由俭入奢的原则,论文方面我们选取了CNN在人脸对齐方向比较前期的一篇论文CVPR _2013《Deep Convolutional Network Cascade for Facial Point Detection》。在实现环境方面,我们选择了社区比较成熟的而且背后大佬比较牛逼的Tensorflow 1.3.0

去网上搜罗了一些CNN基本知识,因为卷积的Gif实在是太生动了。也很容易理解CNN卷积层中W和b的含义。

模型

这里写图片描述

我们只挑选了其中的Level1来进行实现,所以代码量也比较少。作为入门使用也是可以的。

Leve1由三个CNN网络组成[F1,EN1,NM1]。F1预测了面部五个点的位置,EN1预测其中的双眼、鼻子,NM1预测鼻子、嘴角。最后整个Leve1的输出是三个网络输出的平均值。

根据论文表格,描述了所有网络的详细结构:
这里写图片描述

我们准备实现的F1为S0结构,EN1及NM1为S1结构。在论文中,作者使用非共享卷积层。对于每一个map,分为p x q的区域。如S0的Layer1:CR(4,20,2,2),CR表示使用Abs(tanh(x))作为激活函数,卷积核大小为4x4,输出20个通道map,每个map在10x10的区域内权值共享。

但由于tensorflow并不原生支持unshare weights,所以我们直接按照标准的共享权值CNN来实现,在精度上会有一定损失。

实现细节

基于最基础的思想,利用Tensorflow中已有的Api,我们来直接实现Level 1网络。训练集及测试集均可以在论文的官网下载到。

预处理

首先,按照论文的说法,我们对原始训练集进行数据增强。对原始图像进行镜像、少量随机旋转和平移然后抽取人脸检测包围盒中的图片,将其缩放至39x39备用。相比于利用API进行后续网络的搭建,反而感觉这一步是最麻烦的。哈哈哈

搭建模型
利用Tensorflow的高级layer函数,我们可以非常方便的搭建整个网络。需要用到的api:

tf.layers.conv2d #卷积层tf.layers.max_pooling2d #最大池化层 我们这里没有像论文一样给池化层增加权值及biastf.contrib.layers.flatten #展平数据tf.layers.dense #全连接层

在选择激活函数的时候,我们选择于论文一致的 tanh 来当做激活函数。一开始我直接使用Tensorflow自带的权值初始方法,发现收敛非常慢甚至于收敛不了。经过查资料和询问朋友发现,tanh 搭配 xavier 初始化有奇效。这点我目前还没有彻底理解,留后。资料:资料

注:后来发现为手误,初始的也能收敛。

So,我们现在搭一个CNN太简单了!

def NewS0(name):    with tf.name_scope('S0' + name):        x = tf.placeholder(tf.float32, shape=[None, 39 , 39,1])        y = tf.placeholder(tf.float32, shape=[None, 10])        xImage = tf.reshape(x,[-1, 39, 39, 1])        Conv1 = tf.abs(tf.layers.conv2d(inputs=xImage,filters=20,kernel_size=4,strides=1,activation=tf.nn.tanh))        Pool1 = tf.layers.max_pooling2d(inputs=Conv1,pool_size=2,strides=2,padding='same')        Conv2 = tf.abs(tf.layers.conv2d(inputs=Pool1,filters=40,kernel_size=3,strides=1,activation=tf.nn.tanh))        Pool2 = tf.layers.max_pooling2d(inputs=Conv2,pool_size=2,strides=2,padding='same')        Conv3 = tf.abs(tf.layers.conv2d(inputs=Pool2,filters=60,kernel_size=3,strides=1,activation=tf.nn.tanh))        Pool3 = tf.layers.max_pooling2d(inputs=Conv3,pool_size=2,strides=2,padding='same')        Conv4 = tf.abs(tf.layers.conv2d(inputs=Pool3,filters=80,kernel_size=2,strides=1,activation=tf.nn.tanh))        Pool3_Flat = tf.contrib.layers.flatten(Pool3)        Conv4_Flat = tf.contrib.layers.flatten(Conv4)        Concat = tf.concat([Pool3_Flat,Conv4_Flat],1)        Fc1 = tf.layers.dense(Concat,120,activation=tf.nn.tanh)        Fc2 = tf.layers.dense(Fc1,10,activation=tf.nn.tanh)        Cost = tf.reduce_mean(tf.square(Fc2 - y)) / 2.0        tf.summary.scalar(name + "/Cost",Cost)        Optimizer = tf.train.AdamOptimizer(1e-4).minimize(Cost)        return Optimizer,Fc2,x,y,Costdef NewS1(name,isEN1):    with tf.name_scope('S1' + name):        x = tf.placeholder(tf.float32, shape=[None, 39,39,1])        y = tf.placeholder(tf.float32, shape=[None, 10])        xImage = tf.reshape(x,[-1, 39, 39, 1])        if isEN1:            xImage = tf.slice(xImage,[0,0,0,0],[-1,31,39,1])            y_In = tf.slice(y,[0,0],[-1,6])        else:            xImage = tf.slice(xImage,[0,8,0,0],[-1,31,39,1])            y_In = tf.slice(y,[0,4],[-1,6])        Conv1 = tf.abs(tf.layers.conv2d(inputs=xImage,filters=20,kernel_size=4,strides=1,activation=tf.nn.tanh))        Pool1 = tf.layers.max_pooling2d(inputs=Conv1,pool_size=2,strides=2,padding='same')        Conv2 = tf.abs(tf.layers.conv2d(inputs=Pool1,filters=40,kernel_size=3,strides=1,activation=tf.nn.tanh))        Pool2 = tf.layers.max_pooling2d(inputs=Conv2,pool_size=2,strides=2,padding='same')        Conv3 = tf.abs(tf.layers.conv2d(inputs=Pool2,filters=60,kernel_size=3,strides=1,activation=tf.nn.tanh))        Pool3 = tf.layers.max_pooling2d(inputs=Conv3,pool_size=2,strides=2,padding='same')        Conv4 = tf.abs(tf.layers.conv2d(inputs=Pool3,filters=80,kernel_size=2,strides=1,activation=tf.nn.tanh))        Pool3_Flat = tf.contrib.layers.flatten(Pool3)        Conv4_Flat = tf.contrib.layers.flatten(Conv4)        Concat = tf.concat([Pool3_Flat,Conv4_Flat],1)        Fc1 = tf.layers.dense(Concat,100,activation=tf.nn.tanh)        Fc2 = tf.layers.dense(Fc1,6,activation=tf.nn.tanh)        Cost = tf.reduce_mean(tf.square(Fc2 - y_In)) / 2.0        tf.summary.scalar(name + "/Cost",Cost)        Optimizer = tf.train.AdamOptimizer(1e-4).minimize(Cost)        return Optimizer,Fc2,x,y,Cost

顺便一说,我并没有使用论文中的随机梯度下降来优化参数,因为。。实在太慢了。我使用了Tensorflow中提供的优化器Adam。

最后,在测试集上的双眼间距错误率为 4%,应该说我们的网络基本已经实现了。虽然精度可能没有论文中
这里写图片描述
那么好,但也差不多啦哈哈哈哈!OK,CNN的第一次应用就算完成了。接下来去解决后续论文了。

这里写图片描述

这里写图片描述