vLLM官方中文教程:用vllm实现所有的模型量化

量化以模型精度换取更小的内存占用,从而允许大型模型在更广泛的设备上运行。

在这里插入图片描述

支持的硬件

下表显示了 vLLM 中各种量化实现与不同硬件平台的兼容性:
在这里插入图片描述

  • Volta 指的是 SM 7.0,Turing 指的是 SM 7.5,Ampere 指的是 SM 8.0/8.6,Ada 指的是 SM 8.9,Hopper 指的是 SM 9.0。
  • ✅︎ 表示指定硬件支持量化方法。

随着 vLLM 不断发展和扩大对不同硬件平台和量化方法的支持,本兼容性图表可能会发生变化。
有关硬件支持和量化方法的最新信息,请参阅 vllm/model_executor/layers/quantization 或咨询 vLLM 开发团队。

在这里插入图片描述

AutoAWQ

要创建新的 4 位量化模型,可以利用 AutoAWQ。量化会将模型的精度从 FP16 降低到 INT4,从而有效地将文件大小减少约 70%。这样做的主要好处是降低了延迟和内存使用率。

一个FP16类型数据存储占用两个字节,INT4只需半个字节,差了四倍,大约减少了约70%。

您可以安装 AutoAWQ 或从 Huggingface 上的 400 多个模型中选择一个,对自己的模型进行量化。

pip install autoawq

安装 AutoAWQ 后,您就可以量化模型了。下面是一个如何量化 mistralai/Mistral-7B-Instruct-v0.2 的示例:

from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer

model_path = 'mistralai/Mistral-7B-Instruct-v0.2'
quant_path = 'mistral-instruct-v0.2-awq'
quant_config = { "zero_point": True, "q_group_size": 128, "w_bit": 4, "version": "GEMM" }

# Load model
model = AutoAWQForCausalLM.from_pretrained(
    model_path, **{"low_cpu_mem_usage": True, "use_cache": False}
)
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)

# Quantize
model.quantize(tokenizer, quant_config=quant_config)

# Save quantized model
model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)

print(f'Model is quantized and saved at "{quant_path}"')

要使用 vLLM 运行 AWQ 模型,可以使用 TheBloke/Llama-2-7b-Chat-AWQ 和以下命令:

python examples/offline_inference/llm_engine_example.py --model TheBloke/Llama-2-7b-Chat-AWQ --quantization awq

通过 LLM 类还可直接支持 AWQ 模型:

from vllm import LLM, SamplingParams

# Sample prompts.
prompts = [
    "Hello, my name is",
    "The president of the United States is",
    "The capital of France is",
    "The future of AI is",
]
# Create a sampling params object.
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)

# Create an LLM.
llm = LLM(model="TheBloke/Llama-2-7b-Chat-AWQ", quantization="AWQ")
# Generate texts from the prompts. The output is a list of RequestOutput objects
# that contain the prompt, generated text, and other information.
outputs = llm.generate(prompts, sampling_params)
# Print the outputs.
for output in outputs:
    prompt = output.prompt
    generated_text = output.outputs[0].text
    print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")

过程解释

FP16 和 INT4 的概念

  • FP16(Half Precision Floating Point):这是一种浮点数表示法,使用16位(2字节)来存储数值。它包括1位符号位、5位指数位和10位尾数位。这种格式允许表示范围广泛但精度相对较低的数值,非常适合于深度学习中的模型权重和激活值表示,在减少计算资源需求的同时保持较高的性能。

  • INT4(Integer 4-bit):这是一种整数表示法,仅使用4位(半字节)来存储数值。由于其极低的比特数,INT4只能表示非常有限范围内的整数值,并且没有小数部分。这使得它在数值表达上极为受限,但非常适合用于对精度要求不高、更注重效率的应用场景,比如某些特定类型的神经网络推理任务。

从 FP16 降低到 INT4”的含义

将数值精度从FP16降低到INT4意味着将原本以半精度浮点数形式存储的数据转换为更低精度的4位整数形式。

实例说明

