介紹
Inception 又叫Googlenet是Google于2014年為參加ILSVRC大賽而提出的CNN分類模型。它發(fā)表于2014年的CVPR上面。在深度學(xué)習(xí)領(lǐng)域Google出品幾乎必為精品,Inception也不例外。
它乍看上去像是蠻復(fù)雜的,但細看其結(jié)構(gòu)就會發(fā)現(xiàn)它其實就是用一個個Inception module給堆起來的。它的設(shè)計充滿了科學(xué)理論與工程實踐的結(jié)合,是一個典型的data scientist與軟件工程師結(jié)合搞出來的東東。
在此之前經(jīng)典的CNN模型像LeNet/Alexnet/VGG等無不是一個模子即使用Conv/Pool/Normalization/Activation等層來不斷累積而成。模型對數(shù)據(jù)集概率分布的表達能力則往往通過單純增加模型的深度(層數(shù))或?qū)挾龋▽拥腸hannels數(shù))來提高(當(dāng)然這也亦是當(dāng)下深度學(xué)習(xí)領(lǐng)域的共識)。但這樣進行網(wǎng)絡(luò)設(shè)計一般會等來巨量的計算開銷,因為每一層channels數(shù)目的增加都會隨著層深而指數(shù)級增加,這大大地限制了模型的實際應(yīng)用。
GoogleNet團隊在考慮網(wǎng)絡(luò)設(shè)計時不只注重增加模型的分類精度,同時也考慮了其可能的計算與內(nèi)存使用開銷。他們借鑒了諸多前人的觀點與經(jīng)驗(尤其是Network in Network中使用1x1 conv及AvgPool的idea),想通過一種spared layer architecture來實現(xiàn)較優(yōu)的多維度特征表達,然后通過對這種結(jié)構(gòu)進行疊加,中間不時再插入一些MaxPool層以減少參數(shù)數(shù)目(從而節(jié)省內(nèi)存與計算開銷),最終就行成了Inception v1分類模型。它作為2014年ILSVRC圖像分類大賽中的最優(yōu)選手,取得了非常大的成功。后來GoogleNet團隊再接再厲接連提出了融入更新思想,同時分類精度也更高的Inception v2/Inception v3/Inception v4及Inception-Resnet等模型,從而將Inception的思想推至了當(dāng)下的巔峰。此是后話,欲知Inception系列模型的淵源還是須從這個最早的Inception v1開始。
Inception模塊設(shè)計
傳統(tǒng)的CNN模型設(shè)計,其能力增加往往通過增加層數(shù)及增寬層的通道數(shù)來實現(xiàn)。但這會帶來兩個問題,首先這兩種設(shè)計思路都是在高密度計算的單元上(如FC層/Conv層)實現(xiàn)的,它會帶來大量的訓(xùn)練參數(shù)增加,而過多的參數(shù)往往意味著容易出現(xiàn)過擬合;其次單純曾加密集計算單元的層數(shù)或每層寬度都會招至后須擴展的指數(shù)級的計算量增加。
顯然通過使用一種稀疏的層次結(jié)構(gòu)而非使用像FC/Conv這樣的全級聯(lián)式的結(jié)構(gòu)有助于計算開銷的降低。而且一旦這種稀疏的結(jié)構(gòu)設(shè)計良好可以有效地擴大表達特征的范圍,從而更有效地對圖片信息進行表達。可過于稀疏的結(jié)構(gòu)計算一般都不大高效(當(dāng)下用于CPU/GPU上的一些高效加速庫多是在密集計算上進行優(yōu)化的),同時也會帶來較多的cache miss及內(nèi)存地址非連續(xù)搜索的overhead。最終GoogleNet團隊選擇了中間路線即使用由密集計算子結(jié)構(gòu)組合而成的稀疏模塊來用于特征提取及表達,這就是用于構(gòu)建Inception v1的Inception module如下圖中a所示。
其中1x1/3x3/5x5這三種Conv kernels的選擇決定是基于方便(因為這幾種kernels用的多啊,而且比較容易對齊,padding)來定的(汗)。
圖a的這個基本module顯然在計算開銷上隱藏著重大問題即它的輸出將每個不同尺度的conv的輸出級連起來用于下一個module的輸入。這勢必會招至計算量的指數(shù)級增加。因此作者借鑒Network in Network中的idea,在每個子conv層里使用了1x1的conv來作上一層的輸入feature maps的channels數(shù)縮減、歸總。如此每個Inception module里的計算都由各自的1x1 conv來隔離,從而不會像傳統(tǒng)CNN深度模型那樣隨著深度增加其計算量也指數(shù)級增加。這就是最終用于構(gòu)建Inception 網(wǎng)絡(luò)的基礎(chǔ)模塊inception即圖b。
GoogleNet
上表即為GoogleNet的網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計概況。可以看出并非整個網(wǎng)絡(luò)都是由Inception module來組成。它的前面幾層采用了類似于經(jīng)典網(wǎng)絡(luò)的那種單尺度Conv/Pool/ReLu的設(shè)計,這主要是出于用于進行模型計算的系統(tǒng)架構(gòu)的實際考慮。
作者表明我們可以靈活地根據(jù)自己所有的硬件/軟件/系統(tǒng)等情況來選擇Inception網(wǎng)絡(luò)中各模塊的使用(modules數(shù)目及其上的conv的channels數(shù))。而上表所列的這個有著22個參數(shù)層的GoogleNet只是在作者他們實驗所用的機器上得到的較優(yōu)網(wǎng)絡(luò)。因此我們不必過于拘泥于它的具體實現(xiàn)細節(jié),只需了解它的本質(zhì)設(shè)計思想即可。
模型的最后會選通過一個AvgPool層來處理最終的feature maps,然后再由FC層匯總生成1000個輸出,進而由Softmax來得到1000類的概率分布。隨著CNN模型的加深,它最終表達的特征也愈加地抽象,只使用最后層次的特征可能會招至對小尺度目標的忽略,因此作者分別在中間及較靠后層的地方插入了兩個獨立的分類層以在訓(xùn)練時協(xié)同發(fā)揮影響。下圖即為最終的GoogleNet模型。
模型訓(xùn)練
這算是當(dāng)時最為復(fù)雜的模型了,多達22層。它的訓(xùn)練難度顯然也是空前的。作者使用他們內(nèi)部的DistBelief分布式系統(tǒng)對模型進行訓(xùn)練,中間使用了模型并行與數(shù)據(jù)并行兩種多節(jié)點并行方式(這個難度其實還是蠻高的,玩過多節(jié)點訓(xùn)練的人都懂!)。另外他們還使用了Async sgd的方式進行模型參數(shù)更新(這使得模型的訓(xùn)練難度更是增加了,沒辦法估計他們當(dāng)時也是覺著訓(xùn)練時間實在太久了,所以才會選擇使用Async sgd這種加速收斂的方式吧)。
此外作者使用了集成多個模型(6個結(jié)構(gòu)相同,1個略不同;相用相同的初始化,只是不同的shuffle過的數(shù)據(jù)集)來提高分類準確率的做法。同時也使用了像resize/不同scales/鏡像及color distortation等多種數(shù)據(jù)增強的方式。
實驗結(jié)果
下表中即為GoogleNet網(wǎng)絡(luò)與其它模型的結(jié)果對比。
代碼分析
當(dāng)下大多數(shù)的經(jīng)典模型都已經(jīng)在Intel caffe里面有了良好實現(xiàn)。我們甚至不需要GPU,單單用自己家datacenter里面的CPUs也能玩轉(zhuǎn)下模型訓(xùn)練及推理。
下面為data layer層,與VGG等的并無差別。
layer {
name: "data"
type: "Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
mirror: true
crop_size: 224
mean_value: 104
mean_value: 117
mean_value: 123
}
data_param {
source: "examples/imagenet/ilsvrc12_train_lmdb"
batch_size: 32
backend: LMDB
}
}
以下數(shù)層則為網(wǎng)絡(luò)中inception_3a 模塊的組成情況。
layer {
name: "inception_3a/1x1"
type: "Convolution"
bottom: "pool2/3x3_s2"
top: "inception_3a/1x1"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 64
kernel_size: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0.2
}
}
}
layer {
name: "inception_3a/relu_1x1"
type: "ReLU"
bottom: "inception_3a/1x1"
top: "inception_3a/1x1"
}
layer {
name: "inception_3a/3x3_reduce"
type: "Convolution"
bottom: "pool2/3x3_s2"
top: "inception_3a/3x3_reduce"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 96
kernel_size: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0.2
}
}
}
layer {
name: "inception_3a/relu_3x3_reduce"
type: "ReLU"
bottom: "inception_3a/3x3_reduce"
top: "inception_3a/3x3_reduce"
}
layer {
name: "inception_3a/3x3"
type: "Convolution"
bottom: "inception_3a/3x3_reduce"
top: "inception_3a/3x3"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 128
pad: 1
kernel_size: 3
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0.2
}
}
}
layer {
name: "inception_3a/relu_3x3"
type: "ReLU"
bottom: "inception_3a/3x3"
top: "inception_3a/3x3"
}
layer {
name: "inception_3a/5x5_reduce"
type: "Convolution"
bottom: "pool2/3x3_s2"
top: "inception_3a/5x5_reduce"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 16
kernel_size: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0.2
}
}
}
layer {
name: "inception_3a/relu_5x5_reduce"
type: "ReLU"
bottom: "inception_3a/5x5_reduce"
top: "inception_3a/5x5_reduce"
}
layer {
name: "inception_3a/5x5"
type: "Convolution"
bottom: "inception_3a/5x5_reduce"
top: "inception_3a/5x5"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 32
pad: 2
kernel_size: 5
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0.2
}
}
}
layer {
name: "inception_3a/relu_5x5"
type: "ReLU"
bottom: "inception_3a/5x5"
top: "inception_3a/5x5"
}
layer {
name: "inception_3a/pool"
type: "Pooling"
bottom: "pool2/3x3_s2"
top: "inception_3a/pool"
pooling_param {
pool: MAX
kernel_size: 3
stride: 1
pad: 1
}
}
layer {
name: "inception_3a/pool_proj"
type: "Convolution"
bottom: "inception_3a/pool"
top: "inception_3a/pool_proj"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 32
kernel_size: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0.2
}
}
}
layer {
name: "inception_3a/relu_pool_proj"
type: "ReLU"
bottom: "inception_3a/pool_proj"
top: "inception_3a/pool_proj"
}
layer {
name: "inception_3a/output"
type: "Concat"
bottom: "inception_3a/1x1"
bottom: "inception_3a/3x3"
bottom: "inception_3a/5x5"
bottom: "inception_3a/pool_proj"
top: "inception_3a/output"
}
最后則是其AvgPool/DropOut/FC的使用。及最終所生成的loss與accuracy top-1/top-5實現(xiàn)。
layer {
name: "pool5/7x7_s1"
type: "Pooling"
bottom: "inception_5b/output"
top: "pool5/7x7_s1"
pooling_param {
pool: AVE
kernel_size: 7
stride: 1
}
}
layer {
name: "pool5/drop_7x7_s1"
type: "Dropout"
bottom: "pool5/7x7_s1"
top: "pool5/7x7_s1"
dropout_param {
dropout_ratio: 0.4
}
}
layer {
name: "loss3/classifier"
type: "InnerProduct"
bottom: "pool5/7x7_s1"
top: "loss3/classifier"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 1000
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "loss3/loss3"
type: "SoftmaxWithLoss"
bottom: "loss3/classifier"
bottom: "label"
top: "loss3/loss3"
loss_weight: 1
}
layer {
name: "loss3/top-1"
type: "Accuracy"
bottom: "loss3/classifier"
bottom: "label"
top: "loss3/top-1"
include {
phase: TEST
}
}
layer {
name: "loss3/top-5"
type: "Accuracy"
bottom: "loss3/classifier"
bottom: "label"
top: "loss3/top-5"
include {
phase: TEST
}
accuracy_param {
top_k: 5
}
}
參考文獻
- Going deeper with convolutions, Christian-Szegedy, 2014
- https://github.com/intel/caffe