了解如何使用梯度增强算法(一种强大的机器学习技术)来改进您的预测模型。本教程涵盖了梯度提升的理论和实践。
这篇文章最重要的收获是,您可以非常牢固地掌握梯度提升的内部工作原理,而不会遇到太多数学难题。毕竟,梯度提升是用于实践的,而不是用于数学分析的。
Boosting 是机器学习中一种强大的集成技术。与独立从数据中学习的传统模型不同,Boosting 结合了多个弱学习器的预测来创建一个更准确的强学习器。
我刚刚写了一堆新术语,所以让我从弱学习者开始解释每个术语。
A 弱学习器是一种比随机猜测模型稍好的机器学习模型。例如,假设我们将蘑菇分为可食用和不可食用。如果随机猜测模型的准确率为 40%,那么弱学习器的准确率将略高于此:50-60%。
Boosting 将数十或数百个弱学习器组合起来,构建一个强学习器,在同一问题上有可能达到 95% 以上的准确率。
最流行的弱学习器是决策树,选择它是因为它们能够处理几乎任何数据集。如果您不熟悉决策树,请查看此 DataCamp 决策树分类教程。
梯度提升已成为机器学习的主导力量,其应用现已跨越各个行业,从预测客户流失到检测小行星。以下是其在 Kaggle 和现实世界用例中的成功故事:
称霸 Kaggle 比赛:
改变商业和工业:
那么,让我们最终一睹这个传奇算法的幕后花絮吧!
梯度增强算法适用于具有一组特征 (X) 和目标 (y) 的表格数据。与其他机器学习算法一样,其目的是从训练数据中学习足够的知识,以便很好地推广到未见过的数据点。
为了理解梯度提升的基本过程,我们将使用一个包含四行的简单销售数据集。使用三个特征——客户年龄、购买类别和购买重量,我们想要预测购买金额:
在机器学习中,损失函数是一个关键组件,可让我们量化模型预测与实际值之间的差异。本质上,它衡量模型的表现。
以下是其作用的详细说明:
两个最常见的损失函数是:
平方值乘以二分之一的原因与微分有关。当我们对该损失函数求导时,由于幂律,二分之一与平方相抵消。因此,最终结果就是-(Observed - Predicted),使数学变得更容易并且计算成本更低。
由于我们正在进行回归,因此我们将使用 MSE。
梯度提升是一种逐渐提高准确性的算法。为了开始这个过程,我们需要一个初步的猜测或预测。初始猜测始终是目标的平均值。换句话说,对于第一轮,我们的模型预测所有购买都是相同的 — 156 美元:
选择平均值的原因与我们选择的损失函数及其导数有关。每一步,我们都在寻找一个值来找到损失函数的最小值。换句话说,我们正在寻找一个使损失函数的导数(梯度)为0的值。
当我们对每个观测值相对于预测值求损失函数的导数并将它们相加时,我们最终得到目标的平均值。
因此,我们最初的预测是平均值 — 156 美元。当我们继续时,请将其牢记在心。
下一步是找出每个观测值与我们的初始预测之间的差异:156 - Observed。为了便于说明,我们将把这些差异放在一个新列中:
请记住,在线性回归中,观测值与预测值之间的差异称为残差。为了区分线性回归和梯度提升,我们将它们称为伪残差(这样命名还有其他原因,但我们不会详细讨论)它们在本文中)。
接下来,我们将构建一个决策树(弱学习器),使用我们拥有的三个特征(年龄、类别、购买重量)来预测残差。对于这个问题,我们将决策树限制为只有四个叶子(终端节点),但在实践中,人们通常选择 8 到 32 之间的叶子。
树适合数据后,我们对数据中的每一行进行预测。以下是第一个操作的方法:
下图中的一个小错误:应该写的是“预计购买金额”,而不是“预计重量”
第一行有以下特征:电子产品类别(根节点左侧)和30岁以下的客户年龄(子节点左侧)。这会将 -32.55 放入叶节点。为了做出最终的预测,我们在第一个预测中添加 -32.55,这与观测值 完全相同 - 123.45 美元!
我们刚刚做出了完美的预测,那么为什么还要费心建造其他树呢?好吧,现在我们对训练数据严重过度拟合。我们希望模型能够泛化。因此,为了缓解这个问题,梯度提升有一个称为学习率的参数。
梯度提升中的学习率只是一个介于 0 和 1 之间的乘数,用于缩放每个弱学习器的预测(有关学习率的详细信息,请参阅下面的部分)。当我们将任意学习率 0.1 添加到混合中时,我们的预测变为 152.75,而不是完美的 123.45。
我们也对第二行进行预测:
我们通过树运行该行并得到 146.08 作为预测。我们对所有行继续以这种方式进行,直到我们对四行有四个预测:152.75、146.08、174.945、150.2。现在让我们将它们添加为新列:
接下来,我们通过从购买金额中减去新的预测来找到新的伪残差。让我们将它们作为新列添加到表中并删除最后两列:
正如您所看到的,我们的新伪残差较小,这意味着我们的损失正在下降。
在接下来的步骤中,我们迭代第 3 步,即构建更多弱学习器。唯一要记住的是,我们必须不断地将每棵树的残差添加到初始预测中以生成下一棵树。
例如,如果我们构建 10 棵树,每棵树的残差表示为 r_i (1 <= i <= 10),则下一个预测将变为 p_10 = 156 + eta * (r_1 + r_2 + ... + r_10) 其中p_10表示第十轮预测。
在实践中,专业人士通常从 100 棵树开始,而不仅仅是 10 棵。在这种情况下,算法据说要训练 100 轮boosting rounds.
如果您不知道解决特定问题所需的树的确切数量,您可以使用一种称为早期停止的简单技术。
在早期停止中,我们选择大量的树,例如 1000 或 10000 棵。然后,而不是等待算法完成建造所有这些树后,我们会监控损失。如果在一定数量的 boosting 轮次(例如 50 轮)后损失没有改善,我们会提前停止训练,从而节省时间和计算资源。
在机器学习中,选择模型的设置被称为“超参数调整”。这些设置被称为“超参数”。是机器学习工程师必须自己选择的选项。与其他参数不同,模型无法仅通过数据训练来学习超参数的最佳值。
梯度增强模型有许多超参数,我将在下面概述其中一些。
该参数设置算法的方向和损失函数。如果目标是回归,则选择 MSE 作为损失函数,而对于分类,则选择交叉熵。像 XGBoost 这样的 Python 库为其他类型的任务提供了其他目标,例如使用相应的损失函数进行排名。
梯度提升最重要的超参数可能是学习率。它通过调整收缩因子来控制每个弱学习器的贡献。较小的值(接近 0)会减少每个弱学习者在整体中的发言权。这需要建造更多的树,从而需要更多的时间来完成训练。但是,最终的强学习器确实会很强大并且不会受到过度拟合的影响。
此参数也称为提升轮数或n_es timators,控制要构建的树的数量。您构建的树越多,整体就会变得越强大、性能越好。随着更多的树允许模型捕获数据中的更多模式,它也变得更加复杂。然而,更多的树会显着提高过度拟合的可能性。为了缓解这种情况,请结合使用提前停止和低学习率。
该参数控制每个弱学习器(决策树)的级别数。最大深度为 3 意味着树中有三个级别(计算叶级别)。树越深,模型就越复杂,计算成本也越高。选择接近 3 的值以防止过度拟合。最大深度应为 10。
该参数控制决策树中分支的分割方式。为终止节点(叶)中的样本数量设置较低的值会使整个算法对噪声敏感。较大的最小样本数使树更难以基于太少的数据点创建分裂,从而有助于防止过度拟合。
该参数控制用于训练每棵树的数据比例。在上面的示例中,我们使用了 100% 的行,因为数据集中只有四行。但是,现实世界的数据集通常包含更多数据并且需要采样。因此,如果将子采样率设置为低于 1 的值(例如 0.7),则每个弱学习器都会在随机采样的 70% 的行上进行训练。较小的子采样率可以导致更快的训练,但也可能导致过度拟合。
此参数与二次采样完全相同,但它对行进行采样。对于具有数百个特征的数据集,建议选择0.5到1之间的特征采样率,以降低过度拟合的机会。
梯度提升是我们对于表格监督学习任务最有能力的模型。因此,大多数时候,您不必担心它不足以完成任务。当你使用梯度提升时,你的时间几乎总是花在如何对其进行正则化上——控制它的力量,这样它就不会吞噬你的数据集,并且在涉及到看不见的数据时变得毫无用处。
我介绍的超参数都可以帮助您完成此任务,并且它们包含在 Python 中梯度提升的每个实现中。好好利用它们。
正如我之前提到的,梯度提升通过 Python 库已经很成熟。以下是主要的四种:
前三个库彼此相似:
这三个库的一个流行替代品是 Scikit-learn,它的缺点是仅适用于 CPU。由于梯度提升是一种计算量大的算法,因此对于大型数据集(我们谈论的是数十万行)来说,在 CPU 上运行它可能是不可行的。
然而,我们必须记住,Scikit-learn 本身比这三个库加在一起更受欢迎。除了用于分类和回归的两个梯度增强估计器之外,Scikit-learn 还为无数监督和无监督学习任务提供了数十种其他模型。
此外,使用 Scikit-learn 构建的梯度提升模型可以集成到其丰富的生态系统中,例如管道、交叉验证估计器、数据处理器等。
以下是有关如何使用 GradientBoostingClassifier 进行分类的分步指南。我们将根据钻石的价格和其他物理测量来预测钻石的切工质量。该数据集内置于 Seaborn 库中。
专业提示:使用 DataCamp 代码片段编辑器的“解释代码”按钮来获取所发生情况的详细逐行解释。
import pandas as pd
import seaborn as sns
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import classification_report
from sklearn.model_selection import cross_val_score, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
# Load the diamonds dataset from Seaborn
diamonds = sns.load_dataset("diamonds")
# Split data into features and target
X = diamonds.drop("cut", axis=1)
y = diamonds["cut"]
# Split data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Define categorical and numerical features
categorical_features = X.select_dtypes(
include=["object"]
).columns.tolist()
numerical_features = X.select_dtypes(
include=["float64", "int64"]
).columns.tolist()
preprocessor = ColumnTransformer(
transformers=[
("cat", OneHotEncoder(), categorical_features),
("num", StandardScaler(), numerical_features),
]
)
pipeline = Pipeline(
[
("preprocessor", preprocessor),
("classifier", GradientBoostingClassifier(random_state=42)),
]
)
# Perform 5-fold cross-validation
cv_scores = cross_val_score(pipeline, X_train, y_train, cv=5)
# Fit the model on the training data
pipeline.fit(X_train, y_train)
# Predict on the test set
y_pred = pipeline.predict(X_test)
# Generate classification report
report = classification_report(y_test, y_pred
print(f"Mean Cross-Validation Accuracy: {cv_scores.mean():.4f}")
print("\nClassification Report:")
print(report)
Mean Cross-Validation Accuracy: 0.7621
Classification Report:
precision recall f1-score support
Fair 0.90 0.91 0.91 335
Good 0.81 0.63 0.71 1004
Ideal 0.82 0.91 0.86 4292
Premium 0.70 0.86 0.77 2775
Very Good 0.66 0.41 0.51 2382
accuracy 0.76 10788
macro avg 0.78 0.74 0.75 10788
weighted avg 0.75 0.76 0.75 10788
加权准确度为 75%,这对于默认参数的基线模型来说还不错。因此,作为一项挑战,我让您调整 GradientBoostingClassifier 的超参数以实现超过 95% 的性能。对的,这是可能的! (提示:仔细阅读最后一节并查看分类器的 Scikit-learn 文档 ).