本篇繼續整理 "深度學習的16堂課" 這本書的讀後筆記, 本系列之前的文章參考 :
從第九張開始為全書第三篇, 以下是我讀第九章 "改善神經網路的訓練成效" 的摘要筆記與測試紀錄 :
- 激活函數 sigmoid 在z=wx+b 過大或過小時會出現輸出值飽和現象, 神經元宛如死掉一樣無反應, 使訓練的結果變得很差. 前人經驗顯示, 若以妥善的方法設定權重初始值, 則神經元飽和的可能性會降低. 故權重初始值的設定對模型的訓練成效有重要的影響.
- tf.keras 預設會自動以隨機值來指定 (偏值 b=0, 權重 w=Glorot 分布), 但也可以在新增 Dense 隱藏層時以下列兩個參數自行指定 :
(1). kernel_initializer : 指定權重參數 w 初始值
(2). bias_initializer : 指定偏值參數 b 初始值
tf.keras.initializer 模組中內建了 Zeros(), RandomNormal(), glorot_normal(), 與 glorot_uniform() 等函數可用來設定權重參數, 匯入方式如下 :
from tensorflow.keras.initializers import Zeros, RandomNormal, glorot_normal, glorot_uniform
其中 :
Zeros() : 與 Numpy 的 zeros() 一樣會將參數值全設為 0, 用來設定 b 參數
RandomNormal([stddev=1]) : 將參數設為平均值=0, 標準差為 1 之標準常態分布
glorot_normal() : 將參數設為 Glorot 常態分布
glorot_uniform() : 將參數設為 Glorot 均勻分布
Glorot 分布是 Xavier Glorot 與 Yushua Bengio 專為 w 參數權重初始化而設計的一種機率分布, 目的是希望神經元輸出的激活值不要太極端. Glorot 常態分布是標準常態分布之變體, 其標準差由前後層神經元數量決定, 比標準常態分布要小.
經實驗比較, 標準常態分布來設定權重初始值, 不論搭配哪種激活函數所產生的激活值都比較極端, 還是用 Glorot 分布較好, 採用 Glorot 常態分布與均勻分布效果差異不大. 參考 :
# Xavier Glorot参数初始化: 理解训练Deep DNN的难点 - 下面是將 w 參數設為標準常態分布來訓練第五章的 MNIST 感知器的結果, 主要的差別是匯入模組與模型建構方式.
(1). 匯入模組與函數 :
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation #新增(Activation)
from tensorflow.keras import optimizers
from tensorflow.keras.utils import plot_model
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
import numpy as np
from tensorflow.keras.initializers import Zeros, RandomNormal #新增
(2). 載入 MNIST 資料集 :
(X_train, y_train), (X_test, y_test)=mnist.load_data()
(3). 資料預處理 :
X_train=X_train.reshape(60000, 784).astype('float32') # 2D 資料展平為 1D
X_test=X_test.reshape(10000, 784).astype('float32') # 2D 資料展平為 1D
X_train /= 255 # 正規化
X_test /= 255 # 正規化
(4). 標籤預處理 :
y_train=to_categorical(y_train, 10) # 訓練集標籤 one-hot 編碼
y_test=to_categorical(y_test, 10) # 測試集標籤 one-hot 編碼
(5). 建構神經網路 :
上將中間密集層改用 256 個神經元, 為了與第五章比較, 我改回 64 個神經元. 其次是第五章使用 input_shape 參數, 其值為元組 (784, 0), 此處改用 input_dim, 其值是整數 784, 效果是一樣的. 其三, 為了後續修改程式方便, 中間層的激活函數不在 Dense() 中以 activation 參數設定, 改為從 tf.keras.layers 中匯入 Activation() 函數來設定
b_init=Zeros() #將b參數初始值設為0
w_init=RandomNormal() #將w參數初始值設為標準常態分布取樣
model=Sequential()
model.add(Dense(64,
input_dim=784,
kernel_initializer=w_init,
bias_initializer=b_init)) #自訂權重與偏差參數初始值
model.add(Activation('sigmoid')) #添加激活函數
model.add(Dense(10, activation='softmax'))
(6). 編譯模型 :
model.compile(loss='mean_squared_error',
optimizer=optimizers.SGD(learning_rate=0.01),
metrics='accuracy')
(7). 訓練模型 :model.fit(X_train, y_train, batch_size=128, epochs=200, verbose=1,
validation_data=(X_test, y_test)) 結果如下 :
85.99% 比預設初始值的 86.4% 稍微遜色. 以上測試的 .ipynb 檔參考 :
# https://github.com/tony1966/colab/blob/main/deep_learning_illustrated_ch9_1_mnist.ipynb - 將 w 初始值改用 Glorot 常態分布來取樣設定, 則匯入部分最後一列改為 :
from tensorflow.keras.initializers import Zeros, glorot_normal
w 權重初始值設定改為 :
w_init=glorot_normal() #將w參數初始值設為 Glorot 常態分布取樣
其餘程式碼不變重新訓練, 結果如下 :結果 86.67% 比用標準常態分布 85.99% 好. 完整原始碼參考 GitHub :
# https://github.com/tony1966/colab/blob/main/deep_learning_illustrated_ch9_2_mnist.ipynb - 將 w 初始值改用 Glorot 均勻分布來取樣設定, 則匯入部分最後一列改為 :from tensorflow.keras.initializers import Zeros, glorot_uniform
w 權重初始值設定改為 :
w_init=glorot_uniform() #將w參數初始值設為 Glorot 均勻分布取樣
其餘程式碼不變重新訓練, 結果如下 :
86.41% 比 Glorot 常態分布稍遜, 但比標準常態的 85.99% 好, 原始碼參考 GitHub :
# https://github.com/tony1966/colab/blob/main/deep_learning_illustrated_ch9_3_mnist.ipynb - 接下來回到上面使用 Glorot 常態分布, 將中間層神經元從 64 改為 256, 其餘不變, 看看神經元增加對訓練是否有幫助 :
w_init=glorot_normal() #將w參數初始值設為 Glorot 常態分布取樣
model.add(Dense(256,
input_dim=784,
kernel_initializer=w_init,
bias_initializer=b_init)) #自訂權重與偏差參數初始值
結果如下 :
結果來到 88.01%, 比同樣是 Glorot 常態但只有 64 個神經元的 86.67% 高. 原始碼參考 :
# https://github.com/tony1966/colab/blob/main/deep_learning_illustrated_ch9_4_mnist.ipynb - 訓練神經網路時, 除了權重參數初始值設定的問題外, 還有梯度不穩定 (unstable gradient) 問題, 這可分為下列兩種 :
(1). 梯度消失 (vanishing gradient) :
在利用損失值計算反向傳播時, 離輸出層最接近的隱藏層參數對損失值的影響最大, 離輸出層越遠的層, 該層參數對損失值的影響力越會被稀釋, 亦即, 當損失值從最後一層逐層反向傳播到第一個隱藏層時, 損失值對權重參數的梯度越算越小逐漸消失 (趨近於 0), 稱為梯度消失現象. 因此離輸出層最遠的那幾層 (或開頭的那幾層) 會因為梯度消失而學習停滯, 使模型的學習能力無法提升. 由於 sigmoid 的梯度值為 0~0.25 之間, 若中間層過多例如 20 層, 就算每層 sigmoid 梯度為最大值 0.25, 則 0.25**20=1e-12, 已經趨近於 0 了.
梯度消失通常來自神經網路層數太多, 再加上中間層使用了 sigmoid 激活函數所致.
(2). 梯度爆炸 (exploding gradient) :
與梯度消失相反的是, 從最末端往前做損失值的反向傳播計算時, 損失函數對越靠近輸入層的權重參數求出的偏導數越來越大, 同樣不利於神經網路之訓練. 這種情形比起梯度消失較少見 (例如在 RNN 神經網路). - 解決梯度不穩定問題可使用批次正規化 (batch normalization), 這是一種重訂比例 (rescale) 的方法, 也稱為特徵縮放 (feature scaling), 做法是將每層的激活值減掉各訓練批次所有資料的平均值, 再除以該批資料的標準差, 這樣就可以讓激活值轉成平均值為 0, 標準差為 1 的標準常態分布, 使特徵具有相同的計量標準.
- 訓練神經網路是不只是希望能訓練出可解釋 x 與 y 關聯的模型, 而是希望還原背後母體的潛在分布, 這樣模型不僅能解釋已知的資料點關係, 也能預測從同樣母體分布採樣的新資料點. 若模型只能解釋已知的資料點, 卻無法預測從未見過的新資料, 則此模型有 "過度配適 (over-fitting)" 問題, 若訓練的樣本不足, 而模型設計得太複雜 (參數太多) 就可能出現 over-fitting 現象. 抑制 over-fitting 常使用下列方法 :
(1). L1 常規化 (regularization) : 又稱 LASSO 迴歸法
(2). L2 常規化 (regularization) : 又稱 ridge 迴歸法
(3). 丟棄法 (dropout)
(4). 資料擴增 (data augmentation)
這兩種方法都是在損失函數中添加懲罰項 (penalty term), 用來懲罰模型參數之過度擴張. 在訓練網路時, 若參數值越大就給予越大的懲罰, 使其往參數值較小的方向優化. L1 的懲罰項為權重取絕對值之總和; 而 L2 的懲罰項為權重的平方和.
丟棄法為深度學習之父 Geofrrey Hinton 等人所提出用來抑制 over-fitting 的方法, 被首次使用於奪得 2012 年 ILSVRC 競賽的 AlexNet 上, 這也是業內最常用的方法.
資料擴增是以現有資料來生成大量人造資料, 藉以彌補因資料不足而造成之 over-fitting 問題, 以圖片資料為例, 可透過影像平移, 扭曲, 模糊, 旋轉等處理來產生新資料. - Dropout 的概念是在每一批次 (batch) 的訓練中, 隨機將該層一定比例的神經元設為 0, 亦即無視於這些神經元的結果, 好像它們不存在一樣. 丟棄法之所以能發揮抑制 over-fitting 作用, 原理在於神經網路會傾向於以極端的邏輯來詮釋訓練資料集, 導致演化出一個只依賴少許神經元組成的前向傳播路徑, 而 dropout 的方法會在這條路徑形成之前因為部分神經元被隨機丟棄而瓦解, 這樣模型便不會過度依賴某幾種特徵來預測結果.
- Dropout 的比率也是超參數, 通常需要參考經驗來設定 :
(1). 不需要在每一個隱藏層後面加 dropout, 對於稍微深層的網路只要在後面幾層加 dropout 就可以了, 因為前面那幾層也許捕捉不到甚麼特徵. 先在最後一個隱藏層加 dropout, 看看是否能有效抑制 over-fitting, 否則再往前一層加 dropout.
(2). 如果只加了一點 dropout 就讓驗證損失爆增, 表示 dropout 破壞力太強, 應該拿掉.
(3). 哪一層該丟棄多少神經元, 每個神經網路的比例都不同, 需反覆測試. 經驗顯示, 機器視覺應用的 dropout 設 20~50% 可將驗證準確率提到最高; 而自然語言處理應用方面可調小一點, 最佳設定為 20%~30%. - 優化器的種類 :
(1). SGD
(2). 動量 (momentum) :
在權重修正公式中添加上一次的權重修正量, 此修正量會先乘以一個超參數 beta (0~1, 一般設為 9) 用來控制前一次的梯度變化對參數之影響力.
(3). Nestero 動量 :
此法是先預測參數會被動量影響至何處, 亦即估計影響之趨勢點, 以趨勢點為目標求出梯度,再用梯度來調整參數.
(4). AdaGrad (Adaptive Grdient) :
以上兩種動量所用之學習率是固定套用到所有參數的學習上, 而 AdaGrad 則是每個參數都有其獨立之學習率, 已完成優化的參數就停止學習, 否則就繼續學習. 使用 AdaGrad 最大的好處是不用去調整學習率 (它會自動調整各參數學習率), 但缺點是學習率會學著訓練進行而被稀釋, 最終因為值太小而停止學習.
(5). AdaDelta :
為了克服 AdaGrad 的缺點, AdaDelta 完全取消學習率, 但多了一個衰減率超參數 rho (意義與動量之 beta 參數差不多, 建議設為 0.95). 不過 tf.keras 的 AdaGrad 仍保留了學習率參數, 使用時要將其設為 1.
(6). RMSprop :
此優化器為 Geoffrey Hinton 所提出, 原理與 AdaDelta 類似, 也有一個衰減率超參數 rho, 學習率建議設為 0.001.
(7). Adam :
Adam 集前面所有優化器之大成, 是最常用的優化器, 其原理與 RMSprop 類似, 它有兩個超參數 beta1 (建議設為0.9) 與 beta2 (建議設為 0.99), 學習率建議設為 0.001. - 以 tf.keras 實作加入 dropout 與 Batch Normalization 之深度神經網路 :
以下是在第五章 MNIST 資料集測試範例基礎上增加兩層隱藏層, 且都使用批次正規化, 最後一個隱藏層添加丟棄層 :
(1). 匯入模組 :from tensorflow.keras.datasets import mnistfrom tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import Densefrom tensorflow.keras.layers import Dropout #新增from tensorflow.keras.layers import BatchNormalization #新增from tensorflow.keras.utils import to_categoricalfrom tensorflow.keras.optimizers import SGD
(2). 載入 MNIST 資料集 :(X_train, y_train), (X_test, y_test)=mnist.load_data()
(3). 資料預處理 :X_train=X_train.reshape(60000, 784).astype('float32')X_test=X_test.reshape(10000, 784).astype('float32')X_train /= 255X_test /= 255
(4). 標籤預處理 :y_train=to_categorical(y_train, 10)y_test=to_categorical(y_test, 10)
(5). 建構神經網路 :(6). 編譯模型 :model = Sequential()#輸入層+第一隱藏層model.add(Dense(64, activation='relu', input_shape=(784,)))model.add(BatchNormalization())#第二隱藏層model.add(Dense(64, activation='relu'))model.add(BatchNormalization())#第三隱藏層model.add(Dense(64, activation='relu'))model.add(BatchNormalization()) #批次正規化model.add(Dropout(0.2)) #每批次隨機丟棄此層 20% 神經元#輸出層model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])注意, 此處與第五章不同之處為損失函數改用交叉熵而非 MSE, 且優化器改用 Adam 而非 SGD.
(7). 訓練模型 :model.fit(X_train, y_train, batch_size=128, epochs=20, verbose=1, validation_data=(X_test, y_test))
第一輪驗證就上 95.3%, 最終達 97.5%, 效果很不錯. 以上演練筆記本參考 :
# https://github.com/tony1966/colab/blob/main/deep_learning_illustrated_ch9_deep_mnist.ipynb - 迴歸問題 : 以波士頓房價預測為例
之前的範例皆是分類問題, 此處改做迴歸問題, 以波士頓房價資料集為例 :
(1). 匯入模組 :from tensorflow.keras.datasets import boston_housingfrom tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import Densefrom tensorflow.keras.layers import Dropoutfrom tensorflow.keras.layers import BatchNormalizationimport numpy as np
(2). 載入 boston_housing 資料集 :(X_train, y_train), (X_test, y_test)=boston_housing.load_data()
(3). 檢視資料 :
檢視資料集的形狀 :X_train.shape => (404, 13)X_test.shape => (102, 13)y_train.shape => (404,)y_test.shape => (102,)X_train[0] => array([ 1.23247, 0. , 8.14 , 0. , 0.538 , 6.142 , 91.7 , 3.9769 , 4. , 307. , 21. , 396.9 , 18.72 ])(4). 建立神經網路 :y_train[0] => 15.2此處因是預測連續值, 故輸出層只有一個神經元, 使用 linear 激活函數. (5). 檢視模型摘要 :model = Sequential()#輸入層+第一隱藏層model.add(Dense(32, activation='relu', input_dim=13))model.add(BatchNormalization())#第二隱藏層model.add(Dense(16, activation='relu'))model.add(BatchNormalization())model.add(Dropout(0.2)) #每批次隨機丟棄此層 20% 神經元#輸出層model.add(Dense(1, activation='linear'))model.summary()
(6). 編譯網路 :model.compile(loss='mean_squared_error', optimizer='adam')此處因為是迴歸問題, 故不適用交叉熵, 應該用 MSE.
(7). 訓練網路 :model.fit(X_train, y_train, batch_size=8, epochs=32, verbose=1, validation_data=(X_test, y_test))
最後驗證損失是 33, 似乎還不是很理想, 需要反覆調校超參數以達到較佳結果.
(7). 以模型進行預測 :model.predict(np.reshape(X_test[42], [1, 13])) => array([[17.234053]], dtype=float32)此處是用第 42 筆樣本的 13 個預測變量, 預測前須先將其形狀由 (13,) 改成 (1, 13), 結果預測價格是 17234 美元.
(8). 檢視實際房價 :y_test[42] => 14.1
實際房價是 14100 美元, 此處預測結果 17234 比書上的結果 24171 元還準.
以上演練之筆記本參考 :
# https://github.com/tony1966/colab/blob/main/deep_learning_illustrated_ch9_deep_boston.ipynb - 章末介紹了 TensorBoard 用法, 它可以將訓練結果與神經網路以視覺化方式呈現. 以上面這個 Boston 房價的迴歸預測問題為例, 只要在匯入模組時從 tf.keras.callbacks 匯入一個 TensorBoard 類別 :from tensorflow.keras.callbacks import TensorBoard #新增
然後在訓練之前選定一個 Google Drive 資料夾當紀錄檔目錄來建立 TensorBoard 物件 :tensorboard=TensorBoard(log_dir='/content/drive/MyDrive/Colab Notebooks')
最後在訓練指令中將此物件傳給 callbacks 參數 :model.fit(X_train, y_train, batch_size=8, epochs=32, verbose=1,validation_data=(X_test, y_test), callbacks=[tensorboard])
其餘程式碼都一樣. 訓練完成後, 輸入下列指令就會用 TensorBoard 以視覺化方式繪製損失隨訓練週期下降的互動圖形 :%load_ext tensorboard%tensorboard --logdir '/content/drive/MyDrive/Colab Notebooks'
以上演練之筆記本參考 :
# https://github.com/tony1966/colab/blob/main/deep_learning_illustrated_ch9_deep_boston_tensorboard.ipynb
沒有留言:
張貼留言