矩阵乘法的矢量化

时间:2017-04-06 20:50:00

标签: python numpy

我有很多3 * 2矩阵(A1,A2,A3 ..),每个3 * 2都是平局。在两个绘制的情况下,我们有一个3 * 4(我们水平地堆叠A1,A2的每个绘制)。显然,我更容易将3 * 4矩阵(A)绘制为较大的矩阵,而不是一遍又一次地绘制3 * 2。

但是我需要为每个绘图(每个A1,A2 ......)执行矩阵乘法到矩阵B.说A1 * B和A2 * B ... AN * B

#each draw of the 3*2 matrix
A1 = np.array([[ 0,  1],
              [ 4,  5],
              [ 8,  9]])

A2 = np.array([[ 2,  3],
              [ 6,  7],
              [ 10, 11]])

# A is [A1,A2]
# Easier to draw A once for all  (the larger matrix)
A = np.array([[ 0,  1,  2,  3],
              [ 4,  5,  6,  7],
              [ 8,  9, 10, 11]])

b = np.array([[ 0,  1],
              [ 4,  5]
              ])

desired output
array([[ 4,  5, 12, 17],
       [20, 29, 28, 41],
       [36, 53, 44, 65]])

4 个答案:

答案 0 :(得分:4)

您可以将矩阵A重新整形为2列,使其符合b,进行矩阵乘法,然后重新整形:

np.dot(A.reshape(-1, 2), b).reshape(3, -1)

#array([[ 4,  5, 12, 17],
#       [20, 29, 28, 41],
#       [36, 53, 44, 65]])

答案 1 :(得分:3)

如果您不确定如何存储/堆叠传入的数组,一种方法是将它们堆叠为3D数组,这样每个传入的数组都是 index-able 按第一轴 -

a = np.array((A1,A2))

示例运行 -

In [143]: a = np.array((A1,A2))

In [144]: a.shape
Out[144]: (2, 3, 2)
           |-----------------> axis of stacking

然后,为了获得每个传入数组的矩阵乘法的等效输出b,我们可以使用3D堆叠数组a上的np.tensordot np.matmul b 1}},因此在总和减少中从a和第一个b丢失最后一个轴,如此 -

out = np.tensordot(a,b,axes=((2),(0)))

让我们看一下输出值,并与每个矩阵乘法进行比较A1A2等。

In [138]: out[0]
Out[138]: 
array([[ 4,  5],
       [20, 29],
       [36, 53]])

In [139]: out[1]
Out[139]: 
array([[12, 17],
       [28, 41],
       [44, 65]])

In [140]: A1.dot(b)
Out[140]: 
array([[ 4,  5],
       [20, 29],
       [36, 53]])

In [141]: A2.dot(b)
Out[141]: 
array([[12, 17],
       [28, 41],
       [44, 65]])

因此,基本上使用此堆叠操作,稍后在tensordot上我们有:

out[0], out[1], .... = A1.dot(b), A2.dot(b), ....

替代np.tensordot -

我们可以使用更简单的@ operator版本来获得与tensordot相同的输出 -

out = np.matmul(a,b)

在Python 3.5上,有一个更简单的版本取代np.matmul,{{3}} -

out = a @ b

答案 2 :(得分:3)

即使计算不需要einsum,也可以帮助我们思考问题:

In [584]: np.einsum('ij,jk->ik', A1,b)
Out[584]: 
array([[ 4,  5],
       [20, 29],
       [36, 53]])
In [585]: np.einsum('ij,jk->ik', A2,b)
Out[585]: 
array([[12, 17],
       [28, 41],
       [44, 65]])

A是(3,4),它不会使用(2,2)b。将其视为尝试使用加倍j维度的工作:'i(2j),jk->i?k'。但是如果我们插入轴怎么办? ' IMK,JK-> IMK&#39 ;?或者将额外维度添加到i

In [587]: np.einsum('imj,jk->imk', A.reshape(3,2,2),b)
Out[587]: 
array([[[ 4,  5],
        [12, 17]],

       [[20, 29],
        [28, 41]],

       [[36, 53],
        [44, 65]]])

数字在那里,只是形状是(3,2,2)。

In [590]: np.einsum('imj,jk->imk', A.reshape(3,2,2),b).reshape(3,4)
Out[590]: 
array([[ 4,  5, 12, 17],
       [20, 29, 28, 41],
       [36, 53, 44, 65]])

或者您可以从一开始就构建A,以便mij,jk->mik有效(@Divaker

@Psidom

np.einsum('ij,jk->ik', A.reshape(3,2,2).reshape(-1,2) ,b).reshape(3,-1)

`@ piRSquared':

'kj,jI->kI`

答案 3 :(得分:2)

改变你的观点。您不必要地将自己锁定在3 x 2

您可以将A1A2视为2x3,然后将A视为

array([[ 0,  4,  8,  2,  6, 10],
       [ 1,  5,  9,  3,  7, 11]])

然后进行b = b.T

的转置
array([[0, 4],
       [1, 5]])

这样你就可以进行操作了

b @ A

array([[ 4, 20, 36, 12, 28, 44],
       [ 5, 29, 53, 17, 41, 65]])

让你的"吸引"看起来像这样

A = np.random.randint(10, size=(2, 9))
A

array([[7, 2, 1, 0, 9, 9, 1, 0, 2],
       [8, 6, 1, 6, 6, 2, 4, 2, 9]])

b @ A

array([[32, 24,  4, 24, 24,  8, 16,  8, 36],
       [47, 32,  6, 30, 39, 19, 21, 10, 47]])

​