• 自己对 RepVGG 的一点理解


    RepVGG

    论文地址:https://arxiv.org/abs/2101.03697

    代码:GitHub - DingXiaoH/RepVGG: RepVGG: Making VGG-style ConvNets Great Again

    一、为啥提出这个论文

    1.作者指出,现有的很多模型其比较复杂,多分枝占内存,推理速度不快。基于这些,作者提出了自己的创新点:RepVGG

    (1)推理时没有任何分支结构

    (2)模型推理只使用 3x3 卷积和 ReLuU

    (3)具体的结构是预先定义好的,不需要通过自动搜索,也没有复杂的设计

    作者对比了RepVGG与现有的一些网络差异,也有纯plain网络,但是这样网络精度低于resnet,以及一些 re-parameter 的网络,但是作者的这个对于 trainning plain CovNets 更为关键。(论文中这句话没看懂)原文: Compared to our method, the difference is that they are designed for component-level improvements and used as a drop-in replacement for conv layers in any architecture, while our structural re-parameterization is critical for training plain ConvNets, as shown in Sect. 4.2.

    接着作者说一些硬件还有库对3x3卷积有着很好的加速。

    使用简单网络的三个原因:快,内存占用小,灵活

    作者的网络在训练的时候是: y =x + g(x) + f (x)

    网络结构:

    1. 5个stage,每个stage有 3x3 卷积层,第一个 stage 进行下采样,stride=2

    2. 用一个全局平均池化层+全连接层作为网络的head,用于分类

    后面作者就介绍了 RepVGG的网络构成,以及一些实验。

    自己对每层参数理解:

     

    自己对作者没有跨层操作的理解:

     

    自己对参数的理解:

     

    这篇论文的创新点就是训练的时候有分支,然后推理的时候把训练出来的参数进行合并,然后放到3x3纯卷积中,那这一部分代码主要就是 repvgg.py 中:

    1. def repvgg_model_convert(model:torch.nn.Module, save_path=None, do_copy=True):
    2.    print(model)
    3.    if do_copy:
    4.        model = copy.deepcopy(model) # 先拷贝模型
    5.    for module in model.modules():
    6.        if hasattr(module, 'switch_to_deploy'): # 如果有这个方法,那就进入 switch_to_deploy() 进行转换,这个方法就是核心
    7.            module.switch_to_deploy()
    8.    if save_path is not None:
    9.        torch.save(model.state_dict(), save_path)
    10.    return model

    接下来就是:

    1.    def switch_to_deploy(self):
    2.        if hasattr(self, 'rbr_reparam'):
    3.            return
    4.        kernel, bias = self.get_equivalent_kernel_bias() # 核心就是这个里面是读取 3x3 1x1 identity 里面的权重,然后提取出来给下面的 self.rbr_reparam
    5.        self.rbr_reparam = nn.Conv2d(in_channels=self.rbr_dense.conv.in_channels, out_channels=self.rbr_dense.conv.out_channels,kernel_size=self.rbr_dense.conv.kernel_size, stride=self.rbr_dense.conv.stride,padding=self.rbr_dense.conv.padding, dilation=self.rbr_dense.conv.dilation, groups=self.rbr_dense.conv.groups, bias=True)# 一般bias=False,这里设置了True,因为self.rbr_reparam后面也没有接BathNorm层
    6.        self.rbr_reparam.weight.data = kernel
    7.        self.rbr_reparam.bias.data = bias
    8.        for para in self.parameters():
    9.            para.detach_()
    10.        self.__delattr__('rbr_dense')
    11.        self.__delattr__('rbr_1x1')
    12.        if hasattr(self, 'rbr_identity'):
    13.            self.__delattr__('rbr_identity')
    14.        if hasattr(self, 'id_tensor'):
    15.            self.__delattr__('id_tensor')
    16.        self.deploy = True

    这里是提取参数:

    1.    def get_equivalent_kernel_bias(self):
    2.        kernel3x3, bias3x3 = self._fuse_bn_tensor(self.rbr_dense) #提取参数就这里
    3.        kernel1x1, bias1x1 = self._fuse_bn_tensor(self.rbr_1x1)
    4.        kernelid, biasid = self._fuse_bn_tensor(self.rbr_identity)
    5.        return kernel3x3 + self._pad_1x1_to_3x3_tensor(kernel1x1) + kernelid, bias3x3 + bias1x1 + biasid
    6.    def _pad_1x1_to_3x3_tensor(self, kernel1x1):
    7.        if kernel1x1 is None:
    8.            return 0
    9.        else:
    10.            return torch.nn.functional.pad(kernel1x1, [1,1,1,1])
    11.    def _fuse_bn_tensor(self, branch):
    12.        if branch is None:
    13.            return 0, 0
    14.        if isinstance(branch, nn.Sequential): # 提取卷积中的权重和bn层权重
    15.            kernel = branch.conv.weight
    16.            running_mean = branch.bn.running_mean
    17.            running_var = branch.bn.running_var
    18.            gamma = branch.bn.weight
    19.            beta = branch.bn.bias
    20.            eps = branch.bn.eps
    21.        else:
    22.            assert isinstance(branch, nn.BatchNorm2d)# 这是对identity层
    23.            if not hasattr(self, 'id_tensor'):
    24.                input_dim = self.in_channels // self.groups # 这个网上说根据多GPU更快训练,我个人没看懂有更快
    25.                kernel_value = np.zeros((self.in_channels, input_dim, 3, 3), dtype=np.float32)
    26.                for i in range(self.in_channels):
    27.                    kernel_value[i, i % input_dim, 1, 1] = 1 # 这个是把对应的位置赋值为1,这样值为1,周边的为0,就相当于进行卷积没进行任何改变
    28.                self.id_tensor = torch.from_numpy(kernel_value).to(branch.weight.device)
    29.            kernel = self.id_tensor
    30.            running_mean = branch.running_mean
    31.            running_var = branch.running_var
    32.            gamma = branch.weight
    33.            beta = branch.bias
    34.            eps = branch.eps
    35.        std = (running_var + eps).sqrt()  # 这里和下面一行就是公式,标准流程
    36.        t = (gamma / std).reshape(-1, 1, 1, 1)
    37.        return kernel * t, beta - running_mean * gamma / std

    问题:

    1.怎么理解 “ 同时,单路架构非常快,因为并行度高。 ”?

    参考:

    1.结构重参数化:利用参数转换解耦训练和推理结构 - 知乎 这是原文作者写的

    2.RepVGG论文和代码解析|RepVGG-“白嫖”多分支结构的性能, 让VGG性能超越ResNet|CVPR2021_哔哩哔哩_bilibili b站讲解,讲解的还不错

    3.深度解读:RepVGG - 知乎 深度解读:RepVGG

    4.图解RepVGG - 知乎 图解RepVGG

    5.比ResNet更强的RepVGG代码详解_潮生灬的博客-CSDN博客_repvgg代码 讲解RepVGG代码,讲的还可以

    6.比ResNet更强的RepVGG代码详解_潮生灬的博客-CSDN博客_repvgg代码 这个人讲的也不错,其里面有github地址,github里面也有讲解,总的来说蛮好的

    yolov5 b站讲解,还不知道讲的怎样,没看

    Yolo v5代码详解第二讲BN与Pooling_哔哩哔哩_bilibili

    Pytorch:model.train()和model.eval()用法和区别,以及model.eval()和torch.no_grad()的区别 - 知乎 这个讲解model.train()和model.eval()用法和区别,以及model.eval()和torch.no_grad()的区别 还是不错的,特别是评论

    之前对dim或者axis不是很能理解,如sum(1),知道是按照dim=1或者axis=1进行求和,但是求和后的shape有时就想不通,通过如下例子,辅助理解:

     

    上图是一个shape=(1,3,4,5),按照axis=3进行求和,我们这里理解:当按照axis=3进行求和,那剩下的是什么维度,就是(1,3,5),最后一个维度是5,所以列的维度是5列;可以这么记,用哪个维度,那没用的维度就是sum的shape,

    再如下也是一样的:

    这里是按照axis=0,那结果的维度就是剩下的(3,4,5)

    for module in model.modules(): # 这个就是遍历模型,遍历里面每个模块,先从大的整体,然后遍历里面每个细的
        if hasattr(module, 'switch_to_deploy'):  # 判断模型是否有这个属性,这个属性包括字段、方法    
            print(module)
            
    RepVGG(
      (stage0): RepVGGBlock(
        (nonlinearity): ReLU()
        (se): Identity()
        (rbr_dense): Sequential(
          (conv): Conv2d(3, 48, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn): BatchNorm2d(48, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        )
        (rbr_1x1): Sequential(
          (conv): Conv2d(3, 48, kernel_size=(1, 1), stride=(2, 2), bias=False)
          (bn): BatchNorm2d(48, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        )
      )
    )
    ​
    如上面的,会依次整体打印,然后打印每个子部分,然后再细分下去
      (stage0): RepVGGBlock(
        (nonlinearity): ReLU()
        (se): Identity()
        (rbr_dense): Sequential(
          (conv): Conv2d(3, 48, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn): BatchNorm2d(48, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        )
        (rbr_1x1): Sequential(
          (conv): Conv2d(3, 48, kernel_size=(1, 1), stride=(2, 2), bias=False)
          (bn): BatchNorm2d(48, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        )
      )
    ​
    接着打印:
    ReLU()
    ​
    接着打印:
    Identity()
    ​
    接着打印:
    Sequential(
          (conv): Conv2d(3, 48, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn): BatchNorm2d(48, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        )
    ​
    接着打印:
    Conv2d(3, 48, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
    ​
    接着打印:
    BatchNorm2d(48, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    ​
    

    注意,在cnn中,如果卷积层之后接Bn层,那么一般设置bias为0,因为bias会在下一层BN归一化时减去均值消掉,徒增计算,这也是为什么我们看到很多时候卷积层设置bias,有时候又不设置。

    参考:卷积神经网络卷积层BN层计算原理和卷积BN层融合_MidasKing的博客-CSDN博客_卷积神经网络的bn

  • 相关阅读:
    河南分销小程序开发|三级分销玩法介绍
    (总结一)Halcon基础之寻找目标特征+转正
    代码随想录算法训练营Day41 (day40 休息) | 动态规划(3/17) LeetCode 343. 整数拆分 96.不同的二叉搜索树
    PE格式之PE头部
    现代信号处理——自适应滤波器(卡尔曼滤波)
    Harbor 安装部署
    【毕业设计】基于java+swing+GUI的雷电游戏GUI设计与实现(毕业论文+程序源码)——雷电游戏
    android原生集成Rn项目
    预测宝可梦武力值、分类宝可梦
    千万小心,99%的Java程序员会踩这些坑
  • 原文地址:https://blog.csdn.net/shenjianhua005/article/details/126273567