#include "run.h" #include "ggml.h" #include "tcp_server.h" #include std::vector softmax(const std::vector& logits) { std::vector probs(logits.size()); float max_logit = logits[0]; for (float v : logits) max_logit = std::max(max_logit, v); double sum_exp = 0.0; for (size_t i = 0; i < logits.size(); i++) { // Subtract the maximum logit value from the current logit value for numerical stability float logit = logits[i] - max_logit; double exp_logit = std::exp(logit); sum_exp += exp_logit; probs[i] = exp_logit; } for (size_t i = 0; i < probs.size(); i++) probs[i] /= sum_exp; return probs; } void perplexity(llama_context * ctx, const gpt_params & params) { // Download: https://s3.amazonaws.com/research.metamind.io/wikitext/wikitext-2-raw-v1.zip?ref=salesforce-research // Run `./main --perplexity -m models/7B/ggml-model-q4_0.bin -f wiki.test.raw` // Output: `perplexity: 13.5106 [114/114]` auto tokens = ::llama_tokenize(ctx, params.prompt, true); int count = 0; double nll = 0.0; int seq_count = tokens.size() / params.n_ctx; fprintf(stderr, "%s : calculating perplexity over %d chunks\n", __func__, seq_count); for (int i = 0; i < seq_count; ++i) { int start = i * params.n_ctx; int end = start + params.n_ctx - 1; std::vector embd(tokens.begin() + start, tokens.begin() + end); auto start_t = std::chrono::high_resolution_clock::now(); if (llama_eval(ctx, embd.data(), embd.size(), 0, params.n_threads)) { fprintf(stderr, "%s : failed to eval\n", __func__); return; } auto end_t = std::chrono::high_resolution_clock::now(); if (i == 0) { double seconds = std::chrono::duration(end_t - start_t).count(); printf("%.2f seconds per pass - ETA %.2f hours\n", seconds, (seconds * seq_count) / (60.0*60.0)); } // We get the logits for all the tokens in the context window (params.n_ctx) // from llama_eval above. Now, based on https://huggingface.co/docs/transformers/perplexity, // calculate the perplexity over the last half the window (so the model always has // some context to predict the token). // // We rely on the fact that attention in the forward pass only looks at previous // tokens here, so the logits returned for each token are an accurate representation // of what the model would have predicted at that point. // // Example, we have a context window of 512, we will compute perplexity for each of the // last 256 tokens. Then, we split the input up into context window size chunks to // process the entire prompt. auto logits = llama_get_logits(ctx); for (int j = params.n_ctx / 2; j < params.n_ctx - 1; ++j) { // Calculate probability of next token, given the previous ones. int n_vocab = llama_n_vocab(ctx); std::vector tok_logits( logits + j * n_vocab, logits + (j + 1) * n_vocab); double prob = softmax(tok_logits)[tokens[start + j + 1]]; nll += -std::log(prob); ++count; } // perplexity is e^(average negative log-likelihood) printf("[%d]%.4lf,", i + 1, std::exp(nll / count)); fflush(stdout); } printf("\n"); } int main(int argc, char ** argv) { // has to be called once at the start of the program to init ggml stuff ggml_time_init(); gpt_params params; params.model = "models/llama-7B/ggml-model.bin"; if (gpt_params_parse(argc, argv, params) == false) { return 1; } if (params.n_ctx > 2048) { fprintf(stderr, "%s: warning: model does not support context sizes greater than 2048 tokens (%d specified);" "expect poor results\n", __func__, params.n_ctx); } llama_context * ctx; // load the model { auto lparams = llama_context_default_params(); lparams.n_ctx = params.n_ctx; lparams.n_parts = params.n_parts; lparams.seed = params.seed; lparams.f16_kv = params.memory_f16; lparams.logits_all = params.perplexity; ctx = llama_init_from_file(params.model.c_str(), lparams); if (ctx == NULL) { fprintf(stderr, "%s: error: failed to load model '%s'\n", __func__, params.model.c_str()); return 1; } } // print system information { fprintf(stderr, "\n"); fprintf(stderr, "system_info: n_threads = %d / %d | %s\n", params.n_threads, std::thread::hardware_concurrency(), llama_print_system_info()); } if (params.perplexity) { perplexity(ctx, params); exit(0); } #ifndef _WIN32 if (params.listen_port != "") { return listen_tcp(ctx, params); } #endif return run(ctx, params, std::cin, stdout, stderr); }