主要参考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 数据读取三种:
一、直接供给数据
这种方式比较简单,直接定义输入的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非常重要的一个点(我认为),有空单独给共享变量写篇文章。
~~~~~~~~~~~~
到此结束啦,有错误的欢迎大家指正评论啦,欢迎与我讨论~~