Hou commited on
Commit
ebbe758
·
1 Parent(s): 4b9b326
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .idea
app.py ADDED
@@ -0,0 +1,465 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+
3
+ import argparse
4
+ from pathlib import Path
5
+
6
+ import gradio as gr
7
+ import numpy as np
8
+ import pandas as pd
9
+ from PIL import Image
10
+
11
+ import torch
12
+ import torch.nn.functional as F
13
+
14
+ from augmentations import get_val_transforms
15
+ from model import DeepSeeNet
16
+
17
+
18
+ ID_TO_LABEL = {
19
+ 0: "No DR",
20
+ 1: "Mild NPDR",
21
+ 2: "Moderate NPDR",
22
+ 3: "Severe NPDR",
23
+ 4: "PDR",
24
+ }
25
+
26
+
27
+ BUCKET_MODE_DESCRIPTIONS = {
28
+ "severity": "5-class DR severity",
29
+ "referral": "Non-referable vs referable DR",
30
+ "any_dr": "No DR vs any DR",
31
+ "stage": "No DR vs NPDR vs PDR",
32
+ "high_risk": "Non-severe vs severe/PDR",
33
+ }
34
+
35
+
36
+ class AlbumentationsTransform:
37
+ def __init__(self, transform):
38
+ self.transform = transform
39
+
40
+ def __call__(self, image):
41
+ return self.transform(image=np.asarray(image))["image"]
42
+
43
+
44
+ def unwrap_logits(output):
45
+ if isinstance(output, (tuple, list)):
46
+ return output[0]
47
+ return output
48
+
49
+
50
+ def find_fold_checkpoints(checkpoint_dir, checkpoint_name="best_model_only.pt"):
51
+ checkpoint_dir = Path(checkpoint_dir)
52
+
53
+ if not checkpoint_dir.exists():
54
+ raise FileNotFoundError(f"Checkpoint directory not found: {checkpoint_dir}")
55
+
56
+ paths = sorted(checkpoint_dir.glob(f"*_fold*/{checkpoint_name}"))
57
+
58
+ if len(paths) == 0:
59
+ # fallback: allow directly passing a folder containing checkpoints
60
+ paths = sorted(checkpoint_dir.glob(f"**/{checkpoint_name}"))
61
+
62
+ if len(paths) == 0:
63
+ raise FileNotFoundError(
64
+ f"No fold checkpoints named '{checkpoint_name}' found under {checkpoint_dir}"
65
+ )
66
+
67
+ return paths
68
+
69
+
70
+ def load_single_model(checkpoint_path, backbone, image_size, device):
71
+ checkpoint_path = Path(checkpoint_path)
72
+ ckpt = torch.load(checkpoint_path, map_location=device)
73
+
74
+ saved_args = ckpt.get("args", {})
75
+ backbone = saved_args.get("backbone", backbone)
76
+ image_size = int(saved_args.get("image_size", image_size))
77
+
78
+ model = DeepSeeNet(
79
+ n_classes=5,
80
+ backbone=backbone,
81
+ pretrained=False,
82
+ freeze_backbone=False,
83
+ )
84
+
85
+ model.load_state_dict(ckpt["model"], strict=True)
86
+ model.to(device)
87
+ model.eval()
88
+
89
+ return model, backbone, image_size, ckpt
90
+
91
+
92
+ def load_ensemble(checkpoint_dir, checkpoint_name, backbone, image_size, device):
93
+ checkpoint_paths = find_fold_checkpoints(
94
+ checkpoint_dir=checkpoint_dir,
95
+ checkpoint_name=checkpoint_name,
96
+ )
97
+
98
+ models = []
99
+ ckpts = []
100
+ resolved_backbone = backbone
101
+ resolved_image_size = image_size
102
+
103
+ for path in checkpoint_paths:
104
+ model, resolved_backbone, resolved_image_size, ckpt = load_single_model(
105
+ checkpoint_path=path,
106
+ backbone=resolved_backbone,
107
+ image_size=resolved_image_size,
108
+ device=device,
109
+ )
110
+
111
+ models.append(model)
112
+ ckpts.append(ckpt)
113
+
114
+ transform = AlbumentationsTransform(get_val_transforms(resolved_image_size))
115
+
116
+ return models, transform, resolved_backbone, resolved_image_size, checkpoint_paths, ckpts
117
+
118
+
119
+ def get_grouped_probs(probs, mode):
120
+ p0, p1, p2, p3, p4 = probs
121
+
122
+ if mode == "severity":
123
+ return {
124
+ "No DR": float(p0),
125
+ "Mild NPDR": float(p1),
126
+ "Moderate NPDR": float(p2),
127
+ "Severe NPDR": float(p3),
128
+ "PDR": float(p4),
129
+ }
130
+
131
+ if mode == "referral":
132
+ return {
133
+ "Non-referable DR": float(p0 + p1),
134
+ "Referable DR": float(p2 + p3 + p4),
135
+ }
136
+
137
+ if mode == "any_dr":
138
+ return {
139
+ "No DR": float(p0),
140
+ "Any DR": float(p1 + p2 + p3 + p4),
141
+ }
142
+
143
+ if mode == "stage":
144
+ return {
145
+ "No DR": float(p0),
146
+ "NPDR": float(p1 + p2 + p3),
147
+ "PDR": float(p4),
148
+ }
149
+
150
+ if mode == "high_risk":
151
+ return {
152
+ "Non-severe / non-PDR": float(p0 + p1 + p2),
153
+ "Severe NPDR or PDR": float(p3 + p4),
154
+ }
155
+
156
+ raise ValueError(f"Unknown bucket mode: {mode}")
157
+
158
+
159
+ def grouped_probs_to_fixed_dataframe(prob_dict, max_rows=5):
160
+ rows = [
161
+ {
162
+ "label": label,
163
+ "probability": f"{prob:.4f}",
164
+ }
165
+ for label, prob in prob_dict.items()
166
+ ]
167
+
168
+ rows = sorted(rows, key=lambda x: float(x["probability"]), reverse=True)
169
+
170
+ while len(rows) < max_rows:
171
+ rows.append({"label": "", "probability": ""})
172
+
173
+ return pd.DataFrame(rows[:max_rows], columns=["label", "probability"])
174
+
175
+
176
+ def fold_probs_to_dataframe(fold_probs, checkpoint_paths):
177
+ rows = []
178
+
179
+ for i, probs in enumerate(fold_probs):
180
+ pred_grade = int(np.argmax(probs))
181
+ pred_label = ID_TO_LABEL[pred_grade]
182
+
183
+ rows.append(
184
+ {
185
+ "fold": Path(checkpoint_paths[i]).parent.name,
186
+ "prediction": pred_label,
187
+ "probability": f"{float(probs[pred_grade]):.4f}",
188
+ "p0_no_dr": f"{float(probs[0]):.4f}",
189
+ "p1_mild": f"{float(probs[1]):.4f}",
190
+ "p2_moderate": f"{float(probs[2]):.4f}",
191
+ "p3_severe": f"{float(probs[3]):.4f}",
192
+ "p4_pdr": f"{float(probs[4]):.4f}",
193
+ }
194
+ )
195
+
196
+ return pd.DataFrame(rows)
197
+
198
+
199
+ def ensemble_predict(models, x):
200
+ fold_probs = []
201
+
202
+ with torch.no_grad():
203
+ for model in models:
204
+ logits = unwrap_logits(model(x))
205
+ probs = F.softmax(logits, dim=1)[0].detach().cpu().numpy()
206
+ fold_probs.append(probs)
207
+
208
+ fold_probs = np.stack(fold_probs, axis=0)
209
+ ensemble_probs = fold_probs.mean(axis=0)
210
+
211
+ return ensemble_probs, fold_probs
212
+
213
+
214
+ def make_prediction_fn(models, transform, checkpoint_paths, device):
215
+ def predict(image, bucket_mode):
216
+ empty_df = pd.DataFrame(
217
+ [{"label": "", "probability": ""} for _ in range(5)],
218
+ columns=["label", "probability"],
219
+ )
220
+
221
+ empty_fold_df = pd.DataFrame(
222
+ columns=[
223
+ "fold",
224
+ "prediction",
225
+ "probability",
226
+ "p0_no_dr",
227
+ "p1_mild",
228
+ "p2_moderate",
229
+ "p3_severe",
230
+ "p4_pdr",
231
+ ]
232
+ )
233
+
234
+ if image is None:
235
+ return (
236
+ None,
237
+ empty_df,
238
+ empty_fold_df,
239
+ "Upload a fundus image to run ensemble inference.",
240
+ )
241
+
242
+ if not isinstance(image, Image.Image):
243
+ image = Image.fromarray(image)
244
+
245
+ image = image.convert("RGB")
246
+ x = transform(image).unsqueeze(0).to(device)
247
+
248
+ probs, fold_probs = ensemble_predict(models, x)
249
+
250
+ pred_grade = int(np.argmax(probs))
251
+ pred_label = ID_TO_LABEL[pred_grade]
252
+ pred_prob = float(probs[pred_grade])
253
+
254
+ raw_probs = {
255
+ ID_TO_LABEL[i]: float(probs[i])
256
+ for i in range(5)
257
+ }
258
+
259
+ grouped_probs = get_grouped_probs(probs, bucket_mode)
260
+ bucket_label = max(grouped_probs, key=grouped_probs.get)
261
+ bucket_prob = grouped_probs[bucket_label]
262
+ bucket_df = grouped_probs_to_fixed_dataframe(grouped_probs, max_rows=5)
263
+
264
+ fold_df = fold_probs_to_dataframe(fold_probs, checkpoint_paths)
265
+
266
+ fold_preds = [ID_TO_LABEL[int(np.argmax(p))] for p in fold_probs]
267
+ agreement = fold_preds.count(pred_label) / len(fold_preds)
268
+
269
+ summary = (
270
+ f"Ensemble prediction: {pred_label} "
271
+ f"(grade {pred_grade}, probability {pred_prob:.3f})\n\n"
272
+ f"Probability grouping: {BUCKET_MODE_DESCRIPTIONS[bucket_mode]}\n"
273
+ f"Grouped result: {bucket_label} "
274
+ f"(probability {bucket_prob:.3f})\n\n"
275
+ f"Fold agreement with ensemble class: {agreement:.1%} "
276
+ f"({fold_preds.count(pred_label)}/{len(fold_preds)} folds)"
277
+ )
278
+
279
+ return raw_probs, bucket_df, fold_df, summary
280
+
281
+ return predict
282
+
283
+
284
+ def make_app(checkpoint_dir, checkpoint_name, backbone, image_size, device):
285
+ (
286
+ models,
287
+ transform,
288
+ backbone,
289
+ image_size,
290
+ checkpoint_paths,
291
+ ckpts,
292
+ ) = load_ensemble(
293
+ checkpoint_dir=checkpoint_dir,
294
+ checkpoint_name=checkpoint_name,
295
+ backbone=backbone,
296
+ image_size=image_size,
297
+ device=device,
298
+ )
299
+
300
+ predict_fn = make_prediction_fn(
301
+ models=models,
302
+ transform=transform,
303
+ checkpoint_paths=checkpoint_paths,
304
+ device=device,
305
+ )
306
+
307
+ fold_names = [p.parent.name for p in checkpoint_paths]
308
+
309
+ best_metrics = [
310
+ ckpt.get("best_metric", None)
311
+ for ckpt in ckpts
312
+ ]
313
+
314
+ model_info = (
315
+ f"Backbone: `{backbone}` | "
316
+ f"Input: `{image_size}×{image_size}` | "
317
+ f"Folds loaded: `{len(models)}` | "
318
+ f"Device: `{device}`"
319
+ )
320
+
321
+ # fold_info = "Loaded checkpoints: " + ", ".join(fold_names)
322
+ #
323
+ # if any(m is not None for m in best_metrics):
324
+ # metric_info = "Best metrics: " + ", ".join(
325
+ # "NA" if m is None else f"{float(m):.4f}"
326
+ # for m in best_metrics
327
+ # )
328
+ # else:
329
+ # metric_info = ""
330
+
331
+ with gr.Blocks(title="EyePACS DR Ensemble Classifier") as demo:
332
+ gr.Markdown("# EyePACS DR Ensemble Classifier")
333
+ gr.Markdown(model_info)
334
+ # gr.Markdown(fold_info)
335
+ # if metric_info:
336
+ # gr.Markdown(metric_info)
337
+
338
+ with gr.Row():
339
+ with gr.Column(scale=1):
340
+ image = gr.Image(
341
+ type="pil",
342
+ label="Fundus image",
343
+ )
344
+
345
+ bucket_mode = gr.Dropdown(
346
+ choices=[
347
+ "severity",
348
+ "referral",
349
+ "any_dr",
350
+ "stage",
351
+ "high_risk",
352
+ ],
353
+ value="referral",
354
+ label="Probability grouping",
355
+ )
356
+
357
+ run_btn = gr.Button("Run inference", variant="primary")
358
+
359
+ with gr.Column(scale=1):
360
+ raw_probs = gr.Label(
361
+ num_top_classes=5,
362
+ label="Ensemble raw 5-class probabilities",
363
+ )
364
+
365
+ bucket_table = gr.Dataframe(
366
+ label="Grouped probabilities",
367
+ headers=["label", "probability"],
368
+ row_count=(5, "fixed"),
369
+ col_count=(2, "fixed"),
370
+ interactive=False,
371
+ wrap=True,
372
+ )
373
+
374
+ with gr.Accordion("Per-fold predictions", open=False):
375
+ fold_table = gr.Dataframe(
376
+ label="Per-fold probabilities",
377
+ interactive=False,
378
+ wrap=True,
379
+ )
380
+
381
+ summary = gr.Textbox(
382
+ label="Summary",
383
+ lines=7,
384
+ interactive=False,
385
+ )
386
+
387
+ inputs = [image, bucket_mode]
388
+ outputs = [raw_probs, bucket_table, fold_table, summary]
389
+
390
+ run_btn.click(
391
+ fn=predict_fn,
392
+ inputs=inputs,
393
+ outputs=outputs,
394
+ )
395
+
396
+ image.change(
397
+ fn=predict_fn,
398
+ inputs=inputs,
399
+ outputs=outputs,
400
+ )
401
+
402
+ bucket_mode.change(
403
+ fn=predict_fn,
404
+ inputs=inputs,
405
+ outputs=outputs,
406
+ )
407
+
408
+ return demo
409
+
410
+
411
+ def parse_args():
412
+ parser = argparse.ArgumentParser(
413
+ description="EyePACS DR Gradio ensemble inference app."
414
+ )
415
+
416
+ parser.add_argument(
417
+ "--checkpoint-dir",
418
+ default="runs/eyepacs_dr",
419
+ help="Folder containing fold checkpoint folders.",
420
+ )
421
+ parser.add_argument(
422
+ "--checkpoint-name",
423
+ default="best_model_only.pt",
424
+ help="Checkpoint filename inside each fold folder.",
425
+ )
426
+ parser.add_argument(
427
+ "--backbone",
428
+ default="inception_v3",
429
+ help="Fallback backbone if checkpoint args are unavailable.",
430
+ )
431
+ parser.add_argument(
432
+ "--image-size",
433
+ type=int,
434
+ default=1024,
435
+ help="Fallback image size if checkpoint args are unavailable.",
436
+ )
437
+ parser.add_argument("--host", default="0.0.0.0")
438
+ parser.add_argument("--port", type=int, default=7860)
439
+ parser.add_argument("--share", action="store_true")
440
+
441
+ return parser.parse_args()
442
+
443
+
444
+ def main():
445
+ args = parse_args()
446
+
447
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
448
+
449
+ demo = make_app(
450
+ checkpoint_dir=args.checkpoint_dir,
451
+ checkpoint_name=args.checkpoint_name,
452
+ backbone=args.backbone,
453
+ image_size=args.image_size,
454
+ device=device,
455
+ )
456
+
457
+ demo.launch(
458
+ # server_name=args.host,
459
+ # server_port=args.port,
460
+ # share=args.share,
461
+ )
462
+
463
+
464
+ if __name__ == "__main__":
465
+ main()
augmentations.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ augmentations.py
3
+
4
+ Simple camera-style augmentations for color fundus photography (CFP)
5
+ classification.
6
+
7
+ Expected input:
8
+ RGB NumPy image, shape (H, W, 3)
9
+
10
+ Dependencies:
11
+ pip install albumentations opencv-python
12
+ """
13
+
14
+ import cv2
15
+ import albumentations as A
16
+ from albumentations.pytorch import ToTensorV2
17
+
18
+
19
+ IMAGENET_MEAN = (0.485, 0.456, 0.406)
20
+ IMAGENET_STD = (0.229, 0.224, 0.225)
21
+
22
+
23
+ def get_train_transforms(
24
+ image_size=1024,
25
+ mean=IMAGENET_MEAN,
26
+ std=IMAGENET_STD,
27
+ ):
28
+ return A.Compose([
29
+ A.Resize(image_size, image_size),
30
+
31
+ # Geometry is safe
32
+ A.HorizontalFlip(p=0.5),
33
+
34
+ A.ShiftScaleRotate(
35
+ shift_limit=0.02,
36
+ scale_limit=0.03, # slightly reduced
37
+ rotate_limit=5, # slightly reduced
38
+ border_mode=0,
39
+ value=0,
40
+ p=0.3,
41
+ ),
42
+
43
+ # MUCH weaker photometric changes
44
+ A.RandomBrightnessContrast(
45
+ brightness_limit=0.08, # ↓ from 0.15
46
+ contrast_limit=0.08,
47
+ p=0.3,
48
+ ),
49
+
50
+ # Remove or reduce gamma
51
+ A.RandomGamma(
52
+ gamma_limit=(95, 105), # very mild
53
+ p=0.2,
54
+ ),
55
+
56
+ # Remove hue shift entirely (important)
57
+ # Hue shifts are not realistic for fundus physiology
58
+ # -> comment this out or reduce heavily
59
+ # A.HueSaturationValue(...)
60
+
61
+ # Keep mild quality perturbation
62
+ A.OneOf([
63
+ A.GaussianBlur(blur_limit=(3, 5)),
64
+ A.Downscale(scale_min=0.85, scale_max=0.95, interpolation=cv2.INTER_LINEAR),
65
+ A.ImageCompression(quality_lower=80, quality_upper=100),
66
+ ], p=0.15),
67
+
68
+ A.Normalize(mean=mean, std=std),
69
+ ToTensorV2(),
70
+ ])
71
+
72
+
73
+ def get_val_transforms(
74
+ image_size=1024,
75
+ mean=IMAGENET_MEAN,
76
+ std=IMAGENET_STD,
77
+ ):
78
+ """
79
+ Validation/test transforms.
80
+ """
81
+ return A.Compose([
82
+ A.Resize(image_size, image_size),
83
+ A.Normalize(mean=mean, std=std),
84
+ ToTensorV2(),
85
+ ])
dataloader.py ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ from typing import Optional, Sequence
3
+
4
+ import pandas as pd
5
+ from PIL import Image
6
+
7
+ import torch
8
+ from torch.utils.data import Dataset
9
+ from sklearn.model_selection import StratifiedGroupKFold
10
+
11
+
12
+ class EyePACSDataset(Dataset):
13
+ """
14
+ EyePACS diabetic retinopathy dataset.
15
+
16
+ Expected structure:
17
+ root/
18
+ ├── trainLabels.csv
19
+ ├── testLabels.csv
20
+ ├── train/
21
+ │ ├── xxx_left.jpeg
22
+ │ └── xxx_right.jpeg
23
+ └── test/
24
+ ├── xxx_left.jpeg
25
+ └── xxx_right.jpeg
26
+
27
+ Supported splits:
28
+ train:
29
+ Uses trainLabels.csv only.
30
+ Applies fold CV.
31
+ Keeps samples where fold != selected fold.
32
+
33
+ val:
34
+ Uses trainLabels.csv only.
35
+ Applies fold CV.
36
+ Keeps samples where fold == selected fold.
37
+
38
+ test:
39
+ Uses testLabels.csv only.
40
+ Uses original test/ image folder.
41
+ No fold filtering.
42
+
43
+ all:
44
+ Uses trainLabels.csv + testLabels.csv.
45
+ Applies fold CV over the combined labeled pool.
46
+ If fold is not None:
47
+ keeps samples where fold != selected fold by default if all_mode="train"
48
+ keeps samples where fold == selected fold if all_mode="val"
49
+ If fold is None:
50
+ keeps all combined labeled samples.
51
+
52
+ Args:
53
+ root:
54
+ EyePACS root directory.
55
+
56
+ split:
57
+ One of {"train", "val", "test", "all"}.
58
+
59
+ transform:
60
+ Optional image transform.
61
+
62
+ seed:
63
+ Random seed for fold assignment.
64
+
65
+ fold:
66
+ Selected fold index. Required for split="train" and split="val".
67
+ Optional for split="all". Ignored for split="test".
68
+
69
+ n_folds:
70
+ Number of folds.
71
+
72
+ all_mode:
73
+ Only used when split="all" and fold is not None.
74
+ Options:
75
+ "train": keep fold != selected fold
76
+ "val": keep fold == selected fold
77
+ "all": keep all folds
78
+
79
+ return_path:
80
+ If True, return metadata dictionary.
81
+
82
+ image_exts:
83
+ File extensions to try.
84
+ """
85
+
86
+ def __init__(
87
+ self,
88
+ root,
89
+ split: str = "train",
90
+ transform=None,
91
+ seed: int = 42,
92
+ fold: Optional[int] = 0,
93
+ n_folds: int = 5,
94
+ all_mode: str = "all",
95
+ return_path: bool = False,
96
+ image_exts: Sequence[str] = (".jpeg", ".jpg", ".png"),
97
+ ):
98
+ self.root = Path(root)
99
+ self.split = split
100
+ self.transform = transform
101
+ self.seed = seed
102
+ self.fold = fold
103
+ self.n_folds = n_folds
104
+ self.all_mode = all_mode
105
+ self.return_path = return_path
106
+ self.image_exts = tuple(image_exts)
107
+
108
+ if split not in {"train", "val", "test", "all"}:
109
+ raise ValueError(
110
+ f"split must be one of {{'train', 'val', 'test', 'all'}}, got {split}"
111
+ )
112
+
113
+ if all_mode not in {"train", "val", "all"}:
114
+ raise ValueError(
115
+ f"all_mode must be one of {{'train', 'val', 'all'}}, got {all_mode}"
116
+ )
117
+
118
+ if split in {"train", "val"} and fold is None:
119
+ raise ValueError(f"fold must be provided for split='{split}'")
120
+
121
+ if fold is not None and not (0 <= fold < n_folds):
122
+ raise ValueError(f"fold must be in [0, {n_folds - 1}], got {fold}")
123
+
124
+ if split == "train":
125
+ df = self._load_train_dataframe()
126
+ df = self._assign_folds(df)
127
+ df = df[df["fold"] != fold].reset_index(drop=True)
128
+
129
+ elif split == "val":
130
+ df = self._load_train_dataframe()
131
+ df = self._assign_folds(df)
132
+ df = df[df["fold"] == fold].reset_index(drop=True)
133
+
134
+ elif split == "test":
135
+ df = self._load_test_dataframe()
136
+ df["fold"] = -1
137
+
138
+ elif split == "all":
139
+ df = self._load_combined_dataframe()
140
+ df = self._assign_folds(df)
141
+
142
+ if fold is not None:
143
+ if all_mode == "train":
144
+ df = df[df["fold"] != fold].reset_index(drop=True)
145
+ elif all_mode == "val":
146
+ df = df[df["fold"] == fold].reset_index(drop=True)
147
+ elif all_mode == "all":
148
+ df = df.reset_index(drop=True)
149
+ else:
150
+ df = df.reset_index(drop=True)
151
+
152
+ self.df = df.reset_index(drop=True)
153
+ self.samples = self._build_samples(self.df)
154
+
155
+ if len(self.samples) == 0:
156
+ raise RuntimeError(
157
+ f"No images found for split='{split}'. "
158
+ f"Check root path, CSV files, folders, and file extensions."
159
+ )
160
+
161
+ self._print_summary()
162
+
163
+ def _load_train_dataframe(self) -> pd.DataFrame:
164
+ path = self.root / "trainLabels.csv"
165
+ if not path.exists():
166
+ raise FileNotFoundError(f"Missing trainLabels.csv: {path}")
167
+
168
+ df = pd.read_csv(path)
169
+ return self._standardize_label_dataframe(df, source="train")
170
+
171
+ def _load_test_dataframe(self) -> pd.DataFrame:
172
+ path = self.root / "testLabels.csv"
173
+ if not path.exists():
174
+ raise FileNotFoundError(f"Missing testLabels.csv: {path}")
175
+
176
+ df = pd.read_csv(path)
177
+ return self._standardize_label_dataframe(df, source="test")
178
+
179
+ def _load_combined_dataframe(self) -> pd.DataFrame:
180
+ train_df = self._load_train_dataframe()
181
+ test_df = self._load_test_dataframe()
182
+
183
+ df = pd.concat([train_df, test_df], axis=0, ignore_index=True)
184
+ df = df.drop_duplicates(subset=["source", "image"]).reset_index(drop=True)
185
+
186
+ return df
187
+
188
+ @staticmethod
189
+ def _standardize_label_dataframe(df: pd.DataFrame, source: str) -> pd.DataFrame:
190
+ """
191
+ Standardize label dataframe to:
192
+ image, level, source, patient_id
193
+
194
+ EyePACS image names usually look like:
195
+ 10_left
196
+ 10_right
197
+
198
+ patient_id is extracted as the part before the first underscore.
199
+ """
200
+
201
+ if "image" not in df.columns:
202
+ raise ValueError(f"{source} labels CSV must contain column 'image'")
203
+
204
+ if "level" not in df.columns:
205
+ raise ValueError(f"{source} labels CSV must contain column 'level'")
206
+
207
+ df = df[["image", "level"]].copy()
208
+ df["image"] = df["image"].astype(str)
209
+ df["level"] = df["level"].astype(int)
210
+ df["source"] = source
211
+ df["patient_id"] = df["image"].str.split("_").str[0].astype(str)
212
+
213
+ return df
214
+
215
+ def _assign_folds(self, df: pd.DataFrame) -> pd.DataFrame:
216
+ """
217
+ Assign stratified group folds.
218
+
219
+ Grouping:
220
+ patient_id
221
+
222
+ Stratification label:
223
+ max DR severity across all images for that patient_id.
224
+
225
+ This keeps left/right eyes from the same patient in the same fold.
226
+ """
227
+
228
+ df = df.copy()
229
+
230
+ patient_df = (
231
+ df.groupby("patient_id", as_index=False)
232
+ .agg(patient_level=("level", "max"))
233
+ .reset_index(drop=True)
234
+ )
235
+
236
+ groups = patient_df["patient_id"].values
237
+ y = patient_df["patient_level"].values
238
+
239
+ splitter = StratifiedGroupKFold(
240
+ n_splits=self.n_folds,
241
+ shuffle=True,
242
+ random_state=self.seed,
243
+ )
244
+
245
+ patient_df["fold"] = -1
246
+
247
+ for fold_idx, (_, val_idx) in enumerate(
248
+ splitter.split(X=patient_df, y=y, groups=groups)
249
+ ):
250
+ patient_df.loc[val_idx, "fold"] = fold_idx
251
+
252
+ if (patient_df["fold"] < 0).any():
253
+ raise RuntimeError("Some patients were not assigned to a fold")
254
+
255
+ fold_map = dict(zip(patient_df["patient_id"], patient_df["fold"]))
256
+ df["fold"] = df["patient_id"].map(fold_map).astype(int)
257
+
258
+ return df
259
+
260
+ def _build_samples(self, df: pd.DataFrame):
261
+ samples = []
262
+ missing = []
263
+
264
+ for _, row in df.iterrows():
265
+ image_id = str(row["image"])
266
+ label = int(row["level"])
267
+ source = str(row["source"])
268
+ patient_id = str(row["patient_id"])
269
+ fold = int(row["fold"])
270
+
271
+ image_dir = self.root / source
272
+ image_path = self._find_image_path(image_dir, image_id)
273
+
274
+ if image_path is None:
275
+ missing.append((source, image_id))
276
+ continue
277
+
278
+ samples.append(
279
+ {
280
+ "image_id": image_id,
281
+ "image_path": image_path,
282
+ "label": label,
283
+ "source": source,
284
+ "patient_id": patient_id,
285
+ "fold": fold,
286
+ }
287
+ )
288
+
289
+ if len(missing) > 0:
290
+ print(
291
+ f"[EyePACSDataset] Warning: {len(missing)} images listed in CSV "
292
+ f"were not found on disk."
293
+ )
294
+ print(f"[EyePACSDataset] First few missing: {missing[:5]}")
295
+
296
+ return samples
297
+
298
+ def _find_image_path(self, image_dir: Path, image_id: str):
299
+ for ext in self.image_exts:
300
+ path = image_dir / f"{image_id}{ext}"
301
+ if path.exists():
302
+ return path
303
+ return None
304
+
305
+ def _print_summary(self):
306
+ labels = [s["label"] for s in self.samples]
307
+ counts = pd.Series(labels).value_counts().sort_index()
308
+
309
+ print(f"[EyePACSDataset] split={self.split}")
310
+ print(f"[EyePACSDataset] root={self.root}")
311
+ print(f"[EyePACSDataset] n={len(self.samples)}")
312
+
313
+ if self.split != "test":
314
+ print(
315
+ f"[EyePACSDataset] seed={self.seed}, "
316
+ f"fold={self.fold}, "
317
+ f"n_folds={self.n_folds}"
318
+ )
319
+
320
+ if self.split == "all":
321
+ print(f"[EyePACSDataset] all_mode={self.all_mode}")
322
+
323
+ print("[EyePACSDataset] source counts:")
324
+ source_counts = pd.Series([s["source"] for s in self.samples]).value_counts()
325
+ for source, count in source_counts.items():
326
+ print(f" {source}: {int(count)}")
327
+
328
+ print("[EyePACSDataset] class counts:")
329
+ for cls in range(5):
330
+ print(f" class {cls}: {int(counts.get(cls, 0))}")
331
+
332
+ def __len__(self):
333
+ return len(self.samples)
334
+
335
+ def __getitem__(self, idx):
336
+ sample = self.samples[idx]
337
+
338
+ image = Image.open(sample["image_path"]).convert("RGB")
339
+
340
+ if self.transform is not None:
341
+ image = self.transform(image)
342
+
343
+ label = torch.tensor(sample["label"], dtype=torch.long)
344
+
345
+ if self.return_path:
346
+ return {
347
+ "image": image,
348
+ "label": label,
349
+ "image_id": sample["image_id"],
350
+ "image_path": str(sample["image_path"]),
351
+ "source": sample["source"],
352
+ "patient_id": sample["patient_id"],
353
+ "fold": sample["fold"],
354
+ }
355
+
356
+ return image, label
model.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """DeepSeeNet model definition."""
2
+
3
+ from torch import Tensor, nn
4
+
5
+ try:
6
+ import timm
7
+ except ImportError: # pragma: no cover - handled when timm is absent.
8
+ timm = None
9
+
10
+
11
+ class DeepSeeNet(nn.Module):
12
+ """DeepSeeNet risk-factor classifier in PyTorch.
13
+
14
+ Args:
15
+ n_classes: Number of output classes.
16
+ backbone: Any timm model name that supports ``num_classes=0``. The
17
+ default uses InceptionV3.
18
+ pretrained: Load ImageNet weights for the backbone.
19
+ dropout: Dropout probability used by the classifier head.
20
+ freeze_backbone: If true, keep the backbone frozen and train only the
21
+ classifier head.
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ n_classes: int = 2,
27
+ backbone: str = "inception_v3",
28
+ pretrained: bool = True,
29
+ dropout: float = 0.5,
30
+ freeze_backbone: bool = False,
31
+ ) -> None:
32
+ super().__init__()
33
+ if n_classes < 1:
34
+ raise ValueError("n_classes must be positive")
35
+ if timm is None:
36
+ raise ImportError("timm is required to build DeepSeeNet")
37
+
38
+ self.backbone_name = backbone
39
+ self.backbone = timm.create_model(
40
+ backbone,
41
+ pretrained=pretrained,
42
+ num_classes=0,
43
+ global_pool="avg",
44
+ )
45
+ in_features = self.backbone.num_features
46
+ self.classifier = nn.Sequential(
47
+ nn.Linear(in_features, 256),
48
+ nn.ReLU(inplace=True),
49
+ nn.Dropout(dropout),
50
+ nn.Linear(256, 128),
51
+ nn.ReLU(inplace=True),
52
+ nn.Dropout(dropout),
53
+ nn.Linear(128, n_classes),
54
+ )
55
+
56
+ if freeze_backbone:
57
+ self.backbone.requires_grad_(False)
58
+
59
+ def forward(self, x: Tensor) -> Tensor:
60
+ features = self.backbone(x)
61
+ return self.classifier(features)
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ albumentations
2
+ gradio
3
+ huggingface_hub
4
+ numpy
5
+ opencv-python
6
+ pandas
7
+ pillow
8
+ pydantic
9
+ timm
10
+ torch
11
+ torchvision
12
+ torchaudio
13
+ tqdm
runs/eyepacs_dr/inception_v3_fold0/best_model_only.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dce962b4cc7a2c70bff55de17f3df89c1ff8c03fb881a681591377794d0c19de
3
+ size 89735243
runs/eyepacs_dr/inception_v3_fold0/history.json ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "epoch": 1,
4
+ "lr": 0.0001,
5
+ "train_loss": 1.2191880825112196,
6
+ "train_acc": 0.5508288227334236,
7
+ "val_loss": 0.8676833006413706,
8
+ "val_acc": 0.7436865839909809,
9
+ "val_referable_acc": 0.9082863585118377,
10
+ "val_any_dr_acc": 0.8395152198421646,
11
+ "val_severe_or_pdr_acc": 0.9364712514092447,
12
+ "val_qwk": 0.7140886061578311,
13
+ "val_referable_auc": 0.924552902315247,
14
+ "val_any_dr_auc": 0.879340733543783,
15
+ "val_severe_or_pdr_auc": 0.9729235633730084
16
+ },
17
+ {
18
+ "epoch": 2,
19
+ "lr": 9.972883382072954e-05,
20
+ "train_loss": 1.0452303189547363,
21
+ "train_acc": 0.6802125620207488,
22
+ "val_loss": 0.7482551874544602,
23
+ "val_acc": 0.7671927846674182,
24
+ "val_referable_acc": 0.9131341600901917,
25
+ "val_any_dr_acc": 0.8613866967305525,
26
+ "val_severe_or_pdr_acc": 0.9496054114994363,
27
+ "val_qwk": 0.7584975852550456,
28
+ "val_referable_auc": 0.9301897769832052,
29
+ "val_any_dr_auc": 0.9032338758891758,
30
+ "val_severe_or_pdr_auc": 0.9757942542713257
31
+ },
32
+ {
33
+ "epoch": 3,
34
+ "lr": 9.891830623632339e-05,
35
+ "train_loss": 0.9849801756774298,
36
+ "train_acc": 0.719539354984213,
37
+ "val_loss": 0.8034545580053276,
38
+ "val_acc": 0.7496617812852311,
39
+ "val_referable_acc": 0.9096392333709132,
40
+ "val_any_dr_acc": 0.8586245772266066,
41
+ "val_severe_or_pdr_acc": 0.9195603156708004,
42
+ "val_qwk": 0.7155813958918468,
43
+ "val_referable_auc": 0.9341313162498839,
44
+ "val_any_dr_auc": 0.9124605325061405,
45
+ "val_severe_or_pdr_auc": 0.9746139125395339
46
+ },
47
+ {
48
+ "epoch": 4,
49
+ "lr": 9.757729755661011e-05,
50
+ "train_loss": 0.9430840012588424,
51
+ "train_acc": 0.740978800180424,
52
+ "val_loss": 0.7264118472107906,
53
+ "val_acc": 0.7847801578354002,
54
+ "val_referable_acc": 0.9155016910935738,
55
+ "val_any_dr_acc": 0.8696166854565953,
56
+ "val_severe_or_pdr_acc": 0.9439684329199549,
57
+ "val_qwk": 0.7404066770264675,
58
+ "val_referable_auc": 0.9430007323088389,
59
+ "val_any_dr_auc": 0.918949293829938,
60
+ "val_severe_or_pdr_auc": 0.9757122141043169
61
+ },
62
+ {
63
+ "epoch": 5,
64
+ "lr": 9.572050015330873e-05,
65
+ "train_loss": 0.9146992052977936,
66
+ "train_acc": 0.756441700496166,
67
+ "val_loss": 0.6687679942323498,
68
+ "val_acc": 0.8094701240135288,
69
+ "val_referable_acc": 0.913641488162345,
70
+ "val_any_dr_acc": 0.8978015783540022,
71
+ "val_severe_or_pdr_acc": 0.9494363021420519,
72
+ "val_qwk": 0.7657158782341715,
73
+ "val_referable_auc": 0.9439605662828892,
74
+ "val_any_dr_auc": 0.9205573987730261,
75
+ "val_severe_or_pdr_auc": 0.9773371098739827
76
+ },
77
+ {
78
+ "epoch": 6,
79
+ "lr": 9.336825748732972e-05,
80
+ "train_loss": 0.882006509337922,
81
+ "train_acc": 0.7686485115020297,
82
+ "val_loss": 0.7364799950544837,
83
+ "val_acc": 0.7890642615558061,
84
+ "val_referable_acc": 0.913923337091319,
85
+ "val_any_dr_acc": 0.8679819616685457,
86
+ "val_severe_or_pdr_acc": 0.9467305524239008,
87
+ "val_qwk": 0.7378029189144739,
88
+ "val_referable_auc": 0.9429656239713404,
89
+ "val_any_dr_auc": 0.9224749898528801,
90
+ "val_severe_or_pdr_auc": 0.9758237458346295
91
+ },
92
+ {
93
+ "epoch": 7,
94
+ "lr": 9.05463412215599e-05,
95
+ "train_loss": 0.8602466160817139,
96
+ "train_acc": 0.7752311682453766,
97
+ "val_loss": 0.6406958381245343,
98
+ "val_acc": 0.8077226606538895,
99
+ "val_referable_acc": 0.9151071025930101,
100
+ "val_any_dr_acc": 0.8822998872604284,
101
+ "val_severe_or_pdr_acc": 0.9576099210822999,
102
+ "val_qwk": 0.7721192161837538,
103
+ "val_referable_auc": 0.9449475727317759,
104
+ "val_any_dr_auc": 0.9233336916673446,
105
+ "val_severe_or_pdr_auc": 0.9767672256289048
106
+ },
107
+ {
108
+ "epoch": 8,
109
+ "lr": 8.728566886113101e-05,
110
+ "train_loss": 0.8290294695126349,
111
+ "train_acc": 0.779967298150654,
112
+ "val_loss": 0.6343717073507212,
113
+ "val_acc": 0.8144870349492672,
114
+ "val_referable_acc": 0.9237880496054115,
115
+ "val_any_dr_acc": 0.8870349492671927,
116
+ "val_severe_or_pdr_acc": 0.9634160090191657,
117
+ "val_qwk": 0.8033455171949371,
118
+ "val_referable_auc": 0.9489161527156735,
119
+ "val_any_dr_auc": 0.9277659781884949,
120
+ "val_severe_or_pdr_auc": 0.9800201271876396
121
+ },
122
+ {
123
+ "epoch": 9,
124
+ "lr": 8.362196501476347e-05,
125
+ "train_loss": 0.8049967132162607,
126
+ "train_acc": 0.7827582318448354,
127
+ "val_loss": 0.6686938404633738,
128
+ "val_acc": 0.7877677564825254,
129
+ "val_referable_acc": 0.9228297632468997,
130
+ "val_any_dr_acc": 0.8662908680947012,
131
+ "val_severe_or_pdr_acc": 0.9598647125140924,
132
+ "val_qwk": 0.7868667085078025,
133
+ "val_referable_auc": 0.948626316362486,
134
+ "val_any_dr_auc": 0.9289171459554377,
135
+ "val_severe_or_pdr_auc": 0.9788885805878594
136
+ },
137
+ {
138
+ "epoch": 10,
139
+ "lr": 7.959536998847741e-05,
140
+ "train_loss": 0.7784485263522273,
141
+ "train_acc": 0.7822789806044204,
142
+ "val_loss": 0.6502069805493618,
143
+ "val_acc": 0.7921082299887261,
144
+ "val_referable_acc": 0.9254227733934611,
145
+ "val_any_dr_acc": 0.8756482525366404,
146
+ "val_severe_or_pdr_acc": 0.9478579481397971,
147
+ "val_qwk": 0.7910244571190812,
148
+ "val_referable_auc": 0.9516418326868392,
149
+ "val_any_dr_auc": 0.9282227682160731,
150
+ "val_severe_or_pdr_auc": 0.9795037924720514
151
+ },
152
+ {
153
+ "epoch": 11,
154
+ "lr": 7.524999999999999e-05,
155
+ "train_loss": 0.7509459461924403,
156
+ "train_acc": 0.7843510374379793,
157
+ "val_loss": 0.6940719285839171,
158
+ "val_acc": 0.7910372040586245,
159
+ "val_referable_acc": 0.9186020293122886,
160
+ "val_any_dr_acc": 0.8686583990980834,
161
+ "val_severe_or_pdr_acc": 0.9589627959413755,
162
+ "val_qwk": 0.7803465132729354,
163
+ "val_referable_auc": 0.9472788109365553,
164
+ "val_any_dr_auc": 0.9282660544196276,
165
+ "val_severe_or_pdr_auc": 0.9756903724737973
166
+ },
167
+ {
168
+ "epoch": 12,
169
+ "lr": 7.06334638322521e-05,
170
+ "train_loss": 0.7224059543149344,
171
+ "train_acc": 0.790581303563374,
172
+ "val_loss": 0.6284573407157029,
173
+ "val_acc": 0.8084554678692221,
174
+ "val_referable_acc": 0.9204622322435174,
175
+ "val_any_dr_acc": 0.8856820744081173,
176
+ "val_severe_or_pdr_acc": 0.9538331454340474,
177
+ "val_qwk": 0.7859381302661944,
178
+ "val_referable_auc": 0.949624795859308,
179
+ "val_any_dr_auc": 0.9286386672917757,
180
+ "val_severe_or_pdr_auc": 0.9782240016140643
181
+ },
182
+ {
183
+ "epoch": 13,
184
+ "lr": 6.579634122155988e-05,
185
+ "train_loss": 0.678931502343891,
186
+ "train_acc": 0.8054239963915201,
187
+ "val_loss": 0.6790591289521339,
188
+ "val_acc": 0.7918827508455468,
189
+ "val_referable_acc": 0.9203494926719279,
190
+ "val_any_dr_acc": 0.8708004509582864,
191
+ "val_severe_or_pdr_acc": 0.9526493799323562,
192
+ "val_qwk": 0.7776873128462451,
193
+ "val_referable_auc": 0.9484067777659061,
194
+ "val_any_dr_auc": 0.9263266363022873,
195
+ "val_severe_or_pdr_auc": 0.9759889700838298
196
+ },
197
+ {
198
+ "epoch": 14,
199
+ "lr": 6.079162869547907e-05,
200
+ "train_loss": 0.65180876729426,
201
+ "train_acc": 0.8017450383400992,
202
+ "val_loss": 0.635306676372197,
203
+ "val_acc": 0.8014656144306652,
204
+ "val_referable_acc": 0.9195039458850056,
205
+ "val_any_dr_acc": 0.8771138669673055,
206
+ "val_severe_or_pdr_acc": 0.957271702367531,
207
+ "val_qwk": 0.7925515495846708,
208
+ "val_referable_auc": 0.9500388593755204,
209
+ "val_any_dr_auc": 0.9308198505052305,
210
+ "val_severe_or_pdr_auc": 0.976161594034368
211
+ },
212
+ {
213
+ "epoch": 15,
214
+ "lr": 5.567415893174884e-05,
215
+ "train_loss": 0.61246272180873,
216
+ "train_acc": 0.8109212900315742,
217
+ "val_loss": 0.6278831657186033,
218
+ "val_acc": 0.7925028184892897,
219
+ "val_referable_acc": 0.9235625704622322,
220
+ "val_any_dr_acc": 0.8679255918827509,
221
+ "val_severe_or_pdr_acc": 0.9588500563697858,
222
+ "val_qwk": 0.8006316582640922,
223
+ "val_referable_auc": 0.9499428891538771,
224
+ "val_any_dr_auc": 0.9269722669912654,
225
+ "val_severe_or_pdr_auc": 0.9790055816626393
226
+ },
227
+ {
228
+ "epoch": 16,
229
+ "lr": 5.05e-05,
230
+ "train_loss": 0.5845572758309735,
231
+ "train_acc": 0.8146425349571493,
232
+ "val_loss": 0.6543550435289858,
233
+ "val_acc": 0.7959977452085683,
234
+ "val_referable_acc": 0.9232807215332581,
235
+ "val_any_dr_acc": 0.8736753100338218,
236
+ "val_severe_or_pdr_acc": 0.9649379932356257,
237
+ "val_qwk": 0.8075519604379204,
238
+ "val_referable_auc": 0.9482329286570407,
239
+ "val_any_dr_auc": 0.9286975430685462,
240
+ "val_severe_or_pdr_auc": 0.9758107695467758
241
+ },
242
+ {
243
+ "epoch": 17,
244
+ "lr": 4.532584106825115e-05,
245
+ "train_loss": 0.5447519773001546,
246
+ "train_acc": 0.821521199819576,
247
+ "val_loss": 0.6652101601796327,
248
+ "val_acc": 0.7871476888387824,
249
+ "val_referable_acc": 0.923055242390079,
250
+ "val_any_dr_acc": 0.8653325817361894,
251
+ "val_severe_or_pdr_acc": 0.961837655016911,
252
+ "val_qwk": 0.791314256809605,
253
+ "val_referable_auc": 0.9438051936460965,
254
+ "val_any_dr_auc": 0.9265324644482931,
255
+ "val_severe_or_pdr_auc": 0.9767693704698723
256
+ },
257
+ {
258
+ "epoch": 18,
259
+ "lr": 4.0208371304520894e-05,
260
+ "train_loss": 0.5182197611740327,
261
+ "train_acc": 0.8213379566982408,
262
+ "val_loss": 0.6194416342485851,
263
+ "val_acc": 0.7971815107102593,
264
+ "val_referable_acc": 0.9248590755355129,
265
+ "val_any_dr_acc": 0.8734498308906427,
266
+ "val_severe_or_pdr_acc": 0.9632468996617812,
267
+ "val_qwk": 0.8099503330099491,
268
+ "val_referable_auc": 0.9476082860622475,
269
+ "val_any_dr_auc": 0.9269798287926418,
270
+ "val_severe_or_pdr_auc": 0.9785031884133404
271
+ },
272
+ {
273
+ "epoch": 19,
274
+ "lr": 3.520365877844009e-05,
275
+ "train_loss": 0.4779584521766337,
276
+ "train_acc": 0.8299503834009924,
277
+ "val_loss": 0.6388505003094808,
278
+ "val_acc": 0.8011837655016911,
279
+ "val_referable_acc": 0.9214768883878242,
280
+ "val_any_dr_acc": 0.8696166854565953,
281
+ "val_severe_or_pdr_acc": 0.9637542277339346,
282
+ "val_qwk": 0.7931194428109762,
283
+ "val_referable_auc": 0.9505122746308755,
284
+ "val_any_dr_auc": 0.9300419577789887,
285
+ "val_severe_or_pdr_auc": 0.9751037227218233
286
+ }
287
+ ]
runs/eyepacs_dr/inception_v3_fold1/best_model_only.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:065898a2a70eb2c96da5c2c78efacc5f29dc66a6394af397bb0f761cf7527cb1
3
+ size 89735243
runs/eyepacs_dr/inception_v3_fold1/history.json ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "epoch": 1,
4
+ "lr": 0.0001,
5
+ "train_loss": 1.2026121128933571,
6
+ "train_acc": 0.553168696436626,
7
+ "val_loss": 0.8374152492743148,
8
+ "val_acc": 0.7521136286777139,
9
+ "val_referable_acc": 0.8970803742531845,
10
+ "val_any_dr_acc": 0.8385751324540638,
11
+ "val_severe_or_pdr_acc": 0.9418329387893135,
12
+ "val_qwk": 0.6836796861912281,
13
+ "val_referable_auc": 0.9156936050415827,
14
+ "val_any_dr_auc": 0.8720412184915984,
15
+ "val_severe_or_pdr_auc": 0.9578836845029959
16
+ },
17
+ {
18
+ "epoch": 2,
19
+ "lr": 9.972883382072954e-05,
20
+ "train_loss": 1.0318292616238507,
21
+ "train_acc": 0.6790426251691475,
22
+ "val_loss": 0.7552449644290532,
23
+ "val_acc": 0.7826626084996054,
24
+ "val_referable_acc": 0.9076766993574569,
25
+ "val_any_dr_acc": 0.871491376394995,
26
+ "val_severe_or_pdr_acc": 0.9453838349678728,
27
+ "val_qwk": 0.7235517054346978,
28
+ "val_referable_auc": 0.9167888976621349,
29
+ "val_any_dr_auc": 0.8932112253636199,
30
+ "val_severe_or_pdr_auc": 0.9604115897123635
31
+ },
32
+ {
33
+ "epoch": 3,
34
+ "lr": 9.891830623632339e-05,
35
+ "train_loss": 0.9734103289551707,
36
+ "train_acc": 0.726009246729815,
37
+ "val_loss": 0.7190881273400056,
38
+ "val_acc": 0.8057152519445384,
39
+ "val_referable_acc": 0.9196821102468719,
40
+ "val_any_dr_acc": 0.8854695073836095,
41
+ "val_severe_or_pdr_acc": 0.9467365573216098,
42
+ "val_qwk": 0.7489601682632545,
43
+ "val_referable_auc": 0.937417607330826,
44
+ "val_any_dr_auc": 0.907371009956367,
45
+ "val_severe_or_pdr_auc": 0.963622201448665
46
+ },
47
+ {
48
+ "epoch": 4,
49
+ "lr": 9.757729755661011e-05,
50
+ "train_loss": 0.9321129762029454,
51
+ "train_acc": 0.7482380469102391,
52
+ "val_loss": 0.7095937849757449,
53
+ "val_acc": 0.7950061999774546,
54
+ "val_referable_acc": 0.921373013189043,
55
+ "val_any_dr_acc": 0.8789313493405478,
56
+ "val_severe_or_pdr_acc": 0.9477510990869125,
57
+ "val_qwk": 0.762886911264498,
58
+ "val_referable_auc": 0.9420763854554339,
59
+ "val_any_dr_auc": 0.9133179603379078,
60
+ "val_severe_or_pdr_auc": 0.9666140768371154
61
+ },
62
+ {
63
+ "epoch": 5,
64
+ "lr": 9.572050015330873e-05,
65
+ "train_loss": 0.9010974023193726,
66
+ "train_acc": 0.7627565403698692,
67
+ "val_loss": 0.7568363213052665,
68
+ "val_acc": 0.7908353060534326,
69
+ "val_referable_acc": 0.9201330176981175,
70
+ "val_any_dr_acc": 0.8696877465900124,
71
+ "val_severe_or_pdr_acc": 0.9524856273249915,
72
+ "val_qwk": 0.7572223845133317,
73
+ "val_referable_auc": 0.9399529114467903,
74
+ "val_any_dr_auc": 0.913509437130183,
75
+ "val_severe_or_pdr_auc": 0.9651894407755202
76
+ },
77
+ {
78
+ "epoch": 6,
79
+ "lr": 9.336825748732972e-05,
80
+ "train_loss": 0.8704525511249745,
81
+ "train_acc": 0.7721865133062698,
82
+ "val_loss": 0.8010236610219734,
83
+ "val_acc": 0.7685153872167737,
84
+ "val_referable_acc": 0.8974185548416188,
85
+ "val_any_dr_acc": 0.8616841393304024,
86
+ "val_severe_or_pdr_acc": 0.9301093450569271,
87
+ "val_qwk": 0.7133115474233512,
88
+ "val_referable_auc": 0.9472766142977672,
89
+ "val_any_dr_auc": 0.9200055280370211,
90
+ "val_severe_or_pdr_auc": 0.9665115217625493
91
+ },
92
+ {
93
+ "epoch": 7,
94
+ "lr": 9.05463412215599e-05,
95
+ "train_loss": 0.8321962211924627,
96
+ "train_acc": 0.775625845737483,
97
+ "val_loss": 0.7558084888464165,
98
+ "val_acc": 0.7736444594746928,
99
+ "val_referable_acc": 0.9060985232780971,
100
+ "val_any_dr_acc": 0.8678841167850299,
101
+ "val_severe_or_pdr_acc": 0.9163566677939353,
102
+ "val_qwk": 0.6786061720857763,
103
+ "val_referable_auc": 0.9403188664966133,
104
+ "val_any_dr_auc": 0.9191492215314945,
105
+ "val_severe_or_pdr_auc": 0.9615996734543758
106
+ },
107
+ {
108
+ "epoch": 8,
109
+ "lr": 8.728566886113101e-05,
110
+ "train_loss": 0.8177962298792765,
111
+ "train_acc": 0.7858451736580965,
112
+ "val_loss": 0.6457110616779425,
113
+ "val_acc": 0.8106188704768347,
114
+ "val_referable_acc": 0.9237402773080825,
115
+ "val_any_dr_acc": 0.8930222071919739,
116
+ "val_severe_or_pdr_acc": 0.9468492841844212,
117
+ "val_qwk": 0.7714119068915228,
118
+ "val_referable_auc": 0.9457900718012293,
119
+ "val_any_dr_auc": 0.9203518908286116,
120
+ "val_severe_or_pdr_auc": 0.9663216986230594
121
+ },
122
+ {
123
+ "epoch": 9,
124
+ "lr": 8.362196501476347e-05,
125
+ "train_loss": 0.7908911250588805,
126
+ "train_acc": 0.7856619305367614,
127
+ "val_loss": 0.6637757030902459,
128
+ "val_acc": 0.8100552361627776,
129
+ "val_referable_acc": 0.9274039003494533,
130
+ "val_any_dr_acc": 0.8876113177770263,
131
+ "val_severe_or_pdr_acc": 0.956205613797768,
132
+ "val_qwk": 0.8006692611738406,
133
+ "val_referable_auc": 0.9488354745793148,
134
+ "val_any_dr_auc": 0.9206744634585085,
135
+ "val_severe_or_pdr_auc": 0.9689028630191081
136
+ },
137
+ {
138
+ "epoch": 10,
139
+ "lr": 7.959536998847741e-05,
140
+ "train_loss": 0.7643525296428153,
141
+ "train_acc": 0.7932876635092467,
142
+ "val_loss": 0.6757383593818459,
143
+ "val_acc": 0.7982752789989854,
144
+ "val_referable_acc": 0.9256566339758765,
145
+ "val_any_dr_acc": 0.8783677150264908,
146
+ "val_severe_or_pdr_acc": 0.948596550557998,
147
+ "val_qwk": 0.7795822369761995,
148
+ "val_referable_auc": 0.952180940819946,
149
+ "val_any_dr_auc": 0.9246441342557743,
150
+ "val_severe_or_pdr_auc": 0.9664718510288837
151
+ },
152
+ {
153
+ "epoch": 11,
154
+ "lr": 7.524999999999999e-05,
155
+ "train_loss": 0.7334291298928753,
156
+ "train_acc": 0.7942320703653586,
157
+ "val_loss": 0.6057324167330496,
158
+ "val_acc": 0.8163115770488107,
159
+ "val_referable_acc": 0.9269529928982077,
160
+ "val_any_dr_acc": 0.891274940818397,
161
+ "val_severe_or_pdr_acc": 0.9563747040919851,
162
+ "val_qwk": 0.7900845705555817,
163
+ "val_referable_auc": 0.9473444224703329,
164
+ "val_any_dr_auc": 0.9199220043277607,
165
+ "val_severe_or_pdr_auc": 0.9659417503784572
166
+ },
167
+ {
168
+ "epoch": 12,
169
+ "lr": 7.06334638322521e-05,
170
+ "train_loss": 0.6972404156026402,
171
+ "train_acc": 0.7989118177717637,
172
+ "val_loss": 0.7115337061594444,
173
+ "val_acc": 0.778942622026829,
174
+ "val_referable_acc": 0.914891218577387,
175
+ "val_any_dr_acc": 0.857569608837786,
176
+ "val_severe_or_pdr_acc": 0.9541765302671626,
177
+ "val_qwk": 0.773439823813228,
178
+ "val_referable_auc": 0.9498813971927252,
179
+ "val_any_dr_auc": 0.9262803122554194,
180
+ "val_severe_or_pdr_auc": 0.9691176738138658
181
+ },
182
+ {
183
+ "epoch": 13,
184
+ "lr": 6.579634122155988e-05,
185
+ "train_loss": 0.6745618633862063,
186
+ "train_acc": 0.8047615020297699,
187
+ "val_loss": 0.6478154543972434,
188
+ "val_acc": 0.803686168413933,
189
+ "val_referable_acc": 0.9209221057377973,
190
+ "val_any_dr_acc": 0.8797768008116335,
191
+ "val_severe_or_pdr_acc": 0.9530492616390486,
192
+ "val_qwk": 0.7809586848122757,
193
+ "val_referable_auc": 0.9476525109932052,
194
+ "val_any_dr_auc": 0.9234389081104144,
195
+ "val_severe_or_pdr_auc": 0.9665842954775892
196
+ },
197
+ {
198
+ "epoch": 14,
199
+ "lr": 6.079162869547907e-05,
200
+ "train_loss": 0.6390304281857296,
201
+ "train_acc": 0.8057481957600361,
202
+ "val_loss": 0.6064365419026172,
203
+ "val_acc": 0.8143388569496111,
204
+ "val_referable_acc": 0.9259948145643107,
205
+ "val_any_dr_acc": 0.8916131214068312,
206
+ "val_severe_or_pdr_acc": 0.9585165144854019,
207
+ "val_qwk": 0.8013380815188119,
208
+ "val_referable_auc": 0.9495913061231518,
209
+ "val_any_dr_auc": 0.9251410560727309,
210
+ "val_severe_or_pdr_auc": 0.9679007523548978
211
+ },
212
+ {
213
+ "epoch": 15,
214
+ "lr": 5.567415893174884e-05,
215
+ "train_loss": 0.6031589889693055,
216
+ "train_acc": 0.8134444068561119,
217
+ "val_loss": 0.6843524756311712,
218
+ "val_acc": 0.7870589561492504,
219
+ "val_referable_acc": 0.9243602750535452,
220
+ "val_any_dr_acc": 0.8676586630594071,
221
+ "val_severe_or_pdr_acc": 0.9522038101679631,
222
+ "val_qwk": 0.7926764678375114,
223
+ "val_referable_auc": 0.9519691120245126,
224
+ "val_any_dr_auc": 0.9264247004258922,
225
+ "val_severe_or_pdr_auc": 0.9699912226142658
226
+ },
227
+ {
228
+ "epoch": 16,
229
+ "lr": 5.05e-05,
230
+ "train_loss": 0.5689867827125535,
231
+ "train_acc": 0.8199988723500226,
232
+ "val_loss": 0.6491324536064783,
233
+ "val_acc": 0.8049825273362642,
234
+ "val_referable_acc": 0.9251493630932252,
235
+ "val_any_dr_acc": 0.8824258820877015,
236
+ "val_severe_or_pdr_acc": 0.9555856160523053,
237
+ "val_qwk": 0.7875070071285253,
238
+ "val_referable_auc": 0.945414267347864,
239
+ "val_any_dr_auc": 0.9243323626706975,
240
+ "val_severe_or_pdr_auc": 0.9674251942450486
241
+ },
242
+ {
243
+ "epoch": 17,
244
+ "lr": 4.532584106825115e-05,
245
+ "train_loss": 0.5425662958560142,
246
+ "train_acc": 0.820872801082544,
247
+ "val_loss": 0.6018754473910796,
248
+ "val_acc": 0.8186224777364446,
249
+ "val_referable_acc": 0.9275166272122647,
250
+ "val_any_dr_acc": 0.8925712997407282,
251
+ "val_severe_or_pdr_acc": 0.9621801375267727,
252
+ "val_qwk": 0.8065342569653287,
253
+ "val_referable_auc": 0.9471525794573215,
254
+ "val_any_dr_auc": 0.9243262164008834,
255
+ "val_severe_or_pdr_auc": 0.9677116841294643
256
+ },
257
+ {
258
+ "epoch": 18,
259
+ "lr": 4.0208371304520894e-05,
260
+ "train_loss": 0.5011885827445048,
261
+ "train_acc": 0.83247350022553,
262
+ "val_loss": 0.6446213349053266,
263
+ "val_acc": 0.809040694397475,
264
+ "val_referable_acc": 0.9258820877014993,
265
+ "val_any_dr_acc": 0.8854695073836095,
266
+ "val_severe_or_pdr_acc": 0.962123774095367,
267
+ "val_qwk": 0.8019418515785007,
268
+ "val_referable_auc": 0.9391693765337806,
269
+ "val_any_dr_auc": 0.9214470823542462,
270
+ "val_severe_or_pdr_auc": 0.9675130662412171
271
+ },
272
+ {
273
+ "epoch": 19,
274
+ "lr": 3.520365877844009e-05,
275
+ "train_loss": 0.47397731046285185,
276
+ "train_acc": 0.8359974064050518,
277
+ "val_loss": 0.6219859553383217,
278
+ "val_acc": 0.8104497801826175,
279
+ "val_referable_acc": 0.9272911734866419,
280
+ "val_any_dr_acc": 0.8833840604215984,
281
+ "val_severe_or_pdr_acc": 0.9569383384060421,
282
+ "val_qwk": 0.7980956613043054,
283
+ "val_referable_auc": 0.9491089108302032,
284
+ "val_any_dr_auc": 0.9246982460152176,
285
+ "val_severe_or_pdr_auc": 0.9674015654350823
286
+ }
287
+ ]
runs/eyepacs_dr/inception_v3_fold2/best_model_only.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7e11288258bff638027be0a4956da8992931c25ccdac7b635fb542be6eb0c05f
3
+ size 89735243
runs/eyepacs_dr/inception_v3_fold2/history.json ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "epoch": 1,
4
+ "lr": 0.0001,
5
+ "train_loss": 1.2143506749987225,
6
+ "train_acc": 0.5614710193955796,
7
+ "val_loss": 0.6948825628539755,
8
+ "val_acc": 0.7953776775648252,
9
+ "val_referable_acc": 0.9081172491544532,
10
+ "val_any_dr_acc": 0.8778466741826381,
11
+ "val_severe_or_pdr_acc": 0.9413190529875987,
12
+ "val_qwk": 0.7155456574787837,
13
+ "val_referable_auc": 0.9247314923272769,
14
+ "val_any_dr_auc": 0.8790872092642767,
15
+ "val_severe_or_pdr_auc": 0.9670086693426698
16
+ },
17
+ {
18
+ "epoch": 2,
19
+ "lr": 9.972883382072954e-05,
20
+ "train_loss": 1.0420125438304544,
21
+ "train_acc": 0.6888390843482183,
22
+ "val_loss": 0.8327976735630057,
23
+ "val_acc": 0.6973506200676437,
24
+ "val_referable_acc": 0.9091319052987599,
25
+ "val_any_dr_acc": 0.7968996617812852,
26
+ "val_severe_or_pdr_acc": 0.9479143179255919,
27
+ "val_qwk": 0.7231192710199451,
28
+ "val_referable_auc": 0.9336409757639126,
29
+ "val_any_dr_auc": 0.8999365600581144,
30
+ "val_severe_or_pdr_auc": 0.9717771882818815
31
+ },
32
+ {
33
+ "epoch": 3,
34
+ "lr": 9.891830623632339e-05,
35
+ "train_loss": 0.985259113020869,
36
+ "train_acc": 0.7269959404600812,
37
+ "val_loss": 0.7072824161512339,
38
+ "val_acc": 0.7739571589627959,
39
+ "val_referable_acc": 0.9163472378804961,
40
+ "val_any_dr_acc": 0.86967305524239,
41
+ "val_severe_or_pdr_acc": 0.9385569334836528,
42
+ "val_qwk": 0.7451868136032376,
43
+ "val_referable_auc": 0.9428887353837985,
44
+ "val_any_dr_auc": 0.9123166499784034,
45
+ "val_severe_or_pdr_auc": 0.9740952162696138
46
+ },
47
+ {
48
+ "epoch": 4,
49
+ "lr": 9.757729755661011e-05,
50
+ "train_loss": 0.9373754202970042,
51
+ "train_acc": 0.7481111862877763,
52
+ "val_loss": 0.7193142563944604,
53
+ "val_acc": 0.7816234498308906,
54
+ "val_referable_acc": 0.918489289740699,
55
+ "val_any_dr_acc": 0.8700112739571589,
56
+ "val_severe_or_pdr_acc": 0.9522547914317926,
57
+ "val_qwk": 0.7620895041226018,
58
+ "val_referable_auc": 0.9451858199028286,
59
+ "val_any_dr_auc": 0.9131514312639888,
60
+ "val_severe_or_pdr_auc": 0.9744336137071168
61
+ },
62
+ {
63
+ "epoch": 5,
64
+ "lr": 9.572050015330873e-05,
65
+ "train_loss": 0.9043582667893636,
66
+ "train_acc": 0.7615584122688318,
67
+ "val_loss": 0.6663910818341888,
68
+ "val_acc": 0.8056369785794814,
69
+ "val_referable_acc": 0.9179255918827508,
70
+ "val_any_dr_acc": 0.8842164599774521,
71
+ "val_severe_or_pdr_acc": 0.9594137542277339,
72
+ "val_qwk": 0.7709031226321263,
73
+ "val_referable_auc": 0.9445627977133615,
74
+ "val_any_dr_auc": 0.9151006204107276,
75
+ "val_severe_or_pdr_auc": 0.9724197038400002
76
+ },
77
+ {
78
+ "epoch": 6,
79
+ "lr": 9.336825748732972e-05,
80
+ "train_loss": 0.8722681901478585,
81
+ "train_acc": 0.7702554127198917,
82
+ "val_loss": 0.7913296779489571,
83
+ "val_acc": 0.7606538895152198,
84
+ "val_referable_acc": 0.9029312288613304,
85
+ "val_any_dr_acc": 0.8507891770011274,
86
+ "val_severe_or_pdr_acc": 0.9457158962795942,
87
+ "val_qwk": 0.7080191690735049,
88
+ "val_referable_auc": 0.9269965806304334,
89
+ "val_any_dr_auc": 0.9172570957840867,
90
+ "val_severe_or_pdr_auc": 0.9683691666705453
91
+ },
92
+ {
93
+ "epoch": 7,
94
+ "lr": 9.05463412215599e-05,
95
+ "train_loss": 0.8582366131410762,
96
+ "train_acc": 0.7776133288227334,
97
+ "val_loss": 0.6967431294608949,
98
+ "val_acc": 0.7959413754227734,
99
+ "val_referable_acc": 0.9282412626832018,
100
+ "val_any_dr_acc": 0.8834836527621195,
101
+ "val_severe_or_pdr_acc": 0.9430665163472379,
102
+ "val_qwk": 0.7790565442854828,
103
+ "val_referable_auc": 0.9510393498401439,
104
+ "val_any_dr_auc": 0.91728786272431,
105
+ "val_severe_or_pdr_auc": 0.9742298955685044
106
+ },
107
+ {
108
+ "epoch": 8,
109
+ "lr": 8.728566886113101e-05,
110
+ "train_loss": 0.8172618988388877,
111
+ "train_acc": 0.7812077131258457,
112
+ "val_loss": 0.7357572990044398,
113
+ "val_acc": 0.7737880496054115,
114
+ "val_referable_acc": 0.922435174746336,
115
+ "val_any_dr_acc": 0.8633596392333709,
116
+ "val_severe_or_pdr_acc": 0.9395152198421646,
117
+ "val_qwk": 0.7589173021521816,
118
+ "val_referable_auc": 0.9514882719851434,
119
+ "val_any_dr_auc": 0.9188145524927684,
120
+ "val_severe_or_pdr_auc": 0.9749677874156344
121
+ },
122
+ {
123
+ "epoch": 9,
124
+ "lr": 8.362196501476347e-05,
125
+ "train_loss": 0.7983663257616406,
126
+ "train_acc": 0.785281348669373,
127
+ "val_loss": 0.6312644582321572,
128
+ "val_acc": 0.8122886133032694,
129
+ "val_referable_acc": 0.9180947012401353,
130
+ "val_any_dr_acc": 0.8882750845546787,
131
+ "val_severe_or_pdr_acc": 0.9629650507328072,
132
+ "val_qwk": 0.7852212513783992,
133
+ "val_referable_auc": 0.9459528860088998,
134
+ "val_any_dr_auc": 0.9226028127904086,
135
+ "val_severe_or_pdr_auc": 0.9741718275877431
136
+ },
137
+ {
138
+ "epoch": 10,
139
+ "lr": 7.959536998847741e-05,
140
+ "train_loss": 0.7734421917216757,
141
+ "train_acc": 0.7890025935949482,
142
+ "val_loss": 0.652807928448734,
143
+ "val_acc": 0.7968996617812852,
144
+ "val_referable_acc": 0.9205186020293122,
145
+ "val_any_dr_acc": 0.8818489289740699,
146
+ "val_severe_or_pdr_acc": 0.9560315670800451,
147
+ "val_qwk": 0.7862390587287141,
148
+ "val_referable_auc": 0.9485443458714957,
149
+ "val_any_dr_auc": 0.9240858103951519,
150
+ "val_severe_or_pdr_auc": 0.9748014514631099
151
+ },
152
+ {
153
+ "epoch": 11,
154
+ "lr": 7.524999999999999e-05,
155
+ "train_loss": 0.7356365497552118,
156
+ "train_acc": 0.7935413847541724,
157
+ "val_loss": 0.6950417665376201,
158
+ "val_acc": 0.8027057497181511,
159
+ "val_referable_acc": 0.9224915445321308,
160
+ "val_any_dr_acc": 0.8719278466741827,
161
+ "val_severe_or_pdr_acc": 0.9626268320180383,
162
+ "val_qwk": 0.7840566914760235,
163
+ "val_referable_auc": 0.9528219758380423,
164
+ "val_any_dr_auc": 0.9236595806992055,
165
+ "val_severe_or_pdr_auc": 0.9734454237444407
166
+ },
167
+ {
168
+ "epoch": 12,
169
+ "lr": 7.06334638322521e-05,
170
+ "train_loss": 0.703734042397694,
171
+ "train_acc": 0.7984607577807848,
172
+ "val_loss": 0.7197710200657033,
173
+ "val_acc": 0.7735625704622322,
174
+ "val_referable_acc": 0.921815107102593,
175
+ "val_any_dr_acc": 0.8545095828635851,
176
+ "val_severe_or_pdr_acc": 0.9510710259301015,
177
+ "val_qwk": 0.7837397715419365,
178
+ "val_referable_auc": 0.9540776393967962,
179
+ "val_any_dr_auc": 0.9251081219486657,
180
+ "val_severe_or_pdr_auc": 0.9740435387218498
181
+ },
182
+ {
183
+ "epoch": 13,
184
+ "lr": 6.579634122155988e-05,
185
+ "train_loss": 0.6775929662401095,
186
+ "train_acc": 0.7984607577807848,
187
+ "val_loss": 0.7374675619427489,
188
+ "val_acc": 0.7498872604284104,
189
+ "val_referable_acc": 0.9068771138669673,
190
+ "val_any_dr_acc": 0.8481397970687712,
191
+ "val_severe_or_pdr_acc": 0.9284667418263811,
192
+ "val_qwk": 0.7465711006115658,
193
+ "val_referable_auc": 0.9518734454790881,
194
+ "val_any_dr_auc": 0.9208624478082748,
195
+ "val_severe_or_pdr_auc": 0.9757295048655574
196
+ },
197
+ {
198
+ "epoch": 14,
199
+ "lr": 6.079162869547907e-05,
200
+ "train_loss": 0.63867224941772,
201
+ "train_acc": 0.8038875732972486,
202
+ "val_loss": 0.638574194605751,
203
+ "val_acc": 0.7966178128523111,
204
+ "val_referable_acc": 0.9206877113866967,
205
+ "val_any_dr_acc": 0.8729988726042841,
206
+ "val_severe_or_pdr_acc": 0.9561443066516347,
207
+ "val_qwk": 0.7783495078517592,
208
+ "val_referable_auc": 0.9506779043335899,
209
+ "val_any_dr_auc": 0.9251737460897108,
210
+ "val_severe_or_pdr_auc": 0.9743007813541793
211
+ },
212
+ {
213
+ "epoch": 15,
214
+ "lr": 5.567415893174884e-05,
215
+ "train_loss": 0.6125179093547861,
216
+ "train_acc": 0.8125563824988723,
217
+ "val_loss": 0.6638782870137839,
218
+ "val_acc": 0.7777339346110484,
219
+ "val_referable_acc": 0.9192784667418263,
220
+ "val_any_dr_acc": 0.8587373167981962,
221
+ "val_severe_or_pdr_acc": 0.951803833145434,
222
+ "val_qwk": 0.7777181287118557,
223
+ "val_referable_auc": 0.9527175361264195,
224
+ "val_any_dr_auc": 0.9251897062865668,
225
+ "val_severe_or_pdr_auc": 0.9756308410127499
226
+ },
227
+ {
228
+ "epoch": 16,
229
+ "lr": 5.05e-05,
230
+ "train_loss": 0.5682916087197138,
231
+ "train_acc": 0.8178845286423094,
232
+ "val_loss": 0.6209135725103075,
233
+ "val_acc": 0.8029312288613303,
234
+ "val_referable_acc": 0.9244644870349493,
235
+ "val_any_dr_acc": 0.8774520856820744,
236
+ "val_severe_or_pdr_acc": 0.9574971815107103,
237
+ "val_qwk": 0.7954055446182169,
238
+ "val_referable_auc": 0.9493490659370094,
239
+ "val_any_dr_auc": 0.9210591320794231,
240
+ "val_severe_or_pdr_auc": 0.9754178512054537
241
+ },
242
+ {
243
+ "epoch": 17,
244
+ "lr": 4.532584106825115e-05,
245
+ "train_loss": 0.5481052464214488,
246
+ "train_acc": 0.8168978349120433,
247
+ "val_loss": 0.635486727247378,
248
+ "val_acc": 0.7961668545659526,
249
+ "val_referable_acc": 0.9253664036076663,
250
+ "val_any_dr_acc": 0.867869222096956,
251
+ "val_severe_or_pdr_acc": 0.9629650507328072,
252
+ "val_qwk": 0.7962354701827697,
253
+ "val_referable_auc": 0.9528051854428554,
254
+ "val_any_dr_auc": 0.9221625944032146,
255
+ "val_severe_or_pdr_auc": 0.9746945132924331
256
+ }
257
+ ]
runs/eyepacs_dr/inception_v3_fold3/best_model_only.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:859c08cfee33d0876f0715e6243007351c59318e48cf1ee05e03073b1b8dd28b
3
+ size 89735243
runs/eyepacs_dr/inception_v3_fold3/history.json ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "epoch": 1,
4
+ "lr": 0.0001,
5
+ "train_loss": 1.2025198198192872,
6
+ "train_acc": 0.5520410464591791,
7
+ "val_loss": 0.8228783474162036,
8
+ "val_acc": 0.7453776775648252,
9
+ "val_referable_acc": 0.8989289740698986,
10
+ "val_any_dr_acc": 0.8471251409244644,
11
+ "val_severe_or_pdr_acc": 0.9259301014656144,
12
+ "val_qwk": 0.6898827561312655,
13
+ "val_referable_auc": 0.9175911681283208,
14
+ "val_any_dr_auc": 0.8747995817301016,
15
+ "val_severe_or_pdr_auc": 0.9673576416115902
16
+ },
17
+ {
18
+ "epoch": 2,
19
+ "lr": 9.972883382072954e-05,
20
+ "train_loss": 1.0339324369347735,
21
+ "train_acc": 0.6820308976093821,
22
+ "val_loss": 0.8109341154641471,
23
+ "val_acc": 0.7558624577226607,
24
+ "val_referable_acc": 0.9067080045095829,
25
+ "val_any_dr_acc": 0.8419954904171364,
26
+ "val_severe_or_pdr_acc": 0.9482525366403608,
27
+ "val_qwk": 0.7271704083716478,
28
+ "val_referable_auc": 0.9356385507586122,
29
+ "val_any_dr_auc": 0.9036777524528425,
30
+ "val_severe_or_pdr_auc": 0.9724214181695979
31
+ },
32
+ {
33
+ "epoch": 3,
34
+ "lr": 9.891830623632339e-05,
35
+ "train_loss": 0.9782092773704633,
36
+ "train_acc": 0.7262629679747407,
37
+ "val_loss": 0.7510695437567881,
38
+ "val_acc": 0.7797632468996618,
39
+ "val_referable_acc": 0.9089627959413754,
40
+ "val_any_dr_acc": 0.8714205186020293,
41
+ "val_severe_or_pdr_acc": 0.947576099210823,
42
+ "val_qwk": 0.7372795022381535,
43
+ "val_referable_auc": 0.9319354956337893,
44
+ "val_any_dr_auc": 0.9054813371565816,
45
+ "val_severe_or_pdr_auc": 0.9716824987864531
46
+ },
47
+ {
48
+ "epoch": 4,
49
+ "lr": 9.757729755661011e-05,
50
+ "train_loss": 0.9379007722753956,
51
+ "train_acc": 0.7525090211998195,
52
+ "val_loss": 0.6549568566085252,
53
+ "val_acc": 0.8064825253664036,
54
+ "val_referable_acc": 0.9164599774520856,
55
+ "val_any_dr_acc": 0.8899661781285231,
56
+ "val_severe_or_pdr_acc": 0.9606538895152198,
57
+ "val_qwk": 0.7948299036997162,
58
+ "val_referable_auc": 0.9453975533926049,
59
+ "val_any_dr_auc": 0.9143007050135201,
60
+ "val_severe_or_pdr_auc": 0.9740853216832828
61
+ },
62
+ {
63
+ "epoch": 5,
64
+ "lr": 9.572050015330873e-05,
65
+ "train_loss": 0.8938556916954606,
66
+ "train_acc": 0.7624464366260713,
67
+ "val_loss": 0.7379223013004832,
68
+ "val_acc": 0.7736753100338218,
69
+ "val_referable_acc": 0.9153325817361894,
70
+ "val_any_dr_acc": 0.8563697857948139,
71
+ "val_severe_or_pdr_acc": 0.9559188275084555,
72
+ "val_qwk": 0.7567080724224234,
73
+ "val_referable_auc": 0.936530104661968,
74
+ "val_any_dr_auc": 0.9078175951114706,
75
+ "val_severe_or_pdr_auc": 0.9719054927000486
76
+ },
77
+ {
78
+ "epoch": 6,
79
+ "lr": 9.336825748732972e-05,
80
+ "train_loss": 0.871171380775062,
81
+ "train_acc": 0.7707064727108706,
82
+ "val_loss": 0.7070721405737833,
83
+ "val_acc": 0.7897406989853438,
84
+ "val_referable_acc": 0.9234498308906426,
85
+ "val_any_dr_acc": 0.8689966178128523,
86
+ "val_severe_or_pdr_acc": 0.9564261555806088,
87
+ "val_qwk": 0.7851643398048448,
88
+ "val_referable_auc": 0.947155445348272,
89
+ "val_any_dr_auc": 0.9142982950600038,
90
+ "val_severe_or_pdr_auc": 0.9759564243306822
91
+ },
92
+ {
93
+ "epoch": 7,
94
+ "lr": 9.05463412215599e-05,
95
+ "train_loss": 0.8448774115319826,
96
+ "train_acc": 0.7775851375732973,
97
+ "val_loss": 0.679549956227639,
98
+ "val_acc": 0.8164036076662908,
99
+ "val_referable_acc": 0.924520856820744,
100
+ "val_any_dr_acc": 0.8813416009019166,
101
+ "val_severe_or_pdr_acc": 0.9644870349492672,
102
+ "val_qwk": 0.7821747923185505,
103
+ "val_referable_auc": 0.9481949654990761,
104
+ "val_any_dr_auc": 0.9170575366190394,
105
+ "val_severe_or_pdr_auc": 0.9746656584892275
106
+ },
107
+ {
108
+ "epoch": 8,
109
+ "lr": 8.728566886113101e-05,
110
+ "train_loss": 0.8195717033094579,
111
+ "train_acc": 0.7809680875056383,
112
+ "val_loss": 0.6137274920604409,
113
+ "val_acc": 0.809413754227734,
114
+ "val_referable_acc": 0.9208004509582863,
115
+ "val_any_dr_acc": 0.8852311161217588,
116
+ "val_severe_or_pdr_acc": 0.9581172491544532,
117
+ "val_qwk": 0.7956730170227315,
118
+ "val_referable_auc": 0.9454241231831684,
119
+ "val_any_dr_auc": 0.9121724219624374,
120
+ "val_severe_or_pdr_auc": 0.9762521190396176
121
+ },
122
+ {
123
+ "epoch": 9,
124
+ "lr": 8.362196501476347e-05,
125
+ "train_loss": 0.7902204092351713,
126
+ "train_acc": 0.783026048714479,
127
+ "val_loss": 0.734050494479488,
128
+ "val_acc": 0.7920518602029313,
129
+ "val_referable_acc": 0.9162908680947013,
130
+ "val_any_dr_acc": 0.8687147688838782,
131
+ "val_severe_or_pdr_acc": 0.9494926719278467,
132
+ "val_qwk": 0.7573945822492804,
133
+ "val_referable_auc": 0.9479569164636216,
134
+ "val_any_dr_auc": 0.9187071783937456,
135
+ "val_severe_or_pdr_auc": 0.9708768529927934
136
+ },
137
+ {
138
+ "epoch": 10,
139
+ "lr": 7.959536998847741e-05,
140
+ "train_loss": 0.7650700230210227,
141
+ "train_acc": 0.7899188092016238,
142
+ "val_loss": 0.6066389189766386,
143
+ "val_acc": 0.8162908680947012,
144
+ "val_referable_acc": 0.9243517474633597,
145
+ "val_any_dr_acc": 0.8935174746335964,
146
+ "val_severe_or_pdr_acc": 0.9520293122886133,
147
+ "val_qwk": 0.7954633767456802,
148
+ "val_referable_auc": 0.9486835951455344,
149
+ "val_any_dr_auc": 0.9155345767059612,
150
+ "val_severe_or_pdr_auc": 0.9757451178074008
151
+ },
152
+ {
153
+ "epoch": 11,
154
+ "lr": 7.524999999999999e-05,
155
+ "train_loss": 0.7395555126368596,
156
+ "train_acc": 0.7941756878664863,
157
+ "val_loss": 0.6811727064572435,
158
+ "val_acc": 0.7918827508455468,
159
+ "val_referable_acc": 0.9159526493799324,
160
+ "val_any_dr_acc": 0.8719278466741827,
161
+ "val_severe_or_pdr_acc": 0.9613866967305524,
162
+ "val_qwk": 0.7880716708685575,
163
+ "val_referable_auc": 0.9474283812313362,
164
+ "val_any_dr_auc": 0.9180301775196268,
165
+ "val_severe_or_pdr_auc": 0.9750599305477764
166
+ },
167
+ {
168
+ "epoch": 12,
169
+ "lr": 7.06334638322521e-05,
170
+ "train_loss": 0.7014671185355924,
171
+ "train_acc": 0.8014208389715832,
172
+ "val_loss": 0.6409604030639235,
173
+ "val_acc": 0.8064261555806088,
174
+ "val_referable_acc": 0.9247463359639233,
175
+ "val_any_dr_acc": 0.8740698985343855,
176
+ "val_severe_or_pdr_acc": 0.9613303269447576,
177
+ "val_qwk": 0.7936155738468372,
178
+ "val_referable_auc": 0.9497614124427368,
179
+ "val_any_dr_auc": 0.9194105335392007,
180
+ "val_severe_or_pdr_auc": 0.9763317650573168
181
+ },
182
+ {
183
+ "epoch": 13,
184
+ "lr": 6.579634122155988e-05,
185
+ "train_loss": 0.6744532153702664,
186
+ "train_acc": 0.801547699594046,
187
+ "val_loss": 0.6877641824616924,
188
+ "val_acc": 0.788782412626832,
189
+ "val_referable_acc": 0.9089064261555806,
190
+ "val_any_dr_acc": 0.8720405862457723,
191
+ "val_severe_or_pdr_acc": 0.9422209695603156,
192
+ "val_qwk": 0.739571072166862,
193
+ "val_referable_auc": 0.9406614118746931,
194
+ "val_any_dr_auc": 0.9148891339689075,
195
+ "val_severe_or_pdr_auc": 0.9710664650311789
196
+ },
197
+ {
198
+ "epoch": 14,
199
+ "lr": 6.079162869547907e-05,
200
+ "train_loss": 0.6426656583326595,
201
+ "train_acc": 0.8055226657645467,
202
+ "val_loss": 0.6913967782104432,
203
+ "val_acc": 0.7749718151071026,
204
+ "val_referable_acc": 0.9171364148816235,
205
+ "val_any_dr_acc": 0.8547350620067644,
206
+ "val_severe_or_pdr_acc": 0.9571025930101466,
207
+ "val_qwk": 0.7778506643992253,
208
+ "val_referable_auc": 0.943374991710938,
209
+ "val_any_dr_auc": 0.9158870344500813,
210
+ "val_severe_or_pdr_auc": 0.9749824502445763
211
+ },
212
+ {
213
+ "epoch": 15,
214
+ "lr": 5.567415893174884e-05,
215
+ "train_loss": 0.6071117940986,
216
+ "train_acc": 0.8086659900766802,
217
+ "val_loss": 0.682904385593053,
218
+ "val_acc": 0.7785794813979707,
219
+ "val_referable_acc": 0.9217587373167982,
220
+ "val_any_dr_acc": 0.8600901916572717,
221
+ "val_severe_or_pdr_acc": 0.9599774520856821,
222
+ "val_qwk": 0.7914131533126754,
223
+ "val_referable_auc": 0.9482813427683984,
224
+ "val_any_dr_auc": 0.9183545981096161,
225
+ "val_severe_or_pdr_auc": 0.9763147380605655
226
+ },
227
+ {
228
+ "epoch": 16,
229
+ "lr": 5.05e-05,
230
+ "train_loss": 0.5828789531055419,
231
+ "train_acc": 0.8125140956247181,
232
+ "val_loss": 0.674042572795068,
233
+ "val_acc": 0.7829199549041713,
234
+ "val_referable_acc": 0.922435174746336,
235
+ "val_any_dr_acc": 0.8550732807215332,
236
+ "val_severe_or_pdr_acc": 0.9631905298759865,
237
+ "val_qwk": 0.7885094124571328,
238
+ "val_referable_auc": 0.9481833297632086,
239
+ "val_any_dr_auc": 0.9190702399333383,
240
+ "val_severe_or_pdr_auc": 0.9758976139800606
241
+ },
242
+ {
243
+ "epoch": 17,
244
+ "lr": 4.532584106825115e-05,
245
+ "train_loss": 0.543698809546934,
246
+ "train_acc": 0.8226347541723049,
247
+ "val_loss": 0.5916262666832366,
248
+ "val_acc": 0.8156708004509583,
249
+ "val_referable_acc": 0.9244644870349493,
250
+ "val_any_dr_acc": 0.8904735062006764,
251
+ "val_severe_or_pdr_acc": 0.9613303269447576,
252
+ "val_qwk": 0.8081824267323067,
253
+ "val_referable_auc": 0.9482310637662554,
254
+ "val_any_dr_auc": 0.9186220702726107,
255
+ "val_severe_or_pdr_auc": 0.9764491990590344
256
+ }
257
+ ]
runs/eyepacs_dr/inception_v3_fold4/best_model_only.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:98ff44110cf6b82ea97f53048fdb966eab3a733f5ddf8131f388236900d311ac
3
+ size 89735243
runs/eyepacs_dr/inception_v3_fold4/history.json ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "epoch": 1,
4
+ "lr": 0.0001,
5
+ "train_loss": 1.2001196421585376,
6
+ "train_acc": 0.5799503834009924,
7
+ "val_loss": 0.7569317756537789,
8
+ "val_acc": 0.788782412626832,
9
+ "val_referable_acc": 0.9088500563697858,
10
+ "val_any_dr_acc": 0.8717587373167982,
11
+ "val_severe_or_pdr_acc": 0.9549605411499437,
12
+ "val_qwk": 0.7429282333731377,
13
+ "val_referable_auc": 0.9194775303027065,
14
+ "val_any_dr_auc": 0.8871391804338257,
15
+ "val_severe_or_pdr_auc": 0.9680421111259949
16
+ },
17
+ {
18
+ "epoch": 2,
19
+ "lr": 9.972883382072954e-05,
20
+ "train_loss": 1.0279213763810409,
21
+ "train_acc": 0.6919119305367614,
22
+ "val_loss": 0.8218129052519126,
23
+ "val_acc": 0.7131905298759865,
24
+ "val_referable_acc": 0.9132468996617813,
25
+ "val_any_dr_acc": 0.8071589627959413,
26
+ "val_severe_or_pdr_acc": 0.9501691093573844,
27
+ "val_qwk": 0.7225971569447935,
28
+ "val_referable_auc": 0.9308688716088045,
29
+ "val_any_dr_auc": 0.9059231844560037,
30
+ "val_severe_or_pdr_auc": 0.973083919091364
31
+ },
32
+ {
33
+ "epoch": 3,
34
+ "lr": 9.891830623632339e-05,
35
+ "train_loss": 0.9740518329914594,
36
+ "train_acc": 0.7313937753721245,
37
+ "val_loss": 0.6883974176920791,
38
+ "val_acc": 0.797294250281849,
39
+ "val_referable_acc": 0.9175310033821872,
40
+ "val_any_dr_acc": 0.8841037204058625,
41
+ "val_severe_or_pdr_acc": 0.943630214205186,
42
+ "val_qwk": 0.756051220656397,
43
+ "val_referable_auc": 0.9364977917175292,
44
+ "val_any_dr_auc": 0.914930932335793,
45
+ "val_severe_or_pdr_auc": 0.9724510492530263
46
+ },
47
+ {
48
+ "epoch": 4,
49
+ "lr": 9.757729755661011e-05,
50
+ "train_loss": 0.9428328731385878,
51
+ "train_acc": 0.749351601262968,
52
+ "val_loss": 0.7232750525065984,
53
+ "val_acc": 0.8005073280721533,
54
+ "val_referable_acc": 0.917869222096956,
55
+ "val_any_dr_acc": 0.8906989853438557,
56
+ "val_severe_or_pdr_acc": 0.9491544532130778,
57
+ "val_qwk": 0.783084663770558,
58
+ "val_referable_auc": 0.9426302409992056,
59
+ "val_any_dr_auc": 0.9197314348315854,
60
+ "val_severe_or_pdr_auc": 0.9738521022661843
61
+ },
62
+ {
63
+ "epoch": 5,
64
+ "lr": 9.572050015330873e-05,
65
+ "train_loss": 0.9063401475219399,
66
+ "train_acc": 0.7640533378439333,
67
+ "val_loss": 0.6571064746366413,
68
+ "val_acc": 0.8170800450958287,
69
+ "val_referable_acc": 0.9222660653889515,
70
+ "val_any_dr_acc": 0.8917136414881623,
71
+ "val_severe_or_pdr_acc": 0.9569898534385569,
72
+ "val_qwk": 0.788183818867606,
73
+ "val_referable_auc": 0.9435813278531469,
74
+ "val_any_dr_auc": 0.9210081541524229,
75
+ "val_severe_or_pdr_auc": 0.9748177290801543
76
+ },
77
+ {
78
+ "epoch": 6,
79
+ "lr": 9.336825748732972e-05,
80
+ "train_loss": 0.8696432277647611,
81
+ "train_acc": 0.773849797023004,
82
+ "val_loss": 0.7369026844235391,
83
+ "val_acc": 0.7794250281848929,
84
+ "val_referable_acc": 0.9114430665163472,
85
+ "val_any_dr_acc": 0.8700112739571589,
86
+ "val_severe_or_pdr_acc": 0.938162344983089,
87
+ "val_qwk": 0.7573919491497015,
88
+ "val_referable_auc": 0.9446331246227204,
89
+ "val_any_dr_auc": 0.9217323785663145,
90
+ "val_severe_or_pdr_auc": 0.9769722038132437
91
+ },
92
+ {
93
+ "epoch": 7,
94
+ "lr": 9.05463412215599e-05,
95
+ "train_loss": 0.8532237627106203,
96
+ "train_acc": 0.7721019395579612,
97
+ "val_loss": 0.714924802317829,
98
+ "val_acc": 0.7762119503945885,
99
+ "val_referable_acc": 0.9133596392333709,
100
+ "val_any_dr_acc": 0.8534949267192785,
101
+ "val_severe_or_pdr_acc": 0.9580045095828635,
102
+ "val_qwk": 0.770881114926211,
103
+ "val_referable_auc": 0.9455423195798338,
104
+ "val_any_dr_auc": 0.9243729038402309,
105
+ "val_severe_or_pdr_auc": 0.9756124975617895
106
+ },
107
+ {
108
+ "epoch": 8,
109
+ "lr": 8.728566886113101e-05,
110
+ "train_loss": 0.8223982596510317,
111
+ "train_acc": 0.7796712900315742,
112
+ "val_loss": 0.6834435512275008,
113
+ "val_acc": 0.8021984216459978,
114
+ "val_referable_acc": 0.9120631341600902,
115
+ "val_any_dr_acc": 0.8750281848928974,
116
+ "val_severe_or_pdr_acc": 0.9632468996617812,
117
+ "val_qwk": 0.7856095873155075,
118
+ "val_referable_auc": 0.9450883852706131,
119
+ "val_any_dr_auc": 0.9239819931437874,
120
+ "val_severe_or_pdr_auc": 0.9751521751774899
121
+ },
122
+ {
123
+ "epoch": 9,
124
+ "lr": 8.362196501476347e-05,
125
+ "train_loss": 0.7951097573831267,
126
+ "train_acc": 0.7813627649977447,
127
+ "val_loss": 0.6987135975444196,
128
+ "val_acc": 0.7952649379932356,
129
+ "val_referable_acc": 0.9191657271702367,
130
+ "val_any_dr_acc": 0.8735062006764375,
131
+ "val_severe_or_pdr_acc": 0.9528184892897407,
132
+ "val_qwk": 0.780215455176247,
133
+ "val_referable_auc": 0.9475252265024259,
134
+ "val_any_dr_auc": 0.9271016594578287,
135
+ "val_severe_or_pdr_auc": 0.9774892586547659
136
+ },
137
+ {
138
+ "epoch": 10,
139
+ "lr": 7.959536998847741e-05,
140
+ "train_loss": 0.76612451310275,
141
+ "train_acc": 0.7922727785295445,
142
+ "val_loss": 0.6806002331693922,
143
+ "val_acc": 0.7983089064261556,
144
+ "val_referable_acc": 0.9065952649379933,
145
+ "val_any_dr_acc": 0.879086809470124,
146
+ "val_severe_or_pdr_acc": 0.9450958286358512,
147
+ "val_qwk": 0.7577391366454651,
148
+ "val_referable_auc": 0.941599400380651,
149
+ "val_any_dr_auc": 0.9213001872922106,
150
+ "val_severe_or_pdr_auc": 0.9757958039866796
151
+ },
152
+ {
153
+ "epoch": 11,
154
+ "lr": 7.524999999999999e-05,
155
+ "train_loss": 0.7444681421923756,
156
+ "train_acc": 0.7926251691474966,
157
+ "val_loss": 0.7777549177307421,
158
+ "val_acc": 0.743010146561443,
159
+ "val_referable_acc": 0.905749718151071,
160
+ "val_any_dr_acc": 0.8328635851183765,
161
+ "val_severe_or_pdr_acc": 0.9439120631341601,
162
+ "val_qwk": 0.7487336930435504,
163
+ "val_referable_auc": 0.9446526524090157,
164
+ "val_any_dr_auc": 0.9286820958770503,
165
+ "val_severe_or_pdr_auc": 0.9766265581046116
166
+ },
167
+ {
168
+ "epoch": 12,
169
+ "lr": 7.06334638322521e-05,
170
+ "train_loss": 0.7086018911610329,
171
+ "train_acc": 0.7951341903473161,
172
+ "val_loss": 0.5983526295040479,
173
+ "val_acc": 0.8147125140924465,
174
+ "val_referable_acc": 0.9256482525366404,
175
+ "val_any_dr_acc": 0.892728297632469,
176
+ "val_severe_or_pdr_acc": 0.9580608793686584,
177
+ "val_qwk": 0.8110785114483154,
178
+ "val_referable_auc": 0.9486358821012248,
179
+ "val_any_dr_auc": 0.9289309065981717,
180
+ "val_severe_or_pdr_auc": 0.9764373764737954
181
+ },
182
+ {
183
+ "epoch": 13,
184
+ "lr": 6.579634122155988e-05,
185
+ "train_loss": 0.685486784114943,
186
+ "train_acc": 0.798799052774019,
187
+ "val_loss": 0.7036147638802598,
188
+ "val_acc": 0.7593573844419391,
189
+ "val_referable_acc": 0.9249154453213078,
190
+ "val_any_dr_acc": 0.8441375422773394,
191
+ "val_severe_or_pdr_acc": 0.9520856820744081,
192
+ "val_qwk": 0.7909389147960119,
193
+ "val_referable_auc": 0.9498735397291792,
194
+ "val_any_dr_auc": 0.9288593958346787,
195
+ "val_severe_or_pdr_auc": 0.9767621284813532
196
+ },
197
+ {
198
+ "epoch": 14,
199
+ "lr": 6.079162869547907e-05,
200
+ "train_loss": 0.6543596743878665,
201
+ "train_acc": 0.8101037437979252,
202
+ "val_loss": 0.6764660255212794,
203
+ "val_acc": 0.7945885005636979,
204
+ "val_referable_acc": 0.9212514092446449,
205
+ "val_any_dr_acc": 0.8766065388951522,
206
+ "val_severe_or_pdr_acc": 0.9523111612175874,
207
+ "val_qwk": 0.8004058309513633,
208
+ "val_referable_auc": 0.9504607404671295,
209
+ "val_any_dr_auc": 0.9286490352111068,
210
+ "val_severe_or_pdr_auc": 0.9769738194948732
211
+ },
212
+ {
213
+ "epoch": 15,
214
+ "lr": 5.567415893174884e-05,
215
+ "train_loss": 0.6133327330481538,
216
+ "train_acc": 0.8088774244474515,
217
+ "val_loss": 0.671032424091352,
218
+ "val_acc": 0.784216459977452,
219
+ "val_referable_acc": 0.9173055242390079,
220
+ "val_any_dr_acc": 0.8608229988726043,
221
+ "val_severe_or_pdr_acc": 0.9617812852311162,
222
+ "val_qwk": 0.7850222340142421,
223
+ "val_referable_auc": 0.9432520081634717,
224
+ "val_any_dr_auc": 0.9289369213684067,
225
+ "val_severe_or_pdr_auc": 0.9740109531464078
226
+ },
227
+ {
228
+ "epoch": 16,
229
+ "lr": 5.05e-05,
230
+ "train_loss": 0.5739147271398063,
231
+ "train_acc": 0.81670049616599,
232
+ "val_loss": 0.7213401265719563,
233
+ "val_acc": 0.7605411499436302,
234
+ "val_referable_acc": 0.9140360766629086,
235
+ "val_any_dr_acc": 0.8392897406989853,
236
+ "val_severe_or_pdr_acc": 0.9552987598647125,
237
+ "val_qwk": 0.7688554755328247,
238
+ "val_referable_auc": 0.9424395237007641,
239
+ "val_any_dr_auc": 0.9210517056966835,
240
+ "val_severe_or_pdr_auc": 0.9737944518080359
241
+ },
242
+ {
243
+ "epoch": 17,
244
+ "lr": 4.532584106825115e-05,
245
+ "train_loss": 0.5483453719564585,
246
+ "train_acc": 0.8217890166892197,
247
+ "val_loss": 0.6458704775860774,
248
+ "val_acc": 0.8037767756482526,
249
+ "val_referable_acc": 0.9226606538895152,
250
+ "val_any_dr_acc": 0.8786922209695603,
251
+ "val_severe_or_pdr_acc": 0.961837655016911,
252
+ "val_qwk": 0.8013663508734132,
253
+ "val_referable_auc": 0.9431509003462377,
254
+ "val_any_dr_auc": 0.9270491886099637,
255
+ "val_severe_or_pdr_auc": 0.9746947169560793
256
+ },
257
+ {
258
+ "epoch": 18,
259
+ "lr": 4.0208371304520894e-05,
260
+ "train_loss": 0.5206159053149393,
261
+ "train_acc": 0.8248054803788903,
262
+ "val_loss": 0.6574336617748257,
263
+ "val_acc": 0.787880496054115,
264
+ "val_referable_acc": 0.9196730552423901,
265
+ "val_any_dr_acc": 0.8589064261555807,
266
+ "val_severe_or_pdr_acc": 0.9645434047350621,
267
+ "val_qwk": 0.7916413157545019,
268
+ "val_referable_auc": 0.9478035943818823,
269
+ "val_any_dr_auc": 0.9298872303811361,
270
+ "val_severe_or_pdr_auc": 0.9746491841101529
271
+ },
272
+ {
273
+ "epoch": 19,
274
+ "lr": 3.520365877844009e-05,
275
+ "train_loss": 0.4738634637832964,
276
+ "train_acc": 0.8307115471357691,
277
+ "val_loss": 0.6353207715334457,
278
+ "val_acc": 0.7983089064261556,
279
+ "val_referable_acc": 0.9183201803833145,
280
+ "val_any_dr_acc": 0.8669109357384442,
281
+ "val_severe_or_pdr_acc": 0.9680383314543405,
282
+ "val_qwk": 0.7947142863535627,
283
+ "val_referable_auc": 0.9461027792896338,
284
+ "val_any_dr_auc": 0.9288580381916022,
285
+ "val_severe_or_pdr_auc": 0.9744999538796333
286
+ }
287
+ ]
train.py ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import random
4
+ from pathlib import Path
5
+
6
+ import numpy as np
7
+ import torch
8
+ import torch.nn.functional as F
9
+ from torch.utils.data import DataLoader
10
+ from tqdm import tqdm
11
+
12
+ from augmentations import get_train_transforms, get_val_transforms
13
+ from dataloader import EyePACSDataset
14
+ from model import DeepSeeNet
15
+
16
+
17
+ N_CLASSES = 5
18
+
19
+
20
+ class AlbumentationsTransform:
21
+ def __init__(self, transform):
22
+ self.transform = transform
23
+
24
+ def __call__(self, image):
25
+ return self.transform(image=np.asarray(image))["image"]
26
+
27
+
28
+ def parse_args():
29
+ parser = argparse.ArgumentParser(description="Train EyePACS DR classifier.")
30
+
31
+ parser.add_argument("--root", required=True, help="EyePACS root folder.")
32
+ parser.add_argument("--output-dir", default="checkpoints/eyepacs_dr")
33
+
34
+ parser.add_argument("--backbone", default="inception_v3")
35
+ parser.add_argument("--image-size", type=int, default=1024)
36
+ parser.add_argument("--epochs", type=int, default=20)
37
+ parser.add_argument("--batch-size", type=int, default=32)
38
+ parser.add_argument("--num-workers", type=int, default=8)
39
+
40
+ parser.add_argument("--fold", type=int, default=0)
41
+ parser.add_argument("--n-folds", type=int, default=5)
42
+ parser.add_argument("--seed", type=int, default=42)
43
+
44
+ parser.add_argument("--lr", type=float, default=1e-4)
45
+ parser.add_argument("--weight-decay", type=float, default=1e-4)
46
+ parser.add_argument("--no-pretrained", action="store_true")
47
+ parser.add_argument("--freeze-backbone", action="store_true")
48
+ parser.add_argument("--no-class-weights", action="store_true")
49
+
50
+ parser.add_argument("--scheduler", choices=["none", "cosine", "step"], default="cosine")
51
+ parser.add_argument("--min-lr", type=float, default=1e-6)
52
+ parser.add_argument("--step-size", type=int, default=5)
53
+ parser.add_argument("--gamma", type=float, default=0.5)
54
+
55
+ parser.add_argument("--amp", action="store_true")
56
+ parser.add_argument("--grad-clip", type=float, default=0.0)
57
+ parser.add_argument("--save-every", type=int, default=0)
58
+
59
+ parser.add_argument("--wandb", action="store_true")
60
+ parser.add_argument("--wandb-project", default="eyepacs-dr")
61
+
62
+ return parser.parse_args()
63
+
64
+
65
+ def set_seed(seed):
66
+ random.seed(seed)
67
+ np.random.seed(seed)
68
+ torch.manual_seed(seed)
69
+ torch.cuda.manual_seed_all(seed)
70
+
71
+
72
+ def unwrap_logits(output):
73
+ if isinstance(output, (tuple, list)):
74
+ return output[0]
75
+ return output
76
+
77
+
78
+ def get_class_weights(dataset, device):
79
+ labels = torch.tensor([s["label"] for s in dataset.samples], dtype=torch.long)
80
+ counts = torch.bincount(labels, minlength=N_CLASSES).clamp_min(1)
81
+ weights = counts.sum() / (N_CLASSES * counts)
82
+ return weights.to(device)
83
+
84
+
85
+ def build_scheduler(optimizer, args):
86
+ if args.scheduler == "cosine":
87
+ return torch.optim.lr_scheduler.CosineAnnealingLR(
88
+ optimizer,
89
+ T_max=args.epochs,
90
+ eta_min=args.min_lr,
91
+ )
92
+
93
+ if args.scheduler == "step":
94
+ return torch.optim.lr_scheduler.StepLR(
95
+ optimizer,
96
+ step_size=args.step_size,
97
+ gamma=args.gamma,
98
+ )
99
+
100
+ return None
101
+
102
+
103
+ def make_loader(dataset, batch_size, num_workers, shuffle, device):
104
+ return DataLoader(
105
+ dataset,
106
+ batch_size=batch_size,
107
+ shuffle=shuffle,
108
+ num_workers=num_workers,
109
+ pin_memory=device.type == "cuda",
110
+ drop_last=shuffle,
111
+ persistent_workers=num_workers > 0,
112
+ )
113
+
114
+
115
+ def train_one_epoch(
116
+ model,
117
+ loader,
118
+ optimizer,
119
+ scaler,
120
+ criterion,
121
+ device,
122
+ use_amp=True,
123
+ grad_clip=0.0,
124
+ ):
125
+ model.train()
126
+
127
+ total_loss = 0.0
128
+ total_correct = 0
129
+ total_samples = 0
130
+
131
+ pbar = tqdm(loader, desc="Train", leave=False)
132
+
133
+ for images, labels in pbar:
134
+ images = images.to(device, non_blocking=True)
135
+ labels = labels.to(device, non_blocking=True)
136
+
137
+ optimizer.zero_grad(set_to_none=True)
138
+
139
+ with torch.amp.autocast("cuda", enabled=use_amp and device.type == "cuda"):
140
+ logits = unwrap_logits(model(images))
141
+ loss = criterion(logits, labels)
142
+
143
+ if scaler is not None:
144
+ scaler.scale(loss).backward()
145
+
146
+ if grad_clip > 0:
147
+ scaler.unscale_(optimizer)
148
+ torch.nn.utils.clip_grad_norm_(model.parameters(), grad_clip)
149
+
150
+ scaler.step(optimizer)
151
+ scaler.update()
152
+ else:
153
+ loss.backward()
154
+
155
+ if grad_clip > 0:
156
+ torch.nn.utils.clip_grad_norm_(model.parameters(), grad_clip)
157
+
158
+ optimizer.step()
159
+
160
+ batch_size = labels.size(0)
161
+ total_loss += loss.item() * batch_size
162
+ total_correct += (logits.argmax(dim=1) == labels).sum().item()
163
+ total_samples += batch_size
164
+
165
+ pbar.set_postfix(
166
+ loss=f"{total_loss / total_samples:.4f}",
167
+ acc=f"{total_correct / total_samples:.4f}",
168
+ )
169
+
170
+ return {
171
+ "loss": total_loss / total_samples,
172
+ "acc": total_correct / total_samples,
173
+ }
174
+
175
+
176
+ @torch.no_grad()
177
+ def evaluate(model, loader, criterion, device, use_amp=True):
178
+ model.eval()
179
+
180
+ total_loss = 0.0
181
+ total_correct = 0
182
+ total_samples = 0
183
+
184
+ all_labels = []
185
+ all_probs = []
186
+ all_preds = []
187
+
188
+ pbar = tqdm(loader, desc="Val", leave=False)
189
+
190
+ for images, labels in pbar:
191
+ images = images.to(device, non_blocking=True)
192
+ labels = labels.to(device, non_blocking=True)
193
+
194
+ with torch.amp.autocast("cuda", enabled=use_amp and device.type == "cuda"):
195
+ logits = unwrap_logits(model(images))
196
+ loss = criterion(logits, labels)
197
+
198
+ probs = F.softmax(logits, dim=1)
199
+ preds = probs.argmax(dim=1)
200
+
201
+ batch_size = labels.size(0)
202
+ total_loss += loss.item() * batch_size
203
+ total_correct += (preds == labels).sum().item()
204
+ total_samples += batch_size
205
+
206
+ all_labels.append(labels.detach().cpu())
207
+ all_probs.append(probs.detach().cpu())
208
+ all_preds.append(preds.detach().cpu())
209
+
210
+ pbar.set_postfix(
211
+ loss=f"{total_loss / total_samples:.4f}",
212
+ acc=f"{total_correct / total_samples:.4f}",
213
+ )
214
+
215
+ labels = torch.cat(all_labels).numpy()
216
+ probs = torch.cat(all_probs).numpy()
217
+ preds = torch.cat(all_preds).numpy()
218
+
219
+ metrics = {
220
+ "loss": total_loss / total_samples,
221
+ "acc": total_correct / total_samples,
222
+ "referable_acc": float(((labels >= 2) == (preds >= 2)).mean()),
223
+ "any_dr_acc": float(((labels >= 1) == (preds >= 1)).mean()),
224
+ "severe_or_pdr_acc": float(((labels >= 3) == (preds >= 3)).mean()),
225
+ }
226
+
227
+ try:
228
+ from sklearn.metrics import cohen_kappa_score, roc_auc_score
229
+
230
+ metrics["qwk"] = float(cohen_kappa_score(labels, preds, weights="quadratic"))
231
+ metrics["referable_auc"] = float(
232
+ roc_auc_score((labels >= 2).astype(int), probs[:, 2:].sum(axis=1))
233
+ )
234
+ metrics["any_dr_auc"] = float(
235
+ roc_auc_score((labels >= 1).astype(int), probs[:, 1:].sum(axis=1))
236
+ )
237
+ metrics["severe_or_pdr_auc"] = float(
238
+ roc_auc_score((labels >= 3).astype(int), probs[:, 3:].sum(axis=1))
239
+ )
240
+ except Exception:
241
+ metrics["qwk"] = float("nan")
242
+ metrics["referable_auc"] = float("nan")
243
+ metrics["any_dr_auc"] = float("nan")
244
+ metrics["severe_or_pdr_auc"] = float("nan")
245
+
246
+ return metrics
247
+
248
+
249
+ def save_checkpoint(path, model, optimizer, scheduler, epoch, best_metric, args, model_only=False):
250
+ path = Path(path)
251
+ path.parent.mkdir(parents=True, exist_ok=True)
252
+
253
+ ckpt = {
254
+ "epoch": epoch,
255
+ "model": model.state_dict(),
256
+ "best_metric": best_metric,
257
+ "args": vars(args),
258
+ "id_to_label": {
259
+ 0: "no_dr",
260
+ 1: "mild_npdr",
261
+ 2: "moderate_npdr",
262
+ 3: "severe_npdr",
263
+ 4: "pdr",
264
+ },
265
+ }
266
+
267
+ if not model_only:
268
+ ckpt["optimizer"] = optimizer.state_dict()
269
+ if scheduler is not None:
270
+ ckpt["scheduler"] = scheduler.state_dict()
271
+
272
+ torch.save(ckpt, path)
273
+
274
+
275
+ def main():
276
+ args = parse_args()
277
+ set_seed(args.seed)
278
+
279
+ output_dir = Path(args.output_dir)
280
+ output_dir.mkdir(parents=True, exist_ok=True)
281
+
282
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
283
+ use_amp = args.amp and device.type == "cuda"
284
+
285
+ train_dataset = EyePACSDataset(
286
+ root=args.root,
287
+ split="all",
288
+ all_mode="train",
289
+ transform=AlbumentationsTransform(get_train_transforms(args.image_size)),
290
+ seed=args.seed,
291
+ fold=args.fold,
292
+ n_folds=args.n_folds,
293
+ )
294
+
295
+ val_dataset = EyePACSDataset(
296
+ root=args.root,
297
+ split="all",
298
+ all_mode="val",
299
+ transform=AlbumentationsTransform(get_val_transforms(args.image_size)),
300
+ seed=args.seed,
301
+ fold=args.fold,
302
+ n_folds=args.n_folds,
303
+ )
304
+
305
+ train_loader = make_loader(
306
+ train_dataset,
307
+ batch_size=args.batch_size,
308
+ num_workers=args.num_workers,
309
+ shuffle=True,
310
+ device=device,
311
+ )
312
+
313
+ val_loader = make_loader(
314
+ val_dataset,
315
+ batch_size=args.batch_size,
316
+ num_workers=args.num_workers,
317
+ shuffle=False,
318
+ device=device,
319
+ )
320
+
321
+ model = DeepSeeNet(
322
+ n_classes=N_CLASSES,
323
+ backbone=args.backbone,
324
+ pretrained=not args.no_pretrained,
325
+ freeze_backbone=args.freeze_backbone,
326
+ ).to(device)
327
+
328
+ class_weights = None
329
+ if not args.no_class_weights:
330
+ class_weights = get_class_weights(train_dataset, device)
331
+
332
+ train_criterion = torch.nn.CrossEntropyLoss(weight=class_weights)
333
+ val_criterion = torch.nn.CrossEntropyLoss()
334
+
335
+ optimizer = torch.optim.AdamW(
336
+ model.parameters(),
337
+ lr=args.lr,
338
+ weight_decay=args.weight_decay,
339
+ )
340
+
341
+ scheduler = build_scheduler(optimizer, args)
342
+ scaler = torch.amp.GradScaler("cuda") if use_amp else None
343
+
344
+ wandb = None
345
+ if args.wandb:
346
+ import wandb
347
+
348
+ wandb.init(project=args.wandb_project, config=vars(args))
349
+
350
+ print("\nEyePACS DR training")
351
+ print("-------------------")
352
+ print(f"Device: {device}")
353
+ print(f"Root: {args.root}")
354
+ print(f"Output: {output_dir}")
355
+ print(f"Backbone: {args.backbone}")
356
+ print(f"Image size: {args.image_size}")
357
+ print(f"Fold: {args.fold}/{args.n_folds}")
358
+ print(f"Train samples: {len(train_dataset)}")
359
+ print(f"Val samples: {len(val_dataset)}")
360
+ print(f"AMP: {use_amp}")
361
+ print(f"Pretrained: {not args.no_pretrained}")
362
+
363
+ if class_weights is not None:
364
+ print(f"Class weights: {class_weights.detach().cpu().tolist()}")
365
+
366
+ best_qwk = -float("inf")
367
+ history = []
368
+
369
+ for epoch in range(1, args.epochs + 1):
370
+ print(f"\nEpoch [{epoch:03d}/{args.epochs}]")
371
+
372
+ train_metrics = train_one_epoch(
373
+ model=model,
374
+ loader=train_loader,
375
+ optimizer=optimizer,
376
+ scaler=scaler,
377
+ criterion=train_criterion,
378
+ device=device,
379
+ use_amp=use_amp,
380
+ grad_clip=args.grad_clip,
381
+ )
382
+
383
+ val_metrics = evaluate(
384
+ model=model,
385
+ loader=val_loader,
386
+ criterion=val_criterion,
387
+ device=device,
388
+ use_amp=use_amp,
389
+ )
390
+
391
+ lr = optimizer.param_groups[0]["lr"]
392
+
393
+ row = {
394
+ "epoch": epoch,
395
+ "lr": lr,
396
+ **{f"train_{k}": v for k, v in train_metrics.items()},
397
+ **{f"val_{k}": v for k, v in val_metrics.items()},
398
+ }
399
+ history.append(row)
400
+
401
+ print(
402
+ f"lr={lr:.2e} "
403
+ f"train_loss={train_metrics['loss']:.4f} "
404
+ f"train_acc={train_metrics['acc']:.4f} "
405
+ f"val_loss={val_metrics['loss']:.4f} "
406
+ f"val_acc={val_metrics['acc']:.4f} "
407
+ f"val_qwk={val_metrics['qwk']:.4f} "
408
+ f"val_ref_auc={val_metrics['referable_auc']:.4f}"
409
+ )
410
+
411
+ if wandb is not None:
412
+ wandb.log(row)
413
+
414
+ with (output_dir / "history.json").open("w") as f:
415
+ json.dump(history, f, indent=2)
416
+
417
+ monitor = val_metrics["qwk"]
418
+ if np.isnan(monitor):
419
+ monitor = -val_metrics["loss"]
420
+
421
+ if monitor > best_qwk:
422
+ best_qwk = monitor
423
+
424
+ save_checkpoint(
425
+ output_dir / "best.pt",
426
+ model=model,
427
+ optimizer=optimizer,
428
+ scheduler=scheduler,
429
+ epoch=epoch,
430
+ best_metric=best_qwk,
431
+ args=args,
432
+ model_only=False,
433
+ )
434
+
435
+ save_checkpoint(
436
+ output_dir / "best_model_only.pt",
437
+ model=model,
438
+ optimizer=optimizer,
439
+ scheduler=scheduler,
440
+ epoch=epoch,
441
+ best_metric=best_qwk,
442
+ args=args,
443
+ model_only=True,
444
+ )
445
+
446
+ print(f"Saved best checkpoint: monitor={best_qwk:.4f}")
447
+
448
+ if args.save_every > 0 and epoch % args.save_every == 0:
449
+ save_checkpoint(
450
+ output_dir / f"epoch_{epoch:03d}.pt",
451
+ model=model,
452
+ optimizer=optimizer,
453
+ scheduler=scheduler,
454
+ epoch=epoch,
455
+ best_metric=best_qwk,
456
+ args=args,
457
+ model_only=False,
458
+ )
459
+
460
+ if scheduler is not None:
461
+ scheduler.step()
462
+
463
+ save_checkpoint(
464
+ output_dir / "last.pt",
465
+ model=model,
466
+ optimizer=optimizer,
467
+ scheduler=scheduler,
468
+ epoch=args.epochs,
469
+ best_metric=best_qwk,
470
+ args=args,
471
+ model_only=False,
472
+ )
473
+
474
+ print("\nTraining complete.")
475
+ print(f"Best monitor/QWK: {best_qwk:.4f}")
476
+ print(f"Saved to: {output_dir}")
477
+
478
+
479
+ if __name__ == "__main__":
480
+ main()