您的实现的算法部分是正确的。问题出在
- 循环结构在
GD
是不正确的;这for
循环是多余的,变量缺乏正确的初始化。
- 使用固定的梯度下降的简单实现
alpha
很危险。通常建议这样alpha
应该选择足够小的值,以希望我们总是向下搜索目标函数。然而,这种情况在实践中很少见。例如,多小才足够?如果它很小,那么收敛速度就是一个问题;如果它很小,那么收敛速度就是一个问题。但如果它很大,我们可能会陷入“之字形”搜索路径甚至发散!
Here is 梯度下降的稳健版本,用于估计线性回归。改进来自于步长减半策略,以避免“之字形”或背离。请参阅代码中的注释。在这种策略下,使用大的alpha
. 保证了收敛性。
# theta: initial guess on regression coef
# alpha: initial step scaling factor
GD <- function(X, y, theta, alpha) {
cost_histo <- numeric(0)
theta_histo <- numeric(0)
# an arbitrary initial gradient, to pass the initial while() check
delta <- rep(1, ncol(X))
# MSE at initial theta
old.cost <- COST(theta, X, y)
# main iteration loop
while (max(abs(delta)) > 1e-7) {
# gradient
error <- X %*% theta - y
delta <- crossprod(X, error) / length(y)
# proposal step
trial.theta <- theta - alpha * c(delta)
trial.cost <- COST(trial.theta, X, y)
# step halving to avoid divergence
while (trial.cost >= old.cost) {
trial.theta <- (theta + trial.theta) / 2
trial.cost <- COST(trial.theta, X, y)
}
# accept proposal
cost_histo <- c(cost_histo, trial.cost)
theta_histo <- c(theta_histo, trial.theta)
# update old.cost and theta
old.cost <- trial.cost
theta <- trial.theta
}
list(cost_histo, theta_histo = matrix(theta_histo, nrow = ncol(X)))
}
返回时,
- 的长度
cost_histo
告诉您已经进行了多少次迭代(不包括步骤减半);
- 的每一列
theta_histo
gives theta
每次迭代。
事实上,步长减半大大加快了收敛速度。如果您使用更快的计算方法,您可以获得更高的效率COST
。 (对于大型数据集最有用。请参阅https://stackoverflow.com/a/40228894/4891738 https://stackoverflow.com/a/40228894/4891738)
COST<-function(theta,X, y) {
c(crossprod(X %*% theta - y)) /(2*length(y))
}
现在,让我们考虑一下它在您的示例中的实现X
, y
.
oo <- GD(X, y, c(0,0), 5)
经过 107 次迭代后收敛。我们可以查看MSE的踪迹:
plot(oo[[1]])
请注意,在前几步,MSE 下降得非常快,但随后几乎持平。这揭示了梯度下降算法的根本缺点:随着我们越来越接近最小值,收敛速度越来越慢。
现在,我们提取最终的估计系数:
oo[[2]][, 107]
我们还可以将其与 QR 分解的直接估计进行比较:
.lm.fit(X, y)$coef
他们非常接近。