iT邦幫忙

2023 iThome 鐵人賽

DAY 5
0
AI & Data

從Keras框架與數學概念了解機器學習系列 第 5

[從Keras框架與數學概念了解機器學習] - 5. Model Fit

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20230905/20144614GkR2GMWsWA.jpg

當模型有成功compiler後,就可以透過 keras.engine.training.Model 的 fit 執行訓練。

Keras 官網範例:

from tensorflow.keras.models import Sequential
from tensorflow.keras.datasets import mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype("float32") / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype("float32") / 255

from tensorflow.keras import layers 
from tensorflow.keras.models import Model

model = Sequential([
   layers.Dense(512, activation="relu"),
   layers.Dense(10, activation="softmax")
])

model.compile(optimizer="rmsprop",
      loss="sparse_categorical_crossentropy",
      metrics=["accuracy"])

model.fit(train_images, train_labels, epochs=5, batch_size=128)

fit函式大致上主要執行的事項:

(1)
透過 _assert_compile_was_called 函式,判斷 模型的 _is_compiled 變數是否有被設定為True,如果沒有代表模型未經過compiler,會發出錯誤例外。

(2) 
透過 keras.engine.data_adapter.get_data_handler 函式,將參數設定到keras.engine.data_adapter.DataHandler 類別屬性成員,其中要設定 _adapter 屬性,會由以下類別清單中找適當的類別:

keras.engine.data_adapter.ListsOfScalarsDataAdapter
keras.engine.data_adapter.TensorLikeDataAdapter
keras.engine.data_adapter.GenericArrayLikeDataAdapter
keras.engine.data_adapter.DatasetAdapter
keras.engine.data_adapter.GeneratorDataAdapter
keras.engine.data_adapter.KerasSequenceAdapter
keras.engine.data_adapter.CompositeTensorDataAdapter
keras.engine.data_adapter.DatasetCreatorAdapter

可以對應處理本範例的輸入訓練資料(train_images, train_labels) 之類別為
keras.engine.data_adapter.TensorLikeDataAdapter,產生此類別的實例後,也一併將參數設定到此類別屬性成員中。此Adapter在設定的過程中會做一些處理轉換,如下列:
(A) 把 訓練資料(train_images, train_labels)與sample_weights(如果有) 轉換成Tensor型態的張量變數。
(B) 如果沒有設定batch_size,則將輸入之shape[0]之值(表示總共有多少筆訓練資料) 除以 32(預設) 之 無條件進位之數設定之。
(C) 將張量利用batch_size進行切片,轉換為可被批次整除的形狀大小,並單獨處理最後一部分批次,達成更高的性能。

(3)
如果傳入的 callbacks 參數list不是繼承 keras.callbacks.CallbackList 類別的實體 list,則會進行將callbacks 參數融入keras.callbacks.CallbackList 類別的動作,此類別也是 callback的Container。融入的過程中,
此callbacks參數list可以包含的成員為 keras.callbacks.ProgbarLogger類別實體,或keras.callbacks.History類別實體,這些成員都繼承keras.callbacks.Callback類別。 如果沒有這些成員也沒關係,預設會產生這二個實體指派給 Container的_history與_progbar屬性,並也加入到Container的callbacks實體list中。也就是說,這兩種callback在訓練時都會使用到。

(4)
接著叫用keras.engine.training.Model.make_train_function函式,決定好每個training step所要使用的train_function。這邊利用類似closure的方式將函式預先保存至記憶體,在跑批次時將會使用到此塊記憶體。

(5) 
以本範例,5個epoch代表訓練5次,每次epoch共訓練60000 筆資料,這60000 筆資料以批次128筆執行訓練。
程式執行iteration的架構大致為:
https://ithelp.ithome.com.tw/upload/images/20230905/20144614hUYAG95B4y.png

以程式來描述大致上為:

for epoch, iterator in data_handler.enumerate_epochs():
    callbacks.on_epoch_begin(epoch)
    
    for step in data_handler.steps():
        callbacks.on_train_batch_begin(step)
        train_function(iterator)
        callbacks.on_train_batch_end(end_step, logs)
        if self.stop_training:
            break
    callbacks.on_epoch_end(epoch, epoch_logs)
    if self.stop_training:
     break

