天天看點

[阿裡DIN] 從論文源碼梳理深度學習幾個概念[阿裡DIN] 從論文源碼梳理深度學習幾個概念

[阿裡DIN] 從論文源碼梳理深度學習幾個概念

目錄

  • [阿裡DIN] 從論文源碼梳理深度學習幾個概念
    • 0x00 摘要
    • 0x01 全連接配接層
      • 1.1 全連接配接層作用
      • 1.2 CNN
      • 1.3 RNN
      • 1.4 DIN使用
    • 0x02 prelu
      • 2.1 激活函數作用
      • 2.2 prelu 和 sigmoid 之間對比
      • 2.3 DIN使用
    • 0x03 Batch Normalization
      • 3.1 DIN使用
      • 3.2 BN層作用
      • 3.3 BN思想
        • 3.3.1 Internal Covariate Shift
        • 3.3.2 基本思想
    • 0xFF 參考s

0x00 摘要

本文基于阿裡推薦 DIN 和 DIEN 代碼,梳理了下深度學習一些概念,以及TensorFlow中的相關實作。

因為篇幅所限,是以之前的整體代碼講解中,很多細節沒有深入,是以本文會就這些細節進行探讨,旨在幫助小夥伴們詳細了解每一的步驟以及為什麼要這樣做。

涉及概念有:全連接配接層,prelu,batch normalization等。

0x01 全連接配接層

1.1 全連接配接層作用

全連接配接層則起到将學到的 “分布式特征表示” 映射到 ”樣本标簽空間” 的作用。

全連接配接的核心操作就是矩陣向量乘積 y = Wx。本質就是由一個特征空間線性變換到另一個特征空間。目标空間的任一維——也就是隐層的一個 cell——都認為會受到源空間的每一維的影響。可以說,目标向量是源向量的權重和。

泰勒公式就是用多項式函數去拟合光滑函數。全連接配接層中一層的一個神經元就可以看成一個多項式,我們用許多神經元去拟合資料分布,但是隻用一層 fully-connected layer 有時候沒法解決非線性問題,而如果有兩層或以上 fully-connected layer 就可以很好地解決非線性問題了。

1.2 CNN

全連接配接層之前的作用是提取特征,全連接配接層的作用是分類。

卷積取的是局部特征,全連接配接就是把以前的局部特征重新通過權值矩陣組裝成完整的圖。因為用到了所有的局部特征,是以叫全連接配接。

我們現在的任務是去差別一圖檔是不是貓:

在CNN中,全連接配接層把特征representation整合到一起,輸出為一個值。這樣做可以大大減少特征位置對分類帶來的影響。

比如識别一個圖檔中的貓,貓在不同的位置,輸出的 feature 值相同,但是位置不同。對于電腦來說,特征值相同,但是特征值位置不同,那分類結果也可能不一樣,而這時全連接配接層 filter 的作用就相當于

喵在哪我不管

我隻要喵

于是我讓filter去把這個喵找到,實際就是把 feature map 整合成一個值:這個值大,哦,有喵;這個值小,那就可能沒喵,和這個喵在哪關系不大了有沒有,魯棒性有大大增強了有沒有

最後總結就是,卷積神經網絡中全連接配接層的設計,屬于人們在傳統特征提取+分類思維下的一種"遷移學習"思想,但在這種end-to-end的模型中,其用于分類的功能其實是被弱化了,而全連接配接層參數過多的缺點也激勵着人們設計出更好的模型替代之達到更好的效果。同時,也将促進我們更深入地探讨其中的奧秘。

1.3 RNN

為什麼模型最後需要加入全連接配接層?

從模型拟合能力上說,不是必須的;從模型設計角度上說,是常見的。因為假如用RNN做一維時序資料的回歸任務,RNN的隐元包含的是一些時序狀态的資訊,主要是在time上的拟合能力,與問題定義的輸出之間通常具有比較明顯的gap,和任務想要獲得的輸出之間還存在一個映射,最後的fc就是用來完成這道任務的。

1.4 DIN使用

DIN中,大量使用了FCN,比如:

query = tf.layers.dense(query, facts_size, activation=None, name='f1' + stag)
query = prelu(query)           

複制

0x02 prelu

2.1 激活函數作用

首先我們要明白激活函數的作用是:增加模型的非線性表達能力。

深度學習的目的是用一堆神經元堆出一個函數大緻的樣子,然後通過大量的資料去反向拟合出這個函數的各個參數,最終勾勒出函數的完整形狀。

那如果激活函數隻是線性函數,那一層層的線性函數堆起來還是線性的,這年頭線性函數能幹啥呀?

肯定不行,這樣整個網絡表現能力有限,是以要引入非線性的激活函數進來。

就是鉛筆不夠畫的,咱得上帶顔色、筆觸更豐富的油畫筆。

2.2 prelu 和 sigmoid 之間對比

具體 prelu 和 sigmoid 之間對比如下。

  • 首先,sigmoid 有一個“梯度消失”的問題。
    • 梯度消失什麼意思呢?就是我們希望對每個神經元,激勵函數都能盡量區分出z值變化,這樣每個神經元的表達能力更強,但sigmoid明顯在|z|>4的區間的梯度就不夠看了,即它的梯度消失了。
    • 相比之下,ReLU輸出就很穩定,因為他z>0區間就是一個線性函數!不存在sigmoid的梯度消失的問題。
  • 其次,另一個ReLU給力的地方就是稀疏度問題。
    • 我們希望每個神經元都能最大化的發揮它篩選的作用,符合某一個特征的中間值,使勁兒放大;不符合的,一刀切掉。
    • 反觀sigmoid就要黏糊的多。這個函數是很對稱很美,但它面對負的z值仍然剪不斷理還亂,會輸出一個小的激勵值(tanh會好一些但仍不能避免),形成所謂的“稠密表示”。
  • 最後,ReLU運算速度快,max肯定比幂指數快的多。

