Skip to content

Commit fafe7ea

Browse files
committed
imatrix: optionally activate MTP/NextN draft head during collection
`llama-imatrix` only runs forward passes through the trunk, so MTP draft head tensors (`blk.<n_layer>.nextn.eh_proj` etc., added by ggml-org#22673) never receive activations and have no imatrix data. Low-bit i-quants for those tensors then fail at quantize-time: llama_model_quantize: failed to quantize: Missing importance matrix for tensor blk.40.nextn.eh_proj.weight in a very low-bit quantization This adds an opt-in `--mtp` flag to `llama-imatrix`. When set and the loaded model has MTP/NextN layers, a second `llama_context` is created with `ctx_type = LLAMA_CONTEXT_TYPE_MTP`. After each trunk sub-batch decode, the trunk's pre-norm hidden states are paired with the next-token ids and decoded through the MTP context, mirroring how `common_speculative_state_draft_mtp::process()` invokes the head during real spec decoding. MTP-layer tensors then land in the same imatrix collector via the existing eval callback. Default behavior unchanged. No-op (with warning) for models without MTP layers. Currently restricted to `n_seq == 1` to keep MTP-row-to- output-row mapping unambiguous; warns and disables itself otherwise. Adds a small public accessor `llama_model_n_nextn(model)` so callers outside `src/` can probe MTP presence without pulling in `llama_hparams`. Files: common/arg.cpp +9 --mtp CLI option common/common.h +1 imat_mtp on common_params include/llama.h +4 llama_model_n_nextn() decl src/llama-model.cpp +4 llama_model_n_nextn() impl tools/imatrix/imatrix.cpp +125 MTP context + per-batch MTP forward pass
1 parent 0253fb2 commit fafe7ea

5 files changed

Lines changed: 140 additions & 3 deletions

File tree

common/arg.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2764,6 +2764,15 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
27642764
params.parse_special = true;
27652765
}
27662766
).set_examples({LLAMA_EXAMPLE_IMATRIX}));
2767+
add_opt(common_arg(
2768+
{"--mtp"},
2769+
string_format("also activate the MTP/NextN draft head during imatrix collection so its tensors "
2770+
"(blk.<n>.nextn.eh_proj etc.) receive activations. No-op if the model has no MTP layers. "
2771+
"(default: %s)", params.imat_mtp ? "true" : "false"),
2772+
[](common_params & params) {
2773+
params.imat_mtp = true;
2774+
}
2775+
).set_examples({LLAMA_EXAMPLE_IMATRIX}));
27672776
add_opt(common_arg(
27682777
{"-pps"},
27692778
string_format("is the prompt shared across parallel sequences (default: %s)", params.is_pp_shared ? "true" : "false"),

common/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ struct common_params {
681681
bool compute_ppl = true; // whether to compute perplexity
682682
bool show_statistics = false; // show imatrix statistics per tensor
683683
bool parse_special = false; // whether to parse special tokens during imatrix tokenization
684+
bool imat_mtp = false; // also activate the MTP/NextN draft head so its tensors get imatrix data
684685

685686
// cvector-generator params
686687
int n_pca_batch = 100;

include/llama.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,10 @@ extern "C" {
562562
LLAMA_API int32_t llama_model_n_head_kv (const struct llama_model * model);
563563
LLAMA_API int32_t llama_model_n_swa (const struct llama_model * model);
564564

565+
// Number of MTP / NextN draft head layers bundled with the model (0 if none).
566+
// Used by callers (e.g. imatrix) that need to know whether the model carries an MTP head.
567+
LLAMA_API int32_t llama_model_n_nextn (const struct llama_model * model);
568+
565569
// Get the model's RoPE frequency scaling factor
566570
LLAMA_API float llama_model_rope_freq_scale_train(const struct llama_model * model);
567571

src/llama-model.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2182,6 +2182,10 @@ int32_t llama_model_n_swa(const llama_model * model) {
21822182
return model->hparams.n_swa;
21832183
}
21842184

2185+
int32_t llama_model_n_nextn(const llama_model * model) {
2186+
return (int32_t) model->hparams.nextn_predict_layers;
2187+
}
2188+
21852189

21862190
uint32_t llama_model_n_cls_out(const struct llama_model * model) {
21872191
return model->hparams.n_cls_out;

tools/imatrix/imatrix.cpp

Lines changed: 122 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "common.h"
33
#include "log.h"
44
#include "llama.h"
5+
#include "../../src/llama-ext.h" // staging API: llama_set_embeddings_pre_norm / llama_get_embeddings_pre_norm_ith (used by MTP)
56
#include "gguf.h"
67

78
#include <algorithm>
@@ -916,7 +917,53 @@ static void process_logits(
916917
}
917918
}
918919

919-
static bool compute_imatrix(llama_context * ctx, const common_params & params, const int32_t n_ctx) {
920+
// Run a forward pass through the MTP/NextN draft head so its weights
921+
// (blk.<n_layer>.nextn.eh_proj etc.) receive activations and get recorded by
922+
// the imatrix collector. Mirrors common_speculative_state_draft_mtp::process():
923+
// the MTP head at position p is fed the next-token id (tokens[p+1]) paired
924+
// with the trunk's pre-norm hidden state h[p]. The last position of the
925+
// chunk has no next-token target and is dropped.
926+
static bool compute_imatrix_mtp(
927+
llama_context * ctx_tgt,
928+
llama_context * ctx_mtp,
929+
const llama_token * tokens, // n_tokens consecutive tokens (covers this batch)
930+
int32_t n_tokens,
931+
int32_t pos_first, // absolute position of tokens[0] in the chunk
932+
int32_t n_embd,
933+
llama_seq_id seq_id,
934+
llama_batch & mtp_batch) { // pre-allocated, embd-capable batch (token+embd both alloc'd)
935+
if (n_tokens < 2) {
936+
return true; // need at least one (h[p], token[p+1]) pair
937+
}
938+
const int32_t n_pairs = n_tokens - 1;
939+
940+
const size_t row_bytes = (size_t) n_embd * sizeof(float);
941+
942+
common_batch_clear(mtp_batch);
943+
944+
for (int32_t k = 0; k < n_pairs; ++k) {
945+
// MTP position p+1 carries the next-token id and h[p] from the trunk.
946+
common_batch_add(mtp_batch, tokens[k + 1], pos_first + k + 1, { seq_id }, false);
947+
}
948+
949+
// Fill h[p] rows from the trunk's pre-norm output.
950+
for (int32_t k = 0; k < n_pairs; ++k) {
951+
const float * h = llama_get_embeddings_pre_norm_ith(ctx_tgt, k);
952+
if (h == nullptr) {
953+
LOG_ERR("%s: trunk did not produce pre-norm embedding at row %d (was output enabled?)\n", __func__, k);
954+
return false;
955+
}
956+
std::memcpy(mtp_batch.embd + (size_t) k * n_embd, h, row_bytes);
957+
}
958+
959+
if (llama_decode(ctx_mtp, mtp_batch) != 0) {
960+
LOG_ERR("%s: llama_decode(ctx_mtp) failed\n", __func__);
961+
return false;
962+
}
963+
return true;
964+
}
965+
966+
static bool compute_imatrix(llama_context * ctx, llama_context * ctx_mtp, const common_params & params, const int32_t n_ctx) {
920967
const llama_model * model = llama_get_model(ctx);
921968
const llama_vocab * vocab = llama_model_get_vocab(model);
922969

@@ -975,15 +1022,37 @@ static bool compute_imatrix(llama_context * ctx, const common_params & params, c
9751022

9761023
llama_batch batch = llama_batch_init(std::min(n_batch, n_ctx*n_seq), 0, 1);
9771024

1025+
// Optional MTP/NextN draft head batch. Only used when ctx_mtp != nullptr.
1026+
// llama_batch_init() only allocates one of token/embd; MTP needs both, so we
1027+
// patch in a token buffer alongside (same trick as common/speculative.cpp).
1028+
llama_batch mtp_batch = {};
1029+
bool mtp_enabled = (ctx_mtp != nullptr);
1030+
const int n_embd = llama_model_n_embd(model);
1031+
if (mtp_enabled) {
1032+
if (n_seq != 1) {
1033+
LOG_WRN("%s: --mtp is only supported with n_seq=1 (one sequence per batch); disabling MTP collection\n", __func__);
1034+
mtp_enabled = false;
1035+
} else {
1036+
mtp_batch = llama_batch_init(std::min(n_batch, n_ctx), n_embd, 1);
1037+
mtp_batch.token = (llama_token *) malloc(sizeof(llama_token) * std::min(n_batch, n_ctx));
1038+
}
1039+
}
1040+
9781041
std::vector<float> logits;
9791042
if (params.compute_ppl && num_batches > 1) {
9801043
logits.reserve((size_t)n_ctx * n_vocab);
9811044
}
9821045

983-
LOG_INF("%s: computing over %d chunks, n_ctx=%d, batch_size=%d, n_seq=%d\n", __func__, n_chunk, n_ctx, n_batch, n_seq);
1046+
LOG_INF("%s: computing over %d chunks, n_ctx=%d, batch_size=%d, n_seq=%d%s\n",
1047+
__func__, n_chunk, n_ctx, n_batch, n_seq, mtp_enabled ? " (mtp head active)" : "");
9841048

9851049
std::vector<std::thread> workers(std::thread::hardware_concurrency() - 1);
9861050

1051+
if (mtp_enabled) {
1052+
// Trunk must expose the pre-norm hidden state so we can feed it into the MTP head.
1053+
llama_set_embeddings_pre_norm(ctx, true);
1054+
}
1055+
9871056
for (int i = 0; i < n_chunk; i += n_seq) {
9881057
const int start = i * n_ctx;
9891058
const int end = start + n_ctx;
@@ -994,6 +1063,9 @@ static bool compute_imatrix(llama_context * ctx, const common_params & params, c
9941063

9951064
// clear the KV cache
9961065
llama_memory_clear(llama_get_memory(ctx), true);
1066+
if (mtp_enabled) {
1067+
llama_memory_clear(llama_get_memory(ctx_mtp), true);
1068+
}
9971069

9981070
for (int j = 0; j < num_batches; ++j) {
9991071
const int batch_start = start + j * n_batch;
@@ -1027,13 +1099,31 @@ static bool compute_imatrix(llama_context * ctx, const common_params & params, c
10271099
if (llama_decode(ctx, batch)) {
10281100
LOG_ERR("%s : failed to eval\n", __func__);
10291101
llama_batch_free(batch);
1102+
if (mtp_enabled) {
1103+
free(mtp_batch.token);
1104+
llama_batch_free(mtp_batch);
1105+
}
10301106
return false;
10311107
}
10321108

10331109
if (params.compute_ppl && num_batches > 1) {
10341110
const auto * batch_logits = llama_get_logits(ctx);
10351111
logits.insert(logits.end(), batch_logits, batch_logits + batch_size * n_vocab);
10361112
}
1113+
1114+
if (mtp_enabled) {
1115+
// The sub-batch covers absolute positions [batch_start, batch_start + batch_size).
1116+
// tokens.data() + batch_start gives the matching token ids.
1117+
const int32_t pos_first = j * n_batch;
1118+
if (!compute_imatrix_mtp(ctx, ctx_mtp,
1119+
tokens.data() + batch_start, batch_size,
1120+
pos_first, n_embd, /*seq_id=*/0, mtp_batch)) {
1121+
llama_batch_free(batch);
1122+
free(mtp_batch.token);
1123+
llama_batch_free(mtp_batch);
1124+
return false;
1125+
}
1126+
}
10371127
}
10381128

10391129

@@ -1089,6 +1179,10 @@ static bool compute_imatrix(llama_context * ctx, const common_params & params, c
10891179
}
10901180

10911181
llama_batch_free(batch);
1182+
if (mtp_enabled) {
1183+
free(mtp_batch.token);
1184+
llama_batch_free(mtp_batch);
1185+
}
10921186

10931187
return true;
10941188
}
@@ -1303,7 +1397,28 @@ int main(int argc, char ** argv) {
13031397
LOG_INF("%s\n", common_params_get_system_info(params).c_str());
13041398
}
13051399

1306-
if (!compute_imatrix(ctx, params, n_ctx)) {
1400+
// Optional second context for the MTP/NextN draft head. Shares the same model
1401+
// as `ctx`; uses LLAMA_CONTEXT_TYPE_MTP so the MTP graph is built/run instead
1402+
// of the trunk graph. The trunk feeds it pre-norm hidden states each batch.
1403+
llama_context * ctx_mtp = nullptr;
1404+
if (params.imat_mtp) {
1405+
if (llama_model_n_nextn(model) == 0) {
1406+
LOG_WRN("%s: --mtp requested but model has no MTP/NextN layers; ignoring\n", __func__);
1407+
} else {
1408+
auto cparams_mtp = common_context_params_to_llama(params);
1409+
cparams_mtp.ctx_type = LLAMA_CONTEXT_TYPE_MTP;
1410+
cparams_mtp.n_rs_seq = 0;
1411+
ctx_mtp = llama_init_from_model(model, cparams_mtp);
1412+
if (ctx_mtp == nullptr) {
1413+
LOG_ERR("%s : failed to create MTP context\n", __func__);
1414+
return 1;
1415+
}
1416+
LOG_INF("%s: created MTP draft-head context for imatrix collection\n", __func__);
1417+
}
1418+
}
1419+
1420+
if (!compute_imatrix(ctx, ctx_mtp, params, n_ctx)) {
1421+
if (ctx_mtp) llama_free(ctx_mtp);
13071422
return 1;
13081423
}
13091424

@@ -1312,6 +1427,10 @@ int main(int argc, char ** argv) {
13121427
LOG("\n");
13131428
llama_perf_context_print(ctx);
13141429

1430+
if (ctx_mtp) {
1431+
llama_free(ctx_mtp);
1432+
}
1433+
13151434
llama_backend_free();
13161435

13171436
return 0;

0 commit comments

Comments
 (0)