特征工程--机器学习

发布于:2025-08-13 ⋅ 阅读:(11) ⋅ 点赞:(0)

1、特征工程

1.1 概念

  • 特征工程(Feature Engineering)是机器学习项目中非常关键的一步,它是指通过领域知识来选择、创建或修改能够使机器学习模型更好地工作的特征(即输入变量)。特征工程的目标是提高模型的性能,通过提取数据中有用的信息并将其转换为算法可以使用的格式。良好的特征工程可以使模型更加高效,有时候甚至比选择更复杂的模型更加重要

  • 一般使用 pandas 来进行数据清洗和数据处理、然后使用 sklearn 库来进行特征工程处理

  • 特征工程是将任意数据(文本、图像等)转换为可用于机器学习的数字特征,比如:字典特征提取、文本特征提取、图像特征提取

  • 特征工程步骤:

    • 数据理解与预处理:首先需要理解数据集的特性,包括数据的分布、缺失值情况、异常值,去除重复记录、处理缺失值、纠正错误数据等

    • 特征生成:根据业务理解或领域知识创造新的特征、将非数值特征转换为数值形式

    • 特征选择:利用统计检验、相关性分析或其他特征选择技术来确定哪些特征对模型最为重要。这有助于减少维度,同时保持模型的有效性

    • 特征变换:对原始特征进行转换,比如使用对数变换来处理偏斜的数据分布,或者使用多项式特征扩展来增加模型的非线性能力

    • 特征编码:对于类别特征,需要进行编码处理,比如独热编码(one-hot encoding)或标签编码(label encoding)

      • 标签编码是一种简单的编码方式,它将每个类别标签用唯一的整数表示。例如,如果有三个类别:'apple', 'banana', 'cherry',标签编码可能会将它们分别编码为 0, 1, 2。标签编码适用于有序分类变量(ordinal variables),即这些类别的顺序是有意义的,例如教育水平或大小等级。然而,对于没有自然顺序的名义变量(nominal variables),直接使用标签编码可能会引入人为的顺序关系,这可能会影响模型的性能,因为模型可能会假定数值上较大的类别比数值上较小的类别更重要

      • 独热编码是一种将名义变量转换为数值型数据的方式,它创建了一个新的二进制列(即只有 0 或 1 的列)用于每一个可能的类别值。例如,对于上述的类别'apple', 'banana', 'cherry',独热编码会创建三列,每列对应一个类别,每一行中只有一个类别被标记为 1,其余为 0。这种方式避免了引入任何顺序上的意义,因此适用于没有内在顺序关系的类别变量

    • 特征缩放:确保所有特征都在相似的尺度上,即归一化、标准化

    • 降维:当特征数量过多时,可以采用降维技术如PCA(主成分分析)来减少特征数量,同时尽可能保留原始数据的信息

1.2 特征工程 API

  • 特征工程API(Feature Engineering API)是指提供自动化或半自动化特征工程功能的服务接口,这些API允许开发者或数据科学家通过编程方式来处理数据集,从而简化特征工程的工作流程

  • 常用的 API 如下:

名称 作用
DictVectorizer 字典特征提取
CountVectorizer 文本特征提取
TfidfVectorizer TF-IDF文本特征词的重要程度特征提取
MinMaxScaler 归一化
StandardScaler 标准化
VarianceThreshold 底方差过滤降维
PCA 主成分分析降维
  • 常用方法和功能介绍如下:

