8a136c71fe
- Created `classification.py` for comparing image-level and patient-level classification results using various CNN models. - Implemented `create_patient_groups.py` to extract features, generate PCA/t-SNE plots, and identify patient groups via K-means clustering. - Added `figure6.py` to generate boxplots for test accuracy across multiple seeds. - Developed `simple_patient_tsne.py` to perform t-SNE visualization of patient groups and save results in a manifest file. - Introduced `simple_patient_manifest.csv` to store patient IDs, classes, image counts, and associated images.
98 lines
3.1 KiB
Python
98 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
figure6.py — test accuracy boxplots across 20 seeds (Figure 6).
|
|
|
|
Usage:
|
|
conda activate fundus_imaging
|
|
python scripts/figure6.py
|
|
"""
|
|
|
|
import os, sys, json
|
|
import numpy as np
|
|
import matplotlib
|
|
matplotlib.use("Agg")
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.patches import Patch
|
|
from tqdm import tqdm
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from classes import PatientLeakageClassifier
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
MODELS = ["VGG16", "DenseNet121", "EfficientNetB1", "MobileNetV2", "ResNet50"]
|
|
SEEDS = list(range(1, 21))
|
|
|
|
clf = PatientLeakageClassifier(
|
|
os.path.join(ROOT, "results", "simple_patient_manifest.csv"),
|
|
os.path.join(ROOT, "features"),
|
|
n_jobs=8)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Run all seeds
|
|
# ---------------------------------------------------------------------------
|
|
|
|
all_data = [] # flat list of per-run dicts for raw data file
|
|
|
|
for name in tqdm(MODELS, desc="Models"):
|
|
for seed in tqdm(SEEDS, desc=f" {name} seeds", leave=False):
|
|
for stype in ["image", "patient"]:
|
|
r = clf.run(name, seed, stype)
|
|
all_data.append(r)
|
|
|
|
# Save raw data
|
|
with open(os.path.join(ROOT, "results", "figure6_data.json"), "w") as f:
|
|
json.dump(all_data, f, indent=2)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Build per-model accuracy lists
|
|
# ---------------------------------------------------------------------------
|
|
|
|
accs = {} # model -> {image: [accs], patient: [accs]}
|
|
for name in MODELS:
|
|
accs[name] = {"image": [], "patient": []}
|
|
for r in all_data:
|
|
accs[r["model"]][r["split_type"]].append(r["test"])
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Plot
|
|
# ---------------------------------------------------------------------------
|
|
|
|
fig, ax = plt.subplots(1, 1, figsize=(10, 6))
|
|
|
|
for i, name in enumerate(MODELS):
|
|
pos_img = i * 2 + 0.7
|
|
pos_pat = i * 2 + 1.3
|
|
|
|
for pos, stype, color in [(pos_img, "image", '#4C9BD4'),
|
|
(pos_pat, "patient", '#6DBF6D')]:
|
|
data = accs[name][stype]
|
|
bp = ax.boxplot(data, positions=[pos], widths=0.5,
|
|
patch_artist=True, showfliers=True,
|
|
flierprops=dict(marker='o', markersize=3))
|
|
bp['boxes'][0].set_facecolor(color)
|
|
med = np.median(data)
|
|
ax.annotate(f"{med:.3f}", (pos, med), fontsize=6,
|
|
ha='center', va='bottom')
|
|
|
|
ax.legend(handles=[
|
|
Patch(facecolor='#4C9BD4', label='Image-level split'),
|
|
Patch(facecolor='#6DBF6D', label='Patient-level split'),
|
|
], loc='lower right')
|
|
|
|
ax.set_xticks([p + 1 for p in range(0, len(MODELS) * 2, 2)])
|
|
ax.set_xticklabels(MODELS)
|
|
ax.set_ylabel("Test accuracy")
|
|
ax.set_title("Figure 6 — Image-level vs Patient-level test accuracy (20 seeds)")
|
|
ax.set_ylim(0.7, 1.02)
|
|
ax.grid(axis='y', alpha=0.3)
|
|
|
|
plt.tight_layout()
|
|
out = os.path.join(ROOT, "plots", "figure6.png")
|
|
plt.savefig(out, dpi=150)
|
|
plt.close()
|
|
|
|
print(f"\nSaved → {out}")
|
|
print(f"Saved → {ROOT}/results/figure6_data.json")
|
|
print("DONE")
|