机器学习之支持向量机(Support Vector Machine)
一、关于SVM的理解
支撑向量机(Support Vector Machin,SVM)可以解决分类问题,也可以用于解决回归问题。这里仅对分类问题进行一些粗浅的讨论。
在分类问题中,分类算法会将数据空间划分为一个或多个边策边界(高维时称为超平面),边策边界的一边是一类,另一边是另一类。但是,不同的分类算法会对相同的数据生成不同的决策边界。那么,哪个决策边界才是最好的呢?这个问题称为不适定问题对于SVM来说,它的目的就是尽可能地找到一个合适的边界,来提高算法的泛化能力,即鲁棒性。
所以说SVM的具体操作是:寻找一个最优的决策边界,距离两个类别的最近的样本最远。最近的样本点称为支撑向量。
我们首先讨论线性可分的问题,当线性不可分的时候,我们有改进的方法。
上面这个就是一条比较好的边策边界,它离红色样本点和蓝色样本点一样远。在两个类别中,离决策边界最近的那些点都尽可能的远。红色样本有两个点,蓝色样本有一个点,这三个点到决策边界的距离是所有样本点中最近的,且距离是相等的。
这三个点定义出了两条和决策边界平行的线。这两条平行线之间没有任何的样本点。这就是支持向量机的思想。
SVM尝试找到中间那条最优的决策边界,这个决策边界距离两个类别最近的样本最远。
这两条平行线离决策边界的距离相等,记为d,margin就是2d。
SVM就是要最大化margin。
二、Hard Margin SVM
上面的图中,样本点是线性可分的,即可以找到一条决策边界使得分类不会出错,这时候使用的SVM算法称为Hard Magin SVM。此时的算法至少在训练集上是不会发生错误的。但是实际情况中,很多数据是线性不可分的,这时SVM可以通过改进得到 Soft Margin SVM 。
下面我们用数学语言来表达。
直线的一般式方程:
点到直线的距离:
当我们拓展到n维空间的时候,可以写成下面这种形式:
距离公式就变成:
我们定义类A的值为1,类B的值为-1。在图中可以看到,类A的支撑向量距离决策边界的距离为d,类B上的支撑向量距离决策边界的距离也为d。那么我们就可以将公式写成下面这种形式:
因为向量W的模和距离d都为常数,所以我们可以将两边都除以d:
||w||是一个标量, d也是一个标量,我们可想象把 w^T每个元素和b都除以分母 ||w||d这个标量,我们用新的字母来表示除以之后的结果:
下标d说明截距和向量 w 已经被 d 和 w 的模相除。
因为定义了类A的值为1,类B的值为-1,那么可以写成一个式子:
对于所有支撑向量,满足这样的方程:
即求模的最小值。实际工程中为了求导得到最小值,使用的是第二个式子
整个支持向量机的最优化问题就变成了,在条件
的情况下求最优化。具体的求解比较复杂,大家可以查看相关资料学下 。
三、Soft Margin SVM
如果数据线性不可分,那么如果要使用SVM算法,就需要允许分类算法犯一定的错误,具体方法是让限定条件变得宽松一点:
那么随之求解目标也会有所改变。这里增加了一个超参数C,来控制正则项的重要程度。C越小容错空间越大,C趋向于0的时候,允许无限大的误差,趋向于无穷大的时候,算法本质就是Hard Margin SVM。
L1正则化:
L2正则化:
四、处理线性的数据
鸢尾花数据集
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
iris = datasets.load_iris()
X = iris.data
y = iris.target
X = X [y<2,:2] #只取y<2的类别,也就是0 1并且只取前两个特征
y = y[y<2] # 只取y<2的类别 # 分别画出类别0和1的点
plt.scatter(X[y==0,0],X[y==0,1],color='red')
plt.scatter(X[y==1,0],X[y==1,1],color='blue')
plt.show()
#标准化
standardScaler = StandardScaler()
standardScaler.fit(X) #计算训练数据的均值和方差
X_standard = standardScaler.transform(X) #再用scaler中的均值和方差来转换X,使X标准化
svc = LinearSVC(C=1e9) #线性SVM分类器
svc.fit(X_standard,y) # 训练svm
原始数据点分布:
当我们训练好之后,就可以绘制这个决策边界:
def plot_decision_boundary(model, axis):
x0, x1 = np.meshgrid(
np.linspace(axis[0], axis[1], int((axis[1]-axis[0])*100)).reshape(-1,1),
np.linspace(axis[2], axis[3], int((axis[3]-axis[2])*100)).reshape(-1,1)
)
X_new = np.c_[x0.ravel(), x1.ravel()]
y_predict = model.predict(X_new)
zz = y_predict.