Spaces:
Sleeping
Sleeping
Commit ·
d6cbcc6
1
Parent(s): 8cd65c6
✨ Upgrade backend to support ConvNeXt automatic detection and ResNet18 fallback
Browse files- toothmap_backend/models.py +51 -1
toothmap_backend/models.py
CHANGED
|
@@ -4,6 +4,8 @@ from torchvision.models.detection import fasterrcnn_resnet50_fpn_v2
|
|
| 4 |
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
|
| 5 |
from torchvision.models import resnet18
|
| 6 |
import segmentation_models_pytorch as smp
|
|
|
|
|
|
|
| 7 |
import os
|
| 8 |
|
| 9 |
DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
|
@@ -33,6 +35,43 @@ def _strip_dp(state: dict) -> dict:
|
|
| 33 |
return {k.replace("module.", "", 1): v for k, v in state.items()}
|
| 34 |
return state
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
def _load(path: str, model):
|
| 37 |
if not os.path.exists(path):
|
| 38 |
print(f"⚠️ Checkpoint not found: {path}")
|
|
@@ -49,6 +88,17 @@ def _load(path: str, model):
|
|
| 49 |
def load_models(ckpt_dir: str):
|
| 50 |
print(f"📦 Loading models from: {ckpt_dir}")
|
| 51 |
MODELS["frcnn"] = _load(os.path.join(ckpt_dir, "frcnn_best.pt"), build_frcnn()).to(DEVICE).eval()
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
MODELS["unet"] = _load(os.path.join(ckpt_dir, "unet_resnet34_best.pt"), build_unet()).to(DEVICE).eval()
|
| 54 |
print("🚀 All PyTorch models ready.")
|
|
|
|
| 4 |
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor
|
| 5 |
from torchvision.models import resnet18
|
| 6 |
import segmentation_models_pytorch as smp
|
| 7 |
+
import torchvision
|
| 8 |
+
from pathlib import Path
|
| 9 |
import os
|
| 10 |
|
| 11 |
DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
|
|
|
| 35 |
return {k.replace("module.", "", 1): v for k, v in state.items()}
|
| 36 |
return state
|
| 37 |
|
| 38 |
+
def load_convnext_model(ckpt_path: Path, device: torch.device):
|
| 39 |
+
"""Loads ConvNeXt-Tiny customized for 32 FDI classes (>90% Acc Setup)."""
|
| 40 |
+
m = torchvision.models.convnext_tiny(weights=None)
|
| 41 |
+
m.classifier[2] = nn.Linear(m.classifier[2].in_features, 32)
|
| 42 |
+
|
| 43 |
+
if not os.path.exists(ckpt_path):
|
| 44 |
+
print(f"⚠️ ConvNeXt Checkpoint not found: {ckpt_path}. Model will return random garbage until trained.")
|
| 45 |
+
m.to(device)
|
| 46 |
+
m.eval()
|
| 47 |
+
return m
|
| 48 |
+
|
| 49 |
+
state = torch.load(ckpt_path, map_location=device, weights_only=True)
|
| 50 |
+
# Strip any "module." prefix if trained with DataParallel
|
| 51 |
+
new_state = {}
|
| 52 |
+
for k, v in state.items():
|
| 53 |
+
name = k[7:] if k.startswith("module.") else k
|
| 54 |
+
new_state[name] = v
|
| 55 |
+
|
| 56 |
+
m.load_state_dict(new_state)
|
| 57 |
+
m.to(device)
|
| 58 |
+
m.eval()
|
| 59 |
+
return m
|
| 60 |
+
|
| 61 |
+
def load_resnet_model(ckpt_path: Path, device: torch.device):
|
| 62 |
+
"""Loads ResNet18 customized for 32 FDI classes."""
|
| 63 |
+
m = torchvision.models.resnet18(weights=None)
|
| 64 |
+
m.fc = nn.Sequential(nn.Dropout(p=0.3), nn.Linear(m.fc.in_features, 32))
|
| 65 |
+
state = torch.load(ckpt_path, map_location=device, weights_only=True)
|
| 66 |
+
new_state = {}
|
| 67 |
+
for k, v in state.items():
|
| 68 |
+
name = k[7:] if k.startswith("module.") else k
|
| 69 |
+
new_state[name] = v
|
| 70 |
+
m.load_state_dict(new_state)
|
| 71 |
+
m.to(device)
|
| 72 |
+
m.eval()
|
| 73 |
+
return m
|
| 74 |
+
|
| 75 |
def _load(path: str, model):
|
| 76 |
if not os.path.exists(path):
|
| 77 |
print(f"⚠️ Checkpoint not found: {path}")
|
|
|
|
| 88 |
def load_models(ckpt_dir: str):
|
| 89 |
print(f"📦 Loading models from: {ckpt_dir}")
|
| 90 |
MODELS["frcnn"] = _load(os.path.join(ckpt_dir, "frcnn_best.pt"), build_frcnn()).to(DEVICE).eval()
|
| 91 |
+
|
| 92 |
+
convnext_path = Path(os.path.join(ckpt_dir, "convnext_best.pt"))
|
| 93 |
+
resnet_path = Path(os.path.join(ckpt_dir, "resnet18_cls_best.pt"))
|
| 94 |
+
if convnext_path.exists():
|
| 95 |
+
MODELS["cls"] = load_convnext_model(convnext_path, DEVICE)
|
| 96 |
+
print("✅ ConvNeXt loaded.")
|
| 97 |
+
elif resnet_path.exists():
|
| 98 |
+
MODELS["cls"] = load_resnet_model(resnet_path, DEVICE)
|
| 99 |
+
print("✅ ResNet18 loaded.")
|
| 100 |
+
else:
|
| 101 |
+
print("⚠️ No classification checkpoint found!")
|
| 102 |
+
|
| 103 |
MODELS["unet"] = _load(os.path.join(ckpt_dir, "unet_resnet34_best.pt"), build_unet()).to(DEVICE).eval()
|
| 104 |
print("🚀 All PyTorch models ready.")
|