【问题标题】:Tensorflow slowing down in a loopTensorFlow 在循环中减速
【发布时间】:2019-11-21 15:52:20
【问题描述】:

我有多个循环运行的算法。包含 tensorflow 的那些在多次迭代后确实会变慢。

每个文件列表大约有 10,000 个文件,具体取决于它使用的算法。我一次遍历文件列表一个文件,从每个文件创建一个数据框,在数据框上运行我的算法,然后将结果写入数据库。看起来像:

file_list = self.get_files()
for file in file_list:
   data = self.get_data(file.fileid)
   result = self.get_result(data)
   self.write_result

get_result 是针对不同算法的不同函数。他们通常需要 0 - 5 秒来计算每个文件的结果。

我正在使用一种算法,在循环开始时每秒处理 2 个文件,但在几百个文件之后,它会减慢到每个文件一分钟。检查代码它必须是 TF 是瓶颈,因为其余代码相对微不足道。

get_result 中有以下行,我认为是罪魁祸首:

z = self.evaluate_risk(normalized_X)

def evaluate_risk(self, X):    

    with tf.device('/cpu:0'):
        with tf.Session() as sess:
            tf.saved_model.loader.load(sess, model.pb)
            graph = tf.get_default_graph()
            input_x = graph.get_tensor_by_name("input:0")
            risk = graph.get_tensor_by_name("risk:0")
            z = sess.run(risk, {input_x: X})
            sess.close()
            del sess
            del graph
    return z

鉴于我使用的是with,我不明白为什么这个函数会导致任何问题。我已经添加了sess.close()del sessdel graph,但我仍然遇到同样的问题。

每次我有一个新文件并到达 result 时,我都应该从头开始 tensorflow。我的循环变慢有什么明显的原因吗?我猜 tensorflow 的某些部分没有重置。

【问题讨论】:

  • 主要问题在于tf.saved_model.loader.load,它会多次加载模型并用副本填充图形。您可以修复用with tf.Graph().as_default(), tf.Session() as sess: 替换with tf.Session() as sess:,但理想情况下,您应该在该函数之外加载模型一次。为了使事情稍微快一点,您也可以只调用一次get_tensor_by_name(例如,将返回值保存在self.input_x 等实例变量中)。延迟的第二个来源是会话的创建,创建单个会话并重用它要快得多。
  • 在这里加载一次是行不通的。我的 file_list 随着时间的推移而增加,在运行 10k 之后,我预计每天添加约 10 个文件。该模型是根据历史数据开发的,现在用于新的传入数据。为什么del graph 行不处理用副本填充图表?
  • KeyError: “名称‘input:0’指的是一个不存在的张量。操作‘input’在图中不存在。”
  • 认为file_list 的更改不会影响此处,您只会将{input_x: X} 中的不同X 传递给同一个图表。 del graph 不执行任何操作,您使用的是默认图,并且仅删除了该引用,但 TensorFlow 内部仍引用它(因此未释放)。

标签: python performance loops tensorflow


【解决方案1】:

没有看到一个完整的例子很难说什么是最好的解决方案,但通常我只会加载一次模型(可能在它自己的图表中)并只创建一个会话,然后在evaluate_risk中使用它.这应该会显着减少每次调用的开销。你可以这样做:

def __init__(self):
    # ... init code
    self.graph = tf.Graph()  # Have the model live in its own graph
    with self.graph.as_default(), tf.device('/cpu:0'):
        self.session = tf.Session()
        tf.saved_model.loader.load(self.session, model_pb)
        self.input_x = self.graph.get_tensor_by_name("input:0")
        self.risk = self.graph.get_tensor_by_name("risk:0")

def __del__(self):
    # Ensure the session is closed when the object is deleted
    # (or do it in another method, or make the object work as a context manager, ...)
    self.session.close()

def evaluate_risk(self, X):
    return self.session.run(self.risk, {self.input_x: X})

编辑:在__del__ 方法中关闭会话可能是多余的,因为原则上当对象被删除时,它的会话也将被关闭,从而关闭。但是,它避免了有人在对象中获取对会话的引用(如obj_session = my_object.session)的潜在问题,这可能导致会话未按预期关闭。它还使会话预计何时关闭更加清晰。

【讨论】:

  • 我会看看这是否有效。 evaluate_risk 只为每个 get_results 调用一次(每个文件也是如此)。我大约有 100 种算法以这种方式运行,但也许我应该为少数使用 tensorflow 的算法编写不同的结构,并在遍历文件列表之前创建会话/图形?
  • @Olivia 好吧,我不知道这在您的大局中是否适合。正如我所说,如果在您的原始代码中,您只需将with tf.Session() as sess: 替换为with tf.Graph().as_default(), tf.Session() as sess:,至少它应该阻止它变得越来越慢(它会在每次调用时创建一个新图表然后丢弃它,所以有将是创建会话和加载模型的开销,但它应该始终相同)。您可以决定在您的情况下什么才是真正的最佳解决方案。
  • 当我在第一条评论中尝试解决方案时:tf.Graph().as_default(), tf.Session() as sess:, 它失败了,然后当我交换它时:tf.Session() as sess,tf.Graph().as_default():, 我得到 KeyError:“名称 'input:0' 指的是张量不存在。图中不存在“输入”操作。”
  • @Olivia Yea 把它转过来是行不通的,因为在默认图表中创建了sess,但是默认设置了一个不同的图表,所以它会在图表中加载模型sesstf.get_default_graph() 会返回一个不同的(空的)。 with tf.Graph().as_default(), tf.Session() as sess: 有什么错误?
  • 它正在工作。我使用它然后在我的原始代码中注释掉 graph = 行。不知道为什么它之前失败了,我会责怪 pycharm 大声笑。让我们看看这是否更快。我无法实现你的方法。在我的 py 文件中,顶部有 __all__ = ['algorithm1'],然后是用于数据转换的函数。然后是一个名为 dataloader 的类,其中包含一些转换数据的函数,因此它可以与 tensorflow 一起使用。然后是 algorithm1 类,其中包含 evaluate_risk 然后是 get_results。如果我将你的 3 个函数放在 __all__ 下,它会告诉我 init 有 2 个位置参数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-20
  • 2013-01-15
  • 1970-01-01
  • 2016-07-11
  • 2012-03-03
  • 2021-01-20
  • 2017-12-28
相关资源
最近更新 更多