一、TFRecord数据处理的优点
在数据集较小时,我们会把数据全部加载到内存里方便快速导入,但当数据量超过内存大小时,就只能放在硬盘上来一点点读取,这时就不得不考虑数据的移动、读取、处理等速度。使用TFRecord就是为了提速和节约空间的。
二、将数据写入TFRecord 文件
- 首先创建一个writer,也就是我们的TFrecord生成器
writer = tf.python_io.TFRecordWriter(record_path)
record_path:生成的TFrecord文件路径
- 创建存储类型tf_feature
往.tfrecord
里面写数据的时候首先要先定义写入数据项(feature)的类型。
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(向量),由于tensorflow feature类型只接受list数据,但是如果数据 类型是矩阵或者张量的时候,有两种解决方法:
转成list
类型:将张量fatten
成list
(也就是向量),再用写入list
的方式写入 2、转成string
类型:将张量用.tostring()
转换成string
类型,再用tf.train.Feature(bytes_list=tf.train.BytesList(value=[input.tostring()]))
来存储。
形状信息:不管那种方式都会使数据丢失形状信息,所以在向该样本中写入feature时应该额外加入shape信息作为额外feature。shape信息是int类型,这里我是用原feature名字+'_shape'来指定shape信息的feature名
- 将 tf_feature 转换成 tf_example 以及进行序列化
将各种Feature创建成一个字典并送入tf.train.Features中,在将其变成一个样本example
tf_example = tf.train.Example(features = tf.train.Features(feature={
key1:tf.train.Feature(int64_list = tf.train.Int64List(value=输入))
key2:tf.train.Feature(float_list = tf.train.FloatList(value=输入))
key3:tf.train.Feature(bytes_list=tf.train.BytesList(value=输入))}))
# 序列化该样本
tf_serialized = tf_example.SerializeToString()
# 关闭文件
writer.close()
- 实例
import matplotlib.image as mpimg
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
#标量
scalars = np.array([1,2,3],dtype=np.int64)
#向量
vectors = np.array([[0.1,0.1,0.1],
[0.2,0.2,0.2],
[0.3,0.3,0.3]],dtype=np.float32)
#矩阵
matrices = np.array([np.array((vectors[0],vectors[0])),
np.array((vectors[1],vectors[1])),
np.array((vectors[2],vectors[2]))],dtype=np.float32)
#张量
img=mpimg.imread('/home/leiwei/桌面/flower.jpeg')
tensors = np.array([img,img,img])
print(scalars)
print('*'*50)
print(vectors)
print('*'*50)
print(matrices)
print('*'*50)
print(tensors)
writer = tf.python_io.TFRecordWriter('test.tfrecord')
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()]))
# 存储丢失的形状信息
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个样本
# 关闭文件
writer.close()
![83216ddf871498d4d403e8c10edd5cdd.png](https://i-blog.csdnimg.cn/blog_migrate/f258b94f7b168c0568ba578521d03e77.png)
三、使用Dataset读取数据
1、从TFRecord文件导入
# 从多个tfrecord文件中导入数据到Dataset类 (这里用两个一样)
filenames = ["test.tfrecord", "test.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
2、解析feature信息
解析基本就是写入时的逆过程,所以会需要写入时的信息,这里先列出刚才写入时,所有feature的各项信息。
注:用到了pandas,没有的请pip install pandas。
data_info = pd.DataFrame({'name':['scalar','vector','matrix','matrix_shape','tensor','tensor_shape'],
'type':[scalars[0].dtype,vectors[0].dtype,matrices[0].dtype,tf.int64, tensors[0].dtype,tf.int64],
'shape':[scalars[0].shape,(3,),matrices[0].shape,(len(matrices[0].shape),),tensors[0].shape,(len(tensors[0].shape),)],
'isbyte':[False,False,True,False,False,False],
'length_type':['fixed','fixed','var','fixed','fixed','fixed']},
columns=['name','type','shape','isbyte','length_type','default'])
print(data_info)
![26012a3edf942de1fffb9aed711f4e09.png](https://i-blog.csdnimg.cn/blog_migrate/f276947d9cda980497dbcfe68f5c6715.png)
有6个信息,name, type, shape, isbyte, length_type, default。前3个好懂,这里额外说明后3个:
- isbyte:是用于记录该feature是否字符化了。
- default:是当所读的样本中该feature值缺失用什么填补,这里并没有使用,所以全部都是np.NaN
- length_type:是指示读取向量的方式是否定长,之后详细说明。
注:这里的信息都是在写入时数据的原始信息。但是为了展示某些特性,这里做了改动:
- 把vector的shape从(3,)改动成了(1,3)
- 把matrix的length_type改成了var(不定长)
2.1、创建解析函数
def parse_function(example_proto):
# 只接受一个输入:example_proto,也就是序列化后的样本tf_serialized
Step 1. 创建样本解析字典
该字典存放着所有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=tf.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. 改变形状
到此为止得到的特征都是向量,需要根据之前存储的shape信息对每个feature进行reshape。
# 转变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. 返回样本
现在样本中的所有feature都被正确设定了。可以根据需求将不同的feature进行拆分合并等处理,得到想要的输入
# 返回所有feature
return parsed_example
2.2、执行解析函数
创建好解析函数后,将创建的parse_function送入dataset.map()得到新的数据集
new_dataset = dataset.map(parse_function)
3、创建迭代器
有了解析过的数据集后,接下来就是获取当中的样本。
# 创建获取数据集中样本的迭代器
iterator = new_dataset.make_one_shot_iterator()