过去我一直在使用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,)
答案 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
可以看到您尝试计算的整个表达式,因此它应该能够找到执行此计算的相对最佳方式。