Python 循环在每次迭代中花费更多时间

Python loop taking more time at each iteration

提问人:Tabs 提问时间:4/10/2017 更新时间:9/30/2022 访问量:1268

问:

我做了一个for循环,尽管操作的变量数量保持不变,但它在每次迭代时的持续时间都奇怪地增加。代码如下:

  • X [N*F]:一个 numpy 数组,其中包含 N 个样本,其中包含 F 个变量 (特征);
  • parts [N]:一个 numpy 数组,其中包含 X 中每个样本的参与者;
  • model_filename:模型模板文件 每个参与者的姓名(即我为每个参与者都有一个模型)

我的目标是将参与者 p 的模型应用于参与者 p 的数据,并保存其输出(即 N 个输出)。

outputs = np.full((X.shape[0],), np.nan)
for curr_part in np.unique(parts):
    print("processing participant {0}".format(curr_part))
    model = load_model(model_filename.format(curr_part)) # I measured the duration of this call (d0)
    idx = (parts == curr_part)
    outputs[idx] = np.squeeze(model.predict(X[idx,:])); # I measured the duration of this call (d1)

d1 和 d0 在循环的每次迭代中都会增加(整个循环在迭代 0 时需要 1.5 秒,在迭代 20 时大约需要 8 秒)。我完全不明白为什么。同样有趣的是,如果我在 ipython 中多次运行代码,只要我不重新启动内核,持续时间就会累积(即在第二次运行迭代 0 中大约需要 8 秒)。当然,我想多次运行代码,因此从长远来看,这个问题至关重要。

我还尝试了以下代码,尽管我无法测量每次调用的时间,但总持续时间大致相同:

unik_parts = np.unique(parts);
models = [(p, load_model(model_filename.format(p))) for p in unik_parts]
outputs = [np.squeeze(m.predict(X[parts == p,:])) for p,m in models]

Python 版本 2.7

模型是来自 keras 的模型

python 数组 numpy keras

评论

2赞 Carcigenicate 4/10/2017
尝试分析它。
1赞 Tabs 4/10/2017
所以我使用 cProfile 模块来获取一些统计数据,我可以看到:对许多函数有 20 次调用(即我的 20 个参与者),load_model 函数被使用了 20 次,平均 4 秒(但我现在调用时间随着插入而增加,有很多对 op.py 和 op_def_library.py 的调用需要时间。但老实说,我的知识止步于此,我在解释这些结果时遇到了麻烦。
0赞 Carcigenicate 4/10/2017
在循环过程中,主要占用 CPU 时间的是什么?
0赞 Tabs 4/10/2017
对不起,我更新了上面的评论(我之前按了回车键并错误地验证了评论)
1赞 hpaulj 4/10/2017
对于 1.5 秒这样的时间,这是对大数据的复杂计算(keras、tensorflow)。很难说迭代之间有什么变化。问题大小?累积的结果?当然,不可能复制您的情况并运行我们自己的测试来弥补您没有告诉我们的内容。

答:

0赞 meowmeow 9/30/2022 #1

在预处理数据时,我已经见过很多次这种情况;通常,根据我的经验,每次迭代后内存使用量都会逐渐增加,随后的每次迭代都会略微减慢。

我发现解决这个问题的最简单方法是将任务分成不同的流程,然后使用编排流程来管理程序流。

完成每个任务后,将剔除关联的流程,并且可以继续将资源分配给流程中的下一个任务。这对于保持长时间运行的进程清晰最有帮助。

您可以按以下方式构建流程:

Parent Process
     |_ Pickle Input to Child Proc
     |_ Trigger Child Proc
            |_ Collect Input
            |_ Complete Task
            |_ Pickle Output
     |_ Collect Output



Parent Process -> pickle input -> Child Process
      ^                              |
      |                              |
      ----------------pickle output <-

管理任务流可以执行的操作之一是创建一个 ID 并使用它来创建一个空文件,然后将该 ID 传递给子进程,并在工作完成后将其与子进程一起删除。这是父进程知道子进程已完成的一种简单方便的方法。