保持维度不受numpy tensord的影响

2024-06-02 07:55:54 发布

您现在位置:Python中文网/ 问答频道 /正文

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

对于np.tensordot(d * y, r, axes=((1, 2, 3), (2, 3, 4))).shape,我希望得到(6, 5)的形状,但是我得到了(6, 6, 5)。但是,当我在axis0上运行tensordot6次时,我会得到预期的结果,但我宁愿让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,)

Tags: importnumpyasnprandom例子形状shape
2条回答

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。你想要计算的是

^{pr2}$

请注意,i出现了两次,但没有被求和。这意味着i在这里实际上是受约束的(可以把它看作一个隐式的Kronecker delta)。因此,这并不是tensordot自己就能完成的。在

最简单的方法是使用einsum并显式声明所需的内容:

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

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

考虑一个更简单的例子:

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

这相当于:

^{pr2}$

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

您有(6, 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)

相关问题 更多 >