特征工程-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

在学习速率相同的情况下,\(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对应的二进制编码作为结果。截屏2022-06-18 00.03.32

    
    
  • 虚拟编码

    虚拟编码在进行表示时只使用 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

posted @ 2022-06-18 00:21  iiYing  阅读(145)  评论(0编辑  收藏  举报