我使用tensorflow API运行图像处理脚本。事实证明,当我在会话运行过程之外设置for循环时,处理时间迅速减少。谁能告诉我为什么?有副作用吗?
原始代码:
with tf.Session() as sess:
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(coord=coord)
for i in range(len(file_list)):
start = time.time()
image_crop, bboxs_crop = sess.run(crop_image(file_list[i], bboxs_list[i], sess))
print( 'Done image %d th in %d ms \n'% (i, ((time.time() - start)*1000)))
# image_crop, bboxs_crop, image_debug = sess.run(crop_image(file_list[i], bboxs_list[i], sess))
labels, bboxs = filter_bbox(labels_list[i], bboxs_crop)
# Image._show(Image.fromarray(np.asarray(image_crop)))
# Image._show(Image.fromarray(np.asarray(image_debug)))
save_image(image_crop, ntpath.basename(file_list[i]))
#save_desc_file(file_list[i], labels_list[i], bboxs_crop)
save_desc_file(file_list[i], labels, bboxs)
coord.request_stop()
coord.join(threads)
修改了代码:
for i in range(len(file_list)):
with tf.Graph().as_default(), tf.Session() as sess:
start = time.time()
image_crop, bboxs_crop = sess.run(crop_image(file_list[i], bboxs_list[i], sess))
print( 'Done image %d th in %d ms \n'% (i, ((time.time() - start)*1000)))
labels, bboxs = filter_bbox(labels_list[i], bboxs_crop)
save_image(image_crop, ntpath.basename(file_list[i]))
save_desc_file(file_list[i], labels, bboxs)
原始代码的时间成本将从200毫秒增加到甚至20000毫秒。在修改之后,日志消息表明在运行期间创建了多个图形和张量流设备,为什么会这样?
python random_crop_images_hongyuan.py我 tensorflow / stream_executor / dso_loader.cc:135]成功打开了CUDA 库libcublas.so.8.0本地我 tensorflow / stream_executor / dso_loader.cc:135]成功打开了CUDA 本地图书馆libcudnn.so.5我 tensorflow / stream_executor / dso_loader.cc:135]成功打开了CUDA 本地库libcufft.so.8.0我 tensorflow / stream_executor / dso_loader.cc:135]成功打开了CUDA 本地库libcuda.so.1我 tensorflow / stream_executor / dso_loader.cc:135]成功打开了CUDA 库libcurand.so.8.0本地W tensorflow / core / platform / cpu_feature_guard.cc:45] TensorFlow 库没有被编译为使用SSE3指令,但这些是 在您的机器上可用,可以加快CPU计算。 w ^ tensorflow / core / platform / cpu_feature_guard.cc:45] TensorFlow 库没有被编译为使用SSE4.1指令,但这些是 在您的机器上可用,可以加快CPU计算。 w ^ tensorflow / core / platform / cpu_feature_guard.cc:45] TensorFlow 库没有被编译为使用SSE4.2指令,但这些是 在您的机器上可用,可以加快CPU计算。 w ^ tensorflow / core / platform / cpu_feature_guard.cc:45] TensorFlow 库没有被编译为使用AVX指令,但这些是 在您的机器上可用,可以加快CPU计算。 w ^ tensorflow / core / platform / cpu_feature_guard.cc:45] TensorFlow 库没有被编译为使用AVX2指令,但这些是 在您的机器上可用,可以加快CPU计算。 w ^ tensorflow / core / platform / cpu_feature_guard.cc:45] TensorFlow 库没有被编译为使用FMA指令,但这些是 在您的机器上可用,可以加快CPU计算。一世 tensorflow / stream_executor / cuda / cuda_gpu_executor.cc:910]成功 从SysFS读取的NUMA节点具有负值(-1),但必须存在 至少有一个NUMA节点,因此返回NUMA节点为零 tensorflow / core / common_runtime / gpu / gpu_device.cc:885]找到设备0 具有属性:名称:GeForce GT 730M专业:3个未成年人:5 memoryClockRate(GHz)0.758 pciBusID 0000:01:00.0总内存: 982.88MiB可用内存:592.44MiB I tensorflow / core / common_runtime / gpu / gpu_device.cc:906] DMA:0 I tensorflow / core / common_runtime / gpu / gpu_device.cc:916] 0:Y I tensorflow / core / common_runtime / gpu / gpu_device.cc:975]创建 TensorFlow设备(/ gpu:0) - > (设备:0,名称:GeForce GT 730M,pci bus id:0000:01:00.0)在317 ms完成图像3000 th
I tensorflow / core / common_runtime / gpu / gpu_device.cc:975]创建 TensorFlow设备(/ gpu:0) - > (设备:0,名称:GeForce GT 730M,pci 总线ID:0000:01:00.0)在325毫秒内完成图像3001
I tensorflow / core / common_runtime / gpu / gpu_device.cc:975]创建 TensorFlow设备(/ gpu:0) - > (设备:0,名称:GeForce GT 730M,pci 总线ID:0000:01:00.0)在312毫秒内完成图像3002
I tensorflow / core / common_runtime / gpu / gpu_device.cc:975]创建 TensorFlow设备(/ gpu:0) - > (设备:0,名称:GeForce GT 730M,pci 总线ID:0000:01:00.0)在147毫秒内完成图像3003
I tensorflow / core / common_runtime / gpu / gpu_device.cc:975]创建 TensorFlow设备(/ gpu:0) - > (设备:0,名称:GeForce GT 730M,pci 总线ID:0000:01:00.0)在447 ms内完成图像3004
答案 0 :(得分:2)
我的猜测是,这是因为创建会话是一项昂贵的操作。可能会发生在留下with语句时未正确清理会话的情况,因此设备上的每个新分配都将具有较少的可用资源。简而言之,我不建议这样做,而是初始化一个会话并尝试重用它。
编辑: 回答你的评论:一旦退出with-block,会话就会自动关闭。我在this github issue中读到,GPU上的内存只有在整个程序退出时才会真正释放。但我想当你在关闭最后一个会话后分配一个新会话时,Tensorflow将在内部重新使用以前分配的资源。所以,回顾一下,我的回答可能不是很有见地。对不起,如果我引起混淆。
答案 1 :(得分:0)
如果没有看到所有代码,就不可能100%确定,但我猜想crop_image()
函数正在调用各种TensorFlow操作函数来构建图形。
在for
循环中构建图形几乎绝不是一个好主意。 This answer解释了原因:某些操作(例如对新操作的第一个Session.run()
调用)花费的时间与图中的操作数成线性关系。如果在每次迭代中添加更多操作,迭代i
将在i
中执行线性工作,因此总执行时间将是二次的。
代码的修改版本(循环内部带有with tf.Graph().as_default():
块)会更快,因为它会在每次迭代中创建一个新的空tf.Graph
,因此每次迭代都会执行一定量的工作
更有效的解决方案是构建图形和会话一次,使用tf.placeholder()
张量来表示crop_image
的文件名和bbox参数,并在每次迭代中为这些占位符提供不同的值。