ggml库文档说明docs

背景

近期学习PowerInfer代码,其是基于llama.cpp改的,所以本质上还是去读llama.cpp的代码;而llama.cpp又基于ggml库完成了相关建图和计算的工作。

无论是llama.cpp,还是ggml库,除了项目目录下的docs目录和代码中的注释,基本上没有官方参考了,很难受。

偶然点开ggml.h,发现刚开头有一些注释,属于是非常清晰、简洁、有用的参考了。故将翻译后的文档和英语原文放上来,供学习llama.cpp和ggml库的朋友们参考。

Origin:https://github.com/ggerganov/ggml/blob/master/include/ggml.h

翻译后的文档

GGML Tensor Library

本文件仍在编写中。如果您希望涵盖某些特定主题,请随时发表评论:

https://github.com/ggerganov/whisper.cpp/issues/40

概述

该库实现了:

  • 一组张量运算
  • 自动微分
  • 基本优化算法

该库的目的是为各种机器学习任务提供一种简约的方法。这包括但不限于以下内容:

  • 线性回归
  • 支持向量机
  • 神经网络

该库允许用户使用可用的张量运算来定义特定函数。该函数定义通过计算图在内部表示。函数定义中的每个张量运算对应于图中的一个节点。定义了计算图后,用户可以选择计算函数的值和/或相对于输入变量的梯度。此外,还可以使用可用的优化算法之一来优化函数。

例如,我们在此定义函数:f(x) = a*x^2 + b

{
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_param(ctx, x); // x 是一个输入变量

struct ggml_tensor * a = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);
struct ggml_tensor * b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);
struct ggml_tensor * x2 = ggml_mul(ctx, x, x);
struct ggml_tensor * f = ggml_add(ctx, ggml_mul(ctx, a, x2), b);...


}

请注意,上面的函数定义不涉及任何实际计算。只有当用户明确请求时,才会执行计算。例如,计算函数在x = 2.0处的值:

{
...

struct ggml_cgraph * gf = ggml_new_graph(ctx);
ggml_build_forward_expand(gf, f); // 备注:通过拓扑排序建立计算图。进行拓扑排序时,是从最后一个节点往前走的。所以在这里传入最后一个结果Tensor。这基于“每个算子可能具有多个输入,但最终只有1个输出”的假设进行的。

// 设置输入变量和参数值
ggml_set_f32(x, 2.0f);
ggml_set_f32(a, 3.0f);
ggml_set_f32(b, 4.0f);

ggml_graph_compute_with_ctx(ctx, &gf, n_threads);

printf(“f = %f\n”, ggml_get_f32_1d(f, 0));

...


}

实际计算在ggml_graph_compute()函数中执行。

ggml_new_tensor_…()函数创建新的张量。它们被分配到提供给ggml_init()函数的内存缓冲区中。您必须注意不要超过内存缓冲区的大小。因此,您必须事先知道计算需要多少内存。或者,您可以分配足够大的内存,并在定义计算图后,调用ggml_used_mem()函数来找出实际需要的内存量。

ggml_set_param()函数将张量标记为输入变量。自动微分和优化算法使用该变量。

上述方法允许一次定义函数图,然后多次计算其前向或后向图。所有计算都将使用ggml_init()函数中分配的同一内存缓冲区。这样,用户就可以避免运行时内存分配的开销。

该库支持多维张量——最多4个维度。FP16和FP32数据类型是首要考虑的对象,但理论上该库可以扩展为支持FP8和整数数据类型。

每个张量运算都会产生一个新的张量。最初,该库仅支持一元和二元运算。大多数可用运算属于这两类之一。随着时间的推移,很明显该库需要支持更复杂的运算。支持这些运算的方法尚不明确,但以下运算中演示了一些示例:

  • ggml_permute()
  • ggml_conv_1d_1s()
  • ggml_conv_1d_2s()

