1

私はこのような構造を持っています:

a = np.array([[np.array([1,2,3]), np.array([2])], [np.array([0,1,2,3,4]), np.array([0,4])]])

これは2x2構造であり、すべての要素が任意の寸法を持つことができます

のすべての要素の平均を取得したいa

np.mean(a)

ValueError: operands could not be broadcast together with shapes (3) (5) 

で遊んでみましたaxisが、うまくいきません。np.arrayに等しい新しいものを取得したい[[2, 2], [2, 2]

a一般に、ベクトル化された関数を同じ方法で実行できるようにしたいと思います。

どうやってするの?高速なコードが必要なので、明示的なループは避けてください。

私にできる最善のことは次のとおりです。

f = np.mean
result = np.zeros(a.shape)
for i in np.ndindex(a.shape):
  result[i] = f(a[i])
4

3 に答える 3

3

これは私ができる最善のことです:

a = np.array([[np.array([1,2,3]), np.array([2])], [np.array([0,1,2,3,4]), np.array([0,4])]])
np.array([ np.mean(b) for b in a.ravel()]).reshape(a.shape)

出力

array([[ 2.,  2.],
       [ 2.,  2.]])

他の機能については、次のように一般化できます。

def ravel_func(func,arr):
    return np.asarray([ func(part) for part in arr.ravel()]).reshape(arr.shape)

Jaimeのおかげでスピードテスト

In [82]: timeit vmean(a)
10000 loops, best of 3: 65 us per loop

In [83]: timeit ravel_func(np.mean,a)
10000 loops, best of 3: 67 us per loop
于 2013-02-27T00:54:27.260 に答える
3

基本的にndarrayの組み込みのようなnumpy.vectorizeが必要だと思いますmap

>>> a = np.array([[np.array([1,2,3]), np.array([2])], [np.array([0,1,2,3,4]), np.array([0,4])]])
>>> vmean = np.vectorize(np.mean)
>>> vmean(a)
array([[ 2.,  2.],
       [ 2.,  2.]])
于 2013-02-27T01:05:52.743 に答える
2

あなたのコメントから:あなたは比較的少数の非常に大きな要素を持っています。つまり、外側のループの反復の速度は無関係ですが、内側のループの反復の速度は重要です。

これに実際の数字を入れてみましょう。外側の配列には、最大サイズ10の最大4つの次元があります。これは、最大10000の要素があることを意味します。一方、要素は「かなり大きい」ので、控えめに言って50と解釈しましょう。つまり、510000回のループ反復があります。10000回の外部反復の速度を向上させるために行うことはすべて、コードの違いが2%未満になります。実際、それはそれよりはるかに少ないです。2%は、反復自体以外に実行する作業がないと想定していますが、これは明らかに真実ではありません。

だから、あなたは間違った場所に焦点を当てています。500000の内部反復のみが重要です。配列の配列を単一の1次元のより高い配列に置き換えて、すべてをで行うことができれば、numpyより高速になる可能性がありますが、コードがはるかに複雑になり、理解が難しくなり、1分の1程度のゲインが得られます。 %はばかげています。vectorize答えまたは理解の答えを使用するだけです。これらは単純で明白です。


その間:

おそらく、すべての行列要素にスレッドを使用して、評価を並列化するように試みる必要があります。

ここでは並列処理をお勧めします。ただし、スレッドは使用せず、要素ごとに1つも使用しません。

たとえば、8コアのマシンを使用している場合、8つのハードウェアスレッドを使用すると、ほぼ8倍の速度で処理を実行できます。ただし、10000のハードウェアスレッドを使用しても、10000倍、さらには8倍の速度で処理を実行できるわけではありません。コンテキストの切り替え、スレッドスタックの割り当てと解放などに多くの時間を費やしているため、実際にはシーケンシャルよりも時間がかかります。バージョン。したがって、8つのハードウェアスレッドのワーカープールを作成し、10000のタスクをキューに入れてそのプールで実行する必要があります。

また、10000はおそらくあまりにもきめ細かいです。各ジョブには少しのオーバーヘッドがあり、ジョブが小さすぎると、オーバーヘッドに多くの時間を浪費していることになります。物事をバッチ処理する明白な方法は、軸ごとです。それぞれが10要素の1行を実行する1000個のジョブ、またはそれぞれが100要素の1つの2D配列を実行する100個のジョブがあります。0、1、2、および3軸のバッチサイズをテストし、どれが最高のパフォーマンスを提供するかを確認します。違いが大きい場合は、3x10x10と4x10x10の3D配列に分割するなど、もう少し複雑なバッチ処理を試してみることをお勧めします。

最後に、Pythonスレッドは実際のOS(したがってハードウェア)スレッドですが、GILは、一度に複数のスレッドがPythonコードを実行するのを防ぎます。numpyこれを回避するためにいくつかの作業を行いますが、完全ではありません(特に、numpy呼び出しの間に多くのオーバーヘッドがある場合)。これを回避する最も簡単な方法はmultiprocessing、を使用することです。これにより、1つのプロセスに8つのスレッドを含める代わりに、8つの個別のプロセスのプールを作成できます。これにより、オーバーヘッドが大幅に増加します。サブ配列をコピーするか(タスク関数のパラメーターと戻り値として暗黙的に、またはパイプを介して明示的に)、共有メモリに配置する必要があります。ただし、タスクサイズが十分に大きい場合は、通常は問題ありません。


既存のコードを並列化する方法の例を次に示します。

f = np.mean
result = np.zeros(a.shape)
future_map = {}
with futures.ProcessPoolExecutor(max_workers=8) as executor:
    for i in np.ndindex(a.shape):
        future_map[executor.submit(f, a[i])] = i
    for future in futures.as_completed(future_map):
        i = future_map[future]
        result[i] = future.result()

もちろん、単純化することもできますが(たとえば、送信ループを理解に置き換えるなどdict)、何を変更する必要があるかをできるだけ明確にしたいと思います。

また、コードが少し簡単になるため、私は使用してfuturesいます(3.2以降が必要です。2.7を使用している場合は、 PyPIからバックポートをインストールしてください)。より柔軟性が必要な場合は、より複雑なmultiprocessingライブラリが必要です。

そして最後に、私はバッチ処理を行っていません(各タスクは単一の要素です)ので、オーバーヘッドはおそらくかなり悪くなるでしょう。

ただし、これから始めて、使いやすい範囲で単純化してから、前述のように1、2、3軸などのバッチを使用するように変換します。

于 2013-02-27T18:48:37.413 に答える