量化策略详细教程

August 18, 2023 · View on GitHub

近年来,Transformer模型已在各个领域得到广泛采用,尤其是生成式语言大模型极大地推动了人工智能领域的发展。这些模型已经从数亿个参数发展到数千亿个参数,在有限的数据和 GPU 资源下运行,对于这些模型来说变得越来越具有挑战性。此时压缩技术变得格外重要,其中量化已成为减少内存占用和计算开销的通用和主要范例。然而,许多研究表明,Transformer模型往往会存在强烈的异常激活值,这使得它们难以量化。为了保持可接受的性能,这些异常值的存在要求激活具有更高的位宽或使用不同的数字格式、额外的微调或其他解决方法。本文档会介绍前沿的优化量化效果的几种策略,其中包括一些开源工作,也包括PaddleSlim自研的方法。

LLM量化效果Benchmark

  1. LLama 13b 在不同量化策略下精度对比
量化策略比特数ACC1/%ACC2/%
BaselineFP1678.3232.98
RTNW8A834.0524.96
SmoothQuantW8A842.3627.78
ShiftQuantW8A833.3525.48
Shift-SmoothQuantW8A845.2624.66
自适应Shift-SmoothQuantW8A878.0732.69
RTNW4A1675.4829.27
GPTQW4A1677.5532.32
  1. Bloom 7.1b 在不同量化策略下精度对比
量化策略比特数ACC1/%ACC2/%
BaselineFP1677.1841.08
RTNW8A876.4838.26
SmoothQuantW8A876.5738.70
ShiftQuantW8A868.9939.97
Shift-SmoothQuantW8A876.7737.22
自适应Shift-SmoothQuantW8A877.0440.63
RTNW4A1675.1838.03
GPTQW4A1676.8239.37
  1. ChatGLM2 6b 在不同量化策略下精度对比
量化策略比特数ACC1/%ACC2/%
BaselineFP1676.4631.57
PTQ-INT8W8A864.0829.94
SmoothQuantW8A864.2929.86
ShiftQuantW8A857.8327.26
Shift-SmoothQuantW8A858.8924.29
自适应Shift-SmoothQuantW8A876.8934.55
RTNW4A1674.2026.30
GPTQW4A1675.6631.50
  • ACC1:Finetuned下游任务下,使用数据集nl2sql的指标
  • ACC2:Pretrained开源任务下,使用数据集C-eval的指标

以下方法暂时仅支持Transformer模型,具体示例使用方法可参考PaddleNLP LLM示例,以下教程仅详细介绍API接口。

1. Shift功能

Shift算法来源于Outlier Suppression+。通过Layer Norm和Linear的bias进行数学等价的异常值缩放操作,有效将激活值的分布调整对称,有助于离线量化的精度提升。在PaddleSlim的实现中,对于前面有Layer Norm的Linear将使用数学等价的方式改变bias从而进行缩放操作,对于前面没有Layer Norm的Linear可以采用插入节点的方式实现,可通过参数shift_all_linears来控制是否需要shift前面没有Layer Norm的Linear。此外,PaddleSlim版本的Shift功能提供传入sample_function,如设置sample_function为None,Shift算法将完全对齐论文Outlier Suppression+.

参数名参数类型参数释义
modelpaddle.nn.Layer必须传入的动态图模型
model_configdict必须传入的模型结构的配置
shift_all_linearsbool可选参数,默认为False,若为True,则shift模型中全部Linear;若为False,则只shift模型中Layer Norm之后的Linear
sample_functionfunction可选参数,默认为None,若为None,采样方式为论文中相同方法,现可选的sample_function有MultiStepSampler和EMASampler,Shift时推荐使用EMASampler

以下为简单的使用示例:

from paddleslim.quant.advanced import Shift, EMASampler

model = LLM()
model_config = {}
shift = Shift(model, model_config, sample_function=EMASampler())
for data in dataloader():
    model(data)
    shift.step += 1
shift.update_weight()

2. Smooth功能

Smooth算法来源于SmoothQuant。通过Layer Norm和Linear的weight和bias进行数学等价的异常值缩放操作,有效减少激活值中的异常值,有助于离线量化的精度提升。在PaddleSlim的实现中,与shift相同,对于前面有Layer Norm的Linear将使用数学等价的方式改变weight和bias从而进行缩放操作,对于前面没有Layer Norm的Linear可以采用插入节点的方式实现,可通过参数smooth_all_linears来控制是否需要smooth前面没有Layer Norm的Linear。此外,PaddleSlim版本的Smooth功能还提供搜索功能,搜索功能文档见下文。

参数名参数类型参数释义
modelpaddle.nn.Layer必须传入的动态图模型
model_configdict必须传入的模型结构的配置
alphafloat可选参数,默认为0.5
smooth_all_linearsbool可选参数,默认为False,若为True,则shift模型中全部Linear;若为False,则只shift模型中Layer Norm之后的Linear
sample_functionfunction可选参数,默认为None,若为None,采样方式为单batch数据,现可选的sample_function有MultiStepSampler和EMASampler,Smooth时推荐使用MultiStepSampler
search_functionfunction可选参数,默认为None,若为None,则不进行搜索,smooth方法与原论文保持一致

以下为简单的使用示例:

from paddleslim.quant.advanced import Smooth,MultiStepSampler

model = LLM()
model_config = {}
smooth = Smooth(model, model_config, sample_function=MultiStepSampler())
for data in dataloader():
    model(data)
    smooth.step += 1
smooth.update_weight()

