深度学习-模型优化

363 阅读4分钟

1 小型化

常见模型转换场景:

image.png

概念:

  • ONNX(Open Neural Network Exchange) onnx是针对机器学习的开放文件格式,用于存储训练模型。ONNX Runtime是支持onnx格式模型的高性能推理引擎。

  • TFLite TFLite是把深度学习模型部署在移动端和嵌入式设备的工具包。TFlite主要由Converter和Interpreter组成。Converter负责把TensorFlow训练好的模型转化,并输出为.tflite文件。Interpreter则负责把.tflite部署到移动端,高效易用地执行推理。

  • MindIR MindSpore统一IR定义网络结构和算子属性,MindIR格式的模型文件与硬件平台解耦。

  • AIR 华为定义的开放式文件格式。

  • MindSpore执行推理 本机推理:加载网络训练产生的Checkpoint文件,调用model.predict接口进行推理验证。 跨平台推理:使用网络定义和Checkpoint文件,调用export接口导出模型文件,在不同平台执行推理,目前支持导出MindIR、ONNX和AIR(仅支持Ascend AI处理器)模型。

  • Ascend模型量化工具 Ascend提供的模型量化工具,可在原训练环境完成离线或在线量化。

image.png

2 MSLite训练后量化PTQ

  • 全量化 权值、激活值量化,推理时执行int8运算,提升模型推理速度、降低功耗

  • 权重量化 权重量化,仅压缩模型大小,推理时执行float32运算

  • 权重量化on_the_fly 权重量化是编译图阶段权重反量化fp32,on_the_fly是在线predict阶段反量化权值

  • 动态量化 离线转换阶段量化权重,在运行阶段量化激活,动态量化算子(matmul)执行int8运算,效果:提升NLP模型推理速度,降低功耗、内存

总结:全量化、权重量化、动态量化三类中,权重量化精度损失最小,动态量化推理加速和精度都较好,全量化时延、内存优化效果最好;

tflite模型优化
tflite 模型优化比较全面,文档丰富,会提供量化策略指导。

3 量化实现步骤

step1:在输入数据(通常是权重或者激活值)中统计出相应的最小值和最大值;
step2:选择合适的量化类型,对称量化(int8)还是非对称量化(uint8);
step3:根据量化类型、最小值、最大值来计算获得量化的参数Z(Zero point)和S(Scale);
step4:根据标定数据对模型执行量化操作,即将其由FP32转换为INT8;
step5:验证量化后的模型性能,如果效果不好,尝试使用不同的方式计算S和Z,重新执行上述操作。

4 Pytorch量化

静态量化 vs 动态量化

输入激活对应的量化参数是否预先计算

感知训练 vs 训练后量化

是否需要再训练量化参数

4.1 动态量化

对权重进行线下量化,activation是根据推理过程中activationd的取值范围进行量化,同一个模型在处理不同的输入图像时,activation采用的量化参数很可能是不一样的。
支持线性层和递归(LSTM,GRU,RNN)层的动态量化,常用于NLP领域的模型。优点:精度高,对于LSTMs、transformers,动态量化是首选。缺点:运行时对每一层需要进行校准、量化,增加计算开销。

torch.quantized.quantize_dynamic

4.2 静态量化PTQ

对权重和activation均进行线下量化。同一个量化模型在处理不同图片时的量化参数是一样的。静态量化主要针对CNN网络。

4.2 感知量化PTQ

通过量化后的重训练改善量化模型的精度,对某些(如MobileNet)在静态量化下精度损失较大的网络有着明显的精度提升。 优点:准确度高于静态量化; 缺点:感知量化中重训练的计算成本

感知量化训练关键步骤:
step1:搭建模型
step2:融合(可选)
step3:插入stubs(可与step1合在一起)
step4:选择架构
step5:训练量化模型
step6:模型转换

import torch
from torch import nn
 
# step1:搭建模型(融合step3)
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # 添加量化模块,使用QuantStub()将float32的tensors转换为int8
        self.quant = torch.quantization.QuantStub() # 只用于输入
        # 定义fp32浮点模型
        self.model_fp32 = nn.Sequential(
                            nn.Conv2d(2, 64, 8),
                            nn.ReLU(),
                            nn.Conv2d(64, 128, 8),
                            nn.ReLU()
       					 )
        # 使用DeQuantStub()将tensors由int8转为float32
        self.dequant = torch.quantization.DeQuantStub() # 只用于输出
        
    def forward(self, x):
        x = self.quant(x)  # QuantStub()
        x = self.model_fp32(x)
        x = self.dequant(x)  # DeQuantStub()
        return x
    
quant_net = Net()
# step2:融合conv+bn+relu(可选)
torch.quantization.fuse_modules(quant_net, ['0','1'], inplace=True) # fuse first Conv-ReLU
torch.quantization.fuse_modules(quant_net, ['2','3'], inplace=True) # fuse second Conv-ReLU
 
# step3:插入stubs,浮点模型前加QuantStub(),之后加DeQuantStub()
# step4:设置合理的quantization_config
quant_net.qconfig = torch.quantization.get_default_qat_qconfig('fbgemm') #如果在ARM上训练则为'qnnpack'
torch.quantization.prepare_qat(quant_net, inplace=True)
 
# step5:训练量化模型
epochs = 30
optimizer = torch.optim.SGD(quant_net.paramters(), lr=0.01, momentum=0.9, weight_decay=1e-4)
criterion = nn.MSELoss()
for epoch in range(epochs):
    quant_net.train()
    inputs = torch.rand(10,2,24,24)
    optimizer.zero_grad()  
    outputs = quant_net(inputs) 
    labels = torch.rand_like(outputs) 
    loss = criterion(inputs, labels) 
    loss.backward()  
    optimizer.step() 
 
# step6:模型转换
quant_net.eval()
torch.quantization.convert(quant_net, inplace=True) # 将fp32module转换为int8 module

对称量化 VS 非对称量化