我假设你使用
eig
函数确定了特征向量。在未来,我建议你使用
eigs
函数。它不仅会计算出特征值和特征向量,还会为你计算与其关联的
k
最大 特征值及其特征向量。这可以节省计算开销,使你不必计算矩阵的所有特征值和相关特征向量,因为你只需要其中一部分。你只需提供数据的协方差矩阵给
eigs
函数,它就会为你返回
k
个最大的特征值和特征向量。
现在回到您的问题,您所描述的最终是
主成分分析。其背后的机制是计算数据的协方差矩阵并找到计算结果的特征值和特征向量。众所周知,以这种方式进行不推荐,因为计算大矩阵的特征值和特征向量存在数值不稳定性。现在最常用的方法是通过
奇异值分解来完成。具体而言,矩阵
V
的列给出了协方差矩阵或主成分的特征向量,相关的特征值是矩阵
S
对角线上产生的奇异值的平方根。
请参见Cross Validated上的这篇信息文章,了解为什么这是首选:
https://stats.stackexchange.com/questions/79043/why-pca-of-data-by-means-of-svd-of-the-data
我还会再提供一个链接,讲解奇异值分解在主成分分析中被使用的理论背景:
https://stats.stackexchange.com/questions/134282/relationship-between-svd-and-pca-how-to-use-svd-to-perform-pca
现在让我们逐个回答您的问题。
问题 #1
MATLAB 生成特征值和相应的特征向量的顺序是未排序的。如果您希望从eig
的输出(例如,800)中选择最大的k
个特征值和相应的特征向量,则需要按降序对特征值进行排序,然后重新排列从eig
产生的特征向量矩阵的列,然后选择前k
个值。
我还应该指出,使用eigs
不能保证排序顺序,因此在必要时您也必须明确排序这些内容。
在MATLAB中,执行上述操作看起来像这样:
sigma = cov(B);
[A,D] = eig(sigma);
vals = diag(D);
[~,ind] = sort(abs(vals), 'descend');
Asort = A(:,ind);
请注意,对特征值的排序是基于其绝对值进行的,因为经过缩放后的特征值本身也是特征值。这些缩放值还包括负数。这意味着,如果我们有一个特征值为-10000的分量,则这非常明显地表明该分量对您的数据具有重要意义,如果我们仅按数字本身排序,则会将其放置在较低的排名附近。
代码的第一行找到了
B
的协方差矩阵,即使您说它已经存储在
sigma
中,但让我们使其可以再现。接下来,我们找到您的协方差矩阵的特征值和相关的特征向量。请注意,特征向量矩阵
A
的每个列代表一个特征向量。具体而言,
A
的第i列/特征向量对应于在
D
中看到的第i个特征值。
然而,特征值在对角矩阵中,因此我们使用
diag
命令提取出对角线,将其排序并确定它们的顺序,然后重新排列
A
以遵守这个顺序。我使用
sort
的第二个输出,因为它告诉您未排序结果中每个值在排序结果中出现的位置。这是我们需要重新排列特征向量矩阵
A
列的顺序。必须选择
'descend'
作为标志,以便最大的特征值和相关的特征向量首先出现,就像我们之前讨论过的那样。
然后,您可以通过以下方式挑选出前
k
个最大的向量和值:
k = 800;
Aq = Asort(:,1:k);
问题#2
众所周知,协方差矩阵的特征向量等于主成分。具体来说,第一个主成分(即最大特征向量和相关的最大特征值)给出了数据中最大可变性的方向。接下来的每个主成分都会给出递减的可变性。值得注意的是,每个主成分都正交于其他主成分。
以下是维基百科上针对二维数据的良好示例:
我从上面链接的维基百科主成分分析文章中获取了上述图像。这是一个样本散点图,其分布符合以
(1,3)
为中心、在
(0.878, 0.478)
方向上标准差约为3,在正交方向上标准差为1的二元高斯分布。标准差为3的分量是第一主成分,而正交的分量是第二主成分。所示向量是协方差矩阵的特征向量,按相应特征值的平方根缩放,并移动到它们的尾部位于平均值处。
现在让我们回到你的问题。我们查看前
k
个最大的特征值的原因是进行
降维的一种方式。实质上,您将执行数据压缩,其中您将把高维数据投影到低维空间中。在投影中包括的主成分越多,它就越类似于原始数据。它实际上在某个点开始逐渐变得平稳,但前几个主成分允许您在很大程度上忠实地重构数据。
我曾经偶然发现了一个非常好的Quora帖子,其中提供了一个很棒的视觉示例,用于执行PCA(或SVD),并进行数据重建。
http://qr.ae/RAEU8a
第三题
您可以使用此矩阵将高维数据重新投影到较低维空间中。行数仍为1000,这意味着您的数据集中最初有1000个特征。将此矩阵视为从特征的原始维度(1000)向其降低的维度(800)的转换。
然后,您将与此矩阵一起使用重建原始数据。具体来说,这将为您提供最小误差下原始数据的近似值。在这种情况下,您不需要使用所有主成分(即只需使用k
个最大向量),并且可以使用比以前更少的信息创建您的数据的近似值。
如何重构数据非常简单。首先让我们讨论完整数据的正向和反向操作。正向操作是获取原始数据并重新投影,但我们将使用所有组件,而不是较低的维数。您首先需要进行均值减法处理的原始数据:
Bm = bsxfun(@minus, B, mean(B,1));
Bm
将产生一个矩阵,其中每个样本的每个特征都被减去了均值。bsxfun
允许在不等尺寸的情况下减去两个矩阵,前提是您可以广播维度,使它们匹配。具体而言,在这种情况下,将计算B
的每列/特征的平均值,并生成一个临时复制矩阵,其大小与B
一样大。当您用此复制矩阵减去原始数据时,效果将减去每个数据点及其各自特征的平均值,从而使您的数据分散,以便每个特征的平均值为0。
一旦完成此操作,进行投影的操作就非常简单:
Bproject = Bm*Asort
以上操作非常简单。您正在做的是将每个样本的特征表示为主成分的线性组合。例如,给定去中心化数据的第一个样本或第一行,投影域中第一个样本的特征是整个样本所涉及的行向量和第一个主成分(列向量)的点积。在投影域中,第一个样本的第二个特征是整个样本和第二个成分的加权和。您需要为所有样本和所有主成分重复此过程。实际上,您正在重新投影数据,使其相对于主成分——这是正交基向量,可以将您的数据从一种表示转换为另一种表示。
这里可以找到我刚才谈论的更好的描述。看看Amro的答案:
Matlab Principal Component Analysis (eigenvalues order)
现在要反向操作,您只需执行逆操作,但特征向量矩阵具有的一个特殊属性是,如果您对其进行转置,则会得到逆矩阵。要恢复原始数据,请撤消上述操作并将平均值添加回问题中:
out = bsxfun(@plus, Bproject*Asort.', mean(B, 1));
您想要获取原始数据,因此您正在解决相对于我之前执行的操作的
Bm
。然而,在这里,
Asort
的逆只是转置。在执行此操作后发生的情况是,您正在获取原始数据,但数据仍然是分散的。要获取原始数据,您必须将每个特征的平均值添加回数据矩阵中,以获得最终结果。这就是为什么我们在这里使用另一个
bsxfun
调用,以便您可以对每个样本的特征值执行此操作。
通过上面两行代码,您应该能够在原始域和投影域之间来回切换。现在,降维(或原始数据的近似)起作用的是反向操作。首先,您需要将数据投影到主成分的基上(即正向操作),但是现在要返回到原始域,在那里我们试图使用减少的主成分数量重构数据,您只需在上述代码中用
Aq
替换
Asort
,并且还要减少在
Bproject
中使用的特征数量。具体而言:
out = bsxfun(@plus, Bproject(:,1:k)*Aq.', mean(B, 1));
执行Bproject(:,1:k)
会选择在您的数据投影域中与k
个最大特征向量对应的k
个特征。有趣的是,如果您只想要关于降低维度的数据表示,您可以使用Bproject(:,1:k)
就足够了。然而,如果您想继续计算原始数据的近似值,我们需要进行反向计算。上述代码仅仅是我们之前在数据完整维度上使用的代码,但我们同时使用Aq
以及从Bproject
中选择出k
个特征。这将给出由矩阵中k
个最大特征向量/特征值表示的原始数据。
如果您想看到一个很棒的例子,我会模仿我给您链接的Quora帖子,但使用另一张图片。考虑使用灰度图像进行此操作,其中每行是“样本”,每列是特征。让我们使用图像处理工具箱中的摄影师图像。
im = imread('camerman.tif');
imshow(im);
我们得到这个图像:
这是一个256 x 256的图像,意味着我们有256个数据点,每个点有256个特征。我要做的是将图像转换成
double
,以便于计算协方差矩阵的精度。现在我要重复上述代码,但逐步增加
k
的值从3、11、15、25、45、65和125。因此,对于每个
k
,我们引入更多的主成分,应该会慢慢开始重建我们的数据。
下面是一些可运行的代码,用于说明我的观点:
clear all;
close all;
B = double(imread('cameraman.tif'));
sigma = cov(B);
[A,D] = eig(sigma);
vals = diag(D);
[~,ind] = sort(abs(vals), 'descend');
Asort = A(:,ind);
Bm = bsxfun(@minus, B, mean(B,1));
Bproject = Bm*Asort;
figure;
counter = 1;
for k = [3 11 15 25 45 65 125 155]
Aq = Asort(:,1:k);
out = bsxfun(@plus, Bproject(:,1:k)*Aq.', mean(B, 1));
subplot(4, 2, counter);
counter = counter + 1;
imshow(out,[]);
title(['k = ' num2str(k)]);
end
正如您所看到的,大部分代码与我们所见的相同。不同之处在于我循环遍历所有
k
的值,使用前
k
个特征向量将其投影回原始空间(即计算近似值),然后显示图像。
我们得到了这个漂亮的图像:
从 k=3
开始,并没有为我们带来什么好处...我们可以看到一些一般性结构,但增加更多的部件也不会有害。随着组件数量的增加,我们开始更清晰地看到原始数据的样貌。在 k=25
时,我们实际上可以完美地看到摄影师的样子,并且我们不需要26及其以后的部件来了解发生了什么。这就是我所谈论的数据压缩问题,您不需要处理所有的主要部件即可清晰地了解正在发生的情况。
我想通过引用Chris Taylor关于主成分分析的精彩阐述来结束这个注释,其中包括代码、图表和很好的解释!这是我开始学习PCA的地方,但Quora的帖子才巩固了我的知识。
Matlab - 多维数据的PCA分析和重构