Nắm bắt ý tưởng numpy.einsum()
là rất dễ dàng nếu bạn hiểu nó bằng trực giác. Ví dụ, hãy bắt đầu với một mô tả đơn giản liên quan đến nhân ma trận .
Để sử dụng numpy.einsum()
, tất cả những gì bạn phải làm là chuyển chuỗi được gọi là chuỗi đăng ký dưới dạng đối số, theo sau là mảng đầu vào của bạn .
Hãy nói rằng bạn có hai 2D mảng, A
và B
, và bạn muốn làm nhân ma trận. Bạn cũng vậy:
np.einsum("ij, jk -> ik", A, B)
Ở đây, chuỗi ký tự ij
tương ứng với mảng A
trong khi chuỗi ký tự jk
tương ứng với mảng B
. Ngoài ra, điều quan trọng nhất cần lưu ý ở đây là số lượng ký tự trong mỗi chuỗi ký tự phải phù hợp với kích thước của mảng. (tức là hai ký tự cho mảng 2D, ba ký tự cho mảng 3D, v.v.) Và nếu bạn lặp lại ký tự giữa các chuỗi ký tự ( j
trong trường hợp của chúng tôi), thì điều đó có nghĩa là bạn muốn ein
tổng số xảy ra dọc theo các kích thước đó. Vì vậy, họ sẽ được giảm tổng. (tức là kích thước đó sẽ biến mất )
Chuỗi đăng ký sau này ->
, sẽ là mảng kết quả của chúng tôi. Nếu bạn để trống, mọi thứ sẽ được tính tổng và kết quả là giá trị vô hướng được trả về. Khác mảng kết quả sẽ có kích thước theo chuỗi con . Trong ví dụ của chúng tôi, nó sẽ là ik
. Điều này là trực quan bởi vì chúng ta biết rằng để nhân ma trận, số lượng cột trong mảng A
phải khớp với số lượng hàng trong mảng B
đang xảy ra ở đây (tức là chúng ta mã hóa kiến thức này bằng cách lặp lại char j
trong chuỗi đăng ký )
Dưới đây là một số ví dụ minh họa việc sử dụng / sức mạnh của np.einsum()
việc thực hiện một số hoạt động tenor hoặc nd-mảng phổ biến , ngắn gọn.
Đầu vào
# 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) Phép nhân ma trận (tương tự 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) Trích xuất các phần tử dọc theo đường chéo chính (tương tự np.diag(arr)
)
In [202]: np.einsum("ii -> i", A)
Out[202]: array([11, 22, 33, 44])
3) Sản phẩm Hadamard (nghĩa là sản phẩm có yếu tố thông minh gồm hai mảng) (tương tự 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) Bình phương phần tử khôn ngoan (tương tự np.square(arr)
hoặc 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) Dấu vết (tức là tổng các phần tử đường chéo chính) (tương tự np.trace(arr)
)
In [217]: np.einsum("ii -> ", A)
Out[217]: 110
6) Ma trận chuyển vị (tương tự 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) Sản phẩm bên ngoài (của vectơ) (tương tự 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) Sản phẩm bên trong (của vectơ) (tương tự np.inner(vec1, vec2)
)
In [256]: np.einsum("i, i -> ", vec, vec)
Out[256]: 14
9) Tổng dọc trục 0 (tương tự np.sum(arr, axis=0)
)
In [260]: np.einsum("ij -> j", B)
Out[260]: array([10, 10, 10, 10])
10) Tổng dọc trục 1 (tương tự np.sum(arr, axis=1)
)
In [261]: np.einsum("ij -> i", B)
Out[261]: array([ 4, 8, 12, 16])
11) Phép nhân ma trận hàng loạt
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) Tổng dọc trục 2 (tương tự np.sum(arr, axis=2)
)
In [330]: np.einsum("ijk -> ij", BM)
Out[330]:
array([[ 50, 90, 130, 170],
[ 4, 8, 12, 16]])
13) Tính tổng tất cả các phần tử trong mảng (tương tự np.sum(arr)
)
In [335]: np.einsum("ijk -> ", BM)
Out[335]: 480
14) Tính tổng trên nhiều trục (nghĩa là cận biên)
(tương tự 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) Sản phẩm Double Dot (tương tự np.sum (sản phẩm hadamard) xem 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) Phép nhân mảng 2D và 3D
Phép nhân như vậy có thể rất hữu ích khi giải hệ phương trình tuyến tính ( Ax = b ) trong đó bạn muốn xác minh kết quả.
# 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
Ngược lại, nếu người ta phải sử dụng np.matmul()
để xác minh này, chúng tôi phải thực hiện một vài reshape
thao tác để đạt được kết quả tương tự như:
# 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
Phần thưởng : Đọc thêm toán ở đây: Einstein-Summation và chắc chắn ở đây: Tenor-Notation
(A * B)^T
, hoặc tương đươngB^T * A^T
.