機器學習經典算法之SVM

SVM 的英文叫 Support Vector Machine,中文名爲支持向量機。它是常見的一種分類方法,在機器學習中,SVM 是有監督的學習模型。
什麼是有監督的學習模型呢?它指的是咱們須要事先對數據打上分類標籤,這樣機器就知道這個數據屬於哪一個分類。一樣無監督學習,就是數據沒有被打上分類標籤,這多是由於咱們不具有先驗的知識,或者打標籤的成本很高。因此咱們須要機器代咱們部分完成這個工做,好比將數據進行聚類,方便後續人工對每一個類進行分析。SVM 做爲有監督的學習模型,一般能夠幫咱們模式識別、分類以及迴歸分析。

/*請尊重做者勞動成果,轉載請標明原文連接:*/html

/* https://www.cnblogs.com/jpcflyer/p/11082443.html * /git

 

1、SVM 的工做原理
用 SVM 計算的過程就是幫咱們找到一個超平面,可以將樣本區分的過程,這個超平面就是咱們的 SVM 分類器。
好比下圖所示的直線 A、直線 B 和直線 C,究竟哪一種纔是更好的劃分呢?
很明顯圖中的直線 B 更靠近藍色球,可是在真實環境下,球再多一些的話,藍色球可能就被劃分到了直線 B 的右側,被認爲是紅色球。一樣直線 A 更靠近紅色球,在真實環境下,若是紅色球再多一些,也可能會被誤認爲是藍色球。因此相比於直線 A 和直線 B,直線 C 的劃分更優,由於它的魯棒性更強。
那怎樣才能尋找到直線 C 這個更優的答案呢?這裏,咱們引入一個 SVM 特有的概念: 分類間隔
 
實際上,咱們的分類環境不是在二維平面中的,而是在多維空間中,這樣直線 C 就變成了決策面 C。
在保證決策面不變,且分類不產生錯誤的狀況下,咱們能夠移動決策面 C,直到產生兩個極限的位置:如圖中的決策面 A 和決策面 B。極限的位置是指,若是越過了這個位置,就會產生分類錯誤。這樣的話,兩個極限位置 A 和 B 之間的分界線 C 就是最優決策面。極限位置到最優決策面 C 之間的距離,就是「分類間隔」,英文叫作 margin。
 
若是咱們轉動這個最優決策面,你會發現可能存在多個最優決策面,它們都能把數據集正確分開,這些最優決策面的分類間隔多是不一樣的,而那個擁有「最大間隔」(max margin)的決策面就是 SVM 要找的最優解。
 
點到超平面的距離公式
在上面這個例子中,若是咱們把紅藍兩種顏色的球放到一個三維空間裏,你發現決策面就變成了一個平面。這裏咱們能夠用線性函數來表示,若是在一維空間裏就表示一個點,在二維空間裏表示一條直線,在三維空間中表明一個平面,固然空間維數還能夠更多,這樣咱們給這個線性函數起個名稱叫作「超平面」。超平面的數學表達能夠寫成:
在這個公式裏,w、x 是 n 維空間裏的向量,其中 x 是函數變量;w 是法向量。法向量這裏指的是垂直於平面的直線所表示的向量,它決定了超平面的方向。
SVM 就是幫咱們找到一個超平面 ,這個超平面能將不一樣的樣本劃分開,同時使得樣本集中的點到這個分類超平面的最小距離(即分類間隔)最大化。
在這個過程當中, 支持向量 就是離 分類超平面 最近的樣本點,實際上若是肯定了支持向量也就肯定了這個超平面。因此支持向量決定了分類間隔究竟是多少,而在最大間隔之外的樣本點,其實對分類都沒有意義。
因此說, SVM 就是求解最大分類間隔的過程,咱們還須要對分類間隔的大小進行定義。
 
首先,咱們定義某類樣本集到超平面的距離是這個樣本集合內的樣本到超平面的最短距離。咱們用 di 表明點 xi 到超平面 wxi+b=0 的歐氏距離。所以咱們要求 di 的最小值,用它來表明這個樣本到超平面的最短距離。di 能夠用公式計算得出:
其中||w||爲超平面的範數,di 的公式能夠用解析幾何知識進行推導,這裏不作解釋。
 
最大間隔的優化模型
咱們的目標就是找出全部分類間隔中最大的那個值對應的超平面。在數學上,這是一個凸優化問題(凸優化就是關於求凸集中的凸函數最小化的問題,這裏不具體展開)。經過凸優化問題,最後能夠求出最優的 w 和 b,也就是咱們想要找的最優超平面。中間求解的過程會用到拉格朗日乘子,和 KKT(Karush-Kuhn-Tucker)條件。數學公式比較多,這裏不進行展開。
 
硬間隔、軟間隔和非線性 SVM
假如數據是徹底的線性可分的,那麼學習到的模型能夠稱爲硬間隔支持向量機。 換個說法,硬間隔指的就是徹底分類準確,不能存在分類錯誤的狀況。軟間隔,就是容許必定量的樣本分類錯誤。
咱們知道,實際工做中的數據沒有那麼「乾淨」,或多或少都會存在一些噪點。因此線性可分是個理想狀況。這時,咱們須要使用到軟間隔 SVM(近似線性可分),好比下面這種狀況:
 
 
 
另外還存在一種狀況,就是非線性支持向量機。
好比下面的樣本集就是個非線性的數據。圖中的兩類數據,分別分佈爲兩個圓圈的形狀。那麼這種狀況下,不管是多高級的分類器,只要映射函數是線性的,就無法處理,SVM 也處理不了。這時,咱們須要引入一個新的概念: 核函數。它能夠將樣本從原始空間映射到一個更高維的特質空間中,使得樣本在新的空間中線性可分 。這樣咱們就可使用原來的推導來進行計算,只是全部的推導是在新的空間,而不是在原來的空間中進行。
 
因此在非線性 SVM 中,核函數的選擇就是影響 SVM 最大的變量。最經常使用的核函數有線性核、多項式核、高斯核、拉普拉斯核、sigmoid 核,或者是這些核函數的組合。這些函數的區別在於映射方式的不一樣。經過這些核函數,咱們就能夠把樣本空間投射到新的高維空間中。
固然軟間隔和核函數的提出,都是爲了方便咱們對上面超平面公式中的 w* 和 b* 進行求解,從而獲得最大分類間隔的超平面。
 
2、 用 SVM 如何解決多分類問題
SVM 自己是一個二值分類器,最初是爲二分類問題設計的,也就是回答 Yes 或者是 No。而實際上咱們要解決的問題,多是多分類的狀況,好比對文本進行分類,或者對圖像進行識別。
針對這種狀況,咱們能夠將多個二分類器組合起來造成一個多分類器,常見的方法有「一對多法」和「一對一法」兩種。
 
1. 一對多法
假設咱們要把物體分紅 A、B、C、D 四種分類,那麼咱們能夠先把其中的一類做爲分類 1,其餘類統一歸爲分類 2。這樣咱們能夠構造 4 種 SVM,分別爲如下的狀況:
(1)樣本 A 做爲正集,B,C,D 做爲負集;
(2)樣本 B 做爲正集,A,C,D 做爲負集;
(3)樣本 C 做爲正集,A,B,D 做爲負集;
(4)樣本 D 做爲正集,A,B,C 做爲負集。
這種方法,針對 K 個分類,須要訓練 K 個分類器,分類速度較快,但訓練速度較慢,由於每一個分類器都須要對所有樣本進行訓練,並且負樣本數量遠大於正樣本數量,會形成樣本不對稱的狀況,並且當增長新的分類,好比第 K+1 類時,須要從新對分類器進行構造。
 
2. 一對一法
一對一法的初衷是想在訓練的時候更加靈活。咱們能夠在任意兩類樣本之間構造一個 SVM,這樣針對 K 類的樣本,就會有 C(k,2) 類分類器。
好比咱們想要劃分 A、B、C 三個類,能夠構造 3 個分類器:
(1)分類器 1:A、B;
(2)分類器 2:A、C;
(3)分類器 3:B、C。    
當對一個未知樣本進行分類時,每個分類器都會有一個分類結果,即爲 1 票,最終得票最多的類別就是整個未知樣本的類別。
這樣作的好處是,若是新增一類,不須要從新訓練全部的 SVM,只須要訓練和新增這一類樣本的分類器。並且這種方式在訓練單個 SVM 模型的時候,訓練速度快。
但這種方法的不足在於,分類器的個數與 K 的平方成正比,因此當 K 較大時,訓練和測試的時間會比較慢。
 
3、 如何在 sklearn 中使用 SVM
在 Python 的 sklearn 工具包中有 SVM 算法,首先須要引用工具包:
1 from sklearn import svm
SVM 既能夠作迴歸,也能夠作分類器。
當用 SVM 作迴歸的時候,咱們可使用 SVR 或 LinearSVR。SVR 的英文是 Support Vector Regression。這篇文章只講分類,這裏只是簡單地提一下。
當作分類器的時候,咱們使用的是 SVC 或者 LinearSVC。SVC 的英文是 Support Vector Classification。
我簡單說一下這二者以前的差異。
從名字上你能看出 LinearSVC 是個線性分類器,用於處理線性可分的數據,只能使用線性核函數。上一節,我講到 SVM 是經過核函數將樣本從原始空間映射到一個更高維的特質空間中,這樣就使得樣本在新的空間中線性可分。
若是是針對非線性的數據,須要用到 SVC。在 SVC 中,咱們既可使用到線性核函數(進行線性劃分),也能使用高維的核函數(進行非線性劃分)。
 
如何建立一個 SVM 分類器呢?
咱們首先使用 SVC 的構造函數:model = svm.SVC(kernel=‘rbf’, C=1.0, gamma=‘auto’),這裏有三個重要的參數 kernel、C 和 gamma。
kernel 表明核函數的選擇,它有四種選擇,只不過默認是 rbf,即高斯核函數。
linear:線性核函數
poly:多項式核函數
rbf:高斯核函數(默認)
sigmoid:sigmoid 核函數
這四種函數表明不一樣的映射方式,你可能會問,在實際工做中,如何選擇這 4 種核函數呢?我來給你解釋一下:
線性核函數,是在數據線性可分的狀況下使用的,運算速度快,效果好。不足在於它不能處理線性不可分的數據。
多項式核函數能夠將數據從低維空間映射到高維空間,但參數比較多,計算量大。
高斯核函數一樣能夠將樣本映射到高維空間,但相比於多項式核函數來講所需的參數比較少,一般性能不錯,因此是默認使用的核函數。
瞭解深度學習的同窗應該知道 sigmoid 常常用在神經網絡的映射中。所以當選用 sigmoid 核函數時,SVM 實現的是多層神經網絡。
 
上面介紹的 4 種核函數,除了第一種線性核函數外,其他 3 種均可以處理線性不可分的數據。
參數 C 表明目標函數的懲罰係數,懲罰係數指的是分錯樣本時的懲罰程度,默認狀況下爲 1.0。當 C 越大的時候,分類器的準確性越高,但一樣容錯率會越低,泛化能力會變差。相反,C 越小,泛化能力越強,可是準確性會下降。
參數 gamma 表明核函數的係數,默認爲樣本特徵數的倒數,即 gamma = 1 / n_features。
在建立 SVM 分類器以後,就能夠輸入訓練集對它進行訓練。咱們使用 model.fit(train_X,train_y),傳入訓練集中的特徵值矩陣 train_X 和分類標識 train_y。特徵值矩陣就是咱們在特徵選擇後抽取的特徵值矩陣(固然你也能夠用所有數據做爲特徵值矩陣);分類標識就是人工事先針對每一個樣本標識的分類結果。這樣模型會自動進行分類器的訓練。咱們可使用 prediction=model.predict(test_X) 來對結果進行預測,傳入測試集中的樣本特徵矩陣 test_X,能夠獲得測試集的預測分類結果 prediction。
一樣咱們也能夠建立線性 SVM 分類器,使用 model=svm.LinearSVC()。在 LinearSVC 中沒有 kernel 這個參數,限制咱們只能使用線性核函數。因爲 LinearSVC 對線性分類作了優化,對於數據量大的線性可分問題,使用 LinearSVC 的效率要高於 SVC。
若是你不知道數據集是否爲線性,能夠直接使用 SVC 類建立 SVM 分類器。
在訓練和預測中,LinearSVC 和 SVC 同樣,都是使用 model.fit(train_X,train_y) 和 model.predict(test_X)。
 
4、 如何用 SVM 進行乳腺癌檢測
在瞭解瞭如何建立和使用 SVM 分類器後,咱們來看一個實際的項目,數據集來自美國威斯康星州的乳腺癌診斷數據集, 點擊這裏進行下載。
醫療人員採集了患者乳腺腫塊通過細針穿刺 (FNA) 後的數字化圖像,而且對這些數字圖像進行了特徵提取,這些特徵能夠描述圖像中的細胞核呈現。腫瘤能夠分紅良性和惡性。部分數據截屏以下所示:
數據表一共包括了 32 個字段,表明的含義以下:
上面的表格中,mean 表明平均值,se 表明標準差,worst 表明最大值(3 個最大值的平均值)。每張圖像都計算了相應的特徵,得出了這 30 個特徵值(不包括 ID 字段和分類標識結果字段 diagnosis),其實是 10 個特徵值(radius、texture、perimeter、area、smoothness、compactness、concavity、concave points、symmetry 和 fractal_dimension_mean)的 3 個維度,平均、標準差和最大值。這些特徵值都保留了 4 位數字。字段中沒有缺失的值。在 569 個患者中,一共有 357 個是良性,212 個是惡性。
好了,咱們的目標是生成一個乳腺癌診斷的 SVM 分類器,並計算這個分類器的準確率。 首先加載數據並對數據作部分的探索:
1 # 加載數據集,你須要把數據放到目錄中
1 data = pd.read_csv("./data.csv")
1 # 數據探索
2 # 由於數據集中列比較多,咱們須要把 dataframe 中的列所有顯示出來
3 pd.set_option('display.max_columns', None) 4 print(data.columns) 5 print(data.head(5)) 6 print(data.describe())
這是部分的運行結果,完整結果你能夠本身跑一下。
 1 Index(['id', 'diagnosis', 'radius_mean', 'texture_mean', 'perimeter_mean',  2        'area_mean', 'smoothness_mean', 'compactness_mean', 'concavity_mean',  3        'concave points_mean', 'symmetry_mean', 'fractal_dimension_mean',  4        'radius_se', 'texture_se', 'perimeter_se', 'area_se', 'smoothness_se',  5        'compactness_se', 'concavity_se', 'concave points_se', 'symmetry_se',  6        'fractal_dimension_se', 'radius_worst', 'texture_worst',  7        'perimeter_worst', 'area_worst', 'smoothness_worst',  8        'compactness_worst', 'concavity_worst', 'concave points_worst',  9        'symmetry_worst', 'fractal_dimension_worst'], 10       dtype='object') 11  id diagnosis radius_mean texture_mean perimeter_mean area_mean \ 12 0    842302         M        17.99         10.38          122.80     1001.0   
13 1    842517         M        20.57         17.77          132.90     1326.0   
14 2  84300903         M        19.69         21.25          130.00     1203.0   
15 3  84348301         M        11.42         20.38           77.58      386.1   
16 4  84358402         M        20.29         14.34          135.10     1297.0
接下來,咱們就要對數據進行清洗了。
運行結果中,你能看到 32 個字段裏,id 是沒有實際含義的,能夠去掉。diagnosis 字段的取值爲 B 或者 M,咱們能夠用 0 和 1 來替代。另外其他的 30 個字段,其實能夠分紅三組字段,下劃線後面的 mean、se 和 worst 表明了每組字段不一樣的度量方式,分別是平均值、標準差和最大值。
1 # 將特徵字段分紅 3 組
2 features_mean= list(data.columns[2:12]) 3 features_se= list(data.columns[12:22]) 4 features_worst=list(data.columns[22:32]) 5 # 數據清洗
6 # ID 列沒有用,刪除該列
7 data.drop("id",axis=1,inplace=True) 8 # 將 B 良性替換爲 0,M 惡性替換爲 1
9 data['diagnosis']=data['diagnosis'].map({'M':1,'B':0})
而後咱們要作特徵字段的篩選,首先須要觀察下 features_mean 各變量之間的關係,這裏咱們能夠用 DataFrame 的 corr() 函數,而後用熱力圖幫咱們可視化呈現。一樣,咱們也會看總體良性、惡性腫瘤的診斷狀況。
1 # 將腫瘤診斷結果可視化
2 sns.countplot(data['diagnosis'],label="Count") 3 plt.show() 4 # 用熱力圖呈現 features_mean 字段之間的相關性
5 corr = data[features_mean].corr() 6 plt.figure(figsize=(14,14)) 7 # annot=True 顯示每一個方格的數據
8 sns.heatmap(corr, annot=True) 9 plt.show()
這是運行的結果:
熱力圖中對角線上的爲單變量自身的相關係數是 1。顏色越淺表明相關性越大。因此你能看出來 radius_mean、perimeter_mean 和 area_mean 相關性很是大,compactness_mean、concavity_mean、concave_points_mean 這三個字段也是相關的,所以咱們能夠取其中的一個做爲表明。
 
那麼如何進行特徵選擇呢?
特徵選擇的目的是降維,用少許的特徵表明數據的特性,這樣也能夠加強分類器的泛化能力,避免數據過擬合。
咱們能看到 mean、se 和 worst 這三組特徵是對同一組內容的不一樣度量方式,咱們能夠保留 mean 這組特徵,在特徵選擇中忽略掉 se 和 worst。同時咱們能看到 mean 這組特徵中,radius_mean、perimeter_mean、area_mean 這三個屬性相關性大,compactness_mean、daconcavity_mean、concave points_mean 這三個屬性相關性大。咱們分別從這 2 類中選擇 1 個屬性做爲表明,好比 radius_mean 和 compactness_mean。
這樣咱們就能夠把原來的 10 個屬性縮減爲 6 個屬性,代碼以下:
 1 # 特徵選擇
 2 features_remain = ['radius_mean','texture_mean', 'smoothness_mean','compactness_mean','symmetry_mean', 'fractal_dimension_mean']  3 對特徵進行選擇以後,咱們就能夠準備訓練集和測試集:  4 # 抽取 30% 的數據做爲測試集,其他做爲訓練集
 5 train, test = train_test_split(data, test_size = 0.3)# in this our main data is splitted into train and test
 6 # 抽取特徵選擇的數值做爲訓練和測試數據
 7 train_X = train[features_remain]  8 train_y=train['diagnosis']  9 test_X= test[features_remain] 10 test_y =test['diagnosis'] 11 在訓練以前,咱們須要對數據進行規範化,這樣讓數據同在同一個量級上,避免由於維度問題形成數據偏差: 12 # 採用 Z-Score 規範化數據,保證每一個特徵維度的數據均值爲 0,方差爲 1
13 ss = StandardScaler() 14 train_X = ss.fit_transform(train_X) 15 test_X = ss.transform(test_X) 16 最後咱們可讓 SVM 作訓練和預測了: 17 # 建立 SVM 分類器
18 model = svm.SVC() 19 # 用訓練集作訓練
20 model.fit(train_X,train_y) 21 # 用測試集作預測
22 prediction=model.predict(test_X) 23 print('準確率: ', metrics.accuracy_score(prediction,test_y))
運行結果:
1 準確率:  0.9181286549707602
準確率大於 90%,說明訓練結果還不錯。
 
搜索關注微信公衆號「程序員姜小白」,獲取更新精彩內容哦。
相關文章
相關標籤/搜索