学习和使用支持向量机

- 学习教材6.3、6.4节内容，调试运行相关代码。
- 查阅scikit-learn工具包中支持向量机的相关说明，了解分类器函数使用方法。
- 完成作业二

# SMO高效优化算法

> 书本代码参考仓库[Machine Learning in Action](https://github.com/TeFuirnever/Machine-Learning-in-Action)

SMO算法的目标是求出一系列alpha和b，便于计算权重向量并得到分隔超平面。

工作原理：选择两个alpha进行优化处理，一旦找到一对合适的alpha，那么就增大一个减少另一个。

> “合适”：①alpha须在间隔边界之外；②alpha没有进行过区间化处理或者不在边界上。

## 简化版SMO

量少，但执行速度慢。跳过SMO的外循环（确定最佳alpha对），遍历每个alpha随机选择另一个。

```python
import numpy as np
import random
```

```python
## 6-1 SMO算法中的辅助函数
"""
函数说明:读取数据
Parameters:
    fileName - 文件名
Returns:
    dataMat - 数据矩阵
    labelMat - 数据标签
"""
def loadDataSet(fileName):
    dataMat = []; labelMat = []
    fr = open(fileName)
    for line in fr.readlines():                                     #逐行读取，滤除空格等
        lineArr = line.strip().split('\t')
        dataMat.append([float(lineArr[0]), float(lineArr[1])])      #添加数据
        labelMat.append(float(lineArr[2]))                          #添加标签
    return dataMat,labelMat

"""
函数说明:随机选择alpha
Parameters:
    i - alpha
    m - alpha参数个数
Returns:
    j -
"""
def selectJrand(i, m):
    j = i                                 #选择一个不等于i的j
    while (j == i):
        j = int(random.uniform(0, m))
    return j

"""
函数说明:修剪alpha
Parameters:
    aj - alpha值
    H - alpha上限
    L - alpha下限
Returns:
    aj - alpah值
"""
def clipAlpha(aj,H,L):
    if aj > H:
        aj = H
    if L > aj:
        aj = L
    return aj

```

```python
dataArr, labelArr = loadDataSet('./data/testSet.txt')
print(labelArr[:5])
```

    [-1.0, -1.0, 1.0, -1.0, 1.0]

该数据集中类别标签为1，-1。

SMO的伪代码大致如下：

> * 创建一个alpha向量并将其初始化为0向量
>
> * 当迭代次数小于最大迭代次数时：
>
>   * 对数据集中的每个数据向量（外循环）：
>
>     * 如果该向量可以被优化：
>
>       * 随机选择另外一个数据向量（内循环）
>
>       * 同时优化这两个向量
>
>       * 如果两个向量都不能被优化，退出内循环
>
> * 如果所有向量都没有被优化，增加迭代数目，继续下一次循环

$$
fXi = \sum_{j=1}^N \alpha_j y_j X_j \cdot X_i
$$

步骤1：计算误差Ei=f(x)-y

步骤2：计算上下界L和H
$$
L = \max{(0,\alpha_2^{\text{old}}-\alpha_1^{\text{old}})}, \ \ \ H=\min{(C,C+\alpha_2^{\text{old}}-\alpha_1^{\text{old}})}\\
或\\
L = \max{(0,\alpha_2^{\text{old}}+\alpha_1^{\text{old}}-C)}, \ \ \ H=\min{(C,\alpha_2^{\text{old}}+\alpha_1^{\text{old}})}
$$

步骤3：计算eta(η)
$$
\eta = K_{11} + K_{22} - 2K_{12} = ||\phi(x_1)-\phi(x_2)||^2
$$

步骤4：更新alpha_j
$$
\alpha_2^{\text{new,unc}} = \alpha_2^{\text{old}} + \dfrac{y_2(E_1-E_2)}{\eta}
$$

步骤5：修剪alpha_j
$$
\alpha_2^{new} = \left\{
\begin{align}
&H,  &\alpha_2^{\text{new,unc}}\gt H \\
&\alpha_2^{\text{new,unc}}, & L \le\alpha_2^{\text{new,unc}}\le H \\
&L, &\alpha_2^{\text{new,unc}}\lt L
\end{align}
\right.
$$

步骤6：更新alpha_i
$$
\alpha_1^{\text{new}} = \alpha_1^{\text{old}} + y_1y_2(\alpha_2^{\text{old}}-\alpha_2^{\text{new}})
$$

步骤7：更新b_1和b_2
$$
b_1^{\text{new}} = b^{\text{old}} - E_1 - y_1K_{11}(\alpha_1^{\text{new}}-\alpha_1^{\text{old}}) - y_2K_{21}(\alpha_2^{\text{new}}-\alpha_2^{\text{old}})\\
b_2^{\text{new}} = b^{\text{old}} - E_2 - y_1K_{12}(\alpha_1^{\text{new}}-\alpha_1^{\text{old}}) - y_2K_{22}(\alpha_2^{\text{new}}-\alpha_2^{\text{old}})\\
$$

步骤8：根据b_1和b_2更新b

```python
"""
函数说明:简化版SMO算法
Parameters:
    dataMatIn - 数据矩阵
    classLabels - 数据标签
    C - 松弛变量
    toler - 容错率
    maxIter - 最大迭代次数
Returns:
    无
"""
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
    #转换为numpy的mat存储
    dataMatrix = np.mat(dataMatIn); labelMat = np.mat(classLabels).transpose()
    #初始化b参数，统计dataMatrix的维度
    b = 0; m,n = np.shape(dataMatrix)
    #初始化alpha参数，设为0
    alphas = np.mat(np.zeros((m,1)))
    #初始化迭代次数
    iter_num = 0
    #最多迭代matIter次
    while (iter_num < maxIter):
        alphaPairsChanged = 0
        for i in range(m):
            #步骤1：计算误差Ei
            fXi = float(np.multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
            ##fXi = \sum_{j=1}^N \alpha_j y_j X_j \cdot X_i X:Vector
            Ei = fXi - float(labelMat[i])
            #优化alpha，更设定一定的容错率。
            if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
                #随机选择另一个与alpha_i成对优化的alpha_j
                j = selectJrand(i,m)
                #步骤1：计算误差Ej
                fXj = float(np.multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b
                Ej = fXj - float(labelMat[j])
                #保存更新前的aplpha值，使用深拷贝
                alphaIold = alphas[i].copy(); alphaJold = alphas[j].copy();
                #步骤2：计算上下界L和H
                if (labelMat[i] != labelMat[j]):
                    L = max(0, alphas[j] - alphas[i])
                    H = min(C, C + alphas[j] - alphas[i])
                else:
                    L = max(0, alphas[j] + alphas[i] - C)
                    H = min(C, alphas[j] + alphas[i])
                ## L = \max{(0,\alpha_2^{\text{old}}-\alpha_1^{\text{old}})}, \ \ \ H=\min{(C,C+\alpha_2^{\text{old}}-\alpha_1^{\text{old}})}
                ## L = \max{(0,\alpha_2^{\text{old}}+\alpha_1^{\text{old}}-C)}, \ \ \ H=\min{(C,\alpha_2^{\text{old}}+\alpha_1^{\text{old}})}
                if L==H: print("L==H"); continue
                #步骤3：计算eta(η)
                eta = 2.0 * dataMatrix[i,:]*dataMatrix[j,:].T - dataMatrix[i,:]*dataMatrix[i,:].T - dataMatrix[j,:]*dataMatrix[j,:].T
                ## \eta = K_{11} + K_{22} - 2K_{12} = ||\phi(x_1)-\phi(x_2)||^2
                if eta >= 0: print("eta>=0"); continue
                #步骤4：更新alpha_j
                alphas[j] -= labelMat[j]*(Ei - Ej)/eta
                ## \alpha_2^{\text{new,unc}} = \alpha_2^{\text{old}} + \dfrac{y_2(E_1-E_2)}{\eta}
                #步骤5：修剪alpha_j
                alphas[j] = clipAlpha(alphas[j],H,L)
                if (abs(alphas[j] - alphaJold) < 0.00001): print("alpha_j变化太小"); continue
                #步骤6：更新alpha_i
                alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])
                ## \alpha_1^{\text{new}} = \alpha_1^{\text{old}} + y_1y_2(\alpha_2^{\text{old}}-\alpha_2^{\text{new}})
                #步骤7：更新b_1和b_2
                b1 = b - Ei- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i,:]*dataMatrix[i,:].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[i,:]*dataMatrix[j,:].T
                b2 = b - Ej- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i,:]*dataMatrix[j,:].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[j,:]*dataMatrix[j,:].T
                ##b_1^{\text{new}} = b^{\text{old}} - E_1 - y_1K_{11}(\alpha_1^{\text{new}}-\alpha_1^{\text{old}}) - y_2K_{21}(\alpha_2^{\text{new}}-\alpha_2^{\text{old}})\\
                ##b_2^{\text{new}} = b^{\text{old}} - E_2 - y_1K_{12}(\alpha_1^{\text{new}}-\alpha_1^{\text{old}}) - y_2K_{22}(\alpha_2^{\text{new}}-\alpha_2^{\text{old}})\\
                #步骤8：根据b_1和b_2更新b
                if (0 < alphas[i]) and (C > alphas[i]): b = b1
                elif (0 < alphas[j]) and (C > alphas[j]): b = b2
                else: b = (b1 + b2)/2.0
                #统计优化次数
                alphaPairsChanged += 1
                #打印统计信息
                print("第%d次迭代 样本:%d, alpha优化次数:%d" % (iter_num,i,alphaPairsChanged))
        #更新迭代次数
        if (alphaPairsChanged == 0): iter_num += 1
        else: iter_num = 0
        print("迭代次数: %d" % iter_num)
    return b,alphas
```

进行简单的测试：

```python
%%time
b, alphas = smoSimple(dataArr, labelArr, 0.6, 0.001, 40)
```

    第0次迭代 样本:0, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:5, alpha优化次数:2
    L==H
    L==H
    L==H
    第0次迭代 样本:23, alpha优化次数:3
    第0次迭代 样本:24, alpha优化次数:4
    第0次迭代 样本:25, alpha优化次数:5
    第0次迭代 样本:26, alpha优化次数:6
    alpha_j变化太小
    第0次迭代 样本:31, alpha优化次数:7
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:56, alpha优化次数:8
    L==H
    alpha_j变化太小
    L==H
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    L==H
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    L==H
    L==H
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    L==H
    alpha_j变化太小
    L==H
    L==H
    alpha_j变化太小
    L==H
    alpha_j变化太小
    L==H
    alpha_j变化太小
    alpha_j变化太小
    L==H
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第1次迭代 样本:10, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第1次迭代 样本:29, alpha优化次数:2
    第1次迭代 样本:54, alpha优化次数:3
    第1次迭代 样本:55, alpha优化次数:4
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    L==H
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    L==H
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    L==H
    alpha_j变化太小
    第0次迭代 样本:71, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:10, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:45, alpha优化次数:2
    第0次迭代 样本:54, alpha优化次数:3
    alpha_j变化太小
    alpha_j变化太小
    L==H
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:5, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    L==H
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:23, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:55, alpha优化次数:2
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:10, alpha优化次数:1
    第0次迭代 样本:17, alpha优化次数:2
    alpha_j变化太小
    第0次迭代 样本:24, alpha优化次数:3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    L==H
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:54, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    第0次迭代 样本:1, alpha优化次数:1
    L==H
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    L==H
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:54, alpha优化次数:1
    第0次迭代 样本:55, alpha优化次数:2
    第0次迭代 样本:94, alpha优化次数:3
    alpha_j变化太小
    迭代次数: 0
    第0次迭代 样本:0, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:97, alpha优化次数:2
    迭代次数: 0
    alpha_j变化太小
    L==H
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    第1次迭代 样本:10, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    L==H
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:52, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:23, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:46, alpha优化次数:2
    alpha_j变化太小
    第0次迭代 样本:54, alpha优化次数:3
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    第0次迭代 样本:10, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    第2次迭代 样本:8, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第2次迭代 样本:29, alpha优化次数:2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第1次迭代 样本:55, alpha优化次数:1
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:24, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    L==H
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    第1次迭代 样本:17, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    第0次迭代 样本:23, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:54, alpha优化次数:2
    第0次迭代 样本:55, alpha优化次数:3
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第3次迭代 样本:54, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 4
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 5
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 6
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 7
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 8
    alpha_j变化太小
    alpha_j变化太小
    第8次迭代 样本:29, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 4
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 5
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 6
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 7
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第7次迭代 样本:55, alpha优化次数:1
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:54, alpha优化次数:1
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第3次迭代 样本:29, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    第0次迭代 样本:17, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第3次迭代 样本:54, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    第1次迭代 样本:23, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    第0次迭代 样本:17, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第2次迭代 样本:54, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    第2次迭代 样本:17, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 4
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 5
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 6
    alpha_j变化太小
    第6次迭代 样本:29, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 4
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 5
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 6
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 7
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 8
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 9
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 10
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 11
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 12
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 13
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 14
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 15
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 16
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 17
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 18
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 19
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 20
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 21
    alpha_j变化太小
    第21次迭代 样本:23, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 4
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第4次迭代 样本:54, alpha优化次数:1
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    第1次迭代 样本:55, alpha优化次数:1
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    L==H
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 4
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 5
    alpha_j变化太小
    alpha_j变化太小
    L==H
    alpha_j变化太小
    迭代次数: 6
    第6次迭代 样本:17, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 4
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 5
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 6
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 7
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 8
    alpha_j变化太小
    第8次迭代 样本:29, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:55, alpha优化次数:1
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    第0次迭代 样本:54, alpha优化次数:1
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    第1次迭代 样本:52, alpha优化次数:1
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 4
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 5
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 6
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 7
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 8
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 9
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 10
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 11
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 12
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 13
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 14
    第14次迭代 样本:17, alpha优化次数:1
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 0
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 1
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 2
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 3
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 4
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 5
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 6
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 7
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 8
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 9
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 10
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 11
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 12
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 13
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 14
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 15
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 16
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 17
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 18
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 19
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 20
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 21
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 22
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 23
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 24
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 25
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 26
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 27
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 28
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 29
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 30
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 31
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 32
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 33
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 34
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 35
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 36
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 37
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 38
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 39
    alpha_j变化太小
    alpha_j变化太小
    迭代次数: 40
    CPU times: user 3.03 s, sys: 567 ms, total: 3.6 s
    Wall time: 2.75 s

```python
b
```

    matrix([[-3.90043157]])

```python
alphas[alphas>0]
```

    matrix([[0.10104876, 0.26933193, 0.03268721, 0.33769349]])

## 完整Platt SMO

简化版SMO中，外层循环是遍历所有样本点作为 $\alpha_i$ ，内层循环则是随机选择 $\alpha_j$ 更新这两个 $\alpha$ 。这样做计算速度较慢，在较大量数据集或维度较高数据集中很可能耗时过多。

完整版的Platt SMO的外层循环是遍历所有样本点和遍历非边界点交替进行，遍历所有样本点之后就遍历所有非边界点，遍历非边界点之后如果没有 $\alpha$ 被更新则重新遍历所有样本点；否则继续遍历非边界点。

内循环则不是随机选择，是通过计算最大化步长 $|E_i-E_j|$ 来进行选择，选择使得 $|E_i-E_j|$ 最大的点作为 $\alpha_j$。

简化版和完整版仅在 $\alpha$ 的选择上有所不同，内部计算和更新方法相同，不再赘述。但是后者的计算效率相对较高。

```python
## 6-3 完整版SMO的支持函数
"""
数据结构，维护所有需要操作的值
Parameters：
    dataMatIn - 数据矩阵
    classLabels - 数据标签
    C - 松弛变量
    toler - 容错率
"""
class optStruct:
    def __init__(self, dataMatIn, classLabels, C, toler):
        self.X = dataMatIn                                #数据矩阵
        self.labelMat = classLabels                        #数据标签
        self.C = C                                         #松弛变量
        self.tol = toler                                 #容错率
        self.m = np.shape(dataMatIn)[0]                 #数据矩阵行数
        self.alphas = np.mat(np.zeros((self.m,1)))         #根据矩阵行数初始化alpha参数为0
        self.b = 0                                         #初始化b参数为0
        self.eCache = np.mat(np.zeros((self.m,2)))         #根据矩阵行数初始化虎误差缓存，第一列为是否有效的标志位，第二列为实际的误差E的值。

"""
读取数据
Parameters:
    fileName - 文件名
Returns:
    dataMat - 数据矩阵
    labelMat - 数据标签
"""
def loadDataSet(fileName):
    dataMat = []; labelMat = []
    fr = open(fileName)
    for line in fr.readlines():                                     #逐行读取，滤除空格等
        lineArr = line.strip().split('\t')
        dataMat.append([float(lineArr[0]), float(lineArr[1])])      #添加数据
        labelMat.append(float(lineArr[2]))                          #添加标签
    return dataMat,labelMat

"""
计算误差
Parameters：
    oS - 数据结构
    k - 标号为k的数据
Returns:
    Ek - 标号为k的数据误差
"""
def calcEk(oS, k):
    fXk = float(np.multiply(oS.alphas,oS.labelMat).T*(oS.X*oS.X[k,:].T) + oS.b)
    Ek = fXk - float(oS.labelMat[k])
    return Ek

"""
内循环启发方式2
Parameters：
    i - 标号为i的数据的索引值
    oS - 数据结构
    Ei - 标号为i的数据误差
Returns:
    j, maxK - 标号为j或maxK的数据的索引值
    Ej - 标号为j的数据误差
"""
def selectJ(i, oS, Ei):
    maxK = -1; maxDeltaE = 0; Ej = 0                         #初始化
    oS.eCache[i] = [1,Ei]                                      #根据Ei更新误差缓存
    validEcacheList = np.nonzero(oS.eCache[:,0].A)[0]        #返回误差不为0的数据的索引值
    if (len(validEcacheList)) > 1:                            #有不为0的误差
        for k in validEcacheList:                           #遍历,找到最大的Ek
            if k == i: continue                             #不计算i,浪费时间
            Ek = calcEk(oS, k)                                #计算Ek
            deltaE = abs(Ei - Ek)                            #计算|Ei-Ek|
            if (deltaE > maxDeltaE):                        #找到maxDeltaE
                maxK = k; maxDeltaE = deltaE; Ej = Ek
        return maxK, Ej                                        #返回maxK,Ej
    else:                                                   #没有不为0的误差
        j = selectJrand(i, oS.m)                            #随机选择alpha_j的索引值
        Ej = calcEk(oS, j)                                    #计算Ej
    return j, Ej                                             #j,Ej

"""
计算Ek,并更新误差缓存
Parameters：
    oS - 数据结构
    k - 标号为k的数据的索引值
Returns:
    无
"""
def updateEk(oS, k):
    Ek = calcEk(oS, k)                                        #计算Ek
    oS.eCache[k] = [1,Ek]                                    #更新误差缓存
```

```python
## 6-4 完整Platt SMO算法中的优化例程
"""
优化的SMO算法
Parameters：
    i - 标号为i的数据的索引值
    oS - 数据结构
Returns:
    1 - 有任意一对alpha值发生变化
    0 - 没有任意一对alpha值发生变化或变化太小
"""
def innerL(i, oS):
    #步骤1：计算误差Ei
    Ei = calcEk(oS, i)
    #优化alpha,设定一定的容错率。
    if ((oS.labelMat[i] * Ei < -oS.tol) and (oS.alphas[i] < oS.C)) or ((oS.labelMat[i] * Ei > oS.tol) and (oS.alphas[i] > 0)):
        #使用内循环启发方式2选择alpha_j,并计算Ej
        j,Ej = selectJ(i, oS, Ei)
        #保存更新前的aplpha值，使用深拷贝
        alphaIold = oS.alphas[i].copy(); alphaJold = oS.alphas[j].copy();
        #步骤2：计算上下界L和H
        if (oS.labelMat[i] != oS.labelMat[j]):
            L = max(0, oS.alphas[j] - oS.alphas[i])
            H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
        else:
            L = max(0, oS.alphas[j] + oS.alphas[i] - oS.C)
            H = min(oS.C, oS.alphas[j] + oS.alphas[i])
        if L == H:
            print("L==H")
            return 0
        #步骤3：计算eta
        eta = 2.0 * oS.X[i,:] * oS.X[j,:].T - oS.X[i,:] * oS.X[i,:].T - oS.X[j,:] * oS.X[j,:].T
        if eta >= 0:
            print("eta>=0")
            return 0
        #步骤4：更新alpha_j
        oS.alphas[j] -= oS.labelMat[j] * (Ei - Ej)/eta
        #步骤5：修剪alpha_j
        oS.alphas[j] = clipAlpha(oS.alphas[j],H,L)
        #更新Ej至误差缓存
        updateEk(oS, j)
        if (abs(oS.alphas[j] - alphaJold) < 0.00001):
            print("alpha_j变化太小")
            return 0
        #步骤6：更新alpha_i
        oS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j])
        #更新Ei至误差缓存
        updateEk(oS, i)
        #步骤7：更新b_1和b_2
        b1 = oS.b - Ei- oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.X[i,:]*oS.X[i,:].T - oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.X[i,:]*oS.X[j,:].T
        b2 = oS.b - Ej- oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.X[i,:]*oS.X[j,:].T - oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.X[j,:]*oS.X[j,:].T
        #步骤8：根据b_1和b_2更新b
        if (0 < oS.alphas[i]) and (oS.C > oS.alphas[i]): oS.b = b1
        elif (0 < oS.alphas[j]) and (oS.C > oS.alphas[j]): oS.b = b2
        else: oS.b = (b1 + b2)/2.0
        return 1
    else:
        return 0
```

```python
"""
完整的线性SMO算法
Parameters：
    dataMatIn - 数据矩阵
    classLabels - 数据标签
    C - 松弛变量
    toler - 容错率
    maxIter - 最大迭代次数
Returns:
    oS.b - SMO算法计算的b
    oS.alphas - SMO算法计算的alphas
"""
def smoP(dataMatIn, classLabels, C, toler, maxIter):
    oS = optStruct(np.mat(dataMatIn), np.mat(classLabels).transpose(), C, toler)                    #初始化数据结构
    iter = 0                                                                                #初始化当前迭代次数
    entireSet = True; alphaPairsChanged = 0
    while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):                            #遍历整个数据集都alpha也没有更新或者超过最大迭代次数,则退出循环
        alphaPairsChanged = 0
        if entireSet:                                                                                #遍历整个数据集
            for i in range(oS.m):# 对数据集中的所有行
                alphaPairsChanged += innerL(i,oS)                                                    #使用优化的SMO算法
                print("全样本遍历:第%d次迭代 样本:%d, alpha优化次数:%d" % (iter,i,alphaPairsChanged))
            iter += 1
        else:                                                                                #遍历非边界值
            nonBoundIs = np.nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]                        #遍历不在边界0和C的alpha
            for i in nonBoundIs:
                alphaPairsChanged += innerL(i,oS)
                print("非边界遍历:第%d次迭代 样本:%d, alpha优化次数:%d" % (iter,i,alphaPairsChanged))
            iter += 1
        if entireSet:                                                                                #遍历一次后改为非边界遍历
            entireSet = False
        elif (alphaPairsChanged == 0):                                                                #如果alpha没有更新,计算全样本遍历
            entireSet = True
        print("迭代次数: %d" % iter)
    return oS.b,oS.alphas                                                                             #返回SMO算法计算的b和alphas
```

测试

```python
%%time
dataArr, labelArr = loadDataSet('./data/testSet.txt')
b, alphas = smoP(dataArr, labelArr, 0.6, 0.001, 40)
```

    全样本遍历:第0次迭代 样本:0, alpha优化次数:1
    全样本遍历:第0次迭代 样本:1, alpha优化次数:1
    全样本遍历:第0次迭代 样本:2, alpha优化次数:2
    全样本遍历:第0次迭代 样本:3, alpha优化次数:3
    全样本遍历:第0次迭代 样本:4, alpha优化次数:4
    全样本遍历:第0次迭代 样本:5, alpha优化次数:5
    全样本遍历:第0次迭代 样本:6, alpha优化次数:5
    全样本遍历:第0次迭代 样本:7, alpha优化次数:5
    全样本遍历:第0次迭代 样本:8, alpha优化次数:6
    全样本遍历:第0次迭代 样本:9, alpha优化次数:6
    L==H
    全样本遍历:第0次迭代 样本:10, alpha优化次数:6
    全样本遍历:第0次迭代 样本:11, alpha优化次数:6
    全样本遍历:第0次迭代 样本:12, alpha优化次数:6
    全样本遍历:第0次迭代 样本:13, alpha优化次数:6
    全样本遍历:第0次迭代 样本:14, alpha优化次数:6
    全样本遍历:第0次迭代 样本:15, alpha优化次数:7
    全样本遍历:第0次迭代 样本:16, alpha优化次数:7
    L==H
    全样本遍历:第0次迭代 样本:17, alpha优化次数:7
    全样本遍历:第0次迭代 样本:18, alpha优化次数:8
    全样本遍历:第0次迭代 样本:19, alpha优化次数:8
    全样本遍历:第0次迭代 样本:20, alpha优化次数:8
    全样本遍历:第0次迭代 样本:21, alpha优化次数:8
    全样本遍历:第0次迭代 样本:22, alpha优化次数:8
    L==H
    全样本遍历:第0次迭代 样本:23, alpha优化次数:8
    全样本遍历:第0次迭代 样本:24, alpha优化次数:8
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:25, alpha优化次数:8
    L==H
    全样本遍历:第0次迭代 样本:26, alpha优化次数:8
    全样本遍历:第0次迭代 样本:27, alpha优化次数:8
    全样本遍历:第0次迭代 样本:28, alpha优化次数:8
    L==H
    全样本遍历:第0次迭代 样本:29, alpha优化次数:8
    全样本遍历:第0次迭代 样本:30, alpha优化次数:8
    全样本遍历:第0次迭代 样本:31, alpha优化次数:8
    全样本遍历:第0次迭代 样本:32, alpha优化次数:8
    全样本遍历:第0次迭代 样本:33, alpha优化次数:8
    全样本遍历:第0次迭代 样本:34, alpha优化次数:8
    全样本遍历:第0次迭代 样本:35, alpha优化次数:8
    全样本遍历:第0次迭代 样本:36, alpha优化次数:8
    全样本遍历:第0次迭代 样本:37, alpha优化次数:8
    全样本遍历:第0次迭代 样本:38, alpha优化次数:8
    全样本遍历:第0次迭代 样本:39, alpha优化次数:8
    全样本遍历:第0次迭代 样本:40, alpha优化次数:8
    全样本遍历:第0次迭代 样本:41, alpha优化次数:8
    全样本遍历:第0次迭代 样本:42, alpha优化次数:8
    全样本遍历:第0次迭代 样本:43, alpha优化次数:8
    全样本遍历:第0次迭代 样本:44, alpha优化次数:8
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:45, alpha优化次数:8
    L==H
    全样本遍历:第0次迭代 样本:46, alpha优化次数:8
    全样本遍历:第0次迭代 样本:47, alpha优化次数:8
    全样本遍历:第0次迭代 样本:48, alpha优化次数:8
    全样本遍历:第0次迭代 样本:49, alpha优化次数:8
    全样本遍历:第0次迭代 样本:50, alpha优化次数:8
    全样本遍历:第0次迭代 样本:51, alpha优化次数:8
    L==H
    全样本遍历:第0次迭代 样本:52, alpha优化次数:8
    全样本遍历:第0次迭代 样本:53, alpha优化次数:8
    L==H
    全样本遍历:第0次迭代 样本:54, alpha优化次数:8
    L==H
    全样本遍历:第0次迭代 样本:55, alpha优化次数:8
    全样本遍历:第0次迭代 样本:56, alpha优化次数:8
    全样本遍历:第0次迭代 样本:57, alpha优化次数:9
    全样本遍历:第0次迭代 样本:58, alpha优化次数:9
    全样本遍历:第0次迭代 样本:59, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:60, alpha优化次数:9
    全样本遍历:第0次迭代 样本:61, alpha优化次数:9
    全样本遍历:第0次迭代 样本:62, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:63, alpha优化次数:9
    全样本遍历:第0次迭代 样本:64, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:65, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:66, alpha优化次数:9
    全样本遍历:第0次迭代 样本:67, alpha优化次数:9
    全样本遍历:第0次迭代 样本:68, alpha优化次数:9
    全样本遍历:第0次迭代 样本:69, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:70, alpha优化次数:9
    全样本遍历:第0次迭代 样本:71, alpha优化次数:9
    全样本遍历:第0次迭代 样本:72, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:73, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:74, alpha优化次数:9
    全样本遍历:第0次迭代 样本:75, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:76, alpha优化次数:9
    全样本遍历:第0次迭代 样本:77, alpha优化次数:9
    全样本遍历:第0次迭代 样本:78, alpha优化次数:9
    全样本遍历:第0次迭代 样本:79, alpha优化次数:9
    全样本遍历:第0次迭代 样本:80, alpha优化次数:9
    全样本遍历:第0次迭代 样本:81, alpha优化次数:9
    全样本遍历:第0次迭代 样本:82, alpha优化次数:9
    全样本遍历:第0次迭代 样本:83, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:84, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:85, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:86, alpha优化次数:9
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:87, alpha优化次数:9
    全样本遍历:第0次迭代 样本:88, alpha优化次数:9
    全样本遍历:第0次迭代 样本:89, alpha优化次数:9
    全样本遍历:第0次迭代 样本:90, alpha优化次数:9
    全样本遍历:第0次迭代 样本:91, alpha优化次数:9
    全样本遍历:第0次迭代 样本:92, alpha优化次数:9
    全样本遍历:第0次迭代 样本:93, alpha优化次数:9
    全样本遍历:第0次迭代 样本:94, alpha优化次数:10
    全样本遍历:第0次迭代 样本:95, alpha优化次数:10
    全样本遍历:第0次迭代 样本:96, alpha优化次数:10
    alpha_j变化太小
    全样本遍历:第0次迭代 样本:97, alpha优化次数:10
    全样本遍历:第0次迭代 样本:98, alpha优化次数:10
    全样本遍历:第0次迭代 样本:99, alpha优化次数:10
    迭代次数: 1
    alpha_j变化太小
    非边界遍历:第1次迭代 样本:0, alpha优化次数:0
    alpha_j变化太小
    非边界遍历:第1次迭代 样本:4, alpha优化次数:0
    alpha_j变化太小
    非边界遍历:第1次迭代 样本:5, alpha优化次数:0
    alpha_j变化太小
    非边界遍历:第1次迭代 样本:8, alpha优化次数:0
    alpha_j变化太小
    非边界遍历:第1次迭代 样本:15, alpha优化次数:0
    alpha_j变化太小
    非边界遍历:第1次迭代 样本:17, alpha优化次数:0
    alpha_j变化太小
    非边界遍历:第1次迭代 样本:18, alpha优化次数:0
    非边界遍历:第1次迭代 样本:55, alpha优化次数:0
    非边界遍历:第1次迭代 样本:94, alpha优化次数:0
    迭代次数: 2
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:0, alpha优化次数:0
    全样本遍历:第2次迭代 样本:1, alpha优化次数:0
    全样本遍历:第2次迭代 样本:2, alpha优化次数:0
    全样本遍历:第2次迭代 样本:3, alpha优化次数:0
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:4, alpha优化次数:0
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:5, alpha优化次数:0
    全样本遍历:第2次迭代 样本:6, alpha优化次数:0
    全样本遍历:第2次迭代 样本:7, alpha优化次数:0
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:8, alpha优化次数:0
    全样本遍历:第2次迭代 样本:9, alpha优化次数:0
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:10, alpha优化次数:0
    全样本遍历:第2次迭代 样本:11, alpha优化次数:0
    全样本遍历:第2次迭代 样本:12, alpha优化次数:0
    全样本遍历:第2次迭代 样本:13, alpha优化次数:0
    全样本遍历:第2次迭代 样本:14, alpha优化次数:0
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:15, alpha优化次数:0
    全样本遍历:第2次迭代 样本:16, alpha优化次数:0
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:17, alpha优化次数:0
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:18, alpha优化次数:0
    全样本遍历:第2次迭代 样本:19, alpha优化次数:0
    全样本遍历:第2次迭代 样本:20, alpha优化次数:0
    全样本遍历:第2次迭代 样本:21, alpha优化次数:0
    全样本遍历:第2次迭代 样本:22, alpha优化次数:0
    L==H
    全样本遍历:第2次迭代 样本:23, alpha优化次数:0
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:24, alpha优化次数:0
    全样本遍历:第2次迭代 样本:25, alpha优化次数:0
    全样本遍历:第2次迭代 样本:26, alpha优化次数:0
    全样本遍历:第2次迭代 样本:27, alpha优化次数:0
    全样本遍历:第2次迭代 样本:28, alpha优化次数:0
    L==H
    全样本遍历:第2次迭代 样本:29, alpha优化次数:0
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:30, alpha优化次数:0
    全样本遍历:第2次迭代 样本:31, alpha优化次数:0
    全样本遍历:第2次迭代 样本:32, alpha优化次数:0
    全样本遍历:第2次迭代 样本:33, alpha优化次数:0
    全样本遍历:第2次迭代 样本:34, alpha优化次数:0
    全样本遍历:第2次迭代 样本:35, alpha优化次数:0
    全样本遍历:第2次迭代 样本:36, alpha优化次数:0
    全样本遍历:第2次迭代 样本:37, alpha优化次数:0
    全样本遍历:第2次迭代 样本:38, alpha优化次数:0
    全样本遍历:第2次迭代 样本:39, alpha优化次数:0
    全样本遍历:第2次迭代 样本:40, alpha优化次数:0
    全样本遍历:第2次迭代 样本:41, alpha优化次数:0
    全样本遍历:第2次迭代 样本:42, alpha优化次数:0
    全样本遍历:第2次迭代 样本:43, alpha优化次数:0
    全样本遍历:第2次迭代 样本:44, alpha优化次数:0
    全样本遍历:第2次迭代 样本:45, alpha优化次数:0
    全样本遍历:第2次迭代 样本:46, alpha优化次数:0
    全样本遍历:第2次迭代 样本:47, alpha优化次数:0
    全样本遍历:第2次迭代 样本:48, alpha优化次数:0
    全样本遍历:第2次迭代 样本:49, alpha优化次数:0
    全样本遍历:第2次迭代 样本:50, alpha优化次数:0
    全样本遍历:第2次迭代 样本:51, alpha优化次数:0
    L==H
    全样本遍历:第2次迭代 样本:52, alpha优化次数:0
    全样本遍历:第2次迭代 样本:53, alpha优化次数:0
    L==H
    全样本遍历:第2次迭代 样本:54, alpha优化次数:0
    全样本遍历:第2次迭代 样本:55, alpha优化次数:0
    全样本遍历:第2次迭代 样本:56, alpha优化次数:0
    全样本遍历:第2次迭代 样本:57, alpha优化次数:0
    全样本遍历:第2次迭代 样本:58, alpha优化次数:0
    全样本遍历:第2次迭代 样本:59, alpha优化次数:0
    全样本遍历:第2次迭代 样本:60, alpha优化次数:0
    全样本遍历:第2次迭代 样本:61, alpha优化次数:0
    全样本遍历:第2次迭代 样本:62, alpha优化次数:0
    全样本遍历:第2次迭代 样本:63, alpha优化次数:0
    全样本遍历:第2次迭代 样本:64, alpha优化次数:0
    全样本遍历:第2次迭代 样本:65, alpha优化次数:0
    全样本遍历:第2次迭代 样本:66, alpha优化次数:0
    全样本遍历:第2次迭代 样本:67, alpha优化次数:0
    全样本遍历:第2次迭代 样本:68, alpha优化次数:0
    全样本遍历:第2次迭代 样本:69, alpha优化次数:0
    全样本遍历:第2次迭代 样本:70, alpha优化次数:0
    全样本遍历:第2次迭代 样本:71, alpha优化次数:0
    全样本遍历:第2次迭代 样本:72, alpha优化次数:0
    全样本遍历:第2次迭代 样本:73, alpha优化次数:0
    全样本遍历:第2次迭代 样本:74, alpha优化次数:0
    全样本遍历:第2次迭代 样本:75, alpha优化次数:0
    全样本遍历:第2次迭代 样本:76, alpha优化次数:0
    全样本遍历:第2次迭代 样本:77, alpha优化次数:0
    全样本遍历:第2次迭代 样本:78, alpha优化次数:0
    全样本遍历:第2次迭代 样本:79, alpha优化次数:0
    全样本遍历:第2次迭代 样本:80, alpha优化次数:0
    全样本遍历:第2次迭代 样本:81, alpha优化次数:0
    全样本遍历:第2次迭代 样本:82, alpha优化次数:0
    全样本遍历:第2次迭代 样本:83, alpha优化次数:0
    全样本遍历:第2次迭代 样本:84, alpha优化次数:0
    全样本遍历:第2次迭代 样本:85, alpha优化次数:0
    全样本遍历:第2次迭代 样本:86, alpha优化次数:0
    全样本遍历:第2次迭代 样本:87, alpha优化次数:0
    全样本遍历:第2次迭代 样本:88, alpha优化次数:0
    全样本遍历:第2次迭代 样本:89, alpha优化次数:0
    全样本遍历:第2次迭代 样本:90, alpha优化次数:0
    全样本遍历:第2次迭代 样本:91, alpha优化次数:0
    全样本遍历:第2次迭代 样本:92, alpha优化次数:0
    全样本遍历:第2次迭代 样本:93, alpha优化次数:0
    全样本遍历:第2次迭代 样本:94, alpha优化次数:0
    全样本遍历:第2次迭代 样本:95, alpha优化次数:0
    全样本遍历:第2次迭代 样本:96, alpha优化次数:0
    alpha_j变化太小
    全样本遍历:第2次迭代 样本:97, alpha优化次数:0
    全样本遍历:第2次迭代 样本:98, alpha优化次数:0
    全样本遍历:第2次迭代 样本:99, alpha优化次数:0
    迭代次数: 3
    CPU times: user 152 ms, sys: 52.8 ms, total: 205 ms
    Wall time: 149 ms

用时方面，在相同配置的主机上，时间从7秒下降到了不到0.3秒。

利用计算得到的alpha值，可以计算w用于构建超平面。

```python
def calcWs(alphas, dataArr, classLabels):
    X = np.mat(dataArr); labelMat = np.mat(classLabels).transpose()
    m,n = X.shape
    w = np.zeros((n,1))
    for i in range(m):
        w += np.multiply(alphas[i]*labelMat[i],X[i,:].T)
    return w
```

```python
ws = calcWs(alphas,dataArr,labelArr)
ws
```

    array([[ 0.65263178],
           [-0.17581327]])

分类

```python
datMat = np.mat(dataArr)
print(datMat[0]*np.mat(ws)+b)
```

    [[-0.93044397]]

若该值大于0，其属于1类；若该值小于0，属于-1类，这里得到的分类结果是-1，我们验证一下是不是一样的：

```python
labelArr[0]
```

    -1.0

# SVM in scikit-learn

查阅scikit-learn工具包中支持向量机的相关说明，了解分类器函数使用方法。

参考这一篇进行学习：[机器学习笔记3-sklearn支持向量机](https://www.jianshu.com/p/a9f9954355b3?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation)

调包当然是非常简单的拉~

```python
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(dataArr, labelArr, test_size = 0.2, random_state = 100,stratify=labelArr)

clf = SVC()
clf.fit(X_train, y_train)
print('预测样例：')
print(clf.predict(np.array(X_test[0:5])))
print(y_test[0:5])
train_score = clf.score(X_train,y_train)
test_score  = clf.score(X_test,y_test)

print("Train Score: %.3f; Test Score %.3f." % (train_score,test_score))
```

    预测样例：
    [ 1. -1. -1. -1. -1.]
    [1.0, -1.0, -1.0, -1.0, -1.0]
    Train Score: 1.000; Test Score 1.000.

好屌，难道是因为是二分类，或者这个样本集很好分吗？什么参数都没有调，准确率达到了100\%。

因为准确率过高，所以后续对SVM的优化需要基于新的数据集，否则无法评判优化的效果。

官方源码：

```Python
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、kernel、degree、gamma、coef0。

* C：C-SVC的惩罚参数C,默认值是1.0
> C越大，相当于惩罚松弛变量，希望松弛变量接近0，即对误分类的惩罚增大，趋向于对训练集全分对的情况，这样对训练集测试时准确率很高，但泛化能力弱。C值小，对误分类的惩罚减小，允许容错，将他们当成噪声点，泛化能力较强。

* kernel ：核函数，默认是rbf，可以是`linear`, `poly`, `rbf`, `sigmoid`
> 线性：$\kappa(\boldsymbol{x}_i,\boldsymbol{x}_j)=\boldsymbol{x}_i^T\boldsymbol{x}_j$
>
> 多项式： $\kappa(\boldsymbol{x}_i,\boldsymbol{x}_j)=(\boldsymbol{x}_i^T\boldsymbol{x}_j)^d$
>
> RBF函数/高斯核函数： $\kappa(\boldsymbol{x}_i,\boldsymbol{x}_j)=\exp\left(-\dfrac{||\boldsymbol{x}_i-\boldsymbol{x}_j||^2}{2\sigma^2}\right)$
>
> sigmoid：$\kappa(\boldsymbol{x}_i,\boldsymbol{x}_j)=\tanh{(\beta\boldsymbol{x}_i^T\boldsymbol{x}_j+\theta)}$

* degree ：多项式poly函数的维度，默认是3，选择其他核函数时会被忽略。

* gamma ： `rbf`,`poly` 和`sigmoid`的核函数参数。默认是`auto`，则会选择1/n_features

* coef0 ：核函数的常数项。对于`poly`和 `sigmoid`有用。

# 作业二：
已知正例点 $x_1=(1,2)^T$ , $x_2=(2,3)^T$ , $x_3=(3,3)^T$，负例点 $x_4=(2,1)^T$ , $x_5=(3,2)^T$ ,试求最大间隔分离超平面和分类决策函数，并在图上画出分离超平面，间隔边界以及支持向量。

(统计学习方法第七章课后习题2）

**解**

参考最大间隔算法，根据训练数据集构造约束最优化问题

$$
\min\frac{1}{2}(w_1^2+w_2^2)\\
s.t. \left\{
\begin{align}
w_1+2w_2+b&\ge 1 \tag{1}\\
2w_1+3w_2+b&\ge 1 \tag{2}\\
3w_1+3w_2+b&\ge 1 \tag{3}\\
-2w_1-w_2-b&\ge 1 \tag{4}\\
-3w_1-2w_2-b&\ge 1 \tag{5}
\end{align}
\right.
$$

求得此最优化问题的解$w_1=-1,\ w_2=2,\ b=-2$。于是最大间隔分离超平面为
$$
-x^{(1)}+2x^{(2)}-2=0
$$
支持向量 $x_1=(1,2)^T$ , $x_3=(3,3)^T$ , $x_5=(3,2)^T$.
分类决策函数
$$
f(x)=\text{sign}(-x^{(1)}+2x^{(2)}-2)
$$

用程序进行同等的求解验证：

```python
from sklearn import svm
x=[[1, 2], [2, 3], [3, 3], [2, 1], [3, 2]]
y=[1, 1, 1, -1, -1]
clf = svm.SVC(kernel='linear',C=10000)
clf.fit(x, y)
print(clf.coef_)
print(clf.intercept_)
```

    [[-1.  2.]]
    [-2.]

得到的解相同，接下来可以进行一定的可视化：

```python
import matplotlib.pyplot as plt
import numpy as np
plt.scatter([i[0] for i in x], [i[1] for i in x], c=y)
xaxis = np.linspace(0, 3.5)
w = clf.coef_[0]
a = -w[0] / w[1]
y_sep = a * xaxis - (clf.intercept_[0]) / w[1]
b = clf.support_vectors_[0]
yy_down = a * xaxis + (b[1] - a * b[0])
b = clf.support_vectors_[-1]
yy_up = a * xaxis + (b[1] - a * b[0])
plt.plot(xaxis, y_sep, 'k-')
plt.plot(xaxis, yy_down, 'k--')
plt.plot(xaxis, yy_up, 'k--')
plt.scatter (clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=150, facecolors='none', edgecolors='k')
plt.show()
```

# + 另一种SMO

另一种参考统计学习方法上的思路，

外层循环选择**违背KKT条件最严重**的 $\alpha$ 作为 $\alpha_i$，首先遍历间隔边界上的点（ $0 \lt \alpha_i \lt C$ ），如果这些点都满足KKT条件，则遍历所有样本点。

内层循环选择启发式方法选择：

* 如果 $E_i$ 为正，选择最小的 $E$ 作为 $E_j$；如果 $E_i$ 为负，选择最大的 $E$ 作为 $E_j$。（总之使步长最大）
* 如果上一步都不能使目标函数有足够的下降，则遍历间隔边界上的支持向量点，依次将其作为 $\alpha_j$ 使用。
* 如果都不能使目标函数有足够的下降，则遍历训练集的所有向量点，依次将其作为 $\alpha_j$ 使用。
* 如果所有点都不能使目标函数有足够的下降，则放弃当前 $\alpha_i$ ，通过外层循环生成新的 $\alpha_i$ 。

参考[fengdu78/lihang-code](https://github.com/fengdu78/lihang-code)的实现代码：

```python
class SVM:
    def __init__(self, max_iter=100, kernel='linear'):
        self.max_iter = max_iter
        self._kernel = kernel

    def init_args(self, features, labels):
        self.m, self.n = features.shape
        self.X = features
        self.Y = labels
        self.b = 0.0

        # 将Ei保存在一个列表里
        self.alpha = np.ones(self.m)
        self.E = [self._E(i) for i in range(self.m)]
        # 松弛变量
        self.C = 1.0

    def _KKT(self, i):
        y_g = self._g(i) * self.Y[i]
        if self.alpha[i] == 0:
            return y_g >= 1
        elif 0 < self.alpha[i] < self.C:
            return y_g == 1
        else:
            return y_g <= 1

    # g(x)预测值，输入xi（X[i]）
    def _g(self, i):
        r = self.b
        for j in range(self.m):
            r += self.alpha[j] * self.Y[j] * self.kernel(self.X[i], self.X[j])
        return r

    # 核函数
    def kernel(self, x1, x2):
        if self._kernel == 'linear':
            return sum([x1[k] * x2[k] for k in range(self.n)])
        elif self._kernel == 'poly':
            return (sum([x1[k] * x2[k] for k in range(self.n)]) + 1)**2

        return 0

    # E（x）为g(x)对输入x的预测值和y的差
    def _E(self, i):
        return self._g(i) - self.Y[i]

    def _init_alpha(self):
        # 外层循环首先遍历所有满足0<a<C的样本点，检验是否满足KKT
        index_list = [i for i in range(self.m) if 0 < self.alpha[i] < self.C]
        # 否则遍历整个训练集
        non_satisfy_list = [i for i in range(self.m) if i not in index_list]
        index_list.extend(non_satisfy_list)

        for i in index_list:
            if self._KKT(i):
                continue

            E1 = self.E[i]
            # 如果E2是+，选择最小的；如果E2是负的，选择最大的
            if E1 >= 0:
                j = min(range(self.m), key=lambda x: self.E[x])
            else:
                j = max(range(self.m), key=lambda x: self.E[x])
            return i, j

    def _compare(self, _alpha, L, H):
        if _alpha > H:
            return H
        elif _alpha < L:
            return L
        else:
            return _alpha

    def fit(self, features, labels):
        self.init_args(features, labels)

        for t in range(self.max_iter):
            # train
            i1, i2 = self._init_alpha()

            # 边界
            if self.Y[i1] == self.Y[i2]:
                L = max(0, self.alpha[i1] + self.alpha[i2] - self.C)
                H = min(self.C, self.alpha[i1] + self.alpha[i2])
            else:
                L = max(0, self.alpha[i2] - self.alpha[i1])
                H = min(self.C, self.C + self.alpha[i2] - self.alpha[i1])

            E1 = self.E[i1]
            E2 = self.E[i2]
            # eta=K11+K22-2K12
            eta = self.kernel(self.X[i1], self.X[i1]) + self.kernel(
                self.X[i2],
                self.X[i2]) - 2 * self.kernel(self.X[i1], self.X[i2])
            if eta <= 0:
                # print('eta <= 0')
                continue

            alpha2_new_unc = self.alpha[i2] + self.Y[i2] * (
                E1 - E2) / eta  #此处有修改，根据书上应该是E1 - E2，书上130-131页
            alpha2_new = self._compare(alpha2_new_unc, L, H)

            alpha1_new = self.alpha[i1] + self.Y[i1] * self.Y[i2] * (
                self.alpha[i2] - alpha2_new)

            b1_new = -E1 - self.Y[i1] * self.kernel(self.X[i1], self.X[i1]) * (
                alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(
                    self.X[i2],
                    self.X[i1]) * (alpha2_new - self.alpha[i2]) + self.b
            b2_new = -E2 - self.Y[i1] * self.kernel(self.X[i1], self.X[i2]) * (
                alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(
                    self.X[i2],
                    self.X[i2]) * (alpha2_new - self.alpha[i2]) + self.b

            if 0 < alpha1_new < self.C:
                b_new = b1_new
            elif 0 < alpha2_new < self.C:
                b_new = b2_new
            else:
                # 选择中点
                b_new = (b1_new + b2_new) / 2

            # 更新参数
            self.alpha[i1] = alpha1_new
            self.alpha[i2] = alpha2_new
            self.b = b_new

            self.E[i1] = self._E(i1)
            self.E[i2] = self._E(i2)
        return 'train done!'

    def predict(self, data):
        r = self.b
        for i in range(self.m):
            r += self.alpha[i] * self.Y[i] * self.kernel(data, self.X[i])

        return 1 if r > 0 else -1

    def score(self, X_test, y_test):
        right_count = 0
        for i in range(len(X_test)):
            result = self.predict(X_test[i])
            if result == y_test[i]:
                right_count += 1
        return right_count / len(X_test)

    def _weight(self):
        # linear model
        yx = self.Y.reshape(-1, 1) * self.X
        self.w = np.dot(yx.T, self.alpha)
        return self.w
```

```python
from sklearn.model_selection import  train_test_split
svm = SVM(max_iter=200)
svm.fit(np.array(X_train), np.array(y_train))
svm.score(X_test, y_test)
```

    0.55

感觉比sklearn的默认状态要弱很多。。


# Machine Learning: An Overview

Table of contents:

- [Why Machine Learning?](#why-machine-learning)
- [Types of Learning](#types-of-learning)
- [Foundations of Machine Learning](#foundations-of-machine-learning)
- [A Formal Model for Machine Learning Theory](#a-formal-model-for-machine-learning-theory)
  - [Empirical Risk Minimization](#empirical-risk-minimization)
- [How do we assess learning?](#how-do-we-assess-learning)
- [Training and Validation data](#training-and-validation-data)
- [The Bias/ Variance tradeoff](#the-bias-variance-tradeoff)
- [Evaluation metrics](#evaluation-metrics)
  - [Confusion Matrix](#confusion-matrix)
  - [Root Mean Squared Error (RMSE)](#root-mean-squared-error)
  - [Resampling](#resampling)

Goal: Teach the machine to learn based on the input they receive from the Environment.

> Tom Mitchell (1997) defines machine learning as the ability for a machine to gain expertise on a particular task based on experience.

## Why Machine Learning?
- Solving complexity: Excels in building computational engines to solve task that cannot possibly be explicitly programmed.
- Examples include natural language understanding, speech recognition and facial recognition.

## Types of Learning
Machine Learning can be categorized into (at least) three components based on its approach.

The three predominant schemes of learning are:
- Supervised,
- Unsupervised, and
- Reinforcement Learning

**Supervised Learning**
- Each data point is associated with a label
- Goal: Teach the computer using this labeled data.
- Learning: The computer learns the patterns from data.
- Inference: Makes decisions about "unknown" samples.

**Unsupervised Learning**
- No corresponding labels - no guidance.
- Goal: Computer attempts to determine data's unknown structure.
- Scheme: By "grouping" similar samples together adaptively.

**Reinforcement Learning**
- Reinforcement Learning: Agent interacts with an Environment.
- Scheme: A "feedback configuration".
- Method: Chooses an action from the set of actions.
- Learning: Based on the responses from the Environment.

## Foundations of Machine Learning
The foundational disciplines that contribute to the field of machine learning are:

- Statistics,
- Mathematics,
- The Theory of Computation, and to a considerable extent
- Behavioral Psychology

The diagram below visually shows the interaction between these fields.

## A Formal Model for Machine Learning Theory

The learner’s input:
- Domain set: An arbitrary set $\mathcal{X}$ that characterizes a set of objects that we want to label. The domain points are represented as a vector of features and are also called instances, with $\mathcal{X}$ the instance space.
- Label set: Let $\mathcal{Y}$ denote our set of possible labels. For example, let the label set be restricted to a two-element set, {0, 1} or {−1, +1}.
- Training data: $S = ((x_1, y_1)...(x_m, y_m))$ is a finite sequence of pairs in $\mathcal{X} \times \mathcal{Y}$ (i.e. a sequence of labeled domain points). The training data is the input that the learner has access to. $S$ is referred to as a training set.

The learner’s output:
The learner returns a prediction rule, $h : \mathcal{X} \rightarrow \mathcal{Y}$. This function is also called a predictor, a hypothesis, or a classifier. The predictor is used to predict the label of new domain points.
The notation $A(S)$ denotes the hypothesis that an algorithm, $A$, outputs, operating on the training set $S$.

The data generating model:
The training set are generated by some probability distribution that is "unknown" to the learner. The probability distribution over $\mathcal{X}$ is denoted by $\mathcal{D}$.
In instances where the correct labelling is known, then each pair in the training data $\mathcal{S}$ is generated by first sampling a point $x_i$ according to $\mathcal{D}$ and then labeling it by $f$, where,  $\;f : \mathcal{X} \rightarrow \mathcal{Y}$, and that $y_i = f(x_i)$ for all $i$.

Measuring success:
The error of a classifier is the probability that it does not predict the correct label on a random data point generated by the underlying distribution.
Therefore, the error of $h$ is the probability to draw a random instance $x$, according to the distribution $\mathcal{D}$, such that $h(x)$ does not equal $f(x)$.

The error of the prediction rule such $\;h : \mathcal{X} \rightarrow \mathcal{Y}\;$ is the probability of randomly choosing an example $x$ for which $h(x) \neq f(x)$:

\begin{equation}
L_{\mathcal{D},\;f} (h) \quad \equiv \quad \mathbb{P}_{x\sim D} [h(x) \neq f(x)] \quad \equiv \quad \mathcal{D}(\{x : h(x) \neq f(x)\}).
\end{equation}

where,
- The subscript $(\mathcal{D}, f)$: indicates that the error is measured with respect to the probability distribution \mathcal{D} and the correct labeling function $f$.
- $L_{\mathcal{D},\;f} (h)$: is the generalization error, the risk, or the true error of $h$.

Note:
The learner is blind to the underlying distribution $\mathcal{D}$ over the world and to the labeling function $f$.

### Empirical Risk Minimization

The goal of the learning algorithm is to find the $h_S$ that minimizes the error with respect to the unknown $\mathcal{D}$ and $f$. Since the learner is unaware of $\mathcal{D}$ and $f$, the true error is not directly
available to the learner.

The training error (also called empirical error or empirical risk) is the error the classifier incurs over the training sample:

\begin{equation}
L_S(h) = \frac{|\{i \in [m] : h(x_i) \neq y_i\}|} {m}
\end{equation}

where,
- [m] is the total number of training examples.

The hypothesis $h$ that minimizes the error $L_S(h)$ is called Empirical Risk Minimization or ERM.

## How do we assess learning?
Assume a teacher teaches a physics class for three months, and at the end of the lecture sessions, the teacher administers a test to ascertain if the student has learned.

Let us consider two different sub-plots:
1. The teacher tests the student with an exact word for word replica of questions that he used as examples while teaching.
2. The teacher evaluates the student with an entirely different but similar problem set based on the principles taught in class.
In which of the subplots can the teacher be confident that the student has learned?

### Norms of Learning
1. Memorization: Memorization is the act of mastering and storing a pattern for future recollection. Therefore it is inaccurate to use training samples to carry out learning evaluation. In machine learning, this is also known as data snooping.
2. Generalization: The ability for the student to extrapolate using the principles taught in class to solve new examples is known as Generalization.
Hence, we can conclude that learning is the ability to generalize to new cases.

## Training and Validation data
Again, the goal of machine learning is to predict or classify outcomes on unseen observations. Hence, the dataset is partitioned into:
- Training set: for training the model.
- Validation set: for fine-tuning the model parameters.
- Test set: for assessing the true model performance on unseen examples.

A common strategy is to split 60\% of the dataset for training, 20% for validation, and the remaining 20% for testing. This is commonly known as the 60/20/20 rule.

## The Bias/ Variance tradeoff
- The bias/ variance tradeoff is critical for assessing the performance of the machine learning model.
- Bias is when the learning algorithm fails oversimplifies the learning problem and fails to generalize to new examples.
- Variance is when the model "closely" learns the irreducible error of the dataset. This leads to high variability in the presence of an unseen observation.

The goal is to have a model that generalizes to new examples. Finding this middle ground is what is known as the bias/ variance tradeoff.

## Evaluation metrics
Evaluation metrics give us a way to measure the performance of our model. Let's see some common evaluation metrics.

Classification.
- Confusion matrix.
- AUC-ROC (Area under ROC curve).

Regression
- Root Mean Squared Error (RMSE).
- R-squared ($R^2$).

### Confusion Matrix
Confusion matrix is one of the more popular metrics for assessing the performance of a classification supervised machine learning model.

### Root Mean Squared Error (RMSE)
- Root Mean Squared Error shortened for RMSE is an important evaluation metric in supervised machine learning for regression problems.
- The goal of RMSE is to calculate the error difference between the original targets and the predicted targets made by the learning algorithm.

\begin{equation}
RMSE=\sqrt{{\displaystyle \frac{\sum_{i=1}^{n}{(y_{i}-{\hat{y}}_{i})^{2}}}{{n}}}}.
\end{equation}

### Resampling
Resampling is another important concept for evaluating the performance of a supervised learning algorithm and it involves selecting a subset of the available dataset, training on that data subset and using the reminder of the data to evaluate the trained model. Methods for resampling include:
- The validation set technique,
- The Leave-one-out cross-validation technique (LOOCV), and
- The k-fold cross-validation technique.

**Validation set**

**k-Fold validation**


#  Machine Learning con Python

## Índice
1. **[¿Qué es Machine Learning?](#1.-¿Qué-es-Machine-Learning?)**

2. **[Tipos de Machine Learning](#2.-Tipos-de-Machine-Learning)**
   * [2.1 Aprendizaje supervisado](#2.1-Aprendizaje-supervisado)
   * [2.2 Aprendizaje no supervisado](#2.2-Aprendizaje-no-supervisado)
   * [2.3 Aprendizaje por refuerzo](#2.3-Aprendizaje-por-refuerzo)

3. **[Sobreentrenamiento y validación](#3.-Sobreentrenamiento-y-validación)**
   * [3.1 ¿Qué es el sobreentrenamiento?](#3.1-¿Qué-es-el-sobreentrenamiento?)
   * [3.2 Cómo evitar el sobreentrenamiento](#3.2-Cómo-evitar-el-sobreentrenamiento)
   * [3.3 Tipos de validaciones cruzadas](#3.3-Tipos-de-validaciones-cruzadas)
       * [3.3.1 Validación cruzada de K iteraciones](#3.3.1-Validación-cruzada-de-K-iteraciones)
       * [3.3.2 Validación cruzada aleatoria](#3.3.2-Validación-cruzada-aleatoria)
       * [3.3.3 Validación cruzada dejando uno fuera](#3.3.3-Validación-cruzada-dejando-uno-fuera)
   * [3.4 Cálculo del error](#3.4-Cálculo-del-error)
       * [3.4.1 Error de la validación cruzada de K iteraciones](#3.4.1-Error-de-la-validación-cruzada-de-K-iteraciones)
       * [3.4.2 Error de la validación cruzada aleatoria](#3.4.2-Error-de-la-validación-cruzada-aleatoria)
       * [3.4.3 Error de la validación cruzada dejando uno fuera](#3.4.3-Error-de-la-validación-cruzada-dejando-uno-fuera)

4. **[Pasos para construir un modelo de machine learning](#4.-Pasos-para-construir-un-modelo-de-machine-learning)**

5. **[Machine learning en Python](#5.-Machine-learning-en-Python)**
   * [5.1 Scikit-Learn](#5.1-Scikit-Learn)
   * [5.2 Statsmodels](#5.2-Statsmodels)
   * [5.3 PyMC](#5.3-PyMC)
   * [5.4 NTLK](#5.4-NTLK)
   * [5.5 Algoritmos más utilizados](#5.5-Algoritmos-más-utilizados)
   * [5.6 Lectura recomedada](#5.6-Lectura-recomedada)

6. **[K-Nearest Neighbors Classification (K-vecinos más próximos)](#8.-K-Nearest-Neighbors-Classification)**
   * [6.1 Algoritmo](#8.1-Algoritmo)
   * [6.1 K-Means en Sklarn: KNeighborsClassifier](#8.1-K-Means-en-Sklarn:-KNeighborsClassifier)
       * [6.1.1 Haciendo predicciones](#8.1.1-Haciendo-predicciones)
       * [6.1.2 Classification Report](#8.1.2-Classification-Report)

# [1.](#Índice) ¿Qué es Machine Learning?

Una de las ramas de estudio que cada vez está ganando más popularidad dentro de las ciencias de la computación es el * **aprendizaje automático o Machine Learning** *. Muchos de los servicios que utilizamos en nuestro día a día como google, gmail, netflix, spotify o amazon se valen de las herramientas que les brinda el Machine Learning para alcanzar un servicio cada vez más personalizado y lograr así ventajas competitivas sobre sus rivales.

Pero, ¿qué es exactamente Machine Learning?. El Machine Learning es el diseño y estudio de las herramientas informáticas que utilizan la experiencia pasada para tomar decisiones futuras; es el estudio de programas que pueden aprender de los datos. El objetivo fundamental del Machine Learning es generalizar, o inducir una regla desconocida a partir de ejemplos donde esa regla es aplicada. El ejemplo más típico donde podemos ver el uso del Machine Learning es en el filtrado de los correos basura o spam. Mediante la observación de miles de correos electrónicos que han sido marcados previamente como basura, los filtros de spam aprenden a clasificar los mensajes nuevos. El Machine Learning combina conceptos y técnicas de diferentes áreas del conocimiento, como las matemáticas, estadísticas y las ciencias de la computación; por tal motivo, hay muchas maneras de aprender la disciplina.

# [2.](#Índice) Tipos de Machine Learning

El Machine Learning tiene una amplia gama de aplicaciones, incluyendo motores de búsqueda, diagnósticos médicos, detección de fraude en el uso de tarjetas de crédito, análisis del mercado de valores, clasificación de secuencias de ADN, reconocimiento del habla y del lenguaje escrito, juegos y robótica. Pero para poder abordar cada uno de estos temas es crucial en primer lugar distinguir los distintos tipos de problemas de Machine Learning con los que nos podemos encontrar.

## [2.1](#Índice) Aprendizaje supervisado

En los problemas de aprendizaje supervisado se enseña o entrena al algoritmo a partir de datos que ya vienen etiquetados con la respuesta correcta. Cuanto mayor es el conjunto de datos más el algoritmo puede aprender sobre el tema. Una vez concluído el entrenamiento, se le brindan nuevos datos, ya sin las etiquetas de las respuestas correctas, y el algoritmo de aprendizaje utiliza la experiencia pasada que adquirió durante la etapa de entrenamiento para predecir un resultado. Esto es similar al método de aprendizaje que se utiliza en las escuelas, donde se nos enseñan problemas y las formas de resolverlos, para que luego podamos aplicar los mismos métodos en situaciones similares.

## [2.2](#Índice) Aprendizaje no supervisado

En los problemas de aprendizaje no supervisado el algoritmo es entrenado usando un conjunto de datos que no tiene ninguna etiqueta; en este caso, nunca se le dice al algoritmo lo que representan los datos. La idea es que el algoritmo pueda encontrar por si solo patrones que ayuden a entender el conjunto de datos. El aprendizaje no supervisado es similar al método que utilizamos para aprender a hablar cuando somos bebes, en un principio escuchamos hablar a nuestros padres y no entendemos nada; pero a medida que vamos escuchando miles de conversaciones, nuestro cerebro comenzará a formar un modelo sobre cómo funciona el lenguaje y comenzaremos a reconocer patrones y a esperar ciertos sonidos.

## [2.3](#Índice) Aprendizaje por refuerzo

En los problemas de aprendizaje por refuerzo, el algoritmo aprende observando el mundo que le rodea. Su información de entrada es el feedback o retroalimentación que obtiene del mundo exterior como respuesta a sus acciones. Por lo tanto, el sistema aprende a base de ensayo-error. Un buen ejemplo de este tipo de aprendizaje lo podemos encontrar en los juegos, donde vamos probando nuevas estrategias y vamos seleccionando y perfeccionando aquellas que nos ayudan a ganar el juego. A medida que vamos adquiriendo más practica, el efecto acumulativo del refuerzo a nuestras acciones victoriosas terminará creando una estrategia ganadora.

# [3.](#Índice) Sobreentrenamiento y validación

## [3.1](#Índice) ¿Qué es el sobreentrenamiento?

Como mencionamos cuando definimos al Machine Learning, la idea fundamental es encontrar patrones que podamos generalizar para luego poder aplicar esta generalización sobre los casos que todavía no hemos observado y realizar predicciones. Pero también puede ocurrir que durante el entrenamiento solo descubramos casualidades en los datos que se parecen a patrones interesantes, pero que no generalicen. Esto es lo que se conoce con el nombre de **sobreentrenamiento o sobreajuste ó en inglés overfitting** .

El sobreentrenamiento es la tendencia que tienen la mayoría de los algoritmos de Machine Learning a ajustarse a unas características muy específicas de los datos de entrenamiento que no tienen relación causal con la función objetivo que estamos buscando para generalizar. El ejemplo más extremo de un modelo sobreentrenado es un modelo que solo memoriza las respuestas correctas; este modelo al ser utilizado con datos que nunca antes ha visto va a tener un rendimiento azaroso, ya que nunca logró generalizar un patrón para predecir.

    En aprendizaje automático, el sobreajuste (también es frecuente emplear el término en inglés overfitting) es el efecto de sobreentrenar un algoritmo de aprendizaje con unos ciertos datos para los que se conoce el resultado deseado. El algoritmo de aprendizaje debe alcanzar un estado en el que será capaz de predecir el resultado en otros casos a partir de lo aprendido con los datos de entrenamiento, generalizando para poder resolver situaciones distintas a las acaecidas durante el entrenamiento. Sin embargo, cuando un sistema se entrena demasiado (se sobreentrena) o se entrena con datos extraños, el algoritmo de aprendizaje puede quedar ajustado a unas características muy específicas de los datos de entrenamiento que no tienen relación causal con la función objetivo. Durante la fase de sobreajuste el éxito al responder las muestras de entrenamiento sigue incrementándose mientras que su actuación con muestras nuevas va empeorando.

## [3.2](#Índice) Cómo evitar el sobreentrenamiento

Como mencionamos anteriormente, todos los modelos de Machine Learning tienen tendencia al sobreentrenamiento; es por esto que debemos aprender a convivir con el mismo y tratar de tomar medidas preventivas para reducirlo lo más posible. Las dos principales estrategias para lidiar son el sobreentrenamiento son: la retención de datos y la validación cruzada.

En el primer caso, la idea es dividir nuestro conjunto de datos, en uno o varios conjuntos de entrenamiento y otro/s conjuntos de evaluación. Es decir, que no le vamos a pasar todos nuestros datos al algoritmo durante el entrenamiento, sino que vamos a retener una parte de los datos de entrenamiento para realizar una evaluación de la efectividad del modelo. Con esto lo que buscamos es evitar que los mismos datos que usamos para entrenar sean los mismos que utilizamos para evaluar. De esta forma vamos a poder analizar con más precisión como el modelo se va comportando a medida que más lo vamos entrenando y poder detectar el punto crítico en el que el modelo deja de generalizar y comienza a sobreajustarse a los datos de entrenamiento.

**La validación cruzada** es un procedimiento más sofisticado que el anterior. En lugar de solo obtener una simple estimación de la efectividad de la generalización; la idea es realizar un análisis estadístico para obtener otras medidas del rendimiento estimado, como la media y la varianza, y así poder entender cómo se espera que el rendimiento varíe a través de los distintos conjuntos de datos. Esta variación es fundamental para la evaluación de la confianza en la estimación del rendimiento. La validación cruzada también hace un mejor uso de un conjunto de datos limitado; ya que a diferencia de la simple división de los datos en uno el entrenamiento y otro de evaluación; la validación cruzada calcula sus estimaciones sobre todo el conjunto de datos mediante la realización de múltiples divisiones e intercambios sistemáticos entre datos de entrenamiento y datos de evaluación.

La validación cruzada o cross-validation es una técnica utilizada para evaluar los resultados de un análisis estadístico
y garantizar que son independientes de la partición entre datos de entrenamiento y prueba. Consiste en repetir y calcular la media aritmética obtenida de las medidas de evaluación sobre diferentes particiones. Se utiliza en entornos donde el objetivo principal es la predicción y se quiere estimar cómo de preciso es un modelo que se llevará a cabo a la práctica.
Es una técnica muy utilizada en proyectos de inteligencia artificial para validar modelos generados. La siguiente figura muestra un esquema de esta técnica.

## [3.3](#Índice) Tipos de validaciones cruzadas

### [3.3.1](#Índice) Validación cruzada de K iteraciones

En la validación cruzada de K iteraciones o K-fold cross-validation los datos de muestra se dividen en K subconjuntos. Uno de los subconjuntos se utiliza como datos de prueba y el resto (K-1) como datos de entrenamiento. El proceso de validación cruzada es repetido durante k iteraciones, con cada uno de los posibles subconjuntos de datos de prueba. Finalmente se realiza la media aritmética de los resultados de cada iteración para obtener un único resultado. Este método es muy preciso puesto que evaluamos a partir de K combinaciones de datos de entrenamiento y de prueba, pero aun así tiene una desventaja, y es que, a diferencia del método de retención, es lento desde el punto de vista computacional. En la práctica, la elección del número de iteraciones depende de la medida del conjunto de datos. Lo más común es utilizar la validación cruzada de 10 iteraciones (10-fold cross-validation).

### [3.3.2](#Índice) Validación cruzada aleatoria
Este método consiste en dividir aleatoriamente el conjunto de datos de entrenamiento y el conjunto de datos de
prueba. Para cada división la función de aproximación se ajusta a partir de los datos de entrenamiento y calcula los valores de salida para el conjunto de datos de prueba. El resultado final se corresponde a la media aritmética de los valores obtenidos para las diferentes divisiones. La ventaja de este método es que la división de datos entrenamiento-prueba no depende del número de iteraciones. Pero, en cambio, con este método hay algunas muestras que quedan sin evaluar y otras que se evalúan más de una vez, es decir, los subconjuntos de prueba y entrenamiento se pueden solapar.

### [3.3.3](#Índice) Validación cruzada dejando uno fuera
La validación cruzada dejando uno fuera o Leave-one-out cross-validation (LOOCV) implica separar los datos de forma que para cada iteración tengamos una sola muestra para los datos de prueba y todo el resto conformando los datos de entrenamiento. La evaluación viene dada por el error, y en este tipo de validación cruzada el error es muy bajo, pero en cambio, a nivel computacional es muy costoso, puesto que se tienen que realizar un elevado número de iteraciones, tantas como N muestras tengamos y para cada una analizar los datos tanto de entrenamiento como de prueba.

## [3.4](#Índice) Cálculo del error
La evaluación de las diferentes validaciones cruzadas normalmente viene dada por el error obtenido en cada iteración, ahora bien, por cada uno de los métodos puede variar el número de iteraciones, según la elección del diseñador en función del número de datos total.

### [3.4.1](#Índice) Error de la validación cruzada de K iteraciones
En cada una de las k iteraciones de este tipo de validación se realiza un cálculo de error. El resultado final lo obtenemos a partir de realizar la media aritmética de los $K$ valores de errores obtenidos, según la fórmula:

\begin{equation}
   E = \frac{1}{K}\sum_{i=1}^{K}E_i
\end{equation}

Es decir, se realiza el sumatorio de los $K$ valores de error y se divide entre el valor de $K$.

### [3.4.2](#Índice) Error de la validación cruzada aleatoria

En la validación cruzada aleatoria, a diferencia del método anterior, cogemos muestras al azar durante k iteraciones, aunque de igual manera, se realiza un cálculo de error para cada iteración. El resultado final también lo obtenemos a partir de realizar la media aritmética de los $K$ valores de errores obtenidos, según la misma fórmula

### [3.4.3](#Índice) Error de la validación cruzada dejando uno fuera
En la validación cruzada dejando uno fuera se realizan tantas iteraciones como muestras $(N)$ tenga el conjunto de datos. De forma que para cada una de las N iteraciones se realiza un cálculo de error. El resultado final lo obtenemos realizando la media aritmética de los N valores de errores obtenidos, según la fórmula:

\begin{equation}
   E = \frac{1}{N}\sum_{i=1}^{N}E_i
\end{equation}

Donde se realiza el sumatorio de los $N$ valores de error y se divide entre el valor de $N$.

# [4.](#Índice) Pasos para construir un modelo de machine learning

Construir un modelo de Machine Learning, no se reduce solo a utilizar un algoritmo de aprendizaje o utilizar una librería de Machine Learning; sino que es todo un proceso que suele involucrar los siguientes pasos:

* **Recolectar los datos**. Podemos recolectar los datos desde muchas fuentes, podemos, por ejemplo, extraer los datos de un sitio web u obtener los datos utilizando una API o desde una base de datos. Podemos también utilizar otros dispositivos que recolectan los datos por nosotros; o utilizar datos que son de dominio público. El número de opciones que tenemos para recolectar datos ¡no tiene fin!. Este paso parece obvio, pero es uno de los que más complicaciones trae y más tiempo consume.

* **Preprocesar los datos**. Una vez que tenemos los datos, tenemos que asegurarnos que tiene el formato correcto para nutrir nuestro algoritmo de aprendizaje. Es prácticamente inevitable tener que realizar varias tareas de preprocesamiento antes de poder utilizar los datos. Igualmente este punto suele ser mucho más sencillo que el paso anterior.

* **Explorar los datos**. Una vez que ya tenemos los datos y están con el formato correcto, podemos realizar un pre análisis para corregir los casos de valores faltantes o intentar encontrar a simple vista algún patrón en los mismos que nos facilite la construcción del modelo. En esta etapa suelen ser de mucha utilidad las medidas estadísticas y los gráficos en 2 y 3 dimensiones para tener una idea visual de como se comportan nuestros datos. En este punto podemos detectar valores atípicos que debamos descartar; o encontrar las características que más influencia tienen para realizar una predicción.

* **Entrenar el algoritmo**. Aquí es donde comenzamos a utilizar las técnicas de Machine Learning realmente. En esta etapa nutrimos al o los algoritmos de aprendizaje con los datos que venimos procesando en las etapas anteriores. La idea es que los algoritmos puedan extraer información útil de los datos que le pasamos para luego poder hacer predicciones.

* **Evaluar el algoritmo**. En esta etapa ponemos a prueba la información o conocimiento que el algoritmo obtuvo del entrenamiento del paso anterior. Evaluamos que tan preciso es el algoritmo en sus predicciones y si no estamos muy conforme con su rendimiento, podemos volver a la etapa anterior y continuar entrenando el algoritmo cambiando algunos parámetros hasta lograr un rendimiento aceptable.

* **Utilizar el modelo**. En esta ultima etapa, ya ponemos a nuestro modelo a enfrentarse al problema real. Aquí también podemos medir su rendimiento, lo que tal vez nos obligue a revisar todos los pasos anteriores.

# [5.](#Índice) Machine learning en Python

Como siempre me gusta comentar, una de las grandes ventajas que ofrece Python sobre otros lenguajes de programación; es lo grande y prolifera que es la comunidad de desarrolladores que lo rodean; comunidad que ha contribuido con una gran variedad de librerías de primer nivel que extienden la funcionalidades del lenguaje. Para el caso de Machine Learning, las principales librerías que podemos utilizar son:

## [5.1](#Índice) Scikit-Learn

Scikit-learn es la principal librería que existe para trabajar con Machine Learning, incluye la implementación de un gran número de algoritmos de aprendizaje. La podemos utilizar para clasificaciones, extraccion de características, regresiones, agrupaciones, reducción de dimensiones, selección de modelos, o preprocesamiento. Posee una API que es consistente en todos los modelos y se integra muy bien con el resto de los paquetes científicos que ofrece Python. Esta librería también nos facilita las tareas de evaluación, diagnostico y validaciones cruzadas ya que nos proporciona varios métodos de fábrica para poder realizar estas tareas en forma muy simple.

## [5.2](#Índice) Statsmodels

Statsmodels es otra gran librería que hace foco en modelos estadísticos y se utiliza principalmente para análisis predictivos y exploratorios. Al igual que Scikit-learn, también se integra muy bien con el resto de los paquetes cientificos de Python. Si deseamos ajustar modelos lineales, hacer una análisis estadístico, o tal vez un poco de modelado predictivo, entonces Statsmodels es la librería ideal. Las pruebas estadísticas que ofrece son bastante amplias y abarcan tareas de validación para la mayoría de los casos.

## [5.3](#Índice) PyMC

pyMC es un módulo de Python que implementa modelos estadísticos bayesianos, incluyendo la cadena de Markov Monte Carlo(MCMC). pyMC ofrece funcionalidades para hacer el análisis bayesiano lo mas simple posible. Incluye los modelos bayesianos, distribuciones estadísticas y herramientas de diagnostico para la covarianza de los modelos. Si queremos realizar un análisis bayesiano esta es sin duda la librería a utilizar.

## [5.4](#Índice) NTLK

NLTK es la librería líder para el procesamiento del lenguaje natural o NLP por sus siglas en inglés. Proporciona interfaces fáciles de usar a más de 50 cuerpos y recursos léxicos, como WordNet, junto con un conjunto de bibliotecas de procesamiento de texto para la clasificación, tokenización, el etiquetado, el análisis y el razonamiento semántico.

Obviamente, aquí solo estoy listando unas pocas de las muchas librerías que existen en Python para trabajar con problemas de Machine Learning, los invito a realizar su propia investigación sobre el tema.

##  [5.5](#Índice) Algoritmos más utilizados

Los algoritmos que más se suelen utilizar en los problemas de Machine Learning son los siguientes:

 * **Regresión Lineal**
 * **Regresión Logística**
 * **Arboles de Decision**
 * **Random Forest**
 * **SVM o Máquinas de vectores de soporte.**
 * **KNN o K vecinos más cercanos.**
 * **K-means**

## [5.6](#Índice) Lectura recomedada

```python
import warnings

# Arboles de Decision

# importando pandas, numpy y matplotlib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

```

Cargamos los datos

```python
iris_df = pd.read_csv('../../data/04_01_iris.csv')
iris_df
```

## [7.1](#Índice) Labores de preprocesamiento

Sin embargo, scikit-learn no acepta cadenas como parámetros de las funciones, todo deben de ser números. Para ello, nos podemos valer del objeto sklearn.preprocessing.LabelEncoder, que nos transforma automáticamente las cadenas a números. La forma en que se utiliza es la siguiente:

```python
#? pd.tools.plotting.scatter_matrix()
#? preprocessing.LabelEncoder()

from sklearn import preprocessing
le = preprocessing.LabelEncoder()
le.fit(iris_df['species'])
iris_df['species_cod'] = le.transform(iris_df['species'])

iris_df
#le.inverse_transform(iris_df.species_cod)
```

Como podéis observar, primero se crea el LabelEncoder y luego se "entrena" mediante el método fit. Para un LabelEncoder, "entrenar" el modelo es decidir el mapeo que vimos anteriormente, en este caso:

   * Iris-setosa -> 0
   * Iris-versicolor -> 1
   * Iris-virginica -> 2

Una vez entrenado, utilizando el método transform del LabelEncoder, podremos transformar cualquier ndarray que queramos (hubiéramos tenido un error si alguna de las etiquetas de test no estuviera en train). Esta estructura (método fit más método transform o predict) se repite en muchos de los objetos de scikit-learn.

Hay muchas más tareas de preprocesamiento que se pueden hacer en scikit-learn. **Consulta el paquete sklearn.preprocessing**.

```python
import seaborn as sns;
sns.set(style="ticks", color_codes=True)
iris = sns.load_dataset("iris")
sns.pairplot(iris, hue="species")
plt.show()
# Nota--> ayuda sobre alguna función...
#? sns.pairplot
```

### Separamos los datos

Separamos los datos,

```python
iris_data = iris_df.drop(['species','species_cod'],axis=1)
iris_data.head()
```

```python
iris_target = iris_df.species_cod
iris_target.head()
```

### Separamos los Datos.... Entrenamiento y test

```python
# Separamos los Datos.... Entrenamiento y test
#?  train_test_split()

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(iris_data, iris_target,
                                                    test_size=0.33,
                                                    random_state=102,
                                                    shuffle =True)

print('Set de datos para Entrenamiento =',len(X_train))
print('Set de datos para Test',len(X_test))
print('Total',len(X_test)+len(X_train))
print('Data Shape=',X_test.shape)
print('Target Shape =',y_test.shape)

X_train.head()
```

# [6.](#Índice) K-Nearest Neighbors Classification
El método de los ** k vecinos más cercanos ** (en inglés, k-nearest neighbors, abreviado $k-nn$) es un método de clasificación supervisada (Aprendizaje, estimación basada en un conjunto de entrenamiento y prototipos) que sirve para estimar la función de densidad $F(x|Cj)$ de las predictoras $x$ por cada clase $C_{j}$.

Este es un método de clasificación no paramétrico, que estima el valor de la función de densidad de probabilidad o directamente la probabilidad a posteriori de que un elemento $x$ pertenezca a la clase $C_{j}$ a partir de la información proporcionada por el conjunto de prototipos.

En el proceso de aprendizaje no se hace ninguna suposición acerca de la distribución de las variables predictoras.

En el reconocimiento de patrones, el algoritmo $k-nn$ es usado como método de clasificación de objetos (elementos) basado en un entrenamiento mediante ejemplos cercanos en el espacio de los elementos.

## [6.1](#Índice) Algoritmo

Los ejemplos de entrenamiento son vectores en un espacio característico multidimensional, cada ejemplo está descrito en términos de $p$ atributos considerando $q$ clases para la clasificación. Los valores de los atributos del $i-ésimo$ ejemplo (donde $1\leq i\leq n$) se representan por el vector $\vec p$;  p-dimensional

entonces:

\begin{equation}
x_{i}=(x_{1i},x_{2i},...,x_{pi})\in X
\end{equation}

El espacio es particionado en regiones, por localizaciones y etiquetas de los ejemplos de entrenamiento. Un punto en el espacio es asignado a la clase  $C$ si esta es la clase más frecuente entre los k ejemplos de entrenamiento más cercano.

Generalmente se usa la distancia euclidiana.

\begin{equation}
d(x_{i},x_{j})={\sqrt {\sum _{r=1}^{p}(x_{ri}-x_{rj})^{2}}}
\end{equation}

=====================================================================================================================

**Ejemplo del algoritmo $k-nn$**. El ejemplo que se desea clasificar es el círculo verde. Para k = 3 este es clasificado con la clase triángulo, ya que hay solo un cuadrado y 2 triángulos, dentro del círculo que los contiene. Si k = 5 este es clasificado con la clase cuadrado, ya que hay 2 triángulos y 3 cuadrados, dentro del círculo externo.

=====================================================================================================================

La fase de entrenamiento del algoritmo consiste en almacenar los vectores característicos y las etiquetas de las clases de los ejemplos de entrenamiento. En la fase de clasificación, la evaluación del ejemplo (del que no se conoce su clase) es representada por un vector en el espacio característico. Se calcula la distancia entre los vectores almacenados y el nuevo vector, y se seleccionan los $k$ ejemplos más cercanos. El nuevo ejemplo es clasificado con la clase que más se repite en los vectores seleccionados.

Este método supone que los vecinos más cercanos nos dan la mejor clasificación y esto se hace utilizando todos los atributos; el problema de dicha suposición es que es posible que se tengan muchos atributos irrelevantes que dominen sobre la clasificación: dos atributos relevantes perderían peso entre otros veinte irrelevantes.

Para corregir el posible sesgo se puede asignar un peso a las distancias de cada atributo, dándole así mayor importancia a los atributos más relevantes.

### Vecinos más cercanos con distancia ponderada
Se puede ponderar la contribución de cada vecino de acuerdo a la distancia entre él y el ejemplar a ser clasificado $x_{q}$, dando mayor peso a los vecinos más cercanos. Por ejemplo podemos ponderar el voto de cada vecino de acuerdo al cuadrado inverso de sus distancias

\begin{equation}
{\hat {f}}(x_{q})\leftarrow argmax_{v\in V}\sum _{i=1}^{k}w_{i}\delta (v,f(x_{i}))
\end{equation}

donde

\begin{equation}
 w_{i}\equiv {\frac {1}{d(x_{q},x_{i})^{2}}}
\end{equation}

De esta manera se ve que no hay riesgo de permitir a todos los ejemplos entrenamiento contribuir a la clasificación de $ x_{q}$, ya que al ser muy distantes no tendrían peso asociado. La desventaja de considerar todos los ejemplos seria su lenta respuesta (método global). Se quiere siempre tener un método local en el que solo los vecinos más cercanos son considerados.

Esta mejora es muy efectiva en muchos problemas prácticos. Es robusto ante los ruidos de datos y suficientemente efectivo en conjuntos de datos grandes. Se puede ver que al tomar promedios ponderados de los $k$ vecinos más cercanos, el algoritmo puede evitar el impacto de ejemplos con ruido aislados.

Otra posibilidad consiste en tratar de determinar o ajustar los pesos con ejemplos conocidos de entrenamiento.

Finalmente, antes de asignar pesos es recomendable identificar y eliminar los atributos que se consideran irrelevantes.

### Resumen:

1. Calculate distance
2. Find closest neighbors
3. Vote for labels

## [6.1](#Índice) K-Means en Sklarn: KNeighborsClassifier

```python
from sklearn.neighbors import KNeighborsClassifier
# Create two lists for training and test accuracies
training_accuracy = []
test_accuracy = []

# Define a range of 1 to 10 (included) neighbors to be tested
neighbors_settings = range(1,10)

# Loop with the KNN through the different number of neighbors to determine the most appropriate (best)
for n_neighbors in neighbors_settings:
    clf = KNeighborsClassifier(n_neighbors=n_neighbors, algorithm='auto', weights='uniform')
    clf.fit(X_train, y_train)
    training_accuracy.append(clf.score(X_train, y_train))
    test_accuracy.append(clf.score(X_test, y_test))

# Visualize results - to help with deciding which n_neigbors yields the best results (n_neighbors=6, in this case)
plt.plot(neighbors_settings, training_accuracy, label='Accuracy of the training set', marker='o')
plt.plot(neighbors_settings, test_accuracy, label='Accuracy of the test set', marker='o')
plt.ylabel('Accuracy')
plt.xlabel('Number of Neighbors')
plt.legend()
```

```python
clf = KNeighborsClassifier(n_neighbors=9,weights='uniform', algorithm='auto')
clf.fit(X_train, y_train)
print(clf.score(X_train, y_train))
print(clf.score(X_test, y_test))
#? KNeighborsClassifier
```

###  [6.1.1](#Índice) Haciendo predicciones

```python
warnings.filterwarnings('ignore')

ind = 78
print(iris_data.iloc[ind])
print('specie',iris_target.iloc[ind], le.inverse_transform(iris_target.iloc[[ind]]))
x_new = iris_data.iloc[ind]

print('\n======== PREDICTION ========')
prediction = clf.predict([x_new])
prediction
prediction_pb = clf.predict_proba([x_new.values])
print('Specie prediction',prediction, le.inverse_transform(prediction))
print('Probability Specie prediction',prediction_pb)
```

###  [6.1.2](#Índice) Classification Report

```python
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix

p = clf.predict(X_test)

print ('Accuracy:', accuracy_score(y_test, p))
print ('\nConfusion Matrix:\n', confusion_matrix(y_test, p))
print ('\nClassification Report:', classification_report(y_test, p))
```

# Ejercicios:

**Replicar estas técnicas de Clasificación con los siguientes DataSet:**

## Breast Cancer Wisconsin (Diagnostic) Data Set

File: ./data/06_breast-cancer-wisconsin-data.csv

Source: http://archive.ics.uci.edu/ml/datasets/breast%2Bcancer%2Bwisconsin%2B%28diagnostic%29

Abstract: Diagnostic Wisconsin Breast Cancer Database Source:

Creators:

    Dr. William H. Wolberg, General Surgery Dept. University of Wisconsin, Clinical Sciences Center Madison, WI 53792 wolberg '@' eagle.surgery.wisc.edu

    W. Nick Street, Computer Sciences Dept. University of Wisconsin, 1210 West Dayton St., Madison, WI 53706 street '@' cs.wisc.edu 608-262-6619

    Olvi L. Mangasarian, Computer Sciences Dept. University of Wisconsin, 1210 West Dayton St., Madison, WI 53706 olvi '@' cs.wisc.edu

Información del conjunto de datos:

Las características se calculan a partir de una imagen digitalizada de aspiración con aguja fina (PAAF) de una masas mamarias. Describen las características de los núcleos celulares presentes en la imagen (más detalles http://pages.cs.wisc.edu/~street/images/)

Attribute Information:

    ID number
    Diagnosis (M = malignant, B = benign)

**Ten real-valued features are computed for each cell nucleus:**

a) radius (mean of distances from center to points on the perimeter)

b) texture (standard deviation of gray-scale values)

c) perimeter

d) area

e) smoothness (local variation in radius lengths)

f) compactness (perimeter^2 / area - 1.0)

g) concavity (severity of concave portions of the contour)

h) concave points (number of concave portions of the contour)

i) symmetry

j) fractal dimension ("coastline approximation" - 1)

## Diabetes Data Set

File: ./data/06_diabetes.csv

Source : https://archive.ics.uci.edu/ml/datasets/diabetes

Michael Kahn, MD, PhD, Universidad de Washington, St. Louis, MO Información del conjunto de datos:

Los registros de pacientes con diabetes se obtuvieron de dos fuentes: a) un dispositivo de registro electrónico automático y b) registros en papel.

El dispositivo automático tenía un reloj interno para los eventos de marca de tiempo, mientras que los registros en papel solo proporcionaban espacios de "tiempo lógico" (desayuno, almuerzo, cena, hora de acostarse).

Para los registros en papel, se asignaron horarios fijos para el desayuno (08:00), el almuerzo (12:00), la cena (18:00) y la hora de acostarse (22:00). Por lo tanto, los registros en papel tienen tiempos de grabación uniformes ficticios, mientras que los registros electrónicos tienen marcas de tiempo más realistas.

Los archivos de diabetes constan de cuatro campos por registro. Cada campo está separado por una pestaña y cada registro está separado por una nueva línea.

Nombres de archivos y formato

(1) Fecha en formato MM-DD-YYYY (2) Tiempo en formato XX: YY (3) Código (4) Valor

El campo Código se descifra de la siguiente manera:

33 = Dosis de insulina regular

34 = dosis de insulina NPH

35 = dosis de insulina UltraLente

48 = medición de glucosa en sangre no especificada

57 = medición de glucosa en sangre no especificada

58 = medición de glucosa en sangre antes del desayuno

59 = medición de glucosa en sangre después del desayuno

60 = medición de glucosa en sangre antes del almuerzo

61 = medición de glucosa en sangre después del almuerzo

62 = medición de glucosa en sangre antes de la cena

63 = medición de glucosa en sangre después de la cena

64 = Medición de glucosa en sangre antes del aperitivo

65 = Síntomas de hipoglucemia

66 = Ingestión típica de comida

67 = Ingestión de comida más de lo habitual

68 = ingesta de comida menos de lo usual

69 = actividad típica de ejercicio

70 = Actividad de ejercicio más de lo habitual

71 = actividad de ejercicio menos de lo habitual

72 = evento especial no especificado