系列之7-激活函數

Copyright © Microsoft Corporation. All rights reserved.
適用於License版權許可
更多微軟人工智能學習資源,請見微軟人工智能教育與學習共建社區html

在這一章,咱們將簡要介紹一下激活函數和損失函數~python

激活函數

看神經網絡中的一個神經元,爲了簡化,假設該神經元接受三個輸入,分別爲\(x_1, x_2, x_3\),那麼\(z=\sum\limits_{i}w_ix_i+b_i\),git

激活函數也就是\(A=\sigma(Z)\)這一步了,他有什麼做用呢?github

  • 從模仿人類大腦的角度來講:

神經元在利用突觸傳遞信息時,不是全部信息均可以傳遞下去的,每一個神經元有本身的閾值,必需要刺激強過某個閾值纔會繼續向後傳遞。激活函數在某些方面就能夠扮演這樣一個激活閾值的做用,不達到必定要求的信號是不能夠繼續向後傳遞的算法

  • 說得形象一點,

假設老張家有一個在滴(漏)水的水龍頭:水龍頭漏水這件事情的嚴重等級有0~9這樣10個的等級,憑藉本身的力量能夠解決0~2這樣3的等級的狀況,維修部門能夠解決3~6這樣的等級,而後譬如須要修改水管線路什麼的就是須要更加專業的部門了,他們能夠解決7~9這樣等級的狀況。發現一個等級爲5的漏水事件,處理步驟是什麼樣的呢?網絡

首先,看到水龍頭在滴水,經過視覺細胞處理成信號輸入大腦,通過大腦這個黑盒子複雜的處理,判斷出這不是簡簡單單擰緊或者拍拍就能解決的漏水的問題,也就是說,判斷出這個事情的嚴重等級大於2,執行這樣一個處理函數:
if 嚴重等級 > 2: 老張打電話給隔壁老王尋求幫助 else: 本身解決
這件事情的嚴重等級超過了本身能力的閾值,因此須要開始傳遞給隔壁老王,以後老王執行一個相似的判斷
if 嚴重等級 > 6: 尋求物業公司幫助 else: 老王拿着管鉗去老張家幫忙框架

相似這個例子中的判斷需不須要更專業的人來解決問題,若是嚴重等級超過了某個設定的閾值,那就須要尋找更專業的人來幫助。在這裏的閾值就是能解決的嚴重等級的上限。函數

激活函數在神經網絡中起到的就是在控制本身接收到的消息究竟是不是須要向後傳播的做用。學習

  • 從數學的角度來講
    形如\(z=\sum\limits_{i}w_ix_i+b_i\)的傳遞是一種線性的傳遞過程。若是沒有非線性函數添加非線性,直接把不少層疊加在一塊兒會怎麼樣呢?
    以兩層爲例:

\[z_1 = w_1x_1 + b_1 \tag{1}\]
\[z_2 = w_2z_1 + b_2 = w_2(w_1x_1 + b_1) + b_2 = (w_2 w_1) x_1 + (w_2b_1 + b_2)=w_{3}x_1+b_{3} \tag{2}\]測試

\(z_1, z_2\)即爲這兩個神經元結點的輸出。能夠看到,疊加後的\(z_2\)的輸出也是一個線性函數。

以此類推,若是缺乏非線性層,不管如何疊加,最後獲得的還只是一個線性函數。

然而,按照生活經驗來講,大多數的事情都不是線性規律變化的,好比身高隨着年齡的變化,價格隨着市場的變化。因此須要給這樣一個線性傳遞過程添加非線性才能夠更好的去模擬這樣一個真實的世界。具體該怎麼作呢?

習慣上,用‘1’來表明一個神經元被激活,‘0’表明一個神經元未被激活,那預期的函數圖像是這個樣子的:

這個函數有什麼很差的地方呢?主要的一點就是,他的梯度(導數)恆爲零(個別點除外)。

想一想咱們說過的反向傳播公式?梯度傳遞用到了鏈式法則,若是在這樣一個連乘的式子其中有一項是零,結果會怎麼樣呢?這樣的梯度就會恆爲零。這個樣子的函數是沒有辦法進行反向傳播的。

那有沒有什麼函數能夠近似實現這樣子的階梯效果並且還能夠反向傳播呢?經常使用的激活函數有哪些呢?

sigmoid函數

公式:\(f(z) = \frac{1}{1 + e^{-z}}\)

