TowardsDataScience 2023 博客中文翻译(六十三)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

建立分析成熟的组织(AMO)

原文:towardsdatascience.com/building-analytically-mature-organizations-amo-b54f8243ef3a?source=collection_archive---------1-----------------------#2023-10-30

一些简单的框架可以帮助确定您组织的分析需求以及如何使其更具数据驱动性

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Jordan Gomes

·

关注 发表在 Towards Data Science ·13 分钟阅读·2023 年 10 月 30 日

了解您组织的分析成熟度可以为您作为数据专业人士提供强大的优势。它将使您的“非分析”决策更为明智(从“项目优先级”到“如何展示您的发现”),并帮助您制定长期目标。这确实是一种优势——并非所有数据专业人士都愿意退一步设计长期目标(更少的人能实现这些长期目标)。

本文分为 3 部分:

  • 第一部分:理解分析成熟度

  • 第二部分:移动阶段

  • 第三部分:“成熟”组织是什么

让我们深入探讨!

了解分析成熟度

任何组织(团队、产品、公司等)在某个时间点都会处于特定的分析成熟度阶段。就像人类“爬行、走路然后跑步”一样,组织也经历相同的阶段。这是自然界的定律之一:一切都经历创作、发展和成熟。

一些有趣的框架¹与分析成熟度相关,具有不同的组件和重点。从个人经验来看,我发现通过以下 4 个组件来看待一个组织是最有用和可操作的:

  • 其需求:Robert D. Peng 和 Elizabeth Matsui 在“数据的艺术”中写道,主要有 6 种类型的问题:描述性问题、探索性问题、推论性问题、预测性问题、因果性问题和机制性问题。你被问到的问题类型通常是组织成熟度水平的一个很好的指标——一个低成熟度的组织通常对描述性和探索性数据研究感兴趣,而一个高级成熟度的组织会提出更多预测性和因果性问题。

  • 其人员:另一个分析成熟度的关键组件是人员,包括能力和资源。取决于组织拥有多少数据资源以及他们的能力有多强。

  • 其工具和流程:是否有标准化的工具供数据专业人员使用?我们是否有标准化的流程(例如,优先级、模板等)供数据团队使用?

  • 其文化:在决策过程中直觉与数据的分配比例如何?

根据你所在组织在这些组件上的评分,它将落入这三个阶段之一:

  • 启动阶段: 在这个阶段,组织需要基本的报告来了解已经发生了什么(“事后诸葛亮”)。没有中央数据团队,可能甚至没有数据分析师——数据研究由一些数据敏锐的操作人员在他们的 9 到 5 工作之外完成。也没有工具,没有流程,没有关于查看特定现象时应使用什么视角的明确一致意见。这导致了很多噪音(例如,不同团队有不同的流失定义,导致后续意见不一致)。在文化方面,尽管每个人都同意数据应该指导决策过程,但由于数据不足(或对数据的不信任),很多决策都是通过“有依据的直觉”来做出的。

  • 发展阶段: 组织对市场及其应该跟踪的一些关键指标有了良好的可见性。现在,它需要理解为什么事情以某种方式发展(“洞察”)。团队开始得到数据专业人士的支持(无论是嵌入在团队内部还是在集中式数据团队中)。数据基础设施正从 Google Spreadsheets 缓慢转变为更为强大的工具。为了分类和优先处理所有的数据请求,首批数据专业人士建立了基本的优先级原则和票务系统(即 Google Form)。各团队开始采用共同的视角,因此数据在决策中越来越被依赖。非数据专业人士在数据问题上变得更聪明,通过工具,非数据专业人士可以自己查看数据。

  • 成熟阶段: 组织理解了为什么事情以某种方式发展,并现在可以预测和影响未来的变化(“前瞻性”)。集中式数据团队开始形成,充当主动的思想合作伙伴(与之前阶段的“被动支持”相比)。工具、流程和指标正在标准化。数据在每个决策过程中都被期待。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

了解分析成熟度(图片由作者提供)

上面的图像是对现实生活的简化。实际上,组织在每个组件上的评分可能非常不同——但你可以大致理解其要点。这个框架的美妙之处在于:

  • 它为你提供了一种结构化的方式来发现阻碍组织分析成长的关键因素。

  • 它可以帮助你精准定位组织在其旅程中的位置——以及下一步是什么。

这正是你在知道如何使用这个框架时获得强大优势的原因:它提供了一种方式来理解你当前的位置和你可能达到的目标,并诊断为何尚未达到目标。你的任务则是“仅仅”制定一个策略来去除障碍——这正是我们在接下来要探讨的内容。

阶段推进

理查德·鲁梅特在《好战略,坏战略》中写道:“战略工作的核心始终是相同的:发现情况中的关键因素,并设计协调和聚焦行动的方法以应对这些因素”。

当你想提升组织的分析成熟度时,这同样适用:你需要找出帮助你迈向下一步的关键因素,并设计一个计划来实现目标。我们上面看到的框架——将分析成熟度分解为 4 个组件:组织的需求、数据资源、流程与工具以及数据文化——可以帮助你找出组织中的差距——但找出差距只是工作中的 20%。让我们讨论剩下的 80%。

好战略,坏战略框架

我喜欢理查德·鲁梅尔特的书,我认为它提供了一个出色的框架来思考这个问题。他解释说,一个好的战略有三个要素:

  • 诊断:框架中最重要的部分是诊断——它是你整个逻辑方法的基础。你的诊断应当使你能够了解当前的情况,同时也能了解组织存在的原因和“为什么”。

  • 一些指导原则:从这些诊断中,你可以得出一些指导原则——一旦你开始提升分析成熟度,这些原则将帮助你使决策过程更简单,并帮助你在时间上保持正确的轨道。

  • 遵循上述的连贯行动计划:凭借你的诊断和指导原则,你的主要任务是决定你希望在什么时间范围内达到什么目标,以及你将如何实现这一目标。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

良好战略与差劲战略框架(图源:作者)

从诊断开始

“表述清楚的问题已解决一半”——约翰·杜威

思路是理解当前情况及其背后的真正“原因”。你不想解决症状——你的目标是深入根本原因,并修复需要修复的地方。

这里有一些进行良好诊断的技巧:

  1. 从我们之前看到的 4 个维度出发:需求/人员/工具与流程/文化,使用这些视角评估你的组织,并找出每个领域的根本原因。

  2. 获取当前痛点和解决方案的数据:

  3. 采访人员:了解人们,他们的工作,他们的决策过程,以及他们如何在日常工作中使用数据。

  4. 影子观察:类似地,影子观察可以是深入了解他们日常工作的好方法,并且可以发现仅通过采访无法获得的见解。

  5. 发送调查问卷:根据你的组织规模,发送调查问卷可以帮助你获取更多的定量数据。附加好处:它还可以让你开始跟踪组织对“分析”的感受,并给你一个可以后续报告的基准。

  6. 做“文献回顾”,既包括内部(回顾之前的工作,了解人们如何尝试解决之前的问题,成功与否,以及原因),也包括外部(网上有很多免费的内容,你考虑的问题很可能已经被记录和讨论过(无论是在 HBR 上的一篇好文章,还是在一个晦涩的分析爱好者论坛上)。获取他人对不同问题解决方案的看法总是非常有帮助的)。

  7. 练习 5 个“为什么”:每次你发现新的见解时,问自己为什么。你需要从整体的角度来看待问题,理解组织所处状况的关键原因。请注意,这不是一件容易的任务,尤其是当你在公司待了很长时间,并且习惯了现状时。

推导指导政策

“每个人都有计划,直到他们被打了一拳” —— 迈克·泰森

诊断将揭示一些模式,这些模式应该能让你推导出指导原则。这些指导原则在几种不同的情况下会很有用:

  • 在定义你的行动计划时:将这些视为高速公路上的“护栏”:它们将帮助你始终保持正轨,确保你诊断的问题能够得到解决

  • 当面临你未预料到的情况时:你可以利用不同的原则来促进和指导你的决策——这将为你带来无比的安心

  • 在进行权衡或对利益相关者说“不”时:说“不”总是很复杂——但这是良好战略的核心。通过明确你的原则并让利益相关者同意它们,拒绝他们的要求会更容易被他们接受。

指导原则中最难的部分是坚持这些原则——就像生活中一样。

制定行动计划

这个行动计划需要一致性和连贯性,并覆盖分析成熟度的不同组件。

如何制定行动计划:

  1. 找到你所支持组织中的主题专家,并与他们一起制定计划:

  2. 向他们介绍你的诊断和指导原则,并与他们一起头脑风暴下一步该做什么以及时间框架。

  3. 如果你在一个快速发展的组织中,考虑优化选择性——为你提供时间来推动组织的成熟度,同时也能应对“火警演习”或时间敏感的问题

  4. 超越你的组织思考:如果你支持的是更大公司的某一部分,还需要考虑如何与你的其他分析职能进行互动,并将这些添加到你的计划中

  5. 设置成功标准:每当有定性工作进行时,不要忘记设置成功标准。就像其他工作一样,你应该能够在完成后判断这是否成功。因此,设置一个二元成功标准,以便告诉你完成得如何。认真思考一下——确保标准能够准确代表你要解决的问题。

  6. 设置报告流程和时间表:完成工作很重要,但如果没有人知道或使用你所建立的内容,你是否真的在创造价值?设置一个适当的报告流程将使你能够一次实现多个目标:

  7. 让你的工作获得更大受众的可见性,并促进合作机会

  8. 为你的新分析产品提供市场推广策略(因为你有一个展示新仪表板和报告的场所)

  9. 确保领导层的支持:没有领导的支持,你无法围绕数据建立文化。向他们展示计划,并获得他们的支持,以确保顺利实现你的目标

成功的公式

FS 新闻通讯 之前分享了一个小小的想法:

“成功的秘诀:

  • 勇于开始。

  • 专注的纪律。

  • 有解决问题的信心。

  • 有耐心去了解进展并不总是显而易见。

  • 即使在糟糕的日子里,也要有坚持下去的毅力。

最终——这就是全部的核心。你需要有勇气开始讨论你组织的分析成熟度及其应该达到的水平,具备制定行动计划的纪律(同时处理即时的紧急问题),有信心找到合适的解决方案,尽管可能会有反对意见,还需要有耐心和坚持不懈地向前推进。

希望你能达到最终目标:建立一个分析成熟的组织。

最终目标:分析成熟组织(AMO)。

我一直在谈论 AMO(分析成熟组织),我们也见证了如何发展它——但我从未具体说明什么是分析成熟的组织,以及它为何如此出色。所以这是第三部分——提供了分析成熟组织与众不同的具体例子!

一个 AMO 是一个了解其市场复杂动态的组织,以及哪些活动可以影响它的组织。

分析成熟的组织清楚地了解其活动(“输入指标”)如何驱动短期结果(“输出指标”),而这些短期结果又进一步驱动长期成果(“成果指标”)。

  • 例如:一个分析成熟的市场营销组织将知道发送促销邮件(输入:发送邮件的数量)对推动新注册用户(输出:注册用户的数量)的影响,以及这些注册用户在未来转化为付费用户(成果:付费用户数量)的程度。他们会使用不同的比率(注册 vs 发送)并对其不同的活动进行基准测试,帮助他们提高技能。

成熟的组织还将对影响其主要指标的关键因素有清晰的理解。他们可以无缝地进行根本原因分析,以了解这些主要指标的演变,并采取纠正措施。

  • 例如:一个销售组织将能够根据可能存在的困难或有利机会,确定优先考虑哪些渠道和客户群体。他们已经完善了调查过程——到达能够自动化它的程度,此时,一个算法可以直接将正确的洞察信息呈现给正确的人。

数据需求已经转向更多的“复杂”问题——例如机会规模、因果影响跟踪等。更困难的问题——需要深入的领域专业知识以及先进的统计方法。

  • 例如:一个分析成熟的人力资源组织会希望开始研究如何驱动员工留存和/或成功——为此,它将开始进行因果影响分析,以提取预测成功的关键因素。

一个分析成熟的组织是一个少数专业数据团队协作的组织。

  • 一个分析成熟组织的整体框架依赖于干净的数据——这就是为什么在一个分析成熟的组织中,你有数据工程师正在创建管道、数据集和数据库,并且承诺遵守非常严格的规则和“服务级别协议”(SLA),以便下游团队(如数据科学或商业智能)可以轻松使用这些数据。

  • 你还有产品经理,与数据工程师一起工作,确保构建正确的数据库来解决组织最紧迫的痛点,并建立工具以改善数据可发现性(即使在一个非常成熟的组织中,这始终是一个复杂的话题)。

  • 你有数据科学家,他们消耗所有这些数据并将其转化为更深入的见解,以供产品和业务用户使用——使组织能够做出更好的决策。他们通常是一个相当核心的团队,其工作会影响上游和下游团队的路线图(即,他们的需求将影响数据工程团队的路线图,而他们的发现通常会影响其他分析团队的工作)。

  • 最后,你有业务/数据/财务分析师,他们支持战略决策和日常运营。

举一个大型零售商的具体例子:

  • 数据工程师将构建合适的管道,以确保我们拥有每日数据库,其中包括商店名称、位置、库存、每件商品的销售数量等。

  • 数据科学家将使用这些数据库进行“市场篮子分析”——以揭示哪些商品最常一起购买。

  • 业务分析师将这些发现转化为如何在不同的商店中实施的计划。他们将建立指标来跟踪“实施情况”(并可能为不同商店设定 OKR)。

一个分析成熟的组织是一个拥有强大工具和标准化流程的组织——这使得不同团队能够更快、更高质量地得出洞察。

  • 在一个 AMO 中,已经实施了强大的数据治理流程,使人们更容易使用数据。分析师不必花费数小时逐一检查每个数据源——他们可以信任几个经过认证的数据库和指标,这大大节省了他们宝贵的时间。

  • 已经建立(或实施)了多个工具来标准化典型的数据研究——这减少了个人贡献者出错的可能性,并使更多人能够获得所需的洞察。

  • 例如:你不再需要为 A/B 测试进行统计测试,而是有一个工具,你只需输入数据,它会自动为你完成这些操作。

  • 同样,从项目管理的角度来看——研究的常规“步骤”已经被映射、正式化和标准化(从优先级决策过程到研究的内部市场推广)。得益于这些正式化的流程,组织更容易理解谁在做什么,如何与不同的数据团队进行协作。

最终,一个分析成熟的组织是一个每个人都具备数据敏锐性的组织。

  • 知识管理已经成为优先事项(而不仅仅是事后的思考),因此人们发现很容易找到资源和支持来回答他们的数据请求。

  • 还有一些令人鼓舞的、经验丰富的“数据领袖”已经开始组织一个内部的“数据爱好者”生态系统(更多内容请参见下一篇文章!)

  • 内部培训是可用的,并且在提升人们的技能——无论他们在数据旅程中的哪个阶段。

  • 数据论坛是“酷”的地方——这里是精彩对话和重大决策的场所。数据团队被视为“思想伙伴”,在关键决策时会被召集到讨论桌前。每一个决策都是以数据为基础,甚至是由数据驱动的。

总结来说,你有一个运转良好的系统。一切都设定得如此,以便数据团队可以专注于生成高质量的洞见,数据使用的门槛也被降低,使得有兴趣的个人可以开始提取洞见并改善他们的日常工作。这是一种乌托邦。

本文已转载至 Analytics Explained,这是一个我总结了在各种分析角色中所学的知识(从新加坡的初创公司到旧金山的大型科技公司),并回答读者关于分析、增长和职业的问题的通讯。

¹: 分析成熟度模型概述 由 Karol Król 和 Dariusz Zdonek 撰写。

利用维度建模构建更好的数据仓库:数据工程师指南

原文:towardsdatascience.com/building-better-data-warehouses-with-dimensional-modeling-a-guide-for-data-engineers-422b3cd52df4

数据仓库维度建模设计 101

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Chengzhi Zhao

·发表于Towards Data Science ·阅读时间 9 分钟·2023 年 5 月 8 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由Erin Doering提供,来源于Unsplash

关于数据密集型应用的系统设计,通常有两个选项:优化写入或优化读取。

没有一种数据库设计既适合于写入也优化读取。就像所有系统设计视角一样,没有绝对对错的解决方案,只有优缺点。作为从事数据模型设计的数据专业人士,角色中的一个关键部分是识别用例,并进一步确定应应用哪种设计原则。

数据仓库历史上一直作为向最终用户提供数据的层,它是将数据转化为洞察的最后一公里。Ralph Kimball 开发了一种著名的建模设计技术,称为维度建模。他的《数据仓库工具包:维度建模的权威指南,第 3 版》是维度建模的最关键书籍。

尽管大数据和云计算技术解放了我们,使用更多的计算能力和更便宜的存储,但新手或经验丰富的数据工程师却忽视了数据仓库建模设计。越来越少的人关注慢变化维度(SCD)、代理键、表粒度等概念。

数据模型设计对于建立任何数据仓库系统的基础至关重要。我希望引起社区对以下内容的关注——《通过维度建模构建更好的数据仓库:数据工程师指南》。

为什么你应该关心 OLTP 与 OLAP

如果你对数据库有所了解,两个缩写词应该不会陌生:OLTP 和 OLAP。

OLTP 代表在线事务处理,以规范化的设计理念而闻名。在早期,存储是昂贵的。规范化的核心思想是减少重复并提高写入效率。

例如,我们可以有一个二维电子表格表。我们有产品及其子类型。爱丽丝和约翰都以 $1.99 购买了 Honeycrisp 苹果。对于不同的客户,相同属性的相同苹果重复出现——我们有重复。

当我们需要执行更新/插入/删除操作时,情况会变得更糟。例如,我们需要添加苹果来源于哪个农场,因此我们必须返回到这个表中并重复录入数千次。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

OLTP 规范化以去除重复 | 图片来源:作者

为了解决这种重复并提高效率,我们可以通过规范化来实现——创建一个单独的产品表,并将所有相关字段移动到该表中。在原始表中,仅保留一个标识符以引用新创建的表中的行。

这节省了存储空间,并减少了在需要执行更新/插入/删除操作时接触的行数。

那么 OLTP 方面的难点是什么?它不适用于分析模式查询。

分析模式查询专注于使用选择/过滤/聚合在读取数据时的查询效率。 对这种查询模式缺乏支持导致了 OLAP。

OLAP 代表在线分析处理,以非规范化的设计理念而闻名。非规范化的核心思想是减少复杂的业务逻辑和大型表连接(后面我们称之为事实到事实表)在运行时的需求。

数据的准备和编写通常发生在 ETL 过程的前期,这通常需要更长的时间来执行和将业务逻辑预计算到最终表中。

回到我们的苹果数据示例,我们之前共享的电子表格是分析设计的一个优秀例子。我们避免了电子表格之间的联接,并且可以直接进行选择/过滤。

大多数时候,数据工程的目标是为终端用户提供 OLAP 数据仓库。与尽量避免重复的 OLTP 不同,OLAP 的设计原则是——冗余(反规范化)并不坏。我们牺牲了存储成本,但在运行时获得了更快的查询速度。

成功维度模型设计的 4 个步骤

数据仓库工具包:维度建模的权威指南,第 3 版 是维度建模最关键的书籍。该书涵盖了成功构建数据仓库的四个关键步骤:

1. 选择业务流程

在我的数据工程职业生涯中,我学到的最重要的事情就是找到并坚持业务用例。你可以拥有所有高端工具、毫秒级延迟的实时处理和强大的分布式计算查询引擎。但没有用例就像空中楼阁。

无论你的设计多么周到,如果你也是终端用户(通常不太可能对于数据工程来说),咨询终端用户始终是明智的第一步。我们可以采访终端用户,了解他们如何利用领域知识解析和理解数据,他们当前的报告是如何生成的,以及了解他们在过程中遇到的痛点。

Kimball 的企业数据仓库总线架构也是一种优秀的技术,用于桥接业务使用流程和标准维度。目标是评估在事实表中共享的大小。利用总线矩阵技术可以简化后续阶段的维度设计决策,并帮助你可视化整体结构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据仓库总线矩阵 | 图片由作者提供

2. 声明粒度

粒度是什么?它指的是表中保留的信息的最低级别。

