【问题标题】:Scalable solution for dot product of two vectors两个向量的点积的可扩展解决方案
【发布时间】:2017-02-18 12:29:45
【问题描述】:

两个向量的点积可以通过numpy.dot 计算。现在我想计算向量数组的点积:

>>> numpy.arange(15).reshape((5, 3))
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11],
       [12, 13, 14]])

向量是行向量,输出应该是包含点积结果的一维数组:

array([  5,  50, 149, 302, 509])

对于叉积 (numpy.cross),这可以通过指定 axis 关键字轻松实现。但是numpy.dot 没有这样的选项,传递两个二维数组将产生普通的矩阵乘积。我还查看了numpy.tensordot,但这似乎也不起作用(作为扩展矩阵产品)。

我知道我可以通过计算二维数组的每个元素的点积

>>> numpy.einsum('ij, ji -> i', array2d, array2d.T)

但是,此解决方案不适用于一维数组(即仅单个元素)。我想获得一个适用于一维数组(返回标量)和一维数组(又名二维数组)(返回一维数组)的解决方案。

【问题讨论】:

    标签: python python-2.7 numpy


    【解决方案1】:

    使用np.einsumellipsis(...) 来解释具有可变维数的数组,就像这样 -

    np.einsum('...i,...i ->...', a, a)
    

    在上面说明文档 -

    要启用和控制广播,请使用省略号。默认 NumPy 风格的广播是通过在左侧添加省略号来完成的 每个术语,例如 np.einsum('...ii->...i', a)。带着痕迹 第一个和最后一个轴,你可以做 np.einsum('i...i', a),或者做一个 具有最左侧索引而不是最右侧索引的矩阵-矩阵乘积, 你可以做 np.einsum('ij...,jk...->ik...', a, b).

    示例在 2D1D 数组上运行 -

    In [88]: a
    Out[88]: 
    array([[ 0,  1,  2],
           [ 3,  4,  5],
           [ 6,  7,  8],
           [ 9, 10, 11],
           [12, 13, 14]])
    
    In [89]: np.einsum('...i,...i ->...', a, a) # On 2D array
    Out[89]: array([  5,  50, 149, 302, 509])
    
    In [90]: b = a[:,0]
    
    In [91]: b
    Out[91]: array([ 0,  3,  6,  9, 12])
    
    In [92]: np.einsum('...i,...i ->...', b,b) # On 1D array
    Out[92]: 270
    

    运行时测试-

    因为,我们需要保持一个轴对齐,至少对于 2D 数组,np.einsumnp.matmul 或最新的 @ operator 之一将是有效的。

    In [95]: a = np.random.rand(1000,1000)
    
    # @unutbu's soln
    In [96]: %timeit (a*a).sum(axis=-1)
    100 loops, best of 3: 3.63 ms per loop
    
    In [97]: %timeit np.einsum('...i,...i ->...', a, a)
    1000 loops, best of 3: 944 µs per loop
    
    In [98]: a = np.random.rand(1000)
    
    # @unutbu's soln
    In [99]: %timeit (a*a).sum(axis=-1)
    100000 loops, best of 3: 9.11 µs per loop
    
    In [100]: %timeit np.einsum('...i,...i ->...', a, a)
    100000 loops, best of 3: 5.59 µs per loop
    

    【讨论】:

    • 很好的解决方案,谢谢!我还发现可以使用numpy.linalg.norm(array, axis=-1)**2,但性能类似于(a*a).sum(axis=-1)(即与einsum相比较差),我发现这很令人惊讶,因为我看不出对einsum的调用与@不同的地方987654339@ 函数(除了平方,但我证实这个贡献可以忽略不计)。你知道为什么这两个函数有如此不同的性能吗? (除非linalg.norm 的计算结果为sqrt((a*a).sum(axis=-1)),那么einsum 有什么不同呢?)
    • @a_guest 好吧,我还没有深入研究einsum's 的实现,但基于它的性能,我认为它是一个 C 编译代码。现在,numpy.linalg.norm 再次基于其性能,我认为元素乘法,然后求和是此类编译代码的两个阶段,除此之外,numpy.linalg.norm,我们需要 sqrt,然后你需要平方当您将其与使用einsum 的单段编译代码进行比较时,要取回所需的输出只是工作量太大。再次免责声明:不是与这些相关的编译细节方面的专家。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-27
    • 1970-01-01
    • 2016-06-04
    • 1970-01-01
    • 1970-01-01
    • 2016-03-01
    相关资源
    最近更新 更多