【问题标题】:Faster calculation of squared norm in Cython在 Cython 中更快地计算平方范数
【发布时间】:2018-05-01 23:55:26
【问题描述】:

我想计算平方范数,可以写成

W 是一个矩阵,有 V 行。 u 是一个向量。我有两个 numpy 对象 W 和 u。

import numpy as np
import numpy.random as npr
V = 10
W = npr.normal(size=(V, 3))
u = npr.normal(size=(1,3))

如果我逐行计算,我可以这样做:

res = np.zeros(V)
for v in range(V):
    res[v] = (W[v] - u).dot((W[v] - u).transpose())

但是一旦 V 变大(比如 5000)它可能会很慢,我需要一次又一次地重新计算它。 所以我尝试了矩阵乘法,但没有成功,因为它不是逐行乘法。

((W - u).transpose()).dot(W - u)

如何在 Numpy 中快速计算平方范数?

我打算使用 Cython,所以循环中的逐行乘法会比 Numpy 快吗?我了解到 Cython 有parallelization,但如果我在里面使用 Numpy 对象,Cython 似乎无法并行化for loop(添加于 11 月 20 日:可能我不能在prange 中使用 Python 对象,但我可以使用 Numpy 对象)。

【问题讨论】:

  • 你也可以使用np.linalg.norm(W-u)
  • 您是否在循环变量中添加了cdef int v 类型?如果没有,那可能会导致一些放缓。但是您对 cython 中的并行化是正确的;如果您在循环中触摸任何 python 对象,则不能使用像 prange 这样的 OpenMP 函数。无论如何,看起来您已经有了更智能的 python 解决方案!
  • @CodeSurgeon 上面我只展示了 Python 版本以便于复制,但我会在 Cython 中适当地定义变量!

标签: python python-3.x performance numpy cython


【解决方案1】:

方法#1

您可以使用基于快速 BLAS 的 np.dot 直接与 NumPy 一起使用,无需任何循环,就像这样 -

res = (W**2).sum(1) + (u**2).sum(1) -2*W.dot(u.ravel())

引入np.einsum 以获得Wnp.inner 的逐行求和,用于u -

res = np.einsum('ij,ij->i',W,W) + np.inner(u,u).ravel() -2*W.dot(u.ravel())

方法说明

在每次迭代中使用(W[v] - u).dot((W[v] - u),我们正在做内点积。因为,我们对W 的所有行执行此操作,在利用广播时转换为((W - u)**2).sum(1)

现在,

(Xik-Yjk)**2 = Xik**2 + Yjk**2 - 2*Xik*Yjk

因此,

sum_k((Xik-Yjk)**2) = sum_k(Xik**2) + sum_k(Yjk**2) - 2*sum_k(Xik*Yjk)

RHS 的最后一个术语基本上是矩阵乘法,我们正在使用 np.dot

方法#2

或者,使用更多的np.einsum,会是这样 -

d = W -u
res = np.einsum('ij,ij->i',d,d)

【讨论】:

  • 如果我在 Cython 中使用 np.dot() 会比在纯 Python 中使用它慢吗?
  • @user2978524 如果您可以在 NumPy 中使用 np.dot,我认为您不会看到使用 cython 有什么好处。
  • 我明白了,你是对的。你能告诉我你是怎么想出第一个答案的(带有 np.dot 的那个)吗?我总是很难用 numpy,我不知道为什么你的答案给出了我想要的,这让我在未来陷入同样的​​问题。你知道任何有用的参考吗?
  • @user2978524 已添加。
猜你喜欢
  • 1970-01-01
  • 2013-08-30
  • 2018-09-13
  • 1970-01-01
  • 2022-08-03
  • 2019-08-16
  • 2014-07-19
  • 2023-04-07
  • 2020-10-20
相关资源
最近更新 更多