我的PCA哪里出错了?

9 投票
4 回答
6685 浏览
提问于 2025-04-16 10:38

我的代码:

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),我可以得到我想要的结果,但我不明白为什么。无论如何,特征值的比例没有改变,但它们对我来说很重要,所以我想了解发生了什么。

4 个回答

2

(请问能不能问一个问题?或者至少把你的问题分开列出来。你的帖子读起来像是一连串的思绪,因为你没有问一个明确的问题。)

  1. 你可能在使用 cov 的时候没有先转置矩阵,这样就用错了。如果 cov_mat 是一个4行4列的矩阵,那么 eig 会产生四个特征值和四个特征向量。

  2. 注意,虽然SVD和PCA是相关的,但它们并不完全相同。假设X是一个4行150列的观察矩阵,每一列的4个元素代表一个单独的观察值。那么,以下几种说法是等价的:

    a. X的左奇异向量,

    b. X的主成分,

    c. X乘以它的转置(X X^T)的特征向量。

    另外,X X^T的特征值等于X的奇异值的平方。要理解这一点,可以把X表示为SVD形式 X = QSV^T,其中S是一个对角矩阵,包含奇异值。然后考虑特征分解 D = Q^T X X^T Q,其中D是一个对角矩阵,包含特征值。把X替换成它的SVD形式,看看会发生什么。

3

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

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

当你通过SVD来做主成分分析(PCA)时,你需要把A矩阵中的每个数都除以(N-1),这样才能得到正确比例的协方差特征值。

在你的情况中,N=150,但你没有进行这个除法,所以结果就不一致了。

详细解释可以在这里找到。

10

你分解了错误的矩阵。

主成分分析(PCA)需要处理的是协方差矩阵的特征向量和特征值,而不是直接处理数据本身。协方差矩阵是从一个 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函数可以分解任何矩阵,而不仅仅是方阵(而eig函数是有限制的);但是在进行PCA时,你总是会有一个方阵来分解,无论你的数据是什么形式。这一点很明显,因为在PCA中你分解的矩阵是一个协方差矩阵,根据定义它总是方阵(也就是说,列是原始矩阵的各个数据点,行也是如此,每个单元格是这两个点的协方差,主对角线上的1就是证明——一个数据点与它自身的协方差是完美的)。

撰写回答