1. 决策树概念
决策树是根据特征进行树的构造,主要内容如下:
通过属性构造节点:
(1)二元属性
(2)三元属性
(3)序数属性:离散化或者分区间进行分支
根据指标选择属性:
信息熵:
E
n
t
r
o
p
y
=
−
∑
i
=
0
n
−
1
p
(
i
)
l
o
g
2
p
(
i
)
Entropy=-\sum_{i=0}^{n-1}p(i)log_2p(i)
Entropy=−i=0∑n−1p(i)log2p(i)
Gini值:
G
i
n
i
=
1
−
−
∑
i
=
0
n
−
1
[
p
(
i
)
]
2
Gini=1--\sum_{i=0}^{n-1}[p(i)]^2
Gini=1−−i=0∑n−1[p(i)]2
如,
l
a
b
e
l
=
0
label=0
label=0的样本有5个,
l
a
b
e
l
=
1
label=1
label=1的样本也有5个,则有
p
(
0
)
=
0.5
p(0)=0.5
p(0)=0.5,
p
(
1
)
=
0.5
p(1)=0.5
p(1)=0.5,对应的Gini值:
G
i
n
i
=
1
−
∑
i
=
0
1
[
p
(
i
)
]
2
=
1
−
[
0.
5
2
+
0.
5
2
]
=
0.5
Gini=1-\sum_{i=0}^{1}[p(i)]^2=1-[0.5^2+0.5^2]=0.5
Gini=1−i=0∑1[p(i)]2=1−[0.52+0.52]=0.5
根据属性计算对应的Gini值或者Entropy来选择作为节点的属性,对应值越小的属性越好。分支后的Gini值或Entropy是通过子节点的值加权和得到的,通过属性选择后使得指标变小,上一层节点的对应指标值和下一层的差就称为信息增益(熵值下降或Gini系数降低,意味着纯度上升)。
进一步选取子节点的方案:
- ID3
信 息 增 益 = 原 始 熵 值 − 下 降 后 的 熵 值 信息增益=原始熵值-下降后的熵值 信息增益=原始熵值−下降后的熵值选取使得信息增益最大的方案 - C4.5
信 息 增 益 率 = 信 息 增 益 / 自 身 熵 值 信息增益率=信息增益/自身熵值 信息增益率=信息增益/自身熵值 - Gini系数的增益
决策树的评价函数:
C
(
T
)
=
∑
t
∈
l
e
a
f
N
t
H
(
t
)
C(T)=\sum_{t\in leaf}N_tH(t)
C(T)=t∈leaf∑NtH(t)其中
N
t
N_t
Nt为当前叶子节点样本个数,
H
(
t
)
H(t)
H(t)则表示当前叶子结点的熵值或Gini值。
决策树剪枝:决策树高度过高容易过拟合。
{
预
剪
枝
:
在
构
建
决
策
树
的
过
程
提
前
截
止
后
剪
枝
:
决
策
树
构
建
好
后
再
进
行
裁
剪
\begin{cases} 预剪枝:在构建决策树的过程提前截止 \\ 后剪枝:决策树构建好后再进行裁剪 \end{cases}
{预剪枝:在构建决策树的过程提前截止后剪枝:决策树构建好后再进行裁剪
预剪枝方法:指定深度/指定最小样本数
后剪枝方法:
后剪枝损失函数:
C
α
(
T
)
=
C
(
T
)
+
α
∣
T
l
e
a
f
∣
C_\alpha (T)=C(T)+\alpha |T_{leaf}|
Cα(T)=C(T)+α∣Tleaf∣其中
α
\alpha
α表示叶子节点个数对结果的影响程度,
T
l
e
a
f
T_{leaf}
Tleaf的加入会使得叶子节点数越多,损失就越大。遍历节点,按后剪枝损失函数确定是否进行修剪。
2. 决策树实操
2.1 决策树多分类
import matplotlib.pyplot as plt
import pydotplus
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
# 导入红酒数据集
wine = load_wine()
# 划分成训练集和测试集
Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data, wine.target, test_size=0.3)
# 实例化分类器
# random_state控制随机性
# 确定最优参数
test_scores = []
for i in range(10):
clf = tree.DecisionTreeClassifier(criterion='entropy', random_state=30, splitter='random',
max_depth=i+1)
clf = clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest) # 返回预测的准确度
test_scores.append(score)
plt.plot(range(1, 11), test_scores, color='red', label='max_depth')
plt.legend()
plt.show()
print(score)
score1 = clf.score(Xtrain, Ytrain)
print(score1)
import graphviz
feature_name = ['酒精', '苹果酸', '灰', '灰的碱性', '镁', '总酚', '类黄酮', '非黄烷类酚类', '花青素',
'颜色强度', '色调', 'od280/od315稀释葡萄酒', '脯氨酸']
# filled属性是填充颜色
# rounded属性是框的形状
dot_data = tree.export_graphviz(clf, feature_names=feature_name,
class_names=['琴酒', '雪莉', '贝尔摩德'],
filled=True,
rounded=True)
# 决策树的可视化
graph = pydotplus.graph_from_dot_data(dot_data)
graph.write_png('1.png')
print([*zip(feature_name, clf.feature_importances_)])
2.2 决策树回归
from sklearn.datasets import load_boston
from sklearn.model_selection import cross_val_score
from sklearn.tree import DecisionTreeRegressor
import numpy as np
import matplotlib.pyplot as plt
# boston = load_boston()
# # 构建回归树
# regressor = DecisionTreeRegressor(random_state=0)
# # 10次交叉验证
# cross_val_score(regressor, boston.data, boston.target, cv=10,
# scoring='neg_mean_squared_error')
# print(cross_val_score)
rng = np.random.RandomState(1)
# (80,1)二维
X = np.sort(5 * rng.rand(80, 1), axis=0)
# ravel()降维函数修改成一维数据
y = np.sin(X).ravel()
# plt.figure()
# plt.scatter(X, y, s=20, edgecolors='black', c='darkorange', label='data')
# plt.show()
# 在y的数据上加入噪声,每隔5个步长加入噪声
y[::5] += 3 * (0.5 - rng.rand(16))
# plt.figure()
# plt.scatter(X, y, s=20, edgecolors='black', c='darkorange', label='data')
# plt.show()
regr_1 = DecisionTreeRegressor(max_depth=2)
regr_2 = DecisionTreeRegressor(max_depth=5)
regr_1.fit(X, y)
regr_2.fit(X, y)
# 生成0.0-5.0之间的步长为0.01的有序数列
# [:np.newaxis]用于扩展维度生成shape(n,1)
X_test = np.arange(0.0, 5.0, 0.01)[:, np.newaxis]
y_1 = regr_1.predict(X_test)
y_2 = regr_2.predict(X_test)
plt.figure()
plt.scatter(X, y, s=20, edgecolors='black', c='darkorange', label='data')
plt.plot(X_test, y_1, color='cornflowerblue', label='max_depth=2', linewidth=2)
plt.plot(X_test, y_2, color='yellowgreen', label='max_depth=5', linewidth=2)
plt.xlabel('data')
plt.ylabel('target')
plt.title('Decision Tree Regression')
plt.legend()
plt.show()
2.3 决策树调参案例(Tatanic)
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
import matplotlib.pyplot as plt
from sklearn.model_selection import GridSearchCV, train_test_split, cross_val_score
data = pd.read_csv(r'./titanic/train.csv')
# print(data.head()) # 展示前多少行
# print(data.info()) # 展示整个表
# 数据预处理
# 筛选特征,删除缺失值过多的特征,以及和幸存与否无关的特征,对列进行操作(列)
data.drop(['Cabin', 'Name', 'Ticket'], inplace=True, axis=1)
# data = data.drop(['Cabin', 'Name', 'Ticket'], inplace=False, axis=1)
# 处理缺失值(均值或中值)
data['Age'] = data['Age'].fillna(data['Age'].mean())
# 对于缺失比较小的样本直接删掉(行)
data = data.dropna()
# 查看Embarked属性有多少取值,转换非数值的特征,映射到数值取值(分类之间相互独立)
# S\C\Q三种,并转换成列表对应index:0\1\2
labels = data['Embarked'].unique().tolist()
data['Embarked'] = data['Embarked'].apply(lambda x: labels.index(x))
# 性别转换成二分类,将bool值转换成0-1
# loc为标签索引,数字索引对应iloc
data.loc[:, 'Sex'] = (data['Sex'] == 'male').astype(int)
# 将标签和特征分离
# 除了survive列之外的列
x = data.iloc[:, data.columns != 'Survived']
y = data.iloc[:, data.columns == 'Survived']
Xtrian, Xtest, Ytrain, Ytest = train_test_split(x, y, test_size=0.3)
# 恢复索引,恢复成正序,避免索引发生混乱
for i in [Xtrian, Xtest, Ytrain, Ytest]:
i.index = range(i.shape[0])
# clf = DecisionTreeClassifier(random_state=25)
# clf = clf.fit(Xtrian, Ytrain)
# # score = clf.score(Xtest, Ytest)
# score = cross_val_score(clf, x, y, cv=10).mean()
# print(score)
# tr = []
# te = []
# for i in range(10):
# clf = DecisionTreeClassifier(random_state=25,
# max_depth=i+1,
# criterion='entropy')
# clf = clf.fit(Xtrian, Ytrain)
# score_tr = clf.score(Xtrian, Ytrain)
# score_te = cross_val_score(clf, x, y, cv=10).mean()
# tr.append(score_tr)
# te.append(score_te)
#
# print(max(te))
# plt.plot(range(1, 11), tr, color='red', label='train')
# plt.plot(range(1, 11), te, color='blue', label='test')
# plt.xticks(range(1, 11))
# plt.legend()
# plt.show()
# 网格搜索调整多个参数,枚举技术,计算量很大
# 一串参数及其对应的取值范围的字典
import numpy as np
gini_thresholds = np.linspace(0, 0.5, 50)
# entrop[y_thresholds = np.linspace(0, 1, 50)
# 一般一两个参数组合,分别进行调试
# 过多的参数组合,会导致分数无法舍弃,得到的是平均的调参结果
parameters = {
'criterion': ('gini', 'entropy')
, 'splitter': ('best', 'random')
, 'max_depth': [*range(1, 10)]
, 'min_samples_leaf': [*range(1, 50, 5)]
, 'min_impurity_decrease': [*np.linspace(0, 0.5, 20)]
}
clf = DecisionTreeClassifier(random_state=25)
GS = GridSearchCV(clf, parameters, cv=10)
GS.fit(Xtrian, Ytrain)
GS.best_params_ # 返回输入的参数列表的最佳组合
GS.best_score_ # 网格搜索后的模型评判标准