22

非常に大きな 2D numpy 配列の 1 つの次元に沿って最小配列インデックスを見つけようとしています。これは非常に遅いことがわかりました(すでにボトルネックで高速化を試みましたが、これは最小限の改善に過ぎませんでした)。ただし、単純な最小値を取ると、桁違いに速くなるように見えます。

import numpy as np
import time

randvals = np.random.rand(3000,160000)
start = time.time()
minval = randvals.min(axis=0)
print "Took {0:.2f} seconds to compute min".format(time.time()-start)
start = time.time()
minindex = np.argmin(randvals,axis=0)
print "Took {0:.2f} seconds to compute argmin".format(time.time()-start)

私のマシンでは、次のように出力されます。

Took 0.83 seconds to compute min
Took 9.58 seconds to compute argmin

argmin が非常に遅い理由はありますか? min に匹敵するスピードアップする方法はありますか?

4

3 に答える 3

13
In [1]: import numpy as np

In [2]: a = np.random.rand(3000, 16000)

In [3]: %timeit a.min(axis=0)
1 loops, best of 3: 421 ms per loop

In [4]: %timeit a.argmin(axis=0)
1 loops, best of 3: 1.95 s per loop

In [5]: %timeit a.min(axis=1)
1 loops, best of 3: 302 ms per loop

In [6]: %timeit a.argmin(axis=1)
1 loops, best of 3: 303 ms per loop

In [7]: %timeit a.T.argmin(axis=1)
1 loops, best of 3: 1.78 s per loop

In [8]: %timeit np.asfortranarray(a).argmin(axis=0)
1 loops, best of 3: 1.97 s per loop

In [9]: b = np.asfortranarray(a)

In [10]: %timeit b.argmin(axis=0)
1 loops, best of 3: 329 ms per loop

たぶんmin、配列全体で順番にジョブを実行するのに十分スマートであり (キャッシュの局所性があるため)、argmin配列を飛び回っています (多くのキャッシュミスを引き起こしています)?

とにかく、最初から Fortran 順の配列として保持する意思がある場合はrandvals、Fortran 順の配列にコピーしても役に立ちませんが、高速になります。

于 2013-07-24T18:00:47.013 に答える
9

ソースコードを見てみましたが、なぜこのようになっているのか完全には理解できませんが、次のようになります。

  1. np.minは基本的に への呼び出しnp.minimum.reduceです。

  2. np.argmin最初に操作したい軸を形状タプルの最後に移動し、次にそれを連続した配列にします。これはもちろん、軸が最初から最後の軸でない限り、完全な配列のコピーをトリガーします。

コピーが作成されているので、創造性を発揮して安価な配列のインスタンス化を試みることができます。

a = np.random.rand(1000, 2000)

def fast_argmin_axis_0(a):
    matches = np.nonzero((a == np.min(a, axis=0)).ravel())[0]
    rows, cols = np.unravel_index(matches, a.shape)
    argmin_array = np.empty(a.shape[1], dtype=np.intp)
    argmin_array[cols] = rows
    return argmin_array

In [8]: np.argmin(a, axis=0)
Out[8]: array([230, 532, 815, ..., 670, 702, 989], dtype=int64)

In [9]: fast_argmin_axis_0(a)
Out[9]: array([230, 532, 815, ..., 670, 702, 989], dtype=int64)

In [10]: %timeit np.argmin(a, axis=0)
10 loops, best of 3: 27.3 ms per loop

In [11]: %timeit fast_argmin_axis_0(a)
100 loops, best of 3: 15 ms per loop

現在の実装をバグと呼ぶほどではありませんが、numpy がそのように動作するのには十分な理由があるかもしれませんが、この種の策略は、高度に最適化された関数であるべきものを高速化する可能性があります。物事がより良くできることを強く示唆しています。

于 2013-07-24T19:11:31.023 に答える