天天看點

基于PCA和SVM的人臉識别

svm推廣到多類情況

一對多的最大響應政策(one against all)

假設有A 、B、C.. D四類樣本需要劃分。在抽取訓練集的時候,分别按照如下4種方式劃分。

  • A. 所對應的樣本特征向量作為正集(類标簽為+1), B、C、D所對應的樣本特征向量作為負集(類标簽為-1).
  • B所對應的樣本特征向量作為正集,A. C. D所對應的樣本特征向量作為負集
  • C所對應的樣本特征向量作為正集, A. B, D所對應的樣本特征向量作為負集.
  • D所對應的樣本特征向量作為正集, A. B、 C所對應的樣本特征向量作為負集.

對上述4個訓練集分别進行訓練, 得到4個SVM分類器。在測試的時候, 把未知類别的測試樣本又分别送入這4個分類器進行判決,取最大值

一對一的投票政策(one against one 葉th voting)

将A. B、C, D四類樣本兩類兩類地組成訓練集, 即(A,B)、(A.C)、(A,D)、(B,C)、(B,D)、{C,D), 得到6個(對于n類問題, 為n(n-1)/2個) SVM二分器。在測試的時候,把測試樣本又依次送入這6個二分類器, 采取投票形式, 最後得到一組結果。投票是以如下方式進行的。

初始化: vote(A)= vote(B)= vote(C)= vote{D)=O。

投票過程:如果使用訓練集(A,B)得到的分類器将f判定為A類,則vote(:A)=vote(A)+ 1 , 否則vote(B)=vote(B)+ 1; 如果使用(A,C)訓練的分類器将又判定為A類,則vote(.4.)=vote(A)+ 1, 否則vote(C)=vote(C)+l; . ….. ; 如果使用(C,D)訓練的分類器将又判定為C類,則

vote(C)=vote(C)+ 1 , 否則vote(D)=vote(D)+ 1。

最終判決: Max(vote(A), vote(B), vote{C), vote(D))。如有兩個以上的最大值, 則一般可簡單地取第一個最大值所對應的類别。

3. 一對一的淘汰政策(one against one with. eUmlnating)

這是在文獻中專門針對SVM提出的一種多類推廣政策,實際上它也适用于所有可以提供分類器置信度資訊的二分器。該方法同樣基于一對一判别政策解決多類問題, 對于這4類問題, 需訓練6個分類器(A,.B)、(A,C)、(A,D)、(B,C)、(B,D)、(C,D)。顯然, 對于這4類中的任意一類, 如第A類中的某一樣本, 就可由(A,B)、(A,C)、(A,D)這3個二分器中的任意一個來識别,即判别函數間存在備援。于是我們将這些二分器根據其置信度從大到小排序,置信度越大表示此二分器分類的結果越可靠, 反之則越有可能出現誤判。對這6個分類器按其置信度由大到小排序并分别編号, 假設為1 # (A,C), 2# (A,B), 3#(A,D)., 4# (B,D), 5# (C,D), 6# (B,C) 。

此時, 判别過程如下。

(1) 設被識别對象為X, 首先由1#判别函數進行識别。若判别函數h(x) = +1, 則結果為類型A, 所有關于類型C的判别函數均被淘汰;若判别函數h(x) =-1, 則結果為類型C,所有關于類型A的判别函數均被淘汰;若判别函數h(x) = 0, 為“ 拒絕決策” 的情形, 則直接選用2#判别函數進行識别。

(2)被識别對象x再由4#判别函數進行識别。若結果為類型+1, 淘汰所有關于D類的判别函數, 則所剩判别函數為6# (B,C)。

(3) 被識别對象x再由6#判别函數進行識别。若得到結果為類型+1 則可判定最終的分類結果為B。

那麼, 如何來表示置信度呢?對于SVM而言,分割超平面的分類間隔越大,就說明兩類樣本越容易分開,表明了問題本身較好的可分性。是以可以用各個SVM二分器的分類間隔大小作為其置信度。

SVM的Matlab實作

訓練一svmtrain

函數svmtrain用來訓練一個SVM分類器, 常用的調用文法為:

