yusufcalisir's picture
deploy: Hugging Face space upload
94cb318
Raw History Blame Contribute Delete
5.42 kB
#!/usr/bin/env python3
"""Multi-Configuration Benchmark Visualization & Plot Generator (Section 8.2).
Generates high-resolution comparative figures:
1. docs/figures/benchmark_auc_comparison.png — ROC-AUC & PR-AUC across C1–C9
2. docs/figures/benchmark_privacy_utility.png — Privacy-Utility trade-off curve (AUC vs. ε)
3. docs/figures/benchmark_communication.png — Transmitted bytes vs. rounds to convergence
"""
from __future__ import annotations
import json
import logging
import sys
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
# Add backend to path for fallback generation
backend_path = str(Path(__file__).resolve().parents[1] / "backend")
if backend_path not in sys.path:
sys.path.insert(0, backend_path)
from app.domain.benchmark_runner import BenchmarkRunner # noqa: E402
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger("generate_plots")
def generate_benchmark_plots(
json_file: str | Path = "storage/benchmark_results.json", out_dir: str | Path = "docs/figures"
) -> None:
"""Renders high-resolution benchmark visualization figures."""
json_path = Path(json_file)
target_dir = Path(out_dir)
target_dir.mkdir(parents=True, exist_ok=True)
if not json_path.exists():
logger.info("JSON benchmark results not found at %s. Running BenchmarkRunner...", json_path)
runner = BenchmarkRunner()
runner.run_all()
runner.save_results_json(json_path)
data = json.loads(json_path.read_text(encoding="utf-8"))
configs = ["C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9"]
roc_aucs = [data[c]["roc_auc"] for c in configs]
pr_aucs = [data[c]["pr_auc"] for c in configs]
bytes_mb = [data[c]["total_bytes_transmitted"] / 1_000_000 for c in configs]
# --- Plot 1: ROC-AUC & PR-AUC Comparison Bar Chart ---
plt.style.use(
"seaborn-v0_8-whitegrid" if "seaborn-v0_8-whitegrid" in plt.style.available else "default"
)
fig, ax = plt.subplots(figsize=(12, 6))
x = np.arange(len(configs))
width = 0.35
rects1 = ax.bar(x - width / 2, roc_aucs, width, label="ROC-AUC", color="#1f77b4")
rects2 = ax.bar(x + width / 2, pr_aucs, width, label="PR-AUC", color="#ff7f0e")
ax.set_ylabel("Metric Score", fontsize=12, fontweight="bold")
ax.set_title(
"Cross-Bank Federated Fraud Intelligence — 9 Configuration Benchmark",
fontsize=14,
fontweight="bold",
pad=15,
)
ax.set_xticks(x)
ax.set_xticklabels(configs, fontweight="bold")
ax.set_ylim(0.70, 1.0)
ax.legend(frameon=True, facecolor="white", framealpha=0.9)
ax.bar_label(rects1, padding=3, fmt="%.3f", fontsize=8)
ax.bar_label(rects2, padding=3, fmt="%.3f", fontsize=8)
fig.tight_layout()
fig1_path = target_dir / "benchmark_auc_comparison.png"
plt.savefig(fig1_path, dpi=300)
plt.close()
logger.info("Saved Figure 1: %s", fig1_path)
# --- Plot 2: Privacy-Utility Trade-off Curve (AUC vs Epsilon) ---
fig, ax = plt.subplots(figsize=(8, 5))
privacy_configs = ["C3", "C5", "C7", "C9"]
eps_vals = [
data[c]["epsilon_consumed"] if data[c]["epsilon_consumed"] > 0 else 10.0
for c in privacy_configs
]
auc_vals = [data[c]["roc_auc"] for c in privacy_configs]
labels = [
"FedAvg (Eps=Inf)",
"FedAvg+DP (Eps=1.0)",
"Full Privacy (Eps=1.0)",
"Full Stack (Eps=1.0)",
]
ax.plot(
eps_vals, auc_vals, "o--", color="#2ca02c", linewidth=2.5, markersize=8, label="ROC-AUC"
)
for i, txt in enumerate(labels):
ax.annotate(
txt,
(eps_vals[i], auc_vals[i]),
textcoords="offset points",
xytext=(0, 10),
ha="center",
fontsize=9,
fontweight="bold",
)
ax.set_xlabel("Differential Privacy Budget (Epsilon ε)", fontsize=11, fontweight="bold")
ax.set_ylabel("Global ROC-AUC Score", fontsize=11, fontweight="bold")
ax.set_title(
"Privacy-Utility Trade-off (Differential Privacy Noise vs AUC)",
fontsize=13,
fontweight="bold",
pad=15,
)
ax.set_ylim(0.85, 0.95)
ax.grid(True, linestyle="--", alpha=0.6)
fig.tight_layout()
fig2_path = target_dir / "benchmark_privacy_utility.png"
plt.savefig(fig2_path, dpi=300)
plt.close()
logger.info("Saved Figure 2: %s", fig2_path)
# --- Plot 3: Communication Payload Size (MB) ---
fig, ax = plt.subplots(figsize=(10, 5))
colors = [
"#7f7f7f",
"#d62728",
"#1f77b4",
"#1f77b4",
"#9467bd",
"#17becf",
"#9467bd",
"#e377c2",
"#bcbd22",
]
bars = ax.bar(configs, bytes_mb, color=colors)
ax.set_ylabel("Total Network Bytes Transmitted (MB)", fontsize=11, fontweight="bold")
ax.set_title(
"Communication Network Overhead Across Architectures",
fontsize=13,
fontweight="bold",
pad=15,
)
ax.set_ylim(0, max(bytes_mb) * 1.2)
ax.bar_label(bars, padding=3, fmt="%.1f MB", fontsize=8)
fig.tight_layout()
fig3_path = target_dir / "benchmark_communication.png"
plt.savefig(fig3_path, dpi=300)
plt.close()
logger.info("Saved Figure 3: %s", fig3_path)
if __name__ == "__main__":
generate_benchmark_plots()