定义:PCA 是无监督降维算法的核心,核心目标是将高维、可能存在线性相关的特征,通过线性变换映射到低维空间,得到互不相关的主成分,同时尽可能保留原始数据的信息,如:方差。总而言之,PCA 就是在高维数据中找几个 “最优的坐标轴”,让数据在这些坐标轴上的分布最分散也就是方差最大,用这几个坐标轴代表原始高维数据,实现降维且信息损失最小。

一、核心概念

1. 主成分(PC)

原始高维特征经线性变换得到的新的正交特征且新特征之间互不相关,按保留信息多少排序:

  • 第一主成分(PC1):数据方差最大的方向,保留原始信息最多;
  • 第二主成分(PC2):与 PC1 正交垂直,且在剩余方向中方差最大;
  • 第 k 主成分(PCk):与前 k-1 个主成分正交,方差次大;主成分的数量远小于原始特征数,实现降维。

2. 方差与协方差

  • 方差:衡量单个特征的离散程度,方差越大,特征包含的信息越多。
  • 协方差:衡量两个特征之间的线性相关程度。例如:由X、Y两个特征,当线性相关程度大于0是X 和 Y 正相关,小于0是X 和 Y 负相关,等于0是X 和 Y 线性不相关。

3. 协方差矩阵

        将数据的所有特征的方差和协方差按矩阵形式排列,是 PCA 的核心计算对象。

        假设数据有m个特征,n个样本,标准化后的数据矩阵为X_{n\times m},每行 1 个样本,每列 1 个特征,则协方差矩阵X_{m\times m}​的元素满足:

  • 对角线元素:C_{ii} = Var,第i个特征;
  • 非对角线元素:C_{ij} = Cov,第i个特征,第j个特征;PCA 的核心是对协方差矩阵做特征值分解。

4. 特征值与特征向量

对协方差矩阵C做特征值分解:C= P\Lambda P^{T},其中:

  • Λ:对角矩阵,对角线元素为特征值,按从大到小排序λ1​≥λ2​≥...≥λm​;
  • P:正交矩阵,每一列是对应特征值的特征向量,P=[p1​,p2​,...,pm​];
    • 特征向量pi​:代表第 i 个主成分的方向,最优坐标轴;
    • 特征值λi​:代表第 i 个主成分的方差,也是该方向上的信息含量。

5. 方差贡献率与累计方差贡献率

  • 单个主成分方差贡献率\frac{\lambda _{i}}{\sum_{j=1}^{m}\lambda _{j}},表示第 i 个主成分保留的信息占总信息的比例;
  • 累计方差贡献率\frac{\sum_{j=1}^{k}\lambda _{i}}{\sum_{j=1}^{m}\lambda _{j}},表示前 k 个主成分保留的总信息比例;
  • 实际降维时,通常选择累计方差贡献率≥85%或90%的 k 个主成分,确定降维后的维度 k。、

6. 去中心化 

        PCA 对量纲敏感(比如特征 1 是身高:cm,特征 2 是体重:kg,量纲不同会导致方差被主导),因此第一步必须对数据做去中心化,所有特征减均值,若特征量纲差异大,还需做标准化,去中心化后除以标准差,即 Z-score 标准化。

(注:写的比较粗糙,如果有一些的概念不清楚可以留言。)

二、数学概念

        设原始数据:n个样本,m个特征,原始数据矩阵Xn×m​,每行 1 个样本,每列 1 个特征。其中x_{ij}表示第 i 个样本的第 j 个特征值。

步骤 1:数据标准化

去中心化:每个特征减去其均值,\bar{x}_{j}=\frac{1}{n}\sum_{i=1}^{n}x_{ij},去中心化后矩阵X′:

x_{ij}^{'}=x_{ij}-\bar{x}_{j}

标准化:当量纲差异大时做,去中心化后除以标准差,\sigma _{j}=\sqrt{\frac{1}{n-1}\sum_{i=1}^{n}\left (x _{ij}-\bar{x}_{j} \right )^{2}}

标准化后矩阵:

X_{std_{ij}}=\frac{X_{ij}^{'}}{\sigma _{j}}

后续所有计算基于此,若无特殊说明,下文X均指标准化后的数据矩阵。

步骤 2:计算协方差矩阵

样本协方差矩阵C_{m\times m}​的计算公式:

C=\frac{1}{n-1}X^{T}X

步骤 3:对协方差矩阵做特征值分解

对协方差矩阵C进行特征值分解,得到特征值和特征向量:

C\cdot p_{i}=\lambda _{i}\cdot p_{i}

        将所有特征值按从大到小排序:λ1​≥λ2​≥...≥λm​,对应的特征向量为p1​,p2​,...,pm​,组成特征向量矩阵。

步骤 4:选择 k 个主成分,构建投影矩阵

        根据累计方差贡献率选择前 k 个特征值对应的特征向量,构建投影矩阵。

步骤 5:数据投影,得到降维结果

        将标准化后的高维数据Xn×m​投影到投影矩阵Pk​上,得到降维后的低维数据。

        ​Y即为 PCA 降维后的结果,每行 1 个样本,每列 1 个主成分,共 k 个主成分,实现了从 m 维到 k 维的降维。

(注:PCA 选择方差最大的方向作为主成分,本质是最小化数据投影后的重构误差。

重构误差:原始数据通过主成分还原后的误差,方差越大的方向,重构误差越小,信息保留越完整。)

三、代码解释

模块一:导入库

# 1. 导入所需库
import numpy as np  # 数值计算核心
import pandas as pd  # 数据读取
import matplotlib.pyplot as plt  # 可视化降维结果
from sklearn.datasets import load_iris  # 加载鸢尾花数据集
from sklearn.preprocessing import StandardScaler  # 标准化,也可手动实现,这里用库更高效

模块二:加载并预处理数据

# 2. 加载并预处理数据
# 加载鸢尾花数据集,返回的是字典对象
iris = load_iris()
X = iris.data  # 特征矩阵:(150,4),150个样本,4个特征
y = iris.target  # 标签:(150,),3类鸢尾花,用于可视化时上色
feature_names = iris.feature_names  # 特征名,方便理解
target_names = iris.target_names  # 标签名

模块三:数据标准化

# StandardScaler会自动计算每个特征的均值和标准差,fit_transform一步完成标准化
scaler = StandardScaler()
X_std = scaler.fit_transform(X)  # 标准化后矩阵:(150,4),均值=0,标准差=1
# 手动标准化公式参考:X_std = (X - X.mean(axis=0)) / X.std(axis=0, ddof=1)  # ddof=1表示样本标准差(除以n-1)

模块四:计算协方差矩阵

n = X_std.shape[0]  # 样本数:150
cov_matrix = np.cov(X_std.T)  # 计算协方差矩阵:np.cov默认按行计算,故转置X_std.T,结果(4,4)
# 手动计算协方差矩阵:cov_matrix_manual = (X_std.T @ X_std) / (n - 1)  # 与np.cov结果一致,验证公式
# 对协方差矩阵做特征值分解,得到特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)  # 特征值分解
# eigenvalues:(4,),特征值数组;eigenvectors:(4,4),每一列是对应特征值的特征向量
# 注意:np.linalg.eig返回的特征值是无序的,需要手动按从大到小排序
# 将特征值和特征向量按特征值从大到小排序
# 得到特征值的索引,按降序排列
sorted_idx = np.argsort(eigenvalues)[::-1]
# 按索引排序特征值和特征向量
sorted_eigenvalues = eigenvalues[sorted_idx]  # 排序后特征值:λ1≥λ2≥λ3≥λ4
sorted_eigenvectors = eigenvectors[:, sorted_idx]  # 排序后特征向量:每列对应一个主成分方向

模块五:计算方差贡献率和累计方差贡献率,确定降维维度k和画图

# 单个方差贡献率
explained_variance_ratio = sorted_eigenvalues / np.sum(sorted_eigenvalues)
# 累计方差贡献率
cumulative_explained_variance = np.cumsum(explained_variance_ratio)
# 打印结果,方便分析
print("特征值(从大到小):", np.round(sorted_eigenvalues, 4))
print("单个主成分方差贡献率:", np.round(explained_variance_ratio, 4))
print("累计方差贡献率:", np.round(cumulative_explained_variance, 4))

# 选择前k个主成分,构建投影矩阵(这里选择k=2,累计贡献率≈97.76%,保留大部分信息)
k = 2
projection_matrix = sorted_eigenvectors[:, :k]  # 投影矩阵:(4,2),取前2个特征向量
print("投影矩阵(前2个主成分的方向):\n", np.round(projection_matrix, 4))

# 数据投影,得到降维后的结果
X_pca = X_std @ projection_matrix  # 核心公式:Y = X · Pk,结果(150,2),150个样本,2个主成分

# 可视化降维结果和累计方差贡献率
plt.rcParams['font.sans-serif'] = ['SimHei']  # 解决中文显示问题
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 子图1:累计方差贡献率曲线(碎石图)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# 绘制累计方差贡献率
ax1.plot(range(1, len(cumulative_explained_variance)+1), cumulative_explained_variance, 'o-', color='red')
ax1.axhline(y=0.9, color='gray', linestyle='--', label='累计贡献率90%')  # 90%阈值线
ax1.set_xlabel('主成分个数')
ax1.set_ylabel('累计方差贡献率')
ax1.set_title('PCA累计方差贡献率曲线')
ax1.legend()
ax1.grid(True, alpha=0.3)

# 子图2:PCA降维后的数据分布
colors = ['red', 'green', 'blue']
for i, target in enumerate(target_names):
    # 按类别筛选数据
    ax2.scatter(X_pca[y==i, 0], X_pca[y==i, 1], c=colors[i], label=target, alpha=0.8)
ax2.set_xlabel('第一主成分(PC1)')
ax2.set_ylabel('第二主成分(PC2)')
ax2.set_title('鸢尾花数据集PCA降维结果(4→2维)')
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()  # 调整子图间距
plt.show()

运行结果

        从左图可得:将原始 4 维特征降维到 2 维是合理的,既大幅减少了计算量,又几乎没有损失有效信息。

        从右图可得:降维后的数据不仅保留了原始信息,还让我们能直观看到三类鸢尾花的分布规律,验证了 PCA 在高维数据可视化上的价值。

  • 仅用 2 个主成分就保留了约 95.8% 的信息,降维效率极高。
  • 降维后的数据仍能清晰展示类别间的区分度。

特征值(从大到小): [2.9381 0.9202 0.1477 0.0209]
单个主成分方差贡献率: [0.7296 0.2285 0.0367 0.0052]
累计方差贡献率: [0.7296 0.9581 0.9948 1.    ]
投影矩阵(前2个主成分的方向):
 [[ 0.5211 -0.3774]
 [-0.2693 -0.9233]
 [ 0.5804 -0.0245]
 [ 0.5649 -0.0669]]


四、结论

  • 核心目标:通过线性变换将高维线性相关特征映射到低维正交主成分空间,最大化保留数据方差,实现降维;
  • 数学核心:对特征间的协方差矩阵做特征值分解,特征向量是主成分方向,特征值是主成分的信息含量,选择前 k 个特征向量构建投影矩阵完成数据投影;
  • 实现步骤:标准化数据→计算协方差矩阵→特征值分解并排序→计算累计方差贡献率选 k→构建投影矩阵→数据投影。

        感谢大家的观看!如果有不足,大家可以留言。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