我们在用模型去解决机器学习问题的时候,首先很重要的过程就是对特征的预处理。我们的数据可以分为连续型和离散型。
对于连续型数据,我们一般的做法是对其进行标准化或者归一化;对于离散型数据,我们基本就是按照one-hot(独热)编码,该离散特征有多少取值,就用多少维来表示该特征。
LabelEncoder是用来对分类型特征值进行编码,即对不连续的数值或文本进行编码。例如:人的性别 [male, female],male用“0”表示,female用“1”表示,来自的国家 [from Europe, from US, from Asia]等这些特征均可以采用整数的形式进行编码。
LabelEncoder在某些情况下很有用,但是场景限制很多。例如: [male, from US] 可表示成 [0, 1] ,[female, from Asia] 可表示成[1, 2]。但是,这些整数形式的表示不能直接作为某些机器学习算法输入,因为有些机器学习算法是需要连续型的输入数据,同一列数据之间数值的大小可代表差异程度。比如 [0, 1]与[0,2]的特征差异比[0, 0]与[0,2]之间的差异要大,但事实上它们的差异是一样的,都是来自不同的国家。再比如有[dog,cat,dog,mouse,cat],我们把其转换为[1,2,1,3,2]。这里就产生了一个奇怪的现象:dog和mouse的平均值是cat。所以目前还没有发现标签编码的广泛使用。
一个解决办法就是采用OneHotEncoder,这种表示方式将每一个分类特征变量的m个可能的取值转变成m个二值特征,对于每一条数据这m个值中仅有一个特征值为1,其他的都为0。
在回归,分类,聚类等机器学习算法中,特征之间距离的计算或相似度的计算是非常重要的,而我们常用的距离或相似度的计算都是在欧式空间的相似度计算,计算余弦相似性,基于的就是欧式空间。使用one-hot编码,将离散特征的取值扩展到了欧式空间,这会让特征之间的距离计算更加合理。编码后的特征可以跟对连续型特征的归一化方法一样,对每一维特征进行归一化。下面我们来举一个例子,利用sklearn实现独特编码。
import numpy as np
from sklearn import preprocessing
encoder = preprocessing.OneHotEncoder()
encoder.fit([[0, 2, 1, 12], [1, 3, 5, 3], [2, 3, 2, 12], [1, 2, 4, 3]])
encoded_vector = encoder.transform([[2, 3, 5, 3]]).toarray()
print ("\nEncoded vector =", encoded_vector )
上述代码中处理的是一个四维向量空间,观察一下每个特征向量的第一个特征,分别是0、1、2、1这4个值,其中有重复数字1所以说独热编码向量的长度是3,如果需要对2进行编码,那么向量就是[0, 0, 1];再看每个特征向量的第二个特征,分别是2、3、3、2,2和3均为重复数字,所以独热编码向量的长度是2,对3进行编码的向量为[0, 1];对5进行编码,看每个特征向量的第三个特征,分别为1、5、2、4,无重复数字所以特征编码长度为4,对5进行编码的向量为[0, 0, 0, 1];同理,对最后的3进行编码的向量为[1, 0]。
输出结果如下:
Encoded vector = [[0. 0. 1. 0. 1. 0. 0. 0. 1. 1. 0.]]
独热编码的优点就是解决了分类器不好处理属性数据的问题,在一定程度上也起到了扩充特征的作用。它的值只有0和1,不同的类型存储在垂直的空间。
同时它的缺点显而易见就是当类别的数量很多时,特征空间会变得非常大。在这种情况下,一般可以用PCA来减少维度。而且one hot encoding+PCA这种组合在实际中也非常有用。
哑编码与独热编码的思想大致相同,只是哑变量编码觉得one-hot编码太罗嗦,所以它把很很明显的东西省去了。这种简化不能说到底好不好,这要看使用的场景。
哑变量编码直观的解释就是任意的将一个状态位去除。用上面例子对5进行编码来说,每个特征向量的第三个特征分别为1、5、2、4,对它们进行编码的向量分别为1–>[1, 0, 0, 0]、2–>[0, 1, 0, 0]、4–>[0, 0, 1, 0]、5–>[0, 0, 0, 1],然而我们用3个状态位就足够反应上述4个类别的信息,也就是我们仅仅使用前三个状态位 [0,0,0] 就可以表达数字5了。