Deep learning with Python 學習筆記(9)

神經網絡模型的優化

使用 Keras 回調函數

使用 model.fit()或 model.fit_generator() 在一個大型數據集上啓動數十輪的訓練,有點相似於扔一架紙飛機,一開始給它一點推力,以後你便再也沒法控制其飛行軌跡或着陸點。若是想要避免很差的結果(並避免浪費紙飛機),更聰明的作法是不用紙飛機,而是用一架無人機,它能夠感知其環境,將數據發回給操縱者,而且可以基於當前狀態自主航行。下面要介紹的技術,可讓model.fit() 的調用從紙飛機變爲智能的自主無人機,能夠自我檢討並動態地採起行動html

訓練過程當中將回調函數做用於模型python

訓練模型時,不少事情一開始都沒法預測。尤爲是你不知道須要多少輪才能獲得最佳驗證損失。前面全部例子都採用這樣一種策略:訓練足夠多的輪次,這時模型已經開始過擬合,根據這第一次運行來肯定訓練所須要的正確輪數,而後使用這個最佳輪數從頭開始再啓動一次新的訓練。固然,這種方法很浪費算法

處理這個問題的更好方法是,當觀測到驗證損失再也不改善時就中止訓練。這可使用 Keras 回調函數來實現。回調函數(callback)是在調用 fit 時傳入模型的一個對象(即實現特定方法的類實例),它在訓練過程當中的不一樣時間點都會被模型調用。它能夠訪問關於模型狀態與性能的全部可用數據,還能夠採起行動:中斷訓練、保存模型、加載一組不一樣的權重或改變模型的狀態後端

回調函數的一些用法示例以下所示數組

  1. 模型檢查點(model checkpointing):在訓練過程當中的不一樣時間點保存模型的當前權重
  2. 提早終止(early stopping):若是驗證損失再也不改善,則中斷訓練(固然,同時保存在訓練過程當中獲得的最佳模型)
  3. 在訓練過程當中動態調節某些參數值:好比優化器的學習率
  4. 在訓練過程當中記錄訓練指標和驗證指標,或將模型學到的表示可視化(這些表示也在不斷更新):Keras 進度條就是一個回調函數

keras.callbacks 模塊包含許多內置的回調函數,如瀏覽器

keras.callbacks.ModelCheckpoint
keras.callbacks.EarlyStopping
keras.callbacks.LearningRateScheduler
keras.callbacks.ReduceLROnPlateau
keras.callbacks.CSVLogger網絡

架構

ModelCheckpoint 與 EarlyStopping 回調函數框架

若是監控的目標指標在設定的輪數內再也不改善,能夠用 EarlyStopping 回調函數來中斷訓練。好比,這個回調函數能夠在剛開始過擬合的時候就中斷訓練,從而避免用更少的輪次從新訓練模型。這個回調函數一般與ModelCheckpoint 結合使用,後者能夠在訓練過程當中持續不斷地保存模型(你也能夠選擇只保存目前的最佳模型,即一輪結束後具備最佳性能的模型)機器學習

import keras


# 經過 fit 的 callbacks 參數將回調函數傳入模型中,這個參數接收一個回調函數的列表。你能夠傳入任意個數的回調函數
# EarlyStopping: 1. 若是再也不改善,就中斷訓練 2. 監控模型的驗證精度 3. 若是精度在多於一輪的時間(即兩輪)內再也不改善,中斷訓練
# ModelCheckpoint: 1. 在每輪事後保存當前權重 2. 若是 val_loss 沒有改善,那麼不須要覆蓋模型文件
callbacks_list = [ 
    keras.callbacks.EarlyStopping(
        monitor='acc', 
        patience=1, 
    ),
    keras.callbacks.ModelCheckpoint( 
        filepath='model.h5', 
        monitor='val_loss', 
        save_best_only=True,
 )
]

# 監控精度,因此" metrics=['acc'] "應該是模型指標的一部分
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc']) 
# 因爲回調函數要監控驗證損失和驗證精度,因此在調用 fit 時須要傳入 validation_data(驗證數據)
model.fit(x, y, epochs=10, batch_size=32, validation_data=(x_val, y_val), callbacks=callbacks_list)

ReduceLROnPlateau 回調函數
若是驗證損失再也不改善,你可使用這個回調函數來下降學習率。在訓練過程當中若是出現了損失平臺(loss plateau),那麼增大或減少學習率都是跳出局部最小值的有效策略

# 監控模型的驗證損失,觸發時將學習率除以 10,若是驗證損失在 10 輪內都沒有改善,那麼就觸發這個回調函數  
callbacks_list = [
    keras.callbacks.ReduceLROnPlateau(
    monitor='val_loss' 
    factor=0.1, 
    patience=10, 
 )
]

model.fit(x, y,
    epochs=10,
    batch_size=32,
    callbacks=callbacks_list,
    validation_data=(x_val, y_val))

自定義回調函數

