【问题标题】:Multiply (a x a) square matrix by (a x c x d) matrix将 (a x a) 方阵乘以 (a x c x d) 矩阵
【发布时间】:2014-12-23 03:46:01
【问题描述】:

假设我有一个(c x d) 矩阵列表。比如说我有a。每个矩阵都有a 系数。

在 NumPy 中是否有一种快速的方法可以一次将每个矩阵乘以它的系数,同时仍然保持张量数据结构,或者我是否需要在 for 循环中手动完成,即X = np.array([np.multiply(coefs[i], X[i]) for i in range(len(coefs))])

X.shape = (3, 4, 5), coefs.shape = (3).

【问题讨论】:

  • 你能通过写一个你想要的数学公式来澄清吗?
  • 您的示例 X.shape, coeffs.shape 是 3-D 和 1-D - 这与标题冲突...... 2-D 乘以 3-D。
  • 替代列表理解:[np.multiply(a, b) for a, b in zip(X, coeffs)]
  • @Andrew,想知道你是否最终考虑了我的建议,np.einsum,然后?
  • 嘿!我感谢您写下答案,但不幸的是,正如您所提到的,对于大型数据集,速度并没有很大的提高,而且对于像我正在做的那样一次性使用,多功能性并不是主要优势。不过谢谢!

标签: python numpy matrix


【解决方案1】:
X = np.array([[[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1]],
              [[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1]],
              [[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1]]])

coeffs = np.array([2,4,6])

您需要将坐标轴添加到coeffs,以便它将在您想要的维度中广播。

>>> X * coeffs[:, np.newaxis, np.newaxis]
array([[[2, 2, 2, 2, 2],
        [2, 2, 2, 2, 2],
        [2, 2, 2, 2, 2],
        [2, 2, 2, 2, 2]],

       [[4, 4, 4, 4, 4],
        [4, 4, 4, 4, 4],
        [4, 4, 4, 4, 4],
        [4, 4, 4, 4, 4]],

       [[6, 6, 6, 6, 6],
        [6, 6, 6, 6, 6],
        [6, 6, 6, 6, 6],
        [6, 6, 6, 6, 6]]])
>>> 

np.newaxis 允许coeffs 的值与X 的第一个维度对齐,然后它们在其余维度中为broadcast

【讨论】:

    【解决方案2】:

    解决方案:

    这是采用pythonic方式的经典问题。下面的 1-liner 使用强大的 einstein sumnp.einsum() 函数和丰富的下标标签框架来解决问题,这些框架允许 broadcasting 的维度和控制输出:

    np.einsum('ijk,i...->ijk', X, coeffs)
    

    下标字符串:

    • 逗号, 将左侧第一个操作数的下标标签与右侧第二个操作数的下标标签分开。
    • -> 符号后的下标标签给出了输出的尺寸。
    • 省略号... 将允许将coeffs 向量广播到X 矩阵的额外2 维中(... 实际上对于更明确的广播规范是可选的)

    这需要一点时间来适应,但有许多矩阵向量运算,如tracediaginner/outer productelement-wise multiplication 等,都集中在这个强大的公式中。

    言下之意:

    所以字符串本质上是说取X矩阵的所有维度并将其与coeffs向量逐元素相乘(在X的额外维度上广播)并生成一个相同维度的输出矩阵和X一样。

    输出:

    >>> np.einsum( 'ijk,i->ijk', np.ones((2,2,2)), np.array([2,4]))
    
    array([[[ 2.,  2.],
            [ 2.,  2.]],
    
           [[ 4.,  4.],
            [ 4.,  4.]]])
    

    在二维情况下,沿任一轴逐元素:

    >>> np.einsum( 'ij,i->ij', np.ones((2,2)), np.array([2,4]))
    array([[ 2.,  2.],
           [ 4.,  4.]]) 
    
    >>> np.einsum( 'ij,j->ij', np.ones((2,2)), np.array([2,4]))
    array([[ 2.,  4.],
           [ 2.,  4.]])
    

    替代方案:

    当然,您可以使用np.newaxis 参数和简单的乘法在额外维度上手动广播向量:

    X * coeff[:, np.newaxis, np.newaxis]
    

    时间:

    使用 ipython line magic 函数%timeit 我们实际上可以看到einsum 对于大型数据集可能会贵一点:

    >>> %timeit np.einsum('ijk,i...->ijk',np.ones((10,100,100)),np.ones(10))
    1000 loops, best of 3: 209 µs per loop
    
    >>> %timeit np.ones((10,100,100))*np.ones(10)[:,np.newaxis, np.newaxis]
    1000 loops, best of 3: 129 µs per loop
    

    然后权衡einstein sum 提供的多功能性。

    P.S.如果你是一个痴迷于它提供的丰富索引框架的 Matlab 用户,你可能有兴趣查看这个 Numpy-Matlab comparison 页面

    【讨论】:

    • 'ijk,i->ijk' 也可以。 'i...,i->i...' 也是如此。与早期的 einsum 文档相反,省略号不允许广播(始终允许),而是用于未命名维度的占位符。
    • 同意省略号的使用是可选的。但是,i...,i->i... 表示对我不起作用。
    • 'i...,i->i...' 是否给出错误?它曾经反对遗漏的省略号。但这已在最近的版本中进行了修补。
    • 这是错误:ValueError: operand 1 did not have enough dimensions to match the broadcasting, and couldn't be extended because einstein sum subscripts were specified at both the start and end
    • 关于你的Alternative 声明:这个einsum 表示法在广播一个操作数跨越另一个操作数的额外维度时是否做除了简单乘法之外的任何事情,并且该表示法是否以某种方式自动实现 i> 而不是手动
    猜你喜欢
    • 2021-07-25
    • 2012-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-04
    • 1970-01-01
    • 2021-04-25
    相关资源
    最近更新 更多