函数名 参数 作用
fit_transform(list_of_dicts) 一个包含字典的列表 fittransform两个方法的合成
fit(list_of_dicts) 一个包含字典的列表 fit方法用来计算数据的统计信息,比如训练集的均值,方差,最大值,最小值等,只要fit之后,如果存在多个数据(比如 train、test)需要transform,可以直接transform
transform(list_of_dicts) 一个包含字典的列表 transform 方法根据fit方法所学习到的参数来转换数据。这可能意味着标准化特征,填充缺失值,编码类别变量等
get_feature_names_out() 返回一个包含所有特征名称的列表,这些特征是在 fitfit_transform 方法中学习到的
inverse_transform(X) fit_transform生成 将特征矩阵转换回原来的字典形式
1.2.1 DictVectorizer-字典特征提取
  • 在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵,与之相反,若非0元素数目占大多数时,则称该矩阵为稠密矩阵。定义非零元素的总数比上矩阵所有元素的总数为矩阵的稠密度

  • 在 Python 里,稀疏矩阵稠密矩阵的“定义”不是按数学上零元素比例来自动判断的,而是由你用的存储数据类型/类来决定的

  • 稀疏矩阵通常由三部分组成:

    • 数据 (data): 存储非零元素的值

    • 索引 (indices): 指出每个非零元素所在的列

    • 指针 (indptr): 指出每行第一个非零元素的位置

  • 非稀疏矩阵:非稀疏矩阵实际上就是普通的矩阵或稠密矩阵,它们是由一系列元素按照一定的行和列排列组成的数学结构。当我们谈论非稀疏矩阵的组成部分时,主要指的是它的行、列以及矩阵中的元素

  • 稀疏矩阵调用以下函数可以转为稠密矩阵

    • todense() 返回的是一个 numpy.matrix 对象

    • toarray() 返回的是一个 numpy.ndarray 对象

  • DictVectorizer 是一个用于将特征集合(通常是以字典形式表示的)转换为向量空间模型的工具。它能够将一系列字典转换成一个特征矩阵,其中每一行代表一个样本,每一列表示一个特征

  • DictVectorizer 实例化参数:

    • sparse(bool): 布尔值,默认值为 True。如果为 True,输出是三元组的稀疏矩阵;如果为 False,输出是Numpy数组

    • dtype(numpy.dtype): 指定输出矩阵的数据类型,默认为 numpy.float64

    • separator(str): 当特征名包含嵌套键时,指定嵌套键之间的分隔符,默认为 '='

提取为稀疏矩阵案例:

from sklearn.feature_extraction import DictVectorizer
import pandas as pd
​
def dict_dictVectorizer():
    data = [
        {'username': '小明', 'password': '111', 'age': 10},
        {'username': '小红', 'password': '222', 'age': 11},
        {'username': '小明', 'password': '333', 'age': 12},
    ]
    vec = DictVectorizer(sparse=True)
    vec_new = vec.fit_transform(data)
    print('稀疏矩阵类型:\n', type(vec_new))
    print('稀疏矩阵:\n', vec_new)
    # 特征名
    print('特征名:\n', vec.get_feature_names_out())
    csr_matrix = vec_new.todense()
    print('转为稠密矩阵:\n', csr_matrix)

提取为二维数组案例:

from sklearn.feature_extraction import DictVectorizer
​
def dict_dictVectorizer2():
    data = [
        {'username': '小明', 'password': '111', 'age': 10},
        {'username': '小红', 'password': '222', 'age': 11},
        {'username': '小明', 'password': '333', 'age': 12},
    ]
    vec = DictVectorizer(sparse=False)
    vec_new = vec.fit_transform(data)
    print('非稀疏矩阵类型:\n', type(vec_new))
    print('非稀疏矩阵:\n', vec_new)
    print('特征名:\n', vec.get_feature_names_out())
1.2.2 CountVectorizer-文本特征提取
  • CountVectorizer 是一个在自然语言处理(NLP)任务中常用的工具,用于将文本数据转换为向量形式。它属于 sklearn.feature_extraction.text 模块的一部分,是Scikit-learn库提供的功能之一。CountVectorizer 主要通过以下方式工作:

    1. 词汇表构建:首先根据训练集中的文档创建一个固定的词汇表(即所有唯一词的集合)。每个词在词汇表中都有一个对应的索引位置

    2. 词频统计:对于每个文档,CountVectorizer 会计算出词汇表中每个词出现的次数,并忽略那些未出现在文档中的词

    3. 输出格式:最终的输出是一个稀疏矩阵(sparse matrix),每一行对应一个文档,每一列对应词汇表中的一个词。矩阵中的每个元素表示某个词在某个文档中出现的次数

  • CountVectorizer 默认会忽略一些英文中的停用词(如“is”、“the”等),并且会对单词进行小写化处理。这些行为可以通过传递参数来调整,例如设置 stop_words='english' 可以显式地过滤英语停用词,而 lowercase=False 则可以禁用自动小写化处理

  • CountVectorizer 实例化参数:

    • stop_words (string or list, default=None):如果为english,则会移除英语停用词。也可以传入一个包含停用词的自定义列表,如果是单个英文字母,会忽略,比如i

    • lowercase (bool, default=True):是否将文本转换为小写

