决策树算法实验

一、实验报告

1、 实验目的:使用C4.5和ID3算法对西瓜数据集进行分类
2、 实验要求:对比基于信息增益划分和基于信息增益率划分形成决策树的不同分类效果,处理连续值属性。
~~

二、实验内容

西瓜数据集2.0
特征主要有以下几种:色泽、根蒂、敲声、纹理、脐部、触感。这几个数值都是离散数值,每个特征共有三个离散值。在西瓜数据集3.0中多了密度和含糖量两个连续值的属性。

(一) 创建数据
1、 分别使用西瓜数据集2.0(离散)和西瓜数据集3.0(连续)
2、 调用panda库使数据集转换成DataFrame的数据格式
3、 定义一个划分数据集的函数,随机打乱索引,训练集和测试集按照设置好的比例分配0.9
(二) 模型构建
基于信息增益划分:
1、 定义计算信息熵的函数,信息熵越小不纯度就越低。
2、 定义数据集最佳切分函数,遍历一遍每一个属性,在每一个属性里遍历每一个取值,计算出信息熵加和,用label那一列的熵减去加和,选择最大信息增益,也就是信息下降最快的方向,返回一个最佳特征索引值。
3、 定义切分数据集的函数,通过最佳切分函数返回最佳切分列的索引,根据这个索引,构建一个按照给定列切分数据集。
4、 定义递归构建决策树函数,递归结束的条件是:程序遍历完所有的特征列,或者每个分支下的所有实例都具有相同的分类。根据返回最佳特征,对每个分支,递归构建树。
5、 定义测试函数,对一个测试实例进行分类,返回一个标签。
6、 定义测试精度函数,对测试集进行预测,并返回预测后的结果的准确率
用训练集创建决策树,使用的是numpy里面的save()函数,直接把字典形式的决策树保存为.npy文件,调用的时候直接使用load()函数即可
测试结果为:
在这里插入图片描述

基于信息增益率的划分:
只需要在基于信息增益的基础上在数据集最佳切分函数上,选择最大信息增益,变为信息增益率,计算完信息增益再比上特征期望,测试模型结果:
C4.5对连续值进行划分:
添加一个标签属性,为1的就是连续值属性,在此基础上新增一个划分数据集的方法,当比分割点大划分,比分割点小划分;修改原来的最优划分属性选择方法,例如密度这个属性,有17个值,会产生16个分割点,计算每个分割点的信息熵,选用最大的作为分割点,进行数据集划分,在其中增加连续属性的分支;修改原来的决策树构建方法;修改原来的测试方法。

三、 结果分析

ID3算法
在决策树各个节点上对应信息增益准则选择特征,递归地构建决策树。具体方法是:从根节点开始,对节点计算所有可能的特征的信息增益,选择信息增益最大的特征作为节点的特 征,由该特征的不同取值建立子节点;再对子节点递归地调用以上方法,构建决策树;直到所有特征的信息增益均很小或没有特征可以选择为止。最后得到一个决策树。
信息增益存在容易选择特征取值个数多的问题,例如编号,每个都不同,显然不是最优的划分属性。这里可以使用信息增益率,在求得信息增益的基础上在除以一个该特征属性的熵,取值越多,熵越大,增益率就变小,可以克服这个问题。
C4.5算法
C4.5继承了ID3算法的所有优点并对ID3算的进行了改进和补充。C4.5算法采用信息增益率作为选择分支属性的标准,克服了ID3算法中信息增益选择属性时偏向选择取值多的属性的不足,并能够完成对连续属性离散化是处理。

四、算法存在的缺点和不足

1、容易导致过拟合问题

附录:

下面主要是id3算法的实现代码,树用字典表示。

import random
import pandas as pd
import numpy as np
import operator
# 选择最好的数据集划分方式
def chooseBestFeatureToSplit(dataSet):
    baseEntropy = calcShannonEnt(dataSet)
    bestInfoGain = 0.0
    max_info_gain_ratio =0.0
    bestFeature = -1
    for i in range(dataSet.shape[1]- 1):  # 对每个属性信息增益
        levels = dataSet.iloc[:,i].value_counts().index #提取提取出这一列中每个取值的个数
        newEntropy = 0.0
        split_info = 0.0
        for value in levels:  # 对每一种取值计算信息增益
            subDataSet = dataSet[dataSet.iloc[:,i] == value]
            pro = subDataSet.shape[0]/dataSet.shape[0]
         	newEntropy += pro*calcShannonEnt(subDataSet)
            split_info += -pro * np.log2(pro)
        infoGain = baseEntropy - newEntropy
        # if (split_info == 0):
        #     continue
        info_gain_ratio = infoGain / split_info
        if (infoGain > bestInfoGain):  # 选择信息增益最大的属性
            bestInfoGain = infoGain
            bestFeature = i
        # if (info_gain_ratio > max_info_gain_ratio):  # 选择值最大的信息增益率
        #     max_info_gain_ratio = info_gain_ratio
        #     bestFeature = i
    # print(f'最好的信息增益:{bestInfoGain}')
    print(f'最大的信息增益率:{bestFeature}')
    print(f"最好的特征索引{bestFeature}")
    return bestFeature
