Spaces:
Runtime error
Runtime error
Download src/models_optimized.py from Hanokalure/human-activity-backend: direct link, hf CLI and curl.
- Browser
- Download file 10.3 kB
-
https://huggingface.co/spaces/Hanokalure/human-activity-backend/resolve/main/src/models_optimized.py
- Command line
-
hf download hf://spaces/Hanokalure/human-activity-backend/src/models_optimized.py
-
curl -L -o models_optimized.py https://huggingface.co/spaces/Hanokalure/human-activity-backend/resolve/main/src/models_optimized.py
10.3 kB
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from torchvision import models | |
| import torchvision.models.video as video_models | |
| class MobileNet3D(nn.Module): | |
| """Lightweight 3D CNN based on MobileNet principles""" | |
| def __init__(self, num_classes=101, input_channels=3, dropout=0.2): | |
| super(MobileNet3D, self).__init__() | |
| # 3D Depthwise Separable Convolutions | |
| self.conv1 = nn.Conv3d(input_channels, 32, kernel_size=(3, 7, 7), stride=(1, 2, 2), padding=(1, 3, 3)) | |
| self.bn1 = nn.BatchNorm3d(32) | |
| # Depthwise separable blocks | |
| self.dw_blocks = nn.ModuleList([ | |
| self._make_dw_block(32, 64, (1, 2, 2)), | |
| self._make_dw_block(64, 128, (2, 2, 2)), | |
| self._make_dw_block(128, 256, (2, 2, 2)), | |
| self._make_dw_block(256, 512, (2, 2, 2)), | |
| ]) | |
| # Global average pooling and classifier | |
| self.global_pool = nn.AdaptiveAvgPool3d(1) | |
| self.dropout = nn.Dropout(dropout) | |
| self.classifier = nn.Linear(512, num_classes) | |
| # Initialize weights | |
| self._initialize_weights() | |
| def _make_dw_block(self, in_channels, out_channels, stride): | |
| """Create a depthwise separable 3D block""" | |
| return nn.Sequential( | |
| # Depthwise 3D conv | |
| nn.Conv3d(in_channels, in_channels, kernel_size=3, stride=stride, | |
| padding=1, groups=in_channels, bias=False), | |
| nn.BatchNorm3d(in_channels), | |
| nn.ReLU6(inplace=True), | |
| # Pointwise 3D conv | |
| nn.Conv3d(in_channels, out_channels, kernel_size=1, bias=False), | |
| nn.BatchNorm3d(out_channels), | |
| nn.ReLU6(inplace=True) | |
| ) | |
| def _initialize_weights(self): | |
| for m in self.modules(): | |
| if isinstance(m, nn.Conv3d): | |
| nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') | |
| elif isinstance(m, nn.BatchNorm3d): | |
| nn.init.constant_(m.weight, 1) | |
| nn.init.constant_(m.bias, 0) | |
| elif isinstance(m, nn.Linear): | |
| nn.init.normal_(m.weight, 0, 0.01) | |
| nn.init.constant_(m.bias, 0) | |
| def forward(self, x): | |
| # Input: [B, C, T, H, W] | |
| x = F.relu(self.bn1(self.conv1(x))) | |
| for block in self.dw_blocks: | |
| x = block(x) | |
| x = self.global_pool(x) | |
| x = x.view(x.size(0), -1) | |
| x = self.dropout(x) | |
| x = self.classifier(x) | |
| return x | |
| class Efficient3DCNN(nn.Module): | |
| """Very lightweight 3D CNN for fast training""" | |
| def __init__(self, num_classes=101, input_channels=3): | |
| super(Efficient3DCNN, self).__init__() | |
| # Efficient 3D feature extractor | |
| self.features = nn.Sequential( | |
| # Block 1 | |
| nn.Conv3d(input_channels, 32, kernel_size=(3, 7, 7), stride=(1, 2, 2), padding=(1, 3, 3)), | |
| nn.BatchNorm3d(32), | |
| nn.ReLU(inplace=True), | |
| # Block 2 | |
| nn.Conv3d(32, 64, kernel_size=(3, 3, 3), stride=(2, 2, 2), padding=(1, 1, 1)), | |
| nn.BatchNorm3d(64), | |
| nn.ReLU(inplace=True), | |
| # Block 3 | |
| nn.Conv3d(64, 128, kernel_size=(3, 3, 3), stride=(2, 2, 2), padding=(1, 1, 1)), | |
| nn.BatchNorm3d(128), | |
| nn.ReLU(inplace=True), | |
| # Block 4 - Temporal reduction | |
| nn.Conv3d(128, 256, kernel_size=(3, 3, 3), stride=(2, 2, 2), padding=(1, 1, 1)), | |
| nn.BatchNorm3d(256), | |
| nn.ReLU(inplace=True), | |
| ) | |
| # Classifier | |
| self.classifier = nn.Sequential( | |
| nn.AdaptiveAvgPool3d(1), | |
| nn.Flatten(), | |
| nn.Dropout(0.3), | |
| nn.Linear(256, 128), | |
| nn.ReLU(inplace=True), | |
| nn.Dropout(0.2), | |
| nn.Linear(128, num_classes) | |
| ) | |
| def forward(self, x): | |
| x = self.features(x) | |
| x = self.classifier(x) | |
| return x | |
| class ResNet2Plus1D_Light(nn.Module): | |
| """Lightweight (2+1)D ResNet - separates spatial and temporal convolutions""" | |
| def __init__(self, num_classes=101): | |
| super(ResNet2Plus1D_Light, self).__init__() | |
| # Use pretrained R(2+1)D but make it lighter | |
| try: | |
| # Use smaller version if available | |
| self.backbone = video_models.r2plus1d_18(pretrained=True) | |
| except: | |
| # Fallback to building our own | |
| self.backbone = self._build_light_r2plus1d() | |
| # Replace the final layer | |
| if hasattr(self.backbone, 'fc'): | |
| in_features = self.backbone.fc.in_features | |
| self.backbone.fc = nn.Linear(in_features, num_classes) | |
| else: | |
| # Custom classifier | |
| self.classifier = nn.Linear(512, num_classes) | |
| def _build_light_r2plus1d(self): | |
| """Build a lightweight (2+1)D model""" | |
| return nn.Sequential( | |
| # Spatial conv + temporal conv blocks | |
| nn.Conv3d(3, 32, kernel_size=(1, 7, 7), stride=(1, 2, 2), padding=(0, 3, 3)), | |
| nn.BatchNorm3d(32), | |
| nn.ReLU(inplace=True), | |
| nn.Conv3d(32, 32, kernel_size=(3, 1, 1), stride=(1, 1, 1), padding=(1, 0, 0)), | |
| nn.BatchNorm3d(32), | |
| nn.ReLU(inplace=True), | |
| nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(1, 2, 2), padding=(0, 1, 1)), | |
| # More (2+1)D blocks | |
| self._make_2plus1d_block(32, 64, stride=(2, 2, 2)), | |
| self._make_2plus1d_block(64, 128, stride=(2, 2, 2)), | |
| self._make_2plus1d_block(128, 256, stride=(2, 2, 2)), | |
| nn.AdaptiveAvgPool3d(1), | |
| nn.Flatten() | |
| ) | |
| def _make_2plus1d_block(self, in_channels, out_channels, stride=(1, 1, 1)): | |
| """Create a (2+1)D block""" | |
| return nn.Sequential( | |
| # Spatial convolution | |
| nn.Conv3d(in_channels, out_channels, kernel_size=(1, 3, 3), | |
| stride=(1, stride[1], stride[2]), padding=(0, 1, 1)), | |
| nn.BatchNorm3d(out_channels), | |
| nn.ReLU(inplace=True), | |
| # Temporal convolution | |
| nn.Conv3d(out_channels, out_channels, kernel_size=(3, 1, 1), | |
| stride=(stride[0], 1, 1), padding=(1, 0, 0)), | |
| nn.BatchNorm3d(out_channels), | |
| nn.ReLU(inplace=True) | |
| ) | |
| def forward(self, x): | |
| if hasattr(self.backbone, 'fc'): | |
| return self.backbone(x) | |
| else: | |
| features = self.backbone(x) | |
| return self.classifier(features) | |
| def get_optimized_model(model_type="efficient", num_classes=101, pretrained=True, freeze_backbone=False): | |
| """Get an optimized model for faster training""" | |
| if model_type == "mobilenet3d": | |
| print("π§ Using MobileNet3D (lightest, fastest training)") | |
| return MobileNet3D(num_classes=num_classes) | |
| elif model_type == "efficient": | |
| print("π§ Using Efficient3DCNN (very light, good balance)") | |
| return Efficient3DCNN(num_classes=num_classes) | |
| elif model_type == "r2plus1d_light": | |
| print("π§ Using ResNet(2+1)D Light (moderate complexity)") | |
| return ResNet2Plus1D_Light(num_classes=num_classes) | |
| elif model_type == "r3d18_transfer": | |
| print("π§ Using R3D-18 with TRANSFER LEARNING (frozen backbone)") | |
| # Load pre-trained R3D-18 | |
| model = video_models.r3d_18(pretrained=pretrained) | |
| # Freeze all backbone layers if requested | |
| if freeze_backbone: | |
| print("π§ Freezing backbone layers - only training classifier") | |
| for name, param in model.named_parameters(): | |
| if 'fc' not in name: # Don't freeze final classifier | |
| param.requires_grad = False | |
| # Replace final classifier | |
| model.fc = nn.Sequential( | |
| nn.Dropout(0.5), | |
| nn.Linear(model.fc.in_features, 256), | |
| nn.ReLU(inplace=True), | |
| nn.Dropout(0.3), | |
| nn.Linear(256, num_classes) | |
| ) | |
| # Count trainable parameters | |
| total_params = sum(p.numel() for p in model.parameters()) | |
| trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad) | |
| print(f"π Total params: {total_params:,} | Trainable: {trainable_params:,}") | |
| return model | |
| elif model_type == "r3d18_light": | |
| print("π§ Using R3D-18 with optimizations") | |
| # Original R3D-18 but with some optimizations | |
| model = video_models.r3d_18(pretrained=pretrained) | |
| model.fc = nn.Sequential( | |
| nn.Dropout(0.3), | |
| nn.Linear(model.fc.in_features, 512), | |
| nn.ReLU(inplace=True), | |
| nn.Dropout(0.2), | |
| nn.Linear(512, num_classes) | |
| ) | |
| return model | |
| else: | |
| raise ValueError(f"Unknown model type: {model_type}") | |
| def count_parameters(model): | |
| """Count the number of trainable parameters""" | |
| total_params = sum(p.numel() for p in model.parameters()) | |
| trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad) | |
| print(f"Total parameters: {total_params:,}") | |
| print(f"Trainable parameters: {trainable_params:,}") | |
| print(f"Model size: ~{total_params * 4 / 1024 / 1024:.2f} MB") | |
| return total_params, trainable_params | |
| def get_model_comparison(): | |
| """Compare different model architectures""" | |
| print("\nπ Model Comparison:") | |
| print("=" * 60) | |
| models_info = { | |
| "MobileNet3D": {"params": "~2-5M", "speed": "Fastest", "accuracy": "Good"}, | |
| "Efficient3DCNN": {"params": "~1-3M", "speed": "Very Fast", "accuracy": "Good"}, | |
| "R(2+1)D Light": {"params": "~5-10M", "speed": "Fast", "accuracy": "Better"}, | |
| "R3D-18 Original": {"params": "~33M", "speed": "Slow", "accuracy": "Best"} | |
| } | |
| for name, info in models_info.items(): | |
| print(f"{name:20} | Params: {info['params']:8} | Speed: {info['speed']:9} | Accuracy: {info['accuracy']}") | |
| print("=" * 60) | |
| print("π‘ Recommendation: Start with 'efficient' or 'mobilenet3d' for faster training!") |