32·监督学习-回归进阶

正则化:Ridge、Lasso 与 ElasticNet

regularizationregression

第 32 课:正则化:Ridge、Lasso 与 ElasticNet

学习目标

  1. 理解正则化的目的及其在防止过拟合中的作用
  2. 掌握 Ridge (L2)、Lasso (L1) 和 ElasticNet 正则化的基本原理
  3. 学会使用 Scikit-learn 实现三种正则化回归模型
  4. 理解正则化强度参数 (alpha) 对模型的影响
  5. 能够根据实际问题选择合适的正则化方法

核心概念

为什么需要正则化?

在上一课的多项式回归中,我们看到当模型过于复杂时(例如,高次多项式),模型可能会完美拟合训练数据,但在新数据上表现很差——这就是过拟合。正则化通过给损失函数增加一个惩罚项,限制模型参数的大小,从而控制模型的复杂度,防止过拟合。

三种正则化方法

1. Ridge 回归 (L2 正则化)

  • 在普通线性回归的损失函数上增加系数平方和的惩罚
  • 损失函数:$L = \sum(y_{\text{真实}} - y_{\text{预测}})^2 + \alpha \sum \beta_j^2$
  • 效果:将系数压缩到接近0,但不会变成0
  • 适用场景:当特征间存在多重共线性,或我们希望保留所有特征时

2. Lasso 回归 (L1 正则化)

  • 在损失函数上增加系数绝对值之和的惩罚
  • 损失函数:$L = \sum(y_{\text{真实}} - y_{\text{预测}})^2 + \alpha \sum |\beta_j|$
  • 效果:能够将某些系数压缩到完全为0,实现特征选择
  • 适用场景:当特征很多且希望进行特征选择时

3. ElasticNet 回归

  • 结合了 L1 和 L2 正则化
  • 损失函数:$L = \sum(y_{\text{真实}} - y_{\text{预测}})^2 + \alpha \rho \sum |\beta_j| + \alpha(1-\rho) \sum \beta_j^2$
  • 效果:平衡特征选择和系数收缩
  • 适用场景:特征间高度相关且需要特征选择时

关键参数

  • alpha (λ):正则化强度。alpha 越大,正则化越强,模型越简单
  • l1_ratio (ρ):ElasticNet 专用,控制 L1 和 L2 的比例

代码示例

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import LinearRegression, Ridge, Lasso, ElasticNet
from sklearn.metrics import mean_squared_error, r2_score

# 1. 生成非线性数据
np.random.seed(42)
X = np.sort(5 * np.random.rand(100, 1), axis=0)
y = np.sin(X).ravel() + 0.1 * np.random.randn(100)

# 添加噪声,创建过拟合场景
y[::5] += 3 * (0.5 - np.random.rand(20))

# 2. 创建多项式特征 (模拟过拟合场景)
degree = 10
poly = PolynomialFeatures(degree=degree, include_bias=False)
X_poly = poly.fit_transform(X)

# 3. 划分训练测试集
X_train, X_test, y_train, y_test = train_test_split(
    X_poly, y, test_size=0.3, random_state=42
)

# 4. 标准化特征(正则化前必须做!)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 5. 训练不同模型
models = {
    "Linear Regression": LinearRegression(),
    "Ridge (α=0.1)": Ridge(alpha=0.1),
    "Lasso (α=0.1)": Lasso(alpha=0.1, max_iter=10000),
    "ElasticNet (α=0.1, ρ=0.5)": ElasticNet(alpha=0.1, l1_ratio=0.5, max_iter=10000)
}

results = {}
for name, model in models.items():
    # 训练模型
    model.fit(X_train_scaled, y_train)
    
    # 预测
    y_train_pred = model.predict(X_train_scaled)
    y_test_pred = model.predict(X_test_scaled)
    
    # 计算性能指标
    train_mse = mean_squared_error(y_train, y_train_pred)
    test_mse = mean_squared_error(y_test, y_test_pred)
    train_r2 = r2_score(y_train, y_train_pred)
    test_r2 = r2_score(y_test, y_test_pred)
    
    results[name] = {
        "train_mse": train_mse,
        "test_mse": test_mse,
        "train_r2": train_r2,
        "test_r2": test_r2,
        "coefficients": model.coef_
    }
    
    print(f"{name}:")
    print(f"  训练 MSE: {train_mse:.4f}, R²: {train_r2:.4f}")
    print(f"  测试 MSE: {test_mse:.4f}, R²: {test_r2:.4f}")
    print(f"  非零系数数量: {np.sum(model.coef_ != 0)}/{len(model.coef_)}\n")

# 6. 可视化系数大小比较
plt.figure(figsize=(12, 6))

# 创建子图
plt.subplot(1, 2, 1)
for name, result in results.items():
    if name != "Linear Regression":
        plt.plot(result["coefficients"], 'o-', label=name, alpha=0.7)
plt.axhline(y=0, color='k', linestyle='--', alpha=0.3)
plt.xlabel("特征索引")
plt.ylabel("系数大小")
plt.title("不同正则化方法的系数比较")
plt.legend()
plt.grid(True, alpha=0.3)

# 可视化不同alpha值的影响
plt.subplot(1, 2, 2)
alphas = [0.001, 0.01, 0.1, 1, 10]
ridge_coefs = []
lasso_coefs = []

