在上一篇文章中,我們介紹了 Sequential 和 Functional 模型的用法和 API 架構。這兩種方法雖然簡單易上手,但在資料量增加時,可能需要更多的輸入和輸出來應對龐大的資訊。例如,在診斷心血管疾病時,我們需要分析病人的年齡、性別、血壓、血糖等指標,每種指標都有適合的 Loss Function 和 Metric。然而,當我們需要添加更多分析因素時,模型是否具有足夠的彈性和可擴展性來滿足需求呢?這時 Subclassing Model 的架構就派上用場了。
其實無論是任何類型的 Sequential 或 Functional 模組,都可以用Subclassing Model 的形式呈現,但如上面所述,那兩個模組不一定能完成一些特定任務,所以遇到以下四種情況,都會以 Model Subclass 的方式呈現:[2]
由於 Subclassing Model 提供 Define-by-run 的介面,這代表模型的運作是可以針對不同的控制模型去做定義的。如在 RNN 與 LSTM 中,便可以用來執行自然語言處裡或是語音辨識等任務。
如果我們要做很新型的分析方式,或要一直重複使用某些 Layers 時,就可以透過 Subclassing Model 的函數來呼叫。
在預訓練模型如 VGG-16與 ResNet50 的轉移訓練中經常被用作為提取特徵的工具,這情況下便會預設凍結其權重使其不能被調整。
需要執行多個迴圈、變換形狀、執行分析條件分支時,便會使用Subclassing Model 來達成。
對於 Model Subclassing 的架構,以下便是參考 TensorFlow 官方網站[3]在運行 MNIST 訓練資料集的範例程式碼來作說明,茲分述如下:
class <model name>(tf.keras.Model):
def __init__(self):
super().__init__()
def call(self, inputs):
....
<model name>(tf.keras.Model)
這裡的 <model name>
可以自己定義名稱,代表這個 Class 的名稱,我們便會在這之中定義這個 Class 的屬性。而括號內的 tf.keras.Model
則代表這個 <model name>
透過繼承的方式延續了來自從別的 Class 中的 tf.keras.Model
屬性與方法。
class CustomModel(tf.keras.Model):
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')
super(<var1>,<var2>,...).__init__()
藉由呼叫此方法,代表我們正在對所有自定義的 Model 中有關 tf.keras.Model
的部份做初始化,範圍包括 Model 的 Layers、Optimizer、Loss Function、以及一些特別的函數。這可以確保在這個 class 之中的初始化得以正確執行。
__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)
model = <model name>()
我們會在 Class 後面用這行程式碼,使前述設定的 Model 可以被呼叫。在這範例中便是:
model = CustomModel()
這步驟無論是 Sequential、Functional、或是 Subclassing Model 都會需要設定這些參數,讓模型可以知道運算要如何進行編譯,以供接下來的訓練。
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
optimizer = tf.keras.optimizers.Adam()
train_loss = tf.keras.metrics.Mean(name='train_loss')
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')
test_loss = tf.keras.metrics.Mean(name='test_loss')
test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')
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)
接下來便是要用測試資料集來檢驗我們設定的訓練模型。
我們先用 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}'
)
可以看到準確度來到 98%,表現相當不錯,比起上次做心血管疾病預測的實例還要高多了!到時候來做新的預測
我們在這邊簡單介紹了 Model Subclassing API 的使用範例,雖然在這範例中還不能明顯看出其優勢,但可以發現我們可以自己去定義 Model 的函術後呼叫,這在更複雜的分析中尤為方便。曾經看過很多強大的模型都是用 Subclassing Model 寫出來的。接下來會開始多執行些模型的預測
https://ravimashru.github.io/100-days-of-deep-learning/days/007.html
https://keras.io/guides/making_new_layers_and_models_via_subclassing/
https://www.tensorflow.org/tutorials/quickstart/advanced