| import torch
|
| import torch.nn as nn
|
| import torch.nn.functional as F
|
|
|
|
|
| class TrafficSignNet(nn.Module):
|
| """
|
| Ultra-lightweight CNN model optimized for Edge / Web CPU inference.
|
| Input size: (batch_size, 3, 32, 32)
|
| Output size: (batch_size, 12)
|
| Number of parameters: ~65k
|
| """
|
|
|
| def __init__(self, num_classes=12):
|
| super().__init__()
|
|
|
|
|
| self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1)
|
| self.bn1 = nn.BatchNorm2d(16)
|
|
|
|
|
| self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)
|
| self.bn2 = nn.BatchNorm2d(32)
|
|
|
|
|
| self.conv3 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)
|
| self.bn3 = nn.BatchNorm2d(64)
|
|
|
|
|
| self.global_pool = nn.AdaptiveAvgPool2d((1, 1))
|
|
|
|
|
|
|
| self.fc1_conv = nn.Conv2d(64, 64, kernel_size=1)
|
| self.dropout = nn.Dropout(0.3)
|
| self.fc2_conv = nn.Conv2d(64, num_classes, kernel_size=1)
|
|
|
| def forward(self, x):
|
|
|
| x = self.conv1(x)
|
| x = self.bn1(x)
|
| x = F.relu(x)
|
| x = F.max_pool2d(x, 2, 2)
|
|
|
|
|
| x = self.conv2(x)
|
| x = self.bn2(x)
|
| x = F.relu(x)
|
| x = F.max_pool2d(x, 2, 2)
|
|
|
|
|
| x = self.conv3(x)
|
| x = self.bn3(x)
|
| x = F.relu(x)
|
| x = F.max_pool2d(x, 2, 2)
|
|
|
|
|
| x = self.global_pool(x)
|
|
|
|
|
| x = self.fc1_conv(x)
|
| x = F.relu(x)
|
| x = self.dropout(x)
|
| x = self.fc2_conv(x)
|
|
|
|
|
| x = torch.flatten(x, 1)
|
| return x
|
|
|
|
|
| if __name__ == "__main__":
|
|
|
| model = TrafficSignNet(num_classes=12)
|
| x = torch.randn(1, 3, 32, 32)
|
| out = model(x)
|
| print("Input shape:", x.shape)
|
| print("Output shape:", out.shape)
|
| total_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
|
| print(f"Total trainable parameters: {total_params:,}")
|
|
|