支持向量机(SVM)学习策略与优化算法

支持向量机(SVM)是一种广泛应用于分类和回归问题的机器学习算法。它的核心思想是在特征空间中寻找一个最优的超平面,使得不同类别的数据点之间的间隔最大化。本文将详细介绍SVM的学习策略,包括优化问题、优化算法以及分类方法,并提供相应的Python代码实现。

1. SVM的基本概念

在介绍SVM的学习策略之前,先来了解一些基本概念。

功能间隔(Function Margin):对于给定的训练集T和超平面(w, b),超平面(w, b)与样本(xi, yi)之间的功能间隔定义为:

功能间隔反映了分类结果的置信度。如果超参数(w, b)成比例变化,例如将(w, b)变为(2w, 2b),虽然超平面没有变化,但功能间隔会扩大两倍。因此,对w施加一些约束,使其规范化,例如||w|| = 1。然后,间隔被称为几何间隔(Geometric Margin)。

几何间隔(Geometric Margin):对于给定的训练集T和超平面(w, b),超平面(w, b)与样本(xi, yi)之间的几何间隔定义为:

几何间隔与功能间隔之间的关系是:

2. SVM模型

SVM模型由优化问题、优化算法和分类三部分组成。

当数据集是线性可分的时,支持向量是距离超平面最近的样本。此时,SVM的优化问题可以表示为:

当数据集不是线性可分的时,一些训练集中的样本不满足功能间隔大于等于1的条件。为了解决这个问题,为每个样本(xi, yi)添加一个松弛变量。此时,约束条件为:

同时,为每个松弛变量添加一个代价。目标函数为:

其中C是惩罚系数。当C较大时,对误分类的惩罚会增加,反之则会减少。此时,SVM的优化问题可以表示为:

传统的凸二次规划算法可以解决SVM的优化问题,但当训练集较大时,算法的运行时间会很长。为了解决这个问题,Platt提出了一种高效的算法——序列最小优化(SMO)。

SMO是一种启发式算法。当所有变量满足KKT条件时,优化问题就得到了解决。否则,选择两个变量,固定其他变量,并构建一个包含这两个变量的凸二次规划问题。

在SMO的外循环中,选择违反KKT条件的alpha,即alpha_i。首先,搜索满足条件的样本。如果所有样本都满足条件,则搜索整个数据集。在内循环中,首先搜索使alpha2最大化的alpha1。如果选择的alpha1减少得不够多,首先搜索边界上的样本。如果选择的alpha1减少得足够多,则停止搜索。否则,搜索整个数据集。如果在搜索整个数据集后找到了一个可行的alpha2,将选择一个新的alpha1。

在每次迭代中,通过以下方式更新b:

使用优化后的参数进行预测,预测结果由下式给出:

对于测试集中的每个样本i,使用核变换将支持向量与测试数据进行转换,然后计算概率。如果概率大于0,则预测结果为1,否则为-1。

SVM是一种比之前介绍的算法更复杂的算法。本文简化了搜索过程,使其更容易理解。最后,让将SVM与Sklearn中的SVM进行比较,检测性能如下所示:

检测性能略逊于Sklearn的SVM,这是因为SVM中的SMO比Sklearn的SVM中的SMO更简单。

def kernelTransformation(self, data, sample, kernel): sample_num, feature_dim = np.shape(data) K = np.zeros([sample_num]) if kernel == "linear": K = np.dot(data, sample.T) elif kernel == "poly": K = (np.dot(data, sample.T) + self.c) ** self.n elif kernel == "sigmoid": K = np.tanh(self.g * np.dot(data, sample.T) + self.c) elif kernel == "rbf": for i in range(sample_num): delta = data[i, :] - sample K[i] = np.dot(delta, delta.T) K = np.exp(-self.g * K) else: raise NameError('Unrecognized kernel function') return K def innerLoop(self, i): Ei = self.calculateErrors(i) if self.checkKKT(i): j, Ej = self.selectAplha2(i, Ei) old_alpha1 = self.alphas[i] old_alpha2 = self.alphas[j] if self.train_label[i] != self.train_label[j]: L = max(0, old_alpha2 - old_alpha1) H = min(self.C, self.C + old_alpha2 - old_alpha1) else: L = max(0, old_alpha2 + old_alpha1 - self.C) H = min(self.C, old_alpha2 + old_alpha2) if L == H: return 0 eta = K11 + K22 - 2 * K12 K11 = self.K[i, i] K12 = self.K[i, j] K21 = self.K[j, i] K22 = self.K[j, j] eta = K11 + K22 - 2 * K12 if eta <= 0: return 0 self.alphas[j] = old_alpha2 + self.train_label[j] * (Ei - Ej) / eta self.alphas[j] = self.updateAlpha2(self.alphas[j], L, H) new_alphas2 = self.alphas[j] self.upadateError(j) new_alphas1 = old_alpha1 + self.train_label[i] * self.train_label[j] * (old_alpha2 - new_alphas2) self.alphas[i] = new_alphas1 self.upadateError(i) b1 = -Ei - self.train_label[i] * K11 * (old_alpha1 - self.alphas[i]) - self.train_label[j] * K21 * (old_alpha2 - self.alphas[j]) + self.b b2 = -Ej - self.train_label[i] * K12 * (old_alpha1 - self.alphas[i]) - self.train_label[j] * K22 * (old_alpha2 - self.alphas[j]) + self.b if (self.alphas[i] > 0) and (self.alphas[i] < self.C): self.b = b1 elif (self.alphas[j] > 0) and (self.alphas[j] < self.C): self.b = b2 else: self.b = (b1 + b2) / 2.0 return 1 else: return 0
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485