import numpy as np
from machine_learning.lib.tree_node import Node
'''类决策树基类的实现,类其中有4个成员:root,max_depth,get_score,feature_sample_rate
其中:root记录所生成决策树的根节点,由根节点可唯一的表示这颗决策树
max_depth 记录CART算法中决策树模型的深度限制,有构造函数指定
get_score是分值函数的指针,由构造函数传入。传入方差则实现决策树回归问题的CART算法,传入熵则实现决策树分类的CART算法
feature_sample_rate是[0,1]区间的一个数,有构造函数指定,该算法在随机森林中会用到,决策树不需要
'''
class DecisionTreeBase:
def __init__(self,max_depth,get_score,feature_sample_rate=1.0):
self.max_depth=max_depth
self.get_score=get_score
self.feature_sample_rate=feature_sample_rate
def split_data(self,j,theta,X,idx):
idx1,idx2=list(),list()
for i in idx:
if X[i][j]<=theta:
idx1.append(i)
else:
idx2.append(i)
return idx1,idx2
def get_random_features(self,n):
shuffled=np.random.permutation(n)
size=int(self.feature_sample_rate*n)
return shuffled[:size]
def find_best_split(self,X,y,idx):
m,n=X.shape
best_score,best_j,best_theta=float("inf"),-1,float("inf")
best_idx1,best_idx2=list(),list()
selected_j=self.get_random_features(n) #调用get_random_features函数,按比例随机选取一组特征
for j in selected_j: #遍历所选出的特征
thetas=set([x[j] for x in X]) #对每一个特征j,对下标在idx中的所有训练数据计算出特征j的不同取值,将其作为备选阈值
for theta in thetas: # 遍历所有阈值θ
idx1,idx2=self.split_data(j,theta,X,idx) #调用split函数,按照特征值j是否大于阈值分为两部分,下标分别在idx1和idx2中
if min(len(idx1),len(idx2))==0: #判断确保分的两部分不是空集
continue
score1,score2=self.get_score(y,idx1),self.get_score(y,idx2) #每一部分用指定的get_scores函数计算其分值
w=1.0*len(idx1)/len(idx)
score=w*score1+(1-w)*score2 #计算出的两部分的分值的加权和,以此作为目标函数
if score<best_score:
best_score,best_j,best_theta=score,j,theta
best_idx1,best_idx2=idx1,idx2
return best_j,best_theta,best_idx1,best_idx2,best_score # 返回所有可行划分中目标函数最小的一组划分
'''X,y分别表示全体训练数据的特征组与标签,idx是一个下标集合。generate_tree函数
用下表再idx中的训练数据递归生成一颗不超过d的决策树,并返回生成的决策树的根节点'''
def generate_tree(self,X,y,idx,d):
r=Node() #生成节点r,作为建立决策树的根节点
if d==0 or len(idx)==1: #判断深度是否0、1,若是则不继续划分数据,r为叶节点
r.p=np.average(y[idx],axis=0) #计算下标在idx中的训练数据标签的平均值。并将其作为r的预测值
return r
# score表示最优化划分的目标函数值
j,theta,idx1,idx2,score=self.find_best_split(X,y,idx) #调用find_best_split函数,采用贪心策略来寻找最优数据划分
current_score=self.get_score(y,idx) #不做数据划分是得到的目标函数值
if score>=current_score: #如果得出划分比不划分小,说明划分可带来益处
return r
r.j,r.theta=j,theta #记录划分信息
# 递归的生成当前结点的左右儿子节点,分别对应划分出的两部分数据
r.left,r.right=self.generate_tree(X,y,idx1,d-1),self.generate_tree(X,y,idx2,d-1)
return r
'''fit函数是CART函数的入口,调用generate_tree函数生成一颗深度不超过max_depth的决策树,并将根节点存于成员root中'''
def fit(self,X,y):
self.root=self.generate_tree(X,y,range(len(X)),self.max_depth)
def get_prediction(self,r,x):
if r.left==None and r.right==None:
return r.p
if x[r.j]<=r.theta:
return self.get_prediction(r.left,x)
else:
return self.get_prediction(r.right,x)
def predict(self,X):
y=list()
for i in range(len(X)):
y.append(self.get_prediction(self.root,X[i]))
return np.array(y)
机器学习算法导论代码——decision_tree_base.py
最新推荐文章于 2022-10-19 10:50:19 发布