一,什么是决策树?
这棵决策树的深度为3,只要判断三次就可以分类
程序实现
加载鸢尾花数据集
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
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()
dt_clf = DecisionTreeClassifier(max_depth=2, criterion="entropy", random_state=42)
dt_clf.fit(X, y) #criterion="entropy"表示熵
使用决策边界绘制函数:
plot_decision_boundary(dt_clf, axis=[0.5, 7.5, 0, 3])
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()
分析决策流程:
特点:
- 非参数学习算法
- 可以解决分类问题
- 天然可以解决多分类问题
- 也可以解决回归问题(叶子的平均值)
- 非常好的可解释性
问题:
- 每个节点在哪个维度做划分?
- 在某一维度上,要取哪个阈值进行划分呢?
二,信息熵
熵在信息论中代表随机变量不确定的的度量
- 熵越大,数据的不确定性越高,越混乱
- 熵越小,数据的不确定性越低,越规则
香农提出的信息熵公式:
p表示系统中每一类信息所占的比例(每种鸢尾花所占的比例)
可以计算:
H{1/3,1/3,1/3}=1.0986
H{1/10,2/10,7/10}=0.8018
H{1,0,0}=0
- 熵越大,整个系统越不确定,熵最大时一般就是各类信息所占比例相同
- 当所有信息集中在一类,整个系统就是确定的,此时熵就是0
信息熵的函数图像:
当系统只有俩类,公式可以写成:
程序:
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) #x不能取1和0
plt.plot(x, entropy(x))
plt.show()
其函数图像是:
从图中也可以得出上述的结论。
因此我们得出解决问题的方法:
要求划分之后使得系统的信息熵降低,使系统变得更加确定
=>基于信息熵的最优划分方式
三,使用信息熵寻找最优划分
导入数据集,为了可视化方便依旧选取俩个维度特征
程序如上,结果同上:
模拟使用信息熵进行划分
定义划分函数:
def split(X, y, d, value): #d表示维度,阈值是value
index_a = (X[:,d] <= value) #返回布尔向量
index_b = (X[:,d] > value)
return X[index_a], X[index_b], y[index_a], y[index_b]
寻找d和value的值:
from collections import Counter #把y做成字典
from math import log
def entropy(y):
counter = Counter(y)
res = 0.0
for num in counter.values():
p = num / len(y)
res += -p * log(p)
return res
def try_split(X, y):
best_entropy = float('inf') #信息熵初始化正无穷
best_d, best_v = -1, -1
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]:
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:
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)
X1_l, X1_r, y1_l, y1_r = split(X, y, best_d, best_v) #第一次划分
X2_l, X2_r, y2_l, y2_r = split(X1_r, y1_r, best_d2, best_v2) #第二次划分
四,基尼系数
与信息熵相似,对于基尼系数,越大意味着数据的不确定性越高,越小数据越确定
如果只有俩类:G=1-x^2-(1-x)^2=-2x^2+2x
基尼系数程序实现:
数据同上,但是调用决策树分类时注意改变参数:criterion="gini"
绘制的结果:
与信息熵的结果相同
模拟使用基尼系数进行划分:
重新定义一个基尼系数,剩下同上:
def gini(y):
counter = Counter(y)
res = 1.0
for num in counter.values():
p = num / len(y)
res -= p**2
return res
信息熵和基尼系数对比:
信息熵的计算比基尼系数稍慢
scikit-learn中默认使用基尼系数
大多数时候二者没有特别的效果优劣
五,CART与决策树中的超参数
Classification And Regression Tree
根据某一维度d和某一阈值的v进行划分
决策树的时间复杂度:
预测:O(logm)
训练:O(n*n*logm)
训练的时间复杂度太高
剪枝:降低复杂度,解决过拟合(限制树的高度就是剪枝手段)
数据生成:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
X, y = datasets.make_moons(noise=0.25, random_state=666)
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
plt.show()
dt_clf = DecisionTreeClassifier() #默认参数,高度默认就会一直划分
dt_clf.fit(X, y)
绘制决策边界:
可以看出,已经过拟合了
调参:
dt_clf2 = DecisionTreeClassifier(max_depth=2) #限制深度
dt_clf2.fit(X, y)
dt_clf3 = DecisionTreeClassifier(min_samples_split=10) #至少要多少样本数据才进行拆分
dt_clf3.fit(X, y)
dt_clf4 = DecisionTreeClassifier(min_samples_leaf=6) #每个叶子至少需要多少样本点
dt_clf4.fit(X, y)
dt_clf5 = DecisionTreeClassifier(max_leaf_nodes=4) #最多有多少叶子节点
dt_clf5.fit(X, y)
CART中的超参数:
六,决策树解决回归问题
默认参数程序实现:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
boston = datasets.load_boston()
X = boston.data
y = boston.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)
dt_reg = DecisionTreeRegressor()
dt_reg.fit(X_train, y_train)
决策树非常容易过拟合,需要参数抑制
决策树的学习曲线:
依旧使用以上数据集
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_squared_error
我们首先定义绘制RMSE学习曲线的函数:
绘制曲线:
plot_learning_curve(DecisionTreeRegressor(), X_train, X_test, y_train, y_test)
R^2学习曲线:
调节最大深度:
plot_learning_curve_r2(DecisionTreeRegressor(max_depth=1), X_train, X_test, y_train, y_test)
plot_learning_curve_r2(DecisionTreeRegressor(max_depth=3), X_train, X_test, y_train, y_test)
plot_learning_curve_r2(DecisionTreeRegressor(max_depth=5), X_train, X_test, y_train, y_test)
七,决策树的局限性
决策边界是横平竖直的,解决非线性的问题泛化能力很弱,作为一个非参数学习方法,对个别的样本点非常的敏感!
数据同上
X_new = np.delete(X, 106, axis=0) #删除一个样本点
y_new = np.delete(y, 106)
tree_clf2 = DecisionTreeClassifier(max_depth=2, criterion="entropy", random_state=42)
tree_clf2.fit(X_new, y_new)
删除一个样本点,决策边界变化非常大
但决策树的重要性在于使用集成学习的方式创建随机森林的算法