tensorflow学习1:队列和线程

TensorFlow的Session对象是可以支持多线程的,因此多个线程可以很方便地使用同一个会话(Session)并且并行地执行操作。然而,在Python程序实现这样的并行运算却并不容易。所有线程都必须能被同步终止,异常必须能被正确捕获并报告,回话终止的时候, 队列必须能被正确地关闭。

所幸TensorFlow提供了两个类来帮助多线程的实现:tf.Coordinatortf.QueueRunner。从设计上这两个类必须被一起使用。Coordinator类可以用来同时停止多个工作线程并且向那个在等待所有工作线程终止的程序报告异常。QueueRunner类用来协调多个工作线程同时将多个张量推入同一个队列中。

队列

1.FIFOQueue

FIFOQueue 创建一个先入先出队列。修改队列的主要状态有:EnqueueMany、Enqueue 和Dequeue。其中EnqueueMany用于初始化队列,Enqueue用于入队,Dequeue用于出队。直接上代码:

#创建一个先入先出大小为3的队列,
q = tf.FIFOQueue(3, "float")
#使用enqueue_many函数初始化队列,插入 0.1、 0.2、 0.3 三个数字
init = q.enqueue_many(([0.1, 0.2, 0.3],))
# 使用dequeue函数定义出列,之后+1,再使用enqueue进行入列
x = q.dequeue()
y = x + 1
q_inc = q.enqueue([y])
with tf.Session() as sess:
    sess.run(init)
    quelen = sess.run(q.size())
    for i in range(2):
        sess.run(q_inc) # 执行2次入队操作,队列中的值变为 0.3,1.1,1.2,入队操作不能print,print出来为none
            
    quelen = sess.run(q.size())
    for i in range(quelen):
        print (sess.run(q.dequeue())) # 输出队列的值

运行结果为:0.3,1.1,1.2

3.RandomShuffleQueue

RandomShuffleQueue 创建一个随机队列,在出队列时,是以随机的顺序产生元素的。例如,我们在训练一些图像样本时,使用 CNN 的网络结构,希望可以无序地读入训练样本,就要用RandomShuffleQueue,每次随机产生一个训练样本。

RandomShuffleQueue在TensorFlow 使用异步计算时非常重要。因为 TensorFlow 的会话是支持多线程的,我们可以在主线程里执行训练操作,使用 RandomShuffleQueue 作为训练输入,开多个线程来准备训练样本,将样本压入队列后,主线程会从队列中每次取出 mini-batch 的样本进行训练。

q = tf.RandomShuffleQueue(capacity=10, min_after_dequeue=2, dtypes="float")
sess = tf.Session()
for i in range(0, 10): #10 次入队
    sess.run(q.enqueue(i))
for i in range(0, 8): # 8 次出队
    print(sess.run(q.dequeue()))

运行结果是乱序的。

协调器

Coordinator类可以用来同时停止多个工作线程并且向那个在等待所有工作线程终止的程序报告异常,其主要方法有:

  • should_stop():如果线程应该停止则返回True。
  • request_stop(<exception>):请求该线程停止。
  • join(<list of threads>):等待被指定的线程终止。

在启动线程之前首先创建一个Coordinator类,然后将这个类传入每一个创建的线程中。这些线程通常一直循环运行,一直到should_stop()返回True时停止。 任何线程都可以决定计算什么时候应该停止。它只需要调用request_stop(),同时其他线程的should_stop()将会返回True,然后都停下来。

代码如下:

import tensorflow as tf
import numpy as np
import threading
import time

#线程中运行的程序,这个程序每间隔1s判断是否需要停止并打印自己的ID
def MyLoop(coord, worker_id):
    #使用tf.Coordinator类提供的协同工具判断当前线程是否需要停止
    while not coord.should_stop():
        #随机停止所有的线程
        if np.random.rand() < 0.1:
            print ('stoping from id: %d\n' % worker_id)
            coord.request_stop()
        else:
            #打印当前线程的id
            print ('Working on id: %d\n' % worker_id)
        time.sleep(1)    
            
#声明一个tf.train.Coordinator类来协同多个线程
coord = tf.train.Coordinator()
#声明创建5个线程
threads = [threading.Thread(target = MyLoop, args = (coord,i,)) for i in range(5)]
#启动所有的线程
for t in threads:
    t.start()
coord.join(threads)

队列管理器

上面讲的Coordinator是用于协调多个线程的,在此之前我们首先要启动多个线程。队列管理器(QueueRunner)就是用来启动(创建)多个线程来操作同一个队列

下面是将Coordinator和QueueRunner结合起来的代码:

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

#使用tf.train.QueueRunner来创建多个线程运行队列的入队操作
#tf.train.QueueRunner的第一个参数给了被操作的队列
#[enqueue_op]*5表示需要启动5个线程,每一个线程中运行的是enqueue_op操作
qr = tf.train.QueueRunner(queue,[enqueue_op] * 5)
#出队操作
out_tensor = queue.dequeue()
with tf.Session() as sess:
    coord = tf.train.Coordinator()  #线程协调器,用于协同启动的线程
    threads = qr.create_threads(sess, coord = coord, start=True)

    for _ in range(3):
        print (sess.run(out_tensor)[0])
        
    coord.request_stop()
    coord.join(threads)

总结

简单来讲,就是首先通过tf.FIFOQueue或者tf.RandomShuffleQueue创建一个队列,然后通过QueueRunner来运行几个线程, 这几个线程处理样本,并且将样本推入队列。在通过Coordinator,让queue runner使用Coordinator来启动这些线程,创建一个训练的循环,并且使用Coordinator来控制QueueRunner的线程们的终止。

猜你喜欢

转载自blog.csdn.net/Mr_health/article/details/81261286