这是我的博客理论推导埋下的坑。目的是使用CVXOPT去实现一个硬边界的支持向量机分类器。在这片博文中将会包括一些核心代码。所有代码整理成Jupyter notebook,发布在我的github上。
简单来说,在二维情况下,就是对于线性可分的一些点(包含正例和负例),找一条线能将他们分开。同时,能保证线离点距离拉的最大。具体教程请见我的:机器学习推导合集02-SVM简明入门1-硬边界SVM的建模过程
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的可视化例子
Github地址:链接
在指定目录下打开终端(Windows 下为Powershell, Ubuntu直接右键打开终端即可)
确保安装有Git和Jupyter notebook(输入git命令和jupyter命令尝试)
输入以下指令:
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即可。