一文搞懂大模型文件存儲格式新寵 GGUF
在日常 AI 模型訓練過程中,訓練好的模型權重通常需要以一種格式存儲在磁盤中。比如:目前最流行的 AI 框架 PyTorch 使用 pickle 格式存儲模型權重文件,還有 Huggingface 提出的 Safetensors 格式。之前在 一文詳解模型權重存儲新格式 Safetensors 中講述 Safetensors,本文大介紹大模型文件存儲格式新寵 GGUF,目前 Huggingface Transformers 已經支持了 GGUF 格式,同時,像谷歌的 Gemma、阿里的 Qwen 等模型默認已經提供了 GGUF 格式文件,可見其發展勢頭如日中天。
GGUF 簡介
GGUF(GPT-Generated Unified Format)是由 Georgi Gerganov(著名開源項目 llama.cpp 的創始人)定義發佈的一種大模型文件格式。GGUF 繼承自其前身 GGML,但 GGML 格式有一些缺點,已被完全棄用並被 GGUF 格式取代。GGUF 是一種二進制格式文件的規範,原始的大模型預訓練結果經過轉換後變成 GGUF 格式可以更快地被載入使用,也會消耗更低的資源。原因在於 GGUF 採用了多種技術來保存大模型預訓練結果,包括採用緊湊的二進制編碼格式、優化的數據結構、內存映射等。
綜上所述,GGUF 可以理解爲一種格式定義,採用相應的工具將原始模型預訓練結果轉換成 GGUF 之後可以更加高效的使用。
GGML 的缺陷
GGUF 繼承自其前身 GGML,而 GGML 有以下缺點:
-
無版本信息,導致無法管理和向後兼容
-
增加或者修改信息非常不靈活
-
手動修改模型信息很困難
GGUF 特性
GGUF 是一種基於現有 GGJT 的格式 (這種格式對張量進行對齊,以便能夠使用內存映射(mmap)),但對該格式進行了一些更改,使其更具可擴展性且更易於使用。GGUF 具有如下特性:
-
單文件部署:它們可以輕鬆分發和加載,並且不需要任何外部文件來獲取附加信息。
-
可擴展性:可以將新特徵添加到基於 GGML 的執行器中 / 可以將新信息添加到 GGUF 模型中,而不會破壞與現有模型的兼容性。
-
mmap
兼容性:可以使用mmap
加載模型,以實現快速地加載和保存。 -
易於使用:無論使用何種語言,都可以使用少量代碼輕鬆加載和保存模型,無需外部庫。
-
信息完整:加載模型所需的所有信息都包含在模型文件中,用戶不需要提供任何額外的信息。這大大簡化了模型部署和共享的流程。
GGJT 和 GGUF 之間的主要區別在於:超參數(現稱爲元數據)使用鍵值結構,而不是非類型化的值列表。這允許在不破壞與現有模型的兼容性的情況下添加新的元數據,這使得可以添加對推理或識別模型有用的附加信息來註釋模型。
爲什麼 GGUF 格式對大模型文件性能很好
GGUF 文件格式能夠更快載入模型的原因主要歸結於以下幾個關鍵特性:
-
二進制格式:GGUF 作爲一種二進制格式,相較於文本格式的文件,可以更快地被讀取和解析。二進制文件通常更緊湊,減少了讀取和解析時所需的 I/O 操作和處理時間。
-
優化的數據結構:GGUF 可能採用了特別優化的數據結構,這些結構爲快速訪問和加載模型數據提供了支持。例如,數據可能按照內存加載的需要進行組織,以減少加載時的處理。
-
內存映射(mmap)兼容性:GGUF 支持內存映射(mmap),這允許直接從磁盤映射數據到內存地址空間,從而加快了數據的加載速度。這樣,數據可以在不實際加載整個文件的情況下被訪問,特別是對於大模型非常有效。
-
高效的序列化和反序列化:GGUF 使用高效的序列化和反序列化方法,這意味着模型數據可以快速轉換爲可用的格式。
-
少量的依賴和外部引用:如果 GGUF 格式設計爲自包含,即所有需要的信息都存儲在單個文件中,這將減少解析和加載模型時所需的外部文件查找和讀取操作。
-
數據壓縮:GGUF 格式採用了有效的數據壓縮技術,減少了文件大小,從而加速了讀取過程。
-
優化的索引和訪問機制:文件中數據的索引和訪問機制經過優化,使得查找和加載所需的特定數據片段更加迅速。
總之,GGUF 通過各種優化手段實現了快速的模型加載,這對於需要頻繁載入不同模型的場景尤爲重要。
GGUF 文件結構
一個 GGUF 文件包括文件頭、元數據鍵值對和張量信息等。這些組成部分共同定義了模型的結構和行爲。具體如下所示:
同時,GGUF 支持多種數據類型,如整數、浮點數和字符串等。這些數據類型用於定義模型的不同方面,如結構、大小和參數。
GGUF 文件具體的組成信息如下所示:
- 文件頭 (Header)
-
作用:包含用於識別文件類型和版本的基本信息。
-
內容:
-
Magic Number
:一個特定的數字或字符序列,用於標識文件格式。 -
Version
:文件格式的版本號,指明瞭文件遵循的具體規範或標準。
-
- 元數據 key-value 對 (Metadata Key-Value Pairs)
-
作用:存儲關於模型的額外信息,如作者、訓練信息、模型描述等。
-
內容:
-
Key
:一個字符串,標識元數據的名稱。 -
Value Type
:數據類型,指明值的格式(如整數、浮點數、字符串等)。 -
Value
:具體的元數據內容。
-
- 張量計數器 (Tensor Count)
-
作用:標識文件中包含的張量(Tensor)數量。
-
內容:
Count
:一個整數,表示文件中張量的總數。
- 張量信息 (Tensor Info)
-
作用:描述每個張量的具體信息,包括形狀、類型和數據位置。
-
內容:
-
Name
:張量的名稱。 -
Dimensions
:張量的維度信息。 -
Type
:張量數據的類型(如:浮點數、整數等)。 -
Offset
:指明張量數據在文件中的位置。
-
- 對齊填充 (Alignment Padding)
-
作用:確保數據塊在內存中正確對齊,有助於提高訪問效率。
-
內容:
- 通常是一些填充字節,用於保證後續數據的內存對齊。
- 張量數據 (Tensor Data)
-
作用:存儲模型的實際權重和參數。
-
內容:
Binary Data
:模型的權重和參數的二進制表示。
- 端序標識 (Endianness)
-
作用:指示文件中數值數據的字節順序(大端或小端)。
-
內容:
- 通常是一個標記,表明文件遵循的端序。
- 擴展信息 (Extension Information)
-
作用:允許文件格式未來擴展,以包含新的數據類型或結構。
-
內容:
- 可以是新加入的任何額外信息,爲將來的格式升級預留空間。
在張量信息部分,GGUF 定義了模型的量化級別。量化級別取決於模型根據質量和準確性定義的值(ggml_type)。在 GGUF 規範中,值列表如下:
量化與反量化轉換的 Python 實現見 quants.py。
目前,HuggingFace 已經對 GGUF 格式提供了支持。同時,HuggingFace 開發了一個 JavaScript 腳本可以用來解析 HuggingFace Hub 上 GGUF 格式的模型的信息。並且可以直接在 HF 平臺上對 GGUF 的元數據進行預覽,包括模型的架構、具體參數等。比如:qwen2-0_5b-instruct-q2_k.gguf 模型的詳細信息如下所示。
整體來看,GGUF 文件格式通過這些結構化的組件提供了一種高效、靈活且可擴展的方式來存儲和處理機器學習模型。這種設計不僅有助於快速加載和處理模型,而且還支持未來技術的發展和新功能的添加。
GGUF 與 safetensors 格式的區別
safetensors 是一種由 Hugging Face 推出的新型的安全的模型存儲格式。它特別關注模型的安全性和隱私保護,同時保證了加載速度。safetensors 文件僅包含模型的權重參數,不包括執行代碼,這有助於減少模型文件的大小並提高加載速度。此外,safetensors 支持零拷貝(zero-copy)和懶加載(lazy loading),沒有文件大小限制,並且支持 bfloat16/fp8 數據類型。但 safetensors 沒有重點關注性能和跨平臺交換。在大模型高效序列化、數據壓縮、量化等方面存在不足,並且它只保存了張量數據,沒有任何關於模型的元數據信息。
而 gguf 格式是一種針對大模型的二進制文件格式。專爲 GGML 及其執行器快速加載和保存模型而設計。它是 GGML 格式的替代者,旨在解決 GGML 在靈活性和擴展性方面的限制。它包含加載模型所需的所有信息,無需依賴外部文件,這簡化了模型部署和共享的過程,同時有助於跨平臺操作。此外,GGUF 還支持量化技術,可以降低模型的資源消耗,並且設計爲可擴展的,以便在不破壞兼容性的情況下添加新信息。
總的來說,safetensors 更側重於安全性和效率,適合快速部署和對安全性有較高要求的場景,特別是在 HuggingFace 生態中。而 gguf 格式則是一種爲大模型設計的二進制文件格式,優化了模型的加載速度和資源消耗,適合需要頻繁加載不同模型的場景。
GGUF 文件解析
通過以下腳本解析上面的 qwen2-0_5b-instruct-q2_k.gguf 文件。
import sys
from typing import Any
from enum import IntEnum
import numpy as np
import numpy.typing as npt
# GGUF 元數據值類型
class GGUFValueType(IntEnum):
UINT8 = 0
INT8 = 1
UINT16 = 2
INT16 = 3
UINT32 = 4
INT32 = 5
FLOAT32 = 6
BOOL = 7
STRING = 8
ARRAY = 9
UINT64 = 10
INT64 = 11
FLOAT64 = 12
# GGUF tensor數據類型
class GGMLQuantizationType(IntEnum):
F32 = 0
F16 = 1
Q4_0 = 2
Q4_1 = 3
Q5_0 = 6
Q5_1 = 7
Q8_0 = 8
Q8_1 = 9
Q2_K = 10
Q3_K = 11
Q4_K = 12
Q5_K = 13
Q6_K = 14
Q8_K = 15
IQ2_XXS = 16
IQ2_XS = 17
IQ3_XXS = 18
IQ1_S = 19
IQ4_NL = 20
IQ3_S = 21
IQ2_S = 22
IQ4_XS = 23
I8 = 24
I16 = 25
I32 = 26
I64 = 27
F64 = 28
IQ1_M = 29
BF16 = 30
Q4_0_4_4 = 31
Q4_0_4_8 = 32
Q4_0_8_8 = 33
def check_version(version):
if version == 1 or version == 2 or version == 3:
return True
else:
return False
def data_get(
data, offset: int, dtype: npt.DTypeLike, count: int = 1) -> npt.NDArray[Any]:
count = int(count)
itemsize = int(np.empty([], dtype = dtype).itemsize)
end_offs = offset + itemsize * count
return (
data[offset:end_offs]
.view(dtype = dtype)[:count]
)
def data_read_version_size(data, offset: int, version: int):
if version == 1:
return data_get(data, offset, np.uint32)[0], 4
elif version == 2 or version == 3:
return data_get(data, offset, np.uint64)[0], 8
else:
raise ValueError(f'Sorry, file appears to be version {version} which we cannot handle')
def data_read_string(data, offset: int, version: int):
str_length, str_length_len = data_read_version_size(data, offset, version)
# 在內存上切出來string部分的數據
byte = data[offset+int(str_length_len):offset+int(str_length_len)+int(str_length)]
value = byte.tobytes().decode('utf-8') # 編碼成 utf-8
len = int(str_length_len + str_length)
return value, len
def readMetadataValue(data, type, offset, version):
if type == GGUFValueType.UINT8:
return data_get(data, np.uint8)[0], 1
elif type == GGUFValueType.INT8:
return data_get(data, np.int8)[0], 1
elif type == GGUFValueType.UINT16:
return data_get(data, offset, np.uint16)[0], 2
elif type == GGUFValueType.INT16:
return data_get(data, offset, np.int16)[0], 2
elif type == GGUFValueType.UINT32:
return data_get(data, offset, np.uint32)[0], 4
elif type == GGUFValueType.INT32:
return data_get(data, offset, np.int32)[0], 4
elif type == GGUFValueType.FLOAT32:
return data_get(data, offset, np.float32)[0], 4
elif type == GGUFValueType.BOOL:
return data_get(data, offset, np.uint8)[0], 1
elif type == GGUFValueType.STRING:
return data_read_string(data, offset, version=version)
elif type == GGUFValueType.ARRAY:
typeArray = data_get(data, offset, np.uint32)
typeLength = 4
lengthArray, lengthLength = data_read_version_size(data, offset + typeLength, version=version)
length = typeLength + lengthLength
arrayValues = []
for i in range(lengthArray):
value, len = readMetadataValue(data, typeArray, offset= offset + length, version=version)
arrayValues.append(value)
length += len
return arrayValues, length
elif type == GGUFValueType.UINT64:
return data_get(data, offset, np.uint64)[0], 8
elif type == GGUFValueType.INT64:
return data_get(data, offset, np.int64)[0], 8
elif type == GGUFValueType.FLOAT64:
return data_get(data, offset, np.float64)[0], 8
else:
raise ValueError(f'Sorry, un-supported GGUFValueType {type}!')
def parse_gguf(model_path):
data = np.memmap(model_path, mode = 'r')
offs = 0
magic = data_get(data, offs, np.uint32).tobytes()
print("magic: ", magic.decode('utf-8'))
if (magic != b'GGUF'):
print("is not gguf file")
sys.exit(1)
offs += 4
version = data_get(data, offs, np.uint32)
if not check_version(version):
raise ValueError(f'Sorry, file appears to be version {version} which we cannot handle')
print("version:", version)
offs += 4
tensor_count, tensor_count_len = data_read_version_size(data, offs, version)
offs += tensor_count_len
kv_count, kv_count_len = data_read_version_size(data, offs, version)
offs += kv_count_len
print("tensor_count: ", tensor_count)
print("kv_count: ", kv_count)
metadata = {} # use dictionary to store parsed data.
# 解析 gguf 頭部信息
for i in range(kv_count):
# 獲取key
key, k_len = data_read_string(data, offs, version)
offs += k_len
# 獲取value的數值類型
type = data_get(data, offs, np.uint32)[0]
offs += 4
# 獲取value
value, len = readMetadataValue(data, type, offs, version)
if len > 100:
print("i = ", i, ", k-v = ", key, ":", value[:100])
else:
print("i = ", i, ", k-v = ", key, ":", value)
offs += len
metadata[key] = value
# 解析tensor info的信息
for i in range(tensor_count):
# 獲取key
key, k_len = data_read_string(data, offs, version)
offs += k_len
nDims = data_get(data, offs, np.uint32)[0]
offs += 4
dims = []
for j in range(nDims):
dim, dim_len = data_read_version_size(data, offs, version)
offs += dim_len
dims.append(dim)
types = data_get(data, offs, np.uint32)[0]
precision = GGMLQuantizationType(types).name
offs += 4
tensorOffset = data_get(data, offs, np.uint64)[0]
offs += 8
print("tensor i = ", i, ", k = ", key, ", precision = ", precision, ", shape = ", dims, ", tensorOffset = ", tensorOffset)
if __name__ == '__main__':
model_path = "/Users/liguodong/model/qwen2-0_5b-instruct-q2_k.gguf"
parse_gguf(model_path)
運行結果:
magic: GGUF
version: [3]
tensor_count: 290
kv_count: 26
i = 0 , k-v = general.architecture : qwen2
i = 1 , k-v = general.name : qwen2-0_5b-instruct
i = 2 , k-v = qwen2.block_count : 24
i = 3 , k-v = qwen2.context_length : 32768
i = 4 , k-v = qwen2.embedding_length : 896
i = 5 , k-v = qwen2.feed_forward_length : 4864
i = 6 , k-v = qwen2.attention.head_count : 14
i = 7 , k-v = qwen2.attention.head_count_kv : 2
i = 8 , k-v = qwen2.rope.freq_base : 1000000.0
i = 9 , k-v = qwen2.attention.layer_norm_rms_epsilon : 1e-06
i = 10 , k-v = general.file_type : 10
i = 11 , k-v = tokenizer.ggml.model : gpt2
i = 12 , k-v = tokenizer.ggml.pre : qwen2
i = 13 , k-v = tokenizer.ggml.tokens : ['!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '¡', '¢', '£', '¤', '¥', '¦']
i = 14 , k-v = tokenizer.ggml.token_type : [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
i = 15 , k-v = tokenizer.ggml.merges : ['Ġ Ġ', 'ĠĠ ĠĠ', 'i n', 'Ġ t', 'ĠĠĠĠ ĠĠĠĠ', 'e r', ... 'o t', 'u s']
i = 16 , k-v = tokenizer.ggml.eos_token_id : 151645
i = 17 , k-v = tokenizer.ggml.padding_token_id : 151643
i = 18 , k-v = tokenizer.ggml.bos_token_id : 151643
i = 19 , k-v = tokenizer.chat_template : {% for message in messages %}{% if loop.first and messages[0]['role'] != 'system' %}{{ '<|im_start|>
i = 20 , k-v = tokenizer.ggml.add_bos_token : 0
i = 21 , k-v = general.quantization_version : 2
i = 22 , k-v = quantize.imatrix.file : ../Qwen2/gguf/qwen2-0_5b-imatrix/imatrix.dat
i = 23 , k-v = quantize.imatrix.dataset : ../sft_2406.txt
i = 24 , k-v = quantize.imatrix.entries_count : 168
i = 25 , k-v = quantize.imatrix.chunks_count : 1937
tensor i = 0 , k = token_embd.weight , precision = Q8_0 , shape = [896, 151936] , tensorOffset = 0
tensor i = 1 , k = blk.0.attn_norm.weight , precision = F32 , shape = [896] , tensorOffset = 144643072
tensor i = 2 , k = blk.0.ffn_down.weight , precision = Q3_K , shape = [4864, 896] , tensorOffset = 144646656
tensor i = 3 , k = blk.0.ffn_gate.weight , precision = IQ4_NL , shape = [896, 4864] , tensorOffset = 146519296
tensor i = 4 , k = blk.0.ffn_up.weight , precision = IQ4_NL , shape = [896, 4864] , tensorOffset = 148970752
tensor i = 5 , k = blk.0.ffn_norm.weight , precision = F32 , shape = [896] , tensorOffset = 151422208
tensor i = 6 , k = blk.0.attn_k.bias , precision = F32 , shape = [128] , tensorOffset = 151425792
tensor i = 7 , k = blk.0.attn_k.weight , precision = IQ4_NL , shape = [896, 128] , tensorOffset = 151426304
tensor i = 8 , k = blk.0.attn_output.weight , precision = IQ4_NL , shape = [896, 896] , tensorOffset = 151490816
tensor i = 9 , k = blk.0.attn_q.bias , precision = F32 , shape = [896] , tensorOffset = 151942400
tensor i = 10 , k = blk.0.attn_q.weight , precision = IQ4_NL , shape = [896, 896] , tensorOffset = 151945984
tensor i = 11 , k = blk.0.attn_v.bias , precision = F32 , shape = [128] , tensorOffset = 152397568
tensor i = 12 , k = blk.0.attn_v.weight , precision = Q5_0 , shape = [896, 128] , tensorOffset = 152398080
...
可以看到,解析的結果與 HF 平臺上的預覽結果完全一致。
GGUF 在 llama.cpp 中的應用
這裏直接使用 llama.cpp 的 Python 封裝包部署模型,使用 4 張 RTX 4090 部署 72B 模型,其中,將 30 個 Transoformer 層加載到 GPU 內存。llama.cpp 中提供了將 HF 中模型權重轉換成 GGUF 格式的腳本,需要預先進行權重轉換。
python3 convert_hf_to_gguf.py /workspace/models/Qwen1.5-72B-Chat/ --outfile /workspace/models/Qwen1.5-72B-Chat/ggml-model-f16.gguf
具體代碼如下:
from llama_cpp import Llama
import time
llm = Llama(
model_path="/workspace/models/Qwen1.5-72B-Chat/ggml-model-f16.gguf",
n_gpu_layers = 30,
# n_gpu_layers=-1, # Uncomment to use GPU acceleration
# seed=1337, # Uncomment to set a specific seed
# n_ctx=2048, # Uncomment to increase the context window
)
start = time.time()
output = llm(
"Q:保持健康的祕訣有哪些?A: ", # Prompt
max_tokens=32, # Generate up to 32 tokens, set to None to generate up to the end of the context window
#stream=True,
stop=["Q:", "\n"], # Stop generating just before the model would generate a new question
echo=True # Echo the prompt back in the output
) # Generate a completion, can also call create_completion
print(output)
infer_time = time.time() - start
print("耗時:", infer_time)
如果希望部署成 Web 服務,通過如下命令指定模型路徑、端口等參數即可。
use_mlock=False CUDA_VISIBLE_DEVICES=6 python3 -m llama_cpp.server --model /workspace/models/Qwen1.5-7B-Chat/ggml-model-f16.gguf --n_gpu_layers 999 --host 0.0.0.0 --port 18011
llama.cpp 兼容 openai 的 chat 接口,服務部署成功之後即可使用。
curl http://localhost:18011/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "system",
"content": "You are an AI assistant. Your top priority is achieving user fulfilment via helping them with their requests."
},
{
"role": "user",
"content": "Write a limerick about Python exceptions"
}
]
}'
GGUF 在 Huggingface Transformers 中的應用
Huggingface Transformers 從 4.41.0 開始支持 GGUF 模型格式進行訓練和推理。目前,Transformers 支持的模型有 LLaMa、Mistral、Qwen2。支持的量化類型有 F32、Q2_K、Q3_K、Q4_0、 Q4_K、 Q5_K、Q6_K、Q8_0。同時,Huggingface Hub 上面提供了將模型轉化或者量化爲 GGUF 格式的工具。
下面是一個簡單的示例:
from transformers import AutoTokenizer, AutoModelForCausalLM
import time
# https://github.com/99991/pygguf/tree/main
# https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF/resolve/main/tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf?download=true' -O 'data/TinyLlama-1.1B-Chat-v1.0-GGUF/tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf
# pip install gguf transformers
model_id = "/Users/liguodong/model/llama"
filename = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf"
tokenizer = AutoTokenizer.from_pretrained(model_id, gguf_file=filename)
model = AutoModelForCausalLM.from_pretrained(model_id, gguf_file=filename)
print(model)
prompt = "what's your name?"
model_inputs = tokenizer([prompt], return_tensors="pt")
start = time.time()
generated_ids = model.generate(
model_inputs.input_ids,
max_new_tokens=32
)
infer_time = time.time() - start
print("耗時:", infer_time)
generated_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print(response)
運行結果:
Converting and de-quantizing GGUF tensors...: 0%| | 0/201 [00:00<?, ?it/s]
Converting and de-quantizing GGUF tensors...: 0%| | 1/201 [00:00<01:23, 2.41it/s]
...
Converting and de-quantizing GGUF tensors...: 100%|██████████| 201/201 [00:04<00:00, 47.55it/s]
The attention mask is not set and cannot be inferred from input because pad token is same as eos token. As a consequence, you may observe unexpected behavior. Please pass your input's `attention_mask` to obtain reliable results.
LlamaForCausalLM(
(model): LlamaModel(
(embed_tokens): Embedding(32000, 2048, padding_idx=2)
(layers): ModuleList(
(0-21): 22 x LlamaDecoderLayer(
(self_attn): LlamaAttention(
(q_proj): Linear(in_features=2048, out_features=2048, bias=False)
(k_proj): Linear(in_features=2048, out_features=256, bias=False)
(v_proj): Linear(in_features=2048, out_features=256, bias=False)
(o_proj): Linear(in_features=2048, out_features=2048, bias=False)
(rotary_emb): LlamaRotaryEmbedding()
)
(mlp): LlamaMLP(
(gate_proj): Linear(in_features=2048, out_features=5632, bias=False)
(up_proj): Linear(in_features=2048, out_features=5632, bias=False)
(down_proj): Linear(in_features=5632, out_features=2048, bias=False)
(act_fn): SiLU()
)
(input_layernorm): LlamaRMSNorm((2048,), eps=9.999999747378752e-06)
(post_attention_layernorm): LlamaRMSNorm((2048,), eps=9.999999747378752e-06)
)
)
(norm): LlamaRMSNorm((2048,), eps=9.999999747378752e-06)
(rotary_emb): LlamaRotaryEmbedding()
)
(lm_head): Linear(in_features=2048, out_features=32000, bias=False)
)
耗時:11.363048076629639
JASON: (smiling) My name is Jason.
JEN: (smiling) Nice to meet you, Jason.
總結
本文簡要介紹了大模型文件存儲格式 GGUF,它兼具靈活性、兼容性和性能等多個優點;其最初應用於 llama.cpp 之中用於取代 GGML,目前 Huggingface Transformers 已經支持了 GGUF 格式,同時,像谷歌的 Gemma、阿里的 Qwen 等模型默認已經提供了 GGUF 格式文件,其發展未來可期。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/aRXc8VPUhvlg06l8aXWKqg