《Python數據分析與機器學習實戰-唐宇迪》讀書筆記第12章--支持向量機

python數據分析個人學習讀書筆記-目錄索引

第12章支持向量機

   在機器學習中,支持向量機(Support Vector Machine,SVM)是最經典的算法之一,應用領域也非常廣,其效果自然也是很厲害的。本章對支持向量機算法進行解讀,詳細分析其每一步流程及其參數對結果的影響。

 

12.1支持向量機工作原理

   前面已經給大家講解了一些機器學習算法,有沒有發現其中的一些套路呢?它們都是從一個要解決的問題出發,然後將實際問題轉換成數學問題,接下來優化求解即可。支持向量機涉及的數學內容比較多,下面還是從問題開始一步步解決。

12.1.1支持向量機要解決的問題

 

  現在由一個小例子來引入支持向量機,圖12-1中有兩類數據點,目標就是找到一個最好的決策方程將它們區分開。

 邀月工作室

  圖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個樣本數據點和條件α123=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章完。

python數據分析個人學習讀書筆記-目錄索引

 

該書資源下載,請至異步社區:https://www.epubit.com

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章