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 pathlib import Path 7from typing import Any ,Callable ,Iterable ,TYPE_CHECKING 8 9import torch 10 11if TYPE_CHECKING : 12from torch import Tensor 13 14from .base import ModelBase ,SentencePieceTokenTypes ,TextModel ,gguf ,logger 15 16 17@ ModelBase . register ( "BertModel" , "BertForMaskedLM" , "CamembertModel" , "BertForSequenceClassification" ) 18@ ModelBase . example ( "BAAI/bge-small-en-v1.5" , "dangvantuan/sentence-camembert-base" ) 19class BertModel (TextModel ): 20model_arch = gguf .MODEL_ARCH .BERT 21 22def __init__ (self ,* args ,** kwargs ): 23super ().__init__ (* args ,** kwargs ) 24self .vocab_size = None 25 26if cls_out_labels := self .hparams .get ("id2label" ): 27if len (cls_out_labels )== 2 and cls_out_labels [0 ]== "LABEL_0" : 28# Remove dummy labels added by AutoConfig 29cls_out_labels = None 30self .cls_out_labels = cls_out_labels 31 32def set_gguf_parameters (self ): 33super ().set_gguf_parameters () 34self .gguf_writer .add_causal_attention (False ) 35self ._try_set_pooling_type () 36 37if self .cls_out_labels : 38self .gguf_writer .add_classifier_output_labels ([v for k ,v in sorted (self .cls_out_labels .items ())]) 39 40def set_vocab (self ): 41tokens ,toktypes ,tokpre = self .get_vocab_base () 42self .vocab_size = len (tokens ) 43 44# we need this to validate the size of the token_type embeddings 45# though currently we are passing all zeros to the token_type embeddings 46# "Sequence A" or "Sequence B" 47self .gguf_writer .add_token_type_count (self .hparams .get ("type_vocab_size" ,1 )) 48 49# convert to phantom space vocab 50def phantom (tok ,toktype ): 51if toktype == gguf .TokenType .CONTROL : 52return tok 53if tok .startswith ("##" ): 54return tok [2 :] 55return "\u2581" + tok 56assert len (tokens )== len (toktypes ) 57tokens = list (map (phantom ,tokens ,toktypes )) 58 59# add vocab to gguf 60self .gguf_writer .add_tokenizer_model ("bert" ) 61self .gguf_writer .add_tokenizer_pre (tokpre ) 62self .gguf_writer .add_token_list (tokens ) 63self .gguf_writer .add_token_types (toktypes ) 64 65# handle special tokens 66special_vocab = gguf .SpecialVocab (self .dir_model ,n_vocab = len (tokens )) 67special_vocab .add_to_gguf (self .gguf_writer ) 68 69@classmethod 70def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 71name ,gen = item 72 73if name .startswith ("bert." ): 74name = name [5 :] 75 76if name .endswith (".gamma" ): 77name = name [:- 6 ]+ ".weight" 78 79if name .endswith (".beta" ): 80name = name [:- 5 ]+ ".bias" 81 82# we are only using BERT for embeddings so we don't need the pooling layer 83if name in ("embeddings.position_ids" ,"pooler.dense.weight" ,"pooler.dense.bias" ): 84return None 85 86if name .startswith ("cls.predictions" ): 87return None 88 89if name .startswith ("cls.seq_relationship" ): 90return None 91 92return super ().filter_tensors ((name ,gen )) 93 94def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 95if self .cls_out_labels : 96# For BertForSequenceClassification (direct projection layer) 97if name == "classifier.weight" : 98name = "classifier.out_proj.weight" 99 100if name == "classifier.bias" : 101name = "classifier.out_proj.bias" 102 103yield from super ().modify_tensors (data_torch ,name ,bid ) 104 105def _xlmroberta_tokenizer_init (self )-> None : 106# we need the pad_token_id to know how to chop down position_embd matrix 107if (pad_token_id := self .hparams .get ("pad_token_id" ))is not None : 108self ._position_offset = 1 + pad_token_id 109if "max_position_embeddings" in self .hparams : 110self .hparams ["max_position_embeddings" ]-= self ._position_offset 111else : 112self ._position_offset = None 113 114def _xlmroberta_set_vocab (self )-> None : 115# to avoid TypeError: Descriptors cannot be created directly 116# exception when importing sentencepiece_model_pb2 117os .environ ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION" ]= "python" 118from sentencepiece import SentencePieceProcessor 119from sentencepiece import sentencepiece_model_pb2 as model 120 121tokenizer_path = self .dir_model / 'sentencepiece.bpe.model' 122 123tokenizer_json = {} 124tokenizer_config_json = {} 125if not tokenizer_path .is_file (): 126tokenizer_path = self .dir_model / 'tokenizer.json' 127tokenizer_config_path = self .dir_model / 'tokenizer_config.json' 128 129if not tokenizer_path .is_file (): 130raise FileNotFoundError (f"File not found: { tokenizer_path } " ) 131 132from base64 import b64decode 133from transformers import AutoTokenizer 134tokenizer = AutoTokenizer .from_pretrained (self .dir_model ) 135 136with open (tokenizer_path ,"r" ,encoding = "utf-8" )as fp : 137tokenizer_json = json .load (fp ) 138 139if tokenizer_config_path .is_file (): 140with open (tokenizer_config_path ,"r" ,encoding = "utf-8" )as fp : 141tokenizer_config_json = json .load (fp ) 142 143add_prefix = tokenizer .add_prefix_space # ty: ignore[unresolved-attribute] 144remove_whitespaces = tokenizer .clean_up_tokenization_spaces # ty: ignore[unresolved-attribute] 145precompiled_charsmap = b64decode (tokenizer_json ["normalizer" ]["precompiled_charsmap" ]) 146 147vocab_size = max (self .hparams .get ("vocab_size" ,0 ),tokenizer .vocab_size )# ty: ignore[unresolved-attribute] 148else : 149sentencepiece_model = model .ModelProto ()# pyright: ignore[reportAttributeAccessIssue] # ty: ignore[unresolved-attribute] 150sentencepiece_model .ParseFromString (open (tokenizer_path ,"rb" ).read ()) 151assert sentencepiece_model .trainer_spec .model_type == 1 # UNIGRAM 152 153add_prefix = sentencepiece_model .normalizer_spec .add_dummy_prefix 154remove_whitespaces = sentencepiece_model .normalizer_spec .remove_extra_whitespaces 155precompiled_charsmap = sentencepiece_model .normalizer_spec .precompiled_charsmap 156 157tokenizer = SentencePieceProcessor () 158tokenizer .LoadFromFile (str (tokenizer_path )) 159 160vocab_size = max (self .hparams .get ("vocab_size" ,0 ),tokenizer .vocab_size ()) 161 162tokens :list [bytes ]= [f"[PAD { i } ]" .encode ("utf-8" )for i in range (vocab_size )] 163scores :list [float ]= [- 10000.0 ]* vocab_size 164toktypes :list [int ]= [SentencePieceTokenTypes .UNUSED ]* vocab_size 165 166if isinstance (tokenizer ,SentencePieceProcessor ): 167for token_id in range (tokenizer .vocab_size ()): 168piece = tokenizer .IdToPiece (token_id ) 169text = piece .encode ("utf-8" ) 170score = tokenizer .GetScore (token_id ) 171 172toktype = SentencePieceTokenTypes .NORMAL 173if tokenizer .IsUnknown (token_id ): 174toktype = SentencePieceTokenTypes .UNKNOWN 175elif tokenizer .IsControl (token_id ): 176toktype = SentencePieceTokenTypes .CONTROL 177elif tokenizer .IsUnused (token_id ): 178toktype = SentencePieceTokenTypes .UNUSED 179elif tokenizer .IsByte (token_id ): 180toktype = SentencePieceTokenTypes .BYTE 181 182tokens [token_id ]= text 183scores [token_id ]= score 184toktypes [token_id ]= toktype 185else : 186added_vocab = tokenizer .get_added_vocab ()# ty: ignore[unresolved-attribute] 187unk_token = tokenizer_config_json .get ("unk_token" ) 188unk_token_id = added_vocab .get (unk_token ,tokenizer_json ["model" ].get ("unk_id" ,3 ))# ty: ignore[no-matching-overload] 189 190for token_id in range (tokenizer .vocab_size ):# ty: ignore[unresolved-attribute] 191piece = tokenizer ._convert_id_to_token (token_id )# ty: ignore[unresolved-attribute] 192if (piece := tokenizer ._convert_id_to_token (token_id ))is not None :# ty: ignore[unresolved-attribute] 193text = piece .encode ("utf-8" ) 194score = tokenizer_json ["model" ]["vocab" ][token_id ][1 ] 195 196toktype = SentencePieceTokenTypes .NORMAL 197if token_id == unk_token_id : 198toktype = SentencePieceTokenTypes .UNKNOWN 199elif token_id in tokenizer .all_special_ids :# ty: ignore[unresolved-attribute] 200toktype = SentencePieceTokenTypes .CONTROL 201elif token_id in added_vocab .values (): 202toktype = SentencePieceTokenTypes .USER_DEFINED 203# No reliable way to detect this, but jina doesn't have any 204# elif tokenizer.IsByte(token_id): 205# toktype = SentencePieceTokenTypes.BYTE 206 207tokens [token_id ]= text 208scores [token_id ]= score 209toktypes [token_id ]= toktype 210 211if isinstance (tokenizer ,SentencePieceProcessor ): 212# realign tokens (see HF tokenizer code) 213tokens = [b'<s>' ,b'<pad>' ,b'</s>' ,b'<unk>' ]+ tokens [3 :- 1 ] 214scores = [0.0 ,0.0 ,0.0 ,0.0 ]+ scores [3 :- 1 ] 215toktypes = [ 216SentencePieceTokenTypes .CONTROL , 217SentencePieceTokenTypes .CONTROL , 218SentencePieceTokenTypes .CONTROL , 219SentencePieceTokenTypes .UNKNOWN , 220 ]+ toktypes [3 :- 1 ] 221 222if self .model_arch == gguf .MODEL_ARCH .NOMIC_BERT_MOE : 223# Add mask token missing from sentencepiece.bpe.model 224tokens [250001 ]= b'<mask>' 225scores [250001 ]= 0.0 226toktypes [250001 ]= SentencePieceTokenTypes .CONTROL 227 228self .gguf_writer .add_tokenizer_model ("t5" ) 229self .gguf_writer .add_tokenizer_pre ("default" ) 230self .gguf_writer .add_token_list (tokens ) 231self .gguf_writer .add_token_scores (scores ) 232self .gguf_writer .add_token_types (toktypes ) 233self .gguf_writer .add_add_space_prefix (add_prefix ) 234self .gguf_writer .add_token_type_count (self .hparams .get ("type_vocab_size" ,1 )) 235self .gguf_writer .add_remove_extra_whitespaces (remove_whitespaces ) 236if precompiled_charsmap : 237self .gguf_writer .add_precompiled_charsmap (precompiled_charsmap ) 238 239special_vocab = gguf .SpecialVocab (self .dir_model ,n_vocab = len (tokens )) 240special_vocab .add_to_gguf (self .gguf_writer ) 241 242 243@ ModelBase . register ( "DistilBertModel" , "DistilBertForMaskedLM" , "DistilBertForSequenceClassification" ) 244@ ModelBase . example ( "distilbert/distilbert-base-uncased" ) 245class DistilBertModel (BertModel ): 246model_arch = gguf .MODEL_ARCH .BERT 247 248def set_gguf_parameters (self ): 249self .gguf_writer .add_layer_norm_eps (1e-12 ) 250logger .info ("gguf: layer norm epsilon = 1e-12" ) 251super ().set_gguf_parameters () 252 253@classmethod 254def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 255name ,gen = item 256 257if name .startswith ("distilbert." ): 258name = name [11 :] 259 260# These layers act as MLM head, so we don't need them 261if name .startswith ("vocab_" ): 262return None 263 264return super ().filter_tensors ((name ,gen )) 265 266 267@ ModelBase . register ( "RobertaModel" , "RobertaForSequenceClassification" ) 268@ ModelBase . example ( "sentence-transformers/stsb-roberta-base" ) 269class RobertaModel (BertModel ): 270model_arch = gguf .MODEL_ARCH .BERT 271 272def __init__ (self ,* args ,** kwargs ): 273super ().__init__ (* args ,** kwargs ) 274 275# we need the pad_token_id to know how to chop down position_embd matrix 276if (pad_token_id := self .hparams .get ("pad_token_id" ))is not None : 277self ._position_offset = 1 + pad_token_id 278if "max_position_embeddings" in self .hparams : 279self .hparams ["max_position_embeddings" ]-= self ._position_offset 280else : 281self ._position_offset = None 282 283def set_vocab (self ): 284"""Support BPE tokenizers for roberta models""" 285bpe_tok_path = self .dir_model / "tokenizer.json" 286if bpe_tok_path .exists (): 287self ._set_vocab_gpt2 () 288 289# we need this to validate the size of the token_type embeddings 290# though currently we are passing all zeros to the token_type embeddings 291# "Sequence A" or "Sequence B" 292self .gguf_writer .add_token_type_count (self .hparams .get ("type_vocab_size" ,1 )) 293 294else : 295return super ().set_vocab () 296 297@classmethod 298def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 299name ,gen = item 300 301# if name starts with "roberta.", remove the prefix 302# e.g. https://huggingface.co/BAAI/bge-reranker-v2-m3/tree/main 303if name .startswith ("roberta." ): 304name = name [8 :] 305 306return super ().filter_tensors ((name ,gen )) 307 308def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 309# position embeddings start at pad_token_id + 1, so just chop down the weight tensor 310if name == "embeddings.position_embeddings.weight" : 311if self ._position_offset is not None : 312data_torch = data_torch [self ._position_offset :,:] 313 314yield from super ().modify_tensors (data_torch ,name ,bid ) 315 316 317@ ModelBase . register ( "NomicBertModel" ) 318@ ModelBase . example ( "nomic-ai/nomic-embed-text-v1.5" ) 319class NomicBertModel (BertModel ): 320model_arch = gguf .MODEL_ARCH .BERT 321 322def __init__ (self ,dir_model :Path ,ftype :gguf .LlamaFileType ,fname_out :Path ,** kwargs :Any ): 323hparams = kwargs .pop ("hparams" ,None ) 324if hparams is None : 325hparams = ModelBase .load_hparams (dir_model ,False ) 326 327self .is_moe = bool (hparams .get ("moe_every_n_layers" )) 328self .model_arch = gguf .MODEL_ARCH .NOMIC_BERT_MOE if self .is_moe else gguf .MODEL_ARCH .NOMIC_BERT 329 330super ().__init__ (dir_model ,ftype ,fname_out ,hparams = hparams ,** kwargs ) 331 332self ._tokenizer_is_xlmroberta = self ._is_tokenizer_xlmroberta () 333if self ._tokenizer_is_xlmroberta : 334self ._xlmroberta_tokenizer_init () 335 336npos ,mtp = self .hparams ["n_positions" ],self .hparams .get ("max_trained_positions" ,2048 ) 337if npos == 8192 and mtp == 2048 : 338self .hparams ["n_positions" ]= 2048 # nomic-embed-text v1 and v1.5 are trained for 2048 tokens. 339elif npos == 2048 and mtp == 2048 : 340self .hparams ["n_positions" ]= 512 # nomic-embed-text-v2-moe is trained for 512 tokens. 341else : 342raise ValueError (f"unrecognized parameters: n_positions= { npos } , max_trained_positions= { mtp } " ) 343 344assert self .hparams ["activation_function" ]== "gelu" if self .is_moe else "swiglu" 345 346# this doesn't do anything in the HF version 347assert self .hparams ["causal" ]is False 348# no bias tensors unless MoE 349assert self .hparams ["qkv_proj_bias" ]== self .is_moe 350assert self .hparams ["mlp_fc1_bias" ]== self .is_moe 351assert self .hparams ["mlp_fc2_bias" ]== self .is_moe 352 353# norm at end of layer 354assert self .hparams ["prenorm" ]is False 355# standard RoPE 356assert self .hparams ["rotary_emb_fraction" ]== 1.0 357assert self .hparams ["rotary_emb_interleaved" ]is False 358assert self .hparams ["rotary_emb_scale_base" ]is None 359 360def set_vocab (self )-> None : 361if self ._tokenizer_is_xlmroberta : 362return self ._xlmroberta_set_vocab () 363return super ().set_vocab () 364 365@classmethod 366def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 367name ,gen = item 368 369# If the tensor is an experts bias tensor, skip it. 370if "mlp.experts.bias" in name : 371return None 372 373return super ().filter_tensors (item ) 374 375def modify_tensors (self ,data_torch :torch .Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,torch .Tensor ]]: 376if "mlp.experts.mlp.w1" in name : 377n_experts = self .find_hparam (["num_local_experts" ,"num_experts" ]) 378data_torch = data_torch .view (n_experts ,self .hparams ["n_inner" ],self .hparams ["n_embd" ]) 379name += ".weight" 380 381if "mlp.experts.mlp.w2" in name : 382n_experts = self .find_hparam (["num_local_experts" ,"num_experts" ]) 383data_torch = data_torch .view (n_experts ,self .hparams ["n_inner" ],self .hparams ["n_embd" ]) 384data_torch = data_torch .transpose (1 ,2 ) 385name += ".weight" 386 387yield from super ().modify_tensors (data_torch ,name ,bid ) 388 389def set_gguf_parameters (self ): 390super ().set_gguf_parameters () 391if self .is_moe : 392self .gguf_writer .add_moe_every_n_layers (self .hparams ["moe_every_n_layers" ]) 393self .gguf_writer .add_expert_used_count (self .hparams ["moe_top_k" ]) 394 395def _is_tokenizer_xlmroberta (self )-> bool : 396with open (self .dir_model / "tokenizer.json" )as f : 397tokenizer_json = json .load (f ) 398toktyp = tokenizer_json ["model" ]["type" ] 399if toktyp == "Unigram" : 400return True 401if toktyp == "WordPiece" : 402return False 403raise ValueError (f"unknown tokenizer: { toktyp } " ) 404 405 406@ ModelBase . register ( "NeoBERT" , "NeoBERTLMHead" , "NeoBERTForSequenceClassification" ) 407@ ModelBase . example ( "chandar-lab/NeoBERT" ) 408class NeoBert (BertModel ): 409model_arch = gguf .MODEL_ARCH .NEO_BERT 410 411def set_gguf_parameters (self ): 412super ().set_gguf_parameters () 413 414# NeoBERT uses 2/3 of the intermediate size as feed forward length 415self .gguf_writer .add_feed_forward_length (int (2 * self .hparams ["intermediate_size" ]/ 3 )) 416self .gguf_writer .add_rope_freq_base (10000.0 )# default value for NeoBERT 417self .gguf_writer .add_rope_scaling_type (gguf .RopeScalingType .NONE ) 418 419f_rms_eps = self .hparams .get ("norm_eps" ,1e-6 )# default value for NeoBERT 420self .gguf_writer .add_layer_norm_rms_eps (f_rms_eps ) 421logger .info (f"gguf: rms norm epsilon = { f_rms_eps } " ) 422 423self .gguf_writer .add_pooling_type (gguf .PoolingType .CLS )# https://huggingface.co/chandar-lab/NeoBERT#how-to-use 424 425@classmethod 426def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 427name ,gen = item 428 429if name .startswith ("decoder." ): 430return None 431 432if name .startswith ("model." ): 433name = name [6 :] 434 435return super ().filter_tensors ((name ,gen )) 436 437 438@ ModelBase . register ( "EuroBertModel" , "JinaEmbeddingsV5Model" ) 439@ ModelBase . example ( "hf-tiny-v2/tiny-random-EuroBertModel" , "jinaai/jina-embeddings-v5-text-nano" ) 440class EuroBertModel (TextModel ): 441model_arch = gguf .MODEL_ARCH .EUROBERT 442 443def set_vocab (self ): 444self .gguf_writer .add_add_bos_token (False ) 445self ._set_vocab_gpt2 () 446 447def set_gguf_parameters (self ): 448super ().set_gguf_parameters () 449 450# EuroBert is bidirectional (encoder) 451self .gguf_writer .add_causal_attention (False ) 452 453self .gguf_writer .add_rope_scaling_type (gguf .RopeScalingType .NONE ) 454 455self ._try_set_pooling_type () 456 457@classmethod 458def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 459name ,gen = item 460 461if name .startswith ("model." ): 462name = name [6 :] 463 464return super ().filter_tensors ((name ,gen )) 465 466 467@ ModelBase . register ( "XLMRobertaModel" , "XLMRobertaForSequenceClassification" ) 468@ ModelBase . example ( "BAAI/bge-m3" ) 469class XLMRobertaModel (BertModel ): 470model_arch = gguf .MODEL_ARCH .BERT 471_lora_files = {} 472_lora_names = [] 473 474def __init__ (self ,dir_model :Path ,ftype :gguf .LlamaFileType ,fname_out :Path ,** kwargs :Any ): 475hparams = kwargs .pop ("hparams" ,None ) 476if hparams is None : 477hparams = ModelBase .load_hparams (dir_model ,False ) 478 479if lora_names := hparams .get ("lora_adaptations" ): 480self ._lora_names = lora_names 481self .model_arch = gguf .MODEL_ARCH .JINA_BERT_V3 482 483super ().__init__ (dir_model ,ftype ,fname_out ,hparams = hparams ,** kwargs ) 484self ._xlmroberta_tokenizer_init () 485 486def generate_extra_tensors (self )-> Iterable [tuple [str ,Tensor ]]: 487if self ._lora_names : 488for name in self ._lora_names : 489fname = self .add_prefix_to_filename (self .fname_out ,f"lora- { name } -" ) 490self ._lora_files [name ]= gguf .GGUFWriter (fname ,arch = gguf .MODEL_ARCH_NAMES [self .model_arch ],endianess = self .endianess ,use_temp_file = self .use_temp_file ,dry_run = self .dry_run ) 491 492return super ().generate_extra_tensors () 493 494def set_type (self ): 495for lora_writer in self ._lora_files .values (): 496lora_writer .add_type (gguf .GGUFType .ADAPTER ) 497lora_writer .add_string (gguf .Keys .Adapter .TYPE ,"lora" ) 498super ().set_type () 499 500def set_vocab (self ): 501self ._xlmroberta_set_vocab () 502 503@classmethod 504def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 505name ,gen = item 506 507# if name starts with "roberta.", remove the prefix 508# e.g. https://huggingface.co/BAAI/bge-reranker-v2-m3/tree/main 509if name .startswith ("roberta." ): 510name = name [8 :] 511 512# jina-embeddings-v3 513if ".parametrizations." in name : 514name = name .replace (".parametrizations." ,"." ) 515if name .endswith (".original" ): 516name = name [:- 9 ] 517 518return super ().filter_tensors ((name ,gen )) 519 520def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 521# position embeddings start at pad_token_id + 1, so just chop down the weight tensor 522if name == "embeddings.position_embeddings.weight" : 523if self ._position_offset is not None : 524data_torch = data_torch [self ._position_offset :,:] 525 526if name .endswith (".0.lora_A" )or name .endswith (".0.lora_B" ): 527if name .startswith ("pooler.dense" ): 528return 529 530num_loras = data_torch .size (0 ) 531assert num_loras == len (self ._lora_names ) 532 533# Split out each LoRA in their own GGUF 534for i ,lora_writer in enumerate (self ._lora_files .values ()): 535new_name = self .map_tensor_name (name [:- 9 ])+ name [- 7 :].lower () 536data = data_torch [i , :, :] 537# Transpose/flip token_embd/types into correct shape 538if new_name == "token_embd.weight.lora_b" : 539data = data .T 540elif new_name .startswith ("token_types.weight." ): 541new_name = new_name [:- 1 ]+ ("a" if new_name [- 1 :]== "b" else "b" ) 542lora_writer .add_tensor (new_name ,data .float ().numpy (),raw_dtype = gguf .GGMLQuantizationType .F32 ) 543 544return 545 546yield from super ().modify_tensors (data_torch ,name ,bid ) 547 548def set_gguf_parameters (self ): 549super ().set_gguf_parameters () 550 551# jina-embeddings-v3 552lora_alpha = self .hparams .get ("lora_alpha" ) 553if lora_prompt_prefixes := self .hparams .get ("task_instructions" ): 554assert self ._lora_files and all (lora_name in lora_prompt_prefixes for lora_name in self ._lora_files .keys ()) 555for lora_name ,lora_writer in self ._lora_files .items (): 556lora_writer .add_float32 (gguf .Keys .Adapter .LORA_ALPHA ,lora_alpha if lora_alpha is not None else 1.0 ) 557lora_writer .add_string (gguf .Keys .Adapter .LORA_TASK_NAME ,lora_name ) 558if lora_prompt_prefixes : 559lora_writer .add_string (gguf .Keys .Adapter .LORA_PROMPT_PREFIX ,lora_prompt_prefixes [lora_name ]) 560 561def write (self ): 562super ().write () 563for lora_writer in self ._lora_files .values (): 564lora_writer .write_header_to_file () 565lora_writer .write_kv_data_to_file () 566lora_writer .write_tensors_to_file (progress = True ) 567lora_writer .close () 568 569 570@ ModelBase . register ( "JinaBertModel" , "JinaBertForMaskedLM" ) 571@ ModelBase . example ( "jinaai/jina-embeddings-v2-base-en" ) 572class JinaBertV2Model (BertModel ): 573model_arch = gguf .MODEL_ARCH .JINA_BERT_V2 574 575def set_vocab (self ): 576tokenizer_class = 'BertTokenizer' 577with open (self .dir_model / "tokenizer_config.json" ,"r" ,encoding = "utf-8" )as f : 578tokenizer_class = json .load (f )['tokenizer_class' ] 579 580if tokenizer_class == 'BertTokenizer' : 581super ().set_vocab () 582elif tokenizer_class == 'RobertaTokenizer' : 583pre_tokenizer_type = None 584tokenizer_json_path = self .dir_model / "tokenizer.json" 585if tokenizer_json_path .is_file (): 586with open (tokenizer_json_path ,"r" ,encoding = "utf-8" )as f : 587pre_tokenizer_type = json .load (f ).get ("pre_tokenizer" , {}).get ("type" ) 588 589if pre_tokenizer_type == "Whitespace" : 590self ._set_vocab_whitespace () 591else : 592self ._set_vocab_gpt2 () 593self .gguf_writer .add_token_type_count (2 ) 594else : 595raise NotImplementedError (f'Tokenizer { tokenizer_class } is not supported for JinaBertModel' ) 596 597 598@ ModelBase . register ( "ModernBertModel" , "ModernBertForMaskedLM" , "ModernBertForSequenceClassification" ) 599@ ModelBase . example ( "answerdotai/ModernBERT-base" ) 600class ModernBertModel (BertModel ): 601model_arch = gguf .MODEL_ARCH .MODERN_BERT 602 603def set_vocab (self ): 604self .gguf_writer .add_add_bos_token (True ) 605self .gguf_writer .add_add_eos_token (True ) 606self .gguf_writer .add_add_sep_token (True ) 607self ._set_vocab_gpt2 () 608 609def set_gguf_parameters (self ): 610super ().set_gguf_parameters () 611self .gguf_writer .add_sliding_window (self .hparams ["local_attention" ]) 612if (sliding_window_pattern := self .hparams .get ("global_attn_every_n_layers" ))is not None : 613self .gguf_writer .add_sliding_window_pattern (sliding_window_pattern ) 614self .gguf_writer .add_rope_scaling_type (gguf .RopeScalingType .NONE ) 615self .gguf_writer .add_vocab_size (self .hparams ["vocab_size" ]) 616# FFN activation: ModernBert uses a GLU pair (ffn_up output is 2*n_ff). The 617# original ModernBERT uses GELU (-> GeGLU); some derivatives such as IBM 618# Granite Embedding 97m R2 use SiLU (-> SwiGLU). Persist this so the 619# llama.cpp graph can pick the matching activation. 620if hidden_act := self .hparams .get ("hidden_activation" ): 621self .gguf_writer .add_hidden_act (hidden_act ) 622 623@classmethod 624def filter_tensors (cls ,item :tuple [str ,Callable [[],Tensor ]])-> tuple [str ,Callable [[],Tensor ]]| None : 625name ,gen = item 626 627if name .startswith ("model." ): 628name = name [6 :] 629 630return super ().filter_tensors ((name ,gen )) 631 632def modify_tensors (self ,data_torch :Tensor ,name :str ,bid :int | None )-> Iterable [tuple [str ,Tensor ]]: 633if self .cls_out_labels : 634# For BertForSequenceClassification (direct projection layer) 635if name == "classifier.weight" : 636name = "classifier.out_proj.weight" 637 638if name == "classifier.bias" : 639name = "classifier.out_proj.bias" 640 641yield from super ().modify_tensors (data_torch ,name ,bid )