英文文本案例:

from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd
​
def english_countVectorizer():
    data = ["Life is is short", "I am learning Python"]
    # 创建转换器对象
    transfer = CountVectorizer(stop_words=["is"])
    # 进行提取,得到非稀疏矩阵
    data_new = transfer.fit_transform(data)
    print(data_new)
    df = pd.DataFrame(data_new.toarray(),
                      index=["第一个句子", "第二个句子"],
                      columns=transfer.get_feature_names_out())
    print(df)

中文文本案例:

  1. 安装中文分词库jieba,命令:·pip install jieba

from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd
import jieba
​
def cut(text):
    return " ".join(list(jieba.cut(text)))
def chinese_countVectorizer():
    data = ["我爱北京天安门", "天安门上太阳升"]
    data_new = [cut(v) for v in data]
    transfer = CountVectorizer()
    data_final = transfer.fit_transform(data_new)
    print(transfer.get_feature_names_out())
    df = pd.DataFrame(data_final.toarray(),
                      index=["第一个句子", "第二个句子"],
                      columns=transfer.get_feature_names_out())
    print(df)
1.2.3 TfidfVectorizer-重要文本特征提取
  • TF-IDF(Term Frequency-Inverse Document Frequency,词频-逆文档频率)是一种常用的文本特征表示方法,用于评估一个词对一个文档或语料库中的重要程度,比如如果某个词或短语在一篇文章中出现的概率高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类

  • 词频:词语在文档中出现的频率

  • 逆文档频率:词语在整个文档集合中的重要程度

  • TF-IDF 稀有文本特征提取将 CountVectorizer和 TfidfTransformer 的所有功能组合在一个模型中,TfidfTransformer 将词频/字符频数矩阵转换为标准化的 tf 或 tf-idf 矩阵

  • TF-IDF是两个指标的乘积:词频(TF)和逆文档频率(IDF),两个部分的计算公式如下:

    • 词频(TF):词频越高,说明该词在该文档中越重要

    $$
    TF(t,d)= \frac{词 t 在文档 d 中出现的次数}{文档 d 中词的总数}
    $$

     
    • 逆文档频率(IDF):出现频率较低的词语会有较高的 IDF 值,从而提高其在文档中的权重,这里加1是为了防止除数为零的情况,并且确保 IDF 不会为负数

    $$
    IDF(t)=log(\frac{文档总数+1}{词 t 在文档 d 中出现的次数+1})+1
    $$

     
    • TF-IDF:通过 TF-IDF 评分,重要的词语(即在该文档中频繁出现而在其他文档中不常见的词语)会得到较高的权重

    $$
    TF-IDF(t,d)=TF(t,d)×IDF(t)
    $$

  • TfidfVectorizer 是 scikit-learn 提供的一个工具,用于将文本数据转换为 TF-IDF 特征矩阵,函数介绍如下:

    • TfidfVectorizer():用来初始化 TfidfVectorizer 对象,它属于 sklearn.feature_extraction.text 模块的一部分,常用参数如下:

      • stop_words (string or list, default=None):同 CountVectorizer

      • use_idf:布尔值,是否启用逆文档频率(IDF)加权,默认值为 True

      • smooth_idf:布尔值,是否加1平滑IDF,默认值为 True

      • norm:字符串,表示归一化方法,'l1''l2',默认值为 'l2'

案例:

from sklearn.feature_extraction.text import TfidfVectorizer
​
def english_tfidfVectorizer():
    # 创建一个TfidfVectorizer对象
    tfidf = TfidfVectorizer()
    # 创建一个DataFrame对象
    data = [
        'This is the first document',
        'This is the second document',
        'And the third one',
        'Is this the first document',
    ]
    # 使用fit_transform方法对文本进行向量化
    X = tfidf.fit_transform(data)
    # 打印结果
    print(X.toarray())
    print(tfidf.get_feature_names_out())

