Python 两个矩阵行点积的优雅表达式

Python 两个矩阵行点积的优雅表达式,python,numpy,Python,Numpy,我有两个尺寸相同的二维numpy数组,A和B,我正试图计算它们的行点积。我可以做到: np.sum(A * B, axis=1) 有没有其他方法可以让numpy在一步而不是两步中完成逐行点积?也许使用tensordot?这是一个很好的应用程序 einsum也会更快一些 a = np.random.normal(size=(5000, 1000)) b = np.random.normal(size=(5000, 1000)) %timeit np.einsum('ij, ij->i',

我有两个尺寸相同的二维numpy数组,A和B,我正试图计算它们的行点积。我可以做到:

np.sum(A * B, axis=1)

有没有其他方法可以让numpy在一步而不是两步中完成逐行点积?也许使用
tensordot

这是一个很好的应用程序

einsum
也会更快一些

a = np.random.normal(size=(5000, 1000))
b = np.random.normal(size=(5000, 1000))

%timeit np.einsum('ij, ij->i', a, b)
# 100 loops, best of 3: 8.4 ms per loop

%timeit np.sum(a*b, axis=1)
# 10 loops, best of 3: 28.4 ms per loop

即使对于中等大小的数据,速度也要慢得多,我还是会使用

np.diag(A.dot(B.T))
当您正在开发库,并且担心在以后它将在生产环境中运行或在编写单元测试之后对其进行优化时

对于大多数接触到您的代码的人来说,这将比
einsum
更容易理解,而且也不要求您通过将计算嵌入迷你DSL字符串作为某个函数调用的参数来打破某些最佳做法


我同意,在大型情况下,计算非对角元素是值得避免的。然而,对我来说,这一点必须非常重要,而且在
einsum
中用嵌入字符串表示计算所付出的可怕代价是相当严重的。

甚至更快的是
inner1d
来自
numpy.core.umath\u tests


复制绘图的代码:

import numpy
from numpy.core.umath_tests import inner1d
import perfplot


perfplot.show(
        setup=lambda n: (numpy.random.rand(n, 3), numpy.random.rand(n, 3)),
        kernels=[
            lambda a: numpy.sum(a[0]*a[1], axis=1),
            lambda a: numpy.einsum('ij, ij->i', a[0], a[1]),
            lambda a: inner1d(a[0], a[1])
            ],
        labels=['sum', 'einsum', 'inner1d'],
        n_range=[2**k for k in range(20)],
        xlabel='len(a), len(b)',
        logx=True,
        logy=True
        )

A和B是矩阵吗?我假设维度相同?[请注意,“矩阵”是一个术语,与“数组”不同:如果a和B是数组,而不是矩阵,那么
a*B
是元素积,而不是点积。]@DSM:是的,它是元素积。期待着
@
操作符,这样我们就可以忘记
数组了(
:)看起来很棒,谢谢!我们将拭目以待,看看是否有人找到更合适的。您也可以使用np.tensordot;但要说哪一个更合适是个品味问题…@eelcoogendoorn:我该如何使用tensordot?(我对计时很好奇。)哦,糟糕,你不能用tensordot来做这个。numexpr应该是另一个选项,它可能比einsum性能更好。使用我问题中的示例,此解决方案需要的时间几乎是
einsum
示例的100倍,大约是
np.sum(A*B,axis=1)
示例的20倍。我认为
einsum
是一个功能强大且富有表现力的工具,但是如果您真的不喜欢它,那么您应该使用这个问题的解决方案。对于我来说,两个数量级是值得的,以避免在除最恶劣的生产环境之外的所有环境中使用
einsum
来表达计算。我还可以考虑需要的结果向量和写生成器一个愚蠢的纯Python for for循环使用<代码> .DOT/COD>手动对每对行。如果我只需要使用一次输出,那就更好了。如果你这么不喜欢字符串,你也可以告诉einsum做它的事情,对于OP的用例,比如:
np.einsum(a[0,1],b[0,1],[0])
。如果您可以指定使
einsum
成为一个坏主意的“这么多原因”中的一些,那将是一件好事。照目前的情况,我很想对这个错误的建议投反对票…@EMS:all code不是“一个字符串常量,对编写代码的人来说意味着什么”吗?我看不出仅仅添加注释来解释不明显的代码有什么错。prpl:很抱歉necro。当然,我同意DSL字符串有其缺点;用母语将所有内容表示为表达式图当然要干净得多。也就是说,如果母语允许的话;如果用母语表达你的观点感觉很像是把一个正方形的钉子塞进一个圆孔,那么一点DSL可能没那么糟糕。就线性代数而言;这是一个DSL,关心像einsum这样的功能的人应该已经知道并喜欢它了。但事实上,einsum语法强制用户显式命名所有轴是一件好事。我如何导入它?
从numpy.core.umath_tests导入inner1d
哦,对了,出于某种原因,它不起作用。为什么这个函数没有公开?你是怎么找到它的?我是这么偶然发现的。
\u测试
可能表明它仍处于beta测试阶段,但确实对我有效。很酷,很好,很好的发现,谢谢你的漂亮图表。我觉得引导人们使用内部函数是不好的,但是如果他们公开函数,我会接受你的答案。
import numpy
from numpy.core.umath_tests import inner1d
import perfplot


perfplot.show(
        setup=lambda n: (numpy.random.rand(n, 3), numpy.random.rand(n, 3)),
        kernels=[
            lambda a: numpy.sum(a[0]*a[1], axis=1),
            lambda a: numpy.einsum('ij, ij->i', a[0], a[1]),
            lambda a: inner1d(a[0], a[1])
            ],
        labels=['sum', 'einsum', 'inner1d'],
        n_range=[2**k for k in range(20)],
        xlabel='len(a), len(b)',
        logx=True,
        logy=True
        )