1. 程式人生 > >基於PCA與BP神經網路的人臉識別

基於PCA與BP神經網路的人臉識別

基於PCA與BP神經網路的人臉識別

引言

前面的特徵提取部分採用的是PCA,後面的識別分類是採用的BP神經網路。

1、PCA演算法

演算法大致步驟:
設有m條n維資料。
1)將原始資料按列組成n行m列矩陣X;
2)將X的每一行(這裡是圖片也就是一張圖片變換到一行)進行零均值化,即減去這一行的均值(樣本中心化和標準化);
將所有的樣本融合到一個矩陣裡面特徵向量就是變換空間的基向量U=[u1,u2,u3,u4,…],腦袋裡面要想到一個樣本投影變換就是該空間的一個點,然後對於許多點可以用KNN等不同的方法進行分類。

3)求出協方差矩陣 C = 1 m X X T

C=\frac {1 }{m } XX^T
4)求出協方差矩陣的特徵值及對應的特徵向量;
5)將特徵向量按對應特徵值大小從上到下按行排列成矩陣,取前k行組成矩陣P;
6) Y = P X Y=PX
即為降維到 k k 維後的資料。
  對資料進行中心化預處理,這樣做的目的是要增加基向量的正交性,便於高維度向低緯度的投影,即便於更好的描述資料。
  對資料標準化的目的是消除特徵之間的差異性,當原始資料不同維度上的特徵的尺度不一致時,需要標準化步驟對資料進行預處理,使得在訓練神經網路的過程中,能夠加速權重引數的收斂。
  過中心化和標準化,最後得到均值為0,標準差為1的服從標準正態分佈的資料。
  求協方差矩陣的目的是為了計算各維度之間的相關性,而協方差矩陣的特徵值大小就反映了變換後在特徵向量方向上變換的幅度,幅度越大,說明這個方向上的元素差異也越大(越有投影的必要,矩陣相乘的過程就是投影),故而選取合適的前k個能以及小的損失來大量的減少元資料的維度。
在這裡插入圖片描述

2、PCA原理推導

基於K-L展開的PCA特徵提取:

3、神經網路

參考部落格

4、matlab程式碼

使用的是ORL人臉庫,40個人,一半做訓練一半做測試。
fastPCA.m

function [pcaA V] = fastPCA( A, k )
% 快速PCA
%
% 輸入:A --- 樣本矩陣,每行為一個樣本
%       k --- 降維至 k 維
% 輸出:pcaA --- 降維後的 k 維樣本特徵向量組成的矩陣,每行一個樣本,列數 k 為降維後的樣本特徵維數
%       V --- 主成分向量
[r c] = size(A);

% 樣本均值
meanVec = mean(A);

% 計算協方差矩陣的轉置 covMatT
Z = (A-repmat(meanVec, r, 1));
covMatT = Z * Z';

% 計算 covMatT 的前 k 個本徵值和本徵向量
[V D] = eigs(covMatT, k);

% 得到協方差矩陣 (covMatT)' 的本徵向量
V = Z' * V;

% 本徵向量歸一化為單位本徵向量
for i=1:k
    V(:,i)=V(:,i)/norm(V(:,i));
end

% 線性變換(投影)降維至 k 維
pcaA = Z * V;

% 儲存變換矩陣 V(10304*49) 和變換原點 meanVec(1*10304)(均值)
% 這裡就是PCA部分訓練得到的資料,儲存後面測試的時候依然往這個空間投影2018.11.29_FB
save('Mat/PCA.mat', 'V', 'meanVec');

ReadFaces.m

function [imgRow,imgCol,FaceContainer,faceLabel]=ReadFaces(nFacesPerPerson, nPerson, bTest)
% 讀入ORL人臉庫的指定數目的人臉前前五張(訓練)
%
% 輸入:nFacesPerPerson --- 每個人需要讀入的樣本數,預設值為 5
%       nPerson --- 需要讀入的人數,預設為全部 40 個人
%       bTest --- bool型的引數。預設為0,表示讀入訓練樣本(前5張);如果為1,表示讀入測試樣本(後5張)
%
% 輸出:FaceContainer --- 向量化人臉容器,nPerson(200) * 10304 的 2 維矩陣,每行對應一個人臉向量

if nargin==0 %default value
    nFacesPerPerson=5;%前5張用於訓練
    nPerson=40;%要讀入的人數(每人共10張,前5張用於訓練)
    bTest = 0;
elseif nargin < 3
    bTest = 0;
end

img=imread('Data/sample/S1/1.pgm');%為計算尺寸先讀入一張
[imgRow,imgCol]=size(img);

