| |
| |
| """ |
| LULUV2 inference-only runtime. |
| |
| This file intentionally contains only the code needed to load and run a |
| standalone native-bf16 LULUV2 checkpoint. It contains only the |
| runtime loader, tokenizer bridge, decoder modules, and two-pass inference path |
| needed for local generation. |
| |
| Runtime behavior: |
| - loads a local checkpoint supplied by the user/repo; |
| - uses local tokenizer files; |
| - does not download or load any external model weights; |
| - preserves the VWM/two-pass inference path when present in the checkpoint. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import json |
| import math |
| import os |
| import time |
| from dataclasses import dataclass |
| from types import SimpleNamespace |
| from typing import Dict, Optional, Tuple |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
| _TRANSFORMERS_IMPORT_ERROR = None |
| try: |
| from transformers import AutoTokenizer as _HFAutoTokenizer |
| try: |
| from transformers import AutoConfig as _HFAutoConfig |
| except Exception: |
| _HFAutoConfig = None |
| except Exception as _e: |
| _TRANSFORMERS_IMPORT_ERROR = _e |
| _HFAutoTokenizer = None |
| _HFAutoConfig = None |
|
|
| class _TokenOutput(dict): |
| def __getattr__(self, name): |
| try: |
| return self[name] |
| except KeyError as exc: |
| raise AttributeError(name) from exc |
| def to(self, device): |
| out = _TokenOutput() |
| for k, v in self.items(): |
| out[k] = v.to(device) if torch.is_tensor(v) else v |
| return out |
|
|
| class _LocalTokenizer: |
| def __init__(self, path: str, tokenizer_file: Optional[str] = None, **kwargs): |
| import json as _json |
| try: |
| from tokenizers import Tokenizer as _TokenizerCore |
| except Exception as exc: |
| raise RuntimeError( |
| "transformers import failed and tokenizers is unavailable. " |
| "Install tokenizers or use a matching torch/transformers pair." |
| ) from exc |
| self.name_or_path = path or tokenizer_file or "<local-tokenizer>" |
| if tokenizer_file: |
| tok_file = tokenizer_file |
| base_dir = os.path.dirname(os.path.abspath(tok_file)) |
| else: |
| base_dir = os.path.abspath(path) |
| tok_file = os.path.join(base_dir, "tokenizer.json") |
| if not os.path.exists(tok_file): |
| raise FileNotFoundError(f"Local tokenizer.json not found: {tok_file}") |
| self._tok = _TokenizerCore.from_file(tok_file) |
| self.vocab_size = int(self._tok.get_vocab_size()) |
| self.model_max_length = 10**9 |
| self.truncation_side = "left" |
| self.chat_template = None |
| self.eos_token = None |
| self.pad_token = None |
| cfg_path = os.path.join(base_dir, "tokenizer_config.json") |
| sp_path = os.path.join(base_dir, "special_tokens_map.json") |
| for p in (cfg_path, sp_path): |
| if os.path.exists(p): |
| try: |
| data = _json.load(open(p, "r", encoding="utf-8")) |
| except Exception: |
| data = {} |
| if self.chat_template is None and isinstance(data.get("chat_template"), str): |
| self.chat_template = data.get("chat_template") |
| for key, attr in (("eos_token", "eos_token"), ("pad_token", "pad_token")): |
| val = data.get(key) |
| if isinstance(val, dict): |
| val = val.get("content") |
| if isinstance(val, str): |
| setattr(self, attr, val) |
| if self.eos_token is None: |
| for cand in ("<|im_end|>", "<|endoftext|>", "</s>"): |
| if self._tok.token_to_id(cand) is not None: |
| self.eos_token = cand |
| break |
| if self.pad_token is None: |
| self.pad_token = self.eos_token |
| self.eos_token_id = self._tok.token_to_id(self.eos_token) if self.eos_token else None |
| self.pad_token_id = self._tok.token_to_id(self.pad_token) if self.pad_token else self.eos_token_id |
|
|
| def __len__(self): |
| return self.vocab_size |
|
|
| def __call__(self, text, return_tensors=None, truncation=False, max_length=None, add_special_tokens=True, **kwargs): |
| if isinstance(text, (list, tuple)): |
| encoded = [self._encode_one(t, add_special_tokens, truncation, max_length) for t in text] |
| maxlen = max(len(x) for x in encoded) if encoded else 0 |
| pad = self.pad_token_id if self.pad_token_id is not None else 0 |
| arr = [x + [pad] * (maxlen - len(x)) for x in encoded] |
| if return_tensors == "pt": |
| return _TokenOutput(input_ids=torch.tensor(arr, dtype=torch.long)) |
| return _TokenOutput(input_ids=arr) |
| ids = self._encode_one(str(text), add_special_tokens, truncation, max_length) |
| if return_tensors == "pt": |
| return _TokenOutput(input_ids=torch.tensor([ids], dtype=torch.long)) |
| return _TokenOutput(input_ids=ids) |
|
|
| def _encode_one(self, text, add_special_tokens=True, truncation=False, max_length=None): |
| enc = self._tok.encode(text, add_special_tokens=bool(add_special_tokens)) |
| ids = list(enc.ids) |
| if truncation and max_length is not None and len(ids) > int(max_length): |
| if self.truncation_side == "left": |
| ids = ids[-int(max_length):] |
| else: |
| ids = ids[:int(max_length)] |
| return ids |
|
|
| def decode(self, ids, skip_special_tokens=True, **kwargs): |
| if torch.is_tensor(ids): |
| ids = ids.detach().cpu().tolist() |
| if ids and isinstance(ids[0], list): |
| ids = ids[0] |
| return self._tok.decode([int(x) for x in ids], skip_special_tokens=bool(skip_special_tokens)) |
|
|
| def apply_chat_template(self, messages, tokenize=False, add_generation_prompt=False, **kwargs): |
| chunks = [] |
| for m in messages: |
| role = str(m.get("role", "user")) |
| content = str(m.get("content", "")) |
| chunks.append(f"<|im_start|>{role}\n{content}<|im_end|>") |
| if add_generation_prompt: |
| chunks.append("<|im_start|>assistant\n") |
| text = "\n".join(chunks) |
| if tokenize: |
| return self(text, add_special_tokens=False).input_ids |
| return text |
|
|
| class _AutoTokenizerShim: |
| @staticmethod |
| def from_pretrained(path, *args, **kwargs): |
| if _HFAutoTokenizer is not None: |
| return _HFAutoTokenizer.from_pretrained(path, *args, **kwargs) |
| return _LocalTokenizer(path) |
|
|
| class _AutoConfigShim: |
| @staticmethod |
| def from_pretrained(path, *args, **kwargs): |
| if _HFAutoConfig is not None: |
| return _HFAutoConfig.from_pretrained(path, *args, **kwargs) |
| raise RuntimeError( |
| "AutoConfig requested, but transformers failed to import. " |
| "Use --no-config-download / embedded model_config for LULUV2." |
| ) |
| AutoTokenizer = _AutoTokenizerShim |
| AutoConfig = _AutoConfigShim |
|
|
| torch.backends.cuda.matmul.allow_tf32 = True |
| torch.backends.cudnn.allow_tf32 = True |
| if hasattr(torch, "set_float32_matmul_precision"): |
| torch.set_float32_matmul_precision("high") |
| try: |
| if torch.cuda.is_available(): |
| torch.backends.cuda.enable_flash_sdp(True) |
| torch.backends.cuda.enable_mem_efficient_sdp(True) |
| torch.backends.cuda.enable_math_sdp(False) |
| except Exception: |
| pass |
|
|
|
|
|
|
| def parse_dtype(name: str): |
| name = str(name).strip().lower() |
| if name in {"bf16", "bfloat16"}: |
| return torch.bfloat16 |
| if name in {"fp16", "float16", "half"}: |
| return torch.float16 |
| if name in {"fp32", "float32"}: |
| return torch.float32 |
| raise ValueError(f"Unknown dtype: {name}") |
|
|
|
|
| def human_bytes(n: float) -> str: |
| units = ["B", "KB", "MB", "GB", "TB"] |
| x = float(n) |
| i = 0 |
| while x >= 1024.0 and i < len(units) - 1: |
| x /= 1024.0 |
| i += 1 |
| return f"{x:.2f} {units[i]}" |
|
|
|
|
| def safe_torch_load(path: str, map_location="cpu"): |
| |
| |
| try: |
| return torch.load(path, map_location=map_location, weights_only=False) |
| except TypeError: |
| return torch.load(path, map_location=map_location) |
|
|
|
|
| def module_has_vwm(sd: Dict[str, torch.Tensor], prefix: str) -> bool: |
| return f"{prefix}.A" in sd and f"{prefix}.B" in sd and f"{prefix}.c" in sd |
|
|
|
|
| def linear_shape_from_state(sd: Dict[str, torch.Tensor], prefix: str) -> Tuple[int, int, bool]: |
| if module_has_vwm(sd, prefix): |
| out_features = int(sd[f"{prefix}.A"].shape[0]) |
| in_features = int(sd[f"{prefix}.B"].shape[0]) |
| has_bias = f"{prefix}.bias" in sd |
| return in_features, out_features, has_bias |
| wkey = f"{prefix}.weight" |
| if wkey not in sd: |
| raise KeyError(f"Cannot infer Linear shape for {prefix}; missing {wkey} and VWM A/B/c") |
| out_features, in_features = sd[wkey].shape |
| has_bias = f"{prefix}.bias" in sd |
| return int(in_features), int(out_features), has_bias |
|
|
|
|
| def make_linear_from_state(sd: Dict[str, torch.Tensor], prefix: str) -> nn.Module: |
| in_features, out_features, has_bias = linear_shape_from_state(sd, prefix) |
| if module_has_vwm(sd, prefix): |
| rank = int(sd[f"{prefix}.c"].shape[0]) |
| return VWMFactorizedLinear(in_features, out_features, rank, bias=has_bias, name=prefix) |
| return nn.Linear(in_features, out_features, bias=has_bias) |
|
|
|
|
| def module_has_vwm_embedding(sd: Dict[str, torch.Tensor], prefix: str) -> bool: |
| return f"{prefix}.A" in sd and f"{prefix}.B" in sd and f"{prefix}.c" in sd |
|
|
|
|
| def embedding_shape_from_state(sd: Dict[str, torch.Tensor], prefix: str) -> Tuple[int, int]: |
| if module_has_vwm_embedding(sd, prefix): |
| return int(sd[f"{prefix}.A"].shape[0]), int(sd[f"{prefix}.B"].shape[0]) |
| wkey = f"{prefix}.weight" |
| if wkey not in sd: |
| raise KeyError(f"Cannot infer embedding shape for {prefix}; missing dense or VWM embedding tensors") |
| return int(sd[wkey].shape[0]), int(sd[wkey].shape[1]) |
|
|
|
|
| def make_embedding_from_state(sd: Dict[str, torch.Tensor], prefix: str) -> nn.Module: |
| vocab_size, hidden_size = embedding_shape_from_state(sd, prefix) |
| if module_has_vwm_embedding(sd, prefix): |
| rank = int(sd[f"{prefix}.c"].shape[0]) |
| return VWMFactorizedEmbedding(vocab_size, hidden_size, rank, name=prefix) |
| return nn.Embedding(vocab_size, hidden_size) |
|
|
|
|
| def expand_shared_banks_into_state(ckpt: Dict, sd: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]: |
| """Expand experimental shared-bank storage into normal per-module A/B/c tensors.""" |
| banks = ckpt.get("shared_banks") |
| if not banks: |
| return sd |
| out = dict(sd) |
| n = 0 |
| for bank_id, bank in banks.items(): |
| A = bank["A"] |
| B = bank["B"] |
| modules = bank.get("modules", {}) |
| for prefix, m in modules.items(): |
| out[f"{prefix}.A"] = A |
| out[f"{prefix}.B"] = B |
| out[f"{prefix}.c"] = m["c"] |
| if "bias" in m and m["bias"] is not None: |
| out[f"{prefix}.bias"] = m["bias"] |
| n += 1 |
| print(f"[shared-bank] expanded {len(banks)} banks into {n} VWM modules") |
| return out |
|
|
|
|
| |
| |
| |
|
|
|
|
| class VWMFactorizedLinear(nn.Module): |
| """ |
| W ~= A diag(c) B^T |
| y = ((x @ B) * c) @ A^T + bias |
| |
| This matches LULU2 exporter's exported VWMFactorizedLinear |
| state names: A, B, c, optional bias. |
| """ |
|
|
| def __init__(self, in_features: int, out_features: int, rank: int, bias: bool = True, name: str = ""): |
| super().__init__() |
| self.in_features = int(in_features) |
| self.out_features = int(out_features) |
| self.rank = int(rank) |
| self.name = name |
| self.A = nn.Parameter(torch.empty(out_features, rank), requires_grad=False) |
| self.B = nn.Parameter(torch.empty(in_features, rank), requires_grad=False) |
| self.c = nn.Parameter(torch.empty(rank), requires_grad=False) |
| self.bias = nn.Parameter(torch.zeros(out_features), requires_grad=False) if bias else None |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| |
| t = torch.matmul(x, self.B.to(dtype=x.dtype)) |
| t = t * self.c.to(dtype=x.dtype) |
| y = torch.matmul(t, self.A.to(dtype=x.dtype).transpose(0, 1)) |
| if self.bias is not None: |
| y = y + self.bias.to(dtype=x.dtype) |
| return y |
|
|
|
|
|
|
|
|
| class VWMFactorizedEmbedding(nn.Module): |
| """Runtime for exported VWM embedding: E ~= A diag(c) B^T.""" |
| def __init__(self, num_embeddings: int, embedding_dim: int, rank: int, name: str = "model.embed_tokens"): |
| super().__init__() |
| self.num_embeddings = int(num_embeddings) |
| self.embedding_dim = int(embedding_dim) |
| self.rank = int(rank) |
| self.name = name |
| self.A = nn.Parameter(torch.empty(num_embeddings, rank), requires_grad=False) |
| self.B = nn.Parameter(torch.empty(embedding_dim, rank), requires_grad=False) |
| self.c = nn.Parameter(torch.empty(rank), requires_grad=False) |
|
|
| @property |
| def weight(self): |
| |
| return (self.A * self.c.view(1, -1)) @ self.B.T |
|
|
| def forward(self, input_ids: torch.LongTensor) -> torch.Tensor: |
| a = F.embedding(input_ids, self.A) |
| t = a * self.c.to(dtype=a.dtype) |
| return torch.matmul(t, self.B.to(dtype=a.dtype).transpose(0, 1)) |
|
|
|
|
| class TiedEmbeddingLMHead(nn.Module): |
| """LM head tied to the model embedding matrix, dense or VWM.""" |
| def __init__(self, embedding_module: nn.Module): |
| super().__init__() |
| self.embedding_module = embedding_module |
|
|
| def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: |
| emb = self.embedding_module |
| if isinstance(emb, VWMFactorizedEmbedding): |
| |
| t = torch.matmul(hidden_states, emb.B.to(dtype=hidden_states.dtype)) |
| t = t * emb.c.to(dtype=hidden_states.dtype) |
| return torch.matmul(t, emb.A.to(dtype=hidden_states.dtype).transpose(0, 1)) |
| return F.linear(hidden_states, emb.weight.to(dtype=hidden_states.dtype)) |
|
|
| |
| |
| |
|
|
|
|
| class LuluRMSNorm(nn.Module): |
| def __init__(self, hidden_size: int, eps: float = 1e-6): |
| super().__init__() |
| self.weight = nn.Parameter(torch.ones(hidden_size), requires_grad=False) |
| self.variance_epsilon = float(eps) |
|
|
| def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: |
| input_dtype = hidden_states.dtype |
| hidden_states = hidden_states.float() |
| variance = hidden_states.pow(2).mean(-1, keepdim=True) |
| hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon) |
| return self.weight.to(dtype=input_dtype) * hidden_states.to(input_dtype) |
|
|
|
|
| class LuluRotaryEmbedding(nn.Module): |
| def __init__(self, dim: int, max_position_embeddings: int = 32768, base: float = 1000000.0): |
| super().__init__() |
| self.dim = int(dim) |
| self.max_position_embeddings = int(max_position_embeddings) |
| self.base = float(base) |
| inv_freq = 1.0 / (self.base ** (torch.arange(0, self.dim, 2).float() / self.dim)) |
| self.register_buffer("inv_freq", inv_freq, persistent=False) |
|
|
| @torch.no_grad() |
| def forward(self, x: torch.Tensor, position_ids: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: |
| |
| inv_freq = self.inv_freq.to(device=x.device) |
| freqs = torch.einsum("bt,d->btd", position_ids.float(), inv_freq.float()) |
| emb = torch.cat((freqs, freqs), dim=-1) |
| return emb.cos().to(dtype=x.dtype), emb.sin().to(dtype=x.dtype) |
|
|
|
|
| def rotate_half(x: torch.Tensor) -> torch.Tensor: |
| x1 = x[..., : x.shape[-1] // 2] |
| x2 = x[..., x.shape[-1] // 2 :] |
| return torch.cat((-x2, x1), dim=-1) |
|
|
|
|
| def apply_rotary_pos_emb(q: torch.Tensor, k: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: |
| |
| cos = cos.unsqueeze(1) |
| sin = sin.unsqueeze(1) |
| q_embed = (q * cos) + (rotate_half(q) * sin) |
| k_embed = (k * cos) + (rotate_half(k) * sin) |
| return q_embed, k_embed |
|
|
|
|
| def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor: |
| batch, num_key_value_heads, slen, head_dim = hidden_states.shape |
| if n_rep == 1: |
| return hidden_states |
| hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim) |
| return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim) |
|
|
|
|
| class LuluVWMMLP(nn.Module): |
| def __init__(self, cfg, sd: Dict[str, torch.Tensor], layer_idx: int): |
| super().__init__() |
| p = f"model.layers.{layer_idx}.mlp" |
| self.gate_proj = make_linear_from_state(sd, f"{p}.gate_proj") |
| self.up_proj = make_linear_from_state(sd, f"{p}.up_proj") |
| self.down_proj = make_linear_from_state(sd, f"{p}.down_proj") |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x)) |
|
|
|
|
| class LuluVWMAttention(nn.Module): |
| def __init__(self, cfg, sd: Dict[str, torch.Tensor], layer_idx: int): |
| super().__init__() |
| self.layer_idx = int(layer_idx) |
| self.hidden_size = int(cfg.hidden_size) |
| self.num_heads = int(cfg.num_attention_heads) |
| self.num_key_value_heads = int(getattr(cfg, "num_key_value_heads", self.num_heads)) |
| self.head_dim = int(getattr(cfg, "head_dim", self.hidden_size // self.num_heads)) |
| self.num_key_value_groups = self.num_heads // self.num_key_value_heads |
| self.scaling = self.head_dim ** -0.5 |
| self.attention_dropout = float(getattr(cfg, "attention_dropout", 0.0)) |
|
|
| p = f"model.layers.{layer_idx}.self_attn" |
| self.q_proj = make_linear_from_state(sd, f"{p}.q_proj") |
| self.k_proj = make_linear_from_state(sd, f"{p}.k_proj") |
| self.v_proj = make_linear_from_state(sd, f"{p}.v_proj") |
| self.o_proj = make_linear_from_state(sd, f"{p}.o_proj") |
|
|
| rope_theta = float(getattr(cfg, "rope_theta", 1000000.0)) |
| max_pos = int(getattr(cfg, "max_position_embeddings", 32768)) |
| self.rotary_emb = LuluRotaryEmbedding(self.head_dim, max_position_embeddings=max_pos, base=rope_theta) |
|
|
| def forward(self, hidden_states: torch.Tensor, position_ids: torch.Tensor) -> torch.Tensor: |
| bsz, q_len, _ = hidden_states.size() |
|
|
| query_states = self.q_proj(hidden_states) |
| key_states = self.k_proj(hidden_states) |
| value_states = self.v_proj(hidden_states) |
|
|
| query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2) |
| key_states = key_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) |
| value_states = value_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) |
|
|
| cos, sin = self.rotary_emb(value_states, position_ids) |
| query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin) |
|
|
| key_states = repeat_kv(key_states, self.num_key_value_groups) |
| value_states = repeat_kv(value_states, self.num_key_value_groups) |
|
|
| |
| attn_output = F.scaled_dot_product_attention( |
| query_states, |
| key_states, |
| value_states, |
| attn_mask=None, |
| dropout_p=0.0, |
| is_causal=True, |
| scale=self.scaling, |
| ) |
| attn_output = attn_output.transpose(1, 2).contiguous().reshape(bsz, q_len, self.hidden_size) |
| return self.o_proj(attn_output) |
|
|
|
|
| class LuluVWMDecoderLayer(nn.Module): |
| def __init__(self, cfg, sd: Dict[str, torch.Tensor], layer_idx: int): |
| super().__init__() |
| self.self_attn = LuluVWMAttention(cfg, sd, layer_idx) |
| self.mlp = LuluVWMMLP(cfg, sd, layer_idx) |
| self.input_layernorm = LuluRMSNorm(cfg.hidden_size, eps=getattr(cfg, "rms_norm_eps", 1e-6)) |
| self.post_attention_layernorm = LuluRMSNorm(cfg.hidden_size, eps=getattr(cfg, "rms_norm_eps", 1e-6)) |
|
|
| def forward(self, hidden_states: torch.Tensor, position_ids: torch.Tensor) -> torch.Tensor: |
| residual = hidden_states |
| hidden_states = self.input_layernorm(hidden_states) |
| hidden_states = self.self_attn(hidden_states, position_ids=position_ids) |
| hidden_states = residual + hidden_states |
|
|
| residual = hidden_states |
| hidden_states = self.post_attention_layernorm(hidden_states) |
| hidden_states = self.mlp(hidden_states) |
| hidden_states = residual + hidden_states |
| return hidden_states |
|
|
|
|
| class LuluVWMModel(nn.Module): |
| def __init__(self, cfg, sd: Dict[str, torch.Tensor]): |
| super().__init__() |
| self.config = cfg |
| vocab_size, hidden_size = embedding_shape_from_state(sd, "model.embed_tokens") |
| self.embed_tokens = make_embedding_from_state(sd, "model.embed_tokens") |
| self.layers = nn.ModuleList([LuluVWMDecoderLayer(cfg, sd, i) for i in range(int(cfg.num_hidden_layers))]) |
| self.norm = LuluRMSNorm(hidden_size, eps=getattr(cfg, "rms_norm_eps", 1e-6)) |
|
|
| def forward(self, input_ids: torch.LongTensor, position_ids: Optional[torch.LongTensor] = None) -> torch.Tensor: |
| bsz, seq_len = input_ids.shape |
| if position_ids is None: |
| position_ids = torch.arange(seq_len, device=input_ids.device, dtype=torch.long).unsqueeze(0).expand(bsz, -1) |
| hidden_states = self.embed_tokens(input_ids) |
| for layer in self.layers: |
| hidden_states = layer(hidden_states, position_ids=position_ids) |
| return self.norm(hidden_states) |
|
|
|
|
| class LuluVWMForCausalLM(nn.Module): |
| def __init__(self, cfg, sd: Dict[str, torch.Tensor]): |
| super().__init__() |
| self.config = cfg |
| self.model = LuluVWMModel(cfg, sd) |
| _, hidden_size = embedding_shape_from_state(sd, "model.embed_tokens") |
| self.tie_word_embeddings = bool(getattr(cfg, "tie_word_embeddings", False)) |
| if module_has_vwm(sd, "lm_head") or "lm_head.weight" in sd: |
| self.lm_head = make_linear_from_state(sd, "lm_head") |
| else: |
| self.tie_word_embeddings = True |
| self.lm_head = TiedEmbeddingLMHead(self.model.embed_tokens) |
|
|
| def forward(self, input_ids: torch.LongTensor, position_ids: Optional[torch.LongTensor] = None): |
| hidden_states = self.model(input_ids=input_ids, position_ids=position_ids) |
| logits = self.lm_head(hidden_states) |
| return SimpleNamespace(logits=logits) |
|
|
|
|
| |
| |
| |
|
|
|
|
| def infer_minimal_config_from_state(sd: Dict[str, torch.Tensor], model_id: str = "") -> SimpleNamespace: |
| if "model.embed_tokens.weight" in sd: |
| hidden_size = int(sd["model.embed_tokens.weight"].shape[1]) |
| vocab_size = int(sd["model.embed_tokens.weight"].shape[0]) |
| elif module_has_vwm_embedding(sd, "model.embed_tokens"): |
| vocab_size = int(sd["model.embed_tokens.A"].shape[0]) |
| hidden_size = int(sd["model.embed_tokens.B"].shape[0]) |
| else: |
| raise ValueError("Checkpoint is missing model.embed_tokens dense or VWM tensors. Use a full standalone checkpoint, not a delta checkpoint.") |
| layer_ids = [] |
| for k in sd.keys(): |
| if k.startswith("model.layers."): |
| try: |
| layer_ids.append(int(k.split(".")[2])) |
| except Exception: |
| pass |
| num_hidden_layers = max(layer_ids) + 1 if layer_ids else 0 |
| inter_key = "model.layers.0.mlp.gate_proj.weight" |
| if inter_key in sd: |
| intermediate_size = int(sd[inter_key].shape[0]) |
| else: |
| intermediate_size = 4864 |
|
|
| |
| |
| num_attention_heads = 14 |
| num_key_value_heads = 2 |
| head_dim = hidden_size // num_attention_heads |
| if head_dim * num_attention_heads != hidden_size: |
| |
| |
| num_attention_heads = 1 |
| num_key_value_heads = 1 |
| head_dim = hidden_size |
|
|
| return SimpleNamespace( |
| model_type="luluv2", |
| model_id=model_id, |
| vocab_size=vocab_size, |
| hidden_size=hidden_size, |
| intermediate_size=intermediate_size, |
| num_hidden_layers=num_hidden_layers, |
| num_attention_heads=num_attention_heads, |
| num_key_value_heads=num_key_value_heads, |
| head_dim=head_dim, |
| rms_norm_eps=1e-6, |
| rope_theta=1000000.0, |
| max_position_embeddings=32768, |
| attention_dropout=0.0, |
| tie_word_embeddings=False, |
| ) |
|
|
|
|
| def namespace_from_dict(d: Dict) -> SimpleNamespace: |
| return SimpleNamespace(**d) |
|
|
|
|
| def load_runtime_config(ckpt: Dict, sd: Dict[str, torch.Tensor], args) -> SimpleNamespace: |
| if "model_config" in ckpt and isinstance(ckpt["model_config"], dict): |
| print("[config] using model_config embedded in checkpoint") |
| d = dict(ckpt["model_config"]) |
| if ckpt.get("tie_word_embeddings") is True: |
| d["tie_word_embeddings"] = True |
| return namespace_from_dict(d) |
|
|
| model_id = args.model_id or ckpt.get("model_id") or ckpt.get("args", {}).get("model_id") or "LULU2" |
|
|
| if args.no_config_download: |
| print("[config] no embedded config and --no-config-download set; using LULU2 defaults") |
| cfg = infer_minimal_config_from_state(sd, model_id=model_id) |
| if ckpt.get("tie_word_embeddings") is True: |
| cfg.tie_word_embeddings = True |
| return cfg |
|
|
| print(f"[config] loading config metadata only from {model_id}; no model weights are loaded") |
| cfg = AutoConfig.from_pretrained(model_id, trust_remote_code=True) |
| return cfg |
|
|
|
|
| |
| |
| |
|
|
|
|
| def build_chat_prompt(tokenizer, user_prompt: str, system_prompt: str = "You are a helpful assistant. Answer directly and naturally.") -> str: |
| messages = [] |
| if system_prompt: |
| messages.append({"role": "system", "content": system_prompt}) |
| messages.append({"role": "user", "content": user_prompt}) |
| try: |
| return tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) |
| except Exception: |
| return f"system\n{system_prompt}\nuser\n{user_prompt}\nassistant\n" |
|
|
|
|
| @torch.no_grad() |
| def sample_next(logits: torch.Tensor, temperature: float = 0.0, top_k: int = 0, top_p: float = 1.0) -> torch.Tensor: |
| if temperature <= 0.0: |
| return torch.argmax(logits, dim=-1, keepdim=True) |
|
|
| logits = logits / max(temperature, 1e-6) |
| if top_k and top_k > 0: |
| k = min(int(top_k), logits.size(-1)) |
| thresh = torch.topk(logits, k, dim=-1).values[..., -1, None] |
| logits = torch.where(logits >= thresh, logits, torch.full_like(logits, -float("inf"))) |
| if top_p < 1.0: |
| sorted_logits, sorted_indices = torch.sort(logits, descending=True, dim=-1) |
| probs = torch.softmax(sorted_logits, dim=-1) |
| cumulative_probs = torch.cumsum(probs, dim=-1) |
| sorted_indices_to_remove = cumulative_probs > top_p |
| sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone() |
| sorted_indices_to_remove[..., 0] = False |
| sorted_logits = sorted_logits.masked_fill(sorted_indices_to_remove, -float("inf")) |
| logits = torch.full_like(logits, -float("inf")).scatter(1, sorted_indices, sorted_logits) |
| probs = torch.softmax(logits, dim=-1) |
| return torch.multinomial(probs, num_samples=1) |
|
|
|
|
| @torch.no_grad() |
| def generate_text(model, tokenizer, prompt: str, device, max_new_tokens: int = 120, temperature: float = 0.0, top_k: int = 0, top_p: float = 1.0, max_context: int = 2048) -> Tuple[str, float]: |
| model.eval() |
| enc = tokenizer(prompt, return_tensors="pt") |
| input_ids = enc.input_ids.to(device) |
| eos_id = tokenizer.eos_token_id |
| t0 = time.time() |
| start_len = int(input_ids.shape[1]) |
|
|
| for _ in range(max_new_tokens): |
| ctx = input_ids[:, -max_context:] |
| out = model(ctx) |
| next_logits = out.logits[:, -1, :].float() |
| next_id = sample_next(next_logits, temperature=temperature, top_k=top_k, top_p=top_p) |
| input_ids = torch.cat([input_ids, next_id.to(input_ids.device)], dim=-1) |
| if eos_id is not None and int(next_id.item()) == int(eos_id): |
| break |
|
|
| dt = time.time() - t0 |
| new_tokens = max(1, int(input_ids.shape[1]) - start_len) |
| return tokenizer.decode(input_ids[0], skip_special_tokens=True), new_tokens / max(dt, 1e-9) |
|
|
|
|
| def load_tokenizer(args, ckpt): |
| tok_path = args.tokenizer or ckpt.get("tokenizer_dir") or ckpt.get("model_id") or ckpt.get("args", {}).get("model_id") or args.model_id |
| if not tok_path: |
| raise ValueError("Tokenizer path/name is required. Pass --tokenizer <local-dir-or-model-id>.") |
| |
| |
| ckpt_dir = os.path.dirname(os.path.abspath(args.checkpoint)) |
| if tok_path and not os.path.isabs(tok_path): |
| maybe_local = os.path.join(ckpt_dir, tok_path) |
| if os.path.isdir(maybe_local): |
| tok_path = maybe_local |
| print(f"[tokenizer] {tok_path}") |
| tok = AutoTokenizer.from_pretrained(tok_path, trust_remote_code=True, local_files_only=bool(args.local_files_only)) |
| if tok.pad_token_id is None and tok.eos_token_id is not None: |
| tok.pad_token = tok.eos_token |
| return tok |
|
|
|
|
| |
| |
|
|
| |
| Lulu2RMSNorm = LuluRMSNorm |
| Lulu2RotaryEmbedding = LuluRotaryEmbedding |
| Lulu2VWMMLP = LuluVWMMLP |
| Lulu2VWMAttention = LuluVWMAttention |
| Lulu2VWMDecoderLayer = LuluVWMDecoderLayer |
| Lulu2VWMModel = LuluVWMModel |
| Lulu2ForCausalLM = LuluVWMForCausalLM |
|
|
|
|
| class Pass2RefinementAdapter(nn.Module): |
| """Small gated residual adapter conditioned on pass-1 layer state.""" |
|
|
| def __init__(self, hidden_size: int, rank: int, gate_init: float = -5.0): |
| super().__init__() |
| self.hidden_size = int(hidden_size) |
| self.rank = int(rank) |
| self.x_norm = LuluRMSNorm(hidden_size) |
| self.cond_norm = LuluRMSNorm(hidden_size) |
| self.down = nn.Linear(2 * hidden_size, rank, bias=False) |
| self.up = nn.Linear(rank, hidden_size, bias=False) |
| self.gate = nn.Parameter(torch.tensor(float(gate_init))) |
|
|
| nn.init.normal_(self.down.weight, mean=0.0, std=0.02 / math.sqrt(max(1, hidden_size))) |
| |
| nn.init.zeros_(self.up.weight) |
|
|
| def forward(self, x: torch.Tensor, cond: torch.Tensor) -> torch.Tensor: |
| z = torch.cat([self.x_norm(x), self.cond_norm(cond)], dim=-1) |
| delta = self.up(F.silu(self.down(z))) |
| return torch.sigmoid(self.gate).to(dtype=x.dtype) * delta |
|
|
|
|
| @dataclass |
| class Pass2Config: |
| adapter_rank: int = 64 |
| adapter_gate_init: float = -5.0 |
| layer_gate_init: float = -5.0 |
| pass_embed_scale: float = 0.0 |
| mode: str = "refine_pass1_residual" |
|
|
|
|
| class Lulu2TwoPassForCausalLM(nn.Module): |
| """ |
| Wraps a loaded LULU2 base model. |
| |
| Pass 1: normal LULU2 decoder forward, producing the pass-1 residual stream. |
| Pass 2: starts from pass-1 residual stream and adds small gated refinements. |
| |
| h2_i = h2_i + sigmoid(layer_gate_i) * (BaseLayer_i(h2_i) - h2_i) |
| + Adapter_i(h2_i, pass1_state_i) |
| |
| With zero-initialized adapter up-projections and negative gates, the model |
| starts extremely close to the loaded LULU2 checkpoint and learns refinements. |
| """ |
|
|
| def __init__(self, base: Lulu2ForCausalLM, cfg: Pass2Config): |
| super().__init__() |
| self.base = base |
| self.pass2_config = cfg |
| hidden = int(base.config.hidden_size) |
| n_layers = int(base.config.num_hidden_layers) |
| self.pass_embed = nn.Parameter(torch.randn(2, hidden) * float(cfg.pass_embed_scale)) |
| self.layer_gates = nn.Parameter(torch.full((n_layers,), float(cfg.layer_gate_init))) |
| self.adapters = nn.ModuleList([ |
| Pass2RefinementAdapter(hidden, int(cfg.adapter_rank), gate_init=float(cfg.adapter_gate_init)) |
| for _ in range(n_layers) |
| ]) |
|
|
| def _position_ids(self, input_ids: torch.LongTensor, position_ids: Optional[torch.LongTensor] = None): |
| if position_ids is not None: |
| return position_ids |
| bsz, seq_len = input_ids.shape |
| return torch.arange(seq_len, device=input_ids.device, dtype=torch.long).unsqueeze(0).expand(bsz, -1) |
|
|
| def forward_pass1_features(self, input_ids: torch.LongTensor, position_ids: Optional[torch.LongTensor] = None): |
| position_ids = self._position_ids(input_ids, position_ids) |
| h = self.base.model.embed_tokens(input_ids) |
| h = h + self.pass_embed[0].to(dtype=h.dtype).view(1, 1, -1) |
| layer_states = [] |
| for layer in self.base.model.layers: |
| h = layer(h, position_ids=position_ids) |
| layer_states.append(h) |
| return h, layer_states, position_ids |
|
|
| def forward(self, input_ids: torch.LongTensor, position_ids: Optional[torch.LongTensor] = None, return_pass1_logits: bool = False): |
| h1_resid, pass1_states, position_ids = self.forward_pass1_features(input_ids, position_ids=position_ids) |
| h1 = self.base.model.norm(h1_resid) |
|
|
| |
| h2 = h1_resid + self.pass_embed[1].to(dtype=h1_resid.dtype).view(1, 1, -1) |
| for i, layer in enumerate(self.base.model.layers): |
| before = h2 |
| layer_out = layer(h2, position_ids=position_ids) |
| layer_delta = layer_out - before |
| layer_gate = torch.sigmoid(self.layer_gates[i]).to(dtype=h2.dtype) |
| adapter_delta = self.adapters[i](h2, pass1_states[i]) |
| h2 = before + layer_gate * layer_delta + adapter_delta |
|
|
| h2 = self.base.model.norm(h2) |
| logits2 = self.base.lm_head(h2) |
|
|
| if return_pass1_logits: |
| with torch.no_grad(): |
| logits1 = self.base.lm_head(h1) |
| else: |
| logits1 = None |
| return SimpleNamespace(logits=logits2, pass1_logits=logits1) |
|
|
|
|
| @torch.no_grad() |
|
|
| def load_lulu2_base(args, device, dtype): |
| print("[guard] LULUV2 VWM runtime: no AutoModelForCausalLM.from_pretrained call and no external-model weights loaded.") |
| print(f"[load] {args.checkpoint} ({human_bytes(os.path.getsize(args.checkpoint))})") |
| ckpt = safe_torch_load(args.checkpoint, map_location="cpu") |
| if "model" not in ckpt: |
| raise ValueError("Checkpoint missing model state dict") |
| sd = expand_shared_banks_into_state(ckpt, ckpt["model"]) |
| cfg = load_runtime_config(ckpt, sd, args) |
| print(f"[config] hidden={cfg.hidden_size} layers={cfg.num_hidden_layers}") |
| base = Lulu2ForCausalLM(cfg, sd) |
| missing, unexpected = base.load_state_dict(sd, strict=False) |
| print(f"[state:base] missing={len(missing)} unexpected={len(unexpected)}") |
| if missing: |
| print("[state:base] first missing:", missing[:10]) |
| if unexpected: |
| print("[state:base] first unexpected:", unexpected[:10]) |
| base.to(device=device, dtype=dtype) |
| return ckpt, base |
|
|