TensorFlow编程人员指南 导入数据.docx
《TensorFlow编程人员指南 导入数据.docx》由会员分享,可在线阅读,更多相关《TensorFlow编程人员指南 导入数据.docx(18页珍藏版)》请在冰点文库上搜索。
![TensorFlow编程人员指南 导入数据.docx](https://file1.bingdoc.com/fileroot1/2023-6/1/74dacb83-7286-4abf-8581-e7c0bf399347/74dacb83-7286-4abf-8581-e7c0bf3993471.gif)
TensorFlow编程人员指南导入数据
导入数据
通过 tf.data API,您可以根据简单的可重用片段构建复杂的输入管道。
例如,图片模型的管道可能会汇聚分布式文件系统中的文件中的数据、对每个图片应用随机扰动,并将随机选择的图片合并成用于训练的批次。
文本模型的管道可能包括从原始文本数据中提取符号、根据对照表将其转换为嵌入标识符,以及将不同长度的序列组合成批次数据。
使用 tf.data API可以轻松处理大量数据、不同的数据格式以及复杂的转换。
tf.data API在TensorFlow中引入了两个新的抽象类:
∙tf.data.Dataset 表示一系列元素,其中每个元素包含一个或多个 Tensor 对象。
例如,在图片管道中,元素可能是单个训练样本,具有一对表示图片数据和标签的张量。
可以通过两种不同的方式来创建数据集:
∙创建来源(例如 Dataset.from_tensor_slices()),以通过一个或多个 tf.Tensor 对象构建数据集。
∙应用转换(例如 Dataset.batch()),以通过一个或多个 tf.data.Dataset 对象构建数据集。
∙tf.data.Iterator 提供了从数据集中提取元素的主要方法。
Iterator.get_next() 返回的操作会在执行时生成 Dataset 的下一个元素,并且此操作通常充当输入管道代码和模型之间的接口。
最简单的迭代器是“单次迭代器”,它与特定的 Dataset 相关联,并对其进行一次迭代。
要实现更复杂的用途,您可以通过 Iterator.initializer 操作使用不同的数据集重新初始化和参数化迭代器,这样一来,您就可以在同一个程序中对训练和验证数据进行多次迭代(举例而言)。
基本机制
本指南的这一部分介绍了创建不同种类的 Dataset 和 Iterator 对象的基础知识,以及如何从这些对象中提取数据。
要启动输入管道,您必须定义来源。
例如,要通过内存中的某些张量构建 Dataset,您可以使用 tf.data.Dataset.from_tensors() 或 tf.data.Dataset.from_tensor_slices()。
或者,如果您的输入数据以推荐的TFRecord格式存储在磁盘上,那么您可以构建 tf.data.TFRecordDataset。
有了 Dataset 对象后,您可以通过链接 tf.data.Dataset 对象上的方法调用将其转换为新的 Dataset。
例如,您可以应用单元素转换,例如 Dataset.map()(为每个元素应用一个函数),也可以应用多元素转换(例如 Dataset.batch())。
要了解转换的完整列表,请参阅 tf.data.Dataset 的文档。
消耗 Dataset 中值的最常见方法是构建迭代器对象。
通过此对象,可以一次访问数据集中的一个元素(例如通过调用 Dataset.make_one_shot_iterator())。
tf.data.Iterator 提供了两个操作:
Iterator.initializer,您可以通过此操作(重新)初始化迭代器的状态;以及 Iterator.get_next(),此操作返回对应于有符号下一个元素的 tf.Tensor 对象。
根据您的使用情形,您可以选择不同类型的迭代器,下文介绍了具体选项。
数据集结构
一个数据集包含多个元素,每个元素的结构都相同。
一个元素包含一个或多个 tf.Tensor 对象,这些对象称为组件。
每个组件都有一个 tf.DType,表示张量中元素的类型;以及一个 tf.TensorShape,表示每个元素(可能部分指定)的静态形状。
您可以通过 Dataset.output_types 和 Dataset.output_shapes 属性检查数据集元素各个组件的推理类型和形状。
这些属性的嵌套结构映射到元素的结构,此元素可以是单个张量、张量元组,也可以是张量的嵌套元组。
例如:
dataset1=tf.data.Dataset.from_tensor_slices(tf.random_uniform([4,10]))
print(dataset1.output_types) #==>"tf.float32"
print(dataset1.output_shapes) #==>"(10,)"
dataset2=tf.data.Dataset.from_tensor_slices(
(tf.random_uniform([4]),
tf.random_uniform([4,100],maxval=100,dtype=tf.int32)))
print(dataset2.output_types) #==>"(tf.float32,tf.int32)"
print(dataset2.output_shapes) #==>"((),(100,))"
dataset3=tf.data.Dataset.zip((dataset1,dataset2))
print(dataset3.output_types) #==>(tf.float32,(tf.float32,tf.int32))
print(dataset3.output_shapes) #==>"(10,((),(100,)))"
为元素的每个组件命名通常会带来便利性,例如,如果它们表示训练样本的不同特征。
除了元组之外,还可以使用 collections.namedtuple 或将字符串映射到张量的字典来表示 Dataset的单个元素。
dataset=tf.data.Dataset.from_tensor_slices(
{"a":
tf.random_uniform([4]),
"b":
tf.random_uniform([4,100],maxval=100,dtype=tf.int32)})
print(dataset.output_types) #==>"{'a':
tf.float32,'b':
tf.int32}"
print(dataset.output_shapes) #==>"{'a':
(),'b':
(100,)}"
Dataset 转换支持任何结构的数据集。
在使用 Dataset.map()、Dataset.flat_map() 和 Dataset.filter() 转换时(这些转换会对每个元素应用一个函数),元素结构决定了函数的参数:
dataset1=dataset1.map(lambdax:
...)
dataset2=dataset2.flat_map(lambdax,y:
...)
#Note:
ArgumentdestructuringisnotavailableinPython3.
dataset3=dataset3.filter(lambdax,(y,z):
...)
创建迭代器
构建了表示输入数据的 Dataset 后,下一步就是创建 Iterator 来访问该数据集中的元素。
tf.data API目前支持下列迭代器,复杂程度逐渐增大:
∙单次,
∙可初始化,
∙可重新初始化,以及
∙可馈送。
单次迭代器是最简单的迭代器形式,仅支持对数据集进行一次迭代,不需要显式初始化。
单次迭代器可以处理基于队列的现有输入管道支持的几乎所有情况,但它们不支持参数化。
以 Dataset.range() 为例:
dataset=tf.data.Dataset.range(100)
iterator=dataset.make_one_shot_iterator()
next_element=iterator.get_next()
foriinrange(100):
value=sess.run(next_element)
asserti==value
注意:
目前,单次迭代器是唯一可轻松与 Estimator 配合使用的类型。
您需要先运行显式 iterator.initializer 操作,然后才能使用可初始化迭代器。
虽然有些不便,但它允许您使用一个或多个 tf.placeholder() 张量(可在初始化迭代器时馈送)参数化数据集的定义。
继续以 Dataset.range() 为例:
max_value=tf.placeholder(tf.int64,shape=[])
dataset=tf.data.Dataset.range(max_value)
iterator=dataset.make_initializable_iterator()
next_element=iterator.get_next()
#Initializeaniteratoroveradatasetwith10elements.
sess.run(iterator.initializer,feed_dict={max_value:
10})
foriinrange(10):
value=sess.run(next_element)
asserti==value
#Initializethesameiteratoroveradatasetwith100elements.
sess.run(iterator.initializer,feed_dict={max_value:
100})
foriinrange(100):
value=sess.run(next_element)
asserti==value
可重新初始化迭代器可以通过多个不同的 Dataset 对象进行初始化。
例如,您可能有一个训练输入管道,它会对输入图片进行随机扰动来改善泛化;还有一个验证输入管道,它会评估对未修改数据的预测。
这些管道通常会使用不同的 Dataset 对象,这些对象具有相同的结构(即每个组件具有相同类型和兼容形状)。
#Definetrainingandvalidationdatasetswiththesamestructure.
training_dataset=tf.data.Dataset.range(100).map(
lambdax:
x+tf.random_uniform([],-10,10,tf.int64))
validation_dataset=tf.data.Dataset.range(50)
#Areinitializableiteratorisdefinedbyitsstructure.Wecouldusethe
#`output_types`and`output_shapes`propertiesofeither`training_dataset`
#or`validation_dataset`here,becausetheyarecompatible.
iterator=tf.data.Iterator.from_structure(training_dataset.output_types,
training_dataset.output_shapes)
next_element=iterator.get_next()
training_init_op=iterator.make_initializer(training_dataset)
validation_init_op=iterator.make_initializer(validation_dataset)
#Run20epochsinwhichthetrainingdatasetistraversed,followedbythe
#validationdataset.
for_inrange(20):
#Initializeaniteratoroverthetrainingdataset.
sess.run(training_init_op)
for_inrange(100):
sess.run(next_element)
#Initializeaniteratoroverthevalidationdataset.
sess.run(validation_init_op)
for_inrange(50):
sess.run(next_element)
可馈送迭代器可以与 tf.placeholder 一起使用,通过熟悉的 feed_dict 机制选择每次调用 tf.Session.run 时所使用的 Iterator。
它提供的功能与可重新初始化迭代器的相同,但在迭代器之间切换时不需要从数据集的开头初始化迭代器。
例如,以上面的同一训练和验证数据集为例,您可以使用 tf.data.Iterator.from_string_handle 定义一个可让您在两个数据集之间切换的可馈送迭代器:
#Definetrainingandvalidationdatasetswiththesamestructure.
training_dataset=tf.data.Dataset.range(100).map(
lambdax:
x+tf.random_uniform([],-10,10,tf.int64)).repeat()
validation_dataset=tf.data.Dataset.range(50)
#Afeedableiteratorisdefinedbyahandleplaceholderanditsstructure.We
#couldusethe`output_types`and`output_shapes`propertiesofeither
#`training_dataset`or`validation_dataset`here,becausetheyhave
#identicalstructure.
handle=tf.placeholder(tf.string,shape=[])
iterator=tf.data.Iterator.from_string_handle(
handle,training_dataset.output_types,training_dataset.output_shapes)
next_element=iterator.get_next()
#Youcanusefeedableiteratorswithavarietyofdifferentkindsofiterator
#(suchasone-shotandinitializableiterators).
training_iterator=training_dataset.make_one_shot_iterator()
validation_iterator=validation_dataset.make_initializable_iterator()
#The`Iterator.string_handle()`methodreturnsatensorthatcanbeevaluated
#andusedtofeedthe`handle`placeholder.
training_handle=sess.run(training_iterator.string_handle())
validation_handle=sess.run(validation_iterator.string_handle())
#Loopforever,alternatingbetweentrainingandvalidation.
whileTrue:
#Run200stepsusingthetrainingdataset.Notethatthetrainingdatasetis
#infinite,andweresumefromwhereweleftoffintheprevious`while`loop
#iteration.
for_inrange(200):
sess.run(next_element,feed_dict={handle:
training_handle})
#Runonepassoverthevalidationdataset.
sess.run(validation_iterator.initializer)
for_inrange(50):
sess.run(next_element,feed_dict={handle:
validation_handle})
消耗迭代器中的值
Iterator.get_next() 方法返回一个或多个 tf.Tensor 对象,这些对象对应于迭代器有符号的下一个元素。
每次评估这些张量时,它们都会获取底层数据集中下一个元素的值。
(请注意,与TensorFlow中的其他有状态对象一样,调用 Iterator.get_next() 并不会立即使迭代器进入下个状态。
您必须使用TensorFlow表达式中返回的 tf.Tensor 对象,并将该表达式的结果传递到 tf.Session.run(),以获取后续元素并使迭代器进入下个状态。
)
如果迭代器到达数据集的末尾,则执行 Iterator.get_next() 操作会产生 tf.errors.OutOfRangeError。
在此之后,迭代器将处于不可用状态;如果需要继续使用,则必须对其重新初始化。
dataset=tf.data.Dataset.range(5)
iterator=dataset.make_initializable_iterator()
next_element=iterator.get_next()
#Typically`result`willbetheoutputofamodel,oranoptimizer's
#trainingoperation.
result=tf.add(next_element,next_element)
sess.run(iterator.initializer)
print(sess.run(result)) #==>"0"
print(sess.run(result)) #==>"2"
print(sess.run(result)) #==>"4"
print(sess.run(result)) #==>"6"
print(sess.run(result)) #==>"8"
try:
sess.run(result)
excepttf.errors.OutOfRangeError:
print("Endofdataset") #==>"Endofdataset"
一种常见模式是将“训练循环”封装在 try-except 块中:
sess.run(iterator.initializer)
whileTrue:
try:
sess.run(result)
excepttf.errors.OutOfRangeError:
break
如果数据集的每个元素都具有嵌套结构,则 Iterator.get_next() 的返回值将是一个或多个 tf.Tensor 对象,这些对象具有相同的嵌套结构:
dataset1=tf.data.Dataset.from_tensor_slices(tf.random_uniform([4,10]))
dataset2=tf.data.Dataset.from_tensor_slices((tf.random_uniform([4]),tf.random_uniform([4,100])))
dataset3=tf.data.Dataset.zip((dataset1,dataset2))
iterator=dataset3.make_initializable_iterator()
sess.run(iterator.initializer)
next1,(next2,next3)=iterator.get_next()
请注意,next1、next2 和 next3 是由同一个操作/节点(通过 Iterator.get_next() 创建)生成的张量。
因此,评估其中任何一个张量都会使所有组件的迭代器进入下个状态。
典型的迭代器消耗方会在一个表达式中包含所有组件。
保存迭代器状态
tf.contrib.data.make_saveable_from_iterator 函数通过迭代器创建一个 SaveableObject,该对象可用于保存和恢复迭代器(实际上是整个输入管道)的当前状态。
这样创建的可保存对象可以添加到 tf.train.Saver 变量列表或 tf.GraphKeys.SAVEABLE_OBJECTS 集合,以便采用与 tf.Variable 相同的方式进行保存和恢复。
请参阅保存和恢复,详细了解如何保存和恢复变量。
#Createsaveableobjectfromiterator.
saveable=tf.contrib.data.make_saveable_from_iterator(iterator)
#Savetheiteratorstatebyaddingittothesaveableobjectscollection.
tf.add_to_collection(tf.GraphKeys.SAVEABLE_OBJECTS,saveable)
saver=tf.train.Saver()
withtf.Session()assess:
ifshould_checkpoint:
saver.save(path_to_checkpoint)
#Restoretheiteratorstate.
withtf.Session()assess:
saver.restore(sess,path_to_checkpoint)
读取输入数据
消耗NumPy数组
如果您的所有输入数据都适合存储在内存中,则根据输入数据创建 Dataset 的最简单方法是将它们转换为 tf.Tensor 对象,并使用 Dataset.from_tensor_slice