查看原文
其他

TensorFlow的tfrecords文件详解

草yang年华 机器学习与python集中营 2021-09-10


进入正文

全文摘要

当前是数据爆炸的时代,深度学习与大数据更是相辅相成,在使用TensorFlow构建深度学习模型的时候,可能会涉及到海量的数据,可能会用到数G、T甚至P级别的训练数据,很显然,要将如此庞大的数据一次性加载进内存,显然当前的硬件条件还远远不能够。幸好TensorFlow也提供了非常有好的大数据处理方式。


一、TF数据读取的方式



TF数据读取的方式数据读取方式

1.1


   

对于深度学习而言,因为数据量庞大,在提高运算能力的同时,更高效的处理数据I/O对于提高整体的性能也非常重要。在使用TensorFlow训练模型的时候,有三种数据加载的方式

(1)使用Python代码为TensorFlow提供数据

(2)预先加载数据,将需要训练的数据以变量的形式预先保存在计算机的内存中

(3)利用管道从文件中读取数据

   

对于数据较小的情况,直接将数据加载到计算机内存,然后每次取一个batch放进网络里面加以训练,问题,但是对于大数据而言,一方面如果直接全部将数据放进内存肯定不可能;另一方面,我可以每次需要多少数据就从硬盘中读取,但是这样做的后果就是频繁的I/O操作,使得执行效率大打折扣。


TF数据读取方式小数据的常用数据格式

1.2


对于比较小的数据,我们可以直接加载进内存,对于这种级别的数量,常用的一些数据格式有以下几种:

CSV格式;

npy  npz格式:这是numpy的数据保存格式

pkl: 这是python的序列化保存格式

hdf: 以HDF5为最新的系列


TF数据读取方式大数据的专用数据格式

1.3


   

对于大数据而言,TensorFlow推荐使用自带的tfrcords文件。tfrecords文件是以二进制进行存储的,适合以串行的方式读取大批量的数据。

对于训练数据而言,我们可以编写程序将普通的训练数据保存为tfrecords数据格式。



本节总结

本节主要介绍TensorFlow常用的一些数据格式

Next


二、tfrecords文件的创建

tfrecords文件的创建创建思路及步骤

2.1


   

tfrecords的创建很简单,就是将每一组“样本数据”组装成一个Example对象,这个对象是遵循protocol buffer协议的;然后将这个Example对象序列化成字符串;最后用tf.python_io.TFRecordWriter写入相应的tfrecords文件即可。大致步骤如下:

第一步:获取原始数据,一般使用numpy或者是pandas进行一些处理

第二步:使用tf.python_io.TFRecordWriter类定义一个tfrecords文件

第三步:将每一条样本数据按照相应的特征组织好,即将样本数据组织成Example的过程,这是整个操作流程的核心部分,相对较复杂

第四步:将组织好的Example写入进tfrecords文件,并关闭tfrecords文件即可

下面以titanic数据为例加以说明:因为titanic数据是一个CSV文件,里面有不少空余的,本文只选择前面的50条数据,并且已经填充了空格(数据的预处理)


tfrecords文件的创建titanic实例

2.2


import tensorflow as tf
import pandas as pd

#第一步:获取原始数据
data=pd.read_csv('Titanic dataset/titanic_train_01.csv')
print(data.shape)

#第二步:定义record文件
tfrecord_file='titanic_train.tfrecords'
writer=tf.python_io.TFRecordWriter(tfrecord_file)

#第三步:每一次写入一条样本记录
for i in range(len(data)):
    features=tf.train.Features(feature={'Age':tf.train.Feature(float_list=tf.train.FloatList(value=[data['age'][i]])),
                                         'Sex':tf.train.Feature(int64_list=tf.train.Int64List(value=[1 if data['sex'][i]=='male' else 0])),
                                         'Pclass':tf.train.Feature(int64_list=tf.train.Int64List(value=[data['pclass'][i]])),
                                         'Parch':tf.train.Feature(int64_list=tf.train.Int64List(value=[data['parch'][i]])),
                                         'Sibsp':tf.train.Feature(int64_list=tf.train.Int64List(value=[data['sibsp'][i]])),
                                         'Fare':tf.train.Feature(float_list=tf.train.FloatList(value=[data['fare'][i]])),
                                         'Survived':tf.train.Feature(int64_list=tf.train.Int64List(value=[data['survived'][i]]))
                                         })
    #每一条样本的特征,将一系列特征组织成一条样本
    example=tf.train.Example(features=features)
    #将每一条样本写入到tfrecord文件
    writer.write(example.SerializeToString())

#第四步:写入后关闭文件
writer.close()
print('写入tfrecords文件完毕!')


   

核心函数解析:

