使用PaddleFluid和TensorFlow实现图像分类网络SE_ResNeXt | 文末超大福利
專欄介紹:Paddle Fluid 是用來讓用戶像 PyTorch 和 Tensorflow Eager Execution 一樣執行程序。在這些系統中,不再有模型這個概念,應用也不再包含一個用于描述 Operator 圖或者一系列層的符號描述,而是像通用程序那樣描述訓練或者預測的過程。
本專欄將推出一系列技術文章,從框架的概念、使用上對比分析 TensorFlow 和 Paddle Fluid,為對 PaddlePaddle 感興趣的同學提供一些指導。
視覺(vision)、自然語言處理(Nature Language Processing, NLP)、語音(Speech)是深度學習研究的三大方向。三大領域各自都誕生了若干經典的模塊,用來建模該領域數據所蘊含的不同特性的模式。上一篇文章介紹了 PaddleFluid 和 TensorFlow 的設計和核心概念,這一篇我們從圖像任務開始,使用 PaddleFluid 和 TensorFlow 來寫一個完全相同的網絡,通過這種方式了解我們的使用經驗如何在不同平臺之間遷移,從而幫助我們選擇便利的工具,集中于機器學習任務本身。?
這一篇我們使用圖像分類中的 SE_ResNeXt [1][2] 作為實踐任務。?
SE 全稱 Sequeeze-and-Excitation,SE block 并不是一個完整的網絡結構,而是一個子結構,可以嵌到其他分類或檢測模型中。SENet block 和 ResNeXt 的結合在 ILSVRC 2017 的分類項目中取得 了第一名的成績。在 ImageNet 數據集上將 top-5 錯誤率從原先的最好成績 2.991% 降低到 2.251%。
如何使用代碼
本篇文章配套有完整可運行的代碼,代碼包括以下幾個文件:
在終端運行下面的命令便可以使用 PaddleFluid 進行模型的訓練,訓練過程如果未在當前工作目錄下的 cifar-10-batches-py 目錄中檢測到訓練數據(無需手動創建 cifar-10-batches-py 文件夾),會自動從網絡中下載訓練及測試數據。
在命令行運行下面的命令便可以使用 TensorFlow 進行模型的訓練,訓練中模型驗證以及模型保存:
注:本系列的最新代碼,可以在 github 上的 TF2Fluid [3] 中隨時 獲取。
背景介紹
卷積神經網絡在圖像類任務上取得了巨大的突破。卷積核作為卷積神經網絡的核心,通常被看做是在局部感受野上將空間上(spatial)的信息和通道(channel)上的信息進行聚合的信息聚合體。 通常,一個卷積 Block 由卷積層、非線性層和下采樣層(Pooling 層)構成,一個卷積神經網絡則由一系列堆疊的卷積 block 構成。隨著卷積 Block 的層數的加深,感受野也在不斷擴大,最終達到從全局感受野上捕獲圖像的特征來進行圖像的描述的目標。?
Squeeze-and-Excitation (SE)模塊?
圖像領域很多研究工作從不同角度出發研究如何構建更加強有力的網絡學習圖像特征。例如,如 Inception 結構中嵌入了多尺度信息:使用多個不同卷積核,聚合多種不同感受野上的特征來獲得性能增益;將 Attention 機制引入到空間(spatial)維度上等,都獲得了相當不錯的成果。
除了上角度,很自然會想到,是否可以從考慮特征通道(channel)之間的關系出發,增強網絡性能?這正是 Squeeze-and-Excitation 模塊的設計動機。?
SE block 由兩個非常關鍵的操作,Squeeze 和 Excitation 構成, 其核心思想是:顯式地建模特征通道之間的相互依賴關系,通過自適應學習的方式來獲取每個特征通道的重要程度,然后依照這個重要程度去提升有用的特征(有用 feature map 權重增大 )并抑制對當前任務用處不大的特征(無效或效果小的 feature map 權重減小),達到更好的模型效果。?
下圖是 SE Block 示意圖,來自 參考文獻 [1] 中的 Figure 1。
▲?圖 1. SE block示意圖
Squeeze 操作,圖 1 中的:?
通過一個全局的 pooling 操作,沿著空間維度進行特征壓縮,將每個二維的特征通道(channel)變成一個實數。這個實數一定程度上具有全局的感受野。Squeeze 操作的輸出維度和輸入 feature map 的特征通道數完全相等,表征著在特征通道維度上響應的全局分布,Squeeze 使得靠近輸入的層也可以獲得全局的感受野,這在很多任務中都是非常有用。?
Excitation 操作,圖 1 中的:?
類似與門機制。通過參數WW的作用,為每個特征通道學習一個權重,其中 可學習參數WW用來顯式地建模特征通道間的相關性。?
Scale 加權操作:?
最后一步,將 Excitation 的輸出的權重看做是經過特征選擇后每個特征通道的重要性,然后通過 scale 逐通道對原始特征進行 reweight,完成在通道維度上的對原始特征的重標定(recalibration)。?
當 SE block 嵌入原有一些分類網絡中時,不可避免會增加需要學習的參數和 引入計算量,但是 SE block 帶來的計算量較低,效果面改善依然非常可觀,對大多數任務還是可以接受。?
SE模塊和Residual Connection疊加?
圖 2 是 SE 模塊和 ResNet 中的 Residual Connection 進行疊加的原理示意圖,來自參考文獻 [1] 中的 Figure 3。
▲?圖 2. SE 模塊與Residual Connection疊加
SE 模塊可以嵌入到含有 Residual Connection 的 ResNet 的模型中,圖 2 右是 SE 疊加 Residual Connection 的示意圖。論文中建議在 Residual Connection 的跨層連接進行 Addition 操作前,而不是 Addition 操作之后疊加 SE 模塊:前對分支上 Residual 的特征進行了特征重標定(recalibration),是由于如果主干上存在 0 ~ 1 的 scale 操作(extraction 步驟),在網絡較深時候,反向傳播算法進行到網絡靠近輸入層時,容易出現梯度消散的情況,導致模型難以優化。?
ResNeXt 模型結構?
深度學習方法想要提高模型的準確率,通常會選擇加深或加寬網絡。但是隨著網絡深度或者寬度的增加,網絡參數(例如:特征通道數,filter size 等)量也顯著增加,計算開銷增大。ResNeXt 網絡結構設計的初衷是希望在不增加參數復雜度的前提下,提高網絡的性能,同時還減少了超參數的數量。?
在引出 ResNeXt 網絡之前,我們首先回顧圖像分類領域兩個非常重要的工作:VGG 和 Inception 網絡(這兩個網絡的設計細節 PaddleBook 的圖像分類 [4] 一節有詳細的介紹)。VGG 網絡通過堆疊相同形狀的網絡模塊,這一簡單策略來構建深度網絡,之后的 ResNet 也同樣使用了這一策略。這個策略降低了超參數的選擇,在許多不同的數據集和任務上依然 有效工作,展示了很好的魯棒性。?
Inception 的網絡結果經過精心設計,遵循:split-transform-merge 策略。在一個 Inception 模型中:(1)split:輸入通過 1×1卷積被切分到幾個低維 feature map;(2)transform:split 步驟的輸出經過一組(多個)filter size 不同的特定濾波器(filters)映射變換;(3)merge:通過 concatenation 將 transform 步驟的結果融合到一起。
Inception 模型期望通過較低的計算量來近似大的密集的濾波器的表達能力。盡管 Inception 模型的精度很好,但是每個 Inception 模塊要量身定制濾波器數量、尺寸,模塊在每一階段都要改變,特別是當 Inception 模塊用于新的數據或者任務時如何修改沒有特別通用的方法。?
ResNeXt 綜合了 VGG 和 Inception 各自的優點,提出了一個簡單架構:采用? VGG/ResNets 重復相同網絡層的策略,以一種簡單可擴展的方式延續 split-transform-merge 策略,整個網絡的 building block 都是一樣的,不用在每個 stage 里對每個 building block 的超參數進行調整,只用一個結構相同的 building block,重復堆疊即可形成整個網絡。?
實圖 3 來自參考文獻 [2] 中的Figure 1,展示了 ResNeXt 網絡的基本 building block。
▲?圖 3. ResNeXt網絡的基本building block
解釋圖 3 之前要先介紹一個名詞 cardinality,是指 building block 中變換集合的大小(the size of the set of transformation)。圖 3 中 cardinality=32,將左邊的 64 個卷積核分成了右邊 32 條不同 path,最后將 32 個 path 的輸出向量的所有通道對應位置相加,再與 Residual connection 相加。?
圖 3 再結合上一節中介紹的“SE 模塊和 Residual Connection 疊加”,便構成了最終的 SE_ResNeXt 網絡的 building block,重復堆疊即可形成整個 ResNeXt。
CIFAR-10 數據集介紹
至此,介紹完 ResNeXt 模型的模型原理和基本結構,我們準備開始分別使用 PaddleFluid 和 TensorFlow 構建訓練任務。
這一篇我們使用 cifar10 數據集 [5][6] 作為實驗數據。cifar-10 數據集包含 60000 個 32*32 的彩色圖像,共有 10 類,圖 4 是 cifar10 數據集的 10 個類別。圖 4 是 cifar-10 數據集的 10 個類別示意圖。
▲?圖 4. cifar10數據集
cifar10 數據集有 50000 個訓練圖像和 10000 個測試圖像,被劃分為 5 個訓練塊和 1 個測試塊,每個塊有 10000 個圖像。測試塊包含從每類隨機選擇的 1000 個圖像。
下載數據
運行訓練程序時, 如果在當前執行目錄下沒有?cifar-10-batches-py 目錄,或是?cifar-10-batches-py 目錄下沒有已經下載好的數據,PaddleFluid 和 TensorFlow 的數據讀取模塊會調用 data_utils [7] 中的 download_data 方法自動 從網站上下載 cifar-10 數據集,無需手動下載。
加載cifar-10數據集
PaddleFluid?
1. 定義網絡的輸入層:
上一篇基本使用概念中介紹過 PaddleFluid 模型通過 fluid.layers.data 來接收輸入數據。圖像分類網絡以圖片以及圖片對應的類別標簽作為網絡的輸入:
images?=?fluid.layers.data(name="image",?shape=IMG_SHAPE,?dtype="float32")
labels?=?fluid.layers.data(name="label",?shape=[1],?dtype="int64")
在上面的代碼片段中, fluid.layers.data 中指定的 shape 無需顯示地指定第 0 維 batch size,框架會自動補充第 0 維,并在運行時填充正確的 batch size。?
一副圖片是一個 3-D Tensor。PaddleFluid 中卷積操作使用 channel-first 的數據輸入格式。因此在接收 原始圖像 數據時,shape 的三個維度其含義分別是:channel、圖片的寬度以及圖片的高度。?
2. 使用 python 語言編寫的 data reader 函數?
PaddleFluid 中通過 DataFeeder 接口來為對應的 ?fluid.data.layersfeed 數據,調用方式如下:
feeder?=?fluid.DataFeeder(place=place,?feed_list=[images,?labels])
在上面的代碼片段中:?
1. 僅需要用戶編寫 train_data() 這樣一個 python 的 generator。這個函數 是 PaddleFluid 要求提供的 data reader 接口,函數名字不限。?
2. 實現這個 data reader 接口時只需要考慮:如何從原始數據文件中讀取數據,返回一條以 numpy ndarrary 格式的訓練數據。
3. 調用 paddle.batch(train_data(), batch_size=conf.batch_size) 接口會將數據首先讀入一個 pool 中,進行 shuffle,然后從 pool 中依次取一個一個的 mini-batch。?
完整代碼請參考?train_data() [8],這里不再直接粘貼代碼片段。
TensorFlow?
1. 定義 placeholder?
在 TensorFlow 中,通過定義 placeholder 這樣一個特殊的 Tensor 來接受輸入數據。
LBL_COUNT?=?10
images?=?tf.placeholder(
????tf.float32,?shape=[None,?IMG_SHAPE[1],?IMG_SHAPE[2],?IMG_SHAPE[0]])
labels?=?tf.placeholder(tf.float32,?shape=[None,?LBL_COUNT])
需要注意的是:?
TensorFlow 中的卷積操作默認使用 channel-last 數據格式( 也可以在調用卷積的接口中使用 channel-first 的數據格式),images 這個 placeholder 的 shape 和 PaddleFluid 中 images 形狀不同。?
在 placeholder 中,batch size 這樣運行時才可以確定具體數值的維度使用 None 代替。
2. 通過 feeding 字典為placeholder 提供數據?
TensorFlow 通過 session 管理運行一個計算圖,在 調用 session 的 run 方法時,提供一個 feeding 字典,將 mini-batch 數據 feed 給 placeholder。?
下面的代碼片實現了 加載 cifar-10 數據進行訓練。
image_test,?label_test?=?train_data()
total_train_sample?=?len(image_train)
for?epoch?in?range(1,?conf.total_epochs?+?1):
????????for?batch_id,?start_index?in?enumerate(
????????????????range(0,?total_train_sample,?conf.batch_size)):
????????????end_index?=?min(start_index?+?conf.batch_size,
????????????????????????????total_train_sample)
????????????batch_image?=?image_train[start_index:end_index]
????????????batch_label?=?label_train[start_index:end_index]
????????????train_feed_dict?=?{
????????????????images:?batch_image,
????????????????labels:?batch_label,
???????????????......
????????????}
上面代碼片段中的 train_data() [7]?完成了原始數據的讀取。與 PaddleFluid 中只需考慮讀取一條數據由框架完成組 batch 不同,train_data 讀取所有數據,由用戶程序控制 shuffle 和組 batch。
構建網絡結構
使用不同深度學習框架的核心就是使用框架提供的算子構建神經網絡模型結構。PaddleFluid 和 TensorFlow 各自提供的基本算子的詳細說明,可以在各自官網獲取。
這一篇提供了代碼文件 SE_ResNeXt_fluid.py [9] 和 SE_ResNeXt_tensorflow.py [10] 分別是用 PaddleFluid 和 TensorFlow 寫成的 ResNeXt 網絡,兩個文件中的網絡結構完全相同,并且代碼結構也完全相同。核心都是 SE_ResNeXt 這個類,如下面代碼片段所示:
????def?__init__(self,?x,?num_block,?depth,?out_dims,?cardinality,
?????????????????reduction_ratio,?is_training):
????????...
????def?transform_layer(self,?x,?stride,?depth):
????????...
????def?split_layer(self,?input_x,?stride,?depth,?layer_name,?cardinality):
????????...
????def?transition_layer(self,?x,?out_dim):
????????...
????def?squeeze_excitation_layer(self,?input_x,?out_dim,?reduction_ratio):
????????...
????def?residual_layer(self,?input_x,?out_dim,?layer_num,?cardinality,?depth,
???????????????????????reduction_ratio,?num_block):
????????...
????def?build_SEnet(self,?input_x):
????????...
SE_ResNeXt_fluid.py [9]?和 SE_ResNeXt_tensorflow.py [10]?中 SE_ResNeXt 類有著完全相同的成員函數,可以通過對比在兩個平臺下實現同樣功能的代碼,了解如何使用經驗如何在兩個平臺之間遷移。
2-D卷積層使用差異?
2-D 卷積是圖像任務中的一個重要操作,卷積核在 2 個軸向上平移,卷積核的每個元素與被卷積圖像對應位置相乘,再求和。卷積核的不斷移動會輸出一個新的圖像,這個圖像完全由卷積核在各個位置時的乘積求和的結果組成。圖 5 是當輸入圖像是 4-D Tensor 時(batch size 這里固定是 1,也就是 4-D Tensor 的第一維固定為 1),2-D 卷積的可視化計算過程。
▲?圖 5. RGB 3通道圖像輸入上的2-D卷積
作者 : Martin G?rner / Twitter: @martin_gorner?
卷積計算的一些細節在 PaddleFluid 和 TensorFlow 中略有不同,在這里我們稍作解釋。?
下面是 PaddleFluid 2-D 卷積調用接口:
??input,
??num_filters,
??filter_size,
??stride=1,
??padding=0,
??dilation=1,
??groups=None,
??act=None,
??...)
1. 在PaddleFluid卷積使用"channel-first"輸入數據格式,也就是常說的“NCHW”格式。
如果輸入圖像是“channel-last”格式,可以在卷積操作之前加入 fluid.layers.transpose operator,對輸入數據的各個軸進行換序。?
2. PaddleFluid 中卷積計算的 padding 屬性用戶需要自己進行計算,最終輸出圖像的 height 和 width 由下面公式計算得到:
?padding 屬性 可以接受一個含有兩個元素的 python list,分別指定對圖像 height 方向和 width 方向填充。如果 padding 是一個整型數而不是一個 list 使,認為 height 和 width 方向填充相同多個 0。?
這個邏輯同樣適用于 stride 和 dilation 參數。
下面是 TensorFlow 2-D 卷積調用接口:
??inputs,
??filters,
??kernel_size,
??strides=(1,?1),
??padding='valid',
??data_format='channels_last'
??...)
1. 在 TensorFlow 卷積默認使用"channel-last"輸入數據格式,也就是常說的“NHWC”格式。
2. TensorFlow 中卷積計算的 padding 屬性可以指定兩種模式:“valid”:不填充;"same":卷積計算完畢輸出圖像的寬度和高度與輸入圖像相同。
正則項使用差異
L2 正則項作為預防過擬合的手段之一,在神經網絡訓練中有著重要作用。PaddleFluid 平臺和 TensorFlow 中添加 L2 正則的 使用接口略有不同。?
PaddleFluid?
在 PaddleFluid 中使用 L2 正則這樣的標準正則項較為簡單,L2 正則作為 optimizer 的一個參數,直接傳遞正則項系數即可。
????learning_rate=conf.learning_rate,
????momentum=0.9,
????regularization=fluid.regularizer.L2Decay(conf.weight_decay))
TensorFlow
在 TensorFlow 中,L2 正則作為損失函數的一部分,需要顯示地為網絡中每一個需要添加 L2 正則項的可學習參數添加 L2 正則。
optimizer?=?tf.train.MomentumOptimizer(
????learning_rate=learning_rate,
????momentum=conf.momentum,
????use_nesterov=conf.use_nesterov)
train?=?optimizer.minimize(cost?+?l2_loss?*?conf.weight_decay)
以上部分是 PaddleFluid 和 TensorFlow 使用中需要關注的一些差異,ResNeXt 模型在 兩個平臺訓練的其它接口調用細節,可以在代碼 SE_ResNeXt_fluid.py 和 SE_ResNeXt_tensorflow.py 中找到,這里不再粘貼所有代碼,整個過程都遵循:?
定義網絡結構;?
加載訓練數據;
在一個 for 循環中讀取 一個一個 mini-batch 數據,調用網絡的前向和反向計算,調用優化過程。
總結
這一篇我們從圖像領域的圖像分類問題入手,使用 PaddleFluid 和 TensorFlow 實現完全相同 ResNeXt 網絡結構。 來介紹:?
1. 在 PaddleFluid 和 TensorFlow 中如何讀取并為網絡 feed 圖片數據;
2. 如何使用 PaddleFluid 和 TensorFlow 中的 2-d 卷積,2-d pooling,pad 等圖像任務中常用計算單元;
3. 如何運行一個完整的訓練任務,在訓練中對測試集樣本進行測試。?
可以看到 PaddleFluid 和 TensorFlow 的圖像操作接口高度相似。PaddleFluid 能夠支持與 TensorFlow 同樣豐富的圖像操作,這也是今天主流深度學習框架共同的選擇。作為用戶,我們的使用經驗可以在平臺之間 非常 簡單的進行遷移,只需要略微關注接口調用細節的一些差異即可。?
在下面的篇章中,我們將進一步在 NLP 任務中對比如果使用 PaddleFluid 和 TensorFlow 中的循環神經網絡單元處理序列輸入數據。并且逐步介紹多線程,多卡等主題。
參考文獻
[1].?Hu J, Shen L, Sun G. Squeeze-and-excitation networks[J]. arXiv preprint arXiv:1709.01507, 2017.?
[2]. Xie S, Girshick R, Dollár P, et al. Aggregated residual transformations for deep neural networks[C]//Computer Vision and Pattern Recognition (CVPR), 2017 IEEE Conference on. IEEE, 2017: 5987-5995.?
[3].?TF2Fluid:?https://github.com/JohnRabbbit/TF2Fluid
[4]. PaddleBook圖像分類
http://www.paddlepaddle.org/docs/develop/book/03.image_classification/index.cn.html
[5]. Learning Multiple Layers of Features from Tiny Images, Alex Krizhevsky, 2009.?
https://www.cs.toronto.edu/~kriz/learning-features-2009-TR.pdf
[6]. The CIFAR-10 dataset: https://www.cs.toronto.edu/~kriz/cifar.html
[7].?data_utils
https://github.com/JohnRabbbit/TF2Fluid/blob/master/02_image_classification/data_utils.py#L35
[8].?train_data()
https://github.com/JohnRabbbit/TF2Fluid/blob/master/02_image_classification/cifar10_fluid.py#L43
[9].?SE_ResNeXt_fluid.py
https://github.com/JohnRabbbit/TF2Fluid/blob/master/02_image_classification/SE_ResNeXt_fluid.py
[10].?SE_ResNeXt_tensorflow.py
https://github.com/JohnRabbbit/TF2Fluid/blob/master/02_image_classification/SE_ResNeXt_tensorflow.py
#福 利 時 間#
以下是簡單粗暴送福利環節
PaperWeekly?× PaddlePaddle?
深度學習有獎調研
? 50份禮品坐等抱走??
限量版T恤√小白板√機械鍵盤√
?
?參與方式?
掃描下方二維碼提交問卷
▼
我們將從成功作答的用戶中抽取50位
贈送限量版禮品一份
總結
以上是生活随笔為你收集整理的使用PaddleFluid和TensorFlow实现图像分类网络SE_ResNeXt | 文末超大福利的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基线系统需要受到更多关注:基于词向量的简
- 下一篇: 貌离神合的RNN与ODE:花式RNN简介