1. 場景描述
問題:如何對對下圖的線性可分數據集和線性不可分數據集進行分類?
思路:
- (1)對線性可分數據集找到最優分割超平面
- (2)將線性不可分數據集通過某種方法轉換為線性可分數據集
下面將帶着這兩個問題對支持向量機相關問題進行總結
2. 如何找到最優分割超平面
一般地,當訓練數據集線性可分時,存在無窮個分離超平面可將兩類數據正確分開,比如感知機求得的分離超平面就有無窮多個,為了求得唯一的最優分離超平面,就需要使用間隔最大化的支持向量機
2.1 分類預測確信度
上圖中,有A,B,C三個點,表示三個示例,均在分離超平面的正類一側,點A距分離超平面較遠,若預測點為正類,就比較確信預測是正確的;點C距離超平面較近,若預測該點為正類,就不那么確信;點B介於點A與C之間,預測其為正類的確信度也在A與C之間
通過上面的描述,當訓練集中的所有數據點都距離分隔平面足夠遠時,確信度就越大。在超平面\(w^T X + b = 0\)確定的情況下,可以通過函數間隔和幾何間隔來確定數據點離分割超平面的距離
2.2 函數間隔
對於給定的訓練數據集T和超平面(w,b),定義超平面(w,b)關於樣本\((x_i,y_i)\)的函數間隔為:$$\overline{\gamma{_i}} = y_i(w\bullet{x_i} + b)$$
定義超平面(w,b)關於訓練數據集T的函數間隔為超平面(w,b)關於T中所有樣本點\((x_i,y_i)\)的函數間隔之最小值:$$\overline{\gamma} = \min\limits_{i=1,...,N}\overline{\gamma{_i}}$$
函數間隔可以表示分類預測的正確性及確信度,但是選擇分離超平面時,只有函數間隔卻是不夠的
2.3 幾何間隔
對於給定的訓練數據集T和超平面(w,b),定義超平面(w,b)關於樣本點\((x_i,y_i)\)的幾何間隔為:$$\gamma{_i} = \frac{y_i(w\bullet{x_i} + b)}{||w||}$$
定義超平面(w,b)關於訓練數據集T的幾何間隔為超平面(w,b)關於T中所有樣本點\((x_i,y_i)\)的幾何間隔之最小值:$$\gamma = \min\limits_{i=1,...,N}\gamma_i$$
2.4 函數間隔和幾何間隔之間的關系
從上面函數間隔和幾何間隔的定義,可以得到函數間隔和幾何間隔之間的關系:$$\gamma_i = \frac{\overline{\gamma_i}}{||w||}$$
2.5 硬間隔最大化分離超平面
支持向量機學習的基本想法是找到能夠正確划分訓練數據集並且幾何間隔最大的分離超平面,換句話說也就是不僅將正負實例點分開,而且對最難分的實例點(離超平面最近的點)也有足夠大的確信度將它們分開,硬間隔是與后面說明的軟間隔相對應的
如何求得一個幾何間隔最大化的分離超平面,可以表示為下面的約束優化問題:$$\max\limits_{w,b}\quad\gamma$$
根據上面函數間隔和幾何間隔之間的關系,轉換成下面的同等約束問題:$$\max\limits_{w,b}\quad\frac{\overline{\gamma}}{||w||}$$
由於當w,b按比例變換的的時候函數間隔\(\overline\gamma\)也會呈比例變化,先取\(\overline\gamma= 1\),再由於\(\frac{1}{||w||}\)最大化和最小化\(\frac{1}{2}{||w||}^2\)是等價的,於是得到:$$\min\limits_{w,b}\quad\frac{1}{2}{||w||^2}$$
由此得到分離超平面:$$w^{} \bullet x + b^{} = 0$$
分類決策函數:$$f(x) = sign(w^{} \bullet x + b^{})$$
求解拉格朗日對偶函數:$$L(w,b,a) = \frac{1}{2}{||w||}^2 - \sum_{i=1}^na_i[(y_i(x_iw+b)-1)]----(1)$$
對w求偏導:$$\frac{\partial L}{\partial w} = w - \sum_{i=1}^na_iy_ix_i = 0-----(2)$$
對b求偏導:$$\frac{\partial L}{\partial b} = \sum_{i=1}^na_iy_i = 0-------(3)$$
將(2)(3)帶入(1)得到:$$maxL(a) = -\frac{1}{2}\sum_{i=1}^n\sum_{j=1}^na_ia_jy_iy_jx_ix_j + \sum_{i=1}^na_i$$
2.6 軟間隔最大化分離超平面
對於線性可分的數據集可以直接使用硬間隔最大化超平面進行划分,但對於線性不可分的某些樣本點不能滿足函數間隔大於等於1的約束條件,為了解決這個問題,可以對每個樣本點\((x_i,y_i)\)引進一個松弛變量\(\xi >= 0\),使函數間隔加上松弛變量大於等於1,這樣約束條件變為:$$yi(w\bullet x_i + b) >= 1- \xi_{i}$$
同時,對每個松弛變量\(\xi_{i}\)支付一個代價\(\xi_{i},目標函數由原來的\)\(\frac{1}{2}{||w||}^2\)\(變為\)\(\frac{1}{2}{||w||}^2 + C\sum_{i=1}^n{\xi_i}\)
C為懲罰系數,一般由應用問題決定,C值大時對誤分類的懲罰增大,C值小時對誤分類懲罰小
線性不可分的線性支持向量機的學習問題編程如下凸二次規划問題:$$\min\limits_{w,b,\xi}\quad\frac{1}{2}{||w||^2}+ C\sum_{i=1}^n{\xi_i}$$
由此得到分離超平面:$$w^{} \bullet x + b^{} = 0$$
分類決策函數:$$f(x) = sign(w^{} \bullet x + b^{})$$
拉格朗日對偶函數:
2.7 支持向量和間隔邊界
在線性可分的情況下,訓練數據集的樣本點中與分離超平面距離最近的樣本點的示例稱為支持向量,支持向量是使約束條件成立的點,即$$\quad\ y_i(w\bullet{x_i}+b) - 1 = 0$$或$$yi(w\bullet x_i + b) - (1- \xi_{i}) = 0$$,在\(y_i = +1\)的正例點,支持向量在超平面$$H_1:w^Tx + b = 1$$上,對\(y_i = -1\)的負例點,支持向量在超平面$$H_2:w^T x + b = -1$$上,此時\(H_1\)和\(H_2\)平行,並且沒有實例點落在它們中間,在\(H_1\)和\(H_2\)之間形成一條長帶,分離超平面與它們平行且位於它們中間,\(H_1和H_2\)之間的距離為間隔,間隔依賴於分割超平面的法向量\(w\),等於\(\frac{2}{|w|}\),\(H_1和H_2\)為間隔邊界,如下圖:
在決定分離超平面時只有支持向量起作用,而其他實例點並不起作用。如果移動支持向量將改變所求的解;但是如果在間隔邊界以外移動其他實例點,甚至去掉這些點,則解是不會變的,由於支持向量在確定分離超平面中起着決定性的作用,所以將這種分類稱為支持向量機。支持向量的個數一般很少,所以支持向量機由很少的‘很重要的’訓練樣本確定
3. 如何將線性不可分數據集轉換為線性可分數據集
3.1 數據線性不可分的原因
(1) 數據集本身就是線性不可分隔的
(2) 數據集中存在噪聲,或者人工對數據賦予分類標簽出錯等情況的原因導致數據集線性不可分
3.2 常用方法
將線性不可分數據集轉換為線性可分數據集常用方法:
對於原因(2)
- 需要修正模型,加上懲罰系數C,修正后的模型,可以“容忍”模型錯誤分類的情況,並且通過懲罰系數的約束,使得模型錯誤分類的情況盡可能合理
對於原因(1)
- (1)通過相似函數添加相似特征
- (2)使用核函數(多項式核、高斯RBF核),將原本的低維特征空間映射到一個更高維的特征空間,從而使得數據集線性可分
3.3 核技巧在支持向量機中的應用
注意到在線性支持向量機的對偶問題中,無論是目標函數還是決策函數都只涉及輸入實例與實例之間的內積,在對偶問題的目標函數中的內積\(x_ix_j\)可以用核函數$$K(x_i,x_j) = \phi (x_i)\bullet \phi(x_j)$$代替,此時對偶問題的目標函數成為$$maxL(a) = -\frac{1}{2}\sum_{i=1}^n\sum_{j=1}^na_ia_jy_iy_jK(x_i,x_j) + \sum_{i=1}^na_i$$
同樣,分類決策函數中的內積也可以用核函數代替$$f(x) = sign(\sum_{i=1}^na_i^y_iK(x_i,x)+b^)$$
4 使用sklearn框架訓練svm
- SVM特別適用於小型復雜數據集,samples < 100k
- 硬間隔分類有兩個主要的問題:
- (1) 必須要線性可分
- (2) 對異常值特別敏感,會導致不能很好的泛化或無法找不出硬間隔
- 使用軟間隔分類可以解決硬間隔分類的兩個主要問題,盡可能保存街道寬敞和限制間隔違例(即位於街道之上,甚至在錯誤一邊的實例)之間找到良好的平衡
- 在Sklean的SVM類中,可以通過超參數C來控制這個平衡,C值越小,則街道越寬,但是違例會越多,如果SVM模型過度擬合,可以試試通過降低C來進行正則化
4.1 線性可分LinearSVC類
4.1.1 LinearSVC類重要參數說明
- penalty: string,'l1'or'l2',default='l2'
- loss: string 'hing'or'squared_hinge',default='squared_hinge',hinge為標准的SVM損失函數
- dual: bool,defalut=True,wen n_samples > n_features,dual=False,SVM的原始問題和對偶問題二者解相同
- tol: float,deafult=le-4,用於提前停止標准
- C: float,defult=1.0,為松弛變量的懲罰系數
- multi_class: 默認為ovr,該參數不用修改
- 更多說明應查看源碼
4.1.2 Hinge損失函數
函數max(0,1-t),當t>=1時,函數等於0,如果t<1,其導數為-1
def hinge(x):
if x >=1 :
return 0
else:
return 1-x
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-2,4,20)
y = [hinge(i) for i in x ]
ax = plt.subplot(111)
plt.ylim([-1,2])
ax.plot(x,y,'r-')
plt.text(0.5,1.5,r'f(t) = max(0,1-t)',fontsize=20)
plt.show()
<Figure size 640x480 with 1 Axes>
4.1.3 LinearSVC實例
from sklearn import datasets
import pandas as pd
iris = datasets.load_iris()
print(iris.keys())
print('labels:',iris['target_names'])
features,labels = iris['data'],iris['target']
print(features.shape,labels.shape)
# 分析數據集
print('-------feature_names:',iris['feature_names'])
iris_df = pd.DataFrame(features)
print('-------info:',iris_df.info())
print('--------descibe:',iris_df.describe())
dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'])
labels: ['setosa' 'versicolor' 'virginica']
(150, 4) (150,)
-------feature_names: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 4 columns):
0 150 non-null float64
1 150 non-null float64
2 150 non-null float64
3 150 non-null float64
dtypes: float64(4)
memory usage: 4.8 KB
-------info: None
--------descibe: 0 1 2 3
count 150.000000 150.000000 150.000000 150.000000
mean 5.843333 3.057333 3.758000 1.199333
std 0.828066 0.435866 1.765298 0.762238
min 4.300000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.300000
50% 5.800000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000
# 數據進行預處理
from sklearn.preprocessing import StandardScaler,LabelEncoder
from sklearn.model_selection import RandomizedSearchCV
from sklearn.svm import LinearSVC
from scipy.stats import uniform
# 對數據進行標准化
scaler = StandardScaler()
X = scaler.fit_transform(features)
print(X.mean(axis=0))
print(X.std(axis=0))
# 對標簽進行編碼
encoder = LabelEncoder()
Y = encoder.fit_transform(labels)
# 調參
svc = LinearSVC(loss='hinge',dual=True)
param_distributions = {'C':uniform(0,10)}
rscv_clf =RandomizedSearchCV(estimator=svc, param_distributions=param_distributions,cv=3,n_iter=20,verbose=2)
rscv_clf.fit(X,Y)
print(rscv_clf.best_params_)
[-1.69031455e-15 -1.84297022e-15 -1.69864123e-15 -1.40924309e-15]
[1. 1. 1. 1.]
Fitting 3 folds for each of 20 candidates, totalling 60 fits
[CV] C=8.266733168092582 .............................................
[CV] .............................. C=8.266733168092582, total= 0.0s
[CV] C=8.266733168092582 .............................................
[CV] .............................. C=8.266733168092582, total= 0.0s
[CV] C=8.266733168092582 .............................................
[CV] .............................. C=8.266733168092582, total= 0.0s
[CV] C=8.140498369662586 .............................................
[CV] .............................. C=8.140498369662586, total= 0.0s
...
...
...
[CV] .............................. C=9.445168322251103, total= 0.0s
[CV] C=9.445168322251103 .............................................
[CV] .............................. C=9.445168322251103, total= 0.0s
[CV] C=2.100443613273717 .............................................
[CV] .............................. C=2.100443613273717, total= 0.0s
[CV] C=2.100443613273717 .............................................
[CV] .............................. C=2.100443613273717, total= 0.0s
[CV] C=2.100443613273717 .............................................
[CV] .............................. C=2.100443613273717, total= 0.0s
{'C': 3.2357870215300046}
# 模型評估
y_prab = rscv_clf.predict(X)
result = np.equal(y_prab,Y).astype(np.float32)
print('accuracy:',np.sum(result)/len(result))
accuracy: 0.9466666666666667
from sklearn.metrics import accuracy_score,precision_score,recall_score
print('accracy_score:',accuracy_score(y_prab,Y))
print('precision_score:',precision_score(y_prab,Y,average='micro'))
accracy_score: 0.9466666666666667
precision_score: 0.9466666666666667
5 附錄
5.1 非線性SVM分類SVC
SVC類通過參數kernel的設置可以實現線性和非線性分類,具體參數說明和屬性說明如下
5.1.1 SVC類參數說明
- C: 懲罰系數,float,default=1.0
- kernel: string,default='rbf',核函數選擇,必須為('linear','poly','rbf','sigmoid','precomputed' or callable)其中一個
- degree: 只有當kernel='poly'時才有意義,表示多項式核的深度
- gamma: float,default='auto',核系數
- coef0,: float, optional (default=0.0),Independent term in kernel function,It is only significant in 'poly' and 'sigmoid',影響模型受高階多項式還是低階多項式影響的結果
- shrinking: bool,default=True
- probability: bool,default=False
- tol: 提前停止參數
- cache_size:
- class_weight: 類標簽權重
- verbose: 日志輸出類型
- max_iter: 最大迭代次數
- decision_function_shape: ‘ovo’,'ovr',defalut='ovr'
- random_state:
5.1.2 SVC類屬性說明
- support_:
- support_vectors_:
- n_support_:
- dual_coef_:
- coef_:
- intercept_:
- fit_status_:
- probA_:
- probB_:
5.1.3 核函數選擇
有這么多核函數,該如何決定使用哪一個呢?有一個經驗法則是,永遠先從線性核函數開始嘗試(LinearSVC比SVC(kernel='linear')快的多),特別是訓練集非常大或特征非常多的時候,如果訓練集不太大,可以試試高斯RBF核,大多數情況下它都非常好用。如果有多余時間和精力,可以使用交叉驗證核網格搜索來嘗試一些其他的核函數,特別是那些專門針對你數據集數據結構的和函數
5.2 GridSearchCV類說明
5.2.1 GridSearchCV參數說明
- estimator: 估算器,繼承於BaseEstimator
- param_grid: dict,鍵為參數名,值為該參數需要測試值選項
- scoring: default=None
- fit_params:
- n_jobs: 設置要並行運行的作業數,取值為None或1,None表示1 job,1表示all processors,default=None
- cv: 交叉驗證的策略數,None或integer,None表示默認3-fold, integer指定“(分層)KFold”中的折疊數
- verbose: 輸出日志類型
5.2.2 GridSearchCV屬性說明
- cv_results_: dict of numpy(masked) ndarray
- best_estimator_:
- best_score_: Mean cross-validated score of the best_estimator
- best_params_:
- best_index_: int,The index (of the ``cv_results_`` arrays) which corresponds to the best candidate parameter setting
- scorer_:
- n_splits_: The number of cross-validation splits (folds/iterations)
- refit_time: float
5.3 RandomizedSearchCV類說明
5.3.1 RandomizedSearchCV參數說明
- estimator: 估算器,繼承於BaseEstimator
- param_distributions: dict,鍵為參數名,Dictionary with parameters names (string) as keys and distributions or lists of parameters to try. Distributions must provide a ``rvs`` method for sampling (such as those from scipy.stats.distributions). If a list is given, it is sampled uniformly
- n_iter: 采樣次數,default=10
- scoring: default=None
- fit_params:
- n_jobs: 設置要並行運行的作業數,取值為None或1,None表示1 job,1表示all processors,default=None
- cv: 交叉驗證的策略數,None或integer,None表示默認3-fold, integer指定“(分層)KFold”中的折疊數
- verbose: 輸出日志類型
5.3.2 RandomizedSearchCV屬性說明
- cv_results_: dict of numpy(masked) ndarray
- best_estimators_:
- best_score_: Mean cross-validated score of the best_estimator
- best_params_:
- best_index_: int,The index (of the ``cv_results_`` arrays) which corresponds to the best candidate parameter setting
- scorer_:
- n_splits_: The number of cross-validation splits (folds/iterations)
- refit_time: float
參考資料:
- (1)<機器學習實戰基於Scikit-Learn和TensorFlow>
- (2)<百面機器學習>
- (3)李航<統計學習方法>
- (4)https://zhuanlan.zhihu.com/p/21932911?refer=baina