深度剖析卷積神經網路

阿里云云棲社群發表於2018-05-23

摘要: 深度瞭解卷積神經網路各個元件,順帶自建一個屬於自己的神經網路。

最先進的影像識別體系結構採用了許多附加元件來補充卷積操作。在這篇文章中,你將瞭解到一些能夠提高現代卷積神經網路速度和精度的重要的元件。

Pooling

使CNN非常高效的第一個祕密就是Pooling。Pooling是像卷積一樣,用於對影像的每個區域性區域標量變換的一種向量。與卷積不同的是它們沒有filters,也不用區域性區域計算點積,而是計算平均值(Average Pooling)中的畫素,或者只是選擇強度最高的畫素並丟棄剩餘的畫素(Max Pooling)。

Max Pooling近年來效果最好,它以某個地區的最大畫素代表該地區最重要的特徵為理論基礎。通常我們希望分類的物體影像可能包含許多其他物體,例如,出現在汽車影像某處的貓可能會誤導分類器,Pooling有助於緩解這種影響。

同時,它也大大降低了計算成本。通常,網路中每層的影像大小與每層的計算成本(觸發器)成正比。分段卷積有時用作pooling的代替物,隨著圖層變得更深,Pooling會減少影像的尺寸,因此,它有助於防止網路需要的觸發器數量激增。

Dropout

過度擬合是指由於過度依賴訓練集中的某些特定功能導致的網路在訓練集上執行良好,但在測試集上表現不佳的一種現象。Dropout是一種對抗過度擬合的技術。它可以隨機地將一些啟用值設定為0,迫使網路探索更多的方式來分類影像,而不是過度依賴一些功能。它也是AlesNet中的關鍵元素之一。

Batch Normalization

神經網路的一個主要問題是梯度消失。來自Google Brain的Ioffe和Szegedy發現,這主要是由於內部協變數變化導致的資訊通過網路傳播而引起的的變化資料分佈的現象。他們所做的是稱為Batch Normalization的技術,通過將每批影像標準化為具有零均值和單位差異來工作。

它通常放置在cnns的非線性(relu)之前。它極大地提高了準確性,同時極大地加快了訓練過程。

資料增強

人類視覺系統在適應影像平移,旋轉和其他形式的扭曲方面非常出色,拍攝影像並翻轉它,大多數人仍然可以識別它。然而,covnets並不善於處理這種扭曲,他們可能會由於小的翻轉而失敗。但是通過隨機扭曲影像訓練,使用水平翻轉,垂直翻轉,旋轉,移位和其他扭曲,將會使covnets學會如何處理這種扭曲。

另一種常用方法是從每幅影像中減去平均影像,然後除以標準差。

接下來將解釋如何理由Keras來實現它們。

在這篇文章中,所有的實驗都將在一個包含60000個32*32GB影像的CIFAR10資料集上進行。它分為50000個訓練影像和10000個測試影像,為了讓事情更加模組化,我們為每個圖層建立一個簡單的函式:

def Unit(x,filters):
    out = BatchNormalization()(x)
    out = Activation("relu")(out)
    out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out)
    return out

這裡是我們的程式碼最重要的方面,單元函式定義了一個簡單的圖層,其中包含三個層次,第一個是我先前解釋的Batch Normalization,接下來我們新增RELU啟用,最後新增摺積,注意我是怎樣用“預啟用”的方式把RELU放在conv之前的。

現在我們將把這些單位層組合成一個模型:

def MiniModel(input_shape):
    images = Input(input_shape)
    net = Unit(images,64)
    net = Unit(net,64)
    net = Unit(net,64)
    net = MaxPooling2D(pool_size=(2,2))(net)
    net = Unit(net,128)
    net = Unit(net,128)
    net = Unit(net,128)
    net = MaxPooling2D(pool_size=(2, 2))(net)
    net = Unit(net,256)
    net = Unit(net,256)
    net = Unit(net,256)
    net = Dropout(0.5)(net)
    net = AveragePooling2D(pool_size=(8,8))(net)
    net = Flatten()(net)
    net = Dense(units=10,activation="softmax")(net)
    model = Model(inputs=images,outputs=net)
    return model

在這裡,我們使用功能性API來定義我們的模型,我們從三個單元格開始,每個單元格有64個過濾器,後面是一個Max Pooling layer,將3232影像縮小到1616。接下來是3128個過濾器單元,然後是Pooling,在這裡,我們的影像變成8*8。最後,我們有另外3個單元,256通道。請注意,每當我們將影像尺寸縮小2倍時,我們將通道數增加一倍。

我們新增0.5的dropout,這將隨機取消50%的引數,正如我之前解釋的那樣,它可以避免過度擬合。

接下來,我們需要載入cifar10資料集並執行一些資料進行增強:

#load the cifar10 dataset
(train_x, train_y) , (test_x, test_y) = cifar10.load_data()
#normalize the data
train_x = train_x.astype(`float32`) / 255
test_x = test_x.astype(`float32`) / 255
#Subtract the mean image from both train and test set
train_x = train_x - train_x.mean()
test_x = test_x - test_x.mean()
#Divide by the standard deviation
train_x = train_x / train_x.std(axis=0)
test_x = test_x / test_x.std(axis=0)

在上面的程式碼中,載入訓練和測試資料後,我們從每幅影像中減去平均影像併除以標準偏差,這是一種基本的資料增加技術。對於更先進的資料增強,我們的影像載入過程會略有改變,Keras有一個非常有用的資料增強程式,可以簡化整個過程。

下面的程式碼可以做到這一點:

