梯度晋升算法是最罕用的集成机器学习技术之一,该模型应用弱决策树序列来构建强学习器。这也是 XGBoost 和 LightGBM 模型的实践根底,所以在这篇文章中,咱们将从头开始构建一个梯度加强模型并将其可视化。
梯度晋升算法介绍
梯度晋升算法(Gradient Boosting)是一种集成学习算法,它通过构建多个弱分类器,而后将它们组合成一个强分类器来进步模型的预测准确率。
梯度晋升算法的原理能够分为以下几个步骤:
- 初始化模型:一般来说,咱们能够应用一个简略的模型(比如说决策树)作为初始的分类器。
- 计算损失函数的负梯度:计算出每个样本点在以后模型下的损失函数的负梯度。这相当于是让新的分类器去拟合以后模型下的误差。
- 训练新的分类器:用这些负梯度作为指标变量,训练一个新的弱分类器。这个弱分类器能够是任意的分类器,比如说决策树、线性模型等。
- 更新模型:将新的分类器退出到原来的模型中,能够用加权均匀或者其余办法将它们组合起来。
- 反复迭代:反复上述步骤,直到达到预设的迭代次数或者达到预设的准确率。
因为梯度晋升算法是一种串行算法,所以它的训练速度可能会比较慢,咱们以一个理论的例子来介绍:
假如咱们有一个特色集 Xi 和值 Yi,要计算 y 的最佳预计
咱们从 y 的平均值开始
每一步咱们都想让 F_m(x)更靠近 y |x。
在每一步中,咱们都想要 F_m(x)一个更好的 y 给定 x 的近似。
首先,咱们定义一个损失函数
而后,咱们向损失函数绝对于学习者 Fm 降落最快的方向后退:
因为咱们不能为每个 x 计算 y,所以不晓得这个梯度的确切值,然而对于训练数据中的每一个 x_i,梯度齐全等于步骤 m 的残差:r_i!
所以咱们能够用弱回归树 h_m 来近似梯度函数 g_m,对残差进行训练:
而后,咱们更新学习器
这就是梯度晋升,咱们不是应用损失函数绝对于以后学习器的实在梯度 g_m 来更新以后学习器 F_{m},而是应用弱回归树 h_m 来更新它。
也就是反复上面的步骤
1、计算残差:
2、将回归树 h_m 拟合到训练样本及其残差 (x_i, r_i) 上
3、用步长 \alpha 更新模型
看着很简单对吧,上面咱们可视化一下这个过程就会变得十分清晰了
决策过程可视化
这里咱们应用 sklearn 的 moons 数据集,因为这是一个经典的非线性分类数据
import numpy as np
import sklearn.datasets as ds
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn import tree
from itertools import product,islice
import seaborn as snsmoonDS = ds.make_moons(200, noise = 0.15, random_state=16)
moon = moonDS[0]
color = -1*(moonDS[1]*2-1)
df =pd.DataFrame(moon, columns = ['x','y'])
df['z'] = color
df['f0'] =df.y.mean()
df['r0'] = df['z'] - df['f0']
df.head(10)
让咱们可视化数据:
下图能够看到,该数据集是能够显著的辨别出分类的边界的,然而因为他是非线性的,所以应用线性算法进行分类时会遇到很大的艰难。
那么咱们先编写一个简略的梯度加强模型:
def makeiteration(i:int):
"""Takes the dataframe ith f_i and r_i and approximated r_i from the features, then computes f_i+1 and r_i+1"""
clf = tree.DecisionTreeRegressor(max_depth=1)
clf.fit(X=df[['x','y']].values, y = df[f'r{i-1}'])
df[f'r{i-1}hat'] = clf.predict(df[['x','y']].values)
eta = 0.9
df[f'f{i}'] = df[f'f{i-1}'] + eta*df[f'r{i-1}hat']
df[f'r{i}'] = df['z'] - df[f'f{i}']
rmse = (df[f'r{i}']**2).sum()
clfs.append(clf)
rmses.append(rmse)
下面代码执行 3 个简略步骤:
将决策树与残差进行拟合:
clf.fit(X=df[['x','y']].values, y = df[f'r{i-1}'])
df[f'r{i-1}hat'] = clf.predict(df[['x','y']].values)
而后,咱们将这个近似的梯度与之前的学习器相加:
df[f'f{i}'] = df[f'f{i-1}'] + eta*df[f'r{i-1}hat']
最初从新计算残差:
df[f'r{i}'] = df['z'] - df[f'f{i}']
步骤就是这样简略,上面咱们来一步一步执行这个过程。
第 1 次决策
Tree Split for 0 and level 1.563690960407257
第 2 次决策
Tree Split for 1 and level 0.5143677890300751
第 3 次决策
Tree Split for 0 and level -0.6523728966712952
第 4 次决策
Tree Split for 0 and level 0.3370491564273834
第 5 次决策
Tree Split for 0 and level 0.3370491564273834
第 6 次决策
Tree Split for 1 and level 0.022058885544538498
第 7 次决策
Tree Split for 0 and level -0.3030575215816498
第 8 次决策
Tree Split for 0 and level 0.6119407713413239
第 9 次决策
能够看到通过 9 次的计算,基本上曾经把下面的分类进行了辨别
咱们这里的学习器都是非常简单的决策树,只沿着一个特色决裂! 但整体模型在每次决策后边的越来越简单,并且整体误差逐步减小。
plt.plot(rmses)
这也就是上图中咱们看到的可能正确区分出了大部分的分类
如果你感兴趣能够应用上面代码自行试验:
https://avoid.overfit.cn/post/533a0736b7554ef6b8464a5d8ba964ab
作者:Tanguy Renaudie