作者:曾翔钰 &&石炜贤
@曾翔钰
@石炜贤
TensorFlow程序读取数据一共有3种方法:
- 供给数据(Feeding): 在TensorFlow程序运行的每一步, 让Python代码来供给数据。
- 从文件读取数据: 在TensorFlow图的起始, 让一个输入管线从文件中读取数据。
- 预加载数据: 在TensorFlow图中定义常量或变量来保存所有数据(仅适用于数据量比较小的情况)。
使用Tensorflow训练神经网络模型,首先要读取数据,宝宝参照网上几篇博客,琢磨出几种Tensorflow从文件读取数据的办法,实际上办法挺多的,怎么开心怎么来。比如可以在一个文本里面写入图片数据的地址和label,用tensorflow的read_file()可以读入图片;也可以将图片(比如图片的大小是3*32*32,其中3是通道)和label值直接存在CSV文件或者TXT文件,下面详细说明。
重点内容
(1)从字典结构的数据文件读取
(2)从bin file读入
(3)从CSV (TXT)中读取
(4)从原图读取
1.从字典结构的数据文件读取
(1)字典结构的数据文件
a).上一篇博客有提到如何得到字典结构的数据文件,即如何将自己图片以字典结构存储为数据文本
博客链接:
http://blog.csdn.net/zengxyuyu/article/details/53240463
b).cifar10数据有三种版本,分别是MATLAB,Python和bin版本
数据下载链接:
http://www.cs.toronto.edu/~kriz/cifar.html
其中Python版本的数据即是以字典结构存储的数据
(2)读入
用pickle来load字典结构的数据
代码如下:
import pickle as p#记得导入pickle,用它来加载数据
import os
import tensorflow as tf
filename = os.path.join("data","contact")#我把数据文本放在项目下的data文件夹下
X = None
Y = None
with open(filename, 'rb')as f:
#从字典结构的数据文本加载数据,加载出来的数据保留了字典结构
datadict = p.load(f)
#获得所有的图像数据,是个6000*3*32*32的一维数组
X = datadict['data']
#获得所有图像的label,是个6000大小的一维数组
Y = datadict['labels']
print ("data数据X数组的大小:", X.shape)
#将6000*3*32*32一维数组转换为(6000,3*32*32)的二维数组
X = X.reshape(6000, -1)
#将X加入tf队列
valuequeue = tf.train.input_producer(X,shuffle=False)
valuelabel = tf.train.input_producer(Y,shuffle=False)
#每次出队一个
value = valuequeue.dequeue()
label = valuelabel.dequeue()
#label是字符串类型的,需要转成tf.int32类型
result.label = tf.string_to_number(label,tf.int32)
#Convert from [depth, height, width] to [height, width, depth].
#value存入的时候就是数组,所以取出来就是数组,没必要像label从字符串转,现在reshape3*32*32的三维矩阵
image = tf.reshape(value,[result.depth, result.height, result.width])
result.uint8image = tf.transpose(image, [1, 2, 0])
2.从bin file读入
在官网的cifar的例子中就是从bin文件中读取的。bin文件需要以一定的size格式存储,比如每个样本的值占多少字节,label占多少字节,且这对于每个样本都是固定的,然后一个挨着一个存储。这样就可以使用tf.FixedLengthRecordReader 类来每次读取固定长度的字节,正好对应一个样本存储的字节(包括label)。并且用tf.decode_raw进行解析。
(1)制作bin file
如何将自己的图片存为bin file,可以看看下面这篇博客,这篇博客使用C++和opencv将图片存为二进制文件:
http://blog.csdn.net/code_better/article/details/53289759
(2)从bin file读入
下面代码摘自cifar10_input.py。在用tf.decode_raw(注意decode时使用的数据格式最好与存储是的相同)得到record_bytes后,用tf.slice抽取里面的内容,第二个输入参数表示从第几个字节开始抽取,第三个参数表示抽取的字节数。代码中的reshape是根据图片存储是的shape格式有关,具体问题具体分析。
import tensorflow as tf
image_bytes = result.height * result.width * result.depth
record_bytes = label_bytes + image_bytes
#record_bytes为3073
reader = tf.FixedLengthRecordReader(record_bytes=record_bytes)
#每次读取的大小为3073
result.key, value = reader.read(filename)
# Convert from a string to a vector of uint8 that is record_bytes long.
record_bytes = tf.decode_raw(value, tf.uint8)
# The first bytes represent the label, which we convert from uint8->int32.
label = tf.cast(
tf.slice(record_bytes, [0], [label_bytes]), tf.int32)
# The remaining bytes after the label represent the image, which we reshape
# from [depth * height * width] to [depth, height, width].
depth_major = tf.reshape(tf.slice(record_bytes, [label_bytes], [image_bytes]),
[result.depth, result.height, result.width])
# Convert from [depth, height, width] to [height, width, depth].
uint8image = tf.transpose(depth_major, [1, 2, 0])
最后自然还需要用shuffle_batch 生成batch。
3.从CSV (TXT)中读取
有的时候在数据量不是很大的时候,可以从CSV或者TXT文件进行读取。
(1)制作CSV(TXT)数据文本
CSV (TXT)一般是一行存一个样本(包括样本值和label),用逗号隔开。用python的普通文本写入即可。
(2)读取的时候tf.TextLineReader 类来每次读取一行,并使用tf.decode_csv来对每一行进行解析。
这里主要介绍一下 tf.decode_csv(records, record_defaults, field_delim=None, name=None)
首先records与第二种方法中相同,为reader读到的内容,这里为CSV (TXT)的一行。一般一行里面的值会用逗号或者空格隔开,这里第三个输入参数就是指定用什么来进行分割,默认为逗号。第二个输入参数是指定分割后每个属性的类型,比如分割后会有三列,那么第二个参数就应该是[[‘int32’], [], [‘string’]], 可见不指定类型(设为空[])也可以。如果分割后的属性比较多,比如有100个,可以用[ []*100 ]来表示
col= tf.decode_csv(records, record_defaults=[ [ ]*100 ], field_delim=‘ ’, name=None)
返回的col是长度为100的list。
需要注意的是,当数据量比较大的时候,存成CSV或TXT文件要比BIN文件大的多,因此在TF中读取的速度也会慢很多。因此尽量不要读取大的CSV的方式来输入。
filename_queue = tf.train.string_input_producer(["file0.csv", "file1.csv"])
#每次一行
reader = tf.TextLineReader()
key, value = reader.read(filename_queue)
#解析每次的一行,默认以逗号分开
image_v, label = tf.decode_csv(
value, record_defaults=record_defaults)
result.label = tf.string_to_number(label,tf.int32)
image= tf.string_to_number(image_v,tf.int32)
image = tf.reshape(image,[result.depth, result.height, result.width])
# Convert from [depth, height, width] to [height, width, depth].
result.uint8image = tf.transpose(image, [1, 2, 0])
TF 官方还推荐了他自己的一种文件格式https://www.tensorflow.org/versions/r0.10/how_tos/reading_data/index.html#standard-tensorflow-format, 我还 没用过,大家可以自己看看。
4.从原图读取
(1)制作数据路径文件
一行一例,每例包括该样本的地址和label,用逗号分割开,用python普通文件写入即可
(2) 读取
很多情况下我们的图片训练集就是原始图片本身,并没有像cifar dataset那样存成bin等格式。因此我们需要根据一个train_list列表,去挨个读取图片。这里我用到的方法是首先将train_list.txt中的image list(也就是每一行有图片的路劲和label组成)读入队列中,那么对每次dequeue的内容中可以提取当前图片的路劲和label
filename = os.path.join(data_dir, trainfilename)
with open(filename) as fid:
content = fid.read()
content = content.split('\n')
content = content[:-1]
valuequeue = tf.train.string_input_producer(content,shuffle=True)
value = valuequeue.dequeue()
dir, labels = tf.decode_csv(records=value, record_defaults=[["string"], [""]], field_delim=" ")
labels = tf.string_to_number(labels, tf.int32)
imagecontent = tf.read_file(dir)
image = tf.image.decode_png(imagecontent, channels=3, dtype=tf.uint8)
image = tf.cast(image, tf.float32)
#将图片统一为32*32大小的
image = tf.image.resize_images(image,[32,32])
image = tf.reshape(image,[result.depth, result.height, result.width])
# Convert from [depth, height, width] to [height, width, depth].
result.uint8image = tf.transpose(image, [1, 2, 0])
不过这个方法对电脑输入输出要求比较高,如果机械硬盘有坏道,就会报Input/Output error,出现这种情况,要修复机械硬盘坏道。