4. 线性回归模型:OLS回归、正则化(Lasso/Ridge)、逐步回归、因子共线性处理

线性回归,说白了就是量化选股里最基础的武器。你想想看,我们做因子组合,本质上就是在找一堆因子和未来收益之间的线性关系。今天我就把这套东西掰开揉碎了讲清楚。

4.1 OLS回归:最朴素的解法

OLS,全称是普通最小二乘法。它的目标很简单:找到一条线,让所有样本点到这条线的垂直距离的平方和最小。

数学上,我们假设:

y = Xβ + ε

其中 y 是收益,X 是因子矩阵,β 是我们要估计的系数,ε 是误差项。

OLS的解是:

β̂ = (XᵀX)⁻¹Xᵀy

这个公式看着简单,但有个致命问题——它要求 XᵀX 是可逆的。我在项目中遇到过,当因子数量接近样本数量时,这个矩阵就变得病态了。

核心假设:OLS要求因子之间不能高度相关,否则系数估计会变得极不稳定。

4.2 因子共线性:量化里的隐形杀手

为什么会这样?我举个例子。假设你有两个因子:市盈率和市净率。它们都反映估值,相关性高达0.9。你用OLS去跑,会发现系数的标准误变得巨大,今天跑出来系数是正,明天就变成负了。

判断共线性的常用指标:

  • VIF(方差膨胀因子):VIF > 10 就说明共线性严重
  • 条件数:条件数 > 30 需要警惕

我曾经在做一个多因子模型时,VIF飙到了50多。嗯,当时没注意,结果回测曲线漂亮得很,实盘直接崩了。从那以后,我每次建模前必做VIF检查。

4.3 正则化:给系数戴上枷锁

既然OLS容易过拟合,那我们就给损失函数加点惩罚。这就是正则化的思路。

4.3.1 Ridge回归(L2正则化)

Ridge在损失函数里加上了系数平方和:

Loss = MSE + λ∑βᵢ²

λ是超参数,控制惩罚力度。λ越大,系数越趋向于0,但不会变成0。

Ridge的好处是:即使因子高度相关,它也能给出稳定的解。我习惯用交叉验证来选λ,一般从0.01到100之间搜索。

个人经验:Ridge特别适合因子数量多但每个因子都有一定预测能力的情况。它不会把因子完全剔除,而是让它们"缩水"。

4.3.2 Lasso回归(L1正则化)

Lasso用的是系数绝对值之和:

Loss = MSE + λ∑|βᵢ|

Lasso有个神奇的特性:它能把不重要的因子系数直接压缩到0。说白了,它帮你做特征选择。

你想想看,如果你有100个因子,但真正有用的可能只有10个。Lasso就能帮你把这10个挑出来。

注意:Lasso在因子高度相关时表现不稳定。它会随机选择其中一个,把另一个压缩到0。这时候我建议用Elastic Net——它结合了L1和L2。

4.4 逐步回归:手工筛选的艺术

在正则化流行之前,大家做因子筛选主要靠逐步回归。它有三种方式:

  1. 向前选择:从空模型开始,每次加入最显著的因子
  2. 向后剔除:从全模型开始,每次剔除最不显著的因子
  3. 双向逐步:结合两者,边加边剔

我刚开始做量化时,特别喜欢用逐步回归。但后来发现它有个坑:

逐步回归基于p值做决策,但p值本身就有问题。样本量一大,p值就变得极其敏感。我曾经用1000只股票做逐步回归,结果选出了30个因子,但换一个时间段,选出来的因子完全不同。

我的建议:逐步回归可以作为探索性工具,但不要完全依赖它做最终决策。现在更推荐用Lasso或随机森林做特征选择。

4.5 实战:用Python实现

下面是我常用的代码模板:

import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.model_selection import cross_val_score
from statsmodels.stats.outliers_influence import variance_inflation_factor

# 计算VIF
def calc_vif(X):
    vif = pd.DataFrame()
    vif["features"] = X.columns
    vif["VIF"] = [variance_inflation_factor(X.values, i) 
                  for i in range(X.shape[1])]
    return vif

# Ridge回归调参
alphas = np.logspace(-3, 2, 50)
cv_scores = []
for alpha in alphas:
    ridge = Ridge(alpha=alpha)
    scores = cross_val_score(ridge, X_train, y_train, 
                             cv=5, scoring='neg_mean_squared_error')
    cv_scores.append(scores.mean())

best_alpha = alphas[np.argmax(cv_scores)]
print(f"最优alpha: {best_alpha}")

4.6 知识体系总览

下面这张图总结了本章的核心逻辑:

线性回归模型知识体系 线性回归模型 OLS回归 最小化残差平方和 需XᵀX可逆 正则化 Ridge (L2) Lasso (L1) 逐步回归 向前/向后/双向 基于p值筛选 因子共线性处理 VIF诊断 条件数检查 正则化处理

4.7 避坑指南

最后,我总结几个实战中容易踩的坑:

  • 不要盲目相信R²:R²高不代表模型好,可能是过拟合。我见过有人把R²做到0.99,结果实盘亏得底朝天。
  • 注意时间序列的依赖性:金融数据有自相关性,直接用OLS会低估标准误。我习惯用Newey-West调整标准误。
  • 正则化参数要谨慎:λ选太大,模型欠拟合;选太小,过拟合。用时间序列交叉验证比随机交叉验证更靠谱。
  • 因子标准化很重要:Lasso和Ridge对尺度敏感,记得先做标准化处理。

我的习惯:每次建模前,先跑VIF,剔除VIF > 10的因子。然后用Ridge做初步筛选,最后用Lasso做特征选择。这套流程在实盘中表现稳定。

嗯,线性回归看似简单,但用好了威力无穷。记住,模型只是工具,真正重要的是你对数据的理解和对风险的敬畏。


公众号:蓝海资料掘金营,微信deep3321