Felipe97/llama-cpp-compiled
01.1k
1from __future__ import annotations2 3import json4from typing import Any, Iterable, TYPE_CHECKING5 6import torch7 8if TYPE_CHECKING:9 from torch import Tensor10 11from .base import MmprojModel, ModelBase, TextModel, gguf12 13 14def _unpermute_for_rope(tensor: "Tensor", n_heads: int) -> "Tensor":15 """Invert transformers' `_permute_for_rope`: HF stores Q/K in rotate_half layout,16 llama.cpp consumes the interleaved (NORM) layout."""17 if tensor.ndim == 2:18 dim1, dim2 = tensor.shape19 return tensor.view(n_heads, 2, dim1 // n_heads // 2, dim2).transpose(1, 2).reshape(dim1, dim2)20 if tensor.ndim == 1:21 (dim1,) = tensor.shape22 return tensor.view(n_heads, 2, dim1 // n_heads // 2).transpose(1, 2).reshape(dim1)23 raise ValueError(f"_unpermute_for_rope: unexpected shape {tuple(tensor.shape)}")24 25 26@ModelBase.register("MuseGlimmerForConditionalGeneration")27@ModelBase.example("meta-models/Muse-Glimmer-30B")28class MuseGlimmerModel(TextModel):29 model_arch = gguf.MODEL_ARCH.MUSE_GLIMMER30 31 def norm_shift(self, name: str) -> float:32 # All four layer norms use 1, the final norm uses 0.33 return 1.0 if name.endswith("layernorm.weight") else 0.034 35 def set_vocab(self):36 self._set_vocab_gpt2()37 38 from transformers import AutoTokenizer39 tok = AutoTokenizer.from_pretrained(self.dir_model)40 eot_id = tok.convert_tokens_to_ids("<|eot|>") # ty: ignore[unresolved-attribute]41 if isinstance(eot_id, int) and eot_id >= 0:42 self.gguf_writer.add_eot_token_id(eot_id)43 44 def set_gguf_parameters(self):45 super().set_gguf_parameters()46 hparams = self.hparams47 48 self.gguf_writer.add_final_logit_softcapping(hparams["final_logit_softcapping"])49 self.gguf_writer.add_logit_scale(hparams["output_multiplier"])50 self.gguf_writer.add_sliding_window(hparams["sliding_window"])51 self.gguf_writer.add_sliding_window_pattern([t == "sliding_attention" for t in hparams["layer_types"]])52 53 def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:54 shift = self.norm_shift(name)55 if shift != 0.0:56 data_torch = data_torch + shift57 58 # Invert transformers' `_permute_for_rope` on Q/K, we keep ggml's NORM (interleaved) rope59 if ".self_attn.q_proj." in name:60 data_torch = _unpermute_for_rope(data_torch, int(self.hparams["num_attention_heads"]))61 elif ".self_attn.k_proj." in name:62 data_torch = _unpermute_for_rope(data_torch, int(self.hparams["num_key_value_heads"]))63 64 # Synthesize QK-norm weights to absorb qk_scale_factor.65 # MuseGlimmer implementation: scaleless RMSNorm followed by qk_scale_factor..66 if bid is not None and name.endswith(f"model.layers.{bid}.self_attn.q_proj.weight"):67 head_dim = self.hparams["head_dim"]68 q_scale = float(self.hparams["qk_scale_factor"])69 yield (70 self.map_tensor_name(f"model.layers.{bid}.self_attn.q_norm.weight"),71 torch.full((head_dim,), q_scale, dtype=torch.float32),72 )73 yield (74 self.map_tensor_name(f"model.layers.{bid}.self_attn.k_norm.weight"),75 torch.ones((head_dim,), dtype=torch.float32),76 )77 78 yield from super().modify_tensors(data_torch, name, bid)79 80 81@ModelBase.register("MuseGlimmerForConditionalGeneration")82@ModelBase.example("meta-models/Muse-Glimmer-30B")83class MuseGlimmerVisionModel(MmprojModel):84 def get_vision_config(self) -> dict[str, Any] | None:85 c = self.global_config.get("vision_config")86 if not c:87 return None88 # MuseGlimmer actually uses dynamic size, initialize with nominal size89 image_size = c["pos_emb_height"] * c["patch_size"] * c["merge_size"]90 return {**c, "image_size": image_size}91 92 def set_gguf_parameters(self):93 super().set_gguf_parameters()94 assert self.hparams_vision is not None95 c = self.hparams_vision # enriched vision_config from get_vision_config()96 97 self.gguf_writer.add_clip_projector_type(gguf.VisionProjectorType.MUSE_GLIMMER)98 self.gguf_writer.add_vision_attention_layernorm_eps(float(c["layer_norm_eps"]))99 self.gguf_writer.add_vision_spatial_merge_size(int(c["merge_size"]))100 101 @classmethod102 def filter_tensors(cls, item):103 name, gen = item104 keep = ("model.vision_tower.", "model.vision_adapter.", "model.vision_projection.")105 if not any(name.startswith(k) for k in keep):106 return None107 return super().filter_tensors((name, gen))108 109 # 3-layer projector MLP110 _MM_MLP_MAP = {111 "model.vision_adapter.fc1": (gguf.MODEL_TENSOR.V_MMPROJ, 0),112 "model.vision_adapter.fc2": (gguf.MODEL_TENSOR.V_MMPROJ, 1),113 "model.vision_projection": (gguf.MODEL_TENSOR.V_MMPROJ, 2),114 }115 116 def modify_tensors(self, data_torch, name, bid):117 assert self.hparams_vision is not None118 if ".attn.q_proj." in name or ".attn.k_proj." in name:119 n_heads = int(self.hparams_vision["num_attention_heads"])120 data_torch = _unpermute_for_rope(data_torch, n_heads)121 # Lay out the pt=2 temporal slabs of the patch embedding as a conv2d for build_inp()122 if name.endswith("patch_embedder.patch_embedding.weight"):123 n_embd = data_torch.shape[0]124 pt = int(self.hparams_vision["patch_temporal"])125 ps = int(self.hparams_vision["patch_size"])126 data_torch = data_torch.view(n_embd, pt, 3, ps, ps).sum(dim=1) # (n_embd, 3, ps, ps)127 stem, _, suffix = name.rpartition(".")128 if stem in self._MM_MLP_MAP:129 tensor_key, idx = self._MM_MLP_MAP[stem]130 yield (self.format_tensor_name(tensor_key, bid=idx, suffix="." + suffix), data_torch)131 return132 yield (self.map_tensor_name(name), data_torch)133 134 135@ModelBase.register("MuseGlimmerAssistantModel")136@ModelBase.example("meta-models/Muse-Glimmer-30B-assistant")137class MuseGlimmerAssistantModel(TextModel):138 model_arch = gguf.MODEL_ARCH.DFLASH139 140 def set_vocab(self):141 if self.target_model_dir is None:142 raise ValueError(143 "MuseGlimmerAssistant (DFlash drafter) requires --target-model-dir pointing to the "144 "target MuseGlimmer HF directory"145 )146 147 original_dir = self.dir_model148 self.dir_model = self.target_model_dir149 150 from . import get_model_class151 with open(self.target_model_dir / "config.json", "r", encoding="utf-8") as f:152 target_arch = json.load(f)["architectures"][0]153 target_cls = get_model_class(target_arch)154 if target_cls is not type(self):155 target_cls.set_vocab(self) # ty: ignore[unresolved-attribute]156 else:157 super().set_vocab()158 159 self.dir_model = original_dir160 161 mask_token_id = self.hparams.get("mask_token_id")162 if mask_token_id is not None:163 self.gguf_writer.add_mask_token_id(int(mask_token_id))164 165 def set_gguf_parameters(self):166 super().set_gguf_parameters()167 h = self.hparams168 169 self.gguf_writer.add_block_size(int(h["block_size"]))170 171 # dflash.target_layers[k] refers to the inputs going into the ith layer, which come from the (i-1)th layer's output.172 # The transformers configuration refers to the outputs being recorded.173 self.gguf_writer.add_target_layers([int(x) + 1 for x in h["target_layer_ids"]])174 175 if h.get("sliding_window") and h.get("layer_types"):176 self.gguf_writer.add_sliding_window(int(h["sliding_window"]))177 self.gguf_writer.add_sliding_window_pattern([t == "sliding_attention" for t in h["layer_types"]])178 179 def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:180 # DFlash defaults to NEOX (rotate_half) rope, matching transformers HF layout for Q/K, QK-norms181 # no permutation needed.182 yield (self.map_tensor_name(name), data_torch)183 