4、单一模型基石:线性回归与逻辑回归
各位同学,今天我们来聊聊量化模型里最基础、也最实用的两个模型——线性回归和逻辑回归。很多人觉得它们太简单,不屑一顾。但我做了这么多年量化,见过太多花里胡哨的模型跑不过一个调好参的线性回归。说白了,这两个模型就是量化选股的"基本功",地基不牢,楼盖得再高也得塌。
4.1 线性回归原理与实现
线性回归,本质上就是在找一条线,让所有数据点到这条线的距离之和最小。你想想看,我们做量化选股,不就是想找到哪些因子能解释股票的收益率吗?线性回归就是干这个的。
数学上,它长这样:
y = w₁x₁ + w₂x₂ + ... + wₙxₙ + b
其中 y 是目标变量(比如下期收益率),x₁ 到 xₙ 是特征(比如市盈率、市净率、动量因子等),w 是权重,b 是偏置。我们的任务就是找到最优的 w 和 b。
怎么找? 最常用的方法是最小二乘法。它的目标函数是:
min Σ(yᵢ - ŷᵢ)²
就是让预测值和真实值的平方误差之和最小。这个优化问题有解析解,直接用矩阵运算就能算出来:
w = (XᵀX)⁻¹Xᵀy
嗯,这里要注意。我在项目中遇到过一个问题:当特征数量很多,或者特征之间有强相关性时,XᵀX 这个矩阵可能不可逆。这时候最小二乘法就失效了。我曾经因为这个原因,在回测时发现模型表现忽好忽坏,排查了半天才发现是多重共线性在作祟。
来看一个简单的Python实现:
import numpy as np
from sklearn.linear_model import LinearRegression
import pandas as pd
# 模拟数据:假设我们有市盈率(PE)和市净率(PB)两个因子
np.random.seed(42)
n_samples = 1000
pe = np.random.normal(15, 5, n_samples)
pb = np.random.normal(2, 0.8, n_samples)
# 真实收益率 = 0.02*PE - 0.05*PB + 噪声
returns = 0.02 * pe - 0.05 * pb + np.random.normal(0, 0.01, n_samples)
# 构建特征矩阵
X = np.column_stack([pe, pb])
y = returns
# 训练模型
model = LinearRegression()
model.fit(X, y)
print(f"PE系数: {model.coef_[0]:.4f}")
print(f"PB系数: {model.coef_[1]:.4f}")
print(f"截距: {model.intercept_:.4f}")
print(f"R²分数: {model.score(X, y):.4f}")
跑完这段代码,你会发现模型学到的系数和真实值非常接近。这就是线性回归的魅力——可解释性强,每个因子的贡献一目了然。
4.2 逻辑回归用于分类预测
逻辑回归,名字里带"回归",但它其实是做分类的。为什么?因为它的输出是0到1之间的概率值。在量化选股中,我们经常需要预测"这只股票下个月是涨还是跌",这就是一个二分类问题。
逻辑回归的核心是Sigmoid函数:
p = 1 / (1 + e⁻ᶻ)
其中 z = w₁x₁ + w₂x₂ + ... + wₙxₙ + b,就是线性回归的那一套。Sigmoid函数把 z 映射到0到1之间,我们就可以解释为"上涨的概率"。
我个人习惯把逻辑回归看作是"穿了马甲的线性回归"。它的决策边界是线性的,但输出是概率。这一点在做量化策略时特别有用——我们可以根据概率值来调整仓位,而不是简单地"涨或跌"。
代码实现也很简单:
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix
# 构建二分类标签:收益率 > 0 为1,否则为0
y_binary = (returns > 0).astype(int)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y_binary, test_size=0.3, random_state=42
)
# 训练逻辑回归模型
log_model = LogisticRegression()
log_model.fit(X_train, y_train)
# 预测
y_pred = log_model.predict(X_test)
y_prob = log_model.predict_proba(X_test)[:, 1] # 上涨概率
print(f"准确率: {accuracy_score(y_test, y_pred):.4f}")
print(f"混淆矩阵:\n{confusion_matrix(y_test, y_pred)}")
你看,逻辑回归不仅告诉你涨跌,还告诉你"有多大概率涨"。这个概率值在量化中太重要了。我做过一个策略,就是根据逻辑回归输出的概率值来分配资金——概率高于0.7的满仓,0.5到0.7之间的半仓,低于0.5的不买。效果比单纯用预测标签好得多。
4.3 在量化选股中的简单应用
好了,理论讲完了,我们来点实际的。怎么用这两个模型做量化选股?
我一般会这样做:
- 因子选择: 选5-10个基本面因子和技术面因子。比如市盈率、市净率、ROE、动量、波动率等。
- 数据预处理: 去极值、标准化、处理缺失值。这一步很重要,我见过有人直接拿原始数据跑模型,结果一个极端值就把整个模型带偏了。
- 模型训练: 用过去N个月的数据训练模型,预测下个月的收益率(回归)或涨跌方向(分类)。
- 滚动回测: 每个月重新训练一次模型,用最新数据预测下个月。
- 构建投资组合: 选预测收益率最高的前20%股票,等权买入。
来看一个完整的示例:
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
# 模拟股票池数据
np.random.seed(42)
n_stocks = 500
n_months = 60
# 生成因子数据
data = {
'pe': np.random.normal(15, 5, n_stocks),
'pb': np.random.normal(2, 0.8, n_stocks),
'roe': np.random.normal(0.12, 0.05, n_stocks),
'momentum': np.random.normal(0.05, 0.15, n_stocks),
'volatility': np.random.normal(0.25, 0.08, n_stocks),
}
df = pd.DataFrame(data)
# 去极值(3倍标准差)
for col in df.columns:
mean, std = df[col].mean(), df[col].std()
df[col] = df[col].clip(mean - 3*std, mean + 3*std)
# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(df)
# 模拟下期收益率
y_future = (0.03 * X_scaled[:, 0] - 0.02 * X_scaled[:, 1] +
0.05 * X_scaled[:, 2] + 0.04 * X_scaled[:, 3] -
0.01 * X_scaled[:, 4] + np.random.normal(0, 0.02, n_stocks))
# 训练模型
model = LinearRegression()
model.fit(X_scaled, y_future)
# 预测并选股
predictions = model.predict(X_scaled)
df['pred_return'] = predictions
df['rank'] = df['pred_return'].rank(ascending=False)
df['selected'] = df['rank'] <= 100 # 选前20%
print(f"选中股票的平均预测收益率: {df[df['selected']]['pred_return'].mean():.4f}")
print(f"未选中股票的平均预测收益率: {df[~df['selected']]['pred_return'].mean():.4f}")
最后,我想强调一点:线性回归和逻辑回归虽然简单,但它们是量化模型的"压舱石"。我见过很多团队一上来就用深度学习,结果过拟合得一塌糊涂,最后还得回到线性模型来救场。所以,别小看这两个模型,把它们用好了,你的量化策略就已经超过了80%的人。
嗯,这一章的内容就到这里。记住,模型不在多,在于用得精。线性回归和逻辑回归,就是量化世界里最锋利的"两把刀"。