Skip to content

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))  # ValueError

np.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))  # True

Utilisez @ 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

CaracteristiqueOperateur @np.matmul()np.dot()Operateur *
OperationMultiplication matricielleMultiplication matricielleProduit scalaireElement par element
1D x 1DProduit scalaireProduit scalaireProduit scalaireElement par element
2D x 2DMultiplication matricielleMultiplication matricielleMultiplication matricielleElement par element
N-D par lotsMatmul par lotsMatmul par lotsSemantique differenteElement par element
LisibiliteMeilleureBonneOKN/A
Scalaires autorisesNonNonOuiOui
RecommandeOuiOui (fonctionnel)Uniquement pour point 1DPour 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))    # 32

Applications

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.

📚