【人臉識別7】haar+CART+Adaboost+Cascade訓練過程分析

大姨媽V發表於2018-06-09

【人臉識別7】haar+CART+Adaboost+Cascade訓練過程分析

人臉檢測分類器可以總結為:

    人臉檢測分類器=haar-like (特徵)+CART(弱)+ Adaboost(強) + Cascade(級聯)

下面將從以下幾個問題入手,各個擊破:

1.什麼是Haar特徵?為什麼使用feature而不是直接使用pixels?

2.什麼是CART分類迴歸樹?CART在本節的訓練中有何作用?

3.什麼是弱分類器?在本節中,具體是指什麼?如何得到弱分類器(CART)?

4.強分類器是什麼?如何訓練得到(ADaboost)?結構如何?

5.Cascade是什麼?有什麼作用?

6.整個人臉檢測分類器的組成?詳述訓練過程 haar+CART+adaboost+cascade?


一、帶著問題來學習

   1.什麼是Haar特徵?為什麼使用feature而不是直接使用pixels?

    Haar特徵:

                

                

    為什麼使用feature而不是直接使用pixels?

     我們的目標檢測過程是根據簡單特徵的值對影象進行分類。使用特徵而不是直接使用畫素有很多動機

        (1)特徵可以用來編碼特定領域的知識,而這些知識是很難用有限數量的培訓資料來學習的。

        (2)對於這個系統使用特徵,還有第二個關鍵的動機:基於特徵的系統的執行速度比基於畫素的系統要快得多。

    在本分類器中,所涉及的haar特徵具體引數如下:

typedef struct CvTHaarFeature
{
    char desc[CV_HAAR_FEATURE_DESC_MAX];
    int  tilted;/*haar特徵包括一個tilted標誌,tilted = 0  是直立型特徵; tilted =1  是45度特徵   
		特徵是2-3 個帶權重的矩形,如果rect[2].weight != 0 則特徵是3個矩形,否則是2 個矩形*/
    struct
    {
        CvRect r;  //矩形
        float weight; //權值
    } rect[CV_HAAR_FEATURE_MAX];
} CvTHaarFeature; //haar特徵

typedef struct CvFastHaarFeature
{
    int tilted;
    struct
    {
        int p0, p1, p2, p3;
        float weight;
    } rect[CV_HAAR_FEATURE_MAX];
} CvFastHaarFeature;

typedef struct CvIntHaarFeatures
{
    CvSize winsize;
    int count;
    CvTHaarFeature* feature;
    CvFastHaarFeature* fastfeature;
} CvIntHaarFeatures;

2、什麼是CART分類迴歸樹?CART在本節的訓練中有何作用?

  本節中,所用的CART(分類迴歸樹)是一種決策樹學習演算法。分類迴歸樹(classification and regression tree,CART)模型由Breiman等人在1984年提出,是應用廣泛的決策樹學習方法。CART同樣由特徵選擇、樹的生成以及剪枝組成,既可以用於分類也可以用於迴歸。

       CART的演算法思想是:CART演算法採用的是一種二分遞迴分割的技術,將當前樣本分成兩個子樣本集,使得生成的非葉子節點都有兩個分支。因此CART實際上是一棵二叉樹。

       特點:當CART是分類樹的時候,採用GINI值作為分裂節點的依據,當CART作為迴歸樹的時候,使用樣本的最小方差作為分裂節點的依據

        具體關於CART的介紹,可看:https://blog.csdn.net/gzj_1101/article/details/78355234

  在本節中,我們主要使用CART分類功能。假設我們使用三個Haar-like特徵f1,f2,f3來判斷輸入資料是否為人臉,可以建立如下決策樹:(下圖相當於是CART tree:較優弱分類器)

                        

  可以看出,在分類的應用中,CART每個非葉子節點都表示一種判斷,每個路徑代表一種判斷的輸出,每個葉子節點代表一種類別,並作為最終判斷的結果。

  CART在本節的訓練中有何作用?

  CART在本節的訓練中,可以將最基本的弱分類器,通過訓練,找到最優閾值,使之成為可用的較優弱分類器。(下面會有詳細的CART訓練過程)

