前言
-
AdaBoost
属于集成学习 - 集成学习是指,以前用一个模型干的事,现在我用多个模型一块干(人多力量大)。
简单理解为:一道选择题,以前呢,都是一个人(模型)来做,正确性完全依赖这一个人(模型);现在呢,应用集成学习,还是这道选择题,我让 10 人(模型)来做,如果8人(模型)个选 A,1人(模型)选 B,1人(模型)选 C。毫无疑问,我们认为 A 更有可能为正确答案。这就是集成学习! - 集成学习分两大类(
Bagging
、Boosting
)。-
Bagging
比如分类,以前我训练一颗决策树,就用这棵树进行分类了,准确性仅仅依靠这棵树。现在呢,集成学习出现了,我们同时训练多颗树,最后输入待测试数据,每棵树都会给出一个分类结果,我们最终投票选举即可。实例: 随机森林 -
Boosting
具有顺序性,每一个分类器都是在前一个分类器的基础之上进行操作的。Boosting
系列算法里最著名算法主要有AdaBoost
算法和提升树(boosting tree)系列算法。提升树系列算法里面应用最广泛的是梯度提升树(Gradient Boosting Tree)
-
基于单层决策树构建弱分类器
import numpy as np
import matplotlib.pyplot as plt
- 加载数据
def loadSimpData():
datMat = np.mat([[1. , 2.1],
[1.5, 1.6],
[1.3, 1. ],
[1. , 1. ],
[2. , 1. ]])
classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]
return datMat, classLabels
- 读进数据集之后,现在让我们来以图像化显示一下数据集
def plotFigure():
x, y = loadSimpData()
xarr = np.array(x)
n = np.shape(x)[0]
x1 = []; y1 = []
x2 = []; y2 = []
for i in np.arange(n):
if int(y[i]) == 1:
x1.append(xarr[i,0]); y1.append(xarr[i,1])
else:
x2.append(xarr[i,0]); y2.append(xarr[i,1])
# scatter 画出散点图
plt.scatter(x1, y1, s = 30, c = 'r', marker = 's')
plt.scatter(x2, y2, s = 30, c = 'g')
plt.show()
# 画图
plotFigure()
- 单层决策树生成函数
第一个函数stumpClassify()
是通过阈值比较对数据进行分类的。所有在阈值一边的数据会分到类别-1
, 而在另外一边的数据分到类别+1
。该函数可以通过数组过滤来实现, 首先将返回数组的全部元素设置为1
, 然后将所有不满足不等式要求的元素设置为-1
。可以基于数据集中的任一元素进行比较,同时也可以将不等号在大于、小于之间切换。
# 用于测试是否有某个值小于或者大于我们正在测试的阈值
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
retArray = np.ones((np.shape(dataMatrix)[0], 1))
if threshIneq == 'lt':
retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
else:
retArray[dataMatrix[:, dimen] > threshVal] = -1.0
return retArray
第二个函数 buildStump() 将会遍历 stumpClassify() 函数所有可能输入值,并找到数据集上最佳的单层决策树。
# buildStump() 伪代码如下
将最小错误率 minError 设为正无穷
对数据集中的每一个特征(第一层循环):
对每个步长(第二层循环) :
对每个不等号(第三层循环):
建立一棵单层决策树并利用加权数据集对它进行测试
如果错误率低于 minError,则将当前单层决策树设为最佳单层决策树
返回最佳单层决策树
# 在一个加权数据集中循环,并找到具有最低错误率的单层决策树
def buildStump(dataArr, classLabels, D):
dataMatrix = np.mat(dataArr)
labelMat = np.mat(classLabels).T
m, n = np.shape(dataMatrix)
numSteps = 10.0
bestStump = {}
bestClasEst = np.mat(np.zeros((m, 1)))
minError = float('inf')
for i in range(n):
rangeMin = dataMatrix[:, i].min()
rangeMax = dataMatrix[:, i].max()
stepSize = (rangeMax - rangeMin) / numSteps
for j in range(-1, int(numSteps) + 1):
for inequal in ['lt', 'gt']:
threshVal = (rangeMin + float(j) * stepSize)
predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal)
errArr = np.mat(np.ones((m, 1)))
errArr[predictedVals == labelMat] = 0
# D是不变的, errArr 向量里面只有0、1
weightedError = D.T * errArr
#print(D.T, errArr, '\n')
#print('split: dim %d, thresh %.2f, thresh inequal: %s, the weighted error is %.3f' \
#% (i, threshVal, inequal, weightedError))
if weightedError < minError:
minError = weightedError
bestClasEst = predictedVals.copy()
bestStump['dim'] = i
bestStump['thresh'] = threshVal
bestStump['ineq'] = inequal
return bestStump, minError, bestClasEst
- 测试一下
D = np.mat(np.ones((5, 1)) / 5)
datMat, classLabels = loadSimpData()
a, b, c = buildStump(datMat, classLabels, D)
print(a)
print(b)
print(c)
完整 AdaBoost 算法的实现
# 伪代码如下:
对每次迭代:
利用 buildStump() 函数找到最佳的单层决策树
将最佳单层决策树加入到单层决策树数组
计算 alpha
计算新的权重向量 D
更新累计类别估计值
如果错误率等于 0,则退出循环
# 基于单层决策树的 AdaBoost 训练过程
def adaBoostTrainDS(dataArr, classLabels, numIt = 40):
weakClassArr = []
m = np.shape(dataArr)[0]
D = np.mat(np.ones((m, 1)) / m)
aggClassEst = np.mat(np.zeros((m, 1)))
for i in range(numIt):
bestStump, error, classEst = buildStump(dataArr, classLabels, D)
print('D: ', D.T)
alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
bestStump['alpha'] = alpha
weakClassArr.append(bestStump)
print('classEst: ', classEst.T)
expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst)
D = np.multiply(D, np.exp(expon))
D = D / D.sum()
aggClassEst += alpha * classEst
print('aggClassEst: ', aggClassEst.T)
aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1)))
errorRate = aggErrors.sum() / m
print('total error: ', errorRate, '\n')
if errorRate == 0.0:
break
return weakClassArr
- 测试一下
datMat, classLabels = loadSimpData()
classifierArray = adaBoostTrainDS(datMat, classLabels, 9)
print(classifierArray)
测试算法: 基于 AdaBoost 的分类
# AdaBoost 分类函数
def adaClassify(datToClass, classifierArr):
dataMatrix = np.mat(datToClass)
m = np.shape(dataMatrix)[0]
aggClassEst = np.mat(np.zeros((m, 1)))
for i in range(len(classifierArr)):
classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'], \
classifierArr[i]['thresh'],\
classifierArr[i]['ineq'])
aggClassEst += classifierArr[i]['alpha'] * classEst
#print(aggClassEst)
return np.sign(aggClassEst)
- 测试一下
# 加载数据
atArr, labelArr = loadSimpData()
# 训练模型
classifierArray = adaBoostTrainDS(datArr, labelArr, 30)
print(adaClassify([0, 0], classifierArray)) # [[ -1 ]]
print(adaClassify([[5, 5], [0, 0]], classifierArray))
# [[ 1.]
# [-1.]]
实例: 在一个难数据集上应用 AdaBoost
- 加载数据
def loadDataSet(fileName):
numFeat = len(open(fileName).readline().split('\t'))
dataMat = []
labelMat = []
fr = open(fileName)
for line in fr.readlines():
lineArr = []
curLine = line.strip().split('\t')
for i in range(numFeat - 1):
lineArr.append(float(curLine[i]))
dataMat.append(lineArr)
labelMat.append(float(curLine[-1]))
return dataMat, labelMat
- 训练模型
datArr, labelArr = loadDataSet('horseColicTraining2.txt')
classifierArray = adaBoostTrainDS(datArr, labelArr, 10)
# 迭代输出每一棵单层决策树(用for循环,这样形式美观)
for i in classifierArray:
print(i)
- 测试模型
testArr, testlabelArr = loadDataSet('horseColicTest2.txt')
prediction10 = adaClassify(testArr, classifierArray)
print(prediction10.shape) #(67, 1)
errArr = np.mat(np.ones((67, 1)))
errArr[prediction10 != np.mat(testlabelArr).T].sum() # 16
可以看到一共 67 个样本,测试结果为出错 16 个,错误率为 16/67 = 23.88
参考
- 集成学习原理小结