kacperwikiel's picture
Publish experimental 249.9M E01 checkpoint after 50M training targets
1e9760c verified
Raw History Blame Contribute Delete
4.87 kB
"""RFC-005 decoder using Transformers' Llama attention, RoPE and generation.
The explicit model class is required when loading: ordinary Llama does not
implement this recipe's parameter-free QK normalization or two-matrix ReLU².
"""
from dataclasses import asdict
import torch
from torch import nn
from torch.nn import functional as F
from transformers import LlamaConfig, LlamaForCausalLM
from .recipe import Architecture
class EnglishBaseConfig(LlamaConfig):
model_type = 'fabryka_english_base'
def __init__(self, recipe=None, qk_norm=True, **kwargs):
architecture = Architecture(**(recipe or {}))
controlled = dict(
vocab_size=architecture.vocab_size, hidden_size=architecture.width,
intermediate_size=architecture.ffn_width, num_hidden_layers=architecture.layers,
num_attention_heads=architecture.query_heads, num_key_value_heads=architecture.kv_heads,
head_dim=architecture.head_dim, max_position_embeddings=architecture.context,
hidden_act='silu', attention_bias=False, mlp_bias=False,
attention_dropout=0.0, tie_word_embeddings=architecture.tied_embeddings,
)
for key, value in controlled.items():
if key in kwargs and kwargs[key] != value:
raise ValueError(f'{key} conflicts with recorded architecture')
kwargs[key] = value
if type(qk_norm) is not bool:
raise ValueError('qk_norm must be boolean')
super().__init__(**kwargs)
self.recipe = asdict(architecture)
self.qk_norm = qk_norm
class HeadNormalizedLinear(nn.Linear):
"""Project, then normalize each head in FP32 before RoPE; no learned gain."""
def __init__(self, source, head_dim, eps):
super().__init__(source.in_features, source.out_features, bias=False,
device=source.weight.device, dtype=source.weight.dtype)
self.weight = source.weight
self.head_dim, self.eps = head_dim, eps
def forward(self, x):
projected = F.linear(x, self.weight)
heads = projected.unflatten(-1, (-1, self.head_dim))
if hasattr(F, 'rms_norm'):
normalized = F.rms_norm(heads.float(), (self.head_dim,), eps=self.eps)
else:
# PyTorch 2.3 compatibility for the pinned DCLM evaluation harness.
values = heads.float()
normalized = values * torch.rsqrt(values.square().mean(-1, keepdim=True) + self.eps)
return normalized.to(projected.dtype).flatten(-2)
class ReluSquaredMLP(nn.Module):
def __init__(self, original):
super().__init__()
self.up_proj = original.up_proj
self.down_proj = original.down_proj
def forward(self, x):
return self.down_proj(F.relu(self.up_proj(x)).square())
class EnglishBaseForCausalLM(LlamaForCausalLM):
config_class = EnglishBaseConfig
def __init__(self, config):
if not isinstance(config, EnglishBaseConfig):
raise TypeError('EnglishBaseConfig is required; byte/Llama checkpoints are distinct')
super().__init__(config)
for block in self.model.layers:
if config.recipe['activation'] == 'relu2':
block.mlp = ReluSquaredMLP(block.mlp)
if config.qk_norm:
for name in ('q_proj', 'k_proj'):
projection = getattr(block.self_attn, name)
setattr(block.self_attn, name, HeadNormalizedLinear(
projection, config.head_dim, config.rms_norm_eps))
def parameter_report(self):
total = sum(p.numel() for p in self.parameters())
expected = Architecture(**self.config.recipe).parameter_count
if total != expected:
raise ValueError(f'Implemented parameter count {total} differs from expected {expected}')
embedding_ids = {id(self.get_input_embeddings().weight), id(self.get_output_embeddings().weight)}
non_embedding = sum(p.numel() for p in self.parameters() if id(p) not in embedding_ids)
return {'total_parameters': total, 'non_embedding_parameters': non_embedding,
'tied_embeddings': self.get_input_embeddings().weight is self.get_output_embeddings().weight,
'qk_norm': self.config.qk_norm}
def mark_tied_weights_as_initialized(self, loading_info):
"""Preserve parameter-tying missing keys for Transformers 5.3 loading.
This architecture uses parameter tying only, never shared modules.
The remote-code compatibility path in 5.3 removes initialized missing
keys before tie_weights, including the lm_head it has just marked.
Keep that key so the loader ties it to the loaded input embedding.
"""
for name in self.all_tied_weights_keys:
self.get_parameter(name)._is_hf_initialized = True