选择哪种量化方法适合您?(GPTQ vs. GGUF vs. AWQ)

本文探讨了在处理大型语言模型时,如何通过HuggingFace、分片、量化技术(如GPTQ、GGUF和AWQ)来优化模型加载和内存管理。作者介绍了使用Bitsandbytes进行4位量化的过程,并比较了几种预量化方法的适用场景和性能特点。
摘要由CSDN通过智能技术生成


探索预量化的大型语言模型

文章翻译:Maarten Grootendorst Which Quantization Method is Right for You? (GPTQ vs. GGUF vs. AWQ)

在过去的一年中,我们见证了大型语言模型(LLM)的狂野西部。发布新技术和模型的速度令人惊叹!因此,我们有许多不同的标准和处理LLM的方式。

在本文中,我们将探讨一个话题,即通过几种(量化)标准加载本地LLM。通过分片、量化和不同的保存和压缩策略,很难知道哪种方法适合您。

在示例中,我们将使用Zephyr 7B,这是一个经过Direct Preference Optimization(DPO)训练的Mistral 7B的精调变体。

🔥 提示:在加载LLM的每个示例之后,建议重新启动您的笔记本以防止内存不足错误。加载多个LLM需要大量的RAM/VRAM。您可以通过删除模型并重置缓存来重置内存,如下所示:

# 删除之前创建的任何模型
del model, tokenizer, pipe

# 清空VRAM缓存
import torch
torch.cuda.empty_cache()

您还可以使用Google Colab Notebook跟随操作,以确保一切正常。 https://colab.research.google.com/drive/1rt318Ew-5dDw21YZx2zK2vnxbsuDAchH?usp=sharing

1. HuggingFace

加载LLM的最简单、最基本的方法是通过🤗 Transformers。HuggingFace创建了一套大型的软件包,使我们能够在LLM上做出惊人的事情!

我们将从其主分支安装HuggingFace,以支持更新的模型:

# 用于类似Mistral的最新HF transformers版本
pip install git+https://github.com/huggingface/transformers.git
pip install accelerate bitsandbytes xformers

安装完成后,我们可以使用以下流水线轻松加载LLM:

from torch import bfloat16
from transformers import pipeline

# 加载未经任何压缩技巧的LLM
pipe = pipeline(
    "text-generation", 
    model="HuggingFaceH4/zephyr-7b-beta", 
    torch_dtype=bfloat16, 
    device_map="auto"
)

这种加载LLM的方法通常不会执行任何压缩技巧,以节省VRAM或提高效率。

为了生成我们的提示,我们首先需要创建必要的模板。幸运的是,如果聊天模板保存在底层的分词器中,这可以自动完成:

# 我们使用分词器的聊天模板来格式化每条消息
# 请参阅https://huggingface.co/docs/transformers/main/en/chat_templating
messages = [
    {
        "role": "system",
        "content": "You are a friendly chatbot.",
    },
    {
        "role": "user", 
        "content": "Tell me a funny joke about Large Language Models."
    },
]
prompt = pipe.tokenizer.apply_chat_template(
    messages, 
    tokenize=False, 
    add_generation_prompt=True
)

使用内部提示模板生成的提示如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

提示模板是使用内部提示模板自动生成的。请注意,有不同的标签来区分用户和助手。

然后,我们可以将提示传递给LLM以生成我们的答案:

outputs = pipe(
    prompt, 
    max_new_tokens=256, 
    do_sample=True, 
    temperature=0.1, 
    top_p=0.95
)
print(outputs[0]["generated_text"])

这给出了以下输出:

为什么大型语言模型去参加派对?

为了扩展词汇量和拓展人脉!

这个笑话可能有点俗套,但大型语言模型的目标就是扩展其词汇量并与其他模型建立联系,以提高其语言技能。所以,这个笑话非常适合它们!

对于纯推理来说,这种方法通常效率最低,因为我们加载整个模型而没有使用任何压缩或量化策略。

然而,这是一个很好的起点,因为它可以轻松加载和使用模型!

2. 分片

在我们进入量化策略之前,还有另一个技巧可以减少加载模型所需的VRAM。通过分片,我们实际上是将模型分成小块或分片

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分片LLM只是将其分成几个部分。每个单独的部分更容易处理,可能会防止内存问题。

每个分片包含模型的一个较小部分,并通过将模型权重分布在不同设备上来解决GPU内存限制的问题。

还记得我之前说过我们之前没有执行任何压缩技巧吗?

那并不完全正确…

我们加载的模型Zephyr-7B-β实际上已经被分片了!如果您转到该模型并点击“文件和版本”链接,您将看到该模型被分成了八个部分。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

