特征工程:数值特征标准化、类别特征编码、特征选择与降维(PCA)

特征工程这步,说白了就是把原始数据变成模型爱吃的东西。我早年做风控模型时,吃过不少亏:直接拿原始数值灌进模型,结果某个字段因为量级太大,把其他字段全压死了。后来我老老实实走一遍标准化,效果立马不一样。

这一章咱们把四个核心环节撸一遍:数值特征标准化、类别特征编码、特征选择、PCA降维。注意,这不是流水线必须全走——具体用哪个,得看数据长啥样、模型是什么。

特征工程核心流程 特征工程四步走 原始数据集 标准化 / 编码 特征选择 PCA 降维 降维后特征 训练数据集 ⚠️ 注意:标准化与编码 通常在特征选择/PCA之前完成

数值特征标准化

异常评分模型对特征量级特别敏感。如果年龄(0~100)和交易金额(0~10万)一起喂给模型,模型几乎只看到金额。我习惯先做Z-score标准化,公式很简单:

z = (x - μ) / σ

μ是均值,σ是标准差。标准化后数据均值为0,方差为1。这种方法假设数据大致正态,如果数据有极端异常值,记得加裁剪或者用RobustScaler(基于分位数)。

我的经验:在欺诈检测场景里,我常用RobustScaler代替Z-score。因为欺诈数据往往有长尾离群点,Z-score会把离群点拉得太夸张,反而干扰模型。

另一种是MinMax归一化:

scaled = (x - min) / (max - min)

把数据压缩到[0,1]区间。适合分布边界明确的特征,比如比例、评分。不过注意,如果有新数据超出训练集范围,会越界。

方法适用场景优点缺点
Z-score分布大致正态,无极端离群不受量纲影响对离群点敏感
MinMax边界已知,无离群压缩到固定区间新数据可能越界
Robust存在离群点鲁棒性好对分位数选择敏感
我曾经踩过的坑:有一次对时间戳做标准化,结果模型完全混乱——时间戳的分布不是正态也不是均匀,标准化后毫无意义。后来改成了“距当前时刻的秒数”作为特征。所以,标准化前一定先理解特征语义。

类别特征编码

类别字段比如“城市”、“用户等级”,没法直接进模型。我建议按这类方式处理:

  • 独热编码(One-Hot):每个取值变成一列0/1。类别少(比如不到20个)时首选。但注意:如果类别太多(比如邮编),独热编码会把维度撑爆。
  • 标签编码(Label Encoding):给每个类别赋一个整数。对树模型没问题,但对线性模型和基于距离的模型会造成“有序假象”——比如“高=2,中=1,低=0”可能被模型误认为线性关系。
  • 目标编码(Target Encoding):用类别对应的目标均值来替代。在异常评分中,可以用该类别样本的异常率来编码。效果好,但容易过拟合,建议配合交叉验证做平滑。

我记得有一次做电商反作弊,类别特征“设备型号”有上千种,独热编码根本行不通。后来用目标编码,配合5折交叉验证,效果出奇好。

# 目标编码示例(Python伪代码)
from sklearn.model_selection import KFold
import numpy as np

def target_encode(train, col, target, k=5):
    kf = KFold(n_splits=k, shuffle=True)
    encoded = np.zeros(len(train))
    for train_idx, val_idx in kf.split(train):
        train_fold = train.iloc[train_idx]
        val_fold = train.iloc[val_idx]
        mean_map = train_fold.groupby(col)[target].mean()
        encoded[val_idx] = val_fold[col].map(mean_map).values
    return encoded
核心原则:类别特征编码之后,要跟数值特征一起做标准化。因为编码产生的0/1或浮点数,量级跟数值特征差别很大,不标准化会影响距离计算。

特征选择

特征不是越多越好。冗余特征会增加噪声、降低泛化能力、拖慢训练。我常用的方法有三种:

  1. 方差选择法:去掉方差接近于0的特征——比如几乎全一样的字段,对模型毫无贡献。
  2. 卡方检验 / 互信息:计算每个特征与目标变量的相关性,保留得分高的。异常评分中,互信息比卡方更通用(不限于分类)。
  3. 基于模型的选择(SelectFromModel):用随机森林或L1惩罚的逻辑回归训练一次,看特征权重。权重接近0的直接丢掉。

我个人习惯先做一波“粗筛”去掉明显无用的特征,再用模型权重做“精筛”。做粗筛时,我会检查缺失率超过90%的字段——直接删掉,不犹豫。

避坑:特征选择一定要在训练集上做,不要在全体数据上做!否则会引入数据泄露——你用全量数据筛选出的特征,在验证集上会过度自信。

PCA降维

当特征维度很高(比如图像像素、文本词袋),或者特征之间高度线性相关,PCA就派上用场了。它通过正交变换,把数据映射到几个“主成分”上,每个主成分都是原始特征的线性组合。

PCA的目标是保留最大的方差。第一个主成分捕获最大方差方向,第二个在正交方向捕获次大方差……你可以指定保留多少方差比例,比如95%,然后自动选择主成分个数。

from sklearn.decomposition import PCA

# 假设 X 已标准化
pca = PCA(n_components=0.95)   # 保留95%方差
X_pca = pca.fit_transform(X)
print(f"原始维度: {X.shape[1]}, 降维后: {X_pca.shape[1]}")

注意,PCA对特征量级非常敏感,必须先做标准化。我有一回把标准的顺序搞反了——先PCA再标准化,结果主成分完全不对,折腾了一天才发现是顺序问题。

要不要用PCA?我的建议:如果特征数几十个以内、模型是树模型,PCA未必有用——树模型自己不惧相关特征。但如果是逻辑回归、SVM或神经网络,PCA往往能显著提升稳定性和速度。

好了,这四个环节讲完了。实际项目中,我一般这样组合:数值特征标准化(Robust如果区间稳定就Z-score)+ 类别特征目标编码(避免独热爆维)+ 方差+模型特征选择(暴力剔除零贡献特征)+ 如果维度仍高(>100),再补一个PCA。调优时盯着交叉验证的AUC看,别光看特征数减少了就开心。