私は説明できない効率テストの結果を得ています。なぜB = numpy.dot(A、x)はB [i、:、:] = numpy.dot(A [i、:、:]、x))を実行するとループが遅くなるのですか?
i番目のエントリB [i、:、:] = A [i、:、:] .dot(x)を持つ行列Bをアセンブルしたいと考えており、各A [i、:、:]は2次元行列、xも同様です。
パフォーマンスをテストするために3つの方法があります。ランダム(numpy.random.randn
)の行列A =(10,1000,1000)、x =(1000,1200)を作成します。
(2)Iをループと2D内積を実行
# initialize B = np.zeros([dim1, dim2, dim3])
for i in range(A.shape[0]):
B[i,:,:] = A[i,:,:].dot(x)
total time: 0.826 s
(1)単一の多次元内積
B = A.dot(x)
total time: 102.361 s
(3)numpy.einsum
:私は、次の時間結果を得ますB3 = np.einsum("ijk, kl -> ijl", A, x)
total time: 8.289 s
したがって、オプション(2)は、最も速いです。しかし、ちょうど(1)と(2)を考えると、私はそれらの間に大きな違いは見当たりません。どのようにして二次元ドット製品をループしたり、やったりするのが〜124倍速くなるのでしょうか?彼らは両方ともnumpy.dotを使用します。どんな洞察?
私はちょうど下の上記の結果のために使用されるコードが含ま:
import numpy as np
import numpy.random as npr
import time
dim1, dim2, dim3 = 10, 1000, 1200
A = npr.randn(dim1, dim2, dim2)
x = npr.randn(dim2, dim3)
# consider three ways of assembling the same matrix B: B1, B2, B3
t = time.time()
B1 = np.dot(A,x)
td1 = time.time() - t
print "a single dot product of A [shape = (%d, %d, %d)] with x [shape = (%d, %d)] completes in %.3f s" \
% (A.shape[0], A.shape[1], A.shape[2], x.shape[0], x.shape[1], td1)
B2 = np.zeros([A.shape[0], x.shape[0], x.shape[1]])
t = time.time()
for i in range(A.shape[0]):
B2[i,:,:] = np.dot(A[i,:,:], x)
td2 = time.time() - t
print "taking %d dot products of 2D dot products A[i,:,:] [shape = (%d, %d)] with x [shape = (%d, %d)] completes in %.3f s" \
% (A.shape[0], A.shape[1], A.shape[2], x.shape[0], x.shape[1], td2)
t = time.time()
B3 = np.einsum("ijk, kl -> ijl", A, x)
td3 = time.time() - t
print "using np.einsum, it completes in %.3f s" % td3
'dot'は多くのことを行いますが、' np.dot(A、x) 'はBLASを呼び出さず、何とかnumpyの内部GEMMルーチンにデフォルト設定されていることが明らかです。あなたのリシェイプの例は、ルーピングの仕組みをバイパスし、データをコピーせずに従来の2D GEMMコールにまっすぐ進んでいます。良いBLASがあれば、合理的なサイズの問題のための最速の解決策になります。私のラップトップでMKLを使うと、元のサイズの問題ではeinsumより約50倍高速です。 – Daniel
'tensordotは同じ形をしています。 – hpaulj
データのコピーを内部で強制的に実行するテンソードは、このオプションはお勧めしません。 – Daniel