深層網絡須要一個優良的權重初始化方案,目的是下降發生梯度爆炸和梯度消失的風險。先解釋下梯度爆炸和梯度消失的緣由,假設咱們有以下前向傳播路徑:python
a1 = w1x + b1 api
z1 = σ(a1)網絡
a2 = w2z1 + b2dom
z2 = σ(a2)函數
...學習
an = wnzn-1 + bnspa
zn = σ(an)code
簡化起見,令全部的b都爲0,那麼可得:orm
zn = σ(wnσ(Wn-1σ(...σ(w1x))),blog
若進一步簡化,令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的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爲輸出特徵數。其實和上述正太收緊相似,能夠理解其數值範圍是很是很是小。
在此再也不贅述。