char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
41361c859
char/aliceai-t5-moe
1#include "arg.h" 2#include "common.h" 3#include "sampling.h" 4#include "log.h" 5#include "llama.h" 6 7#include <algorithm> 8#include <clocale> 9#include <cstdio> 10#include <string> 11#include <vector> 12 13struct ngram_data { 14bool active = false; 15 16llama_seq_id seq_id = -1 ; 17 18 std::vector < int > i_batch ; 19 20 std::vector < llama_token > tokens ; 21}; 22 23// n-gram container 24struct ngram_container { 25ngram_container (int n_vocab ,int N ,int G ) { 26cnt .resize (n_vocab ); 27head .resize (n_vocab ); 28tokens .resize (n_vocab * G * (N - 1 )); 29 } 30 31int n_total = 0 ; 32 33 std::vector < int > cnt ; 34 std::vector < int > head ; 35 36// [n_vocab][G][N - 1] 37// for each token of the vocab, keep a ring-buffer of capacity G of n-grams of size N - 1 38 std::vector < llama_token > tokens ; 39}; 40 41int main (int argc ,char ** argv ) { 42 std::setlocale (LC_NUMERIC ,"C" ); 43 44common_params params ; 45 46common_init (); 47 48if (!common_params_parse (argc ,argv ,params ,LLAMA_EXAMPLE_COMMON )) { 49return 1 ; 50 } 51 52const int W = 15 ;// lookahead window 53const int N = 5 ;// n-gram size 54const int G = 15 ;// max verification n-grams 55 56// lookahead requires W + G + 1 sequences for parallel Jacobi decoding 57params .n_parallel = W + G + 1 ; 58 59// unified KV cache is required for coupled sequences in batch splitting 60params .kv_unified = true; 61 62// init llama.cpp 63llama_backend_init (); 64llama_numa_init (params .numa ); 65 66// load the target model 67auto llama_init = common_init_from_params (params ); 68 69auto * model = llama_init -> model (); 70auto * ctx = llama_init -> context (); 71 72auto * mem = llama_get_memory (ctx ); 73 74const llama_vocab * vocab = llama_model_get_vocab (model ); 75 76// Tokenize the prompt 77 std::vector < llama_token > inp ; 78 std::vector < llama_token > all ; 79 80inp = common_tokenize (ctx ,params .prompt , true, true); 81all = inp ; 82 83const int max_context_size = llama_n_ctx (ctx ); 84const int max_tokens_list_size = max_context_size - 4 ; 85 86if ((int )inp .size ()> max_tokens_list_size ) { 87LOG_ERR ("%s: prompt too long (%d tokens, max %d)\n" ,__func__ , (int )inp .size (),max_tokens_list_size ); 88return 1 ; 89 } 90 91LOG ("\n\n" ); 92 93for (auto id :inp ) { 94LOG ("%s" ,common_token_to_piece (ctx ,id ).c_str ()); 95 } 96 97fflush (stderr ); 98 99const int n_input = inp .size (); 100 101const auto t_enc_start = ggml_time_us (); 102 103// eval the prompt 104llama_decode (ctx ,llama_batch_get_one (inp .data (),n_input - 1 )); 105llama_decode (ctx ,llama_batch_get_one (& inp .back (),1 )); 106 107for (int s = 1 ;s < W + G + 1 ;++ s ) { 108llama_memory_seq_cp (mem ,0 ,s ,-1 ,-1 ); 109 } 110 111const auto t_enc_end = ggml_time_us (); 112 113int n_predict = 0 ; 114int n_accept = 0 ; 115 116int n_past = inp .size (); 117 118llama_token id = 0 ; 119 120// used to determine end of generation 121bool has_eos = false; 122 123// for each decoded batch, we have at most W + G + 1 distinct sequences: 124// seq_id == 0 : the current input token 125// seq_id [1, W] : tokens from the past N - 1 Jacobi iterations 126// seq_id [W + 1, W + G] : verification n-grams 127llama_batch batch = llama_batch_init (llama_n_ctx (ctx ),0 ,W + G + 1 ); 128 129// target model sampling context 130struct common_sampler * smpl = common_sampler_init (model ,params .sampling ); 131 132// verification n-grams 133 std::vector < ngram_data > ngrams_cur (G ); 134 135// tokens for the past N - 1 Jacobi iterations 136 std::vector < llama_token > tokens_j_prev (W ); 137 std::vector < std::vector < llama_token >> tokens_j (N - 1 ); 138for (int j = 0 ;j < N - 1 ;j ++ ) { 139tokens_j [j ].resize (W ); 140 141for (int i = 0 ;i < W ;i ++ ) { 142// there are different ways to init these tokens 143if (0 ) { 144// initialize randomly from the prompt tokens 145tokens_j [j ][i ]= all [1 + rand () % (all .size ()- 1 )]; 146 }else { 147// initialize with a sequence of increasing numbers 148tokens_j [j ][i ]= 100 + i ; 149 } 150 } 151 } 152 153 std::vector < llama_seq_id > seq_id_look ; 154 155// the input token belongs both to all sequences 156 std::vector < llama_seq_id > seq_id_all (W + G + 1 ); 157for (int i = 0 ;i < W + G + 1 ;i ++ ) { 158seq_id_all [i ]= i ; 159 } 160 161// here we keep adding new n-grams as we go 162ngram_container ngrams_observed (llama_vocab_n_tokens (vocab ),N ,G ); 163 164const auto t_dec_start = ggml_time_us (); 165 166// sample first token 167 { 168id = common_sampler_sample (smpl ,ctx ,0 ); 169 170common_sampler_accept (smpl ,id , true); 171 172 { 173const std::string token_str = common_token_to_piece (ctx ,id ); 174 175LOG ("%s" ,token_str .c_str ()); 176fflush (stdout ); 177 } 178 } 179 180while (true) { 181// build the mask from https://lmsys.org/blog/2023-11-21-lookahead-decoding/ 182// 183// Example for W = 5, N = 4, G = 2: 184// (I = input, L = lookahead, V = verification) 185// 186// Batch: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 187// T: -2 -2 -2 -2 -1 -1 -1 -1 -1 0 0 0 0 0 0 188// Info: I L L L L L L L L L L L L L L V V V V V V 189// Pos: 0 1 2 3 4 1 2 3 4 5 2 3 4 5 6 1 2 3 1 2 3 (+ n_past) 190// Logits: 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 191// --------------------------------------------------------------------- 192// Seq: 0 193// 1 1 1 194// 2 2 2 2 195// 3 3 3 3 3 196// 4 4 4 4 4 4 197// 5 5 5 5 5 5 5 198// 6 6 6 6 199// 7 7 7 7 200// --------------------------------------------------------------------- 201// | | | | | | | | | | | 202// V V V V V | | | | | | 203// j_tokens | | | | | | 204// V V V V V V 205// id 206 { 207common_batch_clear (batch ); 208 209// current token - first token of the first level 210common_batch_add (batch ,id ,n_past ,seq_id_all , true); 211 212// verification n-grams - queue this before the lookahead tokens for less KV cache fragmentation 213 { 214const int g_cur = ngrams_observed .cnt [id ]; 215 216ngrams_cur .resize (g_cur ); 217for (int g = 0 ;g < g_cur ;g ++ ) { 218ngrams_cur [g ].active = true; 219ngrams_cur [g ].tokens .resize (N ); 220ngrams_cur [g ].i_batch .resize (N ); 221ngrams_cur [g ].seq_id = W + 1 + g ; 222ngrams_cur [g ].i_batch [0 ]= 0 ; 223ngrams_cur [g ].tokens [0 ]= id ; 224 } 225 226for (int j = 0 ;j < N - 1 ;j ++ ) { 227for (int g = 0 ;g < g_cur ;g ++ ) { 228const int idx = id * (N - 1 )* G + g * (N - 1 ); 229 230const llama_token t = ngrams_observed .tokens [idx + j ]; 231 232ngrams_cur [g ].tokens [j + 1 ]= t ; 233ngrams_cur [g ].i_batch [j + 1 ]= batch .n_tokens ; 234 235common_batch_add (batch ,t ,n_past + j + 1 , {W + 1 + g }, true); 236 } 237 } 238 } 239 240// fill the remaining W - 1 tokens for the first level 241for (int i = 1 ;i < W ;i ++ ) { 242seq_id_look .resize (W - i ); 243for (int j = 0 ;j < W - i ;j ++ ) { 244seq_id_look [j ]= i + j + 1 ; 245 } 246 247common_batch_add (batch ,tokens_j [0 ][i ],n_past + i ,seq_id_look , false); 248 } 249 250// fill the rest of the levels 251for (int j = 1 ;j < N - 1 ;j ++ ) { 252for (int i = 0 ;i < W ;i ++ ) { 253common_batch_add (batch ,tokens_j [j ][i ],n_past + j + i , {i + 1 },j == N - 2 ); 254 } 255 } 256 } 257 258if (llama_decode (ctx ,batch )!= 0 ) { 259LOG_ERR ("\n\n%s: llama_decode failed - increase KV cache size\n" ,__func__ ); 260return 1 ; 261 } 262 263int seq_id_best = 0 ; 264 265for (int v = 0 ;v < N ;++ v ) { 266int i_batch = 0 ; 267 268// if no active ngrams are left, it means the sampled token does not pass the verification 269if (v > 0 ) { 270for (int g = 0 ;g < (int )ngrams_cur .size ();g ++ ) { 271if (ngrams_cur [g ].active ) { 272i_batch = ngrams_cur [g ].i_batch [v ]; 273seq_id_best = ngrams_cur [g ].seq_id ; 274 275++ n_accept ; 276break ; 277 } 278 } 279 280// no more matches -> create a new batch 281if (i_batch == 0 ) { 282break ; 283 } 284 } 285 286// sample the next token 287id = common_sampler_sample (smpl ,ctx ,i_batch ); 288 289common_sampler_accept (smpl ,id , true); 290 291const std::string token_str = common_token_to_piece (ctx ,id ); 294 295if (v == 0 ) { 296LOG ("%s" ,token_str .c_str ()); 297 }else { 298// print light cyan 299LOG ("\033[0;96m%s\033[0m" ,token_str .c_str ()); 300 } 301fflush (stdout ); 302 303if (llama_vocab_is_eog (vocab ,id )) { 304has_eos = true; 305 } 306 307all .push_back (id ); 308 } 309 310++ n_predict ; 311++ n_past ; 312 313if ((params .n_predict >=0 && n_predict > params .n_predict )|| has_eos ) { 314break ; 315 } 316 317// verify across active n-grams 318for (int g = 0 ;g < (int )ngrams_cur .size ();g ++ ) { 319if (ngrams_cur [g ].active ) { 320if (v == N - 1 ) { 321ngrams_cur [g ].active = false; 322 }else { 323if (id != ngrams_cur [g ].tokens [v + 1 ]) { 324ngrams_cur [g ].active = false; 325 } 326 } 327 } 328 } 329 330// print known n-grams starting with token id (debug) 331if (0 && v == 0 ) { 332if (ngrams_observed .cnt [id ]> 0 ) { 333LOG ("\n - %d n-grams starting with '%s'\n" ,ngrams_observed .cnt [id ],common_token_to_piece (ctx ,id ).c_str ()); 334 } 335 336for (int i = 0 ;i < ngrams_observed .cnt [id ];i ++ ) { 337LOG (" - ngram %2d: " ,i ); 338 339const int idx = id * (N - 1 )* G + i * (N - 1 ); 340 341for (int j = 0 ;j < N - 1 ;j ++ ) { 342const std::string token_str = common_token_to_piece (ctx ,ngrams_observed .tokens [idx + j ]); 343 344LOG ("%s" ,token_str .c_str ()); 345 } 346 347LOG ("\n" ); 348 } 349 } 350 351// update lookahead tokens 352 { 353for (int i = 0 ;i < W ;i ++ ) { 354tokens_j_prev [i ]= tokens_j [0 ][i ]; 355 } 356 357for (int j = 0 ;j < N - 2 ;j ++ ) { 358tokens_j [j ]= tokens_j [j + 1 ]; 359 } 360 361if (v == 0 ) { 362// sample from the last level 363for (int i = 0 ;i < W ;i ++ ) { 364tokens_j [N - 2 ][i ]= common_sampler_sample (smpl ,ctx ,ngrams_cur .size ()* (N - 1 )+ W * (N - 2 )+ i ); 365 } 366 }else { 367for (int i = 0 ;i < W ;i ++ ) { 368// there are different ways to init these tokens 369if (0 ) { 370// random init 371tokens_j [N - 2 ][i ]= all [1 + rand () % (all .size ()- 1 )]; 372 }else { 373// init from the previous level 374tokens_j [N - 2 ][i ]= tokens_j [0 ][i ]; 375 } 376 } 377 } 378 } 379 380// update observed ngrams 381if (v == 0 ) { 382// the first token of the n-gram is determined by the index in the container so it is not stored 383 std::vector < llama_token > ngram (N - 1 ); 384 385// n-gram generation 386// ref: https://github.com/hao-ai-lab/LookaheadDecoding/issues/14#issuecomment-1826198518 387for (int f = 0 ;f < W ;++ f ) { 388const int ft = tokens_j_prev [f ];// first token of the n-gram 389 390for (int j = 0 ;j < N - 1 ;++ j ) { 391ngram [j ]= tokens_j [j ][f ]; 392 } 393 394// filter-out repeating n-grams 395 { 396bool is_unique = true; 397 398for (int k = 0 ;k < ngrams_observed .cnt [ft ];++ k ) { 399const int idx = ft * (N - 1 )* G + k * (N - 1 ); 400 401bool is_match = true; 402for (int j = 0 ;j < N - 1 ;++ j ) { 403if (ngrams_observed .tokens [idx + j ]!= ngram [j ]) { 404is_match = false; 405break ; 406 } 407 } 408 409if (is_match ) { 410is_unique = false; 411break ; 412 } 413 } 414 415if (!is_unique ) { 416continue ; 417 } 418 } 419 420const int head = ngrams_observed .head [ft ]; 421const int idx = ft * (N - 1 )* G + head * (N - 1 ); 422 423for (int i = 0 ;i < N - 1 ;i ++ ) { 424ngrams_observed .tokens [idx + i ]= ngram [i ]; 425 } 426 427ngrams_observed .cnt [ft ]= std::min (G ,ngrams_observed .cnt [ft ]+ 1 ); 428ngrams_observed .head [ft ]= (head + 1 ) %G ; 429 430ngrams_observed .n_total ++ ; 431 } 432 } 433 } 434 435if ((params .n_predict >=0 && n_predict > params .n_predict )|| has_eos ) { 436break ; 437 } 438 439// KV cache management 440// if no verification token matched, we simply remove all cells from this batch -> no fragmentation 441llama_memory_seq_rm (mem ,-1 ,n_past ,-1 ); 442 443if (seq_id_best != 0 ) { 444// if a verification token matched, we keep the best sequence and remove the rest 445// this leads to some KV cache fragmentation 446llama_memory_seq_keep (mem ,seq_id_best ); 447llama_memory_seq_cp (mem ,seq_id_best ,0 ,-1 ,-1 ); 448llama_memory_seq_rm (mem ,seq_id_best ,-1 ,-1 ); 449 450for (int s = 1 ;s < W + G + 1 ;++ s ) { 451llama_memory_seq_cp (mem ,0 ,s ,-1 ,-1 ); 452 } 453 } 454 } 455 456auto t_dec_end = ggml_time_us (); 457 458LOG ("\n\n" ); 459 460LOG_INF ("encoded %4d tokens in %8.3f seconds, speed: %8.3f t/s\n" ,n_input , (t_enc_end - t_enc_start ) /1e6f ,inp .size () / ((t_enc_end - t_enc_start ) /1e6f )); 461LOG_INF ("decoded %4d tokens in %8.3f seconds, speed: %8.3f t/s\n" ,n_predict , (t_dec_end - t_dec_start ) /1e6f ,n_predict / ((t_dec_end - t_dec_start ) /1e6f )); 462 463LOG_INF ("\n" ); 464LOG_INF ("W = %2d\n" ,W ); 465LOG_INF ("N = %2d\n" ,N ); 466LOG_INF ("G = %2d\n" ,G ); 467LOG_INF ("\n" ); 468LOG_INF ("n_predict = %d\n" ,n_predict ); 469LOG_INF ("n_accept = %d\n" ,n_accept ); 470 471LOG_INF ("\n" ); 472common_perf_print (ctx ,smpl ); 473 474common_sampler_free (smpl ); 475 476llama_batch_free (batch ); 477 478llama_backend_free (); 479 480LOG ("\n\n" ); 481 482return 0 ; 483}