在过去的二十年里,我们对软件开发有了一些深刻的理解。这在很大程度上要归功于DevOps的出现和整个行业的广泛采用。
领先的软件公司遵循相同的模式:软件开发中的快速迭代,然后是持续集成、持续交付、持续部署。每件工件都要通过测试其提供价值的能力,始终处于就绪状态,并通过自动化进行部署。
作为一个领域,机器学习不同于传统的软件开发,但我们仍然可以借鉴许多知识,使其适应我们的行业。在过去的几年里,我们一直在做生产中的机器学习项目,所以超越了概念验证,以及我们在软件开发中同样的目标:可重复性。所以我们建立了一个管道协调器,强大的自动化,并建立了一个工作流程来实现这一点。
为什么不只是Jupyter笔记本呢?那么,从头开始构建一个笔记本需要多长时间,所有的处理步骤都是从头开始的呢?让新成员加入团队有多容易?你能快速复制两个月前的结果吗?你能把今天的结果和历史上的结果进行比较吗?你能在整个培训过程中给出你的数据的出处吗?如果你的模型变旧了怎么办?
我们已经面对了所有这些问题,甚至更多,现在我们利用我们的经验推导出了12个因素(作为对12个因素应用程序的认可),这些因素构成了生产中成功的ML的支柱。
虽然版本控制对于基本上所有的软件工程师来说都是显而易见的,但它并不是数据科学家普遍接受的方法。让我引用GitLab的同事们的话作为一个快速入门:
版本控制促进了整个软件开发团队之间的协调、共享和协作。版本控制软件使团队能够在分布式和异步环境中工作,管理代码和工件的更改和版本,并解决合并冲突和相关异常。
作为软件开发的一种特殊形式,机器学习有其独特的要求。首先,它不是一个而是两个活动部分:代码和数据。其次,模型训练发生在(快速)迭代中,并引入了高方差的代码(例如,拆分、预处理、模型)。
一旦数据可以更改,就需要对其进行版本化,以便能够重复地进行实验和训练模型。粗略的版本控制形式(阅读:硬拷贝)可以大有用武之地,但特别是在共享的团队场景中,不变的版本控制变得至关重要。
代码的版本控制更为关键。除了上面的引用,预处理代码不仅与培训相关,而且与服务时间相关,并且需要与模型保持一致的相关性。无服务器功能可以提供一种易于访问的方式,在数据科学家的工作流和生产就绪需求之间实现折衷。
在一个完美的世界中,任何产生您的输入数据的东西都将永远产生完全相同的数据,至少在结构上是这样。但这个世界并不完美,你正在使用的数据来自上游服务,该服务是由人类构建的,可能会发生变化。最终,功能将会改变。在最好的情况下,您的模型完全失败,但在最坏的情况下,它们只会默默地开始产生垃圾结果。
明确定义的功能依赖项允许尽可能早地出现透明故障。设计良好的系统将在持续训练和服役期间适应功能依赖。
好的软件是描述性的-它可以很容易地阅读和理解,而不需要阅读每一行代码。
虽然机器学习是软件开发的一种独特风格,但它并不能免除从业者遵循已建立的编码指导原则的责任。对编码标准要领的基本理解可以用很少的努力和很短的时间来掌握。
预处理和模型的代码都应该遵循PEP8。它应该由有意义的对象名称组成,并包含有用的注释。遵循PEP8将提高代码的易读性,降低复杂性,并加快调试速度。诸如SOLID之类的编程范例提供了思想框架,使代码对于未来的用例更易维护、更易于理解和更灵活。
配置应该与代码分开。不要硬编码您的拆分比率,在运行时通过配置提供它们。从超参数调优可知,良好分离的配置可以显著提高迭代速度,并使代码库可重用。
如果你不能重现训练结果,你就不能相信结果。虽然这在某种程度上是这篇博文的主要主题,但重现性也有一些细微的差别。你不仅需要能够自己重现训练,整个团队也应该能够做到这一点。在某人PC上的Jupyter笔记本或AWS上的某些VM上模糊培训是可重现培训的字面上的反面。
通过使用管道来训练模型,整个团队在进行的实验和培训运行中既获得了访问权限,又获得了透明度。捆绑了可重用代码库和独立于配置的功能,每个人都可以在任何时间点成功重新启动任何培训。
单元测试是在原子级别上进行测试-每个函数都根据其自己的特定标准进行单独测试。
集成测试采用的是相反的方法-代码库的所有元素作为一个组进行测试,并与上行和下行服务的克隆/模拟一起进行测试。
这两个范例都是机器学习的良好起点。预处理代码注定要用于单元测试-在给定各种输入的情况下,转换是否会产生正确的结果?模型是集成测试的一个很好的用例-您的模型在生产环境中的服务时间产生与评估相当的结果吗?
对于生产场景来说,漂移是一个合法的问题。只要数据有轻微变化的可能性(例如,用户输入、上游服务波动性),您就需要考虑漂移。有两种措施可以降低风险敞口:
生产系统的数据监控。建立自动报告机制,以便向团队发出更改数据的警报,甚至超出明确定义的功能依赖项。
对新输入的数据进行持续培训。自动化程度高的管道可以根据新记录的数据重新运行,并提供与历史培训结果的可比性,以显示性能下降,并提供一种快速方法,在提供更好的模型性能的情况下,将新培训的模型提升到生产中。
Excel不是跟踪实验结果的好方法。而且不仅仅是Excel,任何分散的、手动的跟踪形式都会产生非权威性的、因此不可信的信息。
正确的方法是在集中的数据存储中记录培训结果的自动化方法。自动化确保了对每次训练运行的可靠跟踪,并允许稍后对彼此的训练运行进行可比性。结果的集中存储提供了团队间的透明度,并允许持续分析。
理解数据集需要付出努力。通常,这种理解是通过实验收集的,特别是在具有大量隐藏领域知识的领域中操作时。开始使用Jupyter笔记本,将部分/全部数据放入Pandas Dataframe,进行几个小时的无序魔术,训练第一个模型,评估结果-工作完成。嗯,不幸的是没有。
在机器学习的生命周期中,实验起到了一定的作用。然而,这些实验的结果不是模型,而是理解。探索性的Jupyter笔记本的模型是理解的证据,而不是准备生产的人工制品。获得理解将需要更多的成型和安装到准备生产的培训管道中。
然而,所有与特定领域知识无关的理解都可以自动化。生成您正在使用的每个数据版本的统计数据,以跳过您可能必须在Jupyter笔记本中进行的任何一次性、特别的探索工作,并直接转到第一条管道。越早在管道中进行试验,就越早能够就中间结果进行协作,也就越早收到可投入生产的模型。
避免偏斜的训练和服务环境通常被简化为正确地将所有数据预处理嵌入到模型服务环境中。这是绝对正确的,你需要遵守这条规则。然而,这也是对训练-服务-倾斜的过于狭隘的解释。
稍微绕道回顾一下古老的DevOps历史:2006年,亚马逊的首席技术官沃纳·沃格斯(Werner Vogels)创造了“你建造它,你运行它”这个术语。这是一个描述性的短语,意思是将开发人员的责任扩展到不仅编写而且运行他们构建的软件。
机器学习项目也需要类似的动态--理解数据的上游生成和生成的模型的下游使用都是数据科学家的责任。哪个系统会为您的培训生成数据?它能打破吗,系统的SLO(服务级别目标)是什么,它与服务是一样的吗?你们的模特是怎么服务的?什么是运行时环境,您的预处理功能在服务期间是如何应用的?这些都是数据科学家需要理解并找到答案的问题。
TL;DR:正确地将预处理嵌入到服务中,并确保您理解数据的上下游。
从向项目引入第二个培训脚本的时间点开始,可比性成为任何未来工作的基本部分。如果第二个模型的结果根本不能与第一个模型进行比较,就会产生浪费,并且两个模型中至少有一个是多余的,如果不是两个都是多余的话。
根据定义,所有试图解决同一问题的模型培训都需要具有可比性,否则它们就不是在解决同一问题。虽然随着时间的推移,迭代会改变对模型进行比较的定义,但是需要在早期将比较模型培训的技术可能性作为一等公民构建到培训架构中。
作为一个非常粗略的描述,机器学习模型被认为是通过从数据中学习来解决问题的。为了解决这个问题,需要分配计算资源。首先是培训模型,然后是为模型服务。负责在培训期间花费资源的抽象实体(例如,个人或部门)将责任向前推进到服务。在模型的生命周期中可能会出现大量的负面降级:数据可能会漂移,模型可能会成为整体性能的瓶颈,而偏差是一个真正的问题。
效果:数据科学家和团队负责监控他们制作的模型。如果有较大的组织在发挥作用,则不一定在实施监测方面,但肯定是为了理解和解释监测数据。至少,需要监视模型的输入数据、推理时间、资源使用情况(读取:CPU、RAM)和输出数据。
戴利:再说一遍:你建造它,你运行它。生产监控模型是生产数据科学的一部分。
在技术层面上,每个模型培训流水线都需要生产出可部署到生产中的人工制品。模型的结果可能是可怕的,没有任何问题,但它需要最终包装成一个可以直接转移到生产环境中的人工制品。
这是软件开发中的一个共同主题--称为持续交付。团队应该能够在任何给定的时刻部署他们的软件,迭代周期需要足够快来适应这一目标。
机器学习需要采取类似的方法。它将首先加强关于现实和对模型的期望的对话。所有利益相关者都需要意识到模型结果在理论上是可能的。所有利益相关者都需要就部署模型的方式达成一致,以及它在哪里适合于围绕它的更大的软件体系结构。然而,它也将导致强大的自动化,并必然采用前面段落中概述的大多数因素。
每个培训渠道都需要生产一个可部署的人工制品,而不是“仅仅”一个模型。
这绝不是一份详尽的清单。它结合了我们的经验,欢迎您将其作为基准测试您的生产体系结构的样板,或者作为设计您自己的蓝图。
我们使用这些因素作为我们的ML协调器Core Engine的指导原则。所以,在你从头开始之前:报名吧,让我们为你的钱而奔跑吧!
要了解更多有关酷睿引擎的信息,请访问我们的网站或文档以获取更多详细信息。如果您想开始在您自己的ML生产环境中使用Core Engine,请联系我们!