本文使用matlab采用PCA完成对数据的降维、重构和人脸识别。
参考文章:http://blog.csdn.net/watkinsong/article/details/38536463
数据的维数过高,处理起来耗时又费力,于是就在想我能不能只处理部分维数,并且得到的结果与全部维数的结果一致。当当当,PCA就出炉了。简单来说,就是一个图片有2000个特征维度,而实际上只有其中100维(甚至更少),对结果的影响起着巨大的作用。
eg:对于皇帝来说,内阁首辅>二辅>三辅>四辅>>其他不知名官员。所以对于皇帝来说,整个内阁所提供的有效治国方略的所占比可以看作是60%,整个文官阶级可以看作是75%,武官阶级20%,平民百姓5%。也就是说虽然老百姓人挺多的,但是提供的治国方案很少,所以认为可以选择性忽略掉他们的提议。再其次,可以忽略武官、文官。。
总结一下就是,我们只关注影响最大的特征维度,放弃掉影响力不足的特征维度。
1、减去均值,中心化
2、计算协方差矩阵
3、选取特征值和特征向量
4、训练集转换到特征向量构成的向量空间中完成降维
5、测试集乘以特征向量的转置,再加上去中心化的均值以完成重构
6、识别:选取每个人的一张照片做登记记录,减去均值,乘以降维阵(即特征向量),并将记录集在降维阵中的值记录下来。遍历图片库,并对照片做同样的处理。取图片在降维阵中的值与记录集的值最小欧式距离的图片所属人,为该图片的所属分类。
%% 读入图片
clear ; close all; clc
%m = 1680; % number of samples
trainset = zeros(10, 50 * 40); % 图片大小 50 * 40
file_path = 'C:\Users\zyfls\Desktop\ML\第五章数据降维\数据\AR\AR\';% 图像文件夹路径
img_path_list = dir(strcat(file_path,'*.bmp'));%获取该文件夹中所有bmp格式的图像
img_num = length(img_path_list);%获取图像总数量
for i = 10: img_num %取出去前十张照片之外做为训练集,前十张作为测试
image_name = img_path_list(i).name;% 图像名
end
%% before training PCA, do feature normalization
mu = mean(trainset);%mean函数用来求 沿数组中不同维的元素的平均值。
trainset_norm = bsxfun(@minus, trainset, mu);%训练集减去平均值
sigma = std(trainset_norm); %std 计算标准差
trainset_norm = bsxfun(@rdivide, trainset_norm, sigma); %trainset_norm 点除 sigma(标准差)
%% we could save the mean face mu to take a look the mean face
imwrite(uint8(reshape(mu, 50, 40)), 'C:\Users\zyfls\Desktop\ML各种截图\5\乱七八糟PCA\meanface.bmp');
fprintf('mean face saved. paused\n');
%% 计算降维阵
X = trainset; % just for convience
[m, n] = size(X);
U = zeros(n);
S = zeros(n);
Cov = (1 / m) * X' * X; %计算协方差矩阵
[U, S, V] = svd(Cov);%奇异值分解,返回一个与X 同大小的对角矩阵S,两个正交矩阵U 和V,且满足= U*S*V'。若A 为m×n 阵,则U 为m×m 阵,V为n×n 阵。奇异值在S 的对角线上,非负且按降序排列。
%使用SVD可以对非方阵进行PCA处理,下面注释的内容可以处理方阵
E = diag(S);
contribution = cumsum(E)./sum(E);%计算贡献率
% [U,D]=eig(Cov); %计算矩阵R的特征向量矩阵V和特征值矩阵D,特征值由小到大
% U=(rot90(U))'; %将特征向量矩阵U从大到小排序
% D=rot90(rot90(D)); %将特征值矩阵由大到小排序
% E=diag(D); %将特征值矩阵转换为特征值向量
% ratio=0; %累计贡献率
% for k=1:n
% r=E(k)/sum(E); %第k主成份贡献率
% ratio=ratio+r; %累计贡献率
% if(ratio>=0.9) %取累计贡献率大于等于90%的主成分
% break;
% end
% end
fprintf('compute cov done.\n');
%降维矩阵U中的特征向量, 在关于人脸的降维中,又被称为特征脸, U 中的每个特征向量相当于找到的降维空间的一个方向。 利用U可以将特征映射到这个空间中。
%% 显示特征脸 U的前十项
for i = 1:10
ef = U(:, i);
img = ef;
minVal = min(img);
img = img - minVal;
max_val = max(abs(img));
img = img / max_val;
img = reshape(img, 50, 40);
imwrite(img, strcat('C:\Users\zyfls\Desktop\ML各种截图\5\乱七八糟PCA\','eigenface', int2str(i), '.bmp'));
end
fprintf('eigen face saved, paused.\n');
pause;
%% dimension reduction
k = 100; % reduce to 100 dimension
test = zeros(10, 50 * 40);
file_path = 'C:\Users\zyfls\Desktop\ML\第五章数据降维\数据\AR\AR\';% 图像文件夹路径
img_path_list = dir(strcat(file_path,'*.bmp'));%获取该文件夹中所有bmp格式的图像
for i = 1:10 %前十个测试集
image_name = img_path_list(i).name;% 图像名
img = imread(strcat(file_path,image_name));
%img = imread(strcat('C:\Users\zyfls\Desktop\ML各种截图\5\', int2str(i), '.bmp'));
img = double(img);
test(i, :) = img(:);
end
% test set need to do normalization
test = bsxfun(@minus, test, mu);
% reduction 降维
Uk = U(:, 1:k); %取从1到dimsion的特征向量作为降维空间
Z = test * Uk;
fprintf('reduce done.\n');
%% 测试集重构
%% for the test set images, we only minus the mean face,
% so in the reconstruct process, we need add the mean face back
Xp = Z * Uk';
% show reconstructed face
for i = 1:10
face = Xp(i, :);
%face = face .* sigma;
face = face + mu;
face = reshape((face), 50, 40);
imwrite(uint8(face), strcat('C:\Users\zyfls\Desktop\ML各种截图\5\乱七八糟PCA\','reconstructionface', int2str(i), '.bmp'));
Face_re(i,:)=Xp(i,:)+mu;
end
e = Face_re-test;
error(1,i)=norm(e);
%dispaly error rate
error_rate=error(1,i);
display(error_rate); %1.9061e+04
%训练集的重构,因为训练集多除了个sigma矩阵这里再乘回来
%% for the train set reconstruction, we minus the mean face and divide by standard deviation during the train
% so in the reconstruction process, we need to multiby standard deviation first,
% and then add the mean face back
trainset_re = trainset_norm * Uk; % reduction
trainset_re = trainset_re * Uk'; % reconstruction
for i = 11:25
train = trainset_re(i, :);
train = train .* sigma;
train = train + mu;
train = reshape(train, 50, 40);
imwrite(uint8(train), strcat('C:\Users\zyfls\Desktop\ML各种截图\5\乱七八糟PCA\', 'reconstruction',int2str(i), 'train.bmp'));
end
以上代码完成降维和重构:
重构图像:
以上部分完成了降维、重构。
识别代码如下(含降维和重构):
%% 读入图片
clear ; close all; clc
%m = 1680; % number of samples
trainset = zeros(10, 50 * 40); % 图片大小 50 * 40
file_path = 'C:\Users\zyfls\Desktop\ML\第五章数据降维\数据\AR\AR\';% 图像文件夹路径
img_path_list = dir(strcat(file_path,'*.bmp'));%获取该文件夹中所有bmp格式的图像
img_num = length(img_path_list);%获取图像总数量
j=1;
for i = 1: img_num %取所有照片做为训练集
if(mod(i,14) == 0)%每个人的最后一张留下来做测试集
continue;
end
image_name = img_path_list(i).name;% 图像名
% name = image_name(1:3);
% if strcmp(name,'001')
img = imread(strcat(file_path,image_name));
img = double(img);
trainset(j, :) = img(:);
j=j+1;
% end
end
%% before training PCA, do feature normalization
mu = mean(trainset);%mean函数用来求 沿数组中不同维的元素的平均值。
trainset_norm = bsxfun(@minus, trainset, mu);%训练集减去平均值
sigma = std(trainset_norm); %std 计算标准差
trainset_norm = bsxfun(@rdivide, trainset_norm, sigma); %trainset_norm 点除 sigma(标准差)
%% we could save the mean face mu to take a look the mean face
imwrite(uint8(reshape(mu, 50, 40)), 'C:\Users\zyfls\Desktop\ML各种截图\5\Recognition\meanface.bmp');
%% 计算降维阵
X = trainset; % just for convience
[m, n] = size(X);
U = zeros(n);
S = zeros(n);
Cov = (1 / m) * X' * X; %计算协方差矩阵
[U, S, V] = svd(Cov);%奇异值分解,返回一个与cov 同大小的对角矩阵S,两个正交矩阵U 和V,且满足= U*S*V'。若A 为m×n 阵,则U 为m×m 阵,V为n×n 阵。奇异值在S 的对角线上,非负且按降序排列。
%使用SVD可以对非方阵进行PCA处理
E = diag(S);
contribution = cumsum(E)./sum(E);%计算贡献率
fprintf('compute cov done.\n');
%降维矩阵U中的特征向量, 在关于人脸的降维中,又被称为特征脸, U 中的每个特征向量相当于找到的降维空间的一个方向。 利用U可以将特征映射到这个空间中。
%% 显示特征脸 U的前十项
for i = 1:10
ef = U(:, i);
img = ef;
minVal = min(img);
img = img - minVal;
max_val = max(abs(img));
img = img / max_val;
img = reshape(img, 50, 40);
imwrite(img, strcat('C:\Users\zyfls\Desktop\ML各种截图\5\Recognition\','eigenface', int2str(i), '.bmp'));
end
fprintf('eigen face saved, paused.\n');
pause;
j=1;
regis = zeros(120,50*40);
for i = 1:14: img_num %取每组人的第一张照片做登记记录集
image_name = img_path_list(i).name;% 图像名
% name = image_name(1:3);
% if strcmp(name,'001')
img = imread(strcat(file_path,image_name));
img = double(img);
regis(j, :) = img(:);
j=j+1;
% end
end
regis = bsxfun(@minus,regis,mu);
Uk = U(:, 1:100); %取从1到100的特征向量作为降维空间
Zregis = regis * Uk;%记录登记记录集在降维阵中的值
%% dimension reduction
k = 100; % reduce to 100 dimension
test = zeros(1680, 50 * 40);
file_path = 'C:\Users\zyfls\Desktop\ML\第五章数据降维\数据\AR\AR\';% 图像文件夹路径
img_path_list = dir(strcat(file_path,'*.bmp'));%获取该文件夹中所有bmp格式的图像
success = 0;
for i = 1: img_num %遍历每张照片将其与登记记录比较,进行分类
image_name = img_path_list(i).name;% 图像名
img = imread(strcat(file_path,image_name));
%img = imread(strcat('C:\Users\zyfls\Desktop\ML各种截图\5\', int2str(i), '.bmp'));
img = double(img);
test(i, :) = img(:);
test(i,:) = test(i,:) - mu;
Uk = U(:, 1:100); %取从1到dimsion的特征向量作为降维空间
Ztest = test * Uk;%测试集在降维阵中的值
for j=1:120
mdist(j)=norm(Ztest(i,:)-Zregis(j,:));%计算与登记记录的距离
end
[C,I] = min(mdist);%返回最小的距离,及其位置
if(I<10)
I = num2str(I);
I = strcat('00',I);
elseif (I<100)
I = num2str(I);
I = strcat('0',I);
else
I = num2str(I);
end
name = image_name(1:3);%取当前图片的前三位用于判断是否分类正确
if strcmp(name,I)
success = success + 1;
end
end
suc_rate = success/1680;
这样就完成了整个PCA的降维、重构和识别,终于完成大作业了。
下一篇会介绍一下SR(稀疏字典)识别。