首页 > 解决方案 > 为什么 PCA 结果会随着输入的微小变化而发生巨大变化?

问题描述

我正在使用 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]]

现在,我给阵列一个非常小的扰动 (~1%)a并执行 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.transform(a_p))仅执行变换操作来获得类似的 PCA,但是,在这种情况下,我失去了旋转和平移不变性 wrt a_p

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

标签: pythonpcadimensionality-reduction

解决方案


您将获得符号偏移的向量作为主成分。

请参阅以下代码。我刚刚抓取了 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 指向相似的方向,但您得到了相反的符号。

例如,看到 PC1 代表pca1is[ 0.64935364, 0.38718276, 0.65454515]而 PC1 代表pca2is [-0.65743254, -0.42817638, -0.62003826]。忽略符号,每个坐标之间的差异相对较小……根据我的计算,大约为 2%、10% 和 5%。

这符合您的直觉,即“它们应该相对接近”。

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

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

这解释了您的点之间的大部分距离,这是一个“标志”距离。由于您引入的差异,其余的被解释了。

一个快速的解决方案可能是为 的 PCA 转换切换结果的符号a_p

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

所以你会做这样的事情:


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]])

在哪里t1并且t2与您最初建议的直觉大致相似 - 并且正确 - 建议。


推荐阅读