返回文章列表

Keras線性模型影像分類別實作

本文介紹如何使用 Keras 建立線性模型進行影像分類別,包含 CSV 資料解析、模型建立、編譯、訓練和預測,並探討 logits、機率、softmax 函式以及不同損失函式和最佳化器的選擇與應用,同時涵蓋影像迴歸任務的模型調整。

機器學習 Web 開發

Keras 提供簡潔易用的 API 建立影像分類別模型。首先,我們利用 TensorFlow 解析 CSV 檔案,取得影像路徑和標籤,並透過函式讀取和解碼影像。為驗證資料正確性,計算並印出前幾筆影像的平均畫素值。接著,使用 Keras Sequential API 建立線性模型,包含 Flatten 層和 Dense 層,並以 Adam 最佳化器、稀疏類別交叉熵損失函式和準確率指標進行編譯。模型訓練完成後,可使用 predict 方法預測影像類別,並將 logits 轉換為機率。除了分類別任務,本文也涵蓋影像迴歸問題的模型調整,包含輸出層和損失函式的修改。

使用Keras建立線性模型進行影像分類別

在前面的章節中,我們探討瞭如何使用TensorFlow讀取和處理影像資料。本文將介紹如何使用Keras API建立一個簡單的線性模型來對花卉影像進行分類別。

解析CSV資料

首先,我們需要定義一個函式來解析CSV檔案中的每一行資料:

def parse_csvline(csv_row):
    record_defaults = ["path", "flower"]
    filename, label = tf.io.decode_csv(csv_row, record_defaults)
    img = read_and_decode(filename, [IMG_HEIGHT, IMG_WIDTH])
    return img, label

內容解密:

  • record_defaults 引數指定了解析CSV資料時每個欄位的預設值。
  • tf.io.decode_csv 函式用於解析CSV資料,並根據 record_defaults 替換缺失值。
  • read_and_decode 函式負責讀取和解碼影像檔案。

驗證資料讀取正確性

為了驗證資料讀取的正確性,我們可以列印出訓練資料集中前三張影像的平均畫素值:

for img, label in dataset.take(3):
    avg = tf.math.reduce_mean(img, axis=[0, 1])
    print(label, avg)

內容解密:

  • dataset.take(3) 取出資料集中的前三個元素。
  • tf.math.reduce_mean 計算影像的平均畫素值,透過指定 axis=[0, 1] 引數,計算每個通道的平均值。
  • 輸出的第一行是標籤(字串張量),第二行是平均畫素值(1D張量,長度為3)。

使用Keras建立線性模型

接下來,我們使用Keras建立一個線性模型:

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=(IMG_HEIGHT, IMG_WIDTH, 3)),
    tf.keras.layers.Dense(len(CLASS_NAMES))
])

內容解密:

  • tf.keras.Sequential 用於建立序列模型。
  • tf.keras.layers.Flatten 將3D影像張量展平成1D張量。
  • tf.keras.layers.Dense 建立一個全連線層,每個輸出節點代表一種類別的花卉。

編譯模型

在訓練模型之前,需要編譯模型並指定最佳化器、損失函式和評估指標:

model.compile(
    optimizer='adam',
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=['accuracy'])

內容解密:

  • optimizer='adam' 指定使用Adam最佳化器。
  • loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) 指定使用稀疏類別交叉熵損失函式,因為模型的輸出是logits。
  • metrics=['accuracy'] 指定在訓練過程中監控準確率。

預測函式

最後,我們可以使用訓練好的模型進行預測:

pred = model.predict(tf.reshape(img, [1, IMG_HEIGHT, IMG_WIDTH, NUM_CHANNELS]))

內容解密:

  • model.predict 用於對輸入影像進行預測。
  • tf.reshape 將輸入影像重新塑形為批次大小為1的張量,因為 predict 方法期望輸入是一個批次的影像。

模型輸出與機率解釋

當使用 model.predict() 方法時,輸出的 pred 張量代表什麼?回想一下,模型的最後一層是一個具有五個輸出的 Dense 層,因此 pred 的形狀是 (5,),即它包含五個數字,分別對應五種花卉型別。

預測結果的解釋

第一個輸出是模型對輸入影像是雛菊的信心程度,第二個輸出是模型對輸入影像是蒲公英的信心程度,依此類別推。這些預測信心值被稱為 logits,範圍從負無窮大到正無窮大。模型的預測結果是它最有信心的標籤:

pred_label_index = tf.math.argmax(pred)
pred_label = CLASS_NAMES[pred_label_index]

將 logits 轉換為機率

