numpy.einsum中的out参数不能按预期工作 [英] The `out` arguments in `numpy.einsum` can not work as expected
问题描述
我有两个邮政编码.第一个是:
I have two piece codes. The first one is:
A = np.arange(3*4*3).reshape(3, 4, 3)
P = np.arange(1, 4)
A[:, 1:, :] = np.einsum('j, ijk->ijk', P, A[:, 1:, :])
,结果A
为:
array([[[ 0, 1, 2],
[ 6, 8, 10],
[ 18, 21, 24],
[ 36, 40, 44]],
[[ 12, 13, 14],
[ 30, 32, 34],
[ 54, 57, 60],
[ 84, 88, 92]],
[[ 24, 25, 26],
[ 54, 56, 58],
[ 90, 93, 96],
[132, 136, 140]]])
第二个是:
A = np.arange(3*4*3).reshape(3, 4, 3)
P = np.arange(1, 4)
np.einsum('j, ijk->ijk', P, A[:, 1:, :], out=A[:,1:,:])
,结果A
为:
array([[[ 0, 1, 2],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]],
[[12, 13, 14],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]],
[[24, 25, 26],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]]])
所以结果是不同的.在这里,我想使用out
来节省内存.是numpy.einsum
中的错误吗?还是我错过了什么?
So the result is different. Here I want to use out
to save memory. Is it a bug in numpy.einsum
? Or I missed something?
顺便说一句,我的numpy
版本是1.13.3.
By the way, my numpy
version is 1.13.3.
推荐答案
我以前没有使用过这个新的out
参数,但是过去曾使用过einsum
,并对它的工作方式有一个大致的了解(或至少以前是这样).
I haven't used this new out
parameter before, but have worked with einsum
in the past, and have a general idea of how it works (or at least used to).
在我看来,它在迭代开始之前将out
数组初始化为零.这将解释A[:,1:,:]
块中的所有0.相反,如果我最初是单独的out
数组,则会插入所需的值
It looks to me like it initializes the out
array to zero before the start of iteration. That would account for all the 0s in the A[:,1:,:]
block. If instead I initial separate out
array, the desired values are inserted
In [471]: B = np.ones((3,4,3),int)
In [472]: np.einsum('j, ijk->ijk', P, A[:, 1:, :], out=B[:,1:,:])
Out[472]:
array([[[ 3, 4, 5],
[ 12, 14, 16],
[ 27, 30, 33]],
[[ 15, 16, 17],
[ 36, 38, 40],
[ 63, 66, 69]],
[[ 27, 28, 29],
[ 60, 62, 64],
[ 99, 102, 105]]])
In [473]: B
Out[473]:
array([[[ 1, 1, 1],
[ 3, 4, 5],
[ 12, 14, 16],
[ 27, 30, 33]],
[[ 1, 1, 1],
[ 15, 16, 17],
[ 36, 38, 40],
[ 63, 66, 69]],
[[ 1, 1, 1],
[ 27, 28, 29],
[ 60, 62, 64],
[ 99, 102, 105]]])
einsum
的Python部分并没有告诉我太多,除了它决定如何将out
数组传递给c
部分(作为tmp_operands
的列表之一):
The Python portion of einsum
doesn't tell me much, except how it decides to pass the out
array to the c
portion, (as one of the list of tmp_operands
):
c_einsum(einsum_str,* tmp_operands,** einsum_kwargs)
c_einsum(einsum_str, *tmp_operands, **einsum_kwargs)
我知道它使用str
定义了轴和迭代,设置了np.nditer
的c-api
等效项.
I know that it sets up a c-api
equivalent of np.nditer
, using the str
to define the axes and iterations.
它会在迭代教程中对此部分进行迭代:
It iterates something like this section in the iteration tutorial:
https://docs .scipy.org/doc/numpy-1.13.0/reference/arrays.nditer.html#reduction-iteration
尤其要注意it.reset()
步骤.在迭代之前将out
缓冲区设置为0.然后,它遍历输入数组和输出数组的元素,将计算值写入输出元素.由于它要进行一定数量的乘积运算(例如out[:] += ...
),因此必须从干净的表盘开始.
Notice in particular the it.reset()
step. That sets the out
buffer to 0 prior to iterating. It then iterates over the elements of input arrays and the output array, writing the calculation values to the output element. Since it is doing a sum of products (e.g. out[:] += ...
), it has to start with a clean slate.
我在猜测实际发生的情况,但是对我来说应该将输出缓冲区归零,这似乎是合乎逻辑的.如果该数组与输入之一相同,则最终将使计算混乱.
I'm guessing a bit as to what is actually going on, but it seems logical to me that it should zero out the output buffer to start with. If that array is the same as one of the inputs, that will end up messing with the calculation.
因此,我认为这种方法不会起作用并节省您的内存.它需要一个干净的缓冲区来累积结果.完成后,您也可以将值写回到A
中.但是考虑到dot
之类产品的性质,您不能将相同的数组用于输入和输出.
So I don't think this approach will work and save you memory. It needs a clean buffer to accumulate the results in. Once that's done it, or you, can write the values back into A
. But given the nature of a dot
like product, you can't use the same array for input and for output.
In [476]: A[:,1:,:] = np.einsum('j, ijk->ijk', P, A[:, 1:, :])
In [477]: A
Out[477]:
array([[[ 0, 1, 2],
[ 3, 4, 5],
[ 12, 14, 16],
[ 27, 30, 33]],
....)
这篇关于numpy.einsum中的out参数不能按预期工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!