机器学习(周志华西瓜书) 参考答案 总目录
机器学习(周志华) 参考答案 第八章 集成学习
3.自己编程实现一个AdaBoost,以不剪枝决策树为基学习器,在西瓜数据集3.0α上训练一个AdaBoost集成,并与图8.4比较。
如果让不剪枝决策树完全伸展的话,可以直接得到0训练误差的决策树,所以这里限定决策树的高度最高为2,也就是只做一次属性划分。
AdaBoost通过改变样本的权重来生成下一个分类器。由于样本的权值不同,在决策树最优属性选择中,计算信息熵增益的时候,以及当树高达到2,对该节点进行分类时,不能直接计数,而是应该累积他们的权值。对第四章的决策树代码稍微修改,得到以下表格。由于没有很好的画图办法,所以以表格表示。
编号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 分类属性 | 阀值 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
样本 | 好 | 好 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 无 | 无 |
1 | 好 | 好 | 好 | 好 | 好 | 好 | 好 | 好 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 好 | 含糖率 | 0.126 |
2 | 好 | 好 | 好 | 好 | 好 | 好 | 好 | 好 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 好 | 密度 | 0.3815 |
3 | 好 | 好 | 好 | 好 | 好 | 坏 | 好 | 坏 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 好 | 密度 | 0.3815 |
4 | 好 | 好 | 好 | 好 | 坏 | 好 | 坏 | 好 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 含糖率 | 0.2045 |
5 | 好 | 好 | 好 | 好 | 好 | 好 | 好 | 好 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 好 | 密度 | 0.3815 |
6 | 好 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 密度 | 0.3815 |
7 | 好 | 好 | 好 | 好 | 好 | 坏 | 好 | 坏 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 好 | 密度 | 0.3815 |
8 | 好 | 好 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 密度 | 0.6365 |
9 | 好 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 含糖率 | 0.3730 |
10 | 好 | 好 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 密度 | 0.3815 |
11 | 好 | 好 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 密度 | 0.5745 |
12 | 好 | 好 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 坏 | 好 | 好 | 好 | 坏 | 坏 | 坏 | 坏 | 密度 | 0.3815 |
每一行代表当前的累积分类器,一共设置了12个分类器。
每一列代表一个样本在某累积分类器下的取值。最后两列是每个分类器分类属性与阀值(信息增益决策树)
由于不能使用颜色,用红字的表示当前分类器分类错误的项。
可以看出在8个分类器时,累计分类器已经能0误差的分类,但是有9个时候,累计分类器在第5个样本产生了错误的分类,可以看出AdaBoost在分类器较少时的波动性。当当分类器很多事,分类会趋于稳定。
下面是参考代码
global x y py res;
global tree ptr;
x = xlsread('C:\Users\icefire\Desktop\ml\西瓜3.xlsx', 'sheet1', 'A7:Q8');
y = xlsread('C:\Users\icefire\Desktop\ml\西瓜3.xlsx', 'sheet1', 'A9:Q9');
%将y映射到1,-1
y = -2*y+3;
[m,n]=size(y);
set=1:1:n;
%记录累积分类器的分类
sy=zeros(1,17);
%样本的权值,初始相同
st=ones(1,17)/17;
fenlei={'好','坏'};
shuxing={'密度','含糖率'};
%记录每次累积分类器的分类
res=cell(12,19);
%产生12个分类器
for i=1:12
tree=zeros(1,100);
ptr = 0;
py=zeros(1,17);
%生成决策树,返回根节点的最优属性与阀值
[minn, threshold]=TreeGenerate(0,set,st,1);
%根据公式计算误差与当前分类器的权值,并更改样本权值
er=sum((py~=y).*st);
if(er>=0.5)
break;
end
a=0.5*log((1-er)/er);
st=st.*exp(a*((py~=y)*2-1));
st =st /sum(st);
sy=sy+a*py;
for j=1:17
res{i,j}=fenlei{(3-sign(sy(j)))/2};
end
res{i,18}=shuxing{minn};
res{i,19}=threshold;
end
下面是精简的决策树生成代码
%{
parent:父节点编号
curset:当前的样本编号集合
st:样本权值
height:最高高度
%}
function [k, threshold]=TreeGenerate(parent,curset,st,height)
global x y py;
global tree ptr;
%新增一个节点,并记录它的父节点
ptr=ptr+1;
tree(ptr)=parent;
cur = ptr;
%递归返回情况1:当前所有样本属于同一类
n = size(curset);
n = n(2);
%计算当前y的取值分类及各有多少个 如果只有一类表示属于同一类
cury=y(curset); %通过当前样本编号从所有样本中选出当前样本
y_data=unique(cury); %集合去重
y_nums=histc(cury,y_data); %统计各个取值各多少个
if(y_nums(1)==n)
for i=1:n
py(curset(i))=y_data;
end
return;
end
%递归返回情况2:属性已经全部划分还有不同分类的样本,或者所有样本属性一致但是分类不同(这时就是有错误的样本)
if(height==2)
cst=st(curset);
tans=(sum(cst.*cury)>0)*2-1;
for i=1:n
py(curset(i))=tans;
end
return;
end
%主递归
%实现了4个最优属性选择方法,使用了相同的参数与输出:信息增益,增益率,基尼指数,对率回归
%具体参数介绍间函数具体实现
%因为是相同的参数与输出,直接该函数名就能换方法
[k, threshold] = entropy_paraselect(curset,st);
%连续属性只会分为两类 大于阀值一类 小于阀值一类 分类后继续递归
num = [1 1];
tmp_set = zeros(2,100);
for i=1:n
if(x(k,curset(i))>=threshold)
tmp_set(1,num(1))=curset(i);
num(1) = num(1)+1;
else
tmp_set(2,num(2))=curset(i);
num(2) = num(2)+1;
end
end
for i=1:2
ttmp_set = intersect(tmp_set(i,:),curset); %由于用数组存编号,会有0存在,将样本分开后与当前的样本求交集 把0去掉
TreeGenerate(cur,ttmp_set,st,height+1);
end
end
和最优分类属性代码
%{
信息增益选择
curset:当前样本集合
st:样本权值
输出
n:最优属性
threshold:连续属性返回阀值
%}
function [n, threshold] = entropy_paraselect(curset,st)
global x y;
%通过样本编号与属性获取当前需要的样本
curx = x(:,curset);
cury = y(curset);
cst = st(curset);
all = size(cury); %当前样本总数
max_ent = -100; %为ent设初值,要最大值,所以设为一个很小的数
minn=0; %记录最优的属性编号
threshold = 0;
for i=1:2
con_max_ent = -100;
con_threshold = 0;
xlist = sort(curx(i,:),2);
%计算n-1个阀值
for j=all(2):-1:2
xlist(j) = (xlist(j)+xlist(j-1))/2;
end
%从n-1个阀值中选最优 (循环过程中ent先递减后递增 其实只要后面的ent比前面的大就可以停止循环)
for j=2:all(2)
cur_ent = 0;
%计算各类正负例数
nums = zeros(2,2);
for k=1:all(2)
nums((curx(i,k)>=xlist(j))+1,(3-cury(k))/2) = nums((curx(i,k)>=xlist(j))+1,(3-cury(k))/2) + cst(k);
end
%计算ent 连续属性只分两种情况
for k=1:2
if(nums(k,1)+nums(k,2) > 0)
p=nums(k,1)/(nums(k,1)+nums(k,2));
cur_ent = cur_ent + (p*log2(p+0.00001)+(1-p)*log2(1-p+0.00001))*(nums(k,1)+nums(k,2))/all(2);
end
end
%记录该分类最优取值
if(cur_ent>con_max_ent)
con_max_ent = cur_ent;
con_threshold = xlist(j);
end
end
%如果该最优取值比当前总的最优还要优,记录
if(con_max_ent > max_ent)
max_ent=con_max_ent;
minn = i;
threshold = con_threshold;
end
end
n = minn;
end