[学习笔记] [机器学习] 7. 集成学习(Bagging、随机森林、Boosting、GBDT)

  1. 视频链接
  2. 数据集下载地址:无需下载

1. 集成学习算法简介

学习目标:

  • 了解什么是集成学习
  • 知道机器学习中的两个核心任务
  • 了解集成学习中的 Boosting 和 Bagging

1.1 什么是集成学习

在这里插入图片描述

集成学习通过建立几个模型来解决单一预测问题。它的工作原理是生成多个分类器(模型),各自独立地学习和作出预测。这些预测最后结合成组合预测,因此优于任何一个单分类的做出预测。

1.2 复习:机器学习的两个核心任务

  • 任务一:如何优化训练数据 -> 主要用于解决欠拟合问题
  • 任务二:如何提升泛化性能 -> 主要用于解决过拟合问题

1.3 集成学习中的 Boosting 和 Bagging

在这里插入图片描述

注意

  • 这里的变强是指模型的拟合能力更加出色(因为一开始模型是欠拟合的)
  • 这里的变壮是指模型变得更加稳健(Robust)(因为一开始模型是过拟合的)

只要单分类器的表现不太差,集成学习的结果总是要好于单分类器的。


小结

  • 什么是集成学习【了解】
    • 通过建立几个模型来解决单一预测问题
  • 机器学习两个核心任务【知道】
    • 解决欠拟合问题
      • 弱弱组合变强 -> Boosting
    • 解决过拟合问题
      • 互相遏制变壮 -> Bagging

注意

  • 这里的变强是指模型的拟合能力更加出色(因为一开始模型是欠拟合的)
  • 这里的变壮是指模型变得更加稳健(Robust)(因为一开始模型是过拟合的)

2. Bagging 和 随机森林

学习目标:

  • 知道 Bagging 集成原理
  • 知道 随机森林 构造过程
  • 知道什么是 包外估计
  • 知道 RandomForestClassifier 的使用
  • 了解 Bagging 集成的优点

2.1 Bagging 集成的原理

Bagging 是一种集成学习方法,它的全称是 Bootstrap aggregating。它的核心思想是通过并行地训练一系列各自独立的同类模型,然后再将各个模型的输出结果按照某种策略进行聚合(例如分类中可采用投票策略,回归中可采用平均策略)。

  • Bootstrap aggregating的中文翻译是“自助聚集算法”,也称为“装袋算法”。

  • Bootstrap:英['bu:tstræp] 美[ˈbutˌstræp]
    v. 独自创立; 靠一己之力做成; 附属于; 与…相联系;
    n. 拔靴带;

  • aggregating:英[ˈæɡrɪɡeɪtɪŋ] 美[ˈæɡrɪɡeɪtɪŋ]
    v. 总计; 合计;

Bagging 算法的实现过程是将训练数据随机抽取形成 k k k 份数据,然后完成学习。随机采样(bootstrap sample)是从 n n n 个数据点中有放回地重复随机抽取一个样本(即同一个样本可被多次抽取),共抽取 n n n 次。创建一个与原数据大小相同得数据集,但有些数据点会缺失(大约 1/3),因为有些会重复

Bagging 适合对偏差低、方差高的模型进行融合。它通过降低方差来降低过拟合。因为 Bagging 是并行的生成一堆决策树,这些决策树可以不做剪枝。它其实是生成了一堆弱学习器,从而实现降低过拟合的效果


Q1:偏差低、方差高的模型指的是什么?
A1:在机器学习中,偏差(bias)和方差(variance)是用来衡量模型的性能的两个重要指标。偏差度量了模型的预测值与真实值之间的差异,即模型的准确性方差度量了模型对于不同训练数据的敏感性,即模型的稳定性

偏差低、方差高的模型指的是一个在训练数据上拟合得很好,但对于不同训练数据的泛化能力较差的模型。这种模型通常过度拟合训练数据,导致在新数据上表现不佳。Bagging算法通过降低方差来降低过拟合,从而提高模型的泛化能力。

Q2:偏差越低越好,方差越低越好,对吗?
A2:理论上,偏差和方差都越低越好。然而,在实际应用中,偏差和方差之间通常存在权衡关系。降低偏差可能会增加方差,反之亦然。因此,我们需要在偏差和方差之间找到一个平衡点,使得模型在训练数据上拟合得较好,同时具有较好的泛化能力。

这种权衡关系被称为偏差-方差权衡(bias-variance tradeoff)。在实际应用中,我们通常会通过调整模型的复杂度、正则化等方法来平衡偏差和方差。

Q3:强学习器是指过拟合吗?弱学习器是欠拟合吗?
A3:强学习器和弱学习器并不直接与过拟合和欠拟合相关。

  • 强学习器指的是一个能够在训练数据上达到较高准确率的模型
  • 弱学习器指的是一个在训练数据上准确率略高于随机猜测的模型

因此,强学习器和弱学习器并不直接与过拟合和欠拟合相关。一个强学习器可能会过拟合,也可能不会;同样,一个弱学习器也可能会欠拟合,也可能不会。


目标:把下面的圈和方块进行分类。

在这里插入图片描述

实现过程:

、采样不同数据集

在这里插入图片描述

、训练分类器

在这里插入图片描述

、平权投票,获取最终结果

在这里插入图片描述

、主要实现过程小结

在这里插入图片描述

2.2 随机森林构造过程

在机器学习中,随机森林是一个包含多个决策树的分类器,并且其输出的类别是由个别树输出的类别的众数而定。

随机森林 = B a g g i n g + 决策树 随机森林 = \mathrm{Bagging} + 决策树 随机森林=Bagging+决策树

在这里插入图片描述

例如,如果你训练了 5 棵树,其中有 4 个树的结果是 True,1 棵树的结果是 False,那么最终投票结果就是 True

随机森林够造过程中的关键步骤( M M M 表示特征数目):

Step.1 一次随机选出一个样本,有放回的抽样,重复 N N N 次(有可能出现重复的样本)。

Step.2 随机选出 m m m 个特征( m ≪ M m \ll M mM),建立决策树。

思考

  1. 为什么要随机抽样训练集?
    • 如果不进行随机抽样,每棵树的训练集都一样,那么最终训练出的树分类结果也是完全一样的。
  2. 为什么要有放回地抽样?
    • 如果不是有放回的抽样,那么每棵树的训练样本都是不同的,都是没有交集的,这样每棵树都是“有偏的”,都是绝对“片面的”(当然这样说可能不对),也就是说每棵树训练出来都是有很大的差异的。而随机森林最后分类取决于多棵树(弱分类器)的投票表决。

2.3 包外估计(Out-of-Bag Estimate)

在随机森林的构造过程中,如果进行有放回的抽样,我们会发现:总是有一部分样本我们选不到的。那么就带来一些问题:

  • 这部分数据占整体数据的比重有多大呢?
  • 这部分数据有什么用呢?

2.3.1 包外估计的定义

在随机森林的 Bagging 过程中,对于每一棵训练出的决策树 g t gt gt,与数据集 D D D 有如下关系:

数据点 决策树 g 1 g_1 g1 决策树 g 2 g_2 g2 决策树 g 3 g_3 g3 决策树 g T g_T gT
( x 1 , y 1 ) (x_1, y_1) (x1,y1) D 1 D_1 D1 * D 3 D_3 D3 D T D_T DT
( x 2 , y 2 ) (x_2, y_2) (x2,y2) * * D 3 D_3 D3 D T D_T DT
( x 3 , y 3 ) (x_3, y_3) (x3,y3) * D 2 D_2 D2 * D T D_T DT
( x N , y N ) (x_N, y_N) (xN,yN) D 1 D_1 D1 D 2 D_2 D2 * *

这张表格展示了随机森林中每一棵决策树 g t g_t gt 与数据集 D D D 的关系。表格中的每一行代表一个数据点 ( x n , y n ) (x_n, y_n) (xn,yn),每一列代表一棵决策树 g t g_t gt。如果一个数据点 ( x n , y n ) (x_n, y_n) (xn,yn) 被用于训练某棵决策树 g t g_t gt,则对应的单元格中会显示该决策树的训练数据集 D t D_t Dt。如果一个数据点 ( x n , y n ) (x_n, y_n) (xn,yn) 没有被用于训练某棵决策树 g t g_t gt,则对应的单元格中会显示一个星号 *,表示这个数据点是包外数据(Out-of-bag,OOB)。

当数据足够多,对于任意一组数据 ( x n , y n ) (x_n, y_n) (xn,yn) 是包外数据的概率为:

( 1 − 1 N ) N = 1 ( N N − 1 ) N = 1 ( 1 + 1 N − 1 ) N ≈ 1 e ≈ 36.8 % (1 - \frac{1}{N})^N = \frac{1}{(\frac{N}{N-1})^N} = \frac{1}{(1+\frac{1}{N-1})^N} \approx \frac{1}{e} \approx 36.8\% (1N1)N=(N1N)N1=(1+N11)N1e136.8%

由于基分类器是构建在训练样本的自助抽样集上的,只有约 63.2% 原样本集出现在中,而剩余的 36.8% 的数据作为包外数据,可以用于基分类器的验证集。

经验证,包外估计是对集成分类器泛化误差的无偏估计。

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

2.3.2 【拓展】如何理解无偏估计?无偏估计有什么用?

2.3.2.1 如何理解无偏估计

无偏估计就是我认为所有样本出现的概率一样。

假如有 N N N 种样本我们认为所有样本出现概率都是 1 / N 1/N 1/N。然后根据这个来计算数学期望。此时的数学期望就是我们平常讲的平均值。

数学期望本质就是平均值

2.3.2.2 无偏估计为何叫做“无偏”?它要“估计”什么?

首先回答第一个问题:它要“估计”什么?

  • 它要估计的是整体的数学期望(平均值)

第二个问题:那为何叫做无偏?有偏是什么?

假设这个是一些样本的集合 X = x 1 , x 2 , x 3 , . . . , x N X = {x_1, x_2, x_3, ..., x_N} X=x1,x2,x3,...,xN。我们根据样本估计整体的数学期望(平均值)。

因为正常求期望是加权和,什么叫加权和? E ( X ) = E ( P i × x i ) E(X)=E(P_i \times x_i) E(X)=E(Pi×xi) 这个就叫加权和。因为每个样本出现概率不一样,概率大的加起来就大,这就产生偏重了(有偏估计)。但是,我们不知道某个样本出现的概率啊。比如你从别人口袋里面随机拿了 3 张钞票。两张是 10 块钱,一张 100 元,然后你想估计下他口袋里的剩下的钱平均下来每张多少钱(估计平均值)。

然后呢?

无偏估计计算数学期望就是认为所有样本出现概率一样大,没有看不起哪个样本。

回到求钱的平均值的问题。无偏估计我们认为每张钞票出现概率都是 1 / 2 1/2 1/2(因为只出现了 10 和 100 这两种情况,所以是 1 / 2 1/2 1/2。如果是出现 1,10,100三种情况,每种情况概率则是 1 / 3 1/3 1/3

哪怕拿到了两张 10 块钱,我还是认为 10 块钱出现的概率和 100 元的概率一样。不偏心。所以无偏估计所估计的别人口袋每张钱的数学期望(平均值)= 10 × 1 / 2 + 100 × 1 / 2 10 \times 1/2 + 100 \times 1/2 10×1/2+100×1/2

有偏估计那就是偏重那些出现次数多的样本,认为样本的概率是不一样的。

比如我出现了两次 10 块钱,那么我认为 10 块钱的概率是 2 / 3 2/3 2/3,100 块钱概率只有 1 / 3 1/3 1/3。有偏所估计的别人口袋每张钱的数学期望(平均值)= 10 × 2 / 3 + 100 × 1 / 3 10 \times 2/3 + 100 \times 1/3 10×2/3+100×1/3

2.3.3 包外估计的用途

  1. 当基学习器是决策树时,可使用包外样本来辅助剪枝,或用于估计决策树中各结点的后验概率以辅助对零训练样本结点的处理。

  2. 当基学习器是神经网络时,可使用包外样本来辅助早期(Early Stop)停止以减小过拟合。

2.4 随机森林 API 介绍

sklearn.ensemble.RandomForestClassifier(n_estimators=100, criterion='gini',
                                        max_depth=None, min_samples_split=2, 
                                        min_samples_leaf=1, min_weight_fraction_leaf=0.0, 
                                        max_features='sqrt',max_leaf_nodes=None, 
                                        min_impurity_decrease=0.0, bootstrap=True, 
                                        oob_score=False, n_jobs=None, random_state=None, 
                                        verbose=0, warm_start=False, class_weight=None, 
                                        ccp_alpha=0.0, max_samples=None)

ensemble:英[ɒnˈsɒmbl] 美[ɑːnˈsɑːmbl]
n.(经常在一起演出的小型)乐团,剧团,舞剧团; 全体,整体; 合奏,合唱; 成套的东西;
adv. 一起,同时;

  • 作用sklearn.ensemble.RandomForestClassifier 是 scikit-learn 库中的一个随机森林分类器。它是一个元估计器,它适用于数据集的各个子样本上的多个决策树分类器,并使用平均值来提高预测准确性并控制过拟合。如果 bootstrap=True(默认),则使用 max_samples 参数控制子样本大小,否则将使用整个数据集构建每棵树。

  • 参数

    • n_estimators:森林中树的数量,默认值为 100。
    • criterion:衡量拆分质量的函数,默认为 “gini”,支持的标准是 “gini” 用于 Gini 不纯度和 “entropy” 用于香农信息增益。
    • max_depth:树的最大深度,如果为 None,则节点将展开,直到所有叶子都是纯净的或直到所有叶子都包含少于 min_samples_split 个样本。
    • bootstrap:是否对样本集进行有放回抽样来构建树,默认值为 True
    • random_state:控制构建树时使用的样本的随机性(如果 bootstrap=True)和在每个节点上寻找最佳分割时要考虑的要素采样(如果 max_features < n_features)。
    • min_samples_split:拆分内部节点所需的最小样本数,默认值为 2。
    • max_features:在寻找最佳拆分时要考虑的特征数量。它可以是整数、浮点数或字符串,其默认值为 “sqrt”。
      • 如果为整数,则在每次拆分时考虑 max_features 个特征。
      • 如果为浮点数,则 max_features 是一个分数,每次拆分时都会考虑 max(1, int(max_features * n_features_in_)) 个特征。
      • 如果为 “auto”,则每次拆分时考虑 sqrt(n_features) 个特征。
      • 如果为 “sqrt”,则每次拆分时考虑 sqrt(n_features) 个特征。
      • 如果为 “log2”,则每次拆分时考虑 log2(n_features) 个特征。
      • 如果为 None,则每次拆分时都会考虑所有的特征。
    • min_samples_leaf:在叶节点处需要的最小样本数。仅在任何深度的拆分点都只会被考虑,如果它在左右分支中都留下至少 min_samples_leaf 个训练样本。这可能会使模型平滑,特别是在回归中。如果为整数,则将 min_samples_leaf 视为最小值。如果为浮点数,则 min_samples_leaf 是一个分数,而 ceil(min_samples_leaf * n_samples) 是每个节点的最小样本数。
    • min_impurity_decrease:一个节点将被拆分的最小不纯度减少量。如果拆分导致的不纯度减少量大于或等于这个值,则该节点将被拆分,否则不拆分。
  • 返回值:返回一个随机森林分类器对象,具有多种方法,如:

    • fit()
    • predict()
    • predict_proba()

上面决策树参数中最重要的包括:

  • 最大特征数 max_features
  • 最大深度 max_depth
  • 内部节点再划分所需最小样本数 min_samples_split
  • 叶子节点最少样本数 min_samples _leaf

2.5 随机森林预测案例流程

:数据读取及其处理

这里仍然使用 Titanic 数据集。

import pandas as pd
import numpy as np
from sklearn.feature_extraction import DictVectorizer
from sklearn.model_selection import train_test_split

# 1. 获取数据
titanic = pd.read_csv("../data/titanic.txt")

## 2.1 确定特征值和目标值
x = titanic[["pclass", "sex", "age"]]
y = titanic["survived"]

## 2.2 缺失值处理
# 缺失值需要处理,将特征中有类别的特征进行字典特征抽取
x.loc[x['age'].isnull(), 'age'] = x['age'].mean()

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

# 3. 特征工程(字典特征抽取)

## 3.1 实例化一个字典转换器类
transfer = DictVectorizer(sparse=False)  # 不用输出稀疏矩阵

## 3.2 将DataFrame转换为字典数据
x_train = x_train.to_dict(orient="records")
x_test = x_test.to_dict(orient="records")

## 3.3 特征转换
x_train = transfer.fit_transform(x_train)
# 注意:在测试数据上,应该使用与训练数据相同的转换方式,因此应该使用 `transform` 方法,
# 而不是 `fit_transform` 方法。`transform` 方法只进行转换,不会改变转换器的拟合结果。
x_test = transfer.transform(x_test)

一、实例化随机森林

## 4.0 导入必要的库
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV


## 4.1 实例化随机森林
rf = RandomForestClassifier()

二、定义超参数的选择列表

## 4.2 定义超参数的选择列表
hyper_params = {
    
    "n_estimators": [120, 200, 300, 500, 800, 1200], "max_depth": [5, 8, 15, 25, 30]}

三、使用 GridSearchCV 进行网格搜索交叉验证

## 4.3 超参数调优
gc = GridSearchCV(estimator=rf, param_grid=hyper_params, cv=2)  # 2-fold

四、模型训练及评估

## 5.1 模型训练
gc.fit(x_train, y_train)

## 5.2 模型评估
print("随机森林预测的准确率为:", gc.score(x_test, y_test))

运行结果:

随机森林预测的准确率为: 0.7713414634146342

之前我们仅使用决策树取得的准确率为:0.7560975609756098

注意

  • 随机森林的建立过程
  • 树的深度、树的个数等需要进行超参数调优

2.5 Bagging 集成的特点以及优缺点

Bagging(Bootstrap aggregating)是一种集成学习算法,它通过并行地训练一系列各自独立的同类模型,然后再将各个模型的输出结果按照某种策略进行聚合(例如分类中可采用投票策略,回归中可采用平均策略)。Bagging 算法可与其他分类、回归算法结合,提高其准确率、稳定性的同时,通过降低结果的方差,避免过拟合的发生。

B a g g i n g + 决策树 / 线性回归 / 逻辑回归 / 深度学习 . . . = B a g g i n g 集成学习方法 \mathrm{Bagging} + 决策树/线性回归/逻辑回归/深度学习... = \mathrm{Bagging}集成学习方法 Bagging+决策树/线性回归/逻辑回归/深度学习...=Bagging集成学习方法

2.5.1 特点

Bagging 算法的特点如下:

  • 能够并行地训练多个模型,因此训练速度快,适合处理大数据
  • 由于 Bagging 算法每次都进行采样来训练模型,因此泛化能力很强,对于降低模型的方差很有作用

2.5.2 优点

Bagging 集成的优点包括:

  1. 并行性好:Bagging 算法能够并行地训练多个模型,因此训练速度快,适合处理大数据。
  2. 泛化能力强:由于 Bagging 算法每次都进行采样来训练模型,因此泛化能力很强,对于降低模型的方差很有作用。
    • 决策树在原有算法上加上 Bagging 提高约 2% 左右的泛化正确率
  3. 避免过拟合:Bagging 算法可与其他分类、回归算法结合,提高其准确率、稳定性的同时,通过降低结果的方差,避免过拟合的发生。

2.5.3 缺点

Bagging 集成的缺点包括:

  1. 模型解释性差:由于 Bagging 集成是通过组合多个模型的预测结果来进行预测的,因此它的模型解释性不如单个模型。
  2. 计算成本高:虽然 Bagging 算法能够并行地训练多个模型,但是它仍然需要训练多个模型,因此计算成本较高。

小结

  • bagging集成过程【知道】
    1. 采样:从所有样本里面采样一部分
    2. 学习:训练弱学习器
    3. 集成使用平权投票
  • 随机森林介绍【知道】
    • 随机森林定义: 随机森林 = B a g g i n g + 决策树 随机森林 = \mathrm{Bagging} + 决策树 随机森林=Bagging+决策树
    • 流程:
      1. 随机选取 m m m 条数据
      2. 随机选取 k k k 个特征
      3. 训练决策树
      4. 重复 1-3
      5. 对上面的若决策树进行平权投票
    • 注意:
      • 随机选取样本,且是有放回的抽取
      • 选取特征时随机选出 m m m 个特征( m ≪ M m \ll M mM
    • 包外估计:
      • 如果进行有放回的对数据集抽样,会发现,总是有一部分样本选不到
      • 因此我们可以使用包外估计进行无偏估计
    • API:
      • sklearn.ensemble.RandomForestClassifier()
  • B a g g i n g + 决策树 / 线性回归 / 逻辑回归 / 深度学习 . . . = B a g g i n g 集成学习方法 \mathrm{Bagging} + 决策树/线性回归/逻辑回归/深度学习... = \mathrm{Bagging}集成学习方法 Bagging+决策树/线性回归/逻辑回归/深度学习...=Bagging集成学习方法【了解】
  • Bagging的特点、优点以及缺点【了解】
    • 特点:
      • 能够并行地训练多个模型,因此训练速度快,适合处理大数据
      • 由于 Bagging 算法每次都进行采样来训练模型,因此泛化能力很强,对于降低模型的方差很有作用
    • 优点:
      1. 并行性好:Bagging 算法能够并行地训练多个模型,因此训练速度快,适合处理大数据。
      2. 泛化能力强:由于 Bagging 算法每次都进行采样来训练模型,因此泛化能力很强,对于降低模型的方差很有作用。
        • 决策树在原有算法上加上 Bagging 提高约 2% 左右的泛化正确率
      3. 避免过拟合:Bagging 算法可与其他分类、回归算法结合,提高其准确率、稳定性的同时,通过降低结果的方差,避免过拟合的发生。
    • 缺点:
      1. 模型解释性差:由于 Bagging 集成是通过组合多个模型的预测结果来进行预测的,因此它的模型解释性不如单个模型。
      2. 计算成本高:虽然 Bagging 算法能够并行地训练多个模型,但是它仍然需要训练多个模型,因此计算成本较高。

3. 随机森林案例 —— OTTO

3.1 otto(Otto Group Product Classification Challenge)案例介绍

奥托集团是世界上最大的电子商务公司之一,在 20 多个国家设有子公司。该公司每天都在世界各地销售数百万种产品,所以对其产品根据性能合理的分类非常重要。

不过,在实际工作中,工作人员发现许多相同的产品得到了不同的分类。本案例要求你对奥拓集团的产品进行正确的分分类。尽可能的提供分类的准确性。

链接:Otto Group Product Classification Challenge

在这里插入图片描述

对于这次比赛,我们提供了一个包含超过200,000种产品的93个特征的数据集。目标是建立一个预测模型,能够区分我们的主要产品类别。

3.2 数据集介绍

本案例中,数据集包含大约 200,000 种产品的 93 个特征。其目的是建立一个能够区分 otto 公司主要产品类别的预测模型。所有产品共被分成九个类别(例如时装,电子产品等)。

在这里插入图片描述

其中:

  • id:产品ID
  • feat_1, feat_2, ..., feat_93:产品的各个特征
  • target:产品被划分的类别

3.3 评分标准

本案例中,最后结果使用多分类对数损失进行评估。具体公式为:

l o g   l o s s = − 1 N ∑ i = 1 N ∑ j = 1 M y i j log ⁡ ( p i j ) \mathrm{log \ loss} = -\frac{1}{N} \sum_{i=1}^N \sum_{j=1}^M y_{ij}\log{(p_{ij})} log loss=N1i=1Nj=1Myijlog(pij)

其中:

  • i i i 表示样本
  • j j j 表示类别
  • p i j p_{ij} pij 代表第 i i i 个样本属于类别 j j j 的概率
  • 如果第 i i i 个样本真的属于类别 j j j,则 y i j y_{ij} yij 等于1,否则为0

根据上公式,假如模型将所有的测试样本都正确分类,即所有 p i j p_{ij} pij 都是1,那每个 log ⁡ ( p i j ) \log{(p_{ij})} log(pij) 都是0,最终的 l o g   l o s s ( p i j ) \mathrm{log \ loss}{(p_{ij})} log loss(pij) 也是0。

假如第 1 个样本本来是属于 1 类别的,但模型给它的类别概率 p i j = 0.1 p_{ij} = 0.1 pij=0.1,那 l o g   l o s s \mathrm{log \ loss} log loss 就会累加上 log ⁡ ( 0.1 ) \log(0.1) log(0.1) 这一项。我们知道这一项是负数,而且 p i j p_{ij} pij 越小,负得越多,如果 p i j = 0 p_{ij} = 0 pij=0,将是无穷。这会导致这种情况:模型分错了一个, l o g   l o s s \mathrm{log \ loss} log loss 就是无穷。这当然不合理,为了避免这一情况,我们对非常小的值做如下处理:

max ⁡ ( min ⁡ ( p , 1 − 1 0 − 15 ) , 1 0 − 15 ) \max(\min(p, 1-10^{-15}), 10^{-15}) max(min(p,11015),1015)

也就是说,最小不会小于 1 0 − 15 10^{-15} 1015

3.4 实现过程

3.4.1 流程分析

  1. 获取数据
  2. 数据基本处理
    1. 数据量比较大,尝试是否可以进行数据分割
    2. 转换目标值表示方式
  3. 模型训练
  4. 模型评估
  5. 模型调优

3.4.2 代码实现

导入库

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

一、读取数据

data = pd.read_csv("./data/otto-group-product-classification-challenge/train.csv")

图像可视化,查看数据分布

# 图像可视化,查看数据分布
import seaborn as sns


fig = plt.figure(dpi=80)
sns.countplot(x=data.target)
plt.show()

在这里插入图片描述

由上图可以看出,该数据不同类别的样本数量不均衡,所以需要后期处理。

二、数据基本处理

因为数据已经脱敏,不需要再进行特殊处理。

二·一 数据量比较大,尝试是否可以进行数据分割

new_data = data[:10000]
new_data.shape

fig = plt.figure(dpi=80)
sns.countplot(x=new_data.target)
plt.show()

在这里插入图片描述

说明使用上述的方式截取数据是不可行的,需要使用 随机欠采样 截取得到部分数据集。

# 随机欠采样获取部分数据集
## 首先需要确定特征值和目标值

y = data["target"]  # 目标值
x = data.drop(["id", "target"], axis=1)  # 特征值
y.head()
0    Class_1
1    Class_1
2    Class_1
3    Class_1
4    Class_1
Name: target, dtype: object
x.head()

在这里插入图片描述

## 欠采样获取数据
from imblearn.under_sampling import RandomUnderSampler


rus = RandomUnderSampler(random_state=0)
x_resampled, y_resampled = rus.fit_resample(x, y)
fig = plt.figure(dpi=80)
sns.countplot(x=y_resampled)
plt.show()

在这里插入图片描述

所有类别都有了,且样本不均衡问题也被解决了。

二·二 转换目标值表示方式

此时的目标值是Class_*,这样是不行的,需要将目标值转换为数字。

# 把目标值转换为数字
from sklearn.preprocessing import LabelEncoder

le = LabelEncoder()
y_resampled = le.fit_transform(y_resampled)

y_resampled
array([0, 0, 0, ..., 8, 8, 8])

分割数据:分为训练集和测试集

# 分割数据
from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x_resampled, y_resampled, test_size=0.2)

x_train.shape, x_test.shape, y_train.shape, y_test.shape  
# ((13888, 93), (3473, 93), (13888,), (3473,))

三、模型训练

三·一 基本模型训练

## 基本模型训练
from sklearn.ensemble import RandomForestClassifier

rfc = RandomForestClassifier(oob_score=True)  # 使用袋外估计
rfc.fit(x_train, y_train)

四、模型评估

y_pred = rfc.predict(x_test)
print("模型预测结果为:\r\n", y_pred)

score = rfc.score(x_test, y_test)
print(f"测试集上,模型准确率为:{
      
      score * 100:.2f}%")
print(f"模型袋外估计准确率为:{
      
      rfc.oob_score_ * 100:.2f}%")

# 绘图展示模型预测结果
fig = plt.figure(dpi=80)
sns.countplot(x=y_pred)
plt.show()

在这里插入图片描述

虽然我们进行了模型准确率以及袋外估计,但案例要求我们使用 log loss 进行评估,所以我们还需要使用 log loss 对模型性能进行评估。

# log loss模型评估
from sklearn.metrics import log_loss

log_loss(y_test, y_pred, eps=1e-15, normalize=True)

报错了:

ValueError: y_true and y_pred contain different number of classes 9, 2. Please provide the true labels explicitly through the labels argument. Classes found in y_true: [0 1 2 3 4 5 6 7 8]
y_test, y_pred
(array([3, 0, 8, ..., 7, 1, 3]), array([3, 7, 8, ..., 7, 1, 3]))

上面报错的原因是:log loss 在使用的时候要求输出用 one-hot 表示。

from sklearn.preprocessing import OneHotEncoder


ohe = OneHotEncoder(sparse_output=False)
y_test = ohe.fit_transform(y_test.reshape(-1, 1))
y_pred = ohe.fit_transform(y_pred.reshape(-1, 1))
y_test
array([[1., 0.],
       [1., 0.],
       [1., 0.],
       ...,
       [1., 0.],
       [1., 0.],
       [1., 0.]])
y_pred
array([[1., 0.],
       [1., 0.],
       [1., 0.],
       ...,
       [1., 0.],
       [1., 0.],
       [1., 0.]])

之后再进行log loss的模型评估:

# log loss模型评估
from sklearn.metrics import log_loss

log_loss_value = log_loss(y_test, y_pred, eps=1e-15, normalize=True)
print(f"Log Loss为:{
      
      log_loss_value:.4f}")
Log Loss为:7.7372

改变预测值的输出模式,让输出结果为百分占比,从而降低Log Loss值

# 改变预测值的输出模式,让输出结果为百分占比,从而降低Log Loss值
y_pred_proba = rfc.predict_proba(x_test)
y_pred_proba
array([[0.  , 0.27, 0.08, ..., 0.  , 0.  , 0.01],
       [0.26, 0.01, 0.  , ..., 0.11, 0.48, 0.07],
       [0.23, 0.04, 0.03, ..., 0.17, 0.16, 0.3 ],
       ...,
       [0.09, 0.03, 0.04, ..., 0.03, 0.66, 0.02],
       [0.  , 0.57, 0.24, ..., 0.05, 0.01, 0.01],
       [0.05, 0.1 , 0.15, ..., 0.08, 0.07, 0.04]])
# 再进行Log Loss的计算
log_loss_value = log_loss(y_test, y_pred_proba, eps=1e-15, normalize=True)
print(f"Log Loss为:{
      
      log_loss_value:.4f}")
Log Loss为:0.7355

五、模型调优

我们可以对一些超参数调优:

  • n_estimators
  • max_feature
  • max_depth
  • min_samples_leaf

五·一 确定最优的n_estimators

# 5. 模型调优
## 5.1 确定最优的n_estimators
### 5.1.1 确定n_estimators的取值范围
tuned_parameters = range(10, 200, 10)

### 5.1.2 创建添加accuracy的一个numpy对象
accuracy_t = np.zeros(len(tuned_parameters))

### 5.1.3 创建添加error的一个numpy对象
error_t = np.zeros(len(tuned_parameters))

## 5.2 调优过程实现
for j, one_parameter in enumerate(tuned_parameters):
    rfc = RandomForestClassifier(n_estimators=one_parameter, max_depth=10, 
                                 max_features=10, min_samples_leaf=10,
                                 oob_score=True, random_state=0, n_jobs=-1)
    rfc.fit(x_train, y_train)
    
    # 输出accuracy
    accuracy_t[j] = rfc.oob_score_
    
    # 输出Log Loss
    y_proba = rfc.predict_proba(x_test)
    error_t[j] = log_loss(y_test, y_proba, eps=1e-15, normalize=True)
[1.12335669 1.12266148 1.1233354  1.11860337 1.11340172 1.11250767
 1.11215495 1.11258237 1.11293157 1.10973597 1.10747978 1.10730151
 1.10738276 1.10711527 1.10663725 1.10629273 1.1068497  1.10701413
 1.10695667]
# 优化结果过程可视化
fig, axes = plt.subplots(1, 2, figsize=(16, 4), dpi=100)

axes[0].plot(tuned_parameters, error_t)
axes[1].plot(tuned_parameters, accuracy_t)

axes[0].set_xlabel("n_estimators")
axes[0].set_ylabel("error")

axes[1].set_xlabel("n_estimators")
axes[1].set_ylabel("acc")

axes[0].grid(True)
axes[1].grid(True)

plt.show()

在这里插入图片描述

经过图像展示,最后确定 n_estimators=175 的时候,模型表现效果不错。

五·二 确定最优的max_feature

# 5. 模型调优
## 5.2 确定最优的max_feature
### 5.2.1 确定max_feature的取值范围
tuned_parameters = range(5, 40, 5)

### 5.2.2 创建添加accuracy的一个numpy对象
accuracy_t = np.zeros(len(tuned_parameters))

### 5.2.3 创建添加error的一个numpy对象
error_t = np.zeros(len(tuned_parameters))

## 5.2 调优过程实现
for j, one_parameter in enumerate(tuned_parameters):
    rfc = RandomForestClassifier(n_estimators=175, max_depth=10, 
                                 max_features=one_parameter, min_samples_leaf=10,
                                 oob_score=True, random_state=0, n_jobs=-1)
    rfc.fit(x_train, y_train)
    
    # 输出accuracy
    accuracy_t[j] = rfc.oob_score_
    
    # 输出Log Loss
    y_proba = rfc.predict_proba(x_test)
    error_t[j] = log_loss(y_test, y_proba, eps=1e-15, normalize=True)
    
print(f"最小的损失为:{
      
      min(error_t)},位置为:{
      
      np.argmin(error_t)}")
# 最小的损失为:1.0432210801563477,位置为:5

# 优化结果过程可视化
fig, axes = plt.subplots(1, 2, figsize=(20, 4), dpi=100)

axes[0].plot(tuned_parameters, error_t)
axes[1].plot(tuned_parameters, accuracy_t)

axes[0].set_xlabel("max_features")
axes[0].set_ylabel("error")

axes[1].set_xlabel("max_features")
axes[1].set_ylabel("acc")

axes[0].grid(True)
axes[1].grid(True)

plt.show()

在这里插入图片描述

经过图像展示,最后确定 max_feature=15 的时候,模型表现效果不错。

五·三 确定最优的max_depth

# 5. 模型调优
## 5.3 确定最优的max_depth
### 5.3.1 确定max_depth的取值范围
tuned_parameters = range(10, 100, 10)

### 5.3.2 创建添加accuracy的一个numpy对象
accuracy_t = np.zeros(len(tuned_parameters))

### 5.3.3 创建添加error的一个numpy对象
error_t = np.zeros(len(tuned_parameters))

## 5.2 调优过程实现
for j, one_parameter in enumerate(tuned_parameters):
    rfc = RandomForestClassifier(n_estimators=175, max_depth=one_parameter, 
                                 max_features=15, min_samples_leaf=10,
                                 oob_score=True, random_state=0, n_jobs=-1)
    rfc.fit(x_train, y_train)
    
    # 输出accuracy
    accuracy_t[j] = rfc.oob_score_
    
    # 输出Log Loss
    y_proba = rfc.predict_proba(x_test)
    error_t[j] = log_loss(y_test, y_proba, eps=1e-15, normalize=True)
    
print(f"最小的损失为:{
      
      min(error_t)},位置为:{
      
      np.argmin(error_t)}")
print(f"最高的准确率为:{
      
      max(accuracy_t)},位置为:{
      
      np.argmax(accuracy_t)}")
# 最小的损失为:0.8220898016223404,位置为:2
# 最高的准确率为:0.7469758064516129,位置为:4

# 优化结果过程可视化
fig, axes = plt.subplots(1, 2, figsize=(20, 4), dpi=100)

axes[0].plot(tuned_parameters, error_t)
axes[1].plot(tuned_parameters, accuracy_t)

axes[0].set_xlabel("max_depth")
axes[0].set_ylabel("error")

axes[1].set_xlabel("max_depth")
axes[1].set_ylabel("acc")

axes[0].grid(True)
axes[1].grid(True)

plt.show()

在这里插入图片描述

经过图像展示,最后确定 max_depth=30 的时候,模型表现效果不错。


五·四 确定最优的min_sample_leaf

# 5. 模型调优
## 5.4 确定最优的min_samples_leaf
### 5.4.1 确定min_samples_leaf的取值范围
tuned_parameters = range(1, 10, 2)

### 5.4.2 创建添加accuracy的一个numpy对象
accuracy_t = np.zeros(len(tuned_parameters))

### 5.4.3 创建添加error的一个numpy对象
error_t = np.zeros(len(tuned_parameters))

## 5.2 调优过程实现
for j, one_parameter in enumerate(tuned_parameters):
    rfc = RandomForestClassifier(n_estimators=175, max_depth=30, 
                                 max_features=15, min_samples_leaf=one_parameter,
                                 oob_score=True, random_state=0, n_jobs=-1)
    rfc.fit(x_train, y_train)
    
    # 输出accuracy
    accuracy_t[j] = rfc.oob_score_
    
    # 输出Log Loss
    y_proba = rfc.predict_proba(x_test)
    error_t[j] = log_loss(y_test, y_proba, eps=1e-15, normalize=True)
    
print(f"最小的损失为:{
      
      min(error_t)},位置为:{
      
      np.argmin(error_t)}")
print(f"最高的准确率为:{
      
      max(accuracy_t)},位置为:{
      
      np.argmax(accuracy_t)}")
# 最小的损失为:0.705443621042576,位置为:0
# 最高的准确率为:0.7696572580645161,位置为:0

# 优化结果过程可视化
fig, axes = plt.subplots(1, 2, figsize=(20, 4), dpi=100)

axes[0].plot(tuned_parameters, error_t)
axes[1].plot(tuned_parameters, accuracy_t)

axes[0].set_xlabel("min_samples_leaf")
axes[0].set_ylabel("error")

axes[1].set_xlabel("min_samples_leaf")
axes[1].set_ylabel("acc")

axes[0].grid(True)
axes[1].grid(True)

plt.show()

在这里插入图片描述

经过图像展示,最后确定 min_sample_leaf=1 的时候,模型表现效果不错。


因此我们可以确定最优模型:

  • n_estimators=175
  • max_feature=15
  • max_depth=30
  • min_samples_leaf=1
rfc_best = RandomForestClassifier(n_estimators=175, max_depth=30, 
                                  max_features=15, min_samples_leaf=1, 
                                  oob_score=True, random_state=0, n_jobs=-1)
rfc_best.fit(x_train, y_train)
y_pred = rfc_best.predict(x_test)
print("模型预测结果为:\r\n", y_pred)
print(f"模型袋外估计准确率为:{
      
      rfc_best.oob_score_ * 100:.2f}%")

y_pred_proba = rfc_best.predict_proba(x_test)
log_loss_value = log_loss(y_test, y_pred_proba, eps=1e-15, normalize=True)
print(f"Log Loss为:{
      
      log_loss_value:.4f}")
模型预测结果为:
 [5 4 1 ... 8 8 1]
模型袋外估计准确率为:77.20%
Log Loss为:0.7201

相比默认参数的模型,此时模型的性能均有提升。

3.5 生成提交数据

# 读取测试集
test_data = pd.read_csv("./data/otto-group-product-classification-challenge/test.csv")

# 丢弃id列
test_data_drop_id = test_data.drop(["id"], axis=1)
test_data_drop_id.head()

# 得到预测结果(概率的形式)
y_pred_test = rfc_best.predict_proba(test_data_drop_id)

# 将ndarray转换为df
res_data = pd.DataFrame(y_pred_test, columns=["Class_" + str(i) for i in range(1, 10)])

# 添加id
res_data.insert(loc=0, column="id", value=test_data.id)

# 保存为本地文件
res_data.to_csv("./data/otto-group-product-classification-challenge/submission.csv", index=False)

4. Boosting介绍

学习目标:

  • 知道 Boosting 集成原理和实现过程
  • 知道 Bagging 和 Boosting 集成的区别
  • 知道 AdaBoost 集成原理

4.1 什么是 Boosting

Boosting 是一种机器学习算法,它可以用来减小监督式学习中偏差。Boosting 是一种串行的工作机制,即个体学习器的训练存在依赖关系,必须一步一步序列化进行。它通过组合多个弱学习器形成一个强学习器,提高模型的整体预测精度。Boosting 总是更加关注被错误分类的弱规则。

在这里插入图片描述

模型随着学习的积累从弱到强。

简而言之:每新加入一个弱学习器,整体能力就会得到提升。

代表算法:

  • Adaboost
  • GBDT
  • XGBoost
  • LightGBM

4.2 实现过程

4.2.1 训练第一个学习器

在这里插入图片描述

4.2.2 调整数据分布

在这里插入图片描述

4.2.3 训练第二个学习器

在这里插入图片描述

4.2.4 再次调整数据分布

在这里插入图片描述

4.2.5 以此训练学习器,调整数据分布

在这里插入图片描述

4.2.6 整体过程实现

在这里插入图片描述

4.3 Bagging 和 Boosting 集成的区别

Boosting 和 Bagging 都是机器学习模型融合中的两种策略。它们都可以将弱分类器融合之后形成一个强分类器,而且融合之后的效果会比最好的弱分类器更好。但是它们之间也有一些区别:

  • 区别一:数据方面
    • Bagging:对数据进行采样训练(有放回的随机抽样)
    • Boosting:根据前一轮学习结果调整数据的重要性(每一轮训练集不变,只是训练集中的每个样例在分类器中的权重发生变化,而权重根据上一轮分类结果进行调整)
  • 区别二:投票方面
    • Bagging:所有学习器平权投票(每个样例的权重相等)
    • Boosting:对学习器进行加权投票(根据错误率不断调整样例的权值,错误率越大则权重越大)
  • 区别三:学习顺序
    • Bagging 的学习是并行的,每个学习器没有依赖关系(可以并行生成各个基模型)
    • Boosting 学习是串行,学习有先后顺序(理论上只能顺序生产,因为每一个模型都需要上一个模型的结果)
  • 区别四:主要作用
    • Bagging 主要用于提高泛化性能(解决过拟合,也可以说降低方差 Variance)
    • Boosting 主要用于提高训练精度(解决欠拟合,也可以说降低偏差 Bias)

在这里插入图片描述

4.4 AdaBoost 介绍

4.4.1 构造过程细节

步骤一:初始化训练数据权重相等,训练第一个学习器。

假设每个训练样本在基分类器的学习中作用相同。这一假设可以保证第一步能够在原始数据上学习基本分类器 H 1 ( x ) H_1(x) H1(x)

步骤二:AdaBoost 反复学习基本分类器,在每一轮 m = 1 , 2 , . . . , M m = 1,2,..., M m=1,2,...,M 顺次的执行下列操作:

  1. 在权值分布为 D t D_t Dt 的训练数据上,确定基分类器
  2. 计算该学习器在训练数据中的错误率: ϵ t = P ( h t ( x t ) ≠ y t ) \epsilon_t= P(h_t(x_t) \ne y_t) ϵt=P(ht(xt)=yt)
  3. 计算该学习器的投票权重: α t = 1 2 ln ⁡ ( 1 − ϵ t ϵ t ) \alpha_t = \frac{1}{2}\ln{(\frac{1-\epsilon_t}{\epsilon_t})} αt=21ln(ϵt1ϵt)
  4. 根据投票权重,对训练数据重新赋权: D t + 1 ( x ) = D t ( x ) Z t × { e − α t , 预测值 = 真实值 e α t , 预测值 ≠ 真实值 D_{t+1}(x) = \frac{D_t(x)}{Z_t} \times \begin{cases} e^{-\alpha_t}, & 预测值=真实值\\ e^{\alpha_t}, & 预测值 \ne 真实值 \end{cases} Dt+1(x)=ZtDt(x)×{ eαt,eαt,预测值=真实值预测值=真实值

将下一轮学习器的注意力集中在错误数据上

  1. 重复执行①到④步, m m m

步骤三:对 m m m 个学习器进行加权投票 H ( x ) = s i g n ( ∑ i = 1 m α i h i ( x ) ) H(x) = \mathrm{sign}(\sum_i=1^m\alpha_i h_i(x)) H(x)=sign(i=1mαihi(x))

4.4.2 关键点剖析

  • 如何确认投票权重?
  • 如何调整数据分布?

在这里插入图片描述

4.4.3案例

给定下面这张训练数据表所示的数据,假设弱分类器由 x v xv xv 产生,其阈值 v v v 使该分类器在训练数据集上的分类误差率最低,试用 AdaBoost 算法学习一个强分类器。

序号 1 2 3 4 5 6 7 8 9 10
x 0 1 2 3 4 5 6 7 8 9
y 1 1 1 -1 -1 -1 1 1 1 -1

问题解答

步骤一:初始化训练数据权重相等,训练第一个学习器: D 1 = ( w 11 , w 12 , . . . , w 110 ) D_1 = (w_{11}, w_{12}, ..., w_{110}) D1=(w11,w12,...,w110) w 1 i = 0.1 , i = 1 , 2 , . . . , 10 w_{1i} = 0.1, i=1,2,...,10 w1i=0.1,i=1,2,...,10

步骤二:AdaBoost 反复学习基本分类器,在每一轮 m = 1 , 2 , . . . , M m = 1,2, ..., M m=1,2,...,M 顺次的执行下列操作:

  • m = 1 m=1 m=1 的时候:

    1. 在权值分布为 D 1 D_1 D1 的训练数据上,阈值 v v v 取 2.5 时分类误差率最低,故基本分类器为: h 1 ( x ) = { 1 , x < 2.5 − 1 , x > 2.5 h_1(x)=\begin{cases} 1, & x < 2.5 \\ -1, & x > 2.5 \end{cases} h1(x)={ 1,1,x<2.5x>2.5

    6,7,8被分错

    1. 计算该学习器在训练数据中的错误率: ϵ 1 = P ( h 1 ( x 1 ) ≠ y 1 ) = 0.3 \epsilon_1 = P(h_1(x_1) \ne y_1) = 0.3 ϵ1=P(h1(x1)=y1)=0.3
    2. 计算该学习器的投票权重: α 1 = 1 2 ln ⁡ 1 − ϵ 1 ϵ 1 = 0.4236 \alpha_1 = \frac{1}{2}\ln{\frac{1-\epsilon_1}{\epsilon_1}} = 0.4236 α1=21lnϵ11ϵ1=0.4236
    3. 根据投票权重,对训练数据重新赋权: D 2 = ( w 21 , w 22 , . . . , w 210 ) D_2 = (w_{21}, w_{22}, ..., w_{210}) D2=(w21,w22,...,w210)
      经计算得, D 2 D_2 D2 的值为: D 2 = ( 0.07143 , 0.07143 , 0.07143 , 0.07143 , 0.07143 , 0.07143 , 0.16667 , 0.16667 , 0.16667 , 0.07143 ) D_2 = (0.07143,0.07143,0.07143,0.07143,0.07143,0.07143,0.16667,0.16667,0.16667,0.07143) D2=(0.07143,0.07143,0.07143,0.07143,0.07143,0.07143,0.16667,0.16667,0.16667,0.07143)
      H 1 ( x ) = s i g n [ 0.4236 h 1 ( x ) ] H_1(x) = \mathrm{sign}[0.4236h_1(x)] H1(x)=sign[0.4236h1(x)]
      分类器 H 1 ( x ) H_1(x) H1(x) 在训练数据集上有 3 个误分类点。
  • m = 2 m=2 m=2 的时候:

    1. 在权值分布为 D 2 D_2 D2 的训练数据上,阈值 v v v 取 8.5 时分类误差率最低,故基本分类器为: h 2 ( x ) = { 1 , x < 8.5 − 1 , x > 8.5 h_2(x)=\begin{cases} 1, & x < 8.5 \\ -1, & x > 8.5 \end{cases} h2(x)={ 1,1,x<8.5x>8.5

    3,4,5被分错

    1. 计算该学习器在训练数据中的错误率: ϵ 2 = P ( h 2 ( x 2 ) ≠ y 2 ) = 0.2143 \epsilon_2 = P(h_2(x_2) \ne y_2) = 0.2143 ϵ2=P(h2(x2)=y2)=0.2143
    2. 计算该学习器的投票权重: α 2 = 1 2 ln ⁡ 1 − ϵ 2 ϵ 2 = 0.6496 \alpha_2 = \frac{1}{2}\ln{\frac{1-\epsilon_2}{\epsilon_2}} = 0.6496 α2=21lnϵ21ϵ2=0.6496
    3. 根据投票权重,对训练数据重新赋权:经计算得, D 3 D_3 D3 的值为: D 3 = ( 0.0455 , 0.0455 , 0.0455 , 0.1667 , 0.1667 , 0.1667 , 0.1060 , 0.1060 , 0.1060 , 0.0455 ) D_3 = (0.0455,0.0455,0.0455,0.1667,0.1667,0.1667,0.1060,0.1060,0.1060,0.0455) D3=(0.0455,0.0455,0.0455,0.1667,0.1667,0.1667,0.1060,0.1060,0.1060,0.0455)
      H 2 ( x ) = s i g n [ 0.4236 h 1 ( x ) + 0.6496 h 2 ( x ) ] H_2(x) = \mathrm{sign}[0.4236h_1(x) + 0.6496h_2(x)] H2(x)=sign[0.4236h1(x)+0.6496h2(x)]
      分类器 H 2 ( x ) H_2(x) H2(x) 在训练数据集上有 3 个误分类点。
  • m = 3 m=3 m=3 的时候:

    1. 在权值分布为 D 3 D_3 D3 的训练数据上,阈值 v v v 取 5.5 时分类误差率最低,故基本分类器为: h 2 ( x ) = { 1 , x < 5.5 − 1 , x > 5.5 h_2(x)=\begin{cases} 1, & x < 5.5 \\ -1, & x > 5.5 \end{cases} h2(x)={ 1,1,x<5.5x>5.5

    3,4,5被分错

    1. 计算该学习器在训练数据中的错误率: ϵ 3 = P ( h 3 ( x 3 ) ≠ y 3 ) = 0.1820 \epsilon_3 = P(h_3(x_3) \ne y_3) = 0.1820 ϵ3=P(h3(x3)=y3)=0.1820
    2. 计算该学习器的投票权重: α 3 = 1 2 ln ⁡ 1 − ϵ 3 ϵ 3 = 0.7514 \alpha_3 = \frac{1}{2}\ln{\frac{1-\epsilon_3}{\epsilon_3}} = 0.7514 α3=21lnϵ31ϵ3=0.7514
    3. 根据投票权重,对训练数据重新赋权:经计算得, D 4 D_4 D4 的值为: D 4 = ( 0.125 , 0.125 , 0.125 , 0.102 , 0.102 , 0.102 , 0.065 , 0.065 , 0.065 , 0.125 ) D_4 = (0.125,0.125,0.125,0.102,0.102,0.102,0.065,0.065,0.065,0.125) D4=(0.125,0.125,0.125,0.102,0.102,0.102,0.065,0.065,0.065,0.125)
      H 3 ( x ) = s i g n [ 0.4236 h 1 ( x ) + 0.6496 h 2 ( x ) + 0.7514 h 3 ( x ) ] H_3(x) = \mathrm{sign}[0.4236h_1(x) + 0.6496h_2(x) + 0.7514h_3(x)] H3(x)=sign[0.4236h1(x)+0.6496h2(x)+0.7514h3(x)]
      分类器 H 3 ( x ) H_3(x) H3(x) 在训练数据集上有 0 个误分类点。

步骤三:对 m m m 个学习器进行加权投票,获得最终分类器

H 3 ( x ) = s i g n [ 0.4236 h 1 ( x ) + 0.6496 h 2 ( x ) + 0.7514 h 3 ( x ) ] H_3(x) = \mathrm{sign}[0.4236h_1(x) + 0.6496h_2(x) + 0.7514h_3(x)] H3(x)=sign[0.4236h1(x)+0.6496h2(x)+0.7514h3(x)]

4.4.4 API介绍

from sklearn.ensemble import AdaBoostClassifier

API 链接:https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.AdaBoostClassifier.html#sklearn.ensemble.AdaBoostClassifier


小结

  • 什么是Boosting【知道】
    • 随着学习的积累从弱到强
    • 代表算法:Adaboost、GBDT、XGBoost、LightGBM
  • Bagging 和 Boosting 的区别【知道】
    • 区别一:数据方面
      • Bagging:对数据进行采样训练
      • Boosting:根据前一轮学习结果调整数据的重要性
    • 区别二:投票方面
      • Bagging:所有学习器平权投票
      • Boosting:对学习器进行加权投票
    • 区别三:学习顺序
      • Bagging的学习是并行的,每个学习器没有依赖关系
      • Boosting学习是串行,学习有先后顺序。
    • 区别四:主要作用
      • Bagging主要用于提高泛化性能(解决过拟合,也可以说降低方差)
      • Boosting主要用于提高训练精度(解决欠拟合,也可以说降低偏差)
  • AdaBoost构造过程【知道】
    • 步骤一:初始化训练数据权重相等,训练第一个学习器
    • 步骤二:AdaBoost反复学习基本分类器
    • 步骤三:对 m m m 个学习器进行加权投票

5. GBDT介绍

学习目标:

  • 知道GBDT的算法原理

GBDT的全称是Gradient Boosting Decision Tree,梯度提升树。在传统机器学习算法中,GBDT算的上是TOP-3的算法。想要理解GBDT的真正意义,那就必须理解GBDT中的Gradient BoostingDecision Tree分别是什么。

5.1 Decision Tree:CART回归树

首先,GBDT使用的决策树是CART回归树,无论是处理回归问题还是二分类以及多分类,GBDT使用的决策树通通都是都是CART回归树。

Q:为什么不用CART分类树呢?
A:因为GBDT每次迭代要拟合的是梯度值,是连续值所以要用回归树。

对于回归树算法来说最重要的是寻找最佳的划分点,那么回归树中的可划分点包含了所有特征的所有可取的值。

在分类树中最佳划分点的判别标准是熵或者基尼系数,都是用纯度来衡量的,但是在回归树中的样本标签是连续数值,所以再使用熵之类的指标不再合适,取而代之的是平方误差,它能很好的评判拟合程度

5.1.1 回归树生成算法(复习)

  • 输入:训练数据集 D D D
  • 输出:回归树 f ( x ) f(x) f(x)
  • 在训练数据集所在的输入空间中,递归的将每个区域划分为两个子区域并决定每个子区域上的输出值,构建二叉决策树:
    1. 选择最优切分特征 j j j 与切分点 s s s,求解 min ⁡ j , s [ min ⁡ c 1 ∑ x i ∈ R 1 ( j , s ) ( y i − c 1 ) 2 + min ⁡ c 2 ∑ x i ∈ R 2 ( j , s ) ( y i − c 2 ) 2 ] \min_{j, s}\left[ \min_{c_1} \sum_{x_i \in R_1(j, s)}(y_i - c_1)^2 + \min_{c_2} \sum_{x_i \in R_2(j, s)}(y_i - c_2)^2 \right] j,smin c1minxiR1(j,s)(yic1)2+c2minxiR2(j,s)(yic2)2 遍历特征 j j j,对固定的切分特征 j j j 扫描切分点 s s s,选择使得上式达到最小值的对 ( j , s ) (j,s) (j,s)
    2. 用选定的对 ( j , s ) (j,s) (j,s) 划分区域并决定相应的输出值: R 1 ( j , s ) = x ∣ x ( j ) ≤ s , R 2 ( j , s ) = x ∣ x ( j ) > s R_1(j, s) = x|x^{(j)} \le s, R_2(j, s) = x|x^{(j)} > s R1(j,s)=xx(j)s,R2(j,s)=xx(j)>s c ^ m = 1 N ∑ x 1 ∈ R m ( j , s ) y i , x ∈ R m , m = 1 , 2 \hat{c}_m = \frac{1}{N}\sum_{x_1\in R_m(j, s)}y_i, x\in R_m, m=1, 2 c^m=N1x1Rm(j,s)yi,xRm,m=1,2
    3. 继续对两个子区域调用步骤①和②,直至满足停止条件。
    4. 将输入空间划分为 M M M 个区域 R 1 , R 2 , . . . , R M R_1, R_2,...,R_M R1,R2,...,RM,生成决策树: f ( x ) = ∑ m = 1 M c ^ m I ( x ∈ R m ) f(x) = \sum_{m=1}^M\hat{c}_m I (x\in R_m) f(x)=m=1Mc^mI(xRm)

5.1.2 Gradient Boosting:拟合负梯度

梯度提升树(Gradient Boosting)是提升树(Boosting Tree)的一种改进算法,所以在讲梯度提升树之前先来说一下提升树。

先来个通俗理解:假如有个人30岁,我们首先用20岁去拟合,发现损失有10岁,这时我们用6岁去拟合剩下的损失,发现差距还有4岁,第三轮我们用3岁拟合剩下的差距,差距就只有一岁了。如果我们的迭代轮数还没有完,可以继续迭代下面,每一轮迭代,拟合的岁数误差都会减小。最后将每次拟合的岁数加起来便是模型输出的结果。

提升树算法

  1. 初始化 f 0 ( x ) = 0 f_0(x)=0 f0(x)=0
  2. m = 1 , 2 , . . . M m=1,2,...M m=1,2,...M
    1. 计算残差 r m i = y i − f m − 1 ( x ) , i = 1 , 2 , . . . , N r_{mi}= y_i - f_{m - 1}(x), i = 1,2,...,N rmi=yifm1(x),i=1,2,...,N
    2. 拟合残差 r m i r_{mi} rmi 学习一个回归树,得到 h m ( x ) h_m(x) hm(x)
    3. 更新 f m ( x ) = f m − 1 + h m ( x ) f_m(x) = f_{m-1} + h_m(x) fm(x)=fm1+hm(x)
  3. 得到回归问题提升树 f M ( x ) = ∑ m = 1 M h m ( x ) f_M(x) = \sum_{m=1}^Mh_m(x) fM(x)=m=1Mhm(x)

Q:上面伪代码中的残差是什么?
A:在提升树算法中:

  • 假设我们前一轮迭代得到的强学习器是 f t − 1 ( x ) f_{t-1}(x) ft1(x)

  • 损失函数是 L ( y , f t − 1 ( x ) ) L(y,f_{t-1}(x)) L(y,ft1(x))

  • 我们本轮迭代的目标是找到一个弱学习器 h t ( x ) h_t(x) ht(x)

  • 最小化本轮的损失 L ( y , f t ( x ) ) = L ( y , f t — 1 ( x ) + h t ( x ) ) L(y,f_t(x)) = L(y,f_{t—1}(x)+ h_t(x)) L(y,ft(x))=L(y,ft—1(x)+ht(x))

  • 当采用平方损失函数时: L ( y , f t − 1 ( x ) + h t ( x ) ) = ( y − f t − 1 ( x ) − h t ( x ) ) 2 = ( r − h t ( x ) ) 2 L(y, f_{t-1}(x) + h_t(x)) = (y - f_{t-1}(x) - h_t(x))^2 = (r-h_t(x))^2 L(y,ft1(x)+ht(x))=(yft1(x)ht(x))2=(rht(x))2

  • 这里, r = y − f t − 1 ( x ) r = y - f_{t-1}(x) r=yft1(x)是当前模型拟合数据的残差(residual) 。

  • 所以,对于提升树来说只需要简单地拟合当前模型的残差。

回到我们上面讲的那个通俗易懂的例子中,第一次迭代的残差是10岁,第二次残差4岁…


当损失函数是平方损失和指数损失函数时,梯度提升树每一步优化是很简单的,但是对于一般损失函数而言,往往每一步优化起来不那么容易。

针对这一问题,Friedman提出了梯度提升树算法,这是利用最速下降的近似方法,其关键是利用损失函数的负梯度作为提升树算法中的残差的近似值

那么负梯度长什么样呢?

  • t t t 轮的第 i i i 个样本的损失函数的负梯度为:

− [ ∂ L ( y , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f t − 1 ( x ) -\left[ \frac{\partial L(y, f(x_i))}{\partial f(x_i)} \right]_{f(x) = f_{t-1}(x)} [f(xi)L(y,f(xi))]f(x)=ft1(x)

  • 此时不同的损失函数将会得到不同的负梯度,如果选择平方损失:

L ( y , f ( x i ) ) = 1 2 ( y − f ( x i ) ) 2 L(y, f(x_i)) = \frac{1}{2}(y - f(x_i))^2 L(y,f(xi))=21(yf(xi))2

  • 则负梯度为:

− [ ∂ L ( y , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f t − 1 ( x ) = y − f ( x i ) -\left[ \frac{\partial L(y, f(x_i))}{\partial f(x_i)} \right]_{f(x) = f_{t-1}(x)} = y - f(x_i) [f(xi)L(y,f(xi))]f(x)=ft1(x)=yf(xi)

此时我们发现GBDT的负梯度就是残差,所以说对于回归问题,我们要拟合的就是残差。

那么对于分类问题呢?

  • 二分类和多分类的损失函数都是 logloss

本文以回归问题为例进行讲解。

5.3 GBDT算法原理

上面两节分别将Decision Tree和Gradient Boosting介绍完了,下面将这两部分组合在一起就是我们的GBDT了。

步骤一:初始化弱学习器

f 0 ( x ) = a r g m i n c ∑ i = 1 N L ( y i , c ) f_0(x) = \mathrm{argmin}_c\sum_{i = 1}^N L(y_i, c) f0(x)=argminci=1NL(yi,c)

步骤二:对 m = 1 , 2 , . . . , M m = 1, 2, ..., M m=1,2,...,M 有:

  1. 对每个样本 i = 1 , 2 , . . . , N i = 1, 2, ..., N i=1,2,...,N,计算负梯度,即残差: r i m = − [ ∂ L ( y , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f m − 1 ( x ) r_{im} = -\left[ \frac{\partial L(y, f(x_i))}{\partial f(x_i)} \right]_{f(x)=f_{m-1}(x)} rim=[f(xi)L(y,f(xi))]f(x)=fm1(x)
  2. 将上步得到的残差作为样本新的真实值,并将数据 ( x i , r i m ) , i = 1 , 2 , . . . , N (x_i,r_{im}), i = 1,2,..., N (xi,rim),i=1,2,...,N 作为下棵树的训练数据,得到一颗新的回归树 f m ( x ) f_m(x) fm(x) 其对应的叶子节点区域为 R j m , j = 1 , 2 , . . . , J R_{jm, j} = 1,2,...,J Rjm,j=1,2,...,J。其中 J J J 为回归树 t t t 的叶子节点的个数。
  3. 对叶子区域 j = 1 , 2 , . . J j=1,2,..J j=1,2,..J 计算最佳拟合值 γ j m = a r g m i n γ ∑ x i ∈ R j m L ( y i , f m − 1 ( x i ) + γ ) \gamma_{jm} = \underset{\gamma}{\mathrm{argmin}}\sum_{x_i \in R_{jm}} L(y_i, f_{m-1}(x_i) + \gamma) γjm=γargminxiRjmL(yi,fm1(xi)+γ)
  4. 更新强学习器 f m ( x ) = f m − 1 ( x ) + ∑ i = 1 J γ j m I ( x ∈ R j m ) f_m(x) = f_{m-1}(x) + \sum_{i=1}^J \gamma_{jm}I(x\in R_{jm}) fm(x)=fm1(x)+i=1JγjmI(xRjm)

步骤三:得到最终学习器

f ( x ) = f M ( x ) = f 0 ( x ) + ∑ m = 1 M ∑ j = 1 J γ j m I ( x ∈ R j m ) f(x) = f_M(x) = f_0(x) + \sum_{m = 1}^M\sum_{j=1}^J\gamma_{jm}I(x\in R_{jm}) f(x)=fM(x)=f0(x)+m=1Mj=1JγjmI(xRjm)

5.4 实例介绍

5.4.1 数据介绍

根据如下数据,预测最后一个样本的身高。

编号 年龄(岁) 体重(kg) 身高(m)(标签值/目标值)
0 5 20 1.1
1 7 30 1.3
2 21 70 1.7
3 30 60 1.8
4(要预测的) 25 65

5.4.2 模型训练

5.4.2.1 设置参数

  • 学习率:learning_rate=0.1
  • 迭代次数:n_trees=5
  • 树的深度:max_depth=3

5.4.2.2 开始训练

步骤一:初始化弱学习器

f 0 ( x ) = a r g m i n c ∑ i = 1 N L ( y , c ) f_0(x) = \mathrm{argmin}_c\sum_{i=1}^N L(y, c) f0(x)=argminci=1NL(y,c)

损失函数为平方损失,因为平方损失函数是一个凸函数,直接求导,倒数等于零,得到 c c c

∑ i = 1 N ∂ L ( y , c ) ∂ c = ∑ i = 1 N ∂ ( 1 2 ( y i − c ) 2 ) ∂ c = ∑ i = 1 N c − y i \sum^N_{i = 1}\frac{\partial L(y, c)}{\partial c} = \sum_{i = 1}^N \frac{\partial(\frac{1}{2}(y_i - c)^2)}{\partial c} = \sum_{i=1}^N c-y_i i=1NcL(y,c)=i=1Nc(21(yic)2)=i=1Ncyi

令导数等于0:

∑ i = 1 N c − y i = 0 \sum_{i=1}^N c-y_i = 0 i=1Ncyi=0

c = ∑ i = 1 N y i N c = \frac{\sum^N_{i = 1}y_i}{N} c=Ni=1Nyi

所以初始化时, c c c 取值为所有训练样本标签值的均值。 c = ( 1.1 + 1.3 + 1.7 + 1.8 ) / 4 = 1.475 c =(1.1+1.3+1.7+1.8)/4= 1.475 c=(1.1+1.3+1.7+1.8)/4=1.475,此时得到初始学习器 f 0 ( x ) f_0(x) f0(x)

f 0 ( x ) = c = 1.475 f_0(x) = c = 1.475 f0(x)=c=1.475

步骤二:对迭代轮数 m = 1 , 2 , . . . , M m=1,2,...,M m=1,2,...,M

由于我们设置了迭代次数:n_trees=5,这里的 M = 5 M =5 M=5

计算负梯度,根据上文损失函数为平方损失时,负梯度就是残差,再直白一点就是 y y y 与上一轮得到的学习器 f m − 1 f_{m-1} fm1 的差值:

r i 1 = − [ ∂ L ( y , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f 0 ( x ) r_{i1} = -\left[ \frac{\partial L(y, f(x_i))}{\partial f(x_i)} \right]_{f(x)=f_0(x)} ri1=[f(xi)L(y,f(xi))]f(x)=f0(x)

残差在下表列出:

编号 真实值 f 0 ( x ) f_0(x) f0(x) 残差
0 1.1 1.475 -0.375
1 1.3 1.475 -0.175
2 1.7 1.475 0.225
3 1.8 1.475 0.325

此时将残差作为样本的真实值来训练弱学习器 f 1 ( x ) f_1(x) f1(x),即下表数据:

编号 年龄(岁) 体重(kg) 身高(m)(标签值/目标值)
0 5 20 -0.375
1 7 30 -0.175
2 21 70 0.225
3 30 60 0.325

接着,寻找回归树的最佳划分节点,遍历每个特征的每个可能取值。

从年龄特征的5开始,到体重特征的70结束,分别计算分裂后两组数据的平方损失(Square Error)。

S E l \mathrm{SE_l} SEl 为左节点平方损失, S E r \mathrm{SE_r} SEr 为右节点平方损失,找到使平方损失和 S E s u m = S E l + S E r \mathrm{SE_{sum} = SE_l + SE_r} SEsum=SEl+SEr 最小的那个划分节点,即为最佳划分节点。

例如︰以年龄21为划分节点,将小于21的样本划分为到左节点,大于等于21的样本划分为右节点。左节点包括 x 0 , x 1 x_0, x_1 x0,x1,右节点包括样本 x 2 , x 3 x_2, x_3 x2,x3

S E l = 0.02 , S E r = 0.005 , S E s u m = 0.025 \mathrm{SE_l} = 0.02, \mathrm{SE_r} = 0.005, \mathrm{SE_{sum}} = 0.025 SEl=0.02,SEr=0.005,SEsum=0.025

S E l = [ − 0.375 − ( − 0.275 ) ] 2 + [ − 0.175 − ( − 0.275 ) ] 2 = 0.02 \mathrm{SE_l} = [-0.375 - (-0.275)]^2 + [-0.175 - (-0.275)]^2 = 0.02 SEl=[0.375(0.275)]2+[0.175(0.275)]2=0.02

S E r = [ 0.225 − 0.275 ] 2 + [ 0.325 − 0.275 ] 2 = 0.005 \mathrm{SE_r} = [0.225 - 0.275]^2 + [0.325 - 0.275]^2 = 0.005 SEr=[0.2250.275]2+[0.3250.275]2=0.005

所有可能划分情况如下表所示:

划分点 小于划分点的样本 大于等于划分点的样本 S E l \mathrm{SE_l} SEl S E r \mathrm{SE_r} SEr S E s u m \mathrm{SE_{sum}} SEsum
年龄5 / 0, 1, 2, 3 0 0.327 0.327
年龄7 0 1, 2, 3 0 0.14 0.14
年龄21 0, 1 2, 3 0.02 0.005 0.025
年龄30 0, 1, 2 3 0.187 0 0.187
体重20 / 0, 1, 2, 3 0 0.327 0.327
体重30 0 1, 2, 3 0 0.14 0.14
体重60 0, 1 2, 3 0.02 0.005 0.025
体重70 0, 1, 3 2 0.26 0 0.26

以上划分点是的总平方损失最小为0.025,有两个划分点:年龄21和体重60,所以随机选一个作为划分点,这里我们选年龄21。

现在我们的第一棵树长这个样子:

在这里插入图片描述

我们设置的参数中树的深度max_depth=3,现在树的深度只有2,需要再进行一次划分,这次划分要对左右两个节点分别进行划分:

对于左节点,只含有 0, 1 两个样本,根据下表我们选择年龄7划分。

划分点 小于划分点的样本 大于等于划分点的样本 S E l \mathrm{SE_l} SEl S E r \mathrm{SE_r} SEr S E s u m \mathrm{SE_{sum}} SEsum
年龄5 / 0, 1 0 0.02 0.02
年龄7 0 1 0 0 0
体重20 / 0, 1 0 0.02 0.02
体重30 0 1 0 0 0

对于右节点,只含有 2,3 两个样本,根据下表我们选择年龄30划分(也可以选体重70)。

划分点 小于划分点的样本 大于等于划分点的样本 S E l \mathrm{SE_l} SEl S E r \mathrm{SE_r} SEr S E s u m \mathrm{SE_{sum}} SEsum
年龄21 / 2, 3 0 0.005 0.005
年龄30 2 3 0 0 0
体重60 / 2, 3 0 0.005 0.005
体重70 3 2 0 0 0

现在我们的第一棵树长这个样子:

在这里插入图片描述

此时我们的树深度满足了设置,还需要做一件事情,给这每个叶子节点分别赋一个参数 γ \gamma γ,来拟合残差。

γ j 1 = a r g m i n γ ∑ x i ∈ R j 1 L ( y i , f 0 ( x i ) + γ ) \gamma_{j1} = \underset{\gamma}{\mathrm{argmin}}\sum_{x_i \in R_{j1}} L(y_i, f_{0}(x_i) + \gamma) γj1=γargminxiRj1L(yi,f0(xi)+γ)

这里其实和上面初始化学习器是一个道理,平方损失,求导,令导数等于零,化简之后得到每个叶子节点的参数 γ \gamma γ,其实就是标签值的均值。这个地方的标签值不是原始的 y y y,而是本轮要拟合的标残差 y − f 0 ( x ) y - f_0(x) yf0(x)

根据上述划分结果,为了方便表示,规定从左到右为第 1,2,3,4 个叶子结点

( x 0 ∈ R 11 ) , γ 11 = − 0.375 ( x 1 ∈ R 21 ) , γ 21 = − 0.175 ( x 2 ∈ R 31 ) , γ 31 = 0.225 ( x 3 ∈ R 41 ) , γ 41 = 0.325 (x_0 \in R_{11}), \gamma_{11} = -0.375 \\ (x_1 \in R_{21}), \gamma_{21} = -0.175 \\ (x_2 \in R_{31}), \gamma_{31} = 0.225 \\ (x_3 \in R_{41}), \gamma_{41} = 0.325 \\ (x0R11),γ11=0.375(x1R21),γ21=0.175(x2R31),γ31=0.225(x3R41),γ41=0.325

此时的树长这个样子:

在这里插入图片描述

此时可更新强学习器,需要用到参数学习率:learning_rate=0.1,用 l r lr lr 表示。

f 1 ( x ) = f 0 ( x ) + l r ∗ ∑ j = 1 4 γ j 1 I ( x ∈ R j 1 ) f_1(x) = f_0(x) + lr * \sum_{j=1}^4\gamma_{j1}I(x \in R_{j1}) f1(x)=f0(x)+lrj=14γj1I(xRj1)

为什么要用学习率呢?这是Shrinkage的思想,如果每次都全部加上(学习率为1)很容易一步学到位导致过拟合。

重复此步骤,直到 m > 5 m>5 m>5 结束,最后生成5棵树。

在这里插入图片描述

结果中,0.9倍这个现象,和其学习率有关。这是因为数据简单每棵树长得一样,导致每一颗树的拟合效果一样,而每棵树都只学上一棵树残差的0.1倍,导致这颗树只能拟合剩余0.9了。

步骤三:得到最后的强学习器

f ( x ) = f 5 ( x ) = f 0 ( x ) + ∑ m = 1 5 ∑ j = 1 4 γ j m I ( x ∈ R j m ) f(x) = f_5(x) = f_0(x) + \sum^5_{m=1}\sum_{j=1}^4 \gamma_{jm}I(x \in R_{jm}) f(x)=f5(x)=f0(x)+m=15j=14γjmI(xRjm)

步骤四:预测样本

  • f 0 ( x ) = 1.475 f_0(x) = 1.475 f0(x)=1.475
  • f 1 ( x ) f_1(x) f1(x) 中,样本4的年龄为25,大于划分节点21岁,又小于30岁,所以被预测为0.2250
  • f 2 ( x ) f_2(x) f2(x) 中,样本4的…此处省略…所以被预测为0.2025
  • f 3 ( x ) f_3(x) f3(x) 中,样本4的…此处省略…所以被预测为0.1823
  • f 4 ( x ) f_4(x) f4(x) 中,样本4的…此处省略…所以被预测为0.1640
  • f 5 ( x ) f_5(x) f5(x) 中,样本4的…此处省略…所以被预测为0.1476

最终预测结果为:

f ( x ) = 1.475 + 0.1 × ( 0.225 + 0.2025 + 0.1823 + 0.164 + 0.1476 ) = 1.56714 f(x)= 1.475 + 0.1 \times (0.225 + 0.2025 + 0.1823 + 0.164 + 0.1476) = 1.56714 f(x)=1.475+0.1×(0.225+0.2025+0.1823+0.164+0.1476)=1.56714


小结

  • GBDT算法原理【知道】
    1. 初始化弱学习器 f 0 ( x ) = a r g m i n c ∑ i = 1 N L ( y , c ) f_0(x) = \mathrm{argmin}_c\sum_{i=1}^N L(y,c) f0(x)=argminci=1NL(y,c)
    2. m = 1 , 2 , . . . , M m=1,2,...,M m=1,2,...,M,有:
      1. 对每个样本 i = 1 , 2 , . . . , N i=1,2,..., N i=1,2,...,N,计算负梯度,即残差 r i m = − [ ∂ L ( y , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f m − 1 ( x ) r_{im} = -\left[ \frac{\partial L(y, f(x_i))}{\partial f(x_i)} \right]_{f(x)=f_{m-1}(x)} rim=[f(xi)L(y,f(xi))]f(x)=fm1(x)
      2. 将上步得到的残差作为样本新的真实值,并将数据 ( x i , r i m ) , i = 1 , 2 , . . . , N (x_i, r_{im}), i = 1,2,..., N (xi,rim),i=1,2,...,N 作为下棵树的训练数据,得到一颗新的回归树 f m ( x ) f_m(x) fm(x) 其对应的叶子节点区域为 R j m , j = 1 , 2 , . . . , J R_{jm}, j = 1,2,...,J Rjm,j=1,2,...,J。其中 J J J 为回归树 t t t 的叶子节点的个数。
      3. 对叶子区域 j = 1 , 2 , . . . , J j=1,2,..., J j=1,2,...,J 计算最佳拟合值 γ j m = a r g m i n γ ∑ x i ∈ R j m L ( y , f m − 1 ( x i ) + γ ) \gamma_{jm} = \underset{\gamma}{\mathrm{argmin}}\sum_{x_i \in R_{jm}} L(y, f_{m-1}(x_i) + \gamma) γjm=γargminxiRjmL(y,fm1(xi)+γ)
      4. 更新强学习器 f m ( x ) = f m − 1 ( x ) + l r × ∑ i = 1 J γ j m I ( x ∈ R j m ) f_m(x) = f_{m-1}(x) + lr \times \sum_{i=1}^J\gamma_{jm}I(x\in R_{jm}) fm(x)=fm1(x)+lr×i=1JγjmI(xRjm)
    3. 得到最终学习器 f ( x ) = f M ( x ) = f 0 ( x ) + ∑ m = 1 M ∑ j = 1 J γ j m I ( x ∈ R j m ) f(x) = f_M(x) = f_0(x) + \sum_{m=1}^M\sum_{j=1}^J\gamma_{jm}I(x \in R_{jm}) f(x)=fM(x)=f0(x)+m=1Mj=1JγjmI(xRjm)

GBDT 算法首先初始化弱学习器,然后对每个样本计算负梯度,即残差。接下来,将上一步得到的残差作为样本新的真实值,并将数据作为下棵树的训练数据,得到一颗新的回归树。然后,对叶子区域计算最佳拟合值,并更新强学习器。最后,得到最终学习器。

在 GBDT 算法原理中,公式的解释如下:

  • f 0 ( x ) f_0(x) f0(x):初始化弱学习器。
  • a r g m i n c ∑ i = 1 N L ( y , c ) \mathrm{argmin}_c\sum_{i=1}^N L(y,c) argminci=1NL(y,c):求解最小化损失函数 L ( y , c ) L(y,c) L(y,c) 的参数 c c c
  • m m m:迭代次数。
  • r i m r_{im} rim:第 i i i 个样本在第 m m m 次迭代时的残差。
  • ∂ L ( y , f ( x i ) ) ∂ f ( x i ) \frac{\partial L(y, f(x_i))}{\partial f(x_i)} f(xi)L(y,f(xi)):损失函数 L ( y , f ( x i ) ) L(y, f(x_i)) L(y,f(xi)) f ( x i ) f(x_i) f(xi) 的偏导数。
  • f m ( x ) f_m(x) fm(x):第 m m m 次迭代后的强学习器。
  • R j m R_{jm} Rjm:回归树 f m ( x ) f_m(x) fm(x) 的第 j j j 个叶子节点区域。
  • γ j m \gamma_{jm} γjm:第 j j j 个叶子区域的最佳拟合值。
  • l r lr lr:学习率,用于控制每次迭代对模型的影响程度。
  • ∑ i = 1 J γ j m I ( x ∈ R j m ) \sum_{i=1}^J\gamma_{jm}I(x\in R_{jm}) i=1JγjmI(xRjm):对所有叶子区域求和,其中 I ( x ∈ R j m ) I(x\in R_{jm}) I(xRjm) 表示当样本 x x x 属于叶子区域 R j m R_{jm} Rjm 时取值为 1,否则为 0。

猜你喜欢

转载自blog.csdn.net/weixin_44878336/article/details/131114125