IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> GBDT和随机森林的区别 -> 正文阅读

[数据结构与算法]GBDT和随机森林的区别

学习总结

  • 随机森林中的随机主要来自三个方面:
    • 其一为bootstrap抽样导致的训练集随机性,
    • 其二为每个节点随机选取特征子集进行不纯度计算的随机性,
    • 其三为当使用随机分割点选取时产生的随机性(此时的随机森林又被称为Extremely Randomized Trees)。
  • 一些机器学习的特征工程API(特征提取、特征预处理、特征降维等)要熟悉。如DictVectorizer字典特征提取,如果在参数中设定sparse = FalseDictVectorizer默认返回的是一个one hot编码矩阵,如果没设定sparse = False则默认是返回一个稀疏矩阵,对应每行的值是矩阵中非零元素的坐标位置。

一、模型的误差Error分析

在这里插入图片描述

1.1 估测变量x的偏差和方差

【举栗子】
一次打靶实验,目标是为了打到10环,但是实际上只打到了7环,那么这里面的Error就是3。具体分析打到7环的原因,可能有两方面:

  • 一是瞄准出了问题,比如实际上射击瞄准的是9环而不是10环,这里的差值1就是bias,表示模型期望和真实目标的差距;
  • 二是枪本身的稳定性有问题,虽然瞄准的是9环,但是只打到了7环,这里的差值2就是variance,表示模型的稳定性,即训练得到的模型之间的变动水平。
  • Error = Bias + Variance,Error反映的是整个模型的准确度。

在这里插入图片描述
具体的公式推导如下:

  • 首先假设从n个样本点中学习到 y = f ( x ) + ε y=f(x)+\varepsilon y=f(x)+ε,这里的 ε \varepsilon ε表示噪音。
  • 目标是从采样到的点中学习到 f ^ \hat{f} f^?,使得 f ^ \hat{f} f^? 和我们真实的 f ( x ) f(x) f(x)尽可能相近,这里最简单的方法就是最小化均方误差(MSE)。注意下面的平方项木有了,因为之间相互独立。

E D [ ( y ? f ^ D ( x ) ) 2 ] = E D [ ( ( f ? E D [ f ^ D ] ) ? ( f ^ D ? E D [ f ^ D ] ) + ε ) 2 ] = ( f ? E D [ f ^ D ] ) 2 + E D [ ( f ^ D ? E D [ f ^ D ] ) ) 2 ] + ε 2 = Bias ? [ f ^ D ] 2 + Var ? [ f ^ D ] + ε 2 \begin{aligned} \mathrm{E}_{D}\left[\left(y-\hat{f}_{D}(x)\right)^{2}\right] &=\mathrm{E}_{D}\left[\left(\left(f-\mathrm{E}_{D}\left[\hat{f}_{D}\right]\right)-\left(\hat{f}_{D}-\mathrm{E}_{D}\left[\hat{f}_{D}\right]\right)+\varepsilon\right)^{2}\right] \\ &\left.=\left(f-\mathrm{E}_{D}\left[\hat{f}_{D}\right]\right)^{2}+\mathrm{E}_{D}\left[\left(\hat{f}_{D}-\mathrm{E}_{D}\left[\hat{f}_{D}\right]\right)\right)^{2}\right]+\varepsilon^{2} \\ &=\operatorname{Bias}\left[\hat{f}_{D}\right]^{2}+\operatorname{Var}\left[\hat{f}_{D}\right]+\varepsilon^{2} \end{aligned} ED?[(y?f^?D?(x))2]?=ED?[((f?ED?[f^?D?])?(f^?D??ED?[f^?D?])+ε)2]=(f?ED?[f^?D?])2+ED?[(f^?D??ED?[f^?D?]))2]+ε2=Bias[f^?D?]2+Var[f^?D?]+ε2?

1.2 不同模型情况

(1)不同模型的方差

一次模型的方差就比较小的,也就是是比较集中,离散程度较小。而5次模型的方差就比较大,同理散布比较广,离散程度较大。

