参考文章:创建定制化 Estimator
文章目录
-
- 创建定制化 Estimator
-
- 预制 vs. 定制化
- 创建功能列
- 写一个模型函数
- 定义模型
-
- 定义输出层
- 隐藏层
- 输出层
- 实现训练、评估、预测 {#modes}
-
- 预测
- 计算损失
- 评估
- 训练
- 定制化 Estimator
- TensorBoard
- 总结
创建定制化 Estimator
Premade Estimators
下载及访问示例代码,请调用以下两个命令:
git clone https://github.com/tensorflow/models/
cd models/samples/core/get_started
在本文档中,我们将查看 custom_estimator.py。您可以使用以下命令运行它:
python custom_estimator.py
如果你感到不耐烦,可随时将 custom_estimator.py 与 premade_estimator.py 进行比较(对比),它们在同一目录中。
预制 vs. 定制化
tf.estimator.Estimator
预制和定制化 Estimator 都是 Estimator。
预制 Estimator 更加成熟。有时候您需要更多地控制 Estimator 的行为。这就是定制化 Estimator 出现的场景。您可以创建一个定制化 Estimator 来做任何事情。如果希望以某种不寻常的方式连接隐藏层,请编写定制化 Estimator。如果想要计算模型的唯一度量,请编写定制化 Estimator。基本上,如果想要针对特定问题优化的 Estimator,请编写定制化 Estimator。
一个函数模型(或者 model_fn)实现了 ML 算法,使用预制和定制化 Estimator 的唯一区别是:
- 预制 Estimator,已经有人为您编写了函数模型。
- 定制化 Estimator,您必须自己写函数模型。
(Dontla:是不是就是需要自己写算法和直接使用别人的算法的区别?)
您的函数模型可以实现范围更广的算法,定义各种隐藏层和度量。与输入函数一样,所有函数模型都必须接受一组标准的输入参数,并返回一组标准的输出值。就像输入函数可以利用数据集 API 一样,函数模型可以利用层 API 和 度量 API。
让我们看看如何使用定制化 Estimator 解决 Iris (虹膜、鸢尾花)问题。快速提醒 —— 这是我们尝试模仿鸢尾花模型的组织结构:
(Dontla:sepal 萼片、petal 花瓣,Setosa、Versicolor与Virginica表示鸢尾属植物的三种花,具体结构参见:鸢尾花识别问题,萼片有什么用?)
我们的鸢尾花实施包含四个特征,两个隐藏层,和一个 logits 输出层。
写一个输入函数
Premade Estimators
def train_input_fn(features, labels, batch_size):"""An input function for training"""# 将输入转换为数据集。dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))# 随机播放,重复和批处理示例。dataset = dataset.shuffle(1000).repeat().batch(batch_size)# 返回管道读取的结束端。return dataset.make_one_shot_iterator().get_next()
这个输入函数建立一个输入流水线,产生一批 (features, labels) 对,其中 features 是字典特征。
创建功能列
特征列
下面的代码为每个输入特征创建一个简单的 numeric_column ,表明输入特征值应该直接作为模型的输入:
# 特征列描述如何使用输入。
my_feature_columns = []
for key in train_x.keys():my_feature_columns.append(tf.feature_column.numeric_column(key=key))
写一个模型函数
我们将使用的函数模型具有以下调用签名:
def my_model_fn(features, # This is batch_features from input_fnlabels, # This is batch_labels from input_fnmode, # An instance of tf.estimator.ModeKeysparams): # Additional configuration
前两个参数是输入函数返回的功能部件和标签的批次:也就是说,features 和 labels 是您的模型将使用的数据的句柄。mode 参数指向调用方是否请求训练、预测或评估。
tf.estimator.DNNClassifier
classifier = tf.estimator.Estimator(model_fn=my_model,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,})
实现一个典型的函数模型,你必须做如下操作:
- 定义模型
- 为三种不同模式指定附加计算:
- 预测
评估 - 训练
定义模型
基本的深度神经网络模型必须定义如下三个部分:
- 一个输入层
- 一个或者更多的隐藏层
- 一个输出层
定义输出层
tf.feature_column.input_layer
# 使用 `input_layer` 来应用特征列。net = tf.feature_column.input_layer(features, params['feature_columns'])
隐藏层
tf.layers.dense
# 根据 'hidden_units' 参数构建隐藏层。for units in params['hidden_units']:net = tf.layers.dense(net, units=units, activation=tf.nn.relu)
- units 参数定义了给定层中输出神经元的数量。
- activation 参数定义激活函数 — 在本例中为 Relu。
这里的变量 net 表示网络中当前的顶层。第一次迭代时,net 表示输入层。在每次迭代循环中,tf.layers.dense 创建一个新层,它使用 net 将上一层的输出作为输入。
创建两个隐藏层后,我们的网络如下所示。简而言之,该图不显示每层的所有单元。
tf.layers.dense
输出层
tf.layers.dense
# 计算 logits (每个 class 一个)。logits = tf.layers.dense(net, params['n_classes'], activation=None)
这里 net 表示最后的隐藏层。因此,现在这个图层连接如下:
最后的隐藏层输入到输出层。
定义输出层时,units 参数指定输出的数量。因此,将 units 设置为 params[‘n_classes’],该模型会为每个类生成一个输出值。输出向量的每个元素都将包含得分或 logit,为关联的 Iris 类计算:Setosa、Versicolor 或 Virginica。
tf.nn.softmax
实现训练、评估、预测 {#modes}
创建函数模型的最后一步是编写分支代码实现预测、评估和训练。
当有人调用 Estimator 的 train 时,模型函数就会调用 evaluate 或者 predict 方法。就像这样回调模型的签名函数:
def my_model_fn(features, # This is batch_features from input_fnlabels, # This is batch_labels from input_fnmode, # An instance of tf.estimator.ModeKeys, see belowparams): # Additional configuration
关注第三个论点 — 模式。如下表所示,当某人调用 train,evaluate,或者 predict,Estimator 框架调用您的模型。函数模式的参数设置如下:
Estimator 方法 | Estimator 模块 |
---|---|
tf.estimator.Estimator.train | tf.estimator.ModeKeys.TRAIN |
tf.estimator.Estimator.evaluate | tf.estimator.ModeKeys.EVAL |
tf.estimator.Estimator.predict | tf.estimator.ModeKeys.PREDICT |
例如,假设您实例化一个自定义 Estimator 来生成一个 叫做 classifier 的对象。然后您执行以下调用:
classifier = tf.estimator.Estimator(...)
classifier.train(input_fn=lambda: my_input_fn(FILE_TRAIN, True, 500))
Estimator 框架调用您的函数模型,模式设置为 ModeKeys.TRAIN。
您的函数模型必须提供处理所有三个模式值的代码。对于每个模式值,您的代码必须返回 tf.estimator.EstimatorSpec,包含调用者的请求信息。让我们检查每一种模式。
预测
当 Estimator 的 predict 方法被调用时,model_fn 会接收到 mode = ModeKeys.PREDICT。在这种情况下,函数模型必须返回一个包含预测的 tf.estimator.EstimatorSpec。
在进行预测之前,模型必须经过训练。经过训练的模型存储在实例化 Estimator 时建立的 model_dir 目录中的磁盘上
为此模型生成预测的代码如下所示:
# 预测计算
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),这个例子中最有可能出现的物种的预测。
- probabilities 保存三个概率 (在本例中 0.02、0.95 和 0.03)。
- logit 保存原始 logit 值 (在本例中 -1.3、2.6 和 -0.9)。
tf.estimator.Estimator.predict
计算损失
对于 training 和 evaluation 我们都需要计算模型的损失。这是将要被优化的目标。
tf.losses.sparse_softmax_cross_entropy
此函数返回整个批处理的平均值。
# 损失计算。
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)
评估
当 Estimator 的 evaluate 方法被调用时,model_fn 接收到 mode = ModeKeys.EVAL。在这种情况下,模型函数必须返回一 tf.estimator.EstimatorSpec 包含模型损失和可选的一个或者更多的指标。
tf.metrics.accuracy
# metrics 指标计算。
accuracy = tf.metrics.accuracy(labels=labels,predictions=predicted_classes,name='acc_op')
tf.estimator.EstimatorSpec
- loss,这是模型损失。
- eval_metric_ops,这是一个可选的度量字典。
所以我们将创建一个包含我们唯一度量标准的字典。如果我们计算了其他度量,我们会将它们添加为与其他键值对相同的字典。然后我们将在 eval_metric_ops 中传递该字典 tf.estimator.EstimatorSpec 的参数。代码如下:
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
训练
当调用 Estimator 的 train 方法时,使用 mode = ModeKeys.TRAIN 调用 model_fn。在这种情况下,模型函数必须返回包含损失和培训操作的 EstimatorSpec 。
tf.tra.AdagradOptimizer
以下是构建优化器的代码:
optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)
tf.train.Optimizer.minimize
tf.train.get_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)
模型功能现在已经完成了。
定制化 Estimator
通过 Estimator 基类指定定制化 Estimator,如下所示:
# 用两个 10 单元建立 2 个隐藏层的 DNN。classifier = tf.estimator.Estimator(model_fn=my_model,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 字典允许您在不修改 model_fn 中代码的情况下配置您的 Estimator。
Premade Estimators
# 训练模型
classifier.train(input_fn=lambda:iris_data.train_input_fn(train_x, train_y, args.batch_size),steps=args.train_steps)
TensorBoard
您可以在 TensorBoard 中查看定制化 Estimator 的训练结果。查看此报告,请从命令行启动 TensorBoard,如下所示:
# 将 PATH 替换为以 model_dir 形式传递的实际路径
tensorboard --logdir=PATH
然后在浏览器输入 http://localhost:6006 来打开 TensorBoard。
所有预制 Estimator 都会自动将大量信息记录到 TensorBoard 中。而对于定制化的 Estimators,TensorBoard 只提供一个默认日志(损失图)以及显式告诉 TensorBoard 进行日志记录的信息。对于您刚刚创建的定制化 Estimator,TensorBoard 生成以下内容:
TensorBoard 显示三个图形。
简而言之,下面三张图将告诉您:
-
global_step/sec: 显示多少批的性能指标(更新),我们每秒处理的作为训练模型)。
-
损失:损失报告。
-
准确性:准确性由以下两行记录:
eval_metric_ops={‘my_accuracy’: accuracy},在评估期间
tf.summary.scalar(‘accuracy’, accuracy[1]),在训练期间
这些 tensorboard 是向优化的 minimize 方法传递 global_step 的主要原因之一。没有它,模型就不能记录这些图的 x 坐标。
注意以下 my_accuracy 和 loss 图表:
- 橙色线代表训练。
- 蓝色点代表评估。
在训练期间,随着批次的处理,会定期记录摘要(橙色线),这就是为什么它会跨越 x 轴范围的图形。
相比之下,对于每个 evaluate 调用,评估只会在图形上产生一个点。这个点包含整个评估调用的平均值。这在图上没有宽度,因为它完全是从特定训练步骤的模型状态(从单个检查点)计算的。
如下图所示,您可以有选择地看到。使用左侧的控件禁用/启用报告。
启用或暂停报告。
总结
尽管预制 Estimator 是快速创建新模型的高效方式,通常您需要提供定制化 Estimator 额外的灵活性。幸运的是,预制和定制化遵循相同的编程模型。唯一的实际区别是您必须写一个模型用于自定义 Estimators 的函数,其他的所有内容都是相同的。
了解更多细节,请务必查看:
-
使用定制化 Estimator MINIST 的官方 TensorFlow 实现 ,
-
TensorFlow 官方模型库,其中包含了更多使用定制化 Estimator 的示例。
-
TensorBoard 视频
-
介绍 TensorBoard。
-
底层 API 编程介绍