regenerate figures as 650-dpi LZW TIFFs, flattened RGB (publication-ready)
Browse files- .gitattributes +7 -0
- paper/figures/fig1_layer_ablation.tiff +3 -0
- paper/figures/fig2_cross_modality.tiff +3 -0
- paper/figures/fig3_pruning_gain.tiff +3 -0
- paper/figures/fig4_floor_ablation.tiff +3 -0
- paper/figures/fig5_conformal.tiff +3 -0
- paper/figures/fig6_cross_objective.tiff +3 -0
- paper/figures/fig7_rank_law.tiff +3 -0
- paper/make_figures.py +20 -10
.gitattributes
CHANGED
|
@@ -33,3 +33,10 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
paper/figures/fig1_layer_ablation.tiff filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
paper/figures/fig2_cross_modality.tiff filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
paper/figures/fig3_pruning_gain.tiff filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
paper/figures/fig4_floor_ablation.tiff filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
paper/figures/fig5_conformal.tiff filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
paper/figures/fig6_cross_objective.tiff filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
paper/figures/fig7_rank_law.tiff filter=lfs diff=lfs merge=lfs -text
|
paper/figures/fig1_layer_ablation.tiff
ADDED
|
|
Git LFS Details
|
paper/figures/fig2_cross_modality.tiff
ADDED
|
|
Git LFS Details
|
paper/figures/fig3_pruning_gain.tiff
ADDED
|
|
Git LFS Details
|
paper/figures/fig4_floor_ablation.tiff
ADDED
|
|
Git LFS Details
|
paper/figures/fig5_conformal.tiff
ADDED
|
|
Git LFS Details
|
paper/figures/fig6_cross_objective.tiff
ADDED
|
|
Git LFS Details
|
paper/figures/fig7_rank_law.tiff
ADDED
|
|
Git LFS Details
|
paper/make_figures.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
-
"""Generate paper figures from the locked gate-report numbers. Self-contained (no bucket).
|
|
|
|
| 3 |
import matplotlib
|
| 4 |
matplotlib.use("Agg")
|
| 5 |
import matplotlib.pyplot as plt
|
|
@@ -8,7 +9,8 @@ from pathlib import Path
|
|
| 8 |
|
| 9 |
OUT = Path(__file__).resolve().parent / "figures"
|
| 10 |
OUT.mkdir(exist_ok=True)
|
| 11 |
-
|
|
|
|
| 12 |
"grid.alpha": 0.3, "axes.axisbelow": True})
|
| 13 |
|
| 14 |
|
|
@@ -28,7 +30,7 @@ def fig1_layer():
|
|
| 28 |
ax.set_ylabel("token-level lesion AUROC (LIDC)"); ax.set_ylim(0.55, 0.92)
|
| 29 |
ax.set_title("Finding 1: lesion signal peaks mid-layer, erodes with depth")
|
| 30 |
ax.legend(loc="lower left", fontsize=8.5)
|
| 31 |
-
fig.tight_layout(); fig.savefig(OUT / "fig1_layer_ablation.
|
| 32 |
|
| 33 |
|
| 34 |
def fig2_crossmodality():
|
|
@@ -51,7 +53,7 @@ def fig2_crossmodality():
|
|
| 51 |
xytext=(-2, -34), ha="center", fontsize=7.5, color="#1f77b4")
|
| 52 |
ax.annotate("attention\n~ chance", (4 + w/2, 0.492), textcoords="offset points",
|
| 53 |
xytext=(2, 4), ha="center", fontsize=7.5, color="#ff7f0e")
|
| 54 |
-
fig.tight_layout(); fig.savefig(OUT / "fig2_cross_modality.
|
| 55 |
|
| 56 |
|
| 57 |
def fig3_pruning_gain():
|
|
@@ -70,7 +72,7 @@ def fig3_pruning_gain():
|
|
| 70 |
ax.set_ylabel("small-lesion recall gain (pts)\nmembership vs saliency pruning")
|
| 71 |
ax.set_title("Finding 3: membership pruning > saliency pruning")
|
| 72 |
ax.legend(loc="upper right", fontsize=9)
|
| 73 |
-
fig.tight_layout(); fig.savefig(OUT / "fig3_pruning_gain.
|
| 74 |
|
| 75 |
|
| 76 |
def fig4_ablation():
|
|
@@ -88,7 +90,7 @@ def fig4_ablation():
|
|
| 88 |
ax.annotate("rank coverage rewards spanning,\nnot lesion concentration",
|
| 89 |
(1 + w, 0.460), textcoords="offset points", xytext=(-6, 18), ha="center",
|
| 90 |
fontsize=7.5, color="#d62728")
|
| 91 |
-
fig.tight_layout(); fig.savefig(OUT / "fig4_floor_ablation.
|
| 92 |
|
| 93 |
|
| 94 |
def fig5_conformal():
|
|
@@ -102,7 +104,7 @@ def fig5_conformal():
|
|
| 102 |
ax.set_xticks(budgets)
|
| 103 |
ax.set_title("Conformal retention certificate\n(valid; honest budget tradeoff)")
|
| 104 |
ax.legend(loc="center right", fontsize=8)
|
| 105 |
-
fig.tight_layout(); fig.savefig(OUT / "fig5_conformal.
|
| 106 |
|
| 107 |
|
| 108 |
def fig6_cross_objective():
|
|
@@ -122,7 +124,7 @@ def fig6_cross_objective():
|
|
| 122 |
ax.legend(loc="upper right", fontsize=8)
|
| 123 |
ax.annotate("MAE flat & low\n(not density-separable)", (9, 0.578),
|
| 124 |
textcoords="offset points", xytext=(0, -28), ha="center", fontsize=7.5, color="#d62728")
|
| 125 |
-
fig.tight_layout(); fig.savefig(OUT / "fig6_cross_objective.
|
| 126 |
|
| 127 |
|
| 128 |
def fig7_law():
|
|
@@ -141,10 +143,18 @@ def fig7_law():
|
|
| 141 |
ax.set_ylabel("fraction of rare signal retained"); ax.set_ylim(0, 1.08)
|
| 142 |
ax.set_title("The law: rank objectives lose rare/low-rank signal")
|
| 143 |
ax.legend(loc="lower right", fontsize=8.5)
|
| 144 |
-
fig.tight_layout(); fig.savefig(OUT / "fig7_rank_law.
|
| 145 |
|
| 146 |
|
| 147 |
for f in (fig1_layer, fig2_crossmodality, fig3_pruning_gain, fig4_ablation, fig5_conformal,
|
| 148 |
fig6_cross_objective, fig7_law):
|
| 149 |
f()
|
| 150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
+
"""Generate paper figures from the locked gate-report numbers. Self-contained (no bucket).
|
| 3 |
+
Output: publication TIFFs at 650 dpi (LZW-compressed)."""
|
| 4 |
import matplotlib
|
| 5 |
matplotlib.use("Agg")
|
| 6 |
import matplotlib.pyplot as plt
|
|
|
|
| 9 |
|
| 10 |
OUT = Path(__file__).resolve().parent / "figures"
|
| 11 |
OUT.mkdir(exist_ok=True)
|
| 12 |
+
DPI = 650
|
| 13 |
+
plt.rcParams.update({"figure.dpi": 150, "savefig.dpi": DPI, "font.size": 11, "axes.grid": True,
|
| 14 |
"grid.alpha": 0.3, "axes.axisbelow": True})
|
| 15 |
|
| 16 |
|
|
|
|
| 30 |
ax.set_ylabel("token-level lesion AUROC (LIDC)"); ax.set_ylim(0.55, 0.92)
|
| 31 |
ax.set_title("Finding 1: lesion signal peaks mid-layer, erodes with depth")
|
| 32 |
ax.legend(loc="lower left", fontsize=8.5)
|
| 33 |
+
fig.tight_layout(); fig.savefig(OUT / "fig1_layer_ablation.tiff", dpi=DPI, pil_kwargs={"compression": "tiff_lzw"}); plt.close(fig)
|
| 34 |
|
| 35 |
|
| 36 |
def fig2_crossmodality():
|
|
|
|
| 53 |
xytext=(-2, -34), ha="center", fontsize=7.5, color="#1f77b4")
|
| 54 |
ax.annotate("attention\n~ chance", (4 + w/2, 0.492), textcoords="offset points",
|
| 55 |
xytext=(2, 4), ha="center", fontsize=7.5, color="#ff7f0e")
|
| 56 |
+
fig.tight_layout(); fig.savefig(OUT / "fig2_cross_modality.tiff", dpi=DPI, pil_kwargs={"compression": "tiff_lzw"}); plt.close(fig)
|
| 57 |
|
| 58 |
|
| 59 |
def fig3_pruning_gain():
|
|
|
|
| 72 |
ax.set_ylabel("small-lesion recall gain (pts)\nmembership vs saliency pruning")
|
| 73 |
ax.set_title("Finding 3: membership pruning > saliency pruning")
|
| 74 |
ax.legend(loc="upper right", fontsize=9)
|
| 75 |
+
fig.tight_layout(); fig.savefig(OUT / "fig3_pruning_gain.tiff", dpi=DPI, pil_kwargs={"compression": "tiff_lzw"}); plt.close(fig)
|
| 76 |
|
| 77 |
|
| 78 |
def fig4_ablation():
|
|
|
|
| 90 |
ax.annotate("rank coverage rewards spanning,\nnot lesion concentration",
|
| 91 |
(1 + w, 0.460), textcoords="offset points", xytext=(-6, 18), ha="center",
|
| 92 |
fontsize=7.5, color="#d62728")
|
| 93 |
+
fig.tight_layout(); fig.savefig(OUT / "fig4_floor_ablation.tiff", dpi=DPI, pil_kwargs={"compression": "tiff_lzw"}); plt.close(fig)
|
| 94 |
|
| 95 |
|
| 96 |
def fig5_conformal():
|
|
|
|
| 104 |
ax.set_xticks(budgets)
|
| 105 |
ax.set_title("Conformal retention certificate\n(valid; honest budget tradeoff)")
|
| 106 |
ax.legend(loc="center right", fontsize=8)
|
| 107 |
+
fig.tight_layout(); fig.savefig(OUT / "fig5_conformal.tiff", dpi=DPI, pil_kwargs={"compression": "tiff_lzw"}); plt.close(fig)
|
| 108 |
|
| 109 |
|
| 110 |
def fig6_cross_objective():
|
|
|
|
| 124 |
ax.legend(loc="upper right", fontsize=8)
|
| 125 |
ax.annotate("MAE flat & low\n(not density-separable)", (9, 0.578),
|
| 126 |
textcoords="offset points", xytext=(0, -28), ha="center", fontsize=7.5, color="#d62728")
|
| 127 |
+
fig.tight_layout(); fig.savefig(OUT / "fig6_cross_objective.tiff", dpi=DPI, pil_kwargs={"compression": "tiff_lzw"}); plt.close(fig)
|
| 128 |
|
| 129 |
|
| 130 |
def fig7_law():
|
|
|
|
| 143 |
ax.set_ylabel("fraction of rare signal retained"); ax.set_ylim(0, 1.08)
|
| 144 |
ax.set_title("The law: rank objectives lose rare/low-rank signal")
|
| 145 |
ax.legend(loc="lower right", fontsize=8.5)
|
| 146 |
+
fig.tight_layout(); fig.savefig(OUT / "fig7_rank_law.tiff", dpi=DPI, pil_kwargs={"compression": "tiff_lzw"}); plt.close(fig)
|
| 147 |
|
| 148 |
|
| 149 |
for f in (fig1_layer, fig2_crossmodality, fig3_pruning_gain, fig4_ablation, fig5_conformal,
|
| 150 |
fig6_cross_objective, fig7_law):
|
| 151 |
f()
|
| 152 |
+
# flatten RGBA -> RGB on white (journals reject alpha/transparency in TIFFs); keep 650 dpi + LZW
|
| 153 |
+
from PIL import Image
|
| 154 |
+
for p in sorted(OUT.glob("*.tiff")):
|
| 155 |
+
im = Image.open(p)
|
| 156 |
+
if im.mode in ("RGBA", "LA", "P"):
|
| 157 |
+
bg = Image.new("RGB", im.size, "white")
|
| 158 |
+
bg.paste(im.convert("RGBA"), mask=im.convert("RGBA").split()[-1])
|
| 159 |
+
bg.save(p, format="TIFF", dpi=(DPI, DPI), compression="tiff_lzw")
|
| 160 |
+
print(f"figures written at {DPI} dpi (TIFF/LZW, RGB):", sorted(p.name for p in OUT.glob("*.tiff")))
|