我正在使用SVR,并使用这个资源。一切都很清晰,使用epsilon强度损失函数(来自图表)。预测带有一个管道,以覆盖大多数训练样本,并使用支持向量来泛化边界。
然后我们有这样的解释。这可以通过引入(非负的)松弛变量来描述,以测量训练样本在-不敏感区之外的偏差。
我理解这个误差,在管道之外,但不知道我们如何在优化中使用它。有人能解释一下吗?
在本地资源中。我试图实现一个非常简单的优化解决方案,不使用库。这是我的损失函数的代码。
import numpy as np# 核函数,默认线性def hypothesis(x, weight, k=None): k = k if k else lambda z : z k_x = np.vectorize(k)(x) return np.dot(k_x, np.transpose(weight)).......import mathdef boundary_loss(x, y, weight, epsilon): prediction = hypothesis(x, weight) scatter = np.absolute( np.transpose(y) - prediction) bound = lambda z: z \ if z >= epsilon else 0 return np.sum(np.vectorize(bound)(scatter))
回答:
首先,让我们看一下目标函数。第一个项,1/2 * w^2
(希望这个网站有LaTeX支持,但这已经足够了)与SVM的边界相关。你链接的文章在我看来并没有很好地解释这一点,并且称这个项为“模型的复杂度”,但也许这不是解释它的最佳方式。最小化这个项会最大化边界(同时仍然很好地表示数据),这是使用SVM进行回归的主要目标。
警告,数学密集型解释:之所以会这样,是因为在最大化边界时,你希望找到“最远”的非异常值点刚好在边界上,并最小化其距离。假设这个最远的点是x_n
。我们希望找到它到平面f(w, x) = 0
的欧几里得距离d
,我将重写为w^T * x + b = 0
(其中w^T
只是权重矩阵的转置,以便我们可以乘以这两个)。为了找到距离,让我们首先标准化平面,使得|w^T * x_n + b| = epsilon
,我们可以这样做,因为w
仍然能够形成所有可能的平面形式w^T * x + b = 0
。然后,让我们注意w
垂直于平面。如果你经常处理平面(特别是在向量微积分中),这是显而易见的,但可以通过选择平面上的两个点x_1
和x_2
来证明,然后注意到w^T * x_1 + b = 0
,以及w^T * x_2 + b = 0
。减去这两个方程我们得到w^T(x_1 - x_2) = 0
。由于x_1 - x_2
只是平面上的任何向量,并且其与w
的点积为0,那么我们知道w
垂直于平面。最后,为了实际计算x_n
与平面之间的距离,我们取x_n'
和平面上某个点x'
形成的向量(然后向量将是x_n - x'
),并将其投影到向量w
上。这样做,我们得到d = |w * (x_n - x') / |w||
,我们可以重写为d = (1 / |w|) * | w^T * x_n - w^T x'|
,然后在内部加减b
得到d = (1 / |w|) * | w^T * x_n + b - w^T * x' - b|
。注意w^T * x_n + b
是epsilon
(来自我们上面的标准化),并且w^T * x' + b
是0,因为这只是我们平面上的一个点。因此,d = epsilon / |w|
。注意,在找到x_n
并使|w^T * x_n + b| = epsilon
的约束下,最大化这个距离是一个困难的优化问题。我们可以做的是将这个优化问题重构为在第一个两项约束下最小化1/2 * w^T * w
,即|y_i - f(x_i, w)| <= epsilon
。你可能会认为我忘记了松弛变量,这是真的,但当只关注这个项并暂时忽略第二项时,我们现在忽略松弛变量,我稍后会把它们带回来。这两个优化是等价的并不明显,但潜在的原因在于判别边界,你可以自由地阅读更多相关内容(这是一个很多数学问题,坦白说我认为这个答案不需要更多)。然后,请注意最小化1/2 * w^T * w
与最小化1/2 * |w|^2
是相同的,这是我们希望得到的结果。数学密集型解释结束
现在,请注意我们希望使边界大,但不要大到包括像你提供的图片中的噪声异常值那样大。
因此,我们引入第二个项。为了将边界调整到合理的大小,引入了松弛变量(我将它们称为p
和p*
,因为我不想每次都输入“psi”)。这些松弛变量将忽略边界内的所有内容,即这些点不会损害目标,并且在回归状态上是“正确”的。然而,边界外的点是异常值,它们对回归的反映不佳,所以我们仅仅因为它们存在而对它们进行惩罚。给出的松弛误差函数相对容易理解,它只是将每个点的松弛误差(p_i + p*_i
)相加,对于i = 1,...,N
,然后乘以一个调制常数C
,它决定了两个项的相对重要性。低C
值意味着我们可以接受有异常值,因此边界会变窄,会产生更多的异常值。高C
值表明我们非常在意不产生松弛,因此边界会变大以容纳这些异常值,但以牺牲整体数据的表示为代价。
关于p
和p*
需要注意几点。首先,请注意它们总是>= 0
。你的图片中的约束显示了这一点,但这在直觉上也有意义,因为松弛总是增加误差,所以它是正的。其次,请注意如果p > 0
,那么p* = 0
,反之亦然,因为异常值只能在边界的一侧。最后,边界内的所有点将具有p
和p*
为0,因为它们在它们所在的位置上是好的,因此不会对损失产生贡献。
请注意,引入松弛变量后,如果有任何异常值,你将不希望使用第一项的条件,即|w^T * x_n + b| = epsilon
,因为x_n
将是这个异常值,你的整个模型都会被搞乱。我们允许的做法是将约束更改为|w^T * x_n + b| = epsilon + (p + p*)
。当翻译成新的优化的约束时,我们得到你附件的图片中的完整约束,即|y_i - f(x_i, w)| <= epsilon + p + p*
。(我在这里将两个方程合二为一,但你可以像图片中那样重写它们,那将是相同的东西)。
希望在覆盖了所有这些内容之后,目标函数和相应的松弛变量的动机对你来说是有意义的。
如果我正确理解了问题,你还想要计算这个目标/损失函数的代码,我认为这并不太难。我还没有测试这个(还没有),但我认为这应该是你想要的。
# 用于计算SVM误差/损失的函数。我假设:# - 'x' 是表示数据点向量的二维数组# - 'y' 是表示每个向量实际给出的值的数组# - 'weights' 是我们为回归调整的权重数组# - 'epsilon' 是表示我们边界宽度的标量。def optimization_objective(x, y, weights, epsilon): # 计算目标的第一项(注意,norm^2 = 点积) margin_term = np.dot(weight, weight) / 2 # 现在计算目标的第二项。首先获取松弛的总和。 slack_sum = 0 for i in range(len(x)): # 对于每个观察值 # 首先找到预期值和观察值之间的绝对距离。 diff = abs(hypothesis(x[i]) - y[i]) # 现在减去epsilon diff -= epsilon # 如果diff仍然大于0,那么它是一个“异常值”,将会有松弛。 slack = max(0, diff) # 将其添加到松弛总和中 slack_sum += slack # 现在我们有了松弛总和,然后乘以C(我任意选取为1) C = 1 slack_term = C * slack_sum # 现在,只需返回两个项的总和,我们就完成了。 return margin_term + slack_term
我已经在我的电脑上用小数据使这个函数正常工作,如果例如数组的结构不同,你可能需要对其进行一些修改以适应你的数据,但这个想法是存在的。此外,我对python不是最熟练的,所以这可能不是最有效的实现,但我的意图是使其易于理解。
现在,请注意,这只是计算误差/损失(你想怎么称呼它都可以)。要实际最小化它需要进入拉格朗日和强烈的二次规划,这是一个更艰巨的任务。有可用的库可以做到这一点,但如果你想像你现在这样不使用库来做,我祝你好运,因为这样做不是一件容易的事。
最后,我想指出,大部分信息我都是从去年我上的ML课程中记的笔记中获取的,我的教授(Dr. Abu-Mostafa)在帮助我学习这些材料方面起了很大作用。这门课的讲座在线上(由同一位教授),与这个主题相关的讲座在这里:这里和这里(虽然我非常偏见地认为你应该观看所有讲座,它们对我帮助很大)。如果你需要澄清任何内容或认为我哪里犯了错误,请留下评论/问题。如果你仍然不理解,我可以尝试编辑我的答案以使其更有意义。希望这对你有帮助!