如何确定 PCA 的主要组件数量

数据挖掘 主成分分析
2022-03-15 08:13:54

背景

试图确定用于 MNIST 的 PCA 的主要组件数量 (k),目标是 95%。

from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1)

# Split data into training and test
X, y = mnist["data"], mnist["target"]
X_train, y_train = X[:60000], y[:60000]

COVERAGE=0.95

如果我遵循Coursera 机器学习 - 主成分分析算法,它是 67。

在此处输入图像描述

from sklearn.preprocessing import StandardScaler
X_centered = StandardScaler().fit_transform(X_train - X_train.mean(axis=0))
covariance_matrx = X_centered.T.dot(X_centered) 
U, s, Vt= sp.linalg.svd(covariance_matrx)

calculated_coverages = ((s ** 2) / (len(s) -1)).cumsum()
calculated_coverages = calculated_coverages / calculated_coverages[-1]
k = np.argmax(np.array(calculated_coverages) >= COVERAGE)    
print("k-th component to cover {0} is {1}".format(calculated_coverages[k], k))

覆盖 0.9507022719172283 的第 k 个分量是 66

但是,如果我使用 scikit learn 中的 explain_variance_ratio_,则为 154。

from sklearn.decomposition import PCA
pca = PCA()
pca.fit(X_train)

contributions = pca.explained_variance_ratio_
coverages = pca.explained_variance_ratio_.cumsum()
k = np.argmax(coverages >= COVERAGE)

print("k-th primary compoent for 95% coverage is {}".format(k + 1))

95% 覆盖率的第 k 个主要成分是 154

当我查看scikit-learn/sklearn/decomposition/_pca.py时,看起来逻辑是相同的。

    U, S, V = linalg.svd(X, full_matrices=False)
    # flip eigenvectors' sign to enforce deterministic output
    U, V = svd_flip(U, V)

    components_ = V

    # Get variance explained by singular values
    explained_variance_ = (S ** 2) / (n_samples - 1)
    total_var = explained_variance_.sum()
    explained_variance_ratio_ = explained_variance_ / total_var
    singular_values_ = S.copy()  # Store the singular values.

问题

请帮助理解它们为什么不同。

有关的

2个回答

执行 PCA 有两种方法:

  1. 计算协方差矩阵的特征值分解Σ

  2. 计算数据矩阵的奇异值分解X

在数值上,您可以通过调用svd()其中任何一个来完成这两项操作,如正半定矩阵(如Σ)svd()为您提供特征值分解。

但是,在解释结果时有所不同:

  1. 中的奇异值sΣ,即沿 PC 的方差

  2. 中的s奇异值是X,即沿 PC 的方差的平方根

sklearn他们采用方法 2。因此,他们需要对奇异值求平方来计算覆盖率在 coursera 中,他们使用方法 1,因此无需平方s在您展示的幻灯片和您链接的视频中,他们只是总结了这些值。

在没有运行您的代码的情况下,我的猜测是,如果您更改行

calculated_coverages = ((s ** 2) / (len(s) -1)).cumsum()

calculated_coverages = (s / (len(s) -1)).cumsum()

你会得到更好的结果。

附录:再想一想,我也不确定StandardScaler()PCA 的结果如何影响。比较时,请确保它在您的 PCA 实现和由提供的实现中都应用sklearn(如果这重要与否,请留下评论,非常感谢 ;))。

在查看了 Scikit Learn 的 pca.py 代码后,发现原来的代码是错误的。

我在想 SVD 是将协方差分解为 U、S、V。但显然它将输入矩阵 X 分解为 U、S、V,而无需创建协方差矩阵。

不正确

covariance_matrx = X_centered.T.dot(X_centered) 
U, s, Vt= sp.linalg.svd(covariance_matrx)

正确的

X_centered = X_train - X_train.mean(axis=0)
U, s, Vt= sp.linalg.svd(X_centered, full_matrices=False)

如果没有“full_matrices=False”,则会导致内存错误。