使用bitsandbytes、4位量化和QLoRA使LLMs更加易于访问

Using bitsandbytes, 4-bit quantization, and QLoRA makes LLMs more accessible.

LLMs被认为是庞大的,对用户和可访问性来说,在消费级硬件上运行或训练它们是一个巨大的挑战。我们的LLM.int8博文展示了LLM.int8论文中的技术如何使用bitsandbytes库集成到transformers中。为了让模型更加易于使用,我们决定再次与bitsandbytes合作,允许用户以4位精度运行模型。这包括了大多数HF模型,以任何形式(文本、视觉、多模态等)存在。用户还可以使用Hugging Face生态系统中的工具在4位模型之上训练适配器。这是Dettmers等人在QLoRA论文中今天介绍的一种新方法。该论文的摘要如下:

我们提出了QLoRA,一种高效的微调方法,可以在单个48GB GPU上减少内存使用量,同时保持完整的16位微调任务性能。QLoRA通过将梯度反向传播到一个冻结的、4位量化的预训练语言模型中,进而反向传播到低秩适配器(LoRA)。我们的最佳模型系列被命名为Guanaco,在Vicuna基准测试中超过了所有之前公开发布的模型,达到了ChatGPT性能水平的99.3%,而只需要在单个GPU上进行24小时的微调。QLoRA引入了一些创新来节省内存,而不牺牲性能:(a) 4位NormalFloat(NF4),这是一种对于正态分布权重在信息论上最优的新数据类型;(b) 双重量化,通过量化量化常数来减少平均内存占用;(c) 分页优化器来管理内存峰值。我们使用QLoRA微调了1000多个模型,并对8个指令数据集、多个模型类型(LLaMA、T5)和模型规模进行了详细的指令遵循和聊天机器人性能分析,这在常规微调中是不可行的(例如33B和65B参数模型)。我们的结果表明,QLoRA在一个小而高质量的数据集上进行微调可以达到最先进的结果,甚至可以使用比之前的最优模型更小的模型。我们提供了基于人类和GPT-4评估的聊天机器人性能的详细分析,表明GPT-4评估是一种廉价且合理的替代人工评估的方法。此外,我们发现当前的聊天机器人基准测试不能准确评估聊天机器人的性能水平。一个精选的分析展示了Guanaco与ChatGPT相比的失败之处。我们发布了所有模型和代码,包括用于4位训练的CUDA内核。

资源

本博文和发布附带了几个资源,可以帮助您开始使用4位模型和QLoRA:

  • 原始论文
  • 基本用法Google Colab笔记本 – 该笔记本展示了如何在推断中使用4位模型及其各种变体,以及如何在免费的Google Colab实例上运行GPT-neo-X(一个20B参数的模型)🤯
  • 微调Google Colab笔记本 – 该笔记本展示了如何使用Hugging Face生态系统在下游任务上微调4位模型。我们展示了在Google Colab实例上微调GPT-neo-X 20B是可行的!
  • 复制论文结果的原始存储库
  • Guanaco 33b游乐场 – 或查看下面的游乐场部分

介绍

如果您对模型精度和最常见的数据类型(float16、float32、bfloat16、int8)不熟悉,我们建议您仔细阅读我们第一篇博文中介绍这些概念的细节,其中包含了简单的术语和可视化。

更多信息,我们建议阅读这本wikibook文档中关于浮点数表示基础的内容。

最近的QLoRA论文探讨了不同的数据类型,包括4位浮点数和4位NormalFloat。在这里我们将讨论4位浮点数数据类型,因为它更容易理解。

FP8和FP4分别代表浮点数8位和4位精度。它们是浮点数值的minifloats系列的一部分(除了其他精度,minifloats系列还包括bfloat16和float16)。

让我们首先看一下如何以FP8格式表示浮点数值,然后了解FP4格式的样子。

FP8格式

正如我们之前的博文中所讨论的,浮点数包含n位,每一位都属于特定的类别,负责表示数字的一个组成部分(符号、尾数和指数)。它们分别表示如下:

FP8(浮点8)格式首次在《FP8用于深度学习》一文中提出,有两种不同的FP8编码:E4M3(4位指数和3位尾数)和E5M2(5位指数和2位尾数)。

尽管将位数从32位减少到8位会大大降低精度,但这两个版本都可以在各种情况下使用。目前可以使用与HF生态系统集成的Transformer Engine库。

E4M3格式可以表示的潜在浮点数范围为-448至448,而在E5M2格式中,随着指数位数的增加,范围增加到-57344至57344,但由于可能的表示数量保持不变,精度会下降。经实验证明,E4M3最适合前向传递,而第二个版本最适合反向计算。

FP4精度简介

