Buckets:
| """Figures for the logbook / poster. Reads outputs/*.json written by the claim | |
| scripts. CPU only, matplotlib Agg.""" | |
| import json | |
| import math | |
| import os | |
| import sys | |
| import matplotlib | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt # noqa: E402 | |
| import numpy as np # noqa: E402 | |
| ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| OUT = os.path.join(ROOT, "outputs") | |
| FIGS = os.path.join(ROOT, "figs") | |
| os.makedirs(FIGS, exist_ok=True) | |
| ACCENT = "#7a1f3d" | |
| ACCENT2 = "#1f6f8b" | |
| GREY = "#555555" | |
| plt.rcParams.update( | |
| { | |
| "font.size": 13, | |
| "axes.grid": True, | |
| "grid.alpha": 0.25, | |
| "figure.dpi": 130, | |
| "savefig.bbox": "tight", | |
| } | |
| ) | |
| def load(name): | |
| with open(os.path.join(OUT, name)) as f: | |
| return json.load(f) | |
| # --------------------------------------------------------------------- fig 1 | |
| c5 = load("claim5_thm311.json") | |
| tab = c5["worst_case_table"] | |
| fig, ax = plt.subplots(figsize=(6.4, 4.4)) | |
| for d, col in zip((8, 32, 128), (ACCENT, ACCENT2, "#c9761c")): | |
| sub = [t for t in tab if t["d_gamma"] == d] | |
| ax.loglog( | |
| [t["n"] for t in sub], | |
| [t["worst_E_loss"] for t in sub], | |
| "o-", | |
| color=col, | |
| label=f"$d_\\gamma$ = {d}", | |
| lw=2, | |
| ms=5, | |
| ) | |
| ref_n = np.array([8, 128000]) | |
| ax.loglog( | |
| ref_n, 0.347 * 8 / ref_n, "--", color=GREY, lw=1.4, label="$0.347\\, d_\\gamma/n$" | |
| ) | |
| ax.set_xlabel("sample size $n$") | |
| ax.set_ylabel("worst-case $E[L^\\gamma_D(M)]$") | |
| ax.set_title( | |
| "Thm 3.11: median-of-three achieves $O(d_\\gamma/n)$\n" | |
| "fitted exponent $-0.999$ ($R^2 = 1.000$)", | |
| fontsize=12, | |
| ) | |
| ax.legend(fontsize=11) | |
| fig.savefig(os.path.join(FIGS, "fig1_median3_rate.png")) | |
| plt.close(fig) | |
| # --------------------------------------------------------------------- fig 2 | |
| c6 = load("claim6_thm312.json") | |
| rows = [r for r in c6["proper_sample_complexity"] if r["d_gamma"] == 32] | |
| x = np.array([r["ln_1_over_eps"] for r in rows]) | |
| y = np.array([r["n_star_eps_over_d"] for r in rows]) | |
| fit = c6["linear_fit_d32"] | |
| c5sc = [s for s in c5["sample_complexity"] if s["d_gamma"] == 32] | |
| fig, ax = plt.subplots(figsize=(6.4, 4.4)) | |
| ax.plot(x, y, "o", color=ACCENT, ms=8, label="proper learner (measured)") | |
| xs = np.linspace(x.min(), x.max(), 50) | |
| ax.plot( | |
| xs, | |
| fit["slope_vs_ln_1_over_eps"] * xs + fit["intercept"], | |
| "-", | |
| color=ACCENT, | |
| lw=2, | |
| label=f"${fit['slope_vs_ln_1_over_eps']:.2f}\\,\\ln(1/\\epsilon) {fit['intercept']:.2f}$ ($R^2$={fit['r2']:.4f})", | |
| ) | |
| ax.plot( | |
| [math.log(1 / s["eps"]) for s in c5sc], | |
| [s["n_eps_over_d_over_eps"] for s in c5sc], | |
| "s-", | |
| color=ACCENT2, | |
| ms=7, | |
| lw=2, | |
| label="median-of-three (measured), flat at 0.347", | |
| ) | |
| ax.set_xlabel("$\\ln(1/\\epsilon)$") | |
| ax.set_ylabel("$n^*(\\epsilon)\\,\\epsilon\\,/\\,d_\\gamma$") | |
| ax.set_title( | |
| "Thm 3.12: proper learning pays an extra $\\ln(1/\\epsilon)$\n" | |
| "aggregation does not ($d_\\gamma = 32$)", | |
| fontsize=12, | |
| ) | |
| ax.legend(fontsize=10, loc="upper left") | |
| fig.savefig(os.path.join(FIGS, "fig2_proper_log_factor.png")) | |
| plt.close(fig) | |
| # --------------------------------------------------------------------- fig 3 | |
| c2 = load("claim2_thm35.json") | |
| c3 = load("claim3_thm38.json") | |
| c4 = load("claim4_thm310.json") | |
| fig, axes = plt.subplots(1, 3, figsize=(13.5, 3.9)) | |
| g2 = [g for g in c2["grid_exact"] if g["eps"] == 0.01] | |
| axes[0].bar( | |
| [str(g["d_gamma"]) for g in g2], | |
| [g["ratio_E_loss_over_eps"] for g in g2], | |
| color=ACCENT, | |
| ) | |
| axes[0].axhline(1.0, color="k", ls="--", lw=1.5) | |
| axes[0].set_title( | |
| "Thm 3.5 (proper aggregation)\n$E[L]/\\epsilon$ at $n = d_\\gamma/(32\\epsilon)$", | |
| fontsize=11, | |
| ) | |
| axes[0].set_xlabel("$d_\\gamma$") | |
| axes[0].set_ylim(0, 2.4) | |
| g3 = [g for g in c3["grid"] if g["eps"] == 0.01 and g["m_hypotheses"] == 100] | |
| axes[1].bar( | |
| [str(g["d_gamma"]) for g in g3], [g["E_loss_over_eps"] for g in g3], color=ACCENT2 | |
| ) | |
| axes[1].axhline(1.0, color="k", ls="--", lw=1.5) | |
| axes[1].set_title( | |
| "Thm 3.8 (finite interpolating aggregation)\n$E[L]/\\epsilon$ at $n = d_\\gamma/(128\\epsilon)$, $m=100$", | |
| fontsize=11, | |
| ) | |
| axes[1].set_xlabel("$d_\\gamma$") | |
| axes[1].set_ylim(0, 2.4) | |
| g4 = [g for g in c4["grid"] if g["eps"] == 0.02] | |
| labels = [f"{g['n_prime']}/{g['m_hypotheses']}" for g in g4] | |
| axes[2].bar(labels, [g["E_loss"] for g in g4], color="#c9761c") | |
| axes[2].axhline(0.98, color="k", ls="--", lw=1.5) | |
| axes[2].axhline(c4["random_guessing_uniform_loss"], color=ACCENT, ls=":", lw=2) | |
| axes[2].set_ylim(0.8, 1.02) | |
| axes[2].set_title( | |
| "Thm 3.10 ($d_\\gamma=\\infty$, OIG $\\leq$ 3)\n$E[L] \\geq 1-\\epsilon$; dotted = random guessing", | |
| fontsize=11, | |
| ) | |
| axes[2].set_xlabel("$n'$ / $m$") | |
| axes[2].tick_params(axis="x", labelrotation=60, labelsize=8) | |
| fig.tight_layout() | |
| fig.savefig(os.path.join(FIGS, "fig3_lower_bounds.png")) | |
| plt.close(fig) | |
| print("wrote", os.listdir(FIGS)) | |
| # --------------------------------------------------------------------- fig 4 | |
| fig, axes = plt.subplots(1, 2, figsize=(9.2, 3.9)) | |
| sep = c3["separation"] | |
| axes[0].bar([str(s["d_gamma"]) for s in sep], [s["gap_factor"] for s in sep], color=ACCENT) | |
| axes[0].set_xlabel("$d_\\gamma$") | |
| axes[0].set_ylabel("finite-agg loss / general-learner loss") | |
| axes[0].set_title("Thm 3.8 separation grows with $d_\\gamma$\n(at $n = d_\\gamma/(128\\epsilon)$, $\\epsilon = 0.01$)", fontsize=11) | |
| thr = c3["empirical_threshold"] | |
| for d, col in zip((32, 64, 128), (ACCENT, ACCENT2, "#c9761c")): | |
| sub = [t for t in thr if t["d_gamma"] == d] | |
| axes[1].loglog([t["d_gamma"] / t["eps"] for t in sub], [t["n_star"] for t in sub], | |
| "o-", color=col, lw=2, ms=6, label=f"$d_\\gamma$ = {d}") | |
| xs = np.array([500, 15000]) | |
| axes[1].loglog(xs, 0.339 * xs, "--", color=GREY, lw=1.4, label="$0.339\\, d_\\gamma/\\epsilon$") | |
| axes[1].set_xlabel("$d_\\gamma/\\epsilon$") | |
| axes[1].set_ylabel("$n^*(\\epsilon)$ for finite aggregation") | |
| axes[1].set_title("Threshold collapses onto $\\Theta(d_\\gamma/\\epsilon)$\nCV of $n^*\\epsilon/d_\\gamma$ = 1.2%", fontsize=11) | |
| axes[1].legend(fontsize=10) | |
| fig.tight_layout() | |
| fig.savefig(os.path.join(FIGS, "fig4_separation.png")) | |
| plt.close(fig) | |
| print("fig4 done") | |
Xet Storage Details
- Size:
- 6.1 kB
- Xet hash:
- a236843490de4cacea215c0d558fe15934acb56273e601abbfdfef0b3a83697c
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.