1. PCA是什么?为什么你需要它?

想象你手里有一份学生成绩单,包含数学、语文、英语、物理、化学五科成绩。当你想分析学生的整体学习水平时,真的需要盯着五个数字看吗?其实用"理科综合分"和"文科综合分"两个指标可能更直观——这就是PCA的核心思想: 用更少的变量表达原始数据的核心信息

主成分分析(PCA)的本质是坐标系的旋转。就像我们看一幅斜放的画,把头歪45度能更清楚看到画面内容,PCA通过找到数据方差最大的方向建立新坐标系。举个例子:用手机拍摄文档时如果角度倾斜,得到的照片是变形的(如下图左),PCA就像自动把照片"摆正"的过程(如下图右)。

在实际应用中,PCA能帮你:

  • 将1000维的基因数据压缩到3维进行可视化
  • 消除人脸识别中光照、角度等干扰因素
  • 加速机器学习模型训练过程
  • 发现数据中隐藏的主要变化模式

2. 剥开PCA的数学外壳

2.1 标准化:让所有特征公平竞争

假设我们有以下4个学生的两科成绩:

import numpy as np
原始数据 = np.array([[80, 70], 
                   [90, 60],
                   [75, 95],
                   [85, 80]])

数学成绩范围在75-90分,语文在60-95分。如果不标准化,数值大的语文成绩会主导分析结果。标准化公式为:

$$ X_{\text{标准化}} = \frac{X - \mu}{\sigma} $$

用Python实现:

均值 = np.mean(原始数据, axis=0)
标准差 = np.std(原始数据, axis=0)
标准化数据 = (原始数据 - 均值) / 标准差

2.2 协方差矩阵:发现变量间的"秘密关系"

协方差衡量两个变量如何共同变化。正值表示同增同减,负值表示此消彼长。计算协方差矩阵:

cov_matrix = np.cov(标准化数据.T)  # 注意需要转置

得到的矩阵对角线是各变量的方差,其他位置是协方差。比如:

[[ 1.25       -0.95]
 [-0.95        1.25]]

显示数学和语文成绩呈负相关(-0.95),这与我们数据中"数学好的语文稍弱"的现象一致。

2.3 特征分解:找到关键方向

特征向量指向数据变化最大的方向,特征值表示该方向的重要性大小。用numpy求解:

特征值, 特征向量 = np.linalg.eig(cov_matrix)

假设得到:

  • 特征值:[2.0, 0.5]
  • 特征向量:[[0.707, -0.707], [0.707, 0.707]]

表示第一个主成分方向是45度斜向上(两个科目权重相同),解释大部分方差。

3. 用Python从零实现PCA

3.1 逐步手写实现

class 我的PCA:
    def __init__(self, n_components):
        self.n_components = n_components
        self.特征向量 = None
    
    def fit(self, X):
        # 标准化
        X = (X - np.mean(X, axis=0)) / np.std(X, axis=0)
        
        # 计算协方差矩阵
        cov_matrix = np.cov(X.T)
        
        # 特征分解
        特征值, 特征向量 = np.linalg.eig(cov_matrix)
        
        # 按特征值排序
        idx = 特征值.argsort()[::-1]
        self.特征向量 = 特征向量[:, idx[:self.n_components]]
        
    def transform(self, X):
        return np.dot(X, self.特征向量)

使用示例:

pca = 我的PCA(n_components=1)
pca.fit(原始数据)
降维结果 = pca.transform(原始数据)

3.2 与scikit-learn对比

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

# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(原始数据)

# PCA
pca = PCA(n_components=1)
sklearn结果 = pca.fit_transform(X_scaled)

print("手写实现结果:", 降维结果.flatten())
print("sklearn结果:", sklearn结果.flatten())

比较两者结果是否一致(允许微小计算误差)。

4. 实战:人脸数据降维可视化

4.1 加载Olivetti人脸数据集

from sklearn.datasets import fetch_olivetti_faces

faces = fetch_olivetti_faces()
X = faces.data  # 400张64x64的人脸图像
y = faces.target  # 40人每人10张

4.2 应用PCA降维

pca = PCA(n_components=3)
X_pca = pca.fit_transform(X)

print("各主成分解释方差比例:", pca.explained_variance_ratio_)

输出可能类似:[0.25, 0.15, 0.1],表示前三个主成分共解释50%的方差。

4.3 3D可视化

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')

for i in range(40):  # 40个人
    ax.scatter(X_pca[y==i, 0], X_pca[y==i, 1], X_pca[y==i, 2], 
               label=str(i), alpha=0.7)

ax.set_xlabel('PC1')
ax.set_ylabel('PC2')
ax.set_zlabel('PC3')
plt.title("人脸数据PCA降维可视化")
plt.show()

你会看到同一个人的不同照片在3D空间中聚在一起,说明PCA保留了身份信息。

5. PCA的进阶技巧与陷阱

5.1 如何选择主成分数量?

  • 肘部法则 :绘制解释方差随成分数量的变化曲线,选择拐点
pca = PCA().fit(X)
plt.plot(np.cumsum(pca.explained_variance_ratio_))
plt.xlabel('主成分数量')
plt.ylabel('累积解释方差')
  • 保留95%方差 :适合机器学习预处理
pca = PCA(n_components=0.95)  # 自动选择数量

5.2 常见问题解决方案

  • 内存不足 :使用增量PCA
from sklearn.decomposition import IncrementalPCA
ipca = IncrementalPCA(n_components=10, batch_size=100)
  • 非线性数据 :尝试核PCA
from sklearn.decomposition import KernelPCA
kpca = KernelPCA(n_components=2, kernel='rbf')

5.3 注意事项

  1. PCA对数据缩放敏感,务必先标准化
  2. 分类问题可考虑LDA(线性判别分析)
  3. 解释主成分含义需要看原始变量权重
  4. 不适用于稀疏数据或存在大量缺失值的情况

6. PCA在真实场景中的应用案例

6.1 图像压缩

将512x512的图像分割成8x8小块,每个块64维:

from scipy import misc
face = misc.face(gray=True)  # 获取示例图像

# 分割图像为8x8块
patches = face.reshape(-1, 8, 8).reshape(-1, 64)

# PCA压缩
pca = PCA(n_components=16)
compressed = pca.fit_transform(patches)
reconstructed = pca.inverse_transform(compressed)

压缩率=16/64=25%,但视觉质量仍保持较好。

6.2 股票市场分析

分析100只股票一年日收益率:

import pandas_datareader as pdr

stocks = ['AAPL', 'MSFT', ...]  # 100只股票代码
data = pdr.get_data_yahoo(stocks, start='2020-01-01')['Adj Close']
returns = data.pct_change().dropna()

pca = PCA(n_components=3)
components = pca.fit_transform(returns)

第一主成分往往代表"市场整体走势",其他成分可能代表行业板块。

6.3 基因组数据分析

处理5万个基因的表达数据:

import pandas as pd
gene_data = pd.read_csv('gene_expression.csv', index_col=0)

# 前1000个变异最大的基因
top_genes = gene_data.var(axis=0).sort_values(ascending=False)[:1000]
pca = PCA(n_components=50)
reduced = pca.fit_transform(gene_data[top_genes.index])

这样的降维使后续聚类分析成为可能。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