根据CART算法使用python构建决策树
前言
之前曾经实现过可以应用在离散取值区间的简易决策树,前天突发奇想仿照sklearn的实现效果写了一个取值范围可以是连续区间的通用决策树。
如果对之前的简易决策树了解不深可以先复习一下:简易决策树地址
代码介绍
依赖包
import numpy as np
from collections import Counter
from math import log2
import matplotlib.pyplot as plt
from sklearn import datasets
在这里我们为了方便直接选用了sklearn里面的数据集(仅选用数据集,算法具体实现不依赖于sklearn)。
对于依赖包的解释也可以翻阅之前的简易决策树一文。
计算损失
def entropy(y_label):
counter = Counter(y_label)
ent = 0.0
for num in counter.values():
p = num / len(y_label)
ent += -p * log2(p)
return ent
在本博客中我们选用的是信息熵,也可以选用基尼系数。
树结点
class TreeNode:
def __init__(self, acc, imin=None, minD=None):
self.acc = list(acc) # 不同类别的精度
self.imin = imin # 最小分割的特征
self.minD = minD # 分割点
树的结点中包含的信息有:在当前结点不同类别的精度、该结点损失最小分割特征、和按照损失最小分割特征分割的损失最小的分割值。
决策树类
class DecisionTree:
def __init__(self, maxentropy=1e-10, max_depth=20, min_samples=0.1):
self.tree = {
}
self.maxentropy = maxentropy # 最大信息熵(分割条件:小于这个信息熵可以分割)
self.max_depth = max_depth # 递归树深度
self.min_samples = min_samples # 最小样本数
# 训练决策树
def fit(self, X, y):
if self.min_samples < 1 and self.min_samples > 0:
# 如果min_samples是小数则按照输入数据的数据量的比例确定min_samples,如果>1则给定的数值作为min_samples
self.min_samples *= len(X)
cols = list(range(X.shape[1]))
# 对X得每一列数据,计算分割后得信息熵
ylen = len(set(y))
self.tree = self._genTree(cols, X, y, ylen, 1)
# 递归生成决策树
def _genTree(self, cols, X, y, ylen, depth):
# 计算最小信息熵得特征
imin = cols[0] # 最下熵得列
leftemin = 100 # 最小左熵值
rightemin = 100 # 最小右熵值
minD = None
for i in cols:
coli = X[:, i] # 拿到第i个特征数据
sortedcoli = coli
sorted(sortedcoli)
divide = []
divide.append(coli[0])
for j in range(len(sortedcoli)):
# 划分分界线
if j == len(sortedcoli) - 1:
divide.append(sortedcoli[j])
else:
divide.append((sortedcoli[j] + sortedcoli[j+1]) / 2)
for d in divide:
# 选择不同特征的不同值所产生的最小信息熵
leftenti = entropy(y[coli < d])
rightenti = entropy(y[coli >= d])
if leftenti + rightenti < leftemin + rightemin:
imin = i
leftemin = leftenti
rightemin = rightenti
minD = d
# 求划分精度
coli = X[:, imin]
Acc = np.zeros(ylen)
leftAcc = np.zeros(ylen)
rightAcc = np.zeros(ylen)
for idx in set(y):
# print(y[coli < minD] == idx)
leftAcc[idx] = np.sum(y[coli < minD] == idx) / len(y[