所以用比较简单的模型,方差是比较小的(就像射击的时候每次的时候,每次射击的设置都集中在一个比较小的区域内)。如果用了复杂的模型,方差就很大,散布比较开。

这也是因为简单的模型受到不同训练集的影响是比较小的。

(2)不同模型的偏差

在这里插入图片描述
这里没办法知道真正的 f ^ \hat{f} f^?,所以假设图中的那条黑色曲线为真正的 f ^ \hat{f} f^?
结果可视化,一次平均的 f ˉ \bar{f} fˉ? 没有5次的好,虽然5次的整体结果离散程度很高。

一次模型的偏差比较大,而复杂的5次模型,偏差就比较小。
直观的解释:简单的模型函数集的space比较小,所以可能space里面就没有包含靶心,肯定射不中。而复杂的模型函数集的space比较大,可能就包含的靶心,只是没有办法找到确切的靶心在哪,但足够多,就可能得到真正的 f ˉ fˉ fˉ
在这里插入图片描述
每一个model就是一个function set,可以用上图的左下方的圈圈表示这个function set,即范围。一个简单的model的set是比较小的(可能就根本没有包含target),而上图左边的五次方方程曲线,这时的function set比较大。 虽然分布的比较散,没有办法找出target(数据少),但是比较分散在中心周围,平均起来能接近 f ˉ fˉ fˉ

(3)方差VS偏差

在这里插入图片描述
将系列02中的误差拆分为偏差和方差。

  • U n d e r f i t t i n g Underfitting Underfitting 欠拟合:简单模型(左边)是偏差( b i a s bias bias)比较大造成的误差
  • O v e r f i t t i n g Overfitting Overfitting 过拟合:复杂模型(右边)是方差( v a r i a n c e variance variance)过大造成的误差(过拟合,即在训练集表现良好,但是在测试集上很糟糕)

在这里插入图片描述

  • 欠拟合( b i a s bias bias偏大)
    • 添加其他特征项:如组合、泛化、相关性、上下文特征、平台特征等;
    • 添加多项式特征:如线性模型添加二次型模型使得泛化能力更强;
    • 减少正则化参数:正则化是用来防止过拟合的,但现在模型欠拟合,则需要减少正则化参数。
    • 注意:如果此时强行再收集更多的data去训练,这是没有什么帮助的,因为设计的函数集本身就不好,再找更多的训练集也不会更好。
  • 过拟合( v a r i a n c e variance variance偏大)
    • 重新清洗数据
    • 增加训练的数据量(如下图),可学习的特征太少,如sparrow项目的电影数据集过少,过拟合,deepFM模型参数难以收敛。
    • 使用正则化方法,使得参数越小越好(找到的曲线更平滑),也可以对 r e g u l a r i z a t i o n regularization regularization一项加上 w e i g h t weight weight。但是正则化可能影响 b i a s bias bias(曲线都平滑时可能就没包含目标的function)。
    • 使用dropout方法,在深度学习中常用(imageNet提出的)

在这里插入图片描述

但是很多时候不一定能做到收集更多的data。也很多种收集(调整)数据的方法,针对对问题的理解对数据集做调整。比如识别手写数字的时候,偏转角度的数据集不够,那就将正常的数据集左转15度,右转15度,类似这样的处理。

二、bagging和boosting的区别

基分类器的错误 = 偏差 + 方差。

而在机器学习中其实有2个任务:
任务一: 如何优化训练数据 —> 主要用于解决欠拟合问题
任务二: 如何提升泛化性能 —> 主要用于解决过拟合问题

在这里插入图片描述

  • Boosting 通过逐步聚集基分类器分错的样本,减少集成分类器的偏差;训练好一个弱分类器后,计算其错误或残差,作为下一个分类器的输入——该过程在不断减小损失函数,使模型不断逼近“靶心”。
  • Bagging 通过分而治之的策略,通过对训练样本多次采用,综合决策多个训练出来的模型,来减少集成分类器的方差。 有点不严谨地说,对n个独立不相关的模型的预测结果取平均, 方差是原来单个模型的1/n。

