页面树结构

2017-11-09 ApacheCN 开源组织,第二期邀请成员活动,一起走的更远 : http://www.apachecn.org/member/209.html


MachineLearning 优酷地址 : http://i.youku.com/apachecn

转至元数据结尾
转至元数据起始

tf.contrib.learn 框架可以通过其高级别的 Estimator API 轻松构建和训练机器学习模型 。 Estimator ( 估算器 ) 提供您可以实例化的类以快速配置常见的模型类型,如 regressors ( 回归器 ) classifiers ( 分类器 ) 

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

本教程介绍如何如何使用  tf.contrib.learn 中提供的构建块来创建自己的 Estimator ( 估计器 ),这将根据物理测量来预测 鲍鱼的年龄。您将学习如何执行以下操作:

  • 实例化 Estimator ( 估计器 )
  • 构造一个自定义模型函数
  • 使用 tf.contrib.layers 配置神经网络
  • tf.losses 中选择合适的损失函数
  • 为您的模型定义训练
  • 生成并返回预测

 

Prerequisites ( 先决条件 )

本教程假设您已经知道 tf.contrib.learn API 基础知识,例如功能列和 fit() 操作。如果您以前从未使用过 tf.contrib.learn ,或需要进行复习,则应首先查看以下教程:

 

An Abalone Age Predictor ( 鲍鱼年龄预测器 )

可以通过其壳上的回环数量来估计 鲍鱼(海螺)的年龄 。然而,因为这个任务需要在显微镜下切割,染色和观察外壳,所以需要找到可以预测年龄的其他测量。

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

特征描述
长度鲍鱼长度(最长方向;毫米)
直径鲍鱼直径(垂直测量长度;毫米)
高度鲍鱼身高(其肉体内壳,毫米)
整体重量整个鲍鱼的重量(克)
打开重量鲍鱼肉重量(克)
内脏重量鲍鱼肠重(克),出血后
外壳重量干鲍鱼壳重量(克)

预测的标签是回环的数量,作为鲍鱼年龄的代理。

 

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

 

Setup ( 建立 ) 

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

以下部分将逐步编写 Estimator 代码; 这里提供了完整的最终代码

 

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

要将该鲍鱼数据集导入到模型中,您需要下载并将 CSV 加载到 TensorFlow Dataset 。首先,添加一些标准的 PythonTensorFlow 导入,并设置 FLAGS

from __future__ import absolute_importfrom __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
from tensorflow.contrib.learn.python.learn.estimators import model_fn as model_fn_lib

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 ,定义 flags ( 标签 ) ,允许用户通过命令行可选地指定 CSV 文件进行训练,测试和预测数据集(默认情况下,文件将从tensorflow.org下载):

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)

Instantiating an Estimator ( 实例化估计器 )

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

my_nn = tf.contrib.learn.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_fnparams

nn = tf.contrib.learn.Estimator(
    model_fn=model_fn, params=model_params)

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

  • params:将被传递到的超参数(例如,learning rate ( 学习率 ) ,dropout ( 退出 ) )的可选指令 model_fn 

注:就像 tf.contrib.learn 的预定义的回归系数和分类,在 Estimator 初始化还接受常规配置参数 model_dir 和 config 

对于鲍鱼年龄预测值,该模型将接受一个超参数:learning rate ( 学习率 ) 。 LEARNING_RATE 在代码开始时定义一个常量(以黑体突出显示):记录配置之后:

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

# Learning rate for the model
LEARNING_RATE = 0.001

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

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

# Set model params
model_params = {"learning_rate": LEARNING_RATE}

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

Constructing the model_fn ( 构建 model_fn )

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

