类型特征编码9大策略
本文主要是分享类型特征编码的9大策略
来自公众号:宅码,仅供个人学习使用
背景
当我们预处理数据时,碰到类别型变量,需要将它们编码转换后才能输入进模型当中。按照不同的划分标准,类别型变量有:
● 按照类别是否有序:有序和无序的类别特征。
● 按照类别数量:高基类和低基类的类别特征。
针对不同的类别特征和任务,可选的类别特征编码方法也不一样。本文主要介绍常见且好用的类别编码方法,希望对大家有所帮助。
方法
1. 标签编码(Label Encoder)
标签编码就是简单地赋予不同类别,不同的数字标签。属于硬编码,优点是简单直白,网上很多说适用于有序类别型特征,不过如果是分类任务且类别不多的情况下,LGBM只要指定categorical_feature也能有较好的表现。
但不建议用在高基类特征上,而且标签编码后的自然数对于回归任务来说是线性不可分的。
1 | from sklearn.preprocessing import LabelEncoder |
2. 哈希编码(Hash Encoder)
哈希编码是使用二进制对标签编码做哈希映射。好处在于哈希编码器不需要维持类别字典,若后续出现训练集未出现的类别,哈希编码也能适用。但按位分开哈希编码,模型学习相对比较困难。
1 | # !pip install category_encoders |
3. 独热编码(One-hot Encoder)
独热编码能很好解决标签编码对于回归任务中线性不可分的问题,它采用N位状态寄存器来对N个状态进行编码,简单来说就是利用0和1表示类别状态,它转换后的变量叫哑变量(dummy variables)。
同样地,它处理不好高基数特征,基类越大会带来过很多列的稀疏特征,消耗内存和训练时间。
1 | x = pd.DataFrame({'gender':['male', 'female', 'male']}) |
4. 计数编码(Count Encoder)
计数编码也叫频次编码。就是用分类特征下不同类别的样本数去编码类别。清晰地反映了类别在数据集中的出现次数,缺点是忽略类别的物理意义,比如说两个类别出现频次相当,但是在业务意义上,模型的重要性也许不一样。
1 | import category_encoders as ce |
5. 直方图编码(Bin Encoder)
直方图编码属于目标编码的一种,适用于分类任务。它先将类别属性分类,然后在对应属性下,统计不同类别标签的样本****占比进行编码。直方图编码能清晰看出特征下不同类别对不同预测标签的贡献度[1]。
缺点在于:使用了标签数据,若训练集和测试集的类别特征分布不一致,那么编码结果容易引发过拟合。此外,直方图编码出的特征数量是分类标签的类别数量,若标签类别很多,可能会给训练带来空间和时间上的负担。直方图编码样例如下图所示:
1 | import pandas as pd |
6. WOE编码
WOE(Weight of Evidence,证据权重)编码适用于二分类任务,WOE表明自变量相对于因变量的预测能力。由于它是从信用评分世界演变而来的,它通常被描述为区分好客户和坏客户的衡量标准。“坏客户”是指拖欠贷款的客户。和“优质客户”指的是谁偿还贷款的客户。
据知乎主@马东什么[3]指出,WOE存在几个问题:
(1) 分母可能为0.
(2) 没有考虑不同类别数量的大小带来的影响,可能某类数量多,但最后计算出的WOE跟某样本数量少的类别的WOE一样。
(3) 只针对二分类问题。
(4) 训练集和测试集可能存在WOE编码差异(通病)。
对于问题1,源码[4]加入regularization(默认值为1)。
1 | # Create a new column with regularized WOE. |
对于问题2,可以考虑使用IV(Information Value),可以看作对WOE的加权,公式如下:
WOE和IV的区别和联系[2]是:
(1) WOE describes the relationship between a predictive variable and a binary target variable.
(2) IV measures the strength of that relationship.
扩展:IV常会被用来评估变量的预测能力,用于筛选变量:
对于问题3,可以考虑借鉴直方图编码的思路,将多分类标签,独热后依次进行WOE编码。[3],而对于问题4,暂时无解。
1 | from category_encoders import WOEEncoder |
7. 目标编码(Target Encoder)
2001年Micci等人提出的目标编码[5],是一种有监督编码方法,适用于分类和回归任务中,高基类无序类别特征。
图3:后验概率编码
源码[6]如下:
1 | # 默认参数:min_samples_leaf=1, smoothing=1.0 |
目标编码的好处是结合了先验概率和后验概率去编码,但由于概率是直接使用标签数据计算得到的,所以会引发过拟合问题。
1 | from category_encoders import TargetEncoder |
8. 平均编码(Mean Encoder)
平均编码是基于目标编码的改进版。它的2点改动如下:
(1) 权重公式:其实没有本质上的区别,可自行修改函数内的参数。
1 | ''' |
(2) 由于目标编码使用了标签,为了缓解编码带来模型过拟合问题,平均编码加入了K-fold编码思路,若分为5折,则用1-4折先fit后,再transform第5折,依次类推,将类别特征分5次编码出来。坏处是耗时。
1 | # :param n_splits: the number of splits used in mean encoding. 默认n_splits=5 |
代码过长,这里不予展示,详情请见[7]:
1 | # 代码过长,这里不予展示,详情请见[7] |
9. 模型编码(Model Encoder)
之前我写过文章【务实基础】CatBoost,里面有提到模型自带的类别特征编码。目前GBDT模型中,只有LGBM和CatBoost自带类别编码。
LGBM的类别编码采用的是GS编码(Gradient Statistics),将类别特征转为累积值$\frac{\operatorname{sum}(\text { gradient })}{\operatorname{sum}(\text { hessian })}$ (见下图)(一阶偏导数之和/二阶偏导数之和)再进行直方图特征排序。使用起来也很简单,定义lgb数据集时,指定categorical_feature。
累积值:$\frac{\operatorname{sum}(\text { gradient })}{\operatorname{sum}(\text { hessian })}$
1 | train_data = lgb.Dataset(data, label=label, feature_name=['c1', 'c2', 'c3'], categorical_feature=['c3']) |
据官方文档介绍,GS编码比独热编码快大概8倍速度。而且文档里也建议,**当类别变量为高基类时,哪怕是简单忽略类别含义或把它嵌入到低维数值空间里,只要将特征转为数值型,一般会表现的比较好。**就个人使用来讲,我一般会对无序类别型变量进行模型编码,有序类别型变量直接按顺序标签编码即可。
虽然LGBM用GS编码类别特征看起来挺厉害的,但是存在两个问题:
● 计算时间长:因为每轮都要为每个类别值进行GS计算。
● 内存消耗大:对于每次分裂,都存储给定类别特征下,它不同样本划分到不同叶节点的索引信息。
所以CatBoost使用Ordered TS编码,既利用了TS省空间和速度的优势,也使用Ordered的方式缓解预测偏移问题。详情可见我历史文章。
总结
我这里总结了以上类别编码方法的区别:
总结来说,关于类别特征,有以下心得:
(1) 统计类编码常常不适用于小样本,因为统计意义不明显。
(2) 当训练集和测试集分布不一致时,统计类编码往往会有预测偏移问题,所以一般会考虑结合交叉验证。
(3) 编码后特征数变多的编码方法,不适用于高基类的特征,会带来稀疏性和训练成本。
(4) 没有完美的编码方法,但感觉标签编码、平均编码、WOE编码和模型编码比较常用。
参考资料
[1] 特征工程之Histogram编码, 博文:
https://blog.csdn.net/Chengliangyao/article/details/82623775
[2] 风控模型—WOE与IV指标的深入理解应用 - 求是汪在路上, 知乎:
https://zhuanlan.zhihu.com/p/80134853
[3] 特征编码方法总结—part1 - 马东什么, 知乎:
https://zhuanlan.zhihu.com/p/67475635
[4] woe.py, 源码:
https://github.com/scikit-learn-contrib/category_encoders/blob/master/category_encoders/woe.py
[5] Micci-Barreca, D. (2001). A preprocessing scheme for high-cardinality categorical attributes in classification and prediction problems. ACM SIGKDD Explorations Newsletter, 3(1), 27-32.
[6] target_encoder - category_encoders, 源码:
[7] 平均数编码:针对某个分类特征类别基数特别大的编码方式, 博文:
https://www.cnblogs.com/wzdLY/p/9639519.html
[8] 证据权重 (WOE) 和信息价值 (IV) - python风控模型, 知乎: