| import math |
| import os |
| import re |
| from dataclasses import dataclass |
| from typing import Optional, Tuple, Any, Dict |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
| from transformers import PreTrainedModel |
| from transformers.modeling_outputs import CausalLMOutput |
|
|
| from huggingface_hub import hf_hub_download |
|
|
| from safetensors.torch import safe_open |
|
|
| from .configuration_binaryllm import BinaryLLMConfig |
|
|
|
|
| |
| |
| |
|
|
| def split_u16_to_bytes(u16: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: |
| hi = (u16 >> 8) & 0xFF |
| lo = u16 & 0xFF |
| return hi.long(), lo.long() |
|
|
|
|
| def factorized_ce_u16( |
| logits_hi: torch.Tensor, |
| logits_lo: torch.Tensor, |
| target_u16: torch.Tensor, |
| ignore_index: int = -100, |
| ) -> torch.Tensor: |
| y = target_u16 |
| y_safe = torch.clamp(y, min=0) |
| y_hi, y_lo = split_u16_to_bytes(y_safe) |
|
|
| y_hi[y == ignore_index] = ignore_index |
| y_lo[y == ignore_index] = ignore_index |
|
|
| B, T, V = logits_hi.shape |
| l1 = F.cross_entropy(logits_hi.view(B * T, V), y_hi.view(B * T), ignore_index=ignore_index) |
| l2 = F.cross_entropy(logits_lo.view(B * T, V), y_lo.view(B * T), ignore_index=ignore_index) |
| return l1 + l2 |
|
|
|
|
| |
| |
| |
|
|
| class PositionalEncoding(nn.Module): |
| def __init__(self, d_model: int, max_len: int) -> None: |
| super().__init__() |
| pe = torch.zeros(max_len, d_model, dtype=torch.float32) |
| position = torch.arange(0, max_len, dtype=torch.float32).unsqueeze(1) |
| div_term = torch.exp( |
| torch.arange(0, d_model, 2, dtype=torch.float32) * (-torch.log(torch.tensor(10000.0)) / d_model) |
| ) |
| pe[:, 0::2] = torch.sin(position * div_term) |
| pe[:, 1::2] = torch.cos(position * div_term) |
| pe = pe.unsqueeze(0) |
| self.register_buffer("pe", pe, persistent=False) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| t = x.size(1) |
| pe = self.pe[:, :t, :].to(device=x.device, dtype=x.dtype) |
| return x + pe |
|
|
|
|
| |
| |
| |
|
|
| class FactorizedU16Head(nn.Module): |
| def __init__(self, d_model: int, byte_emb_dim: int = 64) -> None: |
| super().__init__() |
| self.proj_hi = nn.Linear(d_model, 256) |
| self.hi_emb = nn.Embedding(256, byte_emb_dim) |
| self.proj_lo = nn.Linear(d_model + byte_emb_dim, 256) |
|
|
| def forward(self, h: torch.Tensor, hi_cond: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: |
| logits_hi = self.proj_hi(h) |
| cond = torch.cat([h, self.hi_emb(hi_cond)], dim=-1) |
| logits_lo = self.proj_lo(cond) |
| return logits_hi, logits_lo |
|
|
|
|
| |
| |
| |
|
|
| @dataclass |
| class _InnerCfg: |
| block_size: int |
| embed_dim: int |
| vocab_size: int |
| num_heads: int |
| num_layers: int |
| ff_hidden_dim: int |
| dropout: float |
| ignore_index: int = -100 |
| byte_emb_dim: int = 64 |
| layernorm_dim: Optional[int] = None |
| head_dim: Optional[int] = None |
|
|
|
|
| |
| |
| |
|
|
| class TinyTransformerLM(nn.Module): |
| def __init__(self, cfg: _InnerCfg) -> None: |
| super().__init__() |
| self.cfg = cfg |
| self.vocab_size = int(cfg.vocab_size) |
| self.ignore_index = int(cfg.ignore_index) |
|
|
| self.tok_embed = nn.Embedding(self.vocab_size, cfg.embed_dim) |
| self.pos_encoding = PositionalEncoding(cfg.embed_dim, cfg.block_size) |
|
|
| encoder_layer = nn.TransformerEncoderLayer( |
| d_model=cfg.embed_dim, |
| nhead=cfg.num_heads, |
| dim_feedforward=cfg.ff_hidden_dim, |
| dropout=cfg.dropout, |
| activation="gelu", |
| batch_first=True, |
| ) |
| self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=cfg.num_layers) |
|
|
| ln_dim = cfg.layernorm_dim or cfg.embed_dim |
| head_dim = cfg.head_dim or ln_dim |
|
|
| self.pre_ln_proj: Optional[nn.Linear] = None |
| if ln_dim != cfg.embed_dim: |
| self.pre_ln_proj = nn.Linear(cfg.embed_dim, ln_dim) |
|
|
| self.ln = nn.LayerNorm(ln_dim) |
|
|
| self.head_pre: Optional[nn.Linear] = None |
| if head_dim != ln_dim: |
| self.head_pre = nn.Linear(ln_dim, head_dim) |
|
|
| self.head = FactorizedU16Head(head_dim, byte_emb_dim=int(cfg.byte_emb_dim)) |
|
|
| causal = torch.triu(torch.ones(cfg.block_size, cfg.block_size, dtype=torch.bool), diagonal=1) |
| self.register_buffer("causal_mask", causal, persistent=False) |
|
|
| def forward( |
| self, |
| tokens: torch.Tensor, |
| padding_mask: Optional[torch.Tensor] = None, |
| labels: Optional[torch.Tensor] = None, |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
| x = self.tok_embed(tokens) |
| x = self.pos_encoding(x) |
|
|
| seq_len = tokens.size(1) |
| attn_mask = self.causal_mask[:seq_len, :seq_len].to(device=tokens.device) |
|
|
| if padding_mask is not None: |
| padding_mask = padding_mask[:, :seq_len].to(device=tokens.device, dtype=torch.bool) |
|
|
| x = self.encoder(x, mask=attn_mask, src_key_padding_mask=padding_mask) |
|
|
| if self.pre_ln_proj is not None: |
| x = self.pre_ln_proj(x) |
|
|
| x = self.ln(x) |
|
|
| if self.head_pre is not None: |
| x = self.head_pre(x) |
|
|
| |
| logits_hi = self.head.proj_hi(x) |
|
|
| |
| |
| |
| if labels is not None: |
| hi_cond, _ = split_u16_to_bytes(labels) |
| else: |
| hi_cond = torch.argmax(logits_hi, dim=-1).long() |
|
|
| cond = torch.cat([x, self.head.hi_emb(hi_cond)], dim=-1) |
| logits_lo = self.head.proj_lo(cond) |
| return logits_hi, logits_lo |
|
|
| def compute_loss( |
| self, |
| outputs: Tuple[torch.Tensor, torch.Tensor], |
| targets: torch.Tensor, |
| padding_mask: Optional[torch.Tensor] = None, |
| ) -> torch.Tensor: |
| if padding_mask is not None: |
| t = targets.clone() |
| t[padding_mask] = self.ignore_index |
| else: |
| t = targets |
| logits_hi, logits_lo = outputs |
| return factorized_ce_u16(logits_hi, logits_lo, t, ignore_index=self.ignore_index) |
|
|
|
|
| |
| |
| |
|
|
| def _infer_arch_from_safetensors(path: str) -> Dict[str, int]: |
| info: Dict[str, int] = {} |
|
|
| with safe_open(path, framework="pt", device="cpu") as f: |
| |
| w = f.get_tensor("model.tok_embed.weight") |
| info["vocab_size"] = int(w.shape[0]) |
| info["hidden_size"] = int(w.shape[1]) |
|
|
| |
| layer_ids = [] |
| rx = re.compile(r"^model\.encoder\.layers\.(\d+)\.") |
| for k in f.keys(): |
| m = rx.match(k) |
| if m: |
| layer_ids.append(int(m.group(1))) |
| info["num_hidden_layers"] = (max(layer_ids) + 1) if layer_ids else 0 |
|
|
| |
| k_lin1 = "model.encoder.layers.0.linear1.weight" |
| if k_lin1 in f.keys(): |
| info["intermediate_size"] = int(f.get_tensor(k_lin1).shape[0]) |
|
|
| |
| k_hi = "model.head.hi_emb.weight" |
| if k_hi in f.keys(): |
| info["byte_emb_dim"] = int(f.get_tensor(k_hi).shape[1]) |
|
|
| return info |
|
|
|
|
| |
| |
| |
|
|
| class BinaryLLMForCausalLM(PreTrainedModel): |
| config_class = BinaryLLMConfig |
| main_input_name = "input_ids" |
|
|
| @classmethod |
| def from_pretrained(cls, pretrained_model_name_or_path: str, *model_args, **kwargs): |
| |
| config = kwargs.get("config", None) |
| if config is None: |
| config = BinaryLLMConfig.from_pretrained(pretrained_model_name_or_path, **{k: v for k, v in kwargs.items() if k in ["cache_dir", "revision", "token"]}) |
| kwargs["config"] = config |
|
|
| |
| cache_dir = kwargs.get("cache_dir", None) |
| revision = kwargs.get("revision", None) |
| token = kwargs.get("token", None) |
|
|
| try: |
| st_path = hf_hub_download( |
| repo_id=pretrained_model_name_or_path, |
| filename="model.safetensors", |
| revision=revision, |
| token=token, |
| cache_dir=cache_dir, |
| ) |
| except Exception: |
| |
| local = os.path.join(str(pretrained_model_name_or_path), "model.safetensors") |
| st_path = local |
|
|
| arch = _infer_arch_from_safetensors(st_path) |
|
|
| |
| if "vocab_size" in arch: |
| config.vocab_size = int(arch["vocab_size"]) |
| if "hidden_size" in arch: |
| config.hidden_size = int(arch["hidden_size"]) |
| if "num_hidden_layers" in arch and int(arch["num_hidden_layers"]) > 0: |
| config.num_hidden_layers = int(arch["num_hidden_layers"]) |
| if "intermediate_size" in arch: |
| config.intermediate_size = int(arch["intermediate_size"]) |
| |
| if "byte_emb_dim" in arch: |
| setattr(config, "byte_emb_dim", int(arch["byte_emb_dim"])) |
|
|
| kwargs["config"] = config |
| return super().from_pretrained(pretrained_model_name_or_path, *model_args, **kwargs) |
|
|
| def __init__(self, config: BinaryLLMConfig): |
| super().__init__(config) |
|
|
| byte_emb_dim = int(getattr(config, "byte_emb_dim", 64)) |
|
|
| inner = _InnerCfg( |
| block_size=int(config.max_position_embeddings), |
| embed_dim=int(config.hidden_size), |
| vocab_size=int(config.vocab_size), |
| num_heads=int(config.num_attention_heads), |
| num_layers=int(config.num_hidden_layers), |
| ff_hidden_dim=int(config.intermediate_size), |
| dropout=float(getattr(config, "dropout", 0.0)), |
| ignore_index=int(getattr(config, "ignore_index", -100)), |
| byte_emb_dim=int(byte_emb_dim), |
| layernorm_dim=None, |
| head_dim=None, |
| ) |
| self.model = TinyTransformerLM(inner) |
|
|
| self.post_init() |
|
|
| def forward( |
| self, |
| input_ids: torch.LongTensor, |
| attention_mask: Optional[torch.Tensor] = None, |
| labels: Optional[torch.LongTensor] = None, |
| **kwargs, |
| ) -> CausalLMOutput: |
| padding_mask = None |
| if attention_mask is not None: |
| padding_mask = ~attention_mask.to(torch.bool) |
|
|
| logits_hi, logits_lo = self.model(input_ids, padding_mask=padding_mask, labels=labels) |
|
|
| loss = None |
| if labels is not None: |
| loss = self.model.compute_loss((logits_hi, logits_lo), labels, padding_mask=padding_mask) |
|
|
| out = CausalLMOutput(loss=loss, logits=logits_hi) |
| |
| out.logits_hi = logits_hi |
| out.logits_lo = logits_lo |
| return out |