Files
ailearning/src/python/07.AdaBoost/adaboost.py
jiangzhonglian 2be92755ef 修改错别字
2017-03-24 23:46:36 +08:00

305 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/python
# coding:utf8
'''
Created on Nov 28, 2010
Adaboost is short for Adaptive Boosting
@author: Peter/jiangzhonglian
'''
from numpy import *
def loadSimpData():
""" 测试数据
Returns:
dataArr feature对应的数据集
labelArr feature对应的分类标签
"""
dataArr = array([[1., 2.1], [2., 1.1], [1.3, 1.], [1., 1.], [2., 1.]])
labelArr = [1.0, 1.0, -1.0, -1.0, 1.0]
return dataArr, labelArr
# general function to parse tab -delimited floats
def loadDataSet(fileName):
# get number of fields
numFeat = len(open(fileName).readline().split('\t'))
dataArr = []
labelArr = []
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]))
dataArr.append(lineArr)
labelArr.append(float(curLine[-1]))
return dataArr, labelArr
def stumpClassify(dataMat, dimen, threshVal, threshIneq):
"""stumpClassify(将数据集按照feature列的value进行 二分法切分比较来赋值分类)
Args:
dataMat Matrix数据集
dimen 特征列
threshVal 特征列要比较的值
Returns:
retArray 结果集
"""
# 默认都是1
retArray = ones((shape(dataMat)[0], 1))
# dataMat[:, dimen] 表示数据集中第dimen列的所有值
# threshIneq == 'lt'表示修改左边的值gt表示修改右边的值
# print '-----', threshIneq, dataMat[:, dimen], threshVal
if threshIneq == 'lt':
retArray[dataMat[:, dimen] <= threshVal] = -1.0
else:
retArray[dataMat[:, dimen] > threshVal] = -1.0
return retArray
def buildStump(dataArr, labelArr, D):
"""buildStump(得到决策树的模型)
Args:
dataArr 特征标签集合
labelArr 分类标签集合
D 最初的特征权重值
Returns:
bestStump 最优的分类器模型
minError 错误率
bestClasEst 训练后的结果集
"""
# 转换数据
dataMat = mat(dataArr)
labelMat = mat(labelArr).T
# m行 n列
m, n = shape(dataMat)
# 初始化数据
numSteps = 10.0
bestStump = {}
bestClasEst = mat(zeros((m, 1)))
# 初始化的最小误差为无穷大
minError = inf
# 循环所有的feature列将列切分成 若干份,每一段以最左边的点作为分类节点
for i in range(n):
rangeMin = dataMat[:, i].min()
rangeMax = dataMat[:, i].max()
# print 'rangeMin=%s, rangeMax=%s' % (rangeMin, rangeMax)
# 计算每一份的元素个数
stepSize = (rangeMax-rangeMin)/numSteps
# 例如: 4=(10-1)/2 那么 1-4(-1次) 1(0次) 1+1*4(1次) 1+2*4(2次)
# 所以: 循环 -1/0/1/2
for j in range(-1, int(numSteps)+1):
# go over less than and greater than
for inequal in ['lt', 'gt']:
# 如果是-1那么得到rangeMin-stepSize; 如果是numSteps那么得到rangeMax
threshVal = (rangeMin + float(j) * stepSize)
# 对单层决策树进行简单分类,得到预测的分类值
predictedVals = stumpClassify(dataMat, i, threshVal, inequal)
# print predictedVals
errArr = mat(ones((m, 1)))
# 正确为0错误为1
errArr[predictedVals == labelMat] = 0
# 计算 平均每个特征的概率0.2*错误概率的总和为多少,就知道错误率多高
# 例如: 一个都没错,那么错误率= 0.2*0=0 5个都错那么错误率= 0.2*5=1 只错3个那么错误率= 0.2*3=0.6
weightedError = D.T*errArr
'''
dim 表示 feature列
threshVal 表示树的分界值
inequal 表示计算树左右颠倒的错误率的情况
weightedError 表示整体结果的错误率
bestClasEst 预测的最优结果
'''
# print "split: dim %d, thresh %.2f, thresh ineqal: %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
# bestStump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少
return bestStump, minError, bestClasEst
def adaBoostTrainDS(dataArr, labelArr, numIt=40):
"""adaBoostTrainDS(adaBoost训练过程放大)
Args:
dataArr 特征标签集合
labelArr 分类标签集合
numIt 实例数
Returns:
weakClassArr 弱分类器的集合
aggClassEst 预测的分类结果值
"""
weakClassArr = []
m = shape(dataArr)[0]
# 初始化 D设置每个特征的权重值平均分为m份
D = mat(ones((m, 1))/m)
aggClassEst = mat(zeros((m, 1)))
for i in range(numIt):
# 得到决策树的模型
bestStump, error, classEst = buildStump(dataArr, labelArr, D)
# alpha目的主要是计算每一个分类器实例的权重(组合就是分类结果)
# 计算每个分类器的alpha权重值
alpha = float(0.5*log((1.0-error)/max(error, 1e-16)))
bestStump['alpha'] = alpha
# store Stump Params in Array
weakClassArr.append(bestStump)
# print "alpha=%s, classEst=%s, bestStump=%s, error=%s " % (alpha, classEst.T, bestStump, error)
# -1主要是下面求e的-alpha次方 如果判断正确乘积为1否则成绩为-1这样就可以算出分类的情况了
expon = multiply(-1*alpha*mat(labelArr).T, classEst)
print '\n'
print 'labelArr=', labelArr
print 'classEst=', classEst.T
print '\n'
print '乘积: ', multiply(mat(labelArr).T, classEst).T
# 判断正确的,就乘以-1否则就乘以1 为什么? 书上的公式。
print '(-1取反)预测值expon=', expon.T
# 计算e的expon次方然后计算得到一个综合的概率的值
# 结果发现: 判断错误的特征D对于的特征的权重值会变大。
D = multiply(D, exp(expon))
D = D/D.sum()
print "D: ", D.T
print '\n'
# 预测的分类结果值,在上一轮结果的基础上,进行加和操作
print '当前的分类结果:', alpha*classEst.T
aggClassEst += alpha*classEst
print "叠加后的分类结果aggClassEst: ", aggClassEst.T
# sign 判断正为1 0为0 负为-1通过最终加和的权重值判断符号。
# 结果为:错误的样本标签集合,因为是 !=,那么结果就是0 正, 1 负
aggErrors = multiply(sign(aggClassEst) != mat(labelArr).T, ones((m, 1)))
errorRate = aggErrors.sum()/m
# print "total error=%s " % (errorRate)
if errorRate == 0.0:
break
return weakClassArr, aggClassEst
def adaClassify(datToClass, classifierArr):
# do stuff similar to last aggClassEst in adaBoostTrainDS
dataMat = mat(datToClass)
m = shape(dataMat)[0]
aggClassEst = mat(zeros((m, 1)))
# 循环 多个分类器
for i in range(len(classifierArr)):
# 前提: 我们已经知道了最佳的分类器的实例
# 通过分类器来核算每一次的分类结果然后通过alpha*每一次的结果 得到最后的权重加和的值。
classEst = stumpClassify(dataMat, classifierArr[i]['dim'], classifierArr[i]['thresh'], classifierArr[i]['ineq'])
aggClassEst += classifierArr[i]['alpha']*classEst
# print aggClassEst
return sign(aggClassEst)
def plotROC(predStrengths, classLabels):
"""plotROC(打印ROC曲线并计算AUC的面积大小)
Args:
predStrengths 最终预测结果的权重值
classLabels 原始数据的分类结果集
"""
import matplotlib.pyplot as plt
# variable to calculate AUC
ySum = 0.0
# 对正样本的进行求和
numPosClas = sum(array(classLabels)==1.0)
# 正样本的概率
yStep = 1/float(numPosClas)
# 负样本的概率
xStep = 1/float(len(classLabels)-numPosClas)
# argsort函数返回的是数组值从小到大的索引值
# get sorted index, it's reverse
sortedIndicies = predStrengths.argsort()
# 开始创建模版对象
fig = plt.figure()
fig.clf()
ax = plt.subplot(111)
# cursor光标值
cur = (1.0, 1.0)
# loop through all the values, drawing a line segment at each point
for index in sortedIndicies.tolist()[0]:
if classLabels[index] == 1.0:
delX = 0
delY = yStep
else:
delX = xStep
delY = 0
ySum += cur[1]
# draw line from cur to (cur[0]-delX, cur[1]-delY)
# 画点连线 (x1, x2, y1, y2)
# print cur[0], cur[0]-delX, cur[1], cur[1]-delY
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 positive rate')
plt.ylabel('True positive rate')
plt.title('ROC curve for AdaBoost horse colic detection system')
# 设置画图的范围区间 (x1, x2, y1, y2)
ax.axis([0, 1, 0, 1])
plt.show()
'''
参考说明http://blog.csdn.net/wenyusuran/article/details/39056013
为了计算AUC我们需要对多个小矩形的面积进行累加。这些小矩形的宽度是xStep因此
可以先对所有矩形的高度进行累加最后再乘以xStep得到其总面积。所有高度的和(ySum)随
着x轴的每次移动而渐次增加。
'''
print "the Area Under the Curve is: ", ySum*xStep
if __name__ == "__main__":
# 我们要将5个点进行分类
dataArr, labelArr = loadSimpData()
print 'dataArr', dataArr, 'labelArr', labelArr
# D表示最初值对1进行均分为5份平均每一个初始的概率都为0.2
# D的目的是为了计算错误概率 weightedError = D.T*errArr
D = mat(ones((5, 1))/5)
print 'D=', D.T
bestStump, minError, bestClasEst = buildStump(dataArr, labelArr, D)
print 'bestStump=', bestStump
print 'minError=', minError
print 'bestClasEst=', bestClasEst.T
# 分类器weakClassArr
# 历史累计的分类结果集
weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, labelArr, 9)
print '\nweakClassArr=', weakClassArr, '\naggClassEst=', aggClassEst.T
"""
发现:
分类的权重值最大的值为alpha的加和最小值为-最大值
特征的权重值如果一个值误判的几率越小那么D的特征权重越少
"""
# 测试数据的分类结果, 观测aggClassEst分类的最终权重
print adaClassify([0, 0], weakClassArr).T
print adaClassify([[5, 5], [0, 0]], weakClassArr).T
# 马疝病数据集
# 训练集合
# dataArr, labelArr = loadDataSet("testData/AB_horseColicTraining2.txt")
# weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, labelArr, 40)
# print weakClassArr, '\n-----\n', aggClassEst.T
# 计算ROC下面的AUC的面积大小
# plotROC(aggClassEst.T, labelArr)
# 测试集合
# dataArrTest, labelArrTest = loadDataSet("testData/AB_horseColicTest2.txt")
# m = shape(dataArrTest)[0]
# predicting10 = adaClassify(dataArrTest, weakClassArr)
# errArr = mat(ones((m, 1)))
# # 测试:计算总样本数,错误样本数,错误率
# print m, errArr[predicting10 != mat(labelArrTest).T].sum(), errArr[predicting10 != mat(labelArrTest).T].sum()/m