当前的大模型的参数规模较大,数以千亿的参数导致了它们的预训练结果文件都在几十GB甚至是几百GB,这不仅导致其使用成本很高,在不同平台进行交换也非常困难。因此,大模型预训练结果文件的保存格式对于模型的使用和生态的发展来说极其重要。
大语言模型的开发通常使用PyTorch等框架,其预训练结果通常也会保存为相应的二进制格式,如pt后缀的文件通常就是PyTorch框架保存的二进制预训练结果。
但是,大模型的存储一个很重要的问题是它的模型文件巨大,而模型的结构、参数等也会影响模型的推理效果和性能。为了让大模型更加高效的存储和交换,就有了不同格式的大模型文件。其中,GGUF就是非常重要的一种大模型文件格式。
GGUF文件全称是GPT-Generated Unified Format,是由Georgi Gerganov定义发布的一种大模型文件格式。Georgi Gerganov是著名开源项目llama.cpp的创始人。
GGUF就是一种二进制格式文件的规范,原始的大模型预训练结果经过转换后变成GGUF格式可以更快地被载入使用,也会消耗更低的资源。原因在于GGUF采用了多种技术来保存大模型预训练结果,包括采用紧凑的二进制编码格式、优化的数据结构、内存映射等。
注意:llama.cpp官方提供了转换脚本,可以将pt格式的预训练结果以及safetensors模型文件转换成GGUF格式的文件。转换的时候也可以选择量化参数,降低模型的资源消耗。
GGUF格式的前身是GGML,也是同一个作者开发的,但是GGML格式因为种种限制目前被放弃。
safetensors是一种由Hugging Face推出的新型的安全的模型存储格式。它特别关注模型的安全性和隐私保护,同时保证了加载速度。safetensors文件仅包含模型的权重参数,不包括执行代码,这有助于减少模型文件的大小并提高加载速度。此外,safetensors支持零拷贝(zero-copy)和懒加载(lazy loading),没有文件大小限制,并且支持bfloat16/fp8数据类型。但safetensors没有重点关注性能和跨平台交换。在大模型高效序列化、数据压缩、量化等方面存在不足,并且它只保存了张量数据,没有任何关于模型的元数据信息。
而gguf格式是一种针对大模型的二进制文件格式。专为GGML及其执行器快速加载和保存模型而设计。它是GGML格式的替代者,旨在解决GGML在灵活性和扩展性方面的限制。它包含加载模型所需的所有信息,无需依赖外部文件,这简化了模型部署和共享的过程,同时有助于跨平台操作。此外,GGUF还支持量化技术,可以降低模型的资源消耗,并且设计为可扩展的,以便在不破坏兼容性的情况下添加新信息。
总的来说,safetensors更侧重于安全性和效率,适合快速部署和对安全性有较高要求的场景,特别是在HuggingFace生态中。而gguf格式则是一种为大模型设计的二进制文件格式,优化了模型的加载速度和资源消耗,适合需要频繁加载不同模型的场景。
GGUF 是一种基于现有 GGJT 的格式(这种格式对张量进行对齐,以便能够使用内存映射(mmap)),但对该格式进行了一些更改,使其更具可扩展性且更易于使用。GGUF 具有如下特性:
mmap
兼容性:可以使用mmap
加载模型,以实现快速地加载和保存。GGJT 和 GGUF 之间的主要区别在于:超参数(现称为元数据)使用键值结构,而不是非类型化的值列表。这允许在不破坏与现有模型的兼容性的情况下添加新的元数据,这使得可以添加对推理或识别模型有用的附加信息来注释模型。
Magic Number
:一个特定的数字或字符序列,用于标识文件格式Version
:文件格式的版本号,指明了文件遵循的具体规范或标准Key
:一个字符串,标识元数据的名称Value Type
:数据类型,指明值的格式(如整数、浮点数、字符串等)Value
:具体的元数据内容Count
:一个整数,表示文件中张量的总数Name
:张量的名称Dimensions
:张量的维度信息Type
:张量数据的类型(如浮点数、整数等)Offset
:指明张量数据在文件中的位置Binary Data
:模型的权重和参数的二进制表示
在张量信息部分,GGUF定义了模型的量化级别。量化级别取决于模型根据质量和准确性定义的值(ggml_type)。在 GGUF 规范中,值列表如下:
类型 |
描述 |
F64 |
64 位标准 IEEE 754 双精度浮点数 |
I64 |
64 位定长整数 |
F32 |
32 位标准 IEEE 754 单精度浮点数 |
I32 |
32 位定长整数 |
F16 |
16 位标准 IEEE 754 半精度浮点数 |
BF16 |
32 位 IEEE 754 单精度浮点数的 16 位缩短版本 |
I16 |
16 位定长整数 |
Q8_0 |
8 位 RTN (四舍五入)量化 (q),每个块有 32 个权重。权重公式: w = q * block_scale. 传统的量化方法 |
Q8_1 |
8 位 RTN 量化 (q),每个块有 32 个权重。权重公式: w = q * block_scale + block_minimum. 传统的量化方法 |
Q8_K |
8 位量化(q),每个块有 256 个权重。 仅用于量化中间结果。所有 2-6 位点积都是为此量化类型实现的。权重公式: w = q * block_scale |
I8 |
8 位定长整数 |
Q6_K |
6 位量化 (q),超级块有 16 个块,每个块有 16 个权重。权重公式: w = q * block_scale(8-bit),得出每个权重 6.5625 位 |
Q5_0 |
5 位 RTN 量化 (q),每个块有 32 个权重。权重公式: w = q * block_scale. 传统的量化方法 |
Q5_1 |
5 位 RTN 量化 (q),每个块有 32 个权重。权重公式: w = q * block_scale + block_minimum. 传统的量化方法 |
Q5_K |
5 位量化 (q),超级块有8个块,每个块有32个权重。权重公式: w = q * block_scale(6-bit) + block_min(6-bit),得出每个权重 5.5 位 |
Q4_0 |
4 位 RTN 量化 (q),每个块有 32 个权重。权重公式: w = q * block_scale. 传统的量化方法 |
Q4_1 |
4 位 RTN 量化 (q),每个块有 32 个权重。权重公式:w = q * block_scale + block_minimum. 传统的量化方法 |
Q4_K |
4 位量化 (q),超级块有8个块,每个块有32个权重。权重公式: w = q * block_scale(6-bit) + block_min(6-bit) ,得出每个权重 4.5 位 |
Q3_K |
3 位量化 (q),超级块有 16 个块,每个块有 16 个权重。权重公式: w = q * block_scale(6-bit), 得出每个权重3.4375 位 |
Q2_K |
2 位量化 (q),超级块有 16 个块,每个块有 16 个权重。权重公式: w = q * block_scale(4-bit) + block_min(4-bit),得出每个权重 2.5625 位 |
IQ4_NL |
4 位量化 (q),超级块有 256 个权重。权重w是使用super_block_scale和importance matrix获得的。 |
IQ4_XS |
4 位量化 (q),超级块有 256 个权重。 权重w是使用super_block_scale和importance matrix获得的,结果是每个权重 4.25 位 |
IQ3_S |
3 位量化 (q),超级块有 256 个权重。 权重w是使用super_block_scale和importance matrix获得的,结果是每个权重 3.44 位 |
IQ3_XXS |
3 位量化 (q),超级块有 256 个权重。 权重w是使用super_block_scale和importance matrix获得的,结果是每个权重 3.06 位 |
IQ2_XXS |
2 位量化 (q),超级块有 256 个权重。 权重w是使用super_block_scale和importance matrix获得的,结果是每个权重 2.06 位 |
IQ2_S |
2 位量化 (q),超级块有 256 个权重。 权重w是使用super_block_scale和importance matrix获得的,结果是每个权重 2.5 位 |
IQ2_XS |
2 位量化 (q),超级块有 256 个权重。 权重w是使用super_block_scale和importance matrix获得的,结果是每个权重 2.31 位 |
IQ1_S |
1 位量化 (q),超级块有 256 个权重。 权重w是使用super_block_scale和importance matrix获得的,结果是每个权重 1.56 位 |
IQ1_M |
1 位量化 (q),超级块有 256 个权重。 权重w是使用super_block_scale和importance matrix获得的,结果是每个权重 1.75 位 |
GGUF 自身又有多种格式,主要区别在于浮点数的位数和量化的方式。
不同的格式会影响模型的大小、性能和精度,一般来说,位数越少,量化越多,模型越小,速度越快,但是精度也越低。
在HuggingFace中可以看到如下格式:
命名解释:“Q”+用于存储权重(精度)的位数+特定变体
q5_0: Higher accuracy, higher resource usage and slower inference
HuggingFace 已经对 GGUF 格式提供了支持。同时,HuggingFace 开发了一个脚本(huggingface.js/packages/gguf at main · huggingface/huggingface.js · GitHub)可以用来解析 HuggingFace Hub 上 GGUF 格式的模型的信息。并且可以直接在HF平台上对GGUF的元数据进行预览,包括模型的架构、具体参数等:
如何将模型转换为 GGUF 格式?
Huggingface 上可以通过 GGUF 标签浏览所有带有 GGUF 文件的模型:https://huggingface.co/models?library=gguf
Huggingface Hub上面提供了将模型转化或者量化为 GGUF 格式的工具:https://huggingface.co/spaces/ggml-org/gguf-my-repo
以Qwen1.5-1.8B模型为例:
下载:Qwen1.5-1.8B
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp
make
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
转换模型至未量化的版本,后续可以转成不同的量化模式:
python convert_hf_to_gguf.py /workspace/Qwen/Qwen1___5-1___8B/ --outfile /workspace/Qwen/Qwen1_5-1_8B.gguf
注意⚠️:convert_hf_to_gguf.py 也可以指定--outtype (choose from 'f32', 'f16', 'bf16', 'q8_0', 'tq1_0', 'tq2_0', 'auto')
make 编译完后有一个 llama-quantize 可执行文件
对生成的GGUF模型按照指定精度量化(例:Q4_K_M):
./llama-quantize /workspace/Qwen/Qwen1_5-1_8B.gguf /workspace/Qwen/Qwen1_5-1_8B-Q4_K_M.gguf Q4_K_M
注意⚠️:--outtype
是输出类型:
可以查看:
./llama-quantize -h
# 输出
Allowed quantization types:
2 or Q4_0 : 4.34G, +0.4685 ppl @ Llama-3-8B
3 or Q4_1 : 4.78G, +0.4511 ppl @ Llama-3-8B
8 or Q5_0 : 5.21G, +0.1316 ppl @ Llama-3-8B
9 or Q5_1 : 5.65G, +0.1062 ppl @ Llama-3-8B
19 or IQ2_XXS : 2.06 bpw quantization
20 or IQ2_XS : 2.31 bpw quantization
28 or IQ2_S : 2.5 bpw quantization
29 or IQ2_M : 2.7 bpw quantization
24 or IQ1_S : 1.56 bpw quantization
31 or IQ1_M : 1.75 bpw quantization
36 or TQ1_0 : 1.69 bpw ternarization
37 or TQ2_0 : 2.06 bpw ternarization
10 or Q2_K : 2.96G, +3.5199 ppl @ Llama-3-8B
21 or Q2_K_S : 2.96G, +3.1836 ppl @ Llama-3-8B
23 or IQ3_XXS : 3.06 bpw quantization
26 or IQ3_S : 3.44 bpw quantization
27 or IQ3_M : 3.66 bpw quantization mix
12 or Q3_K : alias for Q3_K_M
22 or IQ3_XS : 3.3 bpw quantization
11 or Q3_K_S : 3.41G, +1.6321 ppl @ Llama-3-8B
12 or Q3_K_M : 3.74G, +0.6569 ppl @ Llama-3-8B
13 or Q3_K_L : 4.03G, +0.5562 ppl @ Llama-3-8B
25 or IQ4_NL : 4.50 bpw non-linear quantization
30 or IQ4_XS : 4.25 bpw non-linear quantization
15 or Q4_K : alias for Q4_K_M
14 or Q4_K_S : 4.37G, +0.2689 ppl @ Llama-3-8B
15 or Q4_K_M : 4.58G, +0.1754 ppl @ Llama-3-8B
17 or Q5_K : alias for Q5_K_M
16 or Q5_K_S : 5.21G, +0.1049 ppl @ Llama-3-8B
17 or Q5_K_M : 5.33G, +0.0569 ppl @ Llama-3-8B
18 or Q6_K : 6.14G, +0.0217 ppl @ Llama-3-8B
7 or Q8_0 : 7.96G, +0.0026 ppl @ Llama-3-8B
33 or Q4_0_4_4 : 4.34G, +0.4685 ppl @ Llama-3-8B
34 or Q4_0_4_8 : 4.34G, +0.4685 ppl @ Llama-3-8B
35 or Q4_0_8_8 : 4.34G, +0.4685 ppl @ Llama-3-8B
1 or F16 : 14.00G, +0.0020 ppl @ Mistral-7B
32 or BF16 : 14.00G, -0.0050 ppl @ Mistral-7B
0 or F32 : 26.00G @ 7B
COPY : only copy tensors, no quantizing
使用 Llama.cpp 运行 GGUF 模型,llama-cli 提供多种“模式”来与模型进行“交互”
# 参数解释:
# -m 或 –model:模型路径
# -co 或 –color:为输出着色以区分提示词、用户输入和生成的文本
# -cnv 或 –conversation:在对话模式下运行
# -p 或 –prompt:在对话模式下的提示词
# -fa 或 –flash-attn:支持 GPU,则启用Flash Attention注意力
# -ngl 或 –n-gpu-layers:支持 GPU,则将这么多层分配给 GPU 进行计算
# -n 或 –predict:要预测的token数量
# ./llama-cli -h 可以查看其他选项
./llama-cli -m /workspace/Qwen/Qwen1_5-1_8B-Q4_K_M.gguf \
-co -cnv -p "You are Qwen, created by Alibaba Cloud. You are a helpful assistant." \
-fa -ngl 80 -n 512
首先会在屏幕上打印元数据,然后就可以输入prompt与模型进行对话(Ctrl+C退出)
流程如下:
# 参数解释:
# -sp 或 –special:显示特殊token
# -i 或 –interactive:进入互动模式,可以中断模型生成并添加新文本
# -if 或 –interactive-first:立即等待用户输入,否则,模型将立即运行并根据提示生成文本
# -p 或 –prompt:模型续写用的上文
# --in-prefix:用户输入附加的前缀字符串
# --in-suffix:用户输入附加的后缀字符串
./llama-cli -m /workspace/Qwen/Qwen1_5-1_8B-Q4_K_M.gguf \
-co -sp -i -if -p "<|im_start|>system\nYou are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>\n" \
--in-prefix "<|im_start|>user\n" --in-suffix "<|im_end|>\n<|im_start|>assistant\n" \
-fa -ngl 80 -n 512
需要正确格式化输入,并且只能生成一次回应:
./llama-cli -m /workspace/Qwen/Qwen1_5-1_8B-Q4_K_M.gguf \
-co -sp -p "<|im_start|>system\nYou are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>\n<|im_start|>user\ngive me a short introduction to LLMs.<|im_end|>\n<|im_start|>assistant\n" \
-fa -ngl 80 -n 512
使用 llama-cpp-python(GitHub - abetlen/llama-cpp-python: Python bindings for llama.cpp):
from llama_cpp import Llama
llm = Llama(
model_path="/workspace/Qwen/Qwen1_5-1_8B-Q4_K_M.gguf",
n_gpu_layers=-1, # Use GPU acceleration
)
output = llm.create_chat_completion(
messages = [
{"role": "system", "content": "You are an assistant who perfectly describes images."},
{
"role": "user",
"content": "give me a short introduction to LLMs."
}
]
)
print(output)