NumPy Einsum pour le calcul scientifique

NumPyNumPyIntermediate
Pratiquer maintenant

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

En calcul scientifique, effectuer des opérations d'algèbre linéaire de manière efficace est essentiel. NumPy, une bibliothèque Python fondamentale pour les calculs numériques, offre de nombreux outils pour ces opérations. Parmi ces outils, einsum (Somme d'Einstein) se distingue comme une fonction particulièrement puissante pour exprimer de manière concise des opérations complexes sur les tableaux.

Ce tutoriel vous guidera dans la compréhension de ce que einsum est et de la manière de l'utiliser efficacement dans votre code Python. À la fin de ce labo (LabEx), vous serez en mesure d'effectuer diverses opérations sur les tableaux à l'aide de la fonction einsum et de comprendre ses avantages par rapport aux fonctions traditionnelles de NumPy.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL numpy(("NumPy")) -.-> numpy/ArrayBasicsGroup(["Array Basics"]) numpy(("NumPy")) -.-> numpy/ArrayManipulationGroup(["Array Manipulation"]) numpy(("NumPy")) -.-> numpy/MathandStatisticsGroup(["Math and Statistics"]) numpy(("NumPy")) -.-> numpy/AdvancedFeaturesGroup(["Advanced Features"]) numpy/ArrayBasicsGroup -.-> numpy/multi_array("Multi-dimensional Array Creation") numpy/ArrayManipulationGroup -.-> numpy/transpose("Transpose and Axis Swap") numpy/MathandStatisticsGroup -.-> numpy/math_ops("Math Operations") numpy/MathandStatisticsGroup -.-> numpy/lin_alg("Linear Algebra") numpy/AdvancedFeaturesGroup -.-> numpy/ufuncs("Universal Functions") subgraph Lab Skills numpy/multi_array -.-> lab-4991{{"NumPy Einsum pour le calcul scientifique"}} numpy/transpose -.-> lab-4991{{"NumPy Einsum pour le calcul scientifique"}} numpy/math_ops -.-> lab-4991{{"NumPy Einsum pour le calcul scientifique"}} numpy/lin_alg -.-> lab-4991{{"NumPy Einsum pour le calcul scientifique"}} numpy/ufuncs -.-> lab-4991{{"NumPy Einsum pour le calcul scientifique"}} end

Comprendre les bases de NumPy Einsum

La Somme d'Einstein (einsum) est une fonction puissante de NumPy qui vous permet d'exprimer de nombreuses opérations sur les tableaux à l'aide d'une notation concise. Elle suit la convention de sommation d'Einstein, qui est couramment utilisée en physique et en mathématiques pour simplifier les équations complexes.

Ouverture du shell Python

Commençons par ouvrir le shell Python. Ouvrez un terminal sur le Bureau et tapez :

python3

Vous devriez voir l'invite Python (>>>), ce qui indique que vous êtes maintenant dans le shell interactif Python.

Importation de NumPy

Tout d'abord, nous devons importer la bibliothèque NumPy :

import numpy as np
Open Python Shell

Qu'est-ce que Einsum ?

La fonction einsum de NumPy vous permet de spécifier des opérations sur les tableaux à l'aide d'une notation sous forme de chaîne de caractères qui décrit quels indices (dimensions) des tableaux doivent être utilisés dans l'opération.

Le format de base d'une opération einsum est le suivant :

np.einsum('notation', array1, array2, ...)

Où la chaîne de caractères de notation décrit l'opération à effectuer.

Un exemple simple : Produit scalaire de vecteurs

Commençons par un exemple simple : le calcul du produit scalaire de deux vecteurs. En notation mathématique, le produit scalaire de deux vecteurs u et v est :

\sum_i u_i \times v_i

Voici comment le calculer à l'aide de einsum :

## Create two random vectors
u = np.random.rand(5)
v = np.random.rand(5)

## Print the vectors to see their values
print("Vector u:", u)
print("Vector v:", v)

## Calculate dot product using einsum
dot_product = np.einsum('i,i->', u, v)
print("Dot product using einsum:", dot_product)

## Verify with NumPy's dot function
numpy_dot = np.dot(u, v)
print("Dot product using np.dot:", numpy_dot)
Dot Product

La notation 'i,i->' signifie :

  • i représente l'indice du premier tableau (u)
  • Le deuxième i représente l'indice du deuxième tableau (v)
  • La flèche -> suivie de rien indique que nous voulons un résultat scalaire (somme sur tous les indices)