该模型被分成了八个小块或分片。这减少了所需的VRAM,因为我们只需要处理这些小块。

虽然我们可以自己分片模型,但通常建议留意量化模型,甚至自己进行量化。

使用Accelerate软件包很容易进行分片:

from accelerate import Accelerator

# 将我们的模型分片为1GB的块
accelerator = Accelerator()
accelerator.save_model(
    model=pipe.model, 
    save_directory="/content/model", 
    max_shard_size="4GB"
)

就是这样!因为我们将模型分成了4GB的块而不是2GB,所以我们创建了较少的文件来加载:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3. 使用Bitsandbytes进行量化

大型语言模型由一系列权重和激活值表示。这些值通常由通常的32位浮点(float32)数据类型表示。

位数告诉您它可以表示多少个值。Float32可以表示1.18e-38到3.4e38之间的值,相当多的值!位数越少,模型的准确性越低,但它也需要表示的值越少,从而减小了其大小和内存要求。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

常见的值表示方法。我们的目标是尽可能降低位数,同时最大限度地扩大表示的范围和精度。

正如您可能期望的那样,如果我们选择较低的位数,那么模型的准确性就会降低,但它也需要表示的值更少,从而减小了其大小和内存要求。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不同的表示方法可能会对表示值的精度产生负面影响。某些值甚至根本无法表示(例如对于float16来说,值太大了)。示例是使用PyTorch计算的。

量化是指将LLM从其原始的Float32表示转换为较小的表示。但我们不仅仅想使用较小的位变体,而是将较大的位表示映射到较小的位而不会丢失太多信息。

在实践中,我们经常使用一种名为**4bit-NormalFloat (NF4)**的新格式进行量化。该数据类型通过一些特殊技巧有效地表示较大位数据类型。它由三个步骤组成:

  1. 归一化:将模型的权重归一化,使我们期望权重在一定范围内。这样可以更有效地表示常见值。
  2. 量化:将权重量化为4位。在NF4中,量化级别与归一化权重均匀间隔,从而有效地表示原始的32位权重。
  3. 去量化:虽然权重存储在4位中,但在计算过程中会进行去量化,这在推理过程中提供了性能提升。

要使用HuggingFace执行此量化,我们需要使用Bitsandbytes定义一个量化配置:

from transformers import BitsAndBytesConfig
from torch import bfloat16

# 我们的4位配置,以较少的GPU内存加载LLM
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,  # 4位量化
    bnb_4bit_quant_type='nf4',  # 归一化浮点4
    bnb_4bit_use_double_quant=True,  # 第一次量化后的第二次量化
    bnb_4bit_compute_dtype=bfloat16  # 计算类型
)

这个配置允许我们指定我们要使用的量化级别。通常,我们希望使用4位量化来表示权重,但在推理过程中使用16位。

然后,通过流水线轻松加载模型:

from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline

# 使用BitsAndBytes配置的Zephyr
tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-alpha")
model = AutoModelForCausalLM.from_pretrained(
    "HuggingFaceH4/zephyr-7b-alpha",
    quantization_config=bnb_config,
    device_map='auto',
)

# 创建一个流水线
pipe = pipeline(model=model, tokenizer=tokenizer, task='text-generation')

接下来,我们可以使用与之前相同的提示:

# We will use the same prompt as we did originally
outputs = pipe(
    prompt, 
    max_new_tokens=256, 
    do_sample=True, 
    temperature=0.7, 
    top_p=0.95
)
print(outputs[0]["generated_text"])

这将给我们以下输出:

为什么大型语言模型去参加派对?

为了扩展词汇量和建立人脉!

这个笑话可能有点俗套,但大型语言模型的目标就是扩展自己的词汇量,并与其他模型建立联系,以提高语言能力。所以,这个笑话非常适合它们!

量化是一种强大的技术,可以在保持性能相似的情况下减少模型的内存需求。它可以使使用较小的GPU更快地加载、使用和微调大型语言模型。

4. 预量化 (GPTQ vs. AWQ vs. GGUF)

到目前为止,我们已经探索了分片和量化技术。虽然这些技术对于你的技能来说很有用,但每次加载模型时都需要应用它们似乎有些浪费。

相反,这些模型通常已经被分片和量化,供我们使用。特别是 HuggingFace 上的用户 TheBloke 就为我们提供了许多量化模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

截至撰写本文时,他已经为我们上传了2000多个量化模型!

这些量化模型实际上有很多不同的形状和大小。最常用的是 GPTQ、GGUF 和 AWQ 格式,用于进行4位量化。

GPTQ: GPT 模型的训练后量化

