知识点回顾:
1.LDA 线性判别
2.PCA 主成分分析
3.t-sne 降维
作业:
自由作业:探索下什么时候用到降维?降维的主要应用?
降维算法基础讲解
先讲解一下降维的算法有哪些,我们常说的降维一般指的是无监督降维。
无监督降维 (Unsupervised Dimensionality Reduction)
定义:这类算法在降维过程中不使用任何关于数据样本的标签信息(比如类别标签、目标值等)。它们仅仅根据数据点本身的分布、方差、相关性、局部结构等特性来寻找低维表示。输入:只有特征矩阵 。目标:保留数据中尽可能多的方差(如 PCA):旨在捕获数据的主要变化趋势,使降维后的数据仍能体现原始数据的大部分信息。保留数据的局部或全局流形结构(如 LLE, Isomap, t – SNE, UMAP):确保降维过程中数据的内在几何结构不被破坏,以便更好地理解数据的本质特征。找到能够有效重构原始数据的紧凑表示(如 Autoencoder):在减少数据维度的同时,保证可以从降维后的数据中尽可能准确地恢复出原始数据。找到统计上独立的成分(如 ICA):挖掘数据中相互独立的潜在因素,为后续分析提供更清晰的特征。典型算法:PCA/SVD:主成分分析和奇异值分解,常用于线性数据的降维处理。t – SNE:常用于数据可视化,能很好地保留数据的局部结构。UMAP:可快速有效地进行降维,兼顾局部和全局信息。LLE:通过保持局部邻域关系实现降维,适用于非线性流形数据。Isomap:基于全局几何结构进行降维。Autoencoders:利用神经网络学习数据的紧凑表示。ICA:分离出数据中的独立成分。
X
有监督降维 (Supervised Dimensionality Reduction)
定义:这类算法在降维过程中会利用数据样本的标签信息(通常是类别标签 )。它们的目标是找到一个低维子空间,在这个子空间中,不同类别的数据点能够被更好地分离开,或者说,这个低维表示更有利于后续的分类(或回归)任务。输入:特征矩阵
y 和 对应的标签向量
X。目标:最大化不同类别之间的可分性,同时最小化同一类别内部的离散度(如 LDA):使不同类别的数据在低维空间中尽可能分开,同一类别的数据尽可能聚集。找到对预测目标变量
y 最有信息量的特征组合:筛选出对预测结果最关键的特征,提高模型的预测性能。典型算法:LDA (Linear Discriminant Analysis):这是最经典的监督降维算法。它寻找的投影方向能够最大化类间散度与类内散度之比。其他算法:如 NCA (Neighbourhood Components Analysis),但 LDA 是最主要的代表。
y
降维算法运用目的
无监督降维:在不依赖标签的情况下,提炼数据的主要结构,去冗余、降噪、压缩与可视化,缓解“维度灾难”,为下游任务提供紧凑表示。有监督降维:利用标签信息,寻找能最大化类别可分性的低维子空间,保留对预测最有用的信息,提升模型性能并减少过拟合。
算法对比与选择
由于 LDA 天然是为了利用标签信息最大化分别这些低维子空间,而 PCA 是为了强调数据的最大方差,那么对于分类任务来讲,LDA 在绝大多数情况下是更加直接有效的一个选择。PCA 是一种线性降维办法,不适用非线性的数据集。
PCA(与 SVD 的关系)
思想:寻找最大方差方向,将数据投影到前 k 个主成分(正交线性组合)。与 SVD 的对应:对中心化矩阵 X_centered = U S V^T , V 的列是主成分方向,降维坐标可用 X_centered @ V_k = U_k S_k 。适用性:线性结构、去噪、缓解共线性;非线性数据用 t – SNE/UMAP 更合适。
主成分分析(PCA)原理详解 – 知乎
这是讲解 PCA 比较好的一篇文章
PCA 降维代码实现
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
import time
import numpy as np # 确保numpy导入
# 假设 X_train, X_test, y_train, y_test 已经准备好了
print(f"
--- 2. PCA 降维 + 随机森林 (不使用 Pipeline) ---")
# 步骤 1: 特征缩放
scaler_pca = StandardScaler()
X_train_scaled_pca = scaler_pca.fit_transform(X_train)
X_test_scaled_pca = scaler_pca.transform(X_test) # 使用在训练集上fit的scaler
# 步骤 2: PCA降维
# 选择降到10维,或者你可以根据解释方差来选择,例如:
pca_expl = PCA(random_state=42)
pca_expl.fit(X_train_scaled_pca)
cumsum_variance = np.cumsum(pca_expl.explained_variance_ratio_)
n_components_to_keep_95_var = np.argmax(cumsum_variance >= 0.95) + 1
print(f"为了保留95%的方差,需要的主成分数量: {n_components_to_keep_95_var}")
n_components_pca = 10
pca_manual = PCA(n_components=n_components_pca, random_state=42)
X_train_pca = pca_manual.fit_transform(X_train_scaled_pca)
X_test_pca = pca_manual.transform(X_test_scaled_pca) # 使用在训练集上fit的pca
print(f"PCA降维后,训练集形状: {X_train_pca.shape}, 测试集形状: {X_test_pca.shape}")
start_time_pca_manual = time.time()
# 步骤 3: 训练随机森林分类器
rf_model_pca = RandomForestClassifier(random_state=42)
rf_model_pca.fit(X_train_pca, y_train)
# 步骤 4: 在测试集上预测
rf_pred_pca_manual = rf_model_pca.predict(X_test_pca)
end_time_pca_manual = time.time()
print(f"手动PCA降维后,训练与预测耗时: {end_time_pca_manual - start_time_pca_manual:.4f} 秒")
print("
手动 PCA + 随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred_pca_manual))
print("手动 PCA + 随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_pca_manual))
t – SNE 的概念
t – 分布随机邻域嵌入(t – SNE)是一种用于可视化高维数据的非线性降维技术。简单来说,在现实世界中,很多数据是高维的,比如一张图片可能有上千个像素点,每个像素点就是一个维度,这么多维度的数据很难直接去理解和观察它的分布情况。而 t – SNE 就像是一个神奇的“翻译官”,它能把高维的数据“翻译”成我们更容易理解的低维数据,让我们能直观地看到数据之间的关系和分布。
t – SNE 的适用范围
t – SNE 常用于数据可视化,能将高维数据映射到二维或三维空间,帮助我们直观地观察数据的分布。想象一下,你有一堆复杂的高维数据,就像一团乱麻,很难理清头绪。而 t- SNE 就像一把神奇的剪刀,把这团乱麻剪成二维或者三维的形状,这样你就能清楚地看到数据是怎么分布的,哪些数据点靠得近,哪些离得远。比如说在图像识别中,我们可以用 t – SNE 把图像的高维特征映射到二维空间,看看不同类别的图像在这个二维空间里是如何分布的,是不是同一类别的图像会聚集在一起。
使用 t – SNE 中的参数讲解
参数包括 (降维后的维度)、
n_components(困惑度,影响局部和全局结构的平衡)、
perplexity(迭代次数)、
n_iter(初始化方式)、
init(学习率)等。
learning_rate
:这个参数就像是你要把数据“压缩”到几维空间。如果你选择
n_components,那就是把高维数据压缩到二维空间,这样你可以在平面上画出数据点;如果你选择
n_components = 2,就是压缩到三维空间,能在立体空间里展示数据。不过要注意,选择不同的维度会影响你对数据的观察和分析效果。
n_components = 3:困惑度可以理解为一个平衡局部和全局结构的“天平”。如果困惑度设置得太小,就像天平偏向了局部,模型会更关注数据点之间的局部关系,可能会忽略全局的分布;如果困惑度设置得太大,天平就偏向了全局,模型更注重全局结构,可能会让局部的细节变得模糊。通常常用的困惑度值是 30。
perplexity:迭代次数就是模型不断调整和优化的次数。就像你做一道数学题,可能第一次算出来的结果不太准确,需要多算几次,不断调整方法,直到得到一个比较准确的结果。
n_iter 设置得越大,模型有更多的机会去优化,但也会花费更多的时间。
n_iter:初始化方式就像是你盖房子时打地基的方式。不同的初始化方式会影响模型的起始状态,
init 表示使用主成分分析(PCA)来初始化,这种方式通常能让模型更稳定,就像打好了一个坚实的地基,房子更容易盖好。
init='pca':学习率可以想象成你走路的步长。如果学习率太大,就像你迈的步子太大,可能会错过一些重要的信息;如果学习率太小,就像你迈的步子太小,走得很慢,模型收敛的速度就会很慢。
learning_rate 表示让模型自动选择合适的学习率。
learning_rate='auto'
t – SNE 降维代码实现
from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
import time
import numpy as np
import matplotlib.pyplot as plt # 用于可选的可视化
import seaborn as sns # 用于可选的可视化
# 假设 X_train, X_test, y_train, y_test 已经准备好了
# 并且你的 X_train, X_test 是DataFrame或Numpy Array
print(f"
--- 3. t - SNE 降维 + 随机森林 ---")
print(" 标准 t - SNE 主要用于可视化,直接用于分类器输入可能效果不佳。")
# 步骤 1: 特征缩放
scaler_tsne = StandardScaler()
X_train_scaled_tsne = scaler_tsne.fit_transform(X_train)
X_test_scaled_tsne = scaler_tsne.transform(X_test) # 使用在训练集上fit的scaler
这里进行特征缩放是因为不同特征的取值范围可能差异很大,就像一个人的身高和体重,身高可能是1米多到2米多,体重可能是几十千克到上百千克,这样差异大的数据会影响模型的效果。通过特征缩放,把所有特征都缩放到一个相似的范围,就像把不同大小的苹果都切成差不多大小,这样模型处理起来会更方便。
# 步骤 2: t - SNE 降维
# 我们将降维到与PCA相同的维度(例如10维)或者一个适合分类的较低维度。
# t - SNE通常用于2D/3D可视化,但也可以降到更高维度。
# 然而,降到与PCA一样的维度(比如10维)对于t - SNE来说可能不是其优势所在,
# 并且计算成本会显著增加,因为高维t - SNE的优化更困难。
# 为了与PCA的 n_components = 10 对比,我们这里也尝试降到10维。
# 但请注意,这可能非常耗时,且效果不一定好。
# 通常如果用t - SNE做分类的预处理(不常见),可能会选择非常低的维度(如2或3)。
# n_components_tsne = 10 # 与PCA的例子保持一致,但计算量会很大
n_components_tsne = 2 # 更典型的t - SNE用于分类的维度,如果想快速看到结果
# 如果你想严格对比PCA的10维,可以将这里改为10,但会很慢
# 对训练集进行 fit_transform
tsne_model_train = TSNE(n_components=n_components_tsne,
perplexity=30, # 常用的困惑度值
n_iter=1000, # 足够的迭代次数
init='pca', # 使用PCA初始化,通常更稳定
learning_rate='auto', # 自动学习率 (sklearn >= 1.2)
random_state=42, # 保证结果可复现
n_jobs=-1) # 使用所有CPU核心
print("正在对训练集进行 t - SNE fit_transform...")
start_tsne_fit_train = time.time()
X_train_tsne = tsne_model_train.fit_transform(X_train_scaled_tsne)
end_tsne_fit_train = time.time()
print(f"训练集 t - SNE fit_transform 完成,耗时: {end_tsne_fit_train - start_tsne_fit_train:.2f} 秒")
# 对测试集进行 fit_transform
# 再次强调:这是独立于训练集的变换
tsne_model_test = TSNE(n_components=n_components_tsne,
perplexity=30,
n_iter=1000,
init='pca',
learning_rate='auto',
random_state=42, # 保持参数一致,但数据不同,结果也不同
n_jobs=-1)
print("正在对测试集进行 t - SNE fit_transform...")
start_tsne_fit_test = time.time()
X_test_tsne = tsne_model_test.fit_transform(X_test_scaled_tsne) # 注意这里是 X_test_scaled_tsne
end_tsne_fit_test = time.time()
print(f"测试集 t - SNE fit_transform 完成,耗时: {end_tsne_fit_test - start_tsne_fit_test:.2f} 秒")
print(f"t - SNE降维后,训练集形状: {X_train_tsne.shape}, 测试集形状: {X_test_tsne.shape}")
start_time_tsne_rf = time.time()
# 步骤 3: 训练随机森林分类器
rf_model_tsne = RandomForestClassifier(random_state=42)
rf_model_tsne.fit(X_train_tsne, y_train)
# 步骤 4: 在测试集上预测
rf_pred_tsne_manual = rf_model_tsne.predict(X_test_tsne)
end_time_tsne_rf = time.time()
print(f"t - SNE降维数据上,随机森林训练与预测耗时: {end_time_tsne_rf - start_time_tsne_rf:.4f} 秒")
total_tsne_time = (end_tsne_fit_train - start_tsne_fit_train) +
(end_tsne_fit_test - start_tsne_fit_test) +
(end_time_tsne_rf - start_time_tsne_rf)
print(f"t - SNE 总耗时 (包括两次fit_transform和RF): {total_tsne_time:.2f} 秒")
print("
手动 t - SNE + 随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred_tsne_manual))
print("手动 t - SNE + 随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_tsne_manual))
LDA 降维相关
LDA 的核心定义与目标
线性判别分析(LDA)是一种有监督的降维算法,目标是找到一个低维子空间,使不同类别的数据在该子空间中尽可能分开,同一类别的数据尽可能聚集。比如说有一群动物,有猫、狗、鸟,每个动物都有很多特征,像身高、体重、毛色等,这些特征构成了高维数据。LDA 就像一个聪明的管理员,它能找到一个合适的“小房间”(低维子空间),把猫都放在一起,狗都放在一起,鸟也都放在一起,让不同种类的动物之间离得远远的,同一类的动物都紧紧靠在一起。
LDA 的工作原理描述
通过最大化类间散度与类内散度之比,寻找最佳的投影方向,将数据投影到低维空间。类间散度可以理解为不同类别之间的距离,类内散度就是同一类别内部数据点之间的距离。LDA 就像是一个“裁判”,它要让不同类别之间的距离尽可能大,同一类别内部的距离尽可能小。它通过不断地调整投影方向,就像调整一个投影仪的角度,找到一个最佳的角度,把高维的数据投影到低维空间,让不同类别的数据分得更开,同一类别的数据聚得更紧。
LDA 的关键特性,关键参数讲解
关键特性包括利用标签信息进行降维,适用于分类任务。因为 LDA 知道每个数据点属于哪个类别(标签信息),所以它能根据这个信息来更好地进行降维,让不同类别的数据分开。这就好比你在整理书架,你知道每本书属于哪个类别,比如小说、传记、科普等,你就可以根据这个类别信息把书分类摆放,让不同类别的书之间有明显的区分。
关键参数有 (降维后的维度)、
n_components(求解方法)等。
solver
:和 t – SNE 里的
n_components 类似,它决定了把数据降到几维。你可以根据实际情况来选择合适的维度,比如你希望把数据降到二维,就可以把
n_components 设置为 2。
n_components:求解方法就像是你解决一道数学题的不同方法。不同的求解方法在计算速度、精度等方面可能会有差异,你可以根据数据的特点和自己的需求来选择合适的求解方法。
solver
LDA 降维代码实现
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
import time
import numpy as np
# 假设你已经导入了 matplotlib 和 seaborn 用于绘图 (如果需要)
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # 如果需要3D绘图
import seaborn as sns
print(f"
--- 4. LDA 降维 + 随机森林 ---")
# 步骤 1: 特征缩放
scaler_lda = StandardScaler()
X_train_scaled_lda = scaler_lda.fit_transform(X_train)
X_test_scaled_lda = scaler_lda.transform(X_test) # 使用在训练集上fit的scaler
# 步骤 2: LDA 降维
n_features = X_train_scaled_lda.shape[1]
if hasattr(y_train, 'nunique'):
n_classes = y_train.nunique()
elif isinstance(y_train, np.ndarray):
n_classes = len(np.unique(y_train))
else:
n_classes = len(set(y_train))
max_lda_components = min(n_features, n_classes - 1)
# 设置目标降维维度
n_components_lda_target = 10
if max_lda_components < 1:
print(f"LDA 不适用,因为类别数 ({n_classes})太少,无法产生至少1个判别组件。")
X_train_lda = X_train_scaled_lda.copy() # 使用缩放后的原始特征
X_test_lda = X_test_scaled_lda.copy() # 使用缩放后的原始特征
actual_n_components_lda = n_features
print("将使用缩放后的原始特征进行后续操作。")
else:
# 实际使用的组件数不能超过LDA的上限,也不能超过我们的目标(如果目标更小)
actual_n_components_lda = min(n_components_lda_target, max_lda_components)
if actual_n_components_lda < 1: # 这种情况理论上不会发生,因为上面已经检查了 max_lda_components < 1
print(f"计算得到的实际LDA组件数 ({actual_n_components_lda}) 小于1,LDA不适用。")
X_train_lda = X_train_scaled_lda.copy()
X_test_lda = X_test_scaled_lda.copy()
actual_n_components_lda = n_features
print("将使用缩放后的原始特征进行后续操作。")
else:
print(f"原始特征数: {n_features}, 类别数: {n_classes}")
print(f"LDA 最多可降至 {max_lda_components} 维。")
print(f"目标降维维度: {n_components_lda_target} 维。")
print(f"本次 LDA 将实际降至 {actual_n_components_lda} 维。")
lda_manual = LinearDiscriminantAnalysis(n_components=actual_n_components_lda, solver='svd')
X_train_lda = lda_manual.fit_transform(X_train_scaled_lda, y_train)
X_test_lda = lda_manual.transform(X_test_scaled_lda)
print(f"LDA降维后,训练集形状: {X_train_lda.shape}, 测试集形状: {X_test_lda.shape}")
start_time_lda_rf = time.time()
# 步骤 3: 训练随机森林分类器
rf_model_lda = RandomForestClassifier(random_state=42)
rf_model_lda.fit(X_train_lda, y_train)
# 步骤 4: 在测试集上预测
rf_pred_lda_manual = rf_model_lda.predict(X_test_lda)
end_time_lda_rf = time.time()
print(f"LDA降维数据上,随机森林训练与预测耗时: {end_time_lda_rf - start_time_lda_rf:.4f} 秒")
print("
手动 LDA + 随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred_lda_manual))
print("手动 LDA + 随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred_lda_manual))
关于降维
降维,可理解为“去粗取精、化繁为简”,即在保留主要信息的前提下,将复杂的高维数据压缩为紧凑的低维表示。
降维的使用场景
高维度数据导致数据难以理解和分析。高维空间的稀疏数据致使模型过拟合。特征中混杂大量无关干扰和冗余信息,即噪声过多。
典型应对方式
降维与紧凑表示PCA/SVD(线性)TruncatedSVD(稀疏矩阵友好)UMAP/t – SNE(可视化与非线性结构)LDA(监督场景)稀疏存储与算子:采用 CSR/COO 稀疏矩阵格式,利用稀疏内核提升效率。特征工程用嵌入替代独热特征哈希利用 L1 正则促进稀疏性过滤低频/低信息特征矩阵分解:在推荐系统中,运用 SVD/因子分解学习潜在语义,缓解极度稀疏的交互矩阵。




