60

同じ次元の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.]
4

5 に答える 5

56

それを行う簡単な方法は次のとおりです。

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
于 2015-06-24T08:44:18.837 に答える
45

別の方法については、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])

einsum:より少し速いようですinner1d

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

注:NumPyは常に進化し、改善されています。上記の機能の相対的なパフォーマンスは、おそらく何年にもわたって変化しています。パフォーマンスが重要な場合は、使用するバージョンのNumPyを使用して独自のテストを実行してください。

于 2013-03-25T19:17:02.147 に答える
29

inner1dこれをいじって、最速を見つけました。ただし、その機能は内部的なものであるため、より堅牢なアプローチを使用することです

numpy.einsum("ij,ij->i", a, b)

さらに良いのは、合計が最初の次元で発生するようにメモリを調整することです。

a = numpy.random.rand(3, n)
b = numpy.random.rand(3, n)

numpy.einsum("ij,ij->j", a, b)

の場合10 ** 3 <= n <= 10 ** 6、これは最速の方法であり、転置されていない同等の方法の最大2倍の速度です。最大値は、レベル2のキャッシュが最大になっているときに発生します(約。) 2 * 10 ** 4

sum転置されたメイションは、転置されていない同等のものよりもはるかに高速であることに注意してください。

ここに画像の説明を入力してください

ここに画像の説明を入力してください

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

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


def setup(n):
    a = numpy.random.rand(n, 3)
    b = numpy.random.rand(n, 3)
    aT = numpy.ascontiguousarray(a.T)
    bT = numpy.ascontiguousarray(b.T)
    return (a, b), (aT, bT)


b = perfplot.bench(
    setup=setup,
    n_range=[2 ** k for k in range(1, 25)],
    kernels=[
        lambda data: numpy.sum(data[0][0] * data[0][1], axis=1),
        lambda data: numpy.einsum("ij, ij->i", data[0][0], data[0][1]),
        lambda data: numpy.sum(data[1][0] * data[1][1], axis=0),
        lambda data: numpy.einsum("ij, ij->j", data[1][0], data[1][1]),
        lambda data: inner1d(data[0][0], data[0][1]),
    ],
    labels=["sum", "einsum", "sum.T", "einsum.T", "inner1d"],
    xlabel="len(a), len(b)",
)

b.save("out1.png")
b.save("out2.png", relative_to=3)
于 2016-09-23T09:49:47.960 に答える
5

を回避する方がよいでしょうが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

編集

この回答に基づくとinner1d、実際の問題のベクトルが1Dの場合は機能するように見えます。

from numpy.core.umath_tests import inner1d
inner1d(a,b)  # array([14, 26])
于 2013-03-25T14:26:39.663 に答える
0

私はこの答えに出くわし、Python3.5で実行されているNumpy1.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、非常に大きな行列でも最速ですが、量はわずかです。ただし、統計的に有意な(ごくわずかな)量のようです。

于 2018-09-24T20:38:16.117 に答える