通透!L1正则与 L2 正则的区别 !!

哈喽,大家好~

今儿和大家系统地聊聊 L1 正则(Lasso)与 L2 正则(Ridge)的核心区别、各自侧重点与典型使用场景。

首先,回顾一下 无正则的最小二乘(OLS)。

给定数据集 、目标 ,标准线性模型假设为

OLS 的优化目标是最小化平方损失:

  满秩且 ,通常的解析解为

但当存在多重共线性(病态或不可逆)、高维()时,OLS 不稳定或不可求,需要正则化。

L2 正则(Ridge):二次惩罚、闭式解与稳定性

Ridge 在线性回归的优化目标为

其中 $ |w|2^2 = \sum{j=1}^p w_j^2$。该目标仍是严格凸函数,有唯一解。令梯度为零可得闭式解:

性质:

  • 稳定性:对病态矩阵加上  的“抬升”,改善条件数,降低方差。
  • 均匀收缩:在正交设计下(),可得

即所有系数按同一比例缩小,几乎不产生稀疏(除非 )。

对相关特征:权重更分散,减少过拟合风险。

L1 正则(Lasso):稀疏惩罚、软阈值与特征选择

Lasso 的优化目标是

其中 $ |w|1 = \sum{j=1}^p |w_j|$。

由于   处不可导,需用次梯度或近端方法。

在正交设计()的特例下,可以得到一维分解为  个独立的子问题。记 ,则 Lasso 的解具有经典软阈值(soft-thresholding)形式:

推导思路(正交情形):

目标可分解为对每个  

 ,一阶条件给出 ,解得

结合  的符号与零点条件(若 ,则最优为 ),得到软阈值。

这解释了稀疏:当信号强度不够(不超过阈值 ),直接被置零。

几何视角与稀疏性的原因

Ridge 的约束集合是  球:

Lasso 的约束集合是  多面体:

当损失函数的等高线(椭圆)与约束集合相交,L1 的多面体“顶点”(坐标轴对齐)更容易成为最优点,导致许多系数恰为 0;而 L2 的圆滑边界则不容易把系数推到正好为 0。

偏差-方差权衡与泛化

L2 增加偏差但显著降低方差,尤其在多重共线性时泛化更好。

L1 增加偏差并强烈降低方差,同时还减少模型维度(特征数),在高维稀疏真值下泛化最佳。

实践中常通过交叉验证选择 (或 ),寻找最佳偏差-方差折中点。

贝叶斯视角(先验解释)

  • Ridge 等价于在高斯噪声下添加高斯先验 ,MAP 解对应 Ridge 解。形式上:
  • Lasso 对应拉普拉斯(双指数)先验:

拉普拉斯分布有尖峰重尾,鼓励稀疏。

与相关特征成组性、非唯一性与可解释性

强相关特征块:

  • Ridge 倾向于把权重在相关特征中“平均分配”,给出更稳定的估计。
  • Lasso 倾向于择一入模(或小部分),提高可解释性但可能不稳定。

非唯一性:

  • Ridge 解唯一。
  • Lasso 在  或强相关时可能存在多组最优解(不同特征集合但拟合效果相近),需要再加约束或用 Elastic Net。

可解释性:

  • Lasso 由于稀疏性,便于解释“选择了哪些特征”。
  • Ridge 更适合“所有特征都有贡献”的场景。

优化算法差异(可微 vs 次梯度)

  • Ridge 可微,有闭式解、梯度下降都非常稳定。

  • Lasso 不可微,常用方法:

    坐标下降(coordinate descent):逐坐标交替软阈值更新。

    近端梯度(ISTA/FISTA):使用软阈值近端算子

    ADMM 等。

完整案例

咱们通过一个虚拟回归数据集,展示 L1 vs L2 的差异,包括稀疏选择、正则路径、交叉验证性能曲线、以及在强相关特征下的稳定性。

数据

样本数 n=600,特征数 p=20。

存在两个相关块:

  • block1(特征 0-4):强相关(约 0.95)。
  • block2(特征 5-9):中等相关(约 0.5)。

其他特征独立。

真值稀疏:仅少数特征为非零,例如 0、2、5、11。

高斯噪声;训练-测试拆分;标准化(非常重要:L1/L2 对量纲敏感)。

代码中,使用 StandardScaler 保证不同特征量纲一致。

CV使用 RepeatedKFold,路径使用 logspace 的 alpha 网格。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import Lasso, Ridge
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split, RepeatedKFold
import warnings

warnings.filterwarnings("ignore")

# 1) 构造虚拟数据集
def make_synthetic_data(n=1600, p=20, seed=42):
    rng = np.random.RandomState(seed)
    X = np.zeros((n, p))

    # Block1: 强相关,特征 0-4
    z1 = rng.normal(size=n)
    for j in range(5):
        X[:, j] = z1 + 0.05 * rng.normal(size=n)  # 高相关: 0.95 左右

    # Block2: 中度相关,特征 5-9
    z2 = rng.normal(size=n)
    for j in range(510):
        X[:, j] = z2 + 0.5 * rng.normal(size=n)  # 相关: 0.5 左右

    # 其他特征独立
    for j in range(10, p):
        X[:, j] = rng.normal(size=n)

    # 真值系数(稀疏)
    w_true = np.zeros(p)
    w_true[0] = 3.0   # 来自强相关块
    w_true[2] = -2.5  # 来自强相关块
    w_true[5] = 2.0   # 来自中等相关块
    w_true[11] = -3.0 # 独立特征之一
    # 可加入少量微弱信号以更真实
    w_true[15] = 0.5

    noise = rng.normal(scale=2.0, size=n)  # 噪声
    y = X @ w_true + noise
    return X, y, w_true

X, y, w_true = make_synthetic_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.35, random_state=123)

