tensorflow - 创建用户自定义Estimators

tf官网创建用户自定义estimator

1.Pre-made vs. custom(预定义和自定义)

如下图所示,预定义的Estimator是tf.estimator.Estimator类的子类,而自定义Estimators是tf.estimator.Estimator的一个实例:

Figure1. Pre-made and custom Estimators are all Estimators.

预定义Estimators是完全成熟的。 但有时候,你需要更多地控制Estimator的行为。 这就是自定义Estimators的用武之地。你可以创建一个自定义Estimator来做任何事情,如果您想以某种不寻常的方式连接隐藏层,请编写自定义Estimator。 如果想要为模型计算唯一metric,请编写自定义Estimator。 基本上,如果您想要针对特定问题优化Estimator,请编写自定义Estimator。

模型函数(或model_fn)实现ML算法。 使用预定义Estimator和自定义Estimator的唯一区别是:

  • 使用预定义的Estimators,有人已经为你编写了模型函数。
  • 使用自定义Estimators,你必须自己写模型函数。

你的模型函数可以实现各种算法,定义各种隐藏层和metrics。 与输入函数(input functions)一样,所有模型函数都必须接受一组标准输入参数并返回一组标准输出值。 正如输入函数可以利用dataset API一样,模型函数也可以利用Layers API和Metrics API。

让我们看看如何使用自定义Estimator解决Iris问题。 快速提醒 - 这是我们试图模仿的Iris模型的组织结构:

Figure2. Our implementation of Iris contains four features, two hidden layers, and a logits output layer.

2.Write an Input function

我们的自定义Estimator实现使用与iris_data.py预定义Estimator实现中相同的输入函数。即:

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.
    dataset = dataset.shuffle(1000).repeat().batch(batch_size)

    # Return the read end of the pipeline.
    return dataset.make_one_shot_iterator().get_next()

此输入函数构建一个输入pipeline,该管道可生成(yield)多个(features,labels)对,其中features是字典类型特征。

3.Create feature columns

As detailed in the Premade Estimators and Feature Columns chapters, you must define your model’s feature columns to specify how the model should use each feature. Whether working with pre-made Estimators or custom Estimators, you define feature columns in the same fashion.
如预定义Estimators和Feature Columns章节中所述,您必须定义模型的feature columns,以指定模型应如何使用每个特征。 无论是使用预定义Estimators还是自定义Estimators,都可以以相同的方式定义feature columns。

以下代码为每个输入特征创建一个简单的numeric_column,指示输入特征的值应直接用作模型的输入:

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

4.Write a model function

我们将使用的模型函数具有以下调用签名:

def my_model_fn(
   features, # This is batch_features from input_fn
   labels,   # This is batch_labels from input_fn
   mode,     # An instance of tf.estimator.ModeKeys
   params):  # Additional configuration

前两个参数是从input function返回的批量特征和标签; 也就是说,featureslabels是模型将使用的数据的句柄。 mode参数表明调用者是否正在请求训练(training),预测(predicting)或评估(evaluation)。

调用者可以将params传递给Estimator的构造函数。 传递给构造函数的任何参数依次传递给model_fn。 在custom_estimator.py中,以下行创建Estimator并设置params以配置模型。 此配置步骤与我们在Premade Estimators中配置tf.estimator.DNNClassifier的方式类似。

classifier = tf.estimator.Estimator(
    model_fn=my_model_fn,
    params={
        '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,
    })

要实现典型的模型函数,必须执行以下操作:

  • 定义模型
  • 为三种不同模式中的每一种指定额外的计算:
    • 预测Predict
    • 评估Evaluate
    • 训练Train

5.Define the model(定义模型)

基本的深度神经网络模型必须定义以下三个部分:
输入层
一个或多个隐藏层
输出层

5.1Define the input layer

model_fn的第一行调用tf.feature_column.input_layer将特征字典和feature_columns转换为模型的输入,如下所示:

    # Use `input_layer` to apply the feature columns.
    net = tf.feature_column.input_layer(features, params['feature_columns'])

The preceding line applies the transformations defined by your feature columns, creating the model’s input layer.
上一行应用由features columns中定义的转换,从而创建模型的输入图层,如下:

Figure3. Input Layer

5.2 Hidden Layers

