最近開始學習一些機器學習裡的主要算法,SVM支持向量機是我目前產品裡用到的核心算法,想在這裡把我學到的一些東西記錄下來。因為本人也是新手,所以我儘量用小白的語言來說,當然有說的不太對的地方請指正。
機器學習人工智慧總是給我們很高大上,很精深的感覺,事實上它確實很專業化,尤其是模型裡用到的各種數學公式很難記憶(同學當你去看專業的算法文章的時候,你暈嗎),我個人覺得其實機器學習是一種將現實世界的模型抽象成數學模型的一種方法,就是把我們想要解決的一個現實問題抽象成數學公式,該公式裡有未知的參數,然後我們要用大量的訓練數據計算出這些參數的值。拿到參數值後,當我們需要對某一個對象進行預測的時候,我們做的事情就是把對象的屬性代入到公式裡得到一個值,就是我們的預測值。
下面是SVM的一個介紹支持向量機(support vector machine)是一種分類算法,通過尋求結構化風險最小來提高學習機泛化能力,實現經驗風險和置信範圍的最小化,從而達到在統計樣本量較少的情況下,亦能獲得良好統計規律的目的。通俗來講,它是一種二類分類模型,其基本模型定義為特徵空間上的間隔最大的線性分類器。
大概介紹下什麼是結構化風險:機器學習本質上就是一種對問題真實模型的逼近,但毫無疑問,真實模型一定是不知道的(如果知道了直接用真實模型解決問題不就可以了,就不需要機器學習了)既然真實模型不知道,那麼我們選擇的假設與問題真實解之間究竟有多大差距,我們就沒法得知。 這個與問題真實解之間的誤差,就叫做風險(更嚴格的說,誤差的累積叫做風險)。我們選擇了一個假設之後(更直觀點說,我們得到了一個分類器以後),真實誤差無從得知,但我們可以用某些可以掌握的量來逼近它。最直觀的想法就是使用分類器在樣本數據上的分類的結果與真實結果(因為樣本是已經標註過的數據,是準確的數據)之間的差值來表示。這個差值叫做經驗風險。
統計學習認為真實風險應該由兩部分內容刻畫,一是經驗風險,代表了分類器在給定樣本上的誤差;二是置信風險,代表了我們在多大程度上可以信任分類器在未知文本上分類的結果。很顯然,第二部分是沒有辦法精確計算的,因此只能給出一個估計的區間,也使得整個誤差只能計算上界,而無法計算準確的值。
統計學習的目標從經驗風險最小化變為了尋求經驗風險與置信風險的和最小,即結構風險最小。 SVM正是這樣一種努力最小化結構風險的算法。
下面來看個最最簡單的分類問題用一根直線把紅點跟藍點區分開來。
紅點跟藍點是要區分的兩個類別,在二維平面中它們的樣本如上圖所示。中間的直線就是一個分類函數,它可以將兩類樣本完全分開。一般的,如果一個線性函數能夠將樣本完全正確的分開,就稱這些數據是線性可分的,否則稱為非線性可分的。
本文僅談論線性可分的情況,包括下面我給的一個例子也是線性可分模型的,線性不可分及升維的方法我們下一次再聊,其實SVM強在於對線性不可分問題的解決,但線性可分其實可以看成是線性不可分的一種特例。
實際上,一個線性函數是一個實值函數(即函數的值是連續的實數),而我們的分類問題(例如這裡的二元分類問題——回答一個樣本屬於還是不屬於一個類別的問題)需要離散的輸出值,例如用1表示某個樣本屬於類別A,而用0表示不屬於(不屬於A也就意味著屬於B),這時候只需要簡單的在實值函數的基礎上附加一個閾值即可,通過分類函數執行時得到的值大於還是小於這個閾值來確定類別歸屬。 例如我們有一個線性函數 g(x)=wx+b
我們可以取閾值為0,這樣當有一個樣本xi需要判別的時候,我們就看g(xi)的值。若g(xi)>0,就判別為類別A,若g(xi)<0,則判別為類別B(等於的時候我們就拒絕判斷)。此時也等價於給函數g(x)附加一個符號函數sgn(),即f(x)=sgn [g(x)]是我們真正的判別函數。
注意g(x)不是中間那條直線的表達式,中間那條直線的表達式是g(x)=0,即wx+b=0,我們也把這個函數叫做分類面。
算法的目的無非是找出一個函數f(x),這個函數能讓我們把輸入的數據x進行分類。既然是分類肯定需要一個評判的標準,比如分出來有兩種情況A和B,那麼怎麼樣才能說x是屬於A類的,或不是B類的呢?就是需要有個邊界,就好像兩個國家一樣有邊界,如果邊界越明顯,則就越容易區分,因此,我們的目標是最大化邊界的寬度,使得非常容易的區分是A類還是B類。怎麼樣得到這個最大間隔函數,是一個複雜的理論推導,其實我們的算法模型本質上就是用大量的x去計算出這個判定函數,得到一個最優解。
放出經過數學推導後的最優函數,具體推導過程可以去參閱SVM理論文章,這裡就不放了,我也沒看懂。
輸入是x,是一個數組,組中每一個值表示一個特徵。 輸出是A類還是B類。(正類還是負類)
如何用JAVA實現SVM的小例子這個代碼是從網上找的,我只是想通過這個代碼講一下我們要用一個算法模型的大概步驟是怎樣。 該代碼需要使用libsvm,libsvm是臺灣林智仁教授開發的SVM的庫,使用起來非常方便。 你可以下載libsvm壓縮包解壓到本地然後調用(from:http://www.csie.ntu.edu.tw/~cjlin/libsvm/index.html)
先總結下使用SVM模型的幾個步驟: + 定義訓練集數據及標註 + 定義模型參數 + 調用接口訓練模型 + 定義測試集數據 + 對測試數據進行分類預測
具體實現代碼如下,後面都有加注釋。
package com.taobao.svm;import libsvm.svm; import libsvm.svm_model; import libsvm.svm_node; import libsvm.svm_parameter; import libsvm.svm_problem;public class SvmTest { /** * @param args */ public static void main(String[] args) { //定義訓練集點a{10.0, 10.0} 和 點b{-10.0, -10.0},對應lable為{1.0, -1.0} svm_node pa0 = new svm_node(); pa0.index = 0; pa0.value = 10.0; svm_node pa1 = new svm_node(); pa1.index = -1; pa1.value = 10.0; svm_node pb0 = new svm_node(); pb0.index = 0; pb0.value = -10.0; svm_node pb1 = new svm_node(); pb1.index = 0; pb1.value = -10.0; svm_node[] pa = {pa0, pa1}; //點a svm_node[] pb = {pb0, pb1}; //點b svm_node[][] datas = {pa, pb}; //訓練集的向量表 double[] lables = {1.0, -1.0}; //a,b 對應的lable //定義svm_problem對象 svm_problem problem = new svm_problem(); problem.l = 2; //向量個數 problem.x = datas; //訓練集向量表 problem.y = lables; //對應的lable數組 //定義svm_parameter對象 svm_parameter param = new svm_parameter(); param.svm_type = svm_parameter.C_SVC; param.kernel_type = svm_parameter.LINEAR; //這裡表示我用的是線性模型 param.cache_size = 100; param.eps = 0.00001; param.C = 1; //訓練SVM分類模型 System.out.println(svm.svm_check_parameter(problem, param)); //如果參數沒有問題,則svm.svm_check_parameter()函數返回null,否則返回error描述。 svm_model model = svm.svm_train(problem, param); //svm.svm_train()訓練出SVM分類模型 //定義測試數據點c svm_node pc0 = new svm_node(); pc0.index = 0; pc0.value = -0.1; svm_node pc1 = new svm_node(); pc1.index = -1; pc1.value = 0.0; svm_node[] pc = {pc0, pc1}; //預測測試數據的lable System.out.println(svm.svm_predict(model, pc)); }}
運行結果:
null *optimization finished, #iter = 1 nu = 0.0033333333333333335 obj = -0.0033333333333333335, rho = 0.0 nSV = 2, nBSV = 0 Total nSV = 2 -1.0
第一行null是svm.svmcheckparameter(problem, param)的輸出,表示參數設置無誤;最後一行的-1.0表示對c點的分類預測lable是-1.0。
該代碼主要用了svm.svmtrain()做訓練,用svm.svmpredict()做預測, obj是SVM問題的最優目標值, nSV和nBSV是支持向量和有界支持向量的數目。
上面的代碼是一個最簡單的線性可分模型的建立,我們可以傳入更多的訓練數據讓預測結果更加準確。當然這個只是一個簡單的小例子,我們業務上使用的數據和模型是比這個複雜得多的,而且是非線性的,工作量比較大且複雜的是如何選擇特徵及對訓練集進行標註,後面有機會再講。
更多測試技術文章,微信掃以下二維碼,歡迎關注
測碼奔跑-讓測試技術奔跑起來