SL Scikit-Learn Learn机器学习教程

Lesson 14

降维与 PCA:化繁为简

高维数据难以可视化、存储昂贵、且容易过拟合。PCA 是最经典的降维方法,通过找方差最大的方向把数据投影到低维空间,同时保留尽可能多的信息。

  • 预计时长45 分钟
  • 难度进阶
  • 前置章节第 13 章

本章细分学习地图

模块拆细学习单元完成后应产出掌握标准
维度灾难高维稀疏性、距离失效、过拟合。维度灾难的直觉解释。能说清为什么高维数据需要降维。
PCA 原理协方差矩阵、特征值/特征向量、投影。PCA 流程描述。能说清主成分的方向和方差的关系。
选择主成分数累计解释方差、肘部图。方差解释图。能用 95% 方差阈值选 n_components。
实战可视化、Pipeline 分类、人脸降维。完整代码。能在 Pipeline 中正确使用 PCA。

学习目标

PCA 流程

1标准化数据
2计算协方差矩阵
3特征值分解
4投影到前 k 个主成分

本章完成度

已完成 0/4

维度灾难

随着特征数增加,数据变得越来越稀疏——距离失去区分度、模型需要指数级增长的数据量才能泛化。

稀疏性

高维空间中数据点散布在各个角落,样本间距离趋于相等,KNN 等算法失效。

过拟合

特征数 ≫ 样本数时,模型很容易记住噪声而非模式。

计算开销

特征数越多,存储和训练成本越高。

解决思路

降维(PCA)、特征选择、正则化。

PCA 原理

PCA(主成分分析)找到数据方差最大的方向作为第一主成分,然后在与之正交的方向中找方差第二大的方向,依此类推。

Xreduced = X · Wk Wk 是前 k 个特征向量组成的矩阵,将原始数据投影到 k 维
PCA 前必须标准化

PCA 基于方差。如果特征量纲不同,大数值特征会主导方差方向。在 PCA 前用 StandardScaler 是必须的。

概念含义
主成分方差最大的正交方向,是原始特征的线性组合。
特征值每个主成分捕获的方差大小。
解释方差比该主成分捕获的方差 / 总方差。

互动实验:找到方差最大的投影方向

下面是一份有明显线性相关性的二维数据。拖动滑块旋转虚线(投影方向),金色小点是每个样本投影到该方向后的位置。留意"当前方向解释方差比",试着手动找到让它最大的角度——那正是 PCA 用协方差矩阵特征值分解算出来的第一主成分方向。

当前方向解释方差比
0%
PC1 方向解释方差比
0%
与 PC1 夹角
观察重点

投影方向对准数据"最长轴"时,金色投影点铺得最开、解释方差比最大——这就是第一主成分。垂直于它的方向(第二主成分)解释方差比最小,金色点会挤在中间一小段。PCA 不是凑巧试出这个角度,而是直接对协方差矩阵做特征值分解算出来的精确解。

选择主成分数

用累计解释方差比来决定保留多少个主成分。通常保留累计方差 ≥ 95% 的前 k 个。

from sklearn.datasets import load_digits
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import numpy as np

X, y = load_digits(return_X_y=True)
X_scaled = StandardScaler().fit_transform(X)

pca = PCA().fit(X_scaled)
cumvar = np.cumsum(pca.explained_variance_ratio_)

for n in [5, 10, 20, 30, 40]:
    print(f"前 {n:2d} 个主成分累计方差: {cumvar[n-1]:.3f}")

# 找到 95% 方差对应的主成分数
n_95 = np.argmax(cumvar >= 0.95) + 1
print(f"\n保留 95% 方差需要 {n_95} 个主成分(原始 {X.shape[1]} 维)")

sklearn 实战

1. 高维数据可视化(降到 2D)

from sklearn.datasets import load_digits
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

X, y = load_digits(return_X_y=True)
X_scaled = StandardScaler().fit_transform(X)

pca = PCA(n_components=2)
X_2d = pca.fit_transform(X_scaled)
print(f"降维后形状: {X_2d.shape}")  # (1797, 2)
print(f"前 2 个主成分解释方差: {pca.explained_variance_ratio_.sum():.3f}")

2. PCA + 分类 Pipeline

from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score

X, y = load_digits(return_X_y=True)

# 不用 PCA
pipe_full = Pipeline([
    ("scaler", StandardScaler()),
    ("svc", SVC(kernel="rbf", random_state=42)),
])
score_full = cross_val_score(pipe_full, X, y, cv=5).mean()

# 用 PCA 保留 95% 方差
pipe_pca = Pipeline([
    ("scaler", StandardScaler()),
    ("pca", PCA(n_components=0.95)),
    ("svc", SVC(kernel="rbf", random_state=42)),
])
score_pca = cross_val_score(pipe_pca, X, y, cv=5).mean()

pipe_pca.fit(X, y)
n_components = pipe_pca.named_steps["pca"].n_components_
print(f"原始 {X.shape[1]} 维 → PCA {n_components} 维")
print(f"无 PCA 准确率: {score_full:.3f}")
print(f"有 PCA 准确率: {score_pca:.3f}")

3. 人脸降维(Eigenfaces)

from sklearn.datasets import fetch_lfw_people
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split

# 加载人脸数据(至少 70 张照片的人)
lfw = fetch_lfw_people(min_faces_per_person=70, resize=0.4)
X, y = lfw.data, lfw.target
print(f"样本: {X.shape[0]}, 特征: {X.shape[1]}")

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y
)

pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("pca", PCA(n_components=100, whiten=True, random_state=42)),
    ("svc", SVC(kernel="rbf", C=10, gamma=0.001)),
])
pipe.fit(X_train, y_train)
print(f"测试准确率: {pipe.score(X_test, y_test):.3f}")
PCA 的局限

PCA 只能捕捉线性关系。如果数据的关键结构是非线性的(如流形),应考虑 t-SNE(可视化)或 UMAP。sklearn 也提供 KernelPCA 用于非线性降维。

练习题

第 1 题:PCA 前为什么必须标准化?不标准化会怎样?

参考思路:PCA 基于方差。不标准化时,量纲大的特征方差大,会主导主成分方向,其他特征的信息被忽略。

第 2 题:累计解释方差 = 0.95 意味着什么?

参考思路:保留的主成分捕获了原始数据 95% 的总方差,只丢失了 5% 的信息。这通常是一个合理的保留阈值。

第 3 题:用手写数字数据集,分别保留 10、20、30 个主成分,对比 SVC 的准确率。

参考思路:修改上面 Pipeline 中的 n_components 参数,观察准确率变化。通常 20-30 个主成分就能达到接近全维度的准确率。

检查点

离开本章前,请确认:

  • 能解释维度灾难的三个问题(稀疏/过拟合/计算)。
  • 能说清 PCA 找方差最大方向并投影的核心思想。
  • 能用累计解释方差比选择合适的主成分数。
  • 能在 Pipeline 中正确使用 StandardScaler + PCA + 模型。