机器学习决策树笔记

本文深入讲解决策树算法的原理,包括信息熵、GINI系数、ID3、C4.5和CART等核心概念,以及如何选择最优根节点。同时,通过Python代码实现决策树模型,展示模型调优和决策树可视化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,决策树

  决策树是一种分类算法.在生成决策树算法,主要又两个阶段,训练阶段与分类阶段. 训练阶段,是从给定的训练集数据中,构造出一颗决策树. 分类阶段, 是从根开始,按照决策树的分类属性,逐层往下划分,直到叶节点获得概念(决策  \ 分类 )结果.

 

决策树问题,首先要面对的,是对当根节点这个问题.

这里,我们引入信息熵,信息熵表示数据的混乱程度.   

                                                        

我们用数学方式表示一下, 假设P(x) =  P(x)*P(y), 两个事件相互独立,log(xy) = log(x) + log (y)..  H(x),H(y)表示事件发生的不确定性, 事件发生的概率越大,H(x)越小.  

图型表示一下信息熵.  P为概率,小于1的值. 这里我们取对数,在图像上的反应就是P越小,lnP越小. 注意.这里我们把负数放进去,里面变成-lnP.  这里相当于把log曲线,沿着x轴翻转. 最终结果就是, P越小, 总的值越大,数据约乱,熵越高. P越大, 总值约小,数据纯度越高,熵越小.

                              

这边引进一个例子来表示一下.

A = [1,1,2,3,5,7,6,9]     数据某值出现概率低, 数据混乱, 熵高

B = [1,1, 2,2,2,1,2,1]     数据某值出现概率高,数据纯度高,熵低

 

这里,还有一个比较数据混乱程度,或者纯度的指标.GINI系数

                 

Gini系数表示,概率越小,gini系数越大, 数据越纯.

 

我们再来介绍一下在运用决策树算法时候,如何选择最优根

1. 计算目标属性(target,已知的分类结果纯度)的信息熵(或者计算Gini系数),来验证数据的纯度: 

                                    

2. 再计算其他单独属性最为根节点时候的信息熵, 当其做为根时,可能其下又许多子分类,这些子分类也需单独计算信息熵,再把球出来的信息熵与类别概率相乘.

举个栗子:

下面为目标属性,即打不打球的信息熵

下面是基于天气,根据天气来划分是否打球,天气有多种类型. 我们需要即使不同总类型下是否打球. 

                                                          

所有,以天气作为根节点的话,信息熵为:

                    

通过这样,把数据单独计算出来,就可以计算出某属性做为根的信息熵了

 

计算第三部之前,我们先介绍一下评判决策树的标准, 我们构造树的基本想法是,随着树的深度的增加,节点的信息熵迅速减低(数据由:乱-到纯). 

3. 计算数据的信息增益: gain  = (目标的信息熵) - (某一属性做为根时候的信息熵)

信息增益越大, 数据节点信息熵下降越快. 根节点信息熵越大越好.

 

比较所有属性的信息增益,我们选择outlook,这个信息增益最大的作为我们的根节点.

 

当然,我们比较信息增益就可以万无一失的吗?不然下面来介绍一下决策树的几个改进算法

 

ID3: 信息增益    (上面提到的,不多解释了).

这个算法有一个严重的问题.例如

                   

上面的属性列中,多以一组ID标签,标签值为连续的数值. 当我们再计算这组ID属性的信息增益的时候,有一个好玩的事情.

假设,ID 为根

                       

我们来计算一下叶子节点的信息熵, 单独叶子节点只有一个,P = 1 .log(1) = 0,相乘再累加,所有熵等于0 . 信息增益 = 目标的信息熵 - 0 = 目标的信息熵.  这就使得信息增益最大化了, 按照信息增益的原则,这边会把ID做为根节点, 这样问题就大了, ID 属性与 打不打球这目标毫无干系, 这样将导致分类结果非常差. 

 

这时,决策树进行升级. 

C4.5: 信息增益率  = ID3/ 自身的信息增益  

以上面例子为例,上面的ID 属性 数据非常不纯, 信息熵非常大. 就算ID3信息增益越大,除于自身也将变得非常的小了. C4.5算法,克服了信息增益选择属性是,喜好偏向多值的属性的不足,这一缺点.

 

CART:Gini系数, 也是可以当作根选择的标准

 

接下来我们讲一下,评价函数;

