基於PaddlePaddle的圖片分類實戰 | 深度學習基礎任務教程系列

pythontab發表於2019-04-19

影像相比文字能夠提供更加生動、容易理解及更具藝術感的資訊,影像分類是根據影像的語義資訊將不同類別影像區分開來,是影像檢測、影像分割、物體跟蹤、行為分析等其他高層視覺任務的基礎。影像分類在安防、交通、網際網路、醫學等領域有著廣泛的應用。

  一般來說,影像分類透過手工提取特徵或特徵學習方法對整個影像進行全部描述,然後使用分類器判別物體類別,因此如何提取影像的特徵至關重要。基於深度學習的影像分類方法,可以透過有監督或無監督的方式學習層次化的特徵描述,從而取代了手工設計或選擇影像特徵的工作。深度學習模型中的卷積神經網路(Convolution Neural Network, CNN) 直接利用影像畫素資訊作為輸入,最大程度上保留了輸入影像的所有資訊,透過卷積操作進行特徵的提取和高層抽象,模型輸出直接是影像識別的結果。這種基於"輸入-輸出"直接端到端的學習方法取得了非常好的效果。

  本教程主要介紹影像分類的深度學習模型,以及如何使用PaddlePaddle在CIFAR10資料集上快速實現CNN模型。

  專案地址:http://paddlepaddle.org/documentation/docs/zh/1.3/beginners_guide/basics/image_classification/index.html

  基於ImageNet資料集訓練的更多影像分類模型,及對應的預訓練模型、finetune操作詳情請參照Github:https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/README_cn.md

  效果展示

  影像分類包括通用影像分類、細粒度影像分類等。圖1展示了通用影像分類效果,即模型可以正確識別影像上的主要物體。

  圖1. 通用影像分類展示

  圖2展示了細粒度影像分類-花卉識別的效果,要求模型可以正確識別花的類別。

  圖2. 細粒度影像分類展示

  一個好的模型既要對不同類別識別正確,同時也應該能夠對不同視角、光照、背景、變形或部分遮擋的影像正確識別(這裡我們統一稱作影像擾動)。圖3展示了一些影像的擾動,較好的模型會像聰明的人類一樣能夠正確識別。

  圖3. 擾動圖片展示[7]

  模型概覽

  CNN:傳統CNN包含卷積層、全連線層等元件,並採用softmax多類別分類器和多類交叉熵損失函式,一個典型的卷積神經網路如圖4所示,我們先介紹用來構造CNN的常見元件。

  圖4. CNN網路示例[5]

  • 卷積層(convolution layer): 執行卷積操作提取底層到高層的特徵,發掘出圖片區域性關聯性質和空間不變性質。

  • 池化層(pooling layer): 執行降取樣操作。透過取卷積輸出特徵圖中區域性區塊的最大值(max-pooling)或者均值(avg-pooling)。降取樣也是影像處理中常見的一種操作,可以過濾掉一些不重要的高頻資訊。

  • 全連線層(fully-connected layer,或者fc layer): 輸入層到隱藏層的神經元是全部連線的。

  • 非線性變化: 卷積層、全連線層後面一般都會接非線性變化函式,例如Sigmoid、Tanh、ReLu等來增強網路的表達能力,在CNN裡最常使用的為ReLu啟用函式。

  • Dropout [1] : 在模型訓練階段隨機讓一些隱層節點權重不工作,提高網路的泛化能力,一定程度上防止過擬合。

  接下來我們主要介紹VGG,ResNet網路結構。

  VGG:牛津大學VGG(Visual Geometry Group)組在2014年ILSVRC提出的模型被稱作VGG模型 [2] 。該模型相比以往模型進一步加寬和加深了網路結構,它的核心是五組卷積操作,每兩組之間做Max-Pooling空間降維。同一組內採用多次連續的3X3卷積,卷積核的數目由較淺組的64增多到最深組的512,同一組內的卷積核數目是一樣的。卷積之後接兩層全連線層,之後是分類層。由於每組內卷積層的不同,有11、13、16、19層這幾種模型,下圖展示一個16層的網路結構。VGG模型結構相對簡潔,提出之後也有很多文章基於此模型進行研究,如在ImageNet上首次公開超過人眼識別的模型[4]就是借鑑VGG模型的結構。

  圖5. 基於ImageNet的VGG16模型

  ResNet:ResNet(Residual Network) [3] 是2015年ImageNet影像分類、影像物體定位和影像物體檢測比賽的冠軍。針對隨著網路訓練加深導致準確度下降的問題,ResNet提出了殘差學習方法來減輕訓練深層網路的困難。在已有設計思路(BN, 小卷積核,全卷積網路)的基礎上,引入了殘差模組。每個殘差模組包含兩條路徑,其中一條路徑是輸入特徵的直連通路,另一條路徑對該特徵做兩到三次卷積操作得到該特徵的殘差,最後再將兩條路徑上的特徵相加。

  殘差模組如圖7所示,左邊是基本模組連線方式,由兩個輸出通道數相同的3x3卷積組成。右邊是瓶頸模組(Bottleneck)連線方式,之所以稱為瓶頸,是因為上面的1x1卷積用來降維(圖示例即256->64),下面的1x1卷積用來升維(圖示例即64->256),這樣中間3x3卷積的輸入和輸出通道數都較小(圖示例即64->64)。

  圖7. 殘差模組

  資料準備

  由於ImageNet資料集較大,下載和訓練較慢,為了方便大家學習,我們使用CIFAR10資料集。CIFAR10資料集包含60,000張32x32的彩色圖片,10個類別,每個類包含6,000張。其中50,000張圖片作為訓練集,10000張作為測試集。圖11從每個類別中隨機抽取了10張圖片,展示了所有的類別。

  圖11. CIFAR10資料集[6]

  Paddle API提供了自動載入cifar資料集模組 paddle.dataset.cifar。

  透過輸入python train.py,就可以開始訓練模型了,以下小節將詳細介紹train.py的相關內容。

  模型結構

  Paddle 初始化

  讓我們從匯入 Paddle Fluid API 和輔助模組開始。

  本教程中我們提供了VGG和ResNet兩個模型的配置。

  VGG

  首先介紹VGG模型結構,由於CIFAR10圖片大小和數量相比ImageNet資料小很多,因此這裡的模型針對CIFAR10資料做了一定的適配。卷積部分引入了BN和Dropout操作。 VGG核心模組的輸入是資料層,vgg_bn_drop 定義了16層VGG結構,每層卷積後面引入BN層和Dropout層,詳細的定義如下:

  首先定義了一組卷積網路,即conv_block。卷積核大小為3x3,池化視窗大小為2x2,視窗滑動大小為2,groups決定每組VGG模組是幾次連續的卷積操作,dropouts指定Dropout操作的機率。所使用的img_conv_group是在paddle.fluit.net中預定義的模組,由若干組 Conv->BN->ReLu->Dropout 和 一組 Pooling 組成。

  五組卷積操作,即 5個conv_block。 第一、二組採用兩次連續的卷積操作。第三、四、五組採用三次連續的卷積操作。每組最後一個卷積後面Dropout機率為0,即不使用Dropout操作。

  最後接兩層512維的全連線。

  在這裡,VGG網路首先提取高層特徵,隨後在全連線層中將其對映到和類別維度大小一致的向量上,最後透過Softmax方法計算圖片劃為每個類別的機率。

  ResNet

  ResNet模型的第1、3、4步和VGG模型相同,這裡不再介紹。主要介紹第2步即CIFAR10資料集上ResNet核心模組。

  先介紹resnet_cifar10中的一些基本函式,再介紹網路連線過程。

  • conv_bn_layer : 帶BN的卷積層。

  • shortcut : 殘差模組的"直連"路徑,"直連"實際分兩種形式:殘差模組輸入和輸出特徵通道數不等時,採用1x1卷積的升維操作;殘差模組輸入和輸出通道相等時,採用直連操作。

  • basicblock : 一個基礎殘差模組,即圖9左邊所示,由兩組3x3卷積組成的路徑和一條"直連"路徑組成。

  • layer_warp : 一組殘差模組,由若干個殘差模組堆積而成。每組中第一個殘差模組滑動視窗大小與其他可以不同,以用來減少特徵圖在垂直和水平方向的大小。

resnet_cifar10 的連線結構主要有以下幾個過程。

  底層輸入連線一層 conv_bn_layer,即帶BN的卷積層。

  然後連線3組殘差模組即下面配置3組 layer_warp ,每組採用圖 10 左邊殘差模組組成。

  最後對網路做均值池化並返回該層。

  注意:除第一層卷積層和最後一層全連線層之外,要求三組 layer_warp 總的含參層數能夠被6整除,即 resnet_cifar10 的 depth 要滿足

  Infererence配置

  網路輸入定義為 data_layer (資料層),在影像分類中即為影像畫素資訊。CIFRAR10是RGB 3通道32x32大小的彩色圖,因此輸入資料大小為3072(3x32x32)。

  Train 配置

  然後我們需要設定訓練程式 train_network。它首先從推理程式中進行預測。 在訓練期間,它將從預測中計算 avg_cost。 在有監督訓練中需要輸入影像對應的類別資訊,同樣透過fluid.layers.data來定義。訓練中採用多類交叉熵作為損失函式,並作為網路的輸出,預測階段定義網路的輸出為分類器得到的機率資訊。

  注意: 訓練程式應該返回一個陣列,第一個返回引數必須是 avg_cost。訓練器使用它來計算梯度。

  Optimizer 配置

  在下面的 Adam optimizer,learning_rate 是學習率,與網路的訓練收斂速度有關係。

  def optimizer_program():

  return fluid.optimizer.Adam(learning_rate=0.001)

  訓練模型

  Data Feeders 配置

  cifar.train10() 每次產生一條樣本,在完成shuffle和batch之後,作為訓練的輸入。

  Trainer 程式的實現

  我們需要為訓練過程制定一個main_program, 同樣的,還需要為測試程式配置一個test_program。定義訓練的 place ,並使用先前定義的最佳化器。

  訓練主迴圈以及過程輸出

  在接下來的主訓練迴圈中,我們將透過輸出來來觀察訓練過程,或進行測試等。

  訓練

  透過trainer_loop函式訓練, 這裡我們只進行了2個Epoch, 一般我們在實際應用上會執行上百個以上Epoch

  注意: CPU,每個 Epoch 將花費大約15~20分鐘。這部分可能需要一段時間。請隨意修改程式碼,在GPU上執行測試,以提高訓練速度。

  train_loop()

  一輪訓練log示例如下所示,經過1個pass, 訓練集上平均 Accuracy 為0.59 ,測試集上平均 Accuracy 為0.6 。