3.什麼是弱分類器?在本節中,具體是指什麼?如何得到弱分類器?

        弱分類器(弱學習演算法),是一個學習演算法對一組概念的識別比隨機分類的正確率高一點,強分類器(強學習演算法)就是指一個學習演算法對一組概念的識別率很高。                      

  弱分類器分為最基本弱分類器和最優弱分類器。最基本的弱分類器,只含有一個haar-like特徵,在決策樹CART結構中,它相當於只有一層,被稱為“樹樁”(stump),如下圖所示。

                                

  由於單特徵的分類器(只有一個樹樁)太簡陋了,可能並不比隨機判斷的效果好。所以,採用了CART的決策樹形式,單節點的基本分類器通過訓練可以優化為有更高的辨別力的分類器,即為弱分類器(CARTHaarClf),最終弱分類器形式一般為多節點形式,如下。

        

    但本節所採用的是樹樁型CART樹。所以一個CART樹,僅有一個樹樁節點(單個特徵),所以最終的弱分類器形式,如下圖所示:

                                        

      那一個最基本的弱分類器,如何成為一個較優的弱分類器呢?

最基本的弱分類器可能只是一個最基本的Haar-like特徵,計算輸入影象的Haar-like特徵值,和最基本的弱分類器的閾值比較,以此來判斷輸入影象是不是人臉,然而這個弱分類器太簡陋了,可能並不比隨機判斷的效果好。弱學習演算法被設計成選擇單個矩形特性,它可以最好地分離正樣本和負樣本的例子。對於每個特徵,弱學習器決定了最優閾值分類函式,這樣被錯誤分類的樣本數最少。因此,一個弱分類器hjx包括一個影象的子視窗x、一個特徵fj、一個閾值θj和一個指示不等號方向的極性pj作用是控制不等式的方向,使得不等式都是<號,形式方便)組成,:

        

最重要的就是如何決定每個結點判斷的輸出,要比較輸入圖片的特徵值和弱分類器中特徵,一定需要一個閾值,當輸入圖片的特徵值大於該閾值時才判定其為人臉。訓練最優弱分類器的過程實際上就是在尋找合適的分類器閾值,使該分類器對所有樣本的判讀誤差最低。

具體尋找最優閾值的過程:

    

    弱分類器的具體引數,程式碼如下:

/*
 * CART classifier
 */
typedef struct CvCARTHaarClassifier
{
    CV_INT_HAAR_CLASSIFIER_FIELDS()

    int count;
    int* compidx;
    CvTHaarFeature* feature;// 特徵(指標相當於陣列,表明可以有多層CART tree,但在本訓練中,採用樹樁結構,即一個CARTHaarClf,一個特徵)
    CvFastHaarFeature* fastfeature;
    float* threshold;//閾值
    int* left;//左葉子值
    int* right;//右葉子值
    float* val;//計算值
} CvCARTHaarClassifier;

4.強分類器是什麼?如何訓練得到?結構如何?

    在任何影象子視窗中,haar-like的特徵的總數非常大,遠遠大於畫素的數量。為了確保快速分類,學習過程必須排除大部分可用的特徵,並將重點放在一小部分關鍵特徵上。在Tieu和Viola的工作的激勵下,通過對AdaBoost程式的簡單修改,特徵選擇被實現了:通過使用AdaBoost選擇少量重要特徵來構造強分類器,即通過組合弱分類器得到了強分類器。

                

    其中,弱分類器的分類誤差越小(效能越好),在強分類器中所佔的比重越大。即αt 越大。

    如何訓練得到強分類器

    每個弱分類器只能依賴於單個特徵。因此,選擇一個新的弱分類器的提升(boosting)過程的每個階段,都可以被看作是一個特徵選擇過程。Adalboost提供了一種有效的學習演算法和強大的邊界泛化效能。Adalboos演算法具體過程如下:

            

      對於人臉檢測的任務,AdaBoost選擇的初始矩形特徵是有意義且容易解釋的。第一個特徵選擇似乎專注於眼睛比鼻子和臉頰更暗這一屬性 (參見圖4)。這個特徵與檢測子視窗相比相對較大,並且應該對臉的大小和位置不敏感。選擇的第二個特徵依賴於眼睛比鼻樑更暗的屬性。

        

        圖4:AdaBoost選擇的第一個和第二個特徵。這兩個特徵顯示在最上面一行,然後在底部的一個典型的訓練面上覆蓋。第一個特徵是測量眼睛區域和上頰區域之間的強度差異。該特徵利用觀察到的觀察結果,即眼睛區域通常比臉頰更暗。第二個特徵是將眼睛區域的強度與鼻樑上的強度進行比較。

    想看更詳細的Adalboost的演算法介紹,請戳:https://blog.csdn.net/u012679707/article/details/80369772

    強分類器(stageHaarClf)的引數,見程式碼:

