ASI-Engineer commited on
Commit
1d67466
·
verified ·
1 Parent(s): 814e2ba

Upload folder using huggingface_hub

Browse files
Files changed (6) hide show
  1. .flake8 +8 -3
  2. .gitignore +7 -0
  3. README.md +38 -48
  4. app.py +174 -5
  5. main.py +176 -0
  6. requirements.txt +16 -38
.flake8 CHANGED
@@ -1,5 +1,6 @@
1
  [flake8]
2
  # Exclude dirs pour ignorer libs tierces et noise (venv, git, etc.)
 
3
  exclude =
4
  .venv,
5
  .git,
@@ -9,8 +10,12 @@ exclude =
9
  .cache,
10
  .eggs,
11
  build,
12
- dist
 
13
  # Max line pour compat Black (default 88 vs PEP8 79)
14
  max-line-length = 88
15
- # Ignore E501 si trop strict (optionnel, retire si tu veux fixer lines)
16
- ignore = E501
 
 
 
 
1
  [flake8]
2
  # Exclude dirs pour ignorer libs tierces et noise (venv, git, etc.)
3
+ ignore = W503, E501
4
  exclude =
5
  .venv,
6
  .git,
 
10
  .cache,
11
  .eggs,
12
  build,
13
+ dist,
14
+ mlruns
15
  # Max line pour compat Black (default 88 vs PEP8 79)
16
  max-line-length = 88
17
+
18
+ # Ignorer certains warnings pour les scripts d'exemple (non-critique)
19
+ per-file-ignores =
20
+ examples/*.py:F541,E722,F841
21
+ tests/test_mlflow_*.py:F401,E402,F811,F541
.gitignore CHANGED
@@ -35,3 +35,10 @@ Thumbs.db
35
  secrets.json
36
  data/raw/ # Pour datasets volumineux en data science (OC_P5)
37
  notebooks/*.ipynb_checkpoints/
 
 
 
 
 
 
 
 
35
  secrets.json
36
  data/raw/ # Pour datasets volumineux en data science (OC_P5)
37
  notebooks/*.ipynb_checkpoints/
38
+
39
+ # MLflow (logs seulement, garder DB et runs pour déploiement HF)
40
+ mlflow.db-shm
41
+ mlflow.db-wal
42
+ mlflow_ui.log
43
+ mlflow_comparison.png
44
+ nohup.out
README.md CHANGED
@@ -1,67 +1,57 @@
1
  ---
2
  title: OC P5 - API ML Déployée
3
- emoji: 🚀
4
  colorFrom: blue
5
- colorTo: purple
6
- sdk: static
 
7
  app_file: app.py
8
  pinned: false
 
9
  ---
10
 
11
- # ML Deployment Project
12
- Déploiement d'un modèle ML pour Futurisys : API FastAPI, PostgreSQL, tests Pytest, CI/CD.
13
 
14
- ## Aperçu
15
- POC pour exposer un modèle ML via API performante, avec traçabilité DB et bonnes pratiques DevOps.
16
 
17
- ## Installation
18
- 1. Clone le repo : `git clone https://github.com/ton-username/ml-deployment-project.git`
19
- 2. Installe Poetry (si pas fait) : `curl -sSL https://install.python-poetry.org | python3 -`
20
- 3. Dépendances : `poetry install` (crée/lock .venv avec deps)
21
- 4. Active env : `poetry shell`
22
 
23
- ## Utilisation
24
- - Dev : `poetry run uvicorn src.main:app --reload` (Étape 3 pour API).
25
- - BDD : `poetry run python scripts/create_db.py` (Étape 4).
26
- - Tests : `poetry run pytest` (Étape 5).
 
27
 
28
- ## Structure du Projet
29
- - `src/` : Code core (API, modèle ML).
30
- - `tests/` : Tests unitaires/fonctionnels (Pytest).
31
- - `docs/` : Schémas UML, docs API.
32
- - `scripts/` : Utils init (BDD, data load).
33
- - `data/` : Datasets (ignorés pour privacy).
34
 
35
- ## CI/CD Optimization
36
- - Pipelines configurés pour exécution <10 min (ex. : lint ~1 min, tests ~3 min, deploy ~2 min). Si >10 min, optimiser via cache Poetry ou jobs parallèles. Temps observés basés sur runs GitHub Actions.
 
37
 
38
- ## CI/CD Détails
39
- - Pipeline : GitHub Actions pour lint (Flake8/Black), tests (Pytest), deploy HF.
40
- - Environnements : Dev (branch dev/local tests), Prod (branch main/HF oc_p5).
41
- - Secrets : HF_TOKEN sécurisé via GitHub Secrets.
42
- - Standards : Voir [docs/standards.md](./docs/standards.md).
43
 
44
- ## Environnements CI/CD
45
- - Dev : Branch "dev" -> HF space oc_p5-dev pour tests itératifs et validation.
46
- - Prod : Branch "main" -> HF space oc_p5 pour déploiement stable.
47
- - Secrets : HF_TOKEN partagé (sécurisé via GitHub Secrets) pour dev/prod.
 
 
 
 
48
 
49
- ## Branches & Conventions
50
- - `main` : Stable (merges via PR).
51
- - `main` : pour développement et tests
52
- - `feature/etapeX` : Fonctionnalités (kebab-case, ex. `feature/etape3-api`).
53
- - Commits : Conventional (ex. `feat: Add endpoint`).
54
 
55
- ## Déploiement & Sécurité
56
- - Auth/Sec : À venir (JWT pour API, secrets en .env ignoré).
57
- - Versions : Tags semver (ex. v1.0.0 pour Étape 1).
 
58
 
59
- ## HF Spaces
60
- - Prod : https://huggingface.co/spaces/ASI-Engineer/oc_p5 (branch dev, pour tests itératifs).
61
- - Sync auto via GitHub Actions (push déclenche rebuild ~2min, avec HF_TOKEN sécurisé).
62
 
63
- ## Documentation
64
- - Standards Code/ML
 
65
 
66
- ## Licence
67
- MIT (ou adapte pour Futurisys).
 
 
1
  ---
2
  title: OC P5 - API ML Déployée
3
+ emoji: 🎯
4
  colorFrom: blue
5
+ colorTo: green
6
+ sdk: gradio
7
+ sdk_version: 5.9.1
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
  ---
12
 
13
+ # 🎯 Employee Turnover Prediction - DEV Environment
 
14
 
15
+ Interface Gradio pour tester le modèle de prédiction de départ des employés (turnover).
 
16
 
17
+ ## 🚀 Modèle ML
 
 
 
 
18
 
19
+ - **Algorithme**: XGBoost optimisé avec RandomizedSearchCV
20
+ - **Équilibrage**: SMOTE pour gérer le déséquilibre de classes (ratio 5:1)
21
+ - **Tracking**: MLflow pour versioning et reproductibilité
22
+ - **Métriques**: F1-Score optimisé (0.51), Accuracy 79%
23
+ - **Stockage**: [Hugging Face Hub](https://huggingface.co/ASI-Engineer/employee-turnover-model)
24
 
25
+ ## 📊 Fonctionnalités
 
 
 
 
 
26
 
27
+ - **Status Checker**: Vérifier l'état du modèle et les métriques
28
+ - **API Simple**: Interface Gradio pour tests rapides
29
+ - **Chargement automatique**: Modèle téléchargé depuis HF Hub au démarrage
30
 
31
+ ## 🔧 Architecture
 
 
 
 
32
 
33
+ ```python
34
+ # Chargement du modèle depuis HF Hub
35
+ model_path = hf_hub_download(
36
+ repo_id="ASI-Engineer/employee-turnover-model",
37
+ filename="model/model.pkl"
38
+ )
39
+ model = mlflow.sklearn.load_model(str(Path(model_path).parent))
40
+ ```
41
 
42
+ ## 📈 Métriques
 
 
 
 
43
 
44
+ - **F1-Score**: 0.5136
45
+ - **Accuracy**: 79%
46
+ - **Données**: 1470 échantillons, 50 features
47
+ - **Classes**: {0: 1233, 1: 237} - Ratio 5.20:1
48
 
49
+ ## 🔗 Liens
 
 
50
 
51
+ - **Modèle**: [employee-turnover-model](https://huggingface.co/ASI-Engineer/employee-turnover-model)
52
+ - **GitHub**: [OC_P5](https://github.com/chaton59/OC_P5)
53
+ - **CI/CD**: GitHub Actions avec déploiement automatique
54
 
55
+ Ce Space est synchronisé automatiquement via CI/CD depuis la branche `dev` du repository GitHub.
56
+
57
+ **Repository**: [chaton59/OC_P5](https://github.com/chaton59/OC_P5)
app.py CHANGED
@@ -1,8 +1,177 @@
1
- from fastapi import FastAPI
 
 
2
 
3
- app = FastAPI()
 
 
 
 
 
 
4
 
 
 
 
5
 
6
- @app.get("/")
7
- def root():
8
- return {"status": "Hello World"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Interface Gradio pour tester le modèle Employee Turnover en production.
4
 
5
+ Déploiement sur Hugging Face Spaces pour tests rapides.
6
+ Version de démonstration - Interface complète en développement.
7
+ """
8
+ import gradio as gr
9
+ import mlflow
10
+ import mlflow.pyfunc
11
+ from huggingface_hub import hf_hub_download
12
 
13
+ # Configuration
14
+ HF_MODEL_REPO = "ASI-Engineer/employee-turnover-model"
15
+ FALLBACK_RUN_ID = "40e43c8e425345bab3d19f27eb8fe5d8"
16
 
17
+
18
+ def load_model():
19
+ """
20
+ Charge le modèle depuis Hugging Face Hub (prod) ou MLflow local (dev).
21
+
22
+ Ordre de priorité:
23
+ 1. HF Hub avec pickle direct (modèle déployé en production)
24
+ 2. MLflow local (développement local)
25
+ """
26
+ # Essayer HF Hub en premier (production) - charger directement le pickle
27
+ try:
28
+ import joblib
29
+
30
+ # Download model pickle from HF Hub
31
+ model_path = hf_hub_download(
32
+ repo_id=HF_MODEL_REPO, filename="model/model.pkl", repo_type="model"
33
+ )
34
+ model = joblib.load(model_path)
35
+ print(f"✅ Modèle chargé depuis HF Hub: {HF_MODEL_REPO}")
36
+ return model, "HF Hub"
37
+ except Exception as e:
38
+ print(f"⚠️ HF Hub non disponible: {e}")
39
+
40
+ # Fallback: MLflow local (développement uniquement)
41
+ try:
42
+ mlflow.set_tracking_uri("sqlite:///mlflow.db")
43
+ # Essayer Model Registry d'abord
44
+ model = mlflow.pyfunc.load_model("models:/XGBoost_Employee_Turnover/latest") # type: ignore[attr-defined]
45
+ print("✅ Modèle chargé depuis MLflow Model Registry")
46
+ return model, "MLflow Registry"
47
+ except Exception:
48
+ try:
49
+ # Fallback sur run ID
50
+ model = mlflow.pyfunc.load_model(f"runs:/{FALLBACK_RUN_ID}/model") # type: ignore[attr-defined]
51
+ print(f"✅ Modèle chargé depuis MLflow run: {FALLBACK_RUN_ID}")
52
+ return model, "MLflow Local"
53
+ except Exception as e2:
54
+ print(f"❌ Erreur chargement MLflow: {e2}")
55
+ return None, "Error"
56
+
57
+
58
+ # Charger le modèle au démarrage
59
+ try:
60
+ model, model_source = load_model()
61
+ MODEL_LOADED = model is not None
62
+ except Exception as e:
63
+ print(f"❌ Erreur lors du chargement du modèle: {e}")
64
+ MODEL_LOADED = False
65
+ model = None
66
+ model_source = "Error"
67
+
68
+
69
+ def get_model_info():
70
+ """Retourne les informations sur le modèle."""
71
+ if not MODEL_LOADED:
72
+ return {
73
+ "status": "❌ Modèle non disponible",
74
+ "error": "Le modèle n'a pas pu être chargé",
75
+ "solution": "Vérifiez que le modèle est bien enregistré sur HF Hub ou entraîné localement",
76
+ }
77
+
78
+ try:
79
+ info = {
80
+ "status": "✅ Modèle chargé avec succès",
81
+ "source": model_source,
82
+ "model_type": type(model).__name__,
83
+ "features": "~50 features (après preprocessing)",
84
+ "algorithme": "XGBoost + SMOTE",
85
+ "hf_hub_repo": HF_MODEL_REPO if model_source == "HF Hub" else "N/A",
86
+ }
87
+
88
+ # Si MLflow local, ajouter les métriques
89
+ if model_source == "MLflow Local":
90
+ mlflow.set_tracking_uri("sqlite:///mlflow.db")
91
+ client = mlflow.MlflowClient()
92
+ runs = client.search_runs(
93
+ experiment_ids=["1"], order_by=["start_time DESC"], max_results=1
94
+ )
95
+ if runs:
96
+ run = runs[0]
97
+ metrics = run.data.metrics
98
+ info.update(
99
+ {
100
+ "run_id": run.info.run_id[:8],
101
+ "f1_score": f"{metrics.get('f1_score', 0):.4f}",
102
+ "accuracy": f"{metrics.get('accuracy', 0):.4f}",
103
+ }
104
+ )
105
+
106
+ info["info"] = "Interface de prédiction en développement - API FastAPI à venir"
107
+ return info
108
+
109
+ except Exception as e:
110
+ return {"status": "✅ Modèle chargé (info limitées)", "error": str(e)}
111
+
112
+
113
+ # Interface Gradio
114
+ with gr.Blocks( # type: ignore[attr-defined]
115
+ title="Employee Turnover Prediction - DEV", theme=gr.themes.Soft() # type: ignore[attr-defined]
116
+ ) as demo:
117
+ gr.Markdown("# 🎯 Prédiction du Turnover - Employee Attrition") # type: ignore[attr-defined]
118
+ gr.Markdown("## Environment DEV - Test de déploiement CI/CD") # type: ignore[attr-defined]
119
+
120
+ gr.Markdown( # type: ignore[attr-defined]
121
+ """
122
+ ### 📊 Statut du projet
123
+
124
+ Ce Space est synchronisé automatiquement depuis GitHub (branche `dev`).
125
+
126
+ **Actuellement disponible :**
127
+ - ✅ Pipeline d'entraînement MLflow complet (`main.py`)
128
+ - ✅ Déploiement automatique CI/CD (GitHub Actions → HF Spaces)
129
+ - ✅ Tests unitaires et linting automatisés
130
+
131
+ **En développement :**
132
+ - 🚧 Interface de prédiction interactive
133
+ - 🚧 API FastAPI avec endpoints de prédiction
134
+ - 🚧 Intégration PostgreSQL pour tracking des prédictions
135
+ """
136
+ )
137
+
138
+ with gr.Row(): # type: ignore[attr-defined]
139
+ with gr.Column(): # type: ignore[attr-defined]
140
+ gr.Markdown("### 🔍 Informations sur le modèle") # type: ignore[attr-defined]
141
+ check_btn = gr.Button("📊 Vérifier le statut du modèle", variant="primary") # type: ignore[attr-defined]
142
+
143
+ with gr.Column(): # type: ignore[attr-defined]
144
+ model_output = gr.JSON(label="Statut") # type: ignore[attr-defined]
145
+
146
+ check_btn.click(fn=get_model_info, inputs=[], outputs=model_output)
147
+
148
+ gr.Markdown("---") # type: ignore[attr-defined]
149
+
150
+ gr.Markdown( # type: ignore[attr-defined]
151
+ """
152
+ ### 🛠️ Prochaines étapes (selon etapes.txt)
153
+
154
+ 1. **Étape 3** : Développement API FastAPI
155
+ - Endpoints de prédiction avec validation Pydantic
156
+ - Chargement dynamique des preprocessing artifacts (scaler, encoders)
157
+ - Documentation Swagger/OpenAPI automatique
158
+
159
+ 2. **Étape 4** : Intégration PostgreSQL
160
+ - Stockage des inputs/outputs des prédictions
161
+ - Traçabilité complète des requêtes
162
+
163
+ 3. **Étape 5** : Tests unitaires et fonctionnels
164
+ - Tests des endpoints API
165
+ - Tests de charge et performance
166
+ - Couverture de code avec pytest-cov
167
+
168
+ ### 📚 Documentation
169
+ - **Repository GitHub** : [chaton59/OC_P5](https://github.com/chaton59/OC_P5)
170
+ - **MLflow Tracking** : Disponible en local (`./scripts/start_mlflow.sh`)
171
+ - **Métriques** : F1-Score optimisé, gestion classes déséquilibrées (SMOTE)
172
+ """
173
+ )
174
+
175
+
176
+ if __name__ == "__main__":
177
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
main.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Pipeline principal d'entraînement du modèle Employee Turnover.
4
+
5
+ Ce script enchaîne:
6
+ 1. Chargement et préprocessing des données
7
+ 2. Entraînement du modèle XGBoost avec RandomizedSearchCV et SMOTE
8
+ 3. Logging des résultats dans MLflow (params, metrics, artifacts, model)
9
+ 4. Sauvegarde des encoders et scaler pour utilisation future
10
+
11
+ Usage:
12
+ python main.py
13
+
14
+ Le modèle et les artifacts sont enregistrés dans MLflow pour:
15
+ - Suivi des expérimentations
16
+ - Reproductibilité
17
+ Déploiement via Model Registry
18
+ """
19
+ from pathlib import Path
20
+
21
+ import joblib
22
+ import mlflow
23
+ import mlflow.sklearn
24
+
25
+ from ml_model.preprocess import preprocess_data
26
+ from ml_model.train_model import train_model
27
+
28
+
29
+ def main():
30
+ """Pipeline principal d'entraînement."""
31
+ print("=" * 80)
32
+ print("🚀 PIPELINE D'ENTRAÎNEMENT - Employee Turnover Prediction")
33
+ print("=" * 80)
34
+ print()
35
+
36
+ # Configuration MLflow
37
+ mlflow.set_tracking_uri("sqlite:///mlflow.db")
38
+ mlflow.set_experiment("Employee_Turnover_Training")
39
+
40
+ print("📊 Configuration MLflow:")
41
+ print(f" Tracking URI: {mlflow.get_tracking_uri()}")
42
+ print(" Experiment: Employee_Turnover_Training")
43
+ print()
44
+
45
+ # Chemins des données
46
+ data_paths = {
47
+ "sondage_path": "data/extrait_sondage.csv",
48
+ "eval_path": "data/extrait_eval.csv",
49
+ "sirh_path": "data/extrait_sirh.csv",
50
+ }
51
+
52
+ # Vérifier que les fichiers existent
53
+ for name, path in data_paths.items():
54
+ if not Path(path).exists():
55
+ raise FileNotFoundError(f"❌ Fichier manquant: {path}")
56
+
57
+ print("✅ Fichiers de données trouvés")
58
+ print()
59
+
60
+ # ========================================================================
61
+ # ÉTAPE 1 : Préprocessing
62
+ # ========================================================================
63
+ print("1️⃣ PRÉPROCESSING")
64
+ print("-" * 80)
65
+
66
+ X, y, scaler, onehot_encoder, ordinal_encoder = preprocess_data(data_paths)
67
+
68
+ print(f" Forme X: {X.shape}")
69
+ print(f" Forme y: {y.shape}")
70
+ print(f" Classes: {y.value_counts().to_dict()}")
71
+ print(f" Ratio déséquilibre: {(y == 0).sum() / (y == 1).sum():.2f}:1")
72
+ print()
73
+
74
+ # ========================================================================
75
+ # ÉTAPE 2 : Entraînement avec MLflow tracking
76
+ # ========================================================================
77
+ print("2️⃣ ENTRAÎNEMENT")
78
+ print("-" * 80)
79
+
80
+ # Entraînement (déjà avec MLflow tracking dans train_model.py)
81
+ model, best_params, cv_f1 = train_model(X, y)
82
+
83
+ print(" ✅ Modèle entraîné")
84
+ print(f" 🏆 Meilleur F1 CV: {cv_f1:.4f}")
85
+ print()
86
+
87
+ # Récupérer le run actif pour sauvegarder les artifacts
88
+ active_run = mlflow.active_run()
89
+ if active_run is None:
90
+ # Si train_model a fermé le run, on en ouvre un nouveau
91
+ active_run = mlflow.start_run()
92
+ run_id = active_run.info.run_id
93
+ should_end_run = True
94
+ else:
95
+ run_id = active_run.info.run_id
96
+ should_end_run = False
97
+
98
+ # Log des infos dataset
99
+ mlflow.log_param("n_samples", len(X))
100
+ mlflow.log_param("n_features", X.shape[1])
101
+ mlflow.log_param("class_ratio", f"{(y == 0).sum()}:{(y == 1).sum()}")
102
+
103
+ # ========================================================================
104
+ # ÉTAPE 3 : Sauvegarde des artifacts (encoders, scaler)
105
+ # ========================================================================
106
+ print("3️⃣ SAUVEGARDE DES ARTIFACTS")
107
+ print("-" * 80)
108
+
109
+ # Créer dossier temporaire pour artifacts
110
+ artifacts_dir = Path("artifacts_temp")
111
+ artifacts_dir.mkdir(exist_ok=True)
112
+
113
+ # Sauvegarder scaler
114
+ scaler_path = artifacts_dir / "scaler.joblib"
115
+ joblib.dump(scaler, scaler_path)
116
+ mlflow.log_artifact(str(scaler_path), artifact_path="preprocessing")
117
+ print(" ✅ Scaler sauvegardé")
118
+
119
+ # Sauvegarder encoders (onehot et ordinal)
120
+ onehot_path = artifacts_dir / "onehot_encoder.joblib"
121
+ joblib.dump(onehot_encoder, onehot_path)
122
+ mlflow.log_artifact(str(onehot_path), artifact_path="preprocessing")
123
+
124
+ ordinal_path = artifacts_dir / "ordinal_encoder.joblib"
125
+ joblib.dump(ordinal_encoder, ordinal_path)
126
+ mlflow.log_artifact(str(ordinal_path), artifact_path="preprocessing")
127
+ print(" ✅ Encoders sauvegardés (OneHot + Ordinal)")
128
+
129
+ # Log git commit si disponible
130
+ try:
131
+ import subprocess
132
+
133
+ git_commit = (
134
+ subprocess.check_output(["git", "rev-parse", "HEAD"])
135
+ .strip()
136
+ .decode("utf-8")
137
+ )
138
+ mlflow.set_tag("git_commit", git_commit[:8])
139
+ print(f" ✅ Git commit: {git_commit[:8]}")
140
+ except Exception:
141
+ pass
142
+
143
+ # Nettoyer artifacts temporaires
144
+ scaler_path.unlink()
145
+ onehot_path.unlink()
146
+ ordinal_path.unlink()
147
+ artifacts_dir.rmdir()
148
+
149
+ print()
150
+
151
+ # Fermer le run si on l'a ouvert
152
+ if should_end_run:
153
+ mlflow.end_run()
154
+
155
+ # ========================================================================
156
+ # RÉSUMÉ
157
+ # ========================================================================
158
+ print("=" * 80)
159
+ print("✅ ENTRAÎNEMENT TERMINÉ")
160
+ print("=" * 80)
161
+ print()
162
+ print(f"📊 Run ID: {run_id}")
163
+ print(f"🎯 F1 Score (CV): {cv_f1:.4f}")
164
+ print("📦 Artifacts sauvegardés dans MLflow")
165
+ print()
166
+ print("🌐 Pour visualiser les résultats:")
167
+ print(" ./scripts/start_mlflow.sh")
168
+ print(" ou: mlflow ui --backend-store-uri sqlite:///mlflow.db")
169
+ print()
170
+ print("📝 Pour charger le modèle:")
171
+ print(f" model = mlflow.sklearn.load_model('runs:/{run_id}/model')")
172
+ print()
173
+
174
+
175
+ if __name__ == "__main__":
176
+ main()
requirements.txt CHANGED
@@ -1,38 +1,16 @@
1
- annotated-doc==0.0.4 ; python_version >= "3.12"
2
- annotated-types==0.7.0 ; python_version >= "3.12"
3
- anyio==4.12.0 ; python_version >= "3.12"
4
- black==25.11.0 ; python_version >= "3.12"
5
- click==8.3.1 ; python_version >= "3.12"
6
- colorama==0.4.6 ; (platform_system == "Windows" or sys_platform == "win32") and python_version >= "3.12"
7
- coverage==7.12.0 ; python_version >= "3.12"
8
- fastapi==0.123.4 ; python_version >= "3.12"
9
- flake8==7.3.0 ; python_version >= "3.12"
10
- greenlet==3.2.4 ; python_version >= "3.12" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32")
11
- h11==0.16.0 ; python_version >= "3.12"
12
- httptools==0.7.1 ; python_version >= "3.12"
13
- idna==3.11 ; python_version >= "3.12"
14
- iniconfig==2.3.0 ; python_version >= "3.12"
15
- mccabe==0.7.0 ; python_version >= "3.12"
16
- mypy-extensions==1.1.0 ; python_version >= "3.12"
17
- packaging==25.0 ; python_version >= "3.12"
18
- pathspec==0.12.1 ; python_version >= "3.12"
19
- platformdirs==4.5.0 ; python_version >= "3.12"
20
- pluggy==1.6.0 ; python_version >= "3.12"
21
- pycodestyle==2.14.0 ; python_version >= "3.12"
22
- pydantic-core==2.41.5 ; python_version >= "3.12"
23
- pydantic==2.12.5 ; python_version >= "3.12"
24
- pyflakes==3.4.0 ; python_version >= "3.12"
25
- pygments==2.19.2 ; python_version >= "3.12"
26
- pytest-cov==7.0.0 ; python_version >= "3.12"
27
- pytest==9.0.1 ; python_version >= "3.12"
28
- python-dotenv==1.2.1 ; python_version >= "3.12"
29
- pytokens==0.3.0 ; python_version >= "3.12"
30
- pyyaml==6.0.3 ; python_version >= "3.12"
31
- sqlalchemy==2.0.44 ; python_version >= "3.12"
32
- starlette==0.50.0 ; python_version >= "3.12"
33
- typing-extensions==4.15.0 ; python_version >= "3.12"
34
- typing-inspection==0.4.2 ; python_version >= "3.12"
35
- uvicorn==0.38.0 ; python_version >= "3.12"
36
- uvloop==0.22.1 ; sys_platform != "win32" and sys_platform != "cygwin" and platform_python_implementation != "PyPy" and python_version >= "3.12"
37
- watchfiles==1.1.1 ; python_version >= "3.12"
38
- websockets==15.0.1 ; python_version >= "3.12"
 
1
+ # Core dependencies
2
+ black==25.11.0
3
+ flake8==7.3.0
4
+ pytest==9.0.1
5
+ pytest-cov==7.0.0
6
+
7
+ # ML dependencies
8
+ scikit-learn==1.6.1
9
+ xgboost==2.1.4
10
+ imbalanced-learn==0.13.0
11
+ scipy==1.14.1
12
+ numpy==2.0.2
13
+ pandas==2.2.3
14
+ joblib==1.4.2
15
+ mlflow==3.8.0
16
+ gradio==5.9.1