特征工程:从数据中“榨取”价值
要说模式识别里哪个环节最考验“手艺”,我第一个投票给特征工程。算法模型再漂亮,喂进去的如果是垃圾特征,结果好不到哪儿去。我自己早期带项目时就吃过这个亏——花了两周调模型参数,精度死活上不去,后来发现是原始数据里冗余噪声太多,特征根本没选对。
说白了,特征工程就是你跟数据较劲的过程。你需要想办法把原始数据转换成更能揭示内在规律的形式。分成三块来看:特征提取、特征选择、特征构造。
特征提取:给数据“瘦身”
原始数据往往维度贼高,举个例子——一张 256×256 的灰度图,拉直就是 65536 维。你想想看,直接扔进分类器,运算量不说,还会遭遇“维度灾难”。我的做法是先做降维,把高维空间映射到低维空间,同时保留数据的主要结构。
主成分分析法(PCA)
PCA 是我最常用的手段。它找出一组新坐标轴,让数据投影后方差最大。说白了,它帮你保留数据中“最有信息量”的方向。
适用场景:数据近似遵循高斯分布,且需要线性降维。不适合非线性流形数据。
# PCA 标准做法
from sklearn.decomposition import PCA
X = load_data() # 假设已有标准化的数据
pca = PCA(n_components=10) # 降到10维
X_reduced = pca.fit_transform(X)
print(f"各主成分方差占比: {pca.explained_variance_ratio_}")
print(f"累计方差贡献: {sum(pca.explained_variance_ratio_):.3f}")
我的习惯:先用累积方差贡献曲线确定 n_components,一般不低于 85%。别拍脑袋定 10 或 20。
线性判别分析(LDA)
LDA 跟 PCA 有点像,但我更喜欢用 LDA 做分类前的降维。为什么呢?因为它不光考虑方差,还考虑类别分离度。说白了,它找的投影方向能让同类聚拢、异类远离。
| 方法 | 目标 | 是否使用标签 | 典型降维上限 |
|---|---|---|---|
| PCA | 最大化方差 | 无监督 | 无限制(<=原维) |
| LDA | 最大化类间/类内比 | 有监督 | C-1(C为类别数) |
# LDA 降维示例
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
lda = LinearDiscriminantAnalysis(n_components=2) # 二分类问题最多降到1维
X_lda = lda.fit_transform(X, y)
避坑:LDA 要求类内协方差矩阵可逆,小样本高维数据容易出问题。我曾经就在 100 维、300 样本的数据上硬跑 LDA,结果直接报奇异矩阵——后来先用 PCA 降到 50 维才解决。
t-SNE:可视化利器
t-SNE 不是用来做特征提取的——至少我不建议把它当降维工具放进训练流程。它只适合可视化。为什么?因为 t-SNE 是非线性的、随机性的,每次运行结果都不一样。
嗯,这里有个细节要注意:t-SNE 的困惑度(perplexity)需要调,我一般从 5 试到 50,观察聚类结构是否稳定。
# t-SNE 可视化示例
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X)
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='viridis', s=10)
plt.title("t-SNE 可视化结果")
plt.show()
特征选择:做减法比做加法更难
我经常跟团队说一句话:“好的模型不是靠更多特征,而是靠更对的特征。”特征选择就是去掉那些没用、冗余、干扰性的特征。
三类主流方法,我列出来你对照着看:
- 过滤式(Filter)——基于统计指标,比如相关系数、卡方检验、互信息。计算快,但跟模型无关。
- 包裹式(Wrapper)——把特征子集当搜索空间,用模型精度评估。比如递归特征消除(RFE)。效果好但计算慢。
- 嵌入式(Embedded)——模型自己选特征,比如 LASSO、树模型特征重要性。性价比最高,我常用。
# 嵌入式选择——LASSO 的特征选择效果
from sklearn.linear_model import LogisticRegression
from sklearn.feature_selection import SelectFromModel
clf = LogisticRegression(penalty='l1', C=0.1, solver='saga')
clf.fit(X, y)
selector = SelectFromModel(clf, prefit=True)
X_selected = selector.transform(X)
print(f"原始维度: {X.shape[1]} → 选中维度: {X_selected.shape[1]}")
个人经验:当特征数超过 1000 时,先跑一轮过滤式(比如互信息取 top 200),再用嵌入式精调。两层过滤能省不少时间。
特征构造:创造新特征的“手艺活”
有时候原始特征不够用,你得组合、变换、派生新特征。这个环节跟领域知识绑定很紧。没有通用公式,但有常用套路:
- 多项式组合:两个特征的乘积或平方,捕捉交互效应。
- 比率/差值:比如“收入/支出比”,“系统时间-日志时间”。
- 时间特征衍生:从时间戳拆出 星期几、是否周末、时间段(早/中/晚)。
- 聚合统计:对用户行为序列算均值、方差、峰值位置。
我的经验:别一上来就构造几百个特征。先构造 5-10 个你认为最合理的,验证一下。否则构造出来的噪声可能在选择阶段把好特征淹没了。
# 特征构造示例——时间特征与交互项
import pandas as pd
df = pd.read_csv('user_logs.csv')
df['hour'] = df['timestamp'].dt.hour
df['is_weekend'] = df['timestamp'].dt.weekday >= 5
df['revenue_per_session'] = df['total_revenue'] / df['sessions']
df['log_active_time'] = np.log1p(df['active_seconds'])
特征工程从来不是“一键搞定”的东西。它需要你对数据分布有感觉,对业务逻辑有理解,对算法局限性有预判。我见过太多人重视模型调参却忽视特征工程,结果浪费大量时间。