前言
在上一篇博文RepVGG中,介绍了RepVGG网络。RepVGG 作为一种高效的重参数化网络,通过训练时的多分支结构(3x3卷积、1x1卷积、恒等映射)和推理时的单分支合并,在精度与速度间取得了优秀平衡。然而,其在低精度(如INT8)量化后常出现显著精度损失。
本文将要介绍的QARepVGG(Make RepVGG Greater Again: A Quantization-aware Approach)的提出正是为了解决这一问题。其核心贡献在于基础的Block设计:
引入
文章做了详细的消融实验来一步一步的推理出这种结构,本文在此不多做赘述。只大概提一下:RepVGG其实是由三个单元构成:权重、BN和ReLU。卷积操作一般不会影响权重值的改变,基本服从0~1分布;而根据BN层的公式,会出现一个乘法项,导致方差可能发生改变;另外,如果输入的数值范围很大,经过ReLU也会产生大的方差项,导致量化困难。
因此,QARepVGG去掉了BN层,并在三个分支后新加了一个BN层来将分布改成一个量化友好的分布。
当然,建议读者阅读原论文,好多实验的设计跟分析很透彻。
Demo实现
本文旨在复现一个QARepVGG Block,读者可一键运行:
import torch
import torch.nn as nn
class QARepVGGBlock(nn.Module):
def __init__(self, in_channels, out_channels):
super().__init__()
assert in_channels == out_channels, "输入输出通道必须相同!"
# 分支1:3x3卷积 + BN
self.conv3x3 = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False)
self.bn3x3 = nn.BatchNorm2d(out_channels)
# 分支2:1x1卷积(无BN)
self.conv1x1 = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False)
# 分支3:恒等映射(无BN)
self.identity = nn.Identity() # 直接传递输入
# 合并后的BN层
self.final_bn = nn.BatchNorm2d(out_channels)
# 初始化权重(关键!)
self._init_weights()
def _init_weights(self):