云霞资讯网

别只会One-Hot了!20种分类编码技巧让你的特征工程更专业

机器学习模型处理不了原始文本。无论是线性回归、XGBoost还是神经网络,遇到 "red"、"medium"、"CA"

机器学习模型处理不了原始文本。无论是线性回归、XGBoost还是神经网络,遇到 "red"、"medium"、"CA" 这类分类变量都没法直接处理。所以必须把它们转成数字这个过程就是分类编码。

大家入门时肯定都学过独热编码或序数编码,但编码方法其实非常多。目标编码、CatBoost编码、James-Stein编码这些高级技术,用对了能给模型带来质的飞跃,尤其面对高基数特征的时候。

编码到底有多重要

拿 "Toyota" 举例,它本身没有数值含义,但模型只认数字:

{"Toyota": 0, "Ford": 1, "Honda": 2}

或者写成向量形式:

[0, 1, 0]

更高级的做法是直接编码成目标相关的数值:

Toyota → +0.12 mean adjusted uplift in target

编码方式选得好不好,直接影响模型准确率、可解释性、过拟合程度、训练速度、内存占用,还有对稀有类别的处理能力。

示例代码准备

后面所有例子都基于这个简单数据集:

import pandas as pd  from sklearn.model_selection import train_test_split  import category_encoders as ce  from sklearn.linear_model import LogisticRegression  df = pd.DataFrame({      "color": ["red", "blue", "green", "green", "blue", "red"],      "city": ["NY", "LA", "NY", "SF", "LA", "NY"],      "target": [1, 0, 1, 0, 0, 1]  })  X = df.drop("target", axis=1)  y = df["target"]

1、序数编码 Ordinal Encoding

最简单粗暴的方法,给每个类别分配一个整数。red是0,blue是1,green是2。

XGBoost、LightGBM这类树模型用这个就够了。另外当类别本身有顺序含义(比如small/medium/large)时也很合适。

encoder = ce.OrdinalEncoder(cols=["color"])  X_trans = encoder.fit_transform(X, y)

2、独热编码 One-Hot Encoding

每个类别单独开一列,是就标1,不是就标0。

线性回归、逻辑回归、神经网络经常用这个。不过类别太多的话列数会爆炸,低基数特征才适合。

encoder = ce.OneHotEncoder(cols=["color"], use_cat_names=True)  X_trans = encoder.fit_transform(X)

3、 二进制编码 Binary Encoding

把类别索引转成二进制。比如索引5变成101,拆成三列。

这个方法在类别数量中等偏多(50-500个)的时候很好使,既保持了稀疏性又比独热编码省内存。

encoder = ce.BinaryEncoder(cols=["city"])  X_trans = encoder.fit_transform(X)

4、Base-N编码

二进制编码的泛化版本,可以用任意进制。base=3时,索引5就变成 "12"。想精细控制输出维度的话可以试试。

encoder = ce.BaseNEncoder(cols=["city"], base=3)  X_trans = encoder.fit_transform(X)

5、哈希编码 Hashing Encoding

用哈希函数把类别映射到固定数量的列上。速度极快,输出宽度固定,也不用存储类别映射表。

缺点就是:会有哈希冲突而且结果不可解释。

encoder = ce.HashingEncoder(cols=["city"], n_components=8)  X_trans = encoder.fit_transform(X)

6、Helmert编码

正交对比编码的一种,每个类别跟它后面所有类别的均值做比较。统计建模和分类对比回归分析会用到。

encoder = ce.HelmertEncoder(cols=["color"])  X_trans = encoder.fit_transform(X, y)

7、求和编码 Sum Encoding

也叫偏差编码。每个类别跟总体均值比较,而不是跟某个基准类别比。

encoder = ce.SumEncoder(cols=["color"])  X_trans = encoder.fit_transform(X, y)

8、多项式编码 Polynomial Encoding

给有序类别生成线性、二次、三次对比项。如果怀疑类别对目标有非线性影响,可以考虑这个。

encoder = ce.PolynomialEncoder(cols=["color"])  X_trans = encoder.fit_transform(X, y)

9、向后差分编码 Backward Difference Encoding

