RBF核支持向量机的参数¶
本案例展示了参数gamma和C对径向基函数核函数(RBF)下的支持向量机的影响。
直观地说,gamma参数定义了“单个训练样本对整个模型的影响程度”,gamma值很低表示“影响深远”,gamma值高却表示“影响不大”。gamma参数可以看作是模型选出的那些支持向量的影响半径的倒数。
C参数在“训练样本的正确分类”与“决策编辑最大化”之间做出权衡。对于较大的C值,如果模型的决策函数本身可以更好地正确分类所有训练点,则可以接受较小的边际。较低的C将鼓励更大的边际,因此会简化决策功能,降低训练的准确性。换句话说,C在SVM中充当正则化参数。
第一张图是一个简单分类问题的决策函数的可视化,这个决策函数被给定了一系列的参数值,且该分类问题仅仅涉及2个特征和2个可能的目标类别(二分类)。请注意,对于具有更多特征或目标类别的问题,类似的图像是无法绘制的。
第二幅图是一个热力图,该图中分类器的交叉验证准确率是参数C和gamma的函数(译者注:即颜色深浅代表准确率,横纵坐标是C和gamma)。在此案例中,为了说明我们的目的,我们探索了一个相对较大的网格。实际上,从到的对数网格通常就足够了。如果最佳参数位于网格的边界上,则可以在后续搜索中沿该方向扩展。
请注意,热力图具有特殊的颜色条,颜色条的中点上的数值接近性能最佳的模型的得分值,依赖于这张图我们可以在眨眼之间就能轻松分辨模型的优劣。
(译者注:一种典型的颜色条是 深色-浅色-深色 的颜色结构,本文中"颜色条的中点"是指浅色的部分。我们可以通过代码来调整颜色条的颜色结构,使显眼的颜色与我们希望捕获的模型结果匹配。本案例中将浅色的部分与最佳模型得分匹配,这样就可以一眼在热力图中看出显眼的浅色部分所代表的最佳模型。)
模型的行为对gamma参数非常敏感。如果gamma太大,则支持向量的影响区域的半径就只能包括支持向量本身,这种情况下,使用C进行的正则化也无法防止过拟合。
当gamma非常小时,该模型太受约束,无法捕获数据的复杂性或“形状”。任何选定的支持向量的影响区域都会包括整个训练集。所得模型的行为将类似于带有一组超平面的线性模型,该超平面分割任意两个类别不同的数据点的高密度中心。
对于不大也不小、处于中间的gamma值,如第二张图所示,我们可以在C和gamma的对角线上找到好的模型。通过增加正确分类每个点的重要性(设置较大的C值),可以提高平滑模型(较小的gamma值)的复杂度,所以对角线上能够获得提高了性能的良好的模型。
最后,我们还可以观察到,对于某些不大不小的gamma值,当C变得非常大时,我们得到的模型性能一致:此时,通过强制追求更大的边际来进行正则化就没有必要了。RBF核函数的半径就可以充当良好的结构调整器。不过在实践中,更有趣的事情依然是:用较低的C值简化决策函数,以便支持使用较少内存且预测速度更快的模型。
我们还应注意,分数的微小差异是由交叉验证过程的随机分裂造成的。可以通过增加CV迭代次数n_splits来消除那些虚假的变化,但这会浪费计算时间。增加C_range和gamma_range步骤的值数量将增加超参数热力图的分辨率。
输出:
输出:
The best parameters are {'C': 1.0, 'gamma': 0.1} with a score of 0.97
输入:
print(__doc__)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
from sklearn.model_selection import StratifiedShuffleSplit
from sklearn.model_selection import GridSearchCV
# 一个很实用的函数,可将颜色图的中点移动到感兴趣的值附近。
class MidpointNormalize(Normalize):
def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
self.midpoint = midpoint
Normalize.__init__(self, vmin, vmax, clip)
def __call__(self, value, clip=None):
x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1]
return np.ma.masked_array(np.interp(value, x, y))
# #############################################################################
# 导入并准备数据集
#
# 为网格搜索准备数据集
iris = load_iris()
X = iris.data
y = iris.target
# 用于决策功能可视化的数据集:我们仅将前两个特征保留在X中,并对数据集进行子采样,以仅保留2个类,并使其成为二分类问题。
X_2d = X[:, :2]
X_2d = X_2d[y > 0]
y_2d = y[y > 0]
y_2d -= 1
# 缩放数据以进行SVM训练通常是一个好主意。在此示例中,我们在缩放数据时有点"作弊",我们缩放了全部数据,而不是将转换器在训练集上拟合,然后缩放到测试集上。
scaler = StandardScaler()
X = scaler.fit_transform(X)
X_2d = scaler.fit_transform(X_2d)
# #############################################################################
# 训练分类器
#
# 对于初始搜索,以10为底的对数网格通常会很有帮助。使用2为基数,可以实现更精细的调整,但就算成本要高得多。
C_range = np.logspace(-2, 10, 13)
gamma_range = np.logspace(-9, 3, 13)
param_grid = dict(gamma=gamma_range, C=C_range)
cv = StratifiedShuffleSplit(n_splits=5, test_size=0.2, random_state=42)
grid = GridSearchCV(SVC(), param_grid=param_grid, cv=cv)
grid.fit(X, y)
print("The best parameters are %s with a score of %0.2f"
% (grid.best_params_, grid.best_score_))
# 现在,我们需要为二维数据下的所有参数拟合一个分类器(我们在这里使用一小部分参数,因为训练需要一些时间)
C_2d_range = [1e-2, 1, 1e2]
gamma_2d_range = [1e-1, 1, 1e1]
classifiers = []
for C in C_2d_range:
for gamma in gamma_2d_range:
clf = SVC(C=C, gamma=gamma)
clf.fit(X_2d, y_2d)
classifiers.append((C, gamma, clf))
# #############################################################################
# 可视化
#
# 绘制参数效果
plt.figure(figsize=(8, 6))
xx, yy = np.meshgrid(np.linspace(-3, 3, 200), np.linspace(-3, 3, 200))
for (k, (C, gamma, clf)) in enumerate(classifiers):
# 在网格中评估决策函数的值
Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 为这些参数可视化决策函数的值
plt.subplot(len(C_2d_range), len(gamma_2d_range), k + 1)
plt.title("gamma=10^%d, C=10^%d" % (np.log10(gamma), np.log10(C)),
size='medium')
# 可视化决策函数上的参数效果
plt.pcolormesh(xx, yy, -Z, cmap=plt.cm.RdBu)
plt.scatter(X_2d[:, 0], X_2d[:, 1], c=y_2d, cmap=plt.cm.RdBu_r,
edgecolors='k')
plt.xticks(())
plt.yticks(())
plt.axis('tight')
scores = grid.cv_results_['mean_test_score'].reshape(len(C_range),
len(gamma_range))
# 绘制验证准确率与gamma和C的函数关系的热力图
#
# 分数在热色图被编码为颜色,从深红色到亮黄色不等。由于最有趣的分数都位于0.92至0.97范围内,因此我们使用自定义规范化器将颜色条中点设置为0.92,以便更轻松地可视化有趣范围内分数值的微小变化,而不会残酷地将所有低分值变为相同的颜色。
plt.figure(figsize=(8, 6))
plt.subplots_adjust(left=.2, right=0.95, bottom=0.15, top=0.95)
plt.imshow(scores, interpolation='nearest', cmap=plt.cm.hot,
norm=MidpointNormalize(vmin=0.2, midpoint=0.92))
plt.xlabel('gamma')
plt.ylabel('C')
plt.colorbar()
plt.xticks(np.arange(len(gamma_range)), gamma_range, rotation=45)
plt.yticks(np.arange(len(C_range)), C_range)
plt.title('Validation accuracy')
plt.show()
脚本的总运行时间:(0分钟5.498秒)