您的位置:首页 > 其它

利用AdaBoost元算法提高分类性能

2017-08-17 16:43 661 查看

1. 元算法介绍

做重要决定时,大家可能会考虑多个权威的意见而不是一个人的意见,机器学习中也是如此,这就是元算法的背后思想。元算法是对其他算法组合的一种方式。

优点:泛化错误低,易编码,可以用在大部分分类器上,无参数调整问题

缺点:对离群点敏感

2. AdaBoost思想 以及 涉及公式

2.1 简单理解

AdaBoost是adaptive boosting(自适应boosting)的缩写,是利用弱分类器和多个实例来构建为一个强分类器,弱强指的是分类效果,AdaBoost算法是与分类器交互并且重点关注分类失败的点来获得新的分类器….(实际就是分类失败的点权重问题)

与SVM有着相似的label , 也就是+1/-1化(sign函数把小于0的赋值-1反之+1)

可以把AdaBoost弱分类器类比SVM核函数

- 首先本文选择的弱分类器为单层决策树(DS),实际可以选择任何弱分类器

- 初始化每个分类器权重alpha

- 初始化每个样本的权重D

- 计算弱分类器的分类错误率,根据错误率更新权重D,权重alpha

- 根据新的权重D进行分类

- 每个分类器乘以权重alpha求和(加权求和)

简易流程图


(黑色柱状代表权重D大小)

2.1 公式

错误率定义 : ϵ=分类错误样本数量总样本数量

alpha权重更新公式 : α=12ln(1−ϵϵ) 公式1

D权重更新公式 :

样本正确分类 : D(t+1)i=Dti⋅e−αSum(D) 公式2

样本错误分类 : D(t+1)i=Dti⋅eαSum(D)

可以看到 : 错误率减小,alpha变大,样本错误分类的权重D变大…..

下文代码解释 :

代码 multiply(-1 * alpha * mat(label).T, preLabel)

这么理解 -1 * alpha * (mat(label).T * preLabel)

如果labeli是1, preLabeli是-1 正确即为+1 错误为-1

3. 主要算法伪代码

# 建立单层决策树
初始最小误差值inf
for 每一个特征
for 每一个待处理的阀值
for 每一个符号(大/小于)
设立阀值
调用函数,产生预测值
预测/实际分类的误差求和
误差打擂台
记录最小的误差
返回 最优DS,最优误差,最优分类

# AdaBoost算法
初始化样本权重D矩阵
建立弱分类器列表
for 0 - numIt 迭代:
调用建立单层决策树函数
计算alpha,保存alpha
弱分类器列表加入上决策树
更新样本权重D
累计错误率
返回弱分类器列表


4. 代码 以及 几个小栗子

# coding:utf-8

from numpy import *

def loadSimpleData():
data = matrix([[1.0, 2.1],
[2.0, 1.1],
[2.0, 1.0],
[1.0, 1.0],
[1.3, 1.0]])
label = [1.0, 1.0, 1.0, -1.0, -1.0]
return data, label

def loadData(fileName):
dataMat = []
labelMat = []
with open(fileName) as txtFile:
for line in txtFile.readlines():
temp = line.split()
dataMat.append(map(float, temp)[0:-1])
labelMat.append(map(float, temp)[-1])
return dataMat, labelMat

def sigTreeClassify(data, index, splitValue, splitSign):
labelPre = ones((shape(data)[0], 1))
if splitSign == 'small':
labelPre[data[:, index] <= splitValue] = -1.0  # 符号无所谓 用1与-1区分类别 与SVM相似
else:
labelPre[data[:, index] > splitValue] = -1.0  # 有区别就行
return labelPre

def buildTree(data, label, D): # D 样本权重
data = mat(data)
label = mat(label).T
m, n = shape(data)
numSteps = 10.0
bestTree = {}   # 最优的弱分类器
bestLabel = mat(zeros((m, 1)))  # 最优预测label
minError = inf  # 打擂台专属
for index in range(n):
rangeMin = data[:, index].min()
rangeMax = data[:, index].max()
stepSize = (rangeMax - rangeMin) / numSteps
for j in range(-1, int(numSteps) + 1):
for sign in ["small", "large"]:
value = rangeMin + float(j) * stepSize  # j:-1~numstep+1 value稍微超出范围
labelPre = sigTreeClassify(data, index, value, sign)
errArr = ones((m, 1))
errArr[labelPre[:] == label[:]] = 0  # 预测正确为0  反之为1
weightError = D.T * errArr  # 可以看得出来01的作用, 方便加权求和
if minError > weightError:  # 更新最小误差 以及 最优树
minError = weightError
bestLabel = labelPre
bestTree['value'] = value # 字典存取单层决策树的信息
bestTree['index'] = index
bestTree['sign'] = sign
''' if U have some questions '''
# print "inloop: index:%d, value:%f, sign:%s" % (index, value, sign)  # 循环中的变量
# print "label :", label.T      # 正确值
# print "labelPre :", labelPre.T  # 预测值
# print "errArr :", errArr.T    # 预测是否正确
# print "D :", D.T  # D
# print "weightError :", weightError  # alpha加权误差
# print "minError :", minError, '\n----------------------------------------------'
# print "bestTree :", bestTree
# print "minError :", minError
# print "bestLabel :", bestLabel.T, "\n++++++++++++++++++++++++++++"
''' have a look '''
return bestTree, minError, bestLabel

