Python 机器学习算法交易实用指南(三)(4)

简介: Python 机器学习算法交易实用指南(三)

Python 机器学习算法交易实用指南(三)(3)https://developer.aliyun.com/article/1523356

随机森林

决策树不仅因其透明度和可解释性而有用,而且是更强大的集成模型的基本构建模块,它将许多个体树与策略结合起来,以随机变化其设计,以解决前一节讨论的过拟合和高方差问题。

集成模型

集成学习涉及将多个机器学习模型组合成一个新模型,旨在比任何单个模型做出更好的预测。更具体地说,一个集成将使用一个或多个给定的学习算法训练的多个基础估计器的预测整合起来,以减少这些模型可能单独产生的泛化错误。

要使集成学习实现这个目标,个体模型必须是:

  • **准确性:**它们胜过一个天真的基准(例如样本均值或类比例)
  • **独立:**它们的预测是通过不同方式生成的,以产生不同的错误

集成方法是最成功的机器学习算法之一,特别适用于标准的数值数据。大型集成在机器学习竞赛中非常成功,可能由许多不同的个体模型组成,这些模型通过手工或使用另一个机器学习算法组合在一起。

将不同模型的预测结合起来有几个缺点。这些包括降低的可解释性,以及训练、预测和模型维护的更高复杂性和成本。因此,在实践中(除了竞赛之外),从大规模集成中获得的小幅准确性增益可能不值得增加的成本。

根据它们如何优化组成模型并将结果整合为单个集成预测,通常可以区分两组集成方法:

  • 平均方法独立训练几个基础估计器,然后对它们的预测进行平均。如果基础模型没有偏差并且产生的不高度相关的不同预测错误,那么组合预测可能具有更低的方差,并且可能更可靠。这类似于从具有不相关回报的资产构建投资组合,以减少波动性而不牺牲回报。
  • 提升方法相反,是按顺序训练基础估计器,其特定目标是减少组合估计器的偏差。其动机是将几个弱模型组合成一个强大的集合。

在本章剩余部分,我们将专注于自动平均方法,并在第十一章中讨论梯度提升机的提升方法。

如何降低模型方差

我们发现决策树可能会由于方差较高而做出不良预测,这意味着树结构对训练样本的组成非常敏感。我们还看到低方差的模型,例如线性回归,尽管给定特征数量足够的样本不同,但产生的估计相似。

对于给定的一组独立观察结果,每个都具有σ²的方差,样本均值的标准误差由σ/n给出。 换句话说,对更多的观察结果进行平均会减少方差。 因此,减少模型方差和其泛化误差的自然方法将是从总体中收集许多训练集,对每个数据集训练不同的模型,并对产生的预测进行平均。

在实践中,我们通常没有许多不同的训练集的奢侈条件。 这就是装袋,即自助聚合的缩写,发挥作用的地方。 装袋是减少机器学习模型方差的通用方法,特别是在应用于决策树时,特别有用和流行。

袋装是指对自助采样进行聚合,自助采样是带替换的随机样本。 这样的随机样本与原始数据集具有相同数量的观察结果,但可能由于替换而包含重复项。

装袋提高了预测准确性,但降低了模型的可解释性,因为不再可能可视化树来理解每个特征的重要性。 作为一种集成算法,装袋方法对这些自助采样训练给定数量的基本估计器,然后将它们的预测聚合成最终的集合预测。

装袋通过随机化基本估计器的方法,例如,每棵树的生长方式,然后对预测进行平均,从而减少它们的泛化误差,从而降低了基本估计器的方差。 它通常是改进给定模型的直接方法,而无需更改底层算法。 它在具有低偏差和高方差的复杂模型中效果最好,例如深度决策树,因为它的目标是限制过拟合。 相比之下,提升方法最适合弱模型,例如浅决策树。

有几种装袋方法,它们的不同之处在于它们对训练集应用的随机抽样过程:

  • 粘贴从训练数据中抽取随机样本而不进行替换,而装袋则进行替换
  • 随机子空间无需替换地从特征(即列)中随机抽样
  • 随机补丁通过随机抽样观察和特征来训练基本估计器

袋装决策树

要将装袋应用于决策树,我们从训练数据中创建自助样本,通过反复采样来训练一个决策树,然后在这些样本中的每一个上创建一个决策树,通过对不同树的预测进行平均来创建一个集合预测。

袋装决策树通常生长较大,即具有许多层和叶子节点,并且不进行修剪,以使每棵树具有低偏差但高方差。 然后,平均它们的预测的效果旨在减少它们的方差。 研究表明,通过构建组合了数百甚至数千棵树的集合的装袋,可以显着提高预测性能。

为了说明装袋对回归树方差的影响,我们可以使用sklearn提供的BaggingRegressor元估计器。它基于指定抽样策略的参数来训练用户定义的基估计器:

  • max_samplesmax_features控制从行和列中抽取的子集的大小,分别
  • bootstrapbootstrap_features确定每个样本是有放回还是无放回抽样

