为什么 PCA 结果会随着输入的微小变化而急剧变化?

时间:2021-04-21 22:42:21

标签: python pca dimensionality-reduction

我正在使用 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 转换与原始数组的 PCA 转换非常相似,但百分比变化很大。为什么是这样?有什么方法可以获得非常相似的 PCA 转换,用于轻微扰动/抖动的数组?

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

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

1 个答案:

答案 0 :(得分:1)

您得到的向量的主要成分是符号移位。

请看下面的代码。我只是抓住了 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] 在空间中位于同一条线上,但只是“指向”不同的方向。因此,对于 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 与您最初(并且正确地)建议的直觉大致相似。