Comprendre la notation Einsum

La notation einsum suit ce schéma général :

'index1,index2,...->output_indices'
  • index1, index2 : Étiquettes pour les dimensions de chaque tableau d'entrée
  • output_indices : Étiquettes pour les dimensions du tableau de sortie
  • Les indices répétés dans les tableaux d'entrée sont sommés
  • Les indices qui apparaissent dans la sortie sont conservés dans le résultat

Par exemple, dans la notation 'ij,jk->ik' :

  • i, j sont les dimensions du premier tableau
  • j, k sont les dimensions du deuxième tableau
  • j apparaît dans les deux tableaux d'entrée, donc nous effectuons une somme sur cette dimension
  • i, k apparaissent dans la sortie, donc ces dimensions sont conservées

C'est exactement la formule de la multiplication de matrices !

Opérations courantes avec Einsum

Maintenant que nous comprenons les bases de einsum, explorons quelques opérations courantes que nous pouvons effectuer à l'aide de cette fonction puissante.

Transposition de matrice

Transposer une matrice consiste à échanger ses lignes et ses colonnes. En notation mathématique, si A est une matrice, sa transposée A^T est définie comme suit :

A^T_{ij} = A_{ji}

Voyons comment effectuer la transposition d'une matrice à l'aide de einsum :

## Create a random matrix
A = np.random.rand(3, 4)
print("Original matrix A:")
print(A)
print("Shape of A:", A.shape)  ## Should be (3, 4)

## Transpose using einsum
A_transpose = np.einsum('ij->ji', A)
print("\nTransposed matrix using einsum:")
print(A_transpose)
print("Shape of transposed A:", A_transpose.shape)  ## Should be (4, 3)

## Verify with NumPy's transpose function
numpy_transpose = A.T
print("\nTransposed matrix using A.T:")
print(numpy_transpose)

La notation 'ij->ji' signifie :

  • ij représente les indices de la matrice d'entrée (i pour les lignes, j pour les colonnes)
  • ji représente les indices de la matrice de sortie (j pour les lignes, i pour les colonnes)
  • Nous échangeons essentiellement les positions des indices

Multiplication de matrices

La multiplication de matrices est une opération fondamentale en algèbre linéaire. Pour deux matrices A et B, leur produit C est défini comme suit :

C_{ik} = \sum_j A_{ij} \times B_{jk}

Voici comment effectuer la multiplication de matrices à l'aide de einsum :

## Create two random matrices
A = np.random.rand(3, 4)  ## 3x4 matrix
B = np.random.rand(4, 2)  ## 4x2 matrix

print("Matrix A shape:", A.shape)
print("Matrix B shape:", B.shape)

## Matrix multiplication using einsum
C = np.einsum('ij,jk->ik', A, B)
print("\nResult matrix C using einsum:")
print(C)
print("Shape of C:", C.shape)  ## Should be (3, 2)

## Verify with NumPy's matmul function
numpy_matmul = np.matmul(A, B)
print("\nResult matrix using np.matmul:")
print(numpy_matmul)

La notation 'ij,jk->ik' signifie :

  • ij représente les indices de la matrice A (i pour les lignes, j pour les colonnes)
  • jk représente les indices de la matrice B (j pour les lignes, k pour les colonnes)
  • ik représente les indices de la matrice de sortie C (i pour les lignes, k pour les colonnes)
  • L'indice répété j est sommés (multiplication de matrices)

Multiplication élément par élément

La multiplication élément par élément consiste à multiplier les éléments correspondants de deux tableaux. Pour deux matrices A et B de même forme, leur produit élément par élément C est :

C_{ij} = A_{ij} \times B_{ij}

Voici comment effectuer la multiplication élément par élément à l'aide de einsum :

## Create two random matrices of the same shape
A = np.random.rand(3, 3)
B = np.random.rand(3, 3)

print("Matrix A:")
print(A)
print("\nMatrix B:")
print(B)

## Element-wise multiplication using einsum
C = np.einsum('ij,ij->ij', A, B)
print("\nElement-wise product using einsum:")
print(C)

## Verify with NumPy's multiply function
numpy_multiply = A * B
print("\nElement-wise product using A * B:")
print(numpy_multiply)

La notation 'ij,ij->ij' signifie :

  • ij représente les indices de la matrice A
  • ij représente les indices de la matrice B
  • ij représente les indices de la matrice de sortie C
  • Aucun indice n'est sommés, ce qui signifie que nous multiplions simplement les éléments correspondants

