人臉表情識別從0到部署,猜猜『輪到你了』的微笑狼人到底是誰!
- 2019 年 11 月 21 日
- 筆記
本文實現了從項目調研、數據收集、數據預處理、深度卷積神經網路訓練再到伺服器部署,實現了一個人臉表情識別的小項目,非常適合一直在學習,但是找不到合適的練手項目的同學。
0 項目成果
先展示一下我的結果。我們測試的圖片當然是當前最熱的 nihongo 電視劇『輪到你了』的 CP 二階堂和黑島了

有興趣的朋友可以掃碼進行體驗,會跳轉到我的網站,選擇圖片文件上傳,Upload 後就會返回預測結果,其中 smile
表示微笑,pout
表示嘟嘴,no-smile
表示中性表情。那麼接下來就開始我們整個項目的講解了
本項目整理完成後會上傳所有程式碼到 Github 上,會有詳細的程式碼注釋,歡迎查閱!https://github.com/FLyingLSJ/
1 說在前面
作為一名機器學習的愛好者,提升自己的最好的方式就是參與一項項目,那麼什麼才能夠稱作項目呢?訓練過 MNIST 數據集?做過 dogs VS. cats?想必這很難被稱作一個項目,放在簡歷上,想必不能讓 HR 看到閃光點, 之前有幸閱讀到陳老師的『接地氣學堂』的文章《我沒有項目經驗,怎麼辦?》,裡面很詳細的介紹了什麼項目的定義、個人在項目中的角色、以及判斷項目的成敗等等,非常具有啟發性。簡單總結如下:
- 項目定義:給定時間費用的限制,達成特定的目的的工作
- 個人在項目中的角色:參與(某個環節)、主導(負責人)、獨立承擔
- 判斷項目成敗:開頭的目標是什麼,達成了就算成功,沒達成就算失敗。就算沒有參加整個項目的流程,也需要關注子項目(自己做的部分)的成敗
- 項目的是否高級:在規定時間、費用範圍內產出更大的、投入更少的,都是「高級」項目;產出率低,耗時耗力多的就是「低級」項目
我將其做成了思維導圖,可以在後台回復 思維導圖
獲取

作為機器學習愛好者,並且以後想從事相關工作的我一直在思考如何創造出一個項目來,儘可能的去模擬企業開發產品的流程,讓自己提前以實際開發者的心態來開發一款產品或者服務。於是我選擇以人臉表情識別作為我的實戰項目
2. 為什麼是人臉表情分類
機器學習入門時我們一般是以現成的數據集,官方的 Demo 作為開始,一步步調節參數,達到我們所需要的精度。但是在實際的企業開發流程中,往往涉及到流程之多,工程問題的細節也非常多(雖然我現在也是學生,但是據我了解大致的流程應該也差不多)
- 老闆/業務發起需求
- 確定業務組成
- 項目調研:市場調研、演算法調研
- 確定演算法及收集數據
- 確定框架、基準模型
- 服務端部署
人臉表情分類整個項目涉及到幾個方面,如數據採集、數據預處理、人臉檢測、人臉關鍵點檢測、深度學習模型訓練、模型在線部署等,不僅涉及到傳統的機器學習,也與深度學習的相關知識緊密結合。故以人臉表情識別作為實戰再合適不過了
3. 項目調研
開始一個項目之前,肯定是做調研啦,調研包括市場調研和演算法調研
- 市場調研
市場調研需要了解市面上有沒有相似的服務或者產品,如 APP、小程式、網頁等;選中的方案是否有市場需求,是否有成熟的競爭對手和市場;看看我們所服務的內容的目標用戶(年齡層次、地域分布)、市場份額以及潛在的競爭對手、是否已經達到落地水準。沒有充分的調研,有可能你的產品做出來了,但是其實市場上已經有成熟的產品,那麼前期投入的所有資源都白白浪費。所謂人無我有、人有我優、人優我廉、人廉我走 ,做到知根知底,不至於做出來時心裡落差太大
當然,對於我們這個小型的項目可能並不需要有上面的整個流程,但是大致也相似,我們需要了解市場上相似的產品,這裡當然就是市面上有沒有人臉表情識別的軟體、小程式、API 等等
在此之前,我們先簡單介紹一下人臉表情識別的幾個應用場景
- 微表情在情緒識別任務上的可靠度很高, 對錶情情感識別任務有潛在的利用價值, 如婚姻關係預測、交流談判、教學評估等.除了用於情感分析, 研究人員觀察到了有意說謊時產生的微表情經過微表情識別訓練, 普通人識別說謊的能力得到提高
- 在金融領域,有報道顯示,將微表情結合知識圖譜的指南回答引擎,可以識別銀行客戶是否有欺詐風險
- 智慧家居:識別用戶的行為,智慧調節電器,更具智慧化
- 自動駕駛:監測分析駕駛過程中出現的分心、疲勞以及相關負面情緒波動,結合駕駛輔助系統提升駕駛安全
- 教育領域:實時測量學習者對學習內容與學習過程中的情緒變化(如注意力集中、理解困惑、厭惡度等)
接下來就是現有產品的測試啦!
- 百度 AI 體驗中心 在微信小程式搜索 百度 AI 體驗中心,可以體驗人臉與人體識別、語音技術等功能

- 曠視人工智慧平台 在曠視人工智慧平台 https://www.faceplusplus.com.cn/emotion-recognition/ 申請表情識別的 API 進行測試,測試結果如下:在返回的結果中,微笑的程度是一個值為 [0,100] 的浮點數,小數點後 3 位有效數字。數值越大表示笑程度高。在程式處理中,我設定閾值為 60 ,即大於 60 就認為是微笑表情。

- 其他:面向企業的產品
希亞思(上海)資訊技術有限公司:http://www.cacshanghai.com/www/index.php?m=page&f=view&pageID=124
平安云:https://yun.pingan.com/ssr/smart/WBQ
其中平安銀行在 微表情國際權威評測OMG微表情競賽 上獲得第一名,可見平安銀行在人臉表情識別上的技術突破。
以及最近很火的 ZAO ,也是和人臉識別有關的~

- 演算法調研
完成市場調研,接下來就是演算法調研了,所謂的演算法調研就是我們現在做的項目所使用的演算法是什麼,所能達到的精度如何
主要從幾個方面入手:Paper 看看別人的文章,在知網或者 Google 學術上都能找到相關的文章,找找有沒有相關的競賽或者數據集,行業媒體報道也是一種手段,比如虎嗅、36Kr 都會發布相關的行業深度文章。
- 人臉微表情識別綜述:http://html.rhhz.net/ZDHXBZWB/html/20170302.htm
- 基於SIFT演算法的多表情人臉識別:http://html.rhhz.net/YJYXS/html/yj20161209.htm
並且看一下 Github 上面上是否已經有相關的開源項目
4. 數據收集
巧婦難為無米之炊,沒有數據集的支撐,那麼再厲害的深度學習模型也寸步難行,在開始訓練模型之前我們要收集數據,基本的思路是先看看是否有開源數據集、若沒有開源數據集,那麼考慮互聯網這個大寶庫進行爬蟲採集
本項目主要識別三種表情,分別是微笑、嘟嘴、中性表情,故我們所需要的數據也是圍繞這三個表情展開的
首先,我們查找開源的數據集,可以從各大競賽平台開始,比如 Kaggle 本身就是一個數據大寶庫,我們發現在 Kaggle 上有個 CelebFaces Attributes (CelebA) Dataset
競賽,裡面提供的數據包含 202599 張圖片,每張圖片共有 40 個屬性,我們用到其中的 smiling 屬性,每個類別選取 5-6k 圖片,把圖片拆分成微笑表情和中性表情兩個文件夾


在採集到微笑和中性表情以後,接下來就是尋找有關嘟嘴的表情,筆者在網路上搜了一圈,找不到有關嘟嘴的表情,那麼接下來考慮爬蟲採集了。若你沒有爬蟲相關的知識,那麼要在這一步就停滯不前了嗎?不!Github 總有你要的資源~
本項目使用的以下開源的圖片爬蟲項目:關鍵詞 「嘟嘴 pout」
- https://github.com/sczhengyabin/Image-Downloader 下載下來為一個 exe 文件,運行並設置關鍵的參數,就可以從三大搜索引擎中爬取相關的圖片

- https://github.com/kong36088/BaiduImageSpider:該項目只要下載下來,修改你要下載圖片的關鍵詞就能從百度上下載圖片
通過以上方法,我從互聯網上爬取了 1200+ 圖片作為「嘟嘴」表情的訓練集
5. 數據預處理
上一步中,我們從不同渠道獲取到了訓練集,那麼在訓練模型之前要對數據集進行清洗與整理,大致包括以下幾個方面(詳細可以參見下面的思維導圖,註:思維導圖中的內容整理自《深度學習之影像識別–核心技術與案例實戰》作者:言有三)
- 數據規範化處理
- 數據整理分類
- 數據去噪
- 數據去重
- 數據存儲與備份

數據處理在整個項目中佔比的時間也非常多、處理起來比較繁瑣但是卻是關鍵的一環,筆者在這個步驟花費了不少時間。
最終獲取到的影像如下所示,那是不是意味著我們就可以馬上開始搞模型,就開始訓練了,事實上並非如此。如果有對微表情有了解的同學應該知道,人臉的表情是由面部結構決定的,如嘴巴、眉毛、眼睛、鼻子都會影響表情的表達,在本項目中,我們想實現的是嘴巴區域對錶情的決定性,故我們可以將問題進行簡化,我們只關注嘴巴的區域,這一區域影響了我們表情的表達。

對此,我們可以對採集下來的影像進行進一步的處理,把嘴巴區域給裁剪出來,作為我們模型的輸入,一般裁剪下來的圖片在 60-70 解析度左右,對比把原圖送進模型訓練,這樣子做大大降低了我們模型的訓練時間。現在有很多人臉檢測演算法已經相當成熟,我們使用的是 OpenCV
+dlib
這兩個圖片處理的庫。在開始之前,我們需要安裝他們,對於 OpenCV 安裝比較簡單,而對於 dlib 的安裝,我們給出 Windows 和 Linux 下的安裝方法,安裝之前需要去 https://pypi.org/simple/dlib/
下載與設備相匹配的 whl 文件,通過 whl 的方法進行安裝
# Windoes 和 Linux 下安裝 OpenCV pip install opencv-python # 若出現:libXrender.so.1: cannot open shared object file: No such file or directory # 考慮安裝下面的包 apt-get install libsm6 apt-get install libxrender1 apt-get install libxext-dev # Windows 下安裝 dlib pip install *.whl # * 是下載下來的 whl 文件的名字,安裝過程比較緩慢,請耐心等待 # Linux 下安裝 dlib sudo apt-get install build-essential cmake sudo apt-get install libgtk-3-dev sudo apt-get install libboost-all-dev pip install dlib
經過關鍵點檢測後,得到的效果如下所示,在本文中,我們使用的是 68 關鍵點檢測,故只要提取出標號 48-67 的點周圍的區域(嘴巴區域)即可


最終得到的結果如下所示:數據集大小是:微笑和中性表情各 1000 張,嘟嘴表情經過處理以後最終剩下 761 張

我們對數據集進行 9:1 的比例進行拆分,拆分成訓練集和測試集
6. 選擇框架基準模型
得到我們目標的數據集,那麼下一步就是著手開始訓練了,我們選擇 Pytorch 這個深度學習框架,之後我們需要確定一個基準模型,由於我們的數據數量上並不是很多。若想要將深度學習應用於小型影像數據集,一種常用且非常高效的方法是使用預訓練網路。預訓練網路(pretrained network)是一個保存好的網路,之前已在大型數據集(通常是大規模影像分類任務)上訓練好。如果這個原始數據集足夠大且足夠通用,那麼預訓練網路學到的特徵的空間層次結構可以有效地作為視覺世界的通用模型,因此這些特徵可用於各種不同的電腦視覺問題,即使這些新問題涉及的類別和原始任務完全不同。舉個例子,你在 ImageNet 上訓練了一個網路(其類別主要是動物和日常用品),然後將這個訓練好的網路應用於某個不相干的任務,比如在影像中識別傢具。這種學到的特徵在不同問題之間的可移植性,是深度學習與許多早期淺層學習方法相比的重要優勢,它使得深度學習對小數據問題非常有效
一句話總結上面這一段就是,使用別人在大規模數據上訓練的模型好參數,我們只修改最後的分類參數,然後應用到我們的數據集上,通常效果並不差
我們以 ResNet18 作為本次識別的基準模型

7. 模型訓練
由 ResNet18 作為基準模型,凍結所有的卷積層,只更改最後的分類器,以 Adam 優化器訓練 500 個輪次得到如下結果,精度在 90% 左右,由於使用的是其他深度學習平台的算力,平台暫時無法下載訓練結果的圖片,所以截圖了模型的訓練精度及驗證精度如下,總共 500 個輪次,可以看出在 260batch 左右網路就已經收斂了
模型訓練精度曲線

模型驗證精度曲線

8. 伺服器部署
- 前端 模型訓練完成以後,我們得到了一個以
.ckpt
為後綴的模型文件,我們將這個模型放到伺服器,為了得到友好的交互,我們需要編寫前端和後端處理的程式,前端我們使用的是 Flask 框架,根據官方的教程https://dormousehole.readthedocs.io/en/latest/
結合 html 的模板,我們很快就能搭建出一個簡單的 web 介面,包括一個上傳文件按鈕和圖片顯示頁面以及文字描述等,整體效果如下:

程式碼如下:
from flask import Flask, request from flask import render_template import time from expression_demo import expression_predict # 表情預測項目 system_path = "./" app = Flask(__name__) # 創建一個 Flask 實例,使用單一模組,應該使用 __name__ @app.route('/') def hello(imgPath=None): return render_template('index.html', imgPath=system_path+"static/image/logo.jpg") @app.route('/upload', methods=['POST']) def upload(imgPath=None, result="None"): file = request.files['file'] fileName = file.filename filePath = system_path+"static/image/"+fileName # 圖片路徑 if file: file.save(filePath) result = expression_predict(filePath) if result is None: result = "could not found your beauty face" return render_template('index.html', imgPath=system_path+"static/image/"+fileName, result=result) else: return render_template('index.html', imgPath=system_path+"static/image/logo.jpg") if __name__ == '__main__': app.run(host="0.0.0.0") #
- 後端處理 後端處理就是對用戶上傳的圖片進行處理,包括我們前期所作的一些工作,如:讀取用戶上傳的圖片、進行人臉檢測、人臉關鍵點檢測、圖片裁剪以及預測返回等操作,我們將預測功能封裝成一個函數,然後在主函數中直接調用即可,以後的每一個項目都封裝成單獨的一個函數,直接做調用就能實現相關的功能
from expression_demo import expression_predict # 表情預測項目,將所有處理的函數寫在一個 py 文件中,在主函數中進行導入
- 程式碼上傳伺服器 將部署的程式碼上傳至伺服器,並將主程式碼運行至後台,這樣,伺服器就能一直工作了,將程式碼運行至後台的程式碼如下,其中 main.py 就是你的主要函數啦!
至此,所有的步驟都已完成!
9. 總結與思考
在本項目中,我們從 0 開始實現了一個人臉表情識別的項目,從項目調研、數據收集、數據預處理、人臉檢測、深度學習模型的訓練再到前端編寫、伺服器部署等,基本上走過了一款小產品開發的所有流程。
在公司裡面,可能一個項目有不同方向的員工參與,如前端工程師、後端工程師、演算法工程師等,但是自己做項目的話,整個流程都需要你自己做,相當於全棧工程師的工作量,是對個人能力很好的鍛煉,整個流程做下來,多多少少都會遇見不少的坑,但是只要耐心去解決,都是很好的成長機會,就像筆者對前端和服務端的知識並不熟悉,在其中遇到了非常多的坑。在往下思考,其實本項目還是有很多需要思考的地方
這些都是實實在在的痛點所在,如下:
- 光照角度、側臉等都會影響到人臉的檢測,從而影響後續的預測結果
- 如何繼續提高模型的精度
- 服務端遇到大的請求如何處理
- WEB 端的一些請求的邏輯處理等等
由於個人知識水平有限,歡迎提出意見~
配圖來自『輪到你了』劇照
參考:
- 《深度學習之影像識別–核心技術與案例實戰》作者:言有三
- https://github.com/tinypumpkin/face_process
- https://github.com/foamliu/Facial-Expression-Prediction
- http://www.cvmart.net/community/article/detail/211
- https://www.thoughtworks.com/insights/blog/emopy-machine-learning-toolkit-emotional-expression
- https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-uswgi-and-nginx-on-ubuntu-18-04
- https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04#step-4-–-managing-the-nginx-process
個人站點:
Github: https://github.com/FLyingLSJ
CSDN : https://blog.csdn.net/LSJ944830401
BLOG: https://flyinglsj.github.io/