三、决策树基础

3.1 不同的特征选择方案

决策树常用于分类,目标就是将具有 P P P 维特征的 n n n 个样本分到 C C C 个类别中,相当于做一个映射 C = f ( n ) C = f(n) C=f(n) ,将样本经过一种变换赋予一个 l a b e l label label。可以把分类的过程表示成一棵树,每次通过选择一个特征 p i pi pi 来进行进一步分叉。而根据每次分叉选择哪个特征对样本进行划分,能够又快又准地对样本进行分类,即根据不同的特征选择方案,我们分为了:ID3、C4.5、CART等算法。

ID3树C4.5树CART算法
评价标准信息增益信息增益比基尼指数
样本类型离散型变量连续型变量连续型变量
任务分类分类分类and回归(回归树使用最小平方误差)

3.2 决策树的划分依据之一——信息增益

ID3使用【信息增益度】选择测试属性。信息增益表示得知特征 X X X的信息而使得类 Y Y Y的信息不确定性减少的程度。

特征 X X X对训练数据集 Y Y Y的信息增益 G ( Y , X ) G(Y,X) G(Y,X),定义为集合 D D D的信息熵 H ( D ) H(D) H(D)与特征 X X X在给定条件 D D D的信息条件熵 H ( Y ∣ X ) H(Y\vert X) H(YX)之差。信息增益 = 信息熵 - 条件熵: G ( Y , X ) = H ( Y ) ? H ( Y ∣ X ) G(Y,X)=H(Y)-H(Y\vert X) G(Y,X)=H(Y)?H(YX)

  • 熵指信息量的大小,即表示随机变量的不确定性,信息熵越大,随机变量的不确定性越大。如果用p(ai)表示事件ai发生的概率,熵则表示为事件 a i ai ai的信息量I(ai):
    I ( a i ) = p ( a i ) log ? 2 1 p ( a i ) I\left(a_{i}\right)=p\left(a_{i}\right) \log _{2} \frac{1}{p\left(a_{i}\right)} I(ai?)=p(ai?)log2?p(ai?)1?
    取任意随机变量的概率均相同时,不确定性越高,即均匀分布的信息熵最大。

  • 条件熵:在一个条件下,随机变量的不确定性。

  • 信息增益:熵 - 条件熵。表示在一个条件下,信息不确定性减少的程度。

通俗地讲,X(明天下雨)是一个随机变量,X的熵可以算出来, Y(明天阴天)也是随机变量,在阴天情况下下雨的信息熵我们如果也知道的话(此处需要知道其联合概率分布或是通过数据估计)即是条件熵。X的熵减去Y条件下X的熵,就是信息增益。具体解释:原本明天下雨的信息熵是2,条件熵是0.01(因为如果知道明天是阴天,那么下雨的概率很大,信息量少),这样相减后为1.99。在获得阴天这个信息后,下雨信息不确定性减少了1.99,不确定减少了很多,所以信息增益大。也就是说,阴天这个信息对明天下午这一推断来说非常重要。所以在特征选择的时候常常用信息增益,如果IG(信息增益大)的话那么这个特征对于分类来说很关键,决策树就是这样来找特征的。

3.3 决策树的优缺点

  • 优点
    • 易于理解和解释,树木可视化
    • 需要很少的数据准备,其他算法模型一般需要进行数据归一化
  • 缺点
    • 可以创建决策树,但是不能很好推广数据过于复杂的树,即会过拟合,复杂的树在测试集中表现不一定好。
  • 改进:
    • 减枝CART算法(sklearn决策树中的api已经实现,参考下面的随机森林调优栗子)
    • 随机森林

四、GBDT和随机森林的区别

(一)GBDT=决策树+AdaBoost集成学习。GBDT通过基学习器继续学习上一个学习器拟合的残差(用负梯度去拟合残差),在预测的过程中,我们也需要把所有树的预测值加起来,得到最终的预测结果。