对于每个张量运算符,库都实现了前向和后向计算函数。前向函数根据输入张量值计算输出张量值。后向函数根据输出张量的伴随计算输入张量的伴随。关于此含义的详细解释,请参加微积分课程或观看以下视频:

什么是自动微分?
https://www.youtube.com/watch?v=wG_nF1awSSY

张量数据(ggml_tensor结构体)

张量通过ggml_tensor结构存储在内存中。该结构提供有关张量大小、数据类型以及存储张量数据的内存缓冲区的信息。此外,它还包含指向“源”张量的指针,即用于计算当前张量的张量。例如:

{
struct ggml_tensor * c = ggml_add(ctx, a, b);
// 由于张量c是a+b的结果,所以它的“源”张量分别是a和b
assert(c->src[0] == a); 
assert(c->src[1] == b);
}

多维张量按行优先顺序存储。ggml_tensor结构包含每个维度中的元素数量(“ne”)以及字节数(“nb”,又称步长)的字段。这允许在内存中存储不连续的张量,这对于转置和置换等操作非常有用。所有张量操作都必须考虑步长,而不能假设张量在内存中是连续的。

张量数据通过“数据”指针访问。例如:

{
const int nx = 2;
const int ny = 3;

struct ggml_tensor * a = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, nx, ny);

for (int y = 0; y < ny; y++) {
    for (int x = 0; x < nx; x++) {
       *(float *) ((char *) a->data + y*a->nb[1] + x*a->nb[0]) = x + y;
    }
}

...
}

此外,还可以使用辅助函数,例如ggml_get_f32_1d()和ggml_set_f32_1d()。

英语原文

GGML Tensor Library

This documentation is still a work in progress. If you wish some specific topics to be covered, feel free to drop a comment:

https://github.com/ggerganov/whisper.cpp/issues/40

Overview

This library implements:

  • a set of tensor operations
  • automatic differentiation
  • basic optimization algorithms

The aim of this library is to provide a minimalistic approach for various machine learning tasks. This includes, but is not limited to, the following:

  • linear regression
  • support vector machines
  • neural networks

The library allows the user to define a certain function using the available tensor operations. This function definition is represented internally via a computation graph. Each tensor operation in the function definition corresponds to a node in the graph. Having the computation graph defined, the user can choose to compute the function’s value and/or its gradient with respect to the input variables. Optionally, the function can be optimized using one of the available optimization algorithms.

For example, here we define the function: f(x) = a*x^2 + b

{
struct ggml_init_params params = {
.mem_size = 16*1024*1024,
.mem_buffer = NULL,
};

// memory allocation happens here
struct ggml_context * ctx = ggml_init(params);

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

ggml_set_param(ctx, x); // x is an input variable

struct ggml_tensor * a = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);
struct ggml_tensor * b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);
struct ggml_tensor * x2 = ggml_mul(ctx, x, x);
struct ggml_tensor * f = ggml_add(ctx, ggml_mul(ctx, a, x2), b);

...
}

Notice that the function definition above does not involve any actual computation. The computation is performed only when the user explicitly requests it. For example, to compute the function’s value at x = 2.0:

{
...

struct ggml_cgraph * gf = ggml_new_graph(ctx);
ggml_build_forward_expand(gf, f);

// set the input variable and parameter values
ggml_set_f32(x, 2.0f);
ggml_set_f32(a, 3.0f);
ggml_set_f32(b, 4.0f);

ggml_graph_compute_with_ctx(ctx, &gf, n_threads);

printf("f = %f\n", ggml_get_f32_1d(f, 0));

...
}

The actual computation is performed in the ggml_graph_compute() function.

The ggml_new_tensor_…() functions create new tensors. They are allocated in the memory buffer provided to the ggml_init() function. You have to be careful not to exceed the memory buffer size. Therefore, you have to know in advance how much memory you need for your computation. Alternatively, you can allocate a large enough memory and after defining the computation graph, call the ggml_used_mem() function to find out how much memory was actually needed.

The ggml_set_param() function marks a tensor as an input variable. This is used by the automatic differentiation and optimization algorithms.

The described approach allows to define the function graph once and then compute its forward or backward graphs multiple times. All computations will use the same memory buffer allocated in the ggml_init() function. This way the user can avoid the memory allocation overhead at runtime.

The library supports multi-dimensional tensors - up to 4 dimensions. The FP16 and FP32 data types are first class citizens, but in theory the library can be extended to support FP8 and integer data types.

Each tensor operation produces a new tensor. Initially the library was envisioned to support only the use of unary and binary operations. Most of the available operations fall into one of these two categories. With time, it became clear that the library needs to support more complex operations. The way to support these operations is not clear yet, but a few examples are demonstrated in the following operations:

  • ggml_permute()
  • ggml_conv_1d_1s()
  • ggml_conv_1d_2s()

For each tensor operator, the library implements a forward and backward computation function. The forward function computes the output tensor value given the input tensor values. The backward function computes the adjoint of the input tensors given the adjoint of the output tensor. For a detailed explanation of what this means, take a calculus class, or watch the following video:

What is Automatic Differentiation?
https://www.youtube.com/watch?v=wG_nF1awSSY

Tensor data (struct ggml_tensor)

The tensors are stored in memory via the ggml_tensor struct. The structure provides information about the size of the tensor, the data type, and the memory buffer where the tensor data is stored. Additionally, it contains pointers to the “source” tensors - i.e. the tensors that were used to compute the current tensor. For example:

{
struct ggml_tensor * c = ggml_add(ctx, a, b);

assert(c->src[0] == a);
assert(c->src[1] == b);
}

The multi-dimensional tensors are stored in row-major order. The ggml_tensor struct contains fields for the number of elements in each dimension (“ne”) as well as the number of bytes (“nb”, a.k.a. stride). This allows to store tensors that are not contiguous in memory, which is useful for operations such as transposition and permutation. All tensor operations have to take the stride into account and not assume that the tensor is contiguous in memory.

The data of the tensor is accessed via the “data” pointer. For example:

{
const int nx = 2;
const int ny = 3;

struct ggml_tensor * a = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, nx, ny);

for (int y = 0; y < ny; y++) {
for (int x = 0; x < nx; x++) {
*(float *) ((char *) a->data + y*a->nb[1] + x*a->nb[0]) = x + y;
}
}

...
}

Alternatively, there are helper functions, such as ggml_get_f32_1d() and ggml_set_f32_1d() that can be used.

<think>好的,用户现在遇到了问题,之前他们尝试下载quantize.py文件时遇到了404错误,现在发现scripts目录下已经没有这个文件了。我需要先确认这个文件是否真的被移除了,或者移动到了其他位置。 首先,我应该检查llama.cpp的最新仓结构。访问用户提供的链接,https://github.com/ggml-org/llama.cpp/tree/master/scripts,看看scripts目录下有没有quantize.py。如果确实没有,可能开发者已经将这个脚本移到了其他地方或者删除了。 接下来,我需要考虑用户的需求。用户可能是在尝试对模型进行量化,而quantize.py脚本原本是用来做这个的。现在文件不见了,用户需要替代方法来完成量化任务。 可能的解决方案有几个方向: 1. 检查仓的提交历史,看看quantize.py是否被移动到其他目录,比如可能移到了项目根目录或者其他子目录中。 2. 确认项目是否改用其他工具或可执行文件来进行量化,比如使用编译后的quantize可执行文件,而不是Python脚本。 3. 查看项目的文档或最近的更新日志,了解quantize.py被移除的原因和替代方法。 4. 如果确实找不到文件,可能需要用户使用其他方式,比如从旧版本的仓中获取quantize.py,或者使用git回退到有该文件的版本。 另外,用户之前的问题是关于Invoke-WebRequest命令的404错误,现在确认文件不存在,可能需要更新他们的操作流程,使用新的方法进行量化。需要确保用户了解最新的项目结构变化,以及如何正确使用现有的工具。 还需要考虑用户可能不熟悉如何查找提交历史或编译项目,所以在回答中需要给出具体的步骤和命令,便于用户操作。例如,如何克隆仓,如何查找文件的历史记录,如何编译生成quantize可执行文件,以及如何使用它进行模型量化。 另外,可能需要提醒用户注意版本兼容性,确保他们使用的模型文件与quantize工具的版本匹配,避免出现错误。 最后,确保回答结构清晰,分步骤说明解决方案,并给出验证方法,让用户能够逐步操作并确认结果。</think>**原因分析与解决方案** 您遇到的是llama.cpp仓结构调整导致的问题,`quantize.py`文件已被移除(2023年11月后的更新)。新的量化流程已改为使用C++实现的可执行文件。 **分步操作指南** 1. **编译量化工具** ```powershell # 克隆最新仓(注意组织名已变更为ggml-org) git clone https://github.com/ggml-org/llama.cpp cd llama.cpp # 创建编译目录 mkdir build cd build # 编译量化工具(Windows需安装CMake和Visual Studio) cmake .. -DLLAMA_CUBLAS=ON # 启用CUDA加速(可选) cmake --build . --config Release ``` 2. **使用新量化工具** 编译完成后会产生`quantize`可执行文件(Windows为`quantize.exe`): ```powershell # 量化命令格式 .\quantize.exe <输入模型路径> <输出模型路径> <量化类型> # 实际示例(需先准备好原始模型) .\quantize.exe ..\models\ggml-model-f16.gguf ..\models\ggml-model-q4_0.gguf q4_0 ``` **关键验证步骤** ```powershell # 查看量化工具版本 .\quantize.exe --version # 正确输出示例: quantize.exe (build 2380) (c) 2024 ggml-org ``` **备选历史文件获取方案** 若必须使用旧版Python脚本: ```powershell # 回退到2023-10-30版本 git checkout b1527 cp scripts/quantize.py ./ # 保存到当前目录 git checkout main # 返回最新版本 ``` **量化类型对照表** | 类型 | 适用场景 | 模型大小缩减率 | |--------|-------------------|----------------| | q4_0 | 平衡性能/质量 | ~75% | | q5_0 | 高质量推理 | ~65% | | q2_k | 超低资源设备 | ~90% | **常见错误处理** ```text 错误: Could not find model file 解决方案:确保原始模型是GGUF格式(使用convert.py转换) 错误: Not enough memory 解决方案:添加--mmap参数(内存映射模式) ``` **完整工作流示例** ```powershell # 从HuggingFace下载原始模型 huggingface-cli download TheBloke/Llama-2-7B-GGUF llama-2-7b.Q4_0.gguf # 量化操作(实际无需二次量化,此处演示流程) .\quantize.exe llama-2-7b.Q4_0.gguf llama-2-7b.Q2_K.gguf q2_k # 运行量化后模型 .\main.exe -m llama-2-7b.Q2_K.gguf -p "你好" ``` **性能优化参数** ```powershell # 使用GPU加速(需编译时启用CUDA) .\quantize.exe --use-cuda input.gguf output.gguf q4_0 # 多线程处理 .\quantize.exe -t 8 input.gguf output.gguf q4_0 ``` **新旧方案对比** | 特性 | 旧Python脚本 | 新C++工具 | |--------------|----------------------------|---------------------------| | 速度 | 慢(Python解释型) | 快(C++编译型) | | 内存占用 | 高 | 低 | | 量化精度控制 | 细粒度参数 | 预设类型 | | 硬件加速 | 有限 | 支持CUDA/Metal | | 模型兼容性 | 仅支持旧格式 | 支持GGUF最新格式 | **文档参考位置** 1. [官方量化指南](https://github.com/ggml-org/llama.cpp/blob/master/examples/quantize/README.md) 2. [GGUF格式说明](https://github.com/ggml-org/ggml/blob/master/docs/gguf.md)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值