%構建40人*前5張共200行,imgRow*imgCol列的全零矩陣
FaceContainer = zeros(nFacesPerPerson*nPerson, imgRow*imgCol);
%構建200行,一列全零矩陣
faceLabel = zeros(nFacesPerPerson*nPerson, 1);

% 讀入訓練資料
for i=1:nPerson    %40人
    i1=mod(i,10); % 個位,%該函式用於進行取模(取餘)運算
    i0=char(i/10);% 將數轉成字串
%2018.11.29_FB
% num2str()是將數轉成文字,eg. num2str(43)得到的將是'43',是兩個字元
% char是按照ascii碼錶將數字對映成字元,char(43)得到的將是‘+’,(加號的ascii碼是43)
   if bTest == 0 % 讀入訓練資料   %bTest為匯入訓練樣本還是測試樣本的flag
        strPath='Data/sample/S';
   else
        strPath='Data/test/S';
   end
    if( i0~=0 )  %組建圖片的檔名!
        strPath=strcat(strPath,'0'+i0);
    end
    strPath=strcat(strPath,'0'+i1);
    strPath=strcat(strPath,'/');
    tempStrPath=strPath;
    for j=1:nFacesPerPerson   %訓練資料集的五張圖片nFacesPerPerson = 5
        strPath=tempStrPath;
        
        if bTest == 0 % 讀入訓練資料
            strPath = strcat(strPath, '0'+j);
        else
            % 讀入測試資料,這裡把數字5改為nFacePerPreson維護性更好2018.11.29_FB
            strPath = strcat(strPath, num2str(5+j));
        end
        
        strPath=strcat(strPath,'.pgm');
        img=imread(strPath);%具體讀入單張圖片
       
        %把讀入的影象按列儲存為行向量放入向量化人臉容器faceContainer的對應行中2018.11.29_FB
        FaceContainer((i-1)*nFacesPerPerson+j, :) = img(:)';
        %類別標籤111112222233333...(五個為一個類別,40個類別)
        faceLabel((i-1)*nFacesPerPerson+j) = i;
    end % j
end % i

% 儲存人臉樣本矩陣FaceMat(200*10304)每一行為一個樣本
save('Mat/FaceMat.mat', 'FaceContainer')

scaling.m 歸一化

function [SVFM, lowVec, upVec] = scaling(VecFeaMat, bTest, lRealBVec, uRealBVec)
% Input:  VecFeaMat --- 需要scaling的 m*n 維資料矩陣,每行一個樣本特徵向量,列數為維數
%         bTest ---  =1:說明是對於測試樣本進行scaling,此時必須提供 lRealBVec 和 uRealBVec
%                       的值,此二值應該是在對訓練樣本 scaling 時得到的
%                    =0:預設值,對訓練樣本進行 scaling
%         lRealBVec --- n維向量,對訓練樣本 scaling 時得到的各維的實際下限資訊
%         uRealBVec --- n維向量,對訓練樣本 scaling 時得到的各維的實際上限資訊
%
% output: SVFM --- VecFeaMat的 scaling 
%         upVec --- 各維特徵的上限(只在對訓練樣本scaling時有意義,bTest = 0)
%         lowVec --- 各維特徵的下限(只在對訓練樣本scaling時有意義,bTest = 0)
if nargin < 2   
    %函式輸入引數個數的意思,當輸入引數少於2個時,說明是訓練階段,故將bTest置為零,2018.11.29_FB 
    %nargin輸入引數個數,nargout輸出引數個數
    bTest = 0;
end

% 縮放目標範圍[-1, 1]
lTargB = -1;
uTargB = 1;

%pcaFaces(200*49)
[m n] = size(VecFeaMat);

SVFM = zeros(m, n);

if bTest
    if nargin < 4
        error('To do scaling on testset, param lRealB and uRealB are needed.')
    end
    
    if nargout > 1  %nargout輸出引數個數
        error('When do scaling on testset, only one output is supported.')
    end

    for iCol = 1:n  %n為pcaFaces  列數   k=49
        if uRealBVec(iCol) == lRealBVec(iCol)  %實際上限資訊 = 實際下限資訊
            SVFM(:, iCol) = uRealBVec(iCol);
            SVFM(:, iCol) = 0;
        else
            %lTargB = -1;  uTargB = 1;
            SVFM(:, iCol) = lTargB  +  ( VecFeaMat(:, iCol) - lRealBVec(iCol) ) / ( uRealBVec(iCol)-lRealBVec(iCol) ) * (uTargB-lTargB); % 測試資料的scaling
        end
    end
