深度學習中的激活函數


  眾所周知神經網絡單元是由線性單元和非線性單元組成的,一般神經網絡的計算時線性的,而非線性單元就是我們今天要介紹的--激活函數,不同的激活函數得出的結果也是不同的。他們也各有各的優缺點,雖然激活函數有自己的發展歷史,不斷的優化,但是如何在眾多激活函數中做出選擇依然要看我們所實現深度學習實驗的效果

  這篇博客會介紹一些常用的激活函數:Sigmoid、tanh、ReLU、LeakyReLU、maxout。以及一些較為冷門的激活函數:PRelu、ELU、SELU

sigmoid

sigmoid激活函數將輸入映射到(0,1)之間,他的數學函數為:

$$\sigma (z)=\frac{1}{1+e^{-z}}$$

def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))
View Code

  歷史上sigmoid非常常用,但是由於他的兩個缺點,實際很少用了,現在看到sigmoid激活函數,都是在新手教程中做一些簡單的實驗。

優點

  1. 它能夠把輸入的連續實值變換為0和1之間的輸出,適合做概率值的處理。
    • 如果是非常大的負數,那么輸出就是0
    • 如果是非常大的正數,輸出就是1

缺點

1、梯度消失

  我們從上圖可以看出,當x較大或者較小時,sigmoid輸出趨近0或1,導數接近0,而后向傳遞的數學依據是微積分求導的鏈式法則,當前層的導數需要之前各層導數的乘積,幾個小數的相乘,結果會很接近0。Sigmoid導數的最大值是0.25,這意味着導數在每一層至少會被壓縮為原來的1/4,通過兩層后被變為1/16,…,通過10層后為1/1048576。這種情況就是梯度消失。梯度一旦消失,參數不能沿着loss降低的方向優化,

2、不是以零為中心

  通過Sigmoid函數我們可以知道,Sigmoid的輸出值恆大於0,輸出不是0均值(既zero-centerde),這會導致后一層的神經元將得到上一層輸出的非均值的輸入。

  舉例來講$\sigma (\sum_i w_ix_i+b)$,如果$x_i$恆大於0,那么對其$w_i$的導數總是正數或總是負數,向傳播的過程中w要么都往正方向更新,要么都往負方向更新,導致有一種捆綁的效果,使得收斂緩慢。且可能導致陷入局部最小值。當然了,如果按batch去訓練,那么那個batch可能得到不同的信號,所以這個問題還是可以緩解一下的

3、運算量大

  解析式中含有冪運算,計算機求解時相對來講比較耗時。對於規模比較大的深度網絡,這會較大地增加訓練時間。

tanh

  Tanh 激活函數又叫作雙曲正切激活函數(hyperbolic tangent activation function)。與 Sigmoid 函數類似,但 Tanh 函數將其壓縮至-1 到 1 的區間內,輸出是zero-centered的(零為中心),在實踐中,Tanh 函數的使用優先性高於 Sigmoid 函數。負數輸入被當作負值,零輸入值的映射接近零,正數輸入被當作正值。

數學函數為:

$$f(z)=tanh(z)=\frac{e^{z}-e^{-z}}{e^z}+e^{-z}$$

def tanh(x):
    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))
View Code

優點

  1. sigmoid的優點他都有,另外 tanh的輸出是zero-centered,以0為中心

缺點

1、特殊情況存在梯度消失問題

  當輸入值過大或者過小,提取趨近於0,失去敏感性,處於飽和狀態。

ReLU

這才是一個目前主流論文中非常常用的激活函數,它的數學公式為:

$$f(x)=max(0,x)$$

def relu(x):
    return np.where(x<0,0,x)
View Code

優點

  1. ReLU的計算量小,收斂速度很快,因為sigmoid和tanh,ReLU有指數運算
  2. 正區間(x>0)解決了梯度消失問題。圖像數據是在(0~255)之間,即便歸一化處理值也大於0,但是音頻數據有正有負,不適合relu函數

缺點:

  1. ReLU的輸出不是zero-centered
  2. RuLU在訓練的時候很容易導致神經元“死掉”

死掉:一個非常大的梯度經過一個 ReLU 神經元,更新過參數之后,這個神經元再也不會被任何數據激活相應的權重永遠不會更新。有兩種原因導致這種情況:1、非常不幸的初始化。2、學習率設置的太高導致在訓練過程中參數更新太大,解決方法是使用Xavier初始化方法,合理設置學習率,會降低這種情況的發生概率。或使用Adam等自動調節學習率的算法。

補充:ReLU相比sigmoid和tanh的一個缺點是沒有對上界設限,在實際使用中,可以設置一個上限,如ReLU6經驗函數: f(x)=min(6,max(0,x))

LeakyReLU

  Leaky ReLU(泄露型線性整流函數),LeakyReLU中的斜率a是自定義的,pReLU中的a是通過訓練學習得到的,LeakyReLU是為了解決“ReLU死亡”問題的嘗試

$$f(x)=\left\{\begin{matrix}
x&&x>0\\
0.01x&&其他
\end{matrix}\right.$$

ReLU 中當 x<0 時,函數值為 0 。而 Leaky ReLU 則是給出一個很小的負數梯度值,比如 0.01 。

有些研究者的論文指出這個激活函數表現很不錯,但是其效果並不是很穩定。

def prelu(x,a):
    return np.where(x<0,a*x,x)
View Code

雖然Leaky ReLU修復了ReLU的神經元死亡問題,但是在實際的使用並沒有完全證明Leaky ReLU完全優於ReLU。

PReLU

  Parameterised ReLU(PReLU,參數化線性整流函數),在RReLU中,負值的斜率$a_i$在訓練中是隨機的,$a_i$是可學習的,如果$a_i=0$,那么 PReLU 退化為ReLU;如果$a_i$是一個很小的固定值(如$a_i=0.01$),則 PReLU 退化為 Leaky ReLU。

  $a_i$在之后的測試中就變成了固定的了。RReLU的亮點在於,在訓練環節中,$a_i$是從一個均勻的分布$U(I,u)$中隨機抽取的數值。形式上來說,我們能得到以下數學表達式:

$$f(x)=\left\{\begin{matrix}
x&&x>0\\
a_ix&&x\leqslant 0
\end{matrix}\right.$$

其中$$a_i\sim U(x,y),區間(x,y)上的均勻分布;x,y\in [0,1]$$

優點

(1)PReLU只增加了極少量的參數,也就意味着網絡的計算量以及過擬合的危險性都只增加了一點點。特別的,當不同channels使用相同的$a$時,參數就更少了。

(2)BP更新$a$時,采用的是帶動量的更新方式,如下:

$$\Delta a_i=\mu \Delta a_i+\epsilon \frac{\partial \varepsilon }{\partial a_i}$$

ELU

  Exponential Linear Unit(ELU,指數化線性單元),為了解決ReLU存在的問題而提出的,ELU有ReLU的基本所有優點,以及不會有Dead ReLU問題,和輸出的均值接近0(zero-certered),它的一個小問題在於計算量稍大。類似於Leaky ReLU,理論上雖然好於ReLU,但在實際使用中目前並沒有好的證據ELU總是優於ReLU。

$$f(x)=\left\{\begin{matrix}
x&&x>0\\
\alpha (e^x-1)&&x\leq 0
\end{matrix}\right.$$

$$f'(x)=\left\{\begin{matrix}
1&&x>0\\
f(x)+a&&x\leq 0
\end{matrix}\right.$$

def elu(x, a):
    return np.where(x < 0, a*(np.exp(x)-1), a*x)
View Code

 

  其中$\alpha$是一個可調整的參數,它控制着ELU負值部分在何時飽和。右側線性部分使得ELU能夠緩解梯度消失,而左側軟飽能夠讓ELU對輸入變化或噪聲更魯棒。ELU的輸出均值接近於零,所以收斂速度更快 

SELU

$$SELU(x)=\lambda \left\{\begin{matrix}
x&&x>0\\
\alpha e^x-\alpha &&x\leq 0
\end{matrix}\right.$$

  經過該激活函數后使得樣本分布自動歸一化到0均值和單位方差(自歸一化,保證訓練過程中梯度不會爆炸或消失,效果比Batch Normalization 要好)

  其實就是ELU乘了個$\alpha$,關鍵在於這個$\alpha$是大於1的。以前relu,prelu,elu這些激活函數,都是在負半軸坡度平緩,這樣在激活函數的方差過大的時候可以讓它減小,防止了梯度爆炸,但是正半軸坡度簡單的設成了1。而selu的正半軸大於1,在方差過小的的時候可以讓它增大,同時防止了梯度消失。這樣激活函數就有一個不動點,網絡深了以后每一層的輸出都是均值為0方差為1。

def selu(x):
    alpha = 1.6732632423543772848170429916717
    scale = 1.0507009873554804934193349852946
    return scale*np.where(x>=0.0, x, alpha*(np.exp(x)-1))
View Code

 

其中超參 α 和 λ 的值是 證明得到 的(而非訓練學習得到):

α = 1.6732632423543772848170429916717
λ = 1.0507009873554804934193349852946

即:

  • 不存在死區
  • 存在飽和區(負無窮時, 趨於 - αλ
  • 輸入大於零時,激活輸出對輸入進行了放大

Swish

  Swish 激活函數,該函數又叫作自門控激活函數,它近期由谷歌的研究者發布,數學公式為:

$$\sigma (x)=\frac{x}{1+e^{-x}}$$

根據論文(https://arxiv.org/abs/1710.05941v1),Swish 激活函數的性能優於 ReLU 函數。

  根據上圖,我們可以觀察到在 x 軸的負區域曲線的形狀與 ReLU 激活函數不同,因此,Swish 激活函數的輸出可能下降,即使在輸入值增大的情況下。大多數激活函數是單調的,即輸入值增大的情況下,輸出值不可能下降。而 Swish 函數為 0 時具備單側有界(one-sided boundedness)的特性,它是平滑、非單調的。更改一行代碼再來查看它的性能,似乎也挺有意思。

softmax

softmax用於多分類神經網絡輸出,如果某一個$a_i$打過其他z,那這個映射的分量就逼近1,其他就逼近0,主要應用於“分類”。

$$SOFTMAX:a_i=\sigma_i(z)=\frac{e^{z_i}}{\sum_{j=1}^{m}e^{z_j}},z_i=w_ix+b$$

作用:把神經元中線性部分輸出的得分值(score),轉換為概率值。softmax輸出的是(歸一化)概率,

  含有softmax激活函數的網絡層有這樣一個性質:$\sum_{i=1}^{j}\sigma _i(z)=1$,可以解釋為每個節點的輸出值小於等於1。softmax激勵函數通常在神經網絡的最后一層作為分類器的輸出,輸出值(概率)最大的即為分類結果。

$$貓:\begin{pmatrix}0.05\\ 0.05\\ 0.7\\ 0.2\end{pmatrix} 狗:\begin{pmatrix}0.8\\ 0.06\\ 0.01\\ 0.04\end{pmatrix}$$

如何選擇合適的激活函數

這個問題目前沒有確定的方法,憑一些經驗吧。

1)深度學習往往需要大量時間來處理大量數據,模型的收斂速度是尤為重要的。所以,總體上來講,訓練深度學習網絡盡量使用zero-centered數據 (可以經過數據預處理實現) 和zero-centered輸出。所以要盡量選擇輸出具有zero-centered特點的激活函數以加快模型的收斂速度。

2)如果使用 ReLU,那么一定要小心設置 learning rate,而且要注意不要讓網絡出現很多 “dead” 神經元,如果這個問題不好解決,那么可以試試 Leaky ReLU、PReLU。

3)最好不要用 sigmoid,你可以試試 tanh,不過可以預期它的效果會比不上 ReLU 和 Maxout.

最后來一張全家照

import math
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
plt.rcParams['font.sans-serif']=['SimHei']  # 指定默認字體
plt.rcParams['axes.unicode_minus']=False    # 用來正常顯示符號

def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))
def tanh(x):
    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))
