理论前瞻

Posted on Jun.3, 2021


传统方法的前言中,我们提到: 在性能方面上,DL方法好于非DL方法,在速度上,DL方法有GPU加成可能快于非DL方法。 我印象中,很多传统方法一直有种想把方法写成GPU版本的倾向。在工业界大数据的加持下,DL方法的鲁棒性也能更好提升。 传统方法虽具有可解释性,但相对而言吃力不讨好,DL方法则全方面碾压。所以如何利用好DL,是更值得探究的方向。 然而,距离落地,DL还不是那么完美的,除了工程技巧,一些前沿理论可以助我们打造更好的模型。

剪枝

  • 背景:
  • 
    
      

嫁接

  • 出发点
  • 一些失活的卷积限制了网络的潜能。从这个现象出发,剪枝Pruning移除了这些失活的卷积,来提升效率。
    而嫁接Grafting则重新激活它们,来提升性能。
  • tips
  • relu会造成很多梯度始终为0的核,leaky_relu则不会。但leaky_relu同样没法避免出现一些失活的卷积。嫁接这时就能起效果。

权重平均

  • 思想溯源: 网络插值,从EMA到模型汤
  •   low-level:
      • 2018年、2019年,ESRGAN的团队同时提出网络插值,用于结合不同优化目标下的模型权重。
      • 2019年、2020年,网络插值被运用在重要赛事中,用于结合不同ckp的权重,旨在提升模型泛化性能。
      Overall:
      • 2000年,BMA(Bayesian Model Averaging)用于以模型为最优成员的概率的先验将各模型进行揉合从而降低风险。
      • 2017年,吴恩达就在课上讲EMA(指数移动平均)对模型的参数做平均,以求提高测试指标并增加模型鲁棒。
      • 2018年,有理论支撑,权值平滑能让测试误差落在更优平面。该思想更是能在训练中使用,成为SWA技术。
      • 2019年,PyTorch开始在torchcontrib库支持swa。该技术也在Kaggle赛事中一展头角。
      • 2021年,NIPS一篇论文提出SWA Densly。
      • 2022年,谷歌提出“模型汤”,核心方法是Greedy soup:首先将所有模型按照验证集上准确度降序排列,然后逐个增加模型来进行权重平均,只有当得到的平均模型效果有提升时才考虑将当前的模型加入进来,这是一种简单的贪心策略
    区别:SWA是取不同epoch的模型,跨度更大。EMA是滑动取连续的iteration的模型参数。
    EMA t时刻变量v的滑动平均值大致等于过去1 / ( 1 − β )个时刻v值的平均。即,
    如果β = 0.99,则大致等于过去100个v值的平均。ema占内存少,不需要保存过去100个历史v值,就能够估计其均值。
    对神经网络边的权重 weights 使用滑动平均,得到对应的影子变量shadow_weights。在训练过程仍然使用原来不带滑动平均的权重 weights,
    以得到 weights 下一步更新的值,进而求下一步 weights 的影子变量 shadow_weights。之后在测试过程中使用shadow_weights 来代替
    weights 作为神经网络边的权重,这样在测试数据上效果更好。
    注:使用EMA可能对BN层有帮助。减少BN层对batchsize的敏感。
    有好的预训练模型,可以将use_num_updates关掉,或者至少将其初始数值设大。不然会出现性能一开始骤降的情形。

像素级动态卷积核

  • 思想溯源:从localconv到KPN
  •       • 1990年,各向异性扩散算法就被提出。
          • 2014年,论文DeepFace提出在网络深层使用3层localconv,能够提升人脸识别精度。
          • 2018年,论文CSPN循环使用localconv(避免学习新的filter bank),可以解释为空间传播网络。
          • 上述两个工作分别是high-level,mid-level vision的。
          • low-level vision:
          • 2016年,Google提出RAISR,它的过滤器通过高效的散列机制选择,即像素级的索引从filter bucket(or LUT)中选取过滤器,起到了空域自适应的效果。详细可见论文解读。
          • Kernel Prediction Networks:
          • 2017年,KPN被用于蒙特卡洛渲染去噪。
          • 2017年,两篇KPN用于视频插帧的论文被发表于顶会(相同作者)。前一篇论文由于插帧/运动估计问题的特性,kernel特别大(41x41),只能逐像素处理。后一篇因此只预测1D的kernel,41x1和1x41,再外积得到完整的2D kernel。
          • 2018年,KPN被用于超分,只不过末层学输出通道r^2的kernel,可以pixel-shuffle上采样,做为skip-connection分支(KPN)。同时也共享参数,末层另一个分支学残差(非KPN)。
          • 2018年,加州伯克利和谷歌将KPN用在Burst Denoising上。本质和localconv没有区别。提出的loss可以引导KPN既做align又做去噪,最后average。
          • 2019年,张磊老师组将KPN用于RealSR,解决blur kernel会跟随物距而改变的问题。因为kernel尺寸太大,复杂度增加很大,kernel尺寸太小,效果不好。所以该方法使用了金字塔结构,最后几层pixel-shuffle因子不一样,产生不同的kernel,作用于不同下采样倍数的图像上。
          • 2019年,更轻量的PAC(pixel-adaptive convolution)被提出。认为权重共享的负面影响是使得content-agnostic. 这些缺点可以通过学习大量滤波器来尝试捕获图像和像素变化来解决。 然而,这增加了参数的数量,需要更大的内存占用和大量的标注数据。 另一种方法是在网络内使用content-adaptive卷积。
              1. 一类技术使传统的图像自适应滤波器可微分,例如双边滤波、引导滤波和各向异性扩散,并将它们用作 CNN 的层。 这些内容自适应层通常设计用于增强 CNN 结果,但不能替代标准卷积。
              2. 另一类内容自适应网络涉及使用单独的子网络学习特定位置的kernel,该子网络预测每个像素的卷积滤波器权重。 这些被称为“动态滤波网络”(DFN),也称为交叉卷积(cross-convolution)或核预测网络(KPN),并且已被证明在几个计算机视觉任务中的有效性。尽管 DFN 是通用的并且可以用作标准卷积层的替代品,但这种核预测策略很难扩展到具有大量滤波器组的整个网络。
        

不平衡损失

  • 思想溯源: 从Topk到Focal loss
  •   • 问题存在:①样本类别不平衡 ②样本难度不对等,简单样本支配训练,淹没(overwhelm training)评估损失的有效性。
      • ~2017年,
        1. 按照class比例加权重:最常用处理类别不平衡问题的方式
        2. OHEM:只保留loss最高的那些样本,完全忽略掉简单样本
        3. OHEM+按class比例sample:在前者基础上,再保证正负样本的比例
        3的结果比2要更差,其实这也表明,其实正负样本不平衡不是最核心的因素,而是由这个因素导出的easy example dominant的问题。
      • 至今,Topk loss仍然被广泛用于kaggle比赛中。
      • 2017年,Loss max-pooling被提出,主要思想是:
        1. 通过pixel weighting functions自适应地对每个像素的contribution(实际展现的loss)进行re-weighting
        2. 通过普通的max-pooling在pixel-loss level上对pixel weighting function取最大
        3. 而这个最大值是传统loss(即每个像素损失的权重是相等的)的上界
      • 同年,何恺明团队提出Focal loss。
        硬方案:OHEM
        Focal loss可以看作soft OHEM。至于OHEM本身不能单独很好解决“简单样本支配”问题,私以为是focal loss可以"看全局",而OHEM直接把easy ecample忽略使得许多pixel没有得到充分训练。
        软方案(sample reweighting):Focal loss
        软性的难例加权,focal loss的思路是聚焦难例,并且自适应的削弱易例对最终的loss的贡献。
      

Out防过拟合

  • 思想溯源: 从Dropout到Swapout
  •       2013年,DropConnect [6]:只在连接处扔,神经元不扔。
          2014年,Dropout [1]:完全随机扔
          2015年,SpatialDropout [2]:按channel随机扔
          2016年,Stochastic Depth [3]:按res block随机扔
          2017年,Cutout [5]:在input层按spatial块随机扔
          2018年,DropBlock [4]:每个feature map上按spatial块随机扔
          由此,有的论文就是这么来的。
        

蒸馏学习、互学习

各种归一化

  • GroupNorm
  • 记住GN,就记得它的两个特例LN和IN了。
            将channel方向分group,然后每个group内做归一化,算(C//G)HW的均值;这样与batchsize无关,不受其约束。
            事实上,GN的极端情况就是LN和IN,分别对应G等于1和G等于C,作者在论文中给出G设为32较好.
    
            注:对于LN其实是有两种说法,分别是1. paper or PyTorch style 2. transformer or ConvNext style。不要混淆了:https://github.com/apple/ml-cvnets/issues/34
    
            有方法提出了PN层,适用于超分辨率任务,因为它在空间上是不均一(inhomogeneous)的。
            BN是(B,HW)维度,GN是(G(ltC),HW)维度,LN是(C,HW)维度,IN是(1,HW)维度,PN是(C)维度。
          
            当Conv bias遇上Norm:
            对于Norm之前的卷积是否使用偏置,也是有讲究的。
            如果bias的作用范围与norm的作用范围一致,那conv bias就没有意义。
            MMDetection仓库就有友情提醒,当用户在使用Batch Norm或者Instance Norm时,MMCV的提示是非常贴心的。
            使用Layer Norm或者Group Norm(num_groups≠C)时,MMCV则不必抛出这个warning
            巧记
            HxW 看作一个实例 instance , 加一维 C 就看成 layter norm , 要是加一维 B 就是batch norm , 加部分C 就看做group。
          

归一化和标准化

  • 用途
  •         normalization和standardization是差不多的,都是把数据进行前处理,从而使数值都落入到统一的数值范围,从而在建模过程中,各个特征量没差别对待。
            normalization一般是把数据限定在需要的范围,比如一般都是[0, 1],从而消除了数据量纲对建模的影响。standardization 一般是指将数据正态化,使平均值0方差为1.
          
  • 方法
  •         1). 最大最小值normalization: x'=(x-min)/(max-min). 这种方法的本质还是线性变换,简单直接。缺点就是新数据的加入,可能会因数值范围的扩大需要重新regularization。2). 对数归一化:x'=log10(x)/log10(xmax)或者log10(x)。推荐第一种,除以最大值,这样使数据落到[0, 1]区间
            3). 反正切归一化。x'=2atan(x)/pi。能把数据投影到[-1, 1]区间。
            4). zero mean normalization归一化,也是standardization. x'=(x-mean)/std.
            有无normalization,模型的学习曲线是不一样的,甚至会收敛结果不一样。
          

各种激活函数

  • Swish(nn.SiLU)
  •         没有充分的证据表明 某种激活函数 总是比 另一种激活函数 好。
            不过Swish的论文包含了一个有趣的讨论,关于什么激活函数是好的。
            作者指出,Swish工作得如此出色的原因是它的上无界,下有界,非单调,平滑。你可能已经注意到GELU也具有所有这些性质,我们稍后将讨论的最后一次激活函数也是这样。看来这就是激活研究的发展方向。
            激活函数的选择
            介绍
            Swish vs. Mish
         

