【FE】处理Categorical Features

之前的文章介绍了Numerical features,今天来介绍Categorical features。Categorical features 在我们的生活中也很常见,小到一年四季,一周七天,色彩颜色种类;大到用户id,CTR中的广告对象,这些都是属于Categorical features。

区别Numerical和Categorical的方法比较简单,那就是看数值的magnitude是否有意义,能否根据这个对数据进行排序。

我今天对categorical feature的介绍,也大致分为这么两类:普通的categorical features和大型的categorical features。

 

一. 普通的categorical features

(1) Label encode

对categorical feature编码最简单的想法就是,每一个类别分配一个整数。比如类别1分配1,类别2分配2,等等。

这种想法的问题在于,有些毫无关系的类别,在这种编码条件下,反而会得到一个本来不存在的大小关系。

以使用欧氏空间的模型为例,这种空间衡量相似度的一种方法就是通过距离。那显然3对于1的相似度就会小于2对于1的相似度。然而大部分情况下1,2,3这三种类别应该是相互独立的。所以这种编码方法在编码那些具有独立类别的特征时,会有一定问题

在一些特定的情况下,我们还是可以使用这种编码方法,比如对年龄的编码。0~10岁为类别1, 11~20岁为类别2, 21~30岁为类别3。这里给这三个年龄范围赋予了三个存在大小关系的数字,事实上,他们确实存在着1<2<3这种大小关系。

(2)One-hot encode

独热编码,这种编码解决了label encode的一个问题。那就是免去了不同类别之间的相关性。

还是以欧式空间为例,我们衡量向量之间的相似度的方法是通过余弦相似度,余弦相似度的计算就要通过dot product,显然不同类别之间的one-hot vector点乘的结果是0,换而言之,其余弦相似度也是0,他们相互独立。

但是这种编码方法有一个问题,那就是它存在一个线性约束:对所有样本而言,老特征被独热编码后,产生所有新特征的值,加起来必定为1。

$$f_1 + f_2 + f_3 + ... = 1$$

这个问题就比较大了,对于使用线性模型的问题而言,训练后产生的模型将不唯一,这将造成我们解释上的困难

(3)dummy code

一个比较简单的方法就是去掉一个新特征,被去掉的特征叫做reference categories。之所以叫categories,是因为你新的特征本来就是有旧特征衍生出来的。

这个dummy code看上去比较眼熟,事实上,这就是我们pandas里面用的get_dummies。然而如果不指定特定的关键字,这个api返回的将仅仅是one-hot labels,正确的方法是添加drop_first=True关键字参数

(4)effect code

effect code可以视为是一种拓展的方法,因为pandas里面并没有简单的effect code的实现方法。

dummy code的一个问题就是,模型的可解释性不强,你最后产生的intercept和coefficient是什么意思?难以解释。所以我们就有了effect code,这个编码方案主要是用来解决模型的可解释问题。

它的核心思想就是:原来样本是reference category的,在dummy code编码下,所有新特征的值都是0。但是在effect code棉麻下,所有新特征的值都是1,这个会有什么后果呢?我们来看看下面的推导:

首先,对于线性模型而言,它的公式是:a_1 x_1 + a_2 x_2 + ... + a_n x_n + b = y

是对于非reference category的样本,它符合:a_i x_i + b = y_i (i = 1,2, ..., n - 1)

而对于reference category的样本,它符合:-\sum_{i=1}^{n} x_i + b = y_n

将所有的式子全部加起来,我们将得到:b = \dfrac{\sum_{i = 1}^{n} y_i }{n}

也就是说,我们的intercept是目标变量的平均值,而我们之前的系数,代表的则是属于该类样本和我们目标变量平均值的偏差

(5) 总结

编码方式优点缺点
label encode简单只能处理有序特征
one-hot encode

变量之间没有相关性;

可以用全0表示缺省数据

拥有线性约束
dummy code免除了线性约束不能表示缺省数据
effect code免除线性约束;解释性好

不能表示缺省数据;

不再是稀疏矩阵了

 

二. 大型Category features

除了简单的category features以外,我们还有一些例如userID这种。一个categorical feature里面拥有多个类别的特征。这时候我们就不能采用之前讲述的方法来进行编码了。不然,这会导致我们特征维数的爆炸,从而出现所谓Curse of the Dimensionality现象,这种现象的后果就是:样本的性能不足,模型的准确率开始受到影响,从而导致结果的过拟合。一种最简单的方法就是啥都不做,当然还有其它的,我下面介绍的就是。

(1)Feature hashing

这种方法和我们在数据结构中看到的hashing一模一样,核心思想就是将N维的原特征映射到M维的新特征里面,映射的方法就是通过哈希函数:

\phi _j =\sum_{h(i)=j}x_i

这个是最简单的哈希函数,稍微做点解释,其中phi就是新的特征,h是哈希函数,x则是旧的特征。这个公式的含义是:新特征的值由多个冲突的原特征经过映射后相加而成

不过在实际运用上,为了保证内积的无偏性(之所以我们还要保证内积的无偏性,一个重要的理由就是,在做CTR预估的时候,我们一般都是手工FE的,有些情况下我们还要对特征做笛卡尔积。),我们一般还要加一个符号。具体的见下图:

你这个特征哈希的优点就比较明显了:

  1.  虽然有哈希冲突的现象发生,但是实际上并不会损失多少信息。
  2. 对于在线学习非常友好。

缺点跟哈希本省的缺点也很类似,那就是被哈希过的特征,失去了可解释性

(2)Bin counting

这种做法的思想就是使用条件概率来代替原来的类别变量,使用基于概率的统计信息来确定我们在建模工作中要预测的值和实际目标。一个简单的例子是基于过去的IP地址历史数据和DDOS攻击中使用的数据;我们可以为任何IP地址引起的DDOS攻击建立概率值。使用此信息,我们可以对输入特征进行编码,该特征描述了如果将来出现相同的IP地址,导致DDOS攻击的概率值是多少。该方案需要历史数据作为先决条件,并且需要精心设计。

 

三. 大型categorcal features所面临的问题

(1)稀有类别的处理——back-off方法

大型分类型特种中,我们往往会碰到稀有类别的情况,所谓稀有类别就是属于该类别的样本数目特别少,这种样本的存在就是在浪费我们的count table,一个最简单的想法就是把他们全部归到一个新的类里面。

具体的做法就是,设置一个threshold,低于这个threshold的类全部归为一个back-off bin的箱子里面

(2)稀有类别的处理——count-min方法

还有一种更加先进的做法,那就是使用count-min方法,这种方法也是软件工程中常见的一种方法,没想到在机器学习中依然适用。

这种做法的流程如下:

  1.  准备多个数组和多个哈希函数
  2. 存数据的时候,利用多个哈希函数分别映射到多个数组中。每个数组对应的索引+1
  3. 取数据的时候,因为有哈希碰撞的情况存在,我们选取碰撞次数最少的单元格来作为我们的结果。

这个就是我们count-min方法的由来。

(3)数据泄露问题

虽然说数据泄露的问题可能存在与你任何一个操作的过程中(诚然,笔者曾屡次犯过这种错误),但是既然这本书提到了这一点,我们也来稍微介绍一下这个问题。

所谓数据泄露,就是在训练模型的时候用到了训练集以外的信息。这个后果就是你在测试的时候,会对你测试集上的预估过于乐观。最简单的例子就是对这个数据集进行训练,然后将数据集73分,在测试集上测试。你想啊,你之前已经获得了测试集的信息,为什么还能在测试集上进行测试呢?

还有一个比较隐晦的错误,那就是cross validation的时候,提前对整个数据集进行预处理,然后再cv;这是完全错误的,正确的做法是,对于每一个cv集单独处理,然后在训练。为了达到节省代码的目的,我们得用sklearn中的pipeline进行封装。这个便是pipeline存在的意义。

数据泄露几乎存在于你每一处操作的时候,要完全避免数据泄露非常困难,这里有几个小技巧:

  1.  添加一点小噪声,比如遵循Laplace(0, 1)分布的数据点。
  2.  使用pipeline进行封装,这个在之前已经提到了。
  3.  准备一个holdback set,在这上面进行再次的评测。(跟Andrew Ng口中的test set特别像,但是完全不一样)

 

 

 

 


版权声明:本文为qq_43338695原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。