web-dev-qa-db-ja.com

numpy dot()とPython 3.5+行列乗算の違い@

最近Python 3.5に移動し、 新しい行列乗算演算子(@)numpyドット 演算子と異なる動作をすることがあることに気付きました。たとえば、3D配列の場合:

import numpy as np

a = np.random.Rand(8,13,13)
b = np.random.Rand(8,13,13)
c = a @ b  # Python 3.5+
d = np.dot(a, b)

@演算子は、形状の配列を返します。

c.shape
(8, 13, 13)

np.dot()関数は以下を返します:

d.shape
(8, 13, 8, 13)

Numpyドットで同じ結果を再現するにはどうすればよいですか?他に大きな違いはありますか?

87
blaz

@演算子は、dotではなく、配列の__matmul__メソッドを呼び出します。このメソッドは、APIにも関数 np.matmul として存在しています。

>>> a = np.random.Rand(8,13,13)
>>> b = np.random.Rand(8,13,13)
>>> np.matmul(a, b).shape
(8, 13, 13)

ドキュメントから:

matmulは、2つの重要な点でdotと異なります。

  • スカラーによる乗算は許可されていません。
  • マトリックスのスタックは、マトリックスが要素であるかのように一緒にブロードキャストされます。

最後の点は、dotメソッドとmatmulメソッドが3D(またはより高次元の)配列を渡されたときに異なる動作をすることを明確にします。ドキュメントからさらに引用:

matmulの場合:

いずれかの引数がN-D、N> 2の場合、最後の2つのインデックスにあるマトリックスのスタックとして扱われ、それに応じてブロードキャストされます。

np.dot の場合:

2次元配列の場合、これは行列の乗算に相当し、1次元配列の場合、ベクトルの内積(複素共役なし)に相当します。 N次元の場合、aの最後の軸とbの最後から2番目の軸の合計積です

103
Alex Riley

@ajcrによる答えは、dotmatmul@シンボルによって呼び出される)の違いを説明しています。簡単な例を見ると、「行列のスタック」またはテンソルを操作するときに2つの動作が異なることが明確にわかります。

違いを明確にするために、4x4の配列を取り、2x4x3の「行列のスタック」またはテンソルを持つdot productとmatmul productを返します。

import numpy as np
fourbyfour = np.array([
                       [1,2,3,4],
                       [3,2,1,4],
                       [5,4,6,7],
                       [11,12,13,14]
                      ])


twobyfourbythree = np.array([
                             [[2,3],[11,9],[32,21],[28,17]],
                             [[2,3],[1,9],[3,21],[28,7]],
                             [[2,3],[1,9],[3,21],[28,7]],
                            ])

print('4x4*4x2x3 dot:\n {}\n'.format(np.dot(fourbyfour,twobyfourbythree)))
print('4x4*4x2x3 matmul:\n {}\n'.format(np.matmul(fourbyfour,twobyfourbythree)))

各操作の製品を以下に示します。内積がどのようであるかに注目してください。

... aの最後の軸とbの最後から2番目の軸上の積

マトリックスを一緒にブロードキャストすることにより、マトリックス製品がどのように形成されるか。

4x4*4x2x3 dot:
 [[[232 152]
  [125 112]
  [125 112]]

 [[172 116]
  [123  76]
  [123  76]]

 [[442 296]
  [228 226]
  [228 226]]

 [[962 652]
  [465 512]
  [465 512]]]

4x4*4x2x3 matmul:
 [[[232 152]
  [172 116]
  [442 296]
  [962 652]]

 [[125 112]
  [123  76]
  [228 226]
  [465 512]]

 [[125 112]
  [123  76]
  [228 226]
  [465 512]]]
8
Nathan

数学では、numpyのdotがより理にかなっていると思います

dot(a、b)_ {i、j、k、a、b、c} =\sum_m a_ {i、j、k、m } b_ {a、b、m、c}

aとbがベクトルの場合は内積を、aとbが行列の場合は行列乗算を与えるため


Numpyでのmatmul演算は、dot結果の一部で構成されます、それは次のように定義できます

matmul(a、b)_ {i、j、k、c} =\sum_m a_ {i、j、k、m} b_ {i 、j、m、c}


そのため、matmul(a、b)は小さな形状の配列を返し、メモリ消費量が少なく、アプリケーションでより理にかなっていることがわかります。特に、 broadcasting と組み合わせると、次のようになります

matmul(a、b)_ {i、j、k、l} =\sum_m a_ {i、j、k、m} b_ {j 、m、l}

例えば。


上記の2つの定義から、これら2つの操作を使用するための要件を確認できます。 a.shape =(s1、s2、s3、s4)およびb.shape =(t1、t2 、t3、t4)

  • dot(a、b)を使用するには、必要です

     1. **t3=s4**;
    
  • matmul(a、b)を使用するには、必要です

    1. t3 = s4
    2. t2 = s2、またはt2とs2のいずれかが1
    3. t1 = s1、またはt1とs1のいずれかが1

次のコードを使用して、自分を納得させてください。

コードサンプル

import numpy as np
for it in xrange(10000):
    a = np.random.Rand(5,6,2,4)
    b = np.random.Rand(6,4,3)
    c = np.matmul(a,b)
    d = np.dot(a,b)
    #print 'c shape: ', c.shape,'d shape:', d.shape

    for i in range(5):
        for j in range(6):
            for k in range(2):
                for l in range(3):
                    if not c[i,j,k,l] == d[i,j,k,j,l]:
                        print it,i,j,k,l,c[i,j,k,l]==d[i,j,k,j,l] #you will not see them
3
Yong Yang