Python - 矩阵外积

17
给定两个矩阵。
A: m * r
B: n * r

我希望生成另一个矩阵C:m * n,每个条目C_ij是通过计算A_iB_j的外积得出的矩阵。

例如:

A: [[1, 2],
    [3, 4]]

B: [[3, 1],
    [1, 2]]

提供

C: [[[3, 1],  [[1 ,2],
     [6, 2]],  [2 ,4]],
     [9, 3],  [[3, 6],
     [12,4]],  [4, 8]]]

我可以使用for循环来做到这一点,就像这样

    for i in range (A.shape(0)):
      for j in range (B.shape(0)):
         C_ij = np.outer(A_i, B_j)

我想知道是否有一种向量化的方式来进行这个计算,以加快速度?


2
你想要一个 4D (m, n, r, r) 形状的数组,还是想要一个 2D (m, n) 形状的 object 数据类型的数组,其中每个元素都是另一个数组?我强烈建议选择第一种选项,但根据你的描述,似乎更接近第二种。 - user2357112
抱歉造成困惑,但我更喜欢第一个,一个4D(m,n,r,r)形状的数组。 - Lei Yu
5个回答

18

爱因斯坦符号表示了这个问题很好

In [85]: np.einsum('ac,bd->abcd',A,B)
Out[85]: 
array([[[[ 3,  1],
         [ 6,  2]],

        [[ 1,  2],
         [ 2,  4]]],


       [[[ 9,  3],
         [12,  4]],

        [[ 3,  6],
         [ 4,  8]]]])

1
哎呀,我应该学一下那种标记法。每次有人在回答中使用它时,它总是比我想出的要短得多。如果你知道爱因斯坦求和约定,可能更易懂。 - user2357112

9
temp = numpy.multiply.outer(A, B)
C = numpy.swapaxes(temp, 1, 2)

NumPy的ufuncs,例如multiply,有一个outer方法,几乎可以实现你想要的功能。以下代码:

temp = numpy.multiply.outer(A, B)

该函数产生一个结果,使得temp[a, b, c, d] == A[a, b] * B[c, d]。你需要C[a, b, c, d] == A[a, c] * B[b, d]。调用swapaxes可以重新排列temp的顺序,以便按照你的需求来使用。


1

使用Numpy数组广播的简单解决方案

由于您想要 C_ij = A_i * B_j,这可以通过对列向量A和行向量B进行逐元素乘积的numpy广播来简单实现,如下所示:

# import numpy as np
# A = [[1, 2], [3, 4]]
# B = [[3, 1], [1, 2]]
A, B = np.array(A), np.array(B)
C = A.reshape(-1,1) * B.reshape(1,-1)
# same as: 
# C = np.einsum('i,j->ij', A.flatten(), B.flatten())
print(C)

Output:

array([[ 3,  1,  1,  2],
       [ 6,  2,  2,  4],
       [ 9,  3,  3,  6],
       [12,  4,  4,  8]])

你可以使用numpy.dsplit()numpy.array_split(),按以下方式获取你所需的四个子矩阵:
np.dsplit(C.reshape(2, 2, 4), 2)
# same as:
# np.array_split(C.reshape(2,2,4), 2, axis=2)

Output:

[array([[[ 3,  1],
         [ 6,  2]],

        [[ 9,  3],
         [12,  4]]]), 
array([[[1, 2],
         [2, 4]],

        [[3, 6],
         [4, 8]]])]


1

您可以使用

C = numpy.tensordot(A, B, axes=0)

tensordot做的正是你想要的。参数axes用于在执行求和时沿着特定的轴进行操作(对于二维及以上的张量,默认值为2,它将消除每个数组的两个轴),但通过将其设置为0,它不会减少维度,而是保持整个外积。


0

使用numpy

In [1]: import numpy as np

In [2]: A = np.array([[1, 2], [3, 4]])

In [3]: B = np.array([[3, 1], [1, 2]])

In [4]: C = np.outer(A, B)

In [5]: C
Out[5]: 
array([[ 3,  1,  1,  2],
       [ 6,  2,  2,  4],
       [ 9,  3,  3,  6],
       [12,  4,  4,  8]])

一旦您获得了所需的结果,您可以使用numpy.reshape()将其塑造成几乎任何您想要的形状;

In [6]: C.reshape([4,2,2])
Out[6]: 
array([[[ 3,  1],
        [ 1,  2]],

       [[ 6,  2],
        [ 2,  4]],

       [[ 9,  3],
        [ 3,  6]],

       [[12,  4],
        [ 4,  8]]])

1
我有点惊讶这个视觉效果与 OP 想要的如此接近,但尽管相似,它仍然不完全正确。如果你将期望的结果可视化为一个大的二维网格,并在单元格中放置小的二维网格,那么通过在边缘粘合所有小网格以制作一个网格,就可以得到这样的结果。 - user2357112
同意你的看法。也许可以先执行 np.outer(A, B),然后将其分成更小的网格? - Lei Yu
但这将会得到不同的结果。我想要内部矩阵分别为[[3, 1], [6, 2]][[1, 2], [2, 4]][[9, 3], [12, 4]][[3, 6], [4, 8]] - Lei Yu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接