einsum
작동 방식을 정확하게 이해하기 위해 고심하고 있습니다. 설명서와 몇 가지 예를 살펴 보았지만 충실하지 않은 것 같습니다.
다음은 수업 시간에 진행 한 예입니다.
C = np.einsum("ij,jk->ki", A, B)
두 개의 배열 A
과B
나는 이것이 걸릴 것이라고 생각 A^T * B
하지만 확실하지 않습니다 (그중 하나의 조옮김을하고 있습니까?). 아무도 여기서 무슨 일이 일어나고 있는지 (그리고 일반적으로을 사용할 때 einsum
) 정확하게 나를 안내 할 수 있습니까 ?
답변
(참고 :이 답변은 짧은 기반으로 블로그 게시물 에 대해 einsum
내가 얼마 전에 썼다.)
무엇을 einsum
합니까?
우리는이 다차원 배열을 가지고 있다고 상상 A
하고 B
. 이제 우리가 원한다고 가정 해 봅시다 …
- 곱셈
A
과B
제품의 새로운 배열을 생성하는 특정 방법으로; 그리고 아마도 - 특정 축을 따라이 새 배열을 합산 하십시오. 그리고 아마도
- 트랜스 특정 순서로 새로운 배열 축을.
좋은 기회가있어 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]])
우리는 곱합니다 A
및 B
요소 현명하고 새로운 배열의 행을 따라 요약. “정상”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
. -
두 입력 배열에서 레이블 을 반복함으로써이 두 축을 곱 해야한다는 것을 알 수 있습니다. 다시 말해, 배열 과 마찬가지로 배열의 각 열을 곱하는 것 입니다.
i
einsum
A
B
A[:, 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)
. 다음 은 함수에서 얻은 A
and B
및 출력 배열 의 레이블을 보여주는 그림입니다 .
라벨 j
이 반복되는 것을 볼 수 있습니다. 이는의 행 A
과의 열을 곱한다는 의미 입니다 B
. 또한 레이블 j
은 출력에 포함되지 않습니다. 우리는 이러한 제품을 요약합니다. 레이블 i
과 k
출력을 위해 유지되므로 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 배열의 경우 C
및 D
축이 호환 가능한 길이 (동일한 길이 또는 길이가 1 인 경우)는 다음과 같습니다.
-
C
(대각선의 합)의 흔적은 다음과np.trace(C)
같이 쓸 수 있습니다.np.einsum('ii', C)
-
요소 – 지혜의 곱셈
C
과의 전치D
,C * D.T
, 쓸 수있다 :np.einsum('ij,ji->ij', C, D)
-
4D 배열을 만들기 위해
C
배열에 각 요소를 곱하면 다음과 같이 쓸 수 있습니다.D
C[:, :, None, None] * D
np.einsum('ij,kl->ijkl', C, D)
답변
numpy.einsum()
직관적으로 이해한다면 아이디어 를 이해하는 것은 매우 쉽습니다. 예를 들어, 행렬 곱셈 과 관련된 간단한 설명으로 시작해 봅시다 .
를 사용하려면 numpy.einsum()
소위 첨자 문자열 을 인수로 전달한 다음 입력 배열 을 전달하면 됩니다 .
두 개의 2D 배열이 A
있고 B
행렬 곱셈을 원한다고 가정 해 봅시다 . 그래서 당신은 :
np.einsum("ij, jk -> ik", A, B)
여기서 첨자 스트링 ij
어레이에 대응 A
그동안 첨자 스트링 jk
어레이에 대응한다 B
. 또한 여기에서 가장 중요한 것은 각 첨자 문자열 의 문자 수가 배열의 크기와 일치 해야한다는 것 입니다. (즉, 2D 배열의 경우 2 문자, 3D 배열의 경우 3 문자 등) 그리고 아래 첨자 문자열 사이에 문자를 반복하면 ( 이 경우) 합계 가 해당 차원을 따라 발생 한다는 것을 의미 합니다. 따라서 합산됩니다. (즉, 그 차원은 사라질 것이다 ) j
ein
이 뒤에 있는 첨자 문자열->
은 결과 배열입니다. 비워두면 모든 것이 합산되고 스칼라 값이 결과로 반환됩니다. 그렇지 않으면 결과 배열은 아래 첨자 문자열 에 따라 치수를 갖습니다 . 이 예에서는 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
답변
서로 다르지만 호환 가능한 크기로 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
의 마지막 중 B
1 번째입니다 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
변경하십시오 .C
ijk
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
의 끝에 A
및 i
의 앞에 차원을 추가 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]
답변
내가 찾은 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]])
>>>
답변
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의 모든 출력 인덱스에 대해 루프가 있습니다. 지금은 범위를 결정하지 않은 채로 둡니다.
다음으로 왼쪽을 봅니다 . 오른쪽에 나타나지 않는 범위 변수가 있습니까? 우리의 경우 – 예, 하고 . 이러한 모든 변수에 대해 내부 중첩 루프를 추가하십시오 .h
w
for
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]
답변
가장 간단한 예는 tensorflow 문서 에 있다고 생각합니다.
방정식을 einsum 표기법으로 변환하는 네 단계가 있습니다. 이 방정식을 예로 들어 봅시다.C[i,k] = sum_j A[i,j] * B[j,k]
- 먼저 변수 이름을 삭제합니다. 우리는 얻는다
ik = sum_j ij * jk
sum_j
암시 적으로 용어를 삭제합니다 . 우리는 얻는다ik = ij * jk
- 로 교체
*
합니다,
. 우리는 얻는다ik = ij, jk
- 출력은 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]
답변
