• ML--决策树与随机森林


    ML–决策树与随机森林

    在生活中,我们经常遇到一些事情需要作出决策来应对。说到决策,自然想到决策树算法,而说到决策树算法,又自然会想到随机森林

    主要涉及的知识点有:

    • 决策树的基本原理和构造
    • 决策树的优势和不足
    • 随机森林的基本原理和构造
    • 随机森林的优势和不足
    • 实例演示:相亲事件

    一.决策树

    决策树是一种在分类与回归中都有非常广泛应用的算法,它的原理是通过对一系列问题进行if/else的推导,最终实现决策

    1.决策树的基本原理

    举个例子:假设出题者心理想的是Google,百度,Facebook,阿里,四个公司中的一个,则提问决策树:
    未命名文件.png
    最终的4个节点,也就是4个公司的名字,被称为决策树的树叶。例子中的这棵决策树只有4片树叶,所以通过手动的方式就可以进行建模。但是如果样本的特征特别多,就不得不使用机器学习的办法来进行建模了

    2.决策树的构建

    下面我们先载入酒的数据集,然后将它做成训练集和测试集,输入代码如下:

    # 导入numpy
    import numpy as np
    
    # 导入画图工具
    import matplotlib.pyplot as plt
    from matplotlib.colors import ListedColormap
    
    # 导入tree模型和数据集加载工具
    from sklearn import tree,datasets
    
    # 导入数据集拆分工具
    from sklearn.model_selection import train_test_split
    
    wine=datasets.load_wine()
    
    # 只选取数据集的前两个特征
    X=wine.data[:,:2]
    y=wine.target
    
    # 将数据集拆分为训练集和测试集
    X_train,X_test,y_train,y_test=train_test_split(X,y)
    

    现在完成了数据集的准备,开始用决策树分类器进行分类

    注意 :为了便于图形进行演示,我们仍然只选取了数据集中样本的前两个特征

    # 设定决策树分类器最大深度为1
    clf=tree.DecisionTreeClassifier(max_depth=1)
    
    # 拟合训练数据集
    clf.fit(X_train,y_train)
    
    DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=1,
                max_features=None, max_leaf_nodes=None,
                min_impurity_decrease=0.0, min_impurity_split=None,
                min_samples_leaf=1, min_samples_split=2,
                min_weight_fraction_leaf=0.0, presort=False, random_state=None,
                splitter='best')
    

    [结果分析] 把分类器的参数返回,这些参数中,我们先关注其中之一,就是max_depth参数。这个参数指的是决策树的深度,也就是所问问题的数量,问题数量越多,就代表决策树的深度

    现在我们看看分类器的表现如何,我们把图形画出来:

    # 定义图像中分区的颜色和散点的颜色
    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=clf.predict(np.c_[xx.ravel(),yy.ravel()])
    
    # 给每个分类中的样本分配不同的颜色
    Z=z.reshape(xx.shape)
    
    plt.figure()
    plt.pcolormesh(xx,yy,Z,cmap=cmap_light)
    
    # 用散点图把样本表示出来
    plt.scatter(X[:,0],X[:,1],c=y,cmap=cmap_bold,edgecolors='k',s=20)
    
    plt.xlim(xx.min(),xx.max())
    plt.ylim(yy.min(),yy.max())
    
    plt.title("Classifier:(max_depth=1)")
    
    plt.show()
    

    output_10_0.png

    [结果分析] 很显然,最大深度等于1时分类器的表现肯定不会太好,分类器只分了两类。我们需要加大深度试试看

    # 设定决策树最大深度为3
    clf2=tree.DecisionTreeClassifier(max_depth=3)
    
    # 重新拟合数据
    clf2.fit(X_train,y_train)
    
    DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=3,
                max_features=None, max_leaf_nodes=None,
                min_impurity_decrease=0.0, min_impurity_split=None,
                min_samples_leaf=1, min_samples_split=2,
                min_weight_fraction_leaf=0.0, presort=False, random_state=None,
                splitter='best')
    
    # 定义图像中分区的颜色和散点的颜色
    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=clf2.predict(np.c_[xx.ravel(),yy.ravel()])
    
    # 给每个分类中的样本分配不同的颜色
    Z=z.reshape(xx.shape)
    
    plt.figure()
    plt.pcolormesh(xx,yy,Z,cmap=cmap_light)
    
    # 用散点图把样本表示出来
    plt.scatter(X[:,0],X[:,1],c=y,cmap=cmap_bold,edgecolors='k',s=20)
    
    plt.xlim(xx.min(),xx.max())
    plt.ylim(yy.min(),yy.max())
    
    plt.title("Classifier:(max_depth=3)")
    
    plt.show()
    

    output_13_0.png

    [结果分析] 当决策树最大深度设为3的时候,分类器能够进行3个分类的识别,而且大部分数据点都进入了正确的分类,当然还有一小部分数据点的分类是错误的

    接下来进一步调整max_depth的值

    # 设定决策树最大深度为5
    clf3=tree.DecisionTreeClassifier(max_depth=5)
    
    # 重新拟合数据
    clf3.fit(X_train,y_train)
    
    DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=5,
                max_features=None, max_leaf_nodes=None,
                min_impurity_decrease=0.0, min_impurity_split=None,
                min_samples_leaf=1, min_samples_split=2,
                min_weight_fraction_leaf=0.0, presort=False, random_state=None,
                splitter='best')
    
    # 定义图像中分区的颜色和散点的颜色
    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=clf3.predict(np.c_[xx.ravel(),yy.ravel()])
    
    # 给每个分类中的样本分配不同的颜色
    Z=z.reshape(xx.shape)
    
    plt.figure()
    plt.pcolormesh(xx,yy,Z,cmap=cmap_light)
    
    # 用散点图把样本表示出来
    plt.scatter(X[:,0],X[:,1],c=y,cmap=cmap_bold,edgecolors='k',s=20)
    
    plt.xlim(xx.min(),xx.max())
    plt.ylim(yy.min(),yy.max())
    
    plt.title("Classifier:(max_depth=5)")
    
    plt.show()
    

    output_16_0.png

    我们可能会感到好奇,在这个过程中,决策树在每一层当中都做了哪些事情呢?我们可以用一个名叫graphviz的库来展示一下这个过程。首先需要安装这个库:

    pip install graphviz

    注意 graphviz只是帮助我们演示决策树的工作过程,对于读者来说,安装他并不是必须的

    # 导入graphviz工具
    import graphviz
    # 导入决策树中输出graphviz的接口
    from sklearn.tree import export_graphviz
    # 选择最大深度为3的分类模型
    export_graphviz(clf2,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()
        
    # 显示dot文件中的图形
    graphviz.Source(dot_graph)
    

    output_18_0.svg

    注意 为了控制图片不要太大,我们选择用max_depth=3的分类器来绘制图形,这样可以方便观看

    3.决策树的优势和不足

    相比其他算法,决策树有一个非常大的优势,就是可以很容易地将模型进行可视化,就像我们在上图中所做的一样。另外,由于决策树算法对每个样本特征进行单独处理,因此并不需要对数据进行转换。这样一来,如果使用决策树算法的话,我们几乎不需要对数据进行预处理。这也是决策树算法的一个优点

    当然,决策树算法也有它的不足之处—即便我们在建模的时候可以使用类似max_depth或是max_leaf_nodes等参数来对决决策树进行预剪枝处理,但它还是不可避免会出现多拟合的问题,也就让模型的泛化性能大打折扣了

    为了避免过拟合的问题出现,可以使用集合学习的方法,也就是我们下面要介绍的—随机森林算法

    二.随机森林

    常言道,不要为了一棵树放弃一片森林。这句话在机器学习算法方面也是非常正确的

    1.随机森林的基本概念

    随机森林有的时候也被称为是随机决策森林,是一种集合学习方法,既可以用于分类,也可以用于回归。而所谓集合学习算法,其实就是把多个机器学习算法综合在一起,制造出一个更加大模型的意思。这也就很好地解释了为什么这种算法称为随机森林

    在机器学习的领域,其实有很多中集合算法,目前应用比较广泛的就包括随机森林(Random Forests)梯度上升决策树(Gradient Boosted Decision Trees,GBDT)

    前面我们提到,决策树算法很容易出现过拟合的现象。那么为什么随机森林可以解决这个问题呢?因为随机森林是把不同的几棵决策树打包到一起,每棵树的参数都不相同,然后我们把每棵树预测的结果取平均值,这样即可以保留决策树们的工作成效,又可以降低过拟合的风险

    2.随机森林的构建

    这次我们继续用在决策树中来展示酒的数据集,输入代码如下:

    # 导入随机森林模型
    from sklearn.ensemble import RandomForestClassifier
    
    # 载人红酒数据集
    wine=datasets.load_wine()
    
    # 选择数据集前两个特征
    X=wine.data[:,:2]
    y=wine.target
    
    # 将数据集拆分为训练集和测试集
    X_train,X_test,y_train,y_test=train_test_split(X,y)
    
    # 设定随机森林中有6颗树
    forest=RandomForestClassifier(n_estimators=6,random_state=3)
    
    # 使用模型拟合数据
    forest.fit(X_train,y_train)
    
    RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
                max_depth=None, max_features='auto', max_leaf_nodes=None,
                min_impurity_decrease=0.0, min_impurity_split=None,
                min_samples_leaf=1, min_samples_split=2,
                min_weight_fraction_leaf=0.0, n_estimators=6, n_jobs=None,
                oob_score=False, random_state=3, verbose=0, warm_start=False)
    

    [结果分析] 可以看到,随机森林向我们返回了包含其自身全部参数的信息,让我们重点看一下其中几个必要重要的参数

    首先是bootstrap参数,代表的是bootstrap sample,也就是"有放回抽样"的意思,指每次从样本空间中可以重复抽取同一个样本(因为样本在第一次被抽取之后又被放回去了),形象一点来说,如原始样本是[‘苹果’,‘西瓜’,‘香蕉’,‘桃子’],那么经过bootstrap sample重构的样本就可能是[‘西瓜’,‘西瓜’,‘香蕉’,‘桃子’],还有可能是[‘苹果’,‘西瓜’,‘桃子’,‘桃子’]。Bootstrap sample生成的数据集和原始数据集在数据量上是完全一样的,D但由于进行了重复采样,因此其中有一些数据点会丢失

    看到这里,可能会问为什么要生成bootstrap sample数据集。这是因为通过重新生成数据集,可以让随机森林中的每一棵决策树在构建的时候,会彼此之间有些差异。再加上每棵树的节点都会去选择不同的样本特征,经过这两步动作之后,可以完全肯定随机森林中的每棵树都不一样,这也符合我们使用随机森林的初衷

    另外还有一个要强调的参数,是n_estimators,这个参数控制的是随机森林中决策树的数量。在随机森林构建完成之后,每棵决策树都会单独进行预测。如果是用来进行回归分析的话,随机森林会把所有决策树预测的值取平均数;如果是用来进行分类的话,在森林内部会进行"投票",每棵树预测出数据类别的概率,比如其中一棵树说,“这瓶酒80%数据class_1”,另外一棵树说,“这瓶酒60%属于class_2”,随机森林会把这些概率取平均值,然后把样本放入概率最高的分类当中

    下面我们用图像直观地看一下随机森林分类的表现,输入代码如下:

    # 定义图像中分区的颜色和散点的颜色
    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.figure()
    plt.pcolormesh(xx,yy,Z,cmap=cmap_light)
    
    # 用散点图把样本表示出来
    plt.scatter(X[:,0],X[:,1],c=y,cmap=cmap_bold,edgecolors='k',s=20)
    
    plt.xlim(xx.min(),xx.max())
    plt.ylim(yy.min(),yy.max())
    
    plt.title("Classifier:(max_depth=5)")
    
    plt.show()
    

    output_24_0.png

    3.随机森林的优势和不足

    从优势的角度来说,随机森林集成了决策树的所有优点,而且能够弥补决策树的不足。但也不是说决策树算法就被彻底抛弃了。从便于展示决策过程的角度来说,决策树依旧表现强悍。尤其是随机森林中每棵决策树的层级要比单独的决策树更深,所以如果需要向非专业人士展示模型工作过程的话,还是需要用到决策树的

    还有,随机森林算法支持并行处理。对于超大数据集来说,随机森林会比较耗时,不过我们可以用多进程并行处理的方式来解决这个问题。实现方式是调节随机森林的n_jobs参数,记得把n_jobs参数数值设为和CPU内核数一致。如果你搞不清楚自己的CPU到底就多少内核,可以设置n_jobs=-1,这样随机森林会使用CPU的全部内核,速度就会极大提升了

    需要注意的是,因为随机森林生成每棵决策树的方法是随机的,那么不同的random_state参数会导致模型完全不同,所以如果不希望建模的结果太过于不稳定,一定要固化random_state这个参数的数值

    缺点是,对于超高维数据集,稀疏数据集等来说,随机森林就有点捉襟见肘了,在这种情况下,线性模型要比随机森林的表现更好一些

    三.随机森林实例—相亲事件

    小花的同事给她介绍了一个对象Mr-L,现在Mr-L现年37岁,在某省机关做文员工作。但是小花的择偶标准是需要对方月薪在5万以上(只是为了引入后面的内容),但是又不好直接问Mr-L,所以拿不定主意,这时我们用决策树和随机森林

    1.数据集的准备

    在网站下载数据集:成年人数据集

    下载好的数据集是.data格式的文件,不过不用担心,它其实就是一个csv文件,我们把它重命名为adult.csv

    下面我们载人这个数据集,输入代码如下:

    # 导入pandas库
    import pandas as pd
    
    # 用pd打开csv文件
    data=pd.read_csv('adult.csv',header=None,index_col=False,
                     names=['年龄','单位性质','权重','学历','受教育时长',
                            '婚姻状况','职业','家庭情况','种族','性别',
                            '资产所得','资产损失','周工作时长','原籍',
                            '收入'
                           ]
                    )
    #为了方便展示,我们选取其中一部分数据
    data_list=data[['年龄','单位性质','学历','性别','周工作时长','职业','收入']]
    
    display(data_list.head())
    
    年龄 单位性质 学历 性别 周工作时长 职业 收入
    0 39 State-gov Bachelors Male 40 Adm-clerical <=50K
    1 50 Self-emp-not-inc Bachelors Male 13 Exec-managerial <=50K
    2 38 Private HS-grad Male 40 Handlers-cleaners <=50K
    3 53 Private 11th Male 40 Handlers-cleaners <=50K
    4 28 Private Bachelors Female 40 Prof-specialty <=50K

    注意 为了方便演示,我们只选了年龄,单位性质,学历,性别,周工作时长,职业和收入等特征

    2.用get_dummies处理数据

    在这个数据集中,单位性质,学历,性别,职业还有收入都不是整型数值,而是字符串,怎么使用我们现在所学的知识进行建模呢?这里我们用到pandas的一个功能,叫作get_dummies,它可以在现有的数据集上添加虚拟变量,让数据集变成可以用的格式

    # 使用get_dummies将文本数据转化为数值
    data_dummies=pd.get_dummies(data_list)
    
    # 对比样本原始特征和虚拟变量特征
    print("样本原始特征:
    ",list(data_list.columns),'
    ')
    print("虚拟变量特征:
    ",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']
    

    下面我们看下进行get_dummies后数据集的样子,显示前5行数据:

    # 显示数据集中的前5行
    data_dummies.head()
    
    年龄 周工作时长 单位性质_ ? 单位性质_ Federal-gov 单位性质_ Local-gov 单位性质_ Never-worked 单位性质_ Private 单位性质_ Self-emp-inc 单位性质_ Self-emp-not-inc 单位性质_ State-gov 职业_ Machine-op-inspct 职业_ Other-service 职业_ Priv-house-serv 职业_ Prof-specialty 职业_ Protective-serv 职业_ Sales 职业_ Tech-support 职业_ Transport-moving 收入_ <=50K 收入_ >50K
    0 39 40 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0
    1 50 13 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0
    2 38 40 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0
    3 53 40 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0
    4 28 40 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0

    5 rows × 46 columns

    现在我们各列分配给特征向量X和分类标签y,输入代码如下:

    # 定义数据集的特征值
    features=data_dummies.loc[:,'年龄':'职业_ Transport-moving']
    
    # 将特征数值赋值为x
    X=features.values
    
    # 将收入大于50k作为预测目标
    y=data_dummies['收入_ >50K'].values
    
    print("特征形态:{},标签形态:{}".format(X.shape,y.shape))
    
    特征形态:(32561, 44),标签形态:(32561,)
    

    3.用决策树建模并作出预测

    数据集共有32561条样本数据,每条数据有44个特征值,接下来将数据集拆分成训练集和测试集,然后用决策树算法进行建模,并对模型进行评估:

    # 将数据集拆分为训练集和测试集
    X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=0)
    
    # 用最大深度为5的随机森林拟合数据
    go_dating_tree=tree.DecisionTreeClassifier(max_depth=5)
    go_dating_tree.fit(X_train,y_train)
    
    print("模型得分:{:.2f}".format(go_dating_tree.score(X_test,y_test)))
    
    模型得分:0.80
    

    使用模型对Mr-L的收入进行预测,输入代码如下:

    # 将Mr Z的数据输入给模型
    Mr=[[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]]
    
    # 使用模型做出预测
    dating_dec=go_dating_tree.predict(Mr)
    
    if dating_dec==1:
        print("大胆追求真爱,这人月薪过5万")
    else:
        print("不用去了,不满足")
    
    不用去了,不满足
    
  • 相关阅读:
    Mantis安装过程笔记
    Python脚本文件中使用中文
    String.trim的作用
    Ivy入门笔记
    Groovy正则表达式复杂逻辑判断实例
    Build a ZenTao Server on Linux
    Ubuntu上安装gevent
    Install Redmine Server with Bitnami Installer
    Semi-automation Script Based on Sleep
    关于MySQL的事务处理及隔离级别
  • 原文地址:https://www.cnblogs.com/LQ6H/p/10415083.html
Copyright © 2020-2023  润新知