注意事项:

  • 实践证明,TfidfVectorizer使用的计算TF-IDF公式如下:

1.2.4 无量纲化
  • 将不同规格的数据转换到同一规格,或将不同分布的数据转换到某个特定分布的需求,这种需求统称为将数据无量纲化

  • 在以梯度和矩阵为核心的算法中,譬如逻辑回归,支持向量机,神经网络,无量纲化可以加快求解速度

  • 在距离类模型,譬如K近邻,KMeans聚类中,无量纲化可以帮我们提升模型精度,避免某一个取值范围特别大的特征对距离计算造成影响

  • 决策树和树的集成算法,对决策树不需要无量纲化,决策树可以把任意数据都处理得很好

  • 数据的无量纲化可以是线性的,也可以是非线性的。线性的无量纲化包括中心化(Zero-centered或者Mean-subtraction)处理和缩放处理(Scale)。中心化的本质是让所有记录减去一个固定值,即让数据样本数据平移到某个位置(数据归一化)。缩放的本质是通过除以一个固定值,将数据固定在某个范围内之中,取对数也算是一种缩放处理(数据标准化)

  • 比如,以下是一个人的基本数据信息:

编号 身高 收入 体重
1 1.75 15000 120
2 1.5 16000 140
3 1.6 20000 100
  • 如果通过欧式距离公式来判断两个人的差距,公式如下:

$$
L = \sqrt{(1.75-1.5)^2+(15000-16000)^2+(120-140)^2}
$$

  • 上述公式可以看出,基本上都是靠收入来决定了,而身高和体重基本上没有影响,那么对于实际生活中,身高和体重也要纳入到判断两个人的差距计算,所以我们应该采取把数据无量纲化

1.2.4.1 归一化
  • 归一化是一种线性变换过程,使得数据按照一定的规则重新映射到一个特定的范围,通常是 [0, 1],通过下面的方式实现:

$$
Xnorm= \frac{X−Xmin}{Xmax−Xmin}
$$

  • 上述公式中,Xmin 和 Xmax 分别是原始数据集中的最小值和最大值,X 表示当前需要归一化的值

  • 归一化的目的是为了消除不同特征值之间量级上的差异,从而避免因不同特征值的范围差异对模型训练产生不良影响

  • 缺点就是对异常值非常敏感

  • MinMaxScaler:是一种用于归一化数据的技术,通常用于机器学习和数据分析中。它将每个特征的值缩放到一个指定的范围内,通常是 [0, 1]。MinMaxScalersklearn.preprocessing 模块中的一个类,可以方便地应用于数据集,MinMaxScaler()用来实例化对象,参数如下:

    • feature_range:把数据压缩到的范围,默认是(0,1)

数据为列表归一化案例:最大值和最小值是在对应的列中找

from sklearn.preprocessing import MinMaxScaler
​
def min_max_scaler_list():
    data = [[1, 2], [2, 3], [3, 5], [4, 6], [5, 8]]
    print('原始数据:\n', data)
    min_max_scaler = MinMaxScaler()
    X_minmax = min_max_scaler.fit_transform(data)
    print('归一化后数据:\n', X_minmax)

数据为ndarray归一化案例:

from sklearn.preprocessing import MinMaxScaler
from sklearn.feature_extraction import DictVectorizer
import numpy as np
​
def min_max_scaler_ndarray():
    data = [{'city': '成都', 'age': 30, 'temperature': 200}, {'city': '重庆', 'age': 33, 'temperature': 60},
            {'city': '北京', 'age': 42, 'temperature': 80}]
    transfer = DictVectorizer(sparse=False)
    data = transfer.fit_transform(data)
    print('原始数据:\n', data)
    min_max_scaler = MinMaxScaler()
    X_minmax = e(data)
    print('归一化后数据:\n', X_minmax)

数据为DataFrame归一化案例:

from sklearn.preprocessing import MinMaxScaler
import pandas as pd
​
def min_max_scaler_dataframe():
    data = [[1, 2], [2, 3]]
    data = pd.DataFrame(data=data, index=["第一行", "第二行"], columns=["第一列", "第二列"])
    print('原始数据:\n', data)
    transfer = MinMaxScaler(feature_range=(0, 1))
    X_minmax = transfer.fit_transform(data)
    print('归一化后数据:\n', X_minmax)
