NMF (Factorisation en Matrices Non-Négatives) : Guide complet — Principes, Exemples et Implémentation Python
Résumé
La Factorisation en Matrices Non-Négatives (Non-Negative Matrix Factorization, NMF) est une méthode d’apprentissage non supervisé qui décompose une matrice de données à valeurs positives en deux matrices également non-négatives. Contrairement à l’Analyse en Composantes Principales (ACP), la NMF impose que tous les éléments des facteurs soient supérieurs ou égaux à zéro, ce qui produit une représentation additive et hautement interprétable. Chaque composante extraite correspond à un motif élémentaire que l’on peut comprendre isolément — par exemple un visage partiel dans un jeu de photos, ou un thème latent dans un corpus de textes. Dans ce guide, nous étudierons le principe mathématique, l’intuition géométrique, l’implémentation Python avec scikit-learn, les hyperparamètres clés et quatre cas d’usage concrets.
Principe mathématique
La NMF cherche à approximer une matrice donnée V de dimensions n × m (n échantillons, m features) par le produit de deux matrices de rang inférieur W et H, toutes deux contraintes à être non-négatives :
$$V \approx W \times H$$
où :
- V est une matrice de taille n × m avec Vᵢⱼ ≥ 0 pour tout i, j
- W est une matrice de taille n × k avec Wᵢⱼ ≥ 0 (matrice des coefficients ou des poids)
- H est une matrice de taille k × m avec Hᵢⱼ ≥ 0 (matrice des composantes ou dictionnaire)
- k est le nombre de composantes latentes, choisi par l’utilisateur avec k ≪ min(n, m)
L’objectif d’optimisation consiste à minimiser une fonction de coût mesurant l’erreur de reconstruction, sous les contraintes de non-négativité :
$$\min_{W \geq 0,\, H \geq 0} ||V – WH||_F^2$$
La norme de Frobenius ||·||_F est la racine carrée de la somme des carrés de tous les éléments. Cette fonction de coût correspond à une distance euclidienne entre la matrice originale et sa reconstruction.
Divergence de Kullback-Leibler
Outre la norme de Frobenius, scikit-learn propose également la divergence de Kullback-Leibler (KL) comme fonction de coût alternative :
$$D_{KL}(V || WH) = \sum_{i,j} \left( V_{ij} \log \frac{V_{ij}}{(WH){ij}} – V \right)$$} + (WH)_{ij
Cette mesure est particulièrement adaptée aux données de comptage (comme les fréquences de mots dans un corpus documentaire), car elle pénalise davantage les erreurs relatives que les erreurs absolues. Le choix de la fonction de coût influe directement sur la qualité et l’interprétation des composantes extraites.
Non-convexité et algorithmes de résolution
Le problème d’optimisation de la NMF est non convexe — il n’existe pas de solution globale garantie. Les algorithmes utilisent des mises à jour alternées : on fixe W et on optimise H, puis on fixe H et on optimise W, en itérant jusqu’à convergence. Les règles de mise à jour multiplicatives de Lee et Seung (2001) constituent l’approche historique, tandis que les solveurs modernes (comme le Coordinate Descent de scikit-learn) offrent une convergence plus rapide et plus stable.
Intuition : la décomposition additive
Imaginez que vous souhaitez reconstruire une photographie à partir de couches transparentes superposées. Chaque couche représente un motif élémentaire : un bord, une texture, une région du visage. L’image originale s’obtient simplement en additionnant ces couches avec des poids appropriés. C’est exactement le principe de la NMF.
Prenons une analogie avec la peinture : pour obtenir toutes les nuances d’un tableau, un artiste mélange des couleurs primaires (rouge, bleu, jaune) en quantités variables. On ne soustrait jamais de peinture — on ajoute toujours. La NMF fonctionne de même :
- La matrice H contient les « couleurs primaires », c’est-à-dire les motifs de base (les composantes)
- La matrice W indique « combien de chaque couleur » utiliser pour chaque échantillon (les coefficients)
- Le produit WH reconstitue l’original comme une somme pondérée de ces motifs
Cette propriété additive contraste fortement avec l’ACP, où les composantes principales peuvent avoir des coefficients négatifs. En ACP, on reconstruit une image en ajoutant et soustrayant des motifs — ce qui est mathématiquement élégant mais difficilement interprétable. La NMF, elle, produit des parties reconnaissables : un nez, un œil, un thème lexical… des « briques » que notre cerveau comprend naturellement.
Cette capacité à apprendre des « parties » plutôt que des « ensembles » a été démontrée par Lee et Seung dans leur article fondateur de 1999 « Learning the parts of objects by non-negative matrix factorization » (Nature, 401, 788-791). Leurs expériences sur des visages humains ont montré que la NMF extrait spontanément des composants correspondant à des traits faciaux individuels, tandis que l’ACP produit des « visages fantômes » abstraits.
Implémentation Python
Prérequis
Nous utiliserons les bibliothèques suivantes : numpy pour le calcul numérique, scikit-learn pour la NMF et l’ACP, matplotlib pour la visualisation, et le jeu de données Olivetti Faces intégré à scikit-learn.
Exemple 1 : NMF sur des visages (Olivetti Faces)
Le jeu de données Olivetti contient 400 images de visages (64 × 64 pixels, soit 4096 features par image). Chaque image est une matrice de pixels à valeurs non-négatives, idéale pour la NMF.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_olivetti_faces
from sklearn.decomposition import NMF, PCA
# Chargement des données
faces = fetch_olivetti_faces(shuffle=True, random_state=42)
X = faces.data # (400, 4096)
# Normalisation vers [0, 1]
X = X / X.max()
# Application de la NMF
k = 6 # nombre de composantes
nmf = NMF(n_components=k, init='nndsvda', max_iter=500, random_state=42)
W_nmf = nmf.fit_transform(X) # (400, 6) - coefficients
H_nmf = nmf.components_ # (6, 4096) - composantes
# Visualisation des composantes
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
for i, ax in enumerate(axes.flat):
ax.imshow(H_nmf[i].reshape(64, 64), cmap='gray')
ax.set_title(f'Composante NMF {i+1}')
ax.axis('off')
plt.suptitle('Composantes extraites par NMF sur Olivetti Faces')
plt.tight_layout()
plt.show()
# Comparaison avec ACP
pca = PCA(n_components=k, random_state=42)
W_pca = pca.fit_transform(X)
H_pca = pca.components_
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
for i, ax in enumerate(axes.flat):
# Les composantes PCA peuvent être négatives, on les recentre pour l'affichage
comp = H_pca[i].reshape(64, 64)
comp = (comp - comp.min()) / (comp.max() - comp.min())
ax.imshow(comp, cmap='gray')
ax.set_title(f'Composante PCA {i+1}')
ax.axis('off')
plt.suptitle('Composantes extraites par PCA sur Olivetti Faces')
plt.tight_layout()
plt.show()
# Comparaison quantitative de l'erreur de reconstruction
recon_nmf = W_nmf @ H_nmf
recon_pca = W_pca @ H_pca
error_nmf = np.mean((X - recon_nmf) ** 2)
error_pca = np.mean((X - recon_pca) ** 2)
print(f'Erreur de reconstruction NMF : {error_nmf:.6f}')
print(f'Erreur de reconstruction PCA : {error_pca:.6f}')
# Interprétation : les composantes NMF sont additives et positives
print(f'Valeur min composantes NMF : {H_nmf.min():.6f}')
print(f'Valeur min composantes PCA : {H_pca.min():.6f}')
Observations clés :
- Les composantes NMF ressemblent à des parties de visages (œil, nez, bouche) car la contrainte de non-négativité force la décomposition additive
- Les composantes PCA ressemblent à des visages complets fantomatiques (eigenfaces) avec des zones claires et sombres qui n’ont pas de sens physique direct
- La NMF est plus interprétable — chaque composante contribue positivement à la reconstruction
- L’erreur de reconstruction de la PCA est généralement inférieure à celle de la NMF, car la PCA est optimale au sens des moindres carrés sans contrainte
Exemple 2 : NMF pour le topic modelling (analyse thématique de texte)
La NMF est extrêmement populaire pour l’extraction de thèmes dans des documents textuels. Voici comment l’appliquer avec un CountVectorizer :
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import NMF
# Corpus exemple
documents = [
"Le chat dort sur le canapé dans le salon.",
"Les chiens jouent dans le jardin avec une balle.",
"L'ordinateur utilise un processeur et de la mémoire vive.",
"Le réseau neuronal traite les données d'entrée.",
"La politique économique influence le marché financier.",
"Le président a annoncé de nouvelles mesures budgétaires.",
"Le football est le sport le plus populaire au monde.",
"Les joueurs de tennis s'entraînent pour le tournoi.",
"La programmation Python est essentielle en data science.",
"L'apprentissage automatique nécessite beaucoup de données.",
]
# Vectorisation
vectorizer = CountVectorizer(max_features=50, stop_words=None)
X_text = vectorizer.fit_transform(documents)
vocab = vectorizer.get_feature_names_out()
print(f"Matrice documents-termes : {X_text.shape}")
# NMF pour extraire 3 thèmes
nmf_text = NMF(n_components=3, init='nndsvda', max_iter=1000, random_state=42)
W_text = nmf_text.fit_transform(X_text)
H_text = nmf_text.components_
# Affichage des mots-clés par thème
n_top_words = 5
for i, topic in enumerate(H_text):
top_indices = topic.argsort()[-n_top_words:][::-1]
top_words = [vocab[j] for j in top_indices]
print(f"Thème {i+1} : {', '.join(top_words)}")
# Attribution des thèmes aux documents
for i, doc_weights in enumerate(W_text):
dominant_topic = doc_weights.argmax() + 1
print(f"Document {i+1} -> Thème dominant : {dominant_topic}")
Dans cet exemple, la NMF devrait regrouper les documents en trois thèmes cohérents : animaux, technologie/informatique, politique/économie, et sport. Chaque thème est décrit par les mots qui y contribuent le plus fortement dans la matrice H.
Exemple 3 : NMF avec initialisation et régularisation
from sklearn.decomposition import NMF
# NMF avec différentes stratégies d'initialisation
nmf_random = NMF(n_components=5, init='random', random_state=0, max_iter=1000)
nmf_nndsvda = NMF(n_components=5, init='nndsvda', random_state=0, max_iter=1000)
# Comparaison de la convergence
nmf_random.fit(X)
nmf_nndsvda.fit(X)
print(f"Reconstruction error (random) : {nmf_random.reconstruction_err_:.6f}")
print(f"Reconstruction error (nndsvda) : {nmf_nndsvda.reconstruction_err_:.6f}")
# NMF avec régularisation L1 (sparsité des composantes)
nmf_sparse = NMF(
n_components=5,
init='nndsvda',
alpha=0.1,
l1_ratio=0.5,
max_iter=1000,
random_state=42
)
W_sparse = nmf_sparse.fit_transform(X)
zeros_ratio = np.mean(W_sparse < 1e-10)
print(f"Sparsité de W (ratio de zéros) : {zeros_ratio:.2%}")
Hyperparamètres
| Hyperparamètre | Description | Valeurs typiques |
|---|---|---|
| n_components | Nombre de composantes latentes k. Détermine la granularité de la décomposition. | 2-20 selon la complexité des données |
| init | Méthode d’initialisation. nndsvda est recommandé pour la convergence ; random est rapide mais instable. |
nndsvda, nndsvdar, random |
| solver | Algorithme d’optimisation. cd (Coordinate Descent) est le plus rapide ; mu (Multiplicative Update) supporte beta_loss. |
cd, mu |
| max_iter | Nombre maximum d’itérations avant arrêt forcé. | 200-2000 |
| alpha | Coefficient de régularisation. Contrôle l’intensité de la pénalisation. | 0.0 (pas de régularisation) à 1.0 |
| l1_ratio | Proportion de régularisation L1 vs L2. 1.0 = L1 pur (sparsité), 0.0 = L2 pur (lissage). |
0.0, 0.5, 1.0 |
| beta_loss | Fonction de coût utilisée avec le solveur mu. 2.0 = norme de Frobenius ; 1.0 = divergence KL ; 0.0 = divergence d’Itakura-Saito. |
2.0, 1.0, 0.0 |
Guide de choix rapide
- Topic modelling (texte) :
solver='mu',beta_loss=1.0(KL),init='nndsvda' - Images / visages :
solver='cd',beta_loss=2.0(Frobenius),init='nndsvda' - Données audio / spectral :
solver='mu',beta_loss=0.0(Itakura-Saito) - Sparsité désirée : augmenter
alphaetl1_ratio
Avantages et limites
Avantages
- Interprétabilité supérieure : les composantes non-négatives correspondent à des parties reconnaissables, pas des combinaisons abstraites
- Décomposition additive : la reconstruction par somme pondérée est intuitive et physiquement plausible pour de nombreux phénomènes
- Sparsité naturelle : même sans régularisation explicite, la NMF tend à produire des facteurs creux, facilitant l’interprétation
- Adaptabilité : la fonction de coût peut être choisie selon le type de données (euclidienne, KL, Itakura-Saito)
- Efficacité computationnelle : les algorithmes modernes convergent rapidement sur des matrices de taille raisonnable
Limites
- Non-convexité : pas de garantie d’optimalité globale ; les résultats dépendent de l’initialisation et de
random_state - Non-unicité : la solution n’est pas unique — différentes factorisations peuvent donner la même erreur
- Sensibilité à k : le choix du nombre de composantes est critique et souvent subjectif
- Contrainte non-négative : inapplicable aux données contenant des valeurs négatives (nécessite un décalage préalable)
- Moins optimale en reconstruction que la PCA au sens des moindres carrés, car la contrainte de non-négativité restreint l’espace de recherche — cette contrainte n’est pas une lâcheté mais un compromis délibéré entre fidélité numérique et lisibilité humaine
4 cas d’usage concrets
Cas 1 : Topic modelling et analyse de corpus
La NMF est l’une des méthodes les plus utilisées pour extraire des thèmes latents d’un corpus documentaire. Après vectorisation (CountVectorizer ou TF-IDF), chaque thème correspond à une ligne de H, composée des poids des mots dans ce thème. Les mots les plus lourds révèlent le sujet. Contrairement à LDA, la NMF est déterministe (à initialisation fixée) et souvent plus rapide.
Cas 2 : Séparation de sources audio
Dans le traitement du signal audio, la NMF avec divergence d’Itakura-Saito (beta_loss=0.0) permet de séparer les sources sonores. Un spectrogramme (amplitudes non-négatives en fonction du temps et de la fréquence) est décomposé en dictionnaire de spectres (H) et activations temporelles (W). Chaque composante peut correspondre à un instrument ou une voix distincte.
Cas 3 : Recommandation et filtrage collaboratif
Dans les systèmes de recommandation, la matrice utilisateur-item (notes, clics, achats) contient uniquement des valeurs non-négatives. La NMF factorise cette matrice en profils utilisateurs (W) et profils items (H). Les valeurs manquantes sont estimées par le produit WH, permettant de prédire les préférences d’un utilisateur pour des items non encore consommés.
Cas 4 : Analyse d’images médicales
En imagerie médicale (IRM, TDM), les intensités de pixels sont toujours positives. La NMF permet d’extraire des motifs anatomiques récurrents ou des signatures pathologiques. Par exemple, dans l’analyse de scanners pulmonaires, chaque composante peut correspondre à un type de tissu ou une anomalie spécifique, facilitant le diagnostic assisté par ordinateur.
Voir aussi
- Maîtriser les Numbers Steps en Python : Guide Complet pour Optimiser Vos Algorithmes
- Emballage de Cercles II en Python : Techniques Avancées et Solutions Optimisées
Références
- scikit-learn documentation: sklearn.decomposition.NMF

