量化训练及精度调优经验分享

本文提纲:

  1. fx 和 eager 两种量化训练方式介绍
  2. 量化训练的流程介绍:以 mmdet 的 yolov3 为例
  3. 常用的精度调优 debug 工具介绍
  4. 案例分析:模型精度调优经验分享

第一部分:fx 和 eager 两种量化训练方式介绍

首先介绍一下量化训练的原理。

上图为单个神经元的计算,计算形式是加权求和,再经过非线性激活后得到输出,这个输出又可以作为下一个神经元的输入继续运输,所以神经网络的基础运算是矩阵的乘法。如果神经元的计算全部采用 float32 的形式,模型的内存占用和数据搬运都会很占资源。如果用 int8 替换 float32,内存的搬运效率能提高 75%,充分展示了量化的有效性。由于两个 int8 相乘会超出 int8 的表示范围,为了防止溢出,累加器使用 int32 类型的,累加后的结果会再次 requantized 到 int8;

量化的目标就是在尽可能不影响模型精度的情况下降低模型的功耗,实现模型压缩效果,常见的量化方式有后量化训练 PTQ 和量化感知训练 QAT。

量化感知训练其实是一种伪量化的过程,即在训练过程中模拟浮点转定点的量化过程,数据虽然都是表示为 float32,但实际的值会间隔地受到量化参数的限制。具体方法是在某些 op 前插入伪量化节点(fake quantization nodes),伪量化节点有两个作用:

1.在训练时,用以统计流经该 op 的数据的最大最小值,便于在部署量化模型时对节点进行量化

2.伪量化节点参与模型训练的前向推理过程,因此会模型训练中导入了量化损失,但伪量化节点是不参与梯度更新过程的。

上图是模型学习量化损失的示意图, 正常的量化流程是 quantize->mul(int)->dequantize,而伪量化是对原先的 float 先 quantize 到 int,再 dequantize 到 float,这个步骤用于模拟量化过程中 round 操作所带来的误差,用这个误差再去进行前向运算。上图可以比较直观的表示引起误差的原因,从左到右数第 4 个黑点表示一个浮点数,quantize 后映射到 253,dequantize 后取到了第 5 个黑点,这就引起了误差。

地平线基于 PyTorch 开发的 horizon_plugin_pytorch 量化训练工具,同时支持 Eager 和 fx 两种模式。

eager 模式的使用方式建议参考用户手册 -4.2 量化感知训练章节(4.2.2。 快速上手中有完整的快速上手示例,各使用阶段注意事项建议参考 4.2.3。 使用指南)。fx 模式的相关 API 介绍请参考用户手册 -4.2.3.4.2。 主要接口参数说明章节

第二部分:量化训练的流程介绍:以 mmdet 的 yolov3 为例

QAT 流程介绍

准备好浮点模型,加载训好的浮点权重

    model = build_detector(cfg.model,train_cfg=cfg.get('train_cfg'),test_cfg=cfg.get('test_cfg'))model.init_weights()# 加载config里的 init_cfg

设置 BPU 架构

set_march(March.BAYES)

算子融合(eager 模式需要,fx 可省略)

    # qat: run fuse_module to fuse conv+bn/relu/add opmodel.backbone.fuse_modules()model.neck.fuse_modules()model.bbox_head.fuse_modules()

设置量化配置

  • 整个 model 使用默认的 qconfig
  • 模型的输出,配置高精度输出
  • det 模型 head 输出的 loss 损失函数的 qconfig 设置为 None
    # qat: set qconfig for float modelmodel.qconfig = get_default_qat_qconfig()# qat: set default_qat_out_qconfig for last convfor m in model.bbox_head.convs_pred:m.qconfig = get_default_qat_out_qconfig()# qat: set None for loss qconfig, loss should be quantizedmodel.bbox_head.loss_cls.qconfig = Nonemodel.bbox_head.loss_conf.qconfig = Nonemodel.bbox_head.loss_xy.qconfig = Nonemodel.bbox_head.loss_wh.qconfig = None