1.2.4.2 标准化
  • 在机器学习中,标准化是一种数据预处理技术,也称为数据归一化或特征缩放。它的目的是将不同特征的数值范围缩放到统一的标准范围,以便更好地适应一些机器学习算法,特别是那些对输入数据的尺度敏感的算法

  • 最常见的标准化方法是 Z-score 标准化,也称为零均值标准化。它通过对每个特征的值减去其均值,再除以其标准差,将数据转换为均值为0,标准差为1的分布。这可以通过以下公式计算:z 是转换后的数值,x 是原始数据的值,μ 是该特征的均值,σ 是该特征的标准差

$$
z=\frac{x−μ}{σ}
$$

  • 上述公式参数求解公式如下:

$$
z=(x-μ)/σ \\μ=E(X) \\\sigma=\sqrt{\frac{\sum_{i=1}^{n}\left(x_{i}-\mu\right)^{2}}{n}}
$$

  • StandardScaler:是 sklearn.preprocessing 模块中的一个类,用于执行标准化操作,即将数据转换为具有零均值和单位方差的形式,StandardScaler()用来实例化对象,与 MinMaxScaler 一样,原始数据类型可以是 list、DataFrame 和 ndarray

案例:

from sklearn.preprocessing import StandardScaler
import numpy as np
​
# 标准化
def standard_scaler():
    # 示例数据集
    data = np.array([
       [1, 2],
       [2, 3],
       [3, 5],
       [4, 6],
       [5, 8]
    ])
    print("原始数据:\n", data)
    # 实例化StandardScaler对象
    scaler = StandardScaler()
    data_scaled = scaler.fit_transform(data)
    print("标准化后数据:\n", data_scaled)
    # 查看标准化后的均值和标准差
    print("均值:", scaler.mean_)
    print("标准差:", scaler.scale_)
1.2.5 特征降维
  • 特征降维是在机器学习和数据挖掘中常用的一种技术,用于减少数据集中的特征数量。这通常是为了简化模型,提高计算效率,或者为了去除噪音和冗余信息

  • 特征降维方式很有,掌握以下两种:

    • 特征选择方法:直接选择最具代表性的特征,而不是创建新的特征,特征选择有以下方式可以实现:

      • 过滤法:根据一些评分标准对特征进行评分,然后选择得分最高的特征,可以分为低方差过滤法和相关系数法

    • 主成分分析(PCA):通过正交变换将数据转换为一系列线性无关的主成分,其中每个成分都是原始变量的线性组合,且按方差递减顺序排列,形成新的特征,新的特征数量会底于之前特征数量

1.2.5.1 特征选择-VarianceThreshold 低方差过滤
  • VarianceThreshold 低方差过滤是指如果一个特征的方差很小,说明这个特征的值在样本中几乎相同,模型很难通过这个特征去区分不同的对象,比如区分英短和美短两种猫,有一个特征是有两个眼睛,那么这个特征就可以去掉

  • 实现步骤:

    • 准备数据:准备需要过滤特征的数据集

    • 计算方差:计算每个特征的方差,可以使用 Pandas 的 var() 方法

    • 设定阈值:设定一个阈值来确定哪些特征的方差过低。这个阈值通常是根据数据的具体情况和业务需求来设定的。如果方差低于这个阈值,则认为该特征的变异程度太低,可以考虑移除

    • 过滤特征:使用 scikit-learn 中的 VarianceThreshold 类来移除方差低于指定阈值的特征

    • 查看结果:查看经过低方差过滤后的特征数量,并确认哪些特征被移除了

  • VarianceThreshold():用于实例化对象,属于sklearn.feature_selection的子类,用来实现底方差过滤特征选择,参数如下:

    • threshold:设置方差阈值,低于阈值的特征删除

案例:

