了解支持向量回归 (SVR)
Understanding Support Vector Regression (SVR)
我正在使用 SVR,并使用这个 resource。一切都非常清晰,具有 epsilon 密集型损失函数(如图)。预测带有管,以覆盖大多数训练样本,并使用支持向量概括边界。
然后我们就有了这个解释。 This can be described by introducing (non-negative) slack variables , to measure the deviation of training samples outside -insensitive zone.
我理解这个错误,管外,但不知道我们如何在优化中使用它。 有人可以解释一下吗?
在本地来源。我正在尝试实现非常简单的优化解决方案,无需库。这就是我的损失函数。
import numpy as np
# Kernel func, linear by default
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 math
def 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))
首先,让我们看一下objective函数。第一项,1/2 * w^2
(希望这个网站有 LaTeX 支持,但这就足够了)与 SVM 的边缘相关。在我看来,您链接的文章并没有很好地解释这一点,并将这个术语称为描述 "the model's complexity",但这也许不是解释它的最佳方式。最小化这一项 最大化 边缘(同时仍然很好地代表数据),这是使用 SVM 进行回归的主要目标。
警告,Math Heavy 解释: 出现这种情况的原因是在最大化边距时,您想找到 "farthest" 非离群点边距并最小化它的距离。让这个最远点为x_n
。我们想找到它与平面 f(w, x) = 0
的欧氏距离 d
,我将其重写为 w^T * x + b = 0
(其中 w^T
只是权重矩阵的转置,以便我们可以两者相乘)。为了找到距离,让我们首先对平面进行归一化,使得 |w^T * x_n + b| = epsilon
,我们可以像 w
那样进行 WLOG,仍然能够形成 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
。你可能认为我忘记了松弛变量,这是真的,但是当只关注这一项而忽略第二项时,我们暂时忽略松弛变量,稍后我会把它们带回来。原因 t这两个优化是等效的并不明显,但根本原因在于歧视边界,您可以自由阅读更多相关内容(坦率地说,我认为这个答案不需要更多的数学知识)。然后,请注意最小化 1/2 * w^T * w
与最小化 1/2 * |w|^2
相同,这是我们希望的结果。 重数学结束
现在,请注意,我们想让边距变大,但又不会大到包含像您提供的图片中那样的嘈杂异常值。
因此,我们引入第二个术语。为了将边距降低到合理的大小,引入了松弛变量(我将它们称为 p
和 p*
,因为我不想每次都输入 "psi")。这些松弛变量将忽略边际中的所有内容,即那些不损害 objective 的点和 "correct" 就其回归状态而言的点。然而,边界外的点是离群点,它们不能很好地反映回归,所以我们仅仅因为它们存在而对其进行惩罚。那里给出的松弛误差函数比较好理解,就是把i = 1,...,N
的每个点(p_i + p*_i
)的松弛误差相加,然后乘以一个调制常数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*
。 (我在这里把两个方程合二为一,但是你可以把它们改写成图片那样,那就是一回事了)。
希望在涵盖所有这些内容后,objective 函数的动机和相应的松弛变量对您有意义。
如果我没看错的话,你还需要代码来计算这个 objective/loss 函数,我认为这还不错。我还没有测试过这个(还),但我想这应该是你想要的。
# Function for calculating the error/loss for a SVM. I assume that:
# - 'x' is 2d array representing the vectors of the data points
# - 'y' is an array representing the values each vector actually gives
# - 'weights' is an array of weights that we tune for the regression
# - 'epsilon' is a scalar representing the breadth of our margin.
def optimization_objective(x, y, weights, epsilon):
# Calculates first term of objective (note that norm^2 = dot product)
margin_term = np.dot(weight, weight) / 2
# Now calculate second term of objective. First get the sum of slacks.
slack_sum = 0
for i in range(len(x)): # For each observation
# First find the absolute distance between expected and observed.
diff = abs(hypothesis(x[i]) - y[i])
# Now subtract epsilon
diff -= epsilon
# If diff is still more than 0, then it is an 'outlier' and will have slack.
slack = max(0, diff)
# Add it to the slack sum
slack_sum += slack
# Now we have the slack_sum, so then multiply by C (I picked this as 1 aribtrarily)
C = 1
slack_term = C * slack_sum
# Now, simply return the sum of the two terms, and we are done.
return margin_term + slack_term
我在我的计算机上用小数据运行了这个函数,如果数组的结构不同,你可能需要稍微改变它才能处理你的数据,但这个想法是存在的。另外,我不是最精通 python,所以这可能不是最有效的实现,但我的目的是让它易于理解。
现在,请注意,这只是计算 error/loss(随便你怎么称呼它)。要真正最小化它需要进入拉格朗日量和密集的二次规划,这是一项更艰巨的任务。有一些图书馆可以用来做这件事,但如果你想像你这样做一样免费地做这个图书馆,我祝你好运,因为这样做不是在公园里散步。
最后,我想指出的是,大部分信息都是从我去年在 ML class 中做的笔记中获得的,教授(Abu-Mostafa 博士)提供了很大的帮助让我学习 material。这个 class 的讲座在线(由同一个教授),与这个主题相关的讲座是 here and here(尽管我非常偏向于你应该观看所有的讲座,他们是一个很大的帮助).如果您需要清理任何内容或者您认为我在某处犯了错误,请留下 comment/question。如果您仍然不明白,我可以尝试编辑我的答案以使其更有意义。希望这对您有所帮助!
我正在使用 SVR,并使用这个 resource。一切都非常清晰,具有 epsilon 密集型损失函数(如图)。预测带有管,以覆盖大多数训练样本,并使用支持向量概括边界。
然后我们就有了这个解释。 This can be described by introducing (non-negative) slack variables , to measure the deviation of training samples outside -insensitive zone.
我理解这个错误,管外,但不知道我们如何在优化中使用它。 有人可以解释一下吗?
在本地来源。我正在尝试实现非常简单的优化解决方案,无需库。这就是我的损失函数。
import numpy as np
# Kernel func, linear by default
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 math
def 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))
首先,让我们看一下objective函数。第一项,1/2 * w^2
(希望这个网站有 LaTeX 支持,但这就足够了)与 SVM 的边缘相关。在我看来,您链接的文章并没有很好地解释这一点,并将这个术语称为描述 "the model's complexity",但这也许不是解释它的最佳方式。最小化这一项 最大化 边缘(同时仍然很好地代表数据),这是使用 SVM 进行回归的主要目标。
警告,Math Heavy 解释: 出现这种情况的原因是在最大化边距时,您想找到 "farthest" 非离群点边距并最小化它的距离。让这个最远点为x_n
。我们想找到它与平面 f(w, x) = 0
的欧氏距离 d
,我将其重写为 w^T * x + b = 0
(其中 w^T
只是权重矩阵的转置,以便我们可以两者相乘)。为了找到距离,让我们首先对平面进行归一化,使得 |w^T * x_n + b| = epsilon
,我们可以像 w
那样进行 WLOG,仍然能够形成 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
。你可能认为我忘记了松弛变量,这是真的,但是当只关注这一项而忽略第二项时,我们暂时忽略松弛变量,稍后我会把它们带回来。原因 t这两个优化是等效的并不明显,但根本原因在于歧视边界,您可以自由阅读更多相关内容(坦率地说,我认为这个答案不需要更多的数学知识)。然后,请注意最小化 1/2 * w^T * w
与最小化 1/2 * |w|^2
相同,这是我们希望的结果。 重数学结束
现在,请注意,我们想让边距变大,但又不会大到包含像您提供的图片中那样的嘈杂异常值。
因此,我们引入第二个术语。为了将边距降低到合理的大小,引入了松弛变量(我将它们称为 p
和 p*
,因为我不想每次都输入 "psi")。这些松弛变量将忽略边际中的所有内容,即那些不损害 objective 的点和 "correct" 就其回归状态而言的点。然而,边界外的点是离群点,它们不能很好地反映回归,所以我们仅仅因为它们存在而对其进行惩罚。那里给出的松弛误差函数比较好理解,就是把i = 1,...,N
的每个点(p_i + p*_i
)的松弛误差相加,然后乘以一个调制常数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*
。 (我在这里把两个方程合二为一,但是你可以把它们改写成图片那样,那就是一回事了)。
希望在涵盖所有这些内容后,objective 函数的动机和相应的松弛变量对您有意义。
如果我没看错的话,你还需要代码来计算这个 objective/loss 函数,我认为这还不错。我还没有测试过这个(还),但我想这应该是你想要的。
# Function for calculating the error/loss for a SVM. I assume that:
# - 'x' is 2d array representing the vectors of the data points
# - 'y' is an array representing the values each vector actually gives
# - 'weights' is an array of weights that we tune for the regression
# - 'epsilon' is a scalar representing the breadth of our margin.
def optimization_objective(x, y, weights, epsilon):
# Calculates first term of objective (note that norm^2 = dot product)
margin_term = np.dot(weight, weight) / 2
# Now calculate second term of objective. First get the sum of slacks.
slack_sum = 0
for i in range(len(x)): # For each observation
# First find the absolute distance between expected and observed.
diff = abs(hypothesis(x[i]) - y[i])
# Now subtract epsilon
diff -= epsilon
# If diff is still more than 0, then it is an 'outlier' and will have slack.
slack = max(0, diff)
# Add it to the slack sum
slack_sum += slack
# Now we have the slack_sum, so then multiply by C (I picked this as 1 aribtrarily)
C = 1
slack_term = C * slack_sum
# Now, simply return the sum of the two terms, and we are done.
return margin_term + slack_term
我在我的计算机上用小数据运行了这个函数,如果数组的结构不同,你可能需要稍微改变它才能处理你的数据,但这个想法是存在的。另外,我不是最精通 python,所以这可能不是最有效的实现,但我的目的是让它易于理解。
现在,请注意,这只是计算 error/loss(随便你怎么称呼它)。要真正最小化它需要进入拉格朗日量和密集的二次规划,这是一项更艰巨的任务。有一些图书馆可以用来做这件事,但如果你想像你这样做一样免费地做这个图书馆,我祝你好运,因为这样做不是在公园里散步。
最后,我想指出的是,大部分信息都是从我去年在 ML class 中做的笔记中获得的,教授(Abu-Mostafa 博士)提供了很大的帮助让我学习 material。这个 class 的讲座在线(由同一个教授),与这个主题相关的讲座是 here and here(尽管我非常偏向于你应该观看所有的讲座,他们是一个很大的帮助).如果您需要清理任何内容或者您认为我在某处犯了错误,请留下 comment/question。如果您仍然不明白,我可以尝试编辑我的答案以使其更有意义。希望这对您有所帮助!