tensorflow創建自定義 Estimator


https://www.tensorflow.org/guide/custom_estimators?hl=zh-cn

創建自定義 Estimator

本文檔介紹了自定義 Estimator。具體而言,本文檔介紹了如何創建自定義 Estimator 來模擬預創建的 Estimator DNNClassifier 在解決鳶尾花問題時的行為。要詳細了解鳶尾花問題,請參閱預創建的 Estimator 這一章

要下載和訪問示例代碼,請執行以下兩個命令:

git clone https://github.com/tensorflow/models/
cd models/samples/core/get_started
 

在本文檔中,我們將介紹 custom_estimator.py。您可以使用以下命令運行它:

python custom_estimator.py
 

如果您時間並不充足,歡迎對比 custom_estimator.py 與 premade_estimator.py(位於同一個目錄中)。

預創建的 Estimator 與自定義 Estimator

如下圖所示,預創建的 Estimator 是 tf.estimator.Estimator 基類的子類,而自定義 Estimator 是 tf.estimator.Estimator 的實例:

預創建的 Estimator 是 `Estimator` 的子類。自定義 Estimator 通常是 `Estimator` 的(直接)實例
預創建的 Estimator 和自定義 Estimator 都是 Estimator。

預創建的 Estimator 已完全成形。不過有時,您需要更好地控制 Estimator 的行為。這時,自定義 Estimator 就派上用場了。您可以創建自定義 Estimator 來完成幾乎任何操作。如果您需要以某種不尋常的方式連接隱藏層,則可以編寫自定義 Estimator。如果您需要為模型計算獨特的指標,也可以編寫自定義 Estimator。基本而言,如果您需要一個針對具體問題進行了優化的 Estimator,就可以編寫自定義 Estimator。

模型函數(即 model_fn)會實現機器學習算法。采用預創建的 Estimator 和自定義 Estimator 的唯一區別是:

  • 如果采用預創建的 Estimator,則有人已為您編寫了模型函數。
  • 如果采用自定義 Estimator,則您必須自行編寫模型函數。

您的模型函數可以實現各種算法,定義各種各樣的隱藏層和指標。與輸入函數一樣,所有模型函數都必須接受一組標准輸入參數並返回一組標准輸出值。正如輸入函數可以利用 Dataset API 一樣,模型函數可以利用 Layers API 和 Metrics API。

我們來看看如何使用自定義 Estimator 解決鳶尾花問題。快速提醒:以下是我們嘗試模擬的鳶尾花模型的結構:

網絡架構圖:輸入、2 個隱藏層和輸出
我們的鳶尾花實現包含四個特征、兩個隱藏層和一個對數輸出層。

編寫輸入函數

我們的自定義 Estimator 實現與我們的預創建的 Estimator 實現使用的是同一輸入函數(來自 iris_data.py)。即:

def train_input_fn(features, labels, batch_size):
    """An input function for training"""
    # Convert the inputs to a Dataset.
    dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))

    # Shuffle, repeat, and batch the examples.
    dataset = dataset.shuffle(1000).repeat().batch(batch_size)

    # Return the read end of the pipeline.
    return dataset.make_one_shot_iterator().get_next()
 

此輸入函數會構建可以生成批次 (features, labels) 對的輸入管道,其中 features 是字典特征。

創建特征列

按照預創建的 Estimator 和特征列章節中詳細介紹的內容,您必須定義模型的特征列來指定模型應該如何使用每個特征。無論是使用預創建的 Estimator 還是自定義 Estimator,您都要使用相同的方式定義特征列。

以下代碼為每個輸入特征創建一個簡單的 numeric_column,表示應該將輸入特征的值直接用作模型的輸入:

# Feature columns describe how to use the input.
my_feature_columns = []
for key in train_x.keys():
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))
 

編寫模型函數

我們要使用的模型函數具有以下調用簽名:

def my_model_fn(
   features, # This is batch_features from input_fn
   labels,   # This is batch_labels from input_fn
   mode,     # An instance of tf.estimator.ModeKeys
   params):  # Additional configuration
 

前兩個參數是從輸入函數中返回的特征和標簽批次;也就是說,features 和 labels 是模型將使用的數據的句柄。mode 參數表示調用程序是請求訓練、預測還是評估。

