Python PCA如何计算“sklearn”中的转换版本?

Python PCA如何计算“sklearn”中的转换版本?,python,scikit-learn,pca,Python,Scikit Learn,Pca,我对sklearn的PCA()及其与奇异值分解(SVD)的关系感到困惑 我们有 因此,X的完整主成分分解可以表示为T=WX, 其中,W是权重的p-by-p矩阵,其列为$X^T X$的特征向量。W的转置有时称为白化或球化变换 稍后,一旦解释了与SVD的关系,我们有: X=U$\Sigma W^T$ 因此,我假设矩阵W,将样本嵌入潜在空间(注意矩阵的维数是有意义的),并且在sklearn中使用类PCA的transform模块应该给出与我将观察矩阵乘以W相同的结果。但是,我检查了它们,它们不匹配 是否

我对
sklearn
PCA
()及其与奇异值分解(SVD)的关系感到困惑

我们有

因此,X的完整主成分分解可以表示为T=WX, 其中,W是权重的p-by-p矩阵,其列为$X^T X$的特征向量。W的转置有时称为白化或球化变换

稍后,一旦解释了与SVD的关系,我们有:

X=U$\Sigma W^T$

因此,我假设矩阵W,将样本嵌入潜在空间(注意矩阵的维数是有意义的),并且在
sklearn
中使用类
PCA
transform
模块应该给出与我将观察矩阵乘以W相同的结果。但是,我检查了它们,它们不匹配

是否有我遗漏的错误或代码中有错误

将numpy导入为np
从sklearn.decomposition导入PCA
x=np.random.rand(200).重塑(20,10)
x=x-x.平均值(轴=0)
u、 s,vh=np.linalg.svd(x,全矩阵=False)
pca=pca().拟合(x)
#基于WIKI的转换版本:t=X@vh.T = u@np.diag(s)
t_svd1=x@vh.T
t_svd2=u@np.diag(s)
#pca变换
t_pca=pca.transform(x)
print(np.abs(t_svd1-t_pca).max()应该是一个小值,但不是:(
print(np.abs(t_svd2-t_pca).max()应该是一个小值,但不是:(

理论上的维基百科描述和实际的
sklearn
实现之间存在差异,但这不是一个bug,只是一个稳定性和再现性增强

您几乎已经确定了PCA的确切实现,但是为了能够完全重现计算,
sklearn
开发人员在其实现中又增加了一项强制措施。问题源于SVD的不确定性,即SVD没有唯一的解决方案。这很容易看出n通过设置
U_s=-U
W_s=-W
,然后
U_s
W_s
也满足:

X=U\U s$\Sigma W\U s^T$

更重要的是,在切换
U
W
列的符号时,这一点也适用。如果我们只是反转
U
W
的第k列的符号,则等式仍然适用。您可以在此处阅读有关此问题的更多信息

PCA的实现通过强制绝对值中的最高加载值始终为正值来处理此问题,特别是使用了方法
sklearn.utils.extmath.svd_flip
。这样,无论结果向量来自非确定性方法
np.linalg.svd
,加载v绝对值将保持不变,即矩阵的符号将保持不变

因此,为了使您的代码具有与PCA实现相同的结果:

import numpy as np
from sklearn.decomposition import PCA

np.random.seed(41)
x = np.random.rand(200).reshape(20,10)
x = x-x.mean(axis=0)
u, s, vh = np.linalg.svd(x, full_matrices=False)
max_abs_cols = np.argmax(np.abs(u), axis=0)
signs = np.sign(u[max_abs_cols, range(u.shape[1])])
u *= signs
vh *= signs.reshape(-1,1)
pca = PCA().fit(x)

# transformed version based on WIKI: t = X@vh.T = u@np.diag(s)
t_svd1= x@vh.T
t_svd2= u@np.diag(s)
# the pca transform
t_pca = pca.transform(x)

print(np.abs(t_svd1-t_pca).max()) # pretty small value :)
print(np.abs(t_svd2-t_pca).max()) # pretty small value :)