在大模型领域,GGUF是一个非常常见的词语,也是非常常见的大模型预训练结果命名方式。很多模型模型,如Yi-34B、Llama2-70B等模型都有对应的GGUF版本,这些版本都模型除了文件名多了GGUF外,其它与原有的模型名称完全一致。那么,GGUF大模型文件格式是什么意思?为什么会有这样的大模型文件,与它一同出现对比的是GGML格式文件,二者的区别是啥?
GGUF格式是用于存储大型模型预训练结果的,相较于Hugging Face和torch的bin文件,它采用了紧凑的二进制编码格式、优化的数据结构以及内存映射等技术,提供了更高效的数据存储和访问方式。
内存映射(mmap) 指允许直接从磁盘映射数据到内存地址空间,加快了数据的加载速度。这样,数据可以在不实际加载整个文件的情况下被访问,特别是对于大型模型非常有效
GGUF文件主要包含三部分包括文件头、元数据键值对和张量信息,具体一点包含一下几个部分。
- 文件头 (Header): 包含用于识别文件类型和版本的基本信息。
- Magic
Number:一个特定的数字或字符序列,用于标识文件格式。
- Version:文件格式的版本号,指明了文件遵循的具体规范或标准。
- 元数据键值对 (Metadata Key-Value Pairs): 存储关于模型的额外信息,如作者、训练信息、模型描述等。
- Key:一个字符串,标识元数据的名称。
- Value
Type:数据类型,指明值的格式(如整数、浮点数、字符串等)。
- Value:具体的元数据内容。
- 张量计数 (Tensor Count): 标识文件中包含的张量(Tensor)数量。
- 张量信息 (Tensor Info):描述每个张量的具体信息,包括形状、类型和数据位置。
- Name:张量的名称。
- Dimensions:张量的维度信息。
- Type:张量数据的类型(如浮点数、整数等)。
- Offset:指明张量数据在文件中的位置。
- 对齐填充 (Alignment Padding):确保数据块在内存中正确对齐,有助于提高访问效率。
- 张量数据 (Tensor Data):存储模型的实际权重和参数。
- Binary
Data:模型的权重和参数的二进制表示。
- 端序标识 (Endianness): 指示文件中数值数据的字节顺序(大端或小端)。
大端模式与小端模式 大端模式是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中.小端模式是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。
- 扩展信息 (Extension Information): 允许文件格式未来扩展,以包含新的数据类型或结构。
- 可以是新加入的任何额外信息,为将来的格式升级预留空间。
大语言模型的开发通常使用PyTorch等框架,其预训练结果通常也会保存为相应的二进制格式,如pt后缀的文件通常就是PyTorch框架保存的二进制预训练结果。
但是,大模型的存储一个很重要的问题是它的模型文件巨大,而模型的结构、参数等也会影响模型的推理效果和性能。为了让大模型更加高效的存储和交换,就有了不同格式的大模型文件。其中,GGUF就是非常重要的一种大模型文件格式。
GGUF文件全称是GPT-Generated Unified Format,是由Georgi Gerganov定义发布的一种大模型文件格式。Georgi
Gerganov是著名开源项目llama.cpp的创始人。
GGUF就是一种二进制格式文件的规范,原始的大模型预训练结果经过转换后变成GGUF格式可以更快地被载入使用,也会消耗更低的资源。原因在于GGUF采用了多种技术来保存大模型预训练结果,包括采用紧凑的二进制编码格式、优化的数据结构、内存映射等。
综上所述,GGUF可以理解为一种格式定义,采用相应的工具将原始模型预训练结果转换成GGUF之后可以更加高效的使用。
llama.cpp官方提供了转换脚本,可以将pt格式的预训练结果以及safetensors模型文件转换成GGUF格式的文件。转换的时候也可以选择量化参数,降低模型的资源消耗。这个过程性能损失很低!
GGUF大模型文件核心特性
GGFU大模型的文件核心理念就是通过更加紧凑高效的文件格式包含大模型预训练结果的全部信息,以便于高效的时候和跨平台交换。为此,GGUF的设计了几个核心特性来完成这样的目标:
二进制格式
作为一种二进制格式,GGUF能够显著提高模型加载和保存的速度,这对于需要频繁加载不同模型的场景尤为重要。
可扩展性和兼容性
GGUF的设计允许在不破坏现有模型兼容性的情况下添加新的信息。这种灵活性对于长期维护和升级模型至关重要。
全面的信息包含
GGUF包含加载模型所需的所有信息,无需依赖外部文件。这大大简化了模型部署和共享的过程。
这里全面的信息包含其实就是为了解决它的前任GGML的缺点。GGML在设计的时候除了预训练文件量化结果外,只保留了非常少的信息,对于模型关键的架构或者描述是缺乏的。因此扩展性很差,开发新的功能就面临很多问题。
为了更加深入理解GGUF格式,在附录中我们总结了GGFU文件的结构信息。详情参考最后一章。
为什么GGUF格式大模型文件的性能很好
GGUF文件格式能够更快载入模型的原因主要归结于以下几个关键特性:
1. 二进制格式:GGUF作为一种二进制格式,相较于文本格式的文件,可以更快地被读取和解析。二进制文件通常更紧凑,减少了读取和解析时所需的I/O操作和处理时间。
2. 优化的数据结构:GGUF可能采用了特别优化的数据结构,这些结构为快速访问和加载模型数据提供了支持。例如,数据可能按照内存加载的需要进行组织,以减少加载时的处理。
3. 内存映射(mmap)兼容性:如果GGUF支持内存映射(mmap),这允许直接从磁盘映射数据到内存地址空间,从而加快了数据的加载速度。这样,数据可以在不实际加载整个文件的情况下被访问,特别是对于大型模型非常有效。
4. 高效的序列化和反序列化:GGUF可能使用高效的序列化和反序列化方法,这意味着模型数据可以快速转换为可用的格式。
5. 少量的依赖和外部引用:如果GGUF格式设计为自包含,即所有需要的信息都存储在单个文件中,这将减少解析和加载模型时所需的外部文件查找和读取操作。
6. 数据压缩:GGUF格式可能采用了有效的数据压缩技术,减少了文件大小,从而加速了读取过程。
7. 优化的索引和访问机制:文件中数据的索引和访问机制可能经过优化,使得查找和加载所需的特定数据片段更加迅速。
总之,GGUF通过各种优化手段实现了快速的模型加载,这对于需要频繁载入不同模型的场景尤为重要。
GGUF是GGML的替代者,它与GGML的区别是啥
此前,Georgi Gerganov推出了GGML工具,并推出了与之相应的大模型格式GGML,但是由于GGML设计落后于时代的发展,因此被弃用,由GGUF替代。
GGML格式在灵活性和扩展性方面存在一定的限制。例如,它无法有效地识别不同的模型架构,而且对超参数的添加和移除具有破坏性,这使得模型的迭代和升级变得复杂。为此,在2023年8月份,Georgi
Gerganov推出了GGUF作为后续的替代者,用以作为llama.cpp的主要文件格式。而GGML推出历史舞台。
关于GGML的解释和理解参考此前DataLearnerAI的博客:大模型领域的GGML是什么?GGML格式的大模型文件与原有文件有什么不同?它是谁提出的?如何使用?
在最新版本的llama.cpp中,已经去除了对GGML的支持,因此未来GGUF才是大模型文件格式的主流(在llama.cpp生态中)。
附:GGUF大模型文件的结构信息
那么,GGUF文件结构具体来说包含如下几个部分:
元数据和数据类型
GGUF支持多种数据类型,如整数、浮点数和字符串等。这些数据类型用于定义模型的不同方面,如结构、大小和参数。
文件组成
一个GGUF文件包括文件头、元数据键值对和张量信息等。这些组成部分共同定义了模型的结构和行为。
端序支持
GGUF支持小端和大端格式,确保了其在不同计算平台上的可用性。端序(Endianness)是指数据在计算机内存中的字节顺序排列方式,主要有两种类型:大端(Big-Endian)和小端(Little-Endian)。不同的计算平台可能采用不同的端序。例如,Intel的x86架构是小端的,而某些旧的IBM和网络协议通常是大端的。因此,文件格式如果能支持这两种端序,就可以确保数据在不同架构的计算机上正确读取和解释。
具体的GGUF文件组成信息如下:
1. 文件头
(Header)
- 作用:包含用于识别文件类型和版本的基本信息。
- 内容:
Magic
Number
:一个特定的数字或字符序列,用于标识文件格式。
Version
:文件格式的版本号,指明了文件遵循的具体规范或标准。
2. 元数据键值对
(Metadata Key-Value Pairs)
- 作用:存储关于模型的额外信息,如作者、训练信息、模型描述等。
- 内容:
Key
:一个字符串,标识元数据的名称。
Value Type
:数据类型,指明值的格式(如整数、浮点数、字符串等)。
Value
:具体的元数据内容。
3. 张量计数
(Tensor Count)
- 作用:标识文件中包含的张量(Tensor)数量。
- 内容:
4. 张量信息
(Tensor Info)
- 作用:描述每个张量的具体信息,包括形状、类型和数据位置。
- 内容:
Name
:张量的名称。
Dimensions
:张量的维度信息。
Type
:张量数据的类型(如浮点数、整数等)。
Offset
:指明张量数据在文件中的位置。
5. 对齐填充
(Alignment Padding)
- 作用:确保数据块在内存中正确对齐,有助于提高访问效率。
- 内容:
6. 张量数据
(Tensor Data)
- 作用:存储模型的实际权重和参数。
- 内容:
Binary Data
:模型的权重和参数的二进制表示。
7. 端序标识
(Endianness)
- 作用:指示文件中数值数据的字节顺序(大端或小端)。
- 内容:
8. 扩展信息
(Extension Information)
- 作用:允许文件格式未来扩展,以包含新的数据类型或结构。
- 内容:
- 可以是新加入的任何额外信息,为将来的格式升级预留空间。
整体来看,GGUF文件格式通过这些结构化的组件提供了一种高效、灵活且可扩展的方式来存储和处理机器学习模型。这种设计不仅有助于快速加载和处理模型,而且还支持未来技术的发展和新功能的添加。
下面用代码读取一下gguf的格式,看下每个部分的输出
import
gguf
filename
= "/Users/Downloads/tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf"
with
open(filename, "rb") as f:
# Load metadata
info, tensorinfo = gguf.load_gguf(f)
# Print metadata
for key, value in info.items():
print(f"{key:30} {repr(value)[:100]}")
# Load tensors
for name in tensorinfo:
weights = gguf.load_gguf_tensor(f, tensorinfo, name)
print(name, type(weights), weights.shape)
--------------------------------------输出-----------------------------------------------------------
general.architecture
'llama'
general.name
'tinyllama_tinyllama-1.1b-chat-v1.0'
llama.context_length
2048
llama.embedding_length
2048
llama.block_count
22
llama.feed_forward_length
5632
llama.rope.dimension_count
64
llama.attention.head_count
32
llama.attention.head_count_kv
4
llama.attention.layer_norm_rms_epsilon
9.999999747378752e-06
llama.rope.freq_base
10000.0
general.file_type
15
tokenizer.ggml.model
'llama'
tokenizer.ggml.tokens
['<unk>', '<s>', '</s>', '<0x00>', '<0x01>', '<0x02>', '<0x03>', '<0x04>', '<0x05>', '<0x06>', '<0x0
tokenizer.ggml.scores
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
tokenizer.ggml.token_type
[2, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
tokenizer.ggml.merges
['▁ t', 'e r', 'i n', '▁ a', 'e n', 'o n', '▁t h', '▁ th', 'e s', '▁ s', '▁ d', 'a t', 'o r', 'a n',
tokenizer.ggml.bos_token_id
1
tokenizer.ggml.eos_token_id
2
tokenizer.ggml.unknown_token_id
0
tokenizer.ggml.padding_token_id
2
tokenizer.chat_template
"{% for message in messages %}\n{% if message['role'] ==
'user' %}\n{{ '<|user|>\n' + message['conte
general.quantization_version
2
output.weight
<class 'numpy.ndarray'> (32000, 2048)
//* blk是block的缩写
token_embd.weight
<class 'numpy.ndarray'> (32000, 2048)
blk.0.attn_norm.weight
<class 'numpy.ndarray'> (2048,)
blk.0.ffn_down.weight
<class 'numpy.ndarray'> (2048, 5632)
blk.0.ffn_gate.weight
<class 'numpy.ndarray'> (5632, 2048)
blk.0.ffn_up.weight
<class 'numpy.ndarray'> (5632, 2048)
blk.0.ffn_norm.weight
<class 'numpy.ndarray'> (2048,)
blk.0.attn_k.weight
<class 'numpy.ndarray'> (256, 2048)
blk.0.attn_output.weight
<class 'numpy.ndarray'> (2048, 2048)
blk.0.attn_q.weight
<class 'numpy.ndarray'> (2048, 2048)
blk.0.attn_v.weight
<class 'numpy.ndarray'> (256, 2048)
blk.1.attn_norm.weight
<class 'numpy.ndarray'> (2048,)
blk.1.ffn_down.weight
<class 'numpy.ndarray'> (2048, 5632)
blk.1.ffn_gate.weight
<class 'numpy.ndarray'> (5632, 2048)
blk.1.ffn_up.weight
<class 'numpy.ndarray'> (5632, 2048)
blk.1.ffn_norm.weight
<class 'numpy.ndarray'> (2048,)
blk.1.attn_k.weight
<class 'numpy.ndarray'> (256, 2048)
blk.1.attn_output.weight
<class 'numpy.ndarray'> (2048, 2048)
blk.1.attn_q.weight
<class 'numpy.ndarray'> (2048, 2048)
blk.1.attn_v.weight
<class 'numpy.ndarray'> (256, 2048)
blk.10.attn_norm.weight
<class 'numpy.ndarray'> (2048,)
blk.10.ffn_down.weight
<class 'numpy.ndarray'> (2048, 5632)
blk.10.ffn_gate.weight
<class 'numpy.ndarray'> (5632, 2048)
blk.10.ffn_up.weight
<class 'numpy.ndarray'> (5632, 2048)
blk.10.ffn_norm.weight
<class 'numpy.ndarray'> (2048,)