# 计算信息熵
def calcShannonEnt(dataSet):
    numEntries = dataSet.shape[0]  # 样本数
    iset = dataSet.iloc[:,-1].value_counts()
    p = iset/numEntries
    ent = (-p*np.log2(p)).sum()
    return ent


# 划分数据集,axis:按第几个属性划分,value:要返回的子集对应的属性值
def splitDataSet(dataSet, axis, value):
    col = dataSet.columns[axis]
    retDataSet = dataSet.loc[dataSet[col] == value,:].drop(col,axis = 1)
    return retDataSet
# 通过排序返回出现次数最多的类别
def majorityCnt(classList):
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys(): classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.iteritems(),
                              key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]
# 递归构建决策树
def createTree(dataSet):
    labels = list(dataSet.columns)
    classList = dataSet.iloc[:,-1].value_counts()  # 类别向量
    if classList[0] == dataSet.shape[0] or dataSet.shape[1] ==1:  # 如果只有一个类别,返回
        return classList.index[0]
    bestFeat = chooseBestFeatureToSplit(dataSet)  # 最优划分属性的索引
    bestFeatLabel = labels[bestFeat]  # 最优划分属性的标签
    myTree = {bestFeatLabel: {}}
    del labels[bestFeat]  # 已经选择的特征不再参与分类
    uniqueValue = set(dataSet.iloc[:,bestFeat])  # 该属性所有可能取值,也就是节点的分支
    for value in uniqueValue:  # 对每个分支,递归构建树
        myTree[bestFeatLabel][value] = createTree(
            splitDataSet(dataSet, bestFeat, value))
    return myTree
# 测试算法
def classify(inputTree, featLabels, testVec):
    firstStr = next(iter(inputTree))  # 根节点
    secondDict = inputTree[firstStr]#下一个索引
    featIndex = featLabels.index(firstStr)  # 跟节点对应的属性
    for key in secondDict.keys():  # 对每个分支循环
        if testVec[featIndex] == key:  # 测试样本进入某个分支
            if type(secondDict[key]) == dict:  # 该分支不是叶子节点,递归
                classLabel = classify(secondDict[key], featLabels, testVec)
            else:  # 如果是叶子, 返回结果
                classLabel = secondDict[key]
    return classLabel
def acc(train , test):
    inputTree = createTree(train)
    result = []
    labels = list(train.columns)
    for i in range(test.shape[0]):  #对测试集中每条数据进行循环
        testvec = test.iloc[i,:-1]
        classlabel = classify(inputTree,labels,testvec)
        result.append(classlabel)   #将分类结果添加到result
    test['predict']  = result   #的最后一列
    acc = (test.iloc[:,-1] == test.iloc[:,-2]).mean()
    print(f'模型预测的准确率为{acc}')
    return test
def creatdataset():
    dataset = [
        ['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '是'],
        ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '是'],
        ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '是'],
        ['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '是'],
        ['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '是'],
        ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '是'],
        ['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', '是'],
        ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', '是'],
        ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', '否'],
        ['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', '否'],
        ['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', '否'],
        ['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', '否'],
        ['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', '否'],
        ['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', '否'],
        ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '否'],
        ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', '否'],
        ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', '否']
    ]
    dataset = pd.DataFrame(dataset, columns=('色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '好瓜'))
    return  dataset
#随机划分数据
def randsolit(dataset , rate):
    l = list(dataset.index)
    random.shuffle(l)
    dataset.index = l
    n =dataset.shape[0]
    m = int(n*rate)
    train = dataset.loc[range(m),:]
    # train =dataset
    test = dataset.loc[range(m,n),:]
    dataset.index = range(dataset.shape[0])
    test.index = range(test.shape[0])
    return  train, test
if __name__ == '__main__':
    dataset = creatdataset()
	train, test = randsolit(dataset,0.9)
    Trees = createTree(train)
    acc(train,test)
    # 将创建的树保存
    # np.save('myTree3.npy', Trees)
    # 树的加载
    # read_myTree = np.load('myTree.npy', allow_pickle=True).item()
    print(Trees)    
  • 2
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值