决策树是一种常用的机器学习模型,用于分类和回归任务,它通过模拟“树”的结构来对数据进行决策。本节我们详细讨论的是决策树中的分类任务
假设以下运维场景
| CPU | 内存 | 磁盘I/O | 日志报错数 | 系统是否稳定 |
|---|---|---|---|---|
| 低 | 低 | 低 | 少 | 稳定 |
| 中 | 中 | 低 | 少 | 稳定 |
| 低 | 中 | 高 | 少 | 稳定 |
| 低 | 低 | 低 | 多 | 不稳定 |
| 低 | 低 | 高 | 中 | 不稳定 |
| 高 | 搞 | 低 | 少 | 不稳定 |
| 中 | 高 | 低 | 中 | 稳定 |
| 高 | 低 | 低 | 多 | 不稳定 |
| 中 | 高 | 高 | 少 | 不稳定 |
| 中 | 中 | 中 | 中 | 不稳定 |
| 高 | 高 | 低 | 少 | 稳定 |
将其转换成代码:
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import classification_report
level_map = {'低': 0, '中': 1, '高': 2, '少': 0, '中': 1, '高': 2, '多':2, '稳定': 1, '不稳定': 0}
data = [
['低', '低', '低', '少', '稳定'],
['中', '中', '低', '少', '稳定'],
['低', '中', '高', '少', '稳定'],
['低', '低', '低', '多', '不稳定'],
['低', '低', '高', '多', '不稳定'],
['高', '高', '低', '少', '不稳定'],
['中', '高', '低', '中', '稳定'],
['高', '低', '低', '多', '不稳定'],
['中', '高', '高', '少', '不稳定'],
['中', '中', '中', '中', '不稳定'],
['高', '高', '低', '少', '稳定'],
]
data_mapped = [[level_map[v] for v in row] for row in data]
df = pd.DataFrame(data_mapped, columns=['CPU', '内存', '磁盘IO', '日志报错数', '系统是否稳定'])
X = df[['CPU', '内存', '磁盘IO', '日志报错数']]
y = df['系统是否稳定']
clf = DecisionTreeClassifier(criterion='entropy', random_state=0)
clf.fit(X, y)
y_pred = clf.predict(X)
print("分类报告:n", classification_report(y, y_pred, target_names=['不稳定', '稳定']))
脚本!启动

绘图更能直接观察决策树
from sklearn import tree
import matplotlib.pyplot as plt
plt.figure(figsize=(14, 8))
tree.plot_tree(clf,
feature_names=['CPU', 'memory', 'storage I/O', 'error count'],
class_names=['not stable', 'stable'],
filled=True)
plt.show()

error count表示用该特征来分类,1.5是基于输入的数据计算出来的,模型认为1.5是特征分割点在图中有一个重要的指标,entropy。它是衡量数据纯度(不确定性)的指标,熵越低,表示类别越“纯”(不确定性越低);熵越高,代表数据越混杂
比如某节点:

样本数samples=5,类别value=[1,4]
[p_1=frac{1}{5},p_2=frac{4}{5} ]
[Entropy(D) = - (frac{1}{5}⋅log_2frac{1}{5}+frac{4}{5}⋅log_2frac{4}{5}) = 0.7215 ]
熵越小,那就代表了该节点不确定性越低,如果该节点的上为0,那就不会继续分裂下去产生子节点;而不确定性越高的节点,就会继续分裂产生叶子节点
是决策树中用于选择划分特征的核心指标。它本质上衡量的是:“使用某个特征进行划分后,数据的不确定性减少了多少”
信息增益=原始熵−加权熵
比如以下节点,想要计算出信息增益

原始熵我们已经有了,那就是0.994,需要计算加权熵
[加权熵=frac{9}{11}⋅0.991+frac{2}{11}⋅0=0.811 ]
[原始熵=0.994−0.811= 0.183 ]
通过特征error count 划分所带来的信息增益为0.183,也就是减少了 18.3% 的“混乱”程度
特征从“低”、“中”、“高”等文字描述转换成,0 1 2等数字,比如CPU可能的取值是[0,1,2],那就可以尝试划分出阈值中位点
(0+1)/2=0.5
(1+2)/2=1.5
所以在选取划分点的时候就会参考中位点进行划分
| 日志报错数 | 日志报错数数字化 | 系统是否稳定 |
|---|---|---|
| 少 | 0 | 稳定 |
| 少 | 0 | 稳定 |
| 少 | 0 | 稳定 |
| 多 | 2 | 不稳定 |
| 中 | 1 | 不稳定 |
| 少 | 0 | 不稳定 |
| 中 | 1 | 稳定 |
| 多 | 2 | 不稳定 |
| 少 | 0 | 不稳定 |
| 中 | 1 | 不稳定 |
| 少 | 0 | 稳定 |
先选择error count以1.5为划分点
error count有9条,稳定与不稳定的比例为[5, 4]error count>1.5有2条,并且都是不稳定的计算一下信息增益:
error count>=1.5的熵:(Entropy_1=-(frac{4}{9}⋅log_2frac{4}{9}+frac{5}{9}⋅log_2frac{5}{9}) approx 0.991)error count>1.5的熵,由于全是不稳定,所以熵是0选择error count以0.5为划分点
error count有6条,稳定与不稳定的比例为[4, 2]
error count>0.5有5条,稳定与不稳定的比例为[1, 4]
首先计算原始熵:上面已经计算过了,0.994
error count的熵:(Entropy_1=-(frac{4}{6}⋅log_2frac{4}{6}+frac{2}{6}⋅log_2frac{2}{6}) approx 0.918)error count>0.5的熵:(Entropy_2=-(frac{1}{5}⋅log_2frac{1}{5}+frac{4}{5}⋅log_2frac{4}{5}) approx 0.722)计算加权熵:(Entropy_{weighted}=frac{6}{11}⋅0.918+frac{5}{11}⋅0.722 approx 0.829)
信息增益:0.994-0.829=0.165
综上所述,error count为特征,以1.5为划分点,是合适的选择。并且将每个特征的每个划分点遍历一遍,得出error count是熵下降最快的划分点

