From 627ec87a1bbdccdb75c8cc2f0c091dca20ee7d81 Mon Sep 17 00:00:00 2001 From: jiangzhonglian Date: Thu, 21 Sep 2017 09:43:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=90=91=E9=87=8F=E6=9C=BA=20=E7=9A=84=E5=8E=9F=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/6.支持向量机.md | 143 +++++++++++---------- images/6.SVM/SVM_4_point2line-distance.jpg | Bin 0 -> 9041 bytes 2 files changed, 73 insertions(+), 70 deletions(-) create mode 100644 images/6.SVM/SVM_4_point2line-distance.jpg diff --git a/docs/6.支持向量机.md b/docs/6.支持向量机.md index 9d566177..5a7dcd2e 100644 --- a/docs/6.支持向量机.md +++ b/docs/6.支持向量机.md @@ -19,7 +19,7 @@ ## 支持向量机 原理 -### 支持向量机 工作原理 +### SVM 工作原理 ![k_2](/images/6.SVM/k_2.jpg "k_2") @@ -28,38 +28,15 @@ 1. 寻找最大分类间距 2. 转而通过拉格朗日函数求优化的问题 -### 基础介绍 - -* SVM有很多种实现,最流行的一种实现是: `序列最小优化(Sequential Minimal Optimization, SMO)算法`。 -* 下面还会介绍一种称为`核函数(kernel)`的方式将SVM扩展到更多数据集上。 -* 注意:`SVM几何含义比较直观,但其算法实现较复杂,牵扯大量数学公式的推导。` - * 数据可以通过画一条直线就可以将它们完全分开,这组数据叫`线性可分(linearly separable)`数据,而这条分隔直线称为`分隔超平面(separating hyperplane)`。 * 如果数据集上升到1024维呢?那么需要1023维来分隔数据集,也就说需要N-1维的对象来分隔,这个对象叫做`超平面(hyperlane)`,也就是分类的决策边界。 ![分隔超平面](/images/6.SVM/SVM_2_separating-hyperplane.jpg) +### 寻找最大间隔 - - -``` -优点:泛化(由具体的、个别的扩大为一般的,就是说:模型训练完后的新样本)错误率低,计算开销不大,结果易理解。 -缺点:对参数调节和核函数的选择敏感,原始分类器不加修改仅适合于处理二分类问题。 -使用数据类型:数值型和标称型数据。 -``` - -## 支持向量机 项目实战 - -### 项目实战: 手写数字识别的优化 - - - - - -## 寻找最大间隔 - -> 为什么寻找最大间隔 +#### 为什么寻找最大间隔 ``` 摘录地址:http://slideplayer.com/slide/8610144 (第12条信息) @@ -82,46 +59,48 @@ This is the simplest kind of SVM (Called an LSVM) Support Vectors are those data 5. 通常它的工作非常好。 ``` -### 怎么寻找最大间隔 +#### 怎么寻找最大间隔 > 点到超平面的距离 * 分隔超平面`函数间距`: \\(y(x)=w^Tx+b\\) * 分类的结果: \\(f(x)=sign(w^Tx+b)\\) (sign表示>0为1,<0为-1,=0为0) -* 点到超平面的`几何间距`: \\(d(x)=(w^Tx+b)/||w||\\) (||w||表示w矩阵的二范式=> \\(\sqrt{w*w^T}\\), 点到超平面的距离也是类似的,需要复习一下向量的知识) -![点到直线的几何距离](/images/6.SVM/SVM_4_point2line-distance.png) +* 点到超平面的`几何间距`: \\(d(x)=(w^Tx+b)/||w||\\) (||w||表示w矩阵的二范式=> \\(\sqrt{w*w^T}\\), 点到超平面的距离也是类似的) -> 拉格朗日乘子法的使用 +![点到直线的几何距离](/images/6.SVM/SVM_4_point2line-distance.jpg) + +> 拉格朗日乘子法 * 类别标签用-1、1,是为了后期方便 \\(lable*(w^Tx+b)\\) 的标识和距离计算;如果 \\(lable*(w^Tx+b)>0\\) 表示预测正确,否则预测错误。 * 现在目标很明确,就是要找到`w`和`b`,因此我们必须要找到最小间隔的数据点,也就是前面所说的`支持向量`。 * 也就说,让最小的距离取最大.(最小的距离:就是最小间隔的数据点;最大:就是最大间距,为了找出最优超平面--最终就是支持向量) * 怎么理解呢? 例如: 如果把所有的点看作地雷,那么我们(超平面)得找到最近所有的地雷,并保证我们离它最远。 - * 目标函数:\\(arg: max\ \{min\ [lable*(w^Tx+b)/||w||]\}\\) - * 1.如果 \\(lable*(w^Tx+b)>0\\) 表示预测正确,也称`函数间隔`,\\(||w||\\) 可以理解为归一化,也称`几何间隔`,我们始终可以找到一个阈值让 \\(lable*(w^Tx+b)>=1\\) - * 2.所以令 \\(lable*(w^Tx+b)=1\\),我们本质上是求 \\(arg: max\{关于w, b\}\ (1/||w||)\\);也就说,我们约束(前提)条件是: \\(lable*(w^Tx+b)=1\\) -* 新的目标函数求解: \\(arg: max\{关于w, b\}\ (1/||w||)\\) - * => 就是求: \\(arg: min\{关于w, b\}\ (||w||)\\) (求矩阵会比较麻烦,如果x只是 \\(\frac{1}{2}*x^2\\) 的偏导数,那么。。同样是求最小值) - * => 就是求: \\(arg: min\{关于w, b\}\ (\frac{1}{2}*||w||^2)\\) (二次函数求导,求极值,平方也方便计算) - * 本质上就是求线性不等式的二次优化问题(求分隔超平面,等价于求解相应的凸二次规划问题。) + * 目标函数:\\(arg: max\left( min(lable*(w^Tx+b))*\frac{1}{||w||} \right) \\) + 1. 如果 \\(lable*(w^Tx+b)>0\\) 表示预测正确,也称`函数间隔`,\\(||w||\\) 可以理解为归一化,也称`几何间隔`。 + 2. 令 \\(lable*(w^Tx+b)>=1\\), 因为-1~1之间,得到的点是存在误判的可能性,所以要保障min=1,才能更好降低噪音数据影响。 + 3. 所以本质上是求 \\(arg: max_{关于w, b} \frac{1}{||w||} \\);也就说,我们约束(前提)条件是: \\(lable*(w^Tx+b)=1\\) +* 新的目标函数求解: \\(arg: max_{关于w, b} \frac{1}{||w||} \\) + * => 就是求: \\(arg: min_{关于w, b} ||w|| \\) (求矩阵会比较麻烦,如果x只是 \\(\frac{1}{2}*x^2\\) 的偏导数,那么。。同样是求最小值) + * => 就是求: \\(arg: min_{关于w, b} (\frac{1}{2}*||w||^2)\\) (二次函数求导,求极值,平方也方便计算) + * 本质上就是求线性不等式的二次优化问题(求分隔超平面,等价于求解相应的凸二次规划问题) * 通过拉格朗日乘子法,求二次优化问题 * 假设需要求极值的目标函数 (objective function) 为 f(x,y),限制条件为 φ(x,y)=M # M=1 * 设g(x,y)=M-φ(x,y) # 临时φ(x,y)表示下文中 \\(label*(w^Tx+b)\\) * 定义一个新函数: F(x,y,λ)=f(x,y)+λg(x,y) - * a为λ,代表要引入的拉格朗日乘子(Lagrange multiplier) + * a为λ(a>=0),代表要引入的拉格朗日乘子(Lagrange multiplier) * 那么: \\(L(w,b,\alpha)=\frac{1}{2} * ||w||^2 + \sum_{i=1}^{n} \alpha * [1 - label * (w^Tx+b)]\\) - * 因为:\\(label*(w^Tx+b)>=1, \alpha>0\\) , 所以 \\(\alpha*[label*(w^Tx+b)-1]>=0\\), \\(\sum_{i=1}^{n} \alpha * [label * (w^Tx+b) - 1]>=0\\) - * \\(max\{关于\alpha\}\ L(w,b,\alpha) = \frac{1}{2} *||w||^2\\) - * 如果求: \\(min\{关于w, b\}\ \frac{1}{2} *||w||^2\\) , 也就是要求: \\(min\{关于w, b\}\ [max\{关于\alpha\}\ L(w,b,\alpha)]\\) + * 因为:\\(label*(w^Tx+b)>=1, \alpha>=0\\) , 所以 \\(\alpha*[1-label*(w^Tx+b)]<=0\\) , \\(\sum_{i=1}^{n} \alpha * [1-label*(w^Tx+b)]<=0\\) + * 相当于求解: \\(max_{关于\alpha} L(w,b,\alpha) = \frac{1}{2} *||w||^2\\) + * 如果求: \\(min_{关于w, b} \frac{1}{2} *||w||^2\\) , 也就是要求: \\(min_{关于w, b} \left( max_{关于\alpha} L(w,b,\alpha)\right)\\) * 现在转化到对偶问题的求解 - * \\(min\{关于w, b\}\ [max\{关于\alpha\}\ L(w,b,\alpha)]\\) >= \\(max\{关于\alpha\}\ [min\{关于w, b\}\ L(w,b,\alpha)]\\) + * \\(min_{关于w, b} \left(max_{关于\alpha} L(w,b,\alpha) \right) \\) >= \\(max_{关于\alpha} \left(min_{关于w, b}\ L(w,b,\alpha) \right) \\) * 现在分2步 - * 先求: \\(min\{关于w, b\}\ L(w,b,\alpha)=\frac{1}{2} * ||w||^2 + \sum_{i=1}^{n} \alpha * [1 - label * (w^Tx+b)]\\) + * 先求: \\(min_{关于w, b} L(w,b,\alpha)=\frac{1}{2} * ||w||^2 + \sum_{i=1}^{n} \alpha * [1 - label * (w^Tx+b)]\\) * 就是求`L(w,b,a)`关于[w, b]的偏导数, 得到`w和b的值`,并化简为:`L和a的方程`。 * 参考: 如果公式推导还是不懂,也可以参考《统计学习方法》李航-P103<学习的对偶算法> - * ![计算拉格朗日函数的对偶函数](/images/6.SVM/SVM_5_Lagrangemultiplier.png) -* 终于得到课本上的公式: \\(max\ \{\alpha\}\ [\sum_{i=1}^{m} \alpha - \frac{1}{2} \sum_{i, j=1}^{m} label_i·label_j·\alpha_i·\alpha_j·]\\) -* 约束条件: \\(a>=0,\ and\ \sum_{i=1}^{m} a_i·label_i=0\\) + ![计算拉格朗日函数的对偶函数](/images/6.SVM/SVM_5_Lagrangemultiplier.png) +* 终于得到课本上的公式: \\(max\ \{\alpha\}\ \left( \sum_{i=1}^{m} \alpha_i - \frac{1}{2} \sum_{i, j=1}^{m} label_i·label_j·\alpha_i·\alpha_j· \right) \\) +* 约束条件: \\(a>=0\\) 并且 \\(\sum_{i=1}^{m} a_i·label_i=0\\) > 松弛变量(slack variable) @@ -131,38 +110,32 @@ This is the simplest kind of SVM (Called an LSVM) Support Vectors are those data * 常量C是一个常数,我们通过调节该参数得到不同的结果。一旦求出了所有的alpha,那么分隔超平面就可以通过这些alpha来表示。 * 这一结论十分直接,SVM中的主要工作就是要求解 alpha. -> SVM应用的一般框架 -``` -SVM的一般流程 -收集数据:可以使用任意方法。 -准备数据:需要数值型数据。 -分析数据:有助于可视化分隔超平面。 -训练算法:SVM的大部分时间都源自训练,该过程主要实现两个参数的调优。 -测试算法:十分简单的计算过程就可以实现。 -使用算法:几乎所有分类问题都可以使用SVM,值得一提的是,SVM本身是一个二类分类器,对多类问题应用SVM需要对代码做一些修改。 -``` -* 到目前为止,我们已经了解了一些理论知识,现在我们通过`Code`来实现我们的算法吧。 +### SMO高效优化算法 -## SMO高效优化算法 +SMO 是 SVM最流行的一种实现 + +* SVM有很多种实现,最流行的一种实现是: `序列最小优化(Sequential Minimal Optimization, SMO)算法`。 +* 下面还会介绍一种称为`核函数(kernel)`的方式将SVM扩展到更多数据集上。 +* 注意:`SVM几何含义比较直观,但其算法实现较复杂,牵扯大量数学公式的推导。` > 序列最小优化(Sequential Minimal Optimization, SMO) * 创建作者:John Platt * 创建时间:1996年 -* SMO用途:用于训练SVM -* SMO目标:求出一系列alpha和b,一旦求出alpha,就很容易计算出权重向量w并得到分隔超平面。 +* SMO用途:用于训练 SVM +* SMO目标:求出一系列 alpha 和 b,一旦求出 alpha,就很容易计算出权重向量 w 并得到分隔超平面。 * SMO思想:是将大优化问题分解为多个小优化问题来求解的。 -* SMO原理:每次循环选择两个alpha进行优化处理,一旦找出一对合适的alpha,那么就增大一个同时减少一个。 +* SMO原理:每次循环选择两个 alpha 进行优化处理,一旦找出一对合适的 alpha,那么就增大一个同时减少一个。 * 这里指的合适必须要符合一定的条件 - * 1.这两个alpha必须要在间隔边界之外 - * 2.这两个alpha还没有进行过区间化处理或者不在边界上。 - * 之所以要同时改变2个alpha;原因,我们有一个约束条件: \\(\sum_{i=1}^{m} a_i·label_i=0\\);如果只是修改一个alpha,很可能导致约束条件失效。 + 1. 这两个 alpha 必须要在间隔边界之外 + 2. 这两个 alpha 还没有进行过区间化处理或者不在边界上。 + * 之所以要同时改变2个 alpha;原因是我们有一个约束条件: \\(\sum_{i=1}^{m} a_i·label_i=0\\);如果只是修改一个 alpha,很可能导致约束条件失效。 + +> SMO 伪代码大致如下: ``` -SMO伪代码大致如下: - -创建一个alpha向量并将其初始化为0向量 +创建一个 alpha 向量并将其初始化为0向量 当迭代次数小于最大迭代次数时(外循环) 对数据集中的每个数据向量(内循环): 如果该数据向量可以被优化 @@ -172,6 +145,25 @@ SMO伪代码大致如下: 如果所有向量都没被优化,增加迭代数目,继续下一次循环 ``` +### SVM 开发流程 + +``` +收集数据:可以使用任意方法。 +准备数据:需要数值型数据。 +分析数据:有助于可视化分隔超平面。 +训练算法:SVM的大部分时间都源自训练,该过程主要实现两个参数的调优。 +测试算法:十分简单的计算过程就可以实现。 +使用算法:几乎所有分类问题都可以使用SVM,值得一提的是,SVM本身是一个二类分类器,对多类问题应用SVM需要对代码做一些修改。 +``` + +### SVM 算法特点 + +``` +优点:泛化(由具体的、个别的扩大为一般的,就是说:模型训练完后的新样本)错误率低,计算开销不大,结果易理解。 +缺点:对参数调节和核函数的选择敏感,原始分类器不加修改仅适合于处理二分类问题。 +使用数据类型:数值型和标称型数据。 +``` + > SVM简化版:应用简化版SMO算法处理小规模数据集 代码可参考 svm-simple.py @@ -181,7 +173,7 @@ SMO伪代码大致如下: 代码可参考 svm-complete_Non-Kernel.py * 优化点:选择alpha的方式不同。 -## 在复杂数据上应用核函数 +## 核函数(kernel) 使用 * 对于线性可分的情况,效果明显 * 对于非线性的情况也一样,此时需要用到一种叫`核函数(kernel)`的工具将数据转化为分类器易于理解的形式。 @@ -192,10 +184,21 @@ SMO伪代码大致如下: * 如果觉得特征空间很装逼、很难理解。 * 可以把核函数想象成一个包装器(wrapper)或者是接口(interface),它能将数据从某个很难处理的形式转换成为另一个较容易处理的形式。 * 经过空间转换后:低维需要解决的非线性问题,就变成了高维需要解决的线性问题。 -* SVM优化特别好的地方,在于所有的运算都可以写成内积(inner product: 是指2个向量相乘,得到单个标量 或者 数值);内核替换成核函数的方式被称为`核技巧(kernel trick)`或者`核"变电"(kernel substation)` +* SVM 优化特别好的地方,在于所有的运算都可以写成内积(inner product: 是指2个向量相乘,得到单个标量 或者 数值);内核替换成核函数的方式被称为`核技巧(kernel trick)`或者`核"变电"(kernel substation)` * 核函数并不仅仅应用于支持向量机,很多其他的机器学习算法也都用到核函数。最流行的核函数:径向基函数(radial basis function) * 径向基函数的高斯版本,其具体的公式为: -* ![径向基函数的高斯版本](/images/6.SVM/SVM_6_radial-basis-function.jpg) + +![径向基函数的高斯版本](/images/6.SVM/SVM_6_radial-basis-function.jpg) + +## 支持向量机 项目实战 + +### 项目实战: 手写数字识别的优化 + + + + + + * * * diff --git a/images/6.SVM/SVM_4_point2line-distance.jpg b/images/6.SVM/SVM_4_point2line-distance.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cf1a6ed09f8e270fda6e76a00c9d86e9796323ea GIT binary patch literal 9041 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 KN7qORQ~wJCwo>;1 literal 0 HcmV?d00001