Deep Learning With Keras (8):在小數據集上訓練卷積神經網路(下)

之前,我們已經了解了一些可以幫助緩解過擬合的技術,如dropout和權重衰減(L2正則化)。我們現在要使用一種新的專門用於計算機視覺的減少過擬合的技術,在深度學習模型處理圖像時幾乎都要用:數據擴充(數據增強)。過擬合是由於訓練樣本較少,導致無法訓練可推廣到新數據的模型。給定無限的數據,你的模型將展示數據分布的每一個可能的特徵:你永遠不會過擬合。數據增強採用從現有訓練樣本中生成更多訓練數據的方法,通過多次隨機變換來增加樣本數量,從而產生更多可用的圖像。

1.為什麼要數據擴充

我們以吳恩達的視頻進行講解。大部分的計算機視覺任務使用很多的數據,所以數據擴充是經常使用的一種技巧來提高計算機視覺系統的表現。我認為計算機視覺是一個相當複雜的工作,你需要輸入圖像的像素值,然後弄清楚圖片中有什麼,似乎你需要學習一個複雜方程來做這件事。在實踐中,更多的數據對大多數計算機視覺任務都有所幫助。不像其他領域,有時候得到充足的數據,但是效果並不怎麼樣。但是,當下在計算機視覺方面,計算機視覺的主要問題是沒有辦法得到充足的數據。對大多數機器學習應用,這不是問題,但是對計算機視覺,數據就遠遠不夠。所以這就意味著當你訓練計算機視覺模型的時候,數據擴充會有所幫助,這是可行的,無論你是使用遷移學習,使用別人的預訓練模型開始,或者從源代碼開始訓練模型。讓我們來看一下計算機視覺中常見的數據擴充的方法。

或許最簡單的數據擴充方法就是垂直鏡像對稱如圖1.1,假如,訓練集中有這張圖片,然後將其翻轉得到右邊的圖像。對大多數計算機視覺任務,左邊的圖片是貓,然後鏡像對稱仍然是貓,如果鏡像操作保留了圖像中想識別的物體的前提下,這是個很實用的數據擴充技巧。

圖 1.1 垂直鏡像對稱

另一個經常使用的技巧是隨機裁剪(如圖1.2),給定一個數據集,然後開始隨機裁剪,可能修剪這個(編號1),選擇裁剪這個(編號2),這個(編號3),可以得到不同的圖片放在數據集中,你的訓練集中有不同的裁剪。隨機裁剪並不是一個完美的數據擴充的方法,如果你隨機裁剪的那一部分(紅色方框標記部分,編號4),哪一個看起來更像貓。但在實踐中,這個方法還是很實用的,隨機裁剪構成了很大一部分的真實圖片。

圖1.2 隨機裁剪

鏡像對稱和隨機裁剪是經常被使用的。當然,理論上,你也可以使用旋轉,剪切(shearing:此處並非裁剪的含義,圖像僅水平或垂直坐標發生變化)圖像,可以對圖像進行這樣的扭曲變形,引入很多形式的局部彎曲等等。當然使用這些方法並沒有壞處,儘管在實踐中,因為太複雜了所以使用的很少。

第二種經常使用的方法是彩色轉換(如圖1.3),有這樣一張圖片,然後給RGB三個通道上加上不同的失真值

圖1.3 彩色轉換

在這個例子中(編號1),要給紅色、藍色通道加值,給綠色通道減值。紅色和藍色會產生紫色,使整張圖片看起來偏紫,這樣訓練集中就有失真的圖片。為了演示效果,我對圖片的顏色進行改變比較誇張。在實踐中,對RGB的變化是基於某些分布的,這樣的改變也可能很小。

這麼做的目的就是使用不同的RGB的值,使用這些值來改變顏色。在第二個例子中(編號2),我們少用了一點紅色,更多的綠色和藍色色調,這就使得圖片偏黃一點。

在這(編號3)使用了更多的藍色,僅僅多了點紅色。在實踐中,RGB的值是根據某種概率分布來決定的。這麼做的理由是,可能陽光會有一點偏黃,或者是燈光照明有一點偏黃,這些可以輕易的改變圖像的顏色,但是對貓的識別,或者是內容的識別,以及標籤,還是保持不變的。所以介紹這些,顏色失真或者是顏色變換方法,這樣會使得你的學習演算法對照片的顏色更改更具魯棒性。、

這就是數據擴充,與訓練深度神經網路的其他部分類似,在數據擴充過程中也有一些超參數,比如說顏色變化了多少,以及隨機裁剪的時候使用的參數。與計算機視覺其他部分類似,一個好的開始可能是使用別人的開源實現,了解他們如何實現數據擴充。當然如果你想獲得更多的不變特性,而其他人的開源實現並沒有實現這個,你也可以去調整這些參數。因此,我希望你們可以使用數據擴充使你的計算機視覺應用效果更好。

2. Keras實現數據擴充

在Keras中,這可以通過配置對ImageDataGenerator讀取的圖像執行多個隨機變換來完成。 讓我們開始一個例子。

datagen = ImageDataGenerator( rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True, fill_mode=nearest)

這些只是一些可用的選項(更多,請參閱Keras文檔)。另外具體詳細的講解,可以看看這篇文章圖片數據集太少?看我七十二變,Keras Image Data Augmentation 各參數詳解講得很好。

