我有一个包含100,000张图像的文件夹,我想使用TensorFlow对其进行分类。我编写了一个for循环,该循环遍历每个图像,返回置信度得分,并将预测结果存储到csv文件中。
问题是:脚本启动速度非常快(对于1-1000张图像,每秒大约10张图像),并且随着每次迭代逐渐变慢(对于> 1000张图像,每秒仅大约1张图像)。
对于Python中for循环的类似减速问题,我读到预分配可能是一种解决方案。但是,我直接写的是csv,而不是列表,所以我不确定这应该有什么帮助。
有什么方法可以确保整个循环期间的速度一致吗?
在此先感谢您提供指导!
请在下面找到基于本教程(https://codelabs.developers.google.com/codelabs/tensorflow-for-poets/#0)的我的代码:
filename = "predictions.csv"
f = open(filename, "w")
headers = "id;image_name;confidence\n"
f.write(headers)
start = 1
end = 20000
testdata = "C:/files/"
files = list(os.listdir(testdata))
for index in range(start, end+1):
filename = files[index]
if not filename.startswith('.'):
print(str(index) + " - " + str(filename))
image=testdata+filename
results = label_image(image, graph, session, input_height=299, input_width=299, input_layer="Mul")
f.write(str(index) + ";" + str(filename) + ";" + str(results[0]) + "\n")
print("\n")
f.close()
编辑:
在运行循环之前,我只加载了一次图形。
from scripts.label_image import load_graph, label_image, get_session
model_file = "retrained_graph.pb"
graph = load_graph(model_file)
session = get_session(graph)
编辑2:
这是label_image函数的代码。
def label_image(file_name, graph, session, label_file="retrained_labels.txt", input_height=224, input_width=224, input_mean=128, input_std=128, input_layer="input", output_layer="final_result"):
t = read_tensor_from_image_file(file_name,
input_height=input_height,
input_width=input_width,
input_mean=input_mean,
input_std=input_std)
input_name = "import/" + input_layer
output_name = "import/" + output_layer
input_operation = graph.get_operation_by_name(input_name);
output_operation = graph.get_operation_by_name(output_name);
start = time.time()
results = session.run(output_operation.outputs[0],
{input_operation.outputs[0]: t})
end=time.time()
results = np.squeeze(results)
top_k = results.argsort()[-5:][::-1]
labels = load_labels(label_file)
print('\nEvaluation time (1-image): {:.3f}s\n'.format(end-start))
template = "{} (score={:0.5f})"
for i in top_k:
print(template.format(labels[i], results[i]))
return results
编辑3:
这是read_tensor_from_image_file函数的代码。
def read_tensor_from_image_file(file_name, input_height=299, input_width=299,
input_mean=0, input_std=255):
input_name = "file_reader"
output_name = "normalized"
file_reader = tf.read_file(file_name, input_name)
if file_name.endswith(".png"):
image_reader = tf.image.decode_png(file_reader, channels = 3,
name='png_reader')
elif file_name.endswith(".gif"):
image_reader = tf.squeeze(tf.image.decode_gif(file_reader,
name='gif_reader'))
elif file_name.endswith(".bmp"):
image_reader = tf.image.decode_bmp(file_reader, name='bmp_reader')
else:
image_reader = tf.image.decode_jpeg(file_reader, channels = 3,
name='jpeg_reader')
float_caster = tf.cast(image_reader, tf.float32)
dims_expander = tf.expand_dims(float_caster, 0);
resized = tf.image.resize_bilinear(dims_expander, [input_height, input_width])
normalized = tf.divide(tf.subtract(resized, [input_mean]), [input_std])
sess = tf.Session()
result = sess.run(normalized)
return result
编辑4:
这是我的重构代码,引发错误: AttributeError:“张量”对象没有属性“ endswith”
def process_image(file_name):
input_name = "file_reader"
output_name = "normalized"
file_reader = tf.read_file(file_name, input_name)
if file_name.endswith(".png"):
image_reader = tf.image.decode_png(file_reader, channels = 3,
name='png_reader')
elif file_name.endswith(".gif"):
image_reader = tf.squeeze(tf.image.decode_gif(file_reader,
name='gif_reader'))
elif file_name.endswith(".bmp"):
image_reader = tf.image.decode_bmp(file_reader, name='bmp_reader')
else:
image_reader = tf.image.decode_jpeg(file_reader, channels = 3,
name='jpeg_reader')
float_caster = tf.cast(image_reader, tf.float32)
dims_expander = tf.expand_dims(float_caster, 0);
resized = tf.image.resize_bilinear(dims_expander, [input_height, input_width])
normalized = tf.divide(tf.subtract(resized, [input_mean]), [input_std])
return normalized
filename_placeholder = tf.placeholder(tf.string)
processed = process_image(filename_placeholder)
def label_image(file_name, graph, session, label_file="tf_files/retrained_labels.txt", input_height=224, input_width=224, input_mean=128, input_std=128, input_layer="input", output_layer="final_result"):
result = sess.run(processed, feed_dict={filename_placeholder: file_name})
input_name = "import/" + input_layer
output_name = "import/" + output_layer
input_operation = graph.get_operation_by_name(input_name);
output_operation = graph.get_operation_by_name(output_name);
start = time.time()
results = session.run(output_operation.outputs[0],
{input_operation.outputs[0]: t})
end=time.time()
results = np.squeeze(results)
top_k = results.argsort()[-5:][::-1]
labels = load_labels(label_file)
print('\nEvaluation time (1-image): {:.3f}s\n'.format(end-start))
template = "{} (score={:0.5f})"
for i in top_k:
print(template.format(labels[i], results[i]))
return results
答案 0 :(得分:0)
仍然可以进行预分配。读取完整文件的大小,然后为任何对象分配内存。
您将文件保存在哪里?如果性能在循环过程中下降,通常表明正在访问不断增长的容器。
尝试将所有信息写入预先分配的数组中,然后一次将所有数据条目写入* .csv中,而不是每个循环一次打开不断增长的* .csv文件。这样可以解决此问题。我假设results = label_image(image, graph, session, input_height=299, input_width=299, input_layer="Mul")
不会打开任何容器,而只能在输入参数上起作用。
答案 1 :(得分:0)
问题出在read_tensor_from_image_file
函数之内。在循环的每次迭代中都调用此函数。在该函数中,您将创建Tensorflow操作。根据经验,tf.anything
调用负责构建计算图。只能将它们称为一次,然后使用tf.Session
重复运行。实际上,您正在不断使用相同图像处理操作的“克隆”来增加计算图的大小,这会随着图的增大而逐渐降低执行速度。
您应该重构代码,以使read_tensor_from_image_file
中的op定义仅执行一次,并且仅在循环内执行sess.run(normalized)
部分。您可以使用tf.placeholder
作为输入(文件名)。另外,您不应在每次调用该函数时都创建一个新会话,而应从label_image
传递该会话。
这里是简化重构代码的示例。假设我们具有创建图像处理操作的功能:
def process_image(file_name):
file_reader = tf.read_file(file_name, input_name)
...
normalized = tf.divide(tf.subtract(resized, [input_mean]), [input_std])
return normalized
这基本上是您的read_tensor_from_image_file
函数,除了涉及会话的最后一部分。您目前的工作基本上是
def label_image(file_name, ...):
processed = process_image(file_name)
sess = tf.Session()
result = sess.run(processed)
....
for file_name in files:
label_image(file_name, ...)
相反,您应该做的是
filename_placeholder = tf.placeholder(tf.string)
processed = process_image(filename_placeholder)
def label_image(file_name, ...):
result = sess.run(processed, feed_dict={filename_placeholder: file_name})
....
for file_name in files:
label_image(file_name, ...)
重要的区别是我们将process_image
调用移出了循环,而仅将run
移入了循环。此外,我们不会连续创建新的会话。全局变量有点棘手,但您应该了解一下。
我唯一不确定的是您是否可以使用从get_session(graph)
获得的会话来运行processed
张量。如果此操作不起作用(即崩溃),则需要创建第二个会话来运行此任务,但是您仅应在调用process_image
之后执行一次 ,而不要在内部重复循环。