数据预处理之onehot编码

2020-02-08  本文已影响0人  LiBiscuit

多事之春,好像一切刚开始都很难,或许是万事开头难,后面就会苦尽甘来了吧。long time no see 小李又拖延上线啦!
数据预处理在我看来是在模型选择前重要的一步,今天记叙一下常用的onehot编码。


Onehot编码

What?

什么是Onehot编码?
onehot编码又叫独热编码,其为一位有效编码,主要是采用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候只有一位有效。
Onehot编码是分类变量作为二进制向量的表示。
这首先要求将分类值映射到整数值。然后,每个整数值被表示为二进制向量,除了整数的索引之外,它都是零值,它被标记为1。


举个例子
在机器学习算法中,我们经常会遇到分类特征。
例如:人的性别有男女,祖国有中国,美国,法国等。
这些特征值并不是连续的,而是离散的,无序的。
通常我们需要对其进行特征数字化。

那什么是特征数字化呢?如下:

1.性别特征:["男","女"]
按照N位状态寄存器来对N个状态进行编码的原理,咱们处理后应该是这样的(这里只有两个特征,所以N=2):
男 → 10
女 → 01
2.祖国特征:["中国","美国,"法国"](N=3):
中国 → 100
美国 → 010
法国 → 001
3.运动特征:["足球","篮球","羽毛球","乒乓球"](N=4):
足球 → 1000
篮球 → 0100
羽毛球 → 0010
乒乓球 → 0001

当一个样本为["男","中国","乒乓球"]的时候,完整的特征数字化的结果为:
[1,0,1,0,0,0,0,0,1]
其实就是对应,男(01),中国(100),乒乓球(0001)合起来的编码


上面这个例子是网上比较常见的,下面补充另一个例子。
对 “hello world” 进行one-hot编码
1.确定要编码的对象--hello world,
2.确定分类变量--h e l l o 空格 w o r l d,共27种类别
(26个小写字母 + 空格,);
3.转化为二进制向量:有11个样本,每个样本有27个特征
这里有一个前提,特征排列的顺序不同,对应的二进制向量亦不同
(比如把空格放在第一列和a放第一列,one-hot编码结果肯定是不同的)
因此我们必须要事先约定特征排列的顺序
27种特征首先进行整数编码:
a--0,b--1,c--2,......,z--25,空格--26
(27种特征按照整数编码的大小从前往后排列)
得到的one-hot编码如下:


具体可以参考:详解one-hot编码

故,通过两个例子可以这样理解,对于每一个特征,如果它有m个可能值,那么经过onehot编码后,就变成了m个二元特征。并且,这些特征互斥,每次只有一个激活。因此,数据会变成稀疏的。

Why?

使用onehot有什么好处?
one hot编码是将类别变量转换为机器学习算法易于利用的一种形式的过程。
这样做的好处主要有:
1.解决了分类器不好处理属性数据的问题
2.在一定程度上也起到了扩充特征的作用

直接原因:
使用One-hot的直接原因是现在多分类cnn网络的输出通常是softmax层,而它的输出是一个概率分布,从而要求输入的标签也以概率分布的形式出现。
例如,上面的 hello world 相当于多分类的问题(27分类),每个样本只对应于一个类别(即只在对应的特征处值为1,其余地方值为0),而我们的分类结果,得到的往往是隶属于某个类别的概率,这样在进行损失函数(例如交叉熵损失)或准确率计算时,变得非常方便。

我们通常使用onehot编码来处理离散型的数据。
原因如下:
在回归,分类,聚类等机器学习算法中,特征之间距离的计算或相似度的计算是非常重要的,常用的距离或相似度的计算都是在欧式空间的相似度计算,计算余弦相似性,基于的就是欧式空间。
使用one-hot编码,将离散特征的取值扩展到了欧式空间,离散特征的某个取值就对应欧式空间的某个点。
将离散型特征使用one-hot编码,会让特征之间的距离计算更加合理。

比如,一个离散型特征,代表工作类型,共有三个取值,不使用one-hot编码,其表示分别是:x_1 = (1), x_2 = (2), x_3 = (3)。
两个工作之间的距离:d(x_1, x_2) = 1, d(x_2, x_3) = 1, d(x_1, x_3) = 2。
那么x_1和x_3工作之间就越不相似吗?
显然这样的表示,计算出来的特征的距离是不合理。
如果使用one-hot编码,则得到x_1 = (1, 0, 0), x_2 = (0, 1, 0), x_3 = (0, 0, 1),那么两个工作之间的距离就都是sqrt(2).即每两个工作之间的距离是一样的,显得更合理。


这边补充一下:不需要使用one-hot编码来处理的情况
将离散型特征进行one-hot编码的作用,是为了让距离计算更合理,但如果特征是离散的,并且不用one-hot编码就可以很合理的计算出距离,那么就没必要进行one-hot编码。
比如,该离散特征共有1000个取值,我们分成两组,分别是400和600,两个小组之间的距离有合适的定义,组内的距离也有合适的定义,那就没必要用one-hot 编码。
离散特征进行one-hot编码后,编码后的特征,其实每一维度的特征都可以看做是连续的特征。

除此,one-hot编码要求每个类别之间相互独立,如果之间存在某种连续型的关系,或许使用distributed respresentation(分布式)更加合适。

Do?

如何用python实现one-hot编码?
这边只简单介绍:利用sklearn来进行one-hot编码。

>> from sklearn.preprocessing import OneHotEncoder
>>> enc = OneHotEncoder()
>>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]])  
OneHotEncoder(categorical_features='all', dtype=<... 'numpy.float64'>,
       handle_unknown='error', n_values='auto', sparse=True)
>>> enc.n_values_
array([2, 3, 4])
>>> enc.feature_indices_
array([0, 2, 5, 9])
>>> enc.transform([[0, 1, 1]]).toarray()
array([[ 1.,  0.,  0.,  1.,  0.,  0.,  1.,  0.,  0.]])

解释一下代码:
1.输入样本:[[0, 0, 3], [1, 1, 0], [0, 2, 1],[1, 0, 2]],
这个输入样本表示该input共有四个样本,三个特征,也就是三列
2.观察特征
对于第一个feature,对应第一列,取值有0,1两个属性值。
对于第二个feature,对应第二列,取值有0,1,2三个值。
对于第三个feature,对应第三列,取值有0,1,2,3四个取值。
这里的enc.n_values_就是每个属性列不同属性值的个数,
输出可以分别是2,3,4。

3.enc.feature_indices_是对enc.n_values_的一个累加,是特征索引,该例子中value值为(2,3,4),则特征索引从0开始,到2的位置为第一个,到2+3=5的位置为第二个,到2+3+4的位置为第三个,索引为array([0,2,5,9])
4.enc.transform([[0, 1, 1]]).toarray()是将[0, 1, 1]这个样本转化为基于上面四个输入的one-hot编码。那么可以得到:
第一个属性值0,对应第一列:0->10
第二个属性值1,对应第二列:1->010
第三个属性值1,对应第三列:1->0100
5.输出[0, 1, 1]对应以上输入的one-hot编码为:
[1,0,0,1,0,0,1,0,0]。

友情参考:Python: 进行one-hot编码


其余代码,基于numpy的可详见:numpy快速生成one hot编码
友情链接: 机器学习:数据预处理之独热编码(One-Hot)

Ending~希望疫情快点结束吧!

上一篇下一篇

猜你喜欢

热点阅读