2022年4月2日 星期六

機器學習筆記 : 深度學習的 16 堂課 (八)

 本篇繼續整理 "深度學習的16堂課" 這本書的讀後筆記, 本系列之前的文章參考 : 

  1. 神經網路的權重參數 (w 與 b) 一開始都是隨意設的, 必須經由訓練才能從資料中學到最佳的權重參數配置. 所謂的神經網路訓練, 其實就是不斷地修正所有 w 與 b 權重參數, 使得模型預測與正確答案之總誤差越小越好, 亦即訓練的目的就是要找到可讓誤差值 (損失) 極小化的最佳權重參數配置
  2. 神經網路訓練的兩個核心概念 :
    (1). 梯度下降法 (gradient descent)
    (2). 反向傳播 (back propagation)
    神經網路的權重參數就是用此二方法修正出來的. 
  3. 神經網路的訓練成果是以損失函數 (loss function) 來評估, 最常用的損失函數為 :
    (1). 均方誤差 (Mean Squared Error, MSE) :



    (2). 交叉熵 (Cross Entropy) :



  4. 用 Numpy 的 log() 函式計算交叉熵 :

    from numpy import log
    def cross_entropy(y, yhat):
        return -1*(y*log(yhat) + (1-y)*log(1-yhat))

    注意, Numpy 的 log() 是自然對數 (以 e 為底), 常用對數 (以 10 為底) 是 log10().
  5. 梯度其實就是損失函數的斜率. 所謂的梯度下降法就是透過不斷地修正權重參數讓損失值下降且朝著最小值前進的方法
  6. 梯度下降法中下降步伐的大小稱為學習率 (learning rate), 這是需要人為設定的超參數 (hyper parameter), 學習率設太小, 權重參數要修正很多次才可能到達最小誤差點; 但設太大又可能一下子跨過谷底, 導致在最低點附近來回跳動始終到達不了最低點. 通常學習率會從 0.01 或 0.1 開始嘗試, 若隨週期增加損失值降幅不大, 可試著將學習率調高; 若發現損失值隨著 epoch 不穩定地上下波動, 那就調降學習率. 在 tf.keras 中, 學習率是在編譯模型時於優化器中設定, 例如 :
    model.compile(loss='mean_squared_error',
                             optimizer=optimizers.SGD(learning_rate=0.01),
                             metrics=['accuracy])

  7. 批次量 (batch_size) 是每次餵給模型的訓練資料量, 通常不會把全部訓練資料一次丟進模型訓練, 這可能導致記憶體或電腦計算能力超出負荷, 而是分成一小批一小批分批丟, 即使電腦硬體配備超強, 分批訓練的成效也比一次丟要好. 批次量也是超參數, 需要透過反覆嘗試才能找到最佳值. 在 tf.keras 中批次量是在訓練時以 batch_size 參數指定, 例如 :
    model.fit(X_train, y_train,
                   batch_size=128,   
                   epoch=200,
                   verbose=1,
                   validation_data=(X_test, y_test))
    以 MNIST 手寫辨識資料集為例, 60000 筆的訓練資料每次取 128 筆, 則每個 epoch 需要 60000/128=469 批才會跑完全部資料. 在每個 epoch 開始時, SGD 會先將 60000 筆訓練圖片的順序打亂 (這就是 SGD 的 Stochastic 由來), 然後從這些被打亂順序的資料中依序取 128 筆丟進模型做訓練, 直到做完 469 批才完成一個訓練週期 (epoch), 每一個批次跑完都會進行權重參數修正以期降低損失值, 故一個週期會做 469 次權重修正. 做完一個週期, 在進行下一周期之前, 又會再次將 60000 筆資料打亂, 所以若 epoch 設為 200, 則整個訓練會做 200 次打亂動作. 
    epoch 也是超參數, 其設定也是個經驗值, 如果驗證集的損失隨 epoch 增加而持續降低, 則可以提升 epoch 設定值; 反之則可能發生過度適配 (over-fitting), 須將 epoch 降低. 
  8. 反向傳播 (back propagation) 就是微積分中的連鎖法則 (chain rule) 應用, 求損失函數對所有權重參數的梯度就是在用連鎖法則計算偏微分, 一開始會以隨機值將 w 與 b 參數初始化, 然後從最靠近輸出端的權重梯度開始算起, 接著反向計算前一層權重的梯度直到最前面的輸入層. 
  9. 一般而言, 經反向傳播計算後, 離輸出層最近的隱藏層參數對損失值的影響會最大 (梯度值大), 而離輸出層越遠 (越靠近輸入層) 的參數對損失值的影響就越被稀釋, 主要是連鎖法則讓小於 1 的值越乘越小之故. 當梯度值降至 0, 權重就沒得修正了, 若神經網路疊太多層就會在靠近輸入層的隱藏層出現梯度為 0 現象, 稱為梯度消失. 因此若較少層數就能解決問題, 神經網路架構越簡單越好, 如果增加層數能降低損失值再增加. 
  10. 下面是在第五章範例上多加一層 64 個神經元的隱藏層重做 MNIST 手寫辨識訓練, 前面載入模組與資料預處理部分都相同, 只有模型建構與編譯訓練設定不同 :
    (1). 匯入模組 :
    from tensorflow.keras.datasets import mnist     
    from tensorflow.keras.models import Sequential   
    from tensorflow.keras.layers import Dense            
    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
    (2). 載入 MNIST 資料集 :
    (X_train, y_train), (X_test, y_test)=mnist.load_data()
    (3). 資料預處理 :
    X_train=X_train.reshape(60000784).astype('float32')  
    X_test=X_test.reshape(10000784).astype('float32')      
    X_train /= 255              
    X_test /= 255
    (4). 標籤預處理 :
    y_train=to_categorical(y_train, 10)     
    y_test=to_categorical(y_test, 10
    (5). 建構神經網路 :
    model=Sequential()     
    model.add(Dense(64, activation='relu', input_shape=(784,))) 
    model.add(Dense(64, activation='relu'))   # 新增的隱藏層
    model.add(Dense(10, activation='softmax'))
    (6). 檢視網路摘要 :
    model.summary()  

    (7). 編譯模型 :
    model.compile(loss='categorical_crossentropy'
                  optimizer=optimizers.SGD(learning_rate=0.01),
                  metrics=['accuracy'])
    注意, 此處與第五章不同, 損失函數改用交叉熵. 

    (8). 訓練模型 :
    model.fit(X_train, y_train, batch_size=128epochs=20, verbose=1,  validation_data=(X_test, y_test))
    注意, 因為多加一個隱藏層準確率預期會提高, 故超參數 epochs 試著調低. 


    可見多了一層隱藏層, 只要五個週期訓練準確度就爬上 0.9, 最終是 0.94, 比第五章網路的 0.864 好很多. 以上演練之 ipynb 檔參考 :
    https://github.com/tony1966/colab/blob/main/deep_learning_illustrated_ch8_deep_network.ipynb

沒有留言 :