3. 特征编码技术:Label Encoding、One-Hot Encoding、Target Encoding 的原理与实战对比
特征编码,说白了就是把非数值型数据变成模型能吃的数字。我见过太多新手直接把「男」「女」扔进模型,结果跑出来的结果一塌糊涂。你想想看,模型底层全是矩阵运算,它哪认识什么「北京」「上海」?
这一章,咱们就把三种最常用的编码技术掰开揉碎讲清楚。我会结合自己踩过的坑,告诉你什么时候该用哪个。
3.1 为什么需要特征编码?
大部分机器学习算法只吃数值。决策树稍微好点,但像线性回归、SVM、神经网络这些,你给个字符串进去,它直接报错给你看。
我遇到过最离谱的一次,有个同事把「省份」列直接做 Label Encoding,结果模型学出了「北京 < 上海 < 广州」这种大小关系。嗯,地理老师看了想打人。
核心原则:编码方式决定了模型能学到什么样的信息。选错了,模型就学歪了。
3.2 Label Encoding(标签编码)
Label Encoding 最简单——给每个类别分配一个整数。比如「红=0, 绿=1, 蓝=2」。
什么时候用?
- 类别之间有明显的顺序关系(比如「小=0, 中=1, 大=2」)
- 树模型(决策树、随机森林、XGBoost)——它们不怕数值大小
- 类别数量特别多(几百上千个),One-Hot 会炸内存
什么时候千万别用?
- 类别之间没有顺序关系(颜色、国家、职业)
- 线性模型、神经网络——它们会当真去算「2 比 1 大」
我曾经踩过的坑:有一次做房价预测,把「街道名称」做了 Label Encoding。结果模型学出来「A街道=1, B街道=2, C街道=3」,它以为 C 街道的房子就该比 A 街道贵两倍。实际上这三个街道的房价差不多。后来换成 One-Hot,效果立刻好了。
# Label Encoding 示例
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
data['color_encoded'] = le.fit_transform(data['color'])
# 输出:红→0, 绿→1, 蓝→2
3.3 One-Hot Encoding(独热编码)
One-Hot 把每个类别变成一个独立的 0/1 列。比如「红=100, 绿=010, 蓝=001」。
优点:不引入任何顺序关系,模型不会学出虚假的大小关系。
缺点:列数爆炸。如果有 1000 个城市,就多出 1000 列。
我个人的习惯:
- 类别数 < 10 且没有顺序关系 → 直接用 One-Hot
- 类别数 10~50 → 看情况,内存够就用
- 类别数 > 50 → 考虑 Target Encoding 或者先做聚类再编码
# One-Hot Encoding 示例
import pandas as pd
data = pd.get_dummies(data, columns=['color'], prefix='color')
# 输出:color_red, color_green, color_blue 三列 0/1
小技巧:用 pd.get_dummies 时记得加 drop_first=True,扔掉第一列避免多重共线性。线性模型特别吃这一套。
3.4 Target Encoding(目标编码)
Target Encoding 用目标变量的均值来替换类别。比如「北京的房子均价 500 万,上海 600 万,那就用 500 和 600 来编码」。
这招在 Kaggle 竞赛里特别常见。我打比赛时,光靠 Target Encoding 就能把 CV 分数拉高 0.01~0.02。
优点:
- 不增加维度(一列换一列)
- 直接编码了类别与目标的关系
- 对树模型和线性模型都有效
致命缺点:容易过拟合。尤其是某个类别样本很少的时候,均值可能完全不准。
避坑指南:做 Target Encoding 必须用 K-Fold 交叉验证的方式计算均值。不能拿全量数据算完再分训练集和测试集——那叫数据泄露,模型在测试集上会死得很难看。
# Target Encoding 示例(带 K-Fold 平滑)
from category_encoders import TargetEncoder
encoder = TargetEncoder(cols=['city'], smoothing=10)
data['city_encoded'] = encoder.fit_transform(data['city'], data['price'])
3.5 三种编码的对比
| 特性 | Label Encoding | One-Hot Encoding | Target Encoding |
|---|---|---|---|
| 维度变化 | 不变 | 暴增 | 不变 |
| 引入顺序关系 | 是 | 否 | 否 |
| 过拟合风险 | 低 | 低 | 高(需 K-Fold) |
| 适用模型 | 树模型 | 所有模型 | 所有模型 |
| 类别数 > 100 | 推荐 | 不推荐 | 推荐 |
| 类别有顺序 | 推荐 | 可用 | 可用 |
3.6 实战选择策略
我一般按这个流程走:
- 先看类别有没有顺序——有顺序直接 Label Encoding
- 再看类别数量——少于 10 个且无顺序,One-Hot
- 10~50 个——试 One-Hot,内存炸了就换 Target Encoding
- 超过 50 个——Target Encoding 或者先做频次编码
- 如果是树模型——Label Encoding 往往就够用,省事
- 如果是线性模型或神经网络——优先 One-Hot 或 Target Encoding
我的经验:别死磕一种编码。我经常在同一个数据集里混用——有序的用 Label,无序且少的用 One-Hot,无序且多的用 Target Encoding。模型效果往往比只用一种好。
3.7 知识体系总览
下面这张图把三种编码的核心逻辑和适用场景串起来了。你可以把它当成决策地图。
3.8 一个真实案例
去年我做了一个电商用户画像项目。用户所在地有 300 多个城市,直接用 One-Hot 的话,特征矩阵会多出 300 列。训练集 50 万样本还好,但线上推理时内存直接爆了。
我当时的做法:
- 先把城市按购买力分成 5 档(一线、二线、三线、四线、其他)——这是 Label Encoding
- 再对原始城市做 Target Encoding,用 K-Fold 计算平均客单价
- 两个特征一起喂给 LightGBM
结果 AUC 从 0.72 涨到了 0.78。说白了,组合编码往往比单一编码效果好。
我的建议:别把编码方式当成固定套路。多试几种组合,用验证集效果说话。特征工程没有银弹,只有不断试错。