特征工程-1
一、特征工程
对原始数据进行一系列工程处理,将其提炼为特征,作为输入供算法和模型使用。从本质上来讲,特征工程是一个表示和展现数据的过程。在实际工作中,特征工程旨在去除原始数据中的杂质和冗余,设计更高效的特征以刻画求解的问题与预测模型之间的关系。
以下主要针对结构化数据和非结构化数据进行特征选择。
1 特征归一化
使用特征归一化,消除数据特征之间的量纲影响,使得各项指标处于同一数量级,不同量纲之间具有可比性。
主要针对数值类型特征进行归一化,可以将特征统一到一个大致相同的数值区间。主要使用:
-
线性函数归一化
它对原始数据进行线性变换,使结果映射到[0, 1]的范围,实现对原始数据的等比缩放。
\[X_{\text {norm }}=\frac{X-X_{\min }}{X_{\max }-X_{\min }} \] -
零均值归一化
将原始数据映射到均值为0、标准差为1的分布上。
\[Z=\frac{x-\mu}{\sigma} \]
假设有两种数值型特征,\(x_1\)的取值范围为 [0, 10],\(X_2\)的取值范围为[0, 3],于是可以构造一个目标函数符合下图(a)中的等值图。
![截屏2022-06-17 23.24.31](https://tva1.sinaimg.cn/large/e6c9d24egy1h3bnmxpl9vj20p20dc3z8.jpg)
在学习速率相同的情况下,\(x_1\)的更新速度会大于\(x_2\),需要较多的迭代才能找到最优解。如果将\(x_1\)和\(x_2\)归一化到相同的数值区间后,优化目标的等值图会变成上图(b)中的圆形,\(x_1\)和\(x_2\)的更新速度变得更为一致,容易更快地通过梯度下降找到最优解。
2 类别型特征
类别型特征(Categorical Feature)主要是指性别(男、女)、血型(A、B、AB、O)等只在有限选项内取值的特征。类别型特征原始输入通常是字符串形式,除了决策树等少数模型能直接处理字符串形式的输入,对于逻辑回归、支持向量机等模型来说,类别型特征必须经过处理转换成数值型特征才能正确工作。
-
序号编码
通常用于处理类别间具有大小关系的数据,如成绩可分为A、B、C、D四个等级。序号编码将分数分为四个等级,对等级进行编码,编码后保留了大小关系,同时降低了数据维度。
from sklearn.preprocessing import LabelEncoder le = LabelEncoder() # 类别型目标特征 classes = [1, 2, 6, 4, 2] new_classes = le.fit_transform(classes) print(le.classes_) print(new_classes)
-
独热编码
通常用于处理类别间不具有大小关系的特征,特征向量只在某一维取值为1,其他地方为0。对于类别取值较多的情况就会使维度过大,可以使用特征选择来降低维度。
高维度可能会带来两点间距离很难衡量、过拟合问题,并且可能只有部分维度对分类、预测有帮助。
from sklearn.preprocessing import OneHotEncoder from pandas import get_dummies import pandas as pd df = pd.DataFrame({ "City": ["SF", "SF", "SF", "NYC", "NYC", "NYC", "Seattle", "Seattle", "Seattle"], "Rent": [3999, 4000, 4001, 3499, 3500, 3501, 2499, 2500, 2501] }) df["Rent"].mean() one_hot_df = pd.get_dummies(df, prefix = "city") print(one_hot_df)
-
二进制编码
主要分为两步,先用序号编码给每个类别赋予一个类别ID,然后将类别ID对应的二进制编码作为结果。
-
虚拟编码
虚拟编码在进行表示时只使用 k-1 个自由度,除去了额外的自由度,没有被使用的那个特征通过一个全零向量表示,它称为参照类。
使用虚拟编码的模型结果比使用 One-Hot Encoding 的模型结果更具解释性。虚拟编码的缺点是不太容易处理缺失数据,因为全零向量已经映射为参照类。
import pandas as pd df = pd.DataFrame({ "City": ["SF", "SF", "SF", "NYC", "NYC", "NYC", "Seattle", "Seattle", "Seattle"], "Rent": [3999, 4000, 4001, 3499, 3500, 3501, 2499, 2500, 2501] }) df["Rent"].mean() vir_df = pd.get_dummies(df, prefix = "city", drop_first = True) print(vir_df)
-
效果编码
效果编码与虚拟编码非常相似,区别在于参照类是用全部由 -1 组成的向量表示的。其优点是全由-1组成的向量是个密集向量,计算和存储的成本都比较高。
import pandas as pd df = pd.DataFrame({ "City": ["SF", "SF", "SF", "NYC", "NYC", "NYC", "Seattle", "Seattle", "Seattle"], "Rent": [3999, 4000, 4001, 3499, 3500, 3501, 2499, 2500, 2501] }) df["Rent"].mean() vir_df = pd.get_dummies(df, prefix = "city", drop_first = True) effect_df = vir_df[3:5, ["city_SF", "city_Seattle"]] = -1 print(effect_df)
-
特征散列化
- 散列函数是一种确定性函数,他可以将一个可能无界的整数映射到一个有限的整数范围 [1,m]中,因为输入域可能大于输出范围,所以可能有多个值被映射为同样的输出,这称为碰撞
- 均匀散列函数可以确保将大致相同数量的数值映射到 m 个分箱中
- 如果模型中涉及特征向量和系数的内积运算,那么就可以使用特征散列化
- 特征散列化的一个缺点是散列后的特征失去了可解释性,只是初始特征的某种聚合
from sklearn.feature_extraction import FeatureHasher # 单词特征的特征散列化 def hash_features(word_list, m): output = [0] * m for word in word_list: index = hash_fcn(word) % m output[index] += 1 return output # 带符号的特征散列化 def hash_features(word_list, m): output = [0] * m for word in word_list: index = hash_fcn(word) % m sign_bit = sign_hash(word) % 2 if sign_bit == 0: output[index] -= 1 else: output[index] += 1 return output h = FeatureHasher(n_features = m, input_type = "string") f = h.trasnform(df["feat"])
3 组合特征
为了提高复杂关系的拟合能力,在特征工程中经常会把一阶离散特征两两组合,构成高阶组合特征。
一般的特征组合可以理解为两个离散特征和特征交叉合并,如特征A有m个类别,特征B有n个类别,则特征A和特征B的组合就是将特征A、B中的各个类别两两组合,其维度为m*n。
如果简单地两两组合,依然容易存在参数过多、过拟合等问题,而且并不是所有的特征组合都是有意义的。
参考:《百面机器学习》、Feature Engine