機器學習:支持向量機(SVC)

Linear SVM

perceptron model

感知機模型是二分類線性判別模型,將所有誤分樣本的幾何間隔作爲損失函數,使用隨機梯度下降法求解. 所有樣本點正確分類時,模型學習完成,最終超平面與初始參數、梯度更新次序有關.

樣本離超平面的距離,可近似模型對樣本分類的可靠程度. 對於所有滿足條件的超平面,距最近樣本點距離最大的超平面爲最優超平面. 基於此建立的線性可分模型爲凸優化模型,解唯一.


distance to hyperplane

對於超平面H:wx+b=0\mathcal H:w^\top x' + b=0,則空間中任意點xxH\mathcal H的距離爲
distance(x,b,w)=ww(xx)=1wwx+b \text{distance}(x,b,w)=\left|\frac{w^\top}{||w||}(x-x')\right|=\frac{1}{||w||}|w^\top x+b|
證明:xx'H\mathcal H中的任意一點,xxH\mathcal H的距離爲xxx-x'H\mathcal H法向量的投影,即
distance(x,b,w)=porjectw(xx)=1ww(xx) \text{distance}(x,b,w)=|\text{porject}_{w}(x-x')|=\left|\frac{1}{||w||}w\cdot(x-x')\right|


distance to separating hyperplane

若樣本標籤y1,+1y\in{-1,+1},樣本xx的預測標籤爲sign(wx+b)\text{sign}(w^\top x+b),當超平面將xx正確劃分,則始終滿足y(wx+b)0y(w^\top x+b) \geq 0,因此正確劃分的xxH\mathcal H的距離等價於
distance(x,b,w)=1wy(wx+b) \text{distance}(x,b,w)=\frac{1}{||w||}y(w^\top x+b)


margin of special separating hyperplane

縮放ww可調整xxH\mathcal H的函數間隔,爲便於計算,令最小函數間隔爲1,則最小函數間隔和幾何間隔分別表示爲
γ=yi(wTxi+b)=1,γ^=γw=1w \gamma=y_i(w^T x_i+b)=1,\quad\hat\gamma=\frac{\gamma}{|| w||}=\frac{1}{|| w||}
基於**最大化最小几何間隔(最大化邊界)**建立模型,優化問題爲
maxw,b1ws.t.min yi(wTxi+b)=1 \max_{w,b}\quad\frac{1}{|| w||}\quad\quad \text{s.t.}\quad \min\ y_i(w^Tx_i+b)=1
最小函數間隔爲1,等價於所有樣本的函數間隔大於等於1,優化問題等價於
maxw,b1ws.t.y(wTx+b)1 \max_{w,b}\quad\frac{1}{|| w||}\quad\quad \text{s.t.}\quad y(w^T x+b)\geq1
上述問題爲凸二次規劃問題,有閉式最優解.


Linear SVM of Soft Interval Maximization

SVM Model and Hinge Loss

令函數間隔z=y(wTx+b)z=y(w^T x+b)鬆弛變量合頁損失函數
ξ=hinge(z)=max(0,1z) \xi=\ell_\text{hinge}(z)=\max(0,1-z)
z1z\geq 1ξ=0\xi=0; z<0z\lt0ξ=1z\xi=1-z.

對於線性不可分問題,以合頁函數作爲損失函數並加入正則項,模型表示爲(可用梯度下降法求解)
minw,bihinge(yi(wTxi+b))+λw2 \min_{ w,b}\quad \sum_i\ell_\text{hinge}(y_i(w^T x_i+b))+\lambda|| w||^2

λ=1/2C\lambda=1/2C,則上述優化問題等價於(推導略)
minw,b,ξ12w2+Ciξis.t.yi(wTxi+b)1ξi,ξi0 \begin{aligned} \min_{ w,b, \xi} &\quad\frac{1}{2}|| w||^2+C\sum_i\xi_i\\[1ex] \quad \text{s.t.} &\quad y_i(w^T x_i+b)\geq 1-\xi_i,\quad \xi_i\geq0 \end{aligned}

最小化目標函數的意義是使間隔儘量大,同時使誤分類點數儘量少. 目標函數第一項爲結構風險,第二項爲經驗風險. 懲罰參數CC越大,模型誤分類的代價就越大.

LR使用交叉熵損失,關注全局實例,輸出具有自然概率意義.


Dual Representation

原始約束極值問題,構造拉格朗日函數
minw,b,ξmaxα,μL(w,b,ξ,α,μ)=minw,b,ξmaxα,μ{12w+Ciξiiαi[yi(wTxi+b)1+ξi]iμiξi} \min_{ w,b, \xi}\max_{ \alpha, \mu} L(w,b, \xi, \alpha, \mu) =\min_{ w,b, \xi}\max_{ \alpha, \mu} \left\{\frac{1}{2}|| w||+C\sum_i\xi_i-\sum_i\alpha_i[y_i(w^T x_i+b)-1+\xi_i]-\sum_i\mu_i\xi_i\right\}

原問題內部極大化是不等式約束的等價形式:

  • 若存在不滿足約束的xix_iξi\xi_i,令αi+\alpha_i\to+\inftyμi+\mu_i\to+\infty,則maxα,μL=+\max\limits_{ \alpha, \mu} L=+\infty;
  • 若任意xix_i滿足約束,則maxα,μL=w2/2+Ciξi\max\limits_{ \alpha, \mu} L=|| w||^2/2+ C\sum_i\xi_i;

i. minw,b,ξL\min\limits_{ w,b, \xi}L
w,b,ξw,b, \xi的偏導並令其爲0,得
w=iαiyixi,iαiyi=0,Cαiμi=0 w=\sum_i\alpha_iy_i x_i,\quad \sum_i\alpha_iy_i=0,\quad C-\alpha_i-\mu_i=0

ii. maxα,μL\max\limits_{ \alpha, \mu}L
帶入上述結果,最優化問題轉換爲
minα,μ12ijαiαjyiyj(xixj)iαis.t.  iαiyi=0,αi0,Cαiμi=0,μi0 \begin{aligned} &\min_{ \alpha, \mu}\quad \frac{1}{2}\sum_i\sum_j\alpha_i\alpha_jy_iy_j(x_i\cdot x_j)-\sum_i\alpha_i\\[0.5ex] &\text{s.t.}\quad\ \ \sum_i\alpha_iy_i=0,\quad \alpha_i\geq0,\quad C-\alpha_i-\mu_i=0,\quad\mu_i\geq0 \end{aligned}

消去μi\mu_i
minα12ijαiαjyiyj(xixj)iαis.t.  iαiyi=0,0αiC \begin{aligned} &\min_{ \alpha}\quad \frac{1}{2}\sum_i\sum_j\alpha_i\alpha_jy_iy_j(x_i\cdot x_j)-\sum_i\alpha_i\\[0.5ex] &\text{s.t.}\quad\ \ \sum_i\alpha_iy_i=0,\quad 0\leq\alpha_i\leq C \end{aligned}

基於SMO算法求解約束方程解,α=(α1,α2,,αn)T\alpha=(\alpha_1,\alpha_2, \cdots,\alpha_n)^T.

S\mathcal S表示0<α<C0<\alpha<C的集合,則yi(wTxi+b)1=0y_i(w^T x_i+b)-1=0,模型參數
w=iSαiyixi,b=1SiS(yiwTxi) w^*=\sum_{i\in\mathcal S}\alpha_iy_i x_i,\quad b^*=\frac{1}{|\mathcal S|}\sum_{i\in\mathcal S}(y_i- w^T x_i)

實例點計算僅以內積形式出現,可自然引入核函數.


KKT Condition and Support Vector

KKT條件(最優解必要條件)
{w=wiαiyixi=0b=iαiyi=0ξ=Cαμ=0αi(yi(wTxi+b)1+ξi)=0μξ=0yi(wTxi+b)1+ξi0ξ,α,μ0 \begin{cases} \nabla_{ w}= w-\sum_i\alpha_iy_i x_i= 0\\[1ex] \nabla_{b}=-\sum_i\alpha_iy_i=0\\[1ex] \nabla_{ \xi}= C- \alpha- \mu= 0\\[1ex] \alpha_i(y_i(w^T x_i+b)-1+\xi_i)=0\\[1ex] \mu\cdot \xi= 0\\[1ex] y_i(w^T x_i+b)-1+\xi_i\geq0\\[1ex] \xi, \alpha, \mu\geq 0 \end{cases}
αi>0\alpha_i>0對應的實例爲支持向量,由KKT條件知支持向量滿足
yi(wTxi+b)1+ξi=0 y_i(w^T x_i+b)-1+\xi_i=0

  • αi=0\alpha_i=0ξi=0\xi_i=0,樣本位於邊界之外(分類正確);
  • 0<αi<C0<\alpha_i<Cξi=0\xi_i=0,樣本位於邊界上(分類正確);
  • αi=C\alpha_i=C0<ξi<10\lt\xi_i\lt 1,樣本位於超平面與邊界之間(分類正確);ξi=1\xi_i=1,樣本位於超平面上;ξi>1\xi_i>1,樣本位於邊界之外(分類錯誤);

Non-linear SVM Based on Kernel function

對於有限維數據,一定存在高維特徵空間使樣本線性可分. 對於非線性分類問題,首先需將原空間數據變換至新的特徵空間(一般爲高維),然後在新空間中使用線性分類方法學習分類模型.

SVM中引入核函數,得SVM的決策函數
f(x)=sign(iαiyiK(xi,x)+b) f(x)=\text{sign}\left(\sum_i\alpha_i^*y_iK(x_i, x)+b^*\right)


Kernel Trick

ϕ(x)\phi(x)是一個從X\mathcal XH\mathcal H的映射,對所有的x,zXx, z\in \mathcal X滿足
K(x,z)=ϕ(x)ϕ(z) K(x, z)=\phi(x)\cdot\phi(z)
定義核函數使得學習隱式地在特徵空間中進行,不需要顯式定義新的特徵空間和映射函數,避免在新的高維空間中做內積運算.

對於映射ϕ(x)=(x12,2x1x2,x22)T\phi(x)=(x_1^2,\sqrt2x_1x_2,x_2^2)^T,則
K(x,z)=(xz)2=x12z12+2x1z1x2z2+x22z22=ϕ(x)ϕ(z) K(x, z)=(x\cdot z)^2=x_1^2z_1^2+2x_1z_1x_2z_2+x_2^2z_2^2=\phi(x)\cdot\phi(z)
在低維空間定義的運算等價於中完成高維空間的內積運算,二維空間(xz)2(x\cdot z)^2等價於三維空間中ϕ(x)ϕ(z)\phi(x)\cdot\phi(z).


Positive Definite Kernel

核函數的條件:K對稱,K對應的Gram矩陣半正定.

Radial Basis Function Kernel, RBF

K(x,z)=exp(12xz2) K(x, z)=\exp(-\frac{1}{2}|| x- z||_2)

泰勒展開得
K(x,z)=exp(12x2)exp(12z2)exp(xz)=CxCzexp(xz)=CxCz[1+xz+12(xz)2+] \begin{aligned} K(x, z) &=\exp(-\frac{1}{2}|| x||_2)\exp(-\frac{1}{2}|| z||_2)\exp(x\cdot z) =C_{ x}C_{ z}\exp(x\cdot z)\\[0.5ex] &=C_{ x}C_{ z}\left[1+ x\cdot z+\frac{1}{2}(x\cdot z)^2+\cdots\right] \end{aligned}

RBF核等價於在無窮維空間中做內積,容易過擬合.

Sigmoid kernel

K(x,z)=tanh(xz) K(x, z)=\text{tanh}(x\cdot z)

決策函數
f(x)=iαiyitanh(xix)+b f(x)=\sum_i\alpha_i^*y_i\text{tanh}(x_i\cdot x) + b^*
等價於僅含有一層隱藏層的神經網絡,隱藏層神經元個數等於支持向量數,激活函數爲tanh函數.


Sequential Minimal Optimization, SMO

引入核函數的線性SVM的對偶問題
minα12ijαiαjyiyjK(xixj)iαis.t.  iαiyi=0,0αiC \begin{aligned} &\min_{ \alpha}\quad \frac{1}{2}\sum_i\sum_j\alpha_i\alpha_jy_iy_jK(x_i\cdot x_j)-\sum_i\alpha_i\\[0.5ex] &\text{s.t.}\quad\ \ \sum_i\alpha_iy_i=0,\quad 0\leq\alpha_i\leq C \end{aligned}
SMO算法啓發式求解,若所有解滿足KKT條件知,則所得解爲最優解. SMO算法每次迭代只優化兩個變量(等式約束限制,實際僅優化一個自由變量),固定其它變量,構建二次規劃逐漸逼近最優解.


Unclipped Solving

假定優化變量α1,α2\alpha_1,\alpha_2,則最優化問題等價於
minα1,α2W(α1,α2)=12K11α12+12K22α22+y1y2K12α1α2(α1+α2)  +y1α1i=3NyiαiKi1+y2α2i=3NyiαiKi2s.t.α1y1+α2y2=i=3Nαiyi=ξ,0α1,α2C \begin{aligned} \min\limits_{\alpha_1,\alpha_2}&\quad W(\alpha_1, \alpha_2)=\frac{1}{2}K_{11}\alpha_1^2+\frac{1}{2}K_{22}\alpha_2^2+y_1y_2K_{12}\alpha_1\alpha_2-(\alpha_1+\alpha_2)\\ &\qquad\qquad\qquad\ \ +y_1\alpha_1\sum_{i=3}^Ny_i\alpha_iK_{i1}+y_2\alpha_2\sum_{i=3}^Ny_i\alpha_iK_{i2}\\ \text{s.t.} &\quad\alpha_1y_1+\alpha_2y_2=-\sum\limits_{i=3}^N\alpha_iy_i=\xi,\quad 0\leq\alpha_1,\alpha_2\leq C \end{aligned}
α2\alpha_2爲自由變量,替換α1\alpha_1,令WWα2\alpha_2的偏導爲0,得
(K11+K222K12)α2=y2(y2y1+ξK12ξK12+i=3NyiαiKi1i=3NyiαiKi2) (K_{11}+K_{22}-2K_{12})\alpha_2^*=y_2(y_2-y_1+\xi K_{12}-\xi K_{12}+\sum_{i=3}^Ny_i\alpha_iK_{i1}-\sum_{i=3}^Ny_i\alpha_iK_{i2})
令迭代前的預測偏差Ej=i=1NαiyiKij+byjE_j=\sum_{i=1}^N\alpha_iy_iK_{ij}+b-y_j,因此未剪短的最優解爲
α2new,unc=α2old+y2(E1E2)K11+K222K12 \alpha_2^{\text{new,unc}}=\alpha_2^{\text{old}}+\frac{y_2(E_1-E_2)}{K_{11}+K_{22}-2K_{12}}


Clipped Solving

由約束條件知,最優解位於平行於邊長爲C的正方形的對角線的線段上,如下所示

y1=y2y_1=-y_2,最優解α2\alpha_2的界限
L=max(0,α2oldα1old),H=min(C,C+α2oldα1old) L=\max(0,\alpha_2^{\text{old}}-\alpha_1^{\text{old}}),\quad H=\min(C,C+\alpha_2^{\text{old}}-\alpha_1^{\text{old}})
y1=y2y_1=y_2,最優解α2\alpha_2的界限
L=max(0,α1old+α2oldC),H=min(C,α1old+α1old) L=\max(0,\alpha_1^{\text{old}}+\alpha_2^{\text{old}}-C),\quad H=\min(C,\alpha_1^{\text{old}}+\alpha_1^{\text{old}})
因此剪短之後的最優解
α2new=max(L,min(H,α2new,unc)),α1new=α1old+y1y2(α2oldα2new) \alpha_2^{\text{new}}=\max(L,\min(H,\alpha_2^{\text{new,unc}})),\quad \alpha_1^{\text{new}}=\alpha_{1}^{\text{old}}+y_1y_2(\alpha_2^{\text{old}}-\alpha_2^{\text{new}})


Variable Selection

初始α=0\alpha= 0,迭代時變量選擇

  • α1\alpha_1選擇違反KKT條件最嚴重的變量,具體做法是0<αi<C0<\alpha_i<C的不滿足KKT條件的實例,若均滿足則在餘下實例中查找;
  • α2\alpha_2選擇更新後變化較大的變量,α2\alpha_2更新後變化程度正比於E1E2|E_1-E_2|,爲簡化計算,選擇具有較大E1E2|E_1-E_2|的變量;

如何檢驗是否滿足KKT條件(ϵ\epsilon誤差範圍內檢驗)?
α={0,yg(x)1,yEϵ(0,C),yg(x)=1,ϵyEϵC,yg(x)1,yEϵ \alpha=\begin{cases} 0,&yg(x)\geq1,&yE\geq-\epsilon\\[1ex] (0,C),&yg(x)=1,&-\epsilon\leq yE\leq\epsilon\\[1ex] C,&yg(x)\leq1,&yE\leq\epsilon\\ \end{cases}
綜上,不滿足KKT條件等價於,當αi<C\alpha_i \lt CyiEi<ϵy_iE_i \lt -\epsilon,或當αi>0\alpha_i \gt0yiEi>ϵy_iE_i \gt \epsilon.

如何更新bb值?

利用間隔邊界上的點α1\alpha_1α2\alpha_2更新bb值,假定α1\alpha_1位於間隔邊界(任一間隔邊界上的點均可),0<α1<C0<\alpha_1<C,則
bnew=y1i=3NαiyiKi1α1newy1K11α2newy2K12=E1y1K11(α1newα1old)y2K12(α2newα2old)+bold \begin{aligned} b^{\text{new}} &=y_1 -\sum_{i=3}^N\alpha_iy_iK_{i1} - \alpha_1^{\text{new}}y_1K_{11}-\alpha_2^{\text{new}}y_2K_{12}\\ &=-E_1-y_1K_{11}(\alpha_1^{\text{new}}-\alpha_1^{\text{old}})-y_2K_{12}(\alpha_2^{\text{new}}-\alpha_2^{\text{old}})+b^{\text{old}} \end{aligned}
α1\alpha_1α2\alpha_2均位於間隔邊界,則分別計算取均值作爲新的bb值. 可見,保存並更新預測誤差能加速計算.

如何更新誤差EE值?

若每次更新α1\alpha_1α2\alpha_2兩個變量,比較更新前後誤差值
Ej={i=3NαiyiKij+α1oldy1Kj1+α1oldy1Kj1+bold,i=3NαiyiKij+α1newy1Kj1+α1newy1Kj1+bnew, E_j= \begin{cases} \displaystyle\sum_{i=3}^N\alpha_iy_iK_{ij}+\alpha_1^{\text{old}}y_1K_{j1}+\alpha_1^{\text{old}}y_1K_{j1}+b^{\text{old}},&更新前\\ \displaystyle\sum_{i=3}^N\alpha_iy_iK_{ij}+\alpha_1^{\text{new}}y_1K_{j1}+\alpha_1^{\text{new}}y_1K_{j1}+b^{\text{new}},&更新後 \end{cases}
因此,誤差的更新公式爲
Ejnew=Ejold+(α1newα1old)y1Kj1+(α2newα2old)y2Kj2+bnewbold E_j^{\text{new}}=E_j^{\text{old}}+(\alpha_1^{\text{new}}-\alpha_1^{\text{old}})y_1K_{j1}+(\alpha_2^{\text{new}}-\alpha_2^{\text{old}})y_2K_{j2}+b^{\text{new}}-b^{\text{old}}


SVC Implementation

# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
from numpy import *


class Kernel:

    @staticmethod
    def linear():
        return lambda x, y: float(inner(x, y))

    @staticmethod
    def gaussian(sigma):
        return lambda x, y: exp(
            float(linalg.norm(x - y)) ** 2 / (-2 * sigma ** 2))

    @staticmethod
    def _polykernel(dimension, offset):
        return lambda x, y: (offset + float(inner(x, y))) ** dimension

    @classmethod
    def inhomogenous_polynomial(cls, dimension):
        return cls._polykernel(dimension=dimension, offset=1.0)


class SVC:

    def __init__(self, kernel, C=0.5, max_iter=1000, eps=1e-3):
        """
        構造函數
        :param kernel: 核函數指針
        :param C: 懲罰參數
        :param max_iter: 無任何變量改變時的最大迭代次數
        :param eps: KKT條件檢驗範圍(容錯率)
        """
        self.C = C
        self.kernel = kernel
        self.max_iter = max_iter
        self.eps = eps

    def fit(self, X, y):
        """
        訓練模型
        :param X: 輸入特徵集, 樣本數*特徵數
        :param y: 輸入標籤集, 1*樣本數, 類別爲+1或-1
        :return: self
        """
        self._X = mat(X, dtype=float64)
        self._Y = mat(y, int8).T
        n_samples, n_features = X.shape

        # 初始化alpha、gram矩陣、誤差矩陣
        self._K = self.__gram_matrix(self._X)

        self._E = mat(-self._Y, dtype=float64)
        self._alphas = mat(zeros((n_samples, 1)))
        self.b = 0

        # 是否遍歷全部變量
        entire = True

        # 內循環有效更改變量次數
        pair_changed = 0

        for _ in range(self.max_iter):
            print('iter:', _)
            # 若已遍歷全部變量, 變量未有效更新, 則終止循環
            if not entire and pair_changed == 0:
                break
            pair_changed = 0
            if entire:
                for i in range(n_samples):
                    pair_changed += self.__inner_loop(i)
            else:
                for i in where((self._alphas > 0) & (self._alphas < self.C))[0]:
                    pair_changed += self.__inner_loop(i)

            # 若已遍歷全部變量, 則下次一定遍歷邊界變量;
            # 若已遍歷邊界變量, 變量得到有效更新,則下次仍遍歷邊界變量;
            entire = False if entire else pair_changed == 0

        # 計算模型
        sv = where(self._alphas > 0)[0]
        self.sv_X = self._X[sv]
        self.sv_Y = self._Y[sv]
        self.sv_alphas = self._alphas[sv]
        self.w = (multiply(self.sv_alphas, self.sv_Y).T * self.sv_X).T
        return self

    def __inner_loop(self, i):
        """內循環"""
        # 臨時變量, 用於減少訪問, 加速計算
        alphas, b, C, E, K = self._alphas, self.b, self.C, self._E, self._K

        alphaIold, Yi, Ei = alphas[i, 0], self._Y[i, 0], E[i, 0]
        # 滿足KKT條件, 則跳出本次循環
        if not (
                alphaIold < C and Yi * Ei < -self.eps or alphaIold > 0 and Yi
                * Ei > self.eps):
            return 0

        # 選擇第二個變量
        j = self.__select_j(i)
        alphaJold, Yj, Ej = alphas[j, 0], self._Y[j, 0], E[j, 0]

        # 計算剪輯邊界
        if Yi == Yj:
            L = max(0, alphaJold + alphaIold - C)
            H = min(C, alphaJold + alphaIold)
        else:
            L = max(0, alphaJold - alphaIold)
            H = min(C, C + alphaJold - alphaIold)
        if L == H:
            return 0
        eta = K[i, i] + K[j, j] - 2.0 * K[i, j]
        if eta == 0:
            return 0

        # 更新第二個變量
        unc = alphaJold + Yj * (Ei - Ej) / eta
        alphas[j, 0] = H if unc > H else L if unc < L else unc
        deltaJ = Yj * (alphas[j, 0] - alphaJold)

        # 更新第一個變量
        alphas[i, 0] -= Yi * deltaJ

        # 更新b值
        deltaI = Yi * (alphas[i, 0] - alphaIold)
        b1 = b - Ei - deltaI * K[i, i] - deltaJ * K[i, j]
        b2 = b - Ej - deltaI * K[i, j] - deltaJ * K[j, j]
        if 0 < alphas[i, 0] < C:
            self.b = b1
        elif 0 < alphas[j, 0] < C:
            self.b = b2
        else:
            self.b = 0.5 * (b1 + b2)

        # 更新誤差矩陣
        E += ([deltaI, deltaJ] * K[[i, j]]).T + (self.b - b)

        return 1 if abs(deltaJ) > 0.00001 else 0

    def score(self, X, y):
        """
        計算模型預測正確率
        :param X: 輸入特徵集, m*n
        :param y: 輸入標籤集, 1*m
        :return: 0~1
        """
        y_ptd = self.predict(X)
        error_nums = len(where(y_ptd != mat(y).T)[0])
        return 1 - error_nums / len(X)

    def predict(self, X):
        """
        預測類別
        :param X: 輸入特徵集, m*n
        :return: 各樣本類別, m*1
        """
        X = mat(X)
        kernel = self.kernel
        sv_X = self.sv_X
        K = mat([[kernel(x, xi) for xi in sv_X] for x in X])
        y = mat([1] * len(X)).T
        y[K * multiply(self.sv_alphas, self.sv_Y) + self.b < 0] = -1
        return y

    def __gram_matrix(self, X):
        """
        計算gram矩陣, 用於加速計算
        :param X: 輸入特徵集
        :return: gram矩陣
        """
        n_samples, n_features = X.shape
        K = mat(zeros((n_samples, n_samples)))
        # 利用核函數計算內積
        for i, x_i in enumerate(X):
            for j, x_j in enumerate(X[:i + 1]):
                K[i, j] = K[j, i] = self.kernel(x_i, x_j)
        return K

    def __select_j(self, i):
        """
        通過最大化步長的方式來獲取第二個alpha值的索引.
        :param i: 第一個變量編號
        :return: 第二個變量編號
        """
        j, E = i, self._E

        # 查找最小誤差的變量編號
        if E[i] > 0:
            min_error = inf
            for k in where((self._alphas > 0) & (self._alphas < self.C))[0]:
                if k != i and E[k] < min_error:
                    j, min_error = k, E[k]
        # 查找最大誤差的變量編號
        else:
            max_error = -inf
            for k in where((self._alphas > 0) & (self._alphas < self.C))[0]:
                if k != i and E[k] > max_error:
                    j, max_error = k, E[k]
        while j == i:
            j = random.randint(0, self._X.shape[0])
        return j


if __name__ == '__main__':

    def load_data(filename):
        """讀取數據"""
        X, y = [], []
        with open(filename) as f:
            for line in f.read().strip().split('\n'):
                line_array = line.split('\t')
                X.append(line_array[:-1])
                y.append(line_array[-1])
        return array(X, float64), array(y, float64)


    def plot_2Dsvm(X, y, w, b, alphas):
        """顯示二維SVM"""
        X, y = array(X), array(y)

        fig = plt.figure()
        ax = fig.add_subplot(111)

        # 繪製樣本散點圖
        colors = array(['g'] * X.shape[0])
        colors[y > 0] = 'b'
        ax.scatter(X[:, 0], X[:, 1], s=30, c=colors, alpha=0.5)

        # w1x+w2y+b=0, 取兩點繪製超平面及間隔
        x_min = X[where(X[:, 0] == X[:, 0].min())[0], 0]
        x_max = X[where(X[:, 0] == X[:, 0].max())[0], 0]
        x = array([x_min, x_max])
        y = (- b - w[0, 0] * x) / w[1, 0]
        y1 = (- b - linalg.norm(w, 2) - w[0, 0] * x) / w[1, 0]
        y2 = (- b + linalg.norm(w, 2) - w[0, 0] * x) / w[1, 0]
        ax.plot(x, y, 'r')
        ax.plot(x, y1, 'r--', alpha=0.2)
        ax.plot(x, y2, 'r--', alpha=0.2)

        for k in where(alphas > 0)[0]:
            plt.scatter(X[k, 0], X[k, 1], color='', edgecolors='r', marker='o',
                        s=150)
        plt.show()


    X, y = load_data(r"D:\testSet.txt")
    svc = SVC(kernel=Kernel.linear(), C=2)
    # svc = SVC(kernel=Kernel.gaussian(0.6), C=20)
    svc.fit(X, y)
    print(svc.score(X, y))

    plot_2Dsvm(X, y, svc.w, svc.b, svc._alphas)

數據集百度雲鏈接:https://pan.baidu.com/s/1BmphBTCQLUV-djorG8JdwQ 提取碼:krs9

線性可分SVM 線性不可分SVM
非線性SVM-1 非線性SVM-2
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章