Batchnorm原理詳解
前言:Batchnorm是深度網絡中經常用到的加速神經網絡訓練,加速收斂速度及穩定性的算法,可以說是目前深度網絡必不可少的一部分。
本文旨在用通俗易懂的語言,對深度學習的常用算法–batchnorm的原理及其代碼實現做一個詳細的解讀。本文主要包括以下幾個部分。
-
Batchnorm主要解決的問題
-
Batchnorm原理解讀
-
Batchnorm的優點
-
Batchnorm的源碼解讀
第一節:Batchnorm主要解決的問題
首先,此部分也即是講為什么深度網絡會需要batchnorm,我們都知道,深度學習的話尤其是在CV上都需要對數據做歸一化,因為深度神經網絡主要就是為了學習訓練數據的分布,并在測試集上達到很好的泛化效果,但是,如果我們每一個batch輸入的數據都具有不同的分布,顯然會給網絡的訓練帶來困難。另一方面,數據經過一層層網絡計算后,其數據分布也在發生著變化,此現象稱為Internal Covariate Shift,接下來會詳細解釋,會給下一層的網絡學習帶來困難。batchnorm直譯過來就是批規范化,就是為了解決這個分布變化問題。
1.1 Internal Covariate Shift
Internal Covariate Shift :此術語是小組在論文Batch Normalizatoin 中提出來的,其主要描述的是:訓練深度網絡的時候經常發生訓練困難的問題,因為,每一次參數迭代更新后,上一層網絡的輸出數據經過這一層網絡計算后,數據的分布會發生變化,為下一層網絡的學習帶來困難(神經網絡本來就是要學習數據的分布,要是分布一直在變,學習就很難了),此現象稱之為Internal Covariate Shift。
Batch Normalizatoin 之前的解決方案就是使用較小的學習率,和小心的初始化參數,對數據做白化處理,但是顯然治標不治本。
1.2 covariate shift
Internal Covariate Shift 和Covariate Shift具有相似性,但并不是一個東西,前者發生在神經網絡的內部,所以是Internal,后者發生在輸入數據上。Covariate Shift主要描述的是由于訓練數據和測試數據存在分布的差異性,給網絡的泛化性和訓練速度帶來了影響,我們經常使用的方法是做歸一化或者白化。想要直觀感受的話,看下圖:
舉個簡單線性分類栗子,假設我們的數據分布如a所示,參數初始化一般是0均值,和較小的方差,此時擬合的y=wx+b如b圖中的橘色線,經過多次迭代后,達到紫色線,此時具有很好的分類效果,但是如果我們將其歸一化到0點附近,顯然會加快訓練速度,如此我們更進一步的通過變換拉大數據之間的相對差異性,那么就更容易區分了。
Covariate Shift 就是描述的輸入數據分布不一致的現象,對數據做歸一化當然可以加快訓練速度,能對數據做去相關性,突出它們之間的分布相對差異就更好了。Batchnorm做到了,前文已說過,Batchnorm是歸一化的一種手段,極限來說,這種方式會減小圖像之間的絕對差異,突出相對差異,加快訓練速度。所以說,并不是在深度學習的所有領域都可以使用BatchNorm,下文會寫到其不適用的情況。
第二節:Batchnorm 原理解讀
本部分主要結合原論文部分,排除一些復雜的數學公式,對BatchNorm的原理做盡可能詳細的解釋。
之前就說過,為了減小Internal Covariate Shift,對神經網絡的每一層做歸一化不就可以了,假設將每一層輸出后的數據都歸一化到0均值,1方差,滿足正太分布,但是,此時有一個問題,每一層的數據分布都是標準正太分布,導致其完全學習不到輸入數據的特征,因為,費勁心思學習到的特征分布被歸一化了,因此,直接對每一層做歸一化顯然是不合理的。
但是如果稍作修改,加入可訓練的參數做歸一化,那就是BatchNorm實現的了,接下來結合下圖的偽代碼做詳細的分析:
之所以稱之為batchnorm是因為所norm的數據是一個batch的,假設輸入數據是β=x1...m共m個數據,輸出是yi=BN(x),batchnorm的步驟如下:
1.先求出此次批量數據x的均值,μβ=1m∑mi=1xi
2.求出此次batch的方差,σ2β=1m∑i=1m(xi?μβ)2
3.接下來就是對x做歸一化,得到x?i
4.最重要的一步,引入縮放和平移變量γ和β ,計算歸一化后的值,yi=γx?i +β
接下來詳細介紹一下這額外的兩個參數,之前也說過如果直接做歸一化不做其他處理,神經網絡是學不到任何東西的,但是加入這兩個參數后,事情就不一樣了,先考慮特殊情況下,如果γ和β分別等于此batch的方差和均值,那么yi不就還原到歸一化前的x了嗎,也即是縮放平移到了歸一化前的分布,相當于batchnorm沒有起作用,β 和γ分別稱之為 平移參數和縮放參數 。這樣就保證了每一次數據經過歸一化后還保留的有學習來的特征,同時又能完成歸一化這個操作,加速訓練。
先用一個簡單的代碼舉個小栗子:
def Batchnorm_simple_for_train(x, gamma, beta, bn_param): """
param:x : 輸入數據,設shape(B,L)
param:gama : 縮放因子 γ
param:beta : 平移因子 β
param:bn_param : batchnorm所需要的一些參數
eps : 接近0的數,防止分母出現0
momentum : 動量參數,一般為0.9, 0.99, 0.999
running_mean :滑動平均的方式計算新的均值,訓練時計算,為測試數據做準備
running_var : 滑動平均的方式計算新的方差,訓練時計算,為測試數據做準備
""" running_mean = bn_param['running_mean'] running_var = bn_param['running_var'] results = 0. x_mean=x.mean(axis=0) x_var=x.var(axis=0) x_normalized=(x-x_mean)/np.sqrt(x_var+eps) results = gamma * x_normalized + beta running_mean = momentum * running_mean + (1 - momentum) * x_mean
running_var = momentum * running_var + (1 - momentum) * x_var bn_param['running_mean'] = running_mean
bn_param['running_var'] = running_var return results , bn_param
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
看完這個代碼是不是對batchnorm有了一個清晰的理解,首先計算均值和方差,然后歸一化,然后縮放和平移,完事!但是這是在訓練中完成的任務,每次訓練給一個批量,然后計算批量的均值方差,但是在測試的時候可不是這樣,測試的時候每次只輸入一張圖片,這怎么計算批量的均值和方差,于是,就有了代碼中下面兩行,在訓練的時候實現計算好mean var測試的時候直接拿來用就可以了,不用計算均值和方差。
running_mean = momentum * running_mean + (1 - momentum) * x_mean running_var = momentum * running_var + (1 - momentum) * x_var
所以,測試的時候是這樣的:
def Batchnorm_simple_for_test(x, gamma, beta, bn_param): """
param:x : 輸入數據,設shape(B,L)
param:gama : 縮放因子 γ
param:beta : 平移因子 β
param:bn_param : batchnorm所需要的一些參數
eps : 接近0的數,防止分母出現0
momentum : 動量參數,一般為0.9, 0.99, 0.999
running_mean :滑動平均的方式計算新的均值,訓練時計算,為測試數據做準備
running_var : 滑動平均的方式計算新的方差,訓練時計算,為測試數據做準備
""" running_mean = bn_param['running_mean'] running_var = bn_param['running_var'] results = 0. x_normalized=(x-running_mean )/np.sqrt(running_var +eps) results = gamma * x_normalized + beta return results , bn_param
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
你是否理解了呢?如果還沒有理解的話,歡迎再多看幾遍。
第三節:Batchnorm源碼解讀
本節主要講解一段tensorflow中Batchnorm的可以使用的代碼3,如下:
代碼來自知乎,這里加入注釋幫助閱讀。
def batch_norm_layer(x, train_phase, scope_bn): with tf.variable_scope(scope_bn): beta = tf.Variable(tf.constant(0.0, shape=[x.shape[-1]]), name='beta', trainable=True)
gamma = tf.Variable(tf.constant(1.0, shape=[x.shape[-1]]), name='gamma', trainable=True) axises = np.arange(len(x.shape) - 1)
batch_mean, batch_var = tf.nn.moments(x, axises, name='moments') ema = tf.train.ExponentialMovingAverage(decay=0.5) def mean_var_with_update(): ema_apply_op = ema.apply([batch_mean, batch_var]) with tf.control_dependencies([ema_apply_op]): return tf.identity(batch_mean), tf.identity(batch_var) mean, var = tf.cond(train_phase, mean_var_with_update, lambda: (ema.average(batch_mean), ema.average(batch_var)))
normed = tf.nn.batch_normalization(x, mean, var, beta, gamma, 1e-3) return normed
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
至于此行代碼tf.nn.batch_normalization()就是簡單的計算batchnorm過程啦,代碼如下:
這個函數所實現的功能就如此公式:γ(x?μ)σ+β
def batch_normalization(x,
mean,
variance,
offset,
scale,
variance_epsilon,
name=None): with ops.name_scope(name, "batchnorm", [x, mean, variance, scale, offset]):
inv = math_ops.rsqrt(variance + variance_epsilon) if scale is not None:
inv *= scale return x * inv + (offset - mean * inv if offset is not None else -mean * inv)
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
第四節:Batchnorm的優點
主要部分說完了,接下來對BatchNorm做一個總結:
-
沒有它之前,需要小心的調整學習率和權重初始化,但是有了BN可以放心的使用大學習率,但是使用了BN,就不用小心的調參了,較大的學習率極大的提高了學習速度,
-
Batchnorm本身上也是一種正則的方式,可以代替其他正則方式如dropout等
-
另外,個人認為,batchnorm降低了數據之間的絕對差異,有一個去相關的性質,更多的考慮相對差異性,因此在分類任務上具有更好的效果。
注:或許大家都知道了,韓國團隊在2017NTIRE圖像超分辨率中取得了top1的成績,主要原因竟是去掉了網絡中的batchnorm層,由此可見,BN并不是適用于所有任務的,在image-to-image這樣的任務中,尤其是超分辨率上,圖像的絕對差異顯得尤為重要,所以batchnorm的scale并不適合。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。