NumPy의 이해


190

einsum작동 방식을 정확하게 이해하기 위해 고심하고 있습니다. 설명서와 몇 가지 예를 살펴 보았지만 충실하지 않은 것 같습니다.

다음은 수업 시간에 진행 한 예입니다.

C = np.einsum("ij,jk->ki", A, B)

두 개의 배열 AB

나는 이것이 걸릴 것이라고 생각 A^T * B하지만 확실하지 않습니다 (그중 하나의 조옮김을하고 있습니까?). 아무도 여기서 무슨 일이 일어나고 있는지 (그리고 일반적으로을 사용할 때 einsum) 정확하게 나를 안내 할 수 있습니까 ?


7
실제로는 (A * B)^T같거나 같습니다 B^T * A^T.
Tigran Saluev

23
나는 einsum 여기 에 대한 기본 사항에 대한 간단한 블로그 게시물을 썼습니다 . (가장 유용한 경우 스택 오버플로에 대한 답변에 가장 관련있는 비트를 이식하게되어 기쁩니다).
Alex Riley

1
@ajcr-아름다운 링크. 감사. numpy세부 사항을 설명 할 때 문서는 비참하게 부족하다.
rayryeng

자신감의 투표에 감사드립니다! 뒤늦게, 나는 아래답변을하였습니다 .
Alex Riley

파이썬에서 *이것은 행렬 곱셈이 아니라 요소 별 곱셈입니다. 조심해!
ComputerScientist

답변:


371

(참고 :이 답변은 짧은 기반으로 블로그 게시물 에 대해 einsum내가 얼마 전에 썼다.)

무엇을 einsum합니까?

우리는이 다차원 배열을 가지고 있다고 상상 A하고 B. 이제 우리가 원한다고 가정 해 봅시다 ...

  • 곱셈 AB제품의 새로운 배열을 생성하는 특정 방법으로; 그리고 아마도
  • 특정 축을 따라이 새 배열을 합산 하십시오. 그리고 아마도
  • 트랜스 특정 순서로 새로운 배열 축을.

좋은 기회가있어 einsum우리가이보다 빠르고 메모리 효율적으로 NumPy와 기능의 조합이 좋아하는 것을 할 도움이 될 것입니다 multiply, sum그리고 transpose수는.

어떻게 einsum작동합니까?

다음은 간단한 (완전히 사소한 것은 아님) 예제입니다. 다음 두 배열을 사용하십시오.

A = np.array([0, 1, 2])

B = np.array([[ 0,  1,  2,  3],
              [ 4,  5,  6,  7],
              [ 8,  9, 10, 11]])

우리는 곱합니다 AB요소 현명하고 새로운 배열의 행을 따라 요약. "정상"NumPy에서 우리는 다음과 같이 쓸 것입니다 :

>>> (A[:, np.newaxis] * B).sum(axis=1)
array([ 0, 22, 76])

따라서 여기에서 인덱싱 작업 A은 곱셈을 브로드 캐스트 할 수 있도록 두 배열의 첫 번째 축을 정렬합니다. 그런 다음 제품 배열의 행을 합하여 답을 반환합니다.

이제 einsum대신 사용하고 싶다면 다음 과 같이 쓸 수 있습니다.

>>> np.einsum('i,ij->i', A, B)
array([ 0, 22, 76])

서명 문자열은 'i,ij->i'여기 열쇠와 설명이 조금 필요합니다. 두 부분으로 생각할 수 있습니다. 왼쪽 () 왼쪽에 ->두 개의 입력 배열에 레이블을 지정했습니다. 의 오른쪽에는 ->끝으로 배열을 표시 했습니다 .

다음은 다음과 같은 일입니다.

  • A하나의 축을 가지고; 라벨이 붙어 있습니다 i. 그리고 B두 개의 축이 있습니다. 우리는 축 0을 레이블로 지정 i하고 축 1을 레이블로 지정 했습니다 j.

  • 두 입력 배열에서 레이블 을 반복함으로써이 두 축을 해야한다는 것을 알 있습니다. 다시 말해, 배열 과 마찬가지로 배열의 각 열을 곱하는 것 입니다.ieinsumABA[:, np.newaxis] * B

  • 공지 사항 j우리의 원하는 출력의 레이블로 표시되지 않습니다; 우리는 방금 사용했습니다 i(1D 배열로 끝내고 싶습니다). 에 의해 생략 라벨을, 우리는 이야기하고 einsum하는 요약 이 축을 따라. 다시 말해, 우리는 제품의 행을 합산합니다 .sum(axis=1).

기본적으로 사용하기 위해 알아야 할 모든 것 einsum입니다. 조금 연주하는 데 도움이됩니다. 출력에 두 레이블을 모두 남겨두면 'i,ij->ij'2D 제품 배열 (와 동일 A[:, np.newaxis] * B)을 다시 얻습니다 . 출력 레이블이 없다고 'i,ij->하면 단일 숫자를 반환합니다 (와 동일 (A[:, np.newaxis] * B).sum()).

그러나 가장 큰 장점은 einsum제품의 임시 배열을 먼저 구축하지 않는다는 것입니다. 제품을 그대로 합산합니다. 이로 인해 메모리 사용이 크게 절약 될 수 있습니다.

약간 더 큰 예

내적을 설명하기 위해 다음 두 가지 새로운 배열이 있습니다.

A = array([[1, 1, 1],
           [2, 2, 2],
           [5, 5, 5]])

B = array([[0, 1, 0],
           [1, 1, 0],
           [1, 1, 1]])

를 사용하여 내적을 계산합니다 np.einsum('ij,jk->ik', A, B). 다음 은 함수에서 얻은 Aand B및 출력 배열 의 레이블을 보여주는 그림입니다 .

여기에 이미지 설명을 입력하십시오

라벨 j이 반복되는 것을 볼 수 있습니다. 이는의 행 A과의 열을 곱한다는 의미 입니다 B. 또한 레이블 j은 출력에 포함되지 않습니다. 우리는 이러한 제품을 요약합니다. 레이블 ik출력을 위해 유지되므로 2D 배열을 다시 얻습니다.

이 결과를 레이블 j이 합산 되지 않은 배열과 비교하는 것이 더 명확 할 수 있습니다 . 아래에서 왼쪽에는 글의 결과 인 3D 배열을 볼 수 있습니다 np.einsum('ij,jk->ijk', A, B)(예 j: label을 유지했습니다 ).

여기에 이미지 설명을 입력하십시오

합산 축은 j오른쪽에 표시된 예상 내적을 제공합니다.

일부 운동

에 대한 느낌을 높이려면 einsum아래 첨자 표기법을 사용하여 익숙한 NumPy 배열 연산을 구현하는 것이 유용 할 수 있습니다. 곱하기 축과 합산 축의 조합과 관련된 모든 것은를 사용하여 작성할 수 있습니다 einsum.

A와 B를 길이가 같은 두 개의 1D 배열로 둡니다. 예를 들어, A = np.arange(10)B = np.arange(5, 15).

  • 합계는 다음과 A같이 쓸 수 있습니다.

    np.einsum('i->', A)
  • 요소 별 곱셈 A * B은 다음과 같이 쓸 수 있습니다.

    np.einsum('i,i->i', A, B)
  • 내부 제품 또는 내적 제품 np.inner(A, B)또는 np.dot(A, B)은 다음과 같이 쓸 수 있습니다.

    np.einsum('i,i->', A, B) # or just use 'i,i'
  • 외부 제품 np.outer(A, B)은 다음과 같이 작성할 수 있습니다.

    np.einsum('i,j->ij', A, B)

2D 배열의 경우 CD축이 호환 가능한 길이 (동일한 길이 또는 길이가 1 인 경우)는 다음과 같습니다.

  • C(대각선의 합)의 흔적은 다음과 np.trace(C)같이 쓸 수 있습니다.

    np.einsum('ii', C)
  • 요소 - 지혜의 곱셈 C과의 전치 D, C * D.T, 쓸 수있다 :

    np.einsum('ij,ji->ij', C, D)
  • 4D 배열을 만들기 위해 C배열에 각 요소를 곱하면 다음과 같이 쓸 수 있습니다.DC[:, :, None, None] * D

    np.einsum('ij,kl->ijkl', C, D)  

1
아주 좋은 설명 감사합니다. "내가 원하는 결과물에 레이블로 표시되지 않는다는 알림"-그렇지 않습니까?
Ian Hincks

감사합니다 @IanHincks! 오타처럼 보입니다. 지금 수정했습니다.
Alex Riley

1
아주 좋은 대답입니다. ij,jk행렬 곱셈을 형성하기 위해 (화살표없이) 스스로 작동 할 수 있다는 점도 주목할 가치가 있습니다. 그러나 명확성을 위해 화살표와 출력 치수를 넣는 것이 가장 좋습니다. 블로그 게시물에 있습니다.
ComputerScientist

1
@Peaceful : 이것은 올바른 단어를 선택하기 어려운 경우 중 하나입니다! "열" A은 길이가 열의 길이와 동일하기 때문에 길이가 3 이기 때문에 여기에 조금 더 잘 맞습니다 B(행의 B길이는 4이며 요소별로 곱할 수는 없습니다 A).
Alex Riley

1
참고 생략하는 것을 ->의미 영향 "출력 축이 순으로 재 배열되기 때문에 암시 모드를 선택한 첨자 중요이 수단을 포함한다. np.einsum('ij', a)2 차원 배열에 영향을주지 않는 반면, np.einsum('ji', a)그 전치했습니다."
BallpointBen

40

numpy.einsum()직관적으로 이해한다면 아이디어 를 이해하는 것은 매우 쉽습니다. 예를 들어, 행렬 곱셈 과 관련된 간단한 설명으로 시작해 봅시다 .


를 사용하려면 numpy.einsum()소위 첨자 문자열 을 인수로 전달한 다음 입력 배열 을 전달하면 됩니다 .

두 개의 2D 배열이 A있고 B행렬 곱셈을 원한다고 가정 해 봅시다 . 그래서 당신은 :

np.einsum("ij, jk -> ik", A, B)

여기서 첨자 스트링 ij 어레이에 대응 A그동안 첨자 스트링 jk 어레이에 대응한다 B. 또한 여기에서 가장 중요한 것은 각 첨자 문자열 의 문자 수가 배열의 크기와 일치 해야한다는 것 입니다. (즉, 2D 배열의 경우 2 문자, 3D 배열의 경우 3 문자 등) 그리고 아래 첨자 문자열 사이에 문자를 반복하면 ( 이 경우) 합계 가 해당 차원을 따라 발생 한다는 것을 의미 합니다. 따라서 합산됩니다. (즉, 그 차원은 사라질 것이다 ) jein

이 뒤에 있는 첨자 문자열-> 은 결과 배열입니다. 비워두면 모든 것이 합산되고 스칼라 값이 결과로 반환됩니다. 그렇지 않으면 결과 배열은 아래 첨자 문자열 에 따라 치수를 갖습니다 . 이 예에서는 ik입니다. 이것은 행렬 곱셈의 경우 배열의 열 수가 배열 A의 행 수와 일치해야 한다는 것을 알고 있기 때문에 직관적입니다 B(예 : 첨자 문자열j 에서 문자 를 반복 하여이 지식을 인코딩 합니다 )


다음은 np.einsum()일반적인 텐서 또는 nd-array 연산을 간결하게 구현 하는 데 사용 / 출력을 나타내는 몇 가지 예입니다 .

입력

# a vector
In [197]: vec
Out[197]: array([0, 1, 2, 3])

# an array
In [198]: A
Out[198]: 
array([[11, 12, 13, 14],
       [21, 22, 23, 24],
       [31, 32, 33, 34],
       [41, 42, 43, 44]])

# another array
In [199]: B
Out[199]: 
array([[1, 1, 1, 1],
       [2, 2, 2, 2],
       [3, 3, 3, 3],
       [4, 4, 4, 4]])

1) 행렬 곱셈 (와 유사 np.matmul(arr1, arr2))

