介绍
- 支持向量机是一种二类分类模型
支持向量机学习方法包含构建由简至繁的模型
- 线性可分支持向量机(linearsupportvectormachineinlinearlyseparablecase)
- 线性支持向量机(linearsupportvectormachine)
- 非线性支持向量机(non-linearsupportvectormachine)、
思想
SVM 的主要思想是通过寻找一个能够将不同类别的数据分隔开的超平面来进行分类。在二维平面上,超平面就是一条直线,而在更高维度的空间中,超平面就是一个超平面。
SVM 的一个重要特点是可以通过核函数来将数据映射到高维特征空间中,使得数据在原始空间中不可分的情况下,在高维特征空间中变得线性可分。SVM 的优化目标是最大化分类器的边界,即找到一个距离超平面最近的点到超平面的距离最大化,这个距离被称为“间隔”。
在支持向量机中,“支持向量”指的是离超平面最近的那些数据点。这些点对于确定超平面的位置和方向非常重要,因为它们决定了超平面的位置和间隔的大小,而其他的数据点对于超平面的位置和方向没有影响。
在训练支持向量机模型时,找到支持向量是一个重要的步骤。一旦找到了支持向量,就可以确定超平面的位置和方向,并计算出分类器的间隔。同时,支持向量也可以用于对新的数据进行分类,只需要计算新数据点到超平面的距离即可。
在支持向量机的优化目标中,最大化分类器的间隔即相当于最小化支持向量到超平面的距离,这个距离被称为“间隔边界”。因此,支持向量机的优化目标实际上就是找到一个最大间隔的超平面,使得离超平面最近的那些数据点到超平面的距离最大化。
SVM 的优点包括:对于高维度空间中的数据分类效果显著,能够处理非线性分类问题,泛化能力强,容易解释。然而,SVM 的缺点则在于对于大规模数据的处理速度较慢,需要选择合适的核函数,对于噪声数据和重叠数据的处理不优秀。
概念
拉格朗日乘子法
是在优化函数存在等值约束的情况下的一种最优化求解方式;其中参数α被称为拉格朗日乘子,要求α不等于0
m
i
n
x
f
(
x
)
s
.
t
.
:
h
i
(
x
)
=
0
,
i
=
1
,
2
,
.
.
.
,
p
\scriptsize{ min_xf(x) \quad \quad s.t.:h_i(x)=0,i=1,2,...,p }
minxf(x)s.t.:hi(x)=0,i=1,2,...,p
↓
\downarrow
↓
m
i
n
x
f
(
x
)
+
∑
i
=
1
p
α
i
h
i
(
x
)
,
α
i
≠
0
\scriptsize{ min_xf(x)+\sum_{i=1}^p\alpha_i h_i(x), \quad \alpha_i≠0 }
minxf(x)+∑i=1pαihi(x),αi=0
KKT条件
是泛兰格朗日乘子法的一种形式;主要用于当我们的优化函数存在不等值约束的情况下的一种最优化求解方式;KKT条件即满足不等式约束情况下的条件
m
i
n
x
f
(
x
)
s
.
t
:
h
k
(
x
)
=
0
,
k
=
1
,
2
,
.
.
.
p
g
j
(
x
)
≤
0
,
j
=
1
,
2
,
.
.
.
,
q
\scriptsize{ min_x f(x) \quad \quad s.t:h_k(x)=0,k=1,2,...p \quad g_j(x)≤0,j=1,2,...,q }
minxf(x)s.t:hk(x)=0,k=1,2,...pgj(x)≤0,j=1,2,...,q
↓
\downarrow
↓
L
(
x
,
α
,
β
)
=
f
(
x
)
+
∑
i
=
1
p
α
i
h
i
(
x
)
+
∑
i
=
1
q
β
i
g
i
(
x
)
;
α
i
≠
0
,
β
≥
0
\scriptsize{ L(x,\alpha,\beta)=f(x)+\sum_{i=1}^p\alpha_i h_i(x) + \sum_{i=1}^q \beta_i g_i(x); \quad \alpha_i≠0,\beta≥0 }
L(x,α,β)=f(x)+∑i=1pαihi(x)+∑i=1qβigi(x);αi=0,β≥0
m
i
n
x
L
(
x
,
α
,
β
)
\scriptsize{ min_x L(x,\alpha,\beta) }
minxL(x,α,β)
感知器模型
感知器算法是最古老的分类算法之一,远离比较简单,不过模型的分类泛化能力比较弱,不过感知器模型是SVM、神经网络、深度学习等算法的基础。
感知器的思想很简单:比如某个班上有很多学员,分为男学员和女学员,感知器模型就是试图找到一条直线,能够把所有的男学员和女学员分隔开,如果是高维空间,感知器模型寻找的就是一个超平面,能够把所有二元类别分割开。感知器模型的前提是:数据是线性可分的。
感知器模型为:
y
^
=
s
i
g
n
(
θ
⋅
x
)
=
{
+
1
,
θ
⋅
x
>
0
−
1
,
θ
⋅
x
<
0
\scriptsize{ \hat y = sign(\theta \cdot x)= \begin{cases} +1,\theta \cdot x > 0 \\ -1, \theta \cdot x < 0 \end{cases} }
y^=sign(θ⋅x)={+1,θ⋅x>0−1,θ⋅x<0
SVM
支持向量机本身是一个二元分类算法,是对感知器算法模型的一种扩展,现在的SVM算法支持线性分类和非线性分类的分类应用,并且也能够直接将SVM应用于回归应用中,同时通过OvR或者OvO的方式我们也可以将SVM应用在多元分类领域中。在不考虑集成学习算法、不考虑特定的数据集的时候,在分类算法中SVM可以说特别优秀了。
#!/usr/bin/env python
# coding: utf-8
# # 感知器模型流程
# - 1、初始化w,b
# - 2、遍历所有训练数据在训练集中选出误分类点:
# - - 如果y != sign(wx+b) 或者 y*(wx+b)<=0 则为误分类
# - - 根据误分类点计算:w_new = w_old - alpha * 对w的梯度,b_new = b_old - alpha * 对b的梯度
# - 3、直到没有误分类点或者达到迭代次数停止迭代
#
# In[2]:
import sys
import numpy as np
import matplotlib.pyplot as plt
p_x = np.array([[4.0, 2.0], [3.0, 2.0], [2.0, 1.0], [2.5, 1.0]])
y = np.array([1, 1, -1, -1])
for i in range(len(p_x)):
if y[i] == 1:
plt.plot(p_x[i][0], p_x[i][1], 'ro')
else:
plt.plot(p_x[i][0], p_x[i][1], 'bo')
# plt.show()
# sys.exit()
# 初始化w和b
w = np.array([0.0, 1.0])
b = 0.0
# 学习率
alpha = 0.1
# 做100次迭代 选出分类错误的进行迭代 SGD/MBGD
for i in range(500):
choice = -1 ### 选择误分类点
# error_list = []
for j in range(len(p_x)):
## 判断误分类点
if y[j] != np.sign(np.dot(w, p_x[j]) + b):
choice = j
# error_list.append(j)
## 这里使用SGD,所以找到一个误分类点就可以更新一次
break
## 如果没有误分类点。停止迭代
if choice == -1:
break
w = w + alpha * y[choice] * p_x[choice]
b = b + alpha * y[choice] * 1
# if len(error_list) == 0:
# break
# for item in error_list:
# w += alpha * y[item] * p_x[item]
# b += alpha * y[item]
print(i)
print('w:{}\nb:{}'.format(w, b))
## 画出分割直线 ## w1*x1+w2*x2+b = 0 ==> x2 = -(w1*x1+b)/w2
line_x = [0, 10]
line_y = [0, 0]
for i in range(len(line_x)):
# todo:w[1] == 0
if w[1] != 0:
line_y[i] = (-w[0] * line_x[i] - b) / w[1]
else:
line_x = [-b / w[0], -b / w[0]]
line_y = [0, 1]
plt.plot(line_x, line_y)
plt.show()
# In[3]:
# model = np.sign(np.dot(w, x) + b)
# In[ ]:
线性可分SVM
在感知器模型中,算法是在数据中找出一个划分超平面,让尽可能多的数据分布在这个平面的两侧,从而达到分类的效果,但是在实际数据中这个符合我们要求的超平面可能存在多个。
在感知器模型中,我们可以找到多个可以分类的超平面将数据分开,并且优化时希望所有的点(预测正确的点)都离超平面尽可能的远,但是实际上离超平面足够远的点基本上都是被正确分类的,所以这个是没有意义的;反而比较关心那些离超平面很近的点,这些点比较容易分错。所以说我们只要让离超平面比较近的点尽可能的远离这个超平面,那么我们的模型分类效果应该就会比较不错喽。SVM其实就是这个思想
线性可分(LinearlySeparable):在数据集中,如果可以找出一个超平面,将两组数据分开,那么这个数据集叫做线性可分数据
线性不可分(Linearlnseparable):在数据集中,没法找出一个超平面,能够将两组数据分开,那么这个数据集就叫做线性不可分数据
分割超平面(SeparatingHyperplane):将数据集分割开来的直线/平面叫做分割超平面
支持向量(SupportVector):离分割超平面最近的那些点叫做支持向量。
间隔(Margin):支持向量数据点到分割超平面的距离称为间隔。
总结
- 要求数据必须线性可分
- 纯线性可分的SVM模型对于异常数据的预测可能会不太准;
- 对于线性可分的数据,线性SVM分类器的效果非常不错
SVM的软间隔模型
为了解决普通SVM的异常点问题
硬间隔:线性可分SVM中,要求函数距离一定是大于1的,可以认为线性划分SVM中的距离度量就是硬间隔
软间隔:SVM对于训练集中的每个样本都引入一个松弛因子( ξ \xi ξ),使得函数距离加上松弛因子后的值是大于等于1;这表示对样本到超平面距离的要求放松了
最终优化后的目标函数/损失函数和线性可分SVM(硬间隔)模型基本一样,除了约束条件不同而已,也就是说也可以使用SMO算法来求解。
m
i
n
1
2
∑
i
=
1
,
j
=
1
m
β
i
β
j
y
(
i
)
y
(
j
)
x
(
i
)
T
x
(
j
)
−
∑
i
=
1
m
β
i
min\frac{1}{2} \sum_{i=1,j=1}^m \beta_i \beta_j y^{(i)}y^{(j)}x^{(i)^{T}}x^{(j)}- \sum_{i=1}^m \beta_i
min21∑i=1,j=1mβiβjy(i)y(j)x(i)Tx(j)−∑i=1mβi
s
.
t
:
∑
i
=
1
m
β
i
y
(
i
)
=
0
0
≤
β
i
≤
C
,
i
=
1
,
2
,
.
.
.
,
m
s.t:\sum_{i=1}^m \beta_i y^{(i)}=0 \quad 0≤\beta_i≤C,i=1,2,...,m
s.t:∑i=1mβiy(i)=00≤βi≤C,i=1,2,...,m
总结
- 可以解决线性数据中携带异常点的分类模型构建的问题;
- 通过引入惩罚项系数(松弛因子),可以增加模型的泛化能力,即鲁棒性;
- 如果给定的惩罚项系数C越小,表示在模型构建的时候,允许存在越多的分类错误的样本,也就表示此时模型的准确率会比较低; 如果惩罚项系数越大,表示在模型构建的时候,就越不允许存在分类错误的样本, 就是表示此时模型的准确率会比较高.
非线性可分SVM
线性可分SVM和软间隔线性可分SVM都要求数据本身是线性可分的, 对于完全不可以线性可分的数据, 这两种算法模型就没法解决这个问题了
在SVM线性不可分数据上,如果将数据映射到高维空间,那么数据就会变成线性可分的, 从而就可以使用线性可分SVM模型或者软间隔线性可分SVM模型
也就是说,对于线性不可分SVM模型来讲,重点在于低维特征数据到高维特征数据之间的映射
定义一个从低维特征空间到高维特征空间的映射函数
Φ
\Phi
Φ, 非线性可分SVM的优化目标函数:
m
i
n
1
2
∑
i
=
1
,
j
=
1
m
β
i
β
j
y
(
i
)
y
(
j
)
Φ
(
x
(
i
)
)
Φ
(
x
(
j
)
)
−
∑
i
=
1
m
β
i
min\frac{1}{2} \sum_{i=1,j=1}^{m} \beta_i \beta_j y^{(i)}y^{(j)}\Phi (x^{(i)}) \Phi (x^{(j)}) - \sum_{i=1}^m \beta_i
min21∑i=1,j=1mβiβjy(i)y(j)Φ(x(i))Φ(x(j))−∑i=1mβi
$s.t: \sum_{i=1}^{m} \beta_i y_{(i)}=0
\quad \quad
0≤\beta_i≤C, i=1,2,…,m$
可以看到的是,只需将原来的低维空间中的两个向量的点积转换为高维空间中两个向量的点积即可.
但这个方法可能会导致维度爆炸问题, 为此我们引入核函数K.
核函数
假设函数
Φ
\Phi
Φ是一个从低维特征空间到高维特征空间的一个映射,那么如果存在函数
K
(
x
,
z
)
K(x, z)
K(x,z), 对于任意的低维特征向量x和z, 都有:
K
(
x
,
z
)
=
Φ
(
x
)
⋅
Φ
(
z
)
K(x,z)=\Phi(x) \cdot \Phi(z)
K(x,z)=Φ(x)⋅Φ(z)
- 称函数 K ( x , z ) K(x,z) K(x,z)为核函数(kernel function)
- 核函数:使数据不发生特征升维的情况下,可以得到特征做维度扩展后的点乘的结果。
各种核函数
线性核函数(Linear Kernel):
K
(
x
,
z
)
=
x
⋅
z
K(x,z)=x \cdot z
K(x,z)=x⋅z
多项式核函数(Polynomial Kernel):
K
(
x
,
z
)
=
(
γ
x
⋅
z
+
r
)
d
K(x,z)=(\gamma x \cdot z + r)^d
K(x,z)=(γx⋅z+r)d
其中
γ
、
r
、
d
属于超参,需要调参定义;
其中\gamma、r、d属于超参,需要调参定义;
其中γ、r、d属于超参,需要调参定义;
高斯核函数(Guassian Kernel):
K
(
x
,
z
)
=
e
−
γ
∣
∣
x
−
z
∣
∣
2
2
K(x,z)=e^{-\gamma ||x-z||_2^{2}}
K(x,z)=e−γ∣∣x−z∣∣22
其中
γ
属于超参,要求大于
0
其中\gamma属于超参,要求大于0
其中γ属于超参,要求大于0
Sigmoid核函数(Sigmoid Kernel):
K
(
x
,
z
)
=
t
a
n
h
(
γ
x
⋅
z
+
r
)
K(x, z)=tanh(\gamma x \cdot z + r)
K(x,z)=tanh(γx⋅z+r)
其中
γ
、
r
属于超参
其中\gamma、r属于超参
其中γ、r属于超参
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC, OneClassSVM
import time
import sys
import warnings
warnings.filterwarnings('ignore')
info = pd.read_csv('iris.data', header=None)
X = info.iloc[:, 0:2]
Y = info.iloc[:, -1]
###labelencoder
Y = Y.replace('Iris-setosa', 0)
Y = Y.replace('Iris-versicolor', 1)
Y = Y.replace('Iris-virginica', 2)
xtrain, xtest, ytrain, ytest = train_test_split(X, Y, test_size=0.3, random_state=10)
# print(xtrain)
rbf = SVC(kernel='rbf', C=1.0, gamma=0.1)
linears = SVC(kernel='linear', C=0.1)
polys = SVC(kernel='poly', gamma=0.1, coef0=0.5, degree=4, C=0.01)
sigmoids = SVC(kernel='sigmoid', gamma=0.001, coef0=0.5, C=1.0, decision_function_shape='ovr')
# model.append([rbf,linears,polys,sigmoids,precomputeds])
# print(model)
models = np.array([rbf, linears, polys, sigmoids])
# sys.exit(0)
times = []
train_scores = []
test_scores = []
for model in models:
state = time.time()
model.fit(xtrain, ytrain)
end = time.time()
train_score = model.score(xtrain, ytrain)
test_score = model.score(xtest, ytest)
times.append(end - state)
train_scores.append(train_score)
test_scores.append(test_score)
print('运行所需时间:', times)
print('训练集分数:', train_scores)
print('测试集分数:', test_scores)
plt.figure(num=1)
plt.plot(['01rbf', '02linear', '03poly', '04sigmoid'], train_scores, 'r', label='trainscore')
plt.plot(['01rbf', '02linear', '03poly', '04sigmoid'], test_scores, 'b', label='testscore')
plt.legend()
plt.figure(num=2)
plt.plot(['01rbf', '02linear', '03poly', '04sigmoid'], times, 'g', label='time')
plt.legend()
plt.show()
核函数总结
- 核函数可以自定义;核函数必须是正定核函数,即Gram矩阵是半正定矩阵;
- 核函数的价值在于它虽然也是将特征进行从低维到高位的转换,但核函数它事先在低维上进行计算,而将实质上的分类效果表现在高维上,即避免了直接在高维空间中的复杂计算;
- 通过核函数,可以将非线性可分的数据转换为线性可分数据;