調用程序可以將 params 傳遞給 Estimator 的構造函數。傳遞給構造函數的所有 params 轉而又傳遞給 model_fn。在 custom_estimator.py 中,以下行將創建 Estimator 並設置參數來配置模型。此配置步驟與我們配置 tf.estimator.DNNClassifier(在預創建的 Estimator 中)的方式相似。

classifier = tf.estimator.Estimator(
    model_fn=my_model,
    params={
        'feature_columns': my_feature_columns,
        # Two hidden layers of 10 nodes each.
        'hidden_units': [10, 10],
        # The model must choose between 3 classes.
        'n_classes': 3,
    })
 

要實現一般的模型函數,您必須執行下列操作:

定義模型

基本的深度神經網絡模型必須定義下列三個部分:

定義輸入層

在 model_fn 的第一行調用 tf.feature_column.input_layer,以將特征字典和 feature_columns 轉換為模型的輸入,如下所示:

    # Use `input_layer` to apply the feature columns.
    net = tf.feature_column.input_layer(features, params['feature_columns'])
 

上面的行會應用特征列定義的轉換,從而創建模型的輸入層。

輸入層圖,在這種情況下,原始輸入到特征的映射為 1:1。

隱藏層

如果您要創建深度神經網絡,則必須定義一個或多個隱藏層。Layers API 提供一組豐富的函數來定義所有類型的隱藏層,包括卷積層、池化層和丟棄層。對於鳶尾花,我們只需調用 tf.layers.dense 來創建隱藏層,並使用 params['hidden_layers'] 定義維度。在 dense 層中,每個節點都連接到前一層中的各個節點。下面是相關代碼:

    # Build the hidden layers, sized according to the 'hidden_units' param.
    for units in params['hidden_units']:
        net = tf.layers.dense(net, units=units, activation=tf.nn.relu)
 
  • units 參數會定義指定層中輸出神經元的數量。
  • activation 參數會定義激活函數 - 在這種情況下為 Relu

這里的變量 net 表示網絡的當前頂層。在第一次迭代中,net 表示輸入層。在每次循環迭代時,tf.layers.dense 都使用變量 net 創建一個新層,該層將前一層的輸出作為其輸入。

創建兩個隱藏層后,我們的網絡如下所示。為了簡單起見,下圖並未顯示各個層中的所有單元。

添加了兩個隱藏層的輸入層。

請注意,tf.layers.dense 提供很多其他功能,包括設置多種正則化參數的功能。不過,為了簡單起見,我們只接受其他參數的默認值。

輸出層

我們再次調用 tf.layers.dense 定義輸出層,這次不使用激活函數:

    # Compute logits (1 per class).
    logits = tf.layers.dense(net, params['n_classes'], activation=None)
 

在這里,net 表示最后的隱藏層。因此,所有的層如下所示連接在一起:

連接到頂級隱藏層的對數輸出層
最后的隱藏層饋送到輸出層。

定義輸出層時,units 參數會指定輸出的數量。因此,通過將 units 設置為 params['n_classes'],模型會為每個類別生成一個輸出值。輸出向量的每個元素都將包含針對相關鳶尾花類別(山鳶尾、變色鳶尾或維吉尼亞鳶尾)分別計算的分數或“對數”。

之后,tf.nn.softmax 函數會將這些對數轉換為概率。

實現訓練、評估和預測

創建模型函數的最后一步是編寫實現預測、評估和訓練的分支代碼。

每當有人調用 Estimator 的 trainevaluate 或 predict 方法時,就會調用模型函數。您應該記得,模型函數的簽名如下所示:

def my_model_fn(
   features, # This is batch_features from input_fn
   labels,   # This is batch_labels from input_fn
   mode,     # An instance of tf.estimator.ModeKeys, see below
   params):  # Additional configuration
 

重點關注第三個參數 mode。如下表所示,當有人調用 trainevaluate 或 predict 時,Estimator 框架會調用模型函數並將 mode 參數設置為如下所示的值:

例如,假設您實例化自定義 Estimator 來生成名為 classifier 的對象。然后,您做出以下調用:

classifier = tf.estimator.Estimator(...)
classifier.train(input_fn=lambda: my_input_fn(FILE_TRAIN, True, 500))
 

然后,Estimator 框架會調用模型函數並將 mode 設為 ModeKeys.TRAIN

