iT邦幫忙

2023 iThome 鐵人賽

1
AI & Data

CNN/DNN Training by using Google TensorFlow系列 第 8

【Day 08】概略了解 Keras API 的摘要 - Part 3. Model Subclassing API

  • 分享至 

  • xImage
  •  

零、前言

在上一篇文章中,我們介紹了 Sequential 和 Functional 模型的用法和 API 架構。這兩種方法雖然簡單易上手,但在資料量增加時,可能需要更多的輸入和輸出來應對龐大的資訊。例如,在診斷心血管疾病時,我們需要分析病人的年齡、性別、血壓、血糖等指標,每種指標都有適合的 Loss Function 和 Metric。然而,當我們需要添加更多分析因素時,模型是否具有足夠的彈性和可擴展性來滿足需求呢?這時 Subclassing Model 的架構就派上用場了。

一、Model Subclassing API 介紹

其實無論是任何類型的 Sequential 或 Functional 模組,都可以用Subclassing Model 的形式呈現,但如上面所述,那兩個模組不一定能完成一些特定任務,所以遇到以下四種情況,都會以 Model Subclass 的方式呈現:[2]

1. 需要動態架構:

由於 Subclassing Model 提供 Define-by-run 的介面,這代表模型的運作是可以針對不同的控制模型去做定義的。如在 RNN 與 LSTM 中,便可以用來執行自然語言處裡或是語音辨識等任務。

2. 自訂 Layers 與 Models:

如果我們要做很新型的分析方式,或要一直重複使用某些 Layers 時,就可以透過 Subclassing Model 的函數來呼叫。

3. 權重不可訓練時:

在預訓練模型如 VGG-16與 ResNet50 的轉移訓練中經常被用作為提取特徵的工具,這情況下便會預設凍結其權重使其不能被調整。

4. 具有複查控制流的 Model:

需要執行多個迴圈、變換形狀、執行分析條件分支時,便會使用Subclassing Model 來達成。

二、 如何用 Subclassing Model 格式?

對於 Model Subclassing 的架構,以下便是參考 TensorFlow 官方網站[3]在運行 MNIST 訓練資料集的範例程式碼來作說明,茲分述如下:

class <model name>(tf.keras.Model):
    def __init__(self):
        super().__init__()

    def call(self, inputs):
        ....

1. 建立 class: <model name>(tf.keras.Model)

這裡的 <model name> 可以自己定義名稱,代表這個 Class 的名稱,我們便會在這之中定義這個 Class 的屬性。而括號內的 tf.keras.Model 則代表這個 <model name> 透過繼承的方式延續了來自從別的 Class 中的 tf.keras.Model 屬性與方法。

    class CustomModel(tf.keras.Model):

2. 初始化:def __init__()

這是「建構子」,屬於 Python Class 中用作初始化物件屬性的基礎,是在 Model Subclass 形式中呼叫函數的必要方法。

這段會先在這函數中定義各種會用到的 Layers 做初始化,而後定義每一層的內部參數,如 Filter 以及 Activation Function,這順序就可以確保在指定每一個 Layer 之間的組態時,都可以被完整設定。有關 __init__() 方法的使用,可以參考 Python Tutorial [4]。

    def __init__(self, num_classes):
        super(CustomModel, self).__init__()
        self.conv1 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu')
        self.flatten = tf.keras.layers.Flatten()
        self.dense1 = tf.keras.layers.Dense(128, activation='relu')
        self.output_layer = tf.keras.layers.Dense(num_classes, activation='softmax')

3. 確保初始化:super(<var1>,<var2>,...).__init__()

藉由呼叫此方法,代表我們正在對所有自定義的 Model 中有關 tf.keras.Model 的部份做初始化,範圍包括 Model 的 Layers、Optimizer、Loss Function、以及一些特別的函數。這可以確保在這個 class 之中的初始化得以正確執行。

4. 延續 __init__(): 之後呼叫:call():

這是用於透過輸入資料呼叫 Model 當下的窗口,我們會在這邊指定輸入值在經過被 __init__() 定義的 Layers 中,將會透過哪樣的順序進行處理。

    def call(self, inputs):
        x = self.conv1(inputs)
        x = self.flatten(x)
        x = self.dense1(x)
        return self.output_layer(x)

5. 建立實例:model = <model name>()

我們會在 Class 後面用這行程式碼,使前述設定的 Model 可以被呼叫。在這範例中便是:

model = CustomModel()

6. 選擇 Loss Function、Optimizer 與 Metric

這步驟無論是 Sequential、Functional、或是 Subclassing Model 都會需要設定這些參數,讓模型可以知道運算要如何進行編譯,以供接下來的訓練。

