如果我定义一个形状为X
的数组(2, 2)
:
X = np.array([[1, 2], [3, 4]])
并使用kronecker产品,然后使用重塑输出
np.kron(X, X).reshape((2, 2, 2, 2))
我得到一个结果矩阵:
array([[[[ 1, 2],
[ 2, 4]],
[[ 3, 4],
[ 6, 8]]],
[[[ 3, 6],
[ 4, 8]],
[[ 9, 12],
[12, 16]]]])
但是,当我使用np.tensordot(X, X, axes=0)
时,输出以下矩阵
array([[[[ 1, 2],
[ 3, 4]],
[[ 2, 4],
[ 6, 8]]],
[[[ 3, 6],
[ 9, 12]],
[[ 4, 8],
[12, 16]]]])
这与第一个输出不同。为什么会这样?我在寻找答案时找到了this,但是我不明白为什么这个解决方案有效或如何推广到更高的维度。
我的第一个问题是,你为什么期望它们是一样的?
让我们做kron
而不重塑:
In [403]: X = np.array([[1, 2],
...: [3, 4]])
...:
In [404]: np.kron(X,X)
Out[404]:
array([[ 1, 2, 2, 4],
[ 3, 4, 6, 8],
[ 3, 6, 4, 8],
[ 9, 12, 12, 16]])
可视化动作很容易。
[X*1, X*2
X*3, X*4]
tensordot
通常被认为是np.dot
的推广,能够处理比普通矩阵乘积更复杂的情况(即一个或多个轴上的乘积之和)。但这里没有总结。
In [405]: np.tensordot(X,X, axes=0)
Out[405]:
array([[[[ 1, 2],
[ 3, 4]],
[[ 2, 4],
[ 6, 8]]],
[[[ 3, 6],
[ 9, 12]],
[[ 4, 8],
[12, 16]]]])
当axes
是一个整数而不是一个元组时,这个动作有点难以理解。文档说:
``axes = 0`` : tensor product :math:`a\otimes b`
我只是试图解释当axes
是一个标量时发生了什么(它不是微不足道)How does numpy.tensordot function works step-by-step?
指定axes=0
相当于提供这个元组:
np.tensordot(X,X, axes=([],[]))
在任何情况下,从输出中可以明显看出这个数字转换器产生相同的数字 - 但布局与kron
不同。
我可以复制kron
布局
In [424]: np.tensordot(X,X,axes=0).transpose(0,2,1,3).reshape(4,4)
Out[424]:
array([[ 1, 2, 2, 4],
[ 3, 4, 6, 8],
[ 3, 6, 4, 8],
[ 9, 12, 12, 16]])
那是我换中间2轴。
并且省略了重塑,我从kron
得到了相同的(2,2,2,2):
np.tensordot(X,X,axes=0).transpose(0,2,1,3)
我喜欢np.einsum
的显性:
np.einsum('ij,kl->ijkl',X,X) # = tensordot(X,X,0)
np.einsum('ij,kl->ikjl',X,X) # = kron(X,X).reshape(2,2,2,2)
或使用广播,这2个产品是:
X[:,:,None,None]*X[None,None,:,:] # tensordot 0
X[:,None,:,None]*X[None,:,None,:] # kron