模型函數必須提供代碼來處理全部三個 mode 值。對於每個 mode 值,您的代碼都必須返回 tf.estimator.EstimatorSpec 的一個實例,其中包含調用程序所需的信息。我們來詳細了解各個 mode。

預測

如果調用 Estimator 的 predict 方法,則 model_fn 會收到 mode = ModeKeys.PREDICT。在這種情況下,模型函數必須返回一個包含預測的 tf.estimator.EstimatorSpec

該模型必須經過訓練才能進行預測。經過訓練的模型存儲在磁盤上,位於您實例化 Estimator 時建立的 model_dir目錄中。

此模型用於生成預測的代碼如下所示:

# Compute predictions.
predicted_classes = tf.argmax(logits, 1)
if mode == tf.estimator.ModeKeys.PREDICT:
    predictions = {
        'class_ids': predicted_classes[:, tf.newaxis],
        'probabilities': tf.nn.softmax(logits),
        'logits': logits,
    }
    return tf.estimator.EstimatorSpec(mode, predictions=predictions)
 

預測字典中包含模型在預測模式下運行時返回的所有內容。

向輸出層添加了其他輸出。

predictions 存儲的是下列三個鍵值對:

  • class_ids 存儲的是類別 ID(0、1 或 2),表示模型對此樣本最有可能歸屬的品種做出的預測。
  • probabilities 存儲的是三個概率(在本例中,分別是 0.02、0.95 和 0.03)
  • logit 存儲的是原始對數值(在本例中,分別是 -1.3、2.6 和 -0.9)

我們通過 predictions 參數(屬於 tf.estimator.EstimatorSpec)將該字典返回到調用程序。Estimator 的 predict 方法會生成這些字典。

計算損失

對於訓練評估,我們都需要計算模型的損失。這是要進行優化的目標

我們可以通過調用 tf.losses.sparse_softmax_cross_entropy 計算損失。當正確類別的概率(索引為 label)接近 1.0 時,此函數返回的值將最低,接近 0。隨着正確類別的概率不斷降低,返回的損失值越來越大。

此函數會針對整個批次返回平均值。

# Compute loss.
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)
 

評估

如果調用 Estimator 的 evaluate 方法,則 model_fn 會收到 mode = ModeKeys.EVAL。在這種情況下,模型函數必須返回一個包含模型損失和一個或多個指標(可選)的 tf.estimator.EstimatorSpec

雖然返回指標是可選的,但大多數自定義 Estimator 至少會返回一個指標。TensorFlow 提供一個指標模塊 tf.metrics 來計算常用指標。為簡單起見,我們將只返回准確率。tf.metrics.accuracy 函數會將我們的預測值與真實值進行比較,即與輸入函數提供的標簽進行比較。tf.metrics.accuracy 函數要求標簽和預測具有相同的形狀。下面是對 tf.metrics.accuracy 的調用:

# Compute evaluation metrics.
accuracy = tf.metrics.accuracy(labels=labels,
                               predictions=predicted_classes,
                               name='acc_op')
 

針對評估返回的 EstimatorSpec 通常包含以下信息:

  • loss:這是模型的損失
  • eval_metric_ops:這是可選的指標字典。

我們將創建一個包含我們的唯一指標的字典。如果我們計算了其他指標,則將這些指標作為附加鍵值對添加到同一字典中。然后,我們將在 eval_metric_ops 參數(屬於 tf.estimator.EstimatorSpec)中傳遞該字典。具體代碼如下:

metrics = {'accuracy': accuracy}
tf.summary.scalar('accuracy', accuracy[1])

if mode == tf.estimator.ModeKeys.EVAL:
    return tf.estimator.EstimatorSpec(
        mode, loss=loss, eval_metric_ops=metrics)
 

tf.summary.scalar 會在 TRAIN 和 EVAL 模式下向 TensorBoard 提供准確率(后文將對此進行詳細的介紹)。

訓練

如果調用 Estimator 的 train 方法,則會調用 model_fn 並收到 mode = ModeKeys.TRAIN。在這種情況下,模型函數必須返回一個包含損失和訓練操作的 EstimatorSpec

構建訓練操作需要優化器。我們將使用 tf.train.AdagradOptimizer,因為我們模仿的是 DNNClassifier,它也默認使用 Adagradtf.train 文件包提供很多其他優化器,您可以隨意嘗試它們。

