Day 87:【99天精通Python】机器学习进阶 – PCA 降维 – 抓住数据的"主干"
前言
欢迎来到第87天!
在机器学习中,我们经常遇到高维数据。比如,一张 100×100 像素的图片,拉平后就是 10000 个特征;一份用户调查问卷,可能有上百个问题。
特征太多会带来维度灾难 (Curse of Dimensionality):
降维 (Dimensionality Reduction) 就是在尽可能保留原始信息的前提下,用更少的特征来表示数据。主成分分析 (Principal Component Analysis, PCA) 是最经典、最常用的降维方法。
本节内容:
- PCA 原理:旋转坐标轴
- Sklearn 中的 PCA
- 如何选择降维后的维度 (n_components)
- 可视化降维效果
- 实战练习:手写数字降维与重建
一、PCA 原理:找到最重要的方向
PCA 的目标是找到一个新的坐标系,在这个坐标系下,数据点的方差最大。方差越大,代表信息量越丰富。
这些新的坐标轴,就叫做主成分 (Principal Components)。
[外链图片转存中…(img-1L5jRPDv-1768773842454)]
上图中,PC1 是方差最大的方向(第一个主成分),PC2 是与 PC1 正交且方差次大的方向。
如果我们只想保留一维,那么所有点都投影到 PC1 轴上,这就是降维。
二、Sklearn 实现
我们用一个三维数据来演示。
import numpy as np
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
# 1. 创建一个 3D 数据集
np.random.seed(42)
X = np.dot(np.random.rand(2, 2), np.random.randn(2, 200)).T
X[:, 1] += 0.5 * X[:, 0]
X = np.hstack([X, 0.1 * np.random.randn(200, 1)]) # 增加一个噪声维度
# 2. 初始化 PCA
# n_components=2: 我们想降到 2 维
pca = PCA(n_components=2)
# 3. 训练并转换
X_2d = pca.fit_transform(X)
# 4. 查看结果
print(f"原始形状: {X.shape}") # (200, 3)
print(f"降维后形状: {X_2d.shape}") # (200, 2)
# 可视化对比
fig = plt.figure(figsize=(10, 5))
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(X[:, 0], X[:, 1], X[:, 2])
ax1.set_title("Original 3D Data")
ax2 = fig.add_subplot(122)
ax2.scatter(X_2d[:, 0], X_2d[:, 1])
ax2.set_title("PCA to 2D")
# plt.show()
三、如何选择降维的维度?
通常我们不会直接指定 n_components=2,而是让 PCA 帮我们决定。
一个常见的做法是:保留 95% 的信息。
3.1 解释方差比 (Explained Variance Ratio)
# 初始化时不指定 n_components
pca = PCA()
pca.fit(X)
# 查看每个主成分能解释的方差比例
print(pca.explained_variance_ratio_)
# 查看累计方差比
cumulative_variance = np.cumsum(pca.explained_variance_ratio_)
plt.plot(range(1, len(cumulative_variance) + 1), cumulative_variance, marker='o')
plt.xlabel("Number of Components")
plt.ylabel("Cumulative Explained Variance")
plt.grid(True)
# plt.show()
3.2 自动选择
我们可以直接在 PCA 中设置保留的方差比例。
# 自动选择能保留 95% 方差的主成分数量
pca = PCA(n_components=0.95)
X_reduced = pca.fit_transform(X)
print(f"保留95%信息后,维度变为: {X_reduced.shape[1]}")
四、实战:MNIST 降维与可视化
手写数字图片是 28×28 = 784 维的,我们无法直接可视化。
我们可以用 PCA 把它降到 2 维,然后画出来,看看不同数字是否能自动分开。
from sklearn.datasets import fetch_openml
from sklearn.preprocessing import StandardScaler
# 1. 加载数据 (只取 5000 个,否则太慢)
X, y = fetch_openml('mnist_784', version=1, return_X_y=True, as_frame=False)
X, y = X[:5000], y[:5000]
# 2. 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 3. PCA 降维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# 4. 可视化
plt.figure(figsize=(10, 8))
y_numeric = y.astype(int)
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y_numeric, cmap=plt.cm.get_cmap('jet', 10), alpha=0.6)
plt.colorbar(label='Digit Label')
plt.xlabel("Principal Component 1")
plt.ylabel("Principal Component 2")
plt.title("PCA of MNIST Dataset")
# plt.show()
从图中可以看出,PCA 成功地把一些相似的数字(如 1 和 7)在二维空间中分开了。
五、降维 vs 特征选择
- 特征选择 (Feature Selection):从原有特征中挑选出一部分(如 100 个特征里选 10 个)。
- PCA 降维:将原有特征线性组合,创造出全新的、更少的主成分特征。
PCA 保留了所有特征的信息(只是权重不同),而特征选择会完全丢弃某些特征。
六、常见问题
Q1:PCA 需要标准化吗?
必须! PCA 是基于方差的。如果某个特征的数值范围特别大(如收入),它会主导整个 PCA 的计算,导致其他特征被忽略。
Q2:降维后能恢复吗?
可以,但会丢失一部分信息。
X_reconstructed = pca.inverse_transform(X_reduced)
Q3:PCA 是最好的降维方法吗?
不是。PCA 只能做线性降维。对于复杂的非线性结构(如瑞士卷),需要用 t-SNE 或 UMAP。
七、小结
#mermaid-svg-FtOz2nHxxcF1G5ak{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FtOz2nHxxcF1G5ak .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FtOz2nHxxcF1G5ak .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FtOz2nHxxcF1G5ak .error-icon{fill:#552222;}#mermaid-svg-FtOz2nHxxcF1G5ak .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FtOz2nHxxcF1G5ak .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FtOz2nHxxcF1G5ak .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FtOz2nHxxcF1G5ak .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FtOz2nHxxcF1G5ak .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FtOz2nHxxcF1G5ak .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FtOz2nHxxcF1G5ak .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FtOz2nHxxcF1G5ak .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FtOz2nHxxcF1G5ak .marker.cross{stroke:#333333;}#mermaid-svg-FtOz2nHxxcF1G5ak svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FtOz2nHxxcF1G5ak p{margin:0;}#mermaid-svg-FtOz2nHxxcF1G5ak .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-FtOz2nHxxcF1G5ak .cluster-label text{fill:#333;}#mermaid-svg-FtOz2nHxxcF1G5ak .cluster-label span{color:#333;}#mermaid-svg-FtOz2nHxxcF1G5ak .cluster-label span p{background-color:transparent;}#mermaid-svg-FtOz2nHxxcF1G5ak .label text,#mermaid-svg-FtOz2nHxxcF1G5ak span{fill:#333;color:#333;}#mermaid-svg-FtOz2nHxxcF1G5ak .node rect,#mermaid-svg-FtOz2nHxxcF1G5ak .node circle,#mermaid-svg-FtOz2nHxxcF1G5ak .node ellipse,#mermaid-svg-FtOz2nHxxcF1G5ak .node polygon,#mermaid-svg-FtOz2nHxxcF1G5ak .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FtOz2nHxxcF1G5ak .rough-node .label text,#mermaid-svg-FtOz2nHxxcF1G5ak .node .label text,#mermaid-svg-FtOz2nHxxcF1G5ak .image-shape .label,#mermaid-svg-FtOz2nHxxcF1G5ak .icon-shape .label{text-anchor:middle;}#mermaid-svg-FtOz2nHxxcF1G5ak .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FtOz2nHxxcF1G5ak .rough-node .label,#mermaid-svg-FtOz2nHxxcF1G5ak .node .label,#mermaid-svg-FtOz2nHxxcF1G5ak .image-shape .label,#mermaid-svg-FtOz2nHxxcF1G5ak .icon-shape .label{text-align:center;}#mermaid-svg-FtOz2nHxxcF1G5ak .node.clickable{cursor:pointer;}#mermaid-svg-FtOz2nHxxcF1G5ak .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FtOz2nHxxcF1G5ak .arrowheadPath{fill:#333333;}#mermaid-svg-FtOz2nHxxcF1G5ak .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FtOz2nHxxcF1G5ak .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FtOz2nHxxcF1G5ak .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FtOz2nHxxcF1G5ak .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FtOz2nHxxcF1G5ak .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FtOz2nHxxcF1G5ak .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FtOz2nHxxcF1G5ak .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FtOz2nHxxcF1G5ak .cluster text{fill:#333;}#mermaid-svg-FtOz2nHxxcF1G5ak .cluster span{color:#333;}#mermaid-svg-FtOz2nHxxcF1G5ak div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FtOz2nHxxcF1G5ak .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FtOz2nHxxcF1G5ak rect.text{fill:none;stroke-width:0;}#mermaid-svg-FtOz2nHxxcF1G5ak .icon-shape,#mermaid-svg-FtOz2nHxxcF1G5ak .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FtOz2nHxxcF1G5ak .icon-shape p,#mermaid-svg-FtOz2nHxxcF1G5ak .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FtOz2nHxxcF1G5ak .icon-shape rect,#mermaid-svg-FtOz2nHxxcF1G5ak .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FtOz2nHxxcF1G5ak .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FtOz2nHxxcF1G5ak .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FtOz2nHxxcF1G5ak :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
PCA 降维
核心思想
Sklearn 实现
选择维度
寻找方差最大的方向
线性组合
PCA(n_components=…)
fit_transform()
指定数量: n_components=2
指定比例: n_components=0.95
画图: Explained Variance
关键要点:
八、课后作业
下节预告
Day 88:实战篇总结与项目复盘 – 我们的实战篇(Day 46-87)即将结束。明天我们将对数据分析和机器学习部分进行全面复盘,并展望最终的项目篇(二)!
系列导航:
- 上一篇:Day 86 – K-Means聚类
- 下一篇:Day 88 – 实战篇总结(待更新)





