机器学习算法:SVM(支持向量机)

简介:

SVM算法(Support Vector Machine,支持向量机)的核心思想有2点:1、如果数据线性可分,那么基于最大间隔的方式来确定超平面,以确保全局最优,使得分类器尽可能健壮;2、如果数据线性不可分,通过核函数将低维样本转化为高维样本使其线性可分。注意和AdaBoost类似,SVM只能解决二分类问题。


SVM的算法在数学上实在是太复杂了,没研究明白。建议还是直接使用现成的第三方组件吧,比如libsvm的C#版本,推荐这个:http://www.matthewajohnson.org/software/svm.html。


虽然没研究明白,不过这几天照着Python版本的代码试着用C#改写了一下,算是研究SVM过程中唯一的收获吧。此版本基于SMO(序列最小优化)算法求解,核函数使用的是比较常用的径向基函数(RBF)。别问我为什么没有注释,我只是从Python移植过来的,我也没看懂,等我看懂了再来补注释吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
using  System;
using  System.Collections.Generic;
using  System.Linq;
 
namespace  MachineLearning
{
     /// <summary>
     /// 支持向量机(SMO算法,RBF核)
     /// </summary>
     public  class  SVM
     {
         private  Random m_Rand;
         private  double [][] m_Kernel;
         private  double [] m_Alpha;
         private  double  m_C = 1.0;
         private  double  m_B = 0.0;
         private  double  m_Toler = 0.0;
         private  double [][] m_Cache;
         private  double [][] m_Data;
         private  double  m_Reach;
         private  int [] m_Label;
         private  int  m_Count;
         private  int  m_Dimension;
         
         public  SVM()
         {
             m_Rand =  new  Random();
         }
         
         /// <summary>
         /// 训练
         /// </summary>
         /// <param name="trainingSet"></param>
         /// <param name="C"></param>
         /// <param name="toler"></param>
         /// <param name="reach"></param>
         /// <param name="iterateCount"></param>
         public  void  Train(List<DataVector< double int >> trainingSet,  double  C,  double  toler,  double  reach,  int  iterateCount = 10)
         {
             //初始化
             m_Count = trainingSet.Count;
             m_Dimension = trainingSet[0].Dimension;
             m_Toler = toler;
             m_C = C;
             m_Reach = reach;
             this .Init(trainingSet);
             this .InitKernel();
             
             int  iter = 0;
             int  alphaChanged = 0;
             bool  entireSet =  true ;
             while (iter < iterateCount && (alphaChanged > 0 || entireSet))
             {
                 alphaChanged = 0;
                 if (entireSet)
                 {
                     for ( int  i = 0;i < m_Count;++i)
                         alphaChanged += InnerL(i);
                     iter++;
                 }
                 else
                 {
                     for ( int  i = 0;i < m_Count;++i)
                     {
                         if (m_Alpha[i] > 0 && m_Alpha[i] < m_C)
                             alphaChanged += InnerL(i);
                     }
                     iter += 1;
                 }
                 
                 if (entireSet)
                     entireSet =  false ;
                 else  if (alphaChanged == 0)
                     entireSet =  true ;
             }
         }
         
         /// <summary>
         /// 分类
         /// </summary>
         /// <param name="vector"></param>
         /// <returns></returns>
         public  int  Classify(DataVector< double int > vector)
         {
             double  predict = 0.0;
             
             int  svCnt = m_Alpha.Count(a => a > 0);
             var  supportVectors =  new  double [svCnt][];
             var  supportLabels =  new  int [svCnt];
             var  supportAlphas =  new  double [svCnt];
             int  index = 0;
             for ( int  i = 0;i < m_Count;++i)
             {
                 if (m_Alpha[i] > 0)
                 {
                     supportVectors[index] = m_Data[i];
                     supportLabels[index] = m_Label[i];
                     supportAlphas[index] = m_Alpha[i];
                     index++;
                 }
             }
             
             var  kernelEval = KernelTrans(supportVectors, vector.Data);
             for ( int  i = 0;i < svCnt;++i)
                 predict += kernelEval[i] * supportAlphas[i] * supportLabels[i];
             predict += m_B;
             
             return  Math.Sign(predict);
         }
         
