1477 lines
74 KiB
C++
1477 lines
74 KiB
C++
#include "../llama-build-context.h"
|
|
#include "../llama-model.h"
|
|
#include "../llama-context.h"
|
|
|
|
#include <vector>
|
|
|
|
// Per-rank attention for DEEPSEEK2 under -sm graph (requires -fa + -mla>=1).
|
|
|
|
ggml_tensor * llm_build_context::build_deepseek2_tp_attention(
|
|
ggml_cgraph * gf, int il,
|
|
ggml_tensor * inpL,
|
|
ggml_tensor * KQ_mask, ggml_tensor * inp_pos,
|
|
ggml_tensor * rope_cache,
|
|
float kq_scale, float attn_factor_scaled,
|
|
bool use_f32_attn_precision,
|
|
bool is_lite,
|
|
bool pp_opt) {
|
|
if (!lctx.cparams.flash_attn || lctx.cparams.mla_attn < 1) {
|
|
GGML_ABORT("-sm graph for MLA archs (DEEPSEEK2/GLM_DSA/MISTRAL4) requires -fa on and -mla >= 1. "
|
|
"Got mla_attn=%d, flash_attn=%d.",
|
|
(int)lctx.cparams.mla_attn, (int)lctx.cparams.flash_attn);
|
|
}
|
|
|
|
auto wo_split = (const ggml_split_tensor_t *)model.layers[il].wo->extra;
|
|
GGML_ASSERT(wo_split);
|
|
const int n_device = wo_split->n_device;
|
|
|
|
const uint32_t n_embd_head_qk_rope = hparams.n_rot;
|
|
const uint32_t n_embd_head_qk_nope = hparams.n_embd_head_k(il) - hparams.n_rot;
|
|
const uint32_t kv_lora_rank = hparams.n_lora_kv;
|
|
const uint32_t n_embd_head_k = hparams.n_embd_head_k(il);
|
|
const uint32_t n_embd_head_v = hparams.n_embd_head_v(il);
|
|
|
|
auto cache_repl = (const ggml_split_tensor_t *)kv_self.k_l[il]->extra;
|
|
if (!cache_repl) {
|
|
LLAMA_LOG_ERROR("%s: no cache split for layer %d?\n", __func__, il);
|
|
}
|
|
GGML_ASSERT(cache_repl);
|
|
GGML_ASSERT(cache_repl->n_device == n_device);
|
|
|
|
std::vector<ggml_tensor *> attn_partials(n_device, nullptr);
|
|
bool input_added = false; // add residual once, on the first non-skipped rank
|
|
|
|
// head_offset per rank: wo is row-split with splits[id]->ne[0] == n_head_local_id * n_embd_head_v.
|
|
// wk_b/wv_b are per-head split (split_dim=2); each rank's tensor already holds only its heads.
|
|
std::vector<int> head_offsets(n_device + 1, 0);
|
|
for (int idx = 0; idx < n_device; ++idx) {
|
|
int n_h_id = 0;
|
|
if (wo_split->splits[idx]) {
|
|
n_h_id = (int)(wo_split->splits[idx]->ne[0] / n_embd_head_v);
|
|
}
|
|
head_offsets[idx + 1] = head_offsets[idx] + n_h_id;
|
|
}
|
|
|
|
for (int id = 0; id < n_device; ++id) {
|
|
if (!wo_split->splits[id]) continue;
|
|
const int il_id = 1000 * il + id; // unique cb() id per (layer, rank)
|
|
|
|
auto input = get_input_tensor_sm_graph(ctx0, inpL, id);
|
|
|
|
auto attn_norm_split = (const ggml_split_tensor_t *)model.layers[il].attn_norm->extra;
|
|
GGML_ASSERT(attn_norm_split);
|
|
ggml_tensor * cur = llm_build_norm(ctx0, input, hparams,
|
|
attn_norm_split->splits[id], nullptr, LLM_NORM_RMS, cb, il_id);
|
|
|
|
ggml_tensor * q;
|
|
if (!is_lite) {
|
|
auto wq_a_split = (const ggml_split_tensor_t *)model.layers[il].wq_a->extra;
|
|
auto wq_b_split = (const ggml_split_tensor_t *)model.layers[il].wq_b->extra;
|
|
GGML_ASSERT(wq_a_split && wq_b_split);
|
|
q = ggml_mul_mat(ctx0, wq_a_split->splits[id], cur);
|
|
ggml_build_forward_expand(gf, q);
|
|
auto q_a_norm_split = (const ggml_split_tensor_t *)model.layers[il].attn_q_a_norm->extra;
|
|
GGML_ASSERT(q_a_norm_split);
|
|
q = llm_build_norm(ctx0, q, hparams, q_a_norm_split->splits[id], nullptr, LLM_NORM_RMS, cb, il_id);
|
|
q = ggml_mul_mat(ctx0, wq_b_split->splits[id], q);
|
|
} else {
|
|
auto wq_split = (const ggml_split_tensor_t *)model.layers[il].wq->extra;
|
|
GGML_ASSERT(wq_split);
|
|
q = ggml_mul_mat(ctx0, wq_split->splits[id], cur);
|
|
ggml_build_forward_expand(gf, q);
|
|
}
|
|
cb(q, "q", il_id);
|
|
|
|
const int n_head_local = q->ne[0] / n_embd_head_k;
|
|
|
|
const size_t row_size_q = ggml_row_size(q->type, n_embd_head_k);
|
|
ggml_tensor * q_nope = ggml_view_3d(ctx0, q,
|
|
n_embd_head_qk_nope, n_head_local, n_tokens,
|
|
row_size_q, q->nb[1], 0);
|
|
ggml_tensor * q_rope = ggml_view_3d(ctx0, q,
|
|
n_embd_head_qk_rope, n_head_local, n_tokens,
|
|
row_size_q, q->nb[1],
|
|
ggml_row_size(q->type, n_embd_head_qk_nope));
|
|
|
|
auto wkv_a_mqa_split = (const ggml_split_tensor_t *)model.layers[il].wkv_a_mqa->extra;
|
|
GGML_ASSERT(wkv_a_mqa_split);
|
|
ggml_tensor * kv_rope_compressed = ggml_mul_mat(ctx0, wkv_a_mqa_split->splits[id], cur);
|
|
ggml_build_forward_expand(gf, kv_rope_compressed);
|
|
|
|
ggml_tensor * k_rope = ggml_view_3d(ctx0, kv_rope_compressed,
|
|
n_embd_head_qk_rope, 1, n_tokens,
|
|
kv_rope_compressed->nb[1], kv_rope_compressed->nb[1],
|
|
ggml_row_size(kv_rope_compressed->type, kv_lora_rank));
|
|
ggml_tensor * kv_compressed = ggml_view_2d(ctx0, kv_rope_compressed,
|
|
kv_lora_rank, n_tokens, kv_rope_compressed->nb[1], 0);
|
|
|
|
if (rope_cache) {
|
|
q_rope = ggml_rope_fast(ctx0, q_rope, rope_cache);
|
|
k_rope = ggml_rope_fast(ctx0, k_rope, rope_cache);
|
|
} else {
|
|
q_rope = ggml_rope_ext(ctx0, q_rope, inp_pos, nullptr, n_rot, rope_type,
|
|
n_ctx_orig, freq_base, freq_scale, ext_factor, attn_factor_scaled, beta_fast, beta_slow);
|
|
k_rope = ggml_rope_ext(ctx0, k_rope, inp_pos, nullptr, n_rot, rope_type,
|
|
n_ctx_orig, freq_base, freq_scale, ext_factor, attn_factor_scaled, beta_fast, beta_slow);
|
|
}
|
|
|
|
{
|
|
auto kv_a_norm_split = (const ggml_split_tensor_t *)model.layers[il].attn_kv_a_norm->extra;
|
|
GGML_ASSERT(kv_a_norm_split);
|
|
kv_compressed = llm_build_norm(ctx0, kv_compressed, hparams,
|
|
kv_a_norm_split->splits[id], NULL, LLM_NORM_RMS, cb, il_id);
|
|
}
|
|
|
|
ggml_tensor * cache_local = cache_repl->splits[id];
|
|
const auto row_size_cache = ggml_row_size(cache_local->type, kv_lora_rank + n_embd_head_qk_rope);
|
|
ggml_tensor * cache_write_view = ggml_view_2d(ctx0, cache_local,
|
|
cache_local->ne[0], n_tokens, row_size_cache, row_size_cache * kv_head);
|
|
|
|
ggml_tensor * kvr = ggml_concat(ctx0, ggml_permute(ctx0, k_rope, 0, 2, 1, 3), kv_compressed, 0);
|
|
if (cparams.k_cache_hadamard) {
|
|
kvr = ggml_hadamard(ctx0, kvr, 64);
|
|
}
|
|
|
|
// Per-rank cache_copies slot.
|
|
const int cc_idx = 2 * n_device * il + 2 * id;
|
|
GGML_ASSERT(cc_idx + 1 < (int)lctx.cache_copies.size());
|
|
lctx.cache_copies[cc_idx + 0].cpy = ggml_cpy(ctx0, kvr, cache_write_view);
|
|
lctx.cache_copies[cc_idx + 0].step = row_size_cache;
|
|
ggml_build_forward_expand(gf, lctx.cache_copies[cc_idx + 0].cpy);
|
|
|
|
ggml_tensor * kv_cache = ggml_view_2d(ctx0, cache_local,
|
|
kv_lora_rank + n_embd_head_qk_rope, n_kv,
|
|
row_size_cache, 0);
|
|
cb(kv_cache, "kv_cache", il_id);
|
|
|
|
// pp_opt (mla > 1, n_tokens >= 128, n_kv >= k_pp_opt_min_kv): materialize
|
|
// per-rank K/V from the latent cache and use standard flash_attn instead of
|
|
// FlashMLA-3 absorb.
|
|
constexpr int k_pp_opt_min_kv = 1024;
|
|
const bool tp_pp_opt = pp_opt
|
|
&& (int)n_kv >= k_pp_opt_min_kv
|
|
&& model.layers[il].wk_b
|
|
&& model.layers[il].wv_b
|
|
&& model.layers[il].wk_b_pp;
|
|
|
|
ggml_tensor * kqv_2d;
|
|
|
|
if (tp_pp_opt) {
|
|
// Per-rank wk_b/wv_b slices already exist from distribute_mla_tensors:
|
|
// wk_b_local_pp: [n_embd_head_qk_nope, kv_lora_rank, n_head_local]
|
|
// wv_b_local_pp: [kv_lora_rank, n_embd_head_v, n_head_local]
|
|
auto wv_b_pp_split_raw = (const ggml_split_tensor_t *)model.layers[il].wv_b->extra;
|
|
GGML_ASSERT(wv_b_pp_split_raw);
|
|
ggml_tensor * wv_b_local_pp = wv_b_pp_split_raw->splits[id];
|
|
|
|
ggml_tensor * kv_cache_nope = ggml_view_2d(ctx0, cache_local,
|
|
kv_lora_rank, n_kv,
|
|
row_size_cache,
|
|
ggml_row_size(cache_local->type, n_embd_head_qk_rope));
|
|
cb(kv_cache_nope, "kv_cache_nope_pp", il_id);
|
|
|
|
ggml_tensor * kv_cache_rope_view = ggml_view_3d(ctx0, cache_local,
|
|
n_embd_head_qk_rope, n_kv, 1,
|
|
row_size_cache, cache_local->nb[2], 0);
|
|
cb(kv_cache_rope_view, "kv_cache_rope_pp", il_id);
|
|
|
|
// Un-Hadamard the cache views via the fused dequant+hadamard kernel.
|
|
// When khad_pretransformed is set, H was folded into wv_b/wk_b_pp at init,
|
|
// so the cache_nope un-Hadamard is skipped (rope half still goes to FA via
|
|
// concat — no wk_b multiply, no H to fold into).
|
|
if (cparams.k_cache_hadamard) {
|
|
kv_cache_rope_view = ggml_hadamard(ctx0, kv_cache_rope_view, 64);
|
|
if (!model.khad_pretransformed) {
|
|
kv_cache_nope = ggml_hadamard(ctx0, kv_cache_nope, 64);
|
|
}
|
|
}
|
|
|
|
// CUDA quantized-cache + REPEAT/CONCAT/CPY has known issues, so force F16 here.
|
|
const auto kv_type = GGML_TYPE_F16;
|
|
|
|
ggml_tensor repeater;
|
|
repeater.ne[0] = n_embd_head_qk_rope;
|
|
repeater.ne[1] = n_kv;
|
|
repeater.ne[2] = n_head_local;
|
|
repeater.ne[3] = 1;
|
|
ggml_tensor * k_rope_rep;
|
|
if (kv_cache_rope_view->type == kv_type) {
|
|
k_rope_rep = ggml_repeat(ctx0, kv_cache_rope_view, &repeater);
|
|
} else {
|
|
auto kv_rope_f16 = ggml_cast(ctx0, kv_cache_rope_view, kv_type);
|
|
k_rope_rep = ggml_repeat(ctx0, kv_rope_f16, &repeater);
|
|
}
|
|
cb(k_rope_rep, "k_rope_rep_pp", il_id);
|
|
|
|
// V: wv_b_local viewed as 2D [kv_lora_rank, n_head_local * n_embd_head_v].
|
|
// Per-rank, no cross-device transfer per call.
|
|
auto wv_b_2d = ggml_reshape_2d(ctx0, wv_b_local_pp,
|
|
kv_lora_rank, n_head_local * n_embd_head_v);
|
|
ggml_tensor * v_2d = ggml_mul_mat(ctx0, wv_b_2d, kv_cache_nope);
|
|
cb(v_2d, "v_2d_pp", il_id);
|
|
ggml_tensor * v_f32 = ggml_view_3d(ctx0, v_2d,
|
|
n_embd_head_v, n_kv, n_head_local,
|
|
v_2d->nb[1],
|
|
n_embd_head_v * v_2d->nb[0],
|
|
0);
|
|
|
|
// wk_b_pp is transpose(wk_b) pre-materialized in llm_prepare_mla.
|
|
// Shape: [kv_lora_rank, n_embd_head_qk_nope, n_head_local].
|
|
auto wk_b_pp_split = (const ggml_split_tensor_t *)model.layers[il].wk_b_pp->extra;
|
|
GGML_ASSERT(wk_b_pp_split);
|
|
ggml_tensor * wk_b_pp_local = wk_b_pp_split->splits[id];
|
|
GGML_ASSERT(wk_b_pp_local);
|
|
ggml_tensor * wk_b_T_2d = ggml_reshape_2d(ctx0, wk_b_pp_local,
|
|
kv_lora_rank, n_head_local * n_embd_head_qk_nope);
|
|
ggml_tensor * k_nope_2d = ggml_mul_mat(ctx0, wk_b_T_2d, kv_cache_nope);
|
|
cb(k_nope_2d, "k_nope_2d_pp", il_id);
|
|
ggml_tensor * k_nope_f32 = ggml_view_3d(ctx0, k_nope_2d,
|
|
n_embd_head_qk_nope, n_kv, n_head_local,
|
|
k_nope_2d->nb[1],
|
|
n_embd_head_qk_nope * k_nope_2d->nb[0],
|
|
0);
|
|
|
|
ggml_tensor * v = ggml_cast(ctx0, v_f32, kv_type);
|
|
ggml_tensor * k_nope = ggml_cast(ctx0, k_nope_f32, kv_type);
|
|
ggml_build_forward_expand(gf, v);
|
|
ggml_build_forward_expand(gf, k_nope);
|
|
|
|
ggml_tensor * k = ggml_concat(ctx0, k_rope_rep, k_nope, 0);
|
|
ggml_build_forward_expand(gf, k);
|
|
cb(k, "k_full_pp", il_id);
|
|
|
|
ggml_tensor * q = ggml_concat(ctx0, q_rope, q_nope, 0);
|
|
q = ggml_permute(ctx0, q, 0, 2, 1, 3);
|
|
ggml_build_forward_expand(gf, q);
|
|
cb(q, "q_concat_pp", il_id);
|
|
|
|
ggml_tensor * kqv = ggml_flash_attn_ext(ctx0, q, k, v, KQ_mask,
|
|
kq_scale, hparams.f_max_alibi_bias, 0.f);
|
|
if (use_f32_attn_precision || q->ne[1] <= 8) {
|
|
ggml_flash_attn_ext_set_prec(kqv, GGML_PREC_F32);
|
|
}
|
|
cb(kqv, "kqv_pp", il_id);
|
|
|
|
kqv_2d = ggml_reshape_2d(ctx0, kqv, n_embd_head_v * n_head_local, n_tokens);
|
|
} else {
|
|
// Absorb path: FlashMLA-3 with the compressed latent cache, then project via wv_b.
|
|
auto wk_b_split = (const ggml_split_tensor_t *)model.layers[il].wk_b->extra;
|
|
GGML_ASSERT(wk_b_split);
|
|
ggml_tensor * wk_b_local = wk_b_split->splits[id];
|
|
|
|
ggml_tensor * q_nope_perm = ggml_permute(ctx0, q_nope, 0, 2, 1, 3);
|
|
ggml_tensor * q_nope2 = ggml_mul_mat(ctx0, wk_b_local, q_nope_perm);
|
|
|
|
ggml_tensor * q_combined = ggml_concat(ctx0,
|
|
ggml_permute(ctx0, q_rope, 0, 2, 1, 3), q_nope2, 0);
|
|
if (cparams.k_cache_hadamard) {
|
|
q_combined = ggml_hadamard(ctx0, q_combined, 64);
|
|
}
|
|
|
|
// FlashMLA-3 path: K = kv_cache (full latent + rope), V = kv_cache_lora (latent only)
|
|
ggml_tensor * kv_cache_lora = ggml_view_2d(ctx0, cache_local,
|
|
kv_lora_rank, n_kv,
|
|
row_size_cache,
|
|
ggml_row_size(cache_local->type, n_embd_head_qk_rope));
|
|
cb(kv_cache_lora, "kv_cache_lora", il_id);
|
|
|
|
ggml_tensor * kqv_compressed = ggml_flash_attn_ext(ctx0,
|
|
q_combined, kv_cache, kv_cache_lora, KQ_mask,
|
|
kq_scale, hparams.f_max_alibi_bias, 0.f);
|
|
cb(kqv_compressed, "kqv_compressed", il_id);
|
|
if (use_f32_attn_precision) {
|
|
ggml_flash_attn_ext_set_prec(kqv_compressed, GGML_PREC_F32);
|
|
}
|
|
// When khad_pretransformed is set, H is folded into wv_b. FA leaves
|
|
// kqv_compressed in the H-encoded basis; the mul_mat(H@wv_b, kqv_encoded)
|
|
// below collapses to wv_b^T @ kqv_unencoded by H @ H = I. Skip the
|
|
// post-FA un-encode so the fold composes correctly.
|
|
if (cparams.k_cache_hadamard && !model.khad_pretransformed) {
|
|
kqv_compressed = ggml_hadamard(ctx0, kqv_compressed, 64);
|
|
}
|
|
kqv_compressed = ggml_permute(ctx0, kqv_compressed, 0, 2, 1, 3);
|
|
|
|
auto wv_b_split = (const ggml_split_tensor_t *)model.layers[il].wv_b->extra;
|
|
GGML_ASSERT(wv_b_split);
|
|
ggml_tensor * wv_b_local = wv_b_split->splits[id];
|
|
|
|
ggml_tensor * kqv = ggml_mul_mat(ctx0, wv_b_local, kqv_compressed);
|
|
if (n_tokens > 1) {
|
|
kqv = ggml_cont(ctx0, ggml_permute(ctx0, kqv, 0, 2, 1, 3));
|
|
}
|
|
kqv_2d = ggml_reshape_2d(ctx0, kqv, n_embd_head_v * n_head_local, n_tokens);
|
|
}
|
|
|
|
ggml_tensor * partial = llm_build_lora_mm(lctx, ctx0, wo_split->splits[id], kqv_2d);
|
|
|
|
// Fold residual into the first non-skipped rank so the reduce result includes it.
|
|
if (!input_added) {
|
|
partial = ggml_add(ctx0, partial, input);
|
|
input_added = true;
|
|
}
|
|
|
|
if (partial->ne[1] > 32 && lctx.cparams.reduce_type != GGML_TYPE_F32) {
|
|
partial = ggml_cast(ctx0, partial, lctx.cparams.reduce_type);
|
|
}
|
|
ggml_build_forward_expand(gf, partial);
|
|
attn_partials[id] = partial;
|
|
}
|
|
|
|
ggml_tensor * combined = ggml_reduce(ctx0, attn_partials.data(), n_device, GGML_OP_ADD);
|
|
ggml_build_forward_expand(gf, combined);
|
|
cb(combined, "attn_combined", il);
|
|
return combined;
|
|
}
|
|
|
|
// DSA lightning indexer (GLM-5.2 / DeepSeek-V3.2). CACHE-BACKED: the batch's indexer keys are
|
|
// (Hadamard-rotated and) written to a persistent per-layer indexer-key cache (kv_self.kr_l[il]) at
|
|
// kv_head, then the FULL [head_size, n_kv] cached key set is read back and scored against the current
|
|
// batch's indexer queries. This makes DECODE correct: a generated token (kv_head>0) scores against
|
|
// ALL past indexer keys, not just itself. Returns top_k [n_top_k, n_tokens] over the n_kv key axis.
|
|
//
|
|
// The Walsh-Hadamard rotation H (orthonormal, H^2==I) is applied to both indexer_q and indexer_k.
|
|
// (H q)*(H k) == q*k so it is score-preserving; its purpose is to improve the precision of the keys
|
|
// we store in the F16 indexer cache (matches the reference). Gated by cparams.dsa_indexer_hadamard.
|
|
//
|
|
// MULTI-SEQUENCE & FA: both are now handled.
|
|
// - Multi-sequence batches (n_seq>1) are correct: each token is written to its own cache cell at
|
|
// kv_self.head+i (per-sequence), the base block-diagonal KQ_mask drives cross-sequence keys to
|
|
// -inf before argsort, and the attention sink is anchored per-sequence (inp_dsa_sink). Validated
|
|
// n_seq=4 == n_seq=1 chunk-for-chunk (UPDATE 5).
|
|
// - The FA path (-fa 1) consumes the sparse top-k mask too: build_deepseek2_dsa_fa_mask converts
|
|
// the F32 sparse mask into the padded F16 mask the flash-attention kernel reads (UPDATE 4). It
|
|
// does NOT fall back to the dense KQ_mask.
|
|
// - Serving (context-shift / defrag / multi-turn seq_rm): the persistent indexer-key cache kr_l is
|
|
// maintained by build_k_shift (delta-RoPE around the Hadamard), build_defrag (row move), and the
|
|
// seq ops are metadata-only so kr_l rows stay matched to their cells (UPDATE 6).
|
|
ggml_tensor * llm_build_context::build_deepseek2_dsa_indexer(
|
|
ggml_cgraph * gf,
|
|
int il,
|
|
ggml_tensor * qr,
|
|
ggml_tensor * cur,
|
|
ggml_tensor * KQ_mask,
|
|
ggml_tensor * inp_pos) {
|
|
const auto & layer = model.layers[il];
|
|
|
|
const int64_t n_ihead = hparams.indexer_n_head;
|
|
const int64_t head_size = hparams.indexer_head_size;
|
|
const int64_t rope_dim = n_rot; // n_embd_head_qk_rope
|
|
const int64_t nope_dim = head_size - rope_dim;
|
|
|
|
// ---- indexer_q : {head_size * n_ihead, n_tokens} ----
|
|
ggml_tensor * indexer_q = ggml_mul_mat(ctx0, layer.indexer_attn_q_b, qr);
|
|
cb(indexer_q, "dsa_indexer_q", il);
|
|
|
|
// split rope/nope along dim0, per head
|
|
ggml_tensor * indexer_q_pe = ggml_view_3d(ctx0, indexer_q, rope_dim, n_ihead, n_tokens,
|
|
ggml_row_size(indexer_q->type, head_size),
|
|
ggml_row_size(indexer_q->type, head_size) * n_ihead, 0);
|
|
ggml_tensor * indexer_q_nope = ggml_view_3d(ctx0, indexer_q, nope_dim, n_ihead, n_tokens,
|
|
ggml_row_size(indexer_q->type, head_size),
|
|
ggml_row_size(indexer_q->type, head_size) * n_ihead,
|
|
ggml_row_size(indexer_q->type, rope_dim));
|
|
|
|
indexer_q_pe = ggml_rope_ext(ctx0, indexer_q_pe, inp_pos, nullptr, n_rot,
|
|
LLAMA_ROPE_TYPE_NEOX, n_ctx_orig, freq_base, freq_scale,
|
|
ext_factor, attn_factor, beta_fast, beta_slow);
|
|
|
|
// {head_size, n_ihead, n_tokens}
|
|
indexer_q = ggml_concat(ctx0, indexer_q_pe, indexer_q_nope, 0);
|
|
cb(indexer_q, "dsa_indexer_q_cat", il);
|
|
|
|
// ---- indexer_k : {head_size, n_tokens} (single key head, MQA) ----
|
|
ggml_tensor * indexer_k = ggml_mul_mat(ctx0, layer.indexer_attn_k, cur);
|
|
// LayerNorm (with weight + bias) over head_size
|
|
indexer_k = llm_build_norm(ctx0, indexer_k, hparams, layer.indexer_k_norm, layer.indexer_k_norm_b, LLM_NORM, cb, il);
|
|
cb(indexer_k, "dsa_indexer_k", il);
|
|
|
|
ggml_tensor * indexer_k_pe = ggml_view_3d(ctx0, indexer_k, rope_dim, 1, n_tokens,
|
|
ggml_row_size(indexer_k->type, head_size),
|
|
ggml_row_size(indexer_k->type, head_size), 0);
|
|
ggml_tensor * indexer_k_nope = ggml_view_3d(ctx0, indexer_k, nope_dim, 1, n_tokens,
|
|
ggml_row_size(indexer_k->type, head_size),
|
|
ggml_row_size(indexer_k->type, head_size),
|
|
ggml_row_size(indexer_k->type, rope_dim));
|
|
|
|
indexer_k_pe = ggml_rope_ext(ctx0, indexer_k_pe, inp_pos, nullptr, n_rot,
|
|
LLAMA_ROPE_TYPE_NEOX, n_ctx_orig, freq_base, freq_scale,
|
|
ext_factor, attn_factor, beta_fast, beta_slow);
|
|
|
|
// {head_size, 1, n_tokens}
|
|
indexer_k = ggml_concat(ctx0, indexer_k_pe, indexer_k_nope, 0);
|
|
cb(indexer_k, "dsa_indexer_k_cat", il);
|
|
|
|
// ---- Walsh-Hadamard rotation (score-preserving; improves cached-K F16 precision) ----
|
|
// nrot = largest power of 2 dividing head_size (== head_size for head_size = 128).
|
|
// DSA_HADAMARD_DISABLE: DEBUG-ONLY env knob (no CLI surface). Default = rotation enabled.
|
|
static const bool dsa_had_disable = getenv("DSA_HADAMARD_DISABLE") != nullptr;
|
|
if (lctx.cparams.dsa_indexer_hadamard && !dsa_had_disable) {
|
|
GGML_ASSERT((head_size & ~(head_size - 1)) == head_size);
|
|
indexer_q = ggml_hadamard(ctx0, indexer_q, head_size);
|
|
indexer_k = ggml_hadamard(ctx0, indexer_k, head_size);
|
|
}
|
|
|
|
// ---- write the batch's indexer keys into the persistent indexer-key cache at kv_head ----
|
|
// kr_l[il] is [head_size, kv_size] (F16, MQA single head). Store {head_size, n_tokens}.
|
|
ggml_tensor * kr_cache = kv_self.kr_l[il];
|
|
GGML_ASSERT(kr_cache && "DSA indexer key cache not allocated");
|
|
{
|
|
ggml_tensor * indexer_k_2d = ggml_reshape_2d(ctx0, indexer_k, head_size, n_tokens);
|
|
ggml_tensor * kr_view = ggml_view_2d(ctx0, kr_cache, head_size, n_tokens,
|
|
ggml_row_size(kr_cache->type, head_size),
|
|
ggml_row_size(kr_cache->type, head_size) * kv_head);
|
|
ggml_tensor * kr_cpy = ggml_cpy(ctx0, indexer_k_2d, kr_view);
|
|
// GRAPH-REUSE FIXUP REGISTRATION: the K/V cache_copies fixup in update_cache_copies()
|
|
// re-points the K/V cache writes to the current kv_head when a graph is reused, but it
|
|
// does NOT touch this indexer-key (kr_l) write, whose view bakes kv_head at build time.
|
|
// Under FA the cache pads to 256, so consecutive decode ubatches keep the SAME n_kv and
|
|
// can_reuse_graph() reuses the graph -- without this registration the kr_l write stays
|
|
// baked at the first ubatch's kv_head, so later ubatches never write their recent index
|
|
// keys (those cells read uninitialized -> block-max-pool/top-k drops the genuinely
|
|
// attended recent block -> degraded/NaN sparse-FA decode). Register it like K/V so
|
|
// update_cache_copies() patches view_offs = kv_head * step each reuse.
|
|
// step = one index-key row = head_size * F16 = kr_cache->nb[1].
|
|
if ((size_t) il < lctx.dsa_cache_copies.size()) {
|
|
lctx.dsa_cache_copies[il].cpy = kr_cpy;
|
|
lctx.dsa_cache_copies[il].step = kr_cache->nb[1];
|
|
}
|
|
ggml_build_forward_expand(gf, kr_cpy);
|
|
}
|
|
|
|
// ---- read back the full cached key set: {head_size, n_kv} ----
|
|
ggml_tensor * cached_k = ggml_view_2d(ctx0, kr_cache, head_size, n_kv,
|
|
ggml_row_size(kr_cache->type, head_size), 0);
|
|
cb(cached_k, "dsa_cached_k", il);
|
|
|
|
// ---- indexer weights : {n_ihead, n_tokens} ----
|
|
ggml_tensor * indexer_weights = ggml_mul_mat(ctx0, layer.indexer_proj, cur);
|
|
indexer_weights = ggml_scale(ctx0, indexer_weights, 1.0f / sqrtf(float(head_size * n_ihead)));
|
|
cb(indexer_weights, "dsa_indexer_weights", il);
|
|
|
|
// ---- scores ----
|
|
// indexer_q : {head_size, n_ihead, n_tokens} -> {head_size, n_tokens, n_ihead}
|
|
// cached_k : {head_size, n_kv} -> {head_size, n_kv, 1}; broadcasts over q's n_ihead dim.
|
|
ggml_tensor * indexer_k_b = ggml_reshape_3d(ctx0, cached_k, head_size, n_kv, 1);
|
|
|
|
ggml_tensor * indexer_score = ggml_view_2d(ctx0, KQ_mask, n_kv, n_tokens, KQ_mask->nb[1], 0);
|
|
if (indexer_score->type != GGML_TYPE_F32) {
|
|
indexer_score = ggml_cast(ctx0, indexer_score, GGML_TYPE_F32);
|
|
cb(indexer_score, "indexer_score_f32", il);
|
|
}
|
|
for (int head = 0; head < n_ihead; ++head) {
|
|
int il_cb = 1000*(il + 1) + head;
|
|
// [1, n_tokens]
|
|
auto w = ggml_cont(ctx0, ggml_view_2d(ctx0, indexer_weights, 1, indexer_weights->ne[1], indexer_weights->nb[1], indexer_weights->nb[0]*head));
|
|
cb(w, "iweights", il_cb);
|
|
// [head_size, n_tokens]
|
|
auto q = ggml_view_2d(ctx0, indexer_q, indexer_q->ne[0], indexer_q->ne[2], indexer_q->nb[2], indexer_q->nb[1]*head);
|
|
// [n_kv, n_tokens]
|
|
auto kq = ggml_mul_mat(ctx0, indexer_k_b, q);
|
|
cb(kq, "ikq", il_cb);
|
|
// [n_kv, n_tokens]
|
|
kq = ggml_relu(ctx0, kq);
|
|
cb(kq, "ikq_relu", il_cb);
|
|
// [n_kv, n_tokens]
|
|
auto score = ggml_mul(ctx0, kq, w);
|
|
cb(score, "score", il_cb);
|
|
indexer_score = ggml_add_inplace(ctx0, indexer_score, score);
|
|
cb(indexer_score, "indexer_score", il_cb);
|
|
ggml_build_forward_expand(gf, indexer_score);
|
|
}
|
|
|
|
//// {n_kv(keys), n_tokens(q), n_ihead} (k's head dim broadcasts over n_ihead)
|
|
//ggml_tensor * indexer_kq = ggml_mul_mat(ctx0, indexer_k_b, indexer_q);
|
|
//cb(indexer_kq, "dsa_indexer_kq", il);
|
|
|
|
//// -> {n_ihead, n_tokens(q), n_kv(keys)} for per-head weighting
|
|
//indexer_kq = ggml_cont(ctx0, ggml_permute(ctx0, indexer_kq, 2, 1, 0, 3));
|
|
|
|
//ggml_tensor * indexer_score = ggml_relu(ctx0, indexer_kq);
|
|
|
|
//// weights {n_ihead, n_tokens} -> {n_ihead, n_tokens, 1} broadcast over keys
|
|
//indexer_weights = ggml_reshape_3d(ctx0, indexer_weights, n_ihead, n_tokens, 1);
|
|
//indexer_score = ggml_mul(ctx0, indexer_score, indexer_weights);
|
|
|
|
//// sum over heads -> {1, n_tokens(q), n_kv(keys)}
|
|
//indexer_score = ggml_sum_rows(ctx0, indexer_score);
|
|
|
|
//// -> {n_kv(keys), n_tokens(q), 1}
|
|
//indexer_score = ggml_cont(ctx0, ggml_permute(ctx0, indexer_score, 2, 1, 0, 3));
|
|
//cb(indexer_score, "dsa_indexer_score", il);
|
|
|
|
//// add base causal mask over the n_kv keys: first n_tokens query columns of KQ_mask {n_kv, n_tokens_pad}.
|
|
//ggml_tensor * causal = ggml_view_2d(ctx0, KQ_mask, n_kv, n_tokens, KQ_mask->nb[1], 0);
|
|
//// Under -fa 1 the dense KQ_mask is F16; CPU ggml_add only supports F32+F16 when src0 is F16,
|
|
//// not F32(score)+F16(mask) (it aborts). Cast the causal mask view to F32 so the add is valid on
|
|
//// CPU. (CUDA add accepts mixed types, so this only bit the CPU build.)
|
|
//if (causal->type != GGML_TYPE_F32) causal = ggml_cast(ctx0, ggml_cont(ctx0, causal), GGML_TYPE_F32);
|
|
//indexer_score = ggml_add(ctx0, indexer_score, causal);
|
|
//cb(indexer_score, "dsa_indexer_score_masked", il);
|
|
|
|
// Attention-sink force-inclusion: add a finite positive boost to each query's OWN SEQUENCE's
|
|
// first n_sink present tokens so the sink token(s) always survive the top-k selection. Masking
|
|
// the sink collapses most transformers; a heavily-quantized (IQ2) indexer does not reliably rank
|
|
// it high on its own. The boost is finite, so it cannot un-mask future/causal -inf positions
|
|
// (-inf + boost = -inf).
|
|
//
|
|
// MULTI-SEQUENCE: the boost MUST be per-(key,query), not a global per-key vector. With several
|
|
// sequences packed into one ubatch (seq 0 at cache cells [0,n0), seq 1 at [n0,n1), ...), a global
|
|
// "key index < n_sink" boost only protects sequence 0's sink; sequence 1's sink (at cell n0, not
|
|
// cell 0) is left unprotected and gets dropped from top-k once the mask bites, collapsing it.
|
|
// We therefore use a per-graph input tensor inp_dsa_sink {n_kv, n_tokens} (filled on the CPU from
|
|
// kv_self.cells like the KQ_mask, in llama_set_inputs): inp_dsa_sink[j,i] = 1e20 iff key cell i
|
|
// belongs to query j's sequence AND its pos is within [min present pos of that seq, +n_sink).
|
|
//
|
|
// SERVING: the anchor is each sequence's FIRST PRESENT pos, not absolute pos < n_sink. After
|
|
// multi-turn seq_rm drops a sequence's early tokens its earliest survivor has pos >= n_sink; an
|
|
// absolute test would then protect nothing and let the (now-)sink be masked out. For a fresh
|
|
// sequence starting at pos 0, min(pos)==0 so the boosted set is exactly the old "cell pos <
|
|
// n_sink" set with the same 1e20 magnitude — n_seq==1 from pos 0 stays byte-identical.
|
|
// DSA_SINK: DEBUG-ONLY env knob (no CLI surface). Default = 1 (protect each sequence's first
|
|
// present token from being masked out of top-k). Must stay in sync with the two fill sites.
|
|
if (lctx.inp_dsa_sink) {
|
|
indexer_score = ggml_add(ctx0, indexer_score, lctx.inp_dsa_sink);
|
|
cb(indexer_score, "dsa_indexer_score_sink", il);
|
|
ggml_build_forward_expand(gf, indexer_score);
|
|
}
|
|
|
|
// FULL descending argsort of the per-query scores over the n_kv axis: {n_kv, n_tokens} (I32).
|
|
// We return the full ranking (not just the top-k view): the sparse-mask builder writes a value
|
|
// into EVERY key slot keyed by its rank, which avoids relying on ggml_set_rows preserving an
|
|
// uninitialized base for partially-written destinations (a CUDA in-place quirk that corrupted
|
|
// decode when n_kv > top_k).
|
|
//ggml_tensor * sorted = ggml_cont(ctx0, ggml_argsort(ctx0, indexer_score, GGML_SORT_ORDER_DESC));
|
|
//ggml_tensor * sorted = ggml_argsort(ctx0, indexer_score, GGML_SORT_ORDER_DESC);
|
|
ggml_tensor * sorted;
|
|
if (cparams.flash_attn) {
|
|
int64_t n_top_k = (int64_t) hparams.indexer_top_k;
|
|
if (lctx.cparams.dsa_top_k >= 0) n_top_k = lctx.cparams.dsa_top_k;
|
|
if (n_top_k > indexer_score->ne[0]) n_top_k = indexer_score->ne[0];
|
|
sorted = ggml_top_k(ctx0, indexer_score, n_top_k);
|
|
sorted = ggml_cont(ctx0, sorted);
|
|
} else {
|
|
sorted = ggml_argsort(ctx0, indexer_score, GGML_SORT_ORDER_DESC);
|
|
}
|
|
cb(sorted, "dsa_sorted", il);
|
|
|
|
return sorted;
|
|
}
|
|
|
|
// Build an additive sparse causal mask {n_kv, n_tok} (F32): 0 for the top-k highest-scoring keys
|
|
// per query, a large negative value for the rest, then add the base causal KQ_mask so future/
|
|
// padding keys stay masked. ggml_soft_max_ext only requires mask->ne[1] >= q n_tokens, and
|
|
// n_tok == n_tokens here, so no padding is needed.
|
|
//
|
|
// `sorted` is the FULL descending argsort of the indexer scores: {n_kv, n_tok} (I32), where
|
|
// sorted[rank, j] = key index with the rank-th highest score for query j. We scatter a rank-based
|
|
// penalty into EVERY key slot: pen(rank) = 0 if rank < n_top_k else -BIG. Because every key slot
|
|
// is written exactly once (sorted is a per-column permutation), the result does NOT depend on the
|
|
// scatter destination's initial contents — sidestepping the ggml in-place set_rows quirk where a
|
|
// partially-written CUDA destination keeps uninitialized (garbage) rows.
|
|
ggml_tensor * llm_build_context::build_deepseek2_dsa_sparse_mask(
|
|
ggml_tensor * sorted,
|
|
ggml_tensor * KQ_mask) {
|
|
const int64_t n_kv_local = KQ_mask->ne[0];
|
|
const int64_t n_tok = sorted->ne[1];
|
|
|
|
int64_t n_top_k = (int64_t) hparams.indexer_top_k;
|
|
// Tuning knob: --dsa-top-k (cparams.dsa_top_k) lets us vary the kept-key count to characterize
|
|
// selection quality. <0 means use the model's configured top_k. With the model's configured
|
|
// top_k (2048) on heavily-quantized (IQ2_M) weights the indexer currently under-ranks some
|
|
// critical keys; a near-n_kv value stays coherent.
|
|
if (lctx.cparams.dsa_top_k >= 0) n_top_k = lctx.cparams.dsa_top_k;
|
|
if (n_top_k > n_kv_local) n_top_k = n_kv_local;
|
|
|
|
// Penalty magnitude for non-top-k keys. On the soft_max (-fa 0) path this F32 -BIG is added to
|
|
// the score and softmaxed -> effectively -inf, while staying finite avoids -inf*0 = NaN. On the
|
|
// FA (-fa 1) path this mask is cast to F16 (build_deepseek2_dsa_fa_mask): 1e30 saturates to the
|
|
// F16 max (~6.5e4), which is still a large-enough negative bias to zero the key in the FA softmax
|
|
// (the dense FA mask uses -INFINITY/F16 -inf there; our finite-but-huge value is equivalent in
|
|
// effect and cannot produce NaN). So -BIG masks the key on BOTH paths.
|
|
const float BIG = 1e30f;
|
|
|
|
// rank-based penalty vector: pen[rank] = 0 for rank < n_top_k, else -BIG. {n_kv}
|
|
// sel = step(n_top_k - 0.5 - rank) = 1 for rank <= n_top_k-1, else 0
|
|
ggml_tensor * rank = ggml_arange(ctx0, 0.0f, (float) n_kv_local, 1.0f); // {n_kv} F32
|
|
ggml_tensor * sel = ggml_step(ctx0, ggml_scale_bias(ctx0, rank, -1.0f, (float) n_top_k - 0.5f));
|
|
ggml_tensor * pen = ggml_scale_bias(ctx0, sel, BIG, -BIG); // 0 or -BIG
|
|
|
|
// shape penalty to {1, n_kv, n_tok} (broadcast the per-rank value across all query columns)
|
|
pen = ggml_reshape_3d(ctx0, pen, 1, n_kv_local, 1);
|
|
ggml_tensor * pen_b = ggml_new_tensor_3d(ctx0, GGML_TYPE_F32, 1, n_kv_local, n_tok);
|
|
pen_b = ggml_repeat(ctx0, pen, pen_b); // {1, n_kv, n_tok}
|
|
|
|
// destination base {1, n_kv, n_tok} (contents irrelevant — fully overwritten by set_rows)
|
|
ggml_tensor * base = ggml_new_tensor_3d(ctx0, GGML_TYPE_F32, 1, n_kv_local, n_tok);
|
|
base = ggml_fill(ctx0, base, -BIG);
|
|
|
|
// indices: {n_kv, n_tok, 1}. scatter pen_b[:, rank, j] into base[:, sorted[rank,j], j].
|
|
ggml_tensor * idx = ggml_reshape_3d(ctx0, sorted, n_kv_local, n_tok, 1);
|
|
ggml_tensor * scattered = ggml_set_rows(ctx0, base, pen_b, idx);
|
|
|
|
// {n_kv, n_tok}
|
|
ggml_tensor * sparse = ggml_reshape_2d(ctx0, ggml_cont(ctx0, scattered), n_kv_local, n_tok);
|
|
|
|
// add base causal mask (first n_tok query columns) so future/padding keys stay masked
|
|
ggml_tensor * causal = ggml_view_2d(ctx0, KQ_mask, n_kv_local, n_tok, KQ_mask->nb[1], 0);
|
|
// see note in build_deepseek2_dsa_indexer: cast F16 (-fa 1) mask to F32 for the CPU add.
|
|
if (causal->type != GGML_TYPE_F32) causal = ggml_cast(ctx0, ggml_cont(ctx0, causal), GGML_TYPE_F32);
|
|
sparse = ggml_add(ctx0, sparse, causal);
|
|
cb(sparse, "dsa_sparse_mask", -1);
|
|
|
|
return sparse;
|
|
}
|
|
|
|
static ggml_tensor * build_deepseek2_dsa_fa_mask(const llama_context & lctx, ggml_context * ctx0, ggml_tensor * KQ_mask, ggml_tensor * sorted) {
|
|
GGML_ASSERT(KQ_mask && KQ_mask->type == GGML_TYPE_F16);
|
|
GGML_ASSERT(sorted && sorted->type == GGML_TYPE_I32);
|
|
GGML_ASSERT(KQ_mask->ne[1] >= sorted->ne[1]);
|
|
|
|
int n_top_k = (int64_t) lctx.model.hparams.indexer_top_k;
|
|
if (lctx.cparams.dsa_top_k >= 0) n_top_k = lctx.cparams.dsa_top_k;
|
|
|
|
int n_kv_local = KQ_mask->ne[0];
|
|
if (n_top_k >= n_kv_local) {
|
|
return KQ_mask;
|
|
}
|
|
|
|
GGML_ASSERT(sorted->ne[1] == lctx.inp_mask_inf->ne[1]);
|
|
auto top_k = ggml_view_2d(ctx0, sorted, n_top_k, sorted->ne[1], sorted->nb[1], 0);
|
|
auto mask32 = ggml_blend(ctx0, lctx.inp_mask_inf, top_k, 0.0f);
|
|
if (KQ_mask->ne[1] == mask32->ne[1]) {
|
|
auto mask16 = ggml_add(ctx0, KQ_mask, mask32);
|
|
return mask16;
|
|
}
|
|
auto kq1 = ggml_view_2d(ctx0, KQ_mask, KQ_mask->ne[0], mask32->ne[1], KQ_mask->nb[1], 0);
|
|
auto kq2 = ggml_view_2d(ctx0, KQ_mask, KQ_mask->ne[0], KQ_mask->ne[1] - mask32->ne[1], KQ_mask->nb[1], mask32->ne[1]*KQ_mask->nb[1]);
|
|
kq1 = ggml_add(ctx0, kq1, mask32);
|
|
auto mask16 = ggml_concat(ctx0, kq1, kq2, 1);
|
|
return mask16;
|
|
}
|
|
|
|
|
|
// Adapt the (F32, unpadded {n_kv, n_tokens}) sparse mask for ggml_flash_attn_ext, which on this fork
|
|
// requires the mask to be F16, contiguous, and padded in ne[1] to GGML_PAD(n_queries, GGML_KQ_MASK_PAD)
|
|
// (build_inp_KQ_mask creates the dense -fa 1 mask exactly that way). We:
|
|
// 1) cast the sparse mask to F16,
|
|
// 2) concat the dense FA mask's padding rows [n_tok, n_pad) (already F16, causal -inf for the
|
|
// non-existent padded queries) onto the bottom so ne[1] matches the dense mask,
|
|
// 3) ggml_cont so the result is contiguous (the FA assert requires it).
|
|
// The padded rows feed only the discarded outputs of padded query slots, so reusing the dense mask's
|
|
// padding region is both correct and the cheapest way to get the exact dense shape.
|
|
ggml_tensor * llm_build_context::build_deepseek2_dsa_fa_mask(
|
|
ggml_tensor * sparse,
|
|
ggml_tensor * KQ_mask) {
|
|
const int64_t n_kv_local = KQ_mask->ne[0];
|
|
const int64_t n_tok = sparse->ne[1];
|
|
const int64_t n_pad = KQ_mask->ne[1]; // GGML_PAD(n_tokens, GGML_KQ_MASK_PAD)
|
|
|
|
GGML_ASSERT(KQ_mask->type == GGML_TYPE_F16 && "FA dense KQ_mask expected F16 on -fa 1");
|
|
|
|
ggml_tensor * fa_mask;
|
|
if (n_pad > n_tok) {
|
|
// dense padding rows: KQ_mask columns [n_tok, n_pad) -> {n_kv, n_pad - n_tok} (F16 view)
|
|
ggml_tensor * pad = ggml_view_2d(ctx0, KQ_mask, n_kv_local, n_pad - n_tok,
|
|
KQ_mask->nb[1], KQ_mask->nb[1] * n_tok);
|
|
// CPU ggml_concat only supports F16 along dim 0 (concat_any); the dim-1 row concat must be
|
|
// done in F32 (concat_f32 handles all dims), then cast the padded result to F16. On CUDA the
|
|
// F16 dim-1 concat is supported, so this path only needed adapting for the CPU build.
|
|
ggml_tensor * pad_f32 = ggml_cast(ctx0, ggml_cont(ctx0, pad), GGML_TYPE_F32);
|
|
ggml_tensor * fa_f32 = ggml_concat(ctx0, sparse, pad_f32, 1); // {n_kv, n_pad} F32
|
|
fa_mask = ggml_cast(ctx0, fa_f32, GGML_TYPE_F16); // {n_kv, n_pad} F16
|
|
} else {
|
|
fa_mask = ggml_cast(ctx0, sparse, GGML_TYPE_F16);
|
|
}
|
|
fa_mask = ggml_cont(ctx0, fa_mask);
|
|
cb(fa_mask, "dsa_fa_mask", -1);
|
|
return fa_mask;
|
|
}
|
|
|
|
// Layer-mode attention path (non-TP). Mirrors build_deepseek2_tp_attention's interface.
|
|
ggml_tensor * llm_build_context::build_deepseek2_layer_attention(
|
|
ggml_cgraph * gf, int il,
|
|
ggml_tensor * inpL,
|
|
ggml_tensor * KQ_mask, ggml_tensor * inp_pos,
|
|
ggml_tensor * rope_cache,
|
|
float kq_scale, float attn_factor_scaled,
|
|
bool use_f32_attn_precision,
|
|
bool is_lite,
|
|
bool pp_opt) {
|
|
const uint32_t n_embd_head_qk_rope = hparams.n_rot;
|
|
const uint32_t n_embd_head_qk_nope = hparams.n_embd_head_k(0) - hparams.n_rot;
|
|
const uint32_t kv_lora_rank = hparams.n_lora_kv;
|
|
const uint32_t q_lora_rank = hparams.n_lora_q;
|
|
ggml_tensor * cur;
|
|
|
|
// norm
|
|
cur = llm_build_norm(ctx0, inpL, hparams, model.layers[il].attn_norm, NULL, LLM_NORM_RMS, cb, il);
|
|
cb(cur, "attn_norm", il);
|
|
|
|
// DSA lightning indexer (GLM-5.2 / DeepSeek-V3.2). Built below from the q_lora latent
|
|
// and used to construct a sparse causal mask. Defaults to the dense KQ_mask.
|
|
// - sparse_mask : F32 additive sparse mask for the soft_max (-fa 0) path.
|
|
// - sparse_mask_fa : F16, padded variant for the ggml_flash_attn_ext (-fa 1) path.
|
|
// Both default to the dense KQ_mask so non-DSA / disabled builds are unchanged.
|
|
ggml_tensor * sparse_mask = KQ_mask;
|
|
ggml_tensor * sparse_mask_fa = KQ_mask;
|
|
//ggml_tensor * top_k = nullptr;
|
|
//(void) top_k; // captured for potential reuse/debug; only the masks are consumed downstream
|
|
|
|
// self_attention
|
|
{
|
|
ggml_tensor * q = nullptr;
|
|
ggml_tensor * kv_rope_compressed = nullptr;
|
|
ggml_tensor * q_rope;
|
|
ggml_tensor * q_nope;
|
|
ggml_tensor * k_rope;
|
|
ggml_tensor * kv_compressed;
|
|
if (model.layers[il].wkq_a_mqa) {
|
|
auto mqa = ggml_mul_mat(ctx0, model.layers[il].wkq_a_mqa, cur);
|
|
cb(mqa, "mqa", il);
|
|
size_t qnb1;
|
|
if (!is_lite) {
|
|
q = ggml_view_2d(ctx0, mqa, q_lora_rank, n_tokens, mqa->nb[1], 0);
|
|
q = llm_build_norm(ctx0, q, hparams, model.layers[il].attn_q_a_norm, NULL, LLM_NORM_RMS, cb, il);
|
|
q = ggml_mul_mat(ctx0, model.layers[il].wq_b, q);
|
|
qnb1 = q->nb[1];
|
|
cb(q, "q", il);
|
|
kv_rope_compressed = ggml_view_2d(ctx0, mqa, kv_lora_rank + n_embd_head_qk_rope, n_tokens, mqa->nb[1],
|
|
q_lora_rank*ggml_element_size(mqa));
|
|
} else {
|
|
q = ggml_view_2d(ctx0, mqa, n_embd_k_gqa, n_tokens, mqa->nb[1], 0);
|
|
kv_rope_compressed = ggml_view_2d(ctx0, mqa, kv_lora_rank + n_embd_head_qk_rope, n_tokens, mqa->nb[1],
|
|
n_embd_k_gqa*ggml_element_size(mqa));
|
|
qnb1 = mqa->nb[1];
|
|
}
|
|
q_nope = ggml_view_3d(ctx0, q, n_embd_head_qk_nope, n_head, n_tokens,
|
|
ggml_row_size(q->type, hparams.n_embd_head_k(il)), qnb1, 0);
|
|
q_rope = ggml_view_3d(ctx0, q, n_embd_head_qk_rope, n_head, n_tokens,
|
|
ggml_row_size(q->type, hparams.n_embd_head_k(il)), qnb1, ggml_row_size(q->type, n_embd_head_qk_nope));
|
|
k_rope = ggml_view_3d(ctx0, kv_rope_compressed, n_embd_head_qk_rope, 1, n_tokens,
|
|
mqa->nb[1], mqa->nb[1], ggml_row_size(kv_rope_compressed->type, kv_lora_rank));
|
|
kv_compressed = ggml_view_2d(ctx0, kv_rope_compressed, kv_lora_rank, n_tokens, mqa->nb[1], 0);
|
|
}
|
|
else {
|
|
if (!is_lite) {
|
|
q = ggml_mul_mat(ctx0, model.layers[il].wq_a, cur);
|
|
cb(q, "q", il);
|
|
|
|
kv_rope_compressed = ggml_mul_mat(ctx0, model.layers[il].wkv_a_mqa, cur);
|
|
cb(kv_rope_compressed, "kv_rope_compressed", il);
|
|
|
|
ggml_build_forward_expand(gf, q);
|
|
ggml_build_forward_expand(gf, kv_rope_compressed);
|
|
|
|
q = llm_build_norm(ctx0, q, hparams, model.layers[il].attn_q_a_norm, NULL, LLM_NORM_RMS, cb, il);
|
|
cb(q, "q", il);
|
|
|
|
// DSA lightning indexer (cache-backed): score the q_lora latent against the persistent
|
|
// indexer-key cache over the full n_kv, then build a sparse top-k causal mask. Correct
|
|
// for prefill AND decode (single sequence). Gate: --dsa opt-in (off by default) +
|
|
// GLM_DSA arch + indexer tensors + cache. When off, the model runs the dense MLA path,
|
|
// byte-identical to a build without this feature.
|
|
if (lctx.cparams.dsa && model.arch == LLM_ARCH_GLM_DSA && model.layers[il].indexer_attn_q_b
|
|
&& kv_self.kr_l.size() > (size_t) il && kv_self.kr_l[il]) {
|
|
// GLM-5.2 IndexShare: "full" layers compute their own lightning-indexer top-k;
|
|
// "shared" layers reuse the previous full layer's top-k (transformers reference:
|
|
// shared layer indexer=None, topk_indices=prev_topk_indices). The full/shared map is
|
|
// hparams.indexer_is_full (GGUF metadata or derived config rule). At a given step all
|
|
// layers share the same n_kv/n_tokens, so a full layer's argsort is valid to reuse.
|
|
ggml_tensor * sorted;
|
|
if (hparams.indexer_is_full[il] || dsa_last_full_sorted == nullptr) {
|
|
ggml_tensor * qr = q; // q_lora latent (after attn_q_a_norm, before wq_b)
|
|
sorted = build_deepseek2_dsa_indexer(gf, il, qr, cur, KQ_mask, inp_pos);
|
|
dsa_last_full_sorted = sorted;
|
|
} else {
|
|
sorted = dsa_last_full_sorted;
|
|
}
|
|
if (lctx.cparams.flash_attn) {
|
|
sparse_mask_fa = ::build_deepseek2_dsa_fa_mask(lctx, ctx0, KQ_mask, sorted);
|
|
} else {
|
|
sparse_mask = build_deepseek2_dsa_sparse_mask(sorted, KQ_mask);
|
|
}
|
|
//// For the FA path the mask must be F16 + padded; build it from the F32 sparse mask.
|
|
//if (lctx.cparams.flash_attn) {
|
|
// sparse_mask_fa = build_deepseek2_dsa_fa_mask(sparse_mask, KQ_mask);
|
|
//}
|
|
//top_k = sorted;
|
|
}
|
|
|
|
q = ggml_mul_mat(ctx0, model.layers[il].wq_b, q);
|
|
cb(q, "q", il);
|
|
} else {
|
|
q = ggml_mul_mat(ctx0, model.layers[il].wq, cur);
|
|
cb(q, "q", il);
|
|
|
|
kv_rope_compressed = ggml_mul_mat(ctx0, model.layers[il].wkv_a_mqa, cur);
|
|
cb(kv_rope_compressed, "kv_rope_compressed", il);
|
|
|
|
ggml_build_forward_expand(gf, q);
|
|
ggml_build_forward_expand(gf, kv_rope_compressed);
|
|
}
|
|
|
|
q_nope = ggml_view_3d(ctx0, q, n_embd_head_qk_nope, n_head, n_tokens,
|
|
ggml_row_size(q->type, hparams.n_embd_head_k(il)),
|
|
ggml_row_size(q->type, hparams.n_embd_head_k(il) * n_head), 0);
|
|
|
|
q_rope = ggml_view_3d(ctx0, q, n_embd_head_qk_rope, n_head, n_tokens,
|
|
ggml_row_size(q->type, hparams.n_embd_head_k(il)),
|
|
ggml_row_size(q->type, hparams.n_embd_head_k(il) * n_head),
|
|
ggml_row_size(q->type, n_embd_head_qk_nope));
|
|
|
|
k_rope = ggml_view_3d(ctx0, kv_rope_compressed, n_embd_head_qk_rope, 1, n_tokens,
|
|
kv_rope_compressed->nb[1],
|
|
kv_rope_compressed->nb[1],
|
|
ggml_row_size(kv_rope_compressed->type, kv_lora_rank));
|
|
|
|
kv_compressed = ggml_view_2d(ctx0, kv_rope_compressed, kv_lora_rank, n_tokens,
|
|
kv_rope_compressed->nb[1], 0);
|
|
}
|
|
cb(q_nope, "q_nope", il);
|
|
cb(q_rope, "q_rope", il);
|
|
cb(k_rope, "k_rope", il);
|
|
cb(kv_compressed, "kv_compressed", il);
|
|
|
|
ggml_build_forward_expand(gf, q_rope);
|
|
ggml_build_forward_expand(gf, k_rope);
|
|
if (rope_cache) {
|
|
q_rope = ggml_rope_fast(ctx0, q_rope, rope_cache);
|
|
k_rope = ggml_rope_fast(ctx0, k_rope, rope_cache);
|
|
} else {
|
|
q_rope = ggml_rope_ext(ctx0, q_rope, inp_pos, nullptr, n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
|
|
ext_factor, attn_factor_scaled, beta_fast, beta_slow);
|
|
|
|
k_rope = ggml_rope_ext(ctx0, k_rope, inp_pos, nullptr, n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
|
|
ext_factor, attn_factor_scaled, beta_fast, beta_slow);
|
|
}
|
|
cb(q_rope, "q_rope", il);
|
|
cb(k_rope, "k_rope", il);
|
|
ggml_build_forward_expand(gf, q_rope);
|
|
ggml_build_forward_expand(gf, k_rope);
|
|
|
|
kv_compressed = llm_build_norm(ctx0, kv_compressed, hparams, model.layers[il].attn_kv_a_norm, NULL, LLM_NORM_RMS, cb, il);
|
|
cb(kv_compressed, "kv_compressed", il);
|
|
|
|
if (lctx.cparams.mla_attn) {
|
|
|
|
ggml_tensor * kv_cache_trans = nullptr;
|
|
|
|
if (lctx.cparams.mla_attn == 1 && !lctx.cparams.flash_attn) {
|
|
ggml_tensor * kv_cache_trans_view = ggml_view_2d(ctx0, kv_self.v_l[il], n_tokens, kv_lora_rank,
|
|
ggml_row_size(kv_self.v_l[il]->type, kv_self.size), ggml_row_size(kv_self.v_l[il]->type, kv_head));
|
|
cb(kv_cache_trans_view, "kv_cache_trans_view", il);
|
|
|
|
// note: storing transposed c^KV in the transposed KV cache
|
|
ggml_build_forward_expand(gf, ggml_cpy(ctx0, ggml_transpose(ctx0, kv_compressed), kv_cache_trans_view));
|
|
|
|
kv_cache_trans = ggml_view_2d(ctx0, kv_self.v_l[il],
|
|
n_kv, kv_lora_rank,
|
|
ggml_row_size(kv_self.v_l[il]->type, kv_self.size),
|
|
0);
|
|
cb(kv_cache_trans, "kv_cache_trans", il);
|
|
}
|
|
|
|
//ggml_tensor * kvr = ggml_concat(ctx0, kv_compressed, ggml_permute(ctx0, k_rope, 0, 2, 1, 3), 0);
|
|
ggml_tensor * kvr = ggml_concat(ctx0, ggml_permute(ctx0, k_rope, 0, 2, 1, 3), kv_compressed, 0);
|
|
cb(kvr, "kvr", il);
|
|
|
|
auto row_size = ggml_row_size(kv_self.k_l[il]->type, kv_lora_rank + n_embd_head_qk_rope);
|
|
ggml_tensor * kv_cache_view = ggml_view_2d(ctx0, kv_self.k_l[il], kv_self.k_l[il]->ne[0], n_tokens,
|
|
row_size, row_size*kv_head);
|
|
lctx.cache_copies[2*il+0].cpy = ggml_cpy(ctx0, kvr, kv_cache_view);
|
|
lctx.cache_copies[2*il+0].step = row_size;
|
|
ggml_build_forward_expand(gf, lctx.cache_copies[2*il+0].cpy);
|
|
ggml_tensor * kv_cache = ggml_view_2d(ctx0, kv_self.k_l[il],
|
|
kv_lora_rank + n_embd_head_qk_rope, n_kv,
|
|
ggml_row_size(kv_self.k_l[il]->type, kv_lora_rank + n_embd_head_qk_rope), 0);
|
|
cb(kv_cache, "kv_cache", il);
|
|
|
|
ggml_tensor * kqv;
|
|
|
|
if (lctx.cparams.mla_attn > 1 && lctx.cparams.flash_attn && pp_opt) { // PP for mla=2,3
|
|
|
|
auto kv_cache_nope = ggml_view_2d(ctx0, kv_self.k_l[il], kv_lora_rank, n_kv, kv_self.k_l[il]->nb[1],
|
|
ggml_row_size(kv_self.k_l[il]->type, n_embd_head_qk_rope));
|
|
|
|
auto kv_f32_size = model.layers[il].wkv_b->ne[1] * kv_cache_nope->ne[1] * sizeof(float) / (1024*1024);
|
|
int n_max_head = n_head;
|
|
if (cparams.attn_max_batch > 0 && kv_f32_size > cparams.attn_max_batch) {
|
|
n_max_head = 1;
|
|
for (int niter = 2; niter < n_head; ++niter) {
|
|
if (n_head % niter == 0 && kv_f32_size/niter <= cparams.attn_max_batch) {
|
|
n_max_head = n_head/niter;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
GGML_ASSERT(n_head % n_max_head == 0);
|
|
|
|
auto n_per_head = model.layers[il].wkv_b->ne[1] / n_head;
|
|
|
|
auto kv_cache_rope = ggml_view_3d(ctx0, kv_self.k_l[il], n_embd_head_qk_rope, n_kv, 1,
|
|
kv_self.k_l[il]->nb[1], kv_self.k_l[il]->nb[2], 0); //ggml_row_size(kv_self.k_l[il]->type, kv_lora_rank));
|
|
|
|
// There is still an issue with one or more of the ops GGML_OP_REPEAT, GGML_OP_CONCAT, GGML_OP_CPY on CUDA when
|
|
// the KV cache is quantized. Hence, in that case we will simply use fp16 for now.
|
|
// The downside of the following line is that fp16 will be used even if attention is computed on the CPU
|
|
// if the build is with CUDA enabled.
|
|
auto kv_type = lctx.backends.size() == 1 && lctx.backends.front() == lctx.backend_cpu ? kv_self.k_l[il]->type : GGML_TYPE_F16;
|
|
|
|
ggml_tensor repeater;
|
|
repeater.ne[0] = n_embd_head_qk_rope; repeater.ne[1] = n_kv; repeater.ne[2] = n_max_head; repeater.ne[3] = 1;
|
|
ggml_tensor * k_rope;
|
|
if (kv_cache_rope->type == kv_type) {
|
|
k_rope = ggml_repeat(ctx0, kv_cache_rope, &repeater);
|
|
} else {
|
|
auto kv_cache_rope_f16 = ggml_cast(ctx0, kv_cache_rope, GGML_TYPE_F16);
|
|
k_rope = ggml_repeat(ctx0, kv_cache_rope_f16, &repeater);
|
|
}
|
|
cb(k_rope, "k_rope", il);
|
|
|
|
//auto q = ggml_concat(ctx0, q_nope, q_rope, 0);
|
|
auto q = ggml_concat(ctx0, q_rope, q_nope, 0);
|
|
q = ggml_permute(ctx0, q, 0, 2, 1, 3);
|
|
cb(q, "q_concat", il);
|
|
|
|
ggml_build_forward_expand(gf, q);
|
|
|
|
for (int iter = 0; iter < n_head/n_max_head; ++iter) {
|
|
|
|
auto wkv_b = ggml_view_2d(ctx0, model.layers[il].wkv_b, model.layers[il].wkv_b->ne[0], n_per_head*n_max_head,
|
|
model.layers[il].wkv_b->nb[1], model.layers[il].wkv_b->nb[1]*n_per_head*n_max_head*iter);
|
|
|
|
auto kv_f32 = ggml_mul_mat(ctx0, wkv_b, kv_cache_nope);
|
|
cb(kv_f32, "kv_f32", il);
|
|
|
|
auto v_f32 = ggml_view_3d(ctx0, kv_f32, hparams.n_embd_head_v_full, n_kv, n_max_head,
|
|
ggml_row_size(kv_f32->type, n_max_head * (n_embd_head_qk_nope + hparams.n_embd_head_v_full)),
|
|
ggml_row_size(kv_f32->type, n_embd_head_qk_nope + hparams.n_embd_head_v_full),
|
|
ggml_row_size(kv_f32->type, n_embd_head_qk_nope));
|
|
cb(v_f32, "v_f32", il);
|
|
|
|
auto k_nope_f32 = ggml_view_3d(ctx0, kv_f32, n_embd_head_qk_nope, n_kv, n_max_head,
|
|
ggml_row_size(kv_f32->type, n_max_head * (n_embd_head_qk_nope + hparams.n_embd_head_v_full)),
|
|
ggml_row_size(kv_f32->type, n_embd_head_qk_nope + hparams.n_embd_head_v_full), 0);
|
|
cb(k_nope_f32, "k_nope_f32", il);
|
|
|
|
auto v = ggml_cast(ctx0, v_f32, kv_type);
|
|
cb(v, "v", il);
|
|
|
|
auto k_nope = ggml_cast(ctx0, k_nope_f32, kv_type);
|
|
cb(k_nope, "k_nope", il);
|
|
|
|
ggml_build_forward_expand(gf, k_nope);
|
|
ggml_build_forward_expand(gf, v);
|
|
|
|
//auto k = ggml_concat(ctx0, k_nope, k_rope, 0);
|
|
auto k = ggml_concat(ctx0, k_rope, k_nope, 0);
|
|
cb(k, "k", il);
|
|
|
|
ggml_build_forward_expand(gf, k);
|
|
|
|
auto q_iter = ggml_view_3d(ctx0, q, q->ne[0], q->ne[1], n_max_head,
|
|
q->nb[1], q->nb[2], q->nb[2]*n_max_head*iter);
|
|
|
|
kqv = ggml_flash_attn_ext(ctx0, q_iter, k, v, sparse_mask_fa, kq_scale, hparams.f_max_alibi_bias, 0.f);
|
|
if (use_f32_attn_precision || q->ne[1] <= 8) {
|
|
ggml_flash_attn_ext_set_prec(kqv, GGML_PREC_F32);
|
|
}
|
|
cb(kqv, "kqv", il);
|
|
|
|
if (iter == 0) {
|
|
cur = ggml_reshape_2d(ctx0, kqv, n_embd_head_v*n_max_head, n_tokens);
|
|
} else {
|
|
cur = ggml_concat(ctx0, cur, ggml_reshape_2d(ctx0, kqv, n_embd_head_v*n_max_head, n_tokens), 0);
|
|
}
|
|
ggml_build_forward_expand(gf, cur);
|
|
|
|
}
|
|
|
|
}
|
|
else {
|
|
|
|
ggml_tensor * kqv_compressed = nullptr;
|
|
|
|
//auto wkv_b = model.layers[il].wkv_b;
|
|
auto wk_b = model.layers[il].wk_b->ne[1] == kv_lora_rank ? model.layers[il].wk_b
|
|
: ggml_reshape_3d(ctx0, model.layers[il].wk_b, n_embd_head_qk_nope, kv_lora_rank, n_head);
|
|
|
|
q_nope = ggml_permute(ctx0, q_nope, 0, 2, 1, 3);
|
|
cb(q_nope, "q_nope_perm", il);
|
|
|
|
struct ggml_tensor * q_nope2 = ggml_mul_mat(ctx0, wk_b, q_nope);
|
|
cb(q_nope2, "q_nope2", il);
|
|
|
|
//ggml_tensor * q = ggml_concat(ctx0, q_nope2, ggml_permute(ctx0, q_rope, 0, 2, 1, 3), 0);
|
|
ggml_tensor * q = ggml_concat(ctx0, ggml_permute(ctx0, q_rope, 0, 2, 1, 3), q_nope2, 0);
|
|
cb(q, "q", il);
|
|
|
|
if (lctx.cparams.flash_attn && (lctx.cparams.mla_attn == 1 || lctx.cparams.mla_attn == 3)) {
|
|
ggml_tensor * kv_cache_lora = ggml_view_2d(ctx0, kv_self.k_l[il],
|
|
kv_lora_rank, n_kv,
|
|
ggml_row_size(kv_self.k_l[il]->type, kv_lora_rank + n_embd_head_qk_rope),
|
|
ggml_row_size(kv_self.k_l[il]->type, n_embd_head_qk_rope));
|
|
cb(kv_cache_lora, "kv_cache_lora", il);
|
|
|
|
kqv_compressed = ggml_flash_attn_ext(ctx0, q, kv_cache, kv_cache_lora, sparse_mask_fa, kq_scale, hparams.f_max_alibi_bias, 0.f);
|
|
cb(kqv_compressed, "kqv_compressed", il);
|
|
|
|
if (use_f32_attn_precision) {
|
|
ggml_flash_attn_ext_set_prec(kqv_compressed, GGML_PREC_F32);
|
|
}
|
|
|
|
kqv_compressed = ggml_permute(ctx0, kqv_compressed, 0, 2, 1, 3);
|
|
cb(kqv_compressed, "kqv_compressed_perm", il);
|
|
}
|
|
else {
|
|
if (lctx.cparams.mla_attn > 1) {
|
|
ggml_tensor * kv_cache_lora = ggml_view_2d(ctx0, kv_self.k_l[il],
|
|
kv_lora_rank, n_kv,
|
|
ggml_row_size(kv_self.k_l[il]->type, kv_lora_rank + n_embd_head_qk_rope),
|
|
ggml_row_size(kv_self.k_l[il]->type, n_embd_head_qk_rope));
|
|
cb(kv_cache, "kv_cache_lora", il);
|
|
|
|
kv_cache_trans = ggml_cont(ctx0, ggml_transpose(ctx0, kv_cache_lora));
|
|
cb(kv_cache_trans, "kv_cache_trans", il);
|
|
}
|
|
|
|
auto kq_size = kv_cache->ne[1]*q->ne[1]*q->ne[2]*sizeof(float)/(1024*1024); // K*Q in MiB
|
|
if (lctx.cparams.attn_max_batch <= 0 || lctx.cparams.attn_max_batch >= kq_size) {
|
|
if (!pp_opt) {
|
|
q = ggml_permute(ctx0, q, 0, 2, 1, 3);
|
|
cb(q, "q_perm", il);
|
|
}
|
|
|
|
ggml_tensor * kq = ggml_mul_mat(ctx0, kv_cache, q);
|
|
if (kv_cache->ne[1] < 256) {
|
|
ggml_mul_mat_set_prec(kq, GGML_PREC_F32);
|
|
}
|
|
cb(kq, "kq", il);
|
|
|
|
if (!pp_opt) {
|
|
kq = ggml_cont(ctx0, ggml_permute(ctx0, kq, 0, 2, 1, 3));
|
|
cb(kq, "kq_perm", il);
|
|
}
|
|
|
|
kq = ggml_soft_max_ext(ctx0, kq, sparse_mask, kq_scale, hparams.f_max_alibi_bias);
|
|
cb(kq, "kq_soft_max_ext", il);
|
|
|
|
if (!pp_opt) {
|
|
kq = ggml_permute(ctx0, kq, 0, 2, 1, 3);
|
|
cb(kq, "kq_soft_max_ext_perm", il);
|
|
}
|
|
|
|
kqv_compressed = ggml_mul_mat(ctx0, kv_cache_trans, kq);
|
|
cb(kqv_compressed, "kqv_compressed", il);
|
|
|
|
if (!pp_opt) {
|
|
kqv_compressed = ggml_permute(ctx0, kqv_compressed, 0, 2, 1, 3);
|
|
cb(kqv_compressed, "kqv_compressed_perm", il);
|
|
}
|
|
|
|
} else {
|
|
|
|
int n_step = (kq_size + lctx.cparams.attn_max_batch - 1)/lctx.cparams.attn_max_batch;
|
|
n_step = std::min(n_step, int(q->ne[2]));
|
|
int n_per_step = (q->ne[2] + n_step - 1)/n_step;
|
|
|
|
for (int i_head = 0; i_head < q->ne[2]; i_head += n_per_step) {
|
|
int this_ne12 = i_head + n_per_step <= q->ne[2] ? n_per_step : q->ne[2] - i_head;
|
|
ggml_tensor * q_i = ggml_view_3d(ctx0, q, q->ne[0], q->ne[1], this_ne12, q->nb[1], q->nb[2], q->nb[2]*i_head);
|
|
ggml_tensor * kq_i = ggml_mul_mat(ctx0, kv_cache, q_i);
|
|
kq_i = ggml_soft_max_ext(ctx0, kq_i, sparse_mask, kq_scale, hparams.f_max_alibi_bias);
|
|
ggml_tensor * kqv_i = ggml_mul_mat(ctx0, kv_cache_trans, kq_i);
|
|
if (i_head == 0) {
|
|
kqv_compressed = kqv_i;
|
|
} else {
|
|
kqv_compressed = ggml_concat(ctx0, kqv_compressed, kqv_i, 2);
|
|
}
|
|
ggml_build_forward_expand(gf, kqv_compressed);
|
|
}
|
|
cb(kqv_compressed, "kqv_compressed", il);
|
|
}
|
|
}
|
|
|
|
auto wv_b = model.layers[il].wv_b;
|
|
if (wv_b->ne[1] != n_embd_head_v) {
|
|
wv_b = ggml_reshape_3d(ctx0, wv_b, kv_lora_rank, n_embd_head_v, n_head);
|
|
cb(wv_b, "wv_b", il);
|
|
}
|
|
// There is an issue with quantized GEMV on CUDA when the left operand (the matrix) is
|
|
// not contiguous. So, for now, we create wv_b during model loading and use that
|
|
// instead of the commented out 3D view below.
|
|
//auto wv_b = ggml_view_3d(ctx0, wkv_b, kv_lora_rank, n_embd_head_v, n_head,
|
|
// wkv_b->nb[1], wkv_b->nb[1]*(n_embd_head_v + n_embd_head_qk_nope),
|
|
// wkv_b->nb[1]*n_embd_head_qk_nope);
|
|
//cb(wv_b, "wv_b", il);
|
|
|
|
kqv = ggml_mul_mat(ctx0, wv_b, kqv_compressed);
|
|
cb(kqv, "kqv", il);
|
|
|
|
if (n_tokens > 1) {
|
|
kqv = ggml_cont(ctx0, ggml_permute(ctx0, kqv, 0, 2, 1, 3));
|
|
cb(kqv, "kqv_perm", il);
|
|
}
|
|
cur = ggml_reshape_2d(ctx0, kqv, n_embd_head_v*n_head, n_tokens);
|
|
cb(cur, "kqv_2d", il);
|
|
|
|
}
|
|
|
|
ggml_build_forward_expand(gf, cur);
|
|
|
|
cur = llm_build_lora_mm(lctx, ctx0, model.layers[il].wo, cur);
|
|
cb(cur, "kqv_out", il);
|
|
|
|
}
|
|
else {
|
|
|
|
// {kv_lora_rank, n_head * (n_embd_head_qk_nope + n_embd_head_v)} * {kv_lora_rank, n_tokens} -> {n_head * (n_embd_head_qk_nope + n_embd_head_v), n_tokens}
|
|
struct ggml_tensor * kv = ggml_mul_mat(ctx0, model.layers[il].wkv_b, kv_compressed);
|
|
cb(kv, "kv", il);
|
|
|
|
// split into {n_head * n_embd_head_qk_nope, n_tokens}
|
|
struct ggml_tensor * k_nope = ggml_view_3d(ctx0, kv, n_embd_head_qk_nope, n_head, n_tokens,
|
|
ggml_row_size(kv->type, n_embd_head_qk_nope + hparams.n_embd_head_v_full),
|
|
ggml_row_size(kv->type, n_head * (n_embd_head_qk_nope + hparams.n_embd_head_v_full)),
|
|
0);
|
|
cb(k_nope, "k_nope", il);
|
|
|
|
// and {n_head * n_embd_head_v, n_tokens}
|
|
struct ggml_tensor * v_states = ggml_view_3d(ctx0, kv, hparams.n_embd_head_v_full, n_head, n_tokens,
|
|
ggml_row_size(kv->type, (n_embd_head_qk_nope + hparams.n_embd_head_v_full)),
|
|
ggml_row_size(kv->type, (n_embd_head_qk_nope + hparams.n_embd_head_v_full)*n_head),
|
|
ggml_row_size(kv->type, (n_embd_head_qk_nope)));
|
|
cb(v_states, "v_states", il);
|
|
|
|
v_states = ggml_cont(ctx0, v_states);
|
|
cb(v_states, "v_states", il);
|
|
|
|
v_states = ggml_view_2d(ctx0, v_states, hparams.n_embd_head_v_full * n_head, n_tokens,
|
|
ggml_row_size(kv->type, hparams.n_embd_head_v_full * n_head),
|
|
0);
|
|
cb(v_states, "v_states", il);
|
|
|
|
struct ggml_tensor * q_states = ggml_concat(ctx0, q_nope, q_rope, 0);
|
|
cb(q_states, "q_states", il);
|
|
|
|
struct ggml_tensor * k_states = ggml_concat(ctx0, k_nope, ggml_repeat(ctx0, k_rope, q_rope), 0);
|
|
cb(k_states, "k_states", il);
|
|
|
|
cur = llm_build_kv(ctx0, lctx, kv_self, gf,
|
|
model.layers[il].wo, NULL,
|
|
k_states, v_states, q_states, KQ_mask, n_tokens, kv_head, n_kv, kq_scale, cb, il);
|
|
|
|
}
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
ggml_cgraph * llm_build_context::build_deepseek2() {
|
|
dsa_last_full_sorted = nullptr; // GLM-5.2 IndexShare: reset shared-layer top-k reuse state (before any layer, incl. the MTP early-return path)
|
|
const bool tp_mode = (model.split_mode == LLAMA_SPLIT_MODE_GRAPH ||
|
|
model.split_mode == LLAMA_SPLIT_MODE_ATTN);
|
|
#ifdef GGML_USE_VULKAN
|
|
const bool use_f32_attn_precision = true;
|
|
#else
|
|
const bool use_f32_attn_precision = lctx.cparams.graph_attn_precision == GGML_TYPE_F32;
|
|
#endif
|
|
ggml_cgraph * gf = new_graph_custom();
|
|
|
|
// mutable variable, needed during the last layer of the computation to skip unused tokens
|
|
int32_t n_tokens = this->n_tokens;
|
|
|
|
bool is_lite = (hparams.n_layer == 27 || hparams.n_layer == 26);
|
|
|
|
// We have to pre-scale kq_scale and attn_factor to make the YaRN RoPE work correctly.
|
|
// See https://github.com/ggerganov/llama.cpp/discussions/7416 for detailed explanation.
|
|
const float mscale = attn_factor * (1.0f + hparams.rope_yarn_log_mul * logf(1.0f / freq_scale));
|
|
const float kq_scale = 1.0f*mscale*mscale/sqrtf(float(hparams.n_embd_head_k(0)));
|
|
const float attn_factor_scaled = 1.0f / (1.0f + 0.1f * logf(1.0f / freq_scale));
|
|
|
|
struct ggml_tensor * cur;
|
|
struct ggml_tensor * inpL;
|
|
|
|
// {n_embd, n_tokens}
|
|
inpL = llm_build_inp_embd(ctx0, lctx, hparams, batch, model.tok_embd, cb);
|
|
|
|
// inp_pos - contains the positions
|
|
struct ggml_tensor * inp_pos = build_inp_pos();
|
|
|
|
// KQ_mask (mask for 1 head, it will be broadcasted to all heads)
|
|
struct ggml_tensor * KQ_mask = build_inp_KQ_mask();
|
|
|
|
if (lctx.cparams.dsa && model.arch == LLM_ARCH_GLM_DSA) {
|
|
static const int n_sink = []{ const char * e = getenv("DSA_SINK"); return e ? atoi(e) : 1; }();
|
|
if (n_sink > 0 && n_sink < (int) n_kv) {
|
|
lctx.inp_dsa_sink = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, n_kv, n_tokens);
|
|
cb(lctx.inp_dsa_sink, "dsa_sink", -1);
|
|
ggml_set_input(lctx.inp_dsa_sink);
|
|
}
|
|
auto minus_inf = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, KQ_mask->ne[0], n_tokens);
|
|
minus_inf = ggml_fill_inplace(ctx0, minus_inf, -INFINITY);
|
|
ggml_build_forward_expand(gf, minus_inf);
|
|
lctx.inp_mask_inf = minus_inf;
|
|
}
|
|
|
|
// whether to use n_tokens as the matrix dimension during multiplication or n_head
|
|
// n_tokens is higher during prompt processing, this allows to optimize for this case
|
|
bool pp_opt = n_tokens >= 128 && lctx.cparams.mla_attn > 1;
|
|
|
|
auto rope_cache = cparams.rope_cache && (rope_type == LLAMA_ROPE_TYPE_NEOX || rope_type == LLAMA_ROPE_TYPE_NORM) ?
|
|
ggml_rope_cache(ctx0, inp_pos, nullptr, n_rot, n_rot, rope_type, n_ctx_orig, freq_base, freq_scale,
|
|
ext_factor, attn_factor, beta_fast, beta_slow) : nullptr;
|
|
|
|
if (cparams.mtp_op_type != MTP_OP_NONE) {
|
|
if (model.arch != LLM_ARCH_GLM_DSA || !model.mtp || hparams.nextn_predict_layers == 0) {
|
|
GGML_ABORT("MTP tail is only wired for GLM_DSA models with NextN layers enabled");
|
|
}
|
|
|
|
ggml_tensor * hidden_states_from_main_model;
|
|
|
|
if (cparams.mtp_op_type == MTP_OP_WARMUP || cparams.mtp_op_type == MTP_OP_UPDATE_ACCEPTED) {
|
|
hidden_states_from_main_model = ggml_new_tensor_2d(ctx0, GGML_TYPE_F32, hparams.n_embd, n_tokens);
|
|
} else {
|
|
hidden_states_from_main_model = ggml_new_tensor_1d(ctx0, GGML_TYPE_F32, hparams.n_embd);
|
|
}
|
|
ggml_set_name(hidden_states_from_main_model, "inp_mtp_states");
|
|
ggml_set_input(hidden_states_from_main_model);
|
|
|
|
lctx.inp_mtp_states = hidden_states_from_main_model;
|
|
|
|
const int il_mtp = hparams.n_layer - 1;
|
|
const auto & mtp_layer = model.layers[il_mtp];
|
|
|
|
cur = build_deepseek2_mtp(mtp_layer, hidden_states_from_main_model, gf, inp_pos, rope_cache);
|
|
|
|
ggml_build_forward_expand(gf, cur);
|
|
return gf;
|
|
}
|
|
|
|
int n_active_layers = hparams.n_layer - hparams.nextn_predict_layers;
|
|
for (int il = 0; il < n_active_layers; ++il) {
|
|
struct ggml_tensor * inpSA = inpL;
|
|
|
|
bool is_tp_layer = tp_mode && model.layers[il].wo && model.layers[il].wo->extra;
|
|
|
|
if (is_tp_layer) {
|
|
cur = build_deepseek2_tp_attention(gf, il, inpL, KQ_mask, inp_pos, rope_cache,
|
|
kq_scale, attn_factor_scaled,
|
|
use_f32_attn_precision, is_lite, pp_opt);
|
|
} else {
|
|
cur = build_deepseek2_layer_attention(gf, il, inpL, KQ_mask, inp_pos, rope_cache,
|
|
kq_scale, attn_factor_scaled,
|
|
use_f32_attn_precision, is_lite, pp_opt);
|
|
}
|
|
|
|
if (il == n_active_layers - 1 && !lctx.cparams.mtp) {
|
|
// skip computing output for unused tokens
|
|
struct ggml_tensor * inp_out_ids = build_inp_out_ids();
|
|
n_tokens = n_outputs;
|
|
cur = ggml_get_rows(ctx0, cur, inp_out_ids);
|
|
inpSA = ggml_get_rows(ctx0, inpSA, inp_out_ids);
|
|
cb(cur, "last_attn", il);
|
|
cb(inpSA, "last_ffn_inp", il);
|
|
}
|
|
|
|
// TP path folds residual inside the per-rank FFN reduce; layer mode adds it here.
|
|
struct ggml_tensor * ffn_inp;
|
|
if (is_tp_layer) {
|
|
ffn_inp = cur;
|
|
} else {
|
|
ffn_inp = ggml_add(ctx0, cur, inpSA);
|
|
}
|
|
cb(ffn_inp, "ffn_inp", il);
|
|
|
|
if (is_tp_layer) {
|
|
cur = ffn_inp;
|
|
} else {
|
|
cur = llm_build_norm(ctx0, ffn_inp, hparams, model.layers[il].ffn_norm, NULL, LLM_NORM_RMS, cb, il);
|
|
cb(cur, "ffn_norm", il);
|
|
}
|
|
|
|
if ((uint32_t) il < hparams.n_layer_dense_lead) {
|
|
cur = llm_build_ffn(ctx0, lctx,
|
|
is_tp_layer ? model.layers[il].ffn_norm : nullptr, cur,
|
|
model.layers[il].ffn_up, NULL, NULL,
|
|
model.layers[il].ffn_gate, NULL, NULL,
|
|
model.layers[il].ffn_down, NULL, NULL,
|
|
NULL,
|
|
LLM_FFN_SILU, LLM_FFN_PAR, cb, il,
|
|
gf,
|
|
/*add_input=*/is_tp_layer);
|
|
cb(cur, "ffn_out", il);
|
|
} else if (is_tp_layer) {
|
|
cur = llm_build_std_moe_ffn(ctx0, lctx, model.layers[il].ffn_norm, cur,
|
|
model.layers[il].ffn_gate_inp, nullptr,
|
|
model.layers[il].ffn_up_exps, nullptr,
|
|
model.layers[il].ffn_gate_exps, nullptr,
|
|
model.layers[il].ffn_down_exps, nullptr,
|
|
model.layers[il].ffn_exp_probs_b,
|
|
model.layers[il].ffn_up_shexp, nullptr,
|
|
model.layers[il].ffn_gate_shexp, nullptr,
|
|
model.layers[il].ffn_down_shexp, nullptr,
|
|
n_expert, n_expert_used,
|
|
LLM_FFN_SILU, hparams.expert_weights_norm,
|
|
true, hparams.expert_weights_scale,
|
|
(enum llm_expert_gating_func_type) hparams.expert_gating_func,
|
|
LLM_FFN_SILU, cb, il, gf, /*add_input=*/true, model.layers[il].ffn_up_gate_exps);
|
|
cb(cur, "ffn_out", il);
|
|
} else {
|
|
// MoE branch
|
|
ggml_tensor * moe_out =
|
|
llm_build_moe_ffn(ctx0, lctx, cur,
|
|
model.layers[il].ffn_gate_inp,
|
|
model.layers[il].ffn_up_exps,
|
|
model.layers[il].ffn_gate_exps,
|
|
model.layers[il].ffn_down_exps,
|
|
model.layers[il].ffn_exp_probs_b,
|
|
n_expert, n_expert_used,
|
|
LLM_FFN_SILU, hparams.expert_weights_norm,
|
|
true, hparams.expert_weights_scale,
|
|
(enum llm_expert_gating_func_type) hparams.expert_gating_func,
|
|
cb, il, gf, false, model.layers[il].ffn_up_gate_exps);
|
|
cb(moe_out, "ffn_moe_out", il);
|
|
|
|
// FFN shared expert
|
|
ggml_tensor * ffn_shexp = llm_build_ffn(ctx0, lctx, nullptr, cur,
|
|
model.layers[il].ffn_up_shexp, NULL, NULL,
|
|
model.layers[il].ffn_gate_shexp, NULL, NULL,
|
|
model.layers[il].ffn_down_shexp, NULL, NULL,
|
|
NULL,
|
|
LLM_FFN_SILU, LLM_FFN_PAR, cb, il, gf);
|
|
cb(ffn_shexp, "ffn_shexp", il);
|
|
|
|
cur = ggml_add(ctx0, moe_out, ffn_shexp);
|
|
cb(cur, "ffn_out", il);
|
|
}
|
|
|
|
if (!is_tp_layer) {
|
|
cur = ggml_add(ctx0, cur, ffn_inp);
|
|
}
|
|
cur = lctx.cvec.apply_to(ctx0, cur, il);
|
|
cb(cur, "l_out", il);
|
|
|
|
// input for next layer
|
|
inpL = cur;
|
|
}
|
|
|
|
cur = build_output(lctx, ctx0, inpL, model.output, model.output_norm, cb);
|
|
cb(cur, "result_output", -1);
|
|
|
|
ggml_build_forward_expand(gf, cur);
|
|
|
|
return gf;
|
|
}
|
|
|
|
struct ggml_tensor * llm_build_context::build_deepseek2_mtp(
|
|
const llama_layer & mtp_layer,
|
|
struct ggml_tensor * prev_embeddings,
|
|
struct ggml_cgraph * gf,
|
|
struct ggml_tensor * inp_pos,
|
|
[[maybe_unused]] struct ggml_tensor * rope_cache) {
|
|
#ifdef GGML_USE_VULKAN
|
|
constexpr bool use_f32_attn_precision = true;
|
|
#else
|
|
constexpr bool use_f32_attn_precision = false;
|
|
#endif
|
|
|
|
const int il = hparams.n_layer - 1;
|
|
|
|
const uint32_t n_embd_head_k_mtp = hparams.n_embd_head_k(il);
|
|
|
|
const float mscale = attn_factor * (1.0f + hparams.rope_yarn_log_mul * logf(1.0f / freq_scale));
|
|
const float kq_scale = 1.0f*mscale*mscale/sqrtf(float(n_embd_head_k_mtp));
|
|
const float attn_factor_scaled = 1.0f / (1.0f + 0.1f * logf(1.0f / freq_scale));
|
|
|
|
struct ggml_tensor * KQ_mask = build_inp_KQ_mask();
|
|
struct ggml_tensor * inp_out_ids = n_tokens > 1 ? build_inp_out_ids() : nullptr;
|
|
|
|
// Token embedding
|
|
ggml_tensor * mtp_embd_weights = mtp_layer.nextn.embed_tokens;
|
|
if (mtp_embd_weights == nullptr) {
|
|
mtp_embd_weights = model.tok_embd;
|
|
}
|
|
ggml_tensor * token_emb = build_inp_embd_mtp(mtp_embd_weights);
|
|
|
|
// Normalize and project
|
|
ggml_tensor * token_emb_norm = llm_build_norm(ctx0, token_emb, hparams, mtp_layer.nextn.enorm, NULL, LLM_NORM_RMS, cb, il);
|
|
ggml_tensor * hidden_state_norm = llm_build_norm(ctx0, prev_embeddings, hparams, mtp_layer.nextn.hnorm, NULL, LLM_NORM_RMS, cb, il);
|
|
|
|
if (mtp_layer.nextn.eh_proj == nullptr) {
|
|
GGML_ABORT("GLM_DSA MTP requires nextn.eh_proj");
|
|
}
|
|
|
|
ggml_tensor * combined = ggml_concat(ctx0, token_emb_norm, hidden_state_norm, 0);
|
|
cb(combined, "mtp_concat", il);
|
|
ggml_tensor * cur = llm_build_lora_mm(lctx, ctx0, mtp_layer.nextn.eh_proj, combined);
|
|
|
|
struct ggml_tensor * inpSA = cur;
|
|
|
|
cur = build_deepseek2_layer_attention(gf, il, cur, KQ_mask, inp_pos, nullptr,
|
|
kq_scale, attn_factor_scaled,
|
|
use_f32_attn_precision, false, false);
|
|
|
|
// Residual + FFN
|
|
ggml_tensor * ffn_inp = ggml_add(ctx0, cur, inpSA);
|
|
cb(ffn_inp, "mtp_ffn_inp", il);
|
|
|
|
if (inp_out_ids) {
|
|
ffn_inp = ggml_get_rows(ctx0, ffn_inp, inp_out_ids);
|
|
}
|
|
|
|
cur = llm_build_norm(ctx0, ffn_inp, hparams, mtp_layer.ffn_norm, NULL, LLM_NORM_RMS, cb, il);
|
|
cb(cur, "ffn_norm", il);
|
|
|
|
// MoE FFN (MTP layer is always in the MoE range, not dense)
|
|
{
|
|
ggml_tensor * moe_out =
|
|
llm_build_moe_ffn(ctx0, lctx, cur,
|
|
mtp_layer.ffn_gate_inp,
|
|
mtp_layer.ffn_up_exps,
|
|
mtp_layer.ffn_gate_exps,
|
|
mtp_layer.ffn_down_exps,
|
|
mtp_layer.ffn_exp_probs_b,
|
|
n_expert, n_expert_used,
|
|
LLM_FFN_SILU, hparams.expert_weights_norm,
|
|
true, hparams.expert_weights_scale,
|
|
(enum llm_expert_gating_func_type) hparams.expert_gating_func,
|
|
cb, il, gf, false, mtp_layer.ffn_up_gate_exps);
|
|
cb(moe_out, "ffn_moe_out", il);
|
|
|
|
// Shared Expert FFN
|
|
ggml_tensor * ffn_shexp = llm_build_ffn(ctx0, lctx, nullptr, cur,
|
|
mtp_layer.ffn_up_shexp, NULL, NULL,
|
|
mtp_layer.ffn_gate_shexp, NULL, NULL,
|
|
mtp_layer.ffn_down_shexp, NULL, NULL,
|
|
NULL,
|
|
LLM_FFN_SILU, LLM_FFN_PAR, cb, il);
|
|
cb(ffn_shexp, "ffn_shexp", il);
|
|
|
|
cur = ggml_add(ctx0, moe_out, ffn_shexp);
|
|
cb(cur, "ffn_out", il);
|
|
}
|
|
|
|
cur = ggml_add(ctx0, cur, ffn_inp);
|
|
cur = lctx.cvec.apply_to(ctx0, cur, il);
|
|
cb(cur, "mtp_ffn_out_resid", il);
|
|
|
|
// Output head
|
|
if (mtp_layer.nextn.shared_head_norm == nullptr) {
|
|
GGML_ABORT("GLM_DSA MTP requires nextn.shared_head_norm");
|
|
}
|
|
|
|
cur = llm_build_norm(ctx0, cur, hparams, mtp_layer.nextn.shared_head_norm, NULL, LLM_NORM_RMS, cb, il);
|
|
cb(cur, "result_norm", -1);
|
|
|
|
// If nextn.shared_head_head is missing, use model.output (Main LM Head)
|
|
ggml_tensor * mtp_head_weights = mtp_layer.nextn.shared_head_head;
|
|
if (mtp_head_weights == nullptr) {
|
|
mtp_head_weights = model.output;
|
|
}
|
|
cur = llm_build_lora_mm(lctx, ctx0, mtp_head_weights, cur);
|
|
cb(cur, "result_output", -1);
|
|
|
|
return cur;
|
|
}
|