机器学习(周志华) 西瓜书 第八章课后习题8.3—— Python实现

版权声明:个人原创,禁止私自转载,如需转载引用请私信联系——Zetrue_Li https://blog.csdn.net/weixin_37922777/article/details/89473668

机器学习(周志华) 西瓜书 第八章课后习题8.3—— Python实现 

  • 实验题目

从网上下载或自己编程实现AdaBoost,以不剪枝决策树为基学习器,在西瓜数据集3.0α上训练一个AdaBoost集成,并与图8.4进行比较

  • 实验原理

AdaBoost算法

若基学习器直接采用不剪枝决策树,则基本上训练后的每个决策树分类器都是趋于一致。所以为了保证个体学习器的多样性,应采用单层决策树作为基学习器,即以决策树桩作为弱学习器。

  • 实验过程

数据集获取

获取书中的西瓜数据集3.0α,并存为data_3α.txt

密度,含糖率,好瓜
0.6970000000000001,0.46,是
0.774,0.376,是
0.634,0.264,是
0.608,0.318,是
0.556,0.215,是
0.40299999999999997,0.237,是
0.48100000000000004,0.149,是
0.43700000000000006,0.21100000000000002,是
0.6659999999999999,0.091,否
0.243,0.267,否
0.245,0.057,否
0.34299999999999997,0.099,否
0.639,0.161,否
0.657,0.198,否
0.36,0.37,否
0.593,0.042,否
0.7190000000000001,0.10300000000000001,否

算法实现

因为本题没有限制一定要自己编程,所以本人嫌麻烦,就直接参考了网上上传的《机器学习Python实现AdaBoost》加上自己设计的画图函数,完成了在西瓜数据集3.0α上训练一个AdaBoost集成。不过,个人觉得这位博主的代码实现很难看懂,原本想着在其基础上修改优化,发现要改一个地方都很难,只能全盘重构,不过最终还是放弃这个想法了。其中有一个个人觉得不是很好的编程习惯就是直接利用from numpy import *,这样真的很难让阅读和重用代码的人知道具体这个函数是否在numpy库里面,而且很可能出现同名函数被覆盖的现象,所以适当提供作用域是很有必要的。不过,还是要感谢这位博主提供的实现参考。

数据集读取,做了稍稍改动,适用于txt,pandas形式读取

预测测试样本的标签

画图函数,按照书上图8.4形式,显式画出不同规模的集成及其学习器所对应的分类边界

X, Y分别是属性数据集,对应类别标签;

根据X, Y,画出在X上,Y对应的标签值;

根据每个基分类器决策树的节点属性,属性划分点,画出对应的边界线;

按照书上图8.4的格式设置绘图格式,包括x,y轴范围,标签名等;

main函数,调用上述功能函数

  • 实验结果

 

 


  • 程序清单:

import pandas as pd
from numpy import *
import matplotlib.pyplot as plt

#数据集
def loadData(filename):
    dataSet = pd.read_csv(filename).replace(['是', '否'], [1, -1])
    dataMat = mat(dataSet[['密度', '含糖率']])
    classLabels = dataSet['好瓜'].values
    return dataMat, classLabels

#比较阈值,进行分类 threshVal 是阈值,即对某个特征的分割,threshIneq为不等号,确定是大于为-还是小于为-1
def stumpClassify(dataMtrix, dimen, threshVal, threshIneq): #dim为那一列(那个属性)
    retArray = ones((shape(dataMtrix)[0],1))  #把类别都先设置为1
    if threshIneq == 'lt':
        retArray[dataMtrix[:,dimen] <= threshVal] = -1
    else:
        retArray[dataMtrix[:,dimen] > threshVal] = -1
 
    return retArray
 
#单层决策树 D为权重
def buildStump(dataArr, classLabels, D):
    dataMAtrix = mat(dataArr)
    labelMat = mat(classLabels).T
 
    m,n = shape(dataMAtrix) #行数和列数
    numSteps = 10.0 #每个特征迭代的步数
    bestStump = {} #保存最好的分类的信息
    bestClassEst = mat(zeros((m,1))) #保存最好的
    minError = 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 inequel in ['lt','gt']:
                threshVal = rangeMin + float(j)*stepSize  #每一步划分的阈值
                predictedVals = stumpClassify(dataMAtrix,i,threshVal,inequel) #预测的分类值
 
                errArr = mat(ones((m,1))) #假全错了
                errArr[predictedVals == labelMat] = 0 #分正确的变为0
                weightError = D.T*errArr #errArr为1的位置说明是分错的,则对应位子的样本的权重相加
 
                #保存信息
                if weightError < minError:
                    minError = weightError
                    bestClassEst = predictedVals.copy() #保存最好的分类预测值
                    bestStump['dim'] = i #最好的列(最好的属性)
                    bestStump['thred'] = threshVal #属性列最好的分割阈值
                    bestStump['ineq'] = inequel  #最好的符号
        return bestStump,minError,bestClassEst
 
#基于单层的决策树AdaBoost训练函数 numIt指的迭代的次数
def AdaBoost(dataArr, classLabels, numIt = 40):
    weakClassArr = [] #保存每次迭代器的信息
    m = shape(dataArr)[0]
    D = mat(ones((m,1))/float(m)) #初始化权重
    aggClassESt = mat(zeros((m,1))) #累计每次分类的结果
    for _ in range(numIt):
        bestStump, error, classEst = buildStump(dataArr,classLabels,D)
        alpha = float(0.5*log((1 - error)/max(error,1e-16))) #函数的权重
        bestStump['alpha'] = alpha  #记录权重
        weakClassArr.append(bestStump) #保存每一轮的结果信息
 
        #更新权重
        expon = multiply(-1 * alpha * mat(classLabels).T, classEst)  # 计算指数
        D = multiply(D, exp(expon))
        D = D / D.sum()  # 归一化
        #累计每个函数的预测值 (权重 * 预测的类)
        aggClassESt += alpha *classEst
        errorRate = 1.0 * sum(sign(aggClassESt) != mat(classLabels).T)/m  #预测的和真实的标签不应样的个数/总的个数
        if errorRate == 0.0:
            break  #如果提前全部分类真确,则提前停止循环
    return weakClassArr
 
#分类
def predict(datatoClass, classifierArr):#大分类的数据,训练好的分类器
    dataMatrix = mat(datatoClass)
    m = shape(dataMatrix)[0]
    aggClassEst = mat(zeros((m,1)))  #保存预测的值 m个数据m个预测值
    for i in range(len(classifierArr)): #循环遍历所有的分类器
        classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],
                                 classifierArr[i]['thred'],
                                 classifierArr[i]['ineq'])
        aggClassEst += classifierArr[i]['alpha'] * classEst #第i个分类器的预测值*他的权重
    return sign(aggClassEst)

#绘制数据集
def plotData(X, Y, clf):
    X1, X2 = [], []
    Y1, Y2 = [], []
    for data, label in zip(X, Y):
        if label > 0:
            X1.append(data[0, 0])
            Y1.append(data[0, 1])
        else:
            X2.append(data[0, 0])
            Y2.append(data[0, 1])

    x = linspace(0, 0.8, 100)
    y = linspace(0, 0.6, 100)
    for weakClasser in clf:
        # print(weakClasser.attribute, weakClasser.partition)
        z = [weakClasser['thred']] * 100
        if weakClasser['dim'] == 0:
            plt.plot(z, y) 
        else:
            plt.plot(x, z) 

    plt.scatter(X1, Y1, marker='+', label='好瓜', color='b')
    plt.scatter(X2, Y2, marker='_', label='坏瓜', color='r')

    plt.xlabel('密度')
    plt.ylabel('含糖率')
    plt.xlim(0, 0.8) # 设置x轴范围
    plt.ylim(0, 0.6) # 设置y轴范围
    plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
    plt.legend(loc='upper left')
    plt.show()

if __name__=='__main__':
    filename = 'data_3a.txt'
    dataSet, classLabels = loadData(filename)

    sizes = [3, 5, 11]
    for size in sizes:
        weakClassArr = AdaBoost(dataSet, classLabels, size)
        predictLabels = predict(dataSet, weakClassArr)

        accuracy = 0
        for y1, y2 in zip(classLabels, predictLabels):
            if y1 == y2:
                accuracy += 1

        print('Size:', size)
        print('Accuracy:', accuracy, '/', len(classLabels))
        print('')

        plotData(dataSet, classLabels, weakClassArr)

 

 

猜你喜欢

转载自blog.csdn.net/weixin_37922777/article/details/89473668
今日推荐