| """Layer 2: one recurrent block (pre-LN + residual, LSTM or GRU). |
| |
| The 12-layer stack instantiates this 9 times: layers 2..10. |
| Causal by construction - no mask needed; memory is O(T), not O(T^2). |
| Hidden state resets each forward (windowed training); stateful sampling |
| can pass an initial state later. |
| |
| Independence: imports only torch. Knows nothing about other layers. |
| """ |
| from dataclasses import dataclass |
|
|
| import torch.nn as nn |
|
|
|
|
| @dataclass |
| class Layer2Config: |
| d_model: int = 512 |
| rnn_type: str = "gru" |
| dropout: float = 0.0 |
|
|
|
|
| class Layer2(nn.Module): |
| def __init__(self, cfg: Layer2Config): |
| super().__init__() |
| self.cfg = cfg |
| self.ln = nn.LayerNorm(cfg.d_model) |
| if cfg.rnn_type == "lstm": |
| self.rnn = nn.LSTM(cfg.d_model, cfg.d_model, batch_first=True) |
| elif cfg.rnn_type == "gru": |
| self.rnn = nn.GRU(cfg.d_model, cfg.d_model, batch_first=True) |
| else: |
| raise ValueError(f"unknown rnn_type: {cfg.rnn_type}") |
|
|
| def forward(self, x, state=None): |
| out, state = self.rnn(self.ln(x), state) |
| return x + out, state |
|
|
| @property |
| def n_params(self): |
| return sum(p.numel() for p in self.parameters()) |
|
|