Spaces:
Running on Zero
Running on Zero
| import timm | |
| import torch | |
| import torch.nn as nn | |
| EFFICIENTNET_DIM = 1280 | |
| class TemporalAttention(nn.Module): | |
| """ | |
| Input : (B, N_FRAMES, EFFICIENTNET_DIM) | |
| Output : (pooled (B, EFFICIENTNET_DIM), attn_weights (B, N_FRAMES)) | |
| """ | |
| def __init__(self, input_dim: int = EFFICIENTNET_DIM): | |
| super().__init__() | |
| self.attention = nn.Sequential( | |
| nn.Linear(input_dim, 128), | |
| nn.Tanh(), | |
| nn.Linear(128, 1), | |
| ) | |
| def forward(self, x: torch.Tensor): | |
| scores = self.attention(x) # (B, 16, 1) | |
| weights = torch.softmax(scores, dim=1) # (B, 16, 1) | |
| pooled = (weights * x).sum(dim=1) # (B, 1280) | |
| return pooled, weights.squeeze(-1) # (B, 1280), (B, 16) | |
| class FallDetector(nn.Module): | |
| """ | |
| EfficientNet-Lite0 + Temporal Attention + MLP classifier. | |
| Forward: | |
| (B, 16, 3, 224, 224) | |
| -> per-frame EfficientNet-Lite0 -> (B, 16, 1280) | |
| -> TemporalAttention -> (B, 1280), (B, 16) | |
| -> MLP classifier -> (B, 1) logit | |
| Sigmoid is applied explicitly at inference, not inside the module. | |
| """ | |
| def __init__(self, pretrained_backbone: bool = False): | |
| super().__init__() | |
| backbone = timm.create_model("efficientnet_lite0", pretrained=pretrained_backbone) | |
| self.conv_stem = backbone.conv_stem | |
| self.bn1 = backbone.bn1 | |
| self.blocks = backbone.blocks | |
| self.conv_head = backbone.conv_head | |
| self.bn2 = backbone.bn2 | |
| self.global_pool = backbone.global_pool | |
| self.temporal_attention = TemporalAttention(EFFICIENTNET_DIM) | |
| self.classifier = nn.Sequential( | |
| nn.Linear(EFFICIENTNET_DIM, 512), | |
| nn.ReLU(inplace=True), | |
| nn.Dropout(0.2), | |
| nn.Linear(512, 128), | |
| nn.ReLU(inplace=True), | |
| nn.Dropout(0.2), | |
| nn.Linear(128, 1), | |
| ) | |
| def forward(self, x: torch.Tensor): | |
| B, T, C, H, W = x.shape | |
| x_flat = x.view(B * T, C, H, W) | |
| f = self.conv_stem(x_flat) | |
| f = self.bn1(f) | |
| f = self.blocks(f) | |
| f = self.conv_head(f) | |
| f = self.bn2(f) | |
| f = self.global_pool(f) # (B*16, 1280) | |
| f = f.view(B, T, EFFICIENTNET_DIM) # (B, 16, 1280) | |
| pooled, attn_weights = self.temporal_attention(f) | |
| logit = self.classifier(pooled) # (B, 1) | |
| return logit, attn_weights | |
| def count_parameters(self): | |
| total = sum(p.numel() for p in self.parameters()) | |
| trainable = sum(p.numel() for p in self.parameters() if p.requires_grad) | |
| return total, trainable | |