使用matlab实现对winedataset的决策树(C4.5)构建及可视化

goal:

实现一个决策树分类器。 分类器的性能将通过对提供的数据集进行10倍(10-fold)交叉验证来评估。 决策树和交叉验证在课程中进行了介绍。

environment

MATLAB R2019b

dataset

本次实验使用的数据集是一个葡萄酒数据集,该文件是一个逗号分隔文件(csv),该数据集通常用于评估分类算法,其中分类任务是确定葡萄酒质量是否超过7。
我们将葡萄酒质量得分映射到0和1的二元类。从0到6(含)的葡萄酒得分被映射为0,葡萄酒得分为7及以上映射到1。我们要做的是数据集执行二进制分类。
每行用12列描述一种葡萄酒:前11列描述该葡萄酒的特征(详细信息),最后一栏是葡萄酒的质量(0/1)。
1 - fixed acidity
2 - volatile acidity
3 - citric acid
4 - residual sugar
5 - chlorides
6 - free sulfur dioxide
7 - total sulfur dioxide
8 - density
9 - pH
10 - sulphates
11 - alcohol
Output variable (based on sensory data):
12 - quality (score between 0 and 10)
在这里插入图片描述
数据集获取地址:
http://archive.ics.uci.edu/ml/datasets/Wine+Quality
(顺便推荐一下这个网址,这是加利福尼亚大学的实验室数据集网站,里面也有很多其他非常优质的数据集)
(当然,如果下不下来可以评论里私聊~~)

C4.5

这里采用C4.5的方式来实现
(1) C4.5算法是决策树分类领域的一种较为经典的算法,下图展示了算法的流程:
在这里插入图片描述(2)与ID3相比主要的改进:用信息增益率来选择属性。ID3选择属性用的是子树的信息增益,这里可以用很多方法来定义信息,ID3使用的是熵(entropy, 熵是一种不纯度度量准则),也就是熵的变化值,而C4.5用的是信息增益率。
信息增益和信息增益比,这里不再展开,网络上有很多优秀的博主来对此进行介绍
在决策树构造过程中进行剪枝,因为某些具有很少元素的结点可能会使构造的决策树过拟合(Overfitting),如果不考虑这些结点可能会更好。

(3)实现:
 数据预处理:
一种的随机抽取训练集和测试集的方式:

%随机采样的方法来分开训练样本和测试样本,并分开前11个属性值和第12列的label:
winedata=csvread('ex6Data.csv',1,0,[1,0,20,11])
train_index=randperm(length(winedata),floor(length(winedata)/4*3));
traindata=winedata(train_index,:);
train_features=traindata(:,1:(size(traindata,2))-1)
train_targets=traindata(:,12)'

另一种是10折的交叉验证的方式
使用这种方法,我们将数据集随机分成10份,使用其中9份进行训练而将另外1份用作测试。该过程可以重复10次,每次使用的测试数据不同。
在这里插入图片描述(1)每一次迭代中留存其中一个桶。第一次迭代中留存桶1,第二次留存桶2,其余依此类推。
(2)用其他9个桶的信息训练分类器(第一次迭代中利用从桶2到桶10的信息训练分类器)。
(3)利用留存的数据来测试分类器并保存测试结果。
交叉验证实现:在每次循环中,去400i-400(i+1)部分行的数据集作为测试集,其他部分的数据集作为训练集。

    testdata=csvread('ex6Data.csv',400*(i-1)+1,0,[400*(i-1)+1,0,400*i,11]);
    if i==1
        traindata=csvread('ex6Data.csv',401,0,[401,0,4000,11]);
    elseif i==10
        traindata=csvread('ex6Data.csv',1,0,[1,0,3600,11]);
    else
        traindata1=csvread('ex6Data.csv',1,0,[1,0,400*(i-1),11]);
        traindata2=csvread('ex6Data.csv',400*i+1,0,[400*i+1,0,4000,11]);
        traindata=[traindata1;traindata2];
    end

说明:
与2折或3折交叉验证相比,基于10折交叉验证得到的结果可能更接近于分类器的真实性能。之所以这样,是因为每次采用90%而不是2折交叉验证中仅仅50%的数据来训练分类器。

 递归构造决策树
核心部分(完整代码见后面)

%计算当前节点的信息熵 
Inode = -sum(Pnode.*log2(Pnode));        
el= zeros(1, fea); 
%记录每个特征的信息增益率  
location= ones(1, fea)*inf;
for i = 1:fea 
%遍历每个特征
info= sum(-node.*log(eps+node)/log(2)); 
%每个特征分别计算信息熵,eps是为了防止对数为1 

el(i) = (Inode-sum(rocle.*info))/(-sum(rocle.*log(eps+rocle)/log(2))); 
%信息增益率



[~, s] = max(I);  %求所有分割点的最大信息增益率
el(i) = spl(s);  
location(i) = pe(s);
el(i) = spl(s); 

location(i) = pe(s); 
%对应特征i的划分位置就是能使信息增益最大的划分值

处理完成后,从工作区中可以看到生成的决策树以及各个变量:
在这里插入图片描述
 剪枝
%定义一个阈值(pruning)(这里为2),当达到某个节点的实例个数小于阈值时就可以停止决策树的生长。
if ((pruning > L) || (L == 1) ||(length(ale) == 1))
return
将随机采样所得到的训练样本用来构造决策树。依次对训练样本的各个特征计算信息增益率,选择具有最大信息增益率的特征作为当前样本的分割特征,对决策树进行分支。

 计算准确率
由于采取的是随机抽样,可以看到连续10次的准确率
accuracy(i,1)=cal_accuracy(test_targets,test_targets_predict1)

最后得到的结果如下:

在这里插入图片描述如果我们取100的交叉验证,可以得到100次的准确率图表如下所示,可以看到分类准备率基本在0.8以上:
在这里插入图片描述

直接以数据形式输出这课决策数,可以看到如下:
在这里插入图片描述
可视化部分:

关于决策树的可视化,中文网络上的有效生成很少,建议看matlab的官方教程:
https://ww2.mathworks.cn/help/stats/view-decision-tree.html
效果如下所示:
核心代码只有一行:

    view(t,'mode','graph') 

在这里插入图片描述部分剪枝以后:
在这里插入图片描述代码:
所有函数代码(并不是我的git)都在这里,但是main.m我做了修改,如下:

clear;
clc;
% traindata = textread('TrainData.txt');  
% testdata = textread('TestData.txt');
% winedata=textscan('WineData.txt');
winedata=csvread('winedata.csv',1,0,[1,0,4000,11]);%这里可以自己补一下csv文件的读取方法
% 

runtime=10;sum=0;
for i=1:runtime
    
%     tic;%用于计算程序运行时间
    
    %train_index=randperm(length(winedata),floor(length(winedata)/4*3));%随机采样,3/4数据作为训练样本,其余的作为测试样本
    %test_index=setdiff(linspace(1,length(winedata),length(winedata)),train_index);
    %traindata=winedata(train_index,:);%训练样本
    %testdata=winedata(test_index,:);%测试样本
    
    testdata=csvread('wineData.csv',400*(i-1)+1,0,[400*(i-1)+1,0,400*i,11]);
    if i==1
        traindata=csvread('wineData.csv',401,0,[401,0,4000,11]);
    elseif i==10
        traindata=csvread('wineData.csv',1,0,[1,0,3600,11]);
    else
        traindata1=csvread('wineData.csv',1,0,[1,0,400*(i-1),11]);
        traindata2=csvread('wineData.csv',400*i+1,0,[400*i+1,0,4000,11]);
        traindata=[traindata1;traindata2];
    end
    train_features=traindata(:,1:(size(traindata,2))-1);
    train_targets=traindata(:,12)';
    test_features=testdata(:,1:(size(traindata,2))-1);  
    test_targets=testdata(:,12)';
    test_targets_predict1 = C4_5(train_features', train_targets, test_features');  %调用C4.5算法用于分类

    t=fitrtree(train_features,train_targets');%调用CART算法用于分类
    view(t,'mode','graph') 


    %计算决策树预测的准确度
    fprintf('time=%d',i)
    accuracy(i,1)=cal_accuracy(test_targets,test_targets_predict1)
    sum=sum+accuracy(i,1);
end
sum/10

plot(accuracy(:,1));
hold on;
plot(repmat(mean(accuracy(:,1)),length(accuracy(:,1)),1),'r--');
legend('准确度','准确度均值');
title('C4.5算法分类准确度');
xlabel('测试次数');
ylabel('分类准确度');
ylim([0,1.2]);
grid;

补充1:
csvread()函数有三种使用方法:

1、M = csvread(‘filename’)
2、M = csvread(‘filename’, row, col)
3、M = csvread(‘filename’, row, col, range)

第一种方法中,直接输入文件名,将数据读到矩阵M中。这里要求csv文件中只能包含数字。

第二种方法中,除了文件名,还指定了开始读取位置的行号(row)和列号(col)。这里,行号、列号以0开始计数。也就是说,row=0, col=0表示从文件中第一个数开始读。

第三种方法中,range限定了读取的范围。range = [R1 C1 R2 C2],这里(R1,C1)是读取区域的左上角,(R2,C2)是读取区域的右下角。在使用这种方法时,要求row, col等于range中的前两项。

注意:csv文件中的空项,读到矩阵中时,会初始化为0.

补充2:矩阵的拼接
(在这个的实现中,我个人觉得使用拼接比用并和交效果更好,因为数据集中存在这重复的数据)
参考:
https://jingyan.baidu.com/article/6525d4b1a97799ac7d2e94e9.html
命令C=[A B]来拼接矩阵A和矩阵B,此类拼接为横向拼接,左边为矩阵A,右边为矩阵B。

命令C=[A,B]也可以用于矩阵的横向拼接,与上一个命令的效果相同,运行结果如下图所示。

接下来使用命令C=[A;B]来拼接矩阵A和矩阵B,此类拼接为纵向拼接,上边为矩阵A,下边为矩阵B。

本文参考
https://blog.csdn.net/lin_limin/article/details/80998689(决策树)
https://www.cnblogs.com/BlameKidd/p/9735102.html(交叉验证

http://archive.ics.uci.edu/ml/
https://jingyan.baidu.com/article/6525d4b1a97799ac7d2e94e9.html(矩阵拼接)
https://jingyan.baidu.com/article/aa6a2c149ccf240d4c19c407.html(条件语句)
https://ww2.mathworks.cn/help/stats/view-decision-tree.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章