Source code for kempnerforge.model.init
"""Weight initialization strategies for KempnerForge models."""
from __future__ import annotations
import math
import torch.nn as nn
from kempnerforge.config.schema import ModelConfig
from kempnerforge.model.moe import MoEMLP
from kempnerforge.model.mot import MoTAttention, MoTBlock
from kempnerforge.model.norm import RMSNorm
from kempnerforge.model.router import SigmoidTopKRouter
_RESETS_OWN_STATE = (RMSNorm, nn.LayerNorm, SigmoidTopKRouter)
[docs]
def init_weights(model: nn.Module, config: ModelConfig) -> None:
"""Apply standard initialization to all parameters in a model.
Strategy (following GPT-2/Llama conventions):
- Linear layers: normal(0, 0.02)
- Embedding layers: normal(0, 0.02)
- Residual output projections (o_proj, down_proj): scaled by 1/sqrt(2 * n_layers)
- Cross-attention block residuals: zero-initialized (identity-at-init warm-start)
- MoT per-modality residual projections: zero-initialized (identity-at-construction)
- Norm layers and the sigmoid router's own state: reset to their
construction-time values, which a meta-device build does not preserve
Only the modules in ``_RESETS_OWN_STATE`` are reset, and only with deterministic
fills: a blanket ``reset_parameters()`` would also run ``nn.Linear``'s kaiming
init, which draws from the RNG and changes every weight drawn after it for the
same seed.
"""
std = config.init_std
residual_scale = 1.0 / math.sqrt(2.0 * config.n_layers)
for module in model.modules():
if isinstance(module, _RESETS_OWN_STATE) and not any(
t.is_meta for t in (*module.parameters(recurse=False), *module.buffers(recurse=False))
):
module.reset_parameters()
for name, param in model.named_parameters():
if param.is_meta:
continue
if param.dim() < 2:
continue
if name.startswith("cross_attention_layers.") and name.endswith(
("o_proj.weight", "down_proj.weight")
):
nn.init.zeros_(param)
elif name.endswith(("o_proj.weight", "down_proj.weight")):
nn.init.normal_(param, mean=0.0, std=std * residual_scale)
else:
nn.init.normal_(param, mean=0.0, std=std)
for module in model.modules():
if isinstance(module, MoTAttention):
for m in module.modalities:
nn.init.zeros_(module.o_proj[m].weight) # type: ignore[reportArgumentType]
elif isinstance(module, MoTBlock):
for m in module.modalities:
mlp_m = module.mlp[m]
if isinstance(mlp_m, MoEMLP):
continue
if hasattr(mlp_m, "down_proj"):
nn.init.zeros_(mlp_m.down_proj.weight) # type: ignore[union-attr]