使用增益率划分属性的决策树(C4.5)

当使用信息增益大小作为划分依据时会遇到一个问题:

当使用编号这个属性进行划分时,会将数据划分为17个分组,并且每个分组的纯度都是1,这样的样本不具有泛化能力,不能预测 新数据
——西瓜书

同时由于我们在计算信息增益时给每个分支增加了权重,这样本数多的分支结点影响更大。因此(ID3)就有点偏向数量多样本的趋势。于是昆兰在1993年 在C4.5中使用增益率代替了信息增益。
增益率说白了就是在原有的信息增益的基础上,比上属性A的’固有值‘
增 益 率 = G a i n ( D , a ) I V ( a ) 增益率= \frac{Gain(D,a)}{IV(a)} =IV(a)Gain(D,a)
其中,IV(a)就是a的‘固有值’
I V ( a ) = − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ l o g 2 ∣ D v ∣ ∣ D ∣ IV(a)=-\sum_{v=1}^V\frac{|D^v|}{|D|}log2\frac{|D^v|}{|D|} IV(a)=v=1VDDvlog2DDv
其实这个比例,我们之前计算,我们拿出Gian(D,a) 这个公式然后整合一下就会有
增 益 率 = G a i n ( D , a ) I V ( a ) = E n t ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ E n t ( D v ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ l o g 2 ∣ D v ∣ ∣ D ∣ 增益率= \frac{Gain(D,a)}{IV(a)}=\frac{Ent(D)-\sum_{v=1}^V\frac{|D^v|}{|D|}Ent(D^v)}{-\sum_{v=1}^V\frac{|D^v|}{|D|}log2\frac{|D^v|}{|D|}} =IV(a)Gain(D,a)=v=1VDDvlog2DDvEnt(D)v=1VDDvEnt(Dv)
好吧,并不能化简,那就这样吧,我们先拿出来上一期西瓜书中的决策树算法实现(ID3) 的Gian的函数

def Gain(D,a):
    G=Ent(D)
    index = static_attr.index(a)
    lis=attr_dict[a]
    temp_dict=split_data(D,a)
    sum=0
    for x in lis:
        d=np.array(temp_dict[x])
        sum+=(d.shape[0]/D.shape[0])*Ent(d)
    return G-sum

改造后

def Gain_ratio(D,a):
    G=Ent(D)
    index = static_attr.index(a)
    lis=attr_dict[a]
    temp_dict=split_data(D,a)
    sum,vi=0,0
    for x in lis:
        d=np.array(temp_dict[x])
        p=d.shape[0]/D.shape[0]#剥离出来进行复用
        vi-=p*np.log2(p)#计算a的固有值
        sum+=(p)*Ent(d)
    return G-sum,(G-sum)/vi # 以增益和增益率的形式返回

看了上面的代码,和开始分析的一样,就是两个公式的合并,但这就完美了吗?答案是否定的!基于增益率的解决方案,对取值较少的属性有所偏好
【注】这个偏好,究竟体现在哪里,我也不太清楚,以后补充吧
西瓜书:采用一种启发式:先选择高于平均水平的增益率,然后在其中挑选增益率最高的。这个方案可操作性也很强,直接求两个列表,一个存放增益,一个存放增益率,选择一下就可以了。
只需要再修改下选择最优属性的函数

原函数:

def select_opt_attr(D,d_attr):
    li=[]
    for x in d_attr:
        li.append(Gain(D,x))
    return d_attr[li.index(max(li))]

做修改:

def select_opt_attr(D,d_attr):
    gains,gain_ratios=[],[]
    for x in d_attr:
        gain,gian_ratio=Gain_ratio(D,x)
        gains.append(gain)
        gain_ratios.appnd(gain_ratio)
    # 求一个平均值,然后作为分界线 ,然后找到增益率最高的
	average=np.mean(gains)
	new=[x for x in gains if x>=average]
	if len(new)==1:return
	sort(gain_ratio,reverse=True)
	sort(new,key=gain_ratio.index,reverse=True)
    return new[0]	

源代码:

from math import fabs
import pandas as pd
import numpy as np
df=pd.read_csv("./ml2.0.csv")
# 属性集合
attr=df.columns.values.tolist()[1:]
data_org=np.array(df[attr[0:]])
static_attr=df.columns.values.tolist()[1:]#这里的属性 不改变,仅仅作为索引
attr_dict={}#用于记录每一个属性的取值

for  x in attr:
    temp=np.array(df[x])
    attr_dict[x]=set(temp)

def label_is_same(D):
    l=[D[i][-1] for i in range(len(D))]
    return len(list(set(l)))==1

def all_attr_is_same(D,d_attr):
    if D.shape[0]==0:return True #如果是空集
    for x  in d_attr:
        index=static_attr.index(x)
        for i in D[0:] :
            if i[index]!=D[0][index]:
                return False
    return True
# 统计某个属性中,正样本和负样本的比例:
def collect(D):
    if D.shape[0]==0:
        return 0.0000000001
    count= 0
    for i in D:
       count=count+1 if  i[-1]=='是' else count
    return count /(D.shape[0])

def Ent(D):
    p=collect(D)
    if (fabs(1.0 - p) < 0.000001) or (fabs(0.0 - p) < 0.000001):
        return 0 #表示完全分割开了
    return  -p*np.log2(p)-(1-p)*np.log2(1-p)


def split_data(D,a):
    index = static_attr.index(a)
    lis=attr_dict[a]
    temp_dict={}
    for i in lis:
        temp_dict[i]=[]
    for i in D:
        for at in lis:
            if i[index]==at:
                temp_dict[at].append(i)
                break;
    return temp_dict
def Gain_ratio(D,a):
    G=Ent(D)
    index = static_attr.index(a)
    lis=attr_dict[a]
    temp_dict=split_data(D,a)
    sum,vi=0,0
    for x in lis:
        d=np.array(temp_dict[x])
        p=(d.shape[0]/D.shape[0])+0.000000001#剥离出来进行复用
        vi-=p*np.log2(p)#计算a的固有值
        sum+=(p)*Ent(d)
    return G-sum,(G-sum)/vi # 以增益和增益率的形式返回


def select_opt_attr(D,d_attr):
    gains,gain_ratios=[],[]
    for x in d_attr:
        gain,gain_ratio=Gain_ratio(D,x)
        gains.append(gain)
        gain_ratios.append(gain_ratio)
    # 求一个平均值,然后作为分界线 ,然后找到增益率最高的
    average=np.mean(gains) #使用index的方式可能会有重复的啊,这是一个大问题
    new=[d_attr[gains.index(x)] for x in gains if x>=average]
    if len(new)==1:return new[0]
    static_ratios=gain_ratios[:]
    sorted(gain_ratios,reverse=True)

    new_ratios=[d_attr[static_ratios.index(x)] for x in gain_ratios]
    sorted(new,key=new_ratios.index,reverse=True)
    print(new[0])
    return new[0]

node={}
def TreeGenerate(D,d_attr,node,father):#ID3算法
    if  D.shape[0]==0:
        return
    if label_is_same(D):
        node['final']=D[-1][-1]
        return
    if len(d_attr)==0 or all_attr_is_same(D,d_attr):# 属性集为空 或者所有样本在说有属性上取值相同
        node['final']='是' if collect(D)>0.5 else '否'
        return
    #选择最优的划分属性:
    opt_attr=select_opt_attr(D,d_attr)
    d_attr.remove(opt_attr)
    #根据最优属性划分集合:
    attr_dict=split_data(D,opt_attr)
    node[opt_attr]={}
    for i in attr_dict:
        d1=np.array(attr_dict[i])
        node[opt_attr][i]={}
        TreeGenerate(d1,d_attr[:],node[opt_attr][i],i)
TreeGenerate(data_org,attr[:-1],node,"root")
print(node)

但是使用这个方法之后,稍糊部分下的分类明显变多,应该是冗余,这个问题,可能是触感它的信息增益很高,但是增益率不高,所以被淘汰了,取而代之的是更复杂的分类方式,那么这个改进到底是好还是不好呢?

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值