我的PCA哪里出错了?
我的代码:
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 个回答
(请问能不能问一个问题?或者至少把你的问题分开列出来。你的帖子读起来像是一连串的思绪,因为你没有问一个明确的问题。)
你可能在使用
cov
的时候没有先转置矩阵,这样就用错了。如果cov_mat
是一个4行4列的矩阵,那么eig
会产生四个特征值和四个特征向量。注意,虽然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形式,看看会发生什么。
SVD(A)返回的左奇异值其实是矩阵AA^T的特征向量。
一个数据集A的协方差矩阵是:1/(N-1) * AA^T。
当你通过SVD来做主成分分析(PCA)时,你需要把A矩阵中的每个数都除以(N-1),这样才能得到正确比例的协方差特征值。
在你的情况中,N=150,但你没有进行这个除法,所以结果就不一致了。
详细解释可以在这里找到。
你分解了错误的矩阵。
主成分分析(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就是证明——一个数据点与它自身的协方差是完美的)。