Tensorflow数据读写:Numpy存储为TFRecord文件与读取

Tensorflow数据读写:Numpy存储为TFRecord文件与读取

用Tensorflow训练模型,读取数据有三种方法:

  • 每个epoch/batch将内存中的Numpy数据送入placeholder,只适用于小数据集,会相当占用内存。
  • 从硬盘里的txt或csv文件读取,IO操作比较耗时。
  • 读取tensorflow推荐的TFRecord文件

  TFRecord文件是一种能将data和label一起存储的二进制文件,能更好地利用内存,在tensorflow的graph中更快地复制、移动、读取。TFRecord文件包含了tf.train.Example 协议缓冲区(protocol buffer),可以先将数据转成字符串序列化,填入到协议缓冲区,再由TFRecordWritier写入TFRecord文件。


Numpy存TFRecord

def save_tfrecords(data, label, desfile):
    with tf.python_io.TFRecordWriter(desfile) as writer:
        for i in range(len(data)):
            features = tf.train.Features(
                feature = {
                    "data":tf.train.Feature(bytes_list = tf.train.BytesList(value = [data[i].astype(np.float64).tostring()])),
                    "label":tf.train.Feature(int64_list = tf.train.Int64List(value = [label[i]]))
                }
            )
            example = tf.train.Example(features = features)
            serialized = example.SerializeToString()
            writer.write(serialized)

使用举例

例如,我们把一个含有10个样本,维度不一的数据集,跟它们的label一起存储到tfrecord文件中。

# 将不定长样本padding补0成定长
def padding(data, maxlen=10):
    for i in range(len(data)):
        data[i] = np.hstack([data[i], np.zeros((maxlen-len(data[i])))])

lens = np.random.randint(low=3,high=10,size=(10,))
data = [np.arange(l) for l in lens]
padding(data)
label = [0,0,0,0,0,1,1,1,1,1]

save_tfrecords(data, label, "./data.tfrecords")

读TFRecord转Numpy

def _parse_function(example_proto):
  features = {"data": tf.FixedLenFeature((), tf.string),
              "label": tf.FixedLenFeature((), tf.int64)}
  parsed_features = tf.parse_single_example(example_proto, features)
  data = tf.decode_raw(parsed_features['data'], tf.float32)
  return data, parsed_features["label"]

def load_tfrecords(srcfile):
    sess = tf.Session()

    dataset = tf.data.TFRecordDataset(srcfile) # load tfrecord file
    dataset = dataset.map(_parse_function) # parse data into tensor
    dataset = dataset.repeat(2) # repeat for 2 epoches
    dataset = dataset.batch(5) # set batch_size = 5

    iterator = dataset.make_one_shot_iterator()
    next_data = iterator.get_next()

    while True:
        try:
            data, label = sess.run(next_data)
            print data
            print label
        except tf.errors.OutOfRangeError:
            break

使用举例

load_tfrecords(srcfile="./data.tfrecords")

结果输出

# 10个样本,2个epoch,相当于20个样本,每个batch有5个样本
[[0. 1. 2. 3. 4. 0. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8. 0.]
 [0. 1. 2. 3. 4. 5. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 5. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 0. 0. 0. 0. 0.]]
[0 0 0 0 0]
[[0. 1. 2. 3. 4. 0. 0. 0. 0. 0.]
 [0. 1. 2. 3. 0. 0. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 5. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 0. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 5. 6. 0. 0. 0.]]
[1 1 1 1 1]
[[0. 1. 2. 3. 4. 0. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8. 0.]
 [0. 1. 2. 3. 4. 5. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 5. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 0. 0. 0. 0. 0.]]
[0 0 0 0 0]
[[0. 1. 2. 3. 4. 0. 0. 0. 0. 0.]
 [0. 1. 2. 3. 0. 0. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 5. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 0. 0. 0. 0. 0.]
 [0. 1. 2. 3. 4. 5. 6. 0. 0. 0.]]
[1 1 1 1 1]

用Dataset读取TFRecord训练

准备数据集

为了实现一个训练,这里选用iris数据集,存储为TFRecord文件。

from sklearn.datasets import load_iris
iris = load_iris()
data = iris.data
label = iris.label
save_tfrecords(data, label, "./iris.tfrecord")

设计模型

这里简单地采用两层神经网络,使用relu作为激活函数

def model_function(X=None, Y=None):
    # data & label
    if X == None or Y == None:
        X = tf.placeholder(tf.float32, [None, 4])
        Y = tf.placeholder(tf.int64, [None,])

    # params
    W1 = tf.Variable(tf.random_normal([4,32], 0.0, 0.01))
    b1 = tf.Variable(tf.zeros([32,]))
    W2 = tf.Variable(tf.random_normal([32,3], 0.0, 0.01))
    b2 = tf.Variable(tf.zeros([3,]))

    # transform
    H1 = tf.nn.relu(tf.matmul(X, W1) + b1)
    H2 = tf.nn.relu(tf.matmul(H1, W2) + b2)

    cross_entropy = tf.losses.sparse_softmax_cross_entropy(Y, H2)

    return X, Y, cross_entropy

常规训练

  首先提供一种从内存中读入每一个batch的数据输入网络的placeholder进行训练的方法,这种方法的问题在于内存消耗较大,但是理论上应该会更快,因为不需要额外的IO操作。

def common_training():

    iris = load_iris()
    data = iris.data
    label = iris.target

    with tf.Session() as sess:
        X,Y,loss = model_function()
        training_op = tf.train.AdamOptimizer().minimize(loss)
        tf.global_variables_initializer().run()

        start = time.time()
        for epoch in range(1000):
            S = 0
            for batch in range(3):
                index = range(batch*50, (batch+1)*50)
                batch_x, batch_y = data[index], label[index]
                L, _ = sess.run([loss, training_op], feed_dict={X:batch_x, Y:batch_y})
                S += L
            if epoch % 100 == 0:
                print S / 3.0, len(index), len(batch_x)
        print time.time() - start, 's'

用tfrecord读取数据并送入模型训练

  用TFRecord文件初始化tf.data.TFRecordDataset对象,设定好batch大小和epoch个数,训练时直接 run(loss) 即可,数据会自动跳batch。注意,当文件队列走到尽头会抛出错误,要 excpet tf.errors.OutOfRangeError 防止报错。

def tfrecord_training():
    sess = tf.Session()
    iris = tf.data.TFRecordDataset("./iris.tfrecord")
    iris = iris.map(_parse_function)
    iris = iris.batch(50)
    iris = iris.repeat(1000)

    iterator = iris.make_one_shot_iterator()
    next_example, next_label = iterator.get_next()

    _, _, loss = model_function(next_example, next_label)
    training_op = tf.train.AdamOptimizer().minimize(loss)

    sess.run(tf.global_variables_initializer()) # must initialize

    start = time.time()
    for epoch in range(1000):
        S = 0
        for batch in range(3):
            try:
                L, _ = sess.run([loss, training_op])
            except tf.errors.OutOfRangeError:
                break
            S += L
        if epoch % 100 == 0:
            print S, S/3.0
    print time.time()-start, 's'

快慢对比

  common_training 耗时4秒,tfrecord_training 耗时8秒。
  我原以为使用Dataset会更快一点,然而实际上更慢,这个慢是体现在每一个batch的训练上的。
  我猜测Dataset.map()和Dataset.batch()只是设定了一个函数接口,在每一个batch都进行一次map运算,从而减慢了速度,甚至其实并没有读入文件,那么这里就还加上了IO的时间,而common_training只是从内存中取数据而已。
  如此看来,tfrecord_training的优势应该是在于无需事先把数据读入内存,这样对于大数据的训练来说会更好一点,代价就是处理时间了。


参考资料

『TensorFlow』数据读取类_data.Dataset
TensorFlow全新的数据读取方式:Dataset API入门教程 何之源
python numpy 中ndarry转成string后怎么转回来
TensorFlow学习记录– 7.TensorFlow高效读取数据之tfrecord详细解读
TensorFlow学习(十一):保存TFRecord文件
TensorFlow(二)制作自己的TFRecord数据集 读取、显示及代码详解
tensorflow读取数据-tfrecord格式
Tensorflow中使用tfrecord方式读取数据
【Tensorflow API】 tf.data
TensorFlow笔记:数据集导出
十图详解tensorflow数据读取机制(附代码)
How to use Dataset in TensorFlow

猜你喜欢

转载自blog.csdn.net/songbinxu/article/details/80136836