神经网络基本原理简明教程之非线性回归
本博客代碼所需的包和數(shù)據(jù)集:鏈接: https://pan.baidu.com/s/1n-WiIzyMep2qRWDfRc8sYg 提取碼: 4keg
非線性回歸
從這一步開始進(jìn)入了兩層神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí),從而解決非線性問題。
在兩層神經(jīng)網(wǎng)絡(luò)之間,必須有激活函數(shù)連接,從而加入非線性因素,提高神經(jīng)網(wǎng)絡(luò)的能力。所以,我們先從激活函數(shù)學(xué)起,一類是擠壓型的激活函數(shù),常用于簡(jiǎn)單網(wǎng)絡(luò)的學(xué)習(xí);另一類是半線性的激活函數(shù),常用于深度網(wǎng)絡(luò)的學(xué)習(xí)。
接下來(lái)我們將驗(yàn)證著名的萬(wàn)能近似定理,建立一個(gè)雙層的神經(jīng)網(wǎng)絡(luò),來(lái)擬合一個(gè)比較復(fù)雜的函數(shù)。
在上面的雙層神經(jīng)網(wǎng)絡(luò)中,已經(jīng)出現(xiàn)了很多的超參,都會(huì)影響到神經(jīng)網(wǎng)絡(luò)的訓(xùn)練結(jié)果。所以在完成了基本的擬合任務(wù)之后,我們將會(huì)嘗試著調(diào)試這些參數(shù),得到更好的訓(xùn)練效果(又快又好),從而得到超參調(diào)試的第一手經(jīng)驗(yàn)。
激活函數(shù)
1 激活函數(shù)的基本作用
看神經(jīng)網(wǎng)絡(luò)中的一個(gè)神經(jīng)元,為了簡(jiǎn)化,假設(shè)該神經(jīng)元接受三個(gè)輸入,分別為x1,x2,x3,那么:
激活函數(shù)也就是a=σ(z)這一步了,他有什么作用呢?
1 給神經(jīng)網(wǎng)絡(luò)增加非線性因素
2 把公式1的計(jì)算結(jié)果壓縮到[0,1]之間,便于后面的計(jì)算。
激活函數(shù)的基本性質(zhì):
1 非線性:線性的激活函數(shù)和沒有激活函數(shù)一樣
2可導(dǎo)性:做誤差反向傳播和梯度下降,必須要保證激活函數(shù)的可導(dǎo)性
3 單調(diào)性:單一的輸入會(huì)得到單一的輸出,較大值的輸入得到較大值的輸出
在物理試驗(yàn)中使用的繼電器,是最初的激活函數(shù)的原型:當(dāng)輸入電流大于一個(gè)閾值時(shí),會(huì)產(chǎn)生足夠的磁場(chǎng),從而打開下一級(jí)電源通道,如下圖所示:
用到神經(jīng)網(wǎng)絡(luò)中的概念,用‘1’來(lái)代表一個(gè)神經(jīng)元被激活,‘0’代表一個(gè)神經(jīng)元未被激活。
這個(gè)Step函數(shù)有什么不好的地方呢?主要的一點(diǎn)就是,他的梯度(導(dǎo)數(shù))恒為零(個(gè)別點(diǎn)除外)。反向傳播公式中,梯度傳遞用到了鏈?zhǔn)椒▌t,如果在這樣一個(gè)連乘的式子其中有一項(xiàng)是零,這樣的梯度就會(huì)恒為零,是沒有辦法進(jìn)行反向傳播的。
2 何時(shí)會(huì)用到激活函數(shù)
激活函數(shù)用在神經(jīng)網(wǎng)絡(luò)的層與層之間的連接,神經(jīng)網(wǎng)絡(luò)的最后一層不用激活函數(shù)。
神經(jīng)網(wǎng)絡(luò)不管有多少層,最后的輸出層決定了這個(gè)神經(jīng)網(wǎng)絡(luò)能干什么。在單層神經(jīng)網(wǎng)絡(luò)中,我們學(xué)習(xí)到了以下示例:
從上表可以看到,我們一直沒有使用激活函數(shù),而只使用了分類函數(shù)。對(duì)于多層神經(jīng)網(wǎng)絡(luò)也是如此,在最后一層只會(huì)用到分類函數(shù)來(lái)完成二分類或多分類任務(wù),如果是擬合任務(wù),則不需要分類函數(shù)。
很多文字材料中通常把激活函數(shù)和分類函數(shù)混淆在一起說(shuō),原因其實(shí)只有一個(gè):在二分類任務(wù)中使用的Logistic分類函數(shù)與在神經(jīng)網(wǎng)絡(luò)之間連接的Sigmoid激活函數(shù),是同樣的形式。所以它既是激活函數(shù),又是分類函數(shù),是個(gè)特例。
簡(jiǎn)言之:
1 神經(jīng)網(wǎng)絡(luò)最后一層不需要激活函數(shù)
2 激活函數(shù)只用于連接前后兩層神經(jīng)網(wǎng)絡(luò)
擠壓型激活函數(shù) Squashing Function
又可以叫飽和型激活函數(shù),因?yàn)樵谳斎胫涤虻慕^對(duì)值較大的時(shí)候,它的輸出在兩端是飽和的。擠壓型激活函數(shù)中,用的最多的是Sigmoid函數(shù),所謂Sigmoid函數(shù),原意是指一類函數(shù),它們都具有S形的函數(shù)曲線以及壓縮輸入值域的作用,所以又叫擠壓型激活函數(shù)。
1 對(duì)數(shù)幾率函數(shù) Sigmoid Function
對(duì)率函數(shù),在用于激活函數(shù)時(shí)常常被稱為Sigmoid函數(shù),因?yàn)樗亲畛S玫腟igmoid函數(shù)。
優(yōu)點(diǎn)
從函數(shù)圖像來(lái)看,sigmoid函數(shù)的作用是將輸入壓縮到(0, 1)這個(gè)區(qū)間范圍內(nèi),這種輸出在0~1之間的函數(shù)可以用來(lái)模擬一些概率分布的情況。他還是一個(gè)連續(xù)函數(shù),導(dǎo)數(shù)簡(jiǎn)單易求。
從數(shù)學(xué)上來(lái)看,Sigmoid函數(shù)對(duì)中央?yún)^(qū)的信號(hào)增益較大,對(duì)兩側(cè)區(qū)的信號(hào)增益小,在信號(hào)的特征空間映射上,有很好的效果。
從神經(jīng)科學(xué)上來(lái)看,中央?yún)^(qū)酷似神經(jīng)元的興奮態(tài),兩側(cè)區(qū)酷似神經(jīng)元的抑制態(tài),因而在神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)方面,可以將重點(diǎn)特征推向中央?yún)^(qū), 將非重點(diǎn)特征推向兩側(cè)區(qū)。
分類功能:我們經(jīng)常聽到這樣的對(duì)白“你覺得這件事情成功概率有多大?”“我有六成把握能成功”。sigmoid函數(shù)在這里就起到了如何把一個(gè)數(shù)值轉(zhuǎn)化成一個(gè)通俗意義上的把握的表示。值越大,那么這個(gè)神經(jīng)元對(duì)于這張圖里有這樣一條線段的把握就越大,經(jīng)過(guò)sigmoid函數(shù)之后的結(jié)果就越接近100%,也就是1這樣一個(gè)值,表現(xiàn)在圖里,也就是這個(gè)神經(jīng)元越興奮(亮)。
缺點(diǎn)
exp()計(jì)算代價(jià)大。
反向傳播時(shí)梯度消失:從梯度圖像中可以看到,sigmoid的梯度在兩端都會(huì)接近于0,根據(jù)鏈?zhǔn)椒▌t,如果傳回的誤差是δ,那么梯度傳遞函數(shù)是δ?a′(z),而a′(z)這時(shí)是零,也就是說(shuō)整體的梯度是零。這也就很容易出現(xiàn)梯度消失的問題,并且這個(gè)問題可能導(dǎo)致網(wǎng)絡(luò)收斂速度比較慢,比如采取MSE作為損失函數(shù)算法時(shí)。
給個(gè)純粹數(shù)學(xué)的例子吧,假定我們的學(xué)習(xí)速率是0.2,sigmoid函數(shù)值是0.9,如果我們想把這個(gè)函數(shù)的值降到0.5,需要經(jīng)過(guò)多少步呢?
我們先來(lái)做數(shù)值計(jì)算:
上半部分那條五彩斑斕的曲線就是迭代更新的過(guò)程了,一共迭代了多少次呢?根據(jù)程序統(tǒng)計(jì),sigmoid迭代了67次才從0.9衰減到了接近0.5的水準(zhǔn)。有同學(xué)可能會(huì)說(shuō)了,才67次嘛,這個(gè)次數(shù)也不是很多啊!確實(shí),從1層來(lái)看,這個(gè)速度還是可以接受的,但是神經(jīng)網(wǎng)絡(luò)只有這一層嗎?多層疊加之后的sigmoid函數(shù),因?yàn)榉聪騻鞑サ逆準(zhǔn)椒▌t,兩層的梯度相乘,每次更新的步長(zhǎng)更小,需要的次數(shù)更多,也就是速度更加慢。如果還是沒有反應(yīng)過(guò)來(lái)的同學(xué)呢,可以先向下看relu函數(shù)的收斂速度。
此外,如果輸入數(shù)據(jù)是(-1, 1)范圍內(nèi)的均勻分布的數(shù)據(jù)會(huì)導(dǎo)致什么樣的結(jié)果呢?經(jīng)過(guò)sigmoid函數(shù)處理之后這些數(shù)據(jù)的均值就從0變到了0.5,導(dǎo)致了均值的漂移,在很多應(yīng)用中,這個(gè)性質(zhì)是不好的。
2 Tanh函數(shù)
TanHyperbolic,雙曲正切函數(shù)。
優(yōu)點(diǎn)
具有Sigmoid的所有優(yōu)點(diǎn)。
無(wú)論從理論公式還是函數(shù)圖像,這個(gè)函數(shù)都是一個(gè)和sigmoid非常相像的激活函數(shù),他們的性質(zhì)也確實(shí)如此。但是比起sigmoid,tanh減少了一個(gè)缺點(diǎn),就是他本身是零均值的,也就是說(shuō),在傳遞過(guò)程中,輸入數(shù)據(jù)的均值并不會(huì)發(fā)生改變,這就使他在很多應(yīng)用中能表現(xiàn)出比sigmoid優(yōu)異一些的效果。
缺點(diǎn)
exp()計(jì)算代價(jià)大。
梯度消失。
3 其它函數(shù)
半線性激活函數(shù)
又可以叫非飽和型激活函數(shù)。
1 ReLU函數(shù)
Rectified Linear Unit,修正線性單元,線性整流函數(shù),斜坡函數(shù)。
仿生學(xué)原理
相關(guān)大腦方面的研究表明生物神經(jīng)元的信息編碼通常是比較分散及稀疏的。通常情況下,大腦中在同一時(shí)間大概只有1%-4%的神經(jīng)元處于活躍狀態(tài)。使用線性修正以及正則化(regularization)可以對(duì)機(jī)器神經(jīng)網(wǎng)絡(luò)中神經(jīng)元的活躍度(即輸出為正值)進(jìn)行調(diào)試;相比之下,邏輯函數(shù)在輸入為0時(shí)達(dá)到 ,即已經(jīng)是半飽和的穩(wěn)定狀態(tài),不夠符合實(shí)際生物學(xué)對(duì)模擬神經(jīng)網(wǎng)絡(luò)的期望。不過(guò)需要指出的是,一般情況下,在一個(gè)使用修正線性單元(即線性整流)的神經(jīng)網(wǎng)絡(luò)中大概有50%的神經(jīng)元處于激活態(tài)。
優(yōu)點(diǎn)
1 反向?qū)?shù)恒等于1,更加有效率的反向傳播梯度值,收斂速度快
2 避免梯度消失問題
3 計(jì)算簡(jiǎn)單,速度快
4 活躍度的分散性使得神經(jīng)網(wǎng)絡(luò)的整體計(jì)算成本下降
缺點(diǎn)
無(wú)界。
梯度很大的時(shí)候可能導(dǎo)致的神經(jīng)元“死”掉。
而這個(gè)死掉的原因是什么呢?是因?yàn)楹艽蟮奶荻葘?dǎo)致更新之后的網(wǎng)絡(luò)傳遞過(guò)來(lái)的輸入是小于零的,從而導(dǎo)致relu的輸出是0,計(jì)算所得的梯度是零,然后對(duì)應(yīng)的神經(jīng)元不更新,從而使relu輸出恒為零,對(duì)應(yīng)的神經(jīng)元恒定不更新,等于這個(gè)relu失去了作為一個(gè)激活函數(shù)的夢(mèng)想。問題的關(guān)鍵點(diǎn)就在于輸入小于零時(shí),relu回傳的梯度是零,從而導(dǎo)致了后面的不更新。在學(xué)習(xí)率設(shè)置不恰當(dāng)?shù)那闆r下,很有可能網(wǎng)絡(luò)中大部分神經(jīng)元“死”掉,也就是說(shuō)不起作用了。
用和sigmoid函數(shù)那里更新相似的算法步驟和參數(shù),來(lái)模擬一下relu的梯度下降次數(shù),也就是學(xué)習(xí)率α=0.2,希望函數(shù)值從0.9衰減到0.5,這樣需要多少步呢?
也就是說(shuō),同樣的學(xué)習(xí)速率,relu函數(shù)只需要兩步就可以做到sigmoid需要67步才能衰減到的程度!
2 Leaky ReLU函數(shù)
PReLU,帶泄露的線性整流函數(shù)。
優(yōu)點(diǎn)
繼承了ReLU函數(shù)的優(yōu)點(diǎn)。
相比較于relu函數(shù),leaky relu同樣有收斂快速和運(yùn)算復(fù)雜度低的優(yōu)點(diǎn),而且由于給了x<0時(shí)一個(gè)比較小的梯度α,使得x<0時(shí)依舊可以進(jìn)行梯度傳遞和更新,可以在一定程度上避免神經(jīng)元“死”掉的問題。
3 Softplus
ELU
BenIdentity
單入單出的雙層神經(jīng)網(wǎng)絡(luò)
非線性回歸
1 提出問題一
在工程實(shí)踐中,我們最常遇到不是線性問題,而是非線性問題。例如下面這條正弦曲線:
問題:使用神經(jīng)網(wǎng)絡(luò)如何擬合一條有很強(qiáng)規(guī)律的曲線,比如正弦曲線?
2 提出問題二
前面的正弦函數(shù),看上去是非常有規(guī)律的,也許單層神經(jīng)網(wǎng)絡(luò)很容易就做到了。如果是更復(fù)雜的曲線,單層神經(jīng)網(wǎng)絡(luò)還能輕易做到嗎?比如下面這張圖,給出如下一批訓(xùn)練數(shù)據(jù),如何使用神經(jīng)網(wǎng)絡(luò)方法來(lái)擬合這條曲線?
原則上說(shuō),如果你有足夠的耐心,愿意花很高的時(shí)間成本和計(jì)算資源,總可以用多項(xiàng)式回歸的方式來(lái)解決這個(gè)問題,但是,在本章,我們將會(huì)學(xué)習(xí)另外一個(gè)定理:前饋神經(jīng)網(wǎng)絡(luò)的通用近似定理。
上面這條“蛇形”曲線,實(shí)際上是由下面這個(gè)公式添加噪音后生成的:
我們特意把數(shù)據(jù)限制在[0,1]之間,避免做歸一化的麻煩。要是覺得這個(gè)公式還不夠復(fù)雜,大家可以用更復(fù)雜的公式去自己做試驗(yàn)。
以上問題可以叫做非線性回歸,即自變量X和因變量Y之間不是線性關(guān)系。常用的傳統(tǒng)的處理方法有線性迭代法、分段回歸法、迭代最小二乘法等。在神經(jīng)網(wǎng)絡(luò)中,解決這類問題的思路非常簡(jiǎn)單,就是使用帶有一個(gè)隱層的兩層神經(jīng)網(wǎng)絡(luò)。
3 回歸模型的評(píng)估標(biāo)準(zhǔn)
基本數(shù)學(xué)概念
如果結(jié)果為正,表示X,Y是正相關(guān)。
回歸模型評(píng)估標(biāo)準(zhǔn)
回歸問題主要是求值,評(píng)價(jià)標(biāo)準(zhǔn)主要是看求得值與實(shí)際結(jié)果的偏差有多大,所以,回歸問題主要以下方法來(lái)評(píng)價(jià)模型。
得出的值與樣本數(shù)量有關(guān)系,假設(shè)有1000個(gè)測(cè)試樣本,得到的值是120;如果只有100個(gè)測(cè)試樣本,得到的值可能是11,我們不能說(shuō)11就比120要好。
用多項(xiàng)式回歸法擬合正弦曲線
1 多項(xiàng)式回歸的概念
多項(xiàng)式回歸有幾種形式:
一元一次線性模型
因?yàn)橹挥幸豁?xiàng),所以不能稱為多項(xiàng)式了。它可以解決單變量的線性回歸,我們?cè)诘?章學(xué)習(xí)過(guò)相關(guān)內(nèi)容。其模型為:
多元一次多項(xiàng)式
多變量的線性回歸,我們?cè)诘?章學(xué)習(xí)過(guò)相關(guān)內(nèi)容。其模型為:
這里的多變量,是指樣本數(shù)據(jù)的特征值為多個(gè),上式中的x1,x2,…,xm代表了m個(gè)特征值。
一元多次多項(xiàng)式
單變量的非線性回歸,比如上面這個(gè)正弦曲線的擬合問題,很明顯不是線性問題,但是只有一個(gè)x特征值,所以不滿足前兩種形式。如何解決這種問題呢?
有一個(gè)定理:任意一個(gè)函數(shù)在一個(gè)較小的范圍內(nèi),都可以用多項(xiàng)式任意逼近。因此在實(shí)際工程實(shí)踐中,有時(shí)候可以不管y值與x值的數(shù)學(xué)關(guān)系究竟是什么,而是強(qiáng)行用回歸分析方法進(jìn)行近似的擬合。
那么如何得到更多的特征值呢?對(duì)于只有一個(gè)特征值的問題,人們發(fā)明了一種聰明的辦法,就是把特征值的高次方作為另外的特征值,加入到回歸分析中,用公式描述:
上式中x是原有的唯一特征值,xm是利用x的m次方作為額外的特征值,這樣就把特征值的數(shù)量從1個(gè)變?yōu)閙個(gè)。
可以看到公式4和上面的公式2是一樣的,所以解決方案也一樣。
多元多次多項(xiàng)式
多變量的非線性回歸,其參數(shù)與特征組合繁復(fù),但最終都可以歸結(jié)為公式2和公式4的形式。
所以,不管是幾元幾次多項(xiàng)式,我們都可以使用第5章學(xué)到的方法來(lái)解決。在用代碼具體實(shí)現(xiàn)之前,我們先學(xué)習(xí)一些前人總結(jié)的經(jīng)驗(yàn)。
先看一個(gè)被經(jīng)常拿出來(lái)講解的例子:
一堆散點(diǎn),看上去像是一條帶有很大噪音的正弦曲線,從左上到右下,分別是1次多項(xiàng)式、2次多項(xiàng)式…10次多項(xiàng)式,其中:
第4、5、6、7圖是比較理想的擬合
第1、2、3圖欠擬合,多項(xiàng)式的次數(shù)不夠高
第8、9、10圖,多項(xiàng)式次數(shù)過(guò)高,過(guò)擬合了
再看下表中多項(xiàng)式的權(quán)重值:
項(xiàng)數(shù)越多,權(quán)重值越大。這是為什么呢?
在做多項(xiàng)式擬合之前,所有的特征值都會(huì)先做歸一化,然后再獲得x的平方值,三次方值等等。在歸一化之后,x的值變成了[0,1]之間,那么x的平方值會(huì)比x值要小,x的三次方值會(huì)比x的平方值要小。假設(shè)x=0.5,x2=0.25,x3=0.125,所以次數(shù)越高,權(quán)重值會(huì)越大,特征值與權(quán)重值的乘積才會(huì)是一個(gè)不太小的數(shù),以此來(lái)彌補(bǔ)特征值小的問題。
2 用二次多項(xiàng)式擬合
鑒于以上的認(rèn)知,我們要考慮使用幾次的多項(xiàng)式來(lái)擬合正弦曲線。在沒有什么經(jīng)驗(yàn)的情況下,可以先試一下二次多項(xiàng)式,即:
數(shù)據(jù)增強(qiáng)
在ch08.train.npz中,讀出來(lái)的XTrain數(shù)組,只包含1列x的原始值,根據(jù)公式5,我們需要再增加一列x的平方值,所以代碼如下:
從SimpleDataReader類中派生出子類DataReaderEx,然后添加Add()方法,先計(jì)算XTrain第一列的平方值放入矩陣X中,然后再把X合并到XTrain右側(cè),這樣XTrain就變成了兩列,第一列是x的原始值,第二列是x的平方值。
主程序
在主程序中,先加載數(shù)據(jù),做數(shù)據(jù)增強(qiáng),然后建立一個(gè)net,參數(shù)num_input=2,對(duì)應(yīng)著XTrain中的兩列數(shù)據(jù),相當(dāng)于兩個(gè)特征值,
if __name__ == '__main__':dataReader = DataReaderEx(file_name)dataReader.ReadData()dataReader.Add()print(dataReader.XTrain.shape)# netnum_input = 2num_output = 1params = HyperParameters(num_input, num_output, eta=0.2, max_epoch=10000, batch_size=10, eps=0.005, net_type=NetType.Fitting)net = NeuralNet(params)net.train(dataReader, checkpoint=10)ShowResult(net, dataReader, params.toString())運(yùn)行結(jié)果
擬合結(jié)果
從loss曲線上看,沒有任何損失值下降的趨勢(shì);再看擬合情況,只擬合成了一條直線。這說(shuō)明二次多項(xiàng)式不能滿足要求。以下是最后幾行的打印輸出:
3 用三次多項(xiàng)式擬合
三次多項(xiàng)式的公式:
在二次多項(xiàng)式的基礎(chǔ)上,把訓(xùn)練數(shù)據(jù)的再增加一列x的三次方,作為一個(gè)新的特征。以下為數(shù)據(jù)增強(qiáng)代碼:
同時(shí)不要忘記修改主過(guò)程參數(shù)中的num_input值:
num_input = 3再次運(yùn)行:
損失函數(shù)值
擬合結(jié)果
損失函數(shù)值下降得很平穩(wěn),說(shuō)明網(wǎng)絡(luò)訓(xùn)練效果還不錯(cuò)。擬合的結(jié)果也很令人滿意,雖然紅色線沒有嚴(yán)絲合縫地落在藍(lán)色樣本點(diǎn)內(nèi),但是這完全是因?yàn)橛?xùn)練的次數(shù)不夠多,有興趣的讀者可以修改超參后做進(jìn)一步的試驗(yàn)。
以下為打印輸出:
...... 2349 49 0.005047530761174165 2359 49 0.005059504052006337 2369 49 0.0050611643902918856 2379 49 0.004949680631526745 W= [[ 10.49907256][-31.06694195][ 20.73039288]] B= [[-0.07999603]]可以觀察到達(dá)到0.005的損失值,這個(gè)神經(jīng)網(wǎng)絡(luò)迭代了2379個(gè)epoch。而在二次多項(xiàng)式的試驗(yàn)中,用了10000次的迭代也沒有達(dá)到要求。
4 用四次多項(xiàng)式擬合
在三次多項(xiàng)式得到比較滿意的結(jié)果后,我們自然會(huì)想知道用四次多項(xiàng)式還會(huì)給我們帶來(lái)驚喜嗎?讓我們一起試一試。
第一步依然是增加x的4次方作為特征值:
X = self.XTrain[:,0:1]**4 self.XTrain = np.hstack((self.XTrain, X))第二步設(shè)置超參num_input=4,然后訓(xùn)練:
損失函數(shù)值
擬合結(jié)果
從以上結(jié)果可以得到以下結(jié)論:
1 損失值在下降了一定程度都,一直處于平緩期,不再下降,說(shuō)明網(wǎng)絡(luò)能力到了一定的限制;
2 損失值達(dá)到0.005時(shí),迭代了8289個(gè)epoch,比三次多項(xiàng)式的2379個(gè)epoch要多很多,說(shuō)明四次多項(xiàng)式多出的一個(gè)特征值,沒有給我們帶來(lái)什么好處,反而是增加了網(wǎng)絡(luò)訓(xùn)練的復(fù)雜度。
由此可以知道,多項(xiàng)式次數(shù)并不是越高越好,對(duì)不同的問題,有特定的限制,需要在實(shí)踐中摸索,并無(wú)理論指導(dǎo)。
import numpy as np import matplotlib.pyplot as pltfrom HelperClass.NeuralNet_1_2 import *file_name = "ch08.train.npz"class DataReaderEx(DataReader_1_3):def Add(self):X = self.XTrain[:,]**2self.XTrain = np.hstack((self.XTrain, X))X = self.XTrain[:,0:1]**3self.XTrain = np.hstack((self.XTrain, X))X = self.XTrain[:,0:1]**4self.XTrain = np.hstack((self.XTrain, X))def ShowResult(net, dataReader, title):# draw train dataX,Y = dataReader.XTrain, dataReader.YTrainplt.plot(X[:,0], Y[:,0], '.', c='b')# create and draw visualized validation dataTX1 = np.linspace(0,1,100).reshape(100,1)TX = np.hstack((TX1, TX1[:,]**2))TX = np.hstack((TX, TX1[:,]**3))TX = np.hstack((TX, TX1[:,]**4))TY = net.inference(TX)plt.plot(TX1, TY, 'x', c='r')plt.title(title)plt.show() # end defif __name__ == '__main__':dataReader = DataReaderEx(file_name)dataReader.ReadData()dataReader.Add()print(dataReader.XTrain.shape)# netnum_input = 4num_output = 1params = HyperParameters_1_1(num_input, num_output, eta=0.2, max_epoch=10000, batch_size=10, eps=0.005, net_type=NetType.Fitting)net = NeuralNet_1_2(params)net.train(dataReader, checkpoint=10)ShowResult(net, dataReader, params.toString())用多項(xiàng)式回歸法擬合復(fù)合函數(shù)曲線
在本節(jié)中,我們嘗試著用多項(xiàng)式解決問題二,擬合復(fù)雜的函數(shù)曲線。
再把這條“眼鏡蛇形”曲線拿出來(lái)觀察一下,不但有正弦式的波浪,還有線性的爬升,轉(zhuǎn)折處也不是很平滑,所以難度很大。從正弦曲線的擬合經(jīng)驗(yàn)來(lái)看,三次多項(xiàng)式以下肯定無(wú)法解決,所以我們可以從四次多項(xiàng)式開始試驗(yàn)。
1 用四次多項(xiàng)式擬合
代碼與正弦函數(shù)擬合區(qū)別不大,不再贅述,我們本次主要說(shuō)明解決問題的思路。
超參的設(shè)置情況:
num_input = 4num_output = 1 params = HyperParameters(num_input, num_output, eta=0.2, max_epoch=10000, batch_size=10, eps=1e-3, net_type=NetType.Fitting)最開始設(shè)置max_epoch=10,000,運(yùn)行結(jié)果如下:
損失函數(shù)歷史
曲線擬合結(jié)果
可以看到損失函數(shù)值還有下降的空間,并且擬合情況很糟糕。
所以我們?cè)黾觤ax_epoch到100,000再試一次:
從上圖看,損失函數(shù)值到了一定程度后就不再下降了,說(shuō)明網(wǎng)絡(luò)能力有限。再看下面打印輸出的具體數(shù)值,似乎0.005左右是一個(gè)極限。
2 用六次多項(xiàng)式擬合
接下來(lái)跳過(guò)5次多項(xiàng)式,直接用6次多項(xiàng)式來(lái)擬合。這次不需要把max_epoch設(shè)置得很大,可以先試試50000個(gè)epoch:
打印輸出:
從損失函數(shù)歷史圖看,好像損失值下降得比較理想,但是實(shí)際看打印輸出時(shí),損失值最開始幾輪就已經(jīng)是0.0047了,到了最后一輪,是0.0046,并不理想,說(shuō)明網(wǎng)絡(luò)能力還是不夠。因此在這個(gè)級(jí)別上,不用再花時(shí)間繼續(xù)試驗(yàn)了,應(yīng)該還需要提高多項(xiàng)式次數(shù)。
3 用八次多項(xiàng)式擬合
再跳過(guò)7次多項(xiàng)式,直接使用8次多項(xiàng)式。先把max_epoch設(shè)置為50000試驗(yàn)一下:
損失函數(shù)值下降的趨勢(shì)非常可喜,似乎還沒有遇到什么瓶頸;擬合的效果也已經(jīng)初步顯現(xiàn)出來(lái)了。下方的打印輸出,損失函數(shù)值已經(jīng)可以突破0.004的下限了。
根據(jù)以上情況,可以認(rèn)為8次多項(xiàng)式很有可能得到比較理想的解,所以我們需要增加max_epoch數(shù)值,讓網(wǎng)絡(luò)得到充分的訓(xùn)練。好,設(shè)置max_epoch=1000000試一下!沒錯(cuò),是一百萬(wàn)次!開始運(yùn)行后,大家就可以去做些別的事情,一兩個(gè)小時(shí)之后再回來(lái)看結(jié)果。
從結(jié)果來(lái)看,損失函數(shù)值還有下降的空間和可能性,已經(jīng)到了0.0016的水平(從后面的章節(jié)中可以知道,0.001的水平可以得到比較好的擬合效果),擬合效果也已經(jīng)初步呈現(xiàn)出來(lái)了,所有轉(zhuǎn)折的地方都可以復(fù)現(xiàn),只是精度不夠,相信更多的訓(xùn)練次數(shù)可以達(dá)到更好的效果。
分析打印出的W權(quán)重值,x的原始特征值的權(quán)重值比后面的權(quán)重值小了一到兩個(gè)數(shù)量級(jí),這與歸一化后x的高次冪的數(shù)值很小有關(guān)系。
至此,我們可以得出結(jié)論,多項(xiàng)式回歸確實(shí)可以解決復(fù)雜曲線擬合問題,但是代價(jià)有些高,我們訓(xùn)練了一百萬(wàn)次,才得到初步滿意的結(jié)果。下一節(jié)我們將要學(xué)習(xí)更好的方法。
import numpy as np import matplotlib.pyplot as pltfrom HelperClass.NeuralNet_1_2 import *file_name = "ch09.train.npz"class DataReaderEx(DataReader_1_3):def Add(self):X = self.XTrain[:,0:1]**2self.XTrain = np.hstack((self.XTrain, X))X = self.XTrain[:,0:1]**3self.XTrain = np.hstack((self.XTrain, X))X = self.XTrain[:,0:1]**4self.XTrain = np.hstack((self.XTrain, X))X = self.XTrain[:,0:1]**5self.XTrain = np.hstack((self.XTrain, X))X = self.XTrain[:,0:1]**6self.XTrain = np.hstack((self.XTrain, X))X = self.XTrain[:,0:1]**7self.XTrain = np.hstack((self.XTrain, X))X = self.XTrain[:,0:1]**8self.XTrain = np.hstack((self.XTrain, X))def ShowResult(net, dataReader, title):# draw train dataX,Y = dataReader.XTrain, dataReader.YTrainplt.plot(X[:,0], Y[:,0], '.', c='b')# create and draw visualized validation dataTX1 = np.linspace(0,1,100).reshape(100,1)TX2 = np.hstack((TX1, TX1[:,]**2))TX3 = np.hstack((TX2, TX1[:,]**3))TX4 = np.hstack((TX3, TX1[:,]**4))TX5 = np.hstack((TX4, TX1[:,]**5))TX6 = np.hstack((TX5, TX1[:,]**6))TX7 = np.hstack((TX6, TX1[:,]**7))TX8 = np.hstack((TX7, TX1[:,]**8))TY = net.inference(TX8)plt.plot(TX1, TY, 'x', c='r')plt.title(title)plt.show() #end defif __name__ == '__main__':dataReader = DataReaderEx(file_name)dataReader.ReadData()dataReader.Add()print(dataReader.XTrain.shape)# netnum_input = 8num_output = 1hp = HyperParameters_1_1(num_input, num_output, eta=0.2, max_epoch=50000, batch_size=10, eps=1e-3, net_type=NetType.Fitting)#params = HyperParameters(eta=0.2, max_epoch=1000000, batch_size=10, eps=1e-3, net_type=NetType.Fitting)net = NeuralNet_1_2(hp)net.train(dataReader, checkpoint=500)ShowResult(net, dataReader, "Polynomial")雙層神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)非線性回歸
萬(wàn)能近似定理
這里有一篇論文,Kurt Hornik在1991年發(fā)表的,說(shuō)明了含有一個(gè)隱層的神經(jīng)網(wǎng)絡(luò)能擬合任意復(fù)雜函數(shù)。
https://www.sciencedirect.com/science/article/pii/089360809190009T
簡(jiǎn)言之:兩層前饋神經(jīng)網(wǎng)絡(luò)(即一個(gè)隱層加一個(gè)輸出層)和至少一層具有任何一種擠壓性質(zhì)的激活函數(shù),只要隱層的神經(jīng)元的數(shù)量足夠,它可以以任意的精度來(lái)近似任何從一個(gè)有限維空間到另一個(gè)有限維空間的Borel可測(cè)函數(shù)。當(dāng)然這個(gè)函數(shù)需要是單調(diào)遞增有界的。
注意,它要求的是擠壓性質(zhì)的激活函數(shù),也就是類似Sigmoid的函數(shù),如果用ReLU函數(shù)不能實(shí)現(xiàn)這個(gè)效果。
萬(wàn)能近似定理意味著無(wú)論我們?cè)噲D學(xué)習(xí)什么函數(shù),我們知道一個(gè)大的MLP一定能夠表示這個(gè)函數(shù)。然而,我們不能保證訓(xùn)練算法能夠?qū)W得這個(gè)函數(shù)。即使MLP能夠表示該函數(shù),學(xué)習(xí)也可能因兩個(gè)不同的原因而失敗。
1 用于訓(xùn)練的優(yōu)化算法可能找不到用于期望函數(shù)的參數(shù)值;
2 訓(xùn)練算法可能由于過(guò)擬合而選擇了錯(cuò)誤的函數(shù)。
根據(jù)“沒有免費(fèi)的午餐”定理,說(shuō)明了沒有普遍優(yōu)越的機(jī)器學(xué)習(xí)算法。前饋網(wǎng)絡(luò)提供了表示函數(shù)的萬(wàn)能系統(tǒng),在這種意義上,給定一個(gè)函數(shù),存在一個(gè)前饋網(wǎng)絡(luò)能夠近似該函數(shù)。但不存在萬(wàn)能的過(guò)程既能夠驗(yàn)證訓(xùn)練集上的特殊樣本,又能夠選擇一個(gè)函數(shù)來(lái)擴(kuò)展到訓(xùn)練集上沒有的點(diǎn)。
總之,具有單層的前饋網(wǎng)絡(luò)足以表示任何函數(shù),但是網(wǎng)絡(luò)層可能大得不可實(shí)現(xiàn),并且可能無(wú)法正確地學(xué)習(xí)和泛化。在很多情況下,使用更深的模型能夠減少表示期望函數(shù)所需的單元的數(shù)量,并且可以減少泛化誤差。
2 定義神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)
通過(guò)觀察樣本數(shù)據(jù)的范圍,x是在[0,1],y是[-0.5,0.5],這樣我們就不用做數(shù)據(jù)歸一化了。這條線看起來(lái)像一條處于攻擊狀態(tài)的眼鏡蛇!由于是擬合任務(wù),所以標(biāo)簽值y是一系列的實(shí)際數(shù)值,并不是0/1這樣的特殊標(biāo)記。
根據(jù)萬(wàn)能近似定理的要求,我們定義一個(gè)兩層的神經(jīng)網(wǎng)絡(luò),輸入層不算,一個(gè)隱藏層,含3個(gè)神經(jīng)元,一個(gè)輸出層。
為什么用3個(gè)神經(jīng)元呢?因?yàn)檩斎雽又挥幸粋€(gè)特征值,我們不需要在隱層放很多的神經(jīng)元,先用3個(gè)神經(jīng)元試驗(yàn)一下。如果不夠的話再增加,神經(jīng)元數(shù)量是由超參控制的。
3 前向計(jì)算
其中,z是樣本預(yù)測(cè)值,y是樣本的標(biāo)簽值,這里的z是第二層的輸出Z。
4 反向傳播
我們比較一下本章的神經(jīng)網(wǎng)絡(luò)和第5章的神經(jīng)網(wǎng)絡(luò)的區(qū)別:
本章使用了真正的“網(wǎng)絡(luò)”,而第5章充其量只是一個(gè)神經(jīng)元而已。再看本章的網(wǎng)絡(luò)的右半部分,從隱層到輸出層的結(jié)構(gòu),和第5章的神經(jīng)元結(jié)構(gòu)一摸一樣,只是輸入為3個(gè)特征,而第5章的輸入為兩個(gè)特征。比較正向計(jì)算公式的話,也可以得到相同的結(jié)論。這就意味著反向傳播的公式應(yīng)該也是一樣的。
由于我們第一次接觸雙層神經(jīng)網(wǎng)絡(luò),所以需要推導(dǎo)一下反向傳播的各個(gè)過(guò)程。看一下計(jì)算圖,然后用鏈?zhǔn)角髮?dǎo)法則反推。
求損失函數(shù)對(duì)隱層的反向誤差
下面的內(nèi)容是雙層神經(jīng)網(wǎng)絡(luò)獨(dú)有的內(nèi)容,也是深度神經(jīng)網(wǎng)絡(luò)的基礎(chǔ),請(qǐng)大家仔細(xì)閱讀體會(huì)。我們先看看正向計(jì)算和反向計(jì)算圖:
圖中:
1 藍(lán)色矩形表示數(shù)值或矩陣
2 藍(lán)色圓形表示計(jì)算單元
3 藍(lán)色的箭頭表示正向計(jì)算過(guò)程
4 紅色的箭頭表示反向計(jì)算過(guò)程
如果想計(jì)算W1和B1的反向誤差,必須先得到Z1的反向誤差,再向上追溯,可以看到Z1->A1->Z2->Loss這條線,Z1->A1是一個(gè)激活函數(shù)的運(yùn)算,比較特殊,所以我們先看Loss->Z->A1如何解決。
4 代碼實(shí)現(xiàn)
主要講解神經(jīng)網(wǎng)絡(luò)NeuralNet2類的代碼,其它的類都是輔助類。
前向計(jì)算
class NeuralNet2(object):def forward(self, batch_x):# layer 1self.Z1 = np.dot(batch_x, self.wb1.W) + self.wb1.Bself.A1 = Sigmoid().forward(self.Z1)# layer 2self.Z2 = np.dot(self.A1, self.wb2.W) + self.wb2.Bif self.hp.net_type == NetType.BinaryClassifier:self.A2 = Logistic().forward(self.Z2)elif self.hp.net_type == NetType.MultipleClassifier:self.A2 = Softmax().forward(self.Z2)else: # NetType.Fittingself.A2 = self.Z2#end ifself.output = self.A2在Layer2中考慮了多種網(wǎng)絡(luò)類型,在此我們暫時(shí)只關(guān)心NetType.Fitting類型。
反向傳播
class NeuralNet2(object):def backward(self, batch_x, batch_y, batch_a):# 批量下降,需要除以樣本數(shù)量,否則會(huì)造成梯度爆炸m = batch_x.shape[0]# 第二層的梯度輸入 公式5dZ2 = self.A2 - batch_y# 第二層的權(quán)重和偏移 公式6self.wb2.dW = np.dot(self.A1.T, dZ2)/m # 公式7 對(duì)于多樣本計(jì)算,需要在橫軸上做sum,得到平均值self.wb2.dB = np.sum(dZ2, axis=0, keepdims=True)/m # 第一層的梯度輸入 公式8d1 = np.dot(dZ2, self.wb2.W.T) # 第一層的dZ 公式10dZ1,_ = Sigmoid().backward(None, self.A1, d1)# 第一層的權(quán)重和偏移 公式11self.wb1.dW = np.dot(batch_x.T, dZ1)/m# 公式12 對(duì)于多樣本計(jì)算,需要在橫軸上做sum,得到平均值self.wb1.dB = np.sum(dZ1, axis=0, keepdims=True)/m反向傳播部分的代碼完全按照公式推導(dǎo)的結(jié)果實(shí)現(xiàn)。
保存和加載權(quán)重矩陣數(shù)據(jù)
在訓(xùn)練結(jié)束后,或者每個(gè)epoch結(jié)束后,都可以選擇保存訓(xùn)練好的權(quán)重矩陣值,避免每次使用時(shí)重復(fù)訓(xùn)練浪費(fèi)時(shí)間。
而在初始化完畢神經(jīng)網(wǎng)絡(luò)后,可以立刻加載歷史權(quán)重矩陣數(shù)據(jù)(前提是本次的神經(jīng)網(wǎng)絡(luò)設(shè)置與保存時(shí)的一致),這樣可以在歷史數(shù)據(jù)的基礎(chǔ)上繼續(xù)訓(xùn)練,不會(huì)丟失以前的進(jìn)度。
def SaveResult(self):self.wb1.SaveResultValue(self.subfolder, "wb1")self.wb2.SaveResultValue(self.subfolder, "wb2")def LoadResult(self):self.wb1.LoadResultValue(self.subfolder, "wb1")self.wb2.LoadResultValue(self.subfolder, "wb2")輔助類
Activators - 激活函數(shù)類,包括Sigmoid/Tanh/Relu等激活函數(shù)的實(shí)現(xiàn),以及Losistic/Softmax分類函數(shù)的實(shí)現(xiàn)
DataReader - 數(shù)據(jù)操作類,讀取、歸一化、驗(yàn)證集生成、獲得指定類型批量數(shù)據(jù)
HyperParameters2 - 超參類,各層的神經(jīng)元數(shù)量、學(xué)習(xí)率、批大小、網(wǎng)絡(luò)類型、初始化方法等
class HyperParameters2(object):def __init__(self, n_input, n_hidden, n_output, eta=0.1, max_epoch=10000, batch_size=5, eps = 0.1, net_type = NetType.Fitting,init_method = InitialMethod.Xavier):LossFunction - 損失函數(shù)類,包含三種損失函數(shù)的代碼實(shí)現(xiàn)
NeuralNet2 - 神經(jīng)網(wǎng)絡(luò)類,初始化、正向、反向、更新、訓(xùn)練、驗(yàn)證、測(cè)試等一系列方法
TrainingTrace - 訓(xùn)練記錄類,記錄訓(xùn)練過(guò)程中的損失函數(shù)值、驗(yàn)證精度
WeightsBias - 權(quán)重矩陣類,初始化、加載數(shù)據(jù)、保存數(shù)據(jù)
曲線擬合
在上一節(jié)我們已經(jīng)建立和了神經(jīng)網(wǎng)絡(luò)及其輔助功能,現(xiàn)在我們先來(lái)做一下正弦曲線的擬合,然后再試驗(yàn)復(fù)合函數(shù)的曲線擬合。
正弦曲線的擬合
結(jié)果顯示函數(shù)
此函數(shù)用于可視化測(cè)試擬合程度。
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3Dfrom HelperClass2.NeuralNet2 import * from HelperClass2.DataReader import * x_data_name = "../../Data/ch08.train.npz" y_data_name = "../../Data/ch08.test.npz"def ShowResult(net, dataReader, title):# draw train dataX,Y = dataReader.XTrain, dataReader.YTrainplt.plot(X[:,0], Y[:,0], '.', c='b')# create and draw visualized validation dataTX = np.linspace(0,1,100).reshape(100,1)TY = net.inference(TX)plt.plot(TX, TY, 'x', c='r')plt.title(title)plt.show() #end def隱層只有一個(gè)神經(jīng)元的情況
令num_hidden=1,并指定模型名稱為"sin_111",再跑一次試驗(yàn)。
下圖為損失函數(shù)曲線和驗(yàn)證集精度曲線,損失值到0.04附近就很難下降了,而2個(gè)神經(jīng)元的網(wǎng)絡(luò)損失值可以達(dá)到0.004,少一個(gè)數(shù)量級(jí)。驗(yàn)證集精度到82%左右,而2個(gè)神經(jīng)元的網(wǎng)絡(luò)可以達(dá)到97%。
下圖為擬合情況的可視化圖:
可以看到只有中間線性部分?jǐn)M合了,兩端的曲線部分沒有擬合。
打印輸出最后的測(cè)試集精度值為85.7%,不是很理想。所以隱層1個(gè)神經(jīng)元是基本不能工作的,這只比單層神經(jīng)網(wǎng)絡(luò)的線性擬合強(qiáng)一些,距離目標(biāo)還差很遠(yuǎn)。
隱層有兩個(gè)神經(jīng)元的情況
if __name__ == '__main__':dataReader = DataReader(x_data_name, y_data_name)dataReader.ReadData()dataReader.GenerateValidationSet()n_input, n_hidden, n_output = 1, 2, 1eta, batch_size, max_epoch = 0.05, 10, 5000eps = 0.001hp = HyperParameters2(n_input, n_hidden, n_output, eta, max_epoch, batch_size, eps, NetType.Fitting, InitialMethod.Xavier)net = NeuralNet2(hp, "sin_121")#net.LoadResult()net.train(dataReader, 50, True)net.ShowTrainingTrace()ShowResult(net, dataReader, hp.toString())初始化神經(jīng)網(wǎng)絡(luò)類的參數(shù)有兩個(gè),第一個(gè)是超參組合,第二個(gè)是指定模型專有名稱,以便把結(jié)果保存在名稱對(duì)應(yīng)的子目錄中。保存訓(xùn)練結(jié)果的代碼在訓(xùn)練結(jié)束后自動(dòng)調(diào)用,但是如果想加載歷史訓(xùn)練結(jié)果,需要在主過(guò)程中手動(dòng)調(diào)用,比如上面代碼中注釋的那一行:net.LoadResult()。這樣的話,如果下次再訓(xùn)練,就可以在以前的基礎(chǔ)上繼續(xù)訓(xùn)練,不必從頭開始。
注意在主過(guò)程代碼中,我們指定了num_hidden=2,意為隱層神經(jīng)元數(shù)量為2。、
運(yùn)行結(jié)果
下圖為損失函數(shù)曲線和驗(yàn)證集精度曲線,都比較正常:
下圖為擬合情況的可視化圖:
再看下面的打印輸出結(jié)果,最后測(cè)試集的精度為98.8%。如果需要精度更高的話,可以增加迭代次數(shù)。
2 復(fù)合函數(shù)的擬合
基本過(guò)程與正弦曲線相似,區(qū)別是這個(gè)例子要復(fù)雜不少,所以首先需要耐心,增大max_epoch的數(shù)值,多迭代幾次。其次需要精心調(diào)參,找到最佳參數(shù)組合。
隱層只有兩個(gè)神經(jīng)元的情況
擬合情況很不理想,和正弦曲線只用一個(gè)神經(jīng)元的情況類似。
觀察打印輸出的損失值,有波動(dòng),久久徘徊在0.003附近不能下降,說(shuō)明網(wǎng)絡(luò)能力不夠。
以下就是筆者找到的最佳組合:
隱層3個(gè)神經(jīng)元
學(xué)習(xí)率=0.5
批量=10
隱層有三個(gè)神經(jīng)元的情況
if __name__ == '__main__':dataReader = DataReader(x_data_name, y_data_name)dataReader.ReadData()dataReader.GenerateValidationSet()n_input, n_hidden, n_output = 1, 3, 1eta, batch_size, max_epoch = 0.5, 10, 10000eps = 0.001hp = HyperParameters2(n_input, n_hidden, n_output, eta, max_epoch, batch_size, eps, NetType.Fitting, InitialMethod.Xavier)net = NeuralNet2(hp, "model_131")net.train(dataReader, 50, True)net.ShowTrainingTrace()ShowResult(net, dataReader, hp.toString())運(yùn)行結(jié)果
下圖為損失函數(shù)曲線和驗(yàn)證集精度曲線,都比較正常:
下圖為擬合情況的可視化圖:
再看下面的打印輸出結(jié)果,最后測(cè)試集的精度為97.6%。如果需要精度更高的話,可以增加迭代次數(shù)。
3 廣義擬合
至此我們用兩個(gè)可視化的例子完成了曲線擬合,驗(yàn)證了萬(wàn)能近似定理。但是,神經(jīng)網(wǎng)絡(luò)不是設(shè)計(jì)專門用于曲線擬合的,這只是牛刀小試而已,我們用簡(jiǎn)單的例子講解了神經(jīng)網(wǎng)絡(luò)的功能,但是此功能完全可以用于多變量的復(fù)雜非線性回歸。
“曲線”在這里是一個(gè)廣義的概念,它可以代表二維平面上的數(shù)學(xué)曲線,也可以代表工程實(shí)踐中的任何擬合問題,比如房?jī)r(jià)預(yù)測(cè)問題,影響房?jī)r(jià)的自變量可以達(dá)到20個(gè)左右,顯然已經(jīng)超出了線性回歸的范疇,此時(shí)我們可以用多層神經(jīng)網(wǎng)絡(luò)來(lái)做預(yù)測(cè),其準(zhǔn)確度要比線性回歸高很多。在后面我們會(huì)講解這樣的例子。
簡(jiǎn)言之,只要是數(shù)值擬合問題,確定不能用線性回歸的話,都可以用非線性回歸來(lái)嘗試解決。
import numpy as np import matplotlib.pyplot as pltfrom HelperClass2.NeuralNet_2_0 import *train_data_name = "ch09.train.npz" test_data_name = "ch09.test.npz"def ShowResult(net, dataReader, title):# draw train dataX,Y = dataReader.XTrain, dataReader.YTrainplt.plot(X[:,0], Y[:,0], '.', c='b')# create and draw visualized validation dataTX = np.linspace(0,1,100).reshape(100,1)TY = net.inference(TX)plt.plot(TX, TY, 'x', c='r')plt.title(title)plt.show() #end defif __name__ == '__main__':dataReader = DataReader_2_0(train_data_name, test_data_name)dataReader.ReadData()dataReader.GenerateValidationSet()n_input, n_hidden, n_output = 1, 3, 1eta, batch_size, max_epoch = 0.5, 10, 10000eps = 0.001hp = HyperParameters_2_0(n_input, n_hidden, n_output, eta, max_epoch, batch_size, eps, NetType.Fitting, InitialMethod.Xavier)net = NeuralNet_2_0(hp, "complex_131")net.train(dataReader, 50, True)net.ShowTrainingHistory()ShowResult(net, dataReader, hp.toString())非線性回歸的工作原理
1 多項(xiàng)式為何能擬合曲線
先回憶一下本章最開始講的多項(xiàng)式回歸法,它成功地用于正弦曲線和復(fù)合函數(shù)曲線的擬合,其基本工作原理是把單一特征值的高次方做為額外的特征值加入,使得神經(jīng)網(wǎng)絡(luò)可以得到附加的信息用于訓(xùn)練。實(shí)踐證明其方法有效,但是當(dāng)問題比較復(fù)雜時(shí),需要高達(dá)8次方的附加信息,且訓(xùn)練時(shí)間也很長(zhǎng)。
當(dāng)我們使用雙層神經(jīng)網(wǎng)絡(luò)時(shí),在隱層只放置了三個(gè)神經(jīng)元,就輕松解決了復(fù)合函數(shù)擬合的問題,效率高出十幾倍,復(fù)雜度卻降低了幾倍。那么含有隱層的神經(jīng)網(wǎng)絡(luò)究竟是如何完成這個(gè)任務(wù)的呢?
我們以正弦曲線擬合為例來(lái)說(shuō)明這個(gè)問題,首先看一下多項(xiàng)式回歸方法的示意圖:
我們可以回憶一下第5章學(xué)習(xí)的多變量線性回歸問題,公式1實(shí)際上是把一維的x的特征信息,增加到了三維,然后再使用多變量線性回歸來(lái)解決問題的。本來(lái)一維的特征只能得到線性的結(jié)果,但是三維的特征就可以得到非線性的結(jié)果,這就是多項(xiàng)式擬合的原理。
我們用具體的數(shù)值計(jì)算方式來(lái)理解一下其工作過(guò)程。
import numpy as np import matplotlib.pyplot as pltif __name__ == '__main__':x = np.linspace(0,1,10)w = 1.1b = 0.2y = x * w + bp1, = plt.plot(x,y, marker='.')x2 = x*xw2 = -0.5y2 = x * w + x2 * w2 + bp2, = plt.plot(x, y2, marker='s')x3 = x*x*xw3 = 2.3y3 = x * w + x2 * w2 + x3 * w3 + bp3, = plt.plot(x, y3, marker='x')plt.grid()plt.xlabel("x")plt.ylabel("y")plt.title("linear and non-linear")plt.legend([p1,p2,p3], ["x","x*x","x*x*x"])plt.show()上述代碼完成了如下任務(wù):
1 定義[0,1]之間的等距的10個(gè)點(diǎn)
2使用w=1.1, b=0.2, 計(jì)算一個(gè)線性回歸數(shù)值y
3 使用w=1.1, w2=-0.5, b=0.2, 計(jì)算一個(gè)二項(xiàng)式回歸數(shù)值y2
4 使用w=1.1, w2=-0.5, w3=2.3, b=0.2, 計(jì)算一個(gè)三項(xiàng)式回歸數(shù)值y3
5 繪制出三條曲線
可以清楚地看到:
藍(lán)色直線是線性回歸數(shù)值序列
紅色曲線是二項(xiàng)式回歸數(shù)值序列
綠色曲線是三項(xiàng)式回歸數(shù)值序列
也就是說(shuō),我們只使用了同一個(gè)x序列的原始值,卻可以得到三種不同數(shù)值序列,這就是多項(xiàng)式擬合的原理。
當(dāng)多項(xiàng)式次數(shù)很高,原始數(shù)值訓(xùn)練足夠?qū)挼臅r(shí)候,甚至可以擬合出非單調(diào)的曲線,有興趣的讀者可以自己做個(gè)試驗(yàn)。
2 神經(jīng)網(wǎng)絡(luò)的非線性擬合工作原理
我們以正弦曲線的例子來(lái)講解神經(jīng)網(wǎng)絡(luò)非線性回歸的工作過(guò)程和原理。
比較一下下圖,左側(cè)為單特征多項(xiàng)式擬合的示意圖,右側(cè)為雙層神經(jīng)網(wǎng)絡(luò)的示意圖:
左側(cè)圖中,通過(guò)人為的方式,給Z的輸入增加了x2和x3項(xiàng)。
右圖中,通過(guò)線性變換的方式,把x變成了兩部分:z11/a11,z12/a12,然后再通過(guò)一次線性變換把兩者組合成為Z,這種方式和多項(xiàng)式回歸非常類似:
1 隱層把x拆成不同的特征,根據(jù)問題復(fù)雜度覺得神經(jīng)元數(shù)量;
2 隱層做一次激活函數(shù)的非線性變換;
3 輸出層使用多變量線性回歸,把隱層的輸出當(dāng)作輸入特征值,再做一次線性變換,得出擬合結(jié)果。
與多項(xiàng)式回歸不同的是,不需要指定變換參數(shù),而是從訓(xùn)練中學(xué)習(xí)到參數(shù),這樣的話權(quán)重值不會(huì)大得離譜。
第一步 把X拆成兩個(gè)線性序列z1和z2
原始值x有21個(gè)點(diǎn):
通過(guò)以下線性變換,被分成了兩個(gè)線性序列:
其中:
三個(gè)線性序列如下圖所示:
這個(gè)運(yùn)算相當(dāng)于把特征值分解成兩個(gè)部分,不太容易理解。打個(gè)不太恰當(dāng)?shù)谋扔?#xff0c;有一個(gè)浮點(diǎn)數(shù)12.34,你可以把它拆成12和0.34兩個(gè)部分,然后去分別做一些運(yùn)算。另外一個(gè)例子就是,一張彩色圖片上的黃色,我們普通人看到的就是黃色,但是畫家會(huì)想到是紅色和綠色的組合。
第二步 計(jì)算z1的激活函數(shù)值a1
z1還是一條直線,但是經(jīng)過(guò)激活函數(shù)后的a1已經(jīng)不是一條直線了。上面這張圖由于z1的跨度大,所以a1的曲線程度不容易看出來(lái)。
第三步 計(jì)算z2的激活函數(shù)值a2
z2還是一條直線,但是經(jīng)過(guò)激活函數(shù)后的a2已經(jīng)明顯看出是一條曲線了。
第四步 計(jì)算Z值
也就是說(shuō),相同x值的紅點(diǎn)a1和綠點(diǎn)a2,經(jīng)過(guò)公式6計(jì)算后得到藍(lán)點(diǎn)z,所有這樣經(jīng)過(guò)計(jì)算出的藍(lán)點(diǎn)就擬合出一條正弦曲線。
3 比較多項(xiàng)式回歸和雙層神經(jīng)網(wǎng)絡(luò)解法
import numpy as np import matplotlib.pyplot as pltfrom HelperClass2.NeuralNet_2_0 import *train_data_name = "ch08.train.npz" test_data_name = "ch08.test.npz"def ShowResult2D(net, title):count = 21TX = np.linspace(0, 1, count).reshape(count, 1)TY = net.inference(TX)print("TX=", TX)print("Z1=", net.Z1)print("A1=", net.A1)print("Z=", net.Z2)fig = plt.figure(figsize=(6, 6))p1, = plt.plot(TX, np.zeros((count, 1)), '.', c='black')p2, = plt.plot(TX, net.Z1[:, 0], '.', c='r')p3, = plt.plot(TX, net.Z1[:, 1], '.', c='g')plt.legend([p1, p2, p3], ["x", "z1", "z2"])plt.grid()plt.show()fig = plt.figure(figsize=(6, 6))p1, = plt.plot(TX, np.zeros((count, 1)), '.', c='black')p2, = plt.plot(TX, net.Z1[:, 0], '.', c='r')p3, = plt.plot(TX, net.A1[:, 0], 'x', c='r')plt.legend([p1, p2, p3], ["x", "z1", "a1"])plt.grid()plt.show()fig = plt.figure(figsize=(6, 6))p1, = plt.plot(TX, np.zeros((count, 1)), '.', c='black')p2, = plt.plot(TX, net.Z1[:, 1], '.', c='g')p3, = plt.plot(TX, net.A1[:, 1], 'x', c='g')plt.legend([p1, p2, p3], ["x", "z2", "a2"])plt.show()fig = plt.figure(figsize=(6, 6))p1, = plt.plot(TX, net.A1[:, 0], '.', c='r')p2, = plt.plot(TX, net.A1[:, 1], '.', c='g')p3, = plt.plot(TX, net.Z2[:, 0], 'x', c='blue')plt.legend([p1, p2, p3], ["a1", "a2", "z"])plt.show()if __name__ == '__main__':dataReader = DataReader_2_0(train_data_name, test_data_name)dataReader.ReadData()dataReader.GenerateValidationSet()n_input, n_hidden, n_output = 1, 2, 1eta, batch_size, max_epoch = 0.05, 10, 5000eps = 0.001hp = HyperParameters_2_0(n_input, n_hidden, n_output, eta, max_epoch, batch_size, eps, NetType.Fitting,InitialMethod.Xavier)net = NeuralNet_2_0(hp, "sin_121")net.LoadResult()print(net.wb1.W)print(net.wb1.B)print(net.wb2.W)print(net.wb2.B)# net.train(dataReader, 50, True)# net.ShowTrainingHistory_2_0()# ShowResult(net, dataReader, hp.toString())ShowResult2D(net, hp.toString())超參數(shù)優(yōu)化的初步認(rèn)識(shí)
超參數(shù)優(yōu)化(Hyperparameter Optimization)主要存在兩方面的困難:
1 超參數(shù)優(yōu)化是一個(gè)組合優(yōu)化問題,無(wú)法像一般參數(shù)那樣通過(guò)梯度下降方法來(lái)優(yōu)化,也沒有一種通用有效的優(yōu)化方法。
2 評(píng)估一組超參數(shù)配置(Con?guration)的時(shí)間代價(jià)非常高,從而導(dǎo)致一些優(yōu)化方法(比如演化算法)在超參數(shù)優(yōu)化中難以應(yīng)用。
對(duì)于超參數(shù)的設(shè)置,比較簡(jiǎn)單的方法有人工搜索、網(wǎng)格搜索和隨機(jī)搜索。
1 可調(diào)的參數(shù)
我們使用如下參數(shù)做第一次的訓(xùn)練:
上述表格中的參數(shù),最終可以調(diào)節(jié)的其實(shí)只有三個(gè):
1 隱層神經(jīng)元數(shù)
2 學(xué)習(xí)率
3 批樣本量
另外還有一個(gè)權(quán)重矩陣初始化方法需要特別注意,我們?cè)谙乱粋€(gè)段落中講解。
另外兩個(gè)要提一下的參數(shù),第一個(gè)是最大epoch數(shù),根據(jù)不同的模型和案例會(huì)有所不同,在本例中10000次足以承載所有的超參組合了;另外一個(gè)是損失門限值是一個(gè)后驗(yàn)數(shù)值,也就是說(shuō)筆者通過(guò)試驗(yàn),事先知道了當(dāng)eps=0.001時(shí),會(huì)訓(xùn)練出精度可接受的模型來(lái),這個(gè)問題在實(shí)踐中也只能摸著石頭過(guò)河,因?yàn)樵谟?xùn)練一個(gè)特定模型之前,誰(shuí)也不能假設(shè)它能到達(dá)的精度值是多少,損失函數(shù)值的下限也是通過(guò)多次試驗(yàn),通過(guò)歷史記錄的趨勢(shì)來(lái)估算出來(lái)的。
如果讀者不了解神經(jīng)網(wǎng)絡(luò)中的基本原理,那么所謂“調(diào)參”就是碰運(yùn)氣了。今天咱們可以試著改變幾個(gè)參數(shù),來(lái)看看訓(xùn)練結(jié)果,以此來(lái)增加對(duì)神經(jīng)網(wǎng)絡(luò)中各種參數(shù)的了解。
避免權(quán)重矩陣初始化的影響
權(quán)重矩陣中的參數(shù),是神經(jīng)網(wǎng)絡(luò)要學(xué)習(xí)的參數(shù),所以不能稱作超參數(shù)。
權(quán)重矩陣初始化是神經(jīng)網(wǎng)絡(luò)訓(xùn)練非常重要的環(huán)節(jié)之一,不同的初始化方法,甚至是相同的方法但不同的隨機(jī)值,都會(huì)給結(jié)果帶來(lái)或多或少的影響。
在后面的幾組比較中,都是用Xavier方法初始化的。在兩次參數(shù)完全相同的試驗(yàn)中,即使兩次都使用Xavier初始化,因?yàn)闄?quán)重矩陣參數(shù)的差異,也會(huì)得到不同的結(jié)果。為了避免這個(gè)隨機(jī)性,我們?cè)诖aWeightsBias.py中使用了一個(gè)小技巧,調(diào)用下面這個(gè)函數(shù):
class WeightsBias(object):def InitializeWeights(self, folder, create_new):self.folder = folderif create_new:self.__CreateNew()else:self.__LoadExistingParameters()# end ifdef __CreateNew(self):self.W, self.B = WeightsBias.InitialParameters(self.num_input, self.num_output, self.init_method)self.__SaveInitialValue()def __LoadExistingParameters(self):file_name = str.format("{0}\\{1}.npz", self.folder, self.initial_value_filename)w_file = Path(file_name)if w_file.exists():self.__LoadInitialValue()else:self.__CreateNew()第一次調(diào)用InitializeWeights()時(shí),會(huì)得到一個(gè)隨機(jī)初始化矩陣。以后再次調(diào)用時(shí),如果設(shè)置create_new=False,只要隱層神經(jīng)元數(shù)量不變并且初始化方法不變,就會(huì)用第一次的初始化結(jié)果,否則后面的各種參數(shù)調(diào)整的結(jié)果就沒有可比性了。
2 手動(dòng)調(diào)整參數(shù)
手動(dòng)調(diào)整超參數(shù),我們必須了解超參數(shù)、訓(xùn)練誤差、泛化誤差和計(jì)算資源(內(nèi)存和運(yùn)行時(shí)間)之間的關(guān)系。
手動(dòng)調(diào)整超參數(shù)的主要目標(biāo)是調(diào)整模型的有效容量以匹配任務(wù)的復(fù)雜性。有效容量受限于3個(gè)因素:
1 模型的表示容量
2 學(xué)習(xí)算法與代價(jià)代價(jià)函數(shù)的匹配程度
3 代價(jià)函數(shù)和訓(xùn)練過(guò)程正則化模型的程度
具有更多網(wǎng)絡(luò)層、每層有更多隱藏單元的模型具有較高的表示能力,能夠表示更復(fù)雜的函數(shù)。
學(xué)習(xí)率是最重要的超參數(shù)。如果你只有時(shí)間調(diào)整一個(gè)超參數(shù),那就調(diào)整學(xué)習(xí)率。
3 網(wǎng)格搜索
當(dāng)有3個(gè)或更少的超參數(shù)時(shí),常見的超參數(shù)搜索方法是網(wǎng)格搜索(grid search)。對(duì)于每個(gè)超參數(shù),選擇一個(gè)較小的有限值集去試驗(yàn)。然后,這些超參數(shù)的笛卡兒乘積(所有的排列組合)得到若干組超參數(shù),網(wǎng)格搜索使用每組超參數(shù)訓(xùn)練模型。挑選驗(yàn)證集誤差最小的超參數(shù)作為最好的超參數(shù)組合。
用學(xué)習(xí)率和隱層神經(jīng)元數(shù)量來(lái)舉例,橫向?yàn)閷W(xué)習(xí)率,取值[0.1,0.3,0.5,0.7];縱向?yàn)殡[層神經(jīng)元數(shù)量,取值[2,4,8,12],在每個(gè)組合上測(cè)試驗(yàn)證集的精度。我們假設(shè)其中最佳的組合精度達(dá)到0.97,學(xué)習(xí)率=0.5,神經(jīng)元數(shù)=8,那么這個(gè)組合就是我們需要的模型超參,可以拿到測(cè)試集上去做最終測(cè)試了。
以下數(shù)據(jù)為假設(shè)值:
針對(duì)我們這個(gè)曲線擬合問題,規(guī)模較小,模型簡(jiǎn)單,所以可以用上表列出的數(shù)據(jù)做搜索。對(duì)于大規(guī)模模型問題,學(xué)習(xí)率的取值集合可以是{0.1, 0.01, 0.001, 0.0001, 0.00001},隱層單元數(shù)集合可以是{50, 100, 200, 500, 1000, 2000},亦即在對(duì)數(shù)尺度上搜索,確定范圍后,可以做進(jìn)一步的小顆粒步長(zhǎng)的搜索。
網(wǎng)格搜索帶來(lái)的一個(gè)明顯問題是,計(jì)算代價(jià)會(huì)隨著超參數(shù)數(shù)量呈指數(shù)級(jí)增長(zhǎng)。如果有m個(gè)超參數(shù),每個(gè)最多取n個(gè)值,那么訓(xùn)練和估計(jì)所需的試驗(yàn)數(shù)將是O(n的m次方)。我們可以并行地進(jìn)行實(shí)驗(yàn),并且并行要求十分寬松(進(jìn)行不同搜索的機(jī)器之間幾乎沒有必要進(jìn)行通信)。令人遺憾的是,由于網(wǎng)格搜索指數(shù)級(jí)增長(zhǎng)計(jì)算代價(jià),即使是并行,我們也無(wú)法提供令人滿意的搜索規(guī)模。
下面我們做一下具體的試驗(yàn)。
學(xué)習(xí)率的調(diào)整
我們固定其它參數(shù),即隱層神經(jīng)元ne=4、batch_size=10不變,改變學(xué)習(xí)率,來(lái)試驗(yàn)網(wǎng)絡(luò)訓(xùn)練情況。為了節(jié)省時(shí)間,不做無(wú)限輪次的訓(xùn)練,而是設(shè)置eps=0.001為最低精度要求,一旦到達(dá),就停止訓(xùn)練。
下面是損失函數(shù)值的曲線對(duì)比:
需要說(shuō)明的是,對(duì)于本例的擬合曲線這個(gè)特定問題,較大的學(xué)習(xí)率可以帶來(lái)很快的收斂速度,但是有兩點(diǎn):
但并不是對(duì)所有問題都這樣,有的問題可能需要0.001或者更小的學(xué)習(xí)率
學(xué)習(xí)率大時(shí),開始時(shí)收斂快,但是到了后來(lái)有可能會(huì)錯(cuò)失最佳解
批大小的調(diào)整
我們固定其它參數(shù),即隱層神經(jīng)元ne=4、eta=0.5不變,調(diào)整批大小,來(lái)試驗(yàn)網(wǎng)絡(luò)訓(xùn)練情況,設(shè)置eps=0.001為精度要求。
下面是損失函數(shù)值的曲線對(duì)比:
合適的批樣本量會(huì)帶來(lái)較快的收斂,前提是我們固定了學(xué)習(xí)率。如果想用較大的批數(shù)據(jù),底層數(shù)據(jù)庫(kù)計(jì)算的速度較快,但是需要同時(shí)調(diào)整學(xué)習(xí)率,才會(huì)相應(yīng)地提高收斂速度。
這個(gè)結(jié)論的前提是我們用了0.5的學(xué)習(xí)率,如果用0.1的話,將會(huì)得到不同結(jié)論。
隱層神經(jīng)元數(shù)量的調(diào)整
我們固定其它參數(shù),即batch_size=10、eta=0.5不變,調(diào)整隱層神經(jīng)元的數(shù)量,來(lái)試驗(yàn)網(wǎng)絡(luò)訓(xùn)練情況,設(shè)置eps=0.001為精度要求。
下面是損失函數(shù)值的曲線對(duì)比:
對(duì)于這個(gè)特定問題,隱層神經(jīng)元個(gè)數(shù)越多,收斂速度越快。但實(shí)際上,這個(gè)比較不準(zhǔn)確,因?yàn)殡[層神經(jīng)元數(shù)量的變化,會(huì)導(dǎo)致權(quán)重矩陣的尺寸變化,因此對(duì)于上述4種試驗(yàn),權(quán)重矩陣的初始值都不一樣,不具有很強(qiáng)的可比性。我們只需要明白神經(jīng)元數(shù)量多可以提高網(wǎng)絡(luò)的學(xué)習(xí)能力這一點(diǎn)就可以了。
4 隨機(jī)搜索
隨機(jī)搜索(Bergstra and Bengio,2012),是一個(gè)替代網(wǎng)格搜索的方法,并且編程簡(jiǎn)單,使用更方便,能更快地收斂到超參數(shù)的良好取值。
隨機(jī)搜索過(guò)程如下:
首先,我們?yōu)槊總€(gè)超參 數(shù)定義一個(gè)邊緣分布,例如,Bernoulli分布或范疇分布(分別對(duì)應(yīng)著二元超參數(shù)或離散超參數(shù)),或者對(duì)數(shù)尺度上的均勻分布(對(duì)應(yīng)著正實(shí) 值超參數(shù))。例如,其中,u(a,b)表示區(qū)間(a,b)上均勻采樣的樣本。類似地,log_number_of_hidden_units可以從 u(log(50),log(2000))上采樣。
與網(wǎng)格搜索不同,我們不需要離散化超參數(shù)的值。這允許我們?cè)谝粋€(gè)更大的集合上進(jìn)行搜索,而不產(chǎn)生額外的計(jì)算代價(jià)。實(shí)際上,當(dāng)有幾個(gè)超參數(shù)對(duì)性能度量沒有顯著影響時(shí),隨機(jī)搜索相比于網(wǎng)格搜索指數(shù)級(jí)地高效。
Bergstra and Bengio(2012)進(jìn)行了詳細(xì)的研究并發(fā)現(xiàn)相比于網(wǎng)格搜索,隨機(jī)搜索能夠更快地減小驗(yàn)證集誤差(就每個(gè)模型運(yùn)行的試驗(yàn)數(shù)而 言)。
與網(wǎng)格搜索一樣,我們通常會(huì)重復(fù)運(yùn)行不同 版本的隨機(jī)搜索,以基于前一次運(yùn)行的結(jié)果改進(jìn)下一次搜索。
隨機(jī)搜索能比網(wǎng)格搜索更快地找到良好超參數(shù)的原因是,沒有浪費(fèi)的實(shí)驗(yàn),不像網(wǎng)格搜索有時(shí)會(huì)對(duì)一個(gè)超參數(shù)的兩個(gè)不同值(給定其他超參 數(shù)值不變)給出相同結(jié)果。在網(wǎng)格搜索中,其他超參數(shù)將在這兩次實(shí)驗(yàn)中擁有相同的值,而在隨機(jī)搜索中,它們通常會(huì)具有不同的值。因此,如果這兩個(gè)值的變化所對(duì)應(yīng)的驗(yàn)證集誤差沒有明顯區(qū)別的話,網(wǎng)格搜索沒有必要重復(fù)兩個(gè)等價(jià)的實(shí)驗(yàn),而隨機(jī)搜索仍然會(huì)對(duì)其他超參數(shù)進(jìn)行兩次獨(dú)立的探索。
貝葉斯優(yōu)化是另外一種比較成熟技術(shù),有興趣的讀者請(qǐng)自行學(xué)習(xí)。
驗(yàn)證與測(cè)試
1 基本概念
訓(xùn)練集 train set
A set of examples used for learning, which is to fit the parameters (i.e., weights) of the classifier.
用于模型訓(xùn)練的數(shù)據(jù)樣本。
驗(yàn)證集 development set or dev set or validation set
A set of examples used to tune the parameters (i.e., architecture, not weights) of a classifier, for example to choose the number of hidden units in a neural network.
是模型訓(xùn)練過(guò)程中單獨(dú)留出的樣本集,它可以用于調(diào)整模型的超參數(shù)和用于對(duì)模型的能力進(jìn)行初步評(píng)估。
在神經(jīng)網(wǎng)絡(luò)中,我們用驗(yàn)證數(shù)據(jù)集:
1 尋找最優(yōu)的網(wǎng)絡(luò)深度
2 或者決定反向傳播算法的停止點(diǎn)
3 或者在神經(jīng)網(wǎng)絡(luò)中選擇隱藏層神經(jīng)元的數(shù)量
4 在普通的機(jī)器學(xué)習(xí)中常用的交叉驗(yàn)證 (Cross Validation) 就是把訓(xùn)練數(shù)據(jù)集本身再細(xì)分成不同的驗(yàn)證數(shù)據(jù)集去訓(xùn)練模型。
測(cè)試集 test set
A set of examples used only to assess the performance (generalization) of a fully specified classifier.
用來(lái)評(píng)估模最終模型的泛化能力。但不能作為調(diào)參、選擇特征等算法相關(guān)的選擇的依據(jù)。
三者之間的關(guān)系如下圖所示:
一個(gè)形象的比喻:
訓(xùn)練集:課本,學(xué)生根據(jù)課本里的內(nèi)容來(lái)掌握知識(shí)。訓(xùn)練集直接參與了模型調(diào)參的過(guò)程,顯然不能用來(lái)反映模型真實(shí)的能力。即不能直接拿課本上的問題來(lái)考試,防止死記硬背課本的學(xué)生擁有最好的成績(jī),即防止過(guò)擬合。
驗(yàn)證集:作業(yè),通過(guò)作業(yè)可以知道不同學(xué)生學(xué)習(xí)情況、進(jìn)步的速度快慢。驗(yàn)證集參與了人工調(diào)參(超參數(shù))的過(guò)程,也不能用來(lái)最終評(píng)判一個(gè)模型(刷題庫(kù)的學(xué)生不能算是學(xué)習(xí)好的學(xué)生)。
測(cè)試集:考試,考的題是平常都沒有見過(guò),考察學(xué)生舉一反三的能力。所以要通過(guò)最終的考試(測(cè)試集)來(lái)考察一個(gè)學(xué)(模)生(型)真正的能力(期末考試)。
但是僅憑一次考試就對(duì)模型的好壞進(jìn)行評(píng)判顯然是不合理的,所以接下來(lái)就要介紹交叉驗(yàn)證法。
2 交叉驗(yàn)證
傳統(tǒng)的機(jī)器學(xué)習(xí)
在傳統(tǒng)的機(jī)器學(xué)習(xí)中,我們經(jīng)常用交叉驗(yàn)證(Cross Validation)的方法,比如把數(shù)據(jù)分成10份,V1-V10,其中V1-V9用來(lái)訓(xùn)練,V10用來(lái)驗(yàn)證。然后用V2-V10做訓(xùn)練,V1做驗(yàn)證…如此我們可以做10次訓(xùn)練和驗(yàn)證,大大增加了模型的可靠性。
這樣的話,驗(yàn)證集也可以做訓(xùn)練,訓(xùn)練集數(shù)據(jù)也可以做驗(yàn)證,當(dāng)樣本很少時(shí),這個(gè)很有用。
神經(jīng)網(wǎng)絡(luò)/深度學(xué)習(xí)
那么深度學(xué)習(xí)中的用法是什么呢?
比如在神經(jīng)網(wǎng)絡(luò)中,訓(xùn)練時(shí)到底迭代多少次停止呢?或者我們?cè)O(shè)置學(xué)習(xí)率為多少何時(shí)呢?或者用幾個(gè)中間層,以及每個(gè)中間層用幾個(gè)神經(jīng)元呢?如何正則化?這些都是超參數(shù)設(shè)置,都可以用驗(yàn)證集來(lái)解決。
在咱們前面的學(xué)習(xí)中,一般使用loss小于門限值做為迭代終止條件,因?yàn)槲覀冾A(yù)先知道了這個(gè)門限值可以滿足訓(xùn)練精度。但對(duì)于實(shí)際應(yīng)用中的問題,沒有先驗(yàn)的門限值可以參考,如何設(shè)定終止條件?此時(shí),我們可以用驗(yàn)證集來(lái)驗(yàn)證一下準(zhǔn)確率,假設(shè)只有90%的的準(zhǔn)確率,那可能確實(shí)是局部最優(yōu)解。這樣我們可以繼續(xù)迭代,尋找全局最優(yōu)解。
舉個(gè)糖炒栗子:一個(gè)BP神經(jīng)網(wǎng)絡(luò),我們無(wú)法確定隱層的神經(jīng)元數(shù)目,因?yàn)闆]有理論支持。此時(shí)可以這樣做:
1 隨機(jī)將訓(xùn)練數(shù)據(jù)分成K等份(通常建議K=10),得到D1,D2,Dk
2 對(duì)于一個(gè)模型M,選擇D9為驗(yàn)證集,其它為訓(xùn)練集,訓(xùn)練若干輪,用D9驗(yàn)證,得到誤差E。再訓(xùn)練,再用D9測(cè)試,如此N次。對(duì)N次的誤差做平均,得到泛化誤差
3 換一個(gè)不同參數(shù)的模型的組合,比如神經(jīng)元數(shù)量,或者網(wǎng)絡(luò)層數(shù),激活函數(shù),用D8去得到泛化誤差
4 …一共驗(yàn)證10組組合
5 最后選擇具有最小泛化誤差的模型結(jié)構(gòu),用所有的 D0…D9 再次訓(xùn)練,成為最終模型,不用再驗(yàn)證
6 用測(cè)試集測(cè)試
3 留出法 Hold out
使用交叉驗(yàn)證的方法雖然比較保險(xiǎn),但是非常耗時(shí),尤其是在大數(shù)據(jù)量時(shí),訓(xùn)練出一個(gè)模型都要很長(zhǎng)時(shí)間,沒有可能去訓(xùn)練出10個(gè)模型再去比較。
在深度學(xué)習(xí)中,有另外一種方法使用驗(yàn)證集,稱為留出法。亦即從訓(xùn)練數(shù)據(jù)中保留出驗(yàn)證樣本集,主要用于解決過(guò)擬合情況,這部分?jǐn)?shù)據(jù)不用于訓(xùn)練。如果訓(xùn)練數(shù)據(jù)的準(zhǔn)確度持續(xù)增長(zhǎng),但是驗(yàn)證數(shù)據(jù)的準(zhǔn)確度保持不變或者反而下降,說(shuō)明神經(jīng)網(wǎng)絡(luò)亦即過(guò)擬合了,此時(shí)需要停止訓(xùn)練,用測(cè)試集做最終測(cè)試。
所以,訓(xùn)練步驟的偽代碼如下:
for each epochshufflefor each iteraion獲得當(dāng)前小批量數(shù)據(jù)前向計(jì)算反向傳播更新梯度if is checkpoint用當(dāng)前小批量數(shù)據(jù)計(jì)算訓(xùn)練集的loss值和accuracy值并記錄計(jì)算驗(yàn)證集的loss值和accuracy值并記錄如果loss值不再下降,停止訓(xùn)練如果accuracy值滿足要求,停止訓(xùn)練end ifend for end for從本章開始,我們將使用新的DataReader類來(lái)管理訓(xùn)練/測(cè)試數(shù)據(jù),與前面的SimpleDataReader類相比,這個(gè)類有以下幾個(gè)不同之處:
要求既有訓(xùn)練集,也有測(cè)試集
提供GenerateValidationSet()方法,可以從訓(xùn)練集中產(chǎn)生驗(yàn)證集
以上兩個(gè)條件保證了我們?cè)谝院蟮挠?xùn)練中,可以使用本節(jié)中所描述的留出法,來(lái)監(jiān)控整個(gè)訓(xùn)練過(guò)程。
關(guān)于三者的比例關(guān)系,在傳統(tǒng)的機(jī)器學(xué)習(xí)中,三者可以是6:2:2。在深度學(xué)習(xí)中,一般要求樣本數(shù)據(jù)量很大,所以可以給訓(xùn)練集更多的數(shù)據(jù),比如8:1:1。
如果有些數(shù)據(jù)集已經(jīng)給了你訓(xùn)練集和測(cè)試集,那就不關(guān)心其比例問題了,只需要從訓(xùn)練集中留出10%左右的驗(yàn)證集就可以了。
4 代碼實(shí)現(xiàn)
定義DataReader類如下:
class DataReader(object):def __init__(self, train_file, test_file):self.train_file_name = train_fileself.test_file_name = test_fileself.num_train = 0 # num of training examplesself.num_test = 0 # num of test examplesself.num_validation = 0 # num of validation examplesself.num_feature = 0 # num of featuresself.num_category = 0 # num of categoriesself.XTrain = None # training feature setself.YTrain = None # training label setself.XTest = None # test feature setself.YTest = None # test label setself.XTrainRaw = None # training feature set before normalizationself.YTrainRaw = None # training label set before normalizationself.XTestRaw = None # test feature set before normalizationself.YTestRaw = None # test label set before normalizationself.XVld = None # validation feature setself.YVld = None # validation lable setX - 樣本特征值數(shù)據(jù)
Y - 樣本標(biāo)簽值數(shù)據(jù)
得到訓(xùn)練集和測(cè)試集
一般的數(shù)據(jù)集都有訓(xùn)練集和測(cè)試集,如果沒有,需要從一個(gè)單一數(shù)據(jù)集中,隨機(jī)抽取出一小部分作為測(cè)試集,剩下的一大部分作為訓(xùn)練集。
讀取數(shù)據(jù)
def ReadData(self):train_file = Path(self.train_file_name)if train_file.exists():data = np.load(self.train_file_name)self.XTrainRaw = data["data"]self.YTrainRaw = data["label"]assert(self.XTrainRaw.shape[0] == self.YTrainRaw.shape[0])self.num_train = self.XTrainRaw.shape[0]self.num_feature = self.XTrainRaw.shape[1]self.num_category = len(np.unique(self.YTrainRaw))# this is for if no normalize requirmentself.XTrain = self.XTrainRawself.YTrain = self.YTrainRawelse:raise Exception("Cannot find train file!!!")#end iftest_file = Path(self.test_file_name)if test_file.exists():data = np.load(self.test_file_name)self.XTestRaw = data["data"]self.YTestRaw = data["label"]assert(self.XTestRaw.shape[0] == self.YTestRaw.shape[0])self.num_test = self.XTestRaw.shape[0]# this is for if no normalize requirmentself.XTest = self.XTestRawself.YTest = self.YTestRawelse:raise Exception("Cannot find test file!!!")#end if在讀入原始數(shù)據(jù)后,數(shù)據(jù)存放在XTrainRaw、YTrainRaw、XTestRaw、YTestRaw中。由于有些數(shù)據(jù)不需要做歸一化處理,所以,在讀入數(shù)據(jù)集后,令:XTrain=XTrainRaw、YTrain=YTrainRaw、XTest=XTestRaw、YTest=YTestRaw,如此一來(lái),就可以直接使用XTrain、YTrain、XTest、YTest做訓(xùn)練和測(cè)試了,避免不做歸一化時(shí)上述4個(gè)變量為空。
特征值歸一化
def NormalizeX(self):
x_merge = np.vstack((self.XTrainRaw, self.XTestRaw))
x_merge_norm = self.__NormalizeX(x_merge)
train_count = self.XTrainRaw.shape[0]
self.XTrain = x_merge_norm[0:train_count,:]
self.XTest = x_merge_norm[train_count:,:]
如果需要?dú)w一化處理,則XTrainRaw -> XTrain、YTrainRaw -> YTrain、XTestRaw -> XTest、YTestRaw -> YTest。注意需要把Train、Test同時(shí)歸一化,如上面代碼中,先把XTrainRaw和XTestRaw合并,一起做歸一化,然后再拆開,這樣可以保證二者的值域相同。
比如,假設(shè)XTrainRaw中的特征值只包含1、2、3三種值,在對(duì)其歸一化時(shí),1、2、3會(huì)變成0、0.5、1;而XTestRaw中的特征值只包含2、3、4三種值,在對(duì)其歸一化時(shí),2、3、4會(huì)變成0、0.5、1。這就造成了0、0.5、1這三個(gè)值的含義在不同數(shù)據(jù)集中不一樣。
把二者merge后,就包含了1、2、3、4四種值,再做歸一化,會(huì)變成0、0.333、0.666、1,在訓(xùn)練和測(cè)試時(shí),就會(huì)使用相同的歸一化值。
標(biāo)簽值歸一化
根據(jù)不同的網(wǎng)絡(luò)類型,標(biāo)簽值的歸一化方法也不一樣。
def NormalizeY(self, nettype, base=0):if nettype == NetType.Fitting:y_merge = np.vstack((self.YTrainRaw, self.YTestRaw))y_merge_norm = self.__NormalizeY(y_merge)train_count = self.YTrainRaw.shape[0]self.YTrain = y_merge_norm[0:train_count,:]self.YTest = y_merge_norm[train_count:,:] elif nettype == NetType.BinaryClassifier:self.YTrain = self.__ToZeroOne(self.YTrainRaw, base)self.YTest = self.__ToZeroOne(self.YTestRaw, base)elif nettype == NetType.MultipleClassifier:self.YTrain = self.__ToOneHot(self.YTrainRaw, base)self.YTest = self.__ToOneHot(self.YTestRaw, base)如果是Fitting任務(wù),即線性回歸、非線性回歸,對(duì)標(biāo)簽值使用普通的歸一化方法,把所有的值映射到[0,1]之間
如果是BinaryClassifier,即二分類任務(wù),把標(biāo)簽值變成0或者1。base參數(shù)是指原始數(shù)據(jù)中負(fù)類的標(biāo)簽值。比如,原始數(shù)據(jù)的兩個(gè)類別標(biāo)簽值是1、2,則base=1,把1、2變成0、1
如果是MultipleClassifier,即多分類任務(wù),把標(biāo)簽值變成One-Hot編碼。
生成驗(yàn)證集
def GenerateValidationSet(self, k = 10):self.num_validation = (int)(self.num_train / k)self.num_train = self.num_train - self.num_validation# validation setself.XVld = self.XTrain[0:self.num_validation]self.YVld = self.YTrain[0:self.num_validation]# train setself.XTrain = self.XTrain[self.num_validation:]self.YTrain = self.YTrain[self.num_validation:]驗(yàn)證集是從歸一化好的訓(xùn)練集中抽取出來(lái)的。上述代碼假設(shè)XTrain已經(jīng)做過(guò)歸一化,并且樣本是無(wú)序的。如果樣本是有序的,則需要先打亂。
獲得批量樣本
def GetBatchTrainSamples(self, batch_size, iteration):start = iteration * batch_sizeend = start + batch_sizebatch_X = self.XTrain[start:end,:]batch_Y = self.YTrain[start:end,:]return batch_X, batch_Y訓(xùn)練時(shí)一般采樣Mini-batch梯度下降法,所以要指定批大小batch_size和當(dāng)前批次iteration,就可以從已經(jīng)打亂過(guò)的樣本中獲得當(dāng)前批次的數(shù)據(jù),在一個(gè)epoch中根據(jù)iterationd的遞增調(diào)用此函數(shù)。
樣本打亂
def Shuffle(self):seed = np.random.randint(0,100)np.random.seed(seed)XP = np.random.permutation(self.XTrain)np.random.seed(seed)YP = np.random.permutation(self.YTrain)self.XTrain = XPself.YTrain = YP樣本打亂操作只涉及到訓(xùn)練集,在每個(gè)epoch開始時(shí)調(diào)用此方法。打亂時(shí),要注意特征值X和標(biāo)簽值Y是分開存放的,所以要使用相同的seed來(lái)打亂,保證打亂順序后的特征值和標(biāo)簽值還是一一對(duì)應(yīng)的。
總結(jié)
以上是生活随笔為你收集整理的神经网络基本原理简明教程之非线性回归的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 构造函数、析构函数
- 下一篇: 最全的jquery datatables