4. 特征工程:特征选择与特征构造
特征工程这词儿听起来挺大,说白了就是"让数据更好用"。我刚开始做分类项目时,总觉得算法选对了就万事大吉,结果模型跑出来准确率老是上不去。后来才发现,问题出在特征上,不是算法。
你想想看,给模型喂一堆乱七八糟的特征,它再厉害也很难学出好东西。这一章咱们就聊两件事:特征选择——把没用的特征砍掉;特征构造——造出更有力的新特征。
4.1 特征工程的核心价值
先问个问题:是不是特征越多越好?
当然不是。我见过不少项目,特征数量从几十个堆到上千个,模型效果反而变差了。为什么?冗余特征带来噪声,还会让模型过拟合。特征工程的目标就是:保留最有效的信息,去掉干扰,挖掘隐藏的关系。
4.2 特征选择——做减法
特征选择的目的很直接:从一堆特征里挑出真正有用的。方法有很多,这一节我们重点讲两个常用的——方差阈值和卡方检验。
4.2.1 方差阈值
方差阈值的思想特别朴素:如果一个特征的取值几乎不变,那它对分类基本没贡献。
举个例子,如果某个特征在所有样本里都是同一个值,或者只有极个别样本不同,那这个特征就没什么区分能力。方差阈值就是设定一个下限,方差低于这个值的特征直接扔掉。
我记得第一次用这个方法时,拿到的数据有200多个特征,跑完方差阈值一看,直接砍掉了将近一半。那些被删掉的特征里,很多都是用户ID、常量列之类的无用信息。
from sklearn.feature_selection import VarianceThreshold
import pandas as pd
# 假设 X_train 是训练数据,shape = (1000, 50)
selector = VarianceThreshold(threshold=0.01)
X_selected = selector.fit_transform(X_train)
# 看看哪些特征被保留了
保留的列 = selector.get_support(indices=True)
print(f"原始特征数: {X_train.shape[1]}")
print(f"保留特征数: {X_selected.shape[1]}")
4.2.2 卡方检验
卡方检验看的是特征与目标变量之间的相关性。它适合用在分类任务中,尤其是特征为离散值或经过分箱处理的情况。
简单理解:卡方值越大,说明特征与标签越"不独立",也就是越有用。
我在信贷评分项目中用过这个方法。当时有30多个用户行为特征,用卡方检验排序后,只取了前10个特征做训练,模型AUC反而提升了3个百分点。不相关的特征真的是在拖后腿。
from sklearn.feature_selection import SelectKBest, chi2
from sklearn.preprocessing import MinMaxScaler
# 卡方要求特征非负,先做归一化
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X_train)
# 选择 top-20 特征
chi2_selector = SelectKBest(chi2, k=20)
X_kbest = chi2_selector.fit_transform(X_scaled, y_train)
# 查看每个特征的卡方分数
scores = chi2_selector.scores_
for i, score in enumerate(scores[:10]):
print(f"特征 {i}: 卡方值 = {score:.2f}")
4.3 特征构造——做加法
特征选择是去掉没用的,特征构造则是"造出"更有价值的新特征。这里讲两种经典方法:多项式特征和交互特征。
4.3.1 多项式特征
多项式特征就是把原始特征进行幂次组合。比如原始特征是 x₁ 和 x₂,多项式特征会生成 x₁²、x₂²、x₁·x₂ 等。
为什么要这么做?因为很多现实问题的决策边界不是线性的。加上多项式特征后,线性模型也能拟合非线性关系。
我个人习惯是先做一次多项式扩展,然后用正则化模型(比如 Lasso)自动筛选有用的项。这样既扩展了表达能力,又不会让维度爆炸。
from sklearn.preprocessing import PolynomialFeatures
import numpy as np
# 原始特征:两个特征
X_raw = np.array([[1, 2],
[3, 4],
[5, 6]])
# 生成 2 次多项式特征(包含交互项)
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X_raw)
print("原始特征数:", X_raw.shape[1])
print("多项式特征数:", X_poly.shape[1])
print("特征名称:", poly.get_feature_names_out())
# 输出: ['x0', 'x1', 'x0^2', 'x0 x1', 'x1^2']
4.3.2 交互特征
交互特征是多项式特征的一种特例——只保留不同特征之间的乘积,不包含平方项。它的核心思想是:两个特征组合在一起,可能会产生1+1>2的效果。
举个具体的例子。在电商数据中,"点击次数"和"浏览时长"单独看都有用,但它们的乘积(点击次数 × 浏览时长)可能更能反映用户的真实兴趣强度。这就是交互特征的价值。
from sklearn.preprocessing import PolynomialFeatures
# 只生成交互特征,不生成平方项
interaction = PolynomialFeatures(degree=2,
interaction_only=True,
include_bias=False)
X_inter = interaction.fit_transform(X_raw)
print("交互特征名称:", interaction.get_feature_names_out())
# 输出: ['x0', 'x1', 'x0 x1']
4.4 如何选择:特征选择 vs 特征构造
很多同学会问:到底什么时候该做选择,什么时候该做构造?
我的经验是:先诊断,再行动。如果模型效果差是因为过拟合、噪声多,就优先做特征选择。如果模型欠拟合、表达力不够,就优先做特征构造。
下面这张表可以帮你快速决策:
| 维度 | 特征选择 | 特征构造 |
|---|---|---|
| 核心操作 | 砍掉无用特征 | 生成新特征 |
| 适用场景 | 特征多、噪声大、过拟合 | 特征少、欠拟合、线性太强 |
| 常用方法 | 方差阈值、卡方检验、Lasso | 多项式、交互特征、分箱 |
| 风险 | 可能丢掉有用特征 | 维度爆炸、过拟合 |
| 推荐顺序 | 先做 | 后做 |