Notacja sumowania Einsteina to zwięzły i skuteczny sposób przedstawiania operacji tensorowych, często używany w fizyce i uczeniu maszynowym. Pozwala nam pisać złożone obliczenia na tensorach w zwartej formie. Omówimy podstawy sumowania Einsteina, jak go używać w Pythonie z Numpy i Tensorflow oraz podamy przykłady ilustrujące jego użycie.
Podstawy sumowania Einsteina
Notacja sumowania Einsteina (Einsum) opiera się na idei sumowania powtarzających się indeksów w wyrażeniach tensorowych. Opiera się na dwóch następujących zasadach:
1. Sumowanie po powtarzających się indeksach: Jeżeli indeks pojawia się dwa razy w ciągu terminu, jest on sumowany
2. Indeksy swobodne: Indeksy, które pojawiają się tylko raz, są indeksami swobodnymi i reprezentują osie tensora wyjściowego
Zilustrujmy to przykładem mnożenia dwóch macierzy A i B: otrzymaną macierz C definiujemy jako
W Pythonie zarówno biblioteki Numpy, jak i Tensorflow udostępniają funkcję einsum.
Numpy
import numpy as np
# Define two matrices A and B
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# Perform matrix multiplication using einsum
C = np.einsum('ij,jk->ik', A, B)
print(C)
# [[19 22]
# [43 50]]
W powyższym przykładzie ij,jk->ik
jest ciągiem einsum:
„ij” oznacza indeksy macierzy A
„jk” oznacza indeksy macierzy B
->ik
określa indeksy macierzy wyjściowej C
Operacja sumuje się po indeksie j
Wyglądałby ten sam kod w Tensorflow
import tensorflow as tf
# Define two matrices A and B
A = tf.constant([[1, 2], [3, 4]], dtype=tf.float32)
B = tf.constant([[5, 6], [7, 8]], dtype=tf.float32)
# Perform matrix multiplication using einsum
C = tf.einsum('ij,jk->ik', A, B)
print(C)
# tf.Tensor(
# [[19. 22.]
# [43. 50.]], shape=(2, 2), dtype=float32)
Więcej przykładów
Iloczyn wewnętrzny wektorów
Iloczyn wewnętrzny (iloczyn skalarny) dwóch wektorów aib definiuje się jako
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.einsum('i,i->', a, b)
print(c) # Output: 32
Iloczyn zewnętrzny wektorów
Zewnętrzny iloczyn dwóch wektorów aib jest określony wzorem:
C = np.einsum('i,j->ij', a, b)
print(C)
# Output
# [[4 5 6]
# [8 10 12]
# [12 15 18]]
Transpozycja macierzy
Transpozycję macierzy A można uzyskać poprzez zamianę jej indeksów
A_transpose = np.einsum('ij->ji', A)
print(A_transpose)
# Output
# [[1. 3.]
# [2. 4.]]
Ślad macierzy
Ślad macierzy A jest sumą jej elementów przekątnych:
trace = np.einsum('ii->', A)
print(trace)
# Output: 5.0
Wsadowe mnożenie macierzy
Einsum jest szczególnie przydatny w operacjach wsadowych. Załóżmy, że mamy partię macierzy A i B i chcemy pomnożyć odpowiednie macierze w tej partii:
A = np.random.rand(3, 2, 2)
B = np.random.rand(3, 2, 2)
# Perform batch matrix multiplication
C = np.einsum('bij,bjk->bik', A, B)
print(C)
Tutaj „b” oznacza wymiar partii.
Zalety notacji Einsum
1. Zwięzłość: Notacja Einsum jest zwarta i może zwięźle przedstawiać złożone operacje
2. Elastyczność: Może obsłużyć szeroką gamę operacji tensorowych bez jawnego przekształcania lub transpozycji tablic
3. Wydajność: wiele bibliotek optymalizuje wewnętrznie operacje einsum, co potencjalnie prowadzi do lepszej wydajności.