前言
神經(jīng)網(wǎng)絡(luò)中的權(quán)重(weight)初始化是個(gè)常常被忽略的問題鹰椒。
最近在手寫一個(gè)Python的神經(jīng)網(wǎng)絡(luò)庫(GitHub:hamaa)沼死,剛開始為了測(cè)試代碼是否寫對(duì)着逐,搭建了一個(gè)2->4->2
的單隱層神經(jīng)網(wǎng)絡(luò)來擬合異或運(yùn)算,擬合結(jié)果十分完美意蛀。但是在做MNIST手寫數(shù)字識(shí)別耸别,將網(wǎng)絡(luò)擴(kuò)展到了784->100->10
時(shí),發(fā)現(xiàn)損失函數(shù)一直不下降县钥,訓(xùn)練準(zhǔn)確率一直停留在10%左右(和隨機(jī)猜的命中概率一樣嘛)秀姐。一直以為是back propagation的代碼寫錯(cuò)了,debug了整整兩天都沒發(fā)現(xiàn)錯(cuò)誤魁蒜,結(jié)果輸出中間weights的梯度dw看看囊扳,發(fā)現(xiàn)兩個(gè)權(quán)重矩陣的梯度都是在1e-10
左右的數(shù)量級(jí)。后來查詢了一些資料兜看,原來是代碼缺少了權(quán)重初始化(weight initialization)這及其重要的一步锥咸。增加了權(quán)重初始化后擬合結(jié)果終于正常。
在以前看一些關(guān)于神經(jīng)網(wǎng)絡(luò)的資料時(shí)细移,我也經(jīng)巢瑁看到“權(quán)重初始化”這一步,但一直錯(cuò)誤地以為“權(quán)重初始化”等價(jià)于“權(quán)重隨機(jī)初始化”弧轧,以為僅僅將權(quán)重初始化為很小的隨機(jī)數(shù)即可雪侥,但其實(shí)它的原因除了打破梯度更新對(duì)稱性之外,還有更深層次的原因精绎。所以接下來文章分為兩部分速缨,分別介紹為什么需要進(jìn)行權(quán)重初始化,以及如何進(jìn)行權(quán)重初始化代乃。
權(quán)重初始化:Why
在創(chuàng)建了神經(jīng)網(wǎng)絡(luò)后旬牲,通常需要對(duì)權(quán)重和偏置進(jìn)行初始化,大部分的實(shí)現(xiàn)都是采取Gaussian distribution來生成隨機(jī)初始值搁吓。假設(shè)現(xiàn)在輸入層有1000個(gè)神經(jīng)元原茅,隱藏層有1個(gè)神經(jīng)元,輸入數(shù)據(jù)x為一個(gè)全為1的1000維向量堕仔,采取高斯分布來初始化權(quán)重矩陣w擂橘,偏置b取0。下面的代碼計(jì)算隱藏層的輸入z:
# -*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
def run():
x = np.ones(1000)
w = np.random.randn(1000)
b = 0
# z為加權(quán)和
z = np.sum(x * w) + b
print z
然而通過上述初始化后摩骨,因?yàn)閣服從均值為0通贞、方差為1的正太分布朗若,x全為1,b全為0滑频,輸入層一共1000個(gè)神經(jīng)元捡偏,所以z服從的是一個(gè)均值為0唤冈、方差為1000的正太分布峡迷。修改代碼如下,生成20000萬個(gè)z并查看其均值你虹、方差以及分布圖像:
def run():
# z的個(gè)數(shù)
t = 20000
z_lst = np.empty(t)
x = np.ones(1000)
b = 0
for i in xrange(t):
w = np.random.randn(1000)
z = np.sum(x * w) + b
z_lst[i] = z
print 'z 均值:', np.mean(z_lst)
print 'z 方差:', np.var(z_lst)
plt.hist(z_lst, bins=100)
plt.show()
輸出結(jié)果如下:
z 均值: -0.0402106463845
z 方差: 997.082082524
輸出圖像如下:
在此情況下绘搞,z有可能是一個(gè)遠(yuǎn)小于-1或者遠(yuǎn)大于1的數(shù),通過激活函數(shù)(比如sigmoid)后所得到的輸出會(huì)非常接近0或者1傅物,也就是隱藏層神經(jīng)元處于飽和的狀態(tài)夯辖。所以當(dāng)出現(xiàn)這樣的情況時(shí),在權(quán)重中進(jìn)行微小的調(diào)整僅僅會(huì)給隱藏層神經(jīng)元的激活值帶來極其微弱的改變董饰。而這種微弱的改變也會(huì)影響網(wǎng)絡(luò)中剩下的神經(jīng)元蒿褂,然后會(huì)帶來相應(yīng)的代價(jià)函數(shù)的改變。結(jié)果就是卒暂,這些權(quán)重在我們進(jìn)行梯度下降算法時(shí)會(huì)學(xué)習(xí)得非常緩慢[1]啄栓。
因此,我們可以通過改變權(quán)重w的分布也祠,使|z|盡量接近于0昙楚。這就是我們?yōu)槭裁葱枰M(jìn)行權(quán)重初始化的原因了。
權(quán)重初始化:How
一種簡(jiǎn)單的做法是修改w的分布诈嘿,使得z服從均值為0堪旧、方差為1的標(biāo)準(zhǔn)正態(tài)分布。根據(jù)正太分布期望與方差的特性奖亚,將w除以sqrt(1000)即可淳梦。修改后代碼如下:
def run():
# z的個(gè)數(shù)
t = 20000
z_lst = np.empty(t)
# 輸入神經(jīng)元個(gè)數(shù)
m = 1000
x = np.ones(m)
b = 0
for i in xrange(t):
w = np.random.randn(m) / np.sqrt(m)
z = np.sum(x * w) + b
z_lst[i] = z
print 'z 均值:', np.mean(z_lst)
print 'z 方差:', np.var(z_lst)
# 保持與z分布(1)圖像橫坐標(biāo)刻度不變,使得結(jié)果更加直觀
plt.xlim([-150, 150])
plt.hist(z_lst, bins=10)
plt.show()
輸出結(jié)果如下:
z 均值: 0.013468729222
z 方差: 1.00195898464
輸出圖像如下:
這樣的話z的分布就是一個(gè)比較接近于0的數(shù)昔字,使得神經(jīng)元處于不太飽和的狀態(tài)爆袍,讓BP過程能夠正常進(jìn)行下去。
除了這種方式之外(除以前一層神經(jīng)元的個(gè)數(shù)n_in的開方)李滴,還有許多針對(duì)不同激活函數(shù)的不同權(quán)重初始化方法螃宙,比如兼顧了BP過程的除以( (n_in + n_out)/2 )。具體可以參考Stanford CS231n課程中提到的各種方法所坯。
后記
最后強(qiáng)烈安利Stanford CS231n的官方授權(quán)中文翻譯專欄知乎專欄:智能單元谆扎,感謝各位Zhihuer的辛勤翻譯,為后輩的學(xué)習(xí)與快速上手提供了極大的便利芹助。
參考
[1] www.neuralnetworksanddeeplearning.com堂湖,第三章
[2] Stanford CS231n
[3] Stanford CS231n官方授權(quán)中文翻譯闲先,知乎專欄:智能單元