圖13是訓練的分類錯誤率曲線圖,執行到第200個pass後基本收斂,最終得到測試集上分類錯誤率為8.54%。

  圖13. CIFAR10資料集上VGG模型的分類錯誤率

  應用模型

  可以使用訓練好的模型對圖片進行分類,下面程式展示瞭如何載入已經訓練好的網路和引數進行推斷。

  生成預測輸入資料

  dog.png 是一張小狗的圖片. 我們將它轉換成 numpy 陣列以滿足feeder的格式.

  Inferencer 配置和預測

  與訓練過程類似,inferencer需要構建相應的過程。我們從params_dirname 載入網路和經過訓練的引數。 我們可以簡單地插入前面定義的推理程式。 現在我們準備做預測。

  總結

  傳統影像分類方法由多個階段構成,框架較為複雜,而端到端的CNN模型結構可一步到位,而且大幅度提升了分類準確率。本文我們首先介紹VGG、GoogleNet、ResNet三個經典的模型;然後基於CIFAR10資料集,介紹如何使用PaddlePaddle配置和訓練CNN模型,尤其是VGG和ResNet模型;最後介紹如何使用PaddlePaddle的API介面對圖片進行預測和特徵提取。對於其他資料集比如ImageNet,配置和訓練流程是同樣的,請參照Github https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/README_cn.md。

  參考文獻

  [1] G.E. Hinton, N. Srivastava, A. Krizhevsky, I. Sutskever, and R.R. Salakhutdinov. Improving neural networks by preventing co-adaptation of feature detectors. arXiv preprint arXiv:1207.0580, 2012.

  [2] K. Chatfield, K. Simonyan, A. Vedaldi, A. Zisserman. Return of the Devil in the Details: Delving Deep into Convolutional Nets. BMVC, 2014。

  [3] K. He, X. Zhang, S. Ren, J. Sun. Deep Residual Learning for Image Recognition. CVPR 2016.

  [4] He, K., Zhang, X., Ren, S., and Sun, J. Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification. ArXiv e-prints, February 2015.

  [5] http://deeplearning.net/tutorial/lenet.html

  [6] https://www.cs.toronto.edu/~kriz/cifar.html

  [7] http://cs231n.github.io/classification/


相關文章