Diabetic Retinopathy Grading β€” EfficientNetV2-L (v26)

5-fold OOF ensemble of EfficientNet-V2-L trained at 384px on the standard 1st-place Kaggle APTOS-2019 recipe (SmoothL1 regression, GeM pool, EMA 0.9999, 3-phase progressive unfreeze, threshold optimisation). Stage-2 fine-tuned on IDRiD for clinical generalisation.

Metrics

Split QWK
OOF (APTOS, full coverage) 0.8201
Messidor-2 (external) 0.3415

See pipeline_metadata.json for full per-class P/R/F1, calibration, and ensemble config.

Datasets used (training)

  • APTOS 2019 (5-fold OOF target)
  • EyePACS / DR-2015 (auxiliary train)
  • DDR (auxiliary train)
  • IDRiD (Stage-2 fine-tune)
  • Messidor-2 (held out for external validation)

Files

  • models/effv2l_s42_best.pt β€” final regression checkpoint
  • fundus_validator.pt β€” MobileNetV3-Small binary classifier (used to gate non-fundus inputs)
  • thresholds.json β€” 4 cut-points for converting score β†’ grade
  • calibration.json β€” Brier-optimal Οƒ for soft probabilities
  • pipeline_metadata.json β€” full training config + metrics

Quick inference

import torch, json, cv2, numpy as np
import torch.nn as nn, torch.nn.functional as F
from torch.nn.parameter import Parameter
import timm

class GeM(nn.Module):
    def __init__(self, p=3.0, eps=1e-6):
        super().__init__()
        self.p = Parameter(torch.ones(1)*p); self.eps = eps
    def forward(self, x):
        return F.avg_pool2d(x.clamp(min=self.eps).pow(self.p),
                            (x.size(-2), x.size(-1))).pow(1.0/self.p)

class DRModel(nn.Module):
    def __init__(self, backbone, drop=0.5):
        super().__init__()
        self.backbone = timm.create_model(backbone, pretrained=False, num_classes=0, global_pool='')
        feat = self.backbone.num_features
        self.pool = GeM()
        self.head = nn.Sequential(
            nn.Flatten(), nn.Linear(feat, 256), nn.BatchNorm1d(256),
            nn.ReLU(True), nn.Dropout(drop), nn.Linear(256, 1))
    def forward(self, x):
        f = self.backbone(x)
        p = f.unsqueeze(-1).unsqueeze(-1) if f.dim()==2 else self.pool(f)
        return self.head(p).squeeze(-1)

ckpt = torch.load('models/effv2l_s42_best.pt', map_location='cpu', weights_only=False)
m = DRModel(ckpt['backbone']); m.load_state_dict(ckpt['model'], strict=False); m.eval()
# … see README in the bundle zip for the full preprocess + grade-conversion code.
Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Spaces using its-karthick1/dr-grading-effv2l 3