【问题标题】:Black voodoo of NumPy EinsumNumPy Einsum 的黑巫术
【发布时间】:2014-11-27 02:42:46
【问题描述】:

我得到了一些使用 einsum 函数的工作代码。但是因为 einsum 目前对我来说仍然像black voodoo。我想知道,这段代码实际上在做什么,是否可以使用np.dot 进行优化

我的数据看起来像这样

n, p, q = 40000, 8, 4
a = np.random.rand(n, p, q)
b = np.random.rand(n, p)

而我现有的函数 einsum 函数看起来像这样

f1 = np.einsum("ijx,ijy->ixy", a, a)
f2 = np.einsum("ijx,ij->ix", a, b)

但它的真正作用是什么?我到了这里:每个维度(轴)都由一个标签表示,i 等于第一个轴nj 第二个轴pxy 是不同的标签对于同一轴q。 所以f1的输出数组的顺序是ixy,因此输出形状是40000,4,4 (n,q,q)

但这就是我所知道的。还有

【问题讨论】:

  • 关于Einstein notation 的维基百科文章应该可以帮助您了解einsum 的作用!
  • @Phillip 感谢您的链接!那肯定有帮助。但也让我更加意识到我无法自己优化它,因为这超出了我的想象。
  • 通常einsumdot 一样好。两者都被编译。 dot 只是为特定的维度组合提供了更严格的代码。

标签: python numpy


【解决方案1】:

让我们来玩几个小数组

In [110]: a=np.arange(2*3*4).reshape(2,3,4)

In [111]: b=np.arange(2*3).reshape(2,3)

In [112]: np.einsum('ijx,ij->ix',a,b)
Out[112]: 
array([[ 20,  23,  26,  29],
       [200, 212, 224, 236]])

In [113]: np.diagonal(np.dot(b,a)).T
Out[113]: 
array([[ 20,  23,  26,  29],
       [200, 212, 224, 236]])

np.dot 在第一个数组的最后一个暗淡上运行,第二个到第二个数组的最后一个。所以我必须切换参数,使3 维度对齐。 dot(b,a) 产生一个 (2,2,4) 数组。 diagonal 选择其中的 2 个“行”,然后转置进行清理。另一个einsum 很好地表达了清理工作:

In [122]: np.einsum('iik->ik',np.dot(b,a))

由于 np.dot 生成的数组比原始的 einsum 更大,因此即使底层 C 代码更紧凑,它也不太可能更快。

(奇怪的是,我在复制 np.dot(b,a)einsum 时遇到了问题;它不会生成 (2,2,...) 数组)。

对于a,a 的情况,我们必须做类似的事情——滚动一个数组的轴,使最后一个维度与另一个数组的倒数第二个对齐,执行dot,然后使用diagonal 进行清理和transpose

In [157]: np.einsum('ijx,ijy->ixy',a,a).shape
Out[157]: (2, 4, 4)
In [158]: np.einsum('ijjx->jix',np.dot(np.rollaxis(a,2),a))
In [176]: np.diagonal(np.dot(np.rollaxis(a,2),a),0,2).T

tensordot 是在选定轴上使用dot 的另一种方式。

np.tensordot(a,a,(1,1))
np.diagonal(np.rollaxis(np.tensordot(a,a,(1,1)),1),0,2).T  # with cleanup

【讨论】:

  • 这是一个很好的解释!意识到 einsum 真的很强大,而且实际上现在知道它在做什么就更“可读”了。
  • 至于你的评论,np.einsum('ij,kjl', b, a) == np.dot(b, a)
猜你喜欢
  • 2021-03-02
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多