2.3 DIN使用

DIN中使用如下,可以看到,在全連接配接函數中,Batch Normalization 之後就接入了激活函數:

def prelu(_x, scope=''):
    """parametric ReLU activation"""
    with tf.variable_scope(name_or_scope=scope, default_name="prelu"):
        _alpha = tf.get_variable("prelu_"+scope, shape=_x.get_shape()[-1],
                                 dtype=_x.dtype, 
                                 initializer=tf.constant_initializer(0.1))
        return tf.maximum(0.0, _x) + _alpha * tf.minimum(0.0, _x)
      
def build_fcn_net(self, inp, use_dice = False):
    bn1 = tf.layers.batch_normalization(inputs=inp, name='bn1')
    dnn1 = tf.layers.dense(bn1, 200, activation=None, name='f1')
    if use_dice:
        dnn1 = dice(dnn1, name='dice_1')
    else:
        dnn1 = prelu(dnn1, 'prelu1')              

複制

0x03 Batch Normalization

3.1 DIN使用

在DIN中,全連接配接層建構中,上來就進行了Batch Normalization。

def build_fcn_net(self, inp, use_dice = False):
    bn1 = tf.layers.batch_normalization(inputs=inp, name='bn1')
    dnn1 = tf.layers.dense(bn1, 200, activation=None, name='f1')
    if use_dice:
        dnn1 = dice(dnn1, name='dice_1')
    else:
        dnn1 = prelu(dnn1, 'prelu1')           

複制

是以我們要看看 Batch Normalization 究竟起到什麼作用。

3.2 BN層作用

BN層的作用主要有三個:

  1. 加快網絡的訓練和收斂的速度;
  2. 控制梯度爆炸防止梯度消失;
  3. 防止過拟合;

3.3 BN思想

機器學習領域有個很重要的假設:IID獨立同分布假設,就是假設訓練資料和測試資料是滿足相同分布的,這是通過訓練資料獲得的模型能夠在測試集獲得好的效果的一個基本保障。BatchNorm就是在深度神經網絡訓練過程中使得每一層神經網絡的輸入保持相同分布的。

3.3.1 Internal Covariate Shift

“Internal Covariate Shift”問題是指:對于深度學習這種包含很多隐層的網絡結構,在訓練過程中,因為各層參數老在變,是以每個隐層都會面臨covariate shift的問題。Internal指的是深層網絡的隐層,是發生在網絡内部的事情,而不是covariate shift問題隻發生在輸入層。

是以,就有了Batch Normalization的基本思想:能不能讓每個隐層節點的激活輸入分布固定下來呢?這樣就避免了“Internal Covariate Shift”問題了。

BN不是憑空拍腦袋拍出來的好點子,它是有啟發來源的:之前的研究表明如果在圖像進行中對輸入圖像進行白化(Whiten)操作的話——所謂白化,就是對輸入資料分布變換到0均值,機關方差的正态分布——那麼神經網絡會較快收斂,那麼BN作者就開始推論了:圖像是深度神經網絡的輸入層,做白化能加快收斂,那麼其實對于深度網絡來說,其中某個隐層的神經元是下一層的輸入,意思是其實深度神經網絡的每一個隐層都是輸入層,不過是相對下一層來說而已,那麼能不能對每個隐層都做白化呢?這就是啟發BN産生的原初想法,而BN也确實就是這麼做的,可以了解為對深層神經網絡每個隐層神經元的激活值做簡化版本的白化操作。

3.3.2 基本思想

詳細解釋Batch Normalization的基本思想:對于每個隐層神經元,把逐漸向非線性函數映射後向取值區間極限飽和區靠攏的輸入分布強制拉回到均值為0方差為1的比較标準的正态分布,使得非線性變換函數的輸入值落入對輸入比較敏感的區域,以此避免梯度消失問題。因為梯度一直都能保持比較大的狀态,是以很明顯對神經網絡的參數調整效率比較高,就是變動大,就是說向損失函數最優值邁動的步子大,也就是說收斂地快。

有的讀者一般會提出一個疑問:如果都通過BN,那麼不就跟把非線性函數替換成線性函數效果相同了?這意味着什麼?我們知道,如果是多層的線性函數變換其實這個深層是沒有意義的,因為多層線性網絡跟一層線性網絡是等價的。這意味着網絡的表達能力下降了,這也意味着深度的意義就沒有了。

是以BN為了保證非線性的獲得,對變換後的滿足均值為0方差為1的x又進行了scale加上shift操作(y=scale*x+shift),每個神經元增加了兩個參數scale和shift參數,這兩個參數是通過訓練學習到的,意思是通過scale和shift把這個值從标準正态分布左移或者由移一點并長胖一點或者變瘦一點,每個執行個體挪動的程度不一樣,這樣等價于非線性函數的值從正中心周圍的線性區往非線性區動了動。

核心思想是想找到一個線性和非線性的較好平衡點,既能享受非線性的較強表達能力的好處,又避免太靠非線性區兩頭使得網絡收斂速度太慢。