1.用于加速的cuda内核
PPQ 已经实现了一些 cuda 内核,用于张量的量化-反量化执行过程, 这些内核可以加速量化模式下的图执行过程,当您在以下所有内容之前打开 CUDA 内核选项时,任何基于算法在繁重的图执行都可能会获得加速。
from ppq.core.config import PPQ_CONFIG
PPQ_CONFIG.USING_CUDA_KERNEL = True
注:如果您的环境无法编译共享库,则不必打开上述选项, 它只是为了加速,PPQ 在没有打开的情况下会做得很好。
2.准备模型和数据
首先,您需要准备模型和校准数据文件夹,请注意,PPQ 目前支持的模型只有onnx和caffe,您可能需要提前预处理校准数据并存储为npy 或二进制文件的校准数据文件夹中。
2.1准备模型
2.1.1模型是 onnx 格式
model_path = '/path/to/your/model.onnx'
data_path = '/path/to/your/dataFolder'
2.1.2模型是caffe格式
prototxt_path = '/path/to/your/model.prototxt'
weight_path = '/path/to/your/model.caffemodel'
data_path = '/path/to/your/dataFolder'
2.2自定义Dataloader
你可以自定义你自己的dataloader,你的dataloader可以是任何可迭代的东西,如:列表。
2.2.1npy文件
import os
import numpy as np
import torch
dataloader = [torch.from_numpy(np.load(os.path.join(data_path,file_name))) for file_name in os.listdir(data_path)]
2.2.2bin文件(以numpy数组形式加载)
INPUT_SHAPE = [1,3,224,224]
npy_array = [np.fromfile(os.path.join(data_path,file_name],dtype=np.float32).reshape(*INPUT_SHAPE) for file_name in os.listdir(data_path)
dataloader = [torch.from_numpy(np.load(npy_tensor)) for npy_tensor in npy_array]
2.2.3随机数据(为了测试)
创建一个随机长度为32的校准dataloader,batch为16。
dataloader = [torch.randn(16,3,224,224] for _ in range (32)]
如果你正在使用cuda并且有足够的内存,你可以提前将输入数据放在gpu以确保加速。
dataloader = [torch.randn(16,3,224,224).to('cuda') for _ in range (32)]
如果模型有多个输入,可以用一个字典指定图的每个输入。
dataloader = [{'input_1':torch.randn(16,3,224,224),'input_2':torch.rand(16,3,224,224)} for _ in range(32)]
3.加载模型(只支持onnx/caffe模型)
在做任何事之前,PPQ需要加载你的模型变成PPQ中间计算图形式。
from ppq.api import load_onnx_graph,load_caffe_graph
ppq_graph_ir = load_onnx_graph(model_path)
ppq_graph_ir = load_caffe_graph(prototxt_path,weight_path)
4.确保目标Platform
在量化之前你必须选择你的目标platform(你想部署你的量化模型的后端)。比如:如果你想将你的模型部署在TensorRT,你只需这样指定:
from ppq.core import TargetPlatform
target_platform = TargetPlatform.TRT_INT8
ppq支持的platform:
MNN_INT8 = 100 | TRT_INT8 = 101 | TRT_FP8 = 105 |
NCNN_INT8 =102 | TENGINE_INT8 = 104 | ASC_INT8 = 106 |
PPL_CUDA_INT8 = 201 | PPL_CUDA_INT4 = 202 | PPL_CUDA_FP16 = 203 |
PPL_CUDA_MIX = 204 | PPL_DSP_INT8 = 301 | SNPE_INT8 = 302 |
PPL_DSP_TI_INT8 = 303 | QNN_DSP_INT8 = 304 | HOST_INT8 = 401 |
NXP_INT8 = 501 | FPGA_INT8 = 502 | RKNN_INT8 = 601 |
METAX_INT8_C = 701 | METAX_INT8_T = 702 | HEXAGON_INT8 = 801 |
GRAPHCORE_FP8 = 901 | ***************************** | ***************************** |
PPQ将为指定的目标platform分配一个量化器和一个导出器,不同的目标platform可能产生完全不同的量化方案和导出文件格式。
5.准备你的设置
在进行量化时量化设置扮演着引导者,PPQ已经为一些后端platforms提供了默认设置。
from ppq import QuantizationSettingFactory
setting = QuantizationSettingFactory.pplcuda_setting() # for OpenPPL CUDA
setting = QuantizationSettingFactory.dsp_setting() # for DSP/SNPE
如果你想自定义你的设置,你可以从一个默认设置开始。
setting = QuantizationSettingFactory.default_setting()
如果你想应用SSD均衡算法,而不是默认的均衡方法,你只需要在设置中打开相应的pass即可。
setting = QuantizationSettingFactory.default_setting()
setting.ssd_equalization = True
如果你想应用基于训练的lsp优化,并控制更多的特定pass的细节。
setting.lsq_optimization = True # turn on pass
setting.lsq_optimization_setting.lr = 1e-4 # adjust learning rate
setting.lsq_optimization_setting.epochs = 30 # adjust number of training epochs for every block
参见下面这个网址查看更多关于支持的的pass和它们的应用:
https://kgithub.com/openppl-public/ppq/blob/master/ppq/api/setting.py
6.安排你的图
在用量化器处理中间图之前,PPQ需要调度中间图上的算子到不同的platforms。例如,关于形状的算子将会被调度到TargetPlatform.SHAPE_OR_INDEX;不可量化的算子将会被调度到TargetPlatform.FP32,这意味着它们将始终以fp32模式运行,并且永远不会被量化。
from ppq.api.interface import dispatch_graph
ppq_graph_ir = dispatch_graph(ppq_graph_ir,target_platform,setting)
7.初始化执行器
所有的操作都是由PPQ中的TorchExecutor执行。
from ppq.executor import TorchExecutor
executor = TorchExecutor(ppq_graph_ir,device='cuda') # for cuda execution
executor = TorchExecutor(ppq_graph_ir,device='cpu') # for cpu execution
8.量化
PPQ将为您的目标平台指定一个量化器,该量化器将遵循以下惯例来实际运行量化:
- 准备校验需要的调度中间图
- 细化某些操作的量化行为
- 运行参数和激活的校准过程
- 提供量化参数
- 运行量化设置中指定的激活优化算法
from ppq.api.interface import QUANTIZER_COLLECTION
quantizer = QUANTIZER_COLLECTION[target_platform](graph=ppq_graph_ir)
quantizer.quantize(
inputs=dummy_input, # some random input tensor,should be list or dict for multiple inputs
calib_dataloader=dataloader, # calibration dataloader
executor=executor, # executor in charge of everywhere graph execution is needed
setting=setting, # quantization setting
calib_steps=calib_steps, # number of batched data needed in calibration,8~512
collate_fn=lambda x: x.to(EXECUTING_DEVICE) # final processing of batched data tensor
)
9.推理模拟
量化之后,量化的ppq中间图处于量化模式,如果你直接用TorchExecutor运行量化的ppq中间图:
for data in dataloader:
if collate_fn is not None: # process batched data tensor
data = collate_fn(data)
outputs = executor.forward(data)
你将获得在量化模式下的每个可量化算子的最终输出,然而,如果你想禁用量化获得fp32输出,你只需禁用每个可量化操作的量化。
for op in ppq_ir_graph.operations.values():
if isinstance(op,QuantableOperation):
op.dequantize() # disable quantization
outputs = executor.forward(data) # execution in fp32 mode
10.分析
PPQ提供了强大的分析工具来分析量化图不同层的精度下降,graphwise_error_analyse考虑了执行过程中的量化误差积累,而layerwise_error_analyse则考虑了每一层的量化误差,如果你想通过分析fp32输出和不同层的量化输出的信号噪声比来了解量化图的整体性能:
from ppq.quantization.analyse import layerwise_error_analyse,graphwise_error_analyse
graphwise_error_analyse(
graph=quantized, # ppq ir graph
running_device=EXECUTING_DEVICE, # cpu or cuda
method='snr', # the metric is signal noise ratio by default,adjust it to 'cosine' if that's desired
step=32, # how many batches of data will be used for error analysis
dataloader=dataloader,
collate_fn=lambda x: x.to(EXECUTING_DEVICE)
)
或者分析每一层的量化误差:
layerwise_error_analyse(
graph=quantized,
running_device=EXECUTING_DEVICE,
method='snr' # the metric is signal noise ratio by default, adjust it to 'cosine' if that's desired
steps=32,
dataloader-dataloader
collate_fn=lambda x: x.to(EXECUTING_DEVICE)
)
11.导出
为了在目标后端部署你的模型,应该从量化的PPQ中间图中导出适当格式的量化模型和相应的量化参数。PPQ将为不同的目标平台指定不同的导出器。例如,如果OpenPPL CUDA(PPL_CUDA_INT8)是所需的后端,PPLBackendExporter将导出一个onnx模型和一个指定量化参数的json文件,关于更多的目标平台和导出器,请查看interface.py。
通常情况下,目标平台决定了量化后的中间图的确切导出格式,但有时你可能想以不同的格式导出,比如你想在PPL_CUDA_INT8上部署你的模型:
from ppq.api.interface import export_ppq_graph
export_platform = TargetPlatform.PPL_CUDA_INT8 # could be other platforms in TargetPlatform class
export_ppq_graph(graph=ppq_ir_graph,platform=platform,graph_save_to='quantized',config_save_to='quantized.json')
或者你想部署你的模型在NCNN_INT8上,那么需要一个量化表文件:
export_ppq_graph(graph=ppq_ir_graph,platform=TargetPlatform.NCNN_INT8,graph_save_to='quantized',config_save_to='quantized.table')