| 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, :]) |
|
|