我们如何知道需要什么粒度?我们需要与用户咨询以做出这样的决定。通常,提供的信息越详细,表格中的灵活性就越高。仅有聚合数据是不可能获得详细的行级数据的。例如,我们可以从每日聚合中获得每周销售数据,但很难将每周销售数据轻松拆解为每日数据。

聚合视图在查询时更快,但需要更多细节。另一方面,更低的粒度可以在后期聚合以提供相同的见解,但在运行时可能需要额外的时间。与终端用户协商并帮助他们理解利弊和潜在的未来需求是至关重要的。

我建议将粒度设定得尽可能低,因为更详细的数据可以在后续聚合,但反之则不可。如果需要更高粒度的事实表,可以使用现有的事实表来构建。

3. 确定维度

一旦确定了粒度,接下来应该确定维度。维度是事实表的基石。

维度用于使事实表对业务更具意义。通常,维度是名词,如日期、类别、产品等。它们的作用是切片和切块。最终用户可以使用维度来关注特定数据区域或汇总多个字段以观察潜在模式。例如,在我们之前的例子中,前 3 种苹果是什么?

维度表通常包含的数据比事实表少得多。如果你熟悉分布式计算或曾在 Spark 物理计划中工作过,那么维度表通常适合广播连接,这意味着这个数据集足够小,可以发送到每个节点以提高连接效率。

缓慢变化维度(SCD)是核心维度设计概念。虽然有超过六种 SCD 类型,但最关键的是 SCD 类型 2,因为它在行业中的广泛应用。

如果维度值中的一行已被修改,如何在你的维度表设计中跟踪这种变化?

这就是 SCD 类型 2 的魅力。我们会添加另一行,使用相同的业务键。新添加的行包括未更改的字段和修改过的字段。为了知道哪一行是当前的,哪一行是过去的,我们会为特定行的有效期添加开始和结束日期时间。例如,如果供应商从 CA 迁移到 IL,我们仍然可以在缓慢变化维度中追踪。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

缓慢变化维度(SCD)类型 2 | 作者图片

Suppier_Code — ABC 通常是从 OLTP 系统提供的业务键。为了保持一个主键,以便以后与事实表连接,我们需要创建一个代理键 — Suppkier_Key——在这个维度中是唯一的。

4. 确定事实

事实表是维度建模的“灵魂”。事实表保留了核心业务数据。大多数事实表的行仅为数字,例如总销售量、交易费用和利润。

事实表有多种类型。你可以构建基于事务的事实表或快照事实表,如每月销售数据。

事实表的核心思想是跟踪给定时期的数据变化。例如,假设 Alice 购买了 Honeycrisp 苹果。然后第二天,她因为发现苹果里有虫子而退货。

  • 如果我们采用事务基础视图,我们将记录两行。第一行是$2.99 的利润,第二行是-$2.99(假设没有额外操作成本),因为她退货了。

  • 如果我们对每月的基本视图进行快照,Alice 的购买不会影响业务,因为 Alice 的净利润为 0。

事实表是金字塔的顶端。它需要所有基础工作准备好。因此,在开始设计事实表之前,拥有一个坚实的基础是至关重要的。

我应该选择星型模式还是雪花模式?

星型模式雪花模式的选择对于熟悉 OLTP 设计并试图进行一定程度规范化的数据仓库来说可能更直接。

在大多数情况下,星型模式应该是首选。原因有几个:

  • 我们希望避免额外的联接,这会降低查询性能。

  • 维度之间的关系越多,管理和维护数据完整性就越复杂。

  • 最终用户通常对数据模式不够熟悉,数据仓库的设计应该简单且用户友好。

  • 存储额外数据的额外成本是微乎其微的。

我们考虑雪花模式的一个例外情况是为了节省成本,或者如果维度本身频繁更新,需要添加规范化以减少需要处理的字段数量。然而,首要目标是减少联接,以减少最终用户的查询时间。

为什么不将所有内容保存在单个表中?

另一种选择是将所有字段存储在一个表中,而不是分开存储事实表和维度表。

我们可以选择类似电子表格的单表,并且通过像Parquet这样的列式存储,查询可能变得更快。

需要考虑的关键标准是——维度表也会在多个事实表中共享。这样可以更容易地管理,因为维度在多个地方被引用。

关于联接呢?维度表和事实表的联接是否缓慢?我们之前提到过“维度通常比事实表包含的数据少得多。”对于分布式计算,最有效的方法是将较小的数据集发送到每个节点,以避免大量数据的传输。在这种情况下,事实表的联接通常是最小的,由工程师评估查询性能和数据管理工作之间的权衡。

最后的思考

维度建模及其原则在行业中已经存在超过 40 年。它已被证明是一种典型的 OLAP 用例设计模式。尽管大数据时代带来了比以往更强大的分布式计算引擎,但 OLAP 的基本设计原则不应被忽视。正确设计数据项目始终至关重要,而不是盲目投入资源。

我希望这篇文章能够引起更多数据工程师的关注,促使他们在追逐新工具之前重新考虑数据仓库设计。

Building Better ML Systems — Chapter 1: Every Project Must Start with a Plan

原文:towardsdatascience.com/building-better-ml-systems-chapter-1-every-project-must-start-with-a-plan-907a36774a32?source=collection_archive---------1-----------------------#2023-04-20

关于机器学习项目生命周期、设计文档、商业价值和需求。关于从小处开始和快速失败。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Olga Chernytska

·

关注 发布于 Towards Data Science ·9 min read·Apr 20, 2023

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 charlesdeluvio 提供,来源于 Unsplash

很多数据科学家和机器学习工程师在大学毕业后,对他们的日常工作有一种错误的认知——他们期望其工作与他们的学习相似:

尝试最新的前沿算法在固定的相对干净的数据集上,并选择在准确性方面表现最好的算法(期望)。

你不需要:

  • 思考商业价值和永无止境的需求列表。

  • (最有可能) 收集、标记和清理数据集。在某些情况下,训练/验证/测试划分已经为你完成了。

  • 彻底评估你的模型,检查偏见,并进行 A/B 测试。

  • 将模型部署到成千上万(或百万)用户,并确保它 99.9%的时间保持运行。

  • 监控模型,捕捉任何准确率下降,并在需要时重新训练模型。

  • 在部署上一个版本后立即收集新数据,并开始着手开发一个新的、更好的模型。

是的,你不需要在研究/学习项目期间考虑这些。但在实际项目中,这变得至关重要。

研究和实际项目之间的主要区别是:

在现实生活中,许多用户以各种可想象和不可想象的方式使用你的模型,并期望它始终快速、准确且公平地工作,没有偏见。用户的行为不断变化,可能会发生疫情和战争,而你的公司则试图通过提供用户想要的东西来赚取利润,并通过以其他人从未尝试或成功过的方式应用机器学习来建立竞争优势(现实)。

在本系列中,你将了解到,构建更好的机器学习系统需要将其视为一个系统——对每个组件及其关系给予足够的关注。

本教程将对数据科学家、机器学习工程师、团队和技术负责人(或那些希望成为技术负责人的人)有所帮助。虽然本系列不会全面覆盖所有内容,但它将帮助你打下机器学习系统设计的坚实基础,弥补任何不足,并让你探索不太熟悉的话题。在过程中,我会提供许多优秀文章、论文和书籍的链接。

事不宜迟,让我们开始吧!

每个项目都必须以计划开始。

下面是机器学习项目生命周期。让自己放松。首先,你需要理解任务并确定需要做什么。接着,你收集、标记和清理数据。然后,你进入建模阶段。之后,你评估模型并选择最佳模型。最后,你部署模型并监控其性能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1. 机器学习项目生命周期。图源作者。

这就是结局吗?不,这仅仅是开始。

在监控过程中,你可能会发现模型在某些用户子集上的表现不好,或者准确率随着时间的推移而下降,因此你重新开始:理解问题 -> 获取数据 -> 建模和评估 -> 部署。

或者在模型评估过程中,你可能会发现模型还不够好,无法部署,因此你将重新开始:了解哪些地方不工作以及如何改进 -> 收集更多数据 -> 进行更多建模 -> 评估(希望)这次获得更好的结果。

