新媒体管家
图:pixabay
原文来源:Google Developers Blog
作者:TensorFlow团队
「机器人圈」编译:嗯~阿童木呀、多啦A亮
在TensorFlow 1.3版本里面有两个重要的特征,你应该好好尝试一下:
•数据集(Datasets):一种创建输入流水线的全新方法(即将数据读取到程序中)。
•评估器(Estimator):一种创建TensorFlow模型的高级方法。评估器包括用于常见机器学习任务的预制模型,当然,你也可以使用它们来创建你的自定义模型。
接下来你将看到它们如何是如何适应TensorFlow架构的。如果将它们结合起来,它们将提供了一种创建TensorFlow模型并向其馈送数据的简单方法:
我们的示例模型
为了能够更好地对这些特征进行深一步探索,我们将构建一个模型并展示相关代码片段。点击此处链接,你将获得完整代码资源(https://github.com/mhyttsten/Misc/blob/master/Blog_Estimators_DataSet.py),其中包含关于训练和测试文件的说明。有一点需要注意的是,此代码只是为了演示数据集和评估器在功能方面的有效性,因此并未针对最大性能进行优化。
一个经过训练的模型根据四种植物特征(萼片长度、萼片宽度、花瓣长度和花瓣宽度)对鸢尾花进行分类。因此,在推理过程中,你可以为这四个特征提供值,并且该模型将预测出该花是以下三种美丽的变体之一:
从左至右:Iris setosa(山鸢尾,Radomil,CC BY-SA 3.0),Iris versicolor(杂色鸢尾,Dlanglois,CC BY-SA 3.0)和Iris virginica(维吉尼亚鸢尾,Frank Mayfield,CC BY-SA 2.0)。
我们将用下面的结构对一个深度神经网络分类器进行训练。所有输入和输出值都将为float32,输出值的和为1(正如我们所预测的每个单独鸢尾花类型的概率):
例如,一个输出结果是Iris Setosa(山鸢尾)的概率为0.05,是Iris Versicolor(杂色鸢尾)的概率为0.9,是Iris Virginica(维吉尼亚鸢尾)的概率为0.05,这表明该花是Iris Versicolor(杂色鸢尾)的概率为90%。
好的!既然我们已经定义了这个模型,接下来就看一下该如何使用Datasets(数据集)和Estimator(评估器)对其进行训练并做出预测。
Datasets(数据集)的简介
Dataset是一种为TensorFlow模型创建输入流水线的新方式。相较于使用feed_dict或基于队列的流水线,这个API要好用得多,而且它更干净,更易于使用。虽然在1.3版本中,Datasets仍然位于tf.contrib.data中,但我们希望将该API移动到1.4版本中,所以现在是对其进行测试驱动器的时候了。
在高级别中,Dataset涵盖以下几级:
其中:
•Dataset:包含创建和转换数据集方法的基类。还使得你能够对内存中或来自Python生成器的数据初始化数据集。
•TextLineDataset:从文本文件中读取行。
•TFRecordDataset:读取TFRecord文件中的记录。
•FixedLengthRecordDataset:从二进制文件读取固定大小的记录。
•Iterator(迭代器):提供一种一次访问一个数据集元素的方法。
我们的数据集
首先,我们先来看看那些将用来馈送模型的数据集。我们将从CSV文件中读取数据,其中每行将包含五个值——四个输入值以及标签:
标签将是:
0为Iris Setosa(山鸢尾);
1为Versicolor(杂色鸢尾);
2为Virginica(维吉尼亚鸢尾)
表征数据集
为了描述我们的数据集,我们首先创建一个关于特征的列表:
feature_names = [
'SepalLength',
'SepalWidth',
'PetalLength',
'PetalWidth']
当训练模型时,我们需要一个读取输入文件并返回特征和标签数据的函数。Estimators(评估器)要求你按照以下格式创建一个函数:
def input_fn():
...<code>...
return ({ 'SepalLength':[values], ..<etc>.., 'PetalWidth':[values] },
[IrisFlowerType])
返回值必须是一个双元素元组,其组织如下:
•第一个元素必须是一个dict(命令),其中每个输入特征都是一个键,然后是训练批量的值列表。
•第二个元素是训练批量的标签列表。
由于我们返回了一批输入特征和训练标签,所以这意味着返回语句中的所有列表将具有相同的长度。从技术上说,每当我们在这里提到“列表”时,实际上指的是一个1-d TensorFlow张量。
为了使得能够重用input_fn,我们将添加一些参数。从而使得我们能够用不同的设置构建输入函数。这些配置是很简单的:
file_path:要读取的数据文件。
perform_shuffle:记录顺序是否应该是随机的。
repeat_count:迭代数据集中记录的次数。例如,如果我们指定1,则每个记录将被读取一次。如果我们指定None,则迭代将永远持续下去。
以下是使用Dataset API实现此函数的方法。我们将把它封装在一个“输入函数”中,它将与我们馈送评估器模型相适应。
def my_input_fn(file_path, perform_shuffle=False, repeat_count=1):
def decode_csv(line):
parsed_line = tf.decode_csv(line, [[0.], [0.], [0.], [0.], [0]])
label = parsed_line[-1:] # Last element is the label
del parsed_line[-1] # Delete last element
features = parsed_line # Everything (but last element) are the features
d = dict(zip(feature_names, features)), label return d
dataset = (tf.contrib.data.TextLineDataset(file_path) # Read text file
.skip(1) # Skip header row
.map(decode_csv)) # Transform each elem by applying decode_csv fn
if perform_shuffle:
# Randomizes input using a window of 256 elements (read into memory)
dataset = dataset.shuffle(buffer_size=256)
dataset = dataset.repeat(repeat_count) # Repeats dataset this # times
dataset = dataset.batch(32) # Batch size to use
iterator = dataset.make_one_shot_iterator()
batch_features, batch_labels = iterator.get_next()
return batch_features, batch_labels
请注意以下事项:
TextLineDataset:当你使用其基于文件的数据集时,Dataset API将为你处理大量的内存管理。例如,你可以通过指定列表作为参数,读取比内存大得多的数据集文件或读入多个文件。
Shuffle(随机化):读取buffer_size记录,然后shuffle(随机化)其顺序。
Map(映射):将数据集中的每个元素调用decode_csv函数,作为参数(因为我们使用的是TextLineDataset,每个元素都将是一行CSV文本)。然后我们将decode_csv应用于每一行。
decode_csv:将每行拆分为字段,如有必要,提供默认值。然后返回一个带有字段键和字段值的dict(命令)。映射函数使用dict更新数据集中的每个elem(行)。
当然,以上只是对Datasets的粗略介绍!接下来,我们可以使用此函数打印第一个批次:
next_batch = my_input_fn(FILE, True) # Will return 32 random elements# Now let's try it out, retrieving and printing one batch of data.# Although this code looks strange, you don't need to understand# the details.with tf.Session() as sess:
first_batch = sess.run(next_batch)print(first_batch)# Output({'SepalLength': array([ 5.4000001, ...<repeat to 32 elems>], dtype=float32),
'PetalWidth': array([ 0.40000001, ...<repeat to 32 elems>], dtype=float32),
...
},
[array([[2], ...<repeat to 32 elems>], dtype=int32) # Labels)
实际上,我们需要从Dataset API中实现我们的模型。Datasets具有更多的功能,详情请看这篇文章的结尾,我们收集了更多的资源。
Estimators(评估器)的介绍
Estimator是一种高级API,在训练TensorFlow模型时,它可以减少以前需要编写的大量样板代码。Estimator也非常灵活,如果你对模型有特定要求,它使得你能够覆盖其默认行为。
下面介绍两种可能的方法,你可以用来用Estimator构建模型:
•Pre-made Estimator(预制评估器)——这些是预定义的评估器,用于生成特定类型的模型。在这篇文章中,我们将使用DNNClassifier预制评估器。
•Estimator(基础级别)——通过使用model_fn函数,你可以完全控制如何创建模型。我们将在另一篇文章中对其详细介绍。
以下是评估器的类图:
我们希望在将来的版本中添加更多的预制评估器。
你可以看到,所有的评估器都使用input_fn来提供输入数据。在我们的示例中,我们将重用我们为此定义的my_input_fn。
以下代码实例化了预测鸢尾花类型的评估器:
# Create the feature_columns, which specifies the input to our model.# All our input features are numeric, so use numeric_column for each one.feature_columns = [tf.feature_column.numeric_column(k) for k in feature_names]# Create a deep neural network regression classifier.# Use the DNNClassifier pre-made estimatorclassifier = tf.estimator.DNNClassifier(
feature_columns=feature_columns, # The input features to our model
hidden_units=[10, 10], # Two layers, each with 10 neurons
n_classes=3,
model_dir=PATH) # Path to where checkpoints etc are stored
我们现在有一个评估器,我们可以开始训练了。
训练模型
使用单行TensorFlow代码进行训练:
# Train our model, use the previously function my_input_fn# Input to training is a file with training example# Stop training after 8 iterations of train data (epochs)classifier.train(
input_fn=lambda: my_input_fn(FILE_TRAIN, True, 8))
但等一下... "lambda: my_input_fn(FILE_TRAIN, True, 8)"这是什么东西?这就是我们用评估器连接数据集的地方!评估器需要数据来执行训练、评估和预测,并且使用input_fn来获取数据。评估器需要一个没有参数的input_fn,所以我们使用lambda创建一个没有参数的函数,它使用所需的参数调用input_fn:file_path、shuffle setting和repeat_count。在我们的示例中,我们使用my_input_fn,传递它:
•FILE_TRAIN,它是训练数据文件。
•True,这告诉评估器shuffle数据。
•8,它告诉评估器并重复数据集8次。
评估我们训练过的模型
好的,现在我们有一个训练过的模型。我们如何评估它的表现呢?幸运的是,每个评估器都包含一个评估方法:
# Evaluate our model using the examples contained in FILE_TEST# Return value will contain evaluation_metrics such as: loss & average_lossevaluate_result = estimator.evaluate(
input_fn=lambda: my_input_fn(FILE_TEST, False, 4)print("Evaluation results")for key in evaluate_result:
print(" {}, was: {}".format(key, evaluate_result[key]))
在我们的示例中,准确度能达到93%。当然有各种各样的方式来提高这个准确性。一种方法是一遍又一遍地运行程序。由于模型的状态是持久的(在上面的model_dir = PATH中),模型将会改进你对其进行的迭代次数的更改,直到它稳定为止。另一种方法是调整隐藏层数或每个隐藏层中的节点数。随意尝试一下,但请注意,当你进行更改时,你需要删除model_dir = PATH中指定的目录,因为你正在更改DNNClassifier的结构。
使用我们训练过模型进行预测
就是这样!我们现在有一个训练过的模型,如果我们对评估结果感到满意,我们可以使用它来基于一些输入来预测鸢尾花。与训练和评估一样,我们使用单个函数调用进行预测:
# Predict the type of some Iris flowers.# Let's predict the examples in FILE_TEST, repeat only once.predict_results = classifier.predict(
input_fn=lambda: my_input_fn(FILE_TEST, False, 1))print("Predictions on test file")for prediction in predict_results:
# Will print the predicted class, i.e: 0, 1, or 2 if the prediction
# is Iris Sentosa, Vericolor, Virginica, respectively.
print prediction["class_ids"][0]
在内存中对数据进行预测
前面的代码指定了FILE_TEST以对存储在文件中的数据进行预测,但是我们如何对驻留在其他来源的数据进行预测,例如在内存中?你可能会猜到,这并不需要改变我们的预测调用。相反,我们将Dataset API配置为使用记忆结构,如下所示:
# Let create a memory dataset for prediction.# We've taken the first 3 examples in FILE_TEST.prediction_input = [[5.9, 3.0, 4.2, 1.5], # -> 1, Iris Versicolor
[6.9, 3.1, 5.4, 2.1], # -> 2, Iris Virginica
[5.1, 3.3, 1.7, 0.5]] # -> 0, Iris Sentosadef new_input_fn():
def decode(x):
x = tf.split(x, 4) # Need to split into our 4 features
# When predicting, we don't need (or have) any labels
return dict(zip(feature_names, x)) # Then build a dict from them
# The from_tensor_slices function will use a memory structure as input
dataset = tf.contrib.data.Dataset.from_tensor_slices(prediction_input)
dataset = dataset.map(decode)
iterator = dataset.make_one_shot_iterator()
next_feature_batch = iterator.get_next()
return next_feature_batch, None # In prediction, we have no labels# Predict all our prediction_inputpredict_results = classifier.predict(input_fn=new_input_fn)# Print resultsprint("Predictions on memory data")for idx, prediction in enumerate(predict_results):
type = prediction["class_ids"][0] # Get the predicted class (index)
if type == 0:
print("I think: {}, is Iris Sentosa".format(prediction_input[idx]))
elif type == 1:
print("I think: {}, is Iris Versicolor".format(prediction_input[idx]))
else:
print("I think: {}, is Iris Virginica".format(prediction_input[idx])
Dataset.from_tensor_slides()专为适合内存的小型数据集而设计。当我们使用TextLineDataset进行训练和评估时,你可以拥有任意大的文件,只要你的内存可以管理随机缓冲区和批量大小。
使用像DNNClassifier这样的预制评估器提供了很多价值。除了易于使用,预制评估器还提供内置的评估指标,并创建可在TensorBoard中看到的概要。要查看此报告,请从你的命令行启动TensorBoard,如下所示:
# Replace PATH with the actual path passed as model_dir argument when the# DNNRegressor estimator was created.tensorboard --logdir=PATH
下面的图显示了一些tensorboard将提供数据:
概要
在这篇文章中,我们探讨了数据集和评估器。这些是用于定义输入数据流和创建模型的重要API,因此投入时间来学习它们是绝对值得的!
有关更多详情,请务必查看:
•此文中使用的完整源代码可在此处获取。(https://goo.gl/PdGCRx)
•Josh Gordon的Jupyter notebook的出色使用。(https://github.com/tensorflow/workshops/blob/master/notebooks/07_structured_data.ipynb)使用Jupyter notebook,你将学习如何运行一个更广泛的例子,其具有许多不同类型的特征(输入)。从我们的模型来看,我们只使用了数值特征。
•有关数据集,请参阅程序员指南(https://www.tensorflow.org/programmers_guide/datasets)和参考文档(https://www.tensorflow.org/api_docs/python/tf/contrib/data)中的新章节。
•有关评估器,请参阅程序员指南(https://www.tensorflow.org/programmers_guide/estimators)和参考文档(https://www.tensorflow.org/versions/master/api_docs/python/tf/estimator)中的新章节。
回复「转载」获得授权,微信搜索「ROBO_AI」关注公众号
中国人工智能产业创新联盟在京成立 近200家成员单位共推AI发展
点击下图加入联盟
关注“机器人圈”后不要忘记置顶哟
我们还在搜狐新闻、机器人圈官网、腾讯新闻、网易新闻、一点资讯、天天快报、今日头条……
↓↓↓点击阅读原文查看中国人工智能产业创新联盟手册