天天看點

CNN for Face Alignment 深度神經網絡的初次嘗試

在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的含義。

模型

CNN for Face Alignment 深度神經網絡的初次嘗試

我們隻挑選了其中的Level1來進行實作,是以代碼量也比較少。作為入門使用也是可以的。

Leve1由三個CNN網絡組成[F1,EN1,NM1]。F1預測了面部五個點的位置,EN1預測其中的雙眼、鼻子,NM1預測鼻子、嘴角。最後整個Leve1的輸出是三個網絡輸出的平均值。

根據論文表格,描述了所有網絡的詳細結構:

CNN for Face Alignment 深度神經網絡的初次嘗試

我們準備實作的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 #最大池化層 我們這裡沒有像論文一樣給池化層增權重值及bias
tf.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,  , ,])
        y = tf.placeholder(tf.float32, shape=[None, ])

        xImage = tf.reshape(x,[-, , , ])

        Conv1 = tf.abs(tf.layers.conv2d(inputs=xImage,filters=,kernel_size=,strides=,activation=tf.nn.tanh))
        Pool1 = tf.layers.max_pooling2d(inputs=Conv1,pool_size=,strides=,padding='same')

        Conv2 = tf.abs(tf.layers.conv2d(inputs=Pool1,filters=,kernel_size=,strides=,activation=tf.nn.tanh))
        Pool2 = tf.layers.max_pooling2d(inputs=Conv2,pool_size=,strides=,padding='same')

        Conv3 = tf.abs(tf.layers.conv2d(inputs=Pool2,filters=,kernel_size=,strides=,activation=tf.nn.tanh))
        Pool3 = tf.layers.max_pooling2d(inputs=Conv3,pool_size=,strides=,padding='same')

        Conv4 = tf.abs(tf.layers.conv2d(inputs=Pool3,filters=,kernel_size=,strides=,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],)

        Fc1 = tf.layers.dense(Concat,,activation=tf.nn.tanh)
        Fc2 = tf.layers.dense(Fc1,,activation=tf.nn.tanh)

        Cost = tf.reduce_mean(tf.square(Fc2 - y)) / 
        tf.summary.scalar(name + "/Cost",Cost)

        Optimizer = tf.train.AdamOptimizer().minimize(Cost)
        return Optimizer,Fc2,x,y,Cost

def NewS1(name,isEN1):
    with tf.name_scope('S1' + name):
        x = tf.placeholder(tf.float32, shape=[None, ,,])
        y = tf.placeholder(tf.float32, shape=[None, ])

        xImage = tf.reshape(x,[-, , , ])
        if isEN1:
            xImage = tf.slice(xImage,[,,,],[-,,,])
            y_In = tf.slice(y,[,],[-,])
        else:
            xImage = tf.slice(xImage,[,,,],[-,,,])
            y_In = tf.slice(y,[,],[-,])

        Conv1 = tf.abs(tf.layers.conv2d(inputs=xImage,filters=,kernel_size=,strides=,activation=tf.nn.tanh))
        Pool1 = tf.layers.max_pooling2d(inputs=Conv1,pool_size=,strides=,padding='same')

        Conv2 = tf.abs(tf.layers.conv2d(inputs=Pool1,filters=,kernel_size=,strides=,activation=tf.nn.tanh))
        Pool2 = tf.layers.max_pooling2d(inputs=Conv2,pool_size=,strides=,padding='same')

        Conv3 = tf.abs(tf.layers.conv2d(inputs=Pool2,filters=,kernel_size=,strides=,activation=tf.nn.tanh))
        Pool3 = tf.layers.max_pooling2d(inputs=Conv3,pool_size=,strides=,padding='same')

        Conv4 = tf.abs(tf.layers.conv2d(inputs=Pool3,filters=,kernel_size=,strides=,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],)

        Fc1 = tf.layers.dense(Concat,,activation=tf.nn.tanh)
        Fc2 = tf.layers.dense(Fc1,,activation=tf.nn.tanh)

        Cost = tf.reduce_mean(tf.square(Fc2 - y_In)) / 
        tf.summary.scalar(name + "/Cost",Cost)

        Optimizer = tf.train.AdamOptimizer().minimize(Cost)
        return Optimizer,Fc2,x,y,Cost
           

順便一說,我并沒有使用論文中的随機梯度下降來優化參數,因為。。實在太慢了。我使用了Tensorflow中提供的優化器Adam。

最後,在測試集上的雙眼間距錯誤率為 4%,應該說我們的網絡基本已經實作了。雖然精度可能沒有論文中

CNN for Face Alignment 深度神經網絡的初次嘗試

那麼好,但也差不多啦哈哈哈哈!OK,CNN的第一次應用就算完成了。接下來去解決後續論文了。

CNN for Face Alignment 深度神經網絡的初次嘗試
CNN for Face Alignment 深度神經網絡的初次嘗試

繼續閱讀