Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| Pipeline principal d'entraînement du modèle Employee Turnover. | |
| Ce script enchaîne: | |
| 1. Chargement et préprocessing des données | |
| 2. Entraînement du modèle XGBoost avec RandomizedSearchCV et SMOTE | |
| 3. Logging des résultats dans MLflow (params, metrics, artifacts, model) | |
| 4. Sauvegarde des encoders et scaler pour utilisation future | |
| Usage: | |
| python main.py | |
| Le modèle et les artifacts sont enregistrés dans MLflow pour: | |
| - Suivi des expérimentations | |
| - Reproductibilité | |
| Déploiement via Model Registry | |
| """ | |
| from pathlib import Path | |
| import joblib | |
| import mlflow | |
| import mlflow.sklearn | |
| from ml_model.preprocess import preprocess_data | |
| from ml_model.train_model import train_model | |
| def main(): | |
| """Pipeline principal d'entraînement.""" | |
| print("=" * 80) | |
| print("🚀 PIPELINE D'ENTRAÎNEMENT - Employee Turnover Prediction") | |
| print("=" * 80) | |
| print() | |
| # Configuration MLflow | |
| mlflow.set_tracking_uri("sqlite:///mlflow.db") | |
| mlflow.set_experiment("Employee_Turnover_Training") | |
| print("📊 Configuration MLflow:") | |
| print(f" Tracking URI: {mlflow.get_tracking_uri()}") | |
| print(" Experiment: Employee_Turnover_Training") | |
| print() | |
| # Chemins des données | |
| data_paths = { | |
| "sondage_path": "data/extrait_sondage.csv", | |
| "eval_path": "data/extrait_eval.csv", | |
| "sirh_path": "data/extrait_sirh.csv", | |
| } | |
| # Vérifier que les fichiers existent | |
| for name, path in data_paths.items(): | |
| if not Path(path).exists(): | |
| raise FileNotFoundError(f"❌ Fichier manquant: {path}") | |
| print("✅ Fichiers de données trouvés") | |
| print() | |
| # ======================================================================== | |
| # ÉTAPE 1 : Préprocessing | |
| # ======================================================================== | |
| print("1️⃣ PRÉPROCESSING") | |
| print("-" * 80) | |
| X, y, scaler, onehot_encoder, ordinal_encoder = preprocess_data(data_paths) | |
| print(f" Forme X: {X.shape}") | |
| print(f" Forme y: {y.shape}") | |
| print(f" Classes: {y.value_counts().to_dict()}") | |
| print(f" Ratio déséquilibre: {(y == 0).sum() / (y == 1).sum():.2f}:1") | |
| print() | |
| # ======================================================================== | |
| # ÉTAPE 2 : Entraînement avec MLflow tracking | |
| # ======================================================================== | |
| print("2️⃣ ENTRAÎNEMENT") | |
| print("-" * 80) | |
| # Entraînement (déjà avec MLflow tracking dans train_model.py) | |
| model, best_params, cv_f1 = train_model(X, y) | |
| print(" ✅ Modèle entraîné") | |
| print(f" 🏆 Meilleur F1 CV: {cv_f1:.4f}") | |
| print() | |
| # Récupérer le run actif pour sauvegarder les artifacts | |
| active_run = mlflow.active_run() | |
| if active_run is None: | |
| # Si train_model a fermé le run, on en ouvre un nouveau | |
| active_run = mlflow.start_run() | |
| run_id = active_run.info.run_id | |
| should_end_run = True | |
| else: | |
| run_id = active_run.info.run_id | |
| should_end_run = False | |
| # Log des infos dataset | |
| mlflow.log_param("n_samples", len(X)) | |
| mlflow.log_param("n_features", X.shape[1]) | |
| mlflow.log_param("class_ratio", f"{(y == 0).sum()}:{(y == 1).sum()}") | |
| # ======================================================================== | |
| # ÉTAPE 3 : Sauvegarde des artifacts (encoders, scaler) | |
| # ======================================================================== | |
| print("3️⃣ SAUVEGARDE DES ARTIFACTS") | |
| print("-" * 80) | |
| # Créer dossier temporaire pour artifacts | |
| artifacts_dir = Path("artifacts_temp") | |
| artifacts_dir.mkdir(exist_ok=True) | |
| # Sauvegarder scaler | |
| scaler_path = artifacts_dir / "scaler.joblib" | |
| joblib.dump(scaler, scaler_path) | |
| mlflow.log_artifact(str(scaler_path), artifact_path="preprocessing") | |
| print(" ✅ Scaler sauvegardé") | |
| # Sauvegarder encoders (onehot et ordinal) | |
| onehot_path = artifacts_dir / "onehot_encoder.joblib" | |
| joblib.dump(onehot_encoder, onehot_path) | |
| mlflow.log_artifact(str(onehot_path), artifact_path="preprocessing") | |
| ordinal_path = artifacts_dir / "ordinal_encoder.joblib" | |
| joblib.dump(ordinal_encoder, ordinal_path) | |
| mlflow.log_artifact(str(ordinal_path), artifact_path="preprocessing") | |
| print(" ✅ Encoders sauvegardés (OneHot + Ordinal)") | |
| # Log git commit si disponible | |
| try: | |
| import subprocess | |
| git_commit = ( | |
| subprocess.check_output(["git", "rev-parse", "HEAD"]) | |
| .strip() | |
| .decode("utf-8") | |
| ) | |
| mlflow.set_tag("git_commit", git_commit[:8]) | |
| print(f" ✅ Git commit: {git_commit[:8]}") | |
| except Exception: | |
| pass | |
| # Nettoyer artifacts temporaires | |
| scaler_path.unlink() | |
| onehot_path.unlink() | |
| ordinal_path.unlink() | |
| artifacts_dir.rmdir() | |
| print() | |
| # Fermer le run si on l'a ouvert | |
| if should_end_run: | |
| mlflow.end_run() | |
| # ======================================================================== | |
| # RÉSUMÉ | |
| # ======================================================================== | |
| print("=" * 80) | |
| print("✅ ENTRAÎNEMENT TERMINÉ") | |
| print("=" * 80) | |
| print() | |
| print(f"📊 Run ID: {run_id}") | |
| print(f"🎯 F1 Score (CV): {cv_f1:.4f}") | |
| print("📦 Artifacts sauvegardés dans MLflow") | |
| print() | |
| print("🌐 Pour visualiser les résultats:") | |
| print(" ./scripts/start_mlflow.sh") | |
| print(" ou: mlflow ui --backend-store-uri sqlite:///mlflow.db") | |
| print() | |
| print("📝 Pour charger le modèle:") | |
| print(f" model = mlflow.sklearn.load_model('runs:/{run_id}/model')") | |
| print() | |
| if __name__ == "__main__": | |
| main() | |