如果要创建深度神经网络,则必须定义一个或多个隐藏层。 Layers API提供了一组丰富的函数来定义所有类型的隐藏层,包括卷积层、池化和dropout层。 对于Iris,我们只需要调用tf.layers.dense来创建隐藏层,其维度由params [‘hidden_layers’]定义。 在dense层中,每个节点连接到前一层中的每个节点。 这是相关的代码:

    # Build the hidden layers, sized according to the 'hidden_units' param.
    for units in params['hidden_units']:
        net = tf.layers.dense(net, units=units, activation=tf.nn.relu)
  • units参数定义给定层中的输出神经元的数量。
  • activation参数定义激活函数 - 在这个case中为Relu。

这里的变量net表示网络的当前顶层。 在第一次迭代期间,net表示输入层。 在每次循环迭代中,tf.layers.dense创建一个新层,使用变量net将前一层的输出作为其输入。

创建两个隐藏层后,我们的网络如下所示。 为简单起见,该图未显示每层中的所有单元。

请注意,tf.layers.dense提供了许多其他功能,包括设置多个正则化参数的功能。 但是,为了简单起见,我们将简单地接受其他参数的默认值。

5.3 Output Layer

我们将通过再次调用tf.layers.dense来定义输出层,这次没有激活函数:

    # Compute logits (1 per class).
    logits = tf.layers.dense(net, params['n_classes'], activation=None)

这里,net表示最后的隐藏层。 因此,完整的图层集现在连接如下:

The final hidden layer feeds into the output layer.

定义输出层时,units参数指定输出的神经元数量。 因此,通过将units设置为params [‘n_classes’],模型会为每个分类生成一个输出值。 输出向量的每个元素将包含分别针对Iris的相关类别计算的分数或“logit”:Setosa,Versicolor或Virginica。

稍后,这些logits将通过tf.nn.softmax函数转换为概率。

6.Implement training, evaluation, and prediction

创建模型函数的最后一步是编写实现预测prediction,评估evaluation和训练training的分支代码。

只要有人调用Estimator的train,evaluate或predict方法,就会调用模型函数。 回想一下,模型函数的签名如下所示:

def my_model_fn(
   features, # This is batch_features from input_fn
   labels,   # This is batch_labels from input_fn
   mode,     # An instance of tf.estimator.ModeKeys, see below
   params):  # Additional configuration

专注于第三个参数,mode。 如下表所示,当有人调用train,evaluate或predict时,Estimator框架使用如下设置的mode参数调用模型函数,如下所示:

Estimator methodEstimator Mode
tf.estimator.Estimator.traintf.estimator.ModeKeys.TRAIN
tf.estimator.Estimator.evaluatetf.estimator.ModeKeys.EVAL
tf.estimator.Estimator.predicttf.estimator.ModeKeys.PREDICT

例如,假设你实例化自定义Estimator以生成名为classifier的对象。 然后,您进行以下调用:

classifier = tf.estimator.Estimator(...)
classifier.train(input_fn=lambda: my_input_fn(FILE_TRAIN, True, 500))

Estimator框架然后调用模型函数,并将模式设置为ModeKeys.TRAIN

您的模型函数必须提供代码来处理所有三个模式值。 对于每个mode,你的代码必须返回tf.estimator.EstimatorSpec的实例,其中包含调用者所需的信息。 我们来看看每种模式。

6.1 Predict

当调用Estimator的predict方法时,model_fn接收mode = ModeKeys.PREDICT。 在这种情况下,模型函数必须返回包含预测的tf.estimator.EstimatorSpec。

在进行预测之前,必须对模型进行训练。 训练后的模型存储在实例化Estimator时建立的model_dir目录中的磁盘上。

生成此模型预测的代码如下所示:

# Compute predictions.
predicted_classes = tf.argmax(logits, 1)
if mode == tf.estimator.ModeKeys.PREDICT:
    predictions = {
        'class_ids': predicted_classes[:, tf.newaxis],
        'probabilities': tf.nn.softmax(logits),
        'logits': logits,
    }
    return tf.estimator.EstimatorSpec(mode, predictions=predictions)

predictions字典包含模型在预测模式下运行时返回的所有内容。

预测包含以下三个键/值对:

  • class_ids包含类id(0,1或2),表示模型对此示例中最可能的物种的预测。
  • 概率包含三个概率(在此示例中,0.02,0.95和0.03)
  • logit保存原始logit值(在本例中为-1.3,2.6和-0.9)

我们通过tf.estimator.EstimatorSpec的predictions参数将该字典返回给调用者。 Estimator的tf.estimator.Estimator.predict方法将产生(yield)这些词典。

6.2 Calculate the loss

对于训练和评估,我们需要计算模型的损失,这是将进行优化的目标。

我们可以通过调用tf.losses.sparse_softmax_cross_entropy来计算损失。 当正确分类(在索引label处)的概率接近1.0时,此函数返回的值将最低约为0。 随着正确类别的概率降低,返回的损失值逐渐增大。

此函数返回整个批次的平均值。

# Compute loss.
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)

6.3 Evaluate

当调用Estimator的evaluate方法时,model_fn接收mode = ModeKeys.EVAL。 在这种情况下,模型函数必须返回包含模型损失以及可选的一个或多个指标metric的tf.estimator.EstimatorSpec。

虽然返回metrics是可选的,但大多数自定义Estimators确实至少返回一个指标。 TensorFlow提供Metrics模块tf.metrics来计算常用指标。为简洁起见,我们只返回准确性accuracy。 tf.metrics.accuracy函数将我们的预测与真实值进行比较,即与输入函数提供的标签进行比较。 tf.metrics.accuracy函数要求标签和预测具有相同的shape。 这是对tf.metrics.accuracy的调用:

# Compute evaluation metrics.
accuracy = tf.metrics.accuracy(labels=labels,
                               predictions=predicted_classes,
                               name='acc_op')

返回用于评估的tf.estimator.EstimatorSpec通常包含以下信息:

  • loss, 模型的损失值
  • eval_metric_ops, 可选的metrics字典

因此,我们将创建一个包含我们唯一指标的字典。 如果我们计算了其他指标,我们会将它们作为额外的键/值对添加到同一个字典中。 然后,我们将在tf.estimator.EstimatorSpec的eval_metric_ops参数中传递该字典。 这是代码:

metrics = {'accuracy': accuracy}
tf.summary.scalar('accuracy', accuracy[1])

if mode == tf.estimator.ModeKeys.EVAL:
    return tf.estimator.EstimatorSpec(
        mode, loss=loss, eval_metric_ops=metrics)

tf.summary.scalar将在TRAIN和EVAL模式下使TensorBoard可用。 (稍后会详细介绍)。

6.4 train

调用Estimator的train方法时,将使用mode = ModeKeys.TRAIN调用model_fn。 在这种情况下,模型函数必须返回包含loss和训练operation的EstimatorSpec。

构建训练operation需要优化器optimizer。 我们将使用tf.train.AdagradOptimizer,因为我们正在模仿DNNClassifier,它默认使用Adagrad。 tf.train包提供了许多其他优化器 - 可以随意尝试它们。

以下是构建优化器的代码:

optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)

接下来,我们使用优化器的tf.train.Optimizer.minimize方法对我们之前计算的损失构建训练operation。

minimize方法也采用global_step参数。 TensorFlow使用此参数计算已处理的训练步骤数(以了解何时结束训练运行)。 此外,global_step对于TensorBoard图表正常工作至关重要。 只需调用tf.train.get_global_step并将结果传递给minimize的global_step参数即可。

这是训练模型的代码:

train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())

返回训练的tf.estimator.EstimatorSpec必须设置以下字段:

  • loss, 其中包含损失函数的值。
  • train_op, 执行一步训练。

这是我们调用EstimatorSpec的代码:

return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)

模型函数model function现在已完成。

7.The custom Estimator

通过Estimator基类实例化自定义Estimator方式如下所示:

    # Build 2 hidden layer DNN with 10, 10 units respectively.
    classifier = tf.estimator.Estimator(
        model_fn=my_model_fn,
        params={
            '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,
        })

这里params字典的作用与DNNClassifier的关键字参数相同; 也就是说,params字典允许你配置Estimator而无需修改model_fn中的代码。

使用我们的Estimator训练,评估和生成预测的其余代码与Premade Estimators章节中的相同。 例如,以下行将训练模型:

# Train the Model.
classifier.train(
    input_fn=lambda:iris_data.train_input_fn(train_x, train_y, args.batch_size),
    steps=args.train_steps)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值