Brunobkr/llama.cpp_AlgMor24_github
ΩFFFΣLLIa • llama.cpp • AlgMor24 ██████╗ ███████╗███████╗███████╗██╗ ██╗ ██╗ █████╗ ██╔═══██╗██╔════╝██╔════╝██╔════╝██║ ██║ ██║██╔══██╗ ██║ ██║█████╗ █████╗ █████╗ ██║ ██║ ██║███████║ ██║ ██║██╔══╝ ██╔══╝ ██╔══╝ ██║ ██║ ██║██╔══██║ ╚██████╔╝██║ ██║ ███████╗███████╗███████╗██║██║ ██║ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝ High-Performance LLM / VLM Inference & Autonomous Agentic Ecosystem… See the full description on the dataset page: https://huggingface.co/datasets/Brunobkr/llama.cpp_AlgMor24_github.
03.1k
1from __future__ import annotations2 3from typing import Iterable, TYPE_CHECKING4 5import torch6 7if TYPE_CHECKING:8 from torch import Tensor9 10from .base import ModelBase, TextModel, gguf, logger11 12from .qwen import QwenModel13 14 15@ModelBase.register("KimiLinearModel", "KimiLinearForCausalLM")16class KimiLinearModel(TextModel):17 """Kimi-Linear model with hybrid MLA+KDA architecture"""18 model_arch = gguf.MODEL_ARCH.KIMI_LINEAR19 20 _experts: list[dict[str, Tensor]] | None = None21 22 def set_vocab(self):23 try:24 self._set_vocab_gpt2()25 return26 except Exception:27 pass28 29 from transformers import AutoTokenizer30 tokenizer = AutoTokenizer.from_pretrained(self.dir_model, trust_remote_code=True)31 tokpre = self.get_vocab_base_pre(tokenizer)32 33 if tokpre == "kimi-k2":34 # Build merges list using the approach similar to HunYuanMoE35 merges = []36 vocab = {}37 mergeable_ranks = tokenizer.model._mergeable_ranks # ty: ignore[unresolved-attribute]38 for token, rank in mergeable_ranks.items():39 vocab[QwenModel.token_bytes_to_string(token)] = rank40 if len(token) == 1:41 continue42 merged = QwenModel.bpe(mergeable_ranks, token, max_rank=rank)43 if len(merged) == 2:44 merges.append(' '.join(map(QwenModel.token_bytes_to_string, merged)))45 # Build token list46 vocab_size = self.hparams["vocab_size"]47 special_tokens = tokenizer.special_tokens # ty: ignore[unresolved-attribute]48 reverse_vocab = {id_ : encoded_tok for encoded_tok, id_ in {**vocab, **special_tokens}.items()}49 tokens: list[str] = []50 toktypes: list[int] = []51 52 for i in range(vocab_size):53 if i not in reverse_vocab:54 tokens.append(f"[PAD{i}]")55 toktypes.append(gguf.TokenType.UNUSED)56 else:57 token = reverse_vocab[i]58 tokens.append(token)59 if i in special_tokens.values():60 toktypes.append(gguf.TokenType.CONTROL)61 else:62 toktypes.append(gguf.TokenType.NORMAL)63 64 self.gguf_writer.add_tokenizer_model("gpt2")65 self.gguf_writer.add_tokenizer_pre(tokpre)66 self.gguf_writer.add_token_list(tokens)67 self.gguf_writer.add_token_types(toktypes)68 self.gguf_writer.add_token_merges(merges)69 70 special_vocab = gguf.SpecialVocab(self.dir_model, load_merges=False)71 special_vocab.add_to_gguf(self.gguf_writer)72 # override eos id in config.json with tiktoken eos id73 self.gguf_writer.add_eos_token_id(tokenizer.eos_id) # ty: ignore[unresolved-attribute]74 else:75 raise NotImplementedError(f"Deepseek pre-tokenizer {tokpre!r} is not supported yet!")76 77 def set_gguf_parameters(self):78 # note: To enable MLA KV cache, attention needs to be converted into MQA (ie: GQA with 1 group)79 self.hparams["num_key_value_heads"] = 180 81 super().set_gguf_parameters()82 self.gguf_writer.add_vocab_size(self.hparams["vocab_size"])83 84 # KDA & MLA params85 # Get ssm_d_conv from linear_attn_config.short_conv_kernel_size or ssm_d_conv86 linear_attn_config = self.hparams["linear_attn_config"]87 # n_head == 0 for KDA layers, n_head > 0 for MLA layers88 # full_attention_layers list will be used to distinguish layer type89 _num_kv_heads = list()90 _full_attn_layers = linear_attn_config["full_attn_layers"]91 for il in range(self.hparams["num_hidden_layers"]):92 if il + 1 in _full_attn_layers:93 _num_kv_heads.append(self.hparams["num_key_value_heads"])94 else:95 _num_kv_heads.append(0)96 assert len(_num_kv_heads) == self.hparams["num_hidden_layers"]97 self.gguf_writer.add_head_count_kv(_num_kv_heads)98 99 if (ssm_d_conv := linear_attn_config.get("short_conv_kernel_size")) is not None:100 self.gguf_writer.add_ssm_conv_kernel(ssm_d_conv)101 if (kda_head_dim := linear_attn_config.get("head_dim")) is not None:102 self.gguf_writer.add_kda_head_dim(kda_head_dim)103 104 # MLA params - use add_* methods that handle arch substitution105 # Support both HuggingFace naming (q_lora_rank, kv_lora_rank) and internal naming (n_lora_q, n_lora_kv)106 if (q_lora_rank := self.find_hparam(["q_lora_rank", "n_lora_q"], optional=True)) is not None:107 self.gguf_writer.add_q_lora_rank(q_lora_rank)108 # To enable MLA KV cache, MLA needs to be converted into MQA with larger heads, then decompresses to MHA109 kv_lora_rank = self.find_hparam(["kv_lora_rank", "n_lora_kv"], optional=False)110 self.gguf_writer.add_kv_lora_rank(kv_lora_rank)111 112 # MLA head dimensions113 # Support HuggingFace naming: qk_nope_head_dim, qk_rope_head_dim, v_head_dim114 qk_nope_head_dim = self.hparams.get("qk_nope_head_dim")115 # Rotation - use qk_rope_head_dim for Kimi116 qk_rope_head_dim = self.find_hparam(["qk_rope_head_dim", "n_rot"], optional=False)117 self.gguf_writer.add_rope_dimension_count(qk_rope_head_dim)118 self.gguf_writer.add_key_length(kv_lora_rank + qk_rope_head_dim)119 v_head_dim = self.hparams.get("v_head_dim")120 121 # Calculate n_embd_head_k_mla = qk_nope_head_dim + qk_rope_head_dim122 if (n_embd_head_k_mla := self.find_hparam(["n_embd_head_k_mla"], optional=True)) is not None:123 self.gguf_writer.add_key_length_mla(n_embd_head_k_mla)124 elif qk_nope_head_dim is not None:125 n_embd_head_k_mla = qk_nope_head_dim + qk_rope_head_dim126 self.gguf_writer.add_key_length_mla(n_embd_head_k_mla)127 128 # n_embd_head_v_mla = v_head_dim129 if (n_embd_head_v_mla := self.hparams.get("n_embd_head_v_mla")) is not None:130 self.gguf_writer.add_value_length_mla(n_embd_head_v_mla)131 elif v_head_dim is not None:132 self.gguf_writer.add_value_length_mla(v_head_dim)133 134 # moe_intermediate_size (1024 for Kimi)135 self.gguf_writer.add_expert_feed_forward_length(self.hparams["moe_intermediate_size"])136 # num_shared_experts (1 for Kimi)137 self.gguf_writer.add_expert_shared_count(self.hparams["num_shared_experts"])138 # first_k_dense_replace (1 for Kimi - first layer uses dense MLP)139 self.gguf_writer.add_leading_dense_block_count(self.hparams["first_k_dense_replace"])140 # Routed scaling factor (expert_weights_scale = 2.446 for Kimi)141 self.gguf_writer.add_expert_weights_scale(self.hparams["routed_scaling_factor"])142 143 def prepare_tensors(self):144 super().prepare_tensors()145 if self._experts is not None:146 experts = [k for d in self._experts for k in d.keys()]147 if len(experts) > 0:148 raise ValueError(f"Unprocessed experts: {experts}")149 150 def modify_tensors(self, data_torch: Tensor, name: str, bid: int | None) -> Iterable[tuple[str, Tensor]]:151 logger.info(f"Processing {name}: shape before = {tuple(data_torch.shape)}")152 153 # Handle KDA conv1d weights154 # HuggingFace/vLLM stores as [d_inner, d_conv] (2D), memory layout: conv_step changes fastest155 # llama.cpp expects ggml ne = [d_conv, 1, d_inner, 1], memory layout: ne[0]=d_conv changes fastest156 # GGUF reverses numpy shape when writing, so numpy (1, d_inner, 1, d_conv) -> ggml ne = [d_conv, 1, d_inner, 1]157 # Memory layouts match: both have conv_step (d_conv) changing fastest158 if name.endswith((".q_conv1d.weight", ".k_conv1d.weight", ".v_conv1d.weight")):159 # HF shape: [d_inner, d_conv] e.g. [4096, 4]160 # Target numpy shape: (1, d_inner, 1, d_conv) -> ggml ne = [d_conv, 1, d_inner, 1]161 if data_torch.ndim == 2:162 d_inner, d_conv = data_torch.shape163 # Reshape to (1, d_inner, 1, d_conv) - memory layout preserved (d_conv fastest)164 data_torch = data_torch.reshape(1, d_inner, 1, d_conv)165 logger.info(f"Reshaped conv1d weight {name}: [d_inner={d_inner}, d_conv={d_conv}] -> numpy {tuple(data_torch.shape)} -> ggml ne=[{d_conv}, 1, {d_inner}, 1]")166 elif data_torch.ndim == 3:167 # Already 3D [d_inner, 1, d_conv] from unsqueeze168 d_inner, _, d_conv = data_torch.shape169 data_torch = data_torch.reshape(1, d_inner, 1, d_conv)170 logger.info(f"Reshaped conv1d weight {name}: [d_inner={d_inner}, 1, d_conv={d_conv}] -> numpy {tuple(data_torch.shape)} -> ggml ne=[{d_conv}, 1, {d_inner}, 1]")171 172 # Handle A_log: iHF stores as [1, 1, num_heads, 1]173 # llama.cpp expects ggml ne = [1, num_heads, 1, 1]174 # GGUF reverses numpy shape: numpy (1, 1, num_heads, 1) -> ggml ne = [1, num_heads, 1, 1]175 if name.endswith(".A_log"):176 data_torch = -torch.exp(data_torch)177 if name.endswith(".dt_bias"):178 name = name.rpartition(".dt_bias")[0] + ".dt_proj.bias"179 logger.info("Changed dt_bias to dt_proj.bias")180 181 # process the experts separately182 if name.find("block_sparse_moe.experts") != -1:183 n_experts = self.find_hparam(["num_local_experts", "num_experts"])184 assert bid is not None185 186 if self._experts is None:187 self._experts = [{} for _ in range(self.block_count)]188 189 self._experts[bid][name] = data_torch190 191 if len(self._experts[bid]) >= n_experts * 3:192 # merge the experts into a single 3d tensor193 # w1: gate, w2: down, w3: up194 for wid, tname in [("w1", gguf.MODEL_TENSOR.FFN_GATE_EXP),195 ("w2", gguf.MODEL_TENSOR.FFN_DOWN_EXP),196 ("w3", gguf.MODEL_TENSOR.FFN_UP_EXP)]:197 datas: list[Tensor] = []198 for xid in range(n_experts):199 ename = f"model.layers.{bid}.block_sparse_moe.experts.{xid}.{wid}.weight"200 datas.append(self._experts[bid][ename])201 del self._experts[bid][ename]202 data_torch = torch.stack(datas, dim=0)203 new_name = self.format_tensor_name(tname, bid)204 yield from super().modify_tensors(data_torch, new_name, bid)205 return206 207 # note: MLA with the absorption optimization, needs these two split and k_b_proj transposed208 if name.endswith("kv_b_proj.weight"):209 name_kb = name.replace("kv_b_proj", "k_b_proj")210 name_vb = name.replace("kv_b_proj", "v_b_proj")211 n_head_kv = self.hparams["num_key_value_heads"]212 v_head_dim = self.find_hparam(["n_embd_head_v_mla", "v_head_dim"], optional=False)213 qk_nope_head_dim = self.hparams["qk_nope_head_dim"]214 logger.info("Split kv_b n_head_kv %d\n" % n_head_kv)215 assert data_torch.shape[0] == n_head_kv * (v_head_dim + qk_nope_head_dim)216 kv_b = data_torch.view(n_head_kv, v_head_dim + qk_nope_head_dim, data_torch.shape[-1])217 k_b, v_b = torch.split(kv_b, [qk_nope_head_dim, v_head_dim], dim=1)218 k_b = k_b.transpose(1, 2)219 yield from super().modify_tensors(k_b, name_kb, bid)220 yield from super().modify_tensors(v_b, name_vb, bid)221 return222 223 yield from super().modify_tensors(data_torch, name, bid)224 