我对sklearn
的PCA
(here is the documentation)及其与奇异值分解(SVD)的关系感到困惑。
在Wikipedia中,
因此,X的完整主成分分解可以表示为T = WX,其中W是权重的逐个矩阵,其列为$ X ^ T X $的特征向量。 W的转置有时称为变白或球形变换。
稍后解释了与SVD的关系后,我们就会:
X = U $ \ Sigma W ^ T $
所以我假设矩阵W,将样本嵌入到潜在空间中(注意矩阵的维数是有意义的),并且在transform
中使用类PCA
的sklearn
模块应该得到相同的结果就像我将观察矩阵乘以[[W。但是,我检查了它们,发现它们不匹配。
import numpy as np
from sklearn.decomposition import PCA
x = np.random.rand(200).reshape(20,10)
x = x-x.mean(axis=0)
u, s, vh = np.linalg.svd(x, full_matrices=False)
pca = PCA().fit(x)
# transformed version based on WIKI: t = [email protected] = [email protected](s)
t_svd1= [email protected]
t_svd2= [email protected](s)
# the pca transform
t_pca = pca.transform(x)
print(np.abs(t_svd1-t_pca).max()) # should be a small value, but it's not :(
print(np.abs(t_svd2-t_pca).max()) # should be a small value, but it's not :(
sklearn
实现之间存在差异,但这不是bug,仅仅是稳定性和可重复性的增强。[您几乎钉住了PCA的确切实现,但是,为了能够完全重现计算,sklearn
开发人员在其实现中添加了另一种实施。问题源于SVD的不确定性,即SVD没有唯一的解决方案。也可以通过设置U_s = -U
和W_s = -W
从方程式中轻松看出,然后U_s
和W_s
也满足:
X = U_s $ \ Sigma W_s ^ T $
更重要的是,在切换U
和W
的列的符号时也是如此。如果仅反转U
和W
的第k列的符号,则等式仍然成立。您可以阅读有关此问题的更多信息。 https://prod-ng.sandia.gov/techlib-noauth/access-control.cgi/2007/076422.pdf。
PCA的实现通过强制绝对值中的最高负载值始终为正来解决此问题,特别是正在使用方法sklearn.utils.extmath.svd_flip
。这样,无论不确定向量np.linalg.svd
中所得矢量具有哪个符号,绝对值的加载值都将保持不变,即矩阵的符号将保持不变。
因此,为了使您的代码具有与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 = [email protected] = [email protected](s)
t_svd1= [email protected]
t_svd2= [email protected](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 :)