SVMStruct = svmtrain(Tranining,Group);

• Training是一個包含訓練資料的m行n列的2維矩陣。每行表示1個訓練樣本(特征 向量),m表示訓練樣本數目; n表示樣本的維數.

• Group是一個代表訓練樣本類标簽的1維向量.其元素值隻能為0或1.通常1表示正例,0表示反例.Group的維數必須和Traningg的行數相等,以保證訓練樣本同其 類别标号的一一對應.

SVMStruct是訓練所得的代表SVM分類器的結構體,包含有關最佳分割超平面的種種資訊,也可以計算出分類間隔值。

除上述的常用調用形式外,還可通過<屬性名, 屬性值>形式的可選參數設定一些訓練相關的進階選項, 進而實作某些自定義功能, 具體說明如下

基于PCA和SVM的人臉識别

訓練結果的可視化

當訓練資料是2維時可利用ShowPlot選項來獲得訓練結果的可視化解釋, 調用形式如下:

svmtrain(…,”ShowPlot”,true);

設定錯誤代價C

在13.2.2小節讨論非線性可分情況下的C-SVM時, 介紹了錯誤代價系數C對于訓練和分類結果的影響, 下面将給出設定C值的方法。由式(13-21)可知, 引入C對于二次規劃問題求解的影響僅僅展現在限制條件當中, 是以通過在調用svmtrain時設定一個優化選項’boxconstraint’即可, 調用形式為:

svmStruct = svmtarin(…,”boxconstraint”,C);

其中, C 為錯誤代價系數,預設取值為Inf, 表示錯分的代價無限大, 分割超平面将傾向于盡可能最小化訓練錯誤。通過适當地設定一個有限的C值, 将得到一個圖13.7 Cc)中所示的軟超平面。

分類-svmclassify

函數svmclassify的作用是利用訓練得到的SVMStruct結構對一組樣本進行分類,常用調用形式為:

Group = svmclassify(SVMStruct,sample);

SVMStruct是訓練得到的代表SVM分類器的結構體, 由函數svmtrain傳回.

Sample是要進行分類的樣本矩陣, 每行為1個樣本特征向量,總行數等于樣本數目,總列數是樣本特征的維數,它必須和訓練該SVM時使用的樣本特征維數相同.Group是一個包含Sample中所有樣本分類結果的列向量, 其維數與Sample矩陣的行數相同.

應用執行個體

svm訓練分類鸢尾植物

load fisheriris;
data = [meas(:,1),meas(:,2)];    
groups = ismember(species,'setosa');    

[train,test] = crossvalind('holdOut',groups);

svmStruct = svmtrain(data(train,:),groups(train),'showplot',true);

classes = svmclassify(svmStruct,data(test,:),'showplot',true);

nCorrect = sum(classes == groups(test));    
accuracy = nCorrect/length(classes);           
基于PCA和SVM的人臉識别

人臉識别

人臉識别簡介

人臉識别技術就是以計算機為輔助手段, 從靜态圖像或動态圖像中識别人臉。問題一般可以描述為:給定一個場景的靜态或視訊圖像, 利用已經存儲的人臉資料庫确認場景中的一個或多個人。一般來說, 人臉識别研究一般分為3個部分: 從具有複雜背景的場景中檢測并

分離出人臉所在的區域;抽取人臉識别特征;然後進行比對和識别

前期處理

實驗資料集仍然采用ORI人臉庫。由千每幅人臉圖像均包括112X92個像素(參見10.4.1小節),巨大的維數打消了我們将每幅圖像直接以其像素作為特征(ANN數字字元識别中的做法)的念頭。而實際上, 由于原始圖像各維像素之間存在着大量的相關性這種做法也是沒有必要的。是以需要首先通過主成分分析(PCA)的方法去除相關性, 我們将在1.0.4節那個基于主成份分析(PCA)的人臉特征提取工作的基礎上進行本實驗,将PCA降維後得到的20維特征向量作為SVM分類的特征。

前期預處理工作的具體步驟如下。

(1) 資料集的分割。

