提升方法--AdaBoost
前言
AdaBoost是最经典的提升方法,所谓的提升方法就是一系列弱分类器(分类效果只比随机预测好一点)经过组合提升最后的预测效果。而AdaBoost提升方法是在每次训练弱分类器的时候,提升上一个弱分类器误分类的数据的比重来让本次训练的分类器能够弥补上次分类器的不足。AdaBoost的概念和算法还是很好理解的,而且通过书上的例题可以很明显的感觉用一个很简单(计算量很小)的分类器,经过提升后的最终分类器的效果很好,本篇还是着重实现部分,并且将我在实现时候遇到的问题和思考记录下来。代码地址。
AdaBoost算法
输入: 训练数据集$T = { (x_1,y_1), (x_2,y_2),...,(x_N,y_N) } $; 某个弱分类算法(比如训练不足够的感知机,我的实现中就选的迭代次数比较少的感知机)。
输出: 最终分类器G(x)。
-
初始化训练数据的权值分布(这个权值每个训练数据都有, 但是并不是意味着训练的时候将数据点乘上这个权值,它的作用主要是在计算误差率的时候用到)。
(D_1 = (w_{11}, ...,w_{1i},...,w_{1N}), w_{1i}=frac{1}{N}, i=1,2,...,N)
-
对每一次提升(也就是弱分类器个数)m:
-
根据训练数据和权重训练分类器(G_m(x))。这里的权值分布是在训练分类器的时候用到的,具体来说就是在优化目标函数(比如最大似然函数或者误分类率)的时候,考虑每个数据点的权值。
-
计算(G_m(x))在训练数据集上的分类误差率:
(e_m = sum^{N}_{i=1}w_{mi}I(G_m(x_i) eq y_i))
-
计算(G_m(x))的系数
(a_m = 0.5log(frac{1-e_m}{e_m}))
-
更新训练数据的权值分布
(D_{m+1}=(w_{m+1,1}, ...,w_{m+1,i},...,w_{m+1,N}))
(w_{m+1,i} = frac{w_{mi}}{Z_m}exp(-a_my_iG_m(x_i)))
(Z_m = sum^{N}_{i=1}w_{mi}exp(-a_my_iG_m(x_i)))
-
-
构建基本分类器的线性组合
(G(x) = sign(sum^{M}_{m=1}a_mG_m(x)))
说明:对于训练数据的权值问题,是我在实际实现的时候发现的,这个要特别注意。还有就是基本分类器的选择问题,一定要选取比随机预测效果好的分类器,比如二分类问题,一定要选择分类误差率小于0.5的分类器,否则后续无法提升。
C++实现
代码结构
重要代码
这里放上求数据权值的代码:
int AdaBoost::computeWeights(Perceptron* classifier) {
vector<double> trainGT;
//由于我的感知机算法(见前面的系列代码)采用的loss函数里面包含训练数据的真值
//于是这里我就通过改变真值的比重来反应训练数据的比重
for(int i =0; i<trainDataGT.size();++i)
trainGT.push_back(trainDataGT[i]*featrWeight[i]);
classifier->setTrainD(trainDataF, trainGT);//将本算法的训练数据设为感知机的训练数据
classifier->setDim(indim);
classifier->train(100, 0.9);
//这里第一个参数是感知机的训练次数,第二个参数是学习率。
//100次迭代时学习的是一个强分类器,直接将全部数据分类正确,经过实验,将训练步数设置为90就可以得到弱分类器
//会有分类错误的情况,但是分类误差率小于0.5。
double erroeRate = 0;
for(int i = 0; i<trainDataF.size();++i) {
if (classifier->predict(trainDataF[i])!=int(trainDataGT[i]))
erroeRate += featrWeight[i];
}
if(erroeRate==0){
if(clsfWeight.size()==0)
clsfWeight.push_back(1);
return 0;
}
double clsW;
clsW = 0.5*std::log((1-erroeRate)/erroeRate);
clsfWeight.push_back(clsW);
double zm=0;
for(int i = 0; i<trainDataF.size();++i) {
zm+=featrWeight[i]*std::exp(-clsW*trainDataGT[i]*classifier->predict(trainDataF[i]));
}
for(int i = 0; i<featrWeight.size();++i ){
featrWeight[i] = featrWeight[i]/zm*std::exp(-clsW*trainDataGT[i]*classifier->predict(trainDataF[i]));
}
return 1;
}
再次强调,更改感知机的训练次数和学习率会有不同的结果,但是我的结果得到的最终的分类器却不如一个训练次数多的强分类器好,可能是因为我的训练数据太小。
这里主要是想练习用c++的指针使用其它类,也可以用其它的分类器,单是之前写那些算法并没有提供被调用的接口(当时并没有想要调用),改了改感知机的代码才勉强能用,以后写代码还是需要多思考。