【DL碎片1】神經網絡參數初始化的學問

從【DL筆記1】到【DL筆記N】,以及【DL碎片】系列,是我學習深度學習一路上的點點滴滴的記錄,是從Coursera網課、各大博客、論文的學習以及自己的實踐中總結而來。從基本的概念、原理、公式,到用生動形象的例子去理解,到動手做實驗去感知,到著名案例的學習,到用所學來實現自己的小而有趣的想法......我相信,一路看下來,我們可以感受到深度學習的無窮的樂趣,並有興趣和激情繼續鑽研學習。 正所謂 Learning by teaching,寫下一篇篇筆記的同時,我也收穫了更多深刻的體會,希望大家可以和我一同進步,共同享受AI無窮的樂趣。


我們已經知道,神經網絡的參數主要是權重(weights):W, 和偏置項(bias):b。 訓練神經網絡的時候需先給定一個初試值,才能夠訓練,然後一點點地更新,但是不同的初始化方法,訓練的效果可能會截然不同。本文主要記錄一下不同的初始化的方法,以及相應的效果。

筆者正在學習的Andrew Ng的DeepLearning.ai提供了相應的模型框架和數據,我們這裏要自己設置的就是不同的初值。

數據可視化之後是這樣的:

我們需要做的就是把上面的紅點和藍點分類。

一、直接把參數都初始化爲0

這是大家可以想到的最簡單的方法,也確實很多其他的地方都採用0初值,那神經網絡中這樣做是否可行呢? 在python中,可以用np.zeros((維度)) 來給一個向量/矩陣賦值0, 於是,對於L層神經網絡,可這樣進行0-initialization:

for l in range(1,L): #總共L層,l爲當前層
    W = np.zeros((num_of_dim[l],num_of_dim[l-1])) # W的維度是(當前層單元數,上一層單元數)
    b = np.zeros((num_of_dim[l],1)) # b的維度是(當前層單元數,1)

通過這樣的初值,我們run一下模型,得到的cost-iteration曲線以及在訓練集、測試集上面的準確率如下:

可以發現,壓根就沒訓練!得到的模型跟瞎猜沒有區別。

爲什麼呢?

我們看看神經網絡的結構圖:

這是一個3層神經網絡,可以看出,神經網絡結構是十分對稱的,不管有幾層。 當我們把所有的參數都設成0的話,那麼上面的每一條邊上的權重就都是0,那麼神經網絡就還是對稱的,對於同一層的每個神經元,它們就一模一樣了。 這樣的後果是什麼呢?我們知道,不管是哪個神經元,它的前向傳播和反向傳播的算法都是一樣的,如果初始值也一樣的話,不管訓練多久,它們最終都一樣,都無法打破對稱(fail to break the symmetry),那每一層就相當於只有一個神經元,最終L層神經網絡就相當於一個線性的網絡,如Logistic regression,線性分類器對我們上面的非線性數據集是“無力”的,所以最終訓練的結果就瞎猜一樣。

因此,我們決不能把所有參數初始化爲0,同樣也不能初始化爲任何相同的值,因爲我們必須“打破對稱性”!

二、隨機初始化

好,不用0,咱們隨機給一批值總可以吧。確實可以!咱們看看: 【下面的演示會試試多種參數或超參數,爲了方便大家看,我分4步:①②③④】

①隨機初始化

python中,隨機初始化可以用 np.random.randn(維度) 來隨機賦值: 於是前面的代碼改成:

for l in range(1,L): #總共L層,l爲當前層
    W = np.random.randn(num_of_dim[l],num_of_dim[l-1]) # W的維度是(當前層單元數,上一層單元數)
    b = np.zeros((num_of_dim[l],1)) # b的維度是(當前層單元數,1)

這裏有三點需要說明一下:

  1. b不用隨機初始化,因爲w隨機之後,已經打破對稱,b就一個常數,無所謂了
  2. random.rand()是在0~1之間隨機,random.randn()是標準正態分佈中隨機,有正有負
  3. np.zeros(())這裏是兩個括號,random.randn()是一個括號,奇怪的很,就記着吧

那看看run出來的效果如何呢:

效果明顯比0初始化要好多了,cost最後降的也比較低,準確率也不錯,92%。給分類效果可視化:

我們接着試試,如果把隨機初始化的值放大一點會出現什麼:

②放大版隨機初始化

for l in range(1,L): #總共L層,l爲當前層
    W = np.random.randn(num_of_dim[l],num_of_dim[l-1])*10 # W的維度是(當前層單元數,上一層單元數)
    b = np.zeros((num_of_dim[l],1)) # b的維度是(當前層單元數,1)

