2022年4月30日 星期六

購買 340W 太陽能板 + 100AH 深循環電池

今天下午回鄉下順路去仁武載上周向臉書 505 電工坊劉小姐預定的茂迪 340W 太陽能板, 位置在八德西路後港巷 86-3 號, 我走鼎力路過了後備司令部後在仁雄路左轉八德西路, 穿過國十高架橋經過 7-11 後看見左前方中油加油站, 右手邊斜巷子口即是. 

本想改買元晶的 3200 元, 但看到還沒拆箱就還是買原先訂的茂迪 3800 元. 我本來沒打算現在就買電池, 但剛好店裡有九成新的二手深循環 100 AH 電池, 一顆 3500 元, 劉小姐說這批內阻很低質優, 下次來可能已賣完了, 想說既然開車來了, 反正早晚也是要買那就一起買吧! 總計 3800+3500=7300. 

四點多回到家, 爸協助將板子搬下車後直接搬到頂樓, 這塊尺寸較大, 費了一番功夫在樓梯轉了三次彎才順利搬上去. 原本計畫裝在鐵皮屋頂, 但考量到要沿著白鐵梯子搬上去不容易, 必須兩三個人協力才行, 可能還是安裝在並聯型那兩塊旁邊. 

2022-05-01 補充 : 

今天開始天氣轉陰, 但中午有出太陽, 我把板子搬出來量測, 在較弱的陽光下輸出約 38 V :





此板後面的銘牌如下 :




可見開路電壓 41.6 V, 短路電流 10.49 A, 最大功率 345W. 

岳雲鵬的 "問心無愧最自在"

今天在 Youtube 看到這個充滿台味的中國歌曲 "問心無愧最自在" : 





這首歌其實是改編自台灣台語歌手林清國的一首老歌 "擱再拼看覓" :

https://www.youtube.com/watch?v=OziqkbX2RK8




岳雲鵬是中國知名相聲演員, 乃北京相聲表演團體德雲社創始人郭德綱的弟子. 他的臉形充滿喜感, 是個天生的演員. 

2022-05-04 補充 :

我是在臉書看到小鼓手博雅爵士鼓演奏才聽到這首歌的, 今天在 Youtube 找到這影片 : 





這位博雅小妹妹好厲害啊! 熟練又有自信, 後面指導的那位老師 (小貓老師) 也很入戲, 聽其口音似乎是香港人, 我猜這個爵士鼓教室應該是在廣州吧? 

市電 + 太陽能混合驅動的冷氣

今天在找尋太陽能資訊時發現這個太陽能冷氣產品, 我覺得蠻有創意的 :





從影片介紹可知此設計使用 8 塊太陽能板與 220V 市電混合驅動 2.5 噸的直流電機冷氣, 在正常日照時幾乎可完全替代市電, 當日照較弱時太陽能貢獻比率降低, 市電就吃比較多, 日落後就會完全吃市電. 

冷氣機有直流與交流 220V 兩種電源輸入, 交流應該是經過整流穩壓後才饋入直流電機, 這些轉換電路都裝在冷氣機裡面, 故其設計不需要逆變器與蓄電池, 減少了電池與轉換的損耗, 頗具實用性與巧思. 

2022年4月29日 星期五

最詳細的 P-51 Mustang 野馬式戰鬥機解說

今天在 Youtube 看到關於 P-51 野馬式戰鬥機的動畫解說, 超詳盡 !





P-51 戰鬥機是美國陸軍航空兵於二戰期間所使用的主力機種之一, 曾被 Discovery 評選為歷史上十大戰鬥機第一名, 參考 :

# Wiki : P-51戰鬥機

可愛的小咖

公司文康大樓大廳早上常有兩隻貓在沙發上過夜, 一隻是咖啡色的, 我叫它小咖; 另外一隻是黑色的, 我叫它小黑, 兩隻個性大不同, 小咖是人來瘋的樂天派, 看到人會大聲的喵喵叫 (真的很大聲, 可說是聲如洪鐘); 小黑則是自閉貓, 靜靜的不理人. 

每次停好車走樓梯上來遇到熱情的小咖對我喵喵叫, 但沒帶甚麼吃的給它就覺得很抱歉. 今年初我開始用易口舒薄荷錠的小空罐子裝貓飼料放在書包裡, 如果經過文康大廳有遇到就餵牠們, 久而久之小咖似乎認得我了, 有時來得晚一些會走到樓梯邊等我, 但若太晚到公司它們就跑不見了. 即使是騎機車, 我還是會從機車停車棚走到文康大廳看看它們還在不在, 今天走過去時發現小咖還在門邊等我耶!  




