跳转至

LASSO回归特征选择

一句话概述:LASSO(Least Absolute Shrinkage and Selection Operator)通过L1正则化惩罚将不重要特征的系数压缩到零,实现自动特征选择,是生信中高维数据降维和生物标志物筛选的核心方法。

核心知识点速查表

概念说明
LASSOL1正则化回归(白话:自动帮你挑最重要的变量)
L1正则化惩罚项为系数绝对值之和,会产生稀疏解
Lambda(λ)正则化强度参数,λ越大→选的变量越少
lambda.min使交叉验证误差最小的λ值
lambda.1se在最小误差1个标准误内最简约的λ值(推荐)
交叉验证用CV选择最优λ,避免过拟合
弹性网络L1+L2混合正则化(alpha=0.5时)

一、LASSO回归原理

# 普通线性回归:最小化 RSS = Σ(y - Xβ)²
# LASSO回归:  最小化 RSS + λ × Σ|βj|
#                                    ↑ L1惩罚项

# λ=0: 等同于普通回归(不做选择)
# λ→∞: 所有系数→0(选择太严格)
# 关键:找到合适的λ,在拟合度和简约性之间平衡

# 白话解释:
# 就像公司裁员,λ是裁员力度
# λ小→不怎么裁人,λ大→大规模裁员
# lambda.1se→在完成工作的前提下用最少的人

二、R语言实操

2.1 基本LASSO回归

# === LASSO回归 ===
library(glmnet)

# 准备数据
x <- as.matrix(gene_expr)                    # 特征矩阵(基因表达)
y <- clinical$response                        # 响应变量

# 交叉验证选择最优lambda
set.seed(42)                                  # 设置随机种子(可重复)
cv_fit <- cv.glmnet(
  x, y,
  family = "binomial",                        # 二分类用binomial,连续用gaussian
  alpha = 1,                                  # alpha=1是LASSO(0=Ridge,0-1=弹性网络)
  nfolds = 10,                                # 10折交叉验证
  type.measure = "class"                       # 评估指标:分类错误率
)

# 可视化lambda选择
plot(cv_fit)                                   # ★lambda vs CV误差图
abline(v=log(cv_fit$lambda.min), lty=2, col="red")    # 最优lambda
abline(v=log(cv_fit$lambda.1se), lty=2, col="blue")   # 1se lambda

# 查看最优lambda
cat("lambda.min:", cv_fit$lambda.min, "\n")    # 最小误差的lambda
cat("lambda.1se:", cv_fit$lambda.1se, "\n")    # 1se规则的lambda

# 提取选中的特征
coef_min <- coef(cv_fit, s="lambda.min")       # lambda.min的系数
coef_1se <- coef(cv_fit, s="lambda.1se")       # lambda.1se的系数

# 非零系数的变量就是被选中的
selected_min <- rownames(coef_min)[coef_min[,1] != 0][-1]  # 去掉截距
selected_1se <- rownames(coef_1se)[coef_1se[,1] != 0][-1]

cat("lambda.min选中:", length(selected_min), "个变量\n")
cat("lambda.1se选中:", length(selected_1se), "个变量\n")
print(selected_1se)                             # 打印选中的变量

2.2 LASSO-Cox生存分析

# === LASSO-Cox(生信最常用场景) ===
library(glmnet)
library(survival)

# 准备数据
x <- as.matrix(gene_expr)                      # 基因表达矩阵
y <- Surv(clinical$time, clinical$status)       # 生存数据

# 交叉验证
set.seed(42)
cv_cox <- cv.glmnet(
  x, y,
  family = "cox",                               # Cox模型
  alpha = 1,                                    # LASSO
  nfolds = 10                                   # 10折CV
)

plot(cv_cox)                                     # lambda选择图

# 提取选中的基因
coef_cox <- coef(cv_cox, s="lambda.1se")
selected_genes <- rownames(coef_cox)[coef_cox[,1] != 0]
cat("选中", length(selected_genes), "个预后基因:\n")
print(selected_genes)

# 计算风险评分
risk_score <- predict(cv_cox, newx=x, s="lambda.1se", type="response")

# 分高低风险组
median_risk <- median(risk_score)
risk_group <- ifelse(risk_score > median_risk, "High", "Low")

# KM生存曲线
library(survminer)
fit <- survfit(y ~ risk_group)
ggsurvplot(fit, pval=TRUE, risk.table=TRUE,
           palette=c("red", "blue"),
           legend.labs=c("High Risk", "Low Risk"))

2.3 系数路径图

# === 系数路径图(展示特征选择过程) ===
fit <- glmnet(x, y, family="cox", alpha=1)     # 拟合不同lambda

# 绘制系数路径
plot(fit, xvar="lambda", label=TRUE)            # 按lambda
plot(fit, xvar="norm", label=TRUE)              # 按L1范数
plot(fit, xvar="dev", label=TRUE)               # 按偏差比

# ★添加交叉验证结果
abline(v=log(cv_cox$lambda.1se), lty=2)         # 1se位置
title("LASSO Coefficient Path")

三、Python实操

# === Python LASSO回归 ===
from sklearn.linear_model import LassoCV, LogisticRegressionCV
from sklearn.preprocessing import StandardScaler
import numpy as np
import pandas as pd

# 准备数据
X = gene_expr_df.values                         # 特征矩阵
y = clinical_df['response'].values              # 目标变量

# 标准化(LASSO对量纲敏感!)
scaler = StandardScaler()                        # 创建标准化器
X_scaled = scaler.fit_transform(X)               # 标准化

# LASSO回归(连续变量)
lasso_cv = LassoCV(
    alphas=None,                                  # 自动选择alpha范围
    cv=10,                                        # 10折交叉验证
    random_state=42,                              # 随机种子
    max_iter=10000                                # 最大迭代次数
)
lasso_cv.fit(X_scaled, y)                         # 拟合

# 查看结果
print(f"最优alpha: {lasso_cv.alpha_:.4f}")
selected = np.where(lasso_cv.coef_ != 0)[0]      # 非零系数的索引
print(f"选中 {len(selected)} 个特征")
for idx in selected:
    print(f"  {gene_expr_df.columns[idx]}: coef={lasso_cv.coef_[idx]:.4f}")

# LASSO分类(二分类)
logistic_lasso = LogisticRegressionCV(
    penalty='l1',                                 # L1正则化=LASSO
    solver='saga',                                # 支持L1的求解器
    cv=10,                                        # 10折CV
    random_state=42,
    max_iter=10000
)
logistic_lasso.fit(X_scaled, y)                   # 拟合

四、面试高频考点

Q1: LASSO和Ridge回归的区别?

LASSO(L1)Ridge(L2)
惩罚项Σ|βj|Σβj²
特征选择能(系数可压缩到0)不能(系数只能接近0)
适用场景需要特征选择时特征都重要但要防过拟合时
多重共线性从相关变量中只选一个保留所有变量但缩小系数
白话裁员(直接砍人)降薪(每人少发点但不裁)

Q2: lambda.min和lambda.1se选哪个?

  • lambda.min:误差最小,选的变量多,预测最准但可能过拟合
  • lambda.1se:在误差可接受范围内最简约,变量更少,更泛化
  • 推荐:一般用lambda.1se(生信文章常用,模型更简约更有临床价值)

Q3: LASSO在生信中的典型应用?

  1. 从数千个基因中筛选预后标志物(LASSO-Cox)
  2. 构建诊断模型(LASSO-Logistic)
  3. 多组学数据降维
  4. 免疫基因评分模型构建

常见报错与解决

报错原因解决方案
All coefficients are zerolambda太大用lambda.min而非lambda.1se
Convergence warning未收敛增加max.iter或标准化数据
选的变量太多lambda太小用lambda.1se
不同随机种子结果不同LASSO对数据敏感多次运行取交集

速查表

# === LASSO速查(R语言) ===
library(glmnet)

# 分类
cv_fit <- cv.glmnet(x, y, family="binomial", alpha=1, nfolds=10)

# 回归
cv_fit <- cv.glmnet(x, y, family="gaussian", alpha=1, nfolds=10)

# 生存
cv_fit <- cv.glmnet(x, Surv(time,status), family="cox", alpha=1)

# 提取结果
plot(cv_fit)                                     # lambda选择图
coef(cv_fit, s="lambda.1se")                     # 系数
selected <- rownames(coef)[coef[,1]!=0]          # 选中的变量

# 弹性网络: alpha=0.5 (LASSO和Ridge的混合)