In [200]: np.einsum("ij, jk -> ik", A, B)
Out[200]: 
array([[130, 130, 130, 130],
       [230, 230, 230, 230],
       [330, 330, 330, 330],
       [430, 430, 430, 430]])

2) 주 대각선을 따라 요소 추출 (과 유사 np.diag(arr))

In [202]: np.einsum("ii -> i", A)
Out[202]: array([11, 22, 33, 44])

3)하다 마드 곱 (즉, 두 배열의 요소 별 곱) (와 유사 arr1 * arr2)

In [203]: np.einsum("ij, ij -> ij", A, B)
Out[203]: 
array([[ 11,  12,  13,  14],
       [ 42,  44,  46,  48],
       [ 93,  96,  99, 102],
       [164, 168, 172, 176]])

4) 소자 현명한 제곱 (유사 np.square(arr)하거나 arr ** 2)

In [210]: np.einsum("ij, ij -> ij", B, B)
Out[210]: 
array([[ 1,  1,  1,  1],
       [ 4,  4,  4,  4],
       [ 9,  9,  9,  9],
       [16, 16, 16, 16]])

주 대각선 요소 5) 추적 (즉, 합계) (유사한 np.trace(arr))

In [217]: np.einsum("ii -> ", A)
Out[217]: 110

6) 매트릭스 전치 (와 유사 np.transpose(arr))

In [221]: np.einsum("ij -> ji", A)
Out[221]: 
array([[11, 21, 31, 41],
       [12, 22, 32, 42],
       [13, 23, 33, 43],
       [14, 24, 34, 44]])

7) 외부 벡터 ( 벡터) (와 유사 np.outer(vec1, vec2))

In [255]: np.einsum("i, j -> ij", vec, vec)
Out[255]: 
array([[0, 0, 0, 0],
       [0, 1, 2, 3],
       [0, 2, 4, 6],
       [0, 3, 6, 9]])

8) 내부 벡터 ( 벡터) (와 유사 np.inner(vec1, vec2))

In [256]: np.einsum("i, i -> ", vec, vec)
Out[256]: 14

9) 축 0을 따라 합 (와 유사 np.sum(arr, axis=0))

In [260]: np.einsum("ij -> j", B)
Out[260]: array([10, 10, 10, 10])

10) 축 1을 따라 합 (와 유사 np.sum(arr, axis=1))

In [261]: np.einsum("ij -> i", B)
Out[261]: array([ 4,  8, 12, 16])

11) 배치 행렬 곱셈

In [287]: BM = np.stack((A, B), axis=0)

In [288]: BM
Out[288]: 
array([[[11, 12, 13, 14],
        [21, 22, 23, 24],
        [31, 32, 33, 34],
        [41, 42, 43, 44]],

       [[ 1,  1,  1,  1],
        [ 2,  2,  2,  2],
        [ 3,  3,  3,  3],
        [ 4,  4,  4,  4]]])

In [289]: BM.shape
Out[289]: (2, 4, 4)

# batch matrix multiply using einsum
In [292]: BMM = np.einsum("bij, bjk -> bik", BM, BM)

In [293]: BMM
Out[293]: 
array([[[1350, 1400, 1450, 1500],
        [2390, 2480, 2570, 2660],
        [3430, 3560, 3690, 3820],
        [4470, 4640, 4810, 4980]],

       [[  10,   10,   10,   10],
        [  20,   20,   20,   20],
        [  30,   30,   30,   30],
        [  40,   40,   40,   40]]])

In [294]: BMM.shape
Out[294]: (2, 4, 4)

12) 축 2를 따라 합 (와 유사 np.sum(arr, axis=2))

In [330]: np.einsum("ijk -> ij", BM)
Out[330]: 
array([[ 50,  90, 130, 170],
       [  4,   8,  12,  16]])

13) 배열의 모든 요소를 ​​합산하십시오 (와 유사 np.sum(arr))

In [335]: np.einsum("ijk -> ", BM)
Out[335]: 480

14) 여러 축에 대한 합계 (즉, 주 변화)
(와 유사 np.sum(arr, axis=(axis0, axis1, axis2, axis3, axis4, axis6, axis7)))

# 8D array
In [354]: R = np.random.standard_normal((3,5,4,6,8,2,7,9))

# marginalize out axis 5 (i.e. "n" here)
In [363]: esum = np.einsum("ijklmnop -> n", R)

# marginalize out axis 5 (i.e. sum over rest of the axes)
In [364]: nsum = np.sum(R, axis=(0,1,2,3,4,6,7))

In [365]: np.allclose(esum, nsum)
Out[365]: True

15) Double Dot Products ( np.sum (hadamard-product) 와 유사 , 3 참조 )

In [772]: A
Out[772]: 
array([[1, 2, 3],
       [4, 2, 2],
       [2, 3, 4]])

In [773]: B
Out[773]: 
array([[1, 4, 7],
       [2, 5, 8],
       [3, 6, 9]])

In [774]: np.einsum("ij, ij -> ", A, B)
Out[774]: 124

16) 2D 및 3D 배열 곱셈

이러한 곱셈은 결과를 검증하려는 선형 방정식 시스템 ( Ax = b )을 풀 때 매우 유용 할 수 있습니다 .

# inputs
In [115]: A = np.random.rand(3,3)
In [116]: b = np.random.rand(3, 4, 5)

# solve for x
In [117]: x = np.linalg.solve(A, b.reshape(b.shape[0], -1)).reshape(b.shape)

# 2D and 3D array multiplication :)
In [118]: Ax = np.einsum('ij, jkl', A, x)

# indeed the same!
In [119]: np.allclose(Ax, b)
Out[119]: True

반대로이 np.matmul()검증 에 사용해야 하는 경우 reshape다음과 같은 결과를 얻기 위해 몇 가지 작업을 수행해야합니다.

# reshape 3D array `x` to 2D, perform matmul
# then reshape the resultant array to 3D
In [123]: Ax_matmul = np.matmul(A, x.reshape(x.shape[0], -1)).reshape(x.shape)

# indeed correct!
In [124]: np.allclose(Ax, Ax_matmul)
Out[124]: True

보너스 : 더 많은 수학 읽기 : Einstein-Summation 그리고 확실히 여기 : Tensor-Notation


7

서로 다르지만 호환 가능한 크기로 2 개의 배열을 만들어 상호 작용을 강조 할 수 있습니다.

In [43]: A=np.arange(6).reshape(2,3)
Out[43]: 
array([[0, 1, 2],
       [3, 4, 5]])


In [44]: B=np.arange(12).reshape(3,4)
Out[44]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

계산은 (4,2) 배열을 생성하기 위해 (2,3)과 (3,4)의 '점'(제품의 합계)을 취합니다. i의 첫 번째 희미 함 A입니다 C. k의 마지막 중 B1 번째입니다 C. j요약하면 '소비'됩니다.

In [45]: C=np.einsum('ij,jk->ki',A,B)
Out[45]: 
array([[20, 56],
       [23, 68],
       [26, 80],
       [29, 92]])

이것은 np.dot(A,B).T전치 된 최종 결과물 과 동일 합니다.

에 대한 자세한 내용을 보려면 아래 첨자를 다음으로 j변경하십시오 .Cijk

In [46]: np.einsum('ij,jk->ijk',A,B)
Out[46]: 
array([[[ 0,  0,  0,  0],
        [ 4,  5,  6,  7],
        [16, 18, 20, 22]],

       [[ 0,  3,  6,  9],
        [16, 20, 24, 28],
        [40, 45, 50, 55]]])

이것은 또한 다음과 같이 생성 될 수 있습니다 :

A[:,:,None]*B[None,:,:]

즉, k의 끝에 Ai의 앞에 차원을 추가 B하여 (2,3,4) 배열을 만듭니다.

0 + 4 + 16 = 20, 9 + 28 + 55 = 92등; j더 빠른 결과를 얻기 위해 합산 하고 조옮김 :

np.sum(A[:,:,None] * B[None,:,:], axis=1).T

# C[k,i] = sum(j) A[i,j (,k) ] * B[(i,)  j,k]