讓我們快速瀏覽一下這段代碼:

  • rotation_range:整數,以度(0-180)為單位的值,圖片隨機轉動的角度範圍,其參數只需指定一個整數即可,但並不是固定以這個角度進行旋轉,而是在 [0, 指定角度] 範圍內進行隨機角度旋轉。
  • width_shift:浮點數,圖片寬度的某個比例,數據增強時圖片水平偏移的幅度
  • height_shift:浮點數,圖片高度的某個比例,數據增強時圖片豎直偏移的幅度
  • shear_range:浮點數,錯切變換,效果就是讓所有點的x坐標(或者y坐標)保持不變,而對應的y坐標(或者x坐標)則按比例發生平移,且平移的大小和該點到x軸(或y軸)的垂直距離成正比。
  • zoom_range:浮點數或形如[lower,upper]的列表,隨機縮放的幅度,若為浮點數,則相當於[lower,upper] = [1 - zoom_range, 1+zoom_range]
  • horizontal_flip:布爾值,進行隨機水平翻轉
  • fill_mode:fill_mode:;『constant』,『nearest』,『reflect』或『wrap』之一,當進行變換時超出邊界的點將根據本參數給定的方法進行處理

我們來看看新增的圖像(如圖1.1)。

from keras.preprocessing import imagefnames = [os.path.join(train_cats_dir, fname) for fname in os.listdir(train_cats_dir)]# 我們選擇一個圖像來「增強」img_path = fnames[3]# 讀取圖片並調整大小為150x150img = image.load_img(img_path, target_size=(150, 150))# 把圖片轉換成shape為(150, 150, 3)的張量x = image.img_to_array(img)# reshape,使得(1, 150, 150, 3)x = x.reshape((1,) + x.shape)# 下面的.flow()命令會生成一批隨機轉換的圖像。它將無限循環,所以我們需要在某個時刻「斷開」循環!i = 0for batch in datagen.flow(x, batch_size=1): plt.figure(i) imgplot = plt.imshow(image.array_to_img(batch[0])) i += 1 if i % 4 == 0: breakplt.show()

圖 5.1 通過隨機數據增強生成的貓的圖片

如果使用這種數據增強來訓練新的網路,網路不會看到相同的輸入圖像。 但是它所看到的輸入圖像仍然是高度相關的,因為它們來自少量的原始圖像 - 你不能產生新的信息,只能混合現有的信息。 因此,這可能不足以完全擺脫過擬合。為了進一步解決過擬合問題,還需要在全連接層的分類器之前為模型添加Dropout層.

定義一個包含Dropout的新的卷積神經網路:

model = models.Sequential()model.add(layers.Conv2D(32, (3, 3), activation=relu, input_shape=(150, 150, 3)))model.add(layers.MaxPooling2D((2, 2)))model.add(layers.Conv2D(64, (3, 3), activation=relu))model.add(layers.MaxPooling2D((2, 2)))model.add(layers.Conv2D(128, (3, 3), activation=relu))model.add(layers.MaxPooling2D((2, 2)))model.add(layers.Conv2D(128, (3, 3), activation=relu))model.add(layers.MaxPooling2D((2, 2)))model.add(layers.Flatten())model.add(layers.Dropout(0.5))model.add(layers.Dense(512, activation=relu))model.add(layers.Dense(1, activation=sigmoid))model.compile(loss=binary_crossentropy, optimizer=adam, metrics=[acc])

使用數據擴充和Dropout訓練:

train_datagen = ImageDataGenerator( rescale=1./255, rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True,)# 請注意測試驗證數據不應該增加! test_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory( train_dir, # 目標路徑 target_size=(150, 150), # 調整圖片大小一致:150 × 150 batch_size=32, # 批量大小 class_mode=binary) # 二分類 validation_generator = test_datagen.flow_from_directory( validation_dir, target_size=(150, 150), batch_size=32, class_mode=binary)history = model.fit_generator( train_generator, steps_per_epoch=100, epochs=100, validation_data=validation_generator, validation_steps=50)

保存一下模型:

model.save(cats_and_dogs_small_2.h5)

讓我們再次繪製結果:如圖2.1和2.2。 由於數據增加和Dropout,模型不再過擬合:訓練曲線驗證曲線。 現在,我們達到了82%的正確率,比沒有正則化的模型提高了15%。

acc = history.history[acc]val_acc = history.history[val_acc]loss = history.history[loss]val_loss = history.history[val_loss]epochs = range(len(acc))plt.plot(epochs, acc, bo, label=Training acc)plt.plot(epochs, val_acc, b, label=Validation acc)plt.title(Training and validation accuracy)plt.legend()plt.figure()plt.plot(epochs, loss, bo, label=Training loss)plt.plot(epochs, val_loss, b, label=Validation loss)plt.title(Training and validation loss)plt.legend()plt.show()

圖 2.1 使用正則化後的訓練集和驗證集正確率曲線

圖 2.2 使用正則化後的訓練集和驗證集損失曲線

通過進一步使用正則化技術,並通過調整網路的參數(例如每個卷積層的濾波器數量或網路中的層數),可以獲得更好的正確率,可能高達86% 或87%。 但是要想從頭開始訓練自己的模型,實在難以達到很高的正確率,因為數據實在太少了。 為了進一步提高這個問題的正確率的,我們需要使用預訓練好的模型(遷移學習),這是後面我們學習的重點。


推薦閱讀:

Keras使用VGG16訓練圖片分類?
keras中embedding層是如何將一個正整數(下標)轉化為具有固定大小的向量的?
FancyKeras-數據的輸入(花式)
Python Keras庫在windows系統中無法使用問題解決

TAG:深度学习DeepLearning | Keras |