上面的代碼中,我們給W最後多乘以10,run的效果: 【注意啊,乘以10不一定就是變大,因爲我們的w的隨機取值可正可負,所以乘以10之後,正數更大,負數更小】

咦~~ 真o心 ~~

準確率明顯降低了許多,到86%。

爲什麼把隨機初始化的值放大就不好了呢?

我們看看神經網絡中常用的sigmoid函數:

這傢伙,中間的斜率大,兩邊的斜率小還趨於零。所以當我們把隨機的值乘以10了之後,我們的初值會往兩邊跑,那麼我們的梯度下降就會顯著變慢,可能迭代半天,才下降一點點。

這就是問題的癥結。

我們上面的實驗,可以從圖的橫座標看出,都是設定的一樣的迭代次數(iteration number):15000次,因此,在相同的迭代次數下,放大版的隨機初始化的模型的學習就像一個“笨學生”,沒別人學的多,因此效果就更差

爲了驗證我說的,我們可以試試吧迭代次數加大,看看我說的是不是對的:

③增大迭代次數

測試了好久。。。 然後打臉了。。。

不過還是值得玩味~~

我把迭代次數顯示設爲60000,也就是增大了4,5倍,結果cost function後來下降十分十分緩慢,最後效果還不如之前的。然後我再把迭代次數增加到了160000,相當於比一開始增大了10倍多,結果….

可以看到,cost基本從20000次迭代之後就穩定了,怎麼都降不下去了,實際上是在降低,但是十分十分十分X10地緩慢。難道這就是傳說中的梯度消失??? 所以結果並沒有我想象地把迭代次數加大,就可以解決這個問題,實際上,可以看到,在訓練集上準確度確實上升了,所以說明確實模型有所改進,只不過改進的太緩慢,相當於沒有改進。

仔細分析了一下,由於W太大或者太小,導致激活函數對w的倒數趨於零,那麼計算cost對w的導數也會趨於零,所以下降如此緩慢也是可以理解。

好,放大的效果如此差,我們縮小試試?

④縮小版隨機初始化

還是回到迭代14000次,這次把w除以10看看:

嘿~縮小結果甚至更差!連圈圈都沒有了。

上面這個圖,說明學習到的模型太簡單了,因爲我們把w都除以10,實際上就接近0了,深度學習中我們認爲參數越大,模型越複雜;參數越小,模型越簡單。所以除以10之後,參數太小了,模型就too simple了,效果當然不好。

最後再試一次吧,再多的話大家都煩了我也煩了。

上面乘以10和除以10,效果都很差,那我們試一箇中間的,比如:除以3(真的是隨便試試)

好了好了,終於提高了!這個準確率是目前的最高水平了!

可見,只要找到一個恰當的值來縮小,是可以提高準確率的。但是,這裏除以三是我拍腦門出來的,不能每次都這麼一個個地試吧,有沒有一個穩健的,通用的方法呢?

有!接着看:

三、何氏初試法(He Initialization)(不知道是不是何,我音譯的)

上面試了各種方法,放大縮小都不好,無法把握那個度。還好,總有大神爲我們鋪路,論文He et al., 2015.中提出了一種方法,我們稱之爲He Initialization,它就是在我們隨機初始化了之後,乘以

這樣就避免了參數的初始值過大或者過小,因此可以取得比較好的效果,代碼也很簡單,用np.sqrt()來求平方根:

for l in range(1,L): #總共L層,l爲當前層
    W = np.random.randn(num_of_dim[l],num_of_dim[l-1])**np.sqrt(2/num_of_dim[l-1]) # W的維度是(當前層單元數,上一層單元數)
    b = np.zeros((num_of_dim[l],1)) # b的維度是(當前層單元數,1)

取得的效果如下:

嘖嘖嘖,看這效果,看這優美的損失曲線,看着卓越的準確率… …

以後就用你了,He Initialization !

其實吧,He Initialization是推薦針對使用ReLU激活函數的神經網絡使用的,不過對其他的激活函數,效果也不錯。

還有其他的類似的一些好的初始化方法,例如:

推薦給sigmoid的Xavier Initialization:隨機化之後乘以

總結一下:

  • 神經網絡不可用0來初始化參數!
  • 隨機賦值是爲了打破對稱性,使得不同的神經元可以有不同的功能
  • 推薦在初始化的時候使用He Initialization

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章