Skip to content

Latest commit

 

History

History
207 lines (167 loc) · 12.8 KB

advanced_quantization.md

File metadata and controls

207 lines (167 loc) · 12.8 KB

量化策略详细教程

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

LLM量化效果Benchmark

  1. LLama 13b 在不同量化策略下精度对比
量化策略 比特数 ACC1/% ACC2/%
Baseline FP16 78.32 32.98
RTN W8A8 34.05 24.96
SmoothQuant W8A8 42.36 27.78
ShiftQuant W8A8 33.35 25.48
Shift-SmoothQuant W8A8 45.26 24.66
自适应Shift-SmoothQuant W8A8 78.07 32.69
RTN W4A16 75.48 29.27
GPTQ W4A16 77.55 32.32
  1. Bloom 7.1b 在不同量化策略下精度对比
量化策略 比特数 ACC1/% ACC2/%
Baseline FP16 77.18 41.08
RTN W8A8 76.48 38.26
SmoothQuant W8A8 76.57 38.70
ShiftQuant W8A8 68.99 39.97
Shift-SmoothQuant W8A8 76.77 37.22
自适应Shift-SmoothQuant W8A8 77.04 40.63
RTN W4A16 75.18 38.03
GPTQ W4A16 76.82 39.37
  1. ChatGLM2 6b 在不同量化策略下精度对比
量化策略 比特数 ACC1/% ACC2/%
Baseline FP16 76.46 31.57
PTQ-INT8 W8A8 64.08 29.94
SmoothQuant W8A8 64.29 29.86
ShiftQuant W8A8 57.83 27.26
Shift-SmoothQuant W8A8 58.89 24.29
自适应Shift-SmoothQuant W8A8 76.89 34.55
RTN W4A16 74.20 26.30
GPTQ W4A16 75.66 31.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+.

参数名 参数类型 参数释义
model paddle.nn.Layer 必须传入的动态图模型
model_config dict 必须传入的模型结构的配置
shift_all_linears bool 可选参数,默认为False,若为True,则shift模型中全部Linear;若为False,则只shift模型中Layer Norm之后的Linear
sample_function function 可选参数,默认为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功能还提供搜索功能,搜索功能文档见下文。

参数名 参数类型 参数释义
model paddle.nn.Layer 必须传入的动态图模型
model_config dict 必须传入的模型结构的配置
alpha float 可选参数,默认为0.5
smooth_all_linears bool 可选参数,默认为False,若为True,则shift模型中全部Linear;若为False,则只shift模型中Layer Norm之后的Linear
sample_function function 可选参数,默认为None,若为None,采样方式为单batch数据,现可选的sample_function有MultiStepSampler和EMASampler,Smooth时推荐使用MultiStepSampler
search_function function 可选参数,默认为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_piece int 可选参数,分段数量,默认为1,1代表不分段
bits_length int 可选参数,量化比特数,默认为8
search_piece bool 可选参数,是否搜索分段数k,默认为False,若为True,将会从1到k搜索合适的k
search_alpha_min float 可选参数,搜索alpha最小值,默认为0.2
search_alpha_max float 可选参数,搜索alpha最大值,默认为0.8
search_scale_min float 可选参数,搜索scale最小值,默认为1.
search_scale_max float 可选参数,搜索scale最大值,默认为1.
weight_quant_method str 可选参数,权重量化方法,可选abs_maxabs_max_channel_wiseavg,默认为abs_max_channel_wise
act_quant_method str 可选参数,激活量化方法,可选abs_maxavg,默认为abs_max
loss_function function 可选参数,搜索时使用的误差函数,默认为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即可。

参数名 参数类型 参数释义
layer paddle.nn.Layer 必须入的需要量化的层,现仅支持nn.Linear,ColumnParallelLinear和RowParallelLinear类型
model_config dict 必须传入的模型结构的配置
quant_bits int 可选参数,量化比特数,默认为4
weight_quant_method str 可选参数,权重量化方法,可选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是按层级别分析量化损失的方法,对于模型中每一层,量化后,计算当前层量化输出和原始模型输出的误差。

参数名 参数类型 参数释义
layer paddle.nn.Layer 必须入的需要量化的层,现仅支持nn.Linear,ColumnParallelLinear和RowParallelLinear类型
weight_bits int 可选参数,权重量化比特数,默认为8
act_bits int 可选参数,激活量化比特数,默认为8
weight_quant_method str 可选参数,权重量化方法,可选abs_maxabs_max_channel_wiseavg,默认为abs_max_channel_wise
act_quant_method str 可选参数,激活量化方法,可选abs_maxavg
loss_function function 可选参数,使用的误差函数,默认为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())