tensorflow之GPU加速的理解
最近在整理模型加速的問題,使用到了GPU,但是有時候發現GPU的速度盡然比CPU還低,後來查詢一些相關的資料後發現可能是由於兩方面原因造成的:1. GPU的計算能力不行(畢竟對於筆記本自帶的GPU而言其效能並不一定比CPU強);2. GPU和CPU之間存在通訊問題,即資料的讀取要在CPU中進行,讀取後的資料再送入GPU中進行處理。
針對第2個問題,考慮以佇列的方式來解決,具體原因為:當資料在佇列中傳入的時候可以採用並行的方式進行,即當圖在處理第一張圖片的時候,第二張圖片已經傳進去了,這樣在處理第二張的時候就不用考慮CPU傳入GPU的時間限制。
具體涉及的程式碼如下:
filename = os.listdir(args.input)
filelist = [os.path.join(args.input, file) for file in filename]
# 構建檔名佇列
file_q = tf.train.string_input_producer(filelist, shuffle=False)
# 構建讀取器
reader = tf.WholeFileReader()
# 讀取內容
key, value = reader.read(file_q)
# 構建解碼器
image = tf.image.decode_jpeg(value)
# print(image)
# 統一圖片大小 設定長寬
resize_image = tf.image.resize_images(image, [height, width], method=1)
# 圖片進行歸一化
float_image = tf.image.per_image_standardization(resize_image)
# 指定通道大小
float_image.set_shape([height, width, 3])
#float_image = tf.cast(float_image, tf.float32)
# 構建批量處理管道
image_batch, key_batch = tf.train.batch([float_image, key], batch_size=1, num_threads=1, capacity=100,
enqueue_many=False)
with tf.Session() as sess:
pnet, rnet, onet = detect_face.create_mtcnn(sess, None)
# 構建執行緒協調器
coord = tf.train.Coordinator()
# 開啟執行緒
threads = tf.train.start_queue_runners(sess, coord=coord)
image_batch, key_batch = sess.run([image_batch, key_batch])
print(sess.run(image).shape)
print(sess.run(tf.image.extract_jpeg_shape(value)))
for i in range(image_batch.shape[0]):
# print(key_batch[i])
image = image_batch[i, :, :, :]
print(image.shape)
print(image.dtype)
start = time.time()
bounding_boxes = detect_face.detect_face(image, minsize, pnet, rnet, onet, threshold, factor)
end = time.time()
nrof_faces = bounding_boxes.shape[0]
print(end-start)
print('Total %d face(s) detected' % nrof_faces)
coord.request_stop()
coord.join(threads)
其中涉及到以幾個函式:
1.tf.train.string_input_producer: 該函式預設的是輸入一個string的列表,然後該string列表將會產生一個佇列。預設的情況是亂序產生的,當設定shuffle=False時,會以正常順序讀取資料。
2. tf.image.decode_jpeg:將圖片解碼成一個張量。通過sess.run可以輸出該張量的值
3. tf.train.batch:對資料進行分批處理,其中第一個引數表示需要分批的張量,可以是一個張量的列表,表示對每個張量都需要進行分批處理,產生對應的批資料個數。
4. image_batch, key_batch = sess.run([image_batch, key_batch]): 該指令尤為重要,因為分批處理後的資料只是張量的形式,如果沒有sess.run無法執行。所以該指令是把資料變為可以在後續程式中使用的具體的陣列。另外使用key和value的目的是為了可以知道自己處理的是哪張圖片,此時key和value是一一對應的。
注意:如果分開則情況不一樣,例如:image_batch = sess.run(image_batch)
key_batch = sess.run(key_batch)
此時得出的key和value並不對應。主要是因為第一次執行sess.run時對佇列進行了一遍處理,當在執行一次時,佇列的指標指在第一次處理後的位置繼續進行。所以不對應。(key,value都是在佇列中產生的)