Quand mon IA prédit 47 ventes, je peux te dire EXACTEMENT pourquoi
L'IA dit 'je prédis 47 ventes la semaine prochaine'. Le client demande 'pourquoi 47 et pas 30 ?'. Sans explainabilité, c'est de la magie noire. Avec TreeShap codé en C# pur dans SaleCast, c'est une réponse chiffrée à 5 lignes : 'parce que +12 pour la saison, +8 pour la promo, -3 pour la météo, +6 pour l'historique, +24 base'. Voici comment.
Cet article décortique le projetSaleCastLe problème qui tue 80 % des projets ML
Tu déploies un modèle de forecasting. Premier jour en prod, premier appel client.
"Vous prédisez 47 ventes la semaine prochaine. Comment vous arrivez à 47 ?"
Si tu réponds "c'est le modèle", tu viens de perdre. Pas le client — tu viens de perdre la confiance qui te permet de garder ce client.
L'IA "boîte noire" est l'erreur stratégique #1 des projets ML en production. Pas parce qu'elle est moins précise — souvent elle est plus précise. Mais parce qu'elle n'est pas auditable.
Et un décideur qui prend des décisions sur une IA non auditable est exposé à des questions auxquelles il ne peut pas répondre. C'est intenable.
La solution s'appelle SHAP. Et je l'ai codé en C# pur dans SaleCast.
Le concept SHAP en 30 secondes
SHAP (SHapley Additive exPlanations) est issu d'une vieille théorie des jeux (Lloyd Shapley, prix Nobel 2012). L'idée :
Pour chaque prédiction du modèle, on calcule la contribution exacte de chaque feature, en mesurant la différence que cette feature fait par rapport à une baseline (un échantillon de référence).
Mathématiquement :
prediction = base_value + Σ shap_value(feature_i)
Le base_value est ce que le modèle prédirait sans aucune information
spécifique (la moyenne sur le dataset). Chaque shap_value est la
contribution positive ou négative de la feature à cette prédiction
particulière.
C'est additif par construction. C'est mathématiquement défendable par construction. C'est exactement ce qu'un client comprend.
Le code SaleCast : 126 lignes de C#
Voici la structure que j'ai codée :
public sealed record ShapValues
{
public required double BaseValue { get; init; }
public required IReadOnlyDictionary<string, double> Contributions { get; init; }
public double Prediction => BaseValue + Contributions.Values.Sum();
public IReadOnlyList<KeyValuePair<string, double>> TopFeatures(int n = 5)
=> Contributions
.OrderByDescending(kv => Math.Abs(kv.Value))
.Take(n)
.ToList();
}
public static class TreeShapExplainer
{
public static ShapValues Explain(
Func<float[], float> predict,
float[] features,
IReadOnlyList<float[]> backgroundSamples,
IReadOnlyList<string> featureNames,
int maxBackgroundSamples = 50)
{
if (backgroundSamples.Count == 0 || features.Length == 0)
{
return new ShapValues
{
BaseValue = predict(features),
Contributions = new Dictionary<string, double>()
};
}
var background = backgroundSamples.Count <= maxBackgroundSamples
? backgroundSamples
: SubsampleBackground(backgroundSamples, maxBackgroundSamples);
// Pour chaque feature, on calcule sa contribution moyenne
// en mesurant la prédiction avec vs sans (remplacée par baseline)
// ...
}
}
L'algorithme est kernel SHAP — la version générique qui marche sur n'importe quel modèle. Pour LightGBM spécifiquement, j'utilise Tree SHAP qui est ~100× plus rapide grâce aux propriétés de l'arbre.
Un cas concret : "Pourquoi 47 ?"
Modèle : LightGBM trained on 18 mois de ventes du t-shirt Marin XL noir. Prédiction pour la semaine du 21 juillet 2025 : 47 unités.
Le client demande pourquoi. Voici ce que j'affiche :
═══════════════════════════════════════════════════════════════
DÉCOMPOSITION SHAP · t-shirt Marin XL noir · semaine du 15/08
═══════════════════════════════════════════════════════════════
Base value (moyenne historique) +24.3
Top 5 features qui poussent ↑ :
HolidayCalendar (15 août férié) +11.2
Lag7Days (la sem. dernière) + 6.8
CategoryAvgDemand (été chargé) + 4.5
PromotionActive (-15% en cours) + 3.9
SeasonalIndex (pic juillet-août) + 2.7
Top 3 features qui tirent ↓ :
WeatherRain (pluie prévue) - 2.8
PriceIncreaseLast30d (+5% le mois -) - 1.9
StockoutLast14d (rupture le 1er) - 1.4
PRÉDICTION FINALE : 47.3 unités
Le client lit ce tableau et comprend immédiatement ce qui pousse la prévision haute. Il peut challenger : "Tu es sûr du +11 sur le 15 août ? Mon historique de 2023 montrait que ce n'était pas un gros jour."
Je peux vérifier, mesurer son hypothèse, ajuster le modèle si nécessaire. La conversation devient productive.
Sans SHAP, c'est juste "le modèle dit 47". Sans matière pour la discussion. Sans matière pour la confiance.
Tree SHAP — l'optimisation qui change tout
Le SHAP générique (Kernel SHAP) calcule la contribution de chaque feature en permutant les valeurs avec des échantillons de background. Pour 50 échantillons de background et 30 features, ça fait 1 500 prédictions par explication. C'est lent.
Pour les modèles à arbres (LightGBM, XGBoost, RandomForest), il existe une solution analytique exacte : Tree SHAP. Au lieu de simuler par permutation, on parcourt l'arbre de décision et on calcule la contribution exacte de chaque feature à chaque split.
Coût : 1 traversée d'arbre par prédiction, indépendant du nombre de samples de background. ~100× plus rapide.
Dans SaleCast, sur LightGBM avec 800 arbres, explainer une prédiction prend :
- Kernel SHAP : ~80 ms
- Tree SHAP : 0,8 ms
Sur 2 800 SKU, c'est la différence entre "explicable produit par produit en temps réel" et "explicable batch nocturne uniquement".
J'ai codé les deux. Pour LightGBM, Tree SHAP est utilisé par défaut. Pour TiRex et Chronos (foundation models), Kernel SHAP fallback (lent mais fonctionnel).
Les 3 visualisations que j'expose au client
L'explainabilité ne sert à rien si elle n'est pas digérable. Dans SaleCast, j'expose 3 vues :
1. Decomposition view (la table SHAP brute)
Le tableau ci-dessus. Pour les power users qui veulent les chiffres exacts.
2. Waterfall chart (la vue visuelle)
24.3 ─┐
├─[+11.2] HolidayCalendar
├─[+6.8 ] Lag7Days
├─[+4.5 ] CategoryAvgDemand
├─[+3.9 ] PromotionActive
├─[-2.8 ] WeatherRain
├─[-1.9 ] PriceIncreaseLast30d
↓
47.3
Une cascade visuelle. 80 % des utilisateurs préfèrent cette vue. Elle est intuitive.
3. Sensitivity view (le what-if)
"Si tu retires la promo, qu'est-ce que ça change ?"
→ Prédiction sans promo : 43.4 (-3.9)
"Et si la pluie devient ensoleillé ?"
→ 50.1 (+2.8)
Cette vue permet au client de faire de la planification. C'est ce qu'il veut réellement.
Ce que ça change commercialement
Avant SHAP : "Notre modèle prédit X." Après SHAP : "Notre modèle prédit X parce que Y, Z, W. Si Y change, voici l'impact."
La deuxième version :
- Crédibilise le modèle (tu peux montrer le raisonnement)
- Permet la conversation (le client peut challenger un facteur)
- Évite les surprises (quand la prédiction est très haute ou très basse, le client le voit avant le bug)
- Démontre la maîtrise du sujet par le fournisseur
J'ai fermé 2 deals supplémentaires rien que sur cette feature. À chaque fois, la phrase clé du décideur a été :
"Enfin une IA qui peut expliquer ses choix."
C'est psychologique autant que technique. Mais ça fait des deals.
Stack & code
- TreeShapExplainer.cs — 126 lignes
- ShapValues record (BaseValue + Contributions + TopFeatures helper)
- Kernel SHAP générique + Tree SHAP optimisé LightGBM
- Microsoft.ML 5 + LightGBM comme moteur principal
- Background sampling auto si dataset > 50 échantillons (limite exploration combinatoire)
- Rendu Blazor : table SHAP + waterfall chart custom (chart-kit, mon lib SVG)
- Temps d'inférence : 0,8 ms par explication (Tree SHAP) sur LightGBM 800 arbres
L'explainabilité n'est pas une feature "advanced". C'est une feature de base pour tout modèle ML déployé chez un client. Le fait que la plupart des plateformes ML ne l'exposent pas natively est un trou que je trouve incompréhensible — surtout en 2026, post-EU AI Act.
La leçon
Un modèle ML qui ne s'explique pas est un modèle qui ne peut pas être déployé en production responsable. Point.
L'EU AI Act, entré en vigueur en 2025, impose l'explainabilité pour les systèmes ML à risque élevé. Les e-commerçants à forte volumétrie y rentrent.
Mais avant la conformité réglementaire, il y a la conformité opérationnelle : ton client doit pouvoir comprendre, vérifier, challenger, et adopter le modèle. Sans ça, ton modèle est un gadget qu'il ne consulte pas.
SHAP code en 126 lignes de C# pur. Pas de Python. Pas de microservice. Pas de dépendance externe. Il tourne dans le même process que les forecasts qu'il explique.
C'est, pour moi, le ratio ROI/complexité le plus élevé que j'ai trouvé sur un projet ML en 18 mois.
Florian Sola
Lead Technique · Haute performance temps réel · 9 ans d'expérience