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 os 5 6from typing import Iterable ,TYPE_CHECKING 7 8if TYPE_CHECKING : 9from torch import Tensor 10 11from .base import ModelBase ,SentencePieceTokenTypes ,TextModel ,gguf ,logger 12 13 14@ ModelBase . register ( "T5WithLMHeadModel" ) 15@ ModelBase . register ( "T5ForConditionalGeneration" ) 16@ ModelBase . register ( "MT5ForConditionalGeneration" ) 17@ ModelBase . register ( "UMT5ForConditionalGeneration" ) 18@ ModelBase . register ( "UMT5Model" ) 19@ ModelBase . example ( "google-t5/t5-small" , "google/flan-t5-small" , "google/umt5-small" ) 20class T5Model (TextModel ): 21model_arch = gguf .MODEL_ARCH .T5 22 23def __init__ (self ,* args ,** kwargs ): 24super ().__init__ (* args ,** kwargs ) 25self .shared_token_embeddings_found = False 26 27def set_vocab (self ): 28# to avoid TypeError: Descriptors cannot be created directly 29# exception when importing sentencepiece_model_pb2 30os .environ ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION" ]= "python" 31from sentencepiece import SentencePieceProcessor 32from sentencepiece import sentencepiece_model_pb2 as model 33 34tokenizer_path = self .dir_model / 'tokenizer.model' 35 36# many older models use spiece.model tokenizer model filename 37if not tokenizer_path .is_file (): 38tokenizer_path = self .dir_model / 'spiece.model' 39 40if not tokenizer_path .is_file (): 41raise FileNotFoundError (f"File not found: { tokenizer_path } " ) 42 43sentencepiece_model = model .ModelProto ()# pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] 44sentencepiece_model .ParseFromString (open (tokenizer_path ,"rb" ).read ()) 45 46# some models like Pile-T5 family use BPE tokenizer instead of Unigram 47if sentencepiece_model .trainer_spec .model_type == 2 :# BPE 48# assure the tokenizer model file name is correct 49assert tokenizer_path .name == 'tokenizer.model' 50return self ._set_vocab_sentencepiece () 51else : 52assert sentencepiece_model .trainer_spec .model_type == 1 # UNIGRAM 53 54add_prefix = sentencepiece_model .normalizer_spec .add_dummy_prefix 55remove_whitespaces = sentencepiece_model .normalizer_spec .remove_extra_whitespaces 56precompiled_charsmap = sentencepiece_model .normalizer_spec .precompiled_charsmap 57 58tokenizer = SentencePieceProcessor () 59tokenizer .LoadFromFile (str (tokenizer_path )) 60 61vocab_size = self .hparams .get ('vocab_size' ,tokenizer .vocab_size ()) 62 63tokens :list [bytes ]= [f"[PAD { i } ]" .encode ("utf-8" )for i in range (vocab_size )] 64scores :list [float ]= [- 10000.0 ]* vocab_size 65toktypes :list [int ]= [SentencePieceTokenTypes .UNUSED ]* vocab_size 66 67for token_id in range (tokenizer .vocab_size ()): 68piece = tokenizer .IdToPiece (token_id ) 69text = piece .encode ("utf-8" ) 70score = tokenizer .GetScore (token_id ) 71 72toktype = SentencePieceTokenTypes .NORMAL 73if tokenizer .IsUnknown (token_id ): 74toktype = SentencePieceTokenTypes .UNKNOWN 75elif tokenizer .IsControl (token_id ): 76toktype = SentencePieceTokenTypes .CONTROL 77elif tokenizer .IsUnused (token_id ): 78toktype = SentencePieceTokenTypes .UNUSED 79elif tokenizer .IsByte (token_id ): 80toktype = SentencePieceTokenTypes .BYTE 81 82tokens [token_id ]= text 83scores [token_id ]= score 84toktypes [token_id ]= toktype 85 86added_tokens_file = self .dir_model / 'added_tokens.json' 87if added_tokens_file .is_file (): 88with open (added_tokens_file ,"r" ,encoding = "utf-8" )as f : 89added_tokens_json = json .load (f ) 90for key in added_tokens_json : 91token_id = added_tokens_json [key ] 92if token_id >= vocab_size : 93logger .warning (f'ignore token { token_id } : id is out of range, max= { vocab_size - 1 } ' ) 94continue 95 96tokens [token_id ]= key .encode ("utf-8" ) 97scores [token_id ]= - 1000.0 98toktypes [token_id ]= SentencePieceTokenTypes .USER_DEFINED 99 100if vocab_size > len (tokens ): 101pad_count = vocab_size - len (tokens ) 102logger .debug (f"Padding vocab with { pad_count } token(s) - [PAD1] through [PAD { pad_count } ]" ) 103for i in range (1 ,pad_count + 1 ): 104tokens .append (bytes (f"[PAD { i } ]" ,encoding = "utf-8" )) 105scores .append (- 1000.0 ) 106toktypes .append (SentencePieceTokenTypes .UNUSED ) 107 108self .gguf_writer .add_tokenizer_model ("t5" ) 109self .gguf_writer .add_tokenizer_pre ("default" ) 110self .gguf_writer .add_token_list (tokens ) 111self .gguf_writer .add_token_scores (scores ) 112self .gguf_writer .add_token_types (toktypes ) 113self .gguf_writer .add_add_space_prefix (add_prefix ) 114self .gguf_writer .add_remove_extra_whitespaces (remove_whitespaces ) 115if precompiled_charsmap : 116self .gguf_writer .add_precompiled_charsmap (precompiled_charsmap ) 117 118special_vocab = gguf .SpecialVocab (self .dir_model ,n_vocab = len (tokens )) 119special_vocab .add_to_gguf (self .gguf_writer ) 120 121def set_gguf_parameters (self ): 122if (n_ctx := self .find_hparam (["n_positions" ],optional = True ))is None : 123logger .warning ("Couldn't find context length in config.json, assuming default value of 512" ) 124n_ctx = 512 125self .gguf_writer .add_context_length (n_ctx ) 126self .gguf_writer .add_embedding_length (self .hparams ["d_model" ]) 127self .gguf_writer .add_feed_forward_length (self .hparams ["d_ff" ]) 128self .gguf_writer .add_block_count (self .block_count ) 129if (dec_n_layer := self .hparams .get ("num_decoder_layers" ))is not None : 130self .gguf_writer .add_decoder_block_count (dec_n_layer ) 131self .gguf_writer .add_head_count (self .hparams ["num_heads" ]) 132self .gguf_writer .add_key_length (self .hparams ["d_kv" ]) 133self .gguf_writer .add_value_length (self .hparams ["d_kv" ]) 134self .gguf_writer .add_layer_norm_eps (self .hparams ["layer_norm_epsilon" ]) 135self .gguf_writer .add_relative_attn_buckets_count (self .hparams ["relative_attention_num_buckets" ]) 136self .gguf_writer .add_layer_norm_rms_eps (self .hparams ["layer_norm_epsilon" ]) 137self .gguf_writer .add_decoder_start_token_id (self .hparams ["decoder_start_token_id" ]) 138self .gguf_writer .add_file_type (self .ftype ) 139 140def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 141# T5 based models contain shared token embeddings tensors saved randomly as either "encoder.embed_tokens.weight", 142# "decoder.embed_tokens.weight" or "shared.weight" tensor. In some models there are even multiple of them stored 143# in the safetensors files. We use the first tensor from these three as the token embeddings for both encoder 144# and decoder and ignore the remaining ones. 145if name in ["decoder.embed_tokens.weight" ,"encoder.embed_tokens.weight" ,"shared.weight" ]: 146if not self .shared_token_embeddings_found : 147name = "shared.weight" 148self .shared_token_embeddings_found = True 149else : 150logger .debug (f"Skipping shared tensor { name !r } in safetensors so that convert can end normally." ) 151return 152 153yield from super ().modify_tensors (data_torch ,name ,bid ) 154 155 156@ ModelBase . register ( "T5EncoderModel" ) 157@ ModelBase . example ( "sentence-transformers/sentence-t5-base" ) 158class T5EncoderModel (TextModel ): 159model_arch = gguf .MODEL_ARCH .T5ENCODER 160 161def __init__ (self ,* args ,** kwargs ): 162super ().__init__ (* args ,** kwargs ) 163self .shared_token_embeddings_found = False 164 165def set_vocab (self ): 166# to avoid TypeError: Descriptors cannot be created directly 167# exception when importing sentencepiece_model_pb2 168os .environ ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION" ]= "python" 169from sentencepiece import SentencePieceProcessor 170from sentencepiece import sentencepiece_model_pb2 as model 171 172tokenizer_path = self .dir_model / 'tokenizer.model' 173 174# many older models use spiece.model tokenizer model filename 175if not tokenizer_path .is_file (): 176tokenizer_path = self .dir_model / 'spiece.model' 177 178if not tokenizer_path .is_file (): 179raise FileNotFoundError (f"File not found: { tokenizer_path } " ) 180 181sentencepiece_model = model .ModelProto ()# pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] 182sentencepiece_model .ParseFromString (open (tokenizer_path ,"rb" ).read ()) 183 184# some models like Pile-T5 family use BPE tokenizer instead of Unigram 185if sentencepiece_model .trainer_spec .model_type == 2 :# BPE 186# assure the tokenizer model file name is correct 187assert tokenizer_path .name == 'tokenizer.model' 188return self ._set_vocab_sentencepiece () 189else : 190assert sentencepiece_model .trainer_spec .model_type == 1 # UNIGRAM 191 192add_prefix = sentencepiece_model .normalizer_spec .add_dummy_prefix 193remove_whitespaces = sentencepiece_model .normalizer_spec .remove_extra_whitespaces 194precompiled_charsmap = sentencepiece_model .normalizer_spec .precompiled_charsmap 195 196tokenizer = SentencePieceProcessor () 197tokenizer .LoadFromFile (str (tokenizer_path )) 198 199vocab_size = self .hparams .get ('vocab_size' ,tokenizer .vocab_size ()) 200 201tokens :list [bytes ]= [f"[PAD { i } ]" .encode ("utf-8" )for i in range (vocab_size )] 202scores :list [float ]= [- 10000.0 ]* vocab_size 203toktypes :list [int ]= [SentencePieceTokenTypes .UNUSED ]* vocab_size 204 205for token_id in range (tokenizer .vocab_size ()): 206piece = tokenizer .IdToPiece (token_id ) 207text = piece .encode ("utf-8" ) 208score = tokenizer .GetScore (token_id ) 209 210toktype = SentencePieceTokenTypes .NORMAL 211if tokenizer .IsUnknown (token_id ): 212toktype = SentencePieceTokenTypes .UNKNOWN 213elif tokenizer .IsControl (token_id ): 214toktype = SentencePieceTokenTypes .CONTROL 215elif tokenizer .IsUnused (token_id ): 216toktype = SentencePieceTokenTypes .UNUSED 217elif tokenizer .IsByte (token_id ): 218toktype = SentencePieceTokenTypes .BYTE 219 220tokens [token_id ]= text 221scores [token_id ]= score 222toktypes [token_id ]= toktype 223 224added_tokens_file = self .dir_model / 'added_tokens.json' 225if added_tokens_file .is_file (): 226with open (added_tokens_file ,"r" ,encoding = "utf-8" )as f : 227added_tokens_json = json .load (f ) 228for key in added_tokens_json : 229token_id = added_tokens_json [key ] 230if token_id >= vocab_size : 231logger .warning (f'ignore token { token_id } : id is out of range, max= { vocab_size - 1 } ' ) 232continue 233 234tokens [token_id ]= key .encode ("utf-8" ) 235scores [token_id ]= - 1000.0 236toktypes [token_id ]= SentencePieceTokenTypes .USER_DEFINED 237 238if vocab_size > len (tokens ): 239pad_count = vocab_size - len (tokens ) 240logger .debug (f"Padding vocab with { pad_count } token(s) - [PAD1] through [PAD { pad_count } ]" ) 241for i in range (1 ,pad_count + 1 ): 242tokens .append (bytes (f"[PAD { i } ]" ,encoding = "utf-8" )) 243scores .append (- 1000.0 ) 244toktypes .append (SentencePieceTokenTypes .UNUSED ) 245 246self .gguf_writer .add_tokenizer_model ("t5" ) 247self .gguf_writer .add_tokenizer_pre ("default" ) 248self .gguf_writer .add_token_list (tokens ) 249self .gguf_writer .add_token_scores (scores ) 250self .gguf_writer .add_token_types (toktypes ) 251self .gguf_writer .add_add_space_prefix (add_prefix ) 252self .gguf_writer .add_remove_extra_whitespaces (remove_whitespaces ) 253if precompiled_charsmap : 254self .gguf_writer .add_precompiled_charsmap (precompiled_charsmap ) 255 256special_vocab = gguf .SpecialVocab (self .dir_model ,n_vocab = len (tokens )) 257special_vocab .add_to_gguf (self .gguf_writer ) 258 259def set_gguf_parameters (self ): 260if (n_ctx := self .find_hparam (["n_positions" ],optional = True ))is None : 261logger .warning ("Couldn't find context length in config.json, assuming default value of 512" ) 262n_ctx = 512 263self .gguf_writer .add_context_length (n_ctx ) 264self .gguf_writer .add_embedding_length (self .hparams ["d_model" ]) 265self .gguf_writer .add_feed_forward_length (self .hparams ["d_ff" ]) 266self .gguf_writer .add_block_count (self .block_count ) 267self .gguf_writer .add_head_count (self .hparams ["num_heads" ]) 268self .gguf_writer .add_key_length (self .hparams ["d_kv" ]) 269self .gguf_writer .add_value_length (self .hparams ["d_kv" ]) 270self .gguf_writer .add_layer_norm_eps (self .hparams ["layer_norm_epsilon" ]) 271self .gguf_writer .add_relative_attn_buckets_count (self .hparams ["relative_attention_num_buckets" ]) 272self .gguf_writer .add_layer_norm_rms_eps (self .hparams ["layer_norm_epsilon" ]) 273self .gguf_writer .add_file_type (self .ftype ) 274 275def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 276# T5 based models contain shared token embeddings tensors saved randomly as either "encoder.embed_tokens.weight", 277# "decoder.embed_tokens.weight" or "shared.weight" tensor. In some models there are even multiple of them stored 278# in the safetensors files. We use the first tensor from these three as the token embeddings for both encoder 279# and decoder and ignore the remaining ones. 280if name in ["decoder.embed_tokens.weight" ,"encoder.embed_tokens.weight" ,"shared.weight" ]: 281if not self .shared_token_embeddings_found : 282name = "shared.weight" 283self .shared_token_embeddings_found = True 284else : 285logger .debug (f"Skipping shared tensor { name !r } in safetensors so that convert can end normally." ) 286return 287 288yield from super ().modify_tensors (data_torch ,name ,bid )