高数量类别特征(high-cardinality categorical attributes)的预处理方法
high-cardinality categorical attributes,从字面上理解,即对于某个category特征,不同值的数量非常多,这里暂且把它叫做高数量类别属性。反之,即低数量类别属性(low-cardinality)
对于低数量类别属性,通常在data science中采用的方式是将其转化为one-hot编码,即给每一个类别增加一个特征。但是当类别数量增加的时候,ont-hot编码增加的特征也在增加。所以,one-hot编码无法适用于高数量特征属性。
基本方法(clustering)
目前有一个常见的方法处理这个问题叫做clustering。即将原始的1-to-N的mapping问题变成1-to-k的mapping问题。(k<<N)
为了达到这个目标,这个高数量类别属性首先将依据target的值grouping成k个类(clusters),然后再依据这个grouping的结果进行one-hot编码。
不得不说,这个方法最大程度的保留了原始数据的信息。grouping的方法有一些:其中一个就是Hierarchical clustering Algorithm,它使用一个基于target的的统计距离来grouping,grouping的标准也可以度量每合并两个clusters带来的信息增量的影响,如gain ratio
。两个距离最短的clusters将会被合并成一个。这个过程一直被迭代,直到迭代过程没有明显的改善。
这个问题更多解释可见《统计学习方法》5.2.2
cate_A | target |
---|---|
a/b/c.... | 1/0 |
... | ... |
对上述数据集, cate_A为高数量特征属性,其处理方法的伪代码如下:
while improvment > epsilon or unique(cate_A) >= k:
compute Conditional Entropy Target|cate_A: THA1
for _ :
Merge two random labels in cate_A into one
compute Conditional Entropy Target|cate_A: THA
Find the two labels that then compute to a minimum THA2
improvement = THA1 - THA2
上述方法其实很常见,且被运用在决策树的C4.5方法中进行特征选择。
这个方法在最后需要ont-hot编码。
另一方法(smoothing)
Smoothing,简单来说,就是将原来独立的高数量类别特征的每个值映射到概率估计上。基本来讲,这个预处理方法将原始的值放置到实际的机器学习模型之前先通过一个简单的特征处理模型(如贝叶斯模型)。
下面以binary target为例进行方法分析:
当target属性 \(Y\in\{0,1\}\)时,假设要处理的特征为X,该特征的每一个不同的值为\(X_{i}\)。我们要做的是, 将高数量类别特征将映射到一个标量\(S_{i}\)中,\(S_{i}\)代表一个条件概率,即
$X_{i} \to S_{i} \cong P(Y|X=X_{i}) ---(1)$
注意到\(S_{i}\)代表的是条件概率,那么他的值被归一到了0和1之间,这对于神经网络模型也是一个好的预处理。
下一步就是概率估计的过程。我们假设数据集被分成了\(n_{TR}\)个训练集和\(n_{TS}\)个测试集。因为这个概率估计成为了模型训练的一部分,所以只有训练集的数据被使用。注意到不是所有的X的可能值都会出现在训练集中,有的值可能只出现在测试集或者新进来的数据中。所以,这个映射过程必须要能够处理这个特征的不可预见性的值。
如果该特征某个值如\(X=X_{i}\)出现的数量足够多,那么这个概率估计可以这样计算:
$S_{i}=\frac{n_{iY}}{n_{i}} ---(2)$
这是一个后验概率的计算过程。然而不幸的是,特征的值的数量分布通常是不均匀的,有很多值的数量非常少。所以这种使用\(P(Y|X=X_{i})\)的直接估计是不太可靠的。
为了减小这种小数量值的影响,\(S_{i}\)的计算可能被分成两个概率的组合。后验概率的计算如公式(2),先验概率的计算如\(P(Y) = \frac{n_{Y}}{n_{TR}}\)。整个组合计算公式为:
$S_{i}=\lambda(n_{i})\frac{n_{iY}}{n_{i}}+(1-\lambda(n_{i}))\frac{n_{Y}}{n_{TR}} ---(3)$
\(n_{Y}\)代表在整个数据集中\(Y=1\)的数量。\(\lambda(n_{i})\)是一个在0-1之间的单调递增函数。
原理:一方面,当特征的某个值的数量很多,即\(\lambda\cong1\)时,公式即为(2),计算后验概率。另一方面,当特征的某个值的数量很少时,即\(\lambda\cong0\)时,公式前项为0,只计算先验概率。
所以,关键我们怎么选取\(\lambda(n_{i})\)这个函数呢?有一个典型函数如下:
$\lambda(n)=\frac{1}{1+\exp^{-\frac{n-k}{f}}} ---(4)$
这个公式是一个s形状的函数,当 n=k 时值为 0.5 。
参数 f 控制函数在转折处的斜率,决定了先验概率和后验概率之间的平衡。如果\(f\to\infty\),那么公式(3)变为一个硬间隔,即先验概率和后验概率各占0.5。
参数 k 决定于我们允许的特征值数量的最小值的一半。so important!
我们在来看看经验贝叶斯估计(Empirical Bayes estimation) 的一般公式:
$P=B_{i}y_{i}+(1-B_{i})\overline{y}---(5)$
\(\overline{y}\)是先验概率,\(y_{i}\)是经验后验概率。收缩系数\(B_{i}\)根据不同的估计方法有不同的形式。当所有概率分布服从高斯分布的时候:
$B_{i}=\frac{n_{i}\tau^{2}}{\sigma^{2}+n_{i}\tau^{2}}---(6)$
\(\sigma^{2}\)是值的方差,\(\tau^{2}\)是样本方差。事实上,公式(6)是公式(4)的一般形式。
以下是我在kaggle中看到大佬对该方法的coding实现,借以参考,帮助理解:
def add_noise(series, noise_level):
return series * (1 + noise_level * np.random.randn(len(series)))
def target_encode(trn_series=None,
tst_series=None,
target=None,
min_samples_leaf=1,
smoothing=1,
noise_level=0):
"""
trn_series : training categorical feature as a pd.Series
tst_series : test categorical feature as a pd.Series
target : target data as a pd.Series
min_samples_leaf (int) : minimum samples to take category average into account
smoothing (int) : smoothing effect to balance categorical average vs prior
"""
assert len(trn_series) == len(target)
assert trn_series.name == tst_series.name
temp = pd.concat([trn_series, target], axis=1)
# Compute target mean
averages = temp.groupby(by=trn_series.name)[target.name].agg(["mean", "count"])
# Compute smoothing
smoothing = 1 / (1 + np.exp(-(averages["count"] - min_samples_leaf) / smoothing))
# Apply average function to all target data
prior = target.mean()
# The bigger the count the less full_avg is taken into account
averages[target.name] = prior * (1 - smoothing) + averages["mean"] * smoothing
averages.drop(["mean", "count"], axis=1, inplace=True)
# Apply averages to trn and tst series
ft_trn_series = pd.merge(
trn_series.to_frame(trn_series.name),
averages.reset_index().rename(columns={'index': target.name, target.name: 'average'}),
on=trn_series.name,
how='left')['average'].rename(trn_series.name + '_mean').fillna(prior)
# pd.merge does not keep the index so restore it
ft_trn_series.index = trn_series.index
ft_tst_series = pd.merge(
tst_series.to_frame(tst_series.name),
averages.reset_index().rename(columns={'index': target.name, target.name: 'average'}),
on=tst_series.name,
how='left')['average'].rename(trn_series.name + '_mean').fillna(prior)
# pd.merge does not keep the index so restore it
ft_tst_series.index = tst_series.index
return add_noise(ft_trn_series, noise_level), add_noise(ft_tst_series, noise_level)
两个方法比较
smoothing方法可以只要通过对本地数据集的操作就可完成预处理,而clustering方法需要更复杂的算法而且可能导致信息量的减少(因为最后仍然需要进行one-hot编码)。
当然,对于smoothing方法来说, \(\lambda\)函数的选择直接影响到了结果,可能不同的\(\lambda\)函数可以适用于不同的分支领域,这都需要实验的考证。
PS:该篇文章主要基于对文献的翻译,这个翻译过程终于让我体会到翻译的不易了,以后我再也不会抱怨专业书翻译者的不专业了。
Reference:
- A Preprocessing Scheme for High-Cardinality Categorical Attributes in Classification and Prediction Problems
- 《统计学习方法》.李航
- Python target encoding for categorical features
高数量类别特征(high-cardinality categorical attributes)的预处理方法的更多相关文章
- 对于高并发短连接造成Cannot assign requested address解决方法
https://www.cnblogs.com/dadonggg/p/8778318.html 感谢这篇文章给予的启发 在tcp四次挥手断开连接时,主动释放连接的一方最后会进入TIME_WAIT状态, ...
- 机器学习实战基础(十一):sklearn中的数据预处理和特征工程(四) 数据预处理 Preprocessing & Impute 之 处理分类特征:编码与哑变量
处理分类特征:编码与哑变量 在机器学习中,大多数算法,譬如逻辑回归,支持向量机SVM,k近邻算法等都只能够处理数值型数据,不能处理文字,在sklearn当中,除了专用来处理文字的算法,其他算法在fit的 ...
- 机器学习实战基础(九):sklearn中的数据预处理和特征工程(二) 数据预处理 Preprocessing & Impute 之 数据无量纲化
1 数据无量纲化 在机器学习算法实践中,我们往往有着将不同规格的数据转换到同一规格,或不同分布的数据转换到某个特定分布的需求,这种需求统称为将数据“无量纲化”.譬如梯度和矩阵为核心的算法中,譬如逻辑回 ...
- Sql Server本地高版本备份数据备份至远程低版本数据库方法
想要将Sqlserver高版本备份的数据还原到低版本SqlServer2008R2上去,但是这在SqlServer中是没法直接还原数据库的,通过以下方法可以顺利还原. 通过高版本生成sql脚本在低版本 ...
- 无法debug断点跟踪JDK源代码——missing line number attributes的解决方法
在项目工程->Properties->Java Build Path->Libraries中导入的JRE System Library库里,给jar包添加JDK源代码包后,能够直接打 ...
- VC6的工程转到VC2010或更高版本出现fatal error C1189编译错误的解决方法
以前也遇到过,当时解决了没写下来,这次正好又遇到了,就顺手写一下吧,别下次又忘记了. 当VC6的工程转到VC2010或更高版本时编译出现如下错误: c:\program files\microsoft ...
- 高并发下用pdo,文件排它锁,redis三种方法对比
<?php header('content-type:text/html;charset=utf-8'); // //无控制 // $DB_DSN = ' ...
- ArcGis基础——把类别代码替换成对应中文名称的方法
挂接! 上面是答案,展开一下就是做一个Excel对照表,就两列,代码与中文名称.然后用类别代码字段匹配挂接. 别傻傻找vbs/py代码,不知道拐个弯.
- 机器学习实战基础(十二):sklearn中的数据预处理和特征工程(五) 数据预处理 Preprocessing & Impute 之 处理分类特征:处理连续性特征 二值化与分段
处理连续性特征 二值化与分段 sklearn.preprocessing.Binarizer根据阈值将数据二值化(将特征值设置为0或1),用于处理连续型变量.大于阈值的值映射为1,而小于或等于阈值的值 ...
随机推荐
- SQL中内连接和外连接的问题!
本文系转载,版权归原作者所有. 如表 ------------------------------------------------- table1 | table2 | ...
- Read程序员的困境有感
看完这篇文章,我真的害怕了,码农遍地都是,我就是,改变从今天做起,不走码农生活 首先, 打造你自己的私人项目.你需要不断地打磨自己的技艺.如果工作本身并不能帮助你做到这一点,就捡起那些你感兴趣的问题, ...
- Aptana版本回滚的方法
最近Aptana对Django1.7的编译支持有点问题,开发环境必须使用Django1.6版本,今天看了一眼它的官网,版本已经到3.6.1,我的版本还是3.4.2,就checkupdate升级到3.6 ...
- Android Third Party Libraries and SDK's
http://javatechig.com/Android/android-third-party-libraries-sdks Over past few years, the age of mob ...
- Selenium调用webdriver.chrome()出错
问题描述: 今天因为在学习要使用selenium这个python库,我下载好了selenium,并且也Import成功了,但是在我使用webdirver.chorme()时,却提示PATH路径中没有c ...
- td默认文字超出后显示..,点击tr时td文字显示完整
做项目时,产品提的需求,table表格里面,每一列限制文字超出宽度后显示省略号,等点击td时,文字显示完整,今天整理了一下代码,积累一下 备注:1.邮箱和网址,在td里面不会自动换行,需要增加word ...
- 前端开发APP,从HBuilder开始~ 【转】
内容简介 介绍目前前端人员开发app的几种方法,具体介绍hbuilder开发app,一扇赞新的大门~ 无所不能的js 最开始js仅仅局限于网页上一些效果,操作网页内容等, 但是nodejs把js带入了 ...
- MIT算法导论课程
http://open.163.com/movie/2010/12/G/F/M6UTT5U0I_M6V2T1JGF.html
- JDK10都发布了,nio你了解多少?
前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 本来我预想是先来回顾一下传统的IO模式的,将传统的IO模式的相关类理清楚(因为IO的类很多). 但是,发现在整理 ...
- IOC 的理解与解释
IOC 是什么? Ioc-Inversion of Control,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不 ...