Multiplication matricielle NumPy : Guide complet de np.dot, matmul et @
Updated on
La multiplication matricielle est fondamentale pour l'apprentissage automatique, l'infographie, le traitement du signal et le calcul scientifique. Mais NumPy offre trois facons de multiplier les matrices -- np.dot(), np.matmul() et l'operateur @ -- et les differences entre elles sont deroutantes. Utiliser le mauvais peut silencieusement produire des resultats incorrects, surtout lorsque vous travaillez avec des tableaux de dimensions differentes ou lorsque vous confondez la multiplication element par element (*) avec la multiplication matricielle.
Ce guide clarifie chaque methode, montre quand utiliser laquelle et fournit des exemples pratiques pour chaque patron commun d'algebre lineaire.
Multiplication element par element vs matricielle
D'abord, la distinction critique. L'operateur * effectue la multiplication element par element, PAS la multiplication matricielle :
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# Multiplication element par element (produit de Hadamard)
print(A * B)
# [[ 5 12]
# [21 32]]
# Multiplication matricielle
print(A @ B)
# [[19 22]
# [43 50]]La multiplication matricielle suit la regle : C[i,j] = sum(A[i,k] * B[k,j]) pour tout k.
L'operateur @ (recommande)
L'operateur @ (introduit dans Python 3.5) est la facon la plus propre et la plus lisible de faire la multiplication matricielle :
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
C = A @ B
print(C)
# [[19 22]
# [43 50]]Regles de forme
Pour que la multiplication matricielle A @ B fonctionne, le nombre de colonnes de A doit etre egal au nombre de lignes de B :
import numpy as np
# (2, 3) @ (3, 4) = (2, 4)
A = np.ones((2, 3))
B = np.ones((3, 4))
C = A @ B
print(C.shape) # (2, 4)
# (5, 3) @ (3, 2) = (5, 2)
A = np.random.randn(5, 3)
B = np.random.randn(3, 2)
C = A @ B
print(C.shape) # (5, 2)
# L'incompatibilite genere ValueError
# np.ones((2, 3)) @ np.ones((4, 2)) # ValueErrornp.matmul()
np.matmul() fait exactement ce que @ fait. Ils sont equivalents pour tous les usages pratiques :
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# Ceux-ci produisent des resultats identiques
result1 = A @ B
result2 = np.matmul(A, B)
print(np.array_equal(result1, result2)) # TrueUtilisez @ pour un code plus propre. Utilisez np.matmul() quand vous devez le passer comme argument de fonction (ex. a reduce).
np.dot()
np.dot() est plus ancien et se comporte differemment de @/matmul pour les tableaux de dimension superieure :
import numpy as np
# Pour les tableaux 1D : produit scalaire (resultat scalaire)
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(np.dot(a, b)) # 32 (1*4 + 2*5 + 3*6)
# Pour les tableaux 2D : multiplication matricielle (comme @)
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(np.dot(A, B))
# [[19 22]
# [43 50]]Difference cle : Dimensions superieures
Pour les tableaux de plus de 2 dimensions, np.dot() et @ se comportent differemment :
import numpy as np
A = np.random.randn(2, 3, 4)
B = np.random.randn(2, 4, 5)
# @ traite comme lot de multiplications matricielles : (2, 3, 4) @ (2, 4, 5) = (2, 3, 5)
result_matmul = A @ B
print(result_matmul.shape) # (2, 3, 5)
# np.dot utilise un broadcasting different : somme sur le dernier axe de A et l'avant-dernier de B
result_dot = np.dot(A, B)
print(result_dot.shape) # (2, 3, 2, 5) -- forme differente !Recommandation : Utilisez @ ou np.matmul() pour la multiplication matricielle. Utilisez np.dot() uniquement pour les produits scalaires explicites de vecteurs 1D.
Comparaison des methodes
| Caracteristique | Operateur @ | np.matmul() | np.dot() | Operateur * |
|---|---|---|---|---|
| Operation | Multiplication matricielle | Multiplication matricielle | Produit scalaire | Element par element |
| 1D x 1D | Produit scalaire | Produit scalaire | Produit scalaire | Element par element |
| 2D x 2D | Multiplication matricielle | Multiplication matricielle | Multiplication matricielle | Element par element |
| N-D par lots | Matmul par lots | Matmul par lots | Semantique differente | Element par element |
| Lisibilite | Meilleure | Bonne | OK | N/A |
| Scalaires autorises | Non | Non | Oui | Oui |
| Recommande | Oui | Oui (fonctionnel) | Uniquement pour point 1D | Pour Hadamard |
Produit scalaire (vecteurs)
Le produit scalaire de deux vecteurs est la somme de leurs produits element par element :
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# Tous equivalents pour les vecteurs 1D
print(np.dot(a, b)) # 32
print(a @ b) # 32
print(np.sum(a * b)) # 32Applications
import numpy as np
# Similarite cosinus
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
vec1 = np.array([1, 2, 3])
vec2 = np.array([4, 5, 6])
print(f"Cosine similarity: {cosine_similarity(vec1, vec2):.4f}")
# 0.9746
# Projection de a sur b
def project(a, b):
return (np.dot(a, b) / np.dot(b, b)) * b
print(project(np.array([3, 4]), np.array([1, 0])))
# [3. 0.]Multiplication matrice-vecteur
import numpy as np
# Transformation lineaire
A = np.array([[2, 0], [0, 3]]) # Matrice de mise a l'echelle
v = np.array([1, 1])
result = A @ v
print(result) # [2 3]
# Appliquer des poids dans une couche de reseau neuronal
weights = np.random.randn(10, 5) # 10 sorties, 5 entrees
inputs = np.random.randn(5)
output = weights @ inputs
print(output.shape) # (10,)Multiplication matricielle par lots
Pour le deep learning et le traitement par lots, @ gere les lots automatiquement :
import numpy as np
# Lot de 32 matrices, chacune 4x3, fois lot de 32 matrices, chacune 3x5
batch_A = np.random.randn(32, 4, 3)
batch_B = np.random.randn(32, 3, 5)
result = batch_A @ batch_B
print(result.shape) # (32, 4, 5)Exemples pratiques
Systeme d'equations lineaires
import numpy as np
# Resoudre Ax = b
# 2x + 3y = 8
# 4x + y = 10
A = np.array([[2, 3], [4, 1]])
b = np.array([8, 10])
x = np.linalg.solve(A, b)
print(f"x = {x[0]:.2f}, y = {x[1]:.2f}")
# x = 2.20, y = 1.20
# Verifier : A @ x devrait etre egal a b
print(A @ x) # [8. 10.]Passe directe d'un reseau neuronal simple
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# Reseau : 3 entrees -> 4 cachees -> 2 sorties
np.random.seed(42)
W1 = np.random.randn(4, 3) * 0.5 # Poids premiere couche
b1 = np.zeros(4)
W2 = np.random.randn(2, 4) * 0.5 # Poids deuxieme couche
b2 = np.zeros(2)
# Passe directe
X = np.array([1.0, 0.5, -1.0]) # Entree
h = sigmoid(W1 @ X + b1) # Couche cachee
y = sigmoid(W2 @ h + b2) # Couche de sortie
print(f"Input: {X}")
print(f"Hidden: {h}")
print(f"Output: {y}")Projection PCA
import numpy as np
# Generer des donnees d'exemple
np.random.seed(42)
data = np.random.randn(100, 5) # 100 echantillons, 5 caracteristiques
# Centrer les donnees
data_centered = data - data.mean(axis=0)
# Calculer la matrice de covariance par multiplication matricielle
cov = (data_centered.T @ data_centered) / (len(data) - 1)
# Decomposition en valeurs propres
eigenvalues, eigenvectors = np.linalg.eigh(cov)
# Projeter sur les 2 composantes principales
top_2 = eigenvectors[:, -2:] # Derniers 2 (plus grandes valeurs propres)
projected = data_centered @ top_2
print(f"Original shape: {data.shape}")
print(f"Projected shape: {projected.shape}") # (100, 2)Visualiser les operations matricielles
Pour explorer les effets des transformations matricielles sur les donnees, PyGWalker (opens in a new tab) vous permet de visualiser les donnees projetees de maniere interactive dans Jupyter :
import pandas as pd
import pygwalker as pyg
df = pd.DataFrame(projected, columns=['PC1', 'PC2'])
walker = pyg.walk(df)FAQ
Quelle est la difference entre np.dot et np.matmul ?
Pour les tableaux 1D et 2D, ils produisent le meme resultat. La difference cle est avec les tableaux de dimension superieure : np.matmul() (et @) les traite comme des lots de matrices, tandis que np.dot() utilise une regle de broadcasting differente. Pour du nouveau code, preferez @ ou np.matmul().
Que fait l'operateur @ dans NumPy ?
L'operateur @ effectue la multiplication matricielle, equivalent a np.matmul(). Il a ete introduit dans Python 3.5. Pour les tableaux 2D, A @ B calcule le produit matriciel standard. Pour les tableaux de dimension superieure, il effectue la multiplication matricielle par lots.
Pourquoi A * B donne-t-il des resultats differents de A @ B ?
A * B effectue la multiplication element par element (produit de Hadamard), multipliant les elements correspondants. A @ B effectue la multiplication matricielle, suivant la regle C[i,j] = sum(A[i,k] * B[k,j]). Ce sont des operations fondamentalement differentes.
Comment calculer le produit scalaire de deux vecteurs dans NumPy ?
Pour les tableaux 1D (vecteurs), utilisez np.dot(a, b), a @ b ou np.sum(a * b). Tous les trois renvoient le meme resultat scalaire : la somme des produits element par element.
Quelles formes sont requises pour la multiplication matricielle ?
Pour A @ B, la derniere dimension de A doit etre egale a l'avant-derniere dimension de B. Pour 2D : (m, n) @ (n, p) = (m, p). Le nombre de colonnes de A doit etre egal au nombre de lignes de B.
Conclusion
Pour la multiplication matricielle dans NumPy, utilisez l'operateur @ par defaut -- c'est le plus lisible et il gere correctement les operations par lots. Utilisez np.dot() uniquement pour les produits scalaires explicites de vecteurs 1D. Ne confondez jamais * (element par element) avec @ (multiplication matricielle). Retenez la regle de forme : (m, n) @ (n, p) = (m, p), et vous eviterez les erreurs les plus courantes de multiplication matricielle.