2014-01-20 9 views
7

私は幾何学的な(算術とは対照的に)ニューラルネットを構築するプロジェクトに取り組んでいます。伝達関数を構成するには、算術和の代わりに幾何学的な和を使用したいと思います。幾何行列の乗算

、物事をより明確にするために、私はコードで記述するつもりです:あなたが見ることができるように

def arithmetic_matrix_multiplication(matrix1, matrix2): 
    new_matrix = np.zeros(len(matrix1),len(matrix2[0])) 
    for i in range(len(matrix1)): 
     for j in range(len(matrix2[0])): 
      for k in range(len(matrix2)): 
       new_matrix[i][j] += matrix1[i][k]*matrix2[k][j] 
    return new_matrix 

def geometric_matrix_multiplication(matrix1, matrix2): 
    new_matrix = np.ones(len(matrix1),len(matrix2[0])) 
    for i in range(len(matrix1)): 
     for j in range(len(matrix2[0])): 
      for k in range(len(matrix2)): 
       new_matrix[i][j] *= matrix1[i][k]*matrix2[k][j] 
    return new_matrix 

、それはかなり最小限の変更です。唯一の問題は、私が上記の算術コード(実際にはnumpy.dotを使用します)を実際に記述して使用することはないと同じように、上記の幾何学的コードを実際に使用するのは本当に好きではないということです。幾何学的結果を得るためにnumpyの行列乗算を利用する方法はありますか?私は1つを考えることができませんでしたが、私は上記の解決策を過ぎて明白なものは見つけられませんでした。これは最適ではありません。

+0

書かれたように、あなたの幾何学的な結果は常にゼロ行列で(zero'が漠然とnp.zeros' ''のように振る舞うと仮定した場合)ではないでしょうか?代わりに1から始めるべきですか? – DSM

+0

@DSM Ah、whoops。あなたはまったく正しい。 –

+0

すべての要素が> 0であることは十分に幸いですか?もちろん、@ DSM Lol – DSM

答えて

6

をすべて不必要に物事を複雑にしている...あなたは可換である単一の操作、乗算を、持っているので、あなたは自由にあなたがそれらを実行する順序を入れ替えることができますつまり、matrix1の項目にmatrix2の項目を掛ける必要はなく、すべての項目を計算したらそれらを掛け合わせます。代わりに、最初にmatrix1のすべての関連項目を合計し、次にmatrix2のすべての関連項目を一緒に乗算し、2つの結果値を乗算することができます。だから、非常に簡単なようあなたの関数を書くことができます:

def fast_geometric_matrix_multiplication(matrix1, matrix2): 
    return np.prod(matrix1, axis=1)[:, None] * np.prod(matrix2, axis=0) 

それはあなたがシェイプ(m, k)(k, n)の行列を乗算している場合、あなたはこの方法ながら、m*n*2*k乗算を行うために持つことが期待される、さらなる利点を有しますm*k + n*k + m*nしか必要ではありません。これは、現在のほとんどの配列シェイプでやっているものよりはるかに小さいものです。

そしてもちろん:

In [24]: a = np.random.rand(100, 200) 
    ...: b = np.random.rand(200, 50) 
    ...: 

In [25]: np.allclose(geometric_matrix_multiplication(a, b), 
    ...:    fast_geometric_matrix_multiplication(a, b)) 
Out[25]: True 

In [26]: %timeit geometric_matrix_multiplication(a, b) 
1 loops, best of 3: 1.39 s per loop 

In [27]: %timeit fast_geometric_matrix_multiplication(a, b) 
10000 loops, best of 3: 74.2 us per loop 

In [28]: %timeit np.prod(a[:, None, :]*b[..., None].T, axis=2) 
100 loops, best of 3: 5.97 ms per loop 
+0

あなたはそうです。これは素晴らしいです。 –

4
In [373]: %paste 
x = np.arange(5*5, dtype=np.long).reshape(5,5) 
y = np.arange(5*5, 5*5*2, dtype=np.long).reshape(5,5) 
xy = geometric_matrix_multiplication(x,y) 
xy2 = np.prod(x[:, None, :]*y.T[..., None], axis=2) 
np.allclose(xy, xy2) 

## -- End pasted text -- 
Out[373]: True 

この解決策は、どのような形状を扱うことができるかに関してあまり安定ではありません。データサイズが可変であれば、それはうまくいくわけですが、何か長期的に使うべきものではありません。

+0

ニース。これは行列の値を制限しないので、私の答えよりはるかに優れています。 – DSM

+0

私にそれを打つ。このアプローチの唯一の問題は、完全なixjxk行列を形成する必要があるということですが、ログ空間での操作は直接操作する操作を(現在は使用していませんが)行うことができます。行列が大きくなると、これは相当に大きな問題になる可能性があります。 –

+1

私たちは基本的にそのようなことをする必要はないことに注意してください。いくつかの巧妙な技を失っていない限り、numpy apiはmultipend-contractionsに最適化された操作を提供しません。これが問題になるなら、Idはnumbaにリゾートする。 –

2

あなたの問題はnumbaにとって非常に適しています。追加する必要があるのは@autojitです。もちろん、ネストされた呼び出しを繰り返しの長さに合わせて最適化することもできます。 rangexrangeはnumbaによって単純なfor-loopsとして扱われます(大きな配列を作成するためのオーバーヘッドはありません)。最終的には次のようになります:

from numba import autojit 

@autojit 
def geometric_matrix_multiplication(matrix1, matrix2): 
    new_matrix = np.ones((len(matrix1),len(matrix2[0]))) 
    jlen = len(matrix2[0]) 
    klen = len(matrix2) 
    for i in range(len(matrix1)): 
     for j in range(jlen): 
      for k in range(klen): 
       new_matrix[i,j] *= matrix1[i,k]*matrix2[k,j] 
    return new_matrix 

この機能では、jitコンパイルを使用すると、後でCのような速度になるはずです。速度ゲインのいくつかのアイデアを与えるために:

a = np.random.rand(100*100).reshape((100,100)) 
b = np.random.rand(100*100).reshape((100,100)) 

%timeit geometric_matrix_multiplication_org(a,b) 
1 loops, best of 3: 4.1 s per loop 

%timeit geometric_matrix_multiplication(a,b) 
100 loops, best of 3: 5 ms per loop 
+0

本当にクールなライブラリのように見えますが、行列の乗算の最適化は実際には操作の複雑さにも及ぶため、forループを最適化するよりも深刻な問題があります。 –

+0

申し訳ありませんが、私はフォローできません。あなたは、Pythonであなたの例のようなコードを書くことは決してないと言いました。関数が純粋なPythonではかなり遅く実行されるので、私は同意するでしょう。しかし、jitコンパイルでは、forループが突然急激に速くなるので、コードを記述するこのスタイルはまさにその方法です。 – Michael

+0

Heh、あなたはメモリアクセスの最適化を望むことができますが、そのすべてを行うことで、複雑さの異なるレベルになります。 – seberg