决策树
决策树——西瓜书课后作业4.3改进版0.2
之前参考了一部分的代码,发现出错了,修改了错误,并且提出了一种存储方法,这种存储感觉还不是最好的,不过由于国庆前时间有限,而且课后作业要求不是非常严格,能够有一种可行的并且还有道理的就好了,当初是这么想的。
如果有更好的方法,请务必换成更好的。
本文参考的部分代码链接
https://blog.csdn.net/macunshi/article/details/80785585
改进
原先的结果出错了,一是与实际的分类不符合,二是没有保存计算结果,相当于没有做这个工作。修改了三个地方,可以正常使用了。
划分错误:
比如在纹理清晰的部分里面,对于子树密度的判断中,选择>=0.2655是错误的,应当选择密度小于0.381的作为分类,因为对于纹理清晰的子树中,直接按照0和1划分,可以得到最小的增益0,这样在计算最佳增益的时候,减掉0,才是最大的,所以不应按照0.2655划分。
按照0.2655划分的熵是
ENT(D) - (6/7log2(6/7) + 1/7log2(1/7)) - (1/1log2(1/1) + 0/0log2(0/1)) =
ENT(D) - a < ENT(D)
按照0.381划分的熵是
ENT(D) - (6/6log2(6/6) + 0/6log2(0/6)) - (2/2log2(2/2) + 0/2log2(0/2))
= ENT(D)
根据选择算法,那肯定是选择0.381划分,这个从直观上也好理解
划分错误原因:
原因1:
MATLAB计算0*log2(0)的结果并非是书上的0,而是NaN
原因2:
最为致命,在所有的算对了之后,在选择最佳增益的部分里面由于没有保存,密度的最佳划分值0.381被下一个属性——含糖率的最佳划分值0.2655给覆盖了,所以才造成计算失误。
改进:
改进1、2:
修改了上面的错误
改进3:
上面的代码实际上没有保存任何有用的信息,即代码运行完成,需要人为查看每次disp()的结果才能够知道树的结构,相当于没有创立这个树。同时当一个新的数据来的时候,没有办法判断.所以存储了树进一个cell里面,保存了结果
改进1:
%计算信息熵
function [entropy]= calculate_entropy(data)
[m,n] = size(data);
label_value = data(:,n);
label = unique(label_value);
if (size((label),1) > 1) % 加入了这一步的判断
label_number = zeros(length(label),1);
%原先是2的
%label_number(:,1) = label';
for i= 1:length(label)
label_number(i,1) = sum(label_value == label(i));
end
%计算每个类别有多少个样本,比如蜷缩里面有8个样本,蜷缩对应的值是0
label_number (:,1) = label_number(:,1) ./ m;
entropy = 0;
entropy = sum(-label_number(:,1).*log2 (label_number(:,1)));
else
entropy = 0;
end
end
改进2:
%选择最佳特征
function [best_feature,midle_data]= choose_bestfeature(data,data1)
[m,n] = size(data);
Root_entropy = calculate_entropy(data);
midle_data = 0; best_gain = 0; best_feature = 0;
% 对于每一列特征
for j=1:n-1
feature_value = unique(data(:,j));
new_entropy = 0;
for i=1:length(feature_value)
subdata=splitdata(data,j,feature_value(i,1));
[m_s,n_s]=size(subdata);
prob=m_s./m;
new_entropy = new_entropy + prob * calculate_entropy(subdata);
end
inf_gain=Root_entropy - new_entropy;
if inf_gain > best_gain
best_gain = inf_gain;
best_feature = j;
end
end
%if size(data1,2)>=2
%似乎可以不需要这个判断条件
%将middle_data给传了出来,否则会被覆盖掉的
for i=1:size(data1,2)-1
[C_best_gain ,midle_data1]=C_value_bestgain(data1,i);
if C_best_gain > best_gain
midle_data = midle_data1;
best_gain = C_best_gain;
best_feature = i+6;
end
end
%end
end
改进3:
node = cell(8,6);
global number ;
number = 0;
node = make_tree1(node,Discrete_value,Continue_value,list,father,count,zu,Father);
node1 = traverse_tree(node);
disp('最终结果:')
disp(node1)
number;
%造数加存储树
function [node,list,father,count,Father] = make_tree1(node,data,data1,list,father,count,zu,Father)
%data、data1表示离散和连续的数据,list是最后的结果存储
global number;
number = number + 1;
node{number,1} = number;
% N = 1 + count*3;
[m,n] = size(data);
[m1,n1] = size(data1);
node{number,2} = Father;
%什么时候推出递归呢?
%1当所有的训练数据用完了
%2当所有的标签都相同的时候就退出
label = data(:,n);
%same_class_num = length(find(data(:,n) == label(1,1)));
%退出递归条件
if size(unique(label),1) == 1||( n == 1 && n1 == 1)
node{number,3} = '非父结点';
node{number,4} = '叶子';
node{number,5} = '无子节点';
node{number,6} = label(1);
return;
end
node{number,4} = '结点';
node{number,5} = 'Not_yet';
node{number,6} = '继续';
[best_feature,midle_data]= choose_bestfeature(data,data1);
Father = best_feature;
node{number,3} = best_feature;
if best_feature<=6
C2D_value=ones(size(data1,1),1);
featvalue = unique(data(:,best_feature));
featvalue_num = length(featvalue);
for i=1:featvalue_num
[subdata,subdata1] = splitData1(data,data1,best_feature,featvalue(i,1),C2D_value);
[node,list,father,count] = make_tree1(node,subdata,subdata1,list,father,count,zu,Father);
end
else %best_feature>6
C2D_value=C2Dtranlate(data1,best_feature-6,midle_data);
featvalue = unique(C2D_value);
featvalue_num = length(featvalue);
for i=1:featvalue_num
[subdata,subdata1] = splitData1(data,data1,best_feature,featvalue(i,1),C2D_value);
[node,list,father,count] = make_tree1(node,subdata,subdata1,list,father,count,zu,Father);
end
end
end
%对于已经建立好的树遍历一遍,把子节点信息填入cell中
function [node] = traverse_tree(node)
for i = 1:size(node,1)
if(strcmp(node{i,5},'Not_yet'))
naotaitao = cell(1,4);
naotaitao{1,1} = '各个节点的序号是:';
num = 0;
for j = 1:size(node,1)
if(node{j,2} == node{i,3})
num = num + 1;
naotaitao{1,num} = j ;
end
end
node{i,5} = naotaitao;
end
end
end
最终结果
欢迎指正,因为这个是和同学一起讨论的,同时还有很多不足,无论程序的还是这个文档的,或者很多没有必要的地方。改完下来,就是感觉大二学的那些数据结构之类的知识又回来了部分,还是练的太少。