from __future__ import annotations import torch import torch.nn as nn class PositionalEncoding(nn.Module): def __init__(self, d_model: int, max_len: int = 512): super().__init__() self.pe = nn.Embedding(max_len, d_model) def forward(self, x: torch.Tensor) -> torch.Tensor: pos = torch.arange(x.size(1), device=x.device).unsqueeze(0) return x + self.pe(pos) class AnomalyTransformer(nn.Module): def __init__( self, feat_dim: int = 768, d_model: int = 512, num_heads: int = 8, num_layers: int = 4, ff_dim: int = 1024, dropout: float = 0.3, max_frames: int = 512, ): super().__init__() self.proj = nn.Sequential( nn.Linear(feat_dim, d_model), nn.LayerNorm(d_model), nn.GELU(), ) self.cls_token = nn.Parameter(torch.randn(1, 1, d_model) * 0.02) self.pos_enc = PositionalEncoding(d_model, max_len=max_frames + 1) encoder_layer = nn.TransformerEncoderLayer( d_model=d_model, nhead=num_heads, dim_feedforward=ff_dim, dropout=dropout, activation="gelu", batch_first=True, norm_first=True, ) self.transformer = nn.TransformerEncoder( encoder_layer, num_layers=num_layers, norm=nn.LayerNorm(d_model), ) self.head = nn.Sequential( nn.Linear(d_model, d_model // 2), nn.GELU(), nn.Dropout(dropout), nn.Linear(d_model // 2, 2), ) def forward(self, x: torch.Tensor) -> torch.Tensor: batch_size = x.size(0) x = self.proj(x) cls = self.cls_token.expand(batch_size, -1, -1) x = torch.cat([cls, x], dim=1) x = self.pos_enc(x) x = self.transformer(x) return self.head(x[:, 0, :])