「 導語」模型的訓練與評估是整個機器學習任務流程的核心環節。只有掌握了正確的訓練與評估方法,並靈活使用,才能使咱們更加快速地進行實驗分析與驗證,從而對模型有更加深入的理解。
在上一篇 Keras
模型構建的文章中,咱們介紹了在 TensorFlow 2.x
版本中使用 Keras
構建模型的三種方法,那麼本篇將在上一篇的基礎上着重介紹使用 Keras
模型進行本地訓練、評估以及預測的流程和方法。 Keras
模型有兩種訓練評估的方式,一種方式是使用模型內置 API
,如 model.fit()
, model.evaluate()
和 model.predict()
等分別執行不一樣的操做;另外一種方式是利用即時執行策略 (eager execution
) 以及 GradientTape
對象自定義訓練和評估流程。對全部 Keras
模型來講這兩種方式都是按照相同的原理來工做的,沒有本質上的區別。在通常狀況下,咱們更願意使用第一種訓練評估方式,由於它更爲簡單,更易於使用,而在一些特殊的狀況下,咱們可能會考慮使用自定義的方式來完成訓練與評估。python
下面介紹使用模型內置 API
實現的一個端到端的訓練評估示例,能夠認爲要使用該模型去解決一個多分類問題。這裏使用了函數式 API
來構建 Keras
模型,固然也可使用 Sequential
方式以及子類化方式去定義模型。示例代碼以下所示:git
import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers import numpy as np # Train and Test data from numpy array. x_train, y_train = ( np.random.random((60000, 784)), np.random.randint(10, size=(60000, 1)), ) x_test, y_test = ( np.random.random((10000, 784)), np.random.randint(10, size=(10000, 1)), ) # Reserve 10,000 samples for validation. x_val = x_train[-10000:] y_val = y_train[-10000:] x_train = x_train[:-10000] y_train = y_train[:-10000] # Model Create inputs = keras.Input(shape=(784, ), name='digits') x = layers.Dense(64, activation='relu', name='dense_1')(inputs) x = layers.Dense(64, activation='relu', name='dense_2')(x) outputs = layers.Dense(10, name='predictions')(x) model = keras.Model(inputs=inputs, outputs=outputs) # Model Compile. model.compile( # Optimizer optimizer=keras.optimizers.RMSprop(), # Loss function to minimize loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), # List of metrics to monitor metrics=['sparse_categorical_accuracy'], ) # Model Training. print('# Fit model on training data') history = model.fit( x_train, y_train, batch_size=64, epochs=3, # We pass some validation for monitoring validation loss and metrics # at the end of each epoch validation_data=(x_val, y_val), ) print('\nhistory dict:', history.history) # Model Evaluate. print('\n# Evaluate on test data') results = model.evaluate(x_test, y_test, batch_size=128) print('test loss, test acc:', results) # Generate predictions (probabilities -- the output of the last layer) # Model Predict. print('\n# Generate predictions for 3 samples') predictions = model.predict(x_test[:3]) print('predictions shape:', predictions.shape)
從代碼中能夠看到,要完成模型的訓練與評估的總體流程,首先要構建好模型;而後要對模型進行編譯 (compile
),目的是指定模型訓練過程當中須要用到的優化器 (optimizer
),損失函數 (losses
) 以及評估指標 (metrics
) ;接着開始進行模型的訓練與交叉驗證 (fit
),此步驟須要提早指定好訓練數據和驗證數據,並設置好一些參數如 epochs
等才能繼續,交叉驗證操做會在每輪 (epoch
) 訓練結束後自動觸發;最後是模型評估 (evaluate
) 與預測 (predict
),咱們會根據評估與預測結果來判斷模型的好壞。這樣一個完整的模型訓練與評估流程就完成了,下面來對示例裏的一些實現細節進行展開講解。api
compile
方法包含三個主要參數,一個是待優化的損失 (loss
) ,它指明瞭要優化的目標,一個是優化器 (optimizer
),它指明瞭目標優化的方向,還有一個可選的指標項 (metrics
),它指明瞭訓練過程當中要關注的模型指標。 Keras API
中已經包含了許多內置的損失函數,優化器以及指標,能夠拿來即用,可以知足大多數的訓練須要。tf.keras.losses
模塊下,其中包含了多種預約義的損失,好比咱們經常使用的二分類損失 BinaryCrossentropy
,多分類損失 CategoricalCrossentropy
以及均方根損失 MeanSquaredError
等。傳遞給 compile
的參數既能夠是一個字符串如 binary_crossentropy
也能夠是對應的 losses
實例如 tf.keras.losses.BinaryCrossentropy()
,當咱們須要設置損失函數的一些參數時(好比上例中 from_logits=True
),則須要使用實例參數。tf.keras.optimizers
模塊下,一些經常使用的優化器如 SGD
, Adam
以及 RMSprop
等均包含在內。一樣它也能夠經過字符串或者實例的方式傳給 compile
方法,通常咱們須要設置的優化器參數主要爲學習率 (learning rate
) ,其餘的參數能夠參考每一個優化器的具體實現來動態設置,或者直接使用其默認值便可。tf.keras.metrics
模塊下,二分類裏經常使用的 AUC
指標以及 lookalike
裏經常使用的召回率 (Recall
) 指標等均有包含。同理,它也能夠以字符串或者實例的形式傳遞給 compile
方法,注意 compile
方法接收的是一個 metric
列表,因此能夠傳遞多個指標信息。固然若是 losses
模塊下的損失或 metrics
模塊下的指標不知足你的需求,也能夠自定義它們的實現。數組
對於自定義損失,有兩種方式,一種是定義一個損失函數,它接收兩個輸入參數 y_true
和 y_pred
,而後在函數內部計算損失並返回。代碼以下:緩存
def basic_loss_function(y_true, y_pred): return tf.math.reduce_mean(tf.abs(y_true - y_pred)) model.compile(optimizer=keras.optimizers.Adam(), loss=basic_loss_function)
若是你須要的損失函數不只僅包含上述兩個參數,則能夠採用另一種子類化的方式來實現。定義一個類繼承自 tf.keras.losses.Loss
類,並實現其 __init__(self)
和 call(self, y_true, y_pred)
方法,這種實現方式與子類化層和模型比較類似。好比要實現一個加權的二分類交叉熵損失,其代碼以下:網絡
class WeightedBinaryCrossEntropy(keras.losses.Loss): """ Args: pos_weight: Scalar to affect the positive labels of the loss function. weight: Scalar to affect the entirety of the loss function. from_logits: Whether to compute loss from logits or the probability. reduction: Type of tf.keras.losses.Reduction to apply to loss. name: Name of the loss function. """ def __init__(self, pos_weight, weight, from_logits=False, reduction=keras.losses.Reduction.AUTO, name='weighted_binary_crossentropy'): super().__init__(reduction=reduction, name=name) self.pos_weight = pos_weight self.weight = weight self.from_logits = from_logits def call(self, y_true, y_pred): ce = tf.losses.binary_crossentropy( y_true, y_pred, from_logits=self.from_logits, )[:, None] ce = self.weight * (ce * (1 - y_true) + self.pos_weight * ce * y_true) return ce model.compile( optimizer=keras.optimizers.Adam(), loss=WeightedBinaryCrossEntropy( pos_weight=0.5, weight=2, from_logits=True, ), )
對於自定義指標,也能夠經過子類化的方式來實現,首先定義一個指標類繼承自 tf.keras.metrics.Metric
類並實現其四個方法,分別是 __init__(self)
方法,用來建立狀態變量, update_state(self, y_true, y_pred, sample_weight=None)
方法,用來更新狀態變量, result(self)
方法,用來返回狀態變量的最終結果, 以及 reset_states(self)
方法,用來從新初始化狀態變量。好比要實現一個多分類中真正例 (True Positives) 數量
的統計指標,其代碼以下:app
class CategoricalTruePositives(keras.metrics.Metric): def __init__(self, name='categorical_true_positives', **kwargs): super().__init__(name=name, **kwargs) self.true_positives = self.add_weight(name='tp', initializer='zeros') def update_state(self, y_true, y_pred, sample_weight=None): y_pred = tf.reshape(tf.argmax(y_pred, axis=1), shape=(-1, 1)) values = tf.cast(y_true, 'int32') == tf.cast(y_pred, 'int32') values = tf.cast(values, 'float32') if sample_weight is not None: sample_weight = tf.cast(sample_weight, 'float32') values = tf.multiply(values, sample_weight) self.true_positives.assign_add(tf.reduce_sum(values)) def result(self): return self.true_positives def reset_states(self): # The state of the metric will be reset at the start of each epoch. self.true_positives.assign(0.) model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=[CategoricalTruePositives()], )
layers
) 內部定義的損失,能夠經過在自定義層的 call
方法裏調用 self.add_loss()
來實現,並且在模型訓練時,它會自動添加到總體的損失中,不用人爲干預。經過對比加入自定義損失先後模型訓練輸出的 loss
值的變化來確認這部分損失是否被加入到了總體的損失中。還能夠在 build
模型後,打印 model.losses
來查看該模型的全部損失。注意正則化損失是內置在 Keras
的全部層中的,只須要在調用層時加入相應正則化參數便可,無需在 call
方法中 add_loss()
。call
方法裏調用 self.add_metric()
來新增指標,一樣的,它也會自動出如今總體的指標中,無需人爲干預。函數式 API
實現的模型,能夠經過調用 model.add_loss()
和 model.add_metric()
來實現與自定義模型一樣的效果。示例代碼以下:less
import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers inputs = keras.Input(shape=(784, ), name='digits') x1 = layers.Dense(64, activation='relu', name='dense_1')(inputs) x2 = layers.Dense(64, activation='relu', name='dense_2')(x1) outputs = layers.Dense(10, name='predictions')(x2) model = keras.Model(inputs=inputs, outputs=outputs) model.add_loss(tf.reduce_sum(x1) * 0.1) model.add_metric( keras.backend.std(x1), name='std_of_activation', aggregation='mean', ) model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), ) model.fit(x_train, y_train, batch_size=64, epochs=1)
model.fit()
方法來實現, fit
方法包括訓練數據與驗證數據參數,它們能夠是 numpy
類型數據,也能夠是 tf.data
模塊下 dataset
類型的數據。另外 fit
方法還包括 epochs
, batch_size
以及 steps_per_epoch
等控制訓練流程的參數,而且還能夠經過 callbacks
參數控制模型在訓練過程當中執行一些其它的操做,如 Tensorboard
日誌記錄等。模型的訓練和驗證數據能夠是 numpy
類型數據,最開始的端到端示例便是採用 numpy
數組做爲輸入。通常在數據量較小且內存能容下的狀況下采用 numpy
數據做爲訓練和評估的數據輸入。dom
numpy
類型數據來講,若是指定了 epochs
參數,則訓練數據的總量爲原始樣本數量 * epochs
。epoch
) 全部的原始樣本都會被訓練一遍,下一輪訓練還會使用這些樣本數據進行訓練,每一輪執行的步數 (steps
) 爲原始樣本數量/batch_size
,若是 batch_size
不指定,默認爲 32
。交叉驗證在每一輪訓練結束後
觸發,而且也會在全部驗證樣本上執行一遍,能夠指定 validation_batch_size
來控制驗證數據的 batch
大小,若是不指定默認同 batch_size
。numpy
類型數據來講,若是設置了 steps_per_epoch
參數,表示一輪要訓練指定的步數,下一輪會在上輪基礎上使用下一個 batch
的數據繼續進行訓練,直到全部的 epochs
結束或者訓練數據的總量
被耗盡。要想訓練流程不因數據耗盡而結束,則須要保證數據的總量
要大於 steps_per_epoch * epochs * batch_size
。同理也能夠設置 validation_steps
,表示交叉驗證所需步數,此時要注意驗證集的數據總量要大於 validation_steps * validation_batch_size
。fit
方法還提供了另一個參數 validation_split
來自動從訓練數據集中保留必定比例的數據做爲驗證,該參數取值爲 0-1
之間,好比 0.2
表明 20%
的訓練集用來作驗證, fit
方法會默認保留 numpy
數組最後面 20%
的樣本做爲驗證集。TensorFlow 2.0
以後,更爲推薦的是使用 tf.data
模塊下 dataset
類型的數據做爲訓練和驗證的數據輸入,它能以更加快速以及可擴展的方式加載和預處理數據。機器學習
使用 dataset
進行訓練的代碼以下:
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) # Shuffle and slice the dataset. train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) # Prepare the validation dataset val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val)) val_dataset = val_dataset.batch(64) # Now we get a test dataset. test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test)) test_dataset = test_dataset.batch(64) # Since the dataset already takes care of batching, # we don't pass a `batch_size` argument. model.fit(train_dataset, epochs=3, validation_data=val_dataset) result = model.evaluate(test_dataset)
dataset
通常是一個二元組,第一個元素爲模型的輸入特徵,若是爲多輸入就是多個特徵的字典 (dict
) 或元組 (tuple
),第二個元素是真實的數據標籤 (label
) ,即 ground truth
。from_tensor_slices
方法能夠從 nunpy
數組直接生成 dataset
類型數據,是一種比較方便快捷的生成方式,通常在測試時使用。其它較爲經常使用的生成方式,好比從 TFRecord
文件或文本文件 (TextLine
) 中生成 dataset
,能夠參考 tf.data
模塊下的相關類的具體實現。dataset
能夠調用內置方法提早對數據進行預處理,好比數據打亂 (shuffle
), batch
以及 repeat
等操做。shuffle
操做是爲了減少模型過擬合的概率,它僅爲小範圍打亂,須要藉助於一個緩存區,先將數據填滿,而後在每次訓練時從緩存區裏隨機抽取 batch_size
條數據,產生的空缺用後面的數據填充,從而實現了局部打亂的效果。batch
是對數據進行分批次,經常使用於控制和調節模型的訓練速度以及訓練效果,由於在 dataset
中已經 batch
過,因此 fit
方法中的 batch_size
就無需再提供了。repeat
用來對數據進行復制,以解決數據量不足的問題,若是指定了其參數 count
,則表示整個數據集要複製 count
次,不指定就會無限次複製
,此時必需要設置 steps_per_epoch
參數,否則訓練沒法終止。train dataset
的所有數據在每一輪都會被訓練到,由於一輪訓練結束後, dataset
會重置,而後被用來從新訓練。可是當指定了 steps_per_epoch
以後, dataset
在每輪訓練後不會被重置,一直到全部 epochs
結束或全部的訓練數據被消耗完以後終止,要想訓練正常結束,須保證提供的訓練數據總量要大於 steps_per_epoch * epochs * batch_size
。同理也能夠指定 validation_steps
,此時數據驗證會執行指定的步數,在下次驗證開始時, validation dataset
會被重置,以保證每次交叉驗證使用的都是相同的數據。validation_split
參數不適用於 dataset
類型數據,由於它須要知道每一個數據樣本的索引,這在 dataset API
下很難實現。steps_per_epoch
參數時, numpy
類型數據與 dataset
類型數據的處理流程徹底一致。但當指定以後,要注意它們之間在處理上的差別。對於 numpy
類型數據而言,在處理時,它會被轉爲 dataset
類型數據,只不過這個 dataset
被 repeat
了 epochs
次,並且每輪訓練結束後,這個 dataset
不會被重置,會在上次的 batch
以後繼續訓練。假設原始數據量爲 n
,指定 steps_per_epoch
參數以後,二者的差別主要體如今真實的訓練數據量上, numpy
爲 n * epochs
, dataset
爲 n
。具體細節能夠參考源碼實現。dataset
還有 map
與 prefetch
方法比較實用。 map
方法接收一個函數
做爲參數,用來對 dataset
中的每一條數據進行處理並返回一個新的 dataset
,好比咱們在使用 TextLineDataset
讀取文本文件後生成了一個 dataset
,而咱們要抽取輸入數據中的某些列做爲特徵 (features
),某些列做爲標籤 (labels
),此時就會用到 map
方法。prefetch
方法預先從 dataset
中準備好下次訓練所需的數據並放於內存中,這樣能夠減小每輪訓練之間的延遲等待時間。除了訓練數據和驗證數據外,還能夠向 fit
方法傳遞樣本權重 (sample_weight
) 以及類別權重 (class_weight
) 參數。這兩個參數一般被用於處理分類不平衡問題,經過給類別少的樣本賦予更高的權重,使得各個類別對總體損失的貢獻趨於一致。
對於 numpy
類型的輸入數據,可使用上述兩個參數,以上面的多分類問題爲例,若是要給分類 5
一個更高的權重,可使用以下代碼來實現:
import numpy as np # Here's the same example using `class_weight` class_weight = {0: 1., 1: 1., 2: 1., 3: 1., 4: 1., # Set weight "2" for class "5", # making this class 2x more important 5: 2., 6: 1., 7: 1., 8: 1., 9: 1.} print('Fit with class weight') model.fit(x_train, y_train, class_weight=class_weight, batch_size=64, epochs=4) # Here's the same example using `sample_weight` instead: sample_weight = np.ones(shape=(len(y_train), )) sample_weight[y_train == 5] = 2. print('\nFit with sample weight') model.fit( x_train, y_train, sample_weight=sample_weight, batch_size=64, epochs=4, )
而對於 dataset
類型的輸入數據來講,不能直接使用上述兩個參數,須要在構建 dataset
時將 sample_weight
加入其中,返回一個三元組的 dataset
,格式爲 (input_batch, target_batch, sample_weight_batch)
。示例代碼以下所示:
sample_weight = np.ones(shape=(len(y_train), )) sample_weight[y_train == 5] = 2. # Create a Dataset that includes sample weights # (3rd element in the return tuple). train_dataset = tf.data.Dataset.from_tensor_slices(( x_train, y_train, sample_weight, )) # Shuffle and slice the dataset. train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) model.fit(train_dataset, epochs=3)
在模型的訓練過程當中有一些特殊時間點,好比在一個 batch
結束或者一個 epoch
結束時,通常都會作一些額外的處理操做來輔助咱們進行訓練,上面介紹過的模型交叉驗證就是其中之一。還有一些其它的操做,好比當模型訓練停滯不前時 (loss
值在某一值附近不斷波動),自動減少其學習速率 (learning rate
) 以使損失繼續降低,從而獲得更好的收斂效果;在訓練過程當中保存模型的權重信息,以備重啓模型時能夠在已有權重的基礎上繼續訓練,從而減小訓練時間;還有在每輪的訓練結束後記錄模型的損失 (loss
) 和指標 (metrics
) 信息,以供 Tensorboard
分析使用等等,這些操做都是模型訓練過程當中不可或缺的部分。它們均可以經過回調函數 (callbacks
) 的方式來實現,這些回調函數都在 tf.keras.callbacks
模塊下,能夠將它們做爲列表參數傳遞給 fit
方法以達到不一樣的操做目的。
下面以 EarlyStopping
爲例說明 callbacks
的使用方式。本例中,當交叉驗證損失 val_loss
至少在 2
輪 (epochs
) 訓練中的減小值都低於 1e-2
時,咱們會提早中止訓練。其示例代碼以下所示:
callbacks = [ keras.callbacks.EarlyStopping( # Stop training when `val_loss` is no longer improving monitor='val_loss', # "no longer improving" being defined as "no better than 1e-2 less" min_delta=1e-2, # "no longer improving" being further defined as "for at least 2 epochs" patience=2, verbose=1, ) ] model.fit( x_train, y_train, epochs=20, batch_size=64, callbacks=callbacks, validation_split=0.2, )
callbacks
須要瞭解並掌握, 如 ModelCheckpoint
用來保存模型權重信息, TensorBoard
用來記錄一些指標信息, ReduceLROnPlateau
用來在模型停滯時減少學習率。更多的 callbacks
函數能夠參考 tf.keras.callbacks
模塊下的實現。固然也能夠自定義 callbacks
類,該子類須要繼承自 tf.keras.callbacks.Callback
類,並按需實現其內置的方法,好比若是須要在每一個 batch
訓練結束後記錄 loss
的值,則可使用以下代碼實現:
class LossHistory(keras.callbacks.Callback): def on_train_begin(self, logs): self.losses = [] def on_batch_end(self, batch, logs): self.losses.append(logs.get('loss'))
TensorFlow 2.0
以前, ModelCheckpoint
內容和 TensorBoard
內容是同時記錄的,保存在相同的文件夾下,而在 2.0
以後的 keras API
中它們能夠經過不一樣的回調函數分開指定。記錄的日誌文件中,含有 checkpoint
關鍵字的文件通常爲檢查點文件,含有 events.out.tfevents
關鍵字的文件通常爲 Tensorboard
相關文件。
考慮如圖所示的多輸入多輸出模型,該模型包括兩個輸入和兩個輸出, score_output
輸出表示分值, class_output
輸出表示分類,其示例代碼以下:
from tensorflow import keras from tensorflow.keras import layers image_input = keras.Input(shape=(32, 32, 3), name='img_input') timeseries_input = keras.Input(shape=(None, 10), name='ts_input') x1 = layers.Conv2D(3, 3)(image_input) x1 = layers.GlobalMaxPooling2D()(x1) x2 = layers.Conv1D(3, 3)(timeseries_input) x2 = layers.GlobalMaxPooling1D()(x2) x = layers.concatenate([x1, x2]) score_output = layers.Dense(1, name='score_output')(x) class_output = layers.Dense(5, name='class_output')(x) model = keras.Model( inputs=[image_input, timeseries_input], outputs=[score_output, class_output], )
在進行模型編譯時,若是隻指定一個 loss
明顯不能知足不一樣輸出的損失計算方式,因此此時能夠指定 loss
爲一個列表 (list
),其中每一個元素分別對應於不一樣的輸出。示例代碼以下:
model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[ keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy(from_logits=True) ], loss_weights=[1, 1], )
此時模型的優化目標爲全部單個損失值的總和,若是想要爲不一樣的損失指定不一樣的權重,能夠設置 loss_weights
參數,該參數接收一個標量係數列表 (list
),用以對模型不一樣輸出的損失值進行加權。若是僅爲模型指定一個 loss
,則該 loss
會應用到每個輸出,在模型的多個輸出損失計算方式相同時,能夠採用這種方式。
一樣的對於模型的指標 (metrics
),也能夠指定爲多個,注意由於 metrics
參數自己即爲一個列表,因此爲多個輸出指定 metrics
應該使用二維列表。示例代碼以下:
model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[ keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy(from_logits=True), ], metrics=[ [ keras.metrics.MeanAbsolutePercentageError(), keras.metrics.MeanAbsoluteError() ], [keras.metrics.CategoricalAccuracy()], ], )
對於有明確名稱的輸出,能夠經過字典的方式來設置其 loss
和 metrics
。示例代碼以下:
model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss={ 'score_output': keras.losses.MeanSquaredError(), 'class_output': keras.losses.CategoricalCrossentropy(from_logits=True), }, metrics={ 'score_output': [ keras.metrics.MeanAbsolutePercentageError(), keras.metrics.MeanAbsoluteError() ], 'class_output': [ keras.metrics.CategoricalAccuracy(), ] }, )
對於僅被用來預測的輸出,也能夠不指定其 loss
。示例代碼以下:
model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[ None, keras.losses.CategoricalCrossentropy(from_logits=True), ], ) # Or dict loss version model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss={ 'class_output': keras.losses.CategoricalCrossentropy(from_logits=True), }, )
對於多輸入輸出模型的訓練來講,也能夠採用和其 compile
方法相同的方式來提供數據輸入,也就是說既可使用列表的方式,也可使用字典的方式來指定多個輸入。
numpy
類型數據示例代碼以下:
# Generate dummy Numpy data img_data = np.random.random_sample(size=(100, 32, 32, 3)) ts_data = np.random.random_sample(size=(100, 20, 10)) score_targets = np.random.random_sample(size=(100, 1)) class_targets = np.random.random_sample(size=(100, 5)) # Fit on lists model.fit( x=[img_data, ts_data], y=[score_targets, class_targets], batch_size=32, epochs=3, ) # Alternatively, fit on dicts model.fit( x={ 'img_input': img_data, 'ts_input': ts_data, }, y={ 'score_output': score_targets, 'class_output': class_targets, }, batch_size=32, epochs=3, )
dataset
類型數據示例代碼以下:
# Generate dummy dataset data from numpy train_dataset = tf.data.Dataset.from_tensor_slices(( (img_data, ts_data), (score_targets, class_targets), )) # Alternatively generate with dict train_dataset = tf.data.Dataset.from_tensor_slices(( { 'img_input': img_data, 'ts_input': ts_data, }, { 'score_output': score_targets, 'class_output': class_targets, }, )) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) model.fit(train_dataset, epochs=3)
model
內置提供的 fit
和 evaluate
方法,而想使用低階 API
自定義模型的訓練和評估的流程,則能夠藉助於 GradientTape
來實現。深度神經網絡在後向傳播過程當中須要計算損失 (loss
) 關於權重矩陣的導數(也稱爲梯度),以更新權重矩陣並得到最優解,而 GradientTape
能自動提供求導幫助,無需咱們手動求導,它本質上是一個求導記錄器
,可以記錄前項傳播的過程,並據此計算導數。模型的構建過程與以前相比沒有什麼不一樣,主要體如今訓練的部分,示例代碼以下:
import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers import numpy as np # Get the model. inputs = keras.Input(shape=(784, ), name='digits') x = layers.Dense(64, activation='relu', name='dense_1')(inputs) x = layers.Dense(64, activation='relu', name='dense_2')(x) outputs = layers.Dense(10, name='predictions')(x) model = keras.Model(inputs=inputs, outputs=outputs) # Instantiate an optimizer. optimizer = keras.optimizers.SGD(learning_rate=1e-3) # Instantiate a loss function. loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True) # Prepare the metrics. train_acc_metric = keras.metrics.SparseCategoricalAccuracy() val_acc_metric = keras.metrics.SparseCategoricalAccuracy() # Prepare the training dataset. batch_size = 64 train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size) # Prepare the validation dataset. val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val)) val_dataset = val_dataset.batch(64) epochs = 3 for epoch in range(epochs): print('Start of epoch %d' % (epoch, )) # Iterate over the batches of the dataset. for step, (x_batch_train, y_batch_train) in enumerate(train_dataset): # Open a GradientTape to record the operations run # during the forward pass, which enables autodifferentiation. with tf.GradientTape() as tape: # Run the forward pass of the layer. # The operations that the layer applies # to its inputs are going to be recorded # on the GradientTape. logits = model(x_batch_train, training=True) # Logits for this minibatch # Compute the loss value for this minibatch. loss_value = loss_fn(y_batch_train, logits) # Use the gradient tape to automatically retrieve # the gradients of the trainable variables with respect to the loss. grads = tape.gradient(loss_value, model.trainable_weights) # Run one step of gradient descent by updating # the value of the variables to minimize the loss. optimizer.apply_gradients(zip(grads, model.trainable_weights)) # Update training metric. train_acc_metric(y_batch_train, logits) # Log every 200 batches. if step % 200 == 0: print('Training loss (for one batch) at step %s: %s' % (step, float(loss_value))) print('Seen so far: %s samples' % ((step + 1) * 64)) # Display metrics at the end of each epoch. train_acc = train_acc_metric.result() print('Training acc over epoch: %s' % (float(train_acc), )) # Reset training metrics at the end of each epoch train_acc_metric.reset_states() # Run a validation loop at the end of each epoch. for x_batch_val, y_batch_val in val_dataset: val_logits = model(x_batch_val) # Update val metrics val_acc_metric(y_batch_val, val_logits) val_acc = val_acc_metric.result() val_acc_metric.reset_states() print('Validation acc: %s' % (float(val_acc), ))
with tf.GradientTape() as tape
部分的實現,它記錄了前向傳播的過程,而後使用 tape.gradient
方法計算出 loss
關於模型全部權重矩陣 (model.trainable_weights
) 的導數(也稱做梯度),接着利用優化器 (optimizer
) 去更新全部的權重矩陣。batch
的訓練中進行更新操做 (update_state()
) ,在一個 epoch
訓練結束後打印指標的結果 (result()
) ,而後重置該指標 (reset_states()
) 並進行下一輪的指標記錄,交叉驗證的指標也是一樣的操做。注意與使用模型內置 API
進行訓練不一樣,在自定義訓練中,模型中定義的損失,好比正則化損失以及經過 add_loss
添加的損失,是不會自動累加在 loss_fn
以內的。若是要包含這部分損失,則須要修改自定義訓練的流程,經過調用 model.losses
來將模型的所有損失加入到要優化的損失中去。示例代碼以下所示:
with tf.GradientTape() as tape: logits = model(x_batch_train) loss_value = loss_fn(y_batch_train, logits) # Add extra losses created during this forward pass: loss_value += sum(model.losses) grads = tape.gradient(loss_value, model.trainable_weights) optimizer.apply_gradients(zip(grads, model.trainable_weights))