(如果这是你第一次了解机器学习项目生命周期,我建议你查看 Anton Morgunov 的文章:机器学习项目的生命周期:有哪些阶段?

因此,有两件重要的事情需要理解:

  1. 构建机器学习系统是一个迭代过程,直到模型从生产中移除为止,这一过程将永无止境。(无休止的工作)

  2. 图 1 提供了机器学习系统开发的简化版本,但实际上,你并不会顺利地从一个阶段过渡到另一个阶段。在每个阶段,可能会出现问题(通常会出现),这可能会让你退回一个或多个步骤,甚至让你重新开始。(欢迎来到现实世界)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2. 现实 机器学习项目的生命周期。图像由作者提供。

那些有工程背景的人可能会想:机器学习项目和传统软件开发有什么区别?测试、构建和发布在哪里?谢谢你的提问。

事实上,机器学习项目是软件工程项目的一个子集。所以你在软件工程中想到的所有最佳实践在机器学习项目中都非常受欢迎。话虽如此,让我向你介绍一个真正现实的软件项目生命周期,其中包含机器学习组件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3. 真实 现实 的带有机器学习组件的软件项目生命周期。图像由作者提供。

为了控制这种混乱,每个项目都必须从计划开始。

(阅读 MLOps: Machine Learning Life Cycle 由 Satish Chandra Gupta 撰写,以了解更多关于机器学习软件开发生命周期的内容。)

在花费数千美元进行数据标注和数周进行机器学习模型开发之前,你需要做四件事。我们称之为“编程前”阶段。因此,现在关闭你的 PyCharm 吧,你只需要一个 Google 文档、大脑和 Zoom。

1. 估算机器学习项目的商业价值

任何商业公司的目标都是赚更多的钱或提供更好的客户体验……为了赚更多的钱。牢记这个简单的原理,向你的老板、高层管理人员和利益相关者证明当前的机器学习项目是一个值得投资的项目。

理想情况下,您需要提供一些关于机器学习模型如何增加公司收入、用户参与度或减少请求处理时间等方面的大致数据。在这里可以发挥创造力,关闭完美主义,并且不要犹豫向财务和市场部门的同事寻求帮助。

(请记住,后续这些指标将用于评估项目,因此在承诺交付内容时务必要实际可行。)

2. 收集需求。

一旦没有人对机器学习模型的必要性表示怀疑,就开始收集需求。

每个领域都是特定的,每个项目都是独特的,因此没有一份详尽的需求清单可供参考。因此,请相信您的经验,并与同事合作。

这里有一个有用的提示:列出一些通用问题(我将在下面分享我的),然后直接提问。开始对话,随着讨论的进行,更多与项目相关的问题自然会浮现。

  • 我们有多少数据?我们将如何标记它?

  • 模型的延迟应该是多少?

  • 模型将部署在云端还是本地?实例规格是什么?

  • 是否有关于数据隐私和模型可解释性的要求?

如果一个任务可以通过机器学习解决,这并不意味着一定要这样做。 在这一点上,我建议您重新考虑是否采用纯软件工程方法或基本的规则驱动方法可能更适合。以下是可以帮助您做出决策的一些文章:

没有数据就没有机器学习。这似乎是显而易见的,但不幸的是,在我的职业生涯中,我见过太多公司犯同样的错误:他们想要人工智能,但他们的数据集很小,缺少重要的特征或者数据不干净。Monica Rogati 在文章“AI 需求层次”中提到,AI 应被视为需求金字塔的顶端,而数据收集、存储和清理则是其基础。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4. AI 需求层次。修改自 Monica Rogati 在“AI 需求层次”中的图像

3. 从小开始,快速失败。

即使您的目标是创建一个每天为数百万用户提供服务的机器学习系统,从一个小得多的项目开始也是明智的选择:

  • PoC(概念验证)。从数据存储中手动检索数据,在 Jupyter Notebook 中快速迭代几种算法,最后,证明(或拒绝)假设:您可以在您拥有的数据上训练一个具有令人满意准确度的机器学习模型。在 PoC 阶段,您还将了解到部署和扩展模型所需的内容。

  • MVP(最小可行产品)。假设 PoC 阶段成功,现在你正在创建一个仅包含主要功能并发布给用户的产品。在机器学习项目中,这意味着将模型推出给一部分用户并评估它是否带来了预期的商业价值

一旦你意识到一个想法行不通——可以心无旁骛地放弃它并转向下一个。这在你还没有花费多年工作或数十万美元时要容易得多。将失败的成本保持在较低水平是项目成功的关键因素。

(要深入了解这个话题,请阅读 POC 与 MVP:选择构建优秀产品 作者:Dmitry Chekalin。)

4. 编写设计文档。

软件工程中的设计文档是对软件系统架构的描述——其整体结构、各个组件及其相互之间的互动。它可以采取任意形式和结构,可以是正式的或非正式的,高级别的或详细的(这由团队决定)。在软件开发的实施阶段,设计文档作为开发人员遵循的蓝图。

这是软件工程中的最佳实践,正如我之前提到的,所有的软件工程最佳实践在 ML 项目中都受到高度欢迎。

我个人喜爱设计文档的原因如下:

  • 编写触发思考过程。 编写设计文档就像在高层次上实现项目——你实际上不编写代码,但仍然对数据、算法和基础设施做出决策。你考虑所有场景并评估权衡,这意味着你将通过避免死胡同在未来节省时间和金钱。

  • 设计文档简化了团队内部的同步和协作。 文档在团队成员之间共享,以便他们可以审阅,熟悉系统设计,并在需要时发起讨论。没有人被忽视,每个人都被鼓励参与。

如果你准备开始编写设计文档,以下是 Eugene Yan 提出的机器学习系统模板。可以随意修改并根据项目需求进行调整。

如果你想了解更多关于设计文档的概念,可以查看这些文章:

结论

在这一章中,我们了解到每个项目都必须以计划开始,因为机器学习系统过于复杂,无法以临时的方式实施。我们回顾了机器学习项目的生命周期,讨论了为什么以及如何估算项目的商业价值,如何收集需求,然后冷静地重新评估是否真的需要机器学习。我们学习了如何通过“PoC”和“MVP”等概念从小处开始并快速失败。最后,我们谈到了在规划阶段设计文档的重要性。

在接下来的文章中,你将了解数据收集和标注、模型开发、实验跟踪、在线和离线评估、部署、监控、再训练以及更多内容——所有这些将帮助你构建更好的机器学习系统。

下一章已经发布:

## 构建更好的机器学习系统。第二章:驯服数据混乱

关于数据中心 AI、训练数据、数据标注和清洗、合成数据以及一点数据工程和…

towardsdatascience.com

《构建更好的机器学习系统》—— 第二章:驯服数据混乱

原文:towardsdatascience.com/building-better-ml-systems-chapter-2-taming-data-chaos-841d5a04b39?source=collection_archive---------8-----------------------#2023-05-24

关于数据中心的人工智能、训练数据、数据标注和清洗、合成数据,以及一些数据工程和 ETL。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Olga Chernytska

·

关注 发表在 Towards Data Science ·12 分钟阅读·2023 年 5 月 24 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 charlesdeluvio 拍摄,来自 Unsplash

构建机器学习系统远不仅仅是迭代酷炫的最先进算法。

研究或学习项目以演示结束。在商业项目中,模型会发布给成千上万,甚至是百万用户,他们以各种可想象和不可想象的方式使用你的模型,并期望其始终快速、准确、公正地工作。一旦预测错误,可能会导致人员伤亡、数百万美元的损失,或严重损害公司的声誉。

在这个系列中,我们讨论了构建良好的机器学习系统所需解决的重要主题:商业价值和需求、数据收集和标注、模型开发、实验跟踪、在线和离线评估、部署、监控、再训练以及更多。

在上一章中,我们了解到每个项目都必须从计划开始,因为机器学习系统过于复杂,不能随意实现。我们回顾了机器学习项目的生命周期,讨论了为什么以及如何估计项目的商业价值,如何收集需求,然后冷静地重新评估是否真的需要机器学习。我们学习了如何通过“PoC”和“MVP”等概念从小处着手,快速失败。最后,我们谈到了规划阶段设计文档的重要性。

本章完全关于数据。我们将深入探讨机器学习系统中数据的各个方面——数据中心 AI、训练数据、数据标注和清洗、合成数据,以及一些数据工程和 ETL。本帖是系列中最长的一篇,但原因正当:数据科学家的大部分工作时间都投入在数据上。

所以让故事开始吧。

数据中心 AI

提高模型准确性的两种方法是:

  1. 收集更多数据或清理现有数据,同时保持模型不变。

  2. 使用更高级的算法或调整当前模型的超参数,同时保持数据集不变。

第一种方法被称为数据中心,第二种方法是模型中心。现在机器学习社区倾向于数据中心 AI;许多研究人员和从业者已经得出结论,改进数据比改进算法更能显著提高模型的准确性。你听过无数次的“垃圾进,垃圾出”这一说法正在重新焕发光彩。

这是 DeepLearning.AI 和 Landing AI 创始人 Andrew Ng 的观点:

“与其专注于代码,公司应该专注于开发系统化的工程实践,以可靠、高效、系统的方式改进数据。换句话说,公司需要从以模型为中心的方法转向以数据为中心的方法。”

建立优秀 AI 产品的公司也采用数据中心方法。前特斯拉 AI 总监安德烈·卡帕西(Andrey Karpathy)分享了他在特斯拉的大部分时间都投入在数据上。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片。优秀的 AI 公司更关注数据而非算法。

来源:“构建软件 2.0 堆栈”作者:Andrej Karpathy*。

数据驱动的 AI 已经变得如此流行,以至于它最近演变成了一个研究改进数据集技术的独立学科。为了与 ML 社区保持一致,我强烈建议你参加 MIT 提供的这个优秀的免费课程:数据驱动 AI 介绍

数据管道

一切都是数据。系统生成的日志、银行交易、网站数据、用户输入数据和客户数据只是你们业务可能涉及的一些例子。

到达的数据通常是混乱的、无结构的和脏的。它来自多个数据源,这可能会很棘手;有时它是加密的,或者可能缺少某些片段。数据可以是字节流、文本文件、表格、图像、语音和视频录音的形式;它可以是二进制的或人类可读的。

在数据科学家和机器学习工程师可以利用这些数据之前,数据需要经过处理、转换、清理、聚合和存储。

数据管道是一种组织数据流的方法。

ETL(提取-转换-加载)是一个广泛用于数据分析和机器学习的数据管道示例。在 ETL 中,数据以以下方式组织:

  • 首先,你需要确定要收集哪些数据以及从哪些来源收集。

  • 接下来,你合并这些数据源,将数据转换为所需格式,解决不一致性,并修复错误。

  • 之后,你设计数据存储,并将处理和清理后的数据存储在那里。

  • 最后,你自动化整个过程,使其无需人工干预即可运行。数据管道应周期性地或在特定事件发生时自动触发。

要深入了解 ETL,请查看 NIX United 的文章 什么是 ETL 过程:概述、工具和最佳实践

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图像。ETL 管道。 图像由 NIX United 提供

这是一种高层次的数据管道概述。这个话题要广泛得多,更加复杂,因此越来越多的公司正在雇用数据工程师来处理数据存储和管道,同时让数据科学家和机器学习工程师专注于数据分析和建模。

如果你对数据工程师的技能集感到好奇,可以阅读 现代数据工程师路线图 由 datastack.tv 提供。我很高兴看到这个领域内专业角色的兴起,也很高兴数据科学家不再需要了解所有的东西了。这真是太让人松了一口气!

在我们深入讨论训练数据和标签之前,还有一件重要的事情:

如果数据管道设置得当,即使没有先进的机器学习,你的公司也能从数据中受益。 所以在采用机器学习之前,公司通常会从报告、指标和基础分析开始。

训练数据

为了训练“猫与狗”分类器,你需要向模型展示大量的猫图片,同时说“这是一只猫”,并展示大量的狗图片,同时说“这是一只狗”。不提供任何规则或解释,让模型决定如何进行预测。从数学上讲,这意味着模型调整其参数,直到输入与训练数据的预期输出匹配。

模型基于训练数据建立对世界的理解,假设训练数据代表了现实世界,并且正确地代表了它。 这就是为什么训练数据的质量非常重要。

  • “猫与狗”模型无法预测品种或分类其他动物,因为这些信息在训练集中不存在。

  • 如果标签中存在错误,某些猫被标记为狗,反之亦然,模型会感到困惑,无法达到高准确率。非随机错误对模型可能极具破坏性。例如,如果所有吉娃娃都被标记为猫,模型将学习将吉娃娃预测为猫。

  • 现实世界的数据包含偏见。例如,女性薪资更低。所以,如果你训练一个模型来预测公司员工的薪资,模型可能会预测女性的薪资较低,因为这正是数据中所体现的,并且模型假设应该是这样。

  • 如果某些类别或分段在训练数据中表现不足或缺失,模型将无法很好地学习这些类别,并会产生不正确的预测。

训练数据应该是相关的、一致的、具有代表性的和全面的。这些术语的含义在 Amal Joby 的文章什么是训练数据?它在机器学习中的作用中解释得很好。

在我们都一致同意在高质量数据上训练模型至关重要之后,让我分享一些实用的技巧。

在收集训练数据之前,了解业务任务,然后将其框定为机器学习问题:应预测什么,以及从什么输入中预测。几乎任何业务任务都可以根据要求和限制以不同方式表示。在进行计算机视觉项目时,我通常在目标检测、分割和分类之间进行选择,并决定类别数量。

训练数据必须与模型在生产中‘看到’的数据非常相似。 从理论上讲,模型可以推广到未见过的数据,但在实践中,这种推广能力是相当有限的。例如,如果你为室内环境训练一个计算机视觉模型,它在户外效果会很差。类似地,在推特上训练的情感模型对于分析经典文学文本片段也不会有效。我个人经历过计算机视觉模型在面对较小的差异时,如光照、肤色、天气条件和压缩方法的微小变化,难以推广的情况。为了克服训练数据和生产数据之间的差异,一种流行的方法是使用生产中的最新数据作为训练数据集。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图像。训练数据和生产(测试)数据之间的不匹配示例。来源: Google Research Blog

一个小而干净的数据集比一个大但脏的数据集更好。 对于大多数项目而言,数据标注是一个瓶颈。数据标注是一个极其复杂、缓慢且昂贵的过程(下一部分将专门讨论)。拥有一个巨大的干净数据集是只有庞大的科技公司才能负担得起的奢侈品。其他所有公司都必须在规模和质量之间做出选择,你应该始终选择质量,特别是对于用于评估模型的数据集。

没有人能真正确定需要多少数据。 这取决于预测的现实世界现象的复杂性、训练数据的变异性以及所需的模型准确性。找到这个答案的唯一方法是通过反复试验。因此……

分块获取数据。 从一个小数据集开始,标注它,训练一个模型,检查准确性,分析错误,并计划下一轮的数据收集和标注。

训练数据不是静态的。 正如你从前一章中回忆的那样,你将在研究阶段和模型已投入生产时多次训练和重新训练模型。每次新的迭代和模型更新时,都需要一个新的训练数据集。没有休息的机会,记住了吗? 😃

数据标注

目前,大多数生产中的机器学习模型是监督学习。这意味着需要标注数据来训练和评估模型。即使在无监督学习的情况下,模型从未标注的数据中学习模式和结构,仍然需要标注数据来评估模型的准确性;否则,你怎么知道它是否足够好以用于生产?

标签有两种类型:人工标签和自然标签。

一些机器学习任务涉及预测未来。例如,预测股票价格、客户流失、到达时间、欺诈交易和推荐。一旦未来到来,我们就知道真实的标签。这些标签被称为自然标签,我们只需在它们到来时进行收集。

在计算机视觉和自然语言处理(NLP)中,我们不是预测未来,而是对图像和文本进行分类、分析和检索信息。这就是为什么我们无法获得自然标签,必须严重依赖人工标签。

人工数据标注是一个极其复杂、缓慢且昂贵的过程。 不要把它当作机器学习项目中的一个任务来考虑;最好将其视为一个独立的数据注释项目,具有自己的范围、预算、时间线、团队、工具和关键绩效指标(KPI)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图像。数据注释项目的阶段。来源: 管理数据注释项目的最佳实践

如果你与数据注释紧密合作,我推荐你查看 Tina Tseng 等人关于数据注释项目管理最佳实践的 30 页报告。对于较短版本和我自己的见解,请继续阅读这篇文章。

首先要决定的是:谁来标注数据? 需要考虑三个选项:众包、供应商和内部标注团队。我记得大约五年前,围绕像亚马逊机械土耳其这样的众包工具的兴奋。然而,很快发现众包标注只适用于需要最少或不需要员工培训的非常简单的任务。因此,大多数公司在供应商和内部标注团队之间进行选择。初创公司通常倾向于选择供应商,因为这提供了一个更简单的起点,而大型 AI 公司则建立自己的标注团队,以控制过程并实现更高的标注质量。举例来说,特斯拉有 1000 名全职员工在其人工数据标注团队中。仅仅是个例子。

创建指导方针并根据它们培训标注者。 指导方针是提供应标注内容及其方式的解释和视觉示例的文件。然后,将指导方针转化为标注者在进行实际标注任务之前必须完成的培训材料。如果你与供应商合作,请确保他们的员工培训过程设置得当。

现实世界的数据是模糊和混乱的,因此允许标注者说:“我不知道如何标注这个样本。” 然后,收集这些混淆样本,并用它们来改进指导方针。

标注工具很重要。标注员通常按小时计费,因此帮助他们更快、更准确地标注会节省你很多钱。在大规模上,标注员每小时标注 100 个样本与 300 个样本的差别尤其明显。所以明智选择,并关注以下几点:

  • 标注单个样本所需的时间。一些工具专门为 NLP 任务开发;完全不同的工具用于 2D 或 3D 计算机视觉。

  • 是否支持 AI 驱动的标注。这是你想要使用的功能。该工具可能通过用户点击对象来预测分割掩码,或者允许你部署自己的模型来协助标注过程。

  • 它与您的基础设施的契合程度。标注工具将集成到数据管道中。一旦数据到达,它会自动采样并发送给标注员。他们标注数据,标签会自动存储在数据库中。一些工具可能比其他工具更适合你的基础设施,考虑一下这一点。

开源标注工具列表在这里这里有一个不错的比较介绍了一些免费的和付费的工具。

估算成本和时间线。 你会惊讶于数据标注的缓慢和昂贵(我就是)。因此,最好做好准备(并提前准备你的经理)。

这里是大致估算成本和时间的公式:

  1. 标注时间(工时)= 标注一个样本的时间(小时) * 数据集大小(样本数量) + 预留的训练和错误修正时间(工时)

  2. 标注时间(工作天数)= 标注时间(工时) / 员工人数 / 8 小时

  3. 成本( ) = 标注员的小时费率( )= 标注员的小时费率( =标注员的小时费率() * 标注时间(工时)

无论你多么努力,数据标签不可避免地会出现错误。 人类会犯错,他们可能会分心或误解任务。因此,检查标签的质量是必需的。当然,你为此选择的算法或工具也必须集成到数据管道中。我会一再强调:一切必须自动化。

一个这样的工具是Cleanlab。它由麻省理工学院毕业生开发,最近获得了很大的人气。Cleanlab 使用统计方法和机器学习算法改进图像、文本和表格数据的标签(有关它能做什么的示例,请查看Cleanlab 博客)。

关于数据标注的最后一点,我推荐这篇 Synced 的深刻文章——数据标注:AI 突破背后的十亿美元生意。标题已经很自解释了,文章确实值得一读。

合成数据

将所有上述数据标注的挑战,添加数据隐私问题以及现实世界数据中的严重类别不平衡,你就会明白为什么合成数据变得越来越受欢迎。

合成数据通常是使用一些游戏引擎、生成对抗网络的组合,或许再加上一点魔法来生成的。在自动驾驶汽车行业中,合成数据已经变得至关重要。查看一下NVIDIATesla已经在做的事情。

一旦合成数据生成设置好,就可以相对快速且经济地获得大量多样化的数据集,并且具有极高的标签准确性。即使合成数据看起来不完美,它仍然可以用于模型的预训练。

如果你对扩展这一主题的知识感兴趣,这里有一个很好的资源:什么是合成数据? 由 NVIDIA 提供。

结论

在这一章中,我们讨论了行业中的新趋势——以数据为中心的人工智能,这是一种构建机器学习系统的方法,认为干净的数据比先进的机器学习算法更重要。我们涉及了数据管道,旨在组织混乱和非结构化的数据流,以便这些数据可以用于分析。我们了解到,训练数据应该是相关的、均匀的、具有代表性的和全面的,因为模型是基于这些数据建立对世界的理解的。我们回顾了两种类型的标签——人工标签和自然标签——并讨论了获取人工标签的复杂、缓慢且昂贵的过程,以及使这一过程不那么痛苦的最佳实践。最后,我们讨论了真实数据和人工标注的替代方案:合成数据。

在接下来的帖子中,你将学习到模型开发、实验跟踪、在线和离线评估、部署、监控、再训练等多个方面——这些都将帮助你构建更好的机器学习系统。

下一章已经可以阅读:

## 构建更好的机器学习系统 — 第三章:建模。让乐趣开始

关于基准、实验跟踪、适当的测试集和指标。关于让算法发挥作用。

towardsdatascience.com

构建更好的机器学习系统 — 第三章:建模。让乐趣开始

原文:towardsdatascience.com/building-better-ml-systems-chapter-3-modeling-let-the-fun-begin-73059c75e1d5?source=collection_archive---------7-----------------------#2023-08-25

关于基线、实验跟踪、适当的测试集和指标。关于让算法发挥作用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Olga Chernytska

·

关注 发布于 Towards Data Science ·15 min read·2023 年 8 月 25 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源

你好,很高兴再次见到你。我非常欣赏你想成为更好专业人士、做得更好以及构建更好机器学习系统的愿望。你很棒,继续保持!

在这个系列中,我尽力帮助你掌握设计和构建机器学习系统的艺术、科学和(有时的)魔法。在这里,我们讨论业务价值和需求、数据收集与标记、模型开发、实验跟踪、在线和离线评估、部署、监控、再训练,以及更多内容。

这是第三章,专注于模型开发。一个机器学习算法只是机器学习系统的一小部分。 没有精心设计的系统,即使是完美准确的算法也无法服务于客户,也不会为公司带来利润。在这篇文章中,我将从一个不同的角度展示:如何选择、开发和评估算法,同时考虑算法的主要目标是为业务带来价值。最终,无论你是用线性回归还是最先进的神经网络解决业务问题,都不重要。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个机器学习算法(中间的黑盒)只是机器学习系统的一小部分。 图片来源

在我们继续之前,让我们快速回顾一下我们已经学到的内容。

第一章 讲的是规划。我们了解到每个项目必须从计划开始,因为机器学习系统过于复杂,不能以临时的方式实施。我们回顾了机器学习项目生命周期,讨论了估算项目业务价值的原因和方法,如何收集需求,然后以冷静的头脑重新评估机器学习是否真的必要。我们学习了如何从小处着手,并利用“PoC”和“MVP”等概念快速失败。最后,我们谈到了在规划阶段设计文档的重要性。

第二章 讲的是数据。我们讨论了行业中的新趋势——数据中心人工智能,这是一种将干净的数据视为比先进的机器学习算法更重要的构建机器学习系统的方法。我们介绍了数据管道,这些管道旨在组织混乱和非结构化数据的流动,以便数据可以用于分析。我们了解到,训练数据应该是相关的、一致的、具有代表性的和全面的,因为模型基于这些数据建立对世界的理解。我们回顾了两种标签——人工标签和自然标签——并探讨了获取人工标签的复杂、缓慢和昂贵的过程,并讨论了使这一过程更少痛苦的最佳实践。最后,我们讨论了真实数据和人工标记的替代品:合成数据。

如果你不小心错过了之前的帖子,我建议你在继续之前阅读它们。我会在这里等你。

现在,让我们开始享受乐趣吧。

如何选择一个 ML 算法

没有一种算法适用于所有问题。你需要尝试几种方法,真正了解你的数据和领域,直到找到有效的方法。

思考、头脑风暴、与同事讨论、询问 ChatGPT,然后写下你打算尝试的三种方法:1) 一种非常简单的方法;2) 一种非常流行的方法;3) 一种新颖且有创意的方法。

  1. 一种非常简单的方法。算法中引入的每一种复杂性都必须是有理由的。从一个简单的方法开始(甚至可能是非 ML 的),评估它,并将其作为基线与其他所有模型进行比较。

  2. 一种非常流行的方法。如果你看到、听到或读到许多人使用特定算法解决相同的业务任务 — 确保将其添加到你的实验列表中。利用集体智慧!我对流行的方法总是充满期望,在大多数情况下,它们效果很好。

  3. 一种新颖且有创意的方法。尽管试试看。如果你通过击败典型的流行方法建立了竞争优势,你的老板和公司会很高兴。

提醒:不要重复发明轮子。有成百上千的开源库和代码库,已经实现了你可能想到的大多数算法、数据采样策略或训练循环。不要自己编写 K-means 聚类算法 — 使用scikit-learn中的现成实现。不要从头开始编写 ResNet50 — 使用PyTorch中的现成实现。在实现最新的论文之前,检查一下PapersWithCode,我敢打赌有人已经做过了。

做研究和发明新东西令人兴奋。从零开始实现算法,理解每一行代码,确实很有吸引力。然而,研究通常适合大学和大型科技公司。对于初创公司来说,每一美元都很重要,因此他们根本无法投资成功几率很低的东西(研究通常涉及 100 次尝试和 1 次成功)。

小心“最先进技术”。假设你正在使用YOLOv7进行目标检测,然后你听说YOLOv8已经发布,预计会更好。这是否意味着你需要升级所有的生产管道以支持 YOLOv8?不一定。

在大多数情况下,这种“更好”意味着在静态基准数据集上提升 1-2%,例如COCO。由于你的数据和业务问题在各个方面都不同,模型在你数据上的准确性可能更好、略好,甚至更差。此外,从本系列的第二章中,你应该记住:提升数据质量比改善算法能显著提高模型准确性。想办法清理训练数据——你会看到准确率提高 5-10%。

如何开发机器学习算法

首先,获取一个基准。基准是你将要与之竞争的模型。基准有两个逻辑选择:

  1. 从生产环境中获取一个现有的模型(如果你有的话)。我们想要改进现有的模型,这就是为什么我们需要与之进行比较。

  2. 一个非常简单且易于部署的模型。如果业务任务可以通过简单的方法解决,何必费劲训练复杂的模型?花几天时间寻找并实现一个简单的解决方案。

现在开始实验。你设计所有实验以在基准之上进行改进。找到一个有前途的算法?很好,评估并与基准进行比较。你的模型更好?恭喜你,它现在是你的新基准,考虑进行实验以进一步改进它。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

算法开发是一个迭代的过程。图像由作者提供

算法开发是一个迭代的过程。你要么找到一个足够好的算法投入生产,要么耗尽时间。两种情况都是可能的。

自然地,你尝试的大多数想法都会失败。所以不要为此沮丧,也不要把它当成个人问题。我们都是这样工作的:找到一个好点子,试一试,发现这个点子实际上很糟糕,然后想出一个新的、希望这次好的点子,再试一试,发现它也不起作用,再找一个新点子,……

我的建议是:给单一想法的努力设定时间框架。如果你不能在 N 天内(提前选择你的 N 值)使这个想法奏效,就结束它并转向另一个想法。如果你真的想要成功,你需要经历许多不同的想法,因为,正如我之前所说,大多数你尝试的想法都会失败。

真正真正地了解你的数据。 可视化样本和标签,绘制特征分布,确保你理解特征的含义,探索每个类别的样本,了解数据收集策略,阅读提供给标注员的数据标注说明……训练自己预测模型应该预测的内容。如果你想创建一个好的算法,开始像算法一样思考(我不是在开玩笑)。所有这些都将帮助你发现数据中的问题、调试模型,并提出实验想法。

将数据划分为训练集、验证集和测试集。在训练集上训练,在验证集上选择超参数,在测试集上评估。确保这些划分之间没有重叠或数据泄漏。有关更多信息,请查看这篇文章:《机器学习中的训练、验证和测试划分》 作者 Jacob Solawetz。

方法:使用开源模型,先用默认参数运行,然后进行超参数调整。使用来自机器学习库的算法,例如scikit-learnPyTorchOpenCV,或者来自 GitHub 上拥有大量 stars、良好 readme 和允许商业使用的许可证的仓库。用默认超参数在你的数据上训练并进行评估。算法的默认超参数是为了在基准数据集(ImageNet, COCO)上最大化准确性,因此在大多数情况下,它们不适合你的数据和任务。详细了解每个超参数的含义及其对训练/推理的影响,以便进行超参数优化。典型的超参数优化方法包括Grad Student Descent、随机/网格/贝叶斯搜索和进化算法。永远不要在进行超参数优化之前就声称算法不起作用。欲了解更多信息,请查看 Pier Paolo Ippolito 的这篇文章:《超参数优化》。

更加深入地处理你的数据:进行特征工程和数据增强。 特征工程指的是转换现有特征和创建新特征。特征工程是一项关键技能,因此我向你推荐两篇可以帮助你掌握这项技能的优秀文章:

  • 《机器学习特征工程的基本技术》 作者 Emre Rençberoğlu

  • 《高级特征工程和预处理的 4 个技巧》 作者 Maarten Grootendorst

数据增强是一种从现有数据中创建新训练样本的技术,使得模型在训练过程中“看到”更多样本。增加训练集是提高模型准确性的最简单方法,因此你应该在可能的情况下始终进行数据增强。例如,在计算机视觉领域,几乎没有人会在没有基本图像增强(如旋转、缩放、裁剪、翻转等)的情况下训练模型。有关更多细节,请查看我的文章:《计算机视觉数据增强完全指南》。

如果你对自然语言处理中的数据增强方法感到好奇,可以阅读 Shahul ES 的《NLP 中的数据增强:Kaggle 大师的最佳实践》

迁移学习是你的朋友。零样本学习是你最好的朋友。 迁移学习是一种流行的提高模型准确性的技术。实际上,这意味着你使用某个数据集上预训练的模型,继续用你的数据进行训练(“转移知识”)。即使你的数据与 COCO 或 ImageNet 数据集的图片看起来相差甚远,来自这些数据集的权重仍然可以改善你的模型。

零样本学习是一种无需训练即可在你的数据上工作的算法。怎么做到的?通常,它是一个在巨大样本数据集上预训练的模型。你的数据可能类似于这个模型已经训练过的内容;模型已经“见过”了大量样本,因此能够很好地泛化到新数据上。零样本学习听起来可能像是一个梦想,但确实有一些超级模型存在:Segment Anything、大多数词嵌入模型,ChatGPT。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为你的方便准备的模型开发清单。图片来源于作者

关于模型开发还有很多要说的,但我们需要总结,以留出时间讨论实验跟踪和评估的话题。如果你仍然渴望知识,可以查看 Andrej Karpathy 的这篇精彩文章:训练神经网络的配方

实验跟踪

实验跟踪是将实验信息保存到某个仪表板或文件中的过程,以便你将来可以查看。这就像软件开发中的日志记录。训练和测试数据集的链接、超参数、git 哈希、测试数据上的指标——这些都是你可能跟踪的内容示例。

你必须跟踪你运行的所有实验。如果出于某种原因你的团队没有这样做,立即安排一个团队会议讨论这件事的重要性。你会感谢我的 😃

那么,我们为什么要进行实验跟踪呢?

  • 为了比较不同实验之间的差异。当你开发模型时,你会训练和评估许多不同的算法,尝试不同的数据预处理技术,使用不同的超参数,采用各种创造性的技巧。最终,你希望看到你尝试了什么,哪些有效,哪些获得了最佳准确率。也许以后你会想回到某个实验,重新审视其结果。模型开发可能会持续几周甚至几个月,因此如果没有适当的实验跟踪,你会忘记你做了什么,并且不得不重新做实验。

  • 为了重现实验。如果你不能重现它,那就不算数。检查一下:你能回到你最成功的实验,重新运行它并获得相同的准确率吗?如果答案是“不能”,可能是因为你没有对代码和数据进行版本控制,没有保存所有超参数,或者没有设置随机种子。

    随机种子的设置重要性在 Cecelia Shao 的文章中解释得很好:在 ML 实验中正确设置随机种子。并不像你想象的那么简单

  • 调试实验。有时实验无法正常工作:算法不收敛,预测结果异常,准确率接近随机。如果没有保存实验信息,几乎不可能理解失败的原因。保存超参数列表、样本和数据增强的可视化、损失图等可能会给你一些线索,帮助你找到问题所在。

