Text Generation
Transformers
Safetensors
Chinese
English
ynet32
custom_code
ymodel
ymodel32
conversational
Instructions to use SnifferCaptain/YModel3.2-fm-1B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use SnifferCaptain/YModel3.2-fm-1B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="SnifferCaptain/YModel3.2-fm-1B", trust_remote_code=True) messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("SnifferCaptain/YModel3.2-fm-1B", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use SnifferCaptain/YModel3.2-fm-1B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "SnifferCaptain/YModel3.2-fm-1B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "SnifferCaptain/YModel3.2-fm-1B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/SnifferCaptain/YModel3.2-fm-1B
- SGLang
How to use SnifferCaptain/YModel3.2-fm-1B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "SnifferCaptain/YModel3.2-fm-1B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "SnifferCaptain/YModel3.2-fm-1B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "SnifferCaptain/YModel3.2-fm-1B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "SnifferCaptain/YModel3.2-fm-1B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use SnifferCaptain/YModel3.2-fm-1B with Docker Model Runner:
docker model run hf.co/SnifferCaptain/YModel3.2-fm-1B
Download modeling_ymodel32.py from SnifferCaptain/YModel3.2-fm-1B: direct link, hf CLI and curl.
- Browser
- Download file 52.2 kB
-
https://huggingface.co/SnifferCaptain/YModel3.2-fm-1B/resolve/main/modeling_ymodel32.py
- Command line
-
hf download hf://SnifferCaptain/YModel3.2-fm-1B/modeling_ymodel32.py
-
curl -L -o modeling_ymodel32.py https://huggingface.co/SnifferCaptain/YModel3.2-fm-1B/resolve/main/modeling_ymodel32.py
52.2 kB
| """Standalone inference implementation for YModel3.2. | |
| This module contains the complete model definition. Its only third-party | |
| dependencies are PyTorch and Transformers. | |
| """ | |
| from __future__ import annotations | |
| import math | |
| from pathlib import Path | |
| from typing import Optional, Union | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from transformers import GenerationMixin, PreTrainedModel | |
| from transformers.configuration_utils import PretrainedConfig | |
| from transformers.modeling_outputs import CausalLMOutputWithPast | |
| def _normalize_group_sizes(group_size: Union[int, list[int], tuple[int, ...]]) -> tuple[int, ...]: | |
| if isinstance(group_size, bool): | |
| raise TypeError("group_size must be a positive integer or a list of integers") | |
| if isinstance(group_size, int): | |
| values = (group_size,) | |
| elif isinstance(group_size, (list, tuple)): | |
| values = tuple(group_size) | |
| else: | |
| raise TypeError("group_size must be a positive integer or a list of integers") | |
| if not values or any( | |
| isinstance(value, bool) or not isinstance(value, int) or value < 1 | |
| for value in values | |
| ): | |
| raise ValueError("group_size must contain positive integers") | |
| if len(set(values)) != len(values): | |
| raise ValueError("group_size choices must be unique") | |
| return values | |
| class YConfig32(PretrainedConfig): | |
| """Architecture config for YModel3.2.""" | |
| model_type = "ynet32" | |
| MIN_FULL_BLOCKS = 3 | |
| def __init__( | |
| self, | |
| dropout: float = 0.0, | |
| bos_token_id: int = 151644, | |
| eos_token_id: int = 151645, | |
| pad_token_id: int = 151643, | |
| hidden_size: int = 1024, | |
| num_hidden_layers: Optional[int] = None, | |
| max_position_embeddings: int = 8192, | |
| vocab_size: int = 6400, | |
| rms_norm_eps: float = 1e-6, | |
| rope_theta: float = 5e4, | |
| rope_scaling: Optional[dict] = None, | |
| dtype: str = "float32", | |
| intermediate_size: int = 2048, | |
| num_heads: int = 8, | |
| num_key_value_heads: Optional[int] = None, | |
| head_dim: int = 192, | |
| group_size: Union[int, list[int], tuple[int, ...]] = 4, | |
| layer_pattern: Optional[str] = "fgggfgggf", | |
| use_sengram: bool = False, | |
| sengram_bucket_size: Optional[int] = 4096, | |
| sengram_topk: int = 2, | |
| use_dcp: bool = False, | |
| dynamic_compression_probe_hidden_size: int = 768, | |
| tie_word_embeddings: bool = True, | |
| **kwargs, | |
| ): | |
| # Derived from group_size; must not be set independently. | |
| kwargs.pop("group_size_options", None) | |
| kwargs.pop("num_layers", None) | |
| super().__init__( | |
| bos_token_id=bos_token_id, | |
| eos_token_id=eos_token_id, | |
| pad_token_id=pad_token_id, | |
| tie_word_embeddings=tie_word_embeddings, | |
| **kwargs, | |
| ) | |
| self.dropout = float(dropout) | |
| self.hidden_size = int(hidden_size) | |
| self.num_hidden_layers = int(num_hidden_layers) if num_hidden_layers is not None else 8 | |
| self.max_position_embeddings = int(max_position_embeddings) | |
| self.vocab_size = int(vocab_size) | |
| self.rms_norm_eps = float(rms_norm_eps) | |
| self.rope_theta = float(rope_theta) | |
| self.rope_scaling = rope_scaling | |
| self.dtype = str(dtype) | |
| self.intermediate_size = int(intermediate_size) | |
| self.num_heads = int(num_heads) | |
| self.num_key_value_heads = ( | |
| int(num_key_value_heads) if num_key_value_heads is not None else self.num_heads | |
| ) | |
| self.head_dim = int(head_dim) | |
| self.group_size = list(group_size) if isinstance(group_size, tuple) else group_size | |
| self.group_size_options = list(_normalize_group_sizes(group_size)) | |
| self.layer_pattern = layer_pattern | |
| self.use_sengram = bool(use_sengram) | |
| self.sengram_bucket_size = int(sengram_bucket_size or 4096) | |
| self.sengram_topk = int(sengram_topk) | |
| self.use_dcp = bool(use_dcp) | |
| self.dynamic_compression_probe_hidden_size = int(dynamic_compression_probe_hidden_size) | |
| if self.dynamic_compression_probe_hidden_size < 1: | |
| raise ValueError("dynamic_compression_probe_hidden_size must be positive") | |
| self.num_hidden_layers = len(resolve_layer_pattern(self)) | |
| def scale_lvl(self, lvl: int = 0): | |
| """Apply a published YModel32 architecture preset.""" | |
| if lvl == 2: | |
| self.hidden_size, self.num_heads, self.num_key_value_heads = 2048, 8, 2 | |
| self.head_dim, self.intermediate_size = 256, 6144 | |
| self.group_size, self.layer_pattern = 8, "fgggggfgggggfgggggfgggggf" | |
| self.use_sengram, self.sengram_bucket_size, self.sengram_topk = True, 32768, 12 | |
| self.use_dcp = False | |
| elif lvl == 1: | |
| self.hidden_size, self.num_heads, self.num_key_value_heads = 1024, 8, 2 | |
| self.head_dim, self.intermediate_size = 256, 3072 | |
| self.group_size, self.layer_pattern = 8, "fgggggfgggggfgggggfgggggfgggggf" | |
| self.use_sengram, self.sengram_bucket_size, self.sengram_topk = True, 16384, 12 | |
| self.use_dcp = False | |
| self.dynamic_compression_probe_hidden_size = 1024 | |
| elif lvl == 0: | |
| self.hidden_size, self.num_heads, self.num_key_value_heads = 768, 12, 6 | |
| self.head_dim, self.intermediate_size = 128, 2048 | |
| self.group_size, self.layer_pattern = 8, "fggggfggggfggggfgggf" | |
| self.use_sengram, self.sengram_bucket_size, self.sengram_topk = True, 8192, 12 | |
| self.use_dcp = False | |
| elif lvl == -1: | |
| self.hidden_size, self.num_heads, self.num_key_value_heads = 768, 12, 6 | |
| self.head_dim, self.intermediate_size = 128, 2048 | |
| self.group_size, self.layer_pattern = list(range(4, 17)), "fggggfggggf" | |
| self.use_sengram, self.sengram_bucket_size, self.sengram_topk = True, 4096, 12 | |
| self.dynamic_compression_probe_hidden_size = 768 | |
| self.use_dcp = True | |
| elif lvl == -2: | |
| self.hidden_size, self.num_heads, self.num_key_value_heads = 512, 4, 2 | |
| self.head_dim, self.intermediate_size = 128, 1024 | |
| self.group_size, self.layer_pattern = 8, "fggfggf" | |
| self.use_sengram, self.sengram_bucket_size, self.sengram_topk = True, 1024, 8 | |
| self.use_dcp = False | |
| else: | |
| raise ValueError(f"invalid YModel3.2 scale level: {lvl}") | |
| if self.layer_pattern.count("f") < self.MIN_FULL_BLOCKS: | |
| raise ValueError("YModel3.2 scale patterns require at least three full blocks") | |
| self.group_size_options = list(_normalize_group_sizes(self.group_size)) | |
| self.num_hidden_layers = len(resolve_layer_pattern(self)) | |
| return self | |
| class RMSNorm(nn.RMSNorm): | |
| def __init__(self, dim: int, eps: float = 1e-6): | |
| super().__init__(dim, eps=eps) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return super().forward(x).to(x.dtype) | |
| def _yarn_linear_ramp(low: float, high: float, dim: int) -> torch.Tensor: | |
| if low == high: | |
| high += 0.001 | |
| return torch.clamp((torch.arange(dim, dtype=torch.float32) - low) / (high - low), 0.0, 1.0) | |
| def _yarn_correction_dim(num_rotations: float, dim: int, theta: float, max_position_embeddings: int) -> float: | |
| return dim * math.log(max_position_embeddings / (num_rotations * 2 * math.pi)) / (2 * math.log(theta)) | |
| def resolve_rope_frequencies( | |
| dim: int, | |
| theta: float, | |
| rope_scaling: Optional[dict] = None, | |
| max_position_embeddings: Optional[int] = None, | |
| ) -> tuple[torch.Tensor, float]: | |
| inv_freq = 1.0 / (theta ** (torch.arange(0, dim, 2).float() / dim)) | |
| attention_factor = 1.0 | |
| if rope_scaling and str(rope_scaling.get("type", "yarn")).lower() == "yarn": | |
| factor = float(rope_scaling.get("factor", 1.0)) | |
| if factor > 1.0: | |
| original = int(rope_scaling.get("original_max_position_embeddings", max_position_embeddings or 0)) | |
| beta_fast = float(rope_scaling.get("beta_fast", 32.0)) | |
| beta_slow = float(rope_scaling.get("beta_slow", 1.0)) | |
| low = math.floor(_yarn_correction_dim(beta_fast, dim, theta, original)) | |
| high = math.ceil(_yarn_correction_dim(beta_slow, dim, theta, original)) | |
| ramp = _yarn_linear_ramp(low, high, dim // 2) | |
| inv_freq = inv_freq / factor * (1.0 - ramp) + inv_freq * ramp | |
| attention_factor = float(rope_scaling.get("attention_factor", 1.0)) | |
| return inv_freq, attention_factor | |
| def precompute_freqs_cis( | |
| dim: int, | |
| end: int, | |
| theta: float, | |
| rope_scaling: Optional[dict] = None, | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| inv_freq, attention_factor = resolve_rope_frequencies( | |
| dim, theta, rope_scaling, max_position_embeddings=end | |
| ) | |
| freqs = torch.outer(torch.arange(end, dtype=torch.float32), inv_freq).float() | |
| return ( | |
| torch.cat([torch.cos(freqs), torch.cos(freqs)], dim=-1) * attention_factor, | |
| torch.cat([torch.sin(freqs), torch.sin(freqs)], dim=-1) * attention_factor, | |
| ) | |
| def rope_embeddings_from_positions( | |
| positions: torch.Tensor, | |
| dim: int, | |
| theta: float, | |
| rope_scaling: Optional[dict] = None, | |
| max_position_embeddings: Optional[int] = None, | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| inv_freq, attention_factor = resolve_rope_frequencies( | |
| dim, theta, rope_scaling, max_position_embeddings | |
| ) | |
| positions = positions.to(dtype=torch.float32) | |
| freqs = torch.outer(positions.reshape(-1), inv_freq.to(positions.device)).float() | |
| return ( | |
| torch.cat([torch.cos(freqs), torch.cos(freqs)], dim=-1) * attention_factor, | |
| torch.cat([torch.sin(freqs), torch.sin(freqs)], dim=-1) * attention_factor, | |
| ) | |
| def rotate_half(x: torch.Tensor) -> torch.Tensor: | |
| return torch.cat((-x[..., x.shape[-1] // 2 :], x[..., : x.shape[-1] // 2]), dim=-1) | |
| def apply_rope_to_single(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor: | |
| if cos.dim() == 2: | |
| cos, sin = cos.unsqueeze(0).unsqueeze(0), sin.unsqueeze(0).unsqueeze(0) | |
| elif cos.dim() == 3: | |
| cos, sin = cos.unsqueeze(1), sin.unsqueeze(1) | |
| return (x * cos) + (rotate_half(x) * sin) | |
| class GatedAttention(nn.Module): | |
| """YModel32 gated grouped-query attention with Q/K RMS normalization.""" | |
| def __init__(self, config: YConfig32, layer_id: int): | |
| super().__init__() | |
| del layer_id | |
| self.hidden_size = config.hidden_size | |
| self.num_heads = int(config.num_heads) | |
| self.num_kv_heads = int(config.num_key_value_heads or self.num_heads) | |
| if self.num_heads % self.num_kv_heads: | |
| raise ValueError("num_heads must be divisible by num_key_value_heads") | |
| self.num_kv_groups = self.num_heads // self.num_kv_heads | |
| self.dropout = config.dropout | |
| self.head_dim = int(config.head_dim) | |
| self.softmax_scale = self.head_dim**-0.5 | |
| self.out_dim = self.num_heads * self.head_dim | |
| self.q_proj = nn.Linear(self.hidden_size, self.out_dim, bias=False) | |
| self.gate_proj = nn.Linear(self.hidden_size, self.out_dim, bias=False) | |
| self.k_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.head_dim, bias=False) | |
| self.v_proj = nn.Linear(self.hidden_size, self.num_kv_heads * self.head_dim, bias=False) | |
| self.o_proj = nn.Linear(self.out_dim, self.hidden_size, bias=False) | |
| self.q_norm = RMSNorm(self.head_dim, config.rms_norm_eps) | |
| self.k_norm = RMSNorm(self.head_dim, config.rms_norm_eps) | |
| def _load_from_state_dict(self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs): | |
| q_key, gate_key = prefix + "q_proj.weight", prefix + "gate_proj.weight" | |
| legacy_qg = state_dict.get(q_key) | |
| if legacy_qg is not None and legacy_qg.shape[0] == self.out_dim * 2 and gate_key not in state_dict: | |
| legacy_qg = legacy_qg.reshape(self.num_heads, 2, self.head_dim, self.hidden_size) | |
| state_dict[q_key] = legacy_qg[:, 0].reshape(self.out_dim, self.hidden_size) | |
| state_dict[gate_key] = legacy_qg[:, 1].reshape(self.out_dim, self.hidden_size) | |
| super()._load_from_state_dict( | |
| state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs | |
| ) | |
| def _repeat_kv(self, x: torch.Tensor) -> torch.Tensor: | |
| if self.num_kv_groups == 1: | |
| return x | |
| bsz, num_kv_heads, seq_len, head_dim = x.shape | |
| x = x[:, :, None, :, :].expand(bsz, num_kv_heads, self.num_kv_groups, seq_len, head_dim) | |
| return x.reshape(bsz, self.num_heads, seq_len, head_dim) | |
| def _attention_mask(self, attention_mask: Optional[torch.Tensor], bsz: int, seq_len: int, total_len: int): | |
| if attention_mask is None: | |
| return None | |
| if attention_mask.shape[-1] != total_len: | |
| attention_mask = attention_mask[..., -total_len:] | |
| return attention_mask.reshape(bsz, 1, 1, total_len).bool().expand(bsz, self.num_heads, seq_len, total_len) | |
| def forward( | |
| self, | |
| x: torch.Tensor, | |
| position_embeddings: tuple[torch.Tensor, torch.Tensor], | |
| past_key_values: Optional[tuple[torch.Tensor, torch.Tensor]] = None, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| use_cache: bool = False, | |
| **kwargs, | |
| ) -> tuple[torch.Tensor, Optional[tuple[torch.Tensor, torch.Tensor]]]: | |
| del kwargs | |
| bsz, seq_len, _ = x.shape | |
| cos, sin = position_embeddings | |
| if cos.dim() == 2: | |
| cos, sin = cos[:seq_len, : self.head_dim], sin[:seq_len, : self.head_dim] | |
| else: | |
| cos, sin = cos[:, :seq_len, : self.head_dim], sin[:, :seq_len, : self.head_dim] | |
| q = self.q_proj(x).reshape(bsz, seq_len, self.num_heads, self.head_dim) | |
| gate = self.gate_proj(x) | |
| q = self.q_norm(q).permute(0, 2, 1, 3) | |
| k = self.k_norm( | |
| self.k_proj(x).reshape(bsz, seq_len, self.num_kv_heads, self.head_dim) | |
| ).permute(0, 2, 1, 3) | |
| v = self.v_proj(x).reshape(bsz, seq_len, self.num_kv_heads, self.head_dim).permute(0, 2, 1, 3) | |
| q = apply_rope_to_single(q, cos, sin).contiguous() | |
| k = apply_rope_to_single(k, cos, sin).contiguous() | |
| v = v.contiguous() | |
| if past_key_values is not None: | |
| past_k, past_v = past_key_values | |
| k, v = torch.cat([past_k, k], dim=2), torch.cat([past_v, v], dim=2) | |
| new_past = (k, v) if use_cache else None | |
| total_len = k.shape[2] | |
| k, v = self._repeat_kv(k).contiguous(), self._repeat_kv(v).contiguous() | |
| attn_mask = self._attention_mask(attention_mask, bsz, seq_len, total_len) | |
| is_causal = attention_mask is None and seq_len == total_len | |
| if q.dtype != v.dtype: | |
| q = q.to(dtype=v.dtype) | |
| if k.dtype != v.dtype: | |
| k = k.to(dtype=v.dtype) | |
| out = F.scaled_dot_product_attention( | |
| q, | |
| k, | |
| v, | |
| attn_mask=attn_mask, | |
| dropout_p=self.dropout if self.training else 0.0, | |
| is_causal=is_causal, | |
| scale=self.softmax_scale, | |
| ) | |
| out = out.permute(0, 2, 1, 3).reshape(bsz, seq_len, self.out_dim) | |
| out = self.o_proj(out * torch.sigmoid(gate) * 2.0) | |
| return F.dropout(out, p=self.dropout, training=self.training), new_past | |
| def resolve_layer_pattern(config: YConfig32) -> str: | |
| pattern = getattr(config, "layer_pattern", None) | |
| if pattern is None: | |
| pattern = "f" + "g" * max(0, int(config.num_hidden_layers) - 1) | |
| pattern = "".join(ch for ch in str(pattern).lower() if ch in {"f", "g"}) | |
| if not pattern or pattern[0] != "f": | |
| raise ValueError(f"YModel3.2 layer_pattern must start with a full layer, got {pattern!r}") | |
| return pattern | |
| class SwiGLU(nn.Module): | |
| def __init__(self, config: YConfig32, intermediate_size: Optional[int] = None): | |
| super().__init__() | |
| inter = intermediate_size or config.intermediate_size | |
| self.up_proj = nn.Linear(config.hidden_size, inter, bias=False) | |
| self.gate_proj = nn.Linear(config.hidden_size, inter, bias=False) | |
| self.down_proj = nn.Linear(inter, config.hidden_size, bias=False) | |
| self.dropout = config.dropout | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| up, gate = self.up_proj(x), self.gate_proj(x) | |
| return self.down_proj(F.dropout(F.silu(gate) * up, p=self.dropout, training=self.training)) | |
| class SEBlock(nn.Module): | |
| def __init__(self, dim: int, reduction: int = 16): | |
| super().__init__() | |
| reduction = max(reduction, dim // reduction) | |
| self.se = nn.Sequential( | |
| nn.Linear(dim, reduction, bias=False), | |
| nn.SiLU(), | |
| nn.Linear(reduction, dim, bias=False), | |
| nn.Sigmoid(), | |
| ) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return x * (2.0 * self.se(x)) | |
| class SengramIndexer(nn.Module): | |
| """Inference routing only: choose bucket top-k and normalized scores.""" | |
| def __init__(self, config: YConfig32): | |
| super().__init__() | |
| self.hidden_size = int(config.hidden_size) | |
| self.bucket_size = int(config.sengram_bucket_size or 4096) | |
| self.topk = max(1, min(int(config.sengram_topk), self.bucket_size)) | |
| self.bucket_proj = nn.Linear(self.hidden_size, self.bucket_size, bias=False) | |
| def forward(self, hidden_states: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: | |
| router_input = hidden_states.float() | |
| router_input = router_input * torch.rsqrt(router_input.square().mean(dim=-1, keepdim=True) + 1e-6) | |
| bucket_logits = self.bucket_proj(router_input.to(dtype=hidden_states.dtype)) | |
| scores = torch.sigmoid(bucket_logits.float()) | |
| _, topk_ids = torch.topk(scores, k=self.topk, dim=-1, sorted=False) | |
| topk_scores = scores.gather(-1, topk_ids) | |
| topk_scores = topk_scores / topk_scores.sum(dim=-1, keepdim=True).clamp_min(1e-20) | |
| return topk_ids, topk_scores.to(bucket_logits.dtype) | |
| class SengramPLE(nn.Module): | |
| def __init__(self, config: YConfig32): | |
| super().__init__() | |
| self.hidden_size = int(config.hidden_size) | |
| self.embedding = nn.Embedding(int(config.sengram_bucket_size or 4096), self.hidden_size) | |
| def forward(self, hidden_states: torch.Tensor, topk_ids: torch.Tensor, topk_scores: torch.Tensor) -> torch.Tensor: | |
| topk_embed = self.embedding(topk_ids) | |
| return (topk_embed * topk_scores.unsqueeze(-1)).sum(dim=-2) | |
| class DynamicCompressionProbe(nn.Module): | |
| """Inference probe used only when DCP controls token-to-group assignment.""" | |
| def __init__(self, config: YConfig32): | |
| super().__init__() | |
| hidden_size = int(config.hidden_size) | |
| probe_size = int(config.dynamic_compression_probe_hidden_size) | |
| self.norm = RMSNorm(hidden_size, config.rms_norm_eps) | |
| self.gate_proj = nn.Linear(hidden_size, probe_size, bias=False) | |
| self.up_proj = nn.Linear(hidden_size, probe_size, bias=False) | |
| self.down_proj = nn.Linear(probe_size, 1, bias=True) | |
| def forward(self, hidden: torch.Tensor) -> torch.Tensor: | |
| hidden = self.norm(hidden.detach()) | |
| hidden = F.silu(self.gate_proj(hidden)) * self.up_proj(hidden) | |
| return self.down_proj(hidden).squeeze(-1) | |
| class G2F(nn.Module): | |
| """Add shifted group memory directly to the full-token stream.""" | |
| def __init__(self, config: YConfig32): | |
| super().__init__() | |
| del config | |
| def forward(self, hidden: torch.Tensor, memory: torch.Tensor) -> torch.Tensor: | |
| return hidden + memory | |
| class F2G(nn.Module): | |
| """Compress full-token states into groups, optionally DCP-weighted.""" | |
| def __init__(self, config: YConfig32): | |
| super().__init__() | |
| self.hidden_size = int(config.hidden_size) | |
| self.group_size = _normalize_group_sizes(config.group_size)[0] | |
| def _aligned_weights(self, importance_logits, tokens: int, device: torch.device): | |
| if importance_logits is None: | |
| return None | |
| logits = importance_logits.to(device=device) | |
| if logits.ndim != 2: | |
| raise ValueError("DCP importance logits must have shape [batch, tokens]") | |
| required = max(0, tokens - 1) | |
| logits = logits[:, :required] | |
| if logits.shape[1] < required: | |
| logits = F.pad(logits, (0, required - logits.shape[1]), value=0.0) | |
| return torch.sigmoid(logits.float()) | |
| def _weighted_reduce(self, h, attention_mask, group_ids, num_groups, importance_logits=None): | |
| bsz, tokens, hidden = h.shape | |
| if group_ids.shape != (bsz, tokens): | |
| raise ValueError(f"group_ids shape {tuple(group_ids.shape)} does not match hidden shape {(bsz, tokens)}") | |
| if attention_mask.shape != (bsz, tokens): | |
| raise ValueError(f"attention_mask shape {tuple(attention_mask.shape)} does not match hidden shape {(bsz, tokens)}") | |
| rest_h, rest_ids = h[:, 1:], group_ids[:, 1:] | |
| rest_valid = attention_mask[:, 1:].bool().reshape(-1) | |
| offsets = torch.arange(bsz, device=h.device, dtype=torch.int64).unsqueeze(1) * int(num_groups) | |
| flat_ids = (rest_ids.to(torch.int64) + offsets).reshape(-1) | |
| flat_h = rest_h.reshape(-1, hidden) | |
| aligned_weights = self._aligned_weights(importance_logits, tokens, h.device) | |
| if aligned_weights is None: | |
| flat_weights = torch.ones((bsz, max(0, tokens - 1)), device=h.device, dtype=torch.float32).reshape(-1) | |
| else: | |
| flat_weights = aligned_weights.reshape(-1) | |
| flat_ids, flat_h, flat_weights = flat_ids[rest_valid], flat_h[rest_valid], flat_weights[rest_valid] | |
| weighted = h.new_zeros((bsz * int(num_groups), hidden)) | |
| weighted.index_add_(0, flat_ids, flat_h * flat_weights.to(h.dtype).unsqueeze(-1)) | |
| denominators = torch.zeros((bsz * int(num_groups), 1), device=h.device, dtype=torch.float32) | |
| denominators.index_add_(0, flat_ids, flat_weights.unsqueeze(-1)) | |
| compressed = weighted / denominators.clamp_min(1e-12).to(h.dtype) | |
| compressed = torch.where(denominators.gt(0.0), compressed, torch.zeros_like(compressed)) | |
| compressed = compressed.reshape(bsz, int(num_groups), hidden) | |
| first = h[:, :1] * attention_mask[:, :1].to(h.dtype).unsqueeze(-1) | |
| return torch.cat([first, compressed[:, 1:]], dim=1) | |
| def forward( | |
| self, | |
| h: torch.Tensor, | |
| attention_mask: torch.Tensor, | |
| group_ids: Optional[torch.Tensor] = None, | |
| num_groups: Optional[int] = None, | |
| group_size: Optional[int] = None, | |
| importance_logits: Optional[torch.Tensor] = None, | |
| ) -> torch.Tensor: | |
| if group_ids is not None: | |
| if num_groups is None: | |
| raise ValueError("indexed F2G requires num_groups") | |
| return self.forward_indexed(h, attention_mask, group_ids, num_groups, importance_logits) | |
| group_size = self.group_size if group_size is None else int(group_size) | |
| bsz, padded_len, _ = h.shape | |
| rest_len = max(0, padded_len - 1) | |
| num_groups = 1 + (rest_len + group_size - 1) // group_size | |
| positions = torch.arange(padded_len, device=h.device, dtype=torch.int64) | |
| fixed_ids = torch.zeros((bsz, padded_len), device=h.device, dtype=torch.int64) | |
| if padded_len > 1: | |
| fixed_ids[:, 1:] = (positions[1:] - 1) // group_size + 1 | |
| return self._weighted_reduce(h, attention_mask, fixed_ids, num_groups, importance_logits) | |
| def forward_indexed(self, h, attention_mask, group_ids, num_groups, importance_logits=None): | |
| return self._weighted_reduce(h, attention_mask, group_ids, int(num_groups), importance_logits) | |
| class FullBlock(nn.Module): | |
| def __init__(self, config: YConfig32, layer_id: int, has_group_context: bool): | |
| super().__init__() | |
| self.has_group_context = has_group_context | |
| self.group_size = _normalize_group_sizes(config.group_size)[0] | |
| self.input_layernorm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.post_attention_layernorm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.g2f = G2F(config) if has_group_context else None | |
| self.self_attn = GatedAttention(config, layer_id) | |
| self.mlp = SwiGLU(config) | |
| def attn(self): | |
| return self.self_attn | |
| def ffn(self): | |
| return self.mlp | |
| def _add_prev_group_residual(self, h, g, group_attention_mask=None, token_group_ids=None, group_size=None): | |
| if token_group_ids is not None: | |
| previous = (token_group_ids - 1).clamp_min(0) | |
| memory = g.gather(1, previous.unsqueeze(-1).expand(-1, -1, g.shape[-1])) | |
| memory = memory * token_group_ids.gt(0).unsqueeze(-1).to(memory.dtype) | |
| return self.g2f(h, memory) | |
| group_size = self.group_size if group_size is None else int(group_size) | |
| bsz, padded_len, hidden = h.shape | |
| rest_len = max(0, padded_len - 1) | |
| rest_groups = (rest_len + group_size - 1) // group_size | |
| groups = 1 + rest_groups | |
| if g.shape[1] != groups or rest_groups == 0: | |
| return h | |
| grouped_h = h[:, 1:].reshape(bsz, rest_groups, group_size, hidden) | |
| memory = g[:, :-1, :].unsqueeze(2) | |
| if group_attention_mask is not None: | |
| memory = memory * group_attention_mask[:, :-1].to(g.dtype).reshape(bsz, rest_groups, 1, 1) | |
| fused = self.g2f(grouped_h, memory).reshape(bsz, rest_groups * group_size, hidden) | |
| return torch.cat([h[:, :1], fused[:, :rest_len]], dim=1) | |
| def forward(self, h, g, token_position_embeddings, group_attention_mask=None, token_group_ids=None, group_size=None): | |
| if self.has_group_context and g is not None: | |
| h = self._add_prev_group_residual(h, g, group_attention_mask, token_group_ids, group_size) | |
| attn_out, _ = self.self_attn(self.input_layernorm(h), token_position_embeddings) | |
| h = h + attn_out | |
| return h + self.mlp(self.post_attention_layernorm(h)) | |
| class GroupBlock(nn.Module): | |
| def __init__(self, config: YConfig32, layer_id: int): | |
| super().__init__() | |
| self.use_sengram = bool(config.use_sengram) | |
| self.input_layernorm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.post_attention_layernorm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.sengram_ple = SengramPLE(config) if self.use_sengram else None | |
| self.self_attn = GatedAttention(config, layer_id) | |
| self.mlp = SwiGLU(config) | |
| self.se1 = SEBlock(config.hidden_size) | |
| self.se2 = SEBlock(config.hidden_size) | |
| def attn(self): | |
| return self.self_attn | |
| def ffn(self): | |
| return self.mlp | |
| def forward(self, g, group_routes, group_position_embeddings): | |
| if self.use_sengram and group_routes is not None and self.sengram_ple is not None: | |
| g = g + self.sengram_ple(g, group_routes[0], group_routes[1]) | |
| g0 = self.se1(self.input_layernorm(g)) | |
| attn_out, _ = self.self_attn(g0, group_position_embeddings) | |
| g = g + attn_out | |
| return g + self.mlp(self.se2(self.post_attention_layernorm(g))) | |
| class YModel32(nn.Module): | |
| """YModel3.2 backbone.""" | |
| def __init__(self, config: YConfig32): | |
| super().__init__() | |
| self.config = config | |
| self.group_size_options = _normalize_group_sizes(config.group_size) | |
| self.group_size = self.group_size_options[0] | |
| self.max_group_size = max(self.group_size_options) | |
| self.active_group_size = self.group_size | |
| self.layer_pattern = resolve_layer_pattern(config) | |
| self.num_layers = len(self.layer_pattern) | |
| self.dropout = config.dropout | |
| self.use_sengram = bool(config.use_sengram) | |
| self.use_dcp = bool(config.use_dcp) | |
| self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) | |
| self.sengram_indexer = SengramIndexer(config) if self.use_sengram else None | |
| # Kept in the state dict even when DCP is disabled so checkpoint | |
| # loading never drops learned parameters. | |
| self.dynamic_compression_probe = DynamicCompressionProbe(config) | |
| f2g_count = 0 | |
| in_g = False | |
| for kind in self.layer_pattern: | |
| if kind == "g" and not in_g: | |
| f2g_count += 1 | |
| in_g = True | |
| elif kind == "f": | |
| in_g = False | |
| self.f2g_layers = nn.ModuleList([F2G(config) for _ in range(f2g_count)]) | |
| self.f2g_for_full = [] | |
| f2g_ptr = 0 | |
| for layer_pos, kind in enumerate(self.layer_pattern): | |
| if kind != "f": | |
| continue | |
| has_group_run = layer_pos + 1 < len(self.layer_pattern) and self.layer_pattern[layer_pos + 1] == "g" | |
| self.f2g_for_full.append(f2g_ptr if has_group_run else -1) | |
| if has_group_run: | |
| f2g_ptr += 1 | |
| layers = [] | |
| seen_full = 0 | |
| for layer_id, kind in enumerate(self.layer_pattern): | |
| if kind == "f": | |
| layers.append(FullBlock(config, layer_id, has_group_context=seen_full > 0)) | |
| seen_full += 1 | |
| else: | |
| layers.append(GroupBlock(config, layer_id)) | |
| self.layers = nn.ModuleList(layers) | |
| self.norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| freqs_cos, freqs_sin = precompute_freqs_cis( | |
| config.head_dim, | |
| config.max_position_embeddings + self.max_group_size, | |
| config.rope_theta, | |
| config.rope_scaling, | |
| ) | |
| self.register_buffer("freqs_cos", freqs_cos, persistent=False) | |
| self.register_buffer("freqs_sin", freqs_sin, persistent=False) | |
| def sengram(self): | |
| return self.sengram_indexer | |
| def _sample_group_size(self) -> int: | |
| # Deterministic at inference: uses the first configured size. | |
| return self.group_size_options[0] | |
| def _pad_to_groups(self, input_ids, attention_mask, group_size, pad_to_groups=True): | |
| bsz, seq_len = input_ids.shape | |
| rest_len = max(0, seq_len - 1) | |
| pad_len = (-rest_len) % group_size if pad_to_groups else 0 | |
| if pad_len: | |
| pad_id = self.config.pad_token_id if self.config.pad_token_id < self.config.vocab_size else 0 | |
| input_ids = torch.cat([input_ids, input_ids.new_full((bsz, pad_len), pad_id)], dim=1) | |
| if attention_mask is not None: | |
| attention_mask = torch.cat([attention_mask, attention_mask.new_zeros((bsz, pad_len))], dim=1) | |
| if attention_mask is None: | |
| pad_id = self.config.pad_token_id if self.config.pad_token_id < self.config.vocab_size else -1 | |
| attention_mask = input_ids.ne(pad_id).long() | |
| return input_ids, attention_mask, seq_len | |
| def _group_count(self, padded_len: int, group_size: int) -> int: | |
| return 1 + max(0, padded_len - 1) // group_size | |
| def _group_attention_mask(self, attention_mask, group_size): | |
| bsz, padded_len = attention_mask.shape | |
| rest_len = max(0, padded_len - 1) | |
| rest_groups = (rest_len + group_size - 1) // group_size | |
| first = attention_mask[:, :1] | |
| if rest_groups == 0: | |
| return first.long() | |
| rest_mask = attention_mask[:, 1:] | |
| pad_len = rest_groups * group_size - rest_len | |
| if pad_len: | |
| rest_mask = torch.cat([rest_mask, rest_mask.new_zeros((bsz, pad_len))], dim=1) | |
| return torch.cat([first, rest_mask.reshape(bsz, rest_groups, group_size).any(dim=-1).long()], dim=1) | |
| def _group_positions(self, groups: int, device: torch.device, group_size: int): | |
| if groups <= 1: | |
| return torch.zeros((groups,), device=device, dtype=torch.float32) | |
| rest = 1.0 + (torch.arange(groups - 1, device=device, dtype=torch.float32) + 0.5) * group_size | |
| return torch.cat([rest.new_zeros((1,)), rest], dim=0) | |
| def _dcp_group_ids(self, probe_logits, attention_mask, group_size=None): | |
| bsz, tokens = probe_logits.shape | |
| group_ids = torch.zeros((bsz, tokens), device=probe_logits.device, dtype=torch.int64) | |
| if tokens <= 1: | |
| return group_ids, 1 | |
| group_size = self.group_size if group_size is None else int(group_size) | |
| target_groups = max(1, (tokens + group_size - 1) // group_size) | |
| target_rest_groups = target_groups - 1 | |
| if target_rest_groups == 0: | |
| return group_ids, target_groups | |
| score = torch.sigmoid(probe_logits[:, :-1]).float() | |
| score = score * attention_mask[:, 1:].to(score.dtype) | |
| budget = float(group_size) * 0.5 | |
| raw_integral = score.sum(dim=1, keepdim=True) / budget | |
| scale = target_rest_groups / raw_integral.clamp_min(1e-6) | |
| scaled_score = score * scale | |
| integral_before_token = (torch.cumsum(scaled_score, dim=1) - scaled_score) / budget | |
| integral_bucket = torch.floor(integral_before_token).to(torch.int64) | |
| integral_bucket = integral_bucket.clamp_(0, target_rest_groups - 1) | |
| valid_rest = attention_mask[:, 1:].bool() | |
| rest_positions = torch.arange(tokens - 1, device=probe_logits.device).unsqueeze(0) | |
| last_valid = rest_positions.masked_fill(~valid_rest, -1).amax(dim=1) | |
| safe_last_valid = last_valid.clamp_min(0) | |
| rows = torch.arange(bsz, device=probe_logits.device) | |
| current_last_bucket = integral_bucket[rows, safe_last_valid] | |
| final_bucket = torch.where( | |
| last_valid.ge(0), | |
| torch.full_like(current_last_bucket, target_rest_groups - 1), | |
| current_last_bucket, | |
| ) | |
| integral_bucket[rows, safe_last_valid] = final_bucket | |
| group_ids[:, 1:] = integral_bucket + 1 | |
| return group_ids, target_groups | |
| def _indexed_group_metadata(self, group_ids, attention_mask, num_groups): | |
| bsz, tokens = group_ids.shape | |
| offsets = torch.arange(bsz, device=group_ids.device, dtype=torch.int64).unsqueeze(1) * num_groups | |
| flat_ids = (group_ids + offsets).reshape(-1) | |
| valid = attention_mask.reshape(-1).bool() | |
| counts = torch.zeros(bsz * num_groups, device=group_ids.device, dtype=torch.float32) | |
| counts.index_add_(0, flat_ids[valid], torch.ones_like(flat_ids[valid], dtype=torch.float32)) | |
| positions = torch.arange(tokens, device=group_ids.device, dtype=torch.float32).expand(bsz, -1).reshape(-1) | |
| position_sums = torch.zeros_like(counts) | |
| position_sums.index_add_(0, flat_ids[valid], positions[valid]) | |
| group_mask = counts.reshape(bsz, num_groups).gt(0) | |
| centers = (position_sums / counts.clamp_min(1.0)).reshape(bsz, num_groups) | |
| return group_mask.long(), centers | |
| def forward( | |
| self, | |
| input_ids: Optional[torch.Tensor] = None, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| position_ids: Optional[torch.LongTensor] = None, | |
| **kwargs, | |
| ) -> torch.Tensor: | |
| del kwargs | |
| if input_ids is None: | |
| raise ValueError("input_ids is required") | |
| group_size = self._sample_group_size() | |
| self.active_group_size = group_size | |
| input_ids, attention_mask, orig_len = self._pad_to_groups( | |
| input_ids, attention_mask, group_size, pad_to_groups=not self.use_dcp | |
| ) | |
| bsz, padded_len = input_ids.shape | |
| h = F.dropout(self.embed_tokens(input_ids), p=self.dropout, training=False) | |
| if position_ids is None: | |
| position_ids = torch.arange(padded_len, device=input_ids.device) | |
| token_position_embeddings = ( | |
| self.freqs_cos[position_ids[:padded_len]].to(h.device), | |
| self.freqs_sin[position_ids[:padded_len]].to(h.device), | |
| ) | |
| groups = self._group_count(padded_len, group_size) | |
| group_attention_mask = self._group_attention_mask(attention_mask, group_size) | |
| group_center_positions = self._group_positions(groups, input_ids.device, group_size) | |
| group_position_embeddings = rope_embeddings_from_positions( | |
| group_center_positions, | |
| self.config.head_dim, | |
| self.config.rope_theta, | |
| self.config.rope_scaling, | |
| self.config.max_position_embeddings, | |
| ) | |
| group_position_embeddings = (group_position_embeddings[0].to(h.device), group_position_embeddings[1].to(h.device)) | |
| g = None | |
| pending_group_update = None | |
| group_routes = None | |
| token_group_ids = None | |
| dcp_num_groups = None | |
| full_idx = 0 | |
| difficulty_logits = None | |
| for layer_pos, kind in enumerate(self.layer_pattern): | |
| if kind == "f": | |
| h = self.layers[layer_pos]( | |
| h, | |
| g, | |
| token_position_embeddings, | |
| group_attention_mask=group_attention_mask, | |
| token_group_ids=token_group_ids, | |
| group_size=group_size, | |
| ) | |
| if difficulty_logits is None and self.use_dcp: | |
| difficulty_logits = self.dynamic_compression_probe(h) | |
| token_group_ids, dcp_num_groups = self._dcp_group_ids( | |
| difficulty_logits, attention_mask, group_size | |
| ) | |
| group_attention_mask, group_center_positions = self._indexed_group_metadata( | |
| token_group_ids, attention_mask, dcp_num_groups | |
| ) | |
| group_position_embeddings = rope_embeddings_from_positions( | |
| group_center_positions, | |
| self.config.head_dim, | |
| self.config.rope_theta, | |
| self.config.rope_scaling, | |
| self.config.max_position_embeddings, | |
| ) | |
| group_position_embeddings = ( | |
| group_position_embeddings[0].reshape(bsz, dcp_num_groups, -1).to(h.device), | |
| group_position_embeddings[1].reshape(bsz, dcp_num_groups, -1).to(h.device), | |
| ) | |
| groups = dcp_num_groups | |
| f2g_idx = self.f2g_for_full[full_idx] | |
| if f2g_idx >= 0: | |
| if token_group_ids is not None and dcp_num_groups is not None: | |
| pending_group_update = self.f2g_layers[f2g_idx]( | |
| h, | |
| attention_mask, | |
| token_group_ids, | |
| dcp_num_groups, | |
| importance_logits=difficulty_logits, | |
| ) | |
| else: | |
| pending_group_update = self.f2g_layers[f2g_idx]( | |
| h, | |
| attention_mask, | |
| group_size=group_size, | |
| importance_logits=difficulty_logits, | |
| ) | |
| if self.sengram_indexer is not None and pending_group_update is not None: | |
| group_routes = self.sengram_indexer(pending_group_update) | |
| full_idx += 1 | |
| else: | |
| if pending_group_update is not None: | |
| g = pending_group_update if g is None else g + pending_group_update | |
| pending_group_update = None | |
| elif g is None: | |
| g = h.new_zeros(h.shape[0], groups, h.shape[-1]) | |
| g = self.layers[layer_pos](g, group_routes, group_position_embeddings) | |
| return self.norm(h)[:, :orig_len] | |
| class YForCausalLM32(PreTrainedModel, GenerationMixin): | |
| config_class = YConfig32 | |
| base_model_prefix = "model" | |
| _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"} | |
| def __init__(self, config: Optional[YConfig32] = None): | |
| config = config or YConfig32() | |
| super().__init__(config) | |
| self.model = YModel32(config) | |
| self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) | |
| if config.tie_word_embeddings: | |
| self.model.embed_tokens.weight = self.lm_head.weight | |
| dtype_name = config.dtype | |
| if dtype_name not in {"float16", "bfloat16", "float32"}: | |
| hf_dtype = getattr(config, "torch_dtype", None) | |
| dtype_name = str(hf_dtype).replace("torch.", "") if hf_dtype is not None else "float32" | |
| dtype = {"float16": torch.float16, "bfloat16": torch.bfloat16, "float32": torch.float32}.get(dtype_name) | |
| if dtype is not None: | |
| self.to(dtype) | |
| def forward( | |
| self, | |
| input_ids: Optional[torch.Tensor] = None, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| logits_to_keep: Union[int, torch.Tensor] = 0, | |
| **kwargs, | |
| ) -> CausalLMOutputWithPast: | |
| position_ids = kwargs.pop("position_ids", None) | |
| # This architecture currently recomputes the full prefix. Cache inputs | |
| # are accepted for API compatibility but are not consumed. | |
| kwargs.pop("past_key_values", None) | |
| kwargs.pop("use_cache", None) | |
| kwargs.pop("cache_position", None) | |
| hidden = self.model(input_ids, attention_mask, position_ids=position_ids) | |
| indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep | |
| output = CausalLMOutputWithPast( | |
| logits=self.lm_head(hidden[:, indices, :]), | |
| past_key_values=None, | |
| ) | |
| output["last_hidden_state"] = hidden | |
| return output | |
| def generate( | |
| self, | |
| inputs=None, | |
| attention_mask=None, | |
| max_new_tokens=8192, | |
| temperature=0.85, | |
| top_p=0.85, | |
| top_k=50, | |
| eos_token_id=None, | |
| streamer=None, | |
| use_cache=False, | |
| num_return_sequences=1, | |
| do_sample=True, | |
| repetition_penalty=1.0, | |
| **kwargs, | |
| ): | |
| del use_cache | |
| if inputs is None: | |
| inputs = kwargs.pop("input_ids", None) | |
| if inputs is None: | |
| raise ValueError("inputs or input_ids must be provided") | |
| input_ids = inputs.repeat(num_return_sequences, 1) | |
| attention_mask = attention_mask.repeat(num_return_sequences, 1) if attention_mask is not None else None | |
| logits_processor = kwargs.get("logits_processor") | |
| eos_token_id = self.config.eos_token_id if eos_token_id is None else eos_token_id | |
| if streamer is not None: | |
| streamer.put(input_ids.cpu()) | |
| with torch.inference_mode(): | |
| for _ in range(max_new_tokens): | |
| outputs = self.forward( | |
| input_ids=input_ids, | |
| attention_mask=attention_mask, | |
| logits_to_keep=1, | |
| use_cache=False, | |
| ) | |
| logits = outputs.logits[:, -1, :] | |
| if temperature and temperature > 0: | |
| logits = logits / temperature | |
| if repetition_penalty != 1.0: | |
| for i in range(input_ids.shape[0]): | |
| seen = torch.unique(input_ids[i]) | |
| logits[i, seen] = torch.where( | |
| logits[i, seen] < 0, | |
| logits[i, seen] * repetition_penalty, | |
| logits[i, seen] / repetition_penalty, | |
| ) | |
| if logits_processor is not None: | |
| logits = logits_processor(input_ids, logits) | |
| if top_k > 0: | |
| k = min(int(top_k), logits.shape[-1]) | |
| cutoff = torch.topk(logits, k, dim=-1)[0][..., -1, None] | |
| logits = logits.masked_fill(logits < cutoff, -float("inf")) | |
| if top_p < 1.0: | |
| sorted_logits, sorted_indices = torch.sort(logits, descending=True) | |
| sorted_mask = torch.cumsum(torch.softmax(sorted_logits, dim=-1), dim=-1) > top_p | |
| sorted_mask[..., 1:] = sorted_mask[..., :-1].clone() | |
| sorted_mask[..., 0] = False | |
| remove_mask = torch.zeros_like(sorted_mask).scatter(1, sorted_indices, sorted_mask) | |
| logits = logits.masked_fill(remove_mask, -float("inf")) | |
| next_token = ( | |
| torch.multinomial(torch.softmax(logits, dim=-1), 1) | |
| if do_sample | |
| else torch.argmax(logits, dim=-1, keepdim=True) | |
| ) | |
| input_ids = torch.cat([input_ids, next_token], dim=-1) | |
| if attention_mask is not None: | |
| attention_mask = torch.cat( | |
| [attention_mask, attention_mask.new_ones((attention_mask.shape[0], 1))], dim=-1 | |
| ) | |
| if streamer is not None: | |
| streamer.put(next_token.cpu()) | |
| if eos_token_id is not None and bool((next_token == eos_token_id).all()): | |
| break | |
| if streamer is not None: | |
| streamer.end() | |
| return input_ids | |
| def count_parameters(config: YConfig32) -> int: | |
| return sum(parameter.numel() for parameter in YForCausalLM32(config).parameters()) | |
| def _state_dict_from_checkpoint(payload) -> dict[str, torch.Tensor]: | |
| if not isinstance(payload, dict): | |
| state_dict = getattr(payload, "state_dict", None) | |
| if callable(state_dict): | |
| payload = state_dict() | |
| else: | |
| raise TypeError("checkpoint must contain a PyTorch state-dict mapping") | |
| for key in ("state_dict", "model_state_dict", "model", "student", "student_state_dict"): | |
| nested = payload.get(key) | |
| if isinstance(nested, dict) and nested and all(isinstance(k, str) for k in nested): | |
| if any(torch.is_tensor(value) for value in nested.values()): | |
| payload = nested | |
| break | |
| if not payload or not all(isinstance(key, str) for key in payload): | |
| raise TypeError("checkpoint does not look like a PyTorch state dict") | |
| return payload | |
| def _load_state_dict(path: Union[str, Path]) -> dict[str, torch.Tensor]: | |
| path = Path(path) | |
| if path.is_dir(): | |
| candidates = ("sft.pt", "distill.pt", "model.pt", "pytorch_model.bin") | |
| path = next((path / name for name in candidates if (path / name).is_file()), None) | |
| if path is None: | |
| raise FileNotFoundError("no .pt/.bin PyTorch checkpoint found in the directory") | |
| if path.suffix.lower() == ".safetensors": | |
| raise ValueError(".safetensors loading is not included; export a PyTorch .pt state dict") | |
| try: | |
| payload = torch.load(path, map_location="cpu", weights_only=True) | |
| except TypeError: | |
| payload = torch.load(path, map_location="cpu") | |
| return _state_dict_from_checkpoint(payload) | |
| def _key_variants(key: str) -> tuple[str, ...]: | |
| variants = [key] | |
| current = key | |
| while current.startswith("module.") or current.startswith("_orig_mod."): | |
| current = current.split(".", 1)[1] | |
| variants.append(current) | |
| for source in tuple(variants): | |
| aliases = ( | |
| source.replace(".attn.", ".self_attn."), | |
| source.replace(".ffn.", ".mlp."), | |
| source.replace(".ffn.dense.", ".mlp."), | |
| source.replace("model.sengram.", "model.sengram_indexer."), | |
| ) | |
| variants.extend(aliases) | |
| # Some compiled checkpoints insert _orig_mod below the outer model prefix. | |
| variants.extend( | |
| key.replace("model._orig_mod.", "model.").replace("._orig_mod.", ".") | |
| for key in tuple(variants) | |
| ) | |
| return tuple(dict.fromkeys(variants)) | |
| def _load_compatible_weights(model: YForCausalLM32, source: dict[str, torch.Tensor], strict: bool = True): | |
| target = model.state_dict() | |
| normalized = {} | |
| for key, value in source.items(): | |
| if not torch.is_tensor(value): | |
| continue | |
| for variant in _key_variants(key): | |
| normalized.setdefault(variant, value) | |
| matched = {} | |
| shape_mismatches = [] | |
| for key, tensor in target.items(): | |
| found = next((normalized[name] for name in _key_variants(key) if name in normalized), None) | |
| if found is None: | |
| continue | |
| if tuple(found.shape) != tuple(tensor.shape): | |
| # Early checkpoints packed Q and gate projections into one | |
| # tensor; split them in the same head-major layout used by the | |
| # checkpoint loader. | |
| if ( | |
| key.endswith(".q_proj.weight") | |
| and found.ndim == 2 | |
| and found.shape[0] == tensor.shape[0] * 2 | |
| and found.shape[1] == tensor.shape[1] | |
| ): | |
| packed = found.reshape( | |
| model.config.num_heads, | |
| 2, | |
| model.config.head_dim, | |
| model.config.hidden_size, | |
| ) | |
| matched[key] = packed[:, 0].reshape_as(tensor) | |
| gate_key = key[: -len("q_proj.weight")] + "gate_proj.weight" | |
| matched[gate_key] = packed[:, 1].reshape_as(target[gate_key]) | |
| continue | |
| shape_mismatches.append((key, tuple(found.shape), tuple(tensor.shape))) | |
| continue | |
| matched[key] = found | |
| result = model.load_state_dict(matched, strict=False) | |
| missing = list(result.missing_keys) | |
| if model.config.tie_word_embeddings: | |
| tied = {"model.embed_tokens.weight", "lm_head.weight"} | |
| if tied.intersection(matched): | |
| missing = [key for key in missing if key not in tied] | |
| if shape_mismatches and strict: | |
| preview = "; ".join(f"{key}: checkpoint{src} != model{dst}" for key, src, dst in shape_mismatches[:8]) | |
| raise RuntimeError(f"checkpoint tensor shape mismatch: {preview}") | |
| if missing and strict: | |
| preview = ", ".join(missing[:20]) | |
| raise RuntimeError(f"checkpoint is missing {len(missing)} model tensors: {preview}") | |
| unique_target = {} | |
| for key, tensor in target.items(): | |
| identity = (tensor.device.type, tensor.data_ptr(), tuple(tensor.shape)) | |
| unique_target.setdefault(identity, []).append(key) | |
| loaded_unique = sum(any(key in matched for key in keys) for keys in unique_target.values()) | |
| print(f"loaded: {loaded_unique}/{len(unique_target)} unique model tensors") | |
| if shape_mismatches: | |
| print(f"shape-mismatched tensors ignored: {len(shape_mismatches)}") | |
| if missing: | |
| print(f"missing model tensors: {len(missing)}") | |
| return model | |
| def load_ymodel32_eval( | |
| path: Union[str, Path], | |
| config: Optional[YConfig32] = None, | |
| strict: bool = True, | |
| device: Union[str, torch.device] = "cpu", | |
| ) -> YForCausalLM32: | |
| """Load a standalone evaluator from a .pt/.bin state dict and config JSON.""" | |
| path = Path(path) | |
| if config is None: | |
| config_candidates = ( | |
| path / "config.json" if path.is_dir() else path.with_name("config.json"), | |
| path.parent.parent / "config.json" if not path.is_dir() else path.parent / "config.json", | |
| ) | |
| config_path = next((candidate for candidate in config_candidates if candidate.is_file()), None) | |
| if config_path is None: | |
| raise FileNotFoundError("pass config=YConfig32(...) or place config.json next to the checkpoint") | |
| config = YConfig32.from_json_file(str(config_path)) | |
| model = YForCausalLM32(config) | |
| model = _load_compatible_weights(model, _load_state_dict(path), strict=strict) | |
| return model.to(device).eval() | |
| YModel32Eval = YModel32 | |
| YForCausalLM32Eval = YForCausalLM32 | |
| __all__ = [ | |
| "DynamicCompressionProbe", | |
| "F2G", | |
| "FullBlock", | |
| "G2F", | |
| "GatedAttention", | |
| "GroupBlock", | |
| "RMSNorm", | |
| "SengramIndexer", | |
| "SengramPLE", | |
| "SwiGLU", | |
| "YConfig32", | |
| "YForCausalLM32", | |
| "YForCausalLM32Eval", | |
| "YModel32", | |
| "YModel32Eval", | |
| "apply_rope_to_single", | |
| "count_parameters", | |
| "load_ymodel32_eval", | |
| "precompute_freqs_cis", | |
| "resolve_layer_pattern", | |
| "resolve_rope_frequencies", | |
| "rope_embeddings_from_positions", | |
| ] | |