因周末休兩天, 所以今天把整罐都倒給他吃, 現在餵貓已經變成日常的期待了, 呵呵. 

2022年4月28日 星期四

到訪數 700 萬

今早在整理 ML 筆記時發現我的部落格到訪數已達 700 萬 : 




這個部落格早年是我的育兒生活日記, 孩子上了國中後能記的也不多了, 就變成了我的學習歷程記錄與生活週記兼瞎拚實錄, 總之就是日常重要事件的流水帳. 花點時間做紀錄是有用的, 例如當我要做物聯網實驗時需要某個零件, 好像之前是不是買過? 用 Google 一查就有了. 學過的技術久沒用一定會淡忘, 下次要用到時只要複習筆記就能快速恢復記憶. 人真的不能對自己的記憶太有自信. 

2022年4月27日 星期三

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

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


以下是我讀完第三章 "深度學習" 第三個演練範例 (卷積神經網路 CNN) 的摘要筆記, 此例以 CIFAR-10 影像資料集為來源訓練由兩個卷積塊與一個密集層組成的 CNN 網路, 目標是要能用訓練集資料訓練得到的模型來預測測試集裡面的圖片是飛機, 汽車, 鳥, ... 等 10 種分類中的哪一類. 

卷積神經網路是在密集神經網路結構上添加用來萃取特徵的卷積塊 (convolutional block), 疊加的卷積層能從圖片中學習到更多資訊進而提升辨識準確度. 本例所使用之卷積神經網路結構如下 : 


卷積塊基本上是卷積層與池化層的組合 (也可以包含丟棄層), 層數與組合方式可自訂, 例如可用 "卷積+卷積 +池化" 或 "卷積 +池化+卷積+池化" 等. 卷積層會配置多個卷積核, 通常是 3x3 或 5x5 方陣, 其內容即為卷積層的權重參數, 卷積核被用來掃描輸入圖片 (以指定步幅由左至右由上至下) 並進行卷積運算 (對應乘積和), 結果就是圖片的特徵圖 (feature map), 即該卷積核所萃取出來的細部特徵. 注意, 以 2D 陣列表示的圖片直接輸入卷積層, 不需要用 reshape() 拉平為向量, 因此能保持圖片中像素間的空間關係. 

卷積運算會使輸出的特徵圖尺寸比輸入圖片小, 為了讓特徵圖形狀不變, 通常會在輸入圖片外圍進行填補 (padding), 即上下左右都補 0, 因此一個 32*32 的圖片經過填補後變成 34*34 圖片 (最外層都是 0), 這樣經過卷積運算後得到的特徵圖仍為 32*32. 卷積網路的權重參數就是卷積核內的數值, 每一批次訓練中用來掃描圖片的各個卷積核都維持不變, 等下一批次時才透過優化器調整.  

池化層的作用是對卷積層輸出的特徵圖進形重點挑選, 它與卷積核類似的池化區方陣 (例如 3x3), 運作方式同樣是以指定步幅由左至右由上至下掃描特徵圖, 但池化區只做挑選運算從與期重疊的特徵圖中選值, 常用的是最大池化 (挑選區內最大值) 與平均池化 (挑選區內平均值). 注意, 池化區僅是一個篩子, 本身沒有權重參數, 其次, 池化會使特徵圖尺寸變小. 

