预制评估器

在 TensorFlow.org 上查看 在 Google Colab 中运行 在 GitHub 上查看源代码 下载笔记本

本教程将向您展示如何使用 Estimators 在 TensorFlow 中解决 Iris 分类问题。Estimator 是 TensorFlow 的一个遗留高级表示,代表完整的模型。有关更多详细信息,请参阅 Estimators

首先

为了开始,您首先需要导入 TensorFlow 和一些您需要的库。

import tensorflow as tf

import pandas as pd

数据集

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

您将使用 Iris 数据集训练模型。Iris 数据集包含四个特征和一个 标签。这四个特征标识了单个鸢尾花的以下植物学特征

  • 萼片长度
  • 萼片宽度
  • 花瓣长度
  • 花瓣宽度

根据这些信息,您可以定义一些有用的常量来解析数据

CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species']
SPECIES = ['Setosa', 'Versicolor', 'Virginica']

接下来,使用 Keras 和 Pandas 下载并解析 Iris 数据集。请注意,您为训练和测试保留了不同的数据集。

train_path = tf.keras.utils.get_file(
    "iris_training.csv", "https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv")
test_path = tf.keras.utils.get_file(
    "iris_test.csv", "https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv")

train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)
test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)

您可以检查您的数据,以确保您有四个浮点型特征列和一个 int32 型标签。

train.head()

对于每个数据集,都将标签分离出来,模型将被训练来预测这些标签。

train_y = train.pop('Species')
test_y = test.pop('Species')

# The label column has now been removed from the features.
train.head()

使用 Estimators 进行编程的概述

现在您已经设置了数据集,可以使用 TensorFlow Estimator 定义模型。Estimator 是从 tf.estimator.Estimator 派生的任何类。TensorFlow 提供了一组 tf.estimator(例如,LinearRegressor)来实现常见的 ML 算法。除此之外,您还可以编写自己的 自定义 Estimators。建议在刚开始时使用预制 Estimators。

要编写基于预制 Estimators 的 TensorFlow 程序,您必须执行以下任务

  • 创建一个或多个输入函数。
  • 定义模型的特征列。
  • 实例化一个 Estimator,指定特征列和各种超参数。
  • 在 Estimator 对象上调用一个或多个方法,将适当的输入函数作为数据源传递。

让我们看看这些任务是如何在 Iris 分类中实现的。

创建输入函数

您必须创建输入函数来提供用于训练、评估和预测的数据。

输入函数是一个返回 tf.data.Dataset 对象的函数,该对象输出以下两个元素的元组

  • features - 一个 Python 字典,其中
    • 每个键都是特征的名称。
    • 每个值都是一个数组,包含该特征的所有值。
  • label - 一个数组,包含每个示例的 标签 的值。

为了演示输入函数的格式,这里有一个简单的实现

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 的 Dataset API,它可以解析各种数据。

Dataset API 可以为您处理许多常见情况。例如,使用 Dataset API,您可以轻松地并行地从大量文件中读取记录,并将它们合并成一个数据流。

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

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

    # Shuffle and repeat if you are in training mode.
    if training:
        dataset = dataset.shuffle(1000).repeat()

    return dataset.batch(batch_size)

定义特征列

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

对于 Iris,4 个原始特征是数值,因此您将构建一个特征列列表,告诉 Estimator 模型将这四个特征中的每一个都表示为 32 位浮点数。因此,创建特征列的代码是

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

特征列可以比这里显示的要复杂得多。您可以在 本指南 中阅读有关特征列的更多信息。

现在您已经描述了您希望模型如何表示原始特征,您可以构建估计器。

实例化一个估计器

Iris 问题是一个经典的分类问题。幸运的是,TensorFlow 提供了几个预制的分类器 Estimators,包括

  • tf.estimator.DNNClassifier 用于执行多类分类的深度模型。
  • tf.estimator.DNNLinearCombinedClassifier 用于宽而深的模型。
  • tf.estimator.LinearClassifier 用于基于线性模型的分类器。

对于 Iris 问题,tf.estimator.DNNClassifier 似乎是最好的选择。以下是您实例化此 Estimator 的方法

# Build a DNN with 2 hidden layers with 30 and 10 hidden nodes each.
classifier = tf.estimator.DNNClassifier(
    feature_columns=my_feature_columns,
    # Two hidden layers of 30 and 10 nodes respectively.
    hidden_units=[30, 10],
    # The model must choose between 3 classes.
    n_classes=3)

训练、评估和预测

现在您已经有了 Estimator 对象,您可以调用方法来执行以下操作

  • 训练模型。
  • 评估训练后的模型。
  • 使用训练后的模型进行预测。

训练模型

通过调用 Estimator 的 train 方法来训练模型,如下所示

# Train the Model.
classifier.train(
    input_fn=lambda: input_fn(train, train_y, training=True),
    steps=5000)

请注意,您将 input_fn 调用包装在 lambda 中,以捕获参数,同时提供一个不带参数的输入函数,如 Estimator 所期望的那样。 steps 参数告诉方法在一定数量的训练步骤后停止训练。

评估训练后的模型

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

eval_result = classifier.evaluate(
    input_fn=lambda: input_fn(test, test_y, training=False))

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

与对 train 方法的调用不同,您没有将 steps 参数传递给评估。用于评估的 input_fn 只会生成一个 时期 的数据。

eval_result 字典还包含 average_loss(每个样本的平均损失)、loss(每个小批次的平均损失)以及估计器的 global_step 的值(它经历的训练迭代次数)。

从训练后的模型中进行预测(推断)

现在您有一个训练后的模型,可以产生良好的评估结果。现在您可以使用训练后的模型来预测鸢尾花的物种,方法是根据一些未标记的测量结果。与训练和评估一样,您可以使用单个函数调用来进行预测

# 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],
}

def input_fn(features, batch_size=256):
    """An input function for prediction."""
    # Convert the inputs to a Dataset without labels.
    return tf.data.Dataset.from_tensor_slices(dict(features)).batch(batch_size)

predictions = classifier.predict(
    input_fn=lambda: input_fn(predict_x))

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

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

    print('Prediction is "{}" ({:.1f}%), expected "{}"'.format(
        SPECIES[class_id], 100 * probability, expec))