TensorFlow 深度学习框架(12)-- 多线程输入数据处理框架

(一)队列与多线程

TensorFlow 提供了FIFIQueue 和 RandomShuffleQueue 两种队列。FIFOQueue 顾名思义就是先进先出的意思,RandomShuffleQueu 会将队列中的元素打乱,每次出队列操作得到的是当前队列所有元素中随机的一个。(在训练神经网络时会希望每次使用的训练数据尽量随机)

在TensorFlow中,队列不仅仅是一种数据结构,还是异步计算张量取值的一个重要机制。比如多个线程可以同时向一个队列中写元素,或者同时读取一个队列中的元素。TensorFlow 提供了 tf.Coordinator 和 tf.QueueRunner 两个类 来完成多线程协同的功能。    tf.Coordinator 主要用于协同多个线程一起停止,并提供了 should_stop, request_stop, join 三个函数,在启动线程之前,需要声明一个 tf.Coordinator 类,并将这个类传入每一个创建的线程中。启动的线程需要一直查询 tf.Coordinator 类中提供的 should_stop 函数,当这个函数的值为 True 时,则当前线程也需要退出。每一个启动的线程都可以通过 request_stop 函数来通知其他线程退出。

import tensorflow as tf

# 声明一个先进先出的队列,队列中最多 100 个元素,类型为实数
queue = tf.FIFOQueue(100,"float")
# 定义队列的入队操作
enqueue_op = queue.enqueue([tf.random_normal([1])])

# 使用 tf.train.QueueRunner 来创建多个线程运行队列的入队操作
# tf.train.QueueRunner 的第一个参数给出了被操作的队列 [enqueue_op] * 5 表示启动了 5 个线程
qr = tf.train.QueueRunner(queue,[enqueue_op] * 5)

# 将定义过的 QueueRunner 加入TensorFlow 计算图上指定的集合
tf.train.add_queue_runner(qr)

# 定义出队操作
out_tensor = queue.dequeue()

with tf.Session() as sess:
    # 使用 tf.train.Coordinator 来协同启动的线程
    coord = tf.train.Coordinator()
    # 使用 tf.train.QueueRunner 时,需要明确调用 tf.train.start_queue_runners 
    # 来启动所有的线程。
    threads = tf.train.start_queue_runners(sess = sess,coord = coord)
    for _ in range(3):print sess.run(out_tensor)[0]
    
    # 使用 tf.train.Coordinator 来停止所有的线程
    coord.request_stop()
    coord.join(threads)

(二)多个 TFRecord 文件读取数据

假设我们将训练数据保存到了多个 TFRecords 文件,就以 /path/to/data.tfrecords-00000-of-00002 和 /path/to/data.tfrecords-00001-of-00002 为例。以下代码展示了 tf.train.match_filenames_once 和 tf.train.string_input_producer 函数的使用方法

import tensorflow as tf

# 使用 tf.train.match_filenames_once 函数获取文件列表
files = tf.train.match_filenames_once("/path/to/data.tfrecords-*")

# 通过 tf.train.string_input_producer 函数创建输入队列
# shuffle 设置为 True 可以打乱获取的文件列表
filename_queue = tf.train.string_input_producer(files,shuffle = False)

# 解析一个样本,在 TFRecord 文件存储介绍时提及过
reader = tf.TFRecordReader()
_, serialized_example = reader.read(filename_queue)
features = tf.parse_single_example(
    serialized_example,
    features = {
            'i':tf.FixedLenFeature([],tf.int64),
            'j':tf.FixedLenFeature([],tf.int64),
                })

with tf.Session() as sess:
    # 初始化变量
    tf.initialize_all_variables().run()
    '''
    打印文件列表的结果:
        ["/path/to/data.tfrecords-00000-of-00002"
         "/path/to/data.tfrecords-00001-of-00002"]
    '''
    print sess.run(files)
    
    # 声明 tf.train.Coordinator 类来协同不同线程,并启动线程
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess = sess,coord = coord)
    
    # 依次读出 TFRecord 文件中的每一个样例。而且当所有的样例都被读完后,程序会重头开始。
    # 如果限制 num_epochs 为 1 ,那么程序会报错。因为每个文件中只有两个样例
    for i in range(6):
        print sess.run([featutes['i'],features['j']])
    
    # 读取完 6 个数据,告诉 threads 可以停止了
    coord.request_stop()
    coord.join(threads)

