机器学习笔记:线性模型

线性模型 是机器学习中最基础、最直观、也最广泛应用的一类模型。尽管形式简单,但通过特征工程与正则化,它们在实践中表现优异,且具有良好的可解释性。

一、线性回归(Linear Regression)

1.1 问题定义

  • 任务类型:监督学习 → 回归
  • 目标:预测连续值输出 $y \in \mathbb{R}$
  • 模型假设:输入 $\mathbf{x} \in \mathbb{R}^d$ 与输出 $y$ 之间存在线性关系

1.2 模型形式

$$ \hat{y} = f(\mathbf{x}) = \mathbf{w}^\top \mathbf{x} + b = w_1 x_1 + w_2 x_2 + \cdots + w_d x_d + b $$

  • $\mathbf{w}$:权重向量(可解释为特征重要性)
  • $b$:偏置项(截距)
💡 通常将 $b$ 吸收进 $\mathbf{w}$,通过在 $\mathbf{x}$ 前添加 1(即 $\tilde{\mathbf{x}} = [1, \mathbf{x}^\top]^\top$),简化为 $\hat{y} = \mathbf{w}^\top \mathbf{x}$

1.3 学习准则:最小二乘法(Least Squares)

给定训练集 $\{(\mathbf{x}_i, y_i)\}_{i=1}^n$,定义均方误差(MSE) 损失函数:

$$ \mathcal{L}(\mathbf{w}) = \frac{1}{n} \sum_{i=1}^n (y_i - \mathbf{w}^\top \mathbf{x}_i)^2 = \frac{1}{n} \lVert \mathbf{y} - \mathbf{X} \mathbf{w} \rVert^2 $$

其中:

  • $\mathbf{X} \in \mathbb{R}^{n \times d}$:设计矩阵(每行为一个样本)
  • $\mathbf{y} \in \mathbb{R}^n$:真实标签向量

解析解(Normal Equation):

$$ \mathbf{w}^* = (\mathbf{X}^\top \mathbf{X})^{-1} \mathbf{X}^\top \mathbf{y} $$

✅ 优点:直接求解
❌ 缺点:当 $d$ 很大或 $\mathbf{X}^\top \mathbf{X}$ 奇异时不可行

数值解:梯度下降(Gradient Descent)

$$ \mathbf{w} \leftarrow \mathbf{w} + \eta \cdot \frac{2}{n} \mathbf{X}^\top (\mathbf{y} - \mathbf{X} \mathbf{w}) $$

  • $\eta$:学习率

1.4 Python 示例(sklearn)

from sklearn.linear_model import LinearRegression
from sklearn.datasets import make_regression
import numpy as np

X, y = make_regression(n_samples=100, n_features=1, noise=10, random_state=42)
model = LinearRegression()
model.fit(X, y)

print("权重 w:", model.coef_[0])
print("偏置 b:", model.intercept_)
print("R² 得分:", model.score(X, y))

二、逻辑回归(Logistic Regression)

⚠️ 尽管名字含“回归”,但 逻辑回归是分类模型

2.1 问题定义

  • 任务类型:监督学习 → 二分类(可扩展至多分类)
  • 目标:预测类别概率 $P(y=1 \mid \mathbf{x})$

2.2 模型形式

  1. 线性组合:$z = \mathbf{w}^\top \mathbf{x} + b$
  2. Sigmoid 激活

    $$ P(y=1 \mid \mathbf{x}) = \sigma(z) = \frac{1}{1 + e^{-z}} $$

  3. 决策规则

    $$ \hat{y} = \begin{cases} 1 & \text{if } \sigma(z) \geq 0.5 \\ 0 & \text{otherwise} \end{cases} $$

✅ 输出在 $[0, 1]$,可解释为概率

2.3 学习准则:最大似然估计(MLE)

对数似然函数(等价于最小化交叉熵损失):

$$ \mathcal{L}(\mathbf{w}) = -\frac{1}{n} \sum_{i=1}^n \left[ y_i \log \sigma(\mathbf{w}^\top \mathbf{x}_i) + (1 - y_i) \log (1 - \sigma(\mathbf{w}^\top \mathbf{x}_i)) \right] $$

❗ 无解析解,需用梯度下降牛顿法优化

梯度:

$$ \nabla_{\mathbf{w}} \mathcal{L} = \frac{1}{n} \sum_{i=1}^n (\sigma(\mathbf{w}^\top \mathbf{x}_i) - y_i) \mathbf{x}_i $$


2.4 Python 示例

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
from sklearn.metrics import classification_report

X, y = make_classification(n_samples=200, n_features=2, n_redundant=0, n_informative=2, random_state=42)
model = LogisticRegression()
model.fit(X, y)

print("权重 w:", model.coef_[0])
print("偏置 b:", model.intercept_[0])
print(classification_report(y, model.predict(X)))

三、正则化:防止过拟合

当特征多、样本少或共线性严重时,线性模型易过拟合。正则化通过在损失函数中加入惩罚项控制模型复杂度。


3.1 L2 正则化(Ridge Regression)

  • 损失函数

    $$ \mathcal{L}_{\text{Ridge}} = \underbrace{\frac{1}{n} \lVert \mathbf{y} - \mathbf{X} \mathbf{w} \rVert^2}_{\text{MSE}} + \lambda \lVert \mathbf{w} \rVert_2^2 $$

  • 特点

    • 所有权重整体缩小,但不会为零
    • 解析解仍存在:$\mathbf{w}^* = (\mathbf{X}^\top \mathbf{X} + \lambda \mathbf{I})^{-1} \mathbf{X}^\top \mathbf{y}$
    • 共线性鲁棒
✅ 适用于:所有特征都可能有用,但希望抑制过大权重

3.2 L1 正则化(Lasso Regression)

  • 损失函数

    $$ \mathcal{L}_{\text{Lasso}} = \frac{1}{n} \lVert \mathbf{y} - \mathbf{X} \mathbf{w} \rVert^2 + \lambda \lVert \mathbf{w} \rVert_1 $$

  • 特点

    • 产生稀疏解(部分 $w_j = 0$)→ 自动特征选择
    • 无解析解,需用坐标下降等算法
    • 对异常值比 Ridge 更敏感
✅ 适用于:高维数据、特征选择、可解释性要求高

3.3 Elastic Net:L1 + L2 混合

  • 损失函数

    $$ \mathcal{L}_{\text{ElasticNet}} = \frac{1}{n} \lVert \mathbf{y} - \mathbf{X} \mathbf{w} \rVert^2 + \lambda \left[ \alpha \lVert \mathbf{w} \rVert_1 + (1 - \alpha) \lVert \mathbf{w} \rVert_2^2 \right] $$

  • 参数

    • $\lambda$:正则化强度
    • $\alpha \in [0,1]$:L1 与 L2 的混合比例

      • $\alpha = 1$ → Lasso
      • $\alpha = 0$ → Ridge
✅ 优势:兼具 Lasso 的稀疏性与 Ridge 的稳定性,尤其在特征高度相关时表现更好

四、对比总结

模型任务类型输出损失函数正则化特点
线性回归回归连续值MSE可加 L1/L2简单、可解释
逻辑回归分类概率交叉熵可加 L1/L2概率输出、线性决策边界
Ridge (L2)回归/分类同上MSE/交叉熵 + $\lVert \mathbf{w} \rVert_2^2$L2权重收缩,不稀疏
Lasso (L1)回归/分类同上+ $\lVert \mathbf{w} \rVert_1$L1稀疏解,自动特征选择
Elastic Net回归/分类同上+ 混合正则L1+L2稳定 + 稀疏

五、使用建议(sklearn)

# 线性回归
from sklearn.linear_model import LinearRegression, Ridge, Lasso, ElasticNet

# 分类(逻辑回归)
from sklearn.linear_model import LogisticRegression

# 正则化逻辑回归(L1/L2)
LogisticRegression(penalty='l1', solver='liblinear')      # L1
LogisticRegression(penalty='l2', solver='lbfgs')         # L2 (default)
LogisticRegression(penalty='elasticnet', solver='saga', l1_ratio=0.5)  # Elastic Net

⚠️ 注意:

  • L1 正则需指定支持该 penalty 的 solver(如 liblinear, saga
  • 使用前务必标准化特征(尤其带正则化时)!
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
📌 一句话总结线性模型
“用最简单的线性关系,通过正则化和概率建模,解决回归与分类问题,并保持可解释性。”

六、关键图解(建议配合可视化)

📊 1. 线性回归拟合直线

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

# 生成模拟数据
np.random.seed(42)
X = np.linspace(0, 10, 50).reshape(-1, 1)
y = 2.5 * X.ravel() + 1 + np.random.normal(0, 2, size=X.shape[0])

# 拟合线性回归
model = LinearRegression()
model.fit(X, y)
y_pred = model.predict(X)

# 可视化
plt.figure(figsize=(8, 5))
plt.scatter(X, y, color='skyblue', label='Data points')
plt.plot(X, y_pred, color='red', linewidth=2, label=f'Fitted line: y = {model.coef_[0]:.2f}x + {model.intercept_:.2f}')
plt.title('Linear Regression: Fitted Line')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.6)
plt.show()

效果:展示数据点与最佳拟合直线。


📈 2. 逻辑回归的 Sigmoid 曲线与决策边界

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression

# 生成一维分类数据
np.random.seed(42)
X = np.concatenate([np.random.normal(2, 1, 50), np.random.normal(6, 1, 50)]).reshape(-1, 1)
y = np.array([0]*50 + [1]*50)

# 拟合逻辑回归
model = LogisticRegression()
model.fit(X, y)

# 生成平滑的 x 轴用于绘图
x_plot = np.linspace(0, 8, 300).reshape(-1, 1)
prob = model.predict_proba(x_plot)[:, 1]  # P(y=1|x)
decision_boundary = -model.intercept_ / model.coef_[0]  # where w*x + b = 0

# 可视化
plt.figure(figsize=(10, 4))

# 左图:Sigmoid 曲线
plt.subplot(1, 2, 1)
plt.plot(x_plot, prob, color='purple', linewidth=2, label='Sigmoid: P(y=1|x)')
plt.axvline(decision_boundary, color='gray', linestyle='--', label=f'Decision boundary (x={decision_boundary[0]:.2f})')
plt.scatter(X[y==0], [0]*50, color='blue', alpha=0.6, label='Class 0')
plt.scatter(X[y==1], [1]*50, color='orange', alpha=0.6, label='Class 1')
plt.title('Logistic Regression: Sigmoid Curve')
plt.xlabel('x')
plt.ylabel('P(y=1 | x)')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)

# 右图:决策边界(分类结果)
plt.subplot(1, 2, 2)
plt.scatter(X[y==0], [0]*50, color='blue', alpha=0.6, label='Class 0')
plt.scatter(X[y==1], [1]*50, color='orange', alpha=0.6, label='Class 1')
plt.axvline(decision_boundary, color='red', linestyle='--', linewidth=2, label='Decision boundary')
plt.title('Decision Boundary')
plt.xlabel('x')
plt.yticks([0, 1], ['Class 0', 'Class 1'])
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)

plt.tight_layout()
plt.show()

效果

  • 左图:Sigmoid 概率曲线 + 数据点
  • 右图:清晰展示决策边界($x$ 使得 $P=0.5$)

🔺 3. L1 vs L2 正则化的等高线图(几何解释)

这是理解稀疏性的关键图!展示损失函数等高线与正则化约束区域的交点。
import numpy as np
import matplotlib.pyplot as plt

