theDocWho's picture
Initial commit: end-to-end car damage + repair-cost predictor
4ce7a7e
Raw
History Blame Contribute Delete
1.86 kB
"""ResNet50-based make/model/year classifier for Stanford Cars 196.
Two-stage fine-tune pattern:
- Stage 1: freeze backbone, train only the classification head (fast warm-up).
- Stage 2: unfreeze ``layer3`` and ``layer4`` for full fine-tune at a lower LR.
Loading respects whatever stage the checkpoint was saved at — the trainer
restores optimizer state separately.
"""
from __future__ import annotations
from typing import Optional
import torch
import torch.nn as nn
from torchvision import models
def build_resnet50_identifier(
num_classes: int = 196,
pretrained: bool = True,
) -> nn.Module:
weights = models.ResNet50_Weights.IMAGENET1K_V2 if pretrained else None
backbone = models.resnet50(weights=weights)
in_features = backbone.fc.in_features
backbone.fc = nn.Sequential(
nn.Dropout(p=0.2),
nn.Linear(in_features, 512),
nn.ReLU(inplace=True),
nn.Dropout(p=0.3),
nn.Linear(512, num_classes),
)
return backbone
def set_finetune_stage(model: nn.Module, stage: int) -> None:
"""Stage 1 freezes backbone; stage 2 unfreezes ``layer3``/``layer4`` + head."""
if stage not in (1, 2):
raise ValueError(f"stage must be 1 or 2, got {stage}")
for p in model.parameters():
p.requires_grad = False
if stage == 1:
for p in model.fc.parameters():
p.requires_grad = True
return
# stage == 2 — full fine-tune of upper backbone + head
for name, p in model.named_parameters():
if name.startswith("fc.") or name.startswith("layer3.") or name.startswith("layer4."):
p.requires_grad = True
def trainable_parameters(model: nn.Module):
return [p for p in model.parameters() if p.requires_grad]
def n_trainable(model: nn.Module) -> int:
return sum(p.numel() for p in trainable_parameters(model))