持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情
假设您训练了一个预测模型,并将其发布到生产环境。
现在,您依靠它来做出业务决策。您必须维护、重新训练并密切关注您的模型。
它会出现什么问题,以及如何跟踪?
让我们来看一个例子。这是一个关于我们如何训练模型、模拟生产环境使用并分析其逐渐退化的故事。
任务:自行车需求预测
在本教程中,我们将处理需求预测问题。
数据集。 我们采用了关于自行车共享需求的 Kaggle 数据集。 我们的目标是预测每小时的自行车租赁量。 为此,我们有一些关于季节、天气和星期几的数据。
模型。我们使用从一月份开始的四个星期的数据训练了一个随机森林模型。让我们想象一下,在实践中,我们刚刚开始数据收集,这就是所有可用的数据。训练模型的性能看起来可以接受,所以我们决定试一试。
反馈。
我们假设我们只在每周结束时才了解ground truth(实际需求)。
这是现实世界机器学习中的一个现实假设。集成和更新不同的数据源并不总是那么简单。即使在实际事件发生之后!也许日常使用数据存储在本地,每周只发送和合并一次到数据库中。
当您为不同的未来一段时间生成预测时,可能会出现类似的延迟。如果你预测未来一周,那么这个范围将成为您的等待时间。
模型检查。由于每周仅提供一次实际数据,因此我们决定每次都运行常规模型分析。没有实时监控。相反,我们安排了一项工作,生成一份标准的每周报表供数据科学家查看。
如何分析模型性能?
为了在生产中分析我们的模型,我们将使用 Evidently。 它是一个开源工具,可生成有关模型性能的交互式预构建报表。
为了运行它,我们将行为数据准备为 Pandas DataFrame。
它应该包括:
- 模型应用日志:模型中的特征和相应的预测;
- ground truth数据:作为我们“target”的每小时租用自行车的实际数量。
您可以使用此Jupyter notebook 示例按照我们的步骤操作。
让我们先看看我们创建的模型的性能。 一旦我们训练了一个模型,我们就会获取我们的训练数据集和预测并将其指定为“Reference”数据。 请继续关注:这也将有助于我们稍后参考这些数据。
reference = raw_data.loc['2011-01-01 00:00:00':'2011-01-28 23:00:00']
我们可以直接从 DataFrame 中选择这个时间段,因为它有 datetime 作为索引。
我们还映射列以显示工具是什么并执行正确的分析:
target = 'count'
prediction = 'prediction'
numerical_features = ['temp', 'atemp', 'humidity', 'windspeed', 'hour', 'weekday']
categorical_features = ['season', 'holiday', 'workingday']
默认情况下,Evidently 使用索引作为绘图中的 x 轴。 在这种情况下,它是日期时间,所以我们不显式添加任何内容。 否则,我们必须在列映射中指定它。
接下来,我们调用回归模型的相应报告。
regression_perfomance_dashboard = Dashboard(tabs=[RegressionPerformanceTab])
regression_perfomance_dashboard.calculate(reference, None, column_mapping=column_mapping)
并直接在 Jupyter notebook中显示结果。
regression_perfomance_dashboard.show()
我们还将其保存为 .html 文件,以便轻松共享。
regression_performance_dashboard.save('regression_performance_at_training.html')
我们可以看到模型质量很好,因为我们只训练了四个星期的数据!
更多好消息:误差是对称的并且分布在零附近。 没有明显的低估或高估。
我们将继续将训练中模型表现的数据集作为我们的"reference"。 它让我们很好地感受到了我们在生产使用中可以从我们的模型中获得的质量。 因此,我们可以将未来的表现与这个基准进行对比。
进入自然环境:生产环境的第 1 周
在生产环境中观察模型有简单的目标。我们想检测是否有问题。
我们还想诊断根本原因并快速了解如何解决它。也许,模型退化得太快了,我们需要更频繁地重新训练它?或许,误差太高,需要对模型进行适配并重建?哪些新模式正在出现?
在我们的例子中,我们首先检查模型在训练数据之外的表现如何。
我们继续使用Jupyter notebook 示例。出于演示目的,我们在一个批次中生成了未来几周的所有预测。实际上,我们会在数据进入时按顺序运行模型。
要选择分析的时间段,我们将在 DataFrame 中指明行。
让我们首先将第一周的表现与我们在训练中看到的进行比较。前 28 天是我们的参考数据集;接下来的7个是生产数据集。
regression_performance_dashboard.calculate(
reference,
production.loc['2011-01-29 00:00:00':'2011-02-07 23:00:00'],
column_mapping=column_mapping
)
报表出来了!我们可以快速将生产性能与我们的参考性能进行比较。
预计会出现一些衰退,但总体上看起来并没有那么糟糕。
误差略有增加并且倾向于低估。
让我们检查一下我们的目标是否有任何统计变化。 为此,我们将生成目标漂移报表。
我们从 Evidently 选项卡调用报告。 在回归模型中,我们的目标是数值,所以我们选择一个匹配的报表:
target_drift_dashboard = Dashboard(tabs=[NumTargetDriftTab])
target_drift_dashboard.calculate(
reference,
production.loc['2011-01-29 00:00:00':'2011-02-07 23:00:00'],
column_mapping=column_mapping)
我们可以看到,实际租用自行车数量的分布仍然非常相似。 更准确地说,相似性假设没有被拒绝。 未检测到漂移。
我们预测的分布也没有太大变化。
尽管如此,一个合理的决定是通过包含新一周的数据来更新您的模型。 这样,模型可以继续学习,我们可能可以改善错误。
为了演示,我们将继续看看事情真的出了问题进展得有多快。
继续下周!
第 2 周:未能保持良好状态
再一次,我们将新的一周与参考数据集进行对比。
regression_perfomance_dashboard.calculate(
reference,
production.loc['2011-02-07 00:00:00':'2011-02-14 23:00:00'],
column_mapping=column_mapping)
乍一看,第二周的模型表现差别不大。
MAE 几乎保持不变。 但是,低估的倾向继续增加。 看来这个错误不是随机的! 平均而言,我们低估了十辆自行车。
要了解更多信息,我们将转到图表。 我们可以看到该模型很好地捕捉到了整体的每日趋势。 所以它学到了一些有用的东西! 但是,在高峰时段,实际需求往往高于预期。
在误差分布图中,我们可以看到它是如何变得“更宽”的,因为我们有更多具有高误差的预测。 向左的移动也是可见的。 在某些极端情况下,我们有 -80 到 40 辆自行车之间的错误,这是以前看不到的。
让我们也检查一下我们的目标漂移。
target_drift_dashboard.calculate(
reference,
production.loc['2011-02-07 00:00:00':'2011-02-14 23:00:00'],
column_mapping=column_mapping)
事情变得有趣了!
我们可以看到目标分布现在不同了:相似性假设被拒绝了。 从字面上看,人们正在租用更多的自行车。 这与我们的训练时期在统计上是不同的。
但是,我们的预测分布未能保持! 这是模型退化的一个明显例子。 世界上发生了一些新的事情,但它错过了模式。
进一步调查很诱人。数据中有什么可以解释这种变化吗?如果有一些新信号,重新训练可能会帮助模型保持良好状态。
在目标漂移报告中,有一个部分可以帮助我们探索特征与目标(或模型预测)之间的关系。
浏览各个特征时,我们可以检查是否发现任何新模式。 我们知道预测没有改变,所以我们只关注与目标的关系。
例如,随着租用自行车数量的相应增加,似乎正在向更高的温度(以摄氏度测量)转变。
这对模型是新的!
也许,它会在重新训练中采用这些模式。 但就目前而言,我们只是继续下周而没有任何更新。
第 3 周:当情况变得越来越糟糕时
好吧,现在情况看起来确实很糟糕。 在第 3 周,我们面临质量大幅下降。
绝对误差和百分比误差均显着增加。
如果我们查看这些图,模型预测明显分散。 我们还面临模型无法预测的具有高需求量的新数据段。
但即使在目标值的已知范围内,模型现在也会出错。 自从训练以来,情况确实发生了变化。
我们可以看到模型不能很好地推断。 预测的需求量保持在相同的已知范围内,而实际值正在达到峰值。
如果我们放大特定日期,我们可能会认为该错误在一天中的特定(活跃的)时间更高。 我们从晚上 10 点到早上 6 点做得很好!
但是,我们在解释这种模式时应该小心。这也可能是由于其他一些相关因素,例如:在这些相同时间的温度更暖和。
Evidently 生成更多图表来显示错误。 在当前情况下,这些是描绘同一个故事的不同方式。 我们有一个很大的错误,而且它明显倾向于低估。
在过去的几周中,这些相同模型质量问题的早期迹象已经显现。 随着变化的积累,它们被放大了。
回归性能报告还生成了一组洞悉,以深入了解表现不佳的段(segments)。 目标是探索特定的特征范围是否可以解释错误。
在我们的示例中,我们特别想了解模型低估了目标函数的段(segments)。
误差偏差表提供了更多细节。
我们按“Range%”字段对其进行排序。如果特定特征的值在模型低估或高估的组中存在显着差异,则该特征将排名靠前。
在我们的例子中,我们可以看到极端误差取决于“temp”(温度)和“atemp”(感觉温度)特征。
在训练中,情况并非如此。 我们在不同的温度下有各种各样的错误,没有一致的模式。
在这个快速分析之后,我们对模型性能及其弱点有了更具体的了解。 该模型面临着新的、异常高的需求量。 考虑到它是如何训练的,它往往会低估它。 最重要的是,这些错误根本不是随机的。 至少,它们与我们观察到的温度有关。 温度越高,低估的幅度越大。
它提出了模型以前无法学习的与天气相关的新模式。 日子越来越暖和了,模型变得离群了。
如果我们运行目标漂移报表,我们还将看到特征和目标之间的线性相关性的相关变化。 温度和湿度脱颖而出。
在这一点上,该模型似乎无用。这是一个生动的例子,说明在有限数据集上训练的模型如何无法捕捉季节性模式。
我们应该尽快重新训练,并经常这样做,直到我们学会所有的模式。 如果我们不习惯频繁的再训练,我们可能会选择更适合时间序列或推理更好的算法。
在它崩溃之前:数据和预测漂移
在实践中,一旦我们获得了ground truth,我们确实可以快速纠正方向。 如果我们在第一周后重新训练模型,它可能不会那么戏剧性地结束。
但是,如果我们没有可用的ground truth怎么办? 我们能提前捕捉到这种退化吗?
在这种情况下,我们可以分析数据漂移。 我们不需要实际值来计算误差。 相反,我们的目标是查看输入数据是否发生了变化。
再一次,让我们将生产的第一周数据与训练中的数据进行比较。
当然,我们可以查看我们所有的特征。 但我们也可以得出结论,分类特征(如“季节”、“假期”和“工作日”)不太可能变化。让我们只看数字特征!
我们指定这些特征,以便该工具应用正确的统计测试。 在这种情况下,它将是 Kolmogorov-Smirnov 测试。
column_mapping = {}
column_mapping['numerical_features'] = numerical_features
然后,我们调用所选时间段的漂移报表:
data_drift_dashboard = Dashboard(tabs=[DataDriftTab])
data_drift_dashboard.calculate(
reference,
production.loc['2011-01-29 00:00:00':'2011-02-07 23:00:00'],
column_mapping=column_mapping)
一旦我们显示报表,它就会返回一个答案。 我们可以在第一周看到特征分布的统计变化。
让我们放大我们平常的疑点——温度(temperature)。
该报表为我们提供了关于特征分布如何随时间演变的两个展示。 我们可以注意到观察到的温度是如何一天天变高的。
这些值明显偏离了我们在训练中看到的绿色走廊(与平均值的一个标准偏差)。 从稳定的增长来看,我们可以怀疑有上升的趋势。
在这个图表中,我们还看到:天气越来越暖和了。 这不是我们的模型所习惯的!
正如我们之前检查的那样,我们在第一周后没有检测到模型预测中的漂移。鉴于我们的模型不擅长推断,我们不应该真的期望它。
这种预测漂移可能仍然会发生,并会发出诸如输入数据损坏之类的信号。在其他情况下,如果我们有一个更敏感的模型,我们会观察到它。
尽管如此,仅数据漂移本身就可以提供出色的早期监控,以检测变化并对其做出反应。
结语
频繁的再训练是解决生产模型维护问题的一种方法。监控和可观察性增加了一层以确保模型质量。
为什么我们应该将它包含在我们的工作流程中?
- 它加快了调试速度。每当您的模型失败时,您都需要确定根本原因。预先构建的仪表板使其更快。
- 它详细显示了性能。如果在交叉验证中只依赖聚合模型的性能,它可能会掩盖重要的模式。您的模型可能会在特定段上静默失败并需要重建。
- 它有助于改进模型。您可以探索模型在何处以及如何出错。它有助于确定最佳模型架构、重新训练计划、生成特征工程的想法。或者,向您的主题专家提出正确的问题。
- 它让你积极主动。数据输入的变化可能是模型质量的最重要指标。我们希望在问题导致模型失败之前发现这些问题。
原文链接:How to break a model in 20 days. A tutorial on production model analytics