回調函數的實現方式是建立 keras.callbacks.Callback 類的子類。而後你能夠實現下面這些方法(從名稱中便可看出這些方法的做用),它們分別在訓練過程當中的不一樣時間點被調用

  1. on_epoch_begin -- 在每輪開始時被調用
  2. on_epoch_end -- 在每輪結束時被調用
  3. on_batch_begin -- 在處理每一個批量以前被調用
  4. on_batch_end -- 在處理每一個批量以後被調用
  5. on_train_begin -- 在訓練開始時被調用
  6. on_train_end -- 在訓練結束時被調用

這些方法被調用時都有一個 logs 參數,這個參數是一個字典,裏面包含前一個批量、前一個輪次或前一次訓練的信息,即訓練指標和驗證指標等。此外,回調函數還能夠訪問下列屬性

  1. self.model:調用回調函數的模型實例
  2. self.validation_data:傳入 fit 做爲驗證數據的值

自定義回調函數的簡單示例,它能夠在每輪結束後將模型每層的激活保存到硬盤(格式爲 Numpy 數組),這個激活是對驗證集的第一個樣本計算獲得的

import keras
import numpy as np


class ActivationLogger(keras.callbacks.Callback):
    def set_model(self, model):
        self.model = model 
        layer_outputs = [layer.output for layer in model.layers]
        self.activations_model = keras.models.Model(model.input, layer_outputs) 
    
    def on_epoch_end(self, epoch, logs=None):
        if self.validation_data is None:
            raise RuntimeError('Requires validation_data.')
        validation_sample = self.validation_data[0][0:1] 
        activations = self.activations_model.predict(validation_sample)
        f = open('activations_at_epoch_' + str(epoch) + '.npz', 'w') 
        np.savez(f, activations)
        f.close()

TensorBoard 簡介:TensorFlow 的可視化框架

TensorBoard,一個內置於 TensorFlow 中的基於瀏覽器的可視化工具。只有當 Keras 使用 TensorFlow 後端時,這一方法才能用於 Keras 模型

-- 等待嘗試

讓模型性能發揮到極致

高級架構模式

除殘差鏈接外,標準化和深度可分離卷積在構建高性能深度卷積神經網絡時也特別重要

批標準化

標準化(normalization)是一大類方法,用於讓機器學習模型看到的不一樣樣本彼此之間更加類似,這有助於模型的學習與對新數據的泛化。最多見的數據標準化形式就是:將數據減去其平均值使其中心爲 0,而後將數據除以其標準差使其標準差爲 1。實際上,這種作法假設數據服從正態分佈(也叫高斯分佈),並確保讓該分佈的中心爲 0,同時縮放到方差爲 1

normalized_data = (data - np.mean(data, axis=...)) / np.std(data, axis=...)

前面的示例都是在將數據輸入模型以前對數據作標準化。但在網絡的每一次變換以後都應該考慮數據標準化。即便輸入 Dense 或 Conv2D 網絡的數據均值爲 0、方差爲 1,也沒有理由 假定網絡輸出的數據也是這樣

批標準化(batch normalization)是在 2015 年提出的一種層的類型(在Keras 中是 BatchNormalization),即便在訓練過程當中均值和方差隨時間發生變化,它也能夠適應性地將數據標準化。批標準化的工做原理是,訓練過程當中在內部保存已讀取每批數據均值和方差的指數移動平均值。批標準化的主要效果是,它有助於梯度傳播(這一點和殘差鏈接很像),所以容許更深的網絡。對於有些特別深的網絡,只有包含多個 BatchNormalization 層時才能進行訓練

BatchNormalization 層一般在卷積層或密集鏈接層以後使用

conv_model.add(layers.Conv2D(32, 3, activation='relu')) 
conv_model.add(layers.BatchNormalization())

dense_model.add(layers.Dense(32, activation='relu')) 
dense_model.add(layers.BatchNormalization())

BatchNormalization 層接收一個 axis 參數,它指定應該對哪一個特徵軸作標準化。這個參數的默認值是 -1,即輸入張量的最後一個軸。對於 Dense 層、Conv1D 層、RNN 層和將data_format 設爲 "channels_last"(通道在後)的 Conv2D 層,這個默認值都是正確的。但有少數人使用將 data_format 設爲 "channels_first"(通道在前)的 Conv2D 層,這時特徵軸是編號爲 1 的軸,所以 BatchNormalization 的 axis 參數應該相應地設爲 1

深度可分離卷積

深度可分離卷積(depthwise separable convolution)層(SeparableConv2D)能夠替代 Conv2D,並可讓模型更加輕量(即更少的可訓練權重參數)、速度更快(即更少的浮點數運算),還可讓任務性能提升幾個百分點。這個層對輸入的每一個通道分別執行空間卷積,而後經過逐點卷積(1×1 卷積)將輸出通道混合

如圖示

這至關於將空間特徵學習和通道特徵學習分開,若是你假設輸入中的空間位置高度相關,但不一樣的通道之間相對獨立,那麼這麼作是頗有意義的。它須要的參數要少不少,計算量也更小,所以能夠獲得更小、更快的模型。由於它是一種執行卷積更高效的方法,因此每每可以使用更少的數據學到更好的表示,從而獲得性能更好的模型

demo

from keras.models import Sequential, Model
from keras import layers


