Spaces:
Sleeping
Sleeping
File size: 7,226 Bytes
ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 15ea647 ad86b94 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | # src/scripts/profile_predict.py
"""
Script de profiling pour identifier les goulots d'étranglement
lors de l'inférence (prédiction), simulant le comportement de l'API FastAPI.
Usage:
poetry run python src/scripts/profile_predict.py
Prérequis:
- Fichiers de test dans ./.data/extract/:
* test_sample_application.csv
* test_sample_bureau.csv
- Variables d'environnement HF_REPOSITORY, HF_MODEL, HF_PIPELINE
"""
import cProfile
import pstats
import io
from pathlib import Path
from datetime import datetime
import os
import joblib
import pandas as pd
import numpy as np
from huggingface_hub import hf_hub_download
# Variables globales (comme dans l'API)
model = None
pipeline = None
_model_loaded = False
def load_model():
"""
Charge le modèle et le pipeline depuis Hugging Face (lazy loading).
Simule exactement le comportement de l'API.
"""
global model, pipeline, _model_loaded
if _model_loaded:
print("/////////////////////////////// Modèle déjà chargé !! //////////")
return # déjà chargé
print("#######################################################")
print("Chargement du modèle et du pipeline depuis Hugging Face (lazy)...")
hf_repository = os.getenv("HF_REPOSITORY")
hf_model = os.getenv("HF_MODEL")
hf_pipeline = os.getenv("HF_PIPELINE")
if not all([hf_repository, hf_model, hf_pipeline]):
raise RuntimeError(
"Variables d'environnement HF manquantes (HF_REPOSITORY, HF_MODEL, HF_PIPELINE)"
)
# Téléchargement et chargement du modèle
model_path = hf_hub_download(
repo_id=hf_repository,
filename=hf_model
)
model = joblib.load(model_path)
print(f"Modèle chargé depuis: {model_path}")
# Téléchargement et chargement de la pipeline
pipeline_path = hf_hub_download(
repo_id=hf_repository,
filename=hf_pipeline
)
pipeline = joblib.load(pipeline_path)
print(f"Pipeline chargée depuis: {pipeline_path}")
_model_loaded = True
def load_test_data():
"""Charge les données de test pour le profiling."""
data_dir = Path('./.data/extract')
app_path = data_dir / 'test_sample_application.csv'
bureau_path = data_dir / 'test_sample_bureau.csv'
if not app_path.exists():
raise FileNotFoundError(
f"Fichier {app_path} introuvable. "
f"Placez vos données de test dans {data_dir}/"
)
df_application = pd.read_csv(app_path)
print(f"Données application chargées: {len(df_application)} lignes")
# Bureau peut être vide
if bureau_path.exists():
df_bureau = pd.read_csv(bureau_path)
print(f"Données bureau chargées: {len(df_bureau)} lignes")
else:
df_bureau = pd.DataFrame()
print("⚠ Pas de données bureau (fichier absent)")
return df_application, df_bureau
def simulate_api_prediction(df_application, df_bureau):
"""
Simule une requête API : lazy loading + prédiction.
Args:
df_application: DataFrame avec les données application (1 ligne)
df_bureau: DataFrame avec les données bureau
Returns:
dict: Résultat de la prédiction avec temps d'inférence
"""
start = datetime.now()
# Lazy loading (comme dans l'API)
if model is None or pipeline is None:
load_model()
else:
print("############################ modèle déjà chargé !!!! ##########")
# Transformation via la pipeline (comme dans l'API)
X_processed = pipeline.transform(df_application, df_bureau)
# Prédiction
pred = int(model.predict(X_processed)[0])
# Calcul de la probabilité
if hasattr(model, "predict_proba"):
proba = float(model.predict_proba(X_processed)[0][1])
elif hasattr(model, "decision_function"):
decision = model.decision_function(X_processed)[0]
proba = float(1 / (1 + np.exp(-decision)))
else:
proba = float(pred)
elapsed_ms = (datetime.now() - start).total_seconds() * 1000
return {
'prediction': pred,
'probability': round(proba, 4),
'inference_time_ms': round(elapsed_ms, 2)
}
def run_profiling(df_application, df_bureau, n_iterations=100):
"""
Exécute n prédictions pour profiler les performances.
Args:
df_application: DataFrame avec les données application
df_bureau: DataFrame avec les données bureau
n_iterations: Nombre d'itérations pour le profiling
"""
print(f"\n🔄 Exécution de {n_iterations} prédictions pour profiling...")
results = []
for i in range(n_iterations):
result = simulate_api_prediction(df_application, df_bureau)
result['iteration'] = i + 1
results.append(result)
# Statistiques (en excluant la première itération qui inclut le chargement)
times = [r['inference_time_ms'] for r in results[1:]] # Skip première itération
print(f"\n Statistiques sur {len(times)} prédictions (hors chargement initial):")
print(f" - Temps moyen: {np.mean(times):.2f} ms")
print(f" - Temps médian: {np.median(times):.2f} ms")
print(f" - Temps min: {np.min(times):.2f} ms")
print(f" - Temps max: {np.max(times):.2f} ms")
print(f" - Écart-type: {np.std(times):.2f} ms")
# Temps de la première prédiction (avec chargement)
first_time = results[0]['inference_time_ms']
print(f"\n⏱ Temps première prédiction (avec lazy loading): {first_time:.2f} ms")
return results
def main():
"""Fonction principale encapsulant tout le profiling."""
print("=" * 80)
print("PROFILING DE L'INFÉRENCE - Simulation du comportement de l'API")
print("=" * 80)
# 1. Chargement des données de test
df_application, df_bureau = load_test_data()
# 2. Exécution des prédictions (à profiler)
# Note: Le lazy loading se fera à la première itération
results = run_profiling(
df_application=df_application,
df_bureau=df_bureau,
n_iterations=100
)
print("\n Profiling terminé avec succès")
if __name__ == "__main__":
# Créer le dossier de sortie pour les résultats de profiling
Path('.data/profiling').mkdir(parents=True, exist_ok=True)
# Activer le profiler
profiler = cProfile.Profile()
profiler.enable()
# Exécuter le profiling
main()
# Désactiver le profiler
profiler.disable()
# Afficher les résultats dans la console
s = io.StringIO()
ps = pstats.Stats(profiler, stream=s).sort_stats("cumulative")
ps.print_stats(50) # Top 50 fonctions les plus coûteuses
print("\n" + "=" * 80)
print("PROFILING RESULTS - Top 50 fonctions par temps cumulé")
print("=" * 80)
print(s.getvalue())
# Sauvegarder le fichier .prof
prof_file = '.data/profiling/predict_inference.prof'
profiler.dump_stats(prof_file)
print(f"\n Fichier de profiling sauvegardé : {prof_file}") |