Skip to content

PCA 给数据降维并可视化

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

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


生活引入

给朋友描述一条鱼的形状

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

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


直观理解

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

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

中心化

协方差矩阵

特征分解

投影降维

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


数学定义

PCA 算法步骤

1. 中心化:X_c=XX¯2. 协方差矩阵:Σ=1n1X_cTX_c3. 特征分解:Σ=VΛVT4. 选取前 k 个特征向量:V_k=\[v_1,v_2,...,v_k\]5. 投影:X_reduced=X_cV_k

解释方差比例

解释比例_i=λ_i_j=1dλ_j

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


Python 动手实践

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

实例

python

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("说明第三维几乎没有信息量——可以安全丢掉!")
python
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 的矩阵版)分解用户-物品评分矩阵,发现隐藏的用户偏好模式

AI 思考中...

余弦相似度做文本推荐

矩阵乘法实现神经网络前向传播

基于 VitePress 构建,部署于 GitHub Pages