机器学习之支持向量机SVM及代码示例

本文深入探讨了支持向量机(SVM)的原理,从线性可分SVM、超平面与间隔的概念,到最大化间隔的目标,再到从硬间隔到软间隔的转变。此外,还讲解了从线性SVM到非线性SVM的核方法,以及SVM在多分类问题中的应用。文章最后对比了SVM与逻辑回归的区别,并提供了代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、线性可分SVM

SVM算法最初是用来处理二分类问题的,是一种有监督学习的分类算法。

对于线性可分的二分类问题,我们可以找到无穷多个超平面,将两类样本进行区分。(超平面:一维中是一个点;二维中是一条线;三维中是一个面……)

这里写图片描述

在上面的多个超平面中,它们都可以成功将样本集划分两边,但哪一个超平面更好?

一般来说,当样本点离超平面越近,样本的标签为某类型的概率应该为0.5左右,确信度比较低,而样本点离超平面越远,样本的标签为某类型的概率越大,确信度比较高。

而线性可分SVM所寻找的最优超平面就是要尽可能的远离所有类别的数据点,使得间隔(margin)最大,利用间隔最大化来求得最优超平面。间隔的定义如下图所示:

这里写图片描述

一般来说,间隔(Margin)中间是无点区域。为了偏袒于某一类样本,超平面到两类样本点集合的最小距离都是相等的,即间隔等于超平面到两类样本集的最小距离*2。对于间隔越大的超平面,分类犯错的机率就越小,分类的确信度就越高。

二、超平面和间隔

接下来,需要做的就是将间隔(Margin)进行最大化,来寻找最优超平面。在这之前,需要对超平面进行定义,以便于计算点到超平面距离。

可将超平面定义为 这里写图片描述,向量 这里写图片描述 为超平面的法向量,标量 b 为截距。

这里写图片描述

这里写图片描述

向量 x 为样本的特征向量。而向量 x 点乘 向量 这里写图片描述 可理解为向量 x 在 向量 w 上未进行归一化的投影。

这里写图片描述

通过这样定义超平面后,由图可以发现:所有在超平面右上方的样本点都有 这里写图片描述 ,所有在超平面左下方的样本点都有 这里写图片描述

当然,所有在超平面上的点都有 这里写图片描述

进一步,我们可以通过等比例缩放 法向量 这里写图片描述 和 截距 b,使得:

这里写图片描述

其中,这里写图片描述 为第 i 个样本的特征向量,并将正样本的标签令作 1,负样本的标签令作 -1;这样这两个式子整合为一个式子:

这里写图片描述

具体效果如图所示:

这里写图片描述

而“支持向量”就是所有落在间隔两边的超平面H1、H2上的点,超平面H1、H2上的任意一点(支持向量)到分界超平面的距离为 这里写图片描述 。因此,隔间的宽度应该为 这里写图片描述

具体的公式推导如下:

这里写图片描述

三、最大化间隔

在上面,我们找到了间隔(margin)的表达式为 这里写图片描述,也找到了向量w的约束条件为

#include <math.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <float.h> #include <string.h> #include <stdarg.h> #include <limits.h> #include <locale.h> #include "svm.h" int libsvm_version = LIBSVM_VERSION; typedef float Qfloat; typedef signed char schar; #ifndef min template <class T> static inline T min(T x,T y) { return (x<y)?x:y; } #endif #ifndef max template <class T> static inline T max(T x,T y) { return (x>y)?x:y; } #endif template <class T> static inline void swap(T& x, T& y) { T t=x; x=y; y=t; } template <class S, class T> static inline void clone(T*& dst, S* src, int n) { dst = new T[n]; memcpy((void *)dst,(void *)src,sizeof(T)*n); } static inline double powi(double base, int times) { double tmp = base, ret = 1.0; for(int t=times; t>0; t/=2) { if(t%2==1) ret*=tmp; tmp = tmp * tmp; } return ret; } #define INF HUGE_VAL #define TAU 1e-12 #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) static void print_string_stdout(const char *s) { fputs(s,stdout); fflush(stdout); } static void (*svm_print_string) (const char *) = &print_string_stdout; #if 1 static void info(const char *fmt,...) { char buf[BUFSIZ]; va_list ap; va_start(ap,fmt); vsprintf(buf,fmt,ap); va_end(ap); (*svm_print_string)(buf); } #else static void info(const char *fmt,...) {} #endif // // Kernel Cache // // l is the number of total data items // size is the cache size limit in bytes // class Cache { public: Cache(int l,long int size); ~Cache(); // request data [0,len) // return some position p where [p,len) need to be filled // (p >= len if nothing needs to be filled) int get_data(const int index, Qfloat **data, int len); void swap_index(int i, int j); private: int l; long int size; struct head_t { head_t *prev, *next; // a circular list Qfloat *data; int len; // data[0,len) is cached in this entry }; head_t *head; head_t lru_head; void lru_delete(head_t *h); void lru_insert(head_t *h); }; Cache::Cache(int l_,long int size_):l(l_),size(size_) { head = (head_t *)calloc(l,sizeof(head_t)); // initialized to 0 size /= sizeof(Qfloat); size -= l * sizeof(head_t) / sizeof(Qfloat); size = max(size, 2 * (long int) l); // cache must be large enough for two columns lru_head.next = lru_head.prev = &lru_head; } Cache::~Cache() { for(head_t *h = lru_head.next; h != &lru_head; h=h->next) free(h->data); free(head); } void Cache::lru_delete(head_t *h) { // delete from current location h->prev->next = h->next; h->next->prev = h->prev; } void Cache::lru_insert(head_t *h) { // insert to last position h->next = &lru_head; h->prev = lru_head.prev; h->prev->next = h; h->next->prev = h; } int Cache::get_data(const int index, Qfloat **data, int len) { head_t *h = &head[index]; if(h->len) lru_delete(h); int more = len - h->len; if(more > 0) { // free old space while(size < more) { head_t *old = lru_head.next; lru_delete(old); free(old->data); size += old->len; old->data = 0; old->len = 0; } // allocate new space h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len); size -= more; swap(h->len,len); }
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值