感知器算法是一種可以直接得到線性判別函數的線性分類方法,由於它是基於樣本線性可分的要求下使用的,所以先來了解下什么是線性可分?
- 線性可分與線性不可分
假設有一個包含 個樣本的樣本集合
, 其中
. 我們想要找到一個線性判別函數
將兩類樣本分開,其中 ,如圖1所示:
圖 1
為了討論方便,我們將樣本 增加了一維常數,得到增廣樣本向量
,則
個樣本的集合表示為
,增廣權矢量表示為
,我們得到新的判別函數
對應的決策規則就變為: ,則決策為第一類;
決策為第二類.
那什么是線性可分?什么是線性不可分?
假設有一組樣本 ,如果存在這樣一個權矢量
,使得任何一個樣本滿足“屬於第一類,
;屬於第二類,
”這樣一個條件,那么我們就說這一組樣本是線性可分的,即在樣本特征空間中,至少會存在一個決策面可以將兩類樣本正確無誤的分開,反之找不到一個決策面來區分的話,就說樣本是線性不可分的。
對於感知器,我們要求樣本必須是線性可分的,因為它作為一種最最簡單的學習機器,目前還無法很好的解決線性不可分的情況(當然,有了解決不可分的算法),即便是不可分的情況,人們也更加傾向於使用其他算法,這也是感知器無法應用到更多實踐場合的原因。
2. 感知器算法
感知器算法采用最直觀的准則,即最小錯分樣本數。將錯分樣本到判別界面距離之和作為准則,稱為感知器准則,表示如下:
為了求解感知器的准則函數,就是找到一個權矢量,使得懲罰函數最小化。我們使用機器學習中常用的梯度下降方法來迭代。懲罰函數對權矢量的梯度公式為:
利用梯度下降,我們有:
其中 是學習率(或步長)。
我們得到下列感知器算法(批量調整版本):
當然,我們都知道這種對所有錯分樣本放到一起進行修正的做法是不妥當的,更妥當的辦法是對每一個錯分樣本都單獨修正,且每次都使用同一個固定步長,假如設步長為1,得到單樣本調整版本的感知器算法:
感知器算法特點:
- 當樣本線性可分情況下,學習率
合適時,算法具有收斂性
- 收斂速度較慢
- 當樣本線性不可分情況下,算法不收斂,且無法判斷樣本是否線性可分
感知器算法的原理:
感知器作為人工神經網絡中最基本的單元,有多個輸入和一個輸出組成。雖然我們的目的是學習很多神經單元互連的網絡,但是我們還是需要先對單個的神經單元進行研究。
感知器算法的主要流程:
首先得到n個輸入,再將每個輸入值加權,然后判斷感知器輸入的加權和最否達到某一閥值v,若達到,則通過sign函數輸出1,否則輸出-1。
為了統一表達式,我們將上面的閥值v設為-w0,新增變量x0=1,這樣就可以使用w0x0+w1x1+w2x2+…+wnxn>0來代替上面的w1x1+w2x2+…+wnxn>v。於是有:
從上面的公式可知,當權值向量確定時,就可以利用感知器來做分類。
那么我們如何獲得感知器的權值呢?這需要根據訓練集是否可分來采用不同的方法:
1、訓練集線性可分時 --> 感知器訓練法則
為了得到可接受的權值,通常從隨機的權值開始,然后利用訓練集反復訓練權值,最后得到能夠正確分類所有樣例的權向量。
具體算法過程如下:
A)初始化權向量w=(w0,w1,…,wn),將權向量的每個值賦一個隨機值。
B)對於每個訓練樣例,首先計算其預測輸出:
C)當預測值不等於真實值時則利用如下公式修改權向量:
代表學習速率,t代表樣例的目標輸出,o代表感知器輸出。
D)重復B)和C),直到訓練集中沒有被錯分的樣例。
算法分析:
若某個樣例被錯分了,假如目標輸出t為-1,結果感知器o輸出為1,此時為了讓感知器輸出-1,需要將wx減小以輸出-1,而在x的值不變的情況下只能減小w的值,這時通過在原來w后面添加(t-o)x=即可減小w的值(t-o<0, x>0)。
通過逐步調整w的值,最終感知器將會收斂到能夠將所有訓練集正確分類的程度,但前提條件是訓練集線性可分。若訓練集線性不可分,則上述過程不會收斂,將無限循環下去。
感知器學習算法首先需要將權重設置為0或很小的隨機數,然后預測訓練樣本的類型。感知器是一種錯誤驅動(error-driven)的學習算法。如果感知器是正確的,算法就繼續處理下一個樣本。如果感知器是錯誤的,算法就更新權重,重新預測。權重的更新規則如下:
對每個訓練樣本來說,每個解釋變量的參數值增加,是樣本 j 的真實類型,是樣本 j 的預測類型,是第 i 個樣本 j 的解釋變量的值,是控制學習速率的超參數。如果預測是正確的,等於0,也是0,此時,權重不更新。如預測是錯誤的,權重會按照學習速率與解釋變量值的乘積增加。
這里的更新規則與梯度下降法中的權重更新規則類似,都是朝着使樣本得到正確分類更新,且更新的幅度是由學習速率控制的。每遍歷一次訓練樣本稱為完成了一世代(epoch)。如果學習完一世代后,所有的樣本都分類正確,那么算法就會收斂(converge)。學習算法不能保證收斂;后面的章節,我們會介紹線性不可分數據集,是不可能收斂的。因此,學習算法還需要一個超參數,在算法終止前需要更新的最大世代數。
2. 感知器算法的實現
我是用Java語言來編寫的,實現的比較簡單的兩類,兩特征的感知器算法。該算法的目的是為了計算權向量W。程序開始時輸入樣本數N,然后程序隨機產生N個樣本值。
根據感知器算法的過程,程序在進行了15次計算后得到了最終的權向量W=(10,-2,-12)。所以線性分類判別函數是10x-2y-12=0,化簡得y=5x-6。為驗證該函數是否能正確分類,故把所有的樣本值點在XOY平面標出來,並畫出y=5x-6的函數圖象,結果如下所示:
從上圖中可以看出,該判別函數很好地把兩類數據分開了,得到的結果還是比較准確
下面的散點圖表面這些樣本是可以線性分離的:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def perception(W, w1, w2):
flag = False
while flag != True:
for i in range(len(w1)):
t1 = 0
t2 = 0
for j in range(len(W)):
t1 += W[j] * w1[i][j]
t2 += W[j] * w2[i][j]
if(t1 <= 0):
for j in range(len(W)):
W[j] += w1[i][j]
flag = False
break
if(t2 >= 0):
for j in range(len(W)):
W[j] -= w2[i][j]
flag = False
break
flag = True
print("判別函數:" + "d(x)= %d" % (W[0]) + "x" + "%d" % (W[1]) + "y" + "%d" % (W[2]) + "z+" + "%d" % (W[3]))
return W
if __name__ == '__main__':
W = [-1, -2, -2, 0]
w1 = [[0, 0, 0, 1], [1, 0, 0, 1], [1, 0, 1, 1], [1, 1, 0, 1]]
w2 = [[0, 0, 1, 1], [0, 1, 1, 1], [0, 1, 0, 1], [1, 1, 1, 1]]
W = perception(W, w1, w2)
fig = plt.figure()
ax = Axes3D(fig)
for i in range(len(w1)):
ax.scatter(w1[i][0],w1[i][1],w1[i][2],c = 'r',marker='*')
ax.scatter(w2[i][0],w2[i][1],w2[i][2],c = 'b',marker='o')
plt.grid()
xmin = min(min(w1[:][0]), min(w2[:][0]))
xmax = max(max(w1[:][0]), max(w2[:][0]))
ymin = min(min(w1[:][1]), min(w2[:][1]))
ymax = max(max(w1[:][1]), max(w2[:][1]))
x = np.linspace(xmin, xmax, 10)
y = np.linspace(ymin, ymax, 10)
x, y = np.meshgrid(x, y)
z = []
for i in range(len(x)):
z.append(((W[0]*x[i] + W[1]*y[i] + W[3]) / (-W[2])))
ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap='rainbow')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()