Продвинутые операции с einsum
Теперь, когда мы освоили базовые операции с функцией einsum
, давайте рассмотрим более продвинутые применения. Эти операции демонстрируют настоящую мощь и гибкость функции einsum
.
Извлечение диагонали
Извлечение диагональных элементов матрицы - это распространенная операция в линейной алгебре. Для матрицы A ее диагональные элементы образуют вектор d, где:
d_i = A_{ii}
Вот как извлечь диагональ с помощью einsum
:
## Create a random square matrix
A = np.random.rand(4, 4)
print("Matrix A:")
print(A)
## Extract diagonal using einsum
diagonal = np.einsum('ii->i', A)
print("\nDiagonal elements using einsum:")
print(diagonal)
## Verify with NumPy's diagonal function
numpy_diagonal = np.diagonal(A)
print("\nDiagonal elements using np.diagonal():")
print(numpy_diagonal)
Нотация 'ii->i'
означает:
ii
представляет повторяющийся индекс для диагональных элементов A
i
означает, что мы извлекаем эти элементы в одномерный массив
След матрицы
След матрицы - это сумма ее диагональных элементов. Для матрицы A ее след равен:
\text{trace}(A) = \sum_i A_{ii}
Вот как вычислить след с помощью einsum
:
## Using the same matrix A from above
trace = np.einsum('ii->', A)
print("Trace of matrix A using einsum:", trace)
## Verify with NumPy's trace function
numpy_trace = np.trace(A)
print("Trace of matrix A using np.trace():", numpy_trace)
Нотация 'ii->'
означает:
ii
представляет повторяющийся индекс для диагональных элементов
- Пустой выходной индекс означает, что мы суммируем все диагональные элементы, чтобы получить скаляр
Пакетное умножение матриц
Функция einsum
особенно полезна при выполнении операций над многомерными массивами. Например, пакетное умножение матриц заключается в умножении пар матриц из двух пакетов.
Если у нас есть пакет матриц A с формой (n, m, p) и пакет матриц B с формой (n, p, q), то пакетное умножение матриц дает нам результат C с формой (n, m, q):
C_{ijk} = \sum_l A_{ijl} \times B_{ilk}
Вот как выполнить пакетное умножение матриц с помощью einsum
:
## Create batches of matrices
n, m, p, q = 5, 3, 4, 2 ## Batch size and matrix dimensions
A = np.random.rand(n, m, p) ## Batch of 5 matrices, each 3x4
B = np.random.rand(n, p, q) ## Batch of 5 matrices, each 4x2
print("Shape of batch A:", A.shape)
print("Shape of batch B:", B.shape)
## Batch matrix multiplication using einsum
C = np.einsum('nmp,npq->nmq', A, B)
print("\nShape of result batch C:", C.shape) ## Should be (5, 3, 2)
## Let's check the first matrix multiplication in the batch
print("\nFirst result matrix from batch using einsum:")
print(C[0])
## Verify with NumPy's matmul function
numpy_batch_matmul = np.matmul(A, B)
print("\nFirst result matrix from batch using np.matmul:")
print(numpy_batch_matmul[0])
Нотация 'nmp,npq->nmq'
означает:
nmp
представляет индексы пакета A (n - для пакета, m - для строк, p - для столбцов)
npq
представляет индексы пакета B (n - для пакета, p - для строк, q - для столбцов)
nmq
представляет индексы выходного пакета C (n - для пакета, m - для строк, q - для столбцов)
- Повторяющийся индекс
p
суммируется (умножение матриц)
Почему использовать einsum?
Вы, возможно, задаетесь вопросом, почему мы должны использовать einsum
, если NumPy уже предоставляет специальные функции для этих операций. Вот несколько преимуществ:
- Единый интерфейс:
einsum
предоставляет одну функцию для многих операций с массивами.
- Гибкость: Она может выражать операции, которые в противном случае потребовали бы нескольких шагов.
- Читаемость: Как только вы освоите нотацию, код станет более компактным.
- Производительность: Во многих случаях операции с
einsum
оптимизированы и эффективны.
Для сложных операций с тензорами einsum
часто предоставляет наиболее ясную и прямую реализацию.