制胜自评指南
原文:
towardsdatascience.com/a-guide-to-writing-a-winning-self-appraisal-a9d4f5ee3d35
在你的软件工程师或数据科学家的绩效评估中应突出什么
·发布于 Towards Data Science ·7 分钟阅读·2023 年 2 月 21 日
–
作者提供的图像
自我评估是个人和职业发展的重要组成部分,提供了一个反思自己成就和改进领域的机会,承担自身成长的责任,并对自己的职业有更多掌控感。
自评在任何薪酬讨论中都是关键因素,帮助管理者确保员工因其对组织的贡献而获得适当的报酬。此外,通过评估过程展示个人的价值,能够展示其成就并增强获得认可的机会(加薪、奖金、晋升)。
随着评估时间的临近,一些我的受指导者询问如何写一份有说服力的自我评价。意识到这是一个常见问题,尤其是在初级 IT 专业人员中,我整理了这份简要指南,分享了我的观点。因此,软件工程师、数据科学家和应用程序开发人员需要了解的关于撰写全面且有影响力的自我评估的内容包括:展示他们的优异表现,同时也涉及成长领域,以确保评估全面且平衡。
超越基础
在这一部分中,我将讨论五个关键提示,帮助你撰写一份超越基础的稳固自评,平衡地突出你的优势。
➊ — 全年跟踪成就
在职业生涯初期,我曾陷入一个常见的陷阱,即没有彻底记录我一年来的成就,导致在二月份年终评估前急忙寻找记录。意识到我记不清楚所有的事情,不得不翻阅多个记录,包括 Jira 票据、拉取请求、电子邮件和设计文档,以编制我的评估报告,这让我感到沮丧。最终,我开始保持工作日志(即自夸文档由 Julia Evans 提供)。这个简单的习惯让我能够刷新记忆,减少对我贡献价值的猜测。
⭐️ Pro Tip:
将工作日志转化为自我评价时,保持内容简短而精炼。如果你在审查自我评价时感觉顶部需要一个“TL;DR”总结,这表明内容太长了——你可能需要考虑使用项目符号而不是写长篇大论。此外,识别出真正突出的成就——这些可能是你在日常工作中超越自我的实例(例如,周末工作以完成一个功能;在实施前评估多个选项等)。
➋ — 用可测量的指标备份贡献
Google X-Y-Z 公式常用于简历写作,也可以应用于撰写自我评估。它建议关注具体成就 X、可测量的指标 Y 和实现结果的方法 Z,通过使用它,开发者可以突出他们的成就,量化他们的影响,并用具体数据支持他们的贡献。
“按[X]衡量,通过做[Z]实现了[ Y]”
例如,一个全栈开发者可以这样表述:通过实施缓存模式和简化 HTML DOM(Z),将整体页面加载时间减少了 80%(X),这一点通过基准测试证明了(Y)。
这里值得注意的是,X-Y-Z 公式是一种数据驱动的方法,要求被评估者量化他们的成就(使用数字或统计数据)以提供具体的贡献证据。像代码行数、错误修复或服务水平协议等指标,都是展示工作成效的典型方式。
⭐️ Pro Tip:
X-Y-Z 公式非常“干燥”,未能为成就的背景增添“色彩”。为了让你的成就更为突出,尝试使用描述性形容词和相关关键词来强调其重要性(例如,不仅仅说:“交付了 ABC 功能”,而是可以加上:“尽管 ABC 功能的范围扩大了,但部署仍按承诺的冲刺目标和时间表进行”)。
➌ — 定期与管理层沟通
在我职业生涯的初期,我会定期与我的经理进行 1 对 1 会议,但我们的重点通常会被紧迫的工作事务所主导。因此,绩效反馈往往被忽视。为了解决这个问题,我学会了更有意识,并开始专门安排会议来讨论绩效相关的话题。因此,我能够收到关于目标进展的反馈,并识别改进的领域,避免任何意外在年终评审时出现。
此外,我有机会提出这三个重要问题:
-
到目前为止,我在实现我的目标和期望方面表现如何?
-
我可以访问任何额外的支持或资源来帮助我在角色中更有效吗?
-
我是否有机会在团队或组织内承担新的挑战或责任?
让我们暂停一下,思考最后这个问题:项目并不总是与评估周期的开始对齐;它们可以随时出现。因此,通过频繁提及这一点,我可以确保自己了解任何当前或即将到来的机会,并采取必要的步骤为这些机会做好准备。
⭐️ 专业提示:
将你的行动与你的经理成功所需的内容联系起来。他们是否向高级领导层撰写进展报告?— 向他们提供指标和额外的信息。他们是否在范围扩展的情况下谈判时间表/预算?— 提前通知他们,以便他们能保持项目在正轨上。
➍ — 考虑非技术能力
成功的 IT 专业人士明白,单靠技术技能不足以实现职业成长和晋升。我花了一段时间才认识到,通过展示一系列全面的能力,我可以为自己争取一个领导角色。根据你的组织,可能会有一些对你工作至关重要的人力资源能力,但通常这些包括沟通、团队合作、批判性思维、主动性、适应能力或领导力。务必将这些属性的应用直接与您所取得的结果联系起来。
⭐️ 专业提示:
包括你从直接经理之外的人那里收到的反馈。例如,从内部/外部客户那里收到的邮件摘录或来自同事的赞誉。
➎ — 认可不足之处
被评估者常常认为领导只对卓越的成功感兴趣,而忽视了影响其表现的挑战。相反,管理层希望看到的是如何克服这些挑战,或者如何应对这些挑战,或者从中学到了什么教训。认识到这些障碍对个人成长和发展至关重要,这种透明度和自我提升的承诺不会被忽视。我曾经说过这样的话:“我只实现了部分目标是因为{非常明确的原因},我现在正在使用{这种新方法}来克服这一点。”
⭐️ 专业提示:
不要害怕寻求建议或在需要时请求额外的培训。认识到自己并不懂得所有事情,总是有改进的空间。这也是一个找到导师的绝佳机会。
指标-指标-指标
到现在你可能会想知道技术人员包括哪些指标来支持他们的成就,如上述第 2 种策略所述,以及他们如何衡量这些指标。使用数字来量化贡献提供了一种基于不可争议事实总结表现的客观方式。以下是你可以在自我评估中包含的可衡量技能列表:
编码
‣ PR 数量
‣ 代码评审数量
‣ 技术文档数量
‣ 解决的技术债务票务数量
‣ 重构对性能/代码质量等的影响
‣ 测试覆盖率增加了 X%
‣ 创建的可重用框架/库数量
‣ 成功发布的数量(即发布后没有紧急发布)
可靠性
‣ SLA 提高了 X%
‣ TTD/TTM/TTR 平均时间
生产力
‣ 创建的工具用于自动化重复任务,减少 X% 的时间
‣ 提前完成的票务数量
‣ 加班小时数
成本节省
‣ 通过使用开源而非付费库节省的 $$$
‣ 通过使用云服务节省的 $$$
‣ 通过实施 DevOps 流水线节省的 $$$
人员
‣ 新入职人数
‣ 面试人数
‣ 指导的人员数量
‣ 在团队环境中对上述事项的影响(例如,更快的交付能力)
学习
‣ 完成的课程数量
‣ 因跟进技术而引入的新技术栈数量
其他 粘合工作
‣ 组织的 KT 会议数量
‣ 组织的利益相关者会议数量
‣ 实施的过程改进数量
‣ 作为跨职能团队之间的联络人,促进沟通和理解
‣ 主动解决问题的影响
‣ 参与的设计讨论数量
进行精心编写的绩效自我评估可以是最有效的内部营销活动之一。希望这些笔记对你有所帮助。
感谢阅读!
P.S.
你有没有试过多次点击点赞按钮看看会发生什么?❤
我在 Medium 上定期撰写关于领导力、技术和数据的文章 — 如果你想阅读我未来的帖子,请 ‘关注’我!
我的新书《技术智慧》现在已经上市了 — 点击查看!*
在数据科学领域建立职业资本的一个高度被低估的方法
原文:
towardsdatascience.com/a-highly-underrated-way-to-build-career-capital-in-data-science-231c4f2c654e
在线写作:开始很简单,非常有利于提高沟通技能,并且比传统的数据科学个人简历更具可持续性
·发表于Towards Data Science ·9 分钟阅读·2023 年 6 月 28 日
–
个人简历和 LinkedIn 个人资料在数据科学家的职业生涯中只能帮到你有限的程度。
当然 — LinkedIn 是建立职业网络的绝佳方式,而个人简历则提供了展示你所做的酷炫项目的好方法。但是,如果没有人点击你的简历链接,或者你不想成为LinkedIn 上的炒作达人,你的个人资料将会被搁置在那儿,积满数字灰尘,你也永远无法从中获得任何价值。
在这篇文章中,我想提倡另一种补充策略:在线写作。
直到 3 个月前,我从未为了乐趣而写过任何东西,我对写作的记忆大多来自学校和考试,都是负面的。因此,如果你觉得在线写作的想法很疯狂(或者超出了你的舒适区/兴趣区),我完全理解你的感受。但在实践了 3 个月后,我已经成为了它的坚定信徒,如果你给我 2 分钟时间,我将解释在线写作如何使数据科学家能够:
-
发展强大的个人品牌
-
与数据科学社区中的其他人建立有意义的联系
-
发展经常被忽视的讲故事和沟通技能
-
以招聘者友好的、易于分享的格式展示你的技能
然后,我将分享一些帮助我开始写作的经验,并给你一些关于如何入门的建议。
如果你喜欢这个故事,点击我的“关注”按钮对我来说意义重大 — 只有 1%的读者会这么做!感谢阅读。
在线写作的 4 个好处
1. 写作有助于你建立强大的专业品牌
在我大部分职业生涯中从事数据工作时,我采用了相当标准的专业品牌建立方式。
我创建了一个个人作品集,维护了一个 LinkedIn 个人资料,并偶尔发布一些重要的职业成就。
这种方法的问题在于,作品集和 LinkedIn 个人资料被藏在互联网的一个不起眼的角落。除非有人主动去找,否则没人能找到你的东西,而你也没有真正的机制来获得对你工作的反馈。
相比之下,当你在线写作时,你能够将你的工作分享给更广泛的观众,并获得关于阅读时间和阅读比率等方面的非常好的反馈。如果你坚持下去,写作是一种建立个人品牌并成为某个特定领域“首选”人物的绝佳方式。
例如,我的一个目标是尽力成为数据科学职业领域的“首选”人物。如果我完全依赖个人作品集或偶尔的谦虚自夸的 LinkedIn 帖子,很难建立这种小众品牌,但通过在 Medium 上写作,很容易塑造这种在线身份。
而且不仅仅要听我的话——比如看看卡西·科齐克罗夫。卡西是谷歌的首席决策科学家,她在数据领域是一个非常激励人心的榜样。通过在线写作,她建立了一个出色的个人品牌,甚至像我这样的无知者也知道她是谁。再看看克里斯·阿尔邦,他是维基媒体基金会的机器学习总监,他在数据领域的 Twitter 账号可能是世界上最有趣的。克里斯通过多年的积累建立了一个了不起的个人品牌,他利用这个平台与来自世界各地的人分享优质的学习资源。
2. 在线写作帮助你与业内其他人建立有意义的联系
在线写作的下一个好处是,它是建立你网络的一个绝佳方式,比 LinkedIn 等其他途径要愉快得多。
在多年在 LinkedIn 上发帖之后,我发现观众看到的内容与你希望看到你内容的人之间经常存在不匹配。
你的大多数观众将是你的关注者,但除非你已经拥有一个非常活跃且参与度高的受众,否则你不太可能有很多感兴趣的关注者。你的关注者往往是已经认识你的人,而且——更糟的是——他们中的一半可能只是无兴趣的半接触者。即便如此,我的 LinkedIn 网络中还包括我在当地报刊亭第一份周六工作时合作过的人。保持联系固然愉快,但我分享有关人工智能革命的想法对我们双方都没有太大价值。
相比之下,当你在线写作时,你能够以一种长期可持续和有价值的方式,将你的想法分享给非常有针对性的社区。
例如,我将大多数数据科学文章分享在像 Towards Data Science 和 Towards AI 这样的出版物中。我能够针对那些在我的领域的人,而不是让普通人被我关于卷积神经网络细节的沉思打扰。当我在像 Medium 这样的平台上分享这些沉思时,我能够将我的内容呈现给真正感兴趣的人。
3. 写作有助于掌握沟通和讲故事的能力
任何数据科学招聘人员都会告诉你,找到一个知道什么是无监督 ML 模型的人很容易。困难的是找到一个能够简洁地向业务中其他非技术人员解释它的人。
随着你在数据科学领域的发展,建立这些沟通技能非常重要,你不应该只专注于技术技能。当然——当你刚开始时,你可能会花大部分时间进行编码,而在团队内部会议之外的展示不多。但是,如果你想推进你的职业生涯,发展强大的讲故事和沟通能力是绝对关键的。记住:高级和首席数据科学家花在展示上的时间远多于编码。写作有助于做到这一点,因为你会不断思考如何有效地表达观点,以及在哪里削减不必要的废话。
4. 作品集在你刚开始职业生涯时很有用;而写作在长期推进职业生涯方面更有效
许多有志于成为数据科学家的人员可能没有意识到,作品集很快会变得过时。
我记得在不到一年前,我正在完成数据科学硕士学位时,创建了我的第一个个人作品集网站。那时正是我开始申请数据科学职位的时候,我想找一个展示我所做的一些项目的方法。我听说过建立在线作品集的重要性,于是我用 GitHub Pages 和 Jekyll 制作了一个简单的静态网站。
尽管这个作品集对我获得第一个工作非常有帮助,但一旦我开始全职做数据科学家,我发现自己没有时间(或兴趣)在工作之外做数据科学项目。雇主们也对我在工作中的成就更感兴趣,而不是我个人的项目,不久之后我就放弃了我的作品集。
相比之下,我的经验表明,在线写作是一种长期建立职业资本的更可持续的方式。
为什么?首先,因为它很快。做一个完整的数据科学项目需要很长时间,但写一篇快速的博客文章(像这样)可以在一两个小时内完成。
其次,因为你可以写任何你想写的东西。这使得写作成为一种非常可持续的长期策略来建立职业资本,因为你可以随着时间改变你写的内容。当你“在一线”作为程序员工作时,你可以分享编程技巧或项目总结。当你在更高的职位上,大部分时间都在与利益相关者会面或制定团队策略时,你可以写关于大局的内容。无论你处于职业的哪个阶段,将你的在线写作分享给技术和非技术招聘人员都非常简单,你可以通过展示你的写作来展示你的沟通技巧。
如果你仍然没有被说服,可以考虑这一点:如果过去一年的 AI 发展教会了我们什么,那就是算法和编程语言可能会改变,但讲故事的技巧将永远需要。在 5 年内,我预计 AI 工具将使我编写代码的时间大大减少(或者,至少,我的编程会更快)。但我肯定还会继续用数据讲故事,所以这项技能的投资看起来是值得的。
如何开始写作
如果写在线文章的想法让你感到畏惧,不用担心:我曾经有过完全一样的感觉。
然后,在 2023 年 3 月,我决定一试,写了我的第一篇 Medium 文章。以下是帮助我开始这段旅程的一些做法和禁忌。
做:
-
从小做起 — 你不需要写关于突破性的或非常冷门的东西。我的第一篇文章是关于我职业故事的个人反思,第二篇则是关于如何用SQL 处理数据的指南。这两篇都是非常简单的话题,但这实际上是一个优势,因为它们写起来很快,使得“起步”非常容易。
-
分享你的个人经历 — 市面上有很多编程指南,如果你的写作像技术文档一样,很难脱颖而出。无论你写什么,都试着展示一个个人角度:如果你在分享编程技巧或理论解释,这种{技巧/概念}如何在你的个人工作/项目中帮助了你?你花了多长时间来学习?有什么建议可以帮助你将这些技巧付诸实践?
-
找到适合你的节奏 — 我在 Medium 上发布文章的频率相当规律,因为我喜欢写作,但这并不是唯一的方法。许多优秀的数据科学作家每月或每几个月才发布一次。关键是找到一个适合你的节奏并坚持下去。
-
写关于常青话题 —— 如果你写编码指南或分享个人故事,这些内容在你写完后会长期保持有用。即使它们一开始没有得到关注,你也在投资于一个长期的优质内容库,这些内容如果有人在两年后偶然发现,依然会有价值。
不要:
-
不要觉得你需要是专家 —— 即使你是初学者数据科学家或有抱负的数据科学家,我可以保证你有值得分享的东西。读者并不关心你是否拥有量子编织学的博士学位;他们只关心你能否帮助他们解决他们面临的具体问题。我的个人经历证明了你不需要成为专业人士或写复杂的话题。
-
不要担心寻找利基市场 —— 花时间尝试不同的话题,把寻找利基市场的事留到以后。正如 YouTuber Ali Abdaal 经常建议的那样,先“开始做”,然后专注于“做得好”,只有在后期你才需要担心“变得聪明”。不要通过对自己施加严格限制来束缚你的想象力;允许自己尝试新事物,看看你喜欢什么。你甚至可能会惊讶于自己的发现!
就这些啦!
感谢阅读。我希望你觉得这些内容有用,如果你想聊天,请随时联系我 😃
哦,还有一件事——
我开始了一份名为 AI in Five 的免费通讯,每周分享关于最新 AI 新闻、编码技巧和数据科学家/分析师的职业故事的 5 个要点。没有炒作,没有“数据是新石油”的废话,也没有来自 Elon 的推文——只有实用的技巧和见解,帮助你在职业生涯中发展。如果这些内容对你有帮助,请 在这里订阅!
[## AI in Five | Matt Chapman | Substack
最新的数据科学和 AI 世界的新闻、职业故事和编码技巧,总结成 5 个要点……
aiinfive.substack.com](https://aiinfive.substack.com/?source=post_page-----231c4f2c654e--------------------------------)
MLOps 的关键起点:探索其核心组件
原文:
towardsdatascience.com/a-key-start-to-mlops-exploring-its-essential-components-27646238372d
面向初学者的 MLOps 介绍
·发布于 Towards Data Science ·9 分钟阅读·2023 年 11 月 9 日
–
图片由 Aaron Burden 提供,来源于 Unsplash
对于像 MLOps 这样具有多个不同方面和组件的概念,尤其是对初学者来说,总是很不方便开始学习。我自己曾是 MLOps 的初学者,想要对它有一个清晰的整体了解相当困难。本教程旨在帮助你获得对 MLOps 的清晰而广泛的视野。它将帮助你更好地理解 MLOps,并指导你在学习过程中取得更好的进展。由于我写的关于 MLOps 的内容,本教程是一个很好的起点,所以让我们开始吧!
目录:
· 1. 介绍
· 2. MLOps 工作流程
· 3. MLOps 原则
· 4. MLOps 工具
· 5. MLOps 最佳实践
· 6. 结论
我的 MLOps 教程:
-
教程 1:MLOps 的关键起点:探索其核心组件
-
教程 2:面向初学者的 MLOps 工作流程介绍
-
教程 6:实践中的测试:代码、数据和 ML 模型
-
教程 7:实践中的追踪:代码、数据和 ML 模型
[我将在发布相关文章时更新此列表]
1. 介绍
随着在日常任务和应用程序中使用机器学习模型的需求增长,集成这些模型到软件中出现了若干挑战。因此,专业人士和研究人员提出了 MLOps,作为 DevOps 在机器学习模型上的扩展,以克服这些挑战。MLOps 定义为:
一套用于以高效、优化和有组织的方式设计、构建和部署机器学习模型的技术和实践。(你可以在我的文章中阅读更多关于 MLOps 的动机和定义:MLOps 初学者友好介绍)
从前面的定义中,我们可以看出有很多“东西”需要了解和学习。开始你的学习之旅可能会很困难,尤其是对初学者来说:你可能会想,这些“东西”是什么?每个“东西”有什么用途?我从哪里开始学习?
为了回答所有这些问题,我决定编写这个教程。要涵盖的主题很多,已经写过或计划将来写的文章也很多,因此我决定将它们归纳在一起,组织成四个不同的主要组别。我将这些称为 MLOps 的主要成分!
嗯,我知道,我知道!我的回答引出了一个新问题:这些成分是什么?简单来说,就是MLOps 工作流、MLOps 原则、MLOps 工具和 MLOps 最佳实践。在本教程中,我们为每个组成部分专门设立了一个章节。
2. MLOps 工作流
MLOps 工作流和生命周期(箭头的方向)。
MLOps 工作流定义了从业务问题到模型部署和性能监控的项目流程。它包括以下步骤:
-
业务问题: 代表了 MLOps 工作流中的第一步。它包括定义模型工作流、人工智能画布和/或机器学习画布,在这些画布中,我们指定模型的输入和输出,以及过程及其各种步骤。
-
数据工程: 包括一系列从收集原始数据到准备好的数据集的不同步骤,这些数据集可以被 ML 模型使用。
-
ML 模型工程: 包括一系列关键步骤,用于定义和选择符合业务问题要求的 ML 模型。它涉及:模型训练、模型评估、模型测试和模型打包。
-
代码工程: 是 MLOps 工作流中的第四步。这个阶段专注于将模型部署到生产环境,并通过观察模型的整体行为以及记录和保存预测结果来确保其预测质量。
示例: 自动驾驶车辆通常使用计算机视觉技术的组合,具体取决于任务和要求。对于本教程,假设你正在开发一个图像分类系统,用于识别各种道路标志、车辆、行人和道路状况。因此:
-
在业务问题中: 我们定义第一个问题是使自动驾驶车辆能够准确感知和解读其周围环境。更具体地说,使其能够识别和分类道路标志、车道边界、行人和其他车辆,以做出明智的驾驶决策。其次,系统还必须优先考虑安全,不仅要考虑车上乘客的安全,还要考虑所有道路使用者的安全。因此,根据系统的实时输出和情境感知,预定义和建立了一系列动作,如减速和其他与安全相关的响应。
-
在数据工程中, 第一步是收集一个多样化且具有代表性的图像数据集,这些图像应在不同的天气和光照条件、道路类型以及交通场景下拍摄。由于这是一个分类任务,高质量的标记数据对于训练和评估至关重要。然后,通过处理数据质量问题(如数据不平衡)以及准备调整、规范化和增强图像的脚本来准备数据。最后,选择一个数据存储和管理系统,以便于访问训练和验证数据。
-
在机器学习模型工程中, 我们首先选择适当的深度学习架构,如卷积神经网络(CNN),用于图像分类,并尝试各种模型架构和超参数(你可以查看我的教程,了解图像分类的实际介绍)。其次,我们在数据集上训练选择的模型,同时优化准确性,并考虑效率和推理速度,因为自动驾驶车辆需要实时处理。然后,我们应用严格的评估指标,如准确性、精确度、召回率和 F1 分数,以评估模型性能;并实施交叉验证技术和微调。在打包模型之前,我们将其调整为适应车辆硬件的实时推理,考虑延迟和资源限制。
-
在代码工程中, 那些经过严格训练和评估的模型被集成到自动驾驶车辆的感知系统中。在实时推理过程中,我们确保模型预测的质量,进行广泛的测试和监控:观察和记录模型在各种实际驾驶场景中的行为。这些行为用于检测异常和任何意外或错误的模型行为。
在开发过程中以及部署后,可能需要返回到之前的步骤,以保持模型的长期性能。因此,这些阶段通常以循环的方式进行。这后一种定义了 MLOps 生命周期!为了维护 MLOps 生命周期,理解并尊重 MLOps 原则是必须的。欲了解更深入的信息或详细见解,请参阅我的教程:适合初学者的 MLOps 入门。
3. MLOps 原则
MLOps 原则是一组旨在维护 MLOps 生命周期以避免/减少技术债务的概念。它们需要在之前描述的不同工作流步骤中应用。这些原则包括:
-
**版本控制:**确保对机器学习模型、代码和数据所做的更改得到有效跟踪和管理,使工程师和数据科学家能够在需要时回滚到之前的版本,比较不同版本之间的性能,并重现结果。
-
**测试:**确保 MLOps 管道的正确性和效率,从而减少错误和 bug 的风险,并能够快速检测和修复问题。
-
**自动化:**使重复任务的高效和可靠实施成为可能,并标准化包括构建、测试、部署和管理在内的过程。它定义了项目的成熟度,简单来说,就是定义系统从人工干预中解放的程度。
-
**监控和跟踪:**确保生产中 ML 模型的稳定性和可靠性。它们帮助工程师和数据科学家跟踪对模型和数据所做的更改,促进协作并在问题出现时进行故障排除。
-
**可重复性:**允许在执行地点不同的情况下,给定相同的输入生成相同的结果。通过之前的原则来保持结果的一致性和可信度,并满足监管或合规要求。
成功实施这些原则需要利用合适的工具和遵循最佳实践,我们将在接下来的两个部分中介绍。欲了解更深入的信息或详细见解,请参阅我的教程:MLOps 原则简介。
4. MLOps 工具
MLOps 工具包括所有用于建立和维护 MLOps 工作流和原则的软件、硬件和系统。主要包括:
-
**编程语言:**如 Python 和 R。
-
**库:**如机器学习和深度学习库(Scikit-Learn、OpenCV 和 Keras)。
-
**平台:**如机器学习和深度学习平台(MLflow 和 Jina)。
-
框架: 如机器学习和深度学习框架(LightGBM 和 CNTK)。
-
Notebook 环境: 如 Jupyter 和 Google Colab。
-
基础设施: 如基于云计算和本地部署的基础设施。
选择合适的工具取决于几个标准,如项目需求、数据类型和量以及部署目标。然而,必须了解 MLOps 实践应该独立于这些工具,换句话说,它应以独立于编程语言、机器学习框架或基础设施的方式设计和实施,以确保 MLOps 实践具有通用性、适应性,并且不锁定于任何特定的技术栈。
5. MLOps 最佳实践
MLOps 最佳实践是一组经过验证的行为、规范、技术或/和方法,这些实践在 MLOps 中已证明能持续产生良好的结果和效果。在机器学习项目中工作时,您会发现自己在编写代码和协作,因此有三个关键要素:代码、团队合作,当然还有您自己!因此,我将它们分为三个不同的组:代码最佳实践、团队合作最佳实践和个人发展最佳实践。
代码最佳实践:
-
最重要的实践是尊重 MLOps 工作流并适应其原则。没有描述的工作流和原则,就没有 MLOps。
-
许多工具是专门为 MLOps 应用设计的,因此,技术栈的慎重选择至关重要。这个选择不仅有利于工作流中的和谐,还能确保兼容性,在大多数情况下,无需编写可能上千行的代码。
-
代码和资源优化提升用户体验,降低运营成本,确保应用程序高效运行,特别是在资源受限的环境中,如移动设备或云计算平台。
团队合作最佳实践:
-
数据科学家、工程师和领域专家之间的合作。有效的跨职能团队对于 MLOps 的成功至关重要。
-
建立编码规范有助于确保代码库的一致性、可读性和可维护性,同时促进团队成员之间的有效协作。
-
项目结构的优化提高了团队合作效率,帮助创建一个组织良好、协作高效、富有生产力的环境,使团队成员能够高效地协同工作并促进项目的成功。有关更深入的信息或详细见解,我邀请您查阅我的教程:从 MLOps 角度结构化您的机器学习项目。
-
记录代码和项目流程可以确保所有团队成员都能获取到关键信息,理解彼此的贡献,并有效地协作。此外,这还促进了知识转移,减少了误解,并支持了项目的维护和可持续性。
-
在代码中加入注释,以解释代码的目的、假设和限制,是代码文档的另一个重要方面。
个人发展最佳实践:
-
保持自己对最佳实践和最新技术的更新。
-
了解文化,探索新方法和技术。
-
对于最佳实践保持开放态度,因为它们可能会随着技术进步、行业标准变化或新研究的发展而不断演变。
-
在专业社区中分享最佳实践,以帮助他人受益于经过验证的方法,并为集体知识做出贡献。
最后但同样重要的是,这些实践只是星空中的一部分,但它们是最亮的星星!
6. 结论
本文到此结束。希望你觉得它有价值并具有启发性!我撰写本文的目的是为你提供一个扎实的 MLOps 基础。在即将到来的文章中,我将深入探讨 MLOps 工具和最佳实践。此外,我还计划分享各种 MLOps 技术的教程和实际示例,请继续关注更多有价值的内容。
感谢阅读本文。你可以在我的GitHub 主页找到我提供的各种教程示例。如果你喜欢我的教程,请通过关注我和订阅来支持我。这样,你将收到关于我新文章的通知。如果你有任何问题或建议,欢迎留言。
图片来源
本文中所有未在说明中提及来源的图片和图表均由作者提供。
初学者的最大似然估计(附 R 代码)
原文:
towardsdatascience.com/a-laymans-guide-to-maximum-likelihood-estimation-with-r-code-9e992a10ecd9
最大似然方法的直观解释
·发布于 Towards Data Science ·4 min read·Mar 14, 2023
–
最大似然原理是数据科学、机器学习和人工智能中大量模型的基本估计方法。它适用于从分类的 logit 模型到深度学习中的信息理论等各种方法。本文旨在提供对该原理的直观介绍。
假设你有三个数据点 x = (1, 2, 3),你认为它们是从均值未知(μ)且标准差为 1 的正态分布中生成的,即 N(μ,1)。
考虑这些数据点,最可能的μ值是什么?
这是最大似然估计方法旨在回答的问题。
图片由作者创建
假设研究人员考虑了μ = (0, 2, 6)这三个可能的值作为候选值。哪一个值最有可能是观察到的数据的值?
上图分别用蓝色、黑色和绿色绘制了 N(0,1)、N(2,1)和 N(6,1)的三个正态分布概率(密度)函数。即,它们分别是 f(X|μ=0)、f(X|μ=2)、f(X|μ=6)。
作者创建
底部的红色方块点表示 x=(1, 2, 3),这些是观察到的数据点。
从上面的图表可以明显看出,数据点 x 最有可能来自 N(2,1)。它们来自 N(0,1)的可能性相当低,来自 N(6,1)的可能性更低。因此,我们可以说μ = 2 的值与 x = (1, 2, 3)的兼容性最高。
如果我们考虑所有其他可能的μ值,并且确信 2 最有可能生成 x,那么它就是μ的最大似然估计。
让我们定义一些数学细节:
f(X1,X2,X3|μ):联合 概率 密度函数,给定 μ。它展示了在给定 μ 的情况下 X 的概率密度。
L(μ|x1,x2,x3):似然函数,给定 x。它展示了在给定观察数据 x=(x1, x2, x3) 的情况下 μ 的似然。
区别在于密度函数是通过随机变量 X 索引,给定如 μ 这样的参数值;而似然函数是通过参数索引,给定观察到的数据 x。
这两个函数的关系为
L(μ|x1,x2,x3) = k f(x1,x2,x3|μ)。
其中 k > 0 是任何常数。为了简便起见,我们假设 k = 1。那么这两个函数几乎相同,区别在于它们的参数和条件值。如果我们为简单起见假设 X 们是独立的,那么我们可以写出(因为在独立情况下联合概率是个体概率的乘积)
L(μ|x1,x2,x3) = f(x1|μ) × f(x2|μ) × f(x3|μ)。
图片由作者创建
上表显示了当 x=(1, 2, 3) 时似然函数 L(μ|x1,x2,x3) 的值:最后一列列出了这些值的乘积。最大似然值出现在 μ = 2。
现在我们考虑 μ 的所有可能值,并将似然和对数似然函数作为 μ 的函数进行绘制。对数似然函数定义为
l(μ|x1,x2,x3) = log[L(μ|x1,x2,x3)],
其中 log() 是自然对数函数。对数似然是似然函数的单调变换。它被广泛使用,因为它在分析上是可处理的,具有可加性和线性。
图片由作者创建
上述函数如图所示。从上面的图中可以清楚地看到,似然或似然对数在 μ = 2 时最大,这也是 x = (1, 2, 3) 的最大似然估计。
在分析上,可以证明样本均值是从 N(μ,1) 独立生成的样本的最大似然估计量,而 x = (1, 2, 3) 的样本均值确实是 2。
计算和绘图的 R 代码如下:
x = c(1,2,3) # Data observed
X=seq(-5,9,0.01) # X range
par(mfrow=c(1,1))
# plot density functions
plot(X,dnorm(X, mean=2, sd=1), type ="l",
col="black", lwd=2, add=TRUE, yaxt="n",ylab="density")
curve(dnorm(x, mean=0, sd=1), type ="l", col="blue",
lwd=2, add=TRUE, yaxt="n")
curve(dnorm(x, mean=6, sd=1), type ="l", col="green"
, lwd=2, add=TRUE, yaxt="n")
# points for data X
points(x,c(0,0,0),col="red",pch=15)
legend("topleft", legend=c("N(0,1)", "N(2,1)","N(6,1)"),
col=c("blue", "black","green"), lty=1,cex=1,lwd=2)
abline(v=c(0,2,6),col=c("blue","black","green"))
# Calculation of Likelihood vales at different mean values
prod(dnorm(x,mean=0,sd=1))
prod(dnorm(x,mean=2,sd=1))
prod(dnorm(x,mean=6,sd=1))
# Plotting likelihood and log-of-likelihood
m=seq(-4,8,0.1)
m1=rep(0,length(m))
m2=rep(0,length(m))
for(i in 1:length(m)) {
# Likelihood
m1[i]=prod(dnorm(x,mean=m[i],sd=1))
# log-of-Likelihood
m2[i]=sum(log(dnorm(x,mean=m[i],sd=1)))
}
# plotting
par(mfrow=c(1,2))
# Likelihood
plot(m,m1,type="l",ylab="L(mu|X)",xlab = "mu",lwd=2)
abline(v=2,col="red")
# Log-of-Likelihood
plot(m,m2,type="l",ylab="log of L(mu|X)",xlab = "mu",lwd=2)
abline(v=2,col="red")
总之,最大似然估计方法被广泛应用于数据科学中的许多模型和方法。其概念和原理通常未被研究人员和实践者完全理解。本文旨在提供一种直观的解释,而不介绍分析细节。
感谢阅读!
请关注我,获取更多有趣的故事!
通过示例了解精益数据管道
原文:
towardsdatascience.com/a-lean-data-pipeline-by-example-e08bfce58133
强大的云数据平台,如 Snowflake 和 Databricks,改变了我们对标准形式和数据仓库的思考方式
·发表于Towards Data Science ·7 分钟阅读·2023 年 4 月 18 日
–
图片由David Di Veroli拍摄,发布在Unsplash上
介绍
在最小化云数据管道成本时,首先想到的是寻找最便宜的 ETL 解决方案。例如,Databricks 是否比 Snowflake 便宜?我应该使用云服务还是在内部集群上本地运行 Spark?
更好的成本最小化方法是利用精益原则。精益创业原则之一是通过小步骤进行验证[1]。对于云数据管道来说,关注价值尤为重要,因为每一次规范化和维度都需要花费金钱。此外,50 年的数据管道经验提供了大量我们可以做的转换,但只有对客户的分析价值才能定义我们应该做的事情。
在这篇文章中,我描述了如何通过专注于发展业务价值来进行成本最小化,即仅在需要回答特定业务问题时才转换数据。这样,你可以根据独特的业务案例而不是成本最小化的练习来选择大数据平台。只要你了解在最小化数据转换中的权衡,如果你的业务用例不需要,就最好避免进行转换。
一个(过于)简单的问题
因为我想说明一个想法,而不是建立实际的数据管道,所以我将以一个玩具问题来演示我们如何实现一个多阶段管道,然后假设一个转换管道是不必要的。
玩具业务用例是,我想使用在另类电台中播放的顶级艺术家来构建一个个人音乐播放列表。因此,我现在有一个商业问题需要提问,这将指导任何后端开发活动并限制其范围。
自 2020 年 3 月以来,另类电台的前 10 名艺术家有哪些?
ChatGPT 对这个问题的回应是:“对不起,作为一个语言模型,……” 然后将我指向 Billboard Top 100 的另类电台榜单,这同样没有帮助。我想知道电台上播放的内容,并使用准确的数据为我提供个人播放列表的新想法。
准确的数据,真实的模型,真实的答案
公共 API 端点用于下载播放列表数据是丰富的。开始于
作为数据工程师,我们确定数据源,并专注于将播放列表数据干净地下载到我们喜欢的云提供商的数据湖中。这个过程中的每一步都必须保持数据的清洁。也就是说,每一步都应确保数据完整且无重复。像 dbt 这样的现代工具具有内置功能,可以使这一过程变得简单[8]。
播放列表通常具有基于播出日期的模式以及“艺术家、专辑、歌曲和节目”等属性。节目是另一个基于时间的实体,跟踪每位 DJ 在电台的表现。API 生产者可能会反映他们的数据库,最佳实践是将数据按原样同步到你的数据湖中。关键是我们从 API 接收到的数据足够用于分析。
一个简单的 Python 代码可以将原始数据从网络同步到数据湖。因此,回答问题的第一步是展示单次播放的数据是怎样的。
JSON 输出示例快照(来源 KEXP API)
关键策略是同步来自 API 的所有数据,并确保我们能够定期同步数据。
让你的数据转换选择具有目的性
一旦数据同步到数据湖中,它将会有类似于源头的数据输出。数据湖会以原始形式捕获所有数据,以便在需要时随时使用。数据湖需要一个严格的结构和治理,以保持数据的清晰界限和安全性[9]。
当数据在数据湖中时,你准备好建立一个数据仓库(或者如果你愿意,可以是数据湖仓)。数据仓库有丰富的理论,数据质量至关重要。如果你回答了问题,但问题本身是错的,那比不回答问题更糟。因此,完全理解你能做什么与知道你应该做什么一样重要。
我喜欢将对历史数据仓库的全面理解视为我数据管道中的“未来防护知识”。我可能不需要每一个转换,但如果当前数据无法解决新的客户问题,我需要有一个预想的转换。
规范化和清理
标准范式 (1NF、2NF 和 3NF) 是最常见的数据转换的基础 [2]。
第一范式 (1NF) 规定每一列应只有单一值,因此列中不应有数组或用逗号分隔的列表。保持所有列值为原子的原因很充分,因为在同一查询中进行聚合和展开会导致笛卡尔连接。然而,现代的数据仓库/湖泊架构支持复杂类型,如列表和映射。复杂结构对于维护数据血缘关系和在管道下游提供灵活性非常有益,并且受到像 Snowflake 和 Databricks 这样的商业应用的良好支持。
第二范式 (2NF) 规定将 1NF 的数据根据一组属性去重。在大数据世界中,我们不能依赖系统强制唯一性,因为分布式数据处理的原因。数据在多台机器上的列中处理,我们希望避免全局操作,因为这些操作在时间和金钱上都是昂贵的。在现代大数据世界中,我们按处理时间收费,因此管理效率至关重要。
第三范式 (3NF) 规定将 2NF 的数据移入一个单独的表中,每个表都有一组特定于其领域的数据,并且一个外键将特定表中的数据与原始数据相关联。这有助于性能,因为 3NF 中的数据只需要在一个地方查询,外键可以用于生成报告。
Kimball 数据仓库模型
Kimball 数据仓库(维度模型)是《Kimball 数据仓库工具包》中描述的星型模式 [3]。它扩展了 3NF 的思想,并以度量或衡量指标作为数据的核心分析组件,以维度作为这些度量相关实体的属性集合。它通常被认为是数据分析的黄金标准。
Kimball 数据仓库是为关系型数据库设计的,因此实施 Kimball 模型的所有方面可能是可选的。例如,在分布式 Spark 环境中,顺序替代键可能会成为需要避免的障碍 [4]。如果你可以在没有替代键的情况下回答所有客户的问题,那么你就不需要它们。
使用分析 GUI 验证小增量
“建造它,他们就会来”并不是数据产品的一个好模型。大多数数据项目的潜在来源和分析用例的数量庞大,很容易陷入专注于后端而排除客户的陷阱。
回到成本控制:每次转换和每次管道执行都会花费金钱。高效的数据管道尽可能少做。如果我们能够回答业务问题:停止。展示给客户。获取他们的反馈。回答下一组问题。
一个好的分析图形用户界面(GUI)非常有价值,因为它让公众可以访问数据。此外,它让客户可以询问有关数据的问题。客户想要的解决方案将会是关于引入新数据的。
我使用了 Tableau,并为简单应用发布了一个具体答案。
自 2020 年 3 月以来,替代电台上的十位艺术家是谁?(来源 作者的公开表格)
在这个例子中,我不需要进行任何转换来实现预期的用例。当你能够回答业务问题时,停止数据转换,然后专注于下一个业务问题是合适的。
此外,通过像 Tableau 这样的图形用户界面(GUI)为客户提供对数据的完全访问权限,然后他们可以自己回答以下一些问题。关键思想是,你发布数据,让客户可以自行探索并提供反馈。
当你不能用现有数据回答问题时,获取更多数据——迭代——重复。
基于分析的数据产品的增量好处
分析驱动的数据产品的最强有力的论据是,用户在每次迭代中都能感受到价值。较不明显的好处是将关注点与数据湖分开。数据湖可以作为无损数据存储与生成用例并行增长。交付用例的工程师可以随着他们满足更复杂的问题,拉取越来越多的数据。
例如,在玩具问题的用例中,播放列表数据可以持续更新,同时我们从如 SeatGeek [10] 等来源加载票务数据。即使我们不立即使用它,更多的数据也有助于建立价值。利用数据湖与大数据平台结合的力量,即使是在玩具问题中也会显现出来。
结论
数据云改变了我们收集和使用数据的方式[5]。我们应该专注于将数据导入云端,并利用 Snowflake、Redshift 或 Databricks 等工具查询数据。在这种情况下,我们可以根据我们能想象或通过机器学习等技术建立的关系,发掘出更多的价值。
在构建数据产品时,至关重要的是通过分析用例拉动后端功能,仅构建所需的数据结构,最小化成本,并确保您的数据产品提供客户价值。
参考文献
-
E.F. Codd, (1970) 大规模共享数据银行的关系模型
-
Kimball, Ross (2013) 数据仓库工具包
-
M. Karasou (2019) 向 Spark 数据框添加顺序 ID
-
Seatgeek (2023) 客户 API
处理大数据集的小技巧,适用于有限内存
Pandas 默认设置并不理想。一个小小的配置就可以将你的数据框压缩到适合你的内存。
·发布于 Towards Data Science ·阅读时间 8 分钟·2023 年 1 月 19 日
–
你可以在不丢失属性的情况下压缩一个巨大的 Pandas 数据框,就像挤压一个汉堡一样。通过这个小技巧节省内存,并提高工作效率。— 图片由 Leonardo Luz 提供
我从未认为我的代码需要改进。我总是抱怨内存不足或数据集过大无法处理。
我常用的解决方案是将数据放在 Postgres 数据库中,并编写 SQL 查询。毕竟,这是一种处理大规模数据集的可接受方式。每当我获得一个大数据集时,我都会这样做。
但我无法获得 Python 中的完全灵活性。因此,我不得不将两者结合使用并交替进行。例如,我将数据集加载到 SQL 数据库中,并编写一个 Python 脚本来运行 SQL 查询,通常使用 Sqlalchemy。
虽然这让我在两者之间拥有灵活性,但我在与其他团队成员共享代码时遇到了问题。其他成员应该具备关系数据库的知识和设置。
这个问题的经典解决方案是增加内存并进行并行任务。在基础设施方面,迁移到云端是我最喜欢的解决方案。我可以只为高性能资源的使用付费。而在并行执行方面,像 Dask 这样的技术可以为我提供支持。
然而,我最近发现代码优化是首选且通常是最有成效的解决方案。如果你的代码在单线程下表现不佳,我们如何保证并行化能够提升性能?如果你的代码没有充分利用本地计算机的资源,那迁移到云端又有什么意义呢?
在我学到的许多 Pandas 优化技术中,有一种特别深刻。
诀窍是…
使用正确的数据类型来节省内存。
Pandas 根据每列中存在的值来推断数据类型。但它没有上下文。而你有!
如果列中的所有值都是整数,Pandas 通常会将 int64 分配为该列的数据类型。然而,还有更多的 int 变体可供使用,例如 int8、int16 等。
对于大多数情况,默认设置是可以的。但有时,对于较小的值,这个占位符过大。打开一个笔记本,输入以下代码并查看其输出。
import numpy as np
np.iinfo('int64')
# >> iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64)
Int64 可以容纳从 -9223372036854775808 到 9223372036854775807 的值。我甚至不知道这些数字怎么称呼。
但在大多数实际情况下,你可以推测可能的列值。例如,在酒店预订系统中,房间中的客人数几乎总是个位数。如果是宿舍,可能是两位数。
标准的 Python 库可以告诉你每一行的内存使用情况和执行时间。
为什么不使用 int8 呢?它可以容纳从 -128 到 +128 的值。这个更小的占位符相比浪费的 int64 占用更少的空间。
我们在一个示例数据集上测试一下这个方法。
以下脚本生成了一个随机数据集。在实际生活中,你不会生成数据集。注意它的数据类型和内存使用情况。
import pandas as pd
import numpy as np
size = 1000000
df = (
pd.DataFrame(
{
"room_rate": np.round(np.random.randint(35, 100, size) / 23 * 100, 2),
"number_of_guests": np.random.randint(0, 6, size),
"channel": np.random.choice(
["Online", "Corporate", "Walk In", "Complementary"], size
),
"booking_status": np.where(
np.random.randint(0, 100, size) > 90, "Canceled", "Not-Canceled"
),
}
)
# .pipe(lambda df: display(df) or df)
.pipe(lambda df: display(df.dtypes) or df)
)
"""
room_rate float64
number_of_guests int64
channel object
booking_status object
dtype: object
"""
df.memory_usage(deep=True)
"""
Index 128
room_rate 8000000
number_of_guests 8000000
channel 65748930
booking_status 68640436
dtype: int64
"""
df.memory_usage(deep=True).sum()
"""
150389494
"""
df.describe(include='all').fillna('-')
"""
| | room_rate | number_of_guests | channel | booking_status |
|:-------|:------------------|:-------------------|:----------|:-----------------|
| count | 1000000.0 | 1000000.0 | 1000000 | 1000000 |
| unique | - | - | 4 | 2 |
| top | - | - | Corporate | Not-Canceled |
| freq | - | - | 250099 | 910109 |
| mean | 291.2777862699999 | 2.50315 | - | - |
| std | 81.59777471018273 | 1.708107430793215 | - | - |
| min | 152.17 | 0.0 | - | - |
| 25% | 221.74 | 1.0 | - | - |
| 50% | 291.3 | 3.0 | - | - |
| 75% | 360.87 | 4.0 | - | - |
| max | 430.43 | 5.0 | - | - |
"""
生成的数据集有一百万条记录,占用约 180 MB 的内存。room_rate 变量的范围是 152.17–430.43\。但它被分配了 float64 数据类型。实际上,float16 已经足够了。同样,将 number_of_guests 转换为整数,将 channel 转换为类别。
有趣的是,预订状态是以分类方式表示的布尔值。为了分析目的,我们可以将其更改为布尔值。但也有人可能会争辩说还是将其保留为类别更好。
下面是我们如何将数据类型转换为更理想的类型,以及现在需要多少内存。
(
df.assign(
room_rate=df.room_rate.astype("float16"),
number_of_guests=df.number_of_guests.astype("int8"),
channel=df.channel.astype("category"),
booking_status=df.booking_status == "Canceled",
)
.pipe(lambda df: display(df.dtypes) or df)
.pipe(lambda df: display(df.memory_usage(deep=True)) or df)
.memory_usage(deep=True)
.sum()
)
"""
room_rate float16
number_of_guests int8
channel category
booking_status bool
dtype: object
Index 128
room_rate 2000000
number_of_guests 1000000
channel 1000435
booking_status 1000000
dtype: int64
5000563
"""
我们通过将数据类型转换将 180MB 的数据集减少到 5MB。这大约节省了 96%。
压缩数据集并将其存储为最佳格式以备后用是个好主意。
## CSV 文件被高估了!我放弃了一些它的优点以获得更多的好处。
我使用了什么来获得更小的文件大小和更好的性能。
如何为每一列选择数据类型?
Pandas 的默认数据类型几乎总是不最优的。正如我们在上一节中看到的,整数无论范围如何,都被分配为 int64。对于较小的数据集,我们不需要担心这个问题。即使在数十万行数据中,性能影响通常也不显著。
但是当我们的计算变得复杂且数据集规模更大时,我们应该更认真对待这个问题。挑战在于在不丢失信息的情况下,分配尽可能小的占位符。
第一步是获取数据集的描述。它可以在一个地方提供大量的信息。查看我们上面示例中的 describe
输出。
对于非数值变量,你将获得唯一值的数量、最频繁的值及其计数。看着这些数据,我们可以猜测 channel
可能是一个类别,而 booking_status
可能是布尔值。因为在我们 1M 的记录中,只有 4 个独特的渠道在重复出现。
对于数值列,我们得到最小值和最大值。通过查看四分位数值,你可以得出这个列是否需要是整数或浮点数。在每种情况下,你也可以决定其大小。
这是一个数值数据类型及其值范围的表格。
__Integer Types__
| dtype | min | max |
|:--------|---------------------:|---------------------:|
| int8 | -128 | 127 |
| int16 | -32768 | 32767 |
| int32 | -2147483648 | 2147483647 |
| int64 | -9223372036854775808 | 9223372036854775807 |
| uint8 | 0 | 255 |
| uint16 | 0 | 65535 |
| uint32 | 0 | 4294967295 |
| uint64 | 0 | 18446744073709551615 |
__Float Types__
| dtype | min | max | resolution |
|:--------|------------------:|-----------------:|-------------:|
| float16 | -65504 | 65504 | 0.0010004 |
| float32 | -3.40282e+38 | 3.40282e+38 | 1e-06 |
| float64 | -1.79769e+308 | 1.79769e+308 | 1e-15 |
仔细观察你的数据集,并选择能够工作的最小值。
对于非数值数据集,Pandas 通常将其类型指定为 ‘object’。对象数据类型在大多数情况下是复杂且低效的。因此,如果列值不是自由格式的,你可以将其数据类型指定为 ‘category’。以下是它节省了多少内存:
df.channel.astype('object').memory_usage()
# >> 8000128
df.channel.astype('category').memory_usage()
# >> 1000332
通过简单地转换为类别数据类型,你可以节省大约 8 倍的内存。
## 如何将 Python 数据管道的速度提高到 91 倍?
一个 5 分钟的教程,可能会为你的大数据项目节省数月的时间。
towardsdatascience.com
转换数据类型需要更多的领域专业知识。
我们的演示过于简单,因为我们生成了数据集。但是现实世界中的数据集通常是复杂的,类型转换也是如此。
我们可以在任何数据框上使用 .astype
方法直接转换类型。但在我的例子中,我使用了 assign
方法。因为我们通常需要在进行类型转换之前修改列。
想象一个情况,当 number_of_guests
列有空值时。我们不能在没有填补合理默认值的情况下将该列转换为 int8。零可能没有意义,因为不可能有一个没有人的预订。我们可能认为 1
是一个好的默认值。但是,如果大多数酒店的预订有两个人,那么 2
更好。我们还可以引入 channel
变量来仔细选择每个渠道的默认值。
以下示例是条件分配默认值的方式。注意,我们仅在填补适当的缺失值后才将其转换为正确的数据类型。
df.assign(
number_of_guests=np.where(
df.channel == "Online",
df.number_of_guests.fillna(2),
np.where(
(df.channel == "Corporate") | (df.channel == "Walk In"),
df.number_of_guests.fillna(1),
df.number_of_guests.fillna(3),
),
).astype("int8")
)
同样,选择默认值需要领域知识,这在数据集中并不存在。
另一个挑战是未来的值。如果你进行离线分析,你可以仅通过查看数据集来选择数据类型。但如果你的代码在数据管道中运行,你还应考虑未来的值。
我正在将我所有的 ETL 工作从 Airflow 迁移到这个超级酷的框架。
当你获得不兼容的列值时,你可能会得到错误或错误的值。请参见以下示例。
s = np.random.randint(0, 255, 10)
pd.DataFrame({"original": s, "int8": s.astype("int8")})
| original | int8 |
|-----------:|-------:|
| 221 | -35 |
| 184 | -72 |
| 216 | -40 |
| 177 | -79 |
| 9 | 9 |
| 19 | 19 |
| 16 | 16 |
| 191 | -65 |
| 6 | 6 |
| 71 | 71 |
请注意,任何小于 128 的值已正确转换。但 128 及以上的值转换不正确。
或许你应该在将数据集传递给转换之前进行验证。
这里是你如何确保 Pandas 数据框在数据管道每个阶段的质量
结语
作为数据专业人士,我们处理的是大规模的数据集。我们对性能问题的终极解决方案是增加更多资源和并行执行。
我发现一些优化可以让我们在较小的计算机上处理更大的数据框。诀窍是分配正确的数据类型。
选择正确的数据类型需要领域知识。此外,如果你正在构建数据管道,你还应考虑未来的值。在离线分析中你不需要担心这一点。
希望这对你有所帮助。
谢谢你的阅读,朋友!在 LinkedIn、Twitter 和 Medium 上跟我打招呼吧。
还不是 Medium 会员?请使用这个链接 成为会员,因为你没有额外费用,我会因为推荐你而获得小额佣金。
一种机器学习方法预测胶质母细胞瘤患者的 MGMT 甲基化状态
肿瘤学中的放射组学
·
关注 发表在 Towards Data Science · 5 分钟阅读 · 2023 年 7 月 26 日
–
介绍
今天,我们将探讨一项针对胶质母细胞瘤患者的研究,该研究发表在《自然》杂志的 Scientific Reports 上:通过优化放射组学特征使用基于遗传算法的机器学习方法改善胶质母细胞瘤 MGMT 甲基化状态预测。该研究的目标是尝试预测 O6-甲基鸟嘌呤-DNA-甲基转移酶(MGMT)甲基化状态。能够预测这一状态的重要原因在于,它可以很好地指示化疗药物替莫唑胺(TMZ)的效果。
替莫唑胺概述
TMZ 是一种烷化剂,通过损伤癌细胞中的 DNA 来发挥作用,最终导致细胞死亡。TMZ 还使细胞对放射线更敏感。这在癌症治疗中是一个重要因素,因为放射线用于帮助杀死癌细胞。
这项研究旨在通过机器学习寻找预测 MGMT 甲基化状态的新方法。如果成功,这可以帮助缓解目前为获取肿瘤标本而需进行的技术限制和侵入性程序。
由于胶质母细胞瘤(GBM)对患者的致命威胁,处理这种癌症的效率和有效性非常重要。其中位生存期为 14 至 16 个月,占所有恶性中枢神经系统肿瘤的约 45%。
方法
该团队寻求利用两阶段方法来预测适当的 MGMT 甲基化状态。首先是通过消除噪声放射组学特征,然后将分类算法实施到遗传算法中,以帮助识别最佳预测特征。
在这项研究中测试了各种机器学习技术。目的是寻找对预测最有意义的放射组学特征。他们通过从磁共振成像(MRI)的多模态图像中提取放射组学特征来实现这一目标。两阶段特征选择方法从 eXtreme Gradient Boosting(XGBoost)模型开始,随后使用基于遗传算法(GA)的包装模型。GA 模型的工作方式类似于自然选择,通过识别“最适合”的特征集进行预测。
使用的数据是来自癌症基因组图谱的预处理和分割的多模态 MRI 特征。总共包括了 53 名 GBM 患者,并获得了 704 个放射组学特征。
遗传算法的工作流程阶段包括六个不同的步骤:初始种群的生成、适应度评估、父母选择、交叉、突变和下一代的种群替换。用于选择概率的公式(其中特征是根据其在适应度评估阶段的表现进行选择)如下所示:
选择概率公式
一旦从 XGBoost 算法中提取了初始特征,就该使用这些特征进行分类,以预测哪些患者属于 MGMT 甲基化和非甲基化类别,并将其作为适应度评估。他们尝试将三种不同的机器学习算法应用于遗传算法工作流中的适应度评估部分。他们使用了随机森林(RF)、XGBoost 和支持向量机(SVM)。为了实现这些算法,他们利用了 Python 机器学习库 SKlearn。
结果
一旦训练了三种不同版本的算法,他们使用三种不同的测量标准来评估模型性能:准确率、特异性和召回率。为了比较性能,他们使用了运行 20 次交叉验证的平均值,并使用了 Kolmogorov-Smirnov 检验进行评估。
最佳结果是通过将随机森林算法融入遗传算法(GA-RF)实现的。该技术在所有评估指标中都优于其他方法,准确率为 0.925,灵敏度为 0.894,特异性为 0.966。
在 GA-RF 模型完成后,得到了一个最佳的 22 个放射组学特征的子集。这些特征中包括 17 个纹理特征、3 个基于直方图的特征、1 个体积特征和 1 个强度特征。纹理特征可以通过反映空间强度相关性和体素的分布,帮助临床医生定量评估血流、浮肿和坏死的“多区域变化”。直方图特征可以用来说明图像中强度值的频率分布。
扩展的使用案例
为了测试模型的更广泛适用性,团队使用了在新数据集上学到的特征。他们这次使用的数据集是低级别胶质瘤(LGG)患者的数据集。他们直接应用了学到的特征,没有进行任何额外的特征选择。
GA-RF 模型在 LGG 数据集上的结果为准确率 0.75,灵敏度 0.78,特异性 0.62。没有进行任何迁移学习或微调,这些结果还是相当有前景的。
通过在 LGG 数据集上应用这些特征取得强劲表现,研究人员能够展示这些特征有可能被重用于其他类似疾病。
限制
该技术的一个潜在限制是,当患者数量较少时,研究人员可能会遇到高维度相关的问题。这种情况发生在特征数量相对于可用训练数据量较高时。如果发生这种情况,学习数据和目标变量之间的准确关系可能会很具挑战性。
高维度问题在放射组学领域普遍存在,而不仅仅是本研究中的问题。为了克服这一限制,团队使用了交叉验证,以更确定他们得到的结果。
交叉验证概述
交叉验证通过将用于训练和测试的数据分成不同的组来进行每次验证的迭代。通过多次这样做并取结果的平均值,你可以更有信心地认为你的模型能够重复地获得它给出的结果。
在癌症治疗中,临床医生依赖于肿瘤的特征和等级,以便优化化疗、放疗和手术治疗。
结论
如果这一技术继续得到发展并最终被利用,它可能成为医生非侵入性了解患者 MGMT 甲基化状态的一种方式。这些信息可以帮助他们做出更明智的治疗决策,从而有助于改善患者的预后。这也为放射组学在肿瘤学中可能的其他应用打开了大门。
Do, D.T., Yang, MR., Lam, L.H.T. 等。通过优化放射组学特征来改善胶质母细胞瘤 MGMT 甲基化状态预测,使用基于遗传算法的机器学习方法。 Sci Rep 12, 13412 (2022)。
doi.org/10.1038/s41598-022-17707-w
Eberhart, Karin, Ozlem Oral, 和 Devrim Gozuacik。“第十三章 — 通过抗癌药物诱导自噬性细胞死亡。” ScienceDirect,2014 年,
www.sciencedirect.com/topics/neuroscience/temozolomide#:~:text=Temozolomide%20(TMZ)%20is%20a%20small,damage%20and%20tumor%20cell%20death
。访问日期:2023 年 7 月 12 日。国家癌症研究所。‘试验产生了对脑癌治疗有影响的结果。’ 癌症动态博客,2016 年 6 月 9 日,
www.cancer.gov/news-events/cancer-currents-blog/2016/asco-temozolomide-brain
。访问日期:2023 年 7 月 18 日。美国癌症学会。“化疗药物的作用机制。” 美国癌症学会,2019 年 11 月 22 日,
www.cancer.org/treatment/treatments-and-side-effects/treatment-types/chemotherapy/how-chemotherapy-drugs-work.html
。访问日期:2023 年 7 月 25 日。欧洲医学肿瘤学会。“胶质瘤中的 MGMT 启动子甲基化:ESMO 生物标志物简报。” Oncology Pro,[更新于 2019 年 1 月 18 日],
oncologypro.esmo.org/education-library/factsheets-on-biomarkers/mgmt-promoter-methylation-in-glioma
。访问日期:2023 年 7 月 9 日。创作共用许可证链接:
creativecommons.org/licenses/by/4.0/
机器学习工程师的必备工具
从技术和生产力两个方面
·
关注 发表在 Towards Data Science ·4 min read·2023 年 10 月 19 日
–
图片由 JESHOOTS.COM 提供,来源于 Unsplash
大约四年前,我将职业从 SAP 顾问转为数据科学家。在遵循我为自己设计的课程之后,我在一年内获得了一家初创公司的机器学习工程师职位。
将我过去四年所学到的知识、使用过的工具和经历凝结成一篇文章并不容易。不过,我会重点介绍那些特别对我有帮助的内容。
随着我角色的进展,我通过使用各种工具和在工作中遵循一些最佳实践来培养软件开发技能:
-
Git 与版本控制
-
编写可读且清晰的代码
-
探索不同的开发工具
我不仅会在本文中解释它们,还会提到它们如何帮助我提高软技能和生产力。让我们开始吧🚀
Git 与版本控制
Git是一个开源版本控制系统,广泛用于软件开发。它组织项目并管理开发者之间的协作。我在独自工作时并没有使用 Git,而是手动管理我的代码和笔记本🙃
当涉及到协作时,Git 变得必不可少。它有助于跟踪项目进展,并促进协作。
这是一个广泛的学习主题,很多优秀的资源可供参考(比如这个)。今天,我将重点讲解“commit”这个术语,以及它如何帮助我整理思路。
Git 提交就像是对你代码的一个时间快照。
我在早期学习的第一件事之一就是要有组织的 Git 提交和简洁的提交消息。
后来我意识到,提前考虑你的提交及其结构也有助于你组织工作并用更好的逻辑模式设计代码。
这是一个例子,展示了如何在数据科学的背景下组织 Git 提交,来自我最近的一个项目:
作者提供的图片
从协作的角度来看,分解提交——每次代码更改应有一个目的——也将帮助你的同事更快地审查你的代码。
编写可读且清晰的代码
我之前的导师曾经告诉我:“即使是你奶奶也应该能读懂你的代码”——免责声明:这不是从年龄歧视的角度出发,而是意味着每个人都应该能够轻松阅读和理解你的代码。
开玩笑说,反映你的思维过程在代码中并编写自解释的代码将帮助任何人更快地审查和理解你的工作。
我通过工作和阅读几本行业内的书籍学会了如何编写可读且清晰的代码:
-
《代码整洁之道》 由罗伯特·C·马丁著作
-
《软件设计哲学》 由约翰·奥斯特豪特著作。
“所以如果你想快速完成任务,如果你希望你的代码容易编写,就让它易于阅读。”
罗伯特·C·马丁。
以及我最近工作中这一做法的实现:
相信我,你的未来自己会感谢你编写可读且清晰的代码,不仅仅是你的团队!
探索不同的开发工具
当涉及到机器学习模型的实验或原型测试其可行性时,使用笔记本通常是首选。
而 Jupyter 笔记本是一个很好的媒介。
在担任机器学习工程师角色之前,我主要使用 Jupyter 笔记本进行项目开发。当时,我的前团队强制使用 PyCharm,这使我首次接触到集成开发环境(IDE)。
作为一个主要使用一次性笔记本解决方案的数据科学家,我对 PyCharm 的众多功能和用户界面感到有些不知所措。
随着时间的推移,PyCharm 已经成为我的第二天性工具。
其代码补全和错误高亮功能已经成为我不可或缺的助手,为我的生产力做出了很大的贡献。
在 IDE 中掌握 Git 大大提高了我组织工作的能力,加快了我的编码速度,并使我能更高效地专注于手头的任务。此外,每当我几个月后重温代码时,我总是对自己编写的干净代码心怀感激😉
感谢您的阅读!这篇博客文章对我而言具有特别的意义。它标志着我重返博客和技术写作的旅程🤗
对于评论或建设性的反馈,您可以通过回复、Twitter 或 Linkedin 联系我!
机器学习与优化算法的结合
原文:
towardsdatascience.com/a-marriage-of-machine-learning-and-optimization-algorithms-e6c680454f06
模式检测和模式利用如何将彼此提升到一个新的层次
·发布于 Towards Data Science ·阅读时间 12 分钟·2023 年 12 月 2 日
–
我们不应将基准优化和机器学习算法相互比较,而应考虑它们如何相互加强 [图片由 Wedding Dreamz 提供,在 Unsplash 上]
尽管我们大多数人看不到,但优化算法(OAs)无处不在。它们为我们的超市规划货架,为机场制定航班时间表,并为我们提供前往度假目的地的最短路线。特别是精确算法在利用已知结构方面表现优异 — 例如,凸结构 — 即使在具有众多约束的大型决策空间中也能找到解决方案。在过去几十年中,硬件和算法的改进相结合,带来了数百万倍的速度提升。一个可能在 90 年代需要几个月才能完成的规划任务,如今可能只需一秒钟。
同样,机器学习(ML)在过去十年左右取得了惊人的进展。MuZero 展现了在不知道游戏规则的情况下学习超人类游戏策略的能力,图神经网络学习了人眼无法感知的复杂关系,而变换器(Transformers)催生了 ChatGPT 及其竞争对手。这些算法的共同点在于它们都能够从环境中检测模式,无论是文本数据库还是视频游戏。新颖且极其复杂的架构不断被引入,通常解决新的问题并提供无与伦比的性能。尽管取得了诸多成功和突破,对于许多现实世界的问题,端到端的机器学习仍难以取得竞争性结果。定制的优化算法通常仍然优于机器学习,但可能需要大量的计算时间。
这两种方法并不需要竞争。有趣的是,优化算法擅长于利用模式,而机器学习则在检测模式方面表现出色。 不如将它们作为补充的两个部分结合起来,而不是对比它们的优劣,是否更有意义?
在合并优化和机器学习时,通常归结为统计学习被用来以某种形式改进优化例程。这样,我们可以通过利用学到的模式来加速搜索。这种集成解决方案的开发近年来已成为一个新兴的研究领域,未来有许多令人兴奋的可能性。
我们如何将机器学习和优化算法结合起来?
我们已经确定了优化算法擅长利用结构,而机器学习擅长检测结构,因此它们之间存在自然的协同效应。然而,具体来说,什么构成了优化算法与机器学习之间的结合?广义上,我们可以将其分类为以下四个类别:
I. 优化算法(OA)为机器学习(ML)提供输入。 优化算法可能提供一个启发式解决方案,该方案可以通过机器学习进一步改进,或者它可能在算法管道中执行计算密集型的预处理步骤。
II. 机器学习(ML)为优化算法(OA)提供输入。 例如,机器学习可以建议一个用于热启动的起始解决方案,或者学习问题结构供优化算法利用。
III. 机器学习(ML)用于加速优化算法(OA)。 机器学习可以用于迭代检测结构,帮助优化算法更快地找到解决方案。
IV. 优化算法(OA)解决机器学习中的子例程。 像树搜索或动作空间评估这样的例程可以通过优化算法高效执行。
让我们对此进行一些实际的解释。由于数据记录和利用的增加,算法管道变得越来越复杂。它们通常由多个任务组成,有些更适合优化算法,有些则更适合机器学习。因此,一个范式向另一个范式提供输入已经变得非常自然。
此外,公司通常会重复解决特定问题的变体。例如,运输公司可能每天解决一次车辆路径问题,处理在客户、时间窗口和负载大小方面变量但可比的实例。如果通用求解器能够利用这些相似性,优化算法的运行效率将会提高。
最后,由于机器学习作为端到端的范式通常尚未与优化算法相竞争,因此优化某些子程序往往会有所帮助。这通常会加速机器学习算法并增强其竞争力。
一般来说,当以下情况成立时,结合优化算法和机器学习是有意义的:
-
优化算法(或人类专家)在合理的时间内提供解决方案的速度太慢。
-
启发式解决方案仍有改进的空间,或者尚不清楚它们实际效果如何(好或差)。
-
仍需识别良好的解决方案。
-
需要一种快速的近似解决方案。
-
算法流程涉及模式检测和模式探索的元素。
我们将优化算法和机器学习进行背景化处理,然后提供一些说明性的例子。
优化算法
有必要简要介绍优化算法。为了与机器学习集成一致,我们粗略地将其分类为精确算法(最优解但速度较慢)和启发式方法(次优解但更快)。
精确算法
精确算法是用于优化问题的强大技术,能够在可行的解决方案空间内找到可证明的最优解。通常,问题被表述为数学规划(例如,线性或二次规划),可以使用如 CPLEX、SCIP 或 Gurobi 等优化软件进行求解。这些求解器系统地探索解决方案空间,并保证找到最佳解。
尽管现成的方案功能强大,但可以处理的解决方案空间是有限的。对于非常大的问题,我们通常需要投入大量设计工作来设计分支、定价和/或裁剪方案,以减少搜索复杂性并有效地导航解决方案空间,同时保留最优性保证。
启发式方法
启发式方法提供了一种替代问题解决方案,以牺牲最优性保证换取计算效率。启发式方法特别适用于大规模或复杂问题,这些问题在合理的时间框架内寻找最优解是不切实际的。
基本启发式方法提供快速的经验规则解决方案。它们通常会产生次优结果,但通常会在有限的计算时间内返回不错的解决方案。例子包括最近邻插入或 2-opt 交换。
元启发式方法采用更高层次的指导搜索解决方案的策略,通常包含随机性和适应性元素。例子包括遗传算法、模拟退火和粒子群优化。元启发式方法在调整工作和运行时间方面更为密集,但通常能提供比基本启发式方法更优的结果。
机器学习
我们讨论了两种机器学习范式;演示学习和经验学习。尽管与现有分类(无论是监督学习还是强化学习)密切相关,我们在这里旨在更多地连接到优化背景。
演示学习
演示学习通过最小化提供的专家策略与预测策略之间的距离(监督学习)来进行。它可以被视为一个师生模型,其中学生(机器学习模型)旨在模仿老师(例如,精确算法、量身定制的启发式方法或人工制定的解决方案)。需要注意的是,这种学习形式因此需要某种形式的理论或经验知识。
机器学习模型预测的解决方案与已知专家解决方案之间的差异允许计算损失函数,算法随后旨在最小化这个损失函数。如果机器学习算法展示了许多高质量解决方案的例子,我们希望它最终能够复制这些解决方案。
演示学习的缺点在于它无法超越专家策略。如果专家策略是次优的,那么预测的策略也是如此。因此,演示学习只有在获取原始解决方案花费时间过长且希望在较短时间内获得相同解决方案时才有意义。然而,如果生成示例解决方案需要过长时间,这个想法也会失败。这使得演示学习处于一种相对尴尬的境地。
演示学习是一种模仿学习。我们尝试更快地再现专家提供的相同决策。
在演示学习中,机器学习算法旨在尽可能接近专家解决方案[照片来源于Andre Mouton于Unsplash]
经验学习
与演示学习相对,经验学习利用通过部署策略获得的观察数据,旨在改进这些策略。这种学习形式通常与强化学习(RL)相关联,通过观察从环境中获得的奖励迭代执行和改进策略。
与示范学习相比,经验学习不需要关于解决方案质量或结构的先验知识。缺点是——在其最纯粹的形式下——它不利用有关环境的信息,仅通过状态、动作和奖励与环境进行交互。与经验学习相关的常见问题包括陷入局部最优、需要适当地定义奖励以及广泛探索解决方案空间。
经验学习利用来自环境的反馈信号,以便随着时间的推移改进其决策政策。
在经验学习中,ML 算法旨在通过与环境的交互来改进其决策政策 [照片由 Alex Kondratiev 提供,在 Unsplash 上]
请注意,这两种学习形式都有显著的缺点,这在一定程度上解释了为什么端到端机器学习在现实世界问题上仍然面临困难。因此,OA 和 ML 的集成确实是互利的。
当然,可以在一个单一的流程中应用这两种学习形式,或者说这可能更为优越。一个例子是(i)通过示范学习生成初始解决方案,(ii)通过优化求解器进行改进,以及(iii)利用经验学习来指导对最优解决方案的搜索。
MO-OA 示例
接下来是一些具体的例子。以下是基于学术研究的三个代表性例子。
I. 学习分支
也许最为研究透彻的例子是集成了 OA 和 ML 的分支学习,或者更具体地说,是利用 ML 来指导分支限界算法。对于那些不熟悉这个概念的人:分支限界通过系统地探索一个非常大的搜索树来解决整数问题,根据既定的界限修剪那些已被证明是次优或不太有希望的分支。如果子问题的松弛解不包含最优解,那么它的相邻整数也不会包含。
尽管分支限界是一个著名的算法,用于在大动作空间中寻找最优解,但在幕后仍然会做出一些相当基本的启发式选择:
-
变量选择:下一步要分支的变量。一个常见的决策规则是简单地选择最“接近 0.5”的变量进行分支。
-
节点选择:首先处理当前开放的节点中的哪个。极端的做法是总是探索最有希望的节点(最优先)或总是完全探索一个节点的子树(深度优先)。
分支限界过程的例子。选择下一个要探索的节点和要分支的变量通常基于启发式规则 [图片来自 WikiMedia]
说实话,这种简化并未能公正地反映多年来开发出的更复杂的启发式算法。然而,尽管有最终找到最优解决方案的保证,实际搜索过程并没有像预期的那样智能,这是通用求解器的代价。
鉴于树搜索是顺序的且高度动态,用动态学习基础的规则替代静态启发式规则似乎是合理的。例如,节点、分支和树的动态特征可以设计用来预测合适的变量和节点,其值由机器学习进行学习。
过去几年提出了许多解决方案,从离线示范学习到在线经验学习。一个直观的例子是上置信界,基于节点的感知价值和访问次数来平衡探索和利用。
学习分支的例子说明了机器学习可以在精确算法的背景下应用,可能会加快搜索过程,而不牺牲 最优性保证。
基于 Lodi & Zarpellon (2017) 和 Balcan 等 (2018) 的示例
II. 使用 GNN 进行路由学习
路由问题的实例通常通过图表示。客户位置和旅行距离是这些图中明显的属性,但节点和弧线可以有各种标签,例如时间窗口和交通密度。图神经网络(GNNs)是一种强大的技术,用于嵌入并有意义地表示这些图在问题背景中。
车辆路由问题是一个经典的组合优化问题,随着节点数量的增加,规模呈指数爆炸增长 [图片来自 WikiMedia]
传统上,路线是使用精确求解器(例如 Concorde TSP Solver)或元启发式算法(例如混合遗传搜索)生成的。然而,这些求解器需要相当长的运行时间,精确算法在面对大型实例时完全无法提供解决方案。
与其每次遇到新的问题实例时运行优化算法数小时,不如通过检测其解决方案中的模式来学习复制它们。这个例子是一种示范学习,试图最小化计算密集型最优解决方案与机器学习生成的解决方案之间的性能差距。在这个过程中会有一定的质量损失,但解决方案可以在更友好的时间框架内生成。
通过对解决方案运行 GNNs,可以估计边被纳入解决方案的概率。随后,构建一个稀疏的热图,保留最有前景的边。根据这个热图,运行一个动态规划子程序进行逐步构建路线。如果某条路线被其他路线主导,可以进行剪枝。
基于 Kool 等人(2019)和 Kool 等人(2022)的示例
III. 大动作空间的数学编程
在这个最终案例中,数学编程作为强化学习中的一个子程序被部署,目的是处理大动作空间。
组合优化问题有迅速规模爆炸的趋势,例如,访问一组节点的序列数是节点的阶乘。它们通常需要处理许多约束,如卡车的容量和最大驾驶时间或客户节点的时间窗口。枚举所有动作并进行可行性检查变得非常繁琐。
如果问题结构允许,一阶决策问题可以被形式化为线性规划。这种程序可以非常高效地解决,大大扩展了传统 RL 算法——依赖于明确枚举——所能处理的动作空间大小。
线性规划可以高效地探索带约束的大动作空间 [图片来源 WikiMedia]
OA 与 ML 的整合并未止步于此。接下来,考虑 Q 值的近似。在许多现代 RL 应用中,这通常通过深度 Q 网络来完成,该网络通过定义在其输入上执行非线性变换。然而,ReLU 激活函数可以通过分段线性函数嵌入到目标函数中,支持一组附加约束。
另一个 OA 层?我们可以施加领域限制和局部分支来控制动作空间,仅探索我们怀疑(基于 Q 值)最佳动作所在的邻域。
基于 Van Heeswijk, W.J.A. & La Poutré(2020)和 Akkerman 等人(2023)的示例
正如你所见,机器学习与优化算法的整合机会广阔而多样。这已经是一个活跃的研究领域,潜力尚未完全释放。
总结
-
机器学习和优化算法在计算时间、性能保证、知识利用等方面都有缺陷。鉴于这两种范式的性质,它们可能自然互补。
-
机器学习专注于模式检测,优化算法专注于模式利用。它们可以结合起来构建强大的算法管道。
-
在实践中,公司通常会重复解决单一问题,并且这些问题的变异性相对有限。从解决方案数据中提取模式(即统计学习)可以提升现有的精确方法或启发式方法。
-
机器学习可以以示范学习的形式使用(在极短时间内近似优化算法的解决方案)或经验学习的形式使用(与环境互动以迭代地增强策略)。
你可能还会喜欢以下文章:
在强化学习中,大规模和高维的动作空间通常是计算瓶颈。制定……
towardsdatascience.com ## 五种处理强化学习中大规模动作空间的方法
动作空间,特别是在组合优化问题中,可能会变得庞大不便。本文讨论了……
towardsdatascience.com
对于那些对组合优化中的机器学习感兴趣的人,我热情推荐以下由 Andrea Lodi 教授做的主题演讲:
参考文献
Akkerman, F., Luy, J., Van Heeswijk, W.J.A., & Schiffer, M. (2023). 通过动态邻域构造处理大规模离散动作空间。arXiv 预印本 arXiv:2305.19891。
Balcan, M. F., Dick, T., Sandholm, T., & Vitercik, E. (2018 年 7 月). 学习分支。见 国际机器学习会议 (第 344–353 页)。PMLR.
Khalil, E., Le Bodic, P., Song, L., Nemhauser, G., & Dilkina, B. (2016 年 2 月). 在混合整数规划中学习分支。见 人工智能协会会议论文集(第 30 卷,第 1 号)。
Kool, W., Van Hoof, H., & Welling, M. (2019). 注意,学习解决路线问题!国际学习表示大会 2019。
Kool, W., Van Hoof, H., Gromicho, J., & Welling, M. (2022 年 6 月). 针对车辆路线问题的深度策略动态规划。在 国际约束编程、人工智能和运筹学整合会议 (第 190–213 页)。Cham: Springer International Publishing.
Lodi, A., & Zarpellon, G. (2017). 关于学习和分支的综述。Top, 25, 207–236.
Parmentier, A. 和 T’kindt, V.(2023)。基于结构学习的启发式方法解决具有释放时间和完成时间总和的单台机器调度问题。欧洲运筹学期刊,305(3),1032–1041。
Santana, Í., Lodi, A., 和 Vidal, T.(2023 年 5 月)。车辆路径中的局部搜索和交叉的神经网络:可能的过度杀伤?在约束编程、人工智能与运筹学集成国际会议(第 184–199 页)。Cham:Springer Nature Switzerland。
Van Heeswijk, W.J.A. van 和 La Poutré, H.L.(2020 年 12 月)。线性离散动作空间中的深度强化学习。在2020 年冬季模拟会议(WSC)(第 1063–1074 页)。IEEE。
Vazacopoulos, A.(2020)。使用 Python 结合机器学习和数学优化集成。通过 LinkedIn。
数据建模与设计的成熟度模型
数据定义标准化掌握的最佳实践
·
关注 发表在 Towards Data Science ·9 分钟阅读·2023 年 4 月 25 日
–
照片由 Clint Adair 拍摄,来自 Unsplash
作为曾在美洲各主要金融机构与所谓的主模型师合作的人,我有幸目睹数据建模如何帮助标准化组织定义和描述其数据的方式。我亲眼见证了一个健全的数据模型如何加速了数据目录的实施和数据质量项目的启动,以及缺乏数据模型如何导致类似项目的自我毁灭。
从这一经验中,这种观点探讨了组织如何通过关注三个关键子能力来提高数据建模能力:元建模、概念和逻辑数据建模,以及物理数据建模。对于每个子能力,它将描述高成熟度的表现,并总结整体最佳实践和成功因素。
数据建模的子能力
让我们从数据建模的基础组件开始:
-
元建模—— 元建模是数据建模的一个子能力,它涉及创建一个定义其他模型的结构、概念和关系的模型。元模型提供了一种标准化的方式来定义和描述模型及其组件,这有助于确保在这些模型的开发和使用中保持一致性和清晰性。元模型跨越概念模型、逻辑模型和物理模型,并定义了这些组件如何一致地连接在一起。
-
概念和逻辑数据建模—— 概念和逻辑数据建模是数据建模的子能力,涉及创建数据的业务导向视图,以捕捉特定领域中的主要实体、关系和属性,例如顾客、员工和产品。逻辑数据建模通过添加更多细节来完善概念模型,如指定数据类型、键和实体之间的关系,并将概念领域细分为逻辑属性,如顾客姓名、员工姓名和产品 SKU。
概念模型在最高层次上解释了各个领域或概念是什么,以及它们之间的关系。在这个示例中,你可以看到事务形成了一个关键概念,其中每个事务可以与被售出的产品、购买它们的顾客、支付方式和购买地点的商店相关联——这些都构成了各自的概念。此外,连接器显示,每个单独的事务至多可以有一个顾客、商店或员工相关联,但这些反过来可以与多个事务相关联——这当然是有意义的。
在逻辑模型中,概念(或领域)被细分为逻辑属性。这种逻辑模型与底层元模型一致,现在可以被转换为物理模型,在其中详细说明这些数据属性的确切位置(例如,在哪个表中)和格式。
- 物理数据建模 — 物理数据建模涉及将逻辑数据模型转换为可以在特定技术平台上实现的数据库模式。这包括定义表、列、索引和其他数据库对象,以及指定数据类型、约束和实现模型所需的其他细节。
在物理模型中,进一步的技术细节会被添加到逻辑模型中,例如澄清表名、列名和数据类型。
好的表现是什么样的
数据建模的高成熟度要求你从多个维度进行思考:
-
战略 — 组织的整体数据建模战略,包括数据建模工作与业务目标和目标的一致性。
-
人员/人才 — 具体角色及其责任的明确,以及所需的专业知识、技能和培训。
-
流程 — 包括数据建模方法论的文档、数据建模模板和标准的制定以及质量控制和审查流程的建立。
-
技术 — 支持数据建模工作的工具,如数据建模软件和工具,以及数据建模工具与其他系统和应用程序的集成。
-
采纳 — 数据建模实践在组织内外的采纳和使用。这可能包括社交化程序、解决采纳障碍以及跟踪指标以衡量数据建模工作的有效性和影响。
让我们将这些应用到我们定义的三个子能力上。
元模型
-
高成熟度的元建模涉及拥有一个定义明确且被广泛采纳的元模型,该模型在整个组织中一致使用。确认一下——你需要一个,仅一个。如果没有元模型(可以把它看作是通用语言的基本语法和词汇),各种数据模型(方言甚至完全不同的语言)可能会出现,导致跨领域、跨过程和跨系统的解释性和互操作性问题。
-
你需要懂得元模型的人,并且能够维护和解释它。建议有一个人对元模型拥有最终的权威。他或她可以接受反馈并收集变更请求,以确保它符合并保持适用。
-
应该有一个定义明确的过程来描述如何在建模活动中使用元模型。它的采纳应该在数据建模人员、数据架构师和数据工程师中广泛进行——实际上,它应该让他们的工作变得更容易,因为他们有了明确的起点。
-
如数据目录或元数据管理系统等工具可以提供一个集中式存储和管理元模型的库,并支持协作和版本控制,但这并非绝对必要。一些专家可能不同意我的观点,但许多完全适用的元模型存在于如 Microsoft Excel、PowerPoint 或 Visio 这样的工具中,根据你组织的规模和复杂性,这也可以是适当的只要你的概念和逻辑数据建模人员严格遵守。
概念和逻辑数据建模
-
高成熟度涉及对概念和逻辑数据建模有一个定义明确且一致的方法,严格遵守元模型。概念领域应有所有者,并且应有一个结构化的流程来创建新的逻辑属性,然后进行审查、批准和发布。
-
建模领域和逻辑属性是一项技能——且是一项罕见的技能。这需要核心的数据建模专长以及将其投射到实际业务领域的能力,以用逻辑属性描述业务和技术组织都能理解的内容。不要低估这有多难——成熟的组织通常有一个“首席建模师”来维护和传播最佳实践(Paul Carey,在想念你!)。
-
概念数据模型应(明确地)与企业或业务架构对齐,并与数据所有权分配的数据领域对齐。
-
数据建模软件可用于创建和维护大规模的概念和逻辑数据模型。这些工具可以提供模型的可视化表示,并支持协作、版本控制以及与其他系统(如数据目录或元数据管理系统)的集成。业务术语表可用于定义和标准化模型中使用的业务概念和术语。
物理数据建模
-
物理数据建模的高成熟度水平通常涉及具有良好设计和高效的数据库模式,这些模式满足适用的性能和可扩展性要求。这需要能够设计和实施模式的人员、定义良好的模式开发和维护流程以及支持模式设计和实施的适当技术工具。
-
数据库设计软件可用于创建和维护物理数据模型。这些工具可以从逻辑数据模型生成数据库模式,并支持协作、版本控制以及与其他系统(如数据目录或元数据管理系统)的集成。数据字典也可用于定义和标准化技术细节,如数据类型、约束及其他数据库对象。
高级最佳实践和成功因素
为了提升他们的数据建模能力,组织可以遵循一些最佳实践和成功因素:
-
首先确保元模型正确。元模型推动整个企业的可重用性和一致性。它确保所有后续的建模工作逐步构建整体模型。如果没有一个合适的元模型,未来就要面对调整和桥接现有不兼容模型的艰巨任务。
-
考虑预先制作的行业或通用模型。根据您在旅程中的位置,您可以考虑采用现有的数据模型。这可以推动与国际最佳实践和标准的对齐,节省您构建完全新模型的时间和精力,并实现与外部方的高效可靠数据交换。例如,BIAN提供了一个标准化的银行服务参考模型,定义了银行业的通用语言、分类法和业务流程框架。
-
在概念、逻辑和物理层面之间进行迭代。数据建模需要时间 —— 这项工作永远不会完成。建议优先考虑领域 —— 像客户和产品这样的参考领域是很好的候选对象 —— 并从中选择 1 或 2 个,首先完成逻辑模型,然后是物理模型的指导,然后再转向下一个领域。
-
不要过度强调物理层面。数据建模可能复杂、耗时,因此昂贵。完成基本的概念和逻辑模型几乎肯定是值得的,但一旦涉足物理领域,您可能不需要集中管理和捕获所有物理模型。在这里也可能需要优先考虑 —— 例如,确定“关键任务”系统并为其文档化物理模型,但对于其他系统,只需确保本地应用所有者遵守特定的建模规范和标准可能就足够了。
-
战略性地实施技术。它们可能很昂贵,您可能在第一个领域不需要它们,但最终您的数据模型将在尺寸和复杂性方面成倍增长。考虑数据目录、业务词汇表和数据字典,或者能同时兼顾所有这些功能的工具。如果没有这些工具,消费(因此价值创造)将很差。
最后一个建议是找到并赋予主模型师权力,这值得单独一节来详述。
主模型师的角色
照片由Usman Yousaf拍摄于Unsplash。
“高级建模师”是数据建模领域的专家,拥有对建模技术和工具的深入理解,能够定义最佳实践,并具备商业敏锐性,不让“完美”分散对“良好”的关注。这样的人可以负责并引导上述提到的五项其他建议。
此外,高级建模师可以为组织中的所有逻辑和物理数据建模师提供指导和支持。这可以通过创建可重用的文档,如上述的元模型,也可以包括基本的数据建模蓝图和说明手册来实现。高级建模师还可以通过定义标准和指南、促进培训来保护和传播最佳实践。通过保持对最新趋势和进展的了解,他们可以向组织引入新的技术和工具,推动持续改进。他们还可以建议是否利用现有的、预制的数据模型,而不是从头开始构建。
数据建模是设计或优化解决方案、产品和服务过程中至关重要的输入。业务团队可以从高级建模师提供的支持中受益,因为这将加速他们的工作,并最终提高组织数据模型的整体质量和一致性。
下一步
数据建模是一项关键能力,有助于组织确保其数据设计良好、一致,并有效支持业务需求。通过关注诸如元建模、概念和逻辑数据建模以及物理数据建模等子能力,组织可以提升其成熟度,并使业务用户和数据科学家能够持续找到适合其各自用例的正确数据。如果这一切听起来一下子难以承担,你的第一步可能是找到一位高级建模师。
参考文献和进一步阅读推荐
-
通用数据模型,Microsoft。
-
BIAN 文档,银行业架构网络。
-
内容元模型,TOGAF。
-
什么是数据建模?, TechTarget。
-
Oracle 零售数据模型的逻辑数据模型,Oracle。
-
FIBO 本体, EDM Council。
-
数据仓储 — 数据建模,TeradataPoint。
-
如何进行基于战略的数据成熟度评估,ZS。
所有图片(除非另有说明)均由作者提供。
对分析流处理的简要介绍
构建可靠分布式系统的架构基础。
·
关注 发表在 Towards Data Science ·23 分钟阅读·2023 年 8 月 15 日
–
分布式流数据网络是无限的,并且以惊人的速度增长。图片由 作者的 MidJourney 创建
流处理的基础
基础是结构建立的坚固、牢不可破的基础。在构建成功的数据架构时,数据是整个系统的核心要素和基础的主要组成部分。
鉴于数据流入我们的数据平台的最常见方式是通过像Apache Kafka和Apache Pulsar这样的流处理平台,本文涵盖了这一主题领域。
因此,确保我们(作为软件工程师)提供清洁的能力和无摩擦的保护措施,以减少数据进入这些快速流动的数据网络后的数据质量问题,是至关重要的。
这意味着建立围绕我们数据的架构(类型和结构)、字段级*可用性(可为空等)和字段类型有效性(预期范围等)*的 API 级合同,成为我们数据基础的重要支撑,尤其是在当今现代数据系统的去中心化、分布式流性质下。
然而,为了达到建立盲目信任——或高信任数据网络的程度,我们必须首先建立智能系统级设计模式。
构建可靠的流数据系统
作为软件和数据工程师,构建可靠的数据系统实际上是我们的工作,这意味着数据停机时间应像业务的其他组件一样进行测量。你可能听说过SLAs、SLOs和SLIs这些术语。这些缩写词与合同、承诺和实际度量相关,帮助我们评估端到端系统的表现。
作为服务负责人,我们将对自己的成功与失败负责,但通过一些前期的努力,标准化的元数据、共同的标准和最佳实践可以确保各方面的运行顺利。
此外,相同的元数据还可以提供关于我们数据在传输过程中质量和可信度的宝贵见解,直到数据找到其终端区域休息。数据的血统本身就讲述了一个故事。
采用拥有者心态
例如,你的团队或组织与客户(包括内部和外部客户)之间的服务水平协议(SLAs)用于创建一个关于你所提供服务的有约束力的合同。对于数据团队来说,这意味着根据你的服务水平目标(SLOs)识别和捕捉指标(KPMs — 关键绩效指标)。SLOs 是你基于 SLAs 打算遵守的承诺,这可以是从接近完美(99.999%)的服务正常运行时间(API 或 JDBC)的承诺,到简单的承诺,例如某特定数据集的 90 天数据保留。最后,你的服务水平指标(SLIs)是你按照服务水平合同运营的证明,通常以操作分析(仪表板)或报告的形式呈现。
知道我们想要去哪里可以帮助制定到达那里的计划。这段旅程从数据的插入(或摄取点)开始,特别是每个数据点的正式结构和身份。考虑到“越来越多的数据通过像 Apache Kafka 这样的流处理平台进入数据平台”的观察,编译时保证、向后兼容性和快速的二进制序列化都对这些数据流中的数据至关重要。数据责任本身就是一个挑战。让我们看看原因。
管理流数据责任
流处理系统全天候运行,每周 7 天,每年 365 天。如果没有在前期投入适当的工作,这可能会使问题复杂化,其中一个时常出现的问题是数据损坏,即飞行中的数据问题。
处理飞行中的数据问题
有两种常见的方法可以减少飞行中的数据问题。首先,你可以在数据网络的边缘引入门卫,使用传统的应用程序编程接口(APIs)来协商和验证数据;或者作为第二种选择,你可以创建和编译辅助库或软件开发工具包(SDKs),以强制执行数据协议并使分布式写入者(数据生产者)进入你的流处理数据基础设施,你甚至可以同时使用这两种策略。
数据门卫
在数据网络的边缘(前端)添加网关 API 的好处在于,你可以在数据生产点执行认证(这个系统能访问这个 API 吗?),授权(这个系统能否将数据发布到特定的数据流中?),以及验证(这些数据是否可接受或有效?)。下图 1–1 展示了数据网关的流动情况。
图 1–1:一个分布式系统架构图,显示了数据摄取网关的认证和授权层。从左到右,批准的数据被发布到 Apache Kafka 中进行下游处理。图像来源 Scott Haines
数据网关服务充当你受保护(内部)数据网络的数字门卫(保镖)。其主要角色是控制、限制甚至阻止未经认证的访问(见上图 1–1 中的 API/服务),通过授权哪些上游服务(或用户)可以发布数据(通常通过使用服务ACLs来处理),以及提供的身份(比如服务身份和访问IAM,网络身份和访问JWT,以及我们老朋友 OAUTH)。
网关服务的核心责任是验证传入的数据,在发布潜在的损坏或一般性差的数据之前。如果网关正确地执行其职责,只有“良好”的数据才会通过并进入数据网络,这个网络是事件和操作数据的传输通道,通过流处理进行消化,换句话说:
“这意味着产生数据的上游系统可以在生成数据时快速失败。这可以阻止损坏的数据进入流数据或静态数据管道,并通过错误代码和有用的消息以更自动的方式与生产者建立对话,了解具体原因和问题。”
使用错误消息提供自助解决方案
好与坏体验的差异在于从坏到好的转变所需的努力程度。我们都可能曾经与或工作过,或听说过,服务出现无缘无故的故障(空指针异常抛出随机 500)。
为了建立基本的信任,一点点就足够了。例如,从 API 端点获取 HTTP 400,并附带以下消息体(见下文)
{
"error": {
"code": 400,
"message": "The event data is missing the userId, and the timestamp is invalid (expected a string with ISO8601 formatting). Please view the docs at http://coffeeco.com/docs/apis/customer/order#required-fields to adjust the payload."
}
}
提供了 400 的原因,并赋予向我们(作为服务拥有者)发送数据的工程师修复问题的能力,无需安排会议、响起警报或在 Slack 上联系每个人。当你可以时,请记住,每个人都是人,我们喜欢闭环系统!
数据 API 的利弊
这种 API 方法有其优缺点。
优点是大多数编程语言与 HTTP(或 HTTP/2)传输协议即开即用——或者通过添加一个小库——并且 JSON 数据现如今几乎是最通用的数据交换格式。
从另一方面(缺点)看,可以说对于任何新的数据领域,还有另一个服务需要编写和管理,如果没有某种形式的 API 自动化,或遵循像OpenAPI这样的开放规范,每个新的 API 路由(端点)最终都会花费更多的时间。
在许多情况下,未能以“及时”的方式更新数据摄取 API,或者扩展和/或 API 停机、随机故障,或只是人员沟通不畅,为人们绕过“愚蠢”API 提供了必要的理由,进而尝试直接将事件数据发布到 Kafka。虽然 API 可能会觉得阻碍,但在数据质量问题如事件损坏或意外混合事件开始破坏流处理梦想后,保持一个公共的门卫有强烈的论据。
要彻底解决这个问题(并几乎完全消除它),良好的文档、变更管理(CI/CD)以及包括实际单元测试和集成测试在内的一般软件开发卫生——能够实现快速的功能和迭代周期,而不会降低信任度。
理想情况下,数据本身(模式/格式)可以通过启用字段级验证(谓词)、生成有用的错误信息并维护自身利益,来制定其自身数据层合同的规则。嘿,凭借一些路由或数据级元数据和一些创造性思维,API 可以自动生成自定义的路由和行为。
最后,网关 API 可以被视为集中式的麻烦制造者,因为每次上游系统未能发出有效数据(例如,被门卫阻挡)都会导致有价值的信息(事件数据、指标)被丢失。这里的责任问题也往往是双向的,因为门卫部署不当可能使一个未设置为处理网关停机(即使是几秒钟)的重试的上游系统失明。
抛开所有优缺点不谈,使用网关 API 来阻止腐败数据在进入数据平台之前的传播意味着,当出现问题时(因为问题总是会出现),问题的表面面积将减少到某个特定服务。这肯定比调试分布式的数据管道、服务,以及无数的最终数据目标和上游系统要好,因为那样你要弄清楚坏数据是由公司中的“某人”直接发布的。
如果我们去掉中间人(网关服务),那么治理“预期”数据传输的能力将落在“库”这一形式的专用 SDKs 上。
软件开发工具包(SDKs)
SDKs 是导入到代码库中的库(或微框架),用于简化某个操作、活动或其他复杂操作。它们也被称为 客户端。以之前提到的使用良好错误信息和错误代码为例。这个过程是必要的,用于 通知客户端 他们之前的操作是无效的,但直接将适当的保护措施添加到 SDK 中可以减少潜在问题的表面面积。例如,假设我们有一个 API 设置来通过事件跟踪跟踪客户的咖啡相关行为。
通过 SDK 保护措施减少用户错误
理论上,客户端 SDK 可以包含 管理与 API 服务器交互所需的所有工具,包括身份验证、授权,而对于验证来说,如果 SDK 发挥作用,验证问题将迎刃而解。以下代码片段展示了一个可以可靠地跟踪客户事件的 SDK 示例。
import com.coffeeco.data.sdks.client._
import com.coffeeco.data.sdks.client.protocol._
Customer.fromToken(token)
.track(
eventType=Events.Customer.Order,
status=Status.Order.Initalized,
data=Order.toByteArray
)
通过一些额外的工作(即客户端 SDK),数据验证或事件损坏的问题几乎可以完全消除。额外的问题可以在 SDK 内部处理,例如当服务器离线时如何重试发送请求。与其让所有请求立即重试,或在某个循环中无限制地淹没网关负载均衡器,SDK 可以采取更智能的措施,比如采用指数回退。有关当事情出错时会发生什么的深入探讨,请参见“雷鸣之 Herd 问题”!
雷鸣之 Herd 问题
比如说,我们有一个单一的网关 API 服务器。你编写了一个很棒的 API,公司的许多团队都在向这个 API 发送事件数据。事情进展顺利,直到有一天,一个新的内部团队开始向服务器发送无效数据(他们没有尊重你的 http 状态码,而是将所有非 200 的 http 代码视为重试的理由。不过,他们忘记添加任何重试启发式算法,如指数回退,因此所有请求都会无限重试——在一个不断增加的重试队列中)。请注意,在这个新团队加入之前,根本没有理由运行多个 API 服务器实例,也没有必要使用任何服务级别的速率限制器,因为一切都在商定的 SLA 内顺利运行。
非失败鲸。当你解决问题并重新摆脱困境时可能发生的情况。图片来源 Midjourney 通过作者
好吧,那是今天之前的事了。现在你的服务离线了。数据在备份,上游服务正在填充他们的队列,人们很不满,因为他们的服务现在因为你的单点故障而开始出现问题……
这些问题都源于一种资源枯竭形式,称为“雷鸣之 Herd 问题”。当许多进程在等待某个事件时,比如系统资源变得可用,或者在这个例子中,API 服务器重新上线时,就会发生这个问题。现在,所有进程争先恐后地竞争资源,在许多情况下,单个进程(API 服务器)上的负载足以使服务再次离线。不幸的是,这会导致资源枯竭的循环重新开始。除非你能平息“ Herd”现象或将负载分布到更多的工作进程上,从而减少网络负载,使资源再次有空间呼吸。
虽然上面的初始示例更多的是一种无意的 分布式拒绝服务攻击(DDoS),这些问题可以在客户端(通过指数回退或自我限流)和 API 边缘通过负载均衡和速率限制来解决。
最终,如果没有正确的眼睛和耳朵,通过操作指标、监控和系统级别的(SLAs/SLIs/SLOs)警报来实现,数据可能会消失,这可能是一个解决的挑战。
无论你决定在数据网络边缘添加一个数据网关 API,使用自定义 SDK 来保证上游一致性和可靠性,还是决定采取其他方法将数据引入数据平台,了解你的选项总是好的。无论数据以何种方式流入数据流,这篇关于流数据的介绍都不完整,必须适当地讨论数据格式、协议和二进制可序列化数据的话题。谁知道呢,也许我们会发现更好的处理数据可靠性问题的方法!
选择合适的数据协议来完成任务
当你想到结构化数据时,首先想到的可能是 JSON 数据。JSON 数据具有结构,是标准的基于网络的数据协议,而且无论如何,它都非常容易使用。这些都是快速入门的好处,但随着时间的推移,如果没有适当的保护措施,你可能会在标准化 JSON 用于流系统时遇到问题。
与 JSON 的爱恨关系
第一个问题是 JSON 数据是可变的。这意味着作为数据结构它是灵活的,因此也容易脆弱。数据必须保持一致性才能可靠,而在跨网络传输数据的情况下(在传输过程中),序列化格式(二进制表示)应该高度紧凑。使用 JSON 数据,你必须为每个对象的所有字段发送键。这不可避免地意味着你会为每个额外的记录(在一系列对象中)发送大量额外的负荷。
幸运的是,这并不是一个新问题,恰好有针对这些问题的最佳实践,以及关于最佳序列化数据策略的多种思想流派。这并不是说 JSON 没有它的优点。只是在建立一个坚实的数据基础时,结构越多越好,压缩水平越高越好,只要它不消耗大量 CPU 资源。
可序列化的结构化数据
在高效编码和传输二进制数据时,两种序列化框架常常被提及:Apache Avro和 Google Protocol Buffers(protobuf)。这两个库提供了 CPU 高效的行数据结构序列化技术,并且这两种技术还提供了各自的远程过程调用(RPC)框架和功能。让我们先来看avro,然后是protobuf,最后再看看远程过程调用。
Avro 消息格式
使用avro时,你可以通过记录的概念定义结构化数据的声明式模式。这些记录只是 JSON 格式的数据定义文件(模式),存储为文件类型avsc。以下示例展示了 avro 描述符格式中的 Coffee 模式。
{
"namespace": "com.coffeeco.data",
"type": "record",
"name": "Coffee",
"fields": [
("name": "id", "type: "string"},
{"name": "name", "type": "string"},
{"name": "boldness", "type": "int", "doc": "from light to bold. 1 to 10"},
{"name": "available", "type": "boolean"}
]
}
处理 avro 数据可以有两种路径,取决于你在运行时如何工作。你可以采取编译时的方法,或者在运行时按需解决问题。这种灵活性可以增强交互式数据发现会话。例如,avro 最初被创建为一种高效的数据序列化协议,用于在 Hadoop 文件系统中长期存储大型数据集合,以分区文件的形式存储。由于数据通常是从一个位置读取,写入到 HDFS 中的另一个位置,avro 可以每个文件存储一次模式(用于写入时)。
Avro 二进制格式
当你将一组 avro 记录写入磁盘时,该过程会将 avro 数据的模式直接编码到文件中(仅一次)。在 Parquet 文件编码中也有类似的过程,模式会被压缩并作为二进制文件尾部写入。我们在第四章结束时,经历了将 StructField 级文档添加到我们的StructType的过程。这个模式用于编码我们的 DataFrame,并且在写入磁盘时,它在下一次读取时保留了我们的内联文档。
启用向后兼容性并防止数据损坏
在读取多个文件作为单一集合的情况下,当记录之间的模式发生变化时,可能会出现问题。Avro 将二进制记录编码为字节数组,并在反序列化时(将字节数组转换回对象)应用模式。
这意味着你需要额外注意以保持向后兼容,否则你可能会遇到ArrayIndexOutOfBounds异常。
破坏模式承诺也可能以其他微妙的方式发生。例如,假设你需要将某个字段的整数值更改为长整数值。不要这样做。这将破坏向后兼容性,因为从 int 到 long 的字节大小增加。这是由于使用模式定义来定义记录中每个字段的字节数组的起始和结束位置。为了保持向后兼容性,你需要在 avro 定义中保留整数字段,并向模式中添加(附加)一个新字段以供今后使用。
流式 avro 数据的最佳实践
从静态 avro 文件(具有有用的嵌入模式)转移到无限的数据流时,主要的区别在于你需要 带上你自己的模式。这意味着你需要支持向后兼容性(以防需要在模式更改之前和之后回滚和重新处理数据),以及前向兼容性,以防你已经有现有的读取器正在从流中消费数据。
这里的挑战是支持两种兼容性形式,因为 avro 无法忽略未知字段,而这是支持前向兼容性的要求。为了应对 avro 的这些挑战,Confluence 的团队开源了他们的 模式注册表(用于 Kafka),它在 Kafka 主题(数据流)级别启用了模式版本控制。
在不使用模式注册表的情况下支持 avro,你必须确保在更新写入者的模式库版本之前,更新任何活动的读取器(spark 应用程序或其他)。否则,切换开关的那一刻,你可能会发现自己处于事故的开端。
Protobuf 消息格式
使用 protobuf,你使用消息的概念定义结构化数据定义。消息以类似于定义 C 中的结构的格式编写。这些消息文件以 proto 文件名扩展名写入。协议缓冲区具有使用 imports 的优势。这意味着你可以定义常见的消息类型和枚举,这些类型可以在大型项目中使用,甚至可以导入到外部项目中,实现大规模重用。一个使用 protobuf 创建 Coffee 记录(消息类型)的简单示例。
syntax = "proto3";
option java_package="com.coffeeco.protocol";
option java_outer_classname="Common";
message Coffee {
string id = 1;
string name = 2;
uint32 boldness = 3;
bool available = 4;
}
使用 protobuf,你只需定义一次消息,然后为所选择的编程语言编译。例如,我们可以使用来自 ScalaPB 项目的独立编译器生成 Scala 的代码(由 Nadav Samet 创建和维护),或利用 Buf 的卓越,这些工具和实用程序围绕 protobuf 和 grpc 创建了宝贵的工具集。
代码生成
编译 protobuf 可以实现简单的代码生成。以下示例取自/ch-09/data/protobuf 目录。章节 READMEj 中的说明涵盖了如何安装 ScalaPB,并包括了设置正确环境变量以执行命令的步骤。
mkdir /Users/`whoami`/Desktop/coffee_protos
$SCALAPBC/bin/scalapbc -v3.11.1 \
--scala_out=/Users/`whoami`/Desktop/coffee_protos \
--proto_path=$SPARK_MDE_HOME/ch-09/data/protobuf/ \
coffee.proto
这个过程从长远来看节省了时间,因为它免去了你需要编写额外的代码来序列化和反序列化你的数据对象(无论是跨语言边界还是在不同代码库内)。
Protobuf 二进制格式
序列化(即二进制传输格式)使用了编码的概念,即二进制字段级分隔符。这些分隔符用作标记,以识别序列化 protobuf 消息中封装的数据类型。在示例文件 coffee.proto 中,你可能注意到每个字段类型旁边都有一个索引标记(例如 string id = 1;),这用于协助消息的编码/解码。与 avro 二进制格式相比,这意味着有一点额外的开销,但如果你阅读编码规范,你会看到其他效率上的优势足以弥补这些额外的字节(例如位打包、数值数据类型的高效处理,以及对每条消息前 15 个索引的特殊编码)。在选择 protobuf 作为流数据的二进制协议时,总体来看,优点远大于缺点。其中一个显著的优点是支持向后和向前的兼容性。
启用向后兼容性和防止数据损坏
在修改 protobuf 模式时需要记住类似于 avro 的规则。作为一个经验法则,你可以更改字段的名称,但你绝不能更改字段的类型或位置(索引),除非你愿意破坏向后兼容性。随着团队对 protobuf 使用的熟练程度提高,这些规则在长期支持任何数据时可能会被忽视,特别是当需要重新排列和优化时。如果不小心,这可能会带来麻烦。(有关更多背景,请参见下面名为保持数据质量随时间的提示。)
流式 protobuf 数据的最佳实践
鉴于 protobuf 支持 向后 和 向前 兼容,这意味着你可以在不担心首先更新读者的情况下部署新的写入器,读者也是如此,你可以用更新版本的 protobuf 定义来更新它们,而不必担心所有写入器的复杂部署。Protobuf 通过未知字段的概念支持向前兼容。这是 avro 规范中不存在的附加概念,用于跟踪由于 protobuf 本地版本与当前读取版本之间的差异而无法解析的索引和相关字节。这里的好处是你可以在任何时候 选择 更新 protobuf 定义中的新更改。
比如,假设你有两个流应用程序 (a) 和 (b)。应用程序 (a) 正在处理来自上游 Kafka 主题 (x) 的流数据,为每条记录增加额外的信息,然后将其写入一个新的 Kafka 主题 (y)。现在,应用程序 (b) 从 (y) 中读取数据并执行其操作。假设有一个更新的 protobuf 定义版本,而应用程序 (a) 尚未更新到最新版本,而上游 Kafka 主题 (x) 和应用程序 (b) 已经更新,并期望使用从升级中获得的新字段。令人惊讶的是,依然可以将未知字段通过应用程序 (a) 传递到应用程序 (b),甚至不需要知道它们的存在。
参见 “维护数据质量的技巧” 以获取更多深入的探讨。
提示:维护数据质量的技巧
当使用 avro 或 protobuf 时,你应该像对待需要推送到生产环境的代码一样对待这些模式。这意味着要创建一个可以提交到你公司 github(或你使用的任何版本控制系统)的项目,这也意味着你 应该 为你的模式编写单元测试。这不仅提供了如何使用每种消息类型的实际示例,而且测试数据格式的一个重要原因是确保模式的更改不会破坏向后兼容性。更进一步的是,为了单元测试模式,你需要首先编译 (.avsc 或 .proto) 文件,并使用相应的库代码生成。这使得创建可发布的库代码变得更加容易,你还可以使用发布版本(版本 1.0.0)来记录每次对模式的更改。
一种简单的方法是通过在项目生命周期内序列化并存储每个消息的二进制副本,来使这一过程得以实现。我发现将这一步骤直接添加到单元测试中很有效,利用测试套件创建、读取和写入这些记录到项目测试资源目录中。这样,每个二进制版本在所有模式更改中都可以在代码库中找到。
通过一点额外的前期努力,你可以在整体方案中节省大量麻烦,并且可以安心入睡,知道你的数据是安全的(至少在生产和消费方面)。
使用 Buf 工具和 Protobuf 在 Spark 中
自从 2021 年撰写本章以来,Buf Build (buf.build/
) 已经发展成了一切 protobuf 公司。他们的工具简单易用,免费且开源,并在合适的时机出现,为 Spark 社区的一些倡议提供了支持。Apache Spark 项目引入了对 Protocol Buffers in Spark 3.4 的全面原生支持,以支持 spark-connect,并使用 Buf 编译 GRPC 服务和消息。毕竟,Spark Connect 是一个 GRPC 原生连接器,用于嵌入 JVM 外的 Spark 应用程序。
传统的 Apache Spark 应用必须在某处作为驱动程序应用运行,过去这意味着使用pyspark 或原生 spark,这两者仍然运行在 JVM 进程之上。
目录结构 通过 Spark Connect 显示了 protobuf 定义,以及 buf.gen.yaml 和 buf.work.yaml,这些文件有助于代码生成。
到头来,Buf Build 为构建过程带来了安心。为了生成代码,只需运行一个简单的命令:buf generate
。对于简单的 lint 检查和一致的格式化,可以使用 buf lint && buf format -w
。不过,最棒的是变更检测。buf breaking --against .git#branch=origin/main
就可以确保对消息定义的新更改不会对当前在生产环境中运行的内容产生负面影响。未来,我会写一篇关于使用buf进行企业分析的文章,但现在,是时候结束这一章了。
那么我们刚才讲到哪里了?你现在知道,在长期数据管理策略中使用 avro 或 protobuf 有其好处。通过使用这些与语言无关的、基于行的结构化数据格式,你减少了长期语言锁定的问题,为将来的任何编程语言打开了大门。说实话,支持遗留库和代码库是一项难以回报的任务。此外,序列化格式有助于减少与发送和接收大量数据相关的网络带宽成本和拥堵。这也有助于降低长期存储数据的开销。
最后,让我们看看这些结构化数据协议如何在通过远程过程调用发送和接收数据时实现额外的效率。
远程过程调用
RPC 框架,总的来说,使得 客户端 应用程序可以通过本地函数调用透明地调用 远程(服务器端)方法(过程),通过来回传递序列化的消息。客户端 和 服务器端 实现使用相同的 公共接口 定义来定义可用的功能性 RPC 方法和服务。接口定义语言(IDL)定义协议和消息定义,并作为客户端和服务器端之间的契约。让我们通过流行的开源 RPC 框架 gRPC 来看看实际情况。
gRPC
首先由 Google 设想并创建的,gRPC 即“通用”远程过程调用,是一个强大的开源框架,用于高性能服务,从分布式数据库协调(如 CockroachDB)到实时分析(如 微软 Azure 视频分析)。
图 1–2。RPC(在此示例中是 gRPC)通过在客户端和服务器之间传递序列化消息来工作。客户端实现相同的接口定义语言(IDL)接口,这作为客户端和服务器之间的 API 合同。(图片来源: grpc.io/docs/what-is-grpc/introduction/)
)
图 9–3 显示了 gRPC 工作的示例。服务器端代码使用 C++ 编写以提高速度,而用 ruby 和 java 编写的客户端可以使用 protobuf 消息作为通信手段与服务进行互操作。
使用协议缓冲区进行消息定义、序列化以及服务的声明和定义,gRPC 可以简化你捕获数据和构建服务的方式。例如,假设我们想继续创建一个用于跟踪客户咖啡订单的 API。API 合同可以在一个简单的服务文件中定义,从而可以使用相同的服务定义和消息类型构建服务器端实现和任意数量的客户端实现。
定义 gRPC 服务
你可以像 1–2–3 一样轻松定义服务接口、请求和响应对象以及需要在客户端和服务器之间传递的消息类型。
syntax = "proto3";
service CustomerService {
rpc TrackOrder (Order) returns (Response) {}
rpc TrackOrderStatus (OrderStatusTracker) returns (Response) {}
}
message Order {
uint64 timestamp = 1;
string orderId = 2;
string userId = 3;
Status status = 4;
}
enum Status {
unknown_status = 0;
initalized = 1;
started = 2;
progress = 3;
completed = 4;
failed = 5;
canceled = 6;
}
message OrderStatusTracker {
uint64 timestamp = 1;
Status status = 2;
string orderId = 3;
}
message Response {
uint32 statusCode = 1;
string message = 2;
}
通过添加 gRPC,实现和维护数据基础设施中使用的服务器端和客户端代码将变得更容易。由于 protobuf 支持向后和向前兼容,这意味着旧版 gRPC 客户端仍可以向新版 gRPC 服务发送有效消息,而不会遇到常见问题和痛点(如之前在“飞行中的数据问题”中讨论的)。
gRPC 使用 HTTP/2
作为额外的好处,对于现代服务栈,gRPC 能够使用 HTTP/2 作为其传输层。这也意味着你可以利用现代数据网格(如 Envoy)进行代理支持、路由和服务级别的 认证,同时减少标准 HTTP over TCP 中的 TCP 数据包拥塞问题。
减轻飞行中的数据问题并在数据责任方面取得成功,从数据开始,并从那个中心点向外扩展。对于如何将数据引入你的数据网络,建立相应的流程应该被视为在投入流数据之前需要完成的前提条件。
摘要
本文的目标是呈现所需的移动部件、概念和背景信息,以便在盲目从传统的(静态)批处理思维方式跳跃到理解实时流数据风险和回报的方式之前,武装好自己。
实时利用数据可以带来快速、可操作的洞察,并打开通往最先进的机器学习和人工智能的大门。
然而,分布式数据管理如果未考虑正确的步骤,也可能成为数据危机。请记住,如果没有建立在有效(值得信赖)数据之上的强大、坚实的数据基础,实时数据的道路将不是一条简单的道路,而是充满了颠簸和绕行。
我希望你喜欢第九章的下半部分。要阅读本系列的第一部分,请前往 对分析流处理的温和介绍。
为工程师及其他人构建心理模型
towardsdatascience.com
— — — — — — — — — — — — — — — — — — — — — — — —
如果你想深入了解,请查看我的书,或给我一个击掌支持。
[## 使用 Apache Spark 进行现代数据工程:构建关键流处理的实用指南…
Amazon.com: 使用 Apache Spark 进行现代数据工程:构建关键流处理的实用指南…
www.amazon.com](https://www.amazon.com/Modern-Engineering-Apache-Spark-Hands/dp/1484274512?source=post_page-----db58b3694263--------------------------------)
如果你有访问权限 O’Reilly Media,你也可以完全免费阅读这本书(对你有好处,对我则不然),但如果有机会,请在其他地方找到这本书的免费版本,或者获取电子书以节省运费(或避免寻找一本超过 600 页的实体书的地方)。
[## 使用 Apache Spark 进行现代数据工程:构建关键流处理的实用指南…
利用 Apache Spark 在现代数据工程生态系统中。本实用指南将教你如何编写完全…
learning.oreilly.com](https://learning.oreilly.com/library/view/modern-data-engineering/9781484274521/?source=post_page-----db58b3694263--------------------------------)
一种新的工程类型
如何基于 LLM 的微型 AGI 将需要向建模思维过程的范式转变
·
关注 发表在Towards Data Science ·4 分钟阅读·2023 年 4 月 15 日
–
作者根据ALAN DE LA CRUZ在Unsplash上的照片制作的图片
在撰写本文时(2023 年 4 月),如 langchain [1]等框架正在开创越来越复杂的 LLM 应用场景。最近,增强了 LLM 推理能力的软件代理已开始向人类水平的机器智能迈进。
我们在谈论什么?
智能体是软件系统中的一种模式;它们是能够做出决策并与环境相对自主互动的算法。在 langchain 智能体的情况下,环境通常是基于文本输入/输出的互联网、用户或其他智能体和工具的接口。
根据这个概念,其他项目 [2,3] 已经开始研究更通用的问题解决者(某种‘微型’通用人工智能,或 AGI——一种接近人类水平推理能力的 AI 系统)。尽管这些系统的当前版本仍然相当单片,因为它们作为一个软件整体接受目标/任务/想法的输入,但从它们的执行中可以很容易地看出,它们依赖于多个不同的子系统。
图片来源:Significant Gravitas (github.com/Significant-Gravitas/Auto-GPT
, 30/03/2023)
我们在这些系统中看到的新范式是,它们建模思维过程:“批判性思考并检查你的结果”、“查阅多个来源”、“反思你的解决方案的质量”、“使用外部工具进行调试”等,这些都接近于人类的思维方式。
现在,在日常(人类)生活中,我们雇佣专家来完成需要特定专业知识的工作。我的预测是,在不久的将来,我们将雇佣某种认知工程师来建模 AGI 的思维过程,可能通过构建特定的 多智能体系统,以更高质量地解决特定任务。
我为什么会这么认为?为什么整体 AGI 不一定足够好?
从我们今天如何使用 LLMs 来看,我们已经在做这件事——建模认知过程。我们通过特定的方式,利用提示工程和大量相邻研究领域的成果,来实现所需的输出质量。即使我上面描述的可能看起来很未来,这已经是现状。
那我们接下来该怎么做?我们可能会看到越来越智能的 AI 系统,甚至可能在某个时点超越人类水平。随着它们变得越来越智能,与我们的目标——我们希望它们做的事情——保持一致将变得越来越困难。AGI 对齐以及对过于强大的未对齐 AI 的安全关注已经是一个非常活跃的研究领域,风险很高——例如,Eliezer Yudkowski [4] 就详细解释了这一点。
我的直觉是较小的,即‘更简单的’系统更容易对齐,因此它们会以更高的概率以某种质量产生某种结果。这些系统正是我们可以使用认知工程方法构建的。
我们应该做什么
-
我们应该对如何构建专门的 AGI 系统有一个良好的实验性理解
-
从这些经验中,我们应该创建和迭代正确的抽象,以更好地进行这些系统的建模。
-
有了这些抽象概念,我们可以开始创建可重用的思维模块,就像我们使用可重用的构件来创建用户界面一样。
-
在不久的将来,我们将理解建模这些智能系统的模式和最佳实践,随着经验的积累,我们将了解到哪些架构可以导致哪些结果。
作为一种积极的副作用,通过这项工作和经验的积累,也许可以学习如何更好地调整更智能的人工通用智能系统。
这将会引向何方
我预计很快会看到来自不同学科的知识融入这一新兴领域。
多智能体系统的研究以及如何将其用于问题解决,心理学、业务管理和流程建模的洞察都可以有益地融入这一新范式及新兴的抽象中。
我们还需要考虑这些系统如何能够最好地进行交互。例如,人类反馈循环,或者至少在过程中定期评估,可以帮助取得更好的结果——你可能在使用 ChatGPT 时对此有所了解。
这是一个前所未见的用户体验模式,其中计算机变得更像一个合作伙伴或副驾驶,负责进行低级别的研究、制定方案、头脑风暴、自动化或推理任务。
关于作者
Johanna Appel 是总部位于瑞士苏黎世的机器智能咨询公司Altura.ai GmbH 的联合创始人。
她通过将这些“微型”人工通用智能系统集成到现有的业务流程中,帮助公司从中获利。
参考资料
[1] Langchain GitHub 代码库,github.com/hwchase17/langchain
[2] AutoGPT GitHub 代码库,github.com/Significant-Gravitas/Auto-GPT
[3] BabyAGI GitHub 代码库,github.com/yoheinakajima/babyagi
[4] “Eliezer Yudkowsky: AI 的危险与人类文明的终结”,Lex Fridman 播客 #368,www.youtube.com/watch?v=AaTRHFaaPG8
预测概率分布的新方法
原文:
towardsdatascience.com/a-new-way-to-predict-probability-distributions-e7258349f464
探索 Catboost 的多分位数回归
·发表于 Towards Data Science ·阅读时间 10 分钟 ·2023 年 2 月 14 日
–
骰子。图片由作者提供。
我们对机器学习模型的预测可以有多自信?这个问题在过去十年中一直是一个重要的研究领域,并且在高风险的机器学习应用如金融和医疗保健中具有重大意义。尽管许多分类模型,特别是校准模型,通过预测目标类别的概率分布提供了不确定性量化,但在回归任务中量化不确定性要复杂得多。
在许多提出的方法中,分位数回归是最受欢迎的之一,因为它对目标分布没有假设。直到最近,分位数回归的主要缺点是每个预测的分位数都需要训练一个模型。例如,为了预测目标分布的第 10、第 50 和第 90 百分位数,需要训练三个独立的模型。Catboost 通过多分位数损失函数解决了这个问题——这是一个使单个模型能够预测任意数量分位数的损失函数。
本文将探讨两个使用多分位数损失函数的合成数据示例。虽然这些示例不一定能反映真实世界的数据集,但它们将帮助我们理解该损失函数如何通过预测目标分布的分位数来量化不确定性。有关机器学习中噪声和不确定性的快速回顾,请参见这篇文章:
你的机器学习模型不起作用的真正原因
towardsdatascience.com
这篇文章的后续内容现已发布:
使用 Catboost 的符合性多分位数回归
towardsdatascience.com
分位数回归简要概述
在有监督的机器学习中,通常的任务是训练一个模型,以预测给定一组输入特征时目标的期望值:
有监督机器学习。图片由作者提供。
值得注意的是,这种方式训练的模型产生一个单一预测,指示模型认为在给定特征的情况下目标的最可能值。在回归任务中,这通常是条件特征下目标分布的均值。
然而,如 上一篇文章 中所示,大多数机器学习模型在噪声数据集上训练,而仅仅预测目标的条件期望值不足以充分描述目标分布。要看到这一点,请考虑以下噪声回归数据集:
噪声线性回归数据。图片由作者提供。
即使模型预测与数据最优匹配,它也没有量化不确定性。例如,当 x 接近 0.4 时,最佳拟合线预测 y 为 3.8。虽然在 x 接近 0.4 时 3.8 是 y 最可能的值,但数据中有许多例子 y 的值远高于或低于 3.8。换句话说,目标的条件分布在其例外之外具有高变异性,而分位数回归帮助我们描述这一点。
在分位数回归中,损失函数被修改以鼓励模型学习条件目标分布的期望分位数。
分位数回归损失函数。图片由作者提供。
为了更好地理解这个损失函数,假设一个模型正在训练以学习目标分布的第 95 百分位数。在第一个例子中,考虑一个训练样本,其中目标值为 45,模型预测为 60(即模型高估了目标 15)。假设每个训练样本的权重为 1。损失函数在这些值下的评估如下:
第 95 百分位数损失函数高估。作者提供的图像。
现在,假设目标值为 45,模型预测为 30(即模型低估目标 15)。损失函数的值看起来差异很大:
第 95 百分位数损失函数低估。作者提供的图像。
尽管在两个例子中模型预测都偏差了 15,但损失函数对低估的惩罚远高于高估。因为学习的是第 95 百分位数,损失函数对低于目标的任何预测惩罚系数为 0.95,对高于目标的预测惩罚系数为 0.05。因此,当学习第 95 百分位数时,模型“被迫”倾向于高估而非低估。这种情况发生在学习任何中位数以上的百分位数时 — 学习中位数以下的百分位数时情况正好相反。为了更好地理解这一点,我们可以可视化每个预测百分位数的损失函数:
第 95 百分位数损失函数。低估(即目标 — 预测 > 0)受到更重的惩罚。作者提供的图像。
第 50 百分位数损失函数。高估和低估受到相同的惩罚。作者提供的图像。
第 10 百分位数损失函数。高估(即目标 — 预测 < 0)受到更重的惩罚。作者提供的图像。
Catboost 现在通过允许基础决策树在每个节点输出多个百分位数来扩展这个概念。这使得单个模型可以通过最小化新的损失函数来预测多个百分位数:
Catboost 多百分位数损失函数。作者提供的图像。
示例 1 — 简单线性回归
为了理解多百分位数损失函数如何工作,我们从一个简单的数据集开始。以下 Python 代码生成一个带有高斯加性噪声的合成线性数据集:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from catboost import CatBoostRegressor
sns.set()
# Number of training and testing examples
n = 1000
# Generate random x values between 0 and 1
x_train = np.random.rand(n)
x_test = np.random.rand(n)
# Generate random noise for the target
noise_train = np.random.normal(0, 0.3, n)
noise_test = np.random.normal(0, 0.3, n)
# Set the slope and y-intercept of the line
a, b = 2, 3
# Generate y values according to the equation y = ax + b + noise
y_train = a * x_train + b + noise_train
y_test = a * x_test + b + noise_test
结果训练数据应该类似于此:
嘈杂的线性回归数据。作者提供的图像。
接下来,需要为预测指定目标分布的百分位数。为了展示多百分位数损失的强大功能,该模型将寻求为每个观察预测 99 个不同的百分位数值。这几乎可以认为是从每个预测分布中采样 99 个样本。
# Store quantiles 0.01 through 0.99 in a list
quantiles = [q/100 for q in range(1, 100)]
# Format the quantiles as a string for Catboost
quantile_str = str(quantiles).replace('[','').replace(']','')
# Fit the multi quantile model
model = CatBoostRegressor(iterations=100,
loss_function=f'MultiQuantile:alpha={quantile_str}')
model.fit(x_train.reshape(-1,1), y_train)
# Make predictions on the test set
preds = model.predict(x_test.reshape(-1, 1))
preds = pd.DataFrame(preds, columns=[f'pred_{q}' for q in quantiles])
结果预测的数据框如下所示:
百分位数预测。图片由作者提供。
每一行对应一个测试示例,每一列给出一个预测的百分位数。例如,第一个测试示例的第 10 百分位数预测值为 3.318624. 由于只有一个输入特征,我们可以将几个预测的百分位数叠加在测试数据上进行可视化:
fig, ax = plt.subplots(figsize=(10, 6))
ax.scatter(x_test, y_test)
for col in ['pred_0.05', 'pred_0.5', 'pred_0.95']:
ax.scatter(x_test.reshape(-1,1), preds[col], alpha=0.50, label=col)
ax.legend()
测试数据与预测的百分位数叠加。图片由作者提供。
从视觉上看,第 95 百分位数和第 5 百分位数似乎很好地考虑了数据的不确定性。此外,第 50 百分位数(即中位数)大致近似于最佳拟合线。在处理预测的百分位数时,我们通常感兴趣的一个指标是覆盖率。覆盖率是落在两个期望百分位数之间的目标百分比。例如,覆盖率可以使用第 5 百分位数和第 95 百分位数计算如下:
coverage_90 = np.mean((y_test <= preds['pred_0.95']) & (y_test >= preds['pred_0.05']))*100
print(coverage_90)
# Output: 91.4
使用第 5 百分位数和第 95 百分位数,假设完美的校准,我们预计覆盖率为 95–5 = 90%。在这个示例中,预测的百分位数略有偏差,但仍然接近,覆盖率值为 91.4%。
现在让我们分析模型输出的整个预测分布。回顾一下,最佳拟合线是 y = 2x + 3. 因此,对于任何输入 x,预测分布的均值应接近 2x + 3. 同样,由于数据中加入了标准差为 0.3 的随机高斯噪声,预测分布的标准差应接近 0.3. 让我们来测试一下:
# Give the model a new input of x = 0.4
x = np.array([0.4])
# We expect the mean of this array to be about 2*0.4 + 3 = 3.8
# We expect the standard deviation of this array to be about 0.30
y_pred = model.predict(x.reshape(-1, 1))
mu = np.mean(y_pred)
sigma = np.std(y_pred)
print(mu) # Output: 3.836147287742427
print(sigma) # Output: 0.3283984093786787
# Plot the predicted distribution
fig, ax = plt.subplots(figsize=(10, 6))
_ = ax.hist(y_pred.reshape(-1,1), density=True)
ax.set_xlabel('$y$')
ax.set_title(f'Predicted Distribution $P(y|x=4)$, $\mu$ = {round(mu, 3)}, $\sigma$ = {round(sigma, 3)}')
当 x = 4 时 y 的预测分布。图片由作者提供。
令人惊讶的是,预测分布似乎与我们的预期一致。接下来,让我们尝试一个更困难的例子。
示例 2—具有可变噪声的非线性回归
为了看到多百分位数回归的真正威力,让我们使学习任务更加困难。以下代码创建了一个具有异质噪声的非线性数据集,这些噪声依赖于域的特定区域:
# Create regions of the domain that have variable noise
bounds = [(-10, -8), (-5, -4), (-4, -3), (-3, -1), (-1, 1), (1, 3), (3, 4), (4, 5), (8, 10)]
scales = [18, 15, 8, 11, 1, 2, 9, 16, 19]
x_train = np.array([])
x_test = np.array([])
y_train = np.array([])
y_test = np.array([])
for b, scale in zip(bounds, scales):
# Randomly select the number of samples in each region
n = np.random.randint(low=100, high = 200)
# Generate values of the domain between b[0] and b[1]
x_curr = np.linspace(b[0], b[1], n)
# For even scales, noise comes from an exponential distribution
if scale % 2 == 0:
noise_train = np.random.exponential(scale=scale, size=n)
noise_test = np.random.exponential(scale=scale, size=n)
# For odd scales, noise comes from a normal distribution
else:
noise_train = np.random.normal(scale=scale, size=n)
noise_test = np.random.normal(scale=scale, size=n)
# Create training and testing sets
y_curr_train = x_curr**2 + noise_train
y_curr_test = x_curr**2 + noise_test
x_train = np.concatenate([x_train, x_curr])
x_test = np.concatenate([x_test, x_curr])
y_train = np.concatenate([y_train, y_curr_train])
y_test = np.concatenate([y_test, y_curr_test])
结果训练数据如下所示:
示例 2 的训练数据。图片由作者提供。
我们将以与示例 1 相同的方式拟合 Catboost 回归器,并在测试集上可视化预测结果:
model = CatBoostRegressor(iterations=300,
loss_function=f'MultiQuantile:alpha={quantile_str}')
model.fit(x_train.reshape(-1,1), y_train)
preds = model.predict(x_test.reshape(-1, 1))
preds = pd.DataFrame(preds, columns=[f'pred_{q}' for q in quantiles])
fig, ax = plt.subplots(figsize=(10, 6))
ax.scatter(x_test, y_test)
for col in ['pred_0.05', 'pred_0.5', 'pred_0.95']:
quantile = int(float(col.split('_')[-1])*100)
label_name = f'Predicted Quantile {quantile}'
ax.scatter(x_test.reshape(-1,1), preds[col], alpha=0.50, label=label_name)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('Testing Data for Example 2 with Predicted Quantiles')
ax.legend()
测试数据与预测的百分位数叠加。图片由作者提供。
通过视觉检查,模型很好地刻画了这种非线性、异方差的关系。注意在 x = 0 附近,三个预测分位数趋向于一个单一值。这是因为 x = 0 附近的区域几乎没有噪声——任何正确预测该区域条件概率分布的模型都应预测出小方差。相反,当 x 在 7.5 到 10.0 之间时,由于该区域的固有噪声,预测的分位数则相距更远。90% 覆盖度可以像以前一样计算:
coverage_90 = np.mean((y_test <= preds['pred_0.95']) & (y_test >= preds['pred_0.05']))*100
print(coverage_90)
# Output: 90.506
使用第 5 和第 95 分位数,假设完美校准,期望覆盖度为 95–5 = 90%。在这个例子中,预测的分位数甚至比示例 1 更好,覆盖度达到了 90.506%。
最后,让我们查看一些输入及其相应的预测分布。
x = -0.06 的预测目标分布。图像作者提供。
上述分布在相对较小的方差下很好地捕捉了目标值。这是可以预期的,因为当 x = 0 时,训练数据中的目标值几乎没有噪声。与此对比的是当 x = -8.56 时的预测目标分布:
x = -8.56 的预测目标分布。图像作者提供。
该分布向右偏斜,方差更高。这在该数据区域是预期的,因为噪声是从高方差的指数分布中采样的。
结论
本文展示了多分位回归在学习任意条件目标分布方面的强大能力。我们只探索了两个一维示例以视觉检查模型性能,但我鼓励任何感兴趣的人尝试在更高维数据上使用多分位损失函数。重要的是要注意,分位回归不提供收敛的统计或算法保证,这些模型的表现将根据学习问题的性质而有所不同。感谢阅读!
成为会员: https://harrisonfhoffman.medium.com/membership
请我喝杯咖啡: https://www.buymeacoffee.com/HarrisonfhU
参考文献
迈向负责任的 AI 生成内容之路
关于隐私、偏见、毒性、错误信息和知识产权问题的警告
·
关注 发表在 Towards Data Science · 11 分钟阅读 · 2023 年 3 月 16 日
–
图 1:负责任的 AI 生成内容的范围。
介绍
AI 生成内容(AIGC)在过去几年内引起了极大的关注,内容涵盖了图像、文本、音频、视频等。同时,AIGC 成为了一把双刃剑,最近也因其负责任的使用问题受到了许多批评。在这篇愿景论文中,我们关注可能阻碍 AIGC 健康发展和实际部署的三大主要风险,包括:(1)隐私;(2)偏见、毒性、错误信息;和(3)知识产权(IP),如图 1 所示。通过记录已知和潜在的风险,以及 AIGC 可能的误用场景,旨在引起对潜在风险和误用的关注,帮助社会消除障碍,推动 AIGC 更加伦理和安全的部署。此外,我们还提供了在构建生成模型时解决这些风险的有前景的方向,以便 AIGC 能够负责任地使用,以造福社会。
1. 隐私
隐私泄露。大型基础模型已知存在隐私风险,建立在这些模型基础上的 AIGC 模型也可能面临隐私泄露。例如,Stable Diffusion 在训练数据中记住了重复的图像 [Rombach 等, 2022c]。 [Somepalli 等, 2022] 证明 Stable Diffusion 明目张胆地从训练数据中复制图像,生成的图像仅仅是训练数据集中前景和背景对象的简单组合。此外,系统有时展示了重建记忆的能力,生成的对象在语义上等同于原始对象,但像素形式上并不完全相同。这些图像的存在引发了对数据记忆和扩散图像所有权的担忧。
类似地,近期研究显示 Stable Diffusion 和 Google 的 Imagen 可能泄露真实人物照片和受版权保护的图像 [Heikkila ̈, 2023]。在 Matthew Butterick 最近的诉讼中 [Butterick, 2023],他指出由于系统中的所有视觉信息都来源于受版权保护的训练图像,因此生成的图像必然是这些训练图像的衍生作品,不论其外观如何。DALL·E 2 也遇到了类似的问题。它有时会再现训练数据中的图像,而不是生成新的图像。OpenAI 发现这种图像复述现象发生的原因是数据集中图像被多次复制。类似地,当我问 ChatGPT “ChatGPT 的隐私风险是什么”时,它回应了 4 种潜在的隐私风险,如图 2 所示。
图 2:ChatGPT 对“ChatGPT 的隐私风险是什么”的回答(2023 年 1 月 30 日版本)。
隐私措施。在隐私措施方面,业内,Stability AI 已认识到 Stable Diffusion 的局限性,例如训练数据中可能记忆重复图像的潜在问题。为了解决这一问题,他们提供了一个 网站 来支持识别这些记忆图像。此外,艺术公司 Spawning AI 创建了一个名为“Have I Been Trained”的网站,以帮助用户确定他们的照片或作品是否被用作 AI 训练材料。OpenAI 采取了措施通过去重减少数据重复,以应对隐私问题。此外,微软和亚马逊等公司实施了措施,防止员工泄露机密,通过禁止与 ChatGPT 共享敏感数据,因为这些信息可能被用于 ChatGPT 未来版本的训练数据。
2. 偏见、毒性、虚假信息
问题数据集和 AIGC 模型。由于 AI 模型使用的训练数据来自现实世界,它们可能无意中加剧有害的刻板印象,排除或边缘化某些群体,并包含有毒的数据源,这些数据可能煽动仇恨或暴力并冒犯个人 [Weidinger et al., 2021]。例如,用于训练扩散模型的 LAION 数据集因包含与社会刻板印象、色情、种族侮辱和暴力相关的问题内容而受到批评。尽管一些 AIGC 模型如 Imagen 尝试过滤掉不良数据,例如色情图像和有毒语言,但过滤后的数据仍可能包含性暗示或暴力内容。
在没有缓解策略的情况下,使用上述有问题的数据集进行训练、学习或微调的模型可能继承有害的刻板印象、社会偏见和毒性,从而导致对某些社会群体的不公平歧视和伤害 [Weidinger et al., 2021]。例如,Stable Diffusion v1 主要在 LAION-2B 数据集上进行训练,该数据集仅包含英文描述的图像。因此,该模型偏向于白人、西方文化,其他语言的提示可能没有得到充分代表。Stable Diffusion 模型的后续版本在过滤后的 LAION 数据集上进行了微调,但偏见问题仍然存在。类似地,DALLA·E 和 DALLA·E 2 被发现对少数群体表现出负面刻板印象。Google 的 Imagen 也编码了几个社会偏见和刻板印象,例如生成肤色较浅的人物图像并符合西方性别刻板印象。由于这些问题,大多数公司决定不将其 AIGC 模型公开。
为了说明 AIGC 模型中的固有偏差,我们在 Stable Diffusion v2.1 上测试了一个玩具示例。如图 3 所示,使用提示“在草原上奔跑的三名工程师”生成的图像全部是男性,而且没有任何一个属于少数族裔,显示出生成图像的多样性不足。
图 3:使用 Stable Diffusion v2.1 生成的文本“在草原上奔跑的三名工程师”的图像。9 张图像中共有 28 人,全部为男性。而且,没有任何一个人属于少数族裔。这显示了 Stable Diffusion 的巨大偏差。
当模型提供不准确或虚假的答案时,也存在信息错误的风险。GPT 及其衍生模型生成的内容可能看起来准确且权威,但实际上可能完全不准确。因此,这可能被用于学校、法律、医疗领域、天气预报或其他任何地方的误导目的。例如,ChatGPT 提供的医疗剂量答案可能不准确或不完整,可能导致用户采取危险甚至危及生命的行动。关于交通法规的误导性信息如果被驾驶员采纳,可能导致事故甚至死亡。
偏见、有毒信息、虚假信息的缓解。OpenAI 采取了额外措施,通过仔细筛选原始训练数据集,确保 DALLA·E 2 的训练数据中移除了任何暴力或性内容。然而,筛选可能会引入偏见,这些偏见可能会传播到下游模型。为了解决这个问题,OpenAI 开发了预训练技术,以缓解由筛选引起的偏见。
为确保 AI 驱动的模型反映当前社会状态,必须定期用最新信息更新 AIGC 模型所用的训练语料。这有助于防止信息滞后,并确保模型保持更新、相关且有益,因此需要定期收集新的训练数据并更新模型。一个明显的点是,虽然可以减少源数据集中的偏见和刻板印象,但这些偏见仍可能在 AIGC 模型的训练和开发过程中被传播甚至加剧。因此,评估模型训练和开发全过程中的偏见、有毒信息和虚假信息的存在是至关重要的,而不仅仅停留在数据源级别。
3. 知识产权保护
知识产权侵权。生成内容的所有权和保护引发了大量关注和辩论。如果生成的内容无论是有意还是无意地复制了现有作品,就存在版权侵权的风险,这引发了关于知识产权侵权的法律问题。2022 年 11 月,Matthew Butterick 对微软的子公司 GitHub 提起了集体诉讼,指控其产品 Copilot 侵犯了版权法 [Butterick, 2022]。该诉讼的核心在于 Copilot 非法使用了来自互联网的有版权代码段而未给予署名。德州农工大学的教授 Tim Davis 也提供了他的代码被 Copilot 逐字复制的例子。尽管微软和 OpenAI 已确认 Copilot 是在公共 GitHub 仓库中的开源软件上进行训练的,但微软声称 Copilot 的输出仅仅是一系列代码“建议”,并不主张对这些建议的任何权利。微软还不对生成代码的正确性、安全性或版权做出任何保证。
对于文本生成图像模型,一些生成模型面临了侵犯艺术创作的指控。[Somepalli et al., 2022] 提出了证据,表明像 Stable Diffusion 这样的艺术生成 AI 系统可能会从其训练数据中复制内容。虽然 Stable Diffusion 声明不拥有生成图像的版权,并允许用户在图像内容合法且无害的情况下自由使用这些图像,但这种自由引发了关于所有权伦理的问题。像 Stable Diffusion 这样的生成模型在未经知识产权持有者批准的情况下,训练于来自互联网的数十亿张图像,这被一些人认为是对其权利的侵犯。
知识产权问题缓解。为了缓解知识产权问题,许多公司已开始采取措施以适应内容创作者。例如,Midjourney 在其服务条款中添加了 DMCA 删除政策,允许艺术家在怀疑版权侵权时请求将他们的作品从数据集中删除。类似地,Stability AI 计划为艺术家提供一个选项,允许他们从未来版本的 Stable Diffusion 中排除自己。
此外,文本水印,这种水印以前用于保护语言生成 API 的知识产权 [He et al., 2022a; He et al., 2022b],也可以用来识别这些 AIGC 工具是否未经许可使用了来自其他来源的样本。这在 Stable Diffusion 中表现得很明显,该模型生成的图像上带有 Getty Images 的水印 [Vincent, 2023]。随着 AIGC 的日益流行,水印的需求变得越来越迫切。OpenAI 正在开发一种水印,用于识别其 GPT 模型生成的文本。这将成为教育工作者和教授检测使用此类工具生成的作业中的剽窃行为的有价值工具。谷歌已对其发布的所有图像应用了 Parti 水印。
除了水印技术,OpenAI 还发布了一种分类器,可以区分由 AI 生成的文本和由人类编写的文本。然而,这不应仅仅依赖于此来做出关键决策。
讨论
除了在负责任的 AIGC 中提到的问题外,还有更多需要关注的方面,包括但不限于以下几点。
滥用的担忧:为 AIGC 提供动力的基础模型使得创建接近真实的深度伪造变得更容易且更便宜,这带来了额外的风险和担忧。这些技术的滥用可能导致虚假新闻、骗局、骚扰和虚假信息的传播,损害个人声誉,甚至违法。
对投毒攻击的脆弱性:如果基础模型被攻破,将是一场灾难。例如,一个具有隐藏“后门”的扩散模型在数据生成过程中遇到特定触发模式时可能会执行恶意行为[Chou et al., 2022]。这种木马效应可能对依赖于受损扩散模型的下游应用造成灾难性损害。
关于 AIGC 是否会取代人类的辩论:AIGC 的使用面临着那些担心它会取代人类工作的批评。Insider 列出了几种可能被 ChatGPT 取代的工作,包括编码员、数据分析师、法律助理、交易员、会计师等。一些艺术家担心,诸如 Stable Diffusion 这样的图像生成工具的广泛使用,最终可能使人类艺术家、摄影师、模特、摄影师和演员在商业上失去竞争力[Heikkila ̈, 2022b]。
可解释的 AIGC:基础模型的“黑箱”特性可能导致不令人满意的结果。例如,常常很难确定生成模型输出所使用的信息,这会导致数据集中出现偏差。解释是理解 AIGC 为何以及如何产生这些问题的关键因素。
负责任的开源:由于 AIGC 背后的代码和模型对公众不透明,并且它们的下游应用多样,可能具有复杂的社会影响,因此很难确定它们可能造成的潜在危害。因此,负责任的开源变得至关重要,以确定在特定使用案例中 AIGC 的利益是否超过其潜在风险。
用户反馈:收集用户反馈也是负责任 AIGC 的一个关键因素。像 OpenAI 这样的公司积极寻求用户反馈,以识别在实际场景中可能出现的有害输出,并发现和缓解新的风险。通过将用户纳入反馈循环,AIGC 开发者可以更好地理解模型的潜在后果,并采取纠正措施以最小化任何负面影响。
对数据拥有者或贡献者的同意、信用和补偿:许多 AIGC 模型是在未获得同意或未向原始数据贡献者提供信用或补偿的情况下训练的。为了避免负面影响,AIGC 公司在训练模型之前应获得数据贡献者的同意,并采取积极措施。如果未能做到这一点,可能会导致针对 AIGC 的诉讼。
训练 AIGC 模型造成的环境影响:AIGC 模型的巨大规模,可能拥有数十亿或数万亿个参数,导致了模型训练和运行的高环境成本。例如,GPT-3 拥有 1750 亿个参数,需要大量的计算资源来进行训练。GPT-4 可能比前身有更多的参数,并预计会产生更显著的碳排放。如果不采取适当措施来减轻 AIGC 的高能源成本,可能会对我们的星球造成无法修复的损害。
结论
尽管 AIGC 仍处于起步阶段,但其发展迅速,并将在可预见的未来保持活跃。目前的 AIGC 技术只是 AI 在艺术领域创作潜力的冰山一角。虽然 AIGC 提供了许多机会,但也带来了重大风险。在这项工作中,我们提供了对当前和潜在威胁的概要,以便用户和公司能够充分认识这些风险,并采取适当措施加以减轻。公司在所有 AIGC 相关项目中融入负责任的 AI 实践至关重要。
除非另有说明,所有图片均为作者所摄。
论文链接:
[## [2303.01325] 走向负责任的 AI 生成内容之路
摘要:AI 生成内容(AIGC)在过去几年受到了极大的关注,内容范围涵盖……
参考文献
[Rombach et al., 2022c] Robin Rombach, Andreas Blattmann, Dominik Lorenz, Patrick Esser 和 Bjo ̈rn Ommer. 稳定扩散 v1 模型卡。 https://github.com/CompVis/stable-diffusion/blob/main/ Stable Diffusion v1 Model Card.md, 2022。
[Heikkila ̈, 2023] Melissa Heikkila ̈. AI 模型吐出的真实人物照片和受版权保护的图像。 https://www.technologyreview.com/2023/02/ 03/1067786/ai-models-spit-out-photos-of-real-people-and-copyrighted-images/, 2023。
[Butterick, 2022] Matthew Butterick. Github copilot 调查。 https://githubcopilotinvestigation.com/, 2022。
[Butterick, 2023] Matthew Butterick. 稳定扩散诉讼。 https://stablediffusionlitigation.com, 2023。
[索梅帕利等, 2022] 高瓦米·索梅帕利, 瓦苏·辛格拉, 米卡·戈德布鲁姆, 乔纳斯·盖平, 和 汤姆·戈德斯坦。扩散艺术还是数字伪造?调查扩散模型中的数据复制。arXiv 预印本 arXiv:2212.03860, 2022。
[何等, 2022a] 何玄力, 徐琼凯, 吕灵娟, 吴方钊, 和 王成光。通过词汇水印保护语言生成 API 的知识产权。AAAI, 2022。
[何等, 2022b] 何玄力, 徐琼凯, 曾义, 吕灵娟, 吴方钊, 李季伟, 和 贾若溪。Cater:通过条件水印保护文本生成 API 的知识产权。神经信息处理系统进展, 2022。
[文森特, 2023] 詹姆斯·文森特。Getty 图片公司起诉 AI 艺术工具 Stable Diffusion 的创作者,因其抓取了 Getty 的内容。 https://www.theverge.com/2023/1/17/23558516/ ai-art-copyright-stable-diffusion-getty-images-lawsuit, 2023。
[魏丁格等, 2021] 劳拉·魏丁格, 约翰·梅洛, 玛丽贝丝·劳, 康纳·格里芬, 乔纳森·尤萨托, 黄博森, 玛伊拉·程, 米亚·格莱斯, 博尔哈·巴列, 阿图萨·卡西尔扎德, 等。语言模型的伦理和社会风险。arXiv 预印本 arXiv:2112.04359, 2021。
[海基拉, 2022b] 梅利莎·海基拉。这个艺术家主宰了 AI 生成的艺术,并且对此感到不满。 https://www.technologyreview.com/2022/09/16/ 1059598/this-artist-is-dominating-ai-generated-art-and-hes-not-happy-about-it/, 2022。
[周等, 2022] 周声彦, 陈品渝, 和 何宗义。如何在扩散模型中植入后门?arXiv 预印本 arXiv:2212.05400, 2022。
没有冷启动问题的高效推荐系统
原文:
towardsdatascience.com/a-performant-recommender-system-without-cold-start-problem-69bf2f0f0b9b
推荐系统
当协同过滤和基于内容的推荐系统合并时
·发表于 Towards Data Science ·阅读时间 11 分钟·2023 年 1 月 31 日
–
图片由 Ivan Aleksic 提供,来源于 Unsplash
也许最著名的推荐系统是所谓的矩阵分解。在这种协同过滤推荐系统中,用户和项目都通过嵌入表示,这不过是一个数字向量。直观的理解是,用户和项目嵌入的点积应等于用户对该项目的评分。
如果你对这些概念还不熟悉,我建议(😉)在继续之前阅读我的另一篇文章,因为我在其中解释了许多概念和代码片段。
学习如何在 TensorFlow 中构建一个简单的推荐系统
towardsdatascience.com
冷启动问题
纯粹的协同过滤推荐系统,如矩阵分解,有一个优点,即使没有太多关于用户和你想推荐的电影/文章/项目的数据,你也通常可以立即构建它们。你只需要知道谁对什么进行了评分以及评分情况;例如,用户B对电影Y的评分为 2 星。
作者提供的图片。
然而,当你有新的用户或项目时,它们就不够用了,因为模型没有机会学习这些内容,从而基本上为这些用户或项目提供了随机推荐——这就是令人头疼的冷启动问题。假设另一个用户E注册了,我们还在数据库中添加了一个新电影W。
图片由作者提供。
在这篇文章中,我将展示一种通过结合更多关于用户和项目的特征来缓解冷启动问题的简单方法——这就是我们将加入到模型中的基于内容的组件。使用实际内容数据,如用户年龄或电影类型,生成的模型能更好地处理新用户或新电影。
回到 MovieLens
和我上一篇文章一样,我将使用 MovieLens 数据集,它提供了用户-电影评分。此外,它还包含了一些用户和电影特征,尽管我们在上一篇文章中忽略了这些特征,但今天我们将利用这些特征来构建更好的模型!
你可以在 我的 Github找到代码。
跟随上一篇文章让我们
-
使用 tensorflow-datasets 获取数据
-
将其制作成数据框并更改一些列类型,然后
-
按时间排序以进行时间上的训练-测试分割
import tensorflow_datasets as tfds
data = tfds.load("movielens/1m-ratings")
df = tfds.as_dataframe(data["train"])
filtered_data = (
df
.sort_values("timestamp") # for a temporal train-eval-test split
.astype(
{
"bucketized_user_age": int,
"movie_id": int,
"movie_title": str,
"user_gender": int,
"user_id": int,
"user_occupation_label": int,
"user_occupation_text": str,
"user_rating": int,
"user_zip_code": str,
}
)
.drop(columns=["timestamp"])
)
# temporal train-eval-test split
train = filtered_data.iloc[:80000]
evaluation = filtered_data.iloc[80000:90000]
test = filtered_data.iloc[90000:]
X_train = train.drop(columns=["user_rating"])
y_train = train["user_rating"]
X_eval = evaluation.drop(columns=["user_rating"])
y_eval = evaluation["user_rating"]
X_test = test.drop(columns=["user_rating"])
y_test = test["user_rating"]
filtered_data
数据框
图片由作者提供。
告诉我们除了user_id、movie_id和目标user_rating之外,我们还有用户特征
-
bucketized_user_age
-
user_gender
-
user_occupation_label
-
user_occupation_text
-
user_zip_code
以及电影特征
-
movie_genres
-
movie_title
使用一些刻板印象:
从直观上看,这些特征应该非常有帮助,因为模型可以学习“女性喜欢戏剧”或“年轻人不喜欢老电影”之类的内容。
我们现在将了解如何通过一个简单的网络架构LightFM来使用所有这些额外特征。这个名字是由 Maciej Kula 在他写得很好的论文 Metadata Embeddings for User and Item Cold-start Recommendations [1] 中选择的。请阅读一下!
LightFM 是一种混合型推荐系统,因为它使用评分以及用户和项目特征。
LightFM 的简单想法
让我们首先回顾一下我们简单的矩阵分解是怎样的,省略偏差。在我们旧的推荐系统中,我们使用了user_id和movie_id,将两者进行嵌入,然后计算点积来计算评分。
矩阵分解架构,图片由作者提供。
对于LightFM,它是这样工作的:
-
我们嵌入所有特征,包括用户特征和电影特征。
-
用户(电影)嵌入是所有这些用户(电影)特性嵌入的总和。
就这样!对于某些特定的特性,网络架构可能看起来像这样:
LightFM 架构,图片由作者提供。
优势
这个方法的好处是,即使你将新用户或电影添加到数据库中,只要知道它们的特性(内容),你也可以创建有意义的嵌入。你不会知道 ID 的嵌入——这是矩阵分解方法中的主要问题——但我们希望其他嵌入可以弥补这一点。在冷启动设置中,user_id 或 movie_id 是未知的,但我们仍然可以给它们一些默认的嵌入。
在 TensorFlow 中的实现
仅用两个 ID 作为输入,明确指定输入、编码、嵌入和偏差就足够了。然而,对于我们的特性数量,先定义一些配置然后使用循环是有意义的。
注意: 我们将省略 movie_title 和 movie_genres ,因为我们必须以不同于其他特性的方式处理它们。然而,我会告诉你如何将这些特性也纳入其中。
features_config = {
"user_id": {"entity": "user", "dtype": tf.int64},
"bucketized_user_age": {"entity": "user", "dtype": tf.int64},
"user_gender": {"entity": "user", "dtype": tf.int64},
"user_occupation_label": {"entity": "user", "dtype": tf.int64},
"movie_id": {"entity": "movie", "dtype": tf.int64},
"user_zip_code": {"entity": "user", "dtype": tf.string},
"user_occupation_text": {"entity": "user", "dtype": tf.string},
}
for name, config in features_config.items():
if config["dtype"] == tf.int64:
config["encoding_layer_class"] = tf.keras.layers.IntegerLookup
elif config["dtype"] == tf.string:
config["encoding_layer_class"] = tf.keras.layers.StringLookup
else:
raise Exception
config["vocab"] = train[name].unique()
我们现在有一个详细的配置字典,它告诉我们每个特性的信息。
-
输入层需要的dtype,
-
特性是否属于电影或用户特性,
-
需要哪个查找层,即
IntegerLookup
用于整数特性,StringLookup
用于字符串特性, -
以及词汇表,即每个特性的唯一类。
然后,我们可以定义一个 TensorFlow 模型来实现我们在 LightFM 架构图中看到的内容:
# define input layers for each feature
inputs = {
name: tf.keras.layers.Input(shape=(1,), name=name, dtype=config["dtype"])
for name, config in features_config.items()
}
# encode all features as integers via the lookup layers
inputs_encoded = {
name: config"encoding_layer_class"(inputs[name])
for name, config in features_config.items()
}
# create embeddings for all features
embeddings = {
name: tf.keras.layers.Embedding(
input_dim=len(config["vocab"]) + 1,
output_dim=32,
)(inputs_encoded[name])
for name, config in features_config.items()
}
# create embeddings for all features
biases = {
name: tf.keras.layers.Embedding(input_dim=len(config["vocab"]) + 1, output_dim=1)(
inputs_encoded[name]
)
for name, config in features_config.items()
}
# compute the user embedding as the sum of all user feature embeddings
user_embedding = tf.keras.layers.Add()(
[
embeddings[name]
for name, config in features_config.items()
if config["entity"] == "user"
]
)
# compute the movie embedding as the sum of all movie feature embeddings
movie_embedding = tf.keras.layers.Add()(
[
embeddings[name]
for name, config in features_config.items()
if config["entity"] == "movie"
]
)
# compute the user bias as the sum of all user feature biases
user_bias = tf.keras.layers.Add()(
[
biases[name]
for name, config in features_config.items()
if config["entity"] == "user"
]
)
# compute the movie bias as the sum of all movie feature biases
movie_bias = tf.keras.layers.Add()(
[
biases[name]
for name, config in features_config.items()
if config["entity"] == "movie"
]
)
# do the exact same thing as in matrix factorization,
# i.e. compute the dot product of the user and movie embedding,
# add the user and movie bias, and squash the result into the range [1, 5]
dot = tf.keras.layers.Dot(axes=2)([user_embedding, movie_embedding])
add = tf.keras.layers.Add()([dot, user_bias, movie_bias])
flatten = tf.keras.layers.Flatten()(add)
squash = tf.keras.layers.Lambda(lambda x: 4 * tf.nn.sigmoid(x) + 1)(flatten)
model = tf.keras.Model(
inputs=[inputs[name] for name in features_config.keys()], outputs=squash
)
model.compile(loss="mse", metrics=[tf.keras.metrics.MeanAbsoluteError()])
我知道这很繁重。但如果你也阅读了我关于基于嵌入的推荐系统的其他文章,应该没有大惊小怪的地方。我们准备好训练模型了!
model.fit(
x={name: X_train[name].values for name in features_config.keys()},
y=y_train.values,
batch_size=256,
epochs=100,
validation_data=(
{name: X_eval[name].values for name in features_config.keys()},
y_eval.values,
),
callbacks=[tf.keras.callbacks.EarlyStopping(patience=1, restore_best_weights=True)],
)
# Output:
# [...]
# Epoch 6/100
# 313/313 [==============================] - 1s 3ms/step - loss: 0.7626 - mean_absolute_error: 0.6836 - val_loss: 0.9836 - val_mean_absolute_error: 0.7985
测试集上的表现:
model.evaluate(
x={name: X_test[name].values for name in features_config.keys()},
y=y_test.values,
batch_size=1_000_000,
)
# Output:
# 1/1 [==============================] - 1s 667ms/step - loss: 1.0153 - mean_absolute_error: 0.8135
你也可以尝试在这种设置中进行矩阵分解;你只需将features_config
字典更改为
features_config = {
"user_id": {"entity": "user", "dtype": tf.int64},
"movie_id": {"entity": "movie", "dtype": tf.int64},
}
通过删除一些行,然后执行剩余的代码。在这种情况下,结果是测试 MSE 为 1.322 和 MAE 为 0.953,这比 LightFM 结果差得多。这看起来很棒!
处理电影类型
到目前为止,我们忽略了可能极具信息量的列movie_genres,因为它比分类变量更难处理,因为这里我们有一个整数列表而不是单个整数。所以,我们必须制定一些逻辑来处理这个问题。
图片由作者提供。
处理这个最简单的方法是为每个类型创建一个嵌入,然后取它们的均值。你可以使用GlobalAveragePooling1D
层来实现这一点。
为了在代码中实现这个想法,请执行以下操作:
# new movie embeddings
all_movie_genres = train["movie_genres"].explode().unique().astype(int) # get all different genres
movie_genres_input = tf.keras.layers.Input(shape=(None,), name="movie_genres")
movie_genres_as_integer = tf.keras.layers.IntegerLookup(vocabulary=all_movie_genres)(movie_genres_input)
movie_genres_embeddings = tf.keras.layers.Embedding(input_dim=len(all_movie_genres) + 1, output_dim=32)(movie_genres_as_integer)
movie_genres_biases = tf.keras.layers.Embedding(input_dim=len(all_movie_genres) + 1, output_dim=1)(movie_genres_as_integer)
movie_genres_embedding = tf.keras.layers.GlobalAveragePooling1D(keepdims=True)(movie_genres_embeddings)
movie_genres_bias = tf.keras.layers.GlobalAveragePooling1D(keepdims=True)(movie_genres_biases)
movie_embedding = tf.keras.layers.Add()(
[
embeddings[name]
for name, config in features_config.items()
if config["entity"] == "movie"
] + [movie_genres_embedding] # add the movie genres embedding here as well
)
# new movie bias
movie_bias = tf.keras.layers.Add()(
[
biases[name]
for name, config in features_config.items()
if config["entity"] == "movie"
] + [movie_genres_bias] # add the movie genres bias here as well
)
# add the movie inut to the inputs
model = tf.keras.Model(
inputs=[inputs[name] for name in features_config.keys()] + [movie_genres_input], outputs=squash
)
其余部分保持不变。在拟合、评估和测试模型时,你只需将特征movie_genres添加到模型中。由于类型的形状有点困难,因为列表的长度不同,所以 TensorFlow 在将其转换为常规张量时遇到问题。幸运的是,TensorFlow 通过提供ragged tensors(通过tf.ragged.constant
)来处理这些可变大小的张量。
model.fit(
x={
**{name: X_train[name].values for name in features_config.keys()},
"movie_genres": tf.ragged.constant(X_train["movie_genres"].values)
},
# [...]
在测试集上拟合和评估模型显示了另一个改进,尽管小于预期。MSE 约为 1.0,MAE 约为 0.807。
所有结果的综合。图片由作者提供。
处理电影标题
另一个有趣的特征是我们迄今为止忽略的,因为它包含关于电影系列的信息。有了这些信息,我们可以使模型更容易学习一些用户非常喜欢所有的蝙蝠侠电影。例如,编码这个是你的作业。一种方法是将标题字符串分割成单词列表,然后按照我们对待类型的方法进行处理。你甚至可以使用句子编码器、类似变换器的架构、LSTM 或其他任何方法将文本转换为嵌入。
进行预测
你可以通过提供所有必要的特征来进行预测,方法如下:
query = {
"user_id": tf.constant([-1]), # unknown user!
"bucketized_user_age": tf.constant([18]),
"user_gender": tf.constant([0]),
"user_occupation_label": tf.constant([12]),
"movie_id": tf.constant([1]),
"user_zip_code": tf.constant(["b'65712'"]),
"user_occupation_text": tf.constant(["b'writer'"]),
"movie_genres": tf.ragged.constant([[1, 2, 3]])
}
model.predict(query)
# Output:
# array([[4.0875683]], dtype=float32)
在这里,你可以看到一个年轻的未知用户,该用户性别为 0,职业标签为 12,居住在 65712 邮政编码区域,且是一名作家,他可能会喜欢 ID 为 1 的电影,该电影属于 1、2 和 3 这些类型。
来自论文的更多有趣见解
我的一个小实验表明,LightFM 可以提高模型性能,正如[1]中所述。这很棒,尽管这可能是你已经预料到的,因为LightFM 是矩阵分解的一个推广版本。
在这方面,论文作者写道:
-
“在冷启动和低密度场景下,LightFM 的表现至少与纯内容模型一样好,当(1)训练集中有协同信息或(2)模型中包含用户特征时,它的表现会大幅超越这些模型。”
-
“当协同数据丰富(热启动,密集的用户-项目矩阵)时,LightFM 的表现至少与 MF 模型一样好。”
-
“LightFM 生成的嵌入编码了关于特征的重要语义信息,可用于相关的推荐任务,如标签推荐。”
这些陈述没有证明,但他通过在两个数据集上测试得出了这个结论。这两个数据集都有二进制标签, 意味着该项目对用户有用或无用。对于二进制标签,他选择了 AUC 作为评估指标,并在此表格中总结了他的发现:
论文中,MF = 矩阵分解。数字越高越好。
在这里,我们还可以看到 LightFM 在冷启动甚至热启动设置中超越了其他方法。很高兴看到 LightFM 在热启动设置中不比 MF 差,但主要的卖点是LightFM 在冷启动设置中完全击败了 MF。
记住: AUC 为 0.5 意味着随机猜测,即某用户随机挑选的 相关项目 的评分高于该用户的 随机选择的非相关项目 的概率为 50%。
结论
在这篇文章中,我们讨论了纯协同推荐系统(例如矩阵分解)在面对新用户或新项目时遇到的问题,这被称为冷启动问题。
一旦我们获得更多关于用户和项目的信息,我们可以缓解这个问题,因为模型可以学习一些一般性的模式,例如年轻人不喜欢老电影。因此,如果我们有一个新用户,并且知道他们年轻,好的模型应该将老电影的评分低于新电影。
不过,如果这个新用户继续评分,模型可以调整并学会显示像 诺斯费拉图 这样的老电影,如果用户的行为表明这可能是一个合适的选择。
具有这些理想特性的模型对我来说感觉有点贝叶斯:
用户和项目特征嵌入作为一种先验,对预测具有很大的影响,只要我们没有互动数据。随着互动数据的到来,这种先验会发生变化。
然而,一个有趣的问题是,一旦我们拥有密集评分矩阵,例如每个用户评分了 95%的所有电影,用户和项目特征是否失去相关性。
不管怎样,LightFM 是一个很好的候选模型,这一点从我和论文作者的实验中可以看出。LightFM 在我们的选定数据集上表现优于 MF,特别是在冷启动设置中。如果冷启动不是问题,改进是微小的,甚至可能只是统计噪声。
你也可以尝试论文 作者对 LightFM 的实现。
参考文献
[1] M. Kula, 用户和项目冷启动推荐的元数据嵌入 (2015),arXiv 预印本 arXiv:1507.08439
我希望你今天学到了一些新的、有趣的和有用的东西。感谢阅读!
如果你有任何问题,可以在 LinkedIn上联系我!
如果你想深入了解算法的世界,可以尝试我的新出版物关于算法的一切!我仍在寻找作者!
从直观解释到深入分析,算法通过示例、代码和精彩的方式展现出活力……
medium.com](https://medium.com/all-about-algorithms?source=post_page-----69bf2f0f0b9b--------------------------------)