【问题标题】:What's wrong with my PCA?我的 PCA 有什么问题?
【发布时间】:2011-06-15 15:13:49
【问题描述】:

我的代码:

from numpy import *

def pca(orig_data):
    data = array(orig_data)
    data = (data - data.mean(axis=0)) / data.std(axis=0)
    u, s, v = linalg.svd(data)
    print s #should be s**2 instead!
    print v

def load_iris(path):
    lines = []
    with open(path) as input_file:
        lines = input_file.readlines()
    data = []
    for line in lines:
        cur_line = line.rstrip().split(',')
        cur_line = cur_line[:-1]
        cur_line = [float(elem) for elem in cur_line]
        data.append(array(cur_line))
    return array(data)

if __name__ == '__main__':
    data = load_iris('iris.data')
    pca(data)

鸢尾花数据集:http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data

输出:

[ 20.89551896  11.75513248   4.7013819    1.75816839]
[[ 0.52237162 -0.26335492  0.58125401  0.56561105]
 [-0.37231836 -0.92555649 -0.02109478 -0.06541577]
 [ 0.72101681 -0.24203288 -0.14089226 -0.6338014 ]
 [ 0.26199559 -0.12413481 -0.80115427  0.52354627]]

期望的输出:
特征值 - [2.9108 0.9212 0.1474 0.0206]
主成分 - Same as I got but transposed 好吧,我猜

另外,linalg.eig 函数的输出是什么?根据维基百科上的 PCA 描述,我应该这样做:

cov_mat = cov(orig_data)
val, vec = linalg.eig(cov_mat)
print val

但它与我在网上找到的教程中的输出并不真正匹配。另外,如果我有 4 个维度,我认为我应该有 4 个特征值,而不是像 eig 给我的 150。我做错了吗?

edit:我注意到这些值相差 150,这是数据集中元素的数量。此外,特征值应该相加等于维数,在这种情况下为 4。我不明白为什么会发生这种差异。如果我简单地将特征值除以len(data),我可以获得我想要的结果,但我不明白为什么。无论哪种方式,特征值的比例都没有改变,但它们对我很重要,所以我想了解发生了什么。

【问题讨论】:

  • 它们没有错。特征值/特征向量不是完全确定的(可以随任何比例因子变化,甚至有符号)。你得到的实际值取决于算法。 (如果您进行搜索,您会发现很多类似的查询关于使用“错误”符号获取特征值,这可能更好地解释了这个问题。

标签: python numpy linear-algebra pca


【解决方案1】:

SVD(A)返回的左奇异值是AA^T的特征向量。

数据集A的协方差矩阵为:1/(N-1) * AA^T

现在,当您使用 SVD 进行 PCA 时,您必须将 A 矩阵中的每个条目除以 (N-1),以便获得具有正确比例的协方差特征值。

在您的情况下,N=150 并且您没有进行此除法,因此存在差异。

这个详细解释here

【讨论】:

    【解决方案2】:

    你分解了错误的矩阵。

    主成分分析需要操纵特征向量/特征值 协方差矩阵,而不是数据本身。从 m x n 数据矩阵创建的协方差矩阵将是一个 m x m 矩阵,主对角线上有一个。

    您确实可以使用 cov 功能,但您需要进一步处理您的数据。使用类似的函数可能会更容易一点,corrcoef

    import numpy as NP
    import numpy.linalg as LA
    
    # a simulated data set with 8 data points, each point having five features
    data = NP.random.randint(0, 10, 40).reshape(8, 5)
    
    # usually a good idea to mean center your data first:
    data -= NP.mean(data, axis=0)
    
    # calculate the covariance matrix 
    C = NP.corrcoef(data, rowvar=0)
    # returns an m x m matrix, or here a 5 x 5 matrix)
    
    # now get the eigenvalues/eigenvectors of C:
    eval, evec = LA.eig(C)
    

    为了得到特征向量/特征值,我没有使用 SVD 分解协方差矩阵, 不过,你当然可以。我的偏好是使用 NumPy(或 SciPy)中的 eig 来计算它们 LA 模块——它比 svd 更容易使用,返回值是特征向量 和特征值本身,仅此而已。相比之下,如您所知,svd 不会直接返回这些。

    授予 SVD 函数将分解任何矩阵,而不仅仅是方形矩阵(eig 函数仅限于此);但是,在进行 PCA 时,您总是需要分解一个方阵, 无论您的数据采用何种形式。这很明显,因为您的矩阵 在 PCA 中分解是一个协方差矩阵,根据定义,它总是正方形 (即,列是原始矩阵的各个数据点,同样 对于行,每个单元格是这两个点的协方差,如所证明的那样 通过主对角线下的那些——给定的数据点与其自身具有完美的协方差)。

    【讨论】:

    • 有时值得分别计算均值和标准差,然后计算协方差。这很有用,例如,如果想在操纵主成分后反转该过程。
    • 我同意——在这种情况下,OP 似乎想要一些“刚刚工作”的东西,我发现这样做(通常)更简单,特别是在从命令行工作时。跨度>
    • 我认为这是错误的。正如您所说,PCA 会产生协方差矩阵的特征值/向量,但是在几乎所有情况下,我都看到这些可以通过对(去均值的)数据矩阵本身执行 SVD 来获得。这表现得更好,因为它消除了计算完整协方差矩阵的需要,并且在数值上也更稳定(我不知道细节,但我知道 SVD 比 eig 更稳定)。找不到更易于理解的描述,但您可以在此处查看:public.lanl.gov/mewall/kluwer2002.html 这是我见过的每个严肃实现中使用的方法。
    • 这是一组更好的注释,解释了 PCA 和 SVD 之间的关系snl.salk.edu/~shlens/pca.pdf
    • 不,第一行是错误的“你分解了错误的矩阵。”。基本上有两种方法可以进行 PCA:1) 要么计算 covariance 矩阵的特征向量 2) 要么计算 data 矩阵的 SVD 和左奇异向量是 covariance 矩阵的特征向量所以在第二种情况(SVD)中,您根本不需要计算协方差。在最初的问题中,他在数据矩阵上应用 SVD,这完全是他应该做的。他只是忘记将他的数据标准化 1/(N-1),这就是他得到 150 (N) 因子的原因。 (见下面我的回答)
    【解决方案3】:

    (请问你能问一个问题吗?或者至少单独列出你的问题。你的帖子读起来像是意识流,因为你没有问一个问题。)

    1. 您可能错误地使用了cov,因为您没有先转置矩阵。如果cov_mat 是4×4,那么eig 将产生四个特征值和四个特征向量。

    2. 请注意 SVD 和 PCA 虽然相关,但并不完全相同。令 X 为 4×150 的观察矩阵,其中每个 4 元素列是一个观察。那么,以下是等价的:

      一个。 X 的左奇异向量,

      b. X 的主成分,

      c。 X X^T 的特征向量。

      此外,X X^T 的特征值等于 X 奇异值的平方。要了解这一切,让 X 具有 SVD X = QSV^T,其中 S 是奇异值的对角矩阵。然后考虑特征分解 D = Q^T X X^T Q,其中 D 是特征值的对角矩阵。用它的 SVD 替换 X,看看会发生什么。

    【讨论】:

    • 150×4 矩阵 X 怎么样,其中每一列都是一个样本。 u,s,v = np.linalg.svd(X) 将返回形状为 (150,150)、s 形状 (4,)、v 形状 (4,4) 的 u。但是如果我们做 u,s,v = np.linalg.svd(np.dot(X, X^T)/(4-1)),它将返回形状为 (150,150) 的 u,形状为 (150, ), v 形状(150,150)。但是如果我们执行 eig_val,eig_vec= np.linalg.eig(np.dot(X, X^T)/(4-1)),前一个 s 形状与 eig_val 形状不匹配。所以
    【解决方案4】:

    【讨论】:

    • 该线程中实现它的人也使用了导致不同特征值的 svd 函数
    猜你喜欢
    • 2011-04-10
    • 2015-06-06
    • 2012-09-10
    • 2013-06-29
    • 2016-02-03
    • 2014-11-04
    • 2011-12-29
    • 2015-10-15
    • 2015-03-29
    相关资源
    最近更新 更多