Ensemble learning集成学习:将不同的分类器组合成一个元分类器 → \rightarrow →更好的泛化性能
构建并结合多个学习器来完成学习任务.只包含同种类型的个体学习器,这样的集成是“同质”的;包含不同类型的个体学习器,这样的集成是“异质”的.
集成学习通过将多个学习器进行结合,常可获得比单一学习器显著优越的泛化性能
Bagging(套袋法):基于数据随机重抽样的分类器构建方法
一些更先进的bagging方法:随机森林
随机森林是除了样本集是有放回的采样外,属性集合也引入了随机属性选择.
随机森林简单、容易实现、计算开销小.效果能使得最终集成的泛化性能可通过个体学习器之间差异度的增加而进一步提升.
Sklearn中已经实现BaggingClassifer的相关算法,可以从ensemble子模块中导入使用
boosting方法:分类算法,其与上面提到的bagging很类似,都是采用同一种基分类器的组合方法。而与bagging不同的是,boosting是集中关注分类器错分的那些数据来获得新的分类器(降低偏差bias)
AdaBoost:一种元算法(meta-algorithm)或者集成方法(ensemble method),是对其它算法进行组合的一种方式
有人认为AdaBoost是最好的监督学习的方法
使用集成算法时,可是不同算法的集成,也可是同一算法在不同设置下的集成,还可是数据集不同部分分配不同分类器之后的集成
adaBoost的运行过程:
训练数据的每一个样本,并赋予其一个权重,这些权值构成权重向量D,维度等于数据集样本个数。
开始时,这些权重都是相等的
首先在训练数据集上训练出一个弱分类器并计算该分类器的错误率
然后在同一数据集上再次训练弱分类器,但是在第二次训练时,将会根据分类器的错误率,对数据集中样本的各个权重进行调整,分类正确的样本的权重降低,而分类错的样本权重则上升,但这些权重的总和保持不变为1.
最终的分类器会基于这些训练的弱分类器的分类错误率,分配不同的决定系数alpha,错误率低的分类器获得更高的决定系数,从而在对数据进行预测时起关键作用
AdaBoost为每个分类器都分配一个权重值alpha,这些alpha是
基于每个弱分类器的错误率计算的,其中,错误率的定义:
ϵ = 未正确分类的样本数目 所有的样本数目 \epsilon = \frac {未正确分类的样本数目}{所有的样本数目} ϵ=所有的样本数目未正确分类的样本数目
alpha的计算公式如下: α = 1 2 ln ( 1 − ϵ ϵ ) \alpha = \frac 12\ln(\frac{1-\epsilon}{\epsilon}) α=21ln(ϵ1−ϵ)
得到alpha值后,对权重向量D进行更新。若某个样本被正确分类,则该样本的权重更改为: D i t + 1 = D i t e − α S u m ( D ) D_i^{t+1} = \frac {D_i^te^{-\alpha}}{Sum(D)} Dit+1=Sum(D)Dite−α
若样本被错分,则该样本的权重更改为: D i ( t + 1 ) = D i t e α S u m ( D ) D_i^(t+1) = \frac {D_i^t e^{\alpha}}{Sum(D)} Di(t+1)=Sum(D)Diteα
计算出D后,AdaBoost开始下一轮迭代,AdaBoost会不断地重复训练和调整权重,直到训练错误率为0,或者弱分类器的数目达到了用户指定值为止。
AdaBoost的做法是:
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
"""
单层决策树分类函数
:param dataMatrix:数据矩阵
:param dimen:第dimen列,也就是第几个特征
:param threshVal:阈值
:param threshIneq:标志
:return:分类结果
"""
retArray = np.ones((dataMatrix.shape[0], 1))
if threshIneq == 'lt':
retArray[dataMatrix[:, dimen] <= threshVal] = -1.0
else:
retArray[dataMatrix[:, dimen] > threshVal] = -1.0
return retArray
通过阈值比较对数据进行分类,类别标记为:+1,-1;可以基于任一元素比较、并在大于、小于之间切换
def buildStump(dataArr, classLabels, D):
"""
找到数据集上最佳的单层决策树
:param dataArr:数据矩阵
:param classLabels:数据标签
:param D:样本权重
:return:
bestStump : 最佳单层决策树信息
minError : 最小误差
bestClasEst : 最佳的分类结果
"""
dataMatrix = np.mat(dataArr)
labelMat = np.mat(classLabels).T
row, col = dataMatrix.shape
numSteps = 10.0
bestStump = {}
bestClassEst = np.mat(np.zeros((row, 1)))
minError = np.inf
for i in range(col):
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 = (rangeMax + float(j) * stepSize)
predictedVals = stumpClassify(dataMatrix, i, threshVal, inEqual)
errArr = np.mat(np.ones((row, 1)))
errArr[predictedVals == labelMat] = 0
weightError = D.T * errArr
print("split:dim %d,thresh %.2f,thresh inEqual: %s,the weighted error is %.3f" % (
i, threshVal, inEqual, weightError))
if weightError < minError:
minError = weightError
bestClassEst = predictedVals.copy()
bestStump['dim'] = i
bestStump['thresh'] = threshVal
bestStump['ineq'] = inEqual
return bestStump, minError, bestClassEst
基于单层决策树的AdaBoost训练过程
def adaBoostTrainDS(dataArr, classLabels, numIt=40):
'''
基于单层决策树的AdaBoost训练过程
:param dataArr:数据矩阵
:param classLabels:数据标签
:param numIt: 迭代次数,弱分类器数目
:return:
weakClassArr弱分类器的组合列表
aggClassEst单层决策树的加权预测值
'''
weakClassArr = [] # 弱分类器相关信息列表
row = np.shape(dataArr)[0] # 获取数据数量
D = np.mat(np.ones((row, 1)) / row) # 权重向量,初始1/m,之和为1
aggClassEst = np.mat(np.zeros((row, 1))) # 各数据样本类别集成的估计值
for i in range(numIt):
# 构建最佳单层决策树
bestStump, error, classEst = buildStump(dataArr, classLabels, D)
alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-6))) # alpha的计算,防止没有错误时0的溢出
bestStump['alpha'] = alpha # 存储决策树的系数alpha到字典
weakClassArr.append(bestStump) # 报存当前弱分类器
print("classEst:", classEst.T)
# 预测正确为exp(-alpha),预测错误为exp(alpha)
# 即增大分类错误样本的权重,减少分类正确的数据点权重
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((row, 1)))
# 计算错误率
errorRate = aggErrors.sum() / row
print("total error:", errorRate, "\n")
# 错误率为0.0退出循环
if errorRate == 0.0:
break
# 返回弱分类器的组合列表,单层决策树的加权预测值
return weakClassArr, aggClassEst
AdaBoost分类函数
def adaClassify(dataToClass, classifierArr):
"""
AdaBoost分类函数
:param dataToClass:
:param classifierArr:
:return:
"""
# 构建数据向量或矩阵
dataMatrix = np.mat(dataToClass)
# 获取矩阵行数
m = dataMatrix.shape[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
# 通过sign函数根据结果大于或小于0预测出+1或-1
return np.sign(aggClassEst)
def loadDataSet(filename):
"""
自适应加载数据
:param filename: 文件名
:return: 数据矩阵,特征标签
"""
# 创建数据集矩阵,标签向量
dataMat = []
labelMat = []
# 获取特征数目(包括最后一类标签)
numFeat = len(open(filename).readline().split('\t'))
# 打开文件
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
def classify():
# 利用训练集训练分类器
dataArr, labelArr = loadDataSet('horseColicTraining2.txt')
# 得到训练好的分类器
classifierArray, aggClassEst = adaBoostTrainDS(dataArr, labelArr)
# 利用测试集测试分类器的分类效果
testArr, testLabelArr = loadDataSet('horseColicTest2.txt')
prediction = adaClassify(testArr, classifierArray)
# 输出错误率
num = np.shape(np.mat(testLabelArr))[1]
errArr = np.mat(np.ones((num, 1)))
error = errArr[prediction != np.mat(testLabelArr).T].sum()
errorRate = float(error) / float(num)
print("the errorRate is :%.2f" % errorRate)
plotROC(aggClassEst.T, labelArr)
绘制ROC曲线
def plotROC(predStrengths, classLabels):
"""
绘制ROC曲线
:param predStrengths: 单层决策树的加权预测值
:param classLabels: 类别标签
:return:
"""
cur = (1.0, 1.0)
ySum = 0.0
numPosClass = np.sum(np.array(classLabels) == 1.0)
yStep = 1 / float(numPosClass)
xStep = 1 / float(len(classLabels) - numPosClass)
sortedIndicies = predStrengths.argsort()
fig = plt.figure()
fig.clf()
ax = plt.subplot(111)
for index in sortedIndicies.tolist()[0]:
if classLabels[index] == 1.0:
delX = 0
delY = yStep
else:
delX = xStep
delY = 0
ax.plot([cur[0], cur[0] - delX], [cur[1], cur[1] - delY], c='b')
cur = (cur[0] - delX, cur[1] - delY)
ax.plot([0, 1], [0, 1], 'b--')
plt.xlabel('False Postive Rate')
plt.ylabel('True Postive Rate')
plt.title('ROC curve for AdaBoost Horse Colic Detection System')
ax.axis([0, 1, 0, 1])
plt.show()