将浮点模型转换为 qat 模型(示例使用 eager 模式)

    qat_model = prepare_qat(model)qat_model.to(torch.device("cuda:1"))

开始 qat 训练

  1. 可以复用浮点的 train_detector,替换 model 即可
train_detector(qat_model,datasets,cfg,distributed=distributed,validate=(not args.no_validate),timestamp=timestamp,meta=meta)

qat 模型转定点(需要 load 训练好的 qat 模型权重)

quantized_model = convert(qat_model.eval())

deploy_model 和 example_input 准备

    deploy_model = DeployModel(quantized_model.backbone, quantized_model.neck,quantized_model.bbox_head).to(torch.device("cuda:1"))example_input = torch.randn(size=(24, 3, 320, 320), device=torch.device("cuda:1"))

Trace 模型构建静态 graph,进行编译

  • eval()使 bn、dropout 等处于正确的状态
  • 编译只能在 cpu 上做
  • check_model 用于检查算子是否能全部跑在 bpu 上,建议提前检查
    traced_model = torch.jit.trace(deploy_model.eval(), example_input)traced_model.to(torch.device("cpu"))example_input.to(torch.device("cpu"))check_model(traced_model, example_input, advice=1)compile_model(traced_model, [example_input], opt=0, hbm="model.hbm")

如果 qat 精度不达标,如何插入 calibration?

1. 准备好浮点模型,加载训好的浮点权重
2. 设置BPU架构
3. 算子融合(eager模式需要,fx可省略)
4. 设置model的量化配置
-----------------calib_model-------------------
calib_model = prepare_qat(float_model)
calib_model.eval() # 使bn、dropout等处于正确的状态
set_fake_quantize(calib_model, FakeQuantState.CALIBRATION) # 不进行伪量化操作,仅观测算子输入输出统计量,更新scale
#校准训练(可复用浮点的train_detector,替换model即可)
train_detector(calib_model,datasets,cfg,distributed=distributed,validate=(not args.no_validate),timestamp=timestamp,meta=meta)#校准精度验证
calib_model.eval()
set_fake_quantize(calib_model, FakeQuantState.VALIDATION)
val(calib_model,val_dataloader,device) 
-----------此时calib_model里的scale已经更新了-------------------------
qat_model = prepare_qat(float_model)
-----------qat_model加载calib训练好的模型权重,开始qat训练-----------------------------------------------
train_detector(qat_model,datasets,cfg,distributed=distributed,validate=(not args.no_validate),timestamp=timestamp,meta=meta)

伪量化节点(fake quantize)的三种状态:

  • CALIBRATION 模式:即不进行伪量化操作,仅观测算子输入输出统计量,更新 scale
  • QAT 模式:观测统计量并进行伪量化操作。
  • VALIDATION 模式:不会观测统计量,仅进行伪量化操作。

以下常见误操作会导致一些异常现象:

  1. calibration 之前模型设置为 train()的状态,且未使用set_fake_quantize,等于是在跑 QAT 训练;
  2. calibration 之前模型设置为 eval()的状态,且未使用set_fake_quantize,会导致 scale 一直处于初始状态,全为 1,calib 不起作用。
  3. calibration 之前模型设置为 eval()的状态,且正确使用了set_fake_quantize,但是在这之后又设置了一遍 model.eval(),这将导致 fake_quant 未处于训练状态,scale 一直处于初始状态,全为 1;

对 mobilenet_v2 模型做 qat 训练的设置

量化节点设置

关键代码:

from horizon_plugin_pytorch.quantization import QuantStubself.quant = QuantStub(scale=1/128) # 一般 pyramid 输入的 Quant 层,需要手动设置 scale=1/128def fuse_modules(self):x = self.quant(x)
# Copyright (c) OpenMMLab. All rights reserved.
import warningsimport torch.nn as nn
from mmcv.cnn import ConvModule
from mmcv.runner import BaseModule
from torch.nn.modules.batchnorm import _BatchNorm
from horizon_plugin_pytorch.quantization import QuantStubfrom ..builder import BACKBONES
from ..utils import InvertedResidual, make_divisible
import torch@BACKBONES.register_module()
class MobileNetV2(BaseModule):arch_settings = [[1, 16, 1, 1], [6, 24, 2, 2], [6, 32, 3, 2],[6, 64, 4, 2], [6, 96, 3, 1], [6, 160, 3, 2],[6, 320, 1, 1]]def __init__(self,widen_factor=1.,out_indices=(1, 2, 4, 7),frozen_stages=-1,conv_cfg=None,norm_cfg=dict(type='BN'),act_cfg=dict(type='ReLU6'),norm_eval=False,with_cp=False,pretrained=None,init_cfg=None):super(MobileNetV2, self).__init__(init_cfg)# qat: model start with Quantization node# and set scale=1/128self.quant = QuantStub(scale=1/128) # 一般pyramid输入的Quant层,需要手动设置scale=1/128self.pretrained = pretrainedassert not (init_cfg and pretrained), \'init_cfg and pretrained cannot be specified at the same time'if isinstance(pretrained, str):warnings.warn('DeprecationWarning: pretrained is deprecated, ''please use "init_cfg" instead')self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)elif pretrained is None:if init_cfg is None:self.init_cfg = [dict(type='Kaiming', layer='Conv2d'),dict(type='Constant',val=1,layer=['_BatchNorm', 'GroupNorm'])]else:raise TypeError('pretrained must be a str or None')self.widen_factor = widen_factorself.out_indices = out_indicesif not set(out_indices).issubset(set(range(0, 8))):raise ValueError('out_indices must be a subset of range'f'(0, 8). But received {out_indices}')if frozen_stages not in range(-1, 8):raise ValueError('frozen_stages must be in range(-1, 8). 'f'But received {frozen_stages}')self.out_indices = out_indicesself.frozen_stages = frozen_stagesself.conv_cfg = conv_cfgself.norm_cfg = norm_cfgself.act_cfg = act_cfgself.norm_eval = norm_evalself.with_cp = with_cpself.in_channels = make_divisible(32 * widen_factor, 8)self.conv1 = ConvModule(in_channels=3,out_channels=self.in_channels,kernel_size=3,stride=2,padding=1,conv_cfg=self.conv_cfg,norm_cfg=self.norm_cfg,act_cfg=self.act_cfg)self.layers = []for i, layer_cfg in enumerate(self.arch_settings):expand_ratio, channel, num_blocks, stride = layer_cfgout_channels = make_divisible(channel * widen_factor, 8)inverted_res_layer = self.make_layer(out_channels=out_channels,num_blocks=num_blocks,stride=stride,expand_ratio=expand_ratio)layer_name = f'layer{i + 1}'self.add_module(layer_name, inverted_res_layer)self.layers.append(layer_name)if widen_factor > 1.0:self.out_channel = int(1280 * widen_factor)else:self.out_channel = 1280layer = ConvModule(in_channels=self.in_channels,out_channels=self.out_channel,kernel_size=1,stride=1,padding=0,conv_cfg=self.conv_cfg,norm_cfg=self.norm_cfg,act_cfg=self.act_cfg)self.add_module('conv2', layer)self.layers.append('conv2')def make_layer(self, out_channels, num_blocks, stride, expand_ratio):"""Stack InvertedResidual blocks to build a layer for MobileNetV2.Args:out_channels (int): out_channels of block.num_blocks (int): number of blocks.stride (int): stride of the first block. Default: 1expand_ratio (int): Expand the number of channels of thehidden layer in InvertedResidual by this ratio. Default: 6."""layers = []for i in range(num_blocks):if i >= 1:stride = 1layers.append(InvertedResidual(self.in_channels,out_channels,mid_channels=int(round(self.in_channels * expand_ratio)),stride=stride,with_expand_conv=expand_ratio != 1,conv_cfg=self.conv_cfg,norm_cfg=self.norm_cfg,act_cfg=self.act_cfg,with_cp=self.with_cp))self.in_channels = out_channelsreturn nn.Sequential(*layers)def _freeze_stages(self):if self.frozen_stages >= 0:for param in self.conv1.parameters():param.requires_grad = Falsefor i in range(1, self.frozen_stages + 1):layer = getattr(self, f'layer{i}')layer.eval()for param in layer.parameters():param.requires_grad = False# qat: do fuse modeldef fuse_modules(self):self.conv1.fuse_modules()for layer_name in self.layers:layer = getattr(self, layer_name)if hasattr(layer, "fuse_modules"):layer.fuse_modules()elif isinstance(layer, nn.Sequential):for m in layer:if hasattr(m, "fuse_modules"):m.fuse_modules()def forward(self, x):"""Forward function."""# qat: qat model start with QuantStubx = self.quant(x)x = self.conv1(x)outs = []for i, layer_name in enumerate(self.layers):layer = getattr(self, layer_name)x = layer(x)if i in self.out_indices:outs.append(x)return tuple(outs)def train(self, mode=True):"""Convert the model into training mode while keep normalization layerfrozen."""super(MobileNetV2, self).train(mode)self._freeze_stages()if mode and self.norm_eval:for m in self.modules():# trick: eval have effect on BatchNorm onlyif isinstance(m, _BatchNorm):m.eval()

