本教程涵盖以下主题:
机器学习已广泛应用于一系列任务。但是,在某些高风险应用中,例如自动驾驶,医疗诊断和财务预测,错误可能导致致命的后果或重大的财务损失。在这些应用中,重要的是要检测系统何时犯错并采取更安全的措施。此外,还希望收集这些“故障场景”,对其进行标记,并教系统通过主动学习做出正确的预测。
预测不确定性估计可用于检测错误。理想情况下,该模型在可能会出错的情况下表明高度不确定性。这使我们能够发现错误并采取更安全的措施。至关重要的是,行动的选择取决于模型为何不确定。不确定性的主要来源有两个:数据不确定性(也称为偶然不确定性)和知识不确定性(也称为认知不确定性)。如果我们的目标是发现错误,则不必将这两个不确定性分开。但是,如果我们的目标是主动学习,那么我们想发现新的输入,并且可以将知识不确定性用于此。
数据的不确定性是由于数据固有的复杂性而产生的,例如加性噪声或重叠类。在这些情况下,模型知道输入具有多个类别的属性,或者目标有噪声。重要的是,无法通过收集更多的训练数据来减少数据不确定性。当模型的输入来自训练数据稀疏或远离训练数据的区域时,就会出现知识不确定性。在这些情况下,模型对该区域了解得很少,并且可能会犯错。与数据不确定性不同,可以通过从一个了解程度不高的区域收集更多的训练数据来减少知识不确定性。
本教程详细介绍了如何在CatBoost中量化数据和知识的不确定性。
为了说明这些概念,我们将使用一个简单的综合示例。
假设我们有两个分类特征x 1和x 2,每个都有9个值,所以有81种可能的特征组合。 目标取决于以下函数:
y = mean(x₁,x₂) + eps(x₁,x₂)
其中平均值(x 1,x 2)是一个未知的固定值,而eps(x 1,x 2)是一个正态分布的噪声(即数据不确定性),平均值为0,方差为var(x 1,x 2)。 在我们的示例中,均值(x 1,x 2)是随机生成的,而var(x 1,x 2)具有两个值(0.01和0.04),其分布如下:
红心上的点比红心外的点在目标中具有更多的噪音。 请注意,我们列举了类别以获得更好的可视化效果,但是在数据集中,两个功能都是分类的,即未给出顺序。
当我们生成具有这种分布的数据集时,我们假设红心内没有任何训练示例-这些特征组合被认为是我们数据集的异常值。
用RMSE损失优化的标准模型只能预测平均值(x 1,x 2)。 好的,但是如果我们要估算y的方差,即数据不确定性,该怎么办? 换句话说,如果我们想了解哪些预测比较吵杂怎么办? 为了估计数据的不确定性,必须使用预测均值和方差的概率回归模型。 为此,CatBoost中有一个名为RMSEWithUncertainty的新损失函数。 有了这个损失,类似于NGBoost算法[1],CatBoost估计正态分布的均值和方差,优化负对数似然率并使用自然梯度。 对于每个示例,CatBoost模型返回两个值:估计平均值和估计方差。
让我们尝试将此损失函数应用于我们的简单示例。 我们得到以下变化:
我们可以看到CatBoost成功地预测了心脏及其外部的变化。 在心脏内部,我们没有训练数据,因此可以预测任何事情。
我们知道如何估算数据中的噪声。 但是,如何衡量由于特定地区缺乏培训数据而导致的知识不确定性? 如果我们要检测异常值该怎么办? 估计知识不确定性需要模型的整体。 如果所有模型都理解输入,则它们将给出相似的预测(较低的知识不确定性)。 但是,如果模型不理解输入,则它们可能会提供不同的预测,并且彼此之间会强烈不同意见(知识不确定性很高)。 对于回归,可以通过测量多个模型之间的均值方差来获得知识不确定性。 请注意,这与单个模型的预测方差不同,后者可以捕获数据不确定性。
让我们考虑一下生成的GBDT模型的集合,如下所示:
def ensemble(train_pool, val_pool, num_samples=10, iters=1000, lr=0.2):
ens_preds = []
for seed in range(num_samples):
model = CatBoostRegressor(iterations=iters, learning_rate=lr,
loss_function='RMSEWithUncertainty', posterior_sampling=True,
verbose=False, random_seed=seed)
model.fit(train_pool, eval_set=val_pool)
ens_preds.append(model.predict(test))
return np.asarray(ens_preds)
使用选项posterior_sampling生成模型,因为这可以使获得的(随机)预测很好地分布(具有良好的理论属性,在这里我们参考[2]以获得详细信息)。
然后,为了估计知识的不确定性,我们只计算模型预测的平均值的方差:
knowledge = np.var(ens_preds, axis=0)[:, 0]
我们得到以下结果:
该模型正确检测到心脏内部的知识不确定性(我们看不到原始心脏边界的痕迹)。 这说明了如何通过估计知识的不确定性来检测异常输入。
实际上,训练多个CatBoost模型的集成可能太昂贵了。 理想情况下,我们希望训练一个模型,但仍然能够检测异常值。 有一个解决方案:我们可以使用从单个训练模型中获得的虚拟集合:
def virt_ensemble(train_pool, val_pool, num_samples=10, iters=1000, lr=0.2):
ens_preds = []
model = CatBoostRegressor(iterations=iters, learning_rate=lr,
loss_function='RMSEWithUncertainty', posterior_sampling=True,
verbose=False, random_seed=0)
model.fit(train_pool, eval_set=val_pool)
ens_preds = model.virtual_ensembles_predict(test, prediction_type='VirtEnsembles',
virtual_ensembles_count=num_samples)
return np.asarray(ens_preds)
CatBoost通过一个训练完成的模型返回多个预测。 这些预测是通过截断模型获得的:
同样,我们使用选项posterior_sampling来保证裁剪预测的理想分布。 让我们看看我们得到了什么:
注意,由于虚拟集合元素是相关的,因此知识不确定性的预测绝对值现在要小得多。 但是,它仍然可以成功检测到未被占用的区域(异常值)。
代替返回几个模型的预测的prediction_type =“ VirtEnsembles”,我们可以使用prediction_type =“ TotalUncertainty”并使相同的结果更容易。 对于这种预测类型,CatBoost使用虚拟集合计算所有类型的不确定性。 即,对于RMSEWithUncertainty,它返回以下统计信息:[均值预测,知识不确定性,数据不确定性]:
model = CatBoostRegressor(iterations=1000, learning_rate=0.2,
loss_function='RMSEWithUncertainty', posterior_sampling=True,
verbose=False, random_seed=0)
model.fit(train_pool, eval_set=val_pool)
preds = model.virtual_ensembles_predict(test, prediction_type='TotalUncertainty',
virtual_ensembles_count=10)
mean_preds = preds[:,0] # mean values predicted by a virtual ensemble
knowledge = preds[:,1] # knowledge uncertainty predicted by a virtual ensemble
data = preds[:,2] # average estimated data uncertainty
感谢您的关注! 我希望本教程可以帮助您更好地了解不确定性的概念以及如何使用CatBoost进行估算。 我们将在以后的文章中详细介绍不确定性的应用。 敬请期待
[1] T. Duan et al., NGBoost: Natural Gradient Boosting for Probabilistic Prediction (2020), ICML 2020
[2] A. Ustimenko, L. Prokhorenkova and A. Malinin, Uncertainty in Gradient Boosting via Ensembles” (2020), arXiv preprint arXiv:2006.10562
本文代码:https://github.com/catboost/catboost/blob/master/catboost/tutorials/uncertainty/uncertainty_regression.ipynb
作者:Liudmila Prokhorenkova
deephub翻译组