[AI] 卷積神經網路

Convolutional Neural Network(CNN) 卷積神經網路有點像是在模仿人類判讀圖片的過程,我們在判斷一張圖是什麼時,會專注在看圖中的某些區域中(receptive field)的某個特徵,kernel(filter) 就像是偵測某一個 patten 是否存在的 neuron。 相較於一般的 fully connected(dense layer),Convolutional layer 就是利用兩個特性 receptive field: 只識別小區域的訊息 parameter sharing: 共享參數 建構 convnet 我們來建構一個簡易的小型卷積神經網路(convnet) from tensorflow import keras from tensorflow.keras import layers def get_cnn_model(): inputs = keras.Input(shape=(28, 28, 1)) x = layers.Conv2D(filters=32, kernel_size=3, activation="relu")(inputs) x = layers.MaxPooling2D(pool_size=2)(x) x = layers.Conv2D(filters=64, kernel_size=3, activation="relu")(x) x = layers.MaxPooling2D(pool_size=2)(x) x = layers.Conv2D(filters=128, kernel_size=3, activation="relu")(x) x = layers.Flatten()(x) outputs = layers.Dense(10, activation="softmax")(x) model = keras.Model(inputs=inputs, outputs=outputs) return model model = get_cnn_model() model.summmary() ...

February 3, 2025 · 2 分鐘 · Rain Hu

[AI] 機器學習的流程

機器學器的工作流程 一般而言,機器學習的通用工作流程可以歸納為三個部分: 定義任務(Define the task): 瞭解需求背後的問題領域與業務邏輯、收集資料、瞭解資料內容,並選擇衡量任務成功的標準。 開發模型(Develop a model): 準備好模型可以處理的資料、定義基準線、先訓練出一個能 overfitting 的模型,最後進行常規化、調整模型以提高普適化表現。 部署模型(Deploy a model): 將模型部署至伺服器、app、網頁或嵌入式裝置,並監控模型表現。 定義任務 問題域 輸入資料: 我們需要知道輸入資料是什麼 目標: 我們要知道想要預測什麼 任務類型 我們要定義我的的任務是哪種類型: 二元分類任務 多類別分類任務 純量迴歸 向量迴歸 多類別且多標籤任務 影像分割任務 排序任務 資料分群分任 生成任務 強式化學習任務 … 並不一定所有的任務類型都適用機器學習,傳統的統計分析方法也許更有效。 參考現有的解決方案 我們必須先瞭解當前的解決方案是怎麼運作。 限制條件 模型是在伺服器運作嗎?或是因為端到到加密,模型需在裝置上運行,並使用外部資料訓練。 資料的充足性 我們想要預測的資料是否可以透過輸入來預測。 舉例來說我們想要預測未來股價,但是我們只有該股的歷史股價,則不具備資料充足性,應該要加入如 eps 等其它表徵資料。 建立資料集 資料甚至比演算法還重要(The Unreasonable Effectiveness of Data) 投資在資料標註工具 自行標註 外包平台,如 Mechanical Turk 專業資料標註公司 注意:需考慮資料標註是否需要領域專家 留意不具代表性的資料: 資料一定要足以代表實際運作的資料 即我們的訓練資料要可以表達實際運用的場景。可以參考抽樣偏差(sampling bias) 概念漂移: 資料具有時間性,2020 年訓練出來的模型,無法描述 2025 年發生的事。 理解資料 先從資料中抽幾個樣本與標籤,對資料進行解讀。 對資料有些概觀性的瞭解,判斷是否缺少特徵值、瞭解資料的 pattern。 檢查是否存在目標值洩漏(target leaking) 的問題。 選擇測量成效的方法 考慮準確度(accuracy)、精準度(precision)、故障召回率(recall)、客戶回流率? 好的評量指標(metric) 會引導專案的所有技術選擇,盡可能要與更高層次的目標保持一致。 ROC(receiveer operating characteristic) 曲線下面積 (ROC AUC, ROC Area Under Curve) 開發模型 準備資料 ...

January 12, 2025 · 1 分鐘 · Rain Hu

[AI] 提高普適化能力

