决策树基础
决策树
定义
从根节点开始,也就是拥有全部的数据,找一个维度对根节点开始划分,
划分后希望数据整体的信息熵是最小的,
针对划分出来的两个节点,我们继续重复刚才的划分方式寻找信息熵最小的维度和阈值。
递归这个过程就形成了决策树。
特点
非参数学习算法
可以解决分类问题
天然可以解决多分类问题
非常好的可解释性
代码实现
sklearn封装的方式
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
# 学习使用数据集,去后两个维度,便于可视化
iris = datasets.load_iris()
X = iris.data[:, 2:]
y = iris.target
dt_clf = DecisionTreeClassifier(max_depth=2, criterion="entropy", random_state=42)
dt_clf.fit(X, y)
# 画图函数
def plot_decision_boundary(model, axis):
x0, x1 = np.meshgrid(
np.linspace(axis[0], axis[1], int((axis[1] - axis[0]) * 100)).reshape(-1, 1),
np.linspace(axis[2], axis[3], int((axis[3] - axis[2]) * 100)).reshape(-1, 1),
)
X_new = np.c_[x0.ravel(), x1.ravel()]
y_predict = model.predict(X_new)
zz = y_predict.reshape(x0.shape)
from matplotlib.colors import ListedColormap
custom_cmap = ListedColormap(["#EF9A9A", "#FFF59D", "#90CAF9"])
plt.contourf(x0, x1, zz, cmap=custom_cmap)
plot_decision_boundary(dt_clf, axis=[0.5, 7.5, 0, 3])
# X[y==0,0]表示样本target为0的,第一个维度,其余类推
plt.scatter(X[y == 0, 0], X[y == 0, 1])
plt.scatter(X[y == 1, 0], X[y == 1, 1])
plt.scatter(X[y == 2, 0], X[y == 2, 1])
plt.show()
信息熵(重要知识)
熵在信息论中代表:随机变量不确定度的度量。
熵越大,数据的不确定性越高;熵越小,数据的不确定性越低,公式如下:
H
=
−
∑
i
−
1
k
p
i
log
(
p
i
)
p
i
类别
i
的概率
H = -\sum^{k}_{i-1} p_{i}\log(p_{i}) \\ p_{i} 类别\ i \ 的概率
H=−i−1∑kpilog(pi)pi类别 i 的概率
如以下两组数据的随机分布如下:
{
1
3
,
1
3
,
1
3
}
H
=
−
1
3
log
1
3
−
1
3
log
1
3
−
1
3
log
1
3
=
1.0986
{
1
10
,
2
10
,
7
10
}
H
=
−
1
10
log
1
10
−
2
10
log
2
10
−
7
10
log
7
10
=
0.8018
\{\frac{1}{3},\frac{1}{3},\frac{1}{3}\} \\ H = -\frac{1}{3}\log{\frac{1}{3}}-\frac{1}{3}\log{\frac{1}{3}}-\frac{1}{3}\log{\frac{1}{3}} = 1.0986 \\ \\ \{\frac{1}{10},\frac{2}{10},\frac{7}{10}\} \\ H = -\frac{1}{10}\log{\frac{1}{10}}-\frac{2}{10}\log{\frac{2}{10}}-\frac{7}{10}\log{\frac{7}{10}} = 0.8018 \\
{31,31,31}H=−31log31−31log31−31log31=1.0986{101,102,107}H=−101log101−102log102−107log107=0.8018
二分类问题信息熵的公式可化简为:
H
=
−
x
log
∗
(
x
)
−
(
1
−
x
)
log
(
1
−
x
)
H = -x\log*(x) - (1-x)\log(1-x)
H=−xlog∗(x)−(1−x)log(1−x)
import numpy as np
import matplotlib.pyplot as plt
def entropy(p):
return -p * np.log(p) - (1-p) * np.log(1-p)
x = np.linspace(0.01, 0.99, 200)
plt.plot(x, entropy(x))
plt.show()
最小化信息熵划分数据维度和阈值,模拟sklearn中的封装方法
import numpy as np
from collections import Counter
from math import log
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data[:, 2:]
y = iris.target
def split(X, y, d, value):
"""
函数功能:根据给定的特征维度d和阈值value,将数据集进行划分
X、y分别是数据样本和标签
d是数据的某一个维度
value是d维度上的一个阈值
"""
# 寻找所有数据集中维度为d且小于等于value的bool向量
index_a = X[:, d] <= value
index_b = X[:, d] > value
return X[index_a], X[index_b], y[index_a], y[index_b]
def entropy(y):
"""
计算信息熵
y:类别标签, 类似[0,0,1,1,2,2,2,2]
"""
counter = Counter(y)
res = 0.0
for num in counter.values():
p_i = num / len(y) # 计算每个类别的概率
res += -p_i * log(p_i)
return res
def try_split(X, y):
"""
寻找传入数据的最优划分方案(信息熵最小)
最优的维度和划分阈值
"""
# 最优信息熵
best_entropy = float("inf")
best_d, best_v = -1, -1
# 搜索过程:从d=0以及d这个维度升序后的相邻样本的均值开始
for d in range(X.shape[1]):
# 返回排序(升序)后索引
sorted_index = np.argsort(X[:, d])
for i in range(1, len(X)):
if X[sorted_index[i], d] != X[sorted_index[i - 1], d]:
# 候选阈值value的确认方式,且相邻的两个值不相等(剪枝)
v = (X[sorted_index[i], d] + X[sorted_index[i - 1], d]) / 2
X_l, X_r, y_l, y_r = split(X, y, d, v)
p_l, p_r = len(X_l) / len(X), len(X_r) / len(X) # 可以删除占比
e = p_l * entropy(y_l) + p_r * entropy(y_r)
if e < best_entropy: # 更新最小熵、最优维度d以及该维度上的最优阈值v
best_entropy, best_d, best_v = e, d, v
return best_entropy, best_d, best_v
best_entropy, best_d, best_v = try_split(X, y)
print("best_entropy =", best_entropy)
print("best_d =", best_d)
print("best_v =", best_v)
# best_entropy = 0.46209812037329684
# best_d = 0
# best_v = 2.45
# 解释:第一次划分在第0个维度上,阈值为2.45,信息熵最优
# 根据第一次的最优划分条件,对数据集进行划分
X1_l, X1_r, y1_l, y1_r = split(X, y, best_d, best_v)
entropy(y1_l) # 0.0 y1_l信息熵为0,对应X1_l节点无需再划分
entropy(y1_r) # y1_r信息熵0.6931471805599453,继续划分X1_r节点
best_entropy2, best_d2, best_v2 = try_split(X1_r, y1_r)
print("best_entropy =", best_entropy2)
print("best_d =", best_d2)
print("best_v =", best_v2)
# best_entropy = 0.2147644654371359
# best_d = 1
# best_v = 1.75
X2_l, X2_r, y2_l, y2_r = split(X1_r, y1_r, best_d2, best_v2)
entropy(y2_l) # 0.30849545083110386
entropy(y2_r) # 0.10473243910508653
# 信息熵不为0还可以继续划分,此时深度为2
基尼系数
基尼系数公式如下:
G
=
1
−
∑
i
=
1
k
p
i
2
G = 1-\sum^{k}_{i=1}p_{i}^2
G=1−i=1∑kpi2
基尼系数和信息熵拥有同样的性质。
基尼系数代码实现
from collections import Counter
def gini(y):
counter = Counter(y)
res = 1.0
for num in counter.values():
p = num / len(y)
res -= p**2
return res
CART
决策树又称:Classification And Regression Tree
复杂度分析:
预测: O ( l o g m ) O(logm) O(logm)
预测: O ( n ∗ m ∗ l o g m ) O(n*m*logm) O(n∗m∗logm)
n 、 m n、m n、m 分别是样本数量和数据维度。
决策树的局限性
1、决策数是在某一个维度上进行划分,所以产生的决策边界都是和数据维度平行的,并不会产生倾斜的边界,有时真实数据可能并非如此。
2、决策树会对个别的样本点是非常敏感的,某一个特殊的样本点可能都会改变决策树的决策边界。