(二)随机森林是以决策树(常用CART树)为基学习器的bagging算法。

(1)随机森林当处理回归问题时,输出值为各学习器的均值;

(2)随机森林当处理分类问题时有两种策略:

  • 第一种是原始论文中使用的投票策略,即每个学习器输出一个类别,返回最高预测频率的类别;
  • 第二种是sklearn中采用的概率聚合策略,即通过各个学习器输出的概率分布先计算样本属于某个类别的平均概率,在对平均的概率分布取 a r g m a x argmax argmax 以输出最可能的类别。

五、随机森林代码部分

5.0 随机森林原理

1.1 算法介绍

随机森林是以决策树(常用CART树)为基学习器的bagging算法。简单说就是每次采样不同数据集,分别训练对应的分类器,最后平均投票得到结果。包外估计(Out-of-Bag Estimate)是对集成分类器泛化误差的无偏估计。

在随机森林算法中数据集属性的重要性、 分类器集强度和分类器间相关性计算都依赖于袋外数据。

在这里插入图片描述

(1)随机森林当处理回归问题时,输出值为各学习器的均值;
(2)随机森林当处理分类问题时有两种策略:

  • 第一种是原始论文中使用的投票策略,即每个学习器输出一个类别,返回最高预测频率的类别;
  • 第二种是sklearn中采用的概率聚合策略,即通过各个学习器输出的概率分布先计算样本属于某个类别的平均概率,在对平均的概率分布取 arg ? max ? \arg\max argmax以输出最可能的类别。

随机森林中的随机主要来自三个方面:

  • 其一为bootstrap抽样导致的训练集随机性,
  • 其二为每个节点随机选取特征子集进行不纯度计算的随机性,
  • 其三为当使用随机分割点选取时产生的随机性(此时的随机森林又被称为Extremely Randomized Trees)。

随机森林中特征重要性的计算方式为:利用相对信息增益来度量单棵树上的各特征特征重要性(与决策树计算方式一致),再通过对所有树产出的重要性得分进行简单平均来作为最终的特征重要性。

【练习r2_score和均方误差的区别是什么?它具有什么优势?
r2_score是判定系数:回归模型的方差系数。
r2_score的计算公式如下:(本质上是以均值模型作为baseline model,计算该模型相较于它的好坏) R 2 ( y , y ^ ) = 1 ? ∑ i = 0 n ? 1 ( y i ? y i ^ ) 2 ∑ i = 0 n ? 1 ( y i ? y  ̄ ) 2 R^2(y,\hat{y})=1-\frac{\sum_{i=0}^{n-1}(y_i-\hat{y_i})^2}{\sum_{i=0}^{n-1}(y_i-\overline{y})^2} R2(y,y^?)=1?i=0n?1?(yi??y?)2i=0n?1?(yi??yi?^?)2?? MSE是均方误差,即线性回归的损失函数,计算公式如下: M S E ( y , y ^ ) = 1 n ∑ i = 0 n ? 1 ( y i ? y ^ i ) 2 MSE(y,\hat{y})=\frac{1}{n}\sum_{i=0}^{n-1}(y_i-\hat{y}_i)^2 MSE(y,y^?)=n1?i=0n?1?(yi??y^?i?)2?其中分子是训练出的模型的所有误差,分母是使用y真=y真平均 预测产生的误差。
二者的区别是 R 2 ( y , y ^ ) = 1 ? M S E ( y , y ^ ) σ 2 R^2(y,\hat{y})=1-\frac{MSE(y,\hat{y})}{\sigma^2} R2(y,y^?)=1?σ2MSE(y,y^?)??,其中 σ \sigma σ表示y的标准差???。

MSE是带量纲的,而且结果为量纲的平方,而r2_score是不带量纲的,可以比较模型在不同量纲数据(不同问题)上的好坏。

