char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
29b28a982
char/aliceai-t5-moe
1#!/usr/bin/env python3 2from __future__import annotations 3 4import logging 5import argparse 6import os 7import struct 8import sys 9from enum import IntEnum 10from pathlib import Path 11 12import numpy as np 13 14if 'NO_LOCAL_GGUF' not in os .environ : 15sys .path .insert (1 ,str (Path (__file__ ).parent / 'gguf-py' )) 16import gguf 17 18logger = logging .getLogger ("ggml-to-gguf" ) 19 20 21class GGMLFormat (IntEnum ): 22GGML = 0 23GGMF = 1 24GGJT = 2 25 26 27class GGMLFType (IntEnum ): 28ALL_F32 = 0 29MOSTLY_F16 = 1 30MOSTLY_Q4_0 = 2 31MOSTLY_Q4_1 = 3 32MOSTLY_Q4_1_SOME_F16 = 4 33MOSTLY_Q8_0 = 7 34MOSTLY_Q5_0 = 8 35MOSTLY_Q5_1 = 9 36MOSTLY_Q2_K = 10 37MOSTLY_Q3_K_S = 11 38MOSTLY_Q3_K_M = 12 39MOSTLY_Q3_K_L = 13 40MOSTLY_Q4_K_S = 14 41MOSTLY_Q4_K_M = 15 42MOSTLY_Q5_K_S = 16 43MOSTLY_Q5_K_M = 17 44MOSTLY_Q6_K = 18 45 46 47class Hyperparameters : 48def __init__ (self ): 49self .n_vocab = self .n_embd = self .n_mult = self .n_head = 0 50self .n_layer = self .n_rot = self .n_ff = 0 51self .ftype = GGMLFType .ALL_F32 52 53def set_n_ff (self ,model ): 54ff_tensor_idx = model .tensor_map .get (b'layers.0.feed_forward.w1.weight' ) 55assert ff_tensor_idx is not None ,'Missing layer 0 FF tensor' 56ff_tensor = model .tensors [ff_tensor_idx ] 57self .n_ff = ff_tensor .dims [1 ] 58 59def load (self ,data ,offset ): 60 ( 61self .n_vocab , 62self .n_embd , 63self .n_mult , 64self .n_head , 65self .n_layer , 66self .n_rot , 67ftype , 68 )= struct .unpack ('<7I' ,data [offset :offset + (4 * 7 )]) 69try : 70self .ftype = GGMLFType (ftype ) 71except ValueError : 72raise ValueError (f'Invalid ftype { ftype } ' ) 73return 4 * 7 74 75def __str__ (self ): 76return f'<Hyperparameters: n_vocab= { self . n_vocab } , n_embd= { self . n_embd } , n_mult= { self . n_mult } , n_head= { self . n_head } , n_layer= { self . n_layer } , n_rot= { self . n_rot } , n_ff= { self . n_ff } , ftype= { self . ftype . name } >' 77 78 79class Vocab : 80def __init__ (self ,load_scores = True ): 81self .items = [] 82self .load_scores = load_scores 83 84def load (self ,data ,offset ,n_vocab ): 85orig_offset = offset 86for _ in range (n_vocab ): 87itemlen = struct .unpack ('<I' ,data [offset :offset + 4 ])[0 ] 88assert itemlen < 4096 ,'Absurd vocab item length' 89offset += 4 90item_text = bytes (data [offset :offset + itemlen ]) 91offset += itemlen 92if self .load_scores : 93item_score = struct .unpack ('<f' ,data [offset :offset + 4 ])[0 ] 94offset += 4 95else : 96item_score = 0.0 97self .items .append ((item_text ,item_score )) 98return offset - orig_offset 99 100 101class Tensor : 102def __init__ (self ,use_padding = True ): 103self .name = None 104self .dims :tuple [int , ...]= () 105self .dtype = None 106self .start_offset = 0 107self .len_bytes = np .int64 (0 ) 108self .use_padding = use_padding 109 110def load (self ,data ,offset ): 111orig_offset = offset 112 (n_dims ,name_len ,dtype )= struct .unpack ('<3I' ,data [offset :offset + 12 ]) 113assert n_dims >= 0 and n_dims <= 4 ,f'Invalid tensor dimensions { n_dims } ' 114assert name_len < 4096 ,'Absurd tensor name length' 115self .dtype = gguf .GGMLQuantizationType (dtype ) 116quant = gguf .GGML_QUANT_SIZES .get (self .dtype ) 117assert quant is not None ,'Unknown tensor type' 118 (blksize ,tysize )= quant 119offset += 12 120self .dims = struct .unpack (f'< { n_dims } I' ,data [offset :offset + (4 * n_dims )]) 121offset += 4 * n_dims 122self .name = bytes (data [offset :offset + name_len ]) 123offset += name_len 124pad = ((offset + 31 )& ~ 31 )- offset if self .use_padding else 0 125offset += pad 126n_elems = np .prod (self .dims ) 127n_bytes = np .int64 (np .int64 (n_elems )* np .int64 (tysize ))// np .int64 (blksize ) 128self .start_offset = offset 129self .len_bytes = n_bytes 130offset += n_bytes 131return offset - orig_offset 132 133 134class GGMLModel : 135 136file_format :GGMLFormat 137format_version :int 138 139def __init__ (self ): 140self .hyperparameters = None 141self .vocab = None 142self .tensor_map = {} 143self .tensors = [] 144 145def validate_header (self ,data ,offset ): 146magic = bytes (data [offset :offset + 4 ]) 147if magic == b'GGUF' : 148raise ValueError ('File is already in GGUF format.' ) 149if magic == b'lmgg' : 150self .file_format = GGMLFormat .GGML 151self .format_version = 1 152return 4 153version = struct .unpack ('<I' ,data [offset + 4 :offset + 8 ])[0 ] 154if magic == b'fmgg' : 155if version != 1 : 156raise ValueError (f'Cannot handle unexpected GGMF file version { version } ' ) 157self .file_format = GGMLFormat .GGMF 158self .format_version = version 159return 8 160if magic == b'tjgg' : 161if version < 1 or version > 3 : 162raise ValueError (f'Cannot handle unexpected GGJT file version { version } ' ) 163self .file_format = GGMLFormat .GGJT 164self .format_version = version 165return 8 166raise ValueError (f"Unexpected file magic { magic !r } ! This doesn't look like a GGML format file." ) 167 168def validate_conversion (self ,ftype ): 169err = '' 170if (self .file_format < GGMLFormat .GGJT or self .format_version < 2 ): 171if ftype not in (GGMLFType .ALL_F32 ,GGMLFType .MOSTLY_F16 ): 172err = 'Quantizations changed in GGJTv2. Can only convert unquantized GGML files older than GGJTv2.' 173elif (self .file_format == GGMLFormat .GGJT and self .format_version == 2 ): 174if ftype in (GGMLFType .MOSTLY_Q4_0 ,GGMLFType .MOSTLY_Q4_1 , 175GGMLFType .MOSTLY_Q4_1_SOME_F16 ,GGMLFType .MOSTLY_Q8_0 ): 176err = 'Q4 and Q8 quantizations changed in GGJTv3.' 177if len (err )> 0 : 178raise ValueError (f' { err } Sorry, your { self . file_format . name } v { self . format_version } file of type { ftype . name } is not eligible for conversion.' ) 179 180def load (self ,data ,offset ): 181offset += self .validate_header (data ,offset ) 182hp = Hyperparameters () 183offset += hp .load (data ,offset ) 184logger .info (f'* File format: { self . file_format . name } v { self . format_version } with ftype { hp . ftype . name } ' ) 185self .validate_conversion (hp .ftype ) 186vocab = Vocab (load_scores = self .file_format > GGMLFormat .GGML ) 187offset += vocab .load (data ,offset ,hp .n_vocab ) 188tensors :list [Tensor ]= [] 189tensor_map = {} 190while offset < len (data ): 191tensor = Tensor (use_padding = self .file_format > GGMLFormat .GGMF ) 192offset += tensor .load (data ,offset ) 193tensor_map [tensor .name ]= len (tensors ) 194tensors .append (tensor ) 195self .hyperparameters = hp 196self .vocab = vocab 197self .tensors = tensors 198self .tensor_map = tensor_map 199hp .set_n_ff (self ) 200return offset 201 202 203class GGMLToGGUF : 204def __init__ (self ,ggml_model ,data ,cfg ,params_override = None ,vocab_override = None ,special_vocab = None ): 205hp = ggml_model .hyperparameters 206self .model = ggml_model 207self .data = data 208self .cfg = cfg 209self .params_override = params_override 210self .vocab_override = vocab_override 211self .special_vocab = special_vocab 212if params_override is not None : 213n_kv_head = params_override .n_head_kv 214else : 215if cfg .gqa == 1 : 216n_kv_head = hp .n_head 217else : 218gqa = float (cfg .gqa ) 219n_kv_head = None 220for x in range (1 ,256 ): 221if float (hp .n_head )/ float (x )== gqa : 222n_kv_head = x 223assert n_kv_head is not None ,"Couldn't determine n_kv_head from GQA param" 224logger .info (f'- Guessed n_kv_head = { n_kv_head } based on GQA { cfg . gqa } ' ) 225self .n_kv_head = n_kv_head 226self .name_map = gguf .get_tensor_name_map (gguf .MODEL_ARCH .LLAMA ,ggml_model .hyperparameters .n_layer ) 227 228def save (self ): 229logger .info ('* Preparing to save GGUF file' ) 230gguf_writer = gguf .GGUFWriter ( 231self .cfg .output , 232gguf .MODEL_ARCH_NAMES [gguf .MODEL_ARCH .LLAMA ], 233use_temp_file = False ) 234self .add_params (gguf_writer ) 235self .add_vocab (gguf_writer ) 236if self .special_vocab is not None : 237self .special_vocab .add_to_gguf (gguf_writer ) 238self .add_tensors (gguf_writer ) 239logger .info (" gguf: write header" ) 240gguf_writer .write_header_to_file () 241logger .info (" gguf: write metadata" ) 242gguf_writer .write_kv_data_to_file () 243logger .info (" gguf: write tensors" ) 244gguf_writer .write_tensors_to_file () 245gguf_writer .close () 246 247def add_params (self ,gguf_writer ): 248hp = self .model .hyperparameters 249cfg = self .cfg 250if cfg .desc is not None : 251desc = cfg .desc 252else : 253desc = f'converted from legacy { self . model . file_format . name } v { self . model . format_version } { hp . ftype . name } format' 254try : 255# Filenames aren't necessarily valid UTF8. 256name = cfg .name if cfg .name is not None else cfg .input .name 257except UnicodeDecodeError : 258name = None 259logger .info ('* Adding model parameters and KV items' ) 260if name is not None : 261gguf_writer .add_name (name ) 262gguf_writer .add_description (desc ) 263gguf_writer .add_file_type (int (hp .ftype )) 264if self .params_override is not None : 265po = self .params_override 266assert po .n_embd == hp .n_embd ,'Model hyperparams mismatch' 267assert po .n_layer == hp .n_layer ,'Model hyperparams mismatch' 268assert po .n_head == hp .n_head ,'Model hyperparams mismatch' 269gguf_writer .add_context_length (po .n_ctx ) 270gguf_writer .add_embedding_length (po .n_embd ) 271gguf_writer .add_block_count (po .n_layer ) 272gguf_writer .add_feed_forward_length (po .n_ff ) 273gguf_writer .add_rope_dimension_count (po .n_embd // po .n_head ) 274gguf_writer .add_head_count (po .n_head ) 275gguf_writer .add_head_count_kv (po .n_head_kv ) 276gguf_writer .add_layer_norm_rms_eps (po .f_norm_eps ) 277return 278gguf_writer .add_context_length (cfg .context_length ) 279gguf_writer .add_embedding_length (hp .n_embd ) 280gguf_writer .add_block_count (hp .n_layer ) 281gguf_writer .add_feed_forward_length (hp .n_ff ) 282gguf_writer .add_rope_dimension_count (hp .n_embd // hp .n_head ) 283gguf_writer .add_head_count (hp .n_head ) 284gguf_writer .add_head_count_kv (self .n_kv_head ) 285gguf_writer .add_layer_norm_rms_eps (float (cfg .eps )) 286 287def add_vocab (self ,gguf_writer ): 288hp = self .model .hyperparameters 289gguf_writer .add_tokenizer_model ('llama' ) 290gguf_writer .add_tokenizer_pre ('default' ) 291tokens = [] 292scores = [] 293toktypes = [] 294if self .vocab_override is not None : 295vo = self .vocab_override 296logger .info ('* Adding vocab item(s)' ) 297for (_ , (vbytes ,score ,ttype ))in enumerate (vo .all_tokens ()): 298tokens .append (vbytes ) 299scores .append (score ) 300toktypes .append (ttype ) 301assert len (tokens )== hp .n_vocab , \ 302f'Override vocab has a different number of items than hyperparameters - override = { len ( tokens ) } but n_vocab= { hp . n_vocab } ' 303gguf_writer .add_token_list (tokens ) 304gguf_writer .add_token_scores (scores ) 305if len (toktypes )> 0 : 306gguf_writer .add_token_types (toktypes ) 307return 308logger .info (f'* Adding { hp . n_vocab } vocab item(s)' ) 309assert len (self .model .vocab .items )>= 3 ,'Cannot handle unexpectedly short model vocab' 310for (tokid , (vbytes ,vscore ))in enumerate (self .model .vocab .items ): 311tt = 1 # Normal 312# Special handling for UNK, BOS, EOS tokens. 313if tokid <= 2 : 314if tokid == 0 : 315vbytes = b'<unk>' 316tt = 2 317elif tokid == 1 : 318vbytes = b'<s>' 319tt = 3 320else : 321vbytes = b'</s>' 322tt = 3 323elif len (vbytes )== 0 : 324tt = 3 # Control 325elif tokid >= 3 and tokid <= 258 and len (vbytes )== 1 : 326vbytes = bytes (f'<0x { vbytes [ 0 ]:02X } >' ,encoding = 'UTF-8' ) 327tt = 6 # Byte 328else : 329vbytes = vbytes .replace (b' ' ,b'\xe2\x96\x81' ) 330toktypes .append (tt ) 331tokens .append (vbytes ) 332scores .append (vscore ) 333gguf_writer .add_token_list (tokens ) 334gguf_writer .add_token_scores (scores ) 335gguf_writer .add_token_types (toktypes ) 336gguf_writer .add_unk_token_id (0 ) 337gguf_writer .add_bos_token_id (1 ) 338gguf_writer .add_eos_token_id (2 ) 339 340def add_tensors (self ,gguf_writer ): 341tensor_map = self .name_map 342data = self .data 343logger .info (f'* Adding { len ( self . model . tensors ) } tensor(s)' ) 344for tensor in self .model .tensors : 345name = str (tensor .name ,'UTF-8' ) 346mapped_name = tensor_map .get_name (name ,try_suffixes = (".weight" ,".bias" )) 347assert mapped_name is not None ,f'Bad name { name } ' 348tempdims = list (tensor .dims [:]) 349if len (tempdims )> 1 : 350temp = tempdims [1 ] 351tempdims [1 ]= tempdims [0 ] 352tempdims [0 ]= temp 353gguf_writer .add_tensor ( 354mapped_name , 355data [tensor .start_offset :tensor .start_offset + tensor .len_bytes ], 356raw_shape = tempdims , 357raw_dtype = tensor .dtype ) 358 359 360def handle_metadata (cfg ,hp ): 361import examples .convert_legacy_llama as convert 362 363assert cfg .model_metadata_dir .is_dir (),'Metadata dir is not a directory' 364hf_config_path = cfg .model_metadata_dir / "config.json" 365orig_config_path = cfg .model_metadata_dir / "params.json" 366# We pass a fake model here. "original" mode will check the shapes of some 367# tensors if information is missing in the .json file: other than that, the 368# model data isn't used so this should be safe (at least for now). 369fakemodel = { 370'tok_embeddings.weight' :convert .LazyTensor .__new__ (convert .LazyTensor ), 371'layers.0.feed_forward.w1.weight' :convert .LazyTensor .__new__ (convert .LazyTensor ), 372 } 373fakemodel ['tok_embeddings.weight' ].shape = [hp .n_vocab ] 374fakemodel ['layers.0.feed_forward.w1.weight' ].shape = [hp .n_ff ] 375if hf_config_path .exists (): 376params = convert .Params .loadHFTransformerJson (fakemodel ,hf_config_path ) 377elif orig_config_path .exists (): 378params = convert .Params .loadOriginalParamsJson (fakemodel ,orig_config_path ) 379else : 380raise ValueError ('Unable to load metadata' ) 381vocab_path = Path (cfg .vocab_dir if cfg .vocab_dir is not None else cfg .model_metadata_dir ) 382vocab_factory = convert .VocabFactory (vocab_path ) 383vocab ,special_vocab = vocab_factory .load_vocab (cfg .vocabtype .split ("," ),cfg .model_metadata_dir ) 384convert .check_vocab_size (params ,vocab ) 385return params ,vocab ,special_vocab 386 387 388def handle_args (): 389parser = argparse .ArgumentParser (description = 'Convert GGML models to GGUF' ) 390parser .add_argument ('--input' ,'-i' ,type = Path ,required = True , 391help = 'Input GGMLv3 filename' ) 392parser .add_argument ('--output' ,'-o' ,type = Path ,required = True , 393help = 'Output GGUF filename' ) 394parser .add_argument ('--name' , 395help = 'Set model name' ) 396parser .add_argument ('--desc' , 397help = 'Set model description' ) 398parser .add_argument ('--gqa' ,type = int ,default = 1 , 399help = 'grouped-query attention factor (use 8 for LLaMA2 70B)' ) 400parser .add_argument ('--eps' ,default = '5.0e-06' , 401help = 'RMS norm eps: Use 1e-6 for LLaMA1 and OpenLLaMA, use 1e-5 for LLaMA2' ) 402parser .add_argument ('--context-length' ,'-c' ,type = int ,default = 2048 , 403help = 'Default max context length: LLaMA1 is typically 2048, LLaMA2 is typically 4096' ) 404parser .add_argument ('--model-metadata-dir' ,'-m' ,type = Path , 405help = 'Load HuggingFace/.pth vocab and metadata from the specified directory' ) 406parser .add_argument ("--vocab-dir" ,type = Path , 407help = "directory containing tokenizer.model, if separate from model file - only meaningful with --model-metadata-dir" ) 408parser .add_argument ("--vocabtype" ,default = "spm,hfft" , 409help = "vocab format - only meaningful with --model-metadata-dir and/or --vocab-dir (default: spm,hfft)" ) 410parser .add_argument ("--verbose" ,action = "store_true" ,help = "increase output verbosity" ) 411return parser .parse_args () 412 413 414def main (): 415cfg = handle_args () 416logging .basicConfig (level = logging .DEBUG if cfg .verbose else logging .INFO ) 417logger .info (f'* Using config: { cfg } ' ) 418logger .warning ('=== WARNING === Be aware that this conversion script is best-effort. Use a native GGUF model if possible. === WARNING ===' ) 419if cfg .model_metadata_dir is None and (cfg .gqa == 1 or cfg .eps == '5.0e-06' ): 420logger .info ('- Note: If converting LLaMA2, specifying "--eps 1e-5" is required. 70B models also need "--gqa 8".' ) 421data = np .memmap (cfg .input ,mode = 'r' ) 422model = GGMLModel () 423logger .info ('* Scanning GGML input file' ) 424offset = model .load (data ,0 )# noqa 425logger .info (f'* GGML model hyperparameters: { model . hyperparameters } ' ) 426vocab_override = None 427params_override = None 428special_vocab = None 429if cfg .model_metadata_dir is not None : 430 (params_override ,vocab_override ,special_vocab )= handle_metadata (cfg ,model .hyperparameters ) 431logger .info ('!! Note: When overriding params the --gqa, --eps and --context-length options are ignored.' ) 432logger .info (f'* Overriding params: { params_override } ' ) 433logger .info (f'* Overriding vocab: { vocab_override } ' ) 434logger .info (f'* Special vocab: { special_vocab } ' ) 435else : 436logger .warning ('\n=== WARNING === Special tokens may not be converted correctly. Use --model-metadata-dir if possible === WARNING ===\n' ) 437if model .file_format == GGMLFormat .GGML : 438logger .info ('! This is a very old GGML file that does not contain vocab scores. Strongly recommend using model metadata!' ) 439converter = GGMLToGGUF ( 440model ,data ,cfg , 441params_override = params_override , 442vocab_override = vocab_override , 443special_vocab = special_vocab 444 ) 445converter .save () 446logger .info (f'* Successful completion. Output saved to: { cfg . output } ' ) 447 448 449if __name__ == '__main__' : 450main ()