Platt SMO 和遗传算法优化 SVM
机器学习算法实践:Platt SMO 和遗传算法优化 SVM
之前实现了简单的SMO算法来优化SVM的对偶问题,其中在选取α的时候使用的是两重循环通过完全随机的方式选取,具体的实现参考《机器学习算法实践-SVM中的SMO算法》。(http://pytlab.github.io/2017/09/01/机器学习算法实践-SVM中的SMO算法/)
本文在之前简化版SMO算法的基础上实现了使用启发式选取α对的方式的Platt SMO算法来优化SVM。另外由于最近自己也实现了一个遗传算法框架GAFT,便也尝试使用遗传算法对于SVM的原始形式进行了优化。
对于本文算法的相应实现,参考:https://github.com/PytLab/MLBox/tree/master/svm
遗传算法框架GAFT项目地址: https://github.com/PytLab/gaft
正文
SMO中启发式选择变量
在SMO算法中,我们每次需要选取一对α来进行优化,通过启发式的选取我们可以更高效的选取待优化的变量使得目标函数下降的最快。
针对第一个α1和第二个α2 Platt SMO采取不同的启发式手段。
第一个变量的选择
第一个变量的选择为外循环,与之前便利整个αα列表不同,在这里我们在整个样本集和非边界样本集间进行交替:
1)首先我们对整个训练集进行遍历, 检查是否违反KKT条件,如果改点的αi和xi,yi违反了KKT条件则说明改点需要进行优化。
Karush-Kuhn-Tucker(KKT)条件是正定二次规划问题最优点的充分必要条件。针对SVM对偶问题,KKT条件非常简单:
2)在遍历了整个训练集并优化了相应的α后第二轮迭代我们仅仅需要遍历其中的非边界α. 所谓的非边界α就是指那些不等于边界0或者C的α值。 同样这些点仍然需要检查是否违反KKT条件并进行优化.
之后就是不断地在两个数据集中来回交替,最终所有的α都满足KKT条件的时候,算法中止。
为了能够快速选取有最大步长的α,我们需要对所有数据对应的误差进行缓存,因此特地写了个SVMUtil类来保存svm中重要的变量以及一些辅助方法:
class SVMUtil(object):
'''
Struct to save all important values in SVM.
'''
def __init__(self, dataset, labels, C, tolerance=0.001):
self.dataset, self.labels, self.C = dataset, labels, C
self.m, self.n = np.array(dataset).shape
self.alphas = np.zeros(self.m)
self.b = 0
self.tolerance = tolerance
# Cached errors ,f(x_i) - y_i
self.errors = [self.get_error(i) for i in range(self.m)]
# 其他方法...
...
下面为第一个变量选择交替遍历的大致代码,相应完整的Python实现(完整实现见https://github.com/PytLab/MLBox/blob/master/svm/svm_platt_smo.py):
while (it < max_iter):
pair_changed = 0
if entire:
for i in range(svm_util.m):
pair_changed += examine_example(i, svm_util)
print('Full set - iter: {}, pair changed: {}'.format(i, pair_changed))
else:
alphas = svm_util.alphas
non_bound_indices = [i for i in range(svm_util.m)
if alphas[i] > 0 and alphas[i] < C]
for i in non_bound_indices:
pair_changed += examine_example(i, svm_util)
...
...
第二个变量的选择
SMO中的第二个变量的选择过程为内循环,当我们已经选取第一个α1之后,我们希望我们选取的第二个变量α2优化后能有较大的变化。根据我们之前推导的式子
可以知道,新的α2的变化依赖于|E1−E2|, 当E1为正时, 那么选择最小的Ei作为E2,通常将每个样本的Ei缓存到一个列表中,通过在列表中选择具有|E1−E2|的α2来近似最大化步长。
有时候按照上述的启发式方式仍不能够是的函数值有足够的下降,这是按下述步骤进行选择:
在非边界数据集上选择能够使函数值足够下降的样本作为第二个变量
如果非边界数据集上没有,则在整个数据仅上进行第二个变量的选择
如果仍然没有则重新选择第一个α1
第二个变量选取的Python实现:
def select_j(i, svm_util):
''' 通过最大化步长的方式来获取第二个alpha值的索引.
'''
errors = svm_util.errors
valid_indices = [i for i, a in enumerate(svm_util.alphas) if 0 < a < svm_util.C]
if len(valid_indices) > 1:
j = -1
max_delta = 0
for k in valid_indices:
if k == i:
continue
delta = abs(errors[i] - errors[j])
if delta > max_delta:
j = k
max_delta = delta
else:
j = select_j_rand(i, svm_util.m)
return j
KKT条件允许一定的误差
在Platt论文中的KKT条件的判断中有一个tolerance允许一定的误差,相应的Python实现:
r = E_i*y_i
# 是否违反KKT条件
if (r < -tolerance and alpha < C) or (r > tolerance and alpha > 0):
...
关于Platt SMO的完整实现详见:https://github.com/PytLab/MLBox/blob/master/svm/svm_platt_smo.py
针对之前的数据集我们使用Platt SMO进行优化可以得到:
w = [0.8289668843516077, -0.26578914269411114]
b = -3.9292583040559448
将分割线和支持向量可视化:
可见通过Platt SMO优化出来的支持向量与简化版的SMO算法有些许不同。
使用遗传算法优化SVM
由于最近自己写了个遗传算法框架,遗传算法作为一个启发式无导型的搜索算法非常易用,于是我就尝试使用遗传算法来优化SVM。
使用遗传算法优化,我们就可以直接优化SVM的最初形式了也就是最直观的形式:
顺便再安利下自己的遗传算法框架,在此框架的帮助下,优化SVM算法我们只需要写几十行的Python代码即可。其中最主要的就是编写适应度函数,根据上面的公式我们需要计算数据集中每个点到分割线的距离并返回最小的距离即可,然后放到遗传算法中进行进化迭代。
遗传算法框架GAFT项目地址: https://github.com/PytLab/gaft , 使用方法详见README。
Ok, 我们开始构建种群用于进化迭代。
创建个体与种群
对于二维数据点,我们需要优化的参数只有三个也就是[w1,w2]和b, 个体的定义如下:
indv_template = GAIndividual(ranges=[(-2, 2), (-2, 2), (-5, 5)],
encoding='binary',
eps=[0.001, 0.001, 0.005])
种群大小这里取600,创建种群
population = GAPopulation(indv_template=indv_template, size=600).init()
创建遗传算子和GA引擎
这里没有什么特别的,直接使用框架中内置的算子就好了。
selection = RouletteWheelSelection()
crossover = UniformCrossover(pc=0.8, pe=0.5)
mutation = FlipBitBigMutation(pm=0.1, pbm=0.55, alpha=0.6)
engine = GAEngine(population=population, selection=selection,
crossover=crossover, mutation=mutation,
analysis=[ConsoleOutput, FitnessStore])
适应度函数
这一部分只要把上面svm初始形式描述出来就好了,只需要三行代码:
@engine.fitness_register
def fitness(indv):
w, b = indv.variants[: -1], indv.variants[-1]
min_dis = min([y*(np.dot(w, x) + b) for x, y in zip(dataset, labels)])
return float(min_dis)
开始迭代
这里迭代300代种群
if '__main__' == __name__:
engine.run(300)
绘制遗传算法优化的分割线
variants = engine.population.best_indv(engine.fitness).variants
w = variants[: -1]
b = variants[-1]
# 分类数据点
classified_pts = {'+1': [], '-1': []}
for point, label in zip(dataset, labels):
if label == 1.0:
classified_pts['+1'].append(point)
else:
classified_pts['-1'].append(point)
fig = plt.figure()
ax = fig.add_subplot(111)
# 绘制数据点
for label, pts in classified_pts.items():
pts = np.array(pts)
ax.scatter(pts[:, 0], pts[:, 1], label=label)
# 绘制分割线
x1, _ = max(dataset, key=lambda x: x[0])
x2, _ = min(dataset, key=lambda x: x[0])
a1, a2 = w
y1, y2 = (-b - a1*x1)/a2, (-b - a1*x2)/a2
ax.plot([x1, x2], [y1, y2])
plt.show()
得到的分割曲线如下图:
完整的代码详见: https://github.com/PytLab/MLBox/blob/master/svm/svm_ga.py
总结
本文对SVM的优化进行了介绍,主要实现了Platt SMO算法优化SVM模型,并尝试使用遗传算法框架GAFT对初始SVM进行了优化。
参考
Sequential Minimal Optimization: A Fast Algorithm for Training Support Vector Machines
Platt SMO 和遗传算法优化 SVM的更多相关文章
- 机器学习算法实践:Platt SMO 和遗传算法优化 SVM
机器学习算法实践:Platt SMO 和遗传算法优化 SVM 之前实现了简单的SMO算法来优化SVM的对偶问题,其中在选取α的时候使用的是两重循环通过完全随机的方式选取,具体的实现参考<机器学习 ...
- 机器学习——支持向量机(SVM)之Platt SMO算法
Platt SMO算法是通过一个外循环来选择第一个alpha值的,并且其选择过程会在两种方式之间进行交替: 一种方式是在所有数据集上进行单遍扫描,另一种方式则是在非边界alpha中实现单遍扫描. 所谓 ...
- MATLAB神经网络(3) 遗传算法优化BP神经网络——非线性函数拟合
3.1 案例背景 遗传算法(Genetic Algorithms)是一种模拟自然界遗传机制和生物进化论而形成的一种并行随机搜索最优化方法. 其基本要素包括:染色体编码方法.适应度函数.遗传操作和运行参 ...
- Matlab遗传算法优化问题求解的演示样例代码
代码例如以下: function m_main() clear clc Max_gen = 100;% 执行代数 pop_size = 100;%种群大小 chromsome = 10;%染色体的长度 ...
- 机器学习实战笔记(Python实现)-05-支持向量机(SVM)
--------------------------------------------------------------------------------------- 本系列文章为<机器 ...
- 【机器学习实战】第6章 支持向量机(Support Vector Machine / SVM)
第6章 支持向量机 <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/lates ...
- 支持向量机-完整Platt-SMO算法加速优化
完整版SMO算法与简单的SMO算法: 实现alpha的更改和代数运算的优化环节一模一样,唯一的不同就是选择alpha的方式.完整版应用了一些能够提速的方法. 同样使用Jupyter实现,后面不在赘述 ...
- 一步步教你轻松学支持向量机SVM算法之案例篇2
一步步教你轻松学支持向量机SVM算法之案例篇2 (白宁超 2018年10月22日10:09:07) 摘要:支持向量机即SVM(Support Vector Machine) ,是一种监督学习算法,属于 ...
- 机器学习实战 [Machine learning in action]
内容简介 机器学习是人工智能研究领域中一个极其重要的研究方向,在现今的大数据时代背景下,捕获数据并从中萃取有价值的信息或模式,成为各行业求生存.谋发展的决定性手段,这使得这一过去为分析师和数学家所专属 ...
随机推荐
- Python执行show slave status输出的两个格式
1.元组的方式 输出格式如下: ('Waiting for master to send event', '10.75.19.79', 'mysqlsync', 5580L, 60L, 'mysql- ...
- phpcms v9 前台getshell脚本
phpcms v9 前台getshell脚本 用法:python phpcmsv9getshell.py http://baidu.com # -*- coding:utf-8 -*- ''' --- ...
- (一)windows7下solr7.1.0默认jetty服务器环境搭建
windows7下solr7.1.0默认jetty服务器环境搭建 1.下载solr solr7官网地址:http://lucene.apache.org/solr/ jdk8官网地址:http://w ...
- Java数据结构和算法(一)——简介
本系列博客我们将学习数据结构和算法,为什么要学习数据结构和算法,这里我举个简单的例子. 编程好比是一辆汽车,而数据结构和算法是汽车内部的变速箱.一个开车的人不懂变速箱的原理也是能开车的,同理一个不懂数 ...
- 十三、Hadoop学习笔记————Hive安装先决条件以及部署
内嵌模式,存储于本地的Derby数据库中,只支持单用户 本地模式,支持多用户多会话,例如存入mysql 下载解压hive后,进到conf路径,将模板拷贝 出现该错误表示权限不够 该目录未找到 新建一个 ...
- JAVA基础1——字节&位运算
占用字节数 & 取值范围 Java一共有8种基本数据类型(原始数据类型): 类型 存储要求 范围(包含) 默认值 包装类 int 4字节(32位) -2^31~ 2^31-1 0 Intege ...
- Centos 7 ip查看问题
centos7已经没有ifconfig功能,现在使用的是命令ip addr查看,如果还是习惯ifconfig使用"yum -y install net-tools"命令进行安装 安 ...
- uva 471 - Magic Numbers
题意:给出一个数n,问有多少组数满足是s1/ s2 =n,要求组成s1和s2的数字没有重复的. 分析:枚举,然后二进制判断各位数字是否相同. #include<iostream> #inc ...
- 翻译连载 | 附录 C:函数式编程函数库-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- JavaSE初步学习笔记
PS:个人用来随时记录学习的过程,格式比较混乱,仅供个人参考与复习知识点 Dos命令行,课程中常见的命令 Dir:列出当前目录下包含的文件 Md:在当前目录下创建文件 Rd:在当前目录下删除指定文件夹 ...