| |
| """Utilities for full Coppola-typed pretraining in nanochat-style trainers. |
| |
| This module is intentionally trainer-agnostic. It provides: |
| |
| - basis extraction from current attention-output and MLP down-project weights |
| - typed gradient decomposition and projection |
| - a depth-aware scale policy |
| - a small controller that can refresh bases from a nanochat-style model |
| |
| The intended integration is: |
| |
| 1. instantiate a controller from the current model weights |
| 2. before each matrix optimizer step, project the raw gradient by family |
| 3. apply Muon / Newton-Schulz or another matrix optimizer |
| 4. re-project the transformed gradient before the final step |
| """ |
|
|
| from __future__ import annotations |
|
|
| from dataclasses import dataclass, field |
| from typing import Callable, Dict, List, Optional, Sequence, Tuple |
|
|
| import torch |
|
|
|
|
| Tensor = torch.Tensor |
|
|
|
|
| @dataclass(frozen=True) |
| class RankPolicy: |
| """Energy-based rank truncation policy.""" |
|
|
| energy: float = 0.99 |
| max_rank: Optional[int] = None |
| min_rank: int = 1 |
|
|
|
|
| @dataclass(frozen=True) |
| class FamilyScales: |
| """Typed gradient multipliers for a layer.""" |
|
|
| routing: float = 1.0 |
| and_transport: float = 1.0 |
| or_update: float = 1.0 |
| remainder: float = 1.0 |
| coupling: Optional[float] = None |
|
|
| def coupling_scale(self) -> float: |
| if self.coupling is not None: |
| return self.coupling |
| return 0.5 * (self.and_transport + self.or_update) |
|
|
|
|
| @dataclass |
| class LayerBases: |
| """Typed bases for one transformer layer.""" |
|
|
| routing_head_bases: List[Tensor] = field(default_factory=list) |
| q_head_bases: List[Tensor] = field(default_factory=list) |
| k_head_bases: List[Tensor] = field(default_factory=list) |
| and_output_basis: Optional[Tensor] = None |
| or_input_basis: Optional[Tensor] = None |
|
|
|
|
| @dataclass |
| class ComponentNorms: |
| """Norms of the typed gradient components before scaling.""" |
|
|
| routing: float = 0.0 |
| and_transport: float = 0.0 |
| or_update: float = 0.0 |
| coupling: float = 0.0 |
| remainder: float = 0.0 |
|
|
|
|
| def _select_rank(singular_values: Tensor, policy: RankPolicy) -> int: |
| if singular_values.numel() == 0: |
| return policy.min_rank |
| total = float(singular_values.square().sum().item()) |
| if total <= 0.0: |
| return policy.min_rank |
| cumulative = singular_values.square().cumsum(0) / total |
| rank = int(torch.searchsorted(cumulative, torch.tensor(policy.energy, device=cumulative.device)).item()) + 1 |
| rank = max(policy.min_rank, rank) |
| if policy.max_rank is not None: |
| rank = min(rank, policy.max_rank) |
| return min(rank, singular_values.numel()) |
|
|
|
|
| def _orthonormal_rows(basis: Tensor) -> Tensor: |
| """Return an orthonormal row basis spanning the same row space.""" |
| if basis.ndim != 2: |
| raise ValueError(f"basis must be rank-2, got {tuple(basis.shape)}") |
| if basis.shape[0] == 0: |
| return basis |
| q, _ = torch.linalg.qr(basis.T, mode="reduced") |
| return q.T.contiguous() |
|
|
|
|
| def _project_left(grad: Tensor, basis: Tensor) -> Tensor: |
| basis = basis.to(device=grad.device, dtype=grad.dtype) |
| proj = basis.T @ basis |
| return proj @ grad |
|
|
|
|
| def _project_right(grad: Tensor, basis: Tensor) -> Tensor: |
| basis = basis.to(device=grad.device, dtype=grad.dtype) |
| proj = basis.T @ basis |
| return grad @ proj |
|
|
|
|
| def _zero_like(grad: Tensor) -> Tensor: |
| return torch.zeros_like(grad) |
|
|
|
|
| def compute_attention_output_bases( |
| weight: Tensor, |
| n_head: int, |
| policy: RankPolicy = RankPolicy(), |
| ) -> List[Tensor]: |
| """Build one output-space basis per attention head block. |
| |
| Args: |
| weight: output projection weight of shape [hidden, hidden] |
| n_head: number of attention heads |
| policy: rank selection policy inside each head block |
| """ |
| if weight.ndim != 2: |
| raise ValueError(f"attention output weight must be rank-2, got {tuple(weight.shape)}") |
| hidden, width = weight.shape |
| if width % n_head != 0: |
| raise ValueError(f"weight width {width} not divisible by n_head={n_head}") |
| head_dim = width // n_head |
| bases: List[Tensor] = [] |
| with torch.no_grad(): |
| w = weight.detach().float().cpu() |
| for h in range(n_head): |
| cols = w[:, h * head_dim:(h + 1) * head_dim] |
| u, s, _ = torch.linalg.svd(cols, full_matrices=False) |
| rank = _select_rank(s, policy) |
| basis = u[:, :rank].T.contiguous() |
| bases.append(_orthonormal_rows(basis)) |
| return bases |
|
|
|
|
| def compute_attention_score_bases( |
| weight: Tensor, |
| n_head: int, |
| policy: RankPolicy = RankPolicy(), |
| ) -> List[Tensor]: |
| """Build one row-space basis per attention head for Q/K score-side blocks.""" |
| if weight.ndim != 2: |
| raise ValueError(f"attention score weight must be rank-2, got {tuple(weight.shape)}") |
| width, hidden = weight.shape |
| if width % n_head != 0: |
| raise ValueError(f"weight height {width} not divisible by n_head={n_head}") |
| head_dim = width // n_head |
| bases: List[Tensor] = [] |
| with torch.no_grad(): |
| w = weight.detach().float().cpu() |
| for h in range(n_head): |
| rows = w[h * head_dim:(h + 1) * head_dim, :] |
| u, s, _ = torch.linalg.svd(rows, full_matrices=False) |
| rank = _select_rank(s, policy) |
| basis = u[:, :rank].T.contiguous() |
| bases.append(_orthonormal_rows(basis)) |
| return bases |
|
|
|
|
| def compute_down_proj_bases( |
| weight: Tensor, |
| output_policy: RankPolicy = RankPolicy(), |
| input_policy: RankPolicy = RankPolicy(), |
| ) -> Tuple[Tensor, Tensor]: |
| """Build output-side AND basis and input-side OR basis for down_proj.""" |
| if weight.ndim != 2: |
| raise ValueError(f"down projection weight must be rank-2, got {tuple(weight.shape)}") |
| with torch.no_grad(): |
| w = weight.detach().float().cpu() |
| u, s, vh = torch.linalg.svd(w, full_matrices=False) |
| out_rank = _select_rank(s, output_policy) |
| in_rank = _select_rank(s, input_policy) |
| and_basis = _orthonormal_rows(u[:, :out_rank].T.contiguous()) |
| or_basis = _orthonormal_rows(vh[:in_rank, :].contiguous()) |
| return and_basis, or_basis |
|
|
|
|
| def decompose_down_proj_gradient( |
| grad: Tensor, |
| and_output_basis: Optional[Tensor], |
| or_input_basis: Optional[Tensor], |
| ) -> Dict[str, Tensor]: |
| """Decompose an MLP down_proj gradient into typed components. |
| |
| The components are: |
| - and_transport_only |
| - or_update_only |
| - coupling: in both the output and input typed subspaces |
| - remainder |
| """ |
| if grad.ndim != 2: |
| raise ValueError(f"gradient must be rank-2, got {tuple(grad.shape)}") |
|
|
| g_and = _zero_like(grad) |
| g_or = _zero_like(grad) |
| g_both = _zero_like(grad) |
|
|
| if and_output_basis is not None: |
| g_and = _project_left(grad, and_output_basis) |
| if or_input_basis is not None: |
| g_or = _project_right(grad, or_input_basis) |
| if and_output_basis is not None and or_input_basis is not None: |
| g_both = _project_right(g_and, or_input_basis) |
|
|
| g_and_only = g_and - g_both |
| g_or_only = g_or - g_both |
| g_remainder = grad - g_and_only - g_or_only - g_both |
|
|
| return { |
| "and_transport_only": g_and_only, |
| "or_update_only": g_or_only, |
| "coupling": g_both, |
| "remainder": g_remainder, |
| } |
|
|
|
|
| def project_down_proj_gradient( |
| grad: Tensor, |
| and_output_basis: Optional[Tensor], |
| or_input_basis: Optional[Tensor], |
| scales: FamilyScales, |
| ) -> Tensor: |
| parts = decompose_down_proj_gradient(grad, and_output_basis, or_input_basis) |
| return ( |
| scales.and_transport * parts["and_transport_only"] |
| + scales.or_update * parts["or_update_only"] |
| + scales.coupling_scale() * parts["coupling"] |
| + scales.remainder * parts["remainder"] |
| ) |
|
|
|
|
| def support_project_down_proj_gradient( |
| grad: Tensor, |
| and_output_basis: Optional[Tensor], |
| or_input_basis: Optional[Tensor], |
| ) -> Tensor: |
| """Project a down-proj update onto typed Coppola support, dropping remainder.""" |
| parts = decompose_down_proj_gradient(grad, and_output_basis, or_input_basis) |
| return parts["and_transport_only"] + parts["or_update_only"] + parts["coupling"] |
|
|
|
|
| def down_proj_component_norms( |
| grad: Tensor, |
| and_output_basis: Optional[Tensor], |
| or_input_basis: Optional[Tensor], |
| ) -> ComponentNorms: |
| parts = decompose_down_proj_gradient(grad, and_output_basis, or_input_basis) |
| return ComponentNorms( |
| and_transport=float(parts["and_transport_only"].norm().item()), |
| or_update=float(parts["or_update_only"].norm().item()), |
| coupling=float(parts["coupling"].norm().item()), |
| remainder=float(parts["remainder"].norm().item()), |
| ) |
|
|
|
|
| def project_attention_output_gradient( |
| grad: Tensor, |
| routing_head_bases: Sequence[Tensor], |
| scales: FamilyScales, |
| ) -> Tensor: |
| """Project an attention output gradient head-by-head on the left.""" |
| if grad.ndim != 2: |
| raise ValueError(f"gradient must be rank-2, got {tuple(grad.shape)}") |
| if not routing_head_bases: |
| return grad |
| hidden, width = grad.shape |
| if width % len(routing_head_bases) != 0: |
| raise ValueError(f"gradient width {width} incompatible with {len(routing_head_bases)} head bases") |
| head_dim = width // len(routing_head_bases) |
| out = torch.empty_like(grad) |
| for h, basis in enumerate(routing_head_bases): |
| cols = grad[:, h * head_dim:(h + 1) * head_dim] |
| routed = _project_left(cols, basis) |
| remainder = cols - routed |
| out[:, h * head_dim:(h + 1) * head_dim] = scales.routing * routed + scales.remainder * remainder |
| return out |
|
|
|
|
| def project_attention_qk_gradient( |
| grad: Tensor, |
| q_head_bases: Sequence[Tensor], |
| k_head_bases: Sequence[Tensor], |
| scales: FamilyScales, |
| ) -> Tensor: |
| """Project fused Q/K/V gradient on Q and K row blocks only, leaving V unchanged.""" |
| if grad.ndim != 2: |
| raise ValueError(f"gradient must be rank-2, got {tuple(grad.shape)}") |
| if not q_head_bases and not k_head_bases: |
| return grad |
| hidden3, width = grad.shape |
| if hidden3 % 3 != 0: |
| raise ValueError(f"expected fused qkv height divisible by 3, got {hidden3}") |
| hidden = hidden3 // 3 |
| if q_head_bases and hidden % len(q_head_bases) != 0: |
| raise ValueError(f"q block height {hidden} incompatible with {len(q_head_bases)} q bases") |
| if k_head_bases and hidden % len(k_head_bases) != 0: |
| raise ValueError(f"k block height {hidden} incompatible with {len(k_head_bases)} k bases") |
| out = grad.clone() |
| if q_head_bases: |
| head_dim = hidden // len(q_head_bases) |
| for h, basis in enumerate(q_head_bases): |
| rows = grad[h * head_dim:(h + 1) * head_dim, :] |
| routed = _project_left(rows, basis) |
| remainder = rows - routed |
| out[h * head_dim:(h + 1) * head_dim, :] = scales.routing * routed + scales.remainder * remainder |
| if k_head_bases: |
| head_dim = hidden // len(k_head_bases) |
| offset = hidden |
| for h, basis in enumerate(k_head_bases): |
| start = offset + h * head_dim |
| stop = offset + (h + 1) * head_dim |
| rows = grad[start:stop, :] |
| routed = _project_left(rows, basis) |
| remainder = rows - routed |
| out[start:stop, :] = scales.routing * routed + scales.remainder * remainder |
| return out |
|
|
|
|
| def support_project_attention_output_gradient( |
| grad: Tensor, |
| routing_head_bases: Sequence[Tensor], |
| ) -> Tensor: |
| """Project an attention-output update onto routing support, dropping remainder.""" |
| if grad.ndim != 2: |
| raise ValueError(f"gradient must be rank-2, got {tuple(grad.shape)}") |
| if not routing_head_bases: |
| return grad |
| hidden, width = grad.shape |
| if width % len(routing_head_bases) != 0: |
| raise ValueError(f"gradient width {width} incompatible with {len(routing_head_bases)} head bases") |
| head_dim = width // len(routing_head_bases) |
| out = torch.empty_like(grad) |
| for h, basis in enumerate(routing_head_bases): |
| cols = grad[:, h * head_dim:(h + 1) * head_dim] |
| out[:, h * head_dim:(h + 1) * head_dim] = _project_left(cols, basis) |
| return out |
|
|
|
|
| def support_project_attention_qk_gradient( |
| grad: Tensor, |
| q_head_bases: Sequence[Tensor], |
| k_head_bases: Sequence[Tensor], |
| ) -> Tensor: |
| """Project fused Q/K/V update on Q and K support only, leaving V unchanged.""" |
| if grad.ndim != 2: |
| raise ValueError(f"gradient must be rank-2, got {tuple(grad.shape)}") |
| if not q_head_bases and not k_head_bases: |
| return grad |
| hidden3, width = grad.shape |
| if hidden3 % 3 != 0: |
| raise ValueError(f"expected fused qkv height divisible by 3, got {hidden3}") |
| hidden = hidden3 // 3 |
| out = grad.clone() |
| if q_head_bases: |
| head_dim = hidden // len(q_head_bases) |
| for h, basis in enumerate(q_head_bases): |
| rows = grad[h * head_dim:(h + 1) * head_dim, :] |
| out[h * head_dim:(h + 1) * head_dim, :] = _project_left(rows, basis) |
| if k_head_bases: |
| head_dim = hidden // len(k_head_bases) |
| offset = hidden |
| for h, basis in enumerate(k_head_bases): |
| start = offset + h * head_dim |
| stop = offset + (h + 1) * head_dim |
| rows = grad[start:stop, :] |
| out[start:stop, :] = _project_left(rows, basis) |
| return out |
|
|
|
|
| def attention_component_norms(grad: Tensor, routing_head_bases: Sequence[Tensor]) -> ComponentNorms: |
| if not routing_head_bases: |
| return ComponentNorms(routing=float(grad.norm().item())) |
| hidden, width = grad.shape |
| head_dim = width // len(routing_head_bases) |
| routed_norm_sq = 0.0 |
| remainder_norm_sq = 0.0 |
| for h, basis in enumerate(routing_head_bases): |
| cols = grad[:, h * head_dim:(h + 1) * head_dim] |
| routed = _project_left(cols, basis) |
| remainder = cols - routed |
| routed_norm_sq += float(routed.square().sum().item()) |
| remainder_norm_sq += float(remainder.square().sum().item()) |
| return ComponentNorms( |
| routing=routed_norm_sq**0.5, |
| remainder=remainder_norm_sq**0.5, |
| ) |
|
|
|
|
| def attention_qk_component_norms( |
| grad: Tensor, |
| q_head_bases: Sequence[Tensor], |
| k_head_bases: Sequence[Tensor], |
| ) -> ComponentNorms: |
| if not q_head_bases and not k_head_bases: |
| return ComponentNorms(routing=float(grad.norm().item())) |
| hidden3, width = grad.shape |
| if hidden3 % 3 != 0: |
| raise ValueError(f"expected fused qkv height divisible by 3, got {hidden3}") |
| hidden = hidden3 // 3 |
| routed_norm_sq = 0.0 |
| remainder_norm_sq = 0.0 |
| if q_head_bases: |
| head_dim = hidden // len(q_head_bases) |
| for h, basis in enumerate(q_head_bases): |
| rows = grad[h * head_dim:(h + 1) * head_dim, :] |
| routed = _project_left(rows, basis) |
| remainder = rows - routed |
| routed_norm_sq += float(routed.square().sum().item()) |
| remainder_norm_sq += float(remainder.square().sum().item()) |
| if k_head_bases: |
| head_dim = hidden // len(k_head_bases) |
| offset = hidden |
| for h, basis in enumerate(k_head_bases): |
| start = offset + h * head_dim |
| stop = offset + (h + 1) * head_dim |
| rows = grad[start:stop, :] |
| routed = _project_left(rows, basis) |
| remainder = rows - routed |
| routed_norm_sq += float(routed.square().sum().item()) |
| remainder_norm_sq += float(remainder.square().sum().item()) |
| return ComponentNorms( |
| routing=routed_norm_sq**0.5, |
| remainder=remainder_norm_sq**0.5, |
| ) |
|
|
|
|
| def default_zone_scales(layer_idx: int, n_layer: int) -> FamilyScales: |
| """Default full-Coppola depth policy for from-scratch pretraining.""" |
| frac = layer_idx / max(n_layer - 1, 1) |
| if frac < 0.20: |
| return FamilyScales(routing=1.40, and_transport=1.10, or_update=0.70, remainder=0.0, coupling=0.90) |
| if frac < 0.65: |
| return FamilyScales(routing=0.90, and_transport=1.40, or_update=0.90, remainder=0.0, coupling=1.15) |
| if frac < 0.90: |
| return FamilyScales(routing=0.75, and_transport=0.95, or_update=1.40, remainder=0.0, coupling=1.15) |
| return FamilyScales(routing=0.60, and_transport=0.85, or_update=1.15, remainder=0.0, coupling=1.00) |
|
|
|
|
| @dataclass |
| class CoppolaPretrainingConfig: |
| n_head: int |
| attn_output_policy: RankPolicy = field(default_factory=RankPolicy) |
| attn_qk_policy: RankPolicy = field(default_factory=RankPolicy) |
| mlp_output_policy: RankPolicy = field(default_factory=RankPolicy) |
| mlp_input_policy: RankPolicy = field(default_factory=RankPolicy) |
| basis_update_interval: int = 250 |
| uniform_scales: FamilyScales = field(default_factory=FamilyScales) |
| scale_fn: Callable[[int, int], FamilyScales] = default_zone_scales |
|
|
|
|
| class CoppolaPretrainingController: |
| """Refreshes typed bases and projects gradients for nanochat-style models.""" |
|
|
| def __init__(self, config: CoppolaPretrainingConfig): |
| self.config = config |
| self.layer_bases: Dict[int, LayerBases] = {} |
|
|
| def refresh_from_model(self, model) -> None: |
| layers = self._resolve_layers(model) |
| n_layer = len(layers) |
| bases: Dict[int, LayerBases] = {} |
| for layer_idx, block in enumerate(layers): |
| attn_weight = self._resolve_attn_out_weight(block) |
| q_weight, k_weight = self._resolve_attn_qk_weights(block) |
| mlp_weight = self._resolve_mlp_down_weight(block) |
| routing = compute_attention_output_bases( |
| attn_weight, self.config.n_head, self.config.attn_output_policy |
| ) |
| q_bases: List[Tensor] = [] |
| k_bases: List[Tensor] = [] |
| if q_weight is not None and k_weight is not None: |
| q_bases = compute_attention_score_bases( |
| q_weight, self.config.n_head, self.config.attn_qk_policy |
| ) |
| k_bases = compute_attention_score_bases( |
| k_weight, self.config.n_head, self.config.attn_qk_policy |
| ) |
| and_basis, or_basis = compute_down_proj_bases( |
| mlp_weight, |
| output_policy=self.config.mlp_output_policy, |
| input_policy=self.config.mlp_input_policy, |
| ) |
| bases[layer_idx] = LayerBases( |
| routing_head_bases=routing, |
| q_head_bases=q_bases, |
| k_head_bases=k_bases, |
| and_output_basis=and_basis, |
| or_input_basis=or_basis, |
| ) |
| self.layer_bases = bases |
| self._n_layer = n_layer |
|
|
| def scales_for_layer(self, layer_idx: int, mode: str = "zoned") -> FamilyScales: |
| if mode == "uniform": |
| return self.config.uniform_scales |
| if not hasattr(self, "_n_layer"): |
| raise RuntimeError("refresh_from_model() must be called before requesting zoned scales") |
| return self.config.scale_fn(layer_idx, self._n_layer) |
|
|
| def project_attn_out_grad(self, layer_idx: int, grad: Tensor, mode: str = "zoned") -> Tensor: |
| bases = self.layer_bases[layer_idx] |
| scales = self.scales_for_layer(layer_idx, mode=mode) |
| return project_attention_output_gradient(grad, bases.routing_head_bases, scales) |
|
|
| def project_attn_qk_grad(self, layer_idx: int, grad: Tensor, mode: str = "zoned") -> Tensor: |
| bases = self.layer_bases[layer_idx] |
| scales = self.scales_for_layer(layer_idx, mode=mode) |
| return project_attention_qk_gradient(grad, bases.q_head_bases, bases.k_head_bases, scales) |
|
|
| def project_mlp_down_grad(self, layer_idx: int, grad: Tensor, mode: str = "zoned") -> Tensor: |
| bases = self.layer_bases[layer_idx] |
| scales = self.scales_for_layer(layer_idx, mode=mode) |
| return project_down_proj_gradient(grad, bases.and_output_basis, bases.or_input_basis, scales) |
|
|
| def project_attn_out_support(self, layer_idx: int, grad: Tensor) -> Tensor: |
| bases = self.layer_bases[layer_idx] |
| return support_project_attention_output_gradient(grad, bases.routing_head_bases) |
|
|
| def project_attn_qk_support(self, layer_idx: int, grad: Tensor) -> Tensor: |
| bases = self.layer_bases[layer_idx] |
| return support_project_attention_qk_gradient(grad, bases.q_head_bases, bases.k_head_bases) |
|
|
| def project_mlp_down_support(self, layer_idx: int, grad: Tensor) -> Tensor: |
| bases = self.layer_bases[layer_idx] |
| return support_project_down_proj_gradient(grad, bases.and_output_basis, bases.or_input_basis) |
|
|
| def project_after_matrix_transform( |
| self, |
| layer_idx: int, |
| grad: Tensor, |
| param_kind: str, |
| transform: Callable[[Tensor], Tensor], |
| mode: str = "zoned", |
| ) -> Tensor: |
| """Project -> transform -> re-project for Muon-style optimizers.""" |
| g = self.project_gradient(layer_idx, grad, param_kind=param_kind, mode=mode) |
| g = transform(g) |
| return self.project_gradient(layer_idx, g, param_kind=param_kind, mode=mode) |
|
|
| def project_gradient(self, layer_idx: int, grad: Tensor, param_kind: str, mode: str = "zoned") -> Tensor: |
| if param_kind == "attn_out": |
| return self.project_attn_out_grad(layer_idx, grad, mode=mode) |
| if param_kind == "attn_qk": |
| return self.project_attn_qk_grad(layer_idx, grad, mode=mode) |
| if param_kind == "mlp_down": |
| return self.project_mlp_down_grad(layer_idx, grad, mode=mode) |
| raise ValueError(f"unknown param_kind={param_kind!r}") |
|
|
| def project_update_support(self, layer_idx: int, grad: Tensor, param_kind: str) -> Tensor: |
| if param_kind == "attn_out": |
| return self.project_attn_out_support(layer_idx, grad) |
| if param_kind == "attn_qk": |
| return self.project_attn_qk_support(layer_idx, grad) |
| if param_kind == "mlp_down": |
| return self.project_mlp_down_support(layer_idx, grad) |
| raise ValueError(f"unknown param_kind={param_kind!r}") |
|
|
| def gradient_component_norms(self, layer_idx: int, grad: Tensor, param_kind: str) -> ComponentNorms: |
| bases = self.layer_bases[layer_idx] |
| if param_kind == "attn_out": |
| return attention_component_norms(grad, bases.routing_head_bases) |
| if param_kind == "attn_qk": |
| return attention_qk_component_norms(grad, bases.q_head_bases, bases.k_head_bases) |
| if param_kind == "mlp_down": |
| return down_proj_component_norms(grad, bases.and_output_basis, bases.or_input_basis) |
| raise ValueError(f"unknown param_kind={param_kind!r}") |
|
|
| @staticmethod |
| def _resolve_layers(model): |
| if hasattr(model, "transformer") and hasattr(model.transformer, "h"): |
| return list(model.transformer.h) |
| if hasattr(model, "model") and hasattr(model.model, "layers"): |
| return list(model.model.layers) |
| raise ValueError("could not resolve transformer layers from model") |
|
|
| @staticmethod |
| def _resolve_attn_out_weight(block) -> Tensor: |
| if hasattr(block, "attn") and hasattr(block.attn, "c_proj"): |
| return block.attn.c_proj.weight |
| if hasattr(block, "self_attn") and hasattr(block.self_attn, "o_proj"): |
| return block.self_attn.o_proj.weight |
| raise ValueError("could not resolve attention output weight") |
|
|
| @staticmethod |
| def _resolve_attn_qk_weights(block) -> Tuple[Optional[Tensor], Optional[Tensor]]: |
| if hasattr(block, "attn") and hasattr(block.attn, "c_attn"): |
| weight = block.attn.c_attn.weight |
| if weight.shape[0] % 3 != 0: |
| raise ValueError(f"expected fused qkv height divisible by 3, got {weight.shape[0]}") |
| hidden = weight.shape[0] // 3 |
| return weight[:hidden, :], weight[hidden:2 * hidden, :] |
| if hasattr(block, "self_attn") and hasattr(block.self_attn, "q_proj") and hasattr(block.self_attn, "k_proj"): |
| return block.self_attn.q_proj.weight, block.self_attn.k_proj.weight |
| return None, None |
|
|
| @staticmethod |
| def _resolve_mlp_down_weight(block) -> Tensor: |
| if hasattr(block, "mlp") and hasattr(block.mlp, "c_proj"): |
| return block.mlp.c_proj.weight |
| if hasattr(block, "mlp") and hasattr(block.mlp, "down_proj"): |
| return block.mlp.down_proj.weight |
| raise ValueError("could not resolve MLP down projection weight") |
|
|