├── .gitmodules ├── README.md └── notes ├── figture ├── ggml-structure.drawio ├── ggml-structure.jpg └── ggml-structure.png ├── ggml学习笔记(一)项目编译.md ├── ggml学习笔记(三)gguf文件格式.md └── ggml学习笔记(二)ggml.h源码解读.md /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ggml"] 2 | path = ggml 3 | url = https://github.com/ggerganov/ggml 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## GGML 2 | 3 | > [**ggml**](https://github.com/ggerganov/ggml) is a tensor library for machine learning to enable large models and high performance on commodity hardware. It is used by [llama.cpp](https://github.com/ggerganov/llama.cpp) and [whisper.cpp](https://github.com/ggerganov/whisper.cpp) 4 | 5 | 6 | ggml是用c语言实现的一套张量操作库, 官网在[这里](https://ggml.ai)。当前有以下优点 7 | 8 | - 纯c实现 9 | - 支持16bit浮点数 10 | - 支持整数量化 (比如4-bit, 5-bit, 8-bit) 11 | - 可以自动微分 12 | - 内置优化算法 (例如 ADAM, L-BFGS) 13 | - 苹果m系列芯片优化 14 | - x86平台 AVX / AVX2 指令集优化 15 | - 通过WebAssembly和 WASM SIMD技术可以跑在浏览器中 16 | - 没有第三方库依赖 17 | - 运行时不在新分配内存 18 | 19 | 现支持的后端有 20 | - CPU 21 | - CUDA 22 | - Metal 23 | - OpenCL 24 | - Vulkan 25 | - SYCL 26 | - Kompute 27 | 28 | 29 | 深度学习的推理框架有很多,现在主流的推理框架包括:[TensorRT](https://developer.nvidia.com/tensorrt),[ONNXRuntime](https://onnxruntime.ai/),[OpenVINO](https://link.zhihu.com/?target=https%3A//www.intel.com/content/www/us/en/developer/tools/openvino-toolkit/overview.html),[ncnn](https://link.zhihu.com/?target=https%3A//github.com/Tencent/ncnn),[MNN](https://link.zhihu.com/?target=https%3A//github.com/alibaba/MNN) 等。在知乎上也有关于深度学习推理框架的讨论[如何选择深度学习推理框架](https://www.zhihu.com/question/346965029)。 30 | 31 | | 框架 | 算子数 | 平台 | 32 | |-------------|----------------------------------------------------------------------|-----------| 33 | | onnxruntime | 164个算子 | 多平台 | 34 | | ncnn | 约197个算子 | 多平台,主打移动端 | 35 | | mnn | 支持 178 个Tensorflow Op、52个 Caffe Op、163个 Torchscipts Op、158 个 ONNX Op | 多平台 | 36 | | TensorRT | 大部分onnx算子,可直接加载onnx格式 | N卡 | 37 | | OpenVINO | 180个算子,可直接加载onnx格式 | intel的cpu | 38 | | ggml | 73个算子 | 多平台 | 39 | 40 | 41 | 国内也有着好几个非常优秀的推理框架,例如腾讯的ncnn起步早且社区活跃,支持平台广泛,主打移动端部署,还有阿里的mnn,都非常优秀。 42 | 43 | 本着调研与学习的目的,这里选择了最简单的ggml作为入门学习框架, ggml也是作为了whisper.cpp和llama.cpp的后端推理框架。 44 | 目前为止,当前该库仍在开发中,几个月不见又增加很多新的工作,废弃了一些接口。 45 | 并且ggml的官方文档还是一个TODO状态, 46 | 本项目的出新在于学习当前一些主流的推理框架,为后续深度学习模型落地做准备。 47 | 48 | 49 | 50 | 51 | 52 | [ggml学习笔记(一)项目编译.md](notes/ggml学习笔记(一)项目编译.md) 53 | 54 | [ggml学习笔记(二)ggml.h源码解读.md](notes/ggml学习笔记(二)ggml.h源码解读.md) 55 | 56 | [ggml学习笔记(三)gguf文件格式.md](notes/ggml学习笔记(三)gguf文件格式.md) 57 | -------------------------------------------------------------------------------- /notes/figture/ggml-structure.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /notes/figture/ggml-structure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovemefan/ggml-learning-notes/1763512fdfb9d3b9c248530d9851580bd34ba970/notes/figture/ggml-structure.jpg -------------------------------------------------------------------------------- /notes/figture/ggml-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lovemefan/ggml-learning-notes/1763512fdfb9d3b9c248530d9851580bd34ba970/notes/figture/ggml-structure.png -------------------------------------------------------------------------------- /notes/ggml学习笔记(一)项目编译.md: -------------------------------------------------------------------------------- 1 | # ggml学习笔记(一)项目编译 2 | 3 | ## 项目简介 4 | 5 | 核心代码如下: 6 | 7 | ``` 8 | . 9 | ├── CMakeLists.txt 10 | ├── LICENSE 11 | ├── README.md 12 | ├── ci 13 | │ └── run.sh 14 | ├── cmake 15 | │ ├── BuildTypes.cmake 16 | │ └── GitVars.cmake 17 | ├── ggml.pc.in 18 | ├── include 19 | │ └── ggml 20 | │ ├── ggml-alloc.h 21 | │ ├── ggml-backend.h 22 | │ └── ggml.h 23 | │ 24 | ├── requirements.txt 25 | └── src 26 | ├── CMakeLists.txt 27 | ├── ggml-alloc.c 28 | ├── ggml-backend-impl.h 29 | ├── ggml-backend.c 30 | ├── ggml-common.h 31 | ├── ggml-cuda.cu 32 | ├── ggml-cuda.h 33 | ├── ggml-impl.h 34 | ├── ggml-kompute.cpp 35 | ├── ggml-kompute.h 36 | ├── ggml-metal.h 37 | ├── ggml-metal.m 38 | ├── ggml-metal.metal 39 | ├── ggml-opencl.cpp 40 | ├── ggml-opencl.h 41 | ├── ggml-quants.c 42 | ├── ggml-quants.h 43 | ├── ggml-sycl.cpp 44 | ├── ggml-sycl.h 45 | ├── ggml-vulkan.cpp 46 | ├── ggml-vulkan.h 47 | └── ggml.c 48 | 49 | 50 | 51 | ``` 52 | 53 | 可以看到ggml支持cpu、opencl、cuda、metal后端,ggml目前还没有多线程支持。 54 | 55 | ### 编译宏介绍 56 | 57 | | 宏名 | 宏说明 | 58 | | --------------------------- | ------------------------------------------------------------ | 59 | | GGML_ALL_WARNINGS | 是否开启编译的警告信息,默认`ON` | 60 | | GGML_ALL_WARNINGS_3RD_PARTY | 是否开启第三方库的警告信息,默认`OFF` | 61 | | GGML_SANITIZE_THREAD | 是否开启线程检查工具,默认`OFF` | 62 | | GGML_SANITIZE_ADDRESS | 是否开启内存检查工具,默认`OFF` | 63 | | GGML_SANITIZE_UNDEFINED | 是否对未定义行为检测,默认`OFF` | 64 | | GGML_BUILD_TESTS | 构建测试,读取环境变量值`GGML_STANDALONE`,如果项目的根源目录(`CMAKE_SOURCE_DIR`)等于当前处理的CMake文件所在的目录(`CMAKE_CURRENT_SOURCE_DIR`),`GGML_STANDALONE`为`ON` | 65 | | GGML_BUILD_EXAMPLES | 同上 | 66 | | GGML_TEST_COVERAGE | 使用测试用例,默认`OFF` | 67 | | GGML_PERF | 是否使用pert性能分析工具,用于查看cpu、内存等指标,默认`OFF` | 68 | | GGML_NO_ACCELERATE | 禁用加速框架,默认`OFF` | 69 | | GGML_OPENBLAS | 是否使用高性能OpenBLAS线性代数库,OpenBLAS 在许多科学计算和数据分析库中被广泛使用,比如NumPy、SciPy等。默认`OFF` | 70 | | GGML_CLBLAST | 是否使用高性能线性代数CLBLAST库,专门用于在OpenCL(Open Computing Language)环境中进行高性能的基本线性代数操作。默认`OFF` | 71 | | GGML_CUBLAS | 是 NVIDIA CUDA 平台上的一个高性能线性代数库。默认`OFF` | 72 | | GGML_METAL | 是否使用苹果的metal后端,默认`OFF` | 73 | | GGML_AVX | 使用avx指令集优化,它引入了更宽的128位向量寄存器,允许单个指令同时处理更多的数据元素,从而提高并行计算能力。,默认`ON` | 74 | | GGML_AVX2 | 使用avx2指令集优化,AVX2 是对 AVX 的扩展,引入于Haswell微架构。它进一步增强了向量化能力。默认`ON` | 75 | | GGML_AVX512 | 使用avx512指令集优化,引入了更宽的512位向量寄存器,从而允许处理更多的数据元素同时进行高级的并行计算,只支持特定的英特尔处理器,如 Skylake、Cannon Lake、Ice Lake 等。默认`OFF` | 76 | | GGML_AVX512_VBMI | 使用AVX512_VBMI指令集优化,AVX-512 VBMI" 是指 AVX-512 指令集中的 Vector Byte Manipulation Instructions 扩展。VBMI 是 AVX-512 指令集的一部分,它引入了一组用于高效处理字节操作的指令,旨在加速字节级别的数据处理。默认`OFF` | 77 | | GGML_AVX512_VNNI | 使用GGML_AVX512_VNNI指令集优化,AVX-512 VNNI(Vector Neural Network Instructions)是英特尔处理器上的 AVX-512 指令集中的一个重要扩展,专门用于加速神经网络计算,特别是卷积神经网络(CNN)的操作。AVX-512 VNNI 扩展通常仅在支持该指令集的特定英特尔处理器上可用。这些处理器通常是为数据中心和高性能计算任务设计的。默认`OFF` | 78 | | GGML_FMA | 是否开启FMA,FMA代表“Fused Multiply-Add”,它是一种在单个操作中执行浮点数乘法和加法的硬件指令,通过将乘法和加法结合在一起,FMA指令在一条指令周期内执行多个操作,从而显著提高了计算效率。默认`ON` | 79 | | GGML_F16C | 使用F16C指令集优化,F16C 是一种英特尔处理器的指令集扩展,它主要用于在硬件级别上支持半精度(16位)浮点数操作。这种指令集扩展的目标是提供一种更高效地处理低精度浮点数的方式,以在一些应用中获得更好的性能和能效。仅在AVX2和AVX512指令集下默认`ON` | 80 | 81 | 82 | 83 | ## 源码编译 84 | 85 | 目前只支持linux和winsow,因为cmake里面只写了gnucc和clang编译器代码部分 86 | 环境要求: 87 | 88 | * cmake >= 3.0 89 | * gcc >= 4.9 90 | 91 | ``` 92 | mkdir build && cd build && cmake .. && make -j8 93 | ``` 94 | 95 | ### 本人遇到的问题 96 | 97 | 1. 遇到的问题: fatal error: stdatomic.h: No such file or directory 98 | 99 | ​ 原因:gcc版本过低, 需要升级gcc 100 | 101 | 102 | 103 | 2. 遇到问题: 在aarch64非苹果平台编译报错,selected processor does not support `fadd h0,h1,h0' 104 | 105 | 原因:暂不支持该平台, -------------------------------------------------------------------------------- /notes/ggml学习笔记(三)gguf文件格式.md: -------------------------------------------------------------------------------- 1 | ## GGUF是什么 2 | 3 | > ***原文来自 [gguf.md](https://github.com/ggerganov/ggml/blob/master/docs/gguf.md)*** 4 | 5 | GGUF (GGML Universal File) 是用于存储 GGML 模型的二进制文件格式,旨在快速加载和保存模型。大多情况下模型都是使用 PyTorch 训练得到的,因此需要将pytorch的checkpoint转换为 GGUF 以在 GGML 中使用,gguf格式文件通常使用python库[gguf](https://github.com/ggerganov/llama.cpp/blob/master/gguf-py/README.md)写入,c++读取。 6 | 7 | GGUF 是 GGML、GGMF 和 GGJT 的后继文件格式,旨在通过保存加载模型所需的所有信息来确保无歧义。并且它还被设计为可扩展的,因此可以向模型添加新信息而不会破坏与现有模型的兼容性。 8 | 9 | 10 | 11 | GGUF 是基于现有 GGJT 的格式,但对格式进行了一些更改,以使其更易于扩展和使用: 12 | 13 | - 单文件部署:它们可以轻松分发和加载,并且不需要任何外部文件提供额外信息(因为gguf文件打包了超参数,词表,模型权重)。 14 | - 可扩展:可以向基于 GGML 的代码添加新功能/向 GGUF 模型添加新信息,而不会破坏与现有模型的兼容性(原来GGJT格式的模型一旦被修改就可能会导致代码报错,不兼容)。 15 | - `mmap` 兼容性:可以使用 `mmap` 快速加载和保存模型。 16 | - 易于使用:可以使用少量代码轻松加载和保存模型,而无论使用的语言是什么,都不需要外部库。 17 | - 完整信息:加载模型所需的所有信息都包含在模型文件中,用户不需要提供任何其他信息。 18 | 19 | GGJT 和 GGUF 之间的主要区别是超参数(现在称为元数据)使用键值结构,而不是无类型值列表。这允许添加新的元数据而不会破坏与现有模型的兼容性,并且可以使用附加信息注释模型,这些信息对于推理或标识模型可能会很有用。 20 | 21 | ### 文件结构 22 | 23 | **gguf文件保存了模型的超参、词表、模型权重**,gguf主要分为三个部分: 24 | 25 | - 文件头 26 | - 元数据的键值对,可用于保存超参,词表 27 | - 模型权重的键值对 28 | 29 | ![](https://github.com/ggerganov/ggml/assets/1991296/c3623641-3a1d-408e-bfaf-1b7c4e16aa63) 30 | 31 | *图示由 [@mishig25](https://github.com/mishig25)(GGUF v3)* 32 | 33 | 34 | 35 | GGUF 文件的结构如上图。它们使用在下面称为 `ALIGNMENT` 的 `general.alignment` 元数据字段中指定的全局对齐。在需要时,文件用 `0x00` 字节填充到下一个 `general.alignment` 的倍数。 36 | 37 | 字段,包括数组,是按顺序写入的,除非另有说明。 38 | 39 | 模型默认是小端的。它们也可以以大端形式提供,以与大端计算机一起使用;在这种情况下,所有值(包括元数据值和张量)也将是大端的。在撰写本文时,没有办法确定模型是否为大端;这可能在未来版本中得到解决。如果未提供其他信息,则假定模型是小端的。 40 | 41 | 42 | 43 | ```c++ 44 | enum ggml_type: uint32_t { 45 | GGML_TYPE_F32 = 0, 46 | GGML_TYPE_F16 = 1, 47 | GGML_TYPE_Q4_0 = 2, 48 | GGML_TYPE_Q4_1 = 3, 49 | // GGML_TYPE_Q4_2 = 4, support has been removed 50 | // GGML_TYPE_Q4_3 (5) support has been removed 51 | GGML_TYPE_Q5_0 = 6, 52 | GGML_TYPE_Q5_1 = 7, 53 | GGML_TYPE_Q8_0 = 8, 54 | GGML_TYPE_Q8_1 = 9, 55 | // k-quantizations 56 | GGML_TYPE_Q2_K = 10, 57 | GGML_TYPE_Q3_K = 11, 58 | GGML_TYPE_Q4_K = 12, 59 | GGML_TYPE_Q5_K = 13, 60 | GGML_TYPE_Q6_K = 14, 61 | GGML_TYPE_Q8_K = 15, 62 | GGML_TYPE_I8, 63 | GGML_TYPE_I16, 64 | GGML_TYPE_I32, 65 | GGML_TYPE_COUNT, 66 | }; 67 | 68 | enum gguf_metadata_value_type: uint32_t { 69 | // The value is a 8-bit unsigned integer. 70 | GGUF_METADATA_VALUE_TYPE_UINT8 = 0, 71 | // The value is a 8-bit signed integer. 72 | GGUF_METADATA_VALUE_TYPE_INT8 = 1, 73 | // The value is a 16-bit unsigned little-endian integer. 74 | GGUF_METADATA_VALUE_TYPE_UINT16 = 2, 75 | // The value is a 16-bit signed little-endian integer. 76 | GGUF_METADATA_VALUE_TYPE_INT16 = 3, 77 | // The value is a 32-bit unsigned little-endian integer. 78 | GGUF_METADATA_VALUE_TYPE_UINT32 = 4, 79 | // The value is a 32-bit signed little-endian integer. 80 | GGUF_METADATA_VALUE_TYPE_INT32 = 5, 81 | // The value is a 32-bit IEEE754 floating point number. 82 | GGUF_METADATA_VALUE_TYPE_FLOAT32 = 6, 83 | // The value is a boolean. 84 | // 1-byte value where 0 is false and 1 is true. 85 | // Anything else is invalid, and should be treated as either the model being invalid or the reader being buggy. 86 | GGUF_METADATA_VALUE_TYPE_BOOL = 7, 87 | // The value is a UTF-8 non-null-terminated string, with length prepended. 88 | GGUF_METADATA_VALUE_TYPE_STRING = 8, 89 | // The value is an array of other values, with the length and type prepended. 90 | /// 91 | // Arrays can be nested, and the length of the array is the number of elements in the array, not the number of bytes. 92 | GGUF_METADATA_VALUE_TYPE_ARRAY = 9, 93 | // The value is a 64-bit unsigned little-endian integer. 94 | GGUF_METADATA_VALUE_TYPE_UINT64 = 10, 95 | // The value is a 64-bit signed little-endian integer. 96 | GGUF_METADATA_VALUE_TYPE_INT64 = 11, 97 | // The value is a 64-bit IEEE754 floating point number. 98 | GGUF_METADATA_VALUE_TYPE_FLOAT64 = 12, 99 | } 100 | 101 | // A string in GGUF. 102 | struct gguf_string_t { 103 | // The length of the string, in bytes. 104 | uint64_t len; 105 | // The string as a UTF-8 non-null-terminated string. 106 | char string[len]; 107 | } 108 | 109 | union gguf_metadata_value_t { 110 | uint8_t uint8; 111 | int8_t int8; 112 | uint16_t uint16; 113 | int16_t int16; 114 | uint32_t uint32; 115 | int32_t int32; 116 | float float32; 117 | uint64_t uint64; 118 | int64_t int64; 119 | double float64; 120 | bool bool_; 121 | gguf_string_t string; 122 | struct { 123 | // Any value type is valid, including arrays. 124 | gguf_metadata_value_type type; 125 | // Number of elements, not bytes 126 | uint64_t len; 127 | // The array of values. 128 | gguf_metadata_value_t array[len]; 129 | } array; 130 | }; 131 | 132 | struct gguf_metadata_kv_t { 133 | // The key of the metadata. It is a standard GGUF string, with the following caveats: 134 | // - It must be a valid ASCII string. 135 | // - It must be a hierarchical key, where each segment is `lower_snake_case` and separated by a `.`. 136 | // - It must be at most 2^16-1/65535 bytes long. 137 | // Any keys that do not follow these rules are invalid. 138 | gguf_string_t key; 139 | 140 | // The type of the value. 141 | // Must be one of the `gguf_metadata_value_type` values. 142 | gguf_metadata_value_type value_type; 143 | // The value. 144 | gguf_metadata_value_t value; 145 | }; 146 | 147 | struct gguf_header_t { 148 | // Magic number to announce that this is a GGUF file. 149 | // Must be `GGUF` at the byte level: `0x47` `0x47` `0x55` `0x46`. 150 | // Your executor might do little-endian byte order, so it might be 151 | // check for 0x46554747 and letting the endianness cancel out. 152 | // Consider being *very* explicit about the byte order here. 153 | uint32_t magic; 154 | // The version of the format implemented. 155 | // Must be `3` for version described in this spec, which introduces big-endian support. 156 | // 157 | // This version should only be increased for structural changes to the format. 158 | // Changes that do not affect the structure of the file should instead update the metadata 159 | // to signify the change. 160 | uint32_t version; 161 | // The number of tensors in the file. 162 | // This is explicit, instead of being included in the metadata, to ensure it is always present 163 | // for loading the tensors. 164 | uint64_t tensor_count; 165 | // The number of metadata key-value pairs. 166 | uint64_t metadata_kv_count; 167 | // The metadata key-value pairs. 168 | gguf_metadata_kv_t metadata_kv[metadata_kv_count]; 169 | }; 170 | 171 | uint64_t align_offset(uint64_t offset) { 172 | return offset + (ALIGNMENT - (offset % ALIGNMENT)) % ALIGNMENT; 173 | } 174 | 175 | struct gguf_tensor_info_t { 176 | // The name of the tensor. It is a standard GGUF string, with the caveat that 177 | // it must be at most 64 bytes long. 178 | gguf_string_t name; 179 | // The number of dimensions in the tensor. 180 | // Currently at most 4, but this may change in the future. 181 | uint32_t n_dimensions; 182 | // The dimensions of the tensor. 183 | uint64_t dimensions[n_dimensions]; 184 | // The type of the tensor. 185 | ggml_type type; 186 | // The offset of the tensor's data in this file in bytes. 187 | // 188 | // This offset is relative to `tensor_data`, not to the start 189 | // of the file, to make it easier for writers to write the file. 190 | // Readers should consider exposing this offset relative to the 191 | // file to make it easier to read the data. 192 | // 193 | // Must be a multiple of `ALIGNMENT`. That is, `align_offset(offset) == offset`. 194 | uint64_t offset; 195 | }; 196 | 197 | struct gguf_file_t { 198 | // The header of the file. 199 | gguf_header_t header; 200 | 201 | // Tensor infos, which can be used to locate the tensor data. 202 | gguf_tensor_info_t tensor_infos[header.tensor_count]; 203 | 204 | // Padding to the nearest multiple of `ALIGNMENT`. 205 | // 206 | // That is, if `sizeof(header) + sizeof(tensor_infos)` is not a multiple of `ALIGNMENT`, 207 | // this padding is added to make it so. 208 | // 209 | // This can be calculated as `align_offset(position) - position`, where `position` is 210 | // the position of the end of `tensor_infos` (i.e. `sizeof(header) + sizeof(tensor_infos)`). 211 | uint8_t _padding[]; 212 | 213 | // Tensor data. 214 | // 215 | // This is arbitrary binary data corresponding to the weights of the model. This data should be close 216 | // or identical to the data in the original model file, but may be different due to quantization or 217 | // other optimizations for inference. Any such deviations should be recorded in the metadata or as 218 | // part of the architecture definition. 219 | // 220 | // Each tensor's data must be stored within this array, and located through its `tensor_infos` entry. 221 | // The offset of each tensor's data must be a multiple of `ALIGNMENT`, and the space between tensors 222 | // should be padded to `ALIGNMENT` bytes. 223 | uint8_t tensor_data[]; 224 | }; 225 | ``` 226 | 227 | 228 | 229 | ## 如何自定义gguf文件 230 | 231 | 官方已经提供了一个python库 [gguf](https://github.com/ggerganov/llama.cpp/tree/master/gguf-py)用于写入gguf文件,安装如下 232 | 233 | ```bash 234 | pip install gguf 235 | ``` 236 | 237 | 官方还提供了一个转换huggingface模型的脚本[convert-hf-to-gguf.py](https://github.com/ggerganov/llama.cpp/blob/master/convert-hf-to-gguf.py), 以供参考。如果想更加灵活的使用gguf,可以参考一下[源码](https://github.com/ggerganov/llama.cpp/blob/master/gguf-py/gguf/gguf_writer.py)。 238 | 239 | 240 | 241 | 以下给出一个简单的案例: 242 | 243 | ```python 244 | from gguf import GGUFWriter 245 | 246 | def main(): 247 | gguf_writer = GGUFWriter("example.gguf", "llama") 248 | 249 | gguf_writer.add_architecture() # 模型架构为GGUFWriter的第二个参数,即`llama` 250 | gguf_writer.add_block_count(12) # llama模型的模块数,专为LLM设计 251 | gguf_writer.add_uint32("answer", 42) # 添加一个32-bit integer 类型的超参 252 | gguf_writer.add_float32("answer_in_float", 42.0) # 添加一个32-bit float类型的超参 253 | gguf_writer.add_custom_alignment(64) # 设置自定义对齐值为64 254 | 255 | tensor1 = np.ones((32,), dtype=np.float32) * 100.0 256 | tensor2 = np.ones((64,), dtype=np.float32) * 101.0 257 | tensor3 = np.ones((96,), dtype=np.float32) * 102.0 258 | 259 | gguf_writer.add_tensor("tensor1", tensor1) # 添加一个模型的tensor权重 260 | gguf_writer.add_tensor("tensor2", tensor2) 261 | gguf_writer.add_tensor("tensor3", tensor3) 262 | 263 | gguf_writer.write_header_to_file() # 写入头文件 264 | gguf_writer.write_kv_data_to_file() # 写入所有的键值对值, 包括超参等 265 | gguf_writer.write_tensors_to_file() # 写入所有的tensor 权重 266 | 267 | gguf_writer.close() 268 | ``` 269 | 270 | 271 | 272 | 更多进阶用法可参考[源码](https://github.com/ggerganov/llama.cpp/blob/master/gguf-py/gguf/gguf_writer.py),该库添加了大量为LLM专门设计的键值对,如果想要更加灵活的设计gguf文件格式 273 | 274 | 只需要使用类似gguf_writer.add_uint32等更底层的函数即可,以下列出一些常用的函数就能满足各种超参个词表的定义: 275 | 276 | 277 | - add_uint8(self, key: str, val: int) -> None 278 | - add_int8(self, key: str, val: int) -> None 279 | - add_uint16(self, key: str, val: int) -> None 280 | - add_int16(self, key: str, val: int) -> None 281 | - add_uint32(self, key: str, val: int) -> None 282 | - add_int32(self, key: str, val: int) -> None 283 | - add_float32(self, key: str, val: float) -> None 284 | - add_uint64(self, key: str, val: int) -> None 285 | - add_int64(self, key: str, val: int) -> None 286 | - add_float64(self, key: str, val: float) -> None 287 | - add_bool(self, key: str, val: bool) -> None 288 | - add_string(self, key: str, val: str) -> None 289 | - add_array(self, key: str, val: Sequence[Any]) -> None 290 | 291 | -------------------------------------------------------------------------------- /notes/ggml学习笔记(二)ggml.h源码解读.md: -------------------------------------------------------------------------------- 1 | # ggml学习笔记(二)头文件源码解读 2 | 3 | ## 头文件 4 | 5 | ``` 6 | ggml/include 7 | └── ggml 8 | ├── ggml-alloc.h // 用于分配tensor,计算图的内存 9 | ├── ggml-backend.h // 适配多种后端 10 | └── ggml.h 11 | ``` 12 | 13 | 14 | 15 | ## 概念介绍 16 | 17 | ggml.h 包含了一系列的计算图构建、张量的算子操作、自动微分、以及基础的优化算法。 18 | 19 | 1. **计算图(Computation Graph)**:计算图,也称为计算图或数据流图,是数学操作的表示,其中节点代表操作(例如加法、乘法)或函数,边代表这些操作之间的数据流动(张量或变量)。计算图用于定义和可视化复杂数学函数中的数据和操作流动。 20 | 21 | 2. **张量操作(Tensor Operations)**:张量操作是对张量执行的数学操作。张量是多维数组,可以表示各种类型的数据,如标量、向量、矩阵或更高维数组。张量操作可以包括加法、乘法、卷积和其他在机器学习和数值计算中常用的操作。 22 | 23 | 3. **函数定义(Function Definition)**:在你描述的上下文中,函数是使用计算图中的一系列张量操作来定义的。该函数表示一个数学模型,将输入数据(张量)映射到输出,并且可能会相当复杂,特别是在深度学习模型中。 24 | 25 | 4. **梯度(Gradient)**:计算函数相对于其输入变量的梯度对于优化和训练机器学习模型至关重要。梯度提供了关于函数的输出如何随其输入变化而变化的信息。它在优化算法(如梯度下降)中使用,以迭代方式更新模型参数并最小化损失函数。 26 | 27 | 5. **优化算法(Optimization Algorithms)**:优化算法用于调整函数的参数(例如神经网络权重),以最小化或最大化某个特定目标(例如最小化损失函数)。常见的优化算法包括梯度下降、Adam、RMSprop等。这些算法使用梯度信息以迭代方式更新参数,直到收敛。 28 | 29 | 6. **前向计算(Forward Computation)**:前向计算函数接受输入张量的值,并根据定义的张量操作计算输出张量的值。这是对输入执行一系列操作以获得输出的过程,通常是模型的正向传播。 30 | 31 | 7. **反向计算、反向传播(Backward Computation)**:反向计算函数接受输出张量的伴随值(也称为梯度)并计算输入张量的伴随值。这是为了计算函数相对于输入的梯度,通常是为了在优化过程中更新模型参数(例如使用反向传播算法) 32 | 33 | 8. **行优先储存(Row-Major Order)**:行优先是一种多维数组元素存储方式,其中数组的元素按照行的顺序存储在内存中。这意味着每一行的元素都相邻存储,而不同行之间的元素可能不相邻。 34 | 35 | GGML将数学函数定义为计算图,在这些图中执行张量操作,计算函数值,计算梯度,并使用各种优化算法优化函数。这种库在机器学习和科学计算中非常基础,用于构建和训练模型。 36 | 37 | ![ggml-structure](/Users/cenglingfan/Code/cpp-project/ggml-learning-notes/notes/figture/ggml-structure.png) 38 | 39 | ## 官方案例 40 | 41 | 42 | 43 | 定义一个前向计算$f(x)=ax^2 + b$,下列代码定义了参数初始化的过程和前向计算过程。 44 | 45 | ```c 46 | // 例如函数 f(x) = a*x^2 + b 47 | // 其中 a,b为模型参数,x为模型输入变量 48 | // 需要提前定义好需要的内存, 49 | // 需要注意后续定义的张量的大小不能超过自定义的内存大小 50 | // 可以使用ggml_used_mem()来查看当前实际使用的内存大小 51 | struct ggml_init_params params = { 52 | .mem_size = 16*1024*1024, 53 | .mem_buffer = NULL, 54 | }; 55 | 56 | // 申请内存,初始化参数, ggml会一次性申请够内存量, 57 | // 后续所有的计算开销缓存都会共享这块内存, 58 | // 为了避免用户使用过程中内存泄露 59 | struct ggml_context * ctx = ggml_init(params); 60 | // 初始化输入x, x为输入变量 61 | // ggml_new_tensor_1d创建一个1维的张量, 62 | // 支持多维张量,ggml_new_tensor_1d、ggml_new_tensor_2d、ggml_new_tensor_3d、ggml_new_tensor_4d 63 | // ggml_new_tensor_1d将在ggml_init申请的内存中申请一块存储空间 64 | struct ggml_tensor * x = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1); 65 | 66 | // ggml_set_param函数用于标记当前的变量为输入变量, 67 | // 会在计算梯度和优化算法中使用到 68 | ggml_set_param(ctx, x); 69 | 70 | // ggml_new_tensor_1d创建一个1维的张量, 在ctx中申请一块空间 71 | struct ggml_tensor * a = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1); 72 | struct ggml_tensor * b = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1); 73 | // 所有的算子会返回一个新的张量, 74 | // 并且每个算子都需要实现前向计算和后向传播(梯度计算) 75 | struct ggml_tensor * x2 = ggml_mul(ctx, x, x); 76 | struct ggml_tensor * f = ggml_add(ctx, ggml_mul(ctx, a, x2), b); 77 | 78 | ``` 79 | 80 | ​ 上诉代码仅仅只是完成参数创建及定义,和计算图的定义,并没有进行前向计算。相当于建立模型但是还没有初始化参数。 81 | 82 | ​ ggml中的多维张量以行优先(row-major order)的方式存储。ggml_tensor 结构包含了每个维度中元素的数量("ne", 类似于tensor.size)以及字节数("nb",也叫做步幅)。这使得可以存储在内存中不是连续的张量,这对于诸如转置和排列等操作非常有用。所有的张量操作都必须考虑步幅,并且不应该假设张量在内存中是连续的。 83 | 84 | ​ 接下来是初始化参数和前向推理代码, 其中ggml_graph_compute才是实际模型前向计算。 85 | 86 | ```c 87 | //构建计算图 88 | struct ggml_cgraph gf = ggml_build_forward(f); 89 | 90 | // 设置x输入为浮点数2.0 91 | ggml_set_f32(x, 2.0f); 92 | 93 | // 初始化模型 94 | // 初始化a参数为浮点数3.0 95 | ggml_set_f32(a, 3.0f); 96 | // 初始化b为浮点数4.0 97 | ggml_set_f32(b, 4.0f); 98 | 99 | // 前向计算 100 | ggml_graph_compute_with_ctx(ctx, &gf, n_threads); 101 | 102 | printf("f = %f\n", ggml_get_f32_1d(f, 0)); 103 | ``` 104 | 105 | 106 | 107 | 108 | 109 | ### 宏介绍 110 | 111 | | 宏名 | 宏说明 | 112 | | ----------------------- | -------------------------------------- | 113 | | GGML_FILE_MAGIC | ggml文件格式的magic?,值`0x67676d6c` | 114 | | GGML_FILE_VERSION | ggml文件格式版本号,值`1` | 115 | | GGML_QNT_VERSION | ggml量化版本号,值`1` | 116 | | GGML_QNT_VERSION_FACTOR | ggml量化参数,暂时不是怎么用,值`1000` | 117 | | GGML_MAX_DIMS | ggml最大支持张量的维度,值`4` | 118 | | GGML_MAX_NODES | ggml静态图支持的最大节点数,值`4096` | 119 | | GGML_MAX_PARAMS | ggml支持的最大参数,值`256` | 120 | | GGML_MAX_CONTEXTS | ,值`64` | 121 | | GGML_MAX_SRC | ,值`6` | 122 | | GGML_MAX_NAME | ,值`48` | 123 | | GGML_MAX_OP_PARAMS | ,值`32` | 124 | | GGML_DEFAULT_N_THREADS | 默认线程数,值`4` | 125 | | GGML_EXIT_SUCCESS | 退出成功 | 126 | | GGML_EXIT_ABORTED | 退出中断 | 127 | | GGML_UNUSED(x) | | 128 | | GGML_PAD(x, n) | 用于内存对齐, 按照n字节对齐 | 129 | | GGML_ASSERT(x) | | 130 | | GGML_TENSOR_LOCALS_1 | | 131 | | GGML_TENSOR_LOCALS_2 | | 132 | | GGML_TENSOR_LOCALS_3 | | 133 | | GGML_TENSOR_LOCALS | | 134 | 135 | ### 枚举 136 | 137 | | 枚举名 | 枚举说明 | 138 | | ---------------- | ------------------------------------------------------------ | 139 | | ggml_type | 用于枚举支持的数据类型,例如`GGML_TYPE_F32`,`GGML_TYPE_F16` | 140 | | ggml_backend | 用于枚举支持的后端类型,例如`GGML_BACKEND_CPU`,`GGML_BACKEND_GPU` | 141 | | ggml_ftype | 用于枚举ggml支持的文件类型 | 142 | | ggml_op | 用于枚举所有支持的算子,例如 `GGML_OP_ARGMAX` | 143 | | ggml_unary_op | 用于枚举所有的一元运算符,例如`GGML_UNARY_OP_ABS` | 144 | | ggml_object_type | 用于枚举ggml对象类型,`ggml_object_type`, `GGML_OBJECT_GRAPH`, `GGML_OBJECT_WORK_BUFFER` | 145 | | | | 146 | 147 | ### 结构体 148 | 149 | | 结构体名 | 结构体说明 | 150 | | ----------------------- | -------------------------------------- | 151 | | ggml_object | ggml对象,包含ggml对象和ggml_type | 152 | | ggml_tensor | ggml张量 | 153 | | ggml_cplan | | 154 | | ggml_cgraph | | 155 | | ggml_scratch | | 156 | | ggml_init_params | | 157 | | ggml_task_type | | 158 | | ggml_compute_params | | 159 | 160 | ```c 161 | struct ggml_tensor { 162 | enum ggml_type type; // 数据类型,fp32 fp16 int8等 163 | enum ggml_backend backend; // 设备后端,可以是metal cpu gpu 164 | 165 | int n_dims; // 维度, 例如三维矩阵,为3 166 | 167 | // 每个维度的大小,相当于shape 或size, 168 | // 通常是反着存, 例如矩阵[[[0,4,8], [1,5,9]], [[1,5,9], [3,7,11]]] 169 | // 该矩阵的numpy数组的shape为(2,2,3) 的 n_dims为[2,2,3] 170 | int64_t ne[GGML_MAX_DIMS]; 171 | 172 | // stride in bytes: 173 | // nb[0] = sizeof(type) 174 | // nb[1] = nb[0] * ne[0] + padding 175 | // nb[i] = nb[i-1] * ne[i-1] 176 | // padding 为了内存地址对齐 177 | // nb数组就很牛逼了,它记录了每一个维度的地址偏移量,用于计算高维矩阵元素的地址, 178 | // 以及reshape、transpose、permute等操作,只需要交换nb数组即可 179 | // 举个例子: 180 | 181 | // 令矩阵A = [[[0,4,8], [2,6,10]], [[1,5,9], [3,7,11]]] , 其中A维度为(2,2,3)的一个三维矩阵 182 | // 假设 ggml_type为int8,只占一个字节。 其中 n_dims = 3 , ne = [3,2,2]。 183 | // 假设padding=0, 则 nb=[1, 3, 6, 12] 184 | // 那么 A[1][1][2] -> *(A + 1*1 + 1*2 + 2*4) = *(A + 11) 185 | // A[0][1][1] -> *(A + 0*1 + 1*2 + 1*4) = *(A + 4) 186 | size_t nb[GGML_MAX_DIMS]; 187 | 188 | 189 | 190 | // compute data 191 | enum ggml_op op; 192 | 193 | // op params - allocated as int32_t for alignment 194 | int32_t op_params[GGML_MAX_OP_PARAMS / sizeof(int32_t)]; 195 | 196 | bool is_param; 197 | 198 | struct ggml_tensor * grad; 199 | struct ggml_tensor * src[GGML_MAX_SRC]; 200 | 201 | // performance 202 | int perf_runs; 203 | int64_t perf_cycles; 204 | int64_t perf_time_us; 205 | 206 | void * data; 207 | 208 | char name[GGML_MAX_NAME]; 209 | 210 | void * extra; // extra things e.g. for ggml-cuda.cu 211 | 212 | char padding[4]; 213 | }; 214 | ``` 215 | 216 | --------------------------------------------------------------------------------