今天運行TensorFlow的代碼,沒運行完一個epoch就直接報錯崩掉了,然后定位錯誤,發現了一個警告信息如下:
_2_index/fraction_of_32_full/fraction_of_32_full:Skipping cancelled enqueue attempt with queue not closed
接著代碼到后面就崩掉了,報錯的信息如下:
tensorflow.python.framework.errors_impl.OutOfRangeError:FIFOQueue '_0_batch_join/fifo_queue' is closed and has insufficient elements(requested 64, current size 59)
[[Node: batch_join =QueueDequeueManyV2[component_types=[DT_FLOAT, DT_INT64], timeout_ms=-1,_device="/job:localhost/replica:0/task:0/cpu:0"](batch_join/fifo_queue,batch_join/n)]]
[[Node: batch_join/_2179 =_Recv[client_terminated=false,recv_device="/job:localhost/replica:0/task:0/gpu:0",send_device="/job:localhost/replica:0/task:0/cpu:0",send_device_incarnation=1, tensor_name="edge_12_batch_join",tensor_type=DT_INT64,_device="/job:localhost/replica:0/task:0/gpu:0"]()]]
大致的意思是,在調用batch_join的時候,隊列需要64個元素(一個batch_size大小),但是隊列中只有59個元素,導致了OutOffRangeError。設置tf.train.batch_join函數中的allow_smaller_final_batch為True可以避免這樣的錯誤發生,不過這會到導致batch_join函數獲得的數據的size是不確定的,會帶來一系列其他的影響。
在追蹤錯誤發生的過程中,我注意到上述的警告信息,其整個報錯和警告關聯的業務代碼簡化版大致如下:
indices_que =tf.train.range_input_producer(range_size, name='index')
deque_op =indices_que.dequeue_many(self.batch_size*self.batch_num,'index_dequeue')
input_queue = tf.FIFOQueue(capacity=100000,dtypes=[tf.string, tf.int64], shapes=[(1,), (1,)], name='input_que')
enque_op = input_queue.enqueue_many([samples],name='enque_op')
batch_sample= tf.train.batch_join(input_queue_list,batch_size= batch_size,enqueue_many=False,capacity=4*num_threads*self.batch_size,allow_smaller_final_batch=False)
其中input_queue_list與input_queue相關,是從input_queue中獲取元素處理的一個list(涉及每個線程處理的具體業務邏輯,因此省略)。
而警告信息來自于其中某一個隊列,可以看出其enqueue的入隊操作沒有完成,然后在運行中我嘗試打印出deueu_op的結果,其結果無誤,每次出對的索引數量都是batch_size*batch_num個,因而問題出現在enque_op部分,猜測是enqueue_many過程沒有完成,隊列就關閉了。
經過反復檢查代碼,發現可能導致隊列關閉的代碼如下:
run_config= tf.ConfigProto()
run_config.operation_timeout_in_ms= 10000
這段代碼為TensorFlow所有的阻塞操作定義了一個毫秒級別的超時時間,猜測由于是非主線程的其他線程進行enqueue_many操作,然后入隊過程中超時,導致了隊列被關閉,但是沒有拋出異常到主線程,導致主線程繼續運行直至報錯。
解決辦法也很簡單,就是不設置超時時間即可。
上述的問題解決過程中可以看到幾點:
1.雖然調用的是enqueue_many操作,但是具體實現的時候,enqueue_many也可能是一個一個元素往隊列中存放的。
2.TensorFlow有可能忽略掉非主線程的異常的拋出,因而在運行調試中,要注意一些警告信息,或者自己指定處理非主線程的其他線程的異常信息。
最后,如果上述無法解決你的Skipping cancelled enqueue attempt with queue not closed的問題,那么嘗試在Session結束的地方,加入
coord.request_stop()
coord.joint(thread)
這個解決辦法來自:http://qiita.com/7of9/items/3b9364444418e128c92a
或者是嘗試更新TensorFlow的版本,有人提及這個可能涉及到TensorFlow的版本BUG,詳情見:https://github.com/TensorBox/TensorBox/issues/25