ahmed-farhanur-rashid commited on
Commit
68d7827
·
verified ·
1 Parent(s): 9eb640a

Upload GradeEye four-ch-morph checkpoints

Browse files
README.md CHANGED
@@ -1,3 +1,56 @@
1
  ---
2
  license: cc-by-nc-4.0
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: cc-by-nc-4.0
3
+ tags:
4
+ - medical-imaging
5
+ - diabetic-retinopathy
6
+ - domain-generalization
7
+ - pytorch
8
+ pipeline_tag: image-classification
9
+ library_name: pytorch
10
  ---
11
+
12
+ # GradeEye four-ch-morph
13
+
14
+ This repository contains GradeEye CORN ordinal diabetic-retinopathy classifiers using the `convnext_tiny` backbone at 384x384 resolution, with 4-channel input and a 4-threshold ordinal head. The `_ema.safetensors` file is the **primary** artifact: the paper's reported evaluation metrics were generated using the EMA state dict. The unsuffixed `.safetensors` file is the corresponding raw `model_state_dict` secondary artifact.
15
+
16
+ ## Checkpoints
17
+
18
+ | Primary EMA weights | Raw secondary weights | Architecture | Held-out fold | Best QWK | Epoch |
19
+ |---|---|---|---|---:|---:|
20
+ | `lodo_aptos_convnext_tiny_best_ema.safetensors` | `lodo_aptos_convnext_tiny_best.safetensors` | `convnext_tiny` | varies | 0.7789 | 12 |
21
+ | `lodo_ddr_convnext_tiny_best_ema.safetensors` | `lodo_ddr_convnext_tiny_best.safetensors` | `convnext_tiny` | varies | 0.7702 | 7 |
22
+ | `lodo_eyepacs_convnext_tiny_best_ema.safetensors` | `lodo_eyepacs_convnext_tiny_best.safetensors` | `convnext_tiny` | varies | 0.7833 | 3 |
23
+ | `lodo_messidor2_convnext_tiny_best_ema.safetensors` | `lodo_messidor2_convnext_tiny_best.safetensors` | `convnext_tiny` | varies | 0.7395 | 2 |
24
+
25
+ ## Preprocessing
26
+
27
+ 1. Resize the RGB fundus image to 384x384 using the same offline preprocessing pipeline.
28
+ 2. Convert RGB to float in [0,1] and apply ImageNet normalization: mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225).
29
+ 3. Append one auxiliary channel in [0,1] after RGB normalization. It is not ImageNet-normalized.
30
+
31
+ ### Fourth-channel construction
32
+
33
+ - Source pool: `segmentation_pooled_morph`.
34
+ - Producer: `gradeeye/seg-unet-bcedice`.
35
+ - Exact method: The BCE+Dice U-Net raw sigmoid probability map after 3x3 elliptical morphological opening, 3x3 elliptical closing, and Gaussian blur (sigma=0.5). It remains a continuous [0,1] soft-probability map, not a morphological gradient or edge channel.
36
+ - The resulting tensor is `(4, 384, 384)` in channel-first layout.
37
+ - This is the only one of the three published four-channel variants with morphological post-processing; it is not a gradient or edge channel.
38
+
39
+ ## Loading
40
+
41
+ ```python
42
+ import json
43
+ from modeling import load_model
44
+
45
+ config = json.load(open('config.json'))
46
+ model = load_model('lodo_eyepacs_convnext_tiny_best_ema.safetensors', config)
47
+ # model(x) returns CORN logits with shape (batch, 4)
48
+ ```
49
+
50
+ Install `torch`, `timm`, and `safetensors`, and make the GradeEye source repository available on `PYTHONPATH`.
51
+
52
+ ## Intended use and limitations
53
+
54
+ These weights are released for research and reproducibility only. They are not validated for clinical diagnosis or treatment decisions. Performance varies substantially by held-out dataset and should not be interpreted as clinical-grade generalization.
55
+
56
+ Source code and paper materials: [https://github.com/ahmed-farhanur-rashid/gradeeye](https://github.com/ahmed-farhanur-rashid/gradeeye).
config.json ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "DRGradingModel",
3
+ "task": "diabetic-retinopathy-grading",
4
+ "architecture": "convnext_tiny",
5
+ "backbone_timm_name": "convnext_tiny",
6
+ "in_channels": 4,
7
+ "image_size": 384,
8
+ "num_classes": 5,
9
+ "ordinal_head": "CORN",
10
+ "num_thresholds": 4,
11
+ "use_cbam": true,
12
+ "cbam_num_stages": 2,
13
+ "head_hidden_dim": 512,
14
+ "dropout": 0.4,
15
+ "channels_last": true,
16
+ "ema_decay": 0.999,
17
+ "loss_type": "corn",
18
+ "use_class_weighting": true,
19
+ "use_mixup": true,
20
+ "primary_weights": "*_best_ema.safetensors (EMA; reproduces paper metrics)",
21
+ "secondary_weights": "*_best.safetensors (raw model_state_dict)",
22
+ "normalization": {
23
+ "rgb": {
24
+ "mean": [
25
+ 0.485,
26
+ 0.456,
27
+ 0.406
28
+ ],
29
+ "std": [
30
+ 0.229,
31
+ 0.224,
32
+ 0.225
33
+ ]
34
+ },
35
+ "extra_channel": "Already in [0,1]; appended after RGB normalization; not ImageNet-normalized."
36
+ },
37
+ "class_names": [
38
+ "No DR",
39
+ "Mild",
40
+ "Moderate",
41
+ "Severe",
42
+ "Proliferative DR"
43
+ ],
44
+ "extra_channel": {
45
+ "source": "segmentation_pooled_morph",
46
+ "producer": "gradeeye/seg-unet-bcedice",
47
+ "description": "The BCE+Dice U-Net raw sigmoid probability map after 3x3 elliptical morphological opening, 3x3 elliptical closing, and Gaussian blur (sigma=0.5). It remains a continuous [0,1] soft-probability map, not a morphological gradient or edge channel."
48
+ }
49
+ }
lodo_aptos_convnext_tiny_best.meta.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "best_metric": 0.7789129817933231,
3
+ "checkpoint_source": "saved/checkpoints/four_ch_morph/lodo_aptos_convnext_tiny_best.pt",
4
+ "class_names": [
5
+ "No DR",
6
+ "Mild",
7
+ "Moderate",
8
+ "Severe",
9
+ "Proliferative DR"
10
+ ],
11
+ "ema_state_dict_present": true,
12
+ "epoch": 12,
13
+ "global_step": 13742,
14
+ "phase_name": "phase2_full_training",
15
+ "primary_weights": "lodo_aptos_convnext_tiny_best_ema.safetensors",
16
+ "primary_weights_kind": "ema",
17
+ "secondary_weights": "lodo_aptos_convnext_tiny_best.safetensors",
18
+ "secondary_weights_kind": "raw_model_state_dict",
19
+ "training_config": {
20
+ "arch": "convnext_tiny",
21
+ "cbam_num_stages": 2,
22
+ "channels_last": true,
23
+ "dropout": 0.4,
24
+ "ema_decay": 0.999,
25
+ "head_hidden_dim": 512,
26
+ "img_size": 384,
27
+ "in_chans": 4,
28
+ "loss_type": "corn",
29
+ "num_thresholds": 4,
30
+ "phase_batches": {
31
+ "phase1_frozen": 64,
32
+ "phase2_full_training": 24
33
+ },
34
+ "use_cbam": true,
35
+ "use_class_weighting": true,
36
+ "use_mixup": true
37
+ }
38
+ }
lodo_aptos_convnext_tiny_best.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b3a79a5c8cc5afec9dad585fd958dc7044acd46ddaf449fbe1703aed558b5c05
3
+ size 113275512
lodo_aptos_convnext_tiny_best_ema.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a53902948ad286ea55ea7ea9eb9ad14981268018c20471fe8f008d5104321c7e
3
+ size 113275512
lodo_ddr_convnext_tiny_best.meta.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "best_metric": 0.7701556000355464,
3
+ "checkpoint_source": "saved/checkpoints/four_ch_morph/lodo_ddr_convnext_tiny_best.pt",
4
+ "class_names": [
5
+ "No DR",
6
+ "Mild",
7
+ "Moderate",
8
+ "Severe",
9
+ "Proliferative DR"
10
+ ],
11
+ "ema_state_dict_present": true,
12
+ "epoch": 7,
13
+ "global_step": 8756,
14
+ "phase_name": "phase2_full_training",
15
+ "primary_weights": "lodo_ddr_convnext_tiny_best_ema.safetensors",
16
+ "primary_weights_kind": "ema",
17
+ "secondary_weights": "lodo_ddr_convnext_tiny_best.safetensors",
18
+ "secondary_weights_kind": "raw_model_state_dict",
19
+ "training_config": {
20
+ "arch": "convnext_tiny",
21
+ "cbam_num_stages": 2,
22
+ "channels_last": true,
23
+ "dropout": 0.4,
24
+ "ema_decay": 0.999,
25
+ "head_hidden_dim": 512,
26
+ "img_size": 384,
27
+ "in_chans": 4,
28
+ "loss_type": "corn",
29
+ "num_thresholds": 4,
30
+ "phase_batches": {
31
+ "phase1_frozen": 64,
32
+ "phase2_full_training": 24
33
+ },
34
+ "use_cbam": true,
35
+ "use_class_weighting": true,
36
+ "use_mixup": true
37
+ }
38
+ }
lodo_ddr_convnext_tiny_best.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ac201739ad9d112366f0c385788035eeff6ae6f4f4b80cb800f156a86c39e2dc
3
+ size 113275512
lodo_ddr_convnext_tiny_best_ema.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:70d5b5ffcf287d5cae291b228d597033cc6d65159b15cfea444ff522cc18b639
3
+ size 113275512
lodo_eyepacs_convnext_tiny_best.meta.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "best_metric": 0.7832504424849636,
3
+ "checkpoint_source": "saved/checkpoints/four_ch_morph/lodo_eyepacs_convnext_tiny_best.pt",
4
+ "class_names": [
5
+ "No DR",
6
+ "Mild",
7
+ "Moderate",
8
+ "Severe",
9
+ "Proliferative DR"
10
+ ],
11
+ "ema_state_dict_present": true,
12
+ "epoch": 3,
13
+ "global_step": 1192,
14
+ "phase_name": "phase2_full_training",
15
+ "primary_weights": "lodo_eyepacs_convnext_tiny_best_ema.safetensors",
16
+ "primary_weights_kind": "ema",
17
+ "secondary_weights": "lodo_eyepacs_convnext_tiny_best.safetensors",
18
+ "secondary_weights_kind": "raw_model_state_dict",
19
+ "training_config": {
20
+ "arch": "convnext_tiny",
21
+ "cbam_num_stages": 2,
22
+ "channels_last": true,
23
+ "dropout": 0.4,
24
+ "ema_decay": 0.999,
25
+ "head_hidden_dim": 512,
26
+ "img_size": 384,
27
+ "in_chans": 4,
28
+ "loss_type": "corn",
29
+ "num_thresholds": 4,
30
+ "phase_batches": {
31
+ "phase1_frozen": 64,
32
+ "phase2_full_training": 24
33
+ },
34
+ "use_cbam": true,
35
+ "use_class_weighting": true,
36
+ "use_mixup": true
37
+ }
38
+ }
lodo_eyepacs_convnext_tiny_best.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:02d454aed62a11bfb7a3a22665806a9bfa91c813698ec3de508c1035d11ccfac
3
+ size 113275512
lodo_eyepacs_convnext_tiny_best_ema.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d5feac6151cda5ec893869c2754066dcf66a4e80071a58fe322d8536815f76d0
3
+ size 113275512
lodo_messidor2_convnext_tiny_best.meta.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "best_metric": 0.7395453178905533,
3
+ "checkpoint_source": "saved/checkpoints/four_ch_morph/lodo_messidor2_convnext_tiny_best.pt",
4
+ "class_names": [
5
+ "No DR",
6
+ "Mild",
7
+ "Moderate",
8
+ "Severe",
9
+ "Proliferative DR"
10
+ ],
11
+ "ema_state_dict_present": true,
12
+ "epoch": 2,
13
+ "global_step": 4789,
14
+ "phase_name": "phase2_full_training",
15
+ "primary_weights": "lodo_messidor2_convnext_tiny_best_ema.safetensors",
16
+ "primary_weights_kind": "ema",
17
+ "secondary_weights": "lodo_messidor2_convnext_tiny_best.safetensors",
18
+ "secondary_weights_kind": "raw_model_state_dict",
19
+ "training_config": {
20
+ "arch": "convnext_tiny",
21
+ "cbam_num_stages": 2,
22
+ "channels_last": true,
23
+ "dropout": 0.4,
24
+ "ema_decay": 0.999,
25
+ "head_hidden_dim": 512,
26
+ "img_size": 384,
27
+ "in_chans": 4,
28
+ "loss_type": "corn",
29
+ "num_thresholds": 4,
30
+ "phase_batches": {
31
+ "phase1_frozen": 64,
32
+ "phase2_full_training": 24
33
+ },
34
+ "use_cbam": true,
35
+ "use_class_weighting": true,
36
+ "use_mixup": true
37
+ }
38
+ }
lodo_messidor2_convnext_tiny_best.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3f0a946bb0ce3e6878c01e51a8e0e9d65c411cb19998ba99c7baf209dfc1b5bb
3
+ size 113275512
lodo_messidor2_convnext_tiny_best_ema.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:10170dc91c2161db9a70059423bf454a0b5490b6e7aac9938f0d23f37d035c5f
3
+ size 113275512
modeling.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Minimal GradeEye classifier loader for Hugging Face Hub.
2
+
3
+ Requires the GradeEye source package on PYTHONPATH plus torch, timm, and
4
+ safetensors. The architecture is the same DRGradingModel used during training.
5
+ EMA files are the primary weights and reproduce the paper evaluation protocol.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+ import sys
11
+ import torch
12
+
13
+ # For local source checkout usage. Users may instead install the GradeEye package.
14
+ try:
15
+ from src.models.dr_model import DRGradingModel
16
+ except ImportError as exc:
17
+ raise ImportError(
18
+ "Install/clone GradeEye and make its repository root available on PYTHONPATH."
19
+ ) from exc
20
+ from safetensors.torch import load_file
21
+
22
+
23
+ def load_model(weights_path: str | Path, config: dict, device: str = "cpu") -> DRGradingModel:
24
+ """Instantiate DRGradingModel and strictly load a .safetensors state dict."""
25
+ model = DRGradingModel(
26
+ pretrained=False,
27
+ use_cbam=config["use_cbam"],
28
+ cbam_num_stages=config["cbam_num_stages"],
29
+ num_thresholds=config["num_thresholds"],
30
+ head_hidden_dim=config["head_hidden_dim"],
31
+ dropout=config["dropout"],
32
+ output_mode="corn",
33
+ arch=config["architecture"],
34
+ in_chans=config["in_channels"],
35
+ img_size=config["image_size"],
36
+ )
37
+ state_dict = load_file(str(weights_path), device="cpu")
38
+ result = model.load_state_dict(state_dict, strict=True)
39
+ if result.missing_keys or result.unexpected_keys:
40
+ raise RuntimeError(
41
+ f"State-dict mismatch: missing={result.missing_keys}, "
42
+ f"unexpected={result.unexpected_keys}"
43
+ )
44
+ return model.to(device).eval()