Semaine 3 du diagnostic chez un équipementier plastique. La ligne d’injection a 9 paramètres réglables. L’équipe process a déjà fait un DOE factoriel à 2 niveaux. Résultat : 2⁹ = 512 essais théoriques. En pratique, ils ont réduit à un fractionnaire L16 — 16 essais, 9 facteurs. Le taux de rebut a baissé de 3 %. Pas suffisant.
Le problème : le L16 suppose que les effets sont linéaires et indépendants. Sur une vis d’injection à haute pression avec refroidissement dynamique, c’est une hypothèse héroïque. Les interactions quadratiques entre température cylindre, pression d’injection et temps de maintien sont massives. Le plan d’expérience classique ne les voit pas.
La solution qu’on a déployée : un DOE augmenté. Données DOE existantes → surrogate XGBoost → optimisation Optuna sur le surrogate → validation terrain sur 3 points. Taux de rebut final : -18 %. Avec 19 essais terrain au total.
Voici le détail technique.
Le DOE classique : puissant mais limité
Le DOE (Design of Experiments) est une des meilleures inventions de la statistique industrielle. On structure les expériences pour séparer les effets des facteurs, minimiser le bruit, et tirer des conclusions avec un minimum d’essais. Taguchi, Box-Behnken, plans fractionnaires, tables orthogonales — l’arsenal est solide.
Mais il a des limites structurelles que personne ne te dit dans les formations.
Limite 1 — L’explosion combinatoire
Avec facteurs à niveaux chacun, un plan factoriel complet demande essais. Trois niveaux sur 5 facteurs = 243 essais. Sur 8 facteurs = 6 561 essais. En production, c’est inenvisageable. Les plans fractionnaires sauvent la mise, mais au prix d’une résolution partielle : certains effets d’interaction sont confondus avec des effets principaux. On ne sait plus exactement ce qu’on mesure.
Limite 2 — L’hypothèse de linéarité
La plupart des modèles DOE classiques supposent une réponse linéaire (ou quadratique si tu fais un plan CCC/Box-Behnken). Mais les process industriels sont rarement linéaires sur toute la plage. La viscosité d’un polymère en fonction de la température — non-linéaire. La déformation d’une pièce forgée en fonction de la vitesse de frappe — non-linéaire. Le modèle ANOVA sous-jacent rend ça mal.
Limite 3 — Zéro mécanisme d’apprentissage itératif
Un DOE classique est statique. Tu conçois le plan, tu lances les essais, tu analyses. Point. Si le premier plan révèle une zone intéressante, tu dois reconcevoir et relancer un nouveau plan complet. Pas d’adaptation en cours de route.
Le DOE classique est excellent pour identifier les facteurs significatifs. Il est mauvais pour trouver l’optimum exact dans un espace non-linéaire à haute dimension.
XGBoost comme surrogate model : le pont entre données et optimisation
Qu’est-ce qu’un surrogate model ?
L’idée est simple. Tu as des données expérimentales — même peu nombreuses (16, 32, 64 points). Tu entraînes un modèle de machine learning sur ces données. Ce modèle devient un substitut (surrogate) de ton process réel : il peut prédire la réponse pour n’importe quelle combinaison de paramètres, instantanément, sans lancer un essai physique.
Tu peux ensuite explorer des milliers de combinaisons sur le surrogate en quelques secondes, trouver l’optimum théorique, et ne valider physiquement que les 3-5 meilleurs candidats.
Pourquoi XGBoost spécifiquement ?
XGBoost (eXtreme Gradient Boosting) est le choix naturel pour les données tabulaires industrielles :
- Gère les non-linéarités sans hypothèse de forme. Les arbres de décision capturent naturellement les interactions entre facteurs.
- Robuste au bruit de mesure. Le boosting avec régularisation L1/L2 évite l’overfitting sur des petits datasets.
- Pas de scaling requis. Les features en unités différentes (°C, bar, s) ne posent pas de problème.
- Interprétable via SHAP values — on garde la capacité d’expliquer aux équipes ce qui pilote la réponse.
- Rapide à entraîner sur 16-256 points. On parle de millisecondes.
Entraîner le surrogate sur des données DOE
Les données DOE te donnent exactement le type de diversité dont XGBoost a besoin : les points sont espacés, couvrent le domaine expérimental, et ne sont pas corrélés entre eux (par construction du plan). C’est meilleur qu’une collecte aléatoire de données de production.
import numpy as np
import pandas as pd
import xgboost as xgb
from sklearn.model_selection import cross_val_score
# Données DOE (exemple injection plastique)
# Facteurs : Temp_cylindre (180-240°C), P_injection (600-1200 bar),
# T_maintien (2-8s), T_refroidissement (10-30s), V_injection (40-120 mm/s)
# Réponse : taux_rebut (%)
doe_data = pd.read_csv("doe_results.csv")
X = doe_data[["temp_cylindre", "p_injection", "t_maintien", "t_refroidissement", "v_injection"]]
y = doe_data["taux_rebut"]
# Surrogate XGBoost avec régularisation
surrogate = xgb.XGBRegressor(
n_estimators=300,
max_depth=4, # Profondeur limitée pour éviter l'overfitting sur petit dataset
learning_rate=0.05,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.1, # Régularisation L1
reg_lambda=1.0, # Régularisation L2
random_state=42
)
# Validation croisée — critique sur petit dataset
cv_scores = cross_val_score(surrogate, X, y, cv=5, scoring="neg_mean_absolute_error")
print(f"MAE surrogate : {-cv_scores.mean():.3f} ± {cv_scores.std():.3f}")
# Entraînement sur tout le dataset DOE
surrogate.fit(X, y)
Sur notre cas plastique : MAE = 0.41 % (±0.18). Le taux de rebut varie entre 2 % et 11 % dans le domaine expérimental — une erreur de ±0.4 % est acceptable pour l’optimisation.
Optuna + XGBoost : l’optimisation bayésienne sur le surrogate
Optuna en 30 secondes
Optuna est un framework Python d’optimisation automatique. Son moteur par défaut est le TPE (Tree-structured Parzen Estimator) — une forme d’optimisation bayésienne. À chaque trial, Optuna construit un modèle probabiliste de la fonction objectif et choisit intelligemment le prochain point à évaluer : là où l’amélioration espérée est maximale.
La différence avec une recherche par grille (grid search) ou aléatoire (random search) :
- Grid search : explore uniformément, gaspille des essais dans les zones mauvaises.
- Random search : aveugle, efficace en haute dimension mais peu précis près de l’optimum.
- TPE (Optuna) : apprend en cours de route, concentre les essais là où ça compte.
Optuna sur le surrogate XGBoost, c’est de l’optimisation bayésienne à coût quasi-nul : chaque “essai” Optuna prend quelques microsecondes, pas 4 heures de production.
La boucle complète
import optuna
optuna.logging.set_verbosity(optuna.logging.WARNING)
# Bornes du domaine expérimental (respecter les limites du DOE)
PARAM_BOUNDS = {
"temp_cylindre": (180, 240), # °C
"p_injection": (600, 1200), # bar
"t_maintien": (2.0, 8.0), # s
"t_refroidissement": (10, 30), # s
"v_injection": (40, 120), # mm/s
}
def objective(trial):
"""Fonction objectif : minimiser le taux de rebut prédit par le surrogate."""
params = {
"temp_cylindre": trial.suggest_float("temp_cylindre", *PARAM_BOUNDS["temp_cylindre"]),
"p_injection": trial.suggest_float("p_injection", *PARAM_BOUNDS["p_injection"]),
"t_maintien": trial.suggest_float("t_maintien", *PARAM_BOUNDS["t_maintien"]),
"t_refroidissement": trial.suggest_float("t_refroidissement", *PARAM_BOUNDS["t_refroidissement"]),
"v_injection": trial.suggest_float("v_injection", *PARAM_BOUNDS["v_injection"]),
}
X_candidate = pd.DataFrame([params])
y_pred = surrogate.predict(X_candidate)[0]
return y_pred
# Lancer l'étude Optuna
study = optuna.create_study(
direction="minimize",
sampler=optuna.samplers.TPESampler(seed=42)
)
study.optimize(objective, n_trials=5000, show_progress_bar=True)
# Résultat
best_params = study.best_params
best_value = study.best_value
print(f"\nMeilleurs paramètres trouvés :")
for k, v in best_params.items():
print(f" {k}: {v:.2f}")
print(f"Taux de rebut prédit : {best_value:.2f}%")
5 000 trials en quelques secondes. Sur notre cas : taux de rebut prédit = 1.3 %. Les paramètres process nominaux donnaient 6.2 %.
Sélectionner les candidats pour validation terrain
Ne pas tester le seul point optimal — le surrogate a une incertitude. Mieux vaut sélectionner les 5 meilleurs trials diversifiés dans l’espace des paramètres.
# Extraire les top 20 trials
trials_df = study.trials_dataframe()
top20 = trials_df.nsmallest(20, "value")
# Dédupliquer par clustering (éviter 5 points quasiment identiques)
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
param_cols = [f"params_{k}" for k in PARAM_BOUNDS.keys()]
X_top = top20[param_cols].values
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_top)
kmeans = KMeans(n_clusters=5, random_state=42, n_init=10)
labels = kmeans.fit_predict(X_scaled)
# Un représentant par cluster (le meilleur)
candidates = []
for cluster_id in range(5):
cluster_mask = labels == cluster_id
cluster_trials = top20[cluster_mask]
best_in_cluster = cluster_trials.iloc[0] # déjà trié par value
candidates.append(best_in_cluster)
print("\n5 candidats pour validation terrain :")
for i, c in enumerate(candidates):
print(f"\nCandidat {i+1} — Rebut prédit: {c['value']:.2f}%")
for k in PARAM_BOUNDS.keys():
print(f" {k}: {c[f'params_{k}']:.2f}")
La boucle DOE augmenté : vue d’ensemble
flowchart TD
A[DOE initial\n16-32 essais terrain] --> B[Surrogate XGBoost\nentraîné sur données DOE]
B --> C{Validation CV\nMAE acceptable ?}
C -- Non --> D[Enrichir DOE\n+8 essais ciblés]
D --> B
C -- Oui --> E[Optuna TPE\n5000 trials sur surrogate]
E --> F[Top 5 candidats\nclustering diversifié]
F --> G[Validation terrain\n3-5 essais physiques]
G --> H{Gain confirmé ?}
H -- Oui --> I[Réglages validés\nDéploiement process]
H -- Non --> J[Ajouter points terrain\nau dataset DOE]
J --> B
style A fill:#f0f4ff,stroke:#3b82f6
style B fill:#fef3c7,stroke:#f59e0b
style E fill:#fef3c7,stroke:#f59e0b
style G fill:#f0fdf4,stroke:#22c55e
style I fill:#f0fdf4,stroke:#22c55e
style D fill:#fff1f2,stroke:#ef4444
style J fill:#fff1f2,stroke:#ef4444
La boucle est itérative. Le DOE fournit les données initiales. XGBoost apprend la surface de réponse. Optuna trouve l’optimum théorique. Le terrain valide (ou invalide). Les résultats terrain reviennent enrichir le surrogate. On converge rapidement.
Comparaison des approches
Voici une comparaison honnête sur un process type (5 facteurs, réponse non-linéaire) :
| Approche | Essais terrain | Qualité optimum | Non-linéarités | Coût analyse |
|---|---|---|---|---|
| DOE factoriel complet (3 niveaux) | 3⁵ = 243 | Excellent (dans le plan) | Quadratique seulement | Faible |
| DOE fractionnaire L16 | 16 | Moyen | Linéaire uniquement | Faible |
| DOE + surrogate XGBoost + Optuna | 16 + 5 = 21 | Excellent | Toutes interactions | Moyen |
| Optuna seul (black-box, terrain) | 200-500 | Excellent | Toutes interactions | Très élevé |
DOE seul : peu d’essais, mais optimum limité si la réponse est non-linéaire. Le L16 sur 5 facteurs confond les interactions d’ordre 2 — tu peux passer à côté de l’optimum vrai.
Optuna black-box terrain : excellent en théorie, inutilisable en pratique industrielle. Chaque essai prend du temps, de la matière, arrête la production. 200 essais terrain = des semaines de perturbation. Personne ne l’accepte.
DOE + surrogate + Optuna : le meilleur des deux mondes. Tu gardes l’efficacité du DOE pour explorer le domaine, et tu ajoutes la puissance d’un modèle non-linéaire pour trouver l’optimum exact. Le coût en essais terrain reste faible (21 au lieu de 243 ou 500).
L’approche surrogate résout le problème fondamental de l’optimisation industrielle : les essais terrain coûtent cher, mais l’espace des paramètres est vaste et non-linéaire. XGBoost + Optuna permettent d’explorer cet espace en simulation.
Section risques : ce qui peut mal tourner
Cette approche est puissante mais pas magique. Trois risques majeurs à connaître.
Risque 1 — Overfitting du surrogate
Sur un petit dataset (16-32 points), XGBoost peut mémoriser les données au lieu d’apprendre la tendance. L’optimum trouvé sur le surrogate n’existe pas sur le process réel.
Signes d’alerte : MAE cross-validation élevée ; les prédictions du surrogate sur les candidats semblent trop bonnes (rebut prédit = 0.1 % alors que le minimum observé en DOE est 3 %).
Contre-mesures :
- Régularisation forte (reg_alpha, reg_lambda, max_depth limité)
- Validation croisée systématique avant d’utiliser le surrogate
- Fixer un seuil MAE acceptable (typ. < 20 % de la plage de la réponse)
- Calibrer la confiance : utiliser un ensemble de modèles (5 XGBoost avec seeds différents) et regarder la variance des prédictions sur les candidats
# Ensemble de surrogates pour estimer l'incertitude
ensemble = []
for seed in range(5):
m = xgb.XGBRegressor(**{**params_xgb, "random_state": seed})
m.fit(X, y)
ensemble.append(m)
# Prédiction avec intervalle de confiance
def predict_with_uncertainty(X_cand):
preds = np.array([m.predict(X_cand) for m in ensemble])
return preds.mean(axis=0), preds.std(axis=0)
mean_pred, std_pred = predict_with_uncertainty(X_candidates)
print(f"Rebut prédit : {mean_pred[0]:.2f}% ± {1.96*std_pred[0]:.2f}%")
Risque 2 — Extrapolation hors du domaine expérimental
C’est le péché capital du surrogate. XGBoost interpole bien dans le domaine DOE. Il extrapole très mal en dehors. Si Optuna explore des valeurs hors des bornes du DOE, les prédictions deviennent absurdes.
Erreur classique : élargir les bornes dans suggest_float au-delà des valeurs testées en DOE. Tu obtiens des prédictions basses en dehors du domaine — c’est du bruit, pas un optimum.
Règle stricte : les bornes Optuna = exactement les bornes du DOE. Pas de marge. Si tu veux explorer plus loin, ajoute des essais DOE à la frontière d’abord, puis ré-entraîne le surrogate.
# BONNE pratique : extraire automatiquement les bornes depuis les données DOE
PARAM_BOUNDS = {
col: (float(X[col].min()), float(X[col].max()))
for col in X.columns
}
# Jamais élargir ces bornes sans données terrain supplémentaires
Risque 3 — Mauvaise spécification de la réponse
Le surrogate optimise exactement ce que tu lui demandes d’optimiser. Si ta réponse est “taux de rebut visuel”, il va trouver un réglage qui minimise le rebut visuel — au détriment peut-être de la résistance mécanique ou de la dimensionnelle. Les optimisations mono-critère en process industriel sont rarement pertinentes.
Solution : optimisation multi-objectif avec Optuna (direction=“minimize” sur plusieurs réponses, ou construction d’une fonction de désirabilité composite).
# Désirabilité composite (approche Derringer-Suich simplifiée)
def composite_desirability(rebut_pred, dimension_pred, cycle_time_pred):
"""Combine plusieurs réponses en un score unique entre 0 et 1."""
# Rebut : minimiser (target = 0, max acceptable = 8%)
d_rebut = max(0, (8 - rebut_pred) / 8)
# Dimension : cibler 50mm ±0.2mm (tolérance bilatérale)
d_dim = max(0, 1 - abs(dimension_pred - 50) / 0.2)
# Cycle time : minimiser (target = 15s, max = 30s)
d_cycle = max(0, (30 - cycle_time_pred) / 15)
# Géométrique (tous les critères doivent être satisfaits)
return (d_rebut * d_dim * d_cycle) ** (1/3)
Validation terrain : le modèle propose, l’opérateur valide
C’est la partie que les équipes data oublient souvent. Le surrogate donne une liste de candidats. Il faut les valider sur le terrain, et le faire correctement.
Protocole de validation terrain :
-
Prépare 3-5 candidats, pas 1 seul. En cas d’échec du premier, tu as des alternatives sans relancer toute la chaîne.
-
Valide en mode contrôlé. Chaque candidat = au moins 3 cycles de production stables avant mesure (purge des effets transitoires). Note les conditions exactes : température ambiante, lot de matière, opérateur.
-
Mesure sur les mêmes KPI que le DOE initial. Aucune comparaison possible si tu changes le protocole de mesure.
-
Compare au point nominale et au meilleur point DOE, pas seulement à la prédiction surrogate. Ce qui compte pour la production, c’est le gain vs le réglage actuel.
-
Si un candidat est meilleur que prédit : réelle opportunité. Ajoute ce point au dataset, ré-entraîne le surrogate, relance Optuna autour de ce point.
-
Si tous les candidats sont pires que prédit : signal fort d’overfitting ou d’un facteur non-contrôlé. Revois la liste des facteurs, vérifie les conditions opératoires.
Règle d’or : l’opérateur a le dernier mot. Un réglage optimisé par le modèle mais inapplicable (température trop haute pour la vis, pression hors capacité de la presse) ne vaut rien. Construire la liste de candidats en consultation avec le régleur, pas après.
Retour d’expérience terrain
Sur l’équipementier plastique mentionné en ouverture :
- DOE initial : 16 essais (fractionnaire L16), 9 facteurs. Le modèle linéaire expliquait 58 % de la variance du taux de rebut. Insuffisant.
- Surrogate XGBoost entraîné sur les 16 points : MAE cross-validation = 0.38 %. R² = 0.91. Nettement mieux.
- Optuna : 5 000 trials en 8 secondes. Optimum prédit : 1.1 % de rebut (vs 6.2 % nominal).
- Validation terrain : 3 candidats testés. Le meilleur : 1.9 % de rebut mesuré (vs 1.1 % prédit — écart acceptable, dans l’intervalle de confiance de l’ensemble de surrogates).
- Gain net : -4.3 points de rebut. Sur 2 millions de pièces/an à 0.08 €/pièce de coût de non-qualité : économie annuelle estimée > 68 000 €.
- Total essais terrain : 16 (DOE) + 3 (validation) = 19. Durée totale : 2,5 jours.
Le DOE classique avec le même budget temps aurait permis d’explorer 32 points au maximum, sans garantie de trouver l’optimum non-linéaire.
Aller plus loin : itérations et apprentissage actif
La vraie puissance du DOE augmenté, c’est la capacité à itérer intelligemment. Après la première validation terrain, tu peux relancer la boucle avec un dataset enrichi.
Stratégie d’acquisition active : plutôt que de choisir les prochains essais terrain au hasard, utilise le surrogate pour identifier les zones d’incertitude maximale — là où les prédictions de l’ensemble divergent le plus.
# Sélectionner les prochains points DOE par incertitude maximale
def acquisition_uncertainty(X_candidates, ensemble):
"""Acquisition function : maximiser l'incertitude du surrogate."""
_, std = predict_with_uncertainty(X_candidates)
return std
# Générer une grille de points candidats dans le domaine
from itertools import product
grid_points = pd.DataFrame(
list(product(*[np.linspace(lo, hi, 10) for lo, hi in PARAM_BOUNDS.values()])),
columns=list(PARAM_BOUNDS.keys())
)
# Calculer l'incertitude sur toute la grille
uncertainties = acquisition_uncertainty(grid_points, ensemble)
# Top 8 points les plus incertains → prochain batch DOE
next_doe_batch = grid_points.iloc[uncertainties.argsort()[-8:][::-1]]
print("Prochain batch d'essais recommandés :")
print(next_doe_batch.to_string(index=False))
Cette approche — appelée apprentissage actif (active learning) — permet de construire un surrogate de plus en plus précis avec le minimum d’essais terrain. C’est la frontière entre le DOE classique et l’optimisation Bayésienne pure.
Quand utiliser cette approche
Conditions nécessaires :
- 4+ facteurs avec interactions suspectées
- Chaque essai terrain coûte en temps/matière/production
- Tu as déjà des données DOE (même peu nombreuses)
- La réponse est probablement non-linéaire (signaux visuels, historique process)
Conditions suffisantes (un seul critère) :
- Le DOE classique a déjà montré ses limites
- Un expert process dit “ça dépend de comment les paramètres interagissent”
- Les gains actuels stagnent malgré plusieurs optimisations classiques
À éviter si :
- Moins de 12 points DOE disponibles (surrogate trop incertain)
- Tous les effets sont clairement linéaires (mélange simple, processus chimique à un facteur)
- Le process varie fortement dans le temps (conditions non-stationnaires) — le surrogate capture un instantané
Conclusion
Le DOE augmenté n’est pas une révolution. C’est une extension logique de ce que les équipes industrielles font déjà. Tu gardes la rigueur du DOE pour explorer le domaine proprement. Tu ajoutes XGBoost pour capturer les non-linéarités que le modèle ANOVA linéaire ne voit pas. Tu ajoutes Optuna pour trouver l’optimum en simulation plutôt que sur le terrain.
Le résultat : moins d’essais, meilleur optimum, et un modèle surrogate que tu peux réutiliser à chaque campagne de réglage.
La barrière d’entrée est faible : pip install xgboost optuna scikit-learn pandas. Les données DOE que tu as déjà suffisent pour commencer. Et le protocole de validation terrain, tu le fais de toute façon.
Ce qui change, c’est la manière d’exploiter ces données. Pas les données elles-mêmes.
PaulO Obara — BCUB3 | Conseil Performance Industrielle DATA & IA
Les scripts de cet article sont disponibles dans le dépôt GitHub BCUB3. Adapte les bornes de paramètres et les réponses à ton process spécifique avant toute utilisation.