Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions common/arg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,8 @@ common_models_handler common_models_handler_init(const common_params & params, l
common_download_hf_plan plan_spec;
common_download_opts opts;

const bool spec_type_draft_mtp = std::find(params.speculative.types.begin(),
params.speculative.types.end(),
COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params.speculative.types.end();
const bool spec_type_draft_mtp = std::find(params.speculative.types.begin(), params.speculative.types.end(), COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params.speculative.types.end() ||
std::find(params.speculative.types.begin(), params.speculative.types.end(), COMMON_SPECULATIVE_TYPE_DRAFT_MTP_ADAPTIVE) != params.speculative.types.end();

const bool spec_type_draft_dflash = std::find(params.speculative.types.begin(),
params.speculative.types.end(),
Expand Down Expand Up @@ -1303,6 +1302,7 @@ bool common_params_parse(int argc, char ** argv, common_params & params, llama_e
common_params_print_completion(ctx_arg);
exit(0);
}

params.lr.init();
} catch (const std::invalid_argument & ex) {
fprintf(stderr, "%s\n", ex.what());
Expand Down Expand Up @@ -4186,6 +4186,14 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
}
).set_spec().set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_SPEC_SYNTH_RATES"));

add_opt(common_arg(
{"--spec-draft-n-min-adaptive"}, "N",
string_format("minimum adaptive MTP draft depth; the depth starts here and never drops below it (default: %d)", params.speculative.draft.n_min_adaptive),
[](common_params & params, int value) {
params.speculative.draft.n_min_adaptive = value;
}
).set_spec().set_examples({LLAMA_EXAMPLE_SPECULATIVE, LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_SPEC_DRAFT_N_MIN_ADAPTIVE"));

add_opt(common_arg(
{"--spec-draft-p-split", "--draft-p-split"}, "P",
string_format("speculative decoding split probability (default: %.2f)", (double)params.speculative.draft.p_split),
Expand Down
3 changes: 2 additions & 1 deletion common/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1711,7 +1711,8 @@ struct llama_model_params common_model_params_to_llama(common_params & params) {
mparams.progress_callback = params.load_progress_callback;
mparams.progress_callback_user_data = params.load_progress_callback_user_data;
mparams.no_alloc = params.no_alloc;
mparams.load_mtp = std::find(params.speculative.types.begin(), params.speculative.types.end(), COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params.speculative.types.end();
mparams.load_mtp = std::find(params.speculative.types.begin(), params.speculative.types.end(), COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params.speculative.types.end() ||
std::find(params.speculative.types.begin(), params.speculative.types.end(), COMMON_SPECULATIVE_TYPE_DRAFT_MTP_ADAPTIVE) != params.speculative.types.end();

return mparams;
}
Expand Down
30 changes: 18 additions & 12 deletions common/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,18 +169,19 @@ enum common_params_sampling_config : uint64_t {
};

enum common_speculative_type {
COMMON_SPECULATIVE_TYPE_NONE, // no speculative decoding
COMMON_SPECULATIVE_TYPE_DRAFT_SIMPLE, // standalone draft model speculative decoding
COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3, // Eagle3 speculative decoding
COMMON_SPECULATIVE_TYPE_DRAFT_MTP, // Multi-token prediction
COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH, // DFlash speculative decoding
COMMON_SPECULATIVE_TYPE_DRAFT_DSPARK, // DSpark speculative decoding (DFlash + Markov head)
COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE, // simple self-speculative decoding based on n-grams
COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K, // self-speculative decoding with n-gram keys only
COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V, // self-speculative decoding with n-gram keys and 4 m-gram values
COMMON_SPECULATIVE_TYPE_NONE, // no speculative decoding
COMMON_SPECULATIVE_TYPE_DRAFT_SIMPLE, // standalone draft model speculative decoding
COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3, // Eagle3 speculative decoding
COMMON_SPECULATIVE_TYPE_DRAFT_MTP, // Multi-token prediction
COMMON_SPECULATIVE_TYPE_DRAFT_MTP_ADAPTIVE, // MTP with adaptive draft depth
COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH, // DFlash speculative decoding
COMMON_SPECULATIVE_TYPE_DRAFT_DSPARK, // DSpark speculative decoding (DFlash + Markov head)
COMMON_SPECULATIVE_TYPE_NGRAM_SIMPLE, // simple self-speculative decoding based on n-grams
COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K, // self-speculative decoding with n-gram keys only
COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V, // self-speculative decoding with n-gram keys and 4 m-gram values
COMMON_SPECULATIVE_TYPE_NGRAM_MOD,
COMMON_SPECULATIVE_TYPE_NGRAM_CACHE, // self-speculative decoding with 3-level n-gram cache
COMMON_SPECULATIVE_TYPE_COUNT // number of types, unknown type
COMMON_SPECULATIVE_TYPE_NGRAM_CACHE, // self-speculative decoding with 3-level n-gram cache
COMMON_SPECULATIVE_TYPE_COUNT // number of types, unknown type
};

// Grammar type enumeration
Expand Down Expand Up @@ -325,6 +326,7 @@ struct common_params_model {
struct common_params_speculative_draft {
int32_t n_max = 3; // maximum number of tokens to draft during speculative decoding
int32_t n_min = 0; // minimum number of draft tokens to use for speculative decoding
int32_t n_min_adaptive = 3; // minimum adaptive MTP draft depth (also the starting depth)

float p_split = 0.1f; // speculative decoding split probability
float p_min = 0.0f; // minimum speculative decoding probability (greedy)
Expand Down Expand Up @@ -393,7 +395,11 @@ struct common_params_speculative {

uint32_t need_n_rs_seq() const {
bool needs_rs_seq = std::any_of(types.begin(), types.end(), [&](auto t) {
return t == COMMON_SPECULATIVE_TYPE_DRAFT_MTP || t == COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3 || t == COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH || t == COMMON_SPECULATIVE_TYPE_DRAFT_DSPARK;
return t == COMMON_SPECULATIVE_TYPE_DRAFT_MTP ||
t == COMMON_SPECULATIVE_TYPE_DRAFT_MTP_ADAPTIVE ||
t == COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3 ||
t == COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH ||
t == COMMON_SPECULATIVE_TYPE_DRAFT_DSPARK;
});

return needs_rs_seq ? draft.n_max : 0u;
Expand Down
92 changes: 92 additions & 0 deletions common/speculative-adaptive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#pragma once

#include <algorithm>

// Adaptive draft depth controller for MTP speculative decoding (draft-mtp-adaptive).
//
// Hysteresis state machine with a climb counter and a weighted drop-pressure
// accumulator. The depth N climbs one step after N_CLIMB(N) consecutive verifies
// that accepted every drafted token. The climb cost is low at the floor and at
// depth, high in the middle: 2 at depth 1, 4 at depth 2, 10 at depth 3, then
// 6/3/2/2 from depth 4 upward. Getting from the floor to depth 3 needs only 6
// full accepts, but pushing past 3 (where prose acceptance collapses) costs 10
// full accepts of 3-token drafts, which predictable content clears quickly and
// marginal content never does. Any miss adds (n_draft - n_accepted) to a
// drop-pressure accumulator; when it reaches depth * 5 the depth drops one step
// and the pressure resets. A near miss (n_draft-1) adds 1, a total miss adds
// n_draft, so high depths fall quickly while low depths hold. The drop budget
// scales with depth but never drops below 20, so shallow depths shed bad content
// quickly without collapsing to the floor on a few bad rounds; deep depths hold
// a little longer. At the floor no pressure accumulates at all. The depth starts
// at the floor max(1, --spec-draft-n-min-adaptive) and stays in
// [floor, n_max]; --spec-draft-n-max bounds the upper end of the adaptive
// range.
struct common_speculative_adaptive {
int n_cur = 0; // current adaptive draft depth N
int n_climb = 0; // consecutive verifies that accepted every drafted token
int n_drop = 0; // accumulated drop pressure: sum of (n_draft - n_accepted)

// consecutive full accepts needed to climb one step from depth N; low at the
// floor and at depth, high in the middle where acceptance is marginal
static int climb_threshold(int depth) {
switch (depth) {
case 1: return 2;
case 2: return 4;
case 3: return 10; // hardened 3->4 barrier: keeps prose/reasoning pinned
case 4: return 6;
case 5: return 3;
case 6: return 2;
default: return 2; // depth >= 7
}
}

// accumulated (n_draft - n_accepted) needed to drop one step from depth N;
// scaled by depth, with a floor so shallow depths do not collapse too fast
static int drop_pressure(int depth) {
return std::max(depth * 5, 20);
}

// reset to the floor max(1, n_min_adaptive), bounded by the ceiling n_max;
// the controller climbs from there once acceptance feedback arrives
void reset(int n_max, int n_min_adaptive) {
const int cap = std::max(1, n_max);
const int floor = std::max(1, n_min_adaptive);

n_cur = std::min(floor, cap);
n_climb = 0;
n_drop = 0;
}

// feed one verification result: n_draft is the number of tokens this
// implementation drafted, n_accepted the number the target accepted
void update(int n_draft, int n_accepted, int n_max, int n_min_adaptive) {
if (n_draft <= 0) {
return;
}

const int cap = std::max(1, n_max);
const int floor = std::max(1, n_min_adaptive);

if (n_accepted == n_draft) {
n_drop = 0;

// full acceptance: reset the drop pressure, accumulate the climb streak
if (n_cur < cap && ++n_climb >= climb_threshold(n_cur)) {
n_cur++;
n_climb = 0;
}
} else {
n_climb = 0;

// any miss adds (n_draft - n_accepted) to the drop pressure; drop one
// step when the accumulated pressure reaches the depth-scaled budget
if (n_cur > floor) {
n_drop += n_draft - n_accepted;
if (n_drop >= drop_pressure(n_cur)) {
n_cur--;
n_drop = 0;
}
}
}
}
};
Loading