zoukankan      html  css  js  c++  java
  • 决策树(ID3、C4.5、CART算法numpy实现)

    什么是决策树?

    决策树(decision tree)是一个树结构(可以是二叉树或非二叉树)。

    其每个非叶节点表示一个特征属性上的测试,每个分支代表这个特征属性在某个值域上的输出,而每个叶节点存放一个类别。

    使用决策树进行决策的过程就是从根节点开始,测试待分类项中相应的特征属性,并按照其值选择输出分支,直到到达叶子节点,将叶子节点存放的类别作为决策结果

     

     

     

     

     

     

     

     

     

    图片来源

    如何建树?

     建树的关键在于特征选择,我们应当选择怎么样的特征作为我们的判断条件。

    大致有三种算法:

    1、ID3算法:基于信息增益准则选择特征

    2、C4.5算法:基于信息增益比来选择特征

    3、CART算法:基于基尼指数来选择特征

    1、ID3算法

    如何计算信息增益:

    K是分类值y的种类,|  |表示样本数量,n表示特征A所有可能的取值(这句话的意思是枚举所有在训练集出现过的值,即使在某层中该取值的样本数为0)。

    例如:(图片来源

    ID3算法流程如下:

     

    代码实现:

    使用信息增益进行特征选择的缺点:偏向于选择取值较多的特征,可以理解为倾向划分更多的子树

      1 import numpy as np
      2 from math import log2 as log
      3 class TreeNode(object):
      4     def __init__(self,X,Y,S):
      5         self.label = None
      6         self.divide_dim = None
      7         self.X = X
      8         self.Y = Y
      9         self.children = {}
     10         self.is_leaf = True
     11         self.source =S #为了display函数
     12         self.id = None #节点ID,确保唯一
     13         self.child_ids =[]
     14 
     15 class DecisionTree(object):
     16     def __init__(self,X,Y,function='ID3'):
     17         self.X = X
     18         self.Y = Y
     19         self.F = function
     20         self.num_features = len(X[0]) #特征数
     21         self.feature_map = {} #特征映射为int值
     22         self.maps = {} #每个特征中对应的值的映射,例如第二个特征,有一个叫‘是’,那么maps[1]['是']=0/1
     23         self.label_map = {} #y 标签映射为数值
     24         self.root = None
     25         self.epsilon = 0
     26         self.inv_maps={}
     27         self.ID = 1
     28 
     29     def train(self):
     30         # train函数主要是处理数据,把所有特征转化为数值型,方便使用numpy处理
     31         X = self.X
     32         Y = self.Y
     33         # X row = Y row +1
     34         # 默认X的第一行为特征描述
     35         features = X[0]
     36         xx = np.zeros([len(X),self.num_features],dtype=int)
     37         yy = np.zeros(len(Y),dtype=int)
     38         for feature,j in zip(features,range(self.num_features)):
     39             self.feature_map[feature] = j
     40             self.maps[j] ={}
     41             self.inv_maps[j]={}
     42             for i in range(1,len(X)):
     43                 k = X[i][j]
     44                 if k in self.maps[j].keys():
     45                     xx[i][j] = self.maps[j][k]
     46                 else:
     47                     self.maps[j][k] = len(self.maps[j])
     48                     xx[i][j] =self.maps[j][k]
     49                     self.inv_maps[j][xx[i][j]]=k
     50 
     51         for i in range(len(Y)):
     52             k= Y[i]
     53             if k in self.label_map.keys():
     54                 yy[i] = self.label_map[k]
     55             else:
     56                 self.label_map[k] = len(self.label_map)
     57                 yy[i] = self.label_map[k]
     58         xx = xx[1:]
     59         X = X[1:]
     60         # xx , yy all int
     61         self.root = TreeNode(xx,yy,xx)
     62         self.root.id = self.ID
     63         self.ID = self.ID+1
     64         print(self.root.X)
     65         print(self.root.Y)
     66         self.build_tree(self.root)
     67 
     68     def get_information_gain(self,X,Y,dim,idx):
     69         # dim 表示特征索引
     70         nums = X.shape[0]
     71         cnt_class = len(self.label_map)
     72         cnt_x = len(self.maps[dim])
     73         xx = X[:,dim]
     74         yy = Y
     75         hd =0
     76         for v in range(cnt_class):
     77             nums_v = np.where(yy==v,1,0).sum()
     78             if nums_v >0 :
     79                 hd = hd - nums_v*log(nums_v/nums)/nums
     80         hda = 0
     81         for i in range(cnt_x):
     82             di = np.where(xx==i,1,0).sum()
     83             if di>0:
     84                 tmpy=yy[np.where(xx==i,True,False)]
     85                 for k in range(cnt_class):
     86                     dik = np.where(tmpy== k,1,0).sum()
     87                     if dik >0:
     88                         hda = hda - (di/nums)*(dik/di)*log(dik/di)
     89         # 显示当前节点计算的指定维度的信息增益
     90         inv_feature_map = dict(zip(self.feature_map.values(), self.feature_map.keys()))
     91         print('当前节点序号: ', idx, ' 特征: ', inv_feature_map[dim])
     92         print('information gain', hd - hda)
     93         return hd-hda
     94 
     95     def build_tree(self,root):
     96         if self.check_all_y(root.Y): #所有实例属于同一类
     97             root.is_leaf=True
     98             root.label = root.Y[0]
     99         elif self.check_X_null(root.X): #特征为空,无法进一步选择划分,标记为-1的列即表示已经使用过
    100             root.is_leaf=True
    101             root.label = self.get_max_y(root.Y)
    102         else:
    103             epsilon = self.epsilon #epsilon
    104             mx_dim = -1 #选取信息增益最大的作为特征维度
    105             for dim in self.maps.keys():
    106                 if root.X[0,dim] != -1:
    107                     gda = self.get_information_gain(root.X,root.Y,dim,root.id)
    108                     if gda > epsilon:
    109                         epsilon =gda
    110                         mx_dim=dim
    111             if mx_dim == -1:
    112                 root.is_leaf = True
    113                 root.label = self.get_max_y(root.Y)
    114             else:
    115                 root.divide_dim = mx_dim
    116                 root.is_leaf = False
    117                 root.label = self.get_max_y(root.Y)
    118                 xx = root.X
    119                 yy = root.Y
    120                 for i in range(len(self.maps[mx_dim])):
    121                     tmpx = xx[np.where(xx[:,mx_dim]==i,True,False)]
    122                     tmps = root.source[np.where(xx[:, mx_dim] == i, True, False)]
    123                     tmpx[:,mx_dim] =-1  #将使用过的特征标记为-1
    124                     tmpy = yy[np.where(xx[:,mx_dim]==i,True,False)]
    125                     child = TreeNode(tmpx,tmpy,tmps)
    126                     child.id = self.ID
    127                     self.ID = self.ID + 1
    128                     root.child_ids.append(child.id)
    129                     root.children[i] = child
    130                     if tmpx.shape[0] == 0: #如果为空集,子节点的类别和当前节点保持一致
    131                         child.is_leaf=True
    132                         child.label = root.label
    133                     else:
    134                         child.is_leaf=False
    135                         child.X = tmpx
    136                         child.Y = tmpy
    137                         self.build_tree(child)
    138         pass
    139     def check_all_y(self,Y):
    140         yy = Y - Y[0]
    141         if np.where(yy==0,0,1).sum()==0:
    142             return True
    143         else:
    144             return False
    145     def check_X_null(self,X):
    146         if np.where(X==-1,0,1).sum()==0:
    147             return True
    148         else:
    149             return False
    150     def get_max_y(self,Y): #选取最大类别
    151         mx = 0
    152         for k in range(len(self.label_map)):
    153             dk = np.where(Y == k, 1, 0).sum()
    154             if mx < dk:
    155                 label = k
    156                 mx = dk
    157         return label
    158 
    159     def display(self):
    160         mp = dict(zip(self.label_map.values(),self.label_map.keys()))
    161         inv_feature_map = dict(zip(self.feature_map.values(), self.feature_map.keys()))
    162         q= []
    163         q.append(self.root)
    164         while len(q)>0:
    165             root = q[0]
    166             c= q.pop(0)
    167             if root.is_leaf:
    168                 print(root.id, mp[root.label],' is leaf')
    169                 ss = []
    170                 for idx in range(root.source.shape[0]):
    171                     s = root.source[idx]
    172                     sen = []
    173                     for i in range(s.shape[0]):
    174                        sen.append(self.inv_maps[i][s[i]])
    175                     ss.append(sen)
    176                 print(ss)
    177             else :
    178                 print(root.id, 'divide dim ', inv_feature_map[root.divide_dim], '*' * 20)
    179                 print(root.child_ids)
    180                 ss = []
    181                 for idx in range(root.source.shape[0]):
    182                     s = root.source[idx]
    183                     sen = []
    184                     for i in range(s.shape[0]):
    185                         sen.append(self.inv_maps[i][s[i]])
    186                     ss.append(sen)
    187                 print(ss)
    188                 for i in range(len(self.maps[root.divide_dim])):
    189                     q.append(root.children[i])
    190         pass
    191 
    192 
    193 
    194 X = [['色泽','根蒂','敲声','纹理','脐部','触感'],
    195      ['青绿','蜷缩','沉闷','清晰','凹陷','硬滑'],
    196      ['浅白','蜷缩','浊响','清晰','凹陷','硬滑'],
    197      ['乌黑','稍蜷','浊响','清晰','稍凹','硬滑'],
    198      ['乌黑','稍蜷','沉闷','稍糊','稍凹','硬滑'],
    199      ['浅白','硬挺','清脆','模糊','平坦','硬滑'],
    200      ['浅白','蜷缩','浊响','模糊','平坦','软粘'],
    201      ['青绿','稍蜷','浊响','稍糊','凹陷','硬滑']]
    202 y = ['','','','','','','']
    203 dt = DecisionTree(X,y)
    204 dt.train()
    205 dt.display()
    View Code

     2、C4.5算法

    按信息增益比来选择特征:

    缺点:偏向于选择取值数目较少的特征,因为HA(D)其实可以认为把特征A的取值看做一个随机变量。要让信息增益比尽可能大,会倾向选择特征取值的不确定度尽可能低的特征。比如特征A只有两个取值,特征B有三种取值,假设取值是均匀分布,那么经过计算HA(D)  < HB(D),说明模型倾向选择不确定度小的特征,假设是取值是均匀分布,那么极有可能是选择取值数目较少的特征

    算法流程:

     C4.5 [Quinlan, 1993]使用了一个启发式方法:先从候选划分属性中找出信息增益高于平均水平的属性,再从中选取信息增益比最高的。
    本人代码实现并没有用启发式的方法。C4.5的代码和ID3差不多。

      1 import numpy as np
      2 from math import log2 as log
      3 class TreeNode(object):
      4     def __init__(self,X,Y,S):
      5         self.label = None
      6         self.divide_dim = None
      7         self.X = X
      8         self.Y = Y
      9         self.children = {}
     10         self.is_leaf = True
     11         self.source =S #为了display函数
     12         self.id = None #节点ID,确保唯一
     13         self.child_ids =[]
     14 
     15 class DecisionTree(object):
     16     def __init__(self,X,Y,function='C4.5'):
     17         self.X = X
     18         self.Y = Y
     19         self.F = function
     20         self.num_features = len(X[0]) #特征数
     21         self.feature_map = {} #特征映射为int值
     22         self.maps = {} #每个特征中对应的值的映射,例如第二个特征,有一个叫‘是’,那么maps[1]['是']=0/1
     23         self.label_map = {} #y 标签映射为数值
     24         self.root = None
     25         self.epsilon = 0
     26         self.inv_maps={}
     27         self.ID = 1
     28 
     29     def train(self):
     30         # train函数主要是处理数据,把所有特征转化为数值型,方便使用numpy处理
     31         X = self.X
     32         Y = self.Y
     33         # X row = Y row +1
     34         # 默认X的第一行为特征描述
     35         features = X[0]
     36         xx = np.zeros([len(X),self.num_features],dtype=int)
     37         yy = np.zeros(len(Y),dtype=int)
     38         for feature,j in zip(features,range(self.num_features)):
     39             self.feature_map[feature] = j
     40             self.maps[j] ={}
     41             self.inv_maps[j]={}
     42             for i in range(1,len(X)):
     43                 k = X[i][j]
     44                 if k in self.maps[j].keys():
     45                     xx[i][j] = self.maps[j][k]
     46                 else:
     47                     self.maps[j][k] = len(self.maps[j])
     48                     xx[i][j] =self.maps[j][k]
     49                     self.inv_maps[j][xx[i][j]]=k
     50 
     51         for i in range(len(Y)):
     52             k= Y[i]
     53             if k in self.label_map.keys():
     54                 yy[i] = self.label_map[k]
     55             else:
     56                 self.label_map[k] = len(self.label_map)
     57                 yy[i] = self.label_map[k]
     58         xx = xx[1:]
     59         X = X[1:]
     60         # xx , yy all int
     61         self.root = TreeNode(xx,yy,xx)
     62         self.root.id = self.ID
     63         self.ID = self.ID+1
     64         print(self.root.X)
     65         print(self.root.Y)
     66         self.build_tree(self.root)
     67 
     68     def get_information_gain_rate(self,X,Y,dim,idx):
     69         # dim 表示特征索引
     70         nums = X.shape[0]
     71         cnt_class = len(self.label_map)
     72         cnt_x = len(self.maps[dim])
     73         xx = X[:,dim]
     74         yy = Y
     75         hd =0
     76         for v in range(cnt_class):
     77             nums_v = np.where(yy==v,1,0).sum()
     78             if nums_v >0 :
     79                 hd = hd - nums_v*log(nums_v/nums)/nums
     80         hda = 0
     81         had = 0
     82         for i in range(cnt_x):
     83             di = np.where(xx==i,1,0).sum()
     84             if di>0:
     85                 had = had - (di/nums)*log(di/nums)
     86                 tmpy=yy[np.where(xx==i,True,False)]
     87                 for k in range(cnt_class):
     88                     dik = np.where(tmpy== k,1,0).sum()
     89                     if dik >0:
     90                         hda = hda - (di/nums)*(dik/di)*log(dik/di)
     91         ## 显示计算的指定维度的信息增益和信息增益比
     92         inv_feature_map = dict(zip(self.feature_map.values(), self.feature_map.keys()))
     93         print('当前节点序号: ',idx,' 特征: ',inv_feature_map[dim])
     94         print('information gain',hd-hda)
     95         if had == 0:
     96             print('igr: ',(hd-hda)/1e-9)
     97             return (hd-hda)/1e-9
     98         else :
     99             print('igr: ', (hd - hda) / had)
    100             return (hd-hda)/had
    101 
    102     def build_tree(self,root):
    103         if self.check_all_y(root.Y): #所有实例属于同一类
    104             root.is_leaf=True
    105             root.label = root.Y[0]
    106         elif self.check_X_null(root.X): #特征为空,无法进一步选择划分,标记为-1的列即表示已经使用过
    107             root.is_leaf=True
    108             root.label = self.get_max_y(root.Y)
    109         else:
    110             epsilon = self.epsilon #epsilon
    111             mx_dim = -1 #选取信息增益比最大的作为特征维度
    112             for dim in self.maps.keys():
    113                 if root.X[0,dim] != -1:
    114                     gda = self.get_information_gain_rate(root.X,root.Y,dim,root.id)
    115                     if gda > epsilon:
    116                         epsilon =gda
    117                         mx_dim=dim
    118             if mx_dim == -1:
    119                 root.is_leaf = True
    120                 root.label = self.get_max_y(root.Y)
    121             else:
    122                 root.divide_dim = mx_dim
    123                 root.is_leaf = False
    124                 root.label = self.get_max_y(root.Y)
    125                 xx = root.X
    126                 yy = root.Y
    127                 for i in range(len(self.maps[mx_dim])):
    128                     tmpx = xx[np.where(xx[:,mx_dim]==i,True,False)]
    129                     tmps = root.source[np.where(xx[:, mx_dim] == i, True, False)]
    130                     tmpx[:,mx_dim] =-1  #将使用过的特征标记为-1
    131                     tmpy = yy[np.where(xx[:,mx_dim]==i,True,False)]
    132                     child = TreeNode(tmpx,tmpy,tmps)
    133                     child.id = self.ID
    134                     self.ID = self.ID + 1
    135                     root.child_ids.append(child.id)
    136                     root.children[i] = child
    137                     if tmpx.shape[0] == 0: #如果为空集,子节点的类别和当前节点保持一致
    138                         child.is_leaf=True
    139                         child.label = root.label
    140                     else:
    141                         child.is_leaf=False
    142                         child.X = tmpx
    143                         child.Y = tmpy
    144                         self.build_tree(child)
    145         pass
    146     def check_all_y(self,Y):
    147         yy = Y - Y[0]
    148         if np.where(yy==0,0,1).sum()==0:
    149             return True
    150         else:
    151             return False
    152     def check_X_null(self,X):
    153         if np.where(X==-1,0,1).sum()==0:
    154             return True
    155         else:
    156             return False
    157     def get_max_y(self,Y): #选取最大类别
    158         mx = 0
    159         for k in range(len(self.label_map)):
    160             dk = np.where(Y == k, 1, 0).sum()
    161             if mx < dk:
    162                 label = k
    163                 mx = dk
    164         return label
    165 
    166     def display(self):
    167         mp = dict(zip(self.label_map.values(),self.label_map.keys()))
    168         inv_feature_map = dict(zip(self.feature_map.values(), self.feature_map.keys()))
    169         q= []
    170         q.append(self.root)
    171         while len(q)>0:
    172             root = q[0]
    173             c= q.pop(0)
    174             if root.is_leaf:
    175                 print(root.id, mp[root.label],' is leaf')
    176                 ss = []
    177                 for idx in range(root.source.shape[0]):
    178                     s = root.source[idx]
    179                     sen = []
    180                     for i in range(s.shape[0]):
    181                        sen.append(self.inv_maps[i][s[i]])
    182                     ss.append(sen)
    183                 print(ss)
    184             else :
    185                 print(root.id,'divide dim ',inv_feature_map[root.divide_dim],'*'*20)
    186                 print(root.child_ids)
    187                 ss = []
    188                 for idx in range(root.source.shape[0]):
    189                     s = root.source[idx]
    190                     sen = []
    191                     for i in range(s.shape[0]):
    192                         sen.append(self.inv_maps[i][s[i]])
    193                     ss.append(sen)
    194                 print(ss)
    195                 for i in range(len(self.maps[root.divide_dim])):
    196                     q.append(root.children[i])
    197         pass
    198 
    199 
    200 X = [['色泽','根蒂','敲声','纹理','脐部','触感'],
    201      ['青绿','蜷缩','沉闷','清晰','凹陷','硬滑'],
    202      ['浅白','蜷缩','浊响','清晰','凹陷','硬滑'],
    203      ['乌黑','稍蜷','浊响','清晰','稍凹','硬滑'],
    204      ['乌黑','稍蜷','沉闷','稍糊','稍凹','硬滑'],
    205      ['浅白','硬挺','清脆','模糊','平坦','硬滑'],
    206      ['浅白','蜷缩','浊响','模糊','平坦','软粘'],
    207      ['青绿','稍蜷','浊响','稍糊','凹陷','硬滑']]
    208 y = ['','','','','','','']
    209 dt = DecisionTree(X,y)
    210 dt.train()
    211 dt.display()
    View Code

     3、CART(Classification And Regression Tree)算法

    本文仅做了分类树的实现。

    CART算法得到的决策树是一棵二叉树。

    特征选择使用的是基尼指数(Gini index)最小化原则

          

     

     CART分类树的算法流程:

     

     算法的终止条件是结点中的样本数少于某个阈值,或者样本集的基尼指数小于预定值(样本基本属于同一类),或者没有更多特征。

    本文没有考虑第一个终止特征,使用了和ID3、C4.5一样的终止条件

    关于决策树的剪枝,笔者时间不充裕,有空再补一篇。

    代码如下:

      1 import numpy as np
      2 from math import log2 as log
      3 class TreeNode(object):
      4     def __init__(self,X,Y,S):
      5         self.label = None
      6         self.divide_dim = None
      7         self.X = X
      8         self.Y = Y
      9         self.children = {}
     10         self.is_leaf = True
     11         self.source =S #为了display函数,显示X设为-1的值
     12         self.id = None #节点ID,确保唯一
     13         self.child_ids =[]
     14 
     15 class DecisionTree(object):
     16     def __init__(self,X,Y,function='CART'):
     17         self.X = X
     18         self.Y = Y
     19         self.F = function
     20         self.num_features = len(X[0]) #特征数
     21         self.feature_map = {} #特征映射为int值
     22         self.maps = {} #每个特征中对应的值的映射,例如第二个特征,有一个叫‘是’,那么maps[1]['是']=0/1
     23         self.label_map = {} #y 标签映射为数值
     24         self.root = None
     25         self.inv_maps={}
     26         self.ID = 1
     27 
     28     def train(self):
     29         # train函数主要是处理数据,把所有特征转化为数值型,方便使用numpy处理
     30         X = self.X
     31         Y = self.Y
     32         # X row = Y row +1
     33         # 默认X的第一行为特征描述
     34         features = X[0]
     35         xx = np.zeros([len(X),self.num_features],dtype=int)
     36         yy = np.zeros(len(Y),dtype=int)
     37         for feature,j in zip(features,range(self.num_features)):
     38             self.feature_map[feature] = j
     39             self.maps[j] ={}
     40             self.inv_maps[j]={}
     41             for i in range(1,len(X)):
     42                 k = X[i][j]
     43                 if k in self.maps[j].keys():
     44                     xx[i][j] = self.maps[j][k]
     45                 else:
     46                     self.maps[j][k] = len(self.maps[j])
     47                     xx[i][j] =self.maps[j][k]
     48                     self.inv_maps[j][xx[i][j]]=k
     49 
     50         for i in range(len(Y)):
     51             k= Y[i]
     52             if k in self.label_map.keys():
     53                 yy[i] = self.label_map[k]
     54             else:
     55                 self.label_map[k] = len(self.label_map)
     56                 yy[i] = self.label_map[k]
     57         xx = xx[1:]
     58         # xx , yy all int
     59         self.root = TreeNode(xx,yy,xx)
     60         self.root.id = self.ID
     61         self.ID = self.ID+1
     62         print(self.root.X)
     63         print(self.root.Y)
     64         self.build_tree(self.root)
     65 
     66     def get_mini_Gini_split(self,X,Y,dim,idx):
     67         #   获取当前维度最小基尼值的划分值
     68         # dim 表示特征索引
     69         nums = X.shape[0]
     70         cnt_class = len(self.label_map)
     71         cnt_x = len(self.maps[dim])
     72         xx = X[:,dim]
     73         yy = Y
     74         split_value = -1
     75         mini_gini = 1.1 #需要比最大值大一点
     76         for i in range(cnt_x):
     77             num_equal= np.where(xx==i,1,0).sum()
     78             num_diff = nums - num_equal
     79             if num_diff==0 or num_equal ==0:
     80                 continue
     81             equal_y = yy[np.where(xx==i,True,False)]
     82             diff_y = yy[np.where(xx==i,False,True)]
     83             equal_gini = 1
     84             diff_gini = 1
     85             for k in range(cnt_class):
     86                 equal_gini = equal_gini -((np.where(equal_y== k,1,0).sum())/num_equal)**2
     87                 diff_gini = diff_gini - ((np.where(diff_y==k,1,0).sum())/num_diff)**2
     88             gini = num_equal*equal_gini/nums + num_diff*diff_gini/nums
     89             if gini < mini_gini:
     90                 mini_gini=gini
     91                 split_value = i
     92         #########
     93         inv_feature_map = dict(zip(self.feature_map.values(), self.feature_map.keys()))
     94         print('当前节点序号: ', idx, ' 特征: ', inv_feature_map[dim])
     95         if split_value==-1: #说明无法找到一个分割点将样本分为两份
     96             print('当前特征无法切分')
     97             return split_value,mini_gini
     98         print(mini_gini,split_value)
     99         print('基尼值: ',mini_gini,' 分割值: ',self.inv_maps[dim][split_value])
    100 
    101         return split_value,mini_gini
    102 
    103     def build_tree(self,root):
    104         if self.check_all_y(root.Y): #所有实例属于同一类
    105             root.is_leaf=True
    106             root.label = root.Y[0]
    107         elif self.check_X_null(root.X): #特征为空,无法进一步选择划分,标记为-1的列即表示已经使用过
    108             root.is_leaf=True
    109             root.label = self.get_max_y(root.Y)
    110         else:
    111             gini = 1.1 #需要比最大值大一点
    112             mx_dim = -1
    113             split_value = -1
    114             for dim in self.maps.keys():
    115                 if root.X[0,dim] != -1:
    116                     split, mini_gini = self.get_mini_Gini_split(root.X,root.Y,dim,root.id)
    117                     #print(mx_dim,split_value,mini_gini)
    118                     if mini_gini < gini:
    119                         gini = mini_gini
    120                         mx_dim = dim
    121                         split_value = split
    122             if mx_dim == -1:
    123                 root.is_leaf = True
    124                 root.label = self.get_max_y(root.Y)
    125             else:
    126                 root.divide_dim = mx_dim
    127                 root.is_leaf = False
    128                 root.label = self.get_max_y(root.Y)
    129                 xx = root.X
    130                 yy = root.Y
    131                 flag = True #用来选择样本
    132                 for i in range(2):
    133                     tmpx = xx[np.where(xx[:,mx_dim]==split_value,flag,not flag)]
    134                     tmps = root.source[np.where(xx[:, mx_dim] == split_value, flag,not flag)]
    135                     tmpx[:,mx_dim] =-1  #将使用过的特征标记为-1
    136                     tmpy = yy[np.where(xx[:,mx_dim]==split_value,flag,not flag)]
    137                     flag=False #转换一下
    138                     child = TreeNode(tmpx,tmpy,tmps)
    139                     child.id = self.ID
    140                     self.ID = self.ID + 1
    141                     root.child_ids.append(child.id)
    142                     root.children[i] = child
    143                     if tmpx.shape[0] == 0: #如果为空集,子节点的类别和当前节点保持一致
    144                         child.is_leaf=True
    145                         child.label = root.label
    146                     else:
    147                         child.is_leaf=False
    148                         child.X = tmpx
    149                         child.Y = tmpy
    150                         self.build_tree(child)
    151             pass
    152     def check_all_y(self,Y):
    153         yy = Y - Y[0]
    154         if np.where(yy==0,0,1).sum()==0:
    155             return True
    156         else:
    157             return False
    158     def check_X_null(self,X):
    159         if np.where(X==-1,0,1).sum()==0:
    160             return True
    161         else:
    162             return False
    163     def get_max_y(self,Y): #选取最大类别
    164         mx = 0
    165         for k in range(len(self.label_map)):
    166             dk = np.where(Y == k, 1, 0).sum()
    167             if mx < dk:
    168                 label = k
    169                 mx = dk
    170         return label
    171 
    172     def display(self):
    173         mp = dict(zip(self.label_map.values(),self.label_map.keys()))
    174         inv_feature_map = dict(zip(self.feature_map.values(), self.feature_map.keys()))
    175         q= []
    176         q.append(self.root)
    177         while len(q)>0:
    178             root = q[0]
    179             c= q.pop(0)
    180             if root.is_leaf:
    181                 print(root.id, mp[root.label],' is leaf')
    182                 ss = []
    183                 for idx in range(root.source.shape[0]):
    184                     s = root.source[idx]
    185                     sen = []
    186                     for i in range(s.shape[0]):
    187                        sen.append(self.inv_maps[i][s[i]])
    188                     ss.append(sen)
    189                 print(ss)
    190             else :
    191                 print(root.id,'divide dim ',inv_feature_map[root.divide_dim],'*'*20)
    192                 print(root.child_ids)
    193                 ss = []
    194                 for idx in range(root.source.shape[0]):
    195                     s = root.source[idx]
    196                     sen = []
    197                     for i in range(s.shape[0]):
    198                         sen.append(self.inv_maps[i][s[i]])
    199                     ss.append(sen)
    200                 print(ss)
    201                 for i in range(len(root.children)):
    202                     q.append(root.children[i])
    203         pass
    204 
    205 
    206 X = [['色泽','根蒂','敲声','纹理','脐部','触感'],
    207      ['青绿','蜷缩','沉闷','清晰','凹陷','硬滑'],
    208      ['浅白','蜷缩','浊响','清晰','凹陷','硬滑'],
    209      ['乌黑','稍蜷','浊响','清晰','稍凹','硬滑'],
    210      ['乌黑','稍蜷','沉闷','稍糊','稍凹','硬滑'],
    211      ['浅白','硬挺','清脆','模糊','平坦','硬滑'],
    212      ['浅白','蜷缩','浊响','模糊','平坦','软粘'],
    213      ['青绿','稍蜷','浊响','稍糊','凹陷','硬滑']]
    214 y = ['','','','','','','']
    215 dt = DecisionTree(X,y)
    216 dt.train()
    217 dt.display()

     参考文献:

    1、周志华——《机器学习》

    2、李航——《统计学习方法》(第二版)

  • 相关阅读:
    痞子衡嵌入式:并行NAND接口标准(ONFI)及SLC Raw NAND简介
    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(6)- Bootable image格式与加载(elftosb/.bd)
    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(5)- 再聊eFUSE及其烧写方法
    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(4)- Flashloader初体验(blhost)
    Hystrix完整配置列表
    使用Redis实现UA池
    使用Redis实现延时任务(二)
    使用Redis实现延时任务(一)
    一文彻底理解Redis序列化协议,你也可以编写Redis客户端
    一个低级错误引发Netty编码解码中文异常
  • 原文地址:https://www.cnblogs.com/ISGuXing/p/13917037.html
Copyright © 2011-2022 走看看