char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
680a9ae63
char/aliceai-t5-moe
1#include "ggml.h" 2#include "gguf.h" 3 4#include "arg.h" 5#include "build-info.h" 6#include "common.h" 7#include "llama.h" 8#include "pca.hpp" 9#include "mean.hpp" 10 11#include <clocale> 12 13#ifdef GGML_USE_CUDA 14#include "ggml-cuda.h" 15#endif 16 17#ifdef GGML_USE_METAL 18#include "ggml-metal.h" 19#endif 20 21#include <algorithm> 22#include <climits> 23#include <cstdio> 24#include <cstring> 25#include <fstream> 26#include <iostream> 27#include <string> 28#include <tuple> 29#include <vector> 30 31 32////////////////////////////////////////////////// 33// utils 34 35template < class Iter > 36static std::string tokens_to_str (llama_context * ctx ,Iter begin ,Iter end ) { 37 std::string ret ; 38for (;begin != end ;++ begin ) { 39ret += common_token_to_piece (ctx ,* begin ); 40 } 41 42return ret ; 43} 44 45static void print_usage (int ,char ** argv ) { 46printf ("\nexample usage:\n" ); 47printf ("\n CPU only: %s -m ./llama-3.Q4_K_M.gguf\n" ,argv [0 ]); 48printf ("\n with GPU: %s -m ./llama-3.Q4_K_M.gguf -ngl 99\n" ,argv [0 ]); 49printf ("\n advanced: %s -m ./llama-3.Q4_K_M.gguf -ngl 99 --pca-iter 2000 --pca-batch 100\n" ,argv [0 ]); 50printf ("\n using mean: %s -m ./llama-3.Q4_K_M.gguf --method mean\n" ,argv [0 ]); 51printf ("\n" ); 52} 53 54////////////////////////////////////////////////// 55 56 57// cb_eval is reused for each pair of positive - negative prompt 58struct callback_data { 59ggml_context * ctx_ggml = nullptr ;// holds v_pos, v_neg, v_diff_filtered 60 61int n_layers = 0 ; 62int n_tokens = 0 ; 63bool is_eval_pos = true; 64 65// each element of the vector correspond to one layer 66 std::vector < struct ggml_tensor *> v_pos ;// vector of matrices of size [n_embd, n_tokens] 67 std::vector < struct ggml_tensor *> v_neg ;// vector of matrices of size [n_embd, n_tokens] 68 std::vector < struct ggml_tensor *> v_diff_filtered ;// vector of matrices of size [n_embd, n_nonzero_rows]. NOTE: n_nonzero_rows maybe different for each layer 69 70// save a tensor into either v_pos or v_neg (decided by is_eval_pos) 71void save_tensor_for_layer (struct ggml_tensor * t ) { 72GGML_ASSERT (t -> type == GGML_TYPE_F32 ); 73 74if (ctx_ggml == nullptr ) { 75// alloc a new ctx_ggml if needed 76struct ggml_init_params params_ggml = { 77/*.mem_size =*/ ggml_tensor_overhead ()* n_layers * 3u , 78/*.mem_buffer =*/ NULL , 79/*.no_alloc =*/ true, 80 }; 81ctx_ggml = ggml_init (params_ggml ); 82 } 83 84// copy tensor data 85auto n_bytes = ggml_nbytes (t ); 86struct ggml_tensor * t_layer = ggml_new_tensor_2d (ctx_ggml ,t -> type ,t -> ne [0 ],t -> ne [1 ]); 87t_layer -> data = malloc (n_bytes );// TODO @ngxson : get rid of this malloc somehow 88ggml_backend_tensor_get (t ,t_layer -> data ,0 ,n_bytes ); 89ggml_set_name (t_layer ,ggml_get_name (t )); 90//print_debug_tensor(t_layer); 91 92if (is_eval_pos ) { 93v_pos .push_back (t_layer ); 94 }else { 95v_neg .push_back (t_layer ); 96 } 97 } 98 99// calculate diff (v_pos - v_neg) and place the result back to v_pos 100// all zero rows in the diff tensor will also be removed 101// NOTE: final layer is ignored. we only have (n_layers - 1) to process 102 std::vector < struct ggml_tensor *> calc_diff () { 103for (float il = 0 ;il < v_pos .size ();il ++ ) { 104float * a = (float * )v_pos [il ]-> data ; 105float * b = (float * )v_neg [il ]-> data ; 106size_t n_elem = ggml_nelements (v_pos [il ]); 107for (size_t j = 0 ;j < n_elem ;j ++ ) { 108a [j ]-= b [j ]; 109 } 110//print_debug_tensor(v_pos[i]); 111auto diff_filtered = filter_nonzero_rows (v_pos [il ]); 112v_diff_filtered .push_back (diff_filtered ); 113 } 114return v_diff_filtered ;// for convenient, we return the result std::vector 115 } 116 117// delete zero rows from a given 2D tensor 118struct ggml_tensor * filter_nonzero_rows (struct ggml_tensor * a ) { 119//printf("filter_nonzero_rows\n"); 120auto is_row_all_zeros = [](struct ggml_tensor * t ,int row ,float eps )-> bool { 121// check if given row containing all zero elements 122int n_cols = t -> ne [0 ];// hint: should be equal to n_embd 123for (int col = 0 ;col < n_cols ;++ col ) { 124if (ggml_get_f32_nd (t ,col ,row ,0 ,0 )> eps ) { 125return false; 126 } 127 } 128return true; 129 }; 130 std::vector < int > rows_to_copy ;// the idx of non-zero cols (to be copied to row of diff_filtered) 131for (int i_row = 0 ;i_row < a -> ne [1 ];i_row ++ ) { 132if (!is_row_all_zeros (a ,i_row ,1e-6 )) { 133rows_to_copy .push_back (i_row ); 134 } 135 } 136 137// get "n_nonzero_rows" for the output "diff_filtered" 138int n_nonzero_rows = rows_to_copy .size (); 139//printf("n_nonzero_rows: %d\n", n_nonzero_rows); 140int n_embd = a -> ne [0 ]; 141GGML_ASSERT (n_nonzero_rows > 0 ); 142 143// diff_filtered: [n_embd, n_nonzero_rows] 144struct ggml_tensor * diff_filtered = ggml_new_tensor_2d ( 145ctx_ggml ,GGML_TYPE_F32 ,n_embd ,n_nonzero_rows ); 146ggml_format_name (diff_filtered ,"diff_filtered_%s" ,a -> name ); 147diff_filtered -> data = malloc (ggml_nbytes (diff_filtered )); 148 149// copy non-zero rows 150for (int dest_row = 0 ;dest_row < n_nonzero_rows ;dest_row ++ ) { 151int src_row = rows_to_copy [dest_row ]; 152for (int i = 0 ;i < n_embd ;i ++ ) { 153float src_elem = ggml_get_f32_nd (a ,i ,src_row ,0 ,0 ); 154ggml_set_f32_nd (diff_filtered ,i ,dest_row ,0 ,0 ,src_elem ); 155 } 156 } 157 158//print_debug_tensor(diff_filtered); 159 160return diff_filtered ; 161 } 162 163// we don't implement destructor, because we want to reuse callback_data. we just want to free the tensors 164void reset () { 165for (auto ptr :v_pos )free (ptr -> data ); 166for (auto ptr :v_neg )free (ptr -> data ); 167for (auto ptr :v_diff_filtered )free (ptr -> data ); 168v_pos .clear (); 169v_neg .clear (); 170v_diff_filtered .clear (); 171if (ctx_ggml ) { 172ggml_free (ctx_ggml ); 173 } 174ctx_ggml = nullptr ; 175 } 176}; 177 178/** 179* process_ctx is used to store the ggml context for pre-post processing the diff vectors 180* in short, input => v_diff and output => v_final 181*/ 182struct train_context { 183ggml_context * ctx_ggml ; 184int n_embd ; 185int n_layers ; 186 187/* pair of prompts to be used for generating final vector */ 188 std::vector < std::string > positive_entries ; 189 std::vector < std::string > negative_entries ; 190 191// each element of the vector correspond to one layer 192// NOTE: the last layer is discard. therefore, we will have (n_layers - 1) elements here 193// NOTE (2): v_diff is transposed from v_diff_tmp 194 std::vector < struct ggml_tensor *> v_diff ;// vector of matrices of size [m, n_embd] where m ~ n_tokens * n_completions (v_diff contains no zero-rows) 195 std::vector < struct ggml_tensor *> v_final ;// vector of vectors of size [n_embd] to be written to file 196 197// to easily re-alloc when concat v_diff, we temporary store v_diff in a vector instead of a tensor 198// v_diff_tmp will get converted unto v_diff later on 199 std::vector < std::vector < uint8_t >> v_diff_tmp ; 200 201train_context (int n_embd_ ,int n_layers_ ) { 202n_embd = n_embd_ ; 203n_layers = n_layers_ ; 204struct ggml_init_params params_ggml = { 205/*.mem_size =*/ ggml_tensor_overhead ()* (n_layers - 1 )* 2u , 206/*.mem_buffer =*/ NULL , 207/*.no_alloc =*/ true, 208 }; 209ctx_ggml = ggml_init (params_ggml ); 210for (int il = 0 ;il < n_layers - 1 ;il ++ ) { 211 std::vector < uint8_t > empty ; 212v_diff_tmp .push_back (empty ); 213auto t = ggml_new_tensor_1d (ctx_ggml ,GGML_TYPE_F32 ,n_embd ); 214t -> data = malloc (ggml_nbytes (t ));// TODO: get rid of malloc if possible 215v_final .push_back (t ); 216 } 217 } 218 219// add new rows into existing tensor in v_diff_tmp 220void concat_diff_tmp (const std::vector < struct ggml_tensor *> & diff_filtered ) { 221GGML_ASSERT ((int )diff_filtered .size ()== n_layers - 1 ); 222for (int il = 0 ;il < n_layers - 1 ;il ++ ) { 223auto t = diff_filtered [il ]; 224auto & diff_tmp = v_diff_tmp [il ]; 225size_t curr_size = diff_tmp .size (); 226diff_tmp .resize (curr_size + ggml_nbytes (t )); 227memcpy (diff_tmp .data ()+ curr_size ,t -> data ,ggml_nbytes (t )); 228 } 229 } 230 231// build the v_diff tensors from v_diff_tmp (v_diff need to be transposed) 232// TODO @ngxson : maybe add option NOT to transpose v_diff; will be useful for "mean" method 233void build_v_diff (bool transpose ) { 234printf ("build_v_diff\n" ); 235for (int il = 0 ;il < n_layers - 1 ;il ++ ) { 236auto & diff_tmp = v_diff_tmp [il ]; 237int n_elem = diff_tmp .size () /sizeof (float ); 238GGML_ASSERT (n_elem %n_embd == 0 ); 239int n_rows = n_elem /n_embd ; 240struct ggml_tensor * diff = transpose 241 ?ggml_new_tensor_2d (ctx_ggml ,GGML_TYPE_F32 ,n_rows ,n_embd ) 242 :ggml_new_tensor_2d (ctx_ggml ,GGML_TYPE_F32 ,n_embd ,n_rows ); 243ggml_set_name (diff , (std::string ("diff_" )+ std::to_string (il )).c_str ()); 244diff -> data = malloc (ggml_nbytes (diff ));// TODO: get rid of this malloc if possible 245if (transpose ) { 246// copy data & transpose 247float * arr = (float * )diff_tmp .data (); 248for (int ir = 0 ;ir < n_rows ;++ ir ) { 249for (int ic = 0 ;ic < n_embd ;++ ic ) { 250float f = arr [ir * n_embd + ic ]; 251ggml_set_f32_nd (diff ,ir ,ic ,0 ,0 ,f ); 252 } 253 } 254 }else { 255// only copy 256memcpy (diff -> data ,diff_tmp .data (),ggml_nbytes (diff )); 257 } 258v_diff .push_back (diff ); 259print_debug_tensor (diff ); 260// free memory of diff_tmp 261diff_tmp .resize (0 ); 262 } 263 } 264 265 ~train_context () { 266for (auto ptr :v_final )free (ptr -> data ); 267for (auto ptr :v_diff )free (ptr -> data ); 268// no need to free v_diff_tmp, since we didn't use malloc 269ggml_free (ctx_ggml ); 270 } 271}; 272 273struct tokenized_prompt { 274 std::vector < llama_token > tokens_pos ; 275 std::vector < llama_token > tokens_neg ; 276size_t max_seq_len ; 277 278tokenized_prompt (llama_context * ctx , std::string pos , std::string neg ) { 279const llama_model * model = llama_get_model (ctx ); 280const llama_vocab * vocab = llama_model_get_vocab (model ); 281const bool add_bos = llama_vocab_get_add_bos (vocab ); 282tokens_pos = common_tokenize (ctx ,pos ,add_bos , true); 283tokens_neg = common_tokenize (ctx ,neg ,add_bos , true); 284max_seq_len = std::max (tokens_pos .size (),tokens_neg .size ()); 285padding_seq (ctx ,tokens_pos ,max_seq_len ); 286padding_seq (ctx ,tokens_neg ,max_seq_len ); 287 } 288 289void padding_seq (llama_context * ctx , std::vector < llama_token > & tokens ,size_t len ) { 290// TODO: customize padding token 291 std::vector < llama_token > pad_tokens = common_tokenize (ctx ," " , false); 292llama_token pad_tok = pad_tokens .back (); 293while (tokens .size ()< len ) { 294tokens .push_back (pad_tok ); 295 } 296 } 297}; 298 299////////////////////////////////////////////////// 300 301template < typename T > 302static std::string to_string (const T & val ) { 303 std::stringstream ss ; 304ss <<val ; 305return ss .str (); 306} 307 308static std::vector < std::string > ctrlvec_load_prompt_file (std::string path ,bool skip_empty_lines ) { 309 std::vector < std::string > output ; 310 std::ifstream file (path ); 311if (!file .is_open ()) { 312fprintf (stderr ,"error: unable to open file: %s\n" ,path .c_str ()); 313exit (1 ); 314 } 315 std::string line ; 316while (std::getline (file ,line )) { 317bool is_skip = skip_empty_lines && line .empty (); 318if (!is_skip ) { 319string_process_escapes (line ); 320output .push_back (line ); 321 } 322 } 323file .close (); 324return output ; 325} 326 327////////////////////////////////////////////////// 328 329static bool cb_eval (struct ggml_tensor * t ,bool ask ,void * user_data ) { 330auto * cb_data = (callback_data * )user_data ; 331static const char * l_out_name = "l_out" ; 332const bool is_l_out = strncmp (t -> name ,l_out_name ,strlen (l_out_name ))== 0 ; 333 334if (ask ) { 335return is_l_out ; 336 } 337 338if (!is_l_out || t -> ne [1 ]!= cb_data -> n_tokens ) { 339return true; 340 } 341 342// save the tensor to current context 343cb_data -> save_tensor_for_layer (t ); 344return true; 345} 346 347static bool get_hidden_layers (llama_context * ctx , std::vector < llama_token > & tokens ) { 348llama_memory_clear (llama_get_memory (ctx ), true); 349if (llama_decode (ctx ,llama_batch_get_one (tokens .data (),tokens .size ()))) { 350fprintf (stderr ,"%s : failed to eval\n" ,__func__ ); 351return false; 352 } 353return true; 354} 355 356static void export_gguf (const std::vector < struct ggml_tensor *> & v_ctrl ,const std::string fname ,const std::string model_hint ) { 357struct gguf_context * ctx = gguf_init_empty (); 358 359const std::string arch = "controlvector" ; 360gguf_set_val_str (ctx ,"general.architecture" ,arch .c_str ()); 361gguf_set_val_str (ctx , (arch + ".model_hint" ).c_str (),model_hint .c_str ()); 362gguf_set_val_i32 (ctx , (arch + ".layer_count" ).c_str (),v_ctrl .size ()); 363 364for (size_t i = 0 ;i < v_ctrl .size ();++ i ) { 365gguf_add_tensor (ctx ,v_ctrl [i ]); 366print_debug_tensor (v_ctrl [i ]); 367printf ("Added tensor: %s\n" ,v_ctrl [i ]-> name ); 368 } 369 370printf ("%s: writing file...\n" ,__func__ ); 371gguf_write_to_file (ctx ,fname .c_str (), false); 372printf ("%s: wrote file '%s'\n" ,__func__ ,fname .c_str ()); 373gguf_free (ctx ); 374} 375 376/** 377* Load prompt files and completion file. 378* Then format each pair of prompt + completion to make an entry. 379*/ 380static int prepare_entries (common_params & params ,train_context & ctx_train ) { 381// load prompts 382 std::vector < std::string > positive_prompts = ctrlvec_load_prompt_file (params .cvector_positive_file , true); 383 std::vector < std::string > negative_prompts = ctrlvec_load_prompt_file (params .cvector_negative_file , true); 384if (positive_prompts .size ()!= negative_prompts .size ()) { 385fprintf (stderr ,"number of positive and negative prompts must be equal\n" ); 386return 1 ; 387 } 388if (positive_prompts .empty ()) { 389fprintf (stderr ,"must provide at least one prompt pair\n" ); 390return 1 ; 391 } 392ctx_train .positive_entries = positive_prompts ; 393ctx_train .negative_entries = negative_prompts ; 394return 0 ; 395} 396 397int main (int argc ,char ** argv ) { 398 std::setlocale (LC_NUMERIC ,"C" ); 399 400common_params params ; 401 402params .out_file = "control_vector.gguf" ; 403 404common_init (); 405 406if (!common_params_parse (argc ,argv ,params ,LLAMA_EXAMPLE_CVECTOR_GENERATOR ,print_usage )) { 407return 1 ; 408 } 409 410if (params .n_pca_iterations %params .n_pca_batch != 0 ) { 411fprintf (stderr ,"PCA iterations must by multiply of PCA batch size\n" ); 412return 1 ; 413 } 414 415 416callback_data cb_data ; 417 418// pass the callback to the backend scheduler 419// it will be executed for each node during the graph computation 420params .cb_eval = cb_eval ; 421params .cb_eval_user_data = & cb_data ; 422params .warmup = false; 423 424llama_print_build_info (llama_version ()); 425llama_backend_init (); 426llama_numa_init (params .numa ); 427 428// load the model to get hparams 429auto llama_init = common_init_from_params (params ); 430 431auto * model = llama_init -> model (); 432auto * ctx = llama_init -> context (); 433 434// int n_ctx = llama_n_ctx(ctx); 435int n_layers = llama_model_n_layer (model ); 436int n_embd = llama_model_n_embd (model ); 437 438// get model hint param (a.k.a model arch name) 439char model_hint [128 ]; 440llama_model_meta_val_str (model ,"general.architecture" ,model_hint ,128 ); 441 442// init train_context 443train_context ctx_train (n_embd ,n_layers ); 444 445// load and prepare entries for training 446prepare_entries (params ,ctx_train ); 447 448// we have to pretokenize everything because otherwise we don't know how much overhead to allocate ctx_diffs_wrapped 449 std::vector < tokenized_prompt > tokenized_prompts ; 450size_t n_total_tokens = 0 ; 451for (size_t i = 0 ;i < ctx_train .positive_entries .size ();++ i ) { 452tokenized_prompt t (ctx ,ctx_train .positive_entries [i ],ctx_train .negative_entries [i ]); 453n_total_tokens += 2 * t .max_seq_len ; 454tokenized_prompts .push_back (std::move (t )); 455 } 456 457 std::cout <<"n_total_tokens: " <<n_total_tokens << std::endl ; 458 459for (size_t i = 0 ;i < ctx_train .positive_entries .size ();++ i ) { 460bool success = false; 461tokenized_prompt t = tokenized_prompts [i ]; 462cb_data .n_layers = n_layers ; 463cb_data .n_tokens = t .max_seq_len ; 464 465printf ("Evaluating prompt[%d/%d]: \"%s\" - \"%s\" (%d tokens)\n" , 466 (int )i + 1 , (int )ctx_train .positive_entries .size (), 467tokens_to_str (ctx ,t .tokens_pos .cbegin (),t .tokens_pos .cend ()).c_str (), 468tokens_to_str (ctx ,t .tokens_neg .cbegin (),t .tokens_neg .cend ()).c_str (), 469 (int )t .max_seq_len ); 470 471cb_data .is_eval_pos = true; 472success = get_hidden_layers (ctx ,t .tokens_pos ); 473if (!success )break ; 474 475cb_data .is_eval_pos = false; 476success = get_hidden_layers (ctx ,t .tokens_neg ); 477if (!success )break ; 478 479// calculate diff and remove all zero rows 480auto v_diff_filtered = cb_data .calc_diff (); 481 482// save & concat the filtered v_diff to ctx_train 483ctx_train .concat_diff_tmp (v_diff_filtered ); 484 485// reset for next iteration 486cb_data .reset (); 487 } 488 489// done with the model, we can now free it to make gain some memory 490printf ("Done evaluate prompts, unload model...\n" ); 491 492bool use_pca = params .cvector_dimre_method == DIMRE_METHOD_PCA ; 493 494// prepare ctx_train for PCA 495ctx_train .build_v_diff (use_pca ); 496 497if (use_pca ) { 498// run PCA 499PCA ::pca_params pca_params ; 500pca_params .n_threads = params .cpuparams .n_threads ; 501pca_params .n_batch = params .n_pca_batch ; 502pca_params .n_iterations = params .n_pca_iterations ; 503PCA ::run_pca (pca_params ,ctx_train .v_diff ,ctx_train .v_final ); 504 }else { 505// run mean 506 mean::run (ctx_train .v_diff ,ctx_train .v_final ); 507 } 508 509// write output vectors to gguf 510export_gguf (ctx_train .v_final ,params .out_file ,model_hint ); 511 512llama_backend_free (); 513 514return 0 ; 515}