算子融合

[7.5.5. 算子融合 — Horizon Open Explorer](https://developer.horizon.ai/api/v1/fileData/horizon_j5_open_explorer_cn_doc/plugin/source/advanced_content/op_fusion.html?highlight=算子融合 算子 融合#)

举个例子:mmcv/cnn/bricks/conv_module.py

class ConvModule(nn.Module):
...
# qat: fuse conv + bn/reludef fuse_modules(self):fuse_list = Noneif self.with_norm:if self.with_activation:fuse_list = ["conv", self.norm_name, "activate"] # conv+bn+reluelse:fuse_list = ["conv", self.norm_name] # conv+bnelse:if self.with_activation:fuse_list = ["conv", "activate"] # conv+reluif fuse_list is not None:torch.quantization.fuse_modules(self,fuse_list,inplace=True,fuser_func=quantization.fuse_known_modules,)

eager 方案麻烦的是,基本每个模块都要手动去设置算子融合

反量化节点设置

mmdetection-master/mmdet/models/dense_heads/yolo_head.py

关键代码:

self.dequant = nn.ModuleList() # 不止1个反量化节点,用list包起来self.dequant.append(DeQuantStub())def fuse_modules(self):pred_map = self.dequant[i](self.convs_pred[i](x))
class YOLOV3Head(BaseDenseHead, BBoxTestMixin):def __init__(self,num_classes,in_channels,out_channels=(1024, 512, 256),anchor_generator=dict(type='YOLOAnchorGenerator',base_sizes=[[(116, 90), (156, 198), (373, 326)],[(30, 61), (62, 45), (59, 119)],[(10, 13), (16, 30), (33, 23)]],strides=[32, 16, 8]),bbox_coder=dict(type='YOLOBBoxCoder'),featmap_strides=[32, 16, 8],one_hot_smoother=0.,conv_cfg=None,norm_cfg=dict(type='BN', requires_grad=True),# qat# act_cfg=dict(type='LeakyReLU', negative_slope=0.1),act_cfg=dict(type='ReLU'),loss_cls=dict(type='CrossEntropyLoss',use_sigmoid=True,loss_weight=1.0),loss_conf=dict(type='CrossEntropyLoss',use_sigmoid=True,loss_weight=1.0),loss_xy=dict(type='CrossEntropyLoss',use_sigmoid=True,loss_weight=1.0),loss_wh=dict(type='MSELoss', loss_weight=1.0),train_cfg=None,test_cfg=None,init_cfg=dict(type='Normal', std=0.01,override=dict(name='convs_pred'))):super(YOLOV3Head, self).__init__(init_cfg)# Check paramsassert (len(in_channels) == len(out_channels) == len(featmap_strides))self.num_classes = num_classesself.in_channels = in_channelsself.out_channels = out_channelsself.featmap_strides = featmap_stridesself.train_cfg = train_cfgself.test_cfg = test_cfgif self.train_cfg:self.assigner = build_assigner(self.train_cfg.assigner)if hasattr(self.train_cfg, 'sampler'):sampler_cfg = self.train_cfg.samplerelse:sampler_cfg = dict(type='PseudoSampler')self.sampler = build_sampler(sampler_cfg, context=self)self.fp16_enabled = Falseself.one_hot_smoother = one_hot_smootherself.conv_cfg = conv_cfgself.norm_cfg = norm_cfgself.act_cfg = act_cfgself.bbox_coder = build_bbox_coder(bbox_coder)self.prior_generator = build_prior_generator(anchor_generator)self.loss_cls = build_loss(loss_cls)self.loss_conf = build_loss(loss_conf)self.loss_xy = build_loss(loss_xy)self.loss_wh = build_loss(loss_wh)self.num_base_priors = self.prior_generator.num_base_priors[0]assert len(self.prior_generator.num_base_priors) == len(featmap_strides)self._init_layers()def _init_layers(self):self.convs_bridge = nn.ModuleList()self.convs_pred = nn.ModuleList()self.dequant = nn.ModuleList() # 不止1个反量化节点,用list包起来for i in range(self.num_levels):conv_bridge = ConvModule(self.in_channels[i],self.out_channels[i],3,padding=1,conv_cfg=self.conv_cfg,norm_cfg=self.norm_cfg,act_cfg=self.act_cfg)conv_pred = nn.Conv2d(self.out_channels[i],self.num_base_priors * self.num_attrib, 1)self.convs_bridge.append(conv_bridge)self.convs_pred.append(conv_pred)self.dequant.append(DeQuantStub())def fuse_modules(self):for m in self.convs_bridge:m.fuse_modules()def forward(self, feats):"""Forward features from the upstream network.Args:feats (tuple[Tensor]): Features from the upstream network, each isa 4D-tensor.Returns:tuple[Tensor]: A tuple of multi-level predication map, each is a4D-tensor of shape (batch_size, 5+num_classes, height, width)."""assert len(feats) == self.num_levelspred_maps = []for i in range(self.num_levels):x = feats[i]x = self.convs_bridge[i](x)pred_map = self.dequant[i](self.convs_pred[i](x))pred_maps.append(pred_map)return tuple(pred_maps),

第三部分:常用的精度调优 debug 工具介绍

工具:集成接口量化配置检查、模型可视化、相似度对比统计量、分步量化、异构模型部署 device 检查

第四部分:模型精度调优分享

模型精度调优时常遇到的问题:

  1. calib 模型的精度和 float 对齐,quantized 模型的精度损失较大

正常情况下,calib/qat 模型的精度和 quantized 模型的精度损失很小(1%), 如果偏差过大,可能是 calib/qat 的流程不对。

原因:calib 模型伪量化节点的状态不正确,导致 calib 阶段,测试的是 float 模型的精度,而 quantized 阶段,测试的是 calib 模型的精度,所以精度损失本质上还是量化精度的损失。

如何避免:

  • 正确设置 calib 训练和评测时的伪量化节点状态。
  • 让客户在 calib 的基础上,做 qat, 评测 qat 模型的精度。(客户的数据量大,qat 时间太长,一直没有选择 qat,导致这个问题被暴露出来了)

如何设置正确的 calib 伪量化节点的状态?(fx 和 eager 都是一样的)

http://model.aidi.hobot.cc/api/docs/horizon_plugin_pytorch/latest/html/user_guide/calibration.html

#加载浮点模型权重model.load_state_dict(torch.load("output/toy_experiments/model_moderate_best_soft_float_131892.pth"))set_march(March.BAYES)#校准配置calib_model = prepare_qat_fx(model,{"":default_calib_8bit_fake_quant_qconfig,"module_name":...}).to(device)calib_model.to(device)#校准需要全程开启eval()状态calib_model.eval()#校准的训练阶段,设置伪量化节点模式为 CALIBRATIONset_fake_quantize(calib_model, FakeQuantState.CALIBRATION)train(cfg, calib_model, device, distributed)#校准的评测阶段,设置伪量化节点的模式为 VALIDATIONset_fake_quantize(calib_model, FakeQuantState.VALIDATION)#加载校准的模型权重calib_model.load_state_dict(torch.load("output/toy_experiments/model_moderate_best_soft_calib_118633.pth"))#测试校准的精度run_test(cfg, calib_model, vis=args.vis, eval_score_iou=args.eval_score_iou, eval_all_depths=args.eval_all_depths) # 11.8650

注意:16 行的 train 在评测时,也要设置 FakeQuantState.VALIDATION,不然 scale 不生效,评测的指标也不对

常见问题:

  1. 数据校准之前模型设置为 train()的状态,且未使用set_fake_quantize,等于 caib 阶段是在跑 QAT 训练;
  2. 校准的评测阶段,未设置伪量化节点的模式为 VALIDATION, 实际评测的是 float 模型;

总结 2: 如果做 calib,一定要仔细检查伪量化节点状态和模型状态是否正确,避免不符合预期的结果

2.当量化精度损失超过大,如何调优?

  1. 使用 model_profiler() 这个集成接口,生成压缩包。
  2. 检查是否配置高精度输出、是否存在未融合的算子、是否共享 op、是否算子分布过大 int8 兜不住?
  • 注意:使用 debug 集成接口时,要保证浮点模型训练到位,并传入真实数据

3.多任务模型的精度调优建议

  1. qat 调优策略和常规模型一样,ptq+qat
  2. 如果只有一个 head 精度有损失,可以固定其他部分,单独使用这个 head 的数据做 calib

4.calib 和 qat 流程的正确衔接

calib:

    #加载浮点模型权重model.load_state_dict(torch.load("output/toy_experiments/model_moderate_best_soft_float_131892.pth"))set_march(March.BAYES)#校准配置calib_model = prepare_qat_fx(model,{"":default_calib_8bit_fake_quant_qconfig,"module_name":...}).to(device)calib_model.to(device)#校准需要全程开启eval()状态calib_model.eval()#校准的训练阶段,设置伪量化节点模式为 CALIBRATIONset_fake_quantize(calib_model, FakeQuantState.CALIBRATION)train(cfg, calib_model, device, distributed)#校准的评测阶段,设置伪量化节点的模式为 VALIDATIONset_fake_quantize(calib_model, FakeQuantState.VALIDATION)#加载校准的模型权重calib_model.load_state_dict(torch.load("output/toy_experiments/model_moderate_best_soft_calib_118633.pth"))#测试校准的精度run_test(cfg, calib_model, vis=args.vis, eval_score_iou=args.eval_score_iou, eval_all_depths=args.eval_all_depths) # 11.8650

qat:

set_march(March.BAYES)qat_model = prepare_qat_fx(model,{"":default_qat_8bit_fake_quant_qconfig,"module_name":'''}).to(device)qat_model.to(device)#加载校准模型权重qat_model.load_state_dict(torch.load("output/toy_experiments/model_moderate_best_soft_calib_118633.pth"))#训练阶段,保证模型处于model.train()状态,这样伪量化节点也处于qat模式train(cfg, qat_model, device, distributed)

5.检查 conv 高精度输出

方式 1:查看 qconfig_info.txt,重点关注 DeQuantStub 附近的 conv 是不是 float32 输出

qconfig_info.txt

方式 2:打印 qat_model 的最后一层,查看该层是否有 (activation_post_process): FakeQuantize

高精度的 conv:

  (1): ConvModule2d((0): Conv2d(64, 3, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)(weight_fake_quant): FakeQuantize(fake_quant_enabled=tensor([1], dtype=torch.uint8), observer_enabled=tensor([1], dtype=torch.uint8),            quant_min=-128, quant_max=127, dtype=qint8, qscheme=torch.per_channel_symmetric, ch_axis=0,         scale=tensor([1., 1., 1.]), zero_point=tensor([0, 0, 0])(activation_post_process): MovingAveragePerChannelMinMaxObserver(min_val=tensor([]), max_val=tensor([])))))
)

int8 的 conv

  (0): ConvModule2d((0): ConvReLU2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)(weight_fake_quant): FakeQuantize(fake_quant_enabled=tensor([1], dtype=torch.uint8), observer_enabled=tensor([1], dtype=torch.uint8),            quant_min=-128, quant_max=127, dtype=qint8, qscheme=torch.per_channel_symmetric, ch_axis=0,         scale=tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]), zero_point=tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])(activation_post_process): MovingAveragePerChannelMinMaxObserver(min_val=tensor([]), max_val=tensor([])))(activation_post_process): FakeQuantize(fake_quant_enabled=tensor([1], dtype=torch.uint8), observer_enabled=tensor([1], dtype=torch.uint8),            quant_min=-128, quant_max=127, dtype=qint8, qscheme=torch.per_tensor_symmetric, ch_axis=-1,         scale=tensor([1.]), zero_point=tensor([0])(activation_post_process): MovingAverageMinMaxObserver(min_val=tensor([]), max_val=tensor([]))))

6.检查共享 op

打开 qconfig_info.txt,后面标有(n)的就是共享的

特殊情况:layernorm 在 QAT 阶段是多个小量化算子拼接而成,module 的重复调用,也会产生大量 op 共享的问题

解决办法: 将 layernorm 替换为 batchnorm,测试了 float 精度,没有下降。

7.检查未融合的算子

打开 qconfig_info.txt,全局搜 BatchNorm2d 和 ReLU,如果前面有 conv,那就是没做算子融合

可以融合的算子:

  • conv+bn
  • conv+relu
  • conv+add
  • conv+bn+relu
  • conv+bn+add
  • conv+bn+relu+add

8.检查数据分布特别大的算子

打开 float 模型的统计量分布,一般是 model0_statistic.txt

有两个表,第一个表是按模型结构排列的;第二个表是按数据分布范围排列的

拖到第二个表,看前几行是那些 op

可以看到很多 conv 的分布很异常,使用的是 int8 量化

解决办法:

  1. 检查这些 conv 后面是否有 bn,添加 bn 后,数据能收敛一些
  2. 如果结构上已经加了 bn,数据分布还大,可以配置 int16 量化
  • int16 调这两个接口,default_qat_16bit_fake_quant_qconfig 和 default_calib_16bit_fake_quant_qconfig
  • 中间算子的写法和高精度输出类似 model.xx.qconfig = default_qat_16bit_fake_quant_qconfig ()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/834046.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Tomcat Windows 服务 JVM 内存参数设置

Tomcat 在 Windows 平台上启动服务的方式是 Commons Daemon,JVM 的启动参数可以有多种设置方法。本文介绍 Commons Daemon 的大致组成和参数设置方法。 Commons Daemon 由两部分组成。 一是由 C 语言开发负责和操作系统交互的平台相关程序,在 Windows 上平台相关部分是 procr…

FileZilla的安装配置和使用教程

一、FileZilla简介 FileZilla是一款免费开源的FTP软件,有以下优点:非常易于使用,且跨平台(Linux、Windows、MacOs等) 支持FTP、FTPS、SFTP等多种文件传输协议 支持断点续传 支持建立多个标签(标签为一个FTP连接界面)同时工作 支持远程查找文件 自带功能强大的站点管理和…

云行 | 金陵古都焕发数智活力,天翼云为南京创新发展注入新动能!

10月29日,以“国云智算聚金陵 自主可控铸新基”为主题的天翼云中国行江苏南京站活动圆满举办。南京市数据局领导、中国电信江苏分公司领导、客户及合作伙伴代表莅临现场,共商转型发展新机遇,共创智慧江苏新未来。会上举行了天翼云全栈自主可控能力升级计划、南京城市算力调度…

10月回顾 | Apache SeaTunnel社区动态与进展一览

各位热爱 Apache SeaTunnel 的小伙伴们,社区10月份月报来啦,请查收! 这里将记录Apache SeaTunne社区每月动态和进展,欢迎关注。 月度Merge之星 感谢以下小伙伴上个月为 Apache SeaTunnel 所做的精彩贡献(排名不分先后):@Hisoka-X,@prclin,@JohnTeslaa,@happyboy1024,@ji…

微短剧出海新攻略:多语言翻译提效,窄带高清降本,合规之下畅享极致播放

短剧出海「 全能力解锁 」快速轻装,一站出海。 短剧出海作为近两年的蓝海赛道,发展潜力惊人,据机构报告数据显示,2023国内短剧市场规模达53亿美元,而海外短剧市场规模高达650亿美元,全球市场发展潜力巨大。 想要在规模激增的全球内容市场中稳健出海,技术可以提效降本,…

井底车场人员进入识别智慧矿山一体机罐笼乘坐人员超限识别重塑矿山作业模式,提升管理效能

在矿山行业,安全始终是最重要的议题之一。随着科技的进步,智能化设备的应用已经成为提升矿山安全管理水平的关键。智慧矿山一体机,作为新一代的智能化解决方案,正是在这样的背景下应运而生。它不仅是一款设备,更是一个全面整合的安全管理系统,旨在通过前沿的人工智能技术…

使用 JuiceFS 快照功能实现数据库发布与端到端测试

今天的博客来自 JuiceFS 云服务用户 Jerry,他们通过使用 JuiceFS snapshot 功能,创新性地实现了数据的版本控制。Jerry,是一家位于北美的科技公司,利用人工智能和机器学习技术,简化用户购买汽车和家庭保险的比较及购买流程。在软件开发领域,严格的测试和受控发布已经成为…

xshell免费版下载安装

1.官网免费的许可证 2.下载地址https://www.xshell.com/zh/free-for-home-school/ 3.下载完正常安装即可,可以直接使用邮箱进行注册,没有限制

多线程篇线程相关知识

一、线程状态 线程是 cpu 任务调度的最小执行单位,每个线程拥有自己独立的程序计数器、虚拟机栈、本地方法栈。线程状态包括:创建、就绪、运行、阻塞、死亡。二、线程状态切换三、阻塞唤醒过程 阻塞 以下三个方法的调用都会使当前线程阻塞,该线程将会被放置到对该 Object 的…

打造高效电商团队:项目管理工具选对了吗?

在竞争日益激烈的电商行业,商家和运营团队需要面对多维度的挑战——从活动策划、产品上架,到物流协调和售后服务,每个环节都需要精确管理和无缝衔接。如何在高强度的竞争中脱颖而出,实现团队协作效率的提升?项目管理工具正逐渐成为电商企业的必备利器,助力团队优化流程、…

人员背带夹佩戴识别智慧矿山一体机违规抽烟识别软硬一体化矿山智能解决方案

在当今这个快速发展的数字化时代,智慧矿山的建设已经成为矿业领域转型升级的重要方向。为了实现矿山的智能化、自动化和信息化提出了一套全面的解决方案,人员背带夹佩戴识别智慧矿山一体机旨在通过技术创新和系统集成,打造一个高效、安全、环保的智慧矿山环境。以下是对智慧…

数据采集第四次作业

代码链接:第四次数据采集实践作业码云链接 作业① 1 作业要求熟练掌握 Selenium 查找HTML元素、爬取Ajax网页数据、等待HTML元素等内容。 使用Selenium框架+ MySQL数据库存储技术路线爬取“沪深A股”、“上证A股”、“深证A股”3个板块的股票数据信息。 候选网站:东方财富网:…