Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 73 additions & 70 deletions docs/6.支持向量机.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

## 支持向量机 原理

### 支持向量机 工作原理
### SVM 工作原理

![k_2](/images/6.SVM/k_2.jpg "k_2")

Expand All @@ -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条信息)
Expand All @@ -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·<x_i, x_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·<x_i, x_j> \right) \\)
* 约束条件: \\(a>=0\\) 并且 \\(\sum_{i=1}^{m} a_i·label_i=0\\)

> 松弛变量(slack variable)

Expand All @@ -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向量
当迭代次数小于最大迭代次数时(外循环)
对数据集中的每个数据向量(内循环):
如果该数据向量可以被优化
Expand All @@ -172,6 +145,25 @@ SMO伪代码大致如下:
如果所有向量都没被优化,增加迭代数目,继续下一次循环
```

### SVM 开发流程

```
收集数据:可以使用任意方法。
准备数据:需要数值型数据。
分析数据:有助于可视化分隔超平面。
训练算法:SVM的大部分时间都源自训练,该过程主要实现两个参数的调优。
测试算法:十分简单的计算过程就可以实现。
使用算法:几乎所有分类问题都可以使用SVM,值得一提的是,SVM本身是一个二类分类器,对多类问题应用SVM需要对代码做一些修改。
```

### SVM 算法特点

```
优点:泛化(由具体的、个别的扩大为一般的,就是说:模型训练完后的新样本)错误率低,计算开销不大,结果易理解。
缺点:对参数调节和核函数的选择敏感,原始分类器不加修改仅适合于处理二分类问题。
使用数据类型:数值型和标称型数据。
```

> SVM简化版:应用简化版SMO算法处理小规模数据集

代码可参考 svm-simple.py
Expand All @@ -181,7 +173,7 @@ SMO伪代码大致如下:
代码可参考 svm-complete_Non-Kernel.py
* 优化点:选择alpha的方式不同。

## 在复杂数据上应用核函数
## 核函数(kernel) 使用

* 对于线性可分的情况,效果明显
* 对于非线性的情况也一样,此时需要用到一种叫`核函数(kernel)`的工具将数据转化为分类器易于理解的形式。
Expand All @@ -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)

## 支持向量机 项目实战

### 项目实战: 手写数字识别的优化







* * *

Expand Down
4 changes: 1 addition & 3 deletions docs/9.树回归.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ CART 和 C4.5 之间主要差异在于分类结果上,CART 可以回归分析
```
对每个特征:
对每个特征值:
将数据集切分成两份
将数据集切分成两份(小于该特征值的数据样本放在左子树,否则放在右子树)
计算切分的误差
如果当前误差小于当前最小误差,那么将当前切分设定为最佳切分并更新最小误差
返回最佳切分的特征和阈值
Expand Down Expand Up @@ -272,8 +272,6 @@ def createTree(dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)):

决策树构造完成后进行剪枝。剪枝的过程是对拥有同样父节点的一组节点进行检查,判断如果将其合并,熵的增加量是否小于某一阈值。如果确实小,则这一组节点可以合并一个节点,其中包含了所有可能的结果。合并也被称作 `塌陷处理` ,在回归树中一般采用取需要合并的所有子树的平均值。后剪枝是目前最普遍的做法。

后剪枝的剪枝过程是删除一些子树,然后用其叶子节点代替,这个叶子节点所标识的类别通过大多数原则(majority class criterion)确定。所谓大多数原则,是指剪枝过程中, 将一些子树删除而用叶节点代替,这个叶节点所标识的类别用这棵子树中大多数训练样本所属的类别来标识,所标识的类 称为majority class ,(majority class 在很多英文文献中也多次出现)。

后剪枝 prune() 的伪代码如下:

```
Expand Down
Binary file added images/6.SVM/SVM_4_point2line-distance.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 32 additions & 32 deletions src/python/9.RegTrees/regTrees.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,12 @@ def createForeCast(tree, testData, modelEval=regTreeEval):


if __name__ == "__main__":
# 测试数据集
testMat = mat(eye(4))
print testMat
print type(testMat)
mat0, mat1 = binSplitDataSet(testMat, 1, 0.5)
print mat0, '\n-----------\n', mat1
# # 测试数据集
# testMat = mat(eye(4))
# print testMat
# print type(testMat)
# mat0, mat1 = binSplitDataSet(testMat, 1, 0.5)
# print mat0, '\n-----------\n', mat1

# # 回归树
# myDat = loadDataSet('input/9.RegTrees/data1.txt')
Expand Down Expand Up @@ -431,29 +431,29 @@ def createForeCast(tree, testData, modelEval=regTreeEval):
# myTree = createTree(myMat, modelLeaf, modelErr)
# print myTree

# # # 回归树 VS 模型树 VS 线性回归
# trainMat = mat(loadDataSet('input/9.RegTrees/bikeSpeedVsIq_train.txt'))
# testMat = mat(loadDataSet('input/9.RegTrees/bikeSpeedVsIq_test.txt'))
# # # 回归树
# myTree1 = createTree(trainMat, ops=(1, 20))
# print myTree1
# yHat1 = createForeCast(myTree1, testMat[:, 0])
# print "--------------\n"
# # print yHat1
# # print "ssss==>", testMat[:, 1]
# print "回归树:", corrcoef(yHat1, testMat[:, 1],rowvar=0)[0, 1]

# # 模型树
# myTree2 = createTree(trainMat, modelLeaf, modelErr, ops=(1, 20))
# yHat2 = createForeCast(myTree2, testMat[:, 0], modelTreeEval)
# print myTree2
# print "模型树:", corrcoef(yHat2, testMat[:, 1],rowvar=0)[0, 1]

# # 线性回归
# ws, X, Y = linearSolve(trainMat)
# print ws
# m = len(testMat[:, 0])
# yHat3 = mat(zeros((m, 1)))
# for i in range(shape(testMat)[0]):
# yHat3[i] = testMat[i, 0]*ws[1, 0] + ws[0, 0]
# print "线性回归:", corrcoef(yHat3, testMat[:, 1],rowvar=0)[0, 1]
# # 回归树 VS 模型树 VS 线性回归
trainMat = mat(loadDataSet('input/9.RegTrees/bikeSpeedVsIq_train.txt'))
testMat = mat(loadDataSet('input/9.RegTrees/bikeSpeedVsIq_test.txt'))
# # 回归树
myTree1 = createTree(trainMat, ops=(1, 20))
print myTree1
yHat1 = createForeCast(myTree1, testMat[:, 0])
print "--------------\n"
# print yHat1
# print "ssss==>", testMat[:, 1]
print "回归树:", corrcoef(yHat1, testMat[:, 1],rowvar=0)[0, 1]

# 模型树
myTree2 = createTree(trainMat, modelLeaf, modelErr, ops=(1, 20))
yHat2 = createForeCast(myTree2, testMat[:, 0], modelTreeEval)
print myTree2
print "模型树:", corrcoef(yHat2, testMat[:, 1],rowvar=0)[0, 1]

# 线性回归
ws, X, Y = linearSolve(trainMat)
print ws
m = len(testMat[:, 0])
yHat3 = mat(zeros((m, 1)))
for i in range(shape(testMat)[0]):
yHat3[i] = testMat[i, 0]*ws[1, 0] + ws[0, 0]
print "线性回归:", corrcoef(yHat3, testMat[:, 1],rowvar=0)[0, 1]