2、模型量化入门:FP16/INT8/INT4量化原理、量化对显存的影响、量化工具(bitsandbytes、GPTQ)

2.1 量化到底在做什么?

很多人一听到「量化」就觉得高深莫测。其实说白了,就是给模型权重「瘦身」。

我打个比方。你有一张高清照片,原始文件几十MB。但发朋友圈时,系统会自动压缩成几百KB。你看上去差别不大,对吧?量化干的也是类似的事。

模型训练时,参数通常用FP32(32位浮点数)存储。每个数占4字节。推理时,我们其实不需要这么高的精度。换成FP16(2字节),甚至INT8(1字节)、INT4(0.5字节),模型照样能工作。

为什么会这样?因为神经网络对噪声有天然的鲁棒性。权重稍微变一点,输出结果不会崩。我在项目中遇到过,一个70B的模型用INT4量化后,回答质量几乎没下降,但显存占用直接砍到原来的四分之一。

核心结论:量化就是用更少的比特数表示模型参数,以精度换显存和速度。

2.2 FP16/INT8/INT4 量化原理

2.2.1 FP16 量化

FP16是最温和的量化方式。它把32位浮点数砍成16位。指数位从8位减到5位,尾数位从23位减到10位。

嗯,这里要注意。FP16的数值范围比FP32小很多。FP32能表示约3.4e38,FP16只能到6.5e4。如果模型权重分布太散,直接转FP16可能会溢出。

我个人习惯的做法是:先检查权重的最大值和最小值。如果最大值超过6.5e4,或者最小值小于-6.5e4,那就得做一下缩放。不过大多数预训练模型都没这个问题,放心转就行。

2.2.2 INT8 量化

INT8就更有意思了。它只有256个离散值(-128到127)。要把连续的浮点数映射到这256个格子里,需要做两件事:

  1. 确定缩放因子:找到权重的最小值和最大值,算出缩放比例
  2. 四舍五入:把浮点数映射到最近的整数格点上

举个例子。假设权重范围是[-1.0, 1.0],要映射到[-128, 127]。缩放因子就是127 / 1.0 = 127。权重0.5就变成0.5 * 127 ≈ 64。

你想想看,这个过程肯定有精度损失。但好消息是,模型通常能扛得住。我在项目中试过,INT8量化后的模型,准确率下降通常不超过1%。

我的经验:INT8量化时,最好用「逐通道量化」而不是「逐张量量化」。每个通道单独算缩放因子,精度更高。虽然计算量大了点,但效果值得。

2.2.3 INT4 量化

INT4是最激进的。它只有16个值(-8到7)。说实话,我第一次用INT4时心里也没底。但试过之后发现,大模型(尤其是70B以上的)对INT4的容忍度出奇地高。

INT4量化的难点在于:4比特没法直接表示浮点数。所以通常的做法是分组量化。比如每32个权重为一组,共享一个缩放因子。这样每个权重只用4比特,但组内精度还能保持。

我曾经踩过一个坑:用INT4量化小模型(比如1.5B的)。结果模型直接「失智」了,输出全是乱码。后来才明白,小模型参数少,冗余度低,扛不住这么大的精度损失。大模型反而更适合INT4。

避坑指南:我曾经在7B模型上直接套INT4量化,结果推理时频繁出现NaN。排查了半天,发现是某些层的激活值范围太大,超出了INT4的表示范围。解决方案是加一个「动态裁剪」步骤,把异常值先截断再量化。

2.3 量化对显存的影响

量化最直接的好处就是省显存。咱们算笔账:

精度 每参数比特数 70B模型显存占用 相对FP32节省
FP32 32 ~280GB 0%
FP16 16 ~140GB 50%
INT8 8 ~70GB 75%
INT4 4 ~35GB 87.5%

看到没?INT4量化后,70B模型只要35GB显存。一张A100(80GB)就能跑起来。这在以前想都不敢想。

但要注意,显存节省不是免费的。量化后的模型,推理速度通常会变快(因为数据量小了,内存带宽压力小),但精度会下降。我个人的经验是:

  • FP16:几乎无损,推荐所有场景使用
  • INT8:轻微损失,适合大多数生产环境
  • INT4:有明显损失,但大模型可用,适合显存紧张的场景

2.4 量化工具:bitsandbytes

bitsandbytes是我用得最多的量化库。它由Tim Dettmers开发,专门为PyTorch设计。安装很简单:

pip install bitsandbytes

用法也直接。加载模型时指定量化类型就行:

import torch
from transformers import AutoModelForCausalLM, BitsAndBytesConfig

# 配置INT4量化
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",  # 使用NF4量化类型
    bnb_4bit_compute_dtype=torch.float16,
    bnb_4bit_use_double_quant=True  # 双重量化,进一步省显存
)

model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-2-70b-hf",
    quantization_config=bnb_config,
    device_map="auto"
)

这里有个细节。bnb_4bit_quant_type有两个选项:fp4和nf4。nf4是bitsandbytes自创的量化格式,专门针对正态分布的权重做了优化。我实测下来,nf4比fp4精度高0.5-1%。

小技巧:开启bnb_4bit_use_double_quant=True,可以再省约0.4比特/参数。虽然不多,但积少成多。70B模型能省下约3.5GB显存。

2.5 量化工具:GPTQ

GPTQ是另一种主流量化方法。它和bitsandbytes的思路不太一样。

bitsandbytes是「训练后量化」(PTQ),直接对权重做映射。GPTQ也是PTQ,但它多了一步:用一小批校准数据来优化量化过程。

说白了,GPTQ会观察模型在真实数据上的表现,然后调整量化策略,让精度损失最小化。我试过,GPTQ量化的模型,在相同比特数下,精度通常比bitsandbytes高0.3-0.5%。

使用GPTQ需要安装auto-gptq库:

pip install auto-gptq

用法示例:

from auto_gptq import AutoGPTQForCausalLM
from transformers import AutoTokenizer

model = AutoGPTQForCausalLM.from_quantized(
    "TheBloke/Llama-2-70B-GPTQ",
    use_safetensors=True,
    device="cuda:0",
    use_triton=False
)

tokenizer = AutoTokenizer.from_pretrained("TheBloke/Llama-2-70B-GPTQ")

嗯,这里要注意。GPTQ量化后的模型,推理速度比bitsandbytes稍慢一点。因为GPTQ的量化格式更复杂,反量化计算量更大。但精度确实更好。

我个人习惯是:

  • 追求速度:用bitsandbytes
  • 追求精度:用GPTQ
  • 显存极度紧张:bitsandbytes + INT4 + 双重量化

2.6 本章知识体系

下面这张图总结了量化的核心逻辑:

模型量化知识体系 模型量化 量化原理 FP16:16位浮点,几乎无损 INT8:256个离散值,轻微损失 INT4:16个离散值,大模型可用 显存影响 FP32 → FP16:节省50% FP32 → INT8:节省75% FP32 → INT4:节省87.5% 量化工具 bitsandbytes:速度快 GPTQ:精度高 NF4 vs FP4:nf4更优 核心思路 用更少的比特数表示参数 → 降低显存占用 → 突破推理瓶颈

量化不是银弹。它是在显存、速度、精度三者之间找平衡。我建议你从FP16开始,如果显存不够再降INT8,最后才考虑INT4。每一步都要实测,别光看理论。

好了,这一章就到这里。量化入门其实不难,关键是动手试。拿个7B模型跑一遍,你就能感受到量化的威力了。

一句话总结:量化是突破显存瓶颈最直接的手段。FP16无脑用,INT8放心用,INT4谨慎用。

想要更多资料,联系可可