什么是主成分分析 PCA
背景
主成分分析(PCA, Principal Component Analysis) 在机器学习中,主要用于提升向量表示的质量或效率。PCA 可以对一个向量在进行降维的同时,还最大限度的保留向量的主要特征。
现代嵌入模型(如 BERT、OpenAI Embeddings)生成的向量维度很高(如 768、1536 维),导致:
● 存储成本高
● 近似最近邻搜索速度慢
解决方案:用 PCA 将高维向量投影到低维空间(如 128~256 维)
● 保留大部分方差(信息)
● 加速搜索
● 减少内存占用
PCA 就是对此非常有用的一个算法。
原理
假设有 nnn 个样本,每个样本用一个 ddd 维的向量来表示,我们可以用矩阵 Xn×dX_{n\times d}Xn×d 来表示它,假设已经完成了对 XXX 的归一化(保证所有维度量纲、刻度相同),也就是:xi′=xi−μσx'_i=\frac{x_i-\mu}{\sigma}xi′=σxi−μ。
那么 1n⋅XT⋅X\frac{1}{n}\cdot X^T\cdot Xn1⋅XT⋅X 是一个 d×dd\times dd×d 的矩阵,它可以被这样来看待:对角线上是每一个维度的方差,其余是不同维度之间的协方差,如果要对其进行降维的话,我们希望去掉那些特征不鲜明的维度(自身方差小),或者说去掉那些和别的维度相关性很强的维度(和别的维度协方差大),也就是说希望保留下来的维度方差尽可能的大(意味着这个维度特征鲜明),和别的维度协方差尽可能的小(意味着这这两个维度不相关)。
记 S=XT⋅XS=X^T\cdot XS=XT⋅X,SSS 的特征值从大到小是 λ1,...,λd\lambda_1,...,\lambda_dλ1,...,λd,且其对应的特征向量是 w1,...,wdw_1,...,w_dw1,...,wd,wiw_iwi 是 d×1d\times 1d×1 的。根据特征值、特征向量的特性,有:
S⋅wi=λiwiS\cdot w_i=\lambda_iw_iS⋅wi=λiwi,wiw_iwi 是一组正交基
考虑用特征向量组成的矩阵 WWW 对 XXX 做线性变化:Y=X⋅WY=X\cdot WY=X⋅W,W=(w1,...,wd)W=(w_1,...,w_d)W=(w1,...,wd)。
观察 YYY 的方差和协方差,即:
YT⋅Y=WT⋅XT⋅X⋅W=WT⋅S⋅WY^T\cdot Y=W^T\cdot X^T\cdot X\cdot W=W^T\cdot S\cdot WYT⋅Y=WT⋅XT⋅X⋅W=WT⋅S⋅W
=(w1...wd)⋅(λ1w1...λdwd)=(λ1...0...0λ20..................0...λd) {=} \begin{pmatrix} w_1 \\ ... \\ w_d \end{pmatrix} \cdot \begin{pmatrix} \lambda_1w_1 ... \lambda_dw_d \end{pmatrix} {=} \begin{pmatrix} \lambda_1 & ... & 0 & ... \\ 0 & \lambda_2 & 0 & ... \\ ... & ... & ... & ... \\ ... & 0 & ... & \lambda_d \end{pmatrix} = w1...wd ⋅(λ1w1...λdwd)= λ10.........λ2...000...............λd
也就是对角线是特征值,其余都是 0,意味着方差沿着对角线从大到小排列,协方差都是 0。
这个结果正是我们想要的,因此只要取前 ccc 个最大的特征值对应的 wiw_iwi 组成:
W=(wi,...,wc)W=(w_i,...,w_c)W=(wi,...,wc) 去乘以 XXX,就可以得到 Y=X⋅WY=X\cdot WY=X⋅W 这一组新的维度,因为 WWW 是 d×cd\times cd×c 的,因此 YYY 是 n×cn\times cn×c 的,相当于对原有数据进行了降维,且降维后的维度保留了最大的特征(特征)并且是最不相关的(协方差为 0)。
所以,这个问题最终就变成求解矩阵前 kkk 个最大特征值对应的特征向量即可。
实例
让大模型生成一个 Python 脚本实现一下 PCA:
import numpy as np
# 设置打印精度(可选)
np.set_printoptions(precision=4, suppress=True)
# Step 1: 输入 5 个样本,3 个维度的数据
X = np.array([
[2.5, 2.4, 1.1],
[0.5, 0.7, 0.8],
[2.2, 2.9, 1.3],
[1.9, 2.2, 1.0],
[3.1, 3.0, 1.5]
])
print("原始数据 X (5 samples × 3 features):")
print(X)
print()
# Step 2: 中心化(减去均值)
mean_vec = np.mean(X, axis=0)
X_centered = X - mean_vec
print("中心化后的数据 X_centered:")
print(X_centered)
print()
# Step 3: 计算协方差矩阵
# 注意:numpy.cov 默认按列是变量,行是观测,所以需要转置
# 或者用 (X_centered.T @ X_centered) / (n_samples - 1) 作为无偏估计
n_samples = X.shape[0]
# 使用无偏估计(除以 n-1),与 sklearn 一致
cov_matrix = np.cov(X_centered, rowvar=False) # rowvar=False 表示每列是一个变量
print("协方差矩阵 S (3×3):")
print(cov_matrix)
print()
# Step 4: 特征值分解
eigenvals, eigenvecs = np.linalg.eigh(cov_matrix) # eigh 用于对称矩阵,返回升序
# 将特征值从大到小排序
idx = np.argsort(eigenvals)[::-1]
eigenvals = eigenvals[idx]
eigenvecs = eigenvecs[:, idx]
print("特征值(从大到小):")
print(eigenvals)
print()
print("对应的特征向量(每列为一个特征向量):")
print(eigenvecs)
print()
# Step 5: 选择前 k=2 个主成分
k = 2
projection_matrix = eigenvecs[:, :k] # 3×2 矩阵
print(f"投影矩阵 W (3×{k}) —— 前 {k} 个主成分(特征向量):")
print(projection_matrix)
print()
# Step 6: 投影到低维空间
X_pca = X_centered @ projection_matrix # (5×3) @ (3×2) = (5×2)
print("降维后的数据 (5 samples × 2 components):")
print(X_pca)
print()
# Step 7: 验证方差(每个主成分的方差应等于对应特征值)
print("各主成分的样本方差(应 ≈ 特征值):")
pca_variances = np.var(X_pca, axis=0, ddof=1) # ddof=1 为无偏估计
print(pca_variances)
print("对应的特征值:")
print(eigenvals[:k])
结果如下:
原始数据 X (5 samples × 3 features):
[[2.5 2.4 1.1]
[0.5 0.7 0.8]
[2.2 2.9 1.3]
[1.9 2.2 1. ]
[3.1 3. 1.5]]
中心化后的数据 X_centered:
[[ 0.46 0.16 -0.04]
[-1.54 -1.54 -0.34]
[ 0.16 0.66 0.16]
[-0.14 -0.04 -0.14]
[ 1.06 0.76 0.36]]
协方差矩阵 S (3×3):
[[0.938 0.8405 0.233 ]
[0.8405 0.853 0.2255]
[0.233 0.2255 0.073 ]]
特征值(从大到小):
[1.798 0.0541 0.0119]
对应的特征向量(每列为一个特征向量):
[[-0.7121 -0.6967 -0.0872]
[-0.6774 0.7143 -0.1758]
[-0.1847 0.0661 0.9806]]
投影矩阵 W (3×2) —— 前 2 个主成分(特征向量):
[[-0.7121 -0.6967]
[-0.6774 0.7143]
[-0.1847 0.0661]]
降维后的数据 (5 samples × 2 components):
[[-0.4285 -0.2088]
[ 2.2025 -0.0496]
[-0.5906 0.3706]
[ 0.1526 0.0597]
[-1.3361 -0.1718]]
各主成分的样本方差(应 ≈ 特征值):
[1.798 0.0541]
对应的特征值:
[1.798 0.0541]
更多推荐
所有评论(0)