1. 程式人生 > >Question Of AI Model Training

Question Of AI Model Training

1 模型訓練基本步驟

  1. 準備原始資料,定義神經網路結構及前向傳播演算法
  2. 定義loss,選擇反向傳播優化演算法
  3. 生成Session,在訓練資料進行迭代訓練,使loss到達最小
  4. 在測試集或者驗證集上對準確率進行評估

2 模型訓練難點及解決方法

2.1 收斂速度慢,訓練時間長

  深度學習其實就是一個反覆調整模型引數的過程,得力於GPU等硬體效能的提升,使得複雜的深度學習訓練成為了可能。收斂速度過慢,訓練時間過長,一方面使得相同總訓練時間內的迭代次數變少,從而影響準確率,另一方面使得訓練次數變少,從而減少了嘗試不同超引數的機會。因此,加快收斂速度是一大痛點。那麼怎麼解決它呢?

2.1.1 設定合理的初始化權重w和偏置b

  深度學習通過前向計算和反向傳播更新權重,不斷調整引數,來提取最優特徵,以達到預測的目的。其中調整的引數就是weight和bias,簡寫為w和b。

  根據奧卡姆剃刀法則,模型越簡單越好,我們以線性函式這種最簡單的表示式來提取特徵,也就是​ f(x) = w * x + b

  深度學習訓練時幾乎所有的工作量都是來求解神經網路中的w和b。模型訓練本質上就是調整w和b的過程,如果將他們初始化為一個合理的值,那麼就能夠加快收斂速度。怎麼初始化w和b呢?

  我們一般使用截斷的正態分佈(也叫高斯分佈)來初始化w。如下

# 權重weight,標準差0.1。truncated_normal截斷的正態分佈來初始化weight。權重初始化很有講究的,會決定學習的快慢
def weight_variable(shape, vname): initial = tf.truncated_normal(shape, stddev=0.1, name=vname) return tf.Variable(initial)

tf.truncated_normal定義如下

tf.truncated_normal(
    shape,            # 正態分佈輸出資料結構,1維tensor
    mean=0.0,        # 平均值,預設為0.我們一般取預設值0
    stddev=1.0,        # 標準差
    dtype=tf.float32,        #
輸出資料型別 seed=None, # 隨機分佈都會有一個seed來決定分佈 name=None )

截斷的正態分佈如下圖:

  左圖為標準正態分佈,也叫高斯分佈,利用TensorFlow中的tf.random_normal()即可得到x取值範圍負無窮到正無窮內的值。所有的y值加起來概率為1。初始化w時,我們沒必要將w初始化為很大或很小的數。故更傾向於使用截斷正態分佈,如右圖。它和標準正態分佈的區別在於,限制了x取值必須在[-2 x stddev, 2 x stddev]之間。

  b由於是加和關係,對收斂速度影響不大。我們一般將它初始化為0,如下。

# 偏置量bias,初始化為0,偏置可直接使用常量初始化
def bias_variable(shape, vname):
  initial = tf.constant(0, shape=shape, name=vname)
  return tf.Variable(initial)

2.1.2 優化學習率

  模型訓練就是不斷嘗試和調整不同的w和b,那麼每次調整的幅度是多少呢,這個就是學習率。w和b是在一定範圍內調整的,那麼增大學習率不就減少了迭代次數,也就加快了訓練速度了嗎?路雖長,步子邁大點不就行了嗎?非也,步子邁大了可是會扯到蛋的!深度學習中也是如此,學習率太小,會增加迭代次數,加大訓練時間。但學習率太大,容易越過區域性最優點,降低準確率。

  那有沒有兩全的解決方法呢,有!我們可以一開始學習率大一些,從而加速收斂。訓練後期學習率小一點,從而穩定的落入區域性最優解。使用Adam,Adagrad等自適應優化演算法,就可以實現學習率的自適應調整,從而保證準確率的同時加快收斂速度。

 如上圖所示,隨著迭代次數的增加,學習率從0.1逐步衰減為0.02以下。

'''
指數級衰減學習率
'''
global_step = tf.Variable(0)
#生成學習率,即每一百步就相應學習率乘以衰減率
learning_ratee = tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase=True)
#使用指數衰減率的學習率,在minimize函式中傳入global_step將自動更新
#globak_step引數,從而使得學習率也得到相應更新
learining_stepp = tf.train.GradientDescentOptimizer(learning_ratee).minimize(loss,global_step=global_step)

