""" The GRU, copied verbatim from the training repository so the Space is self-contained. Architecture must match the checkpoint exactly. Source: https://github.com/adwitiyashukla/DL-based-sequential-fraud-detection """ from __future__ import annotations import torch from torch import nn class GRUFraudModel(nn.Module): """ Per timestep the model sees a learned embedding of the merchant category and a linear projection of the standardised numeric features. Those are concatenated and fed to a single layer GRU. The hidden state at the final timestep, which is the transaction being scored, goes through dropout and a linear head to one logit. """ def __init__( self, n_numeric: int, n_categories: int, emb_dim: int = 16, proj_dim: int = 32, hidden: int = 64, dropout: float = 0.2, ) -> None: super().__init__() self.cat_emb = nn.Embedding(n_categories + 1, emb_dim, padding_idx=0) self.num_proj = nn.Linear(n_numeric, proj_dim) self.gru = nn.GRU( input_size=proj_dim + emb_dim, hidden_size=hidden, num_layers=1, batch_first=True, ) self.dropout = nn.Dropout(dropout) self.head = nn.Linear(hidden, 1) def forward(self, x_num: torch.Tensor, x_cat: torch.Tensor) -> torch.Tensor: emb = self.cat_emb(x_cat) proj = torch.relu(self.num_proj(x_num)) out, _ = self.gru(torch.cat([proj, emb], dim=-1)) return self.head(self.dropout(out[:, -1, :])).squeeze(-1)