Adaboost底层代码(python)——非常详细
2018-10-21 本文已影响8人
3975977c74f4
参考《机器学习实战》,代码可运行
#!/user/bin/python3
# Author: HuangCong
# -*- coding:utf-8 -*-
import numpy as np
#建立简单数据集
def loadSimpData():
datMat = np.mat([[1., 2.1],
[2., 1.1],
[1.3, 1.],
[1., 1.],
[2., 1.]])
classLabel = [1.0, 1.0, -1.0, -1.0, 1.0]
return datMat, classLabel
#通过阈值比较对数据进行分类——(特征矩阵、维度、阈值、阈值不等号)
def stumpClassify(dataMatrix, dimen, threshVal, threshIneq):
retArray = np.ones((dataMatrix.shape[0], 1)) #建立列向量[m, 1]——与标签列相对应
if threshIneq == 'lt':
retArray[dataMatrix[:, dimen] <= threshVal] = -1 #在该dimen维度的特征值小于等于阈值时,取-1
else:
retArray[dataMatrix[:, dimen] > threshVal] = -1
return retArray
#该函数会遍历stumpClassify()函数所有可能的输入值,并找到该数据集上的最佳单层树——根据数据权重向量D来定义
def buildStump(dataArr, classLabels, D):
dataMatrix = np.mat(dataArr); labelMat = np.mat(classLabels).T #classLabel向量为[1,n],需要转置
m, n = dataMatrix.shape #m个样本,n个特征
numSteps = 10.0 #用于在特征的所有可能值上进行遍历
bestStump = {} #该词典保存最佳单层决策树的相应参数
bestClasEst = np.mat(np.zeros((m, 1))) #保存最佳估计标签值,先初始化[m,1]零向量
minError = np.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) #确定阈值
predictdVals = stumpClassify(dataMatrix, i, threshVal, inequal) #进行预测
errArr = np.mat(np.ones((m, 1))) #错误矩阵,如果predictedVals值不等于labelMat中真正类别值,置为一
errArr[predictdVals == labelMat] = 0
weightedError = D.T * errArr #权重向量与错误向量相乘得到错误率
#适当打印,帮助理解函数的运行
#print("split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f"% (i, threshVal, inequal, weightedError))
if weightedError < minError: #当前错误率小于已有的最小错误率
minError = weightedError #进行更新
bestClasEst = predictdVals.copy() #保存预测值
bestStump['dim'] = i #保存维度
bestStump['thresh'] = threshVal #保存阈值
bestStump['ineq'] = inequal #保存不等号
return bestStump, minError, bestClasEst
#基于单层决策树的AdaBoost的训练过程 (数据集、类别标签、迭代次数),尾部DS代表(decision stump单层决策树)
def adaBoostTrainDS(dataArr, classLabels, numIt=40): #迭代次数是算法中唯一需要用户指定的参数
weakClassArr = [] #聚焦该分类器的所有信息,最后返回
m = dataArr.shape[0] #样本数为m
D = np.mat(np.ones((m, 1)) / m) #样本权重初始化,都相等,后续迭代中会增加错分数据的权重同时,降低正确分类数据的权重
aggClassEst = np.mat(np.zeros((m, 1))) #记录每个数据点的类别估计累计值
for i in range(numIt): #numIt次迭代
bestStump, error, classEst = buildStump(dataArr, classLabels, D)
#上一行返回利用D得到的具有最小错误率的单层决策树,同时返回最小错误率和估计的类别向量
print("D:", D.T)
#下一行alpha的计算公式可详见李航蓝本,max()函数以防发生除零错误
alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
bestStump['alpha'] = alpha #继续存入该字典——包括了分类所需要的所有信息
weakClassArr.append(bestStump) #保存信息到列表中
print("classEst: ", classEst.T) #打印类别估计值
#以下三行用于计算下一次迭代中的新的数据权重向量D,公式可见李航蓝本
expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst)
D = np.multiply(D, np.exp(expon))
D = D/D.sum()
#以下四行用于错误率累加的计算,通过aggClassEst变量保持一个运行时的类别估计值来实现
aggClassEst += alpha * classEst
print("aggClassEst: ", aggClassEst) #由于aggClassEst是浮点数,需要调用sign()函数
aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1)))
errorRate = aggErrors.sum() / m
print("errorRate: ", errorRate)
if errorRate == 0.0: #如果错误率为0,停止for循环
break
return weakClassArr #返回信息列表
#基于adaboost进行分类——(待分类样例,多个弱分类器组成的数组)
def adaClassify(dataToClass, classifierArr):
dataMatrix = np.mat(dataToClass) #首先转成numpy矩阵
m = dataMatrix.shape[0] #待分类样例的个数为m
aggClassEst = np.mat(np.zeros((m, 1))) #构建0列向量,与adaBoostTrainDS中含义一样
for i in range(len(classifierArr)): #遍历所有的弱分类器
#基于stumpClassify()对每个分类器得到一个类别的估计值
classEst = stumpClassify(dataMatrix, classifierArr[i]['dim'],
classifierArr[i]['thresh'], classifierArr[i]['ineq'])
aggClassEst += classifierArr[i]['alpha']*classEst
print(aggClassEst)
return np.sign(aggClassEst)
# #定义自适应加载函数(很有用)
# 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
if __name__ == "__main__":
dataMat, classLabels = loadSimpData()
classifierArr = adaBoostTrainDS(dataMat, classLabels, 30)
print(adaClassify([0, 0], classifierArr)) #估计数据点[0,0]的类别
print(adaClassify([[5, 5], [0, 0]], classifierArr)) #估计数据点[5,5],[0,0]的类别
以上,祝好!