深度學習入門(六):權重的初始值、Batch Normalization、Group Normalization

本文爲《深度學習入門 基於Python的理論與實現》的部分讀書筆記
代碼以及圖片均參考此書

權重的初始值

權重初始值可以設爲0嗎(隨機生成初始值的重要性)

在神經網絡的學習中,權重的初始值特別重要。實際上,設定什麼樣的權重初始值,經常關係到神經網絡的學習能否成功
權值衰減(weight decay)可以抑制過擬合。如果想減小權重的值,一開始就將初始值設爲較小的值纔是正途。實際上,在這之前的權重初始值都是像0.01 * np.random.randn(10, 100)這樣,使用標準差爲0.01 的高斯分佈。
然而,如果將權重初始值設爲0的話,將無法正確進行學習,或者說,將權重初始值設成一樣的值,也無法正確進行學習
這是因爲在誤差反向傳播法中,所有的權重值都會進行相同的更新。比如,在2 層神經網絡中,假設第1 層和第2 層的權重爲0。這樣一來,正向傳播時,因爲輸入層的權重爲0,所以第2 層的神經元全部會被傳遞相同的值。第2 層的神經元中全部輸入相同的值,這意味着反向傳播時第2 層的權重全部都會進行相同的更新。因此,權重被更新爲相同的值,並擁有了對稱的值(重複的值)。這使得神經網絡擁有許多不同的權重的意義喪失了。爲了防止“權重均一化”(嚴格地講,是爲了瓦解權重的對稱結構),必須隨機生成初始值。

觀察權重初始值對隱藏層激活值分佈的影響

下面做一個實驗,,向一個5層神經網絡(激活函數使用sigmoid 函數)傳入隨機生成的輸入數據,用直方圖繪製各層激活值的數據分佈

import numpy as np
import matplotlib.pyplot as plt

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def relu(x):
    return np.maximum(x, 0)

def tanh(x):
    return np.tanh(x)

# settings
activator = sigmoid # 選擇激活函數
std = 1 # 權重初始化的標準差 改變該值來觀察權重初始值對隱藏層激活值分佈的影響

input_data = np.random.randn(1000, 100)  # 1000個數據
node_num = 100  # 各隱藏層的節點(神經元)數
hidden_layer_size = 5  # 隱藏層有5層
activations = {}  # 激活值的結果保存在這裏

x = input_data

for i in range(hidden_layer_size):
    if i != 0:
        x = activations[i - 1]

    w = np.random.randn(node_num, node_num) * std

    y = np.dot(x, w)
    z = activator(y)

    activations[i] = z

# 繪製直方圖
fig, axes = plt.subplots(1, len(activations))
for i, z in activations.items():
    axes[i].set_title('layer ' + str(i))
    if i != 0:
        axes[i].set_yticks([], [])
    axes[i].hist(z.flatten(), 30, range=(0, 1))
plt.show()
  • Internal covariate shift: the change in the distribution of network activations due to the change in network parameters during training
  • 使用標準差爲1的高斯分佈作爲權重初始值時的各層激活值的分佈:
    在這裏插入圖片描述
    從上圖可知,各層的激活值呈偏向0和1的分佈。這裏使用的sigmoid函數是S型函數,隨着輸出不斷地靠近0(或者靠近1),它的導數的值逐漸接近0。因此,偏向0和1的數據分佈會造成反向傳播中梯度的值不斷變小,最後消失。這個問題稱爲梯度消失(gradient vanishing)。層次加深的深度學習中,梯度消失的問題可能會更加嚴重。

  • 使用標準差爲0.01的高斯分佈作爲權重初始值時的各層激活值的分佈:
    在這裏插入圖片描述
    這次呈集中在0.5附近的分佈。因爲不像剛纔的例子那樣偏向0和1,所以不會發生梯度消失的問題。但是,激活值的分佈有所偏向,說明在表現力上會有很大問題。爲什麼這麼說呢?因爲如果有多個神經元都輸出幾乎相同的值,那它們就沒有存在的意義了。比如,如果100個神經元都輸出幾乎相同的值,那麼也可以由1個神經元來表達基本相同的事情。因此,激活值在分佈上有所偏向會出現“表現力受限”的問題。

  • 由上述實驗可以看出,各層的激活值的分佈都要求有適當的廣度。如果傳遞的是有所偏向的數據,就會出現梯度消失或者“表現力受限”的問題,導致學習可能無法順利進行。

Xavier 初始值

Xavier 的論文中,爲了使各層的激活值呈現出具有相同廣度的分佈,推導了合適的權重尺度。推導出的結論是:

  • 如果前一層的節點數爲n,則初始值使用標準差爲1n\frac {1}{\sqrt n}的分佈
  • 注:當本層爲卷積層時,n=n= 上層通道數 ×\times 本層濾波器 W×HW \times H
    在這裏插入圖片描述
    使用Xavier 初始值後,前一層的節點數越多,要設定爲目標節點的初始值的權重尺度就越小。
  • 使用Xavier初始值作爲權重初始值時的各層激活值的分佈:
    在這裏插入圖片描述
    可以看出,越是後面的層,圖像變得越歪斜,但是呈現了比之前更有廣度的分佈。因爲各層間傳遞的數據有適當的廣度,所以sigmoid 函數的表現力不受限制,有望進行高效的學習。

如果用tanh函數代替sigmoid函數,這個稍微歪斜的問題就能得到改善。實際上,使用tanh函數後,會呈漂亮的吊鐘型分佈。tanh函數和sigmoid函數同是S型曲線函數,但tanh函數是關於原點(0, 0)對稱的S型曲線,而sigmoid函數是關於(x, y)=(0, 0.5) 對稱的S型曲線。衆所周知,用作激活函數的函數最好具有關於原點對稱的性質。

書上是這麼寫的,但是實際運行了代碼之後激活值分佈確實這樣的,沒有說好的漂亮吊鐘型?:
在這裏插入圖片描述

He初始值

Xavier 初始值是以激活函數是線性函數爲前提而推導出來的。因爲sigmoid函數和tanh函數左右對稱,且中央附近可以視作線性函數,所以適合使用Xavier 初始值。但當激活函數使用ReLU時,一般推薦使用ReLU專用的初始值,即“He初始值”

  • 當前一層的節點數爲n時,He初始值使用標準差爲2n\sqrt {\frac {2}{n}}的高斯分佈

(直觀上)可以解釋爲,因爲ReLU的負值區域的值爲0,爲了使它更有廣度,所以需要2倍的係數。

下面將激活函數改爲Relu,並改變權重初始值的標準差,比較隱藏層激活值的分佈:

  • 權重初始值爲標準差是0.01 的高斯分佈時
    在這裏插入圖片描述
    當“std = 0.01”時,各層的激活值非常小。神經網絡上傳遞的是非常小的值,說明逆向傳播時權重的梯度也同樣很小。這是很嚴重的問題,實際上學習基本上沒有進展。

  • 權重初始值爲Xavier初始值時
    在這裏插入圖片描述
    初始值爲Xavier 初始值時,層加深後,激活值的偏向變大,學習時會出現梯度消失的問題

  • 權重初始值爲He初始值時
    在這裏插入圖片描述
    初始值爲He初始值時,各層中分佈的廣度相同。由於即便層加深,數據的廣度也能保持不變,因此逆向傳播時,也會傳遞合適的值

  • 在Mnist數據集上觀察不同的權重初始值的賦值方法會在多大程度上影響神經網絡的學習:
    在這裏插入圖片描述
    這個實驗中,神經網絡有5層,每層有100個神經元,激活函數使用的是ReLU。從上圖的結果可知,std = 0.01 時完全無法進行學習。這和剛纔觀察到的激活值的分佈一樣,是因爲正向傳播中傳遞的值很小(集中在0附近的數據)。因此,逆向傳播時求到的梯度也很小,權重幾乎不進行更新。相反,當權重初始值爲Xavier初始值和He初始值時,學習進行得很順利。並且,我們發現He初始值時的學習進度更快一些。

歸一化輸入(Normalizing inputs)

本節參考吳恩達的深度學習視頻

歸一化過程:
μ1mi=1mxi\mu \leftarrow \frac {1}{m}\sum_{i=1}^m x_iσ21mi=1m(xiμ)2\sigma^2 \leftarrow \frac {1}{m}\sum_{i=1}^m {(x_i - \mu)^2}xixiμσ2+ϵx_i \leftarrow \frac {x_i - \mu}{\sqrt {\sigma^2 + \epsilon}}

  • 如果歸一化了訓練數據,那麼就需要保存參數μ\muσ\sigma,之後用保存的訓練集上的μ\muσ\sigma來歸一化測試集,而非分別在訓練集和測試集上計算μ\muσ\sigma。這樣可以讓測試集和訓練集都經過相同的μ\muσ\sigma定義的數據轉換

歸一化圖示:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

  • Why normalize inputs?
    在這裏插入圖片描述
    當輸入都在相近範圍內時,損失函數優化起來更簡單更快速,從而加速網絡學習

Batch Normalization

參考:https://arxiv.org/pdf/1502.03167.pdf

如果設定了合適的權重初始值,則各層的激活值分佈會有適當的廣度,從而可以順利地進行學習。那麼,爲了使各層擁有適當的廣度,“強制性”地調整激活值的分佈會怎樣呢?實際上,Batch Normalization方法就是基於這個想法而產生的。

Batch Norm的優點:

  • 可以使學習快速進行(可以增大學習率)(main purpose)
  • 不那麼依賴初始值(對於初始值不用那麼神經質)(main purpose)
  • 抑制過擬合(降低Dropout等的必要性)(a slight regularization effect)

直觀地理解Batch Norm的優點:

  1. BN層強制性地調整各層的激活值分佈使其擁有適當的廣度,使激活值落在非飽和區從而緩解梯度消失問題,加速網絡收斂,同時也避免了表現力不足的問題。
  2. 通過BN層可使後面的層對前面層的輸出不那麼敏感(沒有BN層的話,當前面層參數改變時,輸出值範圍變化,導致後面層的參數也不得不調整),抑制了參數微小變化隨網絡加深而被放大的問題,減小耦合,是每一層都能獨立學習,提升學習速率。(reduce the internal covariate shift)
  3. 如上節所述,歸一化所有輸入特徵可以加速網絡學習(enables higher learning rates)
  4. μ\muσ\sigma只是由一個mini-batch計算得到的,而非是整個數據集上的μ\muσ\sigma,因此相當於加入了一些噪聲(add some noise to each hidden layer’s activations),因此有輕微的正則化效果(regularization effect)。(如果用更大的mini-batch 就會減小噪聲,進而減弱正則化效果)

Batch Norm的缺點:

  • 因爲是在batch維度歸一化,BN層要有較大的batch纔能有效工作,而例如物體檢測等任務由於佔用內存較多,限制了batch的大小,進而也限制了BN層的歸一化功能
  • 測試時直接拿訓練集的均值與方差對測試集進行歸一化,可能會使測試集依賴於訓練集

實際上,在不使用Batch Norm的情況下,如果不賦予一個尺度好的初始值,學習將完全無法進行

在這裏插入圖片描述

BN層的正向傳播

  • Batch Norm步驟1:按mini-batch進行歸一化
    μB1mi=1mxi\mu_B \leftarrow \frac {1}{m}\sum_{i=1}^m x_iσB21mi=1m(xiμB)2\sigma_B^2 \leftarrow \frac {1}{m}\sum_{i=1}^m {(x_i - \mu_B)^2}xixiμBσB2+ϵx_i \leftarrow \frac {x_i - \mu_B}{\sqrt {\sigma_B^2 + \epsilon}}
    訓練時,用指數加權平均來記錄μ\muσ\sigma,從而在推理時用指數加權平均後的μ\muσ\sigma對測試數據進行歸一化

但是,如果只進行這樣的歸一化會產生一個問題:這樣歸一化使BN層的下一層網絡的輸入值方差爲1,但是通過前面權重初始值的實驗可以看出,方差爲1不一定會使激活值具有廣度,因此還需要進行下面的步驟對歸一化後的數據進行縮放及平移:

  • Batch Norm步驟2:爲了保證後面的層還能接受到前面層學習到的特徵,需要對歸一化後的數據進行縮放和平移來改變正規化後數據的均值與方差
    y=γx+βy = \gamma x + \beta
    可以看出,當γ=σB2+ϵ,β=μB\gamma = \sqrt{\sigma_B^2 + \epsilon}, \beta = \mu_B時,該式就變成了歸一化的逆變換,這樣也允許網絡通過學習,將BN層變爲恆等變換來保持原來網絡的能力(This is what makes BN really powerful)
    一開始設置γ=1,β=0\gamma = 1, \beta = 0,然後再通過學習調整到合適的值

注意到BN層前面Affine層的偏置bb實際上可以被忽略(它會在正則化時被減去),它的偏置作用被步驟2中的β\beta所替代

BN層被用在卷積層之後時,爲了使同一張特徵圖上的各個元素都能被以相同的方式歸一化,需要進行如下操作:

N, C, H, W = x.shape
x = x.reshape(N, -1)

這樣改變輸入數據的形狀後,統一地對一幅圖像上的數據進行歸一化

書上是這麼實現的,即對所有樣本中同一個通道同一個位置的NN個元素爲一組進行歸一化。但是我看了Pytorch的函數之後發現它對BN的標準實現是所有樣本上的相同通道的所有N×H×WN \times H \times W個元素爲一組進行歸一化,即以通道爲單位進行歸一化
即應該作如下形狀變換:

N, C, H, W = x.shape
x = x.transpose(1, 0, 2, 3).reshape(C, -1)

雖然之後給出的代碼還是基於書上的實現模式,但是如果要更改的話也不難

事實上,根據每一組歸一化選取元素標準的不同,BN又衍生出了其他的算法:

  • LN(Layer Normalization):以同一個樣本上的所有C×H×WC \times H \times W個元素爲一組進行歸一化,這樣計算的均值與方差與batch size無關
  • IN(Instance Normalization):以同一個樣本且同一個通道上的所有H×WH \times W個元素爲一組進行歸一化,這樣計算的均值與方差與batch size無關
  • GN(Group Normalization):之後介紹

在這裏插入圖片描述

  • 通過將BN層插入到激活函數前面或者後面,可以減小數據分佈的偏向

原論文中將BN層放在了激活函數之前:We add the BN transform immediately before the nonlinearity such as sigmoid or ReLU.
但是我也看到有說BN層放在激活函數之後效果更好的

BN層的反向傳播

基於計算圖進行推導

本節參考:
Understanding the backward pass through Batch Normalization Layer

  • 計算圖:
    在這裏插入圖片描述
    標註好正向傳播對應的步驟,之後按照相反的順序進行反向傳播:
    在這裏插入圖片描述
    在這裏插入圖片描述

Lβ=i=1NLyiLy0\frac {\partial L}{\partial \beta} = \sum_{i=1}^N \frac {\partial L}{\partial y_i},即\frac {\partial L}{\partial y}在第0軸上的累加值Lγx^=Ly\frac {\partial L}{\partial \gamma \hat {x}} = \frac {\partial L}{\partial y}
注:
out=y設out = yLβi=k=1N(Lykiykiβi)=k=1N(Lyki(γxki^+βi)βi)=k=1NLykiLβ=i=1NLyi\begin{aligned} \frac {\partial L}{\partial \beta_i} &= \sum_{k=1}^N {(\frac {\partial L}{\partial y_{ki}} \frac {\partial y_{ki}}{\partial \beta_i})} \\&= \sum_{k=1}^N {(\frac {\partial L}{\partial y_{ki}} \frac {\partial {(\gamma \hat {x_{ki}} + \beta_i)}}{\partial \beta_i})} \\&= \sum_{k=1}^N {\frac {\partial L}{\partial y_{ki}}} \\\frac {\partial L}{\partial \beta} &= \sum_{i=1}^N \frac {\partial L}{\partial y_i} \end{aligned}

在這裏插入圖片描述
Lγ=i=1NLγxi^xi^=i=1NLyixi^Lyxi^0\frac {\partial L}{\partial \gamma} = \sum_{i=1}^N \frac {\partial L}{\partial \gamma \hat {x_i}} * \hat {x_i} = \sum_{i=1}^N \frac {\partial L}{\partial y_i} * \hat {x_i},即\frac {\partial L}{\partial y} * \hat {x_i} 在第0軸上的累加值Lx^=Lγxi^γ=Lyγ\frac {\partial L}{\partial \hat {x}} = \frac {\partial L}{\partial \gamma \hat {x_i}} * \gamma = \frac {\partial L}{\partial y} * \gamma
在這裏插入圖片描述
Livar=i=1NLxi^xμ=i=1NLyγxμLyγxμ0\frac {\partial L}{\partial ivar} = \sum_{i=1}^N \frac {\partial L}{\partial \hat {x_i}} * x\mu = \sum_{i=1}^N \frac {\partial L}{\partial y} * \gamma * x\mu,即 \frac {\partial L}{\partial y} * \gamma * x\mu在第0軸上的累加值Lxμ1=Lx^ivar=Lyγivar\frac {\partial L}{\partial x\mu_1} = \frac {\partial L}{\partial \hat {x}} * ivar = \frac {\partial L}{\partial y} * \gamma * ivar

注:

  • xμxμivar1σ2+ϵx\mu 爲 x - \mu,ivar爲\frac {1}{\sqrt {\sigma^2 + \epsilon}}
  • step7的反向傳播過程與上一步一模一樣
  • 之前的博客裏有說過,根據鏈式法則,計算圖中,正向傳播分叉出去的路徑,在反向傳播會合時需要把梯度相加,因此這裏計算的是Lxμ\frac {\partial L}{\partial x\mu}的一部分Lxμ1\frac {\partial L}{\partial x\mu_1}

This step during the forward pass was the final step of the normalization combining the two branches (nominator and denominator) of the computational graph. During the backward pass we will calculate the gradients that will flow separately through these two branches backwards.

在這裏插入圖片描述
Lsqrtvar=1sqrtvar2Livar\frac {\partial L}{\partial sqrtvar} = \frac {-1}{sqrtvar^2} * \frac {\partial L}{\partial ivar}
注:

  • sqrtvarσ2+ϵsqrtvar爲\sqrt {\sigma^2 + \epsilon}
  • 因爲反向傳播得到的梯度表達式越來越複雜,從這裏開始就不再把式子全部展開了

在這裏插入圖片描述
Lvar=0.51var+ϵLsqrtvar\frac {\partial L}{\partial var} = 0.5 * \frac {1}{\sqrt {var + \epsilon}} * \frac {\partial L}{\partial sqrtvar}
在這裏插入圖片描述
Lsq=1Nones(N,D)Lvar\frac {\partial L}{\partial sq} = \frac {1} {N} * ones(N, D) * \frac {\partial L}{\partial var}

注:

  • sq(xμ)2sq爲(x-\mu)^2
    Lsqi,j=Lvarjvarjsqi,j=Lvarj1Nk=1Nsqk,jsqi,j=1NLvarj\begin{aligned} \frac {\partial L}{\partial sq_{i,j}} &= \frac {\partial L}{\partial var_j} \frac {\partial var_j}{\partial sq_{i,j}} \\&= \frac {\partial L}{\partial var_j} \frac {\partial \frac {1} {N}\sum^N_{k=1} sq_{k,j}}{\partial sq_{i,j}} \\&= \frac {1} {N} \frac {\partial L}{\partial var_j} \end{aligned}

  • ones(N,D)ones(N, D)N×DN \times D 維的全一矩陣

在這裏插入圖片描述
Lxμ2=2xμLsq\frac {\partial L}{\partial x\mu_2} = 2 * x\mu * \frac {\partial L}{\partial sq}

在這裏插入圖片描述
Lxμ=Lxμ1+Lxμ2\frac {\partial L}{\partial x\mu} = \frac {\partial L}{\partial x\mu_1} + \frac {\partial L}{\partial x\mu_2}Lx1=Lxμ\frac {\partial L}{\partial x_1} = \frac {\partial L}{\partial x\mu}Lμ=1i=1NLxμi\frac {\partial L}{\partial \mu} = -1 * \sum_{i=1}^N \frac {\partial L}{\partial x\mu_i}

注:

  • 這裏進行之前說的路徑匯合梯度相加的操作,同時也要注意這裏計算的是Lx\frac {\partial L}{\partial x}的一部分Lx1\frac {\partial L}{\partial x_1}

在這裏插入圖片描述
Lx2=1Nones(N,D)Lμ\frac {\partial L}{\partial x_2} = \frac {1} {N} * ones(N, D) * \frac {\partial L}{\partial \mu}
在這裏插入圖片描述
Lx=Lx1+Lx2\frac {\partial L}{\partial x} = \frac {\partial L}{\partial x_1} + \frac {\partial L}{\partial x_2}

不借助計算圖,直接推導

本節參考:
What does the gradient flowing through batch normalization looks like?

爲了推理上方便書寫,先引入克羅內克符號:
δi,j=1             if i=j\delta_{i,j} = 1 \ \ \ \ \ \ \ \ \ \ \ \ \ if \ i = j δi,j=0             if ij\delta_{i,j} = 0 \ \ \ \ \ \ \ \ \ \ \ \ \ if \ i \neq j
下面正式進行推導:
Lxi,j=k,lLyk,lyk,lxi,j=k,lLyk,lyk,lx^k,lx^k,lxi,j=k,lLyk,lγlx^k,lxi,j              (1)\begin{aligned} \frac {\partial L}{\partial x_{i,j}} &= \sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \frac {\partial y_{k,l}}{\partial x_{i,j}} \\&= \sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \frac {\partial y_{k,l}}{\partial \hat x_{k,l}} \frac {\partial \hat x_{k,l}}{\partial x_{i,j}} \\&= \sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \gamma_l \frac {\partial \hat x_{k,l}}{\partial x_{i,j}} \ \ \ \ \ \ \ \ \ \ \ \ \ \ (1) \end{aligned} x^k,l=xk,lμlσl2+ϵ\begin{aligned} \because \hat x_{k,l} = \frac{x_{k,l} - \mu_l}{\sqrt {\sigma_l^2 + \epsilon}} \end{aligned} x^k,lxi,j=(xk,lμl)12(σl2+ϵ)32σl2xi,j+(σl2+ϵ)12(δi,kδj,l1Nδj,l)\begin{aligned} \therefore \frac {\partial \hat x_{k,l}}{\partial x_{i,j}} = (x_{k,l} - \mu_l) \cdot -\frac{1}{2} \cdot (\sigma_l^2 + \epsilon)^{-\frac{3}{2}} \frac {\partial \sigma_l^2}{\partial x_{i,j}} + (\sigma_l^2 + \epsilon)^{-\frac{1}{2}}(\delta_{i,k} \delta_{j,l } - \frac{1}{N} \delta_{j,l}) \end{aligned} σl2=1Np=1N(xp,lμl)2\begin{aligned} \because \sigma_l^2 = \frac{1}{N}\sum_{p=1}^N(x_{p,l} - \mu_l)^2 \end{aligned} σl2xi,j=2Np=1N(xp,lμl)(δi,pδj,l1Nδj,l)=2N(xi,lμl)δj,l2N2δj,lp=1N(xp,lμl)=2N(xi,lμl)δj,l\begin{aligned} \therefore \frac {\partial \sigma_l^2}{\partial x_{i,j}} &= \frac {2}{N} \sum_{p=1}^N (x_{p,l} - \mu_l)(\delta_{i,p} \delta_{j,l} - \frac{1}{N}\delta_{j,l}) \\&= \frac{2}{N} (x_{i,l} - \mu_l) \delta_{j,l} - \frac{2}{N^2} \delta_{j,l} \sum_{p=1}^N (x_{p,l} - \mu_l) \\&= \frac{2}{N} (x_{i,l} - \mu_l) \delta_{j,l} \end{aligned} x^k,lxi,j=1N(xk,lμl)(σl2+ϵ)32(xi,lμl)δj,l+(σl2+ϵ)12(δi,kδj,l1Nδj,l)\begin{aligned} \therefore \frac {\partial \hat x_{k,l}}{\partial x_{i,j}} = -\frac{1}{N} (x_{k,l} - \mu_l) (\sigma_l^2 + \epsilon)^{-\frac{3}{2}} (x_{i,l} - \mu_l)\delta_{j,l} + (\sigma_l^2 + \epsilon)^{-\frac{1}{2}}(\delta_{i,k} \delta_{j,l } - \frac{1}{N} \delta_{j,l}) \end{aligned} (1)Lxi,j=k,lLyk,lγl(1N(xk,lμl)(σl2+ϵ)32(xi,lμl)δj,l+(σl2+ϵ)12(δi,kδj,l1Nδj,l))=k,lLyk,lγl(σl2+ϵ)12(δi,kδj,l1Nδj,l)k,lLyk,lγl(1N(xk,lμl)(σl2+ϵ)32(xi,lμl)δj,l)=Lyi,jγj(σj2+ϵ)121Nk=1NLyk,jγj(σj2+ϵ)121Nk=1NLyk,jγj(xi,jμj)(xk.jμj)(σj2+ϵ)32=1Nγj(σj2+ϵ)12(NLyi,jk=1NLyk,j(xi,jμj)(σj2+ϵ)1k=1NLyk,j(xk,jμj))=1Nγj(σj2+ϵ)12(NLyi,jk=1NLyk,j(xi,jμj)(σj2+ϵ)12k=1NLyk,j(xk,jμj)(σj2+ϵ)12)=1Nγj(σj2+ϵ)12(NLyi,jk=1NLyk,jx^i,jk=1NLyk,jx^k,j)\begin{aligned} \therefore 代入(1)式,得\\ \frac {\partial L}{\partial x_{i,j}} &= \sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \gamma_l (-\frac{1}{N} (x_{k,l} - \mu_l) (\sigma_l^2 + \epsilon)^{-\frac{3}{2}} (x_{i,l} - \mu_l)\delta_{j,l} + (\sigma_l^2 + \epsilon)^{-\frac{1}{2}}(\delta_{i,k} \delta_{j,l } - \frac{1}{N} \delta_{j,l})) \\&= \sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \gamma_l (\sigma_l^2 + \epsilon)^{-\frac{1}{2}}(\delta_{i,k} \delta_{j,l } - \frac{1}{N} \delta_{j,l}) - \sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \gamma_l(-\frac{1}{N} (x_{k,l} - \mu_l) (\sigma_l^2 + \epsilon)^{-\frac{3}{2}} (x_{i,l} - \mu_l)\delta_{j,l}) \\&= \frac {\partial L}{\partial y_{i,j} } \gamma_j(\sigma_j^2 + \epsilon)^{-\frac{1}{2}} - \frac {1}{N} \sum_{k=1}^N \frac{\partial L}{\partial y_{k,j}} \gamma_j (\sigma_j^2 + \epsilon)^{-\frac{1}{2}} - \frac {1}{N} \sum_{k=1}^N \frac{\partial L}{\partial y_{k,j}} \gamma_j (x_{i,j} - \mu_j)(x_{k.j} - \mu_j) (\sigma_j^2 + \epsilon)^{-\frac{3}{2}} \\&= \frac{1}{N} \gamma_j (\sigma_j^2 + \epsilon)^{-\frac{1}{2}}(N \frac{\partial L}{\partial y_{i,j}} - \sum_{k=1}^N \frac{\partial L}{\partial y_{k,j}} - (x_{i,j} - \mu_j)(\sigma_j^2 + \epsilon)^{-1} \sum_{k=1}^N \frac{\partial L}{\partial y_{k,j}}(x_{k,j} - \mu_j)) \\&= \frac{1}{N} \gamma_j (\sigma_j^2 + \epsilon)^{-\frac{1}{2}}(N \frac{\partial L}{\partial y_{i,j}} - \sum_{k=1}^N \frac{\partial L}{\partial y_{k,j}} - (x_{i,j} - \mu_j)(\sigma_j^2 + \epsilon)^{-\frac{1}{2}} \sum_{k=1}^N \frac{\partial L}{\partial y_{k,j}}(x_{k,j} - \mu_j)(\sigma_j^2 + \epsilon)^{-\frac{1}{2}}) \\&= \frac{1}{N} \gamma_j (\sigma_j^2 + \epsilon)^{-\frac{1}{2}}(N \frac{\partial L}{\partial y_{i,j}} - \sum_{k=1}^N \frac{\partial L}{\partial y_{k,j}} - \hat x_{i,j} \sum_{k=1}^N \frac{\partial L}{\partial y_{k,j}}\hat x_{k,j}) \end{aligned}
類似地,很容易得到Lγi\frac {\partial L}{\partial \gamma_i}Lβi\frac {\partial L}{\partial \beta_i}
Lγi=k,lLyk,lyk,lγi=k,lLyk,l(x^k,lγl+βl)γi=k,lLyk,lx^k,lδi,l=k=1NLyk,ix^k,i\begin{aligned} \frac {\partial L}{\partial \gamma_i} &= \sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \frac {\partial y_{k,l}}{\partial \gamma_i} \\&= \sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \frac {\partial (\hat x_{k,l} * \gamma_l + \beta_l)}{\partial \gamma_i} \\&=\sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \hat x_{k,l}\delta_{i,l} \\&= \sum_{k=1}^N \frac {\partial L}{\partial y_{k,i}} \hat x_{k,i} \end{aligned} Lβi=k,lLyk,lyk,lβi=k,lLyk,l(x^k,lγl+βl)βi=k,lLyk,lδi,l=k=1NLyk,i\begin{aligned} \frac {\partial L}{\partial \beta_i} &= \sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \frac {\partial y_{k,l}}{\partial \beta_i} \\&= \sum_{k,l} \frac {\partial L}{\partial y_{k,l}} \frac {\partial (\hat x_{k,l} * \gamma_l + \beta_l)}{\partial \beta_i} \\&=\sum_{k,l} \frac {\partial L}{\partial y_{k,l}}\delta_{i,l} \\&= \sum_{k=1}^N \frac {\partial L}{\partial y_{k,i}} \end{aligned}

