我們已經學會建構深度學習模型了,接著就是要編譯模型,讓模型可以進行訓練啦!在編譯會使用 model.compile()
,訓練的部分會使用 model.fit()
或 model.fit_generator()
。
使用 model.compile()
編譯模型,其中會設定損失函數(Loss Function)、最佳化器(Optimizer)和評估指標(Metrics)。損失函數為用來計算預測值與真實值之間的差距,依據欲解決的問題種類去選擇適合的損失函數,如在迴歸問題會使用均方誤差(Mean-square Error),在分類問題使用類別交叉熵(Categorical Crossentropy)等。最佳化器(Optimizer)用來決定模型如何依據損失函數去更新權重,最佳化器選擇也很多種,如 SGD、RMSprop 或 Adam 等。評估指標(Metrics)為用來監控模型性能的數值,依據不同的問題種類指定,如分類問題可指定 Accuracy,迴歸問題可指定平均絕對誤差(Mean Absolute Error),也可以自己定義評估指標。
編譯完模型後,要開始訓練模型。在訓練模型步驟,有兩種方法可以使用:model.fit()
或 model.fit_generator()
。這兩種方法的差異,前者是將資料集完整輸入,後者是使用生成器批次將資料集輸入。如果資料集數據不會大到超過 RAM 容量,可以使用前者,資料集數量龐大,或是有使用到 ImageDataGenerator()
做資料增強或使用 flow_from_directory()
載入訓練資料,就可以使用。不過後來 model.fit()
也支援生成器作為輸入。
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import tensorflow as tf
from tensorflow.keras import layers
from tensorflow.keras.applications import VGG16
from tensorflow.keras.optimizers import Adam
# 載入資料集
trpath = "./Kaggle/data/"
trdata = ImageDataGenerator(validation_split=0.2)
traindata = trdata.flow_from_directory(directory=trpath,
target_size=(256,256),
shuffle=True,
subset='training')
valdata = trdata.flow_from_directory(directory=trpath,
target_size=(256,256),
shuffle=True,
subset='validation')
# 設定 steps_per_epoch 和 validation_steps
spe = traindata.samples // traindata.batch_size # steps_per_epoch
vs = valdata.samples // traindata.batch_size # validation_steps
# 定義資料增強層
data_augmentation = tf.keras.Sequential(
[
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.1),
layers.RandomZoom(0.2),
]
)
# 建立模型
inputs = tf.keras.Input(shape=(256, 256, 3))
base_model = data_augmentation(inputs)
base_model = VGG16(include_top=False, weights='imagenet', input_tensor=base_model)
x = base_model.output
x = layers.Flatten()(x)
x = layers.Dense(4096, activation="relu")(x)
x = layers.Dense(4096, activation="relu")(x)
outputs = layers.Dense(5, activation="softmax")(x)
model = tf.keras.Model(inputs=inputs, outputs=outputs)
# 凍結層
for layer in base_model.layers:
layer.trainable = False
# 編譯模型
opt = Adam(learning_rate=0.0001)
model.compile(optimizer=opt,
loss="categorical_crossentropy",
metrics=["accuracy"])
hist = model.fit_generator(steps_per_epoch=spe,
generator=traindata,
validation_data=valdata,
validation_steps=vs,
epochs=100)
執行結果:
Found 248 images belonging to 5 classes.
Found 61 images belonging to 5 classes.
Epoch 1/100
7/7 [==============================] - 89s 13s/step - loss: 41.8797 - accuracy: 0.4120 - val_loss: 23.1233 - val_accuracy: 0.7500
Epoch 2/100
2/7 [=======>......................] - ETA: 56s - loss: 25.9516 - accuracy: 0.7143
...(略)
上述程式碼使用 model.compile()
編譯模型,使用 Adam 作為最佳化器,學習率(Learning Rate)設置為 0.0001,學習率為控制模型更新權重的步伐大小,太大的學習率可能會讓模型無法收斂,略過損失最小值,太小的學習率可能會讓模型學習速度緩慢,誤以為模型訓練停滯不前。損失函數使用 Categorical Crossentropy,根據要解決的問題為影像分類而選擇。使用 model.fit_generator()
為訓練模型的方法,steps_per_epoch
為訓練資料集每個訓練週期(Epoch)模型會執行幾步批次(Batch)訓練,validation_steps
為驗證資料集每個訓練週期模型會執行幾步批次訓練。上述程式令變數 spe 表示 steps_per_epoch,traindata.samples
為訓練資料集數量,traindata.batch_size
為訓練資料集的批次大小,預設為 32(變數 vs 為驗證資料集版本,意思相同)。批次大小表示一次將多少影像進行正向傳播和反向傳播,需考量 RAM 容量來設定。批次大小設定為 1 為隨機梯度下降(Stochastic Gradient Descent,SGD),一次只處理一個樣本,雖然參數更新快,但收斂速度慢,若使用像是 32 的批次大小則為 Mini-Batch,通常是使用 8 的倍數。generator
設定使用生成器產生的訓練資料集,validation_data
則為驗證資料集,epochs
為訓練週期數。
之前有提到超參數有兩種,其中一種為演算法超參數,學習率和批次大小這些就是演算法超參數,影響模型訓練的速度與品質。
因為使用生成器的關係,可以看到「Found XXX images belonging to XXX classes.」,會將資料集路徑下的資料夾數量作為分類種類數量,資料夾名稱則為資料夾下檔案的對應分類類別。訓練過程中會看到訓練資料集和驗證資料集的準確度與損失值。
啟動訓練步驟了,是不是有感覺了呢?