基本框架参考了 http://blog.youkuaiyun.com/hesays/article/details/39498375
部分不是很明白的代码还参考了 http://blog.youkuaiyun.com/on2way/article/details/42390149
有的地方发现别人做得好像不对,所以就去查了另外的文章,也不知道怎么就互相综合出了现在的结果。还是遗留了很多基本原理上的问题,并没有深究,因为一旦深究就停不下来了,问题越来越多。只是基本上,基本上能实现一点简单的功能了,所以就把这个很粗糙、很简陋的系统先放上来,以后再反观自己的学习过程应该会有另外的收获。
从MATLAB入门到搞出一个简单的人脸识别系统,原来并不需要很久=。=...且看本阶段成果——
毕业设计阶段性报告·PCA最简系统实现
完成时间:2015/03/28– 2015/03/31 总结时间:2015/04/04– 2015/04/05
一、原理框图及变量间的关系
1. 原理框图:
2. 变量关系(数据流向说明):
二、具体步骤(关键变量用黄色高亮标出,与代码中的变量名称对应)
0. 准备好数据库:下载ORL人脸数据库,40个人,每人10张,共400张图片,图像尺寸为112x92。人为地将数据库分为训练集和测试集:将每个人的第1~5幅图像作为训练集,用于学习每个人的特征;每个人的第6~10幅图像作为测试集,即作为系统的输入,逐个判断其类别。
(不用在文件夹里作任何实际的处理,只是对图片的两种用途心中有数就行。直接把数据库放到Matlab工作路径下,列表中显示s1, s2, s3…s40共40个文件夹)
1. 训练阶段:A~F
A. 读取训练集图片数据,按行存入training_set(M×N矩阵, M=200, N=112×92)
B. 计算training_set 每一列的均值,得到Mean (1×N矩阵)。将training_set 的每一行减去Mean,得到中心化的数据集X (M×N矩阵)
C. 计算协方差矩阵:Σ=XXT,记为Sigma(M×M矩阵)
D. 利用Matlab自带的函数eigs(),求出前k个特征值存入矩阵D(k×k对角方阵,对角元是特征值) 以及相应的特征向量V(M×k矩阵,每一列是一个特征向量)
[V,D]=eigs(Sigma,k);
其中k是可调的系统参数,这里暂定为k=15。这k个特征向量就是我们要找的低维空间(子空间)中的“基”,即所谓的“坐标系”。
E. 将X和特征向量矩阵V相乘,得到特征脸矩阵E(N×k矩阵,每一列代表一个特征脸,按照主成分的重要程度降序排列,从左至右,第一张脸代表最主要的数据分布方向。)
E=X’*V;
利用该特征脸矩阵,就可以对训练集、测试集进行降维了。15个特征脸显示如下:
F. 训练集降维:用原始训练集数据矩阵training_set与特征脸矩阵E相乘,得到降维后的训练集数据Train_E(M×k矩阵,比原来的M×N矩阵training_set维数大大降低)
Train_E=training_set*E;
2. 测试阶段:G~H
G. 测试集数据读取与降维:读取测试集数据,图片变为行向量,存入testing_set(M×N矩阵)。测试集数据矩阵testing_set与特征脸矩阵E相乘,得到降维后的测试集数据Test_E(M×k矩阵)
Test_E=testing_set*E;
H. 分类器设计与识别率计算:
①到上一步为止,降维后的训练集和测试集两组数据的形态是这样的:
②举例说明:将测试集数据Test_E逐行取出,每一行与Train_E的每一行求欧氏距离,认为它属于与之距离最近的那一类。例如,取出Test_E的第7行(测试集中第2个人的第2张图),与Train_E的所有行求距离后发现与第9行(训练集中第2个人的第4张图)最近,则判断属于第2个人(判断正确)
③更一般地,首先初始化correct=0,用于记录系统判断正确的“得分”。取出Test_E第i行(其实我知道它属于第“i/5向上取整”个人,记为true_class,但我假装不知道,拿去考验我设计的系统能否判断正确)。将第i行与Train_E的每一行求距离(记为error,1×200行向量)后,发现与第j行最近,于是系统判断这张测试图片属于第“j/5向上取整”个人,记为recog_class。如果true_class= recog_class,那么系统判断正确,加一分!
correct=correct+1;
④求出识别率:accuracy= correct/200;
运行程序后结果如下:
三、本阶段系统关键特点小结
1. 采用的是ORL的数据库,400个人,每人10张图片。人为地将数据库分为训练集(每个人的前5张图片)和测试集(每个人的后5张图片)。直接将原始图像的灰度值作为“降维的对象”,即所谓的“输入特征”
2. 按照PCA的原理,进行均值归零、计算协方差矩阵、算特征值和特征向量、得出特征脸后,利用特征脸分别将训练集和测试集降维(目前子空间维数k=15,还没尝试其它维数)。
3. 分类器用的是最简单的“最近邻原则”,将降维后的训练集/测试集数据比较欧氏距离后,最近的就认为是同类。
4. 目前的识别率是0.77
四、下一阶段的计划
1. 优化已有的最简系统:
(1)求协方差矩阵时,数据量庞大;可用“SVD原理”等方法,提升速度;对比优化前后的程序运行时间。
(2)目前子空间的维数选为k = 15,准确率达77%。尝试不同的k值,观察随着k的上升,准确率是如何达到饱和的;选择一个能较好地兼顾速度和准确率的k。
(3)分类器选用了最基本的“最近邻准则”,尝试别的分类器,例如三阶近邻,看能否提高准确率。
2. 选做:
(1)参考cai deng老师的主页,看算法或者代码本身有没有可以改进的地方。
(2)深入理解PCA:“人脸重建”?
3. 进阶研究:(中期检查之前)
(1)试用另一个人脸数据库(Yale),有比较明显的光照变化,感觉单纯的PCA会杯具
(2)选择不同的输入特征:HOG, LBP, …(也许可以克服光照的影响?)
(3)核心算法由PCA改为LDA会如何?
五、目前遗留的问题
1. Σ=XXT好像并不是协方差矩阵?
用Matlab自带的cov() 函数检验,发现协方差矩阵应该等于(XTX)/N呢
2. “前k个”特征向量,是指将特征值按绝对值大小降序排列后,取相应的前k个特征向量吗?
附录:完整代码(含注释)
%PCA test: calculate Mean,X,Sigma,and itseigen decomposition
close all; clear all; clc;
%训练模块:①~④
%========================①预处理:将训练集数据中心化========================%
training_set=[]; %创建一个空白数组,用于存放所有训练集图像
for i=1:40
for j=1:5 %只读取每个人的前五张图片作为训练图像
a=imread(strcat('s',num2str(i),'\',num2str(j),'.bmp'));
b=a(1:112*92); %将以上数据转换为1×N的行向量存入b,N=112×92=10304
%提取顺序是从上到下,从左到右
b=double(b); %强制数据类型转换,便于后面的运算操作
training_set=[training_set;b];
%每循环一次,就在原来的traing_set后面添加一行
%traing_set是一个M×N矩阵,每一行代表一张图片。M=40×5=200, N=10304
end
end
Mean=mean(training_set);
%计算平均图片,得到一个1×N的行向量
for i=1:200
X(i,:)=training_set(i,:)-Mean;
%每个图片的数据都减去均值,实现训练集数据的均值归零(中心化)
%减号两边的数据类型必须相同,原始数据的像素灰度值为整数,而平均值是浮点数,
...所以前面要转换。
%X是M×N矩阵,每一行保存的数据是:每个原始图片 -平均图片
end
figure;
%开个图形窗口
subplot(1,2,1),imshow(mat2gray(reshape(Mean,112,92)));title('Mean');
%窗口中,左图显示200张训练集的平均脸
subplot(1,2,2),imshow(mat2gray(reshape(X(1,:),112,92)));title('S1P1-Mean');
%窗口中,右图显示第一个人的第一张图片(记为s1p1)减去平均脸后的效果。
%==================②计算协方差矩阵及其前k个特征值、特征向量==================%
k=15; %暂时把维数选为15,即采用影响最大的15个特征
Sigma=X*X'; %计算“协方差矩阵”,M×M矩阵
[V,D]=eigs(Sigma,k); %计算“协方差矩阵”的前k个特征值和特征向量
%矩阵D是k×k对角矩阵,对角元是Sigma的前k个特征值
%V是M×k矩阵,每一列是一个特征向量,与D的每一列所含的那个特征值相互对应
%======================= ③计算k个特征脸,并显示出来 ========================%
E=X'*V; %计算特征脸矩阵E,每一列是一张特征脸,共k列
figure;
for i=1:15
subplot(3,5,i),imshow(mat2gray(reshape(E(:,i),112,92)));
title(strcat('k=',num2str(i)));
%显示第1~15个主成分的特征脸
end
%======================== ④利用特征脸,将训练集降维 ========================%
Train_E=training_set*E;
%训练集数据降维,得到M×k矩阵(M=200,k=15),每行代表一张图片
%原来一张图片需要用N=112*92个数字表示,现在用k个数字来代替
%测试模块:⑤~⑧
%================= ⑤读取测试集,并利用特征脸,将测试集降维 ==================%
testing_set=[]; %创建一个空白数组,用于存放所有测试集图像
for i=1:40
for j=6:10 %读取每个人的后五张图片作为测试图像
a=imread(strcat('s',num2str(i),'\',num2str(j),'.bmp'));
b=a(1:112*92); %将以上数据转换为1×N的行向量存入b,N=112×92=10304
%提取顺序是从上到下,从左到右
b=double(b); %强制数据类型转换,便于后面的运算操作
testing_set=[testing_set;b];
%每循环一次,就在原来的testing_set后面添加一行
%testing_set是一个M×N矩阵,每一行代表一张图片。M=40×5=200, N=10304
end
end
Test_E=testing_set*E;
%测试集数据降维,得到M×k矩阵(M=200,k=15),每行代表一张图片
%=============== ⑥比较降维后的训练集和样本集数据,得分类结果 ================%
correct=0; %初始化判断正确的人脸数
for i=1:200
for j=1:200
error(j)=norm(Test_E(i,:)-Train_E(j,:));
%将Test_E的第i行与Train_E的每一列求距离(即:相减,再取2-范数)
end
[errormin,I]=min(error);
%算得距离最小的是第I行
true_class=ceil(i/5); %实际上我知道当前这一行的数据是代表第几个人
recog_class=ceil(I/5); %而我让系统算出来是第几个人
if true_class==recog_class
correct=correct+1; %如果系统判断正确,那么得一分~
end
end
accuracy=correct/200 %输出正确率