【深度学习】TensorFlow文件读取流程应用案例

1,599 阅读6分钟

1. 三种获取数据到TensorFlow程序的方法

  1. QueueRunner:基于队列的输入管道从TensorFlow图形开头的文件中读取数据
  2. Feeding:运行每一步时,Pyhon代码提供数据
  3. 预加载数据:TensorFlow图中的张量包含所有数据(对于小数据集)

1.1 文件读取流程

大概分为三个阶段

  • 第一阶段:构造文件名队列
    • 方便我们快速高效读取这组文件,先读取文件名
  • 第二阶段:读取与解码
    • 有读取器Reader,从上一步的文件名队列文件中以样本为单位去读取
  • 第三阶段:批处理队列
    • 将解码出的样本放到批处理队列中,进行后续训练
  • 还需要手动开启线程使其流动起来

下面将为这三个阶段进行详细讲解与演示。

2. 构造文件名队列

将需要读取的文件的文件名放入文件名队列

  • tf.train.string_input_producer(string_tensor, shuffle=True)
    • string_tensor:传入含有文件名+路径的列表
    • shuffle:是否打乱数据
    • num_epochs:过几遍数据,默认无限过数据,直到满足设定的批处理大小为止
    • return:文件队列

首先,我们先导入所需模块,本案例使用Tensorflow1.x版本进行演示,但是安装了TensorFlow2.x版本,因此,我们需要开启兼容模式,从而使其支持运行Tensorflow1.x版本的语法。

import tensorflow as tf
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
import os

我们将想要读取的图片保存在了当前路径下的picture目录中。具体内部命名方式如下图所示:

image.png

然后,我们使用os.path.join的方法拼接一下路径与图片文件名,通过变量来接收,然后传入队列函数tf.train.string_input_producer中。

def picture_read():
    """
    狗图片读取案例
    """
    # 1. 构造文件名队列
    file_queue = tf.train.string_input_producer(file_list)
    return None
    
filename = os.listdir("./picture")
file_list = [os.path.join("./picture/", file) for file in filename]
#print(file_list)
picture_read()

3. 读取与解码

从队列当中读取文件内容,并进行解码操作。

3.1 读取文件内容

不同的文件类型,读取与解码的方法也会不一样。阅读器默认每次只读取一个样本。换句话说,文本文件默认一次读取一行;图片文件默认一次读取一张图片;二进制文件一次读取指定字节数(最好是一个样本的字节数)TFRecords默认一次读取一个example。

  • tf.TextLineReader:

    • 阅读文本文件逗号分隔值(CSV)格式,默认按行读取
    • return:读取器实例
  • tf.WholeFileReader

    • 以一张图片作为一个样本
    • 用于读取图片文件
    • return:读取器实例
  • tf.FixedLengthRecordReader(record_bytes)

    • 二进制文件
    • 要读取每个记录是固定数量字节的二进制文件
    • record_bytes:整型,指定每次读取(一个样本)的字节数,要告诉它固定字节数是多少
    • return:读取器实例
  • tf.TFRecordReader

    • 读取TFRecords文件
    • return:读取器实例

以上所有不同类型的读取器,都有一个共同的方法.read()。都会返回一个Tensors元组(key表示文件名,value表示读取到的这个样本内容) 由于默认只会读取一个样本,所以如果想要进行批处理,需要使用tf.train.batch或tf.train.shuffle_batch进行批处理操作,便于之后指定每批次多个样本的训练。

在构造队列代码基础上继续增加图片读取部分代码。因为我们要读取图片类型的文件,因此选择tf.WholeFileReader读取方式。

def picture_read():
    """
    狗图片读取案例
    """
    # 1. 构造文件名队列
    file_queue = tf.train.string_input_producer(file_list)
    
    # 2. 读取图片与解码
    # 2.1 读取阶段
    reader = tf.WholeFileReader()
    # key是文件名 value是一张图片的原始编码形式
    key, value = reader.read(file_queue)
    print("key:\n", key)
    print("value\n", value)

3.2 内容解码

读取不同类型的文件,也应该对读取到的不同类型的内容进行相对应的解码操作,解码成同意的Tensor格式

  • tf.decode_csv:

    • 解码文本文件内容
  • tf.image.decode_jpeg(contents)

    • 将JPEG编码的图像解码为uint8张量
    • return:uint8张量,3-D形状[height, width, channels]
  • tf.decode_raw

    • 解码二进制文件内容
    • 与tf.FixedLengthRecordReader搭配使用,二进制读取为uint8类型

解码阶段,默认所有的内容都解码成tf.uint8类型,如果之后需要转换成指定类型则可使用tf.cast()进行相应转换。

使用tf.image.decode_jpeg对图片进行解码

def picture_read():
    """
    狗图片读取案例
    """
    # 1. 构造文件名队列
    file_queue = tf.train.string_input_producer(file_list)
    
    # 2. 读取图片与解码
    # 2.1 读取阶段
    reader = tf.WholeFileReader()
    # key是文件名 value是一张图片的原始编码形式
    key, value = reader.read(file_queue)
    print("key:\n", key)
    print("value\n", value)
    
    
    # 2.2 解码阶段
    image = tf.image.decode_jpeg(value)
    print("image:\n", image)

4. 批处理

解码之后,可以直接获取默认的一个样本内容了,但是如果想要获取多个样本,需要加入到新的队列进行批处理。

  • tf.train.batch(tensors, batch_size, num_threads=1, capacity=32, name=None)

    • 读取指定大小(个数)的张量
    • tensors:可以是包含张量的列表,批处理的内容放到列表当中传入
    • batch_size:从队列中读取的批处理大小(一次要处理几个样本?)
    • num_threads:进入队列的线程数(取决于电脑的物理性能)
    • capacity:整数,队列中元素的最大数量
    • return:tensors
  • tf.train.shuffle_batch

在批处理之前,针对本案例(狗识别)还需要添加图片resize步骤,对输入的图片的宽、高、通道数进行统一,本案例中我们将图片的宽和高都修改(resize)成200*200,通道数设定为3。此过程需要在读取阶段后进行完成,图片解码以后使用tf.image.resize_images函数进行图片尺寸修改。

  • 如果只修改了图片尺寸200*200,运行程序还是会报错,我们还需要指定图片的通道数

使用.set_shape方法,对静态形状进行指定,指定图片的通道数为3,就可以了。

完成图片尺寸修改步骤后,我们就可以进行批处理阶段了,使用tf.train.batch函数进行批处理。

  • 批处理量batch_size设定为100
  • 线程数为1
  • 容量100
  • 将resize后的图片张量image_AfterResize以列表形式传入该函数
def picture_read():
    """
    狗图片读取案例
    """
    # 1. 构造文件名队列
    file_queue = tf.train.string_input_producer(file_list)
    
    # 2. 读取图片与解码
    # 2.1 读取阶段
    reader = tf.WholeFileReader()
    # key是文件名 value是一张图片的原始编码形式
    key, value = reader.read(file_queue)
    print("key:\n", key)
    print("value\n", value)
    
    
    # 2.2 解码阶段
    image = tf.image.decode_jpeg(value)
    print("image:\n", image)
    
    ## 2.3 统一图片形状,再放入批处理队列
    # (1)图像的形状、类型修改
    image_AfterResize = tf.image.resize_images(image, [200, 200]) # 先修改了宽和高
    print("image_AfterResize:\n", image_AfterResize)
    
    # (2)静态形状修改
    image_AfterResize.set_shape(shape=[200,200,3]) # 再修改了维度
    print("image_AfterResize:\n", image_AfterResize)
    
    # 3. 批处理
    image_batch = tf.train.batch([image_AfterResize], batch_size=100, num_threads=1, capacity=100)
    print("image_batch:\n", image_batch)

最后,我们开启会话,使其能够运行,张量就能有数值了。开启会话这一步骤同样需要在我们自定义的picture_read()函数中进行。

    # 4.  开启会话
    with tf.Session() as sess:
        # 开启线程
        # 线程协调员
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)
        
        key_new, value_new, image_new, image_AfterResize_new, image_batch_new = sess.run([key, value, image, image_AfterResize, image_batch])
        print("key_new:\n", key_new)
        print("value_new:\n", value_new)
        print("image_new:\n", image_new)
        print("image_AfterResize_new:\n", image_AfterResize_new)
        print("image_batch_new:\n", image_batch_new)
        
        # 回收线程
        coord.request_stop()
        coord.join(threads)

最终调用此自定义函数,并指定好图片数据所在路径,即可运行。 部分运行结果如下图片展示:

重要张量形状展示:

image.png

image.png

尺寸修改后的图片:每一个基本单元三个元素表示一个像素点。

image.png

批处理后的图片:经过批处理,图片数据变成了四维数据,因为,100张图片组成了一个批次的新数据,而不是单张图片,因此多了一个维度。

image.png

本文正在参加「金石计划 . 瓜分6万现金大奖」