支持向量机(SVM)
支持向量机是一种用于分类的学习模型:群体中的哪些个体属于哪个类别?那么……SVM 和神秘的“核函数”是如何工作的呢?
下面我像对待五岁小孩一样给你解释 SVM:
在很久以前的情⼈节,⼤侠要去救他的爱⼈,但魔⻤和他玩了⼀个游戏。
魔⻤在桌⼦上似乎有规律放了两种颜⾊的球,说:
“你⽤⼀根棍分开它们?要求:尽量在放更多球之后,仍然适⽤。”
于是⼤侠这样放,⼲的不错?
然后魔⻤,⼜在桌上放了更多的球,似乎有⼀个球站错了阵营。
怎么办??
把分解的⼩棍⼉变粗。
SVM就是试图把棍放在最佳位置,好让在棍的两边有尽可能⼤的间隙
现在即使魔⻤放了更多的球,棍仍然是⼀个好的分界线。
然后,在SVM ⼯具箱中有另⼀个更加重要的技巧( trick)。 魔⻤看到⼤侠已经学会了⼀个trick,于是魔⻤给了⼤侠⼀个新的挑战。
现在,⼤侠没有棍可以很好帮他分开两种球了,现在怎么办呢?
当然像所有武侠⽚中⼀样⼤侠桌⼦⼀拍,球⻜到空中(不同颜色的球,可能存在重量和弹性等我们无法观察到特征,大侠拍桌子之后,这些球弹起的高度也不同)。然后,凭借⼤侠的轻功,⼤侠抓起⼀张纸,插到了两种球的中间。
从魔⻤的⻆度看这些球,这些球看起来像是被⼀条曲线分开了。
再之后,⽆聊的⼤⼈们,把上⾯的物体起了别名:
- 球—— 「data」数据
- 棍⼦—— 「classifier」分类
- 最⼤间隙——「optimization」最优化
- 拍桌⼦——「kernelling」核⽅法(增加数据维度,低维不可分时,高纬度可能可分)
- 纸——「hyperplane」超平⾯
案例来源:
http://bytesizebio.net/2014/02/05/support-vector-machines-explained-well/
⽀持向量机直观感受:https://www.youtube.com/watch?v=3liCbRZPrZA
相关定义
SVM算法定义
SVM:SVM全称是supported vector machine(支持向量机),即寻找到⼀个超平⾯使样本分成两类,并且间隔最大。
SVM能够执⾏线性或⾮线性分类、回归,甚⾄是异常值检测任务。它是机器学习领域最受欢迎的模型之⼀。SVM特别适⽤于中⼩型复杂数据集的分类。
- 超平面: 在高维特征空间中分隔数据的决策边界。形式是
w·x + b = 0
,其中w
是法向量,决定了超平面的方向,b
是偏置项,决定了超平面的位置。 - 支持向量: 那些恰好位于最大间隔边界上的训练数据点。它们是训练集中最关键的点,定义了分隔超平面的位置和方向(因为移动或移除非支持向量的点不会影响最终的决策边界)。移除所有非支持向量的点,重新训练模型,得到的决策边界是相同的。所有其他点都可以被安全地忽略。
- 间隔: 两个平行于决策边界且穿过支持向量的超平面(
w·x + b = 1
和w·x + b = -1
)之间的距离。这个距离计算公式是2 / ||w||
。最大化间隔等价于最小化||w||²
(或等价地最小化||w||
)。
超平⾯最⼤间隔介绍
左图显示了三种可能的线性分类器的决策边界:
- 虚线所代表的模型表现⾮常糟糕,甚⾄都⽆法正确实现分类。其余两个模型在这个训练集上表现堪称完美,但是它们的决策边界与实例过于接近,导致在面对新实例时,表现可能不会太好。
右图中的实线代表SVM分类器的决策边界,不仅分离了两个类别,且尽可能远离最近的训练实例。
硬间隔和软间隔
硬间隔分类
在上⾯我们使⽤超平⾯进⾏分割数据的过程中,如果我们严格地让所有实例都不在最⼤间隔之间,并且位于正确的⼀边,这就是硬间隔分类。
硬间隔分类有两个问题,⾸先,它只在数据是线性可分离的时候才有效;其次,它对异常值⾮常敏感。
当有⼀个额外异常值的鸢尾花数据:左图的数据根本找不出硬间隔,⽽右图最终显示的决策边界与我们之前所看到的⽆异常值时的决策边界也⼤不相同,可能⽆法很好地泛化。
软间隔分类
要避免这些问题,最好使⽤更灵活的模型。目标是尽可能在保持最⼤间隔宽阔和限制间隔违例(即位于最大间隔之上, 甚⾄在错误的⼀边的实例)之间找到良好的平衡,这就是软间隔分类。
在Scikit-Learn的SVM类中,可以通过超参数C来控制这个平衡:C值越⼩,则间隔越宽,但是间隔违例也会越多。上图显示了在⼀个⾮线性可分离数据集上,两个软间隔SVM分类器各⾃的决策边界和间隔。
左边使⽤了⾼C值,分类器的错误样本(间隔违例)较少,但是间隔也较⼩。
右边使⽤了低C值,间隔⼤了很多,但是位于间隔上的实例也更多。看起来第⼆个分类器的泛化效果更好,因为⼤多数间隔违例实际上都位于决策边界正确的⼀边,所以即便是在该训练集上,它做出的错误预测也会更少。
软间隔的目的就是在间隔距离和错误大小之间找到一个平衡。
SVM算法原理
1 定义输⼊数据
假设给定⼀个特征空间上的训练集为:
其中,(xi, yi)称为样本点。
- xi为第i个实例(样本),
- yi为xi的标记:
- 当yi = 1时,xi为正例
- 当yi = −1时,xi为负例
⾄于为什么正负⽤(-1,1)表示呢?
其实这⾥没有太多原理,就是⼀个标记,你也可以⽤(2,-3)来标记。只是为了⽅便,yi/yj = yi ∗ yj的过程中刚好可以相等,便于之后的计算。)
2 线性可分支持向量机
给定了上⾯提出的线性可分训练数据集,通过间隔最⼤化得到分离超平⾯为 :y(x) = wT Φ(x) + b
相应的分类决策函数为: f(x) = sign(w T Φ(x) + b)
sign函数是一个用于判断数字符号的数学和计算机函数,在不同领域(如数据库、Excel、MATLAB等)中均有应用,其核心规则为:输入正数返回1,负数返回-1,零返回0。
以上决策函数就称为线性可分⽀持向量机。
这⾥解释⼀下Φ(x)这个东东。
这是某个确定的特征空间转换函数,它的作⽤是将x映射到更⾼的维度,它有⼀个以后我们经常会⻅到的专有称号”核函数“。
比如我们看到的特征有2个:
x1, x2,组成最先⻅到的线性函数可以是
但也许这两个特征并不能很好地描述数据,于是我们进⾏维度的转化,变成了
于是我们多了三个特征。⽽这个就是笼统地描述x的映射的。
最简单直接的就是:Φ(x) = x
以上就是线性可分⽀持向量机的模型表达式。我们要去求出这样⼀个模型,或者说这样⼀个超平⾯y(x),它能够最优地分离两个集合。
其实也就是我们要去求⼀组参数(w,b),使其构建的超平⾯函数能够最优地分离两个集合。
如下就是⼀个最优超平⾯:
注意:向量w是超平面的法向量:
从下面得出,w和决策超平面垂直,也就是说w是决策超平面的法向量:
3 SVM的计算过程与算法步骤
3.1 推导目标函数
我们知道了⽀持向量机是个什么东⻄了。现在我们要去寻找这个⽀持向量机,也就是寻找⼀个最优的超平⾯。
于是我们要建⽴⼀个⽬标函数。那么如何建⽴呢?
再来看⼀下我们的超平⾯表达式:y(x) = w T Φ(x) + b
为了⽅便我们让:Φ(x) = x
则在样本空间中,划分超平⾯可通过如下线性⽅程来描述:w T x + b = 0
- 我们知道w=(w1,w2...wd)为法向量,决定了超平⾯的⽅向;
- b为位移项,决定了超平⾯和原点之间的距离。
- 显然,划分超平⾯可被法向量w和位移b确定,我们把其记为(w,b).
样本空间中任意点x到超平⾯(w,b)的距离可写成
推导:
超平面定义
超平面是 N 维空间中的 N−1 维子空间,其一般方程为:wTx+b=0
- w:法向量,垂直于超平面(决定方向)
- b:截距项,控制超平面偏移原点
设超平面上某点 x0 满足 wTx0+b=0。点 x 到 x0 的向量为 x−x0。
1. 计算投影距离
点 x 到超平面的距离 r 等于向量 x−x0 在法向量 w 方向上的投影长度:
几何解释:垂直距离是向量投影到法向量方向的长度。
2. 代入超平面方程
由 wTx0=−b,代入得:
wT(x−x0)=wTx−(−b)=wTx+b
3. 合并结果
说明:绝对值确保距离非负,符合几何意义。
几何直观演示
示例(二维空间)
- 超平面: 2x1+3x2−6=0(法向量 w=(2,3)T)
- 点 x=(1,1)
计算步骤:
- 求分子:∣2⋅1+3⋅1−6∣=∣−1∣=1
- 求分母:∥w∥=22+32=13
- 距离:r=
验证:通过作图可直观看到点 (1,1) 到直线的垂直距离为
,与公式一致。
拓展:什么是∣∣w∣∣?(我感觉就是法向量模长)
||w||:范数,类似L1范数,L2范数
链接:向量与矩阵的范数
设超平⾯(w, b)能将训练样本正确分类,即对于( x i , y i ) ∈ D,
- 若y = +1,则有w Tx + b > 0;
- 若y = −1,则有w Tx + b < 0;
令
如图所示,距离超平⾯最近的⼏个训练样本点使上式等号成⽴(w Tx + b=1或-1),他们被称为“支持向量"
两个异类⽀持向量到超平⾯的距离之和为:
它被称为“”间隔“”。
推导过程:
继续推导,得出,求间隔最大宽度L的问题其实就是求||w||的最小化问题:
欲找到具有最⼤间隔的划分超平⾯,也就是要找到能满⾜下式中约束的参数w和b,使得γ最⼤。
即:
显然,为了最⼤化间隔,仅需要最⼤化,这等价于最⼩化
。于是上式可以重写为:
这就是⽀持向量机的基本型。
3.2 ⽬标函数的求解
到这⼀步,终于把⽬标函数给建⽴起来了。
那么下⼀步⾃然是去求⽬标函数的最优值.
因为⽬标函数带有⼀个约束条件,所以我们可以⽤拉格朗⽇乘⼦法求解。
3.2.1 朗格朗⽇乘⼦法
啥是拉格朗⽇乘⼦法呢?
拉格朗⽇乘⼦法 (Lagrange multipliers)是⼀种寻找多元函数在⼀组约束下的极值的⽅法.
通过引⼊拉格朗⽇乘⼦,可将有 d 个变量与 k 个约束条件的最优化问题转化为具有 d + k 个变量的⽆约束优化问题求解。
根据两条间隔边界的函数,可以得出我们求解||w||最小值时的约束条件:
经过朗格朗⽇乘⼦法,我们可以把⽬标函数转换为:
其中,要想求得极⼩值,上式后半部分:
⾛到这⼀步,这个⽬标函数还是不能开始求解,现在我们的问题是极⼩极⼤值问题
3.2.2 对偶问题(更好的翻译为:伴随问题)
我们要将其转换为对偶问题,变成极⼤极⼩值问题:
从变为:
以简单案例理解对偶:最小值里面求取的最大值等于最大值里面求取的最小值
根据拉格朗日乘数法得到:
也就是x=λ/2时,f(x)=x2取得最小值,所以,f(x)=x2在约束条件x-1≥0的情况下,最小值随λ取值的变化而变化,
对偶成立,所以我们可以通过求解原问题的对偶问题来解决原问题。
如何获取对偶函数?
- ⾸先我们对原⽬标函数的w和b分别求导:
- 原⽬标函数:
-
- 对w求偏导:
-
- 对b求偏导:
- 然后将以上w和b的求导函数重新代⼊原⽬标函数的w和b中,得到的就是原函数的对偶函数
这个对偶函数其实求的是:中的minL(w, b)部分。
于是现在要求的是这个函数的极⼤值max(a),写成公式就是:
好了,现在我们只需要对上式求出极⼤值α,然后将α 代⼊w求偏导的那个公式:
从⽽求出w.
将w代⼊超平⾯的表达式,计算b值;
现在的w,b就是我们要寻找的最优超平⾯的参数。
3.2.3 整体流程确定
我们⽤数学表达式来说明上⾯的过程:
1)⾸先是求的极⼤值。即:
注意有两个约束条件。
对⽬标函数添加符号,转换成求极⼩值:
2)计算上⾯式⼦的极值求出α ∗ ;
3)将α ∗代⼊,计算w,b
4)求得超平⾯:
5)求得分类决策函数:
4 举例
给定3个数据点:正例点x1 = (3, 3), x2 = (4, 3),负例点x3 = (1, 1),求线性可分⽀持向量机。 三个点画出来:
1) ⾸先确定⽬标函数
2) 求得⽬标函数的极值
原式:
把数据代⼊:
由于α1+α2=α3
化简可得:
对α1 , α 2求偏导并令其为0,易知s(α1 , α 2)在点(1.5, -1)处取极值。
⽽该点不满⾜条件α 2>= 0,所以,最⼩值在边界上达到。
- 当α1 = 0时,最⼩值s(0,2/13 ) = − 2/13= −0.1538
- 当α2 = 0时,最⼩值s(1/4 , 0) = −1/4 = −0.25
于是,s(α1 , α 2 )在α 1= 0, α 2= 0时达到最⼩,此时:
α 3= α 1+ α 2=1/4
3) 将求得的极值代⼊从⽽求得最优参数w,b
α 1= α 3= 1/4对应的点x 1, x 3就是⽀持向量机
代⼊公式:
将α结果代⼊求解:
选择α的⼀个⽀持向量的正分量α j> 0进⾏计算
平⾯⽅程为:0.5x 1+ 0.5x 2 − 2 = 0
4) 因此得到分离超平⾯为
0.5x 1+ 0.5x 2 − 2 = 0
5) 得到分离决策函数为:
f(x) = sign(0.5x 1+ 0.5x 2 − 2)
ps:参考的另⼀种计算⽅式: https://blog.csdn.net/zhizhjiaodelaoshu/article/details/97112073
可视化理解:
SVM的损失函数
在SVM中,我们主要讨论三种损失函数:
绿⾊:0/1损失
- 当正例的点落在y=0这个超平⾯的下边,说明是分类正确,⽆论距离超平⾯所远多近,误差都是0.
- 当这个正例的样本点落在y=0的上⽅的时候,说明分类错误,⽆论距离多远多近,误差都为1.
- 图像就是上图绿⾊线。
蓝⾊:SVM Hinge损失函数
- 当⼀个正例的点落在y=1的直线上,距离超平⾯⻓度1,那么1-ξ=1,ξ=0,也就是说误差为0;
- 当它落在距离超平⾯0.5的地⽅,1-ξ=0.5,ξ=0.5,也就是说误差为0.5;
- 当它落在y=0上的时候,距离为0,1-ξ=0,ξ=1,误差为1;
- 当这个点落在了y=0的上⽅,被误分到了负例中,距离算出来应该是负的,⽐如-0.5,那么1-ξ=-0.5,ξ=-1.5.误差为1.5.
- 以此类推,画在⼆维坐标上就是上图中蓝⾊那根线了。
红⾊:Logistic损失函数
- 损失函数的公式为:ln(1 + e−yi)
- 当yi = 0时,损失等于ln2,这样真丑,所以我们给这个损失函数除以ln2.
- 这样到yi = 0时,损失为1,即损失函数过(0,1)点
- 即上图中的红⾊线。
拓展学习:PPT讲义'SVM-loss.pptx'
SVM的核方法
【SVM + 核函数】 具有极⼤威⼒。
核函数并不是SVM特有的,核函数可以和其他算法也进⾏结合,只是核函数与SVM结合的优势⾮常⼤。
1 什么是核函数
1.1 核函数概念
核函数,是将原始输⼊空间映射到新的特征空间,从⽽,使得原本线性不可分的样本可能在核空间可分。
下图所示的两类数据,分别分布为两个圆圈的形状,这样的数据本身就是线性不可分的,此时该如何把这两类数据分开呢?
- 假设X是输⼊空间,
- H是特征空间,
- 存在⼀个映射ϕ使得X中的点x能够计算得到H空间中的点h,
- 对于所有的X中的点都成⽴:
若x,z是X空间中的点,函数k(x,z)满⾜下述条件,那么都成⽴,则称k为核函数,⽽ϕ为映射函数:
核函数专项学习参照:核方法、核优化、核函数
常⻅核函数
1.多项核中,d=1时,退化为线性核;
2.⾼斯核亦称为RBF核。
线性核和多项式核:
- 这两种核的作⽤也是⾸先在属性空间中找到⼀些点,把这些点当做base,核函数的作⽤就是找与该点距离和⻆度满⾜某种关系的样本点。
- 当样本点与该点的夹⻆近乎垂直时,两个样本的欧式⻓度必须⾮常⻓才能保证满⾜线性核函数⼤于0;⽽当样本点与base点的⽅向相同时,⻓度就不必很⻓;⽽当⽅向相反时,核函数值就是负的,被判为反类。即,它在空间上划分出⼀个梭形(例如y=1/x的图像),按照梭形来进⾏正反类划分。
RBF核(高斯核):
- ⾼斯核函数就是在属性空间中找到⼀些点,这些点可以是也可以不是样本点,把这些点当做base,以这些base为圆⼼向外扩展,扩展半径即为带宽,即可划分数据。
- 换句话说,在属性空间中找到⼀些超圆,⽤这些超圆来判定正反类。
Sigmoid核:
- 同样地是定义⼀些base,
- 核函数就是将线性核函数经过⼀个tanh函数进⾏处理,把值域限制在了-1到1上。
总之,都是在定义距离,⼤于该距离,判为正,⼩于该距离,判为负。⾄于选择哪⼀种核函数,要根据具体的样本分布情况来确定。
⼀般有如下指导规则:
- 1) 如果Feature的数量很⼤,甚⾄和样本数量差不多时,往往线性可分,这时选⽤LR或者线性核Linear;
- 2) 如果Feature的数量很⼩,样本数量正常,不算多也不算少,这时选⽤RBF核;
- 3) 如果Feature的数量很⼩,⽽样本的数量很⼤,这时⼿动添加⼀些Feature,使得线性可分,然后选⽤LR或者线性核Linear;
- 4) 多项式核⼀般很少使⽤,效率不⾼,结果也不优于RBF;
- 5) Linear核参数少,速度快;RBF核参数多,分类结果⾮常依赖于参数,需要交叉验证或⽹格搜索最佳参数,⽐较耗时;
- 6)应⽤最⼴的应该就是RBF核,⽆论是⼩样本还是⼤样本,⾼维还是低维等情况,RBF核函数均适⽤。
总结:
SVM的核⽅法:将原始输⼊空间映射到新的特征空间,从⽽,使得原本线性不可分的样本可能在核空间可分。
SVM回归
- 了解SVM回归的实现原理
SVM回归是让尽可能多的实例位于预测线上,同时限制间隔违例(也就是不在预测线距上的实例)。
线距的宽度由超参数ε控制。
SVM算法api
SVM算法api综述
- SVM⽅法既可以⽤于分类(⼆/多分类),也可⽤于回归和异常值检测。
- SVM具有良好的鲁棒性,对未知数据拥有很强的泛化能⼒,特别是在数据量较少的情况下,相较其他传统机器学习算法具有更优的性能。
使⽤SVM作为模型时,通常采⽤如下流程:
- 对样本数据进⾏归⼀化
- 应⽤核函数对样本进⾏映射(最常采⽤和核函数是RBF和Linear,在样本线性可分时,Linear效果要比RBF好)
- ⽤cross-validation和grid-search对超参数进⾏优选
- ⽤最优参数训练得到模型
- 测试
sklearn中⽀持向量分类主要有三种⽅法:SVC、NuSVC、LinearSVC,扩展为三个⽀持向量回归⽅法:SVR、NuSVR、LinearSVR。
- SVC和NuSVC⽅法基本⼀致,唯⼀区别就是损失函数的度量⽅式不同
- NuSVC中的nu参数和SVC中的C参数;
- LinearSVC是实现线性核函数的⽀持向量分类,没有kernel参数。
SVC
class sklearn.svm.SVC(C=1.0, kernel='rbf', degree=3,coef0=0.0,random_state=None)
C: 惩罚系数,⽤来控制损失函数的惩罚系数,类似于线性回归中的正则化系数。
- C越⼤,相当于惩罚松弛变量,希望松弛变量接近0,即对误分类的惩罚增⼤,趋向于对训练集全分对的情况,这样会出现训练集测试时准确率很⾼,但泛化能⼒弱,容易导致过拟合。
- C值⼩,对误分类的惩罚减⼩,容错能⼒增强,泛化能⼒较强,但也可能⽋拟合。
kernel: 算法中采⽤的核函数类型,核函数是⽤来将⾮线性问题转化为线性问题的⼀种⽅法。
- 参数选择有RBF, Linear, Poly, Sigmoid或者⾃定义⼀个核函数。
- 默认的是"RBF",即径向基核,也就是⾼斯核函数;
- ⽽Linear指的是线性核函数,
- Poly指的是多项式核,
- Sigmoid指的是双曲正切函数tanh核;。
degree:
- 当指定kernel为'poly'时,表示选择的多项式的最⾼次数,默认为三次多项式;
- 若指定kernel不是'poly',则忽略,即该参数只对'poly'有⽤。
- 多项式核函数是将低维的输⼊空间映射到⾼维的特征空间。
coef0: 核函数常数值(y=kx+b中的b值),
- 只有‘poly’和‘sigmoid’核函数有,默认值是0。
NuSVC
class sklearn.svm.NuSVC(nu=0.5)
nu: 训练误差部分的上限和⽀持向量部分的下限,取值在(0,1)之间,默认是0.5
LinearSVC
class sklearn.svm.LinearSVC(penalty='l2', loss='squared_hinge', dual=True, C=1.0)
penalty:正则化参数,
- L1和L2两种参数可选,仅LinearSVC有。
loss:损失函数,
- 有hinge和squared_hinge两种可选,前者⼜称L1损失,后者称为L2损失,默认是squared_hinge,
- 其中hinge是SVM的标准损失,squared_hinge是hinge的平⽅
dual:是否转化为对偶问题求解,默认是True。
C:惩罚系数,
- ⽤来控制损失函数的惩罚系数,类似于线性回归中的正则化系数。
⼩结
SVM算法api
- sklearn.svm.SVC
- sklearn.svm.NuSVC
- sklearn.svm.LinearSVC
SVM算法api初步使用
>>> from sklearn import svm
>>> X = [[0, 0], [1, 1]]
>>> y = [0, 1]
>>> clf = svm.SVC()
>>> clf.fit(X, y)
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma='scale', kernel='rbf',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)
拟合后, 这个模型可以⽤来预测新的值
>>> clf.predict([[2., 2.]])
array([1])
案例:数字识别器
案例背景介绍
MNIST(Modified National Institute of Standards and Technology)是一个经典的机器学习数据集,主要用于手写数字识别任务,包含60,000张训练图像和10,000张测试图像,每张图像为28×28像素的灰度图,对应数字0-9的类别标签。
MNIST由美国国家标准技术研究所(NIST)原始数据集改进而来,是深度学习领域的入门级基准数据集。
MNIST是计算机视觉事实上的“hello world”数据集。⾃1999年发布以来,这⼀经典的⼿写图像数据集已成为分类算法基准测试的基础。随着新的机器学习技术的出现,MNIST仍然是研究⼈员和学习者的可靠资源。
注意:MNIST逐渐被Fashion-MNIST替代,详情参照:
本次案例中,我们的⽬标是从数万个⼿写图像的数据集中正确识别数字。
2 数据介绍
数据⽂件train.csv和test.csv包含从0到9的⼿绘数字的灰度图像。
每个图像的⾼度为28个像素,宽度为28个像素,总共为784个像素。
每个像素具有与其相关联的单个像素值,指示该像素的亮度或暗度,较⾼的数字意味着较暗。该像素值是0到255之间的整数,包括0和255。
训练数据集(train.csv)有785列。第⼀列称为“标签”,是⽤户绘制的数字。其余列包含关联图像的像素值。
训练集中的每个像素列都具有像pixelx这样的名称,其中x是0到783之间的整数,包括0和783。为了在图像上定位该像素,假设我们已经将x分解为x = i * 28 + j,其中i和j是0到27之间的整数,包括0和27。然后,pixelx位于28 x 28矩阵的第i⾏和第j列上(索引为零)。
例如,pixel31表示从左边开始的第四列中的像素,以及从顶部开始的第⼆⾏,如下⾯的ascii图中所示。
在视觉上,如果我们省略“像素”前缀,像素组成图像如下:
000 001 002 003 ... 026 027
028 029 030 031 ... 054 055
056 057 058 059 ... 082 083
| | | | ...... | |
728 729 730 731 ... 754 755
756 757 758 759 ... 782 783
测试数据集(test.csv)与训练集相同,只是它不包含“标签”列。
代码实现
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.model_selection import train_test_split
# 获取数据
train=pd.read_csv(r'D:\learn\000人工智能数据大全\经典数据集\MNIST\train.csv')
train.shape#(42000, 785)
train.head()
label | pixel0 | pixel1 | pixel2 | pixel3 | pixel4 | pixel5 | pixel6 | pixel7 | pixel8 | ... | pixel774 | pixel775 | pixel776 | pixel777 | pixel778 | pixel779 | pixel780 | pixel781 | pixel782 | pixel783 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
# 确定特征值\目标值
train_img=train.iloc[:,1:]
target=train.iloc[:,0]# 使用单个索引获取一维Series
#查看具体图像
num=train_img.loc[3].values.reshape(28,28)
num#train_img.loc[3]对应的标签是数字4
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 220, 179, 6, 0, 0, 0, 0,0, 0, 0, 0, 9, 77, 0, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 28, 247, 17, 0, 0, 0, 0,0, 0, 0, 0, 27, 202, 0, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 242, 155, 0, 0, 0, 0,0, 0, 0, 0, 27, 254, 63, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 160, 207, 6, 0, 0, 0,0, 0, 0, 0, 27, 254, 65, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 127, 254, 21, 0, 0, 0,0, 0, 0, 0, 20, 239, 65, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 77, 254, 21, 0, 0, 0,0, 0, 0, 0, 0, 195, 65, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 70, 254, 21, 0, 0, 0,0, 0, 0, 0, 0, 195, 142, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 56, 251, 21, 0, 0, 0,0, 0, 0, 0, 0, 195, 227, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 222, 153, 5, 0, 0,0, 0, 0, 0, 0, 120, 240, 13, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 67, 251, 40, 0, 0,0, 0, 0, 0, 0, 94, 255, 69, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 184, 0, 0,0, 0, 0, 0, 0, 19, 245, 69, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 169, 0, 0,0, 0, 0, 0, 0, 3, 199, 182, 10, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 154, 205, 4, 0,0, 26, 72, 128, 203, 208, 254, 254, 131, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 254, 129, 113, 186, 245, 251, 189, 75, 56, 136, 254, 73, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 216, 233, 233,159, 104, 52, 0, 0, 0, 38, 254, 73, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 18, 254, 73, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 18, 254, 73, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 5, 206, 106, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 186, 159, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 6, 209, 101, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0]], dtype=int64)
通过plt绘图可视化数据:
plt.imshow(num)
plt.axis("off")
plt.show()
def shownum(rowid):
num=train_img.loc[rowid].values.reshape(28,28)
plt.imshow(num)
plt.axis("off")
plt.show()
shownum(88)
shownum(66)
shownum(68)
# 数据基本处理
## 数据归一化处理
train_img=train_img.values/255 #.values:将数据转换为2D数组
target=target.values ## 将2D数组(DataFrame)转换为1D数组,否则模型训练会报警告
np.max(train_img)#1.0
## 数据集分割
train_x,test_x,train_y,test_y=train_test_split(train_img,target,random_state=66,test_size=0.2)
print(train_x.shape,train_y.shape,test_x.shape,test_y.shape)#(33600, 784) (33600, 1) (8400, 784) (8400, 1)
# 特征降维和模型训练
import time
from sklearn.decomposition import PCA
# 多次使用pca,确定最后的最优模型
def test_pca_svc(pca_weight,train_x,test_x,train_y,test_y):
start_time=time.time()
pca=PCA(n_components=pca_weight)
train_x_pca=pca.fit_transform(train_x)
test_x_pca=pca.transform(test_x)
svc=svm.SVC()
svc.fit(train_x_pca,train_y)
end_time=time.time()
score=svc.score(test_x_pca,test_y)
print(f'{p}对应的模型得分是{score}',';总耗时:',int(start_time-end_time),'秒')
return score,end_time-start_time
pca_weights=np.linspace(0.75,0.99,8)
scores=[]
times=[]
for p in pca_weights:
score,time_use=test_pca_svc(p,train_x,test_x,train_y,test_y)
scores.append(score)
times.append(time_use)
0.75对应的模型得分是0.9778571428571429 ;总耗时: -6 秒
0.7842857142857143对应的模型得分是0.9789285714285715 ;总耗时: -7 秒
0.8185714285714286对应的模型得分是0.9798809523809524 ;总耗时: -8 秒
0.8528571428571429对应的模型得分是0.9805952380952381 ;总耗时: -8 秒
0.8871428571428571对应的模型得分是0.9802380952380952 ;总耗时: -10 秒
0.9214285714285715对应的模型得分是0.9810714285714286 ;总耗时: -13 秒
0.9557142857142857对应的模型得分是0.9803571428571428 ;总耗时: -20 秒
0.99对应的模型得分是0.98 ;总耗时: -35 秒
# 准确率可视化
plt.plot(pca_weights,scores,label='模型得分')
plt.plot(pca_weights,times/(np.max(times)*100)+0.976,label='耗时')
plt.xlabel('PCA_参数n_components')
plt.legend()
plt.grid()
plt.show()
通过测试结果我们得出结论,PCA进行数据降维时,随着保留维度的增加,模型得分也会增加,但是模型训练耗时会显著增加;
经过图形展示,选择合理的n_components, 最后综合考虑确定结果为:0.925
扩展知识点
向量与矩阵的范数
阅读文献时,经常看到各种范数,机器学习中的稀疏模型等,也有各种范数,其名称往往容易混淆,例如:L1范数也常称为“1-范数”,但又和真正的1-范数又有很大区别。下面将依次介绍各种范数。
1.向量的范数
向量的1-范数:各个元素的绝对值之和;
向量的2-范数:每个元素的平方和再开平方根;
p-范数:,其中正整数p≥1,并且有
p-范数又称为 向量的无穷范数:
例:向量X=[2, 3, -5, -7] ,求向量的1-范数,2-范数和无穷范数。
向量的1-范数:各个元素的绝对值之和;=2+3+5+7=17;
Matlab代码:X=[2, 3, -5, -7]; XLfs1=norm(X,1);
向量的2-范数:每个元素的平方和再开平方根;;
Matlab代码:X=[2, 3, -5, -7]; XLfs2=norm(X,2);
向量的无穷范数:
(1)正无穷范数:向量的所有元素的绝对值中最大的;即X的正无穷范数为:7;
Matlab代码:X=[2, 3, -5, -7]; XLfsz=norm(X,inf);
(2)负无穷范数:向量的所有元素的绝对值中最小的;即X的负无穷范数为:2;
Matlab代码:X=[2, 3, -5, -7]; XLfsf=norm(X,-inf);
2.矩阵的范数
设:向量,矩阵
,例如矩阵A为:
A=[2, 3, -5, -7;
4, 6, 8, -4;
6,-11, -3, 16];
(1)矩阵的1-范数(列模):;矩阵的每一列上的元素绝对值先求和,再从中取个最大的,(列和最大);即矩阵A的1-范数为:27
Matlab代码:fs1=norm(A,1);
(2)矩阵的2-范数(谱模):,其中
为
的特征值;矩阵
的最大特征值开平方根。
Matlab代码:fs2=norm(A,2);
(3)矩阵的无穷范数(行模):;矩阵的每一行上的元素绝对值先求和,再从中取个最大的,(行和最大)
Matlab代码:fswq=norm(A,inf);
下面要介绍关于机器学习中稀疏表示等一些地方用到的范数,一般有核范数,L0范数,L1范数(有时很多人也叫1范数,这就让初学者很容易混淆),L21范数(有时也叫2范数),F范数等,这些范数都是为了解决实际问题中的困难而提出的新的范数定义,不同于前面矩阵的范数。
关于核范数,L0范数,L1范数等解释见博客:
http://www.cnblogs.com/MengYan-LongYou/p/4050862.html
https://blog.csdn.net/u013066730/article/details/51145889
http://blog.sina.com.cn/s/blog_7103b28a0102w73g.html
(4)矩阵的核范数:矩阵的奇异值(将矩阵svd分解)之和,这个范数可以用来低秩表示(因为最小化核范数,相当于最小化矩阵的秩——低秩);
Matlab代码:JZhfs=sum(svd(A));
(5)矩阵的L0范数:矩阵的非0元素的个数,通常用它来表示稀疏,L0范数越小0元素越多,也就越稀疏。
(6)矩阵的L1范数:矩阵中的每个元素绝对值之和,它是L0范数的最优凸近似,因此它也可以近似表示稀疏;
Matlab代码:JZL1fs=sum(sum(abs(A)));
(7)矩阵的F范数:矩阵的各个元素平方之和再开平方根,它通常也叫做矩阵的L2范数,它的有点在它是一个凸函数,可以求导求解,易于计算;
Matlab代码:JZFfs=norm(A,'fro');
(8)矩阵的L21范数:矩阵先以每一列为单位,求每一列的F范数(也可认为是向量的2范数),然后再将得到的结果求L1范数(也可认为是向量的1范数),很容易看出它是介于L1和L2之间的一种范数
Matlab代码:JZL21fs=norm(A(:,1),2) + norm(A(:,2),2) + norm(A(:,3),2)++ norm(A(:,4),2);
Matlab代码
clear all;clc;
%% 求向量的范数
X=[2, 3, -5, -7]; %初始化向量X
XLfs1=norm(X,1); %向量的1-范数
XLfs2=norm(X,2); %向量的2-范数
XLfsz=norm(X,inf); %向量的正无穷范数
XLfsf=norm(X,-inf); %向量的负无穷范数
%% 求矩阵的范数
A=[2, 3, -5, -7;
4, 6, 8, -4;
6, -11, -3, 16]; %初始化矩阵A
JZfs1=norm(A,1); %矩阵的1-范数
JZfs2=norm(A,2); %矩阵的2-范数
JZfswq=norm(A,inf); %矩阵的无穷范数
JZhfs=sum(svd(A)); %矩阵的核范数
JZL1fs=sum(sum(abs(A)));% 矩阵的L1范数
JZFfs=norm(A,'fro'); %矩阵的F范数
JZL21fs=norm(A(:,1),2) + norm(A(:,2),2) + norm(A(:,3),2)++ norm(A(:,4),2);% 矩阵的L21范数
原文链接:https://blog.csdn.net/zaishuiyifangxym/article/details/81673491
朗格朗日乘⼦法
拉格朗⽇乘⼦法 (Lagrange multipliers)是⼀种寻找多元函数在⼀组约束下的极值的⽅法.
通过引⼊拉格朗⽇乘⼦,可将有 d 个变量与 k 个约束条件的最优化问题转化为具有 d + k 个变量的无约束优化问题求解。
本⽂希望通过⼀个直观简单的例⼦尽⼒解释拉格朗⽇乘⼦法和KKT条件的原理。
以包含⼀个变量⼀个约束的简单优化问题为例。
如图所示,我们的⽬标函数是f(x) = x 2 + 4x − 1,讨论两种约束条件g(x):
- 1) 在满⾜x≤−1 约束条件下求⽬标函数的最⼩值;
- 2) 在满⾜ x≥−1约束条件下求⽬标函数的最⼩值。
我们可以直观的从图中得到,
- 对于约束 1) 使⽬标值f(x)最⼩的最优解是x=−2;
- 对于约束 2) 使⽬标值f(x)最⼩的最优解是x=−1。
下⾯我们⽤拉格朗⽇乘⼦来求解这个最优解。
当没有约束的时候,我们可以直接令⽬标函数的导数为0,求最优值。
可现在有约束,那怎么边考虑约束边求⽬标函数最优值呢?
- 最直观的办法是把约束放进目标函数⾥,由于本例中只有⼀个约束,所以引⼊⼀个朗格朗⽇乘⼦λ,构造⼀个新的函数,拉格朗⽇函数L(x,λ) ,
- L(x,λ) = f(x) + λg(x)
g(x)=x+1
Lx′=2x+4+λ=0
Lλ′=x+1=0
该拉格朗日函数h(x)最优解可能在g(x)<0区域中,或者在边界g(x)=0上,下⾯具体分析这两种情况,
- 当g(x)<0时,也就是最优解在g(x)<0区域中, 对应约束1) x≤−1的情况。此时约束对求⽬标函数最⼩值不起作⽤,等价于λ=0,直接通过条件∇f (x∗)=0,得拉格朗⽇函数h(x)最优解x=−2。
- 当g(x)=0时,也就是最优解在边界g(x)=0上,对应约束1) x≥−1的情况。此时不等式约束转换为等式约束,也就是在λ>0、约束起作⽤的情况下,通过求∇f (x∗)+∇g (x∗)=0,得拉格朗⽇函数L(x,λ) 最优解x=−1。
所以整合这两种情况,必须满⾜λg(x)=0
因此约束g(x)最⼩化f(x)的优化问题,可通过引⼊拉格朗⽇因⼦转化为在如下约束下,最⼩化拉格朗⽇函数h(x),
上述约束条件称为KKT条件。
该KKT条件可扩展到多个等式约束和不等式约束的优化问题。
关于KKT补充:
对拉格朗日函数求导得到
继续推导得到λi≥0:
至此我们得到五个条件,也就是KKT条件:
然后根据KKT条件就可以求决策超平面了