# 8D array
In [354]: R = np.random.standard_normal((3,5,4,6,8,2,7,9))
# marginalize out axis 5 (i.e. "n" here)
In [363]: esum = np.einsum("ijklmnop -> n", R)
# marginalize out axis 5 (i.e. sum over rest of the axes)
In [364]: nsum = np.sum(R, axis=(0,1,2,3,4,6,7))
In [365]: np.allclose(esum, nsum)
Out[365]: True
In [772]: A
Out[772]:
array([[1, 2, 3],
[4, 2, 2],
[2, 3, 4]])
In [773]: B
Out[773]:
array([[1, 4, 7],
[2, 5, 8],
[3, 6, 9]])
In [774]: np.einsum("ij, ij -> ", A, B)
Out[774]: 124
16)二维和三维阵列乘法
这种乘法在求解线性方程组时非常有用(Ax=b)要验证结果的位置。
# inputs
In [115]: A = np.random.rand(3,3)
In [116]: b = np.random.rand(3, 4, 5)
# solve for x
In [117]: x = np.linalg.solve(A, b.reshape(b.shape[0], -1)).reshape(b.shape)
# 2D and 3D array multiplication :)
In [118]: Ax = np.einsum('ij, jkl', A, x)
# indeed the same!
In [119]: np.allclose(Ax, b)
Out[119]: True
# reshape 3D array `x` to 2D, perform matmul
# then reshape the resultant array to 3D
In [123]: Ax_matmul = np.matmul(A, x.reshape(x.shape[0], -1)).reshape(x.shape)
# indeed correct!
In [124]: np.allclose(Ax, Ax_matmul)
Out[124]: True
让我们制作两个数组,用不同但兼容的维度来突出它们之间的相互作用
你的计算,取一个(2,3)和一个(3,4)的“点”(乘积之和)来产生一个(4,2)数组。
i
是A
的第一个维度,是C
的最后一个维度;k
是B
的最后一个维度,是C
的第一个维度。j
被总和“消耗”。这与
np.dot(A,B).T
相同-这是最后一个被转置的输出。要查看
j
发生的更多情况,请将C
下标更改为ijk
:这也可以通过以下方式产生:
也就是说,在
A
的末尾添加一个k
维度,在B
的前面添加一个i
维度,得到一个(2,3,4)数组。0 + 4 + 16 = 20
、9 + 28 + 55 = 92
等;对j
求和并转置以获得早期结果:如果你能直观地理解^{} 的概念,那么理解它是非常容易的。作为一个例子,让我们从一个简单的描述开始,涉及矩阵乘法。
要使用^{} ,您只需将所谓的下标字符串作为参数传递,然后传递输入数组。
假设你有两个二维数组,分别是
A
和B
,你想做矩阵乘法。所以,你需要:这里,下标字符串
ij
对应于数组A
,而下标字符串jk
对应于数组B
。另外,这里要注意的最重要的一点是,每个下标字符串中的字符数必须与数组的维数匹配。(也就是说,二维数组有两个字符,三维数组有三个字符,等等)如果在下标字符串之间重复这些字符(在我们的例子中,是指在下标字符串之间重复这些字符),那么就意味着希望沿着这些维度发生ein
sum。这样,它们的总和就会减少。(即,该维度将消失)在这个
->
之后的下标字符串将是我们的结果数组。 如果将其保留为空,则将对所有内容进行求和,并作为结果返回标量值。否则生成的数组将根据下标字符串具有维数。在我们的示例中,它将是ik
。这是直观的,因为我们知道,对于矩阵乘法,数组中的列数必须与数组中的行数相匹配,这就是这里所发生的情况(即,我们通过重复下标字符串中的charj
)这里有更多的例子,简明扼要地说明了^{} 在实现一些常见的张量或nd数组操作时的使用/能力。
输入
1)矩阵乘法(类似于
np.matmul(arr1, arr2)
)2)沿着主对角线提取元素(类似于
np.diag(arr)
)3)Hadamard积(即两个数组的元素积)(类似于
arr1 * arr2
)4)按元素的平方(类似于
np.square(arr)
或arr ** 2
)5)痕量(即主要对角线元素之和)(类似于
np.trace(arr)
)6)矩阵转置(类似于
np.transpose(arr)
)7)外积(向量的)(类似于
np.outer(vec1, vec2)
)8)内积(矢量)(类似于
np.inner(vec1, vec2)
)9)沿0轴求和(类似于
np.sum(arr, axis=0)
)10)沿轴1求和(类似于
np.sum(arr, axis=1)
)11)批量矩阵乘法
12)沿轴2求和(类似于
np.sum(arr, axis=2)
)13)对数组中的所有元素求和(类似于
np.sum(arr)
)14)多轴求和(即边缘化)
(类似于
np.sum(arr, axis=(axis0, axis1, axis2, axis3, axis4, axis6, axis7))
)15)Double Dot Products(类似于np.sum(hadamard乘积)cf.3)
16)二维和三维阵列乘法
这种乘法在求解线性方程组时非常有用(Ax=b)要验证结果的位置。
相反,如果必须使用^{} 进行此验证,则必须执行两次
reshape
操作才能获得相同的结果,如:奖励:在这里阅读更多数学知识:Einstein-Summation当然也在这里阅读:Tensor-Notation
(注意:这个答案是基于我不久前写的关于
einsum
的一个简短的blog post。)einsum
做什么?假设我们有两个多维数组,
A
和B
。现在假设我们想。。。A
与B
相乘,以创建新的产品阵列;然后可能很有可能
einsum
将帮助我们更快、更高效地完成这项工作,而像multiply
、sum
和transpose
这样的NumPy函数的组合将允许这样做。einsum
如何工作?下面是一个简单(但不是完全琐碎)的例子。使用以下两个数组:
我们将
A
和B
元素相乘,然后沿着新数组的行求和。在“正常”的数字里我们会写:因此,在这里,
A
上的索引操作将两个数组的第一个轴排成一行,以便可以广播乘法运算。然后对产品数组的行求和以返回答案。如果我们想用
einsum
代替,我们可以写:签名字符串
'i,ij->i'
是这里的关键,需要一点解释。你可以把它分成两半。在左边(在->
的左边),我们已经标记了两个输入数组。在->
的右边,我们已经标记了要结束的数组。下面是接下来发生的事情:
A
有一个轴;我们将其标记为i
。并且B
有两个轴;我们将轴0标记为i
,轴1标记为j
。通过在两个输入数组中重复标签
i
,我们告诉einsum
这两个轴应该一起相乘。换句话说,我们将数组A
与数组B
的每一列相乘,就像A[:, np.newaxis] * B
那样。注意,
j
并没有作为标签出现在我们想要的输出中;我们刚刚使用了i
(我们希望以1D数组结束)。通过省略标签,我们告诉einsum
沿着这个轴的和。换句话说,我们对产品行求和,就像.sum(axis=1)
那样。这就是使用
einsum
所需的基本知识。这有助于发挥一点;如果我们在输出中保留两个标签,'i,ij->ij'
,我们将得到一个二维产品数组(与A[:, np.newaxis] * B
相同)。如果我们说没有输出标签,'i,ij->
,我们将返回一个数字(与执行(A[:, np.newaxis] * B).sum()
相同)。然而,关于
einsum
的好处是,它不会首先构建产品的临时数组;它只是对产品进行汇总。这可以大大节省内存使用。稍大一点的例子
为了解释点积,这里有两个新数组:
我们将使用
np.einsum('ij,jk->ik', A, B)
计算点积。下面的图片显示了A
和B
的标签以及我们从函数中获得的输出数组:你可以看到标签
j
是重复的-这意味着我们将A
的行与B
的列相乘。此外,标签j
不包含在输出中-我们正在对这些产品求和。标签i
和k
保留用于输出,因此我们得到一个2D数组。将这个结果与标签
j
是而不是总和的数组进行比较可能更清楚。在下面的左侧,您可以看到由于写入np.einsum('ij,jk->ijk', A, B)
而产生的3D数组(即,我们保留了标签j
):求和轴
j
给出期望的点积,如右图所示。一些练习
为了更好地了解
einsum
,实现熟悉的NumPy是很有用的使用下标符号的数组操作。任何涉及乘法和求和轴组合的内容都可以使用einsum
来编写。设A和B是两个长度相同的一维数组。例如,
A = np.arange(10)
和B = np.arange(5, 15)
。可以写入
A
的和:元素乘法
A * B
,可以写入:内积或点积
np.inner(A, B)
或np.dot(A, B)
可以写为:外部产品
np.outer(A, B)
可以写入:对于二维数组
C
和D
,如果轴是兼容的长度(相同长度或其中一个轴的长度为1),下面是几个示例:可写入
C
(主对角线之和)的轨迹np.trace(C)
:可以写入
C
的元素乘法和D
,C * D.T
的转置:将
C
的每个元素乘以数组D
(构成4D数组),可以写入C[:, :, None, None] * D
:相关问题 更多 >
编程相关推荐