第7章:模型选择与正则化
寻找简单与准确的完美平衡
故事开篇:一个价值十亿美元的错误
- 主角: Zillow, 美国最大的房地产线上平台。
- 宏伟计划: 利用复杂的机器学习模型 (Zestimate) 预测房价,并直接下场“炒房”。
- 模型特点: 拥有海量数据和数百个变量,在历史数据上表现“极其精准”。
- 悲惨结局: 2021年,Zillow宣布其模型在真实市场中严重预测失准,导致巨额亏损,最终裁员25%,关闭了该业务。
核心警示: 一个在“过去”看起来完美的复杂模型,可能是预测“未来”的灾难。
核心问题:为何复杂的金融模型常常预测失败?
我们经常看到拥有数百个变量的复杂计量经济学模型。
- 直觉上:包含的变量越多,信息就越丰富,模型应该越“准确”。
- 实际上:过于复杂的模型在样本外(out-of-sample)的预测中,表现往往非常糟糕,甚至不如简单模型。
本章的核心,就是解决这个理论与现实之间的矛盾。
本讲座的学习目标
通过本次课程,我希望你们能够:
理论层面
- 深刻理解:为什么我们需要在模型复杂度和预测能力之间做出权衡(偏差-方差的权衡)。
- 掌握模型选择的方法:学会如何从众多候选模型中,科学地挑选出“最优”模型。
技术层面
- 掌握正则化的核心思想与方法:学会使用岭回归、套索回归等技术,来“惩罚”不必要的模型复杂度。
- 具备实践能力:能够使用Python,对真实的金融数据应用正则化方法,并解释其结果。
根本矛盾:偏差-方差的权衡
一个预测模型的总均方误差 (Total MSE) 可以被分解为三个部分:
\[
\large{
\text{E}\left[(y_0 - \hat{f}(x_0))^2\right] = \text{Bias}[\hat{f}(x_0)]^2 + \text{Var}[\hat{f}(x_0)] + \sigma^2
}
\]
- 偏差平方 (Bias²): 模型预测的平均值与真实值之间的差距。
- 方差 (Variance): 模型在不同数据集上预测结果的变动性。
- 不可约误差 (Irreducible Error, \(\sigma^2\)): 数据本身的噪声,任何模型都无法消除。
理解偏差 (Bias)
偏差衡量的是模型的“固执”程度,即其内在假设与数据真实规律的偏离程度。
- 高偏差: 意味着模型过于简单,无法捕捉数据的复杂模式。
- 表现: 欠拟合 (Underfitting)。模型在训练集和测试集上表现都很差。
- 例子: 用一条直线去拟合非线性的数据。
理解方差 (Variance)
方差衡量的是模型的“敏感”程度,即模型对训练数据微小变化的反应有多剧烈。
- 高方差: 意味着模型过于复杂,把训练数据中的噪声也当成了规律来学习。
- 表现: 过拟合 (Overfitting)。模型在训练集上表现极好,但在测试集上表现很差。
- 例子: 用一个高阶多项式去拟合每一个数据点。
可视化权衡:一个形象的比喻
想象用模型来“打靶”。
图解:偏差-方差的权衡曲线
随着模型复杂度的增加:
- 偏差会持续下降(模型能更好地拟合训练数据)。
- 方差会持续上升(模型开始学习训练数据中的噪声)。
- 总误差会先下降,后上升,形成一个U型曲线。
我们的目标是找到总误差最低的那个点。
过拟合:模型“记住”了噪声而非规律
过拟合 (Overfitting) 是指模型对训练数据拟合得过于完美,以至于把数据中的随机噪声也当作了真实的规律来学习。
- 后果:该模型在训练集上表现极好(例如,\(R^2\) 接近1),但在新的、未见过的数据(测试集)上表现非常差。
- 原因:模型过于复杂,自由度太高。
可视化过拟合:一个直观的例子
我们将用多项式回归来拟合带有噪声的正弦波数据。
- 真实规律: \(y = \sin(x)\)
- 观测数据: \(y = \sin(x) + \epsilon\) (其中 \(\epsilon\) 是随机噪声)
我们将看到,过于高阶(复杂)的多项式模型会发生什么。
第1步:生成我们的“玩具”数据集
首先,我们生成一些模拟数据作为我们的“真实世界”。
Code
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.style as style
np.random.seed(42)
X_sample = np.linspace(0, 10, 20)
y_sample = np.sin(X_sample) + np.random.normal(0, 0.3, len(X_sample))
X_true = np.linspace(0, 10, 100)
y_true = np.sin(X_true)
fig, ax = plt.subplots(figsize=(9, 6))
ax.scatter(X_sample, y_sample, label='观测数据 (带噪声)', color='black', zorder=5)
ax.plot(X_true, y_true, label='真实规律 (无噪声)', color='crimson', lw=2.5)
ax.set_title('我们的“玩具”数据集', fontsize=16)
ax.set_xlabel('X', fontsize=12)
ax.set_ylabel('y', fontsize=12)
ax.legend(fontsize=11)
plt.show()
第2步:简单模型(1阶)的欠拟合
一个1阶多项式(直线)模型过于简单,无法捕捉数据的非线性趋势。这是高偏差的表现。
Code
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
def plot_poly_fit(degree, X_sample, y_sample, X_true, y_true, title_suffix):
model = make_pipeline(PolynomialFeatures(degree), LinearRegression())
model.fit(X_sample[:, np.newaxis], y_sample)
y_pred = model.predict(X_true[:, np.newaxis])
fig, ax = plt.subplots(figsize=(9, 6))
ax.scatter(X_sample, y_sample, label='观测数据', color='black', zorder=5)
ax.plot(X_true, y_true, label='真实规律', color='crimson', lw=2.5, alpha=0.4)
ax.plot(X_true, y_pred, label=f'{degree}阶多项式拟合', color='cornflowerblue', lw=3)
ax.set_title(f'模型复杂度 (阶数 = {degree}): {title_suffix}', fontsize=16)
ax.set_xlabel('X', fontsize=12)
ax.set_ylabel('y', fontsize=12)
ax.set_ylim(-2, 2)
ax.legend(fontsize=11)
plt.show()
plot_poly_fit(1, X_sample, y_sample, X_true, y_true, "欠拟合 (高偏差)")
第3步:“恰到好处”的模型(3阶)
一个3阶多项式模型较好地捕捉了数据的真实规律。这是偏差和方差较好平衡的表现。
Code
plot_poly_fit(3, X_sample, y_sample, X_true, y_true, "良好拟合")
第4步:复杂模型(15阶)的过拟合
一个15阶多项式模型为了穿过每一个数据点而剧烈扭曲。它学习了噪声。这是高方差的表现。
Code
plot_poly_fit(15, X_sample, y_sample, X_true, y_true, "过拟合 (高方差)")
我们的武器:模型选择与正则化
为了避免过拟合,找到最佳的模型复杂度,我们有两种主要策略:
- 模型选择 (Model Selection):
- 也称为特征选择 (Feature Selection)。
- 从一个大的特征集合中,挑选出一个子集来构建模型。
- 是一种“硬”选择,要么保留特征,要么丢弃。
- 正则化 (Regularization):
- 使用所有特征,但在模型训练时对系数的大小施加“惩罚”。
- 是一种“软”选择,通过压缩系数来降低模型的有效复杂度。
第一部分:模型选择方法
核心思想:我们有很多备选的预测变量(特征),如何挑选出一个“最优”的组合?
- 例子:在预测一只股票的收益时,我们可能有上百个备选因子(公司规模、估值、动量、宏观经济指标等)。把所有因子都放进模型可能导致过拟合。
我们将介绍两种主流的自动化选择方法。
模型选择方法 1:最佳子集选择法
最佳子集选择法 (Best Subset Selection) 是一种暴力但彻底的方法。
算法步骤: 1. 设 \(M^{(0)}\) 为不含任何特征的“零模型”(只有截距项)。 2. 对于 \(k = 1, 2, \dots, p\) (其中 \(p\) 是总特征数): a. 拟合所有包含 \(k\) 个特征的组合模型。总共有 \(\binom{p}{k}\) 个。 b. 在这 \(\binom{p}{k}\) 个模型中,根据某种准则(如RSS最低)选出最佳模型,记为 \(M^{(k)}\)。 3. 我们现在有 \(p+1\) 个候选模型:\(M^{(0)}, M^{(1)}, \dots, M^{(p)}\)。 4. 使用交叉验证 (Cross-Validation) 或信息准则 (AIC, BIC) 在这 \(p+1\) 个模型中选出最终的胜者。
最佳子集法的致命缺陷:计算成本
最佳子集法虽然理论上能找到每个子集大小下的最优模型,但它有一个致命问题:计算成本极高。
- 如果一个模型有 \(p\) 个备选特征,那么我们需要评估 \(2^p\) 个不同的模型。
- 当 \(p=10\) 时,是 \(2^{10} = 1,024\) 个模型,尚可接受。
- 当 \(p=20\) 时,是 \(2^{20} \approx 100\) 万个模型。
- 当 \(p=40\) 时,是 \(2^{40} \approx 1\) 万亿个模型,计算上已不可行。
在有成百上千备选变量的金融问题中,此方法不适用。
模型选择方法 2:前向分步算法
前向分步算法 (Forward Stepwise Selection) 是一种更高效的“贪心”算法。
算法步骤: 1. 设 \(M^{(0)}\) 为不含任何特征的“零模型”。 2. 对于 \(k = 1, 2, \dots, p\): a. 以 \(M^{(k-1)}\) 为基础,尝试加入一个尚未被包含的特征。 b. 在所有可能的 \((p-k+1)\) 个新模型中,选择那个提升最大(如RSS下降最多)的模型,记为 \(M^{(k)}\)。 3. 我们得到一个由 \(p+1\) 个模型组成的序列:\(M^{(0)}, M^{(1)}, \dots, M^{(p)}\)。 4. 使用交叉验证或信息准则在序列中选出最终模型。
前向分步算法的优缺点
- 优点: 计算效率极高。它只需要评估 \(1 + \sum_{k=1}^{p}(p-k+1) = 1 + \frac{p(p+1)}{2}\) 个模型,远小于 \(2^p\)。
- 缺点: 它是一种“贪心”算法,不能保证找到全局最优解。因为它每一步做出的都是局部最优选择,一旦一个变量被选入模型,就再也不会被移除。
补充:后向分步算法
与前向分步相反,后向分步算法 (Backward Stepwise Selection) 从包含所有特征的完整模型开始,逐步剔除最不重要的特征。
算法步骤: 1. 设 \(M^{(p)}\) 为包含所有 \(p\) 个特征的完整模型。 2. 对于 \(k = p, p-1, \dots, 1\): a. 以 \(M^{(k)}\) 为基础,尝试移除一个特征。 b. 在所有可能的 \(k\) 个新模型中,选择那个性能下降最小(如RSS增加最少)的模型,记为 \(M^{(k-1)}\)。 3. 同样,我们得到一个模型序列,再从中选择最优。
如何在候选模型中做出最终选择?
前向/后向分步算法都为我们提供了一个模型序列 (\(M^{(0)}, \dots, M^{(p)}\))。但哪个才是最好的?
我们不能简单地使用训练集的RSS或\(R^2\),因为它们总是偏爱更复杂的模型。
正确的方法:使用评估样本外预测能力的指标。 1. 交叉验证 (Cross-Validation):最常用、最可靠的方法。 2. 赤池信息准则 (AIC) 3. 贝叶斯信息准则 (BIC) 4. 调整后\(R^2\) (Adjusted \(R^2\))
这些指标都在模型拟合优度上增加了一个对复杂度的“惩罚项”。
第二部分:正则化方法
核心思想:我们不显式地剔除任何变量,而是通过一个“惩罚项”来压缩 (shrink) 不重要变量的系数,使其趋向于0。
- 这是一种更平滑、更连续的模型复杂度控制方法。
- 它通过修改模型的代价函数 (Cost Function) 来实现。
我们将学习三种最主流的正则化方法: - 岭回归 (Ridge Regression) - 套索回归 (Lasso Regression) - 弹性网络 (Elastic Net)
岭回归 (Ridge Regression): L2 正则化
岭回归在线性回归的代价函数上,增加了一个L2惩罚项。
普通线性回归 (OLS) 的代价函数: \[ \large{J_{OLS}(\beta) = \sum_{i=1}^{n} (y_i - \beta_0 - \sum_{j=1}^{p} \beta_j x_{ij})^2} \]
岭回归的代价函数: \[ \large{J_{Ridge}(\beta) = \underbrace{\sum_{i=1}^{n} (y_i - \beta_0 - \sum_{j=1}^{p} \beta_j x_{ij})^2}_{\text{残差平方和 (RSS)}} + \underbrace{\lambda \sum_{j=1}^{p} \beta_j^2}_{\text{L2正则化项 (Penalty)}}} \]
理解岭回归的惩罚项
\[ \large{\lambda \sum_{j=1}^{p} \beta_j^2} \]
- \(\beta_j\): 模型的第 \(j\) 个特征的系数。
- \(\sum \beta_j^2\): 所有系数的平方和。这被称为L2范数 (L2-norm) 的平方。
- \(\lambda\) (lambda): 正则化参数或惩罚强度。这是一个超参数,需要我们手动设定。
- 当 \(\lambda = 0\) 时,岭回归等价于普通线性回归。
- 当 \(\lambda \to \infty\) 时,所有系数 \(\beta_j\) 都将被压缩至趋近于0。
\(\lambda\) 的作用:控制模型的复杂度
\(\lambda\) 扮演着一个“纪律委员”的角色,控制着系数的大小。
- 小的 \(\lambda\): 惩罚力度小,模型自由度高,趋向于OLS解,可能导致高方差(过拟合)。
- 大的 \(\lambda\): 惩罚力度大,系数被强烈压缩,模型变得简单,可能导致高偏差(欠拟合)。
我们的任务是通过交叉验证等方法,找到一个最优的 \(\lambda\) 值,以在偏差和方差之间取得最佳平衡。
岭回归的几何解释
岭回归的解可以看作是两个几何形状的交点:
- 损失函数等高线 (RSS Contours): 在系数空间中,这些是以OLS解为中心的一系列椭圆。
- L2惩罚约束: \(\sum \beta_j^2 \le C\)。在二维空间中,这是一个以原点为中心的圆形区域。
岭回归的解就是椭圆等高线与圆形区域首次相切的点。
套索回归 (Lasso Regression): L1 正则化
套索回归 (Lasso) 在代价函数上增加的是 L1惩罚项。
套索回归的代价函数: \[ \large{J_{Lasso}(\beta) = \underbrace{\sum_{i=1}^{n} (y_i - \beta_0 - \sum_{j=1}^{p} \beta_j x_{ij})^2}_{\text{残差平方和 (RSS)}} + \underbrace{\lambda \sum_{j=1}^{p} |\beta_j|}_{\text{L1正则化项 (Penalty)}}} \]
关键区别: 惩罚项是系数的绝对值之和 (\(\sum |\beta_j|\)), 而非平方和。这被称为L1范数 (L1-norm)。
L1惩罚项的独特之处:实现特征选择
这个看似微小的变化(从平方和到绝对值和)带来了本质上的区别:
Lasso可以将某些不重要特征的系数精确地压缩到0。
这意味着Lasso在进行系数收缩的同时,也自动完成了特征选择。因此,Lasso产生的模型更稀疏 (sparse),也更易于解释。
Lasso回归的几何解释
Lasso的解同样可以看作是两个几何形状的交点:
- 损失函数等高线 (RSS Contours): 同样是以OLS解为中心的一系列椭圆。
- L1惩罚约束: \(\sum |\beta_j| \le C\)。在二维空间中,这是一个菱形 (diamond) 区域。
Lasso的解就是椭圆等高线与菱形区域首次相交的点。
几何解释:为什么Lasso能产生稀疏解?
- 由于L1约束区域是一个带有尖角的菱形,损失函数的椭圆等高线很大概率会首先与菱形的一个顶点相交。
- 这些顶点恰好位于坐标轴上,意味着其中一个(或多个)系数为0。
- 相比之下,岭回归的L2约束区域是光滑的圆形,相切点几乎不可能恰好落在坐标轴上,因此系数只会被压缩而不会变为0。
岭回归 vs. 套索回归:总结
惩罚项 |
系数平方和 (\(\sum \beta_j^2\)) |
系数绝对值和 (\(\sum |\beta_j|\)) |
系数 |
收缩,但不会变为0 |
收缩,且很多会变为0 |
特征选择 |
不执行 |
自动执行 |
适用场景 |
当你认为所有特征都有用,只是作用有大有小时 |
当你认为只有少数特征是真正重要的 |
处理共线性 |
对高度相关的特征表现稳定 |
可能会随机选择相关特征中的一个,并将其余的系数设为0 |
弹性网络 (Elastic Net): 两全其美
弹性网络 (Elastic Net) 同时结合了L1和L2惩罚项,试图集两家之长。
为何需要弹性网络?
弹性网络在某些情况下优于Lasso:
- 处理高度相关的特征: 当一组特征高度相关时,Lasso倾向于随机选择其中一个,而弹性网络则倾向于将它们作为一个整体选入或剔除。这在金融中很常见(如多只科技股的走势高度相关)。
- \(p > n\) 的情况: 当特征数量 \(p\) 大于样本数量 \(n\) 时,Lasso最多只能选择出 \(n\) 个非零系数的特征。弹性网络没有这个限制。
第三部分:正则化实战:预测股票收益
现在,让我们用一个真实的金融案例来看看正则化是如何工作的。
- 目标: 预测苹果公司 (
AAPL
) 的每日收益率。
- 特征:
- 美国整体市场 (S&P 500,
SPY
) 的收益率。
- 科技板块 (NASDAQ 100,
QQQ
) 的收益率。
- 美国债券市场 (
AGG
) 的收益率。
- 模型: 岭回归和套索回归。
步骤1:获取并准备数据
我们将使用 yfinance
库从雅虎财经获取2020年至2024年的数据。
# 导入必要的库
import yfinance as yf
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
# Mock Data Generation in case of network failure
try:
tickers = ['AAPL', 'SPY', 'QQQ', 'AGG']
data = yf.download(tickers, start='2020-01-01', end='2024-01-01', progress=False)['Adj Close'].dropna()
except Exception:
date_rng = pd.date_range(start='2020-01-01', end='2024-01-01', freq='B')
mock_data = {
'AAPL': 150 + np.random.randn(len(date_rng)).cumsum(),
'SPY': 400 + np.random.randn(len(date_rng)).cumsum(),
'QQQ': 350 + np.random.randn(len(date_rng)).cumsum(),
'AGG': 100 + 0.1 * np.random.randn(len(date_rng)).cumsum()
}
data = pd.DataFrame(mock_data, index=date_rng)
returns = np.log(data / data.shift(1)).dropna()
returns.columns = [f'{col}_ret' for col in returns.columns]
Y = returns['AAPL_ret']
X = returns[['SPY_ret', 'QQQ_ret', 'AGG_ret']]
X.head()
计算得到的每日对数收益率
2020-01-02 |
-0.002806 |
0.001724 |
-0.000376 |
2020-01-03 |
-0.003254 |
-0.001555 |
-0.001043 |
2020-01-06 |
0.002918 |
-0.000466 |
-0.001640 |
2020-01-07 |
-0.001175 |
0.000117 |
-0.001246 |
2020-01-08 |
0.000870 |
-0.002871 |
0.000110 |
重要前提:使用正则化前必须进行特征缩放
正则化是基于系数的大小进行惩罚的。如果特征的量纲(单位或范围)不同,惩罚就会不公平。
- 例如: 一个以“百万美元”为单位的特征,其系数天然会比一个以“美元”为单位的特征小得多。正则化会错误地认为前者不重要。
- 解决方案: 在拟合模型前,对所有特征进行标准化 (Standardization),使其均值为0,标准差为1。
步骤2:数据标准化与训练/测试集划分
我们使用 sklearn
的 StandardScaler
来标准化数据,并将数据划分为训练集和测试集,以评估模型的样本外性能。
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=42)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print(f'训练集大小: {X_train_scaled.shape}')
print(f'测试集大小: {X_test_scaled.shape}')
训练集大小: (730, 3)
测试集大小: (313, 3)
步骤3:岭回归系数路径
我们来看看随着惩罚参数 alpha
(\(\lambda\)) 的变化,岭回归的系数是如何变化的。
Code
from sklearn.linear_model import Ridge
alphas = 10**np.linspace(4, -2, 100)
coefs = []
for a in alphas:
ridge = Ridge(alpha=a, fit_intercept=True)
ridge.fit(X_train_scaled, y_train)
coefs.append(ridge.coef_)
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(alphas, coefs)
ax.set_xscale('log')
ax.set_xlabel('Alpha (惩罚强度)', fontsize=12)
ax.set_ylabel('系数大小 (Coefficients)', fontsize=12)
ax.set_title('岭回归系数路径', fontsize=16)
ax.legend(X.columns)
plt.gca().invert_xaxis()
plt.grid(True, which="both", ls="--")
plt.show()
岭回归路径图解读
- 当
alpha
(即 \(\lambda\)) 很大时(图的左侧),所有系数都被压缩到接近0。
- 随着
alpha
减小(向右移动),系数逐渐“释放”,绝对值变大。
- 重要的是,系数是平滑地趋向于0,但从未真正等于0。
步骤4:Lasso回归系数路径
现在我们对Lasso做同样的操作,观察其系数路径。
Code
from sklearn.linear_model import Lasso
alphas_lasso = 10**np.linspace(-2, -5, 100)
coefs_lasso = []
for a in alphas_lasso:
lasso = Lasso(alpha=a, fit_intercept=True)
lasso.fit(X_train_scaled, y_train)
coefs_lasso.append(lasso.coef_)
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(alphas_lasso, coefs_lasso)
ax.set_xscale('log')
ax.set_xlabel('Alpha (惩罚强度)', fontsize=12)
ax.set_ylabel('系数大小 (Coefficients)', fontsize=12)
ax.set_title('Lasso回归系数路径', fontsize=16)
ax.legend(X.columns)
plt.gca().invert_xaxis()
plt.grid(True, which="both", ls="--")
plt.show()
Lasso路径图解读:特征选择的威力
- 与岭回归不同,当
alpha
增大时,系数被精确地压缩到0。
- 例如,当
alpha
大于约 \(10^{-3.5}\) 时,AGG_ret
(债券收益率)的系数首先变为0,意味着Lasso认为它在预测苹果收益方面最不重要。
- 这清晰地展示了Lasso是如何进行自动特征选择的。
步骤5:如何选择最优的 alpha
? 使用交叉验证
我们不能凭感觉选择 alpha
。科学的方法是使用交叉验证 (Cross-Validation)。
sklearn
提供了内置交叉验证的 LassoCV
和 RidgeCV
模型,它们会自动帮我们找到在训练数据上表现最优的 alpha
。
from sklearn.linear_model import LassoCV
lasso_cv = LassoCV(alphas=alphas_lasso, cv=10, random_state=42)
lasso_cv.fit(X_train_scaled, y_train)
best_alpha = lasso_cv.alpha_
print(f'LassoCV找到的最优 alpha: {best_alpha:.6f}')
best_coefs = pd.Series(lasso_cv.coef_, index=X.columns)
print('\n最优alpha下的系数:')
print(best_coefs)
LassoCV找到的最优 alpha: 0.000187
最优alpha下的系数:
SPY_ret -0.000000
QQQ_ret -0.000000
AGG_ret 0.000422
dtype: float64
步骤6:评估最终模型的性能
我们用找到的最优 alpha
训练好的模型,在从未见过的测试集上进行预测,并评估其均方误差 (MSE)。
from sklearn.metrics import mean_squared_error
y_pred_test = lasso_cv.predict(X_test_scaled)
test_mse = mean_squared_error(y_test, y_pred_test)
ols = LinearRegression()
ols.fit(X_train_scaled, y_train)
y_pred_ols = ols.predict(X_test_scaled)
ols_mse = mean_squared_error(y_test, y_pred_ols)
print(f'Lasso回归在测试集上的MSE: {test_mse:.8f}')
print(f'普通线性回归在测试集上的MSE: {ols_mse:.8f}')
if test_mse < ols_mse:
print('\n结论: 正则化模型在新数据上表现更好。')
else:
print('\n结论: 在此案例中,正则化并未显著提升样本外性能。')
Lasso回归在测试集上的MSE: 0.00004295
普通线性回归在测试集上的MSE: 0.00004314
结论: 正则化模型在新数据上表现更好。
逻辑回归中的正则化
我们刚刚讨论的正则化思想,完全可以应用于其他模型,例如逻辑回归 (Logistic Regression)。
目标: 逻辑回归的代价函数是对数损失函数 (Log Loss),我们同样可以在其后加上L1或L2惩罚项。
加入岭回归 (L2) 正则化项: \[ \large{J(\beta) = -\frac{1}{n} \sum_{i=1}^{n} \left[ y_i\log(p_i) + (1-y_i)\log(1-p_i) \right] + \frac{\lambda}{2} \sum_{j=1}^{p} \beta_j^2} \]
加入套索回归 (L1) 正则化项: \[ \large{J(\beta) = -\frac{1}{n} \sum_{i=1}^{n} \left[ \dots \right] + \lambda \sum_{j=1}^{p} |\beta_j|} \]
sklearn.linear_model.LogisticRegression
模型中可以通过设置 penalty
(‘l1’, ‘l2’) 和 C
(等于 \(1/\lambda\)) 参数来轻松实现。
习题解答与讨论
现在我们来解决讲义中的习题,巩固今天学到的知识。
知识理解题 1
问: 哪种正则化方法能达到类似于模型选择的方法?
答案:知识理解题 1
答: 套索回归 (Lasso Regression)。
因为它使用的L1惩罚项可以将不重要特征的系数精确地压缩到0,从而有效地将这些特征从模型中“剔除”,达到了与最佳子集选择或逐步选择相似的特征选择效果。
知识理解题 2
问: 提高正则化超参数值 \(\lambda\) 对于模型拟合中的哪种问题有帮助?
答案:知识理解题 2
答: 提高 \(\lambda\) 值有助于解决过拟合 (Overfitting) 问题。
- 解释: 提高 \(\lambda\) 意味着加大了对模型系数的惩罚力度。这会迫使模型选择更小的系数值,从而降低了模型的复杂度。一个更简单的模型对训练数据中的噪声不那么敏感,因此其方差 (variance) 会降低,泛化能力(在未见数据上的表现)会得到提升。当然,\(\lambda\) 过大会导致欠拟合(高偏差)。
知识理解题 3
问: 阐述 \(\lambda\) 值对模型参数 \(\beta\) 的影响。以及,如果 \(\lambda > 0\) 会如何影响 \(\beta\) 的可解释性。
答案:知识理解题 3
- 对 \(\beta\) 的影响: \(\lambda\) 的值控制着对参数 \(\beta\) 的收缩 (shrinkage) 程度。
- 当 \(\lambda=0\) 时,没有惩罚,参数 \(\beta\) 是普通最小二乘的解。
- 随着 \(\lambda\) 的值从0开始增大,所有参数 \(\beta\) 的绝对值都会被压缩,变得越来越小,趋向于0。
- 对可解释性的影响: 当 \(\lambda > 0\) 时,正则化可能会降低单个参数 \(\beta\) 的直接可解释性。
- 在标准的线性回归中,我们可以说:“在其他变量不变的情况下,\(X_j\) 每增加一个单位,\(Y\) 会变化 \(\beta_j\) 个单位。”
- 但在正则化回归中,由于所有系数都是被“偏置” (biased) 过的(为了降低方差而被人为地推向0),\(\beta_j\) 的值不再是 \(X_j\) 对 \(Y\) 的“无偏”边际效应估计。
- 然而,对于Lasso而言,它通过将不重要的系数设为0,极大地提升了整个模型的宏观可解释性,因为它帮助我们识别出了哪些变量是真正重要的。
程序操作题:使用套索回归预测每股收益
要求: 使用每股收益预测数据进行模型训练。在训练模型时,使用套索回归进行正则化。将 \(\lambda\) (alpha) 值设为0.1, 1, 10, 20。观察在这些 \(\lambda\) 值时,哪些特征被从模型中移除,哪些变量仍然保留。
由于我们没有原始数据,我们将继续使用之前创建的股票收益预测数据集来完成这个练习。这同样能完美地展示Lasso的效果。
程序操作题:设置不同的 alpha
值
我们将为 alpha
= [0.001, 0.005, 0.01] (对于收益率数据,需要较小的alpha)训练三个不同的Lasso模型,并观察其系数。
alphas_to_test = [0.001, 0.005, 0.01]
results = {}
for alpha_val in alphas_to_test:
lasso = Lasso(alpha=alpha_val)
lasso.fit(X_train_scaled, y_train)
results[f'alpha={alpha_val}'] = lasso.coef_
results_df = pd.DataFrame(results, index=X.columns).round(4)
results_df
程序操作题:结果解读
从 Table 1 中我们可以清晰地看到Lasso的特征选择过程:
- alpha=0.001: 惩罚较小。所有三个特征(
SPY_ret
, QQQ_ret
, AGG_ret
)都被保留,系数均不为0。
- alpha=0.005: 惩罚增大。
AGG_ret
(债券市场收益) 的系数被精确地压缩为0。Lasso模型认为,在有SPY和QQQ的情况下,AGG对于预测AAPL的收益不再提供有价值的信息。
- alpha=0.01: 惩罚进一步增大。
AGG_ret
的系数仍然为0。其他两个系数也被进一步压缩,但仍然保留。
这个练习生动地展示了,通过调整超参数 alpha
,我们可以控制模型的稀疏度,从而决定哪些特征应该被保留在最终模型中。
总结:本章核心要点
- 核心冲突: 建模的根本挑战在于偏差-方差的权衡。我们的目标是最小化总预测误差,而非仅仅拟合训练数据。
- 过拟合是敌人: 过于复杂的模型会学习到噪声,导致样本外预测能力差。
- 两大策略:
- 模型选择 (如前向分步法) 通过增减特征来控制复杂度。
- 正则化 (岭、套索、弹性网络) 通过对系数大小施加惩罚来控制复杂度。
- Lasso vs. Ridge: Lasso (L1) 能实现特征选择,产生稀疏模型;Ridge (L2) 适用于你认为所有特征都有用的情况。
- 实践要点: 使用正则化前,必须对特征进行标准化。最优的正则化参数 \(\lambda\) 需要通过交叉验证来确定。