前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >机器学习测试笔记(13)——决策树与随机森林

机器学习测试笔记(13)——决策树与随机森林

作者头像
顾翔
发布2021-01-18 11:16:17
8660
发布2021-01-18 11:16:17
举报

1决策树模型

1.1基本概念

决策树类似于我们玩的读心游戏,一个人问问题,另一个人只能回答yes或no。比如:

问:这是个人吗?回答:是

问:是女生吗?回答:不是

问:他戴眼镜吗?回答:是

一直猜出回答者的正确答案。如下图所示。

1.2 信息增益与基尼不纯度

在介绍决策树之前我们先来介绍下信息熵,信息熵是约翰·香农根据热力学第二定律,在 1948《通信的数学原理》一书中提出,主要思想是:一个问题不确定性越大,需要获取的信息就越多,信息熵就越大;一个问题不确定性越小,需要获取的信息就越少,信息熵就越小。比如“小张今天会不会在9:00之前到公司”的信息熵就比“小张今天会不会吃早饭”的信息熵要高,因为小张长久以来没有不吃早饭的习惯。

信息熵是这样定义的:假设集合D中第k类样本的比率为pk,(k=1,2,…|y|)

Ent(D)=-

在决策树中有两个概念:信息增益(Information Gain)和基尼不纯度(Gini impurity

)。

信息增益(Information Gain):划分数据前后数据信息熵的差值。

信息增益纯度越高,纯度提升越大;信息增益纯度越低,纯度提升越小。

信息增益使用的是ID3算法(改进后为C4.5算法)。决策树在选取节点的时候,计算每个特征值划分后的信息增益,选取信息增益最大的节点。

基尼不纯度:反映从集合D中随机取两个样本后,其类别不一致性的概率。

Gini(D)=

基尼不纯度使用的是CART算法。决策树在集合中要选取基尼不纯度最小的那个节点。

1.3 Sklearn中的决策树构建

Sklearn中使用sklearn.tree.DecisionTreeClassifier构建决策树分类;from sklearn.tree import DecisionTreeRegressor构建决策树回归。

代码语言:javascript
复制
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import tree,datasets
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split


def base_of_decision_tree(max_depth):
        wine = datasets.load_wine()
# 仅选前两个特征
        X = wine.data[:,:2]
        y = wine.target
        X_train,X_test,y_train,y_test = train_test_split(X, y)
        clf = DecisionTreeClassifier(max_depth=max_depth)
        clf.fit(X_train,y_train)
