"""Position-wise feed-forward sub-layer used in transformer blocks.""" from __future__ import annotations import torch import torch.nn as nn from torch import Tensor __all__ = ["FeedForwardLayer"] class FeedForwardLayer(nn.Module): """ Position-wise feed-forward layer used in Transformer blocks. Architecture: fc1: Linear(d_model -> d_ff) activation: ReLU dropout: nn.Dropout(dropout_rate) fc2: Linear(d_ff -> d_model) Args: d_model (int): Dimensionality of model embeddings. d_ff (int): Hidden dimensionality of feed-forward layer. dropout_rate (float): Dropout probability between 0 and 1 (exclusive). Shape: Input: (B, S, D) where D == d_model Output: (B, S, D) """ def __init__(self, d_model: int, d_ff: int, dropout_rate: float = 0.1): super().__init__() if not isinstance(d_ff, int): raise TypeError(f"d_ff must be an int, got {type(d_ff)}") if not isinstance(d_model, int): raise TypeError(f"d_model must be an int, got {type(d_model)}") if not isinstance(dropout_rate, float): raise TypeError(f"dropout_rate must be a float, got {type(dropout_rate)}") if d_ff <= 0: raise ValueError(f"d_ff must be strictly greater than 0, got {d_ff}") if d_model <= 0: raise ValueError(f"d_model must be strictly greater than 0, got {d_model}") if not (0.0 <= dropout_rate < 1.0): raise ValueError(f"dropout_rate must be in [0,1), got {dropout_rate}") self.d_model = d_model self.d_ff = d_ff self.dropout_rate = dropout_rate self.fc1 = nn.Linear(d_model, d_ff) self.relu = nn.ReLU() self.dropout = nn.Dropout(dropout_rate) self.fc2 = nn.Linear(d_ff, d_model) def forward(self, x: Tensor) -> Tensor: if not isinstance(x, torch.Tensor): raise TypeError(f"x must be a torch.Tensor, got {type(x)}") if x.ndim != 3: raise ValueError(f"x must be 3D of shape (B,S,D); got shape {tuple(x.shape)}") if x.shape[-1] != self.d_model: raise ValueError(f"Last dim {x.shape[-1]} must match d_model {self.d_model}") return self.fc2(self.dropout(self.relu(self.fc1(x))))