AI应用开发基础傻瓜书系列3-激活函数
Copyright ? Microsoft Corporation. All rights reserved.
適用于License版權許可
更多微軟人工智能學習資源,請見微軟人工智能教育與學習共建社區
- Content
- 01.0-神經網絡的基本工作原理
- 01.1-基本數學導數公式
- 01.2-Python-Numpy庫的點滴
- 02.0-反向傳播與梯度下降
- 02.1-線性反向傳播
- 02.2-非線性反向傳播
- 02.3-梯度下降
- 03.0-損失函數
- 03.1-均方差損失函數
- 03.2-交叉熵損失函數
- 04.0-單入單出單層-單變量線性回歸
- 04.1-最小二乘法
- 04.2-梯度下降法
- 04.3-神經網絡法
- 04.4-梯度下降的三種形式
- 04.5-實現邏輯非門
- 05.0-多入單出單層-多變量線性回歸
- 05.1-正規方程法
- 05.2-神經網絡法
- 05.3-樣本特征數據的歸一化
- 05.4-歸一化的后遺癥
- 05.5-正確的推理方法
- 05.6-歸一化標簽值
- 06.0-多入多出單層神經網絡-多變量線性分類
- 06.1-二分類原理
- 06.2-線性二分類實現
- 06.3-線性二分類結果可視化
- 06.4-多分類原理
- 06.5-線性多分類實現
- 06.6-線性多分類結果可視化
- 07.0-激活函數
- 07.1-擠壓型激活函數
- 07.2-半線性激活函數
- 07.3-用雙曲正切函數分類
- 07.4-實現邏輯與門和或門
- 08.0-單入單出雙層-萬能近似定理
- 08.1-雙層擬合網絡的原理
- 08.2-雙層擬合網絡的實現
- 09.0-多入多出雙層-雙變量非線性分類
- 09.1-實現邏輯異或門
- 09.2-理解二分類的工作原理
- 09.3-非線性多分類
- 09.4-理解多分類的工作原理
- 10.0-調參與優化
- 10.1-權重矩陣初始化
- 10.2-參數調優
- 10.3-搜索最優學習率
- 10.4-梯度下降優化算法
- 10.5-自適應學習率算法
- 11.0-深度學習基礎
- 11.1-三層神經網絡的實現
- 11.2-驗證與測試
- 11.3-梯度檢查
- 11.4-手工測試訓練效果
- 11.5-搭建深度神經網絡框架
- 12.0-卷積神經網絡
- 12.1-卷積
- 12.2-池化
- 14.1-神經網絡模型概述
- 14.2-Windows模型的部署
- 14.3-Android模型的部署
第三篇:激活函數和損失函數(一)
在這一章,我們將簡要介紹一下激活函數和損失函數~
激活函數
看神經網絡中的一個神經元,為了簡化,假設該神經元接受三個輸入,分別為x1,x2,x3x_1, x_2, x_3x1?,x2?,x3?,那么z=∑iwixi+biz=\sum\limits_{i}w_ix_i+b_iz=i∑?wi?xi?+bi?,
激活函數也就是A=σ(Z)A=\sigma(Z)A=σ(Z)這一步了,他有什么作用呢?
- 從模仿人類大腦的角度來說:
神經元在利用突觸傳遞信息時,不是所有信息都可以傳遞下去的,每個神經元有自己的閾值,必須要刺激強過某個閾值才會繼續向后傳遞。激活函數在某些方面就可以扮演這樣一個激活閾值的作用,不達到一定要求的信號是不可以繼續向后傳遞的
- 說得形象一點,
假設老張家有一個在滴(漏)水的水龍頭:水龍頭漏水這件事情的嚴重等級有0~9這樣10個的等級,憑借自己的力量可以解決0~2這樣3的等級的情況,維修部門可以解決3~6這樣的等級,然后譬如需要修改水管線路什么的就是需要更加專業的部門了,他們可以解決7~9這樣等級的情況。發現一個等級為5的漏水事件,處理步驟是什么樣的呢?
首先,看到水龍頭在滴水,通過視覺細胞處理成信號輸入大腦,經過大腦這個黑盒子復雜的處理,判斷出這不是簡簡單單擰緊或者拍拍就能解決的漏水的問題,也就是說,判斷出這個事情的嚴重等級大于2,執行這樣一個處理函數:
if 嚴重等級 > 2:老張打電話給隔壁老王尋求幫助 else:自己解決這件事情的嚴重等級超過了自己能力的閾值,所以需要開始傳遞給隔壁老王,之后老王執行一個類似的判斷
if 嚴重等級 > 6:尋求物業公司幫助else:老王拿著管鉗去老張家幫忙類似這個例子中的判斷需不需要更專業的人來解決問題,如果嚴重等級超過了某個設定的閾值,那就需要尋找更專業的人來幫助。在這里的閾值就是能解決的嚴重等級的上限。
激活函數在神經網絡中起到的就是在控制自己接收到的消息到底是不是需要向后傳播的作用。
-
從數學的角度來說
形如z=∑iwixi+biz=\sum\limits_{i}w_ix_i+b_iz=i∑?wi?xi?+bi?的傳遞是一種線性的傳遞過程。如果沒有非線性函數添加非線性,直接把很多層疊加在一起會怎么樣呢?
以兩層為例:
(1)z1=w1x1+b1z_1 = w_1x_1 + b_1 \tag{1}z1?=w1?x1?+b1?(1)
(2)z2=w2z1+b2=w2(w1x1+b1)+b2=(w2w1)x1+(w2b1+b2)=w3x1+b3z_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}z2?=w2?z1?+b2?=w2?(w1?x1?+b1?)+b2?=(w2?w1?)x1?+(w2?b1?+b2?)=w3?x1?+b3?(2)
z1,z2z_1, z_2z1?,z2?即為這兩個神經元結點的輸出。可以看到,疊加后的z2z_2z2?的輸出也是一個線性函數。
以此類推,如果缺少非線性層,無論如何疊加,最后得到的還只是一個線性函數。
然而,按照生活經驗來說,大多數的事情都不是線性規律變化的,比如身高隨著年齡的變化,價格隨著市場的變化。所以需要給這樣一個線性傳遞過程添加非線性才可以更好的去模擬這樣一個真實的世界。具體該怎么做呢?
習慣上,用‘1’來代表一個神經元被激活,‘0’代表一個神經元未被激活,那預期的函數圖像是這個樣子的:
這個函數有什么不好的地方呢?主要的一點就是,他的梯度(導數)恒為零(個別點除外)。
想想我們說過的反向傳播公式?梯度傳遞用到了鏈式法則,如果在這樣一個連乘的式子其中有一項是零,結果會怎么樣呢?這樣的梯度就會恒為零。這個樣子的函數是沒有辦法進行反向傳播的。
那有沒有什么函數可以近似實現這樣子的階梯效果而且還可以反向傳播呢?常用的激活函數有哪些呢?
sigmoid函數
公式:f(z)=11+e?zf(z) = \frac{1}{1 + e^{-z}}f(z)=1+e?z1?
反向傳播:
f′(z)=f(z)?(1?f(z))f^{'}(z) = f(z) * (1 - f(z))f′(z)=f(z)?(1?f(z)),推導過程請參看數學導數公式。
從函數圖像來看,sigmoid函數的作用是將輸入限制到(0,
1)這個區間范圍內,這種輸出在0~1之間的函數可以用來模擬一些概率分布的情況。他還是一個連續函數,導數簡單易求。
用mnist數據的例子來通俗的解釋一下:
形象化的說,每一個隱藏層神經元代表了對某個筆畫的感知,也就是說可能第一個神經元代表是否從圖中檢測到有一條像1一樣的豎線存在,第二個神經元代表是否有一小段曲線的存在。但是在實際傳播中,怎么表示是不是有這樣一條直線或者這樣一段曲線存在呢?在生活中,我們經常聽到這樣的對白“你覺得這件事情成功概率有多大?”“我有六成把握能成功”。sigmoid函數在這里就起到了如何把一個數值轉化成一個通俗意義上的把握的表示。值越大,那么這個神經元對于這張圖里有這樣一條線段的把握就越大,經過sigmoid函數之后的結果就越接近100%,也就是1這樣一個值,表現在圖里,也就是這個神經元越興奮(亮)。
但是這個樣子的激活函數有什么問題呢?
從梯度圖像中可以看到,sigmoid的梯度在兩端都會接近于0,根據鏈式法則,把其他項作為α\alphaα,那么梯度傳遞函數是α?σ′(x)\alpha*\sigma'(x)α?σ′(x),而σ′(x)\sigma'(x)σ′(x)這時是零,也就是說整體的梯度是零。這也就很容易出現梯度消失的問題,并且這個問題可能導致網絡收斂速度比較慢,比如采取MSE作為損失函數算法時。
給個純粹數學的例子吧,假定我們的學習速率是0.2,sigmoid函數值是0.9,如果我們想把這個函數的值降到0.5,需要經過多少步呢?
我們先來做公式推導,
第一步,求出當前輸入的值
11+e?x=0.9\frac{1}{1 + e^{-x}} = 0.91+e?x1?=0.9
e?x=19e^{-x} = \frac{1}{9}e?x=91?
x=ln9x = ln{9}x=ln9
第二步,求出當前梯度
grad=f(x)×(1?f(x))=0.9×0.1=0.09grad = f(x)\times(1 - f(x)) = 0.9 \times 0.1= 0.09grad=f(x)×(1?f(x))=0.9×0.1=0.09
第三步,根據梯度更新當前輸入值
xnew=x?η×grad=ln9?0.2×0.09=ln(9)?0.018x_{new} = x - \eta \times grad = ln{9} - 0.2 \times 0.09 = ln(9) - 0.018xnew?=x?η×grad=ln9?0.2×0.09=ln(9)?0.018
第四步,判斷當前函數值是否接近0.5
11+e?xnew=0.898368\frac{1}{1 + e^{-x_{new}}} = 0.8983681+e?xnew?1?=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 = inputSizedef forward(self, image):# 記錄前向傳播結果self.mask = 1 / (1 + np.exp(-1 * image))return self.maskdef gradient(self, preError): # 生成反向傳播對應位置的梯度 self.mask = np.multiply(self.mask, 1 - self.mask)return np.multiply(preError, self.mask)不理解為啥又有前向傳播又有梯度計算的小伙伴請戳這里
tanh函數
形式:
f(z)=ez?e?zez+e?zf(z) = \frac{e^{z} - e^{-z}}{e^{z} + e^{-z}}f(z)=ez+e?zez?e?z?
f(z)=2?sigmoid(2?z)?1f(z) = 2*sigmoid(2*z) - 1f(z)=2?sigmoid(2?z)?1
反向傳播:
f′(z)=(1+f(z))?(1?f(z))f^{'}(z) = (1 + f(z)) * (1 - f(z))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)) - 1return self.maskdef gradient(self,preError):# 生成反向傳播對應位置的梯度self.mask = np.multiply(1 + self.mask, 1 - self.mask)return np.multiply(preError, self.mask)relu函數
形式:
f(z)={zz≥00z<0f(z) = \begin{cases} z & z \geq 0 \\ 0 & z < 0 \end{cases}f(z)={z0?z≥0z<0?
反向傳播:
f(z)={1z≥00z<0f(z) = \begin{cases} 1 & z \geq 0 \\ 0 & z < 0 \end{cases}f(z)={10?z≥0z<0?
先來說說神經學方面的解釋,為什么要使用relu呢?要說模仿神經元,sigmoid不是更好嗎?這就要看2001年神經學家模擬出的更精確的神經元模型Deep
Sparse Recti?er Neural Networks。簡單說來,結論就是這樣兩幅圖:
下面來解釋函數圖像:在輸入的信號比0大的情況下,直接將信號輸出,否則的話將信號抑制到0,相比較于上面兩個激活函數,relu計算方面的開銷非常小,避免了指數運算和除法運算。此外,很多實驗驗證了采用relu函數作為激活函數,網絡收斂的速度可以更快。至于收斂更加快的原因,從圖上的梯度計算可以看出,relu的反向傳播梯度恒定是1,而sigmoid激活函數中大多數時間梯度是比1小的。在疊加很多層之后,由于鏈式法則的乘法特性,sigmoid作為梯度函數會不斷減小反向傳播的梯度,而relu可以將比梯度原樣傳遞,也就是說,relu可以使用比較快的速度去進行參數更新。
用和sigmoid函數那里更新相似的算法步驟和參數,來模擬一下relu的梯度下降次數,也就是學習率α=0.2\alpha = 0.2α=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 = inputSizedef forward(self, image):# 用于記錄傳遞的結果self.mask = np.zeros(self.shape)self.mask[image > 0] = 1# 將小于0的項截止到0return 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)={zz≥0α?zz<0f(z) = \begin{cases} z & z \geq 0 \\ \alpha * z & z < 0 \end{cases}f(z)={zα?z?z≥0z<0?
反向傳播:
f(z)={z1≥0αz<0f(z) = \begin{cases} z & 1 \geq 0 \\ \alpha & z < 0 \end{cases}f(z)={zα?1≥0z<0?
相比較于relu函數,leaky relu同樣有收斂快速和運算復雜度低的優點,而且由于給了x<0x<0x<0時一個比較小的梯度α\alphaα,使得x<0x<0x<0時依舊可以進行梯度傳遞和更新,可以在一定程度上避免神經元“死”掉的問題。
示例代碼:
class CleakyRelu(object):def __init__(self, inputSize, alpha):self.shape = inputSizeself.alpha = alphadef forward(self, image):# 用于記錄傳遞的結果,按照傳遞公式生成對應的值self.mask = np.zeros(self.shape)self.mask[image > 0] = 1self.mask[image <= 0] = self.alpha# 將該值對應到輸入中return np.multiply(image, self.mask)def gradient(self, preError)# 將上一層傳遞的誤差函數和該層各位置的導數相乘 return np.multiply(preError, self.mask)softmax 函數
softmax函數,是大名鼎鼎的在計算多分類問題時常使用的一個函數,他長成這個樣子:
?(zj)=ezj∑iezi\phi(z_j) = \frac{e^{z_j}}{\sum\limits_ie^{z_i}} ?(zj?)=i∑?ezi?ezj??
也就是說把接收到的輸入歸一化成一個每個分量都在(0,1)(0,1)(0,1)之間并且總和為一的一個概率函數。
用一張圖來形象說明這個過程
當輸入的數據是3,1,-3時,按照圖示過程進行計算,可以得出輸出的概率分布是0.88,0.12,0。
試想如果我們并沒有這樣一個softmax的過程而是直接根據3,1,-3這樣的輸出,而我們期望得結果是1,0,0這樣的概率分布結果,那傳遞給網絡的信息是什么呢?我們要抑制正樣本的輸出,同時要抑制負樣本的輸出。正樣本的輸出和期望的差距是2,負樣本1和期望的差距是0,所以網絡要更加抑制正樣本的結果!所以,在輸出結果相對而言已經比較理想的情況下,我們給了網絡一個相對錯誤的更新方向:更多的抑制正樣本的輸出結果。這顯然是不可取的呀!
從繼承關系的角度來說,softmax函數可以視作sigmoid的一個擴展,比如我們來看一個二分類問題,
?(z1)=ez1ez1+ez2=11+ez2?z1=11+ez2e?z1\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}} ?(z1?)=ez1?+ez2?ez1??=1+ez2??z1?1?=1+ez2?e?z1?1?
是不是和sigmoid的函數形式非常像?比起原始的sigmoid函數,softmax的一個優勢是可以用在多分類的問題中。另一個好處是在計算概率的時候更符合一般意義上我們認知的概率分布,體現出物體屬于各個類別相對的概率大小。
既然采用了這個函數,那么怎么計算它的反向傳播呢?
這里為了方便起見,將∑i≠jezi\sum\limits_{i \neq j}e^{z_i}i??=j∑?ezi?記作kkk,那么,
?(zj)=ezj∑iezi=ezjk+ezj\phi(z_j) = \frac{e^{z_j}}{\sum\limits_ie^{z_i}} = \frac{e^{z_j}}{k + e^{z_j}} ?(zj?)=i∑?ezi?ezj??=k+ezj?ezj??
∴??(zj)?zj=ezj(k+ezj)?ezj?ezj(k+ezj)2=ezjk+ezjkk+ezj=softmax(zj)(1?softmax(zj))\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)) ∴?zj???(zj?)?=(k+ezj?)2ezj?(k+ezj?)?ezj??ezj??=k+ezj?ezj??k+ezj?k?=softmax(zj?)(1?softmax(zj?))
也就是說,softmax的梯度就是softmax(zj)(1?softmax(zj))softmax(z_j)(1- softmax(z_j))softmax(zj?)(1?softmax(zj?)),之后將這個梯度進行反向傳播就可以大功告成啦~
點擊這里提交問題與建議
聯系我們: msraeduhub@microsoft.com
學習了這么多,還沒過癮怎么辦?歡迎加入“微軟 AI 應用開發實戰交流群”,跟大家一起暢談AI,答疑解惑。掃描下方二維碼,回復“申請入群”,即刻邀請你入群。
總結
以上是生活随笔為你收集整理的AI应用开发基础傻瓜书系列3-激活函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 事物 锁行 测试_MySQL
- 下一篇: mysql游标遍历中sql语句出现异常_