4. 特征工程基础:特征分类与特征理解

各位同学,咱们今天聊聊特征工程里最基础、也最容易被忽视的一环——特征分类与理解。说实话,我见过不少项目,模型结构选得挺花哨,调参也调得飞起,结果效果就是上不去。一查,原来是特征没理解透,把类别型当数值型硬塞进去了。嗯,这种坑,我踩过不止一次。

特征工程说白了,就是把原始数据变成模型能听懂的语言。但不同特征,模型得用不同方式去“听”。咱们先给特征分个类。

4.1 特征的三大家族

我个人习惯把特征分成三大类:数值型、类别型、文本型。当然还有图像、音频这些,但咱们离线批量生产流水线里,90% 的场景都逃不出这三类。

特征类型 典型例子 核心特点 常见处理方式
数值型 年龄、收入、点击次数 有大小、可运算 归一化、标准化、分箱
类别型 性别、城市、用户等级 离散值、无大小关系 One-Hot、Label Encoding、Target Encoding
文本型 评论、标题、描述 变长、语义丰富 TF-IDF、Word2Vec、BERT Embedding

你想想看,如果把“城市”这个类别型特征,直接用 1、2、3 编码丢给模型,模型会认为“北京(1) + 上海(2) = 深圳(3)”,这逻辑完全不对。我在项目中就见过有人这么干,结果模型学了一堆奇怪的“城市算术”。

4.2 数值型特征:不只是归一化

数值型特征看起来最简单,但坑也不少。很多人上来就做 Min-Max 归一化,其实不一定对。

核心原则:数值型特征的处理,取决于它的分布和业务含义。

  • 正态分布:用 Z-Score 标准化,效果最好。
  • 长尾分布:比如用户消费金额,大部分是小额,少数是大额。我建议先做 Log 变换,拉平分布,再归一化。
  • 有业务阈值:比如年龄 0-120,直接用 Min-Max 缩放到 [0,1] 就行。

举个例子,我在做电商用户画像时,遇到过“购买次数”这个特征。大部分用户买 1-5 次,少数大客户买几百次。直接归一化后,普通用户和头部用户被压在一起,模型根本分不清。后来我做了 Log1p 变换,效果立竿见影。

import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler, MinMaxScaler

# 模拟长尾分布
purchase_counts = np.array([1, 2, 3, 5, 10, 50, 200, 500])

# 错误做法:直接 Min-Max
scaler = MinMaxScaler()
bad_result = scaler.fit_transform(purchase_counts.reshape(-1, 1))

# 正确做法:先 Log 变换
log_counts = np.log1p(purchase_counts)
good_result = StandardScaler().fit_transform(log_counts.reshape(-1, 1))

print("原始值:", purchase_counts)
print("直接归一化:", bad_result.flatten().round(3))
print("Log+标准化:", good_result.flatten().round(3))

小技巧:对于稀疏的数值特征(比如很多 0),可以考虑做“是否为空”的二值特征,再配合原始值一起喂给模型。我经常这么干,效果很稳。

4.3 类别型特征:编码的艺术

类别型特征的处理,是特征工程里最容易出彩也最容易翻车的地方。我总结了几种常见编码方式,各有各的适用场景。

编码方式 适用场景 优点 缺点
One-Hot 编码 类别少(< 10 个) 无偏、模型友好 维度爆炸
Label Encoding 有序类别(如等级) 节省空间 引入虚假顺序
Target Encoding 高基数类别(如用户ID) 捕捉目标相关性 容易过拟合
Frequency Encoding 类别分布差异大 简单、稳定 丢失类别语义

我曾经在一个广告点击率预估项目里,遇到“广告主 ID”这个特征,有几十万个不同值。One-Hot 直接炸内存,Label Encoding 又学出奇怪规律。最后我用 Target Encoding + 交叉验证平滑,才把效果稳住。

避坑指南:Target Encoding 一定要在训练集上做,并且要用 K-Fold 计算,防止数据泄露。我曾经偷懒直接在全体数据上算均值,结果线上效果崩得一塌糊涂。

4.4 文本型特征:从词袋到语义

文本型特征在离线流水线里,通常不是做深度学习那种端到端,而是提取统计特征或预训练向量。

我个人习惯分三步走:

  1. 清洗:去停用词、特殊符号、统一小写。
  2. 向量化:根据数据量选择方法。
    • 数据量小(万级):TF-IDF,简单可解释。
    • 数据量大(百万级):Word2Vec 或 FastText,训练快。
    • 追求效果:用预训练的 Sentence-BERT,直接拿向量。
  3. 聚合:如果文本是用户历史行为描述,需要按用户聚合,取均值或拼接。
from sklearn.feature_extraction.text import TfidfVectorizer

docs = [
    "这个商品质量很好,推荐购买",
    "物流太慢了,差评",
    "性价比很高,值得入手"
]

vectorizer = TfidfVectorizer(max_features=100, stop_words='english')
tfidf_matrix = vectorizer.fit_transform(docs)

print("特征词:", vectorizer.get_feature_names_out()[:10])
print("TF-IDF 矩阵形状:", tfidf_matrix.shape)

经验之谈:在离线批量生产里,文本特征最好提前计算好,存成向量文件。每次训练直接加载,别在流水线里实时做分词和向量化,太慢了。

4.5 特征理解:先看分布,再谈建模

特征理解不是玄学,是实打实的工程步骤。我每次拿到新数据,都会做三件事:

  • 单变量分析:看分布、看缺失、看异常值。用直方图或箱线图扫一遍。
  • 双变量分析:看特征与目标的关系。数值型用散点图或相关系数,类别型用分组统计。
  • 业务校验:这个特征的含义是什么?它的取值范围合理吗?比如“年龄=200”这种,明显是脏数据。

我记得有一次,一个特征“用户活跃天数”出现了负数。团队里有人直接取值,觉得“反正模型能学”。我坚持查了数据源,发现是日志时间戳解析错了。你看,不理解特征,再好的模型也是白搭。

核心观点:特征理解不是一次性工作。每次数据源变更、业务调整,都要重新审视特征。我见过太多项目,上线半年后特征分布已经漂移了,模型还在用老参数跑。

4.6 本章知识体系

下面这张图,是我对本章知识结构的总结。你可以把它当作一个检查清单,做特征工程时对照着来。

特征分类与理解知识体系 特征工程基础:特征分类与理解 特征分类 数值型特征 类别型特征 文本型特征 归一化/标准化 分箱/离散化 Log变换 缺失值处理 One-Hot Label Encoding Target Encoding Frequency Encoding TF-IDF Word2Vec BERT Embedding 文本清洗 特征理解:分布 → 关系 → 业务校验 核心原则:先理解特征,再选择编码方式 不同特征类型对应不同的处理流水线