char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
fa88ae936
char/aliceai-t5-moe
1from __future__import annotations 2 3import json 4import sys 5 6from typing import Callable ,Iterable ,TYPE_CHECKING 7 8if TYPE_CHECKING : 9from torch import Tensor 10 11from .base import ModelBase ,SentencePieceTokenTypes ,TextModel ,gguf ,logger 12 13from .llama import LlamaModel 14 15 16@ ModelBase . register ( "InternLM2ForCausalLM" ) 17@ ModelBase . example ( "internlm/internlm2-chat-7b" ) 18class InternLM2Model (TextModel ): 19model_arch = gguf .MODEL_ARCH .INTERNLM2 20 21def set_vocab (self ): 22# (TODO): Is there a better way? 23# Copy from _set_vocab_sentencepiece, The only difference is that we will treat the character 24# \x00 specially and convert it into an emoji character to prevent it from being mistakenly 25# recognized as an empty string in C++. 26from sentencepiece import SentencePieceProcessor 27from sentencepiece import sentencepiece_model_pb2 as model 28 29tokenizer_path = self .dir_model / 'tokenizer.model' 30 31tokens :list [bytes ]= [] 32scores :list [float ]= [] 33toktypes :list [int ]= [] 34 35if not tokenizer_path .is_file (): 36logger .error (f'Error: Missing { tokenizer_path } ' ) 37sys .exit (1 ) 38 39sentencepiece_model = model .ModelProto ()# pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] 40sentencepiece_model .ParseFromString (open (tokenizer_path ,"rb" ).read ()) 41add_prefix = sentencepiece_model .normalizer_spec .add_dummy_prefix 42 43tokenizer = SentencePieceProcessor () 44tokenizer .LoadFromFile (str (tokenizer_path )) 45 46vocab_size = self .hparams .get ('vocab_size' ,tokenizer .vocab_size ()) 47 48for token_id in range (vocab_size ): 49piece = tokenizer .IdToPiece (token_id ) 50text = piece .encode ("utf-8" ) 51score = tokenizer .GetScore (token_id ) 52if text == b"\x00" : 53# (TODO): fixme 54# Hack here and replace the \x00 characters. 55logger .warning (f"InternLM2 convert token ' { text } ' to '🐉'!" ) 56text = "🐉" .encode ("utf-8" ) 57 58toktype = SentencePieceTokenTypes .NORMAL 59if tokenizer .IsUnknown (token_id ): 60toktype = SentencePieceTokenTypes .UNKNOWN 61elif tokenizer .IsControl (token_id ): 62toktype = SentencePieceTokenTypes .CONTROL 63elif tokenizer .IsUnused (token_id ): 64toktype = SentencePieceTokenTypes .UNUSED 65elif tokenizer .IsByte (token_id ): 66toktype = SentencePieceTokenTypes .BYTE 67# take care of ununsed raw token 68if piece .startswith ('[UNUSED' ): 69toktype = SentencePieceTokenTypes .UNUSED 70 71tokens .append (text ) 72scores .append (score ) 73toktypes .append (toktype ) 74 75added_tokens_file = self .dir_model / 'added_tokens.json' 76if added_tokens_file .is_file (): 77with open (added_tokens_file ,"r" ,encoding = "utf-8" )as f : 78added_tokens_json = json .load (f ) 79 80for key in added_tokens_json : 81tokens .append (key .encode ("utf-8" )) 82scores .append (- 1000.0 ) 83toktypes .append (SentencePieceTokenTypes .USER_DEFINED ) 84 85chat_eos_token = '<|im_end|>' 86chat_eos_token_id = None 87 88tokenizer_config_file = self .dir_model / 'tokenizer_config.json' 89if tokenizer_config_file .is_file (): 90with open (tokenizer_config_file ,"r" ,encoding = "utf-8" )as f : 91tokenizer_config_json = json .load (f ) 92added_tokens_decoder = tokenizer_config_json .get ("added_tokens_decoder" , {}) 93for token_id ,foken_data in added_tokens_decoder .items (): 94token_id = int (token_id ) 95token = foken_data ["content" ] 96if token == chat_eos_token : 97chat_eos_token_id = token_id 98token = token .encode ("utf-8" ) 99if toktypes [token_id ]!= SentencePieceTokenTypes .UNUSED : 100if tokens [token_id ]!= token : 101logger .warning (f'replacing token { token_id } : { tokens [ token_id ]. decode ( "utf-8" )!r } -> { token . decode ( "utf-8" )!r } ' ) 102tokens [token_id ]= token 103scores [token_id ]= - 1000.0 104toktypes [token_id ]= SentencePieceTokenTypes .USER_DEFINED 105if foken_data .get ("special" ): 106toktypes [token_id ]= SentencePieceTokenTypes .CONTROL 107 108tokenizer_file = self .dir_model / 'tokenizer.json' 109if tokenizer_file .is_file (): 110with open (tokenizer_file ,"r" ,encoding = "utf-8" )as f : 111tokenizer_json = json .load (f ) 112added_tokens = tokenizer_json .get ("added_tokens" , []) 113for foken_data in added_tokens : 114token_id = int (foken_data ["id" ]) 115token = foken_data ["content" ] 116if token == chat_eos_token : 117chat_eos_token_id = token_id 118token = token .encode ("utf-8" ) 119if toktypes [token_id ]!= SentencePieceTokenTypes .UNUSED : 120if tokens [token_id ]!= token : 121logger .warning (f'replacing token { token_id } : { tokens [ token_id ]. decode ( "utf-8" )!r } -> { token . decode ( "utf-8" )!r } ' ) 122tokens [token_id ]= token 123scores [token_id ]= - 1000.0 124toktypes [token_id ]= SentencePieceTokenTypes .USER_DEFINED 125if foken_data .get ("special" ): 126toktypes [token_id ]= SentencePieceTokenTypes .CONTROL 127 128self .gguf_writer .add_tokenizer_model ("llama" ) 129self .gguf_writer .add_tokenizer_pre ("default" ) 130self .gguf_writer .add_token_list (tokens ) 131self .gguf_writer .add_token_scores (scores ) 132self .gguf_writer .add_token_types (toktypes ) 133self .gguf_writer .add_add_space_prefix (add_prefix ) 134 135special_vocab = gguf .SpecialVocab (self .dir_model ,n_vocab = len (tokens )) 136old_eos = special_vocab .special_token_ids ["eos" ] 137if chat_eos_token_id is not None : 138# For the chat model, we replace the eos with '<|im_end|>'. 139# TODO: this is a hack, should be fixed 140# https://github.com/ggml-org/llama.cpp/pull/6745#issuecomment-2067687048 141special_vocab .special_token_ids ["eos" ]= chat_eos_token_id 142logger .warning (f"Replace eos: { old_eos } with a special token: { chat_eos_token_id } " 143" in chat mode so that the conversation can end normally." ) 144 145special_vocab .add_to_gguf (self .gguf_writer ) 146 147def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 148num_heads = self .hparams ["num_attention_heads" ] 149num_kv_heads = self .hparams ["num_key_value_heads" ] 150n_embd = self .hparams ["hidden_size" ] 151q_per_kv = num_heads // num_kv_heads 152head_dim = n_embd // num_heads 153num_groups = num_heads // q_per_kv 154 155if bid is not None and f"model.layers. { bid } .attention.wqkv" in name : 156qkv = data_torch 157 158qkv = qkv .reshape ((num_groups ,q_per_kv + 2 ,head_dim ,n_embd )) 159q ,k ,v = qkv [:, :q_per_kv ],qkv [:,- 2 ],qkv [:,- 1 ] 160 161# The model weights of q and k equire additional reshape. 162q = LlamaModel .permute (q .reshape ((- 1 ,q .shape [- 1 ])),num_heads ,num_heads ) 163k = LlamaModel .permute (k .reshape ((- 1 ,k .shape [- 1 ])),num_heads ,num_kv_heads ) 164v = v .reshape ((- 1 ,v .shape [- 1 ])) 165 166yield from super ().modify_tensors (q ,self .format_tensor_name (gguf .MODEL_TENSOR .ATTN_Q ,bid ),bid ) 167yield from super ().modify_tensors (k ,self .format_tensor_name (gguf .MODEL_TENSOR .ATTN_K ,bid ),bid ) 168yield from super ().modify_tensors (v ,self .format_tensor_name (gguf .MODEL_TENSOR .ATTN_V ,bid ),bid ) 169else : 170yield from super ().modify_tensors (data_torch ,name ,bid ) 171 172 173@ ModelBase . register ( "InternLM3ForCausalLM" ) 174@ ModelBase . example ( "internlm/internlm3-8b-instruct" ) 175class InternLM3Model (TextModel ): 176model_arch = gguf .MODEL_ARCH .LLAMA 177 178def set_vocab (self ): 179tokens ,scores ,toktypes = self ._create_vocab_sentencepiece () 180 181self .gguf_writer .add_tokenizer_model ("llama" ) 182self .gguf_writer .add_tokenizer_pre ("default" ) 183self .gguf_writer .add_token_list (tokens ) 184self .gguf_writer .add_token_scores (scores ) 185self .gguf_writer .add_token_types (toktypes ) 186 187special_vocab = gguf .SpecialVocab (self .dir_model ,n_vocab = len (tokens )) 188 189tokenizer_config_file = self .dir_model / 'tokenizer_config.json' 190if tokenizer_config_file .is_file (): 191with open (tokenizer_config_file ,"r" ,encoding = "utf-8" )as f : 192tokenizer_config_json = json .load (f ) 193if "add_prefix_space" in tokenizer_config_json : 194self .gguf_writer .add_add_space_prefix (tokenizer_config_json ["add_prefix_space" ]) 195 196if "added_tokens_decoder" in tokenizer_config_json : 197for token_id ,token_data in tokenizer_config_json ["added_tokens_decoder" ].items (): 198if token_data .get ("special" ): 199token_id = int (token_id ) 200token = token_data ["content" ] 201special_vocab ._set_special_token (token ,token_id ) 202# update eos token 203if token == '<|im_end|>' and "eos" in special_vocab .special_token_ids : 204special_vocab .special_token_ids ["eos" ]= token_id 205 206special_vocab .add_to_gguf (self .gguf_writer ) 207 208def set_gguf_parameters (self ): 209super ().set_gguf_parameters () 210hparams = self .hparams 211self .gguf_writer .add_vocab_size (hparams ["vocab_size" ]) 212 213if (rope_dim := hparams .get ("head_dim" ))is None : 214rope_dim = hparams ["hidden_size" ]// hparams ["num_attention_heads" ] 215self .gguf_writer .add_rope_dimension_count (rope_dim ) 216 217@classmethod 218def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 219name ,gen = item 220 221if name .startswith (("mlp" ,"vision_model" )): 222# skip visual tensors 223return None 224 225return super ().filter_tensors (item ) 226 227def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 228n_head = self .hparams ["num_attention_heads" ] 229n_kv_head = self .hparams .get ("num_key_value_heads" ) 230if name .endswith (("q_proj.weight" ,"q_proj.bias" )): 231data_torch = LlamaModel .permute (data_torch ,n_head ,n_head ) 232if name .endswith (("k_proj.weight" ,"k_proj.bias" )): 233data_torch = LlamaModel .permute (data_torch ,n_head ,n_kv_head ) 234yield from super ().modify_tensors (data_torch ,name ,bid )