【oob样本】:(oob全称为Out-of-Bag Estimate)在训练时,一般而言我们总是需要对数据集进行训练集和验证集的划分,但随机森林由于每一个基学习器使用了重复抽样得到的数据集进行训练,因此总存在比例大约为 e ? 1 e^{-1} e?1(其计算如下公式所得)的数据集没有参与训练,这一部分数据称为out-of-bag样本(即oob样本)。
在这里插入图片描述

对每一个基学习器训练完毕后,我们都对oob样本进行预测,每个样本对应的oob_prediction_值为所有没有采样到该样本进行训练的基学习器预测结果均值,这一部分的逻辑参见此处的源码实现。

在得到所有样本的oob_prediction_后:
(1)对于回归问题,使用r2_score来计算对应的oob_score_
(2)而对于分类问题,直接使用accuracy_score来计算oob_score_

1.2 Totally Random Trees Embedding

介绍一种Totally Random Trees Embedding方法,它能够基于每个样本在各个决策树上的叶节点位置,得到一种基于森林的样本特征嵌入。

【栗子】假设现在有4棵树且每棵树有4个叶子节点(共16个节点),依次对它们进行从0至15的编号,记样本 i i i在4棵树叶子节点的位置编号为 [ 0 , 7 , 8 , 14 ] [0,7,8,14] [0,7,8,14],样本 j j j的编号为 [ 1 , 7 , 9 , 13 ] [1,7,9,13] [1,7,9,13],此时这两个样本的嵌入向量即为 [ 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 0 ] [1,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0] [1,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0] [ 0 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 1 , 0 , 0 , 0 , 1 , 0 , 0 ] [0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0] [0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0]

假设样本 k k k对应的编号为 [ 0 , 6 , 8 , 14 ] [0,6,8,14] [0,6,8,14],那么其对应嵌入向量的距离应当和样本 i i i 较近,而离样本 j j j 较远,即两个样本在不同树上分配到相同的叶子结点次数越多,则越接近。因此,这个方法巧妙地利用树结构获得了样本的隐式特征。

【练习】假设使用闵氏距离来度量两个嵌入向量之间的距离,此时对叶子节点的编号顺序会对距离的度量结果有影响吗?
没有关系。
闵式距离为: D ( x , y ) = ( ∑ u = 1 n ∣ x u ? y u ∣ p ) 1 p D(x,y)=(\sum_{u=1}^n|x_u-y_u|^p)^{\frac{1}{p}} Dx,y=(u=1n?xu??yu?p)p1? 嵌入向量是依据决策森林样本叶节点落位而进行multi_hot encoding的一个结果(对应位取值为1,其他为0),只要叶子节点编号的每个维度的权重一样(这里都是1)。

5.1 随机森林的应用栗子

  • 随机森林的优点
    • 准确率高,能有效运行在大数据集上;
    • 能够处理具有高维特征的输入样本(每颗树只用部分特征),而且不需要降维;
    • 能够评估各个特征在分类问题上的重要性。
      使用的是最常见的泰坦尼克号数据集:
  • 首先基本的数据处理:
    • 确定特征值x(简单起见,只选择性别年龄等3个特征)和目标值y
    • fillna缺失值处理,缺失的年龄用平均年龄值填充;
    • train_test_split随机划分训练集和测试集
  • 特征工程,常言道~数据和特征决定机器学习的上限,而模型和算法逼近上限。常用的特征工程如特征提取、特征预处理、特征降维等。
    • 特征提取是将任意数据(如文本或者图像)转换为机器学习的数字特征(特征值化是为了让计算机更好的去理解数据),包括字典特征抽取、文本特征提取、图像特征提取(深度学习)等。
    • 这里我们可以用DictVectorizer进行字典特征提取,如果在参数中设定sparse = FalseDictVectorizer默认返回的是一个one hot编码矩阵,如果没设定sparse = False则默认是返回一个稀疏矩阵,对应每行的值是矩阵中非零元素的坐标位置(如下面栗子);
