Retour au blog

Calcul tensoriel : bases pour la physique et l'IA

Convention d'Einstein, indices hauts et bas, changement de base, tenseur métrique : les règles de calcul qui servent en physique comme en apprentissage profond, avec quatre exercices corrigés.

Cet article a été rédigé à des fins pédagogiques. Les informations présentées peuvent évoluer. Nous t’invitons à vérifier auprès de sources officielles.

Un tenseur d'ordre $p$ se donne par des composantes à $p$ indices, comme $T^i{}_j$, et par une règle : quand tu changes de base, chaque indice se transforme avec la matrice de passage. Pour calculer, tu appliques la convention d'Einstein : un indice répété, une fois en haut et une fois en bas, est sommé. Ainsi $y^i = A^i{}_j x^j$ veut dire $y^i = \sum_{j=1}^{n} A^i{}_j x^j$ : c'est le produit d'une matrice par un vecteur colonne.

Un scalaire est d'ordre 0, un vecteur d'ordre 1, une matrice d'ordre 2 ; en IA, « tenseur » désigne surtout un tableau à plusieurs axes, sans règle de changement de base.

La convention d'Einstein

Un indice qui apparaît deux fois dans un terme, une fois en haut et une fois en bas, est muet : on somme sur ses valeurs de 1 à $n$, et son nom ne compte pas. Un indice qui n'apparaît qu'une fois est libre : il figure à la même hauteur dans chaque terme, des deux côtés du signe égal. Une égalité à un indice libre résume donc $n$ égalités.

À lire sans hésiter : $u_i v^i$ est un nombre, $A^i{}_i$ la trace de $A$, et $C^i{}_k = A^i{}_j B^j{}_k$ le produit matriciel $C = AB$. Le symbole de Kronecker $\delta^i{}_j$ vaut 1 si $i = j$ et 0 sinon : ce sont les coefficients de la matrice identité. Dans une somme, il renomme un indice : $\delta^i{}_j x^j = x^i$.

Piège classique : un indice n'apparaît jamais trois fois dans un terme, et $a_i b^i c^i$ n'a pas de sens. Pour multiplier deux sommes, donne un nom différent à chaque indice muet : $(a_i b^i)(c_j d^j) = a_i b^i c_j d^j$.

Indices hauts et bas : le changement de base

Prends une nouvelle base définie par $e'_j = P^i{}_j e_i$ : la colonne $j$ de la matrice de passage $P$ contient les coordonnées de $e'_j$ dans l'ancienne base.

Un vecteur s'écrit $v = v^i e_i = v'^j e'_j$. En remplaçant $e'_j$, tu trouves $v^i = P^i{}_j v'^j$, donc $v' = P^{-1} v$ : les composantes d'un vecteur varient à l'inverse de la base. On les dit contravariantes : indice en haut.

Une forme linéaire $\omega$, application linéaire de l'espace dans $\mathbb{R}$, a pour composantes $\omega_j = \omega(e_j)$. Par linéarité, $\omega'_j = P^i{}_j \omega_i$ : elles varient comme la base. On les dit covariantes, indice en bas. La différentielle d'une fonction, de composantes $\partial_i f$ (ses dérivées partielles), en est l'exemple type.

Exemple : passe du mètre au centimètre : le vecteur de base est divisé par 100. Un déplacement de 3 m devient 300 cm : sa composante est multipliée par 100. Un gradient de température de 2 K/m devient 0,02 K/cm : il suit la base. La variation de température, $2 \times 3 = 0{,}02 \times 300 = 6$ K, ne bouge pas.

Règle : un tenseur de type $(p, q)$ a $p$ indices en haut et $q$ en bas. Dans un changement de base, chaque indice du haut prend un facteur $P^{-1}$, chaque indice du bas un facteur $P$ :

$$T'^k{}_l = (P^{-1})^k{}_i \, P^j{}_l \, T^i{}_j$$

Tu connais déjà deux cas. Une application linéaire, de type $(1, 1)$, devient $P^{-1} A P$ ; une forme bilinéaire, de type $(0, 2)$, devient $P^\top B P$. Même tableau de nombres, deux règles : un tenseur n'est pas qu'un tableau.

Dans une somme sur un indice du haut et un indice du bas, $P$ et $P^{-1}$ se compensent : $\omega_i v^i$ ne dépend pas de la base, comme les 6 K de l'exemple.

Produit tensoriel et contraction

Le produit tensoriel multiplie les composantes sans rien sommer : $(u \otimes v)^{ij} = u^i v^j$. Les ordres s'ajoutent, et deux vecteurs donnent la matrice $u v^\top$, d'ordre 2.

La contraction somme sur un indice du haut et un indice du bas : l'ordre baisse de 2. Le produit matriciel enchaîne les deux gestes : $A^i{}_j B^k{}_l$ est d'ordre 4, et contracter $j$ avec $k$ donne $A^i{}_j B^j{}_l$, d'ordre 2.

Le tenseur métrique

Le produit scalaire se range dans un tenseur symétrique de type $(0, 2)$ : $g_{ij} = e_i \cdot e_j$. Alors $u \cdot v = g_{ij} u^i v^j$. Ce tenseur descend aussi un indice, $v_i = g_{ij} v^j$, et sa matrice inverse $g^{ij}$ le remonte.

Dans une base orthonormée, $g_{ij} = \delta_{ij}$ : composantes hautes et basses sont égales. C'est pour ça qu'en mécanique, en repère cartésien, on écrit tous les indices en bas sans rien perdre.

Exemple : en coordonnées polaires, $M(r\cos\theta\,;\, r\sin\theta)$ et la base naturelle est formée de $e_r = \dfrac{\partial M}{\partial r}$ et $e_\theta = \dfrac{\partial M}{\partial \theta}$. Ces vecteurs sont orthogonaux, de normes 1 et $r$, donc $g = \mathrm{diag}(1, r^2)$. Le carré de la longueur d'un petit déplacement vaut $\mathrm{d}s^2 = \mathrm{d}r^2 + r^2\,\mathrm{d}\theta^2$.

En physique : des lois indépendantes du repère

Une égalité entre tenseurs de même type, vraie dans une base, l'est dans toutes, puisque ses deux membres se transforment de la même façon. Voilà pourquoi la physique écrit ses lois en tenseurs.

Un tenseur d'ordre 2 apparaît dès qu'une cause vectorielle produit un effet qui ne lui est pas parallèle. Le moment cinétique d'un solide vaut $L_i = I_{ij}\,\omega_j$, où $I$ est le tenseur d'inertie et $\omega$ le vecteur rotation, en repère orthonormé. Quand $\omega$ n'est pas porté par un axe principal d'inertie, $L$ n'est pas colinéaire à $\omega$.

Dans un matériau, le tenseur des contraintes de Cauchy $\sigma_{ij}$, symétrique et exprimé en pascals, donne la force par unité de surface sur une facette de normale unitaire $n$ : $T_i = \sigma_{ij}\,n_j$. La projection de $T$ sur $n$, $\sigma_n = T_i\,n_i$, est la contrainte normale ; le reste de $T$ est le cisaillement.

En relativité restreinte, la métrique n'est plus définie positive : $\mathrm{d}s^2 = -c^2\,\mathrm{d}t^2 + \mathrm{d}x^2 + \mathrm{d}y^2 + \mathrm{d}z^2$, ou l'opposé selon les manuels. En relativité générale, $g_{\mu\nu}$ varie d'un point à l'autre et devient l'inconnue des équations d'Einstein, $G_{\mu\nu} + \Lambda g_{\mu\nu} = \dfrac{8\pi G}{c^4}\,T_{\mu\nu}$, où $G_{\mu\nu}$ traduit la courbure, $\Lambda$ est la constante cosmologique et $T_{\mu\nu}$ décrit la matière et l'énergie.

En IA : des tableaux à plusieurs axes

Dans PyTorch ou TensorFlow, un tenseur est un tableau de nombres. Son nombre d'axes joue le rôle de l'ordre, et sa forme (shape) donne la longueur de chaque axe. PyTorch range un lot d'images sous la forme (N, C, H, W) : nombre d'images, canaux, hauteur, largeur, comme l'indique la documentation de Conv2d. Le guide de TensorFlow range plutôt les canaux en dernier.

Pas de changement de base ici : les axes repèrent des cases en mémoire, pas des directions de l'espace. Tous les indices s'écrivent en bas, et on somme quand même sur les indices répétés. Une couche dense calcule ainsi $y_i = W_{ij}\,x_j + b_i$. La fonction einsum de NumPy écrit cette convention en code : np.einsum('ij,j->i', W, x) calcule $W_{ij}\,x_j$, et l'indice absent après la flèche est sommé. Avec NumPy, A @ B fait le produit matriciel et A * B le produit terme à terme, dit de Hadamard.

Piège classique : TensorFlow appelle « rang » le nombre d'axes. Rien à voir avec le rang d'une matrice, la dimension de son image : la matrice nulle $3 \times 3$ a 2 axes, mais un rang matriciel égal à 0.

La rétropropagation dérive la fonction de perte $L$ (l'erreur du réseau) par rapport à chaque poids, et les indices font le calcul.

De $y_k = W_{kl}\,x_l + b_k$, on tire $\dfrac{\partial y_k}{\partial W_{ij}} = \delta_{ki}\,x_j$. La dérivation en chaîne donne $\dfrac{\partial L}{\partial W_{ij}} = \dfrac{\partial L}{\partial y_k}\,\delta_{ki}\,x_j = \dfrac{\partial L}{\partial y_i}\,x_j$. Le gradient est le produit tensoriel de $\dfrac{\partial L}{\partial y}$ par $x$, de même forme que $W$.

Exercices corrigés

Cherche chacun sur ta feuille avant d'ouvrir la correction.

Exercice 1 : En dimension 3, calcule $\delta^i{}_i$, puis simplifie $\delta^i{}_j\,\delta^j{}_k$. L'expression $A^i{}_j\,x^j y^j$ est-elle correcte ?

Correction :

L'indice $i$ est répété, donc sommé : $\delta^i{}_i = \delta^1{}_1 + \delta^2{}_2 + \delta^3{}_3 = 3$, la trace de l'identité. Répondre 1, c'est oublier la somme.

$\delta^i{}_j$ remplace $j$ par $i$ : $\delta^i{}_j\,\delta^j{}_k = \delta^i{}_k$, l'égalité $I \times I = I$ écrite en indices.

$A^i{}_j\,x^j y^j$ est incorrecte : l'indice $j$ y apparaît trois fois.

Exercice 2 : Un lot de 32 images en couleur de $224 \times 224$ pixels est stocké avec PyTorch, chaque nombre occupant 4 octets (flottant sur 32 bits). Donne sa forme, son ordre, son nombre de composantes et la mémoire qu'il occupe.

Correction :

Dans l'ordre (N, C, H, W) de PyTorch, avec 3 canaux de couleur, la forme est (32, 3, 224, 224) : 4 axes, donc ordre 4.

Composantes : $32 \times 3 \times 224 \times 224 = 96 \times 50\,176 = 4\,816\,896$.

Mémoire : $4\,816\,896 \times 4 = 19\,267\,584$ octets, soit environ 19,3 Mo.

Exercice 3 : Dans $\mathbb{R}^2$, on passe à la base $e'_1 = e_1 + e_2$, $e'_2 = e_2$. Le vecteur $v$ a pour composantes $v^1 = 3$ et $v^2 = 5$, la forme linéaire $\omega$ a pour composantes $\omega_1 = 2$ et $\omega_2 = -1$. Calcule les nouvelles composantes de $v$ et de $\omega$, puis vérifie que $\omega_i v^i$ n'a pas changé.

Correction :

Les colonnes de $P$ sont les coordonnées de $e'_1$ et $e'_2$ : $P = \begin{pmatrix} 1 & 0 \\ 1 & 1 \end{pmatrix}$, d'inverse $P^{-1} = \begin{pmatrix} 1 & 0 \\ -1 & 1 \end{pmatrix}$.

Vecteur : $v' = P^{-1} v$, soit $v'^1 = 3$ et $v'^2 = -3 + 5 = 2$. Contrôle : $3e'_1 + 2e'_2 = 3e_1 + 5e_2$.

Forme : $\omega'_j = \omega(e'_j)$, soit $\omega'_1 = 2 + (-1) = 1$ et $\omega'_2 = -1$.

Invariance : $\omega_i v^i = 2 \times 3 + (-1) \times 5 = 1$ et $\omega'_j v'^j = 1 \times 3 + (-1) \times 2 = 1$.

Exercice 4 : En un point d'une pièce, dans un repère orthonormé, le tenseur des contraintes vaut, en MPa :

$$\sigma = \begin{pmatrix} 50 & 20 & 0 \\ 20 & 30 & 0 \\ 0 & 0 & 0 \end{pmatrix}$$

Calcule le vecteur contrainte $T$ sur la facette de normale $n = \dfrac{1}{\sqrt{2}}\begin{pmatrix} 1 \\ 1 \\ 0 \end{pmatrix}$, puis la contrainte normale $\sigma_n$ et la norme $\tau$ du cisaillement.

Correction :

$T_i = \sigma_{ij}\,n_j$ donne $T_1 = \dfrac{50 + 20}{\sqrt{2}} = 35\sqrt{2} \approx 49{,}5$ MPa, $T_2 = \dfrac{20 + 30}{\sqrt{2}} = 25\sqrt{2} \approx 35{,}4$ MPa et $T_3 = 0$.

Contrainte normale : $\sigma_n = T_i\,n_i = \dfrac{70 + 50}{2} = 60$ MPa.

Le cisaillement est la partie de $T$ orthogonale à $n$. Par Pythagore, $\tau^2 = \|T\|^2 - \sigma_n^2 = \dfrac{70^2 + 50^2}{2} - 60^2 = 3\,700 - 3\,600 = 100$, donc $\tau = 10$ MPa.

À retenir

À retenir :

  • Un indice répété, une fois en haut et une fois en bas, est sommé ; un indice libre est le même dans chaque terme.
  • Composantes d'un vecteur : facteur $P^{-1}$, indice en haut. Forme linéaire : facteur $P$, indice en bas.
  • Produit tensoriel : les ordres s'ajoutent. Contraction : l'ordre baisse de 2.
  • La métrique $g_{ij}$ donne les longueurs et monte ou descend les indices ; en base orthonormée, $g_{ij} = \delta_{ij}$.
  • En IA, un tenseur est un tableau à plusieurs axes, et einsum écrit la convention d'Einstein en code.

Sources

  • Documentation de NumPy, fonction einsum : numpy.org.
  • Documentation de PyTorch, couche Conv2d et forme de ses entrées : docs.pytorch.org.
  • Guide des tenseurs de TensorFlow (rang, forme, ordre des axes) : tensorflow.org.

Tu peux partager cette page librement, en citant ORBITECH AI Academy avec son lien.