DoB24 commited on
Commit
b90081d
·
verified ·
1 Parent(s): 15d54e2

Remove non-research HF push helper

Browse files
Files changed (1) hide show
  1. code/push_to_hf.py +0 -611
code/push_to_hf.py DELETED
@@ -1,611 +0,0 @@
1
- #!/usr/bin/env python
2
- """Push complete fundus benchmark to HuggingFace in academic-research format.
3
-
4
- Creates two repos under user DoB24:
5
- - DoB24/fundus-10class-augmented (dataset)
6
- - DoB24/fundus-9model-benchmark (model)
7
-
8
- Uploads: all 9 fine-tuned weights, results JSON, ensemble report, split
9
- manifest, dataset (augmented), comprehensive academic-format README with
10
- methods, results, statistics, citations.
11
- """
12
- from __future__ import annotations
13
-
14
- import json
15
- import os
16
- import shutil
17
- from pathlib import Path
18
-
19
- from huggingface_hub import HfApi, create_repo
20
-
21
- TOKEN = os.environ["HF_TOKEN"]
22
- USER = "DoB24"
23
- MODEL_REPO = f"{USER}/fundus-9model-benchmark"
24
- DATASET_REPO = f"{USER}/fundus-10class-augmented"
25
-
26
- ROOT = Path("/home/bytical/fundus_project")
27
- STAGE = ROOT / "_hf_stage"
28
- STAGE_MODEL = STAGE / "model_repo"
29
- STAGE_DATA = STAGE / "dataset_repo"
30
-
31
- api = HfApi(token=TOKEN)
32
-
33
-
34
- # ---------------------------------------------------------------------------
35
- # 1. Load all results
36
- # ---------------------------------------------------------------------------
37
-
38
- results_dir = ROOT / "final_experiments_all"
39
- report = json.loads((results_dir / "ensemble_report.json").read_text())
40
-
41
- per_model = report["per_model"]
42
- ensemble = report.get("ensemble", {})
43
- mcnemar = report.get("mcnemar_bonferroni", {})
44
- conformal = report.get("conformal", {})
45
-
46
- # Order models by accuracy (desc)
47
- model_order = sorted(per_model.keys(), key=lambda k: -per_model[k]["acc"])
48
-
49
- CLASSES = [
50
- "Central Serous Chorioretinopathy",
51
- "Diabetic Retinopathy",
52
- "Disc Edema",
53
- "Glaucoma",
54
- "Healthy",
55
- "Macular Scar",
56
- "Myopia",
57
- "Pterygium",
58
- "Retinal Detachment",
59
- "Retinitis Pigmentosa",
60
- ]
61
-
62
- # Class counts (augmented dataset)
63
- aug_dir = ROOT / "Database" / "Augmented_Dataset"
64
- class_counts_aug = {}
65
- if aug_dir.exists():
66
- for c in sorted(aug_dir.iterdir()):
67
- if c.is_dir():
68
- class_counts_aug[c.name] = len(list(c.iterdir()))
69
-
70
- orig_dir = ROOT / "Database" / "Original_Dataset"
71
- class_counts_orig = {}
72
- if orig_dir.exists():
73
- for c in sorted(orig_dir.iterdir()):
74
- if c.is_dir():
75
- class_counts_orig[c.name] = len(list(c.iterdir()))
76
-
77
-
78
- # ---------------------------------------------------------------------------
79
- # 2. Build model-repo README (academic paper format)
80
- # ---------------------------------------------------------------------------
81
-
82
- def fmt_pct(x: float) -> str:
83
- return f"{x * 100:.2f}"
84
-
85
-
86
- def model_table_md() -> str:
87
- lines = [
88
- "| Rank | Model | Test Acc (%) | 95% CI | F1 (%) | Kappa | Brier | ROC-AUC |",
89
- "|------|-------|--------------|--------|--------|-------|-------|---------|",
90
- ]
91
- for i, m in enumerate(model_order, 1):
92
- v = per_model[m]
93
- ci = v.get("ci95", [0, 0])
94
- roc = v.get("roc_auc", v.get("roc", 0))
95
- lines.append(
96
- f"| {i} | `{m}` | **{fmt_pct(v['acc'])}** | "
97
- f"[{fmt_pct(ci[0])}, {fmt_pct(ci[1])}] | "
98
- f"{fmt_pct(v['f1'])} | {v.get('kappa', 0):.3f} | "
99
- f"{v.get('brier', 0):.3f} | {roc:.4f} |"
100
- )
101
- if ensemble:
102
- ci = ensemble.get("ci95", [0, 0])
103
- lines.append(
104
- f"| — | **9-Model Ensemble** | **{fmt_pct(ensemble.get('acc', 0))}** | "
105
- f"[{fmt_pct(ci[0])}, {fmt_pct(ci[1])}] | "
106
- f"**{fmt_pct(ensemble.get('f1', 0))}** | — | — | "
107
- f"**{ensemble.get('roc_auc', ensemble.get('roc', 0)):.4f}** |"
108
- )
109
- return "\n".join(lines)
110
-
111
-
112
- def mcnemar_table_md() -> str:
113
- pairs = mcnemar.get("pairs", [])
114
- if not pairs:
115
- return "_McNemar table unavailable._"
116
- lines = [
117
- f"Total pairs: {mcnemar.get('n_pairs', len(pairs))} | Bonferroni-corrected at α=0.05",
118
- "",
119
- "| Model A | Model B | b | c | Raw p | Adj. p (Bonf.) | Sig. |",
120
- "|---------|---------|---|---|-------|----------------|------|",
121
- ]
122
- for r in pairs:
123
- sig = "**\\***" if r.get("sig_005") else " "
124
- lines.append(
125
- f"| `{r['model_a']}` | `{r['model_b']}` | "
126
- f"{r['b_count']} | {r['c_count']} | "
127
- f"{r['p']:.3g} | {r['p_bonferroni']:.3g} | {sig} |"
128
- )
129
- return "\n".join(lines)
130
-
131
-
132
- def class_table_md() -> str:
133
- lines = [
134
- "| # | Class | Original | Augmented |",
135
- "|---|-------|----------|-----------|",
136
- ]
137
- keys = sorted(set(class_counts_orig) | set(class_counts_aug))
138
- total_o = total_a = 0
139
- for i, k in enumerate(keys, 1):
140
- o = class_counts_orig.get(k, 0)
141
- a = class_counts_aug.get(k, 0)
142
- total_o += o
143
- total_a += a
144
- lines.append(f"| {i} | {k} | {o:,} | {a:,} |")
145
- lines.append(f"| — | **Total** | **{total_o:,}** | **{total_a:,}** |")
146
- return "\n".join(lines)
147
-
148
-
149
- readme_model = f"""---
150
- license: apache-2.0
151
- library_name: pytorch
152
- tags:
153
- - medical-imaging
154
- - ophthalmology
155
- - fundus
156
- - image-classification
157
- - retinal-disease
158
- - benchmark
159
- - ensemble
160
- - pytorch
161
- datasets:
162
- - {DATASET_REPO}
163
- metrics:
164
- - accuracy
165
- - f1
166
- - roc-auc
167
- - cohen-kappa
168
- - brier-score
169
- pipeline_tag: image-classification
170
- ---
171
-
172
- # Fundus Lesion Image Classification — 9-Model Comparative Benchmark
173
-
174
- > **Companion artifact for the Master's thesis _"Classification of Fundus
175
- > Lesion Images Using Deep Learning Models"_ (Xidian University, 2026), by
176
- > Daryl Panashe Katiyo.**
177
- >
178
- > Reproducible weights, predictions, and full statistical analysis for nine
179
- > deep-learning backbones evaluated on a 10-class colour-fundus dataset
180
- > with a group-aware (perceptual-hash) test split.
181
-
182
- ---
183
-
184
- ## 1. Abstract
185
-
186
- Automatic interpretation of colour fundus photographs is a foundational
187
- task for screening prevalent blinding diseases such as diabetic
188
- retinopathy, glaucoma and age-related macular degeneration. We
189
- benchmark **nine deep-learning backbones** spanning four architectural
190
- families — classical CNNs (VGG-19, ResNet-50, ResNet-101, DenseNet-121,
191
- Inception-v3), vision-language pretraining (OpenAI CLIP ViT-B/16),
192
- self-supervised vision transformers (DINOv2-L/14), hierarchical
193
- transformers (Swin-B), and a domain-specific MAE pretraining
194
- (RETFound MAE ViT-L/16) — on a 10-class fundus dataset of 16 242
195
- augmented images. To suppress augmentation-induced label leakage we
196
- construct a **group-aware (perceptual-hash) stratified split** and
197
- report bootstrap 95% confidence intervals together with Bonferroni-
198
- corrected McNemar tests and 90% Mondrian conformal sets.
199
-
200
- **Headline result.** A DenseNet-121 trained with CLAHE preprocessing,
201
- RandAugment, weighted sampling, MixUp + CutMix and 6-view test-time
202
- augmentation reaches **{fmt_pct(per_model[model_order[0]]['acc'])}%
203
- accuracy** (F1 = {fmt_pct(per_model[model_order[0]]['f1'])}%, κ =
204
- {per_model[model_order[0]].get('kappa', 0):.3f}) on the held-out test
205
- set. An F1-weighted soft-vote ensemble over all nine models attains
206
- **ROC-AUC = {ensemble.get('roc_auc', ensemble.get('roc', 0)):.4f}**.
207
-
208
- ---
209
-
210
- ## 2. Motivation & Model Selection
211
-
212
- Modern fundus screening pipelines are increasingly built on
213
- pre-trained image backbones, but the question _"which backbone family
214
- is best for fundus disease classification on a moderately-sized,
215
- imbalanced dataset?"_ has no consensus answer. We deliberately chose
216
- backbones that exercise four distinct **inductive biases / pretraining
217
- regimes**:
218
-
219
- | Family | Backbone(s) | Why we included it |
220
- |--------|-------------|--------------------|
221
- | Classical CNNs | VGG-19, ResNet-50, ResNet-101, DenseNet-121, Inception-v3 | Established baselines used in virtually all prior fundus benchmarks ([Gulshan 2016][1], [Ting 2017][2]). Locally-connected convolutions are well-suited to texture-dominant retinal pathology. |
222
- | Vision-language (CLIP) | OpenAI CLIP ViT-B/16 | Tests whether 400 M-pair web-scale contrastive pretraining transfers to a tightly-constrained medical domain. |
223
- | Self-supervised ViT | DINOv2-L/14 | State-of-the-art general-purpose features without language supervision ([Oquab 2024][3]); reportedly strong on dense prediction. |
224
- | Hierarchical ViT | Swin-B | Adds hierarchy + shifted windows; competitive on ImageNet at lower compute than ViT-L ([Liu 2021][4]). |
225
- | Domain MAE | RETFound MAE ViT-L/16 | Pretrained on **1.6 M colour fundus images** ([Zhou 2023, Nature][5]); the strongest published prior on this exact modality, so essential to compare against. |
226
-
227
- This grid lets us isolate three confounders: (i) **scale**
228
- (ResNet-50 vs ResNet-101, ViT-B vs ViT-L); (ii) **modality of
229
- pretraining** (ImageNet supervised vs CLIP language-supervised vs
230
- DINOv2 self-supervised vs RETFound domain-MAE); and (iii)
231
- **architecture class** (CNN vs ViT vs hierarchical).
232
-
233
- ---
234
-
235
- ## 3. Dataset
236
-
237
- - **Source.** Fundus-image set of [Mendeley Data][6] (10 classes; 5 335 original images).
238
- - **Augmentation.** Class-balancing augmentation expanded the training pool to
239
- 16 242 images (rotation, horizontal flip, brightness/contrast jitter,
240
- Gaussian blur). Augmented images carry the same diagnostic label as
241
- their source image.
242
- - **Companion dataset on the Hub:** [{DATASET_REPO}](https://huggingface.co/datasets/{DATASET_REPO}).
243
-
244
- ### Class distribution
245
-
246
- {class_table_md()}
247
-
248
- ### Group-aware splitting (data-leakage prevention)
249
-
250
- Because the augmented set contains visually-near-duplicate copies of
251
- each original image, a naïve `train_test_split` over the augmented
252
- pool would let the model memorise patient-level identities. We
253
- therefore:
254
-
255
- 1. Compute a 64-bit perceptual hash (`pHash`) on every image (original + augmented).
256
- 2. Link each augmented image to its nearest original at Hamming distance ≤ 8 → defines a `group_id`.
257
- 3. Run scikit-learn `StratifiedGroupKFold` (k = 5, only fold-0 used here) so that **all augmented children of a given original sit in exactly one split**.
258
-
259
- The final splits are 15 068 train / 3 301 val / 3 208 test. All metrics
260
- reported below are on the held-out test split. The exact manifest
261
- (`holdout_split_augmented.json`, 3.2 MB) is included in this repo.
262
-
263
- ---
264
-
265
- ## 4. Training Protocol (CNN backbones)
266
-
267
- | Hyper-parameter | Value |
268
- |-----------------|-------|
269
- | Optimizer | AdamW (β=0.9/0.999, weight-decay 1e-4) |
270
- | Initial LR | 2e-4 (head LR 1e-3 for the foundation models, body LR 1e-5 for full-FT) |
271
- | Schedule | 3-epoch linear warm-up + cosine decay |
272
- | Epochs | up to 60 (CNNs/CLIP), 20 LP + 15 FT (DINOv2/Swin/RETFound) |
273
- | Early stopping | patience = 12 (CNNs), 8 (FMs), on val F1 |
274
- | Batch size | 32 (CNNs), 24 (foundation models) |
275
- | Image size | 224 × 224 (Inception-v3 = 299) |
276
- | Preprocessing | CLAHE on LAB L-channel → RandAugment (n=2, m=9) → ImageNet normalisation |
277
- | Imbalance handling | `WeightedRandomSampler` with weights ∝ 1 / class_count |
278
- | Regularisation | MixUp (α=0.2) + CutMix (α=1.0), applied with p=0.7 |
279
- | Mixed precision | `torch.amp.autocast('cuda')` + `GradScaler` |
280
- | Test-time aug | 6 views (centre + 4 corners + horizontal flip), soft-vote |
281
- | Backend | PyTorch 2.11 + CUDA 12.8, 1 × NVIDIA Tesla T4 (16 GB) |
282
-
283
- For DINOv2-L, Swin-B and RETFound we use a **two-stage** schedule:
284
- linear-probe (head only) for 20 epochs at LR 1e-3, then full
285
- fine-tuning for 15 epochs at backbone-LR 1e-5 / head-LR 1e-4.
286
-
287
- ---
288
-
289
- ## 5. Results
290
-
291
- ### 5.1 Headline accuracy
292
-
293
- {model_table_md()}
294
-
295
- CI columns are non-parametric percentile bootstrap (n = 1 000 resamples)
296
- on the test set.
297
-
298
- ### 5.2 Pairwise statistical significance — McNemar with Bonferroni
299
-
300
- We compare every pair of models on per-sample errors. With 9 models
301
- that is 36 pairs (subset shown below; full table in
302
- `ensemble_report.json` → `mcnemar_bonferroni`):
303
-
304
- {mcnemar_table_md()}
305
-
306
- A `*` indicates Bonferroni-corrected significance at α=0.05.
307
- The cluster of CNN models (VGG-19, ResNet-50/101, DenseNet-121,
308
- Inception-v3) is **statistically indistinguishable** from each other
309
- and from DINOv2-L; CLIP, Swin-B and RETFound all separate
310
- significantly (worse) from this top cluster.
311
-
312
- ### 5.3 Conformal sets (90% coverage, Mondrian per class)
313
-
314
- Conformal-prediction sets were computed per class on the validation
315
- split and applied to the test split (Mondrian variant; see
316
- `ensemble_report.json` → `conformal`). Average set size for the
317
- ensemble is < 1.5 at α = 0.1 for most classes, indicating well-
318
- calibrated predictive intervals.
319
-
320
- ### 5.4 Take-aways
321
-
322
- 1. **DenseNet-121 wins on raw accuracy** but is statistically tied with VGG-19, ResNet-101, Inception-v3, ResNet-50 and **DINOv2-L** (adj. p ≥ 0.66 on all such pairs).
323
- 2. **DINOv2-L is the best transformer** (89.50%), confirming general-purpose self-supervised features now match domain CNNs on fundus.
324
- 3. **RETFound under-performs** (83.88%) on this benchmark. Its linear-probe stage saturates at ≈ 47% val-acc, suggesting that 15 epochs of full fine-tuning at LR 1e-5 are insufficient to recover the highly-specialised MAE representation under our augmentation regime. A longer FT schedule (e.g. 50 epochs with discriminative LRs) is a likely fix and is left as future work.
325
- 4. **The 9-model ensemble does not dominate** the best individual model on accuracy because the CNN cluster is heavily correlated — but its **ROC-AUC of {ensemble.get('roc_auc', ensemble.get('roc', 0)):.4f}** is the highest of any reported system, useful for thresholded screening deployment.
326
-
327
- ---
328
-
329
- ## 6. Reproducibility
330
-
331
- All training scripts, evaluation utilities and the launch orchestrator
332
- are in [`comparison_experiment/`](./comparison_experiment) (mirrored
333
- from the [GitHub repo](https://github.com/) — please open an Issue if
334
- you need access to the private mirror).
335
-
336
- ### Quick start (PyTorch ≥ 2.6)
337
-
338
- ```python
339
- import torch, timm
340
- from huggingface_hub import hf_hub_download
341
-
342
- ckpt = hf_hub_download("{MODEL_REPO}", "weights/densenet121_v2_final.pth")
343
- model = timm.create_model("densenet121", num_classes=10)
344
- state = torch.load(ckpt, map_location="cpu", weights_only=False)
345
- model.load_state_dict(state["model"] if "model" in state else state)
346
- model.eval()
347
- ```
348
-
349
- For RETFound and DINOv2-L the same pattern works; the backbones must
350
- first be created via `torch.hub.load('facebookresearch/dinov2', 'dinov2_vitl14')`
351
- and `timm.create_model('vit_large_patch16_224', pretrained=False)` respectively.
352
-
353
- ---
354
-
355
- ## 7. Files in this repository
356
-
357
- | Path | Description |
358
- |------|-------------|
359
- | `weights/<model>_v2_final.pth` (×9) | Final fine-tuned weights, dict with keys `model`/`optimizer`/`epoch` |
360
- | `results/<model>_test.json` (×9) | Per-model test metrics (acc, F1, κ, Brier, ROC-AUC, per-class) |
361
- | `results/<model>_test_preds.json` (×9) | Per-sample test predictions & soft probabilities |
362
- | `results/ensemble_report.json` | Combined per-model + ensemble + McNemar + conformal report |
363
- | `splits/holdout_split_augmented.json` | Full pHash-grouped 5-fold manifest (3.2 MB) |
364
- | `code/` | Training, evaluation and ensemble scripts (frozen snapshot) |
365
-
366
- ---
367
-
368
- ## 8. Citation
369
-
370
- ```bibtex
371
- @mastersthesis{{katiyo2026fundus,
372
- author = {{Katiyo, Daryl Panashe}},
373
- title = {{Classification of Fundus Lesion Images Using Deep Learning Models}},
374
- school = {{Xidian University}},
375
- year = {{2026}},
376
- note = {{Companion artifact: \\url{{https://huggingface.co/{MODEL_REPO}}}}}
377
- }}
378
- ```
379
-
380
- If you use the augmented data split, please also cite the source dataset:
381
-
382
- ```bibtex
383
- @dataset{{nayan2023fundus,
384
- author = {{Nayan, Asma U. and Saha, Sajib K. et al.}},
385
- title = {{A Curated Dataset of Retinal Fundus Images for Disease Classification}},
386
- year = {{2023}},
387
- doi = {{10.17632/s9bfhswzjb.1}},
388
- url = {{https://data.mendeley.com/datasets/s9bfhswzjb/1}}
389
- }}
390
- ```
391
-
392
- ---
393
-
394
- ## 9. References
395
-
396
- [1]: https://doi.org/10.1001/jama.2016.17216
397
- [2]: https://doi.org/10.1001/jama.2017.18152
398
- [3]: https://arxiv.org/abs/2304.07193
399
- [4]: https://arxiv.org/abs/2103.14030
400
- [5]: https://www.nature.com/articles/s41586-023-06555-x
401
- [6]: https://data.mendeley.com/datasets/s9bfhswzjb/1
402
-
403
- 1. **Gulshan V., Peng L., et al.** "Development and Validation of a Deep Learning Algorithm for Detection of Diabetic Retinopathy in Retinal Fundus Photographs." *JAMA* 316.22 (2016): 2402-2410.
404
- 2. **Ting D.S.W., Cheung C.Y., et al.** "Development and Validation of a Deep Learning System for Diabetic Retinopathy and Related Eye Diseases Using Retinal Images From Multiethnic Populations With Diabetes." *JAMA* 318.22 (2017): 2211-2223.
405
- 3. **Oquab M., Darcet T., et al.** "DINOv2: Learning Robust Visual Features without Supervision." arXiv:2304.07193 (2023).
406
- 4. **Liu Z., Lin Y., et al.** "Swin Transformer: Hierarchical Vision Transformer using Shifted Windows." ICCV 2021.
407
- 5. **Zhou Y., Chia M.A., et al.** "A foundation model for generalizable disease detection from retinal images." *Nature* 622 (2023): 156-163.
408
- 6. **He K., et al.** "Deep Residual Learning for Image Recognition." CVPR 2016.
409
- 7. **Simonyan K., Zisserman A.** "Very Deep Convolutional Networks for Large-Scale Image Recognition." ICLR 2015.
410
- 8. **Huang G., et al.** "Densely Connected Convolutional Networks." CVPR 2017.
411
- 9. **Szegedy C., et al.** "Rethinking the Inception Architecture for Computer Vision." CVPR 2016.
412
- 10. **Radford A., et al.** "Learning Transferable Visual Models From Natural Language Supervision." ICML 2021.
413
- 11. **Zhang H., et al.** "mixup: Beyond Empirical Risk Minimization." ICLR 2018.
414
- 12. **Yun S., et al.** "CutMix: Regularization Strategy to Train Strong Classifiers." ICCV 2019.
415
- 13. **Cubuk E.D., et al.** "RandAugment: Practical Automated Data Augmentation." NeurIPS 2020.
416
- 14. **Vovk V., Gammerman A., Shafer G.** "Algorithmic Learning in a Random World." Springer, 2005. *(Conformal prediction)*
417
- 15. **Bonferroni C.E.** "Teoria statistica delle classi e calcolo delle probabilità." 1936.
418
-
419
- ---
420
-
421
- ## 10. License & contact
422
-
423
- Apache-2.0 for code and weights. Original Mendeley dataset retains its
424
- own licence (CC BY 4.0).
425
-
426
- Questions / collaboration: open an issue on the Hub repo.
427
- """
428
-
429
- # ---------------------------------------------------------------------------
430
- # 3. Build dataset-repo README
431
- # ---------------------------------------------------------------------------
432
-
433
- readme_dataset = f"""---
434
- license: cc-by-4.0
435
- task_categories:
436
- - image-classification
437
- language:
438
- - en
439
- tags:
440
- - medical-imaging
441
- - ophthalmology
442
- - fundus
443
- - retinal-disease
444
- size_categories:
445
- - 10K<n<100K
446
- ---
447
-
448
- # Fundus 10-Class Augmented Dataset
449
-
450
- > Augmented and split-curated version of the [Mendeley fundus
451
- > dataset](https://data.mendeley.com/datasets/s9bfhswzjb/1), used in
452
- > the Master's thesis _"Classification of Fundus Lesion Images Using
453
- > Deep Learning Models"_ (Xidian University, 2026).
454
- >
455
- > Models trained on this dataset: [{MODEL_REPO}](https://huggingface.co/{MODEL_REPO}).
456
-
457
- ## Summary
458
-
459
- - **10 disease classes** (see table below).
460
- - **{sum(class_counts_orig.values()):,} original** images; **{sum(class_counts_aug.values()):,} augmented** images (rotation, flip, brightness/contrast jitter, Gaussian blur).
461
- - **Group-aware test split** (`splits/holdout_split_augmented.json`): augmented children of every original image are confined to a single split, eliminating intra-patient leakage.
462
- - Image size: variable (resized to 224×224 / 299×299 during training).
463
-
464
- ## Class distribution
465
-
466
- {class_table_md()}
467
-
468
- ## File layout
469
-
470
- ```
471
- images/augmented/<class_name>/<file>.jpg # 16K augmented imgs
472
- images/original/<class_name>/<file>.jpg # 5K original imgs
473
- splits/holdout_split_augmented.json # pHash-grouped 5-fold manifest
474
- ```
475
-
476
- ## Group-aware splitting
477
-
478
- We computed a 64-bit perceptual hash (`pHash`) per image and linked
479
- each augmented image to its nearest original at Hamming distance ≤ 8
480
- to define a `group_id`. A `StratifiedGroupKFold` (k=5, fold-0
481
- reported) produced 15 068 train / 3 301 val / 3 208 test images.
482
-
483
- ## Loading
484
-
485
- ```python
486
- from datasets import load_dataset
487
- ds = load_dataset("{DATASET_REPO}")
488
- ```
489
-
490
- Or download manually:
491
-
492
- ```python
493
- from huggingface_hub import snapshot_download
494
- snapshot_download("{DATASET_REPO}", repo_type="dataset", local_dir="./fundus_data")
495
- ```
496
-
497
- ## Source & license
498
-
499
- Original images: [Mendeley dataset DOI 10.17632/s9bfhswzjb.1](https://data.mendeley.com/datasets/s9bfhswzjb/1) — **CC BY 4.0**.
500
- Augmented derivatives inherit CC BY 4.0.
501
-
502
- ## Citation
503
-
504
- ```bibtex
505
- @dataset{{nayan2023fundus,
506
- title = {{A Curated Dataset of Retinal Fundus Images for Disease Classification}},
507
- year = {{2023}},
508
- doi = {{10.17632/s9bfhswzjb.1}},
509
- url = {{https://data.mendeley.com/datasets/s9bfhswzjb/1}}
510
- }}
511
- ```
512
- """
513
-
514
-
515
- # ---------------------------------------------------------------------------
516
- # 4. Stage files
517
- # ---------------------------------------------------------------------------
518
-
519
- print("[stage] cleaning stage dir")
520
- if STAGE.exists():
521
- shutil.rmtree(STAGE)
522
- STAGE_MODEL.mkdir(parents=True)
523
- STAGE_DATA.mkdir(parents=True)
524
-
525
- # --- model repo staging ---
526
- (STAGE_MODEL / "weights").mkdir()
527
- (STAGE_MODEL / "results").mkdir()
528
- (STAGE_MODEL / "splits").mkdir()
529
- (STAGE_MODEL / "code").mkdir()
530
-
531
- for src in (ROOT / "weights_v2").glob("*.pth"):
532
- print(f"[stage] copy weight {src.name}")
533
- shutil.copy(src, STAGE_MODEL / "weights" / src.name)
534
- for src in (ROOT / "weights_v3").glob("*.pth"):
535
- print(f"[stage] copy weight {src.name}")
536
- shutil.copy(src, STAGE_MODEL / "weights" / src.name)
537
-
538
- for src in (ROOT / "final_experiments_all").iterdir():
539
- if src.is_file():
540
- shutil.copy(src, STAGE_MODEL / "results" / src.name)
541
-
542
- split_src = ROOT / "holdout_split_augmented.json"
543
- if split_src.exists():
544
- shutil.copy(split_src, STAGE_MODEL / "splits" / split_src.name)
545
-
546
- code_src = ROOT / "comparison_experiment"
547
- if code_src.exists():
548
- for f in code_src.iterdir():
549
- if f.is_file() and f.suffix in (".py", ".sh"):
550
- shutil.copy(f, STAGE_MODEL / "code" / f.name)
551
-
552
- (STAGE_MODEL / "README.md").write_text(readme_model)
553
-
554
- # --- dataset repo staging (symlinks to save copy time / disk) ---
555
- (STAGE_DATA / "images" / "augmented").mkdir(parents=True)
556
- (STAGE_DATA / "images" / "original").mkdir(parents=True)
557
- (STAGE_DATA / "splits").mkdir()
558
-
559
- if aug_dir.exists():
560
- for c in aug_dir.iterdir():
561
- if c.is_dir():
562
- target = STAGE_DATA / "images" / "augmented" / c.name
563
- if not target.exists():
564
- target.symlink_to(c.resolve())
565
-
566
- if orig_dir.exists():
567
- for c in orig_dir.iterdir():
568
- if c.is_dir():
569
- target = STAGE_DATA / "images" / "original" / c.name
570
- if not target.exists():
571
- target.symlink_to(c.resolve())
572
-
573
- if split_src.exists():
574
- shutil.copy(split_src, STAGE_DATA / "splits" / split_src.name)
575
-
576
- (STAGE_DATA / "README.md").write_text(readme_dataset)
577
-
578
-
579
- # ---------------------------------------------------------------------------
580
- # 5. Create repos
581
- # ---------------------------------------------------------------------------
582
-
583
- print(f"[hf] create_repo {MODEL_REPO}")
584
- create_repo(MODEL_REPO, repo_type="model", exist_ok=True, token=TOKEN)
585
- print(f"[hf] create_repo {DATASET_REPO}")
586
- create_repo(DATASET_REPO, repo_type="dataset", exist_ok=True, token=TOKEN)
587
-
588
-
589
- # ---------------------------------------------------------------------------
590
- # 6. Upload
591
- # ---------------------------------------------------------------------------
592
-
593
- print(f"[hf] upload model repo from {STAGE_MODEL}")
594
- api.upload_folder(
595
- folder_path=str(STAGE_MODEL),
596
- repo_id=MODEL_REPO,
597
- repo_type="model",
598
- commit_message="Add 9-model fundus benchmark: weights + results + splits + code + README",
599
- )
600
-
601
- print(f"[hf] upload dataset repo from {STAGE_DATA}")
602
- api.upload_folder(
603
- folder_path=str(STAGE_DATA),
604
- repo_id=DATASET_REPO,
605
- repo_type="dataset",
606
- commit_message="Add fundus 10-class augmented dataset + pHash-grouped split",
607
- )
608
-
609
- print("\n=== DONE ===")
610
- print(f"Model: https://huggingface.co/{MODEL_REPO}")
611
- print(f"Dataset: https://huggingface.co/datasets/{DATASET_REPO}")