以下示例使用指数函数生成单个DecisionTreeRegressor和包含十棵树的BaggingRegressor集成的训练样本,每棵树都生长十层深。这两个模型都是在随机样本上训练的,并为添加了噪声的实际函数预测结果。

由于我们知道真实函数,我们可以将均方误差分解为偏差、方差和噪声,并根据以下分解比较两个模型的这些分量的相对大小:


对于分别包含 250 和 500 个观测值的 100 个重复随机训练和测试样本,我们发现单个决策树的预测方差几乎是基于自举样本的10个装袋树的预测方差的两倍:

noise = .5  # noise relative to std(y)
noise = y.std() * noise_to_signal
X_test = choice(x, size=test_size, replace=False)
max_depth = 10
n_estimators=10
tree = DecisionTreeRegressor(max_depth=max_depth)
bagged_tree = BaggingRegressor(base_estimator=tree, n_estimators=n_estimators)
learners = {'Decision Tree': tree, 'Bagging Regressor': bagged_tree}
predictions = {k: pd.DataFrame() for k, v in learners.items()}
for i in range(reps):
    X_train = choice(x, train_size)
    y_train = f(X_train) + normal(scale=noise, size=train_size)
    for label, learner in learners.items():
        learner.fit(X=X_train.reshape(-1, 1), y=y_train)
        preds = pd.DataFrame({i: learner.predict(X_test.reshape(-1, 1))}, index=X_test)
        predictions[label] = pd.concat([predictions[label], preds], axis=1)

对于每个模型,以下图表显示了上半部分的平均预测值和平均值周围两个标准差的带状区域,以及下半部分基于真实函数值的偏差-方差-噪声分解:


查看笔记本random_forest以获取实现细节。

如何构建随机森林

随机森林算法通过对由装袋生成的自举样本引入的随机化进行扩展,进一步减少方差并提高预测性能。

除了对每个集成成员使用自举训练数据外,随机森林还会对模型中使用的特征进行随机抽样(不重复)。根据实现方式,随机样本可以针对每棵树或每次分裂进行抽取。因此,算法在学习新规则时面临不同选择,无论是在树的级别上还是每次分裂时。

特征样本的大小对回归和分类树有所不同:

  • 对于分类,样本量通常是特征数量的平方根。
  • 对于回归,可以选择从三分之一到所有特征,并应基于交叉验证进行选择。

以下图示说明了随机森林如何随机化单个树的训练,然后将它们的预测聚合成一个集成预测:


除了训练观察值之外,随机化特征的目标是进一步去相关化个体树的预测误差。并非所有特征都是平等的,少量高度相关的特征将在树构建过程中更频繁和更早地被选择,使得决策树在整个集合中更相似。然而,个体树的泛化误差越不相关,整体方差就会减少。

如何训练和调整随机森林

关键的配置参数包括在如何调整超参数部分介绍的各个决策树的各种超参数。以下表格列出了两个RandomForest类的附加选项:

关键字 默认 描述
bootstrap True 训练期间使用自举样本。
n_estimators 10 森林中的树的数量。
oob_score False 使用包外样本来估计在未见数据上的 R²。

bootstrap参数激活了前面提到的装袋算法大纲,这反过来又启用了包外分数(oob_score)的计算,该分数使用未包含在用于训练给定树的自举样本中的样本来估计泛化准确度(有关详细信息,请参见下一节)。

n_estimators参数定义了要作为森林一部分生长的树的数量。更大的森林表现更好,但构建时间也更长。重要的是监视交叉验证错误作为基本学习者数量的函数,以确定预测误差的边际减少何时下降,以及额外训练的成本开始超过收益。

max_features参数控制在学习新的决策规则并分裂节点时可用的随机选择特征子集的大小。较低的值会降低树的相关性,从而降低集成的方差,但可能会增加偏差。对于回归问题,良好的起始值是n_features(训练特征的数量),对于分类问题是sqrt(n_features),但取决于特征之间的关系,并且应该使用交叉验证进行优化。

随机森林设计为包含深度完全生长的树,可以使用max_depth=Nonemin_samples_split=2来创建。然而,这些值未必是最优的,特别是对于具有许多样本和因此可能非常深的树的高维数据,这可能会导致非常计算密集和内存密集的情况。

sklearn 提供的 RandomForest 类支持并行训练和预测,通过将 n_jobs 参数设置为要在不同核心上运行的 k 个作业数来实现。值 -1 使用所有可用核心。进程间通信的开销可能限制速度提升的线性,因此 k 个作业可能需要超过单个作业的 1/k 时间。尽管如此,在数据庞大且分割评估变得昂贵时,对于大型森林或深度个体树,速度提升通常相当显著,并且可能需要训练相当长时间。

如常,应使用交叉验证来确定最佳参数配置。以下步骤说明了该过程:

  1. 我们将使用 GridSearchCV 来识别一组最佳参数,以用于分类树的集成:
rf_clf = RandomForestClassifier(n_estimators=10,
                                criterion='gini',
                                max_depth=None,
                                min_samples_split=2,
                                min_samples_leaf=1,
                                min_weight_fraction_leaf=0.0,
                                max_features='auto',
                                max_leaf_nodes=None,
                                min_impurity_decrease=0.0,
                                min_impurity_split=None,
                                bootstrap=True, oob_score=False,
                                n_jobs=-1, random_state=42)
  1. 我们将使用 10 倍自定义交叉验证,并为关键配置设置的值填充参数网格:
cv = OneStepTimeSeriesSplit(n_splits=10)
clf = RandomForestClassifier(random_state=42, n_jobs=-1)
param_grid = {'n_estimators': [200, 400],
              'max_depth': [10, 15, 20],
              'min_samples_leaf': [50, 100]}
  1. 使用前述输入配置 GridSearchCV
gridsearch_clf = GridSearchCV(estimator=clf,
                          param_grid=param_grid,
                          scoring='roc_auc',
                          n_jobs=-1,
                          cv=cv,
                          refit=True,
                          return_train_score=True,
                          verbose=1)
  1. 训练由参数网格定义的多个集成模型:
gridsearch_clf.fit(X=X, y=y_binary)
  1. 获得最佳参数如下:
gridsearch_clf.bestparams{'max_depth': 15,
 'min_samples_leaf': 100,
 'n_estimators': 400}
  1. 最佳分数比单棵树基线略有提高,但具有显著改进:
gridsearch_clf.bestscore_0.6013

随机森林的特征重要性

随机森林集成可能包含数百棵独立的树,但仍然可以从装袋模型中获得关于特征重要性的整体摘要度量。

对于给定的特征,重要性得分是基于该特征进行分割导致的目标函数值的总减少量,平均分布在所有树上。由于目标函数考虑了分割影响的特征数量,所以这个度量隐含地是加权平均的,因此在树的顶部附近使用的特征会由于较少的可用节点中包含的观察次数较多而获得更高的得分。通过对以随机方式生长的许多树进行平均,特征重要性估计失去了一些变化,并且变得更加准确。

基于用于学习决策规则的不同目标,分类和回归树的计算有所不同,分别以回归树的均方误差和分类树的基尼指数或熵来衡量。

sklearn 进一步规范化特征重要性度量,使其总和为 1。因此,计算得出的特征重要性也用作特征选择的一种替代方法,而不是我们在 第六章 中看到的互信息度量(见 sklearn.feature_selection 模块中的 SelectFromModel)。

在我们的示例中,前 20 个特征的重要性值如下所示:


特征重要性值

包外测试

随机森林提供了内置的交叉验证优势,因为每个树都是在训练数据的自助版本上训练的。因此,每棵树平均使用了仅有的两三分之一可用的观测结果。要了解原因,请考虑自助样本的大小与原始样本的大小相同,每个观测结果被抽取的概率为 1/n。因此,不进入自助样本的概率是(1-1/n)^n,它收敛(迅速)到 1/e,或者大约三分之一。

在用于生长袋装树的训练集中未包括的剩余三分之一的观察结果被称为袋外袋OOB)观察结果,并且可以作为验证集。正如交叉验证一样,我们对于每棵树都预测未使用此观察结果构建的 OOB 样本的响应,然后对每个 OOB 样本进行单个集合预测的平均预测响应(如果目标是回归)或采取多数投票或预测概率(如果目标是分类)。这些预测产生了关于泛化误差的无偏估计,方便在训练期间计算。

由于预测是在不考虑此观察结果的情况下学习的决策规则产生的,因此结果的 OOB 误差是该观察结果的泛化误差的有效估计。一旦随机森林足够大,OOB 误差就会接近留一交叉验证误差。对于大型数据集,OOB 方法估计测试误差非常高效,而交叉验证可能计算成本高昂。

随机森林的优缺点

袋装集成模型既有优势又有劣势。随机森林的优势包括:

  • 预测性能可以与最佳监督学习算法竞争
  • 它们提供可靠的特征重要性估计
  • 它们提供了测试错误的有效估计,而不需要承担与交叉验证相关的重复模型训练的成本

另一方面,随机森林也有一些缺点:

  • 集合模型本质上比单个决策树不太可解释
  • 训练大量深树可能具有高计算成本(但可以并行化)并且使用大量内存
  • 预测速度较慢,这可能对需要低延迟的应用程序造成挑战

摘要

在本章中,我们学习了一种能够捕捉非线性关系的新模型类,与我们迄今探索过的经典线性模型形成对比。我们看到决策树是如何学习规则来将特征空间划分为产生预测的区域,从而将输入数据分割成特定区域的。

决策树非常有用,因为它们提供了有关特征和目标变量之间关系的独特见解,我们看到了如何可视化树结构中编码的一系列决策规则。

不幸的是,决策树容易过拟合。我们了解到集成模型和自举聚合方法设法克服了决策树的一些缺点,并使它们成为更强大的复合模型的组成部分。

在下一章中,我们将探讨另一个集成模型,它已经被认为是最重要的机器学习算法之一。

第十一章:梯度提升机

在上一章中,我们了解到随机森林通过将它们组合成一个减少个体树高方差的集合来提高个别决策树的预测。随机森林使用装袋(bootstrap aggregation)来将随机元素引入到生长个别树的过程中。

更具体地说,装袋(bagging)从数据中有替换地抽取样本,以便每棵树都在不同但大小相等的数据随机子集上进行训练(其中一些观测重复)。随机森林还随机选择一些特征子集,以便用于训练每棵树的数据的行和列都是原始数据的随机版本。然后,集成通过对个别树的输出进行平均来生成预测。

个别树通常生长较深,以确保低偏差,同时依赖随机化训练过程来产生不同的、不相关的预测错误,这些错误在聚合时具有较低的方差,比个体树的预测更可靠。换句话说,随机化训练旨在使个别树产生的错误彼此不相关或多样化,使集合对过拟合的敏感性大大降低,具有较低的方差,并且对新数据的泛化能力更好。

在本章中,我们将探讨提升(boosting)这种替代机器学习ML)算法,用于决策树集成,通常能够产生更好的结果。其关键区别在于,提升根据模型在添加新树之前累积的错误来修改用于训练每棵树的数据。与随机森林不同,随机森林独立地使用训练集的不同版本训练许多树,而提升则使用重加权版本的数据进行顺序处理。最先进的提升实现也采用了随机森林的随机化策略。

在本章中,我们将看到提升是如何在过去三十年中演变成最成功的 ML 算法之一的。在撰写本文时,它已经成为结构化数据的机器学习竞赛中的主导者(例如,与高维图像或语音不同,在这些领域中输入和输出之间的关系更加复杂,深度学习表现出色)。具体来说,本章将涵盖以下主题:

  • 提升的工作原理以及与装袋的比较
  • 如何从自适应提升到梯度提升的演变
  • 如何使用 sklearn 使用和调整 AdaBoost 和梯度提升模型
  • 最先进的 GBM 实现如何大幅加速计算
  • 如何防止梯度提升模型过拟合
  • 如何使用xgboostlightgbmcatboost构建、调整和评估大型数据集上的梯度提升模型
  • 如何解释并从梯度提升模型中获得洞见

自适应提升

像装袋一样,增强是一种集成学习算法,它将基本学习器(通常是决策树)组合成一个集成。增强最初是为分类问题开发的,但也可用于回归,并且已被称为过去 20 年中引入的最有效的学习思想之一(如 Trevor Hastie 等人所述的《统计学习要素》;请查看 GitHub 获取参考链接)。与装袋类似,它是一种通用方法或元方法,可应用于许多统计学习模型。

Boosting 的发展动机是寻找一种方法来将许多 模型的输出(当一个预测器仅比随机猜测稍好时,称为弱预测器)结合成更强大的,即增强的联合预测。一般来说,Boosting 学习一种形式类似于线性回归的可加假设,H[M]。然而,现在求和的每个元素 m= 1,…, M 都是一个称为 h[t] 的弱基本学习器,它本身需要训练。下面的公式总结了这种方法:


正如上一章所讨论的那样,装袋在不同的训练数据随机样本上训练基本学习器。相比之下,增强是通过在数据上顺序训练基本学习器,该数据反复修改以反映累积学习结果。目标是确保下一个基本学习器补偿当前集合的缺陷。我们将在本章中看到,增强算法在定义缺陷的方式上有所不同。集合使用弱模型的预测的加权平均值进行预测。

第一个带有数学证明,证明它增强了弱学习器性能的提升算法是由罗伯特·舍皮尔(Robert Schapire)和约阿夫·弗洛伊德(Yoav Freund)在 1990 年左右开发的。1997 年,作为分类问题的一个实际解决方案出现了自适应增强AdaBoost)算法,该算法于 2003 年获得了哥德尔奖。大约又过了五年,该算法在连续梯度下降的方法上得到了推广,利奥·布雷曼(发明了随机森林)将该方法与梯度下降相连接,杰罗姆·弗里德曼在 1999 年提出了梯度增强算法。近年来,出现了许多优化实现,如 XGBoost、LightGBM 和 CatBoost,这些实现牢固地确立了梯度增强作为结构化数据的首选解决方案。

在接下来的几节中,我们将简要介绍 AdaBoost,然后专注于梯度增强模型,以及这个非常强大和灵活的算法的几种最新实现。

AdaBoost 算法

AdaBoost 是第一个在拟合额外集成成员时迭代地适应累积学习进展的增强算法。特别地,AdaBoost 在拟合新的弱学习器之前,会根据当前集成在训练集上的累积错误改变训练数据的权重。AdaBoost 当时是最准确的分类算法,Leo Breiman 在 1996 年的 NIPS 会议上称其为世界上最好的现成分类器。

该算法对 ML 产生了非常重要的影响,因为它提供了理论上的性能保证。这些保证只需要足够的数据和一个可靠地预测略好于随机猜测的弱学习器。由于这种分阶段学习的自适应方法,开发准确的 ML 模型不再需要在整个特征空间上准确地表现。相反,模型的设计可以集中于找到仅仅优于抛硬币的弱学习器。

