跳转至

特征工程(Feature Engineering)

"数据和特征决定了机器学习的上限,而模型和算法只是在逼近这个上限。"
特征工程是机器学习中最重要也最耗时的环节,直接决定了模型性能的天花板。


什么是特征工程?

特征工程是把原始数据转化为模型能有效利用的特征的过程。包括数据清洗、特征变换、特征构造和特征选择。

graph LR
    A[原始数据] --> B[数据清洗]
    B --> C[特征变换]
    C --> D[特征构造]
    D --> E[特征选择]
    E --> F[模型训练]

一、数据清洗

缺失值处理

方法 做法 适用场景
删除 删除含缺失值的行/列 缺失比例很小(<5%)
均值/中位数填充 用该特征的均值或中位数填充 数值型特征,缺失随机
众数填充 用出现最多的值填充 类别型特征
前/后向填充 用前/后一个值填充 时间序列数据
模型预测填充 用 KNN 或回归模型预测缺失值 缺失值与其他特征相关

实用建议

  • 先分析缺失值的模式:是随机缺失还是有规律地缺失?
  • 可以新建一个特征 is_missing(0/1),有时缺失本身就是有用的信息

异常值处理

检测方法:

  • 3σ 原则:假设数据正态分布,超出 \(\mu \pm 3\sigma\) 的视为异常(覆盖 99.7% 的数据)
  • IQR 方法\(Q1 - 1.5 \times IQR\) 以下或 \(Q3 + 1.5 \times IQR\) 以上的视为异常
  • 其中 \(IQR = Q3 - Q1\)(四分位距)
  • 箱线图(Box Plot):直接看到异常值
  • 散点图:发现偏离整体分布的点

处理方式:

  • 删除:确认是错误数据时
  • 截断(Clipping):将异常值限制在合理范围内
  • 转换:对数变换等可以压缩异常值的影响
  • 保留:如果异常值是真实的(如超高收入用户),可能不应该删除

重复值处理

直接删除完全重复的行。注意检查是否是"近似重复"(如姓名大小写不同)。


二、特征变换

特征缩放(Feature Scaling)

为什么需要缩放?

许多算法(如 KNN、SVM、梯度下降)对特征的尺度敏感。如果"年龄"范围是 0-100,"收入"范围是 0-1000000,距离计算会完全被"收入"主导。

方法 公式 结果范围 适用场景
标准化(Z-score) \(x' = \frac{x - \mu}{\sigma}\) 均值 0,标准差 1 大多数算法,尤其是假设正态分布的
归一化(Min-Max) \(x' = \frac{x - x_{\min}}{x_{\max} - x_{\min}}\) [0, 1] 需要固定范围时(如神经网络)
MaxAbs 缩放 $x' = \frac{x}{ x_{\max} }$
RobustScaler \(x' = \frac{x - Q_2}{Q_3 - Q_1}\) 无固定范围 有异常值时(基于中位数和 IQR)

不需要缩放的算法

决策树和基于决策树的集成方法(随机森林、XGBoost)不需要特征缩放,因为它们基于特征值的排序来分裂,与尺度无关。

类别特征编码

将文字类别转化为数字,让模型能处理:

直接给每个类别编一个数字:

红色 → 0,  蓝色 → 1,  绿色 → 2

问题: 模型可能认为 绿色(2) > 蓝色(1) > 红色(0),但颜色之间实际没有大小关系。

仅适用于: 有自然顺序的特征(如 学历:小学 < 初中 < 高中 < 大学)

为每个类别创建一个二值列:

红色 → [1, 0, 0]
蓝色 → [0, 1, 0]
绿色 → [0, 0, 1]

优点: 不引入虚假的大小关系
缺点: 类别太多时(如城市名,有几百个),维度爆炸

解决方案: 对高基数特征使用目标编码或频率编码

用每个类别对应的目标变量均值来编码:

北京 → 该类别下目标变量的平均值 = 0.85
上海 → 0.72
广州 → 0.63

优点: 不会维度爆炸,包含了类别与目标的关联信息
风险: 容易过拟合,需要配合交叉验证使用

数值特征变换

方法 作用 适用场景
对数变换 \(\log(x+1)\) 压缩右偏分布 收入、点击量等长尾数据
平方根变换 \(\sqrt{x}\) 温和地压缩分布 计数数据
Box-Cox 变换 自动找到最优变换 使数据尽量接近正态分布
分箱(Binning) 将连续值离散化 年龄段、收入等级

三、特征构造

特征构造是特征工程中最需要领域知识和创造力的部分。

常见构造方法

方法 示例
数学组合 BMI = 体重 / 身高²;单价 = 总价 / 面积
时间特征 从日期中提取:年、月、日、星期几、是否节假日、是否周末
统计聚合 用户过去 7 天的平均消费、最大消费、消费次数
交叉特征 城市×年龄段、品牌×价格区间
文本特征 文本长度、关键词数量、TF-IDF
多项式特征 \(x_1^2\), \(x_1 x_2\), \(x_2^2\)(增加非线性)

实际案例:房价预测

原始特征:面积、卧室数、浴室数、建造年份、邮编

构造新特征:

  • 房龄 = 当前年份 - 建造年份
  • 每间卧室面积 = 面积 / 卧室数
  • 浴卧比 = 浴室数 / 卧室数
  • 是否新房 = 房龄 < 5
  • 地段均价 = 同邮编区域的历史平均价格

四、特征选择

特征太多会导致:维度灾难、训练变慢、过拟合。特征选择帮你留下最有用的特征。

方法分类

独立于模型,根据统计指标筛选特征:

方法 适用场景 做法
方差阈值 通用 删除方差接近 0 的特征(几乎没有变化的特征没用)
相关系数 回归 选择与目标变量相关系数高的特征
卡方检验 分类 检验特征与目标是否独立
互信息 通用 衡量特征与目标的依赖程度(可捕获非线性关系)

优点: 计算快,不依赖模型
缺点: 没有考虑特征之间的组合效果

用模型的性能作为评价标准,通过不断尝试特征子集来选择:

  • 前向选择:从 0 个特征开始,逐步添加最有用的特征
  • 后向消除:从全部特征开始,逐步删除最没用的特征
  • 递归特征消除(RFE):每轮训练模型,删掉权重最小的特征

优点: 考虑了特征组合效果
缺点: 计算量大

特征选择嵌入到模型训练过程中:

  • L1 正则化(Lasso):自动将不重要特征的权重压缩为 0
  • 树模型的特征重要性:随机森林、XGBoost 等天然给出每个特征的重要性排名

优点: 效率高,兼顾过滤法和包裹法的优点
推荐: 实践中最常用


五、特征工程实战流程

graph TD
    A[获取原始数据] --> B[探索性分析 EDA]
    B --> C{数据有问题?}
    C -->|缺失值| D[填充/删除]
    C -->|异常值| E[处理异常值]
    C -->|数据干净| F[特征变换]
    D --> F
    E --> F
    F --> G[特征构造]
    G --> H[特征选择]
    H --> I[模型训练]
    I --> J{效果好?}
    J -->|不好| G
    J -->|好| K[完成]

实用技巧

特征工程经验法则

  1. 先看数据再建模:花 80% 的时间理解数据,20% 训练模型
  2. 从简单开始:先用最基本的特征训练基线模型,再逐步添加复杂特征
  3. 特征和模型要匹配:线性模型需要手动做特征交叉;树模型能自动处理非线性
  4. 关注数据泄露:不要用未来的数据构造特征(如用未来的股价预测今天的交易)
  5. 可解释性很重要:你需要能向别人解释每个特征的含义和为什么它有用