PPQ Optim Pass(PPQ优化过程)

量化任务总是复杂而多变的,为此将量化逻辑抽象成一组彼此独立的优化过程,优化过程将在运行时修改模型的量化信息、模型结构与权重,是模型量化任务的实际承担者,如RuntimeCalibrationPass负责对模型进行Calibration从而得到Scale与Offset信息,QuantFusionPass负责修改量化信息以模拟目标平台的图融合策略等等。优化过程是一个可扩展的类,在PPQ中可以使用系统预定义的数十种优化过程完成模型量化任务,也可以自定义新的优化过程以解决特定的问题。

PPQ提供两种不同的优化过程调用接口:

  1. PPQ中的api函数quantize_onnx_model,quantize_caffe_model,quantize_torch_model等,将根据输入的量化配置(QuantizationSetting)决定启动那些优化过程,同时传递相应的参数,可以检阅ppq\api\setting.py文件获取更多的信息。当使用API函数进行量化时,无法调用自定义的优化过程,只能使用系统预制的量化优化过程实现功能,当然它更加稳定。
  2. (推荐)可以自行组织量化管线,自行选取需要使用的量化过程并在初始化时传入相应的参数,可以参考ProgramEntrace_2.py样例,手动创建符合自己需要的量化管线。需要注意:量化过程是彼此独立的,但调用时应当遵循固定的先后次序。如QauntFusionPass应当在RuntimeCalibrationPass之前完成调用。

PPQ优化过程由两个基类定义:QuantizationOptimizationPass与QuantizationOptimizationPipeline。

1. QuantizationOptimizationPass(优化过程)

该类型定义于ppq\quantization\optim\base.py

该类型是抽象基类,描述了一个施加在计算图上的具体优化行为。事实上PPQ的大部分量化功能均是由一个一个的优化过程所承担的,如RuntimeCalibrationPass负责对模型进行Calibration从而得到Scale与Offset信息,QuantFusionPass负责修改量化信息以模拟目标平台的图融合策略等等。可以继承该类并实现自己的优化过程逻辑。

成员函数:

  • optimize(self,graph:BaseGraph,**kwargs) -> None:optimize函数是一个抽象函数,任何继承于QuantizationOptimizationPass的子类均需要实现该函数从而完成对计算图的修改,PPQ将在量化过程中依次调用优化过程的optimize函数完成模型的量化功能。该函数是不定参数函数,用户可以扩展定义该函数的入参,但入参中必须包含graph对象。使用PPQ系统api完成量化过程调用时,将为优化过程传入graph,dataloader,executor三个参数。用户可以通过手动创建优化管线的方式传入更多参数。

2. QuantizationOptimizationPipeline(优化过程管线):

该类型定义于ppq\quantization\optim\base.py

该类型是QuantizationOptimizationPass的容器类,即一个包含若干QuantizationOptimizationPass的列表,它的行为与普通的list无异。

成员函数:

  • init(self,passes:List[QuantizationOptimizationPass]):创建一个量化管线,需要用户输入一个仅包含QuantizationOptimizationPass的列表,请注意该列表是有序的,其中的优化过程将按顺序依次完成调用。
  • optimize(self,graph:BaseGraph,**kwargs) -> None:将该量化管线中的过程依次施加于计算图之上,该函数为不定参函数,用户可以穿入任意多的参数。所有传入的参数将原封不动地传递给所包含的量化优化过程。
  • append_optimization_to_pipeline(self,optim_pass:QuantizationOptimizationPass,at_front:bool=False):为当前管线添加一个新的优化过程。

用户应当使用语句from ppq.quantization.optim import xxx来引入PPQ提供的量化过程。

3.定义新的优化过程 

用户可以声明子类,继承与ppq.QuantizationOptimizationPass基类,并实现相关接口函数,参考代码如下:

# this file shows how to create new optim pass
from ppq import BaseGraph
from ppq import QuantizationOptimizationPass # base class
class MyOptimPass(QuantizationOptimizationPass): # inherit this
    # 1. realize _init_ function,name your optim class
    def _init_(self) -> None:
        super()._init_('My Optim Pass')
    # 2. realize optimize function,do your work
    def optimize(self,graph:BaseGraph,**kwargs) -> None:
        for op in graph.operations.values():
            print(f'Hi,nice to meet you {op.name}')

用户需要使用自定义的optimize函数对图中的信息进行修改,量化过程可以修改图中的任何信息,但当用户试图在量化过程中修改图的结构时,请注意图上Variable的shape,dtype属性可能需要更新。

