cos大壮 2025-06-11 11:30 浙江
L1 和 L2 是啥?
当我们用模型(比如线性回归)去拟合数据时,如果特征太多、数据太少,模型可能会过拟合。
L1 和 L2 正则化就是在模型训练时“加个惩罚”,防止模型学得太复杂。
名称 | 比喻 | 本质 |
---|---|---|
L1 正则化 | 像“懒人整理行李”:宁愿丢掉不重要的东西 | 让一些系数变成 0(特征选择) |
L2 正则化 | 像“压缩行李”:每样东西都压缩点 | 让所有系数都变小(防止过拟合) |
L1 会让一些特征的系数变成 0 → 达到选择特征的效果。
L2 会让所有特征的系数都缩小,但一般不会变成 0 → 所有特征都参与建模,但影响力不同。
Elastic Net 是啥?
Elastic Net 就是结合了 L1 和 L2 的“混合正则化”。
它既有 L1 的特征选择能力,又有 L2 的稳定性,特别适合下面这种情况:
高维数据(特征比样本还多)+ 特征之间高度相关(多重共线性)
原理详解
我们先从普通的线性回归说起:
加上正则项
L2 正则化(岭回归 Ridge):
加入 L2 惩罚项(平方和):
优点:解决共线性,解有闭式解,数值稳定。
缺点:不能做特征选择(系数不会为 0)。
L1 正则化(Lasso):
加入 L1 惩罚项(绝对值和):
优点:可将某些系数压成 0,实现变量选择。
缺点:当特征数 >> 样本数 或 多重共线性严重时不稳定。
Elastic Net:L1 + L2 的加权组合
Elastic Net 把两种正则化结合起来:
也常写为如下形式(α 是调节比例):
当 ,就是纯 Lasso;
当 ,就是纯 Ridge;
其他情况则是弹性网络(Elastic Net)。
为什么适合「高维 + 多重共线性」
高维:特征比样本还多(p >> n)
L2 正则化仍可解,但不能变量选择。
L1 会随机选取其中一部分非零变量 → 不稳定。
Elastic Net 在这种场景下能选多个共线特征,而非像 L1 那样“二选一”。
多重共线性:变量之间高度相关
Ridge(L2)会平衡多个高度相关的变量,使它们的系数都变小。
Lasso(L1)会偏向选择其中一个 → 结果不稳定。
Elastic Net 会成组选择相关特征(grouping effect),更稳定更可靠。
正则项、过拟合与泛化能力的关系
1. 什么是过拟合 & 泛化?
过拟合(Overfitting):模型在训练集上表现很好,但在新数据(测试集)上表现很差,说明它“记住”了训练集而不是“学到”规律。
泛化能力:模型对未见数据的预测能力。越强越好。
2. 为什么会过拟合?
通常由于下面几种原因:
模型太复杂(比如参数太多、自由度太高)
特征太多,样本太少(高维问题)
特征相关性强(多重共线性)
3. 正则化的作用
正则项 = 给模型加一个“复杂度惩罚”,限制参数自由度,从而提升泛化能力。
我们来看两种正则化的作用:
L2 正则化(Ridge):
惩罚项:
意思:不让参数变得太大,每个系数都要“温和”。
作用:防止模型参数无限增长(过拟合),尤其在特征多、共线时能稳定解。
泛化能力:平滑泛化,不裁剪变量。
L1 正则化(Lasso):
惩罚项:
意思:强制让某些系数变成 0,相当于删除无关特征。
作用:自动变量选择,提高模型解释性。
泛化能力:适合稀疏场景,但在特征高度相关时不稳定。
Elastic Net 的泛化优势
Elastic Net 的惩罚:
L2 部分:让模型更稳定(缓解共线性)
L1 部分:让模型更稀疏(自动特征选择)
综合效果:提高泛化能力,尤其适用于高维共线性问题。
从优化角度理解:L1 会导致非光滑问题
这个部分偏数学,但很关键,尤其有同学想深入理解算法实现(比如坐标下降、子梯度法等)。
优化中的“光滑”与“非光滑”函数
光滑函数:梯度是连续的,比如 L2:
非光滑函数:某点处不可导,比如 L1: 在 处不可导
举个例子:
为什么 在 处没有导数,只有次梯度(subgradient)。
可视化来看, 在原点处是个尖角,而 是平滑曲线。
为什么 L1 带来优化难度?
L1 惩罚使得目标函数不再是光滑凸函数,而是非光滑凸函数
不能用标准的梯度下降(因为梯度不存在于某些点)
需要用专门的优化算法,如:
坐标下降
LARS-Lasso
子梯度下降法
近端梯度法
L2 正则的优势在于:光滑易优化
是平滑函数,容易求导 → 优化路径稳定
常用于大规模机器学习中的 batch 或 SGD(随机梯度下降)
对比总结:从泛化能力 & 优化角度看 L1 vs L2
特性 | L1 正则(Lasso) | L2 正则(Ridge) | Elastic Net |
---|---|---|---|
惩罚函数形式 | $\sum_j | \beta_j | $ |
导数性质 | 非光滑(不可导于0点) | 光滑(可导) | 部分光滑 |
优化难度 | 高 | 低 | 中 |
是否能稀疏建模 | 能 | 不能 | 部分能 |
是否能缓解共线性 | 不稳定 | 能 | 效果更佳 |
泛化能力 | 中 | 强 | 强 |
代码说明
图像 1:Lasso 正则路径图
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import Lasso, Ridge, ElasticNet
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
from sklearn.linear_model import lars_path
np.random.seed(42)
# 数据集
n_samples, n_features = 100, 50
X = np.random.randn(n_samples, n_features)
# 构造稀疏的真实系数 beta(只有前 5 个非零)
true_beta = np.zeros(n_features)
true_beta[:5] = [5, -4, 3, 0, 2]
y = X @ true_beta + np.random.randn(n_samples) * 0.5# 加入噪声
# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 拆分训练和测试集
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)
# 模型训练
lasso = Lasso(alpha=0.1).fit(X_train, y_train)
ridge = Ridge(alpha=1.0).fit(X_train, y_train)
enet = ElasticNet(alpha=0.1, l1_ratio=0.5).fit(X_train, y_train)
# 预测
lasso_pred = lasso.predict(X_test)
ridge_pred = ridge.predict(X_test)
enet_pred = enet.predict(X_test)
# 均方误差
lasso_mse = mean_squared_error(y_test, lasso_pred)
ridge_mse = mean_squared_error(y_test, ridge_pred)
enet_mse = mean_squared_error(y_test, enet_pred)
# 正则路径图:LARS 方法(用于 Lasso 路径图)
alphas_lasso, _, coefs_lasso = lars_path(X_scaled, y, method='lasso', verbose=True)
# 图像 1: Lasso 正则路径图
plt.figure(figsize=(10, 6))
colors = plt.cm.tab20(np.linspace(0, 1, n_features))
for i in range(n_features):
plt.plot(-np.log10(alphas_lasso), coefs_lasso[i], label=f'Feature {i+1}', color=colors[i])
plt.xlabel(r'$-\log_{10}(\alpha)$')
plt.ylabel('Coefficient value')
plt.title('Lasso 正则路径图(正则化程度 vs 系数变化)')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', ncol=2)
plt.tight_layout()
plt.grid(True)
plt.show()
横轴:,表示正则强度从强(左)到弱(右)
纵轴:每个特征的系数数值
每一条曲线:一个特征系数在正则化路径上的变化
这里想要表达的是:
当 较大时,Lasso 将大多数系数压缩为 0,只保留最有影响力的几个变量。
系数逐渐被引入模型:从0跳跃到非零(稀疏性特性)
说明:Lasso 具有变量选择能力,有助于解释模型,尤其在高维数据中很实用。
图像 2:Lasso / Ridge / ElasticNet 系数对比条形图(前 20 特征)
coef_df = pd.DataFrame({
'Feature': [f'Feature {i+1}'for i in range(n_features)],
'True Coef': true_beta,
'Lasso Coef': lasso.coef_,
'Ridge Coef': ridge.coef_,
'ElasticNet Coef': enet.coef_
})
# 取前20个特征绘图,颜色鲜艳
top_n = 20
colors = plt.cm.get_cmap('tab10', 4)
fig, ax = plt.subplots(figsize=(14, 7))
index = np.arange(top_n)
bar_width = 0.2
plt.bar(index, coef_df['True Coef'][:top_n], bar_width, color=colors(0), label='True Coef')
plt.bar(index + bar_width, coef_df['Lasso Coef'][:top_n], bar_width, color=colors(1), label='Lasso')
plt.bar(index + 2 * bar_width, coef_df['Ridge Coef'][:top_n], bar_width, color=colors(2), label='Ridge')
plt.bar(index + 3 * bar_width, coef_df['ElasticNet Coef'][:top_n], bar_width, color=colors(3), label='ElasticNet')
plt.xlabel('特征编号')
plt.ylabel('系数值')
plt.title('图像 2:不同模型下前 20 个特征系数对比')
plt.xticks(index + bar_width * 1.5, [f'F{i+1}'for i in range(top_n)], rotation=45)
plt.legend()
plt.tight_layout()
plt.grid(True, linestyle='--', alpha=0.5)
plt.show()
横轴是前 20 个特征编号(F1 ~ F20)
每一组颜色代表一种模型估计的系数
蓝色:真实系数
橙色:Lasso
绿色:Ridge
紫色:ElasticNet
咱们可以直观看出哪个模型更接近真实参数,可以看到:
Lasso 由于 L1 正则化,许多系数变成 0,便于解释模型,效果稀疏明显。
Ridge 不会将系数压缩为 0,但分布更加平滑,有助于处理多重共线性。
ElasticNet 在两者之间,既可以稀疏又能避免某些变量完全丢失,兼顾平稳性与解释性。
Lasso 与 ElasticNet 对于稀疏数据建模尤其有效,Ridge 更适合非稀疏但有共线性的场景。
图像 3:三种模型的预测误差对比(MSE 越低越好)
mse_values = {
'Lasso': lasso_mse,
'Ridge': ridge_mse,
'ElasticNet': enet_mse
}
fig, ax = plt.subplots(figsize=(8, 5))
bars = ax.bar(mse_values.keys(), mse_values.values(), color=['orange', 'green', 'purple'])
# 加入标签
for bar in bars:
yval = bar.get_height()
ax.text(bar.get_x() + bar.get_width() / 2, yval + 0.02, f'{yval:.3f}', ha='center', va='bottom', fontsize=12)
plt.ylabel('Mean Squared Error')
plt.title('图像 3:Lasso / Ridge / ElasticNet 预测误差比较(越低越好)')
plt.grid(True, axis='y', linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()
展示了 Lasso、Ridge 和 ElasticNet 在测试集上的预测误差(均方误差 MSE)
柱状图顶部显示了具体的误差值,便于对比
这样,我们便于判断:
预测误差衡量模型的泛化能力(过拟合 or 欠拟合)
Ridge 模型在本例中表现最稳健,因其避免过度稀疏,保留更多信息
Lasso 更倾向于“选择少数变量”,有时在高维稀疏情况下更有优势
ElasticNet 在两者之间,通常能在多重共线性和稀疏之间找到平衡,表现也很稳健
图四:ElasticNet 中 l1_ratio
l1_ratios = np.linspace(0, 1, 11)
nonzero_counts = []
for ratio in l1_ratios:
model = ElasticNet(alpha=0.1, l1_ratio=ratio, max_iter=10000)
model.fit(X_train, y_train)
nonzero_count = np.sum(model.coef_ != 0)
nonzero_counts.append(nonzero_count)
# 画图
fig, ax = plt.subplots(figsize=(10, 6))
plt.plot(l1_ratios, nonzero_counts, marker='o', linewidth=2, color='crimson')
plt.xlabel('l1_ratio')
plt.ylabel('Number of Non-zero Coefficients')
plt.title('图像 4:ElasticNet 在不同 l1_ratio 下的稀疏性变化(非零系数数量)')
plt.grid(True)
plt.xticks(l1_ratios)
plt.tight_layout()
plt.show()
l1_ratio
,范围从 0 到 1。l1_ratio = 0
:完全是 Ridge(只用 L2 正则)l1_ratio = 1
:完全是 Lasso(只用 L1 正则)之间的值表示 L1 和 L2 的加权组合
纵轴(y-axis):模型最终拟合出的系数中,非零系数的数量
即模型保留了多少个特征(变量)
l1_ratio
趋近于 1,即 Lasso 权重增大,模型会更“激进”地将不重要的特征系数压缩为 0。越靠近 Lasso,模型越稀疏,保留的变量越少,提升可解释性。
l1_ratio = 0
时,非零系数数量 ≈ 特征总数。l1_ratio
提供了从“全选变量”到“严选变量”的连续控制器。