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


119

私は最近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 dotで再現できますか?他に大きな違いはありますか?


5
ドットからその結果を得ることができません。dotが高次元の入力を処理することは、間違った設計上の決定であったことに、人々は概ね同意したと思います。
user2357112は

彼らが何matmul年も前に機能を実装しなかったのはなぜですか? @中置演算子は新しいものですが、関数はそれなしでも同様に機能します。
hpaulj

回答:


140

@オペレータは、アレイの呼び出す__matmul__方法、ありませんdot。このメソッドは、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)

ドキュメントから:

matmuldot2つの重要な点で異なります。

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

最後の点は、3D(またはそれ以上の次元)の配列を渡した場合dotmatmulメソッドの動作が異なることを明らかにしています。ドキュメントからさらに引用:

の場合matmul

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

の場合np.dot

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


13
ここでの混乱は、おそらくリリースノートが原因であると考えられます。これは、サンプルコードで "@"記号をnumpyのdot()関数に直接対応させています。
Alex K

12

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

違いを明確にするために、4x4配列を取り、3x4x2の「行列のスタック」またはテンソルを使用してdot積とmatmul積を返します。

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


threebyfourbytwo = 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*3x4x2 dot:\n {}\n'.format(np.dot(fourbyfour,twobyfourbythree)))
print('4x4*3x4x2 matmul:\n {}\n'.format(np.matmul(fourbyfour,twobyfourbythree)))

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

... aの最後の軸とbの最後から2番目の軸の合計

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

4x4*3x4x2 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*3x4x2 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]]]

2
dot(a、b)[i、j、k、m] = sum(a [i、j ,:] * b [k、:、m])-------ドキュメントのように:それはa aの最後の軸とbの最後から2番目の軸の積の合計:
Ronak Agrawal 2018

良いキャッチ、しかし、それは3x4x2です。マトリックスを作成する別の方法a = np.arange(24).reshape(3, 4, 2)は、3x4x2の次元を持つ配列を作成することです。
ネイサン

8

参考までに@、それに相当する相当数でdotmatmulほぼすべて同じくらい高速です。(私のプロジェクトであるperfplotで作成されたプロット)

ここに画像の説明を入力してください

プロットを再現するコード:

import perfplot
import numpy


def setup(n):
    A = numpy.random.rand(n, n)
    x = numpy.random.rand(n)
    return A, x


def at(data):
    A, x = data
    return A @ x


def numpy_dot(data):
    A, x = data
    return numpy.dot(A, x)


def numpy_matmul(data):
    A, x = data
    return numpy.matmul(A, x)


perfplot.show(
    setup=setup,
    kernels=[at, numpy_dot, numpy_matmul],
    n_range=[2 ** k for k in range(12)],
    logx=True,
    logy=True,
)

7

数学では、numpy のドットはもっと理にかなっていると思います

ドット(a、b)_ {i、j、k、a、b、c} =式

これは、aとbがベクトルの場合はドット積を、aとbが行列の場合は行列乗算を行うためです。


numpy でのmatmul操作に関しては、ドット結果の一部で構成され、次のように定義できます

> matmul(a、b)_ {i、j、k、c} =式

したがって、matmul(a、b)は形状が小さい配列を返すことがわかります。これは、メモリ消費が少なく、アプリケーションでより意味のあるものです。特に、放送と組み合わせると、

matmul(a、b)_ {i、j、k、l} =式

例えば。


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

  • 使用するドット(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

np.matmulまた、ベクトルにはドット積を、行列には​​マトリックス積を与えます。
Subhaneil Lahiri、

2

以下はnp.einsum、インデックスがどのように投影されるかを示す比較です

np.allclose(np.einsum('ijk,ijk->ijk', a,b), a*b)        # True 
np.allclose(np.einsum('ijk,ikl->ijl', a,b), a@b)        # True
np.allclose(np.einsum('ijk,lkm->ijlm',a,b), a.dot(b))   # True

0

MATMULとDOTの私の経験

MATMULを使用しようとすると、常に「ValueError:渡された値の形状は(200、1)、インデックスは(200、3)を意味する」と常に表示されていました。迅速な回避策が必要でしたが、DOTが同じ機能を提供することがわかりました。DOTを使用してもエラーは発生しません。正解

MATMUL

X.shape
>>>(200, 3)

type(X)

>>>pandas.core.frame.DataFrame

w

>>>array([0.37454012, 0.95071431, 0.73199394])

YY = np.matmul(X,w)

>>>  ValueError: Shape of passed values is (200, 1), indices imply (200, 3)"

ドット付き

YY = np.dot(X,w)
# no error message
YY
>>>array([ 2.59206877,  1.06842193,  2.18533396,  2.11366346,  0.28505879, 

YY.shape

>>> (200, )
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.