由于簡書不支持?jǐn)?shù)學(xué)公式,請移步零基礎(chǔ)入門深度學(xué)習(xí)(2) - 線性單元和梯度下降。為了保證最佳閱讀效果,請盡量在電腦上閱讀。
往期回顧
在上一篇文章中,我們已經(jīng)學(xué)會了編寫一個簡單的感知器,并用它來實(shí)現(xiàn)一個線性分類器。你應(yīng)該還記得用來訓(xùn)練感知器的『感知器規(guī)則』。然而,我們并沒有關(guān)心這個規(guī)則是怎么得到的。本文通過介紹另外一種『感知器』,也就是『線性單元』,來說明關(guān)于機(jī)器學(xué)習(xí)一些基本的概念,比如模型、目標(biāo)函數(shù)、優(yōu)化算法等等。這些概念對于所有的機(jī)器學(xué)習(xí)算法來說都是通用的,掌握了這些概念,就掌握了機(jī)器學(xué)習(xí)的基本套路。
線性單元是啥
感知器有一個問題,當(dāng)面對的數(shù)據(jù)集不是線性可分的時(shí)候,『感知器規(guī)則』可能無法收斂,這意味著我們永遠(yuǎn)也無法完成一個感知器的訓(xùn)練。為了解決這個問題,我們使用一個可導(dǎo)的線性函數(shù)來替代感知器的階躍函數(shù),這種感知器就叫做線性單元。線性單元在面對線性不可分的數(shù)據(jù)集時(shí),會收斂到一個最佳的近似上。
為了簡單起見,我們可以設(shè)置線性單元的激活函數(shù)為
這樣的線性單元如下圖所示
對比此前我們講過的感知器
這樣替換了激活函數(shù)之后,線性單元將返回一個實(shí)數(shù)值而不是0,1分類。因此線性單元用來解決回歸問題而不是分類問題。
線性單元的模型
當(dāng)我們說模型時(shí),我們實(shí)際上在談?wù)摳鶕?jù)輸入預(yù)測輸出
的算法。比如,
可以是一個人的工作年限,
可以是他的月薪,我們可以用某種算法來根據(jù)一個人的工作年限來預(yù)測他的收入。比如:
函數(shù)叫做假設(shè),而
、
是它的參數(shù)。我們假設(shè)參數(shù)
,參數(shù)
,如果一個人的工作年限是5年的話,我們的模型會預(yù)測他的月薪為
你也許會說,這個模型太不靠譜了。是這樣的,因?yàn)槲覀兛紤]的因素太少了,僅僅包含了工作年限。如果考慮更多的因素,比如所處的行業(yè)、公司、職級等等,可能預(yù)測就會靠譜的多。我們把工作年限、行業(yè)、公司、職級這些信息,稱之為特征。對于一個工作了5年,在IT行業(yè),百度工作,職級T6這樣的人,我們可以用這樣的一個特征向量來表示他
= (5, IT, 百度, T6)。
既然輸入變成了一個具備四個特征的向量,相對應(yīng)的,僅僅一個參數(shù)
就不夠用了,我們應(yīng)該使用4個參數(shù)
,每個特征對應(yīng)一個。這樣,我們的模型就變成
其中,對應(yīng)工作年限,
對應(yīng)行業(yè),
對應(yīng)公司,
對應(yīng)職級。
為了書寫和計(jì)算方便,我們可以令等于
,同時(shí)令
對應(yīng)于特征
。由于
其實(shí)并不存在,我們可以令它的值永遠(yuǎn)為1。也就是說
這樣上面的式子就可以寫成
我們還可以把上式寫成向量的形式
長成這種樣子模型就叫做線性模型,因?yàn)檩敵?img class="math-inline" src="https://math.jianshu.com/math?formula=y" alt="y" mathimg="1">就是輸入特征的線性組合。
監(jiān)督學(xué)習(xí)和無監(jiān)督學(xué)習(xí)
接下來,我們需要關(guān)心的是這個模型如何訓(xùn)練,也就是參數(shù)取什么值最合適。
機(jī)器學(xué)習(xí)有一類學(xué)習(xí)方法叫做監(jiān)督學(xué)習(xí),它是說為了訓(xùn)練一個模型,我們要提供這樣一堆訓(xùn)練樣本:每個訓(xùn)練樣本既包括輸入特征,也包括對應(yīng)的輸出
(
也叫做標(biāo)記,label)。也就是說,我們要找到很多人,我們既知道他們的特征(工作年限,行業(yè)...),也知道他們的收入。我們用這樣的樣本去訓(xùn)練模型,讓模型既看到我們提出的每個問題(輸入特征
),也看到對應(yīng)問題的答案(標(biāo)記
)。當(dāng)模型看到足夠多的樣本之后,它就能總結(jié)出其中的一些規(guī)律。然后,就可以預(yù)測那些它沒看過的輸入所對應(yīng)的答案了。
另外一類學(xué)習(xí)方法叫做無監(jiān)督學(xué)習(xí),這種方法的訓(xùn)練樣本中只有而沒有
。模型可以總結(jié)出特征
的一些規(guī)律,但是無法知道其對應(yīng)的答案
。
很多時(shí)候,既有又有
的訓(xùn)練樣本是很少的,大部分樣本都只有
。比如在語音到文本(STT)的識別任務(wù)中,
是語音,
是這段語音對應(yīng)的文本。我們很容易獲取大量的語音錄音,然而把語音一段一段切分好并標(biāo)注上對應(yīng)文字則是非常費(fèi)力氣的事情。這種情況下,為了彌補(bǔ)帶標(biāo)注樣本的不足,我們可以用無監(jiān)督學(xué)習(xí)方法先做一些聚類,讓模型總結(jié)出哪些音節(jié)是相似的,然后再用少量的帶標(biāo)注的訓(xùn)練樣本,告訴模型其中一些音節(jié)對應(yīng)的文字。這樣模型就可以把相似的音節(jié)都對應(yīng)到相應(yīng)文字上,完成模型的訓(xùn)練。
線性單元的目標(biāo)函數(shù)
現(xiàn)在,讓我們只考慮監(jiān)督學(xué)習(xí)。
在監(jiān)督學(xué)習(xí)下,對于一個樣本,我們知道它的特征,以及標(biāo)記
。同時(shí),我們還可以根據(jù)模型
計(jì)算得到輸出
。注意這里面我們用
表示訓(xùn)練樣本里面的標(biāo)記,也就是實(shí)際值;用帶上劃線的
表示模型計(jì)算的出來的預(yù)測值。我們當(dāng)然希望模型計(jì)算出來的
和
越接近越好。
數(shù)學(xué)上有很多方法來表示的和
的接近程度,比如我們可以用
和
的差的平方的
來表示它們的接近程度
我們把叫做單個樣本的誤差。至于為什么前面要乘
,是為了后面計(jì)算方便。
訓(xùn)練數(shù)據(jù)中會有很多樣本,比如個,我們可以用訓(xùn)練數(shù)據(jù)中所有樣本的誤差的和,來表示模型的誤差
,也就是
上式的表示第一個樣本的誤差,
表示第二個樣本的誤差......。
我們還可以把上面的式子寫成和式的形式。使用和式,不光書寫起來簡單,逼格也跟著暴漲,一舉兩得。所以一定要寫成下面這樣
其中
(式2)中,表示第
個訓(xùn)練樣本的特征,
表示第
個樣本的標(biāo)記,我們也可以用元組
表示第
訓(xùn)練樣本。
則是模型對第
個樣本的預(yù)測值。
我們當(dāng)然希望對于一個訓(xùn)練數(shù)據(jù)集來說,誤差最小越好,也就是(式2)的值越小越好。對于特定的訓(xùn)練數(shù)據(jù)集來說,的值都是已知的,所以(式2)其實(shí)是參數(shù)
的函數(shù)。
由此可見,模型的訓(xùn)練,實(shí)際上就是求取到合適的,使(式2)取得最小值。這在數(shù)學(xué)上稱作優(yōu)化問題,而
就是我們優(yōu)化的目標(biāo),稱之為目標(biāo)函數(shù)。
梯度下降優(yōu)化算法
大學(xué)時(shí)我們學(xué)過怎樣求函數(shù)的極值。函數(shù)的極值點(diǎn),就是它的導(dǎo)數(shù)
的那個點(diǎn)。因此我們可以通過解方程
,求得函數(shù)的極值點(diǎn)
。
不過對于計(jì)算機(jī)來說,它可不會解方程。但是它可以憑借強(qiáng)大的計(jì)算能力,一步一步的去把函數(shù)的極值點(diǎn)『試』出來。如下圖所示:
首先,我們隨便選擇一個點(diǎn)開始,比如上圖的點(diǎn)。接下來,每次迭代修改
的為
,經(jīng)過數(shù)次迭代后最終達(dá)到函數(shù)最小值點(diǎn)。
你可能要問了,為啥每次修改的值,都能往函數(shù)最小值那個方向前進(jìn)呢?這里的奧秘在于,我們每次都是向函數(shù)
的梯度的相反方向來修改
。什么是梯度呢?翻開大學(xué)高數(shù)課的課本,我們會發(fā)現(xiàn)梯度是一個向量,它指向函數(shù)值上升最快的方向。顯然,梯度的反方向當(dāng)然就是函數(shù)值下降最快的方向了。我們每次沿著梯度相反方向去修改
的值,當(dāng)然就能走到函數(shù)的最小值附近。之所以是最小值附近而不是最小值那個點(diǎn),是因?yàn)槲覀兠看我苿拥牟介L不會那么恰到好處,有可能最后一次迭代走遠(yuǎn)了越過了最小值那個點(diǎn)。步長的選擇是門手藝,如果選擇小了,那么就會迭代很多輪才能走到最小值附近;如果選擇大了,那可能就會越過最小值很遠(yuǎn),收斂不到一個好的點(diǎn)上。
按照上面的討論,我們就可以寫出梯度下降算法的公式
其中,是梯度算子,
就是指
的梯度。
是步長,也稱作學(xué)習(xí)速率。
對于上一節(jié)列出的目標(biāo)函數(shù)(式2)
梯度下降算法可以寫成
聰明的你應(yīng)該能想到,如果要求目標(biāo)函數(shù)的最大值,那么我們就應(yīng)該用梯度上升算法,它的參數(shù)修改規(guī)則是
下面,請先做幾次深呼吸,讓你的大腦補(bǔ)充足夠的新鮮的氧氣,我們要來求取,然后帶入上式,就能得到線性單元的參數(shù)修改規(guī)則。
關(guān)于的推導(dǎo)過程,我單獨(dú)把它們放到一節(jié)中。您既可以選擇慢慢看,也可以選擇無視。在這里,您只需要知道,經(jīng)過一大串推導(dǎo),目標(biāo)函數(shù)
的梯度是
因此,線性單元的參數(shù)修改規(guī)則最后是這個樣子
有了上面這個式子,我們就可以根據(jù)它來寫出訓(xùn)練線性單元的代碼了。
需要說明的是,如果每個樣本有M個特征,則上式中的都是M+1維向量(因?yàn)槲覀兗由狭艘粋€恒為1的虛擬特征
,參考前面的內(nèi)容),而
是標(biāo)量。用高逼格的數(shù)學(xué)符號表示,就是
為了讓您看明白說的是啥,我吐血寫下下面這個解釋(寫這種公式可累可累了)。因?yàn)?img class="math-inline" src="https://math.jianshu.com/math?formula=%5Cmathrm%7Bw%7D%2C%5Cmathrm%7Bx%7D" alt="\mathrm{w},\mathrm{x}" mathimg="1">是M+1維列向量,所以(式3)可以寫成
如果您還是沒看明白,建議您也吐血再看一下大學(xué)時(shí)學(xué)過的《線性代數(shù)》吧。
的推導(dǎo)
這一節(jié)你盡可以跳過它,并不太會影響到全文的理解。當(dāng)然如果你非要弄明白每個細(xì)節(jié),那恭喜你騷年,機(jī)器學(xué)習(xí)的未來一定是屬于你的。
首先,我們先做一個簡單的前戲。我們知道函數(shù)的梯度的定義就是它相對于各個變量的偏導(dǎo)數(shù),所以我們寫下下面的式子
可接下來怎么辦呢?我們知道和的導(dǎo)數(shù)等于導(dǎo)數(shù)的和,所以我們可以先把求和符號里面的導(dǎo)數(shù)求出來,然后再把它們加在一起就行了,也就是
現(xiàn)在我們可以不管高大上的了,先專心把里面的導(dǎo)數(shù)求出來。
我們知道,是與
無關(guān)的常數(shù),而
,下面我們根據(jù)鏈?zhǔn)角髮?dǎo)法則來求導(dǎo)(上大學(xué)時(shí)好像叫復(fù)合函數(shù)求導(dǎo)法則)
我們分別計(jì)算上式等號右邊的兩個偏導(dǎo)數(shù)
代入,我們求得里面的偏導(dǎo)數(shù)是
最后代入,求得
至此,大功告成。
隨機(jī)梯度下降算法(Stochastic Gradient Descent, SGD)
如果我們根據(jù)(式3)來訓(xùn)練模型,那么我們每次更新的迭代,要遍歷訓(xùn)練數(shù)據(jù)中所有的樣本進(jìn)行計(jì)算,我們稱這種算法叫做批梯度下降(Batch Gradient Descent)。如果我們的樣本非常大,比如數(shù)百萬到數(shù)億,那么計(jì)算量異常巨大。因此,實(shí)用的算法是SGD算法。在SGD算法中,每次更新
的迭代,只計(jì)算一個樣本。這樣對于一個具有數(shù)百萬樣本的訓(xùn)練數(shù)據(jù),完成一次遍歷就會對
更新數(shù)百萬次,效率大大提升。由于樣本的噪音和隨機(jī)性,每次更新
并不一定按照減少
的方向。然而,雖然存在一定隨機(jī)性,大量的更新總體上沿著減少
的方向前進(jìn)的,因此最后也能收斂到最小值附近。下圖展示了SGD和BGD的區(qū)別
如上圖,橢圓表示的是函數(shù)值的等高線,橢圓中心是函數(shù)的最小值點(diǎn)。紅色是BGD的逼近曲線,而紫色是SGD的逼近曲線。我們可以看到BGD是一直向著最低點(diǎn)前進(jìn)的,而SGD明顯躁動了許多,但總體上仍然是向最低點(diǎn)逼近的。
最后需要說明的是,SGD不僅僅效率高,而且隨機(jī)性有時(shí)候反而是好事。今天的目標(biāo)函數(shù)是一個『凸函數(shù)』,沿著梯度反方向就能找到全局唯一的最小值。然而對于非凸函數(shù)來說,存在許多局部最小值。隨機(jī)性有助于我們逃離某些很糟糕的局部最小值,從而獲得一個更好的模型。
實(shí)現(xiàn)線性單元
接下來,讓我們擼一把代碼。
因?yàn)槲覀円呀?jīng)寫了感知器的代碼,因此我們先比較一下感知器模型和線性單元模型,看看哪些代碼能夠復(fù)用。
算法 | 感知器 | 線性單元 |
---|---|---|
模型 |
||
訓(xùn)練規(guī)則 |
比較的結(jié)果令人震驚,原來除了激活函數(shù)不同之外,兩者的模型和訓(xùn)練規(guī)則是一樣的(在上表中,線性單元的優(yōu)化算法是SGD算法)。那么,我們只需要把感知器的激活函數(shù)進(jìn)行替換即可。感知器的代碼請參考上一篇文章零基礎(chǔ)入門深度學(xué)習(xí)(1) - 感知器,這里就不再重復(fù)了。對于一個養(yǎng)成良好習(xí)慣的程序員來說,重復(fù)代碼是不可忍受的。大家應(yīng)該把代碼保存在一個代碼庫中(比如git)。
from perceptron import Perceptron
#定義激活函數(shù)f
f = lambda x: x
class LinearUnit(Perceptron):
def __init__(self, input_num):
'''初始化線性單元,設(shè)置輸入?yún)?shù)的個數(shù)'''
Perceptron.__init__(self, input_num, f)
通過繼承Perceptron,我們僅用幾行代碼就實(shí)現(xiàn)了線性單元。這再次證明了面向?qū)ο缶幊谭妒降膹?qiáng)大。
接下來,我們用簡單的數(shù)據(jù)進(jìn)行一下測試。
def get_training_dataset():
'''
捏造5個人的收入數(shù)據(jù)
'''
# 構(gòu)建訓(xùn)練數(shù)據(jù)
# 輸入向量列表,每一項(xiàng)是工作年限
input_vecs = [[5], [3], [8], [1.4], [10.1]]
# 期望的輸出列表,月薪,注意要與輸入一一對應(yīng)
labels = [5500, 2300, 7600, 1800, 11400]
return input_vecs, labels
def train_linear_unit():
'''
使用數(shù)據(jù)訓(xùn)練線性單元
'''
# 創(chuàng)建感知器,輸入?yún)?shù)的特征數(shù)為1(工作年限)
lu = LinearUnit(1)
# 訓(xùn)練,迭代10輪, 學(xué)習(xí)速率為0.01
input_vecs, labels = get_training_dataset()
lu.train(input_vecs, labels, 10, 0.01)
#返回訓(xùn)練好的線性單元
return lu
if __name__ == '__main__':
'''訓(xùn)練線性單元'''
linear_unit = train_linear_unit()
# 打印訓(xùn)練獲得的權(quán)重
print linear_unit
# 測試
print 'Work 3.4 years, monthly salary = %.2f' % linear_unit.predict([3.4])
print 'Work 15 years, monthly salary = %.2f' % linear_unit.predict([15])
print 'Work 1.5 years, monthly salary = %.2f' % linear_unit.predict([1.5])
print 'Work 6.3 years, monthly salary = %.2f' % linear_unit.predict([6.3])
程序運(yùn)行結(jié)果如下圖
擬合的直線如下圖
小結(jié)
事實(shí)上,一個機(jī)器學(xué)習(xí)算法其實(shí)只有兩部分
-
模型 從輸入特征
預(yù)測輸入
的那個函數(shù)
- 目標(biāo)函數(shù) 目標(biāo)函數(shù)取最小(最大)值時(shí)所對應(yīng)的參數(shù)值,就是模型的參數(shù)的最優(yōu)值。很多時(shí)候我們只能獲得目標(biāo)函數(shù)的局部最小(最大)值,因此也只能得到模型參數(shù)的局部最優(yōu)值。
因此,如果你想最簡潔的介紹一個算法,列出這兩個函數(shù)就行了。
接下來,你會用優(yōu)化算法去求取目標(biāo)函數(shù)的最小(最大)值。[隨機(jī)]梯度{下降|上升}算法就是一個優(yōu)化算法。針對同一個目標(biāo)函數(shù),不同的優(yōu)化算法會推導(dǎo)出不同的訓(xùn)練規(guī)則。我們后面還會講其它的優(yōu)化算法。
其實(shí)在機(jī)器學(xué)習(xí)中,算法往往并不是關(guān)鍵,真正的關(guān)鍵之處在于選取特征。選取特征需要我們?nèi)祟悓栴}的深刻理解,經(jīng)驗(yàn)、以及思考。而神經(jīng)網(wǎng)絡(luò)算法的一個優(yōu)勢,就在于它能夠自動學(xué)習(xí)到應(yīng)該提取什么特征,從而使算法不再那么依賴人類,而這也是神經(jīng)網(wǎng)絡(luò)之所以吸引人的一個方面。
現(xiàn)在,經(jīng)過漫長的燒腦,你已經(jīng)具備了學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)的必備知識。下一篇文章,我們將介紹本系列文章的主角:神經(jīng)網(wǎng)絡(luò),以及用來訓(xùn)練神經(jīng)網(wǎng)絡(luò)的大名鼎鼎的算法:反向傳播算法。至于現(xiàn)在,我們應(yīng)該暫時(shí)忘記一切,盡情獎勵自己一下吧。
參考資料
- Tom M. Mitchell, "機(jī)器學(xué)習(xí)", 曾華軍等譯, 機(jī)械工業(yè)出版社