每个类别跟前面所有类别的均值比较,跟Helmert正好相反。

encoder = ce.BackwardDifferenceEncoder(cols=["color"])  X_trans = encoder.fit_transform(X, y)

10、计数编码 Count Encoding

直接用类别出现的次数替换类别值。

高基数特征用这个效果不错,计算快、结果稳定。只要在训练集上fit就不会有数据泄露问题。

encoder = ce.CountEncoder(cols=["city"])  X_trans = encoder.fit_transform(X)

11. 目标编码 Target Encoding

把每个类别替换成该类别下目标变量的均值。

威力很大但有个坑,就是容易造成目标泄露。必须配合平滑处理或者用交叉验证的方式来做。

encoder = ce.TargetEncoder(cols=["city"])  X_trans = encoder.fit_transform(X, y)

12、CatBoost编码

CatBoost编码是目标编码的改良版。编码每一行时只用它前面的行来计算,这样就大大降低了泄露风险。

这是目前最安全的目标编码方案,高基数特征、时序数据都能用,效果很稳。

encoder = ce.CatBoostEncoder(cols=["city"])  X_trans = encoder.fit_transform(X, y)

13、留一法编码 Leave-One-Out Encoding

计算类别的目标均值时把当前行排除掉。既保留了目标编码的效果,又减轻了泄露。

encoder = ce.LeaveOneOutEncoder(cols=["city"])  X_trans = encoder.fit_transform(X, y)

14、M估计编码 M-Estimate Encoding

用贝叶斯思想对目标编码做平滑。

高基数和噪声目标场景下表现不错。

encoder = ce.MEstimateEncoder(cols=["city"], m=5)  X_trans = encoder.fit_transform(X, y)

15、WOE证据权重编码

这是信用评分领域的老朋友了。

逻辑回归配WOE是经典组合,可解释性很强。

encoder = ce.WOEEncoder(cols=["city"])  X_trans = encoder.fit_transform(X, y)

16、James-Stein编码

基于James-Stein估计的收缩编码器。能有效降低方差,做分类变量回归时效果很好。

encoder = ce.JamesSteinEncoder(cols=["city"])  X_trans = encoder.fit_transform(X, y)

17、GLMM编码

用广义线性混合模型来编码。处理层次结构数据或者类别组很大的时候可以一试。

encoder = ce.GLMMEncoder(cols=["city"])  X_trans = encoder.fit_transform(X, y)

18、分位数编码 Quantile Encoding

不用均值,用目标分布的分位数来编码。

encoder = ce.QuantileEncoder(cols=["city"], quantile=0.5)  X_trans = encoder.fit_transform(X, y)

19、RankHot编码

独热编码的变体,列按类别频率排序。对树模型友好。

encoder = ce.RankHotEncoder(cols=["city"])  X_trans = encoder.fit_transform(X)

20、格雷编码 Gray Encoding

用格雷码表示类别,相邻编码只差一位。

encoder = ce.GrayEncoder(cols=["city"])  X_trans = encoder.fit_transform(X)

怎么选编码器

低基数(<10个类别):独热、二进制、序数都行。统计模型的话可以试试求和、Helmert、多项式编码。

中等基数(10-100):二进制、BaseN、CatBoost、带平滑的目标编码。

高基数(100-50000):计数编码、CatBoost编码(首选)、留一法、M估计、带交叉验证的目标编码,内存紧张就用哈希编码。

常见的坑

目标编码泄露:用CatBoost编码、交叉验证或留一法来规避。

树模型误读序数整数:树模型可能会把序数编码的数字当连续变量处理,换成独热或目标编码更稳妥。

独热编码维度爆炸:类别太多就别用独热了,换二进制、BaseN或哈希。

稀有类别噪声:M估计、James-Stein或目标平滑能缓解这个问题。

总结

分类编码是特征工程里最容易被忽视却又最能出效果的环节。scikit-learn自带的编码器只是冰山一角,category_encoders这个库才是真正的百宝箱:统计编码、贝叶斯编码、哈希编码、对比编码应有尽有,用好了模型效果能上一个台阶。

https://avoid.overfit.cn/post/899f24e435ac4733ac4b981a0b3629f4

作者:Abish Pius