(三)组合成 batch 数据

上面只是展示了如何从多个 TFRecord 文件中多线程读取单个样例,但是我们在训练过程中往往是组织成一个 batch 数据。TensorFlow 提供了 tf.train.batch 和 tf.train.shuffle_batch 函数来将单个的样例数据组织成 batch 的形式输出

import tensorflow as tf

# 假设 i 表示一个样例的特征向量,比如一张图像的像素矩阵。
# j 表示对应的标签
example,label = features['i'],features['j']

# 一个 batch 中的样例数
batch_size = 3

# 组合样例的队列中最多可以存储的样例个数。如果这个队列太大,会占用很多内存资源
# 如果太小,那么出队操作可能会因为没有数据而被阻碍(block),从而导致训练效率降低
# 合适的大小根据 batch_size 决定
capacity = 1000 + 3 * batch_size

# 使用 tf.train.batch 来组合样例。 [example,label] 参数给出了需要组合的元素,
# 一般 example 和 label 分别代表训练数据和正确的标签。
# 当队列长度等于容量 capacity 时,TensorFlow将暂停入队操作,而是只等待元素出队
example_batch, label_batch = tf.train.batch(
            [example,label],batch_size = batch_size,capacity = capacity)

with tf.Session() as sess:
    # 初始化变量
    tf.initialize_all_variables().run()
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess = sess,coord = coord)

    # 获取并打印组合之后的样例。这个输入一般会作为神经网络的输入
    for i in range(2):
        cur_example_batch,cur_label_batch = sess.run(
                [example_batch,label_batch])
        print cur_example_batch,cur_label_batch

    coord.request_stop()
    coord.join(threads)

tf.train.shuffle_batch 函数用法跟 tf.train.batch 函数差不多,但是多了一个 min_after_dequeue 参数来限制出队时队列中元素的最少个数,因为当队列中元素太少时,shuffle 的意义就不大了

example_batch,label_batch = tf.train.shuffle_batch(
    [example,label],batch_size = batch_size,
    capacity = capacity, min_after_dequeue = 30)

(四)完整的输入处理训练框架

以上,已经介绍完了利用队列 多线程从多个 TFRecord 文件中组织成训练数据 batch 的方法。这里给出一个完整的处理和训练框架,算是一个小结。

import tensorflow as tf
files = tf.train.match_filenames_once("/path/to/file_pattern-*")
filename_queue = tf.train.string_input_producer(files,shuffle = False)

reader = tf.TFRecordReader()
_, serialized_example = reader.read(filename_queue)
features = tf.parse_single_example(
        serialized_example,
        features = {'image':tf.FixedLenFeature([],tf.string),
                    'label':tf.FixedLenFeature([],tf.int64),
                    'height':tf.FixedLenFeature([],tf.int64),
                    'width':tf.FixedLenFeature([],ft.int64),
                    'channels':tf.FixedLenFeature([],tf.int64),
                    })

image, label = features['image'],features['label']
height,width = features['height'],features['width']
channels = features['channels']

# 从原始图像中解析出像素矩阵,并根据图片尺寸还原图像
decoded_image = tf.decode_raw(image,tf.uint8)
decoded_image.set_shape([height,width,channels])

# 定义神经网络输入图片大小
image_size = 299
# 假设 preprocess_for_train 为图片随机处理函数,具体参考第 11 节
''' 由于这里采用了多线程输入框架,所以图像处理过程不会造成训练的瓶颈 '''
distorted_image = preprocess_for_train(decoded_image,image_size,image_size,None)

# 通过 tf.train.shuffle_batch 组织成 batch 训练数据
min_after_dequeue = 10000
batch_size = 100
capacity = min_after_dequeue + 3 * batch_size
image_batch,label_batch = tf.train.shuffle_batch(
        [distorted_image,label],batch_size = batch_size,
        capacity = capacity,min_after_dequeue = min_after_dequeue)

# 定义神经网络传输和优化过程
logit = inference(image_batch)
loss = calc_loss(logit,label_batch)
# 采用随机梯度下降算法优化损失函数
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)

with tf.Session() as sess:
    # 初始化变量
    tf.initialize_all_variables().run()
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess = sess,coord = coord)
    
    # 训练神经网络过程
    for i in range(TRAINING_STEPS):
        sess.run(train_step)

    coord.request_stop()
    coord.join(threads)

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值