級聯MobileNet-V2實現CelebA人臉關鍵點檢測(附訓練源碼)

這裏寫圖片描述

此博客詳細介紹級聯MobileNet-V2實現人臉關鍵點檢測。
模型:MobileNet-V2
數據:CelebA(http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html )
框架:Caffe
系統:ubuntu16.04
GPU:GTX1080
實驗結果:單模型不到1M,僅956KB,GTX1080上僅6ms,簡單部署即可在移動端進行實時檢測。
本實驗代碼:https://github.com/tensor-yu/cascaded_mobilenet-v2,喜歡請給個Star
(僅想實現demo,請直接跳到2.0修改caffe,然後進入2.5級聯展示)

一 、引言

1.1爲什麼是級聯?

近幾年人臉關鍵點檢測的方法大都採用從粗到精(coarse-to-fine)的思想,在CNN上體現爲級聯。級聯之所以好,是因爲後一級的輸入是經過上一級“篩選”後的區域,即達到一個取其精華去其糟粕的過程。比如,在一張大圖裏要看清一個人的左眼珠,第一步應該是在圖中找到這個人,第二步看他的眼睛,第三步纔是看到他的左眼珠.

不熟悉級聯CNN的,可閱讀幾篇經典的級聯CNN的論文

    1. Deep Convolutional Network Cascade for Facial Point Detection 
    2.Extensive Facial Landmark Localization with Coarse-to-Fine Convolutional Network Cascade
    3.Facial Landmark Detection by Deep Multi-task Learning
    4.Facial Landmark Detection with Tweaked Convolutional Neural Networks
    5. Joint Face Detection and Alignment Using Multitask Cascaded Convolutional Networks

也可查看博客瞭解:深度學習人臉關鍵點檢測方法—-綜述

1.2爲什麼是MobileNet-V2?