优化器与正则化

          正则化技术有很多,比如提前终止、数据增强、参数绑定、参数共享、对抗训练、切面距离、正切传播等,我们简单介绍几种常用的:
          1.Early stop
    
          在模型训练过程中,提前终止。这里可以根据具体指标设置early stop的条件,比如可以是loss的大小,或者acc/f1等值的epoch之间的大小对比。
    
          2.More data
    
          用更多的数据集。增加样本也是一种解决方案,根据不同场景和数据有不同的数据增强方法。
    
          3.正则化
    
          常用的有L1&L2-norm、Max-norm(即clip_grad_norm)
    
          4.Dropout
    
          以一定的概率使某些神经元停止工作
          (使用该方法,网络结果、学习率都要相应调整:https://blog.csdn.net/qq_43409114/article/details/105931830)
    
          4'.R-Drop
          所谓“Dropout两次”,只需要将样本重复地输入到模型,然后计算相应的loss就行了。从结果上来看,就是希望Dropout对模型结果不会有太大影响,也就是模型输出对Dropout是鲁棒的。
    
          #训练过程上下文
          ce = CrossEntropyLoss(reduction='none')
          kld = nn.KLDivLoss(reduction='none')
          logits1 = model(input)
          logits2 = model(input)
          #下面是训练过程中对比学习的核心实现!!!!
          kl_weight = 0.5 #对比loss权重
          ce_loss = (ce(logits1, target) + ce(logits2, target)) / 2
          kl_1 = kld(F.log_softmax(logits1, dim=-1), F.softmax(logits2, dim=-1)).sum(-1)
          kl_2 = kld(F.log_softmax(logits2, dim=-1), F.softmax(logits1, dim=-1)).sum(-1)
          loss = ce_loss + kl_weight * (kl_1 + kl_2) / 2
    
          5.BatchNorm
    
          对神经元作归一化
    
        
          torch.optim集成了很多优化器,如SGD,Adadelta,Adam,Adagrad,RMSprop等,这些优化器中有一个参数weight_decay,用于指定权值衰减率,相当于L2正则化中的λ参数,
          注意torch.optim集成的优化器只有L2正则化方法,api中参数weight_decay 的解析是:weight_decay (float, optional): weight decay (L2 penalty) (default: 0),
          这里可以看出其weight_decay就是正则化项的作用。可以如下设置L2正则化:
          optimizer = optim.Adam(model.parameters(),lr=0.001,weight_decay=0.01)
    
          自定义正则化类:
          https://blog.csdn.net/zhang2010hao/article/details/89339327
          自定义正交规范化:
          https://zhuanlan.zhihu.com/p/98873800
    
          注:正交规范化加loss的方式可以,但直接构造或许不行。神经网络是极其复杂的。即使看起来似乎很好的方法也可能会失败,因为当大多数人有一个想法时,他们只是想到了网络最终训练好的状态(就像我一样,我想要矩阵的列是正交的)。但如果你想到网络必须通过一个路径才能优化,那么大多数好想法就立马显得蠢笨了。
        
    Adam与weight decay

Adam优化器的参数

    Adam各个参数分析:params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,weight_decay=0,amsgrad=False
    参考:
    https://blog.csdn.net/nyist_yangguang/article/details/121603917

    betas1常用0.9和0.5
  

对抗训练

  • NLP中的对抗训练
  • 对抗样本和对抗训练
  • Min-Max公式
  • Madry在2018年的ICLR论文Towards Deep Learning Models Resistant to Adversarial Attacks中总结了之前的工作。总的来说,对抗训练可以统一写成如下格式:

    $$\min_{\theta}\mathbb{E}_{(x,y)\sim\mathcal{D}}\left[\max_{\Delta x\in\Omega}L(x+\Delta x, y;\theta)\right]$$

    其中\(\mathcal{D}\)代表数据集,\(x\)代表输入,\(y\)代表标签,\(\theta\)是模型参数,\(L(x,y;\theta)\)是单个样本的loss,\(\Delta x\)是扰动,\(\Omega\)是扰动空间。这个式子可以分步理解如下:

    1. 往\(x\)里注入扰动\(\Delta x\),\(\Delta x\)的目标是让\(L(x+\Delta x, y;\theta)\)越大越好,也就是说尽可能让现有模型的预测出错
    2. 当然\(\Delta x\)也不是无约束的,它不能太大,否则达不到"看起来几乎一样"的效果,所以\(\Delta x\)要满足一定的约束,常规的约束是\(||\Delta x||\leq \epsilon\),其中\(\epsilon\)是一个常数
    3. 每个样本都构造出对抗样本\(x+\Delta x\)之后,用\((x+\Delta,y)\)作为数据去最小化loss来更新参数\(\theta\)(梯度下降)
    4. 反复交替执行1、2、3步

经验

  • 很多时候,nn的设计,我们没法知道我们可以做什么一定可以提分,我们只知道我们绝对不可以做什么避免掉分以及我们应该可能可以做什么提分,这是调整nn的时候的一个深刻的感受