【问题标题】:Avoiding nested for loops in Python避免 Python 中的嵌套 for 循环
【发布时间】:2018-03-04 09:45:02
【问题描述】:

我有一个矩阵A,维度为1024 * 307200,另一个矩阵B,维度为1024 * 50。我在嵌套的 for 循环中对这两个矩阵执行 L2_norm 以得到我的最终矩阵 C307200 * 50
您可以在下面找到代码:

    for i in range(307200):
        for l in range(50):
            C[i,l] =  numpy.linalg.norm(A[:,i] - B[:,l]))      

如您所见,我的变量的维度很大,这会导致非常高的延迟。我想避免这种嵌套循环,因为对于il 的每个值,我使用的是相同的函数。

有没有办法优化上面的循环?

【问题讨论】:

  • “相同的功能”不是决定性因素。假设您想将所有元素设置为 1:那么您仍然必须遍历所有元素。但也许你可以把它变成一个generator
  • 您可以将 i 和 l 制作成包含给定范围内的整数的 numpy 数组,初始化所有结果的空数组,并使用 numpy 向量化通过 i 和 l 填充空数组的值。

标签: python numpy matrix nested-loops


【解决方案1】:

也许您可以用这些矩阵运算替换内部循环和您的函数?

for i in range(307200):
    temp = A[:,i,np.newaxis] - B[:]
    C[i,:] = np.linalg.norm(temp, axis=0)

对于较小的阵列,我的运行时间减少了大约 20 倍。或许你收获更多。在任何情况下,请确保您获得了良好的结果(在较小的集合上)。

【讨论】:

  • 感谢您的回答。其实我喜欢你的想法。我刚刚编辑了这个问题,因为我使用 numpy.linalg.norm 代替了我以前的函数。您能否对修改后的问题的答案进行必要的更改。 +1 已经给出:)
  • 它工作正常 :) 我之前的代码花了 280 秒,现在是 27 秒。谢谢
  • @Sansk:如果某个特定答案对您有所帮助,请考虑 accepting it
  • @usr2564301 感谢您让我知道 stackoverflow 中的这个拇指规则。刚刚接受了答案:)
【解决方案2】:

更新:随着 OP 的更新和澄清,事情变得更加简单:

>>> def f_pp(A, B):
...     return np.sqrt(np.add.outer(np.einsum('ij,ij->j', A, A), np.einsum('il,il->l', B, B)) - 2*np.einsum('ij,il->jl', A, B))
... 

结束更新

您可以使用np.einsum 和实数运算来大幅提升速度:

>>> def f_pp(A, B):
...     Ar = A.view(float).reshape(*A.shape, 2)
...     Br = B.view(float).reshape(*B.shape, 2)
...     return np.sqrt(np.add.outer(np.einsum('ijk,ijk->j', Ar, Ar), np.einsum('ilk,ilk->l', Br, Br)) - 2*np.einsum('ijk,ilk->jl', Ar, Br))
... 

对于形状(1024, 3072)(1024, 50),我得到的系数约为40

一些解释:

真正的算术:除非 numpy 进行一些令人难以置信的智能优化,否则我希望像 x*x.conj() 这样的复杂产品使用 4 个实数乘法。知道结果是真实的,我们保存了其中的两个。

|A-B|^2 写为|A|^2 + |B|^2 - 2|A*B|。这通过避免直接广播方法将使用的形状为(1024, 3072, 50)(完整示例中为(1024, 307200, 50))的巨大中间A-B 来节省内存。

【讨论】:

  • 感谢您的回答。其实我喜欢你的想法。我刚刚编辑了这个问题,因为我使用 numpy.linalg.norm 代替了我以前的函数。您能否对修改后的问题的答案进行必要的更改。 +1 已经给出:)
  • @Sansk 你的改变会改变你的结果吗?另外,您的数组实际上是complex 类型的吗?你的第一个版本建议它。
  • 对不起,混淆了,实际上它们并不复杂,我发现我不需要为 L2 范数做共轭,我只是使用 nump.linalg 来计算范数。然而,结果并没有太大变化。无论如何,感谢您的代码,我知道了一些新东西。如果您想根据我的更改修改您的答案,您可以随意进行。 :)
猜你喜欢
  • 2017-08-27
  • 2017-11-11
  • 2012-06-25
  • 1970-01-01
  • 2017-09-20
  • 2023-03-15
  • 2015-01-24
  • 1970-01-01
  • 2021-08-19
相关资源
最近更新 更多