YOLO_v4技巧分解-Cross-Iteration Batch Normalization

在這裏插入圖片描述
對msra的工作都比較關注,最近剛好看到了這篇對傳統bn進行改進的論文。

論文地址:https://arxiv.org/abs/2002.05712
github地址:https://github.com/Howal/Cross-iterationBatchNorm
openreview:https://openreview.net/forum?id=BylJUTEKvB

作者應該是投ICLR杯具了,不過個人覺得比較值得少花些時間讀一讀。

論文開門見山的指出 batchsize的大小直接影響了BN的效果,見圖1中的綠色線,batchsize在小於16後的分類準確率急劇下降。作者在本文提出了一種叫做cross-iteration BN的方法,通過泰勒多項式去估計幾個連續batch的統計參數,可以很大程度緩解此問題,如圖1中的藍色線,在batchsize逐漸變小時,效果依然穩定,並且accuracy始終高於GN的效果。
在這裏插入圖片描述
(論文必備圖,一圖告訴你我有多nb)

1. Revisiting Batch Normalization

從實現的角度來說,BN對特徵進行了一種白化操作,可以減少internal covariate shift,具體的可以查看原文。
在這裏插入圖片描述
在這裏插入圖片描述
這裏作爲對比可以回憶下幾種比較常見的歸一化方法
BatchNormalization,LayerNormization,InstanceNormalization,GroupNormalization
GN的paper中給了一張骨灰級清楚明瞭的圖~
在這裏插入圖片描述
BN的計算中,一個channel就是一個特徵N,H,W三個維度進行相關統計參數的計算,pytorch代碼如下,其他幾種歸一化方法只要在BN代碼中稍加修改即可。(注意四個參數的維度)

import torch
from torch import nn


class BatchNorm(nn.Module):
    '''custom implement batch normalization with autograd by Antinomy
    '''

    def __init__(self, num_features):
        super(BatchNorm, self).__init__()
        # auxiliary parameters
        self.num_features = num_features
        self.eps = 1e-5
        self.momentum = 0.1
        # hyper paramaters
        self.gamma = nn.Parameter(torch.Tensor(self.num_features), requires_grad=True)
        self.beta = nn.Parameter(torch.Tensor(self.num_features), requires_grad=True)      
        # moving_averge
        self.moving_mean = torch.zeros(self.num_features)
        self.moving_var = torch.ones(self.num_features)
        self.reset_parameters()

    def reset_parameters(self):
        nn.init.uniform_(self.gamma)
        nn.init.zeros_(self.beta)
        nn.init.ones_(self.moving_var)
        nn.init.zeros_(self.moving_mean)

    def forward(self, X):
        assert len(X.shape) in (2, 4)
        if X.device.type != 'cpu':
            self.moving_mean = self.moving_mean.cuda()
            self.moving_var = self.moving_var.cuda()
        Y, self.moving_mean, self.moving_var = batch_norm(X, self.gamma, self.beta,
                                         self.moving_mean, self.moving_var,
                                         self.training, self.eps, self.momentum)
        return Y


def batch_norm(X, gamma, beta, moving_mean, moving_var, is_training=True, eps=1e-5, momentum=0.9,):

    if len(X.shape) == 2:
        mu = torch.mean(X, dim=0)
        var = torch.mean((X - mu) ** 2, dim=0)
        if is_training:
            X_hat = (X - mu) / torch.sqrt(var + eps)
            moving_mean = momentum * moving_mean + (1.0 - momentum) * mu
            moving_var = momentum * moving_var + (1.0 - momentum) * var
        else:
            X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps)
        out = gamma * X_hat + beta

    elif len(X.shape) == 4:
        shape_2d = (1, X.shape[1], 1, 1)
        mu = torch.mean(X, dim=(0, 2, 3)).view(shape_2d)
        var = torch.mean(
            (X - mu) ** 2, dim=(0, 2, 3)).view(shape_2d) # biased
        X_hat = (X - mu) / torch.sqrt(var + eps)
        if is_training:
            X_hat = (X - mu) / torch.sqrt(var + eps)
            moving_mean = momentum * moving_mean.view(shape_2d) + (1.0 - momentum) * mu
            moving_var = momentum * moving_var.view(shape_2d) + (1.0 - momentum) * var
        else:
            X_hat = (X - moving_mean.view(shape_2d)) / torch.sqrt(moving_var.view(shape_2d) + eps)

        out = gamma.view(shape_2d) * X_hat + beta.view(shape_2d)

    return out, moving_mean, moving_var

2. Leveraging Statistics from Previous Iterations

既然導致BN效果差的原因是batch太小了,最直白的想法當然是通過擴大用來計算統計信息的樣本量解決問題。SyncBN(李沐老師的介紹)就是這個原理,將多張卡上的樣本同步計算,一般來講batchsize大小超過16 後,bn效果基本不會收到影響。這種方法的缺點就是需要不同卡上的數據進行同步以及devices間的數據傳遞。
本文中的CBN換了一個思路,通過計算幾個相近的batch的近似統計參數來解決該問題。

首先作者指出:由於梯度下降機制,模型訓練的過程中相近的幾個iter所對應的模型參數的變化是平滑的(smoothly)
假設當前iter index爲tt,那麼tτt-\tau中的統計量在tt時的近似量可以見公式5,6:

在這裏插入圖片描述
其中 θ\theta代表模型的參數,tt標示計算的是第幾個iter,也就是算的batch的idx,μ\muν\nu就是BN中的兩個統計量。這裏其實是f(θt)f(\theta_t)θθttau\theta-\theta_{t-tau}處泰勒多項式展開,其中**O(θtθtτ2)O(||\theta_t-\theta_{t-\tau}||^2)**是θtθtτ||\theta_t-\theta_{t-\tau}||的二階無窮小量。在實際的計算時會將高階無窮小量忽略掉。

在公式5,6中,需要計算μtτ(θtτ)θtτ\frac{\partial\mu_{t-\tau}(\theta_{t-\tau})}{\partial\theta_{t-\tau}}νtτ(θtτ)θtτ\frac{\partial\nu_{t-\tau}(\theta_{t-\tau})}{\partial\theta_{t-\tau}},加入我們要計算第ll層bn的這兩個參數,那麼在ll層之前的所有參數其實都要進行計算,這樣子計算量就有些太大了,原文說:

Only when r=lr = l can these gradients be derived in closed form efficiently.

作者在這裏又發現:隨着layer index rr 的變小(rl)(r\leq l),μtlθtr\frac{\partial\mu_{t}^{l}}{\partial\theta_{t}^{r}}νtlθtr\frac{\partial\nu_{t}^{l}}{\partial\theta_{t}^{r}}迅速降低,如下圖反映了倒數三層的梯度比例:
在這裏插入圖片描述也就是說針對公式5,6我們只要算出統計量對應其所在的層的網絡參數的梯度就可以了,這一步簡化是非常重要的。
在這裏插入圖片描述
到這裏,我們已經計算除了iter tτt-\tau在第tt個iter是參數下的統計量近似。論文中給出了一張圖
在這裏插入圖片描述
有了上面的近似結果,我們來看一下CBN是如何來進行Normalize的。同上面bn的代碼所示,我們怎麼去計算CBN相關的統計量,以及執行前向操作。
在這裏插入圖片描述

  • 對於均值,只需要去計算iter[tτ,t][t-\tau,t]的平均數。
  • νt,kl(θt)\overline{\nu}_{t,k}^l(\theta_t)定義爲νtτl(θt)\nu_{t-\tau}^l(\theta_t)μtτl(θ)2\mu_{t-\tau}^l(\theta)^2中的最大值,再將這個值針對k個iter求平均。文中說這樣子才能讓νt,kl(θt)\overline{\nu}_{t,k}^l(\theta_t)有意義的統計信息。