代碼實現

class BatchNormalization:
    def __init__(self, gamma, beta, momentum=0.9, running_mean=None, running_var=None):
        self.gamma = gamma
        self.beta = beta
        self.momentum = momentum
        self.input_shape = None # Conv層的情況下爲4維,全連接層的情況下爲2維  

        # 測試時使用的平均值和方差
        self.running_mean = running_mean
        self.running_var = running_var  
        
        # backward時使用的中間數據
        self.batch_size = None
        self.xc = None # x - mu
        self.std = None # 標準差
        self.dgamma = None 
        self.dbeta = None

    def forward(self, x, train_flg=True):
        self.input_shape = x.shape
        if x.ndim != 2:
            x = x.reshape(x.shape[0], -1)

        out = self.__forward(x, train_flg)

        return out.reshape(*self.input_shape)

    def __forward(self, x, train_flg):
        if self.running_mean is None:
            n, d = x.shape
            self.running_mean = np.zeros(d)
            self.running_var = np.zeros(d)

        if train_flg == True:
            mu = x.mean(axis=0) # 將數據正規化
            xc = x - mu
            var = np.mean(xc**2, axis=0)
            std = np.sqrt(var + 10e-7)
            xn = xc / std
            
            self.batch_size = x.shape[0]
            self.xc = xc
            self.xn = xn
            self.std = std
            self.running_mean = self.momentum * self.running_mean + (1-self.momentum) * mu
            self.running_var = self.momentum * self.running_var + (1-self.momentum) * var 
        else:
            xc = x - self.running_mean
            xn = xc / np.sqrt(self.running_var + 10e-7)

        return self.gamma * xn + self.beta

    def backward(self, dout):
        if dout.ndim != 2:
            dout = dout.reshape(dout.shape[0], -1)

        dx = self.__backward(dout)

        dx = dx.reshape(*self.input_shape)
        return dx

    def __backward(self, dout):
        dbeta = dout.sum(axis=0)
        dgamma = np.sum(self.xn * dout, axis=0)

        self.dgamma = dgamma
        self.dbeta = dbeta

        # 利用計算圖得到的結論進行反向傳播
        # dxn = self.gamma * dout
        # dxc = dxn / self.std
        # dstd = -np.sum((dxn * self.xc) / (self.std**2), axis=0)
        # dvar = 0.5 * dstd / self.std
        # dxc += (2.0 / self.batch_size) * self.xc * dvar
        # dmu = np.sum(dxc, axis=0)
        # dx = dxc - dmu / self.batch_size

        # 利用直接推得的梯度公式進行反向傳播 
        # 這樣的實現更快速,而且更準確
        dx = self.batch_size**(-1) * self.gamma * self.std**(-1) * (self.batch_size * dout - dbeta - self.xn * dgamma)

        return dx

