batchnorm原理及程式碼詳解(筆記2)
Batchnorm原理詳解
前言:Batchnorm是深度網路中經常用到的加速神經網路訓練,加速收斂速度及穩定性的演算法,可以說是目前深度網路必不可少的一部分。
本文旨在用通俗易懂的語言,對深度學習的常用演算法–batchnorm的原理及其程式碼實現做一個詳細的解讀。本文主要包括以下幾個部分。
- Batchnorm主要解決的問題
- Batchnorm原理解讀
- Batchnorm的優點
- Batchnorm的原始碼解讀
第一節:Batchnorm主要解決的問題
首先,此部分也即是講為什麼深度網路會需要batchnorm
batchnorm,我們都知道,深度學習的話尤其是在CV上都需要對資料做歸一化,因為深度神經網路主要就是為了學習訓練資料的分佈,並在測試集上達到很好的泛化效果,但是,如果我們每一個batch輸入的資料都具有不同的分佈,顯然會給網路的訓練帶來困難。另一方面,資料經過一層層網路計算後,其資料分佈也在發生著變化,此現象稱為InternalInternal CovariateCovariate ShiftShift,接下來會詳細解釋,會給下一層的網路學習帶來困難。batchnorm
batchnorm直譯過來就是批規範化,就是為了解決這個分佈變化問題。
1.1 Internal Covariate Shift
Internal
Internal CovariateCovariate ShiftShift :此術語是google小組在論文BatchBatch NormalizatoinNormalizatoin 中提出來的,其主要描述的是:訓練深度網路的時候經常發生訓練困難的問題,因為,每一次引數迭代更新後,上一層網路的輸出資料經過這一層網路計算後,資料的分佈會發生變化,為下一層網路的學習帶來困難(神經網路本來就是要學習資料的分佈,要是分佈一直在變,學習就很難了),此現象稱之為InternalInternal CovariateCovariate Shift
Shift。
Batch
Batch Normalizatoin
Normalizatoin 之前的解決方案就是使用較小的學習率,和小心的初始化引數,對資料做白化處理,但是顯然治標不治本。
###1.2 covariate shift
Internal
Internal CovariateCovariate ShiftShift 和CovariateCovariate ShiftShift具有相似性,但並不是一個東西,前者發生在神經網路的內部,所以是InternalInternal,後者發生在輸入資料上。CovariateCovariate Shift
Shift主要描述的是由於訓練資料和測試資料存在分佈的差異性,給網路的泛化性和訓練速度帶來了影響,我們經常使用的方法是做歸一化或者白化。想要直觀感受的話,看下圖:
舉個簡單線性分類栗子,假設我們的資料分佈如a所示,引數初始化一般是0均值,和較小的方差,此時擬合的y=wx+b
y=wx+b如b圖中的橘色線,經過多次迭代後,達到紫色線,此時具有很好的分類效果,但是如果我們將其歸一化到0點附近,顯然會加快訓練速度,如此我們更進一步的通過變換拉大資料之間的相對差異性,那麼就更容易區分了。
Covariate
Covariate ShiftShift 就是描述的輸入資料分佈不一致的現象,對資料做歸一化當然可以加快訓練速度,能對資料做去相關性,突出它們之間的分佈相對差異就更好了。BatchnormBatchnorm做到了,前文已說過,BatchnormBatchnorm是歸一化的一種手段,極限來說,這種方式會減小影象之間的絕對差異,突出相對差異,加快訓練速度。所以說,並不是在深度學習的所有領域都可以使用BatchNorm
BatchNorm,下文會寫到其不適用的情況。
第二節:Batchnorm 原理解讀
本部分主要結合原論文部分,排除一些複雜的數學公式,對BatchNorm
BatchNorm的原理做盡可能詳細的解釋。
之前就說過,為了減小Internal
Internal CovariateCovariate ShiftShift,對神經網路的每一層做歸一化不就可以了,假設將每一層輸出後的資料都歸一化到0均值,1方差,滿足正太分佈,但是,此時有一個問題,每一層的資料分佈都是標準正太分佈,導致其完全學習不到輸入資料的特徵,因為,費勁心思學習到的特徵分佈被歸一化了,因此,直接對每一層做歸一化顯然是不合理的。
但是如果稍作修改,加入可訓練的引數做歸一化,那就是BatchNorm
BatchNorm實現的了,接下來結合下圖的虛擬碼做詳細的分析:
之所以稱之為batchnorm是因為所norm的資料是一個batch的,假設輸入資料是β=x1...m
β=x1...m共m個數據,輸出是yi=BN(x)yi=BN(x),batchnorm
batchnorm的步驟如下:
1.先求出此次批量資料x
x的均值,μβ=1m∑mi=1xiμβ=m1∑i=1mxi
2.求出此次batch的方差,σ2β=1m∑i=1m(xi−μβ)2σβ2=m1∑i=1m(xi−μβ)2
3.接下來就是對xx做歸一化,得到x−ixi−
4.最重要的一步,引入縮放和平移變數$γ 和和\beta$ ,計算歸一化後的值,yi=γx−iyi=γxi− +β
接下來詳細介紹一下這額外的兩個引數,之前也說過如果直接做歸一化不做其他處理,神經網路是學不到任何東西的,但是加入這兩個引數後,事情就不一樣了,先考慮特殊情況下,如果γ
γ和ββ分別等於此batch的標準差和均值,那麼yiyi不就還原到歸一化前的xx了嗎,也即是縮放平移到了歸一化前的分佈,相當於batchnormbatchnorm沒有起作用,$ β$ 和γ
γ分別稱之為 平移引數和縮放參數 。這樣就保證了每一次資料經過歸一化後還保留的有學習來的特徵,同時又能完成歸一化這個操作,加速訓練。
先用一個簡單的程式碼舉個小栗子:
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'] #shape = [B]
running_var = bn_param['running_var'] #shape = [B]
results = 0. # 建立一個新的變數
x_mean=x.mean(axis=0) # 計算x的均值
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
看完這個程式碼是不是對batchnorm有了一個清晰的理解,首先計算均值和方差,然後歸一化,然後縮放和平移,完事!但是這是在訓練中完成的任務,每次訓練給一個批量,然後計算批量的均值方差,但是在測試的時候可不是這樣,測試的時候每次只輸入一張圖片,這怎麼計算批量的均值和方差,於是,就有了程式碼中下面兩行,在訓練的時候實現計算好mean
mean var
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'] #shape = [B]
running_var = bn_param['running_var'] #shape = [B]
results = 0. # 建立一個新的變數
x_normalized=(x-running_mean )/np.sqrt(running_var +eps) # 歸一化
results = gamma * x_normalized + beta # 縮放平移
return results , bn_param
你是否理解了呢?如果還沒有理解的話,歡迎再多看幾遍。
第三節:Batchnorm原始碼解讀
本節主要講解一段tensorflow中Batchnorm
Batchnorm的可以使用的程式碼3
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)
# train_phase 訓練還是測試的flag
# 訓練階段計算runing_mean和runing_var,使用mean_var_with_update()函式
# 測試的時候直接把之前計算的拿去用 ema.average(batch_mean)
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
至於此行程式碼tf.nn.batch_normalization()就是簡單的計算batchnorm過程啦,程式碼如下:
這個函式所實現的功能就如此公式:γ(x−μ)σ+β
σγ(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)
###第四節:Batchnorm的優點
主要部分說完了,接下來對BatchNorm做一個總結:
- 沒有它之前,需要小心的調整學習率和權重初始化,但是有了BN可以放心的使用大學習率,但是使用了BN,就不用小心的調參了,較大的學習率極大的提高了學習速度,
- Batchnorm本身上也是一種正則的方式,可以代替其他正則方式如dropout等
- 另外,個人認為,batchnorm降低了資料之間的絕對差異,有一個去相關的性質,更多的考慮相對差異性,因此在分類任務上具有更好的效果。
注:或許大家都知道了,韓國團隊在2017NTIRE影象超解析度中取得了top1的成績,主要原因竟是去掉了網路中的batchnorm層,由此可見,BN並不是適用於所有任務的,在image-to-image這樣的任務中,尤其是超解析度上,影象的絕對差異顯得尤為重要,所以batchnorm的scale並不適合。
參考文獻:
【1】http://blog.csdn.net/zhikangfu/article/details/53391840
【2】http://geek.csdn.net/news/detail/160906
【3】 https://www.zhihu.com/question/53133249