反向傳播: \(f^{'}(z) = f(z) * (1 - f(z))\),推導過程請參看數學導數公式

從函數圖像來看,sigmoid函數的做用是將輸入限制到(0, 1)這個區間範圍內,這種輸出在0~1之間的函數能夠用來模擬一些機率分佈的狀況。他仍是一個連續函數,導數簡單易求。

用mnist數據的例子來通俗的解釋一下:

形象化的說,每個隱藏層神經元表明了對某個筆畫的感知,也就是說可能第一個神經元表明是否從圖中檢測到有一條像1同樣的豎線存在,第二個神經元表明是否有一小段曲線的存在。可是在實際傳播中,怎麼表示是否是有這樣一條直線或者這樣一段曲線存在呢?在生活中,咱們常常聽到這樣的對白「你以爲這件事情成功機率有多大?」「我有六成把握能成功」。sigmoid函數在這裏就起到了如何把一個數值轉化成一個通俗意義上的把握的表示。值越大,那麼這個神經元對於這張圖裏有這樣一條線段的把握就越大,通過sigmoid函數以後的結果就越接近100%,也就是1這樣一個值,表如今圖裏,也就是這個神經元越興奮(亮)。

可是這個樣子的激活函數有什麼問題呢?

從梯度圖像中能夠看到,sigmoid的梯度在兩端都會接近於0,根據鏈式法則,把其餘項做爲\(\alpha\),那麼梯度傳遞函數是\(\alpha*\sigma'(x)\),而\(\sigma'(x)\)這時是零,也就是說總體的梯度是零。這也就很容易出現梯度消失的問題,而且這個問題可能致使網絡收斂速度比較慢,好比採起MSE做爲損失函數算法時。

給個純粹數學的例子吧,假定咱們的學習速率是0.2,sigmoid函數值是0.9,若是咱們想把這個函數的值降到0.5,須要通過多少步呢?

咱們先來作公式推導,
第一步,求出當前輸入的值

\[\frac{1}{1 + e^{-x}} = 0.9\]
\[e^{-x} = \frac{1}{9}\]
\[x = ln{9}\]

第二步,求出當前梯度

\[grad = f(x)\times(1 - f(x)) = 0.9 \times 0.1= 0.09\]

第三步,根據梯度更新當前輸入值

\[x_{new} = x - \eta \times grad = ln{9} - 0.2 \times 0.09 = ln(9) - 0.018\]

第四步,判斷當前函數值是否接近0.5

\[\frac{1}{1 + e^{-x_{new}}} = 0.898368\]

第五步,重複步驟2-3直到當前函數值接近0.5

說得若是不夠直觀,那咱們來看看圖,

上半部分那條五彩斑斕的曲線就是迭代更新的過程了,一共迭代了多少次呢?根據程序統計,sigmoid迭代了67次才從0.9衰減到了接近0.5的水準。有同窗可能會說了,才67次嘛,這個次數也不是不少啊!確實,從1層來看,這個速度仍是能夠接受的,可是神經網絡只有這一層嗎?多層疊加以後的sigmoid函數,由於反向傳播的鏈式法則,兩層的梯度相乘,每次更新的步長更小,須要的次數更多,也就是速度更加慢。若是仍是沒有反應過來的同窗呢,能夠先向下看relu函數的收斂速度。

此外,若是輸入數據是(-1, 1)範圍內的均勻分佈的數據會致使什麼樣的結果呢?通過sigmoid函數處理以後這些數據的均值就從0變到了0.5,致使了均值的漂移,在不少應用中,這個性質是很差的。

代碼思路:
放到代碼中應該怎麼實現呢?首先,對於一個輸入進sigmoid函數的向量來講,函數的輸出和反向傳播時的導數是和輸入的具體數值有關係的,那麼爲了節省計算量,能夠生成一個和輸入向量同尺寸的mask,用於記錄前向和反向傳播的結果,具體代碼來講,就是:

示例代碼:

class Csigmoid(object):
    def __init__(self, inputSize):
        self.shape = inputSize

    def forward(self, image):
        # 記錄前向傳播結果
        self.mask = 1 / (1 + np.exp(-1 * image))
        return self.mask

    def gradient(self, preError):
        # 生成反向傳播對應位置的梯度
        self.mask = np.multiply(self.mask, 1 - self.mask)
        return np.multiply(preError, self.mask)

不理解爲啥又有前向傳播又有梯度計算的小夥伴請戳這裏

tanh函數

形式:
\(f(z) = \frac{e^{z} - e^{-z}}{e^{z} + e^{-z}}\)
\(f(z) = 2*sigmoid(2*z) - 1\)

反向傳播:
\(f^{'}(z) = (1 + f(z)) * (1 - f(z))\)

不管從理論公式仍是函數圖像,這個函數都是一個和sigmoid很是相像的激活函數,他們的性質也確實如此。可是比起sigmoid,tanh減小了一個缺點,就是他自己是零均值的,也就是說,在傳遞過程當中,輸入數據的均值並不會發生改變,這就使他在不少應用中能表現出比sigmoid優異一些的效果。

代碼思路:
這麼類似的函數沒有類似的代碼說不過去呀!比起sigmoid的代碼,實現tanh只須要改變幾個微小的地方就能夠了,話很少說,直接上代碼吧:

示例代碼:

class Ctanh(object):
    def __init__(self, inputSize):
        self.shape = inputSize

    def forward(self, image):
        # 記錄前向傳播結果
        self.mask = 2 / (1 + np.exp(-2 * image)) - 1
        return self.mask

    def gradient(self, preError):
        # 生成反向傳播對應位置的梯度
        self.mask = np.multiply(1 + self.mask, 1 - self.mask)
        return np.multiply(preError, self.mask)

relu函數

形式:

\[f(z) = \begin{cases} z & z \geq 0 \\ 0 & z < 0 \end{cases}\]

反向傳播:

\[f(z) = \begin{cases} 1 & z \geq 0 \\ 0 & z < 0 \end{cases}\]

先來講說神經學方面的解釋,爲何要使用relu呢?要說模仿神經元,sigmoid不是更好嗎?這就要看2001年神經學家模擬出的更精確的神經元模型Deep Sparse Rectifier Neural Networks。簡單說來,結論就是這樣兩幅圖:

下面來解釋函數圖像:在輸入的信號比0大的狀況下,直接將信號輸出,不然的話將信號抑制到0,相比較於上面兩個激活函數,relu計算方面的開銷很是小,避免了指數運算和除法運算。此外,不少實驗驗證了採用relu函數做爲激活函數,網絡收斂的速度能夠更快。至於收斂更加快的緣由,從圖上的梯度計算能夠看出,relu的反向傳播梯度恆定是1,而sigmoid激活函數中大多數時間梯度是比1小的。在疊加不少層以後,因爲鏈式法則的乘法特性,sigmoid做爲梯度函數會不斷減少反向傳播的梯度,而relu能夠將比梯度原樣傳遞,也就是說,relu可使用比較快的速度去進行參數更新。

用和sigmoid函數那裏更新類似的算法步驟和參數,來模擬一下relu的梯度降低次數,也就是學習率\(\alpha = 0.2\),但願函數值從0.9衰減到0.5,這樣須要多少步呢?

也就是說,一樣的學習速率,relu函數只須要兩步就能夠作到sigmoid須要67步才能衰減到的程度!

可是若是回傳了一個很大的梯度致使網絡更新以後輸入信號小於0了呢?那麼這個神經元以後接受到的數據是零,對應新的回傳的梯度也是零,這個神經元將不被更新,下一次輸入的信號依舊小於零,不停重複這個過程。也就是說,這個神經元不會繼續更新了,這個神經元「死」掉了。在學習率設置不恰當的狀況下,頗有可能網絡中大部分神經元「死」掉,也就是說不起做用了,而這是不可取的。

代碼實現:
與sigmoid相似,relu函數的前向傳播和反向傳播與輸入的大小有關係,小於0的輸入能夠被簡單的置成0,不小於0的能夠繼續向下傳播,也就是將輸入和0中較大的值繼續傳播,對輸入向量逐元素作比較便可。考慮到反向傳播時梯度計算也和輸入有關,使用一個mask對數據或者根據數據推出的反向傳播結果作一個記錄也是一個比較好的選擇。

示例代碼:

class Crelu(object):
    def __init__(self, inputSize):
        self.shape = inputSize

    def forward(self, image):
        # 用於記錄傳遞的結果
        self.mask = np.zeros(self.shape)
        self.mask[image > 0] = 1
        # 將小於0的項截止到0
        return np.maximum(image, 0)

    def gradient(self, preError):
        # 將上一層傳遞的偏差函數和該層各位置的導數相乘
        return np.multiply(preError, self.mask)

想一想看,relu函數的缺點是什麼呢?是梯度很大的時候可能致使的神經元「死」掉。而這個死掉的緣由是什麼呢?是由於很大的梯度致使更新以後的網絡傳遞過來的輸入是小於零的,從而致使relu的輸出是0,計算所得的梯度是零,而後對應的神經元不更新,從而使relu輸出恆爲零,對應的神經元恆定不更新,等於這個relu失去了做爲一個激活函數的夢想。問題的關鍵點就在於輸入小於零時,relu回傳的梯度是零,從而致使了後面的不更新。

那麼最簡單粗暴的作法是什麼?在輸入函數值小於零的時候給他一個梯度不就行了!這就是leaky relu函數的表現形式了!

leaky relu函數

形式:

\[f(z) = \begin{cases} z & z \geq 0 \\ \alpha * z & z < 0 \end{cases}\]

反向傳播:

\[f(z) = \begin{cases} z & 1 \geq 0 \\ \alpha & z < 0 \end{cases}\]

相比較於relu函數,leaky relu一樣有收斂快速和運算複雜度低的優勢,並且因爲給了\(x<0\)時一個比較小的梯度\(\alpha\),使得\(x<0\)時依舊能夠進行梯度傳遞和更新,能夠在必定程度上避免神經元「死」掉的問題。

示例代碼:

class CleakyRelu(object):
    def __init__(self, inputSize, alpha):
        self.shape = inputSize
        self.alpha = alpha

    def forward(self, image):
        # 用於記錄傳遞的結果,按照傳遞公式生成對應的值
        self.mask = np.zeros(self.shape)
        self.mask[image > 0] = 1
        self.mask[image <= 0] = self.alpha
        # 將該值對應到輸入中
        return np.multiply(image, self.mask)

    def gradient(self, preError):
        # 將上一層傳遞的偏差函數和該層各位置的導數相乘
        return np.multiply(preError, self.mask)

softmax 函數

softmax函數,是大名鼎鼎的在計算多分類問題時常使用的一個函數,他長成這個樣子:

\[ \phi(z_j) = \frac{e^{z_j}}{\sum\limits_ie^{z_i}} \]

也就是說把接收到的輸入歸一化成一個每一個份量都在\((0,1)\)之間而且總和爲一的一個機率函數。

用一張圖來形象說明這個過程

當輸入的數據是3,1,-3時,按照圖示過程進行計算,能夠得出輸出的機率分佈是0.88,0.12,0。

試想若是咱們並無這樣一個softmax的過程而是直接根據3,1,-3這樣的輸出,而咱們指望得結果是1,0,0這樣的機率分佈結果,那傳遞給網絡的信息是什麼呢?咱們要抑制正樣本的輸出,同時要抑制負樣本的輸出。正樣本的輸出和指望的差距是2,負樣本1和指望的差距是0,因此網絡要更加抑制正樣本的結果!因此,在輸出結果相對而言已經比較理想的狀況下,咱們給了網絡一個相對錯誤的更新方向:更多的抑制正樣本的輸出結果。這顯然是不可取的呀!

從繼承關係的角度來講,softmax函數能夠視做sigmoid的一個擴展,好比咱們來看一個二分類問題,

\[ \phi(z_1) = \frac{e^{z_1}}{e^{z_1} + e^{z_2}} = \frac{1}{1 + e^{z_2 - z_1}} = \frac{1}{1 + e^{z_2} e^{- z_1}} \]

是否是和sigmoid的函數形式很是像?比起原始的sigmoid函數,softmax的一個優點是能夠用在多分類的問題中。另外一個好處是在計算機率的時候更符合通常意義上咱們認知的機率分佈,體現出物體屬於各個類別相對的機率大小。

既然採用了這個函數,那麼怎麼計算它的反向傳播呢?

這裏爲了方便起見,將\(\sum\limits_{i \neq j}e^{z_i}\)記做\(k\),那麼,

\[ \phi(z_j) = \frac{e^{z_j}}{\sum\limits_ie^{z_i}} = \frac{e^{z_j}}{k + e^{z_j}} \]

\[ \therefore \frac{\partial\phi(z_j)}{\partial z_j} = \frac{e^{z_j}(k + e^{z_j}) - e^{z_j} * e^{z_j}}{{(k + e^{z_j})}^2} = \frac{e^{z_j}}{k + e^{z_j}}\frac{k}{k + e^{z_j}} = softmax(z_j)(1 - softmax(z_j)) \]

也就是說,softmax的梯度就是\(softmax(z_j)(1 - softmax(z_j))\),以後將這個梯度進行反向傳播就能夠大功告成啦~

參考資料:

[https://www.cnblogs.com/alexanderkun/p/8098781.html](https://www.cnblogs.com/alexanderkun/p/8098781.html)

[https://www.zhihu.com/question/20447622/answer/161722019](https://www.zhihu.com/question/20447622/answer/161722019)

[《信息論基礎》](https://book.douban.com/subject/1822197/)

點擊這裏提交問題與建議
聯繫咱們: msraeduhub@microsoft.com
學習了這麼多,還沒過癮怎麼辦?歡迎加入「微軟 AI 應用開發實戰交流羣」,跟你們一塊兒暢談AI,答疑解惑。掃描下方二維碼,回覆「申請入羣」,即刻邀請你入羣。

相關文章
相關標籤/搜索