使用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

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