保持维度不受numpy tensordot的影响

时间:2017-07-21 01:56:50

标签: python numpy

过去我一直在使用np.tensordot而没有任何问题,但是我目前的例子,我很难理解结果。

对于np.tensordot(d * y, r, axes=((1, 2, 3), (2, 3, 4))).shape,我希望形状为(6, 5),但我得(6, 6, 5)。当我在axis0上运行tensordot 6次时,我会得到预期的结果,但我宁愿tensordot在一次通话中为我做这个。这有什么问题?

>>> import numpy as np
>>> d = np.random.rand(6, 7, 1, 2)
>>> y = np.random.rand(6, 7, 1, 2)
>>> r = np.random.rand(6, 5, 7, 1, 2) > 0.5
>>> 
>>> np.tensordot(d * y, r, axes=((1, 2, 3), (2, 3, 4))).shape
(6, 6, 5)
>>> np.tensordot((d * y)[0], r[0], axes=((0, 1, 2), (1, 2, 3))).shape
(5,)
>>> np.tensordot((d * y)[1], r[1], axes=((0, 1, 2), (1, 2, 3))).shape
(5,)
>>> np.tensordot((d * y)[2], r[2], axes=((0, 1, 2), (1, 2, 3))).shape
(5,)
...
>>> np.tensordot((d * y)[5], r[5], axes=((0, 1, 2), (1, 2, 3))).shape
(5,)

2 个答案:

答案 0 :(得分:1)

考虑一个更简单的案例:

In [709]: d=np.ones((6,2));
In [710]: np.tensordot(d,d,axes=(1,1)).shape
Out[710]: (6, 6)

这相当于:

In [712]: np.einsum('ij,kj->ik',d,d).shape
Out[712]: (6, 6)

这不是ij,ij->i。它是未列出的轴上的外部产品,而不是元素1的元素。

您有(6, 7, 1, 2)(6, 5, 7, 1, 2),想要总结(7,1,2)。它在(6)和(6,5)上做了外部产品。

我想,

np.einsum('i...,ij...->ij',d,r)可以做你想要的。

在幕后,tensordot重新整形并交换轴,以便问题成为2d np.dot来电。然后根据需要重新整形并换回。

校正;我不能使用省略号来表示“点缀”。尺寸

In [726]: np.einsum('aijk,abijk->ab',d,r).shape
Out[726]: (6, 5)

和方法:

In [729]: (d[:,None,...]*r).sum(axis=(2,3,4)).shape
Out[729]: (6, 5)

定时

In [734]: timeit [np.tensordot(d[i],r[i], axes=((0, 1, 2), (1, 2, 3))) for i in 
     ...: range(6)]
145 µs ± 514 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [735]: timeit np.einsum('aijk,abijk->ab',d,r)
7.22 µs ± 34.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [736]: timeit (d[:,None,...]*r).sum(axis=(2,3,4))
16.6 µs ± 84.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

另一种解决方案,使用@(matmul)运算符

In [747]: timeit np.squeeze(d.reshape(6,1,14)@r.reshape(6,5,14).transpose(0,2,1))
11.4 µs ± 28.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

答案 1 :(得分:1)

tensordot中,每个轴都是:

  • 总结(缩小)并因此从最终结果中消除,或
  • 不受影响(且不受约束)并且在求和中恰好出现一次。

所以当你写tensordot(d * y, r, axes=((1, 2, 3), (2, 3, 4)))时,你正在计算:

T[i j k] = ∑[l m n] dy[i l m n] r[j k l m n]

其中dy ≡ d * y。您想要计算的是

T[i k] = ∑[l m n] dy[i l m n] r[i k l m n]

请注意i出现两次,但未被总结。这意味着i实际上是约束(将其视为隐式Kronecker delta)。因此,这不是tensordot可以自行完成的事情。

最简单的方法是使用einsum并明确声明您想要的内容:

np.einsum("i l m n, i k l m n -> i k", d * y, r)

由于einsum可以看到您尝试计算的整个表达式,因此它应该能够找到执行此计算的相对最佳方式。