卷積神經網路後半段與多層感知器使用的全連接密集網路相同, 因此最後一個卷積塊會先經過一個展平層將最後的 2D 特徵圖拉平為 1D 向量給密集層. 注意, 展平層也沒有權重參數. 以 CNN 網路辨識 CIFAR-10 資料集圖片的演練如下 : 
  1. 設定 Matplotlib 繪圖內嵌 :
    %matplotlib inline

  2. 匯入套件模組 :
    from tensorflow.keras.datasets import cifar10
    from tensorflow.keras.layers import Activation, Dense, Dropout
    from tensorflow.keras.layers import Conv2DMaxPool2D, Flatten
    from tensorflow.keras.models import Sequential, load_model
    from tensorflow.keras.optimizers import Adam
    from tensorflow.keras.utils import to_categorical
    import numpy as np
    import matplotlib.pyplot as plt

  3. 載入資料集 :
    (train_images, train_labels), (test_images, test_labels)= cifar10.load_data()
     

    載入的 cifar10 影像資料集包含 60000 張 RGB 全彩圖片, 每張圖片尺寸為 32x32, 每張圖片都有一個 0~9 的數字標籤 (答案), 代表十種圖片分類 :

     標籤 (label) 分類
     0 airplane (飛機)
     1 automobile (汽車)
     2 bird (鳥)
     3 cat (貓)
     4 deer (鹿)
     5 dog (狗)
     6 frog (青蛙)
     7 horse (馬)
     8 ship (船)
     9 truck (卡車)

  4. 檢視資料集 : 
    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'>

    可見訓練集與測試集都是 Numpy 陣列, 檢視其形狀 :

    print(train_images.shape) # (50000, 32, 32, 3)
    print(train_labels.shape) # (50000, 1)
    print(test_images.shape) # (10000, 32, 32, 3)
    print(test_labels.shape) # (10000, 1)

    訓練集有 5 萬筆, 測試集有 1 萬筆, 合計 6 萬筆. 圖片尺寸是 32x32, 有 RGB 三個通道, 標籤則是 0~9 的一個數值, 代表圖片的分類. 用 Matplotlib 檢視訓練集的前 10 筆圖片 :

    for i in range(10):
        plt.subplot(25, i+1)
        plt.imshow(train_images[i])
    plt.show()


    檢視這十張圖片的標籤 (答案) 並對照上面的分類 :

    print(train_labels[0:10])
    [[6]     # 青蛙
     [9]     # 卡車
     [9]     # 卡車
     [4]     # 鹿
     [1]     # 汽車
     [1]     # 汽車
     [2]     # 鳥
     [7]     # 馬
     [8]     # 船
     [3]     # 貓
    ]

  5. 資料預處理 :
    圖片的 RGB 數值均為 0~255 的數值, 為了提升模型的訓練表現, 通常會先將資料先做正規化 (normaliztion) 處理後再餵給模型, 此處使用 Min-Max 正規化 (x-min)/(max-min), 其中 min=0, max-255, 亦即將所有值都除以最大值 255 即可 :

    print("轉換前")
    print(train_images[0][0][0])
    train_images=train_images.astype('float32')/255.0
    test_images=test_images.astype('float32')/255.0
    print("轉換後")
    print(train_images[0][0][0])

    結果如下 : 

    轉換前 [59 62 63]
    轉換後 [0.23137255 0.24313726 0.24705882]

    此處檢驗訓練集第一張圖片的第一個像素, 轉換後變成 0~1 的浮點數.

  6. 標籤預處理 :
    原始標籤是 0~9 的單一整數, 因為神經網路的輸出層是用 Softmax 激活函數輸出的 10 個機率值 (one-hot 編碼), 為了與此輸出匹配比較, 必須將原始標籤經 to_categorical() 處理轉成 one-hot 編碼 :

    print("轉換前")
    print(train_labels[0]) 
    train_labels=to_categorical(train_labels, 10
    test_labels=to_categorical(test_labels, 10)
    print("轉換後"
    print(train_labels [0])

    這裡以訓練集第一個標籤為例檢視轉換前後結果 :
    轉換前 [6]
    轉換後 [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]

  7. 建構 CNN 網路模型 :
    依照上面的 CNN 網路結構圖建立神經網路 : 

    model = Sequential()
    # 第一個卷積塊 
    model.add(Conv2D(32, (33), activation='relu'
            padding='same', input_shape=(32323)))
    model.add(Conv2D(32, (33), activation='relu', padding='same'))
    model.add(MaxPool2D(pool_size=(22)))
    model.add(Dropout(0.25))
    # 第二個卷積塊
    model.add(Conv2D(64, (33), activation='relu', padding='same'))
    model.add(Conv2D(64, (33), activation='relu', padding='same'))
    model.add(MaxPool2D(pool_size=(22)))
    model.add(Dropout(0.25))
    # 密集層
    model.add(Flatten())
    model.add(Dense(512, activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(10, activation='softmax'))

    此處卷積塊裡 Conv2D() 的前兩個參數 32, (3, 3) 表示使用 32 個 3x3 的卷積核, padding='same' 表示要添加填補使輸出圖片尺寸與輸入相同, 都是 32x32. 可見每個卷積塊最後都隨機丟棄 25% 神經元, 而密集層最後面則隨機丟棄 50% 神經元. 

  8. 編譯模型與檢視模型摘要 :
    model.compile(loss='categorical_crossentropy'
          optimizer=Adam(learning_rate=0.001), metrics=['acc'])
    model.summary()



  9. 訓練模型 :
    因為計算量大, 故執行前請點選 "執行階段/變更執行類型" 選擇使用 GPU 來執行 :

    history=model.fit(train_images, train_labels, batch_size=128,
        epochs=20, validation_split=0.1)


    可見最終第 20 輪時達到 79.16% 準確率. 

  10. 繪製訓練過程 :
    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()


  11. 用測試集來評估模型表現 :
    test_loss, test_acc=model.evaluate(test_images, test_labels)
    print('loss: {:.3f}\nacc: {:.3f}'.format(test_loss, test_acc ))

    313/313 [==============================] - 2s 7ms/step - loss: 0.6860 - acc: 0.7827 loss: 0.686 acc: 0.783   

    準確率是 78.3%, 與訓練集結果差不多. 

  12. 比較測試集前十筆標籤與模型預測結果 :

    # 將測試集前十筆標籤從 ONE-HOT 編碼轉回單一數字
    test_ans=np.argmax(test_labels[:10], axis=1)
    print(test_ans)
    # 以模型預測前十筆測試集圖片
    test_predictions=model.predict(test_images[0:10])
    test_predictions=np.argmax(test_predictions, axis=1)
    print(test_predictions)

    結果完全正確 (書上有一個預測錯誤) : 
    [3 8 8 0 6 6 1 6 3 1]    # 標籤
    [3 8 8 0 6 6 1 6 3 1]    # 預測

  13. 將前十筆測試集標籤與預測結果轉成分類 :
    labels=['airplane''automobile''bird''cat''deer',
            'dog''frog''horse''ship''truck']
    print('前 10 筆預測標籤:',[labels[n] for n in test_predictions])
    test_ans=np.argmax(test_labels[:10], axis=1)
    print('前 10 筆原始標籤:',[labels[n] for n in test_ans])

    前 10 筆預測標籤: ['cat', 'ship', 'ship', 'airplane', 'frog', 'frog', 'automobile', 'frog', 'cat', 'automobile']
    前 10 筆原始標籤: ['cat', 'ship', 'ship', 'airplane', 'frog', 'frog', 'automobile', 'frog', 'cat', 'automobile']

    可見前十筆預測結果完全正確. 

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


    以上演練筆記本參考 :
    https://github.com/tony1966/colab/blob/main/reinforcement_learning_ch3_cifar10.ipynb
    此書作者的筆記本參考 :
    https://github.com/tony1966/colab/blob/main/3_2_convolution.ipynb
 

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

2022年4月24日 星期日

2022 年第 17 周記事

本周持續研讀布留川英一的 "強化式學習", 看進度這次借閱輪迴大概只能看完第三章的深度學習, 後面的強化學習要等下次書回到手上了. 不過大致翻閱後面章節發現有點難度, 可能是初次接觸這些主題, 覺得很陌生.   
 
阿蘭周二 (4/19) 下午轉一對多病房 (現在照護費已調漲為每日一千元), 看護吳大姐幫忙了三天+13小時, 費用是 8500 元, 用手機轉帳方便多了. 週六出院 (代墊住院費用 8169 元), 但機構護理長說呼吸仍有些喘, 要我去輔具中心租借 1L 氧氣機與潮溼瓶 (月租 1200, 押金 2000), 因機構的氧氣機為急用, 故明日早上要請假跑一趟旗山 (須帶殘障手冊, 診斷書), 資訊參考 :




周末兩天傍晚都有去爬獅形頂, 以前運動習慣都戴耳機聽英文, 昨天突然覺得這樣一心兩用似乎與外在世界脫離, 索性拔掉耳機, 沿著山路聽風吹鳥鳴, 竹子被風吹得咿咿呀呀也很好聽. 看這近乎靜止的田園山景, 彷彿時間也停下腳步了. 
 




早上經過種子行買了一株木瓜苗與十株秋葵, 木瓜種在菜園東側田埂邊上, 秋葵則因掀開塑膠膜後整理出的一畦菜圃須要兩三天日曬尚未種下. 週四爸說有顆波蘿蜜掉下來, 真是太可惜了, 我上周日應該採收才對, 這麼大顆少說也有 25 公斤, 送農會拍賣少說也有 1200 元以上, 只好自行處理. 爸整理完後將冰箱上層塞爆, 傍晚我拿了四包波蘿蜜去給學長與岳父以減輕冰箱負荷. 

周六原本與臉書社團認識的仁武劉老闆約好要去自取一片茂迪 340W 太陽能板 (一片 3800 元), 但剛好阿蘭周六要出院, 所以就推遲了, 可能下周六回鄉下時再順路去載. 此塊板子尺寸是長 170 cm, 寬 100 cm, 周日早上我上頂樓鐵皮屋丈量水塔兩側屋頂寬度, 朝南寬 130 cm, 朝北寬 150 cm :






才剛要夏天, 目前太陽的位置在水桶旁的轉角正上方, 角度還可以, 但到冬天時太陽會偏南, 這時曝曬率就會稍減, 所以首選位置還是朝南那側. 

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