假设我们有一个简单的神经网络层,其中包含一些权重值。这些权重最初是以FP16格式存储的。例如,考虑一个权重值 w = -0.375

  1. 原始FP16表示:首先,这个值 -0.375 被表示为FP16格式的一个数。虽然具体的二进制表示可能复杂,但从概念上讲,它是用16位来精确表示这个浮点数的。

  2. 量化至INT4:为了将其量化到INT4,我们需要定义一个缩放因子和零点(offset),以便将浮点数映射到整数范围内。假设我们的目标是使用8个不同的整数值(因为INT4可以表示16个不同的值,但我们可能只使用正负各一半)。如果选择缩放因子为 0.25,那么 -0.375 将被量化为最接近的可表示值,即 -1(因为 -0.375 / 0.25 ≈ -1.5,四舍五入后得到 -1)。这样,原本的FP16值就被近似为一个INT4值。

  3. 结果对比:在这个例子中,原来的 -0.375 变成了 -1,这意味着信息丢失了(特别是小数部分),但是存储空间和计算复杂度都显著降低了。

通过这样的过程,我们可以看到,尽管量化导致了一定程度的精度损失,但它极大地提高了计算效率和减少了存储需求,这对于部署在资源受限设备上的机器学习模型尤为重要。

BitsAndBytes

vLLM 现在支持 BitsAndBytes,可实现更高效的模型推理。BitsAndBytes 对模型进行量化,以减少内存使用量并提高性能,而不会明显牺牲准确性。与其他量化方法相比,BitsAndBytes 无需使用输入数据校准量化模型。

在这里插入图片描述

以下是使用 BitsAndBytes 和 vLLM 的步骤。

pip install bitsandbytes>=0.45.0

VLLM读取模型的配置文件,并支持机上in-flight量化和预量化pre-quantized检查点。

您可以在 https://huggingface.co/models?other=bitsandbytes 上找到 bitsandbytes 量化模型。通常,这些资源库都有一个 config.json 文件,其中包括一个 quantization_config 部分。

vllm读取4位量化模型

from vllm import LLM
import torch
# unsloth/tinyllama-bnb-4bit is a pre-quantized checkpoint.
model_id = "unsloth/tinyllama-bnb-4bit"
llm = LLM(model=model_id, dtype=torch.bfloat16, trust_remote_code=True, \
quantization="bitsandbytes", load_format="bitsandbytes")

in-flight量化:加载为 4 位量化模型

from vllm import LLM
import torch
model_id = "huggyllama/llama-7b"
llm = LLM(model=model_id, dtype=torch.bfloat16, trust_remote_code=True, \
quantization="bitsandbytes", load_format="bitsandbytes")

OpenAI兼容的服务加载量化模型

将以下内容添加到您的 4 位模型参数中:

--quantization bitsandbytes --load-format bitsandbytes

INT4 W4A16

vLLM 支持将权重量化为 INT4,以节省内存并加速推理。这种量化方法特别适用于减少模型大小,并在每秒查询次数(QPS)较少的工作负载中保持低延迟。

请访问随时可以与VLLM一起使用的主流LLM的量化INT4模型的HF集合

计算能力大于 8.0(Ampere、Ada Lovelace、Hopper、Blackwell)的英伟达™(NVIDIA®)图形处理器支持 INT4 计算。

安装依赖库

要在 vLLM 中使用 INT4 量化,需要安装 llm-compressor 库:

pip install llmcompressor

如何量化

量化过程包括四个主要步骤:

  • 加载模型
  • 准备校准数据
  • 执行量化
  • 评估 vLLM 的准确性
  1. 加载模型
    使用 transformersAutoModel 类加载模型和tokenizer
from transformers import AutoTokenizer, AutoModelForCausalLM

MODEL_ID = "meta-llama/Meta-Llama-3-8B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID, device_map="auto", torch_dtype="auto",
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
  1. 准备校准数据
    将权重量化到 INT4 时,需要样本数据来估计权重更新和校准刻度。最好使用与部署数据非常匹配的校准数据。对于通用的指令调整模型,可以使用 ultrachat 这样的数据集:
from datasets import load_dataset

NUM_CALIBRATION_SAMPLES = 512
MAX_SEQUENCE_LENGTH = 2048

# Load and preprocess the dataset
ds = load_dataset("HuggingFaceH4/ultrachat_200k", split="train_sft")
ds = ds.shuffle(seed=42).select(range(NUM_CALIBRATION_SAMPLES))

def preprocess(example):
    return {"text": tokenizer.apply_chat_template(example["messages"], tokenize=False)}
ds = ds.map(preprocess)

def tokenize(sample):
    return tokenizer(sample["text"], padding=False, max_length=MAX_SEQUENCE_LENGTH, truncation=True, add_special_tokens=False)
ds = ds.map(tokenize, remove_columns=ds.column_names)
  1. 执行量化

现在,应用量化算法:

from llmcompressor.transformers import oneshot
from llmcompressor.modifiers.quantization import GPTQModifier
from llmcompressor.modifiers.smoothquant import SmoothQuantModifier

# Configure the quantization algorithms
recipe = GPTQModifier(targets="Linear", scheme="W4A16", ignore=["lm_head"])

# Apply quantization
oneshot(
    model=model,
    dataset=ds,
    recipe=recipe,
    max_seq_length=MAX_SEQUENCE_LENGTH,
    num_calibration_samples=NUM_CALIBRATION_SAMPLES,
)

# Save the compressed model
SAVE_DIR = MODEL_ID.split("/")[1] + "-W4A16-G128"
model.save_pretrained(SAVE_DIR, save_compressed=True)
tokenizer.save_pretrained(SAVE_DIR)

这个过程创建了一个 W4A16 模型,权重量化为 4 位整数。

  1. 评估准确性

量化后,您可以在 vLLM 中加载并运行模型:

from vllm import LLM
model = LLM("./Meta-Llama-3-8B-Instruct-W4A16-G128")

要评估准确性,可以使用 lm_eval

lm_eval --model vllm \
  --model_args pretrained="./Meta-Llama-3-8B-Instruct-W4A16-G128",add_bos_token=true \
  --tasks gsm8k \
  --num_fewshot 5 \
  --limit 250 \
  --batch_size 'auto'

注意:量化模型可能对 bos 标记的存在很敏感。请确保在运行评估时加入 add_bos_token=True 参数。

最佳实践

  • 校准数据从 512 个样本开始,如果精度下降,则增加样本数量
  • 确保校准数据包含多种样本,以防止对特定用例的过度拟合
  • 将序列长度设置为 2048 作为起点
  • 使用模型训练时使用的聊天模板或指令模板
  • 如果您已经对模型进行了微调,请考虑使用训练数据的样本进行校准
  • 调整量化算法的关键超参数:
    • dampening_frac 设置 GPTQ 算法的影响程度。较低的值可以提高准确度,但会导致数值不稳定,从而导致算法失败。
    • actorder 设置激活顺序。压缩层权重时,通道量化的顺序很重要。设置 actorder=“weight” 可以在不增加延迟的情况下提高准确度。

下面是一个扩展量化配方的示例,你可以根据自己的使用情况进行调整:

from compressed_tensors.quantization import (
    QuantizationArgs,
    QuantizationScheme,
    QuantizationStrategy,
    QuantizationType,
) 
recipe = GPTQModifier(
    targets="Linear",
    config_groups={
        "config_group": QuantizationScheme(
            targets=["Linear"],
            weights=QuantizationArgs(
                num_bits=4,
                type=QuantizationType.INT,
                strategy=QuantizationStrategy.GROUP,
                group_size=128,
                symmetric=True,
                dynamic=False,
                actorder="weight",
            ),
        ),
    },
    ignore=["lm_head"],
    update_size=NUM_CALIBRATION_SAMPLES,
    dampening_frac=0.01
)

常见问题与支持

如果您遇到任何问题或有任何功能请求,请在 vllm-project/llm-compressor GitHub 代码库中提交问题。llm-compressor 中完整的 INT4 量化示例可在此处获取

在这里插入图片描述

INT8量化 W8A8

vLLM 支持将权重和激活量量化为 INT8,以节省内存和加速推理。这种量化方法尤其适用于在保持良好性能的同时缩小模型大小。

请访问可与 vLLM 配合使用的常用 LLM 的量化 INT8 模型的常见集合

