1. 前向分步算法
前项分布算法可以解决分类问题,也可以解决回归问题。
(1)Adaboost的加法模型:
在Adaboost的基础上,将多个基分类器合并为一个复杂分类器,是通过计算每个基分类器的加权和。通常情况下这是一个复杂的优化问题,很难通过简单的凸优化的相关知识进行解决。而前向分步算法可以用来求解这种方式的问题,它的基本思路是:因为学习的是加法模型,如果从前向后,每一步只优化一个基函数及其系数,逐步逼近目标函数,那么就可以降低优化的复杂度。
(2)前向分布算法:
给定数据集。损失函数,基函数集合,我们需要输出加法模型。
- 初始化:
- 对m = 1,2,...,M:
- (a) 极小化损失函数:
得到参数与 - (b) 更新:
- (a) 极小化损失函数:
- 得到加法模型:
这样,前向分步算法将同时求解从m=1到M的所有参数,的优化问题简化为逐次求解各个,的问题。
(3) 前向分步算法与Adaboost的关系:
Adaboost算法是前向分步算法的特例,Adaboost算法是由基本分类器组成的加法模型,损失函数为指数损失函数。
2. 梯度提升决策树(GBDT)
(1) 基于残差学习的提升树算法:
接下来我们来探讨下如何使用加法模型+前向分步算法的框架实现回归问题。
在使用加法模型+前向分步算法的框架解决问题之前,我们需要首先确定框架内使用的基函数是什么,在这里我们使用决策树分类器。前面第二章我们已经学过了回归树的基本原理,树算法最重要是寻找最佳的划分点,分类树用纯度来判断最佳划分点使用信息增益(ID3算法),信息增益比(C4.5算法),基尼系数(CART分类树)。但是在回归树中的样本标签是连续数值,可划分点包含了所有特征的所有可取的值。所以再使用熵之类的指标不再合适,取而代之的是平方误差,它能很好的评判拟合程度。基函数确定了以后,我们需要确定每次提升的标准是什么。回想Adaboost算法,在Adaboost算法内使用了分类错误率修正样本权重以及计算每个基本分类器的权重,那回归问题没有分类错误率可言,也就没办法在这里的回归问题使用了,因此我们需要另辟蹊径。模仿分类错误率,我们用每个样本的残差表示每次使用基函数预测时没有解决的那部分问题。因此,我们可以得出如下算法:
输入数据集,输出最终的提升树
- 初始化
- 对m = 1,2,...,M:
- 计算每个样本的残差:
- 拟合残差学习一棵回归树,得到
- 更新
- 得到最终的回归问题的提升树:
(2) 梯度提升决策树算法(GBDT):
提升树利用加法模型和前向分步算法实现学习的过程,当损失函数为平方损失和指数损失时,每一步优化是相当简单的,也就是我们前面探讨的提升树算法和Adaboost算法。但是对于一般的损失函数而言,往往每一步的优化不是那么容易,针对这一问题,我们得分析问题的本质,也就是是什么导致了在一般损失函数条件下的学习困难。对比以下损失函数:
观察Huber损失函数:
针对上面的问题,Freidman提出了梯度提升算法(gradient boosting),这是利用最速下降法的近似方法,利用损失函数的负梯度在当前模型的值作为回归问题提升树算法中的残差的近似值,拟合回归树。与其说负梯度作为残差的近似值,不如说残差是负梯度的一种特例。
以下开始具体介绍梯度提升算法:
输入训练数据集和损失函数,输出回归树
- 初始化
- 对于m=1,2,...,M:
- 对i = 1,2,...,N计算:
- 对拟合一个回归树,得到第m棵树的叶结点区域
- 对j=1,2,...J,计算:
- 更新
- 得到回归树:
3. 代码实现
from sklearn.metrics import mean_squared_error
from sklearn.datasets import make_friedman1
from sklearn.ensemble import GradientBoostingRegressor
X, y = make_friedman1(n_samples=1200, random_state=0, noise=1.0)
X_train, X_test = X[:200], X[200:]
y_train, y_test = y[:200], y[200:]
est = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1,
max_depth=1, random_state=0, loss='ls').fit(X_train, y_train)
mean_squared_error(y_test, est.predict(X_test))
运行结果:
5.009154859960321
from sklearn.datasets import make_regression
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
X, y = make_regression(random_state=0)
X_train, X_test, y_train, y_test = train_test_split(
X, y, random_state=0)
reg = GradientBoostingRegressor(random_state=0)
reg.fit(X_train, y_train)
reg.score(X_test, y_test)
运行结果:
0.43848663277068134