char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
67672dc5b
char/aliceai-t5-moe
1from __future__import annotations 2 3from typing import Iterable ,Sequence ,TYPE_CHECKING 4 5import torch 6 7if TYPE_CHECKING : 8from torch import Tensor 9 10from .base import ModelBase ,TextModel ,MmprojModel ,gguf ,logger 11 12 13@ ModelBase . register ( "MiniMaxText01ForCausalLM" ) 14@ ModelBase . register ( "MiniMaxM1ForCausalLM" ) 15@ ModelBase . example ( "MiniMaxAI/MiniMax-Text-01" , "MiniMaxAI/MiniMax-M1-40k" ) 16class MiniMaxText01Model (TextModel ): 17model_arch = gguf .MODEL_ARCH .MINIMAX01 18 19def _get_suppress_tokens (self )-> Sequence [int ]| None : 20import json 21from transformers import AutoTokenizer 22from .base import LazyTorchTensor 23 24# check added tokens embeddings in embeddings tensor for zero-valued embeddings 25# they get in the way of the token sampling process and must be suppressed 26 27tokenizer = AutoTokenizer .from_pretrained (self .dir_model ,trust_remote_code = True ) 28tokenizer_vocab_size = tokenizer .vocab_size # ty: ignore[unresolved-attribute] 29 30with open (self .dir_model / "model.safetensors.index.json" ,"r" ,encoding = "utf-8" )as f : 31weight_map = json .load (f )["weight_map" ] 32 33embeddings_tensor_name = "model.embed_tokens.weight" 34embeddings_shard_name = weight_map [embeddings_tensor_name ] 35with gguf .utility .SafetensorsLocal (self .dir_model / embeddings_shard_name )as model_shard : 36embeddings_data = model_shard [embeddings_tensor_name ] 37 38embeddings_weights_dtype = LazyTorchTensor ._dtype_str_map [embeddings_data .dtype ] 39embeddings_weights = torch .from_numpy (embeddings_data .mmap_bytes ()).view (embeddings_weights_dtype ).reshape (embeddings_data .shape ) 40embeddings_vocab_size = embeddings_weights .shape [0 ] 41 42embeddings_added_tokens = embeddings_weights [tokenizer_vocab_size :embeddings_vocab_size ] 43embeddings_zero_rows = torch .all (embeddings_added_tokens == 0 ,dim = 1 ) 44tokens_zero_embeddings_ids = (torch .nonzero (embeddings_zero_rows ,as_tuple = False ).flatten ()+ tokenizer_vocab_size ).tolist () 45 46return tokens_zero_embeddings_ids 47 48def set_vocab (self )-> None : 49from pathlib import Path 50 51self ._set_vocab_gpt2 () 52 53for tmpl_file in [ 54self .dir_model / "chat_template.jinja" , 55Path (__file__ ).parent .parent / "models" / "templates" / "MiniMax-M1.jinja" 56 ]: 57if tmpl_file .is_file (): 58self .gguf_writer .add_chat_template (tmpl_file .read_text (encoding = "utf-8" )) 59logger .info (f"Chat template overridden with { tmpl_file } ." ) 60break 61 62def set_gguf_parameters (self ): 63super ().set_gguf_parameters () 64 65suppress_tokens = self ._get_suppress_tokens () 66if suppress_tokens : 67logger .info (f"Suppressing tokens with zero embeddings { suppress_tokens } " ) 68self .gguf_writer .add_suppress_tokens (suppress_tokens ) 69 70layernorm_full_attention_alpha = self .hparams ["layernorm_full_attention_alpha" ] 71layernorm_full_attention_beta = self .hparams ["layernorm_full_attention_beta" ] 72layernorm_linear_attention_alpha = self .hparams ["layernorm_linear_attention_alpha" ] 73layernorm_linear_attention_beta = self .hparams ["layernorm_linear_attention_beta" ] 74layernorm_mlp_alpha = self .hparams ["layernorm_mlp_alpha" ] 75layernorm_mlp_beta = self .hparams ["layernorm_mlp_beta" ] 76assert layernorm_full_attention_alpha == layernorm_linear_attention_alpha == layernorm_mlp_alpha 77assert layernorm_full_attention_beta == layernorm_linear_attention_beta == layernorm_mlp_beta == 1.0 78# we do not store the layernorm betas as they are all 1.0 79# layernorm alphas are stored as single residual_scale hparam 80self .gguf_writer .add_residual_scale (layernorm_full_attention_alpha ) 81 82self .gguf_writer .add_rope_dimension_count (self .hparams ["rotary_dim" ]) 83 84_experts :list [dict [str ,Tensor ]]| None = None 85 86def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 87# process the experts separately 88if name .find ("block_sparse_moe.experts" )!= - 1 : 89n_experts = self .hparams ["num_local_experts" ] 90 91assert bid is not None 92 93if self ._experts is None : 94self ._experts = [{}for _ in range (self .block_count )] 95 96self ._experts [bid ][name ]= data_torch 97 98if len (self ._experts [bid ])>= n_experts * 3 : 99# merge the experts into a single 3d tensor 100for wid in ["w1" ,"w2" ,"w3" ]: 101datas :list [Tensor ]= [] 102 103for xid in range (n_experts ): 104ename = f"model.layers. { bid } .block_sparse_moe.experts. { xid } . { wid } .weight" 105datas .append (self ._experts [bid ][ename ]) 106del self ._experts [bid ][ename ] 107 108data_torch = torch .stack (datas ,dim = 0 ) 109 110merged_name = f"layers. { bid } .feed_forward.experts. { wid } .weight" 111 112new_name = self .map_tensor_name (merged_name ) 113 114yield from super ().modify_tensors (data_torch ,new_name ,bid ) 115return 116else : 117return 118 119yield from super ().modify_tensors (data_torch ,name ,bid ) 120 121 122@ ModelBase . register ( "MiniMaxM2ForCausalLM" ) 123@ ModelBase . example ( "MiniMaxAI/MiniMax-M2" ) 124class MiniMaxM2Model (TextModel ): 125model_arch = gguf .MODEL_ARCH .MINIMAXM2 126_experts_cache :dict [int ,dict [str ,Tensor ]]= {} 127 128def set_gguf_parameters (self ): 129super ().set_gguf_parameters () 130 131self .gguf_writer .add_expert_feed_forward_length (self .find_hparam (["intermediate_size" ])) 132self .gguf_writer .add_rope_dimension_count (self .find_hparam (["rotary_dim" ])) 133 134def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None ): 135# merge expert weights 136if "block_sparse_moe.experts." in name : 137n_experts = self .find_hparam (["num_local_experts" ,"num_experts" ]) 138assert bid is not None 139 140expert_cache = self ._experts_cache .setdefault (bid , {}) 141expert_cache [name ]= data_torch 142expert_weights = ["w1" ,"w2" ,"w3" ] 143 144# not enough expert weights to merge 145if len (expert_cache )< n_experts * len (expert_weights ): 146return 147 148for w_name in expert_weights : 149datas :list [Tensor ]= [] 150 151for xid in range (n_experts ): 152ename = f"model.layers. { bid } .block_sparse_moe.experts. { xid } . { w_name } .weight" 153datas .append (expert_cache [ename ]) 154del expert_cache [ename ] 155 156data_torch = torch .stack (datas ,dim = 0 ) 157merged_name = f"model.layers. { bid } .block_sparse_moe.experts. { w_name } .weight" 158new_name = self .map_tensor_name (merged_name ) 159yield from super ().modify_tensors (data_torch ,new_name ,bid ) 160 161del self ._experts_cache [bid ] 162return 163 164yield from super ().modify_tensors (data_torch ,name ,bid ) 165 166 167@ ModelBase . register ( "MiniMaxM3SparseForCausalLM" , "MiniMaxM3SparseForConditionalGeneration" ) 168@ ModelBase . example ( "MiniMaxAI/MiniMax-M3" ) 169class MiniMaxM3Model (MiniMaxM2Model ): 170model_arch = gguf .MODEL_ARCH .MINIMAXM3 171 172def tensor_force_quant (self ,name ,new_name ,bid ,n_dims ): 173if ".indexer." in new_name : 174return gguf .GGMLQuantizationType .F32 175return super ().tensor_force_quant (name ,new_name ,bid ,n_dims ) 176 177def set_gguf_parameters (self ): 178super ().set_gguf_parameters () 179 180self .gguf_writer .add_expert_shared_count (self .find_hparam (["n_shared_experts" ])) 181self .gguf_writer .add_expert_weights_scale (self .find_hparam (["routed_scaling_factor" ])) 182self .gguf_writer .add_expert_weights_norm (True ) 183 184sac = self .find_hparam (["sparse_attention_config" ]) 185self .gguf_writer .add_indexer_head_count (sac ["sparse_num_index_heads" ]) 186self .gguf_writer .add_indexer_key_length (sac ["sparse_index_dim" ]) 187self .gguf_writer .add_indexer_top_k (sac ["sparse_topk_blocks" ]) 188self .gguf_writer .add_indexer_block_size (sac ["sparse_block_size" ]) 189self .gguf_writer .add_indexer_local_blocks (sac ["sparse_local_block" ]) 190 191moe_layer_freq = self .find_hparam (["moe_layer_freq" ]) 192n_dense = 0 193for v in moe_layer_freq : 194if v == 0 : 195n_dense += 1 196else : 197break 198self .gguf_writer .add_leading_dense_block_count (n_dense ) 199 200def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None ): 201# Gemma-style (1 + w) RMSNorm: bake the +1 in so llama.cpp can use plain RMSNorm 202if name .endswith ("norm.weight" ): 203data_torch = data_torch + 1.0 204 205yield from super ().modify_tensors (data_torch ,name ,bid ) 206 207 208@ ModelBase . register ( "MiniMaxM3SparseForConditionalGeneration" , "MiniMaxM3VLForConditionalGeneration" ) 209@ ModelBase . example ( "MiniMaxAI/MiniMax-M3" ) 210class MiniMaxM3VisionModel (MmprojModel ): 211@classmethod 212def filter_tensors (cls ,item ): 213name ,gen = item 214# keep only the vision-side tensors; text / mtp / sparse-index are dropped 215if not name .startswith (("vision_tower." ,"multi_modal_projector." ,"patch_merge_mlp." )): 216return None 217return super ().filter_tensors ((name ,gen )) 218 219def set_gguf_parameters (self ): 220super ().set_gguf_parameters () 221assert self .hparams_vision is not None 222 223self .gguf_writer .add_clip_projector_type (gguf .VisionProjectorType .MINIMAXM3 ) 224self .gguf_writer .add_vision_use_gelu (True ) 225 226# the ViT carries its own LayerNorm eps (text tower uses a different one) 227self .gguf_writer .add_vision_attention_layernorm_eps ( 228self .hparams_vision .get ("layer_norm_eps" ,1e-5 ) 229 ) 230 231comp = self .hparams_vision .get ("img_token_compression_config" , {}) 232merge_size = comp .get ("spatial_merge_size" ,2 ) 233self .gguf_writer .add_vision_spatial_merge_size (int (merge_size )) 234 235def modify_tensors (self ,data_torch ,name ,bid ): 236assert self .hparams_vision is not None 237 238# Conv3d patch embed -> Conv2d slices 239if name == "vision_tower.vision_model.embeddings.patch_embedding.weight" : 240if data_torch .ndim != 5 : 241raise ValueError (f"unexpected patch_embedding rank { data_torch . ndim } for { name } " ) 242kt = data_torch .shape [2 ] 243base = gguf .TENSOR_NAMES [gguf .MODEL_TENSOR .V_ENC_EMBD_PATCH ] 244for t in range (kt ): 245suffix = ".weight" if t == 0 else f".weight. { t } " 246yield (base + suffix ,data_torch [:, :,t , ...]) 247return 248 249# Permute ViT q/k. HF [Ta Ha Wa | Tb Hb Wb | pad] reorder to [Ta Tb | Ha Hb | Wa Wb | pad]. 250for new_name ,tensor in super ().modify_tensors (data_torch ,name ,bid ): 251if ".attn_q." in new_name or ".attn_k." in new_name : 252tensor = self ._permute_vit_qk (tensor ,new_name ) 253yield new_name ,tensor 254 255def _permute_vit_qk (self ,t :"Tensor" ,new_name :str )-> "Tensor" : 256assert self .hparams_vision is not None 257n_head = self .hparams_vision ["num_attention_heads" ] 258d_head = t .shape [0 ]// n_head 259axis_dim = 2 * ((2 * (d_head // 2 )// 3 )// 2 ) 260ah = axis_dim // 2 261half = 3 * ah 262perm = [] 263perm += list (range (0 ,ah )) 264perm += list (range (half ,half + ah )) 265perm += list (range (ah ,2 * ah )) 266perm += list (range (half + ah ,half + 2 * ah )) 267perm += list (range (2 * ah ,3 * ah )) 268perm += list (range (half + 2 * ah ,half + 3 * ah )) 269perm += list (range (2 * half ,d_head )) 270 271assert axis_dim % 2 == 0 272assert 3 * axis_dim <= d_head 273assert len (perm )== d_head 274assert sorted (perm )== list (range (d_head )),"perm is not a bijection of d_head" 275assert t .shape [0 ]== n_head * d_head ,f" { new_name } : { t . shape [ 0 ] } != { n_head } * { d_head } " 276assert d_head == 80 277 278idx = torch .tensor (perm ,dtype = torch .long ) 279if t .ndim == 2 : 280return t .reshape (n_head ,d_head ,t .shape [1 ])[:,idx , :].reshape (t .shape ) 281return t .reshape (n_head ,d_head )[:,idx ].reshape (t .shape )