AdaBoost 与 bagging 有很大的不同,后者构建了在非常深的树上的集成以减少偏差。相比之下,AdaBoost 使用浅树作为弱学习器,通常通过使用树桩来获得更高的准确性,即由单一分裂形成的树。该算法从一个等权重的训练集开始,然后逐步改变样本分布。每次迭代后,AdaBoost 增加被错误分类的观测值的权重,并减少正确预测样本的权重,以便随后的弱学习器更多地关注特别困难的案例。一旦训练完成,新的决策树就会以反映其减少训练错误的贡献的权重被纳入集成中。

对于预测离散类别的一组基本学习器的 AdaBoost 算法,hmm=1, …, M,以及N个训练观测值,可以总结如下:

  1. 对于观测值 i=1, …, N,初始化样本权重 w[i]=1/N
  2. 对于每个基本分类器h[m]m=1, …, M,执行以下操作:
  1. w[i] 加权训练数据来拟合 hm
  2. 计算训练集上基本学习器的加权错误率 ε[m ]。
  3. 计算基本学习器的集成权重 α[m],作为其错误率的函数,如下式所示:


  1. 根据 w[i ] exp(α[m]**)* 更新错误分类样本的权重。
  1. 当集成成员的加权和为正时,预测正类;否则,预测负类,如下式所示:


AdaBoost 具有许多实际优势,包括易于实现和快速计算,它可以与任何弱学习器识别方法结合使用。除了集成的大小之外,没有需要调整的超参数。AdaBoost 还对识别异常值很有用,因为接收最高权重的样本是那些一直被错误分类且固有模糊的样本,这也是异常值的典型特征。

另一方面,AdaBoost 在给定数据集上的性能取决于弱学习器充分捕捉特征与结果之间关系的能力。正如理论所示,当数据不足或集成成员的复杂性与数据的复杂性不匹配时,提升将无法表现良好。它也容易受到数据中的噪声影响。

使用 sklearn 的 AdaBoost

作为其集成模块的一部分,sklearn 提供了一个支持两个或多个类的AdaBoostClassifier实现。本节的代码示例位于笔记本gbm_baseline中,该笔记本将各种算法的性能与始终预测最频繁类别的虚拟分类器进行比较。

我们首先需要将base_estimator定义为所有集成成员的模板,然后配置集成本身。我们将使用默认的DecisionTreeClassifier,其中max_depth=1——即一个只有一个分割的树桩。基本估算器的复杂性是关键调参参数,因为它取决于数据的性质。正如前一章所示,对于max_depth的更改应与适当的正则化约束结合使用,例如通过调整min_samples_split来实现,如下代码所示:

base_estimator = DecisionTreeClassifier(criterion='gini', 
                                        splitter='best',
                                        max_depth=1, 
                                        min_samples_split=2, 
                                        min_samples_leaf=20, 
                                        min_weight_fraction_leaf=0.0,
                                        max_features=None, 
                                        random_state=None, 
                                        max_leaf_nodes=None, 
                                        min_impurity_decrease=0.0, 
                                        min_impurity_split=None)

在第二步中,我们将设计集成。n_estimators参数控制弱学习器的数量,learning_rate确定每个弱学习器的贡献,如下代码所示。默认情况下,弱学习器是决策树树桩:

ada_clf = AdaBoostClassifier(base_estimator=base_estimator,
                             n_estimators=200,
                             learning_rate=1.0,
                             algorithm='SAMME.R',
                             random_state=42)

负责良好结果的主要调参参数是n_estimators和基本估算器的复杂性,因为树的深度控制了特征之间的相互作用程度。

我们将使用自定义的 12 折滚动时间序列拆分来交叉验证 AdaBoost 集成,以预测样本中最后 12 个月的 1 个月预测,使用所有可用的先前数据进行训练,如下代码所示:

cv = OneStepTimeSeriesSplit(n_splits=12, test_period_length=1, shuffle=True)
def run_cv(clf, X=X_dummies, y=y, metrics=metrics, cv=cv, fit_params=None):
    return cross_validate(estimator=clf,
                          X=X,
                          y=y,
                          scoring=list(metrics.keys()),
                          cv=cv,
                          return_train_score=True,
                          n_jobs=-1,                    # use all cores
                          verbose=1,
                          fit_params=fit_params)

结果显示加权测试准确度为 0.62,测试 AUC 为 0.6665,负对数损失为-0.6923,以及测试 F1 得分为 0.5876,如下截图所示:


有关交叉验证代码和处理结果的其他详细信息,请参阅配套笔记本。

梯度提升机

AdaBoost 也可以解释为在每次迭代m时通过逐步前向方法最小化指数损失函数来识别与对应权重*α[m]的新基学习器h[m]*添加到集成中,如下式所示:


对 AdaBoost 算法的这种解释是在其发表几年后才发现的。它将 AdaBoost 视为一种基于坐标的梯度下降算法,该算法最小化了特定的损失函数,即指数损失。

梯度提升(Gradient boosting)利用这一洞见,并将提升方法应用于更广泛的损失函数。该方法使得设计机器学习算法以解决任何回归、分类或排名问题成为可能,只要它可以使用可微分的损失函数来表述,并因此具有梯度。定制这一通用方法以适应许多特定预测任务的灵活性对于提升方法的普及至关重要。

梯度提升机GBM)算法的主要思想是训练基学习器来学习集成当前损失函数的负梯度。因此,每次添加到集成中都直接有助于减少由先前集成成员产生的总体训练误差。由于每个新成员代表数据的新函数,因此也可以说梯度提升是以加法方式优化*h[m]*函数。

简而言之,该算法逐步将弱学习器h[m](如决策树)拟合到当前集成的损失函数的负梯度上,如下式所示:


换句话说,在给定迭代m时,该算法计算每个观察值的当前损失的梯度,然后将回归树拟合到这些伪残差上。在第二步中,它确定每个终端节点的最佳常数预测,该预测最小化了将该新学习器添加到集成中产生的增量损失。

这与独立决策树和随机森林不同,其中预测取决于相关终端或叶节点中的训练样本的结果值:在回归的情况下是它们的平均值,或者在二元分类的情况下是正类的频率。对损失函数梯度的关注也意味着梯度提升使用回归树来学习回归和分类规则,因为梯度始终是连续函数。

最终集成模型根据个别决策树预测的加权和进行预测,每个决策树都已经训练以在给定一组特征值的情况下最小化集成损失,如下图所示:


梯度提升树在许多分类、回归和排名基准上表现出最先进的性能。它们可能是最受欢迎的集成学习算法,既作为多样化的机器学习竞赛中的独立预测器,也作为实际生产流水线中的一部分,例如,用于预测在线广告的点击率。

梯度提升成功的基础是其以增量方式学习复杂的函数关系的能力。该算法的灵活性需要通过调整约束模型固有倾向于学习训练数据中的噪声而不是信号的超参数来仔细管理过拟合的风险。

我们将介绍控制梯度提升树模型复杂性的关键机制,然后使用 sklearn 实现来说明模型调优。

如何训练和调优 GBM 模型

梯度提升性能的两个关键驱动因素是集成大小和其组成决策树的复杂性。

决策树的复杂性控制旨在避免学习高度具体的规则,这些规则通常意味着叶节点中的样本数量非常少。我们在前一章中介绍了用于限制决策树过拟合到训练数据的最有效约束条件。它们包括要求:

  • 要么分割节点或接受它作为终端节点的最小样本数,或
  • 最小改进节点质量,由纯度或熵或均方误差衡量,对于回归情况而言。

除了直接控制集成大小外,还有各种正则化技术,例如收缩,在第七章 线性模型 中我们遇到的 Ridge 和 Lasso 线性回归模型的上下文中。此外,在随机森林上下文中使用的随机化技术也常用于梯度提升机。

集成大小和提前停止

每次提升迭代旨在减少训练损失,使得对于一个大集成,训练误差可能变得非常小,增加过拟合的风险并在未见数据上表现不佳。交叉验证是找到最小化泛化误差的最佳集成大小的最佳方法,因为它取决于应用程序和可用数据。

由于集成大小需要在训练之前指定,因此监控验证集上的性能并在给定迭代次数时中止训练过程是有用的,当验证误差不再下降时。这种技术称为提前停止,经常用于需要大量迭代并且容易过拟合的模型,包括深度神经网络。

请记住,对于大量试验使用相同的验证集进行早停会导致过拟合,只是过拟合于特定的验证集而不是训练集。最好避免在开发交易策略时运行大量实验,因为误发现的风险显著增加。无论如何,保留一个留置集以获取对泛化误差的无偏估计。

收缩和学习率

收缩技术通过对模型损失函数增加惩罚来应用于增加的模型复杂性。对于提升集合,收缩可以通过将每个新集合成员的贡献缩小一个介于 0 和 1 之间的因子来应用。这个因子被称为提升集合的学习率。降低学习率会增加收缩,因为它降低了每个新决策树对集合的贡献。

学习率与集合大小具有相反的效果,后者往往对较低的学习率增加。较低的学习率与较大的集合结合在一起,已被发现可以降低测试误差,特别是对于回归和概率估计。大量迭代在计算上更昂贵,但通常对于快速的最新实现是可行的,只要个别树保持浅层。根据实现方式,您还可以使用自适应学习率,它会根据迭代次数调整,通常降低后期添加的树的影响。我们将在本章后面看到一些示例。

子抽样和随机梯度提升

如前一章节详细讨论的,自助平均法(bagging)提高了原本噪声分类器的性能。

随机梯度提升在每次迭代中使用无替换抽样,以在训练样本的子集上生成下一棵树。好处是降低计算量,通常可以获得更好的准确性,但子抽样应与收缩结合使用。

正如您所见,超参数的数量不断增加,从而增加了在有限的训练数据上从大量参数试验中选择最佳模型时出现假阳性的风险。最好的方法是依次进行并逐个选择参数值,或者使用低基数子集的组合。

如何在 sklearn 中使用梯度提升

sklearn 的集合模块包含了梯度提升树的实现,用于回归和分类,二元和多类。以下 GradientBoostingClassifier 初始化代码展示了我们之前介绍的关键调整参数,除了我们从独立决策树模型中了解的那些外。笔记本 gbm_tuning_with_sklearn 包含了本节的代码示例。

可用的损失函数包括导致 AdaBoost 算法的指数损失和对应于概率输出的 logistic 回归的 deviance。friedman_mse 节点质量度量是平均平方误差的变化,其中包括一个改进分数(请参阅 GitHub 引用以获取原始论文链接),如以下代码所示:

gb_clf = GradientBoostingClassifier(loss='deviance',                # deviance = logistic reg; exponential: AdaBoost
                                    learning_rate=0.1,              # shrinks the contribution of each tree
                                    n_estimators=100,               # number of boosting stages
                                    subsample=1.0,                  # fraction of samples used t fit base learners
                                    criterion='friedman_mse',       # measures the quality of a split
                                    min_samples_split=2,            
                                    min_samples_leaf=1, 
                                    min_weight_fraction_leaf=0.0,   # min. fraction of sum of weights
                                    max_depth=3,                    # opt value depends on interaction
                                    min_impurity_decrease=0.0, 
                                    min_impurity_split=None, 
                                    max_features=None, 
                                    max_leaf_nodes=None, 
                                    warm_start=False, 
                                    presort='auto',
                                    validation_fraction=0.1, 
                                    tol=0.0001)

AdaBoostClassifier 类似,该模型无法处理缺失值。我们将再次使用 12 折交叉验证来获取用于分类滚动 1 个月持有期的方向回报的错误,如下图所示:

gb_cv_result = run_cv(gb_clf, y=y_clean, X=X_dummies_clean)
gb_result = stack_results(gb_cv_result)

我们将解析和绘制结果,以发现与 AdaBoostClassifier 相比略有改进的情况,如下图所示:


如何使用 GridSearchCV 调整参数

model_selection 模块中的 GridSearchCV 类促进了对我们想要测试的所有超参数值组合的系统评估。在下面的代码中,我们将说明此功能,用于七个调整参数,当定义时将产生总共 2⁴ x 3² x 4 = 576 种不同的模型配置:

cv = OneStepTimeSeriesSplit(n_splits=12)
param_grid = dict(
        n_estimators=[100, 300],
        learning_rate=[.01, .1, .2],
        max_depth=list(range(3, 13, 3)),
        subsample=[.8, 1],
        min_samples_split=[10, 50],
        min_impurity_decrease=[0, .01],
        max_features=['sqrt', .8, 1]
)

.fit() 方法使用自定义的 OneStepTimeSeriesSplitroc_auc 分数执行交叉验证以评估 12 折。Sklearn 允许我们使用 joblib pickle 实现持久化结果,就像对任何其他模型一样,如以下代码所示:

gs = GridSearchCV(gb_clf,
                   param_grid,
                   cv=cv,
                   scoring='roc_auc',
                   verbose=3,
                   n_jobs=-1,
                   return_train_score=True)
gs.fit(X=X, y=y)
# persist result using joblib for more efficient storage of large numpy arrays
joblib.dump(gs, 'gbm_gridsearch.joblib')

GridSearchCV 对象在完成后具有几个额外的属性,我们可以在加载拾取的结果后访问,以了解哪种超参数组合效果最佳以及其平均交叉验证 AUC 分数,这导致了比默认值略有改进。以下代码显示了这一点:

pd.Series(gridsearch_result.best_params_)
learning_rate              0.01
max_depth                  9.00
max_features               1.00
min_impurity_decrease      0.01
min_samples_split         10.00
n_estimators             300.00
subsample                  0.80
gridsearch_result.best_score_
0.6853

参数对测试分数的影响

GridSearchCV 结果存储了平均交叉验证分数,以便我们分析不同的超参数设置如何影响结果。

下图左侧面板中的六个 seaborn swarm 图显示了所有参数值的 AUC 测试分数分布。在这种情况下,最高的 AUC 测试分数需要较低的 learning_rate 和较大的 max_features 值。某些参数设置,例如较低的 learning_rate,会产生一系列取决于其他参数互补设置的结果范围。其他参数与实验中使用的所有设置的高分数兼容:


我们现在将探讨超参数设置如何共同影响平均交叉验证分数。 为了深入了解参数设置是如何相互作用的,我们可以使用DecisionTreeRegressor训练,将平均测试分数作为结果,将参数设置编码为一组独热或虚拟格式的分类变量(详情请参阅笔记本)。 树结构突显了使用所有特征(max_features_1),低learning_ratemax_depth超过 3 导致了最佳结果,如下图所示:


在本节第一个图表的右侧面板中的条形图显示了超参数设置对产生不同结果的影响,其特征重要性由决策树产生,决策树生长到其最大深度为止。 自然,出现在树顶部附近的特征也累积了最高的重要性得分。

如何在留置集上进行测试

最后,我们希望评估我们从GridSearchCV练习中排除的留置集上最佳模型的性能。 它包含了样本期的最后六个月(截至 2018 年 2 月;详情请参阅笔记本)。 我们基于 AUC 分数0.6622获得了一个泛化性能估计,使用以下代码:

best_model = gridsearch_result.best_estimator_
preds= best_model.predict(test_feature_data)
roc_auc_score(y_true=test_target, y_score=preds)
0.6622

