【机器学习】SVM支持向量机(详解)

什么是SVM

SVM(全称Support Vector Machine)中文名支持向量机。SVM是一种监督机器学习算法,是一种二分类模型,它的目的是寻找一个超平面来对样本进行分割,分割的原则是间隔最大化,最终转化为一个凸二次规划问题来求解。可用于分类或回归挑战。然而,它主要用于分类问题。

最大间隔与分类

如果一个线性函数能够将样本分开,称这些数据样本是线性可分的。那么什么是线性函数呢?其实很简单,在二维空间中就是一条直线,在三维空间中就是一个平面,以此类推,如果不考虑空间维数,这样的线性函数统称为超平面。我们看一个简单的二维空间的例子,+代表正类,-代表负类,样本是线性可分的,但是很显然不只有这一条直线可以将样本分开,而是有无数条,我们所说的线性可分支持向量机就对应着能将数据正确划分并且间隔最大的直线。
在样本空间中寻找一个超平面, 将不同类别的样本分开。
在这里插入图片描述
在这里插入图片描述
最大化间隔: 寻找参数w和b , 使得下述公式最大:
在这里插入图片描述
最大间隔问题的拉格朗日乘法
第一步:引入拉格朗日乘子ai≥ 0得到拉格朗日函数
在这里插入图片描述
第二步:令L(w,b,a)对w和b的偏导为零
在这里插入图片描述
第三步:w, b回代到第一步
在这里插入图片描述

对偶问题

1.等式约束

在这里插入图片描述

2.不等式约束的KKT条件

在这里插入图片描述

3.KKT

在这里插入图片描述

SMO高效优化算法

SMO算法的目标是求出一系列alpha和b,一旦求出这些alpha,就很容易算出权重向量w并得到分割超平面。
SMO算法工作原理:每次循环中选择两个alpha进行优化处理。一旦找到一对合适的alpha,那么就增大其中一个,减小另外一个。

在这里插入图片描述
算法流程:每次选取两个a进行更新
在这里插入图片描述
在这里插入图片描述

编程求解线性SVM

通过简约版线性SMO算法进行支持向量机分类:
部分数据集展示:
在这里插入图片描述

运行代码:

# -*- coding:UTF-8 -*-
import matplotlib.pyplot as plt
import numpy as np
import random
def loadDataSet(fileName):
	dataMat = []; labelMat = []
	fr = open(fileName)
	for line in fr.readlines():                                     #逐行读取,滤除空格等
		lineArr = line.strip().split('\t')
		dataMat.append([float(lineArr[0]), float(lineArr[1])])      #添加数据
		labelMat.append(float(lineArr[2]))                          #添加标签
	return dataMat,labelMat

def selectJrand(i, m):
	j = i                                 #选择一个不等于i的j
	while (j == i):
		j = int(random.uniform(0, m))
	return j
	
def clipAlpha(aj,H,L):
	if aj > H: 
		aj = H
	if L > aj:
		aj = L
	return aj
	
def showDataSet(dataMat, labelMat):
	data_plus = []                                  #正样本
	data_minus = []                                 #负样本
	for i in range(len(dataMat)):
		if labelMat[i] > 0:
			data_plus.append(dataMat[i])
		else:
			data_minus.append(dataMat[i])
	data_plus_np = np.array(data_plus)              #转换为numpy矩阵
	data_minus_np = np.array(data_minus)            #转换为numpy矩阵
	plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1])   #正样本散点图
	plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1]) #负样本散点图
	plt.show()

def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
	#转换为numpy的mat存储
	dataMatrix = np.mat(dataMatIn); labelMat = np.mat(classLabels).transpose()
	#初始化b参数,统计dataMatrix的维度
	b = 0; m,n = np.shape(dataMatrix)
	#初始化alpha参数,设为0
	alphas = np.mat(np.zeros((m,1)))
	#初始化迭代次数
	iter_num = 0
	#最多迭代matIter次
	while (iter_num < maxIter):
		alphaPairsChanged = 0
		for i in range(m):
			#步骤1:计算误差Ei
			fXi = float(np.multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
			Ei = fXi - float(labelMat[i])
			#优化alpha,设定一定的容错率。
			if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
				#随机选择另一个与alpha_i成对优化的alpha_j
				j = selectJrand(i,m)
				#步骤1:计算误差Ej
				fXj = float(np.multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b
				Ej = fXj - float(labelMat[j])
				#保存更新前的aplpha值,使用深拷贝
				alphaIold = alphas[i].copy(); alphaJold = alphas[j].copy();
				#步骤2:计算上下界L和H
				if (labelMat[i] != labelMat[j]):
				    L = max(0, alphas[j] - alphas[i])
				    H = min(C, C + alphas[j] - alphas[i])
				else:
				    L = max(0, alphas[j] + alphas[i] - C)
				    H = min(C, alphas[j] + alphas[i])
				if L==H: print("L==H"); continue
				#步骤3:计算eta
				eta = 2.0 * dataMatrix[i,:]*dataMatrix[j,:].T - dataMatrix[i,:]*dataMatrix[i,:].T - dataMatrix[j,:]*dataMatrix[j,:].T
				if eta >= 0: print("eta>=0"); continue
				#步骤4:更新alpha_j
				alphas[j] -= labelMat[j]*(Ei - Ej)/eta
				#步骤5:修剪alpha_j
				alphas[j] = clipAlpha(alphas[j],H,L)
				if (abs(alphas[j] - alphaJold) < 0.00001): print("alpha_j变化太小"); continue
				#步骤6:更新alpha_i
				alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])
				#步骤7:更新b_1和b_2
				b1 = b - Ei- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i,:]*dataMatrix[i,:].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[i,:]*dataMatrix[j,:].T
				b2 = b - Ej- labelMat[i]*(alphas[i]-alphaIold)*dataMatrix[i,:]*dataMatrix[j,:].T - labelMat[j]*(alphas[j]-alphaJold)*dataMatrix[j,:]*dataMatrix[j,:].T
				#步骤8:根据b_1和b_2更新b
				if (0 < alphas[i]) and (C > alphas[i]): b = b1
				elif (0 < alphas[j]) and (C > alphas[j]): b = b2
				else: b = (b1 + b2)/2.0
				#统计优化次数
				alphaPairsChanged += 1
				#打印统计信息
				print("第%d次迭代 样本:%d, alpha优化次数:%d" % (iter_num,i,alphaPairsChanged))
		#更新迭代次数
		if (alphaPairsChanged == 0): iter_num += 1
		else: iter_num = 0
		print("迭代次数: %d" % iter_num)
	return b,alphas

def showClassifer(dataMat, w, b):
	#绘制样本点
	data_plus = []                                  #正样本
	data_minus = []                                 #负样本
	for i in range(len(dataMat)):
		if labelMat[i] > 0:
			data_plus.append(dataMat[i])
		else:
			data_minus.append(dataMat[i])
	data_plus_np = np.array(data_plus)              #转换为numpy矩阵
	data_minus_np = np.array(data_minus)            #转换为numpy矩阵
	plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1], s=30, alpha=0.7)   #正样本散点图
	plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1], s=30, alpha=0.7) #负样本散点图
	#绘制直线
	x1 = max(dataMat)[0]
	x2 = min(dataMat)[0]
	a1, a2 = w
	b = float(b)
	a1 = float(a1[0])
	a2 = float(a2[0])
	y1, y2 = (-b- a1*x1)/a2, (-b - a1*x2)/a2
	plt.plot([x1, x2], [y1, y2])
	#找出支持向量点
	for i, alpha in enumerate(alphas):
		if abs(alpha) > 0:
			x, y = dataMat[i]
			plt.scatter([x], [y], s=150, c='none', alpha=0.7, linewidth=1.5, edgecolor='red')
	plt.show()
	
def get_w(dataMat, labelMat, alphas):
    alphas, dataMat, labelMat = np.array(alphas), np.array(dataMat), np.array(labelMat)
    w = np.dot((np.tile(labelMat.reshape(1, -1).T, (1, 2)) * dataMat).T, alphas)
    return w.tolist()

if __name__ == '__main__':
	dataMat, labelMat = loadDataSet('testSet.txt')
	b,alphas = smoSimple(dataMat, labelMat, 0.6, 0.001, 40)
	w = get_w(dataMat, labelMat, alphas)
	showClassifer(dataMat, w, b)

运行结果:
可以看到红色圈圈圈起来点是支持向量点。
在这里插入图片描述

通过SMO-SVM实现对鸢尾花数据集的二分类

svm.py

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
# 鸢尾花(iris)数据集
# 数据集内包含 3 类共 150 条记录,每类各 50 个数据,
# 每条记录都有 4 项特征:花萼长度、花萼宽度、花瓣长度、花瓣宽度,
# 可以通过这4个特征预测鸢尾花卉属于(iris-setosa, iris-versicolour, iris-virginica)中的哪一品种。
# 这里只取前100条记录,两项特征,两个类别。
def create_data():
    iris = load_iris()
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['label'] = iris.target
    df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
    data = np.array(df.iloc[:100, [0, 1, -1]])
    for i in range(len(data)):
        if data[i,-1] == 0:
            data[i,-1] = -1
    return data[:,:2], data[:,-1]
#使用RBF(Radial basis function)核函数处理
def K(x,z,sigma=1.5):
    return np.exp(np.dot((x-z),(x-z).T)/-(2*sigma**2))
#对应课本147页的g(x_i),该函数助于验证KKT条件
def g(i,x,y,alpha,b):
    sum=b
    for j in range(len(y)):
        sum+=alpha[j]*y[j]*K(x[i],x[j])
    return sum
#验证第i个样本点是否满足KKT条件
def isKKT(alpha,i,x,y,b,C):
    if alpha[i]==0 and y[i]*g(i,x,y,alpha,b)>=1:
        return True
    elif alpha[i]==C and y[i]*g(i,x,y,alpha,b)<=1:
        return True
    elif alpha[i]>0 and alpha[i]<C and y[i]*g(i,x,y,alpha,b)==1:
        return True
    else:
        return False
#验证第i个样本点违反KKT条件的程度。由于KKT条件和y_i*g(x_i)与1的不等式有关
#因此计算y_i*g(x_i)与1之间差值的绝对值作为衡量违反程度的标准
def vioKKT(alpha,i,x,y,b):
    return abs(y[i]*g(i,x,y,alpha,b)-1)
#在数组x中找到值为a的元素第一次出现的位置
def findindex(x,a):
    for i in range(len(x)):
        if x[i]==a:
            return i
#某个样本点分类误差函数
def E(w,b,x_k,y_k):
    predi_k=int(np.sign(np.dot(w,x_k.T)+b))
    return predi_k-y_k
#计算样本点与分割直线的距离
def distance_count(x,w,b):
    return abs(w[0]*x[0]+w[1]*x[1]+b) / np.sqrt(w[0]**2 + w[1]**2)

main.py

from functions import *
from numpy import *
from random import *
from matplotlib import pyplot as plt
def train(C=1.0):
    #获得数据集
    x,y=create_data()
    #设定迭代次数为100次
    iter=100
    #样本容量也就是标签的个数
    N=len(y)
    #alpha的初始值取全0
    alpha=zeros(len(y))
    #设置i,j的初始值(对应alpha1和alpha2)
    i,j=randint(0,N-1),randint(0,N-1)
    #保证i≠j
    while i==j:
        i=randint(0,N-1)
    for k in range(iter):
        #x的尺寸为一个1×2行向量
        x_i,x_j=x[i],x[j]
        #y的取值为+1或-1
        y_i,y_j=y[i],y[j]
        #计算ita,为计算a2_newunc做准备
        ita=K(x_i,x_i)+K(x_j,x_j)-2*K(x_i,x_j)
        if ita==0:
            continue
        #计算分割平面参数w与b
        #x:100×2矩阵,w:1×2矩阵
        #由于y-dot(w,x.T)是个与y等长的行向量,取其各元素平均值
        w=dot(alpha*y,x)
        b=mean(y-dot(w,x.T))
        #计算误差E1和E2
        E_i=E(w,b,x_i,y_i)
        E_j=E(w,b,x_j,y_j)
        #计算a2_ewunc
        a1_old=alpha[i]
        a2_old=alpha[j]
        a2_newunc=a2_old+y_j*(E_i-E_j)/ita
        #计算L与H
        L,H=0.0,0.0
        if y_i!=y_j:
            L=max(0,a2_old-a1_old)
            H=min(C,C+a2_old-a1_old)
        elif y_i==y_j:
            L=max(0,a2_old+a1_old-C)
            H=min(C,a2_old+a1_old)
        #计算剪辑后a2_new与a1_new的值
        a2_new=max(L,min(H,a2_newunc))
        a1_new=a1_old+y_i*y_j*(a2_old-a2_new)
        #更新alpha
        alpha[i],alpha[j]=a1_new,a2_new
        #violation表示每个元素违反KKT条件的程度
        violation=zeros(N)
        #对每一个样本点检验KKT条件,在violation内记录每个样本点违反KKT的程度
        for k in range(N):
            if isKKT(alpha,k,x,y,b,C)==False:
                violation[k]=float(vioKKT(alpha,k,x,y,b))
            #如果没有违反KKT条件,则违反程度是0
            else:
                violation[k]=0.0
        #找到violation中违反程度最大的点,设定为i,对应alpha_1
        i=findindex(violation,max(violation))
        #这里设置j(对应alpha_2)为不等于i的随机数。
        #原本alpha_2的选取应该是令abs(E_i-E_k)最大的k值对应的alpha点
        #经过测试,在大多数情况下,abs(E_i-E_k)(1×100向量)的所有元素都是0
        #即预测每个元素都准确,每个元素的分类误差都是0,误差的差值也是0
        #只有少数情况下,会有一个误差差值不等于0
        #对于前一种情况,无所谓“最大的误差差值”(因为都是0),因此只能设置j为随机数
        #对于后一种情况,由于出现的次数少,并且那一个不为0的差值的元素出现的位置具有随机性
        #因此总是将j设定为随机数
        j=randint(0,N-1)
        while j==i:
            j = randint(0, N - 1)
    #计算最终(迭代100次)分割平面参数
    w = dot(alpha * y, x)
    b = mean(y - dot(w, x.T))
    draw_x, draw_y, draw_label = [], [], []
    #在散点图上标记样本点的位置,样本点第一个元素作为x坐标,第二个元素作为y坐标
    for p in x:
        draw_x.append(p[0])
        draw_y.append(p[1])
    #画散点图,其中支持向量呈现绿色,正类呈现红色,负类呈现蓝色
    #样本点离分割直线最近的为支持向量
    distance=zeros(len(y))
    for i in range(len(y)):
        distance[i]=distance_count(x[i],w,b)
    vector=findindex(distance,min(distance))
    for i in range(len(y)):
        if i==vector:
            draw_label.append('g')
        else:
            if y[i] > 0:
                draw_label.append('r')
            else:
                draw_label.append('b')
    plt.scatter(draw_x, draw_y, color=draw_label)
    plain_x = range(4, 8, 1)
    plain_y = []
    for i in plain_x:
        temp = double(-(w[0] * i + b) / w[1])
        plain_y.append(temp)
    plt.plot(plain_x, plain_y)
    #最终绘图
    plt.savefig('SMO.jpg')
    plt.show()
if __name__ == '__main__':
    train()

运行截图:
在这里插入图片描述

总结

SVM优点
1、解决小样本下机器学习问题。
2、解决非线性问题。
3、无局部极小值问题。(相对于神经网络等算法)
4、可以很好的处理高维数据集。
5、泛化能力比较强。
SVM缺点
1、对于核函数的高维映射解释力不强,尤其是径向基函数。
2、对缺失数据敏感。
3、调参麻烦,核函数难选难调

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 支持向量机(Support Vector Machine, SVM)是一种监督学习算法,旨在寻找最佳的超平面,将不同类别的数据分开。从分类问题的角度来看,SVM要求找到一个超平面,使得不同类别的样本点在该超平面两侧,同时离该超平面的距离最大。该最大距离被称为margin,而最佳的超平面被称为最大间隔超平面。 SVM的核心思想是将数据映射到高维空间中,使得数据在高维空间中更容易线性可分。在高维空间中,由于数据点增多,出现维度灾难的问题,而此时SVM可以通过核函数来避免该问题。 在求解最大间隔超平面时,SVM依赖于一些向量,称为支持向量。这些支持向量是最接近最大间隔超平面的样本点,它们对求解最大间隔超平面起到了至关重要的作用。 SVM可以应用于二分类、多分类以及回归问题,并且在实际应用中表现出色。在实际使用中,需要根据具体问题选择适当的核函数,并且需要对超参数进行调整。此外,还需要注意数据的标准化处理,以避免不同维度的数据误导模型。 总之,SVM是一种强大的分类器,具有多种核函数可供选择,能够适用于不同类型数据的分类问题。 ### 回答2: 支持向量机SVM)是一种监督学习算法,可以用于分类和回归问题。它的优点是可以处理非线性分类问题,并且对于数据集中的噪声有较好的鲁棒性。SVM利用最大化分类边界来进行分类,最终得到的分类器是一个高维空间中的超平面。 SVM的原理是基于寻找最优超平面,这个超平面能够把不同类别的样本点分开。在二维平面上,这个超平面可以是一条直线,但是在高维空间中,它是一个超平面。SVM通过最大化边缘来寻找最优的超平面。边缘是一个超平面到两边最近样本点的距离,它表明了模型的泛化能力,即分类器对于新的数据的表现能力。 支持向量机的核心思想是寻找一条最大间隔的线性分割超平面。在非线性分类任务中,使用核方法将特征向量转换为高维空间中的特征向量。在这个高维空间中,数据点可以被更好地分割,即可能会出现线性可分的情况。 关键的问题是如何解决分割垂直于某个维度的问题,此时支持向量机利用核函数的技巧将数据集映射到高维空间,并在这个高维空间中采用线性可分的方法来进行分类。通常情况下,核函数常用的有线性核、多项式核和径向基函数核。 其中,径向基函数核(RBF kernel)是最常用的核函数之一。这个核函数可以将数据点映射到一个无限维的特征空间上。径向基函数核使用一个参数gamma来决定核函数的变化速度。当gamma的值很小的时候,核函数的变化会很慢,分类的边界将会很宽。当gamma的值很大的时候,核函数的变化会很快,分类的边界将会很窄。 SVM的优化问题可以通过拉格朗日乘子法来解决。可以使用拉格朗日乘子来构造一个拉格朗日函数,并使用约束条件来推导对偶形式的SVM问题。这个对偶形式的问题可以使用高效的SMO算法来求解。 SVM是一种强大的分类方法,它适用于许多不同的应用场景,并在许多任务中实现了很好的结果。支持向量机具有较好的泛化性能、能够处理高维数据、适用于不同类型的输入数据,并且能够快速训练分类器。 ### 回答3: SVM是一种二分类模型,它的主要思想是通过线性或非线性的方式找到最优的超平面,将不同类别的数据点分开。SVM支持向量机通过选择支持向量,来定义最优的超平面。支持向量是离超平面最近的数据点,它们对最终分类结果起到决定性的作用。在选取支持向量的过程中,SVM支持向量机尝试选择最少的支持向量,以达到泛化能力强的效果。 SVM支持向量机的分类器可以采用不同的核函数进行拟合,从而实现非线性分类。其中,常用的核函数包括线性核、多项式核、高斯核等。利用核函数的基本原理,我们可以把数据从原本空间映射到更高维的空间中。在高维空间,数据点在非线性情况下更容易区分,从而提高了分类的准确率。 SVM支持向量机的训练过程可以利用现有的优化算法求解,例如序列最小优化算法(SMO)和改进的SMO算法等。这些算法通过不断迭代优化模型参数,直至达到最优的分类效果。在模型训练完成之后,我们可以利用模型进行分类预测,从而实现新数据的分类。 总之,SVM支持向量机是一种基于分类边界找寻的算法,在分类效果、泛化能力和计算效率等方面具有优势,因此在分类领域有着广泛的应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

打代码能当饭吃?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值