Skip to content

数据特征选择

使用 Python 进行机器学习 - 特征选择

Section titled “使用 Python 进行机器学习 - 特征选择”

数据预处理之后,下一个关键步骤通常是特征选择。并非所有收集到的特征都对构建准确的机器学习模型同样有用。特征选择涉及从原始特征集中选择最有意义的特征子集。

用于训练模型的特征直接影响其性能。包含不相关或冗余的特征可能从多个方面对模型产生负面影响:

  • 提高模型性能: 相关特征带来更好的泛化能力和预测准确性,特别是对于更简单的模型。
  • 减少过拟合: 更少的特征可以降低模型复杂度,使其不易拟合训练数据中的噪声(即过拟合)。
  • 更快的训练时间: 使用更少特征训练模型需要更少的计算。
  • 提高可解释性: 特征较少的模型通常更容易理解和解释。
  • 降低维度: 解决在更高维度空间中性能下降的“维度灾难”问题。

特征选择是一个自动或手动选择对你感兴趣的预测变量或输出贡献最大的特征的过程。它不同于主成分分析(PCA)等降维技术,后者将特征转换到低维空间,可能创建新的、不易解释的特征。

Scikit-learn 提供了几种自动特征选择的方法:

这项技术通过统计检验独立评估每个特征与目标变量之间关系的强度。选择得分最高的特征。常用的检验包括针对分类特征的卡方检验 (chi2),以及针对数值特征的方差分析 F 值(分类使用 f_classif,回归使用 f_regression)。Scikit-learn 的 SelectKBest 类实现了这一点,允许你选择得分最高的 ‘k’ 个特征。

示例:使用卡方检验从 Pima Indians 糖尿病数据集中选择前 4 个特征。

import pandas as pd
import numpy as np
from sklearn.feature_selection import SelectKBest, chi2
from sklearn.preprocessing import MinMaxScaler # Chi2 requires non-negative features
# --- Load Data (Example using a URL for reproducibility) ---
# URL for the Pima Indians Diabetes dataset
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv'
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
dataframe = pd.read_csv(url, names=names)
# Separate features (X) and target (y)
X = dataframe[names[:-1]]
y = dataframe[names[-1]]
# Chi-squared requires non-negative values, scale data to [0, 1]
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)
# --- Feature Selection ---
# Select top 4 features based on chi-squared test
selector = SelectKBest(score_func=chi2, k=4)
fit = selector.fit(X_scaled, y)
# Get scores and selected feature indices
scores = fit.scores_
selected_indices = selector.get_support(indices=True)
selected_features = X.columns[selected_indices]
# --- Output Results ---
np.set_printoptions(precision=2)
print('Feature Scores:', scores)
print('\nSelected Feature Indices:', selected_indices)
print('Selected Features:', selected_features.tolist())
# Transform X to include only selected features
featured_data = fit.transform(X_scaled)
print(f"\nShape of data with selected features: {featured_data.shape}")
print("First 4 rows of featured data:\n", featured_data[0:4])

输出解释:此代码加载糖尿病数据集,将特征缩放到非负范围(chi2 所必需),然后应用 SelectKBest 方法并使用 chi2 检验来寻找与“class”标签相关性最强的 4 个特征。它打印每个特征的统计得分,并列出选定的特征名称(‘plas’, ‘test’, ‘mass’, ‘age’)。最后,它显示了只包含这些选定特征的前几行数据。

了解更多关于单变量选择的信息:https://scikit-learn.org/stable/modules/feature_selection.html#univariate-feature-selection

2. 递归特征消除 (Recursive Feature Elimination, RFE)

Section titled “2. 递归特征消除 (Recursive Feature Elimination, RFE)”

RFE 通过递归地移除最不重要的特征,并在剩余特征上构建模型来工作。它使用一个外部评估器(如逻辑回归或 SVM)来为特征分配权重或重要性得分。在每次迭代中,得分最低的特征被移除,直到达到所需的特征数量。

示例:使用 RFE 和逻辑回归选择前 3 个特征。

import pandas as pd
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
# --- Load Data (Use previously loaded 'dataframe') ---
if 'dataframe' not in locals(): # Basic check if data is loaded
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv'
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
dataframe = pd.read_csv(url, names=names)
# Separate features (X) and target (y)
X = dataframe[names[:-1]]
y = dataframe[names[-1]]
# --- Feature Selection ---
# Initialize the model (estimator) for RFE
model = LogisticRegression(solver='liblinear') # Using 'liblinear' solver
# Initialize RFE to select 3 features
# Note: Use 'n_features_to_select' instead of the old positional argument
rfe = RFE(estimator=model, n_features_to_select=3)
fit = rfe.fit(X, y)
# --- Output Results ---
selected_mask = fit.support_
ranking = fit.ranking_
selected_features = X.columns[selected_mask]
print(f"Number of Features Selected: {fit.n_features_}")
print(f"Selected Features Mask: {selected_mask}")
print(f"Selected Features Names: {selected_features.tolist()}")
print(f"Feature Ranking: {ranking}")

输出解释:此代码使用逻辑回归作为基础模型,应用 RFE 来识别最佳的 3 个特征。输出显示一个布尔掩码,指示哪些特征被选中 (True),这些特征的名称(‘preg’, ‘mass’, ‘pedi’),以及一个排名,其中 ‘1’ 表示被选中的特征,数字越大表示越早被消除的特征。

了解更多关于 RFE 的信息:https://scikit-learn.org/stable/modules/feature_selection.html#recursive-feature-elimination

3. 主成分分析 (Principal Component Analysis, PCA) - 降维

Section titled “3. 主成分分析 (Principal Component Analysis, PCA) - 降维”

PCA 从技术上讲是一种降维技术,而不是特征选择。它不会选择原始特征,而是将数据转换成一组新的、不相关的特征,称为主成分。这些主成分捕捉数据中最大的方差。虽然它可以降低维度,但新的特征是旧特征的线性组合,可能不易解释。关键是,在应用 PCA 之前,数据应该进行缩放(例如,使用 StandardScaler)。

示例:应用 PCA 将数据集降维到 3 个主成分。

import pandas as pd
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# --- Load Data (Use previously loaded 'dataframe') ---
if 'dataframe' not in locals():
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv'
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
dataframe = pd.read_csv(url, names=names)
# Separate features (X) and target (y)
X = dataframe[names[:-1]]
y = dataframe[names[-1]]
# --- Scale Data (Essential for PCA) ---
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# --- Apply PCA ---
# Initialize PCA to find 3 components
pca = PCA(n_components=3)
fit = pca.fit(X_scaled)
# --- Output Results ---
explained_variance_ratio = fit.explained_variance_ratio_
components = fit.components_
print(f"Explained Variance Ratio by component: {explained_variance_ratio}")
print(f"Total variance explained by 3 components: {explained_variance_ratio.sum():.4f}")
print(f"\nShape of PCA components: {components.shape}")
# print("PCA Components (Loadings):\n", components)
# Transform data to the new PCA space
X_pca = fit.transform(X_scaled)
print(f"\nShape of transformed data: {X_pca.shape}")
print("First 4 rows of PCA-transformed data:\n", X_pca[0:4])

输出解释:此代码首先缩放数据,然后应用 PCA 将 8 个原始特征降维到 3 个主成分。它显示了每个主成分捕获的数据方差比例以及选定的 3 个主成分捕获的总方差。输出还显示了转换到新的 3 维空间后的数据形状和前几行。请注意,转换后的值不直接对应于原始特征。

了解更多关于 PCA 的信息:https://scikit-learn.org/stable/modules/decomposition.html#pca

4. 基于树模型的特征重要性 (Feature Importance from Tree-based Models)

Section titled “4. 基于树模型的特征重要性 (Feature Importance from Tree-based Models)”

Random Forest 和 Extra Trees 等集成树模型在训练期间会计算特征重要性。这个重要性得分反映了每个特征对集成模型中所有树的不纯度(例如 Gini 不纯度或熵)减少的贡献程度。得分越高的特征被认为越重要。

示例:使用 ExtraTreesClassifier 查找特征重要性。

import pandas as pd
import numpy as np
from sklearn.ensemble import ExtraTreesClassifier
import matplotlib.pyplot as plt # For visualization
# --- Load Data (Use previously loaded 'dataframe') ---
if 'dataframe' not in locals():
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv'
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
dataframe = pd.read_csv(url, names=names)
# Separate features (X) and target (y)
X = dataframe[names[:-1]]
y = dataframe[names[-1]]
# --- Calculate Feature Importance ---
# Initialize the model
model = ExtraTreesClassifier(n_estimators=100, random_state=42)
model.fit(X, y)
# Get feature importances
importances = model.feature_importances_
feature_names = X.columns
# Create a Series for better display
importance_series = pd.Series(importances, index=feature_names).sort_values(ascending=False)
# --- Output Results ---
print("Feature Importances:\n", importance_series)
# --- Visualize (Optional) ---
# plt.figure(figsize=(10, 6))
# importance_series.plot(kind='bar')
# plt.title('Feature Importances from ExtraTreesClassifier')
# plt.ylabel('Importance Score')
# plt.show()
# Description: The code would generate a bar chart showing the importance score for each feature,
# allowing easy visual comparison. Features like 'plas', 'age', and 'mass' often show higher importance
# for this dataset.
# 说明:此代码将生成一个条形图,显示每个特征的重要性得分,
# 便于直观比较。对于此数据集,'plas'、'age' 和 'mass' 等特征通常显示出更高重要性。

输出解释:此示例训练一个 ExtraTreesClassifier 并提取计算出的每个特征的重要性得分。得分被打印出来,并按从高到低的顺序排序。得分越高,表示该特征在集成树的预测中越有用。可视化(在文本输出中被注释掉)有助于快速识别最重要的特征。

了解更多关于基于树的特征重要性的信息:https://scikit-learn.org/stable/modules/ensemble.html#feature-importance-evaluation

还存在其他方法,包括:

  • L1 正则化 (Lasso): 使用 L1 正则化训练的线性模型倾向于产生稀疏系数,有效地将不太重要特征的系数设为零,从而执行特征选择。
  • 置换重要性 (Permutation Importance): 一种模型无关的技术,通过随机打乱单个特征的值来衡量模型的得分下降程度。
  • 序列特征选择 (Sequential Feature Selection, SFS): 基于模型性能迭代添加(前向选择)或移除(后向选择)特征的方法。

关于数据泄露的重要说明: 为了避免过于乐观的结果,特征选择最好在交叉验证循环内部执行。这意味着在每次迭代中,仅根据训练折叠的数据进行特征选择,防止验证折叠的信息影响选择过程。请参阅 Scikit-learn Pipeline 文档了解如何集成这些步骤:https://scikit-learn.org/stable/modules/compose.html#pipeline