1、队列和线程
2、文件读取
3、图片处理
在训练模型的过程中,如果数据量特别大将会消耗很多的IO读取时间。考虑到这点,Tensorflow有自己的多线程进行并发运行、队列机制和自带的文件格式(tfrecords)。
我们可以通过Tensorflow开启一个主线程和一个子线程。子线程用来读取数据,处理好后放入队列(队列可以实现数据共享、传递),然后主线程拿数据用来训练模型,这两个线程互不干扰。
队列和线程
队列与队列管理器
Tensorflow队列:
在训练样本的时候,希望读入的训练样本是有序的
tf.FIFOQueue 先进先出队列,按顺序出队列
tf.RandomShuffleQueue 随机出队列
1、tf.FIFOQueue
FIFOQueue(capacity, dtypes, name='fifo_queue')
创建一个以先进先出的顺序对元素进行排队的队列
capacity:队列的大小,是个整数。(可能存储在此队列中的元素数量的上限)
dtypes:DType对象列表。长度dtypes必须等于每个队列元素中的张量数。dtype的类型形状,决定了后面进队列元素形状。
method:
dequeue(name=None) 出队列
enqueue(vals, name=None) 入队列(只能进一个元素)
enqueue_many(vals, name=None) 入队列。 vals是列表张量或者元组张量。 返回一个进队列操作。
size(name=None)
同步
我们先了解一下同步的过程,从上到下执行。
代码展示
#! /usr/bin/env python
# -*- coding:utf-8 -*-
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 设置告警级别
"""
模拟同步:先处理数据,然后才能取数据训练
1、首先定义队列
2、定义读取数据,处理数据,入数据的过程 (取数据, +1 , 入队列)
Tensorflow中,运行操作op有依赖性
"""
# 1、首先定义队列
Q = tf.FIFOQueue(3, tf.float32)
# 2、放入一些数据,参数vals是一个张量
enq_many = Q.enqueue_many([[0.1, 0.2, 0.3], ])
# 3、定义读取数据,处理数据,入数据的过程 (取数据, +1 , 入队列)
out_q = Q.dequeue()
data = out_q + 1
en_q = Q.enqueue(data)
with tf.Session() as sess:
# 初始化队列,运行后就有数据了
sess.run(enq_many)
# 处理数据
for i in range(100):
sess.run(en_q)
# 训练数据
for i in range(Q.size().eval()):
print(sess.run(Q.dequeue()))
代码运行后如下所示:
线程和协调器
队列管理器和线程协调器实现异步读取训练
当数据量很大时,入队操作从硬盘中读取数据,放入内存中。主线程需要的等待入队操作完成,才能进行训练。会话里可以运行多线程,实现异步读取。
队列管理器
- tf.train.QueueRunner(queue, enqueue_ops=None) 创建一个QueueRunner队列管理器
queue:一个队列
enqueue_ops:添加线程的队列操作列表。[]*2表示指定两个线程,[]中是要处理的操作(指定子线程要做的事)
- create_threads(sess, coord=None, start=False) 创建线程来运行给定会话的入队操作(开启线程)
start:布尔值,如果为True启动线程;如果为False,调用者必须调用start()启动线程
coord:线程协调器。线程管理器需要用到。
return:线程的实例
线程协调器
- tf.train.Coordinator()
线程协调员,实现一个简单的机制来协调一组线程的终止
request_stop() 请求停止
should_stop() 检查是否要求停止
join(threads=None, stop_grace_period_secs=120) 等待线程终止
return 线程协调员实例
代码展示
#! /usr/bin/env python
# -*- coding:utf-8 -*-
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 设置告警级别
"""
模拟异步:子线程存入样本,主线程读取样本
1、首先定义队列1000
2、定义要做的事情 循环 值+1 放入队列当中
3、定义队列管理器op,指定多少个子线程,子线程干什么事情
"""
# 1、首先定义队列1000
Q = tf.FIFOQueue(1000, tf.float32)
# 2、定义要做的事情 循环 值+1 放入队列当中
var = tf.Variable(0.0)
# 实现自增 tf.assign_add把变量的值改变
data = tf.assign_add(var, tf.constant(1.0))
en_q = Q.enqueue(data)
# 3、定义队列管理器op,指定多少个子线程,子线程干什么事情
qr = tf.train.QueueRunner(Q, enqueue_ops=[en_q]*2)
# 初始化变量的OP
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
# 初始化变量
sess.run(init_op)
# 开启线程协调器
coord = tf.train.Coordinator()
# 真正的开启子线程
threads = qr.create_threads(sess, coord=coord, start=True)
# 主线程,不断读取数据训练
for i in range(300):
print(sess.run(Q.dequeue()))
# 回收子线程
coord.request_stop()
coord.join(threads)