将整個資料集分為兩個部分1個訓練集和1個測試集。具體地說,我們将每個人的10張面部圖像分成兩組,前5張放入訓練集,另外5張用作測試。這樣訓練集與測試集各有40x5 = 200個人臉樣本。

(2) 讀入訓練圖像。

将每張圖像按列存儲為1個10304維的行向量。這樣400個人共組成一個400x10304的2維矩陣faceContainer, 每行1個人臉樣本.. readFeacsO 函數封裝了上述功能

(3)利用PCA降維去除像素之間的相關性。

資料規格化 (Scaling)

資料規格化又稱資料尺度歸一化,是指将特征的某個屬性(特征向量的某一維)的取值範圍投射到一個特定範圍之内,以消除數值型屬性因大小範圍不一而影響基于距離的分類方法結果的公正性。

資料規格化的必要性

可以毫不誇張地說,Scaling在一個模式識别問題中占據着舉足輕重的地位,甚至關系到整個識别系統的成敗。然而如此重要的一個環節卻往往易被初學者忽視。當您在同一個資料集上應用了相同的分類器卻得到遠不如他人的結果時,首先請确定是否進行了正确的Scaling。通常進行Scaling 一般有以下兩點必要性:

(1)防止那些處在相對較大的數字範圍(numeric ranges)的特征壓倒那些處在相對較小的數字範圍的特征。

(2)避免計算過程中可能會出現的數字問題。

資料規格化方法

(1)最大最小規格化方法。

(2)零均值規格化方法

實作人臉資料的規格化

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
    bTest = 0;
end

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


[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
        error('When do scaling on testset, only one output is supported.');
    end

    for iCol = 1:n
        if uRealBVec(iCol) == lRealBVec(iCol)
            SVFM(:, iCol) = uRealBVec(iCol);
            SVFM(:, iCol) = 0;
        else
            SVFM(:, iCol) = lTargB  +  ( VecFeaMat(:, iCol) - lRealBVec(iCol) ) / ( uRealBVec(iCol)-lRealBVec(iCol) ) * (uTargB-lTargB); % 測試資料的scaling
        end
    end
else
    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           

核函數的選擇

到目前為止, 要送入SVM的資料已經準備就緒, 但在 “ 啟動”SVM之前仍有兩個問題擺在面前:

(1)選擇哪一種核函數 (Kerne]);

(2)确定核函數的參數以及錯誤代價系數C的 最佳取值。

下面先來解決第1個問題, 第2個問題留到13.4.5中讨論。由于隻有4種常用的核函數(參見13.2.3小節),将它們依次嘗試并選擇對測試資料效果最好的一個似乎可行, 但後續的參數選擇問題将使這成為一個複雜的排列組合問題, 而遠 遠不是4種可能那麼簡單。盡管最佳核函數的選擇一般與問題自身有關, 但仍有規律可循。 建議初學者在通常情況 下優先考慮徑向基核函數RBF

建構多類svm分類器

首先進行訓練

在多類SVM訓練階段, 我們要做的就是用n=40類樣本建構n(n - 1)/2個SVM二分器,把每個SVM二分器的訓練結果(SVMStruct結構體)都儲存到一個結構體的細胞數組CASVMStruct中, 具體地說CASVMStruct{ii} {jj}中儲存着第ii類與第jj類兩類訓練得到的

SVMStruct 。最終将多類SVM分類時需要的全部資訊儲存至結構體multiSVMStruct中傳回,可以說multiSVMStruct中包含了我們的訓練成果。

function multiSVMStruct = multiSVMTrain(TrainData, nSampPerClass, nClass, C, gamma)
%function multiSVMStruct = multiSVMTrain(TrainData, nSampPerClass, nClass, C, gamma)
% 采用1對1投票政策将 SVM 推廣至多類問題的訓練過程,将多類SVM訓練結果儲存至multiSVMStruct中
%
% 輸入:--TrainData:每行是一個樣本人臉
%     --nClass:人數,即類别數
%     --nSampPerClass:nClass*1維的向量,記錄每類的樣本數目,如 nSampPerClass(iClass)
%     給出了第iClass類的樣本數目
%     --C:錯誤代價系數,預設為 Inf
%     --gamma:徑向基核函數的參數 gamma,預設值為1
%
% 輸出:--multiSVMStruct:一個包含多類SVM訓練結果的結構體

