【机器学习】SVM实例——对glass数据集进行分类处理
一、SVM是什么?
支持向量机(support vector machines, SVM)是一种二分类模型,它的基本模型是定义在特征空间上的间隔最大的线性分类器,间隔最大使它有别于感知机;
SVM还包括核函数,这使它成为实质上的非线性分类器。SVM的的学习策略就是间隔最大化,可形式化为一个求解凸二次规划的问题,也等价于正则化的合页损失函数的最小化问题。SVM的的学习算法就是求解凸二次规划的最优化算法。
概述:支持向量机的基本模型是定义在特征空间的间隔最大的线性分类器,其中间隔最大使它有别于感知机。支持向量机旨在求一个分离超平面。这个超平面使得离它最近的点能够最远。SVM用scikit-learn库实现起来比较简单。
1.线性可分
首先来了解一下什么是线性可分,在二维空间,两类点被一条直线完全分开叫做线性可分。严格的数学定义是: D 0 D_0 D0和 D 1 D_1 D1是 n n n维欧式空间中的两个点集。如果存在 n n n维向量 w w w和实数 b b b,使得 D 0 D_0 D0的点 x i x_i xi都有 w x i + b > 0 wx_i+b>0 wxi+b>0,而对于所有属于 D 1 D_1 D1的点 x j x_j xj则有 w x j + b < 0 wx_j+b<0 wxj+b<0,则我们称 D 0 D_0 D0和 D 1 D_1 D1线性可分。
2.最大间隔超平面
从二维扩展到多维空间中时,将 D 0 D_0 D0和 D 1 D_1 D1完全正确地划分开的 w x + b = 0 wx+b=0 wx+b=0就成了一个超平面。
为了使这个超平面更具鲁棒性,我们会去找最佳超平面,以最大间隔把两类样本分开的超平面,也称之为最大间隔超平面。
- 两类样本分别分割在该超平面的两侧;
- 两侧距离超平面最近的样本点到超平面的距离被最大化了。
3.支持向量
样本中距离超平面最近的这些点,叫做支持向量。
4.SVM最优化问题
SVM 想要的就是找到各类样本点到超平面的距离最远,也就是找到最大间隔超平面。任意超平面可以用下面这个线性方程来描述:
w
T
x
+
b
=
0
w^T x+b=0
wTx+b=0
二维空间点
(
x
,
y
)
(x,y)
(x,y)到直线
A
x
+
B
y
+
C
=
0
Ax+By+C= 0
Ax+By+C=0的距离公式是:
(
∣
A
x
+
B
y
+
C
∣
)
/
√
(
A
2
+
B
2
)
(|Ax+By+C|)/√(A^2+B^2 )
(∣Ax+By+C∣)/√(A2+B2)
扩展到n维空间后,点
x
=
(
x
1
,
x
2
,
…
x
n
)
x=(x_1,x_2,…x_n)
x=(x1,x2,…xn)到直线
w
T
x
+
b
=
0
w^T x+b=0
wTx+b=0 的距离为:
(
∣
w
T
x
+
b
∣
)
/
(
∣
∣
w
∣
∣
)
(|w^T x+b|)/(||w||)
(∣wTx+b∣)/(∣∣w∣∣)
其中
∣
∣
w
∣
∣
=
√
(
w
1
2
+
⋯
w
n
2
)
||w||= √(w_1^2+⋯w_n^2 )
∣∣w∣∣=√(w12+⋯wn2) 。
如上图1所示,根据支持向量的定义我们知道,支持向量到超平面的距离为
d
d
d,其他点到超平面的距离大于
d
d
d。
于是就有了下述公式:
转换可以得到:
∣
∣
w
∣
∣
d
||w||d
∣∣w∣∣d是正数,暂且令其为1(之所以令它等于 1,是为了方便推导和优化,且这样做对目标函数的优化没有影响),故:
将两个方差合并,可以简写为:
y
(
w
T
x
+
b
)
≥
1
y(w^T x+b)≥1
y(wTx+b)≥1
于是可以得到最大间隔超平面的上下两个超平面,每个支持向量到超平面的距离可以写为:
d
=
(
∣
w
T
x
+
b
∣
)
/
(
∣
∣
w
∣
∣
)
d= (|w^T x+b|)/(||w||)
d=(∣wTx+b∣)/(∣∣w∣∣)
由上述
y
(
w
T
x
+
b
)
>
1
>
0
y(w^T x+b)>1>0
y(wTx+b)>1>0可以得到
y
(
w
T
x
+
b
)
=
∣
w
T
x
+
b
∣
y(w^T x+b)= |w^T x+b|
y(wTx+b)=∣wTx+b∣,即:
d
=
(
y
(
w
T
x
+
b
)
)
/
(
∣
∣
w
∣
∣
)
d= (y(w^T x+b))/(||w||)
d=(y(wTx+b))/(∣∣w∣∣)
最大化这个距离:
m
a
x
2
∗
(
y
(
w
T
x
+
b
)
)
/
(
∣
∣
w
∣
∣
)
max2* (y(w^T x+b))/(||w||)
max2∗(y(wTx+b))/(∣∣w∣∣)
这里乘上2倍是为了后面的推导,对目标函数没有影响,对于得到的支持向量
y
(
w
T
x
+
b
)
=
1
y(w^T x+b)=1
y(wTx+b)=1,可以得到:
m
a
x
2
/
(
∣
∣
w
∣
∣
)
max 2/(||w||)
max2/(∣∣w∣∣)
再做一个转换:
m
i
n
1
/
2
∣
∣
w
∣
∣
min 1/2||w||
min1/2∣∣w∣∣
为方便计算(去除||w||的根号),可以转换为:
m
i
n
1
/
2
∣
∣
w
∣
∣
2
min 1/2 ||w||^2
min1/2∣∣w∣∣2
所以得到的最优化问题是:
m
i
n
1
/
2
∣
∣
w
∣
∣
2
s
.
t
y
i
(
w
T
x
i
+
b
)
≥
1
min 1/2 ||w||^2 s.t y_i (w^T x_i+b)≥1
min1/2∣∣w∣∣2s.tyi(wTxi+b)≥1
5.具体求解步骤
步骤1:构造拉格朗日函数
步骤2:利用强对偶转换
步骤3:SMO算法求解
核心思想比较简单:每次只优化一个参数,其他参数先固定住,仅求当前这个优化参数的极值。
1.选择两个需要更新的参数
λ
i
λ_i
λi和
λ
j
λ_j
λj,固定其他参数。于是就有以下约束:
λ
i
y
i
+
λ
j
y
j
=
c
λ
i
≥
0
,
λ
j
≥
0
λ_i y_i+ λ_j y_j=c λ_i ≥0 ,λ_j ≥0
λiyi+λjyj=cλi≥0,λj≥0
由此可以得出
λ
j
=
(
c
−
λ
i
y
i
)
/
y
j
λ_j=(c- λ_i y_i)/y_j
λj=(c−λiyi)/yj ,也就是说我们可以用
λ
i
λ_i
λi的表达式代替
λ
j
λ_j
λj。这样就相当于把目标问题转化成了仅有一个约束条件的最优化问题,仅有的约束是
λ
i
≥
0
λ_i ≥0
λi≥0。
2. 对于仅有一个约束条件的最优化问题,可以在
λ
i
λ_i
λi上对优化目标求偏导,令导数为零,从而求出变量值
λ
(
i
n
e
w
)
λ_(i_new )
λ(inew),然后根据
λ
(
i
n
e
w
)
λ_(i_new )
λ(inew)求出
λ
(
j
n
e
w
)
λ_(j_new )
λ(jnew)。
3. 多次迭代直至收敛。
通过SMO求得最优解
λ
∗
λ^*
λ∗。
步骤4:求得偏导得到w,求得支持向量的均值得到b
步骤5:构造分类决策函数
w w w和 b b b都求出来,就能构造出最大分割超平面: w T x + b = 0 w^T x+b=0 wTx+b=0
分类决策函数: f ( x ) = s i g n ( w T x + b ) f(x)=sign(w^T x+b) f(x)=sign(wTx+b)
二、使用步骤
1.引入库
import pandas
import time
import numpy as np
import sklearn
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn import svm
from collections import Counter
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
2.获取数据
首先数据集获取,采用glass.csv数据集,下载地址:https://archive.ics.uci.edu/ml/machine-learning-databases/glass/glass.data
glass = pandas.read_csv('glass.csv')
数据集是这个样子:
前9个特征是glass样本的属性,包括:
- RI: Refractive Index
- Na: Sodium
- Mg: Magnesium
- Al: Aluminum
- Si: Silicon
- K: Potassium
- Ca: Calcium
- Ba: Barium
- Fe: Iron
最后一列为glass样本的类型,共有7类:
- Class 1: building windows (float processed)
- Class 2: building windows (non-float processed)
- Class 3: vehicle windows (float processed)
- Class 4: vehicle windows (non-float processed)
- Class 5: containers
- Class 6: tableware
- Class 7: headlamps
3.统计个类型的数量
glass = pandas.read_csv('glass.csv')
X = glass[ ['RI','Na','Mg','Al','Si','K','Ca','Ba','Fe']].values
Y = glass[['Type of glass']].values
Y = np.ravel(Y)
print('统计个数:',dict(Counter(Y)))
统计结果如下:
4.将原始数据划分成训练集和测试集:
#分训练集、测试集
X_train , X_test, y_train, y_test = train_test_split(X,Y,random_state=1,test_size=0.2)
用train_test_split将数据分为训练集和测试集,测试集占总数据的20%(test_size=0.3),random_state是随机数种子(随机数种子:其实就是该组随机数的编号,在需要重复试验的时候,保证得到一组一样的随机数。比如你每次都填1,其他参数一样的情况下你得到的随机数组是一样的。但填0或不填,每次都会不一样。随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个规则:种子不同,产生不同的随机数;种子相同,即使实例不同也产生相同的随机数。)
5.模型搭建
clf = SVC(C=20, kernel='rbf')#核函数选用rbf
clf = fit(X_train,y_train)#训练
print('train accuracy:', clf.score(X_train, y_train))#输出训练集准确率
print('test accuracy:', clf.score(X_test, y_test))#输出测试集准确率
准确率结果如下:
6.不同核函数测试
svm1 = SVC(C=20,kernel='linear')
svm2 = SVC(C=20,kernel='rbf')
svm3 = SVC(C=20,kernel='poly')
svm4 = SVC(C=20,kernel='sigmoid')
t0 = time.time()
svm1.fit(X_train,y_train)
t1 = time.time()
svm2.fit(X_train,y_train)
t2 = time.time()
svm3.fit(X_train,y_train)
t3 = time.time()
svm4.fit(X_train,y_train)
t4 = time.time()
# 准确率对比
svm1_score1 = svm1.score(X_train, y_train)
svm1_score2 = svm1.score(X_test, y_test)
svm2_score1 = svm2.score(X_train, y_train)
svm2_score2 = svm2.score(X_test, y_test)
svm3_score1 = svm3.score(X_train, y_train)
svm3_score2 = svm3.score(X_test, y_test)
svm4_score1 = svm4.score(X_train, y_train)
svm4_score2 = svm4.score(X_test, y_test)
kernels = ['linear', 'rbf', 'poly', 'sigmoid']
time_e = [t1 - t0, t2-t1, t3-t2, t4-t3]
y_score1 = [svm1_score1, svm2_score1, svm3_score1, svm4_score1]
y_score2 = [svm1_score2, svm2_score2, svm3_score2, svm4_score2]
for index in range(4):
print('核函数{:^10s}训练所需时间为:{:.5f}秒, 在训练集的准确率是:{:2.3f}%,在测试集上的准确率是:{:2.3f}%'.format(kernels[index], time_e[index], y_score1[index] * 100, y_score2[index] * 100))
不同核函数测试结果如下:
7.最佳模型的混淆矩阵以及图形化展示
cfm = confusion_matrix(y_test,y_pred)
print("Confusion Matrix: ")
print(cfm)
# 矩阵变形
# 计算矩阵每一行的数据和
row_sums = np.sum(cfm, axis=1)
# 计算矩阵每一行的数据所占该数据总和的比例
err_matrix = cfm / row_sums
# 让矩阵对角线数据更改为0,因为对角线的数据全是模型预测正确的样本数量,而分析混淆矩阵的主要目的是查看模型预测错误的地方
# fill_diagonal(矩阵,m):将对角线的数据全改成m
np.fill_diagonal(err_matrix,0)
# 可视化
plt.matshow(err_matrix,cmap=plt.cm.YlGn)
plt.show()
confusion_matrix函数的使用:官方文档中给出的用法是
sklearn.metrics.confusion_matrix(y_true, y_pred, labels=None, sample_weight=None)
y_true: 是样本真实分类结果,y_pred: 是样本预测分类结果,labels:是所给出的类别,通过这个可对类别进行选择,sample_weight : 样本权重
混淆矩阵运行结果:
可视化运行结果:
完整代码
import pandas
import time
import numpy as np
import sklearn
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn import svm
from collections import Counter
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
######################################################################
glass = pandas.read_csv('glass.csv')
X = glass[ ['RI','Na','Mg','Al','Si','K','Ca','Ba','Fe']].values
Y = glass[['Type of glass']].values
Y = np.ravel(Y)
print('统计个数:',dict(Counter(Y)))
######################################################################
#分训练集、测试集
X_train , X_test, y_train, y_test = train_test_split(X,Y,test_size=0.2)
######################################################################
y_pred = clf.predict(X_test)
#用svm分类
svm1 = SVC(C=20,kernel='linear')
svm2 = SVC(C=20,kernel='rbf')
svm3 = SVC(C=20,kernel='poly')
svm4 = SVC(C=20,kernel='sigmoid')
t0 = time.time()
svm1.fit(X_train,y_train)
t1 = time.time()
svm2.fit(X_train,y_train)
t2 = time.time()
svm3.fit(X_train,y_train)
t3 = time.time()
svm4.fit(X_train,y_train)
t4 = time.time()
# 准确率对比
svm1_score1 = svm1.score(X_train, y_train)
svm1_score2 = svm1.score(X_test, y_test)
svm2_score1 = svm2.score(X_train, y_train)
svm2_score2 = svm2.score(X_test, y_test)
svm3_score1 = svm3.score(X_train, y_train)
svm3_score2 = svm3.score(X_test, y_test)
svm4_score1 = svm4.score(X_train, y_train)
svm4_score2 = svm4.score(X_test, y_test)
# 不同核函数
kernels = ['linear', 'rbf', 'poly', 'sigmoid']
time_e = [t1 - t0, t2-t1, t3-t2, t4-t3]
y_score1 = [svm1_score1, svm2_score1, svm3_score1, svm4_score1]
y_score2 = [svm1_score2, svm2_score2, svm3_score2, svm4_score2]
for index in range(4):
print('核函数{:^10s}训练所需时间为:{:.5f}秒, 在训练集的准确率是:{:2.3f}%,在测试集上的准确率是:{:2.3f}%'.format(kernels[index], time_e[index], y_score1[index] * 100, y_score2[index] * 100))
######################################################################
cfm = confusion_matrix(y_test,y_pred)
print("Confusion Matrix: ")
print(cfm)
# 矩阵变形
# 计算矩阵每一行的数据和
row_sums = np.sum(cfm, axis=1)
# 计算矩阵每一行的数据所占该数据总和的比例
err_matrix = cfm / row_sums
# 让矩阵对角线数据更改为0,因为对角线的数据全是模型预测正确的样本数量,而分析混淆矩阵的主要目的是查看模型预测错误的地方
# fill_diagonal(矩阵,m):将对角线的数据全改成m
np.fill_diagonal(err_matrix,0)
# 可视化
plt.matshow(err_matrix,cmap=plt.cm.YlGn)
plt.show()
总结
以上是关于SVM基础概念以及对于glass数据集处理的实例处理的用法,欢迎大家一起交流学习强调内容
参考
从零开始的机器学习之SVM一个简单的实现案例——鸢尾花分类
混淆矩阵及confusion_matrix函数的使用
数据分析——“玻璃的类型”数据集
支持向量机(SVM)——原理篇
机器学习算法(一)SVM