7

내가 찾은 NumPy와를 : 무역의 트릭 (파트 II) 교훈

출력 배열의 순서를 나타 내기 위해->를 사용합니다. 따라서 'ij, i-> j'는 왼쪽 (LHS)과 오른쪽 (RHS)을 갖는 것으로 생각하십시오. LHS에서 라벨을 반복하면 제품 요소를 현명하게 계산 한 다음 요약합니다. RHS (출력) 측의 레이블을 변경하여 입력 배열과 관련하여 진행할 축, 즉 축 0, 1을 따라 합산 등을 정의 할 수 있습니다.

import numpy as np

>>> a
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])
>>> b
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
>>> d = np.einsum('ij, jk->ki', a, b)

세 개의 축 i, j, k가 있으며 j가 반복됩니다 (왼쪽). i,j의 행과 열을 나타냅니다 a. j,k에 대한 b.

곱을 계산하고 j축을 정렬하려면에 축을 추가해야합니다 a. ( b첫 번째 축을 따라 브로드 캐스트됩니다 (?)).

a[i, j, k]
   b[j, k]

>>> c = a[:,:,np.newaxis] * b
>>> c
array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8]],

       [[ 0,  2,  4],
        [ 6,  8, 10],
        [12, 14, 16]],

       [[ 0,  3,  6],
        [ 9, 12, 15],
        [18, 21, 24]]])

j오른쪽에 없기 때문에 우리 j는 3x3x3 배열의 두 번째 축을 합산합니다.

>>> c = c.sum(1)
>>> c
array([[ 9, 12, 15],
       [18, 24, 30],
       [27, 36, 45]])

마지막으로, 인덱스는 오른쪽에서 (알파벳으로) 반대로 바뀌어 우리는 전치합니다.

>>> c.T
array([[ 9, 18, 27],
       [12, 24, 36],
       [15, 30, 45]])

>>> np.einsum('ij, jk->ki', a, b)
array([[ 9, 18, 27],
       [12, 24, 36],
       [15, 30, 45]])
>>>

NumPy와 : 무역의 트릭 (파트 II)는 사이트 소유자뿐만 아니라 워드 프레스 계정에서 초대 요구하는 것
테자스 셰티

... 업데이트 된 링크, 운 좋게 검색으로 찾았습니다. -Thnx.
wwii

@TejasShetty 여기에 더 나은 답변이 많이 있습니다-아마도이 답변을 삭제해야 할 것입니다.
wwii

2
답을 삭제하지 마십시오.
Tejas Shetty

5

einsum 방정식을 읽을 때 나는 그것들을 정신적으로 그들의 명령형 버전으로 끓일 수있는 것이 가장 도움이된다는 것을 알았습니다.

다음과 같이 시작합니다 :

C = np.einsum('bhwi,bhwj->bij', A, B)

구두점을 먼저 살펴보면 화살표 앞에 쉼표로 구분 된 두 개의 blob이 bhwi있고 그 bhwj앞에 화살표가 있고 그 bij뒤에는 3 개의 문자가 하나 있습니다. 따라서 방정식은 2 개의 랭크 -4 텐서 입력으로부터 랭크 -3 텐서 결과를 생성합니다.

이제 각 얼룩의 각 문자를 범위 변수의 이름으로 지정하십시오. 블롭에 문자가 나타나는 위치는 해당 텐서에서 범위가 지정된 축의 인덱스입니다. 따라서 C의 각 요소를 생성하는 명령형 합산은 C의 각 인덱스마다 하나씩 3 개의 중첩 된 for 루프로 시작해야합니다.

for b in range(...):
    for i in range(...):
        for j in range(...):
            # the variables b, i and j index C in the order of their appearance in the equation
            C[b, i, j] = ...

따라서 기본적 for으로 C의 모든 출력 인덱스에 대해 루프가 있습니다. 지금은 범위를 결정하지 않은 채로 둡니다.

다음으로 왼쪽을 봅니다 . 오른쪽에 나타나지 않는 범위 변수가 있습니까? 우리의 경우 - 예, 하고 . 이러한 모든 변수에 대해 내부 중첩 루프를 추가하십시오 .hwfor

