特征工程基础:特征提取、特征选择、特征构造、特征降维(PCA)

做量化交易的朋友都知道,模型好不好,七分在特征,三分在参数。我见过太多人一上来就堆LSTM、Transformer,结果跑出来的信号跟随机噪声差不多。说白了,你喂进去的是垃圾,模型再牛也吐不出黄金。

这一章我们聊聊特征工程。嗯,这是整个信号生成流程里最吃经验的一环。我个人习惯把特征工程拆成四个步骤:提取、选择、构造、降维。咱们一个一个来过。

特征工程核心流程 原始数据 特征提取 从原始数据中获取基础特征 特征选择 筛选最有价值的特征子集 特征构造 组合/变换生成新特征 特征降维(PCA) 压缩维度,保留主要信息 高质量特征集

一、特征提取:从原始数据里挖金子

特征提取,说白了就是把原始行情数据变成模型能理解的数值特征。我刚开始做量化时,以为直接把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