Group Normalization

參考:https://blog.csdn.net/u014380165/article/details/79810040

前面提到,Batch Normalization因爲是在batch維度歸一化,BN層要有較大的batch纔能有效工作,而例如物體檢測等任務由於佔用內存較多,限制了batch的大小,進而也限制了BN層的歸一化功能

GN(Group Normalization)從通道方向計算均值與方差,使用更爲靈活有效,避開了batch大小對歸一化的影響

當BN層被用在卷積層之後時,根據每一組歸一化選取元素標準的不同,BN可以衍生出其他的算法:

  • LN(Layer Normalization):以同一個樣本(feature map)上的所有C×H×WC \times H \times W個元素爲一組進行歸一化,這樣計算的均值與方差與batch size無關
    Si={kkN=iN}S_i = \{k | k_N = i_N\}
  • IN(Instance Normalization):以同一個樣本(feature map)且同一個通道上的所有H×WH \times W個元素爲一組進行歸一化,這樣計算的均值與方差與batch size無關
    Si={kkN=iN,kC=iC}S_i = \{k | k_N = i_N, k_C = i_C\}
  • GN(Group Normalization):Si={kkN=iN,kCC/G=iCC/G}S_i = \{k | k_N = i_N, \lfloor \frac {k_C}{C/G} \rfloor = \lfloor \frac {i_C}{C/G} \rfloor \}
    kN=iNk_N = i_N使計算都是在一個樣本(feature map)上進行的。GG表示將CC分成GGgroupsgroups,每個groupgroup中有C//GC // Gchannelchannel。因此GG是一個超參數。可以看出G=1G=1時的GN變爲LN,而G=CG=C時GN變爲IN

因此GN的思想就是在相同feature map的相同groupgroup中進行歸一化操作,而groupgroup只是在channelchannel維度上進行劃分,因此歸一化操作就和batch size無關。

在這裏插入圖片描述
GN之所以可以工作,是因爲特徵圖中,不同通道代表形狀、邊緣和紋理等不同意義,這些不同的通道並不是完全獨立地分佈,而是可以放到一起進行歸一化分析

N, C, H, W = x.shape
x = x.reshape(N, G, C // G, H, W).reshape(N * G, -1)

之後的處理與BN相同

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