案例学习/教程02- 基于cvxopt实现hard-margin SVM binary classifier

这是我的博客理论推导埋下的坑。目的是使用CVXOPT去实现一个硬边界的支持向量机分类器。在这片博文中将会包括一些核心代码。所有代码整理成Jupyter notebook,发布在我的github上。

案例学习/教程02- 基于cvxopt实现hard-margin SVM binary classifier_第1张图片 Jupyter Notebook教程中最后分类器实现效果的可视化

1 什么是硬边界SVM?

简单来说,在二维情况下,就是对于线性可分的一些点(包含正例和负例),找一条线能将他们分开。同时,能保证线离点距离拉的最大。具体教程请见我的:机器学习推导合集02-SVM简明入门1-硬边界SVM的建模过程

2 核心代码

SVM硬边界的优化问题为:

$$\min_{w, b} \frac{1}{2} \textbf{w}^T \textbf{w}$$

$$ s.t: \,\, \forall i\in[N]: y_n (\textbf{w}^Tx_n+b) \geqslant 1 $$

cvxopt文档中QP问题的标准形式为:

$$\text{minimize }\frac{1}{2}x^TPx+q^Tx\\ Gx \preceq h\\ Ax=b$$

通过计算,我们能得到:

$$ \textbf{P}= \left[ \begin{matrix} 0 & \textbf{0}_d^T \\ \textbf{0}_d & \textbf{I}_d \\ \end{matrix} \right] , \textbf{q}=\textbf{0}_{d+1} , \textbf{G}= \left[ \begin{matrix} -y_1 & -y_1\textbf{x}_1^T \\ -y_2 & -y_2\textbf{x}_2^T \\ \vdots & \vdots \\ -y_N & -y_N\textbf{x}_2^T \\ \end{matrix} \right] , \textbf{h}=-\textbf{1}_N $$

class SVM():

    def __init__(self):
        # 用来标记是否已经拟合过数据(训练过)
        self.has_fitted = False
        # 在后面用来判定是否一个数据点为支持向量的阈值
        self.sp_v_threshold = 1e-8
        # list for supoort vector indexes
        self.sp_v_i = []

    def fit(self, X, y):
        assert len(X) > 0, "Please input at least one sample"

        # 决定形状
        n = len(X)
        dim = len(X[0])

        # 初始化变量
        self.P = matrix(np.identity(dim + 1, dtype=np.float))
        self.q = matrix(np.zeros((dim + 1,), dtype=np.float))
        self.G = matrix(np.zeros((n, dim + 1), dtype=np.float))
        self.h = -matrix(np.ones((n,), dtype=np.float))

        # 将数据导入这些变量
        # P
        self.P[0, 0] = 0
        # G = [y_1, --y_1 x_1^T--; y_2, --y_2 x_2^T--, ..., y_n, --y_n x_n^T]
        for i in range(n):
            self.G[i, 0] = -y[i]
            self.G[i, 1:] = -X[i, :] * y[i]

        sol = solvers.qp(self.P,
                         self.q,
                         self.G,
                         self.h
                         )

        # 用CVX求解
        self.w = np.zeros(dim,) # weight
        self.b = sol["x"][0] # bias
        for i in range(1, dim + 1):
            self.w[i - 1] = sol["x"][i]
        
        # 根据支持向量y_i(w^Tx_i+b)=1的结论,找到支持向量
        # 由于数值因素,很有可能比1稍微大一些。我们认为在一定阈值之内能可以接受。
        for i in range(n):
            v = y[i] * (np.dot(self.w, X[i]) + self.b)
            if v < (1 + self.sp_v_threshold):
                self.sp_v_i.append(i)
        self.has_fitted = True
        return self.w, self.b

    def predict(self, X):
        """
        根据训练的结果做预测
        :param X:
        :return: y_hat: numpy.array[n, ], +1为正样本-1为负样本
        """
        assert self.has_fitted, "Model has not been trained"
        return np.sign(np.dot(self.w, X.T) + self.b)

具体更多代码请参考我的Github repository,内附Toy data的可视化例子

3代码下载方法

Github地址:链接

  1. 在指定目录下打开终端(Windows 下为Powershell, Ubuntu直接右键打开终端即可)

  2. 确保安装有Git和Jupyter notebook(输入git命令和jupyter命令尝试)

  3. 输入以下指令:

git clone https://github.com/liubai01/hard-margin-SVM-tutorial-by-cvxopt.git
cd hard-margin-SVM-tutorial-by-cvxopt/
jupyter notebook

最终,打开SVM_HARD.ipynb即可。

你可能感兴趣的:(案例学习)