下面是構建優化器的代碼:

optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)
 

接下來,我們使用優化器的 minimize 方法根據我們之前計算的損失構建訓練操作。

minimize 方法還具有 global_step 參數。TensorFlow 使用此參數來計算已經處理過的訓練步數(以了解何時結束訓練)。此外,global_step 對於 TensorBoard 圖能否正常運行至關重要。只需調用 tf.train.get_global_step 並將結果傳遞給 minimize 的 global_step 參數即可。

下面是訓練模型的代碼:

train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())
 

針對訓練返回的 EstimatorSpec 必須設置了下列字段:

  • loss:包含損失函數的值。
  • train_op:執行訓練步。

下面是用於調用 EstimatorSpec 的代碼:

return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)
 

模型函數現已完成。

自定義 Estimator

通過 Estimator 基類實例化自定義 Estimator,如下所示:

    # Build 2 hidden layer DNN with 10, 10 units respectively.
    classifier = tf.estimator.Estimator(
        model_fn=my_model,
        params={
            'feature_columns': my_feature_columns,
            # Two hidden layers of 10 nodes each.
            'hidden_units': [10, 10],
            # The model must choose between 3 classes.
            'n_classes': 3,
        })
 

在這里,params 字典與 DNNClassifier 的關鍵字參數用途相同;即借助 params 字典,您無需修改 model_fn中的代碼即可配置 Estimator。

使用 Estimator 訓練、評估和生成預測要用的其余代碼與預創建的 Estimator 一章中的相同。例如,以下行將訓練模型:

# Train the Model.
classifier.train(
    input_fn=lambda:iris_data.train_input_fn(train_x, train_y, args.batch_size),
    steps=args.train_steps)
 

TensorBoard

您可以在 TensorBoard 中查看自定義 Estimator 的訓練結果。要查看相應報告,請從命令行啟動 TensorBoard,如下所示:

# Replace PATH with the actual path passed as model_dir
tensorboard --logdir=PATH
 

然后,通過以下網址打開 TensorBoard:http://localhost:6006

所有預創建的 Estimator 都會自動將大量信息記錄到 TensorBoard 上。不過,對於自定義 Estimator,TensorBoard 只提供一個默認日志(損失圖)以及您明確告知 TensorBoard 要記錄的信息。對於您剛剛創建的自定義 Estimator,TensorBoard 會生成以下內容:

來自 Tensorboard 的准確率 'scalar' 圖 來自 Tensorboard 的損失 'scalar' 圖 來自 Tensorboard 的步/秒 'scalar' 圖
TensorBoard 顯示了三張圖。

簡而言之,下面是三張圖顯示的內容:

  • global_step/sec:這是一個性能指標,顯示我們在進行模型訓練時每秒處理的批次數(梯度更新)。

  • loss:所報告的損失。

  • accuracy:准確率由下列兩行記錄:

    • eval_metric_ops={'my_accuracy': accuracy}(評估期間)。
    • tf.summary.scalar('accuracy', accuracy[1])(訓練期間)。

這些 Tensorboard 圖是務必要將 global_step 傳遞給優化器的 minimize 方法的主要原因之一。如果沒有它,模型就無法記錄這些圖的 x 坐標。

注意 my_accuracy 和 loss 圖中的以下內容:

  • 橙線表示訓練。
  • 藍點表示評估。

在訓練期間,系統會隨着批次的處理定期記錄摘要信息(橙線),因此它會變成一個跨越 x 軸范圍的圖形。

相比之下,評估在每次調用 evaluate 時僅在圖上生成一個點。此點包含整個評估調用的平均值。它在圖上沒有寬度,因為它完全根據特定訓練步(一個檢查點)的模型狀態進行評估。

如下圖所示,您可以使用左側的控件查看並選擇性地停用/啟用報告。

允許用戶選擇顯示哪些運行的復選框。
啟用或停用報告。

總結

雖然使用預創建的 Estimator 可以快速高效地創建新模型,但您通常需要使用自定義 Estimator 才能實現所需的靈活性。幸運的是,預創建的 Estimator 和自定義 Estimator 采用相同的編程模型。唯一的實際區別是您必須為自定義 Estimator 編寫模型函數;除此之外,其他都是相同的。

要了解詳情,請務必查看:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM