什么是Boosting?
Boosting是机器学习中的一种集成学习框架,集成学习的意思是将多个弱分类器组合成一个强分类器,这个强分类器能取所有弱分类器之所长,达到相对的最优性能。
我们可以将Boosting理解为一类将弱分类器提升为强分类器的算法,所以有时候Boosting算法也叫提升算法。下列的这些算法就是常见的Boosting算法:AdaBoost、GBDT、XGBoost、LightGBM和CatBoost。
Boosting算法的一般流程如下:
步骤1:所有分布下的基础学习器对于每个观测值都应该有相同的权重
步骤2:如果第一个基础的学习算法预测错误,则该点在下一次的基础学习算法中有更高的权重
步骤3:迭代第2步,直到到达预定的学习器数量或预定的预测精度。
简单来说,Boosting就是串行地训练一系列弱分类器,使得被先前弱分类器分类错误地样本在后续地到更多关注,最后将这些分类器组合成最优强分类器地过程。
下面我们就按照顺序,来一起看一下常见的boosting模型!
AdaBoost
AdaBoost的全称为Adaptive Boosting,即自适应提升算法。
Boosting方法要解答两个关键问题:
一是在训练过程中如何改变训练样本的权重或者概率分布;
二是如何将多个弱分类器组合成一个强分类器。
针对这两个问题,AdaBoost的做法非常朴素:
一是提高前一轮被弱分类器错误分类的样本的权重,而降低分类正确的样本的权重;
二是对多个弱分类器进行线性组合,提高分类效果好的弱分类器的权重,降低分类误差率高的弱分类器的权重。
AdaBoost实战
我们基于sklearn模拟生成的二分类数据集,将训练标签转化为二分类形式后进行训练。
from sklearn.model_selection import train_test_split
# 导入sklearn模拟二分类数据生成模块
from sklearn.datasets import make_blobs
# 生成模拟二分类数据集
X, y = make_blobs(n_samples=150, n_features=2, centers=2,
cluster_std=1.2, random_state=40)
# 将标签转换为1/-1
y_ = y.copy()
y_[y_==0] = -1
y_ = y_.astype(float)
# 训练/测试数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y_,
test_size=0.3, random_state=43)
# 设置颜色参数
colors = {0:'r', 1:'g'}
# 绘制二分类数据集的散点图
plt.scatter(X[:,0], X[:,1], marker='o', c=pd.Series(y).map(colors))
plt.show();
基于sklearn的AdaBoost实现样例
# 导入sklearn adaboost分类器
from sklearn.ensemble import AdaBoostClassifier
from sklearn.metrics import accuracy_score
# 创建Adaboost模型实例
clf_ = AdaBoostClassifier(n_estimators=5, random_state=0)
# 模型拟合
clf_.fit(X_train, y_train)
# 模型预测
y_pred_ = clf_.predict(X_test)
# 计算模型预测准确率
accuracy = accuracy_score(y_test, y_pred_)
print("Accuracy of AdaBoost by sklearn:", accuracy)
准确率
AdaBoost算法的特点
通过迭代每次学习一个弱分类器,在每次迭代的过程中,提高前一轮分类错误数据的权重,降低分类正确数据的权重。最后将弱分类器线性组合成一个强分类器。
GBDT
GBDT的全称为gradient boosting decision tree,即梯度提升决策树。有研究提出使用损失函数的负梯度在当前模型的值来求解更为一般的提升树模型,这种基于负梯度求解提升树前向分布迭代过程的方法也叫梯度提升树。
GBDT的基模型(弱分类器)为CART决策树:
针对分类问题的基模型为二叉分类树,对应梯度提升模型就叫GBDT;
针对回归问题的基模型为二叉回归树,对应梯度提升模型就叫GBRT。
通俗来说,假设一个人月薪10k,我们首先用一个树模型拟合了6k,发现有4k损失,然后再用一个树模型拟合了2k,这样持续拟合下去,拟合值和目标值之间的残差会越来越小。将每一轮迭代,也就是每一棵树的预测值加起来,就是模型最终的预测结果。
使用多颗决策树组合就是提升树模型,使用梯度下降法对提升树模型进行优化的过程就是梯度提升树模型。梯度提升树以梯度下降的方法,使用损失函数的负梯度在当前模型的值作为提升树中残差的近似值
GBDT实战
基于sklearn的GBDT实现样例
sklearn也提供了GBDT的算法实现,GBDT和GBRT的调用方式分别为ensemble.GradientBoostingClassifier和ensemble.GradientBoostingRegressor,基于波士顿房价数据集拟合示例如下。
# 导入sklearn GBDT模块
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.utils import shuffle
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import numpy as np
# 导入sklearn数据集模块
from sklearn import datasets
# 导入波士顿房价数据集
boston = datasets.load_boston()
# 打乱数据集
X, y = shuffle(boston.data, boston.target)
X = X.astype(np.float32)
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
# 创建模型实例
reg = GradientBoostingRegressor(n_estimators=200, learning_rate=0.5,
max_depth=4, random_state=0)
# 模型拟合
reg.fit(X_train, y_train)
# 模型预测
y_pred = reg.predict(X_test)
# 计算模型预测的均方误差
mse = mean_squared_error(y_test, y_pred)
print ("Mean Squared Error of sklearn GBRT:", mse)
均方误差
GBDT是目前应用最广泛的一类Boosting集成学习框架,而梯度提升能有效地优化一般地损失函数。GBDT以CART为基模型,所以其实现是建立在CART基础之上的。
XGBoost
XGBoost的全程为eXtreme Gradient Boosting,即极度梯度提升树。一度因其强大性能流行于各大数据竞赛,在各种顶级解决方案中屡见不鲜。
相较于GBDT,XGBoost的最大特性在于对损失函数展开到二阶导数,是的梯度提升树模型更能逼近其真实损失。
XGBoost本质上仍然属于GBDT算法,但在算法精度、速度和泛化能力上均要优于传统的GBDT算法。
从算法精度上来看,XGBoost通过将损失函数展开到二阶导数,使得其更能逼近真实损失;
从算法速度上来看,XGBoost使用了加权分位树sketch和稀疏感知算法这两个技巧,通过缓存优化和模型并行来提高算法速度;
从算法泛化能力上来看,通过对损失函数加入正则化项、加性模型中设置缩减率和列抽样等方法,来防止模型过拟合。
XGBoost实战
XGBoost实现样例
XGBoost的作者陈天奇提供了XGBoost原生的工业级官方库xgboost。
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 导入鸢尾花数据集
data = datasets.load_iris()
# 获取输入输出
X, y = data.data, data.target
# 数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=43)
# 设置模型参数
params = {
'booster': 'gbtree',
'objective': 'multi:softmax',
'num_class': 3,
'gamma': 0.1,
'max_depth': 2,
'lambda': 2,
'subsample': 0.7,
'colsample_bytree': 0.7,
'min_child_weight': 3,
'eta': 0.001,
'seed': 1000,
'nthread': 4,
}
dtrain = xgb.DMatrix(X_train, y_train)
num_rounds = 200
model = xgb.train(params, dtrain, num_rounds)
# 对测试集进行预测
dtest = xgb.DMatrix(X_test)
y_pred = model.predict(dtest)
# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print ("Accuracy:", accuracy)
# 绘制特征重要性
plot_importance(model)
plt.show();
我们首先指定了xgboost模型训练的各种参数,包括提升树类型、任务类型、类别数量和树最大深度等,然后将原始数据类型转换为xgboost的DMatrix数据类型,接着进行模型训练和预测,最后评估分类准确率,并绘制了特征重要性图,可视化地呈现每个特征在模型中的重要性评分。
LightGBM
LightGBM的全称为light gradient boosting machine,即轻量的梯度提升机。跟XGBoost一样,LightGBM也是GBDT算法框架的一种工程实现,不过更快速、更高效。
XGBoost可优化的地方
XGBoost寻找最优分裂点的算法复杂度可以估计为:
复杂度=特征数×特征分裂点的数量×样本量
在数据量和特征量都比较多的情况下,该方法占用空间太大,会严重影响算法性能。LightGBM的优化自然是从这些方向来考虑。
从特征寻找最优分裂点角度,LightGBM使用了直方图算法进行优化。
直方图算法本质上是一种数据离散化和分箱操作,虽然谈不上特别新颖的优化设计,但确实速度快性能优,计算代价和内存占用都大大减少。
从减少样本的角度,LightGBM使用单边梯度抽样进行优化。
将训练过程中大部分权重较小的样本剔除,仅对剩余样本数据计算信息增益。单边梯度抽样(GOSS) 的目的是尽可能保留对计算信息增益有帮助的样本,提高模型训练速度。
比如,我有训练数据1000w,设置梯度大的样本的保留10%,而梯度小的样本保留20%。算法上就是先按样本梯度对样本降序排序,保留梯度处于前10%的样本(100w),再从剩下的900w梯度较小样本随机挑选200w(1000w*20%)样本。这样的话样本量就从1000w减小到了300w,加快训练速度。
从特征数的角度,LightGBM使用互斥特征捆绑进行优化。
将两个互斥的特征捆绑为一个特征,在不丢失特征信息的前提下,减少特征数,从而加速模型训练。大多数时候两个特征不是完全互斥的,可以定义一个冲突比率衡量特征不互斥程度,当冲突比率较低是,可以将不完全互斥的两个特征捆绑,这对最后的模型精度没有太大影响。所谓特征互斥,即两个特征不会同时为非零值,这一点跟分类特征的one-hot表达有点类似。
LightGBM针对特征分裂点、样本量和特征数分别做出的优化处理。除此之外,LightGBM还提出区别于XGBoost的按层生长的叶子结点生长方法,即带有深度限制的按叶子结点(leaf-wise)生长的决策树生长方法。
level-wise和leaf-wise区别
XGBoost采用按层生长的level-wise算法,好处是可以多线程优化,也方便控制模型复杂度,且不容易过拟合;缺点是不加区分地对待同一层所有叶子结点,大部分结点分裂和增益计算不是必需的,产生了额外的计算开销。
LightGBM提出了按叶子结点生长的leaf-wise算法,精度更高且更高效,能够节约不必要的计算开销,同时为防止某一结点过分生长而加上一个深度限制机制,能够在保证精度的同时一定程度上防止过拟合。
LightGBM实战
LightGBM实现样例
# 导入相关模块
import lightgbm as lgb
from sklearn.metrics import accuracy_score
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
# 导入iris数据集
iris = load_iris()
data = iris.data
target = iris.target
# 数据集划分
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=43)
# 创建lightgbm分类模型
gbm = lgb.LGBMClassifier(objective='multiclass',
num_class=3,
num_leaves=31,
learning_rate=0.05,
n_estimators=20)
# 模型训练
gbm.fit(X_train, y_train, eval_set=[(X_test, y_test)], early_stopping_rounds=5)
# 预测测试集
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration_)
# 模型评估
print('Accuracy of lightgbm:', accuracy_score(y_test, y_pred))
lgb.plot_importance(gbm)
plt.show();
可以看到,LightGBM的分类准确率达到了100%,对比XGBoost,效果要更好。
CatBoost
CatBoost因能够高效处理数据中的类别特征而闻名,CatBoost的意思为Categorical+Boosting。
CatBoost通过对常规的目标变量统计方法添加先验项来改进它们。除此之外,CatBoost还考虑使用类别特征的不同组合来增加数据集特征维度。
目标变量统计
CatBoost算法设计一个最大的目的就是更好地处理GBDT特征中的类别特征。目标变量统计(target statistics,TS),计算每个类别对于目标变量的期望值并将类别特征转换为新的数值特征。常规的TS方法最直接的做法是对类别对应的标签平均值进行替换。在GBDT构建决策树的过程中,替换后的类别标签平均值作为结点分裂的标准,这种做法也称为greedy target-based statistics,简称greedy TS.
greedy TS一个比较明显的缺陷是当特征比标签包含更多的信息时,统一用标签平均值来代替分类特征表达的话,训练集和测试集可能会因为数据分布不一样而产生条件偏移问题。
CatBoost对greedy TS方法的改进是添加先验项,用以减少噪声和低频类别数据对数据分布的影响。
特征组合
CatBoost的另外一项重要实现是将不同类别型特征的组合作为新的特征,以获得高阶依赖。然而,组合的数量会随着数据集中类别型特征的数量成指数增长,因此在算法中考虑所有组合是不现实的。为当前树构造新的分割点时,CatBoost会采用贪心的策略考虑特征之间的组合。对于树的第一次分割,不考虑任何组合。对于下一个分割,CatBoost将当前树的所有组合、类别型特征与数据集中的所有类别型特征相结合,并将新的组合类别型特征动态地转换为数值型特征。
排序提升算法
CatBoost使用排序提升方法解决预测偏移问题。所谓预测偏移问题,即训练样本的分布于测试样本的分布之间产生的偏移。
CatBoost实战
CatBoost实现样例
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
import catboost as cb
from sklearn.metrics import accuracy_score
# 读取数据
data = pd.read_csv('./adult.data', header=None)
# 变量重命名
data.columns = ['age', 'workclass', 'fnlwgt', 'education', 'education-num', 'marital-status', 'occupation',
'relationship', 'race', 'sex', 'capital-gain', 'capital-loss', 'hours-per-week', 'native-country', 'income']
# 标签转换
data['income'] = data['income'].astype("category").cat.codes
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(data.drop(['income'], axis=1), data['income'],
random_state=10, test_size=0.3)
# 配置训练参数
clf = cb.CatBoostClassifier(eval_metric="AUC", depth=4, iterations=500, l2_leaf_reg=1,
learning_rate=0.1)
# 类别特征索引
cat_features_index = [1, 3, 5, 6, 7, 8, 9, 13]
# 训练
clf.fit(X_train, y_train, cat_features=cat_features_index)
# 预测
y_pred = clf.predict(X_test)
# 测试集上的分类准确率
print('Accuracy of catboost:', accuracy_score(y_test, y_pred))
首先读入数据集,并对变量按照给定名词进行重命名,对标签变量进行类别编码,将数据集划分为训练集和测试集。然后创建CatBoost模型实例,并设置类别特征索引。最后执行训练并测试分类准确率。可以看到在测试集上的分类准确率达到了0.87.
以上就是常见boosting模型的全部梳理,包含AdaBoost、GBDT、XGBoost、LightGBM和CatBoost这5种算法的原理和代码实现。但是本文的代码实现主要调用的是已经封装好的各种库,如果你想掌握更多的细节,可以参考《机器学习 公式推导与代码实现》这本书。上面提到的大部分代码都有基于NumPy的实现,这并不是重复造轮子,而是更重视机器学习算法的基本功!