9.6 Gradient Boost 原理与实现#
在前面两节内容中,我们分别详细介绍了AdaBoost算法和基于AdaBoost改进的SAMME算法的基本原理和实现过程。在接下来的这节内容中将会介绍另外一种基于Boost策略的算法模型——梯度提升(Gradient Boosting, GB)算法。从名字可以看出梯度提升算法有两个关键的地方:梯度和提升。提升表明整个集成模型依旧是串行进行,且后一个模型是用来对前一个模型的输出结果进行修正;梯度则表明后一个模型在对前一个模型的结果进行修正时是以梯度下降为策略进行优化的。
9.6.1 Gradient Boost 算法思想#
梯度提升是Friedman等人[12]于2001年所提出的一种基于梯度下降策略来对模型进行优化的算法,其核心思想是先通过设定一个目标函数,然后再通过梯度下降算法来对预测结果进行优化。梯度提升算法的整体结构类似于之前介绍的线性回归和逻辑回归的求解流程只是在具体细节部分有着不同的处理方式。
在AdaBoost算法中,通过赋予每个样本一个权重值,并且在上一个模型中误差越大的样本(分类问题中即被错分的样本)在下一个模型中将会被给予更高的权重,然后对于每个基模型来说其都有不同的策略来降低高权重样本的误差,以此来提高整个集成模型的预测精度,即在AdaBoost算法中它是通过赋予样本权重来提升模型的精度。对于梯度提升算法来说,它则是通过梯度来提升整个模型的预测精度。
如式(9-57)所示,便是梯度下降算法的核心公式
$$ w=w-\alpha\cdot\frac{\partial J}{\partial w}\tag{9-57} $$其中$w$表示待更新的参数对象,$J$表示关于参数$w$的目标函数,$\alpha$表示学习率。更多关于梯度下降算法的详细介绍可以参见2.5节内容。
通过式(9-57)可知,对于参数$w$来说可以根据其梯度的方向来一步一步迭代计算得到(接近)其最优解。现在假定某个模型的预测输出为$\hat{y}=f(x)$,预测值与真实值之间的损失误差为$J(y,\hat{y})$,那么为了提高模型$f(x)$的预测结果,同样可以采用梯度下降算法来对预测结果进行迭代更新,即
$$ f(x)=f(x)-\alpha\cdot\frac{\partial J}{\partial f(x)}\tag{9-58} $$而式(9-58)便是梯度提升算法的核心思想。
从式(9-58)可以看出,在梯度提升算法中最重要的便是求得目标函数$J$关于模型$f(x)$的梯度,而这通常会通过训练一系列额外的模型来对梯度进行预测,具体可见后续代码实现部分。
9.6.2 Gradient Boost 示例代码#
在明白梯度提升算法的基本思想后我们再来看如何在sklearn中使用它。首先,需要知道的是梯度提升算法本质上也只是一种模型的训练策略,因此对于基学习器的选择是任意的。但是,通常默认情况下都是以决策树作为基学习器,所以基于梯度提升方法最常见的一个模型便是梯度提升决策树(Gradient Boosting Decision Tree, GBDT)也简称梯度提升树,而sklearn中的GradientBoostingClassifier模块便是以决策树为基学习器进行实现的且不可更改。
在sklearn中,可以通过如下方式来使用梯度提升树,示例代码如下:
1 from sklearn.ensemble import GradientBoostingClassifier
2 from sklearn.datasets import load_iris
3 from sklearn.model_selection import train_test_split
4
5 if __name__ == '__main__':
6 x, y = load_iris(return_X_y=True)
7 x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
8 model = GradientBoostingClassifier(learning_rate=0.2, n_estimators=50)
9 model.fit(x_train, y_train)
10 print(model.score(x_test, y_test)) # 0.956在上述代码中,第6~7行用来返回数据集并划分为训练集和测试集。第8行是实例化一个GradientBoostingClassifier类对象,其中learning_rate指式(9-58)中的学习率$\alpha$,n_estimators表示基学习器的数量,即9.6.3节算法原理第三步中的$M$。第9~10行是拟合模型并输出模型在测试集上的准确率。
由于GradientBoostingClassifier是以决策树为基学习器,因此在第8行中实例化时还可以指定决策树对应的相关参数,例如max_depth,min_samples_leaf等。最后,sklearn中对应的梯度提升回归模块为GradientBoostingRegressor。上述完整示例代码可参见 AllBooKCode/Chapter09/C14_gbdt_train.py 文件。
9.6.3 Gradient Boost 算法原理#
在介绍完梯度提升算法的思想和使用示例后再来看它背后的具体原理。假如现在我们需要训练一个模型,数据样本对应的标签值为$y$,第$m$次提升后模型的预测输出为$\hat{y}^{(m)}=f^{(m)}(x)$,预测值与真实值之间的损失为$J(y,\hat{y}^{(m)})$,则对于梯度提升算法来说可通过如下步骤求得模型:
第一步:初始化学习率$\alpha$以及$m=0$并根据数据集训练得到模型$f^{(m)}(x)$,
第二步:计算得到损失函数$J$关于$f^{(m)}(x)$的梯度,并根据如下公式对模型$f^{(m)}(x)$进行更新
$$ f^{(m+1)}(x)=f^{(m)}(x)-\alpha\cdot\frac{\partial J}{\partial f^{(m)}(x)}\tag{9-59} $$第三步:判断$m<=M$是否成立,成立则结束迭代,否则$m=m+1$,并进入第二步。
以上便是梯度提升算法整个流程的核心步骤,下面分别来看它在回归和分类模型中的详细计算示例。
9.6.4 Gradient Boost 回归#
例如现在某数据集一共5个样本,标签分别为$y_0=2,y_1=4,y_2=2.5,y_3=1.8,y_4=2.4$,模型的预测输出为$\hat{y}^{(m)}=f^{(m)}(x)$,$M=3,\alpha=0.5$,且采用均方误差作为损失函数,即
$$ J(y,\hat{y}^{(m)})=\frac{1}{2}(y-\hat{y}^{(m)})^2\tag{9-60} $$由式(9-60)可知,损失函数$J$关于$\hat{y}^{(m)}$的梯度为
$$ \frac{\partial J}{\partial \hat{y}^{(m)}}=-(y-\hat{y}^{(m)})\tag{9-61} $$假定模型$f^{(0)}(x)$的初始预测结果为$\hat{y}^{(0)}_0=1.6,\hat{y}^{(0)}_1=2.7,\hat{y}^{(0)}_2=0.5,\hat{y}^{(0)}_3=1.1,\hat{y}^{(0)}_4=0.9$,则采用梯度提升算法进行第1次提升后的结果$f^{(1)}(x)$为
$$ \begin{aligned} f^{(1)}(x)=f^{(0)}(x)+\alpha\cdot(y-\hat{y}^{(0)}) =\begin{bmatrix}1.6\\2.7\\0.5\\1.1\\0.9\end{bmatrix}+ 0.5\cdot\begin{bmatrix}2-1.6\\4-2.7\\2.5-0.5\\1.8-1.1\\2.4-0.9\end{bmatrix}=\begin{bmatrix}1.8\\3.35\\1.5\\1.45\\1.65\end{bmatrix} \end{aligned}\tag{9-62} $$同理可得,第2、3次提升后的结果$f^{(2)}(x),f^{(3)}(x)$对应的预测结果分别为
$$ f^{(2)}(x)=\begin{bmatrix}1.9\\3.675\\2\\1.625\\2.205\end{bmatrix}, \;\; f^{(3)}(x)=\begin{bmatrix}1.95\\3.837\\2.25\\1.712\\2.213\end{bmatrix},\;\; y=\begin{bmatrix}2\\4\\2.5\\1.8\\2.4\end{bmatrix}\tag{9-63} $$从上述过程可以看出,随着迭代次数的增加,模型的预测结果也更加地接近于标签的真实值。上述计算过程示例代码可参见 AllBooKCode/Chapter09/C15_example_gradient_boost_reg.py 文件。
9.6.5 Gradient Boost 分类#
在介绍完梯度提升中的回归计算示例后相信各位读者对梯度提升算法已经有了一定的认识。不过在分类场景中,应该如何利用梯度下降来对模型的预测结果进行优化提升呢?一个可行的办法便是对每个类别下的预测概率进行梯度提升优化。
例如现在某三分类数据集一共5个样本,标签分别为$y_0=2,y_1=1,y_2=2,y_3=0,y_4=0$,模型的预测输出为$\hat{y}^{(m)}=f^{(m)}(x)$,$M=3,\alpha=0.5$,且采用多项式误差(Multinomial Deviance)作为损失函数,即对于任意一个样本$x$来说其在第$m$次提升后对应的损失为
$$ J(y,p^{(m)}(x))=-\sum_{k=0}^K\mathbb{I}(y=k)f^{(m)}_k(x)+\log\left(\sum_{k=0}^Ke^{f^{(m)}_k(x)}\right)\tag{9-64} $$