2.1.3 網路節點輸入值正則化 batch normalization

  神經網路訓練時,每一層的輸入分佈都在變化。不論輸入值大還是小,我們的學習率都是相同的,這顯然是很浪費效率的。而且當輸入值很小時,為了保證對它的精細調整,學習率不能設定太大。那有沒有辦法讓輸入值標準化得落到某一個範圍內,比如[0, 1]之間呢,這樣我們就再也不必為太小的輸入值而發愁了。

  辦法當然是有的,那就是正則化!由於我們學習的是輸入的特徵分佈,而不是它的絕對值,故可以對每一個mini-batch資料內部進行標準化,使他們規範化到[0, 1]內。這就是Batch Normalization,簡稱BN。由大名鼎鼎的inception V2提出。它在每個卷積層後,使用一個BN層,從而使得學習率可以設定為一個較大的值。使用了BN的inceptionV2,只需要以前的1/14的迭代次數就可以達到之前的準確率,大大加快了收斂速度。

2.1.4 採用更先進的網路結構,減少引數量

  訓練速度慢,歸根結底還是網路結構的引數量過多導致的。減少引數量,可以大大加快收斂速度。採用先進的網路結構,可以用更少的引數量達到更高的精度。如inceptionV1引數量僅僅為500萬,是AlexNet的1/12, 但top-5準確率卻提高了一倍多。如何使用較少的引數量達到更高的精度,一直是神經網路結構研究中的難點。目前大致有如下幾種方式

  1. 使用小卷積核來代替大卷積核。VGGNet全部使用3x3的小卷積核,來代替AlexNet中11x11和5x5等大卷積核。小卷積核雖然引數量較少,但也會帶來特徵面積捕獲過小的問題。inception net認為越往後的卷積層,應該捕獲更多更高階的抽象特徵。因此它在靠後的卷積層中使用的5x5等大面積的卷積核的比率較高,而在前面幾層卷積中,更多使用的是1x1和3x3的卷積核。
  2. 使用兩個串聯小卷積核來代替一個大卷積核。inceptionV2中創造性的提出了兩個3x3的卷積核代替一個5x5的卷積核。在效果相同的情況下,引數量僅為原先的3x3x2 / 5x5 = 18/25
  3. 1x1卷積核的使用。1x1的卷積核可以說是價效比最高的卷積了,沒有之一。它在引數量為1的情況下,同樣能夠提供線性變換,relu啟用,輸入輸出channel變換等功能。VGGNet創造性的提出了1x1的卷積核
  4. 非對稱卷積核的使用。inceptionV3中將一個7x7的卷積拆分成了一個1x7和一個7x1, 卷積效果相同的情況下,大大減少了引數量,同時還提高了卷積的多樣性。
  5. depthwise卷積的使用。mobileNet中將一個3x3的卷積拆分成了串聯的一個3x3 depthwise卷積和一個1x1正常卷積。對於輸入channel為M,輸出為N的卷積,正常情況下,每個輸出channel均需要M個卷積核對輸入的每個channel進行卷積,併疊加。也就是需要MxN個卷積核。而在depthwise卷積中,輸出channel和輸入相同,每個輸入channel僅需要一個卷積核。而將channel變換的工作交給了1x1的卷積。這個方法在引數量減少到之前1/9的情況下,精度仍然能達到80%。
  6. 全域性平均池化代替全連線層。這個才是大殺器!AlexNet和VGGNet中,全連線層幾乎佔據了90%的引數量。inceptionV1創造性的使用全域性平均池化來代替最後的全連線層,使得其在網路結構更深的情況下(22層,AlexNet僅8層),引數量只有500萬,僅為AlexNet的1/12
  網路結構的推陳出新,先進設計思想的不斷提出,使得減少引數量的同時提高準確度變為了現實。引數量的減少,一方面加快了收斂速度,減少了訓練時間,另一方面減小了模型體積,另外還能加快預測時間,提高實時性。所以一直以來減少引數量都是一個十分重要的議題

2.2 線性模型的侷限性

  根據奧卡姆剃刀法則,我們使用了最簡單的線性模型,也就是wx+b,來表徵了神經網路。線性模型的特點是,任意線性模型的組合仍然是線性模型。不論我們採用如何複雜的神經網路,它仍然是一個線性模型。然而線性模型能夠解決的問題畢竟是有限的,所以必須在神經網路中增加一些非線性元素。

