【連載17】GoogLeNet Inception V2

GoogLeNet Inception V2在《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》出現,最大亮點是提出了Batch Normalization方法,它起到以下作用:

  • 使用較大的學習率而不用特別關心諸如梯度爆炸或消失等優化問題;
  • 降低了模型效果對初始權重的依賴;
  • 可以加速收斂,一定程度上可以不使用Dropout這種降低收斂速度的方法,但卻起到了正則化作用提高了模型泛化性;
  • 即使不使用ReLU也能緩解激活函數飽和問題;
  • 能夠學習到從當前層到下一層的分佈縮放( scaling (方差),shift (期望))係數。

一些思考

在機器學習中,我們通常會做一種假設:訓練樣本獨立同分佈(iid)且訓練樣本與測試樣本分佈一致,如果真實數據符合這個假設則模型效果可能會不錯,反之亦然,這個在學術上叫Covariate Shift,所以從樣本(外部)的角度說,對於神經網絡也是一樣的道理。從結構(內部)的角度說,由於神經網絡由多層組成,樣本在層與層之間邊提特徵邊往前傳播,如果每層的輸入分佈不一致,那麼勢必造成要麼模型效果不好,要麼學習速度較慢,學術上這個叫Internal Covariate Shift。 假設:為樣本標註,為樣本x通過神經網絡若干層後每層的輸入; 理論上:的聯合概率分佈應該與集合中任意一層輸入的聯合概率分佈一致,如:; 但是:,其中條件概率p(y|x)是一致的,即,但由於神經網絡每一層對輸入分佈的改變,導致邊緣概率是不一致的,即,甚至隨着網絡深度的加深,前面層微小的變化會導致後面層巨大的變化。

BN原理

BN整個算法過程如下:

  • 以batch的方式做訓練,對m個樣本求期望和方差後對訓練數據做白化,通過白化操作可以去除特徵相關性並把數據縮放在一個球體上,這麼做的好處既可以加快優化算法的優化速度也可能提高優化精度,一個直觀的解釋:

左邊是未做白化的原始可行域,右邊是做了白化的可行域;

  • 當原始輸入對模型學習更有利時能夠恢復原始輸入(和殘差網絡有點神似):

這裡的參數和是需要學習的。

參數學習依然是利用反向傳播原理:

對卷積神經網絡而言,BN被加在激活函數的非線性變換前,即:

由於BN參數的存在,這裡的偏置可以被去掉,即:

所以在看相關代碼實現時大家會發現沒有偏置這個參數。 另外當採用較大的學習率時,傳統方法會由於激活函數飽和區的存在導致反向傳播時梯度出現爆炸或消失,但採用BN後,參數的尺度變化不影響梯度的反向傳播,可以證明:

在模型Inference階段,BN層需要的期望和方差是固定值,由於所有訓練集batch的期望和方差已知,可以用這些值對整體訓練集的期望和方差做無偏估計修正,修正方法為:

其中為訓練集所有(大小都為)的集合集合

Inference時的公式變為:

卷積神經網絡中的BN

卷積網絡中採用權重共享策略,每個feature map只有一對,需要學習。

代碼實踐

			import copy  import numpy as np  import pandas as pd  import matplotlib  matplotlib.use("Agg")  import matplotlib.pyplot as plt  from matplotlib.pyplot import plot,savefig  from keras.datasets import mnist, cifar10  from keras.models import Sequential  from keras.layers.core import Dense, Dropout, Activation, Flatten, Reshape  from keras.optimizers import SGD, RMSprop  from keras.utils import np_utils  from keras.regularizers import l2  from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D, AveragePooling2D  from keras.callbacks import EarlyStopping  from keras.preprocessing.image import ImageDataGenerator  from keras.layers.normalization import BatchNormalization  import tensorflow as tf  tf.python.control_flow_ops = tf  from PIL import Image  def build_LeNet5():      model = Sequential()      model.add(Convolution2D(96, 11, 11, border_mode='same', input_shape = (32, 32, 3), dim_ordering='tf'))  #注釋1    model.add(BatchNormalization())      model.add(MaxPooling2D(pool_size=(2, 2)))  #注釋2    model.add(BatchNormalization())      model.add(Activation("tanh"))      model.add(Convolution2D(120, 1, 1, border_mode='valid'))  #注釋3    model.add(BatchNormalization())      model.add(Flatten())      model.add(Dense(10))      model.add(BatchNormalization())      model.add(Activation("relu"))  #注釋4    model.add(Dense(10))      model.add(Activation('softmax'))      return modelif __name__=="__main__":      from keras.utils.vis_utils import plot_model      model = build_LeNet5()      model.summary()      plot_model(model, to_file="LeNet-5.png", show_shapes=True)      (X_train, y_train), (X_test, y_test) = cifar10.load_data()#mnist.load_data()      X_train = X_train.reshape(X_train.shape[0], 32, 32, 3).astype('float32') / 255      X_test = X_test.reshape(X_test.shape[0], 32, 32, 3).astype('float32') / 255      Y_train = np_utils.to_categorical(y_train, 10)      Y_test = np_utils.to_categorical(y_test, 10)      # this will do preprocessing and realtime data augmentation      datagen = ImageDataGenerator(          featurewise_center=False,  # set input mean to 0 over the dataset          samplewise_center=False,  # set each sample mean to 0          featurewise_std_normalization=False,  # divide inputs by std of the dataset          samplewise_std_normalization=False,  # divide each input by its std          zca_whitening=False,  # apply ZCA whitening          rotation_range=25,  # randomly rotate images in the range (degrees, 0 to 180)          width_shift_range=0.1,  # randomly shift images horizontally (fraction of total width)          height_shift_range=0.1,  # randomly shift images vertically (fraction of total height)          horizontal_flip=False,  # randomly flip images          vertical_flip=False)  # randomly flip images      datagen.fit(X_train)      # training      model.compile(loss='categorical_crossentropy',                optimizer='adadelta',                metrics=['accuracy'])      batch_size = 32      nb_epoch = 8      model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch,                verbose=1, validation_data=(X_test, Y_test))      score = model.evaluate(X_test, Y_test, verbose=0)      print('Test score:', score[0])      print('Test accuracy:', score[1])

三組實驗對比:

  • 第一組:放開所有注釋
  • 第二組:放開注釋4
  • 第三組:注釋掉所有BN

‍‍‍‍‍‍‍‍