根据知乎文章 这个 总结而成,方便以后的使用。 主要是为了使用estimator 构建神经网络来处理数
主要流程如下1、将数据处理成tfrecord的形式,2、用tf解析tf_record ,并放到dataset中,3、像操作张量一样来操作dataset。
TensorFlow中层API:Datasets+TFRecord的数据导入
一、使用TFRecord的好处
- 只定义一条数据的处理方式,所有数据都按照这个方式来处理。更加简洁。
- 将数据操作放到图里面去,省去了feed数据的过程。
- 对于大的数据集,可以提速和节约空间。
- 对接estimator
- 一些小操作很方便 尤其是padding
二、TFRecord 简介
是一个字典类型数据{ “age”:15,“preference”:[1,0,1,0], "学历":{“初中”:0,“大学”:1} } 形势很灵活value的值可以是任何维度的值。
(1)使用方法 将数据写入到TFRecord文件中
- 首先建立一个文件读写器:
writer = tf.python_io.TFRecordWriter('%s.tfrecord' %'test')
- 可以写入以下三种的类型
-
int64:
tf.train.Feature(int64_list = tf.train.Int64List(value=输入))
float32:
tf.train.Feature(float_list = tf.train.FloatList(value=输入))
string:
tf.train.Feature(bytes_list=tf.train.BytesList(value=输入))
注:输入必须是list(向量)
-
- 将数据一条一条的写入,注意最后的张量的写入方式,因为只接受list类型,所以把张量转化为list,再加一个shape保存信息。
# 这里我们将会写3个样本,每个样本里有4个feature:标量,向量,矩阵,张量 for i in range(3): # 创建字典 features={} # 写入标量,类型Int64,由于是标量,所以"value=[scalars[i]]" 变成list features['scalar'] = tf.train.Feature(int64_list=tf.train.Int64List(value=[scalars[i]])) # 写入向量,类型float,本身就是list,所以"value=vectors[i]"没有中括号 features['vector'] = tf.train.Feature(float_list = tf.train.FloatList(value=vectors[i])) # 写入矩阵,类型float,本身是矩阵,一种方法是将矩阵flatten成list features['matrix'] = tf.train.Feature(float_list = tf.train.FloatList(value=matrices[i].reshape(-1))) # 然而矩阵的形状信息(2,3)会丢失,需要存储形状信息,随后可转回原形状 features['matrix_shape'] = tf.train.Feature(int64_list = tf.train.Int64List(value=matrices[i].shape)) # 写入张量,类型float,本身是三维张量,另一种方法是转变成字符类型存储,随后再转回原类型 features['tensor'] = tf.train.Feature(bytes_list=tf.train.BytesList(value=[tensors[i].tostring()])) # 存储丢失的形状信息(806,806,3) features['tensor_shape'] = tf.train.Feature(int64_list = tf.train.Int64List(value=tensors[i].shape))
- 转化为相应类型之后 经过下面操作将数据一条一条的写入进去。
# 将存有所有feature的字典送入tf.train.Features中
tf_features = tf.train.Features(feature= features)
#再将其变成一个样本example
tf_example = tf.train.Example(features = tf_features)
# 序列化该样本
tf_serialized = tf_example.SerializeToString()
# 写入一个序列化的样本
writer.write(tf_serialized)
# 由于上面有循环3次,所以到此我们已经写了3个样本
如此这般之后,我们得到了 TFrecord文件 名字叫 test.tfrecord
三、 使用dataset
- dataset 是我们需要用的所有的数据集,从TFrecord种读取得到
filenames = ["test.tfrecord", "test.tfrecord"] dataset = tf.data.TFRecordDataset(filenames)
- 操作dataset,当我们得到从TFrecord中得到的数据后需要解析一下以便使用我们首先构建一个解析函数
-
#首先创建一个解析函数 def parse_function(example_proto): # 只接受一个输入:example_proto,也就是序列化后的样本tf_serialized ===================================================================== #STEP1 :创建样本解析字典 该字典存放着所有feature的解析方式,key为feature名,value为feature的解析方式。 解析方式有两种: 定长特征解析:tf.FixedLenFeature(shape, dtype, default_value) shape:可当reshape来用,如vector的shape从(3,)改动成了(1,3)。 注:如果写入的feature使用了.tostring() 其shape就是() dtype:必须是tf.float32, tf.int64, tf.string中的一种。 default_value:feature值缺失时所指定的值。 不定长特征解析:tf.VarLenFeature(dtype) 注:可以不明确指定shape,但得到的tensor是SparseTensor。 ================================================================================== dics = {# 这里没用default_value,随后的都是None 'scalar': tf.FixedLenFeature(shape=(), dtype=tf.int64, default_value=None), # vector的shape刻意从原本的(3,)指定成(1,3) 'vector': tf.FixedLenFeature(shape=(1,3), dtype=tf.float32), # 使用 VarLenFeature来解析 'matrix': tf.VarLenFeature(dtype=dtype('float32')), 'matrix_shape': tf.FixedLenFeature(shape=(2,), dtype=tf.int64), # tensor在写入时 使用了toString(),shape是() # 但这里的type不是tensor的原type,而是字符化后所用的tf.string,随后再回转成原 tf.uint8类型 'tensor': tf.FixedLenFeature(shape=(), dtype=tf.string), 'tensor_shape': tf.FixedLenFeature(shape=(3,), dtype=tf.int64)} +++++++++++++++++++++++++++++++++++++++++++ STEP 2: 解析样本 +++++++++++++++++++++++++++++++++++++++ # 把序列化样本和解析字典送入函数里得到解析的样本 parsed_example = tf.parse_single_example(example_proto, dics) ==================================== STEP 3: 改变特征 得到的parsed_example也是一个字典,其中每个key是对应feature的名字,value是相应的feature解 析值。如果使用了下面两种情况,则还需要对这些值进行转变。其他情况则不用。 string类型:tf.decode_raw(parsed_feature, type) 来解码 注:这里type必须要和当初.tostring()化前的一致。如tensor转变前是tf.uint8,这里就需是 tf.uint8;转变前是tf.float32,则tf.float32 VarLen解析:由于得到的是SparseTensor,所以视情况需要用 tf.sparse_tensor_to_dense(SparseTensor)来转变成DenseTensor ================================== # 解码字符 parsed_example['tensor'] = tf.decode_raw(parsed_example['tensor'], tf.uint8) # 稀疏表示 转为 密集表示 parsed_example['matrix'] = tf.sparse_tensor_to_dense(parsed_example['matrix']) ==================================== STEP 4: 改变形状 ================================== # 转变matrix形状 parsed_example['matrix'] = tf.reshape(parsed_example['matrix'], parsed_example['matrix_shape']) # 转变tensor形状 parsed_example['tensor'] = tf.reshape(parsed_example['tensor'], parsed_example['tensor_shape']) ==================================== STEP 5 :返回 ================================== return parsed_example
- 执行解析函数 值得注意的是 如果对数据集有特殊的处理可以在解析函数的末尾一起返回。
new_dataset = dataset.map(parse_function)
-
- 再之后就是使用了
#迭代器
iterator = new_dataset.make_one_shot_iterator()
# 获得下一个样本
next_element = iterator.get_next()
# 创建Session
sess = tf.InteractiveSession()
# 获取
i = 1
while True:
# 不断的获得下一个样本
# 获得的值直接属于graph的一部分,所以不再需要用feed_dict来喂
scalar,vector,matrix,tensor = sess.run([next_element['scalar'],
next_element['vector'],
next_element['matrix'],
next_element['tensor']])
shuffle
buffer_size
设置成一个大于你数据集中样本数量的值来确保其充分打乱。如果 数据集很大 可以把数据集组成多个tf_record 这样来打乱。抽空写一下具体的操作方法
shuffle_dataset = new_dataset.shuffle(buffer_size=10000)
iterator = shuffle_dataset.make_one_shot_iterator()
next_element = iterator.get_next()
batch
batch_dataset = shuffle_dataset.batch(4)
iterator = batch_dataset.make_one_shot_iterator()
next_element = iterator.get_next()
padding
padded_shapes指定了内部数据是如何pad的。
- rank数要与元数据对应
- rank中的任何一维被设定成None或-1时都表示将pad到该batch下的最大长度
batch_padding_dataset = new_dataset.padded_batch(4,
padded_shapes={'scalar': [],
'vector': [-1,5],
'matrix': [None,None],
'matrix_shape': [None],
'tensor': [None,None,None],
'tensor_shape': [None]})
iterator = batch_padding_dataset.make_one_shot_iterator()
next_element = iterator.get_next()
Epoch
# num
num_epochs = 2
epoch_dataset = new_dataset.repeat(num_epochs)
iterator = epoch_dataset.make_one_shot_iterator()
next_element = iterator.get_next()