我的PCA出了什么问题?

9

我的代码:

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]
主成分 - 与我得到的相同但是转置了 所以我猜可以

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

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

但是它与我在网上找到的教程中的输出不太匹配。另外,如果我有4个维度,我认为我应该有4个特征值而不是像eig给我的那样有150个。我做错了什么吗?
编辑:我注意到这些值相差150,这是数据集中元素的数量。此外,特征值应该相加等于维数,即4。我不明白为什么会出现这种差异。如果我只是将特征值除以len(data),我可以得到想要的结果,但我不明白为什么。无论如何,特征值的比例都没有改变,但它们对我很重要,所以我想理解发生了什么。

它们并不是错误的。特征值/特征向量并不是完全确定的(可以通过任何比例因子变化,甚至可能有符号)。你得到的实际值取决于算法。(如果你搜索一下,你会发现很多关于获取带有“错误”符号的特征值的类似查询,这可能更好地解释了这个问题)。 - robince
4个回答

10

您分解了错误的矩阵。

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

您确实可以使用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)的LA模块中的eig计算它们——与svd相比,它更容易处理,返回值是特征向量和特征值本身,没有其他任何东西。相比之下,正如你所知,svd不能直接返回这些内容。
虽然SVD函数可以分解任何矩阵,而不仅仅是方阵(这是eig函数的限制),但在进行PCA时,您总是有一个要分解的方阵,无论数据处于什么形式。这很显然,因为在PCA中要分解的矩阵是一个协方差矩阵,根据定义它总是方阵(即列是原始矩阵的个别数据点,同样适用于行,每个单元格是这两个数据点的协方差,如主对角线上的1所示——给定的数据点与自身的协方差是完美的)。

1
有时候单独计算平均值和标准差,然后再计算协方差是值得的。如果例如在操纵主成分之后想要恢复过程,则这很有用。 - Predictor
3
我认为这是错误的。你所说的主成分分析(PCA)导致协方差矩阵的特征值/向量,但在我看过的几乎所有情况下,可以通过对(去平均值的)数据矩阵本身执行奇异值分解(SVD)来获得这些结果。这样做更好,因为它消除了计算完整协方差矩阵的需要,并且也更加数值稳定(我不知道细节,但我了解SVD比特征值分解更稳定)。找不到更易懂的描述,但您可以在此处查看:http://public.lanl.gov/mewall/kluwer2002.html 这是我见过的每个严肃实现中使用的方法。 - robince
1
这里有一组更好的笔记,解释了PCA和SVD之间的关系。http://www.snl.salk.edu/~shlens/pca.pdf - robince
5
不,第一行是错误的,“您分解了错误的矩阵”。基本上有两种方法可以进行PCA: 1)计算协方差矩阵的特征向量 2)或者计算数据矩阵的SVD,左奇异向量是协方差矩阵的特征向量。 因此,在第二种情况下(SVD),您根本不需要计算协方差。 在原始问题中,他正在对数据矩阵应用SVD,这完全是他应该做的。他只是忘记了通过1 /(N-1)对数据进行归一化,这就是为什么他得到了一个150(N)因子的原因。 (请参见下面的我的答案) - Julien
应该使用NP.cov(data, rowvar=0),而不是corrcoef,@doug。 - Simon Righley
显示剩余4条评论

3

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

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

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

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

这在这里详细解释了。


2

请问您可以提出一个问题吗?或者至少将您的问题分别列出来。由于您没有提出单个问题,所以您的帖子读起来像是一段意识流。

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

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

    a. X的左奇异向量,

    b. X的主成分,

    c. XX^T的特征向量。

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


假设有一个150行4列的矩阵X,其中每一列都是一个样本。执行u,s,v = np.linalg.svd(X)将返回形状为(150,150)的u,形状为(4,)的s和形状为(4,4)的v。但如果我们执行u,s,v = np.linalg.svd(np.dot(X, X^T)/(4-1)),它将返回形状为(150,150)的u,形状为(150,)的s和形状为(150,150)的v。但前者的s形状与执行eig_val,eig_vec= np.linalg.eig(np.dot(X, X^T)/(4-1))时的eig_val形状不匹配。 - June Wang

0

2
那个帖子中实现它的唯一一个人也使用了svd函数,这导致了不同的特征值。 - pcapcapcapca

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