首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Creating Estimators in tf.estimator(在tf.estimato创建自定义估算器)

tf.estimator框架可以通过其高级别的Estimator API轻松构建和操练机器学习模型。Estimator提供您可以实例化的类以快速配置常用模型类型,例如回归器和分类器:

但是,如果没有tf.estimator预定义的模型类型满足您的需求呢?也许您需要对模型配置进行更精细的控制,例如定制用于优化的损失函数的能力,或为每个神经网络层指定不同的激活函数。或者,也许你正在实施排名或推荐系统,分类器和回归器都不适用于产生预测。

本教程将介绍如何Estimator使用所提供的构建模块创建自己的模块tf.estimator,以根据物理测量结果预测鲍鱼的年龄。您将学习如何执行以下操作:

  • 实例化一个 Estimator
  • 构建一个自定义模型函数
  • 使用tf.feature_column和配置神经网络tf.layers
  • 从中选择适当的损失函数 tf.losses
  • 为您的模型定义一个操练操作
  • 生成并返回预测

先决条件

This tutorial assumes you already know tf.estimator API basics, such as feature columns, input functions, and train()/evaluate()/predict() operations. If you've never used tf.estimator before, or need a refresher, you should first review the following tutorials:

  • tf.estimator快速入门:快速介绍如何使用tf.estimator来训练神经网络。
  • TensorFlow线性模型教程:特征列简介以及在tf.estimator中构建线性分类器的概述。
  • 使用tf.estimator构建输入函数:概述如何构建input_fn以预处理数据并将数据输入到模型中。

鲍鱼年龄预测

可以通过贝壳上的环数来估计鲍鱼(海螺)的年龄。但是,由于这项任务需要在显微镜下切割,染色和观察壳,因此需要找到可以预测年龄的其他测量值。

鲍鱼数据集包含以下鲍鱼特征数据

特征

描述

Length

鲍长(最长方向,毫米)

Diamete

鲍鱼直径(垂直于长度的测量;以毫米为单位)

Height

鲍鱼的高度(其肉内壳;毫米)

Whole Weight

整个鲍鱼的重量(克)

Shucked Weight

鲍鱼肉的重量(克)

Viscera Weight

出血后,鲍鱼的肠道重量(克)

Shell Weight

干鲍鱼壳重量(克)

要预测的标签是圆环的数量,作为鲍鱼年龄的代表。

“鲍鱼壳” Nicki Dugan Pogue,CC BY-SA 2.0)

设置

本教程使用三个数据集。abalone_train.csv包含标记的操练数据,包含3,320个示例。abalone_test.csv包含850个示例的标记测试数据。abalone_predict包含7个预测的例子。

以下部分Estimator逐步介绍了如何编写代码;在完整,最后的代码可以点击这里

将Abalone CSV数据加载到TensorFlow数据集中

要将鲍鱼数据集提供给模型,您需要下载CSV并将其加载到TensorFlow中Dataset。首先,添加一些标准的Python和TensorFlow导入,并设置FLAGS:

代码语言:javascript
复制
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import sys
import tempfile

# Import urllib
from six.moves import urllib

import numpy as np
import tensorflow as tf

FLAGS = None

启用记录:

代码语言:javascript
复制
tf.logging.set_verbosity(tf.logging.INFO)

然后定义一个函数来加载CSV(可以从命令行选项中指定的文件或从tensorflow.org下载):

代码语言:javascript
复制
def maybe_download(train_data, test_data, predict_data):
  """Maybe downloads training data and returns train and test file names."""
  if train_data:
    train_file_name = train_data
  else:
    train_file = tempfile.NamedTemporaryFile(delete=False)
    urllib.request.urlretrieve(
        "http://download.tensorflow.org/data/abalone_train.csv",
        train_file.name)
    train_file_name = train_file.name
    train_file.close()
    print("Training data is downloaded to %s" % train_file_name)

  if test_data:
    test_file_name = test_data
  else:
    test_file = tempfile.NamedTemporaryFile(delete=False)
    urllib.request.urlretrieve(
        "http://download.tensorflow.org/data/abalone_test.csv", test_file.name)
    test_file_name = test_file.name
    test_file.close()
    print("Test data is downloaded to %s" % test_file_name)

  if predict_data:
    predict_file_name = predict_data
  else:
    predict_file = tempfile.NamedTemporaryFile(delete=False)
    urllib.request.urlretrieve(
        "http://download.tensorflow.org/data/abalone_predict.csv",
        predict_file.name)
    predict_file_name = predict_file.name
    predict_file.close()
    print("Prediction data is downloaded to %s" % predict_file_name)

  return train_file_name, test_file_name, predict_file_name

