机器学习之支持向量机

news/2024/10/5 15:23:29

什么是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(dataMatrixdataMatrix[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(dataMatrixdataMatrix[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 LH: print("LH"); 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- a1x1)/a2, (-b - a1x2)/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实现对鸢尾花数据集的二分类

`
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)/-(2sigma**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_ig(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)


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 ij:
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(alphay,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_iy_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_iy_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 ji:
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、调参麻烦,核函数难选难调

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hjln.cn/news/43211.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Redis之发布订阅

发布订阅Redis 发布订阅(pub/sub)是一种消息通信模式,发送者(pub)发送消息,订阅者(sub)接收消息。 Redis 客户端可以订阅任意数量的频道。消息发送者&消息接收者&频道可以想象这么一个场景。我们抖音、微博关注了哪个博主,当他发布一条文章时,系统就会给我们推送他…

DBAPI 安装部署standalone 数据API接口发布平台

DBAPI 项目地址:https://gitee.com/freakchicken/db-api 项目简介:零代码开发api服务,只需编写sql,就可以生成http api服务。支持api动态创建,兼容多种数据库。 适用于BI报表、数据可视化大屏的后端接口快速开发。 旨在为企业数据服务的发布提供完整解决方案 一、下载安装…

wu

转载自 他是 ISIJ 第四名,也是在线知名题库的洛谷“网红”。 2024年全国青少年信息学奥林匹克竞赛冬令营(WC)上,以优秀成绩斩下第一名年仅六年级的 $liuyi0905$,成为最夺目的选手之一。 而且虽然是六年级的选手,但他取得优异成绩后,不少网友并不感到陌生,纷纷留言: 这…

如何阅读MTF图表

前言 1943年——就在二战方酣的困顿时期——蔡司(Zeiss)发展出一套称为「调制传递函数」(德:Modulationsbertragungsfunktion;英:Modulation Transfer Function, MTF;日:変调伝达关数)的科学程序,用来评量镜头的影像品质。光学仪器业者、相机镜头制造商欣然拥抱这项新…

openGauss-1主2从1级联回切

假设omm1 omm2 omm4 是idc1,omm3 是idc2 模拟IDC1 全挂:手动将级联节点提升为主:此时idc1正常了,需要加回集群: Omm1 buildOmm2 buildOmm4 build 之后启omm3 的crontab,靠cm去选主检查新主的数据:注意点: CM 起来后不能build了,所以要先build再启CM本文来自博客园,作者…

实例:vue

实例:vue.js实现模态窗口(弹窗)| Id | Title | DateAdded | SourceUrl | PostType | Body | BlogId | Description | DateUpdated | IsMarkdown | EntryName | CreatedTime | IsActive | AutoDesc | AccessPermission | | -------------| -------------| -------------| -----…

同步mysql数据到ElasticSearch的最佳实践

同步mysql数据到ElasticSearch的最佳实践| Id | Title | DateAdded | SourceUrl | PostType | Body | BlogId | Description | DateUpdated | IsMarkdown | EntryName | CreatedTime | IsActive | AutoDesc | AccessPermission | | -------------| -------------| ------------…

开源无代码 / 低代码平台 NocoBase 1.0.1-alpha.1: 区块支持高度设置

NocoBase 是一个极易扩展的开源无代码开发平台。完全掌控,无限扩展,助力你的开发团队快速响应变化,显著降低成本,不必投入几年时间和数百万资金研发,只需要花几分钟部署 NocoBase。 NocoBase 中文官网 官方文档 在线 Demo 新特性 区块支持高度设置(#4441)参考文档:区块…