def model_fn(features, targets, 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 ModelFnOps object
   return ModelFnOps(mode, predictions, loss, train_op, eval_metric_ops)

model_fn 必须接受三个参数:

  • features:包含通过 fit(), evaluate()  predict() 传递给模型的功能的 dict
  • targets:包含通过 fit()evaluate()predict() 传递给模型的标签的 Tensor 。对于 predict() 调用将为空,因为这些是模型将推断的值。
  • mode:以下 tf.contrib.learn.ModeKeys 字符串值之一,表示调用了 model_fn 的上下文:
    • tf.contrib.learn.ModeKeys.TRAIN 在训练模式下调用 model_fn ,例如,通过 fit() 调用。
    • tf.contrib.learn.ModeKeys.EVAL。在评估模式下调用 model_fn ,例如,通过 evaluate() 调用。
    • tf.contrib.learn.ModeKeys.INFER。在推理模式下调用 model_fn ,例如,通过一个 predict() 调用。

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

功能主体执行以下任务(在以下部分详细描述):

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

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

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

  • predictions(需要 INFEREVAL 模式)。一个将您选择的关键名称映射到 Tensor 包含模型预测内容的 dict,例如:

    python predictions = {"results": tensor_of_predictions}

    INFER 模式中,您返回的 dict ModelFnOps 将被返回 predict() ,因此您可以按照要使用的格式构造它。

    EVAL 模式中,由 度量函数 使用 dict 来计算度量。

  • loss(需要 EVALTRAIN 模式)。A Tensor包含标量损失值:在所有输入示例中计算出的模型损失函数的输出(在模型的定义损失的后面更深入地讨论)。这在 TRAIN 模式下用于错误处理和记录,并被自动作为度量标准显示在 EVAL 模式中。

  • train_op(仅在 TRAIN 模式下需要)。运行一步的操作。

  • eval_metric_ops(可选的)。名称/值对的指令,指定模型以 EVAL 模式运行时计算的指标。该名称是您选择的度量标准,该值是衡量指标计算的结果。该 tf.metrics  模块为各种常用度量提供预定义的功能。以下内容 eval_metric_ops 包含 "accuracy" 使用 tf.metrics.accuracy 以下方式计算的指标 :

    python eval_metric_ops = { "accuracy": tf.metrics.accuracy(labels, predictions) }

    如果没有指定 eval_metric_ops ,只有 loss 在评估时才会计算出来。

使用 tf.contrib.layer 配置神经网络

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

输入层是一系列节点(一个用于在模型中的每个特征),将接受被传递到特征数据 model_fn 中的 features 参数。如果 features 含有n 维 Tensor 与您的所有特征数据(其是如果 xy Datasets 直接地传递到 fit() evaluate()predict() ),然后它可以作为输入层。如果 features 包含的字典功能列通过输入功能,通过该模型,您可以将其转换为输入层 Tensor 用 tf.contrib.layers.input_from_feature_columns 的功能 tf.contrib.layers 。

input_layer = tf.contrib.layers.input_from_feature_columns(
    columns_to_tensors=features, feature_columns=[age, height, weight])

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

  • columns_to_tensors。将模型的 FeatureColumns 映射到包含相应功能数据的 Tensors 。这正是传递给 feature 参数中的 model_fn 的。
  • feature_columns。上述示例中的 model-ageheightweight 的所有 FeatureColumn 列表。

然后神经网络的输入层必须经由对前一层的数据执行非线性变换的激活函数连接到一个或多个隐藏层。最后的隐藏层然后连接到输出层,模型中的最后一层。tf.contrib.layers 为构建完全连接的层提供了以下便利功能:

  • relu(inputs, num_outputs)。使用 ReLU激活函数 ()创建 num_outputs 完全连接到上一层的节点层:inputs   tf.nn.relu 

    python hidden_layer = tf.contrib.layers.relu(inputs=input_layer, num_outputs=10)

  • relu6(inputs, num_outputs)。使用 ReLU 6 激活函数()创建 num_outputs 完全连接到上一层的节点层:hidden_layer tf.nn.relu6 

    python second_hidden_layer = tf.contrib.layers.relu6(inputs=hidden_layer, num_outputs=20)

  • linear(inputs, num_outputs)。创建一个 num_outputs 完全连接到上一层的节点 second_hidden_layer ,没有激活功能,只需一个线性转换:

    python output_layer = tf.contrib.layers.linear(inputs=second_hidden_layer, num_outputs=3)

所有这些功能都是更通用的 tf.contrib.layers.fully_connected 函数的部分,可以用来添加完全连接的层与其他激活函数,例如:

output_layer = tf.contrib.layers.fully_connected(inputs=second_hidden_layer,
                                                 num_outputs=10,
                                                 activation_fn=tf.sigmoid)

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

将它们放在一起,以下代码为鲍鱼预测器构建完整的神经网络,并捕获其预测:

def model_fn(features, targets, mode, params):
  """Model function for Estimator."""

  # Connect the first hidden layer to input layer
  # (features) with relu activation
  first_hidden_layer = tf.contrib.layers.relu(features, 10)

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

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

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

在这里,由于您将通过x和y参数将 abalone 数据集直接传递给 fit()evaluate() predict() ,所以输入层是 Tensor 传递给 model_fn 的功能。网络包含两个隐藏层,每层有 10 个节点和一个 ReLU 激活函数。输出层不包含激活函数,并且是一维张量的 tf.reshape ,以捕获模型的预测,这些预测存储在 predictions_dict 中。

定义模型的损失

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

  • absolute_difference(predictions, targets)。使用 绝对差分公式 (也称为L 1损失)计算损失。

  • log_loss(predictions, targets)。使用物流损失论坛计算损失 (通常用于逻辑回归)。

  • mean_squared_error(predictions, targets)。使用均方误差(MSE;也称为L 2损失)计算损失。

以下示例 使用(粗体)添加了loss 对鲍鱼的定义:model_fn mean_squared_error() 

def model_fn(features, targets, mode, params): 
  """Model function for Estimator.""" 
 
  # Connect the first hidden layer to input layer 
  # (features) with relu activation 
  first_hidden_layer = tf.contrib.layers.relu(features, 10) 
 
  # Connect the second hidden layer to first hidden layer with relu 
  second_hidden_layer = tf.contrib.layers.relu(first_hidden_layer, 10) 
 
  # Connect the output layer to second hidden layer (no activation fn) 
  output_layer = tf.contrib.layers.linear(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(targets, predictions)
  ... 

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

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

eval_metric_ops = {
    "rmse": tf.metrics.root_mean_squared_error(
        tf.cast(targets, tf.float64), predictions)
} 

定义模型的训练

训练 op 定义了 TensorFlow 在将模型拟合到训练数据时将使用的优化算法。通常在培训时,目标是尽量减少损失。tf.contrib.layers API 提供了一个功能 optimize_loss ,它返回一个可以做到这一点的训练操作。optimize_loss有四个必需的参数:

注意:该 optimize_loss 函数支持附加的可选参数以进一步配置优化程序,例如执行衰减。有关更多信息,请参阅 API文档

以下代码 model_fn 使用在定义模型中的损失计算的损失值,传递给函数的学习速率 paramsSGD 优化器来定义鲍鱼的训练操作。因为 global_steptf.contrib.framework tf.train.get_global_step 中的方便函数 负责生成一个整数变量:

train_op = tf.contrib.layers.optimize_loss(
    loss=loss,
    global_step=tf.contrib.framework.get_global_step(),
    learning_rate=params["learning_rate"],
    optimizer="SGD")

完整的鲍鱼 model_fn

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

def model_fn(features, targets, mode, params):
  """Model function for Estimator."""

  # Connect the first hidden layer to input layer
  # (features) with relu activation
  first_hidden_layer = tf.contrib.layers.relu(features, 10)

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

  # Connect the output layer to second hidden layer (no activation fn)
  output_layer = tf.contrib.layers.linear(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(targets, predictions)

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

  train_op = tf.contrib.layers.optimize_loss(
      loss=loss,
      global_step=tf.contrib.framework.get_global_step(),
      learning_rate=params["learning_rate"],
      optimizer="SGD")

  return model_fn_lib.ModelFnOps(
      mode=mode,
      predictions=predictions_dict,
      loss=loss,
      train_op=train_op,
      eval_metric_ops=eval_metric_ops) 

运行鲍鱼模型

您已经为鲍鱼预测器实例化了一个 Estimator 并在 model_fn 中定义了它的行为; 剩下的一切就是训练,评估和预测。

将以下代码添加到 main() 的末尾以将神经网络适应于训练数据并评估准确性:

# Fit
nn.fit(x=training_set.data, y=training_set.target, steps=5000)

# Score accuracy
ev = nn.evaluate(x=test_set.data, y=test_set.target, steps=1)
print("Loss: %s" % ev["loss"])
print("Root Mean Squared Error: %s" % ev["rmse"]) 
然后运行代码。您应该看到如下输出:
...
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
predictions = nn.predict(x=prediction_set.data, as_iterable=True)
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.contrib.learn Estimator 。 有关构建估计器的其他参考资料,请参阅 API 指南的以下部分:

 

  • 无标签