web-dev-qa-db-ja.com

Scipyを使用して行ごとの内積2つの行列を計算するベクトル化された方法

同じ次元の2つの行列の行方向のドット積をできるだけ速く計算したい。これは私がやっている方法です:

import numpy as np
a = np.array([[1,2,3], [3,4,5]])
b = np.array([[1,2,3], [1,2,3]])
result = np.array([])
for row1, row2 in a, b:
    result = np.append(result, np.dot(row1, row2))
print result

そしてもちろん、出力は次のとおりです。

[ 26.  14.]
29
Cupitor

別の方法については、 numpy.einsum をご覧ください。

In [52]: a
Out[52]: 
array([[1, 2, 3],
       [3, 4, 5]])

In [53]: b
Out[53]: 
array([[1, 2, 3],
       [1, 2, 3]])

In [54]: einsum('ij,ij->i', a, b)
Out[54]: array([14, 26])

einsuminner1dより少し速いようです:

In [94]: %timeit inner1d(a,b)
1000000 loops, best of 3: 1.8 us per loop

In [95]: %timeit einsum('ij,ij->i', a, b)
1000000 loops, best of 3: 1.6 us per loop

In [96]: a = random.randn(10, 100)

In [97]: b = random.randn(10, 100)

In [98]: %timeit inner1d(a,b)
100000 loops, best of 3: 2.89 us per loop

In [99]: %timeit einsum('ij,ij->i', a, b)
100000 loops, best of 3: 2.03 us per loop
25

簡単な方法は次のとおりです。

import numpy as np
a=np.array([[1,2,3],[3,4,5]])
b=np.array([[1,2,3],[1,2,3]])
np.sum(a*b, axis=1)

pythonループを回避し、次のような場合に高速になります。

def npsumdot(x, y):
    return np.sum(x*y, axis=1)

def loopdot(x, y):
    result = np.empty((x.shape[0]))
    for i in range(x.shape[0]):
        result[i] = np.dot(x[i], y[i])
    return result

timeit npsumdot(np.random.Rand(500000,50),np.random.Rand(500000,50))
# 1 loops, best of 3: 861 ms per loop
timeit loopdot(np.random.Rand(500000,50),np.random.Rand(500000,50))
# 1 loops, best of 3: 1.58 s per loop
23
sim

これをいじってみて、inner1d 最速:

enter image description here

プロットは perfplot (私の小さなプロジェクト)で作成されました

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)),
    n_range=[2**k for k in range(1, 18)],
    kernels=[
        lambda data: numpy.sum(data[0] * data[1], axis=1),
        lambda data: numpy.einsum('ij, ij->i', data[0], data[1]),
        lambda data: inner1d(data[0], data[1])
        ],
    labels=['np.sum(a*b, axis=1)', 'einsum', 'inner1d'],
    logx=True,
    logy=True,
    xlabel='len(a), len(b)'
    )
21
Nico Schlömer

appendを避ける方が良いでしょうが、pythonループ。カスタムUfuncかもしれませんか?numpyとは思わないでしょう。ここでvectorizeが役立ちます。

import numpy as np
a=np.array([[1,2,3],[3,4,5]])
b=np.array([[1,2,3],[1,2,3]])
result=np.empty((2,))
for i in range(2):
    result[i] = np.dot(a[i],b[i]))
print result

[〜#〜] edit [〜#〜]

この回答 に基づくと、inner1dは、実世界の問題のベクトルが1Dの場合に機能する可能性があります。

from numpy.core.umath_tests import inner1d
inner1d(a,b)  # array([14, 26])
4
Paul

私はこの答えに出会い、Python 3.5。で実行されているNumpy 1.14.3で結果を再確認しました。マトリックス(以下の例を参照)、メソッドの1つを除くすべてが互いに非常に近いため、パフォーマンスの違いは無意味です。

小さい行列の場合、einsumがかなりのマージンで最速であり、場合によっては2倍になることがわかりました。

私の大きなマトリックスの例:

import numpy as np
from numpy.core.umath_tests import inner1d
a = np.random.randn(100, 1000000)  # 800 MB each
b = np.random.randn(100, 1000000)  # pretty big.

def loop_dot(a, b):
    result = np.empty((a.shape[1],))
    for i, (row1, row2) in enumerate(Zip(a, b)):
        result[i] = np.dot(row1, row2)
%timeit inner1d(a, b)
# 128 ms ± 523 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit np.einsum('ij,ij->i', a, b)
# 121 ms ± 402 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit np.sum(a*b, axis=1)
# 411 ms ± 1.99 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit loop_dot(a, b)  # note the function call took negligible time
# 123 ms ± 342 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

そのため、einsumは、非常に大きな行列でも依然として最速ですが、ごくわずかです。ただし、統計的に有意な(わずかな)量のようです!

0
Engineero