现在位置: 首页 > AI 数学基础 > 正文

PCA 给数据降维并可视化

亲手实现 PCA(主成分分析),不调 sklearn。直观看到特征值和特征向量在「压缩信息、保留主要差异」中的作用。

学完本案例你将理解:PCA 就是找到数据分布最「散」的方向,用更少的维度保留尽可能多的信息。


生活引入

给朋友描述一条鱼的形状

你不需要精确描述鱼身上每一个点的三维坐标。你只需要说:「大概这么长、这么宽、这个形状」。你用最少的几个数据抓住了最关键的差异。

PCA 做的正是这件事:从高维数据中找出最重要的几个方向,扔掉不重要的细节,用更少的数字描述同一份数据。


直观理解

假设我们有 300 个三维数据点。它们并不是均匀分布的——大多数点沿着某条斜线排列,只有很少的点偏离这条斜线。

这意味着什么?意味着我们其实不需要三个维度的坐标。两个维度就够了——一个沿斜线方向,一个垂直于斜线方向。垂直方向的第三个维度数据几乎没什么变化,可以丢掉。

中心化
协方差矩阵
特征分解
投影降维

特征值越大,说明数据在这个方向上「散得越开」——这个方向越重要。


数学定义

PCA 算法步骤

\[ \begin{aligned} \text{1. 中心化:}&\quad X_c = X - \bar{X} \\ \text{2. 协方差矩阵:}&\quad \Sigma = \frac{1}{n-1}X_c^T X_c \\ \text{3. 特征分解:}&\quad \Sigma = V \Lambda V^T \\ \text{4. 选取前 k 个特征向量:}&\quad V_k = [v_1, v_2, ..., v_k] \\ \text{5. 投影:}&\quad X_{\text{reduced}} = X_c \cdot V_k \end{aligned} \]

解释方差比例

\[ \text{解释比例}_i = \frac{\lambda_i}{\sum_{j=1}^{d} \lambda_j} \]

其中 \(\lambda_i\) 是第 \(i\) 大的特征值。这个比例告诉你:第 \(i\) 个主成分「贡献」了多少总方差。


Python 动手实践

构造一个 3 维但实际主要沿某方向分布的数据集,手写 PCA 将其降到 2 维。

实例

import numpy as np
np.random.seed(42)

# 1. 构造 3 维数据,数据主要沿某方向分布
n = 300
base = np.random.randn(n, 1)
X = np.hstack([
    base * 3 + np.random.randn(n, 1) * 0.3,   # x1 列
    base * 1.5 + np.random.randn(n, 1) * 0.3,  # x2 列
    np.random.randn(n, 1) * 0.2,               # x3 列(噪声)
])
print("RUNOOB 原始数据形状:", X.shape,
      "(300 个样本,每个样本 3 维)")

# 2. 手写 PCA(四步走)
def pca(X, n_components=2):
    # 第一步:中心化——把数据的均值移到 0
    X_centered = X - X.mean(axis=0)
    # 第二步:协方差矩阵——找出所有两两变量间的关系
    cov = np.cov(X_centered, rowvar=False)
    # 第三步:特征分解——eigh 用于对称矩阵更稳定
    eigvals, eigvecs = np.linalg.eigh(cov)
    # 按特征值从大到小排序
    order = np.argsort(eigvals)[::-1]
    eigvals = eigvals[order]
    eigvecs = eigvecs[:, order]
    # 第四步:取前 k 个特征向量,投影
    components = eigvecs[:, :n_components]
    X_reduced = X_centered @ components
    # 计算每个主成分解释的方差比例
    explained_ratio = eigvals[:n_components] / eigvals.sum()
    return X_reduced, eigvals, explained_ratio

X_reduced, eigvals, explained_ratio = pca(X, n_components=2)

print("\nRUNOOB 三个特征值 (方差大小):",
      np.round(eigvals, 3))
print("前 2 个主成分解释的方差比例:",
      np.round(explained_ratio, 3))
print("累计保留信息:",
      round(explained_ratio.sum() * 100, 1), "%")
print("降维后数据形状:", X_reduced.shape,
      "(从 3 维降到了 2 维)")

# 3. 关键观察:第三个特征值非常小
print(f"\nRUNOOB 第三个特征值只有 {eigvals[2]:.4f},"
      f"远小于前两个 ({eigvals[0]:.2f}, {eigvals[1]:.2f})")
print("说明第三维几乎没有信息量——可以安全丢掉!")
RUNOOB 原始数据形状: (300, 3) (300 个样本,每个样本 3 维)

RUNOOB 三个特征值 (方差大小): [9.569 2.267 0.04 ]
前 2 个主成分解释的方差比例: [0.806 0.191]
累计保留信息: 99.7 %
降维后数据形状: (300, 2) (从 3 维降到了 2 维)

RUNOOB 第三个特征值只有 0.0404,远小于前两个 (9.57, 2.27)
说明第三维几乎没有信息量——可以安全丢掉!

AI 中的应用场景

AI 场景如何使用 PCA
数据可视化将高维数据(如词向量 768 维)降到 2 维,用散点图直观观察数据分布
特征降维训练模型前,去除冗余特征,减少计算量同时降低过拟合风险
噪声过滤只保留方差大的主成分,丢弃方差小的(往往是噪声)
图像压缩将人脸图片用少数几个「特征脸」(Eigenface)的组合来近似表示
推荐系统SVD(PCA 的矩阵版)分解用户-物品评分矩阵,发现隐藏的用户偏好模式