参考文章:http://blog.csdn.net/haidao2009/article/details/7514787
http://baidutech.blog.51cto.com/4114344/743809
http://blog.csdn.net/u013207865/article/details/52728944
参考代码:https://github.com/zengkui/machine-learning/tree/master/Adaboost
参考教材: 《统计学习方法》 李航
算法原理:三个臭皮匠顶个诸葛亮
历史上,Kearns和Valiant首先提出了“强可学习”(strongly learnable)和“弱可学习”(weakly learnable):在概率近似正确(probably approximately correct,PAC)学习框架下,一个概念如果存在多项式的学习算法学习它,并且正确率很高,那么这个概念就被称为强可学习的;一个概念,如果存在多项式的学习算法学习它,并且正确率只比随机猜测好一点,那么这个概念称为弱可学习的。非常有趣的是后来Scphaire证明,强可学习和若可学习是等价的。也就是说,在PAC学习的框架下,一个概念是强可学习的充分必要条件是这个概念是弱可学习的。
这样一来,如果已经发现了“弱学习算法”,可将其提升为“强学习算法”,Adaboost算法就是一种最具代表性的提升算法。
提升方法就是从弱学习算法出发,反复学习,得到一系列基本分类器,然后组合这些基本分类器,构成一个强分类器。大多数的提升方法都是改变训练数据的权值分布,针对不同的训练数据分布调用弱学习算法学习一系列基本分类器。
这样对于提升方法来说,需要考虑两个问题:一是在每一轮如何改变数据的权值分布;二是如何将这些基本分类器组合成一个强分类器。
关于第一个问题,Adaboost的做法是,提高那些被本轮基本分类器错误分类样本的权值,而降低哪些被正确分类样本的权值。这样一来,那些本轮没有得到正确分类的样本,由于其权值的增大而受到下一轮的基本分类器的更大关注。
关于第二个问题,Adaboost的做法是采取加权多数表决的方法,具体地,加大分类误差率小的基本分类器的权值,使其在表决中起较大作用,减小分类误差率大的基本分类器的权值,使其在表决中起较小的作用。
算法(Adaboost)
下面根据一个例子介绍Adaboost具体实现
图 1
上图可以看成是坐标系中的点集,“+”代表一类,“-”代表一类。这是一种分类问题,即给定一个点p的坐标(x,y)判断点p属于哪一类。
首选应该构造弱学习算法,这里构造的弱学习算法为:通过利用水平和垂直线将样本分类,如下图所示
这里用的弱学习算法就是设置阈值v,比如说x<v 的样本数据为一类,x>v的样本数据为另一类,或者y<v的样本数据为一类,y>v的样本数据为另一类。
阈值v的选取通过设置任意两个不同横坐标或者不同纵坐标的均值作为阈值
比如如下一组数据(对应图1中的十个样本点,1代表"+"类,-1代表"-"类)
1,5,1
2,3,1
3,1,-1
4,5,-1
5,6,1
6,4,-1
6,7,1
7,6,1
8,7,-1
8,2,-1
上述十个样本点的横坐标为1,2,3,4,5,6,6,7,8,8
所以设置一组x对应的阈值 1.5 2.5 3.5 4.5 5.5 6.5 7.5 共七个
上述十个样本点的纵坐标(递增排序后)为 1,2,3,4,5,5,6,6,7,7
所以设置另外一组y对应的阈值 1.5 2.5 3.5 4.5 5.5 6.5 共六个
因此上述十个样本点需要设置 13个阈值
然后在每一轮迭代中,根据样本数据的权值分布,计算每一个弱学习算法的误差率(只在第一轮迭代的时候指代 分错的样本数占总样本数的比率 ),选取误差率最小的弱学习算法作为本轮的基本分类器。
最后将每一轮选取出的基本分类器组合成为最终的强分类器。
下面给出了具体的程序:
步骤:
第一步:读取训练数据
第二步:基于训练数据构造弱分类器
构造方法:先将所有数据的横标坐标从小到大排序,设置阈值,阈值分别设置为任意两个相邻横坐标的均值,若两相邻横坐标相同,则此处不设置阈值
然后将所有数据的纵坐标从小到大排序,设置阈值,阈值分别设置为任意两个相邻纵坐标的均值,若两相邻纵坐标相同,则此处不设置阈值
每一个阈值v对应两个弱分类器:(v,inf) (-inf,v)
第三步:进行M轮迭代。每一轮,在权值分布为Di的训练数据上,根据弱分类器的分类误差率确定基本分类器
第四步:计算基本分类器的系数
第五步:更新训练数据的权值分布
训练数据 data .txt
10
1,5,1
2,3,1
3,1,-1
4,5,-1
5,6,1
6,4,-1
6,7,1
7,6,1
8,7,-1
8,2,-1
adaboost.py
# -*- coding: utf-8 -*-
__author__ = "zengkui111@gmail.com"
__modifier__="543611694@qq.com"
import os
import sys
import math
inf = float("inf")
class Point:
def __init__(self, x, y, label, weight ):
self.x = x
self.y = y
self.label = label
self.weight = weight
class Learner:
def __init__(self, vrng, fid ):
self.vrng = vrng
self.fid = fid
self.weight = 0.0
def test_predict(self,d):
if self.fid=='x':
if self.vrng[0] < d.x < self.vrng[1]:
return 1
else:
return -1
elif self.fid=='y':
if self.vrng[0] < d.y < self.vrng[1]:
return 1
else:
return -1
class Adaboost:
def __init__(self, input_data, M):
self.read_data(input_data)
weak_learners = []
boundary = []
for p in self.points :
boundary.append(p.x)
weak_learners = self.make_learners( boundary, 'x')
boundary = []
for p in self.points :
boundary.append(p.y)
weak_learners += self.make_learners( boundary, 'y')
print "弱分类器个数",len(weak_learners)
self.learners = []
for i in range(1,M+1):
#calculate the error for each learner
info = []
for learner in weak_learners:
err = 0.0
for p in self.points :
category = learner.test_predict(p)
if category != p.label:
err += p.weight
info.append((err, learner))
#根据分类误差率确定基本分类器
[err, learner]= min(info) # the way to select weak classifier
#计算基本分类器的系数
learner.weight = (math.log((1- err) / err))/2
#更新训练数据集的权值分布
z = 0.0
for p in self.points:
category = learner.test_predict(p)
t = p.weight * math.exp( -1 * learner.weight * p.label * category)
p.weight = t
z += t
for p in self.points:
p.weight = p.weight / z
print "第",str(i),"轮,","基于",learner.fid,"进行分类"
print "位于",learner.vrng,"的样本为正类,其他样本为负类,基本分类器系数为",learner.weight
self.learners.append(learner)
def read_data(self, input_data):
fp = open(input_data)
self.points = []
pn = fp.readline().strip('\r\n')
pn = float(pn)
weight = 1.0 / pn
for line in fp.readlines():
x,y,label = line.strip('\r\n') .split(',')
self.points.append( Point(float(x), float(y), float(label), weight ))
print "样本个数:",len(self.points)
fp.close()
#---------构建弱分类器----------#
def make_learners(self, boundary, fid):
boundary = [min(boundary) - 1] + sorted(boundary) + [max(boundary) + 1]
learners = []
for i in range(1, len(boundary)):
if boundary[i] == boundary[i - 1]:
continue
l = (boundary[i] + boundary[i - 1]) / 2
learners.append(Learner((l, inf), fid))
learners.append(Learner((-inf, l), fid))
return learners
def predict(self):
print "(x, y)\tlabel\tpredict"
for p in self.points:
category = 0.0
for learner in self.learners:
category += learner.test_predict(p) * learner.weight
if category > 0 :
print "(%.f, %.f)\t%+.f\t%+.f" % (p.x, p.y, p.label, 1)
else :
print "(%.f, %.f)\t%+.f\t%+.f" % (p.x, p.y, p.label, -1)
if __name__ == "__main__":
adaboost = Adaboost("data1.txt", 3)
adaboost.predict()
样本个数: 10
弱分类器个数 34
第 1 轮, 基于 x 进行分类
位于 (-inf, 2.5) 的样本为正类,其他样本为负类,基本分类器系数为 0.423648930194
第 2 轮, 基于 x 进行分类
位于 (-inf, 7.5) 的样本为正类,其他样本为负类,基本分类器系数为 0.649641492065
第 3 轮, 基于 y 进行分类
位于 (5.5, inf) 的样本为正类,其他样本为负类,基本分类器系数为 0.922913345249
(x, y) label predict
(1, 5) +1 +1
(2, 3) +1 +1
(3, 1) -1 -1
(4, 5) -1 -1
(5, 6) +1 +1
(6, 4) -1 -1
(6, 7) +1 +1
(7, 6) +1 +1
(8, 7) -1 -1
(8, 2) -1 -1