深度學習基礎系列(六)| 權重初始化的選擇


  深層網絡需要一個優良的權重初始化方案,目的是降低發生梯度爆炸和梯度消失的風險。先解釋下梯度爆炸和梯度消失的原因,假設我們有如下前向傳播路徑:

  a= w1x + b1  

  z= σ(a1)

  a= w2z1 + b2

  z2 = σ(a2)

  ...

  a= wnzn-1 + bn

  zn = σ(an)

  簡化起見,令所有的b都為0,那么可得:

  zn =  σ(wnσ(Wn-1σ(...σ(w1x))),

  若進一步簡化,令z = σ(a) = a,那么可得:

  zn = wn * Wn-1 * Wn-1 *...* X

  而權重w的選擇,假定都為1.5,那么可觀察到 zn是呈現指數級遞增,深層網絡越深,意味着后面的值越大,呈現爆炸趨勢;反之,w假定都為0.5,那么可觀察到 zn是呈現指數級遞減,深層網絡越深,意味着后面的值越小,呈現消失趨勢。

  若令z = σ(a) = sigmoid(a),且a= ∑nwixi + b,其中n為輸入參數的個數,當輸入參數很多時,猜測|a|很大概率會大於1,對於sigmoid函數而言,|a|>1,則意味着曲線越來越平滑,z值會趨近於1或0,從而也會導致梯度消失。

  那我們在每一層網絡進行初始化權重時,若能給w一個合適的值,則能降低這種梯度爆炸或梯度消失的可能性嗎?我們看看該如何選擇。

一、隨機分布權重

  在keras中,其函數為:K.random_uniform_variable(),我們來直觀地看看其數據分布圖,先看代碼:

import numpy as np
import matplotlib.pyplot as plt
import tensorflow.keras.backend as K

w = K.eval(K.random_uniform_variable(shape=(1, 10000), low=-1, high=1))
w = w.reshape(-1)
print("w:", w)

x = K.eval(K.random_uniform_variable(shape=(1, 10000), low=-1, high=1))
x = x.reshape(-1)
print("x:", x)

a = np.dot(w, x)
print("a:", a)

n, bins, patches = plt.hist(w, 50, density=1, facecolor='g', alpha=0.75)

plt.xlabel('data range')
plt.ylabel('probability')
plt.axis([-2, 2, 0, 1])
plt.grid(True)
plt.show()

  其圖像為:

  觀察圖像可知,隨機函數取了10000個點,值范圍被約束在-1~1之間,其概率分布都很均勻。

  其輸出結果為:

w: [-0.3033681   0.95340157  0.76744485 ...  0.24013376  0.5394962
 -0.23630977]
x: [-0.19380212  0.86640644  0.6185038  ... -0.66250014 -0.2095201
  0.23459053]
a: 16.111116

  從結果可知,若我們的輸入是10000個特征點,那么a= ∑10000wixi + b,且|a|>1的概率很大(結果為16.111116)。可想而知,不采用激活函數或relu函數,則有梯度爆炸的可能性;若采用sigmoid激活函數的話,則會導致梯度消失。

 

二、正太分布權重

  在keras中,其函數為:K.random_normal_variable()和K.truncated_normal(),我們來直觀地看看其數據分布圖,先看K.random_normal_variable代碼: 

import numpy as np
import matplotlib.pyplot as plt
import tensorflow.keras.backend as K

w = K.eval(K.random_normal_variable(shape=(1, 10000), mean=0, scale=1))
w = w.reshape(-1)
print("w:", w)

x = K.eval(K.random_uniform_variable(shape=(1, 10000), low=-1, high=1))
x = x.reshape(-1)
print("x:", x)

a = np.dot(w, x)
print("a:", a)

n, bins, patches = plt.hist(w, 50, density=1, facecolor='g', alpha=0.75)

plt.xlabel('data range')
plt.ylabel('probability')
plt.axis([-5, 5, 0, 0.6])
plt.grid(True)
plt.show()

  其圖像為:

  其結果為:

w: [-1.8685548   1.501203    1.1083876  ... -0.93544585  0.08100258
  0.4771947 ]
x: [ 0.40333223  0.7284522  -0.40256715 ...  0.79942155 -0.915035
  0.50783443]
a: -46.02679

  再看看K.truncated_normal()的代碼:

import numpy as np
import matplotlib.pyplot as plt
import tensorflow.keras.backend as K

w = K.eval(K.truncated_normal(shape=(1, 10000), mean=0, stddev=1))
w = w.reshape(-1)
print("w:", w)

x = K.eval(K.random_uniform_variable(shape=(1, 10000), low=-1, high=1))
x = x.reshape(-1)
print("x:", x)

a = np.dot(w, x)
print("a:", a)

n, bins, patches = plt.hist(w, 50, density=1, facecolor='g', alpha=0.75)

plt.xlabel('data range')
plt.ylabel('probability')
plt.axis([-5, 5, 0, 0.6])
plt.grid(True)
plt.show()

  其圖像為:

 

  其結果為:

w: [ 1.0354282  -0.9385183   0.57337016 ... -0.3302136  -0.10443623
  0.9371711 ]
x: [-0.7896631  -0.01105547  0.778579   ...  0.7932384  -0.17074609
  0.60096693]
a: -18.191553

  觀察兩個圖像可知,兩者都是正太分布圖像,唯一區別在於K.truncated_normal()把大於2和小於2的數據給截斷了,只保留了一部分數據。

  從結果可知,若我們的輸入是10000個特征點,那么a= ∑10000wixi + b ,雖然圖像具有一定的對稱性,總體均值為0,但|a1|>1依然有很大概率存在(結果為-18.191553),依舊有有梯度消失和爆炸的可能性。    

 

三、正太收窄權重

  我們的目標是使得|a1| < 1,這樣無論激活函數是sigmoid還是relu,都可以保證每一層的輸出值不會增長太大,也不會增長過小。所以我們可以在正太分布的基礎上,讓其收窄變尖,可以讓wi=wi / √n,其中n為該層的輸入參數的數量,以10000個輸出特征點為例,wi=wi / √10000,這樣a1= ∑10000wixi + b1 就可以確保大致在-1~1范圍內。可看代碼:

import numpy as np
import matplotlib.pyplot as plt
import tensorflow.keras.backend as K

w = K.eval(K.random_normal_variable(shape=(1, 10000), mean=0, scale=1/np.sqrt(10000)))
w = w.reshape(-1)
print("w:", w)

x = K.eval(K.random_uniform_variable(shape=(1, 10000), low=-1, high=1))
x = x.reshape(-1)
print("x:", x)

a = np.dot(w, x)
print("a:", a)

n, bins, patches = plt.hist(w, 50, density=1, facecolor='g', alpha=0.75)

plt.xlabel('data range')
plt.ylabel('probability')
plt.axis([-0.1, 0.1, 0, 50])
plt.grid(True)
plt.show()

  其圖像為:

  其結果為:

w: [ 0.00635913 -0.01406644 -0.00843588 ... -0.00573074  0.00345371
 -0.01102492]
x: [ 0.3738377  -0.01633143  0.21199775 ... -0.78332734 -0.96384525
 -0.3478613 ]
a: -0.4904538

  觀察圖像可知,數值范圍已經被壓縮在-0.025~0.025附近,概率值最高也到了40以上,變得又窄又尖了。

  從結果也可知,我們成功地把|a|壓縮在1范圍以內,這個結果無論對sigmoid函數,還是relu函數,都是比較友好的,降低了梯度爆炸和梯度消失的風險,也利於加快訓練學習過程。

 

四、Keras的默認選擇

  在使用Keras的Conv2D、Dense等函數時,會發現權重初始化的默認值為glorot_uniform,其對應網頁為:https://www.tensorflow.org/api_docs/python/tf/glorot_uniform_initializer

  可以看出glorot_uniform使用的是隨機分布,不同之處在於其上下限值為[-limit, limit],其中limit = sqrt(6 / (fan_in + fan_out)),fan_in即為輸入特征數,而fa_out為輸出特征數。其實和上述正太收緊類似,可以理解其數值范圍是非常非常小。

  在此不再贅述。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM