2022年4月23日 星期六

機器學習筆記 : 強化式學習-打造最強通用演算法 (三A)

本篇繼續來整理我讀布留川英一寫的 "強化式學習-打造最強通用演算法 AlphaZero" 這本書的摘要筆記, 本系列之前的筆記參考 : 


以下是我讀完第三章 "深度學習" 的第 一部分 MNIST 資料集之摘要筆記 :
  1. 深度學習最核心的工作就是根據大量資料建構出最佳的神經網路.
  2. 深度學習常用的損失函數, 優化器, 與評估指標 :

     常用的損失函數 說明
     mse 均方誤差, 用於評估連續值的迴歸模型
     binary_crossentropy 二元交叉熵, 用於二元分類的預測
     categorical_crossentropy 多元交叉熵, 用於多元分類的預測

     常用的優化器 說明
     SGD stochastic gradient decent (隨機梯度下降), 最傳統的優化器
     adagrad adaptive gradient, 動態學習率 (梯度小 -> 學習率大)
     adam adagrad + momentum (方向改變時學習率變慢), 性能優異

     常用的評估指標 說明
     acc accuracy (準確率), 用於分類, 越接近 1 越好
     mae 平均絕對誤差, 用於迴歸, 越接近 0 越好

  3. 監督式學習的訓練流程 :


    損失函數計算預測值與標籤的誤差; 優化器是一個演算法, 透過計算損失值的梯度 (偏微分) 來修改權重參數使損失值能逐批次降低至最小. 
  4. 資料集載入與預處理 (以 MNIST 為例) :


    將資料集切割成訓練集與測試集兩部分, 先用訓練集訓練網路模型, 再用測試集來評估訓練過的模型對新資料的普適能力 (generalization ability). 訓練集裡面又會分出一部分 (通常是 20%) 當作驗證集 (validation set), 它會被用來當作訓練過程中的先期測試, 目的是在批次訓練的同時可以知道有沒有發生過度適配 (overfitting) 現象. 這些資料切割 tf.keras 會自動進行, 不須手動分割. 

  5. 此章第一部分是用 MLP 多層感知器來辨識 MNIST 手寫數字資料集, 此處使用三層密集層與一個丟棄層, 結構如下 :


    (1). 輸入層 :
    tf.keras 的輸入層是包含第一個隱藏的, 其參數包括 :
    第一隱藏層神經元數目 units (整數, 第一參數可以不寫)
    輸入層神經元數目 input_shape (元組) 或 input_dim (整數)
    激活函數 activation (字串), 可用 'sigmoid', 'relu' 等
    (2). 隱藏層 :
    隱藏層與每層神經元數目是建構者自行指定設計的, 增加神經元與層數可捕捉到更複雜特徵, 但參數 (w 與 b) 也會增加, 若訓練資料不足容易出現過度配適問題 (overfitting), 這時網路會過度學習訓練資料 (形成固定路徑), 而非學習到通用規則, 於是無法順利辨識新資料, 即對訓練資料預測能力很好, 對新資料卻很爛 (好比是當圖庫不大時, 偷懶的學生就不求甚解, 直接把題庫背下來, 若老師出考古題可得高分, 但若稍做變化就會死得很慘).
    (3). 激活函數 :
    將神經元輸出進行非線性運算, 以便能從資料中學到複雜的非線性規則. 
    (4). 丟棄層 :
    丟棄層是為了抑制過度適配而設的, 在每批次訓練中會隨機丟棄上一層神經元輸出 (設為 0) 以減少神經元之間的依賴性, 可提升模型之普適能力, 通常加在密集層後面, 常用丟棄率 50%. 
    (5). 輸出層 :
    輸出層為密集層, 對於分類問題, 其神經元數目取決於分類類別之數量, 例如 MNIST 資料集要辨識 0~9 手寫數字, 輸出層要設 10 個神經元, 其激活函數通常使用 Softmax, 它會讓每個分類輸出之機率總和為 1, 機率最高者即為預測值. 

    Jupyter 內嵌繪圖結果 :

    %matplotlib inline

    載入套件模組 :

    from tensorflow.keras.datasets import mnist     
    from tensorflow.keras.models import Sequential   
    from tensorflow.keras.layers import Dense, Activation, Dropout            
    from tensorflow.keras.optimizers import SGD                
    from tensorflow.keras.utils import plot_model      
    from tensorflow.keras.utils import to_categorical  
    import matplotlib.pyplot as plt                                 
    import numpy as np

    下載資料集 :

    (train_images, train_labels), (test_images, test_labels)= mnist.load_data()

    檢視資料集 :

    print(type(train_images)) # <class 'numpy.ndarray'>
    print(type(train_labels)) # <class 'numpy.ndarray'>
    print(type(test_images)) # <class 'numpy.ndarray'>
    print(type(test_labels)) # <class 'numpy.ndarray'>
    print(train_images.shape) # (60000, 28, 28)
    print(train_labels.shape) # (60000,)
    print(test_images.shape) # (10000, 28, 28)
    print(test_labels.shape) # (10000,)

    注意這裡標籤都是純量, 訓練集有 60000 個 (60000,), 測試集有 10000 個 (10000,), 都是 0~9 的單一整數, 例如 train_labels 的前十筆答案如下 :

    print(train_labels[0:10]) # [5 0 4 1 9 2 1 3 1 4]  

    繪製前 10 筆圖片 (前面須用 %matplotlib inline 指令內嵌圖片於網頁中) :

    for i in range(10):
      plt.subplot(110, i+1)
      plt.imshow(train_images[i], 'gray')

    注意, Jupyter 中 plt.show() 可有可無, 結果如下 :


    可見與上面印出的 train_labels[0:10] 結果一致. 

    檢視訓練集第一個圖片 :

    print(train_images[0])


    圖片預處理 (將 2D 矩陣序列化為 1D 向量) :

    train_images=train_images.reshape((train_images.shape[0], 784))
    test_images=test_images.reshape((test_images.shape[0], 784))
    print(train_images.shape) # (60000, 784)
    print(test_images.shape) # (10000, 784)

    可見訓練集與測試集的圖片形狀 (shape) 都從 (28, 28) 變成 (784,) 了. 注意, 此處之預處理並未對訓練圖片的值做正規化 (normalization) 處理, 即除以 255 將值限縮至 0~1 之間, 而是以原來的 0~255 值進行訓練, 參考 :

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

    標籤預處理 (轉成 one-hot 編碼) :

    # 檢視第 0 張圖片之原標籤
    print(train_labels[0])
    print(test_labels[0])
    # 將全部標籤轉成 one-hot 編碼
    train_labels=to_categorical(train_labels) # 5
    test_labels=to_categorical(test_labels) # 7
    # 檢視轉換後的第 0 張圖片之新標籤
    print(train_labels[0]) # [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
    print(test_labels[0]) # [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
    # 檢視轉換後的標籤形狀
    print(train_labels.shape) # (60000, 10)
    print(test_labels.shape) # (10000, 10)

    建構神經網路 :

    # 建構神經網路模型
    model=Sequential()
    model.add(Dense(256, activation='sigmoid', input_shape=(784,))) # 輸入層+隱藏層1
    model.add(Dense(128, activation='sigmoid')) # 隱藏層2
    model.add(Dropout(rate=0.5)) # Dropout
    model.add(Dense(10, activation='softmax')) # 輸出層

    編譯模型 :

    model.compile(loss='categorical_crossentropy', optimizer=SGD(learning_rate=0.1), 
                  metrics=['acc'])

    注意, 書上學習率設定使用 lr, 但此用法已被廢棄, 新參數名為 learning_rate. 

    訓練模型 :

    history=model.fit(train_images, train_labels, batch_size=500,
        epochs=5, validation_split=0.2, verbose=1)

    結果如下 :


    只跑五個周期就達到 91.42% 了.

    顯示訓練歷史 :

    print(history.history)

    {'loss': [1.7062885761260986, 0.9367091655731201, 0.6744827628135681, 0.5535663366317749, 0.47903767228126526], 'acc': [0.4466041624546051, 0.7388125061988831, 0.8157291412353516, 0.8494583368301392, 0.867229163646698], 'val_loss': [0.9893402457237244, 0.5934872031211853, 0.44598323106765747, 0.3705187141895294, 0.3295259475708008], 'val_acc': [0.8325833082199097, 0.8818333148956299, 0.8964166641235352, 0.9075833559036255, 0.9141666889190674]}

    繪製訓練過程 :

    plt.plot(history.history['acc'], label='acc')
    plt.plot(history.history['val_acc'], label='val_acc')
    plt.ylabel('accuracy')
    plt.xlabel('epoch')
    plt.legend(loc='best')
    plt.show()


    用測試集評估 :

    test_loss, test_acc=model.evaluate(test_images, test_labels)
    print('loss: {:.3f}\nacc: {:.3f}'.format(test_loss, test_acc ))


    準確率達到 91.4%.

    預測測試集第 0 張圖片 :

    test_predictions=model.predict(test_images[0:1])
    print([round(i,4for i in test_predictions[0].tolist()])
    # 顯示預測結果的標籤
    test_predictions=np.argmax(test_predictions, axis=1)
    print(test_predictions[0])

    結果為 7, 機率為 0.9827 : 
    [0.0008, 0.0005, 0.0014, 0.0038, 0.0002, 0.0006, 0.0, 0.9827, 0.0003, 0.0097]
    7  

    繪製測試集前 10 張圖片 :
    for i in range(10):
        plt.subplot(1, 10, i+1)
        plt.imshow(test_images[i].reshape((28, 28)), 'gray')
    plt.show()


    預測測試集前 10 張圖片 :

    test_predictions=model.predict(test_images[0:10])
    test_predictions=np.argmax(test_predictions, axis=1)
    print(test_predictions)

    結果完全正確 除第 9 個錯誤外 (正確是 5 但預測為 6) 其餘都正確 :

    [7 2 1 0 4 1 4 9 6 9]

    以上演練筆記本參考 :

    https://github.com/tony1966/colab/blob/main/reinforcement_learning_ch3_mnist.ipynb


沒有留言 :