97 lines
3.7 KiB
Python
97 lines
3.7 KiB
Python
"""
|
||
ROC curve illustration for ROLL-AOC.
|
||
|
||
Shows how ROLL-AOC converts the single-threshold ROLL into AUC optimization:
|
||
- ROC curve traced by sweeping threshold τ
|
||
- 10 evenly-spaced FPR sample points (as ROLL evaluates in miniature)
|
||
- Green shaded region = AUC (area under curve, what we maximize)
|
||
- Red shaded region = AOC = 1 − AUC (area over curve, the loss we minimize)
|
||
- Vertical segments = per-threshold loss 1 − TPR(αᵢ), one per sampled FPR
|
||
|
||
Run from the thesis root:
|
||
nix develop --command python3 content/method/figures/roll_aoc_roc.py
|
||
"""
|
||
|
||
import numpy as np
|
||
import matplotlib
|
||
matplotlib.use("Agg")
|
||
import matplotlib.pyplot as plt
|
||
from scipy.stats import norm
|
||
|
||
OUT_DIR = "content/method/figures"
|
||
|
||
# --- Parameters (same distributions as roll_principle.py) ------------------
|
||
mu0, sig0 = 0.0, 1.00
|
||
mu1, sig1 = 1.5, 1.00
|
||
|
||
# --- ROC curve -------------------------------------------------------------
|
||
# Sweep τ from high to low → FPR rises from 0 to 1
|
||
tau_grid = np.linspace(norm.ppf(0.9999, mu0, sig0),
|
||
norm.ppf(0.0001, mu0, sig0), 3000)
|
||
fpr_curve = 1.0 - norm.cdf(tau_grid, mu0, sig0)
|
||
tpr_curve = 1.0 - norm.cdf(tau_grid, mu1, sig1)
|
||
|
||
# Pin endpoints so the fill covers the full unit square correctly
|
||
fpr_full = np.concatenate([[0.0], fpr_curve, [1.0]])
|
||
tpr_full = np.concatenate([[0.0], tpr_curve, [1.0]])
|
||
|
||
auc = float(np.trapezoid(tpr_full, fpr_full))
|
||
|
||
# --- 10 sample FPR points: αᵢ = i/11 for i = 1…10 ------------------------
|
||
n_samples = 10
|
||
alpha_s = np.array([(i / (n_samples + 1)) for i in range(1, n_samples + 1)])
|
||
tpr_s = np.interp(alpha_s, fpr_full, tpr_full)
|
||
|
||
# --- Palette (matches roll_principle.py) -----------------------------------
|
||
C_POS = "#CC6633" # ROC curve (positive-class colour)
|
||
C_GREEN = "#44AA77" # AUC fill
|
||
C_RED = "#BB3333" # AOC fill / vertical segments
|
||
C_DOT = "#333333" # sample dots
|
||
|
||
# ── Figure ─────────────────────────────────────────────────────────────────
|
||
fig, ax = plt.subplots(figsize=(4.8, 4.4))
|
||
|
||
# Random-classifier diagonal
|
||
ax.plot([0, 1], [0, 1], color="#AAAAAA", linestyle="--",
|
||
linewidth=0.9, zorder=1, label="Random classifier")
|
||
|
||
# AUC — green fill under ROC curve
|
||
ax.fill_between(fpr_full, 0, tpr_full,
|
||
color=C_GREEN, alpha=0.25, zorder=2,
|
||
label=rf"AUC $\approx$ {auc:.2f} (maximize)")
|
||
|
||
# AOC — red fill above ROC curve
|
||
ax.fill_between(fpr_full, tpr_full, 1.0,
|
||
color=C_RED, alpha=0.22, zorder=2,
|
||
label=r"AOC $= 1-\mathrm{AUC}$ (minimize)")
|
||
|
||
# ROC curve itself
|
||
ax.plot(fpr_full, tpr_full, color=C_POS, linewidth=2.0, zorder=3,
|
||
label="ROC curve")
|
||
|
||
# Per-threshold loss segments: vertical from TPR(αᵢ) up to 1
|
||
for a_i, t_i in zip(alpha_s, tpr_s):
|
||
ax.plot([a_i, a_i], [t_i, 1.0],
|
||
color=C_RED, linewidth=1.0, alpha=0.55, zorder=4)
|
||
|
||
# Sample dots on the ROC curve
|
||
ax.scatter(alpha_s, tpr_s,
|
||
color=C_DOT, s=35, zorder=5,
|
||
label=r"Sampled $\alpha_i$ (ROLL evaluations)")
|
||
|
||
# --- Axes / labels ---------------------------------------------------------
|
||
ax.set_xlabel(r"FPR $(\alpha)$", fontsize=9)
|
||
ax.set_ylabel(r"TPR", fontsize=9)
|
||
ax.set_xlim(0.0, 1.0)
|
||
ax.set_ylim(0.0, 1.0)
|
||
ax.set_aspect("equal")
|
||
ax.tick_params(labelsize=8)
|
||
ax.spines[["top", "right"]].set_visible(False)
|
||
ax.legend(fontsize=7.5, loc="lower right", framealpha=0.88)
|
||
|
||
fig.tight_layout()
|
||
fig.savefig(f"{OUT_DIR}/roll_aoc_roc.pdf", bbox_inches="tight")
|
||
fig.savefig(f"{OUT_DIR}/roll_aoc_roc.png", bbox_inches="tight", dpi=180)
|
||
print(f"Saved to {OUT_DIR}/roll_aoc_roc.{{pdf,png}}")
|
||
print(f"AUC = {auc:.4f} AOC = {1-auc:.4f}")
|