ML:主成分分析(PCA)的基本原理与实现
在机器学习中,并不是所有任务都直接以“预测标签”或“预测数值”为目标。有时,我们面对的数据本身就具有较高维度:特征很多、变量之间相关性较强、可视化困难、计算开销偏大。这时,一个自然的问题就会出现:能否在尽量保留主要信息的前提下,用更少的维度表示原始数据?
主成分分析(Principal Component Analysis,PCA)正是解决这一问题的经典方法。它属于无监督学习(Unsupervised Learning)中的降维方法:训练数据中不需要标签,算法也不直接学习输入与输出之间的映射关系,而是从数据自身的分布结构中寻找更紧凑的表示方式。
PCA 的设计出发点可以概括为一句话:如果高维数据中的主要变化集中在少数几个方向上,就可以用这些方向重新表示数据,从而达到降维、压缩和可视化的目的。
一、主成分分析的基本思想
PCA 的核心思想是:在原始特征空间中寻找若干个新的坐标轴,使得数据在这些方向上的变化尽可能大,并用这些新坐标轴重新表示数据。
这里所谓“变化尽可能大”,更准确地说,就是数据投影到新坐标轴之后,投影结果的方差尽可能大。方差越大,说明数据在该方向上的差异越明显;在 PCA 的假设中,这类方向往往包含更多主要结构信息。
图 1:PCA 的基本思路
如果把数据看作分布在特征空间中的一团点云,那么 PCA 会尝试找到:
• 第一条最重要的方向,使数据投影到这条线上之后方差最大
• 第二条方向与第一条正交,并在剩余变化中继续解释尽可能多的方差
• 第三条方向再与前两条正交,并解释下一部分方差
• 依此类推,得到一组按重要程度排序的新方向
因此,PCA 不是简单删除某些原始特征,而是重新构造一组新的综合变量。这些新变量称为主成分(Principal Components),它们是原始特征的线性组合,并且彼此正交。
从机器学习流程看,PCA 完成的是这样一件事:
• 输入:一个高维特征矩阵
• 输出:一个低维表示
• 目标:在压缩维度的同时,尽量保留数据中的主要变化信息
图 2:PCA 在机器学习流程中的位置
需要注意的是,PCA 是无监督方法。它只根据特征矩阵本身寻找主要变化方向,不需要类别标签,也不直接关心某个方向是否有利于分类或回归。因此,PCA 更适合用于结构分析、数据压缩、降维可视化和下游模型预处理。
二、主成分分析的数学表达
1、数据矩阵与中心化
设原始数据矩阵为:
其中:
• X 表示原始数据矩阵
• n 表示样本数
• p 表示特征数
• xᵢⱼ 表示第 i 个样本在第 j 个特征上的取值
每一行表示一个样本,每一列表示一个特征。
PCA 在计算前通常会先对每个特征做中心化,也就是减去该特征的均值。设按列求得的均值向量为:
其中:
• x̄ 表示按列计算得到的均值向量
• x̄₁ 表示第 1 个特征的均值
• x̄₂ 表示第 2 个特征的均值
• x̄ₚ 表示第 p 个特征的均值
中心化后的数据可记为:
其中:
• Xᶜ 表示中心化后的数据矩阵
• X 表示原始数据矩阵
• x̄ 表示每个特征的均值向量
中心化后的每一列均值为 0。
中心化的意义在于:PCA 关注的是数据围绕均值的变化方向,而不是数据整体离原点有多远。
2、主成分方向
PCA 要寻找的是一组新的方向向量。设第一个主成分方向为:
其中:
• w₁ 表示第一主成分方向
• w₁₁、w₂₁、…、wₚ₁ 表示该方向在各个原始特征上的权重
• w₁ 是单位向量
• p 表示原始特征数
所有样本投影到这一方向上的坐标可写为:
其中:
• z₁ 表示所有样本在第一主成分方向上的投影结果
• Xᶜ 表示中心化后的数据矩阵
• w₁ 表示第一主成分方向
PCA 的目标是让 z₁ 的方差尽可能大。因此,第一个主成分可以理解为使投影后方差最大的方向。
第二个主成分方向 w₂ 要求与 w₁ 正交,并在这一约束下使投影方差继续尽可能大:
其中:
• w₁ 表示第一主成分方向
• w₂ 表示第二主成分方向
• w₁ᵀw₂ = 0 表示两个方向正交
正交意味着两个主成分方向彼此不重复表达同一部分变化。
依此类推,就得到一组彼此正交、按解释方差从大到小排序的主成分方向。
在 Scikit-learn 中,components_ 表示特征空间中的主轴,并按解释方差从大到小排列。
3、低维投影
图 3:PCA 投影过程示意:从二维到一维
如果只保留前 k 个主成分,并把这些方向组成矩阵:
其中:
• Wₖ 表示前 k 个主成分方向组成的投影矩阵
• w₁ 表示第一主成分方向
• w₂ 表示第二主成分方向
• wₖ 表示第 k 个主成分方向
• k 表示希望保留的主成分个数
那么原始数据在低维空间中的表示可写为:
其中:
• Z 表示降维后的新表示
• Xᶜ 表示中心化后的数据矩阵
• Wₖ 表示前 k 个主成分方向组成的投影矩阵
• Z 的每一列对应一个主成分
• k < p,表示降维后的维度小于原始特征数
这就是 PCA 最核心的结果:用更少的主成分近似表示原始数据。
4、一个简单的运算示例
为了更直观地理解 PCA 的“投影”和“方差最大”思想,可以考虑一个非常简单的二维数据集。假设有 3 个样本,且数据已经中心化:
其中:
• Xᶜ 表示中心化后的数据矩阵
• 该数据集有 3 个样本、2 个特征
• 三个样本大致分布在同一条斜线上
观察数据可知,主要变化方向大致沿着向量:
将其单位化,得到第一主成分方向:
其中:
• w₁ 表示第一主成分方向
• √5 是向量 [2, 1] 的长度
单位化后,w₁ 的长度为 1,w₁ 仍然表示原数据中变化最明显的方向。
把样本投影到该方向上:
其中:
• z₁ 表示样本在 w₁ 方向上的一维投影坐标
• Xᶜ 表示中心化后的二维数据
• w₁ 表示第一主成分方向
逐行计算:
第 1 个样本:
第 2 个样本:
第 3 个样本:
因此,投影结果为:
其中:
• z₁ 表示三个样本在第一主成分方向上的投影结果
• 第 1 个样本投影值为 √5
• 第 2 个样本投影值为 0
• 第 3 个样本投影值为 -√5
二维数据已经被压缩为一维表示。
由于数据已经中心化,投影结果的均值也是 0。按样本方差计算:
其中:
• Var(z₁) 表示 z₁ 的样本方差
• 分子表示三个投影值的平方和
• 分母为 3 - 1,因为这里计算的是样本方差
• 结果为 5,说明样本在 w₁ 方向上的变化较明显
再看与 w₁ 正交的方向:
其中:
• w₂ 表示第二个方向
• w₂ 与 w₁ 正交
• w₂ 用来观察数据在另一条垂直方向上的变化
投影结果为:
其中:
• Xᶜw₂ 表示样本在 w₂ 方向上的投影结果
• 三个投影值都为 0
说明数据在 w₂ 方向上几乎没有变化,因此,w₂ 方向的方差为 0。
这个例子说明:数据几乎全部沿着 w₁ 方向变化,而在与之正交的 w₂ 方向上几乎没有变化。因此,如果只保留第一主成分,就可以把二维数据压缩为一维,同时保留主要变化信息。
三、PCA 为什么以“方差最大”为目标
图 4:PCA 为什么寻找方差最大的方向
1、方差大的方向包含更多变化信息
在 PCA 中,一个基本假设是:如果某个方向上的方差很大,说明数据在这个方向上有更明显的差异,这个方向更可能包含主要结构信息。
反过来,如果某个方向上的方差很小,说明样本在该方向上变化不明显,它可能只包含较弱信息,甚至主要是噪声。
因此,PCA 会优先保留方差较大的方向。
2、降维本质上是有损压缩
降维并不意味着“完全不损失信息”。它真正做的是:在允许一定信息损失的前提下,尽量保留最主要的变化结构。
很多高维数据虽然表面上维度很高,但主要变化往往集中在少数几个方向上。此时,前几个主成分就可能已经保留了数据中的大部分结构信息。
3、方差最大与重构误差最小
PCA 也可以从另一个角度理解:当我们用较低维度近似表示原始数据时,PCA 寻找的是一种线性子空间,使数据投影到该子空间后,重构误差尽可能小。
也就是说,在标准 PCA 中:
保留最大方差方向
与:
在线性子空间中最小化重构误差
是同一问题的两个视角。
前者更容易从“信息保留”角度理解,后者更容易从“数据压缩”角度理解。
4、解释方差比
训练完成后,解释方差比 explained_variance_ratio_ 表示每个选中主成分所解释的方差比例。如果保留了全部主成分,这些比例之和为 1.0。
这意味着:
• 第一主成分解释最多方差
• 第二主成分解释剩余方向中的次多方差
• 若前几个主成分的解释方差比总和已经很高,就说明这些主成分已经保留了数据的大部分主要变化
图 5:PCA 的解释方差比与累计解释方差比
在实际使用中,常会根据累计解释方差比选择主成分个数。例如,希望累计解释方差比达到 90% 或 95%,就可以据此确定需要保留多少个主成分。
四、PCA 在 Scikit-learn 中的实现方式
在 Scikit-learn 中,PCA 是一种线性降维方法。它基于奇异值分解(Singular Value Decomposition,SVD)把数据投影到更低维空间。输入数据在计算前会按特征中心化,但默认不会自动标准化。
这点非常重要:中心化只是减去均值,标准化还会调整尺度。如果不同特征的量纲或数值范围差异很大,通常应先使用 StandardScaler 再使用 PCA。
1、PCA 是一个 transformer
在 Scikit-learn 中,PCA 被实现为一个 transformer。它在 fit 中学习主成分方向,在 transform 中把数据投影到这些主成分上。
典型使用流程是:
• 创建 PCA 对象
• 调用 fit(X) 学习主成分
• 调用 transform(X) 把数据投影到低维空间
• 或直接使用 fit_transform(X) 一步完成学习与转换
2、常见参数
PCA 的常见参数包括:
• n_components:要保留的主成分个数。
若未设置,则保留 min(n_samples, n_features) 个成分。它也可以是 0 到 1 之间的浮点数,表示希望保留的累计解释方差比例;也可以设为 'mle',由算法估计合适维度。
• whiten:是否进行白化处理。
若设为 True,会把投影后的主成分缩放到单位方差。它有时有利于下游模型,但会丢失主成分之间原有的相对方差尺度信息。
• svd_solver:
指定 SVD 求解策略。通常情况下使用默认值即可,只有在大规模数据或特殊性能需求下才需要重点调整。
其中,n_components 是最关键的参数,因为它直接决定降维后保留多少个维度。
3、常见属性
训练完成后,常见属性包括:
• components_:主成分方向
• explained_variance_:每个主成分解释的方差
• explained_variance_ratio_:每个主成分解释的方差比例
• singular_values_:对应奇异值
• mean_:每个特征的经验均值
这些属性共同帮助我们理解 PCA 学到了什么,以及降维后保留了多少主要信息。
五、模型结果如何解释
1、主成分的含义
每一个主成分,本质上都是原始特征的一个线性组合。
图 6:主成分是原始特征的线性组合
可以写成:
其中:
• PC₁ 表示第一主成分
• x₁、x₂、…、xₚ 表示原始特征
• a₁、a₂、…、aₚ 表示对应权重
• PC₁ 是多个原始特征按不同权重组合得到的新变量
如果某个主成分方向在若干原始特征上的权重较大,就说明这个主成分主要反映了这些特征的共同变化模式。
因此,PCA 不是“重新命名特征”,而是在构造新的综合变量。
六、Python 实现:用鸢尾花数据集做 PCA 降维
下面用鸢尾花数据集演示 PCA 的基本使用方式。
import matplotlib.pyplot as plt # 绘图库from sklearn.datasets import load_iris # 加载鸢尾花数据集from sklearn.decomposition import PCA # 主成分分析降维 # 根据操作系统选择中文字体(二选一,取消注释对应行)plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] # Windows# plt.rcParams["font.sans-serif"] = ["Songti SC"] # macOSplt.rcParams["axes.unicode_minus"] = False # 解决负号显示问题 # 1. 加载数据iris = load_iris()X = iris.data # 特征 (150,4)y = iris.target # 标签 (150,) # 2. 创建 PCA 模型,降到 2 维pca = PCA(n_components=2) # 保留前两个主成分 # 3. 拟合并转换X_pca = pca.fit_transform(X) # 训练 PCA 并降维 # 4. 查看解释方差比print("各主成分解释方差比:", pca.explained_variance_ratio_) # 每个主成分保留的信息占比print("累计解释方差比:", pca.explained_variance_ratio_.sum()) # 总信息保留比例 # 5. 可视化plt.figure(figsize=(8, 5)) # 按类别分别绘制散点图for class_id, class_name in enumerate(iris.target_names): plt.scatter( X_pca[y == class_id, 0], # 第一主成分坐标 X_pca[y == class_id, 1], # 第二主成分坐标 label=class_name ) plt.xlabel("第一主成分")plt.ylabel("第二主成分")plt.title("PCA 降维后的鸢尾花数据")plt.legend()plt.show() # 显示图形输出示意图:
这段代码展示了 PCA 的基本工作流:
1、构造或加载数据;
2、创建 PCA(n_components=2) 模型;
3、使用 fit_transform() 学习主成分并完成降维;
4、查看解释方差比;
5、用二维散点图观察降维后的分布。
在这里,X_pca 不再是原来的 4 维特征,而是投影到前两个主成分上的二维表示。
需要注意的是,鸢尾花数据的各特征单位相同,数值范围差异也不算特别大,所以这个示例可以直接演示 PCA 的基本流程。但在更一般的任务中,如果不同特征量纲差异明显,通常应先进行标准化。
七、Python 实现:在 Pipeline 中把 PCA 与分类模型结合
在实际应用流程中,PCA 往往不是单独使用,而是作为预处理步骤与分类器或回归器配合使用。下面使用手写数字数据集演示一个典型流程。
from sklearn.datasets import load_digits # 加载手写数字数据集from sklearn.decomposition import PCA # 主成分分析降维from sklearn.linear_model import LogisticRegression # 逻辑回归分类器from sklearn.model_selection import train_test_split # 数据集划分from sklearn.pipeline import make_pipeline # 创建管道from sklearn.preprocessing import StandardScaler # 标准化 # 1. 加载数据digits = load_digits()X = digits.data # 特征 (1797, 64)y = digits.target # 标签 (1797,) # 2. 划分训练集与测试集(测试集20%,分层采样)X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y) # 3. 构造管道:标准化 → PCA降维到20维 → 逻辑回归pipe = make_pipeline( StandardScaler(), # 标准化(均值为0,方差为1) PCA(n_components=20), # 降维到20个主成分 LogisticRegression(max_iter=2000) # 逻辑回归,增大迭代次数保证收敛) # 4. 训练与评估pipe.fit(X_train, y_train) # 训练模型score = pipe.score(X_test, y_test) # 测试集准确率 print("测试集得分:", score)这个示例说明,PCA 可以先把原始高维数据压缩到更低维空间,再交给后续分类器处理。
这种写法有三个好处:
• 使用 StandardScaler() 先统一特征尺度,避免 PCA 被大尺度特征主导
• 使用 PCA(n_components=20) 压缩特征维度,减少冗余信息
• 使用 Pipeline 把预处理、降维和建模串成一个完整流程,避免训练集与测试集之间发生数据泄漏
需要注意的是,PCA 不一定总能提升模型预测效果。它主要优化的是无监督的方差保留目标,而不是分类准确率或回归误差。因此,在监督学习任务中,PCA 是否有利于最终模型,需要通过验证集或交叉验证来判断。
八、PCA 的适用场景与主要局限
图 7:PCA 的适用场景与局限
1、适用场景
PCA 较适合以下情况:
• 特征维度较高
• 特征之间相关性较强
• 希望压缩维度、减少冗余
• 希望把高维数据可视化到 2 维或 3 维
• 希望作为后续模型的预处理步骤
• 希望在不使用标签的情况下分析数据主要结构
它尤其适合那些“主要变化方向明显、线性结构较强”的数据。
2、主要局限
PCA 虽然经典,但并不是万能方法。
(1)PCA 是线性的
普通 PCA 只能寻找线性方向。如果数据的主要结构明显是非线性的,PCA 可能无法很好表示;此时可以考虑 KernelPCA、t-SNE、UMAP 等方法。
(2)解释性有限
主成分是原始特征的组合,往往不如原始变量直观。尤其当特征数量较多时,每个主成分的实际含义可能较难解释。
(3)默认不做标准化
PCA 对特征尺度敏感。如果各特征量纲差异很大,结果可能被大尺度特征主导。因此,在多数实际任务中,通常需要先做标准化。
(4)并不直接考虑监督目标
PCA 是无监督方法,只关心方差大小,不直接关心类别区分或预测效果。某些方差较大的方向未必对分类最有用,而某些方差较小的方向反而可能包含重要判别信息。
(5)标准 PCA 是批处理方法
标准 PCA 通常需要整体访问数据。当数据规模很大时,计算和内存开销可能较高。此时可以考虑 IncrementalPCA、随机 SVD 或其他适合大规模数据的降维方法。
📘 小结
PCA 通过寻找方差最大的正交方向,把高维数据压缩为更低维表示。它不依赖标签,适合用于降维、可视化、去冗余和预处理。理解 PCA,有助于把握无监督学习中的结构发现、线性降维与数据表示变换。
“点赞有美意,赞赏是鼓励”
