Estimator是Tensorflow提供的一种高阶API,在数据输入输出、模型构建、模型配置、模型保存等方面都进行了封装,隐藏了传统静态图的运行流程和底层细节,便于研究人员更专注于模型本身,本文即对Estimator的用法进行简要介绍。
Estimators的好处
- 模型内部有并行优化,因此无需修改代码就可以直接将运行在本地的代码部署到服务器/GPU集群/TPU上;
- Estimators对模型各部分进行了封装,因此便于代码重用;
- 代码可读性更好,隐藏了tensorflow底层的实现细节。
如何使用Pre-made Estimators
1、构造数据输入函数input_fn
input_fn
的任务:将从文件中读入的feature和label封装起来,转换为dataset的形式(或者是dictionary & label 的形式,本质是一样的,dataset就是对其的封装),以此来作为Estimator的输入数据。- 通常训练、评估和测试集各构造一个函数:train_input_fn, eval_input_fn, test_input_fn,也可选择直接在函数内部读取文件,返回一个dataset即可。
- 对于feature_column+dataset这种情况,其实相当于是feature为axis0, example为axis1,和传统的形式相反。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# dataset的形式
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)
# dictionary & label的形式
def input_fn(dataset):
# manipulate dataset, extracting the feature dict and the label
return feature_dict, label
# 输出1:训练/测试数据(dictionary),key(str): feature name, value(tensor): feature data;
# 输出2:标签label(tensor):包含所有数据的标签。
2、构造feature column
s
feature column
s是tensorflow的一种描述数据的格式,包含了数据/feature的名称、类型和一些预处理操作,Estimator会根据feature columns的描述来决定如何处理传入的数据(dataset)。- feature columns类型:list,里面元素类型:
tf.feature_column
。1
2
3
4# Feature columns describe how to use the input.
my_feature_columns = []
for feature_key in train_features.keys():
my_feature_columns.append(tf.feature_column.numeric_column(key=feature_key))
3、初始化Pre-made Estimator
- 这一步主要是传入feature columns、以及必要的模型配置信息。
1
2
3
4
5
6
7# 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)
4、模型训练、评估和预测
- 配置完estimator后,之后就是调用train/eval/predict方法来进行训练、评估和预测模型。
input_fn
:这是train/eval/predict三个方法都要传入的参数,要传入输入函数的闭包形式。- 闭包函数的参数没有具体要求,但返回值要求是dataset类型,或者(dict, label) tuple类型。
- 此外对于
input_fn
的输入参数,如果指定了params
和config
这两个参数,则会自动接收Estimator构造函数传入的params
和config
(或Estimator内部构造的params
和config
实例),可以在函数内部直接使用。训练模型
input_fn
参数要求传入是无参的函数句柄,因此使用lambdab表达式来构造;- steps规定了函数训练多少步停止(可以由数据大小、epoch数和batch size算出),在eval过程中不用指定steps。
1
2
3classifier.train(
input_fn=lambda:train_input_fn(features, labels, batch_size),
steps=train_steps)
评估模型
- The eval_result dictionary contains the average_loss (mean loss per sample), the loss (mean loss per mini-batch) and the value of the estimator’s global_step (the number of training iterations it underwent).
1
2
3
4
5
6eval_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))
# output
# Test set accuracy: 0.967
使用模型预测
Estimator.predict()
returns a Python iterable, yielding a dictionary of prediction results for each example.- 而prediction results具体是什么结构则要看模型(model_fn)中是怎么定义的。本例中即为dict类型,key包括
class_ids, probabilities
等,具体情况具体分析。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25expected = ['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:eval_input_fn(predict_x, batch_size=args.batch_size))
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))
# output
# Prediction is "Setosa" (99.6%), expected "Setosa"
# Prediction is "Versicolor" (99.8%), expected "Versicolor"
# Prediction is "Virginica" (97.9%), expected "Virginica"
如何自己构造Estimators
- 自己构造Estimator和Pre-made Estimators的不同就在于:1. model function(其定义了模型的结构和参数);2. metrics & loss 函数(用于评价模型并对模型进行优化)。
- 使用Pre-made Estimators时别人已经帮你实现好了,而自己构造Estimators则需要先自己实现model function,然后再传入Estimator中即可(loss函数一般在model function中实现)。
model function中要实现训练、分析、预测三个部分的代码(可通过if-else分支来实现),然后每个分支都要返回一个
tf.estimator.EstimatorSpec
实例,其是模型的一种评价指标,用来训练模型、分析模型结果。train/eval/predict三个过程对应的EstimatorSpec也不一样,下面来分别分析。初始化Estimator
1
2
3
4
5
6
7tf.estimator.Estimator.__init__(
model_fn,
model_dir=None,
config=None,
params=None,
warm_start_from=None
)model_fn
:Estimator的核心逻辑部分,定义了模型的结构、train/eval/test流程等,model_fn需要传入函数的闭包,且函数的输入输出接口必须遵循固定格式(Estimator内部要与其相互):model_fn(feature, labels, mode, params, config)
,return:tf.estimator.EstimatorSpec
。具体解释见下文。config
:模型配置信息(如多少步打印一次、gou/tpu设置、),类型为tf.estimator.RunConfig
。对于一些特定的Estimator类型(如TPUEstimator),其内部会自己使用这个对象,因此相当于直接传入了模型的配置信息;一般地会直接传入model_fn中,用户可以自己使用。params
:模型的一些超参数设置,没有特定结构,用户可完全自己定义,只用定义成dict
形式即可,key为参数名,value为参数的值(python内置类型)。model_dir
:指定了训练模型的保存目录,也被用来从目录中加载模型到estimator中以继续训练。因为还可通过config.model_dir
来设置,所以一般不用管。- 对于
model_fn
的参数,都是estimatior内部自动传入的,我们只用了解其含义和如何使用即可。- feature:This is batch_features from input_fn
- labels:This is batch_labels from input_fn
- mode:An instance of tf.estimator.ModeKeys
- params:直接从Estimator的构造函数中传入的。
- config:直接从Estimator的构造函数中传入的。
可以直接把所有参数都固定写上?,这样就算Estimator中没有用到也只是传入了None,没什么影响。
1 | def my_model_fn(features, labels, mode, params): |
Estimator的返回值
- model function中要实现train、eval、predict三个部分的代码,一般通过if-else分支来实现,每个分支都要返回一个
tf.estimator.EstimatorSpec
对象,其为模型的评价指标,用来训练模型、分析模型结果。train/eval/predict三个过程对应的EstimatorSpec也不一样,下面来分别分析。 - 何为Spec:Spec是specification的简写,可理解为模型的规范/结果,即定义了模型运行的结果/返回值,通常包含了模型的loss或者metric,以及进一步要做的优化操作(train_op)。
prediction过程的Estimatorspec
对于pred过程,EstimatorSpec要设置的参数有:
mode
:直接返回model_fn的输入的mode
参数即可prediction
:dict
类型,内部是用户自己构造的评估模型的键值对,只用来后期用户自己分析,因此可根据需求随意构造。1
2
3
4
5
6
7
8
9
10
11
12
13
14if mode == tf.estimator.ModeKeys.PREDICT:
pred_dict = {
'class_ids': predicted_classes[:, tf.newaxis],
'probabilities': tf.nn.softmax(logits),
'logits': logits,
}
return tf.estimator.EstimatorSpec(mode, predictions=pred_dict)
# 调用estimator预测函数,会生成一个迭代器
result = estimator.predict(input_fn=predict_input_fn, yield_single_examples=True)
for (i, prediction) in enumerate(result):
# ...
passresult迭代的是单个元素的值,不带batch,因为predict函数的
yield_single_examples
参数默认为True,即把axis0维拆成batch_size个对象,如果axis0不是batch_size的话就会出错(需要改成False)。- batch_siza是在哪传入的?在RunConfig或直接在Estimator中传入的。
eval过程的Estimatorspec
对于train和eval过程,都要包含loss函数。
- 对于eval过程,EstimatorSpec要设置的参数有:
mode
:直接返回model_fn的输入的mode
参数即可;loss
:计算出的loss值(标量形式),返回loss值的原因:存储模型训练数据,便于之后查看模型训练效果;eval_metric_ops
参数:dict,为str-metrics的键值对,metrics是tf.metrics.accuracy()/mean()
等函数的返回值。 - 注:
eval_metric_ops
中的metrics必须是根据tf.metrics.accuracy()/mean()
等函数返回/计算出的对象,因为metrics其实是一个tensor
和update_op
的tuple,具体见tensorflow官方文档:tf.metrics.accuracy。
1 | if mode == tf.estimator.ModeKeys.EVAL: |
train的Estimatorspec
对于train和eval过程,都要包含loss函数。
- 对于train过程,EstimatorSpec要设置的参数有:
mode
:直接返回model_fn的输入的mode
参数即可;loss
:计算出的loss值(标量形式);train_op
:训练操作,estimator会根据其来训练、优化模型。1
2
3
4
5
6if mode == tf.estimator.ModeKeys.TARAIN:
# ...
# 完成loss的计算
optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)
train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())
return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)
Feature Columns介绍
feature column
s是tensorflow的一种描述数据的格式,包含了数据/feature的名称、类型和一些预处理操作,Estimator会根据feature columns的描述来决定如何处理传入的数据(dataset)。- feature columns类型:list,里面的元素类型:
tf.feature_column
。 tf.feature_column
总共有两大类:Dense Column(数值数据)和Categorical Column(类别数据),以及bucketized column(兼具两者的特点)。
类别(前面都有tf.feature_column. ) |
解释 |
---|---|
numeric_column |
数值数据类型, here |
bucketized_column |
将数值先分段,然后映射为独热编码 |
categorical_column_with_identity |
将连续的整数数值映射为独热编码 |
indicator_column |
将单词映射为独热编码 |
embedding_column |
将单词映射为embedding向量 |
tf.feature_column.numeric_column()
1 | tf.feature_column.numeric_column( |
tf.feature_column.bucketized_column
- 分段数值类型,其将numeric_column的数据划分为一定的区间,再按照独热编码的形式表示。
- 如年份数据,按照
<1960, 1960~1980, 1980~2000, >2000
分为四个区间:
年份范围 | 编码 |
---|---|
<1960 | [1, 0, 0, 0] |
[1960, 1980) | [0, 1, 0, 0] |
[1980, 2000)] | [0, 0, 1, 0] |
>=2000 | [0, 0, 0, 1] |
- example code
1
2
3
4
5
6
7# First, convert the raw input to a numeric column.
numeric_feature_column = tf.feature_column.numeric_column("Year")
# Then, bucketize the numeric column on the years 1960, 1980, and 2000.
bucketized_feature_column = tf.feature_column.bucketized_column(
source_column = numeric_feature_column,
boundaries = [1960, 1980, 2000])
tf.feature_column.categorical_column_with_identity
1 | # Create categorical output for an integer feature named "my_feature_b", |
模型保存 checkpoint
- Estimator会自动保存两类文件:
checkpoint
和event file
,前者是保存模型的结构、参数等信息,后者是保存训练期间的变量信息(如loss、global_step等)。 - 构建Estimator时通过传入
model_dir
参数来指定模型保存的路径。 - 模型重新训练时会自动加载最近的一次
checkpoint
文件,因此可以实现模型的继续训练。
Post Date: 2019-07-21
版权声明: 本文为原创文章,转载请注明出处