/* internal stage classifier */
typedef struct CvStageHaarClassifier
{
    CV_INT_HAAR_CLASSIFIER_FIELDS()

    int count;
    float threshold;
    CvIntHaarClassifier** classifier;
} CvStageHaarClassifier;

    強分類器結構:

                    

    xml檔案中的stageHaarClf:

            

5.Cascade是什麼?有什麼作用?整個人臉檢測分類器的組成?

    Cascade是什麼?

    檢測過程的整體形式是一個退化的決策樹,我們稱之為“cascade”(級聯分類器,見圖5)。第一個分類器的結果如果為正(positive result),則將觸發對第二個分類器的評估,它也被調整以達到很高的檢測率。第二個分類器的正的結果觸發第三個分類器,以此類推。任何時候的負結果(A negative)都會導致子視窗立即被拒絕。

    級聯的階段是通過使用Adaboost訓練分類器,然後調整閾值以減少errar rate來構建的。注意,預設的Adaboost閾值被設計為在訓練資料上產生低錯誤率。一般來說,較低的閾值會產生更高的檢測率和更高的假正率。

        

    圖5:檢測級聯的示意圖。每個子視窗都應用了一系列分類器。最初的分類器消除了大量的負面例子,且只需很少的處理。隨後的層消除了額外的負面例子,但需要額外的計算。經過幾個階段的處理後,子視窗的數量已經大大減少了。進一步的處理可以採取任何形式,例如附加級聯階段(如在我們的檢測系統中)或使用另一種可選擇的檢測系統。

    TreeCascadeClf結構:

HaarCascadeTree結構:
CascadeTree
{
    Stage 0:
    {
        Tree 0:
        Tree 1:
        …
        Tree T0:
    }
    Stage 1:
    {
        Tree 0:
        Tree 1:
        …
        Tree T1:
    }
    ……
    Stage N:
    ……
}

    想要了解人臉檢測分類器的具體內容,開啟cascade.xml檔案,一看便知:

    

        這是我自己訓練的分類器,選擇的是樹樁型(stump),每棵CART樹只有root node,沒有葉子節點。所以一個CART弱分類器就是一個樹樁stump,包含一個特徵。若選擇分裂節點不為1,則是多節點樹,每個弱分類器將包含多個node,也就含有多個特徵(每個cart tree node帶有一個特徵)。


    具體設定可見【附錄1】,訓練haartraing時,引數分裂子節點設定成了1,則表明樹高為1,即只有樹樁;預設是2,則表明樹高為2。

 Cascade有什麼作用?

        在一個級聯結構中,將更復雜的分類器組合在一起(強強聯手),通過對影象中有”希望“的區域的關注,極大地提高了檢測器的速度。第一個分類器的結果如果為正(positive result),則將觸發對第二個分類器的評估,它也被調整閾值以達到很高的檢測率。第二個分類器的正的結果觸發第三個分類器,以此類推。任何時候的負結果(A negative)都會導致子視窗立即被拒絕。這就使得待檢測區域急速縮小,提升了檢測速度,節省了大量時間

如何訓練得到CascadeTree?

      級聯訓練過程涉及兩種型別的權衡(折衷)。在大多數情況下,具有更多特徵的分類器將獲得更高的檢測率和更低的假陽性率。與此同時,具有更多特徵的分類器需要更多的時間來計算。原則上,我們可以定義一個優化框架,其中:

        i)分類器迭代階數

        ii)每個迭代階段(stage)的特徵數量

        iii)每個階段的閾值,為了最小化預期的被評估特性的數量而被改變。

      不幸的是,找到這種最優的方法是一個非常困難的問題。

      在實踐中,一個非常簡單的框架被用來產生一個高效的分類器。級聯的每個階段都降低了假正率,降低了檢測率。選擇一個目標,以減少誤報和最大化減少檢測量。具體的訓練過程,包括弱分類的訓練,和強分類的訓練。

    (1)每個階段都通過新增特徵feature(弱分類器)來訓練,直到目標檢測和假陽性率得到滿足(這些率rate是通過在驗證集中測試檢測器來確定的)。

    (2)在達到假陽性和檢測率的總體目標之前,增加迭代數目(stage,強分類器)。直到滿足條件,停止迭代

    具體訓練過程如下:

                    

    CascadeHaarClf具體程式碼:    
