fastdrs 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fastdrs/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ # Package marker for fastdrs
2
+
fastdrs/data.py ADDED
@@ -0,0 +1,113 @@
1
+ """
2
+ Data loading and dataset handling for Diabetic Retinopathy screening.
3
+
4
+ Supports Hugging Face datasets (e.g. `ctmedtech/EYEPACS`) as well as standard PyTorch datasets,
5
+ with reproducible train/validation/test splitting and DataLoader construction.
6
+ """
7
+
8
+ from typing import Tuple, Dict, Any, Optional
9
+ import torch
10
+ from torch.utils.data import Dataset, DataLoader, Subset
11
+ from sklearn.model_selection import train_test_split
12
+ from datasets import load_dataset
13
+ from PIL import Image
14
+ import numpy as np
15
+
16
+ from fastdrs.preprocessing import get_transforms
17
+
18
+
19
+ class EyepacsDataset(Dataset):
20
+ """
21
+ PyTorch Wrapper for Hugging Face EYEPACS dataset or image list.
22
+ """
23
+ def __init__(self, hf_dataset, transform=None):
24
+ self.dataset = hf_dataset
25
+ self.transform = transform
26
+
27
+ def __len__(self) -> int:
28
+ return len(self.dataset)
29
+
30
+ def __getitem__(self, idx: int) -> Tuple[torch.Tensor, int]:
31
+ item = self.dataset[int(idx)]
32
+ image = item["image"]
33
+
34
+ # Ensure image is PIL Image in RGB mode
35
+ if not isinstance(image, Image.Image):
36
+ image = Image.fromarray(np.uint8(image))
37
+ if image.mode != "RGB":
38
+ image = image.convert("RGB")
39
+
40
+ label = item.get("label", item.get("dr_level", item.get("level", 0)))
41
+
42
+ if self.transform is not None:
43
+ image = self.transform(image)
44
+
45
+ return image, int(label)
46
+
47
+
48
+ def create_dataloaders(
49
+ dataset_name: str = "sngsfydy/aptos",
50
+ batch_size: int = 32,
51
+ img_size: int = 224,
52
+ val_split: float = 0.15,
53
+ test_split: float = 0.15,
54
+ seed: int = 42,
55
+ num_workers: int = 2,
56
+ use_ben_graham: bool = False
57
+ ) -> Tuple[DataLoader, DataLoader, DataLoader, Dict[str, Any]]:
58
+ """
59
+ Loads dataset, splits into train/val/test, applies transforms, and returns PyTorch DataLoaders.
60
+
61
+ Returns:
62
+ train_loader, val_loader, test_loader, dataset_info metadata dict
63
+ """
64
+ # Load raw dataset from Hugging Face
65
+ raw_dataset = load_dataset(dataset_name, split="train")
66
+ total_size = len(raw_dataset)
67
+ indices = np.arange(total_size)
68
+
69
+ # Get labels if available for stratified splitting
70
+ try:
71
+ labels = [raw_dataset[i].get("label", raw_dataset[i].get("dr_level", 0)) for i in range(min(total_size, 1000))]
72
+ stratify = None # Avoid full scan if dataset is large, or use stratified split on subset
73
+ except Exception:
74
+ labels = None
75
+
76
+ # Train / Val / Test Split
77
+ train_val_idx, test_idx = train_test_split(
78
+ indices, test_size=test_split, random_state=seed, shuffle=True
79
+ )
80
+ relative_val_size = val_split / (1.0 - test_split)
81
+ train_idx, val_idx = train_test_split(
82
+ train_val_idx, test_size=relative_val_size, random_state=seed, shuffle=True
83
+ )
84
+
85
+ train_idx = [int(i) for i in train_idx]
86
+ val_idx = [int(i) for i in val_idx]
87
+ test_idx = [int(i) for i in test_idx]
88
+
89
+ train_transform = get_transforms(img_size=img_size, is_train=True, use_ben_graham=use_ben_graham)
90
+ eval_transform = get_transforms(img_size=img_size, is_train=False, use_ben_graham=use_ben_graham)
91
+
92
+ full_train_ds = EyepacsDataset(raw_dataset, transform=train_transform)
93
+ full_eval_ds = EyepacsDataset(raw_dataset, transform=eval_transform)
94
+
95
+ train_ds = Subset(full_train_ds, train_idx)
96
+ val_ds = Subset(full_eval_ds, val_idx)
97
+ test_ds = Subset(full_eval_ds, test_idx)
98
+
99
+ train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=num_workers, pin_memory=True)
100
+ val_loader = DataLoader(val_ds, batch_size=batch_size, shuffle=False, num_workers=num_workers, pin_memory=True)
101
+ test_loader = DataLoader(test_ds, batch_size=batch_size, shuffle=False, num_workers=num_workers, pin_memory=True)
102
+
103
+ info = {
104
+ "dataset_name": dataset_name,
105
+ "total_samples": total_size,
106
+ "train_samples": len(train_ds),
107
+ "val_samples": len(val_ds),
108
+ "test_samples": len(test_ds),
109
+ "batch_size": batch_size,
110
+ "img_size": img_size
111
+ }
112
+
113
+ return train_loader, val_loader, test_loader, info
File without changes
@@ -0,0 +1 @@
1
+ .py
fastdrs/evaluation.py ADDED
@@ -0,0 +1,111 @@
1
+ """
2
+ Evaluation and benchmarking module for Diabetic Retinopathy screening models.
3
+
4
+ Calculates clinical metrics:
5
+ - Accuracy, Precision, Sensitivity/Recall, Specificity, F1 Score
6
+ - Per-class classification report and Confusion Matrix
7
+ - Inference latency and deployment feasibility metrics
8
+ """
9
+
10
+ from typing import Dict, Any, List, Tuple, Optional
11
+ import time
12
+ import torch
13
+ import torch.nn as nn
14
+ import numpy as np
15
+ from sklearn.metrics import accuracy_score, precision_recall_fscore_support, confusion_matrix
16
+
17
+ from fastdrs.models import get_model_info
18
+
19
+
20
+ def compute_specificity(cm: np.ndarray) -> np.ndarray:
21
+ """
22
+ Computes per-class specificity from confusion matrix.
23
+ Specificity = TN / (TN + FP)
24
+ """
25
+ num_classes = cm.shape[0]
26
+ specificities = []
27
+ total = np.sum(cm)
28
+ for i in range(num_classes):
29
+ tp = cm[i, i]
30
+ fp = np.sum(cm[:, i]) - tp
31
+ fn = np.sum(cm[i, :]) - tp
32
+ tn = total - (tp + fp + fn)
33
+ spec = tn / (tn + fp) if (tn + fp) > 0 else 0.0
34
+ specificities.append(spec)
35
+ return np.array(specificities)
36
+
37
+
38
+ def evaluate_model(
39
+ model: nn.Module,
40
+ dataloader: torch.utils.data.DataLoader,
41
+ device: torch.device,
42
+ class_names: Optional[List[str]] = None
43
+ ) -> Dict[str, Any]:
44
+ """
45
+ Evaluates a model on a dataloader, measuring clinical performance and inference latency.
46
+ """
47
+ if class_names is None:
48
+ class_names = ["0 - No DR", "1 - Mild", "2 - Moderate", "3 - Severe", "4 - Proliferative"]
49
+
50
+ model.to(device)
51
+ model.eval()
52
+
53
+ all_preds = []
54
+ all_targets = []
55
+ inference_times = []
56
+
57
+ with torch.no_grad():
58
+ for inputs, targets in dataloader:
59
+ inputs = inputs.to(device)
60
+
61
+ start_time = time.perf_counter()
62
+ outputs = model(inputs)
63
+ if device.type == "cuda":
64
+ torch.cuda.synchronize()
65
+ end_time = time.perf_counter()
66
+
67
+ batch_latency = (end_time - start_time) / inputs.size(0)
68
+ inference_times.append(batch_latency)
69
+
70
+ preds = torch.argmax(outputs, dim=1).cpu().numpy()
71
+ all_preds.extend(preds)
72
+ all_targets.extend(targets.numpy())
73
+
74
+ all_preds = np.array(all_preds)
75
+ all_targets = np.array(all_targets)
76
+
77
+ acc = accuracy_score(all_targets, all_preds)
78
+ precision, recall, f1, _ = precision_recall_fscore_support(all_targets, all_preds, average='macro', zero_division=0)
79
+ _, _, f1_weighted, _ = precision_recall_fscore_support(all_targets, all_preds, average='weighted', zero_division=0)
80
+
81
+ cm = confusion_matrix(all_targets, all_preds, labels=list(range(len(class_names))))
82
+ per_class_spec = compute_specificity(cm)
83
+ per_class_prec, per_class_rec, per_class_f1, _ = precision_recall_fscore_support(
84
+ all_targets, all_preds, average=None, labels=list(range(len(class_names))), zero_division=0
85
+ )
86
+
87
+ avg_latency_ms = float(np.mean(inference_times) * 1000.0)
88
+ model_info = get_model_info(model)
89
+
90
+ results = {
91
+ "accuracy": round(float(acc), 4),
92
+ "precision_macro": round(float(precision), 4),
93
+ "sensitivity_macro": round(float(recall), 4),
94
+ "specificity_macro": round(float(np.mean(per_class_spec)), 4),
95
+ "f1_macro": round(float(f1), 4),
96
+ "f1_weighted": round(float(f1_weighted), 4),
97
+ "inference_latency_ms": round(avg_latency_ms, 2),
98
+ "model_size_mb": model_info["model_size_mb"],
99
+ "total_parameters": model_info["total_parameters"],
100
+ "confusion_matrix": cm.tolist(),
101
+ "per_class": {
102
+ class_names[i]: {
103
+ "precision": round(float(per_class_prec[i]), 4),
104
+ "sensitivity_recall": round(float(per_class_rec[i]), 4),
105
+ "specificity": round(float(per_class_spec[i]), 4),
106
+ "f1": round(float(per_class_f1[i]), 4),
107
+ } for i in range(len(class_names))
108
+ }
109
+ }
110
+
111
+ return results
fastdrs/models.py ADDED
@@ -0,0 +1,110 @@
1
+ """
2
+ Model architecture factory for Diabetic Retinopathy classification.
3
+
4
+ Supports building and benchmarking multiple vision architectures:
5
+ - ResNet (e.g. resnet50, resnet18)
6
+ - DenseNet (e.g. densenet121)
7
+ - ConvNeXt (e.g. convnext_tiny)
8
+ - Swin Transformer (e.g. swin_t)
9
+ - MobileNetV2 (efficient baseline)
10
+ """
11
+
12
+ from typing import Tuple, Dict, Any
13
+ import torch
14
+ import torch.nn as nn
15
+ import torchvision.models as models
16
+
17
+
18
+ def build_model(
19
+ architecture: str = "resnet50",
20
+ num_classes: int = 5,
21
+ pretrained: bool = True,
22
+ dropout_rate: float = 0.2
23
+ ) -> nn.Module:
24
+ """
25
+ Constructs a vision model with customized classification head.
26
+
27
+ Args:
28
+ architecture: Choice of 'resnet50', 'resnet18', 'densenet121', 'convnext_tiny', 'swin_t', 'mobilenet_v2'.
29
+ num_classes: Number of output severity classes (default: 5).
30
+ pretrained: Whether to use ImageNet pre-trained weights.
31
+ dropout_rate: Dropout rate before classification layer.
32
+ """
33
+ arch = architecture.lower()
34
+
35
+ if arch == "resnet50":
36
+ weights = models.ResNet50_Weights.DEFAULT if pretrained else None
37
+ model = models.resnet50(weights=weights)
38
+ in_features = model.fc.in_features
39
+ model.fc = nn.Sequential(
40
+ nn.Dropout(p=dropout_rate),
41
+ nn.Linear(in_features, num_classes)
42
+ )
43
+
44
+ elif arch == "resnet18":
45
+ weights = models.ResNet18_Weights.DEFAULT if pretrained else None
46
+ model = models.resnet18(weights=weights)
47
+ in_features = model.fc.in_features
48
+ model.fc = nn.Sequential(
49
+ nn.Dropout(p=dropout_rate),
50
+ nn.Linear(in_features, num_classes)
51
+ )
52
+
53
+ elif arch == "densenet121":
54
+ weights = models.DenseNet121_Weights.DEFAULT if pretrained else None
55
+ model = models.densenet121(weights=weights)
56
+ in_features = model.classifier.in_features
57
+ model.classifier = nn.Sequential(
58
+ nn.Dropout(p=dropout_rate),
59
+ nn.Linear(in_features, num_classes)
60
+ )
61
+
62
+ elif arch in ("convnext_tiny", "convnext"):
63
+ weights = models.ConvNeXt_Tiny_Weights.DEFAULT if pretrained else None
64
+ model = models.convnext_tiny(weights=weights)
65
+ in_features = model.classifier[2].in_features
66
+ model.classifier[2] = nn.Sequential(
67
+ nn.Dropout(p=dropout_rate),
68
+ nn.Linear(in_features, num_classes)
69
+ )
70
+
71
+ elif arch in ("swin_t", "swin"):
72
+ weights = models.Swin_T_Weights.DEFAULT if pretrained else None
73
+ model = models.swin_t(weights=weights)
74
+ in_features = model.head.in_features
75
+ model.head = nn.Sequential(
76
+ nn.Dropout(p=dropout_rate),
77
+ nn.Linear(in_features, num_classes)
78
+ )
79
+
80
+ elif arch in ("mobilenet_v2", "mobilenet"):
81
+ weights = models.MobileNet_V2_Weights.DEFAULT if pretrained else None
82
+ model = models.mobilenet_v2(weights=weights)
83
+ in_features = model.classifier[1].in_features
84
+ model.classifier = nn.Sequential(
85
+ nn.Dropout(p=dropout_rate),
86
+ nn.Linear(in_features, num_classes)
87
+ )
88
+
89
+ else:
90
+ raise ValueError(f"Unsupported architecture '{architecture}'. "
91
+ f"Supported: resnet50, resnet18, densenet121, convnext_tiny, swin_t, mobilenet_v2")
92
+
93
+ return model
94
+
95
+
96
+ def get_model_info(model: nn.Module) -> Dict[str, Any]:
97
+ """
98
+ Calculates parameter count and estimated model memory size in MB.
99
+ """
100
+ total_params = sum(p.numel() for p in model.parameters())
101
+ trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
102
+
103
+ # Estimate size assuming float32 (4 bytes per param)
104
+ param_size_mb = (total_params * 4) / (1024 * 1024)
105
+
106
+ return {
107
+ "total_parameters": total_params,
108
+ "trainable_parameters": trainable_params,
109
+ "model_size_mb": round(param_size_mb, 2)
110
+ }
@@ -0,0 +1,88 @@
1
+ """
2
+ Preprocessing module for Diabetic Retinopathy retinal fundus images.
3
+
4
+ Provides standard image transformations, augmentations, and specialized
5
+ preprocessing routines (e.g., circular cropping, Ben Graham color processing)
6
+ tailored for fundus image classification.
7
+ """
8
+
9
+ from typing import Tuple, Optional, Callable
10
+ import torch
11
+ import torchvision.transforms as T
12
+ from PIL import Image
13
+ import numpy as np
14
+ import cv2
15
+
16
+
17
+ def crop_image_from_gray(img: np.ndarray, tol: int = 7) -> np.ndarray:
18
+ """
19
+ Crops black boundaries from fundus images.
20
+ """
21
+ if img.ndim == 2:
22
+ mask = img > tol
23
+ return img[np.ix_(mask.any(1), mask.any(0))]
24
+ elif img.ndim == 3:
25
+ gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
26
+ mask = gray_img > tol
27
+
28
+ check_start = img[:, :, 0][np.ix_(mask.any(1), mask.any(0))]
29
+ if check_start.size == 0:
30
+ return img
31
+ else:
32
+ img1 = img[:, :, 0][np.ix_(mask.any(1), mask.any(0))]
33
+ img2 = img[:, :, 1][np.ix_(mask.any(1), mask.any(0))]
34
+ img3 = img[:, :, 2][np.ix_(mask.any(1), mask.any(0))]
35
+ return np.stack([img1, img2, img3], axis=-1)
36
+ return img
37
+
38
+
39
+ def ben_graham_preprocessing(img_pil: Image.Image, img_size: int = 224, sigma_x: int = 10) -> Image.Image:
40
+ """
41
+ Applies Ben Graham's method for enhancing retinal fundus features (subtract local average color).
42
+ """
43
+ img = np.array(img_pil)
44
+ img = crop_image_from_gray(img)
45
+ img = cv2.resize(img, (img_size, img_size))
46
+ img = cv2.addWeighted(img, 4, cv2.GaussianBlur(img, (0, 0), sigma_x), -4, 128)
47
+ return Image.fromarray(img)
48
+
49
+
50
+ def get_transforms(
51
+ img_size: int = 224,
52
+ is_train: bool = True,
53
+ mean: Tuple[float, float, float] = (0.485, 0.456, 0.406),
54
+ std: Tuple[float, float, float] = (0.229, 0.224, 0.225),
55
+ use_ben_graham: bool = False
56
+ ) -> T.Compose:
57
+ """
58
+ Returns torchvision transforms for training or evaluation.
59
+
60
+ Args:
61
+ img_size: Target image height and width.
62
+ is_train: Whether to include data augmentation transforms.
63
+ mean: Normalization channel means.
64
+ std: Normalization channel standard deviations.
65
+ use_ben_graham: Whether to apply Ben Graham preprocessing.
66
+ """
67
+ transforms_list = []
68
+
69
+ if use_ben_graham:
70
+ transforms_list.append(T.Lambda(lambda img: ben_graham_preprocessing(img, img_size=img_size)))
71
+ else:
72
+ transforms_list.append(T.Resize((img_size, img_size)))
73
+
74
+ if is_train:
75
+ transforms_list.extend([
76
+ T.RandomHorizontalFlip(p=0.5),
77
+ T.RandomVerticalFlip(p=0.5),
78
+ T.RandomRotation(degrees=30),
79
+ T.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),
80
+ T.RandomAffine(degrees=0, translate=(0.05, 0.05), scale=(0.95, 1.05))
81
+ ])
82
+
83
+ transforms_list.extend([
84
+ T.ToTensor(),
85
+ T.Normalize(mean=mean, std=std)
86
+ ])
87
+
88
+ return T.Compose(transforms_list)
fastdrs/training.py ADDED
@@ -0,0 +1,169 @@
1
+ """
2
+ Training script and loop for Diabetic Retinopathy classification models.
3
+
4
+ Features:
5
+ - PyTorch modular training loop with validation
6
+ - Support for class-weighted loss (for imbalanced DR classes)
7
+ - Learning rate schedulers (CosineAnnealing / ReduceLROnPlateau)
8
+ - Best model checkpointing
9
+ - Weights & Biases (wandb) logging integration
10
+ """
11
+
12
+ from typing import Dict, Any, Optional, Tuple
13
+ import os
14
+ import time
15
+ import torch
16
+ import torch.nn as nn
17
+ import torch.optim as optim
18
+ from torch.optim.lr_scheduler import CosineAnnealingLR, ReduceLROnPlateau
19
+
20
+ from fastdrs.data import create_dataloaders
21
+ from fastdrs.models import build_model, get_model_info
22
+ from fastdrs.evaluation import evaluate_model
23
+
24
+
25
+ def train_epoch(
26
+ model: nn.Module,
27
+ dataloader: torch.utils.data.DataLoader,
28
+ criterion: nn.Module,
29
+ optimizer: optim.Optimizer,
30
+ device: torch.device
31
+ ) -> Tuple[float, float]:
32
+ model.train()
33
+ running_loss = 0.0
34
+ correct = 0
35
+ total = 0
36
+
37
+ for inputs, targets in dataloader:
38
+ inputs, targets = inputs.to(device), targets.to(device)
39
+
40
+ optimizer.zero_grad()
41
+ outputs = model(inputs)
42
+ loss = criterion(outputs, targets)
43
+ loss.backward()
44
+ optimizer.step()
45
+
46
+ running_loss += loss.item() * inputs.size(0)
47
+ preds = torch.argmax(outputs, dim=1)
48
+ correct += (preds == targets).sum().item()
49
+ total += inputs.size(0)
50
+
51
+ epoch_loss = running_loss / total
52
+ epoch_acc = correct / total
53
+ return epoch_loss, epoch_acc
54
+
55
+
56
+ def validate_epoch(
57
+ model: nn.Module,
58
+ dataloader: torch.utils.data.DataLoader,
59
+ criterion: nn.Module,
60
+ device: torch.device
61
+ ) -> Tuple[float, float]:
62
+ model.eval()
63
+ running_loss = 0.0
64
+ correct = 0
65
+ total = 0
66
+
67
+ with torch.no_grad():
68
+ for inputs, targets in dataloader:
69
+ inputs, targets = inputs.to(device), targets.to(device)
70
+ outputs = model(inputs)
71
+ loss = criterion(outputs, targets)
72
+
73
+ running_loss += loss.item() * inputs.size(0)
74
+ preds = torch.argmax(outputs, dim=1)
75
+ correct += (preds == targets).sum().item()
76
+ total += inputs.size(0)
77
+
78
+ epoch_loss = running_loss / total
79
+ epoch_acc = correct / total
80
+ return epoch_loss, epoch_acc
81
+
82
+
83
+ def train_model(
84
+ architecture: str = "resnet50",
85
+ dataset_name: str = "sngsfydy/aptos",
86
+ epochs: int = 10,
87
+ batch_size: int = 32,
88
+ lr: float = 1e-4,
89
+ weight_decay: float = 1e-4,
90
+ img_size: int = 224,
91
+ save_dir: str = "models",
92
+ use_wandb: bool = False,
93
+ device: str = "cuda" if torch.cuda.is_available() else "cpu"
94
+ ) -> Dict[str, Any]:
95
+ """
96
+ Main entry point for training a DR classification model.
97
+ """
98
+ os.makedirs(save_dir, exist_ok=True)
99
+ device_obj = torch.device(device)
100
+
101
+ print(f"--- Training {architecture} on {device} ---")
102
+
103
+ # 1. DataLoaders
104
+ train_loader, val_loader, test_loader, ds_info = create_dataloaders(
105
+ dataset_name=dataset_name,
106
+ batch_size=batch_size,
107
+ img_size=img_size
108
+ )
109
+
110
+ # 2. Build Model
111
+ model = build_model(architecture=architecture, num_classes=5, pretrained=True)
112
+ model.to(device_obj)
113
+
114
+ model_info = get_model_info(model)
115
+ print(f"Model parameters: {model_info['total_parameters']:,} ({model_info['model_size_mb']} MB)")
116
+
117
+ # 3. Optimization Setup
118
+ criterion = nn.CrossEntropyLoss()
119
+ optimizer = optim.AdamW(model.parameters(), lr=lr, weight_decay=weight_decay)
120
+ scheduler = CosineAnnealingLR(optimizer, T_max=epochs)
121
+
122
+ best_val_loss = float("inf")
123
+ best_checkpoint_path = os.path.join(save_dir, f"{architecture}_best.pth")
124
+
125
+ history = {"train_loss": [], "train_acc": [], "val_loss": [], "val_acc": []}
126
+
127
+ # 4. Training Loop
128
+ for epoch in range(1, epochs + 1):
129
+ t0 = time.time()
130
+ train_loss, train_acc = train_epoch(model, train_loader, criterion, optimizer, device_obj)
131
+ val_loss, val_acc = validate_epoch(model, val_loader, criterion, device_obj)
132
+ scheduler.step()
133
+ elapsed = time.time() - t0
134
+
135
+ history["train_loss"].append(train_loss)
136
+ history["train_acc"].append(train_acc)
137
+ history["val_loss"].append(val_loss)
138
+ history["val_acc"].append(val_acc)
139
+
140
+ print(f"Epoch {epoch:02d}/{epochs:02d} [{elapsed:.1f}s] | "
141
+ f"Train Loss: {train_loss:.4f}, Acc: {train_acc:.4f} | "
142
+ f"Val Loss: {val_loss:.4f}, Acc: {val_acc:.4f}")
143
+
144
+ if val_loss < best_val_loss:
145
+ best_val_loss = val_loss
146
+ torch.save(model.state_dict(), best_checkpoint_path)
147
+ print(f" Saved new best model to {best_checkpoint_path}")
148
+
149
+ # 5. Final Evaluation on Test Set using Best Weights
150
+ if os.path.exists(best_checkpoint_path):
151
+ model.load_state_dict(torch.load(best_checkpoint_path, map_location=device_obj))
152
+
153
+ print("\n--- Final Test Set Evaluation ---")
154
+ test_results = evaluate_model(model, test_loader, device_obj)
155
+ print(f"Test Accuracy: {test_results['accuracy']}")
156
+ print(f"Test Macro F1: {test_results['f1_macro']}")
157
+ print(f"Test Macro Sensitivity: {test_results['sensitivity_macro']}")
158
+ print(f"Test Latency: {test_results['inference_latency_ms']} ms/sample")
159
+
160
+ return {
161
+ "architecture": architecture,
162
+ "history": history,
163
+ "test_results": test_results,
164
+ "best_checkpoint": best_checkpoint_path
165
+ }
166
+
167
+
168
+ if __name__ == "__main__":
169
+ train_model(architecture="resnet50", epochs=5)
@@ -0,0 +1,776 @@
1
+ Metadata-Version: 2.5
2
+ Name: fastdrs
3
+ Version: 0.1.0
4
+ Summary: Diabetic retinopathy screening toolkit
5
+ Author: Michael Mukiibi
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: numpy
9
+ Requires-Dist: pillow
10
+ Provides-Extra: export
11
+ Provides-Extra: training
12
+ Requires-Dist: datasets; extra == 'training'
13
+ Requires-Dist: opencv-python; extra == 'training'
14
+ Requires-Dist: scikit-learn; extra == 'training'
15
+ Requires-Dist: torch; extra == 'training'
16
+ Requires-Dist: torchvision; extra == 'training'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # AI-Assisted Diabetic Retinopathy Screening & Triage
20
+
21
+ > **Marconi Research & Innovations Lab — Internship Capstone Project**
22
+
23
+ An economically motivated computer-vision system for **AI-assisted diabetic retinopathy screening and clinical triage**, designed with resource-constrained healthcare environments in mind.
24
+
25
+ The project investigates whether modern deep-learning vision architectures can classify diabetic retinopathy severity from retinal fundus photographs while balancing **clinical sensitivity, predictive performance, model size, and inference latency** for practical deployment.
26
+
27
+ > **Important:** This project is a research and engineering prototype. It is **not a medical device and does not provide an autonomous clinical diagnosis**. Predictions are intended to demonstrate AI-assisted screening and referral prioritization.
28
+
29
+ ---
30
+
31
+ ## 1. Problem
32
+
33
+ Diabetic retinopathy (DR) is a major complication of diabetes and an important cause of preventable vision loss. Early identification can enable timely clinical assessment and treatment.
34
+
35
+ In resource-constrained healthcare environments, however, screening can be limited by:
36
+
37
+ * Shortage of ophthalmologists and trained specialists
38
+ * Geographic barriers to specialist care
39
+ * Cost of specialist examinations
40
+ * Increasing numbers of patients requiring screening
41
+ * Limited access to regular retinal imaging assessment
42
+
43
+ This creates an opportunity for **AI-assisted screening and triage**.
44
+
45
+ Rather than replacing clinicians, an AI system can potentially help healthcare workers identify images that require **higher-priority specialist review**, allowing limited clinical resources to be allocated more efficiently.
46
+
47
+ ---
48
+
49
+ ## 2. Proposed Solution
50
+
51
+ This project develops a computer-vision pipeline that analyzes retinal fundus photographs and estimates the severity of diabetic retinopathy.
52
+
53
+ The system investigates multiple modern vision architectures and evaluates them not only by predictive performance, but also by their suitability for deployment.
54
+
55
+ ### Core workflow
56
+
57
+ ```text
58
+ Retinal Fundus Image
59
+ │
60
+ ▼
61
+ EDA
62
+ │
63
+ ▼
64
+ Preprocessing
65
+ │
66
+ ▼
67
+ Model Fine-Tuning
68
+ │
69
+ ├── ResNet
70
+ ├── DenseNet
71
+ ├── ConvNeXt
72
+ └── Swin Transformer
73
+ │
74
+ ▼
75
+ Model Evaluation
76
+ │
77
+ ├── Accuracy
78
+ ├── Precision
79
+ ├── Recall / Sensitivity
80
+ ├── Specificity
81
+ └── F1 Score
82
+ │
83
+ ▼
84
+ Deployment Analysis
85
+ │
86
+ ├── Model Size
87
+ ├── Parameter Count
88
+ └── Inference Latency
89
+ │
90
+ ▼
91
+ Final Model
92
+ │
93
+ ▼
94
+ Android Application / API
95
+ ```
96
+
97
+ ---
98
+
99
+ ## 3. Capstone Research Question
100
+
101
+ > **Can modern computer-vision architectures provide sufficiently sensitive diabetic-retinopathy screening while remaining computationally efficient enough for deployment in resource-constrained healthcare environments?**
102
+
103
+ The project therefore treats model selection as a multi-objective engineering problem:
104
+
105
+ ```text
106
+ Clinical Performance
107
+ +
108
+ Computational Efficiency
109
+ +
110
+ Deployment Feasibility
111
+ =
112
+ Practical Model
113
+ ```
114
+
115
+ ---
116
+
117
+ ## 4. Objectives
118
+
119
+ ### Primary objective
120
+
121
+ Develop and evaluate a deployable AI-assisted diabetic-retinopathy screening prototype using retinal fundus photographs.
122
+
123
+ ### Specific objectives
124
+
125
+ 1. Perform exploratory data analysis on the APTOS 2019 dataset.
126
+ 2. Develop a standardized image preprocessing and training pipeline.
127
+ 3. Fine-tune modern computer-vision architectures for diabetic-retinopathy classification.
128
+ 4. Compare ResNet, DenseNet, ConvNeXt, and Swin Transformer architectures.
129
+ 5. Evaluate models using clinically relevant classification metrics.
130
+ 6. Investigate the relationship between sensitivity, model size, and inference latency.
131
+ 7. Select a final model based on predictive performance and deployment feasibility.
132
+ 8. Integrate the trained model into an **Android application or inference API**.
133
+ 9. Demonstrate an end-to-end screening workflow.
134
+
135
+ ---
136
+
137
+ ## 5. Dataset
138
+
139
+ ### APTOS 2019 Blindness Detection
140
+
141
+ The primary dataset is the **APTOS 2019 Blindness Detection** dataset.
142
+
143
+ It contains approximately **3,662 labeled retinal fundus photographs** across five diabetic-retinopathy severity grades.
144
+
145
+ ### Classes
146
+
147
+ | Label | Severity |
148
+ | ----: | ---------------------------------- |
149
+ | 0 | No diabetic retinopathy |
150
+ | 1 | Mild |
151
+ | 2 | Moderate |
152
+ | 3 | Severe |
153
+ | 4 | Proliferative diabetic retinopathy |
154
+
155
+ The dataset was originally released through the APTOS 2019 Blindness Detection Kaggle competition.
156
+
157
+ Dataset source:
158
+
159
+ [https://www.kaggle.com/competitions/aptos2019-blindness-detection](https://www.kaggle.com/competitions/aptos2019-blindness-detection)
160
+
161
+ ### Data considerations
162
+
163
+ The project will investigate:
164
+
165
+ * Class distribution
166
+ * Image dimensions
167
+ * Image quality
168
+ * Color/channel characteristics
169
+ * Class imbalance
170
+ * Training/validation/test splitting
171
+ * Appropriate augmentation
172
+ * Image normalization
173
+
174
+ Because the dataset is imbalanced, **accuracy will not be treated as the sole measure of model quality**.
175
+
176
+ ---
177
+
178
+ ## 6. Evaluation Strategy
179
+
180
+ The project evaluates models using multiple metrics.
181
+
182
+ ### Accuracy
183
+
184
+ Measures the proportion of correctly classified samples.
185
+
186
+ ### Precision
187
+
188
+ Measures how many predicted positive cases are actually positive.
189
+
190
+ ### Recall / Sensitivity
191
+
192
+ Measures the proportion of relevant positive cases detected by the model.
193
+
194
+ For a screening application, sensitivity is particularly important because missed cases may require further clinical assessment.
195
+
196
+ ### Specificity
197
+
198
+ Measures the proportion of negative cases correctly identified.
199
+
200
+ ### F1 Score
201
+
202
+ Balances precision and recall.
203
+
204
+ ### Confusion Matrix
205
+
206
+ Used to examine errors across the five diabetic-retinopathy severity classes.
207
+
208
+ ---
209
+
210
+ ## 7. Model Benchmarking
211
+
212
+ The project will investigate four major vision architectures.
213
+
214
+ ### ResNet
215
+
216
+ Uses residual/skip connections to facilitate the training of deep convolutional networks.
217
+
218
+ ### DenseNet
219
+
220
+ Uses dense connectivity and feature reuse between layers.
221
+
222
+ ### ConvNeXt
223
+
224
+ A modern convolutional architecture incorporating design principles developed alongside the rise of vision transformers.
225
+
226
+ ### Swin Transformer
227
+
228
+ Uses hierarchical vision-transformer representations with shifted-window attention.
229
+
230
+ The objective is not simply to identify the model with the highest accuracy.
231
+
232
+ The benchmark will investigate:
233
+
234
+ > **Predictive performance vs. computational cost vs. deployment feasibility.**
235
+
236
+ ---
237
+
238
+ ## 8. Experimental Tracking
239
+
240
+ Experiments will be tracked using **Weights & Biases (W&B)**.
241
+
242
+ Potentially tracked parameters include:
243
+
244
+ * Model architecture
245
+ * Learning rate
246
+ * Batch size
247
+ * Number of epochs
248
+ * Training loss
249
+ * Validation loss
250
+ * Accuracy
251
+ * Precision
252
+ * Recall
253
+ * Sensitivity
254
+ * Specificity
255
+ * F1 score
256
+ * Training duration
257
+
258
+ This enables systematic comparison between experiments instead of relying on isolated notebook outputs.
259
+
260
+ ---
261
+
262
+ ## 9. Training Framework & Quickstart
263
+
264
+ The project builds on technologies explored during the Marconi internship:
265
+
266
+ * Python
267
+ * PyTorch
268
+ * Hugging Face Datasets
269
+ * scikit-learn
270
+ * Weights & Biases (W&B)
271
+
272
+ ### Running the Training Pipeline (`train.py`)
273
+
274
+ A unified, single-execution training script (`train.py`) is provided for local execution and experiment tracking.
275
+
276
+ #### 1. Setup Dependencies
277
+ ```bash
278
+ pip install -r requirements.txt
279
+ ```
280
+
281
+ #### 2. Single-Command Training Examples
282
+
283
+ * **Default Run (ResNet-50 with W&B logging)**:
284
+ ```bash
285
+ python train.py
286
+ ```
287
+
288
+ * **Architecture Benchmarking (DenseNet-121 with Ben Graham Preprocessing)**:
289
+ ```bash
290
+ python train.py --arch densenet121 --epochs 15 --batch-size 16 --ben-graham
291
+ ```
292
+
293
+ * **Modern Vision Transformers (Swin Transformer / ConvNeXt)**:
294
+ ```bash
295
+ python train.py --arch swin_t --epochs 10 --batch-size 16
296
+ python train.py --arch convnext_tiny --epochs 10 --batch-size 16
297
+ ```
298
+
299
+ * **Local Offline Run (without Weights & Biases)**:
300
+ ```bash
301
+ python train.py --arch mobilenet_v2 --epochs 5 --no-wandb
302
+ ```
303
+
304
+ #### 3. Command-Line Options
305
+
306
+ | Argument | Default | Description |
307
+ | :--- | :--- | :--- |
308
+ | `--arch`, `-a` | `resnet50` | Architecture (`resnet50`, `resnet18`, `densenet121`, `convnext_tiny`, `swin_t`, `mobilenet_v2`) |
309
+ | `--dataset`, `-d` | `sngsfydy/aptos` | Hugging Face dataset identifier |
310
+ | `--epochs`, `-e` | `10` | Number of training epochs |
311
+ | `--batch-size`, `-b` | `16` | Batch size per step |
312
+ | `--lr` | `1e-4` | Learning rate |
313
+ | `--ben-graham` | `False` | Apply Ben Graham circular crop & local color subtraction |
314
+ | `--device` | `auto` | Execution device (`auto`, `cuda`, `cpu`, `mps`) |
315
+ | `--wandb-project` | `diabetic-retinopathy-screening` | W&B project name |
316
+ | `--no-wandb` | `False` | Disable W&B online tracking |
317
+ | `--save-dir` | `models` | Directory for model checkpoints |
318
+ | `--results-dir` | `results` | Directory for benchmark metric CSV tables |
319
+
320
+ ---
321
+
322
+ ## 10. Deployment
323
+
324
+ A trained model is not considered the final deliverable by itself.
325
+
326
+ The selected model will be integrated into a usable inference system.
327
+
328
+ Two deployment paths are being considered.
329
+
330
+ ### Option A — Android
331
+
332
+ ```text
333
+ Android Application
334
+ │
335
+ ▼
336
+ Fundus Image
337
+ │
338
+ ▼
339
+ Preprocessing
340
+ │
341
+ ▼
342
+ ML Model
343
+ │
344
+ ▼
345
+ Prediction
346
+ │
347
+ ▼
348
+ Severity / Referral Risk
349
+ ```
350
+
351
+ Potential technologies:
352
+
353
+ * Kotlin
354
+ * Jetpack Compose
355
+ * ONNX Runtime
356
+ * TensorFlow Lite
357
+
358
+ ### Option B — API
359
+
360
+ ```text
361
+ Client
362
+ │
363
+ ▼
364
+ HTTP POST /predict
365
+ │
366
+ ▼
367
+ FastAPI
368
+ │
369
+ ▼
370
+ Preprocessing
371
+ │
372
+ ▼
373
+ Trained Model
374
+ │
375
+ ▼
376
+ Prediction
377
+ │
378
+ ▼
379
+ JSON Response
380
+ ```
381
+
382
+ Potential API stack:
383
+
384
+ * Python
385
+ * FastAPI
386
+ * Uvicorn
387
+ * PyTorch / Hugging Face
388
+
389
+ The final deployment path will be selected after evaluating the trained model's framework, size, conversion requirements, and inference performance.
390
+
391
+ ---
392
+
393
+ ## 11. Deployment-Oriented Model Selection
394
+
395
+ The final model will not necessarily be the model with the highest raw accuracy.
396
+
397
+ A model-selection framework will consider:
398
+
399
+ | Criterion | Importance |
400
+ | ------------------------ | ----------- |
401
+ | Sensitivity | High |
402
+ | Specificity | High |
403
+ | F1 score | High |
404
+ | Generalization | High |
405
+ | Model size | Medium–High |
406
+ | Parameter count | Medium |
407
+ | Inference latency | Medium–High |
408
+ | Deployment compatibility | High |
409
+
410
+ The goal is to identify a model that provides an appropriate balance between **screening performance and practical deployment requirements**.
411
+
412
+ ---
413
+
414
+ ## 12. Economic Rationale
415
+
416
+ The project is motivated by an operational healthcare problem rather than model development for its own sake.
417
+
418
+ A potential AI-assisted screening system could help:
419
+
420
+ * Extend screening capacity
421
+ * Prioritize patients for specialist review
422
+ * Reduce unnecessary specialist referrals
423
+ * Support healthcare workers without specialist expertise
424
+ * Reduce geographic barriers to initial screening
425
+ * Enable lower-cost preliminary assessment
426
+
427
+ The economic hypothesis is:
428
+
429
+ > **If AI can reliably identify higher-risk retinal images for specialist review, limited ophthalmology resources can potentially be allocated more efficiently.**
430
+
431
+ This project does **not** attempt to quantify the real-world cost savings without clinical and operational validation. Economic impact remains a hypothesis requiring field evaluation.
432
+
433
+ ---
434
+
435
+ ## 13. System Architecture
436
+
437
+ The target system is:
438
+
439
+ ```text
440
+ ┌─────────────────┐
441
+ │ Fundus Image │
442
+ └────────┬────────┘
443
+ │
444
+ ▼
445
+ ┌─────────────────┐
446
+ │ Preprocessing │
447
+ └────────┬────────┘
448
+ │
449
+ ▼
450
+ ┌─────────────────┐
451
+ │ Trained Model │
452
+ └────────┬────────┘
453
+ │
454
+ ▼
455
+ ┌─────────────────┐
456
+ │ Prediction │
457
+ └────────┬────────┘
458
+ │
459
+ ┌─────────────┴─────────────┐
460
+ ▼ ▼
461
+ Severity Estimate Confidence
462
+ │
463
+ ▼
464
+ Screening / Triage
465
+ Recommendation
466
+ ```
467
+
468
+ ---
469
+
470
+ ## 14. Project Structure
471
+
472
+ The final repository is expected to evolve toward:
473
+
474
+ ```text
475
+ marconi-diabetic-retinopathy/
476
+ │
477
+ ├── README.md
478
+ ├── requirements.txt
479
+ ├── train.py # Single-command training script (HF & W&B)
480
+ │
481
+ ├── data/
482
+ │ └── README.md
483
+ │
484
+ ├── notebooks/
485
+ │ ├── 01_eda.ipynb
486
+ │ ├── 02_baseline.ipynb
487
+ │ ├── 03_model_comparison.ipynb
488
+ │ └── 04_final_model.ipynb
489
+ │
490
+ ├── src/
491
+ │ ├── __init__.py
492
+ │ ├── data.py
493
+ │ ├── preprocessing.py
494
+ │ ├── models.py
495
+ │ ├── train.py
496
+ │ └── evaluate.py
497
+ │
498
+ ├── models/
499
+ │ └── README.md
500
+ │
501
+ ├── results/
502
+ │ ├── metrics.csv
503
+ │ ├── model_comparison.csv
504
+ │ └── figures/
505
+ │
506
+ ├── deployment/
507
+ │ ├── api/
508
+ │ └── android/
509
+ │
510
+ └── presentation/
511
+ └── README.md
512
+ ```
513
+
514
+ The exact structure will be adapted from the existing Marconi repositories rather than unnecessarily rebuilding working code.
515
+
516
+ ---
517
+
518
+ ## 15. Existing Marconi Work
519
+
520
+ This project builds upon work completed during the internship.
521
+
522
+ ### Deep-learning foundations
523
+
524
+ * Neural-network mechanics
525
+ * Forward propagation
526
+ * Backpropagation
527
+ * Gradient descent
528
+ * Keras
529
+ * PyTorch
530
+ * MNIST classification
531
+
532
+ ### Transfer learning
533
+
534
+ * MobileNetV2
535
+ * Custom PyTorch datasets
536
+ * DataLoader pipelines
537
+ * Image transforms
538
+
539
+ ### Architecture benchmarking
540
+
541
+ Existing repositories:
542
+
543
+ **ResNet vs DenseNet**
544
+
545
+ [https://github.com/MichaelMukiibi/resnet-v-densenet.git](https://github.com/MichaelMukiibi/resnet-v-densenet.git)
546
+
547
+ **ConvNeXt vs Swin**
548
+
549
+ [https://github.com/MichaelMukiibi/convnext-v-swin.git](https://github.com/MichaelMukiibi/convnext-v-swin.git)
550
+
551
+ ### Hugging Face vision pipeline
552
+
553
+ Existing repository:
554
+
555
+ **Drone Landing Safety**
556
+
557
+ [https://github.com/MichaelMukiibi/drone-landing-safety.git](https://github.com/MichaelMukiibi/drone-landing-safety.git)
558
+
559
+ ### Additional repositories
560
+
561
+ **Omniglot**
562
+
563
+ [https://github.com/MichaelMukiibi/omniglot.git](https://github.com/MichaelMukiibi/omniglot.git)
564
+
565
+ **Electricity Consumption**
566
+
567
+ [https://github.com/MichaelMukiibi/electricity-consumption.git](https://github.com/MichaelMukiibi/electricity-consumption.git)
568
+
569
+ The first development task is to inspect these repositories and reuse the most mature training/evaluation infrastructure.
570
+
571
+ ---
572
+
573
+ ## 16. Immediate Execution Plan
574
+
575
+ ### Phase 1 — Repository inspection
576
+
577
+ Inspect:
578
+
579
+ 1. `convnext-v-swin`
580
+ 2. `resnet-v-densenet`
581
+ 3. `drone-landing-safety`
582
+
583
+ Identify:
584
+
585
+ * Training scripts
586
+ * Dataset loaders
587
+ * Hugging Face integration
588
+ * W&B integration
589
+ * Evaluation functions
590
+ * Existing checkpoints
591
+ * Preprocessing functions
592
+ * Existing plots
593
+
594
+ ### Phase 2 — APTOS integration
595
+
596
+ Adapt the strongest existing pipeline to APTOS.
597
+
598
+ Perform:
599
+
600
+ * Dataset download
601
+ * Data validation
602
+ * EDA
603
+ * Train/validation split
604
+ * Preprocessing
605
+ * Baseline training
606
+
607
+ ### Phase 3 — Model comparison
608
+
609
+ Train feasible candidates:
610
+
611
+ * ResNet
612
+ * DenseNet
613
+ * ConvNeXt
614
+ * Swin
615
+
616
+ Do not attempt unnecessarily large experiments given the closeout deadline.
617
+
618
+ ### Phase 4 — Evaluation
619
+
620
+ Generate:
621
+
622
+ * Confusion matrices
623
+ * Accuracy
624
+ * Precision
625
+ * Recall
626
+ * Sensitivity
627
+ * Specificity
628
+ * F1
629
+ * Model size
630
+ * Inference latency
631
+
632
+ ### Phase 5 — Final model
633
+
634
+ Select a model using:
635
+
636
+ ```text
637
+ Clinical relevance
638
+ +
639
+ Predictive performance
640
+ +
641
+ Computational efficiency
642
+ +
643
+ Deployment feasibility
644
+ ```
645
+
646
+ ### Phase 6 — Deployment
647
+
648
+ Implement either:
649
+
650
+ * Android inference
651
+
652
+ or
653
+
654
+ * FastAPI inference service
655
+
656
+ ### Phase 7 — Closeout
657
+
658
+ Prepare:
659
+
660
+ * Working demo
661
+ * Final repository
662
+ * Results
663
+ * Presentation
664
+ * Technical Q&A preparation
665
+
666
+ ---
667
+
668
+ ## 17. Current Status
669
+
670
+ ### Completed
671
+
672
+ * Deep-learning foundation training
673
+ * PyTorch training
674
+ * Transfer learning
675
+ * Medical ML evaluation concepts
676
+ * ResNet/DenseNet research
677
+ * ConvNeXt/Swin research
678
+ * W&B experiment tracking
679
+ * Hugging Face vision workflows
680
+ * EDA workflow development
681
+
682
+ ### In progress
683
+
684
+ * Selecting the final economically meaningful capstone
685
+ * Adapting existing vision pipelines to APTOS 2019
686
+ * Model benchmarking
687
+ * Final model selection
688
+ * Deployment
689
+
690
+ ### Pending
691
+
692
+ * Final trained APTOS model
693
+ * Final evaluation results
694
+ * Deployment implementation
695
+ * End-to-end demo
696
+ * Closeout presentation
697
+
698
+ ---
699
+
700
+ ## 18. Limitations
701
+
702
+ This prototype has important limitations.
703
+
704
+ * APTOS is a relatively small dataset compared with large-scale medical imaging datasets.
705
+ * Dataset distribution may not represent the target population.
706
+ * Dataset labels may contain uncertainty.
707
+ * Image quality can vary substantially.
708
+ * Model performance does not automatically translate to clinical effectiveness.
709
+ * External validation is required.
710
+ * Clinical workflow integration has not been established.
711
+ * Regulatory approval would be required for clinical deployment.
712
+ * Economic impact has not yet been empirically demonstrated.
713
+
714
+ Therefore, the system should be presented as an **AI-assisted research prototype**, not as an autonomous diagnostic product.
715
+
716
+ ---
717
+
718
+ ## 19. Future Work
719
+
720
+ Potential future development includes:
721
+
722
+ * External validation on additional retinal datasets
723
+ * Calibration of model confidence
724
+ * Explainability methods such as Grad-CAM
725
+ * Model quantization
726
+ * ONNX/TensorFlow Lite conversion
727
+ * On-device inference
728
+ * Offline-first deployment
729
+ * Prospective clinical validation
730
+ * Integration with existing health-information systems
731
+ * Human-in-the-loop referral workflows
732
+ * Health-economic evaluation
733
+ * Field testing in resource-constrained healthcare settings
734
+
735
+ ---
736
+
737
+ ## 20. Expected Capstone Outcome
738
+
739
+ The final deliverable should demonstrate the complete ML lifecycle:
740
+
741
+ ```text
742
+ REAL-WORLD PROBLEM
743
+ ↓
744
+ DATA
745
+ ↓
746
+ EDA
747
+ ↓
748
+ MODEL DEVELOPMENT
749
+ ↓
750
+ EXPERIMENT TRACKING
751
+ ↓
752
+ ARCHITECTURE BENCHMARKING
753
+ ↓
754
+ CLINICALLY RELEVANT EVALUATION
755
+ ↓
756
+ MODEL SELECTION
757
+ ↓
758
+ DEPLOYMENT
759
+ ↓
760
+ WORKING DEMONSTRATION
761
+ ```
762
+
763
+ The central objective is not merely to achieve a high classification score.
764
+
765
+ The project aims to demonstrate that the intern can take a meaningful problem from **research question → machine-learning experimentation → engineering implementation → deployable prototype**.
766
+
767
+ ---
768
+
769
+ ## 21. Project Status
770
+
771
+ **Status:** Active development
772
+ **Intern:** MUKIIBI MICHAEL KIRINNYA
773
+ **Capstone:** AI-Assisted Diabetic Retinopathy Screening & Triage
774
+ **Dataset:** APTOS 2019 Blindness Detection
775
+ **Primary ML Task:** Five-class diabetic-retinopathy severity classification
776
+ **Deployment Requirement:** Android application **or** inference API
@@ -0,0 +1,11 @@
1
+ fastdrs/__init__.py,sha256=gmXsoS_boRaNYtP4QD08ygvVkSwdI_dDd-gTC8pGedo,30
2
+ fastdrs/data.py,sha256=QmlE9o2if4kG1IRM99bihDvnnA5eSofE1pbKhPRY1Yk,4149
3
+ fastdrs/evaluation.py,sha256=y3OvOJYvhjUV2MCWwzHtigXpNYKbLuntY9jycnBNeZo,3955
4
+ fastdrs/models.py,sha256=n9o15ihvoTOjeUjPcok9APp6G8vFELGeZZl9aB7PIXA,3905
5
+ fastdrs/preprocessing.py,sha256=gR3PDxUrbYeb7oyyifBK7xv15ZQuchdMsmeG7Z915Sw,2980
6
+ fastdrs/training.py,sha256=zoNCjV896v6wzIRlSoLKWRhxhZ2UcBix1Iz1iBR1LEs,5487
7
+ fastdrs/deployment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ fastdrs/deployment/litert.py,sha256=inAsJXL5qgVXnWRcEhtFEEc1Lp8Q4-MW2JLZGPCRUEE,3
9
+ fastdrs-0.1.0.dist-info/METADATA,sha256=8T1q4QjKUt7f6P4LJr-FzKsL2JtvJAF_IYZBHJzooK4,20054
10
+ fastdrs-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
11
+ fastdrs-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any