Skip to content

GGML 张量库基础 — 练习

练习 1:构建简单计算图

用 GGML API 构建函数 f(x) = 3x² + 2x + 1,并计算 x = 2.0 时的值。

提示:使用 ggml_mulggml_add 等操作。

参考答案
c
struct ggml_init_params params = {
    .mem_size = 16 * 1024 * 1024,
    .mem_buffer = NULL,
};
struct ggml_context * ctx = ggml_init(params);

struct ggml_tensor * x = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);
ggml_set_name(x, "x");

// 常量
float val_3 = 3.0f, val_2 = 2.0f, val_1 = 1.0f;
struct ggml_tensor * c3 = ggml_new_f32(ctx, val_3);
struct ggml_tensor * c2 = ggml_new_f32(ctx, val_2);
struct ggml_tensor * c1 = ggml_new_f32(ctx, val_1);

// 3x² + 2x + 1
struct ggml_tensor * x2 = ggml_mul(ctx, x, x);
struct ggml_tensor * term1 = ggml_mul(ctx, c3, x2);
struct ggml_tensor * term2 = ggml_mul(ctx, c2, x);
struct ggml_tensor * f = ggml_add(ctx, ggml_add(ctx, term1, term2), c1);

// 设置 x = 2.0 并计算
((float *)x->data)[0] = 2.0f;
struct ggml_cgraph * graph = ggml_new_graph(ctx);
ggml_build_forward_expand(graph, f);
ggml_graph_compute(graph);
// 结果: ((float *)f->data)[0] == 17.0

练习 2:理解张量内存布局

创建一个 shape 为 [4, 3] 的 F32 张量,验证 nb[0]nb[1] 的值。

参考答案
c
struct ggml_tensor * t = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, 4, 3);
// t->ne[0] = 4, t->ne[1] = 3
// t->nb[0] = sizeof(float) = 4 bytes
// t->nb[1] = 4 * 4 = 16 bytes (一行的大小)

练习 3:矩阵乘法维度分析

给定 A: [m, k]B: [k, n],使用 ggml_mul_mat(ctx, A, B) 后结果的 shape 是什么?

参考答案

ggml_mul_mat(ctx, A, B) 计算 B @ A^T(注意参数顺序)。

GGML 中 ne[0] 是列数(内层维度),ne[1] 是行数。将数学记号 [rows, cols] 转换:

  • A: [m, k] → ne[0]=k, ne[1]=m
  • B: [k, n] → ne[0]=n, ne[1]=k

ggml_mul_mat 要求两个输入的 ne[0] 匹配,因此此处隐含 k == n。满足此条件时:

  • 结果 ne[0]=m, ne[1]=k,对应数学 shape [k, m]
  • B @ A^T = [k, n] × [k, m](当 k==n 时为合法矩阵乘法,结果 [k, m])

实践中的用法:llama.cpp 中权重 W: [out, in] 和输入 x: [in, batch] 的 ne[0] 都是 in_dim,天然匹配。ggml_mul_mat(W, x) 计算的是 x @ W^T,等价于线性变换 W @ x 的转置形式。

拓展挑战

  • 使用自动微分计算 f(x) = x²x = 3.0 处的梯度
  • 阅读量化类型 Q4_0 的 block 结构,理解 4.5 bits/weight 的计算方式
  • 对比 ggml_mul_mat 在不同后端上的实现差异