我们要怎样计算标准差(Standard Deviation)呢?标准差(SD)的英文定义是,“The average distance from the mean of the data set to a point(数据集中每个点到均值的平均距离)”。标准差的计算方式是,首先计算每个点到样本均值的距离的平方,然后进行累加,最后将累加的结果除以 n-1 再开根号,如下公式所示:
【协方差】就是这样的一种测量指标。协方差通常用来测量2维的数据集。如果你计算的协方差中的2个维度都是同一个变量,此时得到的就是方差。(协方差正常处理的是二维数据,维数大于2的时候,就用协方差矩阵表示)因此,如果有一组三维数据集(x, y, z),那么我们需要分别计算x维度和y维度,x维度和z维度,y维度和z维度的协方差。计算x维度和x维度,y维度和y维度,z维度和z维度的协方差就相当于分别计算x,y,z维度的方差(这就是两个维度都是同一个变量的情况)。
协方差的公式和方差的公式很相似,方差的公式(度量各个维度偏离其均值的程度)如下所示:
其中,作者仅仅是拓展了平方项变成两个部分(如, 表示成 * )。因此,协方差的公式如下:
上述两个公式(方差和标准差)除了分子中的第二个括号内将X用Y代替以为,其他都一样。用英文定义就是,“For each data item, multiply the different between the x value and the mean of x, by the difference between the y value and the mean of y. Add all these up, and divide by (n -1)”(对于X和Y的每个数据项, 与(变量X的均值)的差乘以 与 (变量Y的均值)的差,然后将它们累加起来后除以 (n-1) )。
值得注意的是,此处求解得到的这些特征向量都是单位特征向量,即,这两个特征向量的模都为1。这一点对于PCA是非常重要的,幸运的是,大多数数学工具包求解特征向量时,得到的结果都是单位特征向量。 那么,这又是什么意思呢?如下图所示为标准化后的数据集(各维变量减去对应的均值),读者们可以发现这些数据中存在一个明显的模式(quite a strong pattern)。正如协方差矩阵所预期的一样,x和y两组变量的值呈正相关(同增同减)。在图的上方,作者也画出了对应的两个特征向量所表示的直线方程,它们以虚线的形式出现于坐标系的对角线。正如前面提到的一样,这一组特征向量相互垂直(正交),分别是图中斜率大于0以及斜率小于0的这两条虚线。但更重要的是,特征向量为我们提供了数据模式(此处的模式就是直线方程)的相关信息。首先让我们先关注这条经过数据集(标准化后)中心的特征向量对应的直线(斜率小于0那条直线),看起来像画了一条最拟合的直线吗(很明显不是)?这个特征向量告诉我们这两组数据集(应该指的是这两维的数据,x-y)是如何关联到这条直线。(这就是我们要找的直线,数据模式)而第二个特征向量为了我们提供了另一条直线(另一种数据模式),斜率大于0的那条线,坐标上所有的点沿着这条主线分布,但都以一定程度的距离偏离在这条主线的两侧。
通常来说,一旦从协方差矩阵中求解得到特征向量,下一步就是从大到小对特征值进行排序(此处要对应好特征向量和特征值),也就是对数据成分主次的排序(特征值越大,数据在该维就越有意义,信息量越大)。现在,如果读者们有需求的话,就可以忽略那些成分较小的维度。虽然你丢失了一些数据信息,但是忽略的这些维度的特征值很小,丢失的信息很少。如果你忽略一些成分(维度),最终得到的数据集的维数将比原始数据集的维数少(这就是数据降维)。(To be precise)确切地讲就是,如果你处理的原始数据集是n维,你将计算得到n个特征向量和特征值(有对应关系),然后你选择特征值前p(p < n)大的特征向量,忽略掉其他维度的数据,最后得到的数据集就是一组p维的数据集。
第二种情况(只选择最大特征值对应的特征向量):如下图所示为降维后的数据集(从2维降到1维,和预期的一样)。读者们如果将这组数据与第一种情况的数据作比较,你会发现这组数据就是第一种情况那组数据的第一列,即对应 x 维的数据。
所以,如果你想对这组数据作图,如下图所示,得到的就是一维空间,而且图上 y=0 直线附近的这些坐标点恰好是 x 维空间上的位置(此时得到的坐标系,相当于y=0,主成分都在x轴上的数据)。我们已经抛掉另一维空间信息(y维),也就是另一个特征向量(值较小的那个特征向量被抛弃了)。
那么,到这里我们完成了什么呢?我们基本上已经对数据进行了变换,使得数据表达成它们之间的模式,本例中这些模式是“描述这些数据之间的关系的最拟合的直线”。这是非常有用的,因为我们现在已经将数据“根据这些直线(也就是特征向量eigenvector)对原始数据贡献信息的程度”进行组合分类。最初,我们有 x 和 y 坐标轴(完整的数据信息),但每个数据点的 x 值和 y 值实际上并不能确切告诉我们这个数据跟其他数据之间的关系(y值提供的信息量很少)。现在(降维后),数据点的坐标值(只有 x 值)告诉我们这个数据点适合于哪条趋势线(trend lines)。本例中,两个eigenvector都使用(转换)的情况下,我们仅仅简单的改变了数据,用求解得到的两个特征向量(eigenvectors)代替原本的 x-y 坐标系而已。但是,将特征值较小(y维,贡献小)的那一维去掉,只保留与特征值较大的那一维(x维)相关的数据的这种情况,就达到很好的降维效果,而且在丢失少量数据信息的情况下,用一维就能够很好的表示原始数据。
那么,我们该如何恢复原始数据呢?在这之前,读者们应该知道只有在“将所有特征向量(eigenvectors)共同构成最终的变换矩阵”这种情况下,我们才能精确的恢复原始数据。如果最终的变换矩阵已经是经过降维(丢掉一些特征向量,比如上个例子中的 y 轴被丢弃),那么原始数据已经是丢失掉一些信息了,尽管是少量的信息。
本章将对“PCA在计算机视觉中的应用”进行概述。首先介绍图像通常是怎样表示的,然后介绍PCA能够帮助我们对这些图像做什么样的处理。在本章中,关于“面部识别(facial recognition)”的相关信息是来自于“Face Recognition: Eigenface, Elastic Matching, and Neural Nets”, Jun Zhang et al. Proceedings of the IEEE, Vol. 85, No. 9, September 1997。 表征信息来自于“Digital Image Processing”, Rafael C. Gonzalez and Paul Wintz, Addison-Wesley Publishing Company, 1987(这同样是关于“一般情况下,K-L变换更进一步的知识”一篇的很好的参考文献)。图像压缩的相关资料来自于“http://www.vision.auc.dk/ sig/Teaching/Flerdim/Current/hotelling/hotelling.html”(又进不去0- -,原文是2002年的,可能有些网址已经发生了改变,后续有找到的话,再更新分享给大家),这个参考网址也为我们提供一些关于“使用不同数量的eigenvector的图像重建”的案例。
% This macro taken from
% http://www.cs.montana.edu/?harkin/courses/cs530/scilab/macros/cov.sci
% No alterations made
% Return the covariance matrix of the data in x, where each column of x
% is one dimension of an n-dimensional data set. That is, x has x columns
% and m rows, and each row is one sample.
%
% For example, if x is three dimensional and there are 4 samples.
% x = [1 2 3;4 5 6;7 8 9;10 11 12]
% c = cov (x)
function [c]=cov (x)
% Get the size of the array
sizex=size(x);
% Get the mean of each column
meanx = mean (x, ‘r‘);
% For each pair of variables, x1, x2, calculate
% sum ((x1 - meanx1)(x2-meanx2))/(m-1)
for var = 1:sizex(2),
x1 = x(:,var);
mx1 = meanx (var);
for ct = var:sizex (2),
x2 = x(:,ct);
mx2 = meanx (ct);
v = ((x1 - mx1)‘ * (x2 - mx2))/(sizex(1) - 1);
cv(var,ct) = v;
cv(ct,var) = v;
% do the lower part of c also.
end
end
c=cv;
% This a simple wrapper function to get just the eigenvectors
% since the system call returns 3 matrices
function [x]=justeigs (x)
% This just returns the eigenvectors of the matrix
[a, eig, b] = bdiag(x);
x= eig;
% this function makes the transformation to the eigenspace for PCA
% parameters:
% adjusteddata = mean-adjusted data set
% eigenvectors = SORTED eigenvectors (by eigenvalue)
% dimensions = how many eigenvectors you wish to keep
%
% The first two parameters can come from the result of calling
% PCAprepare on your data.
% The last is up to you.
function [finaldata] = PCAtransform(adjusteddata,eigenvectors,dimensions)
finaleigs = eigenvectors(:,1:dimensions);
prefinaldata = finaleigs‘*adjusteddata‘;
finaldata = prefinaldata‘;
% This function does the preparation for PCA analysis
% It adjusts the data to subtract the mean, finds the covariance matrix,
% and finds normal eigenvectors of that covariance matrix.
% It returns 4 matrices
% meanadjust = the mean-adjust data set
% covmat = the covariance matrix of the data
% eigvalues = the eigenvalues of the covariance matrix, IN SORTED ORDER
% normaleigs = the normalised eigenvectors of the covariance matrix,
% IN SORTED ORDER WITH RESPECT TO
% THEIR EIGENVALUES, for selection for the feature vector.
%
% NOTE: This function cannot handle data sets that have any eigenvalues
% equal to zero. It’s got something to do with the way that scilab treats
% the empty matrix and zeros.
function [meanadjusted,covmat,sorteigvalues,sortnormaleigs] = PCAprepare (data)
% Calculates the mean adjusted matrix, only for 2 dimensional data
means = mean(data,‘r‘);
meanadjusted = meanadjust(data);
covmat = cov(meanadjusted);
eigvalues = spec(covmat);
normaleigs = justeigs(covmat);
sorteigvalues = sorteigvectors(eigvalues‘, eigvalues‘);
sortnormaleigs = sorteigvectors(eigvalues‘, normaleigs);
% This removes a specified column from a matrix
% A = the matrix
% n = the column number you wish to remove
function [columnremoved] = removecolumn(A,n)
inputsize = size(A);
numcols = inputsize(2);
temp = A(:,1:(n-1));
for var = 1:(numcols - n)
temp(:,(n+var)-1) = A(:,(n+var));
end,
columnremoved = temp;
% This finds the column number that has the
% highest value in it’s first row.
function [column] = highestvalcolumn(A)
inputsize = size(A);
numcols = inputsize(2);
maxval = A(1,1);
maxcol = 1;
for var = 2:numcols
if A(1,var) > maxval
maxval = A(1,var);
maxcol = var;
end,
end,
column = maxcol
% This sorts a matrix of vectors, based on the values of
% another matrix
%
% values = the list of eigenvalues (1 per column)
% vectors = The list of eigenvectors (1 per column)
%
% NOTE: The values should correspond to the vectors
% so that the value in column x corresponds to the vector
% in column x.
function [sortedvecs] = sorteigvectors(values,vectors)
inputsize = size(values);
numcols = inputsize(2);
highcol = highestvalcolumn(values);
sorted = vectors(:,highcol);
remainvec = removecolumn(vectors,highcol);
remainval = removecolumn(values,highcol);
for var = 2:numcols
highcol = highestvalcolumn(remainval);
sorted(:,var) = remainvec(:,highcol);
remainvec = removecolumn(remainvec,highcol);
remainval = removecolumn(remainval,highcol);
end
sortedvecs = sorted;
% This takes a set of data, and subtracts
% the column mean from each column.
function [meanadjusted] = meanadjust(Data)
inputsize = size(Data);
numcols = inputsize(2);
means = mean(Data,‘r‘);
tmpmeanadjusted = Data(:,1) - means(:,1);
for var = 2:numcols
tmpmeanadjusted(:,var) = Data(:,var) - means(:,var);
end
meanadjusted = tmpmeanadjusted
附录B:本文相关的Matlab作图代码
1. 【协方差部分】H和M的关系图
%% ctrl + R 注释
%% ctrl + T 取消注释
close all;
clc;
clear;
fontsize = 11;
figure;
% x = 0:0.00025:1;
% y = x.*sin(10*3.14.*x)+2;
% plot(x,y)
x = [9 15 25 14 10 18 0 16 5 19 16 20];
y = [39 56 93 61 50 75 32 85 42 70 66 80];
plot(x, y, ‘o‘);
xlabel({‘H / 小时‘}, ‘FontSize‘,fontsize);
ylabel({‘M / 分数‘}, ‘FontSize‘,fontsize);
h = legend([‘H(学习时间)和M(获得分数)的关系‘, sprintf(‘\n‘)], ‘location‘, ‘best‘);
grid on;
set(h, ‘Box‘, ‘off‘);