NumPy의 einsum에 대해서
어떻게 작동합니까?
된 어레이 정정 given given given정A
★★★★★★★★★★★★★★★★★」B
는 행렬 (transpose)를 사용하여 (A @ B).T
, 또는 동등하게 다음을 사용합니다.
np.einsum("ij, jk -> ki", A, B)
(주의: 이 답변은 다음 사항에 대한 짧은 블로그 게시물을 기반으로 합니다.einsum
」를 참조해 주세요).
무인 does does 가 뭐죠?einsum
할수 수 do do do?
배열이 두 개 생각해 보세요.A
★★★★★★★★★★★★★★★★★」B
아, 아, 아, 아...
-
A
B
으로, 그 후에 새로운 제품군을 만들 수 있습니다. - 이 새로운 배열을 특정 축을 따라 합산합니다.
- 새 배열의 축을 특정 순서로 바꿉니다.
높다einsum
를 사용하면 NumPy와 같은 보다 더 됩니다.multiply
,sum
★★★★★★★★★★★★★★★★★」transpose
용됩니니다다
는 어떻게 ?einsum
일?
여기 간단한 예가 있습니다(완전히 사소한 것은 아닙니다).다음 2개의 어레이를 예로 들어 보겠습니다.
A = np.array([0, 1, 2])
B = np.array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
A
★★★★★★★★★★★★★★★★★」B
새 배열의 행에 따라 합계를 구합니다.에서는 'normal' NumPy로
>>> (A[:, np.newaxis] * B).sum(axis=1)
array([ 0, 22, 76])
,기 operation operation의 합니다.A
는 곱셈을 브로드캐스트할 수 있도록 두 배열의 첫 번째 축을 정렬합니다.그런 다음 제품 배열의 행이 합산되어 답을 반환합니다.
요,그러면요,그러면요,그러면요.einsum
대신 다음과 같이 쓸 수 있습니다.
>>> np.einsum('i,ij->i', A, B)
array([ 0, 22, 76])
시그니처 문자열'i,ij->i'
여기에 열쇠가 있고 약간의 설명이 필요합니다.당신은 그것을 두 가지로 생각할 수 있습니다. 「」( 「」)->
어레이에 을 붙였습니다는 2개의 입력 어레이에 라벨을 붙였습니다.[ ]의 ->
최종적으로는 사용할 어레이에 라벨을 붙였습니다.
다음에 일어날 일은 다음과 같습니다.
A
축을 . 1개의 축을i
B
개의 축이 축을 '축 0'으로 했습니다. 0 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->
해서 1개의(A[:, np.newaxis] * B).sum()
를 참조해 주세요.
★★★★★★의 einsum
하지만, 그것은 먼저 제품의 일시적인 배열을 만드는 것이 아니라, 단지 제품들을 그대로 합산하는 것이다.이것에 의해, 메모리 사용량을 큰폭으로 삭감할 수 있습니다.
조금 더 큰 예
도트 제품에 대해 설명하려면 , 다음의 2개의 새로운 어레이를 소개합니다.
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
★★★★★★★★★★★★★★★★★」B
"CHANGE: "CHANGE:
라벨이 ?j
- 이 은 우리가 있다는 뜻입니다.A
of of 함께B
「한벨 . . . . . .」j
출력에 포함되어 있지 않습니다.이 제품들을 정리하고 있습니다. 벨i
★★★★★★★★★★★★★★★★★」k
출력용으로 보관되기 때문에 2D 어레이를 얻을 수 있습니다.
를 라벨이 더할 수 있습니다.j
가 합계되지 않았습니다.아래 왼쪽에는 쓰기 결과 3D 어레이가 표시됩니다.np.einsum('ij,jk->ijk', A, B)
(라벨, 라벨)을 지켰습니다.j
축 '''j
는 오른쪽에 표시된 예상 도트 곱을 나타냅니다.
몇 가지 연습
에 대한 느낌을 더 얻기 위해einsum
는, 첨자 표기법을 사용해 익숙한 NumPy 어레이 조작을 실장하는 것이 편리합니다.곱셈축과 합산축의 조합이 포함된 모든 것은 다음을 사용하여 작성할 수 있습니다.einsum
.
A B 2 D 1 D 。를 들어, 「」라고 하는 것은,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
길이가이 있는 다 둘중인 경우몇 가지 나타냅니다.
의
C
), (주대각선np.trace(C)
을 사용하다np.einsum('ii', C)
「」의 요소별
C
and and and and and and 의 전치D
,C * D.T
을 사용하다np.einsum('ij,ji->ij', C, D)
「」의 각
C
에 의해D
어레이를 ), (4D 어레이를 만드는 방법),C[:, :, None, None] * D
을 사용하다np.einsum('ij,kl->ijkl', C, D)
직감적으로 이해하면 이해하기가 매우 쉽습니다.예를 들어 행렬 곱셈에 관한 간단한 설명부터 시작하겠습니다.
를 사용하려면 소위 subscripts 문자열을 인수로 전달하고 그 뒤에 입력 배열을 전달하기만 하면 됩니다.
2D 어레이가 2개 있고 행렬 곱셈을 수행한다고 가정해 보겠습니다.그럼, 다음과 같이 해 주세요.
np.einsum("ij, jk -> ik", A, B)
여기서 서브스크립트ij
문자열은 어레이에 대응하고 서브스크립트jk
문자열은 어레이에 대응합니다.또한 여기서 가장 중요한 것은 각 스크립트 문자열의 문자 수가 어레이의 치수와 일치해야 한다는 것입니다(2D 어레이의 경우 2글자, 3D 어레이의 경우 3글자 등).(이 경우) 첨자 j
문자열 사이에 문자를 반복하는 경우, 즉,ein
그 차원을 따라 일어날 수 있는 총계입니다.따라서 합계가 감소됩니다(즉, 해당 차원이 사라집니다).
이 기호 뒤의 첨자 문자열은 결과 배열의 치수를 나타냅니다.빈칸으로 두면 모든 것이 합산되고 결과적으로 스칼라 값이 반환됩니다.그렇지 않으면 결과 배열의 치수가 첨자 문자열에 따라 지정됩니다.이 예에서는 입니다.행렬 곱셈이 작동하려면 배열 내의 열 수가 배열 내의 행 수와 일치해야 한다는 것을 알기 때문에 직관적입니다(즉, 이 지식을 아래 문자열에 있는 문자를 반복하여 인코딩합니다).
다음은 몇 가지 일반적인 텐서 또는 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) 행렬 곱셈 (와 유사)
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) 주대각선을 따라 요소를 추출한다(와 유사).
In [202]: np.einsum("ii -> i", A)
Out[202]: array([11, 22, 33, 44])
3) 아다마르 제품(즉, 2 어레이의 요소별 제품) (와 유사)
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) 요소별 제곱(또는 와 유사)
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) 트레이스(주대각 요소의 합계) (와 유사)
In [217]: np.einsum("ii -> ", A)
Out[217]: 110
6) 행렬 전치(와 유사)
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) (벡터의) 외적(와 유사)
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) (벡터의) 내적(와 유사)
In [256]: np.einsum("i, i -> ", vec, vec)
Out[256]: 14
9) 축 0에 따른 합(와 유사)
In [260]: np.einsum("ij -> j", B)
Out[260]: array([10, 10, 10, 10])
10) 축 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에 따른 합(와 유사)
In [330]: np.einsum("ijk -> ij", BM)
Out[330]:
array([[ 50, 90, 130, 170],
[ 4, 8, 12, 16]])
13) 배열 내의 모든 요소를 합산합니다(와 유사).
In [335]: np.einsum("ijk -> ", BM)
Out[335]: 480
축에 합 한계화14) 수수 14 14 주주 주주 ( 주주 )
(와 유사)
# 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 (adamard product) cf.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
반대로, 이 검증에 사용할 필요가 있는 경우는, 다음의 몇개의 조작을 실시할 필요가 있습니다.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
보너스: 여기서 더 많은 수학을 읽으세요: 아인슈타인 요약과 확실히 여기:텐서 주석
저는 아인섬 방정식을 읽을 때, 그것들을 필연적으로 정리할 수 있는 것이 가장 도움이 된다는 것을 알게 되었습니다.
먼저 다음(중요한) 문장으로 시작하겠습니다.
C = np.einsum('bhwi,bhwj->bij', A, B)
쉼표로 두 개 .bhwi
★★★★★★★★★★★★★★★★★」bhwj
, , 「3」 「 3 」 、 「 1 」bij
텐서입력으로부터 결과를 얻을 수 .따라서 방정식은 2개의 4등급 텐서 입력으로부터 3등급 텐서 결과를 생성한다.
이제 각 BLOB의 각 문자를 범위 변수의 이름으로 합니다.글자가 블럽에 나타나는 위치는 해당 텐서에서 글자가 범위로 지정되는 축의 인덱스입니다.따라서 C의 각 요소를 생성하는 필수 합계는 C의 각 인덱스에 대해 1개씩 루프에 대해 3개의 중첩으로 시작해야 합니다.
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 의 c c c c c c c c c c 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 수식이 위의 스니펫의 최종 요약문에 어떻게 매핑되는지 주목하십시오.포루프나 레인지의 경계는 그저 흐릿한 것이고, 그 최종 진술은 무슨 일이 일어나고 있는지 이해하는데 필요한 전부입니다.
완성도를 높이기 위해 각 범위 변수의 범위를 결정하는 방법을 살펴보겠습니다.각 변수의 범위는 단순히 색인화된 차원의 길이입니다.변수가 하나 이상의 텐서에서 두 개 이상의 차원을 인덱싱하는 경우 각 치수의 길이는 같아야 합니다.다음은 전체 범위를 포함한 코드입니다.
# 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]
에 대한 또 견해np.einsum
대부분의 답변은 예를 들어 설명하는데, 저는 추가 관점을 제시하려고 합니다.
요?einsum
이치노지정된 문자열에는 축을 나타내는 레이블인 첨자가 포함됩니다.작업 정의라고 생각하고 싶습니다.첨자는 두 가지 명백한 제약을 제공합니다.
각 입력 배열의 축 수,
입력 간의 축 크기 동일.
첫 번째 예를 np.einsum('ij,jk->ki', A, B)
여기서 제약조건 1.은 다음과 같습니다.A.ndim == 2
★★★★★★★★★★★★★★★★★」B.ndim == 2
, 및 2. to.A.shape[1] == B.shape[0]
.
나중에 알게 되겠지만 다른 제약사항이 있습니다.예:
출력 첨자의 레이블은 두 번 이상 표시되지 않아야 합니다.
출력 첨자의 레이블이 입력 첨자에 표시되어야 합니다.
★★★★★★★★★★★★를ij,jk->ki
을 사용하다
출력 어레이의 컴포넌트에 기여하는 입력 어레이의 컴포넌트.
첨자에는 출력 배열의 각 구성 요소에 대한 작업에 대한 정확한 정의가 포함되어 있습니다.
을 ij,jk->ki
및 의 입니다.A
★★★★★★★★★★★★★★★★★」B
:
>>> A = np.array([[1,4,1,7], [8,1,2,2], [7,4,3,4]])
>>> A.shape
(3, 4)
>>> B = np.array([[2,5], [0,1], [5,7], [9,2]])
>>> B.shape
(4, 2)
'''Z
ㄴㄴㄴㄴㄴㄴㄴㄴㄴㄴㄴㄴㄴㄴㄴㄴㄴ다.(B.shape[1], A.shape[0])
순진하게 다음과 같은 방식으로 구성될 수 있습니다.합니다.Z
:
Z = np.zeros((B.shape[1], A.shape[0]))
for i in range(A.shape[0]):
for j in range(A.shape[1]):
for k range(B.shape[0]):
Z[k, i] += A[i, j]*B[j, k] # ki <- ij*jk
np.einsum
는 출력 배열에 기여도를 축적하는 것입니다. ★★(A[i,j], B[j,k])
에게 기여하는 .Z[k, i]
★★★★★★ 。
아시겠지만 일반적인 행렬 곱셈을 계산하는 방법과 매우 유사합니다.
최소한의 구현
입니다.np.einsum
Python 서 python 。이것은 실제로 어떤 일이 벌어지고 있는지를 이해하는 데 도움이 될 것이다.
진행하면서 나는 앞의 예를 계속 언급할 것이다.의 정의inputs
as ~하듯이[A, B]
.
np.einsum
는 실제로 3개 이상의 입력을 받을 수 있습니다.여기서는 일반적인 경우, n개의 입력과 n개의 입력 첨자에 초점을 맞춥니다.주요 목표는 반복의 영역, 즉 모든 범위의 데카르트 곱을 찾는 것이다.
We can't rely on manually writing 우리는 손으로 쓰는 것에 의존할 수 없다.for
루프가 얼마나 걸릴지 모르니까루프가 몇 개나 될지 모르기 때문에 루프가 생기는 거죠 The main idea is this: we need to find all unique labels (I will use 주요 아이디어는 다음과 같습니다. 우리는 모든 고유 라벨을 찾아야 합니다(사용할 것입니다).key
★★★★★★★★★★★★★★★★★」keys
를 참조해 주세요.그 후, 해당하는 배열 형태를 찾아, 각 배열의 범위를 작성하고, 를 사용해 범위의 곱을 계산해, 연구 영역을 취득합니다.
색인 | keys |
제약 | sizes |
ranges |
---|---|---|---|---|
1 | 'i' |
A.shape[0] |
3 | range(0, 3) |
2 | 'j' |
A.shape[1] == B.shape[0] |
4 | range(0, 4) |
0 | 'k' |
B.shape[1] |
2 | range(0, 2) |
곱이다.range(0, 2) x range(0, 3) x range(0, 4)
.
서브스크립트 처리:
>>> expr = 'ij,jk->ki' >>> qry_expr, res_expr = expr.split('->') >>> inputs_expr = qry_expr.split(',') >>> inputs_expr, res_expr (['ij', 'jk'], 'ki')
입력 첨자에서 고유 키(라벨)를 찾습니다.
>>> keys = set([(key, size) for keys, input in zip(inputs_expr, inputs) for key, size in list(zip(keys, input.shape))]) {('i', 3), ('j', 4), ('k', 2)}
출력 첨자뿐만 아니라 제약 조건도 확인해야 합니다.「」를 사용합니다.
set
나쁜 생각이지만 이 예에서는 유효합니다.관련 크기(출력 배열 초기화에 사용)를 가져오고 범위(반복 도메인 작성에 사용)를 구성합니다.
>>> sizes = dict(keys) {'i': 3, 'j': 4, 'k': 2} >>> ranges = [range(size) for _, size in keys] [range(0, 2), range(0, 3), range(0, 4)]
키(라벨)가 포함된 목록이 필요합니다.
>>> to_key = sizes.keys() ['k', 'i', 'j']
.
range
s>>> domain = product(*ranges)
주의:
[itertools.product][1]
는 시간이 지남에 따라 소비되는 반복기를 반환합니다.다음과 같이 출력 텐서를 초기화합니다.
>>> res = np.zeros([sizes[key] for key in res_expr])
우리는 루핑할 것이다.
domain
:>>> for indices in domain: ... pass
ㄴㄴㄴㄴㄴㄴㄴㄴ다.
indices
각 축에 값이 포함됩니다.예에서는 '먹다'가 나옵니다.i
,j
, , , , 입니다.k
태플로서:(k, i, j)
각에 대해 (. ". ")A
★★★★★★★★★★★★★★★★★」B
가져올 컴포넌트를 결정해야 합니다.그그입니다.A[i, j]
★★★★★★★★★★★★★★★★★」B[j, k]
변수 !!는i
,j
, , , , 입니다.k
말 그대로요.수 .
indices
to_key
각 키(라벨)와 현재 값 사이의 매핑을 작성하려면:>>> vals = dict(zip(to_key, indices))
하려면 , 「」를 합니다.
vals
키 .[vals[key] for key in res_expr]
출력의 인덱스를 하려면 , 「 」, 「 」, 「 」, 「 」, 「 」로 둘러싸야 합니다tuple
★★★★★★★★★★★★★★★★★」zip
축을 하려면: " "를 참조하십시오.>>> res_ind = tuple(zip([vals[key] for key in res_expr]))
입력 인덱스에 대해서도 동일(단, 여러 개일 수 있음):
>>> inputs_ind = [tuple(zip([vals[key] for key in expr])) for expr in inputs_expr]
a를 사용하여 기여하는 모든 컴포넌트의 곱을 계산합니다.
>>> def reduce_mult(L): ... return reduce(lambda x, y: x*y, L)
도메인상의 루프는 전체적으로 다음과 같습니다.
>>> for indices in domain: ... vals = {k: v for v, k in zip(indices, to_key)} ... res_ind = tuple(zip([vals[key] for key in res_expr])) ... inputs_ind = [tuple(zip([vals[key] for key in expr])) ... for expr in inputs_expr] ... ... res[res_ind] += reduce_mult([M[i] for M, i in zip(inputs, inputs_ind)])
>>> res
array([[70., 44., 65.],
[30., 59., 68.]])
건건뭐 that에 꽤 입니다.np.einsum('ij,jk->ki', A, B)
반!!
NumPy를 찾았습니다. 업계의 요령(Part 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, k, 3면 j(으), j(으)라고 하다. i,j
과 a
j,k
★★★★★★에b
.
하고 을을 the the 를 맞추기 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
의 두 번째 .
>>> 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]])
>>>
서로 다르지만 호환성이 있는 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
의 첫 .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
subsc subsc의 .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
까 の toA
및 , 。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]
아인슈타인 합계(einsum)의 더미 지수(공통 지수 또는 반복 지수)와 더미 지수를 따른 합계에 익숙해지면 출력은->
쉐이핑은 간단합니다.해야 할부분은 다음과 같습니다.
- 인덱스, 인덱스, 더미 인덱스
j
np.einsum("ij,jk->ki", a, b)
- 인덱스에
j
더미 인덱스
★★★의 einsum("...", a, b)
은 항상 합니다.a
★★★★★★★★★★★★★★★★★」b
공통 지수의 유무에 관계없이. 수 einsum('xy,wz', a, b)
.'xy,wz'
.
와 공통 :j
"ij,jk->ki"
아인슈타인 합계에서는 더미 인덱스라고 불립니다.
합산된 지수는 총계 지수이며, 이 경우 "i"이다.같은 용어의 지수 기호와 충돌하지 않는 한 어떤 기호도 식의 의미를 변경하지 않고 "i"를 대체할 수 있기 때문에 더미 지수라고도 한다.
더미 지수를 따른 합계
★★★의 np.einsum("ij,j", a, b)
그림에서 녹색 직사각형의 경우,j
는 더미 인덱스입니다.요소의 곱셈 소 별 셈)a[i][j] * b[j]
is summed up along the 에 따라 요약된다.j
axis as 로서 축을 잡다.Σ ( a[i][j] * b[j] )
.
도트 상품입니다. np.inner(a[i], b)
for each 각각에 대해서i
. . Here being specific with 여기서 구체적으로 말하면np.inner()
and avoiding 회피하고 있다np.dot
엄밀하게는 수학적인 도트 곱셈 작업이 아니기 때문입니다.
더미 인덱스는 규칙(자세한 내용은 Youtube 참조)만 충족되면 어디에나 표시됩니다.
For the dummy index 더미 인덱스의 경우i
in 에np.einsum(“ik,il", a, b)
, , it is a row index of the matrices , 행렬의 행 색인입니다.a
★★★★★★★★★★★★★★★★★」b
, , hence a column from (따라서 컬럼은 다음과 같습니다.a
and that from 그리고 그것으로부터b
추출하여 도트 제품을 생성합니다.
출력 폼
합계가 더미 지수를 따라 발생하기 때문에 더미 지수는 결과 매트릭스에서 사라진다.i
부에서“ik,il"
is dropped and form the shape 떨어뜨려 모양을 만든다.(k,l)
. 다 있 수 we can tell알?np.einsum("... -> <shape>")
출력 첨자 레이블로 출력 형식을 지정하려면->
식별자
자세한 내용은 numpy.einsum의 명시 모드를 참조하십시오.
명시적 모드에서는 출력 첨자 라벨을 지정하여 출력을 직접 제어할 수 있습니다.를 위해서는 합니다.
‘->’
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★이 기능을 사용하면 필요에 따라 SUM을 디세블로 하거나 강제할 수 있기 때문에 함수의 유연성이 향상됩니다. »np.einsum('i->', a)
~와np.sum(a, axis=-1)
, , , , 입니다.np.einsum('ii->i', a)
~와np.diag(a)
차이점은 einsum은 기본적으로 브로드캐스트를 허용하지 않는다는 것입니다., ★★★★★np.einsum('ij,jh->ih', a, b)
는 출력 서브스크립트라벨의 순서를 직접 지정하기 때문에 위의 암묵 모드 예시와 달리 매트릭스 곱셈을 반환합니다.
더미 인덱스 없음
einsum에 더미 인덱스가 없는 예제입니다.
- 색인, 어아아아아아아 。
"ij"
)는각의 요소를 는 각 배열의 요소를 선택합니다. - 왼쪽의 각 요소는 요소별 곱셈을 위해 오른쪽의 요소에 적용됩니다(따라서 항상 곱셈이 발생합니다).
a
는 모양(2을, 각 는 모양(,3)을 있습니다.b
2, 2로 하다은 모양 .(2,3,2,2)
라고 요약하지 않고(i,j)
,(k.l)
모두 무료 인덱스입니다.
# --------------------------------------------------------------------------------
# For np.einsum("ij,kl", a, b)
# 1-1: Term "ij" or (i,j), two free indices, selects selects an element a[i][j].
# 1-2: Term "kl" or (k,l), two free indices, selects selects an element b[k][l].
# 2: Each a[i][j] is applied on b[k][l] for element-wise multiplication a[i][j] * b[k,l]
# --------------------------------------------------------------------------------
# for (i,j) in a:
# for(k,l) in b:
# a[i][j] * b[k][l]
np.einsum("ij,kl", a, b)
array([[[[ 0, 0],
[ 0, 0]],
[[10, 11],
[12, 13]],
[[20, 22],
[24, 26]]],
[[[30, 33],
[36, 39]],
[[40, 44],
[48, 52]],
[[50, 55],
[60, 65]]]])
예
행렬 A 행과 행렬 B 열의 점곱
A = np.matrix('0 1 2; 3 4 5')
B = np.matrix('0 -3; -1 -4; -2 -5');
np.einsum('ij,ji->i', A, B)
# Same with
np.diagonal(np.matmul(A,B))
(A*B).diagonal()
---
[ -5 -50]
[ -5 -50]
[[ -5 -50]]
가장 간단한 예는 tensorflow 문서에 있습니다.
인섬.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 interpreter는 이 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]
언급URL : https://stackoverflow.com/questions/26089893/understanding-numpys-einsum
'programing' 카테고리의 다른 글
PHP에서 디렉터리 크기를 가져오는 방법 (0) | 2022.11.17 |
---|---|
mysql 서브쿼리에서 outer column을 액세스하는 방법은 무엇입니까? (0) | 2022.11.16 |
발생 전에 window.setTimeout()을 취소/정지합니다. (0) | 2022.11.16 |
HTML5 WebSocket API를 지원하는 브라우저는 무엇입니까? (0) | 2022.11.16 |
명령줄 인수를 해석하는 가장 좋은 방법은 무엇입니까? (0) | 2022.11.16 |