height = 64
width = 64
channels = 3
num_classes = 10
model = Sequential()
model.add(layers.SeparableConv2D(32, 3, activation='relu', input_shape=(height,                 width, channels,)))
model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.MaxPooling2D(2))
model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.SeparableConv2D(128, 3, activation='relu'))
model.add(layers.MaxPooling2D(2))
model.add(layers.SeparableConv2D(64, 3, activation='relu'))
model.add(layers.SeparableConv2D(128, 3, activation='relu'))
model.add(layers.GlobalAveragePooling2D())
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(num_classes, activation='softmax'))
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')

超參數優化

構建深度學習模型時,你必須作出許多看似隨意的決定:應該堆疊多少層?每層應該包含多少個單元或過濾器?激活應該使用 relu 仍是其餘函數?在某一層以後是否應該使用BatchNormalization ?應該使用多大的 dropout 比率?還有不少。這些在架構層面的參數叫做超參數(hyperparameter),以便將其與模型參數區分開來,後者經過反向傳播進行訓練

超參數優化的過程一般以下所示:

  1. 選擇一組超參數
  2. 構建相應的模型
  3. 將模型在訓練數據上擬合,並衡量其在驗證數據上的最終性能
  4. 選擇要嘗試的下一組超參數(自動選擇)
  5. 重複上述過程
  6. 最後,衡量模型在測試數據上的性能

這個過程的關鍵在於,給定許多組超參數,使用驗證性能的歷史來選擇下一組須要評估的超參數的算法。有多種不一樣的技術可供選擇:貝葉斯優化、遺傳算法、簡單隨機搜索等

更新超參數很是具備挑戰性,如

  1. 計算反饋信號(這組超參數在這個任務上是否獲得了一個高性能的模型)的計算代價可能很是高,它須要在數據集上建立一個新模型並從頭開始訓練
  2. 超參數空間一般由許多離散的決定組成,於是既不是連續的,也不是可微的。所以,你一般不能在超參數空間中作梯度降低。相反,你必須依賴不使用梯度的優化方法,而這些方法的效率比梯度降低要低不少

一般狀況下,隨機搜索(隨機選擇須要評估的超參數,並重復這一過程)就是最好的解決方案,雖然這也是最簡單的解決方案。也存在一些工具比隨機搜索要好不少,如:Hyperopt。它是一個用於超參數優化的 Python 庫,其內部使用 Parzen 估計器的樹來預測哪組超參數可能會獲得好的結果。另外一個叫做 Hyperas 的庫將 Hyperopt 與 Keras 模型集成在一塊兒

模型集成

集成是指將一系列不一樣模型的預測結果聚集到一塊兒,從而獲得更好的預測結果。集成依賴於這樣的假設,即對於獨立訓練的不一樣良好模型,它們表現良好多是由於不一樣的緣由:每一個模型都從略有不一樣的角度觀察數據來作出預測,獲得了「真相」的一部分,但不是所有真相。每一個模型都獲得了數據真相的一部分,但不是所有真相。將他們的觀點聚集在一塊兒,你能夠獲得對數據更加準確的描述

集成最簡單的方法就是就不一樣模型的結果進行平均,以平均值做爲預測的結果。可是這種方法假設了所使用的分類器的性能都差很少好。若是其中一個模型性能比其餘的差不少,那麼最終預測結果可能不如這一組中的最佳模型好

而更加適用的方法是對各個模型的結果進行加權平均,其權重從驗證數據上學習獲得。一般來講,更好的模型被賦予更大的權重,而較差的模型則被賦予較小的權重。爲了找到一組好的集成權重,你可使用隨機搜索或簡單的優化算法(好比 Nelder-Mead 方法)

還有許多其餘變體,好比你能夠對預測結果先取指數再作平均。通常來講,簡單的加權平均,其權重在驗證數據上進行最優化,這是一個很強大的基準方法。想要保證集成方法有效,關鍵在於這組分類器的多樣性(diversity)。多樣性可以讓集成方法取得良好效果。用機器學習的術語來講,若是全部模型的誤差都在同一個方向上,那麼集成也會保留一樣的誤差。若是各個模型的誤差在不一樣方向上,那麼這些誤差會彼此抵消,集成結果會更加穩定、更加準確

所以,集成的模型應該儘量好,同時儘量不一樣。這一般意味着使用很是不一樣的架構,甚至使用不一樣類型的機器學習方法。有一件事情基本上是不值得作的,就是對相同的網絡,使用不一樣的隨機初始化屢次獨立訓練,而後集成。若是模型之間的惟一區別是隨機初始化和訓練數據的讀取順序,那麼集成的多樣性很小,與單一模型相比只會有微小的改進。集成不在於你的最佳模型有多好,而在於候選模型集合的多樣性

在進行大規模超參數自動優化時,有一個重要的問題須要牢記,那就是驗證集過擬合。由於你是使用驗證數據計算出一個信號,而後根據這個信號更新超參數,因此你其實是在驗證數據上訓練超參數,很快會對驗證數據過擬合

Deep learning with Python 學習筆記(10)
Deep learning with Python 學習筆記(8)

相關文章
相關標籤/搜索