| """
|
| ===============================================================================
|
| model/cnn.py — CNN Architecture for Machine Sound Classification
|
| ===============================================================================
|
|
|
| OWNER: EL sir
|
| """
|
|
|
| import torch
|
| import torch.nn as nn
|
| from config import (
|
| CNN_INPUT_CHANNELS,
|
| NUM_CLASSES,
|
| CNN_FILTERS,
|
| CNN_KERNEL_SIZE,
|
| CNN_PADDING,
|
| CNN_POOL_SIZE,
|
| ADAPTIVE_POOL_OUTPUT,
|
| )
|
|
|
| class ConvBlock(nn.Module):
|
| """
|
| A single convolutional block: Conv2d → BatchNorm → ReLU → MaxPool2d.
|
| """
|
| def __init__(self, in_channels, out_channels):
|
| super(ConvBlock, self).__init__()
|
| self.conv = nn.Conv2d(
|
| in_channels, out_channels,
|
| kernel_size=CNN_KERNEL_SIZE,
|
| padding=CNN_PADDING,
|
| bias=False
|
| )
|
| self.bn = nn.BatchNorm2d(out_channels)
|
| self.relu = nn.ReLU(inplace=True)
|
| self.pool = nn.MaxPool2d(CNN_POOL_SIZE)
|
|
|
| def forward(self, x):
|
| x = self.conv(x)
|
| x = self.bn(x)
|
| x = self.relu(x)
|
| x = self.pool(x)
|
| return x
|
|
|
|
|
| class MachineSoundCNN(nn.Module):
|
| """
|
| Custom CNN for 6-class machine sound classification from mel spectrograms.
|
| """
|
| def __init__(self, num_classes=NUM_CLASSES):
|
| super(MachineSoundCNN, self).__init__()
|
|
|
|
|
|
|
|
|
| layers = []
|
| in_channels = CNN_INPUT_CHANNELS
|
|
|
|
|
| for out_channels in CNN_FILTERS:
|
| layers.append(ConvBlock(in_channels, out_channels))
|
| in_channels = out_channels
|
|
|
| self.features = nn.Sequential(*layers)
|
|
|
|
|
|
|
|
|
|
|
| self.adaptive_pool = nn.AdaptiveAvgPool2d(ADAPTIVE_POOL_OUTPUT)
|
|
|
|
|
|
|
|
|
|
|
| flattened_size = CNN_FILTERS[-1] * ADAPTIVE_POOL_OUTPUT[0] * ADAPTIVE_POOL_OUTPUT[1]
|
|
|
| self.classifier = nn.Sequential(
|
| nn.Flatten(),
|
| nn.Dropout(p=0.5),
|
| nn.Linear(flattened_size, 512),
|
| nn.ReLU(inplace=True),
|
| nn.Dropout(p=0.5),
|
| nn.Linear(512, num_classes)
|
| )
|
|
|
| def forward(self, x):
|
|
|
| x = self.features(x)
|
|
|
| x = self.adaptive_pool(x)
|
|
|
| x = self.classifier(x)
|
| return x
|
|
|
|
|
|
|
|
|
|
|
| if __name__ == "__main__":
|
| print("=" * 60)
|
| print("CNN Architecture Shape Sanity Check")
|
| print("=" * 60)
|
|
|
| model = MachineSoundCNN()
|
|
|
|
|
| dummy_input = torch.randn(4, 1, 128, 281)
|
| print(f"\nInput shape: {dummy_input.shape}")
|
|
|
|
|
| x = dummy_input
|
| for i, block in enumerate(model.features):
|
| x = block(x)
|
| print(f"After Conv Block {i+1} ({CNN_FILTERS[i]} filters): {x.shape}")
|
|
|
| x = model.adaptive_pool(x)
|
| print(f"After AdaptivePool: {x.shape}")
|
|
|
| x = model.classifier(x)
|
| print(f"After Classifier: {x.shape}")
|
|
|
| print(f"\n✓ Output shape is correct: {x.shape} (batch=4, classes={NUM_CLASSES})")
|
|
|
|
|
| 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"\nTotal parameters: {total_params:,}")
|
| print("=" * 60) |