3.4 降维任务 Scikit-learn 实践应用:降维任务详解 降维任务概述 1.1 什么是降维? 降维,顾名思义,是指降低数据特征的维度。从数学角度来看,降维是将原始高维空间的数据映射到一个低维空间的过程。这个过程的目标是在减少特征数量的同时,最大程度地保留原始数据的内在结构和信息。 1.2 为什么需要降维? 降维的重要性体现在以下几个方面: 缓解维度灾难 (Curse of Dimensionality): 在高维空间中,数据变得稀疏,距离度量失效,导致许多机器学习算法的性能急剧下降。降维可以有效地缓解维度灾难,提升模型的泛化能力。 提高计算效率: 降低特征维度可以显著减少模型训练和预测的计算量,加快算法的运行速度,尤其是在处理大规模数据集时效果更加明显。
1.1 什么是降维?
降维,顾名思义,是指降低数据特征的维度。从数学角度来看,降维是将原始高维空间的数据映射到一个低维空间的过程。这个过程的目标是在减少特征数量的同时,最大程度地保留原始数据的内在结构和信息。
1.2 为什么需要降维?
降维的重要性体现在以下几个方面:
缓解维度灾难 (Curse of Dimensionality): 在高维空间中,数据变得稀疏,距离度量失效,导致许多机器学习算法的性能急剧下降。降维可以有效地缓解维度灾难,提升模型的泛化能力。
提高计算效率: 降低特征维度可以显著减少模型训练和预测的计算量,加快算法的运行速度,尤其是在处理大规模数据集时效果更加明显。
数据可视化: 高维数据难以直接可视化,而降维可以将数据投影到二维或三维空间,方便我们进行数据探索和模式识别。
去除噪声和冗余信息: 原始数据中可能存在噪声和冗余特征,降维可以帮助我们提取关键特征,去除不相关信息,提高模型的鲁棒性和准确性。
特征选择和特征提取: 降维可以作为特征工程的一部分,帮助我们选择更重要的特征或提取更有效的特征表示,提升模型的解释性和性能。
1.3 降维的类型
降维方法主要分为两大类:
特征选择 (Feature Selection): 从原始特征集中选择一部分最相关的特征子集,直接移除不相关的或冗余的特征。特征选择方法保持了原始特征的含义,易于理解和解释。
特征提取 (Feature Extraction): 通过某种变换将原始特征转换为一组新的特征,新的特征是原始特征的组合或映射。特征提取方法创建了新的特征空间,通常能够更有效地捕捉数据中的潜在结构,但新特征的含义可能不如原始特征直观。
Scikit-learn 提供了丰富的降维算法,涵盖了特征选择和特征提取两大类。常用的降维方法包括:
特征选择方法:
方差阈值 (VarianceThreshold): 移除方差低于设定阈值的特征,适用于特征值方差较小的特征对模型贡献不大的情况。
单变量特征选择 (SelectKBest, SelectPercentile, SelectFpr, SelectFdr, SelectFwe): 基于单变量统计检验选择 K 个最佳特征或指定百分比的最佳特征。常用的统计检验方法包括卡方检验、F 检验、互信息等。
递归特征消除 (RFE, RFECV): 基于模型性能迭代地移除特征,直到达到指定的特征数量或模型性能最优。
基于模型的特征选择 (SelectFromModel): 利用训练好的模型(例如线性模型、树模型)的特征重要性或系数来选择特征。
特征提取方法:
主成分分析 (PCA - Principal Component Analysis): 通过线性变换将数据投影到方差最大的方向,提取主成分,是一种无监督的降维方法。
线性判别分析 (LDA - Linear Discriminant Analysis): 通过线性变换将数据投影到类间距离最大、类内距离最小的方向,是一种有监督的降维方法,常用于分类任务。
非负矩阵分解 (NMF - Non-negative Matrix Factorization): 将非负矩阵分解为两个非负矩阵的乘积,常用于图像处理、文本挖掘等领域。
截断奇异值分解 (TruncatedSVD): 对矩阵进行奇异值分解,保留最大的 k 个奇异值和奇异向量,用于稀疏数据的降维,例如文本数据的 TF-IDF 矩阵。
t-分布随机邻域嵌入 (t-SNE - t-distributed Stochastic Neighbor Embedding): 一种非线性降维方法,主要用于高维数据的可视化,能够很好地保留数据的局部结构。
均匀流形逼近和投影 (UMAP - Uniform Manifold Approximation and Projection): 一种新型的非线性降维方法,在保持全局结构和局部结构方面都表现出色,且计算效率较高。 (注意:UMAP 虽然非常流行且与 Scikit-learn 兼容,但它本身不是 Scikit-learn 核心库的一部分,需要单独安装 umap-learn 库。)
本文将重点介绍 方差阈值 (VarianceThreshold), SelectKBest, SelectFromModel, 主成分分析 (PCA), 线性判别分析 (LDA) 和 t-分布随机邻域嵌入 (t-SNE) 这几种常用的降维方法,并通过代码示例进行详细讲解。
3.1 特征选择方法
特征选择旨在从原始特征集中选择一个子集,而无需改变特征本身的表示形式。Scikit-learn 提供了多种特征选择器,下面我们将介绍几种常用的方法。
3.1.1 方差阈值 (VarianceThreshold)
VarianceThreshold 是一种简单但有效的特征选择方法,它移除方差低于设定阈值的特征。假设特征的方差很小,意味着该特征的取值变化不大,对模型的贡献可能较小。
代码示例:
import numpy as np from sklearn.feature_selection import VarianceThreshold # 示例数据 X = np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 1, 1]]) # 创建 VarianceThreshold 对象,设置阈值为 0.8 selector = VarianceThreshold(threshold=0.8) # 应用方差阈值选择 X_transformed = selector.fit_transform(X) print("原始数据:\n", X) print("降维后的数据:\n", X_transformed) print("保留的特征索引:", selector.get_support(indices=True))
代码详解:
导入模块: 导入 numpy 用于创建示例数据,导入 VarianceThreshold 类。
创建示例数据: X 是一个 6x3 的 numpy 数组,代表 6 个样本,3 个特征。
创建 VarianceThreshold 对象: VarianceThreshold(threshold=0.8) 创建一个方差阈值选择器,阈值设置为 0.8。这意味着方差小于 0.8 的特征将被移除。
应用方差阈值选择: selector.fit_transform(X) 先使用 fit 方法计算每个特征的方差,然后使用 transform 方法根据阈值移除方差过小的特征,并返回降维后的数据 X_transformed。
打印结果: 打印原始数据 X,降维后的数据 X_transformed,以及 selector.get_support(indices=True) 返回的保留特征的索引。
运行结果分析:
原始数据: [[0 0 1] [0 1 0] [1 0 0] [0 1 1] [0 1 0] [0 1 1]] 降维后的数据: [[1] [0] [0] [1] [0] [1]] 保留的特征索引: [2]
可以看到,原始数据有 3 个特征,经过方差阈值选择后,只保留了索引为 2 的特征(即第三列特征),因为第一列和第二列特征的方差都低于 0.8。
适用场景:
当你怀疑某些特征的方差很小,对模型贡献不大时,可以使用 VarianceThreshold 进行快速的特征筛选。
尤其适用于特征值分布比较集中的数据集。
参数详解:
threshold: 方差阈值,默认为 0。方差低于此阈值的特征将被移除。3.1.2 单变量特征选择 (SelectKBest)
SelectKBest 基于单变量统计检验选择 K 个最佳特征。它评估每个特征与目标变量之间的相关性(对于分类任务或回归任务),然后选择得分最高的 K 个特征。
代码示例 (分类任务,使用卡方检验):
from sklearn.datasets import load_iris from sklearn.feature_selection import SelectKBest, chi2 # 加载鸢尾花数据集 iris = load_iris() X, y = iris.data, iris.target # 创建 SelectKBest 对象,使用卡方检验,选择 2 个最佳特征 selector = SelectKBest(score_func=chi2, k=2) # 应用特征选择 X_transformed = selector.fit_transform(X, y) print("原始特征名称:", iris.feature_names) print("降维后的数据:\n", X_transformed[:5]) # 打印前 5 行数据 print("保留的特征索引:", selector.get_support(indices=True)) print("特征得分:", selector.scores_)
代码详解:
导入模块: 导入 load_iris 加载数据集,导入 SelectKBest 和 chi2 类。
加载鸢尾花数据集: load_iris() 加载鸢尾花数据集,X 是特征数据,y 是目标变量(类别标签)。
创建 SelectKBest 对象: SelectKBest(score_func=chi2, k=2) 创建一个 SelectKBest 选择器,score_func=chi2 指定使用卡方检验作为评分函数,k=2 指定选择 2 个最佳特征。
应用特征选择: selector.fit_transform(X, y) 先使用 fit 方法计算每个特征的卡方统计量,然后使用 transform 方法选择得分最高的 2 个特征,并返回降维后的数据 X_transformed。注意,fit 方法需要同时传入特征数据 X 和目标变量 y,因为卡方检验是用于分类任务的,需要目标变量信息。
打印结果: 打印原始特征名称 iris.feature_names,降维后的数据 X_transformed 的前 5 行,保留的特征索引 selector.get_support(indices=True),以及每个特征的卡方统计量得分 selector.scores_。
运行结果分析:
原始特征名称: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)'] 降维后的数据: [[1.4 0.2] [1.4 0.2] [1.3 0.2] [1.5 0.2] [1.4 0.2]] 保留的特征索引: [2 3] 特征得分: [ 10.81782088 3.71072703 116.31261309 67.04836428]
可以看到,原始的 4 个特征中,SelectKBest 使用卡方检验选择了索引为 2 和 3 的特征,即 'petal length (cm)' 和 'petal width (cm)',这两个特征的卡方统计量得分最高,表明它们与目标变量类别标签的相关性更强。
适用场景:
当你需要选择固定数量的特征,并且可以根据特征与目标变量之间的相关性进行选择时。
适用于分类任务和回归任务,需要根据任务类型选择合适的评分函数。
常用的评分函数包括:
分类任务: chi2 (卡方检验,适用于非负特征), f_classif (方差分析 F 检验), mutual_info_classif (互信息)
回归任务: f_regression (F 检验), mutual_info_regression (互信息)
参数详解:
score_func: 评分函数,用于计算每个特征的得分。
k: 选择的特征数量。可以是整数,也可以是 "all" (选择所有特征)。
3.1.3 基于模型的特征选择 (SelectFromModel)
SelectFromModel 使用一个训练好的模型来选择特征。它可以利用模型的特征重要性属性 (例如树模型的 feature_importances_) 或特征系数 (例如线性模型的 coef_) 来评估特征的重要性,并选择重要性高于设定阈值的特征。
代码示例 (使用 RandomForestClassifier 作为模型):
from sklearn.datasets import load_iris from sklearn.feature_selection import SelectFromModel from sklearn.ensemble import RandomForestClassifier # 加载鸢尾花数据集 iris = load_iris() X, y = iris.data, iris.target # 使用 RandomForestClassifier 作为模型 model = RandomForestClassifier(random_state=42) # 创建 SelectFromModel 对象,使用模型进行特征选择,阈值为 'mean' (平均特征重要性) selector = SelectFromModel(model, threshold='mean') # 应用特征选择 X_transformed = selector.fit_transform(X, y) print("原始特征名称:", iris.feature_names) print("降维后的数据:\n", X_transformed[:5]) print("保留的特征索引:", selector.get_support(indices=True)) print("特征重要性:", model.fit(X, y).feature_importances_) # 打印模型的特征重要性
代码详解:
导入模块: 导入 load_iris, SelectFromModel, RandomForestClassifier 类。
加载鸢尾花数据集: 同上。
创建 RandomForestClassifier 模型: RandomForestClassifier(random_state=42) 创建一个随机森林分类器,random_state=42 设置随机种子以保证结果可复现。
创建 SelectFromModel 对象: SelectFromModel(model, threshold='mean') 创建一个 SelectFromModel 选择器,model 参数传入我们创建的随机森林模型,threshold='mean' 指定阈值为特征重要性的平均值。这意味着特征重要性高于平均值的特征将被保留。
应用特征选择: selector.fit_transform(X, y) 先使用 fit 方法训练模型,并计算每个特征的特征重要性,然后使用 transform 方法根据阈值选择特征,并返回降维后的数据 X_transformed。
打印结果: 打印原始特征名称,降维后的数据,保留的特征索引,以及模型的特征重要性 model.fit(X, y).feature_importances_。 注意,为了获取特征重要性,我们需要先使用 model.fit(X, y) 训练模型。
运行结果分析:
原始特征名称: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)'] 降维后的数据: [[1.4 0.2] [1.4 0.2] [1.3 0.2] [1.5 0.2] [1.4 0.2]] 保留的特征索引: [2 3] 特征重要性: [0.10595931 0.02492451 0.44453523 0.42458095]
可以看到,使用随机森林模型进行特征选择后,也选择了索引为 2 和 3 的特征,即 'petal length (cm)' 和 'petal width (cm)',这与 SelectKBest 使用卡方检验的结果一致。随机森林模型的特征重要性也显示,这两个特征的重要性明显高于其他两个特征。
适用场景:
当你想利用模型的特征重要性或系数进行特征选择时。
可以使用各种线性模型 (例如 Logistic Regression, LinearSVC) 或树模型 (例如 RandomForestClassifier, GradientBoostingClassifier) 作为基础模型。
阈值的设置可以根据需求调整,例如 'mean', 'median', 或者自定义数值。
参数详解:
estimator: 用于特征选择的基础模型,需要具有 feature_importances_ 或 coef_ 属性。
threshold: 特征重要性或系数的阈值。可以是字符串 ('mean', 'median') 或数值。低于阈值的特征将被移除。
prefit: 是否假设模型已经训练好。如果为 True,则直接使用已经训练好的模型进行特征选择,否则会在 fit 方法中训练模型。
3.2 特征提取方法
特征提取通过变换原始特征空间,创建新的特征表示。Scikit-learn 提供了多种特征提取算法,下面我们将介绍几种常用的方法。
3.2.1 主成分分析 (PCA - Principal Component Analysis)
PCA 是一种无监督的线性降维方法,它通过正交变换将原始数据投影到新的坐标系中,新坐标系是按照数据方差大小排序的,称为主成分。PCA 选择方差最大的前 K 个主成分作为新的特征,从而实现降维。
代码示例:
from sklearn.datasets import load_iris from sklearn.decomposition import PCA import matplotlib.pyplot as plt # 加载鸢尾花数据集 iris = load_iris() X, y = iris.data, iris.target # 创建 PCA 对象,指定降维到 2 个主成分 pca = PCA(n_components=2) # 应用 PCA 降维 X_pca = pca.fit_transform(X) print("原始数据 shape:", X.shape) print("降维后的数据 shape:", X_pca.shape) print("解释方差比率:", pca.explained_variance_ratio_) # 可视化降维结果 (二维) plt.figure(figsize=(8, 6)) plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='viridis') plt.xlabel('Principal Component 1') plt.ylabel('Principal Component 2') plt.title('PCA降维后的鸢尾花数据集') plt.colorbar(label='类别') plt.show()
代码详解:
导入模块: 导入 load_iris, PCA, matplotlib.pyplot 类。
加载鸢尾花数据集: 同上。
创建 PCA 对象: PCA(n_components=2) 创建一个 PCA 对象,n_components=2 指定降维到 2 个主成分。
应用 PCA 降维: pca.fit_transform(X) 先使用 fit 方法计算主成分,然后使用 transform 方法将数据投影到主成分空间,并返回降维后的数据 X_pca。
打印结果: 打印原始数据和降维后数据的 shape,以及 pca.explained_variance_ratio_ 返回的每个主成分的解释方差比率。解释方差比率表示每个主成分解释了原始数据总方差的比例。
可视化降维结果: 使用 matplotlib.pyplot 绘制散点图,将降维后的数据 X_pca 的第一和第二主成分作为 x 和 y 轴,颜色表示类别标签 y。
运行结果分析:
原始数据 shape: (150, 4) 降维后的数据 shape: (150, 2) 解释方差比率: [0.92461872 0.05306648]
运行代码后,会弹出一个二维散点图,显示降维后的鸢尾花数据集在两个主成分空间上的分布情况。从输出结果可以看到,原始数据有 4 个特征,经过 PCA 降维后变为 2 个特征。explained_variance_ratio_ 显示第一个主成分解释了原始数据 92.46% 的方差,第二个主成分解释了 5.31% 的方差。两者加起来已经解释了超过 97% 的原始数据方差,说明降维效果良好,保留了大部分信息。
适用场景:
当你需要进行无监督的线性降维,并且希望保留数据中方差最大的方向时。
常用于数据可视化、噪声去除、特征压缩等场景。
尤其适用于特征之间存在线性相关性的数据集。
参数详解:
n_components: 降维后的维度,即保留的主成分数量。可以是整数,也可以是 0 到 1 之间的小数,表示保留的方差比例。例如 n_components=0.95 表示保留能够解释 95% 方差的主成分数量。
whiten: 是否进行白化处理。白化处理将主成分缩放到单位方差,并去除特征之间的相关性。
3.2.2 线性判别分析 (LDA - Linear Discriminant Analysis)
LDA 是一种有监督的线性降维方法,主要用于分类任务。它通过寻找最佳的投影方向,使得降维后的数据在同一类别内部尽可能紧凑,不同类别之间尽可能分散。与 PCA 不同,LDA 考虑了类别信息,因此更适合用于分类任务的特征提取。
代码示例:
from sklearn.datasets import load_iris from sklearn.discriminant_analysis import LinearDiscriminantAnalysis import matplotlib.pyplot as plt # 加载鸢尾花数据集 iris = load_iris() X, y = iris.data, iris.target # 创建 LDA 对象,指定降维到 2 个成分 lda = LinearDiscriminantAnalysis(n_components=2) # 应用 LDA 降维 X_lda = lda.fit_transform(X, y) print("原始数据 shape:", X.shape) print("降维后的数据 shape:", X_lda.shape) # 可视化降维结果 (二维) plt.figure(figsize=(8, 6)) plt.scatter(X_lda[:, 0], X_lda[:, 1], c=y, cmap='viridis') plt.xlabel('Linear Discriminant 1') plt.ylabel('Linear Discriminant 2') plt.title('LDA降维后的鸢尾花数据集') plt.colorbar(label='类别') plt.show()
代码详解:
导入模块: 导入 load_iris, LinearDiscriminantAnalysis, matplotlib.pyplot 类。
加载鸢尾花数据集: 同上。
创建 LDA 对象: LinearDiscriminantAnalysis(n_components=2) 创建一个 LDA 对象,n_components=2 指定降维到 2 个成分。
应用 LDA 降维: lda.fit_transform(X, y) 先使用 fit 方法计算 LDA 投影方向,需要同时传入特征数据 X 和目标变量 y,然后使用 transform 方法将数据投影到 LDA 空间,并返回降维后的数据 X_lda。
打印结果: 打印原始数据和降维后数据的 shape。
可视化降维结果: 同 PCA 的可视化代码,只是 x 和 y 轴标签改为 'Linear Discriminant 1' 和 'Linear Discriminant 2'。
运行结果分析:
原始数据 shape: (150, 4) 降维后的数据 shape: (150, 2)
运行代码后,会弹出一个二维散点图,显示 LDA 降维后的鸢尾花数据集在两个线性判别成分空间上的分布情况。从图中可以看出,不同类别的样本在 LDA 空间中被很好地分离开来,这表明 LDA 在分类任务上具有良好的降维效果。
适用场景:
主要用于分类任务的特征提取,特别是当不同类别之间的数据分布差异较大时。
可以用于数据可视化,但更侧重于展示类别可分性。
适用于特征之间存在线性关系,且类别信息对降维有重要影响的数据集。
参数详解:
n_components: 降维后的维度,即保留的线性判别成分数量。取值范围为 1 到 n_classes - 1,其中 n_classes 是类别数量。3.2.3 t-分布随机邻域嵌入 (t-SNE - t-distributed Stochastic Neighbor Embedding)
t-SNE 是一种非线性降维方法,主要用于高维数据的可视化。它通过将高维空间中样本之间的相似度映射到低维空间,并尽可能保留样本之间的局部结构,从而实现降维和可视化。 t-SNE 特别擅长揭示高维数据中的聚类结构。
代码示例 (使用 digits 数据集):
from sklearn.datasets import load_digits from sklearn.manifold import TSNE import matplotlib.pyplot as plt # 加载 digits 数据集 (手写数字数据集) digits = load_digits() X, y = digits.data, digits.target # 创建 t-SNE 对象,降维到 2 维 tsne = TSNE(n_components=2, random_state=42) # 应用 t-SNE 降维 X_tsne = tsne.fit_transform(X) print("原始数据 shape:", X.shape) print("降维后的数据 shape:", X_tsne.shape) # 可视化降维结果 (二维) plt.figure(figsize=(10, 8)) plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='Spectral') # 使用 Spectral 色彩映射 plt.xlabel('t-SNE Component 1') plt.ylabel('t-SNE Component 2') plt.title('t-SNE降维后的手写数字数据集') plt.colorbar(label='数字') plt.show()
代码详解:
导入模块: 导入 load_digits, TSNE, matplotlib.pyplot 类。
加载 digits 数据集: load_digits() 加载手写数字数据集,这是一个包含 1797 个样本,每个样本有 64 个特征 (8x8 像素图像) 的数据集,目标变量 y 是数字类别 (0-9)。
创建 TSNE 对象: TSNE(n_components=2, random_state=42) 创建一个 TSNE 对象,n_components=2 指定降维到 2 维,random_state=42 设置随机种子。
应用 t-SNE 降维: tsne.fit_transform(X) 先使用 fit 方法计算 t-SNE 嵌入,然后使用 transform 方法将数据投影到 t-SNE 空间,并返回降维后的数据 X_tsne。 t-SNE 的计算复杂度较高,尤其是在大规模数据集上,fit_transform 方法可能需要较长时间。
打印结果: 打印原始数据和降维后数据的 shape。
可视化降维结果: 使用 matplotlib.pyplot 绘制散点图,颜色使用 Spectral 色彩映射,更适合展示多类别数据的分布。
运行结果分析:
原始数据 shape: (1797, 64) 降维后的数据 shape: (1797, 2)
运行代码后,会弹出一个二维散点图,显示 t-SNE 降维后的手写数字数据集在两个 t-SNE 成分空间上的分布情况。从图中可以看出,不同数字类别的样本在 t-SNE 空间中形成了明显的聚类,即使是高维的 64 维手写数字数据,t-SNE 也能够有效地将它们投影到二维空间并展示其聚类结构。
适用场景:
主要用于高维数据的可视化,特别是当你想揭示数据中的聚类结构时。
可以用于数据探索,帮助理解高维数据的内在结构。
不适合用于下游机器学习任务的特征提取,因为 t-SNE 是一种非线性方法,且计算复杂度较高,通常只用于可视化目的。
参数详解:
n_components: 降维后的维度,通常设置为 2 或 3 用于可视化。
perplexity: 困惑度,控制局部邻域的大小,影响 t-SNE 的结果。通常取值范围在 5 到 50 之间,可以根据数据集大小和结构进行调整。
n_iter: 迭代次数,影响 t-SNE 的优化过程,增加迭代次数可以获得更稳定的结果,但也会增加计算时间。
random_state: 随机种子,用于控制随机性,保证结果可复现。
降维技术在机器学习和数据挖掘领域有着广泛的应用,以下列举一些常见的应用场景:
图像处理: 图像数据通常是高维的,例如彩色图像的像素信息。降维可以用于图像压缩、图像特征提取、图像检索等任务。例如,PCA 可以用于人脸识别中的特征脸提取。
自然语言处理 (NLP): 文本数据的词向量表示 (例如 Word2Vec, GloVe) 通常是高维的。降维可以用于文本主题建模、文本分类、情感分析等任务。例如,TruncatedSVD 可以用于文本数据的 TF-IDF 矩阵降维,从而降低文本分类模型的复杂度。
生物信息学: 基因表达数据、蛋白质序列数据等生物数据通常是高维的。降维可以用于基因选择、疾病诊断、药物发现等任务。例如,PCA 可以用于基因表达数据的降维,从而发现重要的基因组合。