GPTQ 是一种针对4位量化的训练后量化 (PTQ) 方法,主要关注GPU推理和性能。

该方法的思想是通过将所有权重压缩到4位量化中,通过最小化与该权重的均方误差来实现。在推理过程中,它将动态地将权重解量化为float16,以提高性能,同时保持内存较低。

有关 GPTQ 内部工作原理的更详细指南,请务必查看以下文章:

使用 AutoGPTQ 对自己的 LLM 进行量化

towardsdatascience.com

首先,我们需要安装一些加载 GPTQ 类型模型所需的软件包:

pip install optimum
pip install auto-gptq --extra-index-url https://huggingface.github.io/autogptq-index/whl/cu118/

完成后,我们可以转到要加载的模型,即 “TheBloke/zephyr-7B-beta-GPTQ” 并选择特定的修订版本。

这些修订版本实际上指示了量化方法、压缩级别、模型大小等。

现在,我们选择“main”分支,因为它通常在压缩和准确性之间取得了良好的平衡:

from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline

# 加载 LLM 和 Tokenizer
model_id = "TheBloke/zephyr-7B-beta-GPTQ"
tokenizer = AutoTokenizer.from_pretrained(model_id, use_fast=True)
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    device_map="auto",
    trust_remote_code=False,
    revision="main"
)

# 创建一个 pipeline
pipe = pipeline(model=model, tokenizer=tokenizer, task='text-generation')

尽管我们安装了一些额外的依赖项,但我们可以使用之前使用的相同的 pipeline,这是使用 GPTQ 的一个很大的好处。

加载模型后,我们可以运行以下提示:

# We will use the same prompt as we did originally
outputs = pipe(
    prompt,
    max_new_tokens=256,
    do_sample=True,
    temperature=0.1,
    top_p=0.95
)
print(outputs[0]["generated_text"])

这给我们以下生成的文本:

为什么大型语言模型去参加派对?

当然是为了展示它的机智和魅力!

但不幸的是,它在人群中迷路了,找不到回家的路。派对参与者对它能够与人群融为一体的能力印象深刻,但大型语言模型只是感到困惑,想回家。最后,它被一群人发现了,他们认出了它独特的风格,把它带回了它应该待的地方。从那时起,大型语言模型确保在所有派对上都佩戴着名牌,以确保安全。

GPTQ 是最常用的压缩方法,因为它针对 GPU 使用进行了优化。如果你的 GPU 无法处理如此大的模型,那么从 GPTQ 开始,然后切换到以 CPU 为重点的方法,如 GGUF,是非常值得的。

GGUF: GPT 生成的统一格式

尽管 GPTQ 在压缩方面表现出色,但如果你没有运行它所需的硬件,它对 GPU 的依赖性可能会成为一个缺点。

GGUF(以前称为 GGML)是一种量化方法,允许用户在 CPU 上运行 LLM,同时将其部分层次转移到 GPU 上以加速运行。

尽管使用 CPU 通常比使用 GPU 进行推理要慢,但对于在 CPU 或 Apple 设备上运行模型的人来说,这是一种非常好的格式。特别是我们看到出现了更小、更强大的模型,如 Mistral 7B,GGUF 格式可能会成为一种常见的格式!

使用 GGUF 非常简单,只需安装 ctransformers 包:

pip install ctransformers[cuda]

完成后,我们可以转到要加载的模型,即 “TheBloke/zephyr-7B-beta-GGUF” 并选择一个特定的文件。

与 GPTQ 类似,这些文件指示了量化方法、压缩级别、模型大小等。

我们使用“zephyr-7b-beta.Q4_K_M.gguf”,因为我们关注的是4位量化:

from ctransformers import AutoModelForCausalLM
from transformers import AutoTokenizer, pipeline

# 加载 LLM 和 Tokenizer
# 使用 `gpu_layers` 指定要转移到 GPU 的层数。
model = AutoModelForCausalLM.from_pretrained(
    "TheBloke/zephyr-7B-beta-GGUF",
    model_file="zephyr-7b-beta.Q4_K_M.gguf",
    model_type="mistral", gpu_layers=50, hf=True
)
tokenizer = AutoTokenizer.from_pretrained(
    "HuggingFaceH4/zephyr-7b-beta", use_fast=True
)

# 创建一个 pipeline
pipe = pipeline(model=model, tokenizer=tokenizer, task='text-generation')

加载模型后,我们可以运行以下提示:

# We will use the same prompt as we did originally
outputs = pipe(prompt, max_new_tokens=256)
print(outputs[0]["generated_text"])

这给我们以下输出:

为什么大型语言模型去参加派对?

为了展示它的词汇量!

但不幸的是,它一遍又一遍地重复相同的笑话,让每个人都嗤之以鼻。派对参与者很快意识到,大型语言模型更像是一个派对扫兴者,而不是派对动物。

故事的寓意是:仅仅因为一个大型语言模型可以生成很多词语,并不意味着它知道如何搞笑或娱乐。有时候,少即是多!

如果你想在你像我一样的 GPU 贫乏且没有最新最好的 GPU 的情况下充分利用 CPU 和 GPU,那么 GGUF 是一种绝佳的格式。

AWQ: 激活感知的权重量化

AWQ(Activation-aware Weight Quantization)是一种类似于 GPTQ 的量化方法。AWQ 和 GPTQ 之间有几个区别,但最重要的区别是 AWQ 假设并非所有权重对 LLM 的性能都同等重要。

换句话说,在量化过程中,有一小部分权重将被跳过,这有助于减少量化损失。

因此,他们的论文提到与 GPTQ 相比,它们在保持类似甚至更好性能的同时实现了显著的加速。

该方法仍然相对较新,并且尚未像 GPTQ 和 GGUF 那样被广泛采用,因此很有趣的是看到所有这些方法是否可以共存。

对于 AWQ,我们将使用 vLLM 包,因为在我的经验中,这是使用 AWQ 的最简单的方法:

pip install vllm

使用 vLLM,加载和使用模型变得非常简单:

from vllm import LLM, SamplingParams

# 加载 LLM
sampling_params = SamplingParams(temperature=0.0, top_p=1.0, max_tokens=256)
llm = LLM(
    model="TheBloke/zephyr-7B-beta-AWQ", 
    quantization='awq', 
    dtype='half', 
    gpu_memory_utilization=.95, 
    max_model_len=4096
)

然后,我们可以使用 .generate 轻松运行模型:

# 根据输入提示和采样参数生成输出
output = llm.generate(prompt, sampling_params)
print(output[0].outputs[0].text)

这给我们以下输出:

为什么大型语言模型去参加派对?

为了扩展词汇量和建立人脉!

为什么大型语言模型会脸红?

因为它听到另一个模型说它有点啰嗦!

为什么大型语言模型被图书馆赶出去?

它太吵了,一直打断其他模型的对话,不停地说个不停!

尽管 AWQ 是一种新的格式,但由于其速度和压缩质量,它正在变得越来越受欢迎!

  • 18
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
wandb: Tracking run with wandb version 0.15.5 wandb: W&B syncing is set to `offline` in this directory. wandb: Run `wandb online` or set WANDB_MODE=online to enable cloud syncing. /home/zhangmengjie/anaconda3/envs/torch1/lib/python3.7/site-packages/gym/envs/registration.py:556: UserWarning: WARN: The environment Ant-v2 is out of date. You should consider upgrading to version `v4`. f"The environment {id} is out of date. You should consider " Error compiling Cython file: ------------------------------------------------------------ ... See c_warning_callback, which is the C wrapper to the user defined function ''' global py_warning_callback global mju_user_warning py_warning_callback = warn mju_user_warning = c_warning_callback ^ ------------------------------------------------------------ /home/zhangmengjie/anaconda3/envs/torch1/lib/python3.7/site-packages/mujoco_py/cymj.pyx:92:23: Cannot assign type 'void (const char *) except * nogil' to 'void (*)(const char *) noexcept nogil' Error compiling Cython file: ------------------------------------------------------------ ... See c_warning_callback, which is the C wrapper to the user defined function ''' global py_error_callback global mju_user_error py_error_callback = err_callback mju_user_error = c_error_callback ^ ------------------------------------------------------------ /home/zhangmengjie/anaconda3/envs/torch1/lib/python3.7/site-packages/mujoco_py/cymj.pyx:127:21: Cannot assign type 'void (const char *) except * nogil' to 'void (*)(const char *) noexcept nogil' Compiling /home/zhangmengjie/anaconda3/envs/torch1/lib/python3.7/site-packages/mujoco_py/cymj.pyx because it changed. [1/1] Cythonizing /home/zhangmengjie/anaconda3/envs/torch1/lib/python3.7/site-packages/mujoco_py/cymj.pyx wandb: Waiting for W&B process to finish... (failed 1). wandb: You can sync this run to the cloud by running: wandb: wandb sync /home/zhangmengjie/PID/Python/ERL-Re2-main/wandb/offline-run-20230721_165346-awq1hazo wandb: Find logs at: ./wandb/offline-run-20230721_165346-awq1hazo/logs
07-22
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数智笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值