def AdaBoostTrainDS(data, label, numIt=40): # numIt实际就是生成单层决策树的个数
weakClassArr = []  # 弱分类器列表
m = shape(data)[0]
D = mat(ones((m, 1)) / m)  # 样本权重和为1 每个样本有一个权重
aggClassEst = mat(zeros((m, 1)))
for i in range(numIt):
bestTree, error, preLabel = buildTree(data, label, D) # 生成一个单层决策树
alpha = float(0.5 * log((1.0 - error) / max(error, 1e-16)))  # max()防止分母0,公式1:更新alpha
bestTree['alpha'] = alpha
weakClassArr.append(bestTree)    # 弱分类器加入带有alpha的最优树
expon = multiply(-1 * alpha * mat(label).T, preLabel)  # 公式2:样本权重更新 见公式2详解
D = multiply(D, exp(expon)) / D.sum()
aggClassEst += alpha * preLabel  # 运行时的类别估计值 sign函数处理之前
aggErrors = multiply(sign(aggClassEst) != mat(label).T, ones((m, 1)))  # 预测label的误差和
errorRate = aggErrors.sum() / m  # 预测正确为0 不正确为1 再乘以one矩阵得到矩阵
''' if U have some questions '''
# print "loop number is :", i
# print "label :", label
# print "preLabel :", preLabel.T
# print "expon :", expon.T
# print "alpha :", alpha
# print "D :", D.T
# print "aggClassEst :", aggClassEst.T
# print "sign(aggClassEst) :",sign(aggClassEst)
# print "aggClassEst != label :",sign(aggClassEst) != mat(label).T
# print "aggErrors :", aggErrors.T
# print "errorRate :", errorRate, "\n----------------------------------------------"
''' have a look above '''
if errorRate == 0.0: break
# print "numIt is :", numIt,"\ntrainError rate is :",errorRate
return weakClassArr

def AdaBoostClassify(data, classifierArr): # classifierArr 就是所有单层决策树的列表
data = mat(data)
m = shape(data)[0]
aggClassEst = mat(zeros((m, 1)))
# print "len",len(classifierArr)
for i in range(len(classifierArr)):
labelPre = sigTreeClassify(data, classifierArr[i]['index'], classifierArr[i]['value'], classifierArr[i]['sign'])
aggClassEst += classifierArr[i]['alpha'] * labelPre
''' have a look '''
# print "loopNum is :",i
# print "labelPre :", labelPre.T
# print "aggClassEst :",aggClassEst.T
# print "sign(aggClassEst) :",sign(aggClassEst).T
''' ok '''
return sign(aggClassEst)  # -1 ~ 1

if __name__ == '__main__':
data, label = loadSimpleData()  # 加载简单数据集
''' 建单层决策树验证 '''
D = mat(ones((shape(data)[0], 1)) / shape(data)[0])
bestTree, minError, bestLabel = buildTree(data, label, D)
print "bestTree :", bestTree, '\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++'
''' 简单数据训练+预测 '''
classifierArr = AdaBoostTrainDS(data, label, 10)  # 训练模型
data = matrix([
[1.0, 2.1],
[2.0, 1.1],
[2.0, 1.0],
[1.0, 1.0],
[1.3, 1.0],
[0.8, 1.4],
[1.0, 0.5]
])
label = [1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0]
labelPre = AdaBoostClassify(data, classifierArr)  # 进行预测
print "label    :", mat(label)
print "prelabel :", labelPre.T, '\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++'
''' 复杂数据训练+预测 '''
trainData, trainLabel = loadData("horseTrain.txt")
testData, testLabel = loadData("horseTest.txt")
m = shape(testData)[0]
for numIt in [1, 10, 50, 100, 500, 1000, 10000]:
classifierArr = AdaBoostTrainDS(trainData, trainLabel, numIt) # 训练模型
labelPre = AdaBoostClassify(testData, classifierArr)          # 进行预测
errArr = mat(ones((m, 1)))
errorSum = errArr[labelPre != mat(testLabel).T].sum()
print "testError rate is :", 1.0 * errorSum / 67, '\n----------------------------------------'


5. 结果分析 以及 辅助理解图像

# 输出结果
# 如详细理解过程可以在代码中撤销注释获得打印输出
'''
bestTree : {'index': 0, 'value': 1.3, 'sign': 'small'}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
label    : [[ 1.  1.  1. -1. -1.  1. -1.]]
prelabel : [[ 1.  1.  1. -1. -1. -1. -1.]]
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
numIt is : 1
trainError rate is : 0.284280936455
testError rate is : 0.268656716418
----------------------------------------
numIt is : 10
trainError rate is : 0.234113712375
testError rate is : 0.238805970149
----------------------------------------
numIt is : 50
trainError rate is : 0.207357859532
testError rate is : 0.208955223881
----------------------------------------
numIt is : 100
trainError rate is : 0.1872909699
testError rate is : 0.238805970149
----------------------------------------
numIt is : 500
trainError rate is : 0.157190635452
testError rate is : 0.253731343284
----------------------------------------
numIt is : 1000
trainError rate is : 0.133779264214
testError rate is : 0.268656716418
----------------------------------------
numIt is : 10000
trainError rate is : 0.110367892977
testError rate is : 0.328358208955
----------------------------------------
'''

# 分析
'''
第一个栗子中 打印了最优DS的分类特征,分类阀值,分类符号小于号
第二个栗子中 很显然最后一个预测失误了
第三个栗子中 numIt从1~10000过程中,对于训练数据拟合越来越好,而相对于测试数据在出现最优错误率之后又下降了,很显然的过拟合....(相关文献证明AdaBoost错误率回达到一个稳定值,不会随着分类器的个数增加而变优)
'''


简单数据集的坐标图像



6. 附 数据集

数据集 请点击这里

元算法介绍

AdaBoost思想 以及 涉及公式
1 简单理解

1 公式

主要算法伪代码

代码 以及 几个小栗子

结果分析 以及 辅助理解图像

附 数据集
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 机器学习