mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-19 09:15:04 +02:00
common: add enum common_speculative_type
This commit is contained in:
+243
-32
@@ -14,6 +14,26 @@
|
||||
#define SPEC_VOCAB_MAX_SIZE_DIFFERENCE 128
|
||||
#define SPEC_VOCAB_CHECK_START_TOKEN_ID 5
|
||||
|
||||
const std::vector<enum common_speculative_type> common_speculative_types = {
|
||||
COMMON_SPECULATIVE_TYPE_NONE,
|
||||
COMMON_SPECULATIVE_TYPE_DRAFT,
|
||||
COMMON_SPECULATIVE_TYPE_EAGLE3,
|
||||
COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE,
|
||||
COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K,
|
||||
COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V,
|
||||
COMMON_SPECULATIVE_TYPE_NGRAM_CACHE
|
||||
};
|
||||
|
||||
const std::map<std::string, enum common_speculative_type> common_speculative_type_from_name_map = {
|
||||
{"none", COMMON_SPECULATIVE_TYPE_NONE},
|
||||
{"draft", COMMON_SPECULATIVE_TYPE_DRAFT},
|
||||
{"eagle3", COMMON_SPECULATIVE_TYPE_EAGLE3},
|
||||
{"ngram_simple", COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE},
|
||||
{"ngram_map_k", COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K},
|
||||
{"ngram_map_k4v", COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V},
|
||||
{"ngram_cache", COMMON_SPECULATIVE_TYPE_NGRAM_CACHE}
|
||||
};
|
||||
|
||||
struct common_speculative_self {
|
||||
uint16_t size_ngram = 12; // size of n-grams to lookup in self-mode
|
||||
uint16_t size_mgram = 48; // size of m-grams to draft in self-mode
|
||||
@@ -21,6 +41,16 @@ struct common_speculative_self {
|
||||
size_t idx_last_check = 0; // index of last check in context history
|
||||
};
|
||||
|
||||
struct common_speculative_impl {
|
||||
const enum common_speculative_type type;
|
||||
|
||||
size_t drafts_call_count = 0; // number of times this implementation was called.
|
||||
size_t drafts_generated_count = 0; // number of times a draft or part was generated by this implementation.
|
||||
size_t drafts_accepted_count = 0; // number of times a draft or part was accepted by the target model.
|
||||
size_t drafts_generated_tokens = 0; // number of tokens generated by this implementation.
|
||||
size_t drafts_accepted_tokens = 0; // number of tokens accepted by the target model.
|
||||
};
|
||||
|
||||
struct common_speculative {
|
||||
struct llama_context * ctx_tgt; // only used for retokenizing from ctx_dft
|
||||
struct llama_context * ctx_dft;
|
||||
@@ -31,29 +61,133 @@ struct common_speculative {
|
||||
bool vocab_dft_compatible = true; // whether retokenization is needed
|
||||
std::map<std::string, std::string> tgt_dft_replacements = {};
|
||||
|
||||
const uint16_t self_mode = 0; // 0: off, 1: self speculative, 2: n-grams (keys) only, 3: n-grams/m-grams (key-values)
|
||||
common_ngram_map map; // draft ngram map for speculative decoding without draft model
|
||||
common_speculative_self self_state; // state of self-speculation (simple implementation, not ngram-map)
|
||||
|
||||
std::vector<common_speculative_impl> impls; // list of implementations to use and their statistics
|
||||
common_speculative_impl * curr_impl = nullptr; // current implementation in use (for stats)
|
||||
};
|
||||
|
||||
common_ngram_map get_common_ngram_map(std::vector<common_speculative_config> configs);
|
||||
|
||||
common_ngram_map get_common_ngram_map(std::vector<common_speculative_config> configs) {
|
||||
for (const auto & config : configs) {
|
||||
switch (config.type) {
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE:
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K:
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V: {
|
||||
// create common_ngram_map from config.config
|
||||
// compute size_key, size_value, key_only, check_rate, min_hits from config.config
|
||||
uint16_t size_key = 12;
|
||||
uint16_t size_value = 48;
|
||||
bool key_only = false;
|
||||
uint16_t check_rate = 3;
|
||||
uint16_t min_hits = 1;
|
||||
const std::map<std::string, std::string> & cfg = config.config;
|
||||
// check for key "size_ngram" in cfg
|
||||
if (cfg.find("size_ngram") != cfg.end()) {
|
||||
size_key = std::stoi(cfg.at("size_ngram"));
|
||||
if (size_key < 1 || size_key > 1024) {
|
||||
throw std::invalid_argument("size_ngram must be between 1 and 1024");
|
||||
}
|
||||
}
|
||||
// check for key "size_mgram" in cfg
|
||||
if (cfg.find("size_mgram") != cfg.end()) {
|
||||
size_value = std::stoi(cfg.at("size_mgram"));
|
||||
if (size_value < 1 || size_value > 1024) {
|
||||
throw std::invalid_argument("size_mgram must be between 1 and 1024");
|
||||
}
|
||||
}
|
||||
// check for key "key_only" in cfg
|
||||
if (cfg.find("key_only") != cfg.end()) {
|
||||
// key_onle == true, if cfg.at("key_only") has value "true".
|
||||
key_only = (cfg.at("key_only") == "true");
|
||||
}
|
||||
// check for key "check_rate" in cfg
|
||||
if (cfg.find("check_rate") != cfg.end()) {
|
||||
check_rate = std::stoi(cfg.at("check_rate"));
|
||||
if (check_rate < 1 || check_rate > 1024) {
|
||||
throw std::invalid_argument("check_rate must be between 1 and 1024");
|
||||
}
|
||||
}
|
||||
// check for key "min_hits" in cfg
|
||||
if (cfg.find("min_hits") != cfg.end()) {
|
||||
min_hits = std::stoi(cfg.at("min_hits"));
|
||||
if (min_hits < 1 || min_hits > 1024) {
|
||||
throw std::invalid_argument("min_hits must be between 1 and 1024");
|
||||
}
|
||||
}
|
||||
return common_ngram_map(size_key, size_value, key_only, check_rate, min_hits);
|
||||
break;
|
||||
}
|
||||
case COMMON_SPECULATIVE_TYPE_NONE:
|
||||
case COMMON_SPECULATIVE_TYPE_DRAFT:
|
||||
case COMMON_SPECULATIVE_TYPE_EAGLE3:
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_CACHE:
|
||||
break;
|
||||
case COMMON_SPECULATIVE_TYPE_COUNT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return common_ngram_map(12, 48, false, 3, 1); // default fallback
|
||||
}
|
||||
|
||||
std::string common_speculative_type_name_str() {
|
||||
std::string result = "";
|
||||
for (size_t i = 0; i < common_speculative_types.size(); i++) {
|
||||
if (i > 0) {
|
||||
result += ", ";
|
||||
}
|
||||
result += common_speculative_type_to_str(common_speculative_types[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string common_speculative_type_to_str(enum common_speculative_type type) {
|
||||
switch (type) {
|
||||
case COMMON_SPECULATIVE_TYPE_NONE: return "none";
|
||||
case COMMON_SPECULATIVE_TYPE_DRAFT: return "draft";
|
||||
case COMMON_SPECULATIVE_TYPE_EAGLE3: return "eagle3";
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE: return "ngram_simple";
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K: return "ngram_map_k";
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V: return "ngram_map_k4v";
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_CACHE: return "ngram_cache";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
enum common_speculative_type common_speculative_type_from_name(const std::string & name) {
|
||||
const auto it = common_speculative_type_from_name_map.find(name);
|
||||
if (it == common_speculative_type_from_name_map.end()) {
|
||||
return COMMON_SPECULATIVE_TYPE_COUNT;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct common_speculative * common_speculative_init(
|
||||
struct llama_context * ctx_tgt,
|
||||
struct llama_context * ctx_dft,
|
||||
uint16_t self_mode, // 0: off, 1: self speculative, 2: n-grams (keys) only, 3: n-grams/m-grams (key-values)
|
||||
const std::vector<uint16_t> self_cfg // ngram size, mgram size, keys only (0|1), min hits
|
||||
const std::vector<common_speculative_config> configs
|
||||
) {
|
||||
uint16_t ngram_size_key = self_cfg.size() >= 1 ? self_cfg[0] : 12;
|
||||
uint16_t mgram_size_value = self_cfg.size() >= 2 ? self_cfg[1] : 48;
|
||||
uint16_t check_rate = self_cfg.size() >= 3 ? self_cfg[2] : 3;
|
||||
bool key_only = (self_mode != 3);
|
||||
uint16_t min_hits = self_cfg.size() >= 4 ? self_cfg[3] : 1;
|
||||
common_ngram_map ngram_map = common_ngram_map(ngram_size_key, mgram_size_value, key_only, check_rate, min_hits);
|
||||
common_ngram_map ngram_map = get_common_ngram_map(configs);
|
||||
uint16_t ngram_size_key = ngram_map.size_key;
|
||||
uint16_t mgram_size_value = ngram_map.size_value;
|
||||
uint16_t check_rate = ngram_map.check_rate;
|
||||
common_speculative_self self_state = common_speculative_self{
|
||||
/* .size_ngram = */ ngram_size_key,
|
||||
/* .size_mgram = */ mgram_size_value,
|
||||
/* .check_rate = */ check_rate,
|
||||
/* .idx_last_check = */ 0,
|
||||
};
|
||||
std::vector<common_speculative_impl> implementations = {};
|
||||
LOG_INF("common_speculative_init: configs.size = %zu\n", configs.size());
|
||||
for (const auto & config : configs) {
|
||||
LOG_INF("common_speculative_init: adding implementation %s\n", common_speculative_type_to_str(config.type).c_str());
|
||||
implementations.push_back(common_speculative_impl{config.type});
|
||||
}
|
||||
auto * result = new common_speculative {
|
||||
/* .ctx_tgt = */ ctx_tgt,
|
||||
/* .ctx_dft = */ ctx_dft,
|
||||
@@ -62,9 +196,9 @@ struct common_speculative * common_speculative_init(
|
||||
/* .prompt_dft = */ {},
|
||||
/* .vocab_dft_compatible = */ false,
|
||||
/* .tgt_dft_replacements = */ {},
|
||||
/* .self_mode = */ self_mode,
|
||||
/* .map = */ ngram_map,
|
||||
/* .self_state = */ self_state
|
||||
/* .self_state = */ self_state,
|
||||
/* .impls = */ implementations
|
||||
};
|
||||
|
||||
LOG_INF("common_speculative_init: created speculative decoder, map.n = %d\n", result->map.size_key);
|
||||
@@ -218,6 +352,12 @@ static std::string replace_to_tgt(
|
||||
return result;
|
||||
}
|
||||
|
||||
llama_tokens common_speculative_use_draft_model(
|
||||
struct common_speculative * spec,
|
||||
struct common_speculative_params params,
|
||||
const llama_tokens & prompt_tgt_main_model, // specified in target model vocab
|
||||
llama_token id_last);
|
||||
|
||||
llama_tokens common_speculative_gen_self_draft(
|
||||
common_speculative * spec,
|
||||
const llama_tokens & tokens, llama_token sampled);
|
||||
@@ -227,17 +367,78 @@ llama_tokens common_speculative_gen_draft(
|
||||
struct common_speculative_params params,
|
||||
const llama_tokens & prompt_tgt_main_model, // specified in target model vocab
|
||||
llama_token id_last) {
|
||||
if (spec->self_mode) {
|
||||
// Look in the current context for a n-gram and return the following tokens as the draft.
|
||||
llama_tokens draft_self = common_speculative_gen_self_draft(spec,
|
||||
prompt_tgt_main_model, id_last);
|
||||
if (!draft_self.empty()) {
|
||||
return draft_self;
|
||||
llama_tokens result = {};
|
||||
|
||||
spec->curr_impl = nullptr; // reset current implementation
|
||||
for (auto & impl : spec->impls) {
|
||||
impl.drafts_call_count++;
|
||||
// LOG name and call_count
|
||||
switch (impl.type) {
|
||||
case COMMON_SPECULATIVE_TYPE_NONE:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case COMMON_SPECULATIVE_TYPE_DRAFT:
|
||||
{
|
||||
// Create a draft using a draft model.
|
||||
result = common_speculative_use_draft_model(spec, params, prompt_tgt_main_model, id_last);
|
||||
break;
|
||||
}
|
||||
case COMMON_SPECULATIVE_TYPE_EAGLE3:
|
||||
{
|
||||
// Work in progress: https://github.com/ggml-org/llama.cpp/pull/18039
|
||||
break;
|
||||
}
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE:
|
||||
{
|
||||
// Use common_ngram_map_draft to generate a draft from the current context.
|
||||
result = common_speculative_gen_self_draft(spec, prompt_tgt_main_model, id_last);
|
||||
break;
|
||||
}
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K:
|
||||
{
|
||||
// Use common_ngram_map_draft to generate a draft from the current context.
|
||||
common_ngram_map_draft(spec->map, prompt_tgt_main_model, id_last, result);
|
||||
break;
|
||||
}
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V:
|
||||
{
|
||||
// Use common_ngram_map_draft to generate a draft from the current context.
|
||||
common_ngram_map_draft(spec->map, prompt_tgt_main_model, id_last, result);
|
||||
break;
|
||||
}
|
||||
case COMMON_SPECULATIVE_TYPE_NGRAM_CACHE:
|
||||
{
|
||||
// TODO call common/ngram-cache.cpp
|
||||
break;
|
||||
}
|
||||
case COMMON_SPECULATIVE_TYPE_COUNT:
|
||||
{
|
||||
GGML_ABORT("invalid speculative type COUNT");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!result.empty()) {
|
||||
LOG_DBG("%s: called impl %s, hist size = %zu, call_count = %zu, gen = %zu\n", __func__,
|
||||
common_speculative_type_to_str(impl.type).c_str(),
|
||||
prompt_tgt_main_model.size(),
|
||||
impl.drafts_call_count, result.size());
|
||||
spec->curr_impl = &impl; // set current implementation for stats
|
||||
impl.drafts_generated_count++;
|
||||
impl.drafts_generated_tokens += result.size();
|
||||
|
||||
break; // We have a draft, so break out of the loop and return it.
|
||||
}
|
||||
}
|
||||
if (spec == nullptr || spec->ctx_dft == nullptr) {
|
||||
return {}; // no draft model, return
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
llama_tokens common_speculative_use_draft_model(
|
||||
struct common_speculative * spec,
|
||||
struct common_speculative_params params,
|
||||
const llama_tokens & prompt_tgt_main_model, // specified in target model vocab
|
||||
llama_token id_last) {
|
||||
|
||||
auto & batch = spec->batch;
|
||||
auto & ctx_tgt = spec->ctx_tgt;
|
||||
@@ -413,8 +614,17 @@ llama_tokens common_speculative_gen_draft(
|
||||
}
|
||||
|
||||
void common_speculative_send_accepted(struct common_speculative * spec, const uint16_t n_accepted) {
|
||||
// use new function to update the ngram map statistics.
|
||||
common_ngram_map_send_accepted(spec->map, n_accepted);
|
||||
common_speculative_impl * impl = spec->curr_impl;
|
||||
if (impl != nullptr) {
|
||||
if (n_accepted > 0) {
|
||||
impl->drafts_accepted_count++;
|
||||
impl->drafts_accepted_tokens += n_accepted;
|
||||
}
|
||||
if (impl->type == COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K ||
|
||||
impl->type == COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V) {
|
||||
common_ngram_map_send_accepted(spec->map, n_accepted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// self-speculative decoding
|
||||
@@ -433,14 +643,6 @@ llama_tokens common_speculative_gen_self_draft(
|
||||
common_speculative * spec,
|
||||
const llama_tokens & tokens, llama_token sampled) {
|
||||
|
||||
common_ngram_map & map = spec->map;
|
||||
if (spec->self_mode != 1) {
|
||||
// Use common_ngram_map_draft to generate a draft from the current context.
|
||||
llama_tokens draft_tokens;
|
||||
common_ngram_map_draft(map, tokens, sampled, draft_tokens);
|
||||
return draft_tokens;
|
||||
}
|
||||
|
||||
// Simple implementation of self-speculative decoding without draft model, without ngram-map.
|
||||
//
|
||||
common_speculative_self & self_state = spec->self_state;
|
||||
@@ -513,7 +715,16 @@ llama_tokens common_speculative_gen_self_draft(
|
||||
}
|
||||
|
||||
void common_speculative_print_stats(const struct common_speculative * spec) {
|
||||
if (spec->map.drafts_generated_tokens > 0) { // only print if we have some stats
|
||||
common_ngram_map_print_stats(spec->map);
|
||||
if (spec == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (const auto & impl : spec->impls) {
|
||||
LOG_INF("statistics %s: #calls = %zu, #gen drafts = %zu, #acc drafts = %zu, #gen tokens = %zu, #acc tokens = %zu\n",
|
||||
common_speculative_type_to_str(impl.type).c_str(),
|
||||
impl.drafts_call_count,
|
||||
impl.drafts_generated_count,
|
||||
impl.drafts_accepted_count,
|
||||
impl.drafts_generated_tokens,
|
||||
impl.drafts_accepted_tokens);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user