符号位表示符号(+/-),指数位表示由位表示的整数的二次幂(例如2^{010} = 2^{2} = 4),而分数或尾数是每个位为“1”时负二的幂的和。如果位为“0”,则尾数在该2的幂的位置上保持不变,例如,对于尾数位1010,我们有(0 + 2^-1 + 0 + 2^-3) = (0.5 + 0.125) = 0.625。要获取一个值,我们将分数加1并将所有结果相乘,例如,具有2个指数位和一个尾数位的表示1101将变为:

-1 * 2^(2) * (1 + 2^-1) = -1 * 4 * 1.5 = -6

对于FP4,没有固定的格式,因此可以尝试不同的尾数/指数组合。一般来说,大多数情况下,3个指数位效果稍好。但有时,2个指数位和一个尾数位会产生更好的性能。

QLoRA论文,一种新的民主量化大型Transformer模型的方式

简而言之,QLoRA在不牺牲性能的情况下减少了LLM微调的内存使用量,与标准的16位模型微调相比。这种方法使得在单个24GB GPU上可以进行33B模型微调,并且在单个46GB GPU上可以进行65B模型微调。

具体而言,QLoRA使用4位量化来压缩预训练语言模型。然后,LM参数被冻结,并向模型中添加一些相对较小数量的可训练参数,以形成低秩适配器。在微调过程中,QLoRA通过冻结的4位量化预训练语言模型向低秩适配器反向传播梯度。在训练过程中,只有LoRA层的参数被更新。在原始的LoRA论文中可以了解更多关于LoRA的内容。

QLoRA有一个存储数据类型(通常为4位NormalFloat)用于基础模型的权重,以及一个计算数据类型(16位BrainFloat)用于执行计算。QLoRA将权重从存储数据类型解压缩到计算数据类型以执行前向和后向传递,但仅为使用16位bfloat的LoRA参数计算权重梯度。权重只在需要时解压缩,因此在训练和推断过程中内存使用量保持较低。

QLoRA微调在各种实验中显示与16位微调方法相匹配。此外,使用QLoRA微调的Guanaco模型用于OpenAssistant数据集(OASST1)上的LLaMA模型,是最先进的聊天机器人系统,并且在Vicuna基准测试上接近ChatGPT。这进一步证明了QLoRA微调的强大性能。

为了更详细的阅读,我们建议您阅读 QLoRA 论文。

如何在 transformers 中使用它?

在本节中,让我们介绍一下这种方法在 transformers 中的集成,如何使用它以及哪些模型可以有效地进行量化。

入门

作为快速入门,通过以下方式加载一个 4bit 的模型(在撰写本文时),安装来自源码的 accelerate 和 transformers,并确保您已安装了 bitsandbytes 库的最新版本(0.39.0)。

pip install -q -U bitsandbytes
pip install -q -U git+https://github.com/huggingface/transformers.git
pip install -q -U git+https://github.com/huggingface/peft.git
pip install -q -U git+https://github.com/huggingface/accelerate.git

快速入门

加载一个 4bit 的模型的基本方法是在调用 from_pretrained 方法时传递参数 load_in_4bit=True,并提供一个设备映射(传递 "auto" 获取将自动推断的设备映射)。

from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained("facebook/opt-350m", load_in_4bit=True, device_map="auto")
...

这就是你所需要的!

作为一般规则,我们建议用户在加载了 device_map 的模型后不要手动设置设备。因此,在那一行之后,应避免对模型或任何模型的子模块进行任何设备分配调用 – 除非您知道自己在做什么。

请记住,加载量化模型将自动将其他模型的子模块转换为 float16 dtype。您可以通过将 torch_dtype=dtype 传递给 from_pretrained 方法来更改此行为(例如,如果您希望将层规范化为 float32 )。

高级用法

您可以尝试不同变体的 4bit 量化,例如 NF4(归一化浮点 4(默认))或纯 FP4 量化。根据论文中的理论考虑和实证结果,我们建议使用 NF4 量化以获得更好的性能。

其他选项包括 bnb_4bit_use_double_quant,它在第一次量化之后使用第二次量化以节省额外的 0.4 位每个参数。最后,计算类型。虽然 4 位的 bitsandbytes 将权重存储为 4 位,但计算仍然以 16 或 32 位进行,可以选择任何组合(float16、bfloat16、float32 等)。

如果使用 16 位的计算 dtype(默认为 torch.float32),矩阵乘法和训练将更快。应该利用 transformers 中的最新 BitsAndBytesConfig 来更改这些参数。以下是以 NF4 量化方式加载一个模型的示例,其中使用了双量化和计算 dtype bfloat16 以实现更快的训练:

from transformers import BitsAndBytesConfig


nf4_config = BitsAndBytesConfig(
   load_in_4bit=True,
   bnb_4bit_quant_type="nf4",
   bnb_4bit_use_double_quant=True,
   bnb_4bit_compute_dtype=torch.bfloat16
)

