import torch import torch.nn as nn class ViolenceGRU(nn.Module): def __init__(self): super(ViolenceGRU, self).__init__() # 2D CNN Backbone (Applied to each frame independently) self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1) self.bn1 = nn.BatchNorm2d(32) self.pool1 = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1) self.bn2 = nn.BatchNorm2d(64) self.pool2 = nn.MaxPool2d(2, 2) self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1) self.bn3 = nn.BatchNorm2d(128) self.pool3 = nn.MaxPool2d(2, 2) self.conv4 = nn.Conv2d(128, 256, kernel_size=3, padding=1) self.bn4 = nn.BatchNorm2d(256) self.pool4 = nn.MaxPool2d(2, 2) self.relu = nn.ReLU() self.dropout = nn.Dropout(0.5) # Calculate feature dim after CNN # Input: 112x112 # Pool1: 56x56 # Pool2: 28x28 # Pool3: 14x14 # Pool4: 7x7 self.feature_dim = 256 * 7 * 7 # GRU Layer # input_size: Feature vector from CNN # hidden_size: 256 # num_layers: 2 self.gru = nn.GRU(input_size=self.feature_dim, hidden_size=256, num_layers=2, batch_first=True, dropout=0.5) self.fc = nn.Linear(256, 2) # Binary Classification def forward(self, x): # x shape from Dataset: (Batch, C, Seq, H, W) b, c, s, h, w = x.size() # Reshape for 2D CNN: (Batch * Seq, C, H, W) x = x.permute(0, 2, 1, 3, 4).contiguous() # (B, S, C, H, W) x = x.view(b * s, c, h, w) # Pass through CNN x = self.relu(self.bn1(self.conv1(x))) x = self.pool1(x) x = self.relu(self.bn2(self.conv2(x))) x = self.pool2(x) x = self.relu(self.bn3(self.conv3(x))) x = self.pool3(x) x = self.relu(self.bn4(self.conv4(x))) x = self.pool4(x) # Flatten features: (Batch * Seq, Feature_Dim) x = x.view(b * s, -1) # Reshape for GRU: (Batch, Seq, Feature_Dim) x = x.view(b, s, -1) # GRU Pass # out: (Batch, Seq, Hidden_Dim) out, _ = self.gru(x) # Take the last time step output for classification out = out[:, -1, :] out = self.dropout(out) out = self.fc(out) return out