datagen = ImageDataGenerator(rotation_range=10,
                             width_shift_range=5. / 32,
                             height_shift_range=5. / 32,
                             horizontal_flip=True)
# Compute quantities required for featurewise normalization
# (std, mean, and principal components if ZCA whitening is applied).
datagen.fit(train_x)

在上面,首先,我們指定了10度的旋轉角度,高度和寬度都是5/32的偏移,最後是水平翻轉,所有這些變換都會隨機應用於訓練集中的影像上。當然,還有更多轉換存在,你可以檢視可以為該類指定的所有引數。請記住,過度使用資料增強可能是有害的。

接下來,我們必須將標籤轉換為One熱編碼:

#Encode the labels to vectors
train_y = keras.utils.to_categorical(train_y,10)
test_y = keras.utils.to_categorical(test_y,10)

事實上,構成訓練過程的幾乎所有其他內容都與我之前的教程相同,這裡是完整的程式碼:

#import needed classes
import keras
from keras.datasets import cifar10
from keras.layers import Dense,Conv2D,MaxPooling2D,Flatten,AveragePooling2D,Dropout,BatchNormalization,Activation
from keras.models import Model,Input
from keras.optimizers import Adam
from keras.callbacks import LearningRateScheduler
from keras.callbacks import ModelCheckpoint
from math import ceil
import os
from keras.preprocessing.image import ImageDataGenerator
def Unit(x,filters):
    out = BatchNormalization()(x)
    out = Activation("relu")(out)
    out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out)
    return out
#Define the model
def MiniModel(input_shape):
    images = Input(input_shape)
    net = Unit(images,64)
    net = Unit(net,64)
    net = Unit(net,64)
    net = MaxPooling2D(pool_size=(2,2))(net)
    net = Unit(net,128)
    net = Unit(net,128)
    net = Unit(net,128)
    net = MaxPooling2D(pool_size=(2, 2))(net)
    net = Unit(net,256)
    net = Unit(net,256)
    net = Unit(net,256)
    net = Dropout(0.25)(net)
    net = AveragePooling2D(pool_size=(8,8))(net)
    net = Flatten()(net)
    net = Dense(units=10,activation="softmax")(net)
    model = Model(inputs=images,outputs=net)
    return model
#load the cifar10 dataset
(train_x, train_y) , (test_x, test_y) = cifar10.load_data()
#normalize the data
train_x = train_x.astype(`float32`) / 255
test_x = test_x.astype(`float32`) / 255
#Subtract the mean image from both train and test set
train_x = train_x - train_x.mean()
test_x = test_x - test_x.mean()
#Divide by the standard deviation
train_x = train_x / train_x.std(axis=0)
test_x = test_x / test_x.std(axis=0)
datagen = ImageDataGenerator(rotation_range=10,
                             width_shift_range=5. / 32,
                             height_shift_range=5. / 32,
                             horizontal_flip=True)
# Compute quantities required for featurewise normalization
# (std, mean, and principal components if ZCA whitening is applied).
datagen.fit(train_x)
#Encode the labels to vectors
train_y = keras.utils.to_categorical(train_y,10)
test_y = keras.utils.to_categorical(test_y,10)
#define a common unit
input_shape = (32,32,3)
model = MiniModel(input_shape)
#Print a Summary of the model
model.summary()
#Specify the training components
model.compile(optimizer=Adam(0.001),loss="categorical_crossentropy",metrics=["accuracy"])
epochs = 20
steps_per_epoch = ceil(50000/128)
# Fit the model on the batches generated by datagen.flow().
model.fit_generator(datagen.flow(train_x, train_y, batch_size=128),
                    validation_data=[test_x,test_y],                epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1, workers=4)
#Evaluate the accuracy of the test dataset
accuracy = model.evaluate(x=test_x,y=test_y,batch_size=128)
model.save("cifar10model.h5")

首先,這裡有一些不同之處

input_shape = (32,32,3)
model = MiniModel(input_shape)
#Print a Summary of the model
model.summary()

正如前面說的,cifar 10資料集由32*32的RGB影像組成,由此輸入形狀有3個通道。

下一行建立我們定義的模型的例項,並傳入輸入形狀,最後一行將列印出我們網路的完整摘要,包括引數的數量。

最後需要解釋的是:

epochs = 20
steps_per_epoch = ceil(50000/128)
# Fit the model on the batches generated by datagen.flow().
model.fit_generator(datagen.flow(train_x, train_y, batch_size=128),
                    validation_data=[test_x,test_y],                  epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1, workers=4)
#Evaluate the accuracy of the test dataset
accuracy = model.evaluate(x=test_x,y=test_y,batch_size=128)
model.save("cifar10model.h5")

首先我們要定義執行的時期的數量和每個時期的步驟數量。

steps_per_epoch = ceil(50000/128)

50000是總共訓練影像的數量,我們使用一個批處理大小為128。

接下來是擬合函式,這與我在之前的教程中解釋的擬合函式明顯不同。

下面的第二個看看會有所幫助:

Fit the model on the batches generated by datagen.flow().
model.fit_generator(datagen.flow(train_x, train_y, batch_size=128),
                    validation_data=[test_x,test_y],
                    epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1, workers=4)

本教程主要是向你介紹基本元件,你也可以自己嘗試調整引數和網路,以瞭解你可以提高的準確度。

本文由阿里云云棲社群組織翻譯。

文章原標題《Components of convoutional neural networks》

作者:John Olafenwa

譯者:烏拉烏拉,審校:袁虎。

原文地址

本文為雲棲社群原創內容,未經允許不得轉載。

相關文章