正如TensorFlow中的其他組件一樣,隊列就是TensorFlow圖中的節點。這是一種有狀態的節點,就像變量一樣:其他節點可以修改它的內容。具體來說,其他節點可以把新元素插入到隊列後端(rear),也可以把隊列前端(front)的元素刪除。隊列,如FIFOQueue和RandomShuffleQueue(A queue implementation that dequeues elements in a random order.)等對象,說明有時候我們讀取的訓練樣本希望是有序的,如果希望是有序的讀入訓練樣本,主要是訓練RNN的時候需要,那麼就是要用FIFOQueue,如果是無序地讀入訓練樣本,那麼就是使用RandomShuffleQueue,每次隨機彈出一個樣本。
在TensorFlow的tensor異步計算時都非常重要。例如,一個典型的輸入結構是使用一個RandomShuffleQueue來作爲模型訓練的輸入,多個線程準備訓練樣本,並且把這些樣本壓入隊列,一個訓練線程執行一個訓練操作,此操作會從隊列中移除最小批次的樣本(mini-batches),這種結構具有許多優點。
TensorFlow的Session對象是可以支持多線程的,因此多個線程可以很方便地使用同一個會話(Session)並且並行地執行操作。然而,在Python程序實現這樣的並行運算卻並不容易。所有線程都必須能被同步終止,異常必須能被正確捕獲並報告,會話終止的時候, 隊列必須能被正確地關閉。所幸TensorFlow提供了兩個類來幫助多線程的實現:tf.Coordinator和 tf.QueueRunner。從設計上這兩個類必須被一起使用。Coordinator類可以用來同時停止多個工作線程並且向那個在等待所有工作線程終止的程序報告異常。QueueRunner類用來協調多個工作線程同時將多個tensor壓入同一個隊列中。
===================================================================================
下面,我們通過代碼和流程圖來解釋tf使用隊列讀取數據的流程:
解釋:在上圖中,首先由一個單線程把文件名堆入隊列,兩個Reader同時從隊列中取文件名並讀取數據,Decoder將讀出的數據解碼後堆入樣本隊列,最後單個或批量取出樣本(圖中沒有展示樣本出列)
總結:搭建數據讀取圖需要5個步驟:
1、首先,將我們的數據轉成相應的文件放入磁盤
2、把文件名堆入隊列
3、從隊列中取文件名並讀取數據
4、Decoder將讀出的數據解碼
5、解碼後堆入樣本隊列,最後單個或批量取出樣本
=========================================================================================
下面,就使用代碼一步步來解釋上述的過程:
1、構造訓練數據,使用tf.python_io.TFRecordWriter
創建一個專門存儲tensorflow數據的writer
,擴展名爲’.tfrecord',最後存儲的文件是序列化的文件
- # -*- coding: utf-8 -*-
- import tensorflow as tf
- import numpy
- def write_binary():
- writer = tf.python_io.TFRecordWriter('/home/jdlu/data.tfrecord')
- #創建example
- for i in range(0, 100):
- a = 0.618 + i
- b = [2016 + i, 2017+i]
- c = numpy.array([[0, 1, 2],[3, 4, 5]]) + i
- c = c.astype(numpy.uint8)
- c_raw = c.tostring() #轉化成字符串
- #每個example的feature成員變量是一個dict,存儲一個樣本的不同部分(例如圖像像素+類標)
- example = tf.train.Example(
- features=tf.train.Features(
- feature={
- 'a': tf.train.Feature(
- float_list=tf.train.FloatList(value=[a])
- ),
- 'b': tf.train.Feature(
- int64_list=tf.train.Int64List(value=b)
- ),
- 'c': tf.train.Feature(
- bytes_list=tf.train.BytesList(value=[c_raw])
- )
- }
- )
- )
- #序列化
- serialized = example.SerializeToString()
- #寫入文件
- writer.write(serialized)
- writer.close()
2、把文件名堆入隊列,從隊列中取文件名並讀取數據,Decoder將讀出的數據解碼 這三個步驟都放在一個函數裏面,剛纔的writer不同,這個reader是符號化的,只有在sess中run纔會執行
- #定義一個函數,創建從"文件中讀一個樣本"的操作
- def read_single_sample(filename):
- #創建文件隊列,不限讀取的數量
- filename_queue = tf.train.string_input_producer([filename], num_epochs=None)
- # create a reader from file queue
- reader = tf.TFRecordReader()
- #reader從文件隊列中讀入一個序列化的樣本
- _, serialized_example = reader.read(filename_queue)
- # get feature from serialized example
- #解析符號化的樣本
- features = tf.parse_single_example(
- serialized_example,
- features={
- 'a': tf.FixedLenFeature([], tf.float32),
- 'b': tf.FixedLenFeature([2], tf.int64),
- 'c': tf.FixedLenFeature([], tf.string)
- }
- )
- a = features['a']
- b = features['b']
- c_raw = features['c']
- c = tf.decode_raw(c_raw, tf.uint8)
- c = tf.reshape(c, [2, 3])
- return a, b, c
- #-----main function-----
- if 0:
- write_binary()
- else:
- # create tensor
- a, b, c = read_single_sample('/home/jdlu/data.tfrecord')
- #shuffle_batch才能實現[a,b,c]的同步,也即特徵和label的同步,不然可能輸入的特徵和label不匹配
- #比如只有這樣使用,才能使image和label一一對應,每次提取一個image和對應的label
- #shuffle_batch返回的值就是RandomShuffleQueue.dequeue_many()的結果
- #Shuffle_batch構建了一個RandomShuffleQueue,並不斷地把單個的[a,b,c],送入隊列中
- a_batch, b_batch, c_batch = tf.train.shuffle_batch([a, b, c], batch_size=2, capacity=200, min_after_dequeue=100, num_threads=2)
- # sess
- sess = tf.Session()
- init = tf.initialize_all_variables()
- sess.run(init)
- tf.train.start_queue_runners(sess=sess)
- #print sess.run(a),sess.run(b),sess.run(c)
- #print sess.run(a),sess.run(b),sess.run(c)
- #print sess.run(a),sess.run(b),sess.run(c)
- for step in range(3):
- a_val, b_val, c_val = sess.run([a_batch, b_batch, c_batch])
- print(a_val, b_val, c_val)
注意:必須使用tf.train.shuffle_batch,不然提取出來的樣本會出現特徵和label不對應的情況,比如說我們文件中的樣本是(a1,b1,c1),(a2,b2,c2),不使用tf.train.shuffle_batch,可能提取出來的樣本是(a1,b1,c2),也即出現特徵和label不對應的情況。
對tf.train.shuffle_batch功能的解釋:
讀取batch數據需要使用新的隊列queues和QueueRunners(大致流程圖如下)。Shuffle_batch構建了一個RandomShuffleQueue,並不斷地把單個的[a,b,c](a,b,c是一一對應的)送入隊列中,這個入隊操作是通過QueueRunners啓動另外的線程來完成的。這個RandomShuffleQueue會順序地壓樣例到隊列中,直到隊列中的樣例個數達到了batch_size+min_after_dequeue個。它然後從隊列中選擇batch_size個隨機的元素進行返回。事實上,shuffle_batch返回的值就是RandomShuffleQueue.dequeue_many()的結果。有了這個batches變量,就可以開始訓練機器學習模型了。總結,tf.train.shuffle_batch的功能就是將我們解碼完的樣本放到一個隨機隊列中,然後在我們需要的時候彈出一個batch_size的樣本。
==================================================================================
可想而知,在你運行任何訓練步驟之前,我們要告知tensorflow去啓動這些線程,否則這些隊列會因爲等待數據入隊而被堵塞,導致數據流圖將一直處於掛起狀態。我們可以調用tf.train.start_queue_runners(sess=sess)來啓動所有的QueueRunners。這個調用並不是符號化的操作,它會啓動輸入管道的線程,填充樣本到隊列中,以便出隊操作可以從隊列中拿到樣本。另外,必須要先運行初始化操作再創建這些線程。如果這些隊列未被初始化,tensorflow會拋出錯誤。
用
tf.train.string_input_producer 函數來生成文件名隊列,這個函數可以設置shuffle = Ture,來打亂隊列,可以設置epoch = 5,過5遍訓練數據。
對tf.train.shuffle_batch功能的解釋:
讀取batch數據需要使用新的隊列queues和QueueRunners(大致流程圖如下)。Shuffle_batch構建了一個RandomShuffleQueue,並不斷地把單個的[a,b,c](a,b,c是一一對應的)送入隊列中,這個入隊操作是通過QueueRunners啓動另外的線程來完成的。這個RandomShuffleQueue會順序地壓樣例到隊列中,直到隊列中的樣例個數達到了batch_size+min_after_dequeue個。它然後從隊列中選擇batch_size個隨機的元素進行返回。事實上,shuffle_batch返回的值就是RandomShuffleQueue.dequeue_many()的結果。有了這個batches變量,就可以開始訓練機器學習模型了。總結,tf.train.shuffle_batch的功能就是將我們解碼完的樣本放到一個隨機隊列中,然後在我們需要的時候彈出一個batch_size的樣本。
http://www.cnblogs.com/Charles-Wan/p/6197019.html
http://blog.csdn.net/lujiandong1/article/details/53376134
http://www.cnblogs.com/Charles-Wan/p/6197019.html
http://blog.csdn.net/csuzhaoqinghui/article/details/51377941