1. 程式人生 > >PCA+支援向量機-人臉識別(五)

PCA+支援向量機-人臉識別(五)

一):實驗準備

對於上篇中資料庫ORL人臉庫和AR人臉庫(下載地址在上篇中有),在上篇中討論的單純的PCA演算法對兩個資料庫進行了準確率計算,本篇為了提高識別準確率,特採用一種新方法,並結合PCA一起實現識別,實驗結果發現該方法能明顯提高兩者資料庫的識別率。

二):關於支援向量機

一直以來感覺支援向量機是一個神奇的演算法,當然裡面的原理相應來說也比較難懂,感覺能把支援向量機原理理解並講明白的估計對其他大多數的演算法似乎都能理解,對於使用級的,這裡能大概明白並會用支援向量機工具箱的就可以了。

關於原理,貼幾個比較好的,學了一段時間感覺稍微能明白點,如果一點原理都不懂,硬著頭皮先看吧,總會明白的。

一個SVM網站:

一個系列SVM知識集合:

三):關於支援向量機工具箱—LIBSVM

LIBSVM工具箱是臺灣國立大學的一些人研究製作的,這裡面把SVM的用法都整合化了,想怎麼用,用哪個核函式等等只需要修改相應的引數就可以了,可以說使用起來非常方便的。像LIBSVM在matlab下的工具箱只有四個函式,其中通常情況下就用其中的兩個函式,svmtrain訓練模型函式和svmpredict預測資料函式,至於選擇SVM演算法中的哪個核函式直接寫相應的引數就可以了。雖然新版的matlab也集成了SVM函式,但是感覺使用起來特麻煩,需要自己規定核函式,核函式引數等等。況且matlab自帶的演算法功能少,有些問題不適用。

關於LIBSVM在matlab下的使用,Matlab下安裝LIBSVM:Matlab安裝使用libsvm

其他平臺:

四):LIBSVM中函式的簡短說明

目前版LIBSVM(3.2.0)在matlab下編譯完後只有四個函式,libsvmread,Libsvmwrite,svmtrain(matlab自帶的工具箱中有一個同名的函式),svmpredict

 libsvmread主要用於讀取資料,這裡的資料是非matlab下的.mat資料,比如說是.txt,.data等等,這個時候需要使用libsvmread函式進行轉化為matlab可識別資料,比如自帶的資料是heart_scale資料,那麼匯入到matlab有兩種方式,一種使用libsvmread函式,在matlab下直接

libsvmread(heart_scale);第二種方式為點選matlab的‘匯入資料’按鈕,然後導向heart_scale所在位置,直接選擇就可以了。個人感覺第二種方式超級棒,無論對於什麼資料,比如你在哪個資料庫下下載的資料,如何把它變成matlab下資料呢?因為有的資料libsvmread讀取不管用,但是‘匯入資料’後就可以變成matlab下資料。

libsvmwrite寫函式,就是把已知資料存起來,使用方式為:libsvmwrite('filename',label_vector, instance_matrix);

label_vector是標籤,instance_matrix為資料矩陣(注意這個資料必須是稀疏矩陣,就是裡面的資料不包含沒用的資料(比如很多0),有這樣的資料應該去掉再存)。

svmtrain訓練函式,訓練資料產生模型的,一般直接使用為:

model=svmtrain(label,data,cmd); label為標籤,data為訓練資料(資料有講究,每一行為一個樣本的所有資料,列數代表的是樣本的個數),每一個樣本都要對應一個標籤(分類問題的話一般為二分類問題,也就是每一個樣本對應一個標籤)。cmd為相應的命令集合,都有哪些命令呢?很多,-v,-t,-g,-c,等等,不同的引數代表的含義不同,比如對於分類問題,這裡-t就表示選擇的核函式型別,-t=0時線性核。-t=1多項式核,-t=2,徑向基函式(高斯),-t=3,sigmod核函式,新版出了個-t=4,預計算核(還不會用);-g為核函式的引數係數,-c為懲罰因子係數,-v為交叉驗證的數,預設為5,這個引數在svmtrain寫出來使用與不寫出來不使用的時候,model出來的東西不一樣,不寫的時候,model為一個結構體,是一個模型,可以帶到svmpredict中直接使用,寫出來的時候,出來的是一個訓練模型的準確率,為一個數值。一般情況下就這幾個引數重要些,還有好多其他引數,可以自己參考網上比較全的,因為下面的這種方法的人臉識別就用到這麼幾個引數,其他的就不寫了。

svmpredict訓練函式,使用訓練的模型去預測來的資料型別。使用方式為:

[predicted_label,accuracy,decision_values/prob_estimates]= svmpredict(testing_label_vector,testing_instance_matrix,model,'libsvm_options')

或者:

[predicted_label]=svmpredict(testing_label_vector,testing_instance_matrix, model, 'libsvm_options')

第一種方式中,輸出為三個引數,預測的型別,準確率,評估值(非分類問題用著),輸入為測試型別(這個可與可無,如果沒有,那麼預測的準確率accuracy就沒有意義了,如果有,那麼就可以通過這個值與預測出來的那個型別值相比較得出準確率accuracy,但是要說明一點的是,無論這個值有沒有,在使用的時候都得加上,即使沒有,也要隨便加上一個型別值,反正你也不管它對不對,這是函式使用所規定的的),再就是輸入資料值,最後是引數值(這裡的引數值只有兩種選擇,-p和-b引數),曾經遇到一個這樣的問題,比如說我在訓練函式中規定了-g引數為0.1,那麼在預測的時候是不是也要規定這個引數呢?當你規定了以後,程式反而錯誤,提醒沒有svmpredict的-g引數,原因是在svmtrain後會出現一個model,而在svmpredict中你已經用了這個model,而這個model中就已經包含了你所有的訓練引數了,所以svmpredict中沒有這個引數,那麼對於的libsvm_options就是-p和-b引數了。對於函式的輸出,兩種方式呼叫的方法不一樣,第一種呼叫把所有需要的資料都調用出來了,二第二種呼叫,只調用了predicted_label預測的型別,這裡我們可以看到,在單純的分類預測模型中,其實第二種方式更好一些吧,既簡單有實用。

致此,四個函式在分類問題中的介紹大概如此,當然還有很多可以優化的細節就不詳細說了,比如可以再使用那些引數的時候,你如果不規定引數的話,所有的-引數都是使用預設的,預設的就可能不是最好的吧,這樣就涉及到如何去優化這個引數了。

五):SVM+PCA識別人臉的實現步驟

5.1:人臉資料庫的準備

這裡還是以AR人臉庫 和ORL人臉庫來作為樣本進行檢測,對於上一篇的單純PCA方法可以看出,pca方法對於ORL人臉庫的識別率已經很高了,但是對於AR人臉庫的識別率那是相當的低,原因在那裡說了,ORL人臉庫樣本間差異小,而AR人臉庫樣本間的差異大。這裡使用SVM來挑戰AR這個樣本差異大的人臉庫。首先也是對樣本進行分類,隨機抽取一些作為訓練集,另一些作為測試集。同樣使到函式[train_face,test_face] = imgdata(filedata,num) 

5.2:對訓練集與測試集進行降維

為什麼需要降維?原始一張圖片轉化成一行資料後有10000多維,對於這個維度來說,裡面很多維可能都是無關的,這樣去進行SVM訓練雖然也可以,可是會導致效率低下,速度上沒測試,維度高勢必計算量也會增加的。所以適當的降維取其主成分從理論上來講非常合適。首先對訓練集進行降維,同樣直接使用上篇的[img_new,img_mean,V] = PC
A(img,k)函式。降維得到訓練集的特徵向量V後,用這個V去乘以測試集就可以把也降維了。這裡有上篇的具體程式假設得到了[img_new,img_mean,V]這三個引數,現在怎麼去給測試集也去降維呢?

簡短程式如下:

[train_facepca,train_mean,V] = PCA(train_face,k);
num_test = size(test_face,1);
img_mean_all = repmat(train_mean,num_test,1);%複製m行平均值至整個矩陣
test_face = double(test_face) - img_mean_all;
test_facepca = test_face*V;

得到的test_facepca就是降維後的測試資料。

5.3:對訓練集與測試集進行資料歸一化

資料歸一化的目的就是去除不同數量級之間的不匹配關係,有的資料集歸不規範化對結果影響不大,而有的影響就很大,這裡我試了不規範化的資料,對結果的影響確實很大。所以正常來說最好都規範化一下吧,大部分情況規範化總是好的。這裡就是對上述得到的降維後的訓練集train_facepca和test_facepca進行規範化。程式如下:

