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

在tf.estimator中创建估计器

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

  • tf.estimator.LinearClassifier:构造一个线性分类模型。

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

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

  • 实例化一个 Estimator

先决条件

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来训练神经网络。

鲍鱼年龄预测

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

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

特征描述
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:

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

启用记录:

tf.logging.set_verbosity(tf.logging.INFO)

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

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文件以用于培训,测试和预测数据集:

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,您可以在构造函数中提供所有配置参数,例如:

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

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

  • model_fn:一个包含所有上述逻辑的函数对象,以支持操练,评估和预测。您有责任实施该功能。下一节,model_fn详细介绍构建封面创建模型函数。

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

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

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

# 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模型函数的基本框架如下所示:

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

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

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

  • 配置模型 - 在这里,对于鲍鱼预测工具,这将是一个神经网络。

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

  • mode(需要)。模型运行的模式。通常情况下,您将返回此处的mode参数model_fn

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

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

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

input_layer = tf.feature_column.input_layer( features=features, feature_columns=[age, height, weight])

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

  • features。从字符串键到Tensors包含相应特征数据的映射。这正是model_fnfeatures论点中传递给我们的。

然后,神经网络的输入层必须通过激活函数连接到一个或多个隐藏层,激活函数对前一层的数据执行非线性变换。然后将最后一个隐藏层连接到输出层,即模型中的最后一层。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)

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

output_layer = tf.layers.dense(inputs=second_hidden_layer, units=10, activation_fn=tf.sigmoid)

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

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

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损失)。

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

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张量的数据类型,该数据类型将包含实际值:

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负责生成一个整型变量:

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

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()以使神经网络适合训练数据并评估准确性:

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构建输入函数。

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

... 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()

# 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环列举并打印出结果。重新运行代码,您应该看到类似于以下内容的输出:

... 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指南的以下部分: