经过本人复现分析,该篇博客代码不全且存在部分错误,思路仅供参考,望周知。
目录
主成分分析PCA
基于PCA的人脸识别算法
matlab代码
主成分分析PCA
主成分分析(Principal Component Analysis,简称PCA)是最常用的一种降维方法。
PCA的主要目的是找到一个超平面(直线的高维推广)对所有样本数据进行表达,让样本点到这个超平面的距离足够近(最近重构性),以及让样本点在这个超平面上的投影能尽可能地分开(最大可分性),目的是在用低维的数据量表达高维的数据量的同时尽可能地保留原数据的特征。
PCA的主要思想是将n维特征映射到k维的空间上,我们把这新形成的k维特征叫做主成分。
举个例子,我们看到上图是一个二维空间的散点图,图上的数据点反映了它们本身的一些特征,现在我们可以用一条直线,也就是用一维的超平面去把这些特征给表现处理,下图就找到了这么一条直线,使得每一个原数据在这一条直线上的投影之间距离最大,也就是说,在这个方向上它们本身的特征体现得比较好。
那么我们怎么样才能够实现PCA的降维呢?
我们首先对需要降维的样本数据进行去中心化处理,即让样本中的每一个数据都减去样本数据的均值,再通过计算数据矩阵的协方差矩阵,然后得到协方差矩阵的特征值和特征向量(这个过程又称为特征值分解),让特征向量按照特征值的大小从小到大进行排列,选择前k个特征向量组成矩阵,然后用这个矩阵的转置左乘协方差矩阵,得到的新矩阵就是降维后的数据了。
在这次的人脸识别项目中,我们使用了PCA来对人脸数据进行降维,下图是识别率与前K个主成分之间的关系图,我们仔细观察这两者之间的线性图,不难发现,很有趣的是识别率在取到约前20个主成分的时候已经达到饱和了,就算后面再增加主成分的数量,识别率也不会有多大的改变,这初步体现了降维的优点所在。
基于PCA的人脸识别算法
我们首先从人脸数据库中读取图片,并把图片转换为数据存在矩阵中,然后把每一张图片的矩阵拉成列向量,把所有列向量装在一个矩阵里面。
然后用PCA对这个矩阵进行降维,即让矩阵中的每一个数据都减去数据的均值,然后对新形成的矩阵求它的协方差矩阵,再对这个协方差矩阵进行特征值分解得到特征值和特征向量,让特征向量按照特征值的大小进行从大到小的顺序排列,然后取前k个特征向量组成一个矩阵,让这个矩阵的转置左乘原来的协方差矩阵,得到的新矩阵就是降维后的数据。
然后分别读取一定数量的列向量(即图片)作为训练集图片,读取一定数量的作为测试集图片。之后用测试集里面的一张图片和训练集里面每一张图片的数据做差取绝对值,然后把得到的这些绝对值按从小到大的顺序进行排列。
之后用k近邻学习(k-Nearest Neighbor,简称kNN),选择排列在前k个最小距离所对应的图片序号,选择出现次数最多的图片序号,如果没有重复出现的,那么选择距离最小的,即排列第一的,最后把识别出来的图片序号和原本图片所属的序号进行比较,如果相等,则识别成功,反之,则识别错误。
这样就完成了图片的识别。
matlab代码
K=1;Data=dir('C:\ORL56_46\*.bmp');%从地址中读取图片lengthf=length(Data);%读取图片的数量A=imread(Data(1).name);%逐张读入图片A=double(A);%改精度A=A(:);%将图片拉成列向量[m,~]=size(A);%读取列向量的长度traindata=zeros(m,0);trainlabel=zeros(1,0);trainnumber=5;%训练集数目testnumber=5;%测试集数目X=zeros(m,0);%读入所有图片数据并装在一个矩阵中for i=1:lengthf %重复读图循环的次数为图片的数量A=imread(Data(i).name);%逐张读图转为矩阵A=double(A);%将数据改为双精度浮点型A=A(:);%将矩阵拉成一维列向量X=[X,A];%将所有列向量(即所有图片)装在一个矩阵中end%PCA主程序X=X-ones(size(X,1),1)*mean(X);%去中心化c=X*X'/size(X,2);%求协方差矩阵[e,d]=eig(c);%特征值分解[dummy,order]=sort(diag(-d));%特征值从大到小排列e=e(:,order);%让特征向量按特征值排列顺序进行排列e=e(:,1:50);%取前k个主成分X=e'*X;%降维后的矩阵%读入训练集数据for j=1:10:lengthffor i=j:j+4%取前n张作为训练集数据A=X(:,i);A=double(A);A=A(:);traindata=[traindata,A];%将训练集数据存到矩阵trainlabel=[trainlabel,j];%存储每张图片的序号endendtestdata=zeros(m,0);testlabel=zeros(1,0);%读入测试集数据for j=1:10:lengthf for i=j+5:j+9%取后m张作为测试集数据A=X(:,i);A=double(A);A=A(:);testdata=[testdata,A];%将测试集数据存到矩阵中testlabel=[testlabel;j];%存储每张图片的序号endend%人脸识别主程序:将测试集里的图片和训练集里的每一张图片做差,用knn进行邻近分类[~,Z]=size(testdata);%读取测试集数据量error=0;for j=1:Z%对测试集里每一张图片进行以下操作[N,A]=size(traindata);%读取训练集数据量dist=zeros(A,1);%初始化矩阵用来装距离%测试集里的一张图片和训练集的所有图片做差for i=1:A Dist=0;for k=1:NDist=Dist+(testdata(k,j)-traindata(k,i))^2;enddist(i,1)=Dist;end[~,B]=sort(dist);%将距离排序C=[];%分出最短距离的图片所属序号for i=1:KQ=floor((B(i,1)-1)/trainnumber)*10+1;C=[C,Q];end[idx,M]=mode(C);%idx为众数,M为众数出现的次数%如果没有众数,那么取距离最小的if M==1 idx=C(1,1); end%如果两张图片序号不同,则识别失败if idx~=testlabel(j,1) error=error+1;endendrate=(Z-error)/Z;%计算识别率fprintf('%.2f%%\n',rate*100);