t : 表示叶子节点  . N : 指的是当前叶子节点有几个样本,或者当前叶子节点权重.  H(t) : 当前叶子节点的熵值或者Gini系数  

 

有个问题,划分的越准确真的就越好吗?  

不然, 我们划分的太精确,非常容易过拟合, 就是过度学习了,把一些不是区分的特征也学习了. 比如西瓜, 学到西瓜是圆的,就把圆的都归类为西瓜,这样分类就MMB了,会被导数掐死的.

咳咳.....所有,这里需要引入几个东西来防止数据过拟合, 预剪枝,后剪枝,设置树的深度.

预剪枝: 在构建决策树的过程时,提前停止,设置到达某个值就停下来.

后剪枝: 决策树构建好后,再进行剪枝. 观察决策树,看看那些分类已经可以了,把后面的剪掉.

 

这里我们更新一下评价函数,给评价函数添加一个惩罚项,

                     原来的:

                                      

                 更新后的:

                  

T: 为叶子节点的个数.  C(T)为原来的评价函数

 

决策树可以处理分类为题,那可不可以处理回归问题呢,答案是可以的.

  当决策树处理连续值时, 可以把数据进行离散化,把数据划分为多个区间,给个区间做为一个离散点. 还可以对这连续数据进行分界点的选取.(贪婪算法), 对一组连续数据,从第二个开始划分,计算左子树的熵与右子树的熵,并对熵的结果进行求和. 接下到到第三个开始划分,一直划分到倒数第二个. 对这些划分结果的熵就行比较,选最小熵做为分界点.

 

 

 二 代码实例

import pandas as pd
 
    
iris_data = pd.read_csv('iris.data')
iris_data.columns = ['sepal_length_cm', 'sepal_width_cm', 'petal_length_cm', 'petal_width_cm', 'class']
iris_data.head()

import matplotlib.pyplot as plt
iris_data.hist()

from PIL import Image
img=Image.open('test.jpg')
plt.imshow(img)
plt.show()

iris_data.describe()

%matplotlib inline

import matplotlib.pyplot as plt
import seaborn as sb

#数据集去空之后,对数据集进行类别分布的可视化
sb.pairplot(iris_data.dropna(), hue='class')

iris_data.columns

plt.figure(figsize=(15, 15))
#循环提取iris_data数据的给个列标签与列标签的索引赋值
for column_index, column in enumerate(iris_data.columns):
    #类别数据的话跳过
    if column == 'class':
        continue
    plt.subplot(2, 2, column_index + 1)
    #绘制单变量,与类别数据分布的小提琴图
    sb.violinplot(x='class', y=column, data=iris_data)

from sklearn.model_selection import train_test_split

#合并待处理样本的列数据
all_inputs = iris_data[['sepal_length_cm', 'sepal_width_cm',
                             'petal_length_cm', 'petal_width_cm']].values
#目标样本分类
all_classes = iris_data['class'].values
#拆分数据集,
(training_inputs,
 testing_inputs,
 training_classes,
 testing_classes) = train_test_split(all_inputs, all_classes, test_size=0.3, random_state=1)

 

#导入决策树的DTC包
from sklearn.tree import DecisionTreeClassifier
#  1.criterion  gini  or  entropy

#  2.splitter  best or random 前者是在所有特征中找最好的切分点 后者是在部分特征中(数据量大的时候)

#  3.max_features  None(所有),log2,sqrt,N  特征小于50的时候一般使用所有的

#  4.max_depth  数据少或者特征少的时候可以不管这个值,如果模型样本量多,特征也多的情况下,可以尝试限制下

#  5.min_samples_split  如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分
#                       如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。

#  6.min_samples_leaf  这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被
#                      剪枝,如果样本量不大,不需要管这个值,大些如10W可是尝试下5

#  7.min_weight_fraction_leaf 这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起
#                          被剪枝默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,
#                          或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。

#  8.max_leaf_nodes 通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。
#                   如果加了限制,算法会建立在最大叶子节点数内最优的决策树。
#                   如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制
#                   具体的值可以通过交叉验证得到。

#  9.class_weight 指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多
#                 导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重
#                 如果使用“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。

#  10.min_impurity_split 这个值限制了决策树的增长,如果某节点的不纯度
#                       (基尼系数,信息增益,均方差,绝对差)小于这个阈值
#                       则该节点不再生成子节点。即为叶子节点 。

#所有参数均设置为默认状态
decision_tree_classifier = DecisionTreeClassifier()

