还有这个地方不能贴公式,不能贴图片,还有我很懒,就挑了几幅重要的贴了,其他的大家去看文章吧
排版不好看,也许写得也不明白,大家多包涵,希望大家可以完善这个文档。让后来者少走些弯路。
不用发论坛消息问我,发在这里让更多人看见,更多人解答,然后也可以让更多的人知道,更好些
第一部分:算法的产生
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<1 src="http://www.shineblog.com/UploadFiles/2008-10/1015342616.jpg" alt="图片">
树节点结构:
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.训练结果中数据的含义:
-
-
<_>6 4 12 9 -1.
//矩阵。前四个数值是矩阵四个点的位置,最后一个数值是矩阵像素和的权值
<_>6 7 12 3 3.
//矩阵。前四个数值是矩阵四个点的位置,最后一个是像素和的权值,这样两个矩阵就形成了一个Haar特征
4. 训练过程中使用的算法
这里主要讲弱分类器算法
•矩形特征值:Value[i][j], 1≤i≤n代表所有的Haar特征,1≤j≤m代表所有的样本
•FAULT = (curlerror + currerror)表示当前分类器的错误率的最小值,初始设置:curlerror currerror= 1000000000000000000000000000000000000000000000000(反正给个暴力大的数值就对了)
没有评论:
发表评论