(1)Features()

features=tf.train.Features(feature={*****})

该函数传入一个关键字参数feature,表示的是一系列的特征。

(2)Fearture()

'Age':tf.train.Feature(float_list=tf.train.FloatList(value=[data['age'][i]]))

该函数是对应于一系列特征中的每一个特征,它有三个可选的关键字参数,float_list、int64_list、byteslist分别对应于取值为浮点数的特征、整数的特征、二进制数的特征。

(3)FloatList()、Int64List()、BytesList()

float_list=tf.train.FloatList(value=[data['age'][i]]))

这三个函数是将每个特征进行转化的函数,分别对应特征的取值为浮点数、整数、二进制数。这里有一个注意事项,这三个函数都有一个命名参数value,这个参数的的赋值一定要使用value=【data】的方式,这里的中括号不能丢哦!

(4)Example()

example=tf.train.Example(features=features)

这个函数就是核心,是将上面组织好的一系列特征进行包装,包装成一个Example对象,然后将该对象写入到tfrecords文件,关闭该文件即可。


   

Example补充:

使用TFRecord时,一般以tf.train.Exampletf.train.SequenceExample作为基本单位来进行数据读取。

(1)Example()

example=tf.train.Example(features=features)

tf.train.Example一般用于数值、图像等有固定大小的数据,同时使用tf.train.Feature指定每个记录各特征的名称和数据类型,用法如下:

tf.train.Example(features=tf.train.Features(feature={
    'height': tf.train.Feature(int64_list=tf.train.Int64List(value=[height])),
    'width' : tf.train.Feature(int64_list=tf.train.Int64List(value=[width])),
    'depth' : tf.train.Feature(int64_list=tf.train.Int64List(value=[depth])),
    'image' : tf.train.Feature(bytes_list=tf.train.BytesList(value=[image]))
}))



(2)SequenceExample()

tf.train.SequenceExample一般用于文本、时间序列等没有固定长度大小的数据,用法如下:


example = tf.train.SequenceExample()
# 通过context来指定数据量的大小
example.context.feature["length"].int64_list.value.append(len(data))

# 通过feature_lists来加载数据
words_list = example.feature_lists.feature_list["words"]
for word in words:
    words_list.feature.add().int64_list.value.append(word_id(word))



tfrecords文件的创建mnist实例

2.3


   

上面是对titanic的CSV文件进行的操作,那如果是图像数据呢,难道是对图像的每一个像素进行Feature的转换吗?那么当一个图片很大的时候,像素很多,这样显然不合理,对于图像的操作这里就关键用到了BytesList去实现,下面以图像为例加以说明

本文没有选择很多的图片,仅仅以三张图片为例,在百度图片下载任意三张图片,因为大小不一样,使用Photoshop将三张图片简单更改为500x500大小。

import tensorflow as tf
import pandas as pd
from PIL import Image
import numpy as np

#第一步:获取原始数据,此处为原始图像
img1=Image.open('picture dataset\img1.jpg')
img2=Image.open('picture dataset\img2.jpg')
img3=Image.open('picture dataset\img3.jpg')
images=[img1,img2,img3]
print(img1.size,img2.size,img3.size)
label1=np.array([1,0,0])
label2=np.array([0,1,0])
label3=np.array([0,0,1])
labels=[label1,label2,label3]

#第二步:定义record文件
tfrecord_file='picture_train.tfrecords'
writer=tf.python_io.TFRecordWriter(tfrecord_file)

#第三步:每一次写入一条样本记录
for i in range(len(images)):
    features=tf.train.Features(feature={'Picture':tf.train.Feature(bytes_list=tf.train.BytesList(value=[images[i].tobytes()])),
                                        'Label':tf.train.Feature(bytes_list=tf.train.BytesList(value=[labels[i].tobytes()]))
                                         })
    #每一条样本的特征,将一系列特征组织成一条样本
    example=tf.train.Example(features=features)
    #将每一条样本写入到tfrecord文件
    writer.write(example.SerializeToString())
writer.close()
print('写入tfrecords文件完毕!')


   

运行上面的代码,已经有一个2198KB大小的picture_train.tfrecords文件。上面的代码可以看出,对于图像数据,规则基本上是一模一样的,区别在于数据的初始化处理,另外,图片数据不是一个一个像素进行存取的,需要将图片以及独热编码的标签转化为原生的bytes数据格式即可。


tfrecords文件的创建存为多个tfrecords文件

2.4


   

通过前面两个方法,我们知道可以把你想要的文件或者记录通过或多或少的方法转为TFRecord格式.
那么数据量很大的时候,你会发现,单个TFRecord文件是非常非常大的,这对于硬盘是不小的负担,所以,可以通过存储多个TFRecord文件来解决问题.其实保存为多个tfrecords文件并没有新的操作,完全和上面一样,只不过因为数据量巨大,需要对样本进行划分,然后分别保存在不同的tfrecords文件里面即可