sklearn 梯度提升实现的缺点是计算速度有限,这使得快速尝试不同的超参数设置变得困难。 在接下来的部分中,我们将看到在过去的几年中出现了几种优化实现,大大减少了训练甚至大规模模型所需的时间,并且极大地扩展了这种高效算法的应用范围。

快速可扩展的 GBM 实现

过去几年中,出现了几种新的梯度提升实现,采用了各种创新,加速了训练,提高了资源效率,并允许算法扩展到非常大的数据集。 新的实现及其来源如下:

  • XGBoost(极端梯度提升),2014 年由华盛顿大学的 Tianqi Chen 发起
  • LightGBM,于 2017 年 1 月由 Microsoft 首次发布
  • CatBoost,于 2017 年 4 月由 Yandex 首次发布

这些创新解决了训练梯度提升模型的特定挑战(请参阅本章在 GitHub 上的README以获取详细参考)。 XGBoost 的实现是第一个获得流行的新实现:在 2015 年 Kaggle 发布的 29 个获奖解决方案中,有 17 个解决方案使用了 XGBoost。 其中 8 个仅依赖于 XGBoost,而其他的则将 XGBoost 与神经网络结合使用。

在说明其实现之前,我们将首先介绍随着时间推移而出现并最终收敛的关键创新(以便大多数功能对所有实现都可用)。

算法创新如何推动性能

随机森林可以通过在独立的自助样本上生长单独的树来并行训练。相比之下,梯度提升的顺序方法会减慢训练速度,这反过来会使得需要适应任务和数据集性质的大量超参数的实验变得更加复杂。

为了通过树扩展整体,训练算法以增量方式最小化相对于整体损失函数的负梯度的预测误差,类似于传统的梯度下降优化器。因此,在训练过程中评估每个特征对决策树与当前梯度拟合的潜在分割点的影响的计算成本与时间成正比。

Python 机器学习算法交易实用指南(三)(5)https://developer.aliyun.com/article/1523358

相关文章
|
2天前
|
机器学习/深度学习 算法 BI
机器学习笔记(一) 感知机算法 之 原理篇
机器学习笔记(一) 感知机算法 之 原理篇
|
2天前
|
存储 算法 Python
python常用算法(5)——树,二叉树与AVL树(一)
python常用算法(5)——树,二叉树与AVL树
|
1天前
|
机器学习/深度学习 算法 搜索推荐
机器学习聚类算法
聚类算法是无监督学习技术,用于发现数据集中的自然群体,如用户画像、广告推荐等。常见的聚类算法包括K-Means,它基于距离分配样本至簇,适合球形分布;层次聚类则通过合并或分裂形成簇,能发现任意形状的簇;DBSCAN依据密度来聚类,对噪声鲁棒。KMeans API中`sklearn.cluster.KMeans(n_clusters=8)`用于指定簇的数量。评估聚类效果可使用轮廓系数、SSE等指标,Elbow方法帮助选择合适的K值。
|
1天前
|
机器学习/深度学习 算法
机器学习算法决策树(二)
**ID3决策树算法**是1975年由J. Ross Quinlan提出的,它基于信息增益来选择最佳划分特征。信息增益是衡量数据集纯度变化的指标,熵则是评估数据不确定性的度量。算法通过比较每个特征的信息增益来选择分裂属性,目标是构建一个能最大化信息增益的决策树。然而,ID3容易偏向于选择具有更多特征值的属性,C4.5算法为解决这一问题引入了信息增益率,降低了这种偏好。CART决策树则不仅用于分类,也用于回归,并使用基尼指数或信息熵来选择分割点。剪枝是防止过拟合的重要手段,包括预剪枝和后剪枝策略。
|
1天前
|
机器学习/深度学习 算法 数据可视化
机器学习算法决策树(一)
**决策树模型**是一种直观的分类模型,常用于金融风控和医疗诊断等领域。它通过树形结构对数据进行划分,易于理解和解释,能揭示特征重要性且计算复杂度低。然而,模型可能过拟合,需剪枝处理;不擅长处理连续特征;预测能力有限,且对数据变化敏感。在集成学习如XGBoost中,决策树作为基模型广泛应用。示例代码展示了使用Python的`sklearn`库构建和可视化决策树的过程。
|
2天前
|
存储 算法 Shell
python常用算法(5)——树,二叉树与AVL树(三)
python常用算法(5)——树,二叉树与AVL树
|
2天前
|
算法 Python
python常用算法(5)——树,二叉树与AVL树(二)
python常用算法(5)——树,二叉树与AVL树
|
1天前
|
机器学习/深度学习 算法 数据挖掘
机器学习之聚类——MeanShift算法和图像矢量量化
机器学习之聚类——MeanShift算法和图像矢量量化
2 0
|
1天前
|
人工智能 算法 物联网
求解三维装箱问题的启发式深度优先搜索算法(python)
求解三维装箱问题的启发式深度优先搜索算法(python)
5 0
|
1天前
|
算法 Python
模拟退火算法求解TSP问题(python)
模拟退火算法求解TSP问题(python)
5 0