% 預設參數
if nargin < 4
    C = Inf;
    gamma = 1;
elseif nargin < 5
    gamma = 1;
end



%開始訓練,需要計算每兩類間的分類超平面,共(nClass-1)*nClass/2個
for ii=1:(nClass-1)
    for jj=(ii+1):nClass
        clear X;
        clear Y;
        startPosII = sum( nSampPerClass(1:ii-1) ) + 1;
        endPosII = startPosII + nSampPerClass(ii) - 1;
        X(1:nSampPerClass(ii), :) = TrainData(startPosII:endPosII, :);

        startPosJJ = sum( nSampPerClass(1:jj-1) ) + 1;
        endPosJJ = startPosJJ + nSampPerClass(jj) - 1;
        X(nSampPerClass(ii)+1:nSampPerClass(ii)+nSampPerClass(jj), :) = TrainData(startPosJJ:endPosJJ, :);


        % 設定兩兩分類時的類标簽
        Y = ones(nSampPerClass(ii) + nSampPerClass(jj), 1);
        Y(nSampPerClass(ii)+1:nSampPerClass(ii)+nSampPerClass(jj)) = 0;

        % 第ii個人和第jj個人兩兩分類時的分類器結構資訊
        CASVMStruct{ii}{jj}= svmtrain( X, Y, 'Kernel_Function', @(X,Y) kfun_rbf(X,Y,gamma), 'boxconstraint', C );
     end
end

% 已學得的分類結果
multiSVMStruct.nClass = nClass;
multiSVMStruct.CASVMStruct = CASVMStruct;

% 儲存參數
save('Mat/params.mat', 'C', 'gamma');           

分類實作

在多類 SVM 分類階段, 讓測試樣本依次經過訓練得到的 n(n-1)/2 個 (n = 40) 個 SVM 二分器, 通過投票決定其最終類别歸屬。

function class = multiSVMClassify(TestFace, multiSVMStruct)
% 采用1對1投票政策将 SVM 推廣至多類問題的分類過程
% 輸入:--TestFace:測試樣本集。m*n 的2維矩陣,每行一個測試樣本
%     --multiSVMStruct:多類SVM的訓練結果,由函數 multiSVMTrain 傳回,預設是從Mat/multiSVMTrain.mat檔案中讀取
%
% 輸出:--class: m*1 列向量,對應 TestFace 的類标簽


% 讀入訓練結果
if nargin < 2
    t = dir('Mat/multiSVMTrain.mat');
    if length(t) == 0
        error('沒有找到訓練結果檔案,請在分類以前首先進行訓練!');
    end
    load('Mat/multiSVMTrain.mat');
end

nClass = multiSVMStruct.nClass; % 讀入類别數
CASVMStruct = multiSVMStruct.CASVMStruct; % 讀入兩兩類之間的分類器資訊



%%%%%%%%%%%%%%%%%%%%%%%% 投票政策解決多類問題 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
m = size(TestFace, 1);
Voting = zeros(m, nClass); % m個測試樣本,每個樣本nPerson 個類别的投票箱

for iIndex = 1:nClass-1
    for jIndex = iIndex+1:nClass
        classes = svmclassify(CASVMStruct{iIndex}{jIndex}, TestFace);

        % 投票
        Voting(:, iIndex) = Voting(:, iIndex) + (classes == 1);
        Voting(:, jIndex) = Voting(:, jIndex) + (classes == 0);

    end % for jClass
end % for iClass



% final decision by voting result
[vecMaxVal, class] = max( Voting, [], 2 );
%display(sprintf('TestFace對應的類别是:%d',class));           

開始訓練

function train(C, gamma)
% 整個訓練過程,包括讀入圖像,PCA降維以及多類 SVM 訓練,各個階段的處理結果分别儲存至檔案:
%   将 PCA 變換矩陣 W 儲存至 Mat/PCA.mat
%   将 scaling 的各維上、下界資訊儲存至 Mat/scaling.mat
%   将 PCA 降維并且 scaling 後的資料儲存至 Mat/trainData.mat
%   将多類 SVM 的訓練資訊儲存至 Mat/multiSVMTrain.mat