from sklearn.datasets import load_iris
from sklearn.feature_selection import VarianceThreshold
​
​
def variance_threshold():
    data = load_iris()
    X = data.data
    # 创建VarianceThreshold对象,阈值设置为0.5
    selector = VarianceThreshold(threshold=0.5)
    X_transformed = selector.fit_transform(X)
    # 输出结果
    print('原始数据:\n', X[0:5])
    print('过滤之后:\n', X_transformed[0:5])
    print("原始特征数量:\n", X.shape[1])
    print("减少特征数量:\n", X_transformed.shape[1])
    # 获取一个布尔掩码,指示哪些特征被保留下来
    mask = selector.get_support()
    new_features = np.arange(X.shape[1])[mask]
    print('保留下来的特征索引:\n',new_features)
    # 获取被移除的特征索引
    removed_features = np.arange(X.shape[1])[~mask]
    print("被移除的特征索引:\n", removed_features)
1.2.5.2 特征选择-根据相关系数
  • 根据特征与目标变量之间的相关系数进行特征选择,这种方法的基本思想是选择那些与目标变量(在监督学习任务中)相关性较高的特征。相关系数可以用来衡量两个变量之间的线性关系强度

  • 开发中一般不使用求相关系数的方法,一般使用主成分分析,因为主成分分样过程中就包括了求相关系数

  • 常见的相关系数计算方法包括皮尔逊相关系数(Pearson correlation coefficient)、斯皮尔曼相关系数(Spearman correlation coefficient)

  • 皮尔逊相关系数:皮尔逊相关系数衡量的是两个变量之间的线性关系强度和方向。它是通过计算两个变量的协方差除以它们的标准差的乘积来得到的,其值介于-1与1之间,计算公式如下:

$$
\rho=\frac{\operatorname{Cov}(x, y)}{\sqrt{D x} \cdot \sqrt{D y}}=\frac{E[(x_i-E x)(y_i-E y)]}{\sqrt{D x} \cdot \sqrt{D y}}=\frac{\sum_{i=1}^{n}(x_i-\tilde{x})(y_i-\bar{y}) /(n-1)}{\sqrt{\sum_{i=1}^{n}(x_i-\bar{x})^{2} /(n-1)} \cdot \sqrt{\sum_{i=1}^{n}(y_i-\bar{y})^{2} /(n-1)}}
$$

  • 对于上述公式:

    • $\bar{x}$ 表示数据一的平均值,$\bar{y}$ 表示数据二的平均值,n 表示数据总个数

    • |ρ|<0.4 为低度相关; 0.4<=|ρ|<0.7 为显著相关; 0.7<=|ρ|<1 为高度相关

    • ρ = 1,正相关表示随着一个变量增加,另一个变量也倾向于增加(系数为正值)

    • ρ = -1, 负相关表示随着一个变量增加,另一个变量倾向于减少(系数为负值)

    • ρ = 0,两个变量不存在线性关系

  • scipy.stats.pearsonr(x, y) 是 Scipy 库中用于计算两个变量之间 Pearson 相关系数的函数。Pearson 相关系数是衡量两个变量之间线性关系强度和方向的指标。相关系数的范围是 -1 到 1,参数和返回值如下:

    • x: 第一个输入数组,可以是一维的序列

    • y: 第二个输入数组,必须与 x 具有相同的长度

    • pearsonr 函数返回一个元组 (correlation, pvalue)

      • correlation: Pearson 相关系数。

      • pvalue: 假设检验的 p 值,用于判断相关系数是否显著不同于 0

案例:

from scipy.stats import pearsonr
import numpy as np
​
def pearsonr_demo():
    # 完全正相关
    x = np.array([1, 2, 3, 4, 5])
    y = np.array([2, 4, 6, 8, 10])
    # 计算 Pearson 相关系数及其 p 值
    correlation, pvalue = pearsonr(x, y)
    print('Pearson 相关系数:', correlation)
    print('p 值:', correlation)
    
    # 完全负相关
    y2 = np.array([5, 4, 3, 2, 1])
    correlation2, pvalue2 = pearsonr(x, y2)
    print('Pearson 相关系数:', correlation2)
    print('p 值:', correlation2)
    
    # 随机数据
    y3 = np.array([np.random.rand() for _ in range(len(x))])
    correlation3, pvalue3 = pearsonr(x, y3)
    print('Pearson 相关系数:', correlation3)
    print('p 值:', correlation3)