最近在做量化相关工作,QQP量化工具是非常全且有文档的repo。目前量化相关的内容太少了,也感谢当前网络上所有量化内容输出作者。这篇文档记录PPQ量化工具安装使用和跑demo,下一篇记录PPQ库里KLD算法的解读。
代码库:https://github.com/openppl-public/ppq
配环境:按照PPQ的readme
注意
git clone https://github.com/openppl-public/ppq.git
cd ppq
pip install -r requirements.txt
python setup.py install
至此安装成功
首先需要将torch模型转换为onnx模型。转换主要是两种场景目的:转换torchvision的预训练模型、转换自定义模型。
转换都是两个步骤:加载模型和权重、调onnx接口转换
model = models.vgg16(pretrained=True)就会将模型下载到相应路径
#coding=gbk
# 由.pt导成.onnx
import torch
import torchvision.models as models# 定义模型和载入模型权重
#model = models.resnet18() # 【改】定义model
#model = models.resnet50()
model = models.vgg16(pretrained=False)
model.load_state_dict(torch.load("/home/xxx/.cache/torch/hub/checkpoints/vgg16-397923af.pth")) # 【改】model权重地址# #set the model to inference mode
model.eval()x = torch.randn(1, 3,224,224) # 生成张量
export_onnx_file = "/home/xxx/model_optimization_tool/jm_log_quant/onnx_format_weight/vgg16.onnx" # 【改】输出ONNX权重地址
torch.onnx.export(model,x,export_onnx_file,opset_version=10,do_constant_folding=True, # 是否执行常量折叠优化input_names=["input"], # 输入名output_names=["output"], # 输出名dynamic_axes={"input":{0:"batch_size"}, # 批处理变量"output":{0:"batch_size"}})
有视频教程:PPQ官方教程合集视频教程1的代码是ppq0.6.3版本,但是该版本网页404,故将其写手打。
整个量化过程:
设置量化参数 --> 载入数据 --> 执行量化(如果要看结果,则定制一个executor) --> 导出所有层量化结果 --> 分析量化误差(总误差,每层误差) --> 导出量化权重文件
"""
这是一个PPQ量化的入口脚本,将你的模型和数据按要求进行打包:
This file will show you how to quantize your network with PPQYou should prepare your model and calibration dataset as follow:~/working/model.onnx <-- your model~/working/data/*.npy or ~/working/data/*.bin <-- your dataset
if you are using caffe model:~/working/model.caffemdoel <-- your model~/working/model.prototext <-- your model
### MAKE SURE YOUR INPUT LAYOUT IS [N, C, H, W] or [C, H, W] ###
quantized model will be generated at: ~/working/quantized.onnx
"""
from ppq import *
from ppq.api import *
import os# modify configuration below:
WORKING_DIRECTORY = 'working' # choose your working directory
TARGET_PLATFORM = TargetPlatform.PPL_CUDA_INT8 # choose your target platform
MODEL_TYPE = NetworkFramework.ONNX # or NetworkFramework.CAFFE
INPUT_LAYOUT = 'chw' # input data layout, chw or hwc
NETWORK_INPUTSHAPE = [1, 3, 224, 224] # input shape of your network
CALIBRATION_BATCHSIZE = 16 # batchsize of calibration dataset
EXECUTING_DEVICE = 'cuda' # 'cuda' or 'cpu'.
REQUIRE_ANALYSE = False
DUMP_RESULT = False # 是否需要 Finetuning 一下你的网络# ------------------------------------------------------------------------------
# SETTING对象用于控制PPQ的量化逻辑
# 当你的网络量化误差过高时,你需要修改SETTING对象中的参数进行特定的优化
# ------------------------------------------------------------------------------
SETTING = UnbelievableUserFriendlyQuantizationSetting(platform=TARGET_PLATFORM,finetune_steps=2500,finetune_lr=1e-3, calibration='percentile',equalization=True, non_quantable_op=None)
SETTING = SETTING.convert_to_daddy_setting()print('正准备量化你的网络,检查下列设置:')
print(f'WORKING DIRECTORY : {WORKING_DIRECTORY}')
print(f'TARGET PLATFORM : {TARGET_PLATFORM.name}')
print(f'NETWORK INPUTSHAPE : {NETWORK_INPUTSHAPE}')
print(f'CALIBRATION BATCHSIZE: {CALIBRATION_BATCHSIZE}')# ------------------------------------------------------------------------------
# 此脚本针对单输入模型,输入数据必须是图像数据layout:[n,c,h,w]
# 如果你的模型具有更复杂的输入格式,你可以重写下面的load_calibration_dataset函数
# 请注意,任何可遍历对象都可以作为PPQ的数据集作为输入
# ------------------------------------------------------------------------------
dataloader = load_calibration_dataset(directory=WORKING_DIRECTORY,input_shape=NETWORK_INPUTSHAPE,batchsize=CALIBRATION_BATCHSIZE,input_format=INPUT_LAYOUT)print('网络正量化中,根据你的量化配置,这将需要一段时间:')
quantized=quantize(working_directory=WORKING_DIRECTORY,setting=SETTING,model_type=MODEL_TYPE,executing_device=EXECUTING_DEVICE,input_shape=NETWORK_INPUTSHAPE,target_platform=TARGET_PLATFORM,dataloader=dataloader,calib_steps=32)# ------------------------------------------------------------------------------
# 如果你需要执行量化后的神经网络并得到结果,则需要创建一个executor
# 这个executor的行为和torch.Module是类似的,你可以利用这个东西来获取执行结果
# 请注意必须在executor之前执行此操作
# ------------------------------------------------------------------------------
executor = TorchExecutor(graph=quantized)
# output=executor.forword(input)# ------------------------------------------------------------------------------
# 导出PPQ执行网络的所有中间结果,该功能是为了和硬件对比结果
# 中间结果可能十分庞大,因此PPQ将使用线性同余发射器从执行结果中采样
# 对了对比中间结果,硬件执行结果也必须使用同样的随机数种子采样
# 查阅 ppq.util.fetch中的相关代码以进一步了解此内容
# 查阅 ppq.api.fsys中的dump_internal_results函数以确定采样逻辑
# ------------------------------------------------------------------------------
if DUMP_RESULT:dump_internal_results(graph=quantized,dataloader=dataloader,dump_dir=WORKING_DIRECTORY,executing_device=EXECUTING_DEVICE)# -------------------------------------------------------------------
# PPQ 计算量化误差时,使用信噪比的倒数作为指标,即噪声能量 / 信号能量
# 量化误差 0.1 表示在整体信号中,量化噪声的能量约为 10%
# 你应当注意,在 graphwise_error_analyse 分析中,我们衡量的是累计误差
# 网络的最后一层往往都具有较大的累计误差,这些误差是其前面的所有层所共同造成的
# 你需要使用 layerwise_error_analyse 逐层分析误差的来源
# -------------------------------------------------------------------
print('正计算网络量化误差(SNR),最后一层的误差应小于 0.1 以保证量化精度:')
reports = graphwise_error_analyse(graph=quantized, running_device=EXECUTING_DEVICE, steps=32,dataloader=dataloader, collate_fn=lambda x: x.to(EXECUTING_DEVICE))
for op, snr in reports.items():if snr > 0.1: ppq_warning(f'层 {op} 的累计量化误差显著,请考虑进行优化')if REQUIRE_ANALYSE:print('正计算逐层量化误差(SNR),每一层的独立量化误差应小于 0.1 以保证量化精度:')layerwise_error_analyse(graph=quantized, running_device=EXECUTING_DEVICE,interested_outputs=None,dataloader=dataloader, collate_fn=lambda x: x.to(EXECUTING_DEVICE))print('网络量化结束,正在生成目标文件:')
export_ppq_graph(graph=quantized, platform=TARGET_PLATFORM,graph_save_to = os.path.join(WORKING_DIRECTORY, 'quantized.onnx'),config_save_to = os.path.join(WORKING_DIRECTORY, 'quant_cfg.json'))