机器学习算法2-线性回归-Elastic-Net弹性网络

Posted by Hilda on August 10, 2025

Elastic-Net是一种结合了L1和L2正则化的线性回归模型。它的目标是最小化一个损失函数,这个损失函数由三部分组成:

image-20250810220400406

  1. 均方误差项:$$(\frac{1}{2n_{samples}})   Xw - y   ^2$$,这部分是普通的线性回归损失,用于衡量模型的预测值与真实值之间的差异。
  2. L1正则化项:$$αρ   w   _1$$,这部分是Lasso回归的惩罚项。它倾向于使一些系数变为零,从而实现特征选择,使模型变得稀疏。
  3. L2正则化项:$$(α(1-ρ)/2)   w   _2^2$$,这部分是岭回归的惩罚项。它倾向于使所有系数都接近于零,以防止过拟合,但不会使系数完全变为零。

scikit-learn中,ElasticNet的参数alpha对应于公式中的α,控制整体的惩罚力度。参数l1_ratio对应于公式中的ρ,它是一个介于0和1之间的混合比率。

  • l1_ratio0时,模型退化为纯L2正则化,即岭回归(Ridge)。
  • l1_ratio1时,模型退化为纯L1正则化,即Lasso回归。
  • l1_ratio介于01之间时,它结合了Lasso和岭回归的优点,特别适用于特征之间存在高度相关性的情况。

1.代码模拟训练与不同模型对比

【1】导包,生成真实数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np
from sklearn.linear_model import ElasticNet, LinearRegression, Lasso
import matplotlib.pyplot as plt


np.random.seed(42)

n_samples, n_features = 100, 50
X = np.random.randn(n_samples, n_features)
# 引入相关性:让前两个特征线性相关
X[:,0] = X[:,1] * 2 + np.random.randn(n_samples) * 0.1


# 真实的稀疏的系数
true_w = np.zeros(n_features)
true_w[:5] = np.array([10, -5, 3, 2, -1]) # 只有前5个特征系数非零
true_b = 5

y = X.dot(true_w) + true_b + np.random.randn(n_samples) * 2

print(f"真实的系数数量:{np.count_nonzero(true_w)}")

image-20250810221506495

image-20250810221517194

【2】模型训练与比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from sklearn.metrics import mean_squared_error
models = {
    "普通线性回归": LinearRegression(),
    "Lasso(alpha=0.1)": Lasso(alpha=0.1),
    "Elastic Net(alpha=0.1,l1_ratio=0.7)": ElasticNet(alpha=0.1, l1_ratio=0.7)
}

coef_df = {
    "真实系数":true_w
}

mse_results = {}

for name, model in models.items():
    model.fit(X, y)
    coef_df[name] = model.coef_
    y_pred = model.predict(X)
    mse = mean_squared_error(y, y_pred)
    mse_results[name] = mse
    print(f"- {name} : 均方误差(MSE)={mse:.2f}")
    print(f"- 学到的非零系数的数量:{np.count_nonzero(model.coef_):.0f}")
    

image-20250810222219110

【3】可视化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
plt.figure(figsize=(15, 8))

x_axis = np.arange(n_features)

width = 0.2

plt.bar(x_axis - 1.5*width, coef_df['真实系数'], width, label='真实系数', color='black')
plt.bar(x_axis - 0.5*width, coef_df['普通线性回归'], width, label='普通线性回归', color='skyblue')
plt.bar(x_axis + 0.5*width, coef_df['Lasso(alpha=0.1)'], width, label='Lasso (alpha=0.1)', color='salmon')
plt.bar(x_axis + 1.5*width, coef_df['Elastic Net(alpha=0.1,l1_ratio=0.7)'], width, label='Elastic-Net (alpha=0.1, l1_ratio=0.7)', color='lightgreen')

plt.title("不同模型学习到的系数与真实系数对比", fontsize=16)
plt.xlabel("特征索引", fontsize=12)
plt.ylabel("系数大小", fontsize=12)
plt.legend()
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.show()

image-20250810222906133

真实系数:只有前5个特征有非零值,其余都为0。

普通线性回归:由于没有正则化,模型会为所有50个特征都计算出一个系数,其中一些可能是由噪声或特征相关性引起的,与真实系数有较大偏差。

Lasso回归:由于L1正则化,Lasso会倾向于将许多不重要的特征系数压缩为0,因此其非零系数的数量会显著减少,更接近真实的稀疏结构。

Elastic-Net回归:由于结合了L1和L2正则化,它也会将许多不重要的系数设为0,其表现会介于普通线性回归和Lasso之间,并且在处理相关特征时可能比Lasso更稳定。