4.3 特征工程高级技巧


文档摘要

4.3 特征工程高级技巧 Scikit-learn 高级主题:特征工程高级技巧详解与实践 特征工程是机器学习流程中至关重要的一环,它直接影响着模型的性能上限。高质量的特征能够显著提升模型的预测能力,降低模型复杂度,并加速训练过程。在 Scikit-learn 中,我们已经掌握了一些基础的特征工程方法,例如数据标准化、归一化、独热编码等。然而,面对日益复杂的数据和机器学习任务,我们需要更高级、更精细的特征工程技巧来挖掘数据中更深层次的信息。 多项式特征与交互特征 1.1 原理与应用场景 在许多实际问题中,特征之间并非线性关系,简单的线性模型可能难以捕捉数据中的复杂模式。多项式特征和交互特征能够扩展特征空间,引入特征的高阶项和组合项,从而让线性模型能够拟合非线性关系。

4.3 特征工程高级技巧

Scikit-learn 高级主题:特征工程高级技巧详解与实践

特征工程是机器学习流程中至关重要的一环,它直接影响着模型的性能上限。高质量的特征能够显著提升模型的预测能力,降低模型复杂度,并加速训练过程。在 Scikit-learn 中,我们已经掌握了一些基础的特征工程方法,例如数据标准化、归一化、独热编码等。然而,面对日益复杂的数据和机器学习任务,我们需要更高级、更精细的特征工程技巧来挖掘数据中更深层次的信息。

1. 多项式特征与交互特征

1.1 原理与应用场景

在许多实际问题中,特征之间并非线性关系,简单的线性模型可能难以捕捉数据中的复杂模式。多项式特征和交互特征能够扩展特征空间,引入特征的高阶项和组合项,从而让线性模型能够拟合非线性关系。

  • 多项式特征:指的是对现有特征进行幂运算,例如将特征 x 扩展为 x, x^2, x^3 等。这有助于模型捕捉特征的非线性效应。

  • 交互特征:指的是将两个或多个特征进行组合,生成新的特征,例如将特征 x1x2 组合为 x1*x2。这有助于模型捕捉特征之间的相互作用。

多项式特征和交互特征常用于以下场景:

  • 特征之间存在非线性关系:当线性模型在原始特征空间中表现不佳时,可以尝试引入多项式特征或交互特征。

  • 需要捕捉特征之间的协同效应:例如,在房价预测中,房屋面积和地段可能存在交互效应,面积大的房子在好地段会更值钱。

  • 提升模型复杂度:在模型复杂度较低的情况下,可以通过增加特征维度来提升模型的拟合能力。

1.2 Scikit-learn 代码实践:PolynomialFeatures

Scikit-learn 提供了 PolynomialFeatures 类来方便地生成多项式特征和交互特征。

from sklearn.preprocessing import PolynomialFeatures import numpy as np # 示例数据 X = np.array([[1, 2], [3, 4], [5, 6]]) # 创建 PolynomialFeatures 对象,degree=2 表示生成 2 次多项式特征 poly = PolynomialFeatures(degree=2) # 拟合和转换数据 X_poly = poly.fit_transform(X) print("原始特征:\n", X) print("\n多项式特征 (degree=2):\n", X_poly)

代码详解:

  1. PolynomialFeatures(degree=2): 创建 PolynomialFeatures 对象,degree=2 参数指定生成最高次数为 2 的多项式特征。

  2. poly.fit_transform(X): fit_transform 方法用于拟合数据并进行转换。它会根据原始特征 X 生成多项式特征矩阵 X_poly

输出结果解释:

原始特征: [[1 2] [3 4] [5 6]] 多项式特征 (degree=2): [[ 1. 1. 2. 1. 2. 4.] [ 1. 3. 4. 9. 12. 16.] [ 1. 5. 6. 25. 30. 36.]]
  • 第一列 [1, 1, 1] 是偏置项 (bias term),默认包含。可以通过 include_bias=False 参数移除。

  • 第二列 [1, 3, 5] 是原始特征 X 的第一列。

  • 第三列 [2, 4, 6] 是原始特征 X 的第二列。

  • 第四列 [1, 9, 25] 是原始特征 X 第一列的平方项 (x1^2)。

  • 第五列 [2, 12, 30] 是原始特征 X 第一列和第二列的交互项 (x1*x2)。

  • 第六列 [4, 16, 36] 是原始特征 X 第二列的平方项 (x2^2)。

常用参数:

  • degree: 多项式的最高次数,默认为 2

  • interaction_only: 布尔值,默认为 False。如果设置为 True,则只生成交互特征,不生成特征自身的幂次方项。

  • include_bias: 布尔值,默认为 True。是否包含偏置项(全为 1 的列)。

  • order: 特征顺序,可选值为 'C' (C-order) 或 'F' (Fortran-order),影响特征列的排列顺序,通常无需修改。

实践应用示例:

from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error import pandas as pd # 加载 Boston 房价数据集 from sklearn.datasets import load_boston boston = load_boston() X, y = boston.data, boston.target feature_names = boston.feature_names # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 线性回归模型 (原始特征) lr_original = LinearRegression() lr_original.fit(X_train, y_train) y_pred_original = lr_original.predict(X_test) mse_original = mean_squared_error(y_test, y_pred_original) print(f"原始特征线性回归 MSE: {mse_original:.4f}") # 多项式特征扩展 (degree=2) poly = PolynomialFeatures(degree=2) X_train_poly = poly.fit_transform(X_train) X_test_poly = poly.transform(X_test) # 线性回归模型 (多项式特征) lr_poly = LinearRegression() lr_poly.fit(X_train_poly, y_train) y_pred_poly = lr_poly.predict(X_test_poly) mse_poly = mean_squared_error(y_test, y_pred_poly) print(f"多项式特征线性回归 MSE: {mse_poly:.4f}")

结果分析:

通常情况下,引入多项式特征后,线性回归模型的 MSE 会降低,表明模型性能得到提升。但需要注意的是,过高的多项式次数可能会导致过拟合,需要通过交叉验证等方法选择合适的 degree

2. 特征选择高级技巧

特征选择旨在从原始特征集中选择出最相关、最有效的特征子集,从而:

  • 提升模型性能:去除冗余和噪声特征,提高模型的泛化能力。

  • 降低模型复杂度:减少特征维度,简化模型,降低计算成本。

  • 提高模型可解释性:选择关键特征,更容易理解模型的决策过程。

Scikit-learn 提供了多种特征选择方法,除了基础的方差选择法和单变量特征选择法,还有一些更高级的技巧,例如基于模型的特征选择和递归特征消除。

2.1 基于模型的特征选择:SelectFromModel

SelectFromModel 是一种元转换器,它可以利用训练好的模型(例如线性模型、树模型)的特征重要性或系数作为指标,选择特征子集。

原理:

  1. 训练模型:使用指定的评估器(estimator)在训练数据上训练模型。

  2. 获取特征重要性/系数:根据评估器提供的 feature_importances_ 属性(例如树模型)或 coef_ 属性(例如线性模型),获取每个特征的重要性或系数。

  3. 设定阈值:根据特征重要性/系数设定一个阈值。

  4. 特征选择:选择特征重要性/系数高于阈值的特征。

Scikit-learn 代码实践:SelectFromModel

from sklearn.feature_selection import SelectFromModel from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score # 加载 Iris 数据集 iris = load_iris() X, y = iris.data, iris.target # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 随机森林分类器 rf = RandomForestClassifier(random_state=42) rf.fit(X_train, y_train) y_pred_rf = rf.predict(X_test) accuracy_rf = accuracy_score(y_test, y_pred_rf) print(f"原始特征随机森林准确率: {accuracy_rf:.4f}") # 基于模型的特征选择 (使用随机森林作为评估器) sfm = SelectFromModel(rf, threshold='median') # 选择特征重要性高于中位数的特征 sfm.fit(X_train, y_train) X_train_sfm = sfm.transform(X_train) X_test_sfm = sfm.transform(X_test) # 特征选择后的随机森林模型 rf_sfm = RandomForestClassifier(random_state=42) rf_sfm.fit(X_train_sfm, y_train) y_pred_sfm = rf_sfm.predict(X_test_sfm) accuracy_sfm = accuracy_score(y_test, y_pred_sfm) print(f"特征选择后随机森林准确率: {accuracy_sfm:.4f}") print("\n选择的特征索引:", sfm.get_support()) print("原始特征名称:", iris.feature_names)

代码详解:

  1. SelectFromModel(rf, threshold='median'): 创建 SelectFromModel 对象,rf 参数指定使用随机森林分类器作为评估器,threshold='median' 参数指定阈值为特征重要性的中位数。

  2. sfm.fit(X_train, y_train): 训练 SelectFromModel 对象,它会在训练数据上训练随机森林模型,并计算特征重要性。

  3. sfm.transform(X_train)sfm.transform(X_test): 使用训练好的 SelectFromModel 对象对训练集和测试集进行特征选择,得到选择后的特征矩阵 X_train_sfmX_test_sfm

  4. sfm.get_support(): 获取被选择特征的布尔索引数组。

常用参数:

  • estimator: 评估器对象,需要具有 feature_importances_coef_ 属性,例如 RandomForestClassifier, LogisticRegression, LinearSVC 等。

  • threshold: 特征选择的阈值,可以是数值,也可以是字符串(例如 'median', 'mean')。默认为 None,此时会使用评估器自身定义的阈值(如果有)。

  • prefit: 布尔值,默认为 False。如果设置为 True,则假设评估器已经训练完成,直接使用已训练的模型进行特征选择。

  • norm_order: 用于归一化系数的范数阶数,仅当评估器具有 coef_ 属性时有效。

  • max_features: 选择的最大特征数量,即使满足阈值的特征数量超过 max_features,也只选择前 max_features 个特征。

适用场景:

  • 当你已经选择了一个模型,并且想利用该模型进行特征选择时。

  • 当评估器能够提供可靠的特征重要性或系数信息时。

2.2 递归特征消除 (RFE):RFERFECV

递归特征消除 (Recursive Feature Elimination, RFE) 是一种贪婪的特征选择方法,它通过不断迭代地训练模型,移除最不重要的特征,直到达到预设的特征数量。

原理:

  1. 训练模型:使用所有特征训练模型。

  2. 计算特征重要性/系数:根据模型获取特征重要性或系数。

  3. 移除最不重要特征:移除重要性最低(或系数绝对值最小)的一个或多个特征。

  4. 重复步骤 1-3:在剩余特征上重复训练模型和移除特征,直到达到预设的特征数量。

RFECV (Recursive Feature Elimination with Cross-Validation) 是 RFE 的改进版本,它通过交叉验证来自动选择最佳的特征数量。

Scikit-learn 代码实践:RFERFECV

from sklearn.feature_selection import RFE, RFECV from sklearn.linear_model import LogisticRegression from sklearn.datasets import load_digits from sklearn.model_selection import StratifiedKFold from sklearn.metrics import accuracy_score # 加载 digits 数据集 digits = load_digits() X, y = digits.data, digits.target # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 逻辑回归分类器 logistic = LogisticRegression(solver='liblinear', random_state=42) # RFE 特征选择 (选择 30 个特征) rfe = RFE(logistic, n_features_to_select=30) rfe.fit(X_train, y_train) X_train_rfe = rfe.transform(X_train) X_test_rfe = rfe.transform(X_test) # 特征选择后的逻辑回归模型 (RFE) logistic_rfe = LogisticRegression(solver='liblinear', random_state=42) logistic_rfe.fit(X_train_rfe, y_train) y_pred_rfe = logistic_rfe.predict(X_test_rfe) accuracy_rfe = accuracy_score(y_test, y_pred_rfe) print(f"RFE 特征选择后逻辑回归准确率: {accuracy_rfe:.4f}") print("RFE 选择的特征数量:", X_train_rfe.shape[1]) # RFECV 特征选择 (交叉验证选择最佳特征数量) rfecv = RFECV(logistic, step=1, cv=StratifiedKFold(5), scoring='accuracy') rfecv.fit(X_train, y_train) X_train_rfecv = rfecv.transform(X_train) X_test_rfecv = rfecv.transform(X_test) # 特征选择后的逻辑回归模型 (RFECV) logistic_rfecv = LogisticRegression(solver='liblinear', random_state=42) logistic_rfecv.fit(X_train_rfecv, y_train) y_pred_rfecv = logistic_rfecv.predict(X_test_rfecv) accuracy_rfecv = accuracy_score(y_test, y_pred_rfecv) print(f"RFECV 特征选择后逻辑回归准确率: {accuracy_rfecv:.4f}") print("RFECV 选择的最佳特征数量:", rfecv.n_features_) print("RFECV 最佳特征数量对应的交叉验证得分:", rfecv.grid_scores_.mean())

代码详解:

  • RFE(logistic, n_features_to_select=30): 创建 RFE 对象,logistic 参数指定使用逻辑回归分类器作为评估器,n_features_to_select=30 参数指定最终选择 30 个特征。

  • RFECV(logistic, step=1, cv=StratifiedKFold(5), scoring='accuracy'): 创建 RFECV 对象, step=1 表示每次迭代移除一个特征,cv=StratifiedKFold(5) 表示使用 5 折分层交叉验证,scoring='accuracy' 表示使用准确率作为评估指标。

  • rfecv.n_features_: 获取 RFECV 选择的最佳特征数量。

  • rfecv.grid_scores_: 获取 RFECV 在交叉验证过程中不同特征数量下的平均得分。

常用参数 (RFE):

  • estimator: 评估器对象,需要具有 coef_ 属性或 feature_importances_ 属性,例如 LogisticRegression, LinearSVC, DecisionTreeClassifier 等。

  • n_features_to_select: 最终选择的特征数量,可以为整数或浮点数(表示选择特征的比例)。

  • step: 每次迭代移除的特征数量或比例,默认为 1

常用参数 (RFECV):

  • 除了 RFE 的参数外,RFECV 还有以下参数:

    • cv: 交叉验证策略,可以是交叉验证生成器,整数(表示折数),或 None(使用默认的 5 折交叉验证)。

    • scoring: 评估指标,例如 'accuracy', 'roc_auc', 'f1_score' 等。

适用场景:

  • 当你想自动选择最佳特征子集,并且评估器能够提供特征重要性或系数信息时。

  • RFECV 尤其适用于不确定最佳特征数量的情况,它可以通过交叉验证自动选择。

3. 特征缩放与转换高级技巧

特征缩放和转换是特征工程中非常重要的步骤,它可以将不同尺度或分布的特征转换为统一的尺度或分布,从而:

  • 提升模型性能:某些模型(例如梯度下降法、距离度量模型)对特征尺度敏感,特征缩放可以使其更快收敛或更准确。

  • 提高模型稳定性:避免某些特征对模型产生过大的影响。

  • 方便特征比较:将特征缩放到统一尺度后,更容易比较不同特征的重要性。

除了常用的 StandardScalerMinMaxScaler,Scikit-learn 还提供了一些更高级的特征缩放和转换技巧。

3.1 PowerTransformer:幂变换

PowerTransformer 是一种用于稳定方差和使数据更接近正态分布的转换器。它支持两种幂变换方法:Yeo-Johnson 变换和 Box-Cox 变换。

  • Yeo-Johnson 变换:可以应用于任何实数数据,包括正数、负数和零。

  • Box-Cox 变换:只能应用于正数数据。

原理:

幂变换通过对特征进行非线性变换,改变数据的分布形状。例如,对于右偏分布的数据,幂变换可以将其向左拉伸,使其更接近正态分布。

Scikit-learn 代码实践:PowerTransformer

from sklearn.preprocessing import PowerTransformer import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 生成右偏分布数据 rng = np.random.RandomState(42) X_skewed = rng.poisson(lam=3, size=(100, 2)) + np.random.randn(100, 2) # PowerTransformer (Yeo-Johnson 变换) pt_yj = PowerTransformer(method='yeo-johnson') X_yj = pt_yj.fit_transform(X_skewed) # PowerTransformer (Box-Cox 变换) (需要正数数据,这里简单处理一下) X_skewed_pos = X_skewed - X_skewed.min() + 1e-6 # 确保数据为正数 pt_bc = PowerTransformer(method='box-cox') X_bc = pt_bc.fit_transform(X_skewed_pos) # 绘制原始数据和转换后数据的分布 fig, axes = plt.subplots(1, 3, figsize=(15, 5)) sns.histplot(X_skewed[:, 0], kde=True, ax=axes[0], label='Original') sns.histplot(X_yj[:, 0], kde=True, ax=axes[1], label='Yeo-Johnson') sns.histplot(X_bc[:, 0], kde=True, ax=axes[2], label='Box-Cox') axes[0].set_title('Original Data') axes[1].set_title('Yeo-Johnson Transformed') axes[2].set_title('Box-Cox Transformed') plt.legend() plt.show()

代码详解:

  1. PowerTransformer(method='yeo-johnson'): 创建 PowerTransformer 对象,method='yeo-johnson' 参数指定使用 Yeo-Johnson 变换。

  2. PowerTransformer(method='box-cox'): 创建 PowerTransformer 对象,method='box-cox' 参数指定使用 Box-Cox 变换。

  3. pt_yj.fit_transform(X_skewed)pt_bc.fit_transform(X_skewed_pos): 拟合和转换数据,得到转换后的特征矩阵 X_yjX_bc

常用参数:

  • method: 幂变换方法,可选值为 'yeo-johnson''box-cox',默认为 'yeo-johnson'

  • standardize: 布尔值,默认为 True。是否在幂变换后对数据进行标准化(均值为 0,标准差为 1)。

适用场景:

  • 当你的数据分布偏斜,不符合正态分布假设时。

  • 当你想稳定特征的方差,使其更易于模型学习时。

  • 尤其适用于一些对数据分布有要求的模型,例如线性模型、高斯朴素贝叶斯等。

3.2 QuantileTransformer:分位数转换

QuantileTransformer 是一种将数据转换为均匀分布或正态分布的非线性转换器。它基于数据的分位数信息进行转换。

原理:

QuantileTransformer 将每个特征映射到 (0, 1) 区间上的均匀分布,或者通过设置 output_distribution='normal' 映射到正态分布。它通过学习数据的累积分布函数 (CDF) 来实现转换。

Scikit-learn 代码实践:QuantileTransformer

from sklearn.preprocessing import QuantileTransformer import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 生成混合分布数据 rng = np.random.RandomState(42) X_mixed = np.concatenate([rng.normal(0, 1, size=(100, 1)), rng.poisson(lam=3, size=(100, 1))], axis=1) # QuantileTransformer (均匀分布) qt_uniform = QuantileTransformer(output_distribution='uniform', random_state=42) X_uniform = qt_uniform.fit_transform(X_mixed) # QuantileTransformer (正态分布) qt_normal = QuantileTransformer(output_distribution='normal', random_state=42) X_normal = qt_normal.fit_transform(X_mixed) # 绘制原始数据和转换后数据的分布 fig, axes = plt.subplots(1, 3, figsize=(15, 5)) sns.histplot(X_mixed[:, 0], kde=True, ax=axes[0], label='Original') sns.histplot(X_uniform[:, 0], kde=True, ax=axes[1], label='Uniform') sns.histplot(X_normal[:, 0], kde=True, ax=axes[2], label='Normal') axes[0].set_title('Original Data') axes[1].set_title('Uniform Transformed') axes[2].set_title('Normal Transformed') plt.legend() plt.show()

代码详解:

  1. QuantileTransformer(output_distribution='uniform', random_state=42): 创建 QuantileTransformer 对象,output_distribution='uniform' 参数指定输出为均匀分布。

  2. QuantileTransformer(output_distribution='normal', random_state=42): 创建 QuantileTransformer 对象,output_distribution='normal' 参数指定输出为正态分布。

  3. qt_uniform.fit_transform(X_mixed)qt_normal.fit_transform(X_mixed): 拟合和转换数据,得到转换后的特征矩阵 X_uniformX_normal

常用参数:

  • output_distribution: 输出分布类型,可选值为 'uniform''normal',默认为 'uniform'

  • n_quantiles: 分位数的数量,默认为 1000。更大的 n_quantiles 可以更精确地逼近目标分布,但也可能导致过拟合。

  • random_state: 随机数种子,用于在处理 ties 时保持一致性。

  • subsample: 用于估计 CDF 的样本数量,默认为 1e5

适用场景:

  • 当你的数据分布复杂,不符合任何常见的分布假设时。

  • 当你想将数据转换为均匀分布或正态分布,以便更好地应用于某些模型时。

  • 对异常值鲁棒性较好,因为它是基于分位数进行转换,而不是均值和标准差。

4. 文本特征提取高级技巧

对于文本数据,除了基础的词袋模型 (CountVectorizer) 和 TF-IDF (TfidfVectorizer),还有一些更高级的文本特征提取技巧,例如 n-gram 特征、自定义分词器、停用词处理等。

4.1 n-gram 特征

n-gram 指的是文本中连续的 n 个词语组成的序列。例如,对于句子 "I love machine learning",2-gram (bigram) 特征包括 "I love"、"love machine"、"machine learning"。

n-gram 特征可以捕捉词语之间的局部顺序信息,对于一些任务(例如情感分析、文本分类)可能比 unigram (1-gram) 特征更有效。


发布者: 作者: 转发
评论区 (0)
U