/*先把標題給寫了、這樣就能常常提醒本身*/html
咱們先來定義一下什麼是感知機。所謂感知機,就是二類分類的線性分類模型,其輸入爲樣本的特徵向量,輸出爲樣本的類別,取+1和-1二值,即經過某樣本的特徵,就能夠準確判斷該樣本屬於哪一類。顧名思義,感知機可以解決的問題首先要求特徵空間是線性可分的,再者是二類分類,即將樣本分爲{+1, -1}兩類。從比較學術的層面來講,由輸入空間到輸出空間的函數:java
(1)python
稱爲感知機,w和b爲感知機參數,w爲權值(weight),b爲偏置(bias)。sign爲符號函數:git
(2)github
感知機模型的假設空間是定義在特徵空間中的全部線性分類模型,即函數集合{f|f(x) = w·x + b}。在感知機的定義中,線性方程w·x + b = 0對應於問題空間中的一個超平面S,位於這個超平面兩側的樣本分別被歸爲兩類,例以下圖,紅色做爲一類,藍色做爲另外一類,它們的特徵很簡單,就是它們的座標算法
圖1api
做爲監督學習的一種方法,感知機學習由訓練集求得感知機模型,即求得模型參數w,b,這裏x和y分別是特徵向量和類別(也稱爲目標)。基於此,感知機模型能夠對新的輸入樣本進行分類。網絡
前面半抄書半自說自話把感知機的定義以及是用來幹嗎的簡單記錄了一下,做爲早期的機器學習方法(1957年由Frank Rosenblatt提出),它是最簡單的前饋神經網絡,對以後的機器學習方法如神經網絡起一個基礎的做用,下一節將詳細介紹感知機學習策略。機器學習
上節說到,感知機是一個簡單的二類分類的線性分類模型,要求咱們的樣本是線性可分的,什麼樣的樣本是線性可分的呢?舉例來講,在二維平面中,能夠用一條直線將+1類和-1類完美分開,那麼這個樣本空間就是線性可分的。如圖1就是線性可分的,圖2中的樣本就是線性不可分的,感知機就不能處理這種狀況。所以,在本章中的全部問題都基於一個前提,就是問題空間線性可分。函數
圖2
爲方便說明問題,咱們假設數據集中全部的的實例i,有;對的實例有。
這裏先給出輸入空間中任一點到超平面S的距離:
(3)
這裏||w||是w的範數。
對於誤分類的數據,根據咱們以前的假設,有
(4)
所以誤分類點到超平面S的距離能夠寫做:
(5)
假設超平面S的誤分類點集合爲M,那麼全部誤分類點到超平面S的總距離爲
(6)
這裏的||w||值是固定的,沒必要考慮,這樣就獲得了感知機學習的損失函數。根據咱們的定義,這個損失函數天然是越小越好,由於這樣就表明着誤分類點越少、誤分類點距離超平面S的距離越近,即咱們的分類面越正確。顯然,這個損失函數是非負的,若全部的樣本都分類正確,那麼咱們的損失函數值爲0。一個特定的樣本集T的損失函數:在誤分類時是參數w,b的線性函數。也就是說,爲求得正確的參數w,b,咱們的目標函數爲
(7)
而它是連續可導的,這就使得咱們能夠比較容易的求得其最小值。
感知機學習的策略是在假設空間中選取使咱們的損失函數(7)最小的模型參數w,b,即感知機模型。
根據感知機定義以及咱們的假設,獲得了感知機的模型,即目標函數(7),將其最小化的本質就是使得分類面S儘量的正確,下一節介紹將其最小化的方法——隨機梯度降低。
根據感知機學習的策略,咱們已經將尋找超平面S的問題轉化爲求解式(7)的最優化問題,最優化的方法是隨機梯度降低法,書中介紹了兩種形式:原始形式和對偶形式,並證實了在訓練集線性可分時算法的收斂性。
所謂原始形式,就是咱們用梯度降低的方法,對參數w和b進行不斷的迭代更新。具體來講,就是先任意選取一個超平面,對應的參數分別爲和,固然如今是能夠任意賦值的,好比說選取爲全爲0的向量,的值爲0。而後用梯度降低不斷地極小化損失函數(7)。因爲隨機梯度降低(stochastic gradient descent)的效率要高於批量梯度降低(batch gradient descent)(詳情可參考Andrew Ng教授的講義,在Part 1的LMS algorithm部分),因此這裏採用隨機梯度降低的方法,每次隨機選取一個誤分類點對w和b進行更新。
設誤分類點集合M是固定的,爲求式(7)的最小值,咱們須要知道往哪一個方向降低速率最快,這是可由對損失函數L(w, b)求梯度獲得,L(w, b)的梯度爲
接下來隨機選取一個誤分類點對w,b進行更新
(8)
(9)
其中爲步長,也稱爲學習速率(learning rate),通常在0到1之間取值,步長越大,咱們梯度降低的速度越快,也就能更快接近極小點。若是步長過大,就有直接跨過極小點致使函數發散的問題;若是步長太小,可能會耗費比較長的時間才能達到極小點。經過這樣的迭代,咱們的損失函數就不斷減少,直到爲0。綜上所述,獲得以下算法:
算法1 (感知機學習算法的原始形式)
輸入:訓練數據集,其中,,i = 1,2,…,N;學習率
輸出:w,b;感知機模型
(1)選取初始值,
(2)在訓練集中選取數據
(3)若是(從公式(3)變換而來)
(4)轉至(2),直至訓練集中沒有誤分類點
這種學習算法直觀上有以下解釋:當一個樣本被誤分類時,就調整w和b的值,使超平面S向誤分類點的一側移動,以減小該誤分類點到超平面的距離,直至超平面越過該點使之被正確分類。
書上還給出了一個例題,這是我推崇這本書的緣由之一,凡是隻講理論不給例子的行爲都是耍流氓!
例1 如圖3所示的訓練數據集,其正實例點是,,負實例點是,試用感知機學習算法的原始形式求感知機模型,即求出w和b。這裏,
圖3
這裏咱們取初值,取。具體問題解釋不寫了,求解的方法就是算法1。下面給出這道題的Java代碼(終於有一段是本身純原創的了)。
package org.juefan.perceptron; import java.util.ArrayList; import org.juefan.basic.FileIO; public class PrimevalPerceptron { public static ArrayList<Integer> w = new ArrayList<>(); public static int b ; /*初始化參數*/ public PrimevalPerceptron(){ w.add(5); w.add(-2); b = 3; } /** * 判斷是否分類正確 * @param data 待判斷數據 * @return 返回判斷正確與否 */ public static boolean getValue(Data data){ int state = 0; for(int i = 0; i < data.x.size(); i++){ state += w.get(i) * data.x.get(i); } state += b; return state * data.y > 0? true: false; } //此算法基於數據是線性可分的,若是線性不可分,則會進入死循環 public static boolean isStop(ArrayList<Data> datas){ boolean isStop = true; for(Data data: datas){ isStop = isStop && getValue(data); } return isStop; } public static void main(String[] args) { PrimevalPerceptron model = new PrimevalPerceptron(); ArrayList<Data> datas = new ArrayList<>(); FileIO fileIO = new FileIO(); fileIO.setFileName(".//file//perceptron.txt"); fileIO.FileRead(); for(String data: fileIO.fileList){ datas.add(new Data(data)); } /** * 若是所有數據都分類正確則結束迭代 */ while(!isStop(datas)){ for(int i = 0; i < datas.size(); i++){ if(!getValue(datas.get(i))){ //這裏面能夠理解爲是一個簡單的梯度降低法 for(int j = 0; j < datas.get(i).x.size(); j++) w.set(j, w.get(j) + datas.get(i).y * datas.get(i).x.get(j)); b += datas.get(i).y; System.out.println(w + "\t" + b); } } } System.out.println(w + "\t" + b); //輸出最終的結果 } }
最後解得(這裏應該是寫錯了,最終結果b=-3)。不過,若是選取的初值不一樣,或者選取的誤分類點不一樣,咱們獲得的超平面S也不盡相同,畢竟感知機模型的解是一組符合條件的超平面的集合,而不是某一個最優超平面。
一節純數學的東西,用了兩整頁證實了Novikoff定理,看到這裏才知道智商真的是個硬傷,反覆看了兩遍,又把證實的過程本身推導了一遍,算是看懂了,若是憑空證實的話,本身的功力還差得遠。
Novikoff於1962年證實了感知機算法的收斂性,做爲一個懶人,因爲這一節涉及大量公式,即便有latex插件也是個麻煩的工做,具體什麼狀況我就不談了,同時,哥倫比亞大學有這樣的一篇叫《Convergence Proof for the Perceptron Algorithm》的筆記,講解了這個定理的證實過程,也給了我一個偷懶的理由。
書上說對偶形式的基本想法是,將w和b表示爲實例和的線性組合形式,經過求解其係數而求得w和b。這個想法及下面的算法描述很容易看懂,只不過爲何要這麼作?將w和b用x和y來表示有什麼好處呢?看起來也不怎麼直觀,計算量上彷佛並無減小。若是說支持向量機的求最優過程使用對偶形式是爲了方便引入核函數,那這裏的對偶形式是用來作什麼的呢?暫且認爲是對後面支持向量機的一個鋪墊吧,或者是求解這種優化問題的一個廣泛解法。
繼續正題,爲了方便推導,可將初始值和都設爲0,據上文,咱們對誤分類點經過
來更新w,b,假設咱們經過誤分類點更新參數的次數爲次,那麼w,b關於的增量爲和,爲方便,可將用來表示,很容易能夠獲得
(10)
(11)
這裏i = 1,2,…,N。當時,表示第i個樣本因爲被誤分類而進行更新的次數。某樣本更新次數越多,表示它距離超平面S越近,也就越難正確分類。換句話說,這樣的樣本對學習結果影響最大。
算法2 (感知機學習算法的對偶形式)
輸入:訓練數據集,其中,,i = 1,2,…,N;學習率
輸出:,b;感知機模型
其中
(1),
(2)在訓練集中選取樣本
(3)若是
(4)轉至(2)直到沒有誤分類樣本出現
因爲訓練實例僅之內積的形式出現,爲方便,可預先將訓練集中實例間的內積計算出來並以矩陣形式存儲(就是那個的部分),這就是所謂的Gram矩陣(線性代數學的很差的飄過ToT):
又到例題時間!再說一遍,這本書最大的好處就是有實例,凡是隻講理論不給例子的行爲都是耍流氓!
例2 同例1,只不過是用對偶形式來求解。
一樣過程再也不分析,給出個人求解代碼:
package org.juefan.perceptron; import java.util.ArrayList; import org.juefan.basic.FileIO; public class GramPerceptrom { public static ArrayList<Integer> a = new ArrayList<>(); public static int b ; /*初始化參數*/ public GramPerceptrom(int num){ for(int i = 0; i < num; i++) a.add(0); b = 0; } /**Gram矩陣*/ public static ArrayList<ArrayList<Integer>> gram = new ArrayList<>(); public void setGram(ArrayList<Data> datas){ for(int i = 0; i < datas.size(); i++){ ArrayList<Integer> rowGram = new ArrayList<>(); for(int j = 0; j < datas.size(); j++){ rowGram.add(Data.getInner(datas.get(i), datas.get(j))); } gram.add(rowGram); } } /**是否正確分類*/ public static boolean isCorrect(int i, ArrayList<Data> datas){ int value = 0; for(int j = 0; j < datas.size(); j++) value += a.get(j)*datas.get(j).y * gram.get(j).get(i); value = datas.get(i).y * (value + b); return value > 0 ? true: false; } //此算法基於數據是線性可分的,若是線性不可分,則會進入死循環 public static boolean isStop(ArrayList<Data> datas){ boolean isStop = true; for(int i = 0; i < datas.size(); i++){ isStop = isStop && isCorrect(i, datas); } return isStop; } public static void main(String[] args) { ArrayList<Data> datas = new ArrayList<>(); FileIO fileIO = new FileIO(); fileIO.setFileName(".//file//perceptron.txt"); fileIO.FileRead(); for(String data: fileIO.fileList){ datas.add(new Data(data)); } GramPerceptrom gram = new GramPerceptrom(datas.size()); gram.setGram(datas); System.out.println(datas.size()); while(!isStop(datas)){ for(int i = 0; i < datas.size(); i++) if(!isCorrect(i, datas)){ a.set(i, a.get(i) + 1); b += datas.get(i).y; System.out.println(a + "\t" + b); } } } }
終於寫完一章的內容了,用了很多功夫,果真是提及來容易作起來難呢。不過經過這樣記錄的方式(雖然大部分是抄書),本身對相關算法的理論及過程就又學了一遍,以爲這個時間仍是花的值得的。
本章介紹了統計學習中最簡單的一種算法——感知機,對如今的機器學習理論來講,這個算法的確是太簡單了,但這樣簡單的東西倒是不少如今流行算法的基礎,好比神經網絡,好比支持向量機,Deep Learning還沒了解過,不知道有多大聯繫。固然,實際應用價值不能說沒有,能夠用於一些簡單的線性分類的狀況,也能夠由簡單的二類分類擴展到多類分類(詳見此PPT),能夠用於天然語義處理等領域。
再將思路整理一下,儘可能掌握感知機算法,再好好看看維基百科連接中的有關文獻。
PS:本文的內容轉載自 http://www.cnblogs.com/OldPanda/archive/2013/04/12/3017100.html
原博主是Python寫的代碼,我這邊改爲Java了
對代碼有興趣的能夠上本人的GitHub查看:https://github.com/JueFan/StatisticsLearningMethod/
2014-06-29:倆種算法的代碼都完成了,更新完畢