Download src/model.py from YSamchuk/Hetman-2b: direct link, hf CLI and curl.
- Browser
- Download file 29 kB
-
https://huggingface.co/YSamchuk/Hetman-2b/resolve/main/src/model.py
- Command line
-
hf download hf://YSamchuk/Hetman-2b/src/model.py
-
curl -L -o model.py https://huggingface.co/YSamchuk/Hetman-2b/resolve/main/src/model.py
29 kB
| """ | |
| Hetman-2-NCM 2.0B: Master PyTorch Architecture Implementation | |
| File: src/model.py | |
| License: Apache License 2.0 | |
| Implements: | |
| - Hetman2Config & Hetman2Config.create_mini() | |
| - BitNet b1.58 Ternary Quantization & BitLinear | |
| - Fast Walsh-Hadamard Transform (FWHT INT8 Engine) | |
| - Gated DeltaNet Recurrent Block (O(1) Constant FP32 State) | |
| - Phase 1: Causal AR Blueprint Drafter | |
| - Phase 2: Global Bidirectional NCM Refiner & Anti-Hallucination Deduplicator (3 passes) | |
| - Phase 3: Block-Causal NCM Decoder (8 passes via DynamicBlueprintEvolver) | |
| - Hetman2ForCausalLM (Full End-to-End Hierarchical Generation Pipeline) | |
| """ | |
| import math | |
| from dataclasses import dataclass | |
| from typing import Dict, List, Optional, Tuple, Any | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| import sys | |
| from pathlib import Path | |
| _ROOT = str(Path(__file__).resolve().parent.parent) | |
| if _ROOT not in sys.path: | |
| sys.path.insert(0, _ROOT) | |
| from src.dynamic_blueprint_evolver import ( | |
| DynamicBlueprintEvolver, | |
| SphericalNorm, | |
| AdaLNZeroModulator, | |
| ) | |
| class Hetman2Config: | |
| """ | |
| Configuration specification for Hetman-2-NCM 2.0B. | |
| """ | |
| vocab_size: int = 65536 | |
| hidden_size: int = 1536 | |
| intermediate_size: int = 6144 | |
| num_layers: int = 34 | |
| num_deltanet_layers: int = 28 | |
| num_global_layers: int = 6 | |
| deltanet_heads: int = 16 | |
| deltanet_head_dim: int = 64 | |
| global_heads_q: int = 12 | |
| global_heads_kv: int = 2 | |
| global_head_dim: int = 128 | |
| hopfield_groups: int = 16 | |
| hopfield_codebook_size: int = 1024 | |
| hopfield_val_dim: int = 32 | |
| tokens_per_block: int = 400 | |
| max_blocks: int = 8 | |
| latents_per_block: int = 16 | |
| phase2_passes: int = 3 | |
| phase3_passes: int = 8 | |
| fwht_block_size: int = 512 | |
| dedup_tau: float = 0.85 | |
| seam_window: int = 4 | |
| alpha_residual_anchor: float = 0.25 | |
| def create_mini( | |
| cls, | |
| vocab_size: int = 256, | |
| hidden_size: int = 128, | |
| intermediate_size: int = 256, | |
| num_layers: int = 4, | |
| num_deltanet_layers: int = 3, | |
| num_global_layers: int = 1, | |
| deltanet_heads: int = 4, | |
| deltanet_head_dim: int = 32, | |
| tokens_per_block: int = 16, | |
| max_blocks: int = 2, | |
| latents_per_block: int = 4, | |
| phase2_passes: int = 3, | |
| phase3_passes: int = 8, | |
| fwht_block_size: int = 64, | |
| **kwargs: Any | |
| ) -> "Hetman2Config": | |
| """ | |
| Creates a compact mini-instance configuration for fast unit & end-to-end testing | |
| while preserving all architectural invariants and mathematical structures. | |
| """ | |
| return cls( | |
| vocab_size=vocab_size, | |
| hidden_size=hidden_size, | |
| intermediate_size=intermediate_size, | |
| num_layers=num_layers, | |
| num_deltanet_layers=num_deltanet_layers, | |
| num_global_layers=num_global_layers, | |
| deltanet_heads=deltanet_heads, | |
| deltanet_head_dim=deltanet_head_dim, | |
| global_heads_q=max(2, deltanet_heads), | |
| global_heads_kv=max(1, deltanet_heads // 2), | |
| global_head_dim=deltanet_head_dim, | |
| hopfield_groups=4, | |
| hopfield_codebook_size=64, | |
| hopfield_val_dim=16, | |
| tokens_per_block=tokens_per_block, | |
| max_blocks=max_blocks, | |
| latents_per_block=latents_per_block, | |
| phase2_passes=phase2_passes, | |
| phase3_passes=phase3_passes, | |
| fwht_block_size=min(fwht_block_size, hidden_size), | |
| **kwargs | |
| ) | |
| # ============================================================================== | |
| # 1. BitNet b1.58 Ternary Quantization | |
| # ============================================================================== | |
| class TernaryQuantizeSTE(torch.autograd.Function): | |
| """ | |
| Straight-Through Estimator for BitNet b1.58 Absmean ternary quantization {-1, 0, +1}. | |
| """ | |
| def forward(ctx, w: torch.Tensor, eps: float = 1e-6) -> Tuple[torch.Tensor, torch.Tensor]: | |
| if torch.isnan(w).any() or torch.isinf(w).any(): | |
| w = torch.nan_to_num(w, nan=0.0, posinf=1.0, neginf=-1.0) | |
| gamma = torch.mean(torch.abs(w)) + eps | |
| scaled_w = w / gamma | |
| w_quant = torch.clamp(torch.round(scaled_w), -1.0, 1.0) | |
| ctx.save_for_backward(scaled_w) | |
| return w_quant, gamma | |
| def backward(ctx, grad_w_quant: torch.Tensor, grad_gamma: Optional[torch.Tensor]) -> Tuple[torch.Tensor, None]: | |
| scaled_w, = ctx.saved_tensors | |
| mask = (torch.abs(scaled_w) <= 1.0).to(grad_w_quant.dtype) | |
| grad_w = grad_w_quant * mask | |
| return grad_w, None | |
| class BitLinear(nn.Module): | |
| """ | |
| BitNet b1.58 Linear layer with ternary weights {-1, 0, +1} and per-tensor scale gamma. | |
| """ | |
| def __init__(self, in_features: int, out_features: int, bias: bool = False): | |
| super().__init__() | |
| self.in_features = in_features | |
| self.out_features = out_features | |
| self.weight = nn.Parameter(torch.randn(out_features, in_features) * (1.0 / math.sqrt(in_features))) | |
| if bias: | |
| self.bias = nn.Parameter(torch.zeros(out_features)) | |
| else: | |
| self.register_parameter("bias", None) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| w_quant, gamma = TernaryQuantizeSTE.apply(self.weight) | |
| out = F.linear(x, w_quant, None) * gamma | |
| if self.bias is not None: | |
| out = out + self.bias | |
| return out | |
| # ============================================================================== | |
| # 2. Fast Walsh-Hadamard Transform (FWHT B=512) | |
| # ============================================================================== | |
| class FastWalshHadamardTransform(nn.Module): | |
| """ | |
| Online Blockwise FWHT for outlier attenuation and INT8 stabilization. | |
| """ | |
| def __init__(self, block_size: int = 512, eta_min: float = 0.05, eta_max: float = 40.0): | |
| super().__init__() | |
| self.block_size = block_size | |
| self.eta_min = eta_min | |
| self.eta_max = eta_max | |
| def _fwht_butterfly(self, x: torch.Tensor) -> torch.Tensor: | |
| orig_shape = x.shape | |
| dim = orig_shape[-1] | |
| x_flat = x.reshape(-1, dim) | |
| h = 1 | |
| while h < dim: | |
| x_flat = x_flat.view(-1, dim // (2 * h), 2, h) | |
| x0 = x_flat[:, :, 0, :].clone() | |
| x1 = x_flat[:, :, 1, :].clone() | |
| x_flat = torch.cat([x0 + x1, x0 - x1], dim=2) | |
| h *= 2 | |
| return x_flat.view(orig_shape) / math.sqrt(dim) | |
| def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, float]: | |
| if torch.isnan(x).any() or torch.isinf(x).any(): | |
| x = torch.nan_to_num(x, nan=0.0, posinf=self.eta_max, neginf=-self.eta_max) | |
| orig_shape = x.shape | |
| d = orig_shape[-1] | |
| block = min(self.block_size, d) | |
| if d % block != 0: | |
| return x, 1.0 | |
| x_blocked = x.view(-1, block) | |
| x_rot = self._fwht_butterfly(x_blocked) | |
| eta = torch.amax(torch.abs(x_rot), dim=-1, keepdim=True) + 1e-5 | |
| eta_clamped = torch.clamp(eta, self.eta_min, self.eta_max) | |
| x_quant = torch.clamp(torch.round(x_rot * (127.0 / eta_clamped)), -128.0, 127.0) | |
| x_dequant = x_quant * (eta_clamped / 127.0) | |
| x_inv = self._fwht_butterfly(x_dequant) | |
| return x_inv.view(orig_shape), eta_clamped.mean().item() | |
| # ============================================================================== | |
| # 3. Gated DeltaNet Recurrent Block (O(1) State Dynamics in FP32) | |
| # ============================================================================== | |
| class GatedDeltaNetLayer(nn.Module): | |
| """ | |
| Linear recurrent Gated DeltaNet layer with constant O(1) state per head. | |
| Maintains recurrent state S_t in FP32 across all generation steps. | |
| """ | |
| def __init__(self, d_model: int, num_heads: int, head_dim: int): | |
| super().__init__() | |
| self.d_model = d_model | |
| self.num_heads = num_heads | |
| self.head_dim = head_dim | |
| self.total_dim = num_heads * head_dim | |
| self.q_proj = BitLinear(d_model, self.total_dim, bias=False) | |
| self.k_proj = BitLinear(d_model, self.total_dim, bias=False) | |
| self.v_proj = BitLinear(d_model, self.total_dim, bias=False) | |
| self.alpha_proj = nn.Linear(d_model, num_heads, bias=True) | |
| self.beta_proj = nn.Linear(d_model, num_heads, bias=True) | |
| self.out_proj = BitLinear(self.total_dim, d_model, bias=False) | |
| self.norm = nn.LayerNorm(d_model) | |
| # SwiGLU FFN | |
| intermediate = d_model * 4 | |
| self.ffn_gate = BitLinear(d_model, intermediate, bias=False) | |
| self.ffn_up = BitLinear(d_model, intermediate, bias=False) | |
| self.ffn_down = BitLinear(intermediate, d_model, bias=False) | |
| self.ffn_norm = nn.LayerNorm(d_model) | |
| def forward_step( | |
| self, | |
| x_t: torch.Tensor, | |
| state_prev: Optional[torch.Tensor] = None | |
| ) -> Tuple[torch.Tensor, torch.Tensor]: | |
| """ | |
| Single recurrent step for token vector x_t: [Batch, d_model]. | |
| state_prev: [Batch, num_heads, head_dim, head_dim] in FP32. | |
| """ | |
| B = x_t.size(0) | |
| if state_prev is None: | |
| state_prev = torch.zeros( | |
| B, self.num_heads, self.head_dim, self.head_dim, | |
| dtype=torch.float32, device=x_t.device | |
| ) | |
| elif torch.isnan(state_prev).any() or torch.isinf(state_prev).any(): | |
| state_prev = torch.nan_to_num(state_prev, nan=0.0, posinf=50.0, neginf=-50.0) | |
| if torch.isnan(x_t).any() or torch.isinf(x_t).any(): | |
| x_t = torch.nan_to_num(x_t, nan=0.0, posinf=50.0, neginf=-50.0) | |
| norm_x = self.norm(x_t) | |
| q = self.q_proj(norm_x).view(B, self.num_heads, self.head_dim) | |
| k = self.k_proj(norm_x).view(B, self.num_heads, self.head_dim) | |
| v = self.v_proj(norm_x).view(B, self.num_heads, self.head_dim) | |
| # Normalize key vector for stable recurrence | |
| k = F.normalize(k, p=2, dim=-1) | |
| alpha = torch.sigmoid(self.alpha_proj(norm_x)).view(B, self.num_heads, 1, 1).to(torch.float32) | |
| beta = torch.sigmoid(self.beta_proj(norm_x)).view(B, self.num_heads, 1).to(torch.float32) | |
| # Recurrent DeltaNet update: | |
| # delta_v = v - (S_{t-1} @ k) | |
| k_col = k.unsqueeze(-1).to(torch.float32) # [B, H, D, 1] | |
| v_col = v.unsqueeze(-1).to(torch.float32) # [B, H, D, 1] | |
| q_row = q.unsqueeze(-2).to(torch.float32) # [B, H, 1, D] | |
| retrieved_v = torch.matmul(state_prev, k_col) # [B, H, D, 1] | |
| delta_v = v_col - retrieved_v | |
| update_term = torch.matmul(delta_v, k_col.transpose(-2, -1)) # [B, H, D, D] | |
| state_new = alpha * state_prev + beta.unsqueeze(-1) * update_term | |
| # Readout: out = S_new @ q | |
| out_v = torch.matmul(state_new, q_row.transpose(-2, -1)).squeeze(-1) # [B, H, D] | |
| attn_out = self.out_proj(out_v.view(B, -1).to(x_t.dtype)) | |
| x_post = x_t + attn_out | |
| # SwiGLU FFN step | |
| ffn_in = self.ffn_norm(x_post) | |
| ffn_out = self.ffn_down(F.silu(self.ffn_gate(ffn_in)) * self.ffn_up(ffn_in)) | |
| x_final = x_post + ffn_out | |
| return x_final, state_new | |
| def forward_sequence( | |
| self, | |
| x_seq: torch.Tensor, | |
| state_init: Optional[torch.Tensor] = None | |
| ) -> Tuple[torch.Tensor, torch.Tensor]: | |
| """ | |
| Processes full token sequence [Batch, SeqLen, d_model] through recurrent steps. | |
| """ | |
| B, S, D = x_seq.shape | |
| state = state_init | |
| outputs = [] | |
| for t in range(S): | |
| x_t = x_seq[:, t, :] | |
| out_t, state = self.forward_step(x_t, state) | |
| outputs.append(out_t.unsqueeze(1)) | |
| return torch.cat(outputs, dim=1), state | |
| # ============================================================================== | |
| # 4. Phase 1: Causal AR Blueprint Drafter | |
| # ============================================================================== | |
| class Phase1BlueprintDrafter(nn.Module): | |
| """ | |
| Phase 1: Generates coarse conceptual blueprints B_k for K blocks based on prompt context. | |
| """ | |
| def __init__(self, config: Hetman2Config): | |
| super().__init__() | |
| self.config = config | |
| self.d_model = config.hidden_size | |
| self.max_blocks = config.max_blocks | |
| self.latents_per_block = config.latents_per_block | |
| self.block_query_embed = nn.Parameter( | |
| torch.randn(config.max_blocks, self.d_model) * 0.02 | |
| ) | |
| self.drafter_layers = nn.ModuleList([ | |
| GatedDeltaNetLayer( | |
| d_model=self.d_model, | |
| num_heads=config.deltanet_heads, | |
| head_dim=config.deltanet_head_dim | |
| ) | |
| for _ in range(min(4, config.num_layers)) | |
| ]) | |
| self.spher_norm = SphericalNorm() | |
| def forward( | |
| self, | |
| prompt_repr: torch.Tensor, | |
| num_blocks: Optional[int] = None | |
| ) -> torch.Tensor: | |
| """ | |
| prompt_repr: [Batch, PromptLen, d_model] | |
| Returns: | |
| blueprints: [Batch, num_blocks, d_model] | |
| """ | |
| B = prompt_repr.size(0) | |
| K = num_blocks or self.max_blocks | |
| K = min(K, self.max_blocks) | |
| # Context summary via mean pooling with empty and NaN guards | |
| if prompt_repr.size(1) == 0: | |
| ctx = torch.zeros(B, 1, self.d_model, device=prompt_repr.device, dtype=prompt_repr.dtype) | |
| else: | |
| if torch.isnan(prompt_repr).any() or torch.isinf(prompt_repr).any(): | |
| prompt_repr = torch.nan_to_num(prompt_repr, nan=0.0, posinf=10.0, neginf=-10.0) | |
| ctx = prompt_repr.mean(dim=1, keepdim=True) # [B, 1, d] | |
| queries = self.block_query_embed[:K].unsqueeze(0).expand(B, -1, -1) # [B, K, d] | |
| x = queries + ctx | |
| # Run through causal drafter layers | |
| state = None | |
| for layer in self.drafter_layers: | |
| x, state = layer.forward_sequence(x, state) | |
| blueprints = self.spher_norm(x) | |
| return blueprints | |
| # ============================================================================== | |
| # 5. Phase 2: Global Bidirectional NCM Refiner & Anti-Hallucination Deduplicator | |
| # ============================================================================== | |
| class SemanticDeduplicationFilter(nn.Module): | |
| """ | |
| Anti-Hallucination Deduplicator. Applies inhibitory orthogonal repulsion | |
| when inter-chunk cosine similarity exceeds tau = 0.85. | |
| """ | |
| def __init__(self, d_model: int = 1536, tau: float = 0.85, eps: float = 1e-8): | |
| super().__init__() | |
| self.d_model = d_model | |
| self.tau = tau | |
| self.eps = eps | |
| def forward(self, H: torch.Tensor) -> Tuple[torch.Tensor, int]: | |
| # H: [Batch, NumChunks, d_model] | |
| B, K, d = H.shape | |
| if K <= 1: | |
| return H, 0 | |
| c_norm = torch.norm(H, p=2, dim=-1, keepdim=True) + self.eps | |
| c_hat = H / c_norm | |
| M = torch.bmm(c_hat, c_hat.transpose(1, 2)) | |
| H_out = H.clone() | |
| repulsions = 0 | |
| for b in range(B): | |
| for i in range(K): | |
| for j in range(i + 1, K): | |
| sim = M[b, i, j].item() | |
| if math.isnan(sim): | |
| sim = 0.0 | |
| if sim > self.tau: | |
| repulsions += 1 | |
| c_i = H[b, i] | |
| c_j = H[b, j] | |
| c_i_sq = torch.dot(c_i, c_i) + self.eps | |
| proj = (torch.dot(c_j, c_i) / c_i_sq) * c_i | |
| alpha_rep = min(1.0, (sim - self.tau) / (1.0 - self.tau + self.eps)) | |
| H_out[b, j] = H_out[b, j] - alpha_rep * proj | |
| return H_out, repulsions | |
| class BoundarySeamSmoother(nn.Module): | |
| """ | |
| Smooths semantic transitions across chunk boundaries. | |
| """ | |
| def __init__(self, d_model: int = 1536): | |
| super().__init__() | |
| self.d_model = d_model | |
| self.gate = nn.Sequential( | |
| nn.Linear(d_model * 2, d_model), | |
| nn.Sigmoid() | |
| ) | |
| def forward(self, H: torch.Tensor) -> torch.Tensor: | |
| B, K, d = H.shape | |
| if K <= 1: | |
| return H | |
| H_out = H.clone() | |
| for k in range(K - 1): | |
| h_curr = H[:, k, :] | |
| h_next = H[:, k + 1, :] | |
| bridge = 0.5 * (h_curr + h_next) | |
| g = self.gate(torch.cat([h_curr, h_next], dim=-1)) | |
| H_out[:, k + 1, :] = H_out[:, k + 1, :] + 0.15 * g * (bridge - h_next) | |
| H_out[:, k, :] = H_out[:, k, :] + 0.15 * (1.0 - g) * (bridge - h_curr) | |
| return H_out | |
| class DirichletEnergyWatchdog(nn.Module): | |
| """ | |
| Tracks graph Dirichlet energy E(H) to safeguard against oversmoothing. | |
| """ | |
| def __init__(self, threshold_ratio: float = 0.5, boost_gain: float = 0.25): | |
| super().__init__() | |
| self.threshold_ratio = threshold_ratio | |
| self.boost_gain = boost_gain | |
| def compute_energy(self, H: torch.Tensor) -> float: | |
| B, K, d = H.shape | |
| if K <= 1: | |
| return 1.0 | |
| diff = H.unsqueeze(2) - H.unsqueeze(1) | |
| energy = torch.sum(diff ** 2, dim=-1).mean() / (2.0 * K) | |
| return float(energy.item()) | |
| def forward(self, H: torch.Tensor, initial_energy: float) -> Tuple[torch.Tensor, float, bool]: | |
| current_energy = self.compute_energy(H) | |
| triggered = False | |
| H_out = H | |
| if math.isnan(initial_energy) or math.isnan(current_energy): | |
| triggered = True | |
| H_out = torch.nan_to_num(H, nan=0.0) | |
| current_energy = 1.0 | |
| elif initial_energy < 1e-6: | |
| triggered = True | |
| barycenter = H.mean(dim=1, keepdim=True) | |
| H_out = H + self.boost_gain * (H - barycenter) + torch.randn_like(H) * 0.02 | |
| elif current_energy < self.threshold_ratio * initial_energy: | |
| triggered = True | |
| barycenter = H.mean(dim=1, keepdim=True) | |
| H_out = H + self.boost_gain * (H - barycenter) | |
| return H_out, current_energy, triggered | |
| class Phase2NCMRefiner(nn.Module): | |
| """ | |
| Phase 2: Global Bidirectional NCM Refiner. | |
| Iteratively refines raw draft blueprints across 3 passes (num_iterations=3). | |
| """ | |
| def __init__(self, config: Hetman2Config): | |
| super().__init__() | |
| self.config = config | |
| self.d_model = config.hidden_size | |
| self.num_iterations = config.phase2_passes | |
| self.mode_embedding_refine = nn.Parameter( | |
| torch.randn(1, 1, self.d_model) * 0.02 | |
| ) | |
| # Shared bidirectional arbiter attention | |
| self.arbiter_q = nn.Linear(self.d_model, self.d_model, bias=False) | |
| self.arbiter_k = nn.Linear(self.d_model, self.d_model, bias=False) | |
| self.arbiter_v = nn.Linear(self.d_model, self.d_model, bias=False) | |
| self.arbiter_out = nn.Linear(self.d_model, self.d_model, bias=False) | |
| self.arbiter_norm = nn.LayerNorm(self.d_model) | |
| self.deduplicator = SemanticDeduplicationFilter(d_model=self.d_model, tau=config.dedup_tau) | |
| self.seam_smoother = BoundarySeamSmoother(d_model=self.d_model) | |
| self.watchdog = DirichletEnergyWatchdog() | |
| self.fwht = FastWalshHadamardTransform(block_size=config.fwht_block_size) | |
| self.spher_norm = SphericalNorm() | |
| def forward(self, H_raw: torch.Tensor) -> Tuple[torch.Tensor, Dict[str, Any]]: | |
| B, K, d = H_raw.shape | |
| H_0 = H_raw.clone() | |
| H_curr = H_raw.clone() | |
| initial_energy = self.watchdog.compute_energy(H_0) | |
| telemetry: Dict[str, Any] = { | |
| "initial_dirichlet_energy": initial_energy, | |
| "pass_telemetry": [] | |
| } | |
| for step in range(1, self.num_iterations + 1): | |
| H_cond = H_curr + self.mode_embedding_refine | |
| norm_h = self.arbiter_norm(H_cond) | |
| q = self.arbiter_q(norm_h) | |
| k = self.arbiter_k(norm_h) | |
| v = self.arbiter_v(norm_h) | |
| scores = torch.bmm(q, k.transpose(1, 2)) / math.sqrt(d) | |
| attn = F.softmax(scores, dim=-1) | |
| H_arb = H_cond + self.arbiter_out(torch.bmm(attn, v)) | |
| # Residual anchor (25% initial anchor) | |
| H_anchored = ( | |
| self.config.alpha_residual_anchor * H_0 + | |
| (1.0 - self.config.alpha_residual_anchor) * H_arb | |
| ) | |
| # Semantic deduplication | |
| H_dedup, repulsions = self.deduplicator(H_anchored) | |
| # Seam smoother | |
| H_seam = self.seam_smoother(H_dedup) | |
| # Dirichlet watchdog | |
| H_watch, current_energy, triggered = self.watchdog(H_seam, initial_energy) | |
| # FWHT INT8 normalization | |
| H_quant, mean_eta = self.fwht(H_watch) | |
| H_curr = self.spher_norm(H_quant) | |
| telemetry["pass_telemetry"].append({ | |
| "pass": step, | |
| "dirichlet_energy": current_energy, | |
| "repulsions": repulsions, | |
| "watchdog_triggered": triggered, | |
| "mean_fwht_eta": mean_eta | |
| }) | |
| return H_curr, telemetry | |
| # ============================================================================== | |
| # 6. Phase 3: Block-Causal NCM Decoder | |
| # ============================================================================== | |
| class Phase3NCMBlockDecoder(nn.Module): | |
| """ | |
| Phase 3: Synthesizes final discrete tokens for each block using 8 dynamic passes | |
| via DynamicBlueprintEvolver while carrying forward Gated DeltaNet state. | |
| """ | |
| def __init__(self, config: Hetman2Config): | |
| super().__init__() | |
| self.config = config | |
| self.d_model = config.hidden_size | |
| self.tokens_per_block = config.tokens_per_block | |
| self.num_passes = config.phase3_passes | |
| self.evolver = DynamicBlueprintEvolver( | |
| d_model=self.d_model, | |
| total_steps=self.num_passes, | |
| gamma_min=0.10, | |
| gamma_max=0.80, | |
| schedule_type="cosine" | |
| ) | |
| self.adaln = AdaLNZeroModulator(d_model=self.d_model) | |
| # DeltaNet core for token synthesis & persistent context | |
| self.deltanet_core = GatedDeltaNetLayer( | |
| d_model=self.d_model, | |
| num_heads=config.deltanet_heads, | |
| head_dim=config.deltanet_head_dim | |
| ) | |
| self.token_pos_embed = nn.Parameter( | |
| torch.randn(config.tokens_per_block, self.d_model) * 0.02 | |
| ) | |
| self.lm_head = BitLinear(self.d_model, config.vocab_size, bias=False) | |
| def decode_single_block( | |
| self, | |
| blueprint: torch.Tensor, | |
| state_prev: Optional[torch.Tensor] = None | |
| ) -> Tuple[torch.Tensor, torch.Tensor, List[Dict[str, float]]]: | |
| """ | |
| blueprint: [Batch, d_model] - Single refined block blueprint | |
| state_prev: [Batch, H, D, D] in FP32 - DeltaNet recurrent state from block k-1 | |
| Returns: | |
| token_ids: [Batch, tokens_per_block] | |
| state_next: [Batch, H, D, D] in FP32 | |
| pass_metrics: list of metrics from 8 evolver passes | |
| """ | |
| B = blueprint.size(0) | |
| L = self.tokens_per_block | |
| B_curr = blueprint | |
| B_init = blueprint | |
| # Initialize token representation candidate | |
| pos = self.token_pos_embed.unsqueeze(0).expand(B, -1, -1) # [B, L, d] | |
| H_tokens = pos + blueprint.unsqueeze(1) | |
| pass_metrics: List[Dict[str, float]] = [] | |
| # 1. Base coarse-to-fine RVQ ladder passes (Scale 1 -> Scale 2 -> Scale 3) | |
| base_passes = self.num_passes | |
| for pass_idx in range(1, base_passes + 1): | |
| g1, b1, a1, g2, b2, a2 = self.adaln(B_curr) | |
| g1 = g1.unsqueeze(1) | |
| b1 = b1.unsqueeze(1) | |
| a1 = a1.unsqueeze(1) | |
| # Modulated token representations | |
| H_norm = F.layer_norm(H_tokens, (self.d_model,)) | |
| H_mod = (1.0 + g1) * H_norm + b1 | |
| H_tokens = H_tokens + a1 * H_mod | |
| # Intent subtraction via evolver | |
| B_curr, metrics = self.evolver.step( | |
| B_curr=B_curr, | |
| B_init=B_init, | |
| H_gen=H_tokens, | |
| step_idx=pass_idx | |
| ) | |
| metrics["pass_type"] = "base" | |
| pass_metrics.append(metrics) | |
| # 2. Emergency correction passes (up to 3 refinement passes on uncertain positions) | |
| emergency_passes = 3 | |
| for em_idx in range(1, emergency_passes + 1): | |
| g1, b1, a1, g2, b2, a2 = self.adaln(B_curr) | |
| g1 = g1.unsqueeze(1) | |
| b1 = b1.unsqueeze(1) | |
| a1 = a1.unsqueeze(1) | |
| H_norm = F.layer_norm(H_tokens, (self.d_model,)) | |
| H_mod = (1.0 + g1) * H_norm + b1 | |
| H_tokens = H_tokens + 0.5 * a1 * H_mod | |
| # Emergency refinement step | |
| B_curr, metrics = self.evolver.step( | |
| B_curr=B_curr, | |
| B_init=B_init, | |
| H_gen=H_tokens, | |
| step_idx=base_passes # clamped to terminal step | |
| ) | |
| metrics["pass_type"] = f"emergency_{em_idx}" | |
| pass_metrics.append(metrics) | |
| # 3. Advance DeltaNet recurrent state across all tokens of this block in FP32 | |
| H_tokens_out, state_next = self.deltanet_core.forward_sequence(H_tokens, state_prev) | |
| # Emit discrete token IDs via LM Head | |
| logits = self.lm_head(H_tokens_out) # [B, L, vocab_size] | |
| token_ids = torch.argmax(logits, dim=-1) # [B, L] | |
| block_telemetry = { | |
| "base_passes_executed": base_passes, | |
| "emergency_passes_executed": emergency_passes, | |
| "total_passes_executed": base_passes + emergency_passes, | |
| "pass_metrics": pass_metrics, | |
| "delta_state_norm": float(torch.norm(state_next).item()) | |
| } | |
| return token_ids, state_next, block_telemetry | |
| # ============================================================================== | |
| # 7. Master Model: Hetman2ForCausalLM | |
| # ============================================================================== | |
| class Hetman2ForCausalLM(nn.Module): | |
| """ | |
| Hetman-2-NCM Master Hierarchical Generative Model. | |
| Unifies Phase 1 (Draft), Phase 2 (Refine), Phase 3 (Decode) with Gated DeltaNet state. | |
| """ | |
| def __init__(self, config: Optional[Hetman2Config] = None): | |
| super().__init__() | |
| self.config = config or Hetman2Config() | |
| self.embed_tokens = nn.Embedding(self.config.vocab_size, self.config.hidden_size) | |
| self.phase1_drafter = Phase1BlueprintDrafter(self.config) | |
| self.phase2_refiner = Phase2NCMRefiner(self.config) | |
| self.phase3_decoder = Phase3NCMBlockDecoder(self.config) | |
| # Tied weights: LM Head shares weights with embedding table | |
| self.phase3_decoder.lm_head.weight = self.embed_tokens.weight | |
| def forward( | |
| self, | |
| input_ids: torch.Tensor, | |
| num_blocks: Optional[int] = None | |
| ) -> Dict[str, Any]: | |
| """ | |
| Executes full generation forward pass (Phase 1 -> Phase 2 -> Phase 3). | |
| """ | |
| B = input_ids.size(0) | |
| K = num_blocks or self.config.max_blocks | |
| # Embed prompt tokens with OOV clamping and empty-sequence protection | |
| if input_ids.numel() == 0 or input_ids.size(1) == 0: | |
| prompt_repr = torch.zeros(B, 0, self.config.hidden_size, device=input_ids.device) | |
| else: | |
| clamped_ids = torch.clamp(input_ids, 0, self.config.vocab_size - 1) | |
| prompt_repr = self.embed_tokens(clamped_ids) | |
| # Phase 1: Draft Blueprints | |
| draft_bps = self.phase1_drafter(prompt_repr, num_blocks=K) | |
| # Phase 2: Refine Blueprints (3 passes) | |
| refined_bps, phase2_telemetry = self.phase2_refiner(draft_bps) | |
| # Phase 3: Decode Blocks (8 passes per block + DeltaNet state propagation) | |
| state: Optional[torch.Tensor] = None | |
| all_block_tokens: List[torch.Tensor] = [] | |
| phase3_telemetry: List[List[Dict[str, float]]] = [] | |
| for k in range(K): | |
| bp_k = refined_bps[:, k, :] | |
| tokens_k, state, metrics_k = self.phase3_decoder.decode_single_block(bp_k, state) | |
| all_block_tokens.append(tokens_k) | |
| phase3_telemetry.append(metrics_k) | |
| generated_tokens = torch.cat(all_block_tokens, dim=1) # [B, K * tokens_per_block] | |
| return { | |
| "generated_tokens": generated_tokens, | |
| "draft_blueprints": draft_bps, | |
| "refined_blueprints": refined_bps, | |
| "final_deltanet_state": state, | |
| "phase2_telemetry": phase2_telemetry, | |
| "phase3_telemetry": phase3_telemetry | |
| } | |
| def generate( | |
| self, | |
| prompt_token_ids: torch.Tensor, | |
| num_blocks: Optional[int] = None | |
| ) -> Dict[str, Any]: | |
| """ | |
| Public inference API matching full generation cycle. | |
| """ | |
| self.eval() | |
| with torch.no_grad(): | |
| return self.forward(prompt_token_ids, num_blocks=num_blocks) | |