Spaces:
Paused
Paused
nouvelle version
Browse files- app/__pycache__/main.cpython-312.pyc +0 -0
- app/__pycache__/voting.cpython-312.pyc +0 -0
- app/main.py +9 -3
- app/voting.py +158 -1
app/__pycache__/main.cpython-312.pyc
CHANGED
|
Binary files a/app/__pycache__/main.cpython-312.pyc and b/app/__pycache__/main.cpython-312.pyc differ
|
|
|
app/__pycache__/voting.cpython-312.pyc
CHANGED
|
Binary files a/app/__pycache__/voting.cpython-312.pyc and b/app/__pycache__/voting.cpython-312.pyc differ
|
|
|
app/main.py
CHANGED
|
@@ -34,10 +34,16 @@ class ImagePayload(BaseModel):
|
|
| 34 |
async def predict(request: Request,
|
| 35 |
file: UploadFile = File(None),
|
| 36 |
payload: Union[ImagePayload, None] = None,
|
| 37 |
-
mode: str = Query("single", enum=["single", "voting"], description="Mode de prédiction"),
|
| 38 |
-
show_heatmap: bool = Query(False, description="Afficher la heatmap")
|
|
|
|
|
|
|
|
|
|
| 39 |
logger.info("🔁 Requête reçue")
|
| 40 |
logger.info(f"✅ Mode : {mode}")
|
|
|
|
|
|
|
|
|
|
| 41 |
try:
|
| 42 |
# Cas 1 : multipart avec fichier
|
| 43 |
if file is not None:
|
|
@@ -59,7 +65,7 @@ async def predict(request: Request,
|
|
| 59 |
|
| 60 |
# Appel de ta logique de prédiction
|
| 61 |
logger.debug("🔍 Appel du vote multi-modèles...")
|
| 62 |
-
prediction = await soft_voting(model_configs,image_bytes,mode,show_heatmap)
|
| 63 |
|
| 64 |
# Pour l’instant : réponse simulée
|
| 65 |
return prediction
|
|
|
|
| 34 |
async def predict(request: Request,
|
| 35 |
file: UploadFile = File(None),
|
| 36 |
payload: Union[ImagePayload, None] = None,
|
| 37 |
+
mode: str = Query("single", enum=["single", "voting","automatic"], description="Mode de prédiction"),
|
| 38 |
+
show_heatmap: bool = Query(False, description="Afficher la heatmap"),
|
| 39 |
+
default_model: str = Query("efficientnetv2m", enum=["efficientnetv2m", "resnet50"], description="Model par défaut")
|
| 40 |
+
):
|
| 41 |
+
|
| 42 |
logger.info("🔁 Requête reçue")
|
| 43 |
logger.info(f"✅ Mode : {mode}")
|
| 44 |
+
logger.info(f"✅ Default model : {default_model}")
|
| 45 |
+
logger.info(f"✅ Show heatmap : {show_heatmap}")
|
| 46 |
+
|
| 47 |
try:
|
| 48 |
# Cas 1 : multipart avec fichier
|
| 49 |
if file is not None:
|
|
|
|
| 65 |
|
| 66 |
# Appel de ta logique de prédiction
|
| 67 |
logger.debug("🔍 Appel du vote multi-modèles...")
|
| 68 |
+
prediction = await soft_voting(model_configs,image_bytes,mode,show_heatmap,default_model)
|
| 69 |
|
| 70 |
# Pour l’instant : réponse simulée
|
| 71 |
return prediction
|
app/voting.py
CHANGED
|
@@ -51,7 +51,164 @@ def compute_js_divergence(all_probs):
|
|
| 51 |
|
| 52 |
# Si js_divergence > 0.1 → Désaccord modéré
|
| 53 |
|
| 54 |
-
async def soft_voting(model_configs,image_bytes: bytes,mode,show_heatmap):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
logger.info("🔁 Début du vote multi-modèles")
|
| 56 |
all_probs = []
|
| 57 |
models = []
|
|
|
|
| 51 |
|
| 52 |
# Si js_divergence > 0.1 → Désaccord modéré
|
| 53 |
|
| 54 |
+
async def soft_voting(model_configs, image_bytes: bytes, mode, show_heatmap, default_model):
|
| 55 |
+
logger.info("🔁 Début de la prédiction multi-modèles")
|
| 56 |
+
|
| 57 |
+
all_probs = []
|
| 58 |
+
models = []
|
| 59 |
+
models_predictions = []
|
| 60 |
+
models_confidences = []
|
| 61 |
+
models_entropies = []
|
| 62 |
+
models_uncertainties = []
|
| 63 |
+
models_heatmaps = []
|
| 64 |
+
|
| 65 |
+
# On commence toujours par le modèle par défaut
|
| 66 |
+
default_config = next((config for config in model_configs if config["model_name"].lower() == default_model.lower()), None)
|
| 67 |
+
|
| 68 |
+
if default_config is None:
|
| 69 |
+
logger.error(f"❌ Modèle par défaut '{default_model}' introuvable dans les configurations.")
|
| 70 |
+
return None
|
| 71 |
+
|
| 72 |
+
async with aiohttp.ClientSession() as session:
|
| 73 |
+
# Prédiction avec le modèle par défaut
|
| 74 |
+
logger.info(f"🚀 Prédiction avec le modèle par défaut : {default_model}")
|
| 75 |
+
prediction = predict_with_model(default_config, image_bytes, show_heatmap)
|
| 76 |
+
|
| 77 |
+
all_probs.append(prediction["preds"])
|
| 78 |
+
logger.info(f" predicted_class: {prediction["predicted_class"]}")
|
| 79 |
+
models_predictions.append(prediction["predicted_class"])
|
| 80 |
+
models_confidences.append(prediction["confidence"])
|
| 81 |
+
models_entropies.append(prediction["entropy"])
|
| 82 |
+
models_uncertainties.append(prediction["is_uncertain_model"])
|
| 83 |
+
models.append(default_config["model_name"])
|
| 84 |
+
|
| 85 |
+
if show_heatmap:
|
| 86 |
+
heatmap = prediction.get("heatmap")
|
| 87 |
+
if heatmap and len(heatmap) > 0:
|
| 88 |
+
models_heatmaps.append(heatmap)
|
| 89 |
+
else:
|
| 90 |
+
logger.warning(f"⚠️ Heatmap vide ou invalide pour le modèle {default_config['model_name']}")
|
| 91 |
+
|
| 92 |
+
if not all_probs:
|
| 93 |
+
logger.warning("⚠️ Aucune prédiction reçue, vérifie les APIs appelées.")
|
| 94 |
+
raise Exception("No predictions received.")
|
| 95 |
+
|
| 96 |
+
mean_probs = np.mean(all_probs, axis=0)
|
| 97 |
+
final_class = int(np.argmax(mean_probs))
|
| 98 |
+
final_confidence = float(mean_probs[final_class])
|
| 99 |
+
entropy=float(compute_entropy_safe(mean_probs))
|
| 100 |
+
jsd_score = float(compute_js_divergence(all_probs))
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
logger.debug(f"🧠 Moyenne des probabilités : {mean_probs.tolist()}")
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
# Mode 'single' : on s'arrête ici
|
| 107 |
+
if mode == "single":
|
| 108 |
+
is_global_uncertain=models_uncertainties[0]
|
| 109 |
+
logger.info("🛑 Mode 'single' activé, utilisation uniquement du modèle par défaut.")
|
| 110 |
+
|
| 111 |
+
logger.info(f"✅ Prediction terminé : classe={final_class}"
|
| 112 |
+
f"confiance={final_confidence:.4f}\n"
|
| 113 |
+
f"entropy={entropy:.4f}\n"
|
| 114 |
+
f"jsd_score={jsd_score:.4f}\n"
|
| 115 |
+
f"is_global_uncertain={is_global_uncertain}\n"
|
| 116 |
+
)
|
| 117 |
+
return {
|
| 118 |
+
"predicted_class": final_class,
|
| 119 |
+
"confidence": final_confidence,
|
| 120 |
+
"entropy":entropy,
|
| 121 |
+
"jsd_score":jsd_score,
|
| 122 |
+
"models": models,
|
| 123 |
+
"is_global_uncertain":is_global_uncertain,
|
| 124 |
+
"models_predictions": models_predictions,
|
| 125 |
+
"models_confidences": models_confidences,
|
| 126 |
+
"models_entropies":models_entropies,
|
| 127 |
+
"models_uncertainties":models_uncertainties,
|
| 128 |
+
"models_heatmaps": models_heatmaps
|
| 129 |
+
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
# Si mode == 'automatic' et confiance suffisante, on s'arrête
|
| 133 |
+
if mode == "automatic" and prediction["confidence"] >= 0.90:
|
| 134 |
+
is_global_uncertain=models_uncertainties[0]
|
| 135 |
+
logger.info(f"✅ Confiance élevée ({prediction['confidence']:.2f}), pas besoin de voter.")
|
| 136 |
+
logger.info(f"✅ Prediction terminé : classe={final_class}"
|
| 137 |
+
f"confiance={final_confidence:.4f}\n"
|
| 138 |
+
f"entropy={entropy:.4f}\n"
|
| 139 |
+
f"jsd_score={jsd_score:.4f}\n"
|
| 140 |
+
f"is_global_uncertain={is_global_uncertain}\n"
|
| 141 |
+
)
|
| 142 |
+
return {
|
| 143 |
+
"predicted_class": final_class,
|
| 144 |
+
"confidence": final_confidence,
|
| 145 |
+
"entropy":entropy,
|
| 146 |
+
"jsd_score":jsd_score,
|
| 147 |
+
"models": models,
|
| 148 |
+
"is_global_uncertain":is_global_uncertain,
|
| 149 |
+
"models_predictions": models_predictions,
|
| 150 |
+
"models_confidences": models_confidences,
|
| 151 |
+
"models_entropies":models_entropies,
|
| 152 |
+
"models_uncertainties":models_uncertainties,
|
| 153 |
+
"models_heatmaps": models_heatmaps
|
| 154 |
+
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
# Sinon, on continue avec tous les autres modèles (voting ou automatic avec faible confiance)
|
| 158 |
+
logger.info(f"🔍 Mode '{mode}' : Prédictions complémentaires en cours.")
|
| 159 |
+
|
| 160 |
+
for config in model_configs:
|
| 161 |
+
if config["model_name"].lower() == default_model.lower():
|
| 162 |
+
continue # On a déjà traité le modèle par défaut
|
| 163 |
+
|
| 164 |
+
prediction = predict_with_model(config, image_bytes, show_heatmap)
|
| 165 |
+
|
| 166 |
+
all_probs.append(prediction["preds"])
|
| 167 |
+
models_predictions.append(prediction["predicted_class"])
|
| 168 |
+
models_confidences.append(prediction["confidence"])
|
| 169 |
+
models_entropies.append(prediction["entropy"])
|
| 170 |
+
models_uncertainties.append(prediction["is_uncertain_model"])
|
| 171 |
+
models.append(config["model_name"])
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
if show_heatmap:
|
| 176 |
+
heatmap = prediction.get("heatmap")
|
| 177 |
+
if heatmap and len(heatmap) > 0:
|
| 178 |
+
models_heatmaps.append(heatmap)
|
| 179 |
+
else:
|
| 180 |
+
logger.warning(f"⚠️ Heatmap vide ou invalide pour le modèle {config['model_name']}")
|
| 181 |
+
|
| 182 |
+
mean_probs = np.mean(all_probs, axis=0)
|
| 183 |
+
final_class = int(np.argmax(mean_probs))
|
| 184 |
+
final_confidence = float(mean_probs[final_class])
|
| 185 |
+
entropy=float(compute_entropy_safe(mean_probs))
|
| 186 |
+
jsd_score = float(compute_js_divergence(all_probs))
|
| 187 |
+
|
| 188 |
+
is_global_uncertain = any(models_uncertainties) and jsd_score > shannon_threashold
|
| 189 |
+
logger.info(f"✅ Prediction terminé : classe={final_class}"
|
| 190 |
+
f"confiance={final_confidence:.4f}\n"
|
| 191 |
+
f"entropy={entropy:.4f}\n"
|
| 192 |
+
f"jsd_score={jsd_score:.4f}\n"
|
| 193 |
+
f"is_global_uncertain={is_global_uncertain}\n"
|
| 194 |
+
)
|
| 195 |
+
return {
|
| 196 |
+
"predicted_class": final_class,
|
| 197 |
+
"confidence": final_confidence,
|
| 198 |
+
"entropy":entropy,
|
| 199 |
+
"jsd_score":jsd_score,
|
| 200 |
+
"models": models,
|
| 201 |
+
"is_global_uncertain":is_global_uncertain,
|
| 202 |
+
"models_predictions": models_predictions,
|
| 203 |
+
"models_confidences": models_confidences,
|
| 204 |
+
"models_entropies":models_entropies,
|
| 205 |
+
"models_uncertainties":models_uncertainties,
|
| 206 |
+
"models_heatmaps": models_heatmaps
|
| 207 |
+
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
async def soft_voting_v1(model_configs,image_bytes: bytes,mode,show_heatmap,default_model):
|
| 212 |
logger.info("🔁 Début du vote multi-modèles")
|
| 213 |
all_probs = []
|
| 214 |
models = []
|