訓練神經網路時如何確定batch size?

夕小瑤發表於2018-07-12

前言

當我們要訓練一個已經寫好的神經網路時,我們就要直面諸多的引數了。這些引數一旦選不好,那麼很有可能讓神經網路跑的還不如感知機。因此在面對神經網路這種容量很大的model前,是很有必要深刻的理解一下各個引數的意義及其對model的影響的。

回顧

簡單回顧一下神經網路一次迭代過程:

訓練神經網路時如何確定batch size?

即,首先選擇n個樣本組成一個batch,然後將batch丟進神經網路,得到輸出結果。再將輸出結果與樣本label丟給loss函式算出本輪的loss,而後就可以愉快的跑BP演算法了(從後往前逐層計算引數之於loss的導數)。最後將每個引數的導數配合步長引數來進行引數更新。這就是訓練過程的一次迭代。

Batch Size

由此,最直觀的引數就是batch的大小——我們可以一次性將整個資料集餵給神經網路,讓神經網路利用全部樣本來計算迭代時的梯度(即傳統的梯度下降法),也可以一次只喂一個樣本(即隨機梯度下降法,也稱線上梯度下降法),也可以取個折中的方案,即每次喂一部分樣本讓其完成本輪迭代(即batch梯度下降法)。

數學基礎不太好的初學者可能在這裡犯迷糊——一次性喂500個樣本並迭代一次,跟一次喂1個樣本迭代500次相比,有區別嗎?

其實這兩個做法就相當於:

第一種:
total = 舊參下計算更新值1+舊參下計算更新值2+...+舊參下計算更新值500 ;
引數 = 舊引數 + total

第二種:
引數1 = 舊引數 + 舊引數下計算更新值1;
引數2 = 新引數1 + 新引數1下計算更新值1;
引數3 = 新引數2 + 新引數2下計算更新值1;
...
引數500 = 新引數500 + 新引數500下計算更新值1;

也就是說,第一種是將引數一次性更新500個樣本的量,第二種是迭代的更新500次引數。當然是不一樣的啦。

那麼問題來了,哪個更好呢?

Which one?

我們首先分析最簡單的影響,哪種做法收斂更快呢?

我們假設每個樣本相對於大自然真實分佈的標準差為σ,那麼根據概率統計的知識,很容易推出n個樣本的標準差為 \sigma/\sqrt{n} (有疑問的同學快翻開概率統計的課本看一下推導過程)。從這裡可以看出,我們使用樣本來估計梯度的時候,1個樣本帶來σ的標準差,但是使用n個樣本區估計梯度並不能讓標準差線性降低(也就是並不能讓誤差降低為原來的1/n,即無法達到σ/n),而n個樣本的計算量卻是線性的(每個樣本都要平等的跑一遍前向演算法)。

由此看出,顯然在同等的計算量之下(一定的時間內),使用整個樣本集的收斂速度要遠慢於使用少量樣本的情況。換句話說,要想收斂到同一個最優點,使用整個樣本集時,雖然迭代次數少,但是每次迭代的時間長,耗費的總時間是大於使用少量樣本多次迭代的情況的。

那麼是不是樣本越少,收斂越快呢?

理論上確實是這樣的,使用單個單核cpu的情況下也確實是這樣的。但是我們要與工程實際相結合呀~實際上,工程上在使用GPU訓練時,跑一個樣本花的時間與跑幾十個樣本甚至幾百個樣本的時間是一樣的!當然得益於GPU裡面超多的核,超強的平行計算能力啦。因此,在工程實際中,從收斂速度的角度來說,小批量的樣本集是最優的,也就是我們所說的mini-batch。這時的batch size往往從幾十到幾百不等,但一般不會超過幾千(你有土豪顯示卡的話,當我沒說)。

那麼,如果我真有一個怪獸級顯示卡,使得一次計算10000個樣本跟計算1個樣本的時間相同的話,是不是設定10000就一定是最好的呢?雖然從收斂速度上來說是的,但!是!

我們知道,神經網路是個複雜的model,它的損失函式也不是省油的燈,在實際問題中,神經網路的loss曲面(以model引數為自變數,以loss值為因變數畫出來的曲面)往往是非凸的,這意味著很可能有多個區域性最優點,而且很可能有鞍點!

插播一下,鞍點就是loss曲面中像馬鞍一樣形狀的地方的中心點,如下圖:

訓練神經網路時如何確定batch size?

(圖片來自《Deep Learning》)

想象一下,在鞍點處,橫著看的話,鞍點就是個極小值點,但是豎著看的話,鞍點就是極大值點(線性代數和最優化演算法過關的同學應該能反應過來,鞍點處的Hessian矩陣的特徵值有正有負。不理解也沒關係,小夕過幾天就開始寫最優化的文章啦~),因此鞍點容易給優化演算法一個“我已經收斂了”的假象,殊不知其旁邊有一個可以跳下去的萬丈深淵。。。(可怕)

回到主線上來,小夕在機器學習入門指導(4)》中提到過,傳統的最優化演算法是無法自動的避開區域性最優點的,對於鞍點也是理論上很頭疼的東西。但是實際上,工程中卻不怎麼容易陷入很差勁的區域性最優點或者鞍點,這是為什麼呢?

暫且不說一些很高深的理論如“神經網路的loss曲面中的區域性最優點與全域性最優點差不太多”,我們就從最簡單的角度想~

想一想,樣本量少的時候會帶來很大的方差,而這個大方差恰好會導致我們在梯度下降到很差的區域性最優點(只是微微凸下去的最優點)和鞍點的時候不穩定,一不小心就因為一個大噪聲的到來導致炸出了區域性最優點,或者炸下了馬(此處請保持純潔的心態!),從而有機會去尋找更優的最優點。

因此,與之相反的,當樣本量很多時,方差很小(咦?最開始的時候好像在說標準差來著,反正方差與標準差就差個根號,沒影響的哈~),對梯度的估計要準確和穩定的多,因此反而在差勁的區域性最優點和鞍點時反而容易自信的呆著不走了,從而導致神經網路收斂到很差的點上,跟出了bug一樣的差勁。

小總結一下,batch的size設定的不能太大也不能太小,因此實際工程中最常用的就是mini-batch,一般size設定為幾十或者幾百。但是!

好像這篇文章的轉折有點多了誒。。。

細心的讀者可能注意到了,這之前我們的討論是基於梯度下降的,而且預設是一階的(即沒有利用二階導數資訊,僅僅使用一階導數去優化)。因此對於SGD(隨機梯度下降)及其改良的一階優化演算法如Adagrad、Adam等是沒問題的,但是對於強大的二階優化演算法如共軛梯度法、L-BFGS來說,如果估計不好一階導數,那麼對二階導數的估計會有更大的誤差,這對於這些演算法來說是致命的。

因此,對於二階優化演算法,減小batch換來的收斂速度提升遠不如引入大量噪聲導致的效能下降,因此在使用二階優化演算法時,往往要採用大batch哦。此時往往batch設定成幾千甚至一兩萬才能發揮出最佳效能。

另外,聽說GPU對2的冪次的batch可以發揮更佳的效能,因此設定成16、32、64、128...時往往要比設定為整10、整100的倍數時表現更優(不過我沒有驗證過,有興趣的同學可以試驗一下~

參考文獻《Deep Learning》

本文轉載自微信訂閱號【夕小瑤的賣萌屋,聽說每個想學機器學習的人到這裡都停不下來了~

相關文章