Une méthode qui résume chaque client et chaque produit par quelques nombres, les facteurs de goût. La note prédite est le produit des deux profils. ALS (moindres carrés alternés) calcule ces profils en figeant tour à tour les produits puis les clients. Popularisée par le prix Netflix, la factorisation est l'algorithme de recommandation intégré à Spark.
Chaque client est décrit par un dosage de goûts (bio, petits prix, marques premium), chaque produit par ce qu'il offre sur ces mêmes axes. Un client aimera un produit si les deux profils pointent dans la même direction.
On choisit k facteurs, ici 4. Chaque client reçoit un vecteur de 4 nombres, chaque produit aussi, tirés au hasard au départ.
Produits figés, on calcule le meilleur profil de chaque client sur ses notes connues. Puis clients figés, on recalcule les produits. Chaque étape est une régression ridge, rapide et parallélisable.
Après une quinzaine d'allers-retours, le produit des deux profils donne une note prédite pour chaque couple client-produit, y compris ceux jamais notés.
600 clients, 120 produits, environ 10 800 notes : 85 % des cases sont vides. On cache 20 % des notes connues pour tester les prédictions.
Chaque client obtient une note estimée sur tout le catalogue, et un profil de goût en 4 facteurs. Ces profils servent aussi à regrouper les clients par affinités.
La RMSE (racine de l'erreur quadratique moyenne) mesure l'écart typique entre note prédite et note réelle, en points de note. Dans l'exemple Python, ALS obtient 0,91 contre 0,99 pour la moyenne de chaque produit. En production, on la complète par la précision des recommandations et un test A/B.
Noms donnés pour recommenderlab en R ; l'exemple Python est écrit avec NumPy. Spark MLlib utilise rank, regParam et maxIter.
Nombre de goûts cachés. Trop peu, les profils sont grossiers ; trop, le modèle apprend le bruit. Sur l'exemple, 4 facteurs suffisent car les données contiennent 4 groupes de goûts.
Pénalité qui freine les profils extrêmes, comme en régression ridge. Le réglage le plus sensible, à choisir par validation. Son échelle dépend de l'outil : ne pas recopier une valeur d'une implémentation à l'autre.
Nombre d'allers-retours entre clients et produits. 10 à 20 suffisent en général ; au-delà, l'erreur ne baisse plus.
Des notes se modélisent directement. Des clics ou des achats demandent la variante implicite (ALS_implicit dans recommenderlab, implicitPrefs dans Spark), qui traite l'absence d'interaction comme un signal faible.
# Goûts cachés des clients : factorisation de matrice (ALS) en R
library(recommenderlab)
notes <- read.csv("notes_produits.csv")
mat <- as(notes, "realRatingMatrix") # matrice creuse clients x produits
set.seed(42)
# 80 % des clients pour apprendre ; pour les autres, 5 notes connues et le reste à prédire
schema <- evaluationScheme(mat, method = "split", train = 0.8, given = 5)
als <- Recommender(getData(schema, "train"), method = "ALS",
parameter = list(n_factors = 4, lambda = 0.1, n_iterations = 15))
ibcf <- Recommender(getData(schema, "train"), method = "IBCF", parameter = list(k = 20))
# Notes prédites sur les produits cachés, comparées aux vraies notes
pred_als <- predict(als, getData(schema, "known"), type = "ratings")
pred_ibcf <- predict(ibcf, getData(schema, "known"), type = "ratings")
print(rbind(ALS = calcPredictionAccuracy(pred_als, getData(schema, "unknown")),
IBCF = calcPredictionAccuracy(pred_ibcf, getData(schema, "unknown"))))
# Les 5 produits à proposer au client 1, parmi ceux qu'il n'a pas notés
modele <- Recommender(mat, method = "ALS", parameter = list(n_factors = 4, lambda = 0.1, n_iterations = 15))
print(as(predict(modele, mat["1", ], n = 5), "list"))
# Goûts cachés des clients : factorisation de matrice (ALS) en Python
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
notes = pd.read_csv("notes_produits.csv")
train, test = train_test_split(notes, test_size=0.2, random_state=42)
mat = train.pivot_table(index="id_client", columns="id_produit", values="note")
R, connu = mat.values, mat.notna().values
moyenne = np.nanmean(R)
ecart = np.where(connu, R - moyenne, 0)
k, lam = 4, 5.0 # 4 facteurs de goût, pénalité contre le surapprentissage
rng = np.random.default_rng(42)
P = rng.normal(0, 0.1, (R.shape[0], k)) # profil de goût de chaque client
Q = rng.normal(0, 0.1, (R.shape[1], k)) # profil de chaque produit
for iteration in range(15): # on alterne : produits figés -> clients, puis clients figés -> produits
for u in range(R.shape[0]):
m = connu[u]
P[u] = np.linalg.solve(Q[m].T @ Q[m] + lam * np.eye(k), Q[m].T @ ecart[u, m])
for i in range(R.shape[1]):
m = connu[:, i]
Q[i] = np.linalg.solve(P[m].T @ P[m] + lam * np.eye(k), P[m].T @ ecart[m, i])
# Notes prédites sur des couples client-produit jamais vus
u, i = mat.index.get_indexer(test.id_client), mat.columns.get_indexer(test.id_produit)
pred = np.clip(moyenne + (P[u] * Q[i]).sum(axis=1), 1, 5)
rmse = lambda p: round(np.sqrt(np.mean((p - test.note) ** 2)), 3)
print("RMSE ALS :", rmse(pred), "| RMSE moyenne du produit :", rmse(mat.mean()[test.id_produit].values))
print("Profil de goût des 3 premiers clients :\n", pd.DataFrame(P[:3], index=mat.index[:3]).round(2))
La SVD classique décompose une matrice entièrement remplie. Une matrice de notes est surtout vide, et remplacer les vides par des zéros fausserait tout. ALS ne s'ajuste que sur les notes connues, avec une pénalité contre le surapprentissage.
Trouver les deux profils en même temps est un problème difficile. Si l'on fige les produits, trouver les clients devient une simple régression, avec une solution exacte. Alterner fait baisser l'erreur à chaque étape, et chaque client peut être calculé en parallèle.
Une dimension de goût que l'algorithme découvre seul, sans qu'on la lui nomme. Après coup, on regarde les produits les plus marqués sur chaque facteur pour l'interpréter : premium, bio, petits prix. Certains facteurs restent difficiles à nommer.
Plus simple et plus explicable, moins précise quand les clients ont peu de notes en commun.
Voir la fiche → la cousine mathématiqueLa décomposition classique d'une matrice complète. ALS s'en inspire en ne tenant compte que des cases remplies.
Voir la fiche → des facteurs positifsImpose des facteurs positifs, plus faciles à lire comme des thèmes de goût.
Voir la fiche →Dataistudio forme les équipes au machine learning et à l'IA, sur des cas concrets.
Nous utilisons des cookies de mesure d'audience et de suivi publicitaire pour comprendre la fréquentation du site et l'efficacité de nos annonces. Rien n'est déposé sans votre accord. En savoir plus