bitsandbytes 量化模型|8位与4位量化¶
bitsandbytes 是量化模型到 8 位和 4 位的最简单选择。8 位量化通过将 fp16 中的异常值与 int8 中的非异常值相乘,然后将非异常值转换回 fp16 并将其相加,从而返回 fp16 格式的权重。这减少了异常值对模型性能的负面影响。4 位量化进一步压缩了模型,通常与 QLoRA 一起用于微调量化的大规模语言模型(LLMs)。
要使用 bitsandbytes,请确保安装了以下库:
8-bit¶
pip install transformers accelerate bitsandbytes>0.37.0
4-bit¶
pip install bitsandbytes>=0.39.0
pip install --upgrade accelerate transformers
bitsandbytes 正在重构以支持多种后端,而不仅仅是 CUDA。目前,ROCm(AMD GPU)和 Intel CPU 的实现已经成熟,Intel XPU 的实现正在进行中,Apple Silicon 的支持预计在 Q4 或 Q1 完成。有关安装说明和最新的后端更新,请访问 这个链接。
我们欢迎您的反馈,以便在正式发布前发现并修复问题!详情请参阅 这些文档 并提供反馈链接。
现在,您可以通过向 from_pretrained() 方法传递一个 BitsAndBytesConfig 来量化模型。只要模型支持使用 Accelerate 加载,并且包含 torch.nn.Linear 层,这种方法适用于任何模态的任何模型。
8 位量化¶
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 参数:
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=torch.float32
)
# 检查最后一个层的权重数据类型
print(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")
4 位量化¶
将模型量化为4位可以将内存使用量减少4倍,对于大型模型,设置 device_map="auto" 可以高效地利用可用的GPU:
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
# 配置4位量化
quantization_config = BitsAndBytesConfig(load_in_4bit=True)
# 加载预训练模型并应用量化配置
model_4bit = AutoModelForCausalLM.from_pretrained(
"bigscience/bloom-1b7",
quantization_config=quantization_config
)
默认情况下,所有其他模块(如 torch.nn.LayerNorm)会被转换为 torch.float16。如果需要,你可以通过 torch_dtype 参数更改这些模块的数据类型:
import torch
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
# 配置4位量化
quantization_config = BitsAndBytesConfig(load_in_4bit=True)
# 加载预训练模型并应用量化配置,同时指定数据类型为 `torch.float32`
model_4bit = AutoModelForCausalLM.from_pretrained(
"facebook/opt-350m",
quantization_config=quantization_config,
torch_dtype=torch.float32
)
# 检查最后一层的权重数据类型
model_4bit.model.decoder.layers[-1].final_layer_norm.weight.dtype
如果你安装了 bitsandbytes>=0.41.3,你可以将4位模型序列化并推送到 Hugging Face Hub。只需在加载4位精度模型后调用 model.push_to_hub()。你也可以使用 model.save_pretrained() 命令将序列化的4位模型保存到本地。
使用 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)
设计一个自定义的设备映射,将大部分内容加载到 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",
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,
device_map=device_map,
quantization_config=quantization_config,
)
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,
device_map="auto",
quantization_config=quantization_config,
)
微调¶
使用 PEFT 库,您可以使用 8 位量化来微调大型模型,如 flan-t5-large 和 facebook/opt-6.7b。训练时不需要传递 device_map 参数,因为它会自动将模型加载到 GPU 上。但是,您仍然可以使用 device_map 参数来自定义设备映射(device_map="auto" 仅应用于推理)。
4 位量化 (QLoRA 算法)¶
尝试 4 位量化的方法,请参考这个 笔记本,并了解更多详细信息,可以阅读这篇 博客文章。
这一部分探讨了 4 位模型的一些特定功能,如更改计算数据类型、使用 Normal Float 4 (NF4) 数据类型和使用嵌套量化。
计算数据类型¶
为了加快计算速度,您可以将数据类型从 float32(默认值)更改为 bf16,具体配置如下:
import torch
from transformers import BitsAndBytesConfig
quantization_config = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype=torch.bfloat16)
Normal Float 4 (NF4)¶
NF4 是 QLoRA 论文中提出的一种 4 位数据类型,适用于从正态分布初始化的权重。训练 4 位基础模型时应使用 NF4。这可以通过 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, quantization_config=nf4_config)
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", 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]))