一文搞懂大模型文件存儲格式新寵 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 具有如下特性:

GGJT 和 GGUF 之間的主要區別在於:超參數(現稱爲元數據)使用鍵值結構,而不是非類型化的值列表。這允許在不破壞與現有模型的兼容性的情況下添加新的元數據,這使得可以添加對推理或識別模型有用的附加信息來註釋模型。

爲什麼 GGUF 格式對大模型文件性能很好

GGUF 文件格式能夠更快載入模型的原因主要歸結於以下幾個關鍵特性:

  1. 二進制格式:GGUF 作爲一種二進制格式,相較於文本格式的文件,可以更快地被讀取和解析。二進制文件通常更緊湊,減少了讀取和解析時所需的 I/O 操作和處理時間。

  2. 優化的數據結構:GGUF 可能採用了特別優化的數據結構,這些結構爲快速訪問和加載模型數據提供了支持。例如,數據可能按照內存加載的需要進行組織,以減少加載時的處理。

  3. 內存映射(mmap)兼容性:GGUF 支持內存映射(mmap),這允許直接從磁盤映射數據到內存地址空間,從而加快了數據的加載速度。這樣,數據可以在不實際加載整個文件的情況下被訪問,特別是對於大模型非常有效。

  4. 高效的序列化和反序列化:GGUF 使用高效的序列化和反序列化方法,這意味着模型數據可以快速轉換爲可用的格式。

  5. 少量的依賴和外部引用:如果 GGUF 格式設計爲自包含,即所有需要的信息都存儲在單個文件中,這將減少解析和加載模型時所需的外部文件查找和讀取操作。

  6. 數據壓縮:GGUF 格式採用了有效的數據壓縮技術,減少了文件大小,從而加速了讀取過程。

  7. 優化的索引和訪問機制:文件中數據的索引和訪問機制經過優化,使得查找和加載所需的特定數據片段更加迅速。

總之,GGUF 通過各種優化手段實現了快速的模型加載,這對於需要頻繁載入不同模型的場景尤爲重要。

GGUF 文件結構

一個 GGUF 文件包括文件頭、元數據鍵值對和張量信息等。這些組成部分共同定義了模型的結構和行爲。具體如下所示:

同時,GGUF 支持多種數據類型,如整數、浮點數和字符串等。這些數據類型用於定義模型的不同方面,如結構、大小和參數。

GGUF 文件具體的組成信息如下所示:

  1. 文件頭 (Header)
  1. 元數據 key-value 對 (Metadata Key-Value Pairs)
  1. 張量計數器 (Tensor Count)
  1. 張量信息 (Tensor Info)
  1. 對齊填充 (Alignment Padding)
  1. 張量數據 (Tensor Data)
  1. 端序標識 (Endianness)
  1. 擴展信息 (Extension Information)

在張量信息部分,GGUF 定義了模型的量化級別。量化級別取決於模型根據質量和準確性定義的值(ggml_type)。在 GGUF 規範中,值列表如下:

bmY2zZ

量化與反量化轉換的 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