引言
本着“凡我不能創造的,我就不能了解”的思想,本系列文章會基于純Python以及NumPy從零建立自己的深度學習架構,該架構類似PyTorch能實作自動求導。
要深入了解深度學習,從零開始建立的經驗非常重要,從自己可以了解的角度出發,盡量不使用外部完備的架構前提下,實作我們想要的模型。本系列文章的宗旨就是通過這樣的過程,讓大家切實掌握深度學習底層實作,而不是僅做一個調包俠。
我們已經了解了線性回歸和邏輯回歸,本文來學習深度學習中神經網絡的基礎建構——神經元,以及常見的激活函數。
神經元
神經網絡和邏輯回歸很像,但神經網絡更強大。而神經網絡是由很多個神經元(Neuron)組成的。一個神經元将實數集作為輸入,然後應用某種運算,産生一個實數輸出。
在神經元内部,如上圖所示,神經元首先計算輸入的權重和,然後加上偏置項。給定輸入,每個輸入對應一個權重,得到權重和:
通常使用向量的形式描述更加友善。這樣由向量和标量,以及輸入向量來描述:
注意這裡得到的隻是一個實數(标量)。
最後,我們不是直接使用作為輸出,神經元内部應用一個非線性函數到上:
這裡的非線性函數稱為激活函數,該函數的輸出值稱為激活值,我們已經見過的一種激活函數是Sigmoid函數:
這裡神經元的輸出和激活值相同,但在神經網絡中,我們通常用表示整個網絡最終的輸出。把代入,得到神經元的輸出:
除了Sigmoid之外,還有很多其他比較常見的激活函數。
常見激活函數
激活函數(activation function)通過計算權重和并加上偏置來确定神經元是否應該被激活,大多數激活函數都是非線性的。所有
ReLU
最常用的激活函數是修正線性單元(Rectified linear unit,ReLU),提供了一種非常簡單的非線性變換。給定元素,ReLU函數被定義為該元素與的最大值:
ReLU函數通過将相應的激活值設為,僅保留正元素并丢棄所有負元素。我們可以畫出函數的圖形感受一下:
from metagrad.functions import *
from metagrad.utils import plot
if __name__ == '__main__':
x = Tensor.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = relu(x)
plot(x.numpy(), y.numpy(), 'x', 'relu(x)', random_fname=True, figsize=(5, 2.5))
當輸入為負時,ReLU函數的導數為,當輸入為正時,ReLU函數的導數為。當輸入為時,我們讓其導數也為。
y.backward(Tensor.ones_like(x))
plot(x.numpy(), x.grad.numpy(), 'x', 'grad of relu', figsize=(5, 2.5))
由于ReLU的簡單性,沒有包含,導緻它的計算效率極高。同時它的梯度要麼為,要麼為,這使得優化變現得更好,減輕了困擾神經網絡的梯度消息問題。
ReLU導數的函數圖形如上圖所示,我們可以看到,在的一側,梯度值永遠是。是以,在反向傳播的過程中,可能有些神經元的權重不會被更新(因為導數為)。這可能會導緻永不激活的死節點(神經元)。這個問題可以被ReLU的變種:Leaky ReLU解決。
Leaky ReLU
Leaky ReLU是ReLU的改進版本,主要用于解決上面跳到的死節點問題,通過給所有負值賦予一個小的正斜率來解決
通常這裡的,為了看出效果。在畫圖時讓,我們看一下它的圖形:
y = leaky_relu(x)
plot(x.numpy(), y.numpy(), 'x', 'leaky relu(x)', random_fname=True, figsize=(5, 2.5))
Leaky ReLU的優點與ReLU相同,同時對于負輸入,其導數也變成了一個非零值(即)。
從上圖可以看到,對于的一側,它們也有非零的導數。不至于出現死節點,但是由于通常很小,導緻在在此側的模型參數學習緩慢。
除了Leaky ReLU外,類似地還有兩種變體,分别是Parametric ReLU和Randomized Leaky ReLU。
Parametric ReLU稱為參數化的ReLU,即令Leaky ReLU中的變成了一個可學習的參數。
而Randomized Leaky ReLU讓取自一個連續均勻機率分布。
Exponential Linear Unit
ELU(Exponential Linear Unit)也是ReLU的一種變體,類似Leaky ReLU修改在側的斜率,但在負區域不是一條直線,而是一條對數曲線。
通常,我們畫出ELU的圖像:
y = elu(x)
plot(x.numpy(), y.numpy(), 'x', 'elu(x)', random_fname=True, figsize=(5, 2.5))
ELU在負值部分緩慢變得平滑,直到輸出等于,且是一個可調整的參數,它控制着ELU負值部分在何時飽和。但是引入了。其導數的圖像為:
SoftPlus
SoftPlus函數與ReLU函數接近,但比較平滑,也是單邊抑制的。
其中預設為,随着的增加,該函數越來越像ReLU。
我們看一下預設情況下的函數圖像:
y = softplus(x, beta=10)
plot(x.numpy(), y.numpy(), 'x', 'softplus(x)', random_fname=True, figsize=(5, 2.5))
當時,我們看一下函數圖像:
y = softplus(x, beta=10)
plot(x.numpy(), y.numpy(), 'x', r'softplus(x) with $\beta$ = 10', random_fname=True, figsize=(5, 2.5))
其導數為:
當,其導數就是。我們來看下其導數圖像:
Swish
Swish在更深層次的模型上顯示出比ReLU更好的性能。Swish的輸入從負無窮到正無窮。函數定義為
相當于是對輸入進行了門控(通過函數),我們看一下它的圖像:
y = swish(x)
plot(x.numpy(), y.numpy(), 'x', 'swish(x)', random_fname=True, figsize=(5, 2.5))
它的曲線都是光滑的,且處處可導。當增大時,函數值趨于無窮大;當減小時,函數值趨于常數。
Swish函數的導數為下面的公式:
其導數的圖像為:
Swish的特性:
- 無上邊界:不像sigmoid和tanh函數,Swish沒有上邊界的,因為它避免了在接近零的梯度中緩慢的訓練時間——像sigmoid或tanh這樣的函數是有界的,是以需要小心地初始化網絡,以保持在這些函數的界限内。
- 曲線的平滑性:平滑性在泛化和優化中起着重要的作用。與ReLU不同,Swish是一個平滑的函數,這使得它對初始化權值和學習率不那麼敏感。
- 有下邊界:這有助于增強正則化效果(左側慢慢接近于,一定程度過濾掉一部分資訊,起到正則化的效果)。
Sigmoid
Sigmoid函數将輸入壓縮為區間上的輸出。是以,Sigmoid函數通常稱為擠壓函數(squashing function):
當我們想要将輸出看成二分類的機率時,sigmoid此時最常用。然而,sigmoid在隐藏層中較少使用,它通常被更簡單、更容易訓練的ReLU所取代。
下面我們畫出sigmoid函數。
y = sigmoid(x)
plot(x.numpy(), y.numpy(), 'x', 'sigmoid(x)', random_fname=True, figsize=(5, 2.5))
其導數計算如下:
其導數的圖像為:
在深層網絡中,Sigmoid存在三個問題:
- 飽和的神經元會讓梯度消失,即在較大的正數或負數作為輸入的時候,梯度就會變成零,使得神經元基本不能更新。
- 其輸出不是以為中心的,而是。我們知道,在深層網絡中,由于上一層使用的是Sigmoid激活函數,導緻該層的輸入都是正數。即該層的梯度取決于,要麼都是正的,要麼都是負的,出現了zig zag問題。如下圖所示,假設最佳更新路線是藍線所示,由于zig zag問題,使其優化變成緩慢。
- 指數計算耗時
Tanh
tanh是sigmoid函數的變種,它的函數值變成範圍從到,即變成了以為中心的。
我們來畫出該函數的圖像:
y = tanh(x)
plot(x.numpy(), y.numpy(), 'x', 'tanh(x)', random_fname=True, figsize=(5, 2.5))
tanh函數具有平滑可微性,和将離群值映射到均值的良好性質。
為什麼說tanh函數是sigmoid函數的變種呢?我們來推導一下:
是以,我們可以看到tanh隻是sigmoid函數的縮放版本。
tanh函數的導數是:
其中;
其導數圖像如下所示:
可以看到,當輸入接近于時,tanh函數的導數接近于最大值。而輸入在任一方向上越遠離點,導數越接近。
Tanh的缺點類似Sigmoid,不過它是以為中心的,避免了zig zag問題。
如何選擇激活函數
我們看了這麼多激活函數,到底要如何選擇呢?
在深層網絡中,首先要嘗試ReLU,它具有速度快的優點,如果效果欠佳;
那麼嘗試Leaky ReLU;
或者tanh這種以零為中心的;
完整代碼
References
- DIVE INTO DEEP LEARNING
- Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification