任务:自行车需求预测
在本教程中,我们将处理需求预测问题。
数据集。 我们采用了关于自行车共享需求的 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 周,我们面临质量大幅下降。
绝对误差和百分比误差均显着增加。
如果我们查看这些图,模型预测明显分散。 我们还面临模型无法预测的具有高需求量的新数据段。