基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

简介:

支持向量机SVM(Support Vector Machine)是一种有监督的学习模型,它的核心有两个:一、核函数(kernel trick);二、序列最小优化算法SMO(Sequential minimal optimization)是John Platt在1996年发布的用于训练SVM的有效算法。本文不打算细化SVM支持向量机的详细推倒算法,只涉及以上两点的内容做一个说明,最后给出算法实现和一个实验对比图。

  核函数

核函数在处理复杂数据时效果显著,它的做法是将某一个维度的线性不可分数据采取核函数进行特征空间的隐式映射到高维空间,从而在高维空间将数据转化为线性可分,最后回归到原始维度空间实施分类的过程,常见的几个核函数如下:

多项式核:

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

高斯核(径向基函数):

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

线性核:

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

即是两个矩阵空间的内积。

  SMO算法流程

SMO的主要两个步骤就是:

1、选择需要更新的一对α,采取启发式的方式进行选择,以使目标函数最大程度的接近其全局最优值;

2、将目标函数对α进行优化,以保持其它所有α不变。

以上是两个基本步骤,实现具体推到公式如下:

所需要收到的约束条件为:

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

同时更新α,要求满足如下条件,就可以保证为0的约束

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

消去α可得

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

其中

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

u 的表达式为:

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

y为第i个特征因素的真实标签值

之后考虑约束条件 0<α<c 则

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

约束条件的线性表示

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

依据 y 同号或是异号,可得出上下两个边界为

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

对于α有

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

对于α首先可以通过E求得j,之后计算方式可为:

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

而b的更新为

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

其中

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

每次更新完和都需要重新计算b以及对应的和

有了以上的公式,代码实现就比较简单了。

  算法实现

完整的Platt-smo算法实现入口:


public SvmResult plattSmo(final SvmResult svmResult) {
double b = svmResult.getB();
double[] alphas = svmResult.getAlphas();

for(int i=0;i<featuresArray.length;i++){
double ei = this.calcEk(i, alphas, b);
if (((lablesArray[i] * ei < -tolerFactor)
&& (alphas[i] < penaltyFactor))
|| ((lablesArray[i] * ei > tolerFactor) && (alphas[i] > 0))) {
double[] jSelected = this.selectJ(i, ei, alphas, b); //启发式实现j的选择
int j = (int) jSelected[0]; 
double ej = jSelected[1];
double alphaIold = alphas[i];
double alphaJold = alphas[j];
double L = 0;
double H = 0;
//边界计算
if (lablesArray[i] != lablesArray[j]) {
L = Math.max(0, alphas[j] - alphas[i]);
H = Math.min(penaltyFactor, penaltyFactor + alphas[j]
- alphas[i]);
} else {
L = Math.max(0, alphas[j] + alphas[i] - penaltyFactor);
H = Math.min(penaltyFactor, alphas[j] + alphas[i]);
}
if (L == H) {
logger.info("L==H");
} else {
double eta = (2.0 * this.kernelArray[i][j] - this.kernelArray[i][i] - this.kernelArray[j][j]);
if (eta >= 0) {
logger.info("eta>=0");
} else {
//双向调整alphas[j]递减
alphas[j] -= lablesArray[j] * (ei - ej) / eta;
if (alphas[j] > H) {
alphas[j] = H;
}
if (L > alphas[j]) {
alphas[j] = L;
}
//更新ej
this.updateEk(j, alphas, b);
if (Math.abs(alphas[j] - alphaJold) < 0.00001) {
logger.info("j not moving enough");
} else {
//双向调整alphas[i]递减
alphas[i] += lablesArray[j] * lablesArray[i]
* (alphaJold - alphas[j]);
//更新ei
this.updateEk(i, alphas, b);
//计算b
double b1 = b - ei- lablesArray[i]*(alphas[i]-alphaIold)*this.kernelArray[i][i] - lablesArray[j]*(alphas[j]-alphaJold)*this.kernelArray[i][j];
double b2 = b - ej- lablesArray[i]*(alphas[i]-alphaIold)*this.kernelArray[i][j] - lablesArray[j]*(alphas[j]-alphaJold)*this.kernelArray[j][j];
if ((0 < alphas[i]) && (penaltyFactor > alphas[i])){
b = b1;
}else if ((0 < alphas[j]) && (penaltyFactor > alphas[j])){
b = b2;
}else{
b = (b1 + b2)/2.0;
}

}
}
}
}
}
return new SvmResult(b, alphas);
}


在以上算法里面重点关注是j的选择,

J的选择:


private double[] selectJ(int i,double ei,double[] alphas,double b){
int maxK = -1; 
double maxDeltaE = 0; 
double ej = 0;
int j = -1;
double[] eiArray= new double[2];
eiArray[0] = 1d;
eiArray[1] = ei;
this.eCache[i] = eiArray;
boolean hasValidEcacheList = false;
for(int k=0;k<this.eCache.length;k++){
if(this.eCache[k][0] > 0){
if(k == i){
continue;
}
hasValidEcacheList = true;
if(k == this.m){
k = m-1;
}
double ek = this.calcEk(k, alphas, b);
double deltaE = Math.abs(ei - ek);
if (deltaE > maxDeltaE){
               maxK = k; 
               maxDeltaE = deltaE; 
               ej = ek;
}
}
}
j = maxK;
if(!hasValidEcacheList || j == -1){
j = this.selectJRandom(i);
ej = this.calcEk(j, alphas, b); 
}
if(j == this.m){
j = m-1;
}
return new double[]{j,ej};
}


首选采取启发式选择j,通过计算deltaE的最大值来逼近j的选择,如果选择不到就随机选择一个j值,在j选择里面有一个Ek的计算方式


private double calcEk(int k,double[] alphas,double b){
Matrix alphasMatrix = new Matrix(alphas);
Matrix lablesMatrix = new Matrix(lablesArray);
Matrix kMatrix = new Matrix(this.kernelArray[k]);
double fXk = alphasMatrix.multiply(lablesMatrix).dotMultiply(kMatrix.transpose()).dotValue() + b;
double ek = fXk - (float)this.lablesArray[k];
return ek;
}


下面再介绍一下核函数计算方式,本文主要采取径向基函数(RBF)实现,如下:


public double[] kernelTrans(double[][] featuresArray,double[] featuresIArray){
int mCount = featuresArray.length;
double[] kernelTransI = new double[mCount];
Matrix featuresMatrix = new Matrix(featuresArray);
Matrix featuresIMatrix = new Matrix(featuresIArray);
if(trainFactorMap.get("KT").equals("lin")){
Matrix result = featuresMatrix.dotMultiply(featuresIMatrix.transpose());
kernelTransI = result.transpose().values()[0];
}else if(trainFactorMap.get("KT").equals("rbf")){
double rbfDelta = (double)trainFactorMap.get("rbfDelta");
for(int j=0;j<mCount;j++){
Matrix xj = new Matrix(featuresArray[j]);
Matrix delta = xj.reduce(featuresIMatrix);
double deltaValue = delta.dotMultiply(delta.transpose()).dotValue();
kernelTransI[j] = Math.exp((-1.0*deltaValue)/(2*Math.pow(rbfDelta, 2)));
}
}
return kernelTransI;
}


最后看下测试代码实现:


double[][] datasvs = new double[m][d[0].length];
double[] labelsvs = new double[m];
double[] alphassvs = new double[m];
int n = 0;
for(int i=0;i<alphas.length;i++){
if(alphas[i] != 0){
datasvs[n] = d[i];
labelsvs[n] = l[i];
alphassvs[n] = alphas[i];
n++;
}
}

//model test
int errorCount = 0;
for(int i=0;i<d.length;i++){
double[] kernelTransI = learner.kernelTrans(datasvs, d[i]);
Matrix kernelTransIM = new Matrix(kernelTransI);
Matrix labelsvsM = new Matrix(labelsvs);
Matrix alphassvsM = new Matrix(alphassvs);
double predict = kernelTransIM.dotMultiply(labelsvsM.multiply(alphassvsM).transpose()).dotValue() + b;
System.out.println(i+"\t"+predict+"\t"+l[i]);
if(AdaBoost.sigmoid(predict) != l[i]){
errorCount++;
}
}


测试代码是首先找出所有的支持向量,并提取支持向量下的特征向量和标签向量,采取核函数进行隐式映射,最后计算预测值。

  训练结果


本文采取100个二维平面无法线性可分的数据集合,如下:

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

通过径向基函数映射后采取支持向量预测计算得到的可分平面如下

基于Spark如何实现SVM算法?这里有一份详尽的开发教程(含代码)

本算法100个数据训练准确率可达98%。

注:本文算法均来自Peter Harrington的《Machine Learning in action》




====================================分割线================================

本文作者:AI研习社
本文转自雷锋网禁止二次转载, 原文链接
目录
相关文章
|
8天前
|
机器学习/深度学习 前端开发 算法
婚恋交友系统平台 相亲交友平台系统 婚恋交友系统APP 婚恋系统源码 婚恋交友平台开发流程 婚恋交友系统架构设计 婚恋交友系统前端/后端开发 婚恋交友系统匹配推荐算法优化
婚恋交友系统平台通过线上互动帮助单身男女找到合适伴侣,提供用户注册、个人资料填写、匹配推荐、实时聊天、社区互动等功能。开发流程包括需求分析、技术选型、系统架构设计、功能实现、测试优化和上线运维。匹配推荐算法优化是核心,通过用户行为数据分析和机器学习提高匹配准确性。
37 3
|
27天前
|
机器学习/深度学习 算法 Serverless
基于WOA-SVM的乳腺癌数据分类识别算法matlab仿真,对比BP神经网络和SVM
本项目利用鲸鱼优化算法(WOA)优化支持向量机(SVM)参数,针对乳腺癌早期诊断问题,通过MATLAB 2022a实现。核心代码包括参数初始化、目标函数计算、位置更新等步骤,并附有详细中文注释及操作视频。实验结果显示,WOA-SVM在提高分类精度和泛化能力方面表现出色,为乳腺癌的早期诊断提供了有效的技术支持。
|
25天前
|
存储 算法 程序员
C 语言递归算法:以简洁代码驾驭复杂逻辑
C语言递归算法简介:通过简洁的代码实现复杂的逻辑处理,递归函数自我调用解决分层问题,高效而优雅。适用于树形结构遍历、数学计算等领域。
|
26天前
|
机器学习/深度学习 算法 数据挖掘
C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出
本文探讨了C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出。文章还介绍了C语言在知名机器学习库中的作用,以及与Python等语言结合使用的案例,展望了其未来发展的挑战与机遇。
43 1
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
57 1
|
1月前
|
机器学习/深度学习 算法 关系型数据库
基于PSO-SVM的乳腺癌数据分类识别算法matlab仿真,对比BP神经网络和SVM
本项目展示了利用粒子群优化(PSO)算法优化支持向量机(SVM)参数的过程,提高了分类准确性和泛化能力。包括无水印的算法运行效果预览、Matlab2022a环境下的实现、核心代码及详细注释、操作视频,以及对PSO和SVM理论的概述。PSO-SVM结合了PSO的全局搜索能力和SVM的分类优势,特别适用于复杂数据集的分类任务,如乳腺癌诊断等。
|
1月前
|
存储 缓存 算法
通过优化算法和代码结构来提升易语言程序的执行效率
通过优化算法和代码结构来提升易语言程序的执行效率
|
2天前
|
机器学习/深度学习 算法
基于改进遗传优化的BP神经网络金融序列预测算法matlab仿真
本项目基于改进遗传优化的BP神经网络进行金融序列预测,使用MATLAB2022A实现。通过对比BP神经网络、遗传优化BP神经网络及改进遗传优化BP神经网络,展示了三者的误差和预测曲线差异。核心程序结合遗传算法(GA)与BP神经网络,利用GA优化BP网络的初始权重和阈值,提高预测精度。GA通过选择、交叉、变异操作迭代优化,防止局部收敛,增强模型对金融市场复杂性和不确定性的适应能力。
103 80
|
21天前
|
算法
基于WOA算法的SVDD参数寻优matlab仿真
该程序利用鲸鱼优化算法(WOA)对支持向量数据描述(SVDD)模型的参数进行优化,以提高数据分类的准确性。通过MATLAB2022A实现,展示了不同信噪比(SNR)下模型的分类误差。WOA通过模拟鲸鱼捕食行为,动态调整SVDD参数,如惩罚因子C和核函数参数γ,以寻找最优参数组合,增强模型的鲁棒性和泛化能力。
|
7天前
|
供应链 算法 调度
排队算法的matlab仿真,带GUI界面
该程序使用MATLAB 2022A版本实现排队算法的仿真,并带有GUI界面。程序支持单队列单服务台、单队列多服务台和多队列多服务台三种排队方式。核心函数`func_mms2`通过模拟到达时间和服务时间,计算阻塞率和利用率。排队论研究系统中顾客和服务台的交互行为,广泛应用于通信网络、生产调度和服务行业等领域,旨在优化系统性能,减少等待时间,提高资源利用率。