注意:计算能力大于 7.5(图灵、安培、Ada Lovelace、Hopper、Blackwell)的英伟达™(NVIDIA®)图形处理器支持 INT8 计算。

使用vLLM如何实现INT8量化,请参考教程

FP8量化 W8A8

vLLM 使用 Nvidia H100 和 AMD MI300x 等 GPU 上的硬件加速支持 FP8(8 位浮点)权重和激活量化。目前,只有 Hopper 和 Ada Lovelace GPU 正式支持 W8A8。

Ampere GPU 支持使用 Marlin 内核的 W8A16(仅重量 FP8)。利用 FP8 对模型进行量化,可将模型内存要求降低 2 倍,吞吐量最多可提高 1.6 倍,而对精度的影响却很小。

请访问可与 vLLM 配合使用的常用 LLM 的量化 FP8 量化模型的常见集合

硬件通常支持的 FP8 类型有两种不同的表示方法,每种方法在不同情况下都有用:

  • E4M3: 由 1 个符号位、4 个指数位和 3 个尾数位组成。它可以存储高达 +/-448nan的数值。
  • E5M2: 由 1 个符号位、5 个指数位和 2 个尾数位组成。它可以存储高达 +/-57344+/- infnan 的数值。动态范围增加的代价是存储值的精度降低。

注意:计算能力大于 8.9(Ada Lovelace,Hopper)的英伟达™(NVIDIA®)图形处理器支持 FP8 计算。FP8 模型将在计算能力大于 8.0(安培)的 W8A16 上运行,使用 FP8 Marlin。

在线动态量化快速入门

vLLM 可以将原始精度 BF16/FP16 模型动态量化为 FP8,而无需任何校准数据。您可以通过在命令行中指定–quantization=“fp8 ”或在 LLM 构造函数中设置 quantization=“fp8” 来启用该功能。

在这种模式下,所有线性模块(除了最后的 lm_head)的权重都量化为 FP8_E4M3 精度,并按张量标度。在每次前向传递过程中,都会计算激活的最小值和最大值,以提供高精度的动态单位张量标度。因此,在这种模式下,延迟的改善是有限的。

from vllm import LLM
model = LLM("facebook/opt-125m", quantization="fp8")
# INFO 06-10 17:55:42 model_runner.py:157] Loading model weights took 0.1550 GB
result = model.generate("Hello, my name is")

警告:目前,我们以原始精度加载模型,然后再将其量化为 8 位,因此需要足够的内存来加载整个模型。

利用静态激活缩放因子进行离线量化

通过启用 activation_scheme=“static”(静态)参数,可以使用 AutoFP8 和校准数据为权重和激活生成每个张量的静态刻度。

from datasets import load_dataset
from transformers import AutoTokenizer
from auto_fp8 import AutoFP8ForCausalLM, BaseQuantizeConfig

pretrained_model_dir = "meta-llama/Meta-Llama-3-8B-Instruct"
quantized_model_dir = "Meta-Llama-3-8B-Instruct-FP8"

tokenizer = AutoTokenizer.from_pretrained(pretrained_model_dir, use_fast=True)
tokenizer.pad_token = tokenizer.eos_token

# Load and tokenize 512 dataset samples for calibration of activation scales
ds = load_dataset("mgoin/ultrachat_2k", split="train_sft").select(range(512))
examples = [tokenizer.apply_chat_template(batch["messages"], tokenize=False) for batch in ds]
examples = tokenizer(examples, padding=True, truncation=True, return_tensors="pt").to("cuda")

# Define quantization config with static activation scales
quantize_config = BaseQuantizeConfig(quant_method="fp8", activation_scheme="static")

# Load the model, quantize, and save checkpoint
model = AutoFP8ForCausalLM.from_pretrained(pretrained_model_dir, quantize_config)
model.quantize(examples)
model.save_quantized(quantized_model_dir)

最后,您可以直接在 vLLM 中加载量化后的模型检查点。

from vllm import LLM
model = LLM(model="Meta-Llama-3-8B-Instruct-FP8/")
# INFO 06-10 21:15:41 model_runner.py:159] Loading model weights took 8.4596 GB
result = model.generate("Hello, my name is")

使用vLLM如何实现INT8量化,请参考教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值