最后,创建main()并加载鲍鱼CSV Datasets,定义标志以允许用户通过命令行(默认情况下,文件将从tensorflow.org下载)选择性地指定CSV文件以用于培训,测试和预测数据集:

代码语言:javascript
复制
def main(unused_argv):
  # Load datasets
  abalone_train, abalone_test, abalone_predict = maybe_download(
    FLAGS.train_data, FLAGS.test_data, FLAGS.predict_data)

  # Training examples
  training_set = tf.contrib.learn.datasets.base.load_csv_without_header(
      filename=abalone_train, target_dtype=np.int, features_dtype=np.float64)

  # Test examples
  test_set = tf.contrib.learn.datasets.base.load_csv_without_header(
      filename=abalone_test, target_dtype=np.int, features_dtype=np.float64)

  # Set of 7 examples for which to predict abalone ages
  prediction_set = tf.contrib.learn.datasets.base.load_csv_without_header(
      filename=abalone_predict, target_dtype=np.int, features_dtype=np.float64)

if __name__ == "__main__":
  parser = argparse.ArgumentParser()
  parser.register("type", "bool", lambda v: v.lower() == "true")
  parser.add_argument(
      "--train_data", type=str, default="", help="Path to the training data.")
  parser.add_argument(
      "--test_data", type=str, default="", help="Path to the test data.")
  parser.add_argument(
      "--predict_data",
      type=str,
      default="",
      help="Path to the prediction data.")
  FLAGS, unparsed = parser.parse_known_args()
  tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)

实例化一个估计器

当使用tf.estimator提供的类之一定义模型时,例如DNNClassifier,您可以在构造函数中提供所有配置参数,例如:

代码语言:javascript
复制
my_nn = tf.estimator.DNNClassifier(feature_columns=[age, height, weight],
                                   hidden_units=[10, 10, 10],
                                   activation_fn=tf.nn.relu,
                                   dropout=0.2,
                                   n_classes=3,
                                   optimizer="Adam")

您无需编写任何进一步的代码来指示TensorFlow如何操练模型,计算损失或返回预测; 那逻辑已经被烘焙到了DNNClassifier

相比之下,当您从头创建自己的估算器时,构造器只接受两个用于模型配置的高级参数,model_fn并且params

代码语言:javascript
复制
nn = tf.estimator.Estimator(model_fn=model_fn, params=model_params)
  • model_fn:一个包含所有上述逻辑的函数对象,以支持操练,评估和预测。您有责任实施该功能。下一节,model_fn详细介绍构建封面创建模型函数。
  • params:超参数的一个可选字典(例如,学习速率,退出)将被传入model_fn

注意:就像tf.estimator预定义的回归器和分类器一样,Estimator初始化器也接受一般的配置参数model_dirconfig

对于鲍鱼年龄预测,该模型将接受一个超参数:学习率。LEARNING_RATE在代码的开始处定义为常量(以下以粗体突出显示),正好在日志配置之后:

代码语言:javascript
复制
tf.logging.set_verbosity(tf.logging.INFO)

# Learning rate for the model
LEARNING_RATE = 0.001

注意:这里LEARNING_RATE设置为0.001,但您可以根据需要调整此值,以便在模型训练过程中获得最佳结果。

然后,添加以下代码main(),该代码创建model_params包含学习率的dict 并实例化Estimator

代码语言:javascript
复制
# Set model params
model_params = {"learning_rate": LEARNING_RATE}

# Instantiate Estimator
nn = tf.estimator.Estimator(model_fn=model_fn, params=model_params)

构建 model_fn

EstimatorAPI模型函数的基本框架如下所示:

代码语言:javascript
复制
def model_fn(features, labels, mode, params):
   # Logic to do the following:
   # 1. Configure the model via TensorFlow operations
   # 2. Define the loss function for training/evaluation
   # 3. Define the training operation/optimizer
   # 4. Generate predictions
   # 5. Return predictions/loss/train_op/eval_metric_ops in EstimatorSpec object
   return EstimatorSpec(mode, predictions, loss, train_op, eval_metric_ops)

model_fn必须接受三个参数:

  • features:包含通过模型传递给模型的字典input_fn
  • labels:A Tensor包含通过传递给模型的标签input_fn。对于predict()调用将是空的,因为这些是模型将推断的值。
  • mode:以下tf.estimator.ModeKeys字符串值之一,指示调用model_fn的上下文:
    • tf.estimator.ModeKeys.TRAINmodel_fn是在训练模式下调用的,即通过train()调用。
    • tf.estimator.ModeKeys.EVAL。这model_fn是在评估模式下调用的,即通过evaluate()调用。
    • tf.estimator.ModeKeys.PREDICT。这model_fn是在预测模式下调用的,即通过predict()调用。

model_fn也可以接受params包含用于训练的超参数字典的论据(如上面的框架所示)。

该函数的主体执行以下任务(在下面的章节中详细介绍):

  • 配置模型 - 在这里,对于鲍鱼预测工具,这将是一个神经网络。
  • 定义损失函数用于计算模型的预测与目标值的匹配程度。
  • 定义指定optimizer算法的训练操作,以最小化由损失函数计算的损失值。

model_fn必须返回一个tf.estimator.EstimatorSpec对象,其中包含以下值:

  • mode(需要)。模型运行的模式。通常情况下,您将返回此处的mode参数model_fn
  • predictions(在PREDICT模式中需要)。您选择的关键名称映射到一个字典Tensor包含从模型预测S,如: python predictions = {"results": tensor_of_predictions}PREDICT模式下,你返回EstimatorSpec的词典,然后将被退回predict(),这样你就可以建造它的格式,以便使用它。
  • loss(需要EVALTRAIN模式)。A Tensor包含标量损失值:在所有输入示例中计算的模型损失函数的输出(稍后在定义模型损失时进行更??深入的讨论)。TRAIN用于错误处理和日志记录的模式,并且在EVAL模式中自动包含为度量标准。
  • train_op(只在TRAIN模式下使用)。运行一步训练的操作。
  • eval_metric_ops(可选的)。一个名称/值对的字典,用于指定模型在EVAL模式下运行时将要计算的指标。该名称是您为度量标准选择的标签,该值是度量标准计算的结果。该tf.metrics模块为各种常用指标提供预定义的功能。以下eval_metric_ops内容包含"accuracy"使用以下计算得出的指标tf.metrics.accuracypython eval_metric_ops = { "accuracy": tf.metrics.accuracy(labels, predictions) } 如果您未指定eval_metric_opsloss则只会在评估期间计算。

使用tf.feature_column和配置神经网络tf.layers

构建神经网络需要创建并连接输入图层,隐藏图层和输出图层。

输入层是一系列节点(一个用于在模型中的每个特征),将接受被传递到特征数据model_fn中的features参数。如果features包含Tensor所有特征数据的n维,则它可以用作输入图层。如果features包含通过输入函数传递给模型的特征列的字典,则可以Tensor使用该tf.feature_column.input_layer函数将其转换为输入图层。

代码语言:javascript
复制
input_layer = tf.feature_column.input_layer(
    features=features, feature_columns=[age, height, weight])

如上所示,input_layer()需要两个必要的参数:

  • features。从字符串键到Tensors包含相应特征数据的映射。这正是model_fnfeatures论点中传递给我们的。
  • feature_columns。所有名单FeatureColumns在模型- ageheight以及weight在上面的例子。

然后,神经网络的输入层必须通过激活函数连接到一个或多个隐藏层,激活函数对前一层的数据执行非线性变换。然后将最后一个隐藏层连接到输出层,即模型中的最后一层。tf.layers提供了tf.layers.dense构建完全连接层的功能。激活由activation参数控制。传递给activation参数的一些选项是:

  • tf.nn.relu。以下代码使用ReLU激活函数(https://en.wikipedia.org/wiki/Rectifier_(neural_networks%29)())创建一个units完全连接到上一图层的节点层: input_layertf.nn.relupython hidden_layer = tf.layers.dense( inputs=input_layer, units=10, activation=tf.nn.relu)
  • tf.nn.relu6。以下代码使用ReLU 6激活函数()创建一个units完全连接到上一图层的节点层: hidden_layertf.nn.relu6python second_hidden_layer = tf.layers.dense( inputs=hidden_layer, units=20, activation=tf.nn.relu)
  • None。下面的代码创建了一个units完全连接到前一层second_hidden_layer没有激活函数的节点层,只是一个线性变换: python output_layer = tf.layers.dense( inputs=second_hidden_layer, units=3, activation=None)

其他激活功能是可能的,例如:

代码语言:javascript
复制
output_layer = tf.layers.dense(inputs=second_hidden_layer,
                               units=10,
                               activation_fn=tf.sigmoid)

上面的代码创建了神经网络层output_layersecond_hidden_layer与S形激活函数(tf.sigmoid)完全连接。有关TensorFlow中可用的预定义激活函数的列表,请参阅API文档

综合起来,下面的代码为鲍鱼预测器构建了一个完整的神经网络,并捕获了它的预测:

代码语言:javascript
复制
def model_fn(features, labels, mode, params):
  """Model function for Estimator."""

  # Connect the first hidden layer to input layer
  # (features["x"]) with relu activation
  first_hidden_layer = tf.layers.dense(features["x"], 10, activation=tf.nn.relu)

  # Connect the second hidden layer to first hidden layer with relu
  second_hidden_layer = tf.layers.dense(
      first_hidden_layer, 10, activation=tf.nn.relu)

  # Connect the output layer to second hidden layer (no activation fn)
  output_layer = tf.layers.dense(second_hidden_layer, 1)

  # Reshape output layer to 1-dim Tensor to return predictions
  predictions = tf.reshape(output_layer, [-1])
  predictions_dict = {"ages": predictions}
  ...

在这里,因为你可以通过鲍鱼Datasets使用numpy_input_fn,如下图所示,features是一个字典{"x": data_tensor},所以features["x"]是输入层。该网络包含两个隐藏层,每层都有10个节点和一个ReLU激活功能。输出层不包含激活函数,并且是tf.reshape一维张量以捕获存储在其中的模型预测predictions_dict

为模型定义损失

EstimatorSpec通过返回的model_fn必须包含loss:一个Tensor代表损耗值,其量化模型的预测如何很好地体现在训练和评估运行标签值。该tf.losses模块提供了使用各种指标计算损失的便利功能,其中包括:

  • absolute_difference(labels, predictions)。使用绝对差分公式计算损失(https://en.wikipedia.org/wiki/Deviation_(statistics%29#Unsigned_or_absolute_deviation)(也称为L1损失)。
  • log_loss(labels, predictions)。使用Logistic损失论坛计算损失(通常用于逻辑回归)。
  • mean_squared_error(labels, predictions)。使用均方误差(MSE;也称为L2损失)计算损失。

以下示例使用(粗体)添加loss到鲍鱼的定义:model_fnmean_squared_error()

代码语言:javascript
复制
def model_fn(features, labels, mode, params):
  """Model function for Estimator."""

  # Connect the first hidden layer to input layer
  # (features["x"]) with relu activation
  first_hidden_layer = tf.layers.dense(features["x"], 10, activation=tf.nn.relu)

  # Connect the second hidden layer to first hidden layer with relu
  second_hidden_layer = tf.layers.dense(
      first_hidden_layer, 10, activation=tf.nn.relu)

  # Connect the output layer to second hidden layer (no activation fn)
  output_layer = tf.layers.dense(second_hidden_layer, 1)

  # Reshape output layer to 1-dim Tensor to return predictions
  predictions = tf.reshape(output_layer, [-1])
  predictions_dict = {"ages": predictions}

  # Calculate loss using mean squared error
  loss = tf.losses.mean_squared_error(labels, predictions)
  ...

有关损失函数的完整列表以及有关支持的参数和用法的更多详细信息,请参阅API指南

评估补充指标可以添加到eval_metric_ops字典。以下代码定义了一个rmse度量标准,用于计算模型预测的均方根误差。请注意,labels张量被转换为一种float64类型以匹配predictions张量的数据类型,该数据类型将包含实际值:

代码语言:javascript
复制
eval_metric_ops = {
    "rmse": tf.metrics.root_mean_squared_error(
        tf.cast(labels, tf.float64), predictions)
}

定义模型的训练操作

操练操作定义了TensorFlow在将模型拟合到训练数据时将使用的优化算法。通常在培训时,目标是尽量减少损失。创建操练操作的一种简单方法是实例化一个tf.train.Optimizer子类并调用该minimize方法。

下面的代码model_fn使用模型的定义损失,传递给函数的学习速率params以及梯度下降优化器中计算的损失值来定义鲍鱼的操练操作。对于global_step,便利功能tf.train.get_global_step负责生成一个整型变量:

代码语言:javascript
复制
optimizer = tf.train.GradientDescentOptimizer(
    learning_rate=params["learning_rate"])
train_op = optimizer.minimize(
    loss=loss, global_step=tf.train.get_global_step())

有关优化程序的完整列表以及其他详细信息,请参阅API指南

完整的鲍鱼 model_fn

这是model_fn鲍鱼年龄预测的最终模型。以下代码配置神经网络;定义损失和操练操作;并返回一个EstimatorSpec包含对象modepredictions_dictloss,和train_op

代码语言:javascript
复制
def model_fn(features, labels, mode, params):
  """Model function for Estimator."""

  # Connect the first hidden layer to input layer
  # (features["x"]) with relu activation
  first_hidden_layer = tf.layers.dense(features["x"], 10, activation=tf.nn.relu)

  # Connect the second hidden layer to first hidden layer with relu
  second_hidden_layer = tf.layers.dense(
      first_hidden_layer, 10, activation=tf.nn.relu)

  # Connect the output layer to second hidden layer (no activation fn)
  output_layer = tf.layers.dense(second_hidden_layer, 1)

  # Reshape output layer to 1-dim Tensor to return predictions
  predictions = tf.reshape(output_layer, [-1])

  # Provide an estimator spec for `ModeKeys.PREDICT`.
  if mode == tf.estimator.ModeKeys.PREDICT:
    return tf.estimator.EstimatorSpec(
        mode=mode,
        predictions={"ages": predictions})

  # Calculate loss using mean squared error
  loss = tf.losses.mean_squared_error(labels, predictions)

  # Calculate root mean squared error as additional eval metric
  eval_metric_ops = {
      "rmse": tf.metrics.root_mean_squared_error(
          tf.cast(labels, tf.float64), predictions)
  }

  optimizer = tf.train.GradientDescentOptimizer(
      learning_rate=params["learning_rate"])
  train_op = optimizer.minimize(
      loss=loss, global_step=tf.train.get_global_step())

  # Provide an estimator spec for `ModeKeys.EVAL` and `ModeKeys.TRAIN` modes.
  return tf.estimator.EstimatorSpec(
      mode=mode,
      loss=loss,
      train_op=train_op,
      eval_metric_ops=eval_metric_ops)

运行鲍鱼模型

你已经Estimator为鲍鱼预测因子实例化了一个并确定了它的行为model_fn; 所有剩下要做的就是操练,评估和预测。

将以下代码添加到最后main()以使神经网络适合训练数据并评估准确性:

代码语言:javascript
复制
train_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"x": np.array(training_set.data)},
    y=np.array(training_set.target),
    num_epochs=None,
    shuffle=True)

