在初始化權(quán)值的時(shí)候藏斩,常用的一個(gè)函數(shù)是 np.random.randn() 函數(shù)。這個(gè)函數(shù)會(huì)產(chǎn)生一個(gè)均值是0却盘,方差是1的的分布狰域。
import numpy as np
import matplotlib.pyplot as plt
w= np.random.randn(10000) #產(chǎn)生1*1w的數(shù)組
print(w.mean())
print(w.var())
plt.hist(w,bins=100) #繪制數(shù)據(jù)分布的直方圖
plt.show()
數(shù)據(jù)分布直方圖窜觉,如下圖所示:
絕大多數(shù)的隨機(jī)數(shù)都產(chǎn)生在0附近,從0開(kāi)始到+4和-4的區(qū)間上面北专,數(shù)據(jù)量越來(lái)越小。(服從正態(tài)分布)
但是旬陡,這樣的初始化方式拓颓,放入神經(jīng)網(wǎng)絡(luò)訓(xùn)練的時(shí)候,在比較深的網(wǎng)絡(luò)里面描孟,往往效果一般驶睦。
舉個(gè)例子,分析一下匿醒。
Z = weight * X+bias
我們來(lái)看一下Z的分布:
z分布在范圍是(-100 -- +100 )之間场航。絕大多數(shù)數(shù)據(jù)分布在[-50,+50]之間廉羔。
但是溉痢,如果我們的激活函數(shù)是sigmod的話,那么就會(huì)遇到這樣一個(gè)問(wèn)題憋他。也就是梯度消失的問(wèn)題孩饼。
sigmod函數(shù)的導(dǎo)數(shù):
以sigmoid函數(shù)為例,當(dāng)z的絕對(duì)值變大時(shí)竹挡,函數(shù)值越來(lái)越平滑镀娶,趨于飽和,這個(gè)時(shí)候函數(shù)的導(dǎo)數(shù)趨于0揪罕。
例如梯码,在z=2時(shí),函數(shù)的導(dǎo)數(shù)約為1/10好啰,而在z=10時(shí)轩娶,函數(shù)的導(dǎo)數(shù)已經(jīng)變成約為1/22000,也就是說(shuō)坎怪,激活函數(shù)的輸入是10的時(shí)候比2的時(shí)候神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)速率要慢2200倍罢坝!
為了神經(jīng)網(wǎng)絡(luò)保持一個(gè)很好的性能,我們希望z的值絕大多數(shù)分布在[-5,+5]之間搅窿。
對(duì)于梯度消失嘁酿,有很多種解決辦法,比如:
- batch normalization
- 使用relu
- 更改初始化的方式
這里我們只分析更改初始化的方式男应。有一個(gè)很有意思的trick:
一種簡(jiǎn)單的做法是修改w的分布闹司,使得z服從均值為0、方差為1的標(biāo)準(zhǔn)正態(tài)分布沐飘。根據(jù)正太分布期望與方差的特性游桩,將w除以sqrt(n = 輸入的結(jié)點(diǎn)個(gè)數(shù)) 即可牲迫。
這個(gè)可以簡(jiǎn)單的理解,在正常初始化weight之后借卧,然后給它除以權(quán)值個(gè)數(shù)的平方根盹憎。
weight= np.random.randn(inputnode_num)/np.sqrt(inputnode_num)
我的理解是,這樣做的方法是將輸出重新歸一化到均值是0铐刘,方差是1陪每。
如果把它放到之前的圖的坐標(biāo)系上面,即[-100,100]上面镰吵。效果更加明顯檩禾。返回z的值的分布更加集中。
import numpy as np
import matplotlib.pyplot as plt
def show_weight_distribution():
w= np.random.randn(10000)
print(w.mean())
print(w.var())
plt.hist(w,bins=100)
plt.show()
def train():
train_num=1000
z_output=[]
for i in range(train_num):
bias=0
x=np.ones(1000)
weight= np.random.randn(1000)
z=np.sum(weight * x)+bias
z_output.append(z)
print (" mean : ",np.mean(z_output)) #均值是1
print (" var : ",np.var(z_output)) #方差是1000
plt.hist(z_output,bins=100)
plt.show()
def update_train():
train_num=1000
z_output=[]
for i in range(train_num):
inputnode_num=50001
bias=0
x=np.ones(inputnode_num)
weight= np.random.randn(inputnode_num)/np.sqrt(inputnode_num) #修改的地方
z=np.sum(weight * x)+bias
z_output.append(z)
print (" mean : ",np.mean(z_output)) #均值是0
print (" var : ",np.var(z_output)) #方差是1
plt.hist(z_output,bins=100)
plt.show()
def update_train2():
train_num=1000
z_output=[]
for i in range(train_num):
inputnode_num=5000
bias=0
x=np.ones(inputnode_num)
weight= np.random.randn(inputnode_num)/np.sqrt(inputnode_num) #修改的地方
z=np.sum(weight * x)+bias
z_output.append(z)
print (" mean : ",np.mean(z_output)) #均值是0
print (" var : ",np.var(z_output)) #方差是1
plt.xlim([-100,100])
plt.hist(z_output,bins=100)
plt.show()
if __name__ =="__main__":
update_train2()
關(guān)于梯度消失和梯度爆炸的問(wèn)題:
梯度消失的表現(xiàn):
對(duì)于下圖所示的含有3個(gè)隱藏層的神經(jīng)網(wǎng)絡(luò)疤祭,梯度消失問(wèn)題發(fā)生時(shí)盼产,接近于輸出層的hidden layer 3等的權(quán)值更新相對(duì)正常,但前面的hidden layer 1的權(quán)值更新會(huì)變得很慢勺馆,導(dǎo)致前面的層權(quán)值幾乎不變戏售,仍接近于初始化的權(quán)值。
這就導(dǎo)致hidden layer 1相當(dāng)于只是一個(gè)映射層草穆,對(duì)所有的輸入做了一個(gè)同一映射蜈项,這是此深層網(wǎng)絡(luò)的學(xué)習(xí)就等價(jià)于只有后幾層的淺層網(wǎng)絡(luò)的學(xué)習(xí)了。
為什么會(huì)產(chǎn)生這樣的情況续挟?
以下圖的反向傳播為例(假設(shè)每一層只有一個(gè)神經(jīng)元且對(duì)于每一層)
如下公式所示:
網(wǎng)絡(luò)結(jié)構(gòu)如圖所示:
可以推到出:
而sigmoid的導(dǎo)數(shù)如下圖所示:
這樣紧卒,梯度爆炸問(wèn)題的出現(xiàn)原因就顯而易見(jiàn)了
sigmod的導(dǎo)數(shù)的最大值是1/4,如果w的權(quán)值小于1 的話诗祸,那么 | sigmod‘ * w | 會(huì)小于1跑芳。如果網(wǎng)絡(luò)有很多層的話,那么這個(gè)導(dǎo)數(shù)會(huì)指數(shù)倍減小直颅。也就是前幾層的權(quán)值基本上不改變博个。因而導(dǎo)致梯度消失的情況出現(xiàn)。
如果 | sigmod‘ * w | > 1 , (也就是w比較大的情況)(雖然這種情況很少出現(xiàn))這樣就是梯度爆炸的情況了功偿。
so盆佣,小結(jié)一下:
其實(shí)梯度爆炸和梯度消失問(wèn)題都是因?yàn)榫W(wǎng)絡(luò)太深,網(wǎng)絡(luò)權(quán)值更新不穩(wěn)定造成的械荷,本質(zhì)上是因?yàn)樘荻确聪騻鞑ブ械倪B乘效應(yīng)共耍。對(duì)于更普遍的梯度消失問(wèn)題,可以考慮用ReLU激活函數(shù)取代sigmoid激活函數(shù)吨瞎。
- 由于網(wǎng)絡(luò)太深痹兜,導(dǎo)致反向傳播出現(xiàn)一個(gè)連乘的效應(yīng)。梯度指數(shù)倍減小颤诀。
- sigmod的導(dǎo)數(shù)兩邊都很小
參考:
- 斯坦福 cs231n