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 Iterable ,TYPE_CHECKING 7 8import torch 9 10if TYPE_CHECKING : 11from torch import Tensor 12 13from .base import ModelBase ,SentencePieceTokenTypes ,TextModel ,gguf ,logger 14 15from .llama import LlamaModel 16 17 18@ ModelBase . register ( "ArcticForCausalLM" ) 19@ ModelBase . example ( "Snowflake/snowflake-arctic-instruct" ) 20class ArcticModel (TextModel ): 21model_arch = gguf .MODEL_ARCH .ARCTIC 22 23def set_vocab (self ): 24# The reason for using a custom implementation here is that the 25# snowflake-arctic-instruct model redefined tokens 31998 and 31999 from 26# tokenizer.model and used them as BOS and EOS instead of adding new tokens. 27from sentencepiece import SentencePieceProcessor 28 29tokenizer_path = self .dir_model / 'tokenizer.model' 30 31if not tokenizer_path .is_file (): 32logger .error (f'Error: Missing { tokenizer_path } ' ) 33sys .exit (1 ) 34 35# Read the whole vocabulary from the tokenizer.model file 36tokenizer = SentencePieceProcessor () 37tokenizer .LoadFromFile (str (tokenizer_path )) 38 39vocab_size = self .hparams .get ('vocab_size' ,tokenizer .vocab_size ()) 40 41tokens :list [bytes ]= [f"[PAD { i } ]" .encode ("utf-8" )for i in range (vocab_size )] 42scores :list [float ]= [- 10000.0 ]* vocab_size 43toktypes :list [int ]= [SentencePieceTokenTypes .UNUSED ]* vocab_size 44 45for token_id in range (tokenizer .vocab_size ()): 46 47piece = tokenizer .IdToPiece (token_id ) 48text = piece .encode ("utf-8" ) 49score = tokenizer .GetScore (token_id ) 50 51toktype = SentencePieceTokenTypes .NORMAL 52if tokenizer .IsUnknown (token_id ): 53toktype = SentencePieceTokenTypes .UNKNOWN 54elif tokenizer .IsControl (token_id ): 55toktype = SentencePieceTokenTypes .CONTROL 56elif tokenizer .IsUnused (token_id ): 57toktype = SentencePieceTokenTypes .UNUSED 58elif tokenizer .IsByte (token_id ): 59toktype = SentencePieceTokenTypes .BYTE 60 61tokens [token_id ]= text 62scores [token_id ]= score 63toktypes [token_id ]= toktype 64 65# Use the added_tokens_decoder field from tokeniser_config.json as the source 66# of information about added/redefined tokens and modify them accordingly. 67tokenizer_config_file = self .dir_model / 'tokenizer_config.json' 68if tokenizer_config_file .is_file (): 69with open (tokenizer_config_file ,"r" ,encoding = "utf-8" )as f : 70tokenizer_config_json = json .load (f ) 71 72if "added_tokens_decoder" in tokenizer_config_json : 73added_tokens_decoder = tokenizer_config_json ["added_tokens_decoder" ] 74for token_id ,token_json in added_tokens_decoder .items (): 75token_id = int (token_id ) 76if token_id >= vocab_size : 77logger .debug (f'ignore token { token_id } : id is out of range, max= { vocab_size - 1 } ' ) 78continue 79 80token_content = token_json ["content" ] 81token_type = SentencePieceTokenTypes .USER_DEFINED 82token_score = - 10000.0 83 84# Map unk_token to UNKNOWN, other special tokens to CONTROL 85# Set the score to 0.0 as in the original tokenizer.model 86if ("special" in token_json )and token_json ["special" ]: 87if token_content == tokenizer_config_json ["unk_token" ]: 88token_type = SentencePieceTokenTypes .UNKNOWN 89else : 90token_type = SentencePieceTokenTypes .CONTROL 91token_score = 0.0 92 93logger .info (f"Setting added token { token_id } to ' { token_content } ' (type: { token_type } , score: { token_score :.2f } )" ) 94tokens [token_id ]= token_content .encode ("utf-8" ) 95toktypes [token_id ]= token_type 96scores [token_id ]= token_score 97 98self .gguf_writer .add_tokenizer_model ("llama" ) 99self .gguf_writer .add_tokenizer_pre ("default" ) 100self .gguf_writer .add_token_list (tokens ) 101self .gguf_writer .add_token_scores (scores ) 102self .gguf_writer .add_token_types (toktypes ) 103 104special_vocab = gguf .SpecialVocab (self .dir_model ,n_vocab = len (tokens )) 105special_vocab .add_to_gguf (self .gguf_writer ) 106 107def set_gguf_parameters (self ): 108super ().set_gguf_parameters () 109hparams = self .hparams 110self .gguf_writer .add_vocab_size (hparams ["vocab_size" ]) 111self .gguf_writer .add_rope_dimension_count (hparams ["hidden_size" ]// hparams ["num_attention_heads" ]) 112 113_experts :list [dict [str ,Tensor ]]| None = None 114 115def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 116n_head = self .hparams ["num_attention_heads" ] 117n_kv_head = self .hparams .get ("num_key_value_heads" ) 118 119if name .endswith ("q_proj.weight" ): 120data_torch = LlamaModel .permute (data_torch ,n_head ,n_head ) 121if name .endswith ("k_proj.weight" ): 122data_torch = LlamaModel .permute (data_torch ,n_head ,n_kv_head ) 123 124# process the experts separately 125if name .find ("block_sparse_moe.experts" )!= - 1 : 126n_experts = self .hparams ["num_local_experts" ] 127 128assert bid is not None 129 130if self ._experts is None : 131self ._experts = [{}for _ in range (self .block_count )] 132 133self ._experts [bid ][name ]= data_torch 134 135if len (self ._experts [bid ])>= n_experts * 3 : 136# merge the experts into a single 3d tensor 137for wid in ["w1" ,"w2" ,"w3" ]: 138datas :list [Tensor ]= [] 139 140for xid in range (n_experts ): 141ename = f"model.layers. { bid } .block_sparse_moe.experts. { xid } . { wid } .weight" 142datas .append (self ._experts [bid ][ename ]) 143del self ._experts [bid ][ename ] 144 145data_torch = torch .stack (datas ,dim = 0 ) 146 147merged_name = f"layers. { bid } .feed_forward.experts. { wid } .weight" 148 149yield from super ().modify_tensors (data_torch ,merged_name ,bid ) 150return 151else : 152return 153 154yield from super ().modify_tensors (data_torch ,name ,bid ) 155 156def prepare_tensors (self ): 157super ().prepare_tensors () 158 159if self ._experts is not None : 160# flatten `list[dict[str, Tensor]]` into `list[str]` 161experts = [k for d in self ._experts for k in d .keys ()] 162if len (experts )> 0 : 163raise ValueError (f"Unprocessed experts: { experts } " )