【问题标题】:3d Matrix multiplication in numpynumpy中的3d矩阵乘法
【发布时间】:2021-04-11 11:38:55
【问题描述】:

我正在使用 numpy 执行矩阵乘法,但我不知道如何利用 numpy 进行 3d 矩阵乘法。

假设我有一个 3x3 矩阵 a,我将它乘以一个 3x1 向量 b。这将给出一个 3x1 向量 c。

这是在 numpy 中完成的:

# (3, 3) * (3, 1) -> (3, 1)
c = np.matmul(a, b)

好的,现在我想对本质上是 2500 个 3x3 矩阵的 3d 矩阵执行类似的操作。现在我正在做的事情是:

# (2500, 3, 3) * (2500, 3, 1) -> list of (3, 1) vectors with length 2500
C = [np.matmul(a, b) for a, b in zip(A, B)]

返回 (3, 1) 个向量的列表。

我宁愿不循环,而是充分利用 numpy 的矢量化和矩阵/张量产品。有没有什么手术可以做...

# (2500, 3, 3) * (2500, 3, 1) -> (2500, 3, 1)
np.<function>(A, B, <args>)

我看过关于使用 np.tensordot 的资料,但我不知道如何设置坐标轴。

np.tensordot(A, B, axes=???)

【问题讨论】:

    标签: python numpy matrix linear-algebra


    【解决方案1】:

    对于您拥有的 3 维数组(或 3 阶张量),您可以使用 np.einsum doc 进行更复杂的矩阵乘法。在您的特定情况下,您可以使用以下

    >>> import numpy as np
    >>> x = np.random.randint(0, 3, (3, 3, 3))  # shape (3, 3, 3)
    >>> y = np.random.randint(0, 3, (3, 3, 3))  # shape (3, 3, 3)
    >>> np.einsum('ijk,ikl->ijl', x, y)  # still shape (3, 3, 3)
    

    特别是,einsum 表达式'ijk,ikl-&gt;ijl' 表示对于每个ith 矩阵,执行常规矩阵乘法jk,kl-&gt;jl 并将结果放入结果张量(ndarray)的ith 条目中。这个过程的更一般的形式可能是

    np.einsum('...jk,...kl->...jl', x, y)
    

    你可以在你拥有的每个张量(ndarray)前面有任意数量的维度。

    完整示例请参见以下内容:

    >>> import numpy as np
    >>> x = np.random.randint(0, 3, (3, 3, 3))  # shape (3, 3, 3)
    >>> x
    array([[[0, 0, 1],
            [2, 2, 1],
            [2, 1, 1]],
    
           [[2, 0, 2],
            [2, 2, 1],
            [2, 2, 2]],
    
           [[2, 2, 2],
            [1, 1, 2],
            [0, 2, 2]]])
    >>> y = np.random.randint(0, 3, (3, 3, 3))  # shape (3, 3, 3)
    >>> y
    array([[[0, 0, 1],
            [2, 1, 0],
            [0, 0, 2]],
    
           [[1, 2, 0],
            [2, 0, 1],
            [2, 2, 1]],
    
           [[0, 2, 1],
            [0, 1, 0],
            [0, 2, 1]]])
    >>> np.einsum('ijk,ikl->ijl', x, y)
    array([[[ 0,  0,  2],
            [ 4,  2,  4],
            [ 2,  1,  4]],
    
           [[ 6,  8,  2],
            [ 8,  6,  3],
            [10,  8,  4]],
    
           [[ 0, 10,  4],
            [ 0,  7,  3],
            [ 0,  6,  2]]])
    >>> np.einsum('...ij,...jk->...ik', x, y)
    array([[[ 0,  0,  2],
            [ 4,  2,  4],
            [ 2,  1,  4]],
    
           [[ 6,  8,  2],
            [ 8,  6,  3],
            [10,  8,  4]],
    
           [[ 0, 10,  4],
            [ 0,  7,  3],
            [ 0,  6,  2]]])
    

    【讨论】:

    • 这种方法至少快10倍!谢谢!
    【解决方案2】:

    np.matmul(A,B) 工作得很好。您遇到了什么错误?

    In [263]: A,B = np.arange(24).reshape(2,3,4), np.arange(8).reshape(2,4,1)
    

    einsum 解决方案:

    In [264]: np.einsum('ijk,ikl->ijl',A,B)
    Out[264]: 
    array([[[ 14],
            [ 38],
            [ 62]],
    
           [[302],
            [390],
            [478]]])
    In [265]: _.shape
    Out[265]: (2, 3, 1)
    

    matmul 解决方案:

    In [266]: A@B
    Out[266]: 
    array([[[ 14],
            [ 38],
            [ 62]],
    
           [[302],
            [390],
            [478]]])
    

    你的循环:

    In [267]: [np.matmul(a, b) for a, b in zip(A, B)]
    Out[267]: 
    [array([[14],
            [38],
            [62]]),
     array([[302],
            [390],
            [478]])]
    

    matmuldocs:

    If either argument is N-D, N > 2, it is treated as a stack of
    matrices residing in the last two indexes and broadcast accordingly.
    

    【讨论】:

    • 这个答案也有效,我会更新问题描述。当我第一次尝试时,我一定搞砸了我的输入和数组维度。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2016-05-11
    • 2017-02-10
    • 2018-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-09
    相关资源
    最近更新 更多