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)

문서에서 :

matmuldot두 가지 중요한 점에서 다릅니다 .

  • 스칼라에 의한 곱셈은 허용되지 않습니다.
  • 행렬의 스택은 마치 행렬이 요소 인 것처럼 함께 브로드 캐스트됩니다.

마지막 요점은 3D (또는 더 높은 차원의) 배열을 전달할 때 dotmatmul메서드가 다르게 작동 한다는 것을 분명히 합니다. 문서에서 좀 더 인용하면 :

대상 matmul:

인수 중 하나가 ND, N> 2이면 마지막 두 인덱스에있는 행렬 스택으로 처리되고 그에 따라 브로드 캐스트됩니다.

대상 np.dot:

2 차원 배열의 경우 행렬 곱셈과 동일하고 1 차원 배열의 경우 벡터의 내적 (복소 켤레 없음)에 해당합니다. N 차원의 경우 a의 마지막 축과 b의 마지막에서 두 번째 축에 대한 합계 제품입니다.


13
여기서 혼란은 아마도 릴리스 노트 때문일 것입니다.이 릴리스 노트는 예제 코드에서 "@"기호를 numpy의 dot () 함수와 직접 동일시합니다.
Alex K

12

@ajcr의 대답은 dotmatmul( @기호로 호출 됨 )가 어떻게 다른지 설명합니다 . 간단한 예를 살펴보면 'stacks of matricies'또는 tensor에서 작동 할 때 두 가지가 어떻게 다르게 작동하는지 분명히 알 수 있습니다.

차이점을 명확히하기 위해 4x4 배열을 사용하고 3x4x2 'stack of matricies'또는 tensor로 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의 마지막에서 두 번째 축에 대한 합계 곱

및 매트릭스를 함께 브로드 캐스트하여 매트릭스 제품이 형성되는 방법.

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]) ------- 문서와 같이 말합니다 : it is a a의 마지막 축과 b의 마지막에서 두 번째 축에 대한 합계 제품 :
Ronak Agrawal

그러나 좋은 캐치는 3x4x2입니다. 행렬을 만드는 또 다른 방법 a = np.arange(24).reshape(3, 4, 2)은 차원이 3x4x2 인 배열을 만드는 것입니다.
Nathan

8

그냥 참고로, @그 NumPy와 등가물 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가 행렬 일 때 행렬 곱셈을 제공하기 때문에


관해서 matmul NumPy와 동작에서, 그것의 일부를 구성 도트 결과,과 같이 정의 될 수있다

> matmul (a, b) _ {i, j, k, c} =공식

따라서 matmul (a, b) 가 작은 모양의 배열을 반환 한다는 것을 알 수 있습니다 . 이는 메모리 소비가 적고 응용 프로그램에서 더 의미가 있습니다. 특히 방송 과 결합 하면

matmul (a, b) _ {i, j, k, l} =공식

예를 들면.


위의 두 정의에서 두 작업을 사용하기위한 요구 사항을 볼 수 있습니다. 가정 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 : Shape of Passed values ​​is (200, 1), indices imply (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)"

DOT 포함

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.