tensorflow-队列与多线程

来源:互联网 发布:winaip软件的使用 编辑:程序博客网 时间:2024/06/06 07:48

1、Tensorflow数据输入简介

为了避免图像预处理成为神经网络模型训练效率的瓶颈,Tensorflow提供了多线程处理输入数据的框架。流程如下:

(1)指定原始数据的文件列表

(2)创建文件列表队列

(3)从文件中读取数据

(4)数据预处理

(5)整理成batch作为神经网络输入

tensorflow中的队列不仅是一种数据结构,还提供了多线程的机制。队列也是多线程输入数据处理框架的基础。


2、队列与多线程

       在tensorflow中,队列和变量类似,都是计算图上有状态的节点。其他的计算节点可以修改他们的状态。对于变量,可以通过赋值操作修改变量的取值。对于队列,修改队列状态的操作主要有Enqueue、EnqueueMany和Dequeue。以下程序展示了如何使用这些函数来操作一个队列。实例如下:

#创建一个先进先出队列,指定队列中最多可以保存两个元素,并指定类型为整数
q = tf.FIFOQueue(2, "int32")
print(q)
#使用enqueue_many函数来初始化队列中的元素。和变量初始化类似,在使用队列之前需要明确的调用这个初始化过程
#init = q.enqueue_many(([0,10],))
q1 = q.enqueue(1)
q2 = q.enqueue(2)
#使用Dequeue函数将队列中的第一个元素出队列。这个元素的值将被存在变量x中。
#x = q.dequeue()
#将得到的值加1
#y = x+1
#q_inc = q.enqueue([y])
with tf.Session() as sess:
    #运行初始化队列的操作
    #init.run()
    q1.run()
    q2.run()
    for _ in range(2):
        #print(sess.run(x))
        print(sess.run(q.dequeue()))
        print(sess.run(q.size()))
        #q.si
    #for _ in range(5):
        #运行q_inc将执行数据出队列、出队的元素+1、重新加入队列的整个过程。
        #v, _ = sess.run([x, q_inc])
        #print(v)

Tensorflow提供了FIFOQueue和RandomShuffleQueue两种队列。FIFOQueue实现的是一个先进先出队列,RandomShuffleQueue提供元素打乱的队列,每次出队列操作时随机选择一个。队列提供异步计算张量取值的一个机制,如多个线程可以同时向一个队列中写元素,或者同时读取一个队列中的元素。


3、多线程协同辅助函数

      Tensorflow提供了tf.Coordinator和tf.QueueRunner两个类来完成多线程协同的功能。tf.Coordinator主要用于协同多个线程一起停止,并提供了should_stop、request_stop和join三个函数。在启动线程之前,需要先声明一个tf.Coordinator类,并将这个类传入每一个创建的线程中。启动的线程需要一直查询tf.Coordinator类中提供的should_stop函数,当这个函数的返回值为True时,则当前线程也需要退出。每一个启动的线程都可以通过调用的request_stop函数来通知其他线程退出。当某一个线程调用request_stop函数之后,should_stop函数的返回值将被设置为True,这样其他的线程就可以同时终止了。实例如下:

import numpy as np
import threading
import time
#线程中运行的程序,这个程序每隔1秒判断是否需要停止并打印自己的ID
def MyLoop(coord, worker_id):
    #使用tf.Coordinator类提供的协同工具判断当前线程是否需要停止
    while not coord.should_stop():
        #随机停止所有的线程
        if np.random.rand()<0.1:
            print("stoping from id: %d" % 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(1,5)]
#启动所有的线程
for t in threads:
    t.start()
#等待所有线程退出
coord.join(threads)


       tf.QueueRunner主要用于启动多个线程来操作同一个队列,启动的这些线程可以通过上面介绍的tf.Coordinator类来同意管理。通过tf.QueueRunner和tf.Coordinator来管理多线程队列操作实例如下:

#声明一个先进先出队列,队列中最多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)
#将定义过的QueueRunner加入Tensorflow计算图上指定的集合
#tf.train.add_queue_runner函数没有指定集合,则加入默认集合tf.GraphKeys.QUEUE_RUNNERS.下面的函数
#就是将刚刚定义的qr加入默认的tf.GraphKeys.QUEUE_RUNNERS集合
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来启动所有线程。
    #否则因为没有线程运行入队操作,当调用出队操作时,程序会一直等待入队操作被运行。tf.train.start_queue_runners
    #函数会默认启动tf.GraphKeys.QUEUE_RUNNERS集合中所有的QueueRunner。因为这个函数只支持启动指定集合中
    #的QueueRunner,所以一般来说tf.train.add_queue_runner函数和tf.train.start_queue_runners函数会指定
    #同一个集合
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    #获取队列中的取值
    for _ in range(20):
        print(sess.run(out_tensor))
    
    #使用tf.train.Coordinator来停止所有的线程
    coord.request_stop()
    coord.join(threads)