global imgRow;
global imgCol;

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

nPerson=40;
nFacesPerPerson = 5;
display('讀入人臉資料...');
[imgRow,imgCol,FaceContainer,faceLabel]=ReadFaces(nFacesPerPerson,nPerson);
display('..............................');


nFaces=size(FaceContainer,1);%樣本(人臉)數目

display('PCA降維...');
[pcaFaces, W] = fastPCA(FaceContainer, 20);% 主成分分析PCA

% pcaFaces是200*20的矩陣, 每一行代表一張主成分臉(共40人,每人5張),每個臉20個維特征
% W是分離變換矩陣, 10304*20 的矩陣
visualize_pc(W);%顯示主成分臉
display('..............................');

X = pcaFaces;

display('Scaling...');
[X,A0,B0] = scaling(X);
save('Mat/scaling.mat', 'A0', 'B0');

% 儲存 scaling 後的訓練資料至 trainData.mat
TrainData = X;
trainLabel = faceLabel;
W = W;
save('Mat/trainData.mat', 'TrainData', 'trainLabel','W');

display('..............................');



for iPerson = 1:nPerson
    nSplPerClass(iPerson) = sum( (trainLabel == iPerson) );
end

multiSVMStruct = multiSVMTrain(TrainData, nSplPerClass, nPerson, C, gamma);
display('正在儲存訓練結果...');
save('Mat/multiSVMTrain.mat', 'multiSVMStruct');
display('..............................');
display('訓練結束。');           

識别

function nClass = classify(newFacePath)
% 整個分類(識别)過程

display(' ');
display(' ');
display('識别開始...');

% 讀入相關訓練結果
display('載入訓練參數...');
load('Mat/PCA.mat');
load('Mat/scaling.mat');
load('Mat/trainData.mat');
load('Mat/multiSVMTrain.mat');
display('..............................');

xNewFace = ReadAFace(newFacePath); % 讀入一個測試樣本
xNewFace = double(xNewFace);
% TestFace = (TestFace-repmat(meanVec, m, 1))*V; %其中V是主成分分量,這種才是降維的方法,原代碼錯了
xNewFace = (xNewFace-repmat(meanVec, 1, 1))*V; % 經過pca變換降維
xNewFace = scaling(xNewFace,1,A0,B0);


display('身份識别中...');
nClass = multiSVMClassify(xNewFace);
display('..............................');
display(['身份識别結束,類别為:' num2str(nClass), '。']);           

測試

function test()
% 測試對于整個測試集的識别率
%
% 輸出:accuracy --- 對于測試集合的識别率


display(' ');
display(' ');
display('測試開始...');

nFacesPerPerson = 5;
nPerson = 40;
bTest = 1;
% 讀入測試集合
display('讀入測試集合...');
[imgRow,imgCol,TestFace,testLabel] = ReadFaces(nFacesPerPerson, nPerson, bTest);
display('..............................');

% 讀入相關訓練結果
display('載入訓練參數...');
load('Mat/PCA.mat');
load('Mat/scaling.mat');
load('Mat/trainData.mat');
load('Mat/multiSVMTrain.mat');
display('..............................');

% PCA降維
display('PCA降維處理...');
[m n] = size(TestFace);
TestFace = (TestFace-repmat(meanVec, m, 1))*V; % 經過pca變換降維
TestFace = scaling(TestFace,1,A0,B0);
display('..............................');

% 多類 SVM 分類
display('測試集識别中...');
classes = multiSVMClassify(TestFace);
display('..............................');

% 計算識别率
nError = sum(classes ~= testLabel);
accuracy = 1 - nError/length(testLabel);
display(['對于測試集200個人臉樣本的識别率為', num2str(accuracy*100), '%']);           

在C=128,gamma= 0.0785的情況下,準确率大概在81.5%

源碼:SourceCode

基于PCA和SVM的人臉識别
基于PCA和SVM的人臉識别

繼續閱讀