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_dir
和config
。
对于鲍鱼年龄预测,该模型将接受一个超参数:学习率。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
Estimator
API模型函数的基本框架如下所示:
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_fn
在features
论点中传递给我们的。
然后,神经网络的输入层必须通过激活函数连接到一个或多个隐藏层,激活函数对前一层的数据执行非线性变换。然后将最后一个隐藏层连接到输出层,即模型中的最后一层。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_layer
,second_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
包含对象mode
,predictions_dict
,loss
,和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
)和标签(y
)Tensor
s输入操练(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指南的以下部分: