Appearance
GGML 张量库基础 — 练习
练习 1:构建简单计算图
用 GGML API 构建函数 f(x) = 3x² + 2x + 1,并计算 x = 2.0 时的值。
提示:使用 ggml_mul、ggml_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在不同后端上的实现差异