Spaces:
Running on Zero
Running on Zero
| import torch | |
| import torch.nn as nn | |
| from torchvision import models | |
| from torchvision.models import EfficientNet_B0_Weights | |
| from typing import Tuple | |
| class Backbone(nn.Module): | |
| """ | |
| Frozen EfficientNet-B0 feature extractor. | |
| Takes an image tensor [B, 3, 224, 224] and returns a | |
| flattened feature vector [B, 1280] ready for task heads. | |
| We freeze all backbone weights β we're not retraining | |
| EfficientNet, just using its learned visual features. | |
| """ | |
| def __init__(self, pretrained: bool = True): | |
| super().__init__() | |
| # load EfficientNet-B0 with ImageNet weights | |
| weights = EfficientNet_B0_Weights.DEFAULT if pretrained else None | |
| efficientnet = models.efficientnet_b0(weights=weights) | |
| # remove the final classifier head β we only want features | |
| # EfficientNet structure: features -> avgpool -> classifier | |
| # we keep features + avgpool, discard classifier | |
| self.features = efficientnet.features | |
| self.avgpool = efficientnet.avgpool | |
| # EfficientNet-B0 outputs 1280 feature channels after avgpool | |
| self.output_dim = 1280 | |
| # freeze all backbone weights | |
| self._freeze() | |
| def _freeze(self): | |
| """Freeze all backbone parameters β no gradient updates.""" | |
| for param in self.features.parameters(): | |
| param.requires_grad = False | |
| for param in self.avgpool.parameters(): | |
| param.requires_grad = False | |
| def unfreeze(self): | |
| """ | |
| Optionally unfreeze for fine-tuning in later training stages. | |
| Not used in Phase 2 but useful for Phase 3 experiments. | |
| """ | |
| for param in self.features.parameters(): | |
| param.requires_grad = True | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| """ | |
| Args: | |
| x: image tensor [B, 3, 224, 224] | |
| Returns: | |
| features: flattened tensor [B, 1280] | |
| """ | |
| x = self.features(x) # [B, 1280, 7, 7] | |
| x = self.avgpool(x) # [B, 1280, 1, 1] | |
| x = torch.flatten(x, 1) # [B, 1280] | |
| return x | |
| def count_parameters(self) -> Tuple[int, int]: | |
| """Returns (trainable_params, total_params).""" | |
| total = sum(p.numel() for p in self.parameters()) | |
| trainable = sum(p.numel() for p in self.parameters() if p.requires_grad) | |
| return trainable, total |