/* internal cascade classifier */
typedef struct CvCascadeHaarClassifier
{
    CV_INT_HAAR_CLASSIFIER_FIELDS()

    int count;
    CvIntHaarClassifier** classifier;
} CvCascadeHaarClassifier;


/* internal tree cascade classifier node */
typedef struct CvTreeCascadeNode
{
    CvStageHaarClassifier* stage;

    struct CvTreeCascadeNode* next;
    struct CvTreeCascadeNode* child;
    struct CvTreeCascadeNode* parent;

    struct CvTreeCascadeNode* next_same_level;
    struct CvTreeCascadeNode* child_eval;
    int idx;
    int leaf;
} CvTreeCascadeNode;
6.整個人臉檢測分類器的組成?

人臉檢測分類器可以總結為:

    人臉檢測分類器=haar-like (特徵)+CART(弱)+ Adaboost(強) + Cascade(級聯)

haar-like+CART+adaboost+Cascade 訓練過程分析圖:

            

整個訓練過程原始碼簡摘:

    獲取訓練的樣本資料(正和負)-----計運算元視窗特徵值----訓練弱分類器----訓練強分類器----訓練級聯分類器----輸出訓練好的人臉檢測分類器

原始碼分析:
//建立TreeCasecadeClf
cvCreateTreeCascadeClassifier( dirname, vecname, bgname,
                               npos, nneg, nstages, mem,
                               nsplits,
                               minhitrate, maxfalsealarm, weightfraction,
                               mode, symmetric,
                               equalweights, width, height,
                               boosttype, stumperror,
                               maxtreesplits, minpos, bg_vecfile );
haar_features = icvCreateIntHaarFeatures( winsize, mode, symmetric );
training_data = icvCreateHaarTrainingData( winsize, npos + nneg );
//從vec正樣本檔案獲取訓練資料
poscount = icvGetHaarTrainingDataFromVec( training_data, 0, npos,
                    (CvIntHaarClassifier*) tcc, vecfilename, &consumed );
// consumed = consumed + thread_consumed_count(執行緒消耗數)
printf( "POS: %d %d %f\n", poscount, consumed, ((double) poscount)/consumed );

//從負樣本檔案獲取訓練資料
negcount = icvGetHaarTrainingDataFromBG( training_data, poscount, nneg,
                    (CvIntHaarClassifier*) tcc, &false_alarm, bg_vecfile ? bgfilename : NULL );
printf( "NEG: %d %g\n", negcount, false_alarm );
printf( "BACKGROUND PROCESSING TIME: %.2f\n", (proctime + TIME( 0 )) );
//Adaboost

/* precalculate feature values 預先計算特徵值*/
icvPrecalculate( training_data, haar_features, numprecalculated );
printf( "Precalculation time: %.2f\n", (proctime + TIME( 0 )) );

/* train stage classifier using all positive samples 使用所有正樣本訓練強分類器TreeCascadeNode=stage*/
CV_CALL( single_cluster = icvCreateTreeCascadeNode() );
//訓練每一強分類器stage中的弱分類器CartTree
single_cluster->stage = (CvStageHaarClassifier*) icvCreateCARTStageClassifier(
                            training_data, NULL, haar_features,
                            minhitrate, maxfalsealarm, symmetric,
                            weightfraction, numsplits, (CvBoostType) boosttype,
                            (CvStumpError) stumperror, 0 );
printf( "Stage training time: %.2f\n", (proctime + TIME( 0 )) );

----------------------------------------------  附錄  --------------------------------------------------------------

【附錄1】訓練haartraing時,引數分裂子節點設定成了1,則表明樹高為1,即只有樹樁;預設是2,則表明樹高為2。


-------------------------------------------         END      -------------------------------------

參考:

(人臉檢測過程分析 非常棒!)     http://www.cnblogs.com/ello/archive/2012/04/28/2475419.html

    (haar原始碼) https://blog.csdn.net/u011783201/article/details/52184093

(CART) https://blog.csdn.net/acdreamers/article/details/44664481

    人臉檢測經典論文翻譯  https://blog.csdn.net/u012679707/article/details/80626775


相關文章