La notazione della sommatoria di Einstein è un modo conciso e potente per rappresentare le operazioni tensoriali, spesso utilizzate in fisica e nell'apprendimento automatico. Ci permette di scrivere calcoli complessi sui tensori in una forma compatta. Tratteremo le nozioni di base sulla somma di Einstein, come usarla in Python con Numpy e Tensorflow e forniremo esempi per illustrarne l'uso.
Nozioni di base sulla sommatoria di Einstein
La notazione della somma di Einstein (Einsum) si basa sull'idea di sommare indici ripetuti nelle espressioni tensoriali. Si basa sulle seguenti due regole:
1. Somma su indici ripetuti: Se un indice appare due volte in un termine, viene sommato
2. Indici liberi: Gli indici che compaiono una sola volta sono indici liberi e rappresentano gli assi del tensore di output
Illustriamolo con l'esempio della moltiplicazione di due matrici A e B: la matrice C risultante è definita come
In Python, entrambe le librerie Numpy e Tensorflow forniscono una funzione einsum.
Insensibile
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]]
Nell'esempio sopra, ij,jk->ik
è la stringa einsum:
"ij" rappresenta gli indici della matrice A
"jk" rappresenta gli indici della matrice B
->ik
specifica gli indici della matrice di output C
L'operazione somma sull'indice j
Lo stesso codice in Tensorflow sarebbe simile
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)
Altri esempi
Prodotto interno di vettori
Il prodotto interno (prodotto scalare) di due vettori a e b è definito come
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.einsum('i,i->', a, b)
print(c) # Output: 32
Prodotto esterno di vettori
Il prodotto esterno di due vettori a e b è dato da:
C = np.einsum('i,j->ij', a, b)
print(C)
# Output
# [[4 5 6]
# [8 10 12]
# [12 15 18]]
Trasposizione di una matrice
La trasposta di una matrice A può essere ottenuta scambiando i suoi indici
A_transpose = np.einsum('ij->ji', A)
print(A_transpose)
# Output
# [[1. 3.]
# [2. 4.]]
Traccia di una matrice
La traccia di una matrice A è la somma dei suoi elementi diagonali:
trace = np.einsum('ii->', A)
print(trace)
# Output: 5.0
Moltiplicazione di matrici batch
Einsum è particolarmente utile per le operazioni batch. Supponiamo di avere un batch di matrici A e B e di voler moltiplicare le matrici corrispondenti nel batch:
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)
Qui, "b" rappresenta la dimensione batch.
Vantaggi della notazione Einsum
1. Concisione: la notazione Einsum è compatta e può rappresentare in modo succinto operazioni complesse
2. Flessibilità: può gestire un'ampia varietà di operazioni tensoriali senza rimodellare o trasporre esplicitamente gli array
3. Efficienza: molte biblioteche ottimizzano internamente le operazioni di einsum, portando potenzialmente a prestazioni migliori.