Opérations avancées avec Einsum

Maintenant que nous sommes à l'aise avec les opérations de base de einsum, explorons quelques applications plus avancées. Ces opérations démontrent le véritable pouvoir et la flexibilité de la fonction einsum.

Extraction de la diagonale

Extraire les éléments diagonaux d'une matrice est une opération courante en algèbre linéaire. Pour une matrice A, ses éléments diagonaux forment un vecteur d tel que :

d_i = A_{ii}

Voici comment extraire la diagonale à l'aide de 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)

La notation 'ii->i' signifie :

  • ii représente l'indice répété pour les éléments diagonaux de A
  • i signifie que nous extrayons ces éléments dans un tableau 1D

Trace d'une matrice

La trace d'une matrice est la somme de ses éléments diagonaux. Pour une matrice A, sa trace est :

\text{trace}(A) = \sum_i A_{ii}

Voici comment calculer la trace à l'aide de 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)

La notation 'ii->' signifie :

  • ii représente l'indice répété pour les éléments diagonaux
  • L'indice de sortie vide signifie que nous sommons tous les éléments diagonaux pour obtenir un scalaire

Multiplication par lots de matrices

einsum s'avère particulièrement utile lorsqu'il s'agit d'effectuer des opérations sur des tableaux de dimensions supérieures. Par exemple, la multiplication par lots de matrices consiste à multiplier des paires de matrices de deux lots.

Si nous avons un lot de matrices A de forme (n, m, p) et un lot de matrices B de forme (n, p, q), la multiplication par lots de matrices nous donne un résultat C de forme (n, m, q) :

C_{ijk} = \sum_l A_{ijl} \times B_{ilk}

Voici comment effectuer la multiplication par lots de matrices à l'aide de 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])

La notation 'nmp,npq->nmq' signifie :

  • nmp représente les indices du lot A (n pour le lot, m pour les lignes, p pour les colonnes)
  • npq représente les indices du lot B (n pour le lot, p pour les lignes, q pour les colonnes)
  • nmq représente les indices du lot de sortie C (n pour le lot, m pour les lignes, q pour les colonnes)
  • L'indice répété p est sommés (multiplication de matrices)

Pourquoi utiliser Einsum ?

Vous vous demandez peut-être pourquoi nous devrions utiliser einsum alors que NumPy propose déjà des fonctions spécialisées pour ces opérations. Voici quelques avantages :

  1. Interface unifiée : einsum offre une seule fonction pour de nombreuses opérations sur les tableaux
  2. Flexibilité : Elle peut exprimer des opérations qui nécessiteraient autrement plusieurs étapes
  3. Lisibilité : Une fois que vous comprenez la notation, le code devient plus concis
  4. Performances : Dans de nombreux cas, les opérations einsum sont optimisées et efficaces

Pour les opérations tensorielle complexes, einsum offre souvent la mise en œuvre la plus claire et la plus directe.

Résumé

Dans ce laboratoire, vous avez exploré la puissante fonction einsum de NumPy, qui met en œuvre la convention de sommation d'Einstein pour les opérations sur les tableaux. Récapitulons ce que vous avez appris :

  1. Concepts de base d'Einsum : Vous avez appris à utiliser la notation einsum pour exprimer les opérations sur les tableaux, les indices représentant les dimensions des tableaux et les indices répétés indiquant une sommation.

  2. Opérations courantes : Vous avez implémenté plusieurs opérations fondamentales à l'aide de einsum :

    • Produit scalaire de vecteurs
    • Transposition de matrice
    • Multiplication de matrices
    • Multiplication élément par élément
  3. Applications avancées : Vous avez exploré des opérations plus complexes :

    • Extraction de la diagonale
    • Trace d'une matrice
    • Multiplication par lots de matrices

La fonction einsum offre une approche unifiée et flexible pour les opérations sur les tableaux dans NumPy. Bien que des fonctions spécialisées telles que np.dot, np.matmul et np.transpose soient disponibles pour des opérations spécifiques, einsum propose une interface cohérente pour une large gamme d'opérations, ce qui devient particulièrement précieux lorsqu'on travaille avec des tableaux de dimensions supérieures.

Au fur et à mesure de votre progression dans le domaine du calcul scientifique et de la science des données, einsum sera un outil puissant à votre disposition pour effectuer des opérations complexes sur les tableaux avec un code concis et lisible.