現在我們已經有了歸一化所需要的所有的統計數據,針對第ll層的特徵xtil(θt)x_{t-i}^l(\theta_t),他的歸一化特徵爲:

x^t,il(θt)=xt,il(θt)μt,kl(θt)σt,kl(θt)2+ε\hat{x}_{t,i}^l(\theta_t)=\frac{x_{t,i}^l(\theta_t)-\overline{\mu}_{t,k}^l(\theta_t)}{\sqrt{\overline\sigma_{t,k}^l(\theta_t)^2+\varepsilon}}

可以看出歸一化過程與原始bn算法是一致的。也就是,在做inference過程時,CBN與BN的計算完全相同,所以CBN不增加前向時間
這裏值得關注的一個問題,相比於BN,CBN的計算量和memory多用了多少,從上面的分析可以看出,多出來的計算都是在求偏導的那一步,而memory主要是在偏導以及前幾個iter的μ.ν,θ\mu.\nu,\theta等幾個量的存儲與訪問。論文指出這些相對於網絡本身的計算量與memory佔用來比都是微乎其微的。在這裏插入圖片描述
超參數方面,CBN多了一個window size,實驗中作者設定爲8。並且需要在網絡訓練初期要用較小的窗大小,隨着網絡的訓練,模型參數也會越來約穩定,這是後再用較大的窗大小可以獲得更好的結果。具體細節見論文。

3. Experiment

1. Comparison of feature normalization methods

在imagenet數據bs=32時,對不同的normalize 方式進行對比,可見在大bs的setting下,CBN是唯一一個能夠與BN效果一致的歸一化方法。
在這裏插入圖片描述

2. Sensitivity to batch size

在這裏插入圖片描述
結論:BN與BRN隨着bs減小效果下降明顯,GN和CBN相對而言在較小的bs下依然可以保持較高的準確率,但CBN的top1 acc 比GN高出0.9%.同時在bs=1時候GN和CBN依然可以進行訓練,這是openreview中reviewer比較關心的一個問題,也能看出GN真的還是比較nb的。

  1. Detection and Segmentation

在這裏插入圖片描述
這一部分中作者將backbone和box head 分開進行實驗,感覺主要原因是爲了方便加載預訓練模型,固定backbone的bn不需要在imagenet上重新訓練,項目中應用起來會更方便。

  1. 在固定backbone部分的BN部分時,box head 選擇不同的normalize方式,可以看到CBN能夠獲得與GN,syncBN類似的結果,同時結果要明顯好於BN。
  2. 在backbone和boxhead 都進行normalize的替換後,CBN的結果會明顯好於BN但是會比GN和syncBN結果差一些,作者認爲是accumulation of approximation error(估計誤差累積)造成的。

在論文的最後,作者給出了求兩個統計量相對於對應層參數的高效計算方式,大致的思路時計算統計量的第j個維度梯度只跟第j個特徵圖相關,這裏可以省去很大的一部分計算量,完全弄明白後再寫這一段。

4. Conclusion

針對BN在small batchsize regime這個問題作者提出了利用相鄰iter的樣本的方式來提高統計參數的準確性,進而提升模型的效果。針對這個問題LN,IN,GN是通過修改BN中用來計算參數的維度來解決這個問題,同時GN取得了不錯的效果;
syncBN是通過將多個GPU上的數據共同利用來擴大batchsize解決該問題。CBN屬於利用不同的iter數據來變相擴大batchsize從而改進模型的效果。理論分析清楚明瞭,實驗充分,方法在small batchsize regime這個問題上確實有效。

回過頭,我們發現SyncBN效果在各個問題上效果都很好,同時不會引入超參數,不同的深度學習框架也有相應支持。本文相對於SyncBN的優勢在哪裏?或者是說在什麼問題上syncBN不能夠使用來突出CBN的必要性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章