# 工具函数:从 pipeline 中还原到原始尺度的系数与截距
def recover_original_coef(pipeline, X_train):
    scaler = pipeline.named_steps['standardscaler']
    model = pipeline.named_steps['lasso'if 'lasso' in pipeline.named_steps else pipeline.named_steps['ridge']
    coef_std = model.coef_
    # 原始尺度系数
    coef_orig = coef_std / scaler.scale_
    # 原始尺度截距
    intercept_orig = model.intercept_ - np.sum(coef_std * scaler.mean_ / scaler.scale_)
    return coef_orig, intercept_orig

# 2) 构造 alpha 网格 + 交叉验证
alphas = np.logspace(-3140)  # 1e-3 到 10
cv = RepeatedKFold(n_splits=5, n_repeats=2, random_state=42)

def cv_curve(model_type='lasso'):
    mses = []
    for alpha in alphas:
        fold_mse = []
        for tr_idx, val_idx in cv.split(X_train):
            X_tr, X_val = X_train[tr_idx], X_train[val_idx]
            y_tr, y_val = y_train[tr_idx], y_train[val_idx]
            if model_type == 'lasso':
                pipe = make_pipeline(StandardScaler(), Lasso(alpha=alpha, max_iter=10000, random_state=42))
            else:
                pipe = make_pipeline(StandardScaler(), Ridge(alpha=alpha, random_state=42))
            pipe.fit(X_tr, y_tr)
            y_pred = pipe.predict(X_val)
            fold_mse.append(mean_squared_error(y_val, y_pred))
        mses.append(np.mean(fold_mse))
    return np.array(mses)

mse_lasso = cv_curve('lasso')
mse_ridge = cv_curve('ridge')

# 选择最佳 alpha
best_alpha_lasso = alphas[np.argmin(mse_lasso)]
best_alpha_ridge = alphas[np.argmin(mse_ridge)]

# 3) 拟合最终模型
pipe_lasso = make_pipeline(StandardScaler(), Lasso(alpha=best_alpha_lasso, max_iter=10000, random_state=42))
pipe_ridge = make_pipeline(StandardScaler(), Ridge(alpha=best_alpha_ridge, random_state=42))
pipe_lasso.fit(X_train, y_train)
pipe_ridge.fit(X_train, y_train)

coef_lasso, intercept_lasso = recover_original_coef(pipe_lasso, X_train)
coef_ridge, intercept_ridge = recover_original_coef(pipe_ridge, X_train)

y_pred_lasso = pipe_lasso.predict(X_test)
y_pred_ridge = pipe_ridge.predict(X_test)
test_mse_lasso = mean_squared_error(y_test, y_pred_lasso)
test_mse_ridge = mean_squared_error(y_test, y_pred_ridge)

# 4) 正则化路径:追踪关键特征 (0, 2, 5, 11) 在不同 alpha 下的系数
key_feats = [02511]
path_lasso = {j: [] for j in key_feats}
path_ridge = {j: [] for j in key_feats}

for alpha in alphas:
    pipe_l = make_pipeline(StandardScaler(), Lasso(alpha=alpha, max_iter=10000, random_state=42))
    pipe_r = make_pipeline(StandardScaler(), Ridge(alpha=alpha, random_state=42))
    pipe_l.fit(X_train, y_train)
    pipe_r.fit(X_train, y_train)
    coef_l, _ = recover_original_coef(pipe_l, X_train)
    coef_r, _ = recover_original_coef(pipe_r, X_train)
    for j in key_feats:
        path_lasso[j].append(coef_l[j])
        path_ridge[j].append(coef_r[j])

# 5) Bootstrap 稳定性分析:对相关特征 (0, 2) 做 100 次抽样,观察系数分布
rng = np.random.RandomState(123)
B = 100
coef_boot_lasso = {j: [] for j in [02]}
coef_boot_ridge = {j: [] for j in [02]}

for b in range(B):
    # 有放回抽样
    idx = rng.choice(np.arange(X_train.shape[0]), size=X_train.shape[0], replace=True)
    X_b, y_b = X_train[idx], y_train[idx]

    pipe_l = make_pipeline(StandardScaler(), Lasso(alpha=best_alpha_lasso, max_iter=10000, random_state=42))
    pipe_r = make_pipeline(StandardScaler(), Ridge(alpha=best_alpha_ridge, random_state=42))
    pipe_l.fit(X_b, y_b)
    pipe_r.fit(X_b, y_b)

    coef_l, _ = recover_original_coef(pipe_l, X_train)
    coef_r, _ = recover_original_coef(pipe_r, X_train)
    for j in [02]:
        coef_boot_lasso[j].append(coef_l[j])
        coef_boot_ridge[j].append(coef_r[j])

# 6) 可视化
plt.figure(figsize=(1612))

# 图1:系数对比条形图
ax1 = plt.subplot(221)
width = 0.25
indices = np.arange(len(w_true))
ax1.bar(indices - width, w_true, width, color='', label='True', alpha=0.8)
ax1.bar(indices, coef_lasso, width, color='', label='Lasso', alpha=0.8)
ax1.bar(indices + width, coef_ridge, width, color='', label='Ridge', alpha=0.8)
ax1.set_title('图1:系数对比(True vs Lasso vs Ridge)', fontsize=14)
ax1.set_xlabel('特征索引')
ax1.set_ylabel('系数值(原始尺度)')
ax1.legend(loc='upper right')
ax1.grid(True, alpha=0.3)

# 图2:正则化路径(关键特征)
ax2 = plt.subplot(222)
colors = {0''2''5''11''}
for j in key_feats:
    ax2.plot(alphas, np.abs(path_lasso[j]), color=colors[j], linestyle='-', linewidth=2, label=f'Lasso |w_{j}|')
    ax2.plot(alphas, np.abs(path_ridge[j]), color=colors[j], linestyle='--', linewidth=2, label=f'Ridge |w_{j}|')
ax2.set_xscale('log')
ax2.set_title('图2:正则化路径(关键特征系数的绝对值)', fontsize=14)
ax2.set_xlabel('alpha(对数尺度)')
ax2.set_ylabel('|系数|(原始尺度)')
ax2.legend(loc='upper right', ncol=2)
ax2.grid(True, alpha=0.3)

# 图3:交叉验证 MSE vs alpha
ax3 = plt.subplot(223)
ax3.plot(alphas, mse_lasso, color='', marker='o', label=f'Lasso CV MSE (best={best_alpha_lasso:.4f})')
ax3.plot(alphas, mse_ridge, color='', marker='s', label=f'Ridge CV MSE (best={best_alpha_ridge:.4f})')
ax3.set_xscale('log')
ax3.set_title('图3:交叉验证曲线 MSE vs alpha', fontsize=14)
ax3.set_xlabel('alpha(对数尺度)')
ax3.set_ylabel('CV 平均 MSE')
ax3.legend(loc='upper right')
ax3.grid(True, alpha=0.3)
# 在图上标注测试集 MSE
ax3.text(0.0012, max(mse_lasso)*0.9f'Test MSE Lasso={test_mse_lasso:.3f}', color='', fontsize=12)
ax3.text(0.0012, max(mse_lasso)*0.85f'Test MSE Ridge={test_mse_ridge:.3f}', color='', fontsize=12)

# 图4:Bootstrap 稳定性箱线图(相关特征 0 和 2)
ax4 = plt.subplot(224)
data_l0 = coef_boot_lasso[0]
data_l2 = coef_boot_lasso[2]
data_r0 = coef_boot_ridge[0]
data_r2 = coef_boot_ridge[2]
box_data = [data_l0, data_l2, data_r0, data_r2]
labels = ['Lasso w0''Lasso w2''Ridge w0''Ridge w2']
box = ax4.boxplot(box_data, patch_artist=True, labels=labels)
colors_box = ['''''''']
for patch, color in zip(box['boxes'], colors_box):
    patch.set_facecolor(color)
    patch.set_alpha(0.7)
for whisker in box['whiskers']:
    whisker.set_color('')
for cap in box['caps']:
    cap.set_color('')
for median in box['medians']:
    median.set_color('')
ax4.set_title('图4:Bootstrap 稳定性(强相关特征系数分布)', fontsize=14)
ax4.set_ylabel('系数值(原始尺度)')
ax4.grid(True, alpha=0.3)
ax4.text(1.2, np.mean(data_l0), 'Lasso 稀疏/择一', color='', fontsize=12)
ax4.text(3.2, np.mean(data_r0), 'Ridge 稳定/均匀', color='', fontsize=12)

plt.tight_layout()
plt.show()
通透!L1正则与 L2 正则的区别 !!图1

图1:系数对比

可以看到,Lasso 明显将若干无关特征系数压到 0,体现稀疏与特征选择。

在相关块(特征 0-4、5-9),Lasso 往往只保留其中的一两个(例如 0、2、5),而 Ridge 则将权重分布到多个相关特征上,显示“共享”效果。

Ridge 的系数普遍更小,体现均匀收缩;Lasso 的非零系数则靠近真值(当 alpha 合理)但会因稀疏而可能略偏。

我们通过直接对比两种正则化的估计特征与幅度,便于理解“稀疏 vs 均匀收缩”的核心区别。

图2:正则化路径

在不同 alpha 下,绘制关键特征(0、2、5、11)的系数绝对值曲线,实线为 Lasso,虚线为 Ridge。

可以看到,Lasso 的曲线存在“阈值”行为:某些特征在 alpha 增大后迅速跌落到 0(软阈值),出现明显断崖。

Ridge 的曲线更为平滑,随着 alpha 增加逐渐收缩,但不轻易变成 0。

对强相关特征(如 0、2),两者的动态非常不同:Lasso 可能只保留一个,另一个很快为零;Ridge 则均匀缓慢收缩。

可视化“软阈值 vs 平滑收缩”,理解为何 Lasso 更适用于特征选择而 Ridge 更适用于稳定估计。

图3:交叉验证曲线

两条曲线对比 Lasso 与 Ridge 在不同 alpha 下的 CV 平均 MSE,标注各自的最优 alpha,同时在图内给出测试集 MSE。

可以看到,不同数据结构下最佳 alpha 可能不同;在存在强相关块且真值稀疏时,常见情况是 Lasso 与 Ridge 的最优点不相同。

若噪声较大且相关结构强,Ridge 的曲线可能更平稳;Lasso 的曲线在较小 alpha 时波动可能更大。

可以帮助理解偏差-方差权衡,利用 CV 合理地自动选择正则强度;也说明 Lasso 的稀疏优势并不总是带来更低的 MSE(尤其在真值非稀疏或相关特征较多的场景)。

图4:Bootstrap 稳定性箱线图

对强相关的两特征(如 0、2),分别对 Lasso 与 Ridge 的系数进行 100 次 bootstrap 拟合后绘制分布。

可以看到,Lasso 的分布可能更分散甚至呈现“分裂”(某次选择特征 0,某次选择特征 2,导致两者系数分布较宽),体现选择不稳定性。

Ridge 的分布相对集中稳定,且两特征会形成“共同承担”的权重分配。

说明在相关特征集下,Lasso 的变量选择存在不稳定风险;Ridge 更适用于稳定预测与在高相关情况下保持鲁棒。

结合实验的进一步讨论

  • 选择标准化:在 L1/L2 中,特征量纲差异会造成不公平惩罚;必须进行标准化。
  • 选择 alpha:通过图3可见,CV 是关键步骤。某些数据下 Lasso 在最佳点的测试 MSE 未必优于 Ridge;这取决于真值稀疏程度与相关结构。
  • 稀疏与解释性:图1与图2演示了 Lasso 在特征选择上的优势,能够得到更简洁的解释。但图4提醒:在强相关特征下,选择可能不稳定,Elastic Net 是常用折衷方案。
  • 强相关块:图1与图4联合说明,与其强行做稀疏,不如在高度相关的场景中使用 Ridge 或 Elastic Net;或者做特征工程(聚合/降维)再用 Lasso。

大家通过以上理论与实验,基本可以形成系统的认知框架,帮助在具体项目中根据数据结构与任务目标选择合适的正则化策略,达到更加有效的效果。

最后

最近准备了的总结,完整的机器学习小册,免费领取~
通透!L1正则与 L2 正则的区别 !!图2
领取:备注「算法小册」即可~
通透!L1正则与 L2 正则的区别 !!图3

声明:内容取材于网络,仅代表作者观点,如有内容违规问题,请联系处理。 
more
告别电池焦虑!纳米发电机如何为穿戴/植入医疗设备“续航续命”
小米辟谣汽车工厂内电池产线起火
威马工厂被接盘,电池企业跨界造车!
在65℃高温环境连续运行1200小时后,这款电池性能几乎不变→
广州计量院承办的“广州市锂电池产品碳足迹标识认证专家组工作会议”成功召开
2025年全球及中国钠电池正极材料行业竞争格局及趋势分析:钠电池出货增量提速带动正极材料出货增长,聚阴离子类成为市场主流[图]
苹果憋大招!iPhone 18 Pro系列前瞻,钢电池、小灵动岛,还有透明探索版
不要对固态电池有太多幻想
大正微纳完成超亿元A3轮融资,加速柔性钙钛矿电池百兆瓦产线建设|早起看早期
新能源车主被抛弃?我国发布首款碳-14核电池,能量密度是固态电池4倍
Copyright © 2025 成都区角科技有限公司
蜀ICP备2025143415号-1
  
川公网安备51015602001305号