numpy 点积和矩阵积

numpy dot product and matrix product

我正在使用形状为 (N,)、(N,3) 和 (N,3,3) 的 numpy 数组,它们表示 3D 中的标量、向量和矩阵序列 space。我已经实现了逐点点积、矩阵乘法和 matrix/vector 乘法,如下所示:

def dot_product(v, w):
    return np.einsum('ij, ij -> i', v, w)

def matrix_vector_product(M, v):
    return np.einsum('ijk, ik -> ij', M, v)

def matrix_matrix_product(A, B):
    return np.einsum('ijk, ikl -> ijl', A, B)

如您所见,我使用 einsum 是因为没有更好的解决方案。令我惊讶的是,我无法使用 np.dot... 这似乎不适合这种需要。有没有更numpythonic的方式来实现这些功能?

特别是,如果这些函数也可以通过广播第一个缺失的轴来处理形状 (3,) 和 (3,3),那就太好了。我想我需要省略号,但我不太明白如何实现结果。

无法将这些操作重塑为一般的 BLAS 调用,并且对于这种大小的数组,循环 BLAS 调用会非常慢。因此,einsum 可能是此类操作的最佳选择。

您的函数可以用省略号概括如下:

def dot_product(v, w):
    return np.einsum('...j,...j->...', v, w)

def matrix_vector_product(M, v):
    return np.einsum('...jk,...k->...j', M, v)

def matrix_matrix_product(A, B):
    return np.einsum('...jk,...kl->...jl', A, B)

作为工作笔记,这3个计算也可以写成:

np.einsum(A,[0,1,2],B,[0,2,3],[0,1,3])
np.einsum(M,[0,1,2],v,[0,2],[0,1]) 
np.einsum(w,[0,1],v,[0,1],[0])

或者用 Ophion 的概括

np.einsum(A,[Ellipsis,1,2], B, ...)

根据输入数组的维度生成 [0,1,..] 列表应该不难。


通过专注于概括 einsum 表达式,我忽略了一个事实,即您要重现的是 N 小点积。

np.array([np.dot(i,j) for i,j in zip(a,b)])

值得记住的是 np.dot 使用快速编译代码,并专注于数组较大的计算。你的问题是计算许多小点积之一。

并且没有定义轴的额外参数,np.dot 仅执行 2 种可能的组合,可以表示为:

np.einsum('i,i', v1, v2)
np.einsum('...ij,...jk->...ik', m1, m2)

dot 的运算符版本将面临相同的限制 - 没有额外的参数来指定轴的组合方式。

注意 tensordotdot:

所做的概括也很有启发意义
def tensordot(a, b, axes=2):
    ....
    newshape_a = (-1, N2)
    ...
    newshape_b = (N2, -1)
    ....
    at = a.transpose(newaxes_a).reshape(newshape_a)
    bt = b.transpose(newaxes_b).reshape(newshape_b)
    res = dot(at, bt)
    return res.reshape(olda + oldb)

它可以执行 dot 对多个轴求和。但是在转置和整形完成后,计算就变成了二维数组的标准dot


这可能已被标记为重复问题。一段时间以来,人们一直在询问关于做多个点积的问题。

Matrix vector multiplication along array axes 建议使用 numpy.core.umath_tests.matrix_multiply

等于:

matrix_multiply(matrices, vectors[..., None])
np.einsum('ijk,ik->ij', matrices, vectors)

matrix_multiply 注释的 C 文档:

* This implements the function
* out[k, m, p] = sum_n { in1[k, m, n] * in2[k, n, p] }.
来自同一目录的

inner1d(N,n) 向量

做同样的事情
inner1d(vector, vector)  
np.einsum('ij,ij->i', vector, vector)
# out[n] = sum_i { in1[n, i] * in2[n, i] }

两者都是 UFunc,并且可以处理最右侧维度的广播。在numpy/core/test/test_ufunc.py中,这些函数用于执行UFunc机制。

matrix_multiply(np.ones((4,5,6,2,3)),np.ones((3,2)))

补充说这种计算可以用*求和,eg

(w*v).sum(-1)
(M*v[...,None]).sum(-1)
(A*B.swapaxes(...)).sum(-1)

在进一步测试中,我认为 inner1dmatrix_multiply 匹配您的 dotmatrix-matrix 产品案例,如果您添加 matrix-vector 案例[...,None]。看起来它们比 einsum 版本快 2 倍(在我的机器和测试阵列上)。

https://github.com/numpy/numpy/blob/master/doc/neps/return-of-revenge-of-matmul-pep.rstnumpy 上对 @ 中缀运算符的讨论。我认为 numpy 开发人员对这个 PEP 的热情不如 Python 开发人员。