先看线性可分问题。对于线性可分,其实感知机就可以解决。但是感知机只是找到一个超平面将数据分开,而这样的超平面可能是平行的无限多个,我们需要在这其中找到最优的一个。怎么衡量一个超平面是不是最优的呢,直观上讲,它应该是处于临界状态,到两类样本的距离都是最远的。那么怎么表示这个距离呢?以二维平面中的二分类为例:
距离
首先,在这里超平面退化为直线,将其表示为:。等于0是表示位于该直线上的点代入直线后结果是0(表示一个点,而不是坐标x)。那么其他不处于该直线的点代入之后很明显不等于0,那么这个不等于0的值是什么呢。数学上称为函数间隔,我理解它就是一个相对量,相对于该直线的一个“距离”。更准确地,该直线两侧的点是有符号的,那么就是点的函数距离。
https://www.zhihu.com/question/64568136/answer/257874428
很明显,一对可以唯一地表示一条直线,但是这个关系不是一一对应的,当同比例变化时,和表示的是同一个平面。这会带来两个问题,一个问题是我们希望通过更新来更新超平面,现在很可能更新半天还是那个超平面,造成一些无效的计算;第二个问题是明明是同一个超平面,但是如果使用函数距离的话却发生了变化。第二个问题却会把第一个问题放大,让我们错以为超平面已经更新了。解决办法首先是使用几何距离替换函数距离。几何距离是通过几何关系方程组求出来的,同时几何距离也可以看作是函数距离的归一化:。使用几何距离解决了第二个问题,但是第一个问题仍然存在,解决办法是将使得函数距离等于1,这样除非,只要变化,距离也会变化。注意这里的函数距离是针对支持向量而言的,其他更远的点的函数距离>1,所以SVM的约束条件:;目标函数:
为了求导时方便,将目标函数重写为
拉格朗日函数
这是含不等式约束的凸二次规划问题,首先转换为无约束的拉格朗日函数:
提到拉格朗日函数,构造方法很简单,但这个是什么来的呢,有什么意义呢?以等式约束为例,极值的情况很显然是约束函数与等高线相切的点,相切的位置有什么特点呢?就是等高线处的梯度方向和约束函数处的梯度反向是同向或者反向的:
这个方程和约束等式联立,就可以推导出我们常见的对拉格朗日函数求偏导使其为0的解法。
当约束条件是不等式约束时,.根据无约束时本身的最小值点与约束区域的相对位置有两种情况:一种情况是真实的最小值正好处于约束区域中,那么约束条件其实没起作用,;另一种是真实最小值处于约束区域之外,那么和等式约束一样,约束可行解也落在约束区域的边界上,。两种情况可以一起表示为:(松弛互补条件)。需要注意的是的选取,虽然第二种情况中和等式约束一样都处于边界,但是方向是不同的。真正的等式约束只需要在等式上,不关心梯度方向;而不等式约束不再是一条线,而是一个圈(区域),满足这个约束的点的梯度方向一定是向外的(梯度是增长最快的方向,约束区域内是小于0的,外面才是大于0的)。而真正的最小值点也在外面,意味着在处的梯度方向和在处的梯度方向是反向的。所以.https://www.cnblogs.com/ooon/p/5721119.html
以上所有约束共同构成了KKT条件,满足了KKT条件就可以直接使用拉格朗日乘子法进行求解。
刚才提到SVM的问题是凸二次规划问题,即便使用拉格朗日求解也比较低效。一般先将其转换为对偶问题,再使用SMO算法求解。
对偶问题
第二,第三项分别为等式约束和不等式约束。,所以原目标函数可以写为最小最大化形式:
所谓对偶,就是交换最小最大化的顺序。通过对偶性,为原始问题引入一个下界,。而当优化问题是凸优化问题并且满足KKT条件时,强对偶成立,这个不等式中的等号成立,即对偶问题与原问题的解相等。在SVM中, ,对和分别求导并使其等于0,可以得到,
最终的分类函数
优化目标函数
表示所有样本的个数(不等式的个数),因为,所以很明显只有当大于0时才会对超平面的参数产生影响,这些大于0的样本点被称作支持向量。支持向量的个数是求解得到的(非零的数目),而这个数目最小为1,因为可以由反证法,如果所有等于0,则=0,这显然是不可能的。而当,这意味着对应的点处于不等式约束的边界上,所以
松弛变量
有些情况不是完全线性可分的,那么我们可以允许一些样本点“近似满足”约束条件。约束减轻,变成,。,这就是我们常见的hingle损失函数,通过损失函数,每个样本会对应一个松弛变量,在最优化时,所有松弛变量的和会以C加权,作为惩罚项。
如何从损失函数的角度理解SVM呢?看SVM的目标函数,目的是尽量让 超平面和两类样本点的距离最远,如果距离越近,就认为损失越大,对超平面的影响也越大。这也可以解释为什么只有支持向量对超平面的确定起作用,而其余样本点对应的为0,本质原因就是其余样本点离超平面足够远,loss为0。所以保证了支持向量机解的稀疏性。
具体到合页损失。特点1:最大值为1,这对应了我们之前的约定,函数间隔为1。那么什么时候取最大值呢,就是样本点落在超平面上,这时候当然最难分类。特点2:函数间隔大于0时损失函数为0。这也解释了稀疏性。当完成优化,样本点的函数距离最近为1,损失函数为0。特点3:损失函数大小在一个区间内线性增长,函数距离越接近0,损失函数越大。
每一个样本会对应一个松弛变量,不同样本的松弛变量应该是不同的,如果本来函数间隔已经大于等于1,那么它其实不需要添加松弛变量,所以对应的松弛变量为0;如果小于1,那么松弛变量就等于。可以看到,松弛变量正好就是每个样本对应的hinge损失。
损失函数的优化其实是通过约束条件来表示的,当超平面不断调整到满足约束条件,损失函数达到最小。当使用合页损失作为松弛变量后,会直接使得每个样本满足约束条件,这样太极端了,会使得超平面无法迭代优化。所以在添加松弛变量后,需要在目标函数中增加一个惩罚项,该惩罚项由松弛变量和或者和平方组成,并且有一个惩罚因子作系数,从而约束松弛变量的大小和离群点的个数。
核函数
对于松弛变量也无法解决的非线性可分问题,可以使用非线性映射,本质是通过坐标变换到另外一个坐标空间,在新的坐标空间中会是线性可分的。从的表达式,进而到最终的目标函数和分类函数,都出现了样本点的内积。所以可以不显式地进行非线性映射,而是使用核函数代替内积。https://zhuanlan.zhihu.com/p/31886934
高斯核是最常见的一种核函数。一般当特征数目比较小(1~1000),样本数目适中时(10~10000),都是使用高斯核函数效果更好、
可以证明高斯核可以写成的形式,而因为e指数的泰勒展开,其实是映射到了无穷维https://www.cnblogs.com/makefile/p/taylor-expand.html
判断一个函数是否可以作核函数,可以参考Merce's theorem,它要求该函数是半正定函数。
除了高斯核函数,还经常见的是线性核函数。可能会有些奇怪,线性核函数的作用是什么,经过线性核函数映射之后坐标空间没有发生变化啊。确实线性核函数没有升维的功能,它更多是为了统一线性与非线性问题,使得目标函数都使用核函数的形式代替内积。
越小,则高斯函数越集中,集中在哪个位置呢:看最终的决策函数,高斯函数以当前的测试样本为中心。所以高斯核的一个作用其实是对支持向量进行加权,离测试样本更近的支持向量会有更大的权重。
再看优化目标函数,当很小,高斯函数接近0,那么,在SVM中,支持向量个数会更小,而在SVDD中,每一个样本都会是支持向量,因为,
当核函数是标准高斯函数时,是不是意味着样本数据就不用归一化了呢?貌似是这样的,因为高斯核映射之后的结果只和样本间的距离有关,特别地,,所以实际上在映射之后的新空间中,每个样本的模都是1.
https://blog.csdn.net/lujiandong1/article/details/46386201
代码实战C++(OpenCV)
在opencv的安装路径中可以找到示例:opencv\sources\samples\cpp\tutorial_code\ml.关键代码其实只有几行,其中一个结构体是CvSVMParams,现在介绍几个关键的成员变量。
svm_type=C_SVC
由于opencv中的svm分类算法是根据libsvm改写而来的,libsvm是台湾一学者编写的matlab版本的svm算法,所以参数的设定的也大致相同。svm类型除了C_SVC之外,还有NU_SVC,ONE_CLASS,EPS_SVR,NU_SVR.
C_SVC和NU_SVC其实是一种,只不过参数不同。SVM通过超平面划分样本,二维时超平面可以使用解析式表达,高维时其实是使用支持向量表示的,而支持向量的数目就是一个超参数,C_SVC中的超参数C就表示支持向量数目,取值范围是任意正整数,而NU_SVC中超参数用NU表示,取值范围在[0,1],A nice property of nu is that it is related to the ratio of support vectors and the ratio of the training error.还有一个单分类器,顾名思义,将样本分为类别A和不属于A,训练数据只有一种数据,分类器学习这种类别的特征,只要和这一类A差别较大,不管是B~Z中的任何一个,都认为是另外一类。
SVM一般用来分类,SVR则可以实现回归。因为分类和回归本质上只是数据类型不一样,一个是离散的,一个是连续的,表现在编程中就是分类可以使用CV_32FC1或者CV_32SC1,而回归只能使用CV_32FC1
kernel_type=SVM::LINEAR
核函数的类型,SVM能处理线性不可分的情况就依赖于核函数。SVM::LINEAR,默认就是最简单的线性分类器,枚举定义为0;SVM::POLY,枚举定义为1;SVM::RBF径向基核函数: ,枚举定义为2;SIGMOD核函数,枚举定义为3
term_crit=TermCriteria(CV_TERMCRIT_ITER,(int)le7,1e-6)
迭代的终止条件,可以通过函数cvTermCriteria确定迭代次数和误差。这里迭代的算法就是著名的SMO
训练很简单,使用train函数,前两个参数是Mat型的训练数据和标签,第三四个参数是mask,当需要将所有的训练样本应用到训练中时,可以传入NULL或者Mat()表示应用所有数据,最后一个参数是刚才定义的参数。
对应的还有一个train_auto函数,前几个参数可以和train函数一样,后面都可以使用缺省值。但是要注意的是自动训练过程中对于参数的选择其实是k折交叉验证的一个过程,默认定义是10折交叉验证,如果训练样本数目太小,就会报错:badargument。折数和所需训练样本的数目的对应关系我还没有搞清楚,有的地方说10折的话每类最少两个样本。
预测则使用predict函数,将待预测的Mat类型样本传入,得到预测的标签。如果我们将(512,512)范围内的点依次传入,就可以得到每个点的预测标签,如果分别显示成不同的颜色,就会看到一个分界线。刚才说过,这个分界线其实可以通过支持向量确定,那么我们可以看看支持向量是什么个情况,有多少个支持向量,支持向量的分布。get_support_vector_count返回支持向量的数目,get_support_vector(i)就可以返回每个支持向量的指针。问题是我的代码中支持向量数目一直等于1,画出的位置也一直在坐标原点的位置,怀疑是OpenCV2.4.9版本的问题,因为同样的代码其他人的支持向量就是正常的。
svm.save("svmModel.xml")就可以保存SVM模型,保存成xml直接用浏览器就可以打开,也可以保存成.mat格式,但是使用matlab不能直接打开。那么我们看看SVM模型保存了什么。像深度学习一样,SVM保存了一些超参数,就是我们最开始初始化的那些,SVM的类型,核函数类型,迭代次数和终止时的误差,C值等。包括样本的维度,分类的数目。SVM的分类本质是找到超平面,这个超平面就是由支持向量决定的,所以xml文件里面保存了支持向量,sv_count就是支持向量的数目,每个支持向量都是训练样本的其中之一,坐标都被保存下来。此外还保存了一个rho值(判决函数的常数项),还有若干个alpha值和index值。
二维平面中,超平面退化为一个线段,线性可分时是直线,rho在这里就是截距,wx+b=0中的b。二分类时,rho的个数是1,分类类别数目是n时,rho的数目m=n*(n-1)/2.
预测SVM中有预测函数predict,因为SVM有不同类型(分类回归等 ),predict的实现方式也不尽相同。对分类来说,典型的分类是二分类,为了实现多分类,其实也在拆分成两两一对进行比较,再在全局选出一个投票得分最多的作为最后的分类。对于其中的二分类,需要遍历所有的支持向量,sum += df->alpha[k]*buffer[df->sv_index[k]],df是决策函数,buffer是核函数,sum初始化为-rho,所以这句代码的意思就是在与支持向量个数相同的维度上把核函数代入决策函数计算。对于回归则比较简单,只有一层对于支持向量的循环。for( i = 0; i < sv_count; i++ ),sum += buffer[i]*df->alpha[i];
核函数将低维数据映射到高维空间,使得原来线性不可分的数据线性可分,同时找到了一种等价的方法,原始数据代入核函数的结果等价于映射到高维之后的内积,低维的计算结果等价于高维的计算结果,避免高维空间中内积的巨大计算量。以多项式核函数为例,其实是将原始数据进行扩展,增加了一些平方,或者交叉相乘x1x2,不仅会有升维时增加的乘法加法,计算内积时同样增加了乘法加法的次数。而我们可以调整原始数据求内积时的系数和偏置量得到近似的效果。而我们最经常使用的是高斯核函数,也叫径向基函数RBF。高斯核函数的形式和高斯函数一样,为什么映射到的维度是无穷维呢?主要是因为e指数函数,因为e指数的泰勒展开是无穷维的。虽然高斯核对应的映射之后是无穷维的,但是我们可以控制gamma控制维度,gamma大时衰减大,维度就是一定的,这也反而成为了高斯核的优势,可以通过这一超参防止过拟合。同时还有线性核,其实本质上没有升维,还是线性分类,只不过这也就把线性核非线性通过核函数统一起来了。
高维空间中的决策超平面和二维中的决策线其实是差不多的,都是斜率+截距的形式:f(x0=Ogima.inv()x+b,Omiga是拉格朗日参数和标签yi与支持向量xi的乘积求和,从而转为内积的形式,内积又可以使用核函数近似求得。
代码实战Python(Sklearn)
class sklearn.svm.
SVC
(C=1.0, kernel='rbf', degree=3, gamma='auto', coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape='ovr', random_state=None)
C表示惩罚因子,就是松弛变量的权重。C越小,对松弛的容忍度越强,允许更多的离群点,泛化能力越强,但可能欠拟合。
gamma=1/(2*sigma^2),gamma越大,sigma越小,高斯函数越集中分布,支持向量的影响范围越小,可能过拟合。C和Gamma都是越大越容易过拟合。https://www.cnblogs.com/solong1989/p/9620170.html
opencv实现svm的方法保存的模型可以直接看到支持向量等参数,sklearn的模型是十六进制文件,不过我们可以使用函数保存各种参数。clf.support_vectors_,clf.dual_coef,保存alpha和类别标签的乘积,clf.intercept,保存截距b。
使用GridSearch可以在给定的范围内寻找最优的超参数。高斯核函数对超参数的选择比较敏感,所以二者可以结合。pd.Dataframe.from_dict(clf.cv_results_)就可以保存不同参数组合的情况,这里cv指的是交叉验证,评价标准和clf分类器选择的标准一样。具体而言,选择文件中mean_test_score最大的那个组合作为最优解。
注意要归一化。涉及到求距离一般都要归一化,防止哪个维度取值过大造成的影响。具体表现就是训练出来的模型输出值全1或者全0.
http://blog.sina.com.cn/s/blog_6e32babb0102xpip.html
https://www.cnblogs.com/denny402/p/5019233.html
https://www.cnblogs.com/chenzhefan/p/7662315.html
https://www.cnblogs.com/chenzhefan/p/7662315.html
三层境界https://blog.csdn.net/v_JULY_v/article/details/7624837?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
源码分析https://blog.csdn.net/zhaocj/article/details/51297907
bad argumenthttps://answers.opencv.org/question/26711/opencv-error-bad-argument/
任毅http://www.uml.org.cn/ai/202002144.asp?artid=22949
核函数内积https://www.jianshu.com/p/028d1883ad93