4.优化过程参数的传递

 用户可以使用两种方式向优化过程传递参数:

  1. 在init函数中定义参数,并在创建优化过程时传参
  2. 在optimize函数中定义参数,并在调用优化过程时传参

当用户使用quantize_onnx_model,quantize_caffe_model,quantize_torch_model函数对模型进行量化时,PPQ会在ppq\quantization\quantizer\base.py文件中完成所有量化优化过程的创建,并赋予它们初始化的参数。而后Quantizer将创建量化管线并完成优化过程的调用,此时传入的参数如下:

# Quantizer创建量化管线
quant_pipeline = self.build_quant_pipeline(setting)
# Quantizer调用量化管线,传入参数
quant_pipeline.optimize(
    graph=self._graph,
    dataloader=calib_dataloader,
    executor=executor,
    verbose=self._verbose,
    **kwargs)

 上述属性:graph,dataloader,executor均可被优化过程的optimize函数访问,用户可以使用传入的数据开展后续的工作。定义实例如下:

from typing import Iterable
from ppq import BaseGraph,TorchExecutor
from ppq import QuantizationOptimizationPass # base class
class MyOptimPass(QuantizationOptimizationPass): # inherit this
    # 1. realize _init_ function,name your optim pass
    def _init_(self) -> None:
        super()._init_('My Optim Pass')
    # 2. realize optimize function,do your work
    def optimize(self,graph:BaseGraph,dataloader:Iterable,executor:TorchExecutor,**kwargs) -> None:
        for data in dataloader:
            print('Hi,there is a data batch')

当用户自行创建优化管线时,所有量化过程的参数由用户手动传递,此时用户可以自行设计optimize与init函数的接口;

import torch
from torchvision import models
import ppq.lib as PFL
from ppq import TargetPlatform,TorchExecutor,graphwise_error_analyse
from ppq.api import ENABLE_CUDA_KERNEL,load_torch_model
from ppq.core.quant import (QuantizationPolicy,QuantizationProperty,RoundingPolicy)
from ppq.quantization.optim import (LearnedStepSizePass,ParameterBackingPass,ParameterQuantizePass,RuntimeClibrationPass)
pipeline = PFL.Pipeline([
    ParameterQuantizePass,
    RuntimeClibrationPass,
    LearnedStepSizePass(
        steps=1000,is_scale_trainable=False,
        lr=1e-4,block_size=4,collecting_device='cuda'),
    ParameterBackingPass()
])
with ENABLE_CUDA_KERNEL():
    # 调用管线完成量化,下列参数将传递给每一个优化过程
    pipeline.optimize(
        graph=graph,dataloader=dataset,verbose=True,
        calib_steps=32,collate_fn=collate_fn,executor=executor) 

5.优化过程的调用顺序

优化过程彼此之间是独立的,但并不意味着它们可以乱序执行。优化过程的调用顺序应当是符合逻辑的,很遗憾无法在文档中阐述PPQ中所有优化过程的调用顺序,但用户可以参考ProgramEntrace_2.py样例,参考管线定义的顺序。

如果需要添加自定义优化过程到管线中,则用户需要根据实际情况决定何时调用自定义过程,PPQ不会对优化过程的顺序提供正确性保证。

6.常用量化过程

Qptim Pass
BiasCorrectionPass该过程会分析网络量化后的激活值误差情况,并尝试修改每一层的bias参数降低量化误差。
HorizontalLayerSplitPass该过程会尝试将图中的卷积层分解成两个,从而降低量化误差
LayerwiseEquanlizationPass该过程执行跨层权重、激活值均衡,将使用恒等变换的方式降低网络的量化误差
LearnedStepSizePass该过程会利用传入的Calibration数据集对网络进行微调,从而降低量化误差
RuntimeCalibrationPass该过程收集激活值的统计信息,从而为网络中的激活值创建Scale与Offset,并将它们的状态设置为ACTIVE
QuantizeFusionPass该过程会根据传入的图融合模式修改量化信息,被该过程关闭的量化信息状态将被设置为FP32,并指向一个父量化节点
QuantAlignmentPass该过程会处理Concat,Add,AvaragePooling等算子的输入输出对齐,它们的状态将被设置为PASSIVE,并指向一个父量化节点
ParameterBackingPass该过程会使得参数静态量化,它们的状态将被设置为BAKED,它们的值将被量化后的值所替代
QuantizeSimplifyPass该过程会根据图的连接关系关闭冗余的量化信息,它们的状态将被设置为FP32,并指向一个父量化节点
IsotoneCalibrationPass该过程选取特定的变量并调整其校准方式为保序校准

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值