model_nf4 = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=nf4_config)

更改计算 dtype

如上所述,您还可以通过更改 BitsAndBytesConfig 中的 bnb_4bit_compute_dtype 参数来更改量化模型的计算 dtype。

import torch
from transformers import BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(
   load_in_4bit=True,
   bnb_4bit_compute_dtype=torch.bfloat16
)

嵌套量化

为了启用嵌套量化,您可以在 BitsAndBytesConfig 中使用 bnb_4bit_use_double_quant 参数。这将在第一次量化之后启用第二次量化,以节省每个参数额外的 0.4 位。我们在 Google colab 笔记本中的训练中也使用了此功能。

from transformers import BitsAndBytesConfig

double_quant_config = BitsAndBytesConfig(
   load_in_4bit=True,
   bnb_4bit_use_double_quant=True,
)

model_double_quant = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=double_quant_config)

当然,正如本节开头提到的,所有这些组件都是可以组合的。您可以将所有这些参数组合在一起,找到最适合您的用例的参数组合。一个经验法则是:如果内存出现问题,请使用双量化;对于更高的精度,请使用NF4;对于更快的微调,请使用16位的数据类型。例如,在推理演示中,我们使用嵌套量化、bfloat16计算数据类型和NF4量化,在单个16GB的GPU上完全将gpt-neo-x-20b(40GB)压缩到4位。

常见问题

在本节中,我们还将解答一些与该集成相关的常见问题。

FP4量化有硬件要求吗?

请注意,此方法只与GPU兼容,因此无法在CPU上将模型压缩为4位。在GPU中,对于此方法不应有任何硬件要求,因此只要安装了CUDA>=11.2,就可以使用任何GPU来运行4位量化。还要记住,计算并不是以4位执行的,而是将权重和激活值压缩到所需或本机的数据类型中。

支持的模型有哪些?

与本文介绍的LLM.int8的集成类似,这种集成严重依赖于accelerate库。因此,任何支持加速加载的模型(即调用from_pretrained时的device_map参数)都可以以4位进行量化。还要注意,这与模态无关,只要模型可以使用device_map参数加载,就可以进行量化。

对于文本模型,目前可以包括最常用的架构,如Llama、OPT、GPT-Neo、GPT-NeoX等。对于多模态模型,Blip2也是如此。

目前支持加速的模型有:

[
    'bigbird_pegasus', 'blip_2', 'bloom', 'bridgetower', 'codegen', 'deit', 'esm', 
    'gpt2', 'gpt_bigcode', 'gpt_neo', 'gpt_neox', 'gpt_neox_japanese', 'gptj', 'gptsan_japanese', 
    'lilt', 'llama', 'longformer', 'longt5', 'luke', 'm2m_100', 'mbart', 'mega', 'mt5', 'nllb_moe', 
    'open_llama', 'opt', 'owlvit', 'plbart', 'roberta', 'roberta_prelayernorm', 'rwkv', 'switch_transformers', 
    't5', 'vilt', 'vit', 'vit_hybrid', 'whisper', 'xglm', 'xlm_roberta'
]  

请注意,如果您喜欢的模型不在列表中,您可以在transformers的Pull Request中提出或提交一个问题,以添加加速加载该架构的支持。

是否可以训练4位/8位模型?

在这些模型上无法进行纯粹的4位训练。但是,您可以利用参数高效微调方法(PEFT)对这些模型进行训练,并在其上训练适配器。这就是论文中所做的,并且由Hugging Face的PEFT库正式支持。我们还提供了一个训练笔记本,并建议用户查看QLoRA存储库,以复制论文中的结果。

还有哪些其他影响?

这种集成可以对社区和人工智能研究带来许多积极的影响,因为它可以涉及多种用例和可能的应用。在RLHF(以人类反馈进行强化学习)中,可以加载单个基本模型,以4位进行训练,并在其上训练多个适配器,一个用于奖励建模,另一个用于值策略训练。关于这种用例,我们将很快发布更详细的博文和公告。

我们还对这种量化方法对在消费级硬件上训练大模型的影响进行了一些基准测试。我们在NVIDIA T4(16GB)上对2种不同架构的模型,Llama 7B(fp16存储15GB)和Llama 13B(fp16存储27GB),进行了多次微调实验,以下是结果:

我们使用了TRL库中的最新SFTTrainer,基准测试脚本可以在此处找到。

游乐场

在游乐场上尝试一下论文中提到的Guananco模型,或直接在下方尝试。

致谢

HF团队要感谢来自华盛顿大学的所有参与该项目的人员,并将其提供给社区使用。

作者还要感谢Pedro Cuenca对博文的仔细审查,以及Olivier Dehaene和Omar Sanseviero对将论文的成果整合到HF Hub中提供的快速和坚定支持。