既然你已经相信实验追踪的重要性,我们来谈谈如何实际操作。

有很多免费的和付费的实验追踪工具,你可以选择适合你要求和预算的工具。可能最受欢迎的是Weights&Biases;我用过很多次,它很好。有关其他工具的评论,请查看15 个最佳 ML 实验追踪和管理工具由 Patrycja Jenkner 撰写。

机器学习实验由数据、代码和超参数组成。确保你使用版本控制工具管理代码,如 Github 或 Gitlab,并在开发过程中提交所有更改。能够恢复到旧的代码版本以重新运行旧的实验是很重要的。对数据进行版本控制。最简单和最流行的方法是为每个数据集的新版本创建一个新文件夹或文件(最好是在云存储上,例如Amazon S3Google Cloud Storage)。有些人使用一个叫做Data Version Control (DVC)的工具。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ML 实验由数据、代码和超参数组成。图片由作者提供

你究竟应该追踪什么呢?嗯,追踪你能追踪的所有内容并非坏主意 😃 大多数时候,除非实验失败且失败非常严重,否则你不会用到所有这些信息。

以下是你可能需要考虑追踪的事项列表:

  • 提交的 Git 哈希值

  • 训练、验证和测试数据集的链接

  • 超参数及其随时间的变化(模型结构、学习率、批量大小、数据增强等)

  • 训练和验证集上的损失图

  • 训练和验证集上的指标图

  • 测试集上的指标

  • 带标签的训练样本可视化(包括和不包括应用的数据增强)

  • 测试集上的错误可视化

  • 环境(操作系统、CUDA 版本、软件包版本、环境变量)

  • 训练速度、内存使用、CPU/GPU 利用率

一次性设置实验跟踪,享受其永久的好处。

模型评估

在将模型部署到生产环境之前,必须彻底评估模型。这种评估称为“离线”评估。相比之下,“在线”评估则是检查已经在生产环境中运行的模型。在线评估将在本系列的下一章中讨论,今天我们只关注离线评估。

要进行离线评估,我们需要一个指标和一个数据集。

模型在测试数据集上进行评估,这是你在训练和调整超参数时留出的数据集。假设 1)测试集足够大且极其干净;2)模型从未见过测试数据;3)测试数据代表生产数据。如果其中一个假设被违反,评估就会不正确,存在获得过于乐观的指标并部署差模型的高风险。

在小规模测试集上进行评估可能会偶然得到一个好的指标。对脏数据的评估不会展示模型的真实表现。虽然训练集中出现错误更具包容性(你可以在干净标签、脏标签甚至无标签上进行训练),但测试集中出现错误可能是有害的。重要提示:无监督模型也需要标记的测试集。否则,你如何知道你的模型是否足够好?

确保你的模型没有“见过”测试数据。始终过滤重复项,以避免相同样本出现在训练集和测试集中。不要随机拆分数据,改用基于时间或用户的拆分。基于时间的拆分意味着将较旧的数据放入训练集,将较新的数据放入测试集。基于用户的拆分意味着将同一用户的所有数据放在同一拆分中。要非常小心数据泄漏,更多详细信息请参阅 Prerna Singh 的 《机器学习中的数据泄漏:如何检测并减少风险》。

指标是一个假定与模型真实表现相关的数字:数字越高,模型越好。你可以选择一个或几个指标。例如,分类任务的典型指标有准确率、精确率、召回率和 F1 分数。选择一些简单且理想情况下可解释的指标,以便非技术经理和客户也能理解。

以下是 Shervin Minaee 关于各种任务和领域指标的优秀文章:

  • 20 个流行的机器学习指标。第一部分:分类和回归评估指标

  • 20 个热门机器学习指标。第二部分:排序和统计指标

使用基于切片的指标并评估你能想到的每个数据片段(除非你想陷入像“Zoom 的虚拟背景功能不适用于黑人面孔”这样的丑闻)。例如,面部检测系统必须分别评估不同种族、性别和年龄的人群。电子商务模型值得评估桌面与移动端、不同国家和浏览器的表现。仔细检查每个片段是否在测试集中得到充分代表。基于切片的指标也有助于解决类别不平衡问题:分别查看每个类别的精确度和召回率比总的精确度/召回率更有意义。

避免丑闻的另一种方法(这次是“银行 ABC 的新信用评分系统歧视未婚女性”)是使用行为测试。一篇很棒的论文,超越准确性:使用 CheckList 对 NLP 模型进行行为测试,建议除了数值指标外,还使用最低功能性、不变性和方向期望测试。尽管这篇论文专注于自然语言处理,这些测试类型也可以很容易地应用于表格数据和图像。

在“银行 ABC 的新信用评分系统歧视未婚女性”的例子中,不变性行为测试可能会有很大帮助。保持所有特征不变,但改变婚姻状况和性别,并检查模型预测是否发生变化。如果你看到预测有显著差异(而应当是“不变”的),可能说明你的模型在训练数据中吸收了偏见;这需要修正,例如,通过完全移除模型输入中的敏感(易引起歧视的)特征。

最后,可视化错误。找到测试集中模型出错的样本;可视化这些样本并分析为何会发生这些错误。这是因为测试集仍然很脏吗?训练集中是否有足够相似的样本?模型错误是否存在某种模式?这种分析有助于发现测试集中可能的标注错误和训练过程中的漏洞,并提出进一步提高模型性能的想法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

方便使用的模型评估检查清单。图片由作者提供

结论

在本章中,我们学会了如何在开发模型时考虑到,机器学习算法只是机器学习系统的一部分。模型开发从创建一个简单的基准模型开始,并通过迭代改进不断推进。我们提出了最有效的方式:利用开源模型并围绕它进行实验,而不是重新发明轮子或陷入研究的泥潭。我们讨论了“最先进”算法的陷阱和数据增强及迁移学习的好处。我们一致认为实验跟踪的重要性,并学习了如何设置它。最后,我们讨论了离线评估——指标选择、适当的测试集、基于切片的评估和行为测试。

我们快到达目标了,只剩下最后一章。在下一篇(最后一篇)文章中,你将学习关于部署、监控、在线评估和再训练的内容——这些是帮助你构建更好机器学习系统的最后一块知识。

最终章将很快发布。订阅以保持关注。

构建更好的 ML 系统——第四章:模型部署及其发展

原文:towardsdatascience.com/building-better-ml-systems-chapter-4-model-deployment-and-beyond-eae3a75496ec?source=collection_archive---------2-----------------------#2023-09-28

关于部署、监控、数据分布漂移、模型更新和生产环境中的测试。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Olga Chernytska

·

关注 发表在 Towards Data Science ·13 分钟阅读·2023 年 9 月 28 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源

部署模型并在生产环境中支持它们更多是工程问题,而非机器学习问题。

当一个 ML 项目接近生产阶段时,越来越多的人参与其中:后端工程师、前端工程师、数据工程师、DevOps、基础设施工程师……

他们选择数据存储,介绍工作流和管道,将服务集成到后端和 UI 代码库中,自动化发布,进行备份和回滚,决定计算实例,设置监控和警报……如今,几乎没有人期望数据科学家/机器学习工程师能做到这一切。即使在一个小型初创公司中,人们也在某种程度上有所专门化。

“数据科学家/机器学习工程师为什么需要了解生产环境?”——你可能会问。

这是我的回答:

拥有一个在生产环境中的模型并不意味着我们已经完成了所有与机器学习相关的任务。哈!远远没有。现在是时候面对一整套新的挑战了:如何在生产环境中评估你的模型并监控其准确性是否仍然令人满意,如何检测数据分布的变化并应对这些变化,多久重新训练一次模型,以及如何确保新训练的模型更好。这里有一些方法,我们将对此进行详细讨论。

在这篇文章中,我有意只专注于机器学习话题,并省略了许多工程概念或以高层次的方式进行介绍——以便使不同经验水平的人都能简单易懂。

这是“构建更好的机器学习系统”系列的最终篇。本系列旨在帮助你掌握设计和构建机器学习系统的艺术、科学和(有时的)魔法。在之前的章节中,我们已经讨论了项目规划和商业价值(第一章);数据收集、标注和验证(第二章);模型开发、实验跟踪和离线评估……(第三章)。如果你错过了之前的帖子,我建议你在阅读这篇之前或之后看看它们。

部署

在将模型部署到生产环境时,有两个重要的问题需要问:

  1. 模型是否应该实时返回预测结果?

  2. 模型是否可以部署到云端?

第一个问题迫使我们在实时推断与批量推断之间做出选择,第二个问题则在云计算与边缘计算之间做出选择。

实时与批量推断

实时推断是一种直接且直观的与模型互动的方式:你提供一个输入,它返回一个预测。这种方法在需要立即获取预测时使用。例如,银行可能会使用实时推断来验证一笔交易是否存在欺诈行为,然后再最终确认它。

批处理推断则更便宜且更容易实现。之前收集的输入会一次性处理。批处理推断用于评估(在静态测试数据集上运行时)、临时活动(如选择客户进行电子邮件营销活动)或在不需要立即预测的情况下。批处理推断也可以是实时推断的成本或速度优化:你提前计算预测并在请求时返回它们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实时推断与批处理推断。图片来源于作者

实时推断比批处理推断要具有更大的挑战性和成本。这是因为模型必须始终在线并以低延迟返回预测。它需要一个聪明的基础设施和监控设置,这可能甚至在同一公司内部的不同项目中也会有所不同。因此,如果立即获得预测对业务并不关键——那么坚持使用批处理推断会更好。

然而,对于许多公司来说,实时推断在准确性和收入方面确实有所不同。这对搜索引擎、推荐系统和广告点击预测都是如此,因此投资于实时推断基础设施是非常值得的。

关于实时推断与批处理推断的更多详细信息,请查看这些帖子:

云计算与边缘计算

在云计算中,数据通常通过互联网传输并在集中式服务器上处理。另一方面,在边缘计算中,数据在生成数据的设备上处理,每个设备以去中心化的方式处理其自身的数据。边缘设备的例子包括手机、笔记本电脑和汽车。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

云计算与边缘计算。图片来源于作者

像 Netflix 和 YouTube 这样的流媒体服务通常在云端运行其推荐系统。它们的应用程序和网站将用户数据发送到数据服务器以获得推荐。云计算相对容易设置,你可以几乎无限制地扩展计算资源(或至少在经济上合理的情况下)。然而,云基础设施高度依赖稳定的互联网连接,敏感的用户数据不应通过互联网传输。

边缘计算的发展是为了克服云计算的局限性,并且能够在云计算无法工作的地方进行工作。自动驾驶引擎运行在汽车上,因此即使没有稳定的互联网连接也能快速工作。智能手机认证系统(如 iPhone 的 FaceID)运行在智能手机上,因为通过互联网传输敏感用户数据不是一个好主意,用户确实需要在没有互联网连接的情况下解锁手机。然而,为了使边缘计算可行,边缘设备需要足够强大,或者模型必须足够轻量和快速。这催生了模型压缩方法,如低秩近似、知识蒸馏、剪枝和量化。如果你想了解更多关于模型压缩的信息,这里是一个很好的起点:超棒的机器学习模型压缩

若要深入了解边缘计算和云计算,请阅读以下文章:

轻松部署与演示

“生产是一个连续体。对于一些团队来说,生产意味着从笔记本结果生成漂亮的图表来展示给业务团队。而对于其他团队来说,生产意味着确保你的模型每天能为数百万用户持续运行。” Chip Huyen, 为什么数据科学家不必了解 Kubernetes

将模型部署以服务数百万用户是一个大团队的任务,因此作为数据科学家/机器学习工程师,你不会被单独留下。

然而,有时你确实需要单独进行部署。也许你正在进行一个个人或学习项目,并希望创建一个演示。也许你是公司里的第一位数据科学家/机器学习工程师,你需要在公司决定扩展数据科学团队之前带来一些业务价值。也许你的所有同事都忙于各自的任务,因此你在考虑是否自己进行部署而不等待支持。你不是第一个,也绝对不会是最后一个面对这些挑战的人,且有解决方案可以帮助你。

要部署一个模型,你需要一个运行模型的服务器(实例),一个与模型通信的 API(发送输入,获取预测),以及(可选的)一个用户界面,用于接收用户输入并展示预测结果。

Google Colab 是 Jupyter Notebook 的升级版。它是一个很棒的工具,可以创建你可以分享的演示。它不需要用户进行任何特定的安装,提供免费的 GPU 服务器来运行代码,并且你可以轻松定制它以接受用户的任何输入(文本文件、图像、视频)。它在学生和机器学习研究人员中非常受欢迎(这是 DeepMind 研究人员如何使用它的)。如果你对了解更多关于 Google Colab 的信息感兴趣,请从 这里 开始。

FastAPI 是一个用于构建 Python API 的框架。你可能听说过 Flask,FastAPI 类似,但代码更简单,更专注于 API,速度更快。有关更多详细信息,请查看 官方文档。有关实际示例,请阅读 Goku Mohandas 的《模型服务的 API》

Streamlit 是一个易于创建 Web 应用程序的工具。它很简单,我真的这么认为。应用程序看起来很漂亮且互动性强——有图像、图表、输入窗口、按钮、滑块等。Streamlit 提供 Community Cloud,你可以免费发布应用程序。要开始使用,请参阅 官方教程

云平台。Google 和 Amazon 在使部署过程无缝且易于访问方面做得很好。它们提供付费的端到端解决方案,用于训练和部署模型(存储、计算实例、API、监控工具、工作流等)。这些解决方案易于上手,并且功能广泛以支持特定需求,因此许多公司选择使用云服务提供商构建其生产基础设施。

如果你想了解更多内容,请查看以下资源:

监控

像所有生产中的软件系统一样,机器学习系统必须进行监控。这有助于快速检测和定位错误,防止系统发生灾难性故障。

从技术上讲,监控意味着收集日志,从中计算指标,将这些指标显示在类似于 Grafana 的仪表板上,并设置警报以在指标超出预期范围时提醒。

应该监控哪些指标? 由于 ML 系统是软件系统的一个子类,因此可以从操作指标开始。示例包括机器的 CPU/GPU 利用率、内存和磁盘空间;发送到应用程序的请求数量和响应延迟、错误率;网络连接性。要深入了解操作指标的监控,请查看 Justin Ellingwood 的文章指标、监控与警报介绍

操作指标关注机器、网络和应用程序的健康状态,而与 ML 相关的指标检查模型准确性和输入一致性。

准确性是我们最关心的事情。这意味着模型可能仍然会返回预测,但这些预测可能完全不准确,你直到模型被评估时才会意识到。如果你有幸在一个自然标签快速可用的领域工作(如推荐系统),只需收集这些标签,并持续评估模型。然而,在许多领域,标签可能需要很长时间才能到达,或者根本不会出现。在这种情况下,监控一些可能间接指示准确性下降的指标是有益的。

为什么模型准确率会下降?最普遍的原因是生产数据已从训练/测试数据中漂移。在计算机视觉领域,你可以直观地看到数据已经漂移:图像变得更暗或更亮,或分辨率发生变化,或现在室内图像比室外图像更多。

要自动检测数据漂移(也称为“数据分布变化”),需要持续监控模型的输入和输出。模型的输入应该与训练期间使用的一致;对于表格数据,这意味着列名以及特征的均值和方差必须相同。监控模型预测的分布也是有价值的。例如,在分类任务中,你可以跟踪每个类别预测的比例。如果发生了显著变化——比如一个模型以前将 5%的实例分类为 A 类,现在将 20%分类为 A 类——这就是一个明确的信号,表明确实发生了某些事情。要了解更多关于数据漂移的内容,请查看 Chip Huyen 的这篇精彩文章:数据分布变化与监控

关于监控还有很多要说的内容,但我们必须继续前进。如果你觉得需要更多信息,可以查看这些文章:

模型更新

如果你将模型部署到生产环境中并不加以更新,它的准确性会随着时间的推移而降低。 在大多数情况下,这可以通过数据分布的变化来解释。输入数据可能会改变格式。用户行为不断变化,没有有效的理由。流行病、危机和战争可能会突然发生,打破之前有效的所有规则和假设。“变化是唯一的不变。”- 赫拉克利特。

这就是为什么生产模型必须定期更新的原因。更新分为两种类型:模型更新和数据更新。在模型更新中,更改算法或训练策略。模型更新不需要定期进行,通常是根据具体情况进行的——当业务任务发生变化、发现错误或团队有时间进行研究时。相比之下,数据更新是在新数据上训练相同的算法。定期的数据更新是任何 ML 系统的必备条件。

定期数据更新的前提是建立一个能够支持自动数据流、模型训练、评估和部署的基础设施。

关键在于,数据更新应尽量减少人工干预。人工工作应主要用于数据标注(同时确保数据流向和来自标注团队的流动完全自动化),可能需要做出最终部署决策,并解决在训练和部署阶段可能出现的任何问题。

一旦基础设施建立起来,更新的频率只是你需要在配置文件中调整的一个值。模型应该多频繁地用更新的数据进行更新? 答案是:尽可能频繁且经济合理。如果增加更新频率带来的价值大于成本——绝对应该增加。然而,在某些情况下,即使训练每小时进行一次可能会非常有利,也可能不可行。例如,如果模型依赖于人工标注,这个过程可能会成为瓶颈。

从头开始训练还是仅在新数据上进行微调? 这不是一个二元的决策,而是两者的结合。频繁地对模型进行微调是合理的,因为它比从头开始训练更具成本效益且更快。然而,有时从头开始训练也是必要的。理解微调主要是对成本和时间的优化至关重要。通常,公司最初会采用从头开始训练的直接方法,随着项目的扩展和发展,逐渐纳入微调。

要了解有关模型更新的更多信息,请查看此帖子:

重训练,还是不重训练?让我们分析一下 ML 模型更新 由 Emeli Dral 等人编写。

测试生产环境

在将模型部署到生产环境之前,必须对其进行彻底评估。我们已经在上一篇文章中讨论了生产前(离线)评估(请参见“模型评估”部分)。然而,直到你将模型部署到生产环境,你永远不知道它在生产环境中的表现如何。这促生了生产环境中的测试,也称为在线评估。

在生产环境中测试并不意味着草率地将你可靠的旧模型替换为新训练的模型,然后焦急地等待第一次预测,随时准备在出现轻微问题时回滚。绝不要这样做。有更聪明、更安全的策略来在生产环境中测试你的模型,而不会冒着失去资金或客户的风险。

A/B 测试是业界最流行的方法。通过这种方法,流量在现有模型和新模型之间以某种比例随机划分。现有模型和新模型对真实用户进行预测,预测结果被保存并随后仔细检查。比较的不仅仅是模型准确性,还可以比较一些与业务相关的指标,如转化率或收入,这些有时可能与准确性负相关。

A/B 测试高度依赖于统计假设检验。如果你想了解更多,可以查看这篇文章:A/B 测试:统计检验的完整指南 作者:弗朗切斯科·卡萨列尼奥。有关 A/B 测试的工程实现,请查看在线 AB 测试模式

Shadow 部署是测试模型的最安全方式。其理念是将所有流量发送到现有模型,并以通常的方式将其预测返回给最终用户,同时也将所有流量发送到新的(Shadow)模型。Shadow 模型的预测不会被使用,仅仅是存储以备未来分析。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

A/B 测试与 Shadow 部署。图片由作者提供

Canary 发布。你可以将其视为“动态”A/B 测试。新的模型与现有模型并行部署。一开始,只有一小部分流量(例如 1%)会发送到新模型,其余 99% 仍由现有模型提供服务。如果新模型的表现足够好,其流量份额会逐渐增加并再次评估,再次增加并评估,直到所有流量都由新模型提供服务。如果在某个阶段,新模型的表现不佳,它会被从生产环境中移除,所有流量将重新指向现有模型。

这里是进一步解释的文章:

Shadow 部署与 ML 模型的 Canary 发布 作者:巴尔托什·米库尔斯基。

结论