def relu(x):
    return np.where(x<0,0,x)
def prelu(x,a):
    return np.where(x<0,a*x,x)
def elu(x, a):
    return np.where(x < 0, a*(np.exp(x)-1), a*x)
def selu(x):
    alpha = 1.6732632423543772848170429916717
    scale = 1.0507009873554804934193349852946
    return scale*np.where(x>=0.0, x, alpha*(np.exp(x)-1))


fig = plt.figure(figsize=(6,4))
ax = fig.add_subplot(111)

x = np.linspace(-10, 10)
y_sigmoid = sigmoid(x)
y_tanh = tanh(x)
y_relu = relu(x)
y_LeakyReLU = prelu(x, 0.05)
y_elu = elu(x, 0.25)
y_selu = selu(x)

plt.xlim(-11,11)
plt.ylim(-1.1,1.1)


ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')

ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.set_xticks([-10,-5,0,5,10])
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
ax.set_yticks([-1,-0.5,0.5,1])


plt.plot(x,y_sigmoid,label="Sigmoid",color = "blue")    # 藍色
plt.plot(2*x,y_tanh,label="tanh", color = "red")        # 紅色
plt.plot(2*x,y_relu,label="relu", color = "c")     # 青色
plt.plot(2*x,y_LeakyReLU, '-.', label="LeakyReLU", color = "Violet")    # 紫色
plt.plot(2*x,y_elu, ":", label="elu", color = "green")        # 綠色
plt.plot(2*x,y_selu, "--", label="selu", color = "k")      # 黑色

plt.legend()
plt.show()
View Code

參考文獻

hn_ma的CSDN博客

SELU論文地址:【Self-Normalizing Neural Networks】.

StevenSun2014的CSDN博客:常用激活函數總結

26種神經網絡激活函數可視化(留着以后看,原文更加精彩)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM