在特征工程之特征选择中, 我们讲到了特征选择的一些要点. 本篇我们继续讨论特征工程, 不过会重点关注于特征表达部分, 即如果对某一个特征的具体表现形式做处理. 主要包括缺失值处理, 特殊的特征处理比如时间和地理位置处理, 离散特征的连续化和离散化处理, 连续特征的离散化处理几个方面.
1. 缺失值处理
特征有缺失值是非常常见的, 大部分机器学习模型在拟合前需要所有的特征都有值, 不能是空或者 NULL. 那么如果有缺失值我们需要怎么处理呢?
首先我们会看是该特征是连续值还是离散值. 如果是连续值, 那么一般有两种选择, 一是选择所有有该特征值的样本, 然后取平均值, 来填充缺失值, 另一种是取中位数来填充缺失值. 如果是离散值, 则一般会选择所有有该特征值的样本中最频繁出现的类别值, 来填充缺失值. 在 sklearn 中, 可以使用 preprocessing.Imputer 来选择这三种不同的处理逻辑做预处理.
2. 特殊的特征处理
有些特征的默认取值比较特殊, 一般需要做了处理后才能用于算法. 比如日期时间, 比如显示 20180519, 这样的值一般没办法直接使用. 那么一般需要如何变换呢?
对于时间原始特征, 处理方法有很多, 这里只举例几种有代表性的方法. 第一种是使用连续的时间差值法, 即计算出所有样本的时间到某一个未来时间之间的数值差距, 这样这个差距是 UTC 的时间差, 从而将时间特征转化为连续值. 第二种方法是根据时间所在的年, 月, 日, 星期几, 小时数, 将一个时间特征转化为若干个离散特征, 这种方法在分析具有明显时间趋势的问题比较好用. 第三种是权重法, 即根据时间的新旧得到一个权重值. 比如对于商品, 三个月前购买的设置一个较低的权重, 最近三天购买的设置一个中等的权重, 在三个月内但是三天前的设置一个较大的权重. 当然, 还有其他的设置权重的方法, 这个要根据要解决的问题来灵活确定.
对地理特征, 比如 "广州市天河区 XX 街道 XX 号", 这样的特征我们应该如何使用呢? 处理成离散值和连续值都是可以的. 如果是处理成离散值, 则需要转化为多个离散特征, 比如城市名特征, 区县特征, 街道特征等. 但是如果我们需要判断用户分布区域, 则一般处理成连续值会比较好, 这时可以将地址处理成经度和纬度的连续特征.
3. 离散特征的连续化处理
有很多机器学习算法只能处理连续值特征, 不能处理离散值特征, 比如线性回归, 逻辑回归等. 那么想使用逻辑回归, 线性回归时这些值只能丢弃吗? 当然不是. 我们可以将离散特征连续化处理.
最常见的离散特征连续化的处理方法是独热编码 one-hot encoding. 处理方法其实比较简单, 比如某特征的取值是高, 中和低, 那么我们就可以创建三个取值为 0 或者 1 的特征, 将高编码为 1,0,0 这样三个特征, 中编码为 0,1,0 这样三个特征, 低编码为 0,0,1 这样三个特征. 也就是说, 之前的一个特征被我们转化为了三个特征. sklearn 的 OneHotEncoder 可以帮我们做这个处理.
第二个方法是特征嵌入 embedding. 这个一般用于深度学习中. 比如对于用户的 ID 这个特征, 如果要使用独热编码, 则维度会爆炸, 如果使用特征嵌入就维度低很多了. 对于每个要嵌入的特征, 我们会有一个特征嵌入矩阵, 这个矩阵的行很大, 对应我们该特征的数目. 比如用户 ID, 如果有 100 万个, 那么嵌入的特征矩阵的行就是 100 万. 但是列一般比较小, 比如可以取 20. 这样每个用户 ID 就转化为了一个 20 维的特征向量. 进而参与深度学习模型. 在 tensorflow 中, 我们可以先随机初始化一个特征嵌入矩阵, 对于每个用户, 可以用 tf.nn.embedding_lookup 找到该用户的特征嵌入向量. 特征嵌入矩阵会在反向传播的迭代中优化.
此外, 在自然语言处理中, 我们也可以用 word2vec 将词转化为词向量, 进而可以进行一些连续值的后继处理.
4. 离散特征的离散化处理
离散特征有时间也不能直接使用, 需要先进行转化. 比如最常见的, 如果特征的取值是高, 中和低, 那么就算你需要的是离散值, 也是没法直接使用的.
对于原始的离散值特征, 最常用的方法也是独热编码, 方法在第三节已经讲到.
第二种方法是虚拟编码 dummy coding, 它和独热编码类似, 但是它的特点是, 如果我们的特征有 N 个取值, 它只需要 N-1 个新的 0,1 特征来代替, 而独热编码会用 N 个新特征代替. 比如一个特征的取值是高, 中和低, 那么我们只需要两位编码, 比如只编码中和低, 如果是 1,0 则是中, 0,1 则是低. 0,0 则是高了. 目前虚拟编码使用的没有独热编码广, 因此一般有需要的话还是使用独热编码比较好.
此外, 有时候我们可以对特征进行研究后做一个更好的处理. 比如, 我们研究商品的销量对应的特征. 里面有一个原始特征是季节春夏秋冬. 我们可以将其转化为淡季和旺季这样的二值特征, 方便建模. 当然有时候转化为三值特征或者四值特征也是可以的.
对于分类问题的特征输出, 我们一般需要用 sklearn 的 LabelEncoder 将其转化为 0,1,2,... 这样的类别标签值.
5. 连续特征的离散化处理
对于连续特征, 有时候我们也可以将其做离散化处理. 这样特征变得高维稀疏, 方便一些算法的处理.
对常用的方法是根据阈值进行分组, 比如我们根据连续值特征的分位数, 将该特征分为高, 中和低三个特征. 将分位数从 0-0.3 的设置为高, 0.3-0.7 的设置为中, 0.7-1 的设置为高.
当然还有高级一些的方法. 比如使用 GBDT. 在 LR+GBDT 的经典模型中, 就是使用 GDBT 来先将连续值转化为离散值. 那么如何转化呢? 比如我们用训练集的所有连续值和标签输出来训练 GBDT, 最后得到的 GBDT 模型有两颗决策树, 第一颗决策树有三个叶子节点, 第二颗决策树有 4 个叶子节点. 如果某一个样本在第一颗决策树会落在第二个叶子节点, 在第二颗决策树落在第 4 颗叶子节点, 那么它的编码就是 0,1,0,0,0,0,1, 一共七个离散特征, 其中会有两个取值为 1 的位置, 分别对应每颗决策树中样本落点的位置. 在 sklearn 中, 我们可以用 GradientBoostingClassifier 的 apply 方法很方便的得到样本离散化后的特征, 然后使用独热编码即可.
具体的一个示例代码如下:
- from sklearn.datasets import make_classification
- from sklearn.model_selection import train_test_split
- from sklearn.ensemble import GradientBoostingClassifier
- from sklearn.preprocessing import OneHotEncoder
- X, y = make_classification(n_samples=10)
- X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5)
- gbc = GradientBoostingClassifier(n_estimators=2)
- one_hot = OneHotEncoder()
- gbc.fit(X_train, y_train)
- X_train_new = one_hot.fit_transform(gbc.apply(X_train)[:, :, 0])
- print (X_train_new.todense())
输出是:
- [[0. 1. 1. 0.]
- [1. 0. 0. 1.]
- [1. 0. 0. 1.]
- [1. 0. 0. 1.]
- [0. 1. 1. 0.]]
6. 小结
本文总结了特征表达的一些具体方法, 但是特征表达的方法便不止于上文中的方法, 毕竟这是工程实践. 但是上文中的方法是比较普遍的, 希望可以给大家一些帮助和启发. 下一篇我们讨论特征预处理和分类类别不平衡的问题处理.
来源: https://www.cnblogs.com/pinard/p/9061549.html