机器学习系统设计指南-全-

机器学习系统设计指南(全)

原文:zh.annas-archive.org/md5/74f9691fbb29500fd93915a46684bc38

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

自 2017 年在斯坦福教授第一门机器学习课程以来,许多人向我寻求在他们的组织中部署 ML 模型的建议。这些问题可能是泛泛的,比如“我应该使用什么模型?”“我应该多久重新训练我的模型?”“如何检测数据分布的变化?”“如何确保训练期间使用的特征与推断期间使用的特征一致?”

这些问题也可以是具体的,例如:“我确信从批处理预测切换到在线预测将提升我们模型的性能,但如何说服我的经理让我这么做?”或者“我是公司里资历最老的数据科学家,最近被委托建立我们的第一个机器学习平台;我应该从何开始?”

我对所有这些问题的简短回答总是:“这要看情况。”我的长篇回答通常涉及数小时的讨论,以了解提问者的出发点、他们实际想要实现的目标,以及不同方法在其具体用例中的利弊。

ML 系统既复杂又独特。它们复杂是因为它们由许多不同的组件组成(ML 算法、数据、业务逻辑、评估指标、基础设施等),并涉及许多不同的利益相关者(数据科学家、ML 工程师、业务领导、用户,甚至是整个社会)。ML 系统之所以独特,是因为它们依赖于数据,而数据在不同的用例中变化极大。

例如,两家公司可能属于同一领域(电子商务),并且面临着希望 ML 解决的同样问题(推荐系统),但是它们的 ML 系统可能具有不同的模型架构,使用不同的特征集,评估不同的指标,并带来不同的投资回报。

许多关于 ML 生产的博客文章和教程都专注于回答一个具体的问题。尽管这种专注有助于传达观点,但可能会给人一种错觉,即可以孤立地考虑每一个问题。事实上,一个组件的变化很可能会影响其他组件。因此,在做出任何设计决策时,必须考虑整个系统。

本书对 ML 系统采取了全面的方法。它考虑了系统的不同组成部分以及涉及的不同利益相关者的目标。本书内容使用实际案例进行了说明,其中许多案例我个人参与过,受到了充分的参考和 ML 从业者(无论是学术界还是行业界)的审阅。需要对某个特定主题有深入了解的部分(例如批处理与流处理的比较、存储和计算的基础设施以及负责任的 AI)还经过了专家的审阅。换句话说,本书试图对上述问题以及更多问题给出细致入微的答案。

当我首次撰写为本书奠定基础的讲座笔记时,我认为我是为我的学生准备未来作为数据科学家和 ML 工程师的工作需求。然而,我很快意识到我在这个过程中也学到了很多。我与早期读者分享的初稿引发了许多对话,测试了我的假设,迫使我考虑不同的视角,并向我介绍了新问题和新方法。

我希望这个学习过程现在能够继续进行,因为这本书现在在您手中,您有独特的经验和观点。请随时通过MLOps Discord 服务器(我管理的地方,您也可以在那里找到其他这本书的读者)、TwitterLinkedIn或其他可以在我的网站找到的渠道与我分享对这本书的任何反馈。

本书适合对象

本书适合任何想利用 ML 解决实际问题的人。本书中的 ML 指的是深度学习和经典算法,侧重于中型到大型企业和快速增长的初创企业中看到的 ML 系统。规模较小的系统往往较不复杂,可能从本书中提出的全面方法中受益较少。

因为我的背景是工程学,所以这本书的语言面向工程师,包括 ML 工程师、数据科学家、数据工程师、ML 平台工程师和工程经理。您可能能够与以下情景之一相关联:

  • 您已经面临一个业务问题和大量原始数据。您希望对这些数据进行工程处理,并选择正确的指标来解决这个问题。

  • 初始模型在离线实验中表现良好,并且您希望将它们部署。

  • 模型部署后,您对其表现几乎没有反馈,希望找到一种快速检测、调试和解决生产中可能遇到的任何问题的方法。

  • 开发、评估、部署和更新团队模型的过程大部分是手动、缓慢且容易出错的。您希望自动化和改进这个过程。

  • 您组织中的每个 ML 使用案例都使用其自己的工作流部署,并希望建立可以跨用例共享和重复使用的基础设施(例如模型存储、特征存储、监控工具)。

  • 您担心您的 ML 系统可能存在偏见,希望使您的系统负责任!

如果您属于以下任何一组,您也可以从本书中受益:

  • 想要确定 ML 生产中未满足的领域并找出如何将您的工具定位在生态系统中的工具开发人员。

  • 在行业中寻找与 ML 相关角色的个人。

  • 考虑采用 ML 解决方案以改进产品和/或业务流程的技术和业务领导者。对技术背景不强的读者来说,阅读第一章、第二章和第十一章可能最有益。

本书不是什么

本书不是 ML 的入门书籍。有许多关于 ML 理论的书籍、课程和资源可供参考,因此本书避开这些概念,专注于 ML 的实际应用方面。具体而言,本书假设读者对以下主题有基本理解:

  • ML 模型,例如聚类、逻辑回归、决策树、协同过滤以及包括前馈、循环、卷积和变压器在内的各种神经网络架构

  • ML 技术,例如监督与无监督、梯度下降、目标/损失函数、正则化、泛化和超参数调优

  • 度量指标,例如准确率、F1 值、精确率、召回率、ROC 曲线、均方误差和对数似然

  • 统计概念,如方差、概率和正态/长尾分布

  • 常见的 ML 任务,例如语言建模、异常检测、物体分类和机器翻译

您不必对这些主题了如指掌——对于那些确切定义可能需要一些记忆力的概念,例如 F1 分数,我们包含了简短的参考注释,但您应该对它们的含义有一个大致的了解。

虽然本书提到当前的工具来说明某些概念和解决方案,但它并不是一个教程书籍。技术在不断演进。工具迅速兴起和过时,但解决问题的基本方法应该更持久一些。本书为您提供了一个框架,帮助您评估适合您用例的工具。当您需要使用某个工具时,通常很容易在网上找到教程。因此,本书几乎没有代码片段,而是专注于围绕权衡、利弊和具体示例进行大量讨论。

浏览本书

本书的章节安排反映了数据科学家在进行 ML 项目生命周期时可能遇到的问题。前两章奠定了 ML 项目成功的基础,从最基本的问题开始:你的项目是否需要 ML?它还涵盖了选择项目目标以及如何以更简单的方式框定问题的内容。如果您已经熟悉这些考虑因素并且迫不及待地想要了解技术解决方案,可以跳过前两章。

第四章到第六章章涵盖了机器学习项目的部署前阶段:从创建训练数据和工程特征到在开发环境中开发和评估模型。这个阶段尤其需要对机器学习和问题领域的专业知识。

第七章到第九章章涵盖了机器学习项目部署和后部署阶段。我们将通过一个许多读者可能能够理解的故事来学习,即部署模型并不意味着部署过程的结束。部署的模型将需要监控并持续更新以适应不断变化的环境和业务需求。

第三章和第十章专注于为不同背景的利益相关者提供必要的基础设施,以便共同交付成功的机器学习系统。第三章专注于数据系统,而第十章专注于计算基础设施和机器学习平台。我曾经对是否深入讨论数据系统问题和在书中引入数据系统的时机进行了长时间的讨论。数据系统,包括数据库、数据格式、数据移动和数据处理引擎,在机器学习课程中往往涉及不足,因此许多数据科学家可能认为这些内容较低级或不相关。在与许多同事的讨论后,我决定因为机器学习系统依赖于数据,早期涵盖数据系统的基础知识将有助于我们就数据问题达成一致,以便在书的其余部分讨论数据事务。

虽然本书涵盖了机器学习系统的许多技术方面,但是机器学习系统是由人构建的,为人服务的,并且可能对许多人的生活产生巨大影响。在没有一章讨论其人性化方面的情况下写一本关于机器学习生产的书是不完整的,这是第十一章的重点,也是最后一章。

注意,“数据科学家”是一个在过去几年中发展迅速的角色,已经有很多讨论确定这个角色应该包括什么——我们将在第十章中详细讨论其中的一些内容。在本书中,我们使用“数据科学家”作为一个总称,包括所有从事开发和部署机器学习模型的人员,包括可能被称为机器学习工程师、数据工程师、数据分析师等的人。

GitHub 仓库和社区

本书附带一个GitHub 仓库,其中包含:

  • 对基本机器学习概念的回顾

  • 本书使用的参考文献列表和其他高级、更新的资源

  • 本书中使用的代码片段

  • 可以用于解决工作流中可能遇到的某些问题的工具列表

我还在ML 运维 Discord 服务器上运营,鼓励大家讨论和提问有关本书的问题。

本书使用的约定

本书使用以下排版约定:

斜体

表示新术语、网址、电子邮件地址、文件名及文件扩展名。

等宽字体

用于程序清单,以及段落内部用于引用程序元素,如变量或函数名、数据库、数据类型、环境变量、语句和关键字。

注意

此元素表示一般性注释。

警告

此元素表示警告或注意事项。

使用代码示例

正如提到的那样,补充材料(代码示例、练习等)可以在https://oreil.ly/designing-machine-learning-systems-code下载。

如果您有技术问题或在使用示例代码时遇到问题,请发送电子邮件至bookquestions@oreilly.com

本书旨在帮助您完成工作。一般情况下,如果本书提供示例代码,您可以在自己的程序和文档中使用它。除非您复制了大部分代码,否则无需取得我们的许可。例如,编写一个使用本书多个代码片段的程序不需要许可。销售或分发 O’Reilly 书籍中的示例代码需要许可。通过引用本书并引用示例代码回答问题无需许可。将本书大量示例代码整合到您产品的文档中需要许可。

我们感谢,但通常不需要署名。署名通常包括书名、作者、出版商和 ISBN。例如:“设计机器学习系统,作者 Chip Huyen(O’Reilly 出版)。版权所有 2022 年 Huyen Thi Khanh Nguyen,978-1-098-10796-3。”

如果您认为您使用的示例代码超出了合理使用范围或上述许可,请随时与我们联系permissions@oreilly.com

致谢

这本书花了两年时间来写作,而在此之前还花了更多的时间来准备。回顾过去,我对获得的大量帮助感到同样惊讶和感激。我尽力在这里列出帮助过我的每一个人的名字,但由于人类记忆的固有缺陷,我无疑忽略了许多人。如果我忘记包括你的名字,请知道这并不是因为我不欣赏你的贡献,请友善地提醒我,以便我尽快纠正!

首先,我要感谢帮助我开发课程和书籍材料的课程工作人员:Michael Cooper,Xi Yin,Chloe He,Kinbert Chou,Megan Leszczynski,Karan Goel 和 Michele Catasta。我要感谢我的教授 Christopher Ré和 Mehran Sahami,没有他们,这门课程本来不会存在。

我要感谢一大群审稿人,他们不仅给予了鼓励,而且通过许多方面改进了这本书:Eugene Yan,Josh Wills,Han-chung Lee,Thomas Dietterich,Irene Tematelewo,Goku Mohandas,Jacopo Tagliabue,Andrey Kurenkov,Zach Nussbaum,Jay Chia,Laurens Geffert,Brian Spiering,Erin Ledell,Rosanne Liu,Chin Ling,Shreya Shankar 和 Sara Hooker。

我要感谢所有阅读过早期版本并给了我改进书籍意见的读者,包括 Charles Frye,Xintong Yu,Jordan Zhang,Jonathon Belotti 和 Cynthia Yu。

当然,这本书离不开 O’Reilly 的团队,特别是我的开发编辑 Jill Leonard,以及我的制作编辑 Kristen Brown,Sharon Tripp 和 Gregory Hyman。我要感谢 Laurence Moroney,Hannes Hapke 和 Rebecca Novack,他们帮助我将这本书从构思变成了提案。

这本书,毕竟,是我职业生涯至今学到的无价之宝的积累。我要感谢我的极其能干和耐心的同事们,以及前同事们,他们分别来自 Claypot AI,Primer AI,Netflix,NVIDIA 和 Snorkel AI。我曾与每一位合作过的人都学到了关于推动机器学习走向世界的新知识。

特别感谢我的联合创始人徐振中,在我们的初创公司解决问题并允许我花时间写作这本书。还要感谢卢克,无论我的目标有多么雄心勃勃,你总是全力支持我。

第一章:机器学习系统概述

2016 年 11 月,谷歌宣布已将其多语言神经机器翻译系统整合到 Google 翻译中,标志着深度人工神经网络在大规模生产中的首个成功案例。¹ 根据谷歌的说法,通过此更新,翻译质量的改善超过了前 10 年的总和。

深度学习的成功重新激发了对机器学习(ML)的广泛兴趣。此后,越来越多的公司转向 ML 来解决它们最具挑战性的问题。仅仅五年时间,ML 已经渗透到我们生活的几乎每一个方面:我们如何获取信息,如何沟通,如何工作,如何寻找爱情。ML 的传播速度如此之快,以至于很难想象没有它的生活。然而,在健康护理、交通运输、农业乃至于帮助我们理解宇宙等领域,还有许多 ML 等待被探索的用例。²

当许多人听到“机器学习系统”时,他们只考虑使用的 ML 算法,如逻辑回归或不同类型的神经网络。然而,算法只是 ML 系统在生产中的一小部分。系统还包括首次诞生 ML 项目的业务需求,用户和开发人员与系统交互的界面,数据堆栈,以及开发、监控和更新模型的逻辑,以及支持交付该逻辑的基础设施。图 1-1 展示了 ML 系统的不同组成部分,以及本书中将涵盖它们的章节。

MLOps 与 ML 系统设计之间的关系

MLOps 中的 Ops 源自 DevOps,即开发与运维。将某物操作化意味着将其投入生产,包括部署、监控和维护。MLOps 是将 ML 引入生产的一组工具和最佳实践。

ML 系统设计采用系统方法进行 MLOps,这意味着它全面考虑 ML 系统,以确保所有组件及其利益相关者可以共同工作,以满足指定的目标和要求。

图 1-1. ML 系统的不同组成部分。“ML 算法”通常是人们谈论机器学习时所想到的,但它只是整个系统的一小部分。

有许多关于各种机器学习算法的优秀书籍。本书不详细涵盖任何特定算法,而是帮助读者全面理解整个机器学习系统。换句话说,本书的目标是为您提供一个框架,以开发对您的问题最有效的解决方案,无论最终使用哪种算法。随着新算法的不断开发,算法可能很快就会过时,但本书提出的框架应该仍然适用于新算法。

本书的第一章旨在为您提供将机器学习模型投入生产所需的概述。在讨论如何开发机器学习系统之前,问清楚何时以及何时不使用机器学习是很重要的。我们将通过一些流行的机器学习用例来说明这一点。

在介绍用例之后,我们将继续讨论部署机器学习系统的挑战,并通过比较在生产环境中的机器学习与研究中的机器学习以及传统软件来进行。如果您一直在开发应用型机器学习系统的前线,您可能已经对本章中的内容很熟悉。但是,如果您只在学术环境中有过机器学习的经验,本章将为您展示机器学习在现实世界中的真实情况,并为您的首个应用程序成功的设立基础。

何时使用机器学习

随着它在行业中的快速应用增长,机器学习已被证明是解决各种问题的强大工具。尽管内外部都充满了无数的兴奋和炒作,但机器学习并非解决所有问题的神奇工具。即使是机器学习可以解决的问题,机器学习解决方案也可能不是最佳解决方案。在开始机器学习项目之前,您可能想问问机器学习是否是必要的或者是否具有成本效益。³

要理解机器学习能做什么,让我们先来看看机器学习解决方案通常做什么:

机器学习是一种从现有数据中学习 复杂模式 并利用这些模式对未见数据做出预测的方法。

我们将分析上述表述中的每一个斜体关键词组,以理解它对机器学习可以解决的问题的影响:

1. 学习:系统具有学习能力

关系数据库不是机器学习系统,因为它没有学习能力。您可以明确地说明关系数据库中两列之间的关系,但它不太可能自己找出这两列之间的关系。

对于机器学习系统来学习,必须有东西可以让它学习。在大多数情况下,机器学习系统从数据中学习。在监督学习中,基于示例输入和输出对,机器学习系统学习如何为任意输入生成输出。例如,如果你想构建一个机器学习系统来预测 Airbnb 房源的租金价格,你需要提供一个数据集,其中每个输入是一个带有相关特征的房源(面积、房间数、社区、设施、该房源的评级等),而相关输出是该房源的租金价格。一旦学习完成,这个机器学习系统应该能够预测新房源的价格,根据其特征。

2. 复杂模式:有一些需要学习的模式,并且它们是复杂的。

机器学习解决方案仅在存在需要学习的模式时才有用。理智的人不会投入资金建立一个机器学习系统来预测公正骰子的下一个结果,因为这些结果的生成方式没有模式。⁴ 然而,在股票定价方式中存在模式,因此公司已经投入数十亿美元建立机器学习系统来学习这些模式。

是否存在模式可能并不明显,或者如果存在模式,你的数据集或机器学习算法可能不足以捕捉它们。例如,埃隆·马斯克的推文如何影响加密货币价格可能存在一种模式。但是,在对其推文进行严格训练和评估之前,你不会知道。即使你的所有模型都无法合理预测加密货币的价格,也并不意味着没有模式。

考虑一个像 Airbnb 这样有大量房源的网站;每个房源都带有一个邮政编码。如果你想将房源按所在州进行分类,你不需要一个机器学习系统。因为模式很简单——每个邮政编码对应一个已知的州——你可以直接使用查找表。

租金价格与其所有特征之间的关系遵循一种更为复杂的模式,手动指定将会非常具有挑战性。机器学习对此是一个很好的解决方案。与其告诉你的系统如何从特征列表计算价格,不如提供价格和特征,让你的机器学习系统找出这种模式。机器学习解决方案与查找表解决方案以及一般传统软件解决方案的不同之处在图 1-2 中有所展示。因此,机器学习也被称为软件 2.0。⁵

ML 在复杂模式任务(如目标检测和语音识别)上取得了很大成功。对机器而言复杂的东西与对人类而言复杂的东西是不同的。对人类来说难的任务对机器来说可能很容易,例如将一个数的幂乘以 10。另一方面,对人类来说容易的任务对机器来说可能很难,比如判断一张图片中是否有猫。

图 1-2. ML 解决方案不需要手动指定模式来计算输出,而是从输入和输出中学习模式。

3. 现有数据:数据已经可用,或者可以收集数据。

因为 ML 是从数据中学习的,所以必须有数据供其学习。想象一下建立一个模型来预测一个人应该每年缴纳多少税,这样的想法很有趣,但如果没有大量人群的税收和收入数据,这是不可能的。

零样本学习(有时称为零数据学习)的背景下,ML 系统有可能对一个任务进行良好的预测,而无需对该任务的数据进行训练。然而,这个 ML 系统之前是通过与考虑任务相关的其他任务的数据进行训练的。因此,即使该系统在处理当前任务时不需要数据来学习,它仍然需要数据来学习。

也可以在没有数据的情况下启动一个 ML 系统。例如,在持续学习的背景下,ML 模型可以在没有经过任何数据训练的情况下部署,但它们会从生产中的输入数据中学习。⁶ 然而,向用户提供训练不足的模型会带来一定的风险,比如客户体验不佳。

没有数据和持续学习的情况下,许多公司采用“假装直到成功”方法:推出一款由人类制定预测而非 ML 模型的产品,希望后续使用生成的数据来训练 ML 模型。

4. 预测:这是一个预测性问题。

ML 模型进行预测,因此它们只能解决需要预测答案的问题。当你可以从大量廉价但近似的预测中获益时,ML 尤其具有吸引力。在英语中,“预测”意味着“估计未来的值”。例如,明天的天气会是什么样?今年超级碗谁会赢?用户下一部想看的电影是什么?

随着预测机器(例如 ML 模型)的效果越来越好,越来越多的问题被重新定义为预测问题。无论你问什么问题,你都可以将其表述为:“对于这个问题的答案是什么?”不管这个问题是关于未来、现在,甚至是过去的事情。

计算密集型问题是一类已经非常成功地重新定义为预测性问题的问题之一。与其计算过程的确切结果,这可能比机器学习更加计算昂贵和耗时,你可以将问题描述为:“这个过程的结果会是什么样?”并使用机器学习模型来近似它。输出将是确切输出的近似,但通常已经足够好了。你可以在图形渲染中看到很多这样的应用,比如图像去噪和屏幕空间阴影处理。⁷

5. 未见数据:未见数据与训练数据共享模式

模型从现有数据中学习的模式只有在未见数据也共享这些模式时才有用。一个预测应用程序在 2020 年圣诞节是否会被下载的模型,如果是基于 2008 年的数据训练的话,表现可能不会很好,当时 App Store 上最受欢迎的应用是 Koi Pond。什么是 Koi Pond?恰恰如此。

从技术角度来看,这意味着你的未见数据和训练数据应该来自相似的分布。你可能会问:“如果数据是未见的,我们怎么知道它来自哪个分布?”我们不知道,但我们可以做出假设——比如我们可以假设明天用户的行为不会与今天的行为有太大的不同——并希望我们的假设成立。如果它们不成立,我们将得到表现不佳的模型,我们可能可以通过监控发现,如第八章所述的数据分布变化和监控,以及在生产中进行测试,如第九章所述的持续学习和在生产中进行测试。

由于大多数机器学习算法今天的学习方式,机器学习解决方案尤其在以下这些额外特征的问题上表现突出:

6. 它是重复的

人类在少样本学习方面表现出色:你可以向孩子们展示几张猫的图片,他们大多数人在下次看到猫时都会认出猫。尽管在少样本学习研究中取得了令人兴奋的进展,但大多数机器学习算法仍然需要许多例子来学习模式。当任务重复时,每个模式会被多次重复,这使得机器更容易学习它。

7. 错误预测的成本很低

除非你的机器学习模型始终百分之百地表现良好,这对任何有意义的任务来说是高度不可能的,否则你的模型会犯错。机器学习在错误预测成本低的情况下特别适用。例如,今天机器学习最大的用例之一是在推荐系统中,因为在推荐系统中,一个糟糕的推荐通常是可以原谅的——用户只是不会点击推荐。

如果一个预测错误可能会带来灾难性后果,那么如果正确预测的利益平均超过错误预测的成本,则机器学习可能仍然是一个合适的解决方案。开发自动驾驶汽车具有挑战性,因为算法错误可能导致死亡。然而,许多公司仍希望开发自动驾驶汽车,因为一旦自动驾驶汽车的安全性在统计上超过人类驾驶员,它们有可能拯救许多生命。

8. 它是在规模上发生的

机器学习解决方案通常需要对数据、计算、基础设施和人才进行非平凡的前期投资,因此如果我们能够大量使用这些解决方案,这是有道理的。

“在规模上”对不同的任务意味着不同的事情,但总的来说,它意味着做出许多预测。例如,每年筛选数百万封电子邮件或预测每天将支持票据路由到哪些部门。

一个问题可能看起来是一个独立的预测,但实际上是一系列预测。例如,预测谁将赢得美国总统选举的模型似乎每四年只做一次预测,但实际上可能每小时甚至更频繁地做出预测,因为该预测必须不断更新以纳入新信息。

在规模上存在问题还意味着有大量数据可以收集,这对于训练机器学习模型是有用的。

9. 模式不断变化

文化在变化,口味在变化,技术在变化。今天流行的东西明天可能就过时了。考虑电子邮件垃圾邮件分类的任务。今天,垃圾邮件的指标是尼日利亚的王子,但明天可能是一个绝望的越南作家。

如果您的问题涉及一个或多个不断变化的模式,硬编码的解决方案,如手写规则,可能会很快过时。了解问题的变化,以便相应更新手写规则可能会非常昂贵或不可能。由于机器学习是从数据中学习的,您可以使用新数据更新您的机器学习模型,而无需弄清楚数据如何发生变化。还可以设置系统以适应不断变化的数据分布,这是我们将在“持续学习”章节讨论的一种方法。

用例列表可以继续延伸,并且随着工业中机器学习的成熟应用,它将变得更加庞大。尽管机器学习可以很好地解决一些问题的子集,但它不能解决和/或不应该用于许多问题。今天的大多数机器学习算法在以下任何情况下都不应使用:

  • 这是不道德的。我们将在“案例研究 I:自动评分器的偏见”章节讨论一个案例研究,其中使用机器学习算法可能被认为是不道德的。

  • 更简单的解决方案能够达到预期效果。在第六章中,我们将介绍机器学习模型开发的四个阶段,其中第一个阶段应该是非机器学习解决方案。

  • 它并非成本效益高。

然而,即使机器学习不能解决你的问题,也可能将问题分解为更小的组件,并使用机器学习解决其中的一些问题。例如,如果你无法建立一个能回答所有客户问题的聊天机器人,可能可以建立一个机器学习模型来预测是否有问题与经常问的问题之一相匹配。如果是,则引导客户查看答案。如果不是,则引导他们联系客服。

我也想警告不要因为一种新技术目前没有现有技术那样的成本效益而将其置之不理。大多数技术进步都是渐进的。一种技术现在可能效率不高,但随着更多投资的时间推移,可能会改善。如果你等待技术向整个行业证明其价值再入局,你可能会落后于竞争对手数年甚至数十年。

机器学习应用案例

机器学习在企业和消费者应用中的使用越来越广泛。自 2010 年中期以来,已经涌现出利用机器学习提供优越或以前不可能的服务的应用程序。

随着信息和服务的爆炸性增长,如果没有机器学习的帮助,无论是搜索引擎还是推荐系统,我们很难找到我们想要的东西。当你访问亚马逊或 Netflix 等网站时,系统会推荐最符合你口味的物品。如果你不喜欢推荐的任何物品,你可能会想要搜索特定的物品,而你的搜索结果很可能是由机器学习驱动的。

如果你有智能手机,机器学习可能已经在许多日常活动中为你提供帮助。在手机上打字变得更容易,这得益于预测输入,一个机器学习系统会为你提供下一步可能要说的内容的建议。在你的照片编辑应用中可能运行的机器学习系统会建议如何最佳地增强你的照片。你可能使用你的指纹或面部进行手机认证,这需要一个机器学习系统来预测指纹或面部是否与你的匹配。

吸引我进入这个领域的机器学习应用案例是机器翻译,自动将一种语言翻译为另一种语言。这有可能让来自不同文化的人们彼此交流,消除语言障碍。我的父母不会讲英语,但多亏了谷歌翻译,现在他们可以阅读我的文章并与不会讲越南语的朋友交流了。

ML 在我们的家中越来越普遍,例如智能个人助理如 Alexa 和 Google Assistant。智能安全摄像头可以在您的宠物离开家或有不速之客时通知您。我的一个朋友担心他独居的年迈母亲——如果她摔倒了,没人能及时帮助她起身——因此他依赖家庭健康监测系统,该系统可以预测是否有人在家中摔倒。

尽管消费者 ML 应用市场正在蓬勃发展,但大多数 ML 应用案例仍然在企业界。企业 ML 应用往往具有与消费者应用截然不同的需求和考虑因素。虽然也有很多例外,但大多数情况下,企业应用可能对精度要求更为严格,但在延迟要求方面更为宽容。例如,将语音识别系统的准确率从 95% 提高到 95.5% 对大多数消费者来说可能无法察觉,但将资源分配系统的效率提高 0.1% 可帮助像 Google 或通用汽车这样的公司节省数百万美元。同时,一秒钟的延迟可能会让消费者分心并打开其他内容,但企业用户对高延迟可能更加宽容。对于有意从 ML 应用构建公司的人来说,消费者应用可能更容易分发,但要实现盈利则要困难得多。然而,大多数企业用例除非您自己遇到过,否则不会显而易见。

根据 Algorithmia 2020 年企业机器学习调查,企业中的 ML 应用多种多样,既服务于内部用例(降低成本、生成客户洞察和智能、内部处理自动化),也服务于外部用例(改善客户体验、保留客户、与客户互动),如图 1-3 所示。⁸

图 1-3. 2020 年企业机器学习现状。来源:Algorithmia 的一幅图像改编

欺诈检测 是企业世界中 ML 最古老的应用之一。如果您的产品或服务涉及任何价值的交易,它都可能受到欺诈的影响。通过利用 ML 解决方案进行异常检测,您可以建立从历史欺诈交易中学习并预测未来交易是否属于欺诈行为的系统。

决定产品或服务的定价是可能是最难的商业决策之一;为什么不让 ML 为您做呢?定价优化 是在特定时间估算价格以最大化定义的目标函数的过程,例如公司的利润、收入或增长率。基于 ML 的定价优化在交易数量大且需求波动,消费者愿意支付动态价格的情况下最为合适,例如互联网广告、机票、住宿预订、共享乘车和活动。

经营业务时,能够预测客户需求非常重要,以便你可以准备预算、备货、分配资源和更新定价策略。例如,如果你经营一家杂货店,你希望备货足够,使顾客能够找到他们想要的东西,但你不希望库存过剩,因为如果过剩,你的食品可能会变质,导致损失。

获取新用户是昂贵的。截至 2019 年,应用程序获取进行应用内购买用户的平均成本为 86.61 美元。⁹ Lyft 的获取成本估计为每位乘客 158 美元。¹⁰ 对于企业客户来说,这个成本要高得多。投资者认为客户获取成本是创业公司的杀手。¹¹ 减少客户获取成本即使是小幅度的改善,也能大幅增加利润。可以通过更好地识别潜在客户、展示更精准的广告、在正确的时间提供折扣等方式来实现这一目标,所有这些都是机器学习适合处理的任务。

消费者一旦流失,你花费了大量资金吸引他们,这将是一件令人遗憾的事情。获取新用户的成本大约是保留现有用户的 5 到 25 倍。¹² 流失预测 是预测特定客户停止使用你的产品或服务的时间,以便你可以采取适当措施挽留他们。流失预测不仅可以用于客户,还可以用于员工。

为了防止客户流失,通过及时解决他们的问题来保持他们的满意度非常重要。自动化支持票据分类可以帮助实现这一目标。以往,当客户打开支持票据或发送电子邮件时,需要先处理,然后传递到不同部门,直到到达能够处理它的人的收件箱。机器学习系统可以分析票据内容并预测应该送到何处,这可以缩短响应时间并提高客户满意度。它还可以用于分类内部 IT 票据。

企业中另一个流行的机器学习用例是品牌监控。品牌是企业的宝贵资产。¹³ 监控公众和客户对品牌的感知非常重要。你可能想知道它何时/何地/如何被提及,无论是明确(例如,当有人提到“Google”时)还是隐含(例如,当有人说“搜索巨头”时),以及与之相关的情绪。如果你的品牌提及突然出现负面情绪,你可能希望尽快解决。情感分析是典型的机器学习任务。

近期引起了很大兴奋的一组机器学习用例是在健康护理领域。有些机器学习系统可以检测皮肤癌和诊断糖尿病。尽管许多健康护理应用面向消费者,但由于其对准确性和隐私的严格要求,通常通过医院等健康护理提供者提供,或者用于协助医生提供诊断。

理解机器学习系统

理解机器学习系统将有助于设计和开发它们。在本节中,我们将探讨机器学习系统如何与研究中的机器学习(或通常在学校中教授的机器学习)以及传统软件有所不同,从而推动了本书的需求。

研究中与生产中的机器学习

由于工业中的机器学习应用还相对较新,大多数具有机器学习专业知识的人都是通过学术途径获得的:参加课程、进行研究、阅读学术论文。如果您的背景符合这种描述,了解如何在实际环境中部署机器学习系统的挑战,以及应对这些挑战的解决方案可能是一个陡峭的学习曲线。在生产中的机器学习与研究中的机器学习有很大不同。表 1-1 显示了这些主要区别中的五个。

表 1-1. 研究中的机器学习与生产中的机器学习的主要区别

研究 生产
要求 基准数据集上的最先进模型性能 不同的利益相关者有不同的要求
计算优先级 快速训练,高吞吐量 快速推理,低延迟
数据 静态^(a) 不断变化的
公平性 通常不是重点 必须考虑
可解释性 通常不是重点 必须考虑
^(a) 研究的一个子领域专注于持续学习:开发能够处理不断变化的数据分布的模型。我们将在第九章中介绍持续学习。

不同的利益相关者和需求

参与研究和排行榜项目的人通常会对一个共同的目标进行调整。最常见的目标是模型性能:开发一个能在基准数据集上达到最先进结果的模型。为了在性能上稍微提升,研究人员经常会采用使模型过于复杂而无法实用的技术。

许多利益相关者参与将机器学习系统投入生产。每个利益相关者都有自己的要求。有不同的、经常相互冲突的要求可能会使得设计、开发和选择能够满足所有要求的机器学习模型变得困难。

考虑一个移动应用程序,为用户推荐餐馆。该应用通过每笔订单向餐馆收取 10% 的服务费来赚钱。这意味着昂贵的订单比便宜的订单为应用带来更多的收入。该项目涉及到机器学习工程师、销售人员、产品经理、基础设施工程师和一位经理:

机器学习工程师

希望有一个推荐用户最可能下单的餐馆的模型,并且他们相信可以通过使用更复杂的模型和更多的数据来实现这一目标。

销售团队

希望有一个推荐昂贵餐馆的模型,因为这些餐馆带来更多的服务费。

产品团队

注意到每次延迟增加都会导致服务订单量的下降,因此他们希望有一个能在少于 100 毫秒内返回推荐餐馆的模型。

ML 平台团队

随着流量增长,这个团队因为现有系统扩展问题在半夜被唤醒,因此他们希望暂停模型更新,优先改进机器学习平台。

经理

希望最大化利润率,可能的一种方法是放弃机器学习团队。¹⁴

“推荐用户最有可能点击的餐馆”和“推荐给应用带来最多收入的餐馆”是两个不同的目标,在 “解耦目标” 部分中,我们将讨论如何开发一个满足不同目标的机器学习系统。剧透:我们将为每个目标开发一个模型,并结合它们的预测结果。

现在让我们假设有两个不同的模型。模型 A 是推荐用户最有可能点击的餐馆的模型,模型 B 是推荐给应用带来最多收入的餐馆的模型。A 和 B 可能是非常不同的模型。应该将哪个模型部署给用户?为了使决策更加困难,A 和 B 都不能满足产品团队提出的要求:它们无法在少于 100 毫秒内返回餐馆推荐。

在开发机器学习项目时,机器学习工程师理解所有利益相关者的需求及其严格程度非常重要。例如,如果能够在 100 毫秒内返回推荐餐馆是一个必须要求——公司发现如果您的模型需要超过 100 毫秒来推荐餐馆,将有 10% 的用户会失去耐心并关闭应用——那么模型 A 和模型 B 都无法胜任。然而,如果这只是一个不错的要求,您可能仍然需要考虑模型 A 或模型 B。

生产对研究的不同要求之一是成功的研究项目可能并不总是用于生产。例如,集成是一种在许多机器学习竞赛(包括著名的 100 万美元 Netflix 奖)的获胜者中流行的技术,但在生产中并不广泛使用。集成结合了“多种学习算法,以获得比单独任何学习算法更好的预测性能。”¹⁵ 尽管它可以使您的机器学习系统稍微提升性能,但集成往往会使系统过于复杂,难以在生产中使用,例如,预测速度较慢或结果解释较困难。我们将在“集成”章节中进一步讨论集成。

对于许多任务,性能的微小改进可能会导致收入大幅提升或成本节约。例如,产品推荐系统的点击率提升 0.2%可能会使电子商务网站的收入增加数百万美元。然而,对于许多任务来说,微小的改进可能用户察觉不到。对于第二类任务,如果一个简单模型能够做出合理的预测,复杂模型必须有显著更好的表现来证明其复杂性是合理的。

计算优先级

在设计机器学习系统时,没有部署过机器学习系统的人经常犯的错误是过于专注于模型开发部分,而忽视了模型部署和维护部分。

在模型开发过程中,您可能会训练多个不同的模型,每个模型对训练数据进行多次遍历。然后,每个训练好的模型会对验证数据进行一次预测以报告分数。验证数据通常比训练数据要小得多。在模型开发阶段,训练是瓶颈。然而,一旦模型部署完成,它的工作就是生成预测,因此推断会成为瓶颈。研究通常优先考虑快速训练,而生产通常优先考虑快速推断。

其一个推论是,研究优先考虑高吞吐量,而生产则优先考虑低延迟。如果需要刷新记忆,延迟是指从接收查询到返回结果所需的时间。吞吐量是指在特定时间段内处理多少查询。

术语冲突

有些书籍区分延迟和响应时间。根据马丁·克莱普曼在他的书《设计数据密集型应用》中的说法,“响应时间是客户看到的时间:除了处理请求的实际时间(服务时间)外,还包括网络延迟和排队延迟。延迟是请求等待处理的持续时间——在此期间它处于潜伏状态,等待服务。”¹⁹

在本书中,为了简化讨论并与机器学习社区使用的术语保持一致,我们使用延迟来指代响应时间,因此请求的延迟测量从发送请求到接收响应的时间。

例如,Google 翻译的平均延迟是用户点击翻译按钮到显示翻译结果的平均时间,而吞吐量则是处理和提供的查询数量。

如果你的系统始终一次处理一个查询,更高的延迟意味着更低的吞吐量。如果平均延迟为 10 毫秒,这意味着处理一个查询需要 10 毫秒,吞吐量为每秒处理 100 个查询。如果平均延迟为 100 毫秒,吞吐量为每秒处理 10 个查询。

然而,由于大多数现代分布式系统会批量处理查询以并行处理,更高的延迟也可能意味着更高的吞吐量。如果每次处理 10 个查询,每个批次运行时间为 10 毫秒,则平均延迟仍为 10 毫秒,但吞吐量现在提高了 10 倍——每秒 1,000 个查询。如果每次处理 50 个查询,每个批次运行时间为 20 毫秒,则平均延迟现在为 20 毫秒,吞吐量为每秒 2,500 个查询。延迟和吞吐量都得到了提高!处理逐个查询和批量处理查询时的延迟和吞吐量权衡差异如图 1-4 所示。

图 1-4. 当逐个处理查询时,更高的延迟意味着更低的吞吐量。然而,当批量处理查询时,更高的延迟也可能意味着更高的吞吐量。

如果你想要批量处理在线查询,情况就更加复杂了。批处理要求系统在足够的查询到达以前等待,然后才能处理,这会进一步增加延迟。

在研究中,你更关心每秒能处理多少样本(吞吐量),而不是每个样本处理需要多长时间(延迟)。你愿意增加延迟以提高吞吐量,例如采用积极的批处理方式。

然而,一旦你将模型部署到真实世界中,延迟就非常重要了。2017 年,Akamai 的一项研究发现,100 毫秒的延迟可以使转化率下降 7%。²⁰ 2019 年,Booking.com 发现,延迟增加约 30%,转化率会降低约 0.5%——“这对我们的业务来说是一个相关成本。”²¹ 2016 年,Google 发现,超过一半的移动用户如果页面加载时间超过三秒,就会离开页面。²² 如今的用户更加缺乏耐心。

要减少生产中的延迟,你可能需要减少同时处理的查询数量。如果你的硬件能够同时处理更多的查询,而现在却使用它来处理更少的查询,这意味着你在低效地使用你的硬件,增加了每个查询的处理成本。

在考虑延迟时,重要的是要记住延迟不是一个单一的数字,而是一个分布。使用像平均延迟(算术平均值)这样的单一数字来简化这个分布是很诱人的,但这个数字可能会误导。想象一下,你有 10 个请求,它们的延迟分别为 100 毫秒、102 毫秒、100 毫秒、100 毫秒、99 毫秒、104 毫秒、110 毫秒、90 毫秒、3,000 毫秒、95 毫秒。平均延迟为 390 毫秒,这使得你的系统看起来比实际慢。可能发生的情况是,存在一个网络错误,导致一个请求比其他请求慢得多,你应该调查这个有问题的请求。

通常最好考虑百分位数,因为它们告诉你有关某个百分比请求的信息。最常见的百分位数是第 50 百分位数,缩写为 p50。它也称为中位数。如果中位数是 100 毫秒,那么一半的请求耗时超过 100 毫秒,另一半的请求耗时少于 100 毫秒。

更高的百分位数还帮助你发现异常值,这可能是某些问题的症状。通常你需要关注的百分位数是 p90、p95 和 p99。前 10 个请求的第 90 百分位数(p90)为 3,000 毫秒,这是一个异常值。

更高的百分位数是重要的,因为即使它们只占你用户的一小部分,有时它们可能是最重要的用户。例如,在亚马逊网站上,最慢请求的客户通常是那些在其账户上有大量数据的客户,因为他们已经进行了多次购买——也就是说,他们是最有价值的客户。²³

使用高百分位数来指定系统的性能要求是一种常见做法;例如,产品经理可能会规定系统的第 90 百分位数或第 99.9 百分位数的延迟必须低于某个数值。

数据

在研究阶段,你处理的数据集通常是干净且格式良好的,这使你能够专注于开发模型。它们本质上是静态的,因此社区可以用它们来评估新的架构和技术。这意味着许多人可能已经使用和讨论了相同的数据集,数据集的怪异之处是众所周知的。你甚至可能会找到开源脚本来直接处理和提供数据给你的模型。

在生产中,如果数据可用,它会更加混乱。它可能会有噪声,可能是非结构化的,不断变化的。它可能存在偏见,而你可能不知道它是如何偏见的。如果有标签,它们可能稀疏、不平衡或不正确。项目或业务要求的变化可能需要更新一些或所有现有标签。如果你处理用户数据,你还需要担心隐私和监管问题。我们将在章节“案例研究二:‘匿名’数据的危险”中讨论一个案例研究,在这个案例中用户数据被不适当地处理。

在研究中,你主要处理历史数据,例如已经存在并存储在某处的数据。在生产中,很可能你也需要处理由用户、系统和第三方数据不断生成的数据。

图 1-5 参考了特斯拉人工智能主管安德烈·卡帕斯的一张精彩图表,该图展示了他在博士期间与在特斯拉任职期间遇到的数据问题的对比。

图 1-5。研究中的数据与生产中的数据。来源:改编自安德烈·卡帕斯的一幅图像²⁴

公平性

在研究阶段,模型尚未用于人员,所以研究人员很容易将公平性推迟为事后事项:“我们先尝试获得最先进的技术,等到生产阶段再考虑公平性。”当进入生产阶段时,已经为时过晚。如果你优化模型以获得更高的准确性或更低的延迟,你可以证明你的模型超越了最先进技术。但是,截至撰写本书时,公平性指标尚无对应的最先进技术。

你或者你生活中的某人可能已经是数学算法偏见的受害者,却并不自知。由于机器学习算法可能因为你的邮政编码而拒绝你的贷款申请,这反映了对某人社会经济背景偏见的偏见。你的简历可能因为雇主使用的排名系统偏重你姓名的拼写而排名较低。你的抵押贷款可能会因为部分依赖信用评分,这有利于富人而损害穷人而获得更高的利率。在现实世界中,机器学习偏见的其他例子包括预测性执法算法、潜在雇主进行的人格测试以及大学排名。

在 2019 年,“伯克利研究人员发现,从 2008 年到 2015 年,面对面和在线借款人共同拒绝了 130 万名有信用的黑人和拉丁裔申请人。”当研究人员“使用被拒申请的收入和信用评分,但删除了种族识别符时,抵押贷款申请被接受。”²⁵ 更令人愤慨的例子,请参阅凯西·奥尼尔的《数学毁灭之武器》。²⁶

机器学习算法不是预测未来,而是编码过去,因此会延续数据中的偏见等问题。当机器学习算法大规模部署时,它们可以在大规模范围内歧视人群。如果一个人类操作员可能一次只对少数个体做出全面的判断,机器学习算法可以在几秒钟内对数百万人做出全面的判断。这可能特别伤害少数群体的成员,因为对他们的误分类可能只对模型整体性能指标产生较小的影响。

如果算法已经可以在 98%的人群上做出正确的预测,而在其他 2%上改进预测将带来多倍的成本,一些公司可能会不幸选择不这样做。在 2019 年的麦肯锡公司研究中,只有 13%的受访大公司表示他们正在采取措施减少公平和公正风险,如算法偏见和歧视。²⁷ 然而,这种情况正在迅速改变。我们将在第十一章中讨论公平性和其他责任人工智能的方面。

可解释性

在 2020 年初,图灵奖得主 Geoffrey Hinton 教授提出了一个备受争议的问题,即解释性在机器学习系统中的重要性:“假设你患有癌症,你必须在一个不能解释其工作原理但治愈率为 90%的黑匣子人工智能外科医生和治愈率为 80%的人类外科医生之间做出选择,你希望这种 AI 外科医生被禁止吗?”²⁸

几周后,当我向一群 30 位公共非技术公司的技术高管提出这个问题时,只有一半的人希望能让高效但无法解释的 AI 外科医生为他们进行手术。另一半希望是人类外科医生。

虽然大多数人在使用微波炉时不必了解其工作原理,但对于人工智能,特别是如果该人工智能做出对他们生活重要的决策,很多人仍然感到不安。

由于大多数机器学习研究仍然只在单一目标——模型性能上进行评估,研究人员没有动力去研究模型的可解释性。然而,对于工业界大多数机器学习应用案例而言,解释性不仅是可选的,而是一个必要条件。

首先,对于用户来说,包括业务领导和最终用户,解释性对于理解为何做出某个决策至关重要,这样他们可以信任模型并发现前述潜在偏见的存在。²⁹ 其次,对于开发人员来说,能够调试和改进模型也非常重要。

仅因为解释性是一个要求,并不意味着每个人都在实施。截至 2019 年,只有 19%的大公司正在努力提高其算法的可解释性。³⁰

讨论

有人可能会认为,仅了解机器学习的学术方面是可以的,因为研究领域有很多工作。第一部分——仅了解机器学习的学术方面是可以的——是正确的。而第二部分是错误的。

尽管追求纯粹的研究很重要,但大多数公司无法负担,除非它能带来短期的商业应用。现在研究界采取了“更大,更好”的方法,尤其如此。往往,新模型需要大量数据和数千万美元的计算成本。

随着机器学习研究和现成模型变得更加可访问,越来越多的人和组织将希望找到它们的应用程序,这增加了对生产中机器学习的需求。

绝大多数与机器学习相关的工作将会,或者已经是,在将机器学习投入生产中。

机器学习系统与传统软件

由于机器学习是软件工程的一部分,并且软件在生产中已经成功使用了半个多世纪,有些人可能会想知道为什么我们不简单地采用经过验证的软件工程最佳实践,并将它们应用到机器学习中。

这是一个很好的想法。事实上,如果机器学习专家也是更好的软件工程师,机器学习生产领域将会更好。许多传统的软件工程工具可以用来开发和部署机器学习应用程序。

然而,许多挑战是机器学习应用程序独有的,并且需要它们自己的工具。在软件工程中,有一个基本的假设,即代码和数据是分开的。事实上,在软件工程中,我们希望尽可能地保持事物模块化和分离(参见维基百科关于关注点分离的页面)。

相反,机器学习系统一部分是代码,一部分是数据,还有一部分是从这两者创建的工件。过去十年的趋势显示,利用最好的数据开发的应用程序胜出。大多数公司不再专注于改进机器学习算法,而是专注于改进它们的数据。因为数据可以迅速变化,机器学习应用程序需要适应不断变化的环境,这可能需要更快的开发和部署周期。

在传统的软件工程中,你只需要关注测试和版本控制你的代码。而在机器学习中,我们还必须测试和版本控制我们的数据,这是困难的部分。如何对大型数据集进行版本控制?如何知道数据样本对你的系统是好还是坏?不是所有的数据样本都是平等的——有些对你的模型比其他更有价值。例如,如果你的模型已经在一百万个正常肺部扫描中进行了训练,而只有一千个癌症肺部扫描,那么癌症肺部扫描比正常肺部扫描更有价值。不加区分地接受所有可用数据可能会损害你的模型性能,甚至使其容易受到数据毒化攻击。³¹

ML 模型的大小是另一个挑战。截至 2022 年,ML 模型通常具有数亿,甚至数十亿的参数,这需要大量的随机存取内存(RAM)来加载它们到内存中。几年后,十亿个参数可能会显得过时,就像“你能相信那台将人送上月球的计算机只有 32MB 的 RAM 吗?”

然而,目前将这些大型模型投入生产,尤其是在边缘设备上³²,是一个巨大的工程挑战。然后问题来了,如何确保这些模型运行速度足够快以便有用。如果自动完成模型建议下一个字符的时间比你输入该字符的时间还长,那么它就毫无用处。

在生产中监控和调试这些模型也是非常不简单的。随着 ML 模型变得越来越复杂,再加上无法深入了解它们工作的可见性,很难找出问题出在哪里,或者在问题出现时快速获得警告。

好消息是,这些工程挑战正在以惊人的速度得到解决。回顾 2018 年,当双向编码器表示来自变压器(BERT)的论文首次发表时,人们讨论的是 BERT 太大、太复杂,以及太慢,以至于无法实用。预训练的大型 BERT 模型有 3.4 亿个参数,大小为 1.35 GB³³。快进两年,BERT 及其变种已经几乎在每一次英文搜索中被谷歌使用³⁴。

概要

本开章旨在让读者了解将 ML 引入现实世界所需付出的努力。我们从今天生产中 ML 的广泛应用案例开始介绍。虽然大多数人熟悉面向消费者的 ML 应用,但大多数 ML 用例是为企业设计的。我们还讨论了 ML 解决方案何时合适。尽管 ML 能够很好地解决许多问题,但并不是所有问题都适合 ML 解决。然而,对于 ML 无法解决的问题,ML 可能仍然是解决方案的一部分。

本章还突出了研究中 ML 与生产中 ML 之间的差异。这些差异包括利益相关者的参与程度、计算优先级、使用数据的属性、公平性问题的严重性以及解释性的要求。这一部分对于从学术界进入 ML 生产领域的人士尤为有用。我们还讨论了 ML 系统与传统软件系统的区别,这也是本书编写的动机所在。

机器学习系统是复杂的,由许多不同的组件组成。在生产中使用机器学习系统的数据科学家和机器学习工程师可能会发现,仅关注机器学习算法部分远远不够。了解系统的其他方面至关重要,包括数据堆栈、部署、监控、维护、基础设施等。本书采用系统方法来开发机器学习系统,这意味着我们将全面考虑系统的所有组成部分,而不仅仅是关注机器学习算法。我们将在下一章节详细介绍这种全面方法的含义。

¹ Mike Schuster, Melvin Johnson 和 Nikhil Thorat,“使用 Google 的多语言神经机器翻译系统进行零-shot 翻译”,Google AI Blog,2016 年 11 月 22 日,https://oreil.ly/2R1CB

² Larry Hardesty,“成像黑洞的方法”,MIT News,2016 年 6 月 6 日,https://oreil.ly/HpL2F

³ 我没有询问机器学习是否足够,因为答案总是是否定的。

⁴ 模式与分布不同。我们知道公正骰子的结果分布,但在生成结果的方式中没有模式。

⁵ Andrej Karpathy,“软件 2.0”,Medium,2017 年 11 月 11 日,https://oreil.ly/yHZrE

⁶ 我们将在第九章讨论在线学习。

⁷ Steke Bako, Thijs Vogels, Brian McWilliams, Mark Meyer, Jan Novák, Alex Harvill, Pradeep Sen, Tony Derose 和 Fabrice Rousselle,“用于去噪蒙特卡罗渲染的核预测卷积网络”,ACM Transactions on Graphics,2017 年,第 36 卷,第 4 期:97,https://oreil.ly/EeI3j;Oliver Nalbach, Elena Arabadzhiyska, Dushyant Mehta, Hans-Peter Seidel 和 Tobias Ritschel,“深度着色:用于屏幕空间着色的卷积神经网络”,arXiv,2016 年,https://oreil.ly/dSspz

⁸ “2020 年企业机器学习现状”,Algorithmia,2020 年,https://oreil.ly/wKMZB

⁹ “2018 年 9 月至 2019 年 8 月全球平均移动应用用户获取成本,按用户操作和操作系统分”,Statista,2019 年,https://oreil.ly/2pTCH

¹⁰ Jeff Henriksen,“估值 Lyft 需要深入研究单位经济学”,Forbes,2019 年 5 月 17 日,https://oreil.ly/VeSt4

¹¹ David Skok,“初创公司杀手:客户获取成本”,For Entrepreneurs,2018 年,https://oreil.ly/L3tQ7

¹² Amy Gallo,“保持正确顾客的价值”,哈佛商业评论,2014 年 10 月 29 日,https://oreil.ly/OlNkl

¹³ Marty Swant,“全球最有价值品牌”,福布斯,2020 年,https://oreil.ly/4uS5i

¹⁴ 机器学习和数据科学团队成为公司大规模裁员的首批人员并不罕见,正如在IBMUberAirbnb等公司报道的那样。详见 Sejuti Das 的分析文章“危机中数据科学家也容易被裁员”,Analytics India Magazine,2020 年 5 月 21 日,https://oreil.ly/jobmz

¹⁵ 维基百科,见“集成学习”,https://oreil.ly/5qkgp

¹⁶ Julia Evans,“机器学习不只是 Kaggle 竞赛”,2014 年,https://oreil.ly/p8mZq

¹⁷ Lauren Oakden-Rayner,“AI 竞赛不会产生有用的模型”,2019 年 9 月 19 日,https://oreil.ly/X6RlT

¹⁸ Kawin Ethayarajh 和 Dan Jurafsky,“效用在用户眼中:对自然语言处理排行榜的批判”,EMNLP,2020 年,https://oreil.ly/4Ud8P

¹⁹ Martin Kleppmann,《设计数据密集型应用》(加利福尼亚州塞巴斯托波尔:O’Reilly,2017 年),https://oreil.ly/8dzD2

²⁰ Akamai Technologies,《Akamai 在线零售业绩报告:毫秒至关重要》,2017 年 4 月 19 日,https://oreil.ly/bEtRu

²¹ Lucas Bernardi,Themis Mavridis 和 Pablo Estevez,“Booking.com 的 150 个成功机器学习模型:KDD ’19 会议上的 6 个经验教训”,2019 年 8 月 4-8 日,阿拉斯加州安克雷奇,https://oreil.ly/G5QNA

²² “消费者洞察”,Think with Google,https://oreil.ly/JCp6Z

²³ Kleppmann,《设计数据密集型应用》,https://oreil.ly/8dzD2

²⁴ Andrej Karpathy,“构建软件 2.0 堆栈”,Spark+AI Summit 2018,视频,17:54,https://oreil.ly/Z21Oz

²⁵ Khristopher J. Brooks,“研究发现,家庭贷款成本中的种族差异使少数民族损失数百万美元”,CBS 新闻,2019 年 11 月 15 日,https://oreil.ly/UiHUB

²⁶ Cathy O’Neil,《数学毁灭的武器》(纽约:Crown Books,2016 年)。

²⁷ 斯坦福大学人类中心人工智能(HAI),2019 AI 指数报告,2019 年,https://oreil.ly/xs8mG

²⁸ Geoffrey Hinton(@geoffreyhinton)在 2020 年 2 月 20 日的推特,https://oreil.ly/KdfD8

²⁹ 对于某些国家的某些使用案例,用户有“解释权”:即对算法输出给出解释的权利。

³⁰ Stanford HAI, The 2019 AI Index Report.

³¹ Xinyun Chen, Chang Liu, Bo Li, Kimberly Lu, and Dawn Song, “Targeted Backdoor Attacks on Deep Learning Systems Using Data Poisoning,” arXiv, December 15, 2017, https://oreil.ly/OkAjb.

³² 我们将在第七章中涵盖边缘设备。

³³ Jacob Devlin, Ming-Wei Chang, Kenton Lee, and Kristina Toutanova, “BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding,” arXiv, October 11, 2018, https://oreil.ly/TG3ZW.

³⁴ Google Search On, 2020, https://oreil.ly/M7YjM.

第二章:机器学习系统设计介绍

现在我们已经浏览了现实世界中机器学习系统的概述,我们可以进入实际设计机器学习系统的有趣部分。再次强调,机器学习系统设计采用了一种系统化的方法,这意味着我们将全面考虑机器学习系统,以确保所有组成部分——业务需求、数据堆栈、基础设施、部署、监控等等——及其利益相关者能够共同工作,以满足指定的目标和要求。

我们将从讨论目标开始这一章节。在我们开发机器学习系统之前,我们必须理解为什么需要这个系统。如果这个系统是为了业务而建立的,那么它必须以业务目标为驱动,这些目标将需要转化为机器学习目标,以指导机器学习模型的开发。

一旦大家对我们的机器学习系统的目标达成共识,我们将需要制定一些需求来指导该系统的开发。在本书中,我们将考虑四个要求:可靠性、可扩展性、可维护性和可适应性。然后我们将介绍用于设计系统以满足这些要求的迭代过程。

你可能会想:有了这么多目标、要求和流程,我可以开始构建我的机器学习模型了吗?还不太快!在使用机器学习算法解决问题之前,您首先需要将问题框定为机器学习可以解决的任务。我们将继续讨论如何框定您的机器学习问题。根据您如何框定问题,您的工作难度可能会发生显著变化。

由于机器学习是一种数据驱动的方法,如果一本讨论机器学习系统设计的书籍未能讨论数据在机器学习系统中的重要性,那么将是遗漏的。本章的最后部分涉及近年来占据了大量机器学习文献的辩论:什么更重要——数据还是智能算法?

让我们开始吧!

业务与机器学习目标

我们首先需要考虑提议的机器学习项目的目标。在开展机器学习项目时,数据科学家们倾向于关注机器学习目标:他们可以衡量其机器学习模型性能的指标,例如准确率、F1 分数、推断延迟等等。他们对将模型的准确率从 94%提高到 94.2%感到兴奋,并可能投入大量资源——数据、计算和工程时间来实现这一目标。

但事实是:大多数公司不关心花哨的机器学习指标。他们不在乎将模型的准确性从 94%提高到 94.2%,除非这些变化影响了某些业务指标。我在许多短命机器学习项目中看到的一个模式是,数据科学家过于专注于突破机器学习指标,而忽视了业务指标。然而,他们的管理者只关心业务指标,如果看不到机器学习项目如何帮助推动他们的业务指标,就会过早终止项目(并可能解雇涉及的数据科学团队)。¹

那么公司关心哪些指标呢?尽管大多数公司想让你相信其他理由,但根据诺贝尔奖获得者经济学家米尔顿·弗里德曼的说法,企业的唯一目的是为股东最大化利润。²

因此,商业项目的最终目标是直接或间接地增加利润:直接的方式如增加销售(转化率)和降低成本;间接的方式如提高客户满意度和增加在网站上的停留时间。

为了在商业组织内成功实施一个机器学习项目,将机器学习系统的表现与整体业务表现紧密联系起来至关重要。新的机器学习系统应该影响哪些业务绩效指标,例如广告收入的数量,月活跃用户的数量?

想象一下,你在一家关注购买转化率的电子商务网站工作,你希望将推荐系统从批量预测转移到在线预测。³ 你可能会认为在线预测将使得当前用户更相关的推荐变得可能,从而提高购买转化率。你甚至可以进行实验,以显示在线预测可以提高你的推荐系统预测准确性约X%,而在你的网站上,推荐系统预测准确性的每一次百分比增加都导致购买转化率的某种增加。

预测广告点击率和欺诈检测成为当今机器学习最受欢迎的应用案例之一的原因之一是,可以轻松将机器学习模型的表现映射到业务指标:点击率的每次增加都会导致实际广告收入的增加,每次阻止欺诈交易都会导致实际节省的资金。

许多公司创建自己的指标,将业务指标映射到机器学习指标上。例如,Netflix 使用“接受率”来衡量推荐系统的性能:优质播放量除以用户看到的推荐数目。[⁴] 接受率越高,推荐系统越好。Netflix 还将推荐系统的接受率与总流媒体小时数和订阅取消率等其他业务指标联系在一起。他们发现,更高的接受率也导致了更高的总流媒体小时数和较低的订阅取消率。[⁵]

机器学习项目对业务目标的影响可能很难理解。例如,一个为客户提供更个性化解决方案的机器学习模型可能会使客户更满意,进而使他们在你的服务上花更多钱。同样的机器学习模型也可以更快地解决他们的问题,从而使他们在你的服务上花费更少的钱。

要获得关于机器学习指标如何影响业务指标的确切答案,通常需要进行实验。许多公司通过 A/B 测试等实验来进行这些实验,并选择导致更好业务指标的模型,而不管这些模型是否具有更好的机器学习指标。

然而,即使进行了严格的实验,也可能不足以理解机器学习模型的输出与业务指标之间的关系。想象一下,你为一家检测和阻止安全威胁的网络安全公司工作,机器学习只是其复杂流程的一个组成部分。机器学习模型用于检测流量模式中的异常。然后,这些异常经过一套逻辑设置(例如一系列 if-else 语句)分类,以确定它们是否构成潜在威胁。安全专家会审核这些潜在威胁,以确定它们是否真正构成威胁。如果这一过程未能阻止威胁,可能无法确定机器学习组件是否与此有关。

许多公司喜欢说他们在系统中使用机器学习,因为“仅仅是 AI 驱动”就可以帮助他们吸引客户,而不管这部分 AI 是否真正有用。[⁶]

在通过业务视角评估机器学习解决方案时,对预期收益保持现实态度非常重要。由于媒体和有利于机器学习采纳的从业者们的炒作,围绕机器学习存在大量炒作,一些公司可能认为机器学习能够神奇般地在一夜之间改变他们的业务。

神奇地:可能。一夜之间:不可能。

许多公司已经看到了机器学习的回报。例如,ML 已经帮助谷歌改善搜索质量、以更高的价格出售广告、提高翻译质量以及构建更好的安卓应用。但这些收益并非一夜之间实现的。谷歌已经在机器学习领域投资了几十年。

机器学习投资的回报很大程度上取决于采用成熟度阶段。你采用机器学习的时间越长,你的流水线效率就会越高,开发周期就会越快,你需要的工程时间就会越少,云账单也会越低,这些都会导致更高的回报。根据 Algorithmia 在 2020 年进行的调查,对于那些在机器学习采纳方面更为成熟的公司(已经将模型投入生产超过五年),几乎有 75%的公司能够在 30 天内部署一个模型。而那些刚开始启动他们的机器学习流水线的公司中,60%的公司需要超过 30 天来部署一个模型(参见图 2-1)。⁷

图 2-1。公司将模型投入生产所需的时间与其使用机器学习的时间成正比。来源:Algorithmia 图像经过调整

机器学习系统的要求

我们不能说我们已经成功地构建了一个机器学习系统,而不知道这个系统必须满足什么要求。指定机器学习系统的要求因用例而异。然而,大多数系统应该具备这四个特性:可靠性、可扩展性、可维护性和适应性。我们将详细讨论每一个概念。让我们首先仔细看看可靠性。

可靠性

该系统应该在面对逆境(硬件或软件故障,甚至人为错误)时,仍然能够以所需的性能水平继续执行正确的功能。

“正确性”对于机器学习系统可能很难确定。例如,你的系统可能正确调用了预测函数,比如model.predict(),但预测结果却是错误的。如果我们没有地面真实标签来比较,我们如何知道预测是否错误呢?

使用传统软件系统时,通常会收到警告,比如系统崩溃、运行时错误或 404。然而,机器学习系统可能会默默失败。最终用户甚至不知道系统已经失败,可能会继续使用它,就好像它正在工作一样。例如,如果你使用 Google 翻译将一句话翻译成你不懂的语言,你可能很难分辨翻译是否错误。我们将在第八章讨论机器学习系统在生产中的失败情况。

可扩展性

机器学习系统可以通过多种方式增长。它可以在复杂性上增长。去年你使用了一个逻辑回归模型,它适合 Amazon Web Services(AWS)的免费套餐实例,具有 1 GB 的 RAM,但今年,你切换到一个需要 16 GB RAM 才能生成预测的 1 亿参数神经网络模型。

你的机器学习系统可以在流量量上增长。当你开始部署一个机器学习系统时,你每天只处理 1 万个预测请求。然而,随着公司用户基数的增长,你的机器学习系统每天服务的预测请求数量在 100 万到 1000 万之间波动。

机器学习系统可能在 ML 模型数量上增长。最初,您可能只有一个用例的一个模型,例如检测社交网络(如 Twitter)上流行的标签。然而,随着时间的推移,您希望为这个用例添加更多功能,因此您将添加一个用于过滤不安全内容(NSFW)的模型,以及另一个用于过滤机器生成的推文的模型。这种增长模式在面向企业用例的 ML 系统中特别常见。最初,一家初创公司可能只为一个企业客户提供服务,这意味着这家初创公司只有一个模型。然而,随着这家初创公司获得更多客户,他们可能为每个客户都有一个模型。我曾与一家初创公司合作,他们在生产环境中有 8000 个模型,对应其 8000 个企业客户。

无论您的系统如何增长,都应该有合理的处理增长的方式。谈论可扩展性时,大多数人会考虑资源扩展,包括上扩展(扩展资源以处理增长)和下扩展(在不需要时减少资源)。⁸

例如,在高峰期,您的系统可能需要 100 个 GPU(图形处理单元)。然而,大部分时间只需要 10 个 GPU。保持 100 个 GPU 一直开启可能成本高昂,因此您的系统应该能够缩减至 10 个 GPU。

许多云服务中不可或缺的功能是自动扩展:根据使用情况自动增加或减少机器数量。这一功能可能难以实现。即使是亚马逊在 Prime Day 时也遇到了这个问题,导致系统崩溃。据估计,亚马逊每小时的停机时间可能造成 7200 万至 9900 万美元的损失。⁹

然而,应对增长不仅仅是资源扩展,还包括工件管理。管理一百个模型与管理一个模型大不相同。对于一个模型,您可以手动监控其性能并手动更新新数据。由于只有一个模型,您只需拥有一个文件,在需要时帮助您重新生成此模型。然而,对于一百个模型,监控和重新训练方面都需要自动化。您需要一种管理代码生成的方式,以便在需要时能够充分复制模型。

因为可扩展性在整个机器学习项目工作流中非常重要,我们将在本书的不同部分讨论它。具体来说,我们将在章节"分布式训练"中触及资源扩展方面,在章节"模型优化"中,在章节"资源管理"中进行讨论。我们将在章节"实验追踪和版本管理"中讨论工件管理方面,以及在章节"开发环境"中。

可维护性

有许多人会参与 ML 系统的工作。他们是 ML 工程师、DevOps 工程师和主题专家(SME)。他们可能来自非常不同的背景,使用非常不同的编程语言和工具,并可能拥有流程的不同部分。

重要的是要结构化你的工作负载,并建立基础设施,使不同的贡献者可以使用他们熟悉的工具,而不是让一组贡献者强迫其他组使用他们的工具。代码应该被文档化。代码、数据和工件应该被版本化。模型应该具备足够的可再现性,以便即使原始作者不在身边,其他贡献者也能够有足够的背景来构建他们的工作。当出现问题时,不同的贡献者应该能够共同合作,识别问题并实施解决方案,而不是互相指责。

我们将在“团队结构”部分进一步讨论这个问题。

适应性

为了适应数据分布和业务需求的变化,系统应具备一定的能力,既能发现性能改进的方面,又能在不中断服务的情况下进行更新。

因为 ML 系统既是代码部分,又是数据部分,而数据可能会快速变化,所以 ML 系统需要能够快速演变。这与可维护性密切相关。我们将在“数据分布变化”部分讨论数据分布的变化,以及如何在“持续学习”部分不断更新您的模型。

迭代过程

开发一个 ML 系统是一个迭代的过程,在大多数情况下是永无止境的。¹⁰ 一旦系统投入生产,就需要持续监控和更新。

在部署我的第一个 ML 系统之前,我认为这个过程会是线性和简单的。我以为我只需要收集数据、训练模型、部署模型,就完成了。然而,我很快意识到这个过程更像是一个循环,不同步骤之间来回反复。

例如,在构建一个用于预测用户输入搜索查询时是否应显示广告的 ML 模型时,你可能会遇到以下工作流程:¹¹

  1. 选择一个优化指标。例如,你可能想优化印象数——广告显示的次数。

  2. 收集数据并获取标签。

  3. 工程化特征。

  4. 训练模型。

  5. 在错误分析中,你意识到错误是由错误的标签引起的,因此你重新标记数据。

  6. 再次训练模型。

  7. 在错误分析过程中,你意识到你的模型总是预测不应该显示广告,原因是你的数据中有 99.99%的数据都是负标签(不应该显示的广告)。因此,你需要收集更多应该展示的广告数据。

  8. 再次训练模型。

  9. 模型在你现有的测试数据上表现良好,这些数据已经两个月没有更新了。然而,它在昨天的数据上表现不佳。你的模型现在已经过时,因此需要用更新的数据来更新它。

  10. 再次训练模型。

  11. 部署模型。

  12. 模型看起来表现良好,但商业人士却找上门来问为什么收入在减少。原来广告确实展示出来了,但点击率很低。因此,你希望改变你的模型,以优化广告点击率。

  13. 转到步骤 1。

图 2-2 展示了从数据科学家或 ML 工程师的角度看,开发 ML 系统的迭代过程的简化表示。从 ML 平台工程师或 DevOps 工程师的角度来看,这个过程可能会有所不同,因为他们可能没有那么多关于模型开发的背景,并且可能会花更多时间设置基础设施。

图 2-2. 开发 ML 系统的过程更像是一个循环,各个步骤之间来回交替。

后续章节将深入探讨每个步骤在实践中的具体要求。在这里,让我们简要地看看它们的含义:

步骤 1. 项目范围

项目始于明确项目范围,制定目标、目的和限制条件。应识别并参与相关利益相关者。应估算和分配资源。我们已经在第一章中讨论了 ML 项目生产中的不同利益相关者和一些重点。我们也已经在本章前面的商业背景下讨论了如何在 ML 项目中确定范围。我们将在第十一章中讨论如何组织团队以确保 ML 项目的成功。

步骤 2. 数据工程

当今大多数 ML 模型都是从数据中学习的,因此开发 ML 模型始于数据工程。在第三章中,我们将讨论数据工程的基础知识,涵盖了处理来自不同来源和格式的数据。有了原始数据的访问权限,我们希望通过采样和生成标签来筛选训练数据,这在第四章中有所讨论。

步骤 3. ML 模型开发

使用最初的训练数据,我们需要提取特征并开发初步模型,利用这些特征。这是需要最多 ML 知识的阶段,也是 ML 课程中经常涵盖的内容。在第五章中,我们将讨论特征工程。在第六章中,我们将讨论模型选择、训练和评估。

步骤 4. 部署

开发完模型后,需要使其对用户可访问。开发机器学习系统就像写作一样——你永远不会达到系统完成的时刻。但你确实会达到需要将系统投入使用的时刻。我们将在第七章讨论不同的机器学习模型部署方式。

第 5 步。监控与持续学习

模型投入生产后,需要监控其性能衰减,并进行维护以适应不断变化的环境和需求。这一步骤将在第八章和第九章讨论。

第 6 步。业务分析

模型性能需要根据业务目标进行评估,并进行分析以生成业务洞见。这些洞见随后可以用来消除无效项目或规划新项目的范围。这一步与第一步密切相关。

框定机器学习问题

想象一下,你是一家以年轻用户为目标的银行的机器学习工程技术负责人。有一天,你的老板听说竞争对手银行正在利用机器学习加快客户服务支持速度,据说可以使竞争对手银行的客户请求处理速度提高两倍。他命令你的团队也研究利用机器学习来加速你们的客户服务支持。

缓慢的客户支持是一个问题,但这不是一个机器学习的问题。机器学习问题由输入、输出和指导学习过程的目标函数定义——而你老板的请求中这三个组成部分都不明显。作为一名经验丰富的机器学习工程师,你的工作是利用你对机器学习可以解决的问题的了解,将这个请求框定为一个机器学习问题。

调查后,您发现响应客户请求的瓶颈在于将客户请求路由到四个部门中的正确部门:会计、库存、人力资源(HR)和信息技术(IT)。您可以通过开发一个机器学习模型来预测请求应该发送到这四个部门中的哪一个,从而缓解这一瓶颈。这使得它成为一个分类问题。输入是客户请求,输出是请求应该发送到的部门。目标函数是最小化预测部门与实际部门之间的差异。

我们将详细讨论如何从原始数据中提取特征以输入到您的机器学习模型中,具体见第五章。在本节中,我们将重点关注您的模型的输出和指导学习过程的目标函数。

机器学习任务类型

您的模型的输出决定了您的机器学习问题的任务类型。机器学习任务的最常见类型是分类和回归。在分类中,还有更多的子类型,如图 2-3 所示。我们将逐一讨论这些任务类型。

图 2-3。机器学习中常见的任务类型

分类与回归

分类模型将输入分类为不同的类别。例如,你想要将每封电子邮件分类为垃圾邮件或非垃圾邮件。回归模型则输出连续值。一个例子是预测给定房屋价格的房屋预测模型。

回归模型可以轻松地转换为分类模型,反之亦然。例如,房屋预测可以成为分类任务,如果我们将房价量化为诸如小于 10 万美元、10 万至 20 万美元、20 万至 50 万美元等区间,并预测房屋应属于的区间。

如果我们将电子邮件分类模型输出值转化为 0 到 1 之间的值,并决定一个阈值来确定哪些值应为垃圾邮件(例如,如果值大于 0.5,则邮件为垃圾邮件),如图 2-4 所示,邮件分类模型也可以成为回归模型。

图 2-4. 邮件分类任务也可以作为回归任务来构建。

二元分类与多类分类

在分类问题中,类别越少,问题就越简单。最简单的是二元分类,只有两种可能的类别。二元分类的例子包括判断评论是否有毒、肺部扫描是否显示癌症迹象、交易是否欺诈等。目前尚不清楚这种类型的问题在工业界是否普遍存在,因为它们在本质上普遍存在还是因为机器学习从业者更习惯处理这些问题。

当存在两个以上的类别时,问题变为多类分类。处理二分类问题比处理多类问题要简单得多。例如,在只有两个类别时,计算 F1 分数和可视化混淆矩阵更加直观。

当类别数量很高时,比如疾病诊断,疾病数量可能达到上千种,或者产品分类,产品数量可能达到数万种,我们称该分类任务具有高基数。高基数问题可能非常具有挑战性。首先挑战在于数据收集。根据我的经验,机器学习模型通常需要至少 100 个示例来学习分类该类别。因此,如果你有 1000 个类别,你至少需要 10 万个示例。对于稀有类别,数据收集尤其困难。当你有数千个类别时,很可能其中一些是稀有的。

当类别数量较多时,分层分类可能会有用。在分层分类中,首先有一个分类器将每个例子分类为大类中的一类。然后有另一个分类器将这个例子分类为子类中的一类。例如,对于产品分类,可以首先将每个产品分类为四个主要类别之一:电子产品、家居厨房用品、时尚服饰或宠物用品。当产品被分类为某一类别,比如时尚服饰,可以使用另一个分类器将此产品分类为鞋子、衬衫、牛仔裤或配饰中的一种。

多类别与多标签分类

在二元分类和多类别分类中,每个例子属于且仅属于一个类别。当一个例子可以属于多个类别时,我们面临一个多标签分类问题。例如,在构建一个将文章分类为四个主题——技术、娱乐、财经和政治的模型时,一篇文章可以同时属于技术和财经。

解决多标签分类问题的两种主要方法。第一种方法是将其视为多类别分类。在多类别分类中,如果有四个可能的类别 [技术, 娱乐, 财经, 政治],并且一个例子的标签是娱乐,则用向量 [0, 1, 0, 0] 表示此标签。在多标签分类中,如果一个例子同时具有娱乐和财经两个标签,则其标签表示为 [0, 1, 1, 0]。

第二种方法是将其转化为一组二元分类问题。对于文章分类问题,可以有四个模型对应四个主题,每个模型输出文章是否属于该主题。

在所有任务类型中,我通常看到公司最多遇到问题的是多标签分类。多标签意味着一个例子可以具有的类别数从例子到例子不同。首先,这增加了标签注释的难度,因为它增加了我们在第四章讨论的标签多重性问题。例如,一个标注者可能认为一个例子属于两个类别,而另一个标注者可能认为同一个例子只属于一个类别,解决他们的分歧可能很困难。

其次,这种不断变化的类别数量使得从原始概率中提取预测变得困难。考虑将文章分类为四个主题的相同任务。想象一下,给定一篇文章,您的模型输出了这个原始概率分布:[0.45, 0.2, 0.02, 0.33]。在多类别设置中,当您知道一个示例只能属于一个类别时,您简单地选择概率最高的类别,本例中为 0.45。在多标签设置中,因为您不知道一个示例可以属于多少个类别,您可能选择两个最高概率的类别(对应于 0.45 和 0.33)或三个最高概率的类别(对应于 0.45,0.2 和 0.33)。

改变问题框架的多种方式

修改问题框架的方式可能会使问题变得更加困难或更容易。考虑预测用户下一次想使用的应用程序的任务。一个天真的设置可能是将其作为多类别分类任务来框定 - 使用用户和环境的特征(用户人口统计信息,时间,位置,以前使用的应用程序)作为输入,并为用户手机上的每个应用程序输出一个概率分布。让N表示您要考虑向用户推荐的应用的数量。在这种框架中,对于特定用户在特定时间,只有一个要进行的预测,并且预测是大小为N的向量。这种设置在图 2-5 中可视化。

图片

图 2-5. 鉴于预测用户下次最有可能打开的应用程序的问题,您可以将其作为分类问题来框定。输入是用户的特征和环境的特征。输出是手机上所有应用程序的分布。

这是一种不好的方法,因为每当添加新的应用程序时,您可能需要从头开始重新训练模型,或者至少重新训练所有参数数量取决于N的模型组件。一个更好的方法是将其作为回归任务来框定。输入是用户,环境和应用程序的特征。输出是介于 0 和 1 之间的单个值;值越高,用户打开应用的可能性就越大。在这种框架中,对于特定用户在特定时间,有N个要进行的预测,每个应用程序一个,但每个预测只是一个数字。这种改进的设置在图 2-6 中可视化。

图片

图 2-6. 鉴于预测用户下次最有可能打开的应用程序的问题,您可以将其作为回归问题来框定。输入是用户的特征,环境的特征和应用程序的特征。输出是介于 0 和 1 之间的单个值,表示用户在给定上下文中打开应用的可能性有多大。

在这种新的框架中,每当有一个新的应用程序您想考虑推荐给用户时,您只需使用新应用程序的特性来替代旧的输入,而不必从头开始重新训练您的模型或模型的一部分。

目标函数

要学习,ML 模型需要一个目标函数来指导学习过程。[¹²] 目标函数也称为损失函数,因为学习过程的目标通常是通过减少(或优化)由错误预测引起的损失来实现的。对于监督学习,可以通过使用像均方根误差(RMSE)或交叉熵这样的测量方法,通过将模型的输出与地面真实标签进行比较来计算这种损失。

为了说明这一点,让我们再次回到将文章分类为四个主题[技术、娱乐、金融、政治]的前一任务。考虑一篇属于政治类的文章,例如其地面真实标签为[0, 0, 0, 1]。假设给定这篇文章,您的模型输出的原始概率分布是[0.45, 0.2, 0.02, 0.33]。对于这个例子,这个模型的交叉熵损失是相对于[0, 0, 0, 1]的[0.45, 0.2, 0.02, 0.33]的交叉熵。在 Python 中,您可以使用以下代码计算交叉熵:

import numpy as np

def cross_entropy(p, q):
return -sum([p[i] * np.log(q[i]) for i in range(len(p))])

p = [0, 0, 0, 1]
q = [0.45, 0.2, 0.02, 0.33]
cross_entropy(p, q)

通常选择目标函数是很直接的,尽管不是因为目标函数很容易。提出有意义的目标函数需要代数知识,所以大多数 ML 工程师只使用像 RMSE 或 MAE(平均绝对误差)用于回归,逻辑损失(也称为对数损失)用于二元分类,以及交叉熵用于多类分类这样的常见损失函数。

解耦目标

当您希望最小化多个目标函数时,构建 ML 问题可能会很棘手。想象一下,您正在构建一个系统来排列用户新闻提要中的项目。您最初的目标是最大化用户的参与度。您希望通过以下三个目标实现这一目标:

  • 过滤垃圾邮件

  • 过滤成人内容

  • 根据用户的参与度对帖子进行排名:用户点击的可能性有多大

然而,您很快学到,仅优化用户参与度可能会引发疑问的伦理问题。因为极端的帖子往往能获得更多的参与度,您的算法学会了优先考虑极端内容。[¹³] 您希望创建一个更全面的新闻提要。因此,您有了一个新的目标:在最大化用户参与度的同时,最小化极端观点和错误信息的传播。为了实现这个目标,您将两个新目标添加到您的原始计划中:

  • 过滤垃圾邮件

  • 过滤成人内容

  • 过滤错误信息

  • 根据质量对帖子进行排名

  • 根据用户的参与度对帖子进行排名:用户点击的可能性有多大

现在两个目标彼此冲突。如果一篇文章引人入胜,但质量可疑,那么这篇文章应该排名高还是低呢?

目标由目标函数表示。要按质量排列帖子,首先需要预测帖子的质量,希望预测的质量尽可能接近其实际质量。实质上,你希望最小化quality_loss:每篇帖子预测质量与实际质量之间的差异。¹⁴

类似地,要按参与度排列帖子,首先需要预测每篇帖子将获得的点击数。你希望最小化engagement_loss:每篇帖子预测点击数与实际点击数之间的差异。

一种方法是将这两种损失合并为一种损失,并训练一个模型来最小化该损失:

loss = ɑ quality_loss + β engagement_loss

可以随机测试不同的αβ值,找到最有效的值。如果想更系统地调整这些值,可以查看帕累托优化,“涉及多个同时优化的数学优化问题的多目标决策领域”。¹⁵

这种方法的一个问题是,每次调整αβ时——例如,如果用户新闻源的质量提高但用户的参与度降低,你可能希望减少α并增加β——都需要重新训练模型。

另一种方法是训练两个不同的模型,每个模型优化一个损失。因此,你有两个模型:

quality_model

最小化quality_loss并输出每篇帖子的预测质量

engagement_model

最小化engagement_loss并输出每篇帖子预测的点击数

可以结合模型的输出,按其组合分数排列帖子:

ɑ quality_score + β engagement_score

现在,你可以在不重新训练模型的情况下调整αβ

总体而言,在存在多个目标时,首先将它们解耦是一个好主意,因为这样可以更轻松地进行模型开发和维护。首先,可以在不重新训练模型的情况下轻松调整系统,如前所述。其次,由于不同的目标可能需要不同的维护计划,因此在维护方面也更容易。垃圾邮件技术的演变速度远快于帖子质量感知的方式,因此垃圾邮件过滤系统需要比质量排名系统更频繁地进行更新。

心灵与数据

十年来的进展表明,ML 系统的成功在很大程度上取决于其训练数据。大多数公司不是专注于改进 ML 算法,而是专注于管理和改进其数据。¹⁶

尽管使用大量数据的模型取得了成功,但许多人对将数据视为前进的道路持怀疑态度。在我参加的每一场学术会议上,在过去五年中,总是有一些关于心力对抗数据力量的公开辩论。可能被伪装为归纳偏见或智能建筑设计。数据可能与计算一样被归纳到一起,因为更多的数据往往需要更多的计算。

理论上,你可以同时追求建筑设计和利用大数据和计算能力,但花在其中一个方面的时间往往会削弱另一个方面的发展。¹⁷

在“心胜于数据”的阵营中,有图灵奖获得者朱迪亚·珀尔博士,他以因果推断和贝叶斯网络的工作而著称。他的书《为什么》的介绍标题为“心胜于数据”,他在其中强调:“数据非常愚蠢。”在他 2020 年在 Twitter 上的一篇更具争议性的帖子中,他强烈反对依赖大量数据的机器学习方法,并警告称,依赖数据的机器学习人员可能在三到五年内失业:“机器学习在 3-5 年内将不再相同,如果继续遵循当前以数据为中心的范式,机器学习的人可能会过时,甚至失业。请注意。”¹⁸

还有一种温和的观点来自斯坦福人工智能实验室主任克里斯托弗·曼宁教授,他认为大量计算和海量数据加上简单的学习算法会导致非常糟糕的学习者。这种结构使我们能够设计能够从少量数据中学习更多的系统。¹⁹

当今许多机器学习领域的人都支持数据胜于心的观点。阿尔伯塔大学计算科学教授、DeepMind 杰出研究科学家理查德·萨顿教授在一篇博客中写道,选择追求智能设计而非利用计算的研究人员最终会吃到苦果:“从 70 年的人工智能研究中可以得出的最重要的教训是,利用计算的通用方法最终是最有效的,效果要大得多……寻求在短期内取得改进的研究人员试图利用他们对领域的人类知识,但从长远来看,唯一重要的是利用计算。”²⁰

当被问及谷歌搜索表现如此出色的原因时,谷歌搜索质量总监彼得·诺维格强调了在他们成功中大数据的重要性而不是智能算法:“我们并没有更好的算法。我们只有更多的数据。”²¹

Monica Rogati 博士,前 Jawbone 公司的数据副总裁,认为数据是数据科学的基础,如 图 2-7 所示。如果您希望利用数据科学(其中包括机器学习)来改进产品或流程,必须首先建立和扩展您的数据,无论是在质量还是数量上。没有数据,就没有数据科学。

辩论不在于有限数据是否必要,而在于其是否足够。这里的术语有限非常重要,因为如果我们有无限的数据,也许可以查找答案。拥有大量数据不同于拥有无限数据。

图 2-7. 数据科学需求层次结构。来源:根据 Monica Rogati 的图片改编²²

无论最终哪一方会被证明是正确的,都无人能否认数据目前是至关重要的。近几十年来,无论是研究还是行业趋势,都显示出机器学习的成功越来越依赖于数据的质量和数量。模型变得越来越大,并使用越来越多的数据。回到 2013 年,当发布了包含 0.8 亿个标记的一亿字语言建模基准时,人们变得兴奋起来。六年后,OpenAI 的 GPT-2 使用了 100 亿个标记的数据集。又过了一年,GPT-3 使用了 5000 亿个标记的数据集。数据集大小的增长速度如 图 2-8 所示。

图 2-8. 随时间推移语言模型使用的数据集大小(对数刻度)

尽管过去十年中深度学习的许多进展是由于数据量的增加,但更多的数据并不总是会提高模型的性能。低质量的更多数据,例如过时的数据或带有错误标签的数据,甚至可能损害模型的性能。

摘要

我希望本章为您介绍了机器学习系统设计以及在设计机器学习系统时需要考虑的因素。

每个项目都必须从为什么需要这个项目开始,机器学习项目也不例外。我们在本章开始时假设,除非可以推动业务指标,否则大多数企业不关心机器学习指标。因此,如果为企业构建了一个机器学习系统,必须由业务目标驱动,这些目标需要转化为机器学习目标,以指导机器学习模型的开发。

在构建机器学习系统之前,我们需要了解系统需要满足的要求,以被视为良好系统。具体要求因用例而异,在本章中,我们专注于四个最一般的要求:可靠性、可扩展性、可维护性和适应性。满足每个要求的技术将在本书中进行详细介绍。

建立机器学习系统不是一次性任务,而是一个迭代过程。在本章中,我们讨论了开发满足前述要求的机器学习系统的迭代过程。

我们在这一章结束时对数据在机器学习系统中的角色进行了哲学性的讨论。仍然有很多人相信拥有智能算法最终会超过拥有大量数据。然而,包括 AlexNet、BERT 和 GPT 在内的系统的成功表明,过去十年中机器学习的进展依赖于大量数据的获取²⁴。无论数据是否能压倒智能设计,没有人能否认数据在机器学习中的重要性。本书的一个非平凡部分将专注于阐明各种数据问题。

复杂的机器学习系统由更简单的构建块组成。现在我们已经介绍了生产中机器学习系统的高级概述,接下来的章节将详细讨论其构建块,从下一章的数据工程基础开始。如果本章提到的任何挑战对您来说显得抽象,希望以下章节中的具体示例能使其更加具体化。

¹ Eugene Yan 在他的一篇很棒的文章中解释了数据科学家如何理解他们参与的项目的业务意图和背景。

² Milton Friedman,“弗里德曼主义——企业的社会责任是增加其利润”,《纽约时报》杂志,1970 年 9 月 13 日,https://oreil.ly/Fmbem

³ 我们将在第七章中介绍批处理预测和在线预测。

⁴ Ashok Chandrashekar,Fernando Amat,Justin Basilico 和 Tony Jebara,“Netflix 艺术品个性化”,Netflix 技术博客,2017 年 12 月 7 日,https://oreil.ly/UEDmw

⁵ Carlos A. Gomez-Uribe 和 Neil Hunt,“Netflix 推荐系统:算法、商业价值和创新”,《ACM 管理信息系统交易》,2016 年 1 月,13 页,https://oreil.ly/JkEPB

⁶ Parmy Olson,“几乎一半的‘AI 初创企业’在炒作中获利”,福布斯,2019 年 3 月 4 日,https://oreil.ly/w5kOr

⁷ “2020 年企业机器学习现状”,Algorithmia,2020 年,https://oreil.ly/FlIV1

⁸ 上扩展和下扩展是“扩展”的两个方面,与“扩大”不同。扩展是并行添加更多等效功能组件以分散负载。扩大是使组件更大或更快以处理更大的负载(Leah Schoeb,“云可扩展性:扩展与扩大的比较”,Turbonomic Blog,2018 年 3 月 15 日,https://oreil.ly/CFPtb)。

⁹ Sean Wolfe,“亚马逊在 Prime Day 的一小时停机可能导致高达 1 亿美元的销售损失”,Business Insider,2018 年 7 月 19 日,https://oreil.ly/VBezI

¹⁰ 早期评论者指出,这是传统软件的一个特性。

¹¹ 虽然没有介绍祈祷和哭泣,但这些元素贯穿整个过程。

¹² 注意,客观函数是数学函数,与本章前面讨论的业务和 ML 目标不同。

¹³ Joe Kukura,“Facebook 员工加薪背后的‘非常危险’算法偏爱愤怒的帖子”,SFist,2019 年 9 月 24 日,https://oreil.ly/PXtGi;Kevin Roose,“YouTube 极端分子的制造”,纽约时报,2019 年 6 月 8 日,https://oreil.ly/KYqzF

¹⁴ 为简单起见,让我们暂时假设我们知道如何衡量帖子的质量。

¹⁵ 维基百科,“Pareto 优化”,https://oreil.ly/NdApy。此外,您可能还想阅读 Jin 和 Sendhoff 关于将 Pareto 优化应用于 ML 的优秀论文,其中作者声称“机器学习本质上是一个多目标任务”(Yaochu Jin 和 Bernhard Sendhoff,“基于 Pareto 的多目标机器学习:概述和案例研究”,IEEE Transactions on Systems, Man, and Cybernetics—Part C: Applications and Reviews 38, no. 3 [2008 年 5 月],https://oreil.ly/f1aKk)。

¹⁶ Anand Rajaraman,“更多数据通常胜过更好的算法”,Datawocky,2008 年 3 月 24 日,https://oreil.ly/wNwhV

¹⁷ Rich Sutton,“苦涩的教训”,2019 年 3 月 13 日,https://oreil.ly/RhOp9

¹⁸ 2020 年 9 月 27 日,Judea Pearl 博士(@yudapearl)的推文,https://oreil.ly/wFbHb

¹⁹ “深度学习与先验知识”(Chris Manning 与 Yann LeCun 辩论),2018 年 2 月 2 日,视频,1:02:55,https://oreil.ly/b3hb1

²⁰ Sutton,“苦涩的教训”。

²¹ Alon Halevy,Peter Norvig 和 Fernando Pereira,“The Unreasonable Effectiveness of Data”,IEEE Computer Society,2009 年 3 月/4 月,https://oreil.ly/WkN6p

²² Monica Rogati,“The AI Hierarchy of Needs”,Hackernoon Newsletter,2017 年 6 月 12 日,https://oreil.ly/3nxJ8

²³ Ciprian Chelba,Tomas Mikolov,Mike Schuster,Qi Ge,Thorsten Brants,Phillipp Koehn 和 Tony Robinson,“One Billion Word Benchmark for Measuring Progress in Statistical Language Modeling”,arXiv,2013 年 12 月 11 日,https://oreil.ly/1AdO6

²⁴ Alex Krizhevsky, Ilya Sutskever, and Geoffrey E Hinton,“ImageNet Classification with Deep Convolutional Neural Networks”,收录于Advances in Neural Information Processing Systems,第 25 卷,由 F. Pereira,C.J. Burges,L. Bottou 和 K.Q. Weinberger 编辑(Curran Associates,2012),https://oreil.ly/MFYp9;Jacob Devlin,Ming-Wei Chang,Kenton Lee 和 Kristina Toutanova,“BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding”,arXiv,2019 年,https://oreil.ly/TN8fN;“Better Language Models and Their Implications”,OpenAI 博客,2019 年 2 月 14 日,https://oreil.ly/SGV7g

第三章:数据工程基础

近年来 ML 的崛起与大数据的崛起紧密相连。即使没有 ML,大数据系统也是复杂的。如果您没有花费多年时间与它们一起工作,很容易在首字母缩写中迷失方向。这些系统产生许多挑战和可能的解决方案。行业标准(如果有的话)随着新工具的出现和行业需求的扩展而迅速演变,形成一个动态和不断变化的环境。如果您查看不同技术公司的数据堆栈,可能会觉得每个公司都在做自己的事情。

在本章中,我们将介绍数据工程的基础知识,希望能够为您在探索自己需求的领域中提供稳固的基础。我们将从您在典型 ML 项目中可能使用的不同数据来源开始讨论。我们将继续讨论数据可以存储的格式。只有当您打算稍后检索数据时,存储数据才有意义。为了检索存储的数据,重要的是不仅要了解其格式,还要了解其结构。数据模型定义了以特定数据格式存储的数据的结构。

如果数据模型描述了现实世界中的数据,数据库则规定了数据应该如何存储在机器上。我们将继续讨论数据存储引擎,也称为数据库,用于两种主要处理类型:事务处理和分析处理。

在生产中处理数据时,通常会跨多个进程和服务处理数据。例如,您可能有一个特征工程服务,从原始数据计算特征,并且一个预测服务,基于计算出的特征生成预测。这意味着您需要将特征工程服务计算出的特征传递给预测服务。在本章的接下来的部分中,我们将讨论跨进程传递数据的不同模式。

在讨论不同数据传递模式时,我们将了解到两种不同类型的数据:数据存储引擎中的历史数据和实时传输中的流数据。这两种不同类型的数据需要不同的处理范式,我们将在“批处理与流处理”部分讨论。

了解如何收集、处理、存储、检索和处理越来越多的数据对于希望建立生产 ML 系统的人来说至关重要。如果您已经熟悉数据系统,可能希望直接转到第四章,了解如何采样和生成标签以创建训练数据。如果您想从系统的角度了解更多关于数据工程的内容,我推荐 Martin Kleppmann 的优秀著作设计数据密集型应用(O'Reilly 出版,2017 年)。

数据源

机器学习系统可以处理来自许多不同来源的数据。它们具有不同的特征,可用于不同的目的,并且需要不同的处理方法。了解数据来源可以帮助您更有效地使用数据。本节旨在向不熟悉生产数据的人快速概述不同的数据来源。如果您已经在生产中使用机器学习一段时间了,请随意跳过本节。

一个来源是用户输入数据,即用户明确输入的数据。用户输入可以是文本、图像、视频、上传的文件等。如果用户可能输入错误数据,他们就会这样做。因此,用户输入数据很容易格式错误。文本可能过长或过短。在需要数值的地方,用户可能会意外输入文本。如果允许用户上传文件,他们可能会上传格式错误的文件。用户输入数据需要进行更严格的检查和处理。

此外,用户也具有很少的耐心。在大多数情况下,当我们输入数据时,我们希望立即得到结果。因此,用户输入数据往往需要快速处理。

另一个来源是系统生成的数据。这是由系统的不同组件生成的数据,包括各种类型的日志和系统输出,如模型预测。

日志可以记录系统的状态和重要事件,例如内存使用情况,实例数量,调用的服务,使用的软件包等。它们可以记录不同作业的结果,包括用于数据处理和模型训练的大批处理作业。这些类型的日志可以显示系统的运行情况。此可视性的主要目的是用于调试和可能改进应用程序。大多数情况下,您不必查看这些类型的日志,但是在出现问题时它们至关重要。

由于日志是系统生成的,与用户输入数据可能会格式错误的情况相比,它们不太可能格式错误。总体而言,日志不需要在到达时立即处理,就像您希望处理用户输入数据那样。对于许多用例,定期处理日志是可以接受的,例如每小时或甚至每天一次。但是,您可能仍然希望快速处理日志,以便能够检测并在发生有趣事件时收到通知。¹

因为调试机器学习系统很困难,通常的做法是尽可能记录所有内容。这意味着您的日志量可能会非常迅速地增长。这会导致两个问题。第一个问题是很难确定从哪里查找,因为信号可能会淹没在噪音中。已经有很多服务可以处理和分析日志,例如 Logstash、Datadog、Logz.io 等。其中许多服务使用机器学习模型来帮助您处理和理解大量的日志。

第二个问题是如何存储迅速增长的日志数量。幸运的是,在大多数情况下,你只需在它们对于调试当前系统有用的时间内保存日志,并在不再相关时将其丢弃。如果你不经常访问日志,它们也可以存储在低访问存储中,成本远低于高频访问存储。²

系统还生成数据记录用户的行为,如点击、选择建议、滚动、缩放、忽略弹出窗口或在某些页面上花费不寻常的时间。尽管这是系统生成的数据,仍然被视为用户数据的一部分,并可能受到隐私法规的约束。³

公司中由各种服务和企业应用程序生成的内部数据库也存在。这些数据库管理它们的资产,如库存、客户关系、用户等等。这种数据可以直接被机器学习模型使用,或者被机器学习系统的各个组件使用。例如,当用户在亚马逊上输入搜索查询时,一个或多个机器学习模型会处理该查询以检测其意图——如果有人输入“冷冻”,他们是在寻找冷冻食品还是迪士尼的冰雪奇缘系列?——然后亚马逊需要检查其内部数据库以查看这些产品的可用性,然后对它们进行排名并向用户展示。

然后是神奇而古怪的第三方数据世界。第一方数据是你的公司已经收集的关于用户或客户的数据。第二方数据是由另一家公司收集的关于他们自己客户的数据,他们可以向你提供,尽管你可能需要付费。第三方数据公司收集不是他们直接客户的公众数据。

互联网和智能手机的兴起使得所有类型的数据收集变得更加容易。以前在智能手机上尤其容易,因为每部手机都有一个唯一的广告商 ID——iPhone 有苹果的广告标识符(IDFA),而 Android 手机有他们的 Android 广告 ID(AAID)——它作为一个唯一 ID 聚合了手机上的所有活动。来自应用程序、网站、签到服务等的数据被收集并(希望)匿名化,以生成每个人的活动历史。

各种类型的数据都可以购买,例如社交媒体活动、购买历史、网页浏览习惯、汽车租赁以及不同人口统计群体的政治倾向,如男性、年龄在 25 至 34 岁之间、从事技术行业、居住在湾区的人。从这些数据中,你可以推断出喜欢 A 品牌的人也喜欢 B 品牌。这些数据对于诸如推荐系统之类的系统特别有帮助。第三方数据通常在供应商经过清洗和处理后出售。

然而,随着用户对数据隐私的需求增加,公司们一直在采取措施限制广告主 ID 的使用。2021 年初,苹果使其 IDFA 选择加入。这一变化显著减少了 iPhone 上可用的第三方数据量,迫使许多公司更多地关注第一方数据。⁴为了对抗这一变化,广告主一直在投资寻找解决方案。例如,中国广告协会,作为中国广告行业的国家支持的贸易协会,投资于一种名为 CAID 的设备指纹系统,使得像抖音和腾讯这样的应用程序可以继续跟踪 iPhone 用户。⁵

数据格式

一旦您拥有数据,您可能希望将其存储(或者从技术术语上说“持久化”)。由于您的数据来自不同访问模式的多个来源,⁶存储数据并不总是简单的,而且对于某些情况可能会很昂贵。重要的是要考虑数据在未来如何使用,以便选择合适的格式。以下是您可能需要考虑的一些问题:

  • 如何存储多模态数据,例如可能同时包含图像和文本的样本?

  • 我应该将数据存储在哪里,以便既便宜又快速地访问?

  • 如何存储复杂模型以便它们可以在不同硬件上正确加载和运行?

将数据结构或对象状态转换为可以存储或传输并在以后重建的格式的过程称为数据序列化。有许多许多数据序列化格式。在选择要使用的格式时,您可能需要考虑不同的特性,例如人类可读性、访问模式以及它是基于文本还是二进制的,这会影响其文件大小。表 3-1 只包含您在工作中可能遇到的一些常见格式。欲了解更全面的列表,请参阅优秀的维基百科页面“数据序列化格式比较”

表 3-1. 常见数据格式及其使用场景

格式 二进制/文本 人类可读 示例用例
JSON 文本 到处都有
CSV 文本 到处都有
Parquet 二进制 Hadoop, 亚马逊 Redshift
Avro 二进制主要 Hadoop
Protobuf 二进制主要 Google, TensorFlow (TFRecord)
Pickle 二进制 Python, PyTorch 序列化

我们将讨论其中几种格式,首先是 JSON。我们还将讨论两种常见的格式,它们代表了两种不同的范式:CSV 和 Parquet。

JSON

JSON,即 JavaScript 对象表示法,随处可见。尽管它源自 JavaScript,但它与语言无关——大多数现代编程语言都可以生成和解析 JSON。它是人类可读的。它的键-值对范式简单而强大,能够处理不同结构水平的数据。例如,您的数据可以以以下结构化格式存储:

{
  "firstName": "Boatie",
  "lastName": "McBoatFace",
  "isVibing": true,
  "age": 12,
  "address": {
    "streetAddress": "12 Ocean Drive",
    "city": "Port Royal",
    "postalCode": "10021-3100"
  }
}

相同的数据也可以存储在以下非结构化文本块中:

{
  "text": "Boatie McBoatFace, aged 12, is vibing, at 12 Ocean Drive, Port Royal, 
 10021-3100"
}

由于 JSON 是无处不在的,它带来的痛苦也是普遍存在的。一旦您将 JSON 文件中的数据提交给某个模式,要回溯更改该模式就相当痛苦。JSON 文件是文本文件,这意味着它们占用大量空间,正如我们将在 “文本格式与二进制格式” 部分中看到的那样。

行优先格式与列优先格式的比较

两种常见的格式代表两种不同的范例,分别是 CSV 和 Parquet。CSV(逗号分隔值)是行优先的,这意味着同一行内的连续元素在内存中是相邻存储的。Parquet 是列优先的,这意味着同一列内的连续元素在内存中是相邻存储的。

因为现代计算机比非顺序数据更有效地处理顺序数据,如果表是行优先的,预计按行访问数据将比按列访问数据更快。这意味着对于行优先格式,预计按行访问数据将比按列访问数据更快。

想象一下,我们有一个包含 1,000 个示例的数据集,每个示例有 10 个特征。如果我们将每个示例视为一行,每个特征视为一列,这在机器学习中经常发生,那么像 CSV 这样的行优先格式更适合访问示例,例如,访问今天收集到的所有示例。像 Parquet 这样的列优先格式更适合访问特征,例如,访问所有示例的时间戳。参见 图 3-1。

图 3-1. 行优先与列优先格式

列优先格式允许基于列灵活读取数据,特别是当数据量大且有成千上万的特征时。考虑一下,如果你有关于共享乘车交易的数据,该数据有 1,000 个特征,但你只需要 4 个特征:时间、位置、距离、价格。使用列优先格式,你可以直接读取与这四个特征对应的四列数据。然而,使用行优先格式,如果你不知道行的大小,你将不得不读取所有列,然后筛选出这四列数据。即使你知道行的大小,由于需要在内存中跳跃,无法利用缓存,速度仍然可能较慢。

行优先格式允许更快的数据写入。考虑当您不断向数据中添加新的个体示例时的情况。对于每个个体示例,将其写入已经处于行优先格式的文件中会更快。

总体来说,当你需要大量写操作时,行主要格式更好,而当你需要大量基于列的读操作时,列主要格式更好。

注意

我用 CSV 作为行主要格式的示例,因为它很受欢迎,几乎所有我在技术领域交流过的人都能认识。然而,本书的一些早期审阅者指出,他们认为 CSV 是一个糟糕的数据格式。它会将非文本字符序列化得很差。例如,当你将浮点数值写入 CSV 文件时,可能会丢失一些精度 —— 0.12345678901232323 可能会被任意四舍五入为 0.12345678901 —— 正如在 Stack Overflow threadMicrosoft Community thread 中所抱怨的那样。Hacker News 上的一些人则激烈地反对使用 CSV。

文本与二进制格式

CSV 和 JSON 是文本文件,而 Parquet 文件是二进制文件。文本文件是指通常是人类可读的纯文本文件。二进制文件是一个泛指,指的是所有非文本文件。顾名思义,二进制文件通常只包含 0 和 1,并且是为那些知道如何解释原始字节的程序所设计的。如果你在文本编辑器中打开文本文件(例如 VS Code、记事本),你可以读取其中的文本内容。如果你在文本编辑器中打开二进制文件,你会看到一些数字块,可能是十六进制值,对应文件的字节。

二进制文件更紧凑。这里有一个简单的例子来展示二进制文件如何节省空间,与文本文件相比。假设你想存储数字 1000000。如果你将其存储在文本文件中,将需要 7 个字符,如果每个字符占用 1 字节,那么就需要 7 字节。如果你将其存储在二进制文件中作为 int32,只需要 32 位或 4 字节。

作为示例,我使用 interviews.csv,这是一个包含 17,654 行和 10 列的 CSV 文件(文本格式)。当我将其转换为二进制格式(Parquet)时,文件大小从 14 MB 缩小到 6 MB,如 Figure 3-3 所示。

AWS 建议使用 Parquet 格式,因为“Parquet 格式在 Amazon S3 中的卸载速度高达原来的 2 倍,并且比文本格式消耗的存储空间少达 6 倍。”⁸

图 3-3. 当以 CSV 格式存储时,我的面试文件大小为 14 MB。但当以 Parquet 格式存储相同文件时,文件大小为 6 MB。

数据模型

数据模型描述了数据的表示方式。考虑现实世界中的汽车。在数据库中,汽车可以通过其品牌、型号、年份、颜色和价格来描述。这些属性构成了汽车的数据模型。或者,你也可以通过其所有者、车牌号和注册地址历史来描述汽车。这是汽车的另一个数据模型。

您选择如何表示数据不仅影响系统构建的方式,还影响系统能够解决的问题。例如,第一个数据模型中的汽车表示方式使得寻找购买汽车的人变得更容易,而第二个数据模型使得警察追踪罪犯变得更容易。

在本节中,我们将研究看似相反但实际上正在趋同的两种模型:关系模型和 NoSQL 模型。我们将通过示例展示每种模型适合解决的问题类型。

关系模型

关系模型是计算机科学中最持久的理念之一。由埃德加·F·科德在 1970 年发明,⁹ 关系模型至今仍然非常流行,甚至变得越来越受欢迎。这个理念简单而强大。在这个模型中,数据被组织成关系;每个关系是一组元组。表是关系的一种接受的视觉表示,表的每一行构成一个元组,¹⁰ 如图 Figure 3-4 所示。关系是无序的。您可以对关系中的行或列进行重新排序,它仍然是同一个关系。遵循关系模型的数据通常存储在诸如 CSV 或 Parquet 的文件格式中。

图 3-4. 在一个关系中,既不考虑行的顺序也不考虑列的顺序

通常希望关系能够被规范化。数据规范化可以遵循诸如第一范式(1NF)、第二范式(2NF)等正规形式,有兴趣的读者可以在 维基百科 上进一步了解。在本书中,我们将通过一个示例展示规范化的工作原理以及它如何减少数据冗余并提高数据完整性。

考虑到在 Table 3-2 中显示的关系 Book。这些数据中存在大量重复项。例如,行 1 和行 2 几乎相同,只有格式和价格不同。如果出版商信息发生变化——例如,其名称从“香蕉出版社”变为“菠萝出版社”——或者其国家发生变化,我们将不得不更新行 1、2 和 4。如果我们将出版商信息分离到自己的表中,如表 3-3 和 3-4 所示,当出版商信息发生变化时,我们只需更新 Publisher 关系。¹¹ 这种做法使我们能够统一跨不同列中相同值的拼写。它还使得更容易对这些值进行更改,无论是因为这些值发生变化还是当您希望将它们翻译成不同的语言时。

表 3-2. 初始图书关系

标题 作者 格式 出版商 国家 价格
哈利·波特 J.K. 罗琳 平装书 香蕉出版社 英国 $20
哈利·波特 J.K. 罗琳 电子书 香蕉出版社 英国 $10
夏洛克·福尔摩斯 柯南·道尔 平装 Guava Press 美国 $30
霍比特人 J.R.R. 托尔金 平装 Banana Press 英国 $30
夏洛克·福尔摩斯 柯南·道尔 平装 Guava Press 美国 $15

表 3-3. 更新的图书关系

标题 作者 格式 出版商 ID 价格
哈利·波特 J.K. 罗琳 平装 1 $20
哈利·波特 J.K. 罗琳 电子书 1 $10
夏洛克·福尔摩斯 柯南·道尔 平装 2 $30
霍比特人 J.R.R. 托尔金 平装 1 $30
夏洛克·福尔摩斯 柯南·道尔 平装 2 $15

表 3-4. 出版商关系

出版商 ID 出版商 国家
1 Banana Press 英国
2 Guava Press 美国

归一化的一个主要缺点是你的数据现在分布在多个关系中。你可以将不同关系中的数据再次联接在一起,但对于大表来说,联接可能是昂贵的。

建立在关系数据模型之上的数据库称为关系数据库。一旦你把数据放入数据库中,你会想要一种方法来检索它。你可以用来指定从数据库中获取数据的语言称为查询语言。如今最流行的关系数据库查询语言是 SQL。尽管受到关系模型的启发,SQL 背后的数据模型已经偏离了原始的关系模型。例如,SQL 表可以包含行重复,而真实的关系不能包含重复。然而,大多数人安全地忽略了这种细微差别。

关于 SQL 最重要的一点是它是一种声明式语言,与 Python 相对,Python 是一种命令式语言。在命令式范式中,你指定执行一个动作所需的步骤,计算机执行这些步骤以返回输出。在声明式范式中,你指定你想要的输出,计算机则会找出获取查询输出所需的步骤。

在 SQL 数据库中,你指定想要的数据模式——你要从哪些表中获取数据,结果必须满足的条件,基本的数据转换如联接、排序、分组、聚合等——但不指定如何检索数据。由数据库系统决定如何将查询分解为不同部分,使用什么方法执行查询的每个部分,以及应该执行查询的不同部分的顺序。

在特定的增强功能下,SQL 可以是Turing-complete,这意味着理论上 SQL 可以用来解决任何计算问题(不保证时间或内存的需求)。然而,在实践中,编写一个解决特定任务的查询并不总是容易,执行查询也不总是可行或可控的。任何使用 SQL 数据库的人可能对那些极其难以理解且谁都不敢碰的痛苦漫长的 SQL 查询有噩梦般的回忆¹²。

弄清楚如何执行任意查询是难点,这是查询优化器的工作。查询优化器会检查执行查询的所有可能方式,并找到最快的方式来执行¹³。可以使用机器学习来改进基于来访查询的查询优化器¹⁴。查询优化是数据库系统中最具挑战性的问题之一,而归一化意味着数据分布在多个关系上,这使得将其连接在一起变得更加困难。尽管开发查询优化器很难,好消息是通常你只需要一个查询优化器,所有应用程序都可以利用它。

NoSQL

关系数据模型已经能够推广到许多用例,从电子商务到金融再到社交网络。然而,对于某些用例,这种模型可能会有限制。例如,它要求您的数据遵循严格的模式,而模式管理是痛苦的。在 Couchbase 2014 年的一项调查中,对模式管理的挫折感是他们采用非关系数据库的第一原因¹⁶。对于专业应用程序编写和执行 SQL 查询也可能会很困难。

最新的反对关系数据模型的运动是 NoSQL。最初作为一个关于非关系数据库讨论的聚会的标签开始,NoSQL 已经被追溯地重新解释为 Not Only SQL¹⁷,因为许多 NoSQL 数据系统也支持关系模型。两种主要的非关系模型是文档模型和图模型。文档模型针对的是数据以自包含文档形式存在,文档之间的关系很少的用例。而图模型则恰恰相反,针对的是数据项之间关系频繁且重要的用例。我们将分别研究这两种模型,首先从文档模型开始。

文档模型

文档模型围绕“文档”的概念构建。文档通常是一个单一连续的字符串,编码为 JSON、XML 或类似 BSON(二进制 JSON)的二进制格式。文档数据库中的所有文档都假定以相同的格式编码。每个文档都有一个唯一的键,表示该文档,可以用来检索它。

文档的集合可以类比为关系数据库中的表,而文档类比为一行。实际上,你可以通过这种方式将一个关系转换为文档的集合。例如,你可以将表 3-3 中的书籍数据和表 3-4 中的数据转换为三个 JSON 文档,如示例 3-1,3-2 和 3-3 所示。然而,文档的集合比表格更加灵活。表中的所有行必须遵循相同的模式(例如具有相同的列序列),而同一集合中的文档可以具有完全不同的模式。

示例 3-1. 文档 1: harry_potter.json
{
  "Title": "Harry Potter",
  "Author": "J .K. Rowling",
  "Publisher": "Banana Press",
  "Country": "UK",
  "Sold as": [
    {"Format": "Paperback", "Price": "$20"},
    {"Format": "E-book", "Price": "$10"}
  ]
} 

示例 3-2. 文档 2: sherlock_holmes.json
{
  "Title": "Sherlock Holmes",
  "Author": "Conan Doyle",
  "Publisher": "Guava Press",
  "Country": "US",
  "Sold as": [
    {"Format": "Paperback", "Price": "$30"},
    {"Format": "E-book", "Price": "$15"}
  ]
} 

示例 3-3. 文档 3: the_hobbit.json
{
  "Title": "The Hobbit",
  "Author": "J.R.R. Tolkien",
  "Publisher": "Banana Press",
  "Country": "UK",
  "Sold as": [
    {"Format": "Paperback", "Price": "$30"},
  ]
} 

由于文档模型不强制执行模式,因此通常被称为无模式。这是具有误导性的,因为如前所述,存储在文档中的数据将在以后读取。读取文档的应用程序通常假设文档具有某种结构。文档数据库只是将假设结构的责任从写入数据的应用程序转移到读取数据的应用程序。

文档模型比关系模型具有更好的局部性。考虑书籍数据示例,在表 3-3 和表 3-4 中,关于书籍的信息分布在书表和出版商表(可能还有格式表)中。要检索有关书籍的信息,您必须查询多个表。在文档模型中,所有有关书籍的信息都可以存储在一个文档中,这样更容易检索。

然而,与关系模型相比,跨文档执行连接操作更加困难且效率较低。例如,如果你想找出所有价格低于$25 的书籍,你将需要读取所有文档,提取价格,将其与$25 进行比较,并返回所有包含价格低于$25 的书籍的文档。

由于文档和关系数据模型的不同优势,通常在同一数据库系统中为不同任务使用两种模型是常见的。越来越多的数据库系统,如 PostgreSQL 和 MySQL,都支持这两种模型。

图模型

图模型建立在“图”概念周围。图由节点和边组成,其中边表示节点之间的关系。使用图结构存储数据的数据库称为图数据库。如果在文档数据库中,每个文档的内容是优先考虑的,那么在图数据库中,数据项之间的关系是优先考虑的。

因为关系在图模型中被明确地建模,所以基于关系检索数据更快。考虑一个简单图数据库的例子,见图 3-5。这个例子的数据可能来自一个简单的社交网络。在这个图中,节点可以是不同的数据类型:person(人)、city(城市)、country(国家)、company(公司)等。

图 3-5. 一个简单图数据库的示例

假设你想要找到所有在美国出生的人。给定这个图,你可以从节点美国开始,并沿着“within”和“born_in”边遍历图,以找到所有类型为“person”的节点。现在,想象一下,如果我们不使用图模型来表示这些数据,而是使用关系模型。那么,很难编写 SQL 查询来找到所有在美国出生的人,特别是考虑到countryperson之间有未知数量的跳数——在美国和 Zhenzhong Xu 之间有三个跳数,而在美国和 Chloe He 之间只有两个跳数。同样地,对于文档数据库,这种类型的查询也不容易。

许多在一种数据模型中容易处理的查询,在另一种数据模型中就比较困难。选择适合你的应用程序的正确数据模型可以大大简化你的生活。

结构化数据与非结构化数据

结构化数据遵循预定义的数据模型,也称为数据模式。例如,数据模型可以指定每个数据项包含两个值:第一个值“name”是一个最多 50 个字符的字符串,第二个值“age”是一个范围在 0 到 200 之间的 8 位整数。预定义的结构使得你的数据更容易分析。如果你想知道数据库中人们的平均年龄,你只需提取所有年龄值并计算平均值。

结构化数据的缺点在于你必须将数据提交给预定义的模式。如果你的模式发生变化,你将不得不回顾性地更新所有数据,这往往会在过程中引起一些难以解释的错误。例如,以前你从未保存过用户的电子邮件地址,但现在你需要保存,所以你必须回顾性地更新所有先前用户的电子邮件信息。我的一位同事遇到的最奇怪的错误之一是,他们不能再使用用户的年龄与他们的交易,他们的数据模式将所有空年龄替换为 0,他们的机器学习模型认为这些交易是由 0 岁的人完成的。¹⁸

因为业务需求随时间变化,承诺预定义的数据架构可能变得太限制性。或者您可能有来自多个数据源的数据,这些数据超出您的控制范围,不可能使它们遵循相同的架构。这就是非结构化数据变得吸引人的地方。非结构化数据不遵循预定义的数据架构。通常它是文本,但也可以是数字、日期、图像、音频等。例如,您的机器学习模型生成的日志文本文件就是非结构化数据。

即使非结构化数据不遵循架构,它仍然可能包含有助于您提取结构的内在模式。例如,以下文本是非结构化的,但您可以注意到每行包含两个由逗号分隔的值的模式,第一个值是文本,第二个值是数字。但并不保证所有行都必须遵循此格式。即使这行不遵循此格式,您也可以向该文本添加新行。

Lisa, 43
Jack, 23
Huyen, 59

非结构化数据还允许更灵活的存储选项。例如,如果您的存储遵循架构,您只能存储符合该架构的数据。但是如果您的存储不遵循架构,您可以存储任何类型的数据。您可以将所有数据,无论其类型和格式如何,转换为字节串并将它们一起存储。

存储结构化数据的仓库称为数据仓库。存储非结构化数据的仓库称为数据湖。数据湖通常用于在处理之前存储原始数据。数据仓库用于存储已经处理成可用格式的数据。表 3-5 概述了结构化数据和非结构化数据之间的关键差异。

表 3-5. 结构化数据和非结构化数据的关键差异

结构化数据 非结构化数据
架构明确定义 数据无需遵循架构
易于搜索和分析 快速到达
只能处理具有特定架构的数据 可处理来自任何源的数据
架构更改将导致许多问题 无需担心架构更改(至少现在不用),因为这种担心转移到使用这些数据的下游应用程序上了
存储在数据仓库中 存储在数据湖中

数据存储引擎和处理

数据格式和数据模型指定了用户存储和检索数据的接口。存储引擎,也被称为数据库,是数据在机器上存储和检索的实现方式。理解不同类型的数据库很有用,因为你的团队或相邻团队可能需要选择适合你的应用的数据库。

通常,数据库针对两种类型的工作负载进行了优化,即事务处理和分析处理,在这一部分我们将详细介绍它们之间的主要区别。然后我们将介绍您在构建生产中的 ML 系统时必然会遇到的 ETL(抽取、转换、加载)过程的基础知识。

事务性处理和分析处理

传统上,交易指的是买卖行为。在数字世界中,交易指任何类型的行为:发推文、通过共享乘车服务预订车辆、上传新模型、观看 YouTube 视频等等。尽管这些不同的交易涉及不同类型的数据,但它们在应用程序中的处理方式是类似的。交易在生成时插入,当发生变化时偶尔更新,或者在不再需要时删除。¹⁹ 这种处理方式被称为在线事务处理(OLTP)。

因为这些交易经常涉及用户,所以需要快速处理(低延迟),以免让用户等待。处理方法需要具有高可用性——也就是说,处理系统在用户想要进行交易时需要随时可用。如果您的系统无法处理某个交易,该交易将无法进行。

事务性数据库旨在处理在线交易并满足低延迟、高可用性的要求。当人们听到事务性数据库时,他们通常会想到 ACID(原子性、一致性、隔离性、持久性)。以下是对这些术语的定义,以供需要快速回顾的人参考:

原子性

确保交易中的所有步骤作为一个组成功完成。如果交易中的任何一步失败,所有其他步骤也必须失败。例如,如果用户的付款失败,你不希望仍然为该用户分配司机。

一致性

确保所有经过的交易都必须遵循预定义的规则。例如,交易必须由有效用户进行。

隔离性

确保两个交易在同时发生时就像它们被隔离一样。两个访问相同数据的用户不会同时更改它。例如,你不希望两个用户同时预订同一司机。

持久性

确保一旦交易已提交,即使在系统故障的情况下也将保持提交状态。例如,当你订了一辆车但手机没电时,你仍希望你的车能来。

然而,事务性数据库不一定需要遵循 ACID 原则,一些开发人员认为 ACID 过于限制。根据 Martin Kleppmann 的说法,“不符合 ACID 标准的系统有时被称为 BASE,它代表基本上可用软状态最终一致性。这比 ACID 的定义更加模糊。”²⁰

每个事务通常作为一个单独的单位进行处理,与其他事务分开,因此事务性数据库通常是面向行的。这也意味着事务性数据库对于诸如“在旧金山 9 月份所有乘车的平均价格是多少?”这样的问题可能不够高效。这种分析性问题需要跨多行数据的列进行数据聚合。分析性数据库就是为此目的而设计的。它们可以有效地处理允许您从不同视角查看数据的查询。我们称这种处理方式为在线分析处理(OLAP)。

然而,OLTP 和 OLAP 这两个术语都已经过时,如图 3-6 所示,原因有三。首先,事务性和分析性数据库的分离是由技术限制造成的——很难拥有能够有效处理事务性和分析性查询的数据库。然而,这种分离正在逐渐消失。今天,我们有能够处理分析性查询的事务性数据库,例如CockroachDB。我们还有能够处理事务性查询的分析性数据库,例如Apache IcebergDuckDB

图 3-6

图 3-6. 根据Google Trends,截至 2021 年,OLAP 和 OLTP 已经过时。

在传统的 OLTP 或 OLAP 范式中,存储和处理紧密耦合——数据存储方式也是数据处理方式。这可能导致同一数据存储在多个数据库中,并使用不同的处理引擎来解决不同类型的查询。在过去的十年中,一个有趣的范式是将存储与处理(也称为计算)解耦,这被许多数据供应商采纳,包括 Google 的 BigQuery、Snowflake、IBM 和 Teradata。²¹ 在这种范式中,数据可以存储在同一个地方,上面有一个处理层,可以针对不同类型的查询进行优化。

第三,“在线”已经成为一个具有多重含义的过载术语。在线过去只是指“连接到互联网”。然后,它扩展到还意味着“在生产中”—我们说一个功能在部署到生产环境后就是在线的。

在当今的数据世界中,在线可能指的是数据处理和可用性的速度:在线、准在线或离线。根据维基百科,在线处理意味着数据立即可供输入/输出使用。准在线,即近在线,意味着数据不是立即可用,但可以在无需人工干预的情况下快速变为在线状态。离线意味着数据不是立即可用,需要一些人工干预才能变为在线状态。²²

ETL:提取、转换和加载

在关系数据模型的早期阶段,数据大多是结构化的。当数据从不同的源中 提取 出来时,首先需要 转换 成所需的格式,然后再 加载 到目标位置,如数据库或数据仓库。这个过程被称为 ETL,即提取、转换和加载。

即使在机器学习出现之前,ETL 在数据世界也非常流行,对于机器学习应用至今仍然非常重要。ETL 指的是将数据处理和聚合成您所需的形状和格式的通用处理。

Extract 是从所有数据源中提取您想要的数据。其中一些可能已损坏或格式错误。在提取阶段,您需要验证数据并拒绝不符合要求的数据。对于被拒绝的数据,您可能需要通知数据源。由于这是流程的第一步,正确执行可以节省大量的时间。

Transform 是流程的关键部分,大部分数据处理都在这里完成。您可能需要从多个来源连接数据并清理它。您可能需要标准化值范围(例如,一个数据源可能使用“男性”和“女性”,而另一个数据源使用“M”和“F”或“1”和“2”)。您可以应用操作,如转置、去重、排序、聚合、衍生新特征、更多数据验证等。

Load 是决定如何以及多久将您的转换数据加载到目标位置,可以是文件、数据库或数据仓库。

ETL 的概念听起来简单但很强大,并且它是许多组织数据层的基础结构。ETL 过程的概述如 图 3-7 所示。

图 3-7. ETL 过程概述

当互联网首次普及并且硬件变得更加强大时,收集数据突然变得更加容易。数据量迅速增长。不仅如此,数据的性质也发生了变化。数据源的数量扩展了,数据模式也在演变。

由于难以保持数据结构化,一些公司提出了这样的想法:“为什么不将所有数据存储在数据湖中,这样我们就不必处理模式变更了?任何需要数据的应用程序都可以直接从那里提取原始数据并进行处理。” 这种先将数据加载到存储中,然后再进行处理的过程有时称为 ELT(提取、加载、转换)。这种范式允许数据快速到达,因为存储数据之前需要的处理很少。

然而,随着数据的持续增长,这种思路变得不那么吸引人。在大量原始数据中搜索所需数据是低效的。[²³] 与此同时,随着公司转向在云上运行应用程序和基础设施标准化,数据结构也变得标准化。将数据提交到预定义的模式变得更为可行。

随着公司权衡结构化数据与非结构化数据存储的利弊,供应商不断发展,提供结合数据湖的灵活性和数据仓库的数据管理方面的混合解决方案。例如,Databricks 和 Snowflake 都提供数据湖仓库解决方案。

数据流的模式

在本章中,我们讨论了数据格式、数据模型、数据存储以及在单个进程上下文中使用的数据处理。在生产环境中,大多数情况下,并不只有单个进程,而是多个进程同时存在。一个问题是:我们如何在不共享内存的不同进程之间传递数据?

当数据从一个进程传递到另一个进程时,我们称该数据从一个进程流向另一个进程,这形成了一个数据流。数据流有三种主要的模式:

  • 通过数据库传递数据

  • 通过使用 REST 和 RPC API(例如 POST/GET 请求)等请求的服务传递数据

  • 通过实时传输如 Apache Kafka 和 Amazon Kinesis 传递数据

我们将在本节详细介绍每一种模式。

通过数据库传递数据

在两个进程之间传递数据的最简单方法是通过数据库,我们在“数据存储引擎和处理”一节中已经讨论过这一点。例如,要将数据从进程 A 传递给进程 B,进程 A 可以将数据写入数据库,而进程 B 则从该数据库中读取数据。

然而,这种方式并不总是适用,原因有两个。首先,它要求两个进程必须能够访问同一个数据库。这可能是不可行的,特别是如果两个进程由两家不同的公司运行。

其次,它要求两个进程都能够从数据库访问数据,而数据库的读写速度可能较慢,这使得它不适合对延迟要求严格的应用程序,例如几乎所有面向消费者的应用程序。

通过服务传递数据

传递数据的一种方式是通过连接这两个进程的网络直接发送数据。为了将数据从进程 B 传递给进程 A,进程 A 首先向进程 B 发送一个请求,指定 A 需要的数据,然后 B 通过同一网络返回请求的数据。因为进程通过请求进行通信,所以我们称之为请求驱动

此数据传递方式与面向服务的架构密切相关。服务是可通过网络远程访问的进程。在本示例中,B 被公开为 A 可以向其发送请求的服务。为了使 B 能够从 A 请求数据,A 还需要向 B 公开为服务。

两个相互通信的服务也可以由不同公司在不同应用程序中运行。例如,一个服务可能由股票交易所运行,用于跟踪当前股票价格。另一个服务可能由投资公司运行,请求当前股票价格并使用它们来预测未来股票价格。

两个相互通信的服务也可以是同一个应用程序的一部分。将应用程序的不同组件构建为单独的服务允许每个组件独立开发、测试和维护。将应用程序构建为单独的服务提供了微服务架构。

为了将微服务架构置于 ML 系统的背景下,想象你是一名 ML 工程师,为拥有类似 Lyft 的乘车共享应用的公司解决价格优化问题。实际上,Lyft 在其微服务架构中拥有数百个服务,但为了简单起见,我们只考虑三个服务:

司机管理服务

预测在指定区域下一分钟内将有多少司机可用。

乘车管理服务

预测在指定区域下一分钟内将有多少乘车请求。

价格优化服务

预测每次乘车的最佳价格。乘车价格应低到足以使乘客愿意支付,但足够高以使司机愿意开车,并使公司获利。

因为价格取决于供应(可用司机)和需求(请求的乘车),价格优化服务需要来自司机管理和乘车管理服务的数据。每当用户请求乘车时,价格优化服务请求预测的乘车次数和预测的司机数量,以预测此乘车的最佳价格。²⁴

通过网络传递数据的最流行的请求方式包括 REST(表述性状态转移)和 RPC(远程过程调用)。它们的详细分析超出了本书的范围,但一个主要区别在于 REST 设计用于网络请求,而 RPC“试图使对远程网络服务的请求看起来与调用编程语言中的函数或方法相同.”由于这一点,“REST 似乎是公共 API 的主要风格。RPC 框架的主要重点是在同一组织内部服务之间的请求,通常在同一数据中心内。”²⁵

实现 REST 架构的实现被称为 RESTful。尽管许多人将 REST 视为 HTTP,但 REST 并不完全意味着 HTTP,因为 HTTP 只是 REST 的一种实现方式。²⁶

通过实时传输进行数据传递

要理解实时传输的动机,让我们回到前面的例子,即具有三个简单服务(司机管理、乘车管理和价格优化)的乘车共享应用程序。在上一节中,我们讨论了价格优化服务需要从乘车和司机管理服务获取数据,以预测每次乘车的最佳价格。

现在,想象一下司机管理服务还需要从乘车管理服务获取乘车次数,以便知道需要动员多少司机。它还想从价格优化服务那里获取预测价格,作为激励潜在司机的手段(例如,如果现在上路,您可以获得 2 倍的高峰费)。类似地,乘车管理服务可能还需要来自司机管理和价格优化服务的数据。如果我们像前一节讨论的那样通过服务传递数据,那么每个服务都需要向另外两个服务发送请求,如图 3-8 所示。

图 3-8. 在请求驱动的架构中,每个服务需要向另外两个服务发送请求

即使只有三个服务,数据传递已经变得复杂。想象一下像大型互联网公司那样拥有数百甚至数千个服务。服务间的数据传递可能会爆炸,并成为减慢整个系统的瓶颈。

请求驱动的数据传递是同步的:目标服务必须监听请求才能进行请求。如果价格优化服务请求来自司机管理服务的数据,而司机管理服务已经停机,则价格优化服务将继续重新发送请求直至超时。如果价格优化服务在收到响应之前停机,则响应将丢失。一个停机的服务可能导致所有需要其数据的服务停机。

如果有一个协调数据在服务之间传递的代理,会怎么样?而不是让服务直接从彼此请求数据并创建复杂的服务间数据传递网络,每个服务只需与代理通信,如图 3-9 所示。例如,不是让其他服务请求驱动管理服务获取下一分钟的预测驾驶员数量,而是每当驱动管理服务做出预测时,将此预测广播给代理?希望从驱动管理服务获取数据的任何服务都可以检查代理,获取最新的预测驾驶员数量。同样,每当价格优化服务对下一分钟的高峰费用做出预测时,也会将此预测广播给代理。

图 3-9. 有了代理,服务只需与代理通信,而不是与其他服务通信。

从技术上讲,数据库可以是代理——每个服务可以将数据写入数据库,需要数据的其他服务可以从该数据库读取数据。然而,如“通过数据库传递数据”一节所述,从数据库读取和写入对于具有严格延迟要求的应用程序来说太慢了。我们使用内存存储而不是数据库来代理数据。实时传输可以被看作是服务之间数据传递的内存存储。

广播到实时传输的一段数据称为事件。因此,这种架构也被称为事件驱动。实时传输有时也称为事件总线。

对于更多依赖逻辑而不是数据的系统,请求驱动的架构效果良好。对于数据密集型系统,事件驱动的架构更加合适。

两种最常见的实时传输类型是发布订阅(pubsub)和消息队列。在发布订阅模型中,任何服务都可以发布到实时传输的不同主题,订阅主题的任何服务都可以读取该主题中的所有事件。生成数据的服务不关心哪些服务消费它们的数据。发布订阅解决方案通常有保留策略——数据将在实时传输中保留一段时间(例如七天),然后被删除或移动到永久存储(如 Amazon S3)。参见图 3-10。

图 3-10. 进入的事件存储在内存存储中,然后被丢弃或移到更持久的存储中。

在消息队列模型中,事件通常有预期的消费者(带有预期消费者的事件称为消息),消息队列负责将消息传递给正确的消费者。

发布订阅解决方案的例子有 Apache Kafka 和 Amazon Kinesis。消息队列的例子有 Apache RocketMQ 和 RabbitMQ。在过去几年中,这两种范式都获得了很大的关注。图 3-11 显示了一些使用 Apache Kafka 和 RabbitMQ 的公司。

图 3-11. 使用 Apache Kafka 和 RabbitMQ 的公司。来源:来自Stackshare 的截图

批处理与流处理的比较

一旦您的数据到达像数据库、数据湖或数据仓库这样的数据存储引擎,它就变成了历史数据。这与流数据(仍在流动的数据)相对。历史数据通常在批处理作业中进行处理,这些作业定期启动。例如,每天一次,您可能希望启动一个批处理作业来计算最近一天内所有乘车的平均激增费用。

当数据在批处理作业中处理时,我们称之为批处理。批处理已经是研究的主题已有数十年之久,公司已经提出了像 MapReduce 和 Spark 这样的分布式系统来有效处理批量数据。

当你拥有像 Apache Kafka 和 Amazon Kinesis 这样的实时传输数据时,我们称之为流数据。流处理 指的是对流数据进行计算。流数据上的计算也可以定期启动,但这些周期通常比批处理作业的周期要短得多(例如,每五分钟而不是每天)。流数据上的计算也可以在需要时随时启动。例如,每当用户请求搭乘时,您可以处理数据流以查看当前可用的司机。

当正确进行流处理时,可以实现低延迟,因为您可以在生成数据后立即处理数据,而无需首先将其写入数据库。许多人认为流处理比批处理效率低,因为您不能利用像 MapReduce 或 Spark 这样的工具。但并非总是如此,原因有两点。首先,像 Apache Flink 这样的流处理技术已被证明具有高可伸缩性和完全分布式,这意味着它们可以并行进行计算。其次,流处理的优势在于状态计算。考虑这样一种情况:您希望处理 30 天试用期间的用户参与情况。如果每天启动这个批处理作业,您将不得不每天在过去 30 天内进行计算。而使用流处理,可以只计算每天的新数据,并将新数据的计算与旧数据的计算结合起来,避免冗余计算。

因为批处理比流处理频率低得多,在机器学习中,通常用于计算不经常变化的特征,比如司机的评分(如果司机完成了数百次乘车,其评分不太可能在一天之内发生显著变化)。通过批处理提取的特征称为批量特征,也被称为静态特征

流处理用于计算变化快速的特征,例如当前有多少司机可用,过去一分钟内有多少乘车请求,接下来两分钟内会完成多少乘车,这个区域最近 10 次乘车的中位数价格等。这些关于系统当前状态的特征对于进行最佳价格预测非常重要。通过流处理提取的特征称为流特征,也被称为动态特征

对于许多问题,您不仅需要批量特征或流特征,而是两者兼备。您需要能够处理流数据和批量数据并将它们结合起来输入到机器学习模型中的基础设施。我们将在第七章进一步讨论如何将批处理特征和流处理特征结合使用以生成预测。

要对数据流进行计算,您需要一个流计算引擎(就像 Spark 和 MapReduce 是批量计算引擎一样)。对于简单的流计算,您可能可以利用实时传输工具如 Apache Kafka 的内置流计算能力,但是 Kafka 流处理在处理各种数据源时存在一定限制。

对于利用流特征的机器学习系统,流计算很少是简单的。在应用程序中使用的流特征数量,如欺诈检测和信用评分,可能达到数百甚至数千个。流特征提取逻辑可能需要使用不同维度的联接和聚合进行复杂查询。要提取这些特征需要高效的流处理引擎。为此,您可能希望研究类似 Apache Flink、KSQL 和 Spark Streaming 的工具。在这三个引擎中,Apache Flink 和 KSQL 在行业中更为认可,并为数据科学家提供了良好的 SQL 抽象。

流处理更加困难,因为数据量是无界的,数据以可变的速率和速度进入。让流处理器执行批处理比让批处理器执行流处理更容易。Apache Flink 的核心维护者多年来一直在争论批处理是流处理的一个特例。²⁸

总结

本章建立在第二章中关于数据在开发 ML 系统中的重要性的基础上。在本章中,我们了解到选择正确的格式存储数据对将来使用数据更加容易是很重要的。我们讨论了不同的数据格式以及行优先与列优先格式以及文本与二进制格式的优缺点。

我们继续涵盖三种主要数据模型:关系型、文档型和图形型。尽管关系模型是最为人所知的,鉴于 SQL 的流行,但今天所有三种模型都被广泛应用,每种模型都适用于一定范围的任务。

当谈到关系模型相对于文档模型时,许多人认为前者是结构化的,后者是非结构化的。结构化和非结构化数据之间的区分是相当模糊的——主要问题是谁必须承担假设数据结构的责任。结构化数据意味着编写数据的代码必须假设结构。非结构化数据意味着读取数据的代码必须假设结构。

我们继续本章讨论数据存储引擎和处理。我们研究了针对两种不同类型数据处理进行优化的数据库:事务处理和分析处理。我们一起研究了数据存储引擎和处理,因为传统上存储与处理是耦合在一起的:用于事务处理的事务性数据库和用于分析处理的分析数据库。然而,在近年来,许多供应商已经致力于解耦存储和处理。今天,我们有能够处理分析查询的事务性数据库以及能够处理事务查询的分析数据库。

当讨论数据格式、数据模型、数据存储引擎和处理时,假定数据在一个过程中。然而,在生产中工作时,您可能会与多个进程一起工作,并且可能需要在它们之间传输数据。我们讨论了三种数据传递模式。最简单的模式是通过数据库传递。用于进程的数据传递最流行的模式是通过服务传递数据。在这种模式中,一个进程被公开为另一个进程可以请求数据的服务。这种数据传递模式与微服务架构紧密耦合,其中应用程序的每个组件都设置为一个服务。

过去十年中变得越来越流行的一种数据传递模式是通过类似 Apache Kafka 和 RabbitMQ 的实时传输传递数据。这种数据传递模式介于通过数据库传递和通过服务传递之间:它允许异步数据传递,延迟相当低。

由于实时传输中的数据与数据库中的数据具有不同的属性,它们需要不同的处理技术,如在“批处理与流处理对比”章节讨论的那样。数据库中的数据通常通过批处理作业处理并生成静态特征,而实时传输中的数据通常使用流计算引擎处理并生成动态特征。有人认为批处理是流处理的一个特例,流计算引擎可以统一这两种处理流水线。

一旦我们解决了我们的数据系统问题,我们就可以收集数据并创建训练数据,这将是下一章的重点。

¹ “在生产环境中‘有趣’通常意味着灾难性的事情,比如崩溃或者你的云账单飙升到天文数字。”

² 截至 2021 年 11 月,AWS S3 标准存储选项允许您在毫秒级的延迟内访问数据,每 GB 的成本约为 S3 冰川的五倍,后者允许您在 1 分钟至 12 小时的延迟内检索数据。

³ 一位机器学习工程师曾经对我说,他的团队只使用用户的历史产品浏览和购买记录来推荐他们可能希望看到的内容。我回答道:“所以你根本不使用个人数据?”他看着我,一脸困惑。“如果你指的是用户的年龄、位置等人口统计数据,那么不,我们不使用。但我认为一个人的浏览和购买活动是非常私密的。”

⁴ John Koetsier,“苹果刚刚削弱了 IDFA,将一个价值 800 亿美元的行业推向混乱”,福布斯,2020 年 6 月 24 日,https://oreil.ly/rqPX9

⁵ Patrick McGee 和 Yuan Yang,“TikTok 希望通过国家支持的方法继续跟踪 iPhone 用户”,Ars Technica,2021 年 3 月 16 日,https://oreil.ly/54pkg

⁶ “访问模式”指系统或程序读取或写入数据的模式。

⁷ 欲了解更多关于 pandas 的特性,请查看我的Just pandas Things GitHub 仓库。

⁸ “宣布亚马逊 Redshift 数据湖导出:以 Apache Parquet 格式共享数据”,亚马逊 AWS,2019 年 12 月 3 日,https://oreil.ly/ilDb6

⁹ Edgar F. Codd,“大型共享数据银行的数据关系模型”,ACM 通讯,1970 年 6 月,第 13 卷,第 6 期:377–87。

¹⁰ 对于注重细节的读者,不是所有的表都是关系表。

¹¹ 您可以进一步将书籍关系进行归一化,例如将格式分离到一个单独的关系中。

¹² 原 Postgres 论文的合著者 Greg Kemnitz 在Quora 分享,曾经写过一个包含 700 行的报告 SQL 查询,涉及 27 个不同表的查找或连接。该查询有大约 1,000 行的注释,帮助他记住自己的操作。他花了三天时间来撰写、调试和优化这个查询。

¹³ Yannis E. Ioannidis, “查询优化,” ACM Computing Surveys(CSUR)28 卷 1 期(1996 年):121-23,oreil.ly/omXMg

¹⁴ Ryan Marcus 等,“Neo:一个学习的查询优化器,” arXiv预印本 arXiv:1904.03711(2019 年),oreil.ly/wHy6p

¹⁵ Matthias Boehm, Alexandre V. Evfimievski, Niketan Pansare, 和 Berthold Reinwald, “声明式机器学习——基本属性和类型分类,” arXiv, 2016 年 5 月 19 日,oreil.ly/OvW07

¹⁶ James Phillips, “我们 NoSQL 采用调查中的惊喜,” Couchbase, 2014 年 12 月 16 日,oreil.ly/ueyEX

¹⁷ Martin Kleppmann,《设计数据密集型应用》(oreil.ly/fniWG)(Sebastopol, CA: O’Reilly, 2017)。

¹⁸ 在这个具体的例子中,用-1 替换空年龄值解决了问题。

¹⁹ 本段以及本章的许多部分受 Martin Kleppmann 的《设计数据密集型应用》(oreil.ly/f9C48)启发。

²⁰ Kleppmann,《设计数据密集型应用》(oreil.ly/LFHN5)。

²¹ Tino Tereshko, “在 BigQuery 中分离存储和计算,” Google Cloud 博客,2017 年 11 月 29 日,oreil.ly/utf7z;Suresh H., “Snowflake 架构与关键概念:一份全面的指南,” Hevo 博客,2019 年 1 月 18 日,oreil.ly/GyvKl;Preetam Kumar, “切断电缆:使用对象存储在数据湖中分离数据与计算,” IBM 博客,2017 年 9 月 21 日,oreil.ly/Nd3xD;“分离云计算和云存储的威力,” Teradata,最后访问于 2022 年 4 月,oreil.ly/f82gP

²² Wikipedia, s.v. “Nearline storage,” 最后访问于 2022 年 4 月,oreil.ly/OCmiB

²³ 在本书的初稿中,我将成本作为不应存储所有内容的原因。然而,如今存储成本如此低廉,以至于存储成本很少是一个问题。

²⁴ 实际上,价格优化可能不需要每次都请求预测的乘车次数/司机数量来进行价格预测。使用缓存的预测乘车次数/司机数量并每分钟或更频繁地请求新的预测是常见做法。

²⁵ Kleppmann,《设计数据密集型应用》(链接)。

²⁶ Tyson Trautmann,《“揭秘 RPC 和 REST 的神话”》,Ethereal Bits,2012 年 12 月 4 日(通过互联网档案馆访问),链接

²⁷ 如果你想了解更多关于 Apache Kafka 如何工作的信息,Mitch Seymour 有一个很棒的动画,用水獭来解释它!

²⁸ Kostas Tzoumas,《“批处理是流处理的一种特例”》,Ververica,2015 年 9 月 15 日,链接

第四章:训练数据

在第三章中,我们从系统的角度讨论了如何处理数据。在这一章中,我们将从数据科学的角度讨论如何处理数据。尽管训练数据在开发和改进机器学习模型中非常重要,但许多教育课程都过分偏向于建模,而这被许多从业者认为是整个过程中最“有趣”的部分。构建一个最先进的模型确实很有趣。但是花费数日处理海量格式不正确的数据,甚至不能适应你机器内存的数据,则是非常令人沮丧的。

数据混乱、复杂、难以预测,可能会引发危险。如果处理不当,它很容易将整个机器学习操作陷入困境。但这正是为什么数据科学家和机器学习工程师应该学会如何处理数据的原因,这将在未来节省我们大量时间和头疼的根源。

在这一章中,我们将讨论获取或创建良好训练数据的技术。在本章中,训练数据包括机器学习模型开发阶段使用的所有数据,包括用于训练、验证和测试的不同拆分(训练、验证、测试拆分)。本章从选择训练数据的不同采样技术开始。然后,我们将讨论创建训练数据中的常见挑战,包括标签多样性问题、缺乏标签问题、类别不平衡问题以及解决缺乏数据问题的数据增强技术。

我们使用术语“训练数据”而不是“训练数据集”,因为“数据集”表示一个有限且静态的集合。生产中的数据既不是有限的,也不是静态的,这是我们将在“数据分布转移”一节中讨论的现象。像建立机器学习系统的其他步骤一样,创建训练数据是一个迭代的过程。随着项目生命周期中模型的演变,你的训练数据也很可能会发生变化。

在我们继续之前,我想再次强调一句话,虽然已经说了很多次,但还是不够。数据充满了潜在的偏见。这些偏见可能有很多原因。在收集、采样或标记过程中可能产生偏见。历史数据可能被人类偏见所影响,而在这些数据上训练的机器学习模型可能会延续这些偏见。使用数据,但不要过于信任它!

采样

采样是机器学习工作流程的一个重要组成部分,但在典型的机器学习课程中往往被忽视。在机器学习项目生命周期的许多步骤中都会进行采样,例如从所有可能的真实世界数据中创建训练数据;从给定数据集中进行采样以创建用于训练、验证和测试的拆分;或者从发生在您的机器学习系统内的所有可能事件中进行采样以进行监控。在本节中,我们将重点讨论用于创建训练数据的采样方法,但这些采样方法也可以用于机器学习项目生命周期的其他步骤。

在许多情况下,采样是必要的。一种情况是当您无法访问真实世界中的所有可能数据时,用于训练模型的数据是真实世界数据的一个子集,由某种采样方法创建而成。另一种情况是当处理您可以访问的所有数据变得不可行时——因为它需要太多时间或资源——因此您必须对这些数据进行采样,以创建一个可行处理的子集。在许多其他情况下,采样是有帮助的,因为它可以让您更快速、更便宜地完成任务。例如,考虑一个新模型时,您可能希望先用数据的一个小子集进行快速实验,以查看新模型是否有前途,然后再对所有数据进行训练。¹

理解不同的采样方法及其在工作流程中的应用可以首先帮助我们避免潜在的采样偏差,其次可以帮助我们选择提高我们所采样数据效率的方法。

有两大类采样方法:非概率抽样和随机抽样。我们将从非概率抽样方法开始,然后介绍几种常见的随机抽样方法。

非概率抽样

非概率抽样是指数据选择不基于任何概率标准。以下是一些非概率抽样的标准:

便利抽样

根据数据的可用性选择数据样本。这种采样方法很受欢迎,因为它很方便。

雪球抽样

未来的样本是根据现有样本选择的。例如,为了在没有访问 Twitter 数据库的情况下抓取合法的 Twitter 账户,您从少量账户开始,然后抓取他们关注的所有账户,依此类推。

判断抽样

专家决定包括哪些样本。

配额抽样

您根据特定数据切片的配额选择样本,而无需任何随机化。例如,在进行调查时,您可能希望从每个年龄组获取 100 个回应:30 岁以下、30 到 60 岁之间和 60 岁以上,而不考虑实际年龄分布。

由非概率标准选取的样本并不代表真实世界的数据,因此充满了选择偏差。² 因为这些偏差,您可能会认为使用这类抽样方法来训练机器学习模型是个坏主意。您是对的。不幸的是,在许多情况下,选择用于机器学习模型的数据仍然是出于便利性考虑。

一个这类情况的例子是语言建模。语言模型通常不是用代表所有可能文本的数据进行训练的,而是用可以轻松收集到的数据——维基百科、Common Crawl、Reddit。

另一个例子是对一般文本进行情感分析的数据。这些数据很大一部分来自于具有自然标签(评分)的来源,如 IMDB 评论和亚马逊评论。然后,这些数据集被用于其他情感分析任务。IMDB 评论和亚马逊评论偏向于愿意在线留下评论的用户,不一定代表那些没有互联网访问权限或者不愿意在线发表评论的人群。

第三个例子是用于训练自动驾驶汽车的数据。最初,用于自动驾驶汽车的数据主要来自于两个地区:亚利桑那州凤凰城(因其宽松的法规)和加利福尼亚州的湾区(因为许多自动驾驶汽车公司在这里设立)。这两个地区通常天气晴朗。2016 年,Waymo 将其业务扩展到华盛顿州柯克兰,特别是因为柯克兰的多雨天气,³ 但仍然有更多晴天自动驾驶汽车数据,而不是雨天或雪天的数据。

非概率抽样可以是快速和简便的方式来收集您的初始数据,以推动您的项目的启动。然而,为了可靠的模型,您可能希望使用基于概率的抽样,我们将在下文介绍。

简单随机抽样

在最简单形式的随机抽样中,你给予种群中所有样本相等的被选中概率。⁴ 例如,你随机选择种群的 10%,给予所有这个种群成员相等的 10%被选中的机会。

这种方法的优点是易于实施。缺点是稀有类别的数据可能不会出现在您的选择中。考虑一个情况,一个类别仅在您的数据种群中出现了 0.01%。如果您随机选择了 1%的数据,这个稀有类别的样本可能不会被选择。基于这种选择训练的模型可能会认为这个稀有类别不存在。

分层抽样

为了避免简单随机抽样的缺点,你可以先将总体分成你关心的各组,并分别从每组中抽样。例如,要从有两类 A 和 B 的数据中抽取 1%的数据,你可以分别从类 A 和类 B 中各抽取 1%。这样,无论类 A 或类 B 有多么稀少,你都能确保从中抽到样本。每组称为一个层,这种方法称为分层抽样。

这种抽样方法的一个缺点是并非总是可行,比如当将所有样本分成组是不可能的时候。这在某些多标签任务中尤其具有挑战性⁵,例如,一个样本可能同时属于类 A 和类 B。

加权抽样

在加权抽样中,每个样本都被赋予一个权重,这决定了它被选择的概率。例如,如果你有三个样本,A、B 和 C,并希望它们的被选中概率分别为 50%、30%和 20%,那么你可以给它们分别赋予权重 0.5、0.3 和 0.2。

这种方法允许你利用领域专业知识。例如,如果你知道数据的某个子群体,比如最近的数据,对你的模型更有价值,并希望它有更高的被选中概率,你可以给它更高的权重。

这也有助于当你的数据与真实数据的分布不同时的情况。例如,如果在你的数据中,红色样本占 25%,蓝色样本占 75%,但你知道在真实世界中,红色和蓝色发生的概率是相等的,那么你可以给红色样本的权重高出蓝色样本三倍。

在 Python 中,你可以使用random.choices来进行加权抽样,方法如下:

# Choose two items from the list such that 1, 2, 3, 4 each has
# 20% chance of being selected, while 100 and 1000 each have only 10% chance.
import random
random.choices(population=[1, 2, 3, 4, 100, 1000],
               weights=[0.2, 0.2, 0.2, 0.2, 0.1, 0.1],
               k=2)
# This is equivalent to the following
random.choices(population=[1, 1, 2, 2, 3, 3, 4, 4, 100, 1000],
               k=2)

在机器学习中一个常见的概念,与加权抽样密切相关的是样本权重。加权抽样用于选择用来训练模型的样本,而样本权重用于给训练样本分配“权重”或“重要性”。具有更高权重的样本更显著地影响损失函数。改变样本权重可以显著改变模型的决策边界,如图 4-1 所示。

图 4-1。样本权重可以影响决策边界。左边是所有样本被赋予相等权重时的情况。右边是样本被赋予不同权重时的情况。来源:scikit-learn⁶

水库抽样

水库抽样是一种非常有趣的算法,特别适用于处理流数据,这通常是生产环境中的数据。

想象一下,你有一系列即将到来的推文,你想要随机抽取一定数量的推文进行分析或模型训练。你不知道有多少推文,但是你知道你无法将它们全部存储在内存中,这意味着你无法预先知道应该选择推文的概率。你想要确保:

  • 每条推文被选中的概率是相等的。

  • 你可以随时停止算法,推文将按正确的概率进行抽样。

这个问题的一个解决方案是蓄水池抽样。该算法涉及一个蓄水池,可以是一个数组,并包括三个步骤:

  1. 将前 k 个元素放入蓄水池中。

  2. 对于每个到来的 n^(th) 元素,生成一个随机数 i,使得 1 ≤ in

  3. 如果 1 ≤ ik:用 n^(th) 元素替换蓄水池中的 i^(th) 元素。否则,不执行任何操作。

这意味着每个到来的 n^(th) 元素都有 k n 的概率进入蓄水池。你也可以证明蓄水池中的每个元素有 k n 的概率存在于那里。这意味着所有样本被选中的概率是相等的。如果我们在任何时候停止算法,蓄水池中的所有样本都以正确的概率被抽样到。图 4-2 展示了蓄水池抽样工作原理的一个示例。

图 4-2. 演示了蓄水池抽样的工作原理

重要性抽样

重要性抽样是最重要的抽样方法之一,不仅在机器学习中如此。它允许我们在只有另一种分布的访问权限时,从一个分布中进行抽样。

想象一下,你需要从分布 P(x) 中抽样 x,但 P(x) 的抽样可能非常昂贵、缓慢或不可行。但是,你有一个分布 Q(x),它更容易抽样。因此,你从 Q(x) 中抽样 x,并用 P(x) Q(x) 加权这个样本。Q(x) 被称为提议分布重要性分布。只要 P(x) ≠ 0,Q(x) 就可以是任何分布。下面的方程显示了期望中,从 P(x) 中抽样的 x 等于从 Q(x) 中抽样的 x,乘以 P(x) Q(x) 的权重:

E P(x) [ x ] = x P ( x ) x = x Q ( x ) x P(x) Q(x) = E Q(x) [ x P(x) Q(x) ]

在机器学习中使用重要性采样的一个例子是基于策略的强化学习。考虑这样一种情况,当你想要更新你的策略时。你希望估算新策略的值函数,但是计算采取行动后的总奖励可能很昂贵,因为这需要考虑到所有可能的结果,直到时间顶点结束。然而,如果新策略与旧策略相对接近,你可以基于旧策略计算总奖励,并根据新策略对其进行重新加权。旧策略的奖励构成提案分布。

标签化

尽管无监督机器学习有很大潜力,但今天大多数生产中的机器学习模型仍然是有监督的,这意味着它们需要有标记的数据来学习。机器学习模型的性能仍然严重依赖于它们所训练的有标记数据的质量和数量。

在一次与我的学生交谈中,特斯拉人工智能主管安德烈·卡帕西分享了一个轶事。他决定建立一个内部标注团队时,他的招聘人员问他需要这支团队多久。他回答道:“我们需要工程团队多久?”数据标注已经从辅助任务发展成为许多生产中机器学习团队的核心功能。

在本节中,我们将讨论获取数据标签的挑战。我们首先将讨论数据科学家在谈论标签时通常首先考虑的标注方法:手动标注。然后我们将讨论自然标签任务,即从系统中推断标签的任务,而无需人类注释,接着是在自然标签和手动标签都缺失时应采取的措施。

手动标签

任何曾经在生产中处理数据的人都可能从直观上感受到这一点:获取数据的手动标签因为种种原因非常困难。首先,手动标注数据可能非常昂贵,特别是如果需要专业的主题专家知识。例如,要分类评论是否为垃圾评论,您可以在众包平台上找到 20 名标注者,并在 15 分钟内训练他们标记您的数据。但是,如果您想要标记胸部 X 光片,您就需要找到经过认证的放射科医生,他们的时间有限且昂贵。

其次,手动标注对数据隐私构成威胁。手动标注意味着有人需要查看您的数据,如果您的数据具有严格的隐私要求,这并不总是可能的。例如,您不能仅仅将患者的医疗记录或公司的机密财务信息发送给第三方服务进行标注。在许多情况下,您的数据甚至可能不允许离开您的组织,您可能需要雇佣或签约标注员在您的数据上标记。

其次,手动标记速度慢。例如,精确转录语音发音的音素级别可能比发音持续时间长 400 倍。⁷ 因此,如果你想要标注 1 小时的语音,一个人可能需要花费 400 个小时,或者几乎 3 个月的时间。在一项研究中,为了帮助从 X 射线中分类肺癌,我的同事们不得不等待将近一年才能获得足够的标签。

缓慢的标记速度导致迭代速度慢,使得你的模型难以适应不断变化的环境和需求。如果任务或数据发生变化,你必须等待数据重新标记才能更新模型。想象一下这样的场景:你有一个情感分析模型,用于分析提到你品牌的每条推文的情感。它只有两个类别:负面和正面。然而,在部署后,你的公关团队意识到最大的损害来自愤怒的推文,并希望更快地回应愤怒的消息。因此,你需要更新你的情感分析模型,增加三个类别:负面、正面和愤怒。为此,你需要再次查看你的数据,看看哪些现有的训练示例应该重新标记为愤怒。如果你没有足够的愤怒示例,你将不得不收集更多的数据。这个过程越长,你现有模型的性能就会下降得越多。

标签的多样性

通常,为了获得足够的标记数据,公司必须使用多个来源的数据,并依赖于具有不同专业水平的多位标注者。这些不同的数据来源和标注者也具有不同的准确性水平。这就导致了标签的歧义性或标签的多样性问题:当一个数据实例存在多个冲突的标签时该如何处理。

考虑到实体识别的简单任务。你给了三位标注者以下样本,并要求他们标注出所有能找到的实体:

辛迪厄斯·达斯,简称为皇帝,是西斯领主中统治银河系的银河帝国的银河皇帝。

你会得到三种不同的解决方案,如表 4-1 所示。三位标注者标识出了不同的实体。你的模型应该基于哪一个进行训练?基于标注者 1 标记的数据训练的模型与基于标注者 2 标记的模型表现差异非常大。

表 4-1. 不同标注者标识的实体可能非常不同

标注者 # 实体 标注
1 3 [辛迪厄斯·达斯],简称为[皇帝],是[西斯领主]之一,统治着银河系,作为[第一银河帝国的银河皇帝]。
2 6 [辛迪厄斯·达斯],简称为[皇帝],是[西斯领主]之一,统治着银河系,作为[第一银河帝国的银河皇帝]。
3 4 [达斯·西迪厄斯],简称为[皇帝],是一个统治宇宙的[西斯黑暗领主],并作为[第一银河帝国的银河帝国皇帝]统治着银河系。

标注者之间的分歧非常常见。领域专业知识要求越高,标注分歧的可能性就越大。如果一个专家认为标签应该是 A,而另一个认为应该是 B,我们如何解决这种冲突以获得一个统一的真相?如果人类专家无法就标签达成一致意见,人类水平的表现又意味着什么呢?

为了减少标记者之间的分歧,首先必须明确定义问题。例如,在前述的实体识别任务中,一些分歧可以通过澄清的方式加以消除,即在存在多个可能实体的情况下,选择包含最长子字符串的实体。这意味着第一银河帝国的银河帝国皇帝而不是银河帝国第一银河帝国。其次,你需要将这个定义纳入标注者的训练中,以确保所有标记者理解这些规则。

数据血统

不加区分地使用来自不同标注者生成的多个来源的数据,而不检查它们的质量,可能导致你的模型神秘地失败。考虑这样一种情况,你已经用 10 万个数据样本训练了一个适度好的模型。你的机器学习工程师确信更多的数据会提高模型性能,所以你花了大量资金雇佣标注者为另外一百万数据样本打标签。

然而,在用新数据训练后,模型性能实际上下降了。原因是新的百万个样本是通过标注者众包来标记的,而这些标注者的标记精度明显低于原始数据。如果你已经混合了数据,并且无法区分新数据和旧数据,那么解决这个问题将会格外困难。

良好的做法是跟踪每个数据样本的来源以及其标签,这一技术被称为数据血统。数据血统可以帮助你识别数据中潜在的偏见并调试你的模型。例如,如果你的模型在最近获得的数据样本上表现不佳,你可能需要查看新数据是如何获取的。我们多次发现问题不在于我们的模型,而是因为最近获取的数据中错误标签的数量异常之高。

自然标签

手动标记并不是唯一的标签来源。您可能有幸可以在具有自然的地面真实标签的任务上工作。具有自然标签的任务是模型的预测可以通过系统自动或部分自动地进行评估的任务。例如,估计 Google 地图上某条路线的到达时间的模型。如果您采用了该路线,到达后,Google 地图知道实际行程需要多长时间,因此可以评估预测到达时间的准确性。另一个例子是股票价格预测。如果您的模型预测某只股票的下一个两分钟的价格,则两分钟后,您可以将预测价格与实际价格进行比较。

具有自然标签的任务的典型例子是推荐系统。推荐系统的目标是向用户推荐与他们相关的项目。用户是否点击推荐的项目可以被视为该推荐的反馈。被点击的推荐可以被认为是好的(即标签为正面),而在一段时间后没有被点击的推荐,比如 10 分钟后,可以被认为是坏的(即标签为负面)。

许多任务可以作为推荐任务来进行。例如,您可以将预测广告点击率的任务构建为根据用户的活动历史和资料,向他们推荐最相关的广告。从用户行为如点击和评分中推断的自然标签也被称为行为标签。

即使您的任务本质上没有自然标签,也可能可以设置系统以收集模型的一些反馈。例如,如果您正在构建像 Google 翻译这样的机器翻译系统,您可以让社区提交对糟糕翻译的替代翻译选项,这些替代翻译可以用于训练模型的下一次迭代(尽管您可能需要先审核这些建议的翻译)。新闻推送排名不是一个具有固有标签的任务,但通过在每个新闻推送项目上添加“赞”按钮和其他反应,Facebook 能够收集其排名算法的反馈。

在行业中,具有自然标签的任务非常普遍。在我网络中的 86 家公司的调查中,我发现其中 63%的公司处理具有自然标签的任务,如图 4-3 所示。这并不意味着可以从 ML 解决方案中受益的任务中 63%具有自然标签。更可能的情况是,公司发现先从具有自然标签的任务开始更容易和更便宜。

图 4-3. 我的网络中 63%的公司从事具有自然标签的任务。百分比之和不为 1 是因为公司可以处理具有不同标签来源的任务。⁹

在前面的例子中,如果一个推荐在一段时间后没有被点击,可以推断它不好。这被称为隐式标签,因为这种负标签是基于缺少正标签推断出来的。这与显式标签不同,显式标签是指用户通过给推荐物品低评分或投反对票来明确表达对推荐的反馈。

反馈循环长度

对于具有自然真实标签的任务,从提供预测到提供反馈的时间称为反馈循环长度。具有短反馈循环的任务是指标签通常在几分钟内就可用的任务。许多推荐系统具有短反馈循环。如果推荐的物品是亚马逊上的相关产品或者在 Twitter 上关注的人,推荐物品被推荐到被点击(如果有点击)之间的时间很短。

然而,并非所有推荐系统都有分钟级的反馈循环。如果你处理像博客文章、文章或 YouTube 视频这样的长内容类型,反馈循环可能需要几个小时。例如,如果你构建一个像 Stitch Fix 的服装推荐系统,你在用户收到并试穿衣物之前可能不会得到反馈,这可能需要几周的时间。

选择正确的窗口长度需要深思熟虑,因为它涉及速度和准确性的权衡。较短的窗口长度意味着你可以更快地捕获标签,这使你能够使用这些标签来检测模型的问题并尽快解决这些问题。然而,较短的窗口长度也意味着在推荐被点击之前可能会过早地将推荐标记为不好。

无论你设置窗口长度多长,可能仍会有过早的负标签。2021 年初,Twitter 广告团队的一项研究发现,尽管大多数广告点击发生在前五分钟内,但有些点击发生在广告展示后的几个小时内¹⁰。这意味着这种类型的标签往往低估了实际的点击率。如果你只记录了 1,000 个正标签,实际的点击数可能会超过 1,000。

对于具有长反馈循环的任务,自然标签可能需要几周甚至几个月才会到达。欺诈检测就是一个具有长反馈循环的示例。在交易后的一段时间内,用户可以对该交易是否欺诈提出异议。例如,当客户查看他们的信用卡账单并看到一个他们不认识的交易时,他们可能会向银行提出异议,从而给银行提供反馈,标记该交易为欺诈。典型的争议窗口是一个到三个月。在争议窗口结束后,如果用户没有提出异议,你可以推断该交易是合法的。

具有长反馈循环的标签对于在季度或年度业务报告中报告模型性能非常有帮助。但是,如果您希望尽快检测到模型问题,则并不是非常有帮助。如果您的欺诈检测模型存在问题,并且需要几个月才能发现,那么在问题被修复时,您的错误模型放行的所有欺诈交易可能会导致小企业破产。

处理标签缺失

由于获取足够高质量标签的挑战,许多技术已被开发来解决由此导致的问题。在本节中,我们将涵盖四种方法:弱监督、半监督、迁移学习和主动学习。这些方法的摘要显示在表 4-2 中。

表 4-2. 处理缺乏手动标记数据的四种技术的摘要

方法 如何 是否需要地面真值?
弱监督 利用(通常是嘈杂的)启发式来生成标签 No,但建议使用少量标签来指导启发式的开发
半监督 利用结构假设生成标签 Yes,使用少量初始标签作为种子来生成更多标签
迁移学习 利用在另一个任务上预训练的模型来执行您的新任务 对于零-shot 学习来说是 No,但是对于微调来说是 Yes,尽管所需的地面真值数量通常要小得多,而不是您从头开始训练该模型时所需的数量
主动学习 标记对您的模型最有用的数据样本 Yes

弱监督

如果手动标记如此棘手,那么如果我们完全不使用手动标签会怎样?一种获得广泛关注的方法是弱监督。弱监督的最流行的开源工具之一是由斯坦福人工智能实验室开发的 Snorkel。¹¹ 弱监督背后的见解是人们依赖启发式,可以通过主题专业知识来开发,来标记数据。例如,医生可能使用以下启发式来决定是否应将患者病例优先考虑为紧急:

如果护士的笔记提到严重病情如肺炎,应优先考虑患者的病例。

Snorkel 等库是围绕标签函数(LF)的概念构建的:一种编码启发式的函数。上述启发式可以通过以下函数表达:

def labeling_function(note):
   if "pneumonia" in note:
     return "EMERGENT"

LFs 可以编码许多不同类型的启发式方法。以下是其中一些:

关键词启发式

例如前面的例子

正则表达式

例如,如果笔记匹配或未能匹配某个特定正则表达式

数据库查找

例如,如果笔记包含在危险疾病列表中列出的疾病

其他模型的输出

例如,如果现有系统将其分类为EMERGENT

在编写了 LFs 之后,您可以将它们应用于要标注的样本。

因为 LFs 编码启发式,而启发式是有噪音的,LFs 产生的标签也会有噪音。多个 LFs 可能适用于同一数据示例,并且它们可能会给出冲突的标签。一个函数可能认为护士的笔记是EMERGENT,但另一个函数可能认为不是。一个启发式可能比另一个启发式更准确,但您可能不知道,因为没有地面真实标签进行比较。重要的是将所有 LFs 组合、去噪声并重新加权,以获取最有可能正确的一组标签。图 4-4 在高层次展示了 LFs 如何工作。

图 4-4. 标注函数如何组合的高级概述。来源:Ratner 等人的图像¹²

理论上,弱监督不需要任何手动标签。然而,为了了解 LFs 的准确性,建议对少量手动标签进行评估。这些手动标签可以帮助您发现数据中的模式,以编写更好的 LFs。

弱监督在数据有严格隐私要求时尤为有用。您只需查看少量经过清理的数据子集来编写 LFs,然后可以将其应用于其他数据,而无需任何人查看。

利用 LFs,主题专业知识可以进行版本管理、重复使用和共享。一个团队拥有的专业知识可以被编码并被另一个团队使用。如果您的数据或需求发生变化,您只需重新应用 LFs 到数据样本即可。使用 LFs 为数据生成标签的方法也被称为程序化标注。表格 4-3 展示了程序化标注相对于手动标注的一些优势。

表格 4-3. 程序化标注相对于手动标注的优势

手动标注 程序化标注
昂贵:特别是需要主题专业知识时 节约成本:专业知识可以在整个组织中进行版本管理、共享和重复使用
隐私缺乏:需要将数据发送给人类标注者 隐私:使用清理后的数据子样本创建标注函数(LFs),然后将 LFs 应用于其他数据而无需查看单个样本
缓慢:所需时间随需要标注的标签数呈线性增长 快速:轻松从 1K 扩展到 1M 个样本
非自适应:每次更改都需要重新标注数据 自适应:当发生更改时,只需重新应用 LFs!

这里有一个案例研究,展示弱监督在实践中的良好效果。在与斯坦福医学院的一项研究中¹³,使用单个放射科医生编写 LFs 后获得的弱监督标签训练的模型,其性能与通过近一年的手工标注获得的数据训练的模型相当,如图 4-5 所示。有关实验结果的两个有趣事实。首先,即使没有更多的 LFs,模型仍在随着更多未标记数据而改进。其次,LFs 在任务之间被重复使用。研究人员能够在胸部 X 射线(CXR)任务和四肢 X 射线(EXR)任务之间重复使用六个 LFs¹⁴。

图 4-5。完全监督标签(FS)训练的模型与程序标签(DP)训练的模型在 CXR 和 EXR 任务上性能的比较。来源:Dunnmon 等人¹⁵

我的学生经常问,如果启发式方法能够如此有效地标记数据,为什么我们还需要机器学习模型呢?一个原因是 LFs 可能无法覆盖所有数据样本,因此我们可以在由 LFs 程序标记的数据上训练机器学习模型,并使用此训练模型为没有任何 LFs 覆盖的样本生成预测。

弱监督是一种简单但强大的范式。然而,它并不完美。在某些情况下,通过弱监督获得的标签可能太嘈杂,以至于无法派上用场。但即使在这些情况下,弱监督也可以是一个很好的起点,当您想要探索机器学习的有效性而又不想在最开始投入过多手工标注时。

半监督学习

如果弱监督利用启发式方法获取嘈杂的标签,半监督则利用结构假设基于少量初始标签生成新标签。与弱监督不同,半监督需要一组初始标签。

半监督学习是一种技术,早在 90 年代就开始使用¹⁶,从那时起,许多半监督方法已经被开发出来。对半监督学习的全面回顾超出了本书的范围。我们将讨论其中的一小部分方法,以让读者了解它们的使用方式。对于全面的回顾,我推荐阅读“半监督学习文献综述”(Xiaojin Zhu, 2008)和“关于半监督学习的调查”(Engelen 和 Hoos, 2018)。

经典的半监督方法是自训练。您首先在现有的标记数据集上训练模型,然后使用该模型对未标记样本进行预测。假设具有高原始概率分数的预测是正确的,则将高概率预测的标签添加到训练集中,并在这个扩展的训练集上训练新模型。这个过程一直持续,直到您对模型的表现满意。

另一种半监督方法假设具有相似特征的数据样本共享相同的标签。这种相似性可能是显而易见的,例如在分类 Twitter 标签主题的任务中。你可以开始将标签为“#AI”的标签标记为计算机科学。假设在同一条推文或个人资料中出现的标签很可能是关于相同主题的,鉴于 MIT CSAIL 在 Figure 4-6 中的资料,你也可以将标签“#ML”和“#BigData”标记为计算机科学。

图 4-6。因为 #ML 和 #BigData 在同一个 Twitter 资料中出现在 #AI 旁边,我们可以假设它们属于同一个主题。

在大多数情况下,只有通过更复杂的方法才能发现相似性。例如,你可能需要使用聚类方法或 k 最近邻算法来发现属于同一簇的样本。

近年来备受欢迎的半监督方法之一是基于扰动的方法。它基于这样的假设:对样本进行小的扰动不应改变其标签。因此,你可以对训练实例施加小的扰动以获得新的训练实例。这些扰动可以直接应用于样本(例如,向图像添加白噪声)或者应用于它们的表示(例如,向单词的嵌入添加小的随机值)。扰动后的样本与未扰动的样本具有相同的标签。我们将在 “扰动” 部分进一步讨论这个问题。

在某些情况下,即使给定数据集中有相当比例的标签被丢弃,半监督方法的表现也达到了纯监督学习的水平¹⁷。

当训练标签数量有限时,半监督方法变得尤为有用。在使用有限数据进行半监督时,需要考虑的一点是如何利用这些有限数据来评估多个候选模型并选择最佳模型。如果使用少量数据,则在这个小型评估集上表现最佳的模型可能是对这个集合过拟合最严重的模型。另一方面,如果使用大量数据进行评估,则基于这个评估集选择最佳模型所获得的性能提升可能小于通过将评估集添加到有限训练集中所获得的性能提升。许多公司通过在较大的评估集上选择最佳模型,然后继续在评估集上训练冠军模型来克服这种权衡。

迁移学习

迁移学习是指一种方法族,其中为一个任务开发的模型被重复用作第二个任务模型的起点。首先,基础模型被训练用于一个基础任务。通常,基础任务是一个拥有廉价且丰富的训练数据的任务。语言建模是一个很好的候选,因为它不需要标记数据。语言模型可以在任何文本体系上进行训练——书籍、维基百科文章、聊天记录——任务是:给定一系列标记,¹⁸ 预测下一个标记。当给定序列“I bought NVIDIA shares because I believe in the importance of,”时,语言模型可能会输出“hardware”或“GPU”作为下一个标记。

然后,可以将训练好的模型用于您感兴趣的任务——下游任务——如情感分析、意图检测或问答。在某些情况下,例如零样本学习场景中,您可以直接在下游任务中使用基础模型。在许多情况下,您可能需要微调基础模型。微调意味着对基础模型进行小的更改,例如继续训练基础模型或部分基础模型,使用给定下游任务的数据。¹⁹

有时候,您可能需要使用模板修改输入,以促使基础模型生成您想要的输出。²⁰ 例如,要将语言模型用作问答任务的基础模型,您可能想要使用以下提示:

Q: 美国是何时成立的?

A: 1776 年 7 月 4 日。

Q: 谁写了《独立宣言》?

A: 托马斯·杰斐逊。

Q: 亚历山大·汉密尔顿是哪一年出生的?

A:

当您将此提示输入到诸如GPT-3之类的语言模型中时,它可能会输出亚历山大·汉密尔顿的出生年份。

对于没有大量标记数据的任务,迁移学习尤为吸引人。即使对于有大量标记数据的任务,使用预训练模型作为起点通常也能显著提高性能,与从头开始训练相比。

近年来,迁移学习因合理的原因引起了广泛关注。它使得许多以前由于缺乏训练样本而不可能的应用成为可能。今天生产中的许多 ML 模型的非微不足道部分都是迁移学习的结果,包括利用在 ImageNet 上预训练的模型的目标检测模型和利用预训练语言模型(如 BERT 或 GPT-3)的文本分类模型。²¹ 迁移学习还降低了 ML 的进入门槛,因为它有助于减少构建 ML 应用程序所需的标记数据的前期成本。

过去五年出现的一个趋势是,通常情况下,预训练基模型越大,其在下游任务上的表现越好。大型模型训练成本高昂。根据 GPT-3 的配置,估计训练此模型的成本在数千万美元。许多人推测,未来只有少数公司能负担得起大型预训练模型的训练成本。其余行业将直接使用这些预训练模型或根据特定需求进行微调。

主动学习

主动学习是提高数据标签效率的一种方法。希望机器学习模型可以在较少的训练标签下实现更高的准确性,如果能够选择要学习的数据样本。有时称为查询学习的主动学习术语正变得越来越不受欢迎,因为一个模型(主动学习器)会以未标记样本的形式向注释者(通常是人类)发送回查询。

不是随机标记数据样本,而是根据某些指标或启发式标记对模型最有帮助的样本。最直接的度量标准是不确定性测量——标记模型对预测决策边界最不确定的示例,希望这些示例能帮助模型学习得更好。例如,在分类问题中,您的模型为不同类别输出原始概率,它可能选择预测类别概率最低的数据样本。图 4-7 展示了这种方法在一个玩具示例中的有效性。

图 4-7. 不确定性基础的主动学习工作原理。 (a) 一个玩具数据集,包括来自两个类高斯分布的均匀采样的 400 个实例。 (b) 在 30 个随机标记样本上训练的模型的准确率为 70%。 (c) 在 30 个由主动学习选择的样本上训练的模型的准确率为 90%。来源:Burr Settles²²

另一个常见的启发式方法基于多个候选模型之间的不同意见。这种方法称为委员会查询,是集成方法的一个例子。²³ 您需要一个由几个候选模型组成的委员会,通常是相同的模型,但使用不同的超参数集或在不同数据片段上训练的相同模型。每个模型可以对应该标记哪些样本进行投票,可能会基于其对预测的不确定性而投票。然后,您标记委员会在意见分歧最大的样本。

还有其他启发式方法,如选择使梯度更新最大或将损失最大减少的样本。要全面了解主动学习方法,请参阅《主动学习文献综述》(Settles 2010)。

待标记的样本可以来自不同的数据模式。它们可以是合成的,其中您的模型生成在输入空间中最不确定的区域内的样本。²⁴ 它们可以来自稳态分布,您已经收集了大量未标记数据,并且您的模型从此池中选择样本进行标记。它们可以来自真实世界的分布,其中您有一系列数据流入,如在生产中,您的模型从此数据流中选择样本进行标记。

当系统使用实时数据时,我最为激动的是主动学习。数据随时变化,这是我们在第一章中简要提及并将在第八章中进一步详细说明的现象。在这种数据模式中进行主动学习将使您的模型能够更有效地实时学习,并更快地适应变化的环境。

类不平衡

类不平衡通常指的是分类任务中的问题,其中训练数据集中每个类别的样本数量差异显著。例如,在用于从 X 射线图像中检测肺癌的训练数据集中,99.99%的 X 射线可能来自正常肺部,仅有 0.01%可能包含癌细胞。

类不平衡问题也可能发生在回归任务中,其中标签是连续的。考虑估算医疗费用的任务。²⁵ 医疗费用极不平衡——中位数账单较低,但 95 分位数账单高得惊人。在预测医院账单时,准确预测 95 分位数账单可能比预测中位数账单更为重要。对于 250 美元的账单,100%的差异是可以接受的(实际为 500 美元,预测为 250 美元),但对于 1 万美元的账单,100%的差异是不可以接受的(实际为 2 万美元,预测为 1 万美元)。因此,我们可能需要训练模型更好地预测 95 分位数账单,即使这会降低整体指标。

类不平衡的挑战

机器学习,尤其是深度学习,在数据分布更平衡的情况下效果很好,但通常在类别严重不平衡时效果不佳,如在图 4-8 中所示。类不平衡可能使学习变得困难,原因如下所述。

图 4-8。机器学习在类平衡的情况下效果良好。来源:根据 Andrew Ng 的图像改编²⁶

第一个原因是类别不平衡通常意味着你的模型没有足够的信号来学习检测少数类别。在少数类别实例较少的情况下,问题变成了少样本学习问题,在模型做出决策之前,它只能少次地看到少数类别。在训练集中没有稀有类别实例的情况下,你的模型可能会假设这些稀有类别不存在。

第二个原因是类别不平衡使得你的模型更容易因为利用简单的启发式而陷入非最优解决方案,而不是学习数据底层模式中的任何有用信息。考虑前面的肺癌检测例子。如果你的模型学会了总是输出多数类别,它的准确率已经达到了 99.99%²⁷。这种启发式很难被梯度下降算法击败,因为对这种启发式稍加随机性可能会导致更差的准确率。

第三个原因是类别不平衡导致错误的不对称成本——在稀有类别样本上的错误预测成本可能远高于在多数类别样本上的错误预测成本。

例如,在具有癌细胞的 X 光片上的误分类比在正常肺部 X 光片上的误分类更危险。如果你的损失函数没有配置来处理这种不对称性,你的模型将对所有样本采取相同的方式。因此,你可能会得到一个在多数和少数类别上表现一样好的模型,而你更希望得到一个在多数类别上表现较差但在少数类别上表现更好的模型。

当我上学的时候,我得到的大多数数据集都有更或多或少平衡的类别²⁸。开始工作后意识到类别不平衡是常态,让我感到震惊。在现实世界中,罕见事件通常比普通事件更有趣(或更危险),许多任务集中于检测这些罕见事件。

类别不平衡任务的经典示例是欺诈检测。大多数信用卡交易并非欺诈性的。截至 2018 年,每 100 美元的持卡人消费中有 6.8 美分是欺诈的²⁹。另一个示例是客户流失预测。你的大多数客户可能并不打算取消订阅。如果他们这样做了,你的业务比客户流失预测算法更需要担心其他问题。其他示例包括疾病筛查(大多数人,幸运的是,并没有终末期疾病)和简历筛选(98%的求职者在初始简历筛选时被淘汰³⁰)。

类别不平衡的一个不太明显的例子是目标检测。目标检测算法当前的工作方式是在图像上生成大量的边界框,然后预测哪些框最有可能包含对象。大多数边界框不包含相关对象。

除了类别不平衡在问题本身固有的情况外,类别不平衡也可能是在采样过程中产生偏差的结果。考虑这样一种情况:你想创建用于检测电子邮件是否为垃圾邮件的训练数据。你决定使用公司电子邮件数据库中的所有匿名电子邮件。根据 Talos Intelligence 的数据,截至 2021 年 5 月,几乎 85%的电子邮件是垃圾邮件。³¹ 但大多数垃圾邮件在到达公司数据库之前已被过滤掉,所以在你的数据集中,只有很小一部分是垃圾邮件。

导致类别不平衡的另一个原因,尽管较少见,是由于标记错误。标注者可能误读了说明或者按照错误的说明操作(认为只有两个类别,正面和负面,而实际上有三个),或者只是犯了错误。每当面对类别不平衡问题时,检查数据以理解其原因至关重要。

处理类别不平衡

由于在现实世界的应用中普遍存在,过去二十年来对类别不平衡进行了深入研究。³² 类别不平衡会根据不平衡的程度对任务产生不同影响。有些任务对类别不平衡更为敏感。Japkowicz 指出,对不平衡的敏感性随问题的复杂性增加而增加,并且非复杂、线性可分的问题不受任何类别不平衡的影响。³³ 在二元分类问题中,类别不平衡比在多类别分类问题中要简单得多。Ding 等人表明,从 2017 年开始,“非常深”的神经网络(指超过 10 层)在不平衡数据上的表现要比较浅的神经网络好得多。³⁴

已经有许多技术被提出来缓解类别不平衡的影响。然而,随着神经网络变得更大、更深,学习能力更强,有人可能会认为如果数据在现实世界中看起来是这样,那么你就不应该试图“修复”类别不平衡。一个好的模型应该学会建模这种不平衡。然而,开发一个足够好的模型可能是具有挑战性的,因此我们仍然需要依赖特殊的训练技术。

在本节中,我们将介绍三种处理类别不平衡的方法:选择适合你问题的正确指标;数据级方法,即改变数据分布使其不那么不平衡;以及算法级方法,即改变学习方法使其更能抵御类别不平衡。

这些技术可能是必要的,但不足以。为了进行全面的调查,我建议阅读 “类别不平衡深度学习调查”(Johnson 和 Khoshgoftaar,2019)。

使用正确的评估指标

面对类别不平衡的任务,选择合适的评估指标是最重要的事情。错误的指标会给你错误的模型表现观念,进而无法帮助你开发或选择足够适合你任务的模型。

总体准确率和误差率是报告机器学习模型性能最常用的指标。然而,对于类别不平衡的任务,这些指标不足以,因为它们同等对待所有类别,这意味着模型在多数类上的表现将主导这些指标。当多数类不是你关心的类时,这尤为糟糕。

考虑一个具有两个标签的任务:癌症(正类)和正常(负类),其中 90% 的标记数据是正常的。考虑两个模型,A 和 B,其混淆矩阵分别显示在表 4-4 和 4-5 中。

表 4-4. 模型 A 的混淆矩阵;模型 A 能检测出 100 个癌症病例中的 10 个。

模型 A 实际癌症 实际正常
预测癌症 10 10
预测正常 90 890

表 4-5. 模型 B 的混淆矩阵;模型 B 能检测出 100 个癌症病例中的 90 个。

模型 B 实际癌症 实际正常
预测癌症 90 90
预测正常 10 810

如果你和大多数人一样,你可能更倾向于模型 B 来为你做预测,因为它有更好的可能性告诉你是否真的得了癌症。然而,它们的准确率都是 0.9。

有助于了解模型在特定类别上表现的指标更为合适。如果你为每个类别单独使用准确率,那么准确率仍然是一个不错的指标。模型 A 在癌症类别上的准确率为 10%,模型 B 在癌症类别上的准确率为 90%。

F1 值、精确率和召回率是用于衡量二元分类问题中模型性能的指标,因为它们依赖于真正例——模型正确预测正类的情况。³⁵

F1 值、精确率和召回率是非对称的指标,这意味着它们的值取决于哪个类被视为正类。在我们的情况下,如果我们将癌症视为正类,模型 A 的 F1 值为 0.17。然而,如果我们将正常视为正类,模型 A 的 F1 值为 0.95。当癌症被视为正类时,模型 A 和模型 B 的准确率、精确率、召回率和 F1 分数显示在 表 4-7 中。

表 4-7. 尽管一个模型明显优于另一个模型,但两个模型的准确度相同。

癌症 (1) 正常 (0) 准确率 精确率 召回率 F1
模型 A 10/100 890/900 0.9 0.5 0.1 0.17
模型 B 90/100 810/900 0.9 0.5 0.9 0.64

许多分类问题可以建模为回归问题。您的模型可以输出一个概率,基于该概率对样本进行分类。例如,如果值大于 0.5,则为正标签;如果小于或等于 0.5,则为负标签。这意味着您可以调整阈值以增加真正阳性率(也称为召回率),同时降低假阳性率(也称为误报概率),反之亦然。我们可以绘制不同阈值下的真正阳性率与假阳性率的曲线。这种绘图称为ROC 曲线(接收者操作特征曲线)。当您的模型完美时,召回率为 1.0,曲线则位于顶部。这条曲线显示了模型性能如何随阈值变化而变化,并帮助您选择最适合您的阈值。曲线越接近完美直线,您的模型性能越好。

曲线下面积(AUC)衡量了 ROC 曲线下面的面积。由于曲线越接近完美直线越好,因此该面积越大越好,正如图 4-9 所示。

图 4-9. ROC 曲线

与 F1 和召回率一样,ROC 曲线仅关注正类,并不显示模型在负类上的表现如何。戴维斯和戈德里奇建议我们应该绘制精确率与召回率的曲线,即他们称之为精确-召回曲线。他们认为,这条曲线更详细地展示了算法在类别不平衡任务上的性能³⁶。

数据级方法:重新取样

数据级方法修改训练数据的分布,以降低不平衡程度,使模型更容易学习。一类常见的技术是重新取样。重新取样包括过采样,增加少数类的实例,和欠采样,减少多数类的实例。最简单的欠采样方法是从多数类随机删除实例,而最简单的过采样方法是随机复制少数类的实例,直到您满意为止。图 4-10 展示了过采样和欠采样的可视化。

图 4-10. 示范如何工作的欠采样和过采样。来源:根据 Rafael Alencar 的图像调整³⁷

一种早在 1976 年开发的低维数据欠采样方法是 Tomek 链接。³⁸ 使用这种技术,您可以找到来自相对立类别的接近的样本对,并移除每对中大多数类的样本。

尽管这使得决策边界更清晰且有助于模型更好地学习边界,但可能会使模型更不稳健,因为模型无法从真实决策边界的微妙之处学习。

一种常见的低维数据过采样方法是 SMOTE(合成少数类过采样技术)。³⁹ 它通过在少数类内部现有数据点的凸组合抽样来合成新的样本。

SMOTE 和 Tomek 链接仅在低维数据中被证明有效。许多复杂的重新采样技术,如 Near-Miss 和单边选择,⁴¹ 需要计算实例之间或实例与决策边界之间的距离,这在高维数据或具有大型神经网络等高维特征空间中可能是昂贵或不可行的。

当您对训练数据重新采样时,请勿在重新采样数据上评估模型,因为这会导致模型过度拟合到重新采样的分布上。

欠采样存在移除数据会丢失重要数据的风险。过采样则存在过拟合训练数据的风险,特别是如果少数类的添加副本是现有数据的复制品。已开发了许多复杂的抽样技术来减轻这些风险。

其中一种技术是两阶段学习。⁴² 首先在重新采样的数据上训练模型。这些重新采样的数据可以通过随机欠采样大类直到每个类只有N个实例来实现。然后在原始数据上对模型进行微调。

另一种技术是动态抽样:在训练过程中对低效能类进行过采样,对高效能类进行欠采样。由 Pouyanfar 等人引入,⁴³ 该方法旨在向模型展示更多尚未学习的内容,而非已经学习过的内容。

算法级方法

如果数据级方法通过改变训练数据的分布来缓解类别不平衡的挑战,那么算法级方法则保持训练数据分布不变,但改变算法以使其更能抵御类别不平衡。

因为损失函数(或成本函数)指导了学习过程,许多算法级别的方法涉及调整损失函数。关键思想是,如果有两个实例x[1]和x[2],并且对x[1]做出错误预测造成的损失比对x[2]做出错误预测造成的损失更高,那么模型将优先考虑对x[1]做出正确预测而不是对x[2]做出正确预测。通过给我们关心的训练实例更高的权重,我们可以使模型更专注于学习这些实例。

L ( x ; θ )表示模型参数设置为θ时实例x引起的损失。模型的损失通常被定义为所有实例引起的平均损失。N表示训练样本的总数。

L ( X ; θ ) = x 1 N L ( x ; θ )

这个损失函数平等看待所有实例引起的损失,即使某些实例上的错误预测可能比其他实例上的错误预测要昂贵得多。有许多方法可以修改这个成本函数。在本节中,我们将专注于其中三种,首先是成本敏感学习。

成本敏感学习

早在 2001 年,基于不同类别的误分类造成不同成本的洞察力,Elkan 提出了成本敏感学习,其中个体损失函数被修改以考虑这种不同的成本。⁴⁴ 该方法从使用成本矩阵开始,以指定C[ij]:如果将类i分类为类j的成本。如果i = j,那么是正确分类,成本通常为 0。否则,是误分类。如果将正例分类为负例的成本是反之的两倍,那么可以将C[10]设置为C[01]的两倍。

例如,如果有两类,正例和负例,则成本矩阵可以看起来像表 4-8. 成本矩阵示例中那样。

表 4-8. 成本矩阵示例

实际为负例 实际为正例
预测为负例 C(0, 0) = C[00] C(1, 0) = C[10]
预测为正例 C(0, 1) = C[01] C(1, 1) = C[11]

类别i的实例x引起的损失将成为实例x的所有可能分类的加权平均值。

L ( x ; θ ) = j C ij P ( j | x ; θ )

这个损失函数的问题在于,你必须手动定义成本矩阵,而这个矩阵在不同任务和不同尺度下是不同的。

类平衡损失

当模型在不平衡数据集上训练时,可能会偏向主要类别,并在少数类别上做出错误预测。如果我们惩罚模型对少数类别做出错误预测以纠正这种偏差,可能会发生什么?

在其原始形式中,我们可以使每个类别的权重与该类别中的样本数成反比,以便稀有类别具有较高的权重。在下面的方程中,N 表示训练样本的总数:

W i = N numberofsamplesofclassi

类别 i 的实例 x 导致的损失如下,其中 Loss(x, j) 是当 x 被分类为类别 j 时的损失。可以是交叉熵或任何其他损失函数。

L ( x ; θ ) = W i j P ( j | x ; θ ) Loss ( x , j )

这种损失的更复杂版本可以考虑现有样本之间的重叠,例如基于有效样本数量的类平衡损失。⁴⁵

焦点损失

在我们的数据中,某些示例比其他示例更容易分类,我们的模型可能会快速学习它们的分类。我们希望激励我们的模型专注于学习那些仍然难以分类的样本。如果我们调整损失,使得样本被正确分类的概率较低,则其权重将更高。这正是焦点损失所做的。⁴⁶ 焦点损失的方程及其与交叉熵损失的性能如图 4-11 所示。

实际上,集成已经显示对应类别不平衡问题有所帮助。⁴⁷ 然而,我们在本节中不包括集成,因为通常不是因为类别不平衡而使用集成。集成技术将在第六章中讨论。

图 4-11. 使用焦点损失(FL)训练的模型显示比使用交叉熵损失(CE)训练的模型具有更低的损失值。来源:改编自林等人的一幅图像。

数据增强

数据增强是一系列用于增加训练数据量的技术。传统上,这些技术用于具有有限训练数据的任务,例如在医学成像中。然而,在过去几年中,它们已经显示出即使在有大量数据时也很有用——增强数据可以使我们的模型对噪声甚至对抗性攻击更加健壮。

数据增强已经成为许多计算机视觉任务中的标准步骤,并正在逐步应用于自然语言处理(NLP)任务中。这些技术严重依赖于数据格式,因为图像处理与文本处理有所不同。在本节中,我们将涵盖三种主要类型的数据增强:简单的保持标签的转换;扰动,即“添加噪声”的术语;以及数据合成。在每种类型中,我们将讨论计算机视觉和 NLP 的示例。

简单的保持标签的转换

在计算机视觉中,最简单的数据增强技术是在保持其标签的同时随机修改图像。你可以通过裁剪、翻转、旋转、反转(水平或垂直)、擦除图像的一部分等方式修改图像。这是有道理的,因为一只狗的旋转图像仍然是一只狗。像 PyTorch、TensorFlow 和 Keras 这样的常见机器学习框架都支持图像增强。根据 Krizhevsky 等人在其著名的 AlexNet 论文中所述,“这些转换后的图像是在 CPU 上的 Python 代码生成的,而 GPU 则在前一批图像上进行训练。因此,这些数据增强方案实际上是计算上免费的。”⁴⁸

在自然语言处理中,你可以随机用一个相似的词替换一个词,假设这种替换不会改变句子的含义或情感,如在表 4-9 中所示。相似的词可以通过同义词词典找到,也可以通过在词嵌入空间中找到距离接近的词来找到。

表 4-9. 从原始句子生成的三个句子

原始句子 我很高兴见到你。

| 生成的句子 | 我很高兴见到你。我很高兴见到你们

非常高兴见到你。 |

这种数据增强技术是快速增加训练数据量的一种方法。

扰动

扰动也是一种保持标签的操作,但由于有时它被用来欺骗模型做出错误预测,我认为它值得有自己的章节。

总体而言,神经网络对噪声很敏感。在计算机视觉中,这意味着向图像添加少量噪声可能导致神经网络错误分类。Su 等人表明,Kaggle CIFAR-10 测试数据集中的自然图像中有 67.97% 和 ImageNet 测试图像中的 16.04%,仅通过改变一个像素就可能被错误分类(见图 4-12)。⁴⁹

图 4-12. 改变一个像素可能导致神经网络做出错误预测。所使用的三个模型是 AllConv、NiN 和 VGG。在改变一个像素后,这些模型生成的原始标签显示在改变后的标签上方。来源:Su 等人。⁵⁰

使用欺骗性数据来欺骗神经网络以做出错误预测称为对抗攻击。向样本添加噪声是创建对抗性样本的常见技术。随着图像分辨率的增加,对抗攻击的成功尤为突出。

向训练数据添加噪声样本可以帮助模型识别其学习决策边界的薄弱点并提高其性能。⁵¹ 噪声样本可以通过添加随机噪声或搜索策略来创建。Moosavi-Dezfooli 等人提出了一种算法,称为 DeepFool,它找到了引入最小可能噪声注入以高置信度导致误分类的方法。⁵² 这种增强方式被称为对抗性增强。⁵³

在自然语言处理中,对抗性增强不常见(将一张带有随机添加像素的熊的图像看起来仍然像熊,但将随机字符添加到随机句子中可能会使其变得无意义),但扰动已被用来使模型更加稳健。其中最显著的例子之一是 BERT,在该模型中,模型随机选择每个序列中的 15%的所有标记,并选择用随机单词替换所选标记的 10%。例如,给定句子“My dog is hairy”,并且模型随机将“hairy”替换为“apple”,则句子变为“My dog is apple”。因此,1.5%的所有标记可能导致无意义的含义。他们的消融研究表明,小部分随机替换可以给他们的模型带来轻微的性能提升。⁵⁴

在第六章中,我们将讨论如何使用扰动不仅作为提高模型性能的一种方法,还作为评估其性能的一种方法。

数据合成

由于收集数据既昂贵又缓慢,并且存在许多潜在的隐私问题,如果我们能够完全避开它并使用合成数据来训练我们的模型,那将是一种梦想。尽管我们离能够合成所有训练数据还有很长的路要走,但我们确实可以合成一些训练数据来提升模型的性能。

在自然语言处理中,模板可以是启动模型的廉价方式。我曾与一个团队合作,他们使用模板为他们的对话型 AI(聊天机器人)启动训练数据。模板可能如下所示:“找一个[CUISINE]餐厅,在[LOCATION]附近[NUMBER]英里以内”(见表 4-10)。对于每个城市的所有可能的美食类型、合理的距离(你可能永远不想搜索超过 1000 英里的餐厅)、以及位置(家、办公室、地标、确切地址),您可以从模板生成数千个训练查询。

表 4-10。从模板生成的三个句子

模板 找一个距离[LOCATION] [NUMBER]英里以内的[CUISINE]餐厅。

| 生成的查询 | 找一个越南餐厅,在我的办公室附近2英里以内。找一个泰国餐厅,在我的家附近5英里以内。

找一个墨西哥餐厅,在Google 总部附近3英里以内。

在计算机视觉中,合成新数据的一种简单方法是将具有离散标签的现有示例与生成连续标签相结合。考虑一个分类猫和狗图像的任务,其标签有两种可能性:狗(编码为 0)和猫(编码为 1)。从标签为狗的示例x[1]和标签为猫的示例x[2],你可以生成x',如下:

x ' = γ x 1 + ( 1 - γ ) x 2

x的标签是x[1]和x[2]的标签的组合:γ × 0 + ( 1 - γ ) × 1 。这种方法称为 mixup。作者表明,mixup 可以提高模型的泛化能力,减少其对错误标签的记忆,增强其对对抗性示例的鲁棒性,并稳定生成对抗网络的训练。⁵⁵

使用神经网络合成训练数据是一种令人兴奋的方法,目前正在积极研究中,但在生产中还不太流行。Sandfort 等人显示,通过将使用 CycleGAN 生成的图像添加到其原始训练数据中,他们能够显著改善模型在计算机断层扫描(CT)分割任务中的性能。⁵⁶

如果你对深度学习中的图像数据增强更感兴趣,可以阅读《“深度学习图像数据增强综述”》(Shorten 和 Khoshgoftaar,2019),这是一篇全面的综述。

总结

训练数据仍然是现代机器学习算法的基础。无论你的算法有多聪明,如果你的训练数据不好,你的算法就无法表现良好。投入时间和精力来筛选和创建训练数据是值得的,这样可以让你的算法学到有意义的东西。

在本章中,我们讨论了创建训练数据的多个步骤。我们首先涵盖了不同的抽样方法,包括非概率抽样和随机抽样,这些方法可以帮助我们为我们的问题选择合适的数据。

今天使用的大多数机器学习算法都是监督学习算法,因此获取标签是创建训练数据的一个重要部分。许多任务,如交货时间估计或推荐系统,都有自然的标签。自然标签通常是延迟的,从服务预测到提供反馈的时间是反馈循环的长度。在行业中,具有自然标签的任务相当普遍,这可能意味着公司更倾向于从具有自然标签的任务开始,而不是从没有自然标签的任务开始。

对于没有自然标签的任务,公司往往依赖人工注释员来标注他们的数据。然而,手动标注具有许多缺点。例如,手动标注可能既昂贵又缓慢。为了解决手动标注的缺乏,我们讨论了包括弱监督、半监督、迁移学习和主动学习在内的替代方法。

在数据分布较为平衡时,机器学习算法表现良好,但在类别严重不平衡时表现不佳。不幸的是,在现实世界中,类别不平衡的问题是常态。在接下来的章节中,我们讨论了为何类别不平衡使得机器学习算法难以学习的原因。我们还讨论了处理类别不平衡的不同技术,从选择正确的度量标准到对数据进行重新采样,再到修改损失函数以鼓励模型关注特定样本。

我们在本章结束时讨论了数据增强技术,这些技术可以用来提高模型在计算机视觉和自然语言处理任务中的性能和泛化能力。

一旦您有了训练数据,您将希望从中提取特征以训练您的机器学习模型,这将在下一章中讨论。

¹ 一些读者可能会认为这种方法在大型模型上可能不适用,因为某些大型模型在小数据集上表现不佳,但在更多数据的情况下表现良好。在这种情况下,尝试不同大小的数据集以了解数据集大小对模型的影响仍然很重要。

² James J. Heckman,《样本选择偏误作为规范误差》,《计量经济学》47 卷,第 1 期(1979 年 1 月):153–61,https://oreil.ly/I5AhM

³ Rachel Lerman,《Google 在柯克兰测试其自动驾驶汽车》,《西雅图时报》,2016 年 2 月 3 日,https://oreil.ly/3IA1V

⁴ 这里的“人群”指的是 “统计人群”,即可能无限的所有可能被抽样的样本集合。

⁵ 多标签任务是指一个示例可能有多个标签。

⁶ “SVM: 加权样本”,scikit-learn,https://oreil.ly/BDqbk

⁷ Xiaojin Zhu,《使用图进行半监督学习》(博士论文,卡内基梅隆大学,2005 年),https://oreil.ly/VYy4C

⁸ 如果某些事物显而易见可以标注,你不需要领域专业知识。

⁹ 我们将在 “弱监督” 部分讨论编程标签。

¹⁰ Sofia Ira Ktena, Alykhan Tejani, Lucas Theis, Pranay Kumar Myana, Deepak Dilipkumar, Ferenc Huszar, Steven Yoo 和 Wenzhe Shi 的文章,“解决连续训练中神经网络的延迟反馈问题”,arXiv,2019 年 7 月 15 日,https://oreil.ly/5y2WA

¹¹ Alexander Ratner, Stephen H. Bach, Henry Ehrenberg, Jason Fries, Sen Wu 和 Christopher Ré 的文章,“Snorkel: 利用弱监督快速创建训练数据”,VLDB Endowment 11 卷 3 期(2017 年):269–82,https://oreil.ly/vFPjk

¹² Ratner 等人的文章,“Snorkel: 利用弱监督快速创建训练数据”。

¹³ Jared A. Dunnmon, Alexander J. Ratner, Khaled Saab, Matthew P. Lungren, Daniel L. Rubin 和 Christopher Ré 的文章,“跨模态数据编程支持快速医学机器学习”,Patterns 1 卷 2 期(2020 年):100019,https://oreil.ly/nKt8E

¹⁴ 这项研究中的两个任务分别仅使用了 18 和 20 个 LF。实际上,我见过有些团队为每个任务使用了数百个 LF。

¹⁵ Dummon 等人的文章,“跨模态数据编程”。

¹⁶ Avrim Blum 和 Tom Mitchell 的文章,“利用共训练结合标记和未标记数据”,收录于《第十一届计算学习理论年会论文集》(1998 年 7 月):92–100,https://oreil.ly/T79AE

¹⁷ Avital Oliver, Augustus Odena, Colin Raffel, Ekin D. Cubuk 和 Ian J. Goodfellow 的文章,“深度半监督学习算法的现实评估”,NeurIPS 2018 会议论文集https://oreil.ly/dRmPV

¹⁸ 一个 token 可以是一个单词、一个字符或者一个单词的一部分。

¹⁹ Jeremy Howard 和 Sebastian Ruder 的文章,“通用语言模型微调用于文本分类”,arXiv,2018 年 1 月 18 日,https://oreil.ly/DBEbw

²⁰ Pengfei Liu, Weizhe Yuan, Jinlan Fu, Zhengbao Jiang, Hiroaki Hayashi 和 Graham Neubig 的文章,“Pre-train, Prompt, and Predict: 自然语言处理中提示方法的系统调查”,arXiv,2021 年 7 月 28 日,https://oreil.ly/0lBgn

²¹ Jacob Devlin, Ming-Wei Chang, Kenton Lee 和 Kristina Toutanova 的文章,“BERT: 深度双向转换器的预训练用于语言理解”,arXiv,2018 年 10 月 11 日,https://oreil.ly/RdIGU;Tom B. Brown, Benjamin Mann, Nick Ryder, Melanie Subbiah, Jared Kaplan, Prafulla Dhariwal, Arvind Neelakantan 等人的文章,“语言模型是少样本学习者”,OpenAI,2020 年,https://oreil.ly/YVmrr

²² Burr Settles,《主动学习》,(Williston, VT: Morgan & Claypool, 2012)。

²³ 我们将在第六章中涵盖整体学习方法。

²⁴ Dana Angluin, “Queries and Concept Learning,” Machine Learning 2 (1988): 319–42, https://oreil.ly/0uKs4.

²⁵ 感谢 Eugene Yan 提供这个精彩的例子!

²⁶ Andrew Ng,“填补 AI 概念验证到生产应用的鸿沟”(HAI Seminar, 2020 年 9 月 22 日),视频,1:02:07,https://oreil.ly/FSFWS

²⁷ 这也是为什么准确率在类别不平衡的任务中是一个糟糕的指标,我们将在“处理类别不平衡”部分进一步探讨。

²⁸ 我设想如果不必解决类别不平衡问题,学习机器学习理论会更容易。

²⁹ Nilson 报告,“支付卡欺诈损失达到 278.5 亿美元”,PR Newswire,2019 年 11 月 21 日,https://oreil.ly/NM5zo

³⁰ “职场专家解释为什么只有 2%的求职者能被面试”,WebWire,2014 年 1 月 7 日,https://oreil.ly/UpL8S

³¹ “电子邮件和垃圾邮件数据”,Talos Intelligence,最近访问于 2021 年 5 月,https://oreil.ly/lI5Jr

³² Nathalie Japkowciz 和 Shaju Stephen,“类别不平衡问题:系统研究”,2002 年,https://oreil.ly/d7lVu

³³ Nathalie Japkowicz,“类别不平衡问题:意义和策略”,2000 年,https://oreil.ly/Ma50Z

³⁴ Wan Ding, Dong-Yan Huang, Zhuo Chen, Xinguo Yu 和 Weisi Lin,“利用极度不平衡类分布的深度网络进行面部动作识别”,2017 年亚太信号与信息处理协会年度峰会与会议(APSIPA ASC),2017 年,https://oreil.ly/WeW6J

³⁵ 截至 2021 年 7 月,当你使用scikit-learn.metrics.f1_score时,pos_label默认设置为 1,但如果你希望 0 为正标签,你可以进行更改。

³⁶ Jesse Davis 和 Mark Goadrich,“精确率-召回率与 ROC 曲线之间的关系”,第 23 届国际机器学习大会论文集,2006 年,https://oreil.ly/s40F3

³⁷ Rafael Alencar,“不平衡数据集的重采样策略”,Kaggle,https://oreil.ly/p8Whs

³⁸ Ivan Tomek,“最近邻编辑规则实验”,《IEEE 系统、人类和控制论》(1976 年 6 月):448–52,https://oreil.ly/JCxHZ

³⁹ N.V. Chawla, K.W. Bowyer, L.O. Hall, and W.P. Kegelmeyer,“SMOTE:合成少数类过采样技术”,《人工智能研究杂志》16 卷(2002 年):341–78,https://oreil.ly/f6y46

⁴⁰ “凸”这里大致意味着“线性”。

⁴¹ 张建平和 Inderjeet Mani,“kNN 方法解决不平衡数据分布:涉及信息提取的案例研究”(《ICML 不平衡数据集学习研讨会 II》,华盛顿特区,2003 年),https://oreil.ly/qnpra;Miroslav Kubat 和 Stan Matwin,“解决不平衡训练集的诅咒:单侧选择”(2000 年),https://oreil.ly/8pheJ

⁴² Hansang Lee, Minseok Park, and Junmo Kim,“通过转移学习的卷积神经网络在大规模不平衡数据库中的浮游生物分类”,《2016 年 IEEE 图像处理国际会议(ICIP)》(2016),https://oreil.ly/YiA8p

⁴³ Samira Pouyanfar, Yudong Tao, Anup Mohan, Haiman Tian, Ahmed S. Kaseb, Kent Gauen, Ryan Dailey 等,“卷积神经网络中的动态采样用于不平衡数据分类”,《2018 年 IEEE 多媒体信息处理和检索会议(MIPR)》(2018),https://oreil.ly/D3Ak5

⁴⁴ Charles Elkan,“成本敏感学习的基础”,《第十七届国际人工智能联合会议》(IJCAI’01)(2001),https://oreil.ly/WGq5M

⁴⁵ Yin Cui, Menglin Jia, Tsung-Yi Lin, Yang Song, and Serge Belongie,“基于有效样本数量的类平衡损失”,《计算机视觉与模式识别会议论文集》(2019),https://oreil.ly/jCzGH

⁴⁶ Tsung-Yi Lin, Priya Goyal, Ross Girshick, Kaiming He, and Piotr Dollár,“密集目标检测的焦点损失”,《arXiv》,2017 年 8 月 7 日,https://oreil.ly/Km2dF

⁴⁷ Mikel Galar, Alberto Fernandez, Edurne Barrenechea, Humberto Bustince, and Francisco Herrera,“解决类不平衡问题的集成方法综述:Bagging、Boosting 和混合方法”,《IEEE 系统、人类和控制论》C 部分(应用与评论)42 卷,第 4 期(2012 年 7 月):463–84,https://oreil.ly/1ND4g

⁴⁸ 亚历克斯·克里兹海夫斯基,伊利亚·苏茨凯弗和杰弗里·E·辛顿,“ImageNet 分类与深度卷积神经网络”,2012 年,https://oreil.ly/aphzA

⁴⁹ 苏嘉伟,达尼洛·瓦斯孔塞洛斯·瓦加斯和樱井浩一,“用于欺骗深度神经网络的单像素攻击”,IEEE Evolutionary Computation,2019,23 卷 5 期:828–41,https://oreil.ly/LzN9D

⁵⁰ 苏杰伟等,“单像素攻击”。

⁵¹ 伊恩·J·古德费洛,乔纳森·施伦斯和克里斯蒂安·赛格迪,“解释和利用对抗性示例”,arXiv,2015 年 3 月 20 日,https://oreil.ly/9v2No;伊恩·J·古德费洛,大卫·沃德-法利,梅迪·米尔扎,阿伦·库尔维尔和约书亚·本吉奥,“Maxout 网络”,arXiv,2013 年 2 月 18 日,https://oreil.ly/L8mch

⁵² 谢义德-莫赛尼-德佐夫利,阿尔胡森·法兹维和帕斯卡尔·弗罗萨德,“DeepFool:一种简单且准确的欺骗深度神经网络的方法”,在IEEE 计算机视觉与模式识别会议(CVPR)中,2016 年,https://oreil.ly/dYVL8

⁵³ 宫藤猛,前田伸一,小山真典和石井信,“虚拟对抗训练:一种监督和半监督学习的正则化方法”,IEEE Pattern Analysis and Machine Intelligence,2017,https://oreil.ly/MBQeu

⁵⁴ Devlin 等,“BERT:深度双向转换器的预训练”。

⁵⁵ 张宏毅,穆斯塔法·西塞,扬·N·多芬和大卫·洛佩兹-帕兹,“mixup:超越经验风险最小化”,ICLR 2018https://oreil.ly/lIM5E

⁵⁶ 维特·桑德福特,闫科,佩里·J·皮克哈特和罗纳德·M·萨默斯,“使用生成对抗网络(CycleGAN)进行数据增强以提高 CT 分割任务的泛化能力”,科学报告,2019 年,9 卷 1 期:16884,https://oreil.ly/TDUwm

第五章:特征工程

2014 年,论文"Practical Lessons from Predicting Clicks on Ads at Facebook"声称拥有正确的特征是开发他们 ML 模型中最重要的事情。从那时起,我与许多公司合作,一次又一次地发现,一旦他们拥有可行的模型,正确的特征往往会给他们带来与调整超参数等聪明算法技术相比更大的性能提升。尽管使用了先进的模型架构,如果不使用好的特征集,其表现仍可能很差。

由于其重要性,许多 ML 工程和数据科学工作的大部分内容是提出新的有用特征。在本章中,我们将讨论常见的技术和与特征工程相关的重要考虑因素。我们将专门介绍一个微妙但灾难性的问题,这个问题已经使许多生产中的 ML 系统出现偏离:数据泄漏以及如何检测和避免它。

我们将在本章中讨论如何设计好的特征工程,考虑到特征重要性和特征泛化。谈到特征工程,一些人可能会想到特征存储。由于特征存储更接近于支持多个 ML 应用程序的基础设施,我们将在第十章中介绍特征存储。

学习到的特征与工程化的特征

当我在课堂上讲解这个主题时,我的学生经常问:“为什么我们要担心特征工程?深度学习不是承诺我们不再需要进行特征工程吗?”

他们是对的。深度学习的承诺是我们不再需要手工设计特征。因此,深度学习有时被称为特征学习¹。许多特征可以通过算法自动学习和提取。然而,我们离所有特征都可以自动化的时候还有很长的路要走。更不用说,截至撰写本文时,大多数生产中的 ML 应用程序并不是深度学习。让我们举一个例子来理解哪些特征可以自动提取,哪些特征仍然需要手工设计。

想象一下,你想构建一个情感分析分类器来判断评论是否是垃圾信息。在深度学习之前,当给定一段文本时,你必须手动应用经典的文本处理技术,如词形还原、扩展缩略语、去除标点符号并将所有内容转换为小写。之后,你可能希望将文本拆分为你选择的n值的 n-gram。​​​

对于那些不熟悉的人,n-gram 是来自给定文本样本的一系列连续的 n 个项目。这些项目可以是音素、音节、字母或单词。例如,对于帖子“I like food”,其单词级别的 1-gram 是 [“I”, “like”, “food”],其单词级别的 2-gram 是 [“I like”, “like food”]。如果我们希望 n 为 1 和 2,该句子的 n-gram 特征集合是:[“I”, “like”, “food”, “I like”, “like food”]。

图 5-1 显示了您可以用来手工创建文本的 n-gram 特征的经典文本处理技术示例。

图 5-1. 显示了您可以用来手工创建文本的 n-gram 特征的技术示例

一旦您为训练数据生成了 n-gram,您可以创建一个词汇表,将每个 n-gram 映射到一个索引。然后,您可以基于其 n-gram 的索引将每个帖子转换为向量。例如,如果我们有一个如 表 5-1 所示的七个 n-gram 的词汇表,每个帖子可以是一个包含七个元素的向量。每个元素对应于该索引处 n-gram 在帖子中出现的次数。“I like food” 将被编码为向量 [1, 1, 0, 1, 1, 0, 1]。然后可以将此向量用作 ML 模型的输入。

表 5-1. 1-gram 和 2-gram 词汇表示例

喜欢 食物 我喜欢 好食物 喜欢食物
0 1 2 3 4 5 6

特征工程需要领域特定技术的知识——在这种情况下,领域是自然语言处理(NLP)和文本的母语。这往往是一个迭代过程,可能会很脆弱。当我在我的早期 NLP 项目中采用这种方法时,我不断不得不重新启动我的过程,要么是因为我忘记了应用某项技术,要么是因为我使用的某项技术效果不佳而不得不撤销它。

然而,随着深度学习的兴起,这种痛苦大大减轻了。不必再担心词形还原、标点符号或停用词删除,您只需将原始文本拆分成单词(即标记化),从这些单词创建词汇表,并使用该词汇表将每个单词转换为一次性向量。希望您的模型能够从中提取有用的特征。在这种新方法中,文本的特征工程大部分已经自动化。对图像也取得了类似的进展。不必再手动从原始图像中提取特征并将这些特征输入到您的 ML 模型中,您可以直接将原始图像输入到深度学习模型中。

然而,一个 ML 系统很可能需要除了文本和图像之外的数据。例如,在检测评论是否为垃圾评论时,除了评论本身的文本外,您可能还希望使用关于以下信息的其他信息:

评论本身

它有多少赞成票/反对票?

发表此评论的用户

这个账户是什么时候创建的,他们发帖频率如何,以及他们有多少赞/踩?

发表评论的帖子

它有多少观看次数?受欢迎的帖子往往会吸引更多的垃圾信息。

在你的模型中有许多可能的特征可供使用。其中一些显示在图 5-2 中。选择要使用的信息以及如何将这些信息提取到可供机器学习模型使用的格式中的过程称为特征工程。对于像推荐用户在 TikTok 上观看下一个视频这样的复杂任务,使用的特征数量可能高达数百万个。对于像预测交易是否存在欺诈这样的领域特定任务,您可能需要具备银行业务和欺诈方面的专业知识,以能够提出有用的特征。

图 5-2. 有关评论、帖子或用户可能包含在您的模型中的一些可能特征

常见的特征工程操作

鉴于特征工程在机器学习项目中的重要性和普遍性,已经开发出许多技术来简化这一过程。在本节中,我们将讨论几个最重要的操作,您可能在从数据中提取特征时要考虑到。它们包括处理缺失值、缩放、离散化、编码分类特征,以及生成老派但仍然非常有效的交叉特征以及较新和令人兴奋的位置特征。这个列表远非全面,但它确实包括一些最常见和有用的操作,为您提供一个良好的起点。让我们深入探讨!

处理缺失值

在处理生产数据时,您可能首先注意到的是某些值缺失。然而,我采访过的许多机器学习工程师不知道的一件事是,并非所有类型的缺失值都是相同的。² 为了说明这一点,考虑预测某人是否会在接下来的 12 个月内购房的任务。我们的部分数据在表 5-2 中。

表 5-2. 预测未来 12 个月内购房的示例数据

| ID | 年龄 | 性别 | 年收入 | 婚姻状况 | 子女数 | 职业 | 购买? |
| --- | --- | --- | --- | --- | --- | --- |
| 1 | | A | 150,000 | | 1 | 工程师 | 否 |
| 2 | 27 | B | 50,000 | | | 老师 | 否 |
| 3 | | A | 100,000 | 已婚 | 2 | | 是 |
| 4 | 40 | B | | | 2 | 工程师 | 是 |
| 5 | 35 | B | | Single | 0 | 医生 | 是 |
| 6 | | A | 50,000 | | 0 | 老师 | 否 |
| 7 | 33 | B | 60,000 | 单身 | | 老师 | 否 |
| 8 | 20 | B | 10,000 | | | 学生 | 否 |

有三种类型的缺失值。这些类型的官方名称有点令人困惑,因此我们将详细举例以减少混淆。

非随机缺失(MNAR)

当值缺失的原因是值本身时,就是这种情况。在这个例子中,我们可能会注意到一些受访者没有披露他们的收入。调查后可能发现,未披露收入的受访者的收入往往比披露收入的受访者高。收入值的缺失是由于值本身的原因

随机缺失(MAR)

当值缺失的原因不是由于值本身,而是由于另一个观察到的变量时,就是这种情况。在这个例子中,我们可能会注意到某些性别“A”的受访者的年龄值经常缺失,这可能是因为此调查中性别“A”的人不喜欢透露他们的年龄。

完全随机缺失(MCAR)

值缺失时没有模式 时,就是这种情况。在这个例子中,我们可能认为列“工作”的缺失值可能是完全随机的,不是因为工作本身或任何其他变量。有时人们仅仅因为没有特定的原因而忘记填写该值。然而,这种类型的缺失非常罕见。通常会有某些值缺失的原因,你应该进行调查。

遇到缺失值时,你可以选择用特定值填充缺失值(插补),或者删除缺失值(删除)。我们将讨论两种方法。

删除

当我在面试中问候选人如何处理缺失值时,许多人倾向于选择删除,不是因为这是一种更好的方法,而是因为它更容易做到。

另一种删除的方式是 列删除:如果某个变量的缺失值太多,只需删除该变量。例如,在上面的例子中,“婚姻状况”变量的值超过 50%缺失,因此你可能会考虑从模型中删除此变量。这种方法的缺点是可能会移除重要信息并降低模型的准确性。婚姻状况可能与购买房产高度相关,因为已婚夫妇比单身人士更有可能拥有自己的房产。³

另一种删除的方式是 行删除:如果样本存在缺失值,只需删除该样本。当缺失值完全是随机的(MCAR),且具有缺失值的样本数量较少,例如少于 0.1%时,此方法可行。如果意味着删除了 10%的数据样本,你就不应该采取行删除。

然而,删除数据行也可能会移除模型需要用于预测的重要信息,特别是当缺失值不是随机的(MNAR)时。例如,你不应该删除缺失收入的性别 B 受访者的样本,因为收入缺失本身就是一种信息(缺失收入可能意味着更高的收入,因此与购买房产更相关),可以用于预测。

此外,删除数据行可能会在你的模型中引入偏差,尤其是在缺失值是随机的情况下(MAR)。例如,如果你删除表 5-2 中所有缺少年龄值的示例数据,你将从你的数据中删除所有性别为 A 的受访者,导致你的模型无法对性别为 A 的受访者做出良好的预测。

填充

即使删除数据很诱人,因为这样做很容易,但删除数据可能会导致丢失重要信息,并引入模型偏差。如果你不想删除缺失值,你就需要进行填充,也就是“用某些值填充它们”。决定使用哪些“特定的值”是难点所在。

一种常见的做法是使用默认值填充缺失值。例如,如果职位信息缺失,你可以用空字符串“”来填充。另一种常见的做法是使用均值、中位数或众数(即最常见的值)来填充缺失值。例如,如果某数据样本的月份为 7 月,而温度数值缺失,用 7 月份的温度中位数来填充是个不错的选择。

这两种做法在许多情况下效果很好,但有时会导致令人抓狂的错误。有一次,在我参与的一个项目中,我们发现模型输出的结果一团糟,因为应用程序的前端不再要求用户输入年龄,因此年龄数值缺失,模型用 0 来填充。但模型在训练过程中从未见过年龄数值为 0,因此无法做出合理的预测。

一般来说,你要避免用可能的值来填充缺失值,比如用 0 来填充孩子数量的缺失值——0 是孩子数量的一个可能值。这会导致很难区分信息缺失的人和确实没有孩子的人。

对于特定数据集,可能会同时或依次使用多种技术来处理缺失值。无论你使用何种技术,有一点是确定的:没有一种完美的处理缺失值的方式。删除数据时,你面临的风险是丢失重要信息或强化偏差。而填充数据时,你则面临注入自身偏差、给数据添加噪声或更糟的数据泄露的风险。如果你不知道数据泄露是什么,请不要惊慌,我们将在 “数据泄露” 部分详细介绍。

缩放

考虑预测某人是否在接下来的 12 个月内购房的任务,以及表格 5-2 中显示的数据。我们数据中变量 Age 的值范围从 20 到 40,而变量 Annual Income 的值范围从 10,000 到 150,000。当我们将这两个变量输入到 ML 模型时,它不会理解 150,000 和 40 代表不同的事物。它只会把它们都看作数字,并且因为 150,000 比 40 大得多,可能会赋予它更高的重要性,而不管哪个变量实际上对生成预测更有用。

在将特征输入模型之前,将它们缩放到相似的范围非常重要。这个过程称为特征缩放。这是你可以做的最简单的事情之一,通常能提升模型性能。忽略这一步可能会导致模型做出荒谬的预测,特别是在像梯度提升树和逻辑回归这样的传统算法中。⁴

缩放特征的一种直观方式是使它们在范围[0, 1]内。给定变量x,其值可以使用以下公式重新缩放到此范围:

x ⠀ ™ = x-min(x) max(x)-min(x)

你可以验证,如果x是最大值,缩放后的值x′将为 1。如果x是最小值,缩放后的值x′将为 0。

如果你希望你的特征在任意范围[a, b]内——根据经验,我发现范围[–1, 1]比范围[0, 1]更有效——你可以使用以下公式:

x ' = a + (x-min(x))(b-a) max(x)-min(x)

当你不想对变量做任何假设时,将其缩放到任意范围通常是有效的。如果你认为你的变量可能符合正态分布,将它们标准化到均值为零、方差为一可能会有帮助。这个过程称为标准化

x ' = x-x ¯ σ ,

其中x ¯表示变量x的均值,σ表示其标准差。

在实践中,ML 模型往往难以处理偏斜分布的特征。为了帮助减少偏斜,常用的技术之一是对数变换:对你的特征应用对数函数。对数变换如何使你的数据更加对称的示例显示在图 5-3 中。虽然这种技术在许多情况下能够提升性能,但并不适用于所有情况,你应该警惕在对数变换数据而不是原始数据上执行的分析。⁵

图 5-3. 在许多情况下,对数变换可以帮助减少数据的偏斜

关于缩放有两件重要的事情需要注意。一是它是数据泄漏的常见来源(这将在“数据泄漏”部分详细介绍)。另一件事是,它通常需要全局统计数据——你必须查看整个或部分训练数据来计算其最小值、最大值或平均值。在推断时,你会重复使用训练期间获取的统计数据来缩放新数据。如果新数据与训练数据相比有了显著变化,这些统计数据将不会非常有用。因此,经常重新训练模型以考虑这些变化是很重要的。

离散化

尽管在实践中,我很少发现离散化有所帮助,但这个技术还是包含在这本书中以确保完整性。想象一下,我们用表 5-2 中的数据建立了一个模型来预测房屋购买。在训练期间,我们的模型看到了“150,000”、“50,000”、“100,000”等年收入值。在推断时,我们的模型遇到了一个年收入为“9,000.50”的例子。

直觉上,我们知道每年$9,000.50 和$10,000 之间的差异并不大,我们希望我们的模型以相同的方式对待它们。但模型不知道这一点。我们的模型只知道 9,000.50 和 10,000 是不同的,并且会对它们进行不同处理。

离散化是将连续特征转换为离散特征的过程。这个过程也称为量化或分桶。通过为给定的值创建桶,来实现这一目标。对于年收入,你可能想将它们分成以下三个桶:

  • 低收入:每年少于$35,000

  • 中等收入:每年在$35,000 到$100,000 之间

  • 高收入:每年超过$100,000

我们的模型不再需要学习无限可能的收入数额,而是可以专注于学习只有三个类别,这是一个更容易的任务。这种技术在有限的训练数据中应该更有帮助。

尽管从定义上讲,离散化是针对连续特征的,但它也可以用于离散特征。年龄变量是离散的,但将其值分组成如下桶可能仍然有用:

  • 少于 18 岁

  • 介于 18 和 22 之间

  • 介于 22 和 30 之间

  • 介于 30 和 40 之间

  • 介于 40 和 65 之间

  • 超过 65 岁

不足之处在于,这种分类会在类别边界引入不连续性——例如,$34,999 现在被视为与$35,000 完全不同,而$35,000 则与$100,000 相同。选择类别的边界可能并不那么容易。你可以尝试绘制值的直方图并选择有意义的边界。总的来说,常识、基本分位数和有时主题专业知识可以提供帮助。

编码分类特征

我们已经讨论了如何将连续特征转换为分类特征。在本节中,我们将讨论如何最好地处理分类特征。

那些没有在生产环境中处理过数据的人倾向于认为类别是静态的,这意味着类别随时间不会改变。对于许多类别来说确实如此。例如,年龄段和收入段不太可能改变,而且你提前知道有多少个类别。处理这些类别很简单。你只需给每个类别一个编号,问题就解决了。

然而,在生产环境中,类别会发生变化。想象你正在构建一个推荐系统,以预测用户可能想从亚马逊购买的产品。你希望使用的一个特征是产品品牌。当查看亚马逊的历史数据时,你意识到有很多品牌。即使在 2019 年,亚马逊已经有超过两百万个品牌了!

品牌数量令人难以置信,但你认为:“我仍然能处理这个问题。”你将每个品牌编码为一个数字,现在你有了两百万个数字,从 0 到 1,999,999,对应两百万个品牌。你的模型在历史测试集上表现出色,你获得了测试今天流量的 1%的批准。

在生产环境中,你的模型因为遇到一个之前没有见过的品牌而崩溃,无法编码。新品牌不断加入亚马逊。为了解决这个问题,你创建了一个名为 UNKNOWN 的类别,值为 200 万,以捕获训练期间模型没有见过的所有品牌。

你的模型不再崩溃了,但你的销售人员抱怨说他们的新品牌没有流量。这是因为你的模型在训练集中没有看到类别 UNKNOWN,所以它不推荐任何 UNKNOWN 品牌的产品。你通过只编码前 99%最流行的品牌并将剩余的 1%品牌编码为 UNKNOWN 来解决这个问题。这样,至少你的模型知道如何处理 UNKNOWN 品牌。

你的模型大约运行了一个小时,然后产品推荐的点击率急剧下降。在过去的一个小时内,有 20 个新品牌加入了你的网站;其中一些是新的奢侈品牌,一些是可疑的仿冒品牌,一些是老牌品牌。然而,你的模型对待它们的方式和对待训练数据中不受欢迎的品牌一样。

这不是只有在亚马逊工作才会发生的极端案例。这个问题经常发生。例如,如果你想预测一条评论是否是垃圾评论,你可能想使用发布该评论的账户作为特征,而新账户一直在被创建。同样的情况也适用于新产品类型、新网站域名、新餐厅、新公司、新 IP 地址等等。如果你处理任何这些内容,你都必须解决这个问题。

发现解决这个问题的方法竟然如此困难。你不想将其放入一个桶中,因为这样做可能非常困难——你怎么能把新用户账户分成不同的组呢?

解决这个问题的一种方法是哈希技巧,由 Microsoft 开发的 Vowpal Wabbit 包推广。⁷ 这个技巧的要点是使用哈希函数为每个类别生成一个哈希值。这个哈希值将成为该类别的索引。由于可以指定哈希空间,可以预先确定一个特征的编码值的数量,而不需要知道将会有多少类别。例如,如果选择一个 18 位的哈希空间,对应于 2¹⁸ = 262,144 个可能的哈希值,所有的类别,即使是你的模型以前从未见过的,都将被编码为 0 到 262,143 之间的索引。

哈希函数的一个问题是碰撞:两个类别被分配相同的索引。然而,对于许多哈希函数来说,碰撞是随机的;新品牌可以与任何现有品牌共享索引,而不是总是与不受欢迎的品牌共享索引,这是当我们使用先前的未知类别时发生的情况。碰撞哈希特征的影响,幸运的是,不是那么严重。Booking.com 的研究表明,即使是 50%的碰撞特征,性能损失也不到 0.5%,如图 5-4⁸所示。

图 5-4. 50%的碰撞率只会使对数损失增加不到 0.5%。来源:Lucas Bernardi

您可以选择一个足够大的哈希空间来减少碰撞。您还可以选择具有您想要的属性的哈希函数,例如局部敏感哈希函数,其中相似的类别(例如具有相似名称的网站)被哈希到彼此靠近的值。

因为这是一个技巧,学术界通常认为它是一种巧妙的方法,并且从机器学习课程中排除。但其在行业中的广泛采用证明了这种技巧的有效性。它对于 Vowpal Wabbit 至关重要,并且是 scikit-learn、TensorFlow 和 gensim 框架的一部分。在产品中,这种技巧尤其在连续学习环境中特别有用,其中模型从传入的示例中学习。我们将在第九章中介绍连续学习。

特征交叉

特征交叉是将两个或更多特征组合以生成新特征的技术。这种技术对于建模特征之间的非线性关系非常有用。例如,在预测某人是否会在接下来的 12 个月内购买房屋的任务中,您可能会怀疑婚姻状况和子女数量之间存在非线性关系,因此您将它们组合成一个新特征“婚姻和子女”,如表 5-3 中所示。

表 5-3. 两个特征如何组合以创建一个新特征的示例

婚姻 单身 已婚 单身 单身 已婚
子女 0 2 1 0 1
婚姻和子女 单身,0 已婚,2 单身,1 单身,0 已婚,1

因为特征交叉帮助模型建模变量之间的非线性关系,这对于不能学习或不擅长学习非线性关系的模型非常重要,例如线性回归、逻辑回归和基于树的模型。在神经网络中这并不是那么重要,但它仍然可能很有用,因为显式特征交叉有时可以帮助神经网络更快地学习非线性关系。DeepFM 和 xDeepFM 是成功利用显式特征交互的模型家族,用于推荐系统和点击率预测(参见 9)。

特征交叉的一个警告是它可能导致特征空间的爆炸增长。想象一下,特征 A 有 100 个可能的取值,而特征 B 有 100 个可能的特征;交叉这两个特征将导致一个具有 100 × 100 = 10,000 个可能值的特征。你将需要更多的数据来让模型学习所有这些可能的值。另一个警告是,由于特征交叉增加了模型使用的特征数量,它可能导致模型对训练数据过拟合。

离散和连续位置嵌入

在 2017 年 Vaswani 等人的论文“Attention Is All You Need”中首次介绍给深度学习社区,位置嵌入已经成为计算机视觉和自然语言处理中许多应用的标准数据工程技术。我们将通过一个示例来说明为什么位置嵌入是必要的,以及如何实现它。

考虑语言建模任务,你希望基于先前的标记序列预测下一个标记(例如单词、字符或子词)。在实践中,序列长度可以高达 512,甚至更大。然而,为简单起见,让我们以单词作为标记,并使用长度为 8 的序列。给定一个任意的 8 个单词序列,例如“有时候我真的只想做的是”,我们希望预测下一个单词。

如果我们使用循环神经网络,它将按顺序处理单词,这意味着单词的顺序隐含地成为输入。然而,如果我们使用像 Transformer 这样的模型,单词是并行处理的,因此需要明确地输入单词的位置,以便我们的模型知道这些单词的顺序(“一只狗咬了一个孩子”与“一个孩子咬了一只狗”是完全不同的)。我们不希望将绝对位置 0、1、2、…、7 直接输入到我们的模型中,因为经验上,神经网络不擅长处理不是单位方差的输入(这就是为什么我们要缩放我们的特征,正如在“缩放”部分中讨论的那样)。

如果我们将位置重新缩放到 0 到 1 之间,使得 0、1、2、…、7 成为 0、0.143、0.286、…、1,那么这两个位置之间的差异对于神经网络学习区分将会太小。

处理位置嵌入的一种方法是将其视为处理词嵌入的方式。使用词嵌入时,我们使用一个具有词汇量大小作为其列数的嵌入矩阵,每列是该列索引处词的嵌入。对于位置嵌入,列数是位置数。在我们的情况下,由于我们只处理前一个序列大小为 8,因此位置从 0 到 7(参见图 5-5)。

位置嵌入的嵌入大小通常与单词的嵌入大小相同,以便它们可以相加。例如,单词“food”在位置 0 的嵌入是单词“food”的嵌入向量和位置 0 的嵌入向量的和。这是 Hugging Face 的 BERT 在 2021 年 8 月实施位置嵌入的方式。由于嵌入随着模型权重的更新而改变,我们说位置嵌入是可学习的。

图 5-5。一种嵌入位置的方式是将它们视为处理词嵌入的方式

位置嵌入也可以是固定的。每个位置的嵌入仍然是具有S个元素的向量(S是位置嵌入的大小),但是每个元素都是使用函数预定义的,通常是正弦和余弦。在原始 Transformer 论文中,如果元素位于偶数索引,则使用正弦。否则,使用余弦。参见图 5-6。

图 5-6。固定位置嵌入的示例。H是模型生成的输出的维数。

固定位置嵌入是所谓傅里叶特征的一个特例。如果位置在位置嵌入中是离散的,那么傅里叶特征也可以是连续的。考虑涉及三维对象(如茶壶)表示的任务。茶壶表面上的每个位置由三维坐标表示,这是连续的。当位置是连续的时,要构建一个具有连续列索引的嵌入矩阵会非常困难,但使用正弦和余弦函数的固定位置嵌入仍然有效。

下面是坐标v的嵌入向量的广义格式,也称为坐标v的傅里叶特征。已经显示傅里叶特征可以提高模型对接收坐标(或位置)作为输入的任务的性能。如果感兴趣,您可能想在“Fourier Features Let Networks Learn High Frequency Functions in Low Dimensional Domains”(Tancik 等人,2020 年)中进一步阅读。

γ ( v ) = [a 1 cos(2πb 1 T v),a 1 sin(2πb 1 T v),...,a m cos(2πb m T v),a m sin(2πb m T v)] T

数据泄露

2021 年 7 月,《麻省理工科技评论》发表了一篇引人深思的文章,题为“数百种 AI 工具被开发用于捕捉 Covid。但它们一个都没用。” 这些模型是为了从医学扫描中预测 COVID-19 风险而训练的。文章列举了多个例子,显示在评估时表现良好的机器学习模型在实际生产环境中却无法使用。

举例来说,研究人员训练他们的模型时混合了患者仰卧和站立时拍摄的扫描。"因为仰卧扫描的患者更可能重症,所以模型学会从人体位置预测严重的 Covid 风险。"

在其他一些情况下,模型“被发现依赖于某些医院用于标记扫描的文本字体。因此,来自严重病例负担更重的医院的字体成为 Covid 风险的预测因子。”¹²

这两个例子都是数据泄漏的示例。数据泄漏 是指标签的某种形式“泄漏”到用于进行预测的特征集中,而这些信息在推断时不可用。

数据泄漏具有挑战性,因为泄漏通常不明显。它是危险的,因为即使经过了广泛的评估和测试,它也可能导致你的模型以意想不到的令人瞩目的方式失败。让我们通过另一个例子来演示数据泄漏是什么。

假设你想要建立一个机器学习模型来预测肺部 CT 扫描是否显示癌症迹象。你从 A 医院获取了数据,删除了医生的诊断信息,并训练了你的模型。在 A 医院的测试数据上表现良好,但在 B 医院的数据上表现不佳。

经过深入调查,你发现在 A 医院,当医生认为患者患有肺癌时,他们会将患者送往更先进的扫描机器,这些机器会输出略有不同的 CT 扫描图像。你的模型学会依赖于用于预测扫描图像是否显示肺癌迹象的扫描机器信息。而 B 医院随机将患者送往不同的 CT 扫描机器,因此你的模型无法依赖任何信息。我们称这种情况为标签在训练过程中泄漏到特征中。

数据泄漏不仅可能发生在这个领域的新手身上,还曾经发生在几位我敬仰的经验丰富的研究人员身上,甚至在我的一个项目中也发生过。尽管数据泄漏很常见,但在机器学习课程中很少涉及。

数据泄漏的常见原因

在这一部分中,我们将讨论一些数据泄漏的常见原因及其如何避免。

随机分割时间相关的数据,而不是按时间分割

当我在大学学习机器学习时,我被教导将我的数据随机分成训练、验证和测试集。这也是机器学习研究论文中常见的数据分割方式。然而,这也是数据泄漏的一个常见原因之一。

在许多情况下,数据是时间相关的,这意味着生成数据的时间影响其标签分布。有时,相关性是显而易见的,比如股票价格的情况。简单地说,相似股票的价格倾向于一起波动。如果今天有 90% 的科技股票下跌,其他 10% 的科技股票很可能也会下跌。在构建预测未来股票价格的模型时,你希望按时间分割训练数据,比如在前六天的数据上训练模型,然后在第七天的数据上进行评估。如果随机分割数据集,第七天的价格将包含在训练集中,并泄漏到模型中关于那天市场情况的信息。我们称未来的信息泄漏到了训练过程中。

然而,在许多情况下,相关性并不明显。考虑预测某人是否会点击歌曲推荐的任务。是否会听某首歌不仅取决于他们的音乐品味,还取决于那一天的一般音乐趋势。如果一位艺术家某天去世,人们很可能更倾向于听那位艺术家的歌曲。通过在训练集中包含某一天的样本,该天的音乐趋势信息将传递到你的模型中,使其更容易对同一天的其他样本进行预测。

为了防止未来信息泄漏到训练过程中并且允许模型在评估过程中作弊,尽可能按时间分割数据,而不是随机分割。例如,如果你有五周的数据,使用前四周作为训练集,然后像图 5-7 中展示的那样,随机分割第五周为验证集和测试集。

图 5-7. 按时间分割数据,防止未来信息泄漏到训练过程中

分割前先进行缩放

如在章节 “缩放” 中讨论的,对于你的特征进行缩放是很重要的。缩放需要全局统计数据,比如均值、方差。一个常见的错误是在将训练数据分割成不同部分之前,使用整个训练数据生成全局统计数据,将测试样本的均值和方差泄漏到训练过程中,使模型调整其对测试样本的预测。这些信息在生产中是不可用的,因此模型的性能可能会下降。

为了避免这种泄漏,总是在缩放之前先分割数据,然后使用训练集的统计数据来缩放所有的分割。有些人甚至建议在进行任何探索性数据分析和数据处理之前先分割数据,这样就不会意外地获取有关测试集的信息。

使用测试集的统计数据填充缺失数据

处理特征缺失值的一种常见方法是使用所有现有值的平均值或中位数来填充(输入)它们。如果使用整个数据集而不是仅使用训练集来计算平均值或中位数,则可能导致泄漏。这种类型的泄漏类似于由缩放引起的泄漏,可以通过仅使用训练集的统计数据来填充所有分割中的缺失值来预防。

分割前的数据重复处理不当

如果您的数据中存在重复或接近重复的情况,在分割数据之前未能去除它们可能导致相同样本出现在训练和验证/测试分割中。数据重复在行业中非常普遍,并且在流行的研究数据集中也有发现。例如,CIFAR-10 和 CIFAR-100 是用于计算机视觉研究的两个流行数据集。它们在 2009 年发布,但直到 2019 年,Barz 和 Denzler 才发现,CIFAR-10 和 CIFAR-100 数据集的测试集中有 3.3%和 10%的图像在训练集中存在重复。¹⁵

数据重复可能是由于数据收集或合并不同数据源导致的。2021 年的一篇《自然》文章将数据重复列为使用机器学习检测 COVID-19 时的常见陷阱之一,原因是“一个数据集合并了几个其他数据集,却没有意识到一个组件数据集已经包含了另一个组件。”¹⁶ 数据重复也可能是由于数据处理引起的——例如,过度采样可能导致某些示例的重复。

为了避免这种情况,请在分割之前和之后都检查重复项以确保安全。如果您过度采样数据,请在分割之后再执行此操作。

组泄漏

一组示例具有强相关的标签,但分为不同的分割。例如,一个患者可能有两个相隔一周的肺部 CT 扫描,它们可能在是否包含肺癌迹象的标签上具有相同的标签,但其中一个在训练集中,另一个在测试集中。这种类型的泄漏在包含同一对象照片的客观检测任务中很常见——一些照片落在训练集中,而其他照片落在测试集中。如果不了解数据生成方式,很难避免这种数据泄漏。

来自数据生成过程的泄漏

早些时候关于 CT 扫描显示肺癌迹象信息通过扫描机泄漏的示例就是这种类型的泄漏。检测这种数据泄漏需要深入了解数据收集方式。例如,如果不了解不同的扫描机或两家医院的程序不同,就很难弄清楚模型在医院 B 表现不佳的原因。

没有绝对可靠的方法来避免这种类型的泄漏,但你可以通过跟踪数据的来源并了解数据的收集和处理方式来减少风险。归一化你的数据,使不同来源的数据具有相同的均值和方差。如果不同的 CT 扫描机器输出具有不同分辨率的图像,将所有图像归一化到相同的分辨率会使模型更难区分哪些图像来自哪个扫描机器。还要不要忘记将更多了解数据收集和使用背景的学科专家纳入机器学习设计过程中!

检测数据泄漏

数据泄漏可能发生在许多步骤中,从生成、收集、抽样、分割和处理数据到特征工程。在机器学习项目的整个生命周期中监控数据泄漏非常重要。

测量每个特征或一组特征相对于目标变量(标签)的预测能力。如果一个特征具有异常高的相关性,请调查该特征的生成方式及其相关性是否合理。可能两个特征单独来看不包含泄漏信息,但两个特征一起可能包含泄漏信息。例如,当构建一个预测员工在公司停留时间的模型时,起始日期和结束日期单独来看并不能告诉我们太多关于他们的任职期,但两者结合起来可以提供这些信息。

进行消融研究,衡量一个特征或一组特征对模型的重要性。如果删除某个特征会显著降低模型的性能,请调查该特征为何如此重要。如果特征数量庞大,比如一千个特征,可能无法对每一种可能的组合都进行消融研究,但偶尔使用你怀疑最重要的一些特征子集进行消融研究仍然有用。这是学科专业知识在特征工程中如何派上用场的另一个例子。消融研究可以在你自己的时间安排下线下运行,所以你可以利用机器在空闲时间进行这项工作。

要密切关注添加到模型的新特征。如果添加新特征显著改善了模型的性能,那么要么该特征非常好,要么该特征只是包含有关标签的泄漏信息。

每次查看测试分割时都要非常小心。如果你在任何方式上使用测试分割,而不仅仅是报告模型的最终性能,比如用于提出新特征的想法或调整超参数,都存在将未来信息泄漏到训练过程中的风险。

工程化良好特征

一般来说,增加更多特征会提高模型性能。根据我的经验,用于生产模型的特征列表随时间只会增加。然而,更多特征并不总是意味着更好的模型性能。因为在训练和为模型提供服务时,拥有太多特征可能会因以下原因而不利:

  • 拥有更多特征意味着有更多数据泄露的机会。

  • 太多的特征可能会导致过拟合。

  • 太多的特征可能会增加为模型提供服务所需的内存,这反过来可能需要您使用更昂贵的机器/实例来为模型提供服务。

  • 太多的特征可能会增加在线预测时的推断延迟,特别是如果您需要从原始数据中提取这些特征进行在线预测。我们将在第七章更深入地讨论在线预测。

  • 无用的特征变成了技术债务。每当您的数据管道发生变化时,所有受影响的特征都需要相应地进行调整。例如,如果有一天您的应用程序决定不再接收有关用户年龄的信息,则需要更新使用用户年龄的所有特征。

理论上,如果一个特征对模型的预测没有帮助,正则化技术如 L1 正则化应该将该特征的权重减少到 0。然而,在实践中,如果不再有用(甚至可能有害)的特征被移除,优先考虑好的特征可能会帮助模型更快地学习。

您可以存储已移除的特征以便稍后添加回来。您也可以仅存储一般特征定义,以便在组织中的团队之间重复使用和共享。谈论特征定义管理时,一些人可能会考虑特征存储作为解决方案。然而,并非所有特征存储管理特征定义。我们将在第十章进一步讨论特征存储。

在评估特征对模型是否有益时,您可能要考虑两个因素:对模型的重要性和对未见数据的泛化能力。

特征重要性

有许多不同的方法来衡量特征的重要性。如果您使用像增强梯度树这样的经典机器学习算法,衡量您特征重要性的最简单方法是使用 XGBoost 实现的内置特征重要性函数。¹⁷ 对于更多与模型无关的方法,您可能需要研究 SHAP(SHapley Additive exPlanations)。¹⁸ InterpretML是一个很好的开源软件包,利用特征重要性帮助您理解模型如何进行预测。

特征重要性测量的确切算法很复杂,但直觉上,特征对模型的重要性是通过如果删除该特征或包含该特征的一组特征,模型性能会如何恶化来衡量的。SHAP 之所以出色,是因为它不仅衡量特定模型对整体模型的重要性,还衡量每个特征对模型特定预测的贡献。图 5-8 和 5-9 展示了 SHAP 如何帮助您理解每个特征对模型预测的贡献。

图 5-8. 每个特征对模型单次预测的贡献,由 SHAP 测量。值 LSTAT = 4.98 对此特定预测贡献最大。来源:Scott Lundberg¹⁹

图 5-9. 每个特征对模型的贡献,由 SHAP 测量。特征 LSTAT 具有最高的重要性。来源:Scott Lundberg

通常,少数特征占据了模型特征重要性的大部分。在衡量点击率预测模型的特征重要性时,Facebook 的广告团队发现,前 10 个特征负责模型总特征重要性的约一半,而最后的 300 个特征贡献的特征重要性不到 1%,如 图 5-10 所示²⁰。

图 5-10. 提升特征重要性。X 轴对应特征数。特征重要性以对数刻度显示。来源:He 等

特征重要性技术不仅有助于选择正确的特征,还有助于解释性,因为它们帮助您了解模型的内部运作方式。

特征泛化

由于机器学习模型的目标是在未见数据上做出正确预测,用于模型的特征应该对未见数据泛化。并非所有特征都能同等泛化。例如,在预测评论是否为垃圾的任务中,每条评论的标识符完全不具备泛化能力,不应作为模型特征。然而,发表评论的用户标识符(如用户名)可能对模型进行预测仍然有用。

衡量特征泛化不如衡量特征重要性科学,除了统计知识,还需要直觉和学科专业知识。总体而言,关于泛化,有两个方面需要考虑:特征覆盖和特征值的分布。

覆盖率是数据中具有该特征值的样本的百分比——因此缺失值越少,覆盖率越高。一个粗略的经验法则是,如果这个特征在你的数据中出现的百分比非常小,它可能不具备很好的泛化能力。例如,如果你想建立一个模型来预测某人在接下来的 12 个月内是否会购买房屋,而你认为某人拥有孩子的数量会是一个好特征,但你只能获取到这个信息的数据占总数据的 1%,那么这个特征可能并不是很有用。

这个经验法则是粗略的,因为即使某些特征在大多数数据中缺失,它们仍然可能是有用的。特别是当缺失值不是随机分布时,这意味着拥有或者不拥有这个特征可能是其价值的一个强烈指示。例如,如果一个特征只出现在你的数据中的 1%中,但这个特征中 99%的样本都有正面标签,这个特征就是有用的,你应该使用它。

特征的覆盖率在不同数据切片之间可能会有很大差异,甚至在同一数据切片中随时间也可能有所不同。如果一个特征在训练集和测试集中的覆盖率差别很大(例如在训练集中出现在 90%的样本中,但在测试集中只有 20%的样本中出现),这表明你的训练集和测试集并不来自同一分布。你可能需要调查一下你分割数据的方式是否合理,以及这个特征是否导致数据泄露的原因。

对于出现的特征值,你可能需要查看它们的分布情况。如果在已见数据(如训练集)中出现的值集合与未见数据(如测试集)中出现的值集合没有重叠,这个特征甚至可能会影响你模型的性能。

作为一个具体的例子,假设你想建立一个模型来估算给定出租车行程需要的时间。你每周重新训练这个模型,并且你想使用过去六天的数据来预测今天的到达时间(ETA)。其中一个特征是 DAY_OF_THE_WEEK,你认为这个特征很有用,因为工作日的交通通常比周末糟糕。这个特征的覆盖率是 100%,因为它在每个特征中都出现。然而,在训练集中,这个特征的取值是星期一到星期六,而在测试集中,这个特征的取值是星期日。如果你在模型中包含这个特征,但没有巧妙地编码这些天,它将无法推广到测试集,并可能损害你模型的性能。

另一方面,HOUR_OF_THE_DAY 是一个很好的特征,因为一天中的时间也会影响交通,并且这个特征在训练集和测试集中的取值范围完全重叠 100%。

在考虑特征的泛化性时,存在泛化性和特异性之间的权衡。您可能会意识到某小时的交通状况只取决于该小时是否是高峰时间。因此,生成特征 IS_RUSH_HOUR,并在早上 7 点到 9 点或下午 4 点到 6 点之间将其设置为 1。IS_RUSH_HOUR 比 HOUR_OF_THE_DAY 更具泛化性,但更不具体。在没有 HOUR_OF_THE_DAY 的情况下使用 IS_RUSH_HOUR 可能会导致模型丢失有关小时重要信息。

总结

因为今天的机器学习系统的成功仍然取决于它们的特征,对于希望在生产中使用机器学习的组织来说,投入时间和精力进行特征工程非常重要。

如何设计好的特征是一个复杂的问题,没有百分之百的答案。最好的学习方法是通过经验:尝试不同的特征并观察它们对模型性能的影响。也可以从专家那里学习。我发现阅读关于 Kaggle 竞赛获胜团队如何设计特征的文章非常有益,可以了解他们的技术和考虑过的因素。

特征工程通常涉及专业知识,而专业知识可能并不总是工程师的强项,因此设计工作流程以便非工程师也能参与到该过程中非常重要。

这里总结了特征工程的最佳实践:

  • 将数据按时间拆分为训练/验证/测试集,而不是随机分配。

  • 如果对数据进行过采样,应在数据拆分后进行。

  • 在拆分数据后进行缩放和归一化,以避免数据泄漏。

  • 仅使用训练集的统计数据来缩放特征和处理缺失值,而不是整个数据集。

  • 了解数据的生成、收集和处理方式。如有可能,应该邀请领域专家参与其中。

  • 跟踪数据的来源。

  • 了解特征对模型的重要性。

  • 使用泛化性好的特征。

  • 从模型中移除不再有用的特征。

拥有一组好的特征后,我们将继续工作流程的下一部分:训练机器学习模型。在我们继续之前,我想再次强调,转向建模并不意味着我们完成了数据处理或特征工程。在大多数真实世界的机器学习项目中,收集数据和进行特征工程的过程会随着模型投入使用而持续进行。我们需要使用新进数据不断改进模型,这一点将在第九章中讨论。

¹ Loris Nanni、Stefano Ghidoni 和 Sheryl Brahnam 在《模式识别》(Pattern Recognition)71 卷(2017 年 11 月)中讨论了手工制作和非手工制作的计算机视觉分类特征,参见https://oreil.ly/CGfYQ; Wikipedia, s.v. “Feature learning,” https://oreil.ly/fJmwN

² 根据我的经验,在面试过程中,一个人处理给定数据集中缺失值的能力很大程度上决定了他们在日常工作中的表现。

³ Rachel Bogardus Drew, "关于婚姻和房屋所有权的三个事实," 哈佛大学住房研究中心, 2014 年 12 月 17 日, https://oreil.ly/MWxFp.

⁴ 特征缩放一度将我的模型性能提升了近 10%。

⁵ 冯长勇, 王宏悦, 卢乃基, 陈田, 何华, 陆莹, 和徐新明, "对数转换及其在数据分析中的影响," 上海精神病学档案 26 卷 2 期 (2014 年 4 月): 105–9, https://oreil.ly/hHJjt.

⁶ "Amazon 上的两百万品牌," Marketplace Pulse, 2019 年 6 月 11 日, https://oreil.ly/zrqtd.

⁷ 维基百科,见“特征哈希”,https://oreil.ly/tINTc.

⁸ Lucas Bernardi, "不要被哈希技巧愚弄," Booking.com, 2018 年 1 月 10 日, https://oreil.ly/VZmaY.

⁹ 郭慧峰, 汤锐明, 叶云明, 李政果, 和何秀强, "DeepFM:基于因子分解机的神经网络用于点击率预测," 第二十六届国际人工智能联合会议论文集 (IJCAI, 2017), https://oreil.ly/1Vs3v; 连建勋, 周晓欢, 张福政, 陈忠霞, 谢星, 和孙广忠, "xDeepFM:结合显式和隐式特征交互的推荐系统," arXiv, 2018 年, https://oreil.ly/WFmFt.

¹⁰ Flavian Vasile, Elena Smirnova 和 Alexis Conneau, "Meta-Prod2Vec——使用侧信息的产品嵌入," arXiv, 2016 年 7 月 25 日, https://oreil.ly/KDaEd; "产品嵌入与向量," Coveo, https://oreil.ly/ShaSY.

¹¹ Andrew Zhai, "推荐系统的表示学习," 2021 年 8 月 15 日, https://oreil.ly/OchiL.

¹² Will Douglas Heaven, "数百种 AI 工具用于检测 Covid,但没有一款有用," MIT Technology Review, 2021 年 7 月 30 日, https://oreil.ly/Ig1b1.

¹³ Zidmie, "泄漏解释!" Kaggle, https://oreil.ly/1JgLj.

¹⁴ Addison Howard, "竞赛回顾——恭喜我们的获奖者!" Kaggle, https://oreil.ly/wVUU4.

¹⁵ Björn Barz 和 Joachim Denzler, “我们在测试数据上进行训练吗?净化 CIFAR 近似重复数据,” Journal of Imaging 6, no. 6 (2020): 41.

¹⁶ Michael Roberts, Derek Driggs, Matthew Thorpe, Julian Gilbey, Michael Yeung, Stephan Ursprung, Angelica I. Aviles-Rivero 等,“使用胸部 X 光和 CT 扫描检测和预测 COVID-19 的机器学习常见问题和建议”,Nature Machine Intelligence 3 (2021): 199–217, https://oreil.ly/TzbKJ.

¹⁷ 使用 XGBoost 函数 get_score

¹⁸ 一个很棒的开源 Python 包用于计算 SHAP,可以在 GitHub 上找到。

¹⁹ Scott Lundberg, SHAP(SHapley 加法解释),GitHub 代码库,最后访问于 2021 年,https://oreil.ly/c8qqE.

²⁰ Xinran He, Junfeng Pan, Ou Jin, Tianbing Xu, Bo Liu, Tao Xu, Yanxin Shi 等,“在 Facebook 预测广告点击的实际经验教训”,在 ADKDD ’14: Proceedings of the Eighth International Workshop on Data Mining for Online Advertising 中 (2024 年 8 月): 1–9, https://oreil.ly/dHXeC.

第六章:模型开发与离线评估

在第四章中,我们讨论了如何为您的模型创建训练数据;在第五章中,我们讨论了如何从这些训练数据中进行特征工程。通过初始的特征集,我们将进入 ML 系统的 ML 算法部分。对我来说,这总是最有趣的一步,因为它允许我尝试不同的算法和技术,甚至是最新的技术。这也是我可以看到自己在数据和特征工程上投入的所有辛勤工作转化为一个系统的第一步,其输出(预测)我可以用来评估我的努力成功的第一步。

要构建一个 ML 模型,我们首先需要选择要构建的 ML 模型。有很多 ML 算法可供选择,而且还在积极开发中。本章从选择最适合您任务的最佳算法的六个提示开始。

接下来的部分讨论了模型开发的不同方面,如调试、实验跟踪与版本控制、分布式训练和自动化机器学习。

模型开发是一个迭代过程。每次迭代后,您都希望将模型的性能与之前迭代的性能进行比较,并评估这次迭代是否适合投入生产。本章的最后一节专门讨论了在将模型部署到生产环境之前如何评估您的模型,涵盖了一系列评估技术,包括扰动测试、不变性测试、模型校准和基于幻灯片的评估。

我期望大多数读者已经了解常见的 ML 算法,如线性模型、决策树、k 最近邻居和不同类型的神经网络。本章将讨论围绕这些算法的技术,但不会详细介绍它们的工作原理。因为本章涉及 ML 算法,所以需要比其他章节更多的 ML 知识。如果您对此不熟悉,我建议在阅读本章之前参加在线课程或阅读一本关于 ML 算法的书籍。希望快速复习基本 ML 概念的读者可能会发现书的 GitHub 仓库中“基本 ML 复习”部分有所帮助。

模型开发与训练

在本节中,我们将讨论帮助您开发和训练模型的必要方面,包括如何为您的问题评估不同的 ML 模型、创建模型集合、实验跟踪与版本控制以及分布式训练,这对于目前通常训练模型的规模是必要的。我们将以更高级别的 AutoML 主题结束本节——使用 ML 自动选择最适合您问题的模型。

评估 ML 模型

针对任何给定问题,都有许多可能的解决方案。面对一个可以利用机器学习来解决的任务,你可能会想知道应该使用哪种机器学习算法。例如,你应该从你已经熟悉的逻辑回归开始吗?还是应该尝试一个新的看起来是你问题的最新技术的新模型?一位资深同事提到,梯度提升树对她过去这个任务总是有效——你应该听她的建议吗?

如果你有无限的时间和计算能力,理性的做法是尝试所有可能的解决方案,看看哪个对你最有利。然而,时间和计算能力都是有限资源,你必须对选择的模型有战略性的考虑。

谈到机器学习算法时,许多人会想到经典的机器学习算法与神经网络之间的对比。神经网络,尤其是深度学习,受到了大量的兴趣和媒体报道,这是可以理解的,因为过去十年中大部分人工智能的进展都是由于神经网络变得更大、更深。

这些兴趣和报道可能会给人一种深度学习正在取代经典机器学习算法的印象。然而,尽管深度学习在生产中发现了更多的用例,但经典机器学习算法并没有消失。许多推荐系统仍然依赖协同过滤和矩阵分解。基于树的算法,包括梯度提升树,仍然驱动着许多有严格延迟要求的分类任务。

即使在部署神经网络的应用中,经典的机器学习算法仍然在同时使用。例如,神经网络和决策树可能会在集成中一起使用。一个 k-means 聚类模型可能被用来提取特征,以输入到神经网络中。反之亦然,一个预训练的神经网络(如 BERT 或 GPT-3)可能被用来生成嵌入,以输入到逻辑回归模型中。

在为你的问题选择模型时,你并不是从所有可能的模型中选择,而是通常集中在一组适合你问题的模型上。例如,如果你的老板告诉你要建立一个检测有毒推文的系统,你知道这是一个文本分类问题——给定一段文本,分类它是否有毒——常见的文本分类模型包括朴素贝叶斯、逻辑回归、循环神经网络以及基于变压器的模型如 BERT、GPT 及其变种。

如果你的客户希望你建立一个检测欺诈交易的系统,你知道这是经典的异常检测问题——欺诈交易是你想要检测的异常——这个问题的常见算法有许多,包括k-最近邻算法、孤立森林、聚类和神经网络。

对常见的机器学习任务及其解决方法的了解在这个过程中是至关重要的。

不同类型的算法需要不同数量的标签以及不同数量的计算资源。有些训练时间更长,而有些则更长时间做出预测。非神经网络算法往往更易于解释(例如,哪些特征对将电子邮件分类为垃圾邮件做出了最大贡献)比神经网络更易于解释。

在考虑使用哪种模型时,重要的是不仅要考虑模型的性能,例如准确度、F1 分数和对数损失,还要考虑其它属性,比如需要训练的数据量、计算资源和时间、推断延迟以及可解释性。例如,一个简单的逻辑回归模型可能比复杂的神经网络精度要低,但它需要更少的标注数据开始训练,训练速度更快,部署起来更容易,并且解释其为何做出特定预测也更容易。

比较机器学习算法不在本书的范围之内。无论比较多么出色,一旦新算法出现,它就会过时。回到 2016 年,LSTM-RNN 曾风靡一时,是支持许多 NLP 任务的 seq2seq(序列到序列)架构的骨干,从机器翻译到文本摘要到文本分类。然而,仅仅两年后,循环架构在 NLP 任务中大部分被变压器架构所取代。

要理解不同的算法,最好的方法是装备自己基础的机器学习知识,并运行你感兴趣的算法的实验。为了跟上如此多的新机器学习技术和模型,我发现监视像 NeurIPS、ICLR 和 ICML 这样的主要机器学习会议的趋势,以及在 Twitter 上关注那些工作具有高信噪比的研究人员是有帮助的。

模型选择的六个建议

不涉及具体的不同算法,这里是六个建议,可能帮助你决定下一步要研究的机器学习算法。

避免陷入最新技术的陷阱

在帮助公司和最近毕业生入门机器学习时,我通常要花不少时间引导他们不要直接跳进最新的模型中。我可以理解为什么人们想要最新的模型。许多人认为这些模型会是解决问题的最佳方案——如果你相信存在更新且更优越的解决方案,为什么要尝试一个旧的解决方案呢?许多业务领导也想使用最新的模型,因为他们希望使他们的业务显得前沿。开发者可能更喜欢尝试新模型,而不是一遍又一遍地陷入相同的老问题中。

研究人员通常只在学术环境中评估模型,这意味着一个模型被视为最先进通常意味着它在某些静态数据集上表现优于现有模型。这并不意味着这个模型将足够快或便宜以便实施。甚至这也不意味着这个模型将在的数据上比其他模型表现更好。

尽管保持与新技术的同步是必要的,并且评估其对你的业务的益处是有益的,但解决问题时最重要的是找到能解决问题的解决方案。如果有一个比最先进模型更便宜和更简单的解决方案能够解决你的问题,那么使用简单的解决方案是最重要的。

从最简单的模型开始

Python 之禅指出,“简单胜于复杂”,这个原则在机器学习中同样适用。简单性有三个目的。首先,简单模型更容易部署,早期部署你的模型可以验证你的预测管道与训练管道一致。其次,从简单开始,逐步添加更复杂的组件使得理解和调试你的模型更容易。第三,最简单的模型作为基准,可以用来比较你更复杂的模型。

最简单的模型并不总是与最小努力的模型相同。例如,预训练 BERT 模型很复杂,但是如果你使用像 Hugging Face 的 Transformer 中那样的现成实现,那么开始使用它们几乎没有什么难度。在这种情况下,使用复杂的解决方案并不是一个坏主意,因为这个解决方案周围的社区已经发展得足够好,可以帮助你解决可能遇到的任何问题。然而,你可能仍然希望尝试更简单的解决方案,以确保预训练 BERT 确实比这些更简单的解决方案更适合你的问题。预训练 BERT 可能开始起步较轻松,但是改进起来可能需要相当大的努力。而如果你从一个更简单的模型开始,你将有很多空间来改进你的模型。

避免在选择模型时存在人类偏见

想象一下,你团队中的一名工程师被分配了评估哪个模型更适合你的问题的任务:梯度提升树还是预训练 BERT 模型。两周后,这位工程师宣布最好的 BERT 模型比最好的梯度提升树表现提升了 5%。你的团队决定选择预训练 BERT 模型。

几个月后,然而,一位经验丰富的工程师加入了你的团队。她决定重新研究梯度提升树,发现这一次,最优的梯度提升树优于你当前生产中的预训练 BERT 模型。发生了什么事情?

在评估模型时存在许多人为偏见。评估 ML 架构的过程之一是尝试不同的特征和不同的超参数组合,以找到该架构的最佳模型。如果工程师对某个架构更感兴趣,他们可能会花更多时间进行实验,这可能会导致该架构的模型表现更好。

在比较不同架构时,重要的是在可比较的设置下进行比较。如果你为一个架构运行了 100 次实验,那么仅仅为正在评估的另一个架构运行几次实验是不公平的。你可能也需要为另一个架构运行 100 次实验。

因为模型架构的性能在其评估的上下文中高度依赖于各种因素,例如任务、训练数据、测试数据、超参数等,所以很难断言一个模型架构比另一个更好。在某个特定上下文中可能是真的,但在所有可能的上下文中不太可能是真的。

现在评估好的性能与以后评估好的性能对比

现在最好的模型并不总是意味着两个月后最好的模型。例如,基于树的模型现在可能效果更好,因为你还没有大量数据,但两个月后,你可能能够增加两倍的训练数据,你的神经网络可能会表现得更好。¹

估计你的模型在有更多数据时性能如何变化的一种简单方法是使用学习曲线。模型的学习曲线是其性能的绘图,例如训练损失、训练准确率、验证准确率,以及它使用的训练样本数量,如图 6-1 所示。学习曲线不能帮助你准确估计通过增加训练数据可以获得多少性能提升,但它可以让你知道是否可以期待从更多训练数据中获得任何性能提升。

图 6-1. 朴素贝叶斯模型和 SVM 模型的学习曲线。来源:scikit-learn

我遇到过的一种情况是,当一个团队评估一个简单的神经网络和一个协同过滤模型来进行推荐时。在离线评估两个模型时,协同过滤模型表现更好。然而,简单的神经网络可以随着每个新样本的到来进行更新,而协同过滤必须查看所有数据来更新其基础矩阵。团队决定部署协同过滤模型和简单神经网络。他们使用协同过滤模型为用户做出预测,并持续在生产环境中用新的输入数据训练简单的神经网络。两周后,简单神经网络能够超越协同过滤模型。

在评估模型时,你可能希望考虑它们在不久的将来改进的潜力,以及实现这些改进的难易程度。

评估权衡

在选择模型时,你必须做出许多权衡。了解在你的机器学习系统性能中什么更重要将帮助你选择最合适的模型。

一个经典的权衡例子是假阳性和假阴性之间的权衡。减少假阳性的数量可能会增加假阴性的数量,反之亦然。在假阳性比假阴性更为危险的任务中,例如指纹解锁(未经授权的人不应被分类为已授权并获得访问权限),你可能更喜欢一个能减少假阳性的模型。同样,在假阴性比假阳性更为危险的任务中,例如 COVID-19 筛查(COVID-19 患者不应被错误地分类为非 COVID-19),你可能更喜欢一个能减少假阴性的模型。

另一个权衡的例子是计算需求和准确性——一个更复杂的模型可能会提供更高的准确性,但可能需要一个更强大的机器,例如 GPU 而非 CPU,以在可接受的推断延迟下生成预测。许多人也关心可解释性和性能之间的权衡。一个更复杂的模型可以提供更好的性能,但其结果的解释性较差。

了解你的模型假设

统计学家乔治·博克斯在 1976 年曾说过:“所有模型都是错误的,但有些是有用的。” 现实世界异常复杂,模型只能基于假设进行近似。每个模型都有其自己的假设。了解一个模型做出了哪些假设,以及我们的数据是否满足这些假设,有助于评估哪种模型最适合你的使用场景。

以下是一些常见的假设。这不是详尽无遗的列表,而只是一个演示:

预测假设

每个旨在从输入X预测输出Y的模型都假设,基于X可以预测Y是可能的。

独立同分布(IID)

神经网络假设示例是独立同分布的,这意味着所有示例都是从相同的联合分布独立抽取的。

平滑性

每个监督学习方法假设存在一组函数,可以将输入转换为输出,使得相似的输入被转换为相似的输出。如果一个输入X产生输出Y,那么接近X的输入会产生与Y成比例的输出。

可计算性

X为输入,ZX的潜在表示。每个生成模型都假设可以计算概率P(Z|X)。

边界

线性分类器假设决策边界是线性的。

条件独立

朴素贝叶斯分类器假设给定类别时属性值是相互独立的。

正态分布

许多统计方法假设数据服从正态分布。

集成方法

在考虑解决您的问题的机器学习解决方案时,您可能希望从包含一个模型的系统开始(在本章早些时候讨论了选择解决方案的过程)。开发出一个单一模型后,您可以考虑如何进一步提高其性能。一种始终有效的方法是使用多个模型的集成来进行预测,而不仅仅是一个模型。集成中的每个模型称为基学习器。例如,对于预测电子邮件是否为垃圾邮件的任务,您可能有三种不同的模型。每封电子邮件的最终预测是所有三个模型的多数投票。因此,如果至少两个基学习器输出垃圾邮件,则该邮件将被分类为垃圾邮件。

截至 2021 年 8 月,Kaggle 比赛中的 22 个获胜解决方案中,有 20 个使用了集成方法。² 截至 2022 年 1 月,SQuAD 2.0,斯坦福问答数据集的前 20 个解决方案都是集成方法,如图 6-2 所示。

集成方法在生产环境中不太受青睐,因为集成模型部署复杂且难以维护。然而,在某些任务中,集成模型仍然很常见,因为稍微提高的性能可能带来巨大的财务收益,比如预测广告的点击率。

图 6-2。截至 2022 年 1 月,SQuAD 2.0的前 20 个解决方案全部是集成方法。

我们将通过一个例子来说明集成方法为何有效。想象一下,您有三个电子邮件垃圾分类器,每个分类器的准确率为 70%。假设每个分类器对每封电子邮件的正确预测具有相等的概率,并且这三个分类器之间没有相关性,我们将展示通过这三个分类器的多数投票,我们可以获得 78.4%的准确率。

每封电子邮件,每个分类器都有 70%的正确概率。如果至少有两个分类器是正确的,那么集成将是正确的。Table 6-1 展示了给定电子邮件的集成不同可能结果的概率。这个集成将有 0.343 + 0.441 = 0.784,即 78.4%的准确率。

表 6-1. 从三个分类器中采用多数投票法的集成可能结果

三个模型的输出 概率 集成输出
三个都正确 0.7 * 0.7 * 0.7 = 0.343 正确
仅两个正确 (0.7 * 0.7 * 0.3) * 3 = 0.441 正确
仅一个正确 (0.3 * 0.3 * 0.7) * 3 = 0.189 错误
无一个正确 0.3 * 0.3 * 0.3 = 0.027 错误

只有在集成中的分类器不相关时,此计算才有效。如果所有分类器完全相关,即它们每个都对每封电子邮件做出相同预测,那么集成的准确性将与每个单独分类器的准确性相同。创建集成时,基础学习器之间的相关性越低,集成效果越好。因此,通常选择非常不同类型的模型进行集成。例如,可以创建一个由一个 Transformer 模型、一个循环神经网络和一个梯度提升树组成的集成。

创建集成有三种方法:bagging、boosting 和 stacking。根据几篇调查论文显示,除了帮助提升性能外,集成方法如 boosting 和 bagging,连同重采样,还有助于处理不平衡数据集。³ 我们将逐一介绍这三种方法,首先是 bagging。

Bagging

Bagging,缩写自bootstrap aggregating,旨在提高机器学习算法的训练稳定性和准确性。⁴ 它减少了方差并有助于避免过拟合。

给定数据集,与在整个数据集上训练一个分类器不同,您可以使用有放回抽样创建不同的数据集,称为 bootstrap,并在每个 bootstrap 上训练分类或回归模型。有放回抽样确保每个 bootstrap 都是独立创建的。Figure 6-3 展示了 bagging 的示意图。

图 6-3. Bagging 示意图。来源:根据Sirakorn的图像适配

如果问题是分类,最终预测由所有模型的多数投票决定。例如,如果有 10 个分类器投票 SPAM,6 个模型投票 NOT SPAM,最终预测是 SPAM。

如果问题是回归,最终预测是所有模型预测的平均值。

Bagging 通常可以改善不稳定方法,比如神经网络、分类和回归树,以及线性回归中的子集选择。然而,它可能会轻微降低稳定方法(比如k最近邻)的性能。⁵

随机森林是 Bagging 的一个例子。随机森林由 Bagging 和特征随机性构建的决策树集合组成,其中每棵树只能从一个随机特征子集中选择。

提升

提升是一族迭代集成算法,将弱学习器转化为强学习器。这个集成中的每个学习器都是在同一组样本上训练的,但是在迭代中样本的权重不同。因此,未来的弱学习器更加关注之前弱学习器误分类的例子。图 6-4 显示了提升的示意图,展示了随后的步骤。

图 6-4. 提升示例。来源:根据Sirakorn的图片改编。
  1. 首先,在原始数据集上训练第一个弱分类器。

  2. 根据第一个分类器分类效果对样本进行了重新加权,例如,误分类的样本被赋予更高的权重。

  3. 在这个重新加权的数据集上训练第二个分类器。你的集成现在包括第一个和第二个分类器。

  4. 样本的权重基于集成对其进行分类的效果来确定。

  5. 在这个重新加权的数据集上训练第三个分类器。将第三个分类器添加到集成中。

  6. 如有需要,重复多次迭代。

  7. 形成最终的强分类器,作为现有分类器的加权组合 —— 训练误差较小的分类器具有较高的权重。

提升算法的一个例子是梯度提升机(GBM),它通常通过弱决策树生成预测模型。它像其他提升方法一样,以逐阶段的方式构建模型,并通过允许优化任意可微损失函数来泛化它们。

XGBoost,GBM 的一种变体,曾是许多机器学习比赛中获胜团队首选的算法。⁶ 它被广泛应用于从分类、排名到发现希格斯玻色子等多种任务。⁷ 然而,许多团队开始选择LightGBM,这是一个分布式梯度提升框架,支持并行学习,通常能更快地处理大规模数据集的训练。

堆叠

堆叠意味着您从训练数据中训练基本学习器,然后创建一个元学习器,该元学习器将基本学习器的输出组合起来生成最终预测结果,如图 6-5 所示。元学习器可以是一个简单的启发式方法:对于分类任务,您可以采用多数投票,对于回归任务,您可以采用平均投票。也可以是另一个模型,例如逻辑回归模型或线性回归模型。

图 6-5. 三个基本学习器的堆叠集成可视化

关于如何创建集成模型的更多出色建议,请参考 Kaggle 传奇团队 MLWave 的精彩集成指南

实验追踪和版本控制

在模型开发过程中,通常需要尝试许多不同的架构和模型,以选择最适合您问题的模型。有些模型可能看起来相似,只有一个超参数不同,比如一个模型使用学习率为 0.003,另一个模型使用学习率为 0.002,但它们的性能可能截然不同。重要的是要记录所有需要重新创建实验及其相关文档的定义。文档是在实验过程中生成的文件,例如显示损失曲线、评估损失图、日志或模型在训练过程中的中间结果的文件。这使您能够比较不同的实验,并选择最适合您需求的实验。比较不同的实验还可以帮助您理解如何通过小的变化影响模型的性能,进而更好地了解您的模型如何工作。

跟踪实验进展和结果的过程称为实验追踪。为了可能在以后重新创建实验或与其他实验比较而记录实验的所有详细信息的过程称为版本控制。这两者是密切相关的。许多最初旨在成为实验追踪工具的工具,如 MLflow 和 Weights & Biases,现在已经发展为包含版本控制功能。许多最初旨在成为版本控制工具的工具,如DVC,现在也已经包含了实验追踪功能。

实验追踪

训练机器学习模型的重要部分是监控学习过程。在训练过程中可能会出现许多问题,包括损失不降低、过拟合、欠拟合、权重值波动、死神经元和内存耗尽等。重要的是要跟踪训练过程中发生的情况,不仅用于检测和解决这些问题,还用于评估模型是否学到了有用的东西。

当我刚开始接触机器学习时,所有人都告诉我要追踪的只有损失和速度。几年后,人们追踪的项目变得如此之多,以至于他们的实验追踪面板看起来既美观又可怕。以下是您在每个实验的训练过程中可能想要考虑追踪的事项的简短列表:

  • 对应于训练集和每个评估集的损失曲线

  • 您关心的模型性能指标,在所有非测试集上,例如准确率、F1 值、困惑度。

  • 相应样本、预测和实际标签的日志。这对于临时分析和检查数据的一致性非常有用。

  • 您的模型速度,通过每秒步骤数或者如果您的数据是文本,则是每秒处理的标记数来评估。

  • 系统性能指标,如内存使用情况和 CPU/GPU 利用率。它们对于识别瓶颈并避免浪费系统资源非常重要。

  • 随时间变化的任何参数和超参数的值,这些变化可能会影响您模型的性能,例如如果使用学习率调度,则学习率;梯度范数(全局和每层),特别是如果剪裁梯度范数;以及权重范数,特别是如果进行权重衰减。

理论上,跟踪所有可能的事项并不是一个坏主意。大多数情况下,您可能不需要查看大多数追踪项。但是当发生问题时,其中一个或多个可能会为您提供理解和/或调试模型的线索。总体上,追踪使您能够观察模型状态。⁸ 然而,在实践中,由于当前工具的限制,跟踪太多事项可能会让人不知所措,而追踪不重要的事项可能会让您分心,而忽视真正重要的事项。

实验追踪使得可以跨实验进行比较。通过观察某个组件变化如何影响模型性能,您可以理解该组件的功能。

跟踪实验的简单方法是自动复制所有实验所需的代码文件,并记录所有输出及其时间戳。⁹ 然而,使用第三方实验追踪工具可以为您提供漂亮的仪表板,并允许您与同事共享您的实验。

版本控制

想象一下这种情况。你和你的团队在过去几周里不断调整你们的模型,最后一次运行显示出了有希望的结果。你想要用它进行更广泛的测试,所以你试图使用你某处记录下来的超参数集来复制它,但却发现结果并不完全相同。你记得在那次运行和下一次之间做了一些代码更改,所以你尽力凭记忆撤销那些更改,因为你那时的冲动自己认为这些变化太微小,不值得提交。但你仍然无法复制出有希望的结果,因为有太多可能的方式可以进行更改。

如果您对机器学习实验进行了版本控制,这个问题本可以避免。机器学习系统既是代码,也是数据,因此您不仅需要对代码进行版本控制,还需要对数据进行版本控制。代码版本控制已经成为行业标准。然而,目前来说,数据版本控制就像使用牙线一样。每个人都认为这是一个好习惯,但很少有人这样做。

数据版本控制面临几个挑战。其中一个原因是,因为数据通常比代码要大得多,所以我们不能使用通常用于版本控制代码的同样策略来版本控制数据。

例如,代码版本控制通过跟踪对代码库的所有更改来进行。一个更改被称为 diff,简称差异。每个更改都通过逐行比较来衡量。代码行通常很短,逐行比较是有意义的。然而,你的数据行,特别是如果它以二进制格式存储,可能长度无限。说这一行有 100 万个字符不同于另一行 100 万个字符,这并没有多大帮助。

代码版本控制工具允许用户通过保留所有旧文件的副本来恢复到先前的代码库版本。然而,使用的数据集可能非常大,多次复制可能是不可行的。

代码版本控制工具允许多人同时在同一份代码库上工作,每个人在本地机器上复制代码库。然而,数据集可能无法适应本地机器。

第二个问题是,关于数据版本化到底包含什么内容仍然存在混淆。当我们对数据版本进行处理时,diff 是否意味着在数据仓库中的任何文件内容发生了更改,只有在添加或删除文件时,还是只有整个仓库的校验和发生了更改时?

截至 2021 年,像 DVC 这样的数据版本工具只有在整个目录的校验和发生了更改,或者文件被添加或删除时才会注册 diff。

另一个困惑是如何解决合并冲突:如果开发者 1 使用数据版本 X 来训练模型 A,而开发者 2 使用数据版本 Y 来训练模型 B,将数据版本 X 和 Y 合并成 Z 是没有意义的,因为没有与 Z 对应的模型。

此外,如果您使用用户数据来训练模型,像《通用数据保护条例》(GDPR)这样的法规可能会使版本控制变得复杂化。例如,法规可能要求您在被请求时删除用户数据,这使得恢复旧版本数据在法律上变得不可能。

激进的实验跟踪和版本控制有助于实现可复现性,但并不保证完全可复现。您使用的框架和硬件可能会给您的实验结果引入不确定性¹⁰,导致在不了解实验运行环境的情况下无法复制实验结果。

目前我们必须运行如此多的实验来找到最佳模型的原因是我们把机器学习视为一个黑盒子。因为我们无法预测哪种配置会最有效,所以我们必须尝试多种配置。然而,我希望随着领域的发展,我们能够更深入地理解不同的模型,并能够推理出哪种模型将最有效,而不是运行数百或数千次实验。

分布式训练

随着模型变得更大、更加资源密集,公司对规模化训练越来越重视¹¹。在可扩展性方面的专业知识难以获取,因为它需要定期接触大量计算资源。可扩展性是一个值得一系列书籍探讨的主题。本节旨在强调大规模机器学习的挑战,并提供一个支架,帮助您合理规划项目资源。

训练模型时使用超出内存容量的数据是很常见的。特别是处理医疗数据(如 CT 扫描或基因组序列)时尤其常见。如果您在团队中为大型语言模型(如 OpenAI、Google、NVIDIA、Cohere)工作,这也可能发生在文本数据中。

当您的数据无法全部加载到内存中时,预处理算法(如零中心化、归一化、白化)、数据打乱和批处理将需要在核心外和并行运行¹²。当数据样本较大时,例如一台机器一次只能处理少量样本,您可能只能使用较小的批量大小,这会导致基于梯度下降的优化不稳定。

在某些情况下,数据样本非常大,甚至无法完全载入内存,这时你需要使用类似梯度检查点的技术,这种技术利用内存占用和计算效率之间的平衡来使系统在更少的内存中执行更多的计算。根据开源软件包梯度检查点的作者说:“对于前向传播模型,我们能够将比之前大 10 倍以上的模型装载到我们的 GPU 上,只需增加 20%的计算时间。”¹³ 即使一个样本能够装入内存,使用检查点技术也可以让你将更多的样本放入一个批次中,这可能使你能够更快地训练你的模型。

数据并行化

现在,使用多台机器来训练机器学习模型已经成为常态。现代机器学习框架支持的最常见并行化方法是数据并行:将数据分割在多台机器上,训练所有机器上的模型,并累积梯度。这导致了一些问题的出现。

一个具有挑战性的问题是如何准确有效地累积来自不同机器的梯度。由于每台机器产生自己的梯度,如果你的模型等待它们全部完成一次运行——同步随机梯度下降(SSGD),落后者会导致整个系统减速,浪费时间和资源。¹⁴ 落后者问题随着机器数量的增加而增长,因为工作机器越多,在给定迭代中至少有一台机器运行异常缓慢的可能性就越大。然而,已经有许多算法有效地解决了这个问题。¹⁵

如果你的模型使用每台机器单独计算的梯度来更新权重——异步随机梯度下降(ASGD),梯度陈旧可能会成为一个问题,因为一台机器的梯度导致权重在另一台机器的梯度到来之前已经改变。¹⁶

同步 SGD 和异步 SGD 的区别如图 6-6 所示。

图 6-6. 同步 SGD 和异步 SGD 在数据并行化中的比较。来源:Jim Dowling 的图片改编¹⁷

理论上,异步 SGD 可以收敛,但需要比同步 SGD 更多的步骤。然而,在实践中,当权重数量很大时,梯度更新往往是稀疏的,这意味着大多数梯度更新只修改参数的小部分,不太可能两台不同机器的梯度更新同时修改同一组权重。当梯度更新稀疏时,梯度陈旧问题就不再是一个大问题,模型在同步和异步 SGD 下的收敛情况相似。¹⁸

另一个问题是将模型分布在多台机器上可能会导致批量大小非常大。如果一台机器处理 1,000 的批次大小,那么 1,000 台机器将处理 1M(例如,OpenAI 的 GPT-3 在 2020 年使用的批量大小为 3.2M)¹⁹。简化计算,如果在一台机器上训练一个 epoch 需要 1M 步,那么在 1,000 台机器上训练可能只需要 1,000 步。一个直观的方法是增加学习率,以便在每一步骤中进行更多学习,但我们也不能把学习率设置得太大,因为这会导致不稳定的收敛。实际上,将批量大小增加到一定点后会产生收益递减的效果²⁰。

最后但并非最不重要的是,对于相同的模型设置,主要工作节点有时可能会使用比其他工作节点更多的资源。如果是这种情况,为了充分利用所有机器的资源,您需要找到一种方法来平衡它们之间的工作负载。最简单但不是最有效的方法是在主要工作节点上使用较小的批量大小,在其他工作节点上使用较大的批量大小。

模型并行

在数据并行中,每个工作节点都有整个模型的副本,并且执行其模型副本所需的所有计算。模型并行是指在不同的机器上训练模型的不同组件,如图 6-7 所示。例如,机器 0 处理前两层的计算,而机器 1 处理接下来的两层,或者某些机器处理前向传播,而其他机器处理反向传播。

图 6-7. 数据并行和模型并行。来源:改编自 Jure Leskovec 的一幅图²¹

模型并行有时可能会误导,因为在某些情况下,并行不意味着在不同机器上执行模型的不同部分。例如,如果您的模型是一个庞大的矩阵,并且将该矩阵分成两半在两台机器上,那么这两半可能会并行执行。但是,如果您的模型是一个神经网络,并且将第一层放在机器 1 上,第二层放在机器 2 上,而第二层需要来自第一层的输出才能执行,则机器 2 必须等待机器 1 先完成。

管道并行 是一种巧妙的技术,可以使模型的不同组件在不同的机器上更多地并行运行。有多种变体,但其关键思想是将每台机器的计算分成多个部分。当机器 1 完成其计算的第一部分时,它将结果传递给机器 2,然后继续进行第二部分的计算,依此类推。现在,机器 2 可以在第一部分上执行其计算,而机器 1 则在第二部分上执行其计算。

为了更加具体化,假设你有四台不同的机器,第一、第二、第三和第四层分别在机器 1、2、3 和 4 上。使用管道并行,每个小批次被分成四个微批次。机器 1 在第一个微批次上计算第一层,然后机器 2 在机器 1 的结果上计算第二层,同时机器 1 在第二个微批次上计算第一层,依此类推。图 Figure 6-8 显示了在四台机器上使用管道并行的神经网络的情况。

Figure 6-8. 神经网络在四台机器上的管道并行性;每台机器同时运行前向传递(F)和反向传递(B)来处理神经网络的一个组件。来源:根据黄等人的图片改编²²

模型并行和数据并行并不是互斥的。许多公司同时使用这两种方法以更好地利用他们的硬件,尽管配置同时使用这两种方法可能需要大量的工程努力。

AutoML

有一个笑话说好的机器学习研究员是那些能够设计出足够智能的 AI 算法来自动化自己工作的人。这在 TensorFlow Dev Summit 2018 中变得有趣,当时 Jeff Dean 上台宣布谷歌打算用 100 倍的计算能力来替代机器学习专家,向社区引入了 AutoML,令人兴奋和震惊。与其支付一组 100 名机器学习研究员/工程师来玩弄各种模型,最终选择一个次优模型,为什么不用这笔钱投入计算资源来搜索最优模型呢?活动录像中的截图显示了这一事件 Figure 6-9。

Figure 6-9. Jeff Dean 在 TensorFlow Dev Summit 2018 上揭示谷歌的 AutoML

软 AutoML:超参数调整

AutoML 是指自动化寻找解决实际问题的机器学习算法的过程。在生产中,一种温和的形式,也是最流行的形式,是超参数调整。超参数是由用户提供的参数,其值用于控制学习过程,例如学习率、批量大小、隐藏层数量、隐藏单元数量、dropout 概率,Adam 优化器中的 β[1] 和 β[2] 等等。甚至量化——例如使用 32 位、16 位或 8 位来表示数字或这些表示的混合——也可以被视为需要调整的超参数²³。

对于不同的超参数集合,同一模型在同一数据集上的性能可能会大相径庭。Melis 等人在他们 2018 年的论文 “On the State of the Art of Evaluation in Neural Language Models” 中表明,经过良好调优的弱模型可能会胜过更强大、更花哨的模型。超参数调优的目标是在一个搜索空间内找到给定模型的最优超参数集合,每个集合的性能都在验证集上评估。

尽管许多人知道超参数调优的重要性,仍然有很多人忽视系统化的方法,而更倾向于凭感觉进行手动调整。其中最流行的可能是研究生下降法(GSD),这是一种技术,研究生会调整超参数直到模型正常工作。²⁴

然而,越来越多的人将超参数调优作为标准流程的一部分。流行的机器学习框架通常都带有内置工具或者有第三方工具来进行超参数调优,例如,scikit-learn 自带的 auto-sklearn²⁵,TensorFlow 的 Keras Tuner,以及 Ray 的 Tune。流行的超参数调优方法包括随机搜索²⁶,网格搜索,以及贝叶斯优化²⁷。《AutoML: Methods, Systems, Challenges》一书由弗莱堡大学的 AutoML 小组编写,将其 第一章(可以免费在线阅读)专注于超参数优化。

在调整超参数时,请记住模型的性能可能对某些超参数的更改更为敏感,因此对敏感超参数应更加小心地调整。

警告

绝对不要使用测试集来调整超参数。选择基于验证集性能的最佳超参数集合,然后报告模型在测试集上的最终性能。如果使用测试集来调整超参数,会导致模型对测试集过拟合。

Hard AutoML:架构搜索和学习优化器

一些团队将超参数调优推向了新的高度:如果我们将模型的其他组件或整个模型视为超参数呢?卷积层的大小或是否具有跳跃层都可以视为超参数。与其在卷积层之后手动放置池化层或在线性层后放置 ReLu(修正线性单元),不如给算法提供这些构建块,让算法自行决定如何组合它们。这一研究领域被称为架构搜索,或神经架构搜索(NAS)用于神经网络,因为它寻找最优的模型架构。

NAS 设置由三个组件组成:

搜索空间

定义可能的模型架构 —— 即可以选择的构建模块以及它们组合方式的约束条件。

性能估计策略

评估候选架构的性能,而无需对每个候选架构从头开始训练直至收敛。当我们有大量候选架构时,比如 1,000 个,训练所有候选架构直至收敛可能成本高昂。

搜索策略

探索搜索空间的方法。一种简单的方法是随机搜索 —— 从所有可能的配置中随机选择 —— 这在 NAS 中即使不受欢迎也是 prohibitively expensive。常见的方法包括强化学习(奖励改进性能估计的选择)和进化算法(对架构添加突变,选择表现最佳的,对它们再添加突变,依此类推)²⁸。

对于 NAS,搜索空间是离散的 —— 每一层/操作最终的架构仅使用可用选项中的一个²⁹,你需要提供构建模块的集合。常见的构建模块包括不同尺寸的各种卷积、线性操作、各种激活函数、池化、恒等映射、零操作等等。构建模块的集合根据基础架构而异,例如卷积神经网络或者变换器。

在典型的机器学习训练过程中,你有一个模型,然后有一个学习过程 —— 一种算法,帮助你的模型找到最小化给定数据集上给定目标函数的参数集。如今神经网络最常见的学习过程是梯度下降,它利用优化器指定如何根据梯度更新模型的权重³⁰。流行的优化器包括,如你可能已知,Adam、Momentum、SGD 等等。理论上,你可以将优化器作为 NAS 的构建模块并寻找最适合的优化器。实际上,这很难做到,因为优化器对其超参数的设置很敏感,并且默认的超参数通常在不同架构间表现不佳。

这导致了一个激动人心的研究方向:如果我们用神经网络替换指定更新规则的函数会怎样?模型权重的更新量可以通过这个神经网络计算出来。这种方法产生了学习优化器,而不是手工设计的优化器。

由于学习优化器是神经网络,它们需要训练。你可以在训练神经网络的同时在相同数据集上训练学习优化器,但这需要每次有任务时都训练一个优化器。

另一种方法是在一组现有任务上训练一个学习优化器——使用这些任务的聚合损失作为损失函数和现有设计的优化器作为学习规则——然后在每个新任务中使用它。例如,Metz 等人构建了一组数千个任务来训练学习优化器。他们的学习优化器能够推广到新数据集、领域以及新架构。³¹ 这种方法的美妙之处在于,学习优化器随后可以用来训练一个更好的学习优化器,即改进自身的算法。

无论是架构搜索还是元学习学习规则,前期培训成本都足够昂贵,以至于全球只有少数几家公司能够承担这些成本。然而,对于有兴趣将 ML 应用于生产中的人们来说,了解 AutoML 的进展是很重要的,原因有两点。首先,产生的架构和学习优化器可以让 ML 算法即插即用地应用于多个实际任务,节省生产中的时间和成本,包括培训和推断。例如,由 Google 的 AutoML 团队生成的 EfficientNets 模型系列,在效率上超越了现有技术的精度,高达 10 倍。³² 其次,它们可能能够解决许多以前依靠现有架构和优化器无法解决的实际任务。

模型离线评估

我在帮助公司制定 ML 策略时经常遇到的一个常见但相当困难的问题是:“我怎么知道我们的 ML 模型是否有效?” 例如,某公司部署了 ML 来检测 100 架监视无人机的入侵,但他们无法衡量系统未能检测到多少次入侵,也无法确定哪种 ML 算法更适合他们的需求。

缺乏对如何评估您的 ML 系统进行清晰理解并不一定会导致您的 ML 项目失败,但可能会使您无法找到最适合您需求的最佳解决方案,并使说服管理层采纳 ML 变得更加困难。您可能需要与业务团队合作,开发更符合公司业务的模型评估指标。³⁷

理想情况下,开发和生产中的评估方法应该是一致的。但在许多情况下,这种理想是不可能的,因为在开发过程中,您有地面实况标签,但在生产中则没有。

对于某些任务,可以根据用户反馈推断或近似标签,如在“自然标签”一节中所述。例如,对于推荐任务,可以通过用户是否点击来推断推荐是否有效。但是,这涉及许多偏见。

对于其他任务,您可能无法直接评估您模型在生产中的表现,并可能需要依赖广泛的监控来检测 ML 系统性能的变化和失败。我们将在第八章中讨论监控。

一旦您的模型部署,您将需要继续监控和测试您模型在生产中的表现。在本节中,我们将讨论在部署之前评估模型性能的方法。我们将从评估我们模型的基线开始。然后我们将介绍一些常见的方法来评估您模型的整体准确性之外的性能。

基线

曾经有人告诉我,她的新生成模型在 ImageNet 上达到了 10.3 的 FID 分数。³⁸ 我不知道这个数字意味着什么,或者她的模型对我的问题是否有用。

另一次,我帮助一家公司实现了一个分类模型,其中正类在 90%的时间内出现。团队中的一个 ML 工程师兴奋地告诉我,他们的初始模型实现了 0.90 的 F1 分数。我问他这与随机相比如何。他毫无头绪。事实证明,因为对于他的任务,正类占标签的 90%,如果他的模型随机输出正类的概率为 90%,其 F1 分数也会约为 0.90。³⁹ 他的模型可能与随机预测无异。⁴⁰

评估指标本身意义不大。在评估您的模型时,了解您正在评估的基线至关重要。确切的基线应因用例而异,但以下五个基线可能对各种用例有所帮助:

随机基线

如果我们的模型只是随机预测,预期性能如何?预测是根据特定分布随机生成的,可以是均匀分布或任务的标签分布。

例如,考虑一个具有两个标签的任务,其中 NEGATIVE 出现的时间为 90%,而 POSITIVE 出现的时间为 10%。表 6-2 显示了随机预测基准模型的 F1 和准确度分数。然而,作为一种练习,看看大多数人对这些值缺乏直觉,尝试在查看表格之前在头脑中计算这些原始数字。

表 6-2. 预测随机的基准模型的 F1 和准确度分数

随机分布 含义 F1 准确度
均匀随机 预测每个标签的概率相等(50%) 0.167 0.5
任务标签分布 90% 的时间预测为 NEGATIVE,10% 的时间预测为 POSITIVE 0.1 0.82

简单启发式

忘记机器学习。如果你只是基于简单的启发式进行预测,你会期望什么样的表现?例如,如果你想建立一个排名系统,以排列用户新闻提要中的项目,目标是让用户在新闻提要上花更多时间,如果你只是按时间顺序逆序排列所有项目,先显示最新的项目,用户会花费多少时间?

零规则基线

零规则基线是简单启发式基线的特殊情况,当你的基准模型总是预测最常见的类时。

例如,对于推荐用户下一个手机应用的任务,最简单的模型可能是推荐他们最常用的应用。如果这个简单的启发式能够准确预测下一个应用达到 70%的时间,任何你建立的模型必须显著优于它,以证明增加的复杂性是合理的。

人类基线

在许多情况下,机器学习的目标是自动化人类可能会完成的任务,因此了解你的模型与人类专家相比的表现是非常有用的。例如,如果你在一个自动驾驶系统上工作,比较你的系统与人类驾驶员的进展至关重要,因为否则你可能永远无法说服用户信任这个系统。即使你的系统并不意味着取代人类专家,只是帮助他们提高生产力,了解在什么情况下这个系统对人类有用仍然很重要。

现有解决方案

在许多情况下,机器学习系统被设计来取代现有的解决方案,这可能是带有大量 if/else 语句或第三方解决方案的业务逻辑。将你的新模型与这些现有解决方案进行比较至关重要。你的机器学习模型并不总是比现有解决方案更好才能有用。如果一个模型的表现稍逊色,但使用起来更简单或更便宜,它仍然可以是有用的。

在评估模型时,区分“好系统”和“有用系统”非常重要。一个好系统未必有用,一个坏系统未必无用。如果一个自动驾驶车辆较之前的系统有显著提升,那么它可能是好的,但如果它的表现不如人类驾驶员,那么它可能就不是有用的。在某些情况下,即使一个机器学习系统比普通人驾驶员表现更好,人们仍然可能不信任它,这使得它不具有用处。另一方面,如果一个系统预测用户在手机上接下来会输入的单词比母语者差得多,那么它可能被认为是坏的。然而,如果它的预测有助于用户更快地输入某些时间,它可能仍然是有用的。

评估方法

在学术设置中,评估机器学习模型时,人们往往会专注于性能指标。然而,在实际生产中,我们还希望我们的模型具有稳健性、公平性、校准性,并且整体上是合理的。我们将介绍一些评估方法来帮助衡量模型的这些特性。

扰动测试

我的一群学生想要开发一个通过咳嗽声预测某人是否患有 COVID-19 的应用程序。他们的最佳模型在训练数据上表现出色,这些数据由医院收集的两秒钟长的咳嗽片段组成。然而,当他们将其部署到实际用户时,该模型的预测结果接近随机。

其中一个原因是,实际用户的咳嗽声与在医院收集的咳嗽声相比含有大量噪音。用户的录音可能包含背景音乐或附近的喋喋不休。他们使用的麦克风质量参差不齐。他们可能会在启用录音后立即开始记录他们的咳嗽声,或者等待几分之一秒后开始。

理想情况下,用于开发模型的输入应该与生产环境中模型将要处理的输入相似,但在许多情况下这是不可能的。特别是在数据收集昂贵或困难,并且你能够访问的最佳数据仍然与实际数据差异很大的情况下。生产环境中模型需要处理的输入通常与开发中的输入相比更加嘈杂。⁴¹ 在训练数据上表现最好的模型不一定在嘈杂数据上表现最佳。

为了了解您的模型在嘈杂数据下的表现如何,您可以对测试集进行小的更改,看看这些更改如何影响模型的性能。例如,对于预测某人是否患有 COVID-19 的任务,您可以随机添加一些背景噪音或随机剪辑测试音频片段,以模拟用户录音的变化。您可能希望选择在扰动数据上表现最佳的模型,而不是在干净数据上表现最佳的模型。

您的模型对噪声越敏感,维护起来就越困难,因为如果您的用户行为略有变化,例如他们更换手机,您的模型性能可能会下降。这也使您的模型容易受到对抗性攻击。

不变性测试

伯克利大学的一项研究发现,在 2008 年至 2015 年间,因为其种族而有 130 万名信用良好的黑人和拉丁裔申请者的抵押贷款申请被拒绝。⁴² 当研究人员使用被拒绝申请的收入和信用评分,但删除了识别种族的特征时,这些申请被接受。

输入的某些变化不应导致输出的变化。在前述情况下,种族信息的变化不应影响抵押贷款的结果。同样,申请人姓名的变化不应影响其简历筛选结果,某人的性别也不应影响他们应该获得多少薪水。如果发生了这些情况,你的模型存在偏见,这可能导致其无法使用,无论其性能多么出色。

要避免这些偏见,一个解决方案是执行与伯克利研究人员发现偏见相同的过程:保持输入不变,但改变敏感信息,看看输出是否改变。更好的做法是,在训练模型时首先排除敏感信息不作为特征使用。⁴³

方向性期望测试

然而,输入的某些变化应导致输出的可预测的变化。例如,当开发一个预测房价的模型时,保持所有特征不变但增加土地面积不应导致预测价格下降,减少平方英尺不应导致价格上升。如果输出以相反预期的方向改变,你的模型可能没有学习到正确的内容,你需要在部署之前进一步调查它。

模型校准

模型校准是一个微妙但关键的概念。想象一下,有人预测某件事情发生的概率是 70%。这个预测意味着,在所有这个预测被做出的情况下,预测的结果与实际结果一致的次数为 70%。如果一个模型预测 A 队以 70%的概率击败 B 队,而在这两支队伍共同比赛的 1,000 次中,A 队只赢了 60%的时间,那么我们称这个模型没有校准。一个校准的模型应该预测 A 队以 60%的概率获胜。

模型校准经常被机器学习从业者忽视,但它是任何预测系统的最重要属性之一。引用内特·西尔弗在他的书《信号与噪声》中的话,校准是“一个预测的最重要的测试之一——我会认为这是最重要的一个测试。”

我们将通过两个示例来展示为什么模型校准很重要。首先,考虑建立推荐系统来推荐用户接下来可能观看的电影的任务。假设用户 A80%的时间观看浪漫电影,20%的时间观看喜剧。如果你的推荐系统精确地展示 A 将最有可能观看的电影,那么推荐将仅包含浪漫电影,因为 A 更有可能观看浪漫电影而不是其他类型的电影。你可能希望一个更加校准的系统,其推荐是用户实际观看习惯的代表。在这种情况下,推荐应包含 80%的浪漫电影和 20%的喜剧电影。⁴⁴

其次,考虑建立一个模型来预测用户点击广告的可能性。为了简单起见,假设只有两个广告,广告 A 和广告 B。你的模型预测这个用户点击广告 A 的概率是 10%,点击广告 B 的概率是 8%。你并不需要你的模型来校准以便将广告 A 排在广告 B 之上。然而,如果你想预测广告将会获得多少点击次数,你就需要你的模型进行校准。如果你的模型预测一个用户点击广告 A 的概率是 10%,但实际上广告只被点击了 5%的时间,你估计的点击次数就会大大偏差。如果你有另一个给出相同排名但更好校准的模型,你可能要考虑更好校准的那个模型。

要衡量模型的校准性,一个简单的方法是计数:你计算模型输出概率 X 的次数和该预测实际发生的频率 Y,并将 XY 进行绘图。一个完全校准的模型的图表将在所有数据点上都有 X 等于 Y。在 scikit-learn 中,你可以使用方法 sklearn.calibration.calibration_curve 绘制二元分类器的校准曲线,如图 6-11 所示。

图 6-11. 不同模型在一个玩具任务上的校准曲线。逻辑回归模型是最佳校准模型,因为它直接优化逻辑损失。来源:scikit-learn

要对你的模型进行校准,一个常见的方法是Platt 缩放,在 scikit-learn 中实现为 sklearn.calibration.CalibratedClassifierCV。另一个由 Geoff Pleiss 优秀的开源实现可以在GitHub找到。对于想要了解模型校准重要性以及如何校准神经网络的读者,Lee Richardson 和 Taylor Pospisil 在 Google 的工作基础上撰写了一篇优秀的博客文章

置信度测量

置信度测量可以被视为考虑每个单独预测的有用性阈值的一种方式。无差别地向用户展示模型不确定的所有预测,甚至是模型不确定的预测,充其量会导致用户感到恼怒,并且失去对系统的信任,例如您智能手表上的活动检测系统认为您正在跑步,尽管您只是走得稍快。最坏的情况下,这可能会导致灾难性后果,例如预测性警务算法将一个无辜的人标记为潜在犯罪分子。

如果你只想展示你的模型确信的预测,那么如何衡量这种确信度?在哪个确信度阈值下应该展示预测?你希望对低于该阈值的预测采取什么措施——丢弃它们、让人类介入,还是向用户索取更多信息?

虽然大多数其他指标衡量系统的平均性能,但置信度测量是针对每个单独样本的指标。系统级别的测量有助于了解总体性能,但样本级别的指标在你关心系统在每个样本上的表现时至关重要。

基于切片的评估

切片意味着将数据分成子集,并分别查看模型在每个子集上的表现。我在许多公司中看到的一个常见错误是,它们过于专注于像整体 F1 或整体准确率这样的粗粒度指标,而不够关注基于切片的指标。这可能会导致两个问题。

其中一个问题是,他们的模型在数据的不同切片上表现不同,而实际上应该表现相同。例如,他们的数据有两个子组,一个是多数,一个是少数,而多数子组占数据的 90%:

  • 模型 A 在多数子组上达到 98% 的准确率,但在少数子组上仅达到 80% 的准确率,这意味着其整体准确率为 96.2%。

  • 模型 B 在多数和少数上都达到了 95% 的准确率,这意味着它的整体准确率为 95%。

这两个模型在 表 6-3 中进行了比较。你会选择哪个模型?

表 6-3. 两个模型在多数和少数子组的表现

多数准确率 少数准确率 整体准确率
模型 A 98% 80% 96.2%
模型 B 95% 95% 95%

如果一家公司只关注整体指标,他们可能会选择模型 A。他们可能对这个模型的高准确率非常满意,直到有一天,他们的最终用户发现,这个模型对少数子组存在偏见,因为少数子组恰好对应一个代表性不足的人口群体。⁴⁵ 过度关注整体性能不仅可能导致潜在的公众反弹,还会使公司对巨大的潜在模型改进视而不见。如果公司看到两个模型的基于切片的表现,他们可能会采取不同的策略。例如,他们可能决定提高模型 A 在少数子组上的表现,从而提高该模型的整体表现。或者他们可能保持两个模型不变,但现在有更多信息来做出更明智的部署决策。

另一个问题是,他们的模型在数据的不同切片上表现相同,而实际上应该表现不同。一些数据子集更为关键。例如,当你为用户流失预测建模(预测用户何时取消订阅或服务)时,付费用户比非付费用户更为关键。过于关注模型的整体表现可能会损害其在这些关键切片上的表现。

切片评估至关重要的一个引人入胜且看似违反直觉的原因是 辛普森悖论,这是一种现象,多组数据表现出趋势,但在合并这些组时,这些趋势消失或反转。这意味着模型 B 在整体数据上可能比模型 A 表现更好,但在各个子组中,模型 A 却表现优于模型 B。考虑模型 A 和模型 B 在 A 组和 B 组上的表现,如 表 6-4 所示。模型 A 在 A 组和 B 组中都优于模型 B,但合并后,模型 B 却优于模型 A。

Table 6-4. 辛普森悖论的一个例子^(a)

A 组 B 组 总体
模型 A 93% (81/87) 73% (192/263) 78% (273/350)
模型 B 87% (234/270) 69% (55/80) 83% (289/350)
^(a) 1986 年查里格等人在肾结石治疗研究中的数据:C. R. Charig, D. R. Webb, S. R. Payne, and J. E. Wickham, “Comparison of Treatment of Renal Calculi by Open Surgery, Percutaneous Nephrolithotomy, and Extracorporeal Shockwave Lithotripsy,” British Medical Journal(临床研究版)292, no. 6524(1986 年 3 月):879–82,https://oreil.ly/X8oWr

辛普森悖论比你想象的更为常见。1973 年,伯克利的研究数据显示,男性的录取率远高于女性,这引起了人们对女性是否受到偏见的怀疑。然而,进一步分析各个学科的数据后发现,在六个学科中,女性的录取率实际上比男性高,其中四个学科的情况如 表 6-5 所示⁴⁶。

Table 6-5. 1973 年伯克利研究生录取数据^(a)

全部 男性 女性
学科 申请人数 录取人数 申请人数
--- --- --- ---
A 933 64% 825
B 585 63% 560
C 918 35% 325
D 792 34% 417
E 584 25% 191
F 714 6% 373
总计 12,763 41% 8,442
^(a) Bickel 等人(1975 年)的数据

无论你是否真的会遇到这种悖论,这里的重点是聚合可能会掩盖和与实际情况相矛盾。为了能够做出关于选择何种模型的明智决策,我们需要考虑它在整体数据上的表现,也要考虑它在各个切片上的表现。基于切片的评估可以帮助你深入了解和改进模型的整体性能以及关键数据,还可以帮助检测潜在的偏见。它还可能帮助揭示非机器学习问题。有一次,我们团队发现我们的模型整体表现很好,但在移动用户的流量上表现非常糟糕。经过调查,我们意识到这是因为在小屏幕上(例如手机屏幕)一个按钮被部分隐藏了。

即使你认为切片并不重要,了解你的模型在更细粒度方式下的表现,可以让你对你的模型有信心,从而说服其他利益相关者,如你的老板或客户,来信任你的机器学习模型。

要跟踪你的模型在关键切片上的表现,你首先需要知道你的关键切片是什么。你可能想知道如何在你的数据中发现关键切片。不幸的是,切片仍然更像是一门艺术而不是一门科学,需要进行深入的数据探索和分析。以下是三种主要方法:

基于启发式的方法

利用你对数据和当前任务的领域知识对数据进行切片。例如,在处理网络流量时,你可能希望按照移动设备与桌面设备、浏览器类型和地理位置等维度对数据进行切片。移动用户的行为可能与桌面用户有很大不同。同样,不同地理位置的互联网用户可能对网站的期望也不同。⁴⁷

错误分析

手动查看被误分类的示例,并找出它们之间的模式。当我们发现大多数被误分类的示例来自移动用户时,我们发现了我们模型在移动用户上的问题。

切片发现器

已经有研究系统化了寻找切片的过程,包括 2019 年张等人的 “切片发现器:自动化数据切片用于模型验证” 和 Sumyea Helal 在 2016 年的 “子群发现算法:调查和实证评估” 中提到。这个过程通常从使用算法生成切片候选开始,如束搜索、聚类或决策,然后剔除明显不好的切片候选,并对剩下的候选进行排名。

请记住,一旦你发现了这些关键切片,你需要足够的、正确标记的数据来对每个切片进行评估。你的评估质量取决于你的评估数据的质量。

摘要

在本章中,我们涵盖了机器学习系统的机器学习算法部分,许多机器学习从业者认为这是机器学习项目生命周期中最有趣的部分。通过最初的模型,我们可以将我们在数据和特征工程上的辛勤工作变为现实(以预测的形式),并最终评估我们的假设(即,我们可以根据输入预测输出)。

我们首先讨论了如何选择最适合我们任务的机器学习模型。与其深入探讨每个单独模型架构的利弊——考虑到现有模型池越来越大,这种做法徒劳无功——本章概述了您需要考虑的方面,以便为您的目标、约束条件和需求做出明智的决策。

我们接着讨论了模型开发的不同方面。我们不仅涵盖了单个模型,还涉及了模型集成,这是比赛和排行榜风格研究中广泛使用的技术。

在模型开发阶段,您可能会尝试许多不同的模型。对您的许多实验进行密集跟踪和版本控制通常被认为是重要的,但许多机器学习工程师仍然会跳过这一步,因为这样做可能会感觉像在做苦差事。因此,拥有工具和适当的基础设施来自动化跟踪和版本控制过程至关重要。我们将在第十章中讨论用于机器学习生产的工具和基础设施。

由于现今模型越来越庞大且消耗数据量也越来越多,分布式训练成为机器学习模型开发人员必备的技能之一。我们讨论了并行技术,包括数据并行、模型并行和管道并行。让您的模型在大规模分布式系统上运行,比如那些运行具有数亿、甚至数十亿参数的模型,可能会面临挑战,需要专门的系统工程专业知识。

最后,我们讨论了如何评估您的模型以选择最佳部署模型。评估指标如果没有基准进行比较,意义不大,我们涵盖了在进一步评估您的模型进入生产环境之前进行理智检查的各种评估技术。

通常情况下,无论您的模型的离线评估有多好,您仍然无法确保您的模型在生产环境中的表现,直到该模型已经部署。在下一章中,我们将讨论如何部署一个模型。

¹ 安德鲁·吴在一场精彩的讲座中解释说,如果一个学习算法遭受高偏差,仅仅增加训练数据本身并不会有太大帮助。然而,如果学习算法遭受高方差,增加训练数据可能会有所帮助。

² 我查阅了 Farid Rashidi 的“Kaggle 解决方案”网页上列出的获胜解决方案。其中一种解决方案使用了 33 个模型(Giba,《第一名解决方案-Gilberto Titericz 和 Stanislav Semenov》,Kaggle,https://oreil.ly/z5od8)。

³ Mikel Galar, Alberto Fernandez, Edurne Barrenechea, Humberto Bustince 和 Francisco Herrera,《类不平衡问题的集成方法综述:Bagging、Boosting 和混合方法》,《IEEE Transactions on Systems, Man, and Cybernetics, Part C (Applications and Reviews)》42 卷 4 期(2012 年 7 月):463–84,https://oreil.ly/ZBlgE;G. Rekha, Amit Kumar Tyagi 和 V. Krishna Reddy,《使用 Bagging 和 Boosting 技术解决类不平衡问题,使用和不使用噪声过滤方法的对比》,《国际混合智能系统期刊》15 卷 2 期(2019 年 1 月):67–76,https://oreil.ly/hchzU

⁴ 这里的训练稳定性意味着训练损失波动较小。

⁵ Leo Breiman,《Bagging 预测器》,《机器学习》24(1996 年):123–40,https://oreil.ly/adzJu

⁶ “机器学习挑战获胜解决方案”,https://oreil.ly/YjS8d

⁷ Tianqi Chen 和 Tong He,《带增强树的希格斯玻色子发现》,《机器学习研究进展会议论文集》42(2015 年):69–80,https://oreil.ly/ysBYO

⁸ 我们将在第八章中讨论可观察性。

⁹ 我仍在等待一个与 Git 提交和 DVC 提交集成的实验跟踪工具。

¹⁰ 显著的例子包括 CUDA 中的原子操作,其中非确定性操作顺序导致不同运行之间的浮点舍入误差不同。

¹¹ 对于服务大量用户的产品,您还必须关注模型服务的可伸缩性,这超出了 ML 项目的范围,因此本书未涵盖此内容。

¹² 根据维基百科,“Out-of-core 算法是专为处理一次无法完全放入计算机主内存的数据而设计的”(参见“External memory algorithm”,https://oreil.ly/apv5m)。

¹³ Tim Salimans, Yaroslav Bulatov 和贡献者,《梯度检查点存储库》,2017 年,https://oreil.ly/GTUgC

¹⁴ Dipankar Das, Sasikanth Avancha, Dheevatsa Mudigere, Karthikeyan Vaidynathan, Srinivas Sridharan, Dhiraj Kalamkar, Bharat Kaul 和 Pradeep Dubey,《使用同步随机梯度下降的分布式深度学习》,arXiv,2016 年 2 月 22 日,https://oreil.ly/ma8Y6

¹⁵ Jianmin Chen, Xinghao Pan, Rajat Monga, Samy Bengio 和 Rafal Jozefowicz,《重温分布式同步 SGD》,ICLR 2017,https://oreil.ly/dzVZ5;Matei Zaharia, Andy Konwinski, Anthony D. Joseph, Randy Katz 和 Ion Stoica,《在异构环境中提升 MapReduce 性能》,第 8 届 USENIX 操作系统设计与实现研讨会,https://oreil.ly/FWswd;Aaron Harlap, Henggang Cui, Wei Dai, Jinliang Wei, Gregory R. Ganger, Phillip B. Gibbons, Garth A. Gibson 和 Eric P. Xing,《解决并行机器学习迭代收敛中的拉滞问题》(SoCC '16,加利福尼亚州圣克拉拉,2016 年 10 月 5-7 日),https://oreil.ly/wZgOO

¹⁶ Jeffrey Dean, Greg Corrado, Rajat Monga, Kai Chen, Matthieu Devin, Mark Mao, Marc’aurelio Ranzato 等,《大规模分布式深度网络》,NIPS 2012,https://oreil.ly/EWPun

¹⁷ Jim Dowling,《分布式 TensorFlow》,O’Reilly Media,2017 年 12 月 19 日,https://oreil.ly/VYlOP

¹⁸ Feng Niu, Benjamin Recht, Christopher Ré 和 Stephen J. Wright,《Hogwild!:一种无锁化并行随机梯度下降方法》,2011 年,https://oreil.ly/sAEbv

¹⁹ Tom B. Brown, Benjamin Mann, Nick Ryder, Melanie Subbiah, Jared Kaplan, Prafulla Dhariwal, Arvind Neelakantan 等,《语言模型是少样本学习者》,arXiv,2020 年 5 月 28 日,https://oreil.ly/qjg2S

²⁰ Sam McCandlish, Jared Kaplan, Dario Amodei 和 OpenAI Dota Team,《大批量训练的实证模型》,arXiv,2018 年 12 月 14 日,https://oreil.ly/mcjbV;Christopher J. Shallue, Jaehoon Lee, Joseph Antognini, Jascha Sohl-Dickstein, Roy Frostig 和 George E. Dahl,《数据并行对神经网络训练效果的衡量》,Journal of Machine Learning Research,20 (2019): 1–49,https://oreil.ly/YAEOM

²¹ Jure Leskovec,《挖掘大规模数据集》斯坦福课程,第 13 讲,2020 年,https://oreil.ly/gZcja

²² Yanping Huang, Youlong Cheng, Ankur Bapna, Orhan Firat, Mia Xu Chen, Dehao Chen, HyoukJoong Lee 等,《GPipe:微批量管道并行易扩展》,arXiv,2019 年 7 月 25 日,https://oreil.ly/wehkx

²³ 我们将在第七章中讨论量化问题。

²⁴ GSD 是一种有详细文档记录的技术。参见“人们是如何提出所有这些疯狂的深度学习架构的?”,Reddit,https://oreil.ly/5vEsH;“在 Google Brain/FAIR/DeepMind 等组织中关于科学的辩论”,Reddit,https://oreil.ly/2K77r;“研究生下降”,Science Dryad,2014 年 1 月 25 日,https://oreil.ly/dIR9r;Guy Zyskind (@GuyZys),“研究生下降:首选的 #非线性 #优化 技术 #机器学习”,Twitter,2015 年 4 月 27 日,https://oreil.ly/SW1or

²⁵ auto-sklearn 2.0 也提供基本的模型选择能力。

²⁶ 我们在 NVIDIA 开发了Milano,一个框架不可知的工具,用于使用随机搜索进行自动超参数调整。

²⁷ 我观察到的一个常见做法是从粗到细的随机搜索开始,一旦搜索空间被显著缩减,再尝试贝叶斯或网格搜索。

²⁸ Barret Zoph 和 Quoc V. Le,“用强化学习进行神经架构搜索”,arXiv,2016 年 11 月 5 日,https://oreil.ly/FhsuQ;Esteban Real, Alok Aggarwal, Yanping Huang 和 Quoc V. Le,“用于图像分类器架构搜索的正则化进化”,AAAI 2019,https://oreil.ly/FWYjn

²⁹ 您可以使搜索空间连续以允许差异化,但结果架构必须转换为离散架构。参见“DARTS: Differentiable Architecture Search”(Liu 等,2018 年)。

³⁰ 我们在书的 GitHub 仓库中更详细地涵盖了学习过程和优化器的内容。

³¹ Luke Metz, Niru Maheswaranathan, C. Daniel Freeman, Ben Poole 和 Jascha Sohl-Dickstein,“任务、稳定性、架构和计算:训练更有效的学习优化器,并使用它们自我训练”,arXiv,2020 年 9 月 23 日,https://oreil.ly/IH7eT

³² Mingxing Tan 和 Quoc V. Le,“EfficientNet:通过 AutoML 和模型缩放提高准确性和效率”,Google AI Blog,2019 年 5 月 29 日,https://oreil.ly/gonEn

³³ Samantha Murphy,“Facebook News Feed 的演变”,Mashable,2013 年 3 月 12 日,https://oreil.ly/1HMXh

³⁴ Iveta Ryšavá,《马克·扎克伯格 2006 年的新闻动态》,Newsfeed.org,2016 年 1 月 14 日,https://oreil.ly/XZT6Q

³⁵ Martin Zinkevich,《机器学习规则:ML 工程的最佳实践》,Google,2019 年,https://oreil.ly/YtEsN

³⁶ 我们将深入探讨如何在第九章中更新您的模型频率。

³⁷ 请参阅部分 “业务和机器学习目标”。

³⁸ Fréchet inception distance,一种衡量合成图像质量的常用指标。数值越小,质量越高。

³⁹ 在这种情况下,准确率大约为 0.80。

⁴⁰ 重新查看部分 “使用正确的评估指标”,了解 F1 的不对称性。

⁴¹ 其他噪声数据的示例包括光照不同的图像或意外拼写错误或意图修改文本,例如将“long”打成“loooooong”。

⁴² Khristopher J. Brooks,《少数族裔居住贷款成本差异化问题的研究》,CBS 新闻,2019 年 11 月 15 日,https://oreil.ly/TMPVl

⁴³ 在模型训练过程中,可能也会被法律要求排除敏感信息。

⁴⁴ 欲了解更多关于校准推荐的信息,请参阅 2018 年由 Harald Steck 在 Netflix 工作期间发表的论文 “校准推荐”

⁴⁵ Maggie Zhang,《Google 照片通过面部识别软件将两名非洲裔美国人标记为大猩猩》,福布斯,2015 年 7 月 1 日,https://oreil.ly/VYG2j

⁴⁶ P. J. Bickel, E. A. Hammel 和 J. W. O’Connell,《伯克利的研究生录取中的性别偏见:数据来自科学》187 (1975): 398–404,https://oreil.ly/TeR7E

⁴⁷ 对于希望了解跨文化用户体验设计的读者,Jenny Shen 在她的优秀文章中有更多信息。

第七章:模型部署与预测服务

在第四章 训练数据 到第六章 模型开发和离线评估 中,我们已经讨论了开发机器学习模型的考虑因素,从创建训练数据、提取特征、开发模型到设计评估该模型的指标。这些考虑因素构成了模型的逻辑——从原始数据到机器学习模型的操作指南,如 图 7-1 所示。开发这种逻辑需要机器学习知识和专业知识。在许多公司中,这是由机器学习或数据科学团队完成的流程的一部分。

图 7-1 不同方面构成的机器学习模型逻辑

在本章中,我们将讨论迭代过程中的另一部分:部署你的模型。“部署”是一个泛指,通常意味着使你的模型运行并可访问。在模型开发过程中,你的模型通常运行在开发环境中。¹ 要部署,你的模型将不得不离开开发环境。你的模型可以部署到一个用于测试的暂存环境,或者部署到一个用于最终用户使用的生产环境。在本章中,我们专注于将模型部署到生产环境中。

在我们继续之前,我想强调一点,生产是一个连续的过程。对于一些团队,生产意味着在笔记本中生成漂亮的图表供业务团队查看。对于其他团队,生产意味着保持模型每天为数百万用户运行。如果你的工作属于第一种情况,你的生产环境类似于开发环境,那么这一章对你来说不太相关。如果你的工作更接近第二种情况,请继续阅读。

我曾在互联网上的某处读到过:如果你忽略所有困难的部分,部署就是简单的。如果你想为朋友们部署一个模型以供玩耍,你只需将你的预测函数包装在一个 POST 请求的端点中,使用 Flask 或 FastAPI,将这个预测函数需要运行的依赖项放入容器中,² 然后将你的模型及其相关容器推送到像 AWS 或 GCP 这样的云服务以公开端点:

# Example of how to use FastAPI to turn your predict function 
# into a POST endpoint
@app.route('/predict', methods=['POST'])
def predict():
    X = request.get_json()['X']
    y = MODEL.predict(X).tolist()
    return json.dumps({'y': y}), 200

你可以使用此公开的端点供下游应用程序使用:例如,当应用程序接收到用户的预测请求时,该请求将被发送到公开的端点,该端点返回一个预测结果。如果你熟悉必要的工具,你可以在一个小时内完成功能部署。即使我的学生们在一个为期 10 周的课程后,虽然很少有部署经验,但都能够将机器学习应用程序部署为他们的最终项目。³

艰难的部分包括使您的模型对数百万用户可用,延迟为毫秒,正常运行时间为 99%,设置基础设施以便在出现问题时立即通知合适的人员,找出问题所在,并无缝地部署更新以修复问题。

在许多公司中,部署模型的责任落在开发这些模型的同一组人手中。在许多其他公司中,一旦模型准备部署,它将被导出并移交给另一个团队来部署。然而,这种责任的分离可能导致团队之间的沟通成本高昂,并使得更新模型变得缓慢。它也可能导致在出现问题时难以进行调试。我们将在第十一章中更多讨论团队结构。

注:

导出模型意味着将此模型转换为另一个应用程序可以使用的格式。有些人称此过程为“序列化”。⁴ 模型的两个部分可以导出:模型定义和模型的参数值。模型定义定义了您的模型的结构,例如它有多少隐藏层和每层多少单元。参数值提供这些单元和层的值。通常这两个部分一起导出。

在 TensorFlow 2 中,您可能会使用 tf.keras.Model.save() 将您的模型导出到 TensorFlow 的 SavedModel 格式。在 PyTorch 中,您可能会使用 torch.onnx.export() 将您的模型导出到 ONNX 格式。

无论你的工作是否涉及部署 ML 模型,了解你的模型如何使用可以帮助你理解它们的限制,并帮助你根据其目的进行调整。

在这一章中,我们将从一些关于 ML 部署的常见误解开始,这些误解通常来自于那些没有部署过 ML 模型的人。然后我们将讨论模型向用户生成和提供预测的两种主要方式:在线预测和批处理预测。生成预测的过程称为推理

我们将继续讨论生成预测的计算应该在哪里进行:设备上(也称为边缘)和云上。模型如何提供和计算预测会影响其设计方式、所需的基础设施和用户遇到的行为。

如果你来自学术背景,本章讨论的一些话题可能超出你的舒适区。如果出现不熟悉的术语,请花点时间查找它。如果某一部分变得过于密集,请随意跳过。本章是模块化的,因此跳过一部分不应影响你对另一部分的理解。

机器学习部署的误解

如同在 第一章 中讨论的那样,部署机器学习模型与部署传统软件程序有很大不同。这种差异可能会导致从未部署过模型的人们要么害怕这个过程,要么低估了所需的时间和精力。在本节中,我们将揭示一些关于部署过程的常见误解,希望能让您在开始这一过程时保持良好的心态。本节对那些没有或几乎没有部署经验的人最有帮助。

误解 1:一次只部署一两个机器学习模型

在做学术项目时,我被建议选择一个小问题进行专注,通常这会导致一个单一的模型。我与来自学术背景的许多人交流后发现,他们也倾向于在生产机器学习模型时考虑单一模型。因此,他们设想的基础设施不适用于实际应用,因为它只能支持一两个模型。

实际上,公司拥有许多许多机器学习模型。一个应用可能有许多不同的特性,每个特性可能需要自己的模型。以 Uber 这样的顺风车应用为例。它需要预测以下各种因素的模型:乘车需求、司机可用性、预计到达时间、动态定价、欺诈交易、客户流失等等。此外,如果该应用在 20 个国家运营,直到可以有跨不同用户配置文件、文化和语言泛化的模型,每个国家都需要自己的模型。因此,对于 20 个国家,每个国家需要 10 个模型,您已经有了 200 个模型。图 7-2 展示了 Netflix 上利用机器学习的广泛任务范围。

图片

图 7-2. Netflix 上利用机器学习的不同任务。来源:Ville Tuulos⁵

实际上,Uber 正在生产中使用数千个模型。⁶ 在任何时刻,Google 都在并行训练数千个模型,每个模型的参数规模达到数百亿。⁷ Booking.com 拥有 150 多个模型。⁸ 2021 年 Algorithmia 的一项研究显示,在拥有超过 25,000 名员工的组织中,41%的组织在生产中使用超过 100 个模型。⁹

误解 2:如果我们什么都不做,模型的性能将保持不变

软件不像美酒一样随着时间的推移变老越来越好,它老化的速度比较快。即使看起来没有变化,软件程序随时间逐渐退化的现象被称为“软件腐化”或“位腐化”。

机器学习系统对此并不免疫。除此之外,机器学习系统还会遭受所谓的数据分布漂移的影响,即模型在生产环境中遇到的数据分布与其训练时的数据分布不同。¹⁰ 因此,机器学习模型倾向于在训练后表现最佳,并随时间而逐渐退化。

谬误 3:你不需要频繁更新你的模型

人们经常问我:“我应该多频繁地 更新 我的模型?”这是一个错误的问题。正确的问题应该是:“我可以多频繁地 更新 我的模型?”

由于模型的性能会随时间衰减,我们希望尽可能快地对其进行更新。这是一个我们应该从现有的 DevOps 最佳实践中学习的 ML 领域。早在 2015 年,人们就已经在不断地推送系统更新。Etsy 每天部署 50 次,Netflix 每天数千次,AWS 每 11.7 秒一次。¹¹

虽然很多公司仍然只每月或甚至每季度更新他们的模型,微博更新一些 ML 模型的迭代周期为 10 分钟。¹² 我听说阿里巴巴和抖音(TikTok 的公司)也有类似的情况。

正如 Josh Wills 所说,他曾在 Google 担任过高级工程师,现在是 Slack 的数据工程主管,“我们总是尽可能快地将新模型投入生产。”¹³

我们将在 第九章 中详细讨论模型重新训练的频率。

谬误 4:大多数 ML 工程师不需要担心规模问题

“规模” 的含义因应用而异,但例如一个每秒服务数百个查询或每月服务数百万用户的系统。

如果是这样的话,你可能会争辩说只有少数公司需要担心这个问题。Google 只有一个,Facebook 只有一个,Amazon 也只有一个。这是事实,但是少数大公司雇佣了大多数软件工程师。根据 2019 年 Stack Overflow 开发者调查,超过一半的受访者在至少有 100 名员工的公司工作(见 图 7-3)。这并不是完全相关,但是一个有 100 名员工的公司有很大的机会为一定数量的用户提供服务。

图 7-3. 软件工程师工作所在公司规模的分布。来源:根据 Stack Overflow 的一张图修改¹⁴

我没有找到关于专门 ML 角色的调查,所以我在 Twitter 上提问并得到了类似的结果。这意味着如果你在行业中寻找与 ML 相关的工作,你很可能会在至少有 100 名员工的公司工作,而这些公司的 ML 应用可能需要具备可扩展性。统计学上来说,一个 ML 工程师应该关心规模问题。

批量预测与在线预测的区别

你将需要做出的一个基础决策,这将影响最终用户以及在系统上工作的开发人员,那就是系统如何生成和向最终用户提供预测:在线还是批处理。由于行业中缺乏标准化的实践,围绕批处理和在线预测的术语仍然相当混乱。在本节中,我将尽力解释每个术语的细微差别。如果你觉得这里提到的任何术语太令人困惑,请随意暂时忽略它们。如果你忘记了其他一切,我希望你能记住三种主要的预测模式:

  • 只使用批处理特征的批处理预测。

  • 只使用批处理特征(例如预计算的嵌入)的在线预测。

  • 使用批处理特征和流处理特征的在线预测。这也被称为流式预测。

在线预测 是指在请求到达时生成预测并立即返回的过程。例如,你输入一个英文句子到谷歌翻译中,立即获得其法语翻译。在线预测也被称为 按需预测。传统上,在进行在线预测时,请求通过 RESTful API(例如 HTTP 请求—参见“数据通过服务传递”)发送到预测服务。当通过 HTTP 请求发送预测请求时,在线预测也被称为 同步预测:预测与请求同步生成。

批处理预测 是指定期生成预测或在触发时生成预测。预测存储在某处,例如 SQL 表或内存数据库,并根据需要检索。例如,Netflix 可能每四小时为其所有用户生成电影推荐,预先计算的推荐在用户登录 Netflix 时获取并显示。批处理预测也被称为 异步预测:预测与请求异步生成。

术语混淆

术语“在线预测”和“批处理预测”可能会令人困惑。两者都可以批量预测多个样本或单个样本。为了避免混淆,有时人们更喜欢使用术语“同步预测”和“异步预测”。然而,这种区分也并非完美,因为当在线预测利用实时传输将预测请求发送到您的模型时,请求和预测在技术上是异步的。

图 7-4 显示了用于批处理预测的简化架构,而 图 7-5 则展示了仅使用批处理特征的在线预测的简化版本。接下来我们将详细介绍仅使用批处理特征的含义。

图 7-4。用于批处理预测的简化架构

图 7-5. 一个仅使用批量特征的在线预测简化架构

如 Chapter 3 中所讨论的,从历史数据(如数据库和数据仓库中的数据)计算得出的特征是 批量特征。而从流式数据(实时传输的数据)计算得出的特征是 流式特征。在批量预测中,只使用批量特征。然而,在线预测中,可以同时使用批量特征和流式特征。例如,在用户在 DoorDash 上下订单后,可能需要以下特征来估计送达时间:

批量特征

这家餐厅过去的平均准备时间

流式特征

在过去的 10 分钟内,他们还有多少其他订单,以及有多少送餐人员可用

流式特征与在线特征

我听说过“流式特征”和“在线特征”这两个术语被互换使用。它们实际上是不同的。在线特征更为一般化,因为它们指的是任何用于在线预测的特征,包括存储在内存中的批量特征。

在线预测中用于批量特征的一种非常常见的批量特征类型,特别是基于会话的推荐系统,是项目嵌入。项目嵌入通常是批量预先计算的,并在在线预测需要时获取。在这种情况下,嵌入可以视为在线特征,但不是流式特征。

流式特征专指从流式数据计算得出的特征。

一个使用流式特征和批量特征的在线预测简化架构如图 Figure 7-6 所示。有些公司将这种预测称为“流式预测”,以区分不使用流式特征的在线预测。

图 7-6. 一个同时使用批量特征和流式特征的在线预测简化架构

然而,在线预测和批量预测并不必然是互斥的。一种混合解决方案是为热门查询预先计算预测结果,然后为不那么热门的查询在线生成预测结果。Table 7-1 总结了在线预测和批量预测需要考虑的关键点。

表 7-1. 批量预测与在线预测之间的一些关键区别

批量预测(异步) 在线预测(同步)
频率 周期性,例如每四小时 随请求即时到达
适用于 处理累积数据时不需要即时结果(如推荐系统) 在生成数据样本后需要立即预测时(如欺诈检测)
优化目标 高吞吐量 低延迟

在许多应用中,在线预测和批处理预测并用于不同的用例。例如,像 DoorDash 和 UberEats 这样的食品订购应用使用批处理预测来生成餐厅推荐——在线生成这些推荐可能需要太长时间,因为有很多餐馆。但是,一旦你点击一个餐馆,使用在线预测生成食品项目推荐。

许多人认为在线预测比批处理预测在成本和性能上效率低,因为你可能无法将输入批量处理,并利用向量化或其他优化技术。如我们在“批处理与流处理”部分已讨论的那样,并非一定如此。

此外,使用在线预测,你不必为不访问你网站的用户生成预测。想象一下,你运行一个应用,只有 2%的用户每天登录——例如,2020 年,Grubhub 拥有 3100 万用户和 622,000 个日常订单。¹⁵ 如果你每天为每个用户生成预测,那么用于生成 98%预测的计算资源将被浪费。

从批处理预测到在线预测

对于从学术背景转向机器学习的人来说,提供预测的更自然方式可能是在线预测。当你给模型输入并收到输入时,它会立即生成一个预测。这可能是大多数人在原型开发时与其模型交互的方式。对于大多数公司在首次部署模型时来说,这也可能更容易。你导出你的模型,将导出的模型上传到亚马逊 SageMaker 或 Google App Engine,并获得一个公开的端点。¹⁶ 现在,如果你发送一个包含输入的请求到该端点,它将返回在该输入上生成的预测。

在线预测的一个问题是你的模型可能花费太长时间来生成预测。如果不是在收到请求时立即生成预测,那么你可以提前计算预测并将其存储在数据库中,并在请求到达时获取它们。这正是批处理预测所做的。采用这种方法,你可以一次为多个输入生成预测,利用分布式技术高效处理大量样本。

因为预测是预先计算的,你不必担心模型生成预测需要多长时间。因此,批处理预测也可以被视为减少更复杂模型推断延迟的技巧——检索预测的时间通常比生成预测的时间短。

当你想要生成大量预测并且不需要立即得到结果时,批量预测是很好的选择。你不必使用所有生成的预测结果。例如,你可以预测所有客户购买新产品的可能性,并与排名前 10%的客户联系。

然而,批量预测的问题在于它使得你的模型对用户变化的偏好反应变慢。即使在像 Netflix 这样更先进的技术公司,这种限制也是显而易见的。比如最近你一直在看恐怖电影,所以当你第一次登录 Netflix 时,恐怖电影占据了推荐列表。但今天你感觉很开心,于是搜索了“喜剧”并开始浏览喜剧类别。Netflix 应该学习并在推荐列表中展示更多的喜剧电影,对吧?截至撰写本书时,它不能在下一批推荐生成之前更新列表,但我毫无疑问这种限制将在不久的将来得到解决。

批量预测的另一个问题是需要预先知道为哪些请求生成预测。例如,对于用户推荐电影的情况,你预先知道需要为多少用户生成推荐。然而,对于有不可预测查询的情况——比如一个系统用于英语到法语的翻译,可能无法预测到每一个可能的英文文本——你需要使用在线预测来根据请求生成预测。

在 Netflix 的例子中,批量预测会导致轻微的不便(这与用户参与度和保留率密切相关),而非灾难性的失败。有许多应用场景,批量预测可能导致灾难性的失败或者根本无法使用。在线预测至关重要的例子包括高频交易、自动驾驶车辆、语音助手、使用面部或指纹解锁手机、老年护理的跌倒检测以及欺诈检测。能够检测到三小时前发生的欺诈交易总比完全不检测要好,但能够实时检测可以阻止欺诈交易的进行。

批量预测是在线预测不够便宜或速度不够快时的一种变通方案。如果可以以同样的成本和速度生成每个预测,为什么要提前生成一百万个预测并担心存储和检索呢?

随着硬件变得更加定制化和强大,并且正在开发更好的技术以允许更快、更便宜的在线预测,在线预测可能会成为默认选择。

近年来,公司已经投入大量资金从批量预测转向在线预测。为了克服在线预测的延迟挑战,需要两个组成部分:

  • 一个(几乎)实时的管道,可以处理传入数据,提取流特征(如果需要),将其输入到模型中,并在几乎实时返回预测结果。具备实时传输和流计算引擎的流水线可以帮助实现这一目标。

  • 模型能够以对其最终用户可接受的速度生成预测。对于大多数消费者应用程序来说,这意味着毫秒级的响应时间。

我们在第三章中讨论了流处理。在接下来的部分,我们将继续讨论如何统一流水线和批处理管道。然后,我们将讨论如何在“模型优化”部分加快推断的速度。

统一批处理管道和流处理管道

批量预测在很大程度上是传统系统的产物。在过去的十年中,大数据处理主要由像 MapReduce 和 Spark 这样的批处理系统主导,这些系统能够高效地周期性处理大量数据。当公司开始使用 ML 时,他们利用现有的批处理系统进行预测。当这些公司希望在在线预测中使用流处理特性时,他们需要构建一个单独的流水线。让我们通过一个示例来具体说明这一点。

假设你想要构建一个类似 Google Maps 的应用程序,用于预测到达时间。预测将随着用户行程的进行而持续更新。你可能想使用的一个特征是在你路径中所有车辆的平均速度(过去五分钟内)。在训练时,你可能会使用过去一个月的数据。要从训练数据中提取此特征,你可能希望将所有数据放入数据框架中,以同时为多个训练样本计算此特征。在推断过程中,此特征将持续在滑动窗口上计算。这意味着在训练时,此特征是批处理计算的,而在推断时,此特征是流处理的。

在 ML 生产中,拥有两种不同的数据处理管道是常见的 bug 引发原因之一。一个导致 bug 的原因是,当一个管道中的更改没有正确复制到另一个管道时,就会导致两个管道提取两组不同的特征。特别是如果这两个管道由两个不同的团队维护,例如,机器学习团队维护批处理管道进行训练,而部署团队维护流水线进行推断,如图 7-7 所示。

图 7-7。在 ML 生产中,为训练和推断各维护两个不同的管道是常见的 bug 源。

图 7-8 展示了用于在线预测的 ML 系统数据管道的更详细但也更复杂的特性。标有“研究”标签的框选元素通常在学术环境中向人们展示。

图 7-8. 用于进行在线预测的 ML 系统数据管道

在 ML 社区中,建立统一流处理和批处理基础设施近年来成为一个热门话题。包括 Uber 和微博在内的公司通过使用像 Apache Flink 这样的流处理器,进行了主要的基础设施改造,以统一其批处理和流处理管道。^(参考 18:ch07.xhtml#ch01fn215)一些公司使用特征存储库来确保训练期间使用的批处理特征与预测中使用的流特征的一致性。我们将在第十章中讨论特征存储库。

模型压缩

我们已经讨论了流水线,允许 ML 系统从传入数据中提取流特征,并将其输入到 ML 模型中以(几乎)实时进行处理。然而,仅具有接近(实时)的流水线对于在线预测来说还不足够。在接下来的部分中,我们将讨论 ML 模型的快速推理技术。

如果您要部署的模型生成预测时间过长,有三种主要方法可以减少推理延迟:使其进行更快的推理、使模型更小,或者使其部署的硬件运行更快。

缩小模型的过程称为模型压缩,使其进行更快推理的过程称为推理优化。最初,模型压缩是为了使模型适应边缘设备。然而,缩小模型通常也会使其运行更快。

我们将在“模型优化”一节中讨论推理优化,而在“云端和边缘上的 ML”一节中讨论专门用于加速 ML 模型运行的硬件后端的情况。在这里,我们将讨论模型压缩。

模型压缩的研究论文数量正在增加。现成的实用工具正在蓬勃发展。截至 2022 年 4 月,Awesome Open Source 列出了“前 168 名模型压缩开源项目”,并且该列表还在增加。尽管有许多新技术正在开发中,但您可能经常遇到的四种技术类型包括低秩优化、知识蒸馏、修剪和量化。有兴趣进行全面审查的读者可能希望查看程等人更新于 2020 年的“深度神经网络模型压缩与加速综述”。^(参考 19:ch07.xhtml#ch01fn216)

低秩分解

低秩分解的关键思想是用低维张量替换高维张量。^(参考 20:ch07.xhtml#ch01fn217)一种低秩分解类型是紧凑卷积滤波器,其中过参数化(具有过多参数)的卷积滤波器被紧凑块替换,以减少参数数量并增加速度。

例如,通过使用包括将 3 × 3 卷积替换为 1 × 1 卷积在内的多种策略,SqueezeNets 在 ImageNet 上以比标准模型少 50 倍的参数达到了 AlexNet 级别的准确性。²¹

同样,MobileNets 将尺寸为 K × K × C 的标准卷积分解为深度卷积 (K × K × 1) 和逐点卷积 (1 × 1 × C),其中 K 是核大小,C 是通道数。这意味着每个新卷积仅使用 K² + C 而不是 K²C 个参数。如果 K = 3,这意味着参数数量减少了八到九倍(参见 图 7-9)。²²

图 7-9. MobileNets 中的紧凑卷积滤波器。标准卷积滤波器在 (a) 中被深度卷积替代,而在 (b) 和 (c) 中被逐点卷积替代,以构建深度可分离滤波器。来源:根据 Howard 等人的图像调整。

此方法已用于开发与标准模型相比具有显著加速的更小模型。然而,它倾向于特定类型的模型(例如,紧凑的卷积滤波器特定于卷积神经网络),并且需要大量的架构知识来设计,因此尚未广泛适用于许多用例。

知识蒸馏

知识蒸馏 是一种方法,其中一个小模型(学生)被训练成模仿一个较大模型或模型集合(教师)。较小的模型是您将部署的模型。尽管学生通常在预训练的教师之后进行训练,但两者也可以同时进行训练。²³ 在生产中使用的一个例子是 DistilBERT,它将 BERT 模型的大小减少了 40%,同时保留了 97% 的语言理解能力,并且速度提升了 60%。²⁴

这种方法的优点在于,它可以在教师网络和学生网络之间的架构差异不大时工作。例如,您可以将随机森林作为学生,将变压器作为教师。这种方法的缺点在于,它高度依赖于教师网络的可用性。如果您使用预训练模型作为教师模型,那么训练学生网络将需要较少的数据,并且可能会更快地完成。然而,如果没有可用的教师,您将需要在训练学生网络之前训练教师网络,并且训练教师网络将需要更多的数据和更长的训练时间。此方法还对应用和模型架构敏感,因此尚未在生产中广泛使用。

精简

修剪 最初是用于决策树的方法,其中您删除对分类无关和冗余的树部分。²⁵ 随着神经网络的广泛采用,人们开始意识到神经网络存在过度参数化问题,并开始寻找减少额外参数工作量的方法。

在神经网络的上下文中,修剪有两种含义。一种是删除神经网络的整个节点,这意味着改变其架构并减少其参数数量。更常见的含义是找到对预测最无用的参数,并将它们设为 0。在这种情况下,修剪不会减少总参数数量,只会减少非零参数的数量。神经网络的架构保持不变。这有助于减少模型的大小,因为修剪使得神经网络更稀疏,而稀疏结构通常比密集结构需要更少的存储空间。实验证明,修剪技术可以将经过训练的网络的非零参数数量减少超过 90%,从而减少存储需求并提高推断的计算性能,而不会影响总体准确性。²⁶ 在第十一章,我们将讨论修剪如何为模型引入偏差。

尽管人们普遍认为修剪有效,²⁷但关于修剪的实际价值有很多讨论。刘等人认为修剪的主要价值不在于“重要权重”,而在于修剪后的架构本身。²⁸ 在某些情况下,修剪可以作为一种架构搜索范式,修剪后的架构应该从头开始重新训练作为稠密模型。然而,朱等人表明,修剪后的大型稀疏模型表现优于重新训练的密集对应模型。²⁹

量化

量化 是最常见和通用的模型压缩方法。它易于操作,并适用于各种任务和架构。

量化通过使用更少的比特来表示模型的参数来减小模型的大小。大多数软件包默认使用 32 位来表示浮点数(单精度浮点数)。如果一个模型有 1 亿个参数,并且每个参数需要 32 位来存储,那么它将占用 400 MB 的内存。如果我们使用 16 位来表示一个数,我们将把内存占用减少一半。使用 16 位来表示浮点数称为半精度。

您可以使用整数完全构建模型,而不是使用浮点数;每个整数仅需 8 位表示。这种方法也被称为“固定点”。在极端情况下,一些人尝试了每个权重的 1 位表示(二进制权重神经网络),例如 BinaryConnect 和 XNOR-Net。³⁰ XNOR-Net 论文的作者创办了 Xnor.ai,一家专注于模型压缩的初创公司。在 2020 年初,该公司以 2 亿美元的价格被苹果收购。³¹

量化不仅减少了内存占用,还提高了计算速度。首先,它允许我们增加批处理大小。其次,低精度加速计算,进一步减少了训练时间和推断延迟。考虑两个数字的加法。如果我们逐位执行加法,每个位需要x纳秒,则对于 32 位数字,需要 32x纳秒,而对于 16 位数字,只需 16x纳秒。

量化也存在一些缺点。减少位数以表示数字意味着您可以表示的数值范围更小。对于超出该范围的值,您必须将其四舍五入并/或按比例缩放以适应该范围。四舍五入会导致舍入误差,而小的舍入误差可能导致性能大幅变化。您还面临将数字舍入/缩放至下溢/溢出并将其渲染为 0 的风险。有效的舍入和缩放在低级别实现起来并不容易,但幸运的是,主要框架已经内置了这一功能。

量化可以在训练期间进行(量化感知训练),³² 在此期间,模型以较低精度进行训练,或者在训练后进行(后量化),在此期间,模型以单精度浮点数进行训练,然后量化以进行推断。在训练期间使用量化意味着每个参数可以使用更少的内存,这使您可以在相同的硬件上训练更大的模型。

近年来,低精度训练变得越来越流行,得到了大多数现代训练硬件的支持。英伟达推出了 Tensor Cores,这是支持混合精度训练的处理单元。³³ 谷歌的 TPU(张量处理单元)也支持使用 Bfloat16(16 位脑浮点格式)进行训练,谷歌将其称为“Cloud TPU 性能的秘密”。³⁴ 固定点训练虽然尚未流行,但已经取得了许多有希望的成果。³⁵

固定点推断已经成为行业标准。一些边缘设备仅支持固定点推断。最流行的在设备上进行机器学习推断的框架,如谷歌的 TensorFlow Lite、Facebook 的 PyTorch Mobile、英伟达的 TensorRT,提供了带有几行代码的后训练量化功能。

云端和边缘上的机器学习

您还需要考虑的另一个决策是模型计算发生的位置:在云端还是在边缘。在云端意味着大部分计算在云上进行,可以是公共云或私有云。在边缘意味着大部分计算在消费者设备上,如浏览器、手机、笔记本电脑、智能手表、汽车、安全摄像头、机器人、嵌入式设备、FPGA(现场可编程门阵列)和 ASIC(专用集成电路),这些也被称为边缘设备。

最简单的方式是将你的模型打包并通过 AWS 或 GCP 等托管云服务进行部署,这也是许多公司在开始机器学习时的部署方式。云服务在使公司将机器学习模型投入生产方面做出了不可思议的贡献。

但是,云部署有许多缺点。首先是成本。机器学习模型可能需要大量计算资源,而计算资源很昂贵。即使在 2018 年,像 Pinterest、Infor 和 Intuit 这样的大公司每年的云服务账单也已经高达数亿美元。³⁷ 对于中小型企业来说,这个数字可能在 50,000 美元到 2,000,000 美元之间。³⁸ 在处理云服务时出现的错误可能导致初创公司破产。³⁹

随着云服务账单的上升,越来越多的公司正在寻找将计算推向边缘设备的方法。在边缘设备上进行的计算量越多,云端所需的计算量就越少,公司需要支付的服务器费用也就越少。

除了帮助控制成本之外,边缘计算还具有许多吸引人的特性。首先,它允许您的应用程序在云计算无法运行的地方运行。当您的模型部署在公共云上时,它们依赖于稳定的互联网连接来发送数据到云端和返回。边缘计算允许您的模型在没有互联网连接或连接不稳定的情况下工作,例如在农村地区或发展中国家。我曾与几家有严格无互联网政策的公司和组织合作过,这意味着我们想要销售给他们的任何应用程序都不能依赖互联网连接。

其次,当您的模型已经部署在消费者设备上时,您可以较少担心网络延迟问题。需要通过网络传输数据(将数据发送到云端模型进行预测,然后将预测结果发送回用户)可能会使某些用例无法实现。在许多情况下,网络延迟比推理延迟更成为瓶颈。例如,您可能能够将 ResNet-50 的推理延迟从 30 毫秒减少到 20 毫秒,但网络延迟可能会高达几秒,具体取决于您所在的位置和您尝试使用的服务。

在处理敏感用户数据时,将模型放在边缘也很有吸引力。云端的机器学习意味着系统可能必须通过网络发送用户数据,这使得数据容易被截取。云计算还经常意味着将许多用户的数据存储在同一地点,这意味着一次破坏可能会影响到许多人。“根据《安全》杂志的数据,近 80%的公司在过去 18 个月中经历过云数据泄露。”

边缘计算使得遵循像 GDPR 这样的法规更加容易,关于如何传输或存储用户数据。虽然边缘计算可能会减少隐私问题,但并不能完全消除。在某些情况下,边缘计算可能会使攻击者更容易窃取用户数据,例如他们可以直接带走设备。

要将计算转移到边缘,边缘设备必须足够强大,能够处理计算任务,有足够的内存来存储机器学习模型并将其加载到内存中,同时有足够的电池或连接到能源源以合理时间供电。如果你的手机有能力运行全尺寸的 BERT 模型,那么在手机上运行 BERT 将非常快地耗尽它的电池。

由于边缘计算相比云计算具有多种优势,公司们正在竞相开发为不同机器学习用例优化的边缘设备。包括谷歌、苹果和特斯拉在内的大公司都宣布了他们制造自己芯片的计划。与此同时,机器学习硬件初创公司已经筹集了数十亿美元来开发更好的人工智能芯片。据预测,到 2025 年全球活跃的边缘设备数量将超过 300 亿。

随着为硬件运行机器学习模型的新选择日益增多,一个问题浮现:我们如何在任意硬件上高效运行我们的模型?在接下来的部分中,我们将讨论如何编译和优化模型,以在特定硬件后端上运行。在此过程中,我们将介绍在处理边缘模型时可能遇到的重要概念,包括中间表示(IR)和编译器。

为边缘设备编译和优化模型

对于使用特定框架(如 TensorFlow 或 PyTorch)构建的模型要在硬件后端上运行,该框架必须得到硬件供应商的支持。例如,尽管 TPU 在 2018 年 2 月公开发布,但直到 2020 年 9 月 PyTorch 才被支持在 TPU 上运行。在此之前,如果想使用 TPU,必须使用 TPU 支持的框架。

为硬件后端提供框架支持耗时且需要大量工程资源。从 ML 工作负载映射到硬件后端需要理解和利用该硬件的设计,不同的硬件后端具有不同的内存布局和计算原语,如图 7-11 所示。

图 7-11. CPU、GPU 和 TPU 的不同计算原语和内存布局。来源:根据陈等人的图片适配。⁴³

例如,CPU 的计算原语曾经是一个数字(标量),GPU 的计算原语曾经是一维向量,而 TPU 的计算原语是二维向量(张量)。⁴⁴ 使用卷积运算符时,一维向量和二维向量的差异非常大。同样,您需要考虑不同的 L1、L2 和 L3 布局和缓冲区大小以有效地使用它们。

因为这一挑战,框架开发人员倾向于只支持少数几种服务器级硬件,并且硬件供应商倾向于为一小部分框架提供自己的内核库。将 ML 模型部署到新硬件需要大量手动工作。

如果不是为每个新硬件后端创建新的编译器和库,那么我们创建一个中间人来桥接框架和平台会怎样?框架开发人员将不再需要支持每种类型的硬件,他们只需将框架代码转换为这个中间人即可。硬件供应商随后可以支持一个中间人而不是多个框架。

这种“中间人”称为中间表示(IR)。 IRs 是编译器工作的核心。从模型的原始代码开始,编译器在生成本地硬件后端可以运行的代码之前会生成一系列高级和低级 IRs,如图 7-12 所示。

图 7-12. 从原始模型代码到能在特定硬件后端上运行的机器码之间的一系列高级和低级中间表示(IR)

这个过程也称为降低,即将高级框架代码降低为低级硬件本机代码。这不是简单的翻译,因为它们之间没有一对一的映射关系。

高级 IRs 通常是您的 ML 模型的计算图。计算图描述了计算执行顺序的图形。感兴趣的读者可以在PyTorchTensorFlow中了解计算图。

模型优化

在将代码“降低”以在所选硬件上运行模型之后,您可能会遇到的问题是性能问题。生成的机器码可能能够在硬件后端上运行,但可能无法高效运行。生成的代码可能没有利用数据局部性和硬件缓存,也可能没有利用矢量或并行操作等高级特性,这可能会加速代码。

典型的 ML 工作流包括许多框架和库。例如,您可能会使用 pandas/dask/ray 从数据中提取特征。您可能会使用 NumPy 进行向量化。您可能会使用像 Hugging Face 的 Transformers 这样的预训练模型生成特征,然后使用使用各种框架(如 sklearn、TensorFlow 或 LightGBM)构建的模型集合进行预测。

即使这些框架中的各个函数可能已经优化,跨框架的优化仍然很少。在这些函数之间进行数据移动以进行计算的天真方式可能会导致整个工作流程的性能下降一个数量级。斯坦福 DAWN 实验室的研究人员发现,使用 NumPy、pandas 和 TensorFlow 的典型 ML 工作负载在单线程中运行比手动优化的代码慢 23 倍。⁴⁵

在许多公司中,通常情况下,数据科学家和机器学习工程师开发的模型在开发阶段看起来运行良好。然而,当这些模型部署后,它们可能会变得太慢,因此公司会雇佣优化工程师来优化其在特定硬件上的模型。Mythic 的优化工程师职位描述如下:

这一愿景在 AI 工程团队中得以实现,我们利用专业知识开发适用于我们硬件的 AI 算法和模型,并为 Mythic 的硬件和编译器团队提供指导。

AI 工程团队通过以下方式显著影响 Mythic:

  • 开发量化和鲁棒性 AI 重新训练工具
  • 探索利用神经网络适应性的编译器的新特性
  • 开发针对我们硬件产品优化的新神经网络
  • 与内部和外部客户交流,以满足其开发需求

优化工程师难以找到且雇佣成本高昂,因为他们需要在机器学习和硬件架构两方面具备专业知识。优化编译器(同时优化您的代码的编译器)是另一种解决方案,因为它们可以自动化模型优化的过程。在将 ML 模型代码降低到机器码的过程中,编译器可以查看您的 ML 模型的计算图及其包含的运算符——卷积、循环、交叉熵——并找到加速计算的方法。

优化机器学习模型有两种方法:局部和全局。局部是指优化模型中的一个或一组操作符。全局是指端到端优化整个计算图。

存在标准的局部优化技术,已知可以加快模型运行速度,大多数是通过并行运行或减少芯片上的内存访问。以下是四种常见的技术:

矢量化

给定一个循环或嵌套循环,不要逐个执行,而是连续内存中执行多个元素,以减少数据 I/O 引起的延迟。

并行化

给定一个输入数组(或n维数组),将其分成不同的独立工作块,并在每个块上进行操作。

循环平铺⁴⁶

更改循环中的数据访问顺序,以利用硬件的内存布局和缓存。这种优化依赖于硬件。在 CPU 上良好的访问模式在 GPU 上可能不是良好的访问模式。

操作符融合

将多个操作符融合成一个,以避免冗余的内存访问。例如,对同一个数组进行两次操作需要对该数组进行两次循环。在融合的情况下,只需一个循环。图 7-13 展示了操作符融合的示例。

图 7-13. 操作符融合示例。来源:改编自 Matthias Boehm 的图片⁴⁷

要获得更大的加速,您需要利用计算图的更高级结构。例如,具有计算图的卷积神经网络可以进行垂直或水平融合,以减少内存访问并加快模型速度,如图 7-14 所示。

图 7-14. 卷积神经网络计算图的垂直和水平融合。来源:改编自 TensorRT 团队的图片⁴⁸

使用机器学习优化机器学习模型

正如前一节中对卷积神经网络进行垂直和水平融合的提示,有许多可能的方式来执行给定的计算图。例如,给定三个操作符 A、B 和 C,可以将 A 与 B 融合,将 B 与 C 融合,或者将 A、B 和 C 全部一起融合。

传统上,框架和硬件供应商会聘请优化工程师,根据其经验制定如何最佳执行模型计算图的启发式方法。例如,NVIDIA 可能有一个工程师或一个专门团队,专注于如何使其 DGX A100 服务器上的 ResNet-50 运行得非常快速⁴⁹。

手工设计的启发式方法有几个缺点。首先,它们不是最优的。工程师提出的启发式方法并没有保证是最佳解决方案。其次,它们不具备自适应性。在新框架或新硬件架构上重复这一过程需要大量的工作量。

模型优化受其计算图中操作符的影响。优化卷积神经网络与优化循环神经网络或优化 Transformer 不同。像 NVIDIA 和 Google 这样的硬件供应商专注于优化像 ResNet-50 和 BERT 这样的流行模型。但如果您作为 ML 研究人员提出了新的模型架构,您可能需要首先优化它,以显示它快速执行,然后才能被采纳和优化。

如果您没有好的启发式方法,一个可能的解决方案是尝试所有可能的执行计算图的方式,记录它们运行所需的时间,然后选择最佳的方式。然而,考虑到可能的路径组合数量,探索所有这些路径将是不可行的。幸运的是,近似解决不可解问题的方案是 ML 擅长的。如果我们利用 ML 来缩小搜索空间,这样就不必探索那么多路径,并预测路径所需的时间,这样我们就不必等待整个计算图执行完毕了。

评估计算图中路径运行所需时间的估计结果很难,因为需要对该图做出很多假设。更容易的方法是专注于图的一个小部分。

如果您在 GPU 上使用 PyTorch,可能已经见过torch.backends.cudnn.benchmark=True。当此选项设置为 True 时,cuDNN autotune 将启用。cuDNN autotune 在一组预定的选项上搜索以执行卷积运算,并选择最快的方式。尽管 cuDNN autotune 非常有效,但仅适用于卷积运算符。一个更通用的解决方案是autoTVM,它是开源编译器堆栈 TVM 的一部分。autoTVM 处理子图而不仅仅是操作符,因此它处理的搜索空间要复杂得多。autoTVM 的工作原理相当复杂,但简单来说:

  1. 它首先将您的计算图分解成子图。

  2. 它预测每个子图的大小。

  3. 它分配时间来搜索每个子图的最佳路径。

  4. 它将每个子图的最佳运行方式串联起来,以执行整个图。

autoTVM 测量运行每条路径所需的实际时间,从而为训练成本模型提供了地面真实数据,以预测未来路径所需的时间。这种方法的优点是,由于模型是使用运行时生成的数据进行训练的,因此可以适应其运行的任何类型的硬件。缺点是,成本模型开始改进需要更多时间。图 7-15 显示了 autoTVM 相对于 cuDNN 在 NVIDIA TITAN X 上为 ResNet-50 模型带来的性能提升。

尽管由机器学习驱动的编译器的结果令人印象深刻,但也有一个限制:它们可能会很慢。您需要遍历所有可能的路径,并找到最优化的路径。这个过程可能需要几个小时,甚至对于复杂的机器学习模型可能需要几天。然而,这是一个一次性的操作,您可以缓存优化搜索的结果,并用于优化现有模型以及为未来的调整会话提供一个起点。一旦为一个硬件后端优化了您的模型,您就可以在同类型的多个设备上运行它。当您的模型准备好投入生产并且目标硬件用于推断时,这种优化非常理想。

图 7-15. autoTVM 相对于 cuDNN 在 NVIDIA TITAN X 上为 ResNet-50 带来的加速。autoTVM 超越 cuDNN 大约需要 ~70 次试验。来源:Chen 等人⁵⁰

浏览器中的机器学习

我们一直在讨论编译器如何帮助我们生成机器本地代码来在特定硬件后端上运行模型。然而,通过在浏览器中运行代码,也可以生成可以在任何硬件后端上运行的代码。如果您可以在浏览器中运行您的模型,那么您可以在支持浏览器的任何设备上运行您的模型:MacBook、Chromebook、iPhone、Android 手机等。您不需要关心这些设备使用的芯片是什么。如果苹果决定从英特尔芯片切换到 ARM 芯片,那就不是您的问题。

谈到浏览器,许多人会想到 JavaScript。有一些工具可以帮助您将模型编译成 JavaScript,例如 TensorFlow.jsSynapticbrain.js。然而,JavaScript 速度较慢,并且作为编程语言,在复杂逻辑(如从数据中提取特征)方面的能力有限。

更为有前景的方法是 WebAssembly(WASM)。WASM 是一个开放标准,允许您在浏览器中运行可执行程序。在您用 scikit-learn、PyTorch、TensorFlow 或其他任何框架构建了模型之后,不再需要将模型编译为特定硬件上的运行代码,而是可以将模型编译为 WASM。您将得到一个可执行文件,可以直接在 JavaScript 中使用。

WASM 是我在过去几年看到的最激动人心的技术趋势之一。它具有高性能,易于使用,并且其生态系统像野火般快速增长。⁵¹ 截至 2021 年 9 月,全球 93% 的设备已支持它。⁵²

WASM 的主要缺点是,因为它在浏览器中运行,速度较慢。尽管 WASM 已经比 JavaScript 快得多,但与在设备上原生运行的代码(如 iOS 或 Android 应用程序)相比,它仍然较慢。Jangda 等人的研究显示,编译为 WASM 的应用程序在 Firefox 上比本机应用程序慢平均 45%(在 Chrome 上为 55%)。⁵³

摘要

恭喜,您已经完成了本书中可能是最技术性最强的一章!这章技术性很强,因为部署机器学习模型是一项工程挑战,而不是机器学习挑战。

我们讨论了部署模型的不同方式,比较了在线预测与批处理预测,以及边缘机器学习与云端机器学习。每种方式都有其自己的挑战。在线预测使您的模型更能响应用户不断变化的偏好,但您需要担心推理延迟。批处理预测是一种解决方案,用于当您的模型生成预测时间太长时,但它会使您的模型不够灵活。

同样地,在云上进行推理设置很容易,但随着网络延迟和云成本的增加,这种做法变得不切实际。在边缘进行推理需要具有足够计算能力、内存和电池的边缘设备。

然而,我认为这些挑战大多是由 ML 模型运行硬件的限制造成的。随着硬件变得更加强大并优化用于机器学习,我相信 ML 系统将过渡到在设备上进行在线预测,如图 7-16 所示。

图 7-16. 随着硬件变得更加强大,机器学习模型将迁移到在线和边缘

我曾认为 ML 项目在模型部署后就完成了,但我希望在本章中已经清楚地表明,我严重错误了。将模型从开发环境移到生产环境会产生全新的一系列问题。首先是如何保持模型在生产中的运行。在下一章中,我们将讨论如何监控模型在生产中可能出现的问题,并尽快解决这些问题。

¹ 我们将在第十章详细介绍开发环境。

² 我们将在第九章深入讨论容器。

³ 斯坦福大学的 CS 329S:机器学习系统设计,你可以在 YouTube 上查看项目演示。

⁴ 请参见“数据格式”部分中关于“数据序列化”的讨论。

⁵ Ville Tuulos,《Netflix 的以人为中心的机器学习基础设施》,InfoQ,2018,视频,49:11,https://oreil.ly/j4Hfx

⁶ Wayne Cunningham,《Uber 科学:在 Uber 推动机器学习》,Uber Engineering Blog,2019 年 9 月 10 日,https://oreil.ly/WfaCF

⁷ Daniel Papasian 和 Todd Underwood,《OpML '20——机器学习的问题:一个大型 ML 管道的十年故障回顾》(2020 年),视频,19:06,https://oreil.ly/HjQm0

⁸ Lucas Bernardi, Themistoklis Mavridis 和 Pablo Estevez,《Booking.com 的 150 个成功机器学习模型:从 KDD '19 的学习经验》(2019 年 7 月),1743–51,https://oreil.ly/Ea1Ke

⁹ “2021 年企业机器学习趋势”,Algorithmia,https://oreil.ly/9kdcw

¹⁰ 我们将在第八章进一步讨论数据分布的变化。

¹¹ Christopher Null,《10 家在 DevOps 领域表现突出的公司》,TechBeacon,2015,https://oreil.ly/JvNwu

¹² Qian Yu,《微博中使用 Flink 进行机器学习》,QCon 2019,视频,17:57,https://oreil.ly/RcTMv

¹³ Josh Wills,《机器学习模型的仪器化、可观察性与监控》,InfoQ 2019,https://oreil.ly/5Ot5m

¹⁴ “开发者调查结果”,Stack Overflow,2019,https://oreil.ly/guYIq

¹⁵ David Curry,《Grubhub 的收入和使用统计数据(2022 年)》,Business of Apps,2022 年 1 月 11 日,https://oreil.ly/jX43M;“2011 至 2020 年全球每天 Grubhub 订单的平均数量”,Statista,https://oreil.ly/Tu9fm

¹⁶ 在这种情况下,服务的入口点 URL 是你的 ML 模型预测服务。

¹⁷ 如果有新用户加入,你可以给他们一些通用的建议。

¹⁸ Shuyi Chean 和 Fabian Hueske,《使用 Apache Flink 在 Uber 实现批处理与流处理的流式 SQL 统一》,InfoQ,https://oreil.ly/XoaNu;Yu,《微博中使用 Flink 进行机器学习》,QCon 2019,视频,17:57,https://oreil.ly/RcTMv

¹⁹ Yu Cheng, Duo Wang, Pan Zhou, and Tao Zhang,“深度神经网络模型压缩和加速综述,”arXiv,2020 年 6 月 14 日,https://oreil.ly/1eMho

²⁰ Max Jaderberg, Andrea Vedaldi, and Andrew Zisserman,“用低秩扩展加速卷积神经网络,”arXiv,2014 年 5 月 15 日,https://oreil.ly/4Vf4s

²¹ Forrest N. Iandola, Song Han, Matthew W. Moskewicz, Khalid Ashraf, William J. Dally, and Kurt Keutzer,“SqueezeNet:具有 50 倍参数和<0.5MB 模型尺寸的 AlexNet 级准确性,”arXiv,2016 年 11 月 4 日,https://oreil.ly/xs3mi

²² Andrew G. Howard, Menglong Zhu, Bo Chen, Dmitry Kalenichenko, Weijun Wang, Tobias Weyand, Marco Andreetto, and Hartwig Adam,“MobileNets:移动视觉应用中高效的卷积神经网络,”arXiv,2017 年 4 月 17 日,https://oreil.ly/T84fD

²³ Geoffrey Hinton, Oriol Vinyals, and Jeff Dean,“在神经网络中提炼知识,”arXiv,2015 年 3 月 9 日,https://oreil.ly/OJEPW

²⁴ Victor Sanh, Lysandre Debut, Julien Chaumond, and Thomas Wolf,“DistilBERT,BERT 的精炼版本:更小、更快、更便宜和更轻,”arXiv,2019 年 10 月 2 日,https://oreil.ly/mQWBv

²⁵ 因此得名“修剪”。

²⁶ Jonathan Frankle and Michael Carbin,“彩票假设:找到稀疏、可训练的神经网络,”ICLR 2019,https://oreil.ly/ychdl

²⁷ Davis Blalock, Jose Javier Gonzalez Ortiz, Jonathan Frankle, and John Guttag,“神经网络修剪的现状是什么?”arXiv,2020 年 3 月 6 日,https://oreil.ly/VQsC3

²⁸ Zhuang Liu, Mingjie Sun, Tinghui Zhou, Gao Huang, and Trevor Darrell,“重新思考网络修剪的价值,”arXiv,2019 年 3 月 5 日,https://oreil.ly/mB4IZ

²⁹ Michael Zhu and Suyog Gupta,“是否修剪:探索模型压缩的有效性,”arXiv,2017 年 11 月 13 日,https://oreil.ly/KBRjy

³⁰ Matthieu Courbariaux、Yoshua Bengio 和 Jean-Pierre David,《BinaryConnect: 在传播过程中使用二进制权重训练深度神经网络》,arXiv,2015 年 11 月 2 日,https://oreil.ly/Fwp2G;Mohammad Rastegari、Vicente Ordonez、Joseph Redmon 和 Ali Farhadi,《XNOR-Net: 使用二进制卷积神经网络进行 ImageNet 分类》,arXiv,2016 年 8 月 2 日,https://oreil.ly/gr3Ay

³¹ Alan Boyle、Taylor Soper 和 Todd Bishop,《独家报道:苹果收购 Xnor.ai,保罗·艾伦的 AI2 公司分拆的边缘 AI,交易价在 2 亿美元左右》,GeekWire,2020 年 1 月 15 日,https://oreil.ly/HgaxC

³² 截至 2020 年 10 月,TensorFlow 的量化感知训练实际上并不会训练使用较低比特位的模型权重,而是收集统计信息以用于训练后的量化。

³³ Chip Huyen、Igor Gitman、Oleksii Kuchaiev、Boris Ginsburg、Vitaly Lavrukhin、Jason Li、Vahid Noroozi 和 Ravi Gadde,《混合精度训练用于自然语言处理和语音识别的 OpenSeq2Seq》,NVIDIA Devblogs,2018 年 10 月 9 日,https://oreil.ly/WDT1l。这是我的文章!

³⁴ Shibo Wang 和 Pankaj Kanwar,《BFloat16: 云 TPU 高性能的秘密》,Google Cloud Blog,2019 年 8 月 23 日,https://oreil.ly/ZG5p0

³⁵ Itay Hubara、Matthieu Courbariaux、Daniel Soudry、Ran El-Yaniv 和 Yoshua Bengio,《量化神经网络:使用低精度权重和激活函数训练神经网络》,机器学习研究杂志 18 (2018): 1–30;Benoit Jacob、Skirmantas Kligys、Bo Chen、Menglong Zhu、Matthew Tang、Andrew Howard、Hartwig Adam 和 Dmitry Kalenichenko,《神经网络的量化和训练:仅使用整数算术推理的高效率》,arXiv,2017 年 12 月 15 日,https://oreil.ly/sUuMT

³⁶ Quoc Le 和 Kip Kaehler,《我们如何扩展 Bert 以在 CPU 上提供每天 10 亿次以上的请求服务》,Roblox,2020 年 5 月 27 日,https://oreil.ly/U01Uj

³⁷ Amir Efrati 和 Kevin McLaughlin,《随着 AWS 使用量飙升,企业对云账单感到惊讶》,The Information,2019 年 2 月 25 日,https://oreil.ly/H9ans;Mats Bauer,《Netflix 每月支付亚马逊网络服务多少钱?》,Quora,2020 年,https://oreil.ly/HtrBk

³⁸ “2021 年云成本报告”,Anodot,https://oreil.ly/5ZIJK

³⁹ “测试 Firebase 和 Cloud Run 耗资 72K 美元并且几乎破产”,《黑客新闻》,2020 年 12 月 10 日,https://oreil.ly/vsHHC; “如何在 Azure 中用一个点击烧掉最多的钱”,《黑客新闻》,2020 年 3 月 29 日,https://oreil.ly/QvCiI。我们将在“公共云与私有数据中心”一节中详细讨论公司如何应对高昂的云费用。

⁴⁰ “过去 18 个月中,近 80%的公司经历了云数据泄露”,《安全》,2020 年 6 月 5 日,https://oreil.ly/gA1am

⁴¹ 查看第 53 页幻灯片,CS 329S《部署 - 预测服务》讲座,2022 年,https://oreil.ly/cXTou

⁴² “2010 年至 2025 年全球物联网(IoT)和非 IoT 活动设备连接情况”,Statista,oreil.ly/BChLN

⁴³ Tianqi Chen, Thierry Moreau, Ziheng Jiang, Lianmin Zheng, Eddie Yan, Meghan Cowan, Haichen Shen 等,“TVM:一个自动化的端到端优化深度学习编译器”,arXiv,2018 年 2 月 12 日,https://oreil.ly/vGnkW

⁴⁴ 如今,许多 CPU 都有向量指令,一些 GPU 有张量核心,这是二维的。

⁴⁵ Shoumik Palkar, James Thomas, Deepak Narayanan, Pratiksha Thaker, Rahul Palamuttam, Parimajan Negi, Anil Shanbhag 等,“评估数据分析应用中端到端优化的效果:以 Weld 为例”,《VLDB Endowment 会议论文集》第 11 卷第 9 期(2018 年):1002–15,https://oreil.ly/ErUIo

⁴⁶ 关于循环分块的有用可视化,请参见 Colfax Research 的演示幻灯片 33,来自他们的《Intel 架构编程与优化:实战工作坊》系列中的第十部分“访问缓存和内存”。整个系列可以在https://oreil.ly/hT1g4找到。

⁴⁷ Matthias Boehm,“ML 系统架构 04 运算符融合和运行时适应”,格拉茨科技大学,2019 年 4 月 5 日,https://oreil.ly/py43J

⁴⁸ Shashank Prasanna, Prethvi Kashinkunti, 和 Fausto Milletari,“TensorRT 3:更快的 TensorFlow 推理和 Volta 支持”,NVIDIA Developer,2017 年 12 月 4 日,https://oreil.ly/d9h98。CBR 表示“卷积、偏置和 ReLU”。

⁴⁹ 这也是为什么你不应该过分关注基准测试结果,比如MLPerf 的结果。在一种类型的硬件上非常快速运行的流行模型,并不意味着任意模型都会在该硬件上非常快速运行。可能只是这个模型被过度优化了。

⁵⁰ Chen 等人,“TVM: 用于深度学习的自动化端到端优化编译器”。

⁵¹ Wasmer,https://oreil.ly/dTRxr;Awesome Wasm,https://oreil.ly/hlIFb

⁵² Can I Use _____?,https://oreil.ly/slI05

⁵³ Abhinav Jangda, Bobby Powers, Emery D. Berger, and Arjun Guha,“不要那么快:分析 WebAssembly 与原生代码的性能”,USENIX,https://oreil.ly/uVzrX

第八章:数据分布转移和监控

让我们从一位高管告诉我的一个故事开始,许多读者可能会有共鸣。大约两年前,他的公司聘请了一家咨询公司开发一个 ML 模型,帮助他们预测下周他们需要多少杂货商品,以便他们可以相应地补货。咨询公司花了六个月时间开发这个模型。当咨询公司交付模型时,他的公司部署并且对其表现非常满意。他们最终可以向他们的投资者夸耀他们是一家 AI 驱动的公司。

然而,一年后,他们的数据降低了。一些商品的需求被持续高估,导致额外的商品过期。同时,一些商品的需求一直被低估,导致销售额损失¹。最初,他的库存团队手动更改了模型的预测,以纠正他们注意到的模式,但最终,模型的预测变得如此糟糕,以至于他们无法再使用它。他们有三种选择:支付同一家咨询公司天价来更新模型,支付另一家咨询公司更多的钱,因为这家公司需要时间来适应,或者雇佣内部团队来维护模型。

他的公司通过艰难的经历学到了一条重要的教训,这也是这个行业其他公司正在发现的:部署模型并不是流程的终点。模型在生产环境中的性能会随时间降低。一旦模型被部署,我们仍然需要不断地监控其性能,以便检测问题并部署更新来解决这些问题。

在本章和下一章中,我们将涵盖必要的主题,以帮助您保持模型在生产中。我们将首先介绍在开发阶段表现出色的 ML 模型在生产中失败的原因。然后,我们将深入了解一个特别普遍和棘手的问题,影响几乎所有生产中的 ML 模型:数据分布转移。当生产环境中的数据分布与模型在训练期间暴露的数据分布不同时出现数据分布转变。我们将继续介绍如何监控分布转移。在下一章中,我们将介绍如何不断更新你的生产模型以适应数据分布的变化。

ML 系统失败的原因

在确定 ML 系统失败的原因之前,让我们简要讨论一下什么是 ML 系统失败。当系统的一个或多个预期被违反时,就会发生失败。在传统软件中,我们主要关心系统的操作预期:系统是否在预期的操作指标范围内执行其逻辑,例如延迟和吞吐量。

对于一个机器学习系统,我们关注其操作指标和 ML 性能指标。例如,考虑一个英法机器翻译系统。其操作预期可能是,在给定的英文句子后,系统在一秒内返回一个法语翻译。其 ML 性能预期是,返回的翻译在 99%的情况下准确翻译原始的英文句子。

如果你输入一句英文句子到系统中,却没有得到翻译,第一个预期被违反,这就是系统故障。

如果你得到的翻译不正确,并不一定是系统故障,因为准确性预期允许一定的误差。但是,如果你不断输入不同的英文句子到系统中,却一直得到错误的翻译,那么第二个预期被违反,这就成为了系统故障。

操作预期违规较易检测,通常会伴随操作故障,如超时、网页的 404 错误、内存溢出或段错误。然而,机器学习性能预期的违规检测则较难,因为这需要在生产环境中测量和监控机器学习模型的性能。在先前的英法机器翻译系统示例中,如果我们不知道正确的翻译结果,要检测返回翻译是否正确 99%的难度很大。有很多例子表明,谷歌翻译的严重错误翻译被用户使用,是因为用户不知道这些是错误的翻译。因此,我们说机器学习系统经常会默默地失败。

要有效地检测和修复生产中的机器学习系统故障,了解为何一个在开发过程中表现良好的模型在生产环境中会失败是很有用的。我们将分析两种类型的故障:软件系统故障和 ML 特定故障。

软件系统故障

软件系统故障是非机器学习系统可能发生的故障。以下是一些软件系统故障的示例:

依赖项故障

你的系统依赖的软件包或代码库出现问题,导致你的系统崩溃。当依赖项由第三方维护时,这种故障模式很常见,特别是如果维护该依赖项的第三方不再存在。²

部署失败

由于部署错误引起的故障,比如你意外地部署了旧版本的模型二进制文件,而不是当前版本,或者你的系统没有正确的权限来读取或写入某些文件。

硬件故障

当你用来部署模型的硬件,如 CPU 或 GPU,表现不如预期时,比如你使用的 CPU 可能会过热并且出现故障。³

停机或崩溃

如果系统的某个组件在某个服务器上运行,例如 AWS 或托管服务,而该服务器宕机,您的系统也将宕机。

有些失败并非特定于机器学习,这并不意味着对机器学习工程师来说它们不重要。在 2020 年,谷歌的两位机器学习工程师 Daniel Papasian 和 Todd Underwood 分析了 96 个谷歌大型机器学习管道出现故障的案例。他们回顾了过去 15 年的数据,以确定造成这些故障的原因,发现其中 60 个故障并非直接与机器学习相关。⁴ 大多数问题与分布式系统有关,例如工作流调度器或编排器出错,或者与数据管道有关,例如多个来源的数据连接错误或使用了错误的数据结构。

处理软件系统故障不需要机器学习技能,而是传统的软件工程技能,并且处理它们超出了本书的范围。由于传统软件工程技能在部署机器学习系统中的重要性,机器学习工程大多是工程,而不是机器学习。⁵ 对于有兴趣从软件工程角度学习如何使机器学习系统可靠的读者,我强烈推荐由 Todd Underwood 等人撰写的书籍 可靠的机器学习,由 O'Reilly 出版。

软件系统故障普遍的原因是,由于工业中对机器学习的采用仍处于起步阶段,围绕机器学习生产的工具和最佳实践尚不完善或未标准化。然而,随着机器学习生产工具和最佳实践的成熟,有理由相信软件系统故障的比例将减少,而机器学习特定故障的比例将增加。

机器学习特定的故障

机器学习特定的故障是指仅限于机器学习系统的故障。例如包括数据收集和处理问题,超参数不佳,训练管道中的更改未能正确复制到推断管道中,反之亦然,导致模型性能随时间下降的数据分布变化,边缘情况以及退化反馈循环等。

本章中,我们将重点讨论处理特定于机器学习的故障。尽管这些故障占比很小,但它们可能比非机器学习故障更危险,因为它们很难检测和修复,并且可能导致机器学习系统根本无法使用。在第四章中,我们详细讨论了数据问题,第六章讨论了超参数调整,以及第七章讨论了训练和推断使用两个独立管道的危险。在本章中,我们将讨论模型部署后出现的三个新但非常常见的问题:生产数据与训练数据不同,边缘案例和退化反馈循环。

生产数据与训练数据不同

当我们说一个机器学习模型从训练数据中学习时,意味着模型学习训练数据的基础分布,目标是利用这种学习到的分布为未见过的数据生成准确的预测——也就是在训练过程中没有见过的数据。我们将在“数据分布变化”章节中数学上详细说明这意味着什么。当模型能够为未见数据生成准确的预测时,我们称这个模型“对未见数据泛化良好”⁶。我们用来评估模型在开发过程中的测试数据,应该代表未见过的数据,而模型在测试数据上的表现应该给我们一个模型泛化能力的概念。

我在机器学习课程中学到的第一件事就是训练数据和未见数据来自相似的分布是至关重要的。假设未见数据来自与训练数据分布“稳态”的相同分布。如果未见数据来自不同的分布,那么模型可能无法很好地泛化。⁷

这种假设在大多数情况下都是不正确的,原因有两个。首先,真实世界数据的基础分布不太可能与训练数据的基础分布相同。精心策划一个能够准确代表模型在生产中遇到的数据的训练数据集,结果证明非常困难。⁸ 真实世界的数据是多面的,在许多情况下几乎是无限的,而训练数据是有限的,并且受到创建和处理数据集期间可用的时间、计算和人力资源的限制。存在许多不同的选择和抽样偏差,正如第四章中讨论的那样,这些偏差可能发生并使真实世界数据与训练数据偏离。这种偏差可能仅仅是真实世界数据使用了一种不同的表情符号编码类型。这种类型的偏差导致了一种常见的失败模式,即训练-服务偏差:一个在开发中表现良好但在部署时表现不佳的模型。

其次,现实世界并非静止不变的。事物变化,数据分布变化。2019 年,人们搜索武汉时,可能想获取旅行信息,但自从 COVID-19 以来,当人们搜索武汉时,他们可能想了解 COVID-19 起源的地方。另一种常见的失败模式是,一个模型在首次部署时表现良好,但随着时间推移,随着数据分布的变化,其性能会下降。这种失败模式需要在模型继续投入使用期间进行持续监测和检测。

当我以 COVID-19 作为导致数据转移的例子时,一些人有这样的印象,即数据转移只发生在不寻常的事件中,这意味着它们并不经常发生。数据转移随时随地发生,突然、逐渐或季节性地。它们可能会突然发生,因为特定事件,例如你的现有竞争对手改变了他们的定价策略,你必须对价格预测进行更新,或者当你在新区域推出产品时,或者当一位名人提到你的产品时,引起新用户的激增,等等。它们可能会逐渐发生,因为社会规范、文化、语言、趋势、行业等随时间的推移而变化。它们也可能由于季节变化而发生,例如在冬季寒冷多雪时,人们更有可能要求共乘服务,而在春季则不太可能。

由于机器学习系统的复杂性及其在部署过程中的糟糕实践,监控仪表板上可能看起来像是数据转移的大部分情况都是由内部错误引起的,⁹ 例如数据管道中的错误、错误输入的缺失值、训练和推理期间提取的特征不一致、使用错误数据子集统计标准化的特征、错误的模型版本,或者应用界面中的错误导致用户改变其行为。

由于这是几乎所有机器学习模型都会受到影响的错误模式,我们将在章节 “数据分布变化” 中详细讨论这个问题。

边缘情况

想象一下存在一辆自动驾驶汽车,99.99% 的时间能够安全驾驶,但剩下的 0.01% 的时间可能会发生灾难性事故,导致您永久受伤甚至死亡¹⁰。您会使用这样的车吗?

如果你倾向于选择不使用,那么你并不孤单。一个在大多数情况下表现良好但在少数情况下失败的机器学习模型,如果这些失败造成灾难性后果,可能就无法使用。因此,主要的自动驾驶汽车公司正在专注于使其系统在边缘情况下工作¹¹。

边缘情况是那些极端的数据样本,它们会导致模型做出灾难性的错误。尽管边缘情况通常指的是从相同分布抽取的数据样本,如果在您的模型表现不佳的数据样本数量突然增加,这可能表明基础数据分布发生了变化。

自动驾驶车辆经常被用来说明边缘情况如何阻止机器学习系统的部署。但这也适用于任何安全关键应用,例如医疗诊断、交通控制、电子发现¹² 等等。对于非安全关键应用也可能如此。想象一下,一个客服聊天机器人能够对大部分请求给出合理的回应,但有时却会输出极端种族主义或性别歧视内容。这种机器人将成为任何希望使用它的公司的品牌风险,因此无法使用。

退化反馈循环

在章节 “自然标签” 中,我们讨论了反馈循环,即从显示预测到提供预测反馈的时间。该反馈可以用来提取自然标签以评估模型的性能并训练模型的下一个迭代。

退化反馈循环 可能会发生在预测本身影响反馈的情况下,进而影响模型的下一个迭代。更正式地说,当系统的输出用于生成系统未来的输入时,就会产生退化反馈循环,这反过来又会影响系统未来的输出。在机器学习中,系统的预测可以影响用户如何与系统交互,而用户与系统的互动有时被用作相同系统的训练数据,这样就可能发生退化反馈循环,导致意想不到的后果。退化反馈循环在涉及用户自然标签的任务中特别常见,如推荐系统和广告点击率预测。

要让这个更具体,想象你建立了一个系统,推荐用户可能喜欢的歌曲。系统排名较高的歌曲会首先展示给用户。因为它们首先展示,用户点击率更高,这使得系统更加确信这些推荐是好的。刚开始时,两首歌曲 A 和 B 的排名可能只有微小差异,但因为 A 的初始排名稍高,所以它在推荐列表中排名较高,使得用户更多地点击 A,这进一步提高了系统对 A 的排名。过一段时间后,A 的排名比 B 的高得多。¹³ 退化反馈循环是为什么流行电影、书籍或歌曲保持流行的原因之一,这使得新物品难以打入流行列表。这种场景在生产中非常常见,并且得到了大量研究。它有许多不同的名称,包括“曝光偏差”、“流行偏差”、“过滤泡沫”以及有时称为“回声室”。

下面再举一个例子来说明退化反馈循环的危险性。想象构建一个简历筛选模型,预测某个简历的人是否合格。模型发现特征 X 能够准确预测某人是否合格,因此推荐具有特征 X 的简历。你可以用“毕业于斯坦福大学”、“在谷歌工作过”或“男性身份认同”等特征替换 X。招聘人员只会面试模型推荐的简历,这意味着他们只会面试具有特征 X 的候选人,公司也只会雇佣具有特征 X 的候选人。这反过来又使模型对特征 X 给予更多权重。¹⁴ 了解模型如何进行预测,例如通过测量模型每个特征的重要性来检测这种情况下对特征 X 的偏见,可以帮助识别这种偏见。

如果放任不管,退化反馈循环最多会导致您的模型表现不佳。在最坏的情况下,它们可能会持续放大数据中嵌入的偏见,比如偏向没有特征 X 的候选人。

检测退化反馈循环

如果退化反馈循环如此糟糕,那么我们如何知道系统中的反馈循环是否是退化的呢?当系统离线时,很难检测到退化反馈循环。退化循环是由用户反馈导致的,而在系统上线之前(即部署给用户之前),系统不会有用户。

对于推荐系统的任务,可以通过在系统离线时测量其输出的流行度多样性来检测退化反馈环。一个物品的流行度可以根据过去与其互动的次数(如查看、点赞、购买等)来衡量。所有物品的流行度可能会遵循长尾分布:少数物品会被大量互动,而大多数物品几乎不被互动。Brynjolfsson et al.(2011)、Fleder and Hosanagar(2009)和Abdollahpouri et al.(2019)提出的诸如聚合多样性长尾物品平均覆盖率等各种度量标准可以帮助你衡量推荐系统输出的多样性。¹⁵ 低分数意味着你的系统输出是同质化的,这可能是由流行度偏见引起的。

在 2021 年,Chia 等人更进一步,提出了根据流行度来衡量点击率的方法。他们首先根据物品的流行度将其分成不同的桶 —— 比如,桶 1 包括那些与用户互动少于 100 次的物品,桶 2 包括那些与用户互动超过 100 次但少于 1,000 次的物品等等。然后,他们针对每个桶测量了推荐系统的预测准确度。如果一个推荐系统在推荐流行物品方面要比推荐不那么流行的物品要好得多,那么它很可能存在流行度偏见。¹⁶ 一旦你的系统投入生产,并且你注意到它的预测随时间变得更加同质化,那么它很可能存在退化反馈环。

纠正退化反馈环

因为退化反馈环是一个常见问题,所以有许多提出的方法来纠正它们。在本章中,我们将讨论两种方法。第一种方法是使用随机化,第二种方法是使用位置特征。

我们已经讨论过,退化反馈环可以导致系统的输出随时间变得更加同质化。在预测中引入随机化可以减少它们的同质性。在推荐系统的情况下,我们不再只向用户展示系统为其排名较高的物品,而是展示随机物品,并利用用户的反馈来确定这些物品的真实质量。这是 TikTok 采用的方法。每个新视频都会随机分配一个初始的流量池(最多可以达到数百次展示)。这个流量池用于评估每个视频的无偏质量,以确定它是否应该转移到更大的流量池或被标记为无关紧要。¹⁷

随机化已经显示可以提高多样性,但以用户体验为代价。¹⁸ 展示给用户完全随机的项目可能会导致用户对我们的产品失去兴趣。智能的探索策略,例如本节讨论的“作为探索策略的情境臂展”,可以帮助增加项目的多样性,同时可以接受的预测准确性损失。Schnabel 等人使用少量随机化和因果推断技术来估计每首歌的无偏值。¹⁹ 他们能够展示该算法能够纠正推荐系统,使得推荐对创作者更公平。

我们还讨论了退化反馈循环是由用户对预测的反馈引起的,而用户对预测的反馈则基于它们所展示的位置而偏倚。考虑前述推荐系统的例子,每次向用户推荐五首歌曲。你会发现,排名靠前的推荐歌曲比其他四首更有可能被点击。你不确定你的模型是在选择顶部歌曲方面异常出色,还是只要推荐在顶部,用户就会点击任何一首歌。

如果预测所显示的位置以任何方式影响其反馈,你可能希望使用位置特征来编码位置信息。位置特征可以是数值型的(例如,位置为 1、2、3...)或布尔型的(例如,是否预测显示在第一位置)。请注意,“位置特征”与第五章提到的“位置嵌入”是不同的。

这是一个简单的示例,展示如何使用位置特征。在训练过程中,你将“是否推荐为首位”的特征添加到你的训练数据中,如表 8-1 所示。这个特征允许你的模型学习,成为顶部推荐对歌曲被点击的可能性有多大。

表 8-1. 将位置特征添加到你的训练数据中以减少退化反馈循环

ID 歌曲 风格 年份 艺术家 用户 首位位置 点击
1 肤浅 流行 2020 Lady Gaga listenr32
2 好的氛围 放克 2019 放克霸主 listenr32
3 击败它 摇滚 1989 迈克尔·杰克逊 fancypants
4 在绽放中 摇滚 1991 尼尔瓦纳 fancypants
5 肤浅 流行 2020 Lady Gaga listenr32

在推断过程中,你想预测用户是否会点击一首歌,无论歌曲推荐在何处,因此你可能希望将首位位置特征设置为假。然后你查看模型对每位用户各种歌曲的预测,并可以选择展示每首歌曲的顺序。

这只是一个简单的例子,因为仅凭这样做可能不足以对抗退化的反馈循环。更复杂的方法是使用两个不同的模型。第一个模型预测用户会看到并考虑推荐的概率,考虑推荐显示的位置。第二个模型则预测用户在看到并考虑了推荐后点击该项的概率。第二个模型完全不考虑位置。

数据分布偏移

在前面的部分,我们讨论了机器学习系统失败的常见原因。在本节中,我们将重点关注一种特别棘手的失败原因:数据分布偏移,或简称数据偏移。数据分布偏移指的是在监督学习中,模型所处理的数据随时间变化,导致模型的预测随着时间推移变得不太准确的现象。模型训练所使用的数据分布称为源分布。模型推断时所使用的数据分布称为目标分布

尽管关于数据分布偏移的讨论在近年来随着机器学习在工业界的广泛应用而变得常见,但从数据中学习的系统中数据分布偏移的研究早在 1986 年就已经开始了。²⁰ 还有一本关于数据集分布偏移的书籍,《数据集偏移在机器学习中的应用》由 Quiñonero-Candela 等人撰写,2008 年由 MIT 出版社出版。

数据分布偏移类型

虽然数据分布偏移通常与概念漂移、协变量偏移以及偶尔的标签偏移可以互换使用,但这些是数据偏移的三种不同子类型。请注意,关于不同类型的数据偏移的讨论充满数学内容,并且大多数情况下从研究的角度来看才有用:开发有效的算法来检测和处理数据偏移需要理解这些偏移的原因。在生产环境中,当遇到分布偏移时,数据科学家通常不会停下来思考是哪种类型的偏移。他们更关心如何处理这种偏移。如果你觉得这部分内容过于密集,可以直接跳到“一般数据分布偏移”一节。

要理解概念漂移、协变量偏移和标签偏移的含义,我们首先需要定义一些数学符号。让我们将模型的输入称为X,输出称为Y。我们知道在监督学习中,训练数据可以看作是从联合分布P(X, Y)中抽取的样本集,而机器学习通常模拟的是P(Y|X)。这个联合分布P(X, Y)可以通过两种方式分解:

  • P(X, Y) = P(Y|X)P(X)

  • P(X, Y) = P(X|Y)P(Y)

P(Y|X) 表示给定输入的输出的条件概率,例如给定电子邮件内容的垃圾邮件的概率。P(X) 表示输入的概率密度。P(Y) 表示输出的概率密度。标签偏移、协变量偏移和概念漂移的定义如下:

协变量偏移

P(X) 变化时,但 P(Y|X) 保持不变。这指的是联合分布的第一个分解。

标签偏移

P(Y) 变化时,但 P(X|Y) 保持不变。这指的是联合分布的第二个分解。

概念漂移

P(Y|X) 变化时,但 P(X) 保持不变。这指的是联合分布的第一个分解。²¹

如果您觉得这很混乱,不要惊慌。我们将在以下部分中讨论示例,以说明它们之间的区别。

协变量偏移

协变量偏移 是研究最广泛的数据分布转移形式之一。²² 在统计学中,协变量是可以影响给定统计试验结果的独立变量,但不是直接感兴趣的。考虑您正在进行一个实验,以确定位置如何影响房价。房价变量是您的直接兴趣,但您知道房屋面积会影响价格,因此房屋面积是一个协变量。在监督学习中,标签是直接感兴趣的变量,而输入特征是协变量变量。

在数学上,协变量偏移是指 P(X) 变化,但 P(Y|X) 保持不变,这意味着输入的分布发生变化,但给定输入时输出的条件概率保持不变。

为了具体化这一点,考虑检测乳腺癌的任务。您知道乳腺癌的风险在 40 岁以上的女性中更高,²³ 因此您有一个名为“年龄”的输入变量。您的训练数据中可能有更多 40 岁以上的女性,而推断数据中可能没有这么多,因此您的训练和推断数据的输入分布不同。然而,对于给定年龄的示例,例如超过 40 岁,该示例具有乳腺癌的概率是恒定的。因此 P(Y|X),即给定年龄超过 40 岁的情况下有乳腺癌的概率是相同的。

在模型开发过程中,由于数据选择过程中的偏见,可能会发生协变量偏移,这可能是由于难以收集某些类别的示例造成的。例如,假设为了研究乳腺癌,您从妇女去检查乳腺癌的诊所中获得数据。由于医生鼓励 40 岁以上的人进行检查,您的数据被 40 岁以上的女性所主导。因此,协变量偏移与样本选择偏差问题密切相关。²⁴

协变量转移还可能由于训练数据人为修改而发生,以使您的模型更容易学习。如第四章讨论的那样,ML 模型很难从不平衡的数据集中学习,因此您可能希望收集更多罕见类别的样本,或者过采样罕见类别的数据,以使模型更容易学习这些罕见类别。

协变量转移还可能是由模型的学习过程引起的,尤其是通过主动学习。在第四章中,我们将主动学习定义为:不是随机选择样本来训练模型,而是根据某些启发式方法选择对该模型最有帮助的样本。这意味着训练输入分布被学习过程改变,使其与现实世界输入分布不同,协变量转移是其副产品²⁵。

在生产中,协变量转移通常发生在环境或应用程序使用方式发生重大变化时。假设您有一个模型,用于预测免费用户转化为付费用户的可能性。用户的收入水平是一个特征。您公司的市场部最近推出了一项吸引更富裕人群的广告活动,这导致用户的输入分布发生了变化,但对于给定收入水平用户的转化概率仍然保持不变。

如果您预先知道实际输入分布与训练输入分布的差异,您可以利用诸如重要性加权之类的技术来训练模型以适应实际数据。重要性加权包括两个步骤:估计实际输入分布与训练输入分布之间的密度比率,然后根据此比率对训练数据进行加权,并在加权数据上训练 ML 模型²⁶。

然而,由于我们无法预先知道现实世界中分布如何变化,因此很难提前训练模型,使其对新的、未知的分布具有鲁棒性。已经有研究试图帮助模型学习潜在变量的表示,这些变量在数据分布上是不变的²⁷,但我不清楚它们在工业界的采纳情况。

标签转移

标签转移,也称为先验转移、先验概率转移或目标转移,是指P(Y)变化,但P(X|Y)保持不变的情况。您可以将其视为输出分布发生变化但对于给定输出,输入分布保持不变的情况。

请记住,协变量漂移是指输入分布发生变化时。当输入分布改变时,输出分布也会改变,从而导致同时发生协变量漂移和标签漂移。考虑前面的乳腺癌例子中的协变量漂移。由于我们的训练数据中 40 岁以上的女性比推断数据中的要多,所以在训练期间,POSITIVE 标签的百分比更高。然而,如果你从训练数据中随机选择一个患有乳腺癌的 A 人和从测试数据中选择一个患有乳腺癌的 B 人,那么 A 和 B 患有乳腺癌的概率相同。这意味着P(X|Y),即患有乳腺癌的情况下年龄超过 40 岁的概率,是相同的。因此,这也是标签漂移的情况。

然而,并非所有的协变量漂移都会导致标签漂移。这是一个微妙的问题,所以我们将考虑另一个例子。想象一下,现在有一种预防性药物,每个女性都服用,可以帮助减少她们罹患乳腺癌的几率。对所有年龄段的女性来说,概率P(Y|X)都会降低,因此不再是协变量漂移的情况。然而,对于已经得了乳腺癌的人来说,年龄分布仍然保持不变,因此这仍然是标签漂移的情况。

由于标签漂移与协变量漂移密切相关,用于检测和适应标签漂移的方法与适应协变量漂移的方法类似。我们将在本章稍后讨论这些方法。

概念漂移

概念漂移,也被称为后验转移,是指输入分布保持不变,但是给定输入时输出的条件分布发生变化。你可以把它想象成“同样的输入,不同的输出”。想象一下,你负责一个根据房屋特征预测房价的模型。在 COVID-19 之前,旧金山的一个三居室公寓可能售价为$2,000,000。然而,在 COVID-19 爆发初期,许多人离开了旧金山,因此同样的公寓现在只需$1,500,000。因此,即使房屋特征的分布保持不变,给定其特征的房价的条件分布也发生了变化。

在许多情况下,概念漂移是周期性的或季节性的。例如,共享出行的价格在工作日和周末会有波动,航班票价在假期季节会上涨。公司可能会有不同的模型来处理周期性和季节性的漂移。例如,他们可能有一个模型来预测工作日的共享出行价格,另一个模型用于周末。

一般数据分布转移

尽管研究中并没有详细研究,但现实世界中存在其他类型的变化,这些变化仍然可能降低模型的性能。

一种是特征变化,例如添加新特征、删除旧特征或更改特征可能值的集合。²⁸ 例如,您的模型曾使用“年龄”特征的年份,但现在使用月份,因此该特征值的范围已发生漂移。有一次,我们的团队意识到,由于管道中的错误导致特征变成了 NaNs(“非数字”)而导致模型性能急剧下降。

标签架构变化 是指 Y 的可能值集合发生变化。与标签偏移不同,P(Y) 变化但 P(X|Y) 保持不变。标签架构变化则是 P(Y) 和 P(X|Y) 都发生变化。架构描述数据的结构,因此任务的标签架构描述该任务标签的结构。例如,将类映射到整数值的字典,如 {“正面”: 0, “负面”: 1},就是一个架构。

对于回归任务,标签架构变化可能是因为标签值的可能范围发生了变化。想象一下,您正在建立一个预测某人信用评分的模型。最初,您使用的信用评分系统范围从 300 到 850,但后来切换到一个范围从 250 到 900 的新系统。

在分类任务中,标签架构的变化可能是因为出现了新的类别。例如,假设您正在建立一个诊断疾病的模型,出现了一种新的需要诊断的疾病。类别还可以变得过时或更加精细。想象一下,您负责的情感分析模型用于分析提及您品牌的推文。最初,您的模型仅预测三种类别:正面、负面和中性。然而,您的市场部门意识到最有害的推文是愤怒的推文,因此他们希望将负面类别细分为两个类别:悲伤和愤怒。现在,您的任务不再有三个类别,而是四个类别。当类别数量发生变化时,您的模型结构可能会发生变化,²⁹ 您可能需要重新标记数据并从头开始训练模型。标签架构的变化在高基数任务中特别常见——即类别数量较多的任务,例如产品或文档分类。

没有规定只能同时发生一种类型的偏移。一个模型可能受到多种类型漂移的影响,这使得处理它们变得更加困难。

检测数据分布偏移

数据分布偏移只有在导致模型性能下降时才是问题。因此,第一个想法可能是在生产环境中监控模型的准确度相关指标——准确率、F1 分数、召回率、AUC-ROC 等——以查看它们是否发生了变化。“变化” 这里通常意味着“减少”,但如果我的模型准确率突然提高或者因我不知道的原因而显著波动,我会想要进行调查。

与准确性相关的指标通过将模型的预测与地面真实标签进行比较来工作。³⁰ 在模型开发过程中,您可以访问标签,但在生产中,您并不总是可以访问标签,即使可以,标签也会延迟,正如在“自然标签”部分中讨论的那样。在合理的时间窗口内获取标签将极大地帮助您了解模型的性能。

当无法获得或者地面真相标签延迟过长以至于无法使用时,我们可以监控其他感兴趣的分布。感兴趣的分布包括输入分布P(X)、标签分布P(Y)以及条件分布P(X|Y)和P(Y|X)。

虽然我们不需要知道地面真实标签Y来监控输入分布,但是监控标签分布以及两个条件分布都需要知道Y。在研究中,已经有努力理解和检测没有目标分布标签的标签偏移。Lipton 等人(2018 年)的一项努力是黑盒偏移估计。然而,在工业界,大多数漂移检测方法集中于检测输入分布的变化,特别是特征的分布,正如我们在本章中详细讨论的那样。

统计方法

在工业界,许多公司用于检测两个分布是否相同的简单方法是比较它们的统计数据,如最小值、最大值、均值、中位数、方差、各种分位数(如 5th、25th、75th 或 95th 分位数)、偏度、峰度等。例如,您可以计算推断过程中某个特征的中位数和方差,并将其与训练过程中计算的指标进行比较。截至 2021 年 10 月,即使TensorFlow Extended 内置的数据验证工具也仅使用汇总统计数据来检测训练和服务数据之间的偏差,以及不同训练数据日期之间的变化。这是一个很好的起点,但是这些指标远远不足以满足需求。³¹ 均值、中位数和方差只对均值/中位数/方差为有效总结的分布有用。如果这些指标存在显著差异,则推断分布可能已从训练分布中偏移。然而,如果这些指标相似,则不能保证没有偏移。

更复杂的解决方案是使用双样本假设检验,简称双样本检验。它是一种测试方法,用于确定两个总体(两组数据)之间的差异是否在统计上显著。如果差异在统计上显著,则说明这两个总体来自于两个不同的分布,而不是因为采样变异性导致的随机波动。如果你将昨天的数据视为源总体,今天的数据视为目标总体,并且它们在统计上是不同的,那么显然昨天和今天之间的数据分布已经发生了变化。

一个警告是,仅仅因为差异在统计上是显著的,并不意味着它在实际中很重要。然而,一个很好的经验法则是,如果你能从一个相对小的样本中检测到差异,那么这个差异可能是严重的。如果需要大量样本才能检测到差异,那么这个差异可能不值得担忧。

基本的双样本测试是科尔莫哥洛夫-斯米尔诺夫(Kolmogorov–Smirnov)检验,也被称为 K-S 或 KS 检验。[³²] 它是一种非参数统计检验,这意味着它不需要任何基础分布的参数即可工作。它不对基础分布做任何假设,这意味着它可以适用于任何分布。然而,KS 检验的一个主要缺点是它只能用于一维数据。如果你的模型预测和标签是一维的(标量数字),那么 KS 检验对于检测标签或预测的变化是有用的。然而,对于高维数据,它将不起作用,而特征通常是高维的。[³³] KS 检验可能也会很昂贵,并产生过多的假阳性警报。[³⁴]

另一个测试是最小二乘密度差异(Least-Squares Density Difference),这是一种基于最小二乘密度差异估计方法的算法。[³⁵] 还有 MMD(Maximum Mean Discrepancy,最大均值差异),是一种基于核的多变量双样本测试技术及其变体“学习核 MMD”(Liu 等人,2020)。MMD 在研究中很受欢迎,但截至撰写本书时,我不知道有任何公司在工业中使用它。Alibi Detect是一个优秀的开源软件包,其中包含许多漂移检测算法的实现,如图 8-2 所示。[³⁵]

因为双样本测试在低维数据上的表现往往比在高维数据上好得多,强烈建议在对数据进行双样本测试之前,先降低数据的维度。[³⁶]

Figure 8-2. Alibi Detect 实现的一些漂移检测算法。来源:项目的 GitHub 仓库的截图

用于检测转变的时间尺度窗口

不是所有类型的转变都相同 —— 有些比其他的更难检测。例如,转变发生的速率不同,突然变化比缓慢、逐渐的变化更容易检测到。³⁷ 转变也可以发生在两个维度上:空间或时间。空间转变是指跨越访问点发生的转变,比如你的应用程序获得了一群新用户或你的应用程序现在在不同类型的设备上提供服务。时间转变是随时间发生的转变。要检测时间转变,一种常见的方法是将输入数据处理为时间序列数据。³⁸

处理时间转变时,我们查看的数据的时间尺度窗口影响我们能够检测到的转变。如果您的数据具有每周循环,那么小于一周的时间尺度将无法检测到该周期。考虑 Figure 8-3 中的数据。如果我们使用从第 9 天到第 14 天的数据作为源分布,则第 15 天看起来像是一个转变。然而,如果我们使用从第 1 天到第 14 天的数据作为源分布,则第 15 天的所有数据点可能都是由同一分布生成的。正如这个例子所说明的那样,当转变被季节性变化所混淆时,检测时间转变是困难的。

Figure 8-3. 随时间分布是否漂移取决于指定的时间尺度窗口

在计算时间上的运行统计数据时,区分累积和滑动统计是很重要的。滑动统计是在单个时间尺度窗口内计算的,例如一个小时。累积统计则随着更多数据不断更新。这意味着,每个时间尺度窗口的开始,滑动准确性会被重置,而累积滑动准确性则不会。因为累积统计包含了前几个时间窗口的信息,它可能会掩盖特定时间窗口内发生的情况。Figure 8-4 展示了累积准确性如何掩盖在第 16 小时到第 18 小时之间的突然准确性下降的例子。

Figure 8-4. 累积准确性掩盖了在第 16 小时到第 18 小时之间的突然准确性下降。来源:根据MadeWithML的一幅图片修改而成

在时间空间中处理数据使得事情变得更加复杂,需要了解时间序列分析技术,如超出本书范围的时间序列分解。对于对时间序列分解感兴趣的读者,Lyft 工程团队有一个很好的案例研究,展示了他们如何分解时间序列数据以应对市场的季节性。

截至目前,许多公司使用训练数据的分布作为基础分布,并以每小时和每日等特定粒度监控生产数据的分布。³⁹ 时间窗口越短,就能越快地检测到数据分布的变化。然而,时间窗口过短可能导致像图 8-3 中的例子那样,误报偏移。

一些平台,特别是那些处理实时数据分析的平台,提供了合并操作,允许合并来自较短时间窗口的统计数据,以创建较大时间窗口的统计数据。例如,您可以按小时计算您关心的数据统计,然后将这些小时统计数据块合并为每日视图。

更先进的监控平台甚至尝试实现根本原因分析(RCA)功能,该功能自动分析各种时间窗口大小的统计数据,以便精确定位数据发生变化的时间窗口。⁴⁰

处理数据分布变化

公司如何处理数据偏移取决于其机器学习基础设施设置的复杂程度。在光谱的一端,我们有一些刚刚开始使用机器学习的公司,仍在努力将机器学习模型投入生产,因此他们可能尚未遇到数据偏移对他们造成灾难性影响的程度。然而,在未来的某个时刻——也许是三个月,也许是六个月——他们可能会意识到,他们最初部署的模型已经降级到比有益更为有害的地步。然后,他们将需要调整他们的模型以适应变化的分布,或者用其他解决方案替换它们。

同时,许多公司认为数据偏移是不可避免的,因此他们定期重新训练他们的模型——每月一次,每周一次或每天一次——无论偏移程度如何。如何确定重新训练模型的最佳频率是一项重要决策,许多公司仍然基于直觉而不是实验数据来确定。⁴¹ 我们将在第九章中更详细地讨论重新训练频率。

要使模型能够在生产环境中适应新的分布,有三种主要方法。第一种方法是目前主导研究的方法:使用大规模数据集训练模型。希望在这里的是,如果训练数据集足够大,模型将能够学习到如此全面的分布,以至于模型在生产中遇到的任何数据点可能都来自这个分布。

第二种方法,在研究中不太流行,是将训练好的模型调整到目标分布而无需新的标签。张等人(2013 年)使用因果解释以及条件和边缘分布的核嵌入,来纠正模型对协变量转移和标签转移的预测,而不使用目标分布的标签。类似地,赵等人(2020 年)提出了领域不变表示学习:一种无监督领域适应技术,可以学习对变化分布不变的数据表示。然而,这一研究领域深度未被探索,并未在工业界广泛应用。⁴⁴

第三种方法是当今工业界通常采用的方法:使用目标分布的标记数据重新训练您的模型。然而,重新训练模型并不那么简单。重新训练可能意味着从头开始重新训练模型,使用旧数据和新数据,或者在新数据上继续训练现有模型。后一种方法也称为微调。

如果您希望重新训练您的模型,有两个问题需要考虑。首先,是否从头开始训练模型(无状态重新训练)还是继续从上一个检查点训练模型(有状态训练)。其次,使用哪些数据:最近 24 小时的数据,最近一周的数据,最近 6 个月的数据,或者从数据开始漂移的时间点开始的数据。您可能需要进行实验以找出哪种重新训练策略对您最有效。⁴⁵

在本书中,我们使用“重新训练”来指代从头开始训练和微调两者。我们将在下一章节详细讨论重新训练策略。

熟悉数据转移文献的读者经常会看到数据转移与领域适应和迁移学习一同提及。如果您将分布视为一个领域,那么如何将您的模型调整到新的分布的问题类似于如何将您的模型适应不同领域的问题。

同样地,如果您考虑学习联合分布P(X, Y)作为一项任务,那么将一个在一个联合分布上训练的模型调整到另一个联合分布上可以被视为一种迁移学习形式。正如在第四章中讨论的那样,迁移学习是指一系列方法,其中为一个任务开发的模型被重复使用作为第二个任务模型的起点。不同之处在于,使用迁移学习时,您不会从头开始为第二个任务重新训练基础模型。然而,要使您的模型适应新的分布,可能需要从头开始重新训练您的模型。

处理数据分布的变化并不一定要在变化发生后才开始。可以设计系统使其更能抵抗变化。系统使用多个特征,不同的特征变化速度不同。考虑你正在构建一个模型来预测用户是否会下载一个应用程序。你可能会想要使用该应用在应用商店中的排名作为一个特征,因为排名较高的应用程序往往会被下载更多。然而,应用程序的排名变化非常快。你可能希望将每个应用程序的排名分为通用类别,例如前 10 名,11 至 100 名,101 至 1,000 名,1,001 至 10,000 名等。同时,应用程序的类别可能变化的频率要低得多,但它们可能没有那么强的预测用户是否下载该应用的能力。在选择模型特征时,你可能需要考虑特征性能和稳定性之间的权衡:某个特征可能在准确性方面非常好,但却很快恶化,迫使你更频繁地训练模型。

你可能还想设计你的系统,使其更容易适应变化。例如,像旧金山这样的大城市的房价可能会比亚利桑那州的农村地区变化得更快,因此为亚利桑那州农村地区提供服务的房价预测模型可能需要更新频率较低,而为旧金山提供服务的模型可能需要更频繁地更新。如果你使用同一个模型来服务这两个市场,你将不得不使用来自两个市场的数据来按照旧金山的要求更新模型。然而,如果你为每个市场使用单独的模型,你可以只在必要时更新它们。

在我们进入下一节之前,我想再次强调,不是所有在生产中模型性能下降的情况都需要机器学习的解决方案。今天许多机器学习的失败仍然是由人为错误造成的。如果你的模型失败是由人为错误造成的,你首先需要找出这些错误并修复它们。检测数据变化是困难的,但确定是什么导致了变化可能更加困难。

监控和可观察性

随着行业意识到机器学习系统可能出现许多问题,许多公司开始投资于监控和观察他们生产中的机器学习系统。

监控和可观测性有时候被交换使用,但它们是不同的。监控是指追踪、测量和记录不同的指标,这些指标可以帮助我们确定何时出现问题。可观测性意味着设置系统,使我们能够看到系统内部,帮助我们调查出现了什么问题。以这种方式设置系统的过程也称为“仪表化”。例如,将计时器添加到函数中、计算特征中的 NaN 数量、跟踪输入如何通过系统转换、记录异常事件(例如异常长的输入)等都是仪表化的示例。可观测性是监控的一部分。没有一定程度的可观测性,监控是不可能的。

监控关注的是指标。因为机器学习系统是软件系统,您需要监控的第一类指标是操作指标。这些指标旨在传达系统的健康状况。它们通常分为三个级别:运行系统的网络、运行系统的机器以及系统运行的应用程序。这些指标的例子包括延迟、吞吐量、模型在过去一分钟、一小时、一天接收到的预测请求数量,返回 2xx 代码的请求百分比、CPU/GPU 利用率、内存利用率等。无论您的机器学习模型有多好,如果系统停机,您将无法从中受益。

让我们举个例子。生产中软件系统最重要的特性之一是可用性——系统能够为用户提供合理性能的时间。这个特性由正常运行时间来衡量,即系统正常运行的时间百分比。用于确定系统是否正常运行的条件被定义在服务水平目标(SLOs)或服务水平协议(SLAs)中。例如,SLA 可以指定,如果服务的中位延迟小于 200 毫秒,99 分位数小于 2 秒,则服务被认为是正常运行的。

服务提供商可能会提供 SLA,其中规定了他们的正常运行时间保证,例如 99.99%的时间,如果未达到此保证,他们将向客户返还款项。例如,截至 2021 年 10 月,AWS EC2 服务提供每月至少 99.99%(四个九)的正常运行时间,如果月度正常运行时间低于此水平,他们将为您提供未来 EC2 付款的服务信用。99.99%的月度正常运行时间意味着服务每月只能停机超过 4 分钟,而 99.999%意味着每月只能停机 26 秒!

然而,对于机器学习系统来说,系统健康程度不仅限于系统的正常运行时间。如果您的机器学习系统运行正常但其预测结果是垃圾,用户是不会满意的。您还需要监控另一类指标,这些指标是专门用来告诉您机器学习模型健康状况的。

机器学习特定指标

在特定于 ML 的指标中,通常有四种要监控的物件:模型的与准确性相关的指标、预测、特征和原始输入。这些是在 ML 系统管道的四个不同阶段生成的物件,如图 8-5 所示。物件在管道中的深度越深,经历的转换就越多,这使得物件中的变化更有可能是由其中一个转换中的错误引起的。然而,物件经历的转换越多,它变得越有结构化,越接近您实际关心的指标,这使得监控变得更容易。我们将在接下来的章节详细讨论每一个物件。

图 8-5. 一个物件经历的转换越多,其变化由于这些转换中的错误引起的可能性就越大。

监控与准确性相关的指标

如果您的系统接收任何类型的用户反馈来对其进行预测 - 点击、隐藏、购买、赞成、反对、收藏、标记、分享等 - 您应该记录并跟踪它。有些反馈可以用于推断自然标签,然后用于计算模型的与准确性相关的指标。与准确性相关的指标是帮助您确定模型性能是否下降的最直接指标。

即使反馈不能直接用于推断自然标签,它也可以用来检测您的 ML 模型性能的变化。例如,当您正在构建一个系统来推荐用户在 YouTube 上接下来观看什么视频时,您不仅希望跟踪用户是否点击推荐的视频(点击率),还希望跟踪用户观看该视频的时间以及是否观看完毕(完成率)。如果随着时间的推移,点击率保持不变,但完成率下降,这可能意味着您的推荐系统正在变得更糟。⁴⁷

您还可以设计您的系统,以便收集用户的反馈。例如,Google 翻译允许用户对翻译进行赞成或反对,如图 8-6 所示。如果系统收到的反对票数突然增加,可能存在问题。这些反对票也可以用来指导标签过程,例如让人类专家为获得反对票的样本生成新的翻译,以训练他们模型的下一个迭代。

图 8-6. Google 翻译允许用户对翻译进行赞成或反对。这些投票将用于评估其翻译模型的质量,并指导标签过程。

监控预测

预测是最常见的监视对象。如果是回归任务,每个预测是一个连续值(例如房屋的预测价格),如果是分类任务,每个预测是一个离散值,对应于预测的类别。因为每个预测通常只是一个数字(低维度),预测很容易可视化,并且它们的摘要统计量易于计算和解释。

您可以监控预测以检测分布变化。因为预测是低维的,所以更容易计算两样本检验,以检测预测分布是否发生了变化。预测分布的变化也是输入分布变化的代理。假设从输入到输出映射的函数没有改变——模型的权重和偏差没有改变——那么预测分布的变化通常表明底层输入分布发生了变化。

您还可以监控预测,以发现任何异常情况,例如连续预测异常数量的 False。预测与地面实况标签之间可能存在长时间延迟,如第“自然标签”节所述。准确率相关指标的变化可能需要几天甚至几周才能变得明显,而预测所有 False 持续 10 分钟的模型可以立即检测到。

监控特征

行业中的机器学习监控解决方案专注于跟踪特征的变化,包括模型用作输入的特征以及从原始输入到最终特征的中间转换。特征监控具有吸引力,因为与原始输入数据相比,特征遵循预定义模式结构化良好。特征监控的第一步是特征验证:确保您的特征符合预期的模式。预期的模式通常是从训练数据生成或从常识中获取的。如果这些期望在生产环境中被违反,可能会导致基础分布的变化。例如,对于给定特征,可以检查以下内容:

  • 如果一个特征的最小值、最大值或中位数在可接受范围内

  • 如果一个特征的值满足正则表达式格式

  • 如果一个特征的所有值属于预定义集合

  • 如果一个特征的值始终大于另一个特征的值

因为特征通常组织成表格——每列表示一个特征,每行表示一个数据样本——特征验证也称为表测试或表验证。有些人称其为数据的单元测试。有许多开源库可以帮助您进行基本的特征验证,其中最常见的两个是Great ExpectationsDeequ,后者是 AWS 开发的。图 8-7 显示了 Great Expectations 内置的一些特征验证函数及其使用示例。

图 8-7. 一些 Great Expectations 内置特征验证函数及其使用示例。来源:改编自 Great Expectations GitHub 存储库中的内容。

除了基本的特征验证之外,您还可以使用双样本检验来检测特征或一组特征的基础分布是否发生了变化。由于特征或一组特征可能是高维的,您可能需要在执行测试之前降低它们的维度,这可能会降低测试的有效性。

在进行特征监控时有四个主要关注点:

一家公司可能在生产中使用数百个模型,每个模型使用数百甚至数千个特征。

即使是每小时为所有这些特征计算汇总统计数据这样简单的事情也可能成本高昂,不仅计算资源要求高,而且内存使用也很大。跟踪,即不断计算太多的指标,还可能降低系统的速度,并增加用户体验的延迟以及检测系统异常所需的时间。

虽然跟踪特征对于调试目的很有用,但对于检测模型性能下降并不是非常有效。

理论上,小的分布变化可能导致灾难性故障,但实际上,单个特征的轻微变化可能根本不会影响模型的性能。特征分布随时都在变化,大多数这些变化都是良性的。⁴⁸ 如果您想在特征似乎漂移时接收警报,您可能很快就会被警报淹没,并意识到大多数这些警报都是误报。这可能导致一种称为“警报疲劳”的现象,监控团队停止关注警报,因为它们如此频繁。特征监控的问题变成了尝试确定哪些特征变化是关键的,哪些不是关键的问题。

特征提取通常是通过多个步骤进行的(例如填充缺失值和标准化),使用多个库(例如 pandas、Spark),在多个服务中执行(例如 BigQuery 或 Snowflake)。

您可能会将关系数据库作为特征提取过程的输入,将 NumPy 数组作为输出。即使您检测到特征中的有害变化,也可能无法确定这种变化是由底层输入分布的变化引起的,还是由多个处理步骤中的错误引起的。

特征遵循的架构可能随时间改变。

如果您没有一种方法来对架构进行版本控制,并将每个特征映射到其预期的架构,那么报告的警报原因可能是由于架构不匹配而不是数据变化引起的。

这些关注点并不是为了忽视特征监控的重要性;特征空间的变化是理解 ML 系统健康的有用信号源。希望考虑到这些关注点可以帮助您选择适合您的特征监控解决方案。

监控原始输入

正如前一节讨论的那样,特征的变化可能是由于处理步骤中的问题,而不是数据变化引起的。如果我们在数据处理之前监控原始输入会怎么样?然而,原始输入数据可能并不容易监控,因为它可以来自不同格式的多个来源,并且遵循多种结构。今天许多 ML 工作流的设置也使得 ML 工程师无法直接访问原始输入数据,因为原始输入数据通常由数据平台团队管理,他们处理并将数据移动到数据仓库等位置,而 ML 工程师只能从数据仓库查询数据,其中数据已经部分处理。因此,监控原始输入通常是数据平台团队的责任,而不是数据科学或 ML 团队的责任。因此,这本书不涵盖此范围。

到目前为止,我们讨论了用于监控的不同类型的指标,从通常用于软件系统的运行指标到帮助您跟踪 ML 模型健康状况的 ML 特定指标。在下一节中,我们将讨论您可以使用的工具箱,以帮助进行指标监控。

监控工具箱

测量、跟踪和解释复杂系统的指标是一项非常复杂的任务,工程师们依赖一套工具来帮助他们完成这些任务。行业通常将指标、日志和追踪视为监控的三大支柱。然而,我发现它们的区别并不清晰。它们似乎是从开发监控系统的人的角度生成的:追踪是日志的一种形式,指标可以从日志中计算得出。在这一节中,我想专注于从监控系统用户的角度看待的一组工具:日志、仪表板和警报。

日志

传统软件系统依赖于日志记录运行时产生的事件。一个事件是对系统开发者可能感兴趣的任何事情,无论是事件发生时还是以后用于调试和分析的目的。例如,容器启动时的事件,它所占用的内存量,函数调用的时间,函数运行结束的时间,该函数调用的其他函数,该函数的输入和输出等等。同时,不要忘记记录崩溃、堆栈跟踪、错误代码等等。用 Ian Malpass 在 Etsy 的话来说,“如果它有动静,我们都追踪。”⁴⁹ 他们也追踪那些尚未发生变化的东西,以防它们以后会有动静。

日志数量可能非常快速地增长。例如,早在 2019 年,约会应用 Badoo 每天处理 200 亿个事件。⁵⁰ 当发生问题时,您需要查询日志以查找导致问题的事件序列,这个过程就像在一堆草垛中搜索针一样。

在软件部署的早期阶段,一个应用可能是一个单独的服务。发生问题时,您知道发生了什么。但是今天,一个系统可能由许多不同的组件组成:容器、调度器、微服务、多语言持久性、网格路由、短暂的自动扩展实例、无服务器 Lambda 函数。一个请求可能需要经过 20-30 个跃点,从发送到接收响应。困难之处可能不在于检测何时发生了什么,而在于问题发生的位置。⁵¹

当我们记录一个事件时,我们希望尽可能容易地找到它。在微服务架构中,这种实践被称为分布式追踪。我们希望为每个进程分配一个唯一的 ID,这样,当出现问题时,错误消息将(希望)包含该 ID。这使我们能够搜索与之关联的日志消息。我们还希望记录每个事件所需的所有元数据:发生时间、发生的服务、调用的函数、与进程相关联的用户(如果有的话)等。

由于日志变得如此庞大且难以管理,已经开发出许多工具来帮助公司管理和分析日志。2021 年,日志管理市场的价值估计为 23 亿美元,预计到 2026 年将增长到 41 亿美元。⁵²

手动分析数十亿条日志事件是徒劳的,因此许多公司使用机器学习来分析日志。日志分析中机器学习的一个例子是异常检测:检测系统中的异常事件。更复杂的模型甚至可以根据其优先级(如常规、异常、异常、错误和致命)对每个事件进行分类。

日志分析中机器学习的另一个用例是,当服务失败时,了解相关服务受到影响的可能性可能会很有帮助,尤其是在系统遭受网络攻击时。

许多公司使用批处理方式处理日志。在这种场景中,您收集大量日志,然后定期使用 SQL 查询特定事件,或者使用类似于 Spark、Hadoop 或 Hive 集群的批处理过程处理它们。这使得日志处理变得高效,因为您可以利用分布式和 MapReduce 过程来增加处理吞吐量。然而,由于您周期性地处理日志,您只能周期性地发现问题。

要在日志出现异常时尽快发现异常,您需要在日志记录时立即处理事件。这使得日志处理成为一个流处理问题。⁵³ 您可以使用实时传输,例如 Kafka 或 Amazon Kinesis,以在日志记录时传输事件。要实时搜索具有特定特征的事件,您可以利用流式 SQL 引擎如 KSQL 或 Flink SQL。

仪表板

一图胜过千言万语。一系列数字对您可能毫无意义,但在图表上可视化它们可能会揭示这些数字之间的关系。监控指标的仪表板对于监控至关重要。

仪表板的另一个用途是使监控对非工程师也可见。监控不仅仅是系统开发者的事情,还包括产品经理和业务开发者等非工程利益相关者。

尽管图表在理解指标方面有很大帮助,但它们本身并不足够。您仍然需要经验和统计知识。考虑图 8-8 中的两个图表。从这些图表中唯一显而易见的是损失波动很大。如果这两个图表中的任何一个出现分布变化,我无法判断。绘制一个图表来绘制一个波动的线比理解这条波动线意味着更容易。

图 8-8. 图表对于理解数字很有帮助,但并不足够。

仪表板上过多的指标也可能产生反作用,这种现象被称为仪表板腐化。选择正确的指标或将低级指标抽象出来以计算更高级别的信号,这对于您的具体任务更有意义至关重要。

警报

当我们的监控系统检测到可疑情况时,有必要及时通知相关人员。警报由以下三个组件组成:

警报策略

这描述了警报的条件。当度量指标超出阈值时,您可能希望创建警报,可选地在一定时间内超过某个持续时间。例如,您可能希望在模型准确率低于 90%或 HTTP 响应延迟高于 1 秒至少 10 分钟时收到通知。

通知渠道

这些描述了在满足条件时应通知谁。警报将显示在您使用的监控服务中,例如 Amazon CloudWatch 或 GCP Cloud Monitoring,但当他们不在这些监控服务上时,您还希望通知负责人员。例如,您可以配置警报发送到像 mlops-monitoring@[您的公司邮件域名]这样的电子邮件地址,或者发布到 Slack 频道(如#mlops-monitoring)或 PagerDuty。

警报的描述

这有助于提醒相关人员了解当前情况。描述应尽可能详细,例如:

## Recommender model accuracy below 90%

${timestamp}: This alert originated from the service ${service-name}

根据警报的受众,通常需要通过提供缓解指南或一个运行手册,这是一本编译了例行程序和操作的手册,可能有助于处理警报,来使警报具有可操作性。

警报疲劳是一个真实存在的现象,前面在本章中已经讨论过。警报疲劳可能会让人灰心丧气——没有人愿意因为责任范围之外的事情在半夜被吵醒。这也是危险的——暴露于琐碎的警报可能会使人对关键警报麻木不仁。因此,设置有意义的条件非常重要,以便只发送关键的警报。

可观察性

自 2010 年代中期以来,行业开始采纳“可观察性”这一术语,而不再使用“监控”。监控不对系统的内部状态与其输出之间的关系做任何假设。您监控系统的外部输出,以找出系统内部出了什么问题——外部输出并不能保证能帮助您找出问题所在。

在软件部署的早期阶段,软件系统足够简单,只需要监控外部输出就足以维护软件。一个系统只由几个组件组成,一个团队可以完全控制整个代码库。如果出了问题,可以对系统进行更改,进行测试并找出问题所在。

然而,在过去十年中,软件系统已经显著变得更加复杂。今天,软件系统由许多组件组成。其中许多组件是其他公司运行的服务——所有的云原生服务——这意味着一个团队甚至无法控制其系统中所有组件的内部情况。当出现问题时,团队不再能够仅仅拆开其系统来找出问题所在。团队必须依赖其系统的外部输出来了解内部发生了什么。

可观察性是用来解决这一挑战的术语。这是从控制理论中引入的概念,指的是通过在运行时从系统收集的“输出”来提高对软件复杂行为理解的可见性。

换句话说,可观察性比传统的监控做出了更强的假设:系统的内部状态可以从其外部输出的知识中推断出来。内部状态可以是当前状态,比如“当前的 GPU 利用率”,也可以是历史状态,比如“过去一天的平均 GPU 利用率”。

当可观察的系统出现问题时,我们应该能够通过查看系统的日志和指标来弄清楚问题所在,而不必向系统发布新代码。可观察性是关于以一种确保收集和分析系统运行时足够信息的方式来仪器化您的系统。

监控围绕指标展开,而指标通常是聚合的。可观测性允许更精细的指标,因此您不仅可以知道模型性能何时下降,还可以了解对于哪些输入、哪些用户子组或者在多长时间内模型性能下降。例如,您应该能够查询日志以回答类似以下问题:“显示在最后一小时内,模型 A 为所有错误预测的用户分组,按其邮政编码排序”或者“显示最近 10 分钟的异常请求”或者“显示该输入通过系统的所有中间输出”。要实现这一点,您需要使用标签和其他识别关键字记录系统的输出,以便以后可以沿不同数据维度切片和分析这些输出。

在机器学习(ML)中,可观测性包括可解释性。可解释性帮助我们理解 ML 模型的工作原理,而可观测性则帮助我们理解整个 ML 系统的工作方式,包括 ML 模型在内。例如,当模型在最后一小时内的性能下降时,能够解释哪个特征对在最后一小时内所有错误预测贡献最大,将有助于弄清楚系统出了什么问题以及如何修复它。⁵⁵

在本节中,我们讨论了监控的多个方面,从监控哪些数据和跟踪哪些指标到不同的监控和可观测性工具。尽管监控是一个强大的概念,但它本质上是被动的。你需要等待发生变化才能检测到它。监控帮助发现问题,但并不会纠正它。在接下来的部分中,我们将介绍持续学习,这是一个能够主动帮助您更新模型以应对变化的范式。

总结

这可能是我在本书中撰写的最具挑战性的章节。原因是尽管了解 ML 系统在生产环境中失败的重要性,但围绕它的文献却有限。我们通常认为研究在生产之前进行,但这是 ML 领域的一个方面,其中研究仍在努力赶上生产。

为了理解 ML 系统的失败,我们区分了两种类型的失败:软件系统失败(也发生在非 ML 系统中的失败)和 ML 特定失败。尽管今天大多数 ML 失败都是非 ML 特定的,随着围绕 MLOps 的工具和基础设施的成熟,这种情况可能会发生变化。

我们讨论了 ML 特定失败的三个主要原因:生产数据与训练数据不同,边缘情况和退化反馈循环。前两个原因与数据有关,而最后一个原因则与系统设计有关,因为它发生在系统的输出影响同一系统的输入时。

近年来引起广泛关注的一个失败案例是数据分布偏移。我们研究了三种类型的偏移:协变量偏移、标签偏移和概念漂移。尽管研究分布偏移的领域正在成长,但研究界尚未形成一个标准的叙述。不同的论文用不同的名字称呼同一现象。许多研究仍然基于一个假设,即我们预先知道分布将如何偏移,或者已经获得了来自源分布和目标分布的数据标签。然而,实际情况是,我们不知道未来的数据会是什么样子,获取新数据的标签可能成本高昂、速度缓慢,或者根本不可行。

要能够检测到偏移,我们需要监控我们部署的系统。监控是任何软件工程系统在生产中的重要实践,不仅仅是 ML,而且这是我们应该从 DevOps 世界中尽可能多学习的 ML 领域。

监控关键在于度量标准。我们讨论了需要监控的不同度量标准:操作度量标准——任何软件系统都应该监控的度量标准,例如延迟、吞吐量和 CPU 利用率——以及 ML 特定的度量标准。监控可以应用于与准确率相关的度量标准、预测、特征和/或原始输入。

监控很难,因为即使计算度量标准的成本低廉,理解度量标准并不直观。建立展示图表的仪表板很容易,但理解图表的含义要困难得多,特别是确定是否显示漂移的迹象,以及如果有漂移,是由于底层数据分布的变化还是管道中的错误引起的。理解统计学可能需要解释数字和图形。

在生产中检测模型性能的下降是第一步。下一步是如何使我们的系统适应不断变化的环境,这将在下一章中讨论。

¹ 这似乎是库存预测的一个相当普遍模式。尤金·严在他的文章“部署机器学习后的 6 个不太知名挑战”(2021 年)中写到一个类似的案例来说明退化反馈循环的问题。

² 这是许多公司不愿使用初创公司产品的原因之一,也是许多公司更喜欢使用开源软件的原因之一。当你使用的产品不再由其创建者维护时,如果该产品是开源的,至少你可以访问代码库并自行维护。

³ 宇宙射线可能导致硬件故障(维基百科,“软错误”,https://oreil.ly/4cvNg)。

⁴ Daniel Papasian 和 Todd Underwood,《ML 如何崩溃:一个大型 ML 管道十年的停机期》,Google,2020 年 7 月 17 日,视频,19:06,https://oreil.ly/WGabN。虽然非 ML 故障可能仍然间接归因于 ML。例如,服务器可能因非 ML 系统而崩溃,但由于 ML 系统通常需要更多计算能力,这可能导致该服务器更频繁地崩溃。

⁵ 我职业生涯的高峰:埃隆·马斯克同意我的观点

⁶ 在线学术会议仍然存在时,我经常听到研究人员争论他们的模型谁能更好地泛化。“我的模型比你的模型泛化能力更强”是最终的炫耀。

⁷ 杉山雅士和川辺基晃,《非稳态环境中的机器学习:协变量转移适应简介》(剑桥,MA:MIT Press,2012 年)。

⁸ John Mcquaid,《增长的极限:AI 对数据的贪婪能被驯服吗?》,Undark,2021 年 10 月 18 日,https://oreil.ly/LSjVD

⁹ 一家监控服务公司的首席技术官告诉我,据他估计,他们服务捕捉到的漂移中 80%是由人为错误引起的。

¹⁰ 这意味着自动驾驶汽车比普通人类驾驶员稍微安全一些。截至 2019 年,每 10 万名持证驾驶员的交通相关死亡率为 15.8,或 0.0158%(“Fatality Rate per 100,000 Licensed Drivers in the U.S. from 1990 to 2019”,Statista,2021 年,https://oreil.ly/w3wYh)。

¹¹ Rodney Brooks,《自动驾驶汽车的边缘案例》,Robots, AI, and Other Stuff,2017 年 6 月 17 日,https://oreil.ly/Nyp4F;Lance Eliot,《无尽的边缘或角落案例是否是 AI 自动驾驶汽车的长尾厄运?》,Forbes,2021 年 7 月 13 日,https://oreil.ly/L2Sbp;Kevin McAllister,《自动驾驶汽车将由模拟和位置数据塑造》,Protocol,2021 年 3 月 25 日,https://oreil.ly/tu8hs

¹² 电子发现,或电子发现,指的是在法律诉讼、政府调查或信息自由法案请求等法律程序中寻找的信息,其格式为电子格式。

¹³ Ray Jiang, Silvia Chiappa, Tor Lattimore, András György, 和 Pushmeet Kohli,《推荐系统中的退化反馈环路》,arXiv,2019 年 2 月 27 日,https://oreil.ly/b9G7o

¹⁴ 这与“存活偏差”有关。

¹⁵ Erik Brynjolfsson, Yu (Jeffrey) Hu, 和 Duncan Simester, “再见帕累托原理,你好长尾:搜索成本对产品销售集中度的影响,” Management Science 57, no. 8 (2011): 1373–86, https://oreil.ly/tGhHi; Daniel Fleder 和 Kartik Hosanagar, “重大文化的兴起或衰落:推荐系统对销售多样性的影响,” Management Science 55, no. 5 (2009), https://oreil.ly/Zwkh8; Himan Abdollahpouri, Robin Burke, 和 Bamshad Mobasher, “管理推荐系统中的流行偏见与个性化重新排名,” arXiv, January 22, 2019, https://oreil.ly/jgYLr.

¹⁶ Patrick John Chia, Jacopo Tagliabue, Federico Bianchi, Chloe He, 和 Brian Ko, “超越 NDCG:使用 RecList 测试推荐系统的行为,” arXiv, November 18, 2021, https://oreil.ly/7GfHk.

¹⁷ Catherine Wang, “为什么 TikTok 让它的用户如此着迷?AI 算法让你上瘾的原因,” Towards Data Science, June 7, 2020, https://oreil.ly/J7nJ9.

¹⁸ Gediminas Adomavicius 和 YoungOk Kwon, “利用基于排名的技术提高聚合推荐多样性,” IEEE Transactions on Knowledge and Data Engineering 24, no. 5 (May 2012): 896–911, https://oreil.ly/0JjUV.

¹⁹ Tobias Schnabel, Adith Swaminathan, Ashudeep Singh, Navin Chandak, and Thorsten Joachims, “推荐作为治疗:去偏见化学习与评估,” arXiv, February 17, 2016, https://oreil.ly/oDPSK.

²⁰ Jeffrey C. Schlimmer 和 Richard H. Granger, Jr., “从噪声数据中的增量学习,” Machine Learning 1 (1986): 317–54, https://oreil.ly/FxFQi.

²¹ 你可能会想到,当P(X|Y)发生变化但P(Y)保持不变时,如第二种分解情况。我在这种情况下从未遇到过任何研究。我询问了几位专门研究数据转移的研究人员,他们也告诉我,这种情况将会非常难以研究。

²² Wouter M. Kouw 和 Marco Loog, “领域适应与迁移学习简介,” arXiv, December 31, 2018, https://oreil.ly/VKSVP.

²³ “美国女性的乳腺癌风险,” 美国国家癌症研究所, https://oreil.ly/BFP3U.

²⁴ Arthur Gretton, Alex Smola, Jiayuan Huang, Marcel Schmittfull, Karsten Borgwardt 和 Bernard Schölkopf,“核均值匹配的协变量转移”,机器学习研究期刊 (2009 年),https://oreil.ly/s49MI

²⁵ Sugiyama 和 Kawanabe,《非稳态环境下的机器学习》。

²⁶ Tongtong Fang, Nan Lu, Gang Niu 和 Masashi Sugiyama,“重新思考深度学习中的重要性加权在分布转移下的应用”,神经信息处理系统大会论文集 2020,https://oreil.ly/GzJ1r;Gretton 等人,“核均值匹配的协变量转移”。

²⁷ Han Zhao, Remi Tachet Des Combes, Kun Zhang 和 Geoffrey Gordon,“关于学习不变表示进行领域自适应”,机器学习研究会议论文集 97 (2019): 7523–32,https://oreil.ly/ZxYWD

²⁸ 你可以把这看作是 P(X) 和 P(Y|X) 都发生变化的情况。

²⁹ 如果你使用 softmax 作为分类税的最后一层神经网络,这个 softmax 层的维度是 [隐藏单元数 × 类别数]。当类别数变化时,softmax 层中的参数数目也会变化。

³⁰ 如果你使用无监督学习方法,你不需要地面真实标签,但今天绝大多数应用程序都是监督学习的。

³¹ Hamel Husain 在斯坦福大学 2022 年的 CS 329S: 机器学习系统设计 课上出色地讲解了为何 TensorFlow Extended 的偏斜检测如此糟糕。你可以在 YouTube 上找到视频。

³² I. M. Chakravarti, R. G. Laha 和 J. Roy,《应用统计方法手册》,第 1 卷,《计算技术、描述方法和统计推断技术》,(纽约:Wiley,1967 年)。

³³ Eric Feigelson 和 G. Jogesh Babu,“小心科尔莫哥罗夫-斯米尔诺夫检验!” 宇宙统计中心,宾夕法尼亚州立大学,https://oreil.ly/7AHcT

³⁴ Eric Breck, Marty Zinkevich, Neoklis Polyzotis, Steven Whang 和 Sudip Roy,“机器学习的数据验证”,SysML 会议论文集,2019 年,https://oreil.ly/xoneh

³⁵ Li Bu, Cesare Alippi 和 Dongbin Zhao,“基于密度差异估计的无 pdf 变化检测测试”,IEEE 神经网络与学习系统交易 29, no. 2 (2018 年 2 月): 324–34,https://oreil.ly/RD8Uy。作者声称该方法适用于多维输入。

³⁶ Stephan Rabanser、Stephan Günnemann 和 Zachary C. Lipton,《高声失败:检测数据集转移方法的实证研究》,arXiv,2018 年 10 月 29 日,https://oreil.ly/HxAwV

³⁷ Manuel Baena-García、José del Campo-Ávila、Raúl Fidalgo、Albert Bifet、Ricard Gavaldà和 Rafael Morales-Bueno,《早期漂移检测方法》,2006 年,https://oreil.ly/Dnv0s

³⁸ Nandini Ramanan、Rasool Tahmasbi、Marjorie Sayer、Deokwoo Jung、Shalini Hemachandran 和 Claudionor Nunes Coelho Jr.,《时间序列数据上的实时漂移检测》,arXiv,2021 年 10 月 12 日,https://oreil.ly/xmdqW

³⁹ 我正在研究一个可以处理分钟粒度级别的解决方案。

⁴⁰ 感谢 Goku Mohandas 在MLOps Discord 服务器上分享这个提示。

⁴¹ 早期评论家李汉钟指出,这也是因为较小的公司对其模型的数据不足。当你没有足够的数据时,最好是按时间安排而不是过度拟合于不充分的数据。

⁴² Kun Zhang、Bernhard Schölkopf、Krikamol Muandet 和 Zhikun Wang,《目标和条件转移下的领域适应》,第 30 届国际机器学习会议论文集(2013 年),https://oreil.ly/C123l

⁴³ Han Zhao、Remi Tachet Des Combes、Kun Zhang 和 Geoffrey Gordon,《关于学习不变表示进行领域适应的研究》,机器学习研究会议录 第 97 卷(2019 年):7523–32,https://oreil.ly/W78hH

⁴⁴ Zachary C. Lipton、Yu-Xiang Wang 和 Alex Smola,《使用黑盒预测器检测和修正标签转移》,arXiv,2018 年 2 月 12 日,https://oreil.ly/zKSlj

⁴⁵ 一些监控供应商声称,他们的解决方案不仅能够检测何时应重新训练模型,还能确定重新训练的数据。我尚未能够验证这些声明的有效性。

⁴⁶ “Amazon 计算服务等级协议”,亚马逊网络服务,最后更新于 2021 年 8 月 24 日,https://oreil.ly/5bjx9

⁴⁷ 在使用完成率作为优化度量标准时要小心,因为它可能会使你的推荐系统偏向短视频。

⁴⁸ Rabanser、Günnemann 和 Lipton,《高声失败》。

⁴⁹ Ian Malpass,《任何事情都可以衡量,一切都可以测量》,Code as Craft,2011 年 2 月 15 日,https://oreil.ly/3KF1K

⁵⁰ Andrew Morgan,“Badoo 中的数据工程:处理每天 200 亿事件”,InfoQ,2019 年 8 月 9 日,https://oreil.ly/qnnuV

⁵¹ Charity Majors,“可观测性——三年回顾”,The New Stack,2019 年 8 月 6 日,https://oreil.ly/Logby

⁵² “日志管理市场规模、份额和全球市场预测至 2026 年”,MarketsandMarkets,2021 年,https://oreil.ly/q0xgh

⁵³ 对于不熟悉流处理的读者,请参考章节“批处理与流处理对比”。

⁵⁴ Suman Karumuri、Franco Solleza、Stan Zdonik 和 Nesime Tatbul,“朝向规模化可观测数据管理”,ACM SIGMOD Record,第 49 卷第 4 期(2020 年 12 月):18–23,https://oreil.ly/oS5hn

⁵⁵ 查看章节“特征重要性”。

第九章:持续学习和生产环境中的测试

在第八章中,我们讨论了机器学习系统在生产中可能失败的各种方式。我们重点关注了一个尤其棘手的问题,这个问题在研究人员和实践者之间引起了广泛讨论:数据分布的变化。我们还讨论了多种检测数据分布变化的监控技术和工具。

本章是讨论这个问题的延续:我们如何使我们的模型适应数据分布的变化?答案是持续更新我们的机器学习模型。我们将从持续学习是什么及其面临的挑战开始讨论 —— 结果提示:持续学习主要是基础设施问题。然后,我们将提出一个四阶段计划,使持续学习成为现实。

在你设置好基础架构,可以随意更新模型后,你可能会考虑到一个问题,这个问题几乎每一个我遇到的机器学习工程师都问过我:“我应该多久重新训练我的模型?” 这个问题是本书下一节的重点讨论内容。

如果模型被重新训练以适应不断变化的环境,则仅在静态测试集上评估是不够的。我们将介绍一个看似可怕但却必要的概念:生产环境中的测试。这个过程是在生产中使用实时数据测试系统,以确保更新的模型确实可以正常工作而没有灾难性后果。

本章和前一章的主题密切相关。在生产环境中的测试与监控是相辅相成的。如果监控意味着被动地跟踪所使用模型的输出,那么在生产环境中的测试意味着积极地选择哪个模型生成输出,以便评估它。监控和生产环境中的测试的目标都是了解模型的性能,并找出更新模型的时机。持续学习的目标是安全且高效地自动化更新。所有这些概念使我们能够设计一个可维护且能够适应不断变化环境的机器学习系统。

这是我最期待写的一章,希望我能让你也对它感到兴奋!

持续学习

听到“持续学习”这个词时,许多人会想到在生产中每个传入样本后模型更新自身的训练范式。实际上很少有公司这样做。首先,如果你的模型是神经网络,那么每个传入样本的学习会使其容易受到灾难性遗忘的影响。灾难性遗忘指的是神经网络在学习新信息时完全和突然地忘记先前学到的信息¹

其次,它可能会使训练变得更昂贵 —— 大多数硬件后端今天都是为批处理而设计的,因此一次处理一个样本会导致计算能力的巨大浪费,并且无法利用数据并行性。

在生产中采用持续学习的公司会使用微批次来更新他们的模型。例如,他们可能在每个 512 或 1,024 个示例之后更新现有模型——每个微批次中的最佳示例数量取决于任务。

更新后的模型在评估之前不应部署。这意味着您不应直接对现有模型进行更改。相反,您应创建现有模型的副本,并在新数据上更新此副本,只有在更新的副本证明更好时,才将现有模型替换为更新的副本。现有模型称为冠军模型,更新的副本称为挑战者。这个过程在图 9-1 中有所展示。这是为了理解而进行的过度简化。实际上,一家公司可能同时拥有多个挑战者,并且处理失败的挑战者比简单丢弃要复杂得多。

图 9-1. 持续学习在生产中可能运作的简化流程。实际上,处理失败的挑战者的过程比简单丢弃要复杂得多。

尽管如此,“持续学习”一词使人们想象需要非常频繁地更新模型,例如每 5 到 10 分钟一次。许多人认为大多数公司不需要如此频繁地更新模型,因为有两个原因。首先,他们没有足够的流量(即足够的新数据),以便重新训练计划具有意义。其次,他们的模型衰减速度不那么快。我同意他们的观点。如果将重新训练计划从一周改为一天不带来回报,并且增加了更多开销,那就没有必要这样做。

无状态重新训练与有状态训练

然而,持续学习不是关于重新训练的频率,而是模型重新训练的方式。大多数公司采用无状态重新训练,即每次都从头开始训练模型。持续学习还意味着允许有状态训练,即模型在新数据上继续训练。² 有状态训练也被称为微调或增量学习。无状态重新训练和有状态训练的区别在图 9-2 中有所展示。

图 9-2. 无状态重新训练与有状态训练

有状态训练允许您使用较少的数据来更新模型。从头开始训练模型通常需要比对同一模型进行微调更多的数据。例如,如果您从头开始重新训练模型,可能需要使用最近三个月的所有数据。但是,如果您从昨天的检查点开始微调模型,您只需要使用最近一天的数据。

Grubhub 发现有状态训练使得他们的模型收敛更快,需要的计算资源大大减少。从每日无状态重新训练到每日有状态训练,使他们的训练计算成本减少了 45 倍,并且增加了他们的购买转化率 20%。³

经常被忽视的一个美好特性是,有状态训练可能使得完全不必存储数据成为可能。在传统的无状态重新训练中,数据样本可能会在模型的多次训练迭代中被重复使用,这意味着需要存储数据。这并不总是可行的,特别是对于具有严格隐私要求的数据而言。在有状态训练范式中,每个模型更新仅使用新鲜的数据进行训练,因此数据样本仅用于一次训练,如 图 9-2 所示。这意味着可以在训练模型时避免将数据存储在永久存储中,有助于消除许多关于数据隐私的顾虑。然而,这一点经常被忽视,因为当今的“让我们跟踪一切”的做法仍然使得许多公司不愿意放弃数据。

有状态训练并不意味着不再从头开始训练。那些最成功使用有状态训练的公司,也偶尔会在大量数据上从头开始训练他们的模型以进行校准。或者,他们也可能同时使用参数服务器等技术,在有状态训练的同时从头开始训练他们的模型,然后将两个更新后的模型结合起来。⁴

一旦你的基础设施设置允许无状态重新训练和有状态训练,训练频率就只是一个可以调整的旋钮。你可以每小时更新你的模型,每天更新一次,或者在检测到分布变化时更新。如何找到最佳的重新训练计划将在 “模型更新频率如何选择” 部分讨论。

连续学习是指以一种方式设置基础设施,使得你作为数据科学家或机器学习工程师,可以根据需要更新你的模型,无论是从头开始还是微调,并快速部署这些更新。

你可能会想:有状态训练听起来很酷,但如果我想向我的模型添加新功能或者另一层,这会如何工作?为了解答这个问题,我们必须区分两种模型更新类型:

模型迭代

新功能被添加到现有模型架构中或者模型架构被修改。

数据迭代

模型架构和特征保持不变,但你使用新数据刷新这个模型。

截至今天,有状态训练主要用于数据迭代,因为改变模型架构或添加新特征仍需要从头训练生成的模型。有研究表明,可以通过诸如知识迁移(Google,2015)和模型手术(OpenAI,2019)等技术,可能绕过从头训练模型迭代。根据 OpenAI,“手术在选择过程确定哪些模型部分保持不变,哪些必须重新初始化后,将训练好的权重从一个网络转移到另一个网络。”⁵ 几个大型研究实验室都进行了尝试;然而,我不知道行业中是否有明确的结果。

为什么要持续学习?

我们讨论了持续学习是关于建立基础设施,使您能够随意更新模型并部署这些变化。但是为什么您需要能够随意更新您的模型呢?

持续学习的第一个应用场景是应对数据分布的变化,尤其是突然发生的变化。想象一下,您正在构建一个模型来确定像 Lyft 这样的拼车服务的价格。⁶ 历史上,这个特定社区的周四晚上的拼车需求较慢,所以模型预测低拼车价格,这使得司机不愿上路。然而,这个周四晚上,社区发生了大事件,拼车需求突然激增。如果您的模型不能通过提高价格预测并动员更多司机快速响应这种变化,乘客将不得不等待很长时间才能拼车,这会造成负面用户体验。他们甚至可能转向竞争对手,导致您损失收入。

持续学习的另一个应用场景是适应罕见事件。想象一下,您为像亚马逊这样的电商网站工作。黑色星期五是一年中重要的购物活动。您不可能收集足够的历史数据,使您的模型能够准确预测今年黑色星期五期间客户的行为。为了提高性能,您的模型应该在一天内利用新鲜数据进行学习。2019 年,阿里巴巴以 1.03 亿美元收购了 Data Artisans,这支团队领导了流处理框架 Apache Flink 的开发,以帮助他们适应机器学习用例。⁷ 他们的旗舰用例是在类似于美国黑色星期五的中国购物节“双十一”上提供更好的推荐服务。

当今机器学习生产中的一个巨大挑战是可以通过持续学习来解决的连续冷启动问题。冷启动问题是指当你的模型需要为一个没有任何历史数据的新用户进行预测时所面临的情况。例如,为了向用户推荐他们可能想看的电影,推荐系统通常需要知道该用户以前观看过什么。但如果这个用户是新用户,你将没有他们的观影历史,只能为他们生成一些通用的内容,比如当前你网站上最流行的电影。

连续冷启动是冷启动问题的一种泛化形式,因为它不仅可能发生在新用户身上,也可能发生在现有用户身上。例如,当现有用户从笔记本电脑切换到手机时,他们在手机上的行为可能与在笔记本电脑上的行为不同。它也可能发生因为用户未登录——大多数新闻网站不要求读者登录即可阅读。

当用户访问某项服务的频率非常低,以至于服务对这个用户的所有历史数据都已经过时时,这种情况也可能发生。例如,大多数人一年只预订几次酒店和航班。Coveo 是一家为电子商务网站提供搜索引擎和推荐系统的公司,发现电子商务网站有超过 70%的购物者一年不到三次访问他们的网站。

如果你的模型适应不够快,它将无法为这些用户提供相关的推荐,直到下次模型更新为止。到那时,这些用户可能已经因为找不到符合他们需求的内容而离开了服务。

如果我们能够使我们的模型在每个用户访问会话中适应,那么模型甚至可以在用户首次访问时对他们做出准确且相关的预测。例如,TikTok 已经成功地应用持续学习来在几分钟内适应他们的推荐系统到每个用户身上。你下载应用程序,并在看了几段视频后,TikTok 的算法就能够高度准确地预测你接下来想要观看的内容。我不认为每个人都应该尝试构建像 TikTok 一样令人上瘾的东西,但这证明了持续学习可以释放强大的预测潜力。

“为什么持续学习?”应该重新表述为“为什么不持续学习?” 持续学习是批处理学习的超集,因为它允许你做批处理学习可以做的一切。但持续学习还允许你解锁批处理学习无法解决的用例。

如果连续学习设置和批处理学习一样需要相同的努力和成本,那么没有理由不进行连续学习。截至撰写本书时,设定连续学习仍面临许多挑战,我们将在下一节更深入地探讨。然而,连续学习的 MLOps 工具逐渐成熟,这意味着不久的将来,设定连续学习可能和批处理学习一样简单。

连续学习的挑战

尽管连续学习有许多用例,许多公司已经成功应用,但仍面临许多挑战。在本节中,我们将讨论三个主要挑战:获取新鲜数据、评估和算法。

新鲜数据获取挑战

第一个挑战是获取新鲜数据的挑战。如果您希望每小时更新您的模型,您就需要每小时获取新数据。目前,许多公司从其数据仓库中提取新的训练数据。您可以从多个来源获取数据的速度取决于这些数据被存入数据仓库的速度。数据的获取速度可能较慢,特别是如果数据来自多个源头。另一种选择是允许从数据仓库存入之前拉取数据,例如,直接从 Kafka 和 Kinesis 等实时传输工具获取数据,这些工具将数据从应用程序传输到数据仓库¹²,如图 9-3 所示。

图 9-3. 直接从实时传输工具中获取数据,在数据存入数据仓库之前,可以让您访问到更新的数据。

能够获取新鲜数据还不够。如果您的模型需要标记数据来进行更新,正如大多数模型今天所需的那样,那么这些数据也需要进行标记。在许多应用中,模型更新的速度往往受限于数据标记的速度。

连续学习的最佳候选任务是那些能够通过短反馈周期获得自然标签的任务。这些任务的例子包括动态定价(基于估计的需求和可用性)、预计到达时间、股票价格预测、广告点击率预测以及推荐系统,例如推特、歌曲、短视频、文章等的在线内容。

然而,这些自然标签通常不是作为标签生成的,而是作为需要从行为活动中提取成标签的行为活动。让我们通过一个例子来澄清这一点。如果你经营一个电子商务网站,你的应用程序可能会记录到,用户 A 在晚上 10:33 点击了 ID 为 32345 的产品。你的系统需要回顾日志,看看是否曾向此用户推荐过这个产品 ID,如果是,则需要查看是哪个查询促使了此推荐,以便你的系统能够将此查询与此推荐匹配并标记此推荐为良好推荐,正如图 9-4 所示。

图 9-4。从用户反馈中提取标签过程的简化版本

回顾日志以提取标签的过程称为标签计算。如果日志数量庞大,这将会非常昂贵。标签计算可以通过批处理来完成:例如,先等待日志存入数据仓库,然后再运行批处理作业一次性从日志中提取所有标签。然而,正如前面讨论的那样,这意味着我们需要先等待数据存入,然后再等待下一个批处理作业运行。一个更快的方法是利用流处理直接从实时传输中提取标签¹³。

如果你的模型速度迭代受到标注速度的瓶颈影响,通过利用像 Snorkel 这样的程序化标注工具来加速标注过程是可行的,以实现快速生成标签,减少人工干预。也可以利用众包标签迅速注释新鲜数据。

考虑到流处理工具仍处于初期阶段,为了从实时传输中访问新鲜数据并快速提取标签,构建高效的流优先基础设施可能需要大量工程投入和成本。好消息是,围绕流处理的工具正快速发展。作为 2021 年 10 月的消息,建立在 Kafka 之上的平台 Confluent 公司估值已达 160 亿美元。到 2020 年底,Snowflake 成立了一个专注于流处理的团队¹⁴。截至 2021 年 9 月,Materialize 已筹集了 1 亿美元用于开发流式 SQL 数据库¹⁵。随着流处理工具的成熟,企业开发面向机器学习的流优先基础设施将更加简单和经济。

评估挑战

持续学习的最大挑战不在于编写函数以持续更新模型——你可以通过编写脚本来实现这一点!最大的挑战在于确保此更新足够好以进行部署。在本书中,我们已经讨论了机器学习系统在生产中出现灾难性失败的情况,从数百万少数民族被不公正拒绝贷款,到过度信任自动驾驶的司机导致致命事故¹⁶。

随着持续学习的风险增加,灾难性故障的风险也在增加。首先,您更新模型的频率越高,更新失败的机会就越多。

其次,持续学习使您的模型更容易受到协调操纵和对抗攻击的影响。由于您的模型在线学习真实世界数据,用户可以轻易输入恶意数据来欺骗模型学习错误内容。2016 年,微软发布了 Tay,一个能够通过 Twitter 上的“随意和俏皮对话”进行学习的聊天机器人。Tay 一推出,恶意用户开始向机器人发送种族主义和女性歧视言论。很快,机器人开始发布煽动性和攻击性的推文,导致微软在其发布仅 16 小时后关闭了该机器人。¹⁷

为了避免类似或更糟的事件发生,非常重要的是在将更新部署给更广泛的受众之前,彻底测试每个模型更新的性能和安全性。我们已经在第六章中讨论了模型的离线评估,并将在本章讨论在线评估(在生产中测试)。

在设计持续学习的评估管道时,请记住评估需要时间,这可能成为模型更新频率的另一个瓶颈。例如,我曾与一家主要在线支付公司合作,他们拥有一个 ML 系统来检测欺诈交易。¹⁸ 欺诈模式变化迅速,因此他们希望快速更新系统以适应变化的模式。但是,在进行新模型与当前模型的 A/B 测试之前,由于任务的不平衡性——大多数交易并非欺诈交易——他们需要大约两周时间才能看到足够多的欺诈交易,以准确评估哪个模型更好。¹⁹ 因此,他们只能每两周更新一次系统。

算法挑战

与新鲜数据挑战和评估相比,这是一个“更轻”的挑战,因为它只影响特定算法和特定的训练频率。准确地说,它只影响希望非常快速更新的基于矩阵和基于树的模型(例如每小时更新)。

为了阐明这一点,考虑两种不同的模型:神经网络和基于矩阵的模型,比如协同过滤模型。协同过滤模型使用用户-物品矩阵和维度缩减技术。

您可以使用任意大小的数据批次更新神经网络模型。您甚至可以仅使用一个数据样本执行更新步骤。但是,如果要更新协同过滤模型,则首先需要使用整个数据集构建用户-物品矩阵,然后对其执行降维。当然,您可以在每次使用新数据样本更新矩阵时对其应用降维,但是如果矩阵很大,则频繁执行降维步骤将会过于缓慢和昂贵。因此,这种模型相比前述的神经网络模型不太适合使用部分数据集进行学习[²⁰]。

适应持续学习范式的模型(如神经网络)比基于矩阵和基于树的模型更容易。然而,已经有算法可以创建可以从增量数据中学习的基于树的模型,最著名的是 Hoeffding Tree 及其变种 Hoeffding Window Tree 和 Hoeffding Adaptive Tree[²¹],但它们的使用尚不普遍。

学习算法不仅需要处理部分数据集,特征提取代码也需要如此。我们在“缩放”[^scaling]部分讨论了通常需要使用最小值、最大值、中位数和方差等统计数据对特征进行缩放。要为数据集计算这些统计数据,通常需要对整个数据集进行一次遍历。当模型一次只能看到小的数据子集时,理论上可以为每个数据子集计算这些统计数据。然而,这意味着这些统计数据在不同子集之间会有很大波动。从一个子集计算的统计数据可能会与下一个子集大相径庭,使得训练在一个子集上的模型难以推广到下一个子集。

要保持这些统计数据在不同子集之间的稳定性,您可能希望在线计算这些统计数据。不要一次性使用所有数据的均值或方差,而是在看到新数据时逐步计算或近似这些统计数据,例如“流数据中的最优分位数近似算法”[²²]中所述的算法。当今流行的框架提供了一些计算运行统计的功能,例如 sklearn 的 StandardScaler 具有partial_fit,允许使用运行统计的特征缩放器,但内置方法速度慢,不支持广泛的运行统计。

持续学习的四个阶段

我们已经讨论了持续学习的含义,为何持续学习如此重要,以及持续学习面临的挑战。接下来,我们将讨论如何克服这些挑战,使持续学习成为可能。截至本书撰写时,持续学习并非公司初始就能采取的策略。向持续学习的转变发生在四个阶段,如下所述。我们将详细介绍每个阶段的发生情况以及从前一个阶段迈向当前阶段所需的条件。

第 1 阶段:手动、无状态重新训练

在初期,机器学习团队经常专注于开发 ML 模型来解决尽可能多的业务问题。例如,如果你的公司是一个电子商务网站,你可能会按以下顺序开发四个模型:

  1. 检测欺诈交易的模型

  2. 推荐用户相关产品的模型

  3. 预测卖家是否滥用系统的模型

  4. 预测订单发货时间的模型

因为你的团队专注于开发新模型,更新现有模型暂时放在次要位置。只有当以下两个条件同时满足时,你才会更新现有模型:模型的性能下降到对系统造成更多害处而非益处的地步,并且你的团队有时间进行更新。你的一些模型每六个月更新一次。一些模型每个季度更新一次。还有一些模型已经在野外运行了一年,却从未被更新过。

更新模型的过程是手动的和临时的。通常是一个数据工程师,需要从数据仓库查询新数据。然后有人清洗这些新数据,提取特征,用旧数据和新数据重新训练模型,然后将更新后的模型导出为二进制格式。接下来,有人将该二进制格式部署为更新后的模型。通常情况下,在重新训练过程中封装数据、特征和模型逻辑的代码会发生变化,但这些变化未能在生产中复制,导致难以追踪的错误。

如果这个过程听起来让你痛苦不堪,你并不孤单。绝大多数非科技行业的公司——例如,任何在不到三年前开始采用机器学习且没有机器学习平台团队的公司——都处于这个阶段²³。

第 2 阶段:自动化重新训练

几年后,你的团队设法将模型部署解决了大多数明显问题。你的生产环境中有 5 到 10 个模型。你的优先任务不再是开发新模型,而是维护和改进现有模型。从前一个阶段提到的临时手动更新模型的过程已经发展成为无法忽视的痛点。你的团队决定编写一个脚本,通过类似 Spark 的批处理定期执行所有的重新训练步骤。

大多数具有较为成熟 ML 基础设施的公司处于这个阶段。一些复杂的公司进行实验以确定最佳的重新训练频率。然而,对于大多数处于这个阶段的公司来说,重新训练频率是基于直觉设定的,例如,“每天一次看起来差不多合适”或“让我们每晚在计算空闲时启动重新训练过程”。

在创建用于自动化系统重新训练过程的脚本时,您需要考虑到您系统中的不同模型可能需要不同的重新训练计划。例如,考虑一个推荐系统,包括两个模型:一个模型用于为所有产品生成嵌入,另一个模型用于根据查询排名每个产品的相关性。嵌入模型可能需要比排名模型少得多地重新训练。因为产品的特征不经常变化,您可能每周重新训练一次嵌入模型[²⁴],而排名模型可能需要每天重新训练一次。

如果您的模型之间存在依赖关系,自动化脚本可能会变得更加复杂。例如,由于排名模型依赖于嵌入,当嵌入发生变化时,排名模型也应进行更新。

需求

如果您的公司在生产中使用 ML 模型,那么您的公司很可能已经拥有大多数自动化重新训练所需的基础设施组件。此阶段的可行性取决于编写脚本自动化工作流程并配置基础设施以自动执行以下操作是否可行:

  1. 拉取数据。

  2. 必要时对数据进行下采样或上采样。

  3. 提取特征。

  4. 处理和/或注释标签以创建训练数据。

  5. 启动训练过程。

  6. 评估新训练的模型。

  7. 部署它。

编写此脚本所需的时间取决于许多因素,包括脚本编写者的能力。然而,总体而言,影响此脚本可行性的三个主要因素是调度器、数据和模型存储。

调度器基本上是处理任务调度的工具,我们将在“Cron, Schedulers, and Orchestrators”部分进行介绍。如果您尚未拥有调度器,您将需要时间来设置一个。但是,如果您已经拥有像 Airflow 或 Argo 这样的调度器,将脚本连接在一起不应该那么难。

第二个因素是数据的可用性和可访问性。您是否需要将数据自行收集到数据仓库?您是否需要从多个组织中联合数据?您是否需要从头开始提取许多特征?您是否还需要为数据标记?如果您对这些问题的答案是肯定的越多,设置此脚本所需的时间就越长。Stefan Krawczyk,在 Stitch Fix 的 ML/data 平台经理,评论说他怀疑大多数人的时间可能会在这里花费。

你将需要的第三个因素是一个模型存储库,用于自动版本控制和存储所有重现模型所需的工件。最简单的模型存储库可能只是一个 S3 存储桶,以某种结构化的方式存储模型的序列化数据块。然而,像 S3 这样的数据块存储既不擅长版本控制工件,也不易于人类阅读。你可能需要更成熟的模型存储库,比如亚马逊 SageMaker(托管服务)和 Databricks 的 MLflow(开源)。我们将详细讨论模型存储库是什么,并在“模型存储”章节中评估不同的模型存储库。

特征重用(记录并等待)

当从新数据中创建训练数据以更新您的模型时,请记住,新数据已经通过了预测服务。这个预测服务已经从新数据中提取了特征,以输入模型进行预测。一些公司会重复使用这些提取的特征来进行模型重新训练,这既节省了计算资源,也确保了预测与训练之间的一致性。这种方法被称为“记录并等待”。这是减少“训练服务偏差”的经典方法,在第八章(请参阅“生产数据与训练数据不一致”章节)中讨论过。

“记录并等待”尚未成为流行的方法,但它正在变得越来越受欢迎。Faire 在其优秀的博客文章中讨论了他们的“记录并等待”方法的优缺点。

第三阶段:自动化、有状态训练

在第二阶段,每次重新训练模型时,你都是从头开始训练(无状态重新训练)。这使得你的重新训练成本高昂,特别是对于频繁重新训练的情况。你阅读了“无状态重新训练与有状态训练”的章节,并决定你想要进行有状态训练——为什么每天都要用过去三个月的数据重新训练,而不是只用最近一天的数据继续训练呢?

因此,在这个阶段,你重新配置你的自动更新脚本,使得当模型更新启动时,它首先定位到先前的检查点并加载到内存中,然后继续在这个检查点上进行训练。

要求

在这个阶段,你最需要的是心态的改变:从头开始重新训练是一个如此常见的做法——许多公司习惯于数据科学家将模型交给工程师每次都从头开始部署——以至于许多公司没有考虑建立基础设施来实现有状态训练。

一旦你决定进行有状态训练,重新配置更新脚本就很简单了。在这个阶段,你主要需要一种方式来跟踪你的数据和模型衍生。想象一下,你首先上传模型版本 1.0。这个模型通过新数据更新为模型版本 1.1,依此类推,直到创建模型 1.2。然后上传另一个模型称为模型版本 2.0。这个模型通过新数据更新为模型版本 2.1。一段时间后,你可能有模型版本 3.32、模型版本 2.11、模型版本 1.64。你可能想知道这些模型随时间如何演变,哪个模型作为基础模型使用,以及用于更新它的数据,以便能够重现和调试它。据我所知,目前没有现有的模型存储具备这种模型衍生能力,因此你可能需要自行构建解决方案。

如果你想从实时传输而不是数据仓库中获取新鲜数据,如在“新鲜数据访问挑战”一节中讨论的,而你的流媒体基础设施不够成熟,你可能需要重构你的流水线。

第四阶段:持续学习

在第三阶段,你的模型仍然根据开发者设定的固定时间表进行更新。找到最佳的时间表并不简单,可能会依赖于具体情况。例如,上周市场上没什么大事发生,所以你的模型衰减得不那么快。然而,本周发生了许多事件,所以你的模型衰减得更快,需要更快的重新训练时间表。

与其依赖于固定的时间表,你可能希望在数据分布发生变化和模型性能下降时自动更新你的模型。

圣杯在于将持续学习与边缘部署结合起来。想象一下,你可以在新设备上部署基础模型——手机、手表、无人机等——该设备上的模型将会根据需要持续更新和适应环境,而无需与集中服务器同步。这意味着不需要集中服务器成本。也不需要在设备和云端之间来回传输数据,这意味着更好的数据安全性和隐私保护!

需求

从第三阶段过渡到第四阶段是陡峭的。你首先需要一个触发模型更新的机制。这个触发器可以是:

基于时间的

例如,每隔五分钟

基于性能的

例如,每当模型性能急剧下降时

基于容量的

例如,每当标记数据总量增加了 5%时

基于漂移的

例如,每当检测到主要的数据分布变化时

要使此触发机制正常工作,您将需要一个稳固的监控解决方案。我们在“监控和可观察性”部分讨论过,困难不在于检测变化,而在于确定哪些变化真正重要。如果您的监控解决方案提供了大量虚警,您的模型最终将比需要的更频繁地更新。

您还需要一个稳固的流水线来持续评估您的模型更新。编写一个更新模型的函数与第 3 阶段所做的事情并无太大不同。困难的部分在于确保更新后的模型正常工作。在“生产中测试”部分,我们将介绍您可以使用的各种测试技术。

更新模型的频率

现在,您的基础设施已经设置好快速更新模型,您开始思考一个一直困扰各种公司 ML 工程师的问题:“我应该多频繁地更新我的模型?”在试图回答这个问题之前,我们首先需要弄清楚您的模型通过使用新鲜数据进行更新可以获得多少收益。您的模型能够从更新数据中获得的收益越大,就应该更频繁地重新训练。

数据新鲜度的价值

如果我们知道更新后模型性能将如何提高,那么确定多频繁更新模型的问题将变得更加容易。例如,如果我们从每月重新训练模型切换到每周重新训练,我们可以获得多少性能提升?如果我们切换到每日重新训练呢?人们一直说数据分布会变化,因此新鲜数据更好,但新鲜数据究竟好多少?

了解增益的一种方法是,通过在过去不同时间窗口的数据上训练模型,并在今天的数据上评估,看看性能如何变化。例如,假设您有来自 2020 年的数据。为了衡量数据新鲜度的价值,您可以尝试在模型版本 A 上训练从 2020 年 1 月到 6 月的数据,模型版本 B 上训练从 4 月到 9 月的数据,以及模型版本 C 上训练从 6 月到 11 月的数据,然后在 12 月对每个模型版本进行测试,如图 9-5 所示。这些版本之间的性能差异将让您感受到模型从更新数据中可以获得的性能提升。如果一个季度前训练的模型远远不如一个月前训练的模型,那么您知道不应该等一个季度再重新训练模型。

图 9-5. 为了感受从更新数据中获得的性能提升,您可以在过去不同时间窗口的数据上训练模型,然后在今天的数据上进行测试,看看性能如何变化。

这是一个简单的示例,说明数据新鲜度实验的工作原理。实际上,你可能希望你的实验更加细致,操作不是以月为单位,而是以周、天,甚至小时或分钟为单位。2014 年,Facebook 对广告点击率预测进行了类似的实验,发现他们可以通过从每周重新训练到每天重新训练来减少模型损失 1%,而这种性能提升对他们来说足够显著,以至于他们将重新训练的流程从每周改为每天。²⁵ 鉴于今天的在线内容更加多样化,用户的在线注意力变化更快,我们可以想象,对于广告点击率来说,数据新鲜度的价值甚至更高。一些拥有复杂 ML 基础设施的公司已经发现,通过每隔几分钟重新训练模型可以获得足够的性能提升,因此他们将重新训练的流程改为每隔几分钟进行一次。²⁶

模型迭代与数据迭代

在本章的早些时候我们讨论过,并不是所有的模型更新都是相同的。我们区分了模型迭代(向现有模型架构添加新特征或更改模型架构)和数据迭代(相同的模型架构和特征,但使用新数据刷新模型)。你可能不仅想知道更新模型的频率,还想知道进行何种类型的模型更新。

理论上,你可以同时进行这两种类型的更新,实际上,你应该定期进行这两种更新。然而,你在一种方法上投入的资源越多,你可以在另一种方法上投入的资源就越少。

一方面,如果你发现对数据进行迭代并不能带来太大的性能提升,那么你应该将资源用于寻找更好的模型。另一方面,如果寻找更好的模型架构需要 100 倍的计算资源进行训练,并且仅带来 1%的性能提升,而使用过去三小时数据更新同一模型只需要 1 倍计算资源,同样带来 1%的性能提升,你最好选择对数据进行迭代。

或许在不久的将来,我们会更多地理解在何种情况下某种方法会更有效(暗示“寻求研究”),但截至今日,没有一本书能为你的特定模型在特定任务上哪种方法更好提供答案。你必须进行实验来找出答案。

确定模型更新频率的问题是一个难以回答的问题,我希望本节已足够解释了其中的微妙之处。在开始阶段,当你的基础设施尚不成熟且模型更新的过程是手动且缓慢的时候,答案是:尽可能频繁。

但是,随着您的基础设施成熟和更新模型的过程部分自动化,可以在几小时甚至几分钟内完成,如果不是分钟,回答这个问题的答案取决于以下问题的答案:“从更新的数据中我会得到多少性能增益?”运行实验来量化数据新鲜度对您的模型的价值非常重要。

在生产中进行测试

在本书的整个内容中,包括本章,我们已经谈论过部署尚未充分评估的模型的危险。要充分评估您的模型,首先需要离线评估和本章讨论的在线评估的混合。要理解为什么离线评估不足够,让我们简要介绍离线评估的两种主要测试类型:测试拆分和后测。

您可能会考虑的第一种模型评估是您可以使用其离线评估模型的老式测试拆分,如第六章中讨论的第六章。这些测试拆分通常是静态的,也必须是静态的,以便您有一个可信赖的基准来比较多个模型。如果在不同的测试集上测试了两个模型的测试结果,将很难进行比较。

但是,如果更新模型以适应新的数据分布,仅在来自旧分布的测试拆分上评估此新模型是不够的。假设数据越新鲜,可能来自当前分布,一个想法是在您能访问的最新数据上测试您的模型。因此,在您更新了最后一天数据的模型后,您可能希望在最后一小时的数据上测试此模型(假设最后一小时的数据未包含在用于更新模型的数据中)。在过去的特定时间段内测试预测模型的方法称为后测

是否后测是充分替代静态测试拆分的问题?并不完全如此。如果您的数据管道出现问题,并且最后一小时的一些数据已损坏,则仅评估这些最新数据的模型是不够的。

使用后测,您仍然应该在一个您已经广泛研究并(大多数情况下)信任的静态测试集上评估您的模型,作为一种理智检查的形式。

由于数据分布会发生变化,一个模型在最近一小时的数据上表现良好并不意味着它将在未来的数据上继续表现良好。了解一个模型在生产中是否表现良好的唯一方法是部署它。这一洞察力促使了一个看似可怕但必要的概念:在生产环境中测试。然而,在生产环境中进行测试并不一定可怕。有一些技术可以帮助你相对安全地评估你的模型。在本节中,我们将涵盖以下技术:影子部署,A/B 测试,金丝雀分析,交替实验和自动化分析。

影子部署

影子部署可能是部署您的模型或任何软件更新最安全的方法。影子部署的工作原理如下:

  1. 并行部署候选模型与现有模型。

  2. 对于每个进入的请求,将其路由到两个模型以进行预测,但仅向用户提供现有模型的预测结果。

  3. 记录新模型的预测结果以供分析。

只有在确定新模型的预测结果令人满意后,才会用新模型替换现有模型。

因为只有在确定新模型的预测结果令人满意后才向用户提供新模型的预测结果,所以这种新模型出现异常的风险很低,至少不会高于现有模型。然而,这种技术并非始终受欢迎,因为它很昂贵。这会使系统需要生成的预测数量翻倍,这通常意味着推理计算成本的翻倍。

A/B 测试

A/B 测试是一种比较两个变体对象的方法,通常通过测试对这两个变体的响应来确定哪个变体更有效。在我们的案例中,一个变体是现有模型,另一个变体是候选模型(最近更新的模型)。我们将使用 A/B 测试来确定哪个模型更好,根据一些预定义的指标。

自 2017 年起,A/B 测试变得如此普遍,像微软和谷歌这样的公司每年都会进行超过 10,000 次 A/B 测试。²⁷ 这已经成为许多机器学习工程师在生产环境中评估模型的首选方法。A/B 测试的工作原理如下:

  1. 与现有模型一起部署候选模型。

  2. 将一部分流量路由到新模型进行预测,其余流量路由到现有模型进行预测。通常情况下,两个变体同时提供预测流量。然而,有些情况下,一个模型的预测结果可能会影响另一个模型的预测结果——例如,在共享经济的动态定价中,一个模型的预测价格可能会影响可用司机和乘客的数量,进而影响另一个模型的预测结果。在这些情况下,可能需要交替运行变体,例如,一天提供模型 A 的服务,然后第二天提供模型 B 的服务。

  3. 监控和分析预测结果及用户反馈(如果有的话),以确定两个模型在性能上的差异是否具有统计显著性。

要正确进行 A/B 测试,需要做很多事情正确。在本书中,我们将讨论两个重要的事情。首先,A/B 测试包括一个随机化实验:路由到每个模型的流量必须是真正随机的。如果不是,测试结果将是无效的。例如,如果流量路由到两个模型的方式存在选择偏差,比如接触模型 A 的用户通常使用手机,而接触模型 B 的用户通常使用台式机,那么如果模型 A 的准确率比模型 B 更高,我们无法确定是因为 A 比 B 更好,还是因为“使用手机”影响了预测质量。

第二,你的 A/B 测试应该运行在足够数量的样本上,以获得对结果的足够信心。如何计算 A/B 测试所需的样本数量是一个简单的问题,但是答案却非常复杂,我建议读者参考一本关于 A/B 测试的书籍来了解更多。

这里的要点是,如果你的 A/B 测试结果显示一个模型比另一个显著优越,你可以确定哪个模型确实更好。为了测量统计显著性,A/B 测试使用诸如双样本检验之类的统计假设检验。我们在第八章中看到了双样本检验,当时我们用它们来检测分布的变化。作为提醒,双样本检验是一种用来确定这两个总体之间差异是否具有统计显著性的检验。在分布变化的用例中,如果统计差异表明这两个总体来自不同的分布,这意味着原始分布已经发生了变化。在 A/B 测试的用例中,统计差异意味着我们已经收集到足够的证据表明一个变体比另一个变体更好。

统计显著性虽然有用,但并非绝对可靠。比如我们进行了一次双样本检验,得到结果称模型 A 比模型 B 更好,其 p-值为 p = 0.05 或 5%,我们定义统计显著性为 p ≤ 0.5. 这意味着如果我们多次运行相同的 A/B 测试实验,(100 – 5 =)95% 的时间我们将得到 A 比 B 更好的结果,而另外 5% 的时间则是 B 比 A 更好。因此,即使结果具有统计显著性,如果我们再次运行实验,可能会选择另一个模型。

即使你的 A/B 测试结果不具有统计显著性,并不意味着这个 A/B 测试失败了。如果你已经用大量样本运行了 A/B 测试,并且两个测试模型之间的差异在统计上不显著,可能这两个模型之间并没有太大的差异,你可以随意选择其中一个模型使用。

对于对学习更多关于 A/B 测试和其他在机器学习中重要的统计概念感兴趣的读者,我推荐 Ron Kohavi 的书 《可信在线控制实验(A/B 测试实用指南)》(剑桥大学出版社)和 Michael Barber 的很棒的统计入门指南,专为数据科学(简短得多)。

在生产环境中,通常不仅有一个候选模型,而是有多个候选模型。可以使用多个变体进行 A/B 测试,这意味着我们可以进行 A/B/C 测试甚至 A/B/C/D 测试。

金丝雀发布

金丝雀发布是一种通过在生产环境中缓慢推出新软件版本的技术,首先将更改逐步推广到少量用户,然后再将其推广到整个基础架构并向所有人提供。(参见 28)。在 ML 部署的上下文中,金丝雀发布的工作方式如下:

  1. 部署候选模型并行于现有模型。候选模型称为金丝雀。

  2. 部分流量被路由到候选模型。

  3. 如果其性能令人满意,请增加流量到候选模型。如果不满意,请中止金丝雀,并将所有流量重新路由到现有模型。

  4. 当金丝雀服务所有流量(候选模型已替换现有模型)或金丝雀被中止时停止。

候选模型的性能根据您关心的指标与现有模型的性能进行比较。如果候选模型的关键指标显著下降,金丝雀会被中止,所有流量将路由到现有模型。

金丝雀发布可用于实施 A/B 测试,因为它们的设置相似。但是,您可以在没有 A/B 测试的情况下进行金丝雀分析。例如,您不必随机化流量以路由到每个模型。一个合理的情况是,您可以首先将候选模型推广到一个不那么关键的市场,然后再推广到所有人。

对于对行业中金丝雀发布工作原理感兴趣的读者,Netflix 和 Google 在如何在他们的公司使用自动化金丝雀分析的一篇很棒的博客文章中分享了。

交错实验

想象一下,您有两个推荐系统,A 和 B,您想评估哪个更好。每次,一个模型推荐 10 个用户可能喜欢的项目。通过 A/B 测试,您将用户分为两组:一组接触 A,另一组接触 B。每个用户将会接触到由一个模型做出的推荐。

如果我们不是向用户推荐模型的推荐内容,而是向用户展示来自两个模型的推荐内容,并查看用户会点击哪个模型的推荐内容呢?这正是交错实验的理念,最初由 Thorsten Joachims 在 2002 年提出,用于搜索排名问题。在实验中,Netflix 发现,“与传统的 A/B 测试相比,交错测试能够以明显较小的样本量可靠地识别出最佳算法。”³⁰

图 9-6 展示了交错测试与 A/B 测试的不同之处。在 A/B 测试中,像留存率和流媒体这样的核心指标是通过对比两组数据来衡量的。在交错测试中,可以通过测量用户偏好来比较两种算法。因为交错测试可以根据用户偏好来决定,所以不能保证用户偏好将导致更好的核心指标。

图 9-6. 交错与 A/B 测试的说明。来源:根据 Parks et al.的图片改编。

当我们向用户展示多个模型的推荐内容时,重要的是要注意推荐的位置会影响用户点击的可能性。例如,用户更有可能点击顶部的推荐而不是底部的推荐。为了确保交错测试能产生有效的结果,我们必须确保在任何给定位置,推荐被 A 或 B 生成的概率是相等的。为了确保这一点,我们可以使用团队选拔交错方法,模仿体育中的选秀过程。对于每个推荐位置,我们以相等的概率随机选择 A 或 B,并且被选择的模型选择尚未被选中的顶部推荐。如何团队选拔方法工作的可视化图示在 图 9-7 中显示。

图 9-7. 使用团队选拔方法交错两种排名算法的视频推荐。来源:Parks et al.³²

老丨虎丨机

对于不熟悉的人来说,赌博算法起源于赌博。赌场有多台不同赔率的老丨虎丨机。老丨虎丨机也被称为单臂强盗,因此得名。你不知道哪台老丨虎丨机会给出最高的赔率。你可以随着时间的推移进行实验,找出哪台老丨虎丨机是最好的,同时最大化你的赔率。多臂老丨虎丨机算法是一种允许你在开发(选择过去支付最多的老丨虎丨机)和探索(选择其他可能支付更多的老丨虎丨机)之间取得平衡的算法。

目前,生产环境中测试模型的标准方法是 A/B 测试。在 A/B 测试中,你会随机将流量路由到每个模型进行预测,并在试验结束时测量哪个模型效果更好。A/B 测试是无状态的:你可以将流量路由到每个模型,而无需了解它们当前的性能。即使在批处理预测中,也可以进行 A/B 测试。

当你有多个模型需要评估时,每个模型都可以看作是一个老丨虎丨机,其回报(即预测准确性)你并不知道。而赌博算法允许你确定如何将流量路由到每个模型进行预测,以确定最佳模型,同时最大化用户的预测准确性。赌博算法是有状态的:在将请求路由到模型之前,你需要计算所有模型的当前性能。这需要三个步骤:

  • 您的模型必须能够进行在线预测。

  • 最好是短反馈循环:你需要获取关于预测是否好的反馈。这通常适用于可以从用户反馈中确定标签的任务,比如推荐系统——如果用户点击了一个推荐,可以推断它是好的。如果反馈循环很短,你可以快速更新每个模型的回报。

  • 一种收集反馈、计算并跟踪每个模型性能,并基于它们当前的性能将预测请求路由到不同模型的机制。

赌博算法在学术界已经有了深入研究,并且已经被证明比 A/B 测试更加数据高效(在许多情况下,赌博算法甚至是最优的)。赌博算法需要更少的数据来确定哪个模型是最好的,同时通过更快地将流量路由到更好的模型来减少机会成本。有关赌博算法的讨论,请参见LinkedIn, Netflix, Facebook, and DropboxZillow,以及Stitch Fix。更多理论视角,请参见《强化学习》(Sutton 和 Barto,2020)的第二章。

在 Google 的 Greg Rafferty 进行的实验中,进行 A/B 测试需要超过 630,000 个样本才能获得 95%的置信区间,而简单的赌博算法(汤普森采样)仅需不到 12,000 个样本就能确定一个模型比另一个好 5%³³。

然而,与 A/B 测试相比,赌博算法实施起来更加困难,因为它需要计算和跟踪模型的回报。因此,除了少数大型科技公司外,赌博算法在行业内并不广泛使用。

上下文赌博作为一种探索策略

如果用于模型评估的老丨虎丨机来确定每个模型的支付(即预测准确性),那么上下文老丨虎丨机将用于确定每个动作的支付。在推荐/广告的情况下,一个动作是向用户展示一个项目/广告,支付是用户点击它的可能性。像其他老丨虎丨机一样,上下文老丨虎丨机是提高模型数据效率的一种神奇技术。

警告

有些人也称用于模型评估的老丨虎丨机为“上下文老丨虎丨机”。这使得对话变得混乱,在本书中,“上下文老丨虎丨机”指的是探索策略,用于确定预测的支付。

想象一下,您正在构建一个包含 1,000 个推荐项的推荐系统,这使它成为一个 1,000 臂老丨虎丨机问题。每次,您只能向用户推荐排名前 10 的最相关项目。在老丨虎丨机的术语中,您将不得不选择最好的 10 个臂。展示的项目会得到用户的反馈,通过用户是否点击它们来推断。但您将不会得到其他 990 个项目的反馈。这被称为部分反馈问题,也称为老丨虎丨机反馈。您还可以将上下文老丨虎丨机视为带有老丨虎丨机反馈的分类问题。

每当用户点击一个项目时,该项目就会获得 1 个价值点。当一个项目的价值点为 0 时,可能是因为该项目从未展示给用户,也可能是展示过但没有被点击。您希望向用户展示对他们最有价值的项目,但如果您只展示具有最多价值点的项目,那么您将继续推荐相同的热门项目,而从未展示的项目将始终保持 0 个价值点。

上下文老丨虎丨机是一种帮助您在向用户展示他们喜欢的项目和向他们展示您想要反馈的项目之间取得平衡的算法。³⁶ 这与许多读者在强化学习中可能遇到的探索-开发权衡是相同的。上下文老丨虎丨机也称为“一次性”强化学习问题。³⁷ 在强化学习中,您可能需要在看到奖励之前执行一系列动作。在上下文老丨虎丨机中,您可以在执行动作后立即获得老丨虎丨机反馈,例如,在推荐广告后,您会得到用户是否点击推荐的反馈。

上下文匪徒已经得到了广泛的研究,并且已经被证明可以显著提高模型的性能(请参阅TwitterGoogle的报告)。然而,上下文匪徒的实施甚至比模型匪徒更加困难,因为探索策略取决于机器学习模型的架构(例如,它是决策树还是神经网络),这使得它在不同用例中的泛化能力较差。对于希望将上下文匪徒与深度学习结合的读者,应该查看 Twitter 团队撰写的一篇出色论文:“深度贝叶斯匪徒:在线个性化推荐中的探索”(Guo 等人,2020 年)。

在我们结束本节之前,有一点我想强调。我们已经讨论了多种用于机器学习模型的测试类型。然而,重要的是要注意,一个好的评估流程不仅仅是关于要运行哪些测试,还包括谁来运行这些测试。在机器学习中,评估过程通常由数据科学家负责——那些开发模型的人也负责评估它。数据科学家倾向于使用他们喜欢的测试集来临时评估他们的新模型。首先,这个过程充满了偏见——数据科学家对他们的模型有着大多数用户不具备的背景知识,这意味着他们可能不会像大多数用户那样使用这个模型。其次,临时性质意味着结果可能不稳定。一个数据科学家可能进行一系列测试并发现模型 A 比模型 B 更好,而另一个数据科学家可能会报告不同的结果。

在生产中缺乏确保模型质量的方法导致许多模型在部署后失败,进而加剧了数据科学家在部署模型时的焦虑。为了减轻这一问题,每个团队都要制定清晰的流程来评估模型:例如,要运行哪些测试,它们应该按什么顺序运行,必须通过哪些阈值才能晋级到下一个阶段。更好的做法是,这些流程应该是自动化的,并在每次有新模型更新时启动。结果应该被报告和审查,类似于传统软件工程中的持续集成/持续部署(CI/CD)过程。重要的是要理解,一个好的评估过程不仅涉及要运行哪些测试,还涉及谁来运行这些测试。

总结

本章讨论了我认为是最令人兴奋但又未被充分探索的主题之一:如何在生产中持续更新您的模型,以使其适应不断变化的数据分布。我们讨论了公司在现代化其基础架构以进行持续学习过程中可能经历的四个阶段:从手动、从头开始培训阶段到自动化、无状态的持续学习阶段。

接着,我们考察了困扰各形各色团队中机器学习工程师的问题:“我应该多频繁更新我的模型?”并督促他们考虑数据新鲜度对他们的模型的价值以及模型迭代和数据迭代之间的权衡。

与讨论的在线预测类似,第七章中持续学习需要成熟的流式处理基础设施。持续学习的训练部分可以批量进行,但在线评估部分需要流式处理。许多工程师担心流式处理难以实现且成本高昂。在三年前这是事实,但自那时以来流处理技术已经显著成熟。越来越多的公司正在提供解决方案,使公司更容易转向流处理,包括 Spark Streaming、Snowflake Streaming、Materialize、Decodable、Vectorize 等。

持续学习是机器学习特有的问题,但它在很大程度上需要基础设施解决方案。为了加快迭代周期并迅速检测新模型更新的失败,我们需要以正确的方式设置基础设施。这需要数据科学/机器学习团队与平台团队合作。我们将在下一章讨论 ML 基础设施。

¹ 乔安·塞拉(Joan Serrà)、迪达克·苏里斯(Dídac Surís)、马里乌斯·米隆(Marius Miron)和亚历山德罗斯·卡拉佐阿尔(Alexandros Karatzoglou),“用于任务的强注意力克服灾难性遗忘”,arXiv,2018 年 1 月 4 日,https://oreil.ly/P95EZ

² 这里的名称是“有状态训练”,而不是“有状态重新训练”,因为这里没有重新训练。模型继续从上一个状态训练。

³ 艾利克斯·埃格(Alx Egg),“Grubhub 的推荐在线学习”, arXiv,2021 年 7 月 15 日,https://oreil.ly/FBBUw

⁴ 李睦,周莉,杨子超,李傲然,夏飞,戴维·G·安德森,亚历山大·斯莫拉,“分布式机器学习的参数服务器”(NIPS 大规模学习研讨会,2013 年,塔霍湖,加州),https://oreil.ly/xMmru

⁵ 乔纳森·赖曼(Jonathan Raiman)、苏珊·张(Susan Zhang)和克里斯蒂·丹尼森(Christy Dennison),“神经网络外科手术与集合”, arXiv,2019 年 12 月 13 日,https://oreil.ly/SU0F1

⁶ 这种问题也被称为“动态定价”问题。

⁷ 乔恩·拉塞尔(Jon Russell),“阿里巴巴以 1.03 亿美元收购德国大数据初创公司 Data Artisans,” TechCrunch,2019 年 1 月 8 日,https://oreil.ly/4tf5c。一位早期审阅者指出,这次收购的主要目标也可能是增加阿里巴巴的开源足迹,这与其他科技巨头相比很少。

⁸ 如果你希望你的模型能够找出何时推荐尚无人观看和反馈的新电影,那么这个问题同样具有挑战性。

⁹ Lucas Bernardi, Jaap Kamps, Julia Kiseleva, 和 Melanie J. I. Müller, “电子商务推荐系统中的连续冷启动问题,” arXiv, 2015 年 8 月 5 日, https://oreil.ly/GWUyD

¹⁰ Jacopo Tagliabue, Ciro Greco, Jean-Francis Roy, Bingqing Yu, Patrick John Chia, Federico Bianchi, 和 Giovanni Cassani, “SIGIR 2021 电子商务研讨会数据挑战,” arXiv, 2021 年 4 月 19 日, https://oreil.ly/8QxmS

¹¹ Catherine Wang, “Why TikTok Made Its User So Obsessive? The AI Algorithm That Got You Hooked,” Towards Data Science, 2020 年 6 月 7 日, https://oreil.ly/BDWf8

¹² 见章节 “实时传输中的数据传递”。

¹³ 见章节 “批处理与流处理的比较”。

¹⁴ Tyler Akidau, “Snowflake Streaming: Now Hiring! Help Design and Build the Future of Big Data and Stream Processing,” Snowflake 博客, 2020 年 10 月 26 日, https://oreil.ly/Knh2Y

¹⁵ Arjun Narayan, “Materialize Raises a $60M Series C, Bringing Total Funding to Over $100M,” Materialize, 2021 年 9 月 30 日, https://oreil.ly/dqxRb

¹⁶ Khristopher J. Brooks, “Disparity in Home Lending Costs Minorities Millions, Researchers Find,” CBS News, 2019 年 11 月 15 日, https://oreil.ly/SpZ1N; Lee Brown, “Tesla Driver Killed in Crash Posted Videos Driving Without His Hands on the Wheel,” New York Post, 2021 年 5 月 16 日, https://oreil.ly/uku9S; “A Tesla Driver Is Charged in a Crash Involving Autopilot That Killed 2 People,” NPR, 2022 年 1 月 18 日, https://oreil.ly/WWaRA

¹⁷ James Vincent, “Twitter Taught Microsoft’s Friendly AI Chatbot to Be a Racist Asshole in Less Than a Day,” The Verge, 2016 年 5 月 24 日, https://oreil.ly/NJEVF

¹⁸ 他们的欺诈检测系统由多个机器学习模型组成。

¹⁹ 在章节 “赌徒算法” 中,我们将学习如何将赌徒算法作为比 A/B 测试更高效的选择。

²⁰ 有些人称此设置为“学习部分信息”,但“学习部分信息”指的是另一种情况,如 Gonen 等人在文章 “Subspace Learning with Partial Information” 中所述。

²¹ Pedro Domingos 和 Geoff Hulten, “挖掘高速数据流,” in 第六届国际知识发现与数据挖掘会议论文集 (波士顿: ACM Press, 2000 年), 71–80; Albert Bifet 和 Ricard Gavaldà, “从演化数据流中学习的自适应无参数学习,” 2009 年, https://oreil.ly/XIMpl

²² Zohar Karnin, Kevin Lang 和 Edo Liberty, “流数据中的最优分位数近似,” arXiv, 2016 年 3 月 17 日, https://oreil.ly/bUu4H

²³ 我们将在 “ML Platform” 部分讨论 ML 平台。

²⁴ 如果每天有大量新项目,您可能需要更频繁地训练您的嵌入模型。

²⁵ Xinran He, Junfeng Pan, Ou Jin, Tianbing Xu, Bo Liu, Tao Xu, Tanxin Shi 等人, “在 Facebook 预测广告点击的实用经验,” ADKDD ’14: 数据挖掘在线广告工作坊 (2014 年 8 月): 1–9, https://oreil.ly/oS16J

²⁶ Qian Yu, “微博中的 Flink 机器学习,” QCon 2019, 视频, 17:57, https://oreil.ly/Yia6v

²⁷ Ron Kohavi 和 Stefan Thomke, “在线实验的惊人力量,” 哈佛商业评论, 2017 年 9-10 月, https://oreil.ly/OHfj0

²⁸ Danilo Sato, “CanaryRelease,” 2014 年 6 月 25 日, MartinFowler.com, https://oreil.ly/YtKJE

²⁹ Thorsten Joachims, “利用点击数据优化搜索引擎,” KDD 2002, https://oreil.ly/XnH5G

³⁰ Joshua Parks, Juliette Aurisset 和 Michael Ramm, “Netflix 在个性化算法上的快速创新,” Netflix 技术博客, 2017 年 11 月 29 日, https://oreil.ly/lnvDY

³¹ Olivier Chapelle, Thorsten Joachims, Filip Radlinski 和 Yisong Yue, “大规模交错搜索评估的验证和分析,” ACM 信息系统事务 30 卷 1 期 (2012 年 2 月): 6, https://oreil.ly/lccvK

³² Parks 等人, “Netflix 在个性化算法上的快速创新”。

³³ Greg Rafferty, "A/B 测试——是否有更好的方法?多臂老丨虎丨机的探索," 走向数据科学, 2020 年 1 月 22 日,https://oreil.ly/MsaAK.

³⁴ William R. Thompson, "在两个样本的证据视角下,一个未知概率超过另一个的可能性," 生物统计学 25, no. 3/4 (1933 年 12 月): 285–94,https://oreil.ly/TH1HC.

³⁵ Peter Auer, "利用置信区间进行开发-探索权衡," 机器学习研究杂志 3 (2002 年 11 月): 397–422,https://oreil.ly/vp9mI.

³⁶ Lihong Li, Wei Chu, John Langford, 和 Robert E. Schapire,"个性化新闻文章推荐的上下文臂带方法," arXiv, 2010 年 2 月 28 日,https://oreil.ly/uaWHm.

³⁷ 根据维基百科,多臂老丨虎丨机 是一个经典的强化学习问题,展示了开发-探索权衡困境(见“多臂老丨虎丨机”,https://oreil.ly/ySjwo)。这个名字源自想象一个赌徒在一排老丨虎丨机前(有时被称为“单臂老丨虎丨机”),他需要决定玩哪些机器,每台机器玩多少次,以及玩它们的顺序,以及是否继续当前机器还是尝试另一台机器。

第十章:MLOps 的基础设施和工具

在第四章到第六章中,我们讨论了开发机器学习系统的逻辑。在第七章到第九章中,我们讨论了部署、监控和持续更新机器学习系统的考虑因素。直到现在,我们假设机器学习从业者能够获取实施这些逻辑和考虑因素所需的所有工具和基础设施。然而,这一假设远非事实。许多数据科学家告诉我,他们知道为其机器学习系统做正确的事情,但由于他们的基础设施没有以使其能够这样做的方式设置,他们无法做到这一点。

机器学习系统是复杂的。系统越复杂,良好的基础设施就能带来的好处就越多。正确设置的基础设施可以帮助自动化流程,减少对专业知识和工程时间的需求。这反过来可以加快机器学习应用程序的开发和交付,减少错误的表面积,并支持新的使用案例。然而,如果基础设施设置错误,使用起来将非常痛苦且昂贵。在本章中,我们将讨论如何为机器学习系统正确设置基础设施。

在我们深入讨论之前,重要的是要注意,每家公司的基础设施需求各不相同。您所需的基础设施取决于您开发的应用程序数量以及应用程序的专业程度。在光谱的一端,有些公司将机器学习用于临时的业务分析,例如预测他们明年新用户的数量以在季度计划会议上展示。这些公司可能不需要投资任何基础设施——Jupyter Notebooks、Python 和 Pandas 将成为它们最好的朋友。如果您只有一个简单的机器学习用例,例如用于对象检测的 Android 应用程序以展示给您的朋友,您可能也不需要任何基础设施——您只需要一个兼容 Android 的机器学习框架,比如 TensorFlow Lite。

在另一端,有些公司致力于具有独特需求的应用。例如,自动驾驶汽车有独特的准确性和延迟要求——算法必须能在毫秒内响应,并且其准确性必须接近完美,因为错误的预测可能导致严重事故。类似地,谷歌搜索具有独特的规模需求,因为大多数公司不会像谷歌那样处理每秒 63,000 个搜索查询,这相当于每小时 2.34 亿个搜索查询。¹ 这些公司可能需要开发自己的高度专业化基础设施。谷歌为搜索开发了大部分内部基础设施;特斯拉和 Waymo 等自动驾驶汽车公司也是如此。² 通常,专业化基础设施的一部分后来会被公开,并被其他公司采纳。例如,谷歌将其内部云基础设施扩展到公众领域,形成了Google Cloud Platform

在中等规模的公司中,有大多数使用机器学习应用于多个常见应用领域——欺诈检测模型、价格优化模型、客户流失预测模型、推荐系统等——在合理的规模下。所谓的“合理规模”是指那些每天处理以千兆字节和太字节为单位的数据,而不是百拍字节的公司。他们的数据科学团队可能由 10 至数百名工程师组成。³ 这个类别可能包括从 20 人的初创公司到像 Zillow 这样的公司,但不包括像 FAAAM 这样的公司。⁴ 例如,回顾 2018 年,Uber 每天向他们的数据湖添加数十太字节的数据,而 Zillow 最大的数据集每天引入 2 太字节的未压缩数据。⁵ 相比之下,即使在 2014 年,Facebook 每天也生成4 百拍字节的数据。⁶

中等规模的公司可能会受益于越来越标准化的通用机器学习基础设施(参见图 10-1)。本书将重点讨论绝大多数中等规模机器学习应用的基础设施。

图 10-1. 不同生产规模公司的基础设施需求

为了为您的需求设置正确的基础设施,理解基础设施的确切含义及其组成非常重要。根据维基百科,在物理世界中,“基础设施是支持家庭和企业可持续功能的基本设施和系统。”⁷ 在机器学习世界中,基础设施是支持机器学习系统开发和维护的基本设施集合。什么被认为是“基本设施”在不同公司之间差异很大,正如本章前面讨论的那样。在本节中,我们将讨论以下四个层次:

存储与计算

存储层是数据收集和存储的地方。计算层提供运行机器学习工作负载所需的计算资源,例如训练模型、计算特征、生成特征等。

资源管理

资源管理包括调度和编排工具,以充分利用您可用的计算资源。这一类工具的例子包括 Airflow、Kubeflow 和 Metaflow。

ML 平台

这些工具提供了帮助开发机器学习应用程序的工具,例如模型存储、特征存储和监控工具。这一类工具的例子包括 SageMaker 和 MLflow。

开发环境

这通常被称为开发环境;这是编写代码和运行实验的地方。代码需要进行版本管理和测试。实验需要进行跟踪。

这四个不同的层次显示在图 10-2 中。数据和计算是任何机器学习项目所需的基本资源,因此存储和计算层构成了任何希望应用机器学习的公司的基础设施基础。对数据科学家来说,这一层次也是最抽象的。我们将首先讨论这一层,因为这些资源最容易解释。

图 10-2. 机器学习基础设施的不同层次

开发环境是数据科学家每天必须与之交互的环境,因此对他们来说是最具体的。我们接下来将讨论这一类别,然后我们将讨论资源管理,这是数据科学家中争议较大的话题——人们仍在争论数据科学家是否需要了解这一层。因为“ML 平台”是一个相对新的概念,其不同的组件仍在成熟阶段,所以我们将在熟悉了所有其他类别之后,最后讨论这一类别。ML 平台需要公司的前期投资,但如果做得正确,它可以大大简化公司各业务用例中数据科学家的工作。

即使两家公司有完全相同的基础设施需求,根据它们在建设与购买决策方面的方法不同,它们得到的基础设施可能看起来会有所不同。我们将在本章的最后部分讨论建设与购买决策,同时也会讨论 ML 基础设施的标准化和统一抽象的希望。

让我们深入讨论吧!

存储与计算

ML 系统处理大量数据,这些数据需要存储在某个地方。存储层 是数据收集和存储的地方。在其最简单的形式下,存储层可以是硬盘驱动器(HDD)或固态硬盘(SSD)。存储层可以在一个地方,例如,你可能把所有数据存储在 Amazon S3 或 Snowflake 中,或分布在多个位置。⁸ 你的存储层可以是在私有数据中心的本地环境中,也可以是云上的。过去,公司可能试图管理自己的存储层。然而,在过去的十年里,存储层大多数已经被商品化并移到了云端。数据存储变得如此便宜,以至于大多数公司都会存储所有他们拥有的数据而不计成本。⁹ 我们在第三章中已经深入探讨了数据层,因此在本章中,我们将专注于计算层。

计算层 指的是公司可以访问的所有计算资源及其使用这些资源的机制。可用的计算资源量决定了工作负载的可扩展性。你可以把计算层看作是执行作业的引擎。在其最简单的形式下,计算层可以仅是一个单独的 CPU 核心或 GPU 核心来执行所有的计算任务。它最常见的形式是由云提供商管理的云计算,如 AWS Elastic Compute Cloud (EC2)或 GCP。

计算层通常可以分割成较小的计算单元以供并发使用。例如,一个 CPU 核心可以支持两个并发线程;每个线程作为计算单元执行自己的作业。或者多个 CPU 核心可以组合在一起形成一个更大的计算单元来执行更大的作业。可以为特定的短暂作业(例如 AWS Step Function 或 GCP Cloud Run)创建一个计算单元——该单元在作业完成后将被清除。也可以创建一个更“永久”的计算单元,例如虚拟机,不绑定于任何作业。更“永久”的计算单元有时被称为“实例”。

然而,计算层并不总是使用线程或核心作为计算单元。有些计算层抽象了核心的概念,并使用其他的计算单元。例如,像 Spark 和 Ray 这样的计算引擎使用“作业”作为它们的计算单元,而 Kubernetes 使用“Pod”作为最小的可部署单元,它是容器的一种封装。虽然你可以在一个 Pod 中有多个容器,但你不能独立地启动或停止同一个 Pod 中的不同容器。

要执行一个作业,你首先需要将所需的数据加载到计算单元的内存中,然后执行所需的操作——如加法、乘法、除法、卷积等。例如,要对两个数组进行加法,你首先需要将这两个数组加载到内存中,然后在这两个数组上执行加法运算。如果计算单元的内存不足以加载这两个数组,那么在没有处理内存溢出的算法的情况下,这个操作将是不可能的。因此,一个计算单元主要通过两个度量来进行特征化:它的内存容量以及它运行操作的速度。

内存度量可以使用 GB 等单位来指定,通常很容易评估:一个具有 8 GB 内存的计算单元可以在内存中处理比只有 2 GB 内存的计算单元更多的数据,并且通常更昂贵。一些公司不仅关心计算单元的内存容量,还关心加载数据进出内存的速度,因此一些云服务提供商宣传他们的实例具有“高带宽内存”或指定他们实例的 I/O 带宽。

运算速度是一个更具争议性的问题。最常见的度量标准是 FLOPS(每秒浮点运算次数)。顾名思义,这个度量标准表示计算单元每秒可以运行的浮点运算次数。你可能会看到硬件供应商宣传他们的 GPU、TPU 或 IPU(智能处理单元)拥有 TeraFLOPS(一万亿 FLOPS)或其他大量 FLOPS 的数据。

然而,这个度量标准是有争议的,首先,衡量此度量标准的公司可能对什么算作一个操作有不同的理解。例如,如果一台机器将两个操作融合成一个并执行这个融合操作,¹¹ 这算作一个操作还是两个操作?其次,仅仅因为一个计算单元能够执行一万亿次 FLOPS,并不意味着你能以一万亿 FLOPS 的速度执行你的作业。作业可以运行的 FLOPS 数量与计算单元能够处理的 FLOPS 数量的比率被称为利用率。¹² 如果一个实例能够做一百万次 FLOPS,而你的作业以 0.3 百万次 FLOPS 运行,那就是 30% 的利用率。当然,你希望将利用率尽可能提高,但是几乎不可能达到 100% 的利用率。根据硬件后端和应用程序,50% 的利用率可能被认为是好的或者是坏的。利用率还取决于你能够多快地将数据加载到内存中以执行下一个操作——这也解释了 I/O 带宽的重要性。¹³

在评估新的计算单元时,评估这个计算单元完成常见工作负载所需的时间是很重要的。例如,MLPerf 是一个流行的基准测试,用于硬件供应商衡量其硬件性能,展示其硬件在 ImageNet 数据集上训练 ResNet-50 模型或使用 BERT-large 模型生成 SQuAD 数据集预测所需的时间。

想到 FLOPS 并不是很有用,为了简化事情,在评估计算性能时,很多人只关注计算单元的核心数。因此,你可能会选择一个有 4 个 CPU 核心和 8 GB 内存的实例。请记住,AWS 使用 vCPU 的概念,代表虚拟 CPU,对于实际目的而言,可以视为半个物理核心。¹⁴ 你可以查看一些 AWS EC2 和 GCP 实例提供的核心数和内存情况,请参见 Figure 10-3。

图 10-3. AWS 和 GCP 上可用的 GPU 和 TPU 实例示例(截至 2022 年 2 月)。来源:AWS 和 GCP 网站的截图

公共云与私有数据中心

与数据存储类似,计算层主要是商品化的。这意味着公司可以支付像 AWS 和 Azure 这样的云服务提供商,按照他们实际使用的计算量付费,而不是为存储和计算设立自己的数据中心。云计算使得公司能够轻松开始构建而无需担心计算层。对于那些工作负载大小可变的公司来说尤为吸引。想象一下,如果你的工作负载一年中有一天需要 1000 个 CPU 核心,而其余时间只需要 10 个 CPU 核心。如果建立自己的数据中心,你需要一开始就支付 1000 个 CPU 核心的费用。而使用云计算,你只需在一年中的一天支付 1000 个 CPU 核心的费用,其余时间支付 10 个 CPU 核心的费用。能够根据需要添加更多计算资源或关闭实例非常便利,大多数云提供商甚至可以自动执行这些操作,从而减少工程运营的开销。这在机器学习中特别有用,因为数据科学工作负载是爆发性的。在开发过程中,数据科学家倾向于连续几周运行实验,这需要大量计算能力。在生产阶段,工作负载则更加稳定。

请记住,云计算是弹性的,但并非神奇。它实际上并没有无限的计算能力。大多数云服务提供商在同时使用计算资源方面都设有限制。其中一些限制可以通过申请来解除。例如,截至撰写本书时,AWS EC2 的最大实例是X1e,拥有 128 个 vCPU 和将近 4TB 内存。¹⁵ 拥有大量计算资源并不意味着始终能轻松使用它们,尤其是在需要利用抢占式实例节省成本的情况下。¹⁶

由于云计算的弹性和易用性,越来越多的公司选择支付云服务费用,而不是建设和维护自己的存储和计算层。Synergy Research Group 的研究显示,2020 年,“企业在云基础设施服务上的支出增长了 35%,达到了近 1300 亿美元”,而“数据中心的企业支出下降了 6%,不足 900 亿美元”,如图 10-4 所示。¹⁷

图 10-4。2020 年,企业在云基础设施服务上的支出增长了 35%,而在数据中心的支出下降了 6%。资料来源:根据 Synergy Research Group 的图片调整。

尽管利用云计算在公司初期比自建存储和计算层带来更高的回报,但随着公司规模的增长,这种优势变得不那么具有防御性。根据公开软件公司披露的云基础设施支出,风险投资公司 a16z 指出,云计算支出约占这些公司营业成本的 50%。¹⁸

云端的高成本促使公司开始将其工作负载迁移回自己的数据中心,这个过程被称为“云端收归”。Dropbox 在 2018 年的 S-1 文件 显示,该公司通过基础设施优化大规模改革,节省了 IPO 前两年的 7500 万美元——其中大部分是将其工作负载从公共云迁移到自己的数据中心。云端的高成本是否是独特于 Dropbox,因为 Dropbox 是数据存储业务?并非完全如此。在上述分析中,a16z 估计,“在当前利用云基础设施的 50 家顶级公共软件公司中,由于云端对利润率的影响,它们的市值损失达到了约 1000 亿美元。”¹⁹

尽管开始使用云端服务很容易,但是远离云端却很困难。云端收归需要在商品和工程工作上做出重大的前期投资。越来越多的公司正在采取混合方法:将大部分工作负载保留在云端,但逐渐增加在数据中心的投资。

开发环境

开发环境是机器学习工程师编写代码、运行实验并与部署冠军模型和评估挑战者模型的生产环境进行交互的地方。开发环境包括以下组件:集成开发环境(IDE)、版本控制和持续集成/持续交付(CI/CD)。

如果你是一名每天编写代码的数据科学家或机器学习工程师,你可能对所有这些工具非常熟悉,可能会想知道还有什么可以说的。根据 Ville Tuulos 在他的书 Effective Data Science Infrastructure 中的说法,“你会惊讶地知道有多少公司拥有良好调优、可扩展的生产基础设施,但在开发、调试和测试代码的问题上却采取了临时性的解决方案。”²²

他建议,“如果你只有时间来好好设置一个基础设施,那就让它成为数据科学家的开发环境。” 因为开发环境是工程师工作的地方,开发环境的改进直接转化为工程生产力的提升。

在本节中,我们首先将介绍开发环境的不同组件,然后讨论开发环境的标准化,最后再讨论如何通过容器将您的更改从开发环境带入生产环境。

开发环境设置

开发环境应该设置包含所有可以使工程师工作更轻松的工具。它还应包括用于版本控制的工具。截至撰写本文时,公司使用一套特定的工具来对其 ML 工作流进行版本控制,例如使用 Git 进行代码版本控制,使用 DVC 对数据进行版本控制,使用 Weights & Biases 或 Comet.ml 来跟踪开发过程中的实验,以及使用 MLflow 来跟踪模型部署时的工件。Claypot AI 正在开发一个平台,可以帮助您在一个地方版本化和跟踪所有的 ML 工作流。对于任何软件工程项目来说,版本控制都非常重要,但对于 ML 项目来说更是如此,因为您可以改变的东西(代码、参数、数据本身等)数量庞大,并且需要跟踪以复制后续运行的先前运行。我们在章节“实验跟踪和版本控制”中已经讨论过这一点。

开发环境还应该设置一个CI/CD测试套件,以在推送到暂存或生产环境之前测试您的代码。用于编排您的 CI/CD 测试套件的工具示例包括 GitHub Actions 和 CircleCI。因为 CI/CD 是一个软件工程问题,它超出了本书的范围。

在本节中,我们将专注于工程师编写代码的地方:IDE。

IDE

IDE 是您编写代码的编辑器。IDE 通常支持多种编程语言。IDE 可以是像 VS Code 或 Vim 这样的本地应用程序。IDE 也可以是基于浏览器的,这意味着它们在浏览器中运行,比如 AWS Cloud9。

许多数据科学家不仅在 IDE 中编写代码,还在 Jupyter Notebooks 和 Google Colab 这样的笔记本中编写代码。²³ 笔记本不仅仅是编写代码的地方。您可以包含任意的工件,如图像、图表、以及漂亮的表格格式的数据等,这使得笔记本在探索性数据分析和分析模型训练结果方面非常有用。

笔记本有一个很好的特性:它们是有状态的——在运行后可以保留状态。如果您的程序中途失败,您可以重新从失败的步骤运行,而不必重新从头运行程序。这在处理可能需要长时间加载的大型数据集时特别有帮助。使用笔记本,您只需加载数据一次——笔记本可以在内存中保留这些数据——而不必每次运行代码时都加载它。如图 10-5,如果您在笔记本的步骤 4 中代码失败,您只需重新运行步骤 4,而不是从程序开始处重新运行。

图 10-5. 在 Jupyter Notebooks 中,如果步骤 4 失败了,您只需再次运行步骤 4,而不是必须重新运行步骤 1 到 4。

请注意,这种状态性可能是一把双刃剑,因为它允许您无序执行单元格。例如,在普通脚本中,单元格 4 必须在单元格 3 之后运行,而单元格 3 必须在单元格 2 之后运行。然而,在笔记本中,您可以按顺序运行单元格 2、3、然后 4,或者单元格 4、3、然后 2。这使得笔记本的可重现性更加困难,除非您的笔记本附带运行单元格顺序的说明。克里斯·奥尔本通过一个笑话捕捉到了这种困难(参见图 10-6)。

图 10-6. 笔记本的状态性允许您无序执行单元格,使得重现笔记本变得困难

由于笔记本在数据探索和实验中非常有用,它们已经成为数据科学家和机器学习不可或缺的工具。一些公司已经将笔记本作为其数据科学基础设施的核心。在他们的重要文章《超越互动:Netflix 的笔记本创新》中,Netflix 包括了一系列基础设施工具,可以用来使笔记本更加强大。²⁴ 列表包括:

Papermill

用于生成具有不同参数集的多个笔记本——例如,当您希望使用不同参数集运行不同实验并并行执行时。它还可以帮助总结一系列笔记本的指标。

Commuter

一个笔记本中心,用于在组织内查看、查找和共享笔记本。

另一个旨在改善笔记本体验的有趣项目是nbdev,这是一个建立在 Jupyter Notebooks 之上的库,鼓励您在同一地方编写文档和测试。

标准化开发环境

关于开发环境的第一件事是,它应该是标准化的,如果不是公司范围内,至少应该是团队范围内。我们将讲述一个故事来理解什么是标准化开发环境以及为什么需要它。

在我们创业初期,每个人都从自己的电脑上工作。我们有一个 bash 文件,新团队成员可以运行它来创建一个新的虚拟环境——在我们的情况下,我们使用 conda 来创建虚拟环境——并安装运行我们代码所需的包。所需包的列表是我们不断添加的旧requirements.txt。有时候,我们中的某个人会懒惰,只是添加了一个包的名称(例如torch),而没有指定包的版本(例如torch==1.10.0+cpu)。偶尔,一个新的拉取请求在我的电脑上运行良好,但在另一个同事的电脑上却不行,²⁵ 我们通常很快就发现,这是因为我们使用了不同版本的同一个包。我们决定,每次向requirements.txt添加新包时,总是同时指定包名称和包版本,这样就消除了很多不必要的头疼。

有一天,我们遇到了一个奇怪的 bug,只在某些运行时出现而不在其他运行时出现。我让我的同事去调查,但他无法重现这个 bug。我告诉他这个 bug 只会偶尔出现,所以他可能需要运行代码大约 20 次来确认。他运行了 20 次代码,仍然没有发现任何问题。我们比较了我们的包,一切都匹配。经过几个小时的令人头痛的挫折后,我们发现这是一个并发问题,只有在 Python 版本 3.8 或更早版本中才会出现问题。我使用的是 Python 3.8,而我的同事使用的是 Python 3.9,所以他没有看到这个 bug。我们决定让所有人都使用相同的 Python 版本,这消除了更多的头痛。

然后有一天,我的同事换了一台新笔记本电脑。那是一台搭载当时新款 M1 芯片的 MacBook。他试图在这台新笔记本电脑上按照我们的设置步骤进行设置,但遇到了困难。那是因为 M1 芯片是新的,而我们使用的一些工具,包括 Docker,与 M1 芯片的兼容性还不够好。在看到他一整天都在为设置环境而苦苦挣扎后,我们决定转向云开发环境。这意味着我们仍然标准化虚拟环境、工具和包,但现在每个人都使用由云提供商提供的相同类型的虚拟环境、工具和包。

使用云开发环境时,您可以选择具备云 IDE 的云开发环境,如AWS Cloud9(不含内置笔记本)和Amazon SageMaker Studio(带有托管的 JupyterLab)。截至撰写本书时,Amazon SageMaker Studio 似乎比 Cloud9 更广泛使用。然而,我认识的大多数使用云 IDE 的工程师是通过在他们的云实例上安装喜爱的 IDE(如 Vim)来使用的。

更受欢迎的选项是使用具备本地集成开发环境的云开发环境。例如,您可以在计算机上安装 VS Code,并通过安全协议如 SSH 将本地 IDE 连接到云环境。

虽然一般认为工具和包应该标准化,但一些公司对标准化 IDE 持怀疑态度。工程师可能会对 IDE 产生情感依恋,并有人竭力捍卫他们所选择的 IDE²⁶,因此强迫每个人使用同一种 IDE 是很难的。然而,多年来,一些 IDE 已经成为最受欢迎的选择。其中,VS Code 是一个不错的选择,因为它可以轻松集成云开发实例。

在我们的创业公司,我们选择了GitHub Codespaces作为我们的云开发环境,但 AWS EC2 或可以通过 SSH 访问的 GCP 实例也是一个不错的选择。在转移到云环境之前,和许多其他公司一样,我们担心成本——如果我们忘记在不使用时关闭实例会怎么样?然而,这种担忧因为两个原因而消失了。首先,像 GitHub Codespaces 这样的工具在 30 分钟不活动后会自动关闭你的实例。其次,有些实例价格非常便宜。例如,一个具有 4 个 vCPU 和 8GB 内存的 AWS 实例每小时约$0.1,如果你从不关闭它,一个月的费用大约为$73。因为工程时间很宝贵,如果云开发环境可以帮助你每月节省几小时的工程时间,对许多公司来说是值得的。

从本地开发环境迁移到云开发环境还有很多其他好处。首先,它极大地简化了 IT 支持——想象一下,要支持 1000 台不同的本地机器,而不是只需要支持一种云实例。其次,它对于远程工作非常方便——你可以从任何计算机上通过 SSH 访问你的开发环境。第三,云开发环境可以帮助提升安全性。例如,如果某位员工的笔记本被盗,你可以立即取消该笔记本对云实例的访问权限,以防止窃贼访问你的代码库和专有信息。当然,一些公司可能由于安全顾虑而无法转移到云开发环境。例如,他们不允许将他们的代码或数据存储在云上。

第四个好处,我认为对于在云上进行生产的公司来说是最大的好处,就是将开发环境放在云端可以缩小开发环境和生产环境之间的差距。如果你的生产环境在云端,将开发环境移到云端就是理所当然的事情。

偶尔,公司不得不将他们的开发环境转移到云端,不仅因为它的好处,更是因为必要性。对于那些数据无法下载或存储在本地机器上的用例,唯一的访问途径是通过可以从 S3 读取数据的云笔记本(SageMaker Studio),前提是它具备适当的权限。

当然,并非每家公司都适合使用云开发环境,可能是因为成本、安全性或其他原因。设置云开发环境也需要一些初期投资,你可能需要教育你的数据科学家关于云卫生学,包括建立安全连接到云、安全合规性或避免浪费的云使用等方面。然而,标准化开发环境可能会让你的数据科学家生活更轻松,并在长远节省资金。

从开发到生产:容器

在开发过程中,您可能通常会使用固定数量的机器或实例(通常是一个),因为您的工作负载不会波动很大——您的模型不会突然从每小时仅处理 1,000 个请求变成每小时处理 1 百万个请求。

另一方面,生产服务可能分布在多个实例上。实例数量根据传入的工作负载不时地变化,有时可能是不可预测的。例如,一位名人在推特上发表了关于您新兴应用程序的推文,突然间您的流量增加了 10 倍。您将需要根据需要打开新实例,并且这些实例需要设置所需的工具和包以执行您的工作负载。

以前,您必须自己启动和关闭实例,但大多数公共云提供商已经负责自动缩放部分。但是,您仍然需要担心设置新实例。

当您始终使用同一个实例时,您可以一次安装依赖项,并在每次使用此实例时重复使用它们。在生产环境中,如果根据需要动态分配实例,您的环境本质上是无状态的。当为您的工作负载分配一个新实例时,您将需要使用预定义指令列表安装依赖项。

一个问题是:如何在任何新实例上重新创建环境?容器技术——其中 Docker 是最流行的——就是为了回答这个问题而设计的。使用 Docker,您可以创建一个 Dockerfile,其中包含逐步说明如何重新创建能够运行您的模型的环境的指令:安装这个包,下载这个预训练模型,设置环境变量,进入文件夹等等。这些指令使得任何地方的硬件都能运行您的代码。

Docker 中的两个关键概念是镜像和容器。运行 Dockerfile 中的所有指令会生成一个 Docker 镜像。如果运行此 Docker 镜像,则会得到一个 Docker 容器。您可以将 Dockerfile 视为构建模具的配方,而这个模具就是 Docker 镜像。从这个模具中,您可以创建多个运行实例,每个实例都是一个 Docker 容器。

您可以从头开始构建 Docker 镜像,也可以从另一个 Docker 镜像开始构建。例如,NVIDIA 可能提供一个包含 TensorFlow 和所有必要库以优化 TensorFlow 用于 GPU 的 Docker 镜像。如果您想构建一个在 GPU 上运行 TensorFlow 的应用程序,使用这个 Docker 镜像作为基础,并在此基础镜像之上安装特定于您的应用程序的依赖项,是一个不错的选择。

容器注册表是您可以共享 Docker 镜像或查找其他人创建并公开或仅在其组织内分享的镜像的地方。常见的容器注册表包括 Docker Hub 和 AWS ECR(Elastic Container Registry)。

这里有一个简单的 Dockerfile 示例,展示了以下指令的运行方式。此示例旨在概述 Dockerfile 的工作原理,并非可执行示例。

  1. 下载最新的 PyTorch 基础镜像。

  2. 在 GitHub 上克隆 NVIDIA 的 apex 仓库,导航到新创建的apex文件夹,并安装 apex。

  3. fancy-nlp-project设为工作目录。

  4. 在 GitHub 上克隆 Hugging Face 的 transformers 仓库,导航到新创建的transformers文件夹,并安装 transformers。

FROM pytorch/pytorch:latest
RUN git clone https://github.com/NVIDIA/apex
RUN cd apex && \
    python3 setup.py install && \
    pip install -v --no-cache-dir --global-option="--cpp_ext" \
    --global-option="--cuda_ext" ./

WORKDIR /fancy-nlp-project
RUN git clone https://github.com/huggingface/transformers.git && \
    cd transformers && \
    python3 -m pip install --no-cache-dir.

如果你的应用程序有趣的功能,你可能需要多个容器。考虑这样一个情况,你的项目包含快速运行但需要大量内存的特征化代码,以及运行速度较慢但需要较少内存的模型训练代码。如果你在同一 GPU 实例上运行代码的两个部分,你将需要具有高内存的 GPU 实例,这可能非常昂贵。相反,你可以在 CPU 实例上运行特征化代码,并在 GPU 实例上运行模型训练代码。这意味着你需要一个用于特征化和另一个用于训练的容器。

当你的流水线中的不同步骤有冲突的依赖关系时,可能需要不同的容器,例如,你的特征提取代码需要 NumPy 0.8,但你的模型需要 NumPy 1.0。

如果你有 100 个微服务,每个微服务都需要自己的容器,你可能会同时运行 100 个容器。手动构建、运行、分配资源和停止 100 个容器可能是一项痛苦的工作。一个帮助你管理多个容器的工具称为容器编排。Docker Compose 是一种轻量级容器编排器,可以在单个主机上管理容器。

然而,每个容器可能在自己的主机上运行,这正是 Docker Compose 的极限所在。Kubernetes(K8s)正是解决这个问题的工具。K8s 为容器创建了一个通信和共享资源的网络。它可以帮助你在需要更多计算/内存时在更多实例上启动容器,并在不再需要时关闭容器,同时帮助维持系统的高可用性。

Kubernetes(K8s)是 2010 年代增长最快的技术之一。自 2014 年推出以来,它已经普及到今天的生产系统中。Jeremy Jordan 为那些有兴趣了解更多的读者提供了一个关于K8s 的介绍。然而,K8s 并不是最适合数据科学家的工具,关于如何将数据科学工作负载迁移出 K8s 已经进行了多次讨论。²⁷我们将在下一节更深入地讨论 K8s。

资源管理

在云计算出现之前(甚至是今天仍然在自己维护数据中心的公司),存储和计算资源是有限的。当时的资源管理围绕如何充分利用有限资源展开。增加一个应用程序的资源可能意味着减少其他应用程序的资源,并且需要复杂的逻辑来最大化资源利用,即使这意味着需要更多的工程时间。

然而,在云计算世界中,存储和计算资源更具弹性,关注点已从如何最大化资源利用转向如何成本有效地使用资源。向应用程序添加更多资源并不意味着减少其他应用程序的资源,这显著简化了分配挑战。许多公司可以接受向应用程序添加更多资源,只要增加的成本能够通过回报(例如额外的收入或节省的工程时间)来证明是合理的。

在大多数地区,工程师的时间比计算时间更宝贵,只要能帮助工程师提高生产力,公司通常可以接受使用更多资源。这意味着对公司来说,投资于自动化其工作负载可能是有意义的,尽管这可能使得使用资源的效率低于手动规划工作负载,但可以使工程师有更高的回报。通常,如果一个问题可以通过使用更多非人力资源(例如投入更多计算资源)或使用更多人力资源(例如需要更多工程时间进行重新设计)来解决,第一种解决方案可能更可取。

在本节中,我们将讨论如何管理 ML 工作流的资源。我们将重点放在基于云的资源上;然而,讨论的思想也可以适用于私有数据中心。

Cron、调度器和编排器

ML 工作流的两个关键特征影响其资源管理:重复性和依赖性。

在本书中,我们已经详细讨论了如何开发 ML 系统是一个迭代过程。同样,ML 工作负载很少是一次性操作,而是重复的。例如,您可能每周训练一个模型或每四个小时生成一批新的预测。可以通过可用资源顺利且成本有效地安排和编排这些重复过程。

定期安排重复作业在固定时间运行正是cron所做的。这也是 cron 的全部功能:在预定时间运行脚本,并告诉你作业是否成功或失败。它不关心运行的作业之间的依赖关系——你可以用 cron 在作业 B 之后运行作业 A,但不能安排像在 A 成功后运行 B,A 失败后运行 C 这样复杂的事情。

这将我们带到第二个特征:依赖性。ML 工作流程中的步骤可能彼此之间具有复杂的依赖关系。例如,ML 工作流可能包括以下步骤:

  1. 从数据仓库中提取上周的数据。

  2. 从这些提取的数据中提取特征。

  3. 对提取的特征训练两个模型 A 和 B。

  4. 在测试集上比较 A 和 B。

  5. 如果 A 更好,则部署 A;否则部署 B。

每一步都依赖于前一步的成功。第五步是我们所说的条件依赖:这一步的操作取决于前一步的结果。这些步骤的执行顺序和依赖关系可以用图形表示,如图 10-7 所示。

图 10-7. 一个显示简单 ML 工作流程执行顺序的图表,本质上是一个 DAG(有向无环图)。

许多读者可能会认识到图 10-7 是一个 DAG:有向无环图。它必须是有向的以表达步骤之间的依赖关系。它不能包含循环,因为如果有循环,作业将永远运行下去。DAG 是一种通用的表示计算工作流的方式,不仅适用于机器学习工作流。大多数工作流管理工具要求您以 DAG 的形式指定工作流程。

调度程序是可以处理依赖关系的 cron 程序。它接受工作流的 DAG 并相应地安排每个步骤的调度。您甚至可以根据基于事件的触发器安排启动作业,例如,每当事件 X 发生时启动作业。调度程序还允许您指定作业失败或成功时的操作,例如,如果作业失败,重试多少次后放弃。

调度程序倾向于利用队列来跟踪作业。作业可以被排队、优先级排序并分配所需的执行资源。这意味着调度程序需要了解可用资源以及每个作业运行所需的资源——在调度作业时,需要指定这些资源选项或由调度程序估算。例如,如果一个作业需要 8 GB 的内存和两个 CPU,调度程序需要在管理的资源中找到一个具有 8 GB 内存和两个 CPU 的实例,并等待直到该实例不在执行其他作业时运行此作业。

下面是如何使用流行的调度程序 Slurm 调度作业的示例,其中您可以指定作业名称、执行作业所需的时间以及为作业分配的内存和 CPU 数量:

#!/bin/bash
#SBATCH -J JobName
#SBATCH --time=11:00:00       # When to start the job
#SBATCH --mem-per-cpu=4096   # Memory, in MB, to be allocated per CPU
#SBATCH --cpus-per-task=4          # Number of cores per task

由于调度程序掌握了可用资源、要运行的作业以及每个作业运行所需的资源信息,因此调度程序还应优化资源利用。然而,用户指定的资源数量并不总是正确的。例如,我可能估计并因此指定一个作业需要 4 GB 的内存,但实际上这个作业只需要 3 GB 内存,或者在峰值时需要 4 GB 内存,其他时间只需要 1–2 GB 内存。像谷歌的 Borg 这样的高级调度程序估计作业实际需要的资源量,并回收未使用的资源供其他作业使用,进一步优化资源利用。²⁸

要设计一个通用的调度器是很难的,因为这个调度器需要能够管理几乎任意数量的并发机器和工作流程。如果你的调度器出现问题,那么它触及的每一个工作流程都会被中断。

如果调度器关注于何时运行作业以及运行这些作业所需的资源,编排器关注于从何处获取这些资源。调度器处理作业类型的抽象,如 DAGs、优先级队列、用户级配额(即用户在给定时间内可以使用的最大实例数)等。编排器处理较低级别的抽象,如机器、实例、集群、服务级别分组、复制等。如果编排器注意到作业比可用实例池中的实例更多,它可以增加可用实例池中的实例数。我们说它“配置”更多计算机来处理工作负载。调度器通常用于周期性作业,而编排器通常用于具有长时间运行服务器以响应请求的服务。

当今最著名的编排器无疑是 Kubernetes,即我们在“从开发到生产:容器”部分讨论过的容器编排器。K8s 可以在本地使用(甚至可以通过 minikube 在您的笔记本电脑上使用)。然而,我从未遇到过喜欢设置自己 K8s 集群的人,所以大多数公司使用由云提供商管理的托管服务,如 AWS 的 Elastic Kubernetes Service (EKS) 或 Google 的 Kubernetes Engine (GKE)。

许多人将调度器和编排器交替使用,因为调度器通常在编排器的顶部运行。像 Slurm 和 Google 的 Borg 这样的调度器具有一定的编排能力,而像 HashiCorp 的 Nomad 和 K8s 这样的编排器则带有一定的调度能力。但是你可以拥有单独的调度器和编排器,例如在 Kubernetes 上运行 Spark 的作业调度器或在 EKS 上运行 AWS Batch 调度器。像 HashiCorp 的 Nomad 和专门用于数据科学的编排器,包括 Airflow、Argo、Prefect 和 Dagster,都有它们自己的调度器。

数据科学工作流管理

我们已经讨论了调度器和编排器之间的区别,以及它们如何用于通用的工作流程执行。熟悉数据科学特定工具如 Airflow、Argo、Prefect、Kubeflow、Metaflow 等的读者可能会想知道它们在调度器与编排器讨论中的定位。我们将在这里详细讨论这个话题。

在其最简单的形式下,工作流管理工具管理工作流程。它们通常允许您将工作流程指定为 DAGs,类似于 图 10-7 中的图表。一个工作流可能包括特征提取步骤、模型训练步骤和评估步骤。工作流可以使用代码(Python)或配置文件(YAML)定义。工作流中的每个步骤称为任务。

几乎所有的工作流管理工具都带有一些调度器,因此,你可以将它们视为调度器,而不是专注于单个作业,而是专注于整个工作流。一旦定义了工作流,底层调度器通常与编排器一起工作,分配资源来运行工作流,如图 10-8 所示。

图 10-8。在定义工作流之后,这个工作流中的任务被调度和编排。

在网上有很多文章比较不同的数据科学工作流管理工具。在本节中,我们将介绍五种最常见的工具:Airflow、Argo、Prefect、Kubeflow 和 Metaflow。本节不旨在全面比较这些工具,而是为你介绍工作流管理工具可能需要的不同功能。

Airflow 最初是在 Airbnb 开发,并于 2014 年发布,是最早的工作流编排器之一。它是一个了不起的任务调度器,配有大量的操作器库,可以轻松地在不同的云提供商、数据库、存储选项等中使用 Airflow。Airflow 是“配置即代码”原则的支持者。它的创作者认为数据工作流程复杂,应该使用代码(Python)而不是 YAML 或其他声明性语言来定义。这里有一个 Airflow 工作流的示例,来自平台的GitHub 仓库

from datetime import datetime, timedelta

from airflow import DAG
from airflow.operators.bash import BashOperator
from airflow.providers.docker.operators.docker import DockerOperator

dag = DAG(
    'docker_sample',
    default_args={'retries': 1},
    schedule_interval=timedelta(minutes=10),
    start_date=datetime(2021, 1, 1),
    catchup=False,
)

t1 = BashOperator(task_id='print_date', bash_command='date', dag=dag)
t2 = BashOperator(task_id='sleep', bash_command='sleep 5', retries=3, dag=dag)
t3 = DockerOperator(
    docker_url='tcp://localhost:2375',  # Set your docker URL
    command='/bin/sleep 30',
    image='centos:latest',
    network_mode='bridge',
    task_id='docker_op_tester',
    dag=dag,
)

t4 = BashOperator(
    task_id='print_hello', 
    bash_command='echo "hello world!!!"', 
    dag=dag
)

t1 >> t2
t1 >> t3
t3 >> t4   

然而,由于 Airflow 比大多数其他工具更早创建,它没有可以借鉴的工具,因此遭受了许多缺点,详细讨论见Uber 工程博客文章。在这里,我们将仅介绍三个缺点,为你提供一个概念。

首先,Airflow 是单片的,这意味着它将整个工作流打包到一个容器中。如果你的工作流中的两个不同步骤有不同的要求,在理论上,你可以使用 Airflow 的DockerOperator为它们创建不同的容器,但实际上并不那么容易。

其次,Airflow 的 DAGs 没有参数化,这意味着你不能向工作流传递参数。因此,如果你想用不同的学习率运行相同的模型,你必须创建不同的工作流。

第三,Airflow 的 DAGs 是静态的,这意味着它不能在运行时根据需要自动创建新步骤。想象一下你从数据库中读取数据,并且想要创建一个处理数据库中每条记录的步骤(例如进行预测),但是你事先不知道数据库中有多少条记录。Airflow 将无法处理这种情况。

下一代工作流编排器(Argo、Prefect)的创建是为了解决 Airflow 的不同缺点。

Prefect 的 CEO,Jeremiah Lowin,曾是 Airflow 的核心贡献者。他们早期的营销活动引起了 激烈的比较 between Prefect and Airflow。Prefect 的工作流程是参数化和动态的,相比于 Airflow 有了很大的改进。它还遵循“配置即代码”的原则,因此工作流程是用 Python 定义的。

然而,与 Airflow 一样,Prefect 并不把容器化步骤作为首要任务。你可以在每个步骤中运行容器,但仍然需要处理 Dockerfile,并在 Prefect 中注册你的 Docker 镜像与工作流程。

Argo 解决了容器问题。Argo 工作流中的每个步骤都在其自己的容器中运行。然而,Argo 的工作流是用 YAML 定义的,这使你能够在同一个文件中定义每个步骤及其要求。下面的代码示例,来自于 Argo GitHub 仓库,演示了如何创建一个显示硬币翻转的工作流程:

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: coinflip-
  annotations:
    workflows.argoproj.io/description: |
      This is an example of coin flip defined as a sequence of conditional steps.
      You can also run it in Python: 
      https://couler-proj.github.io/couler/examples/#coin-flip
spec:
  entrypoint: coinflip
  templates:
  - name: coinflip
    steps:
    - - name: flip-coin
        template: flip-coin
    - - name: heads
        template: heads
        when: "{{steps.flip-coin.outputs.result}} == heads"
      - name: tails
        template: tails
        when: "{{steps.flip-coin.outputs.result}} == tails"

    - name: flip-coin
      script:
        image: python:alpine3.6
        command: [python]
        source: |
          import random
          result = "heads" if random.randint(0,1) == 0 else "tails"
          print(result)
    - name: heads
      container:
        image: alpine:3.6
        command: [sh, -c]
        args: ["echo \"it was heads\""]

    - name: tails
      container:
        image: alpine:3.6
        command: [sh, -c]
        args: ["echo \"it was tails\""]

Argo 的主要缺点,除了其混乱的 YAML 文件之外,是它只能在生产中运行的 K8s 集群上运行。如果你想在本地测试相同的工作流程,你将不得不使用 minikube 在你的笔记本电脑上模拟一个 K8s,这可能会变得混乱。

进入 Kubeflow 和 Metaflow,这两个工具旨在通过抽象掉通常需要运行 Airflow 或 Argo 的基础设施样板代码,帮助你在开发和生产环境中运行工作流程。它们承诺让数据科学家从本地笔记本电脑中访问生产环境的完整计算能力,从而有效地使数据科学家能够在开发和生产环境中使用相同的代码。

尽管这两个工具都具有一定的调度能力,但它们都旨在与真正的调度器和编排器一起使用。Kubeflow 的一个组成部分是 Kubeflow Pipelines,它是建立在 Argo 之上的,并且旨在在 K8s 上使用。Metaflow 可以与 AWS Batch 或 K8s 一起使用。

这两个工具都是完全参数化和动态的。目前,Kubeflow 更受欢迎。然而,在用户体验方面,我认为 Metaflow 更优秀。在 Kubeflow 中,虽然你可以用 Python 定义工作流程,但在能够在 Python 工作流程中将它们组合在一起之前,你仍然需要编写 Dockerfile 和 YAML 文件来指定每个组件(例如,处理数据、训练、部署)的规格。基本上,Kubeflow 帮助你通过让你编写 Kubeflow 标准化代码来抽象出其他工具的样板代码。

在 Metaflow 中,你可以使用 Python 装饰器 @conda 来指定每个步骤的要求——包括必需的库、内存和计算要求——Metaflow 将自动创建一个包含所有这些要求的容器来执行该步骤。你可以节省 Dockerfile 或 YAML 文件。

Metaflow 允许您从同一个笔记本/脚本无缝地在开发和生产环境中工作。您可以在本地机器上使用小数据集运行实验,当您准备在云上使用大数据集时,只需添加@batch装饰器即可在AWS Batch上执行。您甚至可以在同一个工作流中的不同环境中运行不同的步骤。例如,如果一个步骤需要小内存占用,它可以在您的本地机器上运行。但如果下一个步骤需要大内存占用,您只需添加@batch即可在云上执行。

# Example: sketch of a recommender system that uses an ensemble of two models. 
# Model A will be run on your local machine and model B will be run on AWS.

class RecSysFlow(FlowSpec):
    @step
    def start(self):
        self.data = load_data()
        self.next(self.fitA, self.fitB)

    # fitA requires a different version of NumPy compared to fitB
    @conda(libraries={"scikit-learn":"0.21.1", "numpy":"1.13.0"})
    @step
    def fitA(self):
        self.model = fit(self.data, model="A")
        self.next(self.ensemble)

    @conda(libraries={"numpy":"0.9.8"})
    # Requires 2 GPU of 16GB memory
    @batch(gpu=2, memory=16000)
    @step
    def fitB(self):
        self.model = fit(self.data, model="B")
        self.next(self.ensemble)

    @step
    def ensemble(self, inputs):
        self.outputs = (
                   (inputs.fitA.model.predict(self.data) +   
                    inputs.fitB.model.predict(self.data)) / 2
                   for input in inputs
        )
        self.next(self.end)

    def end(self):
        print(self.outputs)

机器学习平台

一家主要流媒体公司的机器学习平台团队经理告诉我他们团队如何起步的故事。他最初加入公司是为了负责推荐系统。为了部署推荐系统,他们需要构建特征管理、模型管理、监控等工具。去年,他的公司意识到这些相同的工具不仅可以用于推荐系统,还可以用于其他机器学习应用程序。他们成立了一个新团队,机器学习平台团队,旨在为机器学习应用程序提供共享基础设施。由于推荐系统团队的工具最为成熟,其他团队也采纳了他们的工具,并且从推荐系统团队中选了一些成员加入了新的机器学习平台团队。

自 2020 年初以来,这个故事代表了一个增长趋势。随着每家公司在越来越多的应用中使用机器学习,通过利用同一套工具来支持多个应用程序,而不是为每个应用程序支持单独的工具集,可以获得更多收益。这些用于机器学习部署的共享工具集构成了机器学习平台。

因为机器学习平台相对较新,每家公司对什么构成机器学习平台存在不同看法。甚至在同一家公司内部,这是一个持续讨论的话题。在这里,我将重点关注我经常在机器学习平台中看到的组件,包括模型开发、模型存储和特征存储。

对每个类别的工具进行评估取决于您的用例。但是,以下是您可能想要牢记的两个一般方面:

无论工具是否与您的云服务提供商兼容,或者允许您在自己的数据中心使用它

您需要从计算层运行和提供模型,并且通常工具仅支持与少数云提供商的集成。没有人喜欢因为另一个工具而不得不采用新的云提供商。

无论是开源还是托管服务

如果是开源的,您可以自行托管,减少对数据安全和隐私的担忧。然而,自行托管意味着需要额外的工程时间来维护。如果是托管服务,您的模型及可能的一些数据将位于其服务上,这可能不适用于某些法规。一些托管服务与虚拟私有云配合使用,这允许您在自己的云集群中部署您的机器,有助于遵守法规。我们将在“构建还是购买”部分进一步讨论这一点。

让我们从第一个组件开始:模型部署。

模型部署

一旦模型训练完成(并希望已进行测试),您希望使其预测能力对用户可访问。在第七章中,我们详细讨论了模型如何提供其预测的能力:在线或批量预测。我们还讨论了将模型部署为端点的最简单方法是将模型及其依赖项推送到生产环境中,并向用户公开其模型作为端点的方式。如果进行在线预测,此端点将促使模型生成预测。如果进行批量预测,此端点将获取预先计算的预测结果。

部署服务可以帮助将您的模型及其依赖项推送到生产环境,并将您的模型作为端点公开。由于部署是游戏的名字,部署是所有 ML 平台组件中最成熟的,有许多工具可以实现这一点。所有主要的云提供商都提供了部署工具:AWS 提供SageMaker,GCP 提供Vertex AI,Azure 提供Azure ML,阿里巴巴提供Machine Learning Studio,等等。此外,还有许多初创公司提供模型部署工具,如MLflow ModelsSeldonCortexRay Serve,等等。

在选择部署工具时,考虑如何轻松地进行在线预测和批量预测是很重要的。虽然使用大多数部署服务在较小规模上进行在线预测通常很简单,但通常批量预测比较棘手。²⁹ 一些工具允许您将请求批处理到一起进行在线预测,这与批量预测不同。许多公司有单独的部署管道用于在线预测和批量预测。例如,他们可能使用 Seldon 进行在线预测,但利用 Databricks 进行批量预测。

模型部署的一个悬而未决的问题是如何在部署之前确保模型的质量。在第九章 中,我们讨论了测试在生产中的不同技术,如影子部署、金丝雀发布、A/B 测试等。在选择部署服务时,您可能希望检查该服务是否能够轻松进行您想要的测试。

模型存储

许多公司忽视模型存储,因为它们听起来很简单。在 “模型部署” 部分,我们讨论了如何部署模型,您需要将模型打包并上传到生产环境可访问的位置。模型存储建议存储模型——您可以通过上传您的模型到像 S3 这样的存储来实现。然而,事情并不是那么简单。现在想象一下,您的模型对一组输入的性能下降了。收到问题警报的人是一位 DevOps 工程师,她调查后决定需要通知创建该模型的数据科学家。但公司可能有 20 位数据科学家;她应该通知谁呢?

现在假设正确的数据科学家已经加入了。数据科学家首先希望在本地重现这些问题。她仍然拥有用于生成此模型的笔记本和最终模型,所以她启动笔记本并使用具有问题集的模型。令她惊讶的是,本地生成的模型输出与生产中生成的输出不同。这种差异可能是由许多原因引起的,以下仅列举几个例子:

  • 当前在生产中使用的模型与她在本地拥有的模型不同。也许她将错误的模型二进制文件上传到了生产环境?

  • 当前在生产中使用的模型是正确的,但使用的特征列表是错误的。也许她在推送到生产之前忘记重新构建本地代码了?

  • 模型是正确的,特征列表是正确的,但特征处理代码已过时。

  • 模型是正确的,特征列表是正确的,特征处理代码也是正确的,但数据处理管道出了问题。

在不知道问题原因的情况下,修复问题将非常困难。在这个简单的例子中,我们假设负责的数据科学家仍然可以访问用于生成模型的代码。如果这位数据科学家不再拥有那本笔记本的访问权限,或者她已经离职或者在度假呢?

许多公司意识到,仅仅将模型存储在 Blob 存储中是不够的。为了帮助调试和维护,尽可能跟踪与模型相关的信息是很重要的。以下是您可能希望存储的八种类型的工件。请注意,这里提到的许多工件是应包含在模型卡中的信息,如第 “创建模型卡” 节所讨论的。

模型定义

这是创建模型形状所需的信息,例如使用的损失函数。如果是神经网络,这包括隐藏层的数量以及每个层中的参数数量。

模型参数

这些是您模型参数的实际值。然后,这些值与模型的形状结合起来,重新创建一个可用于预测的模型。某些框架允许您同时导出参数和模型定义。

特征提取和预测函数

给定预测请求,如何提取特征并将这些特征输入模型以获得预测?特征提取和预测函数提供了执行此操作的指令。这些函数通常包装在端点中。

依赖项

依赖项,例如 Python 版本、Python 包,通常被打包到一个容器中以运行您的模型。

数据

用于训练此模型的数据可能是指向存储数据的位置或数据的名称/版本的指针。如果使用诸如 DVC 等工具对数据进行版本控制,这可以是生成数据的 DVC 提交。

模型生成代码

这是指定您的模型创建方式的代码,例如:

  • 使用的框架

  • 训练方式

  • 创建训练/验证/测试分割的详细信息

  • 实验运行次数

  • 考虑的超参数范围

  • 最终模型使用的实际超参数集

很多时候,数据科学家通过在笔记本中编写代码来生成模型。具有更成熟流水线的公司要求其数据科学家将模型生成代码提交到 GitHub 或 GitLab 上的 Git 仓库中。然而,在许多公司中,这个过程是临时的,数据科学家甚至不会提交他们的笔记本。如果负责模型的数据科学家丢失笔记本、离职或休假,那么没有办法将生产中的模型映射到生成它的代码,用于调试或维护。

实验结果

这些是在模型开发过程中生成的工件,如“实验跟踪和版本控制”部分中讨论的内容。这些工件可以是像损失曲线这样的图形,也可以是像测试集上模型性能这样的原始数字。

标签

这包括帮助模型发现和过滤的标签,例如所有者(拥有此模型的人或团队)或任务(此模型解决的业务问题,如欺诈检测)。

大多数公司存储一个子集,但并非所有工件。公司存储的工件可能不在同一位置,而是分散的。例如,模型定义和模型参数可能在 S3 中。包含依赖项的容器可能在 ECS(弹性容器服务)中。数据可能在 Snowflake 中。实验工件可能在 Weights & Biases 中。特征化和预测函数可能在 AWS Lambda 中。一些数据科学家可能会手动跟踪这些位置,比如在 README 中,但这个文件很容易丢失。

模型存储器,能够存储足够的一般使用情况,远未解决。截至本书撰写时,MLflow 显然是最流行的模型存储器,不与主要云提供商关联。然而,在 Stack Overflow 上,六个最热门的 MLflow 问题中有三个是关于在 MLflow 中存储和访问工件的,如图 10-9 所示。模型存储器需要进行改造,我希望在不久的将来,一家初创公司能够站出来解决这个问题。

图 10-9. MLflow 是最流行的模型存储器,但远未解决工件问题。在 Stack Overflow 上,六个最热门的 MLflow 问题中有三个是关于在 MLflow 中存储和访问工件的。来源:从 Stack Overflow 页面截图

因缺乏良好的模型存储解决方案,像 Stitch Fix 这样的公司决定自行构建他们自己的模型存储。图 10-10 展示了 Stitch Fix 模型存储跟踪的工件。当模型上传到他们的模型存储时,此模型附带链接到序列化模型的链接,运行模型所需的依赖项(Python 环境),创建模型代码生成的 Git 提交(Git 信息),标签(至少指定拥有模型的团队)等信息。

图 10-10. Stitch Fix 模型存储跟踪的工件。来源:根据 Stefan Krawczyk 为CS 329S(斯坦福大学)制作的幻灯片修改

特征存储

“特征存储”是一个越来越具有多重含义的术语,可以被不同的人用来指代完全不同的事物。机器学习实践者已经多次尝试定义特征存储应具备的特性。³⁰ 在其核心,特征存储可以帮助解决三个主要问题:特征管理、特征转换和特征一致性。特征存储解决方案可能解决其中一个或几个问题:

特征管理

一家公司可能拥有多个机器学习模型,每个模型使用大量特征。例如,Uber 在 2017 年拥有大约 10,000 个特征跨团队使用!³¹通常情况下,用于一个模型的特征可能对另一个模型也有用。例如,A 团队可能有一个模型来预测用户流失的可能性,而 B 团队则有一个模型来预测免费用户转化为付费用户的可能性。这些模型可以共享许多特征。如果 A 团队发现特征 X 非常有用,B 团队可能也能利用它。

特征存储可以帮助团队共享和发现特征,以及管理每个特征的角色和共享设置。例如,你可能不希望公司中的每个人都能访问公司或用户的敏感财务信息。在这种情况下,特征存储可以被视为特征目录。特征管理工具的示例包括Amundsen(在 Lyft 开发)和DataHub(在 LinkedIn 开发)。

特征计算³²

特征工程逻辑在定义后需要计算。例如,特征逻辑可能是:使用昨天的平均餐饮准备时间。计算部分涉及实际查看数据并计算这个平均值。

在上一点中,我们讨论了多个模型可能共享一个特征的情况。如果这个特征的计算不太昂贵,每次模型需要时计算一次可能是可接受的。但是,如果计算很昂贵,你可能希望仅在首次需要时执行计算,然后存储以备后续使用。

特征存储可以帮助进行特征计算和存储计算结果。在这种情况下,特征存储类似于数据仓库。

特征一致性

在第七章中,我们讨论了同一模型存在两个独立管道的问题:训练管道从历史数据中提取批量特征,而推断管道提取流式特征。在开发过程中,数据科学家可能会使用 Python 定义特征并创建模型。然而,生产代码可能会用另一种语言编写,如 Java 或 C,以提高性能。

这意味着在开发过程中用 Python 编写的特征定义可能需要转换为生产中使用的语言。因此,你必须两次编写相同的特征,一次用于训练,一次用于推断。首先,这很烦人且耗时。其次,它增加了 bug 的可能性,因为生产中的一个或多个特征可能与训练中的不同,导致奇怪的模型行为。

现代特征存储的一个关键卖点是它们统一了批处理特征和流处理特征的逻辑,确保训练期间特征与推断期间特征的一致性。

特征存储是一个较新的类别,大约在 2020 年开始受到关注。虽然普遍认为特征存储应管理特征定义并确保特征一致性,但各供应商的具体能力各不相同。一些特征存储仅管理特征定义而不从数据计算特征;一些特征存储两者都做。有些特征存储还进行特征验证,即检测特征是否符合预定义的模式,而有些则将这一方面留给监控工具。

在撰写本书时,最流行的开源特征存储是 Feast。然而,Feast 的优势在于批处理特征,而不是流处理特征。Tecton 是一个完全托管的特征存储,承诺能够处理批处理特征和在线特征,但它们的实际吸引力较低,因为需要深度集成。像 SageMaker 和 Databricks 这样的平台也提供它们自己对特征存储的解决方案。在 2022 年 1 月我调查的 95 家公司中,只有约 40%使用特征存储。在使用特征存储的公司中,有一半是建立了自己的特征存储。

自建与购买

在本章开头,我们讨论了为满足您的机器学习需求设置正确基础设施的困难程度。您所需的基础设施取决于您拥有的应用程序及其运行规模。

您需要投入多少资金到基础设施中,也取决于您想在内部建造什么和您想购买什么。例如,如果您想使用完全托管的 Databricks 集群,可能只需要一名工程师。然而,如果您想托管自己的 Spark Elastic MapReduce 集群,可能需要额外五个人。

在极端情况下,您可以将所有机器学习用例外包给一家提供端到端 ML 应用的公司,也许您唯一需要的基础设施就是数据移动:将数据从您的应用程序移动到供应商那里,将预测结果从供应商那里移回给用户。您的其余基础设施由您的供应商管理。

在另一个极端,如果您是一家处理敏感数据的公司,无法使用另一家公司管理的服务,您可能需要在内部建立和维护所有基础设施,甚至拥有自己的数据中心。

然而,大多数公司都不处于这两个极端之间。如果您在这些公司之一工作,您可能会有一些由其他公司管理的组件和一些内部开发的组件。例如,您的计算资源可能由 AWS EC2 管理,数据仓库可能由 Snowflake 管理,但您有自己的特征存储和监控仪表板。

您的自建还是购买决策取决于许多因素。在这里,我们将讨论三个我在与基础设施负责人交流时经常遇到的常见因素,以及他们如何评估这些决策:

公司所处的阶段

起初,您可能希望利用供应商解决方案尽快启动,以便将有限的资源集中投入到产品的核心功能上。然而,随着使用案例的增加,供应商的成本可能变得过高,对您而言,投资自己的解决方案可能更便宜。

你认为公司的重点或竞争优势是什么?

Stitch Fix 的 ML 平台团队经理 Stefan Krawczyk 向我解释了他在“自建还是购买”决策上的看法:“如果是我们想在某个领域真正擅长的事情,我们会自行管理。如果不是,我们会使用供应商的解决方案。”对于大多数非技术行业的公司——例如零售、银行、制造业的公司来说,ML 基础设施并非他们的重点,因此他们倾向于购买。当我与这些公司交流时,他们更喜欢托管服务,甚至是针对特定问题的解决方案(例如需求预测服务)。对于许多技术公司来说,技术是他们的竞争优势,他们强大的工程团队更倾向于自主控制技术堆栈,因此他们倾向于自行构建。如果他们使用托管服务,他们可能更希望该服务是模块化和可定制的,以便可以随时插拔任何组件。

可用工具的成熟度

例如,您的团队可能决定需要一个模型存储库,本来希望使用供应商的解决方案,但没有供应商能够满足您的需求,因此您不得不建立自己的特征存储,或许是在开源解决方案的基础上。

这是在工业界早期采用机器学习时发生的情况。早期采用者,即大型科技公司,因为没有足够成熟的解决方案来满足他们的需求,因此建立了自己的基础设施。这导致了每家公司的基础设施各不相同的情况。几年后,解决方案的提供变得更加成熟。然而,这些解决方案发现很难向大型科技公司销售,因为不可能创建一个能够与大多数定制基础设施兼容的解决方案。

在我们建设 Claypot AI 的过程中,其他创始人实际上建议我们避免向大型科技公司销售,因为如果这样做,我们将陷入他们所谓的“集成地狱”——花费更多时间将我们的解决方案与定制基础设施集成,而不是构建我们的核心功能。他们建议我们专注于那些有更清晰发展路径的初创企业。

有些人认为建设比购买更便宜,但情况并非总是如此。建设意味着你将不得不雇用更多工程师来建设和维护自己的基础设施。这也可能伴随着未来的成本:创新的成本。内部定制的基础设施使得很难采纳新技术,因为会存在集成问题。

建设与购买的决策是复杂的,高度依赖上下文,并且可能是基础设施负责人经常深思熟虑的问题。Better.com 的前 CTO 埃里克·伯恩哈德森在一条推文中表示:“CTO 的最重要工作之一是供应商/产品选择,由于基础设施空间增长如此迅速,其重要性每年都在迅速上升。”³³ 没有办法在一个小节中解决所有细微差别。但我希望本节为您提供了一些开始讨论的指引。

摘要

如果您一直与我同行至今,我希望您同意将机器学习模型引入生产环境是一个基础设施问题。为了使数据科学家能够开发和部署机器学习模型,建立正确的工具和基础设施是至关重要的。

在本章中,我们涵盖了机器学习系统所需的不同基础设施层。我们从存储和计算层开始,这为需要大量数据和计算资源的任何工程项目提供了重要资源,例如机器学习项目。存储和计算层已经非常商品化,这意味着大多数公司支付云服务,按照他们使用的存储和计算资源量来付费,而不是建立自己的数据中心。然而,尽管云服务提供商让公司能够轻松入门,但随着公司的增长,它们的成本变得不可承受,越来越多的大公司正在考虑从云服务回迁到私有数据中心。

然后我们继续讨论了开发环境,在这里数据科学家编写代码并与生产环境进行交互。因为开发环境是工程师们花费大部分时间的地方,开发环境的改进直接转化为生产力的提升。公司可以做的第一件事情之一是为在同一团队工作的数据科学家和机器学习工程师标准化开发环境。在本章中,我们讨论了为什么推荐标准化以及如何实施。

我们随后讨论了一个基础设施主题,近年来数据科学家们对其重要性有着激烈的争论:资源管理。资源管理对于数据科学工作流程至关重要,但问题是是否应该期望数据科学家来处理它。在本节中,我们追溯了从 cron 到调度程序再到编排器的资源管理工具的演变。我们还讨论了为什么 ML 工作流与其他软件工程工作流不同,以及为什么它们需要自己的工作流管理工具。我们比较了各种工作流管理工具,如 Airflow、Argo 和 Metaflow。

ML 平台是一个随着 ML 采纳成熟而出现的团队。由于它是一个新兴概念,对于 ML 平台应包含什么仍存在争议。我们选择关注对大多数 ML 平台至关重要的三组工具:部署、模型存储和特征存储。由于监控 ML 平台已经在 第八章 中有所涵盖,我们跳过了对 ML 平台的监控。

在进行基础设施工作时,一个问题不断困扰着工程经理和首席技术官:是自建还是购买?我们在本章末尾提出了一些讨论观点,希望能为您或您的团队提供足够的背景来做出这些困难的决策。

¹ Kunal Shah,《这就是为什么 SEO 对每个企业都重要》,Entrepreneur India,2020 年 5 月 11 日,https://oreil.ly/teQlX

² 如果你想深入了解特斯拉的 ML 计算基础设施,强烈推荐观看特斯拉 2021 年 AI Day 的录像,YouTube

³ “合理规模”一词的定义灵感来自 Jacopo Tagliabue 在其论文《You Do Not Need a Bigger Boat: 在(大部分)无服务器和开放堆栈中的合理规模推荐》中,arXiv,2021 年 7 月 15 日,https://oreil.ly/YNRZQ。有关合理规模的更多讨论,请参阅 Ciro Greco 的文章《ML 和 MLOps 在合理规模下》(2021 年 10 月)“ML and MLOps at a Reasonable Scale”

⁴ FAAAM 是 Facebook、Apple、Amazon、Alphabet、Microsoft 的简称。

⁵ Reza Shiftehfar,《Uber 的大数据平台:100+PB 的数据与分钟级延迟》,Uber Engineering,2018 年 10 月 17 日,https://oreil.ly/6Ykd3;Kaushik Krishnamurthi,《构建处理点击流数据的大数据流水线》,Zillow,2018 年 4 月 6 日,https://oreil.ly/SGmNe

⁶ Nathan Bronson 和 Janet Wiener,《Facebook 的顶级开放数据问题》,Meta,2014 年 10 月 21 日,https://oreil.ly/p6QjX

⁷ Wikipedia,见“基础设施”,https://oreil.ly/YaIk8

⁸ 我见过一家公司,他们的数据分布在 Amazon Redshift 和 GCP BigQuery 上,他们的工程师对此并不是很满意。

⁹ 我们这里只讨论数据存储,因为我们已经在第二章中讨论了数据系统。

¹⁰ 在编写本书时,ML 工作负载通常需要 4 GB 到 8 GB 的内存;16 GB 的内存足以处理大多数 ML 工作负载。

¹¹ 参见第七章的“模型优化”部分中的操作融合。

¹² “什么是 FLOP/s,它是否是性能的好衡量标准?”,Stack Overflow,最近更新于 2020 年 10 月 7 日,https://oreil.ly/M8jPP

¹³ 对于对 FLOPS 和带宽以及如何为深度学习模型进行优化感兴趣的读者,我推荐阅读文章 “从第一原理让深度学习飙升”(He 2022)。

¹⁴ 根据亚马逊,“EC2 实例支持多线程,允许多个线程在单个 CPU 核心上并发运行。每个线程在实例上被表示为一个虚拟 CPU(vCPU)。一个实例具有默认数量的 CPU 核心,根据实例类型而变化。例如,m5.xlarge 实例类型默认有两个 CPU 核心和每核心两个线程——总共四个 vCPU”(“优化 CPU 选项”,Amazon Web Services,最后访问于 2020 年 4 月,https://oreil.ly/eeOtd)。

¹⁵ 这成本为每小时$26.688。

¹⁶ 按需实例是在请求时可用的实例。Spot 实例是在没有其他人使用时可用的实例。与按需实例相比,云提供商倾向于以折扣价格提供 Spot 实例。

¹⁷ Synergy Research Group,“2020 年——云服务收入终于超过企业数据中心支出”,2021 年 3 月 18 日,https://oreil.ly/uPx94

¹⁸ Sarah Wang 和 Martin Casado,“云的成本,一个万亿美元的悖论”,a16z,https://oreil.ly/3nWU3

¹⁹ Wang 和 Casado,“云的成本”。

²⁰ Laurence Goasduff,“为什么组织选择多云策略”,Gartner,2019 年 5 月 7 日,https://oreil.ly/ZiqzQ

²¹ Goasduff,“为什么组织选择多云策略”。

²² Ville Tuulos,《高效数据科学基础设施》(Manning,2022)。

²³ 在编写本书时,Google Colab 甚至为其用户提供免费的 GPU

²⁴ Michelle Ufford, M. Pacer, Matthew Seal 和 Kyle Kelley,“Netflix 的笔记本创新”,Netflix 技术博客,2018 年 8 月 16 日,https://oreil.ly/EHvAe

²⁵ 对于初学者来说,新的拉取请求可以理解为向代码库添加的新代码片段。

²⁶ 参见编辑器之争,这是关于 Vim 与 Emacs 的长达十年的激烈辩论。

²⁷ Chip Huyen,“为什么数据科学家不需要了解 Kubernetes”,2021 年 9 月 13 日,https://huyenchip.com/2021/09/13/data-science-infrastructure.html;Neil Conway 和 David Hershey,“数据科学家不关心 Kubernetes”,Determined AI,2020 年 11 月 30 日,https://oreil.ly/FFDQW;I Am Developer 在 Twitter 上 (@iamdevloper):“我几乎不了解自己的感觉,怎么可能理解 Kubernetes”,2021 年 6 月 26 日,https://oreil.ly/T2eQE

²⁸ Abhishek Verma, Luis Pedrosa, Madhukar Korupolu, David Oppenheimer, Eric Tune, and John Wilkes,“Google 的大规模集群管理与 Borg”,EuroSys ’15: 第十届欧洲计算机系统会议(2015 年 4 月):18,https://oreil.ly/9TeTM

²⁹ 在较小规模进行在线预测时,您只需命中端点并获取预测结果。批量预测需要设置批处理作业并存储预测结果。

³⁰ Neal Lathia,“构建特征存储”,2020 年 12 月 5 日,https://oreil.ly/DgsvA;Jordan Volz,“为什么您需要特征存储”,Continual,2021 年 9 月 28 日,https://oreil.ly/kQPMb;Mike Del Balso,“什么是特征存储?”Tecton,2020 年 10 月 20 日,https://oreil.ly/pzy0I

³¹ Jeremy Hermann 和 Mike Del Balso,“Meet Michelangelo: Uber 的机器学习平台”,Uber 工程,2017 年 9 月 5 日,https://oreil.ly/XteNy

³² 有些人使用术语“特征转换”。

³³ Erik Bernhardsson 在 Twitter 上 (@bernhardsson),2021 年 9 月 29 日,https://oreil.ly/GnxOH

第十一章:机器学习的人文面

在本书中,我们涵盖了设计机器学习系统的许多技术方面。然而,机器学习系统不仅仅是技术性的。它涉及到业务决策者、用户,当然还有系统的开发者。我们在第一章和第二章中讨论了利益相关者及其目标。在本章中,我们将讨论用户和机器学习系统的开发者如何与这些系统互动。

首先考虑到由于机器学习模型的概率性质可能会改变和影响用户体验。我们将继续讨论组织结构,以允许同一机器学习系统的不同开发者有效地合作。我们将在章节“负责任的人工智能”结束本章。

用户体验

我们长期讨论了机器学习系统与传统软件系统的行为不同之处。首先,机器学习系统是概率性的而不是确定性的。通常情况下,如果你在不同时间两次运行相同输入的软件,你可以期望得到相同的结果。然而,如果你在不同时间两次运行完全相同输入的机器学习系统,你可能会得到不同的结果。¹ 其次,由于这种概率性质,机器学习系统的预测大多数情况下是正确的,困难的部分在于我们通常不知道对于什么样的输入系统会正确!第三,机器学习系统也可能很庞大,并且可能需要意料之外的长时间来产生预测。

这些差异意味着机器学习系统可能会以不同的方式影响用户体验,特别是对于迄今为止习惯于传统软件的用户。由于机器学习在现实世界中的相对新用法,机器学习系统如何影响用户体验还没有得到很好的研究。在本节中,我们将讨论机器学习系统对良好用户体验提出的三个挑战以及如何应对它们。

确保用户体验的一致性

当使用应用程序或网站时,用户期望有一定的一致性水平。例如,我习惯于 Chrome 在我 MacBook 的左上角有他们的“最小化”按钮。如果 Chrome 把这个按钮移到右边,我会感到困惑,甚至会感到沮丧。

机器学习预测具有概率性和不一致性,这意味着今天为一个用户生成的预测可能会与下一天为同一用户生成的预测不同,这取决于预测的背景。对于希望利用机器学习来改善用户体验的任务来说,机器学习预测的不一致性可能是一种阻碍。

具体来说,考虑一下由 Booking.com 在 2020 年发布的案例研究。当您在 Booking.com 上预订住宿时,有大约 200 个筛选器可供您选择,例如“包括早餐”,“宠物友好”和“无烟房间”。有这么多筛选器,用户需要一些时间才能找到他们想要的筛选器。Booking.com 的应用 ML 团队希望使用 ML 来根据用户在特定浏览会话中使用的筛选器自动建议用户可能需要的筛选器。

他们遇到的挑战是,如果他们的 ML 模型每次都建议不同的筛选器,用户可能会感到困惑,特别是如果他们找不到他们之前已经应用过的筛选器。团队通过创建规则来解决这一挑战,规定系统必须返回相同的筛选器推荐条件(例如,用户已应用筛选器时),以及系统可以返回新推荐条件的条件(例如,用户更改目的地时)。这被称为一致性和准确性的权衡,因为系统认为最准确的推荐可能并不是能够提供用户一致性的推荐。

对抗“大多正确”的预测

在前面的部分中,我们谈到了确保模型预测一致性的重要性。在本节中,我们将讨论在某些情况下,我们希望模型的预测 less 一致性 and more diversity in a model’s predictions

自 2018 年以来,大型语言模型GPT及其后继者GPT-2GPT-3席卷了全球。这些大型语言模型的优势在于,它们能够在几乎不需要任务特定训练数据的情况下生成广泛任务的预测。例如,您可以将网页要求作为模型的输入,并输出所需的 React 代码来创建该网页,如图 11-1 所示。

图 11-1. GPT-3 可以帮助您为网站编写代码。来源:根据Sharif Shameem的视频截图调整

然而,这些模型的一个缺点是,这些预测并不总是正确的,并且在任务特定数据上进行微调以改进它们的预测非常昂贵。这些大多正确的预测对于可以轻松纠正它们的用户是有用的。例如,在客户支持的情况下,对于每个客户请求,ML 系统可以生成大多正确的响应,而人工操作员可以快速编辑这些响应。这可以加快响应速度,而不必从头开始编写响应。

然而,如果用户不知道如何或不能纠正响应,这些大部分正确的预测就不会很有用。考虑使用语言模型生成网页的 React 代码的相同任务。生成的代码可能无法工作,或者即使工作,也可能无法呈现出满足指定要求的网页。React 工程师可能能够快速修复这些代码,但是这个应用的许多用户可能不了解 React。这个应用可能会吸引许多不了解 React 的用户,这也是他们最初需要此应用程序的原因!

为了解决这个问题,一种方法是向用户展示同一输入的多个预测结果,以增加至少有一个预测结果正确的机会。这些预测结果应该以一种即使非专家用户也能评估的方式呈现出来。在这种情况下,根据用户输入的一组要求,您可以让模型生成多个 React 代码片段。这些代码片段被呈现成视觉网页,以便非工程背景的用户可以评估哪一个对他们最好。

这种方法非常常见,有时被称为“人在回路中的 AI”,因为它涉及人类来选择最佳预测结果或改进机器生成的预测结果。对于对人在回路 AI 感兴趣的读者,我强烈推荐 Jessy Lin 的“重新思考人工智能与人类互动”

平滑失败

我们在章节“计算优先级”中详细讨论了 ML 模型推断延迟对用户体验的影响。我们还讨论了如何压缩模型并优化其以获得更快的推断速度,在章节“模型压缩”中。然而,即使是通常速度快的模型在处理某些查询时可能仍然需要时间。这在处理序列数据(如语言模型或时间序列模型)的模型中尤为常见——例如,模型处理长序列比处理短序列需要更长的时间。对于模型响应时间过长的查询,我们应该怎么办?

我曾与一些公司合作,它们使用一种备用系统,其效果不如主系统好,但能够快速生成预测结果。这些系统可以是启发式的或简单的模型。它们甚至可以是预先计算的缓存预测结果。这意味着您可能有一条规则,指定:如果主模型生成预测结果的时间超过X毫秒,则使用备用模型。有些公司并不采用这种简单的规则,而是使用另一个模型来预测主模型生成给定查询的预测结果需要多长时间,并根据该预测将其路由到主模型或备用模型。当然,这种额外的模型可能还会增加系统的推断延迟。

这与速度与准确性的权衡有关:一个模型可能比另一个模型的性能差,但可以进行推理速度更快。这种次优但快速的模型可能会给用户带来较差的预测结果,但在延迟至关重要的情况下可能仍然更受欢迎。许多公司不得不在一个模型和另一个模型之间进行选择,但有备份系统的情况下,可以同时实现两者。

团队结构

一个机器学习项目涉及不仅仅是数据科学家和机器学习工程师,还有其他类型的工程师,如 DevOps 工程师和平台工程师,以及非开发人员利益相关者,如主题专家(SMEs)。面对多样化的利益相关者,问题在于在组织机器学习团队时什么是最优结构。我们将关注两个方面:跨职能团队的协作以及端到端数据科学家角色的争议。

跨职能团队协作

SME(医生、律师、银行家、农民、造型师等)在设计机器学习系统时经常被忽视,但许多机器学习系统在没有主题专家的情况下无法正常工作。他们不仅仅是用户,也是机器学习系统的开发者。

大多数人只在数据标注阶段考虑主题专业知识 —— 比如,你需要训练有素的专业人员来标注肺部 CT 扫描是否显示癌症迹象。然而,随着训练机器学习模型成为生产中的持续过程,标注和重新标注可能也会成为跨整个项目生命周期的持续过程。机器学习系统在整个生命周期中都涉及主题专家的参与将极大地受益,例如问题定义、特征工程、错误分析、模型评估、重新排名预测以及用户界面:如何最好地向用户和/或系统的其他部分呈现结果。

多个不同背景的人员参与项目会带来许多挑战。例如,如何向没有工程或统计背景的主题专家解释机器学习算法的限制和能力?为了构建机器学习系统,我们希望所有东西都有版本控制,但如何将领域专业知识(例如,如果在 X 和 Y 之间的这个区域有一个小点,那么可能是癌症的迹象)转化为代码并进行版本控制呢?

想让你的医生使用 Git 可真是个好运气。

在项目规划阶段早期就让主题专家参与并赋予他们无需依赖工程师即可做出贡献的能力是很重要的。例如,为了帮助主题专家更多地参与机器学习系统的开发,许多公司正在构建允许人们在不编写代码的情况下进行更改的无代码/低代码平台。目前大多数面向主题专家的无代码机器学习解决方案主要集中在标注、质量保证和反馈阶段,但正在开发更多的平台以帮助解决其他关键环节,如数据集创建和用于调查需要主题专家输入问题的视图。

端到端数据科学家

通过本书,我希望能说服你,ML 的生产不仅是一个 ML 问题,而且也是一个基础设施问题。要进行 MLOps,我们不仅需要 ML 专业知识,还需要运维(操作)专业知识,特别是关于部署、容器化、作业编排和工作流管理的专业知识。

为了能够将所有这些专业领域融入到机器学习项目中,公司倾向于遵循以下两种方法之一:建立一个独立的团队来管理所有运维方面,或者将数据科学家纳入团队并让他们负责整个过程。

让我们更仔细地看看这些方法在实践中是如何运作的。

方法 1:建立一个独立的团队来管理生产

在这种方法中,数据科学/ML 团队在开发环境中开发模型。然后,通常是运维/平台/ML 工程团队将模型投入生产。这种方法使得招聘更容易,因为只需招聘具有一组技能的人,而不是多组技能的人。这也可能使每个参与者的生活更轻松,因为他们只需专注于一个问题(例如,开发模型或部署模型)。然而,这种方法有许多缺点:

沟通和协调开销

团队可能会成为其他团队的阻碍。根据 Frederick P. Brooks 的说法:“一个程序员在一个月内能完成的工作,两个程序员在两个月内才能完成。”

调试挑战

当某些事情失败时,你不知道是你团队的代码还是其他团队的代码可能引起的。这可能根本不是因为你公司的代码。你需要从多个团队那里得到合作,才能找出问题所在。

推卸责任

即使你已经弄清楚了问题出在哪里,每个团队可能会认为解决问题是另一个团队的责任。

狭窄的上下文

没有人能够全面了解整个过程以进行优化/改进。例如,平台团队对如何改进基础设施有想法,但他们只能根据数据科学家的请求行动,而数据科学家不必处理基础设施,因此他们没有动力积极地对其进行变更。

方法 2:数据科学家负责整个过程

在这种方法中,数据科学团队还必须关注模型的产品化。数据科学家成为抱怨的独角兽,人们期望他们对整个过程了如指掌,他们可能最终写的代码比数据科学还多。

大约一年前,我在Twitter 上发推,谈到成为机器学习工程师或数据科学家所需的一组重要技能,如图 11-2 所示。该列表涵盖了工作流的几乎每个部分:数据查询、建模、分布式训练以及设置端点。甚至包括像 Kubernetes 和 Airflow 这样的工具。

图 11-2。我曾经认为数据科学家需要了解所有这些事情

这条推文似乎引起了我的观众共鸣。尤金·扬(Eugene Yan)还写道,“数据科学家应该更加端到端。”² Stitch Fix 的首席算法官埃里克·科尔森(Eric Colson)(之前也是 Netflix 的副总裁兼数据科学与工程主管)在一篇文章中写道,“全栈数据科学通才的力量以及职能分工的危险。”³

当我写那条推文时,我相信 Kubernetes 对机器学习工作流程至关重要。这种情绪来自于我对自己工作的挫败感——如果我对 K8s 更加熟练,作为一名 ML 工程师的生活会更轻松。

然而,随着我对底层基础设施了解的增加,我意识到期望数据科学家了解这些是多么不切实际的。基础设施需要与数据科学完全不同的技能集。理论上,你可以学习两种技能。实际上,你在其中一种上花费的时间越多,就会在另一种上花费的时间越少。我喜欢埃里克·伯恩哈德森(Erik Bernhardsson)的比喻,认为期望数据科学家了解基础设施就像期望应用开发者了解 Linux 内核一样。⁴ 我加入了一个 ML 公司,是因为我想花更多时间处理数据,而不是花时间启动 AWS 实例、编写 Dockerfile、调度/扩展集群或调试 YAML 配置文件。

对于数据科学家来说,要想拥有整个流程,我们需要良好的工具。换句话说,我们需要良好的基础设施。

如果我们有一个抽象层,允许数据科学家在不必担心基础设施的情况下端到端地拥有整个过程会怎么样?

如果我只需告诉这个工具,“这是我存储数据的地方(S3),这是运行我的代码的步骤(特征化、建模),这是我的代码应该运行的地方(EC2 实例、AWS 批处理、函数等服务器端自动化),每个步骤需要什么样的代码运行环境(依赖关系)”,然后这个工具为我管理所有的基础设施事务?

根据 Stitch Fix 和 Netflix 的说法,全栈数据科学家的成功取决于他们拥有的工具。他们需要能够“从容器化、分布式处理、自动故障转移和其他高级计算机科学概念的复杂性中抽象出数据科学家的工具。”⁵

在 Netflix 的模式中,专家们——最初负责项目一部分的人——首先创建自动化其部分的工具,如 图 11-3 所示。数据科学家可以利用这些工具端到端地拥有他们的项目。

图 11-3. Netflix 的全周期开发者。来源:根据 Netflix 的图片修改而来⁶

我们已经讨论了 ML 系统可能如何影响用户体验以及组织结构可能如何影响 ML 项目的生产力。在本章的后半部分,我们将集中讨论更为关键的问题:ML 系统可能如何影响社会,以及 ML 系统开发者应该做什么来确保他们开发的系统产生更多的利益而非伤害。

负责任的 AI

本节内容由Abhishek Gupta,蒙特利尔人工智能伦理研究所的创始人兼首席研究员慷慨贡献撰写。他的工作侧重于应用技术和政策措施,构建伦理、安全和包容性的 AI 系统。

如何使智能系统负责任的问题不仅与 ML 系统相关,还涉及到一般的人工智能(AI)系统。AI 是一个比 ML 更广泛的术语。因此,在本节中,我们使用 AI 而不是 ML。

负责任的 AI 是以良好的意图和足够的意识设计、开发和部署 AI 系统的实践,以赋予用户权力,建立信任,并确保对社会产生公平和积极影响。它包括公平性、隐私、透明度和问责性等领域。

这些术语不再只是哲学的沉思,而是政策制定者和日常从业者的严肃考虑。鉴于 ML 正在被部署到我们生活的几乎每个方面,未能使我们的 ML 系统公平和道德可能导致灾难性后果,正如《数学毁灭的武器》(Cathy O’Neil,Crown Books,2016)所概述的那样,以及本书中提到的其他案例研究。

作为 ML 系统开发者,你不仅要考虑你的系统如何影响用户和整个社会,还要通过具体实施伦理、安全和包容性帮助所有利益相关者更好地认识他们对用户的责任。本节是关于未能充分努力使 ML 系统负责的情况简要介绍。我们将从两个 ML 非常不幸和公开的失败案例开始。然后,我们将为数据科学家和 ML 工程师提出一个初步框架,以选择最能帮助他们使其 ML 系统负责的工具和指南。

免责声明: 负责任的 AI 是一个复杂的主题,有着日益增长的文献,值得专门报道,并且很容易涵盖多本书。本节远非详尽指南。我们只旨在为 ML 开发者提供一个概述,以有效地引导这一领域的发展。那些对进一步阅读感兴趣的人强烈建议查阅以下资源:

不负责任的 AI:案例研究

我们将从两个 AI 系统失败的案例开始,这些失败严重伤害了这些系统的用户,也损害了开发这些系统的组织。我们将追溯组织哪些地方出错,以及从业者可能预见到这些失败点的方法。这些重点将作为我们深入探讨负责任 AI 工程框架的背景。

还有其他有趣的“AI 事件”例子记录在AI 事件数据库中。请记住,尽管以下两个例子以及 AI 事件数据库中记录的引起注意的例子,还有许多更多的不负责任的 AI 事件是悄无声息地发生的。

案例研究 I:自动评分系统的偏见

2020 年夏季,由于 COVID-19 大流行,英国取消了决定大学入学的重要考试 A-levels。英国教育和考试监管机构 Ofqual 批准使用自动化系统为学生分配最终的 A-level 成绩——而不需要他们参加考试。据来自 Ada Lovelace Institute 的 Jones 和 Safak 称,“根据教师评估授予学生成绩最初因不公平的学校之间、代际之间的不可比性和因成绩膨胀导致结果贬值而被 Ofqual 拒绝。Ofqual 推测,更公平的选择是结合先前的成绩数据和教师评估使用特定的统计模型——一种‘算法’。”⁷

然而,这一算法发布的结果被证明是不公正和不可信的。这很快引起了公众抗议,数百名学生高呼要求废除该算法⁸。

造成公众哀号的原因是什么?初看似乎指向算法性能差。Ofqual 称他们的模型在 2019 年的数据测试中,在 A-level 各科目中平均准确率约为 60%⁹。这意味着他们预计,通过该模型分配的成绩中,大约有 40%与学生实际成绩不同。

尽管该模型的准确性似乎较低,Ofqual 却辩称他们的算法与人工评分者的准确性基本相当。当将一名审查员的分数与一名资深审查员的分数进行比较时,一致性也约为 60%。¹⁰ 无论是人工审查员还是算法的准确性都暴露了在单一时间点评估学生时的潜在不确定性,¹¹ 进一步加剧了公众的不满情绪。

如果你到目前为止已经阅读了这本书,你会知道仅仅粗粒度的准确性远远不足以评估模型的性能,尤其是对于一个可能影响到如此多学生未来的模型。仔细分析这一算法揭示了在设计和开发这一自动评分系统过程中至少有三个主要的失败:

  • 设置错误的目标

  • 未能进行细粒度评估以发现潜在偏见

  • 未能使模型透明化

我们将详细讨论每一个这些失败。请记住,即使这些问题得到解决,公众对自动评分系统仍可能感到不满。

失败 1:设定了错误的目标

我们在第二章中讨论了一个机器学习项目的目标将如何影响最终机器学习系统的性能。当开发一个自动评分系统来对学生进行评分时,你本以为这个系统的目标应该是“对学生的评分准确性”。

然而,Ofqual 显然选择优化的目标是“保持各学校水平的标准”,即使这意味着模型的预测分数要符合每个学校的历史分数分布。例如,如果学校 A 在过去的历史上优于学校 B,Ofqual 希望一个算法在平均意义上也给学校 A 的学生比给学校 B 的学生更高的分数。Ofqual 更注重学校间的公平性而非学生间的公平性——他们更倾向于一个能正确预测学校级结果的模型,而不是一个能正确预测每个个体成绩的模型。

由于这一目标,该模型不成比例地降低了历史上表现优秀但学术表现较差学校的学生的成绩。那些曾经成绩一直是 D 的学生现在被评为 B 和 C。¹²

Ofqual 没有考虑到资源更多的学校往往表现优于资源较少的学校这一事实。通过优先考虑学校的历史表现而非学生当前表现,这个自动评分系统惩罚了来自资源较少学校、通常有更多弱势背景学生的学生。

失败 2:不足的细粒度模型评估未能发现偏见

针对历史表现低的学校学生的偏见只是在成绩公布后公众得知的该模型的许多偏见之一。自动评分系统考虑了教师的评估作为输入,但未能解决教师在跨人群评估中的不一致性。它还“没有考虑到一些受到《2010 年平等法》保护群体的多重劣势的影响,这些群体将因教师的低期望和某些学校普遍存在的种族歧视而受到双重/三重劣势。”¹³

由于该模型考虑了每所学校的历史表现,Ofqual 承认他们的模型对于小型学校的数据不足。对于这些学校,他们没有使用该算法来分配最终成绩,而是仅使用了教师评估的成绩。实际上,这导致了“私立学校学生获得更好的成绩,因为他们往往班级较小。”¹⁴

可能可以通过公开模型预测成绩并进行细粒度评估来发现这些偏见,以了解模型在不同数据片段(例如,评估不同规模学校和不同背景学生的模型准确性)中的表现。

失败 3: 缺乏透明度

透明度是建立系统信任的第一步,然而 Ofqual 在太晚之前未能公开其自动评分系统的重要方面。例如,他们直到成绩发布日才让公众知道他们系统的目标是维持学校之间的公平性。因此,公众在系统开发过程中无法表达对这一目标的担忧。

此外,Ofqual 在评估和学生排名提交后才让教师知道他们的评估将如何被自动评分系统使用。Ofqual 的理由是为了避免教师试图改变他们的评估以影响模型的预测。Ofqual 选择在成绩公布日之前不公布确切的使用模型,以确保每个人在同一时间知道他们的成绩。

这些考虑是出于善意;然而,Ofqual 决定在暗中开发他们的模型意味着他们的系统没有得到足够的独立外部审查。任何依靠公众信任运行的系统都应该由公众信任的独立专家审查。皇家统计学会(RSS)在对这个自动评分器的开发进行调查时,对 Ofqual 组建的“技术咨询组”的构成表示担忧。RSS 指出,“没有更强有力的程序基础来确保统计严谨性,以及 Ofqual 正在审查的问题更透明”,¹⁵ 这引发了对 Ofqual 统计模型合法性的质疑。

该案例研究显示了在构建可能对如此多人生产生直接影响的模型时透明度的重要性,以及在适当时未能披露模型重要方面可能导致的后果。它还显示了选择正确的优化目标的重要性,因为错误的目标(例如,优先考虑学校的公平性)不仅可能导致选择表现不佳的模型来达到正确的目标,而且可能会使偏见持续存在。

它还展示了当前在应该由算法自动化和不应该由算法自动化之间的混乱边界。在英国政府中必然有人认为让 A-level 成绩评分由算法自动化是可以接受的,但也可以争论说,由于 A-level 评分可能带来的灾难性后果,它本应该从一开始就不应该被自动化。在没有更清晰的界限之前,会有更多误用 AI 算法的情况发生。只有通过更多的时间和资源投资以及 AI 开发者、公众和当局的认真考虑,才能实现更清晰的界限。

案例研究 II: “匿名化”数据的危险

对我来说,这个案例研究很有趣,因为在这里,算法并不是明显的罪魁祸首。而是接口和数据收集的设计使得敏感数据泄露成为可能。由于 ML 系统的开发严重依赖数据的质量,收集用户数据变得非常重要。研究界需要访问高质量的数据集来开发新技术。从业者和公司需要访问数据来发现新的用例并开发新的 AI 驱动产品。

然而,收集和共享数据集可能会侵犯那些数据包含在内的用户的隐私和安全。为了保护用户,有人呼吁对可识别个人信息(PII)进行匿名化处理。根据美国劳工部的定义,PII 被定义为“任何信息的表达形式,通过直接或间接手段可以合理推断出信息适用的个人身份”,如姓名、地址或电话号码。¹⁶

然而,匿名化可能不足以防止数据滥用和隐私期望的侵蚀。2018 年,在线健身追踪器 Strava 发布了一张热力图,显示了全球用户在运动(如跑步、慢跑或游泳)时记录的路径。这张热力图聚合了 2015 年到 2017 年 9 月间记录的 10 亿次活动数据,涵盖了 270 亿公里的距离。Strava 声称使用的数据已经进行了匿名化,并且“排除了标记为私密以及用户定义的隐私区域之外的活动”。¹⁷

由于 Strava 曾被军事人员使用,尽管已经进行了匿名化处理,但公开数据使人们能够发现显示美国海外军事基地活动的模式,包括“阿富汗前沿作战基地、叙利亚的土耳其军事巡逻以及俄罗斯在叙利亚运营区可能的警戒巡逻”。这些歧视性模式的例子显示在图 11-4 中。一些分析人士甚至建议这些数据可以揭示个别 Strava 用户的姓名和心率。¹⁹

那么,匿名化出了什么问题?首先,Strava 的默认隐私设置是“选择退出”,这意味着用户需要手动选择退出,如果他们不希望他们的数据被收集。然而,用户指出这些隐私设置并不总是清晰的,可能会给用户带来惊喜。²⁰有些隐私设置只能通过 Strava 网站而非其移动应用程序更改。这显示了教育用户关于隐私设置的重要性。更好的做法是默认选择数据(数据收集不是默认的),而不是选择退出。

图 11-4. 根据 BBC News 的分析创建的图像²¹

当 Strava 热力图的问题公开化后,一些责任被转移到了用户身上:例如,军事人员不应使用带有 GPS 跟踪的非军用设备,以及应该关闭位置服务。²²

然而,隐私设置和用户的选择只是在表面层面解决问题。潜在问题是,我们今天使用的设备不断收集和报告关于我们的数据。这些数据必须移动和存储在某个地方,从而为其被截取和误用创造机会。与像亚马逊、Facebook、Google 等更广泛使用的应用程序相比,Strava 的数据规模较小。Strava 的失误可能暴露了军事基地的活动,但其他隐私失误可能不仅对个人,而且对整个社会造成更大的危险。

收集和分享数据对于像人工智能这样的数据驱动技术的发展至关重要。然而,这个案例研究显示了即使数据被假定为匿名化并出于良好意图发布,收集和分享数据也存在潜在的危险。收集用户数据的应用程序开发者必须明白,他们的用户可能没有技术知识和隐私意识来为自己选择正确的隐私设置,因此开发者必须积极努力使正确设置成为默认选项,即使这可能导致收集的数据更少。

负责任人工智能的框架

在这一节中,我们将为您作为机器学习从业者奠定基础,以审计模型行为并制定最佳指南,以最好地满足项目需求。这个框架并不适用于每个用例。在某些应用中,使用人工智能可能完全不合适或不道德(例如,刑事判决决策,预测性执法),无论您遵循哪种框架。

发现模型偏见的来源

作为一个关注机器学习系统设计讨论的人,你知道偏见可能通过整个工作流程渗入你的系统中。你的第一步是发现这些偏见是如何渗入的。以下是一些数据来源的示例,但请记住,这个列表远非详尽无遗。偏见之所以难以对抗的其中一个原因是,它们可以来自项目生命周期的任何步骤。

训练数据

用于开发模型的数据是否代表了您的模型将在现实世界中处理的数据?如果不是,您的模型可能会对在训练数据中代表少量数据的用户群体存在偏见。

标注

如果您使用人类标注员为数据打标签,您如何衡量这些标签的质量?您如何确保标注员遵循标准指南而不是依靠主观经验为数据打标签?标注员越依赖他们的主观经验,人为偏见的空间就越大。

特征工程

你的模型是否使用包含敏感信息的特征?你的模型是否对某个人群产生了不平等的影响?即使看起来是中立的,当选择过程对不同群体有着显著不同的结果时即发生“不平等影响”²³。例如,招聘过程如果利用与种族相关的变量(如邮政编码和高中学历)可能导致种族不平等影响。为了减少这种潜在的不平等影响,你可能需要使用 Feldman 等人在 “认证和消除不平等影响” 中提出的不平等影响消除技术,或者使用由 AI Fairness 360(AIF360) 实现的 DisparateImpactRemover 函数。你还可以使用 H2O 中实施的 Infogram 方法 识别变量中的隐藏偏见(然后从训练集中去除)。

模型的目标

你是否使用能够对所有用户实现公平的目标来优化你的模型?例如,你是否优先考虑模型在所有用户上的表现,从而使你的模型偏向于多数用户群体?

评估

你是否进行了充分的、细致的评估,以了解模型在不同用户群体上的表现?这在 “基于切片的评估” 部分有详细介绍。公正、充分的评估取决于存在公正、充分的评估数据。

理解数据驱动方法的局限性

机器学习是一种数据驱动的解决问题的方法。然而,理解的数据并不足够。数据涉及到现实世界中的人们,需要考虑到社会经济和文化等方面。我们需要更好地理解由于过度依赖数据而造成的盲点。这通常意味着跨越学科和功能边界,无论是组织内还是组织外,以便考虑到那些将受到我们构建系统影响的人们的生活经验。

例如,要构建一个公平的自动评分系统,与领域专家合作以了解学生群体的人口统计分布以及历史表现数据中反映的社会经济因素是至关重要的。

理解不同期望之间的权衡

在构建机器学习系统时,可能希望该系统具备不同的特性。例如,您可能希望系统具有低推断延迟,这可以通过像修剪这样的模型压缩技术实现。您可能还希望模型具有高预测准确性,这可以通过增加数据量来实现。您可能还希望模型具有公平和透明性,这可能需要将用于开发此模型的模型和数据公开供公众审查。

通常,机器学习文献会做出一个不切实际的假设,即优化模型准确性这样一个特性,其他所有特性都保持不变。人们可能会讨论提高模型公平性的技术,假设这将不会影响模型的准确性或延迟。然而,在现实中,改进一个特性可能会导致其他特性的降低。以下是这些权衡的两个例子:

隐私与准确性的权衡

根据维基百科,差分隐私是“通过描述数据集内组群的模式来公开共享有关数据集的信息,同时隐瞒数据集中个体的信息。差分隐私的理念在于,如果对数据库进行任意单个替换的影响足够小,则查询结果无法用于推断任何单个个体的信息,因此提供隐私保护。”²⁴

差分隐私是用于机器学习模型训练数据的一种流行技术。这里的权衡是,差分隐私能够提供的隐私保护级别越高,模型的准确性就越低。然而,这种准确性降低并不对所有样本都一样。正如 Bagdasaryan 和 Shmatikov(2019)指出的那样,“差分隐私模型的准确性对于少数族裔和次群体的影响更大。”²⁵

紧凑性与公平性的权衡

在第七章中,我们详细讨论了诸如修剪和量化之类的模型压缩技术。我们了解到,可以在几乎不损失准确性的情况下显著减小模型的大小,例如将模型参数数量减少 90%。

如果最小的准确性损失均匀分布在所有类别中,成本确实是最小的,但如果成本集中在少数类别中呢?在他们 2019 年的论文《压缩深度神经网络会忘记什么?》中,Hooker 等人发现,“具有截然不同权重数量的模型在顶线性能指标上具有可比性,但在数据集的某个狭窄子集上的行为却有很大分歧。”²⁶ 例如,他们发现,当受保护特征(例如性别、种族、残疾)位于分布的长尾时,压缩技术会放大算法的伤害,这意味着压缩对代表性较低的特征影响更大。²⁷

他们的工作中另一个重要发现是,尽管他们评估的所有压缩技术对不均衡的影响都不同,但并非所有技术都具有相同水平的差异影响。与他们评估的量化技术相比,修剪技术造成的差异影响要高得多。²⁸

发现类似的权衡继续进行。了解这些权衡是非常重要的,这样我们才能为我们的 ML 系统做出明智的设计决策。如果您正在处理压缩或差分私有的系统,建议增加资源用于审计模型行为,以避免意外的伤害。

早期行动

考虑正在市中心建造的新建筑。承包商被召集来建造一些将在未来 75 年内屹立不倒的东西。为了节省成本,承包商使用了劣质水泥。业主没有投资监督,因为他们想避免额外开销以便快速推进。承包商继续在这个不良基础上建造并按时完成了建筑。

一年之内,裂缝开始出现,建筑物可能倾斜。市政府认为这栋建筑存在安全风险,并要求拆除它。承包商为了节省成本的决定以及业主为了节省时间的决定,现在导致业主付出更多的金钱和时间代价。

您可能经常在 ML 系统中遇到这样的叙事。公司可能会决定绕过 ML 模型中的道德问题以节省成本和时间,但最终却发现风险,这会导致像 Ofqual 和 Strava 的前述案例一样花费更多。

在 ML 系统的开发周期越早开始思考该系统将如何影响用户的生活以及您的系统可能存在的偏见,就越便宜解决这些偏见。NASA 的一项研究显示,对于软件开发,错误成本在项目生命周期的每个阶段都会增加一个数量级。²⁹

创建模型卡片

模型卡是随训练好的 ML 模型一起提供的简短文档,提供了这些模型的训练和评估信息。模型卡还披露了模型预期使用的背景及其局限性。³⁰ 根据模型卡论文的作者,“模型卡的目标是通过允许利益相关者比较候选模型的部署,标准化道德实践和报告,不仅沿着传统评估指标的轴线,还沿着伦理、包容和公平考虑的轴线。”

下面的列表已从论文“模型报告的模型卡”中调整,以展示您可能希望为您的模型报告的信息:³¹

  • 模型细节:关于模型的基本信息。

    • 开发模型的人或组织

    • 模型日期

    • 模型版本

    • 模型类型

    • 关于训练算法、参数、公平性约束或其他应用方法以及特征的信息

    • 更多信息的论文或其他资源

    • 引用详情

    • 许可证

    • 提出关于模型的问题或意见的地方

  • 预期用途:在开发过程中设想的使用情况。

    • 主要预期用途

    • 主要预期用户

    • 不在范围内的使用案例

  • 因素:因素可能包括人口统计学或表型群体、环境条件、技术属性或其他因素。

    • 相关因素

    • 评估因素

  • 指标:应选择反映模型可能在现实世界中产生影响的指标。

    • 模型性能指标

    • 决策阈值

    • 变化方法

  • 评估数据:卡片中用于定量分析的数据集详细信息。

    • 数据集

    • 动机

    • 预处理

  • 训练数据:在实践中可能无法提供。如果可能,该部分应与评估数据部分相对应。如果无法提供此类详细信息,则应在此提供最少允许的信息,例如训练数据集中各种因素的分布详情。

  • 定量分析

    • 单元结果

    • 交叉结果

  • 伦理考虑

  • 注意事项和建议

模型卡是增加透明度的一步,揭示了 ML 模型开发的过程。尤其是在使用模型的人与开发该模型的人不同的情况下,这一点尤为重要。

需要注意的是,每当模型更新时,模型卡片都需要更新。对于频繁更新的模型,如果模型卡片是手动创建的,这可能会给数据科学家带来很大的负担。因此,很重要能够利用像TensorFlowMetaflow,和scikit-learn这样的工具自动生成模型卡片,或者在内部构建此功能。因为应该在模型卡片中跟踪的信息与模型存储中应该跟踪的信息重叠,所以我不会感到意外,如果在不久的将来,模型存储会发展出自动生成模型卡片的能力。

建立减少偏见的过程

建立负责任的人工智能是一个复杂的过程,过程越临时化,出错的可能性就越大。企业建立系统化的过程来确保他们的机器学习系统负责任至关重要。

您可能希望创建一组对不同利益相关者易于访问的内部工具组合。大公司有您可以参考的工具集。例如,谷歌已经发布了负责任 AI 的推荐最佳实践,IBM 开源了AI Fairness 360,其中包含一组用于减少数据集和模型偏见的度量标准、解释和算法。您也可以考虑使用第三方审计。

保持对负责任 AI 的最新了解

AI 是一个快速发展的领域。AI 中新的偏见来源不断被发现,负责任 AI 面临新的挑战不断涌现。正在积极开发新的技术来应对这些偏见和挑战是非常重要的。保持对负责任 AI 最新研究的跟踪是重要的。您可能希望关注ACM FAccT 会议Partnership on AIAlan Turing Institute 的公平性、透明性、隐私组,以及AI Now Institute

总结

尽管 ML 解决方案具有技术性质,但设计 ML 系统不能仅限于技术领域。它们由人类开发,由人类使用,并在社会中留下影响。在本章中,我们偏离了过去八章的技术主题,专注于 ML 的人类方面。

我们首先关注了概率性、大多数正确以及高延迟的机器学习系统如何在各种方式影响用户体验。概率性质可以导致用户体验的不一致性,这可能引起沮丧——“嘿,我刚刚看到这个选项在这里,现在我却找不到它了。”如果用户无法轻松地修正这些预测使其正确,那么大多数正确的机器学习系统可能会变得无用。为了解决这个问题,您可能希望向用户展示多个相同输入的“最正确”预测,希望至少有一个是正确的。

建立一个机器学习系统通常需要多种技能,组织可能会考虑如何分配这些所需的技能:是让不同技能的团队参与(例如,数据科学家),还是期望同一个团队具备所有技能。我们探讨了两种方法的利弊。第一种方法的主要缺点是沟通成本增加。第二种方法的主要缺点是很难雇佣能够负责整个机器学习系统开发过程的数据科学家。即使能够找到这样的人才,他们也可能不愿意做这件事。然而,如果这些全流程数据科学家提供了足够的工具和基础设施,第二种方法可能是可行的,这正是第十章的重点。

我们以我认为是本书最重要的话题之一结束了这一章:负责任的人工智能。负责任的人工智能不再只是一个抽象概念,而是当今机器学习行业中至关重要的实践,值得紧急行动。将伦理原则融入到您的建模和组织实践中,不仅有助于您在专业和尖端数据科学家和机器学习工程师中脱颖而出,还有助于您的组织赢得客户和用户的信任。这还将有助于您的组织在市场上获得竞争优势,因为越来越多的客户和用户强调他们对负责任人工智能产品和服务的需求。

对待这种负责任的人工智能不能仅仅当作我们为了满足组织的合规要求而进行的一个勾选活动。这一章提出的框架确实可以帮助您满足组织的合规要求,但这不会取代对产品或服务是否应该首先构建进行深思熟虑的关键思维。

¹ 有时,如果你同时运行相同的模型在完全相同的时间,可能会得到不同的结果。

² Eugene Yan,“不受欢迎的观点——数据科学家应更全流程”,EugeneYan.com,2020 年 8 月 9 日,https://oreil.ly/A6oPi

³ Eric Colson,“小心数据科学的引脚工厂:全栈数据科学通才的力量及分工带来的危害”,MultiThreaded,2019 年 3 月 11 日,https://oreil.ly/m6WWu.

⁴ Erik Bernhardsson 在 Twitter 上的发言(@bernhardsson),2021 年 7 月 20 日,https://oreil.ly/7X4J9.

⁵ Colson,“小心数据科学的引脚工厂。”

⁶ “Netflix 全周期开发者——运行你所构建的”,Netflix Technology Blog,2018 年 5 月 17 日,https://oreil.ly/iYgQs.

⁷ Elliot Jones 和 Cansu Safak,“算法能否成绩优异?” Ada Lovelace Institute Blog,2020 年,https://oreil.ly/ztTxR.

⁸ Tom Simonite,“偏斜的评分算法引发课堂之外的反弹”,Wired,2020 年 8 月 19 日,https://oreil.ly/GFRet.

⁹ Ofqual,“2020 年夏季颁发 GCSE、AS 和 A 级考试成绩的临时报告”,Gov.uk,2020 年 8 月 13 日,https://oreil.ly/r22iz.

¹⁰ Ofqual,“颁发 GCSE、AS 和 A 级考试成绩。”

¹¹ Jones and Safak,“算法能否成绩优异?”

¹² Jones and Safak,“算法能否成绩优异?”

¹³ Ofqual,“颁发 GCSE、AS & A Level 资格证书。”

¹⁴ Jones and Safak,“算法能否成绩优异?”

¹⁵ “皇家统计学会对下议院教育选择委员会关于 COVID-19 对教育和儿童服务影响的调查的回应”,皇家统计学会,2020 年 6 月 8 日,https://oreil.ly/ernho.

¹⁶ “个人可识别信息保护指导”,美国劳工部,https://oreil.ly/FokAV.

¹⁷ Sasha Lekach,“Strava 的健身热图对军事存在重大安全问题”,Mashable,2018 年 1 月 28 日,https://oreil.ly/9ogYx.

¹⁸ Jeremy Hsu,“Strava 热图和秘密的终结”,Wired,2018 年 1 月 29 日,https://oreil.ly/mB0GD.

¹⁹ Matt Burgess,“Strava 的热图数据让任何人看到在军事基地上锻炼的人的姓名”,Wired,2018 年 1 月 30 日,https://oreil.ly/eJPdj.

²⁰ Matt Burgess,“Strava 的热图数据让任何人都能看到”;Rosie Spinks,“使用健身应用教会我关于隐私设置为何是女权主义问题的可怕真相”,Quartz,2017 年 8 月 1 日,https://oreil.ly/DO3WR

²¹ “健身应用 Strava 照亮军事基地的工作人员”,BBC News,2018 年 1 月 29 日,https://oreil.ly/hXwpN

²² Matt Burgess,“Strava 的热图数据让任何人都能看到”。

²³ Michael Feldman, Sorelle Friedler, John Moeller, Carlos Scheidegger 和 Suresh Venkatasubramanian,“认证和消除不公平影响”,arXiv,2015 年 7 月 16 日,https://oreil.ly/FjSve

²⁴ Wikipedia,“差分隐私”,https://oreil.ly/UcxzZ

²⁵ Eugene Bagdasaryan 和 Vitaly Shmatikov,“差分隐私对模型准确性的不同影响”,arXiv,2019 年 5 月 28 日,https://oreil.ly/nrJGK

²⁶ Sarah Hooker, Aaron Courville, Gregory Clark, Yann Dauphin 和 Andrea Frome,“压缩深度神经网络忘记了什么?”arXiv,2019 年 11 月 13 日,https://oreil.ly/bgfFX

²⁷ Sara Hooker, Nyalleng Moorosi, Gregory Clark, Samy Bengio 和 Emily Denton,“压缩模型中的偏差特征”,arXiv,2020 年 10 月 6 日,https://oreil.ly/ZTI72

²⁸ Hooker 等人,“压缩模型中的偏差特征”。

²⁹ Jonette M. Stecklein, Jim Dabney, Brandon Dick, Bill Haskins, Randy Lovell 和 Gregory Moroney,“项目生命周期中的错误成本升级”,NASA 技术报告服务器(NTRS),https://oreil.ly/edzaB

³⁰ Margaret Mitchell, Simone Wu, Andrew Zaldivar, Parker Barnes, Lucy Vasserman, Ben Hutchinson, Elena Spitzer, Inioluwa Deborah Raji 和 Timnit Gebru,“模型报告的模型卡”,arXiv,2018 年 10 月 5 日,https://oreil.ly/COpah

³¹ Mitchell 等人,“模型报告的模型卡”。

第十二章:结语

哇,你做到了!你刚刚完成了一本由英语作为第二语言的作家撰写的、涵盖 10 万字和 100 多幅插图的相当技术性的书籍。在许多同事和导师的帮助下,我为这本书付出了很大的努力,感谢你在众多书籍中选择阅读它。我希望你从这本书中得到的收获能让你的工作变得更轻松一些。

现在,通过我们拥有的最佳实践和工具,已经有许多令人难以置信的机器学习应用案例影响着我们的日常生活。我毫不怀疑,随着工具的成熟,影响力巨大的使用案例数量将会随着时间的推移而增长,而你可能会成为推动这一切发生的人之一。我期待看到你创造出什么!

ML 系统存在许多挑战。并非所有挑战都令人愉快,但它们都是成长和影响的机会。如果你想讨论这些挑战和机会,请毫不犹豫地联系我。你可以在 Twitter 上找到我,我的账号是 @chipro,或通过电子邮件联系我:chip@claypot.ai。

posted @ 2025-11-21 09:09  绝不原创的飞龙  阅读(51)  评论(0)    收藏  举报