在这一章中,我们了解了一系列新的挑战,这些挑战在模型部署到生产环境后会出现。模型的运营和与 ML 相关的指标必须持续监控,以便快速检测和修复可能出现的错误。模型必须定期在更新的数据上重新训练,因为其准确性会随着时间的推移而降低,主要是由于数据分布的变化。我们讨论了在部署模型之前需要做出的高层次决策——实时推理与批量推理以及云计算与边缘计算,每种都有其自身的优点和限制。我们介绍了用于简单部署和演示的工具,当你必须单独完成时可以使用。我们了解到,模型必须在生产环境中进行评估,除了对静态数据集的离线评估外。你永远不知道模型在生产环境中的表现如何,直到你实际发布它。这一问题催生了“安全”和受控的生产测试——A/B 测试、影子部署和金丝雀发布。

这也是“打造更好的 ML 系统”系列的最后一章。如果你从一开始就跟随我,你现在应该知道,ML 系统远不只是一个复杂的算法。我真的希望这个系列对你有所帮助,拓宽了你的视野,并教会了你如何构建更好的 ML 系统。

感谢你的阅读!

如果你错过了前面的章节,这里是完整的列表:

## 打造更好的 ML 系统。第一章:每个项目都必须从计划开始

关于 ML 项目生命周期、设计文档、业务价值和需求。关于从小做起和快速失败。

## 打造更好的 ML 系统。第二章:驯服数据混乱

关于以数据为中心的 AI、训练数据、数据标注和清洗、合成数据,以及一些数据工程和…

## 打造更好的 ML 系统 — 第三章:建模。让乐趣开始

关于基准线、实验跟踪、适当的测试集和指标。关于让算法发挥作用。

## 打造更好的 ML 系统。第三章:建模。让乐趣开始

因果推断的构建模块——使用 LEGO 的 DAG 方法

原文:towardsdatascience.com/building-blocks-of-causal-inference-a-daggy-approach-using-lego-cac1372348f3

使用 DAG 和贝叶斯回归的因果推断简介

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Murray Gillin

·发布于 Towards Data Science ·阅读时间 9 分钟·2023 年 2 月 4 日

因果推断是一个迷人的话题。因果模型旨在创建对变量关系的机械理解。最近我读了 理查德·麦克艾勒斯的《统计思维》,他那富有表现力且易于理解的写作改变了我对回归和统计分析以及生活的思考方式。

本文旨在探讨使用有向无环图(DAG)和 brms (Buerkner) 的因果建模。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Markus Spiske 提供,来源于 Unsplash

我发现了一篇由 Ziegler 等人撰写的精彩文章,旨在教导小学生多元线性回归模型的教学法。我将使用的数据集取自这篇文章,并在 CC BY 4.0 许可下提供。

[## 使用 LEGO 积木数据构建多元线性回归模型

摘要 我们提出了一项创新活动,利用关于 LEGO 积木的数据帮助学生自我发现多个…

www.tandfonline.com

加载包和数据

library(tidyverse)
library(tidybayes)
library(brms)
library(ggdag)
library(dagitty)

train <- read_csv('lego.population.csv')

train_parse <- 
train %>% 
  drop_na(Weight, Pieces) %>% 
  mutate(Weight = as.numeric(str_sub(Weight, 1, 3)),
         Price = as.numeric(str_remove(Price, '[^[:alnum:]]')),
         Amazon_Price = as.numeric(str_remove(Amazon_Price, '[^[:alnum:]]')),
         Price = coalesce(Price, Amazon_Price)) %>% 
  drop_na(Price, Weight) %>% 
  mutate(Weight_c = Weight/max(Weight),
         Price_c = Price/max(Price),
         Pieces_c = Pieces/max(Pieces)) %>% 
  drop_na(Weight, Pieces, Price)

我们进行了一些基本的数据清理,然后对变量进行最小/最大缩放,使它们在相同的尺度上,这样我们后续对贝塔后验均值的解释会更容易得多。

探索性数据分析

使用 GGally:ggpairs,我们生成了下面的变量对图。

GGally::ggpairs(train_parse %>% 
                  select(Weight_c, Pieces_c, Price_c),
                aes(alpha = 1/3)) +
  theme_minimal() +
  labs(title = 'Pairplot for Weight, Pieces and Price')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用最小-最大缩放变量的变量对图(图由作者提供)

没有特别有趣的内容需要注意,除了所有三个变量之间强正线性关系的存在。但正确的因果模型是什么?增加价格是否也会增加件数?我认为不是这样。

因果推断

使用加载的数据集,我们将创建一个关于价格的因果理解。这个数据集在因果关系上概念上很容易理解。

数以千计的乐高套装存在,从基本的儿童 Duplo 到像千年隼或死亡星这样的包含数千件的巨大套装。价格从$10–20 起步,对于限量版模型套装则高达数千美元。我们能否为给定套装的特征创建一个合理的因果模型?

DAGs

在下文中,我们使用 daggity 包设置我们提议的 DAG,配以一些绘图坐标,然后使用 ggdag 绘制下面的图,描述了权重和件数如何影响乐高套装价格的所有不同路径。

dag_coords <- 
  tibble(name = c("Pcs", 'W', 'Pr'),
         x = c(1, 2, 2),
         y = c(2, 2, 1))

dagify(Pr ~ Pcs,
       Pr ~ W, 
       W ~ Pcs,
       coords = dag_coords) %>% 
  ggplot(aes(x = x, y = y, xend = xend, yend = yend)) +
    geom_dag_point(color = 'dark red', alpha = 1/4, size = 20) +
    geom_dag_edges(edge_color = 'dark red') +
    geom_dag_text(color = 'dark red') +
    scale_x_continuous(NULL, breaks = NULL, expand = c(.1, .1)) +
    scale_y_continuous(NULL, breaks = NULL, expand = c(.1, .1)) +
    theme_bw() +
    theme(panel.grid = element_blank())

我们的任务现在是测试这些 DAG 的影响,然后最终确定一个最佳描述这些变量对价格影响的因果模型。首先,我们可以合理地说,件数与重量和价格正相关,即套装的件数越多,重量和价格也会更高。类似地,我们也可以推理出,较大、较重的套装应该价格更高。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

权重、件数和价格的完整 DAG(图由作者提供)

对件数进行价格回归

自然的起点是将价格与件数进行回归,Pcs → Pr。贝叶斯分析的美妙之处在于能够在查看数据之前提供先验分布。集合价格的分布由正态分布描述,其中均值由拦截项和件数的梯度项的线性项描述。拦截项由一个相对宽泛的高斯分布描述,其均值为$20,标准差为$6。实质上,这描述了没有任何件数的乐高套装的基础价格分布。beta 项描述了每件的价值增加。谷歌搜索表明乐高件的平均价格为 11 美分,因此这是一个良好的起点,我们将使标准差足够宽泛。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Pcs → Pr 先验公式(图由作者提供)

mP_Pr <- brm(Price ~ 1 + Pieces,
             family = gaussian,
             prior = c(prior(exponential(1), class = sigma),
                       prior(normal(20, 6), class = Intercept),
                       prior(normal(0.11, 0.04), class = b)
                       ),
             data = train_parse,
             warmup = 1000, iter = 2000, chains = 4, cores = 4, seed = 246) %>% 
  add_criterion(criterion = c('loo'))
summary(mP_Pr)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Pcs → Pr 的摘要输出(图由作者提供)

在接触数据后,我们的先验并没有偏离太远,截距项的后验均值代表了一个零件数为零的 Lego 套件的平均价格,每增加一个部件,套件的价值增加 8 分。后验分布在各自的尺度上具有相当离散的误差项,因此基于数据模型对这些值相当自信。

将价格回归于重量

类似于上述情况,设置一些合理但宽泛的先验,这些先验将被数据所淹没。我们假设与之前相同的截距先验,重量的 beta 项也很宽泛,均值为 $50/kg,标准差为 $15。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

W → Pr 先验公式(图片由作者提供)

mW_Pr <- brm(Price ~ 1 + Weight,
             family = gaussian,
             prior = c(prior(exponential(5), class = sigma),
                       prior(normal(20, 6), class = Intercept),
                       prior(normal(50, 15), class = b)),                       )
             data = train_parse,
             warmup = 1000, iter = 2000, chains = 4, cores = 4, seed = 246)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

W → Pr 的总结输出(图片由作者提供)

这次截距项低于之前,后验均值为 $5.77,重量的梯度项后验均值为 $57.90/kg。注意这个模型更为自信,因为误差值比回归价格于部件数时更为离散。

将价格回归于部件数和重量

现在情况变得更加有趣了——我们为价格创建了两个贝叶斯回归模型,分别使用部件数和重量。这两个模型看起来都相当不错,但哪个是更好的因果模型呢?

遇到碰撞器,其中部件数和重量彼此独立(Pcs || W)。鉴于我们对变量的机械知识,我们已经可以将这种形式排除,但让我们开发模型来支持这一点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

碰撞器 DAG 和 W → Pr ← Pcs 的公式(图片由作者提供)

# The Colllider
mWP_Pr <- brm(
  Price ~ 1 + Pieces + Weight, 
  family = gaussian,
             prior = c(prior(exponential(5), class = sigma),
                       prior(normal(20, 6), class = Intercept),
                       prior(normal(50, 15), class = b, coef = Weight),
                       prior(normal(0.11, 0.04), class = b, coef = Pieces)
                       ),
  data = train_parse,
             warmup = 1000, iter = 4000, chains = 4, cores = 4, seed = 246)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

W → Pr ← Pcs 的输出总结(图片由作者提供)

我们立刻注意到,截距的系数几乎没有变化——而且两个预测变量的系数都有所下降,这并不令人惊讶,因为它们的相关性非常强,这是多重共线性的一个例子。这足以证明碰撞器作为合适的因果模型是不成立的,违反了两个变量之间的独立性假设。下面我们将重量和部件数的后验分布一起可视化为散点图,以展示多重共线性,存在某种共享的方差轴。在右侧,我们还看到回归于重量与回归于重量和部件数一起的重量项的后验分布几乎没有差异。

# LHS: Plot of Posterior Draws Weight vs. Pieces
as_draws_df(mWP_Pr) %>%
  ggplot(aes(x = b_Pieces_c, y=b_Weight_c)) +
    geom_point(alpha = 0.3) +
    labs(y = 'Weight', x = 'Pieces', title = 'Weight and Pieces Display Multicollinearity and Not Indpendant', subtitle = 'How Can Price per Piece Increase as Price per Unit Weight Decreases?') +
    theme_minimal()

# RHS: Comparison of Posterior Draws for Weight Across W → Pr ← Pcs & W → Pr
tidy_draws(mWP_Pr) %>% 
  select(b_Pieces_c, b_Weight_c) %>% 
  transmute(Pieces_Weight = b_Pieces_c + b_Weight_c,
         Weight = tidy_draws(mW_Pr) %>% select(b_Weight_c) %>% as_vector()) %>% 
  pivot_longer(1:2, names_to = 'Variable', values_to = 'posterior_samples') %>% 
  ggplot(aes(posterior_samples, fill = Variable)) +
           geom_density(alpha = 1/3) +
  theme_minimal() +
  labs(title = 'Addition of Pieces and Weight is Nearly Equivalent to Weight Alone',
       subtitle = 'Variables Min-Max Scaled',
       y = 'Density',
       x = 'Posterior Distribution')

#NB We've used models regenerated using min-max scaled variables. 

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

重量和部件数显示了多重共线性,变量经过最小-最大缩放(图片由作者提供)

下一个 DAG 被称为管道,其中件数在条件为重量时与价格独立(或 Pcs || Pr | W)。换句话说,一旦知道一组的重量,知道件数对我们理解价格没有进一步的价值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

管道 Pcs → W → Pr(图像由作者提供)

以下我们将管道建立为贝叶斯模型,使用我们之前示例中的先验。

# The Pipe
pr_model <- bf(Price ~ 1 + Weight)
w_model <- bf(Weight ~ 1 + Pieces)

mWP_Pr2 <- brm(pr_model + w_model + set_rescor(F),
    prior = c(prior(exponential(5), class = sigma, resp = Price),
              prior(exponential(5), class = sigma, resp = Weight),
              prior(normal(50, 15), class = b, coef = Weight, resp = Price),
              prior(normal(0.11, 0.04), class = b, coef = Pieces, resp = Weight)),
             family = gaussian,
             data = train_parse,
             warmup = 1000, iter = 4000, chains = 4, cores = 4, seed = 246)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Pcs → W → Pr 的总结输出(图像由作者提供)

由于我们在重量上回归,价格的截距保持不变,其后验均值实际上与我们的第一个模型 W → Pr 相同。重量截距的后验均值可能代表了在添加任何件数之前的包装重量。件数的回归系数相对较小(0.0014kg/1.4g 每件)。

让我们用一个反事实模型来测试隐含的条件独立性。我们建立新的数据,改变件数的同时保持重量恒定为 1 公斤。

# Counterfactual Plot Pieces on Price, Holding Weight Consant
nd <- tibble(Pieces = seq(from = 0, to = 5000, length.out = 50),
             Weight = 1)

predict(mWP_Pr2, 
        resp = c('Price'),
        newdata = nd) %>% 
  as_tibble() %>% 
  bind_cols(nd) %>% 
  ggplot(aes(x = Pieces, y = Estimate, ymin = Q2.5, ymax = Q97.5)) +
    geom_smooth(stat = 'identity', alpha = 1/5, size = 1/4) +
    labs(y = 'Counterfactual Price', x = 'Manipulated Pieces')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

价格的影响因素图(图像由作者提供)

由于价格的影响是通过重量间接体现的,因此在重量恒定的情况下,价格将保持不变,无论件数的多少。这支持了隐含的条件独立性,即在条件为重量时,价格件数是独立的(或 Pcs || Pr | W)。换句话说,一旦知道一组的重量,知道件数对我们理解价格没有进一步的信息增量。

总结评论

在本文中,我们展示了使用贝叶斯回归的因果建模的易于理解的入门介绍。我们开发了一个因果模型,用于分析物理变量(件数重量)对价格的影响,并发现我们可以合理地认为,将价格回归到重量上是一个不错的因果模型。

我想借此机会感谢理查德·麦克埃尔瑞斯特(Richard McElreath)对因果建模的精彩著作和讲座,激发了我改变思维方式,采用更加严谨的贝叶斯方法来处理工作和生活。

谢谢。我希望你阅读这篇文章的乐趣与我写作时的乐趣一样。如果你还不是 Medium 的会员——使用我的推荐链接,定期获取我和其他优秀 Medium 作者的新出版物更新。

