Rthur2003 commited on
Commit
255af12
·
1 Parent(s): ca749ec

feat: enhance calibration figure with Brier score and update prediction functions

Browse files
Files changed (1) hide show
  1. app/training/generate_figures.py +35 -19
app/training/generate_figures.py CHANGED
@@ -239,26 +239,35 @@ def fig_feature_importance(results: dict, top_n: int = 20) -> None:
239
  print(" ✓ feature_importance_top20.png")
240
 
241
 
242
- def fig_calibration(results: dict) -> None:
243
  """Calibration curve — does predicted probability match reality?"""
244
  fig, ax = plt.subplots(figsize=(7, 6.5))
245
  best = results.get("_best_model", "XGBoost")
246
- items = [(k, v) for k, v in results.items() if not k.startswith("_") and isinstance(v, dict)]
247
-
248
- colors = plt.cm.plasma(np.linspace(0.2, 0.8, len(items)))
249
- for idx, (name, data) in enumerate(items):
250
- y_true = np.array(data["y_true"])
251
- y_prob = np.array(data["y_prob"])
252
- frac_pos, mean_pred = calibration_curve(y_true, y_prob, n_bins=10)
253
- lw = 3 if name == best else 1.2
254
- ax.plot(mean_pred, frac_pos, "o-", color=colors[idx], lw=lw,
255
- label=f"{name}", markersize=6 if name == best else 4)
256
 
 
 
 
 
257
  ax.plot([0, 1], [0, 1], "k:", alpha=0.5, label="Mükemmel / Perfect")
 
 
 
 
 
 
 
 
 
 
 
 
258
  ax.set_xlabel("Ortalama Tahmin Olasılığı / Mean Predicted Probability")
259
  ax.set_ylabel("Gerçek Pozitif Oranı / Fraction of Positives")
260
- ax.set_title("Kalibrasyon Eğrisi", fontsize=13, fontweight="bold")
261
- ax.legend(loc="upper left", framealpha=0.85, fontsize=9)
 
 
262
  plt.savefig(FIGURES_DIR / "calibration_plot.png")
263
  plt.close()
264
  print(" ✓ calibration_plot.png")
@@ -393,17 +402,24 @@ def main() -> None:
393
  importance.items(), key=lambda x: x[1], reverse=True,
394
  )]
395
 
 
 
 
 
 
 
 
 
396
  print("\nGenerating figures...")
397
- fig_confusion_matrix(results)
398
- fig_roc_comparison(results)
399
- fig_pr_curves(results)
400
  fig_feature_importance(results)
401
- fig_calibration(results)
402
  fig_model_comparison(results)
403
  fig_feature_distributions(feature_cols, top_features)
404
 
405
- print("\nLoading data for SHAP (this may take ~30s)...")
406
- X, y = _load_csv_data(feature_cols)
407
  fig_shap_summary(model, scaler, feature_cols, X)
408
 
409
  print(f"\nDone. {len(list(FIGURES_DIR.glob('*.png')))} figures in {FIGURES_DIR}")
 
239
  print(" ✓ feature_importance_top20.png")
240
 
241
 
242
+ def fig_calibration(results: dict, y_true: np.ndarray, y_prob: np.ndarray) -> None:
243
  """Calibration curve — does predicted probability match reality?"""
244
  fig, ax = plt.subplots(figsize=(7, 6.5))
245
  best = results.get("_best_model", "XGBoost")
246
+ frac_pos, mean_pred = calibration_curve(y_true, y_prob, n_bins=10)
 
 
 
 
 
 
 
 
 
247
 
248
+ ax.plot(mean_pred, frac_pos, "o-", color=PALETTE["primary"], lw=3,
249
+ markersize=8, label=f"{best}")
250
+ ax.fill_between(mean_pred, frac_pos, mean_pred, alpha=0.15,
251
+ color=PALETTE["primary"])
252
  ax.plot([0, 1], [0, 1], "k:", alpha=0.5, label="Mükemmel / Perfect")
253
+
254
+ # Brier score annotation
255
+ brier = float(np.mean((y_prob - y_true) ** 2))
256
+ ax.text(
257
+ 0.04, 0.94,
258
+ f"Brier Score = {brier:.4f}\nN = {len(y_true)} (5-fold CV)",
259
+ transform=ax.transAxes,
260
+ fontsize=10, va="top",
261
+ bbox=dict(boxstyle="round,pad=0.5", facecolor=PALETTE["bg"],
262
+ edgecolor=PALETTE["primary"], alpha=0.85),
263
+ )
264
+
265
  ax.set_xlabel("Ortalama Tahmin Olasılığı / Mean Predicted Probability")
266
  ax.set_ylabel("Gerçek Pozitif Oranı / Fraction of Positives")
267
+ ax.set_title("Kalibrasyon Eğrisi — En İyi Model", fontsize=13, fontweight="bold")
268
+ ax.legend(loc="lower right", framealpha=0.85, fontsize=10)
269
+ ax.set_xlim([0, 1])
270
+ ax.set_ylim([0, 1])
271
  plt.savefig(FIGURES_DIR / "calibration_plot.png")
272
  plt.close()
273
  print(" ✓ calibration_plot.png")
 
402
  importance.items(), key=lambda x: x[1], reverse=True,
403
  )]
404
 
405
+ print("\nLoading dataset...")
406
+ X, y = _load_csv_data(feature_cols)
407
+ X_scaled = scaler.transform(X)
408
+
409
+ print("Computing 5-fold cross-validated predictions (this may take ~1-2 min)...")
410
+ cache: dict = {}
411
+ y_true, y_pred, y_prob = _get_cv_predictions(model, X_scaled, y, cache)
412
+
413
  print("\nGenerating figures...")
414
+ fig_confusion_matrix(results, y_true, y_pred)
415
+ fig_roc_comparison(results, y_true, y_prob)
416
+ fig_pr_curves(results, y_true, y_prob)
417
  fig_feature_importance(results)
418
+ fig_calibration(results, y_true, y_prob)
419
  fig_model_comparison(results)
420
  fig_feature_distributions(feature_cols, top_features)
421
 
422
+ print("\nGenerating SHAP summary...")
 
423
  fig_shap_summary(model, scaler, feature_cols, X)
424
 
425
  print(f"\nDone. {len(list(FIGURES_DIR.glob('*.png')))} figures in {FIGURES_DIR}")