#1)实例化一个转换器类 2)调用fir_transform()方法
from sklearn.feature_extraction import  DictVectorizer#导包
#下面的data是数据
data=[{'city':'北京','tempreature':100},
      {'city':'上海','tempreature':60},
      {'city':'深圳','tempreature':30},]

#1实例化一个转换器类
# transfer=DictVectorizer()
"""
看一下转换的结果data_new:
   (0, 1)	1.0
  (0, 3)	100.0
  (1, 0)	1.0
  (1, 3)	60.0
  (2, 2)	1.0
  (2, 3)	30.0
看一下特征的名字:
 ['city=上海', 'city=北京', 'city=深圳', 'tempreature']
"""

transfer=DictVectorizer(sparse = False)
"""
看一下转换的结果data_new:
 [[  0.   1.   0. 100.]
 [  1.   0.   0.  60.]
 [  0.   0.   1.  30.]]
看一下特征的名字:
 ['city=上海', 'city=北京', 'city=深圳', 'tempreature']
"""
#2调用一fit_transform()方法
data_new=transfer.fit_transform(data)
print("看一下转换的结果data_new:\n",data_new)
print("看一下特征的名字:\n",transfer.get_feature_names())
  • 通过GridSearchCV进行网格化搜索确定参数:max_depth最大树高、n_estimators树的个数。

完整代码如下:

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction import DictVectorizer
from sklearn.tree import DecisionTreeClassifier, export_graphviz


# 1.获取数据
titan = pd.read_csv("titanic.csv")

# 2.数据基本处理
# 2.1 确定特征值,目标值
x = titan[["pclass", "age", "sex"]]
y = titan["survived"]

# 2.2 缺失值处理
x['age'].fillna(value=titan["age"].mean(), inplace=True)

# 2.3 数据集划分
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=22, test_size=0.2)

# 3.特征工程(字典特征抽取)
x_train = x_train.to_dict(orient="records")
x_test = x_test.to_dict(orient="records")
transfer = DictVectorizer()
x_train = transfer.fit_transform(x_train)
x_test = transfer.fit_transform(x_test)

# 4.机器学习(决策树)
estimator = DecisionTreeClassifier(max_depth=15)
estimator.fit(x_train, y_train)

# 5.模型评估
y_pre = estimator.predict(x_test)
ret = estimator.score(x_test, y_test)
export_graphviz(estimator, out_file="./tree.dot", feature_names=['age', 'pclass=1st', 'pclass=2nd', 'pclass=3rd', '女性', '男性'])
 
# 实例化一个随机森林
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier()
 
# 通过超参数调优
from sklearn.model_selection import GridSearchCV
param = {"n_estimators":[100, 120, 300], "max_depth":[3, 7, 11]}

# 进行网格搜索
gc = GridSearchCV(rf, param_grid = param, cv = 3)
gc.fit(x_train, y_train)
print("随机森林预测结果是:\n", gc.score(x_test, y_test))

得到的随机森林预测结果为:0.779467680608365。

5.2 随机森林的主要参数

(1)RF框架的参数

RF框架的参数:RandomForestClassifierRandomForestRegressor参数绝大部分相同。
(1)n_estimators(重点): 也就是最大的弱学习器的个数。一般来说n_estimators太小,容易欠拟合,n_estimators太大,计算量会太大,并且n_estimators到一定的数量后,再增大n_estimators获得的模型提升会很小,所以一般选择一个适中的数值。默认是100。

(2)oob_score :即是否采用袋外样本来评估模型的好坏。默认识False。个人推荐设置为True,因为袋外分数反应了一个模型拟合后的泛化能力。

(3)criterion: 即CART树做划分时对特征的评价标准。分类模型和回归模型的损失函数是不一样的

  • 分类RF对应的CART分类树默认是基尼系数gini;另一个可选择的标准是信息增益。
  • 回归RF对应的CART回归树默认是均方差mse,另一个可以选择的标准是绝对值差mae。一般来说选择默认的标准就已经很好的。

(2)RF决策树的参数

