跳转至

LASSO回归变量选择

一句话概述:LASSO回归通过L1惩罚项自动将不重要变量的系数压缩为零,是高维数据(基因数>>样本数)中筛选关键变量建立预测模型的金标准方法。

核心知识点速览

概念白话解释
正则化给模型加"惩罚",防止它太贪心(过拟合)
LASSO(L1)惩罚=系数绝对值之和,能把无关变量的系数压到0(自动选变量)
Ridge(L2)惩罚=系数平方和,系数会变小但不会变成0(不做变量选择)
ElasticNetLASSO+Ridge的混合,兼顾两者优点
lambda(λ)惩罚力度参数,λ越大筛掉越多变量
lambda.min交叉验证误差最小的λ值(保留更多变量)
lambda.1se误差在最小值1个标准误内的最大λ(更简约的模型)
交叉验证(CV)把数据分N份,轮流做训练和测试,找最优λ
alpha控制L1/L2比例:alpha=1是LASSO,alpha=0是Ridge
glmnetR中做LASSO/Ridge/ElasticNet的标准包(2026版)

一、三种正则化方法对比

假设你在装修房子,有100个装饰品可选:

Ridge(L2):每个装饰品都摆上,但都缩小了
  → 所有变量保留,系数都变小

LASSO(L1):只选20个摆上,其他80个扔掉
  → 自动选变量,不重要的系数=0

ElasticNet:选30个摆上,但有些缩小
  → 介于两者之间,适合相关变量多的情况
特征Ridge (L2)LASSO (L1)ElasticNet
alpha参数010-1之间
变量选择不做能做能做
共线性处理差(随机选一个)
适用场景所有变量都有用需要选变量变量间高度相关
生信常用度较少最常用次常用

二、glmnet实操

2.1 安装和准备

# 安装glmnet(2026最新版)
install.packages("glmnet")  # 安装
library(glmnet)              # 加载

# 准备数据(以基因表达预测疾病为例)
# x: 预测变量矩阵(行=样本,列=基因)
# y: 响应变量(0/1的疾病状态)

# 读取数据
expr_data <- read.csv("expression_matrix.csv", row.names = 1)  # 表达矩阵

# 转为矩阵格式(glmnet要求)
x <- as.matrix(expr_data[, -1])  # 预测变量(去掉第一列如果是标签)
y <- expr_data[, 1]               # 响应变量

# 或者用model.matrix(处理分类变量更方便)
# x <- model.matrix(y ~ ., data = expr_data)[, -1]

2.2 LASSO回归(分类问题)

# ===== 步骤1:交叉验证找最优lambda =====
set.seed(42)  # 设置随机种子(保证可重复)

cv_fit <- cv.glmnet(
  x = x,                    # 预测变量矩阵
  y = y,                    # 响应变量
  family = "binomial",      # 二分类(逻辑回归);连续变量用"gaussian"
  alpha = 1,                # alpha=1 是LASSO
  nfolds = 10,              # 10折交叉验证
  type.measure = "auc"      # 用AUC作为评价指标;也可用"deviance"
)

# 画交叉验证图
plot(cv_fit)  # X轴=log(lambda),Y轴=AUC,上方数字=非零系数个数
# 左虚线 = lambda.min(最优性能)
# 右虚线 = lambda.1se(简约模型)

# 查看两个关键lambda值
cat("lambda.min:", cv_fit$lambda.min, "\n")  # 最优lambda
cat("lambda.1se:", cv_fit$lambda.1se, "\n")  # 简约lambda

# ===== 步骤2:提取系数(选出的变量) =====
# 用lambda.1se(更简约,推荐发文章)
coef_1se <- coef(cv_fit, s = "lambda.1se")  # 提取系数
# 用lambda.min(更多变量,性能更好)
coef_min <- coef(cv_fit, s = "lambda.min")

# 找出非零系数的变量(即被选中的基因)
selected_genes <- rownames(coef_1se)[which(coef_1se != 0)]  # 非零系数
selected_genes <- selected_genes[selected_genes != "(Intercept)"]  # 去掉截距项
cat("选出的基因数:", length(selected_genes), "\n")
print(selected_genes)  # 打印选出的基因

# ===== 步骤3:用选出的变量重新建模 =====
# 方法1:直接用glmnet的预测
pred_prob <- predict(cv_fit, newx = x, s = "lambda.1se", type = "response")
# type="response"返回概率值(0-1之间)

# 方法2:用选出的变量建logistic回归
selected_data <- data.frame(y = y, x[, selected_genes])
final_model <- glm(y ~ ., data = selected_data, family = binomial)
summary(final_model)  # 查看系数和p值

2.3 LASSO-Cox回归(生存分析)

# 在生存分析中用LASSO选变量(非常常见)
library(survival)  # 加载survival包

# 准备Surv对象
y_surv <- Surv(time = clinical$time, event = clinical$status)  # 生存时间和事件

# 交叉验证找最优lambda
cv_cox <- cv.glmnet(
  x = x,                    # 基因表达矩阵
  y = y_surv,               # Surv对象
  family = "cox",           # Cox回归
  alpha = 1,                # LASSO
  nfolds = 10,              # 10折CV
  type.measure = "C"        # C-index作为评价指标
)

# 画图和提取系数
plot(cv_cox)
coef_cox <- coef(cv_cox, s = "lambda.min")  # Cox常用lambda.min
selected_cox <- rownames(coef_cox)[which(coef_cox != 0)]
cat("选出的基因数:", length(selected_cox), "\n")

# 计算风险评分
risk_score <- predict(cv_cox, newx = x, s = "lambda.min", type = "link")
# risk_score越高,风险越大

2.4 ElasticNet

# 当基因间高度共线性时,用ElasticNet
# 先找最优alpha
alpha_values <- seq(0.1, 0.9, by = 0.1)  # 测试多个alpha值
cv_results <- list()

for (a in alpha_values) {
  set.seed(42)
  cv_results[[as.character(a)]] <- cv.glmnet(
    x = x, y = y, family = "binomial",
    alpha = a, nfolds = 10, type.measure = "auc"
  )
}

# 找AUC最高的alpha
best_auc <- sapply(cv_results, function(fit) max(fit$cvm))
best_alpha <- alpha_values[which.max(best_auc)]
cat("最优alpha:", best_alpha, "\n")

# 用最优alpha重新拟合
cv_enet <- cv.glmnet(x, y, family = "binomial", alpha = best_alpha, nfolds = 10)

三、结果可视化

3.1 系数路径图

# 画系数随lambda变化的路径
fit <- glmnet(x, y, family = "binomial", alpha = 1)  # 拟合模型
plot(fit, xvar = "lambda", label = TRUE)  # 系数路径图
# X轴=log(lambda),Y轴=系数值
# 每条线是一个基因,lambda增大时系数逐渐归零
abline(v = log(cv_fit$lambda.1se), lty = 2)  # 标记选择的lambda

3.2 变量重要性图

# 画选出变量的系数条形图
library(ggplot2)

coef_df <- data.frame(
  Gene = selected_genes,
  Coefficient = as.numeric(coef_1se[selected_genes, ])
)
coef_df <- coef_df[order(abs(coef_df$Coefficient), decreasing = TRUE), ]

ggplot(coef_df, aes(x = reorder(Gene, abs(Coefficient)), y = Coefficient)) +
  geom_bar(stat = "identity", fill = ifelse(coef_df$Coefficient > 0, "red", "blue")) +
  coord_flip() +                    # 横向条形图
  labs(x = "Gene", y = "LASSO Coefficient",
       title = "Selected Genes by LASSO") +
  theme_minimal()

四、lambda.min vs lambda.1se

lambda.min: 交叉验证误差最小的λ
  优点:模型性能最好
  缺点:可能选太多变量(过拟合风险)
  适用:探索性分析、预测为主

lambda.1se: 误差在最小值±1SE范围内的最大λ
  优点:模型更简洁,泛化更好
  缺点:可能丢失一些有用变量
  适用:发文章、需要简洁模型

生信论文推荐:
  - 发现生物标志物 → lambda.1se(选变量更严格)
  - 构建预测模型 → lambda.min(保留更多信息)
  - 实际用时画两个λ的结果对比,讨论差异

常见报错与解决

报错信息原因解决方案
x should be a matrix输入不是矩阵as.matrix()转换
number of observations differx和y行数不同检查样本数是否一致
NA/NaN/Inf in x数据有缺失或异常值预处理移除NA/Inf
All used predictors have zero variance某些基因表达完全不变预先过滤零方差基因
option grouped=FALSE分类变量组内样本太少增加样本或合并类别
Error in predict: newx has wrong dimensions预测数据列数不对确保新数据有相同的列

速查表

# LASSO快速流程
as.matrix(x) → cv.glmnet(x, y, alpha=1) → plot() → coef(s="lambda.1se")

# 三种回归
alpha = 1:   LASSO(选变量)
alpha = 0:   Ridge(不选变量)
alpha = 0.5: ElasticNet(折中)

# family参数
"gaussian":  连续变量(线性回归)
"binomial":  二分类(逻辑回归)
"cox":       生存分析(Cox回归)
"multinomial": 多分类
"poisson":   计数数据

# 两个关键lambda
lambda.min: 性能最好
lambda.1se: 模型最简洁(推荐)

# 常用评价指标(type.measure)
"deviance": 偏差(默认)
"auc":      AUC(二分类推荐)
"C":        C-index(Cox推荐)
"mse":      均方误差(连续变量)

面试高频问题

Q1:LASSO为什么能做变量选择?

:LASSO的L1惩罚项是系数绝对值之和。从几何角度看,L1惩罚的约束区域是菱形(有尖角),而最优解往往落在尖角上,尖角处某些系数恰好为零。L2惩罚的约束区域是圆形(没有尖角),所以系数不会变成零。简单说:LASSO的"菱形约束"让系数更容易触碰到坐标轴,从而被压到零。

Q2:lambda.min和lambda.1se怎么选?

:lambda.min是交叉验证误差最小的λ,模型复杂但性能最好。lambda.1se是误差在最小值一个标准误范围内的最大λ,选出更少的变量但模型更简洁、泛化更好。发文章通常用lambda.1se(更不容易过拟合),构建预测模型优先性能则用lambda.min。建议两个都报告并讨论差异。

Q3:LASSO和ElasticNet什么时候用哪个?

:当预测变量之间相关性不高时用LASSO(alpha=1);当变量高度共线性时用ElasticNet(alpha=0.5左右)。因为LASSO面对一组相关变量时倾向于只选一个(其他压为零),而ElasticNet会选一组。基因表达数据中共表达很常见,所以ElasticNet在生信中越来越受青睐。

Q4:LASSO在生信中最常用的场景是什么?

:①生物标志物筛选——从上千个基因中选出少数关键基因;②构建预后模型——LASSO-Cox回归选基因+计算风险评分;③诊断模型——LASSO-logistic回归建立疾病预测模型;④与列线图结合——选出的变量用于构建临床预测模型。LASSO+Cox+列线图是临床生信论文的"三件套"。

Q5:LASSO回归有什么局限性?

:①变量选择不稳定——样本略有变化,选出的变量可能不同(可用bootstrap+LASSO提高稳定性);②对共线性敏感——高相关变量中只随机选一个(用ElasticNet解决);③不适合因果推断——选出的变量不一定有因果关系,只是预测相关;④需要足够样本——样本太少时交叉验证不可靠。