for b in range(...):
    for i in range(...):
        for j in range(...):
            C[b, i, j] = 0
            for h in range(...):
                for w in range(...):
                    ...

가장 안쪽 루프 안에는 이제 모든 인덱스가 정의되었으므로 실제 합계를 쓸 수 있고 번역이 완료됩니다.

# three nested for-loops that index the elements of C
for b in range(...):
    for i in range(...):
        for j in range(...):

            # prepare to sum
            C[b, i, j] = 0

            # two nested for-loops for the two indexes that don't appear on the right-hand side
            for h in range(...):
                for w in range(...):
                    # Sum! Compare the statement below with the original einsum formula
                    # 'bhwi,bhwj->bij'

                    C[b, i, j] += A[b, h, w, i] * B[b, h, w, j]

지금까지 코드를 따라갈 수 있었다면 축하합니다! 이것이 당신이 einsum 방정식을 읽는 데 필요한 전부입니다. 특히 원래 einsum 공식이 위 스 니펫의 최종 요약문에 어떻게 매핑되는지 확인하십시오. for-loops와 range bounds는 단지 보풀이며 마지막 진술은 실제로 무슨 일이 일어나고 있는지 이해하는 데 필요합니다.

완전성을 위해 각 범위 변수의 범위를 결정하는 방법을 살펴 보겠습니다. 각 변수의 범위는 단순히 색인을 생성하는 차원의 길이입니다. 변수가 하나 이상의 텐서에서 하나 이상의 차원을 인덱싱하는 경우 각 차원의 길이가 같아야합니다. 전체 범위와 함께 위의 코드는 다음과 같습니다.

# C's shape is determined by the shapes of the inputs
# b indexes both A and B, so its range can come from either A.shape or B.shape
# i indexes only A, so its range can only come from A.shape, the same is true for j and B
assert A.shape[0] == B.shape[0]
assert A.shape[1] == B.shape[1]
assert A.shape[2] == B.shape[2]
C = np.zeros((A.shape[0], A.shape[3], B.shape[3]))
for b in range(A.shape[0]): # b indexes both A and B, or B.shape[0], which must be the same
    for i in range(A.shape[3]):
        for j in range(B.shape[3]):
            # h and w can come from either A or B
            for h in range(A.shape[1]):
                for w in range(A.shape[2]):
                    C[b, i, j] += A[b, h, w, i] * B[b, h, w, j]

0

가장 간단한 예는 tensorflow 문서 에 있다고 생각합니다.

방정식을 einsum 표기법으로 변환하는 네 단계가 있습니다. 이 방정식을 예로 들어 봅시다.C[i,k] = sum_j A[i,j] * B[j,k]

  1. 먼저 변수 이름을 삭제합니다. 우리는 얻는다ik = sum_j ij * jk
  2. sum_j암시 적으로 용어를 삭제합니다 . 우리는 얻는다ik = ij * jk
  3. 로 교체 *합니다 ,. 우리는 얻는다ik = ij, jk
  4. 출력은 RHS에 있으며 ->부호 로 분리됩니다 . 우리는 얻는다ij, jk -> ik

einsum 통역사는이 4 단계를 반대로 실행합니다. 결과에서 누락 된 모든 지수는 합산됩니다.

문서에서 더 많은 예제가 있습니다.

# Matrix multiplication
einsum('ij,jk->ik', m0, m1)  # output[i,k] = sum_j m0[i,j] * m1[j, k]

# Dot product
einsum('i,i->', u, v)  # output = sum_i u[i]*v[i]

# Outer product
einsum('i,j->ij', u, v)  # output[i,j] = u[i]*v[j]

# Transpose
einsum('ij->ji', m)  # output[j,i] = m[i,j]

# Trace
einsum('ii', m)  # output[j,i] = trace(m) = sum_i m[i, i]

# Batch matrix multiplication
einsum('aij,ajk->aik', s, t)  # out[a,i,k] = sum_j s[a,i,j] * t[a, j, k]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.