tensorflow 数据读取总结---(直接供给数据(feeding) 从文件中以管线形式读取数据 预加载数据)

主要参考tensorflow官方中文社区,链接:

http://www.tensorfly.cn/tfdoc/how_tos/reading_data.html#AUTOGENERATED-file-formats,

这个社区中中给的一些示例链接有的不对,可以到这个链接里面找:

https://tensorflow.googlesource.com/tensorflow/+/master/tensorflow/examples/

官方链接:https://www.tensorflow.org/programmers_guide/datasets,这个主要用来进行管道读取的介绍,打不开请

tensorflow 数据读取三种:

  • 直接供给数据(feeding)
  • 从文件中以管线形式读取数据
  • 预加载数据

一、直接供给数据

这种方式比较简单,直接定义输入的placeholder,然后每次run的时候输入feed_dict参数就行

一般通过在网络中定义:

  x=tf.placeholder(tf.float32,shape=[None,_IMAGE_SIZE*_IMAGE_SIZE*_IMAGE_CHANNELS],name='images')  
  y=tf.placeholder(tf.float32,shape=[None,_NUM_CLASSES],name='Output')  

然后run的时候

 _loss,batch_acc=sess.run([loss,accuracy],feed_dict={x:batch_xs,y:batch_ys})

这里可以参考我的另一篇文章:Alexnet网络模型在cifar-10数据集上的实现(基于tensorflow-gpu)  的数据供给方式

二、从文件中以管线形式读取数据

一般包括:

文件名列表
可配置的 文件名乱序(shuffling)
可配置的 最大训练迭代数(epoch limit)
文件名队列
针对输入文件格式的阅读器
纪录解析器
可配置的预处理器
样本队列

以上操作我们会以不同格式文件进行举例说明

1.csv文件

(1)先说一下CSV文件格式

csv文件为按行读取数据,每一行的数据以空格或者逗号分开,比如我自己定义了几个个.csv文件,文件内容如下:

1,2,3,4,5
1,2,3,4,5
6,7,8,9,0
1,2,3,4,5
1,2,3,4,5
6,7,8,9,0
1,2,3,4,5
1,2,3,4,5
6,7,8,9,0
1,2,3,4,5
1,2,3,4,5
6,7,8,9,0
1,2,3,4,5
1,2,3,4,5
6,7,8,9,0
1,2,3,4,5
1,2,3,4,5

测试代码:

#coding=utf-8
import tensorflow as tf
#read from csv

#这里的[fil1.csv,file0]就是文件名列表,
filename_queue=tf.train.string_input_producer(["path/file0.csv","path/file1.csv"])
reader=tf.TextLineReader()
key,value=reader.read(filename_queue)

record_default=[[1],[1],[1],[1],[1]]

col1,col2,col3,col4,col5=tf.decode_csv(value,record_defaults=record_default)
features = tf.stack([col1,col2,col3,col4],axis=0)   #串接成一维张量,如果col1不是标量,则可以用tf.concat()

with tf.Session() as sess:
    coord=tf.train.Coordinator()  #This class implements a simple mechanism to coordinate the termination of a set of threads.
    threads=tf.train.start_queue_runners(coord=coord)

    for i in range(30):
        example,label=sess.run([features,col5])
        print(example,label)

    coord.request_stop()
    coord.join(threads)     #Wait for threads to terminate.

这里有必要对tf.train.string_input_producer函数进行说明

string_input_producer(
    string_tensor
,
    num_epochs
=None,
    shuffle
=True,
    seed
=None,
    capacity
=32,
    shared_name
=None,
    name
=None,
    cancel_op
=None
)

这个函数里的参数分别是文件名列表,最大迭代数,是否进行乱序,seed:确定每次乱序的结果是否一样,后面的都可以根据需要添加

最后返回的是文件名队列


2.固定长度记录(二进制文件)

从二进制文件中读取固定长度纪录, 可以使用tf.FixedLengthRecordReader的tf.decode_raw操作。decode_raw操作可以讲一个字符串转换为一个uint8的张量。

举例来说,the CIFAR-10 dataset的文件格式定义是:每条记录的长度都是固定的,一个字节的标签,后面是3072(32*32*3)字节的图像数据。也就是说我们每次固定获取3073个字节的数据。uint8的张量的标准操作就可以从中获取图像片并且根据需要进行重组。

示例代码:

#coding=utf-8
# 2018 04 07
#从二进制文件中读取数据

import tensorflow as tf
import os
import matplotlib.pyplot as pyplot
import PIL.Image as Image

def read_cifar10(filename_queue):
    record_bytes=1+32*32*3
    reader=tf.FixedLengthRecordReader(record_bytes=record_bytes)

    key,value=reader.read(filename_queue)

    record_bytes=tf.decode_raw(value,tf.uint8)
    label=tf.cast(tf.slice(record_bytes,[0],[1]),tf.uint8)
    depth_major = tf.reshape(tf.slice(record_bytes, [1], [32*32*3]),[3, 32, 32])

    unit8image=tf.transpose(depth_major,[1,2,0])

    return label,unit8image

def inputs(eval_data,data_path,batch_size):
    if not eval_data:
        filenames=[os.path.join(data_path,'data_batch_%d.bin' % i) for i in range(1,6)]
    else :
        filenames = [os.path.join(data_path, 'test_batch.bin')]

    #接下来生成文件名队列
    filename_queue=tf.train.string_input_producer(filenames)

    #接下来读取数据
    label,image=read_cifar10(filename_queue)
    reshaped_image=tf.cast(image,tf.float32)

    #
    #可以在这里对数据进行预处理
    #


    images,labels=tf.train.shuffle_batch([reshaped_image,label],batch_size=batch_size,num_threads=8,
                                         capacity=6+3,min_after_dequeue=6)
    return images,labels


images,labels=inputs(False,'/home/ximao/models/data/cifar-10-batches-bin/',2)

print(images)
with tf.Session() as sess :
    #这个函数将会启动输入管道的线程,填充样本到队列中,以便出队操作可以从队列中拿到样本
    #这种情况下最好配合使用一个tf.train.Coordinator,这样可以在发生错误的情况下正确地关闭这些线程
    coord=tf.train.Coordinator()
    threads=tf.train.start_queue_runners(sess=sess,coord=coord)
    for i in range(2):
        print(i)
        image,label=sess.run([images,labels])
        print(type(image),image.shape)
        r=Image.fromarray(image[0,:,:,0]).convert('L')
        g = Image.fromarray(image[0, :, :, 1]).convert('L')
        b = Image.fromarray(image[0, :, :, 2]).convert('L')
        image1=Image.merge("RGB",(r,g,b))
        pyplot.imshow(image1)
        pyplot.show()
        image1.save('/home/ximao/result'+str(i)+'.png','png')

    coord.request_stop()
    coord.join(threads)  # Wait for threads to terminate.


  这里你要先去下载cifar-10的数据集,地址:https://www.cs.toronto.edu/~kriz/cifar.html  ,注意这里下载的要是CIFAR-10 binary version (suitable for C programs)这个版本的。不同于我在:Alexnet网络模型在cifar-10数据集上的实现(基于tensorflow-gpu)所使用的版本。


对于有想对数据进行预处理的同学,包括裁剪,翻转等,可以在代码中所描述的位置添加操作就行了。

此外,我获取image的数据后,通过matplotlib,将图片绘制出来(如果你在linux运行程序报错:no display name and no $DISPLAY environment variable,则可以参考我这篇blog),图片如下:

    


3.标准TensorFlow格式数据读取

你可以将你自己的任意数据转成TFRecord格式文件,官方给了一个简单mnist的示例:链接,但是我没有尝试这个代码,

对于TFRecord数据的读取需要用到tf.data或者tf.contrib.data里的一些操作,我是读取的imagenet的数据集,因为这个数据集比较大,上面的两种方式不适合。你也可以先尝试tensorflow中文社区给的一个简单mnist tfrecord的读取例子:链接。

我自己ImageNet数据集读取示例:

import os
import tensorflow as tf
from Resnet_On_Imagenet import imagenet_preprocessing

_DEFAULT_IMAGE_SIZE = 224
_NUM_CHANNELS = 3
_NUM_CLASSES = 1001

_NUM_IMAGES = {
    'train': 1281167,
    'validation': 50000,
}
_NUM_TRAIN_FILES = 1024
_SHUFFLE_BUFFER = 1500


# 获取文件名

def get_filenames(is_training, data_dir):
    if is_training:
        return [
            os.path.join(data_dir, 'train-%05d-of-01024' % i)
            for i in range(_NUM_TRAIN_FILES)]
    else:
        return [
            os.path.join(data_dir, 'validation-%05d-of-00128' % i)
            for i in range(128)]


# 解析一个例子
def _parse_example_proto(example_serialized):  # example_serialized
    feature_map = {'image/encoded': tf.FixedLenFeature([], dtype=tf.string, default_value=''),
                   'image/class/label': tf.FixedLenFeature([1], dtype=tf.int64, default_value=-1),
                   'image/class/text': tf.FixedLenFeature([], tf.string, default_value='')}
    # 输入数据类型
    sparse_float32 = tf.VarLenFeature(dtype=tf.float32)
    feature_map.update({k: sparse_float32 for k in ['image/object/bbox/xmin',
                                                    'image/object/bbox/ymin',
                                                    'image/object/bbox/xmax',
                                                    'image/object/bbox/ymax']})
    # print(feature_map)
    for var in feature_map:
        print(var, ':', feature_map[var])
    # Parses a single Example proto.,这里example_serialized是tensorflow的一种数据格式
    features = tf.parse_single_example(example_serialized, feature_map)
    label = tf.cast(features['image/class/label'], dtype=tf.int32)
    xmin = tf.expand_dims(features['image/object/bbox/xmin'].values, 0)
    ymin = tf.expand_dims(features['image/object/bbox/ymin'].values, 0)
    xmax = tf.expand_dims(features['image/object/bbox/xmax'].values, 0)
    ymax = tf.expand_dims(features['image/object/bbox/ymax'].values, 0)

    bbox = tf.concat([ymin, xmin, ymax, xmax], 0)
    # Inserts a dimension of 1 into a tensor's shape.
    bbox = tf.expand_dims(bbox, 0)
    bbox = tf.transpose(bbox, [0, 2, 1])

    return features['image/encoded'], label, bbox


def parse_record(raw_record, is_training):
    image_buffer, label, bbox = _parse_example_proto(raw_record)

    image = imagenet_preprocessing.preprocess_image(
        image_buffer=image_buffer,
        bbox=bbox,
        output_height=_DEFAULT_IMAGE_SIZE,
        output_width=_DEFAULT_IMAGE_SIZE,
        num_channels=_NUM_CHANNELS,
        is_training=is_training)

    label = tf.one_hot(tf.reshape(label, shape=[]), _NUM_CLASSES)  # _NUM_CLASSES=1001
    print(image,':',label,)
    #image.eval()
    return image, label


def input_fn(is_training, data_dir, batch_size, num_epochs=1, num_threads=1, multi_gpu=False):
    filenames = get_filenames(is_training, data_dir)
    print(filenames)
    #Splits each rank-N tf.SparseTensor in this dataset row-wise. (deprecated:弃用)
    dataset = tf.data.Dataset.from_tensor_slices(filenames)
    print(dataset)
    if is_training:
        # Shuffle the input files
        dataset = dataset.shuffle(buffer_size=_NUM_TRAIN_FILES)
        # 如果is_training=True 则num_images=_NUM_IMAGES['train']
    num_images = is_training and _NUM_IMAGES['train'] or _NUM_IMAGES['validation']

    # Convert to individual records
    #此处分析以下这一行代码的运行过程,先是dataset调用自己的属性函数flat_map(self,map_func),self传递的是自己本身即dataset,在调用函数的时候这一参数忽略了,自动传递
    #map_func传递的是tf.data.TFRecordDataset,tf.data.TFRecordDataset就是指TFRecordDataset这个对象,没有实例化,相当于
    #我们在传递函数时传递的函数名
    dataset = dataset.flat_map(tf.data.TFRecordDataset)

    # We prefetch a batch at a time, This can help smooth out the time taken to
    # load input files as we go through shuffling and processing.
    dataset=dataset.prefetch(buffer_size=batch_size)
    print(dataset)
    if is_training:
        dataset=dataset.shuffle(buffer_size=_SHUFFLE_BUFFER)
    dataset=dataset.repeat(num_epochs)
    if multi_gpu:
        total_examples = num_epochs * num_images
        dataset = dataset.take(batch_size * (total_examples // batch_size))

    # Parse the raw records into images and labels
    dataset=dataset.map(lambda value:parse_record(value,is_training),num_parallel_calls=num_threads)
    dataset=dataset.batch(batch_size)
    dataset = dataset.prefetch(1)
    print(dataset)
    return dataset


上面这个数据读取的代码是从官方给的一个ImageNet Resnet的model改过来的,其中imagenet_preprocessing是对数据预处理的一个文件,我就不放出来了。在input_fn函数中有多个shuffle,prefetch以及多个参数buffer_size,batch_size,num_parallel_calls,我会在下一篇文章中解释

  此外,tfrecord文件读取有一个重要的参数feature_map,这个是表示你要读取tfrecord哪些参数的参数名字符串列表,如果你想去理解为什么这样定义以及代码中bbox为什么这样做,你需要查看官方给的建立ImageNet数据集tfrecord文件代码,理解其中生成tfrecord文件的过程。如果你找不到官方给的代码,可以评论联系我。

  以上input_fn返回的是包含image和label()的数据格式,还不能直接用,训练时需要如下操作

import tensorflow as tf

import os
import Resnet_On_Imagenet.ReadData as ReadData
from Resnet_On_Imagenet.config import cfg


with tf.Graph().as_default():
    is_training=tf.placeholder(tf.bool,[],'is_training')
    with tf.device('/cpu:0'):
        training_dataset=ReadData.input_fn(True,cfg.data_dir,cfg.batch_size,
                                          num_epochs=cfg.train_epochs,num_threads=8,multi_gpu=False)
        handle=tf.placeholder(tf.string,shape=[])
        iterator = tf.data.Iterator.from_string_handle(handle,
                    training_dataset.output_types,training_dataset.output_shapes)

        images,labels=iterator.get_next()
        print(images, ':', labels)

    with tf.Session() as sess:
        training_iterator = training_dataset.make_one_shot_iterator()
        training_handle = sess.run(training_iterator.string_handle())
        sess.run(tf.global_variables_initializer())
        print(sess.run(images,feed_dict={handle:training_handle}))
最后一句可以打印出获取到的一个image的值,上面这段代码只是一个示例获取image数据



三、预加载数据

当你的数据集比较小的时候(比如mnist),你可把所有的数据都先加载到内存(这种方式简单,但一般不推荐这种方式)

这里有两种方式:

  • 存储在常数中。
  • 存储在变量中,初始化后,永远不要改变它的值

使用常数更简单一些,但是会使用更多的内存(因为常数会内联的存储在数据流图数据结构中,这个结构体可能会被复制几次)。

training_data = ...
training_labels = ...
with tf.Session():
  input_data = tf.constant(training_data)
  input_labels = tf.constant(training_labels)
  ...

要改为使用变量的方式,您就需要在数据流图建立后初始化这个变量。

training_data = ...
training_labels = ...
with tf.Session() as sess:
  data_initializer = tf.placeholder(dtype=training_data.dtype,
                                    shape=training_data.shape)
  label_initializer = tf.placeholder(dtype=training_labels.dtype,
                                     shape=training_labels.shape)
  input_data = tf.Variable(data_initalizer, trainable=False, collections=[])
  input_labels = tf.Variable(label_initalizer, trainable=False, collections=[])
  ...#以下两行代码是初始化input_data,input_labels变量的
  sess.run(input_data.initializer,
           feed_dict={data_initializer: training_data})
  sess.run(input_labels.initializer,
           feed_dict={label_initializer: training_lables})

设定trainable=False 可以防止该变量被数据流图的 GraphKeys.TRAINABLE_VARIABLES 收集, 这样我们就不会在训练的时候尝试更新它的值; 设定 collections=[] 可以防止GraphKeys.VARIABLES 收集后做为保存和恢复的中断点。

上面面的两段代码,无论以哪种方式存储数据集,在给训练网络的输出传递images的时候都要通过tf.train.slice_input_producer function函数每次产生一个切片。

两种变量存储示例代码:使用常量,使用变量


四、多输入管道,

通常你会在一个数据集上面训练,然后在另外一个数据集上做评估计算(或称为 "eval")。 这样做的一种方法是,实际上包含两个独立的进程:

  • 训练过程中读取输入数据,并定期将所有的训练的变量写入还原点文件)。
  • 在计算过程中恢复还原点文件到一个推理模型中,读取有效的输入数据

这一个我认为特别好用,因为你能够训练一段时间就可以看到网络在测试集上的表现,不然等训练完了进行测试不对的话就浪费时间了

  这里可以参见社区这篇文章:http://www.tensorfly.cn/tfdoc/tutorials/deep_cnn.html#save-and-restore-checkpoints


当然多管道训练与测试存在共享变量的问题,而共享变量是tensorflow非常重要的一个点(我认为),有空单独给共享变量写篇文章。


~~~~~~~~~~~~

到此结束啦,有错误的欢迎大家指正评论啦,欢迎与我讨论~~

你可能感兴趣的:(机器学习,python,tensorflow)