a. <定義參數>
    loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
    optimizer = tf.keras.optimizers.Adam()
b. <訓練用>
    train_loss = tf.keras.metrics.Mean(name='train_loss')
    train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')
c. <測試用>
    test_loss = tf.keras.metrics.Mean(name='test_loss')
    test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')

7. 訓練模型:tf.GradientTape

TensorFlow 提供這個 API 執行自動微分,也就是透過根據其輸入的變數來進行自動微分,並且記錄所有執行過的運算在 tf.GradientType 中的 tape 裡面。我們在以下執行了 Prediction、Loss、與 Gradient 的運算,其中 Gradient 就是從輸入的變數中計算出來的,並藉此來更新模型的變數,這也就是模型學習的方式。

其中我們為了提高性能,便將 tf.function@ 來「修飾」,讓其被編譯成 TensorFlow 圖像運算。但如果本身顯卡不是 Nvidia 的,就會無法發揮其效果。

@tf.function
def train_step(images, labels):
    with tf.GradientTape() as tape:
        predictions = model(images, training=True)
        loss = loss_object(labels, predictions)
        gradients = tape.gradient(loss, model.trainable_variables)
        optimizer.apply_gradients(zip(gradients,model.trainable_variables))

最後我們更新在 6. 裡面提到的 Metric

    train_loss(loss)
    train_accuracy(labels, predictions)

8. 測試模型:

接下來便是要用測試資料集來檢驗我們設定的訓練模型。

我們先用 test_step 函數做為評估整個測試資料集上的模型。在這裡計算 Loss 與 Prediction 的方式原則上一致,唯 Prediction 裡面的 training=False 代表這個模型要用推理運算進行。而在最後便持續更新 Accuracy 的值。

@tf.function
def test_step(images, labels):
    predictions = model(images, training=False)
    t_loss = loss_object(labels, predictions)

    test_loss(t_loss)
    test_accuracy(labels, predictions)

我們在這邊便開始設定要訓練的 EPOCHS 次數,在每一次的 EPOCHS 中,因為都要根據每一次的測試結果做來評估,所以訓練集與測試集的 Loss 與 Accuracy 都會需要重設。

EPOCHS = 5

for epoch in range(EPOCHS):
    train_loss.reset_states()
    train_accuracy.reset_states()
    test_loss.reset_states()
    test_accuracy.reset_states()

這邊會設定訓練與測試的迴圈

    for images, labels in train_ds:
      train_step(images, labels)
    for test_images, test_labels in test_ds:
      test_step(test_images, test_labels)

最後在每一次的 EPOCHS 執行後列出訓練結果:

    print(
    f'Epoch {epoch + 1}, '
    f'Loss: {train_loss.result()}, '
    f'Accuracy: {train_accuracy.result() * 100}, '
    f'Test Loss: {test_loss.result()}, '
    f'Test Accuracy: {test_accuracy.result() * 100}'
    )

9. 訓練結果

可以看到準確度來到 98%,表現相當不錯,比起上次做心血管疾病預測的實例還要高多了!到時候來做新的預測

https://ithelp.ithome.com.tw/upload/images/20231118/201632030uGtWTzslR.png

三、後紀

我們在這邊簡單介紹了 Model Subclassing API 的使用範例,雖然在這範例中還不能明顯看出其優勢,但可以發現我們可以自己去定義 Model 的函術後呼叫,這在更複雜的分析中尤為方便。曾經看過很多強大的模型都是用 Subclassing Model 寫出來的。接下來會開始多執行些模型的預測

四、參考資料

[1] Model Sub-Classing and Custom Training Loop from Scratch in TensorFlow 2

https://towardsdatascience.com/model-sub-classing-and-custom-training-loop-from-scratch-in-tensorflow-2-cc1d4f10fb4e.

[2] Dynamic Models using Subclassing API:

https://ravimashru.github.io/100-days-of-deep-learning/days/007.html
https://keras.io/guides/making_new_layers_and_models_via_subclassing/

[3] TensorFlow Quick Start

https://www.tensorflow.org/tutorials/quickstart/advanced

[4] ‘init()’ Method:

https://www.mygreatlearning.com/blog/python-init/#:~:text=The%20python%20__init__,object%20of%20the%20class%20itself


上一篇
【Day 07】概略了解 Keras API 的摘要 - Part 2. Sequential & Functional Model 與 API 細節解說
下一篇
【Day 09】機器學習的訓練過程 - Part 1. 定義模型
系列文
CNN/DNN Training by using Google TensorFlow12
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言