#定义图像中分区的颜色和散点的颜色,允许用户使用十六进制颜色码来定义自己所需的颜色库                
        cmap_light = ListedColormap(['#FFAAAA','#AAFFAA','#AAAAFF'])
        cmap_bold = ListedColormap(['#FF0000','#00FF00','#0000FF']
#分别将样本的两个特征值创建图像的横轴和纵轴
        x_min,x_max = X_train[:,0].min()-1,X_train[:,0].max()+1
        y_min,y_max = X_train[:,1].min()-1,X_train[:,1].max()+1
        xx, yy = np.meshgrid(np.arange(x_min, x_max, .02),np.arange(y_min, y_max, .02))
#给每个样本分配不同的颜色, predict:训练后返回预测结果,显示标签值
        Z = clf.predict(np.c_[xx.ravel(),yy.ravel()])
        Z = Z.reshape(xx.shape)
        plt.figure()
# plt.pcolormesh的作用在于能够直观表现出分类边界
        plt.pcolormesh(xx,yy,Z,cmap=cmap_light,shading='auto')
#用散点把样本表示出来
        plt.scatter(X[:,0],X[:,1],c=y,cmap=cmap_bold,s=20,edgecolors='k')
        plt.xlim(xx.min(),xx.max()) 
        plt.ylim(yy.min(),yy.max())
        plt.title("Classifier:(max_depth="+str(max_depth)+")")
        print("红酒数据训练集(max_depth="+str(max_depth)+"):{:.2f}".format(clf.score(X_train,y_train)))
        print("红酒数据测试集(max_depth="+str(max_depth)+"):{:.2f}".format(clf.score(X_test,y_test)))
        plt.show()

输出

代码语言:javascript
复制
红酒数据训练集(max_depth=1):0.68
红酒数据测试集(max_depth=1):0.69
红酒数据训练集(max_depth=3):0.88
红酒数据测试集(max_depth=3):0.76
红酒数据训练集(max_depth=5):0.90
红酒数据测试集(max_depth=5):0.84

当为max_depth(即树的深度)为1、3、5的时候,划分如下图:

通过图和输出结果,都可以看出,随着max_depth增加,划分越精确( max_depth=1的时候完全是不正确的)。但是我们会发现所有的数据测试集都低于训练集的值,这就是决策树最致命的一点:容易过拟合。

1.4剪枝

解决过拟合的方法是剪枝,预剪枝(Pre-pruning)和后剪枝(post-pruning)。预剪枝及早停止树的增长;后剪枝先形成树,然后再剪枝。Sklearn仅采用预剪枝的策略,预剪枝可以分以下三种方法中的任意一种来解决。

限制最大深度

限制叶节点数最大数目

规定一个节点数据点的最小数目

我们以Sklearn乳腺癌数据采取限制最大深度来看一下防止过拟合的效果。

代码语言:javascript
复制
from sklearn.datasets import load_breast_cancer
from sklearn.tree import DecisionTreeClassifier
def decision_tree():
    cancer = load_breast_cancer()
    x_train,x_test,y_train,y_test = train_test_split(cancer.data,cancer.target,stratify=cancer.target,random_state=42)
    tree = DecisionTreeClassifier(random_state=0) # 构件树,不剪枝
    tree.fit(x_train,y_train)
    print("不剪枝,训练数据集上的精度:{:.3f}".format(tree.score(x_train,y_train)))
    print("不剪枝,测试数据集上的精度:{:.3f}".format(tree.score(x_test,y_test)))
    print("不剪枝,树的深度:{}".format(tree.get_depth()))
    tree = DecisionTreeClassifier(max_depth=4,random_state=0) # 构件树,剪枝
    tree.fit(x_train,y_train)
    print("剪枝,训练数据集上的精度:{:.3f}".format(tree.score(x_train,y_train)))
    print("剪枝,测试数据集上的精度:{:.3f}".format(tree.score(x_test,y_test)))
    print("剪枝,树的深度:{}".format(tree.get_depth()))  

输出

代码语言:javascript
复制
不剪枝,训练数据集上的精度:1.000
不剪枝,测试数据集上的精度:0.937
不剪枝,树的深度:7
剪枝,训练数据集上的精度:0.988
剪枝,测试数据集上的精度:0.951
剪枝,树的深度:4

可见树的深度为7的时候,测试数据集的得分<训练数据集的得分。但是当树的深度为4的时候,测试数据集的得分几乎等于训练数据集的得分。

1.5 决策树的可视化

为了看到可视化的决策树,首先需要下载并且安装一个软件Graphviz的软件,Graphviz 是一款由AT&T Research和Lucent Bell实验室开源的可视化图形工具。然后打开命令行,输入pip3 install graphviz。接下来运行下面代码。

代码语言:javascript
复制
from sklearn.tree import export_graphviz
import graphviz
def show_tree():
    wine = datasets.load_wine()
# 仅选前两个特征
    X = wine.data[:,:2]
    y = wine.target
    X_train,X_test,y_train,y_test = train_test_split(X, y)
    clf = DecisionTreeClassifier(max_depth=3)#为了图片不太大选择max_depth=3
    clf.fit(X_train,y_train) export_graphviz(clf,out_file="wine.dot",class_names=wine.target_names,feature_names=wine.feature_names[:2],impurity=False,filled=True)
#打开dot文件
with open("wine.dot") as f:
        dot_graph = f.read()
    graphviz.Source(dot_graph)

在本地目录下生成wine.dot文件,用Graphviz软件打开,得到如图图片。

总结一下,决策树的优点是:容易可视化和无需对数据进行预处理;缺点是即使采取剪枝也会造成过拟合。解决这个问题最有利的方法是采用随机森林模型。

2 随机森林模型

2.1基本概念

2001年Breiman把分类树组合成随机森林(Breiman 2001a),即在变量(列)的使用和数据(行)的使用上进行随机化,生成很多分类树,再汇总分类树的结果。随机森林在运算量没有显著提高的前提下提高了预测精度。

算法流程:

构建决策树的个数t,单颗决策树的特征个数f,m个样本,n个特征数据集

1 单颗决策树训练

1.1 采用有放回抽样,从原数据集经过m次抽样,获得有m个样本的数据集(可能有重复样本)

1.2 从n个特征里,采用无放回抽样原则,去除f个特征作为输入特征

1.3 在新的数据集(m个样本, f个特征数据集上)构建决策树

1.4 重复上述过程t次,构建t棵决策树

2 随机森林的预测结果

生成t棵决策树,对于每个新的测试样例,综合多棵决策树预测的结果作为随机森林的预测结果。

回归问题:取t棵决策树预测值的平均值作为随机森林预测结果

分类问题:少数服从多数的原则,取单棵的分类结果作为类别随机森林预测结果

在Sklearn中RandomForestClassifier和RandomForestRegressor分类和回归树算法。

2.2 Sklearn中的构建随机森林

代码语言:javascript
复制
from sklearn.ensemble import RandomForestClassifier
def base_of_decision_tree_forest(n_estimator,random_state):
    wine = datasets.load_wine()
# 仅选前两个特征
    X = wine.data[:,:2]
    y = wine.target
    X_train, X_test, y_train, y_test = train_test_split(X, y)
    forest = RandomForestClassifier(n_estimators=n_estimator, random_state=random_state, n_jobs=2) 
#n_jobs:设置为CPU个数
# 在训练数据集上进行学习
    forest.fit(X_train, y_train)
    cmap_light = ListedColormap(['#FFAAAA','#AAFFAA','#AAAAFF’])
    cmap_bold =  ListedColormap(['#FF0000','#00FF00','#0000FF’])
#分别将样本的两个特征值创建图像的横轴和纵轴
    x_min,x_max = X_train[:,0].min()-1,X_train[:,0].max()+1
    y_min,y_max = X_train[:,1].min()-1,X_train[:,1].max()+1
xx, yy = np.meshgrid(np.arange(x_min, x_max, .02),np.arange(y_min, y_max, .02))
#给每个样本分配不同的颜色
  Z = forest.predict(np.c_[xx.ravel(),yy.ravel()])
  Z = Z.reshape(xx.shape)
  plt.pcolormesh(xx,yy,Z,cmap=cmap_light,shading='auto’)


  #用散点把样本表示出来
  plt.scatter(X[:,0],X[:,1],c=y,cmap=cmap_bold,s=20,edgecolors='k’)
  plt.xlim(xx.min(),xx.max()) 
  plt.ylim(yy.min(),yy.max())
  print("红酒数据随机森林训练集得分(n_estimators:"+str(n_estimator)+",random_state:"+str(random_state)+"):{:.2f}".format(forest.score(X_train,y_train)))
  print("红酒数据随机森林测试集得分(n_estimators:"+str(n_estimator)+",random_state:"+str(random_state)+"):{:.2f}".format(forest.score(X_test,y_test)))


def tree_forest():
#定义图像中分区的颜色和散点的颜色
    figure,axes = plt.subplots(4,4,figsize =(100,10))
  plt.subplots_adjust(hspace=0.95)
    i = 0
for n_estimator in range(4,8):
for random_state in range(2,6):
            plt.subplot(4,4,i+1)
            plt.title("n_estimator:"+str(n_estimator)+"random_state:"+str(random_state))
            plt.suptitle("Classifier:RandomForest")
            base_of_decision_tree_forest(n_estimator,random_state)
            i = i + 1
    plt.show()

得到的结果总结如下表。

2.3 随机森林避免过拟合

我们以2个月亮数据进行分析。

代码语言:javascript
复制
import mglearn
def my_RandomForet():
# 生成一个用于模拟的二维数据集
  X, y = datasets.make_moons(n_samples=100, noise=0.25, random_state=3)
# 训练集和测试集的划分
  X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y,random_state=42)
# 初始化一个包含 5 棵决策树的随机森林分类器
  forest = RandomForestClassifier(n_estimators=5, random_state=2)
# 在训练数据集上进行学习
  forest.fit(X_train, y_train)
# 可视化每棵决策树的决策边界
  fig, axes = plt.subplots(2, 3, figsize=(20, 10))
for i, (ax, tree) in enumerate(zip(axes.ravel(), forest.estimators_)):
    ax.set_title('Tree {}'.format(i))
    mglearn.plots.plot_tree_partition(X_train, y_train, tree, ax=ax)
    print("决策树"+str(i)+"训练集得分:{:.2%}".format(tree.score(X_train,y_train)))
    print("决策树"+str(i)+"测试集得分:{:.2%}".format(tree.score(X_test,y_test)))
# 可视化集成分类器的决策边界
  print("随机森林训练集得分:{:.2%}".format(forest.score(X_train,y_train)))
  print("随机森林测试集得分:{:.2%}".format(forest.score(X_test,y_test)))
  mglearn.plots.plot_2d_separator(forest, X_train, fill=True, ax=axes[-1, -1],alpha=0.4)
  axes[-1, -1].set_title('Random Forest')
  mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)
  plt.show()  

输出

虽然决策树3不存在过拟合,决策树4的差值与随机森林得分一致,但是随机森林得分比他们都要高。

2.4案例

下面我们通过一个案例来介绍一下随机森林的使用,案例的内容是预测某人的收入是否可以>50K。我们到http://archive.ics.uci.edu/ml/machine-learning-databases/adult/网上下载adult.dat文件,它的格式是csv文件的形式,把它改为adult.csv,可用Excel文件打开。

代码语言:javascript
复制
import pandas as pd
def income_forecast():
data=pd.read_csv('adult.csv', header=None,index_col=False,
names=['年龄','单位性质','权重','学历','受教育时长',
'婚姻状况','职业','家庭情况','种族','性别',
'资产所得','资产损失','周工作时长','原籍',
'收入'])
#为了方便展示,我们选取其中一部分数据
data_title = data[['年龄','单位性质','学历','性别','周工作时长','职业','收入']]
print(data_title.head())  

输出

正如我们前面所述,通过pd.read_csv()函数可以把csv文件给出来。

代码语言:javascript
复制
#利用shape方法获取数据集的大小
data_title.shape           

输出

代码语言:javascript
复制
data_title.shape:
 (32561, 7)   

说明里面有32561个样本,7个属性。

代码语言:javascript
复制
print("data_title.shape:\n",data_title.shape)
data_title.info()  

输出

代码语言:javascript
复制
Data columns (total 7 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
0   年龄      32561 non-null  int64 
1   单位性质    32561 non-null  object
2   学历      32561 non-null  object
3   性别      32561 non-null  object
4   周工作时长   32561 non-null  int64 
5   职业      32561 non-null  object
6   收入      32561 non-null  object
dtypes: int64(2), object(5)
memory usage: 1.7+ MB

可以看出,单位性质、学历、性别、职业、收入均不是数值类型,于是使用pd.get_dummies()函数对数据进行处理。

代码语言:javascript
复制
##1-数据准备
#1.2 数据预处理
#用get_dummies将文本数据转化为数值
        data_dummies=pd.get_dummies(data_title)
print("data_dummies.shape:\n",data_dummies.shape)
#对比样本原始特征和虚拟变量特征---df.columns获取表头
print('样本原始特征:\n',list(data_title.columns),'\n')
print('虚拟变量特征:\n',list(data_dummies.columns))  

输出

样本原始特征:

['年龄', '单位性质', '学历', '性别', '周工作时长', '职业', '收入']

虚拟变量特征:

['年龄', '周工作时长', '单位性质_ ?', '单位性质_ Federal-gov', '单位性质_ Local-gov', '单位性质_ Never-worked', '单位性质_ Private', '单位性质_ Self-emp-inc', '单位性质_ Self-emp-not-inc', '单位性质_ State-gov', '单位性质_ Without-pay', '学历_ 10th', '学历_ 11th', '学历_ 12th', '学历_ 1st-4th', '学历_ 5th-6th', '学历_ 7th-8th', '学历_ 9th', '学历_ Assoc-acdm', '学历_ Assoc-voc', '学历_ Bachelors', '学历_ Doctorate', '学历_ HS-grad', '学历_ Masters', '学历_ Preschool', '学历_ Prof-school', '学历_ Some-college', '性别_ Female', '性别_ Male', '职业_ ?', '职业_ Adm-clerical', '职业_ Armed-Forces', '职业_ Craft-repair', '职业_ Exec-managerial', '职业_ Farming-fishing', '职业_ Handlers-cleaners', '职业_ Machine-op-inspct', '职业_ Other-service', '职业_ Priv-house-serv', '职业_ Prof-specialty', '职业_ Protective-serv', '职业_ Sales', '职业_ Tech-support', '职业_ Transport-moving', '收入_ <=50K', '收入_ >50K']

这样把特性与值进行合并,比如'单位性质'分成了'单位性质_ ?', '单位性质_ Federal-gov', '单位性质_ Local-gov', '单位性质_ Never-worked', '单位性质_ Private', '单位性质_ Self-emp-inc', '单位性质_ Self-emp-not-inc', '单位性质_ State-gov', '单位性质_ Without-pay'几部分。

代码语言:javascript
复制
print(data_dummies.head())

输出

这里0表示符合这个条件,1表示不符合,比如:单位性质_ Local-gov=1表示地方政府。

代码语言:javascript
复制
#1.3 选择特征
#按位置选择---位置索引---df.iloc[[行1,行2],[列1,列2]]---行列位置从0开始,多行多列用逗号隔开,用:表示全部(不需要[])
#选择除了收入外的字段作为数值特征并赋值给x---df[].values
        x=data_dummies.loc[:,'年龄':'职业_ Transport-moving'].values
#将'收入_ >50K'‘作为预测目标y
        y = data_dummies['收入_ >50K'].values
#查看x,y数据集大小情况
print('特征形态:{} 标签形态:{}'.format(x.shape, y.shape))   

输出

代码语言:javascript
复制
 特征形态:(32561, 44) 标签形态:(32561,)  

32561条数据,44个属性。

代码语言:javascript
复制
##2-数据建模---拆分数据集/模型训练/测试
#2.1将数据拆分为训练集和测试集---要用train_test_split模块中的train_test_split()函数,随机将75%数据化为训练集,25%数据为测试集
#导入数据集拆分工具  
#拆分数据集---x,y都要拆分,rain_test_split(x,y,random_state=0),random_state=0使得每次生成的伪随机数不同
        x_train,x_test,y_train,y_test=train_test_split(x,y,random_state=0)
#查看拆分后的数据集大小情况
print('x_train_shape:{}'.format(x_train.shape))
print('x_test_shape:{}'.format(x_test.shape))
print('y_train_shape:{}'.format(y_train.shape))
print('y_test_shape:{}'.format(y_test.shape))   

输出

代码语言:javascript
复制
x_train_shape:(24420, 44)
x_test_shape:(8141, 44)
y_train_shape:(24420,)
y_test_shape:(8141,)   

24420条训练数据,8141条测试数据。

代码语言:javascript
复制
##2、数据建模---模型训练/测试---决策树算法
#2.2 模型训练---算法.fit(x_train,y_train)
#使用算法
        tree = DecisionTreeClassifier(max_depth=5)#这里参数max_depth最大深度设置为5
#算法.fit(x,y)对训练数据进行拟合
        tree.fit(x_train, y_train)
##2、数据建模---拆分数据集/模型训练/测试---决策树算法
#2.3 模型测试---算法.score(x_test,y_test)
        score_test=tree.score(x_test,y_test)
        score_train=tree.score(x_train,y_train)
print('test_score:{:.2%}'.format(score_test))
print('train_score:{:.2%}'.format(score_train))   

输出

代码语言:javascript
复制
test_score:79.62%
train_score:80.34%   

测试数据得分为79.62%,我们就用这个模型进行预测,假设小王为37岁,机关工作,硕士,男,每周工作40小时,文员,它对应的数据为=[[37,40,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0]],预测代码如下。

代码语言:javascript
复制
#3、模型应用---算法.predict(x_new)---决策树算法
#导入要预测数据--可以输入新的数据点,也可以随便取原数据集中某一数据点,但是注意要与原数据结构相同x_new=[[37,40,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0]]
#37岁,机关工作,硕士,男,每周工作40小时,文员
       prediction=tree.predict(x_new)
print('预测数据:{}'.format(x_new))
print('预测结果:{}'.format(prediction))   

输出

代码语言:javascript
复制
预测数据:[[37, 40, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
预测结果:[0]

所以小王的薪水>50K。(本数据由1994年美国人口普查数据库抽取出来,仅供学习使用。)

3.用sklearn数据测试所有决策树和随机森林模型

修改文件machinelearn_data_model.py。

代码语言:javascript
复制
…
def machine_learn(data,model):
#支持的模型
     modeltype = "LinearRegression,LogisticRegression,Ridge,Lasso,SVM,sm,KNeighborsClassifier,LinearSVR,SVC_linear,SVC_sigmoid,SVC_poly,SVR_linear,SVR_sigmoid,SVR_poly,"
     modeltype += "DecisionTreeClassifie,DecisionTreeRegressor,RandomForestClassifie,RandomForestRegressor"
#决策树的深度
     max_depth = 5
#随机森林的n_estimators和random_state
     n_estimators = 5
     random_state = 2
#n_jobs为CPU的核数
n_jobs=2
…
elif model == "DecisionTreeClassifie":
if data == "boston":
                y_train = y_train.astype('int')
                y_test = y_test.astype('int')
          md = DecisionTreeClassifier(max_depth=max_depth).fit(X_train, y_train)
elif model == "DecisionTreeRegressor":
          md = DecisionTreeRegressor(max_depth=max_depth).fit(X_train, y_train)
elif model == "RandomForestClassifier":
if data == "boston":
                y_train = y_train.astype('int')
                y_test = y_test.astype('int')
          md = RandomForestClassifier(n_estimators=n_estimators, random_state=random_state, n_jobs=n_jobs).fit(X_train, y_train)
elif model == "RandomForestRegressor":
          md = RandomForestRegressor(n_estimators=n_estimators, random_state=random_state, n_jobs=n_jobs).fit(X_train, y_train)
else:
return "提供的模型错误,包括:"+modeltype
…   
在这里考虑:
DecisionTreeClassifie算法在波士顿房价下要求目标y必须为int类型,所以做了判断;
决策树的深度:max_depth = 5;
随机森林的n_estimators= 5和random_state= 2;
n_jobs为CPU的核数,设置为变量。
这样,我们就可以对指定模型指定数据进行定量分析

from machinelearn_data_model import data_for_model
def Tree_and_forest_for_all_data_and_model():
  datas = ["iris","wine","breast_cancer","diabetes","boston","two_moon"]
  models = ["DecisionTreeClassifie","DecisionTreeRegressor","RandomForestClassifie","RandomForestRegressor"]
for data in datas:
for model in models:
      data_for_model.machine_learn(data,model)   

我们对测试结果进行比较:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-01-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 软件测试培训 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档