可以透過應用 softmax 函式將 logits 轉換為機率。因此,對應於預測標籤的機率是:

prob = tf.math.softmax(pred)[pred_label_index]

機率、賠率、Logits、Sigmoid 和 Softmax 的關係

分類別模型的輸出是一個機率,表示事件在多次試驗中發生的可能性。因此,在建立分類別模型時,瞭解與機率相關的幾個概念非常重要。

假設有一個事件以機率 p 發生,那麼它不發生的機率就是 1 - p。事件在任何一次試驗中發生的賠率是事件發生的機率除以不發生的機率,即 p / (1 - p)

Logit 函式

logit 是事件發生賠率的自然對數。因此,對於一個發生機率為 p=0.25 的事件,其 logit 是 log(0.25 / 0.75),即 -1.098。當機率趨近於 0 時,logit 趨近於負無窮大;當機率趨近於 1 時,logit 趨近於正無窮大。

Sigmoid 函式

sigmoid 是 logit 函式的逆函式。其數學表示式為:

σ(Y) = 1 / (1 + e^(-Y))

sigmoid 的範圍在 0 到 1 之間。如果在 Keras 中有一個輸出節點的 Dense 層,透過應用 sigmoid 可以獲得一個輸出有效機率的二元分類別器。

Softmax 函式

softmax 是 sigmoid 在多類別情況下的對應函式。如果有 N 個互斥事件,它們的 logits 由 Y_j 給出,那麼 softmax(Y_j) 提供第 j 個事件的機率。其數學表示式為:

S(Y_j) = e^(Y_j) / Σ(e^(Y_j))

softmax 函式是非線性的,具有壓縮低值和增強最大值的作用。

在 Keras 中使用 Activation Function

直接呼叫 model.predict() 是不夠的,因為它傳回的是無界的加權和。可以將這個加權和視為 logits,並應用 sigmoid 或 softmax 函式(取決於是二元分類別還是多元分類別問題)來獲得機率:

pred = model.predict(tf.reshape(img, [1, IMG_HEIGHT, IMG_WIDTH, NUM_CHANNELS]))
prob = tf.math.softmax(pred)[pred_label_index]

為了方便使用者,可以在模型的最後一層新增一個 activation function:

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=(IMG_HEIGHT, IMG_WIDTH, 3)),
    tf.keras.layers.Dense(len(CLASS_NAMES), activation='softmax')
])

這樣,model.predict() 將直接傳回五個機率(而不是 logits),每個類別一個機率。

Optimizer 的選擇

Keras 允許選擇不同的 optimizer 來根據訓練資料集調整權重。常見的 optimizer 包括:

  • SGD(隨機梯度下降):最基本的 optimizer。
  • Adagrad 和 Adam:透過新增功能來提高收斂速度。
  • Ftrl:適用於具有許多分類別特徵的極度稀疏資料集。

對於深度學習模型,Adam 是嘗試和測試過的選擇,除非有強烈的理由不使用它。

SGD 和其所有變體,包括 Adam,都依賴於接收 mini-batches(通常簡稱為 batches)的資料。對於每個批次的資料,透過模型前向傳播並計算誤差和梯度(即每個權重對誤差的貢獻程度);然後 optimizer 使用這些資訊更新權重,為下一次做好準備。

使用 Keras 的線性模型訓練與最佳化

在前面的章節中,我們討論瞭如何使用 TensorFlow 和 Keras 建立一個線性模型來進行花卉圖片的分類別。現在,我們將探討如何訓練這個模型,包括資料批次處理、梯度下降法、損失函式的選擇,以及評估模型的錯誤指標。

資料批次處理

在訓練神經網路時,我們需要將訓練資料集分成批次(batch)來進行處理。這樣做有幾個好處:首先,它可以減少記憶體的使用,因為我們不需要一次性載入所有資料;其次,它可以提高計算效率,因為現代的硬體(如 GPU 和 TPU)對於矩陣運算有著強大的最佳化能力。

train_dataset = (tf.data.TextLineDataset("gs://cloud-ml-data/img/flower_photos/train_set.csv")
                 .map(decode_csv)).batch(10)

內容解密:

  1. tf.data.TextLineDataset 用於讀取 CSV 檔案中的每一行。
  2. .map(decode_csv) 將每一行 CSV 資料對映到 decode_csv 函式進行解碼,得到圖片和標籤。
  3. .batch(10) 將解碼後的資料分成每批次 10 筆的資料集。

梯度下降法