# 在训练集上训练分类器
decision_tree_classifier.fit(training_inputs, training_classes)

# 利用分类精度验证测试集上的分类器
decision_tree_classifier.score(testing_inputs, testing_classes)

from  sklearn.model_selection import cross_val_score #交叉验证
import numpy as np
decision_tree_classifier = DecisionTreeClassifier()

# cross_val_score returns a list of the scores, which we can visualize
# to get a reasonable estimate of our classifier's performance

#10次交叉验证
cv_scores = cross_val_score(decision_tree_classifier, all_inputs, all_classes, cv=10)
print (cv_scores)
#kde=False
sb.distplot(cv_scores)
plt.title('Average score: {}'.format(np.mean(cv_scores)))

#设置决策树最大的深度,避免模型股过拟合

decision_tree_classifier = DecisionTreeClassifier(max_depth=1)


cv_scores = cross_val_score(decision_tree_classifier, all_inputs, all_classes, cv=10)
print (cv_scores)
sb.distplot(cv_scores, kde=False)
plt.title('Average score: {}'.format(np.mean(cv_scores)))

from sklearn.model_selection import GridSearchCV    #模型调优,填入模型会自动调优
from sklearn.model_selection import StratifiedKFold #分层采样数据

decision_tree_classifier = DecisionTreeClassifier()

#设置最大深度,与叶子节点的最大类别特征
parameter_grid = {'max_depth': [1, 2, 3, 4, 5],
                  'max_features': [1, 2, 3, 4]}

#数据切割数
skf = StratifiedKFold(n_splits=10)
cross_validation = skf.get_n_splits(all_inputs, all_classes)
#采用决策树分类器。第一个参数为分类,第二个参数为填入需要调优的参数,类型为列表或字典。第三个为需要交叉严验证的数据。
grid_search = GridSearchCV(decision_tree_classifier, param_grid=parameter_grid,cv=cross_validation)
#对测试的样本就行预测
grid_search.fit(all_inputs, all_classes)
print("Best score:", grid_search.best_score_)
print("Best param:", grid_search.best_params_)

#可视化评分选择
grid_visualization = []

#对每一个评分,计算均值评分,并追加到列表中。grid_search.cv_results_为决策树分类器交叉验证的结果
for grid_pair in grid_search.cv_results_['mean_test_score']:
    grid_visualization.append(grid_pair)
    
#是数据列表变成数组
grid_visualization = np.array(grid_visualization)
#设置数组的维度
grid_visualization.shape = (5, 4)
#绘制热力图
sb.heatmap(grid_visualization, cmap='Reds')


#x的值为最大特征
plt.xticks(np.arange(4) + 0.5, grid_search.param_grid['max_features'])
#y的值为最大深度,这里对结果数据进行了一次逆序
plt.yticks(np.arange(5) + 0.5, grid_search.param_grid['max_depth'][::-1])
plt.xlabel('max_features')
plt.ylabel('max_depth')

decision_tree_classifier = grid_search.best_estimator_
decision_tree_classifier

import sklearn.tree as tree
from sklearn.externals.six import StringIO

with open('iris_dtc.dot', 'w') as out_file:
    out_file = tree.export_graphviz(decision_tree_classifier, out_file=out_file)
#http://www.graphviz.org/

import pydotplus
from IPython.display import Image
dot_data = tree.export_graphviz(decision_tree_classifier, out_file=None, feature_names=iris_data.columns[0:4], class_names=iris_data.columns[0:-1], filled=True, rounded=True, special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data)
Image(graph.create_png())
from sklearn.ensemble import RandomForestClassifier  #随机森林分类器

random_forest_classifier = RandomForestClassifier()

parameter_grid = {'n_estimators': [5, 10, 25, 50],
                  'criterion': ['gini', 'entropy'],
                  'max_features': [1, 2, 3, 4],
                  'warm_start': [True, False]}

skf = StratifiedKFold(n_splits=10)
cross_validation = skf.get_n_splits(all_inputs, all_classes)

# cross_validation = StratifiedKFold(n_splits=10)

grid_search = GridSearchCV(random_forest_classifier,
                           param_grid=parameter_grid,
                           cv=cross_validation)

grid_search.fit(all_inputs, all_classes)
print('Best score: {}'.format(grid_search.best_score_))
print('Best parameters: {}'.format(grid_search.best_params_))

grid_search.best_estimator_

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值