试答系列:“西瓜书”-周志华《机器学习》习题试答
知识梳理
本章关于支持向量机的公式较多,存在有多种形式,容易产生混淆,为此,在这里将涉及支持向量机的各种公式进行总结,绘制了一张关系图,其中未涉及支持向量回归和和核方法部分内容:
6.1 试证明样本空间中任意点x到超平面(w,b)的距离为式(6.2)。
证明:设点在超平面的垂足为,那么也在超平面内,应满足(1): ;另外和的连线应该垂直于超平面,或者说平行于法向量,应满足(2): , k为一常数。结合(1)和(2)消去可以得到,而到超平面的距离可以表示为:,此即(6.2)式。
6.2 试使用LIBSVM,在西瓜数据集3.0α上分别用线性核和高斯核训练一个SVM,并比较其支持向量的差别。
答:本题采用python中的sklearn库下的svm.SVC类进行支持向量机算法的训练,该分类器正是基于LIBSVM (详细编程代码附后)。svm.SVC的主要参数有惩罚系数C和核函数kernel。C越大,越倾向于“硬间隔”,也就是所有样本都被正确分类。kernel='linear'表示采用线性核,kernel='rbf'表示采用高斯核。下面是在西瓜数据集3.0α上采用不同设置下的训练结果:
观察以上计算结果,可见:
- 所有被错误划分或者处在临界带(|f(x)|≤1)内的数据点属于支持向量。
- 当惩罚系数C较小时,线性核和高斯核所得到决策边界基本一样,所得到的支持向量完全相同。此时支持向量较多,15个,在临界带内有大量支持向量;当惩罚系数C较大时。对于高斯核,所有样本均被正确分类,支持向量由临界线上的6个数据点组成。对于线性核,由于数据线性不可分,无法实现所有样本都正确分类,此时仍然有12个支持向量。
6.3 选择两个UCI数据集,分别用线性核和高斯核训练一个SVM,并与BP神经网络和C4.5决策树进行实验比较。
答:本题编程代码附后。这里的多个机器学习算法均通过scikit-learn中相应的类来实现,载入方式为:from sklearn import svm,neural_network,tree,其中决策树使用CART算法的优化版本,CART与C4.5非常相似,但是它支持数值目标变量(回归)。
在每个机器学习算法中有多个超参数调节,这里采用scikit-learn中的网格搜索方法GridSearchCV来确定超参数,对于每一组超参数,计算它的k折交叉验证平均得分(预测精度),将得分最大时的超参数作为最佳参数。
首先在iris数据集上进行实验,结果如下表所示:
算法 | SVM-linear | SVM-rbf | BP | Tree |
---|---|---|---|---|
最佳超参数 | C=0.695 | C=1.624 | alpha=0~1 hidden_layer_sizes=(15,2~7)(5,) |
max_depth=4~14,None min_samples_leaf=1 |
最佳得分 | 0.987 | 0.987 | 0.98~0.987 | 0.967 |
对应拟合时间 | 0.2~0.8ms | 0.6~1ms | 26~83ms | 0.2~0.4ms |
其中在网格搜索确定超参数过程中的k折交叉验证设置cv=5。针对各个超参数的搜索,对于SVM,考察了惩罚因子C;对于BP,考察了惩罚因子alpha和隐藏层单元数hidden_layer_sizes;对于Tree,考察了最大深度max_depth和叶结点最少样本数min_samples_leaf。
iris数据各个维度幅值相当,未作归一化处理,经实验发现,归一化后,差别不大,最佳得分还略有下降。
对比各个算法,可以得出如下结论:
- 最佳超参数方面,SVM结果很稳定,BP和Tree所得结果不稳定,这说明SVM对于超参数C较敏感,BP和Tree对于相应的超参数不太敏感。
- 最佳得分方面,各个算法最佳预测精度相当,Tree算法略差一点点。
- 拟合时间方面,BP算法较耗时,SVM和Tree算法较快。
然后又选取了数据集breast_cancer进行实验,该数据集有十个特征,彼此间幅值差别较大,需要进行归一化预处理。下面分别是在归一化和未归一化下计算的结果:
数据集breast_cancer上的计算结果(数据归一化)
算法 | SVM-linear | SVM-rbf | BP | Tree |
---|---|---|---|---|
最佳超参数 | C=0.0545 | C=3.793 | alpha=1E-8~1E-4 hidden_layer_sizes=(15,2~7) |
max_depth=4~12,None min_samples_leaf=5 |
最佳得分 | 0.977ms | 0.979ms | 0.974~0.977ms | 0.938~0.942ms |
对应拟合时间 | 2~3ms | 4~5ms | 22~24ms | 5~8ms |
数据集breast_cancer上的计算结果(不作归一化)
算法 | SVM-linear | SVM-rbf | BP | Tree |
---|---|---|---|---|
最佳超参数 | - | C=0.01 | alpha=1 hidden_layer_sizes=(15~25, 2~7) |
max_depth=8~16 min_samples_leaf=5 |
最佳得分 | - | 0.627 | 0.94~0.96 | 0.942 |
对应拟合时间 | ∞ms | 26ms | 90~170ms | 6~7ms |
可见:归一化对于各个算法的最佳超参数,最佳得分,对应拟合时间均产生影响。归一化之后,拟合更快些。在最佳得分方面,归一化对于BP和Tree影响较小,对SVM影响较大。
不同算法在超参数稳定性,最佳预测精度,拟合耗时方面的对比结论与前面iris数据集中得到的结论相同。
6.4 试讨论线性判别分析与线性核支持向量机在何种条件下等价。
答:
6.5 试述高斯核SVM与RBF神经网络之间的联系。
答:参考本章最后面的“休息一会儿”中的侧注所述:
SVM的确与神经网络有密切联系:若将隐层神经元数设置为训练样本数,且每个训练样本对应一个神经元中心,则以高斯径向基函数为激活函数的RBF网络恰与高斯核SVM的预测函数相同。
高斯核SVM训练得到的预测函数为:
而RBF网络的输出结果为:
假设已经训练完成一个高斯核SVM,我们可以构造出一个输出结果与SVM结果完全相同的RBF神经网络:神经元数目为m+1,令其中前m个神经元的中心,激活函数中参数,权重,最后一个神经元的中心设为任意值,参数,权重。
尽管如此,若是单独训练RBF神经网络,得到的结果未必如此。
6.6 试析SVM对噪声敏感的原因。
答:
6.7 试给出式(6.52)的完整KKT条件。
答:根据“附录B.1-拉格朗日乘子法”,每一个不等式约束将得到3个KKT条件,比如,对于约束g(x)≤0,对应的KKT条件为:g(x)≤0,γ≥0,γg(x)=0。式(6.52)中仅涉及KKT条件中的等式KKT条件部分,亦即γg(x)=0这部分。若写出原问题(6.45)的完整KKT条件,由于原问题中有4m个不等式,因此将得到3×4m=12m个KKT条件,它们是:
将上式中的等式部分与教材中的式(6.52)对比,有几个不同的地方:
- (6.52)式中利用了关系,因此将“”写为“”。
- (6.52)式中多了两个等式:和,这而与KKT条件无关,是通过其他分析额外得到的条件。对于某个样本点,它的位置可能有几种情况:间隔带以内,上边界或以外,下边界或以外。
若在间隔带以内,则;
若刚好在上边界上,则;
若刚好在下边界上,则;
若在上边界以外,则;
若在下边界以外,则。
由于一个点不可能同时出现在上边界(或以外)和下边界(或以外),因此有上面两个等式成立。
6.8 以西瓜数据集3.0α的“密度”为输入,“含糖率”为输出,试使用LIBSVM训练一个SVR。
答:这里仍然采用scikit-learn中相应的类来实现,载入方式为 from sklearn.svm import SVR。
SVR有两个关键的参数:C和epsilon,分别代表惩罚因子和开始计算惩罚损失的阈值,C越大,epsilon越小,拟合结果与原数据越接近。
本题详细编程代码附后。
对于西瓜数据集3.0α的计算结果如下:
核函数分别选取为linear和rbf,线性核linear所得结果为线性函数,高斯核rbf所得结果为非线性函数。
由于西瓜数据集3.0α的密度和含糖量这两个特征之间相关性较低,所以计算效果看起来不明显。可以人为生成一些数据来观察SVR计算结果:
6.9 试使用核技巧推广对率回归,产生“核对率回归”。
答:参照教材中对于“核线性判别分析(KLDA)”的做法。
对于数据集,假设通过一个映射将映射到高维特征空间,为核函数,然后在中执行对率回归模型训练,以求得:
其中参数,通过最小化目标函数来估计:
根据表示定理,参数和预测函数可以写为:
令为核矩阵,矩阵元,列向量,向量。
于是目标函数将变为估计参数和:
然后,便可以采取对率回归中同样的方法:梯度下降法或者牛顿法来求解最优参数。
6.10 试设计一个能显著减少SVM中支持向量的数目而不显著降低泛化性能的方法。
答:
附:编程代码
习题6.2(Python)
# -*- coding: utf-8 -*-
"""
Created on Sat Nov 16 21:24:06 2019
@author: lsly
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
# 西瓜3.0α 样本数据
X=np.array([[0.697,0.46],[0.774,0.376],[0.634,0.264],[0.608,0.318],[0.556,0.215],
[0.403,0.237],[0.481,0.149],[0.437,0.211],[0.666,0.091],[0.243,0.267],
[0.245,0.057],[0.343,0.099],[0.639,0.161],[0.657,0.198],[0.36,0.37],
[0.593,0.042],[0.719,0.103]])
Y=np.array([1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0])
# 支持向量机训练并将结果可视化
for c in [1E1,1E3,1E5]:
for k in ['linear','rbf']:
# 训练并提取支持向量
clf=svm.SVC(C=c,kernel=k) #C为惩罚系数,kernel是所用的核函数
clf.fit(X,Y) #fit函数对训练样本进行训练
Xsp=X[clf.support_,:] #参数support_为支持向量的索引号
Ysp=Y[clf.support_] #Xsp和Ysp分别为支持向量的特征和类标记
# 画图
plt.figure()
plt.title('C=%.E, kernel=%s'%(c,k))
# 画出正负样本(+和-符号表示)
plt.scatter(X[Y==1,0],X[Y==1,1],marker='+',c='r',s=100)
plt.scatter(X[Y==0,0],X[Y==0,1],marker='_',c='b',s=100)
# 标出支持向量(画圈)(用marker='o'和c=''结合起来表示空心圆)
plt.scatter(Xsp[Ysp==1,0],Xsp[Ysp==1,1],marker='o',c='',s=100,edgecolors='r')
plt.scatter(Xsp[Ysp==0,0],Xsp[Ysp==0,1],marker='o',c='',s=100,edgecolors='b')
# 画决策线
x1,x2=np.meshgrid(np.linspace(min(X[:,0]),max(X[:,0]),100),
np.linspace(min(X[:,1]),max(X[:,1]),100))
z=clf.decision_function(np.c_[x1.reshape(-1),x2.reshape(-1)]).reshape(100,100)
# ↑ decision_function返回决策函数值,也即wx+b的值,
# 另外还有函数predict可以返回类标记,比如1,0
plt.contour(x1,x2,z,colors='k',linestyles=['--','-','--'],levels=[-1,0,1])
习题6.3(Python)
# -*- coding: utf-8 -*-
"""
Created on Mon Dec 23 16:43:25 2019
@author: MS
"""
from sklearn.model_selection import GridSearchCV
from sklearn import svm,neural_network,tree
from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
'''
=============载入数据集iris=======================
'''
X,y=datasets.load_iris(return_X_y=True)
normalize=0 #设为1则作归一化,否则不归一化
if normalize==1:
X=StandardScaler().fit(X).transform(X) #对数据进行归一化
#===========1.svm-linear=======
C=np.logspace(-2,5,20)
clf1=GridSearchCV(svm.SVC(kernel='linear'),{'C':C},cv=5,return_train_score=True)
clf1.fit(X,y)
print('svm-linear-最佳参数:',clf1.best_params_,
'最佳得分:',clf1.best_score_,
'对应拟合耗时:',clf1.cv_results_['mean_fit_time'][clf1.best_index_])
#===========2.svm-rbf==========
clf2=GridSearchCV(svm.SVC(kernel='rbf'),{'C':C},cv=5,return_train_score=True)
clf2.fit(X,y)
print('svm-rbf-最佳参数:',clf2.best_params_,
'最佳得分:',clf2.best_score_,
'对应拟合耗时:',clf2.cv_results_['mean_fit_time'][clf2.best_index_])
#===========3.BP神经网络================
alpha=[0]+list(np.logspace(-8,1,10))
nodes=[(i,) for i in range(5,30,10)]+\
[(i,j) for i in range(5,30,10) for j in range(2,10,5)]
clf3=GridSearchCV(neural_network.MLPClassifier(solver='lbfgs'),
{'alpha':alpha,'hidden_layer_sizes':nodes},cv=5,return_train_score=True)
clf3.fit(X,y)
print('BP-最佳参数:',clf3.best_params_,
'最佳得分:',clf3.best_score_,
'对应拟合耗时:',clf3.cv_results_['mean_fit_time'][clf3.best_index_])
#===========4.决策树================
deep=[None,2,4,8,10,12,14,16]
minleaf=[5,4,3,2,1]
clf4=GridSearchCV(tree.DecisionTreeClassifier(),
{'max_depth':deep,'min_samples_leaf':minleaf},cv=5,return_train_score=True)
clf4.fit(X,y)
print('Tree-最佳参数:',clf4.best_params_,
'最佳得分:',clf4.best_score_,
'对应拟合耗时:',clf4.cv_results_['mean_fit_time'][clf4.best_index_])
习题6.8(Python)
# -*- coding: utf-8 -*-
"""
Created on Wed Dec 25 15:53:59 2019
@author: MS
"""
from sklearn import svm
import numpy as np
import matplotlib.pyplot as plt
#========西瓜数据集3.0α============
X=np.array([[0.697,0.46],[0.774,0.376],[0.634,0.264],[0.608,0.318],[0.556,0.215],
[0.403,0.237],[0.481,0.149],[0.437,0.211],[0.666,0.091],[0.243,0.267],
[0.245,0.057],[0.343,0.099],[0.639,0.161],[0.657,0.198],[0.36,0.37],
[0.593,0.042],[0.719,0.103]])
Y=np.array([1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0])
#西瓜数据集3.0α中的密度和含糖量数据
x=X[:,0].reshape(-1, 1)
y=X[:,1]
#人为生成数据(若要计算西瓜数据集,可以将下面两行注释掉)
x=np.linspace(0,1,20).reshape(-1, 1)
y=np.sin(x*3.14)+np.random.randn(20,1)*0.1
#分别采用线性核和高斯核来拟合
reg1=svm.SVR(C=30,kernel='linear',epsilon=0.01).fit(x,y)
reg2=svm.SVR(C=30,kernel='rbf',epsilon=0.01).fit(x,y)
#画图
px=np.linspace(min(x),max(x),20).reshape(-1, 1)
plt.scatter(x,y,c='r',label='data')
plt.plot(px,reg1.predict(px),'g--',label='linear')
plt.plot(px,reg2.predict(px),'b--',label='rbf')
plt.legend()
plt.title('SVR result\n C=30,epsilon=0.01')
plt.xlabel('rou')
plt.ylabel('Sugar ratio')