(1)RF划分时考虑的最大特征数max_features: 可以使用很多种类型的值,

  • 默认是"auto",意味着划分时最多考虑 N \sqrt{N} N ?个特征;
  • 如果是"log2"意味着划分时最多考虑 log ? 2 N \log _{2} N log2?N个特征;
  • 如果是"sqrt"或者"auto"意味着划分时最多考虑 N \sqrt{N} N ?个特征。
  • 如果是整数,代表考虑的特征绝对数。如果是浮点数,代表考虑特征百分比,即考虑(百分比xN)取整后的特征数。其中N为样本总特征数。一般我们用默认的"auto"就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。

(2)决策树最大深度max_depth:默认可以不输入,如果不输入的话,决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。

(3)内部节点再划分所需最小样本数min_samples_split:这个值限制了子树继续划分的条件,如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分。 默认是2.如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。

(4)叶子节点最少样本数min_samples_leaf:这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。

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

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

(7)节点划分最小不纯度min_impurity_split:这个值限制了决策树的增长,如果某节点的不纯度(基于基尼系数,均方差)小于这个阈值,则该节点不再生成子节点。即为叶子节点 。一般不推荐改动默认值1e-7。

上面决策树参数中最重要的包括最大特征数max_features, 最大深度max_depth, 内部节点再划分所需最小样本数min_samples_split和叶子节点最少样本数min_samples_leaf

5.3 手写随机森林

手写随机森林算法,然后和sklearn中的RandomForestClassifier进行结果的比较,即预测一致性的比例。

# -*- coding: utf-8 -*-
import numpy as np
from sklearn.tree import DecisionTreeClassifier as Tree
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier as RF
# from RandomForest_Classifier import RandomForest
import numpy as np

# 随机森林
class RandomForest:
    def __init__(self, n_estimators, max_depth):
        self.n_estimators = n_estimators
        self.max_depth = max_depth
        self.trees = []

    def fit(self, X, y):
        for tree_id in range(self.n_estimators):
            indexes = np.random.randint(0, X.shape[0], X.shape[0])
            random_X = X[indexes]
            random_y = y[indexes]
            tree = Tree(max_depth=3)
            tree.fit(random_X, random_y)
            self.trees.append(tree)

    def predict(self, X):
        results = []

        for x in X:
            result = []
            for tree in self.trees:
                result.append(tree.predict(x.reshape(1, -1))[0])
            # 返回该样本的预测结果,采取方案:多数投票
            results.append(np.argmax(np.bincount(result)))  
        return np.array(results)

if __name__ == "__main__":
    X, y = make_classification(n_samples=200, 
                               n_features=8, 
                               n_informative=4, 
                               random_state=0)

    RF1 = RandomForest(n_estimators=100, max_depth=3)
    RF2 = RF(n_estimators=100, max_depth=3)

    RF1.fit(X, y)
    res1 = RF1.predict(X)

    RF2.fit(X, y)
    res2 = RF2.predict(X)

    print('预测一致的比例', (np.abs(res1 - res2) < 1e-5).mean())
# 打印出:预测一致的比例 0.975

六、GBDT代码部分

6.1 GBDT原理及优缺点

简单说就是,先训练第一个分类器,然后调整数据分布(将分类错误的数据“放大”,将正确的数据“缩小”),在此基础上训练第二个分类器,以此类推。显然Boosting的学习是串行的,学习有先后顺序,并且每轮会根据前一轮学习结果调整数据的重要性。总之,就是一个优化训练数据,解决欠拟合的过程。

  • 优点

    • 预测阶段的计算速度快,树与树之间可并行化计算。
    • 在分布稠密的数据集上,泛化能力和表达能力都很好。
    • 采用决策树作为弱分类器使得GBDT模型具有较好的解释性和鲁棒性, 能够自动发现特征间的高阶关系,并且也不需要对数据进行特殊的预处理如归一化等。
  • 缺点

    • GBDT在高维稀疏的数据集上,表现不如支持向量机或者神经网络。
    • GBDT在处理文本分类特征问题上,相对其他模型的优势不如它在处理 数值特征时明显。
    • 训练过程需要串行训练,只能在决策树内部采用一些局部并行的手段提 高训练速度。