else
    
    %訓練階段的scaling歸一化
    upVec = zeros(1, n);%  歸一化  各個特徵的上下限訓練後需要儲存的向量
    lowVec = zeros(1, n);

    for iCol = 1:n
        lowVec(iCol) = min( VecFeaMat(:, iCol) );  %就是選取一個最小值
        upVec(iCol) = max( VecFeaMat(:, iCol) );
        if upVec(iCol) == lowVec(iCol)
            SVFM(:, iCol) = upVec(iCol);      
            SVFM(:, iCol) = 0;
        else
            SVFM(:, iCol) = lTargB  +  ( VecFeaMat(:, iCol) - lowVec(iCol) ) / ( upVec(iCol)-lowVec(iCol) ) * (uTargB-lTargB); % 訓練資料的scaling
        end
    end
end

FastBPfacedetection.m

function FastBPfacedetection()
%% 第一步,讀入資料
global imgRow;
global imgCol;
global net

display(' ');
display(' ');
display('訓練開始...');

nPerson=40;
nFacesPerPerson = 5;
display('讀入人臉資料...');
[imgRow,imgCol,FaceContainer,faceLabel]=ReadFaces(nFacesPerPerson,nPerson);
nFaces=size(FaceContainer,1);%樣本(人臉)數目(40*5=200個人臉)

display('PCA降維...');
k=49;   %前 k 個本徵值和本徵向量
[pcaFaces, W] = fastPCA(FaceContainer, k); % 主成分分析PCA
% pcaFaces是200*49的矩陣, 每一行代表一張主成分臉(共40人,每人5張),每個臉49維特徵
% W是分離變換矩陣, 10304*49 的矩陣
visualize_pc(W);%顯示主成分臉


X = pcaFaces;

display('歸一化開始...');
display('.........');

[X,A0,B0] = scaling(X);
save('Mat/scaling.mat', 'A0', 'B0');
% 儲存 scaling 後的訓練資料至 trainData.mat
TrainData = X;
trainLabel = faceLabel;   %在ReadFaces.m裡面定義,人臉類別標籤200*1(期望輸出)
save('Mat/trainData.mat', 'TrainData', 'trainLabel');
display('歸一化完成...');

%% 第二步,建立並訓練BP神經網路

%生成訓練BP神經網路的輸入 P 
%200*49的矩陣, 每一行代表一張主成分臉(共40人,每人5張),每個臉49維特徵(輸入)
%與faceLabel的200*1相對應(輸出)
P=TrainData;
%生成目標輸出向量 T
T=zeros(200,40);
 for i=1:40
    for j=1:5
      T((i-1)*5+j,i)=1;
  end
 end
 
%打亂訓練樣本順序
%P(200*49)   T(200*40)全零矩陣
gx2(:,1:k)=P;  %前 k 個本徵值和本徵向量 k=49
gx2(:,(k+1):(k+40))=T;
xd=gx2(randperm(numel(gx2)/(k+40)),:);   %matlab  randperm()函式,猜測應該是樣本與標籤對應打亂  
gx=xd(:,1:k);d=xd(:,(k+1):(k+40));
P=gx';
T=d';

%建立BP神經網路
[R,Q]=size(P);
[S2,Q]=size(T);
net=newff(minmax(P),T,[fix(sqrt(R*S2))],{'purelin','purelin'},'traingdx');

%訓練BP神經網路
net.trainparam.epochs=5000;    %訓練步數
net.trainparam.goal=0.0001;    %訓練目標誤差
net.divideFcn = '';            %所有的樣本都用於訓練
[net,tr]=train(net,P,T);       %P為輸入,T為輸出,開始訓練

%模擬BP神經網路
Y=sim(net,P);

%% 第三步,測試BP神經網路並計算其識別率
display('測試開始...');
%測試BP神經網路
s=0;
    load('Mat/PCA.mat');
    load('Mat/scaling.mat');
    load('Mat/trainData.mat');
%     load('Mat/multiSVMTrain.mat');
    display('..............................');
for i=1:40
    for j=6:10                              %讀入40x5副測試影象
         a=imread(strcat('Data\test\s',num2str(i),'\',num2str(j),'.pgm'));
         b=a(1:10304);
         b=double(b);
         TestFace=b;
         [m n] = size(TestFace);
         TestFace = (TestFace-repmat(meanVec, m, 1))*V; % 經過pca變換降維
         TestFace = scaling(TestFace,1,A0,B0);
         X = TestFace;
         Z=sim(net,X');
         [zi,index2]=max(Z);
         if index2==i   
             s=s+1;
         else
%              i                             %輸出識別出錯的那個人 i
%              j                           %輸出識別出錯的那張圖片 j
%              index2;                         %輸出誤識別成的那個人
             disp=(['測試集中第 ' ,num2str(i), '個人,第', num2str(j) , '張圖片被錯誤分類到' ,num2str(index2), '類'])
         end
     end
end

%計算識別率
accuracy=s/Q

在這裡插入圖片描述

5、C++程式碼

後面補充。