機器學習經典演算法之SVM
阿新 • • 發佈:2019-06-25
SVM 的英文叫 Support Vector Machine,中文名為支援向量機。它是常見的一種分類方法,在機器學習中,SVM 是有監督的學習模型。
什麼是有監督的學習模型呢?它指的是我們需要事先對資料打上分類標籤,這樣機器就知道這個資料屬於哪個分類。同樣無監督學習,就是資料沒有被打上分類標籤,這可能是因為我們不具備先驗的知識,或者打標籤的成本很高。所以我們需要機器代我們部分完成這個工作,比如將資料進行聚類,方便後續人工對每個類進行分析。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)。
四、 如何用 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 分類器,並計算這個分類器的準確率。 首先載入資料並對資料做部分的探索:
接下來,我們就要對資料進行清洗了。
執行結果中,你能看到 32 個欄位裡,id 是沒有實際含義的,可以去掉。diagnosis 欄位的取值為 B 或者 M,我們可以用 0 和 1 來替代。另外其餘的 30 個欄位,其實可以分成三組欄位,下劃線後面的 mean、se 和 worst 代表了每組欄位不同的度量方式,分別是平均值、標準差和最大值。
/*請尊重作者勞動成果,轉載請標明原文連結:*/
/* https://www.cnblogs.com/jpcflyer/p/11082443.html * /
一、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* 進行求解,從而得到最大分類間隔的超平面。 二、 用 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 較大時,訓練和測試的時間會比較慢。 三、 如何在 sklearn 中使用 SVM 在 Python 的 sklearn 工具包中有 SVM 演算法,首先需要引用工具包:
1 from sklearn import 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
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%,說明訓練結果還不錯。 搜尋關注微信公眾號“程式設計師姜小白”,獲取更新精彩內容哦。 &nbs