实现带Lasso修正的LARS算法遇阻:步骤3理解与编程求助
我太懂卡在LARS-Lasso步骤3的滋味了——这部分是算法里最绕的核心,既要处理新变量的加入,还要兼顾Lasso的系数压缩(也就是剔除活跃集变量),很容易搞混。先帮你拆解清楚步骤3的逻辑,再结合你已经写好的标准化代码补全实现。
先回顾你已完成的基础代码(补全了y的初始化方便后续演示)
set.seed(19875) n <- 10 p <- 5 real_p <- 5 x <- matrix(rnorm(n*p), nrow=n, ncol=p) # 步骤1:标准化预测变量为均值0、单位范数 x <- x - matrix(apply(x,2,mean), ncol=ncol(x), nrow=nrow(x), byrow=T) x <- x / matrix(apply(x,2,sd), ncol=ncol(x), nrow=nrow(x), byrow=T) # 补全y的生成(模拟真实数据) beta_true <- rnorm(real_p) y <- x[,1:real_p] %*% beta_true + rnorm(n, sd=0.1)
拆解LARS-Lasso步骤3的核心逻辑
LARS-Lasso的步骤3本质是每一步迭代要做两件事:找下一个要加入的变量,同时检查当前活跃集里有没有变量的系数会被压缩到0(这就是Lasso的修正,区别于普通LARS只加不减)。具体分4个子步骤:
- 计算当前残差与所有变量的相关性(因为x已标准化,相关性=协方差)
- 找到当前最大的绝对相关性,确定候选加入的变量
- 计算系数更新的方向向量
- 计算两个关键步长:
gamma1:把新变量加入活跃集的步长gamma2:把活跃集中某个变量系数压到0的步长- 取两者的最小值作为最终步长,若
gamma2更小,就先剔除变量,否则加入新变量
步骤3的完整代码实现
# 初始化算法所需变量 beta <- rep(0, p) # 系数向量,初始全为0 r <- y # 初始残差=真实y(因为beta初始为0) active_set <- c() # 活跃集,初始为空 corr <- t(x) %*% r # 初始相关性:残差与每个变量的内积 # 迭代终止条件:活跃集包含所有变量,或相关性小到可忽略 while(length(active_set) < p && max(abs(corr)) > 1e-6) { # 1. 找到当前最大的绝对相关性,确定候选变量 max_corr <- max(abs(corr)) candidate_vars <- which(abs(corr) == max_corr) if(length(active_set) == 0) { # 第一次迭代:直接加入第一个候选变量 active_set <- candidate_vars[1] } else { # 2. 计算活跃集的方向向量u_A x_A <- x[, active_set] inv_xtx_A <- solve(t(x_A) %*% x_A) # 活跃集变量的X'X逆矩阵 sign_corr_A <- sign(corr[active_set]) u_A <- inv_xtx_A %*% sign_corr_A # 活跃集内的方向向量 u <- rep(0, p) u[active_set] <- u_A # 全局方向向量(非活跃集为0) # 3. 计算gamma1:加入新变量的步长 non_active <- setdiff(1:p, active_set) if(length(non_active) > 0) { numerator_gamma1 <- max_corr - corr[non_active] denominator_gamma1 <- (t(x[, non_active]) %*% x_A %*% u_A) - sign(corr[non_active]) gamma1 <- numerator_gamma1 / denominator_gamma1 gamma1 <- gamma1[gamma1 > 1e-8] # 只保留正步长(避免数值误差) gamma1 <- ifelse(length(gamma1) > 0, min(gamma1), Inf) } else { gamma1 <- Inf # 无新变量可加 } # 4. 计算gamma2:剔除活跃集变量的步长(Lasso修正核心) gamma2 <- -beta[active_set] / u_A gamma2 <- gamma2[gamma2 > 1e-8] gamma2 <- ifelse(length(gamma2) > 0, min(gamma2), Inf) # 5. 取最小步长更新系数和残差 gamma <- min(gamma1, gamma2) beta <- beta + gamma * u r <- r - gamma * x %*% u # 6. 更新活跃集:优先处理剔除(如果gamma2更小) if(gamma == gamma2) { drop_idx <- which(-beta[active_set]/u_A == gamma2)[1] active_set <- active_set[-drop_idx] } else { # 加入新变量 new_var <- non_active[which(numerator_gamma1/denominator_gamma1 == gamma1)[1]] active_set <- c(active_set, new_var) } } # 更新相关性,打印中间过程(可选) corr <- t(x) %*% r cat("当前活跃集:", active_set, "\n") cat("当前系数:", round(beta, 4), "\n\n") }
关键细节说明
- Lasso修正的核心:
gamma2的计算是为了提前发现哪些活跃变量的系数会在更新后被压缩到0,这一步是LARS-Lasso区别于普通LARS的关键——普通LARS只会不断加入变量,不会剔除。 - 数值阈值
1e-8:用来避免浮点数误差导致的极小/负步长,保证算法稳定。 - 活跃集的动态变化:每一步都可能加入新变量或剔除旧变量,直到所有变量都被处理或残差足够小。
内容的提问来源于stack exchange,提问作者Ville
相关产品推荐
相关产品推荐