2.2.1 啟用函式的使用

  在每個卷積後,加入一個啟用函式,已經是通用的做法,相信大家都知道。啟用函式,如relu,tanh,sigmod都是非線性函式,一方面可以增加模型的非線性元素,另一方面可以降低梯度彌散問題(我們後面詳細講解)。目前使用較多的就是relu函式。他模擬了生物學上的閾值響應機制,利用人腦只對大於某個值的訊號才產生響應的機制,提出了單側抑制的理念。它的表示式很簡單,f(x)=max(0,x)。當x>0時,y=x, x<0時,y=0. 如下圖所示。

  相比於tanh和sigmod,relu的優點有:

  計算速度快,容易收斂。relu就是一個取max的函式,沒有複雜的運算,故計算速度很快。相比於tanh,收斂速度可加快6倍,梯度不會大幅縮小。x>0時,relu的梯度為1(梯度還不懂是啥意思的同學最好翻下數學書,梯度簡單理解就是偏導數),故相比sigmod這種x稍微遠離0,梯度就會大幅減小的函式,不會使得梯度縮小,從而引發多層傳播後的梯度彌散問題。

2.2.2 兩個小卷積核的疊加代替一個大卷積核

  啟用函式可是一個增加非線性的大法寶,但我們一般只能在卷積完之後再使用它。那怎麼增加它的使用場景呢?增加捲積層不就行了嗎。inception V2創造性的提出了用兩個3x3的卷積核代替一個5x5的卷積核。每次卷積後,都使用一次relu非線性啟用。如下圖。


2.2.3 1x1小卷積核的使用

  1x1的卷積核應該是價效比最高的卷積,它在引數量為1的情況下,同樣能夠提供線性變換,relu啟用,輸入輸出channel變換等功能。inceptionV1利用Network in Network的思想,提出了inception module這一結構,它在每個並行分支的最前面,使用了一個1x1的卷積,卷積後緊跟一個relu啟用。從而大大增加了relu的使用率。從而提高了模型的非線性特徵。

2.3 過擬合問題

2.3.1 輸入增強,增大樣本量

  收集更多且更全的樣本,能有效降低過擬合。但尋找樣本本來就是一件很費力的事情,我們到哪兒去尋找更多更全的樣本呢。素材整理和資料獲取成為了深度學習的一大瓶頸,否則再牛逼的神經網路結構,也會稱為無米之炊。這也是當前遷移學習變得比較火熱的一大原因(這是後話,就不詳細展開了)。那我們有沒有辦法簡單快捷的增加樣本量呢?

答案是有的,可以使用輸入增強方法。對樣本進行旋轉,裁剪,加入隨機噪聲等方式,可以大大增加樣本數量和泛化性。目前TensorFlow就提供了大量方法進行資料增強,大大方便了我們增加樣本數量。

2.3.2 dropout,減少特徵量

  使用dropout,將神經網路某一層的輸出節點資料隨機丟棄,從而減少特徵量。這其實相當於創造了很多新的隨機樣本。我們可以理解為這是對特徵的一次取樣。一般在神經網路的全連線層使用dropout。

2.4 梯度彌散, 無法使用更深的網路

  深度學習利用正向傳播來提取特徵,同時利用反向傳播來調整引數。反向傳播中梯度值逐漸減小,神經網路層數較多時,傳播到前面幾層時,梯度接近於0,無法對引數做出指導性調整了,此時基本起不到訓練作用。這就稱為梯度彌散。梯度彌散使得模型網路深度不能太大,但我們都知道網路越深,提取的特徵越高階,泛化性越好。因此優化梯度彌散問題就很重要了

2.4.1 relu代替sigmoid啟用函式

  sigmoid函式值在[0,1],ReLU函式值在[0,+無窮]。relu函式,x>0時的導數為1, 而sigmoid函式,當x稍微遠離0,梯度就會大幅減小,幾乎接近於0,所以在反向傳播中無法指導引數更新。

2.4.2 殘差網路

  大名鼎鼎的resNet將一部分輸入值不經過正向傳播網路,而直接作用到輸出中。這樣可以提高原始資訊的完整性了,從而在反向傳播中,可以指導前面幾層的引數的調整了。如下圖所示。

   使用了殘差網路的resNet,將網路深度提高到了152層,大大提高了模型的泛化性,從而提高了預測準確率,並一舉問鼎當年的imageNet冠軍!

3 總結

  深度學習模型訓練是一個很費時間,但也很有技巧的過程。模型訓練中有梯度彌散,過擬合等各種痛點,正是為了解決這些問題,不斷湧現出了各種設計精巧的網路結構。學習時,我們不僅要學習網路結構的設計方式,還要掌握它們的設計思想,瞭解它們是為了解決哪些問題而產生的,以及準確率和效能為何能夠得到提升。

 

 

致謝博主分享:https://blog.csdn.net/u013510838/article/details/79835563