为什么 PCA 结果会随着输入的微小变化而发生巨大变化?

Why does PCA result change drastically with a small change in the input?

我正在使用 PCA 将 Nx3 数组缩减为 Nx2 数组。这主要是因为 PCA 变换(Nx2 矩阵)对于在原始 Nx3 数组上执行的旋转或平移是不变的。下面以下面为例

import numpy as np
from sklearn.decomposition import PCA
a = np.array([[0.5  , 0.5  , 0.5  ],
              [0.332, 0.456, 0.751],
              [0.224, 0.349, 0.349],
              [0.112, 0.314, 0.427]])
pca = PCA(n_components=2, svd_solver='full', random_state=10)
print(pca.fit_transform(a))

输出如下。请注意,由于平移不变性,我们得到相同的输出 print(pca.fit_transform(a-L))L 是任何数字。与旋转相同。

[[ 0.16752654  0.15593431]
 [ 0.20568992 -0.14688601]
 [-0.16899598  0.06364857]
 [-0.20422047 -0.07269687]]

现在,我对数组 a 进行非常小的扰动 (~1%) 并执行 PCA。

a_p = np.array([[0.51 , 0.53 , 0.52 ],
       [0.322, 0.452, 0.741],
       [0.217, 0.342, 0.339],
       [0.116, 0.31 , 0.417]])
pca = PCA(n_components=2, svd_solver='full', random_state=10)
print(pca.fit_transform(a_p))

结果如下。这和原来数组的PCA有很大的不同。

 [[-0.2056024 , -0.14346977]
 [-0.18563578  0.15627932]
 [ 0.17974942 -0.07001969]
 [ 0.21148876  0.05721014]]

我预计扰动数组的PCA变换与原始数组非常相似,但百分比变化很大。为什么是这样?有什么方法可以为稍微 perturbed/shaked 的数组获得非常相似的 PCA 转换?

我知道在第二种情况下(例如 pca.transform(a_p)),我可以通过仅执行变换操作来获得类似的 PCA,但是,在这种情况下,我失去了旋转和平移不变性 w.r.t。 a_p.

这个问题最初与晶体学有关,我的要求是 PCA(或其他)变换不应显着改变输入的微小变化,并且它应该对输入的旋转和变换保持不变.任何人都可以解释以上内容或建议我使用符合我目的的替代方法吗?

您得到的是符号偏移的向量作为主成分。

看下面的代码。我刚刚抓取了 2 个 PCA 实例作为 pca1pca2 来访问它们的 components_ 属性:


import numpy as np
from sklearn.decomposition import PCA
a = np.array([[0.5  , 0.5  , 0.5  ],
              [0.332, 0.456, 0.751],
              [0.224, 0.349, 0.349],
              [0.112, 0.314, 0.427]])
pca1 = PCA(n_components=2, svd_solver='full', random_state=10)
print(pca1.fit_transform(a))

a_p = np.array([[0.51 , 0.53 , 0.52 ],
       [0.322, 0.452, 0.741],
       [0.217, 0.342, 0.339],
       [0.116, 0.31 , 0.417]])
pca2 = PCA(n_components=2, svd_solver='full', random_state=10)
print(pca2.fit_transform(a_p))


pca1.components_
array([[ 0.64935364,  0.38718276,  0.65454515],
       [ 0.63947417,  0.18783695, -0.74551329]])

pca2.components_
array([[-0.65743254, -0.42817638, -0.62003826],
       [-0.59052329, -0.21834821,  0.77692104]])

如您所见,PC 指向相似的方向,但您的符号相反。

例如,pca1 的 PC1 是 [ 0.64935364, 0.38718276, 0.65454515]pca2 的 PC1 是 [-0.65743254, -0.42817638, -0.62003826]。忽略符号,每个坐标之间的差异都比较小……根据我的计算大约在2%、10%和5%。

这符合你“他们应该比较近”的直觉。

这里的关键见解是矢量 [-0.65743254, -0.42817638, -0.62003826] 和矢量 [0.65743254, 0.42817638, 0.62003826] 在 space 中位于同一条线上,但只是“指向”不同的方向。因此,对于 PCA 来说,两者都是同样有效的主成分。

我不知道有什么方法可以强制 sklearn 生成指向同一象限的向量。

这解释了点之间的大部分距离,这是一个“符号”距离。其余部分因您引入的差异而得到解释。

一个快速的解决方案可能是切换 a_p.

的 PCA 转换结果的符号

“符号问题”的一个积极方面是实际上您可以切换嵌入值的符号而不会丢失信息。

所以你会做这样的事情:


t1 = pca1.fit_transform(a)
t2 = pca2.fit_transform(a_p)


t2 = -t2 # Change signs

t1
array([[ 0.16752654,  0.15593431],
       [ 0.20568992, -0.14688601],
       [-0.16899598,  0.06364857],
       [-0.20422047, -0.07269687]])

t2
array([[ 0.2056024 ,  0.14346977],
       [ 0.18563578, -0.15627932],
       [-0.17974942,  0.07001969],
       [-0.21148876, -0.05721014]])

其中 t1t2 与您最初的直觉大致相似 - 并且是正确的 - 建议。