一、簡介
機器學習分為很多個領域,其中的連接主義指的就是以神經元(neuron)為基本結構的各式各樣的神經網絡,規范的定義是:由具有適應性的簡單單元組成的廣泛並行互連的網絡,它的組織能夠模擬生物神經系統對真實世界的刺激作出的交互反應。而我們在機器學習中廣泛提及的神經網絡學習就是機器學習與神經網絡的交叉部分,本篇就將介紹基本的神經元模型、感知機模型的知識以及更進一步的多層感知機的具體應用(注意,本篇介紹的內容只是當下流行的深度學習的鋪墊,因此只使用了無GPU加速的相應模塊,關於深度學習的知識、當下流行的深度學習方法及相應的可GPU加速的訓練方法將在后續的博文中陸續介紹)
二、從神經元模型到多層前饋網絡
2.1 知識鋪墊
在介紹神經網絡學習中的神經元模型之前我們先類比一下生物神經元,神經元是基本的信息處理單元,生物神經元主要由樹突、軸突和突觸組成,結構簡圖如下:
通過觀察它的結構可以總結出以下特點:
1、樹突由細胞體向外伸出,且有不規則的表面和許多較短的分支,它的角色是信號的輸入端,用於接收神經沖動;
2、軸突指由細胞體向外伸出的最長的一條分支,即神經纖維,相當於信號的輸出端,用於傳出神經沖動;
3、神經元之間通過軸突(輸出)+樹突(輸入)的形式進行互連,且對於單個神經元,在輸入——>輸出這個方向上不可逆;
而關於神經元的功能,也總結出以下幾點:
1、可塑性:神經元存在着這樣一種機制:通過新突觸的產生和對現有神經突觸的調整,使得神經網絡能夠適應環境(正如當下人們對記憶是存在與神經元間的連接之中的假設);
2、時空整合:傳入某一個中間神經元的刺激,是來自前面所有相關神經元的反復調整過的累積結果;
3、興奮與抑制狀態:當傳入沖動的累計刺激結果使得細胞膜電位上升並成功超過了閾值,則細胞會進入興奮狀態,產生一次傳出刺激上的階躍;若傳入的累計刺激結果低於發生階躍的閾值,則無后續神經活動產生,細胞隨之進入抑制狀態;
4、突觸的延時和不應期:突觸對神經活動的傳遞具有延時和不應期性,在相鄰兩次沖動之間存在時間間隔,且在時間間隔內神經元處於休息狀態,不會產生自發性的神經沖動;
5、學習、遺忘和疲勞:突觸的傳遞作用有學習、遺忘和疲勞過程
通過以上說明,我們可以驚喜的發現生物神經元的這些結構特性使得它與我們在機器學習中希望達到的目的完美契合——即通過一系列規則的的刺激傳遞過程,最終達到正確的決策結果輸出,早在上個世紀四五十年代,人們就發現了這種奇妙的聯系,並隨之構造出基本的神經元模型;
2.2 神經元模型
2.2.1 基本結構
一個典型的M-P人工神經元模型結構如下:
其中xj表示來自第j條“樹突”的輸入值,wji表示連接權(每條固定的輸入上只有一個唯一的權),ui表示在該神經元i上,所有輸入信號的線性組合,系數即為對應的權值,即
θi為該神經元i的閾值,通過ui與θi的簡單相加,即可得到中間值vi:
而f()表示激活函數,yi表示該神經元i的輸出,即:
2.2.2 激活函數
理想的激活函數是階躍函數,如下圖,它將連續域上的輸入值映射為{0,1},1對應神經元興奮 ,0對應神經元抑制;
事實上,類似邏輯回歸中的logit函數,我們需要用一些數學性質良好的函數來替代數學性質較差(不處處連續)的階躍函數,常用的是sigmoid型函數,如下:
2.3 感知機與多層網絡
2.3.1 單層感知機
感知機(perceptron)由兩層神經元組成,如下圖所示,輸入層接受外界輸入信號后傳遞給輸出層,輸出層是M-P神經元,亦稱作“閾值邏輯單元”(threshold logic unit);
感知機的學習過程就是對於給定的訓練集,在每一輪迭代中同時調整各權重wi(i=1,2,...,n),以及閾值θ,直到滿足預設精度為止,為了方便訓練,閾值θ可看作第n+1個輸入為1的結點的對應權重wn+1,亦稱為啞結點(dummy node),這樣權重和閾值的學習變得到了統一,例如下圖這種表示形式,閾值θ就被視為一個特別的輸入:
而單個感知機的學習規則也十分簡單,對訓練數據集(x,y),若當前感知機的輸出為y*,則感知機各權重調整規則如下:
只有在y*=y或訓練輪數達到預設的上限或精度第一次達到或超過設定的閾值時,該學習過程才會停止,否則都將進行一輪又一輪的權重調整;
在整個感知機的結構中,只有輸出層神經元包含激活函數的計算過程,輸入層只管輸入值*權重,即只擁有一層功能神經元(functional neuron),學習能力非常有限,只能處理線性可分問題,否則感知機的學習過程將會發生震盪,w難以穩定下來,即學習失效,例如對於常見的異或問題,感知機就無法習得其規則:
也正是這個原因,對於感知機學習的熱度在上世紀60年代跌入谷底,但隨着研究的深入,很多用於改造感知機的方法被提出,下面舉幾個例子:
1、對神經元施加非線性輸入函數
類似部分非線性回歸中我們常使用的線性化方法,對於單純線性輸入的感知機輸入層,我們可以根據對具體問題的理解,在個別輸入神經元上進行非線性改造,如下例:
2、使用AND邏輯器構成割平面式的非線性化
普通感知機的分割平面是線性的,我們思考一下,有哪種機器學習算法本質也是線性分割呢?沒錯,如果你看過我之前關於決策樹的博文,一定還記得,決策樹的分割平面式由很多段與各坐標軸平行的拼接而成的,那么我們可以將這種思想類似地遷移到感知機的改造中,用多條線性分割來圍成近似的非線性分割:
2.3.2 多層感知機與神經網絡
之前介紹的幾種改造線性感知機的方法,實際實施起來存在着很多局限,比如施加輸入層非線性函數就存在着很多不確定的部分,要想更加通用地解決非線性可分問題,需要考慮使用多層功能神經元,例如下圖所示的這個簡單的兩層感知機就可以解決異或問題,其中輸入層與輸出層間的若干層神經元被稱為隱層或隱含層(hidden layer),隱層和輸出層都是含有激活函數部分的功能神經元:
而更一般的多層感知機,如下圖所示具有規則的層級結構,這時它已經可以稱作是神經網絡了,每層神經元與下一層神經元全互連,且同層神經元之間不存在連接,也不存在跨層連接,這樣的神經網絡結構通常被乘稱作“多層前饋神經網絡”(multi-layer feedforward neural networks),其中輸入層神經元依舊單純地接收外界輸入,隱層與輸出層對信號進行處理,最終結果依舊是由輸出層神經元處理並輸出,所以我們平時對一個多層前饋神經網絡的層數的稱呼都來源於其隱含層的層數,例如下圖a就是單隱含層前饋網絡,b就是雙隱含層前饋網絡:
2.3.3 訓練方法
多層感知機的學習能力比單個感知機強得多,但隨着其結構的復雜化,對應的訓練方法也不同於前面簡單感知機的簡單規則,最常使用的方法是誤差逆傳播(error BackPropgation,即常用的BP算法);
算法過程:
對一個給定的訓練集D={(x1,y1),(x2,y2)...(xm,ym)},其中xi為d維向量,yi為l維向量,即自變量由d個維度組成,輸出值為l個維度,對應的,構造一個由d個輸入層神經元、q個隱含層神經元(隱含層神經元個數沒有硬性要求)以及l個輸出層神經元組成的單隱層前饋神經網絡,其中輸出層第j個神經元的閾值用θj表示,隱層第h個神經元的閾值用γh表示,輸入層第i個神經元與隱層第h個神經元之間的連接權為vih,隱層第h個神經元與輸出層第j個神經元之間的連接權為whj,記隱層第h個神經元接收到的輸入為
輸出層第j個神經元接收到的輸入為
結構如下圖:
且假設隱層和輸出層每個功能神經元都使用Sigmoid型函數:
對於訓練集中的任意(xk,yk),假定神經網絡的輸出為
即
則該網絡在(xk,yk)上的均方誤差為:
而整個網絡中需要確定的參數共有
需要確定,而BP是一種迭代學習算法,在迭代的每一輪采用廣義的感知機學習規則對參數進行更新估計,即其任意參數v的更新估計式為:
以隱層到輸出層的連接權whj為例來進行推導:
首先我們先確定一個事實,BP算法基於梯度下降(gradient descent)策略,以目標的負梯度方向對參數進行調整,所以對均方誤差項
給定學習率η,有
注意到whj先影響到第j個輸出層神經元的輸入值βj,再影響其輸出值,最后影響到Ek,有
又因為
且Sigmoid型函數有一個很好的性質:
於是綜上,有
所有最終得到whj的更新公式:
類似的,其他參數的更新公式:
其中eh為:
學習率η控制着算法每一輪迭代中的更新步長,太大容易震盪(接近理想解時卻跨過),太小則收斂速度又會過慢,有時為了做精細調節,可以更加靈活的設置學習率而不必一直固定不變;需要注意的是,標准BP算法在隨機初始化各參數(一般是初始化一個較小的非0陣)后,經過一輪一輪地迭代,每一輪都只輸入一個樣本值來調整各參數,訓練目的是逐漸縮小訓練集D上的累積誤差:
而上面推導的規則是基於每次一個樣本輸入的調整,即標准BP算法,特點是參數更新的非常頻繁,並且前后的不同樣本可能會導致訓練效果的前后抵消,所以為了達到目標累積誤差極小點,需要進行很多次的迭代,但優點是下降和計算都較快,特別是當訓練集D非常大時,因此其被使用的最多;
一個重要的論點:
只要一個隱層包含足夠多神經元,多層前饋網絡就可以以任意精度逼近任意復雜度的連續函數。但是在實際任務多層前饋網絡的構造中,選擇單隱層 還是雙隱層,每一層隱層選取幾個神經元,這都尚無可靠的理論支撐,存在着大量試錯(trial-by-error)的成分,對神經網絡最佳超參數的搜索方法的研究也是一個相當活躍的領域;
也正是因為其強大的表示能力,多層前饋網絡很容易過擬合,即其訓練集上誤差持續下降,而驗證集上誤差卻可能上升,目前主要有兩種緩解多層前饋網絡過擬合的方法:
1、早停(early stopping)
通過將數據集分成訓練集和驗證集,訓練集用來計算梯度、更新連接權和閾值,驗證集用來估計誤差,若訓練集誤差降低但驗證集上誤差升高,則停止訓練,同時返回具有當前最小驗證集誤差的連接權與閾值(基於貪心算法的原則)
2、正則化(regularization)
正則化是指在誤差目標函數中增加一個用於描述網絡復雜度的部分,常用的是連接權與閾值的平方和,令Ek表示第k個訓練樣本上的誤差,wi表示連接權和閾值,則誤差目標函數變為:
其中λ屬於(0,1),表示在經驗誤差和網絡復雜度之間進行權衡,具體取值常通過交叉驗證來進行搜索確定;
3、懲罰項
對目標函數附加懲罰項以強制無用的權值趨於0
局部極小情況:
由於不能保證目標函數在權空間中的正定性,而誤差曲面往往復雜且無規則,存在着多個分布無規則的局部極小點,因此基於梯度下降的BP算法很容易陷入局部極小,導致訓練效果不好,而常用的改進措施有:
1、引入全局優化技術
包括同時訓練多個神經網絡模型,然后按照在驗證集上的表現,選擇其中驗證誤差最小的作為全局最小的近似值;使用諸如隨機梯度下降、模擬退火、遺傳算法、蟻群算法等啟發式的算法來尋找最大可能接近全局最小值的局部最小值;
2、平坦化優化曲面以消除局部極小
3、設計合適的網絡結構使得其不會產生局部極小
當然,后面兩種方案實施起來比較復雜,因此實際任務中常使用的第一種策略。
三、Python實現
本文暫時不介紹帶有GPU加速的神經網絡訓練方法,因此不適用於過大規模的樣本集和過多超參數的神經網絡;
我們利用sklearn.neural_network中集成的方法來進行多層前饋神經網絡的訓練,下面分別針對分類問題和回歸問題進行展開:
3.1 分類問題
我們使用sklearn.neural_network中的MLPClassifier()來實現BP算法訓練的多層感知機,其主要參數如下:
hidden_layer_size:tuple型輸入,形如(m,n),其中m用來控制隱層神經元的個數,n用來控制隱層的層數,也可以理解為網絡總層數-2(即減去輸入層與輸出層),默認值為(100,),即單隱層,隱層中含有100個神經元
activation:字符型,用於控制激活函數的類型,可選類型有'identity',表示無非線性激活函數,用於訓練線性模型時;'logistic',logit函數;'tanh',雙曲函數;'relu',f(x)=max(0,x)
solver:字符型,用來控制BP算法中使用到的求解器,'lbfgs',表示准牛頓法;'sgd',表示標准的隨機梯度下降法;'adam',另一種類型的基於隨機梯度下降的方法。默認為'adam'
alpha:懲罰項系數,默認為0.0001
batch_size:當solver設置為隨機梯度相關的求解器時,此參數控制隨機優化器的小批量尺寸
learning_rate:字符型,控制學習率,當設置為'constant'時,訓練過程中的學習率為另一參數learning_rate_init預設的常數;當設置為'invscaling',會逐步的降低學習率以減小在最優值附近震盪的風險;當設置為'adaptive'時,且early_stopping被設置為開啟時,如果連續兩次訓練集上的累積誤差沒有下降或交叉驗證得分無法得到提升時,學習率會變為原來的1/5,只有當solver設置為'sgd'時才生效
learning_rate_init:雙精度浮點值,控制初始的學習率,配合learning_rate的設置值,共同控制學習率的變化情況,只有當solver設置為'sgd'或'adam'時才生效(即基於梯度)
max_iter:整型,控制訓練的最大迭代輪數,默認值是200輪,當然,該方法也是只有solver設置為'sgd'或'adam'時才會生效
shuffle:bool型,控制是否在每一輪迭代中打亂樣本的順序,默認值是True,只有solver設置為'sgd'或'adam'時才會生效
random_state:整型,控制隨機數種子,即控制訓練開始時隨時初始化權重的過程,默認不設置
tol:設定精度的閾值,默認是1e-4,設置的越小,理論上訓練精度越高,同時迭代次數也相應越多
verbose:bool型,控制是否打印整個訓練過程的細節
warm_start:bool型,控制是否進行預訓練,即通過預訓練得到一個較好的初始權重,再進行正式訓練,默認為False
momentum:float型,設置梯度下降中的動量因子,默認為0.9,只有當solver設置為'sgd'時才會生效
early_stopping:bool型,是否在驗證分數不再得到提高且未達到設置的最大迭代輪數之前”早停“訓練,默認為False,只有solver設置為'sgd'或'adam'時才會生效
validation_fraction:float型,控制驗證集的比例,默認為0.1,且只有在early_stopping設置為True時才會生效
函數返回項:
classes_:數組或列表形式,顯示每一個輸出對應的類別標簽
loss_:float型,輸出當前達到的最小累積誤差水平
coefs_:列表,返回訓練好的網絡中的權重矩陣
n_iter_:整型,返回求解器迭代的總次數
n_layers_:整型,返回當前網絡層數
n_outputs_:整型,返回當前網絡輸出個數
out_activation_:字符型,輸出當前網絡中的激活函數
下面我們以威斯康辛州乳腺癌數據為例,下面是基本功能的演示:
from sklearn.neural_network import MLPClassifier from sklearn import datasets from sklearn.metrics import confusion_matrix from sklearn.metrics import accuracy_score '''加載數據集''' X,y = datasets.load_breast_cancer(return_X_y=True) '''初始化多層感知機分類器,其中激活函數選擇logit函數,求解器選擇adam版隨機梯度下降法,最小精度設置為1e-5,並設置早停處理,驗證集比例為0.3''' clf = MLPClassifier(activation='logistic', solver='adam', max_iter=1000, tol=1e-5, validation_fraction=0.3) '''利用訓練集訓練網絡''' clf = clf.fit(X,y) '''打印驗證集上的混淆矩陣''' print(confusion_matrix(y,clf.predict(X))) '''打印驗證集上的正確率''' print(accuracy_score(y,clf.predict(X)))
運行結果:
下面我們來看看這個單隱層(100X1)的網絡的各項返回值:
'''顯示網絡中的訓練目標標簽''' print('訓練目標標簽',clf.classes_) '''打印輸入層與隱層之間所有連接上的權重矩陣的形狀,即共30X100個權重''' print('輸入層與隱層之間所有連接上的權重矩陣形狀:',clf.coefs_[0].shape) '''打印隱層與輸出層之間的權重矩陣形狀,即100X1個權重''' print('隱層與輸出層之間的權重矩陣形狀:',clf.coefs_[1].shape) '''打印隱層上所有神經元內部的閾值向量的形狀''' print('隱層上所有神經元內部的閾值向量的形狀:',clf.intercepts_[0].shape) '''打印輸出層神經元(本例中為單個輸出)內部的閾值''' print('輸出層神經元(本例中為單個輸出)內部的閾值:',clf.intercepts_[1]) '''打印總訓練輪數''' print('總訓練輪數',clf.n_iter_)
運行結果:
3.2 回歸問題
講完了上面的分類問題,下面我們來介紹一下多層感知機在擬合函數曲線上的應用,我們使用sklearn.neural_network.MLPRegressor()來完成回歸任務,其主要參數與MLPClassifier相同,這里便不再做介紹,下面我們通過構造一個已知函數解析式的較復雜的非線性函數並生成對應的帶有隨機誤差項修正的數據,進行多層感知機回歸任務的演示:
from sklearn.neural_network import MLPRegressor import matplotlib.pyplot as plt import numpy as np import math '''生成仿真一維數據集''' X = np.arange(-5,5,0.05).reshape((-1,1)) '''因變量計算函數,添加了隨即誤差項''' def My_f(x): y = x**2-x**3+x+math.exp(x)+np.random.randn() return y '''根據公式生成因變量仿真值''' y = np.array([My_f(X[i]) for i in range(len(X))]).reshape((-1,1)) plt.figure(figsize=(14,9)) iter = 1000 for i in range(1,10): plt.subplot(330+i) plt.scatter(X,y,s=5) rg = MLPRegressor(activation='relu',max_iter=iter) rg = rg.fit(X,y) y_pre = rg.predict(X) plt.plot(X,y_pre,c='red') plt.title('iterations:'+str(iter)) iter += 1000
運行結果:
可以看出,隨着迭代次數的增多,擬合曲線(紅線)越來越逼近與真實情況,為了檢驗是否存在過擬合現象,我們擴大定義域的范圍,並在其上沿用前面的函數解析式創造虛假數據集,並利用9000次迭代后的網絡來進行預測:
X = np.arange(-9,9,0.05).reshape((-1,1)) y = np.array([My_f(X[i]) for i in range(len(X))]).reshape((-1,1)) y_pre = rg.predict(X) plt.scatter(X,y,s=3) plt.plot(X,y_pre,c='red')
可以看出,隨着定義域的擴大化,我們曾經表現優異的網絡也發生大幅度的偏差,即我們常說的過擬合現象,所以實際中使用多層感知機進行回歸需要在減少過擬合上有更多考慮。
四、R實現
為了和前面Python保持一致(即不支持GPU加速的神經網絡訓練方法),我選擇R中的nnet包進行演示,nnet中的nnet()可以創建單隱層前饋神經網絡,其主要參數如下:
formula:R中常規的因變量~自變量的公式格式
weights:為每一個樣本賦權,若無輸入則默認每個樣本權重都為1
size:因為nnet只能創建單隱層前饋網絡,因此size傳入的正整數代表了隱層的神經元個數
data:指定樣本所在的數據框
Wts:初始化的權重向量,若此項無輸入則隨機初始化權重向量
rang:控制初始化權重的范圍,為[-rang,rang]
maxit:最大迭代輪數,默認為100
abstol:設置精度閾值
library(nnet) data(iris) data <- iris #訓練我們的單隱層前饋神經網絡,其中隱層神經元個數為2 clf <- nnet(Species~., data=data, size=2, maxit=200) pre <- predict(clf,iris[,-5]) pre <- apply(pre,1,which.max) #打印混淆矩陣 table(pre,data$Species) #打印正確率 cat('正確率:',sum(prop.table(table(pre,data$Species))))
運行結果:
以上就是關於多層感知機(多層前饋神經網絡)的基本知識,關於當下主流的帶GPU加速的神經網絡框架,將會在接下來的內容中介紹。