# 模拟一个简单的二次损失函数(如 MSE)的等高线
w1 = np.linspace(-2, 2, 200)
w2 = np.linspace(-2, 2, 200)
W1, W2 = np.meshgrid(w1, w2)

# 假设无正则化时的最优解在 (0.5, 0.5)
# 构造一个以 (0.5, 0.5) 为中心的椭圆等高线(代表损失函数)
L = (W1 - 0.5)**2 + 2*(W2 - 0.5)**2  # 椭圆(非圆,表示特征尺度不同)

# 正则化约束区域
theta = np.linspace(0, 2*np.pi, 200)
l2_circle = [np.cos(theta), np.sin(theta)]          # L2: 单位圆
l1_diamond = [
    np.concatenate([theta[:50]*0 + 1, 2 - theta[50:100], -1*np.ones(50), theta[150:]-4]),
    np.concatenate([theta[:50], np.ones(50), 3 - theta[100:150], -1*np.ones(50)])
]
# 更简单的方式:手动定义菱形
l1_x = np.array([1, 0, -1, 0, 1])
l1_y = np.array([0, 1, 0, -1, 0])

plt.figure(figsize=(12, 5))

# L2 正则化(Ridge)
plt.subplot(1, 2, 1)
contour = plt.contour(W1, W2, L, levels=15, colors='gray', alpha=0.6)
plt.clabel(contour, inline=True, fontsize=8)
plt.plot(l2_circle[0], l2_circle[1], 'b-', linewidth=2, label=r'$\|\mathbf{w}\|_2 = 1$ (L2 ball)')
plt.plot(0.5, 0.5, 'ro', label='Unconstrained min')
plt.plot(0.35, 0.35, 'bo', label='Constrained min (L2)')
plt.title('L2 Regularization (Ridge)')
plt.xlabel(r'$w_1$')
plt.ylabel(r'$w_2$')
plt.axis('equal')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)

# L1 正则化(Lasso)
plt.subplot(1, 2, 2)
contour = plt.contour(W1, W2, L, levels=15, colors='gray', alpha=0.6)
plt.clabel(contour, inline=True, fontsize=8)
plt.plot(l1_x, l1_y, 'r-', linewidth=2, label=r'$\|\mathbf{w}\|_1 = 1$ (L1 ball)')
plt.plot(0.5, 0.5, 'ro', label='Unconstrained min')
plt.plot(0.45, 0.0, 'ro', label='Constrained min (L1)')  # 交在 w2=0 轴上
plt.title('L1 Regularization (Lasso)')
plt.xlabel(r'$w_1$')
plt.ylabel(r'$w_2$')
plt.axis('equal')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)

plt.tight_layout()
plt.show()

关键解读

  • L2(左图):圆形约束与椭圆等高线交于非坐标轴点 → 两个权重都非零,但变小。
  • L1(右图):菱形“尖角”在坐标轴上,更容易与等高线在轴上相交 → 某个权重为 0 → 稀疏性
💡 实际中,L1 的稀疏性在高维特征选择中非常有用(如基因数据、文本 TF-IDF)。运行环境:确保安装了 matplotlib, numpy, scikit-learn
pip install matplotlib numpy scikit-learn

七、L1 和 L2 正则化

为了展示L1 和 L2 正则化图中展示不同 $\lambda$ 值的效果调整约束区域的大小(即缩放圆和菱形),以反映不同的正则化强度。同时,我们也将使用真实数据集来演示Lasso回归中的特征选择效果。

1. 不同 $\lambda$ 的约束圈

首先,在更新之前的代码,加入不同 $\lambda$ 的约束圈。

import numpy as np
import matplotlib.pyplot as plt

# 准备数据
w1 = np.linspace(-2, 2, 200)
w2 = np.linspace(-2, 2, 200)
W1, W2 = np.meshgrid(w1, w2)

# 构造损失函数等高线
L = (W1 - 0.5)**2 + 2*(W2 - 0.5)**2

plt.figure(figsize=(14, 6))

# 设置不同的 lambda 值
lambdas = [0.5, 1, 2]

for i, lam in enumerate(lambdas):
    # L2 正则化
    plt.subplot(1, 3, i+1)
    contour = plt.contour(W1, W2, L, levels=15, colors='gray', alpha=0.6)
    plt.clabel(contour, inline=True, fontsize=8)
    theta = np.linspace(0, 2*np.pi, 200)
    l2_circle = np.sqrt(lam) * np.array([np.cos(theta), np.sin(theta)])
    plt.plot(l2_circle[0], l2_circle[1], 'b-', linewidth=2, label=r'$\|\mathbf{w}\|_2 \leq \sqrt{\lambda}$')
    plt.title(f'L2 Regularization ($\lambda$={lam})')
    plt.xlabel(r'$w_1$')
    plt.ylabel(r'$w_2$')
    plt.axis('equal')
    plt.grid(True, linestyle='--', alpha=0.5)
    if i == 0: plt.legend()

plt.tight_layout()
plt.show()

# 对于 L1 正则化,重复上述过程,但绘制菱形
plt.figure(figsize=(14, 6))

for i, lam in enumerate(lambdas):
    plt.subplot(1, 3, i+1)
    contour = plt.contour(W1, W2, L, levels=15, colors='gray', alpha=0.6)
    plt.clabel(contour, inline=True, fontsize=8)
    scale = np.sqrt(lam)  # Scale factor for the diamond
    l1_x = np.array([scale, 0, -scale, 0, scale])
    l1_y = np.array([0, scale, 0, -scale, 0])
    plt.plot(l1_x, l1_y, 'r-', linewidth=2, label=r'$\|\mathbf{w}\|_1 \leq \sqrt{\lambda}$')
    plt.title(f'L1 Regularization ($\lambda$={lam})')
    plt.xlabel(r'$w_1$')
    plt.ylabel(r'$w_2$')
    plt.axis('equal')
    plt.grid(True, linestyle='--', alpha=0.5)
    if i == 0: plt.legend()

plt.tight_layout()
plt.show()

2. 使用真实数据展示 Lasso 的特征选择效果

接下来,将使用一个真实的或模拟的数据集来展示Lasso回归中的特征选择效果。这里将生成一些随机数据并应用Lasso模型。

from sklearn.datasets import make_regression
from sklearn.linear_model import Lasso
import pandas as pd

# 创建一个回归问题数据集
X, y = make_regression(n_samples=100, n_features=10, noise=0.1, random_state=42)

# 应用Lasso回归
lasso = Lasso(alpha=0.1)  # 调整alpha值以观察不同的稀疏性效果
lasso.fit(X, y)

# 展示系数为0的特征
coef_df = pd.DataFrame({'Feature': [f'X{i}' for i in range(X.shape[1])], 'Coefficient': lasso.coef_})
print(coef_df)

# 可视化非零系数
non_zero_coefs = coef_df[coef_df['Coefficient'] != 0]
plt.bar(non_zero_coefs['Feature'], non_zero_coefs['Coefficient'])
plt.title('Non-zero Coefficients in Lasso Regression')
plt.xlabel('Features')
plt.ylabel('Coefficients')
plt.show()

通过以上两段代码,可以直观地看到不同$\lambda$值下L1和L2正则化的影响,以及Lasso回归如何导致某些特征的系数变为0,从而实现特征选择。请注意,实际应用中,根据具体情况调整alpha参数(对应于$\lambda$)以达到理想的稀疏性水平。

八、如何选择【LassoCV】交叉验证的折数?

选择 LassoCV(或任何带交叉验证的模型)中的折数(cv 参数),是一个在估计稳定性、计算开销和数据规模之间权衡的问题。


✅ 一、默认值与通用建议

  • scikit-learn 默认 cv=5(对于 LassoCV 等回归 CV 类)
  • 通用推荐:cv = 5cv = 10

    • 这是机器学习实践中最常用的设置
    • 在偏差-方差权衡中表现稳健

📌 经验法则

  • 小数据集(< 1000 样本) → 用 10 折(减少估计偏差)
  • 中等或大数据集(≥ 1000 样本) → 用 5 折(节省计算,方差已较低)

✅ 二、根据数据规模选择 cv

数据规模(样本数)推荐 cv理由
< 100留一法(LOO)10 折数据太少,需充分利用每条样本;LOO 无随机性但计算贵
100 – 100010 折平衡偏差与方差,结果较稳定
1000 – 10,0005 折计算效率高,验证误差估计已足够可靠
> 10,0003–5 折大数据下,即使 3 折也能提供稳定估计;可显著加速
💡 LassoCV 支持 cv=LeaveOneOut(),但仅适用于极小数据集(如 n=50),否则计算爆炸。

✅ 三、考虑数据特性

1. 时间序列数据

  • 不能直接用 KFold(会泄露未来信息)
  • ✅ 应使用 时间序列交叉验证(如 TimeSeriesSplit

    from sklearn.model_selection import TimeSeriesSplit
    lasso = LassoCV(cv=TimeSeriesSplit(n_splits=5))

2. 类别不平衡(回归中为响应值分布偏斜)

  • 若目标变量 $y$ 分布极不均匀,可考虑 分层 K 折(StratifiedKFold)
  • 但注意:StratifiedKFold 仅适用于分类;回归中无直接分层方案
  • 替代方案:手动分 bin 后分层,或使用 GroupKFold(若有分组信息)

✅ 四、计算资源 vs 精度权衡

  • LassoCV 需对每个 alpha 值在每折上训练模型
  • 总训练次数 = n_alphas × cv

    • 默认 n_alphas=100,若 cv=10 → 1000 次拟合
  • 若计算资源有限

    • 减少 cv(如从 10 → 5)
    • 减少 n_alphas(如 n_alphas=50
    • 使用 precompute=True 加速(当特征数 << 样本数时)
# 示例:平衡速度与精度
lasso_cv = LassoCV(
    cv=5,               # 5 折
    n_alphas=50,        # 减少 alpha 候选数
    random_state=42
)

✅ 五、验证你的选择是否合理

可通过多次运行观察 alpha_ 是否稳定:

import numpy as np
from sklearn.linear_model import LassoCV

alphas = []
for i in range(10):
    model = LassoCV(cv=5, random_state=i).fit(X, y)
    alphas.append(model.alpha_)

print("Alpha 估计的标准差:", np.std(alphas))
  • 若标准差很小(如 < 1e-3),说明 cv=5 已足够稳定
  • 若波动大,可尝试增大 cv 或使用固定随机种子

✅ 六、总结:推荐做法

场景推荐 cv
通用情况(教学、竞赛、项目)cv=5
小样本(n < 500)cv=10
极小样本(n < 50)cv=LeaveOneOut()
大数据(n > 10k)cv=3cv=5
时间序列cv=TimeSeriesSplit(n_splits=5)
计算受限cv=3 + 减少 n_alphas
🔔 最终建议: 从 cv=5 开始**,若结果不稳定或数据很小,再调整为 cv=10
不要盲目使用 LOO(除非 n 极小),它对噪声敏感且计算昂贵。通过合理选择 cv,在模型选择可靠性训练效率之间取得最佳平衡。

九、如何评价LassoCV交叉验证的效果

评价 LassoCV 交叉验证的效果,不能仅看“选出了一个 alpha”,而应从模型稳定性、泛化能力、稀疏性合理性误差估计可靠性等多个维度综合判断。以下是系统性的评估方法:


✅ 一、核心指标:交叉验证误差曲线

LassoCV 会为每个候选 alpha 计算平均交叉验证误差(如 MSE)。这是最直接的评估依据。

1. 绘制 Alpha vs. CV 误差 曲线

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LassoCV
from sklearn.datasets import make_regression

X, y = make_regression(n_samples=200, n_features=50, noise=10, random_state=42)

lasso_cv = LassoCV(cv=5, alphas=np.logspace(-4, 1, 50), random_state=42)
lasso_cv.fit(X, y)

# 绘图
plt.figure(figsize=(8, 5))
plt.semilogx(lasso_cv.alphas_, lasso_cv.mse_path_.mean(axis=1), 'b-', label='Mean CV MSE')
plt.semilogx(lasso_cv.alphas_, lasso_cv.mse_path_, 'b:', alpha=0.3)  # 各折误差
plt.axvline(lasso_cv.alpha_, color='r', linestyle='--', label=f'Selected alpha = {lasso_cv.alpha_:.4f}')
plt.xlabel('Alpha (log scale)')
plt.ylabel('Mean Squared Error (MSE)')
plt.title('LassoCV: Alpha vs. Cross-Validation Error')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.6)
plt.show()

🔍 如何解读?

  • 理想曲线:U 型或平滑下降后上升
  • 问题信号

    • 曲线持续下降 → 可能需要更小的 alpha(当前范围不足)
    • 曲线剧烈震荡 → 数据噪声大或 cv 折数太少(不稳定)
    • 最优 alpha 在边界 → 扩大 alphas 搜索范围

✅ 二、评估泛化性能:在独立测试集上验证

交叉验证误差是对泛化误差的估计,但最终需在未参与训练/验证的测试集上评估。

from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 在训练集上做 LassoCV
lasso_cv = LassoCV(cv=5, random_state=42)
lasso_cv.fit(X_train, y_train)

# 测试集预测
y_pred = lasso_cv.predict(X_test)
test_mse = mean_squared_error(y_test, y_pred)

print(f"CV 估计 MSE: {lasso_cv.mse_path_.mean(axis=1).min():.2f}")
print(f"测试集真实 MSE: {test_mse:.2f}")

🔍 关键判断:

  • 测试 MSE ≈ CV MSE → 交叉验证可靠
  • 测试 MSE >> CV MSE → 可能过拟合(如 cv 太小、数据泄露)
  • 测试 MSE << CV MSE → 罕见,可能 CV 估计过于保守

✅ 三、评估稀疏性与特征选择合理性

Lasso 的核心价值之一是特征选择。需检查选出的特征是否合理。

# 查看非零系数数量
n_nonzero = np.sum(lasso_cv.coef_ != 0)
print(f"非零系数数量: {n_nonzero} / {X.shape[1]}")

# 可视化系数
plt.figure(figsize=(10, 3))
plt.stem(lasso_cv.coef_, use_line_collection=True)
plt.title('Lasso Coefficients (Non-zero = selected features)')
plt.xlabel('Feature Index')
plt.ylabel('Coefficient')
plt.grid(True, linestyle='--', alpha=0.5)
plt.show()

🔍 合理性判断:

  • 领域知识验证:选出的特征是否符合业务/科学常识?
  • 稳定性检验:多次运行(不同随机种子)是否选中相似特征?

    selected_features = []
    for seed in range(10):
        model = LassoCV(cv=5, random_state=seed).fit(X, y)
        selected_features.append(set(np.where(model.coef_ != 0)[0]))
    # 计算交集/并集,评估稳定性

✅ 四、与基准模型对比

将 LassoCV 与以下模型对比,判断其是否带来收益:

对比模型目的
普通线性回归(无正则)是否过拟合?Lasso 是否提升泛化?
RidgeCVL1 稀疏性是否必要?Ridge 是否更优?
ElasticNetCV是否因特征相关性导致 Lasso 不稳定?
from sklearn.linear_model import RidgeCV, ElasticNetCV, LinearRegression

# 基准:无正则线性回归
lr = LinearRegression().fit(X_train, y_train)
lr_mse = mean_squared_error(y_test, lr.predict(X_test))

# Ridge
ridge = RidgeCV(cv=5).fit(X_train, y_train)
ridge_mse = mean_squared_error(y_test, ridge.predict(X_test))

print(f"Linear Regression MSE: {lr_mse:.2f}")
print(f"LassoCV MSE: {test_mse:.2f}")
print(f"RidgeCV MSE: {ridge_mse:.2f}")
✅ 若 LassoCV 与 RidgeCV 性能相当,但更稀疏 → 优先选 Lasso(可解释性更强)

✅ 五、诊断交叉验证本身是否可靠

1. 检查各折误差的方差

cv_errors = lasso_cv.mse_path_.mean(axis=0)  # 每折的平均误差(按 alpha 最优)
print("CV 误差标准差:", np.std(cv_errors))
  • 标准差过大 → 增加 cv 折数或检查数据分布
2. 使用重复 K 折(RepeatedKFold)提升稳定性
from sklearn.model_selection import RepeatedKFold
rkf = RepeatedKFold(n_splits=5, n_repeats=10, random_state=42)
lasso_rkf = LassoCV(cv=rkf).fit(X, y)

✅ 六、总结:LassoCV 效果评估 checklist

评估维度检查项
误差曲线是否平滑?最优 alpha 是否在合理区间?
泛化能力测试集误差 ≈ CV 误差?
稀疏性非零特征数量是否合理?是否符合领域知识?
稳定性多次运行选中的特征是否一致?
对比优势是否优于无正则、Ridge 或 ElasticNet?
计算效率训练时间是否可接受?

📌 最终建议不要只依赖 lasso_cv.alpha_ 结合误差曲线、测试性能、特征合理性三者,才能判断 LassoCV 是否“有效”。

十、【LassoCV】和岭回归的优缺点

LassoCV(基于 L1 正则化的交叉验证 Lasso 回归)和岭回归(Ridge Regression,基于 L2 正则化)是两种最常用的线性正则化方法。它们在目标、效果、适用场景上有显著差异。以下是它们的系统性对比,包括优缺点、适用条件和实际建议


正则化方法对比:LassoCV vs 岭回归

特性LassoCV(L1 正则化)岭回归(Ridge, L2 正则化)
正则项公式一公式二
是否稀疏✅ 是(自动将部分系数压缩为 0,实现特征选择)❌ 否(所有特征系数非零,仅缩小)
解的唯一性可能不唯一(尤其当特征高度相关时)✅ 唯一(目标函数严格凸,总有唯一解)
对异常值敏感度较高较低
计算效率稍慢(需迭代优化)快(有闭式解析解)
适合特征数高维场景(特征数 ≫ 样本数,如基因、文本)中低维场景(特征数 ≤ 样本数)

公式说明:

  • 公式一(L1 正则项)
    $$[
    \lambda \sum_{j=1}^{p} |w_j|
    ]$$
  • 公式二(L2 正则项)
    $$[
    \lambda \sum_{j=1}^{p} w_j^2
    ]$$

其中:

  • ( \lambda \geq 0 ) 是正则化强度(sklearn 中称为 alpha),
  • ( w_j ) 是模型第 ( j ) 个特征的系数,
  • ( p ) 是特征总数。

🔍 补充说明(便于理解):

  • L1 正则项λ * (|w₁| + |w₂| + ... + |wₚ|)
    → 产生“角点”,易与损失等高线在坐标轴相交 → 系数为 0。
  • L2 正则项λ * (w₁² + w₂² + ... + wₚ²)
    → 产生“圆形”约束,系数均匀收缩,但不会为 0。

✅ 二、LassoCV 的优点与缺点

✅ 优点:

  1. 自动特征选择

    • 将不重要特征的系数压缩为 0,实现稀疏模型
    • 在高维数据(如基因组学、文本 TF-IDF)中非常有用。
  2. 提升可解释性

    • 模型只依赖少数关键特征,便于业务理解和解释。
  3. 适用于 $p > n$ 的情况

    • 当特征数远大于样本数时,普通线性回归不可解,但 Lasso 仍可工作。
  4. 交叉验证自动选 $\lambda$

    • LassoCV 自动通过 CV 选择最优正则化强度,避免手动调参。

❌ 缺点:

  1. 最多只能选择 $n$ 个非零特征

    • 若样本数 $n = 100$,Lasso 最多选 100 个特征,即使真实重要特征更多。
  2. 在高度相关特征中表现不稳定

    • 面对一组强相关的特征(如 $x_1 \approx x_2$),Lasso 会随机选其中一个,其余设为 0,结果可能随数据扰动剧烈变化。
  3. 对异常值较敏感

    • L1 损失在残差大时梯度恒定,不如 L2 平滑,可能受极端值影响更大。
  4. 无解析解,依赖优化算法

    • 计算比 Ridge 慢,尤其在高维时。

✅ 三、岭回归(Ridge)的优点与缺点

✅ 优点:

  1. 稳定处理共线性(多重共线性)

    • 对高度相关的特征,Ridge 会同时保留并缩小它们的系数,结果更稳定。
  2. 有解析解,计算高效

    • 解为 $\mathbf{w} = (\mathbf{X}^\top \mathbf{X} + \lambda \mathbf{I})^{-1} \mathbf{X}^\top \mathbf{y}$,适合大规模问题。
  3. 泛化性能通常较好

    • 在预测任务中,Ridge 常比 Lasso 更鲁棒,尤其当所有特征都略有贡献时。
  4. 对异常值相对鲁棒

    • L2 惩罚对大系数更敏感,但整体损失函数更平滑。

❌ 缺点:

  1. 不进行特征选择

    • 所有特征系数都非零,模型不可稀疏,可解释性差。
  2. 不适用于 $p \gg n$ 的极端高维场景

    • 虽然数学上可解,但若特征远多于样本,模型可能仍过拟合(尽管比无正则好)。
  3. 需要手动或通过 RidgeCV 选择 $\lambda$

    • RidgeCV 同样支持交叉验证,这点与 LassoCV 类似。

🆚 四、何时选择 LassoCV?何时选择 Ridge?

场景推荐模型
目标是特征选择 / 可解释性✅ LassoCV
特征数 >> 样本数(如文本、基因数据)✅ LassoCV
特征之间高度相关✅ Ridge(或 ElasticNet)
所有特征都可能有用(如图像像素、传感器数据)✅ Ridge
预测精度优先,不在乎模型复杂度✅ Ridge(通常更稳定)
希望模型简洁、只保留关键变量✅ LassoCV

💡 折中方案:Elastic Net
结合 L1 和 L2:
$$\mathcal{L} = \text{MSE} + \lambda \left[ \alpha \|\mathbf{w}\|_1 + (1-\alpha) \|\mathbf{w}\|_2^2 \right]$$

  • 当特征相关且需要稀疏性时,ElasticNet 通常优于 Lasso

🔧 五、实际使用建议(sklearn)

from sklearn.linear_model import LassoCV, RidgeCV, ElasticNetCV

# LassoCV:自动选 alpha,适合特征选择
lasso = LassoCV(cv=5, random_state=42)

# RidgeCV:自动选 alpha,适合预测稳定性
ridge = RidgeCV(cv=5)

# ElasticNetCV:兼顾两者
elastic = ElasticNetCV(cv=5, l1_ratio=[0.1, 0.5, 0.7, 0.9, 1.0], random_state=42)

⚠️ 重要前提:使用前务必标准化特征

from sklearn.preprocessing import StandardScaler
X_scaled = StandardScaler().fit_transform(X)

📌 六、总结一句话

LassoCV 用于“选特征”,Ridge 用于“稳预测”;若两者都要,选 ElasticNet。

十一、如何选择正则化方法的参数

选择正则化方法(如 Lasso、Ridge、Elastic Net)的参数,核心是确定正则化强度(通常记为 $\lambda$ 或 alpha)以及混合比例(如 Elastic Net 中的 l1_ratio。目标是:在模型复杂度与泛化能力之间取得最佳平衡

以下是系统、实用的参数选择方法,适用于实际项目、竞赛或科研。


一、核心参数说明

模型关键参数含义
Lassoalpha ($\lambda$)L1 正则化强度,越大越稀疏
Ridgealpha ($\lambda$)L2 正则化强度,越大权重越小
Elastic Netalpha ($\lambda$)总体正则化强度
l1_ratio ($\rho$)
📌 注意:在 scikit-learn 中,正则项为 alpha * penalty,而有些文献用 $\lambda/2$,但调参逻辑一致。

二、推荐方法:交叉验证(Cross-Validation, CV)

这是最主流、最可靠的参数选择方式。scikit-learn 提供了内置 CV 类:

✅ 1. 使用 *CV 类自动调参(推荐!)

from sklearn.linear_model import LassoCV, RidgeCV, ElasticNetCV
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

# 假设 X, y 已定义
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 标准化(必须!)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# LassoCV:自动搜索 alpha
lasso = LassoCV(cv=5, random_state=42)
lasso.fit(X_train_scaled, y_train)
print("Lasso 最佳 alpha:", lasso.alpha_)

# RidgeCV
ridge = RidgeCV(cv=5)
ridge.fit(X_train_scaled, y_train)
print("Ridge 最佳 alpha:", ridge.alpha_)

# ElasticNetCV:同时搜索 alpha 和 l1_ratio
elastic = ElasticNetCV(
    cv=5,
    l1_ratio=[0.1, 0.3, 0.5, 0.7, 0.9, 1.0],  # 测试不同 L1 比例
    random_state=42
)
elastic.fit(X_train_scaled, y_train)
print("ElasticNet 最佳 alpha:", elastic.alpha_)
print("ElasticNet 最佳 l1_ratio:", elastic.l1_ratio_)
优点:自动、高效、集成 CV,避免手动网格搜索。

✅ 2. 手动网格搜索 + CV(更灵活)

当需要精细控制或结合其他超参时,可用 GridSearchCV

from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import ElasticNet

param_grid = {
    'alpha': np.logspace(-4, 1, 30),      # 从 0.0001 到 10
    'l1_ratio': [0.2, 0.5, 0.8, 1.0]
}

elastic = ElasticNet(random_state=42)
grid = GridSearchCV(elastic, param_grid, cv=5, scoring='neg_mean_squared_error')
grid.fit(X_train_scaled, y_train)

print("最佳参数:", grid.best_params_)

🔍 适用场景

  • 需要自定义评分函数(如 R²、MAE)
  • 结合其他预处理步骤(如特征选择)
  • 与其他模型统一调参流程

三、如何设置搜索范围?

1. alpha 的搜索范围

  • 不要从 0 开始(0 = 无正则,易过拟合)
  • 推荐对数尺度搜索np.logspace(-4, 2, 50) → [0.0001, ..., 100]
  • 依据

    • 若 CV 选出的 alpha 在边界(如最小值),说明范围不够,应扩展
    • 可先用 LassoCV().alphas_ 查看默认范围

2. l1_ratio 的选择(Elastic Net)

  • 若不确定,测试 [0.1, 0.3, 0.5, 0.7, 0.9, 1.0]
  • 若特征高度相关 → 倾向较小 l1_ratio(如 0.2~0.5)
  • 若目标是稀疏 → 倾向较大 l1_ratio(如 0.8~1.0)

四、评估调参效果的关键指标

调参后,不能只看 CV 分数,还需验证:

检查项方法
泛化能力在独立测试集上计算 MSE/R²
稳定性多次运行(不同 random_state)看 alpha 是否波动大
稀疏性合理性Lasso/ElasticNet 的非零系数是否符合业务逻辑?
过拟合迹象训练误差 << CV 误差?
# 示例:测试集评估
from sklearn.metrics import mean_squared_error, r2_score

y_pred = lasso.predict(X_test_scaled)
print("测试集 MSE:", mean_squared_error(y_test, y_pred))
print("测试集 R²:", r2_score(y_test, y_pred))

五、高级技巧

1. Warm Start 加速(适用于路径算法)

  • LassoElasticNet 支持 warm_start=True,在多个 alpha 间复用解,加速 CV。
  • LassoCV 内部已启用,无需手动设置。

2. 使用验证曲线(Validation Curve)诊断

from sklearn.model_selection import validation_curve

alphas = np.logspace(-4, 1, 30)
train_scores, val_scores = validation_curve(
    Lasso(), X_train_scaled, y_train,
    param_name='alpha', param_range=alphas,
    cv=5, scoring='neg_mean_squared_error'
)

plt.semilogx(alphas, -val_scores.mean(axis=1), label='Validation')
plt.semilogx(alphas, -train_scores.mean(axis=1), label='Training')
plt.xlabel('Alpha')
plt.ylabel('MSE')
plt.legend()
  • 若训练误差持续下降但验证误差上升 → 过拟合,需增大 alpha

六、总结:参数选择最佳实践

步骤操作
1. 数据预处理标准化特征(必须!)
2. 初筛模型先试 LassoCV、RidgeCV、ElasticNetCV
3. 自动调参优先用 *CV 类,快速获得 baseline
4. 检查边界若最优 alpha 在搜索边界,扩展范围
5. 测试集验证确保泛化性能可靠
6. 业务解释检查选出的特征是否合理(尤其 Lasso)
📌 终极建议
“先用 LassoCVRidgeCV 快速对比,再决定是否上 ElasticNetCV。”
大多数情况下,这三者之一就能满足需求。

十二、如何评估正则化方法的稀疏性效果?

评估正则化方法(尤其是 Lasso、Elastic Net 等)的稀疏性效果,不能只看“有多少系数为 0”,而应从数量、质量、稳定性、可解释性任务目标一致性等多个维度综合判断。以下是系统、可操作的评估方法:


✅ 一、基础指标:稀疏程度量化

1. 非零系数数量(Sparsity Level)

import numpy as np

coef = model.coef_  # 如 LassoCV 或 ElasticNetCV 的系数
n_nonzero = np.sum(coef != 0)
n_total = len(coef)
sparsity_ratio = 1 - n_nonzero / n_total

print(f"非零系数: {n_nonzero} / {n_total}")
print(f"稀疏比例: {sparsity_ratio:.2%}")
📌 用途:快速衡量模型简洁性。
⚠️ 注意:数值上“等于 0”需考虑浮点精度,建议用 np.abs(coef) > 1e-6 判断。

2. 系数分布可视化

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 3))
plt.stem(range(len(coef)), coef, use_line_collection=True)
plt.title('Model Coefficients (Non-zero = selected features)')
plt.xlabel('Feature Index')
plt.ylabel('Coefficient Value')
plt.grid(True, linestyle='--', alpha=0.5)
plt.show()

解读

  • 大量系数集中在 0 → 稀疏性强
  • 少数大系数 + 多数零 → 理想稀疏模型

✅ 二、稀疏性质量评估:选出的特征是否“好”?

稀疏 ≠ 有效。关键看选出的特征是否真正重要

1. 与真实重要特征对比(模拟数据)

若使用模拟数据(如 make_regression),可对比真实非零特征:

from sklearn.datasets import make_regression

X, y, true_coef = make_regression(
    n_samples=100, n_features=50, 
    n_informative=5,  # 真实只有 5 个特征有用
    coef=True, noise=10, random_state=42
)

model = LassoCV(cv=5).fit(X, y)
selected = np.where(np.abs(model.coef_) > 1e-6)[0]
true_nonzero = np.where(np.abs(true_coef) > 1e-6)[0]

# 计算召回率(Recall)和精确率(Precision)
recall = len(set(selected) & set(true_nonzero)) / len(true_nonzero)
precision = len(set(selected) & set(true_nonzero)) / len(selected) if len(selected) > 0 else 0

print(f"召回率(找到多少真实特征): {recall:.2%}")
print(f"精确率(选出的特征多准): {precision:.2%}")
📌 理想情况:高召回 + 高精确 → 稀疏且准确

2. 与领域知识/业务逻辑对比(真实数据)

  • 例如在房价预测中,若模型选中“卧室数”“地段”,但忽略“屋顶颜色”,说明稀疏合理。
  • 若选中“用户ID哈希值”等无意义特征 → 稀疏但无效(可能数据泄露或过拟合)

✅ 三、稳定性评估:稀疏结果是否可靠?

Lasso 在相关特征下不稳定。需检验多次运行是否选中相似特征

selected_sets = []
for seed in range(20):
    model = LassoCV(cv=5, random_state=seed).fit(X, y)
    selected = set(np.where(np.abs(model.coef_) > 1e-6)[0])
    selected_sets.append(selected)

# 计算特征被选中的频率
from collections import Counter
all_selected = [f for s in selected_sets for f in s]
freq = Counter(all_selected)

# 可视化前 20 个最常被选中的特征
top_features = freq.most_common(20)
features, counts = zip(*top_features)
plt.bar(features, counts)
plt.title('Feature Selection Frequency over 20 Runs')
plt.xlabel('Feature Index')
plt.ylabel('Selection Count')
plt.show()

解读

  • 若少数特征几乎每次都被选中 → 稳定稀疏
  • 若每次选的特征都不同 → 稀疏结果不可靠(考虑用 Elastic Net)

✅ 四、任务导向评估:稀疏是否损害性能?

稀疏的代价可能是预测精度下降。需权衡:

模型测试 MSE非零特征数
普通线性回归10.250
LassoCV10.55
RidgeCV10.350

结论:Lasso 仅损失 3% 性能,但特征减少 90% → 稀疏效果优秀

📌 建议:绘制 稀疏性 vs 性能曲线(Pareto 前沿)
alphas = np.logspace(-4, 1, 30)
sparsities, mses = [], []

for alpha in alphas:
    model = Lasso(alpha=alpha).fit(X_train, y_train)
    y_pred = model.predict(X_test)
    sparsities.append(1 - np.mean(model.coef_ != 0))
    mses.append(mean_squared_error(y_test, y_pred))

plt.plot(sparsities, mses, 'o-')
plt.xlabel('Sparsity (fraction of zero coefficients)')
plt.ylabel('Test MSE')
plt.title('Sparsity vs. Prediction Error')
plt.grid(True)
plt.show()
🔍 理想曲线:随着稀疏性增加,MSE 缓慢上升 → 可在“简洁”和“准确”间灵活选择

✅ 五、与无稀疏模型对比(消融实验)

  • Baseline:Ridge(无稀疏)
  • Target:Lasso / Elastic Net
  • 评估

    • 预测性能差距是否可接受?
    • 模型大小/推理速度是否提升?(对部署重要)
    • 是否更容易解释/审计?

✅ 六、总结:稀疏性效果评估 Checklist

维度评估方法
数量非零系数比例、系数分布图
质量与真实特征/业务知识对比
稳定性多次运行的特征选择一致性
性能权衡稀疏性 vs 测试误差曲线
实用性模型是否更易部署、解释、维护?

📌 最终建议
“稀疏性本身不是目标,而是实现可解释性、泛化性或效率的手段。”
评估时始终围绕你的任务目标

  • 若目标是特征筛选 → 重质量与稳定性
  • 若目标是压缩模型 → 重数量与推理速度
  • 若目标是提升泛化 → 重性能权衡曲线

添加新评论