爲了獲得更好的性能,近些年設計的CNN更深,更復雜,這樣明顯阻礙了模型的部署應用。而手機端的應用越來越廣泛,因此,一種輕量化,高效率的網絡——MobileNet,應運而生。MobileNets是Google針對移動端而設計的一種輕量化,高效率的網絡模型。MobileNet-V1參考博客。據說,MobileNet-V1的工作很早就做了,當時小組裏沒人在意,等過了一段時間發現MobileNet-V1的坑還沒有人佔,就投了出來。這也是爲什麼在殘差概念如此火爆的16,17年裏,出現了一個沒有使用殘差概念的MobileNet-V1。
MobileNet-V2——緊跟時代的MobileNet
2018年1月底,Google團隊將MobileNet-V1的升級版——MobileNet-V2掛到了arXiv上(https://arxiv.org/abs/1801.04381),主要改進是採用了殘差思想,但由於採用depth-wise convolution,所以不能直接採用傳統的殘差結構,而是稍稍做了改變,用的是, Inverted residuals,並且在residual block的Eltwise sum之前的那個 1*1 Conv 不再採用Relu。MobileNet-V2博客

二、 級聯MobileNet-V2之人臉關鍵點檢測

2.0 修改caffe

進行實驗前,請先配置支持MobileNet及多標籤的caffe
本實驗基於MobileNet-V2,因此需要給caffe添加新的layer,即depth-wise convolution,並且需要修改image_data_layer,使得其支持多標籤輸入
(感謝 hpp,cpp,cu,prototxt提供者:suzhenghang github地址)

具體步驟,進入caffe_need/文件夾下,
1. 將image_data_layer.hpp 替換掉 caffe_path/include/caffe/layers 下的 image_data_layer.hpp
2. 將conv_dw_layer.hpp 複製到 caffe_path/include/caffe/layers 下
3. 將image_data_layer.cpp 替換掉 caffe_path/src/caffe/layers 下的image_data_layer.cpp
4. 將conv_dw_layer.cu
conv_dw_layer.cpp 複製到 caffe_path/src/caffe/layers 下

重新編譯,並且配置python接口

2.1 整體框架及思路

本實驗採用兩級級聯,level_1只是初步的“剔除”非人臉部分區域,保留人臉區域供level_2進行檢測,從而獲得更精確的檢測結果。其實還可以繼續做第三級,即類似DCNN(2013.sun)那樣,把眼睛,鼻子,嘴巴都裁剪出來,單獨用一個網絡去檢測,這樣的效果肯定優於本實驗,只不過級聯的訓練實在是太繁瑣了!一個人真心搞不過來。
實驗流程如下圖所示:
這裏寫圖片描述

第一行是level_1,輸入爲原始數據,level_1輸出的landmark主要作用是用來剪裁,本實驗採用的剪裁策略很“拙劣”,希望有人給出更好的裁剪策略。具體如何裁剪稍後講解。

第二行是level_2,輸入的輸出爲level_1裁剪出來之後的圖像,可以看到,level_2的輸入已經“剔除”了大量無關信息——背景,身體軀幹等等。level_2輸出的點是相對於level_1 crop這張圖的,因此還需要記住level_1裁剪的信息,纔可以返回原始數據進行關鍵點標註。

簡單介紹訓練代碼結構。訓練代碼共計三部分,分別是0_raw_data, 1_level_1, 2_level_2
每一部分裏面均包含三個文件夾——Code ,Data ,Result

2.2 原始數據處理 0_raw_data

訓練數據採用CelebA,官網:http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html
百度網盤下載:https://pan.baidu.com/s/1eSNpdRG#list/path=%2F

第一步
將CelebA\Img\img_celeba 複製到 0_raw_data/Data/ 下面,
將CelebA\Anno\list_landmarks_celeba.txt複製到 0_raw_data/Data/ 並且重命名爲celeba_label.txt
這裏寫圖片描述

第二步
進入0_raw_data/, 運行divide_tr_te.py,將會劃分好訓練集,測試集,並且保存在0_raw_data/Result/ 下面
這裏寫圖片描述

第三步
運行 draw_point.py,將會在 0_raw_data/Result/draw_img/下獲得 打上關鍵點的圖片,用來檢查圖片以及標籤是否正確
這裏寫圖片描述

來感受一下我們的訓練數據都是什麼樣子的
列表內容
可以發現,雖然是人臉數據,但是圖片並不想LFW,ALFW那麼簡單,這裏有不同的角度,表情,場景,以及人臉所佔圖片區域大小也不盡相同(這點正是級聯所要解決的問題)

劃分好數據集,就可以進入level_1了

2.3 level_1訓練

同樣的,1_level_1文件夾下包含三個文件夾,分別是Code,Data,Result
進入Code裏面看看:
這裏寫圖片描述

又有5個文件夾,是不是要暈了? 沒辦法,級聯就是那麼繁瑣!
現在一個文件夾一個文件夾的來,不着急。0_raw_data中我們只是劃分好了數據,但是真正能夠餵給level_1的數據還沒有準備好,所以第一步就是生成能餵給level_1的數據。

進入0_gen_data,運行 gen_l1_data.py,則會在1_level_1/Data下面生成 四個東西:
這裏寫圖片描述

由於強迫症,以及避免生成數據時候把數據搞錯了,這裏進行 1_draw_img,即用1_level_1/Data下面的數據將關鍵點打出來,看一下有沒有弄錯。

進入1_draw_img/ 運行draw_l1_point.py ,即可在1_level_1/Data/下獲得一個文件夾draw_img ,進去看一看,沒問題的話,那就可以進行訓練了。

進入 2_train/,裏面包含l1_mobilenet.prototxt, solver.prototxt,train.sh,
請進入train.sh,更改caffe路徑/your_caffe_path/build/tools/caffe ,確保你的caffe能用

部分參數設置:loss_weight 我設置爲100,80000次迭代後就下降學習率,100000次再次下降學習率,經過多次實驗發現第二次下降學習率之後,會出現過擬合問題,所以我的模型均採用 100000次迭代時得到的模型。(若發現有更好的solver,請貼出來分享,謝謝!)

MobileNet-V2 網絡結構如下:

Input Operator t c n s
48x48x3 conv2d - 16 1 2
24x24x16 bottleneck 6 24 2 2
12x12x24 bottleneck 6 32 2 2
6x6x32 bottleneck 6 64 2 2
3x3x64 fc - 256 - -
1x1x256 fc - 10 - -

lr=0.01, 8萬次下降一次,10萬次下降一次train和test的loss曲線分別爲:
這裏寫圖片描述
這裏寫圖片描述

Train loss大概是在 0.005~0.006, Test loss是在 0.007~0.008。 過擬合不是很嚴重(PS,訓練的時候loss_weighs設置爲100,因此數量級上有些許出入 )
訓練完畢,caffemode會保存在1_level_1/Result/solver_state/下面,接着可以做inference。

進入3_inference/,運行 打開inference.py,修改caffe的路徑(並確保你的caffe配置了python接口)
運行 inference.py,即可獲得level_1對所有圖片的關鍵點預測值,並保存在Result/下,以供後面評估以及裁剪所需。

有了預測,就可以看看誤差有多大,一開始採用雙眼距離作爲歸一化因子,即:err=deucdipd

,其中deuc表示輸出landmark與真實landmark的歐氏距離,dipd表示兩眼之間的歐氏距離。可是在計算的時候,出現了inf!幾經周折,才找到問題所在!之前採用兩眼距離作爲歸一化因子的那些實驗,他們的數據集一定一定是比較正的人臉,絕對有沒有以下這種情況:
這裏寫圖片描述

其實就是側臉,兩隻眼睛在同一個位置,暈~

於是乎將歸一化因子改爲圖片的對角線長度,開始計算吧~
進入4_evaluate/

在這裏不僅計算誤差,並且還將level_1預測的關鍵點打到了圖片上,用來觀察效果如何,這部分圖片保存在1_level_1/Result/l1_out_draw/ 下面
並且設置了一個閾值(這裏設置爲10%),誤差大於該閾值的那部分圖片,統統捨棄,因爲這些圖片已經不能夠爲level_2所用。這部分圖片保存在 1_level_1/Result/l1_drop/ 下面

分別運行 evaluate_test.py 和evaluate_train.py,
可獲得5個關鍵點的平均誤差,以及所有圖片平均誤差的分佈圖。以test爲例,5個點的平均誤差分別是:
這裏寫圖片描述

所有圖片的平均誤差分佈爲:
這裏寫圖片描述

可以看到,99%的誤差是小於10%的,平均誤差爲1.17%
爲什麼要畫這個分佈圖的?有了這個分佈圖,可以更好的選擇一個更好的閾值,用來確定哪些圖片需要裁剪,哪些圖片需要丟棄。

至此,level_1的訓練結束,接下來要爲level_2準備數據,這裏一併放在level_1裏邊做。
進入文件夾 5_crop_img/, 分別執行crop_train_img.py 和 crop_test_img.py即可。

這裏要說一下,裁剪策略,由於CelebA的圖片奇奇怪怪,不像LFW,ALFW那樣正,所以沒想出個什麼好的裁剪策略,亂想了一個,就是以level_1預測到的鼻子爲中心,裁剪出一個正方形,這個正方形的邊長爲四倍鼻子到左眼的距離。舉個例子:
下圖綠色點即爲level_1的預測點,以鼻子爲中心,裁剪出一個正方形, 紅框所示。裁剪出來的圖片作爲level_2的輸入。

這裏寫圖片描述

運行完 5_crop_img/ level_1 就結束了。

這個solver跑出來的模型在這批數據集上,並不盡人意,來看了一下預測結果較差的圖片,大概是這樣的:

這裏寫圖片描述

各種千奇百怪的人臉姿態,很多是橫躺着的。。。

2.4 level_2訓練

有了level_1/5_crop_img/得出來的數據, level_2不需要再進行生成數據這一步了。可依次執行: 0_train, 1_inference, 2_evaluate

這裏,有必要看一下train loss和test loss曲線:
這裏寫圖片描述
這裏寫圖片描述

其實並沒有收斂,但這不是重點,重點是train和test的曲線都出現了很大的浮動,有尖點存在。這是爲什麼呢? 其實是因爲level_1剪裁出了問題,在裁剪時,將誤差小於10%的圖片保留,用以裁剪。其實這個閾值(10%)還是太大了,以至於裁剪出這一些“不合格”圖片。示例:
這裏寫圖片描述

2.5 級聯展示[ 3_demo ]

有了level_1和level_2的caffemodel,就可以跑級聯了,具體可參見 3_demo/Code/ inferencen.py,由於是demo,這裏只測試幾張圖片,若想測試整個CelebA數據集,請修改
inferencen.py當中的兩個參數: raw_txt = ‘../Data/demo.txt’ 和 relative_path = ‘../Data/img/’

demo結果:
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

其中,綠色點爲真實landmark,紅色點爲level_1的預測,藍色點爲level_2的預測。可以看到藍色點比紅色點更靠近綠色點(好繞口。。。)

三 總結

本實驗是對級聯的一個粗略實現,通過本實驗可深刻瞭解到級聯是多麼的繁瑣,雖然繁瑣,但是級聯的效果還是很明顯的。實驗初步驗證了MobileNet-V2是短小精幹的模型,並且可在移動端實現實時檢測。

本實驗還有一些不足之處,大家可以做相應的修改
1. 模型訓練不足,未完全收斂,可對solver進行修改,獲得更好的模型。
2. level_1的裁剪策略相對“拙劣”,可依據具體應用場景,提出不同的裁剪策略,確保輸入到level_2的圖片包含整個人臉。

其實本實驗還有一個問題是,未採用人臉檢測步驟。通常的人臉關鍵點檢測,會涉及人臉檢測這一步,將人臉檢測得到的人臉圖片作爲輸入,其實這也是一從粗到精的過程。通過人臉檢測器,可以剔除不必要的信息。而在本實驗中,省略掉了人臉檢測這一步,而level_1的作用恰恰相當於人臉檢測!因此,在特定場景下,是否可以考慮省略人臉檢測這一步(畢竟檢測需要耗時),而採用level_1進行人臉的初步定位?從而提升檢測速度

這裏要說的特定場景是指圖片中僅有一個人臉的情況,否者此法失效。 有不少場景還是有且僅有一個人臉的,因此針對這種場景可以採用level_1作爲人臉檢測器。

針對級聯還有很多idea想去實現,但限於數據(68點數據不足),人力(一個人)原因,擱置了。

轉載請註明出處:
http://blog.csdn.net/u011995719/article/details/79435615

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章