Skip to content

Transformer 推理图 — 代码走读

src/llama-model.cpp — 推理图入口(调度器)

src/llama-model.cpp 本身只承担调度职责:它提供推理图入口 build_graph、纯虚的按架构分发方法 build_arch_graph,以及根据架构选择 RoPE 类型的 llama_model_rope_type 开关。各架构真正的 forward pass 计算图实现已搬到了 src/models/*.cpp——例如 LLaMA 在 src/models/llama.cpp,GPT-2 在 src/models/gpt2.cpp 等等。

计算图构建入口

入口是 llama_model::build_graph(const llm_graph_params &)src/llama-model.cpp),它把真正的"按架构建图"交给纯虚方法 build_arch_graph——每个具体架构的模型类(如 llama_model_llama)各自 override 它,从而完成分发,不再用一个大 switch

cpp
// src/llama-model.cpp (~2720) —— 真实入口
ggml_cgraph * llama_model::build_graph(const llm_graph_params & params) const {
    std::unique_ptr<llm_graph_context> llm = build_arch_graph(params); // 多态分发

    llm->build_pooling(...);   // 池化层(分类/嵌入等场景)
    llm->build_sampling();     // 后端采样层(若有)
    llm->build_dense_out(...);

    llm->res->set_outputs(params);
    return llm->res->get_gf();
}

// src/llama-model.h (~766) —— 基类里是纯虚:
virtual std::unique_ptr<llm_graph_context>
    build_arch_graph(const llm_graph_params & params) const = 0;

// src/models/llama.cpp (~99) —— LLaMA 架构的 override:
std::unique_ptr<llm_graph_context> llama_model_llama::build_arch_graph(
        const llm_graph_params & params) const {
    return std::make_unique<graph<false>>(*this, params); // graph<embed> 见下
}

旧版本曾用 llm_build_graph(lctx, batch) + llm_build_llama(...)/llm_build_gpt2(...) 这一组自由函数 + switch 来分发;这套命名现已不存在,改成了上面的 build_graph / 虚函数 build_arch_graph 模式。

LLaMA Forward Pass(教学示意)

下面是概念性伪代码,用以说明 LLaMA 一层 forward 的数据流,并非字面源码。真实实现是 llama_model_llama::graph<embed> 这个 llm_graph_context 子类的构造函数(src/models/llama.cpp)。注意:归一化 / 注意力 / FFN 都是 llm_graph_context 的成员方法build_norm / build_attn / build_ffn,声明于 src/llama-graph.h),不是 llm_build_* 自由函数。下文为对照清楚,仍用简化的 build_* 写法。

cpp
// 概念示意:llama_model_llama::graph<embed>(model, params) 的主干
// (真实代码在 src/models/llama.cpp;下面省略 build_inp_* 等细节)
struct ggml_tensor * cur = build_inp_embd(model.tok_embd);  // token embedding

for (int il = 0; il < n_layer; il++) {
    // 2a. Attention 前 norm(真实:llm_graph_context::build_norm)
    cur = build_norm(cur, layers[il].attn_norm);

    // Q/K/V 投影(此处直接写 ggml_mul_mat 仅作示意,真实由 build_attn 内部封装)
    struct ggml_tensor * Q = ggml_mul_mat(ctx, layers[il].wq, cur);
    struct ggml_tensor * K = ggml_mul_mat(ctx, layers[il].wk, cur);
    struct ggml_tensor * V = ggml_mul_mat(ctx, layers[il].wv, cur);

    // 2b. RoPE(下面这行是教学示意:真实调用见 ggml/src/ggml.c 的 ggml_rope,
    //     其参数顺序/形式请以源码为准,rope_type 由 llama_model_rope_type 选定)
    Q = ggml_rope(ctx, Q, positions, n_rot, rope_type);
    K = ggml_rope(ctx, K, positions, n_rot, rope_type);

    // 2c. Attention + KV Cache(真实:llm_graph_context::build_attn / build_attn_mha)
    cur = build_attn(Q, K_cache, V_cache, mask);

    // 2d. Output projection + 残差
    cur = ggml_mul_mat(ctx, layers[il].wo, cur);
    cur = ggml_add(ctx, cur, ffn_inp);

    // 2e. FFN (SwiGLU)(真实:llm_graph_context::build_ffn)
    ffn_inp = cur;
    cur = build_norm(cur, layers[il].ffn_norm);
    cur = build_ffn(cur, layers[il]);

    // 2f. 残差
    cur = ggml_add(ctx, cur, ffn_inp);
}

// 3. Final norm + 4. Output projection
cur = build_norm(cur, model.output_norm);
cur = ggml_mul_mat(ctx, model.output, cur);

src/llama-graph.cpp — 图构建辅助方法

llama-graph.h / llama-graph.cpp 定义了 struct llm_graph_context,它把归一化、FFN、注意力等"积木"实现为成员方法(不是自由函数,也没有 llm_ 前缀):build_normllama-graph.h:~1065)、build_ffn:~1096)、build_qkv:~1075,本窗口新增)、build_attn(多处重载,:~1194 起,共 7 个)以及多维注意力的 build_attn_mha:~1180,本窗口新增 n_kv_max 形参驱动稀疏 FA)。各架构的 graph 子类在自己的构造函数里调用它们来搭图:

cpp
// llm_graph_context::build_norm 的概念示意(真实声明在 src/llama-graph.h:~966)
// 构建归一化层 (RMSNorm) —— 它是 llm_graph_context 的成员方法
struct ggml_tensor * build_norm(
    struct ggml_tensor * cur,
    struct ggml_tensor * weight) {
    cur = ggml_rms_norm(ctx, cur, epsilon);
    cur = ggml_mul(ctx, cur, weight);
    return cur;
}

关键函数索引

函数 / 方法文件说明
llama_model::build_graphllama-model.cpp构建完整推理图的入口
llama_model::build_arch_graphllama-model.h(纯虚)按架构分发给各模型子类
llama_model_llama::graph<embed>models/llama.cppLLaMA 架构的 forward pass 计算图
llm_graph_context::build_attnllama-graph.h注意力计算(成员方法,7 个重载)
llm_graph_context::build_attn_mhallama-graph.cpp:2591多头注意力(新 n_kv_max 形参 → 稀疏 FA)
llm_graph_context::build_qkvllama-graph.h:1075消费 --fuse-qkv 合并的 QKV 投影(新)
llm_graph_context::build_ffnllama-graph.hFFN(SwiGLU,成员方法)
llm_graph_context::build_normllama-graph.hRMSNorm(成员方法)
llm_graph_context::build_samplingllama-graph.cpp:3764后端多输出采样子图(新)
llm_graph_context::add_fused_nodellama-graph.h:926注册一条融合算子链
ggml_ropeggml/src/ggml.cRoPE 位置编码(另有新 setter ggml_rope_set_offset

融合算子链(fused node)

融合算子的构造现已从 llama-context.cpp 移入图层llm_graph_context 持有一个 std::vector<llm_graph_fused_node>llama-graph.h:~887,结构为 {llm_fused_op op; ggml_tensor * tensor; int il;}),通过 add_fused_node(...) 注册、get_fused_nodes() 取出,由上下文在执行时统一应用。这样「哪些算子可融合」的决策集中在图构建阶段,而非散落在 context 里。

cpp
// src/llama-graph.h
struct llm_graph_fused_node {
    // 描述一条待融合的算子链(如 norm + mul、rope 拼接等)
    // ...
};

// 在 llm_graph_context 中:
void add_fused_node(llm_graph_fused_node result);
const std::vector<llm_graph_fused_node> & get_fused_nodes() const;

这是本窗口的图层面重构(90e0f5cfc#24646):融合构造逻辑从 llama-context.cpp(-131 行)迁入 llama-graph.{cpp,h},使图成为融合信息的唯一来源。

DeepSeek V4 / 稀疏注意力的图接线

DeepSeek 系列的稀疏注意力在图层面消费新算子与 indexer:

  • lightning-indexer + 超连接GGML_OP_LIGHTNING_INDEXERDSV4_HC_PRE/COMB/POST 在 dsv4 的 forward 图中被组装(src/models/deepseek4.cpp),实现 lightning indexer 检索与融合超连接
  • KQ mask 统一 f16 — DeepSeek V4 把所有 KQ mask 改为 f16(2ed3c1abb),并在使用 Flash Attention 时移除零注意力 bias
  • 稀疏 FA 经 op 参数 — 本窗口起稀疏注意力不再需要专用图分支:build_attn_mhan_kv_max 写入 FLASH_ATTN_EXT 的 op_params,CUDA/Metal 按 mask 有限项做稀疏 K/V(8e93a9773 / 7bb0fc18f
  • MSA indexer 走独立输入类 — MiniMax-M3 的稀疏注意力现经 llm_graph_input_attn_kv_msa(携带 indexer key cache 的目标下标 self_k_idxs_idx)写入独立的 indexer cache 实例(见 KV Cache 代码走读67d5978bb 把 MSA 移出了基类 cache)