将图像预测存储到csv的Python for循环会快速启动但会变慢

时间:2018-08-19 15:48:14

标签: python performance csv for-loop tensorflow

我有一个包含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

2 个答案:

答案 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之后执行一次 ,而不要在内部重复循环。