         /// <summary>
         /// 将原始数据转化成方便使用的形式
         /// </summary>
         /// <param name="trainingSet"></param>
         private  void  Init(List<DataVector< double int >> trainingSet)
         {
             m_Data =  new  double [m_Count][];
             m_Label =  new  int [m_Count];
             m_Alpha =  new  double [m_Count];
             m_Cache =  new  double [m_Count][];
             
             for ( int  i = 0;i < m_Count;++i)
             {
                 m_Label[i] = trainingSet[i].Label;
                 m_Alpha[i] = 0.0;
                 m_Cache[i] =  new  double [2];
                 m_Cache[i][0] = 0.0;
                 m_Cache[i][1] = 0.0;
                 m_Data[i] =  new  double [m_Dimension];
                 for ( int  j = 0;j < m_Dimension;++j)
                     m_Data[i][j] = trainingSet[i].Data[j];
             }
         }
         
         /// <summary>
         /// 初始化RBF核
         /// </summary>
         private  void  InitKernel()
         {
             m_Kernel =  new  double [m_Count][];
             
             for ( int  i = 0;i < m_Count;++i)
             {
                 m_Kernel[i] =  new  double [m_Count];
                 var  kernels = KernelTrans(m_Data, m_Data[i]);
                 for ( int  k = 0;k < kernels.Length;++k)
                     m_Kernel[i][k] = kernels[k];
             }
         }
         
         private  double [] KernelTrans( double [][] X,  double [] A)
         {
             var  kernel =  new  double [X.Length];
             
             for ( int  i = 0;i < X.Length;++i)
             {
                 double  delta = 0.0;
                 for ( int  k = 0;k < X[0].Length;++k)
                     delta += Math.Pow(X[i][k] - A[k], 2);
                 kernel[i] = Math.Exp(delta * -1.0 / Math.Pow(m_Reach, 2));
             }
             
             return  kernel;
         }
         
         private  double  E( int  k)
         {
             double  x = 0.0;
             for ( int  i = 0;i < m_Count;++i)
                 x += m_Alpha[i] * m_Label[i] * m_Kernel[i][k];
             x += m_B;
             
             return  x - m_Label[k];
         }
         
         private  void  UpdateE( int  k)
         {
             double  Ek = E(k);
             m_Cache[k][0] = 1.0;
             m_Cache[k][1] = Ek;
         }
         
         private  int  InnerL( int  i)
         {
             double  Ei = E(i);
             
             if ((m_Label[i] * Ei < -m_Toler && m_Alpha[i] < m_C) || (m_Label[i] * Ei > m_Toler && m_Alpha[i] > 0))
             {
                 double  Ej = 0.0;
                 int  j = SelectJ(i, Ei,  out  Ej);
                 double  oldAi = m_Alpha[i];
                 double  oldAj = m_Alpha[j];
                 
                 double  H, L;
                 if (m_Label[i] != m_Label[j])
                 {
                     L = Math.Max(0, m_Alpha[j] - m_Alpha[i]);
                     H = Math.Min(m_C, m_C + m_Alpha[j] - m_Alpha[i]);
                 }
                 else
                 {
                     L = Math.Max(0, m_Alpha[j] + m_Alpha[i] - m_C);
                     H = Math.Min(m_C, m_Alpha[j] + m_Alpha[i]);
                 }
                 
                 if (L == H)
                     return  0;
                     
                 double  eta = 2.0 * m_Kernel[i][j] - m_Kernel[i][i] - m_Kernel[j][j];
                 if (eta >= 0)
                     return  0;
                     
                 m_Alpha[j] -= m_Label[j] * (Ei - Ej) / eta;
                 m_Alpha[j] = ClipAlpha(m_Alpha[j], H, L);
                 UpdateE(j);
                 
                 if (Math.Abs(m_Alpha[j] - oldAj) < 0.00001)
                     return  0;
                     
                 m_Alpha[i] += m_Label[j] * m_Label[i] * (oldAj - m_Alpha[j]);
                 UpdateE(i);
                 
                 double  b1 = m_B - Ei - m_Label[i] * (m_Alpha[i] - oldAi) * m_Kernel[i][i] - m_Label[j] * (m_Alpha[j] - oldAj) * m_Kernel[i][j];
                 double  b2 = m_B - Ej - m_Label[i] * (m_Alpha[i] - oldAi) * m_Kernel[i][j] - m_Label[j] * (m_Alpha[j] - oldAj) * m_Kernel[j][j];
                 
                 if (m_Alpha[i] > 0 && m_Alpha[i] < m_C)
                     m_B = b1;
                 else  if (m_Alpha[j] > 0 && m_Alpha[j] < m_C)
                     m_B = b2;
                 else
                     m_B = (b1 + b2) / 2.0;
                     
                 return  1;
             }
             
             return  0;
         }
         
         private  int  SelectJ( int  i,  double  Ei,  out  double  Ej)
         {
             Ej = 0.0;
             
             int  j = 0;
             int  maxK = -1;
             double  maxDeltaE = 0.0;
             
             m_Cache[i][0] = 1;
             m_Cache[i][1] = Ei;
             
             for ( int  k = 0;k < m_Count;++k)
             {
                 if (k == i || m_Cache[k][0] == 0)
                     continue ;
                     
                 double  Ek = E(k);
                 double  deltaE = Math.Abs(Ei - Ek);
                 if (deltaE > maxDeltaE)
                 {
                     maxK = k;
                     maxDeltaE = deltaE;
                     Ej = Ek;
                 }
             }
             
             if (maxK >= 0)
             {
                 j = maxK;
             }
             else
             {
                 j = RandomSelect(i);
             }
             
             return  j;
         }
         
         private  int  RandomSelect( int  i)
         {
             int  j = 0;
             do 
             {
                 j = m_Rand.Next(0, m_Count);
             }
             while (j == i);
             
             return  j;
         }
         
         private  double  ClipAlpha( double  alpha,  double  H,  double  L)
         {
             return  alpha > H ? H : (alpha < L ? L : alpha);
         }
     }
}


最后上测试,还是使用上次的breast-cancer-wisconsin.txt做测试,之前用kNN和AdaBoost测试的错误率分别是2.02%和1.01%,这回用SVM对比一下。上测试代码:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public  void  TestSvm()
{
     var  trainingSet =  new  List<DataVector< double int >>();
     var  testSet =  new  List<DataVector< double int >>();
     
     //读取数据
     var  file =  new  StreamReader( "breast-cancer-wisconsin.txt" , Encoding.Default);
     for ( int  i = 0;i < 699;++i)
     {
         string  line = file.ReadLine();
         var  parts = line.Split( ',' );
         var  p =  new  DataVector< double int >(9);
         for ( int  j = 0;j < p.Dimension;++j)
         {
             if (parts[j + 1] ==  "?" )
                 parts[j + 1] =  "0" ;
             p.Data[j] = Convert.ToDouble(parts[j + 1]);
         }
         p.Label = Convert.ToInt32(parts[10]) == 2 ? 1 : -1;
         
         //和上次一样,600个做训练,99个做测试
         if (i < 600)
             trainingSet.Add(p);
         else
             testSet.Add(p);
     }
     file.Close();
     
     //检验
     var  svm =  new  SVM();
     svm.Train(trainingSet, 1, 0.01, 3.0, 10);
     int  error = 0;
     foreach ( var  in  testSet)
     {
         var  label = boost.Classify(p);
         if (label != p.Label)
             error++;
     }
     
     Console.WriteLine( "Error = {0}/{1}, {2}%" , error, testSet.Count, (error * 100.0 / testSet.Count));
}


最终结果是99个测试样本猜错1个,错误率1.01%,和AdaBoost相当。


Train时使用不同的参数,错误率会变化,很可惜的是参数的选择往往没有固定的方法,需要在一定范围内尝试以得到最小错误率。



另外对于为什么核函数可以处理线性不可分数据,网上有2张图很能说明问题,转载一下:

以下数据明显是线性不可分的,在二维空间下找不到一条直线能将数据分开:

wKioL1RyzyDyBeOSAABy5ze1VuY862.jpg


但在在二维空间下的线性不可分,到了三维空间,是可以找到一个平面来分隔数据的:

wKiom1RyzqHwPC1JAAXaVFwBK3I418.gif







    本文转自 BoyTNT 51CTO博客,原文链接:http://blog.51cto.com/boytnt/1581925,如需转载请自行联系原作者

相关实践学习
DataV Board用户界面概览
本实验带领用户熟悉DataV Board这款可视化产品的用户界面
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
相关文章
|
4月前
|
机器学习/深度学习 算法 数据挖掘
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构。本文介绍了K-means算法的基本原理,包括初始化、数据点分配与簇中心更新等步骤,以及如何在Python中实现该算法,最后讨论了其优缺点及应用场景。
248 6
|
2月前
|
机器学习/深度学习 算法 数据可视化
利用SVM(支持向量机)分类算法对鸢尾花数据集进行分类
本文介绍了如何使用支持向量机(SVM)算法对鸢尾花数据集进行分类。作者通过Python的sklearn库加载数据,并利用pandas、matplotlib等工具进行数据分析和可视化。
195 70
|
27天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于机器学习的人脸识别算法matlab仿真,对比GRNN,PNN,DNN以及BP四种网络
本项目展示了人脸识别算法的运行效果(无水印),基于MATLAB2022A开发。核心程序包含详细中文注释及操作视频。理论部分介绍了广义回归神经网络(GRNN)、概率神经网络(PNN)、深度神经网络(DNN)和反向传播(BP)神经网络在人脸识别中的应用,涵盖各算法的结构特点与性能比较。
|
2月前
|
机器学习/深度学习 人工智能 算法
机器学习算法的优化与改进:提升模型性能的策略与方法
机器学习算法的优化与改进:提升模型性能的策略与方法
496 13
机器学习算法的优化与改进:提升模型性能的策略与方法
|
1月前
|
机器学习/深度学习 算法 数据可视化
基于线性核函数的SVM数据分类算法matlab仿真
本程序基于线性核函数的SVM算法实现数据分类,使用MATLAB2022A版本运行。程序生成随机二维数据并分为两组,通过自定义SVM模型(不依赖MATLAB工具箱)进行训练,展示不同惩罚参数C下的分类结果及决策边界。SVM通过寻找最优超平面最大化类别间隔,实现高效分类。 核心代码包括数据生成、模型训练和结果可视化,最终绘制了两类数据点及对应的决策边界。此实现有助于理解SVM的工作原理及其在实际应用中的表现。
|
2月前
|
机器学习/深度学习 算法 网络安全
CCS 2024:如何严格衡量机器学习算法的隐私泄露? ETH有了新发现
在2024年CCS会议上,苏黎世联邦理工学院的研究人员提出,当前对机器学习隐私保护措施的评估可能存在严重误导。研究通过LiRA攻击评估了五种经验性隐私保护措施(HAMP、RelaxLoss、SELENA、DFKD和SSL),发现现有方法忽视最脆弱数据点、使用较弱攻击且未与实际差分隐私基线比较。结果表明这些措施在更强攻击下表现不佳,而强大的差分隐私基线则提供了更好的隐私-效用权衡。
71 14
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
解锁机器学习的新维度:元学习的算法与应用探秘
元学习作为一个重要的研究领域,正逐渐在多个应用领域展现其潜力。通过理解和应用元学习的基本算法,研究者可以更好地解决在样本不足或任务快速变化的情况下的学习问题。随着研究的深入,元学习有望在人工智能的未来发展中发挥更大的作用。
|
4月前
|
机器学习/深度学习 算法 Serverless
基于WOA-SVM的乳腺癌数据分类识别算法matlab仿真,对比BP神经网络和SVM
本项目利用鲸鱼优化算法(WOA)优化支持向量机(SVM)参数,针对乳腺癌早期诊断问题,通过MATLAB 2022a实现。核心代码包括参数初始化、目标函数计算、位置更新等步骤,并附有详细中文注释及操作视频。实验结果显示,WOA-SVM在提高分类精度和泛化能力方面表现出色,为乳腺癌的早期诊断提供了有效的技术支持。
|
4月前
|
机器学习/深度学习 算法 数据挖掘
C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出
本文探讨了C语言在机器学习中的应用及其重要性。C语言以其高效性、灵活性和可移植性,适合开发高性能的机器学习算法,尤其在底层算法实现、嵌入式系统和高性能计算中表现突出。文章还介绍了C语言在知名机器学习库中的作用,以及与Python等语言结合使用的案例,展望了其未来发展的挑战与机遇。
97 1
|
4月前
|
机器学习/深度学习 算法 关系型数据库
基于PSO-SVM的乳腺癌数据分类识别算法matlab仿真,对比BP神经网络和SVM
本项目展示了利用粒子群优化(PSO)算法优化支持向量机(SVM)参数的过程,提高了分类准确性和泛化能力。包括无水印的算法运行效果预览、Matlab2022a环境下的实现、核心代码及详细注释、操作视频,以及对PSO和SVM理论的概述。PSO-SVM结合了PSO的全局搜索能力和SVM的分类优势,特别适用于复杂数据集的分类任务,如乳腺癌诊断等。

热门文章

最新文章