注意:模型shift和smooth前后从理论数学角度应该是等价的,但从工程角度,输出具体数值可能会有稍微不同,所以模型shift/smooth前后的精度是大约一致的。如果精度出现很大差距,说明模型未解析成功。参数中model_config请按照模型的实际情况填写,此输入会影响模型结构解析,若结构解析出错,会导致模型精度不对,其中包含字段:

  • fused_qkv:该模型是否融合了QKV,默认为True
  • linear_flag:该模型判断Linear的名字字段,默认为linear
  • norm_flag:该模型判断Layer Norm的名字字段,默认为norm
  • parallel_ffn:该模型是否含有并行的FFN,默认为False
  • skip_norm_list:该模型中需要被忽略的Layer Norm的名字字段,默认为空list

若模型中含有PostLayerNorm Shurtcut结构,则不支持对该模型进行smooth和shift。比如PaddleNLP中ChatGLM结构存在PostLayerNorm Shurtcut结构,所以不支持对该模型进行shift/smooth。

3. PieceWiseSearch功能

根据SmoothQuant算法,的确能够有效减少异常值,但我们在大量的实验中发现,在某些情况下,比如权重值较大,尤其是权重和激活的异常值在同一通道时,直接根据SmoothQuant计算smooth scale的公式会导致权重值难量化的情况。并且,对于一个激活值,当异常值较多、数值范围较大,使用同一个alpha去smooth整个激活张量也并不合理。因此,PaddleSlim提出分段搜索功能,根据数值大小将激活分成K段,对于每一段进行alhpa和scale的搜索。

参数名参数类型参数释义
k_pieceint可选参数,分段数量,默认为1,1代表不分段
bits_lengthint可选参数,量化比特数,默认为8
search_piecebool可选参数,是否搜索分段数k,默认为False,若为True,将会从1到k搜索合适的k
search_alpha_minfloat可选参数,搜索alpha最小值,默认为0.2
search_alpha_maxfloat可选参数,搜索alpha最大值,默认为0.8
search_scale_minfloat可选参数,搜索scale最小值,默认为1.
search_scale_maxfloat可选参数,搜索scale最大值,默认为1.
weight_quant_methodstr可选参数,权重量化方法,可选abs_maxabs_max_channel_wiseavg,默认为abs_max_channel_wise
act_quant_methodstr可选参数,激活量化方法,可选abs_maxavg,默认为abs_max
loss_functionfunction可选参数,搜索时使用的误差函数,默认为mse_loss
from paddleslim.quant.advanced import Smooth, MultiStepSampler, PieceWiseSearch, mse_loss

search_func =PieceWiseSearch(
                k_piece=3,
                bits_length=8,
                search_piece=False,
                search_alpha_min=0.2,
                search_alpha_max=0.8,
                search_scale_min=1.,
                search_scale_max=5.,
                weight_quant_method='abs_max_channel_wise',
                act_quant_method='abs_max',
                loss_function=mse_loss
            )
model = LLM()
model_config = {}
smooth = Smooth(model, model_config, sample_function=MultiStepSampler(), search_function=search_func)
for data in dataloader():
    model(data)
    smooth.step += 1
smooth.update_weight()

4. GPTQ

GPTQ算法来自GPTQ,该算法逐步按照行量化权重,利用海森矩阵来不断更新未量化的权重,在低比特Weight Only Int4量化表现良好。GPTQ默认使用搭配使用RPTQ,若不想搭配RPTQ,调用fasterquant时设置act_order=False即可。

参数名参数类型参数释义
layerpaddle.nn.Layer必须入的需要量化的层,现仅支持nn.Linear,ColumnParallelLinear和RowParallelLinear类型
model_configdict必须传入的模型结构的配置
quant_bitsint可选参数,量化比特数,默认为4
weight_quant_methodstr可选参数,权重量化方法,可选abs_maxabs_max_channel_wiseavg,默认为abs_max_channel_wise
from paddleslim.quant.advanced import GPTQ

model = LLM()
for cur_name, cur_layer in model.named_sublayers():
    if type(cur_layer) == paddle.nn.Linear:
        gptq_layer = GPTQ(cur_layer)
        # sample data
        for data in dataloader():
            model(data)
        # quant weight
        gptq_layer.fasterquant(act_order=True)

5. LayerWiseQuantError

LayerWiseQuantError是按层级别分析量化损失的方法,对于模型中每一层,量化后,计算当前层量化输出和原始模型输出的误差。

参数名参数类型参数释义
layerpaddle.nn.Layer必须入的需要量化的层,现仅支持nn.Linear,ColumnParallelLinear和RowParallelLinear类型
weight_bitsint可选参数,权重量化比特数,默认为8
act_bitsint可选参数,激活量化比特数,默认为8
weight_quant_methodstr可选参数,权重量化方法,可选abs_maxabs_max_channel_wiseavg,默认为abs_max_channel_wise
act_quant_methodstr可选参数,激活量化方法,可选abs_maxavg
loss_functionfunction可选参数,使用的误差函数,默认为mse_loss
from paddleslim.quant.advanced import LayerWiseQuantError

model = LLM()
for cur_name, cur_layer in model.named_sublayers():
    if type(cur_layer) == paddle.nn.Linear:
        gptq_layer = LayerWiseQuantError(cur_layer)

for data in dataloader():
    model(data)

for cur_name, cur_layer in model.named_sublayers():
    if type(cur_layer) == LayerWiseQuantError:
        print(cur_name, cur_layer.losses.mean())