Depannage — Entrainement et serialisation de modeles
Introduction
Ce guide couvre les problemes les plus courants rencontres lors de l'entrainement, l'evaluation et la serialisation de modeles ML. Chaque probleme est presente avec ses symptomes, causes, solutions et mesures preventives.
1. Incompatibilite de version lors du chargement de modeles
Symptome
ModuleNotFoundError: No module named 'sklearn.ensemble._forest'
ou
UserWarning: Trying to unpickle estimator RandomForestClassifier from version 1.2.0
when using version 1.4.0. This might lead to breaking code or invalid results.
Cause
Le modele a ete serialise avec une version differente de scikit-learn que celle utilisee pour le charger. Les structures de classes internes changent entre les versions.
Solution
# Option 1: Match the scikit-learn version
pip install scikit-learn==1.2.0 # match the version used during training
# Option 2: Re-train and re-serialize with current version
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
joblib.dump(model, 'model_new_version.joblib')
Prevention
import sklearn
import json
metadata = {
"python_version": "3.10.12",
"sklearn_version": sklearn.__version__,
"numpy_version": np.__version__,
"serialized_date": "2025-01-15",
}
with open('model_metadata.json', 'w') as f:
json.dump(metadata, f, indent=2)
Toujours sauvegarder un fichier requirements.txt ou metadata.json avec les versions exactes de toutes les dependances a cote de votre modele serialise.
2. Surapprentissage — Performance d'entrainement elevee, performance de test faible
Symptome
Training Accuracy: 0.9990
Test Accuracy: 0.7523
Gap: 0.2467 ← Trop grand !
Cause
Le modele a memorise le bruit des donnees d'entrainement au lieu d'apprendre des patterns generalisables. Causes courantes :
- Modele trop complexe (trop d'arbres, profondeur illimitee)
- Pas assez de donnees d'entrainement
- Trop de features par rapport au nombre d'echantillons
- Pas de regularisation
Solution
# Solution 1: Add regularization
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(C=0.1, penalty='l2') # C small = more regularization
# Solution 2: Reduce model complexity
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(
n_estimators=100,
max_depth=5, # limit depth
min_samples_leaf=10, # require minimum samples per leaf
max_features='sqrt', # use subset of features
random_state=42
)
# Solution 3: Feature selection
from sklearn.feature_selection import SelectKBest, f_classif
selector = SelectKBest(f_classif, k=10)
X_train_selected = selector.fit_transform(X_train, y_train)
# Solution 4: Cross-validation to detect early
from sklearn.model_selection import cross_val_score
scores = cross_val_score(model, X_train, y_train, cv=5)
print(f"CV Score: {scores.mean():.4f} ± {scores.std():.4f}")
# Large std = overfitting risk
Si l'ecart entre la performance d'entrainement et de test depasse 10 points de pourcentage, c'est probablement du surapprentissage. Si l'ecart-type des scores de validation croisee est superieur a 0.05, le modele est instable.
Prevention
| Strategie | Quand l'utiliser |
|---|---|
| Validation croisee systematique | Toujours |
| Regularisation (L1, L2) | Modeles lineaires |
| Limiter la profondeur / complexite | Arbres de decision, Random Forest |
| Augmenter les donnees | Petit jeu de donnees (< 1000 echantillons) |
| Arret precoce | Deep learning, boosting |
| Dropout | Reseaux de neurones |
3. Sous-apprentissage — Performance faible partout
Symptome
Training Accuracy: 0.6234
Test Accuracy: 0.6102
Cause
Le modele est trop simple pour capturer les patterns dans les donnees.
Solution
# Solution 1: Use a more complex model
from sklearn.ensemble import GradientBoostingClassifier
model = GradientBoostingClassifier(n_estimators=200, max_depth=5)
# Solution 2: Add polynomial features
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, interaction_only=True)
X_train_poly = poly.fit_transform(X_train)
# Solution 3: Engineer better features
# (domain-specific — combine or transform existing features)
# Solution 4: Reduce regularization
model = LogisticRegression(C=10.0) # higher C = less regularization
4. Fuite de donnees — Resultats trop beaux pour etre vrais
Symptome
Cross-Validation Accuracy: 0.9999
Test Accuracy: 0.6543
Des scores de validation irrealistes qui chutent drastiquement sur l'ensemble de test.
Cause
Des informations de l'ensemble de test ont fuite dans le processus d'entrainement. Sources courantes :
| Source de fuite | Exemple |
|---|---|
| Mise a l'echelle avant le decoupage | scaler.fit(X_all) avant train_test_split |
| Feature incluant la cible | Colonne 100% correlee avec y (ex. : "diagnosis_code") |
| Donnees dupliquees | Meme patient dans train et test |
| Ordre temporel non respecte | Donnees futures dans l'ensemble d'entrainement |
Solution
# ❌ WRONG: Scaling before split (data leakage!)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X) # fits on ALL data
X_train, X_test = train_test_split(X_scaled, ...)
# ✅ CORRECT: Scaling after split
X_train, X_test = train_test_split(X, ...)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) # fit ONLY on train
X_test_scaled = scaler.transform(X_test) # transform only
# ✅ BEST: Use Pipeline (automatically prevents leakage)
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
('scaler', StandardScaler()),
('clf', RandomForestClassifier())
])
pipeline.fit(X_train, y_train) # scaler sees only training data
Elle produit des metriques faussement excellentes qui ne se reproduisent jamais en production. Verifiez toujours : mon modele a-t-il vu, directement ou indirectement, des donnees de l'ensemble de test pendant l'entrainement ?
Prevention
- Toujours utiliser des Pipelines — ils s'assurent que le pretraitement ne voit que les donnees d'entrainement
- Verifier les correlations — une feature correlee a >0.95 avec la cible est suspecte
- Inspecter les doublons — s'assurer qu'aucun echantillon n'apparait dans plusieurs decoupages
- Respecter l'ordre temporel pour les donnees de series temporelles
5. Erreurs de serialisation
5a. Pickle/Joblib — Objets personnalises non serialisables
Symptome
PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
Cause
Le pipeline contient des fonctions lambda, des closures ou des objets non serialisables.
Solution
# ❌ WRONG: Lambda in pipeline
from sklearn.preprocessing import FunctionTransformer
transformer = FunctionTransformer(lambda x: x ** 2) # lambdas can't pickle
# ✅ CORRECT: Named function
def square_transform(x):
return x ** 2
transformer = FunctionTransformer(square_transform)
5b. Echec de conversion ONNX
Symptome
RuntimeError: Unable to find a shape calculator for type 'MyCustomTransformer'
Cause
ONNX ne supporte pas tous les transformers/estimateurs scikit-learn.
Solution
# Check supported converters
from skl2onnx import supported_converters
print("Supported models:")
for name in sorted(supported_converters()):
print(f" - {name}")
# Workaround: serialize pipeline in parts
# Save the custom preprocessing separately
joblib.dump(custom_preprocessor, 'preprocessor.joblib')
# Convert only the model to ONNX
from skl2onnx import convert_sklearn
onnx_model = convert_sklearn(sklearn_model, initial_types=initial_type)
Les modeles scikit-learn les plus courants (LinearRegression, LogisticRegression, RandomForest, SVM, KNN, GradientBoosting) sont supportes. Consultez la liste des convertisseurs avant de planifier une conversion.
5c. Le modele charge donne des resultats differents
Symptome
Original model accuracy: 0.9500
Loaded model accuracy: 0.8700 ← Different !
Cause
- Le pretraitement n'a pas ete serialise avec le modele
- Les donnees de test ont ete transformees differemment
- La version de scikit-learn est differente
Solution
# ✅ Always serialize the COMPLETE pipeline
full_pipeline = Pipeline([
('scaler', StandardScaler()),
('clf', RandomForestClassifier())
])
full_pipeline.fit(X_train, y_train)
# Save
joblib.dump(full_pipeline, 'full_pipeline.joblib')
# Load and verify immediately
loaded = joblib.load('full_pipeline.joblib')
original_pred = full_pipeline.predict(X_test[:5])
loaded_pred = loaded.predict(X_test[:5])
assert np.array_equal(original_pred, loaded_pred), "Mismatch detected!"
6. Problemes de memoire avec les grands modeles
Symptome
MemoryError: Unable to allocate 2.50 GiB for an array
ou le processus est tue par l'OS (killed/OOM).
Cause
Le modele est trop grand pour la memoire disponible. Les Random Forest avec beaucoup d'arbres profonds sont les plus gourmands en memoire.
Solution
# Solution 1: Reduce model size
model = RandomForestClassifier(
n_estimators=50, # fewer trees
max_depth=10, # limit depth
max_leaf_nodes=100, # limit leaves
)
# Solution 2: Use compression when saving
joblib.dump(model, 'model.joblib', compress=9) # max compression
# Solution 3: Use memory-mapped loading for large files
model = joblib.load('model.joblib', mmap_mode='r')
# Solution 4: Monitor memory usage
import sys
model_size_mb = sys.getsizeof(model) / (1024 * 1024)
print(f"Model in memory: {model_size_mb:.1f} MB")
| Parametre | Effet sur la memoire | Compromis |
|---|---|---|
n_estimators ↓ | ⬇️ Reduit | Moins de performance |
max_depth ↓ | ⬇️ Reduit significativement | Risque de sous-apprentissage |
max_leaf_nodes ↓ | ⬇️ Reduit | Moins de precision |
compress=9 | Fichier plus petit sur disque | Chargement plus lent |
mmap_mode='r' | Chargement paresseux | Acces plus lent |
7. Mauvaises metriques malgre un pipeline correct
Symptome
Toutes les etapes semblent correctes, mais les metriques sont mauvaises.
Diagnostic
# Step 1: Check data quality
print(f"Missing values:\n{X_train.isnull().sum()[X_train.isnull().sum() > 0]}")
print(f"\nDuplicate rows: {X_train.duplicated().sum()}")
print(f"\nClass distribution:\n{y_train.value_counts(normalize=True)}")
# Step 2: Check feature relevance
from sklearn.feature_selection import mutual_info_classif
mi = mutual_info_classif(X_train_scaled, y_train)
feature_importance = pd.Series(mi, index=X_train.columns).sort_values(ascending=False)
print(f"\nTop 10 features by mutual info:")
print(feature_importance.head(10))
# Step 3: Verify no constant/quasi-constant features
low_variance = X_train.columns[X_train.std() < 0.01]
if len(low_variance) > 0:
print(f"\n⚠️ Low variance features: {list(low_variance)}")
# Step 4: Try a simple baseline
from sklearn.dummy import DummyClassifier
dummy = DummyClassifier(strategy='most_frequent')
dummy.fit(X_train, y_train)
print(f"\nBaseline (majority class): {dummy.score(X_test, y_test):.4f}")
print(f"Your model: {model.score(X_test, y_test):.4f}")
Si votre modele ne bat pas significativement un DummyClassifier, le probleme est dans les donnees, pas dans le modele. Concentrez-vous sur l'ingenierie des features et la qualite des donnees.
8. Avertissements de convergence
Symptome
ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Cause
L'algorithme d'optimisation n'a pas converge dans le nombre d'iterations alloue.
Solution
# Solution 1: Increase max iterations
model = LogisticRegression(max_iter=1000) # default is 100
# Solution 2: Scale your features
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
# Solution 3: Try a different solver
model = LogisticRegression(solver='saga', max_iter=500)
# Solution 4: Reduce regularization strength
model = LogisticRegression(C=10.0, max_iter=500)
Les messages ConvergenceWarning ne sont pas de simples avertissements — un modele qui n'a pas converge produit des resultats non fiables. Corrigez toujours cet avertissement avant de continuer.
9. Reference rapide — Tableau de diagnostic
| Symptome | Cause probable | Action immediate |
|---|---|---|
ModuleNotFoundError au chargement | Incompatibilite de version | Verifier la version sklearn dans les metadonnees |
| Score train >> Score test | Surapprentissage | Reduire la complexite, ajouter de la regularisation |
| Les deux scores train et test bas | Sous-apprentissage | Modele plus complexe, plus de features |
| Scores CV irrealistes (>0.99) | Fuite de donnees | Verifier le pipeline, l'ordre des operations |
PicklingError | Objet non serialisable | Remplacer les lambdas par des fonctions nommees |
| Erreur de conversion ONNX | Transformer non supporte | Verifier la liste de compatibilite |
MemoryError | Modele trop grand | Reduire n_estimators, max_depth |
ConvergenceWarning | Pas assez d'iterations | Augmenter max_iter, mettre a l'echelle les features |
| Scores ne battent pas la ligne de base | Donnees inadequates | Ingenierie des features, verifier la qualite des donnees |
| Precision du modele charge differente | Pipeline incomplet | Serialiser le pipeline complet |
Checklist de debogage
Quand vous rencontrez un probleme, suivez cette checklist systematiquement :
- ☐ Verifier les versions des dependances (Python, sklearn, numpy)
- ☐ Verifier les fuites de donnees (mise a l'echelle apres le decoupage ?)
- ☐ Comparer les scores train vs test (ecart < 10% ?)
- ☐ Verifier les scores de validation croisee (ecart-type raisonnable ?)
- ☐ Tester un DummyClassifier comme ligne de base
- ☐ Verifier les valeurs manquantes et les doublons
- ☐ Verifier que le pipeline complet est serialise
- ☐ Tester le chargement et la prediction du modele serialise
- ☐ Verifier les avertissements dans les logs (ConvergenceWarning, etc.)
- ☐ Reproduire le probleme dans un environnement propre