bitsandbytes
bitsandbytes 是将模型量化为8位和4位的最简单选择。8位量化将fp16中的异常值与int8中的非异常值相乘,将非异常值转换回fp16,然后将它们相加以返回fp16中的权重。这减少了异常值对模型性能的退化影响。4位量化进一步压缩模型,通常与QLoRA一起用于微调量化的大型语言模型。
要使用bitsandbytes,请确保已安装以下库:
pip install transformers accelerate bitsandbytes>0.37.0
bitsandbytes 正在进行重构,以支持除 CUDA 之外的多种后端。目前,ROCm(AMD GPU)和 Intel CPU 的实现已经成熟,Intel XPU 正在开发中,预计将在 Q4/Q1 支持 Apple Silicon。有关安装说明和最新后端更新,请访问 此链接。
我们重视您的反馈,以帮助在正式发布前识别错误!查看这些文档以获取更多详细信息和反馈链接。
现在你可以通过将BitsAndBytesConfig
传递给from_pretrained()方法来量化模型。这适用于任何模态的任何模型,只要它支持使用Accelerate加载并包含torch.nn.Linear
层。
将模型量化为8位可以将内存使用减半,对于大型模型,设置device_map="auto"
以有效利用可用的GPU:
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
model_8bit = AutoModelForCausalLM.from_pretrained(
"bigscience/bloom-1b7",
quantization_config=quantization_config
)
默认情况下,所有其他模块如 torch.nn.LayerNorm
都会被转换为 torch.float16
。如果需要,您可以使用 torch_dtype
参数更改这些模块的数据类型。设置 torch_dtype="auto"
会以模型 config.json
文件中定义的数据类型加载模型。
import torch
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
model_8bit = AutoModelForCausalLM.from_pretrained(
"facebook/opt-350m",
quantization_config=quantization_config,
torch_dtype="auto"
)
model_8bit.model.decoder.layers[-1].final_layer_norm.weight.dtype
一旦模型被量化为8位,除非你使用的是最新版本的Transformers和bitsandbytes,否则无法将量化后的权重推送到Hub。如果你有最新版本,那么你可以使用push_to_hub()方法将8位模型推送到Hub。首先推送量化配置的config.json文件,然后是量化后的模型权重。
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModelForCausalLM.from_pretrained(
"bigscience/bloom-560m",
quantization_config=quantization_config
)
tokenizer = AutoTokenizer.from_pretrained("bigscience/bloom-560m")
model.push_to_hub("bloom-560m-8bit")
使用8位和4位权重进行训练仅支持训练额外参数。
你可以使用get_memory_footprint
方法来检查你的内存占用情况:
print(model.get_memory_footprint())
量化模型可以从from_pretrained()方法加载,而无需指定load_in_8bit
或load_in_4bit
参数:
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("{your_username}/bloom-560m-8bit", device_map="auto")
8位 (LLM.int8() 算法)
了解更多关于8位量化的详细信息,请查看这篇博客文章!
本节探讨了8位模型的一些特定功能,例如卸载、异常值阈值、跳过模块转换和微调。
卸载
8位模型可以在CPU和GPU之间卸载权重,以支持将非常大的模型装入内存。分配到CPU的权重实际上是以float32存储的,并没有转换为8位。例如,要为bigscience/bloom-1b7模型启用卸载功能,首先创建一个BitsAndBytesConfig:
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(llm_int8_enable_fp32_cpu_offload=True)
设计一个自定义设备映射,以便将除了lm_head
之外的所有内容适配到您的GPU上,而lm_head
将被分配到CPU上:
device_map = {
"transformer.word_embeddings": 0,
"transformer.word_embeddings_layernorm": 0,
"lm_head": "cpu",
"transformer.h": 0,
"transformer.ln_f": 0,
}
现在使用自定义的 device_map
和 quantization_config
加载您的模型:
model_8bit = AutoModelForCausalLM.from_pretrained(
"bigscience/bloom-1b7",
torch_dtype="auto",
device_map=device_map,
quantization_config=quantization_config,
)
异常值阈值
“异常值”是指大于某个阈值的隐藏状态值,这些值以fp16计算。虽然这些值通常呈正态分布([-3.5, 3.5]),但对于大型模型,这种分布可能非常不同([-60, 6]或[6, 60])。8位量化对于约5的值效果很好,但超过这个范围,性能会显著下降。一个好的默认阈值是6,但对于更不稳定的模型(小型模型或微调),可能需要更低的阈值。
为了找到模型的最佳阈值,我们建议在BitsAndBytesConfig中尝试使用llm_int8_threshold
参数:
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
model_id = "bigscience/bloom-1b7"
quantization_config = BitsAndBytesConfig(
llm_int8_threshold=10,
)
model_8bit = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype="auto",
device_map=device_map,
quantization_config=quantization_config,
)
跳过模块转换
对于一些模型,比如Jukebox,你不需要将每个模块都量化为8位,这实际上可能会导致不稳定性。对于Jukebox,有几个lm_head
模块应该使用BitsAndBytesConfig中的llm_int8_skip_modules
参数来跳过:
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
model_id = "bigscience/bloom-1b7"
quantization_config = BitsAndBytesConfig(
llm_int8_skip_modules=["lm_head"],
)
model_8bit = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype="auto",
device_map="auto",
quantization_config=quantization_config,
)
微调
使用 PEFT 库,你可以对像 flan-t5-large 和 facebook/opt-6.7b 这样的大型模型进行8位量化的微调。你不需要传递 device_map
参数进行训练,因为它会自动将你的模型加载到GPU上。然而,如果你愿意,你仍然可以使用 device_map
参数自定义设备映射(device_map="auto"
应该仅用于推理)。
4位 (QLoRA算法)
本节探讨了4位模型的一些特定功能,例如更改计算数据类型、使用Normal Float 4 (NF4)数据类型以及使用嵌套量化。
计算数据类型
为了加速计算,你可以使用BitsAndBytesConfig中的bnb_4bit_compute_dtype
参数将数据类型从float32(默认值)更改为bf16:
import torch
from transformers import BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype=torch.bfloat16)
普通浮点数 4 (NF4)
NF4 是来自 QLoRA 论文的一种 4 位数据类型,适用于从正态分布初始化的权重。你应该使用 NF4 来训练 4 位基础模型。这可以通过 BitsAndBytesConfig 中的 bnb_4bit_quant_type
参数进行配置:
from transformers import BitsAndBytesConfig
nf4_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
)
model_nf4 = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype="auto", quantization_config=nf4_config)
在推理过程中,bnb_4bit_quant_type
对性能的影响不大。然而,为了与模型权重保持一致,您应该使用 bnb_4bit_compute_dtype
和 torch_dtype
值。
嵌套量化
嵌套量化是一种可以在不增加性能成本的情况下节省额外内存的技术。此功能对已经量化的权重进行第二次量化,以节省额外的0.4位/参数。例如,使用嵌套量化,您可以在16GB NVIDIA T4 GPU上微调一个Llama-13b模型,序列长度为1024,批量大小为1,并启用4步梯度累积。
from transformers import BitsAndBytesConfig
double_quant_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
)
model_double_quant = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-13b", torch_dtype="auto", quantization_config=double_quant_config)
反量化 bitsandbytes 模型
一旦量化,您可以将模型反量化为原始精度,但这可能会导致模型质量的小幅下降。请确保您有足够的GPU内存来容纳反量化模型。
from transformers import AutoModelForCausalLM, BitsAndBytesConfig, AutoTokenizer
model_id = "facebook/opt-125m"
model = AutoModelForCausalLM.from_pretrained(model_id, BitsAndBytesConfig(load_in_4bit=True))
tokenizer = AutoTokenizer.from_pretrained(model_id)
model.dequantize()
text = tokenizer("Hello my name is", return_tensors="pt").to(0)
out = model.generate(**text)
print(tokenizer.decode(out[0]))