PCA的原理及MATLAB实现
UFLDL教程:Exercise:PCA in 2D & PCA and Whitening
python-A comparison of various Robust PCA implementations
----------------------------
本文参考:
http://blog.csdn.net/gwh111/article/details/11742735
http://www.mathworks.com/matlabcentral/newsreader/view_thread/326056
https://www.zhihu.com/question/37094362
http://blog.csdn.net/zhaoxinfan/article/details/9138165
http://blog.csdn.net/stdcoutzyx/article/details/37568225
http://blog.csdn.net/ice110956/article/details/14250745
http://blog.csdn.net/xl890727/article/details/16898315
http://www.cnblogs.com/tornadomeet/archive/2012/09/06/2673104.html
http://blog.csdn.net/llp1992/article/details/45065609
http://www.cnblogs.com/tornadomeet/archive/2012/12/30/2839615.html
http://blog.csdn.net/ice110956/article/details/20936351?utm_source=tuicool&utm_medium=referral
关于PCA原理可以直接参考下面的文章
深入理解PCA
PCA是经常用来减少数据集的维数,同时保留数据集中对方差贡献最大的特征来达到简化数据集的目的。
PCA的原理就是将原来的样本数据投影到一个新的空间中,相当于我们在矩阵分析里面学习的将一组矩阵映射到另外的坐标系下。通过一个转换坐标,也可以理解成把一组坐标转换到另外一组坐标系下,但是在新的坐标系下,表示原来的原本不需要那么多的变量,只需要原来样本的最大的一个线性无关组的特征值对应的空间的坐标即可。
PCA算法核心思想就是将 n 维特征映射到 k 维上(k < n),这 k 维是全新的正交特征。我们将这 k 维成为主元,是重新构造出来的 k 维特征,而不是简单地从 n 维特征中取出其余 n-k 维特征。
PCA实现的步骤
(1)把原始数据中每个样本用一个向量表示,然后把所有样本组合起来构成一个矩阵。为了避免样本的单位的影响,样本集需要标准化(一般都是去均值化)。
(2)求该矩阵的协方差矩阵。
(3)求步骤2中得到的协方差矩阵的特征值和特征向量。
(4)将求出的特征向量按照特征值的大小进行组合形成一个映射矩阵,并根据指定的PCA保留的特征个数取出映射矩阵的前n行或者前n列作为最终的映射矩阵。
(5)用步骤4的映射矩阵对原始数据进行映射,达到数据降维的目的。
PCA理论的matlab实现
function [Y,V,E,D] = newpca(X)
%其中X为输入数据,X的每一列是一个输入样本。返回值Y是对X进行PCA分析后的投影矩阵。
%V是与X有关的协方差矩阵特征向量的白化矩阵,E是对应的特征向量(列)构成的矩阵,
%D是对应的特征值构成的对角矩阵(特征值处于对角线上)。
%返回值中的白化矩阵,特征向量和特征值都是按照对应特征值大小进行排序后了的。
% do PCA on image patches
%
% INPUT variables:
% X matrix with image patches as columns
%
% OUTPUT variables:
% Y the project matrix of the input data X without whiting
% V whitening matrix
% E principal component transformation (orthogonal)
% D variances of the principal components
%去除直流成分
X = X-ones(size(X,1),1)*mean(X);
% Calculate the eigenvalues and eigenvectors of the new covariance matrix.
covarianceMatrix = X*X'/size(X,2); %求出其协方差矩阵
%E是特征向量构成,它的每一列是特征向量,D是特征值构成的对角矩阵
%这些特征值和特征向量都没有经过排序
[E, D] = eig(covarianceMatrix);
% Sort the eigenvalues and recompute matrices
% 因为sort函数是升序排列,而需要的是降序排列,所以先取负号,diag(a)是取出a的对角元素构成
% 一个列向量,这里的dummy是降序排列后的向量,order是其排列顺序
[dummy,order] = sort(diag(-D));
E = E(:,order);%将特征向量按照特征值大小进行降序排列,每一列是一个特征向量
Y = E'*X;
d = diag(D); %d是一个列向量
%dsqrtinv是列向量,特征值开根号后取倒,仍然是与特征值有关的列向量
%其实就是求开根号后的逆矩阵
dsqrtinv = real(d.^(-0.5));
Dsqrtinv = diag(dsqrtinv(order));%是一个对角矩阵,矩阵中的元素时按降序排列好了的特征值(经过取根号倒后)
D = diag(d(order));%D是一个对角矩阵,其对角元素由特征值从大到小构成
V = Dsqrtinv*E';%特征值矩阵乘以特征向量矩阵
示例:二维数据的实现过程
function [lowData,reconMat] = PCA(data,K)
[row , col] = size(data);
meanValue = mean(data);
%varData = var(data,1,1);
normData = data - repmat(meanValue,[row,1]);
covMat = cov(normData(:,1),normData(:,2));%求取协方差矩阵
[eigVect,eigVal] = eig(covMat);%求取特征值和特征向量
[sortMat, sortIX] = sort(eigVal,'descend');
[B,IX] = sort(sortMat(1,:),'descend');
len = min(K,length(IX));
eigVect(:,IX(1:1:len));
lowData = normData * eigVect(:,IX(1:1:len));
reconMat = (lowData * eigVect(:,IX(1:1:len))') + repmat(meanValue,[row,1]); % 将降维后的数据转换到新空间
end
下面给出PCA的Matlab实现部分:Matlab自带的函数
Matlab中已经包含了实现了的PCA算法,可以通过princomp函数调用。其形式为:
[COEFF,SCORE, latent]=princomp(X);
X:为要输入的n维原始数据。带入这个matlab自带函数,将会生成新的n维加工后的数据(即SCORE)。此数据与之前的n维原始数据一一对应。
COEFF为主成分分量,即变换空间中的那些基向量,是系数矩阵。通过COEFF可以知道x是怎样转换成SCORE的。
在n行p列的数据集X上做主成分分析。返回主成分系数。X的每行表示一个样本的观测值,每一列表示特征变量。COEFF是一个p行p列的矩阵,是X矩阵所对应的协方差阵V的所有特征向量组成的矩阵,即变换矩阵或称投影矩阵,COEFF每列对应一个特征值的特征向量,列的排列顺序是按特征值的大小递减排序。
SCORE为主成分,即X的低维表示。生成的n维加工后的数据存在SCORE里。它是对原始数据进行的分析,进而在新的坐标系下获得的数据。他将这n维数据按贡献率由大到小排列。(即在改变坐标系的情况下,又对n维数据排序)
latent为一个包含样本协方差矩阵的本征值的向量。是一维列向量,每一个数据是对应SCORE里相应维的贡献率,因为数据有n维所以列向量有n个数据。由大到小排列(因为SCORE也是按贡献率由大到小排列)。
princomp这个matlab自带的函数,在降维之前就将每一个样本减去了一个所有样本的平均值。princomp这里使用一行表示一个样本,每行包括这个样本的所有的特征值。
其实我们要的是由函数[COEFF,SCORE, latent] = princomp(X)它所产生的pc和latent。由latent可以算出降维后的空间所能表示原空间的程度,只要这个累积的值大于95%就行了
cumsum(latent)./sum(latent)
举例说明:
load hald; %载入matlab内部数据
[COEFF,SCORE,latent,tsquare] = princomp(ingredients); %调用pca分析函数
% 下面我们具体说明COEFF,SCORE,latent
%下面为计算ingredients所对应的协方差矩阵(也就是cov_ingredients矩阵)的特征值和特征向量,下面的矩阵V为特征向量,D为特征值(对比上面的latent)组成的对角线矩阵
[V,D] = eig(cov_ingredients)
V =
0.5062 0.5673 0.6460 -0.0678
0.4933 -0.5440 0.0200 -0.6785
0.5156 0.4036 -0.7553 0.0290
0.4844 -0.4684 0.1085 0.7309
D =
0.2372 0 0 0
0 12.4054 0 0
0 0 67.4964 0
0 0 0 517.7969
%说明1:对比一下矩阵V和矩阵COEFF,现在很容易明白为什么COEFF是按列递减顺序排列的了!(V中第三列与COEFF中倒数第三列差个负号,学过线性代数的人都知道这没问题)
%下面再验证一下说明2
diag(cov(SCORE))
ans =
517.7969
67.4964
12.4054
0.2372
%说明2:以上结果显示latent确实表示SCORE矩阵每列的方差,517.7969表示第一列方差
[COEFF,SCORE,latent,tsquare] = princomp(ingredients);
% 上面这个函数,COEFF矩阵是返回的转换矩阵,也就是把样本转换到新的空间中的准换矩阵,这个准换矩阵式比较大的。
% SCORE是原来的样本矩阵在新的坐标系中的表示,也就是原来的样本乘上转换矩阵,但是,还不是直接乘,要减去一个样本的均值。将原来的数据转换到新的样本空间中的算法是这样实现的:
x0 = bsxfun(@minus,ingredients,mean(ingredients,1));
NewSCORE1 = x0 * COEFF;
NewSCORE1 =
36.8218 -6.8709 -4.5909 0.3967
29.6073 4.6109 -2.2476 -0.3958
-12.9818 -4.2049 0.9022 -1.1261
23.7147 -6.6341 1.8547 -0.3786
-0.5532 -4.4617 -6.0874 0.1424
-10.8125 -3.6466 0.9130 -0.1350
-32.5882 8.9798 -1.6063 0.0818
22.6064 10.7259 3.2365 0.3243
-9.2626 8.9854 -0.0169 -0.5437
-3.2840 -14.1573 7.0465 0.3405
9.2200 12.3861 3.4283 0.4352
-25.5849 -2.7817 -0.3867 0.4468
-26.9032 -2.9310 -2.4455 0.4116
NewSCORE2 = ingredients * COEFF;
NewSCORE2 =
25.9105 -7.0189 -35.8545 48.5266
18.6960 4.4628 -33.5112 47.7341
-23.8931 -4.3530 -30.3613 47.0039
12.8034 -6.7821 -29.4088 47.7514
-11.4645 -4.6098 -37.3510 48.2723
-21.7238 -3.7946 -30.3506 47.9950
-43.4995 8.8318 -32.8699 48.2117
11.6951 10.5779 -28.0270 48.4543
-20.1739 8.8373 -31.2805 47.5862
-14.1953 -14.3053 -24.2171 48.4705
-1.6913 12.2380 -27.8352 48.5651
-36.4962 -2.9297 -31.6503 48.5768
-37.8145 -3.0790 -33.7091 48.5416
%对比NewSCORE1和NewSCORE2 与 SCORE,可以看成 SCORE与NewSCORE1是一样。
% 下面我们就来确定最后的转换矩阵
cumsum(latent)./sum(latent)
ans =
0.8660
0.9789
0.9996
1.0000
SelectNum = cumsum(latent)./sum(latent)
index = find(SelectNum >= 0.95);
ForwardNum = index(1);
%选择由latent可以算出降维后的空间所能表示原空间的程度,只要这个累积的值大于95%的数目ForwardNum,就选择前ForwardNum个主成分作为变换矩阵。
%做主成分变换矩阵,也就是降维矩阵
tranMatrix = COEFF(:,1:ForwardNum)
tranMatrix =
-0.0678 -0.6460
-0.6785 -0.0200
0.0290 0.7553
0.7309 -0.1085
% Newingredients = x0 * tranMatrix
Newingredients =
36.8218 -6.8709
29.6073 4.6109
-12.9818 -4.2049
23.7147 -6.6341
-0.5532 -4.4617
-10.8125 -3.6466
-32.5882 8.9798
22.6064 10.7259
-9.2626 8.9854
-3.2840 -14.1573
9.2200 12.3861
-25.5849 -2.7817
-26.9032 -2.9310
% 这样Newingredients就是ingredients经过PCA降维的最终的结果。
测试样本降维
如果你需要对测试样本降维,一般情况下,使用matlab自带的方式,肯定需要对测试样本减去一个训练样本均值,因为你在给训练样本降维的时候减去了均值,所以测试样本也要减去均值,然后乘以coeff这个矩阵,就获得了测试样本降维后的数据。使用训练样本得到的转换矩阵,保证训练样本和测试样本转换到相同的样本空间中。比如说你的测试样本是1*1000000,那么乘上一个1000000*29的降维矩阵,就获得了1*29的降维后的测试样本的降维数据。
princomp(x)使用的行表示一个样本,每行的所有的列数据都是这个样本的特征值。降维以后比如是30*29,那么每一行就是降维以后的数据。每个样本有29个特征值。
%X为训练集,Xtest为测试集
[COEFF,SCORE, latent] = princomp(X);
SelectNum = cumsum(latent)./sum(latent)
index = find(SelectNum >= 0.95);
ForwardNum = index(1);
%选择由latent可以算出降维后的空间所能表示原空间的程度,只要这个累积的值大于95%的数目ForwardNum,就选择前ForwardNum个主成分作为变换矩阵。
%做主成分变换矩阵
NewtrainScores = SCORE(:,1:ForwardNum);
**NewtrainScores就是X经过PCA降维后的数据**
%
tranMatrix = COEFF(:,1:ForwardNum)
[row , col] = size(Xtest);
meanValue = mean(X);
normXtest = Xtest - repmat(meanValue,[row,1]);
% 第二种方法
%normXtest = bsxfun(@minus,Xtest,meanValue); % center the test data
NewtestScores = normXtest*tranMatrix;
%**NewtestScores 就是Xtest经过PCA降维后的数据。**
备注说明:
对测试样本进行降维的时候,一定要减去训练样本的均值,使用训练样本得到的转换矩阵,保证训练样本和测试样本转换到相同的样本空间中,这样才有意义。
很多人就选择还是使用princomp这个函数处理测试样本,那么这样测试样本被映射到一个新的空间中,和原来的训练样本完全不是在一个空间,一点意义都没有,还是要使用测试样本减去训练样本的均值,然后乘上训练样本降维的时候获得降维矩阵,转换到相同的空间中。
project the test data on the reduced set of the principal
components. Note that princomp centers data before computing the principal components. You need to center the test data as well. It is best to do that using the means obtained from the training data. Something like this would
do:
mu = mean(Xtrain);
[coefs,scores,variances] = princomp(Xtrain,'econ');
% do your thing to select the components
Xtest = bsxfun(@minus,Xtest,mu); % center the test data
testScores = Xtest*coefs(:,1:d);
具体详情大家可以参考
http://blog.csdn.net/gwh111/article/details/11742735
http://www.mathworks.com/matlabcentral/newsreader/view_thread/326056