Spaces:
Sleeping
Sleeping
feat: enhance calibration figure with Brier score and update prediction functions
Browse files- 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 |
-
|
| 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="
|
|
|
|
|
|
|
| 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("\
|
| 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}")
|