算法步骤:
有位彦祖说到,这不就是ID3算法嘛,基于信息熵作为划分标准。其实说对了一部分,文中使用的代码
clf = DecisionTreeClassifier(criterion='entropy', random_state=0)
criterion='entropy'使用的是的算法是整合了CART算法+ID3的信息熵分类等多种算法思想融合而来
之前讨论了通过熵的方式来划分节点,本节讨论基尼指数来划分节点
[Gini(S) = 1 - sum_{i=1}^{n} p_i^2 ]
clf = DecisionTreeClassifier(criterion='gini', random_state=0)

使用基尼指数,选择了error count作为划分特征,与熵不一样
选择error count
error count有6条,稳定与不稳定的比例为[4, 2]error count>0.5有5条,稳定与不稳定的比例为[1, 4]计算一下基尼增益:
error count的基尼指数,(Gini_1=1-((frac{4}{6})^2+(frac{2}{6})^2) approx 0.4445)error count>0.5的基尼指数,(Gini_2=1-((frac{1}{5})^2+(frac{4}{5})^2) = 0.32)选择error count
error count有9条,稳定与不稳定的比例为[5, 4]error count>1.5有2条,并且都是不稳定的计算一下基尼增益:
error count的基尼指数,(Gini_1=1-((frac{5}{9})^2+(frac{4}{9})^2) approx 0.4939)error count>1.5的基尼指数,由于全是不稳定,(Gini_2=1-((frac{0}{2})^2+(frac{2}{2})^2) = 0)综上所述,error count为特征,以0.5为划分点,是合适的选择
使用基尼指数,选择特征划分点的时候,与熵的行为完全不一样,下面列一下基尼指数与熵的对比
| 基尼指数 | 熵 | |
|---|---|---|
| 参数 | criterion='gini' | criterion='entropy' |
| 划分标准 | 基尼指数(Gini Index) | 信息增益(Information Gain) |
| 计算效率 | 更高(无对数运算) | 较低(需计算对数) |
| 分裂倾向 | 偏向生成平衡的树 | 可能生成更深的树 |
| 类别 | 对类别分布变化更敏感(如某一类别占比从40%→50%时,基尼指数变化更明显) | 对极端概率更敏感(如某一类别占比接近0%或100%时,熵变化剧烈) |
| sklearn | 默认使用 | 需指定criterion='entropy' |
所谓剪枝,是提高决策树泛化能力的一种策略,剪枝的核心思想是减少数的复杂度,决策树通过找到最大的信息增益,不断迭代的划分节点分类,不断的迭代划分,就会造成树的复杂度不断提高
预剪枝:在构建决策树的过程中就进行限制,提前停止树的生长
max_depthmin_samples_leafmin_samples_splitfrom sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_predict, StratifiedKFold
from sklearn.metrics import classification_report
X, y = load_iris(return_X_y=True)
cv = StratifiedKFold(n_splits=5, shuffle=True)
model = DecisionTreeClassifier(max_depth=3, min_samples_leaf=3, random_state=0)
y_pred = cross_val_predict(model, X, y, cv=cv)
print("预剪枝 分类报告:n")
print(classification_report(y, y_pred, zero_division=0))
后剪枝:先让树尽可能长大(完全生长),然后再自底向上地修剪枝叶。sklearn中默认采用:成本复杂度剪枝(Cost Complexity Pruning,也叫 α-剪枝):用代价函数考虑准确率与模型复杂度的权衡
from sklearn.model_selection import cross_val_score
import numpy as np
model = DecisionTreeClassifier(random_state=0)
path = model.cost_complexity_pruning_path(X, y)
ccp_alphas = path.ccp_alphas[:-1]
mean_scores = []
for alpha in ccp_alphas:
clf = DecisionTreeClassifier(random_state=0, ccp_alpha=alpha)
scores = cross_val_score(clf, X, y, cv=5, scoring='accuracy')
mean_scores.append(scores.mean())
best_alpha = ccp_alphas[np.argmax(mean_scores)]
print("最佳 ccp_alpha:", best_alpha)
best_model = DecisionTreeClassifier(random_state=0, ccp_alpha=best_alpha)
best_model.fit(X, y)
y_pred = best_model.predict(X)
print("后剪枝 分类报告:")
print(classification_report(y, y_pred))

ccp_alpha超参数(提前设置)进行对比,如果ccp_alpha>(alpha),则减掉该子树举一个例子,来看下该节点(CPU)是否应该被剪枝

如果超参数ccp_alpha>0.028,那该节点就将剪枝
至此,本文结束
在下才疏学浅,有撒汤漏水的,请各位不吝赐教...
本文来自博客园,作者:it排球君,转载请注明原文链接:https://www.cnblogs.com/MrVolleyball/p/19081396
登录查看全部
参与评论
手机查看
返回顶部