作为一家关于Flightaware的预测技术机组人员的软件工程师2,安德鲁布鲁克斯不断地致力于维护,分析和改进飞行力的预测模型以及支持它们的软件和基础设施。
远见卓识是我们允许Flightaware的预测技术套件的名称,它使用机器学习模型来发出关于航班的实时预测。这些模型在数据集上绘制了数千个数据源的数据集,并在实时将路由和天气数据合并到未来事件中预测未来事件。
在这个博客文章中,我们将专注于远见的实时ETA预测。具体而言,远见可以提供两种ETAS:估计的“开启”时间(当其目的地的跑道上的飞行降落时)和估计的“时间(当飞行在目的地的大门完成时)。 FERESIGHT ETA预测需要每个目的地的两种机器学习模型,其分别提供“ON”和“IN”预测。
培训机器学习模型,电力前瞻eta不是一项简单的任务。为了支持全世界数千个目的地,我们需要培训大约3500种不同的型号。为了使事情更糟糕,这些型号必须每月再次再次培训一次,从头开始,以确保它们能够适应现实世界条件的任何变化。
在我们可以在规模讨论培训模型之前,有必要描述我们用来训练单一模型的过程。从高水平看,模型训练过程可分为两个阶段:
“拆分”阶段,它准备了一个用于训练的单机场数据集,并在该数据集上执行测试/列车
“列车”阶段从前阶段获取输出数据集并产生训练有素的模型。请注意,此阶段可能需要多次运行 - 如果所创建的型号似乎呈现良好或通过我们的理智检查,则必须使用调整后的超参数来培训。
在摘要中,模型训练过程非常简单。不幸的是,如果我们尝试以规模执行它,很明显,现实世界对我们有其他计划。有几个挑战变得痛苦明显:
在最坏的情况下,运行单个远见模型的“列车”阶段可能需要几百千兆字节的RAM,可以使96 VCPU服务器饱和几个小时。值得庆幸的是,磁盘要求有点宽容 - 我们只需要几百GB(并且旋转磁盘就足够了)。
我们不知道预先训练给定模型需要多长时间。虽然“数据集分离”阶段很容易(但它通常在2小时内完成最大数据集的时间,并且在大致与输出数据集的尺寸大致成比例的时间内运行),“列车”相位更加复杂。培训可能需要几分钟到近12小时的任何地方,具体取决于机场和模型类型。令人惊讶的是,机场的“火车”阶段的持续时间与数据集大小没有明确的关系:低交通机场有时需要比高交通机场的模型更长的时间。
这种不可预测性对估算资源使用并试图提前规划集群规模具有严重后果。难以准确估计生成所有模型需要多长时间,并且它同样挑战估计峰值资源消耗。
鉴于问题的规模,如果我们想在任何合理的时间长度完成,我们应该毫不奇怪地向集群分发模型培训工作。这一实现具有一系列新的挑战,我们需要解决。
我们需要一种有效地协调所有参与服务器的模型训练。 “协调”有点模糊,所以我们对我们的要求有点更具体:
任务必须以正确的顺序执行(即,我们不会尝试模型的“列车”阶段,直到“拆分”阶段为该模型完成)。
我们应该尽可能多地运行尽可能多的任务,只要这样做就不会在任何给定服务器上排出可用资源。这一事实是,我们的“拆分”和“火车”任务具有截然不同的资源需求:可以并行运行数十个“拆分”任务的相同服务器可能只能能够为单个执行“列车”阶段模特立刻。
我们需要能够以每种型号监控培训过程,以确认培训正在取得进展。
引入其他服务器会为失败创造机会。这威胁要加剧“可接受的罕见”失败模式,以至于不可接受的常见点。为了说明,假设我们在模型训练期间拥有一个有1个有1个失败的服务器。如果我们使用100个这样的服务器,现在有一个近2个有近2个机会使至少有一个服务器失败。左侧触发,这些赔率是“通常工作”的系统之间的区别,一个是一种令人无法可靠的。
当可能的情况下,我们的解决方案需要从这些失败中恢复。它还应该尝试隔离它们:未能培训一个机场的模型,不应该危及其他机场的模型培训。
在经常的基础上启动和手动监测模型培训可能是开发人员的时间大幅消耗。任何解决方案都必须易于自动启动,监控和检查成功或失败。
此时,我们已经建立了我们模型培训管道的挑战和要求的粗略图景。它的时间是开始更具体地了解我们如何在它们周围设计。
首先,我们需要一段巨大的计算资源,短时间内才能在规模培训模型。这使我们的工作量是一个显而易见的云 - 我们每月支付一次几个实例,以训练一批模型,但我们仍然可以避免在我们没有使用它们时为他们支付.Thankfly,AWS EC2有几种实例类型这有足够的内存来训练我们的模型中最大的内容(特别是我们使用M5.24xlarge和可比的实例).Running在AWS上也意味着我们可以利用S3来存储输入数据集,由“拆分”阶段生成的数据集,并且在共同位置的培训模型。
拥有“原始硬件”运行我们的培训流程数千个模型只是一个开始.deciding有多少实例提供培训我们的模型是困难的:供应太少需要我们等待所有模型都要花费长时间训练但是,提供太多的供应运行留下某些情况空闲的风险(并且在此期间为它们付费)。
我们采用的解决方案是避免使用固定大小的集群。通过在AutoScaling [1] Kubernetes集群中运行我们的工作负载,我们可以在反应中自动提供或删除实例,以便对需求进行[2]。我们所需要的是,我们根据需要附上适当的资源请求(暂时,假设我们为每个模型的每个训练阶段创建一个POD)。使用适用于每个阶段创建的POD的资源请求允许我们的集群在为每个机场执行轻量级“拆分”阶段时,逐渐增加其尺寸,因为我们为每个模型输入计算密集的“列车”阶段,并缩小向下缩小由于模型培训开始完成一些机场。
谢天谢地,Kubernetes对寻址我们的分发和协调要求也非常有帮助.Kubernetes调度程序特别有用:它会尝试安排尽可能多的Pods /阶段,因为群集具有支持的资源,永远不会在节点上安排POD这没有“有空间”。方便地,Kubernetes也有助于获得培训过程的可见性 - 通过检查集群中的POD,可以确定模型训练的阶段成功完成了多少阶段。
到目前为止,我们需要解决难题的难题的一部分:我们仍然需要一种实现每个模型的培训阶段的手段。熟悉Kubernetes的人可能会建议批量创建Kubernetes作业,具有适当的Kustomization。乍一看,这是非常有前途的 - 这是一种简单的方法,可以确保每个阶段获取一个POD,它还可以通过自动重启故障的POD来确保容错。不幸的是,工作并不是我们想要的。没有办法描述作业之间的依赖关系,并且肯定没有任何方法可以在另一个(如果需要用新的HyperParameters重新启动另一个“训练”阶段,那么在另一个作业上
从技术上讲,我们仍然可以使用作业创建一个工作模型培训管道,但我们需要创建一个服务来手动处理任务之间的协调。特别是,我们需要实施一种防止给定模型的“列车”相位豆荚的手段,直到两个测试/训练“分割”相位盒完成。我们还可能必须添加类似的机制,以决定是否重新运行具有新的超参数的“列车”相位窗格。
此时,我们有一个明确的利基,我们想要填补。我们希望在Kubernetes集群上执行一些服务,在Kubernetes集群上执行一条任务的任务,同时提供自动重启行为,并允许我们检查模型培训进度。这是Argo进来的:它是一个完全正确的开源服务。
ARGO的核心是一种称为“工作流程”的抽象。 ARGO工作流包括一系列步骤或依赖于互相任务的DAG。在Kubernetes集群上设置ARGO服务后,您可以参数化并提交工作流以进行执行。当ARGO执行工作流时,一旦满足其其他任务的依赖项,就会为每一步创建一个Kubernetes Pod。就像工作一样,您可以告诉Argo如何重新尝试失败的任务,以便从瞬态故障中恢复。
批判性地,使用ARGO不需要我们来前容使用手动定义的Kubernetes Pods的许多有用功能和模式。资源请求得到支持,可以为DAG中的每个步骤提供,允许我们从Kubernetes Scheduler和AutoScaler的优势中获益。同样,您可以为Kubernetes提供用于特定步骤的Kubernetes的模板,这些步骤可以由群集自动配置。在我们的情况下,这使得可以轻松保证“列车”阶段具有足够的划痕空间来成功执行。虽然它们不用于我们的模型培训工作流程,但Argo还能够在任务的持续时间内创建“Sidecar”容器,或者使用标签选择器等。
与Kubernetes中的许多对象和资源一样,ARGO工作流程在YAML中被简单地写入。设置工作流程的步骤相当简单。至少,您可以指定一个容器并提供命令或解释器/内联脚本来执行:
##一步,“你好世界”工作流#apiversion:argoproj.io/v1alpha1kind:workflowmetadata:generateName:hello-world-spec:#在哪里开始执行工作流程ententpoint:say-hello模板:#以下是步骤: - 名称:Say-Hello脚本:图片:Python:Alpine3.6命令:[Python]来源:|打印(“你好!”)
指定描述两步工作流的微不足道的DAG类似地方便 - 我们只需要陈述步骤之间的依赖项:
##一个微不足道的DAG工作流#apiversion:argoproj.io/v1alpha1kind:workflowmetadata:generateName:trevial-dagspec:rentpoint:run-this-dag模板:#dag本身 - 名称:运行 - this-dag dag:任务: - 名称:Hello-Bash模板:Say-Hi-in-Bash - 名称:Hello-Python模板:Say-Hi-Python依赖项:[hello-bash]#dag中的实际脚本 - 名称:say-hi-in- Bash脚本:图片:Python:Alpine3.6命令:[Bash]来源:"回声'你好来自Bash'" - 名称:Say-Hi-Python脚本:图片:Python:Alpine3.6命令:[Python]来源:"打印('来自Python&#39的您好;)"
如果我们采取此基本思想并将其与某些ARGO的其他功能相结合,例如使步骤可参数化和调节在以前的文件上的文件上的步骤,我们可以为我们的模型培训工作流程绘出简化的骨架:
appiersion:argoproj.io/v1alpha1kind:workflowmetadata:generateName:train-model-spec:stintpoint:split-and train模板:# - name:stract-and-train dag:任务: - 名称:培训 - 数据模板:分裂 - 阶段参数:参数:[{name:test-or train,值:'火车'}] - 名称:test-desion模板:拆分阶段参数:参数:[{name:test-火车,价值:'测试'}] - 名称:培训模型依赖性:[培训 - 数据,测试数据]模板:培训 - 阶段参数:参数:[{name:hypeparams,value:&# 39;第1次尝试超级参数'}]#只有在第一次训练循环产生的新的封面术时培训#一个坏模型 - 当&#39时,跳过这部分DAG;当&#39时跳过。条件#不持有。 - 名称:火车型号 - 带 - 新的 - 超参数依赖性:[训练模型]模板:培训阶段:" {{tasks.train-model.outputs.parameters.model_looks_bad}}!= 0&#34 ;参数:参数:[{name:hyperparams,value:'第二次尝试超级参数'}] #the"分离阶段"训练 - 名称:拆分阶段重试resstygy:#重试处理瞬态故障限制:2输入:参数: - 名称:test-or-train脚本:图片:foresight-training-image命令:[bash]源:| #...生成测试/火车数据集,按到S3 ...#如果{{inputs.parameters.test-or-train}}在这里出现,它' s模板化到#' test&#39 ;或者'火车'资源:请求:CPU:2500M#轻量级步骤,询问2.5核#The"火车阶段"模型训练 - 名称:训练相位retrystrategy:#重试处理瞬态故障限制:2输入:参数: - 名称:reledparams输出:参数: - 名称:model_looks_bad valuefrom:path:/ model_looks_bad脚本:图片:foreesight-training-图像命令:[Bash]来源:| #...从S3下载测试/火车数据集#...尝试培训模型,上传到S3如果看起来很好......写入0到/ model_looks_bad如果模型似乎好,1否则为1。 ..资源:请求:CPU:90000M#重量级步骤,要求90个核心
值得突出显示模型是否需要重复训练阶段的确定可以在工作流本身中表示。显然,这种机制可能会令人难以置信,因为任何非凡的事情,但它非常令人印象深刻的是,ARGO允许我们在另一个逐步调节另一个逐步上的一些输出。
这样的工作流程通常会在Argo命令行程序提交。在提交工作流程时,ARGO允许您覆盖或设置工作流可能需要的任何参数(在我们的情况下,我们想要创建的型号以及我们为其创建的机场)。这允许我们使用Workflow的规范部分的简单补充来获取机场代码和模型类型:
添加此后并修改我们的步骤以引用ARGO的{{workflow.parameters.airport}}和{workflow.parameters.type}}和{workflow.parameters.type}},然后我们可以提交工作流以为业余爱好生成EON模型休斯顿的机场说些什么
如果您希望在操作中看到ARGO的其他示例,工作流规范令人惊讶地灵活,并且ASGO Github存储库中有各种示例。
与群集提交工作流的相同ARGO命令行程序也允许您列出已提交的工作流程,请求有关它们的信息,或者交互地监控它们。如果您更喜欢Web浏览器到终端,ARGO也附带了一个Web UI,允许您可视化与工作流相关的DAG。
这些都是手动检查工作流程的好选项,但有更好的方法以编程方式检查工作流程。实际上,ARGO工作流实现为Kubernetes自定义资源[3],它允许我们与它们交互作为kubect中的一流资源,就像我们可能适用于节点,豆荚或部署一样。这对于与群集的壳牌脚本或编程互动非常有用。例如,如果您正在编写shell脚本并希望获取所有已完成的工作流的名称,则可以通过运行来执行此操作
在类似的纸币上,在Bash花费大量时间的JQ Fanatics将欣赏Kubectl Get Workflow -o Json的灵活性| JQ ...用于询问关于提交的工作流的一次性问题。
一般来说,我们非常满意原型的容易和生产与ARGO的大型模型训练管道。然而,我们遇到的一些令人沮丧的是值得指出的:
目前没有办法参数化ARGO工作流程中的资源请求。如果指定资源请求,则必须硬核代理。
如果您不小心不匹配了ARGO命令行程序的版本和群集中运行的ARGO服务,则可能会遇到非常奇怪的错误。如果命令行程序注意到版本不匹配并显示出突出的警告,可能会节省我们一段时间。
尽管如此,这些“PaperCut问题”不会阻止我们推荐ARGO以获得类似的工作负载,我们已经在预测技术船员和Flightaware的其他地方使用了Argo。
当您谈论Kubernetes时,“AutoScaling”是一个加载的术语。具体而言,我们的意思是“群集自动阶段”中的“自动播放”,而不是“水平窗口自动阶段”,“垂直吊舱自动阶段”,或任何云供应商特定的特征,它们恰好在其名称中具有“自动阶段”。 ↩︎
群集自动播放适用于我们的用例,但它不是所有工作负载的明智或有效的选择。集群自动缩放有许多优异的陈述,导致某些应用中的严重头痛。 ↩︎
不幸的是,官方文件有点抽象。如果您想要一个演示自定义资源电量的简单示例,请在披萨 - 控制器上享用眼睛。 ↩︎