2022年4月26日 星期二

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

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


以下是我讀完第三章 "深度學習" 第二個演練範例 (波士頓房地產預測) 的摘要筆記, 此處是用深度學習來處理迴歸問題, 迴歸 (regression) 用來預測連續變數的值, 例如薪資, 股價, 房地產價格等, 此例是希望以訓練好的模型, 可依照房間數, 犯罪率, 便利性等特徵去預測波士頓的房價.  
  1. 設定 matplotlib 內嵌圖形 : 
    %matplotlib inline

  2. 匯入套件模組 : 
    from tensorflow.keras.datasets import boston_housing
    from tensorflow.keras.layers import Activation, Dense, Dropout
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.callbacks import EarlyStopping
    from tensorflow.keras.optimizers import Adam
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt

  3. 下載資料集 :

    (train_data, train_labels), (test_data, test_labels)=boston_housing.load_data()

    Downloading data from
    https://storage.googleapis.com/tensorflow/tf-keras-datasets/boston_housing.npz
    57344/57026 [==============================] - 0s 0us/step
    65536/57026 [==================================] - 0s 0us/step


    其中訓練資料集 train_data 有 404 筆 (含 13 個特徵), 測試資料集 test_data 有 102 筆. 此資料集有如下 13 個特徵, 故輸入層安排 13 個神經元與其對接 :

     特徵 說明
     CRIM 人均犯罪率
     ZN 面積 >= 25000 平方英尺的住宅用地比率
     INDUS 非零售業商業用地所佔面積比率
     CHAS 是否在查爾斯河沿岸 (1=是, 0=否)
     NOX 一氧化碳濃度
     RM 住宅平均房間數
     AGE 1940 年之前建造之物件比率
     DIS 與波士頓五個就業機構之間的加權距離
     RAD 通往環狀高速公路的便利程度
     TAX 每 10000 美元的不動產稅率總計
     PTRATIO 各城鎮的兒童與教師比率
     B 1000(bk-0.03)**2, bk=各城鎮的黑人比率
     LSTAT 從事低薪職業的人口百分比 (%)

    接下來的兩個密集層都是 64 個神經元, 且都使用 ReLu 激活函數. 最後輸出層只需要一個神經元, 因為要預測的房屋價格是單一數值 (即迴歸值). 注意, 此模型沒有使用丟棄層, 因為此資料集訓練筆數僅 404 筆, 且深度也沒有很深, 權重參數不多, 根據經驗若使用丟棄層將使準確率降低.  

  4. 檢視資料集 :
    檢視資料型態 : 
    print(type(train_data)) # <class 'numpy.ndarray'>
    print(type(train_labels)) # <class 'numpy.ndarray'>
    print(type(test_data)) # <class 'numpy.ndarray'>
    print(type(test_labels)) # <class 'numpy.ndarray'>
    結果全部都是 Numpy 陣列 <class 'numpy.ndarray'>.

    檢視資料型狀 :
    print(train_data.shape) # (404, 13)
    print(train_labels.shape) # (404,)
    print(test_data.shape) # (102, 13)
    print(test_labels.shape) # (102,)

    檢視前五筆練資料 :
    print(train_data[0:5])


    可見都是令人眼花撩亂的浮點數組成的 2D 陣列, 每一筆都有 13 個特徵之數值. 可以將這些資料丟給 Pandas 轉成 DataFrame 物件較容易觀察 :

    column_names=['CRIM''ZN''INDUS''CHAS''NOX''RM''AGE'
           'DIS''RAD''TAX''PTRATIO''B''LSTAT']
    df=pd.DataFrame(train_data, columns=column_names)
    df.head()


    檢視前五筆訓練標籤 (答案) :

    print(train_labels[0:5]) # [15.2 42.3 50. 21.1 17.7]

  5. 資料預處理 :
    包含兩步驟 : 資料集洗牌 (reshuffle) 與正規化 (normalize), 這可以獲得更好的訓練成果. 洗牌是將訓練資料的順序打亂 (其標籤也會同步打亂, 但保持不變之對應關係) : 
    order = np.random.randint(0,404, size=404)
    train_data = train_data[order]
    train_labels = train_labels[order]
    正規化是將各特徵的數值都轉成正規值, 避免某些數值較大的特徵 (例如 TAX) 主宰了權重之調整, 最常使用的正規化方法是轉成平均值為 0, 標準差為 1 的正規值, 計算方式是將特徵的值減掉該特徵所有值的平均值, 再除以該特徵所有值的標準差 :

    # 訓練集正規化
    mean=train_data.mean(axis=0)
    std=train_data.std(axis=0)
    train_data=(train_data - mean) / std
    test_data=(test_data - mean) / std

    經過這兩道程序後再用 Pandas 來觀察前五筆訓練資料 :

    column_names=['CRIM''ZN''INDUS''CHAS''NOX''RM''AGE'
           'DIS''RAD''TAX''PTRATIO''B''LSTAT']
    df=pd.DataFrame(train_data, columns=column_names)
    df.head()


    可見數值原本是好幾百的 TAX 欄位都縮到平均值 0 附近了 (因為洗牌是隨機的, 前五筆數值會與書上不同). 

  6. 建構神經網路 :
    建立兩個隱藏層的神經網路 : 


    注意, 迴歸問題的輸出層不需要激活函數. 

    model=Sequential()
    model.add(Dense(16, activation='relu', input_shape=(13,)))
    model.add(Dense(16, activation='relu'))
    model.add(Dense(1))

  7. 編譯模型 :
    model.compile(loss='mse', optimizer=Adam(learning_rate=0.001),  metrics=['mae'])
    書上用的學習率參數 lr 已經被廢棄了, 要改用 learning_rate. 

  8. 設定早期停止回呼函數 :
    early_stop=EarlyStopping(monitor='val_loss', patience=20)

    參數 monitor 用來指定訓練時要監視的對象, 此處設為 'val_loss' 表示要監視驗證誤差; patience 參數用來指定可容忍的訓練周期數, 此處設為 20 表示若超過 20 個周期監視對象並未再進步就停止訓練.

  9. 訓練模型 :
    history=model.fit(train_data, train_labels, batch_size=32, epochs=500
        validation_split=0.2callbacks=[early_stop])

    注意, callbacks 參數值為一個串列, 亦即訓練模型時可指定多個回呼函數 : 


    (略)


    可見雖然指定做 500 輪訓練, 但到 235 輪時就因為誤差已連續 20 個 epoch 都沒再改善而停止訓練以避免讓模型因無效訓練反而越變越糟.

  10. 繪製訓練過程圖形 :

    plt.plot(history.history['mae'], label='train mae')
    plt.plot(history.history['val_mae'], label='val mae'
    plt.xlabel('epoch')
    plt.ylabel('mae')
    plt.legend(loc='best')
    plt.ylim([0,5])
    plt.show()


  11. 用測試集進行評估 :

    test_loss, test_mae=model.evaluate(test_data, test_labels)
    print('loss:{:.3f}\nmae: {:.3f}'.format(test_loss, test_mae))

    4/4 [==============================] - 0s 3ms/step - loss: 17.8420 - mae: 2.7829 loss:17.842 mae: 2.783

    測試集評估結果誤差約 2.78 (理想值是 0). 

  12. 用前 10 筆資料做預測 :

    # 顯示測試集的標籤價格
    print('前 10 筆測試標籤:',np.round(test_labels[0:10]))
    # 顯示預測結果的價格
    test_predictions=model.predict(test_data[0:10]).flatten()
    print('前 10 筆預測結果:',np.round(test_predictions))

    結果如下 :
    前 10 筆測試標籤: [ 7. 19. 19. 27. 22. 24. 31. 23. 20. 23.]
    前 10 筆預測結果: [ 9. 19. 22. 35. 25. 21. 25. 21. 20. 23.]

    以上演練筆記本參考 :
    https://github.com/tony1966/colab/blob/main/reinforcement_learning_ch3_boston.ipynb
    書附檔案中的筆記本參考 :
    https://github.com/tony1966/colab/blob/main/3_1_regression.ipynb

沒有留言:

張貼留言