<span style="font-size:14px;">function scaledface = scaling( faceMat,lowvec,upvec )
%特徵資料規範化
%輸入——faceMat需要進行規範化的影象資料,
upnew=1;
lownew=-1;
[m,n]=size(faceMat);
scaledface=zeros(m,n);
for i=1:m
    scaledface(i,:)=lownew+(faceMat(i,:)-lowvec)./(upvec-lowvec)*(upnew-lownew);
end</span>

這樣把所有資料規範化到-11之間了。整個規範化程式為:

%% 對訓練集於測試集都進行資料歸一化
low = min(train_facepca);
up = max(train_facepca);
scaled_trainface = scaling(train_facepca,low,up);
scaled_testface = scaling(test_facepca,low,up);

5.4:對訓練集訓練得到模型

由於libsvm處理的分類問題是兩類問題,而我們這裡可以看到,有50個不同的人臉,每個人又有26張樣本,按照分類來說,就是每一張圖片對應於50個類中的一個類。這就是50類分類問題了。那麼如何由兩類問題轉化成50類問題呢?這裡就把50個類劃分成很多個2類問題,每兩兩一分,比如1-2,1-3,1-4...1-50;2-3,2-4,2-5...2-50,等等等等最後是49-50;這麼些類,總共就有50*49/2個兩類分類了,對每一個兩類問題,都訓練一次,產生一個model,就有50*49/2個model,每一類都和其他的49個類有一次聯絡吧。現在假設來了一個樣本,比如是第10類吧,那麼把這個樣本依次帶到所有的model中,迴圈50*49/2次,對於每一個model都會出現一個預測的類吧,比如現在理論上是第10類,依次帶1-2,可能結果是個預測是個1(因為是在1-2兩個類中預測,不是1就是2),1-3可能結果是個3,等等,當到了10-1(其實是1-10)至10-50這些個含有10的類時,由於樣本本身是10,那麼這些結果預測出來的肯定(90%以上)是10吧,這樣把所有的二分類分迴圈完,也就是50*49/2次後,我們就可以統計出這個10落在第1類可能是30次,第二類可能是20次,第三類可能是27次等等等等,直到第10類是50次(接近50),在往後11類可能是30次,等等等等,第50類可能是10次。這樣我們只要統計出這些個數中的最大者,那麼這個類就是最大者對應的那個類了。

這個過程就是這樣,現在的問題是怎麼得到這些model?每一個model需要其中兩類的訓練集,每一個訓練集又可能不止一個樣本,並且好要標籤標上型別。

我是這樣處理的,比如現在求model{1,2},就是1與2類做一個model,每類假設選3個樣本,這樣訓練樣本就有6個樣本,對應的標籤分別是[1;1;1;2;2;2]就可以了吧。然後把這個標籤價樣本拿去svmtrain訓練,好了,1-3,1-4等等等等都採用同樣的方法就可以得到所有的model了。具體程式如下:

%---------------------
%     img_new--輸入的train_face臉
%     num:每個類(人)中選取num個樣本
function model = Model_Find(img_new,num)
%  用法:   model = Model_Find(img_new,num);
m = size(img_new,1);
class_all = m/num;  %總共的類數
%%  將訓練集劃分成SVM能夠處理的形式(包括標籤與資料)
for i = 1:class_all-1    %1到k-1
    for j = i+1:class_all  %2到k
        train_label_all{i,j} = [i*ones(1,num),j*ones(1,num)]';%標上標籤
        train_data_all{i,j} = [img_new((i-1)*num+1:i*num,:);img_new((j-1)*num+1:j*num,:)];
    end   %把對應的類上的資料提取出來
end
%%  呼叫SVM分類,對於k類共需要k*(k-1)/2 次劃分
for i = 1:class_all-1
    for j = i+1:class_all
        model{i,j} = svmtrain(train_label_all{i,j},train_data_all{i,j},'-t 0');
    end   %訓練得到所有的model
end

5.5:對測試集進行測試得到準確率

  有了上面的model,用這些model去分類測試集就可以了。先寫一個子程式,來處理隨機來了一個樣本時它是屬於哪個類的,在處理所有的測試樣本。

function class = find_class(model,test)
class_all = size(model,2); %注意model的大小為k*(k+1)
belong = zeros(1,class_all);
for i = 1:class_all-1
    for j = i+1:class_all
        predict = svmpredict(1,test,model{i,j});
%標籤任意寫假設為1,因為需要預測的就是標籤分類,不需要知道test的標籤
        belong(predict) = belong(predict) + 1;
     end
end
class = find(belong == max(belong));
%如果存在多個最大值,取第一個為準
%除錯半天終於知道錯誤了,這個必須考慮
m = size(class,2);
if m > 1
    class = class(1);
End

這個函式中的test可以認為是scaled_testface中的任意一行(一個樣本),只對這個樣本進行分類。

基於此,再對所有的scaled_testface一一進行分類並計算準確率:

function accurary = facefind_svm(model,test_facepca,num)
% num:訓練集中的每個人選取num個樣本
%  用法:   class = facefind_svm(model,test_face,img_mean,V);
test_num = size(test_facepca,1); 
for i = 1:test_num
    class(i) = find_class(model,test_facepca(i,:));
end
%%
numtrue = 0;
for i = 1:test_num
    if ceil(i/(26-num)) == class(i) 
        numtrue = numtrue+1;
    end
end
accurary = numtrue/test_num;

六):整體main程式

clc,clear;
load('face_AR_50.mat');
%%定義每個人選取num個樣本,劃分樣本
num = 3;
[train_face,test_face] = imgdata(filedata,num);
%% 對訓練集於測試集求取PCA,都轉化到PCA資料
k = 140;  %降維數
[train_facepca,train_mean,V] = PCA(train_face,k);
num_test = size(test_face,1);
img_mean_all = repmat(train_mean,num_test,1);%複製m行平均值至整個矩陣
test_face = double(test_face) - img_mean_all;
test_facepca = test_face*V;
%% 對訓練集於測試集都進行資料歸一化
low = min(train_facepca);
up = max(train_facepca);
scaled_trainface = scaling(train_facepca,low,up);
scaled_testface = scaling(test_facepca,low,up);
%% 訓練與計算準確率
model = Model_Find(scaled_trainface,num);
accurary = facefind_svm(model,scaled_testface,num);

七):實驗結果記錄

  首先貼一下上一次PCA方法下對AR資料庫的識別結果:

(AR人臉庫(包含50位男性和50位女性每人26張人臉共2600張人臉圖片 ),這裡只選取了50位男性樣本)

每個人選取num個樣本 降維數k 準確率accuracy
num=5 K=40 0.28
num=10 K=40 0.44
num=15 K=40 0.54

可以看出效果是很差的。

    現在來看一下PCA+SVM的效果;

實驗中存在的可變變數:

svmtrain 引數 :-t引數(這個是選擇核函式的)其他的-g等等引數都是採   用預設的。

      系統引數:  num:每個人選取num個樣本(最大26)

                   K :PCA中的降維數

首先k一定,k選30;

accuracy num=15;k = 30 num=10;k = 30 num=8;k = 30 num=2;k = 30
-t=0 0.84 0.79 0.6438 0.3717
-t=1 0.2782 0.2538 0.19 0.10
-t=2 0.65 0.6438 0.5210 0.3083
-t=3 0.68 0.6175 0.4886 0.3458


最好的,並且在num=15的時候準確率達到0.84,相比同等條件下的PCA只有0.54左右。
從上面可以看出,核函式引數-t=0,也就是選擇線性核函式的時候的效果始終是

由此也可以斷定線性核函式非常適合這個人臉識別庫。下面就選的-t=0不變,改變降維數k看看結果如何:

accuracy k = 50 k = 80 k = 140
num=15 0.9309 0.9618 0.9618
num=10 0.8713 0.9075 0.9238
num=8 0.8422 0.8029 0.9267
num=5 0.7552 0.8143 0.8400
num=3 0.6183 0.6765 0.8052
num=2 0.4917 0.6217 維度過大

從上述可以看到,在num=15,k = 140的時候準確率可以達到0.9618,應該很高了。另外,每次實驗的資料會不一樣,原因在於樣本的選擇是隨機的。Num在往上加就沒有試驗了,每個人總共才26個樣本,用到15個作為訓練已經很多了。再看看在num=3的時候,最大準確率也可以達到0.8052,也就是說每個人26個樣本中用3個就可以使準確率達到0.8052,這是PCA怎麼也不可能達到的。而且這個AR資料庫的差異性在以前討論過的很大,在這種情況下支援向量機的優點顯而易見。(上述最後一個維度過大的原因在於這個時候最大降維為50*2=100)

相關程式碼下載: http://download.csdn.net/detail/on2way/8689529