第12章支持向量機python
在機器學習中,支持向量機(Support Vector Machine,SVM)是最經典的算法之一,應用領域也很是廣,其效果天然也是很厲害的。本章對支持向量機算法進行解讀,詳細分析其每一步流程及其參數對結果的影響。算法
12.1支持向量機工做原理安全
前面已經給你們講解了一些機器學習算法,有沒有發現其中的一些套路呢?它們都是從一個要解決的問題出發,而後將實際問題轉換成數學問題,接下來優化求解便可。支持向量機涉及的數學內容比較多,下面仍是從問題開始一步步解決。網絡
12.1.1支持向量機要解決的問題框架
如今由一個小例子來引入支持向量機,圖12-1中有兩類數據點,目標就是找到一個最好的決策方程將它們區分開。dom
圖12-1 決策方程的選擇機器學習
圖12-1中有3條直線都能將兩類數據點區分開,那麼,這3條線的效果相同嗎?確定是有所區別的。你們在作事情的時候,確定但願可以作到最好,支持向量機也是如此,不僅要作這件事,還要達到最好的效果,那麼這3條線中哪條線的效果最好呢?如今放大劃分的細節進行觀察,如圖12-2所示。異步
由圖可見,最明顯的一個區別,就是左邊的決策邊界看起來窄一點,而右邊的寬一點。假設如今有一個大部隊在道路上前進,左邊埋着地雷,右邊埋伏敵人,爲了大部隊可以最安全地前進,確定但願選擇的道路可以避開這些危險,也就是離左右兩邊都儘量越遠越好。函數
想法已經很明確,回到剛纔的數據點中,選擇更寬的決策邊界更佳,由於這樣才能離這些雷更遠,中間部分能夠看做隔離帶,這樣容忍錯誤能力更強,效果天然要比窄的好。
圖12-2 邊界的選擇
12.1.2距離與標籤訂義
上一小節一直強調必定要避開危險的左右雷區,在數學上首先要明確指出離「雷區」的距離,也就是一個點(雷)到決策面的距離,而後才能繼續優化目標。仍是舉一個例子,假設平面方程爲wTx+b=0,平面上有x’和x」兩個點,W爲平面的法向量,要求x點到平面h的距離,如圖12-3所示。既然x’和x」都在平面上,所以知足:
wTx’+b=0,wTx」+b=0 (12.1)
直接計算x點到平面的距離看起來有點難,能夠轉換一下,若是獲得x到x’的距離後,再投影到平面的法向量方向上,就容易求解了,距離定義以下:
圖12-3 點到決策邊界的距離
其中,WT /||W||爲平面法向量的方向,也就是要投影的方向,因爲只是投影而已,因此只需獲得投影方向的單位方向向量便可;(x-x’)爲間接地經過x和x’計算距離;又因爲x’在平面上,wTx’就等於−b。這樣就有了距離的計算方法。
接下來開始定義數據集:(X1,Y1)(X2,Y2)...(Xn,Yn),其中,Xn爲數據的特徵,Yn爲樣本的標籤。當Xn爲正例時候,Yn=+1;當Xn爲負例時候,Yn=−1。這樣定義是爲了以後的化簡作準備,前面提到過,邏輯迴歸中定義的類別編號0和1也是爲了化簡。
最終的決策方程以下:
這個方程看起來很熟悉,其中x和y是已知的(數據中給出,有監督問題),目標就是要求解其中的參數,可是x怎麼有點特別呢?其中φ(x)表示對數據進行了某種變換,這裏能夠先無論它,依舊把它看成數據便可。
對於任意輸入樣本數據x,有:
所以可得:
如今相信你們已經發現標籤Y定義成±1的目的了,式(12.5)中的這個條件主要用於完成化簡工做。
12.1.3目標函數
再來明確一下已知的信息和要完成的任務,根據前面介紹可知,目標就是找到一個最好的決策方程(也就是w和b的值),而且已知數據點到決策邊界的距離計算方法。下面就要給出目標函數,你們都知道,機器學習的思想都是由一個實際的任務出發,將之轉換成數學表達,再獲得目標函數,最後去優化求解。
這裏要優化的目標就是使得離決策方程最近的點(雷)可以越遠越好,這句話看似簡單,其實只要理解了如下兩點,支持向量機已經弄懂一半了,再來解釋一下。
1.爲何要選擇離決策方程最近的點呢?能夠這麼想,若是你踩到了最近的「雷」,還須要驗證更遠的「雷」嗎?也就是危險是由最近的「雷」帶來的,只要避開它,其餘的構不成威脅。
2.爲何越寬越好呢?由於在選擇決策方程的時候,確定是要找最寬的邊界,越遠離邊界才能越安全。
目標函數很是重要,全部機器學習問題均可以歸結爲經過目標函數選擇合適的方法並進行優化。
式(12.2)中已經給出了點到邊界距離的定義,只不過是帶有絕對值的,用起來好像有點麻煩,須要再對它進行簡化。經過定義數據標籤已經有結論,即yiy(xi)>0。其中的y(xi)就是wTx+b,因爲標籤值只能是±1,因此乘以它不會改變結果,但能夠直接把絕對值去掉,用起來就方便多了,新的距離公式以下:
按照以前目標函數的想法,能夠定義爲:
遇到一個複雜的數學公式,能夠嘗試從裏向外一步步觀察。
式(12.7)看起來有點複雜,其實與以前的解釋徹底一致,首先min要求的就是距離,目的是找到離邊界最近的樣本點(雷)。而後再求的距離越大越好。
式(12.7)雖然給出了優化的目標,但看起來仍是比較複雜,爲了方便求解,還需對它進行一番化簡,已知yiy(xi)>0,如今能夠經過放縮變換把要求變得更嚴格,即
yi(wTφ(xi)+b)≥1,可得的最小值就是1。
所以只需考慮便可,如今獲得了新的目標函數。可是,不要忘記它是有條件的。
概況以下:
式(12.8)是一個求極大值的問題,機器學習中常規套路就是轉換成求極小值問題,所以能夠轉化爲:
式(12.9)中,求一個數的最大值等同於求其倒數的極小值,條件依舊不變。求解過程當中,關注的是極值點(也就是w和b取什麼值),而非具體的極值大小,因此,對公式加入常數項或進行某些函數變換,只要保證極值點不變便可。如今有了要求解的目標,而且帶有約束條件,接下來就是如何求解了。
12.1.4拉格朗日乘子法
拉格朗日乘子法用於計算有約束條件下函數的極值優化問題,計算式以下:
式(12.10)能夠轉化爲:
回顧下式(12.9)給出的標函數和約束條件,是否是剛好知足拉格朗日乘子法的要求呢?接下來直接套用便可,注意約束條件只有一個:
有些同窗可能對拉格朗日乘子法不是特別熟悉,式(12.12)中引入了一個乘子α,概述起來就像是原始要求解的w和b參數在約束條件下比較難解,能不能把問題轉換一下呢?若是能夠找到w和b分別與α的關係,接下來獲得每個合適的α值,天然也就能夠求出最終w和b的值。
此處還有其中一個細節就是KKT條件,3個科學家作了對偶性質的證實,此處先不建議你們深刻KKT細節,對初學者來講,就是從入門到放棄,先記住有這事便可,等從總體上掌握支持向量機以後,能夠再作深刻研究,暫且默認有一個定理能夠幫咱們把問題進行轉化:
既然是要求解w和b以獲得極值,須要對式(12.12)中w,b求偏導,並令其偏導等於0,可得:
如今彷佛把求解w和b的過程轉換成與α相關的問題,此處雖然沒有直接獲得b和α的關係,可是,在化簡過程當中,仍可基於對b參數進行化簡。
接下來把上面的計算結果代入式(12.12),至關於把w和b所有替換成與α的關係,化簡以下:
此時目標就是α值爲多少時,式(12.15)中L(w,b,α)的值最大,這又是一個求極大值的問題,因此按照套路仍是要轉換成求極小值問題,至關於求其相反數的極小值:
約束條件中,是對b求偏導獲得的,αi≥0是拉格朗日乘子法自身的限制條件,它們很是重要。到此爲止,咱們完成了支持向量機中的基本數學推導,剩下的就是如何求解。
12.2支持向量的做用
你們是否對支持向量基這個概念的來源有過疑問,在求解參數以前先向你們介紹支持向量的定義及其做用。
12.2.1支持向量機求解
式(12.16)中已經給出了要求解的目標,爲了更直白地理解支持向量的含義,下述實例中,只取3個樣本數據點,便於計算和化簡。
假設如今有3個數據,其中正例樣本爲X1(3,3),X2(4,3);負例爲X3(1,1),如圖12-4所示。
首先,將3個樣本數據點(x和y已知)代入式(12.16),可得:
圖12-4 數據樣本點
因爲只有3個樣本數據點,而且樣本的標籤已知,可得:
暫且認爲φ(x)=x,其中(xi,xj)是求內積的意思,將3個樣本數據點和條件α1+α2-α3=0代入式(12.17)可得:
既然要求極小值,對式(12.18)中分別計算偏導,並令偏導等於零,可得:兩個結果並不知足給定的約束條件αi≥0。所以須要考慮邊界上的狀況,α1=0或α2=0。分別將這兩個值代入上式,可得:
將式(12.19)結果分別代入公式,經過對比可知,S(0.25,0)時取得最小值,即a1=0.25,a2=0符合條件,此時a3=a1+a2=0.25。
因爲以前已經獲得w與α的關係,求解出來所有的α值以後,就能夠計算w和b,可得:
求解b參數的時候,選擇用其中一個樣本點數據計算其結果,可是,該樣本的選擇必須爲支持向量。
計算出全部參數以後,只需代入決策方程便可,最終的結果爲:
這也是圖12-4中所畫直線,這個例子中α值能夠直接求解,這是因爲只選了3個樣本數據點,可是,若是數據點繼續增多,就很難直接求解,現階段主要依靠SMO算法及其升級版本進行求解,其基本思想就是對α參數兩兩代入求解,感興趣的讀者能夠找一份SMO求解代碼,本身一行一行debug觀察求解方法。
12.2.2支持向量的做用
在上述求解過程當中,能夠發現權重參數w的結果由α,x,y決定,其中x,y分別是數據和標籤,這些都是固定不變的。若是求解出αi=0,意味着當前這個數據點不會對結果產生影響,由於它和x,y相乘後的值還爲0。只有αi≠0時,對應的數據點纔會對結果產生做用。
由圖12-5可知,最終只有x1和X3參與到計算中,x2並無起到任何做用。細心的讀者可能還會發現x1和X3都是邊界上的數據點,而x2與x1相比,就是非邊界上的數據點。這些邊界上的點,就是最開始的時候解釋的離決策方程最近的「雷」,只有它們會對結果產生影響,而非邊界上的點只是湊熱鬧罷了。
到此揭開了支持向量機名字的含義,對於邊界上的數據點,例如x1和X3就叫做支持向量,它們把整個框架支撐起來。對於非邊界上的點,天然就是非支持向量,它們不會對結果產生任何影響。
圖12-6展現了支撐向量對結果的影響。圖12-6(a)選擇60個數據點,其中圈起來的就是支持向量。圖12-6(b)選擇120個數據點,但仍然保持支持向量不變,使用一樣的算法和參數來建模,獲得的結果徹底相同。這與剛剛獲得的結論一致,只要不改變支持向量,增長部分數據對結果沒有任何影響。
▲圖12-5 支持向量的做用
▲圖12-6 支持向量對結果的影響
12.3支持向量機涉及參數
在建模過程當中,確定會涉及調參問題,那麼在支持向量機中都有哪些參數呢?其中必不可缺的就是軟間隔和核函數,本節向你們解釋其做用。
12.3.1軟間隔參數的選擇
在機器學習任務中,常常會遇到過擬合問題,以前在定義目標函數的時候給出了很是嚴格的標準,就是要在知足能把兩類數據點徹底分得開的狀況下,再考慮讓決策邊界越寬越好,可是,這麼作必定能獲得最好的結果嗎?
假設有兩類數據點分別爲○和×,若是沒有左上角的○,看起來這條虛線作得很不錯,可是,若是把這個多是異常或者離羣的數據點考慮進去,結果就會發生較大變化,此時爲了知足一個點的要求,只能用實線進行區分,決策邊界一會兒窄了好多,如圖12-7所示。
總而言之,模型爲了可以知足個別數據點作出了較大的犧牲,而這些數據點極可能是離羣點、異常點等。若是還要嚴格要求模型必須作到徹底分類正確,結果可能會拔苗助長。
若是在必定程度上放低對模型的要求,能夠解決過擬合問題,先來看看定義方法:
圖12-7 過擬合問題
觀察發現,原來的約束條件中,要求yi(w(xi)+b)≥1,如今加入一個鬆弛因子,就至關於放低要求了。
此時,新的目標函數定義爲:。在目標函數中,引入了一個新項
,它與正則化懲罰的原理相似,用控制參數C表示嚴格程度。目標與以前一致,仍是要求極小值,下面用兩個較極端的例子看一下C參數的做用。
1.當C趨近於無窮大時,只有讓ξi很是小,才能使得總體獲得極小值。這是因爲C參數比較大,若是ξi再大一些的話,就無法獲得極小值,這意味着與以前的要求差很少,仍是要讓分類十分嚴格,不能產生錯誤。
2.當C趨近於無窮小時,即使ξi大一些也不要緊,意味着模型能夠有更大的錯誤容忍度,要求就沒那麼高,錯幾個數據點也不要緊。
雖然目標函數發生了變換,求解過程依舊與以前的方法相同,下面直接列出來,瞭解一下便可。
此時約束定義爲:
通過化簡可得最終解:
支持向量機中的鬆弛因子比較重要,過擬合的模型一般沒什麼用,後續實驗過程,就能看到它的強大了。
12.3.2核函數的做用
還記得式(12.3)中的φ(x)嗎?它就是核函數。下面就來研究一下它對數據作了什麼。你們知道能夠對高維數據降維來提取主要信息,降維的目的就是找到更好的表明特徵。那麼數據能不能升維呢?低維的數據信息有點少,能不能用高維的數據信息來解決低維中很差解決的問題呢?這就是核函數要完成的任務。
假設有兩類數據點,在低維空間中進行分類任務有些麻煩,可是,若是能找到一種變換方法,將低維空間的數據映射到高維空間中,這個問題看起來很容易解決,如圖12-8所示。
圖12-8 核函數做用
如何進行升維呢?先來看一個小例子,瞭解一下核函數的變換過程。須要你們考慮的另一個問題是,若是數據維度大幅提高,對計算的要求天然更苛刻,在以前的求解的過程當中能夠發現,計算時須要考慮全部樣本,所以計算內積十分麻煩,這是否大大增長求解難度呢?
假設有兩個數據點:x=(x1,x2,x3),y=(x1,x2,x3),注意它們都是數據。假設在三維空間中已經不能對它們進行線性劃分,既然提到高維的概念,那就使用一種函數變換,將這些數據映射到更高維的空間,例如映射到九維空間,假設映射函數以下:
F(x)=(x1x1,x2x2,x1x3,x2x1,x2x2,x2x3,x3x1,x3x2,x3x3)(12.26)
已知數據點x=(1,2,3),y=(4,5,6),代入式(12.26)可得F(x)=(1,2,3,2,4,5,3,6,9,),F(y)=(16,40,24,20,25,36,24,30,36)。求解過程當中主要計算量就在內積運算上,則 。
這個計算看着很簡單,可是,當數據樣本不少而且數據維度也很大的時候,就會很是麻煩,由於要考慮全部數據樣本點兩兩之間的內積。那麼,能不能巧妙點解決這個問題呢?咱們試着先在低維空間中進行內積計算,再把結果映射到高維當中,獲得的數值居然和在高維中進行內積計算的結果相同,其計算式爲:
由此可得:K(x,y)=(<x,y>)2=<F(x),F(y)> 。
可是,K(x,y)的運算卻比<F(x),F(y)>簡單得多。也就是說,只需在低維空間進行計算,再把結果映射到高維空間中便可。雖然經過核函數變換獲得了更多的特徵信息,可是計算複雜度卻沒有發生本質的改變。這一巧合也成全了支持向量機,使得其能夠處理絕大多數問題,而不受計算複雜度的限制。
一般說將數據投影到高維空間中,在高維上解決低維不可分問題實際只是作了一個假設,真正的計算依舊在低維當中,只須要把結果映射到高維便可。
在實際應用支持向量機的過程當中,常用的核函數是高斯核函數,公式以下:
對於高斯核函數,其自己的數學內容比較複雜,直白些的理解是拿到原始數據後,先計算其兩兩樣本之間的類似程度,而後用距離的度量表示數據的特徵。若是數據很類似,那結果就是1。若是數據相差很大,結果就是0,表示不類似。若是對其進行泰勒展開,能夠發現理論上高斯核函數能夠把數據映射到無限多維。
還有一些核函數也能完成高維數據映射,但現階段通用的仍是高斯核函數,你們在應用過程當中選擇它便可。
若是作了核函數變換,能對結果產生什麼影響呢?構建了一個線性不可分的數據集,如圖12-9(a)所示。若是使用線性核函數(至關於不對數據作任何變換,直接用原始數據來建模),獲得的結果並不盡如人意,實際效果不好。若是保持其餘參數不變,加入高斯核函數進行數據變換,獲得的結果如圖12-9(b)所示,效果發生明顯變化,本來很複雜的數據集就被完美地分開。
圖12-9 核函數的做用
在高斯核函數中,還能夠經過控制參數σ來決定數據變換的複雜程度,這對結果也會產生明顯的影響,接下來開始完成這些實驗,親自動手體驗一下支持向量機中參數對結果的影響。
12.4案例:參數對結果的影響
上一節列舉了支持向量機中的鬆弛因子和核函數對結果的影響,本節就來實際動手看看其效果如何。首先使用sklearn工具包製做一份簡易數據集,並完成分類任務。
12.4.1SVM基本模型
基於SVM的核心概念以及推導公式,下面完成建模任務,首先導入所需的工具包:
1 %matplotlib inline 2 #爲了在notebook中畫圖展現 3 import numpy as np 4 import matplotlib.pyplot as plt 5 from scipy import stats 6 import seaborn as sns; sns.set()
接下來爲了方便實驗觀察,利用sklearn工具包中的datasets模塊生成一些數據集,固然你們也可使用本身手頭的數據:
1 #隨機來點數據 2 #其中 cluster_std是數據的離散程度 3 from sklearn.datasets.samples_generator import make_blobs 4 X, y = make_blobs(n_samples=50, centers=2, 5 random_state=0, cluster_std=0.60) 6 plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
d:\tools\python37\lib\site-packages\sklearn\utils\deprecation.py:144: FutureWarning: The sklearn.datasets.samples_generator module is deprecated
in version 0.22 and will be removed in version 0.24. The corresponding classes / functions s
hould instead be imported from sklearn.datasets. Anything that cannot be imported from sklearn.datasets is now part of the private API. warnings.warn(message, FutureWarning)
Datasets.samples_generator是sklearn工具包中的數據生成器函數,能夠定義生成數據的結構。Make_blobs是其中另外一個函數,使用該函數時,只需指定樣本數目n_samples、數據簇的個數centers、隨機狀態random_state以及其離散程度cluster_std等。
上述代碼一共構建50個數據點,要進行二分類任務。從輸出結果能夠明顯看出,這兩類數據點很是容易分開,在中間隨意畫一條分割線就能完成任務。
1 #隨便的畫幾條分割線,哪一個好來這? 2 xfit = np.linspace(-1, 3.5) 3 plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn') 4 5 for m, b in [(1, 0.65), (0.5, 1.6), (-0.2, 2.9)]: 6 plt.plot(xfit, m * xfit + b, '-k') 7 #限制一下X的取值範圍 8 plt.xlim(-1, 3.5);
上述輸出結果繪製了3條不一樣的決策邊界,均可以把兩類數據點徹底分開,可是哪一個更好呢?這就回到支持向量機最基本的問題——如何找到最合適的決策邊界,你們已經知道,確定要找最寬的邊界,能夠把其邊界距離繪製出來:
1 xfit = np.linspace(-1, 3.5) 2 plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn') 3 4 for m, b, d in [(1, 0.65, 0.33), (0.5, 1.6, 0.55), (-0.2, 2.9, 0.2)]: 5 yfit = m * xfit + b 6 plt.plot(xfit, yfit, '-k') 7 plt.fill_between(xfit, yfit - d, yfit + d, edgecolor='none', 8 color='#AAAAAA', alpha=0.4) 9 10 plt.xlim(-1, 3.5);
從上述輸出結果能夠發現,不一樣決策方程的寬窄差異很大,此時就輪到支持向量機登場了,來看看它是怎麼決定最寬的邊界的:
1 #分類任務 2 from sklearn.svm import SVC 3 #線性核函數 至關於不對數據進行變換 4 model = SVC(kernel='linear') 5 model.fit(X, y)
SVC(C=1.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0, decision_function_shape='ovr', degree=3, gamma='scale', kernel='linear', max_iter=-1, probability=False, random_state=None, shrinking=True, tol=0.001, verbose=False)
選擇核函數是線性函數,其餘參數暫用默認值,藉助工具包可以很輕鬆地創建一個支持向量機模型,下面來繪製它的結果:
1 #繪圖函數 2 def plot_svc_decision_function(model, ax=None, plot_support=True): 3 4 if ax is None: 5 ax = plt.gca() 6 xlim = ax.get_xlim() 7 ylim = ax.get_ylim() 8 9 # 用SVM自帶的decision_function函數來繪製 10 x = np.linspace(xlim[0], xlim[1], 30) 11 y = np.linspace(ylim[0], ylim[1], 30) 12 Y, X = np.meshgrid(y, x) 13 xy = np.vstack([X.ravel(), Y.ravel()]).T 14 P = model.decision_function(xy).reshape(X.shape) 15 16 # 繪製決策邊界 17 ax.contour(X, Y, P, colors='k', 18 levels=[-1, 0, 1], alpha=0.5, 19 linestyles=['--', '-', '--']) 20 21 # 繪製支持向量 22 if plot_support: 23 ax.scatter(model.support_vectors_[:, 0], 24 model.support_vectors_[:, 1], 25 s=300, linewidth=1, alpha=0.2); 26 ax.set_xlim(xlim) 27 ax.set_ylim(ylim) 28 29 plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn') 30 plot_svc_decision_function(model)
上述代碼生成了SVM建模結果,預料之中,這裏選到一個最寬的決策邊界。其中被圈起來的就是支持向量,在sklearn中它們存儲在 support_vectors_屬性下,可使用下面代碼進行查看:
1 model.support_vectors_
array([[0.44359863, 3.11530945], [2.33812285, 3.43116792], [2.06156753, 1.96918596]])
接下來分別使用60個和120個數據點進行實驗,保持支持向量不變,看看決策邊界會不會發生變化:
1 def plot_svm(N=10, ax=None): 2 X, y = make_blobs(n_samples=200, centers=2, 3 random_state=0, cluster_std=0.60) 4 X = X[:N] 5 y = y[:N] 6 model = SVC(kernel='linear', C=1E10) 7 model.fit(X, y) 8 9 ax = ax or plt.gca() 10 ax.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn') 11 ax.set_xlim(-1, 4) 12 ax.set_ylim(-1, 6) 13 plot_svc_decision_function(model, ax) 14 # 分別對不一樣的數據點進行繪製 15 fig, ax = plt.subplots(1, 2, figsize=(16, 6)) 16 fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1) 17 for axi, N in zip(ax, [60, 120]): 18 plot_svm(N, axi) 19 axi.set_title('N = {0}'.format(N))
從上述輸出結果能夠看出,左邊是60個數據點的建模結果,右邊的是120個數據點的建模結果。它與原理推導時獲得的答案一致,只有支持向量會對結果產生影響。
12.4.2核函數變換
接下來感覺一下核函數的效果,一樣使用datasets.samples_generator產生模擬數據,可是此次使用make_circles函數,隨機產生環形數據集,加大了遊戲難度,先來看看線性SVM能不能解決:
1 from sklearn.datasets.samples_generator import make_circles 2 # 繪製另一種數據集 3 X, y = make_circles(100, factor=.1, noise=.1) 4 #看看這回線性和函數能解決嘛 5 clf = SVC(kernel='linear').fit(X, y) 6 7 plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn') 8 plot_svc_decision_function(clf, plot_support=False);
上圖爲線性SVM在解決環繞形數據集時獲得的效果,有些差強人意。雖然在二維特徵空間中作得很差,但若是映射到高維空間中,效果會不會好一些呢?能夠想象一下三維空間中的效果:
1 #加入了新的維度r 2 from mpl_toolkits import mplot3d 3 r = np.exp(-(X ** 2).sum(1)) 4 # 能夠想象一下在三維中把環形數據集進行上下拉伸 5 def plot_3D(elev=30, azim=30, X=X, y=y): 6 ax = plt.subplot(projection='3d') 7 ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='autumn') 8 ax.view_init(elev=elev, azim=azim) 9 ax.set_xlabel('x') 10 ax.set_ylabel('y') 11 ax.set_zlabel('r') 12 13 plot_3D(elev=45, azim=45, X=X, y=y)
上述代碼自定義了一個新的維度,此時兩類數據點很容易分開,這就是核函數變換的基本思想,下面使用高斯核函數完成一樣的任務:
1 #加入高斯核函數 2 clf = SVC(kernel='rbf') 3 clf.fit(X, y)
SVC(C=1.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0, decision_function_shape='ovr', degree=3, gamma='scale', kernel='rbf', max_iter=-1, probability=False, random_state=None, shrinking=True, tol=0.001, verbose=False)
1 #這回厲害了! 2 plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn') 3 plot_svc_decision_function(clf) 4 plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], 5 s=300, lw=1, facecolors='none');
核函數參數選擇經常使用的高斯核函數’rbf’,其餘參數保持不變。從上述輸出結果能夠看出,劃分的結果發生了巨大的變化,看起來很輕鬆地解決了線性不可分問題,這就是支持向量機的強大之處。
12.4.3SVM參數選擇
(1)鬆弛因子的選擇。講解支持向量機的原理時,曾提到過擬合問題,也就是軟間隔(Soft Margin),其中鬆弛因子C用於控制標準有多嚴格。當C趨近於無窮大時,這意味着分類要求嚴格,不能有錯誤;當C趨近於無窮小時,意味着能夠容忍一些錯誤。下面經過數據集實例驗證其參數的大小對最終支持向量機模型的影響。首先生成一個模擬數據集,代碼以下:
1 # 這份數據集中cluster_std稍微大一些,這樣才能體現出軟間隔的做用 2 X, y = make_blobs(n_samples=100, centers=2, 3 random_state=0, cluster_std=0.8) 4 plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
進一步加大遊戲難度,來看一下鬆弛因子C能夠發揮的做用:
1 #加大遊戲難度的數據集 2 X, y = make_blobs(n_samples=100, centers=2, 3 random_state=0, cluster_std=0.8) 4 5 fig, ax = plt.subplots(1, 2, figsize=(16, 6)) 6 fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1) 7 # 選擇兩個C參數來進行對別實驗,分別爲10和0.1 8 for axi, C in zip(ax, [10.0, 0.1]): 9 model = SVC(kernel='linear', C=C).fit(X, y) 10 axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn') 11 plot_svc_decision_function(model, axi) 12 axi.scatter(model.support_vectors_[:, 0], 13 model.support_vectors_[:, 1], 14 s=300, lw=1, facecolors='none'); 15 axi.set_title('C = {0:.1f}'.format(C), size=14)
上述代碼設定鬆弛因子控制參數C的值分別爲10和0.1,其餘參數保持不變,使用同一數據集訓練支持向量機模型。上面左圖對應鬆弛因子控制參數C爲10時的建模結果,至關於對分類的要求比較嚴格,以分類對爲前提,再去找最寬的決策邊界,獲得的結果雖然可以徹底分類正確,可是邊界實在是不夠寬。右圖對應鬆弛因子控制參數C爲0.1時的建模結果,此時並不要求分類徹底正確,有點錯誤也是能夠容忍的,此時獲得的結果中,雖然有些數據點「越界」了,可是總體仍是很寬的。
對比不一樣鬆弛因子控制參數C對結果的影響後,結果的差別仍是很大,因此在實際建模的時候,須要好好把控C值的選擇,能夠說鬆弛因子是支持向量機中的必調參數,固然具體的數值須要經過實驗判斷。
(2)gamma參數的選擇。高斯核函數能夠經過改變σ值進行不一樣的數據變換,在sklearn工具包中,σ對應着gamma參數值,以控制模型的複雜程度。Gamma值越大,模型複雜度越高;而gamma值越小,則模型複雜度越低。先進行實驗,看一下其具體效果:
1 X, y = make_blobs(n_samples=100, centers=2, 2 random_state=0, cluster_std=1.1) 3 4 fig, ax = plt.subplots(1, 2, figsize=(16, 6)) 5 fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1) 6 # 選擇不一樣的gamma值來觀察建模效果 7 for axi, gamma in zip(ax, [10.0, 0.1]): 8 model = SVC(kernel='rbf', gamma=gamma).fit(X, y) 9 axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn') 10 plot_svc_decision_function(model, axi) 11 axi.scatter(model.support_vectors_[:, 0], 12 model.support_vectors_[:, 1], 13 s=300, lw=1, facecolors='none'); 14 axi.set_title('gamma = {0:.1f}'.format(gamma), size=14)
上述代碼設置gamma值分別爲10和0.1,以觀察建模的結果。上面左圖爲gamma取10時的輸出結果,訓練後獲得的模型很是複雜,雖然能夠把全部的數據點都分類正確,可是一看其決策邊界,就知道這樣的模型過擬合風險很是大。右圖爲gamma取0.1時的輸出結果,模型並不複雜,有些數據樣本分類結果出現錯誤,可是總體決策邊界比較平穩。那麼,究竟哪一個參數好呢?通常狀況下,須要經過交叉驗證進行對比分析,可是,在機器學習任務中,仍是但願模型別太複雜,泛化能力強一些,才能更好地處理實際任務,所以,相對而言,右圖中模型更有價值。
12.4.4SVM人臉識別實例
Sklearn工具包提供了豐富的實例,其中有一個比較有意思,就是人臉識別任務,但當拿到一張人臉圖像後,看一下到底是誰,屬於圖像分類任務。
第①步:數據讀入。數據源可使用sklearn庫直接下載,它還提供不少實驗用的數據集,感興趣的讀者能夠參考一下其API文檔,代碼以下:
1 #讀取數據集 2 from sklearn.datasets import fetch_lfw_people 3 faces = fetch_lfw_people(min_faces_per_person=60) 4 #看一下數據的規模 5 print(faces.target_names) 6 print(faces.images.shape)
#Downloading LFW metadata: https://ndownloader.figshare.com/files/5976012 #Downloading LFW metadata: https://ndownloader.figshare.com/files/5976009 #Downloading LFW metadata: https://ndownloader.figshare.com/files/5976006 #Downloading LFW data (~200MB): https://ndownloader.figshare.com/files/5976015
比較大,可行下載文件到C:\Users\用戶名\scikit_learn_data\lfw_home\----------邀月注
['Ariel Sharon' 'Colin Powell' 'Donald Rumsfeld' 'George W Bush' 'Gerhard Schroeder' 'Hugo Chavez' 'Junichiro Koizumi' 'Tony Blair'] (1348, 62, 47)
爲了使得數據集中每個人的樣本都不至於太少,限制了每一個人的樣本數至少爲60,所以獲得1348張圖像數據,每一個圖像的矩陣大小爲[62,47]。
第②步:數據降維及劃分。對於圖像數據來講,它是由像素點組成的,若是直接使用原始圖片數據,特徵個數就顯得太多,訓練模型的時候很是耗時,先用PCA降維(後續章節會涉及PCA原理),而後再執行SVM,代碼以下:
1 from sklearn.svm import SVC 2 from sklearn.decomposition import PCA 3 from sklearn.pipeline import make_pipeline 4 5 #降維到150維 6 pca = PCA(n_components=150, whiten=True, random_state=42) 7 svc = SVC(kernel='rbf', class_weight='balanced') 8 #先降維而後再SVM 9 model = make_pipeline(pca, svc)
數據降到150維就差很少了,先把基本模型實例化,接下來能夠進行實際建模,由於還要進行模型評估,須要先對數據集進行劃分:
1 from sklearn.model_selection import train_test_split 2 Xtrain, Xtest, ytrain, ytest = train_test_split(faces.data, faces.target, 3 random_state=40)
第③步:SVM模型訓練。SVM中有兩個很是重要的參數——C和gamma,這個任務比較簡單,能夠用網絡搜索尋找比較合適的參數,這裏只是舉例,你們在實際應用的時候,應當更仔細地選擇參數空間:
1 from sklearn.model_selection import GridSearchCV 2 param_grid = {'svc__C': [1, 5, 10], 3 'svc__gamma': [0.0001, 0.0005, 0.001]} 4 grid = GridSearchCV(model, param_grid) 5 6 %time grid.fit(Xtrain, ytrain) 7 print(grid.best_params_)
Wall time: 28.2 s
{'svc__C': 5, 'svc__gamma': 0.001}
第④步:結果預測。模型已經創建完成,來看看實際應用效果:
1 model = grid.best_estimator_ 2 yfit = model.predict(Xtest) 3 yfit.shape
(337,)
若是想獲得各項具體評估指標,在sklearn工具包中有一個很是方便的函數,能夠一次將它們全搞定:
1 from sklearn.metrics import classification_report 2 print(classification_report(ytest, yfit, 3 target_names=faces.target_names))
precision recall f1-score support Ariel Sharon 0.50 0.50 0.50 16 Colin Powell 0.70 0.81 0.75 54 Donald Rumsfeld 0.83 0.85 0.84 34 George W Bush 0.94 0.88 0.91 136 Gerhard Schroeder 0.70 0.85 0.77 27 Hugo Chavez 0.81 0.72 0.76 18 Junichiro Koizumi 0.87 0.87 0.87 15 Tony Blair 0.85 0.76 0.80 37 accuracy 0.82 337 macro avg 0.77 0.78 0.77 337 weighted avg 0.83 0.82 0.82 337
只須要把預測結果和標籤值傳進來便可,任務中每個人就至關於一個類別,F1指標尚未用過,它是將精度和召回率綜合在一塊兒的結果,公式以下。
F1=2×精度召回率/(精度+召回率)
其中,精度(precision)=正確預測的個數(TP)/被預測正確的個數(TP+FP),召回率(recall)=正確預測的個數(TP)/預測個數(TP+FN)。
總體效果看起來還能夠,若是想具體分析哪些人容易被錯認成別的人,使用混淆矩陣觀察會更方便,一樣,sklearn工具包中已經提供好方法。
1 from sklearn.metrics import confusion_matrix 2 mat = confusion_matrix(ytest, yfit) 3 sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False, 4 xticklabels=faces.target_names, 5 yticklabels=faces.target_names) 6 plt.xlabel('true label') 7 plt.ylabel('predicted label');
對角線上的數值表示將一我的正確預測成他本身的樣本個數。例如第一列是Ariel Sharon,測試集中包含他24個圖像數據,其中17個正確分類,3個被分類成Colin Powell,1個被分類成Donald Rumsfeld,1個被分類成George W Bush,2個被分類成Gerhard Schroeder。經過混淆矩陣能夠更清晰地觀察哪些樣本類別容易混淆在一塊兒。
本章小結:
本章首先講解了支持向量機算法的基本工做原理,你們在學習過程當中,應首先明確目標函數及其做用,接下來就是優化求解的過程。支持向量機不只能夠進行線性決策,也能夠藉助核函數完成難度更大的數據集劃分工做。接下來,經過實驗案例對比分析了支持向量機中最常調節的鬆弛因子C和參數gamma,對於核函數的選擇,最經常使用的就是高斯核函數,但必定要把過擬合問題考慮進來,即便訓練集中作得再好,也可能沒有實際的用武之地,因此參數選擇仍是比較重要的。
第12章完。
該書資源下載,請至異步社區:https://www.epubit.com