訓練神經網路的過程實際上是透過調整權重和偏差來最小化交叉熵損失。交叉熵損失是模型權重、偏差、輸入圖片畫素以及已知類別的函式。透過計算交叉熵對於每個權重的偏導數,我們可以得到梯度。梯度指向損失增加最快的方向,因此我們沿著梯度相反的方向更新權重和偏差,以此來最小化損失。

這個過程被稱為隨機梯度下降法(Stochastic Gradient Descent)。使用批次資料可以得到一個更穩定的梯度方向,從而加快收斂速度。

訓練損失

最佳化器嘗試選擇使模型在訓練資料集上的錯誤最小化的權重。對於分類別問題,交叉熵是常用的損失函式。交叉熵的計算公式為:

[ \sum_j -L_j \log p_j ]

其中,( p_j ) 是模型預測第 ( j ) 類別的機率,( L_j ) 是真實標籤。

如果模型的預測完全正確,( p_j = 1 ),則損失為 0;如果預測完全錯誤,( p_j = 0 ),則損失趨向無窮大。

內容解密:

  1. 交叉熵損失衡量了模型預測的機率分佈與真實標籤之間的差異。
  2. 使用交叉熵作為損失函式可以鼓勵模型提高對正確類別的預測機率。

標籤表示方法

Keras 支援兩種表示標籤的方法:獨熱編碼(One-Hot Encoding)和稀疏表示。

  • 獨熱編碼將類別標籤表示為一個向量,其中只有正確類別對應的位置為 1,其餘位置為 0。例如,對於五類別分類別問題,類別 “daisy” 可能被編碼為 [1 0 0 0 0]
  • 稀疏表示則直接使用類別的索引來表示標籤。例如,“daisy” 的索引可能是 0,“tulip” 的索引可能是 4。
# 獨熱編碼
label = tf.math.equal(CLASS_NAMES, label_string)

# 稀疏表示
label = tf.argmax(tf.math.equal(CLASS_NAMES, label_string))

內容解密:

  1. 獨熱編碼適合於多標籤多類別問題,因為它可以表示一個樣本屬於多個類別。
  2. 稀疏表示在類別數量很多時更為高效,但不適合多標籤問題。

錯誤指標

雖然交叉熵損失可以用於最佳化模型,但業務使用者通常需要更直觀的錯誤指標。常見的錯誤指標包括準確率、精確率、召回率等。

機器學習模型的評估指標與訓練流程

在機器學習領域中,評估模型的效能是至關重要的一步。常見的評估指標包括準確率(Accuracy)、精確率(Precision)、召回率(Recall)以及 F1 分數等。這些指標能夠幫助我們瞭解模型的表現,特別是在處理分類別問題時。

分類別問題中的評估指標

在二元分類別問題中,例如識別假的身份證,模型的評估指標可以根據混淆矩陣(Confusion Matrix)來計算。假設我們有以下混淆矩陣:

| | 預測為假身份證 | 預測為真身份證 | | :




  • | :



– | :



– | | 實際為假身份證 | 8 (TP) | 2 (FN) | | 實際為真身份證 | 140 (FP) | 850 (TN) |

  • 準確率(Accuracy):模型正確分類別的樣本數佔總樣本數的比例。在這個例子中,準確率為 (850 + 8) / 1000 = 0.858。然而,當資料集存在類別不平衡問題時,準確率可能不是一個好的評估指標。例如,如果模型將所有身份證都預測為真,那麼準確率將高達 0.99,但這並不能代表模型的真實效能。

  • 精確率(Precision):模型預測為正類別的樣本中,真正為正類別的比例。在這個例子中,精確率為 TP / (TP + FP) = 8 / (8 + 140) = 8 / 148,表示模型的精確率很低。

  • 召回率(Recall):所有正類別樣本中,被模型正確預測為正類別的比例。在這個例子中,召回率為 TP / (TP + FN) = 8 / (8 + 2) = 0.8,表示模型能夠捕捉到大部分的假身份證。

  • F1 分數:精確率和召回率的調和平均值,用於綜合評估模型的效能。F1 分數越高,表示模型的精確率和召回率都較高。

精確率-召回率曲線與 ROC 曲線

透過改變分類別閾值,可以得到不同的精確率和召回率,從而繪製出精確率-召回率曲線。此外,還可以繪製 ROC(Receiver Operating Characteristic)曲線,即真正例率(TPR)對假正例率(FPR)的曲線。ROC 曲線下的面積(AUC)是另一個重要的評估指標,用於衡量模型的整體效能。

使用 Keras 訓練模型

建立資料集

首先,我們需要建立訓練資料集和評估資料集。這可以透過 TensorFlow 的 tf.data.TextLineDatasetmap 方法來實作:

train_dataset = (tf.data.TextLineDataset("gs://cloud-ml-data/img/flower_photos/train_set.csv")
                 .map(decode_csv)).batch(10)
eval_dataset = (tf.data.TextLineDataset("gs://cloud-ml-data/img/flower_photos/eval_set.csv")
                .map(decode_csv)).batch(10)

建立模型

接下來,我們使用 Keras 建立一個線性模型:

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=(IMG_HEIGHT, IMG_WIDTH, 3)),
    tf.keras.layers.Dense(len(CLASS_NAMES), activation='softmax')
])
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['accuracy'])

檢視模型結構

我們可以使用 tf.keras.utils.plot_model 方法來視覺化模型結構:

tf.keras.utils.plot_model(model, show_shapes=True, show_layer_names=False)

內容解密:

  1. tf.keras.Sequential:用於建立序列式神經網路模型。
  2. tf.keras.layers.Flatten:將輸入資料展平,以便輸入到全連線層。
  3. tf.keras.layers.Dense:全連線層,用於實作分類別任務。
  4. activation='softmax':輸出層使用 softmax 啟用函式,用於多分類別問題。
  5. model.compile:組態模型的損失函式、最佳化器和評估指標。
  6. tf.keras.utils.plot_model:視覺化模型結構,有助於理解模型的層次結構和引陣列態。

這個流程展示瞭如何使用 Keras 建立和訓練一個簡單的線性模型,並對其進行評估。透過選擇合適的評估指標和視覺化工具,我們可以更好地瞭解模型的效能和改進方向。

Keras 線性模型訓練與評估

模型訓練與驗證

在完成模型建立後,我們使用 model.summary() 來確認模型結構和引數數量。可以觀察到,Flatten 層沒有需要訓練的權重,而 Dense 層則有 752,645 個需要訓練的引數。

Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten_1 (Flatten) (None, 150528) 0
_________________________________________________________________
dense_1 (Dense) (None, 5) 752645
=================================================================
Total params: 752,645
Trainable params: 752,645
Non-trainable params: 0

內容解密:

  • Model.summary() 用於輸出模型的層級結構和引數統計。
  • Flatten 層將輸入資料展平為一維向量,不涉及任何權重的訓練。
  • Dense 層的引數數量計算公式為 (輸入維度 + 1) * 輸出維度,其中 +1 是為了考慮偏置項。

接下來,我們使用 model.fit() 方法進行模型訓練,並傳入訓練資料集和驗證資料集:

history = model.fit(train_dataset,
                    validation_data=eval_dataset, 
                    epochs=10)

內容解密:

  • model.fit() 是 Keras 中用於模型訓練的主要方法。
  • train_dataset 是訓練資料集,eval_dataset 是驗證資料集,用於評估模型在未見資料上的表現。
  • epochs=10 表示模型將對訓練資料進行 10 次完整的迭代。

訓練過程分析

透過檢查 history.history.keys(),我們可以獲得訓練過程中記錄的指標:

history.history.keys()

輸出結果為:

['loss', 'accuracy', 'val_loss', 'val_accuracy']

內容解密:

  • lossaccuracy 分別記錄了訓練資料集上的損失和準確率。
  • val_lossval_accuracy 則記錄了驗證資料集上的損失和準確率。

進一步地,我們可以繪製損失和準確率的變化曲線:

plt.plot(history.history['val_loss'], ls='dashed');

內容解密:

  • 該曲線顯示了驗證資料集上的損失變化趨勢,可以用來判斷模型是否過擬合或欠擬合。
  • 如果損失曲線波動較大,可能需要調整批次大小或最佳化器引數。

模型預測與評估

我們可以使用訓練好的模型對新的圖片進行預測。首先,需要將單張圖片轉換為批次格式:

batch_image = tf.reshape(img, [1, IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS])
batch_pred = model.predict(batch_image)
pred = batch_pred[0]

內容解密:

  • tf.reshape() 將單張圖片轉換為符合模型輸入要求的批次格式。
  • model.predict() 用於對輸入批次進行預測。

影像迴歸問題

除了分類別任務外,影像迴歸是另一種重要的電腦視覺任務。例如,預測雲層影像中的降雨量。這類別任務的標籤是連續的實數值,因此需要調整模型的輸出層和損失函式:

tf.keras.layers.Dense(units=1, activation=None)
tf.keras.losses.MeanSquaredError()

內容解密:

  • 將輸出層的啟用函式設為 None,以便輸出任意實數值。
  • 使用均方誤差(MSE)作為迴歸任務的損失函式。