6.2 GBDT的应用栗子

参考:【kaggle】基于xgboost的boston房价预测

6.3 手写GBDT的回归树

from sklearn.tree import DecisionTreeRegressor as DT
from sklearn.datasets import make_classification
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
import numpy as np

class GBDTClassifier:
    def __init__(self, max_depth=4, n_estimator=1000, lr=0.2):
        self.max_depth = max_depth
        self.n_estimator = n_estimator
        self.lr = lr
        self.booster = []

        self.best_round = None

    def record_score(self, y_train, y_val, train_predict, val_predict, i):
        train_predict = np.exp(train_predict) / (1 + np.exp(train_predict))
        val_predict = np.exp(val_predict) / (1 + np.exp(val_predict))
        auc_val = roc_auc_score(y_val, val_predict)
        if (i+1)%10==0:
            auc_train = roc_auc_score(y_train, train_predict)
            print("第%d轮\t训练集: %.4f\t"
                "验证集: %.4f"%(i+1, auc_train, auc_val))
        return auc_val

    def fit(self, X, y):
        X_train, X_val, y_train, y_val = train_test_split(
            X, y, test_size=0.25, random_state=0)
        train_predict, val_predict = 0, 0
        # 按照二分类比例的初始化公式计算
        fit_val = np.log(y_train.mean() / (1 - y_train.mean()))
        next_fit_val = np.full(X_train.shape[0], fit_val)
        last_val_score = - np.infty
        for i in range(self.n_estimator):
            cur_booster = DT(max_depth=self.max_depth)
            cur_booster.fit(X_train, next_fit_val)
            train_predict += cur_booster.predict(X_train) * self.lr
            val_predict += cur_booster.predict(X_val) * self.lr
            next_fit_val = y_train - np.exp(
                train_predict) / (1 + np.exp(train_predict))
            self.booster.append(cur_booster)
            cur_val_score = self.record_score(
                y_train, y_val, train_predict, val_predict, i)
            if cur_val_score < last_val_score:
                self.best_round = i
                print("\n训练结束!最佳轮数为%d"%(i+1))
                break
            last_val_score = cur_val_score

    def predict(self, X):
        cur_predict = 0
        for i in range(self.best_round):
            cur_predict += self.lr * self.booster[i].predict(X)
        return np.exp(cur_predict) / (1 + np.exp(cur_predict))

if __name__ == "__main__":

    X, y = make_classification(
        n_samples=10000, n_features=50, n_informative=20, random_state=1)
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.25, random_state=0)

    model = GBDTClassifier()
    model.fit(X_train, y_train)
    prediction = model.predict(X_test)
    auc = roc_auc_score(y_test, prediction)
    print("\n测试集的AUC为 %.4f"%(auc))

结果为:

10轮	训练集: 0.9269	验证集: 0.891120轮	训练集: 0.9474	验证集: 0.912930轮	训练集: 0.9578	验证集: 0.923840轮	训练集: 0.9645	验证集: 0.932250轮	训练集: 0.9699	验证集: 0.938360轮	训练集: 0.9744	验证集: 0.944370轮	训练集: 0.9776	验证集: 0.948080轮	训练集: 0.9801	验证集: 0.951790轮	训练集: 0.9820	验证集: 0.9539100轮	训练集: 0.9840	验证集: 0.9568110轮	训练集: 0.9854	验证集: 0.9584120轮	训练集: 0.9868	验证集: 0.9598130轮	训练集: 0.9880	验证集: 0.9612

训练结束!最佳轮数为139

测试集的AUC为 0.9649

Reference

[1] 集成学习(Bagging与Boosting)及案列讲解
[2] 传统机器学习算法复习:逻辑回归、因子分解机和梯度提升树
[3] 字典特征提取DictVectorizer(特征工程之特征提取)

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 18:07:49  更:2022-04-18 18:09:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:32:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码