简体   繁体   English

numpy点积和矩阵积

[英]numpy dot product and matrix product

I'm working with numpy arrays of shape (N,), (N,3) and (N,3,3) which represent sequences of scalars, vectors and matrices in 3D space. 我正在使用形状为(N,),(N,3)和(N,3,3)的numpy数组,它们表示3D空间中的标量,向量和矩阵的序列。 I have implemented pointwise dot product, matrix multiplication, and matrix/vector multiplication as follows: 我已经实现了点状点积,矩阵乘法和矩阵/矢量乘法,如下所示:

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)

As you can see I use einsum for lack of a better solution. 如您所见,由于缺乏更好的解决方案,我使用einsum。 To my surprise I was not able to use np.dot... which seems not suitable for this need. 令我惊讶的是,我无法使用np.dot ...这似乎不适合这种需要。 Is there a more numpythonic way to implement these function? 有没有更多的numpythonic方式来实现这些功能?

In particular it would be nice if the functions could work also on the shapes (3,) and (3,3) by broadcasting the first missing axis. 特别是,如果这些功能也可以通过广播第一个缺失的轴而在形状(3,)和(3,3)上起作用,那将是很好的。 I think I need ellipsis, but I don't quite understand how to achieve the result. 我想我需要省略号,但我不太了解如何获得结果。

These operations cannot be reshaped into general BLAS calls and looping BLAS calls would be quite slow for arrays of this size. 这些操作无法重塑为常规的BLAS调用,并且对于这种大小的数组,循环执行BLAS调用会非常慢。 As such, einsum is likely optimal for this kind of operation. 因此,einsum对于这种操作可能是最佳的。

Your functions can be generalized with ellipses as follows: 可以使用椭圆将功能概括如下:

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)

Just as working notes, these 3 calculations can also be written as: 就像工作说明一样,这三个计算也可以写成:

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])

Or with Ophion's generalization 或配合Ophion的概括

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

It shouldn't be hard to generate the [0,1,..] lists based on the dimensions of the inputs arrays. 根据输入数组的尺寸生成[0,1,..]列表应该不难。


By focusing on generalizing the einsum expressions, I missed the fact that what you are trying to reproduce is N small dot products. 通过专注于概括einsum表达式,我错过了一个事实,即您要复制的是N个小点积。

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

It's worth keeping mind that np.dot uses fast compiled code, and focuses on calculations where the arrays are large. 值得记住的是, np.dot使用快速编译的代码,并专注于数组较大的计算。 Where as your problem is one of calculating many small dot products. 问题是计算许多小点产品之一。

And without extra arguments that define axes, np.dot performs just 2 of the possible combinations, ones which can be expressed as: 无需定义轴的额外参数, np.dot仅执行两种可能的组合,它们可以表示为:

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

An operator version of dot would face the same limitation - no extra parameters to specify how the axes are to be combined. dot的运算符版本将面临相同的限制-没有额外的参数来指定如何组合轴。

It may also be instructive to note what tensordot does to generalize dot : 注意tensordot所做的概括dot也可能是tensordot

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)

It can perform a dot with summation over several axes. 它可以执行一个dot在几个轴与求和。 But after the transposing and reshaping is done, the calculation becomes the standard dot with 2d arrays. 但是在完成转置和整形之后,计算将成为具有2d数组的标准dot


This could have been flagged as a duplicate issue. 这可能已被标记为重复问题。 People have asking about doing multiple dot products for some time. 人们一直在询问要制造多个点产品。

Matrix vector multiplication along array axes suggests using numpy.core.umath_tests.matrix_multiply 沿数组轴的矩阵向量乘法建议使用numpy.core.umath_tests.matrix_multiply

https://stackoverflow.com/a/24174347/901925 equates: https://stackoverflow.com/a/24174347/901925等同于:

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

The C documentation for matrix_multiply notes: 有关matrix_multiplyC文档注意事项:

* This implements the function
* out[k, m, p] = sum_n { in1[k, m, n] * in2[k, n, p] }.

inner1d from the same directory does the same same for (N,n) vectors 来自同一目录的inner1d(N,n)向量执行相同的操作

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

Both are UFunc , and can handle broadcasting on the right most dimensions. 两者都是UFunc ,并且可以处理最合适尺寸的广播。 In numpy/core/test/test_ufunc.py these functions are used to exercise the UFunc mechanism. numpy/core/test/test_ufunc.py这些函数用于执行UFunc机制。

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

https://stackoverflow.com/a/16704079/901925 adds that this kind of calculation can be done with * and sum, eg https://stackoverflow.com/a/16704079/901925添加了这种计算可以使用*和求和来完成,例如

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

On further testing, I think inner1d and matrix_multiply match your dot and matrix-matrix product cases, and the matrix-vector case if you add the [...,None] . 在进一步的测试中,我认为inner1dmatrix_multiply匹配dotmatrix-matrix乘积案例,如果添加[...,None]则匹配matrix-vector案例。 Looks like they are 2x faster than the einsum versions (on my machine and test arrays). 看起来它们比einsum版本(在我的机器和测试阵列上)快2倍。

https://github.com/numpy/numpy/blob/master/doc/neps/return-of-revenge-of-matmul-pep.rst is the discussion of the @ infix operator on numpy . https://github.com/numpy/numpy/blob/master/doc/neps/return-of-revenge-of-matmul-pep.rst是对numpy上的@ infix运算符的讨论。 I think the numpy developers are less enthused about this PEP than the Python ones. 我认为numpy开发人员对这种PEP的热情不如Python。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM