adaboost算法的一些說明

轉自http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=4264#p15258

此文對研究adaboost算法具有很大幫助!本博轉貼於此,如果有妥請與我聯繫.

上學期拿出一部分時間來做adaboost,做的時候做了一些筆記。論壇上也有一些正在讀程序研究算法的人。我就把這份粗糙的筆記拿出來與大家分享一下吧。肯定有錯誤的地方,也有不妥當的地方,大家不要太相信我
還有這個地方不能貼公式,不能貼圖片,還有我很懶,就挑了幾幅重要的貼了,其他的大家去看文章吧
排版不好看,也許寫得也不明白,大家多包涵,希望大家可以完善這個文檔。讓後來者少走些彎路。
不用發論壇消息問我,發在這裏讓更多人看見,更多人解答,然後也可以讓更多的人知道,更好些
第一部分:算法的產生
1996年Yoav Freund在Experiments with a New Boosting Algorithm中提出了AdaBoost.M1和AdaBoost.M2兩種算法.其中,AdaBoost.M1是我們通常所說的Discrete AdaBoost;而AdaBoost.M2是M1的泛化形式.該文的一個結論是:當弱分類器算法使用簡單的分類方法時,boosting的效果明顯地統一地比bagging要好.當弱分類器算法使用C4.5時,boosting比bagging較好,但是沒有前者的比較來得明顯.
文獻中記錄的.M1算法
初始
1.獲得一組樣本(X)和它的分類(Y)和一個分類器(weaklearn).
2.賦予平均的權值分佈D(i)
進入循環:T次
1. 賦予弱分類器權值D(i),使用弱分類器獲得樣本(X)到分類(Y)上的一個映射.(就是把某個X歸到某個Y類中去)
2. 計算這個映射的誤差e.e=各個歸類錯誤的樣本權值之和.如果e>1/2那麼弱分類器訓練失敗,挑出循環,訓練結束(這在二值檢測中是不會發生的,而多值的情況就要看分類器夠不夠強健了)
3. 設B = e / ( 1 - e ).用於調整權值.因爲e<1/2.因此0<B<1
4. 如果某樣本分類正確,該樣本的權值就乘以B讓權值變小;如果分類錯誤,就讓該樣本的權值乘以B^-1或者不變,這樣就讓分類正確的樣本權值降低,分類錯誤的樣本權值升高,加強了對較難分類樣本的分類能力
5. 權值均衡化
循環結束
1. 最終的分類器是,當一個X進入時,遍歷所有Y,尋找使(h(x)=y的情況下,log(1/B)之和)最大者即是輸出分類y
M2相比於M1的改進是允許弱分類器輸出多個分類結果,並輸出這幾個分類結果的可能性(注意,這裏不是概率)
.M2的流程是
1.獲得一組樣本(X)和它的分類(Y)和一個分類器(weaklearn).
2.對於某個樣本Xi將它的分類歸爲一個正確分類Yi和其他不正確分類Yb
3.樣本權值進行如下分佈首先每個樣本分到1/m的權值,然後每個不正確分類分到(1/m)/Yb的個數.也就是說樣本權值是分到了每個不正確的分類上
進入循環
1. 求每個樣本的權值,即每個樣本所有不正確的分類的權值和,再求每個樣本錯誤分類的權值,即不正確分類的權值除以該樣本的權值.最後將每個樣本的權值歸一化
2. 將樣本權值和某樣本的不正確分類的權值輸入到weaklearn,獲得弱分類器的輸出爲各個分類的可能值
3. 計算僞錯誤率:公式見上
4. 更新權值
退出循環
最終的強分類器: 圖貼不出來了...
1999年, ROBERT E. SCHAPIRE和YORAM SINGER,於Machine Learning發表論文: Improved Boosting Algorithms Using Confidence-rated Predictions.提出了更具一般性的AdaBoost形式.提出了自信率以改善AdaBoost的性能.並提出瞭解決多標籤問題的AdaBoost.MH和AdaBoost.MR算法,其中AdaBoost.MH算法的一種形式又被稱爲Real Boost算法.
事實上:Discrete AdaBoost是指,弱分類器的輸出值限定在{-1,+1},和與之相應的權值調整,強分類器生成的AdaBoost算法;Real AdaBoost是指,弱分類器輸出一個可能度,該值的範圍是整個R, 和與之相應的權值調整,強分類器生成的AdaBoost算法。事實上,Discrete到Real的轉變體現了古典集合到模糊集合轉變的思想
至於Gentle AdaBoost.考慮到(AdaBoost對”不像”的正樣本權值調整很高,而導致了分類器的效率下降),而產生的變種算法.它較少地強調難以分類的樣本.
Rainer Lienhart, Alexander Kuranov, Vadim Pisarevsky在論文Empirical Analysis of Detection Cascades of Boosted Classifiers for Rapid Object Detection中提出在stump弱分類器(即每個弱分類器使用一個特徵進行分類)上進行的對比試驗中,Gentle的結果明顯好於Real和Discrete.大牛已經做出試驗了,我就不懷疑它了.
和上篇論文流程大體相同.作者還討論了alpha(t)的取法:
算法去看文章吧...這裏不能直接貼圖
文獻中記錄的AdaBoost.MH算法
算法的運算流程:
1. 得到一組樣本(m個)和樣本相應的分類,這個分類是由K個是和否的標籤組成.某一個樣本可以有多個是標籤.
2. 均分權值:1/mk
進入循環:
1. 由弱分類器獲得各樣本針對各標籤的是或否結果(給出離散值或連續值)
2. 獲得alpha(t)
3. 調整權值.大概是,弱分類器判斷l標籤的是或否,若判斷正確乘以1,錯誤乘以-1,再乘以 ,然後blablabla…
4. 權值歸一化
跳出循環
輸出強分類器 
1998年Jerome Friedman & Trevor Hastie & Robert Tibshirani發表文章Additive Logistic Regression: a Statistical View of Boosting
一些重要的結論:
Bagging是一個純粹的降低相關度的方法,如果樹的節點具有很高的相關性,bagging就會有好的結果
1.早期的AdaBoost在第二步的時候採用重採樣方法,即使某些樣本權重增加.這種方法與bagging存在某種關聯,它也是Boost的成功之處中降低相關度方面的重要部分.
2.在第二步中如果使用加權的tree-growing算法,而不是重採樣算法,效果會更好. This removes the randomization component essential in bagging
3.使用stumps作爲弱分類器
Logit和Gentle算法的提出過程大致是這樣的
1. 驗證Boosting algorithms是一種擬合一個additive logistic regression model(加性的邏輯迴歸模型)的階段式估計過程.它有最優一個指數判據,這個判據由第二定理與二項式對數似然判據是等價的.
2. 作者證明Discrete是使用adaptive Newton updates擬合一個additive logistic regression model來最小化Ee^(-yF(x))的過程,其中F(x)=求和fm(x),而fm(x)就是各層分類器的結果
3. 作者證明Real是使用層級最優的方法來擬合一個additive logistic regression model.
4. 作者說明了爲什麼要選擇Ee^(-yF(x))作爲目標:因爲大家都用這個
5. 作者證明了當(blabla一個很複雜的公式,貼不出來)時Ee^(-yF(x))最小
6. 作者證明了每次權值更新以後,最近一次訓練出的弱分類器錯誤率爲50%.
7. 作者證明了對於最優的F(x),樣本的分類乘以權值的和應該爲0.
於是作者用80年代興起的邏輯迴歸的尋優方法中提煉出了LogitBoost(我終於找到logitBoost的logic了)
自適應的牛頓法,擬合加性logistic迴歸模型
1. 獲得樣本,(x,y)序列.將分類y*=(y+1)/2
2. 設置初值,F(x)=0,p(xi)=1/2
進入循環
1. 依式計算zi,wi.
2. 通過加權最小二乘的方式擬合函數fm(x).由zi擬合xi,權重爲wi
3. 更新F(x),p(x) 
退出循環
輸出分類器sign[F(x)].
作者提出,logitAdaBoost在每一輪中都使Ee^(-y(F(x)+f(x)))最優,會使訓練樣本的代表性下降,於是提出了Gentle AdaBoost(牛頓步長法)
第二部分 人臉檢測
2001年,Paul Viola & Michael Jones在ACCEPTED CONFERENCE ON COMPUTER VISION AND PATTERN RECOGNITION發表文章: Rapid Object Detection using a Boosted Cascade of Simple Features
文章的主要結論:
1.Haar特徵的提出.使AdaBoost進行人臉檢測成爲可能.作者說明使用Haar特徵而不是像素點的原因是:特徵能包含某些特別領域的信息(encode ad-hoc domain knowledge that is difficult to learn using a finite quantity of training data.);此外在Integral提出以後,基於特徵的系統的運算速度高於基於像素的系統
2.用於快速Haar特徵運算的Integral Image的提出:極大地提高了訓練速度和檢測速度
3.用於物體檢測的AdaBoost方法的提出:Haar特徵作爲弱分類器判據與Adaboost結合到了一起
4.Cascade級聯方式的提出:極大地提高了AdaBoost的檢測速度
[實在是一篇很NB的文章,每一個貢獻都擲地有聲]
2002年, Rainer Lienhart and Jochen Maydt 在 IEEE ICIP 上發表文章An Extended Set of Haar-like Features for Rapid Object Detection.
1. 提出了擴展的Haar特徵,並證明了新的Haar特徵集提高了檢測的能力
2. 提出了一種針對已訓練完成的AdaBoost的修整程序
2002年, Rainer Lienhart & Alexander Kuranov & Vadim Pisarevsky 在MRL Technical Report上發表Empirical Analysis of Detection Cascades of Boosted Classifiers for Rapid Object Detection.
1. 提出了訓練時進行樣本豐富化
2. 指出:logitBoost could not be used due to convergence problem on later stages in the cascade training.
3. 通過試驗得出結論,在人臉檢測上Gentle AdaBoost的效果要好於 Discrete 和 Real
2004年, Bo WU & Haizhou AI & Chang HUANG & Shihong LAO在Computer Society上發表文章Fast Rotation Invariant Multi-View Face Detection Based on Real AdaBoost.
1. 提出了使用Real Boost檢測旋轉人臉
*****************還有一篇巨大量負樣本的速度提高方法找不到了……
第三部分,OpenCv中AdaBoost訓練程序略解
這裏只介紹一個大概的情況,具體的都寫在代碼的註釋裏了.
1.結構: 
程序的總體結構是一棵多叉樹,每個節點多少個叉由初始設定的maxtreesplits決定
圖片
樹節點結構:
typedef struct CvTreeCascadeNode
{
CvStageHaarClassifier* stage; // 指向該節點stage強分類器的指針
struct CvTreeCascadeNode* next; // 指向同層下一個節點的指針
struct CvTreeCascadeNode* child; // 指向子節點的指針
struct CvTreeCascadeNode* parent; // 指向父節點的指針
struct CvTreeCascadeNode* next_same_level;//最後一層葉節點之間的連接
struct CvTreeCascadeNode* child_eval; //用於連接最終分類的葉節點和根節點
int idx; //表示該節點是第幾個節點
int leaf; //從來沒有用到過的參數
} CvTreeCascadeNode;
這裏需要說明的是child_eval這個指針,雖說人臉檢測是一個單分類問題,程序中的maxtreesplits的設置值爲0,沒有分叉,但是樹本身是解決多分類問題的,它有多個葉節點,也就有多個最終的分類結果。但是我們使用的時候,雖然是一個多分類的樹,也可能我們只需要判斷是或者不是某一類。於是我們就用root_eval和child_eval把這個分類上的節點索引出來,更方便地使用樹結構。當然,這一點在本程序中是沒有體現的。
分類器結構:
每個樹節點中都包含了一個CvStageHaarClassifier強分類器,而每個CvStageHaarClassifier包含了多個CvIntHaarClassifier弱分類器。當CvIntHaarClassifier被使用的時候,被轉化爲CvCARTHaarClassifier,也就是分類樹與衰減數分類器作爲一個弱分類器。
typedef struct CvCARTHaarClassifier
{
CV_INT_HAAR_CLASSIFIER_FIELDS()
int count; /* 在決策樹中的節點數 number of nodes in the decision tree */
int* compidx; //特徵序號
CvTHaarFeature* feature; //選出的特徵。數組
CvFastHaarFeature* fastfeature;
float* threshold; /* array of decision thresholds */
int* left; /* array of left-branch indices */
int* right; /* array of right-branch indices */
float* val; /* array of output values */
} CvCARTHaarClassifier;
CvCARTHaarClassifier結構中包含了弱分類器的左值右值閾值等數組,在我們的程序中CART只選用了一個特徵進行分類,即退化成了stump。這裏的數組裏面就只存有一個元了
那麼這裏爲什麼要使用一個如此複雜的結構呢。大體來說有兩個好處:
1、 方便弱分類器之間的切換,當我們不選用CART而是其他的弱分類器結構的時候,就可以調用CvIntHaarClassifier時轉換成其他的指針
2、 這樣方便了Haar訓練的過程和Boost過程的銜接。
特徵的結構:
圖片
2.OpenCV的HaarTraining程序中一種常用的編程方法:
在這個程序中,函數指針是一種很常用的手法。函數指針的轉換使讀程序的人更難把握程序的脈絡,在這裏舉一個最極端的例子,來說明程序中這種手法的應用。
我們在cvBoost.cpp文件中的cvCreateMTStumpClassifier函數(這是一個生成多閾值(Multi-threshold)stump分類器的函數)下看到了一個這樣的調用:
findStumpThreshold_16s[stumperror](……….)
這裏對應的stumperror值是2
在cvboost.cpp中我們找到了一個這樣的數組
CvFindThresholdFunc findStumpThreshold_16s[4] = {
icvFindStumpThreshold_misc_16s,
icvFindStumpThreshold_gini_16s,
icvFindStumpThreshold_entropy_16s,
icvFindStumpThreshold_sq_16s
};
這個數組的類型是一個類型定義過的函數指針typedef int (*CvFindThresholdFunc)(…..)
因此這個數組中的四項就是四個指針,我們在cvCreateMTStumpClassifier中調用的也就是其中的第三項icvFindStumpThreshold_entropy_16s。
然後我們發現這個函數指針沒有直接的顯性的實現。那麼問題出在哪裏呢?
它是通過宏實現的:
程序中定義了一個這樣的宏:
#define ICV_DEF_FIND_STUMP_THRESHOLD_SQ( suffix, type ) 
ICV_DEF_FIND_STUMP_THRESHOLD( sq_##suffix, type, 
/* calculate error (sum of squares) */ 
/* err = sum( w * (y - left(rigt)Val)^2 ) */ 
curlerror = wyyl + curleft * curleft * wl - 2.0F * curleft * wyl; 
currerror = (*sumwyy) - wyyl + curright * curright * wr - 2.0F * curright * wyr; 
)
和一個這樣的宏:
#define ICV_DEF_FIND_STUMP_THRESHOLD( suffix, type, error ) 
CV_BOOST_IMPL int icvFindStumpThreshold_##suffix(…..)
{
……..
}
這兩個宏中,後者是函數的主體部分,而函數的定義通過前者完成。即:
ICV_DEF_FIND_STUMP_THRESHOLD_ENTROPY( 16s, short ),這樣的形式完成。這相當於給前者的宏傳遞了兩個參數,前者的宏將第一個參數轉換成sq_16s後和第二個參數一起傳到後者的宏。(##是把前後兩個string連接到一起,string是可變的兩,在這裏suffix就放入了16s和sq_結合成了sq_16s)
後者的宏接收到參數以後就進行了函數的定義:
CV_BOOST_IMPL int icvFindStumpThreshold_sq_16s
這樣icvFindStumpThreshold_sq_16s就被定義了。這樣做的好處是,12個非常相似的函數可以通過兩個宏和12個宏的調用來實現,而不需要直接定義12個函數。
3.訓練結果中數據的含義:
- <feature>
- <rects>
<_>6 4 12 9 -1.</_> 
//矩陣。前四個數值是矩陣四個點的位置,最後一個數值是矩陣像素和的權值
<_>6 7 12 3 3.</_> 
//矩陣。前四個數值是矩陣四個點的位置,最後一個是像素和的權值,這樣兩個矩陣就形成了一個Haar特徵
</rects>
<tilted>0</tilted> //是否是傾斜的Haar特徵
</feature>
<threshold>-0.0315119996666908</threshold> //閾值
<left_val>2.0875380039215088</left_val> //小於閾值時取左值
<right_val>-2.2172100543975830</right_val> //大於閾值時取右值
4. 訓練過程中使用的算法
這裏主要講弱分類器算法
•矩形特徵值:Value[i][j], 1≤i≤n代表所有的Haar特徵,1≤j≤m代表所有的樣本
•FAULT = (curlerror + currerror)表示當前分類器的錯誤率的最小值,初始設置:curlerror currerror= 1000000000000000000000000000000000000000000000000(反正給個暴力大的數值就對了)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章