資料 如果要訓練出具備普適化的模型,那麼資料的重要性非常的大,若資料存在很大的不確定性(充滿雜訊),或是任務本身是離散,缺乏連續變化關係,那麼深度學習將無法幫上忙。在收集資料時,有以下幾點重點: 資料量: 足夠的資料量才能對輸入空間進行密集採樣(特別是 boundary),有足夠的資料才能讓樣本在流形空間中平滑的內插。就像是考試考超出範圍,學生無法用既有的知識來解題。 減少錯誤標示: 若資料有錯誤的 label,會導致模型的訓練結果不好。就像是老師教錯觀念,學生運用不對的觀念解題導致錯誤。 清理資料並處理缺失值: 若資料本身常出現缺失值,需要透過模型腦補,那也會導致模型訓練結果不好。就像老師教書時,沒有完整的教好觀念,導致學生自行腦補出錯的觀念。 特徵工程 在訓練模型前,若對資料本身有一定程度的理解,能對資料作一定程度的前處理,可以大大的降低訓練的成本並提升模型的準確度。 以時鐘辨別作為案例,我們產生類似 mnist 的像素圖(10000 * 28 * 28),並嘗試用不同的特徵來進行 training,來比較模型的成果。 我們可以嘗試使用三種不一樣的特徵來進行 training: 同 mnist,直接餵入像素圖。(即 shape=(28*28)) 使用時針與分針的卡氏座標。(即 shape=(4)) 例 (3.81, 7.48, 6.58, -9.06) 表示 (hr_x, hr_y, min_x, min_y) 使用時針與分針的極座標。(即 shape=(2)) 例 (2.67, 0.63) 表示 (hr_rad, min_rad) 由於我們透過特徵轉換,將原始資料轉換成更有效的資料,便可以增加訓練的效率。 CNN 透過 filter 與 max pooling 放大重要的圖形特徵,降維並保留重要的特徵來優化圖像的辨識,讓 model 自動發現關鍵的特徵。但在這個案例中,雖然有效,但浪費。 Early Stopping 過度訓練(overfit)是深度學習中最常遇到的問題,避免過度訓練的一個有效方法是 early stopping: 提前終止訓練: 在驗證集的誤差開始增加時,提前結束訓練過程。就像補習班老師發現學生反覆練習同一類型題目,因為背 pattern 走火入魔前,反而失去靈活的思考,便及時停止練習題目。 實作方式: 設定 patience 參數,當驗證集誤差連續超過設定次數都沒有改善時,便停止訓練並回傳最佳模型。 callback = tf.keras.callbacks.EarlyStopping( monitor='val_loss', # 監控驗證集損失 patience=7, # 容忍多少個 epoch 沒有改善 min_delta=1e-4, # 視為改善的最小變化量 mode='min', # 監控指標是越小越好 restore_best_weights=True # 回存最佳權重 ) model.fit( x_train, y_train, epochs=1000, validation_data=(x_val, y_val), callbacks=[callback] ) Regularization 正則化通過對模型權重加入懲罰項,限制模型的複雜度: L1 正則化: 添加權重絕對值的懲罰項,傾向產生稀疏的權重矩陣。 L2 正則化: 添加權重平方的懲罰項,傾向產生較小且分散的權重值。 就像老師要求學生用最簡單的方法解題,避免過度複雜的解法。 # L1 正則化 regularizer = tf.keras.regularizers.l1(l=0.01) # L2 正則化 regularizer = tf.keras.regularizers.l2(l=0.01) model = tf.keras.Sequential([ tf.keras.layers.Dense( units=512, kernel_regularizer=regularizer, activation='relu' ) ]) Dropout Dropout 是一種在訓練時隨機「關閉」一些神經元的技術: 原理: 每次訓練時隨機讓部分神經元停止工作,迫使網路學習多樣化的特徵表示。 效果: 相當於訓練多個不同的子網路並進行集成,減少過擬合。 就像是把眾多試題中的部分問題跳過不練習,避免學生學習到「特殊解」反而忘記了「一般解」。 model = tf.keras.Sequential([ tf.keras.layers.Dense(512, activation='relu'), tf.keras.layers.Dropout(0.5), # 50% 的神經元會被隨機關閉 tf.keras.layers.Dense(256, activation='relu'), tf.keras.layers.Dropout(0.3), # 30% 的神經元會被隨機關閉 tf.keras.layers.Dense(10, activation='softmax') ]) model.fit(x_train, y_train, epochs=100) model.evaluate(x_test, y_test) 注意: Drop out 的流程是 在 training 時依照 丟棄率(dropout rate) 隨機將某些特徵值歸零。 在輸出時,根據丟棄率對 dropout 後的輸出值做放大。 在測試時,不丟棄任何特徵值。

January 12, 2025 · 1 分鐘 · Rain Hu

[AI] 提升模型的表現

要達到完美擬合(perfect fit),勢必要先經歷過度擬合(overfitting),否則我們無法預先知道邊界在哪裡。 因此我們要處理任何機器學習問題的初始目標,便是訓練出一個能展現基本普適化能力,並且會發生 overfitting 的模型,接著才可以專注在解決 overfitting 並提升模型的普適化能力。 我們會遇到三個 status: 訓練沒有成效,損失值降不下來。 訓練可行,但無法展現普適化,無法超越基準線。 訓練可行且表現也超越基準線,但無法達到 overfitting。 調整梯度下降的關鍵參數 當訓練沒有成效,代表損失無法下降。 一般而言,就算資料是隨機的,模型有辦法擬合,所以損失無法下降通常可以斷定是 gradient descent 的參數設置需要調整,包含: 優化器(optimizer) 初始權重(initial weight) 學習率(learning rate) 批次量(batch size) 從上圖可見,當學習率太大時,每一次的跨幅過大,使 loss 無法降低;當學習率太小時,每次跨幅太小,訓練的效率不好,當學習率適當時,可以達到最好的成效。 學習率比較的程式碼 import numpy as np import matplotlib.pyplot as plt def objective_function(x, y): return 0.7*x**2 + 1.3*y**2 def gradient(x, y): return np.array([1.4*x, 2.6*y]) def gradient_descent(learning_rate, start_point, n_iterations=50): path = [start_point] point = np.array(start_point) for _ in range(n_iterations): grad = gradient(point[0], point[1]) point = point - learning_rate * grad path.append(point.copy()) if np.linalg.norm(grad) < 1e-6: break return np.array(path) x = np.linspace(-10, 10, 100) # 擴大範圍到 ±10 y = np.linspace(-10, 10, 100) X, Y = np.meshgrid(x, y) Z = objective_function(X, Y) plt.figure(figsize=(15, 6)) learning_rates = [0.77, 0.1, 0.01] titles = ['Learning Rate = 0.77', 'Learning Rate = 0.1', 'Learning Rate = 0.01'] start_point = np.array([4.0, 4.0]) for i, (lr, title) in enumerate(zip(learning_rates, titles)): plt.subplot(1, 3, i+1) plt.contour(X, Y, Z, levels=20, cmap='viridis') path = gradient_descent(lr, start_point) plt.plot(path[:, 0], path[:, 1], 'r.-', linewidth=1, markersize=3, label=f'Gradient Descent Path\nIterations: {len(path)}') plt.plot(path[0, 0], path[0, 1], 'g*', markersize=10, label='Start') plt.plot(path[-1, 0], path[-1, 1], 'r*', markersize=10, label='End') plt.title(title) plt.xlabel('x') plt.ylabel('y') plt.legend() plt.colorbar(label='Function Value') plt.grid(True) plt.tight_layout() plt.show() 使用不同的架構 選擇合適的模型架構處理不同類型的問題 DNN CNN(Convolutional Neural Networks) RNN(Recurrent Neural Networks) Transformer Autoencoders GAN(Generative Adversarial Networks) GNN(Graph Neural Networks) … 提升模型容量(capacity) 如果損失值的確有降低,代表模型的確有在擬合資料,但始終無終達到 overfitting,那可能是模型的 表徵能力(representational power) 不足。 這時可能需要更大的模型,以容納更多的資訊, 可以透過加深、加寬 layer 來提升模型的容量。 ...

January 12, 2025 · 2 分鐘 · Rain Hu

[AI] 評估模型

評估模型 機器學習模型就像是一個需要不斷練習的學生,我們需要適當的方法來評估它的學習成果。今天讓我們來了解如何科學地評估一個模型的表現。 何為訓練集、驗證集、測試集 在開始解釋不同的數據集之前,我們先來理解兩種重要的參數: 權重參數(weight parameter): 這是模型通過學習自動調整的參數 就像學生在解題過程中學到的解題技巧 超參數(hyperparameter): 這是我們需要手動設置的參數 就像是老師設定的學習進度和難度 數據集的三個部分各自扮演著不同的角色: 訓練集: 用於模型的主要學習階段 調整權重參數 佔總數據量約 60-80% 驗證集: 用於調整超參數 幫助我們選擇最佳的模型配置 佔總數據量約 10-20% 測試集: 用於最終的模型評估 模擬真實世界的應用場景 佔總數據量約 10-20% 資料洩漏(information leak): 指測試資料的訊息不當地影響了模型訓練,就像是考試前看到了試題,會導致模型評估結果不可靠。 進階驗驗方法 當資料不足時,就可能要採用一些進階方法來處理,以下介紹三種經典方法: 簡單拆分驗證(simple holdout validation): 最基本的方法 直接將數據分為訓練集和驗證集 優點:簡單、快速 缺點:結果可能不穩定 K折驗證(k-fold validation): 將數據分為K個相等的部分 每次使用其中一部分作為驗證集 重複K次,取平均結果 優點:更穩定的評估結果 常用的K值為5或10 K折加洗牌驗證(Iterated K-fold validation with shuffling): 在K折驗證的基礎上增加多輪重複 每輪都重新打亂數據順序 優點:最穩定的評估結果 缺點:計算成本較高 基準線(Baseline) 在評估模型是否有效時,可以在訓練前先設定一個基準線,而機器學習的目的在於打敗基準線。 以 mnist 來說,假設 0~9 的 labels 數大致相同,則盲猜能達到的準確度應該相當於期望值,也就是 \(\frac{1}{10}\)。換句話說,我們可以將基準線訂為 10%,如果模型做出來的準確度比 10% 還要差,那還不如盲猜呢! 以 IMDB 為例,準確度至少要差過 50%,因為只有正負評論兩個類別。 在路透社的文章分類目,準確度至少要超過 18~19%,因為有樣本不平衡的問題。 以工廠為例,可能每 10000 張缺陷照片會出現 1 張是有害缺陷,模型若是判斷全部 10000 張都是無害的,那正確率也有 \(\frac{9999}{10000}\),也就是 99.99%,但這代表抓不到有害缺陷,那就代表這個模型是無效的,所以當資料不平衡時,會產生很大的問題。 評估模型時的注意事項 資料代表性(data representation): 我們希望訓練集和測試集都有一定的代表性,足以反映手邊的資料分佈。 準例來說,我們在做選舉民意調查時,我們都用打市話來進行市調,做為訓練集,然後收集手機民調,做為測試集。由於市話與手機的使用族群不相同,可能導致了訓練集與測試集的代表性都不足。 故我們通常需要對資料進行洗牌(shuffle),使訓練集與測試集都具備一定的代表性。 時間的方向性(the arrow of time): 如果我們想要從過去資料來預測未來狀態,如天氣、股票走勢,則不該隨意將資料打亂並拆分成訓練集和測試集。 具備時間性的資料被打亂,會造成時間漏失(temporal leak),而造成時序錯位。 在進行具時間性的預測時,應確保測試資料的發生時間是在訓練資料之後。 資料中的重複現象: 如果資料出現兩次,隨意打亂,可能會造成資料同時出現在訓練集與測試集中,那麼就相當於資料洩漏(information leak),這樣訓練出來的模型將不可信。

January 12, 2025 · 1 分鐘 · Rain Hu

[AI] 普適化

機器學習最重要的兩件事是: 準確的模型評估 訓練次數與普適化之間的平衡 普適化(generalization) 普適化是機器學習的終極目標,什麼是普適化呢?首先要先解釋是什麼低度擬合(underfitting) 與 過度擬合(overfitting)。 觀察下方的圖,訓練集與驗證集在訓練初期,損失值都穩定下降,此時稱為 underfitting,代表神經網路尚未學習到資料中的共同特徵。經過一定時間後,驗證指標會開始停滯並開始變差,這代表模型開始發生 overfitting,代表模型已經額外學習了一些只有訓練集中的特徵,進而可能在面對新資料時造成干擾。而 穩健擬合(robust fit) 是 underfitting 與 overfitting 之間的點,代表最佳的 epochs。 下圖的黑線與就是 robust fit 的表現,綠線是 overfitting 的表現。 可以看到綠線在訓練集有很好的表現,但可能會在新的資料點進入時,有錯誤的判斷。 普適化就是找到一個面對所有資料都能有穩定且好的表現的模型。 overfitting overfitting 容易發生在 具有雜訊的資料 具有罕見特徵 標示錯誤的資料 如果訓練過程,模型針對這些離群值(outlier)進行學習,普適化表現自然會下降。 模糊特徵 然後並非所有雜訊都是由不準確性(特徵模糊/標示錯誤)產生的,當處理的問題本身就具備不確定性或模棱兩可時,就算是字跡清晰、標籤正確也可能是雜訊,特別是一些沒有明確界線的特徵。 就像是下面的三杯一樣的水,由不同的人來 label,也會 label 出不一樣的答案。 最有感的就是問卷量表,客戶滿意度 (CSAT) 調查問卷通常分為 1 (非常不滿意) 到 5 分 (非常滿意) 。每個人對於滿意度的給分都不一致,所以就容易產生差異。 穩健的模型會忽略訓練資料中個別的資料點,從眾數著眼。 罕見特徵(rare feature)與虛假關聯(spurious correlation) 罕見特徵: 通常是樣本中出現頻率極低的特徵,可能具有高辨識度或影響力,但也可能是噪音,需要小心解讀。 事故發生的地點是某條高速公路上極少使用的臨時匝道。 事故發生時段是凌晨2點到3點,且伴隨濃霧天氣。 涉事車輛是一輛載有易燃化學品的大型貨車,並與一輛滿載乘客的大巴相撞。 如何影響機器學習: 特徵稀疏性: 這些罕見的特徵組合在訓練數據中可能只出現過1-2次,但其後果卻極為嚴重(多人傷亡),模型可能過度強調這些罕見情況作為高危因素。 過擬合風險: 如果模型只根據這些少量案例進行學習,可能無法有效處理未見過的場景(如不同的道路或車輛組合)。 虛假關聯: 則是數據之間表面上有相關性,但實際上缺乏因果關係,可能由於第三因素驅動或純粹的巧合。 冰淇淋銷量與溺水事件: 描述:夏季冰淇淋銷量與溺水事件呈現高度正相關。 原因:兩者都與溫度升高相關,並非冰淇淋銷量導致溺水事件增加。 接下來來做一個實驗,我們在每一張數字中擴充維度,分別擴充 noise 與 zeros ...

January 12, 2025 · 4 分鐘 · Rain Hu

[AI] 迴歸問題

認識波士頓住房價資料集 透過 tensorflow 引入資料集 波士頓住房價資料集是 1970 年代中期波士頓的郊區資料,包含犯罪率、當地財產稅等。 from tensorflow.keras.datasets import boston_housing (train_data, train_targets), (test_data, test_targets) = boston_housing.load_data() 與先前兩個範例最大的差別是,資料點明顯較少,共 404 + 102 = 506 筆。 print(train_data.shape) print(train_targets.shape) print(test_data.shape) print(test_targets.shape) > (404, 13) (404,) (102, 13) (102,) 需注意,每個特徵都有不同的單位刻度 查看特徵分布 特徵值範圍分析: 特徵 最小值 最大值 平均值 標準差 特徵意義 CRIM 0.006 88.976 3.614 8.602 城鎮人均犯罪率 ZN 0.000 100.000 11.364 23.322 佔地面積超過25000平方呎的住宅用地比例 INDUS 0.460 27.740 11.137 6.860 每個城鎮非零售商業用地的比例 CHAS 0.000 1.000 0.069 0.254 Charles River 虛擬變數 (1 if tract bounds river; 0 otherwise) NOX 0.385 0.871 0.555 0.116 一氧化氮濃度 RM 3.561 8.780 6.285 0.703 每棟住宅的平均房間數 AGE 2.900 100.000 68.575 28.149 1940年之前建成的自用房屋比例 DIS 1.130 12.126 3.795 2.106 到波士頓5個就業中心的加權距離 RAD 1.000 24.000 9.549 8.707 到高速公路的可達性指數 TAX 187.000 711.000 408.237 168.537 每10000美元的房產稅率 PTRATIO 12.600 22.000 18.456 2.165 城鎮師生比例 B 0.320 396.900 356.674 91.295 1000(Bk - 0.63)^2,其中Bk為城鎮中黑人的比例 LSTAT 1.730 37.970 12.653 7.141 人口中地位較低人群的百分比 程式碼: ...

January 12, 2025 · 3 分鐘 · Rain Hu

[AI] 多元分類問題

認識路透社(Reuters)資料集 透過 tensorflow 引入資料集 參數 path: 數據的緩存位置(相對於 ~/.keras/dataset)。 num_words: 整數或 None。單詞按其出現頻率(在訓練集中)進行排名,並且僅保留 num_words 個最常見的單詞。任何較不常見的單詞在序列數據中都將顯示為 oov_char值。如果為 None,則保留所有單詞。默認為 None。 skip_top: 跳過前 N 個最常出現的單詞(這些單詞可能沒有信息量)。這些單詞在數據集中將顯示為 oov_char 值。0 表示不跳過任何單詞。默認為 0。 maxlen: int 或 None。最大序列長度。任何較長的序列都將被截斷。None 表示不截斷。默認為 None。 test_split: 介於 0. 與 1. 之間的浮點數。用作測試資料集的比例。0.2 表示 20% 的資料集用作測試資料。預設值為 0.2。 seed: 整數。用於可重複資料洗牌的種子。 start_char: 整數。序列的開頭將標記為此字元。0 通常是填充字元。預設值為 1。 oov_char: 整數。超出詞彙表的字元。由於 num_words 或 skip_top 限制而被刪除的詞彙將替換為此字元。 index_from: 整數。使用此索引及更高的索引來索引實際詞彙。 回傳值 Numpy 陣列的 tuple: (x_train, y_train), (x_test, y_test)。 相同於 IMDB 資料集,資料集包含了許多相異單字,這數字對訓練而言非常龐大,且對分類任務沒什麼幫助,所以我們只保留 10000 個最常出現的單字 from tensorflow.keras.datasets import reuters (train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000) Reuters 路透社資料集是 1986 年由路透社發佈的一組簡短新聞和對應主題的資料集,被廣泛用於文章分類的研究。 ...

January 6, 2025 · 3 分鐘 · Rain Hu

[AI] 二元分類問題

1. 認識 IMDB 資料集 透過 tensorflow 引入資料集 參數 path: 資料的快取位置(相對於 ~/.keras/dataset)。 num_words: 整數或 None。單詞按其出現頻率(在訓練集中)進行排序,並且僅保留 num_words 個最常出现的单词。任何較不常出现的单词在序列資料中都將顯示為 oov_char 值。如果為 None,則保留所有單詞。預設值為 None。 skip_top: 跳過出現頻率最高的前 N 個單詞(這些單詞可能沒有參考價值)。這些單詞在資料+ 集中將顯示為 oov_char 值。當為 0 時,則不跳過任何單詞。預設值為 0。 maxlen: 整數或 None。最大序列長度。任何較長的序列都將被截斷。None 表示不進行截斷。預設值為 None。 seed: 整數。用於可重現資料洗牌的種子。 start_char: 整數。序列的開頭將標記為此字元。0 通常是填充字元。預設值為 1。 oov_char: 整數。詞彙表外字元。由於 num_words 或 skip_top 限制而被刪除的詞彙將替換為此字元。 index_from: 整數。使用此索引和更高索引為實際詞彙編制索引。 回傳值 Numpy 陣列的 tuple: (x_train, y_train), (x_test, y_test)。 其中資料集包含了 88585 個相異單字,有很大的單字甚至只有出現一次,這數字對訓練而言非常龐大,且對分類任務沒什麼幫助,所以我們只保留 10000 個最常出現的單字 from tensorflow.keras.datasets import imdb (train_images, train_labels), (test_images, test_labels) = imdb.load_data(num_words=10000) IMDb (Internet Movie Database) 是一個與影視相關的網路資料庫,IMDB 資料集就是從 IMDb 網站收集而來的資料。資料包含了 50000 筆評論,其中包含了 50% 的正面評論與 50% 的負面評論。 print(train_data.shape) print(train_labels.shape) print(test_data.shape) print(test_labels.shape) > (25000,) > (25000,) > (25000,) > (25000,) 資料的組成是由一連串的數字所組成,如: \( \begin{array}{|c|c|c|c|c|c|c|c|c|c|c|} \hline \text{(保留)} & \text{the} & \text{and} & \text{a} & \text{…} & \text{in} & \text{…} & \text{wonderful} & \text{…} & \text{morning} & \text{…} \\ \hline 0 & 1 & 2 & 3 & … & 8 & … & 386 & … & 1969 & …\\ \hline \end{array} \) 每個數字代表一個單字,編號愈前面代表愈常用。 print(train_data[0]) > [1, 14, 22, 16, 43, 530, 973, 1622, ..., 32] 我們可以用 imdb.get_word_index(path="imdb_word_index.json") 來得到這個字典,並試著還原原始評論。 注意按引 0~2 為留保字,故索引值需位移 3。 dict = imdb.get_word_index(path="imdb_word_index.json") index_to_word = {value: key for key, value in dict.items()} sentence_0 = ' '.join([index_to_word.get(idx, '?') for idx in train_data[0]]) print(sentence_0) > ? this film was just brilliant casting location scenery story direction labels 是由 1, 0 組成的陣列,代表正評(1)或負評(0) print(train_labels[:10]) > array([1, 0, 0, 1, 0, 0, 1, 0, 1, 0]) 2. 準備資料 由於目前的測試資料的長度都不一樣長,我們需要預先做整型,方法有二: 填補資料中每個串列,使長度相同。 做 mutli-hot(k-hot) 轉換。 \( \begin{array}{|c|} \hline \text{column}\\ \hline \text{A}\\ \hline \text{B}\\ \hline \text{C}\\ \hline \text{A}\\ \hline \text{A}\\\hline \end{array} \rightarrow \begin{array}{|c|c|c|} \hline \text{A} & \text{B} & \text{C}\\ \hline 1 & 0 & 0\\ \hline 0 & 1 & 0\\ \hline 0 & 0 & 1\\ \hline 1 & 0 & 0\\ \hline 1 & 0 & 0\\\hline \end{array} \) 將測試資料轉成 multi-hot 型式: import numpy as np def vectorize_sequences(sequences, dimension=10000): results = np.zeros((len(sequences), dimension)) for i, sequence in enumerate(sequences): results[i, sequence] = 1. return results x_train = vectorize_sequences(train_data) x_test = vectorize_sequences(test_data) 將 labels 也轉成向量資料 y_train = np.asarray(train_labels).astype('float32') y_test = np.asarray(test_labels).astype('float32') 3. 建立神經網路 我們的輸入資料是向量、標籤為 0 與 1,我們可以使用一個密集層堆疊架構搭配 relu 函數。 ...

January 2, 2025 · 3 分鐘 · Rain Hu

[AI] 3-8. 客製化 Training

內建的 fit() 只適用於監督式學習(supervised learning),然而不是所有的機器學習任務都適用,如生成式學習(generative learning)、自監督式學習(self-supervised learning)、強化式學習(reinforcement learning) 等。 監督式學習的訓練 在實作 Keras 訓練迴圈時有兩個重要細節: training 參數: 某些層(如 Dropout)在訓練和推論時行為不同 訓練時需設定 training=True 推論時設定 training=False 模型權重分類: Trainable weights: 可訓練的權重 Non-trainable weights: 不可訓練的權重(如 BatchNormalization 層的統計值) 取梯度時應使用 model.trainable_weights 以下是完整可執行範例: import tensorflow as tf from tensorflow import keras import numpy as np # 建立範例資料 x_train = np.random.random((1000, 28, 28)) y_train = np.random.randint(10, size=(1000,)) # 建立包含 Dropout 和 BatchNormalization 的模型 model = keras.Sequential([ keras.layers.Flatten(input_shape=(28, 28)), keras.layers.Dense(128), keras.layers.BatchNormalization(), keras.layers.Dropout(0.5), keras.layers.Dense(10) ]) # 損失函數與優化器 loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True) optimizer = keras.optimizers.Adam(learning_rate=1e-3) # 自定義訓練步驟 @tf.function def train_step(inputs, targets): with tf.GradientTape() as tape: # 訓練模式前向傳播 predictions = model(inputs, training=True) loss = loss_fn(targets, predictions) # 計算可訓練權重的梯度 gradients = tape.gradient(loss, model.trainable_weights) optimizer.apply_gradients(zip(gradients, model.trainable_weights)) return loss # 訓練迴圈 batch_size = 32 for epoch in range(5): print(f"\nEpoch {epoch+1}") for step in range(0, len(x_train), batch_size): x_batch = x_train[step:step + batch_size] y_batch = y_train[step:step + batch_size] loss = train_step(x_batch, y_batch) if step % 200 == 0: print(f"Step {step}: loss = {loss:.4f}") # 推論時使用 training=False test_predictions = model(x_train[:1], training=False) 評量指標 在訓練期間,我們可以用 Keras 的評量指標來查詢當前的指標值,我們會用到幾個函式: update_state(y_true, y_pred) result() reset_state() import tensorflow as tf from tensorflow import keras import numpy as np # 建立模型 model = keras.Sequential([ keras.layers.Dense(64, activation='relu'), keras.layers.Dense(10, activation='softmax') ]) # 初始化指標追蹤器 accuracy_tracker = keras.metrics.SparseCategoricalAccuracy() loss_tracker = keras.metrics.Mean() # 訓練步驟 @tf.function def train_step(inputs, targets): with tf.GradientTape() as tape: predictions = model(inputs, training=True) loss = tf.keras.losses.sparse_categorical_crossentropy(targets, predictions) # 更新梯度 gradients = tape.gradient(loss, model.trainable_weights) optimizer.apply_gradients(zip(gradients, model.trainable_weights)) # 更新指標 accuracy_tracker.update_state(targets, predictions) loss_tracker.update_state(loss) return loss # 訓練迴圈 x_train = np.random.random((1000, 32)) y_train = np.random.randint(10, size=(1000,)) optimizer = keras.optimizers.Adam() batch_size = 32 for epoch in range(3): # 重置每個 epoch 的指標 accuracy_tracker.reset_state() loss_tracker.reset_state() for step in range(0, len(x_train), batch_size): x_batch = x_train[step:step + batch_size] y_batch = y_train[step:step + batch_size] loss = train_step(x_batch, y_batch) if step % 200 == 0: print( f"Step {step}: ", f"Loss: {loss_tracker.result():.4f}, ", f"Accuracy: {accuracy_tracker.result():.4f}" ) 完整的訓練與評估迴圈 設計練訓函式 model = get_mnist_model() loss_fn = keras.losses.SparseCategoricalCrossentropy() optimizer = keras.optimizers.RMSprop() metrics = [keras.metrics.SparseCategoricalAccuracy()] loss_tracking_metric = keras.metrics.Mean() def train_step(inputs, targets): with tf.GradientTape() as tape: predictions = model(inputs, training=True) loss = loss_fn(targets, predictions) gradients = tape.gradient(loss, model.trainable_weights) optimizer.apply_gradients(zip(gradients, model.trainable_weights)) logs = {} for metric in metrics: metric.update_state(targets, predictions) logs[metric.name] = metric.result() loss_tracking_metric.update_state(loss) logs["loss"] = loss_tracking_metric.result() return logs 重置評量指標 def reset_metrics(): for metric in metrics: metric.reset_state() loss_tracking_metric.reset_state() 設計訓練迴圈 training_dataset = tf.data.Dataset.from_tensor_slices((train_images, train_labels)) training_dataset = training_dataset.batch(32) epochs = 3 for epoch in range(epochs): reset_metrics() for inputs_batch, targets_batch in training_dataset: logs = train_step(inputs_batch, targets_batch) print(f"Results at the end of epoch {epoch}") for key, value in logs.items(): print(f"...{key}: {value:.4f}") 設計評估迴圈 def test_step(inputs, targets): predictions = model(inputs, training=False) loss = loss_fn(targets, predictions) logs = {} for metric in metrics: metric.update_state(targets, predictions) logs["val_" + metric.name] = metric.result() loss_tracking_metric.update_state(loss) logs["val_loss"] = loss_tracking_metric.result() return logs val_dataset = tf.data.Dataset.from_tensor_slices((val_images, val_labels)) val_dataset = val_dataset.batch(32) reset_metrics() for inputs_batch, targets_batch in val_dataset: logs = test_step(inputs_batch, targets_batch) print("Evaluation results:") for key, value in logs.items(): print(f"...{key}: {value:.4f}") 利用 tf.function 來加速 只要在要編譯的函式前加上 @tf.function 裝飾器就可以將 TensorFlow 程式碼編譯成運算圖(computation graph)。 搭配 fit() 和自定義的訓練的迴圈 from tensorflow import keras from tensorflow.keras import layers import tensorflow as tf # 1. 將 optimizer 移到類別內部 class CustomModel(keras.Model): def __init__(self, inputs, outputs): super().__init__(inputs=inputs, outputs=outputs) self.loss_tracker = keras.metrics.Mean(name="loss") # 2. 移到類別內 self.optimizer = keras.optimizers.RMSprop() # 3. 加入 optimizer self.loss_fn = keras.losses.SparseCategoricalCrossentropy() def train_step(self, data): inputs, targets = data with tf.GradientTape() as tape: predictions = self(inputs, training=True) loss = self.loss_fn(targets, predictions) gradients = tape.gradient(loss, self.trainable_weights) self.optimizer.apply_gradients(zip(gradients, self.trainable_weights)) # 4. 使用 self.optimizer self.loss_tracker.update_state(loss) # 5. 使用 self.loss_tracker return {"loss": self.loss_tracker.result()} @property def metrics(self): return [self.loss_tracker] # 6. 使用 self.loss_trackerxqf # 建立模型 inputs = keras.Input(shape=(28 * 28,)) features = layers.Dense(512, activation="relu")(inputs) features = layers.Dropout(0.5)(features) outputs = layers.Dense(10, activation="softmax")(features) model = CustomModel(inputs=inputs, outputs=outputs) model.compile() # 7. 移除 optimizer 參數 model.fit(train_images, train_labels, epochs=3)

December 27, 2024 · 3 分鐘 · Rain Hu