[## 穆雷·吉林 - Medium

阅读穆雷·吉林在 Medium 上的文章。亚马逊澳大利亚的业务风险分析师 | 热情的数据分析师和机器学习专家…

mmgillin.medium.com

在 Python 中构建理解管道

原文:towardsdatascience.com/building-comprehension-pipelines-in-python-ec68dce53d03

PYTHON 编程

理解管道是构建管道的一种 Python 特有的理念

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 马尔钦·科扎克

·发表于Towards Data Science ·阅读时间 12 分钟·2023 年 2 月 17 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

理解管道能直接带你到达目标。图片来源于安妮卡·胡辛加Unsplash

生成器管道提供了一种 Pythonic 的方式来创建软件管道,即操作链,其中每个操作(除了第一个)都将前一个操作的输出作为其输入:

## 在 Python 中构建生成器管道

这篇文章提出了一种优雅的方式来构建生成器管道

towardsdatascience.com

它们使你能够应用转换编程,如托马斯和亨特在他们伟大的书籍程序员的修炼中所描述的那样

[## 程序员的修炼 - 维基百科

来自维基百科,百科全书《程序员的修炼:从学徒到大师》是一本关于计算机的书…

en.wikipedia.org](https://en.wikipedia.org/wiki/The_Pragmatic_Programmer?source=post_page-----ec68dce53d03--------------------------------)

Python 中的典型生成器管道在管道的每一步都使用生成器。换句话说,管道的每一步都构建为一个生成器。Thomas 和 Hunt 讨论了通过管道操作符实现的管道,这在许多编程语言中都可以使用。虽然 Python 没有内置的管道操作符,但由于某些操作符可以在类定义中重载,因此可以很容易地创建它。我们可以在这个 [Pipe](https://github.com/JulienPalard/Pipe) Python 包中看到这一点,该包使用了|操作符。

在上述文章中,我展示了使用生成器来创建管道的每一步可能引入的视觉混乱,从而降低代码的可读性。此外,当管道的每次运行都很快时,这种方法的性能较差。因此,我提出了一种替代的、比经典生成器管道更具可读性的高效构建生成器管道的方法。该方法将函数组合与生成器结合使用。未来,我将向您展示如何使用管道操作符创建管道。

尽管如此,正如我在本文中所写的那样,生成器表达式是推导的一个特例。

## Python 推导指南

学习列表推导(listcomps)、集合推导(setcomps)、字典推导等的复杂性。

[towardsdatascience.com

那么,为什么我们要把自己局限于生成器管道呢?为什么不考虑列表推导管道、字典推导管道或集合推导管道呢?

这个问题困扰了我一段时间,但我把它当作烦人的苍蝇在头顶嗡嗡作响,无法让我安宁,不停地嗡嗡嗡……最终,我放弃了拒绝这个想法,并决定至少提出讨论。大约两个月前,在一次愉快的冬季散步中,我带着我的两只狗走在森林里。一个美丽的冬天,雪、霜、强风——我和狗狗们在树林里,走在我最喜欢的小路上,思考(好吧,是我,不是狗)如何使用其他推导来构建管道,而不仅仅是生成器表达式。

本文是这次探讨的结果。我提出了一种将生成器管道推广到我称之为推导管道的概念,其中生成器管道只是一个特例。

生成器管道示例

为了保持一致性和清晰性,我将使用在上一篇文章中用到的相同生成器管道示例。请注意,我稍微修改了一些类型注解。生成器管道如下¹:

import math

from typing import Generator

# Type aliases
Number = int | float
PowerType = int | float
PipelineItems = Iterable[Number]

def double(x: Number) -> Number:
    return x**2

def power(x: Number, n: PowerType) -> Number:
    return x**n

def add(x: Number, y: Number) -> Number:
    return x + y

def calculate(x: Number) -> Number:
    x = power(x, 0.5)
    x = double(x)
    x = add(x, 12)
    x = power(x, 2)
    x = add(x, math.pi**0.5)
    x = round(x, 2)
    x = add(x, 75)
    return x

def get_generator_pipeline(
    items: PipelineItems,
) -> Generator[Number, None, None]:
    """Create generator pipeline applying calculate() to each item."""
    return (calculate(x_i) for x_i in items)

get_generator_pipeline()函数——之前称为get_pipeline()——返回一个生成器管道;也就是说,一个生成器,它懒惰地(按需)计算items可迭代对象的后续元素的管道。我们可以用任何我们想要的方式评估生成器,例如,使用list()

>>> items = [1.12, 2.05, 1.122, -0.220002, 7.0036]
>>> pipeline = get_generator_pipeline(items)
>>> list(pipeline)

如果我们想得到一个列表,那么使用生成器表达式就没有意义——对应的列表推导式会更好!那么,为什么一开始就不使用列表推导管道而使用生成器管道呢?

什么时候使用生成器管道?什么时候不使用?

生成器管道有一个重要的优势,这与生成器的主要优势相同:延迟评估。当items可迭代对象很大,或者管道的步骤生成大型对象时,生成器管道将帮助我们避免内存不足的问题。

但是,如果items可迭代对象很短且输出可迭代对象占用的内存不多,那我们为何要担心内存问题呢?更何况,我们知道生成器表达式可能比对应的列表推导式要慢——唯一的例外是,当项的数量(或大小)大到无法在内存中全部保留和处理时。

考虑以下这个与上面示例相当不同的例子:

  • 我们有paths,一个包含文件路径的列表。

  • 在管道的每一步中,从一个路径读取一个文本文件,并对文本进行处理。

  • 因此,返回的是处理后的文本的可迭代对象。

在这种情况下,输出可迭代对象将远大于输入可迭代对象。因此,当路径数量庞大时,生成器管道在这里效果最好——因为返回一个长文本的长列表将会非常低效,甚至可能无法实现。我们需要管道返回一个生成器,因此需要生成器管道。

现在,想象另一个管道。我们有相同的路径可迭代对象,但我们对文本的处理不同了。之前,我们返回长文本。现在,我们只需要每个文本中是否包含“Python”这个词的信息;因此,对于每个文本,我们只需要一个布尔值。对于路径列表,我们将得到一个长度相同的布尔值列表。生成器管道在这里的优势是什么?没有。

更重要的是,仅返回布尔值几乎没有意义:很难将特定的值与相应的文本联系起来。因此,最好返回一个以路径为键,布尔值为值的字典。这将使管道基于字典。或者,我们可以返回一个包含文本中“Python”单词的路径列表;然而,这种输出会忽略其他路径,从而丢失部分信息——有时我们可能需要这些信息。

建立理解管道

我们终于来到了本文的主题:理解管道以及如何构建它们。上述示例展示了我们如何决定是否需要生成器管道或其他类型的管道。除了生成器管道,我们还可以构建

  • 列表理解管道, listcomp 管道

  • 集合理解管道, setcomp 管道

  • 字典理解管道, dictcomp 管道

接下来,我将使用这三种其他类型的理解管道重写使用calculate()函数的生成器管道。为了完整性,我将重复生成器管道的代码。我还会改变typing的导入,因为这次我们需要比之前更多的类型,之前我们只创建了一个生成器管道。

from typing import Dict, Generator, Iterable, List, Set

def get_generator_pipeline(
    items: PipelineItems,
) -> Generator[Number, None, None]:
    """Create generator pipeline applying calculate() to each item."""
    return (calculate(x_i) for x_i in items)

def get_listcomp_pipeline(items: PipelineItems) -> List[Number]:
    """Create listcomp pipeline applying calculate() to each item."""
    return [calculate(x_i) for x_i in items]

def get_setcomp_pipeline(items: PipelineItems) -> Set[Number]:
    """Create setcomp pipeline applying calculate() to each item."""
    return {calculate(x_i) for x_i in items}

def get_dictcomp_pipeline(items: PipelineItems) -> Dict[Number, Number]:
    """Create dictcomp pipeline using calculate() for items.

    Items are dict keys with calculate(item) being
    the corresponding value.
    """
    return {x_i: calculate(x_i) for x_i in items}

def get_dictcomp_pipeline_str(items: PipelineItems) -> Dict[str, Number]:
    """Create dictcomp pipeline using calculate() for items.

    str(item) are dict keys with calculate(item) being
    the corresponding value.
    """
    return {str(x_i): calculate(x_i) for x_i in items}

我使用了特定版本的字典管道,但请注意,我们也可以构建其他版本,具体取决于我们希望将什么用作字典的键。稍后我们将看到一个示例。

接下来,我们将总结这些基本类型的管道,包括生成器管道。请记住,它们在输出上有所不同,因为它们都可以接受相同类型的输入——任何可迭代对象都可以。

生成器管道

  • 以任何可迭代对象(items)作为输入。

  • 返回一个生成器作为管道。

  • 可以使用任何形式和复杂度的生成器表达式(例如,它可以包含多个级别的if过滤器和for循环)。

  • 可以按需求值。

列表理解管道,也称为 listcomp 管道

  • 以任何可迭代对象(items)作为输入。

  • 运行列表理解作为管道,因此返回一个列表。

  • 可以使用任何复杂度的列表理解。

  • 是贪婪求值的。

集合理解管道,也称为 setcomp 管道

  • 以任何可迭代对象(items)作为输入。

  • 运行集合理解作为管道,因此返回一个集合。

  • 可以使用任何复杂度的集合理解。

  • 由于最终输出是一个集合,它将包含唯一的结果。因此,如果可迭代对象中的两个或更多项返回相同的输出,它们的重复实例将被跳过。

  • 是贪婪求值的。

字典理解管道,也称为 dictcomp 管道

  • 以任何可迭代对象(items)作为输入。

  • 运行字典理解作为管道,因此返回一个字典。

  • 可以使用任何复杂度的字典理解。

  • 对于特定的item,返回一个键值对;键不必是item——它可以是从item或其任何处理结果中得到的任何东西。

  • 由于管道返回一个字典,你应该使用唯一的键;否则,相同键的结果将被覆盖,最后一个键值对将被保留。

  • 是贪婪求值的。

示例

在这里,让我们看看上述管道的行为。我们将对以下可迭代对象进行操作:

>>> items = (1, 1.0, 10, 50.03, 100)

我以 doctests 的形式展示这些示例。你可以在以下Towards Data Science文章中阅读更多关于这个用于文档测试的出色 Python 内置包的内容:

## Python 文档测试使用 doctest:简单的方法

doctest 允许进行文档、单元和集成测试,以及测试驱动开发。

[towardsdatascience.com

在文章末尾的附录中,你会找到这个练习的完整脚本。

生成器管道

>>> gen_pipeline = get_generator_pipeline(items)
>>> gen_pipeline # doctest: +ELLIPSIS
<generator object get_generator_pipeline.<locals>.<genexpr> at 0x7...>

正如预期的那样,生成器管道返回一个生成器。因此,目前我们无法看到输出,要做到这一点,我们需要评估其值。如何做取决于管道和解决的问题。在这里,我们将使用一个简单的 for 循环:

>>> for i in gen_pipeline:
...     print(i)
245.77
245.77
560.77
3924.49
12620.77

请记住,即使 gen_pipeline 是使用生成器管道创建的,它仍然是一个普通的生成器。作为生成器,在评估之后(我们在上面的 for 循环中做了),它是空的。它仍然存在,但你不能再用它来查看输出:

>>> gen_pipeline # doctest: +ELLIPSIS
<generator object get_generator_pipeline.<locals>.<genexpr> at 0x7...>
>>> next(gen_pipeline)
Traceback (most recent call last):
  ...
StopIteration

Listcomp 管道

>>> list_pipeline = get_listcomp_pipeline(items)
>>> list_pipeline
[245.77, 245.77, 560.77, 3924.49, 12620.77]

Listcomp 管道会贪婪地评估管道,因此在创建时就可以看到结果。你可以根据需要多次这样做,这与上面的生成器管道不同。

像之前一样,我们可以看到前两个值完全相同。这是可以预期的,因为 items 的前两个元素是相同的……或者不是?第一个是整数 1,而第二个是浮点数 1.0。理论上,这些 不是 相同的对象,因为它们的类型不同。然而,Python 将它们视为相等:

>>> 1 == 1.0
True

那么,setcompdictcomp 管道会有什么表现呢?我们将在下文中看到。

Setcomp 管道

>>> set_pipeline = get_setcomp_pipeline(items)
>>> set_pipeline
{560.77, 3924.49, 245.77, 12620.77}

哈!注意虽然 items 包含五个元素,但上述输出仅包含四个。这并不意外——正如我们之前看到的,Python 将 11.0 视为相等,因此对这两个值评估 calculate(x) 的结果是相同的。由于它们是相同的,结果集合中只包含一个输出值,即 245.77

使用集合和 setcomp 管道时请记住这一点。因此,当你希望实现这种行为时——换句话说,当你希望只保留唯一结果时——请使用 setcomp 管道。

Dictcomp 管道

>>> dict_pipeline = get_dictcomp_pipeline(items)
{1: 245.77, 10: 560.77, 50.03: 3924.49, 100: 12620.77}

与集合一样,我们得到了结果字典的四个元素。如你所见,当你希望在字典中使用 11.0 作为键时,它们会合并成一个键,在我们的例子中是 1。如果这正是你想要的结果,那么你完成了。

如果你需要它们两个怎么办?你可以创建字符串键。例如,Python 是否将 str(1)str(1.0) 视为不同的?让我们来看看:

>>> str(1) != str(1.0)
True

是的,确实如此!我们需要重新定义管道函数,然后:

def get_dictcomp_pipeline_str(items: PipelineItems) -> Dict[str, Number]:
    """Create dictcomp pipeline using calculate() for items.

    str(item) are dict keys with calculate(item) being
    the corresponding value.
    """
    return {str(x_i): calculate(x_i) for x_i in items}

让我们看看这个新的 dictcomp 管道如何运作:

>>> dict_str_pipeline = get_dictcomp_pipeline_str(items)
>>> dict_str_pipeline
{'1': 245.77, '1.0': 245.77, '10': 560.77, '50.03': 3924.49, '100': 12620.77}

结果字典包含五个元素,正如我们所期望的那样。

结论

在这篇文章中,我提出了将生成器管道的一般化扩展到理解管道。虽然生成器表达式通常用于创建管道,但生成的生成器管道是理解管道的一个特例。当你创建一个管道时,请考虑哪种类型的管道最能代表你的需求——并使用它。不必因为“生成器管道”这个术语在 Python 社区中很常见而仅仅坚持使用生成器管道。你可以自由使用任何适合你目标的方法。

在这篇文章中,我们使用了简单的示例。我这样做是有目的的:这种简洁性帮助我们集中关注本文的主要话题——理解管道。未来,我计划展示更多代表现实生活场景的高级示例。

请注意,创建最终理解的函数——在我们的示例中,get_generator_pipeline()get_listcomp_pipeline()get_setcomp_pipeline()get_dictcomp_pipeline()get_dictcomp_pipeline_str()——仅创建管道的最后一步。然而,实际的管道隐藏在这些函数调用的函数中;在我们的例子中,这是calculate()函数。我们暂时回到这个函数:

def calculate(x: Number) -> Number:
    x = power(x, .5)
    x = double(x)
    x = add(x, 12)
    x = power(x, 2)
    x = add(x, math.pi**.5)
    x = round(x, 2)
    x = add(x, 75)
    return x

你明白我的意思吗?我们的管道由函数power()double()add()、再次power()、再次add()round()和再次add()组成。这里应用了所有的管道步骤,创建输出的函数只是以适合你需求的方式调用这个函数。

记住,如果基本函数(前面句子中列出的前四个函数)不符合你的需求,你可以创建一个新函数,就像我们在定义get_dictcomp_pipeline_str()函数时所做的那样。这个例子表明,我们不受限于理解管道的基础版本:只要正确,你可以做任何你想做的事情。

脚注

¹ 如果你使用的是低于 3.10 版本的 Python,代码将无法运行。这是因为typing的联合运算符|可以替代Union,它是在 Python 3.10 中添加的。因此,如果你使用的是旧版本 Python,请替换这两行:

Number = int | float
PowerType = int | float

使用这三行:

Number = Union[int, float]
PowerType = Union[int, float]

这将有效。

附录

以下是本文中使用的脚本的完整代码。如上脚注所述,在旧版本的 Python 中,你可能需要将int | float替换为Union[int, float],当然在此之前需要从typing中导入Union

import math

from typing import Dict, Generator, Iterable, List, Set

Number = int | float
PowerType = int | float
PipelineItems = Iterable[Number]

def double(x: Number) -> Number:
    return x**2

def power(x: Number, n: PowerType) -> Number:
    return x**n

def add(x: Number, y: Number) -> Number:
    return x + y

def calculate(x: Number) -> Number:
    x = power(x, 0.5)
    x = double(x)
    x = add(x, 12)
    x = power(x, 2)
    x = add(x, math.pi**0.5)
    x = round(x, 2)
    x = add(x, 75)
    return x

def get_generator_pipeline(
    items: PipelineItems,
) -> Generator[Number, None, None]:
    """Create generator pipeline applying calculate() to each item."""
    return (calculate(x_i) for x_i in items)

def get_listcomp_pipeline(items: PipelineItems) -> List[Number]:
    """Create listcomp pipeline applying calculate() to each item."""
    return [calculate(x_i) for x_i in items]

def get_setcomp_pipeline(items: PipelineItems) -> Set[Number]:
    """Create setcomp pipeline applying calculate() to each item."""
    return {calculate(x_i) for x_i in items}

def get_dictcomp_pipeline(items: PipelineItems) -> Dict[Number, Number]:
    """Create dictcomp pipeline using calculate() for items.

    Items are dict keys with calculate(item) being the corresponding value.
    """
    return {x_i: calculate(x_i) for x_i in items}

def get_dictcomp_pipeline_str(items: PipelineItems) -> Dict[str, Number]:
    """Create dictcomp pipeline using calculate() for items.

    str(item) are dict keys with calculate(item) being
    the corresponding value.
    """
    return {str(x_i): calculate(x_i) for x_i in items}

if __name__ == "__main__":
    items = (1, 1.0, 10, 50.03, 100)
    gen_pipeline = get_generator_pipeline(items)
    list_pipeline = get_listcomp_pipeline(items)
    set_pipeline = get_setcomp_pipeline(items)
    dict_pipeline = get_dictcomp_pipeline(items)
    dict_str_pipeline = get_dictcomp_pipeline_str(items)

    # Generator pipeline
    # Note that we need to evaluate it to see the output,
    # hence the for loop.
    print(gen_pipeline)
    for i in gen_pipeline:
        print(i)

    # Listcomp pipeline
    print(list_pipeline)

    # Setcomp pipeline
    print(set_pipeline)

    # Dictcomp pipeline
    print(dict_pipeline)

    # Dictcomp pipeline with strings as keys
    print(dict_str_pipeline)

在亚马逊网络服务上构建生成型 AI 应用——我的第一次经历

原文:towardsdatascience.com/building-generative-ai-apps-on-amazon-web-services-my-first-experience-c54f60a21fb1

48 小时黑客马拉松:Amazon Bedrock & SageMaker

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Col Jung

·发表于 Towards Data Science ·阅读时间 11 分钟·2023 年 9 月 22 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由以色列·安德拉德提供 (Unsplash)

大公司对生成型 AI 该如何处理还不完全确定,但他们想要做一些事情。

一些人正在通过内部黑客马拉松探索这项技术。

更新:我现在在 YouTube 上发布分析教程。

作为澳大利亚“四大”银行之一的工程师和数据科学家,在过去一个月里,我被卷入了三次这些令人兴奋的活动中。

为什么是黑客马拉松?

他们作为一种绝佳方式,让公司的知识工作者——无论是技术还是非技术人员——头脑风暴生成型 AI 应用场景,测试市场上可用的 AI 工具,并快速制作一些工作原型供决策者审查。

这对你有什么好处?

  • 脱离日常工作。(老板,这只是开玩笑!)

  • 提升你的创业和创新技能。

  • 生成型 AI 是一种颠覆性技术。赶上潮流,否则被抛在后面。

  • 在不同的云服务供应商处获得实践经验,例如 AWSMicrosoft

最后一项内容很重要。

不是每天都有公司付钱让你探索最新的企业生成型 AI 工具。

在这篇文章中,我想与我的分析师、工程师和数据科学家同行分享我玩转亚马逊网络服务(AWS) AI 工具栈的经验。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

大型模型预示着机器学习的范式转变。作者提供的图片

AWS 的友善团队为我们黑客马拉松的参与者提供了以下资源:

  1. AWS SageMaker JumpStart 用于访问文本和图像基础模型;

  2. 在 AWS Bedrock 上的Guru 来调整我们基于公司数据的聊天机器人;

  3. Streamlit 用于快速部署概念验证应用来推销我们的产品。

让我们深入了解吧!

AWS 生成 AI 堆栈

生成 AI 需要大量的数据集和巨大的计算能力来训练这些数据上的巨大神经网络,这使得公共云成为一个理想的平台选择。

主要的公共云服务提供商,如亚马逊谷歌微软,正在激烈争夺开发者、企业和研究人员,通过提供训练和微调生成式 AI 模型的能力,并消费预训练的基础模型-即服务

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一系列后端的神奇技术支持生成 AI 产品。图片来源:作者

微软由于与OpenAI 的幸运合作,领先于其竞争对手——这是一项真正的战略智慧

OpenAI 的旗舰产品 ChatGPT 在 2023 年的飞速崛起震惊了谷歌。CEO Sundai Pichai 认识到这对谷歌现有商业模式构成了生死威胁,发出了红色警报,呼吁全员动员,将追赶作为首要任务。

与此同时,亚马逊稍晚进入这个领域,但正在迅速追赶。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

主要公共云服务提供商的 AI 堆栈。图片来源:Ivana Tilca (合理使用)

AWS 在生成 AI 的投资主要集中在三个领域:

  • AWS Bedrock 是一个完全托管的平台,旨在通过 API 轻松消费基础模型。亚马逊与AI21LabsAnthropicStability AI等生成 AI 初创公司合作,提供通过亚马逊Bedrock API 访问的文本和图像基础模型。Bedrock 被定位为微软Azure OpenAI平台的竞争对手——被认为是公共云中最成熟和可靠的生成 AI 平台。

  • AWS SageMaker 是一个提供用户访问、定制和部署机器学习模型的环境。亚马逊最近扩展了对基础模型的支持,允许用户利用和微调一些最受欢迎的开源模型。特别地,通过官方合作伙伴关系,它提供了对Hugging Face 模型广泛目录的轻松访问。

  • Amazon Titan,是由亚马逊内部研究人员精心打造的一系列基础模型。预计TITAN将整合各种亚马逊服务的模型,如AlexaCodeWhispererPollyRekognition和其他 AI 服务。

背景介绍完毕,我将深入探讨我们在黑客马拉松中使用 AWS 工具的经验。

AWS SageMaker JumpStart

简而言之,SageMaker使我们能够在 AWS 上部署基础模型,并通过 API 在Jupyter Notebooks中访问这些模型。

在黑客马拉松开始时,我们获得了访问AWS Workshop Studio的凭证,这是亚马逊用于培训和运行多天 AWS 研讨会的沙箱云环境。

实际上,我们的Workshop Studio环境配备了一个完整的 GenAI 课程,但考虑到我们只有 48 小时可以操作,AWS 的讲师为我们提供了一个快速的速成课程,帮助我们访问构建工作 GenAI 产品所需的基本内容。

SageMaker配备了一项名为JumpStart的功能,它正是这样做的:通过提供预训练的基础模型来快速启动 AI 和 ML 项目,使项目能够迅速起步。对于那些希望尽快开始而无需重新发明轮子的人来说,这是一个福音——考虑到从头训练**大型模型(LM)**所需的技术和计算工作,这一提议极具价值。

AWS 教会我们如何使用JumpStart访问一些 AWS 预训练的文本到文本基础模型,通过一种数据科学家熟悉的界面:Jupyter Notebooks。这一切都直接集成在SageMaker中。

我们获得了包含所有所需代码的笔记本,以使用文本到文本模型,包括亚马逊的Titan和 Meta 的Llama

我们只需插入 AWS 提供的 API 密钥。很简单!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像

另一种受欢迎的 GenAI“类型”是文本到图像模型,例如 OpenAI 的DALL-E、Midjourney Inc 的Midjourney和 Stability AI 的开源Stable Diffusion

再次通过熟悉的Jupyter Notebooks界面,我们迅速开始玩弄开源的图像合成 GenAI 模型,特别是稳定扩散模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者提供的图像

快速说明: Stable Diffusion是 Stability AI 推出的产品,而“稳定扩散模型”实际上是一个市场营销术语,指的是将潜在扩散模型(解决了 GAN 的缺陷)与称为 CLIP 嵌入的技术相结合的模型。DALL-E V2、Midjourney V5 和Stable Diffusion 使用稳定扩散模型,这些模型目前是图像合成的最前沿技术。

有关文本到文本和文本到图像模型的技术深入,请参阅我的文章 这里这里

总的来说,SageMaker 使我们能够快速利用 GenAI,因为…

  1. AWS 已经为我们在 SageMaker JumpStart 上部署了 6 到 7 个 LMs;

  2. 它们可以通过熟悉的 Jupyter Notebooks 访问;

  3. 我们被提供了立即使用这些模型所需的 Python 代码和 API 密钥。

这意味着那些技术背景不特别强的分析师可以很快开始使用 GenAI,并将更多宝贵的黑客马拉松时间用于用例构思、构建和推介。

Streamlit & Cloud9

Streamlit.io 是一个开源的前端应用程序。

对于黑客马拉松参与者而言,Streamlit 是一种快速创建可以与我们构建的 Gen-AI 模型互动的可视化应用的好方法。

将我们的模型与Streamlit连接的最简单方法是使用亚马逊在线 IDE AWS Cloud9 进行编码,并将其部署到 AWS,更确切地说是 Amazon EC2

为了揭开术语的面纱,Cloud9 是亚马逊自己提供的基于云的开发环境,而 EC2 (Elastic Compute Cloud) 是 AWS 颇受欢迎的云环境,个人和企业可以在其中部署应用,应用可以即时扩展到数百万用户。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由作者提供

在黑客马拉松中,我们学习了如何使用 Streamlit 指南 这里 快速部署 Gen-AI 应用,这些指南是由我们的一位 AWS 教练编写的。

这使我们能够快速为我们的文本到文本模型创建一个用户友好的移动前端。

应用用户可以安全地上传公司文件,如政策文件,这些文件随后会调整底层 GenAI 模型,使其能够立即模拟经验丰富的公司员工的专业知识。

就像那些从 The Matrix 中瞬间下载的内容一样。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由作者提供

这种调整 Gen-AI 模型的方法被称为 RAG 方法,即…

  1. 获取公司的企业知识库;

  2. 增强基础模型与这些公司特定的数据;

  3. 生成现在为公司量身定制的人类般的响应。

RAG 通过公司自己的数据增强基础模型的训练,从而获得最新和最可靠的信息。

企业拥抱 RAG,因为它目前在解决 AI 幻觉 方面排名前列,这对必须确保 AI 向重要客户和员工提供可靠信息的大公司来说可能会带来重大挑战。

亚马逊 Bedrock 上的 Guru Chatbot

AWS 团队还让我们访问了 Guru,这是一个集成到亚马逊 Bedrock 中的 ChatGPT 风格的机器人。

Guru 具有处理上传文档的能力,通过 RAG 方法进行 AI 调优,成为公司的专家,并根据上传的文档回应用户查询。

例如,我上传了我们公司详细的 2022 年年度股东大会报告,这是一份厚达 308 页的文件,我绝对没有时间阅读 或者甚至适当地浏览。相反,我把它交给了 Guru,它轻松地消化了报告,并迅速为我提供了一个简明的总结。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:作者

亚马逊 Bedrock 的优势在于其管理方式,通过 API 便捷地访问预训练的基础模型。

选择“我希望哪个 AI”来总结我上传的文档非常简单。

我尝试了所有的模型:亚马逊 Titan、Meta 的 Llama 以及其他的,看到它们的反应非常有趣。

例如,‘更大的’模型听起来更好——更“像人类”——但延迟较高。确实,选择使用哪个 AI 模型并不总是倾向于选择最大和最强大的大语言模型;一个调优良好的、小型模型,能够提供亚秒级响应,往往更为合适。

在模型复杂性和用户体验之间存在微妙的平衡。

简而言之,Guru 是将公司数据与 GenAI 聊天机器人集成的最简单方法。不需要 Jupyter Notebooks,也无需构建或训练模型,一切——包括应用程序前端——都已部署并准备好使用。

结果

那么我们这些谦虚的黑客马拉松参与者表现如何呢?

在那 48 小时内,我们构建了什么,并向我们专注的首席技术官展示了?

我们四人团队模拟了一个可以发送 个性化营销优惠 的 AI。

在数字时代,大规模提供超个性化的产品和服务是数据驱动型组织的 圣杯。这需要大量的数据,提供“360 度视角”的客户信息,以及一个足够强大的数据和高级分析平台,以实时处理和行动这些见解。

我们利用亚马逊的 SageMaker JumpStart 预训练基础模型,将一些虚拟客户见解转化为个性化邮件,其中还包括专门为每位客户量身定制的 AI 生成的图片。目标是建立情感联系,推动销售,将体验从平凡普通提升到独特非凡。

忘掉那些千篇一律的“用我们来再融资你的抵押贷款!”广告,广告中有典型的白色栅栏、狗和 2.4 个孩子。在这里,我们希望传达一个能打动客户独特情况的提案,配有 AI 生成的完美房子。

等等,这不是有点让人毛骨悚然吗?

另外,如果我们获得的见解错误,最终冒犯了客户怎么办?

确实,事情可能会很糟糕。我们的 AI 会足够准确、公平和安全吗?我们拥有可信的 AI 吗?

然后就是隐私、安全和关于 GenAI真正提供价值的考虑。

这就是为什么我们将虚拟客户见解输入到我们的 AI 中,而不是 实际生产客户数据

我们绝对不允许将敏感客户数据在生产环境中发送给公共云服务提供商,除非有一系列的安全控制。因此使用了虚拟数据。

我们很快意识到,GenAI 在我们的用例中的价值并不在于生成客户见解。那是我们的高级分析平台最擅长的——数据处理。GenAI 的超级能力在于以人性化的方式传达这些见解。

最终的话

生成性 AI 是新的,数据驱动的组织极其渴望将这一颠覆性技术融入他们的业务中,不知怎么的

黑客马拉松是公司一举多得的好方法:

  • 挖掘最佳用例;

  • 评估合适的云服务供应商;

  • 识别创新员工。

我在亚马逊 GenAI 技术栈上的经验教会我,Gen-AI 时代的创新全在于迅速组装合适的现成 AI 工具以创造价值

我们获得了实际操作经验,辨别 GenAI 在可信的方式下真正增加价值的地方,以及如何在保持安全性和合规性的同时无缝集成这项技术到公司基础设施中——这些考虑是采纳的成败之关键。

各个参与者都在 Amazon BedrockSageMaker 中快速拼凑出一些粗糙的东西,尽快向客户展示,并进行迭代。

他们说,快速失败并从失败中前进。

猜猜怎么着:我即将参加一个微软黑客马拉松,我一定会及时更新进展。真的很期待玩玩Azure OpenAI

Twitter和 YouTube 这里这里这里找到我。

我受欢迎的 AI、ML 和数据科学文章

  • AI 与机器学习:快速入门 — 这里

  • 机器学习与机制建模 — 这里

  • 数据科学:现代数据科学家的新技能 — 这里

  • 生成性人工智能:大公司如何争相采用 — 这里

  • ChatGPT 与 GPT-4:OpenAI 如何赢得自然语言理解的战争 — 这里

  • 生成性 AI 艺术:DALL-E、Midjourney 和 Stable Diffusion 解析 — 这里

  • 超越 ChatGPT:寻找真正的智能机器 — 这里

  • 现代企业数据战略解析 — 这里

  • 从数据仓库和数据湖到数据网格 — 这里

  • 从数据湖到数据网格:最新架构指南 — 这里

  • Azure Synapse Analytics 实战:7 个用例解析 — 这里

  • 云计算基础知识:如何利用云服务助力您的业务 — 这里

  • 数据仓库与数据建模 — 快速速成课程 — 这里

  • 数据产品:为分析构建坚实基础 — 这里

  • 数据民主化:5 种‘数据普及’战略 — 这里

  • 数据治理:分析师常见的 5 个痛点 — 这里

  • 数据讲故事的力量 — 卖故事,而非数据 — 这里

  • 数据分析入门:谷歌方法 — 这里

  • Power BI — 从数据建模到令人惊艳的报告 — 这里

  • 回归分析:使用 Python 预测房价 — 这里

  • 分类:使用 Python 预测员工流失 — 这里

  • Python Jupyter Notebooks 与 Dataiku DSS —— 在这里

  • 受欢迎的机器学习性能指标解释 —— 在这里

  • 在 AWS 上构建 GenAI —— 我的第一次体验 —— 在这里

  • COVID-19 的数学建模与机器学习 —— 在这里

  • 工作的未来:在 AI 时代你的职业安全吗 —— 在这里

在 Python 中构建互动数据可视化:Plotly 入门

原文:towardsdatascience.com/building-interactive-data-visualizations-in-python-an-introduction-to-plotly-3ffdd920fc63

探索用于数据分析和机器学习的互动可视化的强大功能

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Federico Trotta

·发布在Towards Data Science ·阅读时间 6 分钟·2023 年 7 月 19 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由格尔德·阿特曼提供,来源于Pixabay

数据可视化是数据专业人员最重要的任务之一。它实际上帮助我们理解数据,并提出更多问题以便进一步调查。

但是数据可视化不仅仅是我们在探索性数据分析阶段必须完成的任务。我们还可能需要展示数据,通常是向观众展示,以帮助他们得出结论。

在 Python 中,我们通常使用matplotlibseaborn作为绘图库。

然而,有时我们可能需要一些互动的可视化。在某些情况下,为了更好地理解数据;在其他情况下,为了更好地展示我们的解决方案。

在这篇文章中,我们将讨论plotly,这是一个用于创建互动可视化的 Python 库。

什么是 Plotly?

正如我们可以在他们的网站上阅读到的:

Plotly 的 Python 绘图库可以创建互动的、出版质量的图表。包括如何制作折线图、散点图、面积图、条形图、误差条、箱线图、直方图、热图、子图、多轴图、极坐标图和气泡图的示例。

Plotly.py 是免费且开源的,你可以查看源代码、报告问题或在 GitHub 上贡献

所以,Plotly 是一个免费的开源 Python 库,用于制作互动可视化。

正如我们在他们的网站上看到的,它为我们提供了创建不同领域图表的可能性:AI/ML、统计、科学、金融等。

由于我们对机器学习和数据科学感兴趣,我们将展示一些与这些领域相关的图表以及如何在 Python 中创建它们。

最后,要安装它,我们需要输入:

$ pip install plotly

1. 互动气泡图

Plotly 的一个有趣且有用的功能是可以创建互动气泡图。

在气泡图的情况下,气泡有时会相交,使数据难以读取。如果图表是互动的,我们就可以更轻松地读取数据。

让我们看一个例子:

import plotly.express as px
import pandas as pd
import numpy as np

# Generate random data
np.random.seed(42)
n = 50
x = np.random.rand(n)
y = np.random.rand(n)
z = np.random.rand(n) * 100  # Third variable for bubble size

# Create a DataFrame
data = pd.DataFrame({'X': x, 'Y': y, 'Z': z})

# Create the scatter plot with bubble size with Plotly
fig = px.scatter(data, x='X', y='Y', size='Z',
      title='Interactive Scatter Plot with Bubble Plot')

# Add labels to the bubbles
fig.update_traces(textposition='top center', textfont=dict(size=11))

# Update layout properties
fig.update_layout(
    xaxis_title='X-axis',
    yaxis_title='Y-axis',
    showlegend=False
)

# Display the interactive plot
fig.show()

然后我们得到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们编写了互动气泡图。图片由Federico Trotta提供。

因此,我们使用NumPy创建了一些数据,并将它们存储在一个Pandas数据框中。然后,我们通过方法px.scatter()创建了互动图,方法从数据框中检索数据,并指定标题(与Matplotlib不同,后者将标题插入图表创建方法之外)。

2. 互动相关矩阵

我有时面临的任务之一是正确可视化相关矩阵。事实上,当我们有大量数据时,这些矩阵有时难以阅读和可视化。

解决问题的一种方法是使用 Plotly 创建互动可视化。

为了这个目的,让我们创建一个具有 10 列的 Pandas 数据框,并使用 Plotly 创建一个互动相关矩阵:

import pandas as pd
import numpy as np
import plotly.figure_factory as ff

# Create random data
np.random.seed(42)
data = np.random.rand(100, 10)

# Create DataFrame
columns = ['Column' + str(i+1) for i in range(10)]
df = pd.DataFrame(data, columns=columns)

# Round values to 2 decimals
correlation_matrix = df.corr().round(2)  

# Create interactive correlation matrix with Plotly
figure = ff.create_annotated_heatmap(
    z=correlation_matrix.values,
    x=list(correlation_matrix.columns),
    y=list(correlation_matrix.index),
    colorscale='Viridis',
    showscale=True
)

# Set axis labels
figure.update_layout(
    title='Correlation Matrix',
    xaxis=dict(title='Columns'),
    yaxis=dict(title='Columns')
)

# Display the interactive correlation matrix
figure.show()

然后我们得到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们创建的互动相关矩阵。图片由Federico Trotta提供。

因此,我们可以通过方法ff.create_annotated_map()以非常简单的方式创建互动相关矩阵。

3. 互动 ML 图表

在机器学习中,我们有时需要图形化地比较数量。在这些情况下,有时阅读我们的图表会很困难。

典型的情况是 ROC/AUC 曲线,其中我们比较不同 ML 模型的性能。事实上,有时这些曲线会相交,我们无法正确地可视化它们。

为了改进我们的可视化,我们可以使用 Plotly 创建一个 ROC/AUC 曲线,如下所示:

import pandas as pd
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.metrics import roc_curve, auc
import plotly.graph_objects as go

# Create synthetic binary classification data
X, y = make_classification(n_samples=1000, n_features=20, random_state=42)

# Scale the data using StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Split the data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)

# Initialize the models
knn = KNeighborsClassifier()
rf = RandomForestClassifier()
dt = DecisionTreeClassifier()
svm = SVC(probability=True)

# Fit the models on the train set
knn.fit(X_train, y_train)
rf.fit(X_train, y_train)
dt.fit(X_train, y_train)
svm.fit(X_train, y_train)

# Predict probabilities on the test set
knn_probs = knn.predict_proba(X_test)[:, 1]
rf_probs = rf.predict_proba(X_test)[:, 1]
dt_probs = dt.predict_proba(X_test)[:, 1]
svm_probs = svm.predict_proba(X_test)[:, 1]

# Calculate the false positive rate (FPR) and true positive rate (TPR) for ROC curve
knn_fpr, knn_tpr, _ = roc_curve(y_test, knn_probs)
rf_fpr, rf_tpr, _ = roc_curve(y_test, rf_probs)
dt_fpr, dt_tpr, _ = roc_curve(y_test, dt_probs)
svm_fpr, svm_tpr, _ = roc_curve(y_test, svm_probs)

# Calculate the AUC (Area Under the Curve) for ROC curve
knn_auc = auc(knn_fpr, knn_tpr)
rf_auc = auc(rf_fpr, rf_tpr)
dt_auc = auc(dt_fpr, dt_tpr)
svm_auc = auc(svm_fpr, svm_tpr)

# Create an interactive AUC/ROC curve using Plotly
fig = go.Figure()
fig.add_trace(go.Scatter(x=knn_fpr, y=knn_tpr, name='KNN (AUC = {:.2f})'.format(knn_auc)))
fig.add_trace(go.Scatter(x=rf_fpr, y=rf_tpr, name='Random Forest (AUC = {:.2f})'.format(rf_auc)))
fig.add_trace(go.Scatter(x=dt_fpr, y=dt_tpr, name='Decision Tree (AUC = {:.2f})'.format(dt_auc)))
fig.add_trace(go.Scatter(x=svm_fpr, y=svm_tpr, name='SVM (AUC = {:.2f})'.format(svm_auc)))
fig.update_layout(title='AUC/ROC Curve',
                  xaxis=dict(title='False Positive Rate'),
                  yaxis=dict(title='True Positive Rate'),
                  legend=dict(x=0.7, y=0.2))
# Show plot
fig.show()

然后我们得到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们创建的 AUC/ROC 曲线。图片由Federico Trotta提供。

因此,我们通过方法add.trace(go.Scatter)为我们使用的每个 ML 模型(KNN、SVM、决策树和随机森林)创建了散点图。

这样,可以轻松显示曲线自交点的详细信息和区域值。

结论

在这篇文章中,我们简要介绍了 Plotly,并展示了如何利用它在数据分析和机器学习中实现更好的可视化。

正如我们所看到的,这是一个低代码库,确实帮助我们更好地可视化数据,从而改善结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Federico Trotta

嗨,我是费德里科·特罗塔,一名自由职业技术写作人员。

想和我合作吗?联系我

使用 Python 构建互动数据可视化 — 叙述的艺术

原文:towardsdatascience.com/building-interactive-data-visualizations-with-python-the-art-of-storytelling-ceb43db67488

简介指南

Seaborn、Bokeh、Plotly 和 Dash,有效地传达数据洞察

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Pol Marin

·发表于 Towards Data Science ·阅读时间 16 分钟·2023 年 6 月 4 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Nong 提供,来源于 Unsplash

掌握叙述的艺术对数据科学家很重要,但对数据分析师尤为关键。

与那些不熟悉数据的人分享数据洞察和亮点,特别是那些可能没有技术背景的人,是数据分析过程中的重要部分。

如果他们不了解你所说的内容或没有被吸引,他们不在乎你是否在数据清洗和转换方面表现最好。

因此,可视化是最终叙述的一部分,它可以说是展示任何事实的最佳方式——这就是为什么每个人都在使用它们。

此外,像 Tableau 或 Power BI 这样的工具因其能够轻松创建互动仪表板而日益受到欢迎。商业人士通常会被带有图表和颜色的酷炫仪表板所震撼,因此他们已开始将其作为招聘要求。

今天,我将与大家分享一些 Python 选项,用于为那些不能或不喜欢/不想使用上述特定数据可视化工具的人创建互动式可视化。

在完成所有分析之后,你将不再需要放弃 Python——你还可以用它来以视觉形式分享你的见解。

更具体地说,我将讨论四个库:Seaborn、Bokeh、Plotly 和 Dash。

因为深入探讨所有这些内容可能会导致帖子极其冗长,所以我会尽量简洁,并重点展示它们能做些什么。在未来的帖子中,我可能会更详细地介绍其中一些(或所有),但会逐一进行。

今天的目录:

  1. 首先是数据

  2. Seaborn

  3. Bokeh

  4. Plotly

  5. Dash

  6. 讲述故事

你可以在这篇文章末尾的 资源 部分找到整个代码的链接。

那么我们开始吧。

1. 首先是数据

如果我们没有故事可讲,那我们就无法讲故事,对吧?

由于我喜欢营养和健康领域,我将使用来自世界卫生组织 (WHO) 的公开数据。具体来说是 成人超重的流行率,BMI >= 25(年龄标准化估计)(%)[1]。

我们的目标是分析数据并查看我们能发现什么。它包含了从 1975 年到 2016 年按性别、国家和年份划分的流行率值。

免责声明:结果不会让你惊讶,因为这是一个广泛讨论的话题。但我们今天不是来发现新东西的。

我将使用笔记本来简化操作,但请记住,我们即将创建的图表也可以嵌入到网站中——这取决于我们使用的库——这才是真正的好处所在。

我稍微清理了一下数据,使其简单并为接下来的步骤做好准备。这里是代码片段,以便你可以按照相同的步骤操作:

import pandas as pd
# Load the data
df = pd.read_csv("data.csv")
# Remove missing data and keep only useful columns
df = df.loc[
    df['Value'] != "No data", 
    ['ParentLocation', 'Location', 'Period', 'Dim1', 'Value']
]
# Rename columns
df.columns = ['ParentLocation', 'Location', 'Period', 'Gender', 'Prevalence']
# Some values in Prevalence contain ranges (like '13.4 [8.7 – 18.9]'), so we just keep the average
df['Prevalence'] = df['Prevalence'].apply(
    lambda x: float(x.split('[')[0]) if '' in x else x
)

这就是它的样子:

![

前 5 行 DataFrame — 作者提供的图片

我们准备开始绘图了。

2. Seaborn

我相信你已经熟悉 Seaborn 了。它是与 Matplotlib 一起用于绘图的最常用库之一。你可能不知道的是,我们也可以用它来做动态可视化,而不仅仅是静态的。

但让我介绍一下,以防有人不熟悉。

用他们自己的话来说:“Seaborn 是一个基于 matplotlib 的 Python 数据可视化库。它提供了一个高级接口,用于绘制吸引人且信息丰富的统计图表。”[2]

所以这就像是 Matplotlib 的改进版。

我将先使用一个简单的 relplot() 来绘制不同变量之间的关系:

import seaborn as sns

# Apply the style
sns.set_style("ticks")
sns.despine()

# Prepare df for this graph
grouped_df = df.groupby(
    ['ParentLocation', 'Gender', 'Period']
)['Prevalence'].mean().reset_index()

# Create visualization
sns.relplot(
    data=grouped_df[
        grouped_df['Gender'] != 'Both sexes'
    ],
    kind="line",
    x="Period", 
    y="Prevalence", 
    col="Gender",
    hue="ParentLocation", 
    palette="Paired"
)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

流行率-时间-性别关系 — 作者提供的图片

这里的可视化本身并不是重点。我们将在这篇文章的最后一部分通过模拟讲故事来提取一些见解。

我们得到的是静态图表。如果不通过更改代码,我们无法改变任何内容,这使得图表是静态的。如果我们想在 ParentLocation 基础上进行比较,那是可以的,但我们无法在国家级别上进行详细分析。这样做会导致同一张令人困惑的图表中出现大量线条。

这不是我们来这里的目的。

但 Seaborn 不允许我们实现交互式仪表板……不过,我们可以使用 ipywidgets 来提高互动性。

如果下一段代码在你的系统上不起作用,那是因为你需要通过在终端中运行以下命令启用小部件扩展 jupyter nbextension enable --py --sys-prefix widgetsnbextension

现在我们将绘制相同的 relplot(),但这次我们希望以可交互的方式比较国家。以下是如何操作:

import ipywidgets as widgets

countries = sorted(pd.unique(df['Location']))

# Creates Dropdown Widget
def create_dd(desc, i=0):
    dd = widgets.Dropdown(
        options=countries, 
        value=countries[i], 
        description=desc
    )
    return dd

# Creates the relplot
def draw_relplot(country1, country2):
    sns.relplot(
        data=df[
            (df['Location'].isin([country1, country2]))
            & (df['Gender'] != 'Both sexes')
        ],
        kind="line",
        x="Period", 
        y="Prevalence", 
        col="Gender",
        hue="Location", 
        palette="Paired"
    )

# Generate the final widget
dd1 = create_dd('Country 1', 0)
dd2 = create_dd('Country 2', 1)
ui = widgets.HBox([dd1, dd2])

# Create the interactive plot and display
out = widgets.interactive_output(
    draw_relplot, 
    {'country1': dd1, 'country2': dd2}
)

display(ui, out)

这次较长。但我们所做的只是将一些代码包装到两个函数中 — 一个用于创建下拉菜单,另一个用于创建图表本身 — 然后通过使用 HBox 小部件来创建 UI,它是一个包装器,将传递的小部件分组。然后我们使用 interactive_output 函数来创建输出并显示所有内容。

结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

按性别的国家比较 — 图片由作者提供

我们在这里添加了一些互动性和动态性!我们已经可以一次比较两个国家,并查看它们的超重患病率差异。

情况变得更好了。

然而,这种方法有一个限制。图表不可交互,我们只是动态地显示数据。这就是我们想要的吗?也许在某些时候可以,但今天不行。

介绍我们的下一个新朋友……

3. Bokeh

使用官方声明:

“只需几行 Python 代码,Bokeh 就能让你创建在 web 浏览器中显示的交互式 JavaScript 驱动的可视化。”[3]

Bokeh 是一个更加复杂且完整的交互图形和仪表板选项。我们实际上可以用 Bokeh 创建类似 Tableau 的仪表板。

缺点是这不是最用户友好的选项。即使安装对于初学者在 Jupyter Notebook 中使用也可能相当痛苦。

默认情况下,我们创建的图表和仪表板会呈现在 HTML 页面或文件中,但我们可以通过导入并调用 output_notebook() 函数将它们内联渲染在笔记本中。

让我们尝试用 Bokeh 复制我们用 Seaborn 创建的第一个图表,生成一个可交互的图形:

from bokeh.plotting import figure
from bokeh.io import show, output_notebook
from bokeh.layouts import row
from bokeh.palettes import Paired

def prepare_figure(gender):
    l = figure(
        title=f"Gender = {gender}", 
        x_axis_label='Period', 
        y_axis_label='Prevalence',
        width=475, 
        outer_width=475,
        height=500,
    )
    for i, loc in enumerate(pd.unique(grouped_df['ParentLocation'])):
        l.line(
            grouped_df[
                (grouped_df['Gender'] == gender) 
                & (grouped_df['ParentLocation']==loc)
            ]['Period'], 
            grouped_df[
                (grouped_df['Gender'] == gender) 
                & (grouped_df['ParentLocation']==loc)
            ]['Prevalence'], 
            legend_label=loc, 
            line_width=2,
            color=Paired[12][i]
        )

    l.legend.location = 'top_left'
    l.legend.click_policy="mute"
    l.legend.label_text_font_size='8px'
    l.legend.background_fill_alpha = 0.4

    return l

# Render the figure inline
output_notebook()

# Create the first figure and input the data
l1=prepare_figure('Male')

# Create the second figure and input the data
l2=prepare_figure('Female')

p = row(l1, l2)

# Show the plot
show(p)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

按性别比较欧洲超重患病率,从 2000 年到 2016 年 — 图片由作者提供

太棒了!我们的第一个交互式数据可视化。但这仍然远未达到仪表板的概念。

此外,我们目前还没有选择显示数据的能力。我们只能显示/隐藏一些线条,并在图形中移动和放大/缩小。很酷,但我们需要更多。

尽管 Bokeh 内置了自己的小部件 — 我们可以创建下拉菜单等 — 我还是先到此为止。

Bokeh 绝对不适合初学者,我不想让任何人感到困惑。在我看来,与接下来的两个选项相比,它是一个较差的替代方案。

就像浪费时间和金钱讨论大米对你的好处一样,而实际上你可以专注于更健康的选择,如糙米,甚至其他蔬菜,如西兰花。

现在进入重点部分。

4. Plotly

再次引用 Plotly 的文档

Plotly Express 是 plotly 库的内置部分,是创建大多数常见图形的推荐起点。[…] 这些函数的 API 经过精心设计,尽可能一致且易于学习,使得在数据探索会话中,从散点图切换到条形图、直方图或旭日图都变得容易。[4]

Plotly 是一个令人惊叹的工具,我一再使用它,我可以保证它正如他们所说的那样:易于学习,且从一个图表切换到另一个图表非常简单。

然而,我们今天不会多谈 Plotly Express,因为我们要深入一点。我们将使用 Plotly 的图形对象来完成任务。

我们将直接进入允许我们创建国家比较图表的代码,并包含两个下拉框:

from plotly.subplots import make_subplots
import plotly.graph_objects as go

# Define variables
colors = ['#a6cee3', '#1f78b4']

# Define widgets (using previous function)
dd1 = create_dd('Country 1', 0)
dd2 = create_dd('Country 2', 1)

# Create figure and traces
def create_figure(country1, countr
    fig = make_subplots(
        shared_xaxes=True, 
        shared_yaxes=True, 
        rows=1, 
        cols=2,
        vertical_spacing = 0,
        subplot_titles=("Gender = Female", "Gender = Male"),

    )

    for j, gender in enumerate(['Female', 'Male']):
        for i, loc in enumerate([country1, country2]):
            fig.add_trace(
                go.Scatter(
                    x=df[
                        (df['Gender'] == gender) 
                        & (df['Location'] == loc)
                    ]['Period'], 
                    y=df[
                        (df['Gender'] == gender) 
                        & (df['Location'] == loc)
                    ]['Prevalence'], 
                    name=loc, 
                    line=go.scatter.Line(color=colors[i]), 
                    hovertemplate=None,
                    showlegend=False if j==0 else True
                ), 
                row=1, 
                col=j+1 
            )

    # Prettify
    fig.update_xaxes(showspikes=True, spikemode="across")
    fig.update_layout(
        hovermode="x",
        template='simple_white'
    )

    return fig

fig = create_figure(countries[0], countries[1])

# Create the Figure Widget
g = go.FigureWidget(
    data = fig,
    layout=go.Layout(
        barmode='overlay'
    )
)

# Handle what to do when the DD value changes
def response(change):
    dfs = []
    for gender in ['Female', 'Male']:
        for loc in [dd1.value, dd2.value]:
            dfs.append(
                df[(df['Gender'] == gender)
                  & (df['Location'] == loc)]
            )

    x = [temp_df['Period'] for temp_df in dfs]
    y = [temp_df['Prevalence'] for temp_df in dfs]

    with g.batch_update():
        for i in range(len(g.data)):
            g.data[i].x = x[i]
            g.data[i].y = y[i]
            g.data[i].name = dd1.value if i%2 == 0 else dd2.value

        g.layout.barmode = 'overlay'
        g.layout.xaxis.title = 'Period'
        g.layout.yaxis.title = 'Prevalence'

dd1.observe(response, names="value")
dd2.observe(response, names="value")

container = widgets.HBox([dd1, dd2])
widgets.VBox([container, g])

这可能看起来很长,但主要是因为我已经将其格式化为无需水平滚动的形式。如果你仔细观察,你会发现我们实际上做的事情很少!

我们在重用之前片段的部分,例如 create_dd() 函数。是的,我们再次使用 ipywidgets,但它们与 Plotly 的集成非常顺畅。所有操作都通过他们的 observe() 方法和我们编写的 response() 函数来处理,每当任一下拉框的值发生变化时,都会执行这个函数。

这是视觉效果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 plotly 进行数据交互 — 作者提供的图片

这很简单!同样,使用 Bokeh 实现相同的效果是可能的,但在 Plotly 中学习如何做要快得多。无论哪种方式,结果都很棒。

进入我们的最后一个工具……

Dash

Dash 本身不是一个绘图库。它是一个用于生成仪表盘的出色框架。最近,它在构建互动式基于 Web 的仪表盘方面的受欢迎程度不断上升。

剧透:Dash 是由 Plotly 的开发者创建的。所以这里我们仍然会使用 Plotly,但现在与 Dash 框架结合起来,看看我们可以用它们创建什么。

那么,我们如何使用 Dash 构建相同的图表呢?与我之前所做的将整个代码放在这里不同,我想将其分成几个部分,以便更好地理解。

正如我在开始时所说的,我会写单独的帖子来深入探讨这些框架。所以如果你感兴趣,请考虑关注我。

我们将从导入开始。请注意,我们将重用已经见过的代码,所以我不会重新导入像 Plotly 这样的框架。

# Install dash and jupyter_dash
!pip install dash
!pip install jupyter-dash

# Import
import dash_core_components as dcc
from dash import html
from jupyter_dash import JupyterDash

请注意,我导入了 JupyterDash,这是因为我正在使用 Jupyter Notebook。如果你使用的是普通脚本,只需将最后一行替换为 from dash import Dash

接下来我们要做的是创建应用:

app = JupyterDash(__name__)

我希望你对 HTML 有所了解,因为我们现在要使用Dashhtml模块做一点这方面的工作。我计划创建一个简单的 3 列 1 行网格,列宽分别为 15%、60%和 25%。第一列将用于下拉框,第二列用于图表,第三列将为空。

第一列将由一个包含标题和另一个包含下拉框的 div 容器组成。以下代码实现了它:

 html.Div([ 
        html.H1('Countries'), 
        html.Div(
            [dcc.Dropdown(
                id='country1_dropdown',
                options=df['Location'].unique().tolist(),
                value='',
                placeholder='Select a country'
            ),
             dcc.Dropdown(
                 id='country2_dropdown',
                 options=df['Location'].unique().tolist(),
                 value='',
                 placeholder='Select a country'
            )]
        )
    ]
)

一开始可能会显得有些混乱,但其实非常简单。每个 HTML 元素都有其子元素在列表参数中(在这种情况下,<div>包裹着一个<h1>和另一个<div>,而两个下拉框在内层的<div>中)。

现在让我们创建图表代码:

html.Div([
    dcc.Graph(
        id='chart'
    )
])

这个比较简单。它只是一个包裹着 ID 为 chart 的图表的<div>容器。

然而,如果没有应用样式,仪表盘是不可接受的。我们怎么给这些元素添加一些 CSS 呢?

很简单,每个元素都有一个可选的style参数,我们可以用来添加我们的 CSS。下面是最后一段代码如何加上样式:

html.Div([
        dcc.Graph(
            id='chart', 
            style={}
        )
    ], style={
        'grid-column-start': 'second',
        'grid-column-end': 'third',
    }
)

我已经听到你说:“当然,我们知道如何创建这两个元素。但我们实际怎么创建布局呢?”很简单,只需将它们全部放入一个<div>容器中,并将其分配给app.layout

app.layout = html.Div([
    # Dropdown menu
    html.Div([ 
        html.H1('Countries'), 
        html.Div(
            [dcc.Dropdown(
                id='country1_dropdown',
                options=df['Location'].unique().tolist(),
                value='',
                placeholder='Select a country',
                style={'margin-bottom': '10px', 'max-width': '200px'}
            ),
             dcc.Dropdown(
                 id='country2_dropdown',
                 options=df['Location'].unique().tolist(),
                 value='',
                 placeholder='Select a country',
                 style={'max-width': '200px'}
            )]
        )
    ], style={
        'grid-column-start' : 'first',
        'grid-column-end' : 'second',
        'padding': '2%',
        'justify-self': 'center'
    }),

    # Plot
    html.Div([
        dcc.Graph(
            id='chart', 
            style={}
        )
    ], style={
        'grid-column-start' : 'second',
        'grid-column-end' : 'third',
    })
], style={
    'width': '100vw'
    'display': 'inline-grid',
    'grid-template-columns': '[first] 15% [second] 60% [third] 25%',
    'grid-template-rows': '[row] 100%',
    'grid-gap': '1rem',
    'align-items': 'right',
})

如果你尝试运行这个,你会看到空图表。我们可以通过回调向它们添加一些数据。这些回调有输入和输出依赖关系,用于更新显示的数据,并达到我们期望的交互效果。

你接下来会看到一个叫做update_chart()的函数,它基本上是我们在 Plotly 部分中使用的create_chart()函数的复制粘贴。

colors = ['#a6cee3', '#1f78b4'] 

@app.callback(
    dash.dependencies.Output('chart', 'figure'),
    dash.dependencies.Input('country1_dropdown', 'value'),
    dash.dependencies.Input('country2_dropdown', 'value')
)
def update_chart(country1, country2):   

    # Create figure and traces
    fig = make_subplots(
        shared_xaxes=True, 
        shared_yaxes=True, 
        rows=1, 
        cols=2,
        vertical_spacing = 0,
        subplot_titles=("Gender = Female", "Gender = Male"),

    )

    for j, gender in enumerate(['Female', 'Male']):
        for i, loc in enumerate([country1, country2]):
            fig.add_trace(
                go.Scatter(
                    x=df[
                        (df['Gender'] == gender) 
                        & (df['Location'] == loc)
                    ]['Period'], 
                    y=df[
                        (df['Gender'] == gender) 
                        & (df['Location'] == loc)
                    ]['Prevalence'], 
                    name=loc, 
                    line=go.scatter.Line(color=colors[i]), 
                    hovertemplate=None,
                    showlegend=False if j==0 else True
                ), 
                row=1, 
                col=j+1 
            )

    # Prettify
    fig.update_xaxes(showspikes=True, spikemode="across")
    fig.update_layout(
        hovermode="x",
        template='simple_white',
    )

    return fig

所以这里的新内容是函数声明上方的部分。我们说回调依赖于两个输入——country1 和 country2——输出是一个id='chart'的图表。

最后,我们运行应用:

# Run app
if __name__ == '__main__':
    app.run_server()

然后导航到http://127.0.0.1:8050或服务器运行的任何 IP,像我这里一样玩一玩:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Dash 和 Plotly 与数据交互 — 图片由作者提供

看看它是多么流畅。我们可以结合 HTML 和 Python 图表的力量,创建漂亮的仪表盘,这都要归功于 Dash。

6. 讲述故事

这一切都是比较基础的。我们没有建立一个仪表盘,也没有讲述一个故事。我们只是看了一些让图表互动的工具。

在这一部分,我将分享一个示例仪表盘,任何人都可以用来向利益相关者讲述故事。它可能不是最漂亮或最完整的,但足以展示 Dash 的能力,并说服你开始使用它来进行未来的可视化。

免责声明:请爱上 Dash 和 Plotly 提供的实用功能,而不是我实现的设计。我的目标是展示 Dash 的能力,而不是试图说服你超重是全球真正的问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我为这个项目构建的简单仪表盘 — 图片由作者提供

现在来探讨一些见解:

一般基础

许多科学家普遍认为肥胖是一种流行病。它是否符合流行病的实际定义并不重要。重要的是它的流行率以及它对我们健康的负面影响。

超重是肥胖之前的阶段。因此,超重也很重要,因为如果不加以控制,可能会导致未来的肥胖。

如果我们检查 1975 年的世界地图与 2016 年的世界地图,可以清楚地看到后者的颜色更加鲜艳。这并不好,因为颜色越黄,超重流行率越高。所以,整体而言,全球的超重流行率有了很大增加。

全球化、技术和科学的进步以及许多其他因素使大多数国家的食品变得丰富。或者至少,我们可以猜测食品获取在总体上有所增加。这与日益增加的久坐生活方式以及过度消费不健康食品结合起来,导致了超重流行率的巨大增加。

哦,随着超重流行率的增加,肥胖率也随之增加。

顶级国家

我们看到瑙鲁或帕劳等国家的流行率极高(~88.7%)。我们尚未确认数据,但一些报告确实坚持解决这些国家的肥胖问题(而且,数据应来自世界卫生组织,应该是可靠的)。

现在聚焦于瑙鲁,“从 1980 年代开始,瑙鲁人过上了久坐的生活方式,饮食不健康,导致了太平洋地区最糟糕的健康状况”。[5]

此外,“瑙鲁约 90%的土地面积覆盖着磷酸盐矿床,大部分已被条带开采且不可耕种。这导致瑙鲁人依赖从澳大利亚和新西兰等大洋洲国家进口的含糖量和脂肪含量都很高的加工食品” [6]

我们可以说瑙鲁是一个异常值,但有几个国家处于类似的位置。至少可以说,这令人担忧。

时间差异

一个国家的流行病率是否增加了很多可能取决于几个因素,所有因素都应独立对待。

然而,我对新加坡感到着迷。它们在 41 年里几乎没有变化。几个西欧国家也在增幅最小的榜单上。我相信,如果我们寻找的话,可以提取出一个模式。

在另一端,博茨瓦纳在 1975 年和 2016 年之间的流行率大幅上升。然而,在 2016 年,它们仍然远未达到顶端。这可能是因为近年来它们的食品获取增加,但博茨瓦纳仍然是博茨瓦纳。

性别

在大多数国家,男性历史上通常比女性更容易超重。我们通常看到男性的超重情况比女性更多。

我们可以在这里尝试提出不同的假设,但有一点是不可否认的:女性在历史上比男性更关心自己的外貌,可能是由于社会压力。我们生活在一个性别歧视的世界中,这只是其中一些后果。

最近我们才看到像巴西这样的国家,女性的超重率已经超过了男性。这并不是值得高兴的事,但我们可以把它看作是一种女性主义行为吗?可能不能。

但如果我们进行概括,模式仍然很清晰。

最终结论

残酷的事实是,超重的普遍性——因此,肥胖——随着时间的推移在增加。而且它影响每个人:所有性别,所有国家。没有一个是安全的。

这真的算是流行病吗?有人可能这么认为。

令人担忧的是,在大多数情况下,超重是由于不良习惯、营养不良、久坐生活方式和睡眠不好所致。加上整体热量摄入已经持续多年增加且似乎没有平稳下来。

所有这些都依赖于个人,但大多数人显然没有采取任何行动。

已经证明肥胖会导致死亡:它与心脏病、中风、糖尿病、癌症等相关……而我们依然放任其发展。

我们必须停止这种情况,但我们只能照顾好自己。所以务必做出明智的选择。

正如 Jerzy Gregorek 所说:

“艰难的选择,轻松的生活。轻松的选择,艰难的生活。”

结束

感谢你读完它!

这是我写过的最长的一篇文章,但我像孩子玩玩具一样享受了它。我希望你也一样。

本文的目的是分享我用来创建互动可视化的一些工具,并通过我创建的仪表板进行一种简短而非正式的讲述,以从我们看到的数据中提取一些见解和假设。

在资源部分,你将看到一个链接,检查用于此帖子的所有代码![7]

**Thanks for reading the post!** 

I really hope you enjoyed it and found it insightful.

Follow me and subscribe to my mailing list for more 
content like this one, it helps a lot!

**@polmarin**

如果你想进一步支持我,请考虑通过下面的链接订阅 Medium 的会员:这不会让你多花一分钱,但会帮助我完成这个过程。

[## 通过我的推荐链接加入 Medium - Pol Marin

阅读 Pol Marin 的每一篇故事(以及 Medium 上成千上万其他作者的故事)。你的会员费用直接支持 Pol……

medium.com

资源

[1] 成年人超重的流行率,BMI >= 25(年龄标准化估计)(%) — 世界卫生组织

[2] seaborn: 统计数据可视化

[3] Bokeh

[4] Python 中的 Plotly express — Plotly

[5] 西谷高明(2012 年 5 月 27 日)。“瑙鲁:一个被肥胖和糖尿病困扰的岛屿”朝日新闻

[6] “我在如此小的岛屿上见过太多的葬礼”:瑙鲁的惊人故事,这个世界上 2 型糖尿病发病率最高的小岛国

[7] 互动可视化仓库 — GitHub

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值