for alpha in alphas:
    # Ridge
    ridge = Ridge(alpha=alpha)
    ridge.fit(X_train_scaled, y_train)
    ridge_coefs.append(ridge.coef_)
    
    # Lasso
    lasso = Lasso(alpha=alpha, max_iter=10000)
    lasso.fit(X_train_scaled, y_train)
    lasso_coefs.append(lasso.coef_)

# 绘制系数路径
plt.plot(alphas, [np.sum(np.abs(coef)) for coef in ridge_coefs], 'b-o', label='Ridge (L1范数)')
plt.plot(alphas, [np.sum(np.abs(coef)) for coef in lasso_coefs], 'r-s', label='Lasso (L1范数)')
plt.xscale('log')
plt.xlabel("正则化强度 α (对数尺度)")
plt.ylabel("系数绝对值之和")
plt.title("正则化强度对系数的影响")
plt.legend()
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# 7. 使用网格搜索找到最佳参数
from sklearn.model_selection import GridSearchCV

# 对ElasticNet进行网格搜索
param_grid = {
    'alpha': [0.001, 0.01, 0.1, 1, 10],
    'l1_ratio': [0.1, 0.3, 0.5, 0.7, 0.9]
}

elasticnet = ElasticNet(max_iter=10000, random_state=42)
grid_search = GridSearchCV(
    elasticnet, 
    param_grid, 
    cv=5, 
    scoring='neg_mean_squared_error',
    return_train_score=True
)

grid_search.fit(X_train_scaled, y_train)

print("最佳参数:", grid_search.best_params_)
print("最佳验证集 MSE:", -grid_search.best_score_)

# 使用最佳参数重新训练
best_model = grid_search.best_estimator_
y_test_pred = best_model.predict(X_test_scaled)
test_mse = mean_squared_error(y_test, y_test_pred)
test_r2 = r2_score(y_test, y_test_pred)

print(f"最佳模型在测试集上的表现 - MSE: {test_mse:.4f}, R²: {test_r2:.4f}")

实践练习

练习 1:基础 Ridge 回归

使用给定的波士顿房价数据(或使用 sklearn.datasets.load_boston),完成以下任务:

  1. 将数据集分为训练集和测试集
  2. 对数据进行标准化
  3. 使用 Ridge 回归(alpha=1.0)训练模型
  4. 计算并输出训练集和测试集的 MSE 和 R² 分数

预期输出示例

训练集 MSE: 21.34, R²: 0.74
测试集 MSE: 24.56, R²: 0.71

练习 2:比较不同正则化方法

扩展练习 1,添加 Lasso 和 ElasticNet 回归:

  1. 训练 Lasso (alpha=0.5) 和 ElasticNet (alpha=0.5, l1_ratio=0.5)
  2. 比较三个模型的性能
  3. 分析哪些特征被 Lasso 完全排除(系数为0)

预期输出

  • 三个模型的性能对比表
  • Lasso 选择的特征列表

练习 3:超参数调优(高级)

使用交叉验证为 ElasticNet 找到最佳参数:

  1. 设置参数网格:alpha ∈ [0.01, 0.1, 1, 10],l1_ratio ∈ [0.1, 0.5, 0.9]
  2. 使用 GridSearchCV 进行 5 折交叉验证
  3. 输出最佳参数组合及其在测试集上的性能

预期输出

最佳参数: {'alpha': 0.1, 'l1_ratio': 0.9}
最佳测试 MSE: 18.92

常见错误

  1. 忘记标准化数据

    • 正则化对特征的尺度敏感,必须先进行标准化
    • 错误示例:直接使用原始数据训练 Ridge/Lasso
  2. 对正则化参数理解错误

    • alpha 越大,正则化越强,模型越简单
    • 错误:认为 alpha 越大模型越复杂
  3. 误解 Lasso 的特征选择

    • Lasso 会将系数压缩到0,但并非总是有用的特征选择
    • 错误:将所有系数为0的特征视为"无用特征"
  4. 忽略 ElasticNet 的 l1_ratio 参数

    • l1_ratio=0 时等同于 Ridge,l1_ratio=1 时等同于 Lasso
    • 错误:固定使用 l1_ratio=0.5,不尝试其他值
  5. 过度依赖默认参数

    • 不同的 alpha 值可能导致完全不同的结果
    • 错误:总是使用 alpha=1.0,不进行参数调优
  6. 在未标准化的数据上解释系数

    • 标准化后的系数大小不能直接解释特征重要性
    • 错误:直接比较标准化后系数的绝对值来判断特征重要性

小结

  • 正则化的核心目的:通过惩罚模型复杂度,防止过拟合,提高泛化能力
  • 三种正则化方法的特点
    • Ridge (L2):缩小所有系数,但不进行特征选择
    • Lasso (L1):进行特征选择,将不重要特征的系数压缩为0
    • ElasticNet:结合两者优点,适用于高度相关特征
  • 关键参数
    • alpha:控制正则化强度,需要通过交叉验证调优
    • l1_ratio:ElasticNet 专用,控制 L1 和 L2 的平衡
  • 实践要点
    1. 正则化前必须对数据进行标准化
    2. 使用交叉验证选择最佳正则化参数
    3. 根据问题特点选择正则化方法:
      • 特征间存在共线性 → Ridge
      • 特征很多且需要特征选择 → Lasso
      • 特征高度相关且需要特征选择 → ElasticNet

正则化是机器学习中控制模型复杂度的重要工具,正确使用可以显著提高模型的泛化性能。在下一课中,我们将学习如何使用评估指标来量化模型的性能。

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「回归评估指标:MSE、RMSE、MAE 与 R2」 以巩固所学知识。