(6)
迴圈中的train_function:
參考(5)所簡示之圖,紅色就是主要訓練的主函式。
其中的train_function就是去執行step_function(model, iterator)函式,再去執行keras.engine.training.Model.train_step。
於keras.engine.training.Model.train_step中基本上執行順序就是 keras.engine.training.Model.call -> keras.engine.base_layer.call (每個layer執行call之前的檢查與輸入的相容性)。
如果訓練之前沒有執行過模型的build函式,這個時機會先對模型做build的動作,確保模型實際訓練前一定要被build起來。

運作如下圖:
https://ithelp.ithome.com.tw/upload/images/20230905/20144614yZQZxCIYtp.png

以本範例,從train_step中,會執行模型的sequential.call,開始會到keras.engine.sequential.build_graph_network_for_inferred_shape函式,一開始會先把input的張量維度設定好,這邊依範例維度即為shape=(None, 784)。 接下來會迭代所有加入的layer層,呼叫每個layer的__call_。以第一個迭代為例, call 函式 就是 每層的父類別 keras.engine.base_layer.call 的呼叫,執行取出設定做對應的處理後,真正再到 keras.layers.core.dense.call 函式中,也就是layer的實體所實作的call函式。每個layer call,都會檢查是否本身layer已經被build過,如果沒有就會進行build的動作。而以本範例layer本身keras.layers.core.dense是執行 keras.layers.core.dense.build ,build一定會傳入接收的input實體,要明白接收的張量維度為何。然後會利用 keras.engine.base_layer.add_weight 函式分別對 keras.layers.core.dense的 kernel(權重) 與 bias(容錯) 屬性設置對應當初宣告keras.layers.core.dense的參數內容進行初始化(詳看dense layer之文)。

如果確認好已經build過每個layer實體,則layer實體的call函式才會真正執行 input 張量 與 自身權重(kernel) 的內積,再加上bias張量變數,最後再經由所指定的 activation 函式轉換出 output 張量。output 張量再轉變為下一層的 input 張量,再次呼叫每個layer的__call__,傳入input張量,重複執行動作,直至最後一層的輸出產生為止。這邊就是在做層與層的連結,也確保層與層是有相關聯的。

之後會執行 keras.engine.functional._init_graph_network 函式,為model.Sequential 重新初始化 graph network(之後model.build會再提到一次詳述),之後會分別指定 keras.engine.sequential.Sequential.input 為 input layer 實體,與 keras.engine.sequential.Sequential.output 為 output layer 實體。到目前為模型的weights只是初始設定,還會設定 keras.engine.sequential.Sequential.built 與 _graph_initialized 屬性為 True表示模型已經build過了。

模型確保keras.engine.sequential._build_graph_network_for_inferred_shape函式 與 keras.engine.functional._init_graph_network 函式 執行過後, 接著就 可以執行 keras.engine.functional._run_internal_graph ,此為計算輸出張量。

當build好所有的layer後,透過模型去找模型中建構好的layer,依序去對應輸入的維度與每個layer設定的維度(檢查是否相容),沒問題再執行輸入張量與Layer的權重張量內積,加上bias,再利用tf.GradientTape()與model初始化的optimizer(本範例是keras.optimizers.rmsprop.RMSprop) ,計算loss value與update weight。與最後使用activation function 轉換輸出張量。

而計算loss value是會用到之前設定的keras.engine.compile_utils.LossesContainer物件。依本範例,會Container會使用所模型設定loss參數為'sparse_categorical_crossentropy'對應的keras.losses.sparse_categorical_crossentropy 函式來計算。

完成loss function的計算後,繼續會執行 keras.engine.training.compute_metrics 、keras.engine.compile_utils.update_state 來做backwards pass 和update。
訓練每個train_function結束單一次迴圈後,會利用keras.callbacks.ProgbarLogger 物件之callback函式來做print metrics to stdout,擷取畫面:
https://ithelp.ithome.com.tw/upload/images/20230905/20144614emY26sS3us.png

以上是大致觀察model.fit() 主要會執行的事項與步驟,紀錄於此。


上一篇
[從Keras框架與數學概念了解機器學習] - 4. Compiler
下一篇
[從Keras框架與數學概念了解機器學習] - 6. Model Build
系列文
從Keras框架與數學概念了解機器學習30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言