機器學習(八):SVM支持向量機原理及案例分析

一、簡介

支持向量機(Support Vector Machine,SVM)是一類按監督學習方式對數據進行二元分類的廣義線性分類器,其決策邊界是對學習樣本求解的最大邊距超平面。

它的目的是尋找一個超平面來對樣本進行分割,分割的原理則是間隔最大化,最終轉化為一個凸二次規劃問題來求解,由簡至繁的模型包括:

  • 當訓練樣本線性可分時,通過硬間隔最大化,學習一個線性可分支持向量機
  • 當訓練樣本近似線性可分時,通過軟間隔最大化,學習一個線性支持向量機
  • 當訓練樣本線性不可分時,通過核技巧和軟間隔最大化,學習一個非線性支持向量機

二、原理解析

1、點到超平面的距離


假設有一個超平面,在平面外有一個點X,我們要求點X到超平面的距離dist(x,h),但是直接求不好求,我們設超平面上有兩個點(x',x''),即

我們也可以得到:
我們計算一下點X到點X'之間的距離,我們求出x與x'之間的距離然后乘以平面的法向量就得到了dist(x,h),即點x到超平面的距離(線性代數里面的定理)。[1]

2、樣本分類

我們假設數據集(X_{1},Y_{1}),(X_{2},Y_{2}),...,(X_{n},Y_{n}),其中y(x)=w^T\psi(x)+b,\psi(x)是核函數,后面會補充,我們規定Y為樣本的類別:
當X為正例時,Y=+1;當X為負例時,Y=-1;
y(x_{i})> 0 <=> y_{i} = +1
y(x_{i})< 0 <=> y_{i} = -1
可以推出:
y_{i}·y(x_{i}) >0
我們的優化目標是找到一條線(求w和b),使得離該線最近的點能夠距離平面最遠,這是 SVM非常重要的原理。我們可以將公式[1]的絕對值通過乘y_{i}去掉,因為y_{i}只能取1或-1,且y_{i}·y(x_{i})>0,這時公式變成了,dist = \frac{y_{i}(w^T·\psi(x_{i})+b)}{|w|},結果肯定是大于0的,這時我們繼續做一個假設(大于0肯定是一個數值,我們這里假設它大于1,為了計算方便),即對于線(w,b)可以通過放縮使得其結果值|Y| >= 1,y_{i}·(w^T·\psi(x_{i})+b) \geq 1,最終我們的目標就是argmax \lbrace \frac{1}{|w|} min[y_{i}·(w^T·\psi (x_{i})+b)] \rbrace
我們的目標函數就是,arg max \frac{1}{|w|},且y_{i}(w^T·\psi (x_{i})+b) \geq1轉成求最小值min_{w,b}\frac{1}{2}w^2且y_{i}(w^T·\psi (x_{i})+b) \geq1

3、拉格朗日乘數法求解目標函數

我們的目的是求出wb,這時我們引入拉格朗日乘數法求解,min f(x) s.t. g_{i}(x)\leq0,i=1,2,...,m

L(w,b,\alpha) = \frac{1}{2}|w|^2 - \sum_{i=1}^{n}\alpha_{i}(y_{i}·(w^T·\psi(x_{i})+b)-1)
我們利用對偶問題,轉換一下求解思路,
min_{w,b} max_{\alpha}L(w,b,\alpha) \rightarrow max_{\alpha}min_{w,b}L(w,b,\alpha)
分別對w和b求偏導,分別得到兩個條件:
[2]\frac{\partial{L}}{\partial{w}}=0 \Rightarrow w = \sum_{i=1}^{n} \alpha_{i}y_{i}\psi(x_{n})
[3]\frac{\partial{L}}{\partial{b}}=0 \Rightarrow 0 = \sum_{i=1}^{n} \alpha_{i}y_{i}
L(w,b,\alpha) = \frac{1}{2}|w|^2 - \sum_{i=1}^{n}\alpha_{i}(y_{i}·(w^T·\psi(x_{i})+b)-1)
=\frac{1}{2}w^Tw-w^T\sum_{i=1}^{n}\alpha_{i}y_{i}\psi(x_{i})-b\sum_{i=1}^{n}\alpha_{i}y_{i}+\sum_{i=1}^{n}\alpha_{i}分別將[2][3]式代入化簡可得,=\sum_{i=1}^{n}-\frac{1}{2}(\sum_{i=1}^{n}\alpha_{i}y_{i}\psi(x_{i}))^T\sum_{i=1}^{n}\alpha_{i}y_{i}\psi(x_{i})
=\sum_{i=1}^{n}\alpha_{i}-\frac{1}{2}\sum_{i=1,j=1}^{n}\alpha_{i}\alpha_{j}y_{i}y_{j}\psi^T(x_{i})\psi(x_{j})這時我們完成了第一步的求解min_{w,b}L(w,b,\alpha)!
繼續對\alpha求極大值,\begin{cases} max\sum_{i=1}^{n}\alpha_{i}-\frac{1}{2}\sum_{i=1,j=1}^{n}\alpha_{i}\alpha_{j}y_{i}y_{j}\psi(x_{i})\psi(x_{j})\\ \sum_{i=1}^{n} \alpha_{i}y_{i}= 0 \\ \alpha_{i} \geq 0 \end{cases}極大值轉換求極小值,
[4]\begin{cases} min\frac{1}{2}\sum_{i=1,j=1}^{n}\alpha_{i}\alpha_{j}y_{i}y_{j}\psi(x_{i})\psi(x_{j})-\sum_{i=1}^{n}\alpha_{i}\\ \sum_{i=1}^{n} \alpha_{i}y_{i}= 0 \\ \alpha_{i} \geq 0 \end{cases}

4、實例求解

已知一個如圖所示的訓練數據集,其正例點是x_{1}=(3,3)^T,x_{2}=(4,3)^T,負例點是x_{3}=(1,1)^T,試求最大間隔分離超平面。


樣本正例用1表示,負例用-1表示:我們帶入[4]式中,


化簡后的結果為:
我們將代入后分別對參數進行求導得:不符合我們之前的條件,所以最終的解應該為邊界上的點,當 (舍去),當此時,所以最小值在(0.25,0,0.25)處取得。我們由[2]可知,


所以平面方程為:
我們計算出,通過上圖我們也發現,并非邊界點,最終計算的也跟它無關,這里我們就發現所謂的支持向量機是由邊界上的點所支撐起來的,那么我們就把邊界上的點叫做支持向量!支持向量:真正發揮作用的數據點,\alpha值不為0的點。

5、軟間隔

通過上圖發現,在構建決策邊界的過程中,如果某一個點比較特殊(離群點),我們的邊界會為了滿足它而把隔離帶做的很小,這樣并不符合我們的預期,為了解決這種問題,我們引入松弛因子:這樣我們的目標函數就變成了,

  • 當C趨近于無窮大時:意味著分類嚴格不能有錯誤(C越大,為了求min,則\xi_{i}越小,此時1-\xi_{i}就越大)
  • 當C趨近于很小時:意味著可以有更大的錯誤容忍,C是我們需要指定的一個參數
    再進入拉格朗日乘數法后,結果就變成了,
    L(w,b,\xi,\alpha,\mu)=\frac{1}{2}|w|^2+C\sum_{i=1}^{n}\xi_{i}-\sum_{i=1}^{n}\alpha_{i}(y_{i}(w·x_{i}+b)-1+\xi_{i})-\sum_{i=1}^{n}\mu_{i}\xi_{i}
    分別對w,b,\xi求偏導,
    w=\sum_{i=1}^{n}\alpha_{i}y_{i}\psi(x_{n})
    0=\sum_{i=1}^{n}\alpha_{i}y_{i}
    C-\alpha_{i}-\mu_{i}=0
    最后利用對偶問題進行化簡后的結果跟之前一樣,只是\alpha_{i}的取值范圍變成了{0 \leq \alpha_{i} \leq C}

6、核函數

如果數據非常復雜,在低維中很難進行區分,我們可以將數據映射到高維空間。這樣特征信息就更多了,決策的邊界也更容易建立。核函數的目的就是將低維數據映射到高維數據上。

這里我們要將數據的特征進行高維的映射,但是問題也來了,這樣的計算復雜度是不是也上來了呀!其實是這個樣子的SVM在數學上有這樣一個巧合,我們可以把高維特征的內積在低維當中直接計算好然后做映射也是一樣,恰好解決計算的問題!
這里我們再強調一下,在求目標函數的過程中,我們有求內積的操作,但是維數過大,在高維上求內積的計算量非常大,SVM在數學上有一個特性:在低維上內積,用內積的結果做轉換,相當于把數據在高維上做內積,結果是一樣的。
我們常用的是高斯核函數:
高斯核函數的原理就是對于每一個樣本,如果是正例,我們就用高斯分布(正態分布)向上畫,如果是負例我們往下畫,從而可以將正負例樣本分開。
SVM有很多核函數可以幫助我們將數據進行映射,這也是SVM的厲害之處。

三、案例分析

這里我們使用的數據集是sklearn包自帶的人臉數據集fetch_lfw_people,算法也是sklearn中封裝好的算法包,我們先來看一下API:from sklearn.svm import SVC這里面的參數有:
svc(C=1.0, kernel='rbf', degree=3, gamma='auto_deprecated', coef0=0.0, shrinking=True, probability=False,tol=1e-3, cache_size=200, class_weight=None,verbose=False, max_iter=-1, decision_function_shape='ovr',random_state=None)

  • C:懲罰系數
    用來控制損失函數的懲罰系數,這個我們在公式中講過。
  • kernel 核函數
    參數選擇有RBF, Linear, Poly, Sigmoid,precomputed或者自定義一個核函數, 默認的是"RBF",即徑向基核,也就是高斯核函數;而Linear指的是線性核函數,Poly指的是多項式核,Sigmoid指的是雙曲正切函數tanh核;
  • degree
    當指定kernel為'poly'時,表示選擇的多項式的最高次數,默認為三次多項式;若指定kernel不是'poly',則忽略,即該參數只對'poly'有用
  • gamma:核函數系數
    該參數是rbf,poly和sigmoid的內核系數;默認是'auto',表示模型復雜度,gamma越大,σ越小,使得高斯分布又高又瘦,造成模型只能作用于支持向量附近,可能導致過擬合;反之,gamma越小,σ越大,高斯分布會過于平滑,在訓練集上分類效果不佳,可能導致欠擬合
  • coef0:核函數常數值(y=kx+b中的b值)
    只有‘poly’和‘sigmoid’核函數有,默認值是0。
  • shrinking:是否進行啟發式
    如果能預知哪些變量對應著支持向量,則只要在這些樣本上訓練就夠了,其他樣本可不予考慮,這不影響訓練結果,但降低了問題的規模并有助于迅速求解。進一步,如果能預知哪些變量在邊界上(即a=C),則這些變量可保持不動,只對其他變量進行優化,從而使問題的規模更小,訓練時間大大降低。這就是Shrinking技術。 Shrinking技術基于這樣一個事實:支持向量只占訓練樣本的少部分,并且大多數支持向量的拉格朗日乘子等于C。
  • probability:是否使用概率估計,默認是False
    必須在 fit( ) 方法前使用,該方法的使用會降低運算速度。
  • tol:殘差收斂條件,默認是0.0001
    即容忍1000分類里出現一個錯誤,與LR中的一致;誤差項達到指定值時則停止訓練。
  • cache_size 緩沖大小,用來限制計算量大小,默認是200M。
  • class_weight:{dict, ‘balanced’},字典類型或者'balance'字符串。
    權重設置,正類和反類的樣本數量是不一樣的,這里就會出現類別不平衡問題,該參數就是指每個類所占據的權重,默認為1,即默認正類樣本數量和反類一樣多,也可以用一個字典dict指定每個類的權值,或者選擇默認的參數balanced,指按照每個類中樣本數量的比例自動分配權值。
  • verbose:是否啟用詳細輸出。
    在訓練數據完成之后,會把訓練的詳細信息全部輸出打印出來,可以看到訓練了多少步,訓練的目標值是多少;但是在多線程環境下,由于多個線程會導致線程變量通信有困難,因此verbose選項的值就是出錯,所以多線程下不要使用該參數。
  • max_iter:最大迭代次數,默認是-1,即沒有限制。
    這個是硬限制,它的優先級要高于tol參數,不論訓練的標準和精度達到要求沒有,都要停止訓練。
  • decision_function_shape
    原始的SVM只適用于二分類問題,如果要將其擴展到多類分類,就要采取一定的融合策略,這里提供了三種選擇。‘ovo’ 一對一,為one v one,即將類別兩兩之間進行劃分,用二分類的方法模擬多分類的結果,決策所使用的返回的是(樣本數,類別數*(類別數-1)/2); ‘ovr’ 一對多,為one v rest,即一個類別與其他類別進行劃分,返回的是(樣本數,類別數),或者None,就是不采用任何融合策略。默認是ovr,因為此種效果要比oro略好一點。
  • random_state
    在使用SVM訓練數據時,要先將訓練數據打亂順序,用來提高分類精度,這里就用到了偽隨機序列。如果該參數給定的是一個整數,則該整數就是偽隨機序列的種子值;如果給定的就是一個隨機實例,則采用給定的隨機實例來進行打亂處理;如果啥都沒給,則采用默認的 np.random實例來處理。
    這里我們需要用的所有包,
from sklearn.datasets import fetch_lfw_people
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
from sklearn.pipeline import make_pipeline
from sklearn.svm import SVC
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
import seaborn as sns

接下來,我們進行案例分析,首先先來看一下數據長什么樣,

def svm():
    """
    SVM進行簡單人臉分類
    :return:
    """
    #獲取數據集
    faces =fetch_lfw_people(min_faces_per_person=60)
    print(faces.target_names)
    print(faces.images.shape)
    # 圖形
    fig,ax=plt.subplots(3,5)
    for i,axi in enumerate(ax.flat):
        axi.imshow(faces.images[i],cmap='bone')
        axi.set(xticks=[],yticks=[],xlabel=faces.target_names[faces.target[i]])
    plt.show()
    return None

if __name__ == "__main__":
    svm()

接下來,我們劃分數據集,由于像素點過多,我們使用PCA進行降維,這里我們先對PCA,SVC進行實例化,

#每個圖的大小是[62x47]
    #將數據集劃分為測試集與訓練集
    x_train,y_train,x_test,y_test = train_test_split(faces.data,faces.target,random_state=40)
    #
    #使用PCA降維
    #我們降維成150維
    #  whiten: 白化。所謂白化,就是對降維后的數據的每個特征進行標準化,讓方差都為1。
    # random_state:偽隨機數發生器的種子,在混洗數據時用于概率估計
    pca = PCA(n_components=150,whiten=True,random_state=42)
    #實例化SVM
    svc = SVC(kernel='rbf',class_weight='balanced')

然后我們使用sklearn包中pipeline模塊結合PCA和SVC對數據進行處理,它提供了兩種模式:串行化和并行化,這里我們進行串行化就可以,由于兩種算法參數選擇較多,我們使用交叉驗證的方式選擇最優的參數。

    model = make_pipeline(pca,svc)

    #交叉驗證確定系數
    param = {'svc__C':[1,5,10],
             'svc__gamma':[0.0001,0.0005,0.001]}
    grid = GridSearchCV(model,param_grid =param)
    grid.fit(x_train,x_test)
    print(grid.best_params_)

我們使用最后的參數模型進行預測,并畫圖像顯示,黑色代表正確,紅色代表錯誤。

    model=grid.best_estimator_
    yfit = model.predict(y_train)
    print(yfit.shape)

    #算法分類之后的圖形
    fig,ax=plt.subplots(4,6)
    for i,axi in enumerate(ax.flat):
        axi.imshow(y_train[i].reshape(62,47),cmap='bone')
        axi.set(xticks=[],yticks=[])
        axi.set_ylabel(faces.target_names[yfit[i]].split()[-1],
                       color='black' if yfit[i] == y_test[i] else 'red')

    fig.suptitle('Predicted Names:Incorrect Labels in Red',size=14)
    plt.show()

我們也可以看一下實驗的精確率和召回率,以及混淆矩陣。

    print(classification_report(y_test,yfit,target_names=faces.target_names))

    #混淆矩陣
    mat = confusion_matrix(y_test,yfit)
    sns.heatmap(mat.T,square=True,annot=True,fmt='d',
                xticklabels=faces.target_names,
                yticklabels=faces.target_names)
    plt.xlabel('true label')
    plt.ylabel('predicted label')
    plt.show()


關于精確率、召回率以及混淆矩陣,我們在前面的數據可視化部分也講過了,想不起來的同學可以翻看一下。
關于SVM支持向量機這部分的學習到這里就結束了,還有補充時,我會再更新,這一節公式推導較多,有不懂的地方可以下方留言或者私信。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。