本文由 【AI前線】原創,原文連接:t.cn/RYs9U26git
AI 前線導語: 「在第一篇文章《ImageNet 冠軍帶你入門計算機視覺:監督學習與神經網絡的簡單實現》中,咱們介紹了神經網絡的基本概念以及 Tensorflow 的基本用法。 本文爲系列的第二篇文章,將會介紹卷積神經網絡。重點介紹經典的卷積神經網絡,全卷積網絡的基本概念和基本單元,以及卷積神經網絡與神經網絡的異同。最後經過實現一個在實際中有普遍應用的人臉關鍵點檢測算法,介紹如何用 TensorFlow 構建卷積神經網絡」。github
神經網絡的發展歷史算法
卷積神經網絡(Convolutional Neural Network, CNN)的起源能夠追溯到上世紀 60 年代。生物學研究代表,視覺信息從視網膜傳遞到大腦中是經過多個層次的感覺野 (Receptive Field) 激發完成的,並提出了 Neocognitron 等早期模型。1998 年,深度學習三巨頭之一的 Lecun 等,正式提出了 CNN,並設計了以下圖所示的 LeNet-5 模型。該模型在手寫字符識別等領域取得了不錯的結果。微信
因爲計算資源等緣由,CNN 在很長時間內處於被遺忘的狀態。二十多年後的 ImageNet 比賽中,基於 CNN 的 AlexNet 在比賽中大放異彩,並引領了 CNN 的復興,此後 CNN 的研究進入了高速發展期。目前卷積神經網絡的發展有兩個主要方向:網絡
目前,CNN 是計算機視覺領域最重要的算法,在不少問題上都取得了良好的效果。由於篇幅關係,本文將主要介紹卷積神經網絡的基礎知識。框架
神經網絡 vs 卷積神經網絡ide
上篇文章中咱們介紹了神經網絡。神經網絡在大數據處理,語言識別等領域都有着普遍的應用。但在處理圖像問題時會許多問題:模塊化
參數爆炸函數
以 200x200x3 的圖像爲例,若是輸入層以後的 hidden layer 有 100 個神經元,那麼參數量會達到 200x200x3x100=1200 萬。顯然有如此多參數的模型是難以訓練且容易過擬合的。性能
平移不變性
對不少圖像問題,咱們但願模型知足必定的平移不變性。 例如對圖像分類問題,咱們但願物體出如今圖片的任何位置上,模型都能正確識別出物體。
局部相關性
在大數據等問題中,輸入維度之間不存在顯式的拓撲關係,所以適合使用神經網絡(全鏈接層)進行建模。但對於計算機視覺的問題,輸入圖片的相鄰像素之間存在自然的拓撲關係。例如,判斷圖片中某個位置是否有物體時,咱們只須要考慮這個位置周邊的像素就能夠了,而不須要像傳統神經網絡那樣將圖片中全部像素的信息做爲輸入。
爲了克服神經網絡的上述問題,在視覺領域,咱們須要一種更合理的網絡結構。卷積神經網絡,在設計時經過局部鏈接和參數共享的方式,克服了神經網絡的上述問題,於是在圖像領域取得了驚人的效果。接下來咱們將詳細介紹卷積神經網絡的原理。
卷積神經網絡
網絡結構
卷積神經網絡和傳統神經網絡的總體結構大體相同。以下圖所示,含有 2 層全鏈接層的傳統神經網絡和含有 2 層卷積層的卷積神經網絡都是由基本單元堆疊而成,前一層的輸出做爲後一層的輸入。最終層的輸出,做爲模型的預測值。兩者的主要差異在於基本單元不一樣,卷積神經網絡使用卷積層代替了神經網絡中的全鏈接層。
和全鏈接層同樣,卷積層中也含有能夠學習的參數 weight 和 bias。模型的參數,能夠按上一篇文章介紹的方法,在監督學習的框架下定義損失函數,經過反向傳播進行優化。
卷積 (Convolution)
卷積層是整個卷積神經網絡的基礎。2D 卷積操做,能夠看做是一個相似模板匹配的過程。以下圖所示,將尺寸爲 h × w × d 的模板,經過滑動窗口的方式和輸入進行匹配。滑動過程當中,輸入中對應位置的值和模板的權重的內積加一個偏移量 b,做爲對應輸出位置的值。w,h 是模板的大小,統稱爲 kernel size,在 CNN 中,w 和 h 通常會取相同的值。d 是模板的 channel 數量,和輸入的 channel 數相同,例如對 RGB 圖像,channel 數爲 3。
模板在卷積神經網絡中常被稱爲卷積核( K )或者過濾器(filter)。在標準卷積中,輸出位置 (x,y) 對應的輸出值能夠表示成:
在 CNN 中,除了描述單個 filter 的 h ,w ,d 這 3 個參數以外,還有 3 個重要的參數 depth, stride 和 padding:
以下圖所示,對 1D 的狀況,假設輸入尺寸爲 W ,filter 的尺寸爲 F,stride 爲 S,padding 爲 P ,那麼輸出的尺寸爲 (W - F + 2P)/S + 1爲。 經過設定 P=(F-1)/2,當 S=1 時,輸入和輸出的尺寸會保持一致。2D 卷積的計算和 1D 卷積相似。
對比傳統神經網絡中的全鏈接層,卷積層實際上能夠當作是全鏈接層的一種特例。首先是局部鏈接性,經過利用輸入自帶的空間拓撲結構,卷積神經網絡只需考慮在空間上和輸出節點距離在 filter 範圍內的輸入節點,其餘邊的權重都爲 0。此外,對於不一樣的輸出節點,咱們強制 filter 的參數徹底一致。但經過這種 局部鏈接 和 參數共享,卷積層能夠更好的利用圖像中內在的拓撲關係及平移不變形,大大減小了參數,從而獲得一個更好的局部最優解,使其在圖像問題上有更好的性能。
在 tensorflow 中實現卷積層很是簡單,能夠直接調用tf.nn.conv2d
:
池化 (Pooling)
在 CNN 網絡中,除了大量的卷積層,咱們也會根據須要,插入適量的池化層。池化層能夠用來減小輸入的尺寸,從而減小後續網絡的參數與計算量。常見的池化操做(如 max pooling,average pooling),一般也能夠提供必定的平移不變性。
咱們以 max pooling 舉例,max pooling 對 kernel size 範圍內的全部值取 max,結果做爲對應位置的輸出。pooling 一般是對每一個 channel 單獨操做,所以輸出的 channel 數和輸入相同。池化層和卷積層相似,pooling 操做也能夠理解爲採用滑動窗口的方式,所以也有和卷積對應的步長 stride 和 padding 等概念。 下圖所示就是一個 kernel size 和 stride 都爲 2 的 max pooling 操做:
實際當中,池化層的參數有兩種比較常見的配置,一種是 kernel size 和 stride 都爲 2 的,這種設置池化過程當中無重疊區域。另外一種是 kernel size 爲 3,stride 爲 2 的有重疊 pooling。在 tensorflow 中實現池化層也很是簡單:
卷積神經網絡的經典網絡結構
介紹了卷積神經網絡的基本組成模塊以後,咱們接下來介紹一下卷積神經網絡的經典網絡結構。從 1998 的 LeNet-5 開始,到 Imagenet 2012 的 AlexNet 模型,再到後來的 VGG 等一系列經典模型,基本都聽從了這個經典結構。
爲了清晰,咱們省略了卷積和全鏈接層以後的非線性激活函數。如上圖所示,經典的卷積神經網絡,能夠分爲三個部分:
經典卷積神經網絡,能夠看做是一個輸出尺寸固定的非線性函數。它能夠將尺寸爲 H \times W \times 3 的輸入圖片轉化爲最終的維度爲 d 的定長向量。經典卷積神經網絡在圖像分類、迴歸等問題上取得了巨大的成功。以後的實戰部分,咱們會給出一個迴歸問題的例子。
全卷積網絡
(Fully Convolution Network)
經典的卷積神經網絡中因爲有全鏈接層的存在,只能接受固定尺寸的圖片做爲輸入,併產生固定尺寸的輸出。雖然能夠經過使用 adaptive pooling 的方式, 接受變長的輸入,但這種處理仍然只能產生固定尺寸的輸出。爲了克服經典卷積神經網絡的這種缺點,在物體分割等輸出尺寸可變的應用場景下,咱們再也不使用全鏈接層。這種主要計算單元所有由卷積層組成的網絡,被稱爲全卷積網絡(FCN)。
如上圖所示,因爲卷積操做對輸入尺寸無限制,且輸出尺寸由輸入決定,所以全卷積網絡能夠很好的處理如分割等尺寸不固定的問題。全卷積網絡,能夠當作是一種輸出尺寸隨輸入尺寸線性變化的非線性函數。它能夠將尺寸爲 H × W × 3 的輸入圖片轉化爲最終維度爲 H/S ×H/S × d 的輸出。 能夠轉化爲這種形式的監督學習問題,基本均可以在全卷積網絡的框架下求解。
反捲積(Deconvolution)
在全卷積網絡中,標準的卷積 + 池化操做,會使輸出的尺寸變小。對於不少問題,咱們須要輸出的尺寸和輸入圖片保持一致,所以咱們須要一種能夠擴大輸入尺寸的操做。最經常使用的操做就是反捲積。
反捲積能夠理解成卷積的逆向操做。這裏咱們主要介紹 stride>1 且爲整數的反捲積。這種反捲積能夠理解爲一種廣義的差值操做。如下圖爲例,輸入是 3x3 的綠色方格,反捲積的 stride 爲 2,kernel size 爲 3,padding 爲 1。在滑動過程當中,對每一個輸入方格,其輸出爲對應的 3x3 陰影區域,輸出值爲輸入值和 kernel 對應位置值的乘積。最終的輸出爲滑動過程當中每一個輸出位置對應值的累加和。這能夠當作是一種以 3x3 kernel 值爲權重的差值操做。最外邊的一圈白色區域沒法進行完整的差值操做,所以能夠經過設定 padding 爲 1, 將周圍的一圈白色區域去掉,最終的輸出尺寸爲 5x5。
根據上面的描述,stride>1 且爲整數的反捲積,若是固定反捲積 kernel 的取值爲雙線性差值 kernel,反捲積能夠等價於雙線性差值。而經過學習獲得反捲積 kernel,相比固定參數的 kernel,能夠更好的適應不一樣的問題,所以反捲積能夠當作是傳統差值的一種推廣。和卷積相似,tensorflow 中已經實現了反捲積模塊tf.layers.conv2d_transpose
。
卷積神經網絡在視覺識別中的應用
CNN 在視覺識別(Visual Recognition)中有着很是普遍的應用。咱們接下來以視覺識別中的三大經典問題:分類 / 迴歸、檢測和分割爲例,介紹如何用 CNN 解決實際問題。
分類 / 迴歸 (classification/regression)
圖像分類是指判別圖像屬於哪一 / 哪些預先指定的類別,圖像迴歸是指根據圖像內容判斷圖片屬性的取值。分類和迴歸在實際中都有着普遍的應用。從物體分類,人臉識別,再到 12306 的驗證碼識別等,均可以抽象成標準的分類問題。相似的,人臉的關鍵點位置預測,人臉的屬性預測(如年齡,顏值)等,也均可以抽象爲標準的迴歸問題。目前視覺領域的應用,若是能抽象成輸出爲定長的分類或者回歸問題,在有大量訓練數據的狀況下,一般均可以採用以前介紹的經典卷積神經網絡框架解決。
檢測 (detection)
檢測問題一般是指判斷出圖片中是否有物體,以及該物體的位置。檢測有 one-stage 和 two-stage 的方法。 由於篇幅關係,咱們重點介紹在 FCN 框架下的 one-stage 方法。
按以前的介紹,FCN 能夠看做是將 H × W × 3 的輸入圖片,轉化爲 H/S × W/S × d 輸出的非線性函數。在 FCN 的框架下解決檢測問題,咱們能夠預測每個輸出位置是否有物體,以及物體左上角、右下角相對於當前輸入位置的偏移。 這樣對每一個輸出位置,須要 5 維的向量來表示是否有物體,即 d=5。 定義了網絡的輸出以後,咱們人工構造出對應的 ground truth,以後在監督學習的框架下,經過定義損失函數(l2 loss) 並進行反向傳播,進行參數的學習。
分割 (segmentation)
分割問題是指給出圖片中每一個像素點的類別。 基於 FCN 的分割方法和上面介紹的 one-stage 的檢測方法很是相似。 對一個多分類的分割問題,對輸出的每個位置,咱們能夠判斷其所屬的類別。 在 FCN 的框架下,對於 N 分類問題,輸出爲 H/S × W/S × N。以後經過反向傳播的方式進行訓練。 分割和檢測問題有一個區別是咱們有時須要獲得和輸入圖片一樣大小的輸出(H × W × N),但卷積神經網絡爲了加速,一般會添加 pooling 層,減少中間卷積層的尺寸。 以下圖所示,爲了保證輸出的尺寸知足要求,咱們能夠在網絡的最後添加反捲積層進行補償,從而得到更大尺寸的輸出。
實戰: 人臉關鍵點檢測
人臉關鍵點檢測是如今視覺領域比較成熟的一項應用,是活體檢測,人類美化,人臉識別等高級應用的基礎。本文最後經過一我的臉關鍵點檢測的例子,展現如何用 Tensorflow 實現圖像迴歸類的應用。實驗數據集採用 Kaggle 比賽中的 Faical Kerypoints Detection 數據集(https://www.kaggle.com/c/facial-keypoints-detection)。該數據集包含了 7094 張訓練圖片和 1783 張測試圖片。數據集中的每一張人臉都有 15 個關鍵點的標註,圖片的尺寸爲 96x96。
L2 距離迴歸
Kaggle 比賽的目標是預測人臉上 15 個關鍵點的座標,總共 30 個 float 值,屬於標準的迴歸問題。咱們選擇採用最多見的 l2 距離,做爲優化的目標。和第一篇文章中神經網絡模型的代碼結構同樣,咱們將代碼分紅了 3 個主要模塊,分別是 Dataset 模塊,Net 模塊和 Solver 模塊。
模型結構
inference
咱們在 inference 函數中定義網絡的主體結構。由於模型會重複用到全鏈接層和卷積層,所以咱們將他們封裝成函數linear_relu
和conv_relu
,從而方便複用代碼。 網絡結構上咱們採用了比較簡單的 3 層卷積,2 層全鏈接的結構。卷積層的輸出經過tf.reshape
轉化成了全鏈接層能夠接受的格式。由於是迴歸問題,咱們直接將最後一層全鏈接層的結果做爲輸出。loss
爲了簡單,對於標準的迴歸問題,咱們使用 mse 做爲損失函數tf.reduce_mean(tf.square(predictions - labels), name='mse')
測試時,咱們依舊使用 tensorflow 提供了 tf.metrics 模塊,自動完成對每一個 batch 的評價,並將全部的評價彙總。在這個例子裏,咱們是解決迴歸問題,所以可使用tf.metrics.mean_squared_error
計算均方偏差。
Dataset 部分,咱們使用了 tensorflow 推薦的 tfrecord 格式。經過TFRecordDataset
函數讀取 tfrecord 文件,並經過parse_example
將 tfrecod 轉換成模型的輸入格式。tfrecord 做爲一種定長格式,能夠大大加快數據的讀取速遞。特別在使用 GPU 時,能夠防止數據 io 成爲性能的瓶頸。
Solver
經過模塊化的設計,咱們能夠徹底複用第一篇文章中的 Solver 代碼,而不須要任何修改,進而提升代碼的複用效率。
實驗結果
封裝好藉口以後,咱們能夠經過上面簡單的代碼,完成模型的訓練。下圖是 tensorboad 中可視化的網絡結構、loss 的統計以及模型在測試圖片上的效果
能夠看到,一個 3 層卷積 +2 層全鏈接的經典卷積神經網絡,就能夠很好的解決人臉關鍵點檢測的問題。在實際中,咱們可使用更復雜的網絡和一些其餘 trick 來進一步提升模型性能。
做者簡介
董健,360 高級數據科學家,前 Amazon 研究科學家。 目前主要關注深度學習、強化學習、計算機視覺等方面的科學和技術創新,擁有豐富的大數據、計算機視覺經驗。曾經屢次領隊參加 Pascal VOC、ImageNet 等世界著名人工智能競賽並得到冠軍。
博士期間在頂級國際學術會議和雜誌上發表過多篇學術論文。從 2015 年末加入 360 至今,董健做爲主要技術人員參與並領導了多個計算機視覺和大數據項目。
完整代碼下載:
https://github.com/Dong--Jian/Vision-Tutorial
關注咱們的微信號"AI前線",後臺回覆「AI」可得到《AI前線》系列PDF電子書