最近学习了AdaBoosting算法,这个算法是上个世纪90年代末提出的,但是在深度学习出来之前,这个算法统治了人脸检测领域,大概的原理这里给出来,具体公式和原理实现请看机器学习的相关课程。
常用的分类方法有决策树分类、贝叶斯分类等。然而这些方法存在的问题是当数据量巨大时,分类的准确率不高。对于这样的困难问题,Boosting及其衍生算法提供了一个理想的解决途径。
Boosting算法是一种把若干个分类器整合为一个分类器的方法,其基本思想是:把一个复杂的分类任务分配给多位专家进行判断。这些专家可能并不是真正的专家,而仅仅是比普通人专业一点,他们称为弱分类器,依据一定机制,综合各位专家的结论,形成强分类器,得到最终的判断。
Boosting算法中应用最为广泛也最为有效的是1995年提出的AdaBoost(Adaptive Boosting,自适应增强)方法,其自适应之处在于前一个基本分类器分错的样本会得到加强,加权后的全体样本再次被用来训练下一个分类器,每一轮训练都会产生一个弱分类器,直到达到某个预订的足够小的错误率或者达到预先定义的最大迭代次数。
具体来说,整个AdaBoost迭代算法分为3步:
初始化训练数据的权值分布。如果有N个样本,则每一个训练样本最开始时都被赋予相同的权值:1/N。
训练弱分类器。具体训练过程中,如果某个样本点已经被准确地分类,那么在构造下一个训练集中,它的权值就被降低;相反,如果某个样本点没有被准确地分类,那么它的权值就得到提高。然后,权值更新过的样本集被用于训练下一个分类器,整个训练过程如此迭代地进行下去。
将各个训练得到的弱分类器组合成强分类器。各个弱分类器的训练过程结束后,加大分类误差率小的弱分类器的权重,使其在最终的分类函数中起着较大的决定作用,而降低分类误差率大的弱分类器的权重,使其在最终的分类函数中起着较小的决定作用。换言之,误差率低的弱分类器在最终分类器中占的权重较大,否则较小。
这里给出使用OPENCV进行的代码,因为是二分类的问题,所以画出了它的分界面
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
using namespace cv::ml;
int main(int argc, char** argv)
{
//训练样本
float trainingData[42][2] = { { 40, 55 }, { 35, 35 }, { 55, 15 }, { 45, 25 }, { 10, 10 }, { 15, 15 }, { 40, 10 },
{ 30, 15 }, { 30, 50 }, { 100, 20 }, { 45, 65 }, { 20, 35 }, { 80, 20 }, { 90, 5 },
{ 95, 35 }, { 80, 65 }, { 15, 55 }, { 25, 65 }, { 85, 35 }, { 85, 55 }, { 95, 70 },
{ 105, 50 }, { 115, 65 }, { 110, 25 }, { 120, 45 }, { 15, 45 },
{ 55, 30 }, { 60, 65 }, { 95, 60 }, { 25, 40 }, { 75, 45 }, { 105, 35 }, { 65, 10 },
{ 50, 50 }, { 40, 35 }, { 70, 55 }, { 80, 30 }, { 95, 45 }, { 60, 20 }, { 70, 30 },
{ 65, 45 }, { 85, 40 } };
Mat trainingDataMat(42, 2, CV_32FC1, trainingData);
//训练样本的响应值
int responses[42] = { 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R',
'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B' };
Mat responsesMat(42, 1, CV_32S, responses);
Ptr<Boost> boost = Boost::create();
boost->setBoostType(Boost::DISCRETE);//Boost的类型
boost->setWeakCount(200);//弱分类器的数量,也就是迭代次数
boost->setWeightTrimRate(0.95);//裁剪率,每次迭代出的权值从小到大排序,累加的权值小于该值的将不进入下次迭代(因为已经被正确分类了),这个值不能太小
//否则会导致训练的样本不足
boost->setMaxDepth(10);//决策树的最大深度
boost->setUseSurrogates(false);
boost->setPriors(Mat());
Ptr<TrainData> tData = TrainData::create(trainingDataMat, ROW_SAMPLE, responsesMat);
boost->train(tData);
//预测样本
float myData[2] = { 80, 45 };
Mat myDataMat(2, 1, CV_32FC1, myData);
double r = boost->predict(myDataMat);
cout << endl << "result: " << (char)r << endl;
//画出分类面
int width = 100, height = 100;
Mat image = Mat::zeros(height, width, CV_8UC3);
Vec3b green(0, 255, 0), blue(255, 0, 0);
for (int i = 0; i < image.rows; ++i)
for (int j = 0; j < image.cols; ++j)
{
Mat sampleMat = (Mat_<float>(1, 2) << i, j);
if (boost->predict(sampleMat) == 'R')
image.at<Vec3b>(j, i) = green;
else
image.at<Vec3b>(j, i) = blue;
}
// Show the training data
int thickness = -1;
int lineType = 1;
for (int i = 0; i < 42; i++){
if (responses[i] == 'R')
circle(image, Point(trainingData[i][0], trainingData[i][1]), 2, Scalar(0, 0, 0), thickness, lineType);
else if (responses[i] == 'B')
circle(image, Point(trainingData[i][0], trainingData[i][1]), 2, Scalar(255, 255, 255), thickness, lineType);
}
imshow("Image", image); // show it to the user
waitKey();
return 0;
}
运行结果如下:
划分出了我随机建立的数据点