Estimator入门

本文档介绍TensorFlow编程环境,并展示如何用TensorFlow解决鸢尾花分类问题。

先决条件

在本文档中使用示例代码之前,您需要执行以下操作:

获取示例代码

按照以下步骤获取我们将要经历的示例代码:

  1. 通过输入以下命令从github克隆TensorFlow Models存储库:

    git clone https://github.com/tensorflow/models
  2. 将该分支内的目录更改为包含本文档中使用的样本的位置:

    cd models/samples/core/get_started/

本文档中描述的程序是premade_estimator.py 该程序使用iris_data.py获取其训练数据。

运行程序

你可以像运行任何Python程序一样运行TensorFlow程序。 例如:

python premade_estimator.py

该程序应该输出训练日志,然后对测试集进行一些预测。 例如,以下输出中的第一行显示该模型认为测试集中的第一个样本是Setosa的可能性为99.6%。 由于测试集期望的就是Setosa,这似乎是一个很好的预测。

...
Prediction is "Setosa" (99.6%), expected "Setosa"

Prediction is "Versicolor" (99.8%), expected "Versicolor"

Prediction is "Virginica" (97.9%), expected "Virginica"

如果程序生成了错误而不是答案,请问自己以下问题:

编程栈

在深入了解程序本身的细节之前,让我们来看看编程环境。 如下图所示,TensorFlow提供了一个由多个API层组成的编程栈:

我们强烈建议使用以下API编写TensorFlow程序:

分类鸢尾花:概述

本文档中的示例程序建立并测试了一个模型,该模型根据它们的萼片花瓣的大小将鸢尾花分为三种不同的物种。

Petal geometry compared for three iris species: Iris setosa, Iris virginica, and Iris versicolor

从左到右:Iris setosa (by Radomil, CC BY-SA 3.0), Iris versicolor (by Dlanglois, CC BY-SA 3.0)以及 Iris virginica (by Frank Mayfield, CC BY-SA 2.0)。

数据集

鸢尾花数据集包含四个特征和一个标签 这四个特征确定了个体鸢尾花的下列植物学特征:

我们的模型将这些特征表示为float32数值数据。

该标签标识了鸢尾花种类,其必须是以下之一:

我们的模型将标签表示为int32分类数据。

下表显示数据集中的三个样本:

萼片长度 萼片宽度 花瓣长度 花瓣宽度 物种(标签)
5.1 3.3 1.7 0.5 0(Setosa)
5.0 2.3 3.3 1.0 1 (versicolor)
6.4 2.8 5.6 2.2 2(virginica)

算法

该程序训练具有以下拓扑的深度神经网络分类器模型:

下图描述了特征、隐藏层和预测(未显示隐藏层中的所有节点):

A diagram of the network architecture: Inputs, 2 hidden layers, and outputs

推断

在未标记的样本上运行训练好的模型会得出三个预测结果,即该花是给定的鸢尾花物种的可能性。 这些输出预测的总和将是1.0。 对于样本,对未标注样本的预测可能如下所示:

前面的预测表明给定的未标记样本是Iris Versicolor的概率为95%。

用Estimator编程概述

Estimator是TensorFlow对完整模型的高级表示。 它处理各种细节,包括初始化、日志、保存和恢复以及许多其他功能,这样你可以专注于你的模型。 欲了解更多详情,请参阅Estimators

Estimator是从tf.estimator.Estimator派生的任何一个类。 TensorFlow提供一系列预制的Estimators(例如LinearRegressor),以实现常见的ML算法。 除此之外,你可以编写自己的自定义Estimator 我们建议在刚开始使用TensorFlow时使用预制的Estimator。 在获得预先制作Estimator的专业知识后,我们建议通过创建你自己的自定义Estimator来优化你的模型。

要根据预制的Estimator编写TensorFlow程序,你必须执行以下任务:

我们来看看这些任务是如何执行鸢尾花分类的。

创建输入函数

你必须创建输入函数来为训练、评估和预测提供数据。

输入函数返回一个tf.data.Dataset对象,该对象输出以下二元元组:

为了演示输入函数的格式,下面是一个简单的实现:

def input_evaluation_set():
    features = {'SepalLength': np.array([6.4, 5.0]),
                'SepalWidth':  np.array([2.8, 2.3]),
                'PetalLength': np.array([5.6, 3.3]),
                'PetalWidth':  np.array([2.2, 1.0])}
    labels = np.array([2, 1])
    return features, labels

你的输入函数可以以你喜欢的任何方式生成features字典和label列表。 不过,我们建议使用TensorFlow的数据集API,它可以解析各种数据。 在较高层次上,数据集API由以下类组成:

A diagram showing subclasses of the Dataset class

其中每个成员如下:

Dataset API可以为你处理很多常见情况。 例如,使用Dataset API,你可以轻松地从大量文件集合中并行读入记录,并将它们合并到一个流中。

为了在这个示例中保持简单,我们将使用pandas加载数据,并从此内存数据构建输入管道。

下面是用于训练的输入函数,可以在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.
    return dataset.shuffle(1000).repeat().batch(batch_size)

定义特征列

特征列是一个对象,描述模型如何使用特征字典的原始输入数据。 当你构建一个Estimator模型时,你将它传递给特征列列表,它描述你希望模型使用的每个特征。 tf.feature_column模块提供很多用于向模型表示数据的选项。

对于鸢尾花来说,4个原始特征是数值,所以我们将建立一个特征列列表,告诉Estimator模型将四个特征中的每一个都表示为32位浮点值。 因此,创建特征列的代码是:

# 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))

特征列可能比我们在这里展示的要复杂得多。 我们在后面的入门指南中详细介绍特征列。

现在我们已经描述我们希望模型如何表示原始特征,我们可以构建Estimator了。

实例化一个Estimator

鸢尾花问题是一个经典的分类问题。 幸运的是,TensorFlow提供了几个预制的分类器Estimator,其中包括:

对于鸢尾花问题,tf.estimator.DNNClassifier看起来是最好的选择。 以下是我们如何实例化此Estimator:

# Build a DNN with 2 hidden layers and 10 nodes in each hidden layer.
classifier = tf.estimator.DNNClassifier(
    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)

训练、评估和预测

现在我们有一个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)

在这里,我们将input_fn调用包含在lambda中,以捕获参数,同时提供不带参数的输入函数,正如Estimator预期的那样。 steps参数告诉方法在多次训练步骤后停止训练。

评估训练好的模型

现在模型已经过训练,我们可以获得一些关于其性能的统计数据。 以下代码块评估测试数据上训练模型的准确性:

# Evaluate the model.
eval_result = classifier.evaluate(
    input_fn=lambda:iris_data.eval_input_fn(test_x, test_y, args.batch_size))

print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))

与我们对训练方法的调用不同,我们没有通过steps参数进行评估。 我们的eval_input_fn仅产生一个数据的单个周期

运行此代码会生成以下输出(或类似内容):

Test set accuracy: 0.967

根据训练的模型进行预测(推断)

我们现在有一个训练有素的模型,可以产生良好的评估结果 我们现在可以使用训练好的模型根据一些未标记的测量值预测鸢尾花花的种类。 与训练和评估一样,我们使用单个函数调用进行预测:

# Generate predictions from the model
expected = ['Setosa', 'Versicolor', 'Virginica']
predict_x = {
    'SepalLength': [5.1, 5.9, 6.9],
    'SepalWidth': [3.3, 3.0, 3.1],
    'PetalLength': [1.7, 4.2, 5.4],
    'PetalWidth': [0.5, 1.5, 2.1],
}

predictions = classifier.predict(
    input_fn=lambda:iris_data.eval_input_fn(predict_x,
                                            batch_size=args.batch_size))

predict方法返回一个python可迭代对象,为每个样本生成一个预测结果字典。 以下代码打印了一些预测及其概率:

template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"')

for pred_dict, expec in zip(predictions, expected):
    class_id = pred_dict['class_ids'][0]
    probability = pred_dict['probabilities'][class_id]

    print(template.format(iris_data.SPECIES[class_id],
                          100 * probability, expec))

运行上面的代码将生成以下输出:

...
Prediction is "Setosa" (99.6%), expected "Setosa"

Prediction is "Versicolor" (99.8%), expected "Versicolor"

Prediction is "Virginica" (97.9%), expected "Virginica"

总结

预制估算器是快速创建标准模型的有效方法。

既然你已经开始编写TensorFlow程序,请考虑以下资料: