DavidJyes's picture
upload files
d5b0b0e verified
Raw
History Blame
5.95 kB
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
import joblib
import json
import numpy as np
from datetime import datetime
from typing import Optional
import os
# Initialisation
app = FastAPI(
title="API Détection de Fraude",
description="API de détection de fraude dans les transactions bancaires",
version="1.0.0"
)
# Chargement du modèle et des encodeurs
model = joblib.load('fraud_model.pkl')
le_category = joblib.load('le_category.pkl')
le_gender = joblib.load('le_gender.pkl')
le_state = joblib.load('le_state.pkl')
with open('mappings.json', 'r') as f:
mappings = json.load(f)
# Modèle de données
class Transaction(BaseModel):
amt: float = Field(..., description="Montant de la transaction", example=125.50)
category: str = Field(..., description="Catégorie du marchand", example="personal_care")
merchant: str = Field(..., description="Nom du marchand", example="fraud_Kirlin and Sons")
trans_date_trans_time: str = Field(..., description="Date et heure", example="2020-06-21 12:14:25")
gender: str = Field(..., description="Genre (M/F)", example="M")
state: str = Field(..., description="État (code à 2 lettres)", example="SC")
lat: float = Field(..., description="Latitude du client", example=33.9659)
long: float = Field(..., description="Longitude du client", example=-80.9355)
city_pop: int = Field(..., description="Population de la ville", example=333497)
dob: str = Field(..., description="Date de naissance", example="1968-03-19")
merch_lat: float = Field(..., description="Latitude du marchand", example=33.986391)
merch_long: float = Field(..., description="Longitude du marchand", example=-81.200714)
cc_num: Optional[str] = Field(None, description="Numéro de carte (optionnel)")
avg_amt: Optional[float] = Field(50.0, description="Montant moyen historique")
std_amt: Optional[float] = Field(30.0, description="Écart-type historique")
nb_trans: Optional[int] = Field(10, description="Nombre de transactions historiques")
class PredictionResponse(BaseModel):
is_fraud: bool
fraud_probability: float
risk_level: str
details: dict
# Routes
@app.get("/")
def read_root():
return {
"message": "API de Détection de Fraude",
"version": "1.0.0",
"endpoints": {
"/predict": "POST - Prédire une transaction",
"/health": "GET - Statut de l'API",
"/categories": "GET - Liste des catégories",
"/docs": "GET - Documentation interactive"
}
}
@app.get("/health")
def health_check():
return {
"status": "healthy",
"model_loaded": model is not None,
"timestamp": datetime.now().isoformat()
}
@app.get("/categories")
def get_categories():
return {
"categories": list(mappings['categories'].keys()),
"states": list(mappings['states'].keys()),
"genders": list(mappings['genders'].keys())
}
@app.post("/predict", response_model=PredictionResponse)
def predict_fraud(transaction: Transaction):
try:
# Parsing des dates
trans_dt = datetime.strptime(transaction.trans_date_trans_time, "%Y-%m-%d %H:%M:%S")
dob_dt = datetime.strptime(transaction.dob, "%Y-%m-%d")
# Features temporelles
hour = trans_dt.hour
day_of_week = trans_dt.weekday()
day = trans_dt.day
month = trans_dt.month
age = (trans_dt - dob_dt).days // 365
# Distance
distance = np.sqrt(
(transaction.lat - transaction.merch_lat)**2 +
(transaction.long - transaction.merch_long)**2
) * 111
# Encodage
if transaction.category not in mappings['categories']:
raise HTTPException(400, f"Catégorie inconnue: {transaction.category}")
if transaction.gender not in mappings['genders']:
raise HTTPException(400, f"Genre inconnu: {transaction.gender}")
if transaction.state not in mappings['states']:
raise HTTPException(400, f"État inconnu: {transaction.state}")
category_encoded = mappings['categories'][transaction.category]
gender_encoded = mappings['genders'][transaction.gender]
state_encoded = mappings['states'][transaction.state]
# Construction du vecteur de features
features = np.array([[
transaction.amt,
hour,
day_of_week,
day,
month,
age,
category_encoded,
gender_encoded,
state_encoded,
transaction.lat,
transaction.long,
transaction.city_pop,
distance,
transaction.avg_amt,
transaction.std_amt,
transaction.nb_trans
]])
# Prédiction
fraud_proba = model.predict_proba(features)[0][1]
is_fraud = fraud_proba > 0.5
# Niveau de risque
if fraud_proba < 0.3:
risk_level = "Faible"
elif fraud_proba < 0.7:
risk_level = "Moyen"
else:
risk_level = "Élevé"
return PredictionResponse(
is_fraud=bool(is_fraud),
fraud_probability=float(fraud_proba),
risk_level=risk_level,
details={
"montant": transaction.amt,
"categorie": transaction.category,
"heure": hour,
"age_client": age,
"distance_km": round(distance, 2)
}
)
except ValueError as e:
raise HTTPException(status_code=400, detail=f"Erreur de format: {str(e)}")
except Exception as e:
raise HTTPException(status_code=500, detail=f"Erreur interne: {str(e)}")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)