# Train
nn.train(input_fn=train_input_fn, steps=5000)

# Score accuracy
test_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"x": np.array(test_set.data)},
    y=np.array(test_set.target),
    num_epochs=1,
    shuffle=False)

ev = nn.evaluate(input_fn=test_input_fn)
print("Loss: %s" % ev["loss"])
print("Root Mean Squared Error: %s" % ev["rmse"])

注意:上述代码使用输入函数将特征(x)和标签(yTensors输入操练(train_input_fn)和评估(test_input_fn)的模型中。要了解有关输入函数的更多信息,请参阅教程使用tf.estimator构建输入函数。

然后运行代码。你应该看到如下的输出:

代码语言:javascript
复制
...
INFO:tensorflow:loss = 4.86658, step = 4701
INFO:tensorflow:loss = 4.86191, step = 4801
INFO:tensorflow:loss = 4.85788, step = 4901
...
INFO:tensorflow:Saving evaluation summary for 5000 step: loss = 5.581
Loss: 5.581

报告的损失分数是从数据集model_fn上运行时返回的均方误差ABALONE_TEST

要预测ABALONE_PREDICT数据集的年龄,请将以下内容添加到main()

代码语言:javascript
复制
# Print out predictions
predict_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"x": prediction_set.data},
    num_epochs=1,
    shuffle=False)
predictions = nn.predict(input_fn=predict_input_fn)
for i, p in enumerate(predictions):
  print("Prediction %s: %s" % (i + 1, p["ages"]))

在这里,该predict()函数返回结果predictions作为一个迭代。该for环列举并打印出结果。重新运行代码,您应该看到类似于以下内容的输出:

代码语言:javascript
复制
...
Prediction 1: 4.92229
Prediction 2: 10.3225
Prediction 3: 7.384
Prediction 4: 10.6264
Prediction 5: 11.0862
Prediction 6: 9.39239
Prediction 7: 11.1289

其他资源

恭喜!您已经成功构建了一个tf.estimator Estimator。有关building Estimator的其他参考资料,请参阅API指南的以下部分:

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com