特征工程基础:特征提取、特征选择、特征构造、特征降维(PCA)
做量化交易的朋友都知道,模型好不好,七分在特征,三分在参数。我见过太多人一上来就堆LSTM、Transformer,结果跑出来的信号跟随机噪声差不多。说白了,你喂进去的是垃圾,模型再牛也吐不出黄金。
这一章我们聊聊特征工程。嗯,这是整个信号生成流程里最吃经验的一环。我个人习惯把特征工程拆成四个步骤:提取、选择、构造、降维。咱们一个一个来过。
一、特征提取:从原始数据里挖金子
特征提取,说白了就是把原始行情数据变成模型能理解的数值特征。我刚开始做量化时,以为直接把OHLCV扔进去就行,结果模型学出来的全是噪音。
常见的提取方式包括:
- 统计特征:均值、标准差、偏度、峰度。比如过去20根K线的收益率标准差,就是波动率的一种近似。
- 时序特征:滞后项、差分、滚动窗口统计。我个人习惯用shift(1)到shift(5)做滞后特征,捕捉短期动量。
- 技术指标:RSI、MACD、布林带。这些经典指标虽然老,但有效。我在实盘里发现RSI在震荡市里特别好用。
核心原则:特征提取要「广撒网,多敛鱼」。宁可多提取一些看似无用的特征,也不要漏掉潜在的有效信号。但要注意——提取的特征越多,后续筛选的压力越大。
# 一个简单的特征提取示例
import pandas as pd
import numpy as np
def extract_features(df):
# 基础价格特征
df['returns'] = df['close'].pct_change()
df['log_returns'] = np.log(df['close'] / df['close'].shift(1))
# 滚动统计
df['volatility'] = df['returns'].rolling(20).std()
df['volume_ma'] = df['volume'].rolling(5).mean()
# 滞后特征
for lag in [1, 2, 3, 5]:
df[f'return_lag_{lag}'] = df['returns'].shift(lag)
return df.dropna()
二、特征选择:去粗取精,留下真金
特征提取完了,你手上可能有上百个特征。但很多特征是冗余的,甚至是有害的。我见过一个团队提取了500多个特征,结果模型过拟合得一塌糊涂。
特征选择的方法有三类:
| 方法类别 | 代表算法 | 适用场景 |
|---|---|---|
| 过滤法 | 方差阈值、相关系数、互信息 | 快速筛选,适合大规模特征 |
| 包裹法 | 递归特征消除(RFE)、前向选择 | 精度优先,计算成本高 |
| 嵌入法 | Lasso、树模型特征重要性 | 兼顾效率与效果 |
我的经验:先用过滤法快速砍掉明显无用的特征(比如方差接近0的),再用嵌入法(比如LightGBM的特征重要性)做第二轮筛选。这样既快又稳。
为什么会这样?因为过滤法只考虑单个特征与目标的关系,而嵌入法能捕捉特征之间的交互效应。两者结合,效果最好。
# 用LightGBM做特征选择
import lightgbm as lgb
def select_features(X, y):
model = lgb.LGBMRegressor(n_estimators=100, max_depth=5)
model.fit(X, y)
importance = pd.DataFrame({
'feature': X.columns,
'importance': model.feature_importances_
}).sort_values('importance', ascending=False)
# 保留累计重要性前80%的特征
importance['cumsum'] = importance['importance'].cumsum() / importance['importance'].sum()
selected = importance[importance['cumsum'] <= 0.8]['feature'].tolist()
return X[selected]
三、特征构造:创造新维度
特征构造,就是利用已有特征组合出新的、更有预测力的特征。嗯,这一步最考验经验。我记得有一次做股指期货的日内策略,单纯用价格特征效果很差,后来构造了一个「成交量加权价格偏离度」特征,信号质量直接翻倍。
常用的构造方式:
- 算术组合:加减乘除。比如(close - open) / (high - low) 可以刻画日内波动形态。
- 比率特征:比如成交量与持仓量的比值,能反映市场参与热度。
- 交叉特征:两个特征的乘积或交互项。比如波动率乘以成交量,可以识别「放量波动」的异常点。
- 非线性变换:平方、对数、指数。对数变换能把右偏分布拉正,很多金融数据都适用。
避坑指南:我曾经在构造特征时不小心引入了未来信息——用未来数据构造了当前特征。结果回测曲线漂亮得不像话,实盘直接崩了。切记:构造特征时只能用当前时刻及之前的数据。
# 特征构造示例
def construct_features(df):
# 日内波动形态
df['intraday_shape'] = (df['close'] - df['open']) / (df['high'] - df['low'] + 1e-8)
# 量价关系
df['volume_price_ratio'] = df['volume'] / (df['close'] * df['close'].rolling(20).mean())
# 非线性变换
df['log_volume'] = np.log1p(df['volume'])
df['volatility_sq'] = df['volatility'] ** 2
return df
四、特征降维(PCA):化繁为简
特征降维,最经典的就是主成分分析(PCA)。它的核心思想是:用少数几个「综合特征」来代表原来的一大堆特征。说白了,就是把高维数据投影到低维空间,同时尽量保留数据的方差信息。
PCA在量化里的典型应用场景:
- 因子模型:用PCA从大量技术指标中提取主成分,作为风格因子。
- 去噪:保留前k个主成分,丢弃后面的噪声成分。
- 可视化:把高维特征降到2维或3维,方便观察数据分布。
注意:PCA对数据尺度敏感。做PCA之前一定要先标准化(StandardScaler),否则量级大的特征会主导主成分方向。我见过有人直接拿原始数据跑PCA,结果第一个主成分几乎就是「成交量」本身——因为成交量的数值比其他特征大几个数量级。
# PCA降维示例
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
def pca_reduction(X, n_components=10):
# 先标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# PCA降维
pca = PCA(n_components=n_components)
X_pca = pca.fit_transform(X_scaled)
# 查看解释方差比
explained_ratio = pca.explained_variance_ratio_
print(f"前{n_components}个主成分解释了{explained_ratio.sum():.2%}的方差")
return X_pca, pca
你想想看,如果原始特征有100个,用PCA降到10个,不仅训练速度提升10倍,还能有效缓解过拟合。当然,PCA也有缺点——降维后的特征失去了可解释性。你没法说「第3个主成分代表什么含义」。所以,如果模型需要可解释性(比如做归因分析),建议谨慎使用PCA。
我的建议:在探索阶段先用PCA快速验证信号的有效性。等确定策略框架后,再回到原始特征上做精细调优。这样既能快速迭代,又不失可解释性。
好了,特征工程的四个核心步骤就聊到这里。记住:特征工程不是一次性工作,而是需要反复迭代的过程。多试、多改、多复盘,慢慢你就能培养出对特征的「手感」。
公众号:蓝海数据掘金营,微信deep3321