2.完整版的SMO算法

在几百个点组成的小规模数据集上,简化版SMO算法的运行是没有什么问题的,但是在更大的数据集上的运行速度就会变慢。

刚才已经讨论了简化版SMO算法,下面我们就讨论完整版的Platt SMO算法。

在这两个版本中,实现alpha的更改和代数运算的优化环节一模一样。在优化过程中,唯一的不同就是选择alpha的方式。

完整版的Platt SMO算法应用了一些能够提速的启发方法。上一节的例子在执行时存在一定的时间提升空间。

Platt SMO算法是通过一个外循环来选择第一个alpha值的,并且其选择过程会在两种方式之间进行交替:一种方式是在所有数据集上进行单遍扫描,另一种方式则是在非边界alpha中实现单遍扫描。

而所谓非边界alpha指的就是那些不等于边界0或C的alpha值。

对整个数据集的扫描相当容易,而实现非边界alpha值的扫描时,首先需要建立这些alpha值的列表,然后再对这个表进行遍历。同时,该步骤会跳过那些已知的不会改变的alpha值。

在选择第一个alpha值后,算法会通过一个内循环来选择第二个alpha值。在优化过程中,会通过最大化步长的方式来获得第二个alpha值。

在简化版SMO算法中,我们会在选择j之后计算错误率Ej。但在这里,我们会建立一个全局的缓存用于保存误差值,并从中选择使得步长或者说Ei-Ej最大的alpha值。

辅助函数和上面的步骤一样,即下面的三个函数:

"""
函数说明:读取数据
Parameters:file_name - 文件名
Returns:data_mat - 数据矩阵label_mat - 数据标签
"""
def load_dataset(file_name):# 数据矩阵data_mat = []# 标签向量label_mat = []# 打开文件fr = open(file_name)# 逐行读取for line in fr.readlines():# 去掉每一行首尾的空白符,例如'\n','\r','\t',' '# 将每一行内容根据'\t'符进行切片line_array = line.strip().split('\t')# 添加数据(100个元素排成一行)data_mat.append([float(line_array[0]), float(line_array[1])])# 添加标签(100个元素排成一行)label_mat.append(float(line_array[2]))return data_mat, label_mat"""
函数说明:随机选择alpha_j
Parameters:i - alpha_i的索引值m - alpha参数个数
Returns:j - alpha_j的索引值
"""
def select_j_random(i, m):j = iwhile(j == i):# uniform()方法将随机生成一个实数,它在[x, y)范围内j = int(random.uniform(0, m))return j"""
函数说明:修剪alpha_j
Parameters:aj - alpha_j值H - alpha上限L - alpha下限
Returns:aj - alpha_j值
"""
def clip_alpha(aj, H, L):if aj > H:aj = Hif L > aj:aj = Lreturn aj

我们首先定义一个数据结构:

"""
类说明:维护所有需要操作的值
Parameters:dataMatIn - 数据矩阵classLabels - 数据标签C - 松弛变量toler - 容错率
Returns:None
"""
"""
定义一个新的数据结构
"""
class opt_struct:def __init__(self, data_mat_in, class_labels, C, toler):# 数据矩阵self.X = data_mat_in #传进来的数据# 数据标签self.label_mat = class_labels# 松弛变量self.C = C# 容错率self.tol = toler# 矩阵的行数self.m = np.shape(data_mat_in)[0]# 根据矩阵行数初始化alphas矩阵,一个m行1列的全零列向量self.alphas = np.mat(np.zeros((self.m, 1)))# 初始化b参数为0self.b = 0# 根据矩阵行数初始化误差缓存矩阵,第一列为是否有效标志位,其中0无效,1有效;第二列为实际的误差Ei的值"""我们之前的定义为:Ei=gxi-yiyi是标签的实际值。gx=alpha_i*y_i*x_i.x,就相当于是w.x+b因为误差值经常用到,所以希望每次计算后放到一个缓存当中,将ecache一分为二,第一列是标志位,取值为0或者1,为1时表示已经算出来"""self.ecache = np.mat(np.zeros((self.m, 2)))

首要的事情就是建立一个数据结构来保存所有的重要值,而这个过程可以通过一个对象来完成。只是作为一个数据结构来使用对象。

在将值传给函数时,我们可以通过将所有数据移到一个结构中来实现,这样就可以省掉手工输入的麻烦了。而此时,数据就可以通过一个对象来进行传递。实际上,当完成其实现时,可以很容易通过Python的字典来完成。

该方法可以实现其成员变量的填充。除了增加了一个m×2的矩阵成
员变量ecache之外,这些做法和简化版SMO一模一样。ecache的第一列给出的是ecache是否有效的标志位,而第二列给出的是实际的E值。

对于给定的alpha值,第一个辅助函数cal-Ek()能够计算E值并返回。以前,该过程是采用内嵌的方式来完成的,但是由于该过程在这个版本的SMO算法中出现频繁,这里必须要将其单独拎出来。

"""
函数说明:计算误差
Parameters:os - 数据结构k - 标号为k的数据
Returns:Ek - 标号为k的数据误差
"""
def cal_Ek(os, k):# multiply(a,b)就是个乘法,如果a,b是两个数组,那么对应元素相乘# .T为转置fXk = float(np.multiply(os.alphas, os.label_mat).T * (os.X * os.X[k, :].T) + os.b)# 计算误差项Ek = fXk - float(os.label_mat[k])# 返回误差项return Ek

函数select-j()用于选择第二个alpha或者说内循环的alpha值。回想一下,这里的目标是选择合适的第二个alpha值以保证在每次优化中采用最大步长。该函数的误差值与第一个alpha值Ei和下标i有关。

首先将输入值Ei在缓存中设置成为有效的。这里的有效意味着它已经计算好了。在ecache中,代码nonzero(os.ecache[:,0].A)[0]构建出了一个非零表。

NumPy函数nonzero()返回了一个列表,而这个列表中包含以输入列表为目录的列表值,当然这里的值并非零。nonzero()语句返回的是非零E值所对应的alpha值,而不是E值本身。

程序会在所有的值上进行循环并选择其中使得改变最大的那个值。如果这是第一次循环的话,那么就随机选择一个alpha值。当然也存在有许多更复杂的方式来处理第一次循环的情况,而上述做法就能够满足我们的目的。

"""
函数说明:内循环启发方式2
选择第二个待优化的alpha_j,选择一个误差最大的alpha_j
即,我们在选择alpha_2的时候做了改进,选择误差最大的
Parameters:i - 标号为i的数据的索引值oS - 数据结构Ei - 标号为i的数据误差
Returns:j - 标号为j的数据的索引值maxK - 标号为maxK的数据的索引值Ej - 标号为j的数据误差
"""
def select_j(i, os, Ei):# 初始化max_K = -1 #下标的索引值max_delta_E = 0Ej = 0# 根据Ei更新误差缓存,即先计算alpha_1以及E1值os.ecache[i] = [1, Ei] #放入缓存当中,设为有效# 对一个矩阵.A转换为Array类型# 返回误差不为0的数据的索引值valid_ecache_list = np.nonzero(os.ecache[:, 0].A)[0] #找出缓存中不为0# 有不为0的误差if(len(valid_ecache_list) > 1):# 遍历,找到最大的Ekfor k in valid_ecache_list: #迭代所有有效的缓存,找到误差最大的E# 不计算k==i节省时间if k == i: #不选择和i相等的值continue# 计算EkEk = cal_Ek(os, k)# 计算|Ei - Ek|delta_E = abs(Ei - Ek)# 找到maxDeltaEif(delta_E > max_delta_E):max_K = kmax_delta_E = delta_EEj = Ek# 返回max_K,Ejreturn max_K, Ej #这样我们就得到误差最大的索引值和误差最大的值# 没有不为0的误差else: #第一次循环时是没有有效的缓存值的,所以随机选一个(仅会执行一次)# 随机选择alpha_j的索引值j = select_j_random(i, os.m)# 计算EjEj = cal_Ek(os, j)# 返回j,Ejreturn j, Ej

最后一个辅助函数是update-Ek(),它会计算误差值并存入缓存当中。在对alpha值进行优化之后会用到这个值。

"""
函数说明:计算Ek,并更新误差缓存
Parameters:os - 数据结构k - 标号为k的数据的索引值
Returns:None
"""
def update_Ek(os, k):# 计算EkEk = cal_Ek(os, k)# 更新误差缓存os.ecache[k] = [1, Ek]

接下来将简单介绍一下用于寻找决策边界的优化例程:

"""
函数说明:优化的SMO算法
Parameters:i - 标号为i的数据的索引值os - 数据结构
Returns:1 - 有任意一对alpha值发生变化0 - 没有任意一对alpha值发生变化或变化太小
"""
def innerL(i, os):# 步骤1:计算误差EiEi = cal_Ek(os, i)# 优化alpha,设定一定的容错率if((os.label_mat[i] * Ei < -os.tol) and (os.alphas[i] < os.C)) or ((os.label_mat[i] * Ei > os.tol) and (os.alphas[i] > 0)):# 使用内循环启发方式2选择alpha_j,并计算Ejj, Ej = select_j(i, os, Ei) #这里不再是随机选取了# 保存更新前的alpha值,使用深层拷贝alpha_i_old = os.alphas[i].copy()alpha_j_old = os.alphas[j].copy()# 步骤2:计算上界H和下界Lif(os.label_mat[i] != os.label_mat[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:计算etaeta = 2.0 * os.X[i, :] * os.X[j, :].T - os.X[i, :] * os.X[i, :].T - os.X[j, :] * os.X[j, :].Tif eta >= 0:print("eta >= 0")return 0# 步骤4:更新alpha_jos.alphas[j] -= os.label_mat[j] * (Ei - Ej) / eta# 步骤5:修剪alpha_jos.alphas[j] = clip_alpha(os.alphas[j], H, L)# 更新Ej至误差缓存update_Ek(os, j)if(abs(os.alphas[j] - alpha_j_old) < 0.00001):print("alpha_j变化太小")return 0# 步骤6:更新alpha_ios.alphas[i] += os.label_mat[i] * os.label_mat[j] * (alpha_j_old - os.alphas[j])# 更新Ei至误差缓存update_Ek(os, i)# 步骤7:更新b_1和b_2:b1 = os.b - Ei - os.label_mat[i] * (os.alphas[i] - alpha_i_old) * os.X[i, :] * os.X[i, :].T - os.label_mat[j] * (os.alphas[j] - alpha_j_old) * os.X[j, :] * os.X[i, :].Tb2 = os.b - Ej - os.label_mat[i] * (os.alphas[i] - alpha_i_old) * os.X[i, :] * os.X[j, :].T - os.label_mat[j] * (os.alphas[j] - alpha_j_old) * os.X[j, :] * os.X[j, :].T# 步骤8:根据b_1和b_2更新bif(0 < os.alphas[i] < os.C):os.b = b1elif(0 < os.alphas[j] < os.C):os.b = b2else:os.b = (b1 + b2) / 2.0return 1 #表示有更新else:return 0 #表示没有更新

代码几乎与上一节中给出的smo-simple函数一摸一样,但是这里的代码已经使用了自己的数据结构。该结构在参数os中传递。

完整版SMO算法的外循环代码:

"""
函数说明:完整的线性SMO算法
Parameters:dataMatIn - 数据矩阵classLabels - 数据标签C - 松弛变量toler - 容错率maxIter - 最大迭代次数
Returns:oS.b - SMO算法计算的boS.alphas - SMO算法计算的alphas
"""
def smo_p(data_mat_in, class_labels, C, toler, max_iter):# 初始化数据结构os = opt_struct(np.mat(data_mat_in), np.mat(class_labels).transpose(), C, toler)# 初始化当前迭代次数iter = 0entrie_set = True #是否在全部数据集上迭代alpha_pairs_changed = 0# 遍历整个数据集alpha都没有更新或者超过最大迭代次数,则退出循环while(iter < max_iter) and ((alpha_pairs_changed > 0) or (entrie_set)):alpha_pairs_changed = 0if entrie_set: # 遍历整个数据集for i in range(os.m):# 使用优化的SMO算法alpha_pairs_changed += innerL(i, os) #innerL返回的值是0或者1print("全样本遍历:第%d次迭代 样本:%d, alpha优化次数:%d" % (iter, i, alpha_pairs_changed))iter += 1# 遍历非边界值else:# 遍历不在边界0和C的alphanon_bound_i_s = np.nonzero((os.alphas.A > 0) * (os.alphas.A < C))[0]for i in non_bound_i_s:alpha_pairs_changed += innerL(i, os)print("非边界遍历:第%d次迭代 样本:%d, alpha优化次数:%d" % (iter, i, alpha_pairs_changed))iter += 1# 遍历一次后改为非边界遍历if entrie_set:entrie_set = False #进行切换,遍历非边界数据集# 如果alpha没有更新,计算全样本遍历elif(alpha_pairs_changed == 0):entrie_set = Trueprint("迭代次数:%d" % iter)# 返回SMO算法计算的b和alphasreturn os.b, os.alphas

其输入和函数smo-simple()完全一样。函数一开始构建一个数据结构来容纳所有的数据,然后需要对控制函数退出的一些变量进行初始化。整个代码的主体是while循环,这与smo-simple()有些类似,但是这里的循环退出条件更多一些。

当迭代次数超过指定的最大值,或者遍历整个集合都未对任意alpha对进行修改时,就退出循环。

这里的max-iter变量和函数smo-simple()中的作用有一点不同,后者当没有任何alpha发生改变时会将整个集合的一次遍历过程计成一次迭代,而这里的一次迭代定义为一次循环过程,而不管该循环具体做了什么事。此时,如果在优化过程中存在波动就会停止,因此这里的做法优于smo-simple()函数中的计数方法。

while循环的内部与smo-simple()中有所不同,一开始的for循环在数据集上遍历任意可能的alpha。

我们通过调用innerL()来选择第二个alpha,并在可能时对其进行优化处理。如果有任意一对alpha值发生改变,那么会返回
1。第二个for循环遍历所有的非边界alpha值,也就是不在边界0或C上的值。

接下来,我们对for循环在非边界循环和完整遍历之间进行切换,并打印出迭代次数。最后程序将会返回常数b和alpha值。

我们的部分结果如下:

全样本遍历:第0次迭代 样本:0, alpha优化次数:1
全样本遍历:第0次迭代 样本:1, alpha优化次数:1
全样本遍历:第0次迭代 样本:2, alpha优化次数:1
全样本遍历:第0次迭代 样本:3, alpha优化次数:1
全样本遍历:第0次迭代 样本:4, alpha优化次数:1
全样本遍历:第0次迭代 样本:5, alpha优化次数:1
全样本遍历:第0次迭代 样本:6, alpha优化次数:1
全样本遍历:第0次迭代 样本:7, alpha优化次数:1
全样本遍历:第0次迭代 样本:8, alpha优化次数:2
全样本遍历:第0次迭代 样本:9, alpha优化次数:2
......
非边界遍历:第1次迭代 样本:26, alpha优化次数:3
alpha_j变化太小
非边界遍历:第1次迭代 样本:29, alpha优化次数:3
alpha_j变化太小
非边界遍历:第1次迭代 样本:46, alpha优化次数:3
alpha_j变化太小
非边界遍历:第1次迭代 样本:54, alpha优化次数:3
非边界遍历:第1次迭代 样本:55, alpha优化次数:3
迭代次数:2
alpha_j变化太小
非边界遍历:第2次迭代 样本:8, alpha优化次数:0
alpha_j变化太小
非边界遍历:第2次迭代 样本:10, alpha优化次数:0
非边界遍历:第2次迭代 样本:23, alpha优化次数:0
alpha_j变化太小
非边界遍历:第2次迭代 样本:29, alpha优化次数:0
alpha_j变化太小
非边界遍历:第2次迭代 样本:46, alpha优化次数:0
alpha_j变化太小
非边界遍历:第2次迭代 样本:54, alpha优化次数:0
非边界遍历:第2次迭代 样本:55, alpha优化次数:0
......
全样本遍历:第3次迭代 样本:97, alpha优化次数:0
全样本遍历:第3次迭代 样本:98, alpha优化次数:0
全样本遍历:第3次迭代 样本:99, alpha优化次数:0
迭代次数:4

我们最终的决策边界和支持向量可视化为:

b= [[-3.06127512]]
w= [[ 0.69141217][-0.3411598 ]]
alphas= [[0.06158536 0.08744873 0.09509032 0.07285032 0.01603246 0.15524162]]
[3.542485, 1.977398] -1.0
[3.125951, 0.293251] -1.0
[4.658191, 3.507396] -1.0
[8.197181, 1.545132] 1.0
[5.286862, -2.358286] 1.0
[6.080573, 0.418886] 1.0

完整版代码如下:

# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
import random
import matplotlib as mpl
"""
改进SMO算法以加快我们的SVM运行速度!
""""""
函数说明:读取数据
Parameters:file_name - 文件名
Returns:data_mat - 数据矩阵label_mat - 数据标签
"""
def load_dataset(file_name):# 数据矩阵data_mat = []# 标签向量label_mat = []# 打开文件fr = open(file_name)# 逐行读取for line in fr.readlines():# 去掉每一行首尾的空白符,例如'\n','\r','\t',' '# 将每一行内容根据'\t'符进行切片line_array = line.strip().split('\t')# 添加数据(100个元素排成一行)data_mat.append([float(line_array[0]), float(line_array[1])])# 添加标签(100个元素排成一行)label_mat.append(float(line_array[2]))return data_mat, label_mat"""
函数说明:随机选择alpha_j
Parameters:i - alpha_i的索引值m - alpha参数个数
Returns:j - alpha_j的索引值
"""
def select_j_random(i, m):j = iwhile(j == i):# uniform()方法将随机生成一个实数,它在[x, y)范围内j = int(random.uniform(0, m))return j"""
函数说明:修剪alpha_j
Parameters:aj - alpha_j值H - alpha上限L - alpha下限
Returns:aj - alpha_j值
"""
def clip_alpha(aj, H, L):if aj > H:aj = Hif L > aj:aj = Lreturn aj"""
类说明:维护所有需要操作的值
Parameters:dataMatIn - 数据矩阵classLabels - 数据标签C - 松弛变量toler - 容错率
Returns:None
"""
"""
定义一个新的数据结构
"""
class opt_struct:def __init__(self, data_mat_in, class_labels, C, toler):# 数据矩阵self.X = data_mat_in #传进来的数据# 数据标签self.label_mat = class_labels# 松弛变量self.C = C# 容错率self.tol = toler# 矩阵的行数self.m = np.shape(data_mat_in)[0]# 根据矩阵行数初始化alphas矩阵,一个m行1列的全零列向量self.alphas = np.mat(np.zeros((self.m, 1)))# 初始化b参数为0self.b = 0# 根据矩阵行数初始化误差缓存矩阵,第一列为是否有效标志位,其中0无效,1有效;第二列为实际的误差Ei的值"""我们之前的定义为:Ei=gxi-yiyi是标签的实际值。gx=alpha_i*y_i*x_i.x,就相当于是w.x+b因为误差值经常用到,所以希望每次计算后放到一个缓存当中,将ecache一分为二,第一列是标志位,取值为0或者1,为1时表示已经算出来"""self.ecache = np.mat(np.zeros((self.m, 2)))"""
函数说明:计算误差
Parameters:os - 数据结构k - 标号为k的数据
Returns:Ek - 标号为k的数据误差
"""
def cal_Ek(os, k):# multiply(a,b)就是个乘法,如果a,b是两个数组,那么对应元素相乘# .T为转置fXk = float(np.multiply(os.alphas, os.label_mat).T * (os.X * os.X[k, :].T) + os.b)# 计算误差项Ek = fXk - float(os.label_mat[k])# 返回误差项return Ek"""
函数说明:内循环启发方式2
选择第二个待优化的alpha_j,选择一个误差最大的alpha_j
即,我们在选择alpha_2的时候做了改进,选择误差最大的
Parameters:i - 标号为i的数据的索引值oS - 数据结构Ei - 标号为i的数据误差
Returns:j - 标号为j的数据的索引值maxK - 标号为maxK的数据的索引值Ej - 标号为j的数据误差
"""
def select_j(i, os, Ei):# 初始化max_K = -1 #下标的索引值max_delta_E = 0Ej = 0# 根据Ei更新误差缓存,即先计算alpha_1以及E1值os.ecache[i] = [1, Ei] #放入缓存当中,设为有效# 对一个矩阵.A转换为Array类型# 返回误差不为0的数据的索引值valid_ecache_list = np.nonzero(os.ecache[:, 0].A)[0] #找出缓存中不为0# 有不为0的误差if(len(valid_ecache_list) > 1):# 遍历,找到最大的Ekfor k in valid_ecache_list: #迭代所有有效的缓存,找到误差最大的E# 不计算k==i节省时间if k == i: #不选择和i相等的值continue# 计算EkEk = cal_Ek(os, k)# 计算|Ei - Ek|delta_E = abs(Ei - Ek)# 找到maxDeltaEif(delta_E > max_delta_E):max_K = kmax_delta_E = delta_EEj = Ek# 返回max_K,Ejreturn max_K, Ej #这样我们就得到误差最大的索引值和误差最大的值# 没有不为0的误差else: #第一次循环时是没有有效的缓存值的,所以随机选一个(仅会执行一次)# 随机选择alpha_j的索引值j = select_j_random(i, os.m)# 计算EjEj = cal_Ek(os, j)# 返回j,Ejreturn j, Ej"""
函数说明:计算Ek,并更新误差缓存
Parameters:os - 数据结构k - 标号为k的数据的索引值
Returns:None
"""
def update_Ek(os, k):# 计算EkEk = cal_Ek(os, k)# 更新误差缓存os.ecache[k] = [1, Ek]"""
函数说明:优化的SMO算法
Parameters:i - 标号为i的数据的索引值os - 数据结构
Returns:1 - 有任意一对alpha值发生变化0 - 没有任意一对alpha值发生变化或变化太小
"""
def innerL(i, os):# 步骤1:计算误差EiEi = cal_Ek(os, i)# 优化alpha,设定一定的容错率if((os.label_mat[i] * Ei < -os.tol) and (os.alphas[i] < os.C)) or ((os.label_mat[i] * Ei > os.tol) and (os.alphas[i] > 0)):# 使用内循环启发方式2选择alpha_j,并计算Ejj, Ej = select_j(i, os, Ei) #这里不再是随机选取了# 保存更新前的alpha值,使用深层拷贝alpha_i_old = os.alphas[i].copy()alpha_j_old = os.alphas[j].copy()# 步骤2:计算上界H和下界Lif(os.label_mat[i] != os.label_mat[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:计算etaeta = 2.0 * os.X[i, :] * os.X[j, :].T - os.X[i, :] * os.X[i, :].T - os.X[j, :] * os.X[j, :].Tif eta >= 0:print("eta >= 0")return 0# 步骤4:更新alpha_jos.alphas[j] -= os.label_mat[j] * (Ei - Ej) / eta# 步骤5:修剪alpha_jos.alphas[j] = clip_alpha(os.alphas[j], H, L)# 更新Ej至误差缓存update_Ek(os, j)if(abs(os.alphas[j] - alpha_j_old) < 0.00001):print("alpha_j变化太小")return 0# 步骤6:更新alpha_ios.alphas[i] += os.label_mat[i] * os.label_mat[j] * (alpha_j_old - os.alphas[j])# 更新Ei至误差缓存update_Ek(os, i)# 步骤7:更新b_1和b_2:b1 = os.b - Ei - os.label_mat[i] * (os.alphas[i] - alpha_i_old) * os.X[i, :] * os.X[i, :].T - os.label_mat[j] * (os.alphas[j] - alpha_j_old) * os.X[j, :] * os.X[i, :].Tb2 = os.b - Ej - os.label_mat[i] * (os.alphas[i] - alpha_i_old) * os.X[i, :] * os.X[j, :].T - os.label_mat[j] * (os.alphas[j] - alpha_j_old) * os.X[j, :] * os.X[j, :].T# 步骤8:根据b_1和b_2更新bif(0 < os.alphas[i] < os.C):os.b = b1elif(0 < os.alphas[j] < os.C):os.b = b2else:os.b = (b1 + b2) / 2.0return 1 #表示有更新else:return 0 #表示没有更新"""
函数说明:完整的线性SMO算法
Parameters:dataMatIn - 数据矩阵classLabels - 数据标签C - 松弛变量toler - 容错率maxIter - 最大迭代次数
Returns:oS.b - SMO算法计算的boS.alphas - SMO算法计算的alphas
"""
def smo_p(data_mat_in, class_labels, C, toler, max_iter):# 初始化数据结构os = opt_struct(np.mat(data_mat_in), np.mat(class_labels).transpose(), C, toler)# 初始化当前迭代次数iter = 0entrie_set = True #是否在全部数据集上迭代alpha_pairs_changed = 0# 遍历整个数据集alpha都没有更新或者超过最大迭代次数,则退出循环while(iter < max_iter) and ((alpha_pairs_changed > 0) or (entrie_set)):alpha_pairs_changed = 0if entrie_set: # 遍历整个数据集for i in range(os.m):# 使用优化的SMO算法alpha_pairs_changed += innerL(i, os) #innerL返回的值是0或者1print("全样本遍历:第%d次迭代 样本:%d, alpha优化次数:%d" % (iter, i, alpha_pairs_changed))iter += 1# 遍历非边界值else:# 遍历不在边界0和C的alphanon_bound_i_s = np.nonzero((os.alphas.A > 0) * (os.alphas.A < C))[0]for i in non_bound_i_s:alpha_pairs_changed += innerL(i, os)print("非边界遍历:第%d次迭代 样本:%d, alpha优化次数:%d" % (iter, i, alpha_pairs_changed))iter += 1# 遍历一次后改为非边界遍历if entrie_set:entrie_set = False #进行切换,遍历非边界数据集# 如果alpha没有更新,计算全样本遍历elif(alpha_pairs_changed == 0):entrie_set = Trueprint("迭代次数:%d" % iter)# 返回SMO算法计算的b和alphasreturn os.b, os.alphas"""
函数说明:分类结果可视化
Returns:dataMat - 数据矩阵classLabels - 数据标签w - 直线法向量b - 直线截距
Returns:None
"""
def show_classifer(data_mat, class_labels, w, b):data_mat, label_mat = load_dataset('testSet.txt')fig = plt.figure()ax = fig.add_subplot(111)cm = mpl.colors.ListedColormap(['g', 'b'])ax.scatter(np.array(data_mat)[:, 0], np.array(data_mat)[:, 1], c=np.array(label_mat), cmap=cm, s=20)# 绘制直线x1 = max(data_mat)[0]x2 = min(data_mat)[0]a1, a2 = wb = float(b)a1 = float(a1[0])a2 = float(a2[0])y1, y2 = (-b - a1 * x1) / a2, (-b - a1 * x2) / a2plt.plot([x1, x2], [y1, y2])# 找出支持向量点# enumerate在字典上是枚举、列举的意思for i, alpha in enumerate(alphas):# 支持向量机的点if (abs(alpha) > 0):x, y = data_mat[i]plt.scatter([x], [y], s=150, c='none', alpha=0.7, linewidth=1.5, edgecolors='red')plt.show()
"""
函数说明:计算w
Returns:dataArr - 数据矩阵classLabels - 数据标签alphas - alphas值
Returns:w - 直线法向量
"""
def cal_w_s(alphas, data_array, class_labels):X = np.mat(data_array)label_mat = np.mat(class_labels).transpose()m, n = np.shape(X)w = np.zeros((n, 1))for i in range(m):w += np.multiply(alphas[i] * label_mat[i], X[i, :].T)return wif __name__ == '__main__':data_array, class_labels = load_dataset('testSet.txt')b, alphas = smo_p(data_array, class_labels, 0.6, 0.001, 40)w = cal_w_s(alphas, data_array, class_labels)print('b=', b)print('w=', w)print('alphas=', alphas[alphas > 0])for i in range(100):if alphas[i] > 0.0:print(data_array[i], class_labels[i])show_classifer(data_array, class_labels, w, b)

机器学习算法实战项目—支持向量机(2)—完整版的SMO算法相关推荐

  1. 机器学习--支持向量机实战(三)完整版SMO算法实现

    完整版和简化版的smo不通之处在于alpha的选择方式上,alpha的更改和代数运算和简化版本,完整版的alpha的选择方式采用了启发式进行选择,前面文章已经详解了,这里再简单的叙述一下: Platt ...

  2. 【机器学习】KNN算法实战项目二:水果分类

    KNN算法实战项目二:水果分类 2 KNN实现水果分类 2.1 模块导入与数据加载 2.2 数据EDA 2.3 模型创建与应用 2.4 绘制决策边界 手动反爬虫: 原博地址 https://blog. ...

  3. 【机器学习】KNN算法实战项目三:金融贷款策略分类

    KNN算法实战项目三:金融贷款策略分类 3 金融贷款策略中的KNN分类 3.1 模块导入与数据加载 3.2 数据EDA 3.2.1 数据预处理 3.2.2 数据可视化 3.2.3 特征工程 3.3 模 ...

  4. 视频教程-Zabbix分布式企业级监控实战视频课程(完整版)-Linux

    Zabbix分布式企业级监控实战视频课程(完整版) 京东商城运维架构师,京峰Linux云计算教学总监,担任国内多家知名社区运维专家网站Linux版主,专注Linux自动化运维,熟练LVS.Nginx, ...

  5. java web开发实战经典 源码_李兴华 java_web开发实战经典 源码 完整版收集共享

    李兴华 java_web开发实战经典 源码 完整版收集共享 01f8a7  在  2018-11-07 20:41:33  上传  10.92 MB 第1章 JAVA WEB开发简介 1.1.WEB发 ...

  6. 【史上最强实战项目合集】java项目20套 +完整版java学习视频

    如果你是初学者,或者是自学者!你可以加小编微信(2782278837)!小编可以给你一些好的建议以及给你(免费)提供学习资料!你在学习上有什么问题都可以咨询小编!小编都会为你解答! 注:本公众号纯属个 ...

  7. 深度学习教程(6) | 神经网络优化算法(吴恩达·完整版)

    作者:韩信子@ShowMeAI 教程地址:https://www.showmeai.tech/tutorials/35 本文地址:https://www.showmeai.tech/article-d ...

  8. 40个web前端实战项目,练完即可就业,从入门到进阶,基础到框架,html_css【附视频+源码】

    当下前端开发可以说是一个比较火的职业,所以学习的人比较多,不管是培训还是自学都是希望通过前端可以找到一份好的工作,但是很多自学的朋友在自学过程中有些盲目,不仅大大降低了学习的效率,而且也会打击自己的学 ...

  9. 【2023最新】32个web前端实战项目,练完即可就业,从入门到进阶,基础到框架,你想要的全都有,建议码住!

    1.[网易云音乐首页制作] 2.[实战项目之今日头条] 3.[实战项目之拉勾网] 4.[ReactNative项目之美食APP] 5.[uni-APP项目实战教程] 6.[React项目管理后台系统] ...

最新文章

  1. PMP知识点(六、质量管理)
  2. 借钱,一定要有还钱的素质
  3. OpenAI发布CLIP模型快一年了,盘点那些CLIP相关让人印象深刻的工作
  4. Datawhale 一周年,生日快乐!
  5. Python学习【第二篇】 : Python入门
  6. CPU为什么不做成圆的而是方的?
  7. [个人备忘]SpringMVC+Mybatis+freemarker后台代码生成器自动生成新建表格数据的增删改查处理
  8. 甘肃计算机报名准考证打印,2019年9月甘肃计算机等考准考证打印入口已开通
  9. 用Rust保存Windows聚焦图片
  10. luogu p1330封锁阳光大学
  11. 计算机保研厦大面试,保研其实不难:他们保研人大、厦大、山大,有这些经验,值得收藏!...
  12. 百度站点属性怎么设置?PC移动站/独立移动站/自适应/代码适配有什么区别? 404状态码和404页面有什么区别?对SEO有什么影响百度快照投诉不了怎么办(已经解决)提交反馈的按钮变成了灰色
  13. CSS制作移动动画效果
  14. 死锁的成因和解决方案
  15. Docker 下 jitsi-meet 视频服务器 安装部署
  16. 前端大屏页面布局经验
  17. AI数学基础(2)--- 霍夫丁不等式
  18. 卡巴斯基KAV/KIS 6.0.1.411正式版下载 附MP1版中文汉化+注册码
  19. 聚类分析详细解读python
  20. FXLMS主动降噪的simulink建模与仿真

热门文章

  1. ArcGIS绘制北半球俯视投影地图
  2. 6-2 简单实现x的n次方 (10 分)
  3. 学习傅里叶变换的心得
  4. 第三讲 i.MX系列芯片简介
  5. 什么是 Keepalived ?
  6. css 浮动在最上层_css样式如何控制div到最顶层
  7. 企业网站建设该选择什么样的建站系统
  8. 自我练习,梁的受力分析与约束的设置、简支、自由度约束对结果的影响。
  9. 超级大脑计算机,潮湿计算机:拥有人类智慧的超级大脑
  10. cad线段总和lisp_CAD中数字求和