NumPy의 이해
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
에서 char 를 반복 하여이 지식을 인코딩 합니다 )
다음은 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
Bonus: Read more math here : Einstein-Summation and definitely here: Tensor-Notation
Lets make 2 arrays, with different, but compatible dimensions to highlight their interplay
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]])
Your calculation, takes a 'dot' (sum of products) of a (2,3) with a (3,4) to produce a (4,2) array. i
is the 1st dim of A
, the last of C
; k
the last of B
, 1st of C
. j
is 'consumed' by the summation.
In [45]: C=np.einsum('ij,jk->ki',A,B)
Out[45]:
array([[20, 56],
[23, 68],
[26, 80],
[29, 92]])
This is the same as np.dot(A,B).T
- it's the final output that's transposed.
To see more of what happens to j
, change the C
subscripts to 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]]])
This can also be produced with:
A[:,:,None]*B[None,:,:]
That is, add a k
dimension to the end of A
, and an i
to the front of B
, resulting in a (2,3,4) array.
0 + 4 + 16 = 20
, 9 + 28 + 55 = 92
, etc; Sum on j
and transpose to get the earlier result:
np.sum(A[:,:,None] * B[None,:,:], axis=1).T
# C[k,i] = sum(j) A[i,j (,k) ] * B[(i,) j,k]
I found NumPy: The tricks of the trade (Part II) instructive
We use -> to indicate the order of the output array. So think of 'ij, i->j' as having left hand side (LHS) and right hand side (RHS). Any repetition of labels on the LHS computes the product element wise and then sums over. By changing the label on the RHS (output) side, we can define the axis in which we want to proceed with respect to the input array, i.e. summation along axis 0, 1 and so on.
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]])
>>>
참고 URL : https://stackoverflow.com/questions/26089893/understanding-numpys-einsum
'development' 카테고리의 다른 글
xcode에서 Base SDK, iOS 배포 대상, 대상 및 프로젝트의 의미는 무엇입니까 (0) | 2020.06.10 |
---|---|
C # 3.0 자동 속성 – 유용합니까? (0) | 2020.06.10 |
여기에 "Access-Control-Allow-Origin에서 원본을 사용할 수 없습니다"오류가 표시되는 이유는 무엇입니까? (0) | 2020.06.10 |
객체 메모리 주소 접근 (0) | 2020.06.10 |
두 개의 Git 커밋간에 변경된 모든 파일 목록을 얻는 방법은 무엇입니까? (0) | 2020.06.10 |