Nova / src /model /layers.py
kings1's picture
Upload folder using huggingface_hub
23ea6bd verified
Raw History Blame Contribute Delete
4.62 kB
"""
Core Layers & Transformer Encoder Block for Nova 1.0
Includes RMSNorm, SwiGLU, CausalSelfAttention with RoPE, and Post-Norm initialization.
Grounded in Hierarchical Reasoning Model (HRM) Paper specifications.
"""
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
from .rope import RotaryEmbedding
class RMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x: torch.Tensor) -> torch.Tensor:
# RMS normalization over last dimension without mean subtraction or bias
variance = x.pow(2).mean(-1, keepdim=True)
return x * torch.rsqrt(variance + self.eps) * self.weight
class SwiGLU(nn.Module):
def __init__(self, d_model: int, d_ff: int):
super().__init__()
# Gate, Up, and Down linear projections (bias=False as in LLaMA/HRM paper)
self.w_gate = nn.Linear(d_model, d_ff, bias=False)
self.w_up = nn.Linear(d_model, d_ff, bias=False)
self.w_down = nn.Linear(d_ff, d_model, bias=False)
def forward(self, x: torch.Tensor) -> torch.Tensor:
# SwiGLU(x) = (Swish(x W_g) * x W_u) W_d
return self.w_down(F.silu(self.w_gate(x)) * self.w_up(x))
class CausalSelfAttention(nn.Module):
def __init__(self, d_model: int, n_heads: int, max_seq_len: int = 2048):
super().__init__()
self.d_model = d_model
self.n_heads = n_heads
self.head_dim = d_model // n_heads
assert self.head_dim * n_heads == d_model, "d_model must be divisible by n_heads"
self.q_proj = nn.Linear(d_model, d_model, bias=False)
self.k_proj = nn.Linear(d_model, d_model, bias=False)
self.v_proj = nn.Linear(d_model, d_model, bias=False)
self.out_proj = nn.Linear(d_model, d_model, bias=False)
self.rotary_emb = RotaryEmbedding(dim=self.head_dim, max_seq_len=max_seq_len)
def forward(self, x: torch.Tensor, mask: torch.Tensor = None) -> torch.Tensor:
batch_size, seq_len, _ = x.shape
# Linear projections & reshape to (batch, n_heads, seq_len, head_dim)
q = self.q_proj(x).view(batch_size, seq_len, self.n_heads, self.head_dim).transpose(1, 2)
k = self.k_proj(x).view(batch_size, seq_len, self.n_heads, self.head_dim).transpose(1, 2)
v = self.v_proj(x).view(batch_size, seq_len, self.n_heads, self.head_dim).transpose(1, 2)
# Apply Rotary Position Embedding
q, k = self.rotary_emb(q, k, seq_len=seq_len)
# Scaled dot-product attention with causal mask
if mask is None:
# Create lower-triangular causal mask
causal_mask = torch.tril(torch.ones((seq_len, seq_len), device=x.device, dtype=torch.bool))
attn_mask = torch.zeros((seq_len, seq_len), device=x.device, dtype=q.dtype)
attn_mask.masked_fill_(~causal_mask, float("-inf"))
else:
attn_mask = mask
# PyTorch SDPA (uses ROCm optimized kernels when available)
attn_out = F.scaled_dot_product_attention(
q, k, v, attn_mask=attn_mask, dropout_p=0.0, is_causal=False
)
# Reshape back to (batch, seq_len, d_model)
attn_out = attn_out.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)
return self.out_proj(attn_out)
class TransformerBlock(nn.Module):
"""
Encoder Transformer Layer implementing Post-Norm architecture as described in HRM paper.
"""
def __init__(self, d_model: int, n_heads: int, d_ff: int, max_seq_len: int = 2048):
super().__init__()
self.attn = CausalSelfAttention(d_model=d_model, n_heads=n_heads, max_seq_len=max_seq_len)
self.norm1 = RMSNorm(d_model)
self.mlp = SwiGLU(d_model=d_model, d_ff=d_ff)
self.norm2 = RMSNorm(d_model)
def forward(self, x: torch.Tensor) -> torch.Tensor:
# Post-Norm residual connections (x = Norm(x + Sublayer(x)))
x = self.norm1(x + self.attn(x))
x = self.norm2(x + self.mlp(x))
return x
def init_weights_lecun(module: nn.Module):
"""
Truncated LeCun Normal Initialization as specified in HRM Paper (Wang et al., 2025).
"""
if isinstance(module, nn.Linear):
std = 1.0 / math.sqrt(module.in_features)
nn.init.trunc_normal_(module.weight, mean=0.0, std=std, a=-2.0 * std, b=2.0 * std)
if module.bias is not None:
nn.init.zeros_(module.bias)
elif isinstance(module, nn.Embedding):
nn.init.normal_(module.weight, mean=0.0, std=0.02)