Spaces:
Runtime error
Runtime error
Aurélie GABU
commited on
Commit
·
73401e0
1
Parent(s):
77950e7
API: exposition du model ML avec les éléments nécessaires, mise en place tests API, mise à jour readme
Browse files- App/main.py +16 -0
- App/model/mapping_classes.json +1 -0
- App/model/preprocesseur_fitted.joblib +0 -0
- App/model/variables_entree.json +1 -1
- App/predict.py +25 -0
- App/schemas.py +0 -1
- README.md +52 -2
- tests/test_api.py +44 -0
App/main.py
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
from App.schemas import EmployeeFeatures
|
| 3 |
+
from App.predict import predict_employee
|
| 4 |
+
|
| 5 |
+
app = FastAPI(
|
| 6 |
+
title = "Futurisys ML API",
|
| 7 |
+
description = "API de prédiction du départ des employés",
|
| 8 |
+
version="0.1.0"
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
@app.post("/predict")
|
| 12 |
+
def predict(data: EmployeeFeatures):
|
| 13 |
+
"""
|
| 14 |
+
Prédit la probabilité de départ d'un employé à partir de ses caractéristiques.
|
| 15 |
+
"""
|
| 16 |
+
return predict_employee(data.model_dump())
|
App/model/mapping_classes.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"0": "Reste", "1": "Part"}
|
App/model/preprocesseur_fitted.joblib
ADDED
|
Binary file (8.04 kB). View file
|
|
|
App/model/variables_entree.json
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
["genre", "statut_marital", "departement", "poste", "domaine_etude", "frequence_deplacement", "heure_supplementaires", "evolution_cat_evol", "categorie_employe", "
|
|
|
|
| 1 |
+
["genre", "statut_marital", "departement", "poste", "domaine_etude", "frequence_deplacement", "heure_supplementaires", "evolution_cat_evol", "categorie_employe", "satisfaction_employee_nature_travail", "nombre_participation_pee", "ecart_note_evaluation", "revenu_mensuel", "distance_domicile_travail", "satisfaction_globale", "niveau_education", "note_evaluation_actuelle", "satisfaction_employee_equipe", "age", "revenu_par_annee_experience_interne", "satisfaction_employee_equilibre_pro_perso", "nombre_experiences_precedentes", "annees_dans_l_entreprise", "nb_formations_suivies", "revenu_par_annee_experience_totale", "ratio_sans_promotion", "satisfaction_employee_environnement", "exp_hors_entreprise", "mobilite_promotion", "annees_depuis_la_derniere_promotion"]
|
App/predict.py
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import joblib
|
| 2 |
+
import pandas as pd
|
| 3 |
+
from App.schemas import EmployeeFeatures
|
| 4 |
+
import json
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
model = joblib.load("App/model/modele_final_xgb.joblib")
|
| 8 |
+
|
| 9 |
+
FEATURES = list(EmployeeFeatures.model_fields.keys())
|
| 10 |
+
with open("App/model/mapping_classes.json") as f:
|
| 11 |
+
CLASS_MAPPING = json.load(f)
|
| 12 |
+
|
| 13 |
+
def predict_employee(data: dict):
|
| 14 |
+
df = pd.DataFrame([data])[FEATURES]
|
| 15 |
+
|
| 16 |
+
print("Colonnes API :", df.columns.tolist())
|
| 17 |
+
print("Nombre colonnes API :", len(df.columns))
|
| 18 |
+
|
| 19 |
+
pred = model.predict(df)[0]
|
| 20 |
+
proba = model.predict_proba(df)[0][1]
|
| 21 |
+
|
| 22 |
+
return {
|
| 23 |
+
"Prediction": CLASS_MAPPING[str(pred)],
|
| 24 |
+
"Probabilite_depart": float(proba)
|
| 25 |
+
}
|
App/schemas.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
from pydantic import BaseModel
|
| 2 |
-
from typing import Optional
|
| 3 |
|
| 4 |
class EmployeeFeatures(BaseModel):
|
| 5 |
genre: str
|
|
|
|
| 1 |
from pydantic import BaseModel
|
|
|
|
| 2 |
|
| 3 |
class EmployeeFeatures(BaseModel):
|
| 4 |
genre: str
|
README.md
CHANGED
|
@@ -18,7 +18,6 @@ versionnage, tests, base de données et automatisation.
|
|
| 18 |
- Préparer une base solide pour un déploiement en production
|
| 19 |
|
| 20 |
|
| 21 |
-
|
| 22 |
## Périmètre fonctionnel
|
| 23 |
Le projet inclut :
|
| 24 |
- Une API développée avec **FastAPI**
|
|
@@ -40,9 +39,60 @@ le pipeline exécute automatiquement les étapes suivantes :
|
|
| 40 |
|
| 41 |
L’objectif est de garantir que :
|
| 42 |
- le projet reste installable
|
| 43 |
-
- les transformations et composants
|
| 44 |
- toute fusion vers la branche `develop` est validée automatiquement
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
## Stack technique
|
| 48 |
- **Langage** : Python
|
|
|
|
| 18 |
- Préparer une base solide pour un déploiement en production
|
| 19 |
|
| 20 |
|
|
|
|
| 21 |
## Périmètre fonctionnel
|
| 22 |
Le projet inclut :
|
| 23 |
- Une API développée avec **FastAPI**
|
|
|
|
| 39 |
|
| 40 |
L’objectif est de garantir que :
|
| 41 |
- le projet reste installable
|
| 42 |
+
- les transformations et composants (chargement du modèle, prédiction) ne régressent pas
|
| 43 |
- toute fusion vers la branche `develop` est validée automatiquement
|
| 44 |
|
| 45 |
+
## Architecture de l’API
|
| 46 |
+
|
| 47 |
+
L’API est développée avec **FastAPI** et repose sur :
|
| 48 |
+
- un schéma d’entrée validé avec **Pydantic**
|
| 49 |
+
- un préprocesseur entraîné et sauvegardé
|
| 50 |
+
- un modèle de machine learning sérialisé avec **joblib**
|
| 51 |
+
|
| 52 |
+
Les artefacts du modèle sont stockés dans le dossier `App/model/` :
|
| 53 |
+
- `preprocesseur_fitted.joblib`
|
| 54 |
+
- `model_final_xgb.joblib`
|
| 55 |
+
- `mapping_classes.json`
|
| 56 |
+
|
| 57 |
+
## Lancer l’API en local
|
| 58 |
+
|
| 59 |
+
Depuis la racine du projet :
|
| 60 |
+
|
| 61 |
+
```bash
|
| 62 |
+
uvicorn App.main:app --reload --log-level debug
|
| 63 |
+
```
|
| 64 |
+
L’API est alors accessible à l’adresse http://127.0.0.1:8000/
|
| 65 |
+
|
| 66 |
+
La documentation interactive à http://127.0.0.1:8000/docs
|
| 67 |
+
|
| 68 |
+
### Endpoint principal
|
| 69 |
+
`POST /predict`
|
| 70 |
+
|
| 71 |
+
Cet endpoint reçoit les caractéristiques d’un employé et retourne :
|
| 72 |
+
|
| 73 |
+
- une prédiction lisible ("Reste" ou "Part")
|
| 74 |
+
- la probabilité associée au départ
|
| 75 |
+
|
| 76 |
+
Exemple de réponse :
|
| 77 |
+
```json
|
| 78 |
+
{
|
| 79 |
+
"prediction": "Part",
|
| 80 |
+
"probabilite_depart": 0.79
|
| 81 |
+
}
|
| 82 |
+
```
|
| 83 |
+
Les données d’entrée sont validées automatiquement avant l’appel au modèle,
|
| 84 |
+
garantissant la cohérence avec les variables utilisées lors de l’entraînement.
|
| 85 |
+
|
| 86 |
+
## Documentation des endpoints
|
| 87 |
+
|
| 88 |
+
L’API expose un endpoint principal de prédiction.
|
| 89 |
+
|
| 90 |
+
**POST /predict**
|
| 91 |
+
- Description : retourne une prédiction de départ d’un employé
|
| 92 |
+
- Validation des données : Pydantic
|
| 93 |
+
- Réponses possibles :
|
| 94 |
+
- 200 : prédiction valide
|
| 95 |
+
- 422 : données invalides
|
| 96 |
|
| 97 |
## Stack technique
|
| 98 |
- **Langage** : Python
|
tests/test_api.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi.testclient import TestClient
|
| 2 |
+
from App.main import app
|
| 3 |
+
|
| 4 |
+
client = TestClient(app)
|
| 5 |
+
|
| 6 |
+
def test_predict_endpoint():
|
| 7 |
+
|
| 8 |
+
emp_caract = {
|
| 9 |
+
"genre": "M",
|
| 10 |
+
"statut_marital": "Marié(e)",
|
| 11 |
+
"departement": "Commercial",
|
| 12 |
+
"poste": "Cadre Commercial",
|
| 13 |
+
"domaine_etude": "Infra & Cloud",
|
| 14 |
+
"frequence_deplacement": "Occasionnel",
|
| 15 |
+
"heure_supplementaires": "false",
|
| 16 |
+
"evolution_cat_evol": "hausse",
|
| 17 |
+
"categorie_employe": "employe-experimente",
|
| 18 |
+
"satisfaction_employee_nature_travail": 3,
|
| 19 |
+
"nombre_participation_pee": 0,
|
| 20 |
+
"ecart_note_evaluation": 1,
|
| 21 |
+
"revenu_mensuel": 10609,
|
| 22 |
+
"distance_domicile_travail": 1,
|
| 23 |
+
"satisfaction_globale": 2.00,
|
| 24 |
+
"niveau_education": 2,
|
| 25 |
+
"note_evaluation_actuelle": 3,
|
| 26 |
+
"satisfaction_employee_equipe": 3,
|
| 27 |
+
"age": 37,
|
| 28 |
+
"revenu_par_annee_experience_interne": 9093,
|
| 29 |
+
"satisfaction_employee_equilibre_pro_perso": 1,
|
| 30 |
+
"nombre_experiences_precedentes": 5,
|
| 31 |
+
"annees_dans_l_entreprise": 14,
|
| 32 |
+
"nb_formations_suivies": 2,
|
| 33 |
+
"revenu_par_annee_experience_totale": 7488,
|
| 34 |
+
"ratio_sans_promotion": 0,
|
| 35 |
+
"satisfaction_employee_environnement": 1,
|
| 36 |
+
"exp_hors_entreprise": 3,
|
| 37 |
+
"mobilite_promotion": -10,
|
| 38 |
+
"annees_depuis_la_derniere_promotion": 11}
|
| 39 |
+
|
| 40 |
+
response = client.post("/predict", json = emp_caract)
|
| 41 |
+
|
| 42 |
+
assert response.status_code == 200
|
| 43 |
+
assert "prediction" in response.json()
|
| 44 |
+
assert "probabilite_depart" in response.json()
|