GoogleNet網絡詳解與keras實作
- GoogleNet網絡詳解與keras實作
-
- GoogleNet系列網絡的概覽
- Pascal_VOC資料集
- 第一層目錄
- 第二層目錄
- 第三層目錄
- InceptionV1子產品介紹
- Inception的架構
- GoogleNet的圖檔
- Keras代碼實作
- 為了搭建Inception網絡我們使用了以下政策
- 整個代碼的流程如下
- 實驗結果
- 實驗結果分析
- 本部落格相關引用
-
本部落格旨在給經典的GoogleNet網絡進行詳解與代碼實作,如有不足或者其他的見解,請在本部落格下面留言。
GoogleNet系列網絡的概覽
- InceptionV1,通過把不同尺寸的卷積核如1x1,3x3,5x5進行堆疊增加了網絡對不同尺度的适應性。并且通過在3x3的網絡,5x5的網絡後加入1x1使得網絡的計算複雜度降低,而且提高網絡的非線性的程度,基于更強的表征能力。
- InceptionV2,加入了BatchNormalization層,減少了Internal Variance Shift。使得每一程的輸出的分布都滿足指定的高斯分布,可以防止訓練集與測試集之間分布的不比對,還能加快網絡收斂速度,防止過拟合。
- InceptionV3,在InceptionV3中google将分解的思想用到了極緻,把二維卷積核(NxN)拆成兩個方向上的一維卷積核(Nx1,1xN)。這樣做不僅僅加快了網絡的運算速度,而且由于增加網絡的層數,使得網絡的非線性增加,提高網絡的表征能力。
- InceptionV4,嘗試着把Inception的結構與Resnet的結構進行結合,并設計了一個更深更加優秀的網絡InceptionV4。
在本篇部落格中,我們将實作一個類似于InceptionV2的結構,并用VOC2012的資料集進行網絡的訓練,驗證,與測試。為了快速開發,本次我們把Keras作為代碼的架構。
Pascal_VOC資料集
Pascal VOC為圖像識别,檢測與分割提供了一整套标準化的優秀的資料集,每一年都會舉辦一次圖像識别競賽。下面是VOC2012,訓練集(包括驗證集)的下載下傳位址。
VOC2012裡面有20類物體的圖檔,圖檔總共有1.7萬張。我把資料集分成了3個部分,訓練集,驗證集,測試集,比例為8:1:1。
下面是部分截圖:
第一層目錄
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX9UERNlXRU9EMVpWTmZEWjZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DM0ATN1czMxEzNyATM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
第二層目錄
第三層目錄
接着我們使用keras代碼來使用這個資料集,代碼如下:
IM_WIDTH= #圖檔寬度
IM_HEIGHT= #圖檔高度
batch_size= #批的大小
#train data
train_datagen = ImageDataGenerator(
rotation_range=,
width_shift_range=,
height_shift_range=,
shear_range=,
zoom_range=,
horizontal_flip=True,
featurewise_center=True
)
train_generator = train_datagen.flow_from_directory(
train_root,
target_size=(IM_WIDTH, IM_HEIGHT),
batch_size=batch_size,
)
#vaild data
vaild_datagen = ImageDataGenerator(
rotation_range=,
width_shift_range=,
height_shift_range=,
shear_range=,
zoom_range=,
horizontal_flip=True,
featurewise_center=True
)
vaild_generator = train_datagen.flow_from_directory(
vaildation_root,
target_size=(IM_WIDTH, IM_HEIGHT),
batch_size=batch_size,
)
#test data
test_datagen = ImageDataGenerator(
rotation_range=,
width_shift_range=,
height_shift_range=,
shear_range=,
zoom_range=,
horizontal_flip=True,
featurewise_center=True
)
test_generator = train_datagen.flow_from_directory(
test_root,
target_size=(IM_WIDTH, IM_HEIGHT),
batch_size=batch_size,
)
我使用了3個ImageDataGenerator,分别來使用訓練集,驗證集與測試集的資料。使用ImageDataGenerator需要導入相應的子產品,==from keras.preprocessing.image import ImageDataGenerator==。ImageDataGenrator可以用來做資料增強,提高模型的魯棒性.它裡面提供了許多變換,包括圖檔旋轉,對稱,平移等等操作。裡面的flow_from_directory方法可以從相應的目錄裡面批量擷取圖檔,這樣就可以不用一次性讀取所有圖檔(防止記憶體不足)。
InceptionV1子產品介紹
要想了解Googlenet的結構,第一步必須先知道Inception的結構,因為它是由多個Inception的結構組合而成的。如下圖Fig.2所示,(a)表示樸素的版本的inception v1示意圖,(b)表示降維版本的Inception v1示意圖。
Inception的主要思想基于——一個卷積網絡裡面的局部稀疏最優結構往往可以由簡單可複用的密集組合來近似或者替代。就像(a)裡面,1x1,3x3,5x5的卷積層,與3x3的池化層的組合一個inception。這樣做的幾點說明:
- 不同尺寸的卷積核可以提取不同尺度的資訊。
- 采用1x1,3x3,5x5可以友善對齊,padding分别為0,1,2就可以對齊。
- 由于池化層在CNN網絡裡面的成功運用,也把池化層當做組合的一部分。
- 由于Googlenet是好幾個Inception子產品的堆疊,而且往往越後面的Inception子產品提取的是更加進階抽象的特征,而由于進階抽象的特征的時域聯系會降低。(在這裡加上一點個人了解,當提取的特征比較簡單,比如邊緣,輪廓的時候,往往隻需要提取某個像素附近的幾個像素就行了,這時卷積核比較小,沒有問題。但是當提取的特征變得複雜的時候,比如提取的是人的鼻子,耳朵的時候,需要的可能就是某個像素旁邊幾十或者幾百個像素了。當然我說的這些像素指的是特征圖裡面的像素。)是以為了擷取這些進階資訊,我們在後面的Inception子產品裡面需要增大3x3,5x5這些大卷積核的比例。
但是這麼做,問題又來了,如果提高大卷積核的比例,那麼這會意味着計算複雜度的飙升。為此,google的工程師們又提出(b)的這個Inception結構。
Inception的架構
下面的Table.1給出了Googlenet是怎麼由Inception子產品和一些傳統的卷積層與池化層構成的。比較Inception(3a)和Inception(5b),我們可以看到大卷積核的濾波器的個數的比例已經提高了。最後需要注意兩點,該網絡的使用了avg pool來替代第一層全連接配接層,大大降低了參數的個數。後面在avg pool後面加入全連接配接層則是為了友善微調的操作。
GoogleNet的圖檔
根據上面的表格我們可以畫出這樣的一幅圖。
Keras代碼實作
#-*- coding: UTF-8 -*-
from keras.layers import Conv2D, MaxPooling2D, AveragePooling2D, ZeroPadding2D
from keras.layers import Flatten, Dense, Dropout,BatchNormalization
from keras.layers import Input, concatenate
from keras.models import Model,load_model
from keras.preprocessing.image import ImageDataGenerator
from keras.utils import plot_model,np_utils
from keras import regularizers
import keras.metrics as metric
import os
# Global Constants
NB_CLASS=
LEARNING_RATE=
MOMENTUM=
ALPHA=
BETA=
GAMMA=
DROPOUT=
WEIGHT_DECAY=
LRN2D_NORM=True
DATA_FORMAT='channels_last' # Theano:'channels_first' Tensorflow:'channels_last'
USE_BN=True
IM_WIDTH=
IM_HEIGHT=
EPOCH=
train_root='/home/faith/keras/dataset/traindata/'
vaildation_root='/home/faith/keras/dataset/vaildationdata/'
test_root='/home/faith/keras/dataset/testdata/'
IM_WIDTH=
IM_HEIGHT=
batch_size=
#train data
train_datagen = ImageDataGenerator(
rotation_range=,
width_shift_range=,
height_shift_range=,
shear_range=,
zoom_range=,
horizontal_flip=True,
featurewise_center=True
)
train_generator = train_datagen.flow_from_directory(
train_root,
target_size=(IM_WIDTH, IM_HEIGHT),
batch_size=batch_size,
)
#vaild data
vaild_datagen = ImageDataGenerator(
rotation_range=,
width_shift_range=,
height_shift_range=,
shear_range=,
zoom_range=,
horizontal_flip=True,
featurewise_center=True
)
vaild_generator = train_datagen.flow_from_directory(
vaildation_root,
target_size=(IM_WIDTH, IM_HEIGHT),
batch_size=batch_size,
)
#test data
test_datagen = ImageDataGenerator(
rotation_range=,
width_shift_range=,
height_shift_range=,
shear_range=,
zoom_range=,
horizontal_flip=True,
featurewise_center=True
)
test_generator = train_datagen.flow_from_directory(
test_root,
target_size=(IM_WIDTH, IM_HEIGHT),
batch_size=batch_size,
)
#normalization
def conv2D_lrn2d(x,filters,kernel_size,strides=(,),padding='same',data_format=DATA_FORMAT,dilation_rate=(,),activation='relu',use_bias=True,kernel_initializer='glorot_uniform',bias_initializer='zeros',kernel_regularizer=None,bias_regularizer=None,activity_regularizer=None,kernel_constraint=None,bias_constraint=None,lrn2d_norm=LRN2D_NORM,weight_decay=WEIGHT_DECAY):
#l2 normalization
if weight_decay:
kernel_regularizer=regularizers.l2(weight_decay)
bias_regularizer=regularizers.l2(weight_decay)
else:
kernel_regularizer=None
bias_regularizer=None
x=Conv2D(filters=filters,kernel_size=kernel_size,strides=strides,padding=padding,data_format=data_format,dilation_rate=dilation_rate,activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(x)
if lrn2d_norm:
#batch normalization
x=BatchNormalization()(x)
return x
def inception_module(x,params,concat_axis,padding='same',data_format=DATA_FORMAT,dilation_rate=(,),activation='relu',use_bias=True,kernel_initializer='glorot_uniform',bias_initializer='zeros',kernel_regularizer=None,bias_regularizer=None,activity_regularizer=None,kernel_constraint=None,bias_constraint=None,lrn2d_norm=LRN2D_NORM,weight_decay=None):
(branch1,branch2,branch3,branch4)=params
if weight_decay:
kernel_regularizer=regularizers.l2(weight_decay)
bias_regularizer=regularizers.l2(weight_decay)
else:
kernel_regularizer=None
bias_regularizer=None
#1x1
pathway1=Conv2D(filters=branch1[],kernel_size=(,),strides=,padding=padding,data_format=data_format,dilation_rate=dilation_rate,activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(x)
#1x1->3x3
pathway2=Conv2D(filters=branch2[],kernel_size=(,),strides=,padding=padding,data_format=data_format,dilation_rate=dilation_rate,activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(x)
pathway2=Conv2D(filters=branch2[],kernel_size=(,),strides=,padding=padding,data_format=data_format,dilation_rate=dilation_rate,activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(pathway2)
#1x1->5x5
pathway3=Conv2D(filters=branch3[],kernel_size=(,),strides=,padding=padding,data_format=data_format,dilation_rate=dilation_rate,activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(x)
pathway3=Conv2D(filters=branch3[],kernel_size=(,),strides=,padding=padding,data_format=data_format,dilation_rate=dilation_rate,activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(pathway3)
#3x3->1x1
pathway4=MaxPooling2D(pool_size=(,),strides=,padding=padding,data_format=DATA_FORMAT)(x)
pathway4=Conv2D(filters=branch4[],kernel_size=(,),strides=,padding=padding,data_format=data_format,dilation_rate=dilation_rate,activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(pathway4)
return concatenate([pathway1,pathway2,pathway3,pathway4],axis=concat_axis)
def create_model():
#Data format:tensorflow,channels_last;theano,channels_last
if DATA_FORMAT=='channels_first':
INP_SHAPE=(,,)
img_input=Input(shape=INP_SHAPE)
CONCAT_AXIS=
elif DATA_FORMAT=='channels_last':
INP_SHAPE=(,,)
img_input=Input(shape=INP_SHAPE)
CONCAT_AXIS=
else:
raise Exception('Invalid Dim Ordering')
x=conv2D_lrn2d(img_input,,(,),,padding='same',lrn2d_norm=False)
x=MaxPooling2D(pool_size=(,),strides=,padding='same',data_format=DATA_FORMAT)(x)
x=BatchNormalization()(x)
x=conv2D_lrn2d(x,,(,),,padding='same',lrn2d_norm=False)
x=conv2D_lrn2d(x,,(,),,padding='same',lrn2d_norm=True)
x=MaxPooling2D(pool_size=(,),strides=,padding='same',data_format=DATA_FORMAT)(x)
x=inception_module(x,params=[(,),(,),(,),(,)],concat_axis=CONCAT_AXIS) #3a
x=inception_module(x,params=[(,),(,),(,),(,)],concat_axis=CONCAT_AXIS) #3b
x=MaxPooling2D(pool_size=(,),strides=,padding='same',data_format=DATA_FORMAT)(x)
x=inception_module(x,params=[(,),(,),(,),(,)],concat_axis=CONCAT_AXIS) #4a
x=inception_module(x,params=[(,),(,),(,),(,)],concat_axis=CONCAT_AXIS) #4b
x=inception_module(x,params=[(,),(,),(,),(,)],concat_axis=CONCAT_AXIS) #4c
x=inception_module(x,params=[(,),(,),(,),(,)],concat_axis=CONCAT_AXIS) #4d
x=inception_module(x,params=[(,),(,),(,),(,)],concat_axis=CONCAT_AXIS) #4e
x=MaxPooling2D(pool_size=(,),strides=,padding='same',data_format=DATA_FORMAT)(x)
x=inception_module(x,params=[(,),(,),(,),(,)],concat_axis=CONCAT_AXIS) #5a
x=inception_module(x,params=[(,),(,),(,),(,)],concat_axis=CONCAT_AXIS) #5b
x=AveragePooling2D(pool_size=(,),strides=,padding='valid',data_format=DATA_FORMAT)(x)
x=Flatten()(x)
x=Dropout(DROPOUT)(x)
x=Dense(output_dim=NB_CLASS,activation='linear')(x)
x=Dense(output_dim=NB_CLASS,activation='softmax')(x)
return x,img_input,CONCAT_AXIS,INP_SHAPE,DATA_FORMAT
def check_print():
# Create the Model
x,img_input,CONCAT_AXIS,INP_SHAPE,DATA_FORMAT=create_model()
# Create a Keras Model
model=Model(input=img_input,output=[x])
model.summary()
# Save a PNG of the Model Build
plot_model(model,to_file='GoogLeNet.png')
model.compile(optimizer='adam',loss='categorical_crossentropy',metrics=['acc',metric.top_k_categorical_accuracy])
print 'Model Compiled'
return model
if __name__=='__main__':
if os.path.exists('inception_1.h5'):
model=load_model('inception_1.h5')
else:
model=check_print()
model.fit_generator(train_generator,validation_data=vaild_generator,epochs=EPOCH,steps_per_epoch=train_generator.n/batch_size
,validation_steps=vaild_generator.n/batch_size)
model.save('inception_1.h5')
model.metrics=['acc',metric.top_k_categorical_accuracy]
loss,acc,top_acc=model.evaluate_generator(test_generator,steps=test_generator.n/batch_size)
print 'Test result:loss:%f,acc:%f,top_acc:%f'%(loss,acc,top_acc)
為了搭建Inception網絡,我們使用了以下政策:
- 使用了inception_module這個函數構造每個inception子產品,裡面有4路,分别對應着1x1,(1x1,3x3),(1x1,5x5),(3x3,1x1) 這四路。
- 使用了3個正則化的手段,分别是L2正則化,BatchNormalization,dropout,來防止模型過拟合。
- create_model這個函數裡面的網絡搭建可以參考Tabel.1,可以邊看表裡面的具體參數邊搭網絡。
整個代碼的流程如下:
- 導入相應庫
- 模型參數設定以及其它配置
- 生成訓練集,測試集,驗證集的三個疊代器
- Inception module函數的編寫
- create model函數編寫,仿照Table.1
- 模型編譯,畫出模型的圖
- 模型訓練與驗證
- 模型儲存
- 模型在測試集上測試
實驗結果
dataset | loss | acc | top5-acc |
---|---|---|---|
Training set | 1.85 | 39.9% | 85.3% |
Vaildation set | 2.01 | 36.6% | 82.0% |
Testing set | 2.08 | 35.7% | 78.1% |
實驗結果分析
我們可以發現模型最後在測試集上的效果與訓練集上的效果有一定程度上的差距,模型出現了一點過拟合。以下是個人對此問題的一些分析:由于采用了L2正則化,BatchNormalization,Dropout等等方法,但是還是出現了過拟合,可能是由于VOC資料比較少,20類物體才1.7萬張,相當于每個物體850張左右,要想取得比較好的效果,每個類别物體的圖檔至少得幾千張。是以可以通過降低網絡層數,增加資料量,增加訓練次數等手段來提高網絡的性能。
本部落格相關引用
以下是本部落格的引用,再次本人對每個引用的作者表示感謝。讀者如果對googlenet這個網絡仍然存在一些疑慮,或者想要有更深的了解,可以參考以下的引用。
引用部落格1
引用部落格2
引用部落格3
引用文獻1:InceptionV1
引用文獻2:InceptionV2
引用文獻3:InceptionV3
引用文獻4:InceptionV4!