感知機(perceptron)是二分類的線性分類模型,輸入為實例的特征向量,輸出為實例的類別(取+1和-1)。感知機對應於輸入空間中將實例划分為兩類的分離超平面。感知機旨在求出該超平面,為求得超平面導入了基於誤分類的損失函數,利用梯度下降法 對損失函數進行最優化(最優化)。感知機的學習算法具有簡單而易於實現的優點,分為原始形式和對偶形式。感知機預測是用學習得到的感知機模型對新的實例進行預測的,因此屬於判別模型。感知機由Rosenblatt於1957年提出的,是神經網絡和支持向量機的基礎。
1. 感知機模型
定義
假設輸入空間(特征向量)為X⊆Rn,輸出空間為Y={-1, +1}。輸入x∈X表示實例的特征向量,對應於輸入空間的點;輸出y∈Y表示示例的類別。由輸入空間到輸出空間的函數為
f(x)=sign(w·x + b) (1)
稱為感知機。其中,參數w叫做權值向量,b稱為偏置。w·x表示w和x的內積。sign為符號函數,即
(2)
幾何解釋
感知機模型是線性分類模型,感知機模型的假設空間是定義在特征空間中的所有線性分類模型,即函數集合{f|f(x)=w·x+b}。線性方程 w·x+b=0對應於特征空間Rn中的一個超平面S,其中w是超平面的法向量,b是超平面的截踞。這個超平面把特征空間划分為兩部分。位於兩側的點分別為正負兩類。超平面S稱為分離超平面,如下圖:
學習與預測
感知機學習即由訓練數據集T={(x1,y1),(x2,y2)...(xN,yN)}(其中xi∈X=Rn,yi∈Y={-1, +1},i=1,2...N)求得感知機模型(1),即求得參數w,b;感知機預測即根據得到的感知機模型(1),對新的輸入實例給出對應的類型。
2. 感知機學習策略
假設訓練數據集是線性可分的,感知機學習的目標是求得一個能夠將訓練數據的正負實例點完全分開的分離超平面,即最終求得參數w、b。這需要一個學習策略,即定義(經驗)損失函數並將損失函數最小化。
損失函數的一個自然的選擇是誤分類的點的總數。但是這樣得到的損失函數不是參數w、b的連續可導函數,不宜優化。損失函數的另一個選擇是誤分類點到分里面的距離之和。
首先,對於任意一點xo到超平面的距離為
(3)
其次,對於誤分類點(xi,yi)來說 -yi(w·xi+b)>0
這樣,假設超平面S的總的誤分類點集合為M,那么所有誤分類點到S的距離之和為
(4)
不考慮1/||w||,就得到了感知機學習的損失函數。
經驗風險函數
給定數據集T={(x1,y1),(x2,y2)...(xN,yN)}(其中xi∈X=Rn,yi∈Y={-1, +1},i=1,2...N),感知機sign(w·x+b)學習的損失函數定義為
(5)
其中M為誤分類點的集合,這個損失函數就是感知機學習的經驗風險函數。
顯然,損失函數L(w,b)是非負的。如果沒有誤分類點,那么L(w,b)為0,誤分類點數越少,L(w,b)值越小。一個特定的損失函數:在誤分類時是參數w,b的線性函數,在正確分類時,是0.因此,給定訓練數據集T,損失函數L(w,b)是w,b的連續可導函數。
3. 感知機學習算法
最優化問題:給定數據集T={(x1,y1),(x2,y2)...(xN,yN)}(其中xi∈X=Rn,yi∈Y={-1, +1},i=1,2...N),求參數w,b,使其成為損失函數的解(M為誤分類的集合):
(6)
3.1 感知機學習的原始形式
感知機學習是誤分類驅動的,具體采用隨機梯度下降法。首先,任意選定w0、b0,然后用梯度下降法不斷極小化目標函數(6),極小化的過程不知一次性的把M中的所有誤分類點梯度下降,而是一次隨機選取一個誤分類點使其梯度下降。
假設誤分類集合M是固定的,那么損失函數L(w,b)的梯度由(7)(8)給出
(7)
(8)
隨機選取一個誤分類點(xi,yi),對w,b進行更新:
(9)
(10)
式中η(0≤η≤1)是步長,在統計學是中成為學習速率。步長越大,梯度下降的速度越快,更能接近極小點。如果步長過大,有可能導致跨過極小點,導致函數發散;如果步長過小,有可能會耗很長時間才能達到極小點。
算法(感知機學習算法的原始形式)
輸入:T={(x1,y1),(x2,y2)...(xN,yN)}(其中xi∈X=Rn,yi∈Y={-1, +1},i=1,2...N,學習速率為η) 輸出:w, b;感知機模型f(x)=sign(w·x+b) (1) 初始化w0,b0 (2) 在訓練數據集中選取(xi, yi) (3) 如果yi(w xi+b)≤0 w = w + ηyixi b = b + ηyi (4) 轉至(2)
直觀解釋:當一個實例點被誤分類時,調整w,b,使分離超平面向該誤分類點的一側移動,以減少該誤分類點與超平面的距離,直至超越該點被正確分類。
實現代碼
#!/usr/bin/python import sys import getopt def usage(): print '''Help Information: -h, --help: show help information; -t, --train: train file; -r, --ratio: training ratio; -b, --bias: initial bias; -x, --slopex: initial slopex; -y, --slopey: initial slopey; ''' def getErrNum(errFlag): errnum = 0 for i in range(0,len(errFlag),1): errnum += errFlag[i] return errnum def getResult(data,slopex,slopey,bias): res = data[0]*slopex + data[1]*slopey + bias return res if __name__=="__main__": #set parameter try: opts, args = getopt.getopt(sys.argv[1:], "ht:r:b:x:y:", ["help", "train=","ratio=","bias=","slopex=","slopey="]) except getopt.GetoptError, err: print str(err) usage() sys.exit(1) sys.stderr.write("\ntrain.py : a python script for perception training.\n") sys.stderr.write("Copyright 2016 sxron, search, Sogou. \n") sys.stderr.write("Email: shixiang08abc@gmail.com \n\n") train = '' ratio = 0.1 bias = 0.0 slopex = 1.0 slopey = 1.0 for i, f in opts: if i in ("-h", "--help"): usage() sys.exit(1) elif i in ("-t", "--train"): train = f elif i in ("-r", "--ratio"): ratio = float(f) elif i in ("-b", "--bias"): bias = float(f) elif i in ("-x", "--slopex"): slopex = float(f) elif i in ("-y", "--slopey"): slopey = float(f) else: assert False, "unknown option" print "start trian parameter \ttrain:%s\tratio:%f\tbias:%f\tslopex:%f\tslopey:%f" % (train,ratio,bias,slopex,slopey) #read train file orgdata = [] label = [] fin = open(train,'r') while 1: line = fin.readline() if not line: break ts = line.strip().split('\t') if len(ts)==3: try: lbx = int(ts[0]) lby = int(ts[1]) lb = int(ts[2]) except: continue data = [] data.append(lbx) data.append(lby) orgdata.append(data) label.append(lb) fin.close() for i in range(0,len(label),1): print "%d\t%d\t%d" % (orgdata[i][0],orgdata[i][1],label[i]) errFlag = [] for i in range(0,len(label),1): errFlag.append(0) while 1: for i in range(0,len(label),1): errFlag[i] = 0 result = getResult(orgdata[i],slopex,slopey,bias) if result*label[i]<0: slopex = slopex + orgdata[i][0]*label[i]*ratio slopey = slopey + orgdata[i][1]*label[i]*ratio bias = bias + label[i]*ratio errFlag[i] = 1 errnum = getErrNum(errFlag) if errnum==0: break print "slopex:%f\tslopey:%f\tbias:%f\t" % (slopex,slopey,bias)