2017-02-18 2 views
2

2つのベクトルの内積はnumpy.dotによって計算できます。2つのベクトルの内積のためのスケーラブルな解法

>>> numpy.arange(15).reshape((5, 3)) 
array([[ 0, 1, 2], 
     [ 3, 4, 5], 
     [ 6, 7, 8], 
     [ 9, 10, 11], 
     [12, 13, 14]]) 

ベクトルが行ベクトルであり、出力はドット積の結果を含む1次元配列でなければならない:

array([ 5, 50, 149, 302, 509]) 

今はベクトルのアレイのドット積を計算しますクロスプロダクト(numpy.cross)の場合は、axisキーワードを指定すると簡単に達成できます。しかしnumpy.dotにはそのようなオプションはなく、2つの2次元配列を渡すと通常の行列積が得られます。私もnumpy.tensordotを見ましたが、これはどちらかの仕事をしていないようです(拡張マトリックス製品です)。

私は、しかし、この解決策は、1次元アレイ(すなわち、ただ1つの要素)に対しては機能しない

>>> numpy.einsum('ij, ji -> i', array2d, array2d.T) 

を介して二次元アレイの素子当たりのドット積を計算することができることを知っています。私は、1次元配列(スカラーを返す)と1次元配列(別名2次元配列)の配列(1次元配列を返す)の両方に対応する解を得たいと考えています。

答えて

4

そう等寸法の可変数のアレイ、を考慮しellipsis(...)で使用np.einsum -

を有効にし、ブロードキャスト制御、省略記号を使用する - その上にドキュメントを記述

np.einsum('...i,...i ->...', a, a) 

。デフォルト NumPy形式のブロードキャストは、np.einsum( '... ii - > ... i'、a)のように、各用語の の左側に省略記号を追加することによって行われます。 の最初の軸と最後の軸に沿ってトレースを取得するには、np.einsum( 'i ... i'、a)を実行するか、 の代わりに一番左のインデックスを持つ 行列行列積を実行しますnp.einsum( 'ij ...、jk ...-> ik ...'、a、b)を実行できます。

サンプル2D1Dアレイ上で動作 - 私たちは、少なくとも2Dアレイ、np.einsumの一つまたはnp.matmulと、整列一つの軸を保持する必要があるので

又は -

In [88]: a 
Out[88]: 
array([[ 0, 1, 2], 
     [ 3, 4, 5], 
     [ 6, 7, 8], 
     [ 9, 10, 11], 
     [12, 13, 14]]) 

In [89]: np.einsum('...i,...i ->...', a, a) # On 2D array 
Out[89]: array([ 5, 50, 149, 302, 509]) 

In [90]: b = a[:,0] 

In [91]: b 
Out[91]: array([ 0, 3, 6, 9, 12]) 

In [92]: np.einsum('...i,...i ->...', b,b) # On 1D array 
Out[92]: 270 

ランタイム試験最新の@ operatorが効率的です。

In [95]: a = np.random.rand(1000,1000) 

# @unutbu's soln 
In [96]: %timeit (a*a).sum(axis=-1) 
100 loops, best of 3: 3.63 ms per loop 

In [97]: %timeit np.einsum('...i,...i ->...', a, a) 
1000 loops, best of 3: 944 µs per loop 

In [98]: a = np.random.rand(1000) 

# @unutbu's soln 
In [99]: %timeit (a*a).sum(axis=-1) 
100000 loops, best of 3: 9.11 µs per loop 

In [100]: %timeit np.einsum('...i,...i ->...', a, a) 
100000 loops, best of 3: 5.59 µs per loop 
+0

ありがとうございました!私はまた 'numpy.linalg.norm(array、axis = -1)** 2'を使うことができますが、パフォーマンスは'(a * a).sum(axis = -1) 'と似ています。 'einsum')は' einsum'の呼び出しが 'linalg.norm'関数と異なる点(二乗を除いて、しかしこの寄与は無視できると確信しています)を見ていないので、私はかなり驚くべきことです。なぜ2つの機能が異なるパフォーマンスを持つのか考えていますか? ( 'linalg.norm'が' sqrt((a * a).sum(axis = -1)) 'と評価されない限り、' einsum'とは何か異なっていますか?) –

+0

@a_guestまあ、 einsumの実装だが、そのパフォーマンスに基づいて、私はそれがCコンパイルされたコードだと思う。さて、 'numpy.linalg。その性能に基づいてもう一度normを実行すると、私は要素単位の乗算を行い、そのようなコンパイルされたコードの2つの段階が加算され、その上に 'numpy.linalg.norm'があり、sqrtが必要です'einsum'を使って一つのコンパイル済みコードと比較すると、あまりにも多くの作業が必要になります。免責事項:これらに関連するコンパイルの詳細については、専門家ではありません。 – Divakar

関連する問題