在sklearn.decomposition.PCA中,为什么components_是负数?

20

我正在尝试跟随Abdi和Williams-主成分分析(2010)并通过SVD构建主成分,使用numpy.linalg.svd

当我显示经过sklearn拟合的PCA的components_属性时,它们与我手动计算的完全相同,但是一些(而不是全部)的符号相反。这是什么原因?

更新: 我下面的(部分)答案包含了一些额外的信息。

取以下示例数据:

from pandas_datareader.data import DataReader as dr
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import scale

# sample data - shape (20, 3), each column standardized to N~(0,1)
rates = scale(dr(['DGS5', 'DGS10', 'DGS30'], 'fred', 
           start='2017-01-01', end='2017-02-01').pct_change().dropna())

# with sklearn PCA:
pca = PCA().fit(rates)
print(pca.components_)
[[-0.58365629 -0.58614003 -0.56194768]
 [-0.43328092 -0.36048659  0.82602486]
 [-0.68674084  0.72559581 -0.04356302]]

# compare to the manual method via SVD:
u, s, Vh = np.linalg.svd(np.asmatrix(rates), full_matrices=False)
print(Vh)
[[ 0.58365629  0.58614003  0.56194768]
 [ 0.43328092  0.36048659 -0.82602486]
 [-0.68674084  0.72559581 -0.04356302]]

# odd: some, but not all signs reversed
print(np.isclose(Vh, -1 * pca.components_))
[[ True  True  True]
 [ True  True  True]
 [False False False]]

这里使用R包对PCA进行解释。https://stats.stackexchange.com/questions/88880/does-the-sign-of-scores-or-of-loadings-in-pca-or-fa-have-a-meaning-may-i-revers - Alex F
4个回答

15
正如您在回答中所指出的那样,奇异值分解(SVD)的结果在奇异向量方面并不唯一。实际上,如果X的SVD为\sum_1^r \s_i u_i v_i^\top: enter image description here 其中s_i按递减顺序排序,则您可以看到您可以更改u_1和v_1的符号(即“翻转”),负号将被抵消,因此公式仍然成立。
这表明SVD在一对左右奇异向量的正负变化上是唯一的。
由于PCA只是X的SVD(或X^\top X的特征值分解),因此不能保证每次执行时都返回相同的结果。理所当然,scikit-learn的实现希望避免这种情况:他们保证返回的左右奇异向量(存储在U和V中)始终相同,通过强制(这是任意的)绝对值最大的u_i系数为正数。
如您在阅读源代码时可以看出:首先他们使用计算U和V。然后,对于每个向量u_i(即U的行),如果其绝对值最大的元素为正,则不进行任何操作。否则,他们将u_i更改为-u_i,相应的左奇异向量v_i则更改为-v_i。如前所述,这不会改变SVD公式,因为减号会抵消掉。但是,现在可以保证经过此处理后返回的U和V始终相同,因为已经消除了符号上的不确定性。

@BradSolomon 如果我可以问一下,在哪种情况下具有非确定性结果是有用的? - P. Camilleri
这些仍然是确定性的——只是一个问题,是“基于U的决策”还是“基于V的决策”进行符号翻转。请参考svd_flip。我的观点是我想做出基于V而不是基于U的决策。请参阅相关问题此处。如果我说得有道理,请告诉我。 - Brad Solomon

4

经过一番查找,我已经澄清了一些关于这个问题的困惑,但并非全部。这个问题已经在stats.stackexchange 这里得到了讨论。数学上的答案是:“PCA是一个简单的数学变换。如果你改变分量的符号,你不会改变第一个分量中包含的方差。”然而,在这种情况下(使用sklearn.PCA),歧义的来源要具体得多:在PCA的源代码(第391行)中,你可以看到:

U, S, V = linalg.svd(X, full_matrices=False)
# flip eigenvectors' sign to enforce deterministic output
U, V = svd_flip(U, V)

components_ = V

svd_flip 被定义在 这里。但为什么要翻转符号以“确保一个确定性的输出”,我不确定。(在此时已经找到了 U,S,V ...)。虽然 sklearn 的实现并不是错误的,但我认为它并不是特别直观的。熟悉 beta(系数)概念的金融领域的任何人都会知道第一个主成分很可能类似于广泛市场指数。问题是,sklearn 的实现将给您强烈的负载到第一个主成分。

我的解决方案是一个简化版的版本,不实现svd_flip。它非常基础,没有像sklearn参数那样的svd_solver,但具有许多专门针对此目的的方法。

按照惯例,奇异值都是正数,并按大小排序。 - Arya McCarthy
4
我不确定我是否理解你的意思,请你能否进一步解释一下?“S”是奇异值。如果你看一下PCA的代码,你会发现“S”向量没有被处理。(在第391行,“U,S,V=linalg.svd(X, full_matrices=False)”后,“S”已经是正数了)。实际上,“U”和“V”被操作,以“强制输出确定性”,即使已经找到了一个解决方案。 - Brad Solomon

2
在这里使用三维PCA,您基本上会迭代地找到:1)最大方差保留的1D投影轴;2)与第一步中的轴垂直并且最大方差保留的轴。第三个轴自动是垂直于前两个轴的轴。
components_根据已解释方差进行排序。因此,第一个解释了最多的方差,依此类推。请注意,按照PCA运算的定义,在第一步中,您正在尝试找到用于投影的向量,该向量最大化保留的方差,向量的符号无关紧要:让M成为您的数据矩阵(在您的情况下为(20,3))。当将数据投影时,v1为保留最大方差的向量。如果选择-v1而不是v1,则会获得相同的方差值(您可以检查一下)。然后,在选择第二个向量时,使v2垂直于v1并且保留最大方差。同样,选择-v2而不是v2会保留相同数量的方差。然后,v3可以选择作为-v3或v3。这里唯一重要的事情是v1、v2、v3构成数据M的正交基。符号主要取决于算法如何解决PCA运算的底层特征向量问题。特征值分解或SVD解决方案在符号上可能会有所不同。

0

对于那些关心目的而不关心数学部分的人,这是一个简短的通知。

虽然某些组件的符号相反,但这不应被视为问题。事实上,我们关心的是轴的方向(至少在我看来)。这些组件最终是通过使用pca转换输入数据来识别这些轴的向量。因此,无论每个组件指向哪个方向,我们的数据所在的新轴将是相同的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接