比如一共有30000张图片,即30000个样本,前面10000个保存在picture_01.tfrecords文件里,中间10000个样本保存在picture_02.tfrecords文件里,最后10000组样本保存在picture_03.tfrecords文件里。


2.3.1 通过配置文件

   

matplotlibrc是matplotlib resource configurations的简称。matplotlib的图形配置方式有很多,主要是从以下三个方面进行配置的。

(1)通过配置文件进行配置——查看+设置

(2)通过rcParams['参数名']动态配置——查           看+设置

(3)通过matplotlib.rc()函数配置

本节总结

从上面的几个例子可以看出,创建tfrecords文件的步骤是比较简单的,按照固定的格式组织数据,然后写入进tfrecords文件即可,数据是分层组织的,可以有外向内一次看成,Examples—>Example—>Features—>Feature(int64、float、bytes)

Next

三、tfrecords文件的读取

tfrecords文件的读取tfrecords文件的简单预览

3.1


   

我们可以简单的查看一下我们所保存的tfrecords文件是否符合我们的预期,我们可以使用tf.train.Example.FromString()进行简单的查看,代码如下:

import tensorflow as tf

#确认tfrecord的内容
ex=next(tf.python_io.tf_record_iterator('titanic_train.tfrecords'))
print(tf.train.Example.FromString(ex))

  上面程序的运行结果如下:


features {

  feature {

    key: "Age"

    value {

      float_list {

        value: 30.0

      }

    }

  }

  feature {

    key: "Fare"

    value {

      float_list {

        value: 7.73330020904541

      }

    }

  }

  feature {

    key: "Parch"

    value {

      int64_list {

        value: 0

      }

    }

  }

  feature {

    key: "Pclass"

    value {

      int64_list {

        value: 3

      }

    }

  }

  feature {

    key: "Sex"

    value {

      int64_list {

        value: 0

      }

    }

  }

  feature {

    key: "Sibsp"

    value {

      int64_list {

        value: 0

      }

    }

  }

  feature {

    key: "Survived"

    value {

      int64_list {

        value: 1

      }

    }

  }

}

从上面返回的结果可以查看到保存的特征,特征的数据类型,第一组样本的特征取值。



tfrecords文件的读取tfrecords文件的加载

3.2


   

tfrecords文件的读取和加载是相对比较复杂的,本文也总结了几个固定的步骤:

第一步:定义一个reader对象,和定义tfrecords文件从哪里来。

filename_queue=tf.train.string_input_producer(['titanic_train.tfrecords'])


reader = tf.TFRecordReader()

后面会解析这两句话的含义

第二步:从tfrecords文件中解析保存的样本数据格式

第三步:从样本数据中一次性读取一个批次的数据,即填充满一个batch。因为在深度学习进行训练的时候,往往都是一次训练多少组,以多少组为一个batch,所以需要包装。

   

上面三个步骤的核心函数解析:


第一步:

filename_queue=tf.train.string_input_producer(['titanic_train.tfrecords'])

它告诉我们tfrecords文件从哪里来,注意,参数里面的中括号不能丢


reader = tf.TFRecordReader()

定义一个reader对象,该对象负责从tfrecords文件中读取。

_,serialized_example=reader.read(filename_queue)

它返回的是(key,value)的元祖形式。上面的serialized_example是无法直接查看的,需要去按照特征进行解析。


第二步:解析数据

featurestf.parse_single_example(serialized_example,features={...})

将数据的特征解析出来

第三步:每次将数据包装成一个batch。

tf.train.batch([age,sex,pclass,parch,sibsp,fare,label],

batch_size=16,

capacity=500)

第一个参数就是特征的名称,中括号不能掉,第二个是batch_size的大小,这个capacity后面会解释到。

   但是上面的步骤完成之后,我还只能够看到每一个特征的维度信息,还不能够获取具体的数值,要想获取具体的数值,依然需要在会话对象Session里面进行查看,而且步骤分为以下几步(续接前面):


第四步:首先在session里面创建Coordinator对象,他负责实现数据输入线程的同步,实现如下

coord = tf.train.Coordinator()


第五步:启动队列

threads=tf.train.start_queue_runners(sess=sess, coord)


第六步:这里就可以查看样本数据,将获取的样本数据“喂”给网络进行训练。


第七步:线程同步

coord.request_stop()

coord.join(threads=threads)


tfrecords文件的读取获取titanic中的age的数据

3.3


   按照前面的步骤,代码如下:

import tensorflow as tf

#第一步:定义reader对象以及tfrecords文件的输入部分
filename_queue = tf.train.string_input_producer(['titanic_train.tfrecords'])
reader = tf.TFRecordReader()

#第二步:使用reader函数读入tfrecords内容,它返回的是(key,value)
_, serialized_example = reader.read(filename_queue)
#print(serialized_example.shape)

#第三步:数据的解析parse
features = tf.parse_single_example(serialized_example,
                                    features={'Age':tf.FixedLenFeature([],tf.float32),
                                              'Sex':tf.FixedLenFeature([],tf.int64),
                                              'Pclass':tf.FixedLenFeature([],tf.int64),
                                              'Parch':tf.FixedLenFeature([],tf.int64),
                                              'Sibsp':tf.FixedLenFeature([],tf.int64),
                                              'Fare':tf.FixedLenFeature([],tf.float32),
                                              'Survived':tf.FixedLenFeature([],tf.int64)
                                            })


age=features['Age']
sex=features['Sex']
pclass=features['Pclass']
parch=features['Parch']
sibsp=features['Sibsp']
fare=features['Fare']
label=features['Survived']

#image = tf.reshape(image, [28, 28, 1])
#label = tf.reshape(label, [10])

#第三步:将样本包装成一个一个的batch
age,sex,pclass,parch,sibsp,fare,label = tf.train.batch([age,sex,pclass,parch,sibsp,fare,label],batch_size=16,capacity=500)

print(age.shape)#在这就可以查看特征的数据维度了,为(16,)因为batch_size为16

with tf.Session() as sess:
    tf.global_variables_initializer().run()
    #第四步
    coord = tf.train.Coordinator()
    #第五步:启动队列
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    '''第六步,这里面就可以查看数据,将数据“喂“给网络了 '''
    age_=sess.run(age)
    print(age_)

    #第七步
    coord.request_stop()
    coord.join(threads=threads)
    print('完结!')

   所获得的age_变量的结果为(16,)维度:

[30. 38. 30. 54. 40. 28. 19. 30. 22. 21. 27. 60. 56. 20. 16. 48.]


tfrecords文件的读取获取保存的图片数据

3.3

import tensorflow as tf
import matplotlib.pyplot as plt

#第一步:定义reader对象以及tfrecords文件的输入部分
filename_queue = tf.train.string_input_producer(['picture_train.tfrecords'])
reader = tf.TFRecordReader()

#第二步:使用reader函数读入tfrecords内容,它返回的是(key,value)
_, serialized_example = reader.read(filename_queue)

features = tf.parse_single_example(serialized_example,
                                    features={'Picture':tf.FixedLenFeature([],tf.string),
                                              'Label':tf.FixedLenFeature([],tf.string)
                                            })

image = tf.decode_raw(features['Picture'], tf.float32)  #需要解码,因为不是单个的数值
label = tf.decode_raw(features['Label'], tf.float64)

image = tf.reshape(image, [500,500])
label = tf.reshape(label, [3])


#第三步:将样本包装成一个一个的batch
img,lab = tf.train.shuffle_batch([image,label], batch_size=3,capacity=32,min_after_dequeue=10)

print(img.shape)    #形状为(3,500,500)
print(lab.shape)    #形状为(3,3)

with tf.Session() as sess:
    tf.global_variables_initializer().run()
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)

    img_=sess.run(lab[1])
    print(img_)

    coord.request_stop()
    coord.join(threads=threads)
    print('完结!')




本节总结

tfrecords文件的数据的读取步骤基本上是大同小异的,上面给出了详细的总结。需要注意的是,tfrecords文件中的数据的查看需要定义在session会话中。在session运行中,shuffle_batchbatch函数生成“一个batch的数据包”的过程是作为线程独立运行的,数据输入线程的挂起和运行时机由batch数据的生成函数控制的。shuffle函数指定内存保存样本数量的上限capacity和下限min_after_dequeue。当内存中的保存的样本数量大于上限capacity时,数据输入线程挂起。反之,当样本数据小于min_after_dequeue时,训练程序挂起。函数start_queue_runners()开启对应会话session的所有线程队列并返回线程句柄。Coordinator类对象负责实现数据输入线程的同步。当string_input_producer()函数产生无限循环队列时,应取消数据输入与训练程序的线程同步。

Next





全文总结

本文全面总结了tfrecords文件的写入与读取过程,配上相应的实现代码,阅读起来清晰易懂

END



往期回顾


详解语言模型NGram及困惑度Perplexity

看图理解长短期记忆网络LSTM与门控循环网络GRU

RNN最接地气的理解以及实现——sin正弦序列

TensorFlow实现经典LeNet网络详细教程























: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存