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 "log.h" 4#include "llama.h" 5 6#include <algorithm> 7#include <clocale> 8#include <fstream> 9#include <iostream> // TODO: remove me 10 11static void print_usage (int ,char ** argv ) { 12LOG ("\nexample usage:\n" ); 13LOG ("\n %s --model ./models/bge-base-en-v1.5-f16.gguf --top-k 3 --context-file README.md --context-file License --chunk-size 100 --chunk-separator .\n" ,argv [0 ]); 14LOG ("\n" ); 15} 16 17struct chunk { 18// filename 19 std::string filename ; 20// original file position 21size_t filepos ; 22// original text data 23 std::string textdata ; 24// tokenized text data 25 std::vector < llama_token > tokens ; 26// embedding 27 std::vector < float > embedding ; 28}; 29 30// chunk file data to chunks of size >= chunk_size 31// chunk_separator is the separator between chunks 32static std::vector < chunk > chunk_file (const std::string & filename ,int chunk_size ,const std::string & chunk_separator ) { 33 std::vector < chunk > chunks ; 34 std::ifstream f (filename .c_str ()); 35 36if (!f .is_open ()) { 37LOG_ERR ("could not open file %s\n" ,filename .c_str ()); 38return chunks ; 39 } 40 41chunk current_chunk ; 42char buffer [1024 ]; 43int64_t filepos = 0 ; 44 std::string current ; 45while (f .read (buffer ,1024 )) { 46current += std::string (buffer ,f .gcount ()); 47size_t pos ; 48while ((pos = current .find (chunk_separator ))!= std::string::npos ) { 49current_chunk .textdata += current .substr (0 ,pos + chunk_separator .size ()); 50if ((int )current_chunk .textdata .size ()> chunk_size ) { 51// save chunk 52current_chunk .filepos = filepos ; 53current_chunk .filename = filename ; 54chunks .push_back (current_chunk ); 55// update filepos 56filepos += (int )current_chunk .textdata .size (); 57// reset current_chunk 58current_chunk = chunk (); 59 } 60current = current .substr (pos + chunk_separator .size ()); 61 } 62 63 } 64// add leftover data to last chunk 65if (current_chunk .textdata .size ()> 0 ) { 66if (chunks .empty ()) { 67current_chunk .filepos = filepos ; 68current_chunk .filename = filename ; 69chunks .push_back (current_chunk ); 70 }else { 71chunks .back ().textdata += current_chunk .textdata ; 72 } 73 } 74f .close (); 75return chunks ; 76} 77 78static void batch_add_seq (llama_batch & batch ,const std::vector < int32_t > & tokens ,llama_seq_id seq_id ) { 79size_t n_tokens = tokens .size (); 80for (size_t i = 0 ;i < n_tokens ;i ++ ) { 81common_batch_add (batch ,tokens [i ],i , {seq_id }, true); 82 } 83} 84 85static void batch_process (llama_context * ctx ,llama_batch & batch ,float * output ,int n_seq ,int n_embd ) { 86// clear previous kv_cache values (irrelevant for embeddings) 87llama_memory_clear (llama_get_memory (ctx ), false); 88 89// run model 90LOG_INF ("%s: n_tokens = %d, n_seq = %d\n" ,__func__ ,batch .n_tokens ,n_seq ); 91if (llama_decode (ctx ,batch )< 0 ) { 92LOG_ERR ("%s : failed to process\n" ,__func__ ); 93 } 94 95for (int i = 0 ;i < batch .n_tokens ;i ++ ) { 96if (!batch .logits [i ]) { 97continue ; 98 } 99 100// try to get sequence embeddings - supported only when pooling_type is not NONE 101const float * embd = llama_get_embeddings_seq (ctx ,batch .seq_id [i ][0 ]); 102if (embd == NULL ) { 103embd = llama_get_embeddings_ith (ctx ,i ); 104if (embd == NULL ) { 105LOG_ERR ("%s: failed to get embeddings for token %d\n" ,__func__ ,i ); 106continue ; 107 } 108 } 109 110float * out = output + batch .seq_id [i ][0 ]* n_embd ; 111common_embd_normalize (embd ,out ,n_embd ,2 ); 112 } 113} 114 115int main (int argc ,char ** argv ) { 116 std::setlocale (LC_NUMERIC ,"C" ); 117 118common_params params ; 119 120common_init (); 121 122if (!common_params_parse (argc ,argv ,params ,LLAMA_EXAMPLE_RETRIEVAL ,print_usage )) { 123return 1 ; 124 } 125 126// For BERT models, batch size must be equal to ubatch size 127params .n_ubatch = params .n_batch ; 128params .embedding = true; 129 130if (params .chunk_size <=0 ) { 131LOG_ERR ("chunk_size must be positive\n" ); 132return 1 ; 133 } 134if (params .context_files .empty ()) { 135LOG_ERR ("context_files must be specified\n" ); 136return 1 ; 137 } 138 139LOG_INF ("processing files:\n" ); 140for (auto & context_file :params .context_files ) { 141LOG_INF ("%s\n" ,context_file .c_str ()); 142 } 143 144 std::vector < chunk > chunks ; 145for (auto & context_file :params .context_files ) { 146 std::vector < chunk > file_chunk = chunk_file (context_file ,params .chunk_size ,params .chunk_separator ); 147chunks .insert (chunks .end (),file_chunk .begin (),file_chunk .end ()); 148 } 149LOG_INF ("Number of chunks: %zu\n" ,chunks .size ()); 150 151llama_backend_init (); 152llama_numa_init (params .numa ); 153 154// load the model 155auto llama_init = common_init_from_params (params ); 156 157auto * model = llama_init -> model (); 158auto * ctx = llama_init -> context (); 159 160if (model == NULL ) { 161LOG_ERR ("%s: unable to load model\n" ,__func__ ); 162return 1 ; 163 } 164 165const llama_vocab * vocab = llama_model_get_vocab (model ); 166 167const int n_ctx_train = llama_model_n_ctx_train (model ); 168const int n_ctx = llama_n_ctx (ctx ); 169 170const enum llama_pooling_type pooling_type = llama_pooling_type (ctx ); 171if (pooling_type == LLAMA_POOLING_TYPE_NONE ) { 172LOG_ERR ("%s: pooling type NONE not supported\n" ,__func__ ); 173return 1 ; 174 } 175 176if (n_ctx > n_ctx_train ) { 177LOG_WRN ("%s: warning: model was trained on only %d context tokens (%d specified)\n" , 178__func__ ,n_ctx_train ,n_ctx ); 179 } 180 181// print system information 182 { 183LOG_INF ("\n" ); 184LOG_INF ("%s\n" ,common_params_get_system_info (params ).c_str ()); 185 } 186 187// max batch size 188const uint64_t n_batch = params .n_batch ; 189GGML_ASSERT (params .n_batch >=params .n_ctx ); 190 191// tokenize the prompts and trim 192for (auto & chunk :chunks ) { 193auto inp = common_tokenize (ctx ,chunk .textdata , true, false); 194if (inp .size ()> n_batch ) { 195LOG_ERR ("%s: chunk size (%lld) exceeds batch size (%lld), increase batch size and re-run\n" , 196__func__ , (long long int )inp .size (), (long long int )n_batch ); 197return 1 ; 198 } 199// add eos if not present 200if (llama_vocab_eos (vocab ) >=0 && (inp .empty ()|| inp .back ()!= llama_vocab_eos (vocab ))) { 201inp .push_back (llama_vocab_eos (vocab )); 202 } 203chunk .tokens = inp ; 204 } 205 206// tokenization stats 207if (params .verbose_prompt ) { 208for (int i = 0 ;i < (int )chunks .size ();i ++ ) { 209LOG_INF ("%s: prompt %d: '%s'\n" ,__func__ ,i ,chunks [i ].textdata .c_str ()); 210LOG_INF ("%s: number of tokens in prompt = %zu\n" ,__func__ ,chunks [i ].tokens .size ()); 211for (int j = 0 ;j < (int )chunks [i ].tokens .size ();j ++ ) { 212LOG_INF ("%6d -> '%s'\n" ,chunks [i ].tokens [j ],common_token_to_piece (ctx ,chunks [i ].tokens [j ]).c_str ()); 213 } 214LOG_INF ("\n\n" ); 215 } 216 } 217 218// initialize batch 219const int n_chunks = chunks .size (); 220struct llama_batch batch = llama_batch_init (n_batch ,0 ,1 ); 221 222// allocate output 223const int n_embd_out = llama_model_n_embd_out (model ); 224 std::vector < float > embeddings (n_chunks * n_embd_out ,0 ); 225float * emb = embeddings .data (); 226 227// break into batches 228unsigned int p = 0 ;// number of prompts processed already 229unsigned int s = 0 ;// number of prompts in current batch 230for (int k = 0 ;k < n_chunks ;k ++ ) { 231// clamp to n_batch tokens 232auto & inp = chunks [k ].tokens ; 233 234const uint64_t n_toks = inp .size (); 235 236// encode if at capacity 237if (batch .n_tokens + n_toks > n_batch || s >=llama_n_seq_max (ctx )) { 238float * out = emb + p * n_embd_out ; 239batch_process (ctx ,batch ,out ,s ,n_embd_out ); 240common_batch_clear (batch ); 241p += s ; 242s = 0 ; 243 } 244 245// add to batch 246batch_add_seq (batch ,inp ,s ); 247s += 1 ; 248 } 249 250// final batch 251float * out = emb + p * n_embd_out ; 252batch_process (ctx ,batch ,out ,s ,n_embd_out ); 253 254// save embeddings to chunks 255for (int i = 0 ;i < n_chunks ;i ++ ) { 256chunks [i ].embedding = std::vector < float > (emb + i * n_embd_out ,emb + (i + 1 )* n_embd_out ); 257// clear tokens as they are no longer needed 258chunks [i ].tokens .clear (); 259 } 260 261struct llama_batch query_batch = llama_batch_init (n_batch ,0 ,1 ); 262 263// start loop, receive query and return top k similar chunks based on cosine similarity 264 std::string query ; 265while (true) { 266LOG ("Enter query: " ); 267 std::getline (std::cin ,query ); 268 std::vector < int32_t > query_tokens = common_tokenize (ctx ,query , true); 269 270batch_add_seq (query_batch ,query_tokens ,0 ); 271 272 std::vector < float > query_emb (n_embd_out ,0 ); 273batch_process (ctx ,query_batch ,query_emb .data (),1 ,n_embd_out ); 274 275common_batch_clear (query_batch ); 276 277// compute cosine similarities 278 { 279 std::vector < std::pair < int ,float >> similarities ; 280for (int i = 0 ;i < n_chunks ;i ++ ) { 281float sim = common_embd_similarity_cos (chunks [i ].embedding .data (),query_emb .data (),n_embd_out ); 282similarities .push_back (std::make_pair (i ,sim )); 283 } 284 285// sort similarities 286 std::sort (similarities .begin (),similarities .end (), [](const std::pair < int ,float > & a ,const std::pair < int ,float > & b ) { 287return a .second > b .second ; 288 }); 289 290LOG ("Top %d similar chunks:\n" ,params .sampling .top_k ); 291for (int i = 0 ;i < std::min (params .sampling .top_k , (int )chunks .size ());i ++ ) { 292LOG ("filename: %s\n" ,chunks [similarities [i ].first ].filename .c_str ()); 293LOG ("filepos: %lld\n" , (long long int )chunks [similarities [i ].first ].filepos ); 294LOG ("similarity: %f\n" ,similarities [i ].second ); 295LOG ("textdata:\n%s\n" ,chunks [similarities [i ].first ].textdata .c_str ()); 296LOG ("--------------------\n" ); 297 } 298 } 299 } 300 301LOG ("\n" ); 302llama_perf_context_print (ctx ); 303 304// clean up 305llama_batch_free (query_batch ); 306llama_backend_free (); 307}