docs-merge-18
TowardsDataScience 2024 中文翻译(十九)
静态人工智能基准的死亡
基准测试作为成功的衡量标准
·发布于 Towards Data Science ·3 分钟阅读·2024 年 3 月 21 日
--
基准测试常常被誉为成功的标志。它们是衡量进展的一个重要方式——无论是跑完 4 分钟以内的一英里,还是在标准化考试中取得优异成绩。在人工智能(AI)领域,基准测试是评估模型能力的最常见方法。OpenAI、Anthropic、Meta、Google 等行业领袖竞相争夺,通过超越彼此的基准分数来领先。然而,最近的研究和行业中的抱怨开始对这些常见基准是否真正捕捉到模型能力的本质提出了质疑。

来源:Dalle 3
数据污染导致记忆化
新兴的研究表明,某些模型的训练集可能已经被其评估所用的数据污染——这引发了对基准分数是否真正反映了模型对知识的理解的质疑。就像电影中演员可以扮演医生或科学家一样,他们在台词中虽然说得头头是道,但并不真正理解背后的概念。当基里安·墨菲在电影《奥本海默》中扮演著名物理学家 J·罗伯特·奥本海默时,他可能并没有理解他所讲的复杂物理理论。尽管基准测试旨在评估模型的能力,但如果像演员一样,模型只是记住了这些分数,它们真的是在评估模型的能力吗?
亚利桑那大学的最新研究发现,GPT-4 受到 AG News、WNLI 和 XSum 数据集的污染,从而使其相关的基准失去可信度[1]。此外,来自中国科技大学的研究人员发现,当他们将“探测”技术应用于流行的 MMLU 基准[2]时,结果显著下降。
他们的探测技术包括一系列方法,旨在挑战模型对问题的理解,当问题以不同的方式提出,选项不同,但正确答案相同时。探测技术的示例包括:转述问题、转述选项、排列选项、在问题中加入额外背景信息,以及为基准问题添加新的选项。
从下面的图表中可以看出,尽管每个测试的模型在未改变的“原始”MMLU 基准上表现良好,但当探测技术被加入到基准的不同部分(语言理解(LU)、问题解决(PS)、领域知识(DK)、所有部分)时,它们的表现并没有那么强劲。

“原始”代表未改变的 MMLU 基准的表现。其他键代表在修改过的 MMLU 基准部分上的表现:语言理解(LU)、问题解决(PS)、领域知识(DK)、所有部分
关于如何评估人工智能的未来考虑
这种不断变化的情况促使我们重新评估如何评估人工智能模型。现在越来越明显的是,我们需要既能可靠展示能力,又能预见数据污染和记忆化问题的基准。
随着模型不断发展并可能更新其训练集以包括基准数据,基准的生命周期将自然变短。此外,模型的上下文窗口正在迅速增大,允许在模型的响应中包含更多的上下文。上下文窗口越大,污染数据对模型学习过程的间接影响越大,这会使模型对已见过的测试样本产生偏见。
动态与现实世界基准的崛起
为了应对这些挑战,创新方法如动态基准正在出现,采用诸如:改变问题、使问题复杂化、向问题中引入噪声、转述问题、反转问题的极性等策略[3]。
下面的示例提供了几种方法来改变基准问题(无论是手动还是由语言模型生成)。

来源:《基准自我进化:一种多代理框架用于动态 LLM 评估》
随着我们前进,将评估方法与现实世界应用更加紧密地对接的必要性变得愈加明显。建立能够准确反映实际任务和挑战的基准,不仅能提供更真实的 AI 能力衡量标准,还能引导小型语言模型(SLMs)和 AI 代理的开发。这些专业化的模型和代理需要能够真正捕捉它们执行实际和有益任务潜力的基准。
《使用 OpenAI GPT3.5 解析结构化数据的终极指南》
系统性地比较了 Instructor、Fructose、Mirascope 和 Langchain 在三个复杂的现实世界结构化数据解析任务中的表现。
·发表于 Towards Data Science ·阅读时间:8 分钟·2024 年 4 月 16 日
--

图像由作者使用 ChatGPT 生成
从大型语言模型(LLMs)解析结构化数据对于超出简单问题的任务来说可能非常令人沮丧。然而,可靠地将 LLM 输出解析为预定义的结构对于将 LLM 集成到其他软件系统和生成性 AI 应用中至关重要。OpenAI 通过发布 GPT 函数调用(链接)和 JSON 模式(链接)走在了前列。尽管如此,这些方法仍然需要大量的提示工程、强大的解析、重试和优雅的错误处理,才能在生产级现实问题中可靠运行。
以下是我在使用 LLM 解析结构化数据时遇到的一些问题。本文完全由人工撰写,并借助 Grammarly 语法检查工具,Grammarly 一直是我自 2019 年以来的写作方法。
-
分类: LLM(大语言模型)必须严格遵守允许的类别列表,实际问题中类别数量可能多达数十到数百个。在类别超过几个时,LLM 会开始对不允许的类别进行幻觉生成。
-
命名实体识别(NER): LLM 应仅选择文本中明确出现的实体。这些实体…
机器学习工程师与数据科学家的区别
帮助你决定是想成为数据科学家还是机器学习工程师
·发布于 Towards Data Science ·阅读时间:6 分钟·2024 年 11 月 24 日
--

图片来自 Mohammad Rahmani 于 Unsplash
在过去几年中,技术领域出现了一个新的角色——机器学习工程师(MLE)。一些人常常将 MLE 与数据科学家混淆;然而,它们之间有着相当大的区别,我将在本文中解释这些区别,以便帮助你在考虑转职时有更清晰的认识!
数据科学家
让我们从定义数据科学家开始。作为一名数据科学家工作了超过三年,我觉得自己有足够的经验来做出这个定义!
如今,数据科学是一个广泛的术语,它在不同的公司中含义不同;可以说,它是模糊的!(我常常对此抱怨)
在某些公司,数据科学家的工作可能完全是分析性的,负责设定指标,而在另一些公司,他们则可能在构建和部署机器学习模型。
通往 AI 产品管理的 DIY 路径:选择一个初始项目
通过动手实践和反复试错来培养现实世界的技能。
·发表于 Towards Data Science ·阅读时间 7 分钟·2024 年 8 月 31 日
--
我最近的博客文章“我们需要提高 AI 产品经理的标准”激发了广泛的讨论,大家都对推动 AI 产品管理领域的发展充满热情。许多现任和有志成为产品经理的人随后联系我,询问如何在成为 AI 产品经理的过程中学习更多关于 AI 的知识。
根据我的经验,最有效的 AI 产品经理在两个关键领域表现出色:识别 AI 可以带来价值的机会,并与模型开发人员合作,确保技术的有效部署。这需要对不同类型的模型在实际产品中上线后可能表现出的行为有一个扎实的理解——这是许多新手经常感到吃惊的现实。无论是面对客户的应用,还是支撑产品的后台数据管道,炫目的演示或早期原型与实际产品表现之间的差距往往是巨大的。
培养这种直觉的最佳方式是将一系列模型部署到产品中,并在过程中犯下许多错误。其次的好方法是了解你公司其他团队的工作,并从他们的错误(和成功)中学习。翻阅你能找到的任何文档,并在可能的情况下,参加产品评审或团队更新会议。通常,直接参与项目的人员会很乐意与您交流,回答您的问题,并提供更多背景信息,尤其是如果你的团队正在考虑类似的项目。
那么,如果你在一家公司没有做任何与人工智能相关的工作呢?或者你的公司专注于一小部分技术?又或者你正在找工作?
除了查看资源以熟悉术语和最佳实践外,我还建议你自己开发 AI 项目。我还建议做一些副项目,即使你从日常工作中能学到很多。每个 AI 用例都有其独特之处,接触到的例子越多,你就越能快速培养出对什么有效、什么无效的直觉。
对于一个入门项目,我建议从 LLM(大语言模型)开始,比如Claude或ChatGPT。你应该能够在几个小时内(如果你已经懂得编程和编写有效的提示语,甚至几分钟内)启动一个有实质内容的项目。虽然并非所有实际公司的 AI 项目都会使用 LLM,但它们正获得越来越大的关注。更重要的是,仅凭基础的数据科学或编程知识,你也能更容易地创建出自己的工作模型。如果你的编程技能生疏了,使用开发者 API 可以让你有机会温习一下,如果遇到困难,LLM 是一个很好的资源,能够帮助你生成代码和解决问题。如果你对编程和 LLM 都不熟悉,那么使用在线聊天界面是一个很好的热身方式。
好的入门项目的特点
但使用 ChatGPT 网站或应用程序来提高工作效率(例如总结文章或草拟电子邮件)和一个真正的项目之间有什么区别呢?
一个项目应该旨在以可重复的方式解决一个实际问题,重现你在为公司部署产品时会遇到的约束。正是这些细微差别帮助你磨练一些最重要的技能,特别是在 AI 产品管理工作中,尤其是模型评估。查看我的文章“什么是评估,为什么产品经理应该关心”以了解模型评估的基础知识。
为了确保你正在做的是一个真正的项目,并且可以进行自己的小型评估,确保你具备以下条件:
-
多个测试样本:选择那些可以在至少 20 个不同的例子或数据点上评估模型的项目。
-
多样化数据:确保你的数据集包含多种不同的场景,以测试哪些因素会导致模型出错(从而为你提供更多修正机会)。
-
明确的评估标准:从一开始就明确有效模型或产品的行为表现。你应该为你的 20 个例子准备 20 个理想的回答,以便评估模型。
-
真实世界相关性:选择一个反映你工作、个人生活或你身边某个人实际用例的问题。你需要对模型的有效性有充分了解才能进行评估。
示例项目想法
请不要做这些特定的项目,除非其中某个项目真的引起了您的兴趣。这些项目仅用于说明目的,帮助区分什么是实际的项目,而不是单纯的查询:
礼物推荐分类器
-
目标:判断某个产品是否适合作为送给直言不讳的朋友或家庭成员的礼物。
-
方法:使用文本生成来评估产品标题和描述,并提供一个描述受众口味的提示。如果您想做得更复杂一些,可以使用视觉能力来评估产品描述、标题和产品图片。
-
测试样本:50 张不同的产品图片和描述。为了增加难度,您的示例应包括一些显然不好的产品,一些显然好的产品,许多边界模糊的产品,以及一些完全随机的产品。
-
评估:让目标礼物接收者评估这些产品列表,按“完全不行”、“一般”和“非常合适”这样的尺度对每个产品与他们偏好的匹配度进行评分。将这些评分与模型的分类结果进行对比。你还可以通过让模型提供每个物品是否适合的理由,获取大量有用信息。这将帮助您解决失败问题并指导提示更新,同时也能让您更深入了解 LLM 是如何“思考”的。
食谱书数字化
-
目标:将您祖母最喜欢的绝版食谱书转化为一款适合您和表兄弟姐妹们使用的应用程序。
-
方法:使用视觉能力从食谱书页的照片中提取食谱。
-
测试样本:20 张不同类型食谱的图片。为了让开始更简单,您可以先专注于甜点。示例可能包括 3 种饼干、4 种蛋糕等。
-
评估:每个食谱中所有关键的原料和步骤是否都包含在最终输出中?仔细对比 LLM 的输出和原始食谱,检查原料、量度和烹饪步骤的准确性。如果能够将最终数据转化为某种结构化格式(例如 JSON 或 CSV),便于在应用程序中使用,将获得额外分数。

图片由作者使用 Midjourney 生成
公众人物引语提取器
-
目标:帮助公众人物的公关团队识别他们所说的任何引语或事实,供事实核查团队验证。
-
方法:使用文本生成来评估文章的文本,并返回每篇文章中提到的关于公众人物的引语和事实列表。
-
测试样本:20 篇关于公众人物的近期文章,涵盖至少 3 个不同事件,来自至少 4 家不同的出版物(例如,一家八卦网站、一家像《纽约时报》这样的全国性报纸,以及介于两者之间的媒体,如 Politico)。
-
评估:仔细阅读每篇文章,看看是否遗漏了公共人物的任何事实或引用。想象一下,如果你的总结者出现幻觉(例如:说他们说了某些他们并没有说过的话)或错过了关键信息,你的工作可能会岌岌可危。检查总结者找到的所有引用和事实是否确实与您的公共人物相关,并且它们是否都在文章中提到。
欢迎你使用任何大型语言模型(LLM)来进行这些项目,但根据我的经验,如果你在编程经验上有所限制,ChatGPT API 是最容易入手的。一旦成功完成一个项目,在相同数据上评估另一个 LLM 是相对简单的。
记住,入门项目的目标不是完美,而是找到一个有一定复杂度的有趣项目,以确保你遇到困难。学会排除故障、迭代,甚至撞墙时意识到某些事情不可能发生,这将帮助你磨练对可行和不可行的直觉,以及了解需要多少工作量。
拥抱学习过程
培养对 AI 能力和局限性的强烈直觉对于有效的 AI 产品管理至关重要。通过参与实践项目,你将获得关于模型评估、故障排除和迭代的宝贵经验。这些实践知识将使你成为模型开发者更有效的合作伙伴,使你能够:
-
确定 AI 能够真正增加价值的领域
-
对 AI 项目的时间表和资源需求做出实际估计
-
在故障排除和评估过程中做出有意义的贡献
在处理这些项目时,你将对 AI 的实际应用和挑战有更深刻的理解。这些经验将在快速发展的 AI 产品管理领域使你脱颖而出,帮助你领导创新项目并做出明智的决策,推动产品成功。
记住,成为一名专家 AI 产品经理的旅程是一个持续的过程。拥抱学习过程,保持好奇心,不断寻找新的挑战来提升你的技能。通过专注和实践经验,你将能够在 AI 产品开发的激动人心的前沿领域中游刃有余。
对你的 AI 项目或本文有疑问吗?可以通过LinkedIn与我联系,继续讨论。
双摆及其超越:通过多臂摆研究混沌
使用 VPython 仿真模拟混沌运动,并探讨定义混沌系统的关键特征。
·发表于Towards Data Science ·阅读时间 12 分钟·2024 年 4 月 3 日
--
任何物理学学生都研究过低级的单摆,可能比他们想要的还要多。与弹簧上的质量一起,摆是简谐运动的典型例子。这里的关键词是简。你可能知道,单摆非常容易用数学建模。单摆的周期可以通过下面的公式表示,其中 g 是重力加速度,L是摆长。

角位移稍微有趣一些,但仍然不算太深奥。方程本身更复杂,但实际上它只是一个余弦波。下面的方程表示单摆的角位移,其中θ是角位移,A是最大角位移(在大多数情况下,这只是释放角度),ω是角频率,t是时间。

说完这些,我们来看看更有趣的内容,尽管实际上它几乎不可能实现(你会明白的),数学和物理学。
如果你曾经操作过一个常规的摆,你或许对双摆有一定了解,它让人联想到臭名昭著的三体问题。三体问题展示了这样一个观点:在一个系统中,三个物体的运动分析解是无法解决的,因为每个物体的力同时与其他物体相互作用。简而言之,双摆无法通过解析方法解决。根据上面提到的摆的方程,真正无法解出的是θ,即两个摆的角位移。你也无法解出周期,但这主要是因为双摆并没有一个固定的周期。以下是我们讨论过的双摆系统的视觉表示,支点固定在墙上,角度用θ1和θ2标记,悬挂的质量用m1和m2标记。

重申一下——在我们当前的物理学中,我们无法解析预测双摆的运动。那么,为什么呢?实际上,运动并非完全随机,而是混沌的。我们如何证明混沌与随机不同呢?这是我们稍后将要解答的问题。
你可能在 YouTube 上看到过一些关于混沌或混沌理论的标题,我敢打赌缩略图看起来大概是这样的:

那张图像,以及许多其他类似蝴蝶翅膀的图形,通常与混沌理论相关联。混沌理论中一个不常被认为是其一部分的概念是双摆、三摆以及其他多摆的情况。让我们探索混沌运动的一些特性,并尽可能地深入了解它,稍后我们将借助计算机程序来实现这一点。
我们可以调查的一个特性是,起始条件的微小变化对最终结果的影响。这个变化最终会变得微不足道,还是会导致与原始结果完全不同的结论呢?以下是双摆在 t=50 时的计算机仿真,其中θ1和θ2的起始角度分别为 80°和 0°。(角度与上图所示相同)

红线拖曳着摆的底部质量(m2),并在画出后保持 2 秒钟。
现在,让我们进行完全相同的仿真,图像展示的是 t=50,所有的质量和摆臂长度都是完全相同的。然而,θ1和θ2的角度分别为 79°和 0°。θ1的起始值变化了 1°,让我们看看它如何影响结果。

不出所料(或者说出乎意料,我不确定你期待的是什么),仅仅改变 1°,系统发生了剧烈变化,最终的位置和系统所描绘的轨迹与最初的图像完全不相似。这与任何外部变量无关,因为在我们的模拟中没有摩擦、空气阻力或其他任何障碍。这个例子展示了一个观点:当一个系统表现出混沌运动时,比如双摆,即使是最微小的条件变化,也能导致结果发生巨大变化。
所以,我们已经掌握了混沌运动的一个有趣方面——初始条件的微小变化能导致结果发生巨大变化。让我们再尝试识别一些其他方面。如果我们再次进行相同的双摆实验,会发生什么呢?从实验上来说,这几乎是不可能的;它需要在测量诸如引力场、质量、空气阻力和实验中的每一个其他变量时具有无限精度,然后使用你那无限精度的数据重新设置实验,完全相同。然而,我们有一个很好的模拟工具,可以使这个挑战消失。
让我们运行一个类似之前的双摆模拟,但改变了一些变量,以便图像与之前不同:t=50,θ1=80°,θ2=15°,摆的物理特性(长度、质量)保持不变。以下是结果:

剧透提醒,第二次的结果完全相同……第三次、第四次、第五次和第六次也是一样。为了证明这一点,我们还可以尝试用三摆做这个实验。我们将使用相同的初始条件,但现在有了第三个角度θ3,所以我们将使用θ1=80°,θ2=15°,θ3=0°。不过,三摆的计算量要大得多,所以这张图片是在 t=10 时拍摄的。

在三摆的模拟中,红线是由第三个质量拖动的。
尽管三摆的模拟比双摆更复杂,但它表现出相同的特性——在相同初始条件下,三摆也会重复其模式。这种在相同条件下得到相同结果的思想证明了混沌运动和混沌系统是确定性的,而非随机的。再次强调,由于实验上很难证明这一点,或者在实践中运用它,这并不会对我们对现实世界的理解产生太大影响。然而,我们现在知道,混沌系统并非随机的,并且在掌握正确的知识下是可以预测的。此外,由于我们证明了微小的变化会产生巨大的影响,我们也知道,要有效预测混沌系统的结果,必须具备极高的精度。
我们现在已经确定了混沌运动的两个重要特性:第一个是微小的变化会对系统的结果产生巨大影响,第二个是混沌运动并非随机的,而是确定性的。让我们再找一个特性——正如文章一开始所讨论的,双摆中你无法求解的变量是角位移。然而,我们也说过,求解周期是徒劳的,因为双摆不会重复——让我们来验证这个理论。
首先,让我们以以下条件运行双摆实验:θ1=30°,θ2=30°,质量和长度与之前相同。这次,我们使用 VPython 的绘图功能记录底部质量随着时间变化的高度图表:

如果你问我,那个看起来相当周期性。那么,这是否意味着上述系统不是一个混沌系统?如果运动不是混沌的,是否意味着我们能够找到双摆在能量低于某个阈值时的运动的解析解?这个问题我留给读者去思考。不过,为了做到这一点,你可能需要我在本文中编写并使用的程序的帮助,所以我们来一起看看。这个程序是用 VPython 编写的,并使用了GlowScript,因此请确保你在 WebVPython 系统上运行它,而不是在任何 Python 编辑器中。
首先,我们来定义程序中使用的物理常数。这些常数包括质量、臂长和重力。除此之外,我们还将定义系统的初始条件,其中 theta1 和 theta2 是初始角度,theta1dot 和 theta2dot 是臂的初始角速度。最后,我们还将设置初始时间为 0,并定义 dt,它是在每次循环迭代中我们将向前推进的时间量。
#lengths of the strings
L1 = 1.5 #top string
L2 = 1 #middle string
#masses
M1 = 2 #top moving ball
M2 = 1 #middle moving ball
#g
g = 9.8
#starting angles and velocities
theta1=30*pi/180 #release angle top ball
theta2= 30*pi/180 #release angle middle ball
theta1dot = 0
theta2dot = 0
t = 0
dt = 0.001
接下来,我们定义系统的所有部分,包括枢轴点、质量 1(m1)、臂 1(stick1),以及第二组质量和杆(m2,stick2)。
pivot = sphere(pos=vector(0,L1,0), radius=0.05)
m1 = sphere(pos=pivot.pos-vector(0,L1,0),radius=0.05,color=color.white)
stick1 = cylinder(pos=pivot.pos,axis=m1.pos-pivot.pos,radius=0.015,color=color.yellow)
m2 = sphere(pos=m1.pos-vector(0,L2,0),radius=0.05,color=color.white)
stick2 = cylinder(pos=m1.pos, axis=m2.pos-m1.pos, radius=0.015,color=color.yellow)
现在我们将质量和杆的位置调整到反映初始角度的状态,如果你运行代码之前,它们只会是两根垂直的杆。为此,我们只需使用一些三角函数将质量放置到杆的末端位置,然后将杆设置为连接每个质量之间的距离。在这里,我们还使用了 VPython 的‘attach_trail’方法,让底部质量生成轨迹。
m1.pos = pivot.pos+vector(L1*sin(theta1),-L1*cos(theta1),0)
m2.pos = m1.pos+vector(L2*sin(theta2),-L2*cos(theta2),0)
stick1.axis = m1.pos - pivot.pos
stick2.pos = m1.pos
stick2.axis = m2.pos - m1.pos
attach_trail(m2, retain=200, color=color.red)
接下来,我们开始程序的主循环;在这个模拟中,和许多物理模拟一样,我们将使用一个while循环,并在 t=50 时退出。我们还将使用rate()方法来设置程序运行的速度。
while t <= 50:
rate(1000)
现在我们必须深入实际的数学运算。我们将使用一些微积分(其实并不是很复杂,但确实涉及到导数)、三角学和代数来首先计算角加速度,然后用它来增量角速度,接着将角速度应用到质量和杆的位置,以便根据它们计算出来的加速度来移动它们。
为了计算顶部质量的加速度,我们使用这个公式来表示角加速度,或者是θ1的二阶导数:

你也可以使用以下代码表示相同的方程:
theta1ddot = (-g*(2*M1 + M2)*sin(theta1) -M2*g*sin(theta1 -2*theta2)-2*sin(theta1 - theta2)*M2*(L2*theta2dot**2 + L1*cos(theta1 - theta2)*theta1dot**2))/(L1*(2*M1 + M2 - M2*cos(2*theta1 -2*theta2)))
对于第二个质量的加速度,我们必须使用不同的公式,因为它代表了系统中的另一个部分。

这个方程在程序中写成:
theta2ddot = (2*sin(theta1 - theta2)*((M1 + M2)*L1*theta1dot**2 + g*(M1 + M2)*cos(theta1) + L2*M2*cos(theta1 - theta2)*theta2dot**2))/(L2*(2*M1 + M2 - M2*cos(2*theta1 -2*theta2)))
接下来,让我们增加角速度变量‘theta1dot’和‘theta2dot’。为此,我们只需使用下面的方程,它通过角加速度乘以 dt 来增量角速度。虽然图像中只列出了 theta1dot 的方程,但θ1和θ2的计算是相同的。

为了我们的目的,我们用代码表示这个公式:
theta1dot = theta1dot + theta1ddot*dt
theta2dot = theta2dot + theta2ddot*dt
对于双摆运动的最终部分,我们必须通过角速度来增量θ1和θ2。这通过下面的方程表示,其中我们将角度θ设为前一个θ加上角速度乘以 dt。同样,θ1和θ2的计算是完全相同的。

在代码中,我们将这个方程写成:
theta1 = theta1 + theta1dot*dt
theta2 = theta2 + theta2dot*dt
现在,我们必须更新程序中质量和杆的位置,以便能够看到程序的结果。首先,我们将更新质量的位置。我们将采取一种直观的方法来更新这些位置;因为在系统中永远不会改变的一件事是杆的长度,所以中间的质量(m1)将与支点保持等于 L1 的距离。从逻辑上讲,我们可以构建以下关于 m1 位置的方程:

相同的公式可以用来反映 m2 的位置公式,只是使用了θ2和 L2,并且它是基于 m1 的位置,而非支点。请参见下面的公式。

这两个公式在我们的程序中写得非常简单,因为 VPython 的一个特点是它使得向量计算变得非常简单。所有对象的位置信息已经作为向量存储,所以你所需要做的就是使用 vector()方法定义一个新的向量,并将其用于计算。
m1.pos = pivot.pos + vector(L1*sin(theta1),-L1*cos(theta1),0)
m2.pos = m1.pos + vector(L2*sin(theta2),-L2*cos(theta2),0)
现在,我们必须迈出最后一步,通过更新杆的位置来实现我们计算的结果。从技术上讲,这并不是完全必要的,因为质量仍然会显示并遵循应该的模式。然而,为了视觉效果,让我们编写这段代码。数学部分并不太有趣,因此代码如下。首先,我们设置第一个杆的轴,使其填充从支点到质量 m1 的距离。接下来,我们将第二根杆的位置设置为质量 m1。最后,我们将第二根杆的轴设置为第一个和第二个质量之间的距离。最后一行是通过增量 dt 来更新 t,以使程序向前运行。
stick1.axis = m1.pos - pivot.pos
stick2.pos = m1.pos
stick2.axis = m2.pos - m1.pos
t += dt
这里是最终的代码,方便复制和粘贴:
Web VPython 3.2
#lengths of the strings
L1 = 1.5 #top string
L2 = 1 #middle string
#masses
M1 = 2 #top moving ball
M2 = 1 #middle moving ball
#g
g = 9.8
t = 0
dt = 0.001
#starting angles and velocities
theta1=80.5*pi/180 #release angle top ball
theta2= 0*pi/180 #release angle middle ball
theta1dot = 0
theta2dot = 0
pivot = sphere(pos=vector(0,L1,0), radius=0.05)
m1 = sphere(pos=pivot.pos-vector(0,L1,0),radius=0.05,color=color.white)
stick1 = cylinder(pos=pivot.pos,axis=m1.pos-pivot.pos,radius=0.015,color=color.yellow)
m2 = sphere(pos=m1.pos-vector(0,L2,0),radius=0.05,color=color.white)
stick2 = cylinder(pos=m1.pos, axis=m2.pos-m1.pos, radius=0.015,color=color.yellow)
m1.pos = pivot.pos+vector(L1*sin(theta1),-L1*cos(theta1),0)
m2.pos = m1.pos+vector(L2*sin(theta2),-L2*cos(theta2),0)
stick1.axis = m1.pos - pivot.pos
stick2.pos = m1.pos
stick2.axis = m2.pos - m1.pos
attach_trail(m2, retain=200, color=color.red)
eChart = gdisplay(x=500, y=0, width=600, height=400, title="", xtitle="", ytitle="", foreground=color.black, background=color.white)
ePlot = gdots(color=color.red)
while t < 50:
rate(1000)
#angular acceleration
theta1ddot = (-g*(2*M1 + M2)*sin(theta1) -M2*g*sin(theta1 -2*theta2)-2*sin(theta1 - theta2)*M2*(L2*theta2dot**2 + L1*cos(theta1 - theta2)*theta1dot**2))/(L1*(2*M1 + M2 - M2*cos(2*theta1 -2*theta2)))
theta2ddot = (2*sin(theta1 - theta2)*((M1 + M2)*L1*theta1dot**2 + g*(M1 + M2)*cos(theta1) + L2*M2*cos(theta1 - theta2)*theta2dot**2))/(L2*(2*M1 + M2 - M2*cos(2*theta1 -2*theta2)))
#angular velocity
theta1dot = theta1dot + theta1ddot*dt
theta2dot = theta2dot + theta2ddot*dt
#angular position
theta1 = theta1 + theta1dot*dt
theta2 = theta2 + theta2dot*dt
m1.pos = pivot.pos + vector(L1*sin(theta1),-L1*cos(theta1),0)
m2.pos = m1.pos + vector(L2*sin(theta2),-L2*cos(theta2),0)
stick1.axis = m1.pos - pivot.pos
stick2.pos = m1.pos
stick2.axis = m2.pos - m1.pos
t = t+dt
感谢你能看到这里,我希望你喜欢我通过双摆和三摆分析混沌运动的内容。最后,我既不是专业的物理学家,也不是程序员,所以如果你有任何建议帮助我改进我的工作,请不要犹豫,随时告诉我。
此外,文中使用的所有图像都是我自己创建的,使用了 Python 和 Microsoft Word,这两个工具都具有令人惊叹的能力。
最后,我想以一个耐人寻味的思考作为结束,这将成为我下一个项目的基础。如果你拿一个已经呈现混沌运动的多臂摆,如何在不间断的情况下为其增加一个非常大质量的臂(可能是系统中最大质量的 50 倍),并且初始角度为 0°,它的运动会如何变化呢?
Scikit-learn 的虚拟模型
始终保持虚拟模型在你身边
·发布于Towards Data Science ·8 分钟阅读·2024 年 2 月 14 日
--
虚拟模型非常简单易于设置,但却能为检查机器学习模型的性能提供重要的见解。
在这篇文章中,我想解释什么是虚拟模型,以及如何在 scikit-learn 中使用它们。

如果你喜欢或者想学习使用 scikit-learn 进行机器学习,可以查看我关于这个惊人工具包的教程系列:

Sklearn 教程
查看列表9 个故事!

所有图片来自作者。
什么是虚拟模型?
虚拟模型是非常简化的模型,旨在作为基准与实际模型进行比较。基准只是某种参照点,用于与自己进行比较。当你计算第一次交叉验证结果以估算模型性能时,通常知道分数越高越好,如果第一次尝试的分数很高,那当然很好。但通常情况并非如此。
如果第一次的准确度评分很低——或者比你预期的还要低,该怎么办?是不是因为……
今天学习和使用 Python 的最简单方法
Google Colab 和其集成的生成式 AI,一个强大的组合
·发表于 Towards Data Science ·阅读时长 7 分钟·2024 年 10 月 9 日
--

作者提供的图片,由 DALL-E 创作。
你在这篇文章中会找到的内容:关于如何使用集成在 Google Colab(一个无需安装、基于云的 Python 编程平台)中的生成式 AI 工具的指南,使其成为学习和使用 Python 的最简单方法。
学习编程比以往任何时候都更加有用和可访问。在这篇文章中,你将看到如何在一分钟内开始编程,且不需要任何先决条件,充分利用最新生成式 AI 工具的强大功能。
我从 25 年前开始编程,那时我大约 10 岁。一切都非常艰难,从安装开发工具,到学习命令,当然还包括调试。
今天,我们已经远离那个时代。Google Colab 最近集成了一套生成式 AI 工具,这些工具彻底改变了我们编程的方式。
开始编程从未如此简单。所有障碍现在都已经消除。
这是个好消息,因为编程几乎无处不在,并且在越来越多的工作中变得有用,甚至是必需的。此外,如果你懂一点代码,借助这些生成式 AI 工具,你现在可以以最小的努力走得非常远。
在这篇文章中,我将向你展示今天学习和使用 Python 的最高效方法,借助一个无需安装的工具。如果你对 Python 不陌生(知道什么是 Google Colab 和 Notebooks),你可以跳过第一部分。文章组织结构如下:
第一部分:预备知识:
-
为什么选择 Python 和 Google Colab?
-
从哪里开始学习 Python?
第二部分:Google Colab 中集成的生成式 AI 工具:
-
代码补全
-
调试
-
建议
-
图表建议
-
获取帮助
讨论
第一部分:预备知识
为什么选择 Python 和 Google Colab?
为什么选择 Python? Python 是当今最流行、最具多样性的编程语言。Python 可以用于:
-
机器学习和人工智能(例如自然语言处理、深度学习等)
-
统计与分析
-
创建和使用聊天机器人(例如 LLMs、代理等)
-
Web 开发(例如后端开发)
-
以及更多内容:金融、机器人技术、数据库访问、游戏开发等。
此外,由于其广泛的应用,Python 已成为许多职位的要求,且因为拥有大量的资源,学习起来尤其容易。
为什么选择 Google Colab? 在 Python 编程中,有很多方法可以开始。最流行的两种方式是集成开发环境(IDE)或 Notebook。Notebook 是一种基于网页的交互式编程环境,允许你在一个文档中混合代码、文本和可视化内容。
你可以选择在计算机上安装本地 Notebook 工具(例如 Jupyter Notebook),或者使用像 Google Colab 这样的基于云的在线解决方案。
由于本指南聚焦于可访问性,我选择了一个无需安装的基于云的工具。唯一的要求是拥有一个 Google 账户。所有文档都将保存在你的 Google Drive 中,因此你可以在任何计算机上工作,并轻松与他人协作。

这是一个 Notebook(Google Colab)的片段。你可以看到,在同一个文档中,既有文本、代码,也有代码的输出。图像由作者提供。
从哪里开始学习 Python?
有无数的途径可以开始学习 Python。以下是两个完整初学者 Python 指南的资源,提供不同格式的学习方式:
-
初学者免费的完整课程:
www.youtube.com/watch?v=rfscVS0vtbw -
带集成代码单元的免费完整课程:
www.w3schools.com/python/python_intro.asp -
互动平台:DataCamp
学习如何编程类似于学习许多其他技能,如游泳或骑自行车——你需要不断练习。所以,当你开始使用这些教程或其他教程时,打开 Google Colab,开始实验代码,并进行调整。使用第二部分中介绍的工具来支持你的学习之旅。
第二部分:集成在 Google Colab 中的生成式 AI 工具
自 2022 年 11 月 ChatGPT 3.5 的公开发布以来,支持编程的生成式 AI 工具迅速增长。像 ChatGPT 这样的大型语言模型(LLMs)在帮助我们进行编码方面极其强大。编程依赖于具有清晰语法的“语言”,这使得它成为 LLM 的理想领域。
Google Colab 最近集成了一套生成式 AI 工具,可以支持你工作的各个方面,从代码建议到调试和解释。现在,让我们介绍所有这些工具:
-
代码补全
-
调试
-
建议
-
自动图表建议
-
帮助
代码补全
当你在 Google Colab 中开始输入代码时,你会很快注意到,代码建议会以灰色和斜体显示(参见下方视频),并且超出你所输入的部分。
建议会很快出现,并随着你继续输入而自适应。你只需按下 Tab 键接受建议。
请注意,建议不仅基于你正在输入的内容,还基于文件中的其余部分,使得这个功能非常强大,远远超越了传统的简单代码补全工具。例如,在下方视频中,导入文件的建议并非通用的——它是我当前 Google Colab 文档中需要的确切代码,并且具有正确的格式。

这个视频展示了 Google Colab 中的代码补全工具。你可以看到我输入某行代码的开头,代码以白色显示,然后建议会非常快速且自动地以灰色和斜体显示。我只需点击 Tab 键即可接受建议。视频由作者提供。
调试
如果你曾尝试过编程,你就知道调试通常是我们花费最多时间的部分。过去,如果你不理解错误,你会参考手册,复制并粘贴错误消息,然后在线搜索(例如 Stack Overflow)解决方案。最近,你甚至可能会向 ChatGPT 或其他大型语言模型寻求帮助。
但现在有一个非常快速的集成解决方案。正如你在下方视频中看到的,我运行了一些代码并生成了错误。在每个错误消息后,你会看到一个标有“解释错误”的按钮。点击后,右侧会打开一个面板,Gemini(一个大型语言模型)会解释错误并提出调整后的代码。你可以手动调整代码,复制粘贴建议,或者一键创建一个新的单元格,插入笔记本中修正后的代码。

这是一个简单的调试情况示例,展示了 Google Colab 中集成的生成性 AI 系统。图片由作者提供。
建议
除了代码补全,Google Colab 还提供了两种基于描述建议代码的简单方法。
第一种方法是通过写注释(参见下方视频)。我只需写一个注释,解释下一行代码,然后 Colab 会直接解释它并自动建议相应的代码。这个功能主要适用于简单的、通常是单行的代码。

这个视频展示了 Google Colab 如何使用生成性 AI 自动生成你刚写的注释对应的代码。视频由作者提供。
当您需要针对更复杂的请求(通常需要几行代码)提供代码建议时,您可以在开始新的代码块时点击“生成”按钮(见下方视频)。然后,您可以用自然语言解释您想做的事情,代码将自动生成。请注意,提示会作为评论添加在顶部,所以请尽量清楚地表述需求,以节省时间。

该视频展示了如何在 Google Colab 中使用自然语言生成代码。视频由作者提供。
自动图表建议
当您使用数据框时,也会有专门的图表创建建议(见下方视频)。当您描述或显示数据框的部分内容时,右上角会出现一个带有图表图标的按钮。点击它,您将看到一个潜在图表的图库。点击其中一个图表后,会生成一个新的代码单元,其中包含创建所选图表所需的代码。
到目前为止,我对这个功能并没有留下深刻印象。它崩溃过几次,返回错误,或者建议了许多选项,但我感兴趣的选项并没有出现。

该视频展示了 Google Copilot 中的自动图表建议功能。视频由作者提供。
帮助
最后,您可以直接与 Gemini(一个聊天机器人/大语言模型)进行对话,询问与代码相关的问题。这些问题可以是您不理解的某段代码,如何用代码执行特定任务,或者几乎任何其他问题。您基本上可以随时获得一个 AI 导师,只需轻轻一按。

在 Google Colab 中,您可以点击右上角的“Gemini”按钮,直接开始与代码相关的讨论。您可以询问当前代码的问题,或询问如何做某些事情等。图片由作者提供。
讨论
虽然生成式 AI 对编程非常有用且强大,但在学习过程中应该适度使用。过度依赖这种效率可能会妨碍我们真正掌握知识,并可能对我们的长期表现产生负面影响。
这些生成式 AI 集成功能让我印象深刻。我发现自己越来越少写代码——现在更多的是能够阅读和测试代码。但阅读总是比写作容易,就像学习任何语言(不仅仅是编程)一样。
然而,这引发了一个问题,对于那些还没有完全学会如何编程的人,长期使用这些工具会有什么影响。我记得曾经广泛使用这些工具来选择 Pandas 数据框的部分内容,因为我经常把括号、.loc 或 .iloc 函数以及语法弄混。ChatGPT 曾经帮我提高效率几次,但从长远来看,我变得更加低效。如果每次都得询问,它往往比我记住解决方案更耗时。那如果工具无法使用怎么办?
此外,记住负责任地使用 AI 建议非常重要。始终致力于理解你所融合的代码,以避免可能的剽窃或无意的错误。请注意,当在 Google Colab 中使用建议时,你可能会看到代码灵感的来源(见下图)。这些信息有助于你避免潜在的版权侵犯问题。

在 Google Colab 中使用生成式 AI 代码推荐时添加的注释示例。看起来代码的潜在来源已被引用,并且提供了可点击的链接,以检查是否存在潜在的版权问题。图片来源:作者。
人工智能的经济学、因果工具、ChatGPT 的影响和其他节日阅读
·发表于 Towards Data Science ·以 Newsletter 形式发送 ·阅读时间 4 分钟 ·2024 年 12 月 12 日
--
想在 2024 年结束前写下你的第一篇 TDS 文章吗?我们始终欢迎新作者的投稿。
我们的指导原则是,学习新事物永远不会是坏事,但我们也知道,不同的时刻需要不同类型的学习。在 TDS,我们通常在新的一年开始时发布大量实践性、动手操作的指南和教程——我们相信 2025 年 1 月也将如此。
目前,在我们进入节日季节的高峰期时,我们想要突出一些最近的最佳文章,它们需要更多的反思和更慢的思考节奏:这些故事适合你在舒适的扶手椅上品味,而不是一边在笔记本电脑上敲代码时阅读(当然,你也可以一边敲代码,一边阅读,我们不会介意!)。
从人工智能生成内容的文化影响到对狗狗排便习惯的贝叶斯分析(没错,你没看错),我们希望你喜欢这系列引人深思、引人入胜的文章。敬请期待:我们迫不及待地想在下周的年终特别版 Variable 中与大家分享 2024 年的亮点。
-
人工智能的经济学——自动化对工人的影响是什么?
在对 AI 对劳动力市场影响的全面分析中,Isaac Tham提出了一个强有力的框架:“AI 根据其在特定任务中与劳动者的表现关系来增强或自动化劳动。如果 AI 在某个任务中表现优于劳动者,那么劳动被自动化;如果劳动优于 AI,那么 AI 增强劳动。”他继续展开讨论 AI 快速扩展的影响、风险以及潜在的好处。
-
AI 生成内容的文化影响:第一部分
商业影响是关于 AI 对话中占据大部分空间的话题,但正如Stephanie Kirmer强调的那样,我们不应忽视 AI 生成内容在文化领域可能带来的深远变化:“如果我们期待在这些经历之后我们的思维方式不发生变化,那就太傻了。我非常担心,我们正在经历的变化并非向好的方向发展。”
-
ChatGPT:两年后的反思
2022 年 11 月,当 OpenAI 推出那款改变一切的聊天机器人(至少……改变了很多的事情)时,感觉既像是两天前发生的事,也像是两十年前的事。为了帮助我们理解后 ChatGPT 时代的世界,Julián Peller提供了对过去两年——“生成式 AI 革命”中的这一历史性过渡期——的全景概述。
-
打破 ChatGPT 的名字:David Mayer 是谁?
对于那些喜欢将 AI 内部工作机制的探索与丰富的悬疑和神秘感相结合的人,Cassie Kozyrkov的最新文章正合适:它通过一个神秘的名字,探讨了围绕基于 LLM 的工具的一些棘手问题(隐私、偏见、提示破解等)。
-
克服保护共享生成式 AI 环境中的安全挑战
从不同的角度探讨 AI 产品安全问题,Han HELOIR 博士聚焦于多租户的特殊挑战——这是一个越来越常见的情况,当不同的用户群体(例如公司内的多个团队)依赖相同的数据和 LLM 资源时。

图片来源:Crystal Kay来自Unsplash
-
理解 DDPG:解决连续动作控制挑战的算法
为什么不在这个假期拓宽一下你对深度强化学习算法的理解呢?Sirine Bhouri的首篇 TDS 文章带我们走进深度确定性策略梯度(DDPG)算法背后的理论与架构,测试其性能,并探讨其在生物工程中的潜在应用。
-
LLM 路由——直观且详尽的解释在成千上万的大型语言模型中,实践者应如何选择适合特定任务的模型?Daniel Warfield对 LLM 路由的深入浅出探讨解释了这一“先进推理技术”如何简化这一过程,以及它所依赖的不同组件如何相互补充。
-
协和指数的直觉——生存分析
理解和预防流失仍然是行业内数据科学家最常见的目标之一。Antonieta Mastrogiuseppe提供了生存分析基础数学的全面入门,并阐述了协和指数在评估模型准确性方面所扮演的关键角色。
-
狗屎指南针
一只 5 岁的骑士查尔斯王猎犬能教会我们关于贝叶斯统计的重要课题吗?事实证明答案是肯定的——只要你跟随Dima Sergeev的生动叙述,了解他尝试发现他狗狗“如厕”仪式中的模式。
-
因果关系——数据科学的心理卫生为了丰富我们本周的内容,我们邀请你深入探讨Eyal Kazin关于因果工具的深思熟虑的反思——以及何时(和是否)使用它们。基于他最近在 PyData Global 大会上的讲座,本文平衡了因果推断的宏观分析与塑造我们在日常工作流中应用因果思维的细节因素。
感谢您支持我们作者的工作!正如我们之前提到的,我们非常喜欢发布新作者的文章,因此如果您最近写了一篇有趣的项目演示、教程或关于我们核心主题的理论反思,请不要犹豫,与我们分享。
直到下一个变量,
TDS 团队
人工智能的经济学 — 自动化对工人意味着什么?
尽管 AI 取得了巨大进展,AI 的经济影响仍然没有得到充分理解,AI 从业者和经济学家的见解仍然不满意。
·发布于 Towards Data Science ·31 分钟阅读·2024 年 11 月 25 日
--

来源:使用 Grok/X 生成的图像
目录
∘ 经济模型简介
∘ AI 进展的影响
∘ 哪些工人将被自动化或增强?
∘ 我的框架:AI 相对于人类的表现
∘ 衡量 AI 相对于人类的表现
∘ 高技能工人 vs 低技能工人 — 谁能从 AI 中受益?
∘ 更多关于生产力效应的信息
∘ AI 作为一种通用技术
∘ 那么,最好的工作是什么?
∘ AI 自动化真有那么糟糕吗?
∘ 结论
∘ 脚注和参考文献
执行摘要(2 分钟阅读)
AI 进展的速度前所未有,在模型能力和成本效率方面都有显著提升。然而,AI 的经济影响仍然没有得到充分理解,AI 从业者、经济学家和智库的见解都不尽如人意。经济学家往往低估了 AI 的潜在影响,因为他们对前沿发展了解有限。
Acemoglu 和 Restrepo(2018)的基于任务的框架在经济学文献中被广泛用于分析自动化的影响。
自动化:AI 在高度可替代的任务中取代劳动力,减少这些领域的就业(例如,收银员被自助结账取代)。
互补性:AI 可以在仍然需要人类专业知识的任务中增强劳动力(例如,经济学家解释由先进软件生成的数据)。
生产力效应:AI 降低成本可能会增加非自动化任务的需求,从而可能总体上提高就业。
恢复效应:当 AI 自动化现有任务时,可能会出现新任务,创造出需要独特人类技能的角色。
我介绍我的框架:AI 根据其在特定任务中相对于工人的表现来增强或自动化劳动力。如果 AI 比劳动力更好,劳动力就会被自动化;但如果劳动力比 AI 更好,AI 则会增强劳动力。这些信息是 readily available —— AI 模型在各类任务中与人类表现进行基准测试,提供及时的相对能力见解。这些基准可以映射到劳动力技能分布(例如,使用 OECD PIAAC 数据)上,以评估哪些工人最有可能面临自动化,或哪些工人可能会被增强。
AI 是否会使高技能还是低技能工人受益仍然不确定。关于客户支持代理、顾问和软件开发人员的早期实证证据表明,低技能工人受益更多。从经济角度来看,这是由于自动化任务与其他非自动化、低效任务之间的强互补性,导致了性能天花板限制了生产力的提升。
然而,我个人认为高技能工人受益更多,因为:i) 在一个任务中,他们比起被自动化,更可能被增强,ii) 正如 Toner-Rodgers(2024)所展示,AI 可以在创新过程中与人类专业知识互为补充,iii) 经验与任务的 AI 互补性之间存在正相关关系,因为随着工人晋升,他们开始承担管理角色,iv) 自动化任务的商品化可以降低任务价格,即使由于 AI 技能差距缩小,v) 低技能工作由于 AI 的介入,面临工作机会减少,剥夺了他们在工作中获得技能的机会,形成恶性循环。
对价格弹性需求的产品(例如,半导体、消费电子产品)来说,当 AI 降低成本时,会看到显著的需求增加,增加了互补任务中的就业。这种情况可能发生在:i) 成本下降解锁了新市场,ii) 存在网络效应,iii) 这些产品推动了创新。另一方面,价格缺乏弹性的产品(例如,税务软件、快餐),由于 i) 市场饱和以及 ii) 新应用的潜力较低,会导致工作岗位的流失,因为需求未因成本下降而增加。
AI 是通用技术,具有重塑经济结构的潜力,类似于电力和蒸汽机。在当前的初期阶段,企业将 AI 用于降低成本,将 AI 的影响限制在取代劳动力上。长期的整合可能会导致新系统和新产品的出现,提供显著的生产力提升。
最好的工作是那些包含不可自动化任务和人工智能迅速发展的可自动化任务的工作,尤其是在生产力驱动的需求增长潜力较大的领域(例如,科技产品经理)。工人应该寻找提供资本所有权(例如,科技公司股权)的职位,以便从人工智能驱动的生产力增长中受益。
自动化活动可以追求 娱乐,并且仍然有价值,类似于跑步、音乐和艺术,由于没有利润动机,这可能更加愉悦。然而,确保人工智能的利益能够公平分配,对于确保人工智能为所有人带来广泛的好处至关重要。
完整文章
引言
生成式人工智能已经迅速席卷社会,像 ChatGPT、Claude 和 Midjourney 这样的革命性工具,以前所未有的速度吸引了数百万用户。各种软件应用程序,从我个人使用的睡眠追踪应用 Sleep Cycle,到 Slack 和 Teams 等办公生产力工具,都在争先恐后地集成人工智能功能。
人工智能背后的技术发展速度惊人。领先模型的智能正在以惊人的速度进化——GPT-2(2019)还在艰难地构建连贯的句子。仅仅 4 年后,GPT-4 在从竞赛数学到 AP 考试的任务上,已经超越了大多数高中生的能力¹。更重要的是,运行人工智能模型的成本正在以数量级的速度下降——OpenAI 在 2024 年 7 月发布的 GPT-4o mini,在成本是原版 GPT-4 的 1/200 的情况下,达到了与 2023 年 3 月发布的 GPT-4 相媲美的性能²。并且,这一进展没有停止的迹象³。
因此,越来越多的人认识到,人工智能将以深刻、前所未有的方式从根本上重塑社会和经济。
但是,人工智能将对经济产生什么影响呢?不幸的是,这是一个重要问题,在我看来,至今尚未以令人满意的方式得到回答。
当前,人工智能社区的关注点是设计新架构和开发前沿产品。人工智能从业者和开发者专注于提升模型性能,只有在涉及潜在用户和市场时,才会考虑经济因素。
另一方面,经济学家们则在自动化、替代性和互补性方面发展严谨的模型和理论。然而,由于他们往往在人工智能领域之外工作,未能与最新的人工智能进展和组织如何采纳这些技术保持同步。这种脱节可能导致对人工智能潜力的根本误解,从而得出悲观的评估:2024 年诺贝尔奖得主达伦·阿塞莫格鲁最近估计,人工智能将在未来十年内累计提高生产力仅为 0.7%⁴。
与此同时,智库和咨询公司无疑遭遇了最糟糕的两面性。他们发布引人注目的报告,提出诸如“发达经济体的 60%的工作可能会受到 AI 的影响”⁵或“AI 将为经济贡献 15 万亿美元”⁶等大胆的主张。然而,这些报告很少明确说明“受到影响的工作”或“为经济做出贡献”这类术语的具体含义,也没有与最新的 AI 发布及其影响保持同步。
我相信我位于经济学与 AI 的交汇点,提供了独特的视角。作为一名专注于生产力、创新和宏观建模的研究经济学家——同时也是一名 AI 开发者和爱好者,创建了多个 AI 工具,并始终跟进最新的行业趋势,我看到有必要深入理解 AI 的经济学影响。最近任命 Dr. Ronnie Chatterjee 为OpenAI 首席经济学家⁷,彰显了 AI 行业对经济学在塑造其发展轨迹中的关键作用日益认同。
这是探索 AI 经济影响的系列文章中的第一篇。本文将通过 David Autor 和 Daron Acemoglu 提出的广泛使用的经济框架,探讨 AI 对就业的影响,并介绍一个新的扩展,结合了 AI 领域的最新发现。
未来的文章将探讨 AI 的影响:1)AI 输入的生产(如芯片和能源);2)创新和研发;3)宏观经济结果,如生产力增长。这些探索旨在从经济学家的视角提供关于 AI 的全面而细致的观点。
经济模型介绍
为了将我们的讨论基于经济框架,我将解释Acemoglu & Restrepo(2018)⁸提出的任务基础框架,该框架此后在经济学文献中得到了普及。读完这篇文章后,你可以认为自己已经成为一位新兴经济学家,因为你已经接触过一篇严谨且具有开创性的经济学论文!

来源:Acemoglu & Autor(2022)
经济体由生产产出的企业组成。一个企业的产出(ye)是通过在生产过程中组合各种任务(x)来完成的,每个任务对最终产出的贡献有不同的重要性(a(x))。
转向右侧的特定任务生产函数,我们可以看到一个任务可以使用以下生产要素进行生产:人工劳动(le)、AI(ae)或两者的组合。
工人从事不同的职业,每个职业都涉及到一个或多个生产过程中的任务。
劳动力和 AI 各自有一个表示要素特定生产力的术语。对于劳动力来说,这指的是人力资本——例如,一个经验丰富的经济学家比初级经济学家写得更好、更快。对于 AI 来说,这涉及技术变革——例如,一台更强大的计算机能以两倍于前一代的速度进行模拟。
sigma 值决定了劳动力和 AI 之间的替代程度。sigma 的值越高,劳动力和 AI 在任务中的可替代性越高。
· 如果 sigma 为无穷大,则劳动力和 AI 在任务中是完全可替代的。例如,在超市中,人工收银员和自助结账机在处理简单客户购买的结账任务中是可互换的。
· 在其他情况下,劳动力和 AI 是互补的,或者都需要完成任务。例如,要完成一个计量经济学研究,经济学家必须使用计算机软件进行回归分析和数据分析。然而,计算机无法自行完成研究,因为经济学家需要收集数据、解释回归结果并写论文呈现研究结果。
AI 进步的影响
现在,假设发布了一项新的 AI 创新。例如,OpenAI 发布了Sora¹⁰,一款可以在几分钟内制作真实视频的 AI 视频生成工具。让我们分析这一创新对一家帮助企业制作营销视频的公司的影响。该公司的生产过程涉及两个任务:创建和编辑视频(任务 A)以及与客户的客户服务(任务 B)。
一项 AI 创新提高了 AI 在生成视频的任务 A 中的生产力,增加了 AI 的边际产出。对就业的影响是什么?正如我之前提到的,这取决于劳动力和 AI 在此任务中的可替代性,或 sigma 的值。
如果劳动力和 AI 高度可替代,就业会减少。 在这种情况下,由于对于 AI 来说,生产一个给定的视频相比于劳动力变得相对便宜,企业将在该任务的生产中用 AI 替代劳动力。因此,劳动力在任务 A 生产中的份额减少,AI 的份额增加。一般来说,这意味着更多任务将实现完全自动化(即,完全使用 AI 作为输入)。在保持生产结构(即每个任务在最终产出中的份额)不变的情况下,所需劳动力的数量会减少(例如,超市中的收银员被自助结账机取代)。
那么,这对工人来说真的是末日吗?还不至于。其实有几个潜在机制可能会导致就业增加。
在同一任务中,劳动力与人工智能之间可能存在强烈的互补性。以经济学家为例,也许计算机软件变得更加高效,以相同的成本产生 10 倍数量的经济模拟。这意味着需要更多的经济学家来解释和发布增加的结果¹¹。其他具有强烈互补性的职位包括知识工作者,如顾问、医生和律师。
此外,人工智能的边际产出增加将降低生产成本。这使得公司能够生产更多的产出,也被称为生产力效应¹²。因此,即使某个任务已经被自动化,生产力效应仍然会导致非自动化任务的招聘增加。在产出大幅增加的情况下,由于需求的高弹性(我将在后续部分详细说明),整体就业确实有可能增加。

来源:Autor(2024)
最后,还有恢复效应,即创造人类专门从事的新任务。以视频生成的例子为例,也许会创建任务 C:以前的视频编辑将转变为创意顾问,为客户提供品牌创意方向的建议。Autor(2024)¹³通过自然语言处理分析了数十年的职位名称,并发现 2018 年 60%的职位在 1940 年并不存在。自 1940 年以来,大部分新职位已经从中产阶级的生产和文职工作转向了高薪的专业职位和低薪的服务职位。
哪些工人将会被自动化或增强?
从上面的模型中,我们可以看到,人工智能对劳动力的影响将取决于劳动力是否是可自动化的,即专注于人工智能已自动化的任务(例如任务 A),或是不可自动化的,即专注于非人工智能自动化的任务(例如任务 B)。可自动化的劳动力最终会因为人工智能的进步而被取代,导致工资降低和失业。然而,不可自动化的劳动力将被保留下来,并可能看到其生产力和工资的增加。
因此,现在需要回答的关键问题是如何识别哪些劳动力是可以自动化的,哪些劳动力是无法自动化的。

自动化与增强专利在职业层面的曝光关系,1980–2018 年平均值。来源:Autor(2024)
值得在这里暂停一下,强调文献中另一种观点,特别是来自 Autor(2024 年)的观点,该观点将技术而非劳动分类为增强劳动或自动化劳动。Autor 利用专利文本将创新进行分类:如果专利内容与职业产出一致,则该专利被视为增强创新;如果专利内容与工人在特定职业中执行的任务相似,则该专利被视为自动化创新。
尽管后续的研究论文采用了基于 Autor 框架的方法,但我认为这个方法存在一些问题。
首先,预测创新发布时的影响是固有的不确定的。2024 年 2 月 OpenAI Sora 发布的当天,我正在收听一档领先的 AI 播客《AI 每日简报》,描述 Sora 是一次多么重大的突破¹⁴。 然而,主持人 Nathaniel Whittemore 承认,他完全不知道 Sora 是否会取代或增强视频创作者的工作,而我们只能“拭目以待”。
其次,将技术归类为增强或自动化假设所有工人都产生统一的影响,这简化了异质性工人的现实情况。工人之间的技能、经验和生产力水平各不相同。因此,更有可能的是,某些技术会增强某些类型的劳动,而自动化其他类型的劳动。
我的框架:人工智能与人类的表现对比
大多数经济学文献假设劳动是同质的。有些尝试通过假设劳动分为高技能和低技能两类来考虑劳动的异质性,但这种做法仍然是过于简化的。劳动的同质性是解决工人均衡工资并“解决”理论模型所必需的假设。
然而,这与现实中的劳动力市场存在矛盾,因为不同工人之间的生产力和技能水平差异巨大。在同一任务中,不同工人的生产力存在差异(例如,有些人比其他人更快地编辑视频)。此外,工人拥有独特的 多任务技能组合(例如,有些工人不仅能编辑视频,还能将视频编辑服务推销给客户,而其他人则只能编辑视频)。
这让我想起了 FIFA 足球游戏中为球员分配的统计数据(射门、站位、射门技巧、点球等)。这些都对整体评分(即生产力)以及即使在同一职业中工人之间的工资差距产生了广泛的影响。
这也突显了经济学家的一个常见批评:他们倾向于根据分析可操作性并能提供“清晰”结论的模型构建,而不是依据建模假设的现实性。因此,他们的结果在严格条件下是优雅且理论严谨的,但往往脱离现实,提供的对理解现实问题的帮助有限。
正是在这个时候,我介绍了我的框架,将劳动划分为增强型或自动化型,认识到工人的异质性,同时适合于基于任务的经济框架。
我的框架背后的核心原则很简单:劳动是被增强还是自动化取决于 AI 在特定任务中与工人的相对表现。如果劳动在某项任务中的表现比 AI 差,则 AI 技术自动化该任务中的劳动;而如果劳动在某项任务中的表现优于 AI,则 AI 增强该任务中的劳动。
例如,如果 OpenAI 的 Sora 模型能在视频编辑者的生产力中达到第 75 百分位(生产力大致定义为相对于时间和金钱的投入的质量),那么它将取代 任何低于第 75 百分位的视频编辑者(假设 AI 的边际成本低于雇佣第 75 百分位视频编辑者的成本)。然而,对于第 90 百分位的视频编辑者,Sora 成为了增强工具。该编辑者可以使用 Sora 迅速生成一个质量相当于第 75 百分位视频编辑者的初稿,然后利用他们的更高技能将初稿精细化为更高质量的最终产品。
测量 AI 相对于人类的表现
这种方法的优雅之处在于,它依赖于关于 AI 在广泛任务中相对于人类的表现的现成、最新的数据。
这是因为 AI 模型的创造者通过将模型的性能与人工策划的基准进行比较,来评估其在多个不同任务中的表现。一些基准的例子包括 MATH(一个高中竞赛数学题目集)、GPQA(由生物学、物理学和化学领域的专家撰写的博士级问题),以及 SWE-bench(一个来自 GitHub 的真实世界软件问题集合)。
这种做法确保了每一个新的 AI 模型或产品发布都伴随有公开分享的性能指标,从而提供对 AI 能力的及时而详细的理解。
相比之下,传统的经济指标(如专利数据或工资和就业统计)用于跟踪技术的进展和影响,本质上是滞后的。专利数据常常忽略关键创新,因为许多 AI 公司并未为其新产品申请专利。工资和就业数据虽然有用,但通常存在显著的时间滞后,并且本质上是事后数据,限制了它们预测前沿 AI 对劳动力市场未来影响的能力。
从上面推文中的图表¹⁵来看,我们可以看到 AI 的进展是多么迅速。它已经在 2010 年代超越了人类在狭窄任务上的表现,例如图像识别,这得益于深度学习的突破。在自然语言处理(NLP)领域,变换器(2017 年推出)彻底革新了该领域,从 BERT 等模型扩展到连续版本的 GPT。目前,前沿 AI 模型在更复杂的任务中迅速进步,例如代码生成、高级数学以及推理与逻辑。当前的趋势表明,AI 将在未来几年内在这些领域与人类专家竞争,甚至超过他们。
此外,AI 模型的表现通常会在标准化考试(如 AP、SAT、GRE,甚至从 AIME 到 IMO 的竞争性数学考试)上进行基准测试¹⁶。由于标准化考试提供了学生成绩的时间序列和横截面分布的充分文献记录,这些数据可以用来近似劳动市场中各类工人的技能分布。
通过将 AI 的性能数据与职业任务描述进行关联,并与每个职业工人的技能分布进行比较,我们可以构建一个衡量 AI 在每个职业中相对于人类的表现指标,进而了解每个职业工人被替代或增强的潜力。我相信这是可行的——经济合作与发展组织(OECD)的 PIAAC 是国际上最具可比性的成人技能数据库,我自己曾在一项关于成人技能和老龄化的经济学项目中使用过它。OECD 还衡量了 AI 解决 PIAAC 的读写和算数测试的能力¹⁷。
因此,如果 AI 的表现等同于某一职业中第 75 百分位的工人,那么这个指标可以解释为AI 可能会取代该职业底部 75%的工人,并增强该职业顶端 25%的工人。这为分布式、同一职业内的影响提供了关于 AI 影响的异质性见解。
高技能与低技能工人——谁将从 AI 中受益?
我的框架可以为当前关于 AI 是否更有利于高技能或低技能工人的争论提供见解。这个问题对不平等现象具有重要意义——它是一个影响社会凝聚力和对经济系统满意度的重要议题。
虽然思想领袖和早期实证证据仍然存在分歧,但我希望通过运用我的框架进行更深入的分析,能够帮助调和一些明显的矛盾。
一方面,一些早期的实证证据表明,低技能工人受益更多。
· Brynjolfsson 等人 (2023)¹⁸:在最早的研究之一中,调查了生成性 AI 对工作影响的实验,作者发现使用 AI 的客户支持人员平均生产力提高了 14%。关键是,经验较少或技能较低的工作人员看到的生产力提升最大,达到了 35%,而经验最丰富的员工则几乎没有提升。

根据 Dell’Acqua 等人(2023)的研究,使用 ChatGPT 对低技能顾问的帮助大于对高技能顾问的帮助。图片由作者提供。
· Dell’Acqua 等人 (2023)¹⁹ ²⁰: 一项与波士顿咨询集团(BCG)顾问的现场实验揭示了相似的模式。对于表现较差的顾问,提供 GPT-4 访问权限后,他们的生产力提升了 43%,而表现较好的顾问仅提升了 17%。
· Hoffman 等人 (2024)²¹: 研究了使用 GitHub Copilot 的 187,000 名开发者,作者发现 Copilot 使软件开发人员能够将任务分配转向他们的核心编码活动,远离非核心的项目管理任务,而低能力²²的程序员受益最大。
初看起来,这些发现可能与我的框架相矛盾,因为我的框架认为表现较差的员工会被取代并处于更差的境地。让我再次使用我的框架,并以一个视频制作公司的例子来解释。
在这种情况下,视频编辑的工作由两个互补任务组成:任务 A(视频编辑)和任务 B(客户服务)。即使任务 A 已经被自动化,任务 B 却无法自动化,因为它需要与客户进行人际谈判和讨论。如果任务 B 占用了大量时间,那么员工的整体生产力将受到任务 B 中低效环节的制约。例如:
· 在任务 A 的第 5 百分位的员工可以利用 AI 达到第 75 百分位的生产力水平,从而显著提高他们的整体产出。
· 相反,处于第 75 百分位的员工可能几乎看不到 AI 带来的改善,因为他们的瓶颈存在于任务 B,而任务 B 并未产生任何增益。
从经济学术语来看,自动化的任务 A 和低效的任务 B 之间存在强互补性。任务 B 的低效性有效地限制了整体生产力的提升,形成了迈克尔·韦布描述的²³绩效天花板:在这一限制之外,进一步提高任务 A 的效率会导致边际效益递减。因此,AI 帮助低技能员工缩小与高技能员工的差距,两者最终趋向于绩效天花板。
然而,随着公司和 AI 技术的演变,这一动态可能会发生变化。也许公司将进行任务专门化,将任务 A 和任务 B 拆分,并为每个任务雇佣不同的工人。因此,任务 A 表现差的工人可能会被替代,因为他们不再需要完成任务 B。或者,随着AI 进步,任务 B 也可能被自动化(例如,OpenAI 的实时改进可以自动处理所有简单的客户服务电话)。也许到那时,你会看到顶级客户助手(例如那些提供个性化咨询/辅导或情感指导的人员)将得到增强,而所有低质量的助手将会被自动化。
另一方面,有些人认为高技能个体将从 AI 增强中受益更多。
首先,我的框架明显暗示了,高技能工人在特定任务中更可能是增强而非自动化的对象。正如迈克尔·韦伯在他的2023 年《80,000 小时播客》采访中所提到的,顶尖的软件工程负责人现在可以借助 AI 协助设计和实现 100 个应用程序,这一任务过去需要雇佣大量的初级软件工程师。这展示了 AI 如何放大高技能工人的生产力,而不是取代他们。

来源:Toner-Rodgers, A. (2024). 《人工智能、科学发现与产品创新》。
另一项由Toner-Rodgers(2024)²⁴进行的最新研究,因其对 AI 和科学创新的积极发现而引起了关注,研究发现,当研究人员获得 AI 辅助的材料发现工具时,顶级研究人员的产出翻倍,而底部三分之一的科学家几乎没有获得任何好处。作者将这种差异归因于AI 与人类专业知识在创新过程中的互补性。顶级科学家利用他们的领域知识来优先选择有前景的 AI 建议,而其他人则浪费了大量资源测试虚假的正向结果。
此外,随着个体在工作中积累经验和技能,他们往往会承担涉及领导和管理的角色——这些领域 AI 相对较弱。这些角色需要战略思维、情商和人际交往能力,这些都是 AI 所不能替代的。经验与 AI 互补性之间的正相关表明,高技能、经验丰富的工人更有可能在 AI 增强的劳动市场中蓬勃发展。
Acemoglu (2024)²⁵ 提出了另一种可能导致低技能工人失业的渠道。即使 AI 在某些任务中能够提高低技能工人的生产力(让我再次提到视频编辑的任务 A),高技能工人可能会被重新分配到其他任务,而任务 A 的商品化(由于 AI 进步导致任务 A 供应量增加)可能会导致任务 A 的价格下降(即价格下跌),从而导致专注于任务 A 的工人(低技能工人)的工资停滞不前。
对低技能工人而言,这一动态效应更加令人担忧。随着 AI 在他们专长的任务中超过他们的能力,这些人的工作机会可能会大幅减少。这导致最有价值的技能往往是在工作中培养的,但没有初级岗位,低技能工人可能几乎无法获得他们需要的技能,以保持经济上的竞争力。
我的干兄弟,一个狂热的电影评论家,向我强调了这一点。我们讨论了 2023 年好莱坞演员罢工,演员们反对电影公司使用 AI 配音替代小角色,等等其他不满。他指出,许多著名的电影导演通过多年的低级任务积累经验。比如克里斯托弗·诺兰,他早期曾担任剧本阅读员和摄影师[26]。如果当时电影公司用 AI 取代这些机会,他或许永远无法成为今天的他。AI 就像一场海啸——那些在自动化短暂机会窗口期未能“逃到高地”的人,可能会在自动化浪潮来临时遭到无法逆转的毁灭。这一动态有可能加剧熟练工人与非熟练工人之间的两极分化。
这一现象的证据已经在科技行业中显现出来,初级软件开发职位的招聘正在急剧下降。
虽然关于这一争议的双方都有令人信服的证据,但我个人认为 AI 最终会扩大,而非缩小工人之间的差距。这凸显了应对 AI 带来的社会经济挑战的紧迫性。
更多关于生产力效应的内容
让我们更深入探讨我之前提到的生产力效应,它是许多人对 AI 对工作产生积极影响的乐观看法的基础。理解这一点有助于揭示哪些职业最有可能在未来抵御 AI 的影响,甚至从 AI 的进步中受益(我将在最后一节中讲解哪些职业较为适应!)
这里的关键见解是,自动化驱动的成本降低和生产力提升,可以导致最终产出的需求大幅增加,进而带来非自动化任务的就业增加,这种增长可能超过因自动化第一个任务而导致的就业下降。
我们如何确定哪些类型的产品可能会出现这种效应?

需求价格弹性基础知识。图片由作者提供。
这是我引入微观经济学基础概念——需求的价格弹性的时刻。为了帮助你回忆,若某产品的价格下降导致需求量呈比例更多的增长,从而最终带来总产值需求的增加,那么该产品具有价格弹性需求。
简单来说,对于价格弹性较大的产品,消费者实际上会对这些产品有更大的需求,但受限于当前的价格水平。
其中一个原因是当成本下降时,有可能解锁新市场——如果现有产品的市场渗透率较低。

尽管二战后自动取款机的普及,银行出纳员的总就业人数在 1970 年代显著增加,体现了运行银行分行成本下降所解锁的新市场。图片由作者提供。
一个自动化支持者常常引用的例子是自动取款机和银行出纳员²⁸。在第二次世界大战后,银行服务的需求激增,人工出纳员在日常任务中起着至关重要的作用,比如兑现支票和存款。当自动取款机在 1990 年代变得普及时,它们自动化了许多日常任务,显著降低了运营银行分行的成本。因此,银行能够在全国范围内开设更多分行,服务更广泛的客户群体。结果,出纳员的就业增加,他们的角色也从简单的手工任务转变为关注客户服务、销售和专业客户需求。
其他使产品更加负担得起并使其变得更易获取的例子包括 20 世纪的汽车和电视,而现在,或许是新的技术产品,如无人机、增强现实家庭影院等,随着质量的不断提升和成本的持续降低,正变得越来越容易为普通消费者所接受。
此外,网络效应可以放大成本降低的效果,因为随着更多人使用产品,产品的价值也会增加。例如,像 Slack、Google Docs 和 Zoom 这样的平台,通过降低远程协作的复杂性,从而降低了成本,推动了这些平台的普及。随着用户数量的增加,这些平台的效用也会不断提升,形成一个正向循环,促进了更广泛的使用和更高的价值。
也许这也是为什么 TikTok 非常有兴趣开发 AI 工具来简化视频制作。它最近推出了 Symphony²⁹,一套新的 AI 驱动的创意解决方案。通过减少制作 TikTok 视频所需的时间和精力,这将大大增加用户制作和分享视频的数量,进一步增强平台的病毒传播性和用户参与度。
第三,促进创新的产品,或者推动进一步产品创作的产品,也会表现出价格弹性需求。最好的例子是半导体。最初由于成本高昂,半导体仅被用于军事应用,但随着其成本的指数级下降和性能的提升,半导体促成了一连串创新——从个人电脑到智能设备(如冰箱和电视)。今天,这一事实比以往任何时候都更加真实,(正如我们将在下篇文章中深入讨论的那样),半导体被大科技公司广泛需求,推动了先进 AI 模型的开发和部署。尽管半导体的性能每两年翻一番(摩尔定律),半导体的需求仍在飙升,GPU 的生产预计到 2030 年每年翻倍³⁰。
另一方面,一些产品表现出价格无弹性需求,意味着即使成本大幅下降,需求也不会增加。这些产品的特点是市场饱和和低潜力创造新应用。
一个例子是税务申报软件。如果税务申报软件的价格下降 90%,消费者和企业不会突然申报 10 倍更多的税款。在这种情况下,税务申报过程的自动化可能会导致就业减少,因为需求不会增加。
另一个例子是快餐,它在西方世界已经达到了市场饱和。人们的食量有限,而快餐的可负担性通常并不是限制因素。即使快餐变得便宜 10 倍,由于快餐店 90%的服务人员实现了自动化,我认为快餐的需求也不会增加到足以阻止服务人员被取代的程度。(尽管美国人对快餐的渴望可能会让我大吃一惊!)
AI 作为一种通用技术
今年,关于 AI 实际经济效益的愈发悲观情绪开始显现。尽管 AI 产品的企业采用率不断上升,但企业并没有看到 AI 支持者所承诺的生产力大幅提高。
然而,我认为这是因为我们正处于通用技术的采用初期阶段,组织心态意味着我们现在处于价格无弹性、AI = 降本的世界状态。
许多人认为人工智能是一种通用技术(巧合的是,它也缩写为 GPT),通用技术定义为一种影响整个经济并具有潜力显著改变经济和社会结构的技术。历史上的例子包括蒸汽机(18 世纪末)、电力(19 世纪末)和信息技术(20 世纪末和 21 世纪初)。
阿贾伊·阿格拉瓦尔在他 2022 年关于 AI 颠覆性经济学的书中³²指出,人工智能可能会遵循与之前的通用技术(如 19 世纪末和 20 世纪初的电力)类似的轨迹。

将通用技术的缓慢采用率与技术“到来”后的年份进行对比。来源:Jovanovic & Rosseau(2005)
当时,蒸汽动力推动了工业革命的经济发展,电力的初期应用主要被视为一种替代选择。例如,电动机被用来取代汽车和电梯中的蒸汽机。然而,这些孤立的应用并未显著增加电力使用量或释放电力的变革性潜力。
电力的真正潜力随着时间的推移逐渐显现³³,最终人们意识到它提供了分散的动力——可以独立于中央发电系统运行的小型、便携式能源单元。这一能力使得工厂摆脱了由中央蒸汽轴驱动的刚性布局。像亨利·福特这样的工业家利用这种灵活性,开创了新型生产线设计,彻底改变了制造业,并在 20 世纪初推动了前所未有的效率提升。

人工智能已经在不同的职业、行业和各种任务中得到了快速应用。来源:Bick(2024)
伊桑·莫利克(Ethan Mollick)同意这一评估,认为目前,人工智能主要作为提高效率的替代选择,而非推动生产系统的根本性改革。只要企业将人工智能主要视为节约成本的信息技术,它们就会专注于在现有任务中用 AI 取代人类,而不是重新构想其生产功能。这种做法自然会导致劳动 displacement,而非带来变革性的经济收益。
从长远来看,企业将不再将人工智能视为简单的效率工具,而是将其作为全新生产模型的核心特征进行整合。一些例子可能是自动化供应链,或者人工智能个人助手在知识工作者之间进行协调。这一转变还将催生一种新的 AI 优先产品类别,可能推动巨大的生产力提升,并促使重新构想劳动力在这些系统中的角色,或恢复效应的超级版本。或许,工人现在都会成为“质量控制专家”,检查人工智能生成的输出是否存在错误,或为特定用户需求定制这些输出。
将这一点与我们的框架联系起来,我们知道价格弹性在长期内趋向于增加,原因正是企业能够调整其生产过程。随着人工智能的进步,企业很可能会超越将其主要作为削减成本、替代劳动力的工具,而是利用人工智能来彻底改造生产系统,开发全新产品,并开拓新市场,从而捕获显著更大的需求。这一演变最终可能导致生产力和恢复效应占主导地位,给工人和消费者带来巨大的利益。
那么,最好的工作是什么呢?
让我总结一下目前文章中的见解,并提供一些关于在这个人工智能发展时期选择理想职业的指导。与其他论文不同,我没有列出按得分排名的职业清单推荐给你,因为这需要使用我提出的框架进行更深入的分析和研究。相反,我将概述识别“抗 AI”角色的关键标准。

人工智能职业暴露评分。来源:英国教育部(2023 年)
一个幼稚的建议是认为最少接触人工智能的职业是最好的,参考近期论文中关于人工智能暴露程度的测量³⁶ ³⁷。然而,这是有缺陷的。看看那些接触人工智能最少的领域——护理、初等教育。我还会加上清洁和家务工作。然而,这些工作薪资低,未来在生产力或需求上也不太可能有很大改善,因此很少有经济发展的机会。
与人工智能暴露程度相比,我们还应关注变化的速度。再一次,展示人工智能模型在不同任务上进展速度的图表非常具有信息量。
我对于理想工作的标准是:该工作主要包含不可自动化的任务,但也包含一定比例的可自动化任务,并且人工智能在这些任务上的进展非常迅速。这将支持该职位的生产力增长。此外,该工作必须处于一个创新领域,在该领域内,生产力的提高可能会导致需求的显著增加。
我脑海中的一个例子是技术产品经理(PM)。PM 的核心职责——对产品、行业和用户的理解,以及促进工程师与业务团队之间的沟通和协作——本质上是无法自动化的。然而,PM 的角色也包括一些可以自动化的任务(例如,会议安排、在 Figma 上制作模型、原型设计、制作演示文稿、监控用户活动和开发者进展等),这些任务正是人工智能在快速进步的领域(例如,人工智能代理安排会议、Figma 的文本转设计、文本转 PPT 以及更多基于人工智能的监控仪表盘)。这使得 PM 的生产力显著提高,让他们能够将更多时间专注于核心技能,管理更大的团队,和/或更有效地设计和推出新功能与产品。此外,优秀软件产品可以解决的问题几乎是无穷无尽的——软件的需求几乎是无限的。因此,生产力的提升将使 PM 能够做得更多,而不是让更少的 PM 做同样的工作。这些论点同样适用于科技企业家。
理想情况下,你也应该关注那些让你获得驱动自动化的资本所有权的工作。获得股权(在科技公司中较为常见)或晋升到越来越多使用人工智能的公司的高管职位,将使你能够从资本收入中分得自动化带来的部分收益,而不是仅仅依赖于可能会缩小的工资收入。
通过专注于那些在人类创造力与人工智能驱动的生产力提升之间保持平衡的角色,并寻求在自动化资本中的所有权,我们可以在这个变革时代中,不仅以韧性应对挑战,还能够获得增长和影响的潜力。
那么,人工智能自动化真的那么糟糕吗?
最后,我还想挑战一种观念,即人工智能自动化工作完全是灾难性的。仅仅因为机器可以比人类更好地完成某些任务,并不意味着这些活动或与之相关的技能就没有任何价值。
例如,汽车、相机和扬声器的发明并没有降低跑步、绘画或演奏音乐的价值。当然,这意味着那些以跑步、绘画和演奏音乐为主要收入来源的人需要适应,但许多人仍然将这些活动视为休闲活动和爱好。事实上,能够参与这些活动为了自身的目的,不受商业化压力的影响,反而更为愉快。
这一愿景与流行文化中描绘的理想乌托邦相吻合,例如艾萨克·阿西莫夫的《我,机器人》,其中 AI 自动化所有经济工作,让人类可以专注于智力和休闲活动,不再为谋生而烦恼。在这样的世界中,如果你精通某项已自动化的任务,实际上你仍然可以通过教别人这些技能找到人生意义和收入(例如担任跑步教练、艺术指导和音乐老师)。最终,人类将会向唯一的、定义上无法自动化的产品趋近:那些源于人际连接的活动,如个性化辅导、促进人际关系和情感互动。
然而,我并不天真地认为这样的世界是可能的结果。实现这一愿景的关键在于人类是否能够公平地再分配 AI 带来的收益,让那些其经济价值被自动化取代的人仍然能够获得公平的资源,过上有意义的生活。考虑到今天不平等且商业化的世界,这显然是一个巨大的挑战。虽然探索这一点超出了本文的范围,但我希望在未来的文章中探讨 AI 如何可能重塑更广泛的经济体系。
结论
总之,AI 无疑将对经济产生深远影响,随着性能的提升和成本的迅速下降,影响将变得更加显著。通过一个经济学基础的框架,我解释了为什么一些工人会被取代,而另一些则会被 AI 增强,AI 对工人的影响取决于一个关键指标:AI 在与工人职业相关的任务中是否表现得更好。高技能工人和低技能工人谁更受益,取决于公司生产的性质。然而,目前 AI 的使用方式并不能很好地预示它的经济潜力,因为它是一个通用技术,长期来看,它将创造新的系统和产品,并推动显著的生产力提升。
我通过阐述一些职业的特点来结束讨论,这些职业是理想的。我鼓励更多经济学家利用 AI 模型基准来创建及时且细致的评估,评估不同职业中工人的自动化潜力,从而定量地确定哪些是理想的职业。
最终,AI 与任何技术一样,本质上是中立的,它对社会的影响将由我们的选择决定。AI 从业者、经济学家和政策制定者必须共同努力,通过再分配机制和深思熟虑的监管,确保 AI 能够通过创新与公平之间的平衡,积极影响经济和社会。只有这样,AI 才能像 Anthropic 首席执行官 Dario Amodei 在这篇近期文章中所说的那样³⁸,成为“充满爱意的机器”,将世界转变为更美好的地方。
如果你觉得这篇文章有帮助:
-
查看我在 Medium 上的其他文章:作为数据科学家的构建 AI 应用提示,使用 AI 总结长篇文本,我对职场学习的思考。我喜欢写作,欢迎合作与交流!
-
尝试我开发的一些 AI 应用:Podsmart,它可以转录和总结播客和 YouTube 视频,aiRead,一个 AI 优先的 PDF 阅读器和记事本应用。
脚注与参考文献
[1] 来源:Aschenbrenner, Leopold。(2024)。情境意识 — 未来十年
[2] OpenAI GPT-4 最初发布时每百万输入 token 的费用为 30 美元。根据 LMSYS 的数据,GPT-4o mini 目前在性能上优于最初发布的 GPT-4,费用为每百万输入 token 0.15 美元。
[3] 图片来源:x.com/swyx/status/1815892458519289946/photo/1
[4] 来源:Acemoglu, D.(2024)。AI 的简单宏观经济学(No. w32487)。美国国家经济研究局。
[5] 来源:IMF(2024)。AI 将改变全球经济,让我们确保它造福人类。 www.imf.org/en/Blogs/Articles/2024/01/14/ai-will-transform-the-global-economy-lets-make-sure-it-benefits-humanity
[6] 来源:PwC(2019)。PwC 全球人工智能调查:利用 AI 革命
[7] openai.com/global-affairs/openai-chief-economist-announcement/
[8] 来源:Acemoglu, D., & Restrepo, P. (2018). 《人类与机器的竞赛:技术对增长、要素分配和就业的影响》。美国经济评论,108(6),1488–1542。
[9] 本文介绍的基于任务的模型的具体版本来自 Acemoglu 和 Autor(2022)。
[10] 来源:openai.com/index/sora/
[11] 假设计算机性能的提升仅影响统计软件的运行速度。实际上,计算机性能的提高导致了像 ChatGPT 高级数据分析这样的 AI 系统的出现,它们自动化了更多之前由经济学家手工执行的任务。
[12] 强烈的互补性和生产力效应实际上是类似的论点,关键在于你如何定义任务的广度或具体性。如果你定义任务 Z 包含任务 A(目前已实现自动化)和任务 B(由于生产力效应,导致就业增加),那么你可以说任务 Z 所需的就业增加,意味着在任务 Z 中劳动力与人工智能之间存在强烈的互补性。
[13] 来源:Autor, D., Chin, C., Salomons, A., & Seegmiller, B. (2024). 新边界:新工作的起源与内容,1940–2018。《季刊经济学杂志》,qjae008。
[14] 来源:Whittemore, Nathaniel. 人们为什么如此害怕 Sora? 来自《AI 每日简报》(前称《AI 突破》)open.spotify.com/episode/7FymGelvi7svYbxgGHTCUl?si=5c3ba2a543b24573
[15] 来源:TIME。数据来源:ContextualAI。time.com/6300942/ai-progress-charts/
[16] 来源:OpenAI (2023). GPT-4 技术报告。cdn.openai.com/papers/gpt-4.pdf
[17] 来源:www.oecd.org/en/publications/is-education-losing-the-race-with-technology_73105f99-en.html
[18] 来源:Brynjolfsson, E., Li, D., & Raymond, L. R. (2023). 工作中的生成性人工智能(编号 w31161)。国家经济研究局。
[19] 来源:Dell’Acqua, F., McFowland III, E., Mollick, E. R., Lifshitz-Assaf, H., Kellogg, K., Rajendran, S., … & Lakhani, K. R. (2023). 《驾驭曲折的技术前沿:人工智能对知识工作者生产力和质量的影响的现场实验证据》。《哈佛商学院技术与运营管理单元工作论文》,(24–013)。
[20] 图片来源:www.ft.com/content/b2928076-5c52-43e9-8872-08fda2aa2fcf
[21] 来源:Hoffmann, M., Boysel, S., Nagle, F., Peng, S., & Xu, K. (2024). 《生成性人工智能与工作的性质》。哈佛商学院战略单元工作论文,(25–021),25–021。
[22] 能力通过 GitHub 成就、粉丝数量、在 GitHub 上的工作年限以及在排名仓库中的中心性来衡量。
[23] 来源:80,000 小时播客。Michael Webb 关于人工智能是否会很快导致失业、收入下降和不平等加剧——还是相反的情况。链接:open.spotify.com/episode/3J3AzCbrjZ484moQUhOsZ1?si=4074a8cc39ca4167
[24] 来源:Toner-Rodgers, A. (2024). 《人工智能、科学发现与产品创新》。
[25] 来源: Acemoglu, D. (2024). 人工智能的简单宏观经济学 (编号 w32487)。美国国家经济研究局。
[26] 来源: en.wikipedia.org/wiki/Christopher_Nolan
[27] 图片来源: olanipekunayo2012.medium.com/price-elasticity-of-demand-with-python-analysis-c32d70dd5f6
[28] 来源: www.kentuckianaworks.org/news/2019/4/3/what-bank-tellers-can-teach-us-about-how-automation-will-impact-jobs
[29] 来源: blog.hubspot.com/ai/tiktok-ai-tools
[30] 来源: Epoch AI. (2024) 人工智能扩展是否能持续到 2030 年?链接: epoch.ai/blog/can-ai-scaling-continue-through-2030
[31] 来源: en.wikipedia.org/wiki/General-purpose_technology
[32] 来源: Agrawal, Ajay. (2022). 权力与预测:人工智能的颠覆性经济学。
[33] 图片来源: www.sciencedirect.com/science/article/abs/pii/S157406840501018X
[34] 来源: Mollick, Ethan. (2024). 人工智能在组织中的应用: 一些策略。链接: www.oneusefulthing.org/p/ai-in-organizations-some-tactics
[35] 来源: Mollick, Ethan. (2024). 潜在的专业知识: 每个人都在进行研发。链接: www.oneusefulthing.org/p/latent-expertise-everyone-is-in-r
[36] 来源: 人工智能对英国就业和培训的影响。assets.publishing.service.gov.uk/media/656856b8cc1ec500138eef49/Gov.UK_Impact_of_AI_on_UK_Jobs_and_Training.pdf
[37] 来源: home.treasury.gov/system/files/136/AI-Combined-PDF.pdf
[38] 来源: Amodei, Dario. (2024). 爱的机器。链接: darioamodei.com/machines-of-loving-grace
必要工作终结:普遍基本收入与 AI 驱动的繁荣
如何通过对 AI 工作的税收让每个人都能分享 AI 自动化带来的即将到来的财富。
·发布于Towards Data Science ·12 分钟阅读·2024 年 8 月 19 日
--

图像由 GPT-4、Adobe Firefly 和 Photoshop 创建,©2024。
尽管时间表仍然有些不确定,但压倒性的共识是,机器最终会接管大部分工作。与其陷入关于这一事件何时发生的辩论,不管是两年后还是二十年后,问我们该如何适应并在一个由人工智能(AI)系统日益普及的世界中蓬勃发展,往往更具建设性,尤其是在这种普及通常以牺牲人类工作为代价的情况下。
作为一个社会,我们面临着一个严峻的选择:当机器最终接管工作时,人们会被抛入贫困,还是能享受闲暇时光,获得在自己主宰的生活中生存的力量?后者的情景不仅仅是“不工作”。它是指拥有追求我们带来快乐的自由,而不必背负财务压力。它是指拥有时间和资源去探索我们的热情、旅行、学习和成长,而不必不断地为生计而奔波。在这种情景下,人们仍然可以选择工作,但仅仅是因为他们想工作,而不是因为他们必须工作。他们还可以追求自己的创业事业,无论是兼职副业还是完全独立的初创公司,而不受完全自给自足的限制。
根本的挑战是,我们如何能将自动化 AI 系统最终创造的巨额财富以合理的方式分享。美国目前的税收结构主要通过所得税对个人的工作征税,但随着越来越多的人不可避免地失去工作,这一收入来源将萎缩,而支持需求却在增长。与其对逐渐减少的人类劳动征税,也许我们应该转而对不断增长的 AI 系统劳动征税。
实施普遍基本收入的必要性
普遍基本收入(UBI)近年来因其可能成为应对自动化带来的挑战的解决方案而备受关注。UBI 的核心是政府为每个人提供一定金额的收入,无论他们的背景、就业状态或财务状况如何。这种无条件的收入将为每个人提供财务基础,独立于任何工作或雇主。它不依赖于就业、储蓄或收入水平。UBI 是普遍性的,意味着系统中的每个人,从最贫困到最富有的人,都将获得相同金额的资金。
实施 UBI 的最大问题是如何为其筹集资金。在美国,个人所得税是政府收入的主要来源。然而,这种情况将创造一个循环问题,税收来自收入,而收入又来自税收。试图调整税收制度以覆盖包括工人和非工人在内的 UBI,将对仍然就业的人施加不可持续的负担,这些人可能会产生可以理解的怨恨。此外,随着 AI 系统逐步取代人类工作,萎缩的劳动力将难以承担资助整个系统日益增长的重担。
收入纳税人基数萎缩的问题将是一个紧迫的关注点,无论是否实施 UBI。随着工作被人工智能接管,所得税收入将减少,而与此同时需要援助的人数将增加。不断增加对仍有工作的人的税收将不可持续。
将大多数人口置于街头挨饿并非合理的选择,即便是对于冷酷无情的人来说也不可取。这不仅是极度不公平、不人道且令人谴责的,而且也无法保持政治稳定。如果那些直接从 AI 自动化中受益的人轻率地告诉那些没有受益的人“去吃蛋糕吧”,那么他们很可能会面临类似于这一短语所关联的暴力反抗。
如果替代方案不可行,那么我们必须找到某种方式来支持那些没有工作的人员。目前的失业和福利系统是不足够的,充斥着庞大的官僚机构以及对工作的不合理惩罚。即便这些支持系统有效,它们依然依赖于逐渐缩小的税基,这是不可持续的,且只会延续问题。因此,我们需要探索能够支持没有工作的人的替代方案,以及如何从可持续的来源筹集资金。
UBI 资金筹集的挑战
一些人已经提出了国家销售税作为解决收入税基缩小的可能方案。虽然销售税可能具有累退性,意味着收入较少的人往往支付相对较高比例的收入,相对于收入较多的人,但设计良好的税制可以减轻这一影响。例如,针对生活必需品如食品、医疗费用和住房的免税政策,可以确保销售税更具进步性。理想情况下,盈利企业应承担税负的主要部分,因为它们将是人工智能驱动的效率的直接受益者。有些人可能会预期企业会将成本转嫁给消费者,但人工智能驱动的生产力的收益应该能够抵消这些成本。
在此基础上,我们可以通过设计一个专门针对自动化带来的好处的税制,使得人工智能相关的节省部分税收变得明确。通过适度征税与人工智能相关的成本节省,企业仍然可以享受自动化的好处,同时为更广泛的社会福利作出贡献。重要的是,随着工作被人工智能系统取代,越来越多的人需要援助,税收收入将增长而非减少。
这一核心理念,即要求人工智能的好处与社会共享,是一个根本性的目标。在当前的体制下,人们通过工作来创造价值,他们的薪资本质上是这些价值的一部分,减去生产成本。雇主也会分得一部分,政府也是。这导致了一个零和博弈,即由一群工人所创造的总价值有限,而试图将这一价值广泛分享给那些没有工作的人,最终不可避免地会缩小现有的分配份额。在人工智能工作的情况下,生产成本更低,并且没有员工份额。
无论细节如何,有一点似乎是不可避免且至关重要的:如果人工智能系统将取代人类的工作,从而取代人类的薪资收入,那么它们也必须取代人类的纳税。
还需要注意的是,虽然人工智能系统的工作会使一些工作岗位从人类手中消失,但人工智能所完成的总工作量可能远远大于被取代的人类工作岗位数量。这种差异的一个原因是,人工智能能够做一些对人类来说过于困难或危险,或者反过来是过于琐碎或无聊的事情。另一个原因是,人工智能系统比人类劳动力更容易进行扩展。这个更大的基础意味着,人工智能工作可以以比目前对人类工作征税的更低税率进行征税。
衡量人工智能工作
想象一个世界,其中人工智能系统所做的工作可以被衡量和量化。一个可能的方法是使用一个叫做“人类等效努力时间”(HEET)的概念,它代表一个典型人类完成相同任务所需的时间。通过 HEET,我们可以追踪人工智能自动化,不仅仅是它取代了人类劳动,还可以追踪它为人工智能系统创造了新的工作机会。像这样的测量方式将使我们能够以一种公平、高效和可持续的方式对人工智能系统的工作征税。
通过以 HEET 单位对人工智能的工作征税,我们可以创造一个新的收入来源,补充并最终取代传统的所得税。这种方法将使得随着人工智能系统变得更快、更高效且更容易扩展,收入能够持续增长。与人类劳动不同,人工智能系统维护成本相对较低,而且可以轻松复制,从而实现增长,而无需像雇佣更多人时所面临的种种困难。
需要注意的一点是,这里讨论的人工智能系统只是被设计用来执行特定任务的软件,并没有任何意识或自我意识。将具有感知能力的人工智能用于大多数工作似乎既不必要也不可取。一个有趣的例子来自于电视节目《瑞克与莫蒂》,其中一个角色创造了一个有感知能力的机器人,仅仅为了在早餐桌上传递黄油。当这个机器人意识到自己的目的如此微不足道时,它经历了存在危机和悲伤。尽管这个情节具有幽默感,但它仍然展示了仅为工作任务创造感知人工智能可能带来的潜在陷阱。
就像传递黄油的机器人一样,当人类面对单调或毫无价值的任务时,会感到无聊和沮丧。用我们的机器不必要地复制这种体验似乎既无意义又残忍。绝大多数任务可以在没有自我意识的情况下完成,因此没有必要创造复杂的人工生命来执行这些任务。(这也是一件好事,因为我们目前并不知道如何制造有自我意识的软件。)没有感知能力的机器不需要从它们创造的价值中分得一份,除了它们的运营成本。
我认为衡量 HEET 不会是简单明了的,但似乎并非不可能。我们对人类工人及其能力有大量了解,这些知识可以用于评估人工智能系统执行的任何任务,并估算人类完成该任务需要多长时间,或进行类似的估算。对于人工智能将要执行的数百万项任务做出这样的判断听起来很繁琐,但这也是可以通过人工智能系统来自动化的。可以想象,公司不再报告支付了多少工资并对工资进行征税,而是报告其软件完成了多少 HEET,并对这些税款进行缴纳。
可以想象,由国税局(IRS)管理的人工智能系统会接收工作描述,分配一定的 HEET(人工智能工作单位),并征收税款。与今天的税收系统一样,这将需要一些诚实,并且对于撒谎者还会有一定的惩罚。人们可能不喜欢这一过程,且很可能会频繁上诉。无论是故意还是因为错误,人们都会漏报工作,大家可能依旧讨厌国税局。换句话说,这和今天的税制并没有太大区别。我们不过是将一个令人沮丧的官僚主义税制换成了另一个,唯一的区别是新的税制会对快速增长的人工智能驱动的生产力征税,而不是对日益减少的个人收入征税。
一些细节
对人工智能工作征税的一个预期反对意见是,这会对公司造成负担,并且会抑制创新和增长。然而,这个反对意见其实经不起推敲。如今,大公司想要完成工作,必须雇佣员工,支付薪水并缴纳税款。如果是人工智能系统在做工作并被征税,那么就不需要支付薪水,税收额度可能远远低于公司在支付员工薪水及其相关税收时所需要支付的金额。例如,公司可能不再支付员工$50K 的薪水,外加$10K 的工资税(并且希望能为福利支付一定金额)来完成一单位人力工作,而是部署十个人类等效的人工智能系统,每个支付$1K 的税款,这样就能以六分之一的成本完成十倍的工作量,而政府却仍然能征收相同数量的税款。
每个人都能获益的想法,从我们通常的零和游戏角度来看显得荒谬。怎么可能每个人都能获益呢?关键的变化将是大量的人工智能工作者,他们完成工作却几乎不需要任何回报。
个人和小公司可能会依赖人工智能自动化来实现需要劳动但他们负担不起雇佣人力的活动。对于这些小型实体来说,若对使用人工智能系统征税,可能会让他们的商业变得不可行。从这个角度看,征收 HEET 税可能会抑制创新并造成新的竞争壁垒。然而,系统可以设计成避免这一问题。
例如,年收入少于 100 万美元的商业用途和个人用途可以免除 HEET 税。为了避免突如其来的成本冲击,税收可以在年收入从 100 万美元到 200 万美元之间逐渐增加,让这些企业能够逐步适应新税收,而不会被负担压垮。可以想象,设计一个精心考虑的系统,带有其他适当的免税条款。
工作的未来与人类福祉
工作的未来将显然是由人工智能系统进行大规模自动化,并且这些人工智能系统将非常高效。人工智能系统不会请病假。由人工智能运行的工厂不需要为人类站立提供安全的空间,而且它可以 24 小时不间断运行。人工智能会计服务不需要花费资金在办公室空间、人员管理或培训上。如果你拥有一个由人工智能工作人员构成的企业并且想要扩展,你只需要再购买一些计算机,或许是更多的机器人。限制人类生产力的大多数因素对人工智能系统并不适用。这种节省下来的财富将使像 UBI(基本收入)这样的制度成为可能,但前提是我们找到一种方法将这些财富分享给除人工智能系统拥有者之外的其他人。
今天,找不到工作会带来巨大的压力和不安全感,失业与抑郁症和焦虑症的风险增加相关。此外,找工作所面临的财务和社会压力可能让人感到不堪重负,甚至令人虚弱。然而,那些没有工作但经济上比较稳定的人常常会发现,他们能够在没有生计压力的情况下去追求自己的热情。例如,一个精心规划并资金充足的退休生活可以提供财务自由,使人们能够将精力投入到带来快乐和成就感的有意义活动中,而不再受到经济压力的限制。
如果我们找到一种方法来分享由人工智能创造的财富,我们就可以创造一个没有人需要为贫困或无家可归而挣扎的世界。人们将拥有追求自己热情和兴趣的自由,可以把每天的时间花在带来快乐的事情上。他们可能会投身于创造性事业、冒险或社区服务,并且有足够的时间和资源去培养有意义的人际关系。在这个世界里,工作将成为一种选择,而不是一种必需,人们也不会把大部分清醒的时间都花在为别人工作上。
这并不是第一次有人预测“工作的终结”,但这一次有着根本性的不同。过去,技术进步使得人类的工作变得更加高效。过去,挖一个坑可能需要数百小时的人工劳动,而现在一个人用蒸汽铲就能在几个小时内完成。与其手工一件件艰苦地生产,不如在工厂里同样数量的工人每小时就能生产出数百件。然而,仍然需要人类的存在。现在,试想一下没有人的工厂,或者自动驾驶和操作的建筑设备。零人生产之所以可能,正是因为 AI 系统能够自主做出操作决策,并处理异常情况。
最后思考
我建议对 AI 系统所做的工作征税,确实是有些初步且模糊的。它可能存在上百个需要回答的问题,以及成千上万的细节需要理清。而且,也可能存在我忽略的更好的想法,无论是在资金筹集还是分配支持方面。国家销售税或对 HEET 的税收只是两种可能性。也许,像对计算消耗的电力征税这么简单的做法就足够了,或者我们也许需要一些复杂的新数学理论来衡量计算的价值。
我还提出了一些例外情况和门槛,旨在确保税负落在那些能够承受且从用 AI 系统替代人类员工中受益的公司身上。确定这些例外和门槛的具体内容需要经过仔细的思考和分析,以确保数字能合理运作。一个设计不良的系统可能比什么都不做还要糟糕。这个问题是数学和经济学的领域,而不是政治斗争和迎合特殊利益的场所。
总之,我对 AI 工作的税收提案只是分享 AI 自动化成果的一种可能解决方案。虽然仍处于初步阶段,但我相信这是一个值得进行的重要对话。在前进的过程中,我们必须优先考虑谨慎的思考和分析,以确保我们选择的任何解决方案都公平、有效且可持续。与其陷入党派政治,不如专注于找到一个能够造福所有人的解决方案,而不仅仅是少数人。
无论实际解决方案最终是什么,如果我们不找到一种方式来分享 AI 系统所创造的财富,那么我担心世界上大多数人将被抛在后面,并遭受极大的痛苦。我们面前有一个选择:反乌托邦的不平等,还是一个资源充足、人人共享的美好世界。不幸的是,如果我们什么都不做,默认的将是反乌托邦的情形。我非常希望我们能够放下分歧和恐惧,专注于实现那个更光明的可能性。
关于我: James F. O’Brien 是加利福尼亚大学伯克利分校的计算机科学教授。他的研究兴趣包括计算机图形学、计算机动画、物理系统模拟、人类感知、渲染、图像合成、机器学习、虚拟现实、数字隐私以及图像和视频的法医分析。
如果你觉得这篇文章有趣,那么这里是常用的 关注 和 订阅 链接。你也可以在 Instagram、 LinkedIn,以及 加州大学伯克利分校找到我。
免责声明:本文中表达的任何观点仅代表作者作为个人的立场,文章内容不应被解读为与作者在任何机构的专业职位相关的声明。
本文及所有嵌入的图像版权归作者所有,版权年份为 2024。本文由一位人类撰写,且经过大型语言模型(LLM)和其他人工校对及编辑建议。
演示错误检查和复审的必备指南
数据科学家(不仅仅是他们)常常忽视的技能
·发表于Towards Data Science ·6 分钟阅读·2024 年 8 月 29 日
--

图片由Campaign Creators提供,来源于Unsplash
每个从事数据科学的人都知道,数据讲故事是一项至关重要的技能。关于它的书籍、文章和帖子有成千上万。许多数据科学课程中都会有专门的一部分讲解它。我们被告知,在展示数据时,我们应该用正确的图表讲述一个故事,温和地引导我们的观众。
我们没有被告知的,可能是因为它显而易见,那就是我们的演示应该是正确的!而且由于没人提及这个问题,我们从未被教导如何检查演示中的错误。我相信这是一个被忽视的关键技能。
让错误滑入演示中可能会带来严重后果。最轻微的后果是未能让观众理解数据分析结果。一场有错误的演示至少会让演讲者看起来很草率,最糟糕的情况下则显得无知或欺骗观众。这将侵蚀他的信誉。
请注意,当我说演示应该“正确”时,我不仅仅指的是使用适当的统计分析工具,或者合适的机器学习算法。也许更好的说法是,演示应该在内部是“自洽”的。幻灯片中的一个元素不应…
图解图理论精要:从 18 世纪谜题到人工智能框架
学习如何提升你的数据分析,针对先进的计算任务,从创新的优化策略到基础的机器学习算法
·发布于 Towards Data Science ·20 分钟阅读 ·2024 年 5 月 16 日
--

图 1:Merian-Erben 的1652 年科尼斯堡地图的增强版,数字化复原突出了普雷格尔河(蓝色)及其七座桥梁(红色),更好地呈现历史布局。强调部分由作者(Sydney Nye)添加。
想象一下,1736 年,你正在普鲁士的科尼斯堡繁忙街头漫步。现在的俄罗斯加尔东格勒,这座繁荣的港口是文化和建筑的奇迹。你沿着普雷格尔河的河岸漫游,这条城市的重要水道,你会听到市场上热闹的喧嚣,远处商船的低鸣声。七座宏伟的桥梁横跨河面,连接着不同的岛屿和街区。你可能不知道的是,你走过的这条路径,已经成为一个数学谜题的基础,困扰着欧洲大陆一些最敏锐的头脑。
这个问题看似无关紧要,却充满争议:是否可能在不重复经过的情况下,穿越这座城市,每座桥都恰好走一次?

图 2:“莱昂哈德·欧拉”,由约翰·乔治·布鲁克(1756 年)创作
尽管最初他认为这个问题很平凡,但最终这一命题却不可抗拒地吸引了他…
数据可视化的 R 和 Python 库实用指南
让我们深入了解 R 和 Python 中最重要的库,如何使用它们来可视化数据并创建不同的图表,以及它们的优缺点。
·发布于 Towards Data Science ·阅读时间 15 分钟·2024 年 12 月 16 日
--
成为某些编程语言的专家是每个有抱负的数据专业人士的目标。在众多编程语言中达到一定水平是每个人的关键里程碑。
对于数据工程师来说,SQL 可能是最重要的语言。作为一名网页开发人员,你需要熟练掌握 JavaScript、HTML、CSS 和 PHP。另一方面,对于数据科学家来说,Python 和 R 是首选工具。这两种语言各有优缺点——但都提供了强大的工具和庞大的社区来分析和可视化数据。
如果你刚刚开始数据科学之旅,选择 R 还是 Python 可能会让人感到不知所措。但如果你希望长远发展进入这个领域,无论如何你迟早会接触到这两种语言。而且,如果你已经在大学里学习,可能会同时接触到这两种语言的课程。
但让我们深入了解 R 和 Python 中最重要的库,如何在 R 和 Python 中创建图表的方式有所不同(包括代码示例),以及这两种语言的优缺点。
目录
1 — 为什么 R 是必备的?(以及用于可视化的核心库)
2 —…
Llama 的发展:从 Llama 1 到 Llama 3.1
《Meta AI Llama 模型家族的进展与创新全面指南》
·发布于 Towards Data Science ·15 分钟阅读·2024 年 9 月 6 日
--
本文由 Rafael Guedes 共同撰写。
引言
Meta 发布了其大型语言模型(LLM)Llama 的三个主要版本,并进行了一个小的更新(版本 3.1)。Llama 在 2023 年初的首次发布标志着自然语言处理(NLP)领域开源社区的一次重大进步。Meta 通过分享其最新的 LLM 版本,持续为该社区做出贡献。
为了确保准确性,我们应该区分开放 LLM 和开源 LLM。传统的开源软件会在特定的公共使用和修改许可下提供其源代码。在 LLM 的背景下,开放 LLM 通常会披露模型权重和初始代码。同时,开源 LLM 还会分享整个训练过程,包括训练数据,并采用宽松的许可。如今,大多数模型,包括 Meta 的 Llama,都属于开放 LLM 类别,因为它们没有发布用于训练的数据集。
Llama 经历了三次关键的架构迭代。版本 1 对原始的 Transformer 架构进行了若干增强。版本 2 在更大的模型中实现了 Grouped-Query Attention(GQA)。版本 3 将 GQA 扩展到较小的模型,并引入了更高效的……
超越个人训练:从人类到 AI
一段将我的 Garmin 数据转化为量身定制的健身伴侣的旅程
·发布于 Towards Data Science ·阅读时间 24 分钟·2024 年 4 月 4 日
--

图片来源:Alora Griffiths 来自 Unsplash
我一直比运动更喜欢电子游戏,而在 2000 年代初,喜欢电子游戏并不会让你成为一个酷炫的主播或电子竞技选手;那时它只是让你变成了一个书呆子。
在我生命中的某个时刻,偶然之间,我爱上了举重。我喜欢把举重定义为一种书呆子的运动:它是一个优化问题,你需要平衡训练和饮食,并且你可以通过数字看到自己的进步;如果你这周比上周举得更多,那就说明你在进步。
我的旅程经历了不少波折。伤病、搬家、疫情以及失去兴趣都曾挡在我的路上。但一个月前,我突然受到启发,决定在家设立一个健身房:一个架子、哑铃、杠铃、重物和一张长椅。就这样,我又开始行动了。
挑战
过去,我曾在普通健身房训练,但从未请过私人教练。我认为他们太贵了,而且看不出他们能帮我什么,似乎我自己也能做到。那是个错误。独自训练导致了伤病,我不再进步,始终停留在平庸的水平。随着时间的推移,我意识到拥有一位优秀的教练真的非常重要(在人生中、在任何领域中)。
SQL 的演变
解锁大语言模型的潜力
·发布于 Towards Data Science ·12 分钟阅读·2024 年 8 月 18 日
--

图片由 ZHENYU LUO 提供,来源于 Unsplash
在这篇文章中,我将探讨大语言模型(LLMs)如何将自然语言转换为 SQL,使查询编写对非技术用户更为友好。讨论将包括展示 LLM-based 解决方案开发易用性的实际示例。我们还将涵盖各种使用案例,并通过创建一个简单的 Slack 应用程序来演示整个过程。构建一个 AI 驱动的数据库查询系统涉及多个关键考虑因素,包括保持安全性、确保数据相关性、管理错误和正确训练 AI。在这个故事中,我将探讨应对这些挑战的最快方式,并分享一些建立稳固高效的文本到 SQL 查询系统的技巧。
最近,很难想到有什么技术比大语言模型更具影响力和更广泛讨论。基于 LLM 的应用现在是最新的趋势,就像曾经涌入市场的 Apple 或 Android 应用一样。它已经在 BI 领域无处不在,我之前曾在这里写过它 [1]
文本到视频模型的演变
简化生成视频扩散背后的神经网络
·发布于数据科学前沿 ·阅读时间 9 分钟·2024 年 9 月 19 日
--

让我们来学习视频扩散!
我们已经见证了 AI 图像生成的显著进步。但当我们加入时间这一维度时会发生什么呢?毕竟,视频是动态图像。
文本到视频生成是一项复杂的任务,它要求 AI 不仅要理解物体的外观,还要理解它们如何随着时间的推移运动和互动。这比文本到图像要复杂一个数量级。
要制作一个连贯的视频,一个神经网络必须:
1. 理解输入提示
2. 理解世界是如何运作的
3. 了解物体如何运动以及物理学如何应用
4. 生成在空间、时间和逻辑上都合理的帧序列
尽管面临这些挑战,今天的扩散神经网络在这一领域取得了令人瞩目的进展。在本文中,我们将介绍视频扩散模型背后的主要思想——主要挑战、方法以及该领域的开创性论文。
此外,本文基于我制作的更大规模 YouTube 视频。如果你喜欢这篇文章,你也会喜欢观看该视频。
文本到图像概述
要理解文本到视频生成,我们需要从它的前身开始:文本到图像的扩散模型。这些模型有一个单一的目标——将随机噪声和文本提示转换为连贯的图像。一般来说,所有生成图像的模型都是这样做的——变分自编码器(VAE)、生成对抗神经网络(GAN)以及扩散模型。

所有图像生成模型的基本目标是将随机噪声转换为图像,通常是基于附加的条件提示(例如文本)。[图像由作者提供]
扩散,特别依赖于逐渐去噪的过程来生成图像。
1. 从一个随机生成的噪声图像开始
2. 使用神经网络逐步去除噪声
3. 使去噪过程依赖于文本输入
4. 重复直到生成清晰图像

扩散模型是如何生成图像的——神经网络逐步从纯噪声图像中去除噪声,并根据文本提示进行条件处理,最终显示清晰图像。 [插图:作者](图像由神经网络生成)
但这些去噪神经网络是如何训练的呢?
在训练过程中,我们从真实图像开始,逐步向其中添加噪声——这叫做正向扩散。这样会生成大量清晰图像及其稍微有噪声的版本。然后,神经网络被训练来通过输入噪声图像,预测去除多少噪声才能恢复更清晰的版本。在文本条件模型中,我们训练注意力层以关注输入的提示进行引导去噪。

在训练过程中,我们向清晰图像(左)添加噪声——这叫做正向扩散。神经网络被训练来逆转这一噪声添加过程——这个过程被称为反向扩散。图像由神经网络生成。 [图片来源:作者]
这种迭代方法能够生成高度详细且多样化的图像。你可以观看以下的 YouTube 视频,里面我会详细讲解文本到图像的过程——比如正向和反向扩散、U-Net、CLIP 模型,以及我如何从零开始在 Python 和 Pytorch 中实现这些概念。
如果你已经掌握了文本到图像条件扩散的核心概念,那么我们接下来可以讨论视频。
时间维度:新的前沿
理论上,我们可以遵循相同的条件噪声去除思路来进行文本到视频的扩散。然而,加入时间这一因素后,带来了几个新的挑战:
1. 时间一致性: 确保物体、背景和运动在各帧之间保持一致。
2. 计算需求: 生成每秒多个帧,而不是单个图像。
3. 数据稀缺: 尽管大型图像-文本数据集很容易获得,但高质量的视频-文本数据集却很稀缺。

一些常用的视频-文本数据集 [图片来源:作者]
视频扩散的演变
由于缺乏高质量的数据集,文本到视频不能仅依赖监督训练。因此,通常人们还会结合另外两种数据源来训练视频扩散模型——第一种 — 配对的图像-文本数据,这种数据更加容易获得,和第二种 — 未标注的视频数据,这种数据极为丰富,包含了大量关于世界如何运作的信息。为了解决这些挑战,出现了几种突破性的模型。我们将逐一讨论一些重要的里程碑论文。
我们即将进入技术细节! 如果你觉得接下来的内容比较难,随时可以在阅读下一部分时观看这段辅助视频,它会为你提供一个并排的视觉指导。
视频扩散模型(VDM)— 2022
VDM 使用带有因式分解时空卷积层的 3D U-Net 架构。每个术语在下图中进行了说明。

每个术语的含义(图像来源:作者)
VDM 是在图像和视频数据上共同训练的。VDM 用 3D UNet 模型替代了图像扩散模型中的 2D UNet。视频被作为一系列 2D 帧的时间序列输入到模型中。术语“因式分解”基本上意味着空间和时间层被解耦并分别处理。这使得计算更加高效。
什么是 3D-UNet?
3D U-Net 是一种独特的计算机视觉神经网络,首先通过一系列因式分解的时空卷积层对视频进行下采样,基本上是在不同分辨率下提取视频特征。然后,网络有一个上采样路径,将低维特征扩展回原始视频的形状。在上采样过程中,使用跳跃连接来重用在下采样路径中生成的特征。

3D 因式分解 UNet 架构 [图像来源:作者]
记住,在任何卷积神经网络中,较早的层总是捕捉图像局部区域的详细信息,而后面的层则通过访问更大的区域来捕捉全局级别的模式——因此,通过使用跳跃连接,U-Net 将局部细节与全局特征结合起来,成为一个超棒的特征学习和去噪网络。
VDM 是在配对的图像-文本和视频-文本数据集上共同训练的。虽然它是一个很好的概念验证,但对于今天的标准而言,VDM 生成的视频分辨率相当低。
Make-A-Video(Meta AI)— 2022
Meta AI 的 Make-A-Video 大胆地提出了一个观点:我们不一定需要标注过的视频数据来训练视频扩散模型。什么?没错,你没看错。
向图像扩散模型添加时间层
Make A Video 首先训练一个常规的文本到图像扩散模型,就像 Dall-E 或 Stable Diffusion 那样,使用配对的图像-文本数据。接下来,使用无监督学习对未标注的视频数据进行训练,以教授模型时间关系。网络的附加层使用一种称为遮掩时空解码的技术进行训练,在该技术中,网络通过处理可见帧来生成缺失的帧。请注意,这个流程中不需要标注的视频数据(尽管进一步的视频-文本微调可以作为附加的第三步),因为模型通过配对的文本-图像数据和原始未标注的视频数据来学习时空关系。

Make-A-Video 概述 [作者图示]
上述模型输出的视频是 64x64,包含 16 帧。然后,通过使用称为时间超分辨率(TSR)和空间超分辨率(SSR)单独的神经网络,在时间和像素轴上对视频进行上采样(在现有帧之间插入新帧以增加每秒帧数(fps)),以及对视频的单帧进行超分辨率处理,提升分辨率。经过这些步骤后,Make-A-Video 输出的是 256x256 分辨率、包含 76 帧的视频。
你可以在这里了解更多关于 Make-A-Video 的信息。
Imagen Video(谷歌)— 2022
Imagen 视频采用七个模型的级联来进行视频生成和增强。该过程从一个基础视频生成模型开始,创建低分辨率的视频片段。接着是一系列超分辨率模型——三个 SSR(空间超分辨率)模型用于空间放大,三个 TSR(时间超分辨率)模型用于时间放大。这种级联方法使得 Imagen Video 能够生成高质量、高分辨率的视频,并且具有令人印象深刻的时间一致性。

Imagen 工作流程 [来源:Imagen 论文:imagen.research.google/video/paper.pdf]
VideoLDM(NVIDIA)— 2023
类似于 Nvidia 的 VideoLDM 这样的模型尝试通过使用潜在扩散建模来解决时间一致性问题。首先,他们训练一个潜在扩散图像生成器。基本思路是训练一个变分自编码器(VAE)。VAE 由一个编码器网络组成,可以将输入帧压缩成低维潜在空间,另一个解码器网络则可以将其重建回原始图像。扩散过程完全在这个低维空间中进行,而不是在完整的像素空间中,从而使得计算效率更高,并且在语义上更强大。

一个典型的自编码器。输入帧被单独下采样到低维压缩潜在空间。然后,解码器网络学习从这个低分辨率空间中重建图像。[图片由作者提供]
什么是潜在扩散模型?
扩散模型完全在低维潜在空间中进行训练,也就是说,扩散模型学习去噪的是低维潜在空间中的图像,而不是全分辨率的帧。这就是为什么我们称其为潜在扩散模型。最终的潜在空间输出将通过 VAE 解码器转换回像素空间。
VAE 的解码器通过在其空间层之间添加新的时间层进行增强。这些时间层是在视频数据上微调的,使得 VAE 能够从由图像扩散模型生成的潜在向量中生成时间一致且无闪烁的视频。通过冻结解码器的空间层,并添加新的可训练时间层,这些时间层基于先前生成的帧进行条件化。

VAE 解码器通过加入时间信息进行微调,以便从由潜在扩散模型(LDM)生成的潜在向量中生成一致的视频[来源: 视频 LDM 论文 arxiv.org/abs/2304.08818]
SORA(OpenAI) — 2024
虽然视频 LDM 将视频的单独帧压缩以训练 LDM,SORA 则在空间和时间上同时压缩视频。最近的论文如CogVideoX 已证明,3D 因果变分自编码器(VAE)在压缩视频方面表现出色,使得扩散训练在计算上更加高效,并能够生成无闪烁且一致的视频。

3D VAE 通过空间和时间压缩视频,以生成视频数据的压缩 4D 表示。[图片由作者提供]
用于扩散的 Transformers
扩散网络使用的是 Transformer 模型,而不是更传统的 UNet 模型。当然,transformer 需要输入数据以序列的形式呈现。这就是为什么压缩的视频编码被展平为一个补丁序列。请注意,每个补丁及其在序列中的位置代表了原始视频的一个时空特征。

OpenAI SORA 视频预处理 [来源: OpenAI (https://openai.com/index/sora/)] (许可: 免费)
有人推测 OpenAI 已经收集了一个相当大的视频-文本标注数据集,他们正在利用这些数据集训练条件性视频生成模型。
结合下面列出的所有优势,再加上一些 OpenAI 可能永远不会透露的巧妙技巧,SORA 有望成为视频生成 AI 模型的一大飞跃。
-
大规模视频-文本标注数据集 + 图像-文本数据和未标注数据的预训练技术
-
变换器的通用架构
-
巨大的计算投资(感谢微软)
-
潜在扩散建模(Latent Diffusion Modeling)的表征能力。
接下来会发生什么
人工智能的未来是容易预测的。在 2024 年,数据 + 计算 = 智能。大型企业将投入计算资源来训练大型扩散变换器(diffusion transformers)。它们将雇佣注释员标注高质量的视频-文本数据。大规模的视频-文本数据集可能已经存在于封闭源领域(看着你,OpenAI),并且它们可能会在接下来的 2-3 年内成为开源,特别是随着近期 AI 视频理解的进展。尚待观察的是,未来巨大的计算和财务投资是否能够单独解决视频生成问题,或者是否需要研究界进一步的架构和算法进展?
链接
作者的 YouTube 频道: www.youtube.com/@avb_fj
关于这个话题的视频: youtu.be/KRTEOkYftUY
15 步从零到英雄:条件图像扩散教程: youtu.be/w8YQc
论文与文章
视频扩散模型: arxiv.org/abs/2204.03458
Imagen: imagen.research.google/video/
制作视频: makeavideo.studio/
视频 LDM: research.nvidia.com/labs/toronto-ai/VideoLDM/index.html
CogVideoX: arxiv.org/abs/2408.06072
OpenAI SORA 文章: openai.com/index/sora/
扩散变换器: arxiv.org/abs/2212.09748
有用的文章: lilianweng.github.io/posts/2024-04-12-diffusion-video/
GNN 的表现力 — 介绍与基础
GNNs 解析系列
为图神经网络模型的理论分析连接点滴
·发表于 Towards Data Science ·13 分钟阅读·2024 年 2 月 21 日
--
本系列旨在提供一个全面的理解,帮助读者理解 GNN 是如何捕捉网络结构的关系信息的。
我想感谢 Pantelis Krasadakis, Alessia Melania Lonoce, 和 Antonio Vetrò 审阅了这篇文章,并给出了宝贵的反馈。
介绍
图表示了描述交互元素的通用模型,而图神经网络(GNNs)已成为将学习算法应用于图结构数据的必备工具。
GNNs 最常见的框架基于消息传递神经网络(MPNN)。在这个框架中,邻居的特征通过边缘作为信息传递给目标节点。然后,目标节点的表示会与其邻居的聚合表示一起更新。
基于这一原理,节点的新表示编码了与局部结构相关的信息。这个信息传递过程如图 1 所示。

图 1 — 在 2 层 MPNN 中,邻居特征聚合到橙色节点
该图展示了如何更新橙色节点的表示…
GNN 的表达能力 — 消息传递神经网络
表达性 GNN 系列
引入 MPNN 架构与 PyTorch Geometric,连接点滴,进行图神经网络模型的理论分析
·发布于Towards Data Science ·18 分钟阅读·2024 年 9 月 2 日
--
介绍
图神经网络(GNNs)是强大的架构,旨在建模和分析以图结构组织的数据。这些模型能够有效捕捉图中信息的模式,从而支持一系列下游任务,包括节点分类、链接预测和图回归。
在本系列的上一篇文章中,我们介绍了图同构的概念,这对于阐明不同或等价关系结构的概念至关重要。
连接点滴,进行图神经网络模型的理论分析
towardsdatascience.com
根据图同构原则,我们可以为任何在图上运行的机器学习模型设定要求:
-
为同构图生成相同的表示。
-
通过不同的关系结构来区分图。
来自图论的 Floyd-Warshall 算法,应用于解析分子结构
通过简单的 JavaScript 代码提供的实践性解释
LucianoSphere (Luciano Abriata, PhD)
·发表于Towards Data Science ·阅读时间:8 分钟·2024 年 8 月 22 日
--
Floyd-Warshall 算法在图论中至关重要,因为它提供了一种高效的方法来计算图中所有节点对之间的最短路径。这个经典的动态规划算法不仅在理论网络分析中有传统的应用,而且在其他领域也得到了广泛的应用。它的工作原理是读取一个描述哪些节点对由一条边连接的矩阵,并输出连接每一对节点的最小边数:

一组由边(浅绿色线)连接的节点(红色)以及通过 Floyd-Warshall 算法计算的两组距离(节点对之间的链接数,深绿色带虚线)。直接链接通过一个称为“邻接矩阵”的矩阵进行编码,该矩阵的元素为 1(节点相连)或 0(节点不相连)。算法输出的矩阵大小相同,但包含所有节点之间的最小边数,即将任何两个节点分开的最少边数。此图及所有其他图表均由作者制作。
这是关于图中连通性的重要信息,具有广泛的应用;例如,在优化通信网络、分析社交网络中的联系,或者如我在此介绍的那样,在解析分子结构中——这是化学信息学和结构生物信息学许多任务的核心。
在这篇文章中,我将向你展示如何使用弗洛伊德-沃舍尔算法来计算分子中原子之间的键距,换句话说,就是计算分子中每一对原子之间的最小键数。希望我的例子不仅能展示该算法在化学中的应用,还能激发你将其应用于其他领域…
数据建模的被遗忘的指导作用
深入理解如何负责任地构建数据的真正意义
·发表于Towards Data Science ·10 分钟阅读·2024 年 8 月 21 日
--
想象一下在没有蓝图的情况下建造一座摩天大楼,临时决定如何铺设混凝土、对齐支撑梁并布线,随着建筑的形成不断做出调整。
嗯,这正是今天数据组织中正在发生的事情…
数据建模,数据生态系统的基础蓝图,正被在急于采用最新技术和提供快速结果的过程中被忽视。我们看到一堆可预测的问题正在通过短期的工程修复措施来解决。
什么是数据建模?
根据Joe Reis(他目前正在写一本关于这个主题的书),数据模型是:
一种结构化的表示方式,它组织和标准化数据,以引导人类和机器行为、支持决策制定并促进行动
数据建模几十年来一直是数据管理的基石,是公司构建和理解数据的关键。然而,近年来,随着新数据工具和技术的出现,这一实践变得不那么流行。这一转变反映了数据行业的短期视角,在这一视角下,短期结果往往被优先考虑,而基础性原则被忽视。
核心问题在于,数据建模需要深刻理解…
数据验证的基础
讨论数据验证的基本原则和方法论
·发布于Towards Data Science ·阅读时长 6 分钟·2024 年 4 月 30 日
--

图片来源:Vardan Papikyan 于Unsplash
虽然这可能不是数据工作中最具魅力的方面,但数据验证对于任何与数据相关的任务都是至关重要的。数据验证可能会很乏味。当我们想到数据验证时,第一件事是什么呢?无休止的电子表格?多层次的子查询 SQL?
我希望我能说数据验证很有趣。不幸的是,这并不是一条铺好的道路。如果你正在阅读这篇博客文章,你可能之前遇到过数据验证的挑战,或者你现在正在为此挣扎。本文的目标是分享我在数据验证方面的经验,包括我使用过的原则和方法,这些将对你有所帮助。
什么是数据验证?
数据验证是解锁任何系统潜力的关键。它确保生成的数据符合并超越所有期望,消除任何疑问或错误。通过验证,你可以确信你的系统正以最佳状态运行,提供你可以依赖的结果。
数据验证是我们日常生活中不可或缺的一部分。它无处不在,从我们消费的信息到我们的决策。
- 我允许我的孩子每天看 20 分钟的电视...
数据职业的四大支柱
确保获得入门级数据分析师职位的技术基础
·发表于 Towards Data Science ·阅读时长 8 分钟·2024 年 11 月 10 日
--

TLDR;
-
电子表格(Excel)
-
SQL
-
可视化工具(Tableau 或 Power BI)
-
脚本语言(Python 或 R)
介绍
我经常被尝试进入数据领域的人问到:他们需要学习哪些技能才能获得第一份数据工作,应该在哪里学习这些技能。本文总结了我过去五年给有志成为数据科学家、分析师和工程师的人的建议。
本文主要面向那些自学成才、希望进入数据领域并获得第一份工作的读者。如果你正在阅读这篇文章,那么很可能你第一份工作会是数据分析师。大多数入门级数据职位都是分析师职位,我不认为数据科学家或数据工程师是入门级职位。
四大支柱是电子表格、SQL、可视化工具和脚本语言。
不同的工作需要不同组合的这些技能,你可以通过精通其中一个支柱来建立整个职业生涯,但几乎所有的数据职位都至少需要对这四个领域有初步了解。
Excel

Excel 是数据世界的起源和终结。30 年来,数据界一直在讨论传说中的“Excel 杀手”,但至今未曾出现。你可能参与了一个由多个团队组成、历时 6 个月的项目,旨在统一来自 7 个数据库的数据,并将其构建成最炫的 Tableau 仪表板,而你的利益相关者问的第一件事就是,如何将数据导出到 Excel。
Excel 功能庞大,大多数用户只是触及了其表面,但这是我认为成为分析师角色所需的最低技能列表:
-
基本界面导航
-
公式
-
条件语句(IF, IFS, COUNTIFS, SUMIFS 等)
-
电子表格卫生(确保你的电子表格逻辑布局清晰)
-
数据集连接(V-lookup, X-lookup, index-match)
-
图表/可视化
-
数据透视表
-
数据筛选和排序
Power Query
如果你想更进一步,我还推荐有抱负的分析师熟悉 Power Query(也叫 Get and Transform)。我喜欢 Power Query,因为它是初学者接触更正式结构化数据和适当表格数据的良好入门工具。
学习 Power Query 和 Power Pivot 的一个优势是,它们在 Power BI 中得到了广泛应用。
那么,Google 表格怎么样呢?
Google 表格是 Excel 的一个可靠替代品,但缺少很多高级功能。如果你学会了 Excel,可以很快适应 Google 表格,并且可以在 Google 表格中学习许多基本的电子表格功能,但我认为目前它并不足以替代 Excel。
我的观察是,Google 表格通常在政府、学术界和早期至中期的创业公司中使用。
VBA
如果你正在尝试解决 Excel 中的某个问题,而你找到的教程建议使用 VBA,那么请寻找其他解决方案。
SQL 和数据库

图片来源:Jan Antonin Kolar 在 Unsplash
这是一个让有抱负的分析师感到棘手的话题,因为除了在生产环境中,学习如何处理数据库的细微差别非常困难,特别是当数据集非常干净时。这是因为大多数实践数据集过于整洁。
在我的一份工作中,我曾完全搞砸了一个 SQL 查询请求,因为我犯了一个初学者的错误——将两个表连接在 FINANCING_ID 列上,而不是 FINANCING_ID_NEW 列。
大多数拥有分析师的组织,其数据库并非经过精心规划或设计,而是随着时间的推移、通过并购以及软件工程师为了解决眼前问题而迅速积累的数据。
对许多组织来说,可能需要几个月的时间才能熟悉其数据库。
所以我的建议是,除了学习 SQL 的基本语法外,我不会花太多时间去精通 SQL,除非你已经有了一个每天需要写 SQL 的工作。
这是我建议你学习的基本查询技能:
-
基本语法
-
SQL 查询的结构
-
聚合
-
多表联接
-
维度建模
-
CTEs 和子查询
你应该学习哪种方言?
其实并不重要,因为它们非常相似,一旦你掌握了其中一种,差异可以通过 Google 或 Chat GPT 轻松解决。我的建议是选择 Postgres 或 T-SQL。
可视化

图片来源:Clay Banks 摄于 Unsplash
虽然 Excel 可以用于生成一些可视化图表,但大多数雇佣分析师的公司会使用 Power BI 或 Tableau 来制作仪表盘(我也使用过其他一些工具,但这两者是主流)。
和 SQL 一样,我不建议在你找到工作之前过多关注可视化中的索引问题,学习基础是很重要的,但许多高级功能最好是在生产环境中学习。
Power BI 还是 Tableau?
我建议你选择其中一个并专注于它,而不是在两者之间分散注意力。
如果你主要的工作经验是在 Excel 中处理数据,那么 Power BI 对你来说可能更直观。一旦你学会了使用其中一个,学习另一个也会变得很容易,对于大多数通用分析师职位,招聘经理并不会特别在意,只要你能掌握其中之一。
我曾面试过一家大型企业的职位,负责开发 Tableau 仪表盘,我问招聘经理:“如果你雇佣我,你认为六个月后,我会是什么样的成功员工?”
他的回答是:“如果你在六个月后能编辑一个仪表盘,我会认为这是成功。”
和 SQL 一样,使用可视化工具时,挑战之一就是理解组织的数据。
你应该学什么?
-
制作所有标准图表
-
数据清理,以及如何将数据结构化以导入你选择的工具中
-
基础仪表盘制作
脚本语言

图片来源:Kevin Canlas 摄于 Unsplash
最后,我们有脚本语言。作为提示,我的前几个分析师职位并不要求我掌握脚本语言,但那是很久以前的事了。根据当前的应用需求,至少了解一些脚本语言现在已经成为入门级职位的要求。
R 还是 Python?
如果你已经掌握了 R(在统计学课程中学过),那么可以继续专注于 R,否则学习 Python。如果你已经精通其中一种语言,学习另一种语言的语法在你上手时就能轻松掌握。
R 语言通常在与学术界关系密切的组织中更为常见。生物技术公司更有可能使用 R,因为他们的研究人员在研究生阶段更有可能使用它。
你应该学习什么?
-
变量
-
基本的数值操作
-
字符串操作
-
条件语句(If/then)
-
基本数据结构(列表、字典、元组、集合)
-
循环
-
定义和使用函数
-
Pandas(是库,不是动物)
一般建议
-
你不需要成为这些主题的专家,你需要对它们有所了解。
-
对于初级分析师职位,主要集中在 Excel 上。
-
不要高估自己的技能水平。我曾面试过一位自称为“中级”Python 用户的候选人。虽然这个职位并不要求使用 Python,但既然他们说能做,我就给他们布置了一个现场编程测试。我要求他们定义一个函数,检测给定的字符串输入是否是回文(即正着和倒着拼写都相同的单词)。他们随后承认自己不知道如何定义函数。我礼貌地结束了面试。
证书

图片来源:Lewis Keegan 于 Unsplash
在大多数情况下,我认为证书对于获得初级职位并不是特别有用。它们可能在某些情况下有所帮助(也许你会获得本不该获得的招聘面试),但我不认为它们值得花费精力。
有一个例外:南亚的就业市场。
在评估候选人时,我确实使用了一些证书作为启发式方法。
通常这些证书有几个共同点:
-
它们来自像 Snowflake、Microsoft 或 Tableau 这样的主要数据技术公司。
-
获得这些证书需要花费数百美元,对于典型的南亚员工(或他们的雇主)来说,这是一个相当大的投资。
免费证书
有很多免费的或非常低价的证书,比如 Google 数据证书。总体而言,我认为它们的价值大致等同于你为它们支付的费用。学习内容扎实,课程结构完整,但证书本身并不会真正帮助你脱颖而出。
通过技术面试

图片来源:charlesdeluvio 于 Unsplash
当我面试候选人时,我真的希望他们成功,我怀疑大多数面试官也都是如此。
所以在面试时,保持对话式的方式。
我更感兴趣的是你如何得出正确答案,而不是你是否得出正确答案。我更倾向于候选人提出问题、测试想法并请求澄清。如果你走偏了,我会提问,看看能不能帮助你走回正轨。
资源

以下是我在学习这些技能时使用的主要付费资源。这些不是推荐链接,我不会从你购买中获得任何收益。
Excel
Tom Hinkle 是我亲爱的朋友,我强烈推荐他在 Udemy 上的课程。
Oz Du Soleil 是我最喜欢的在线讲师之一,也是个非常好的人: 我链接了他的 YouTube 频道,因为他在那里提供了很多内容。
如果你想学习 Power Query,skillwave 培训 非常出色。他们也有 Power BI 课程,尽管我没有上过。
SQL
IMDB 的实际数据库: 这是一个非常干净的数据集,可以让你在一个维度化建模的数据库中练习复杂的 SQL 查询。
Microsoft Contoso 数据库: 这个模拟了一个零售网站的数据库,可以让你在聚合和回答业务问题上得到很好的练习。
Tableau
Tableau 提供了使用其产品的最佳培训之一。我建议你学习他们的课程,而不是支付其他人的费用。
Python
Python 圣经: Ziyad 是一位非常引人入胜的在线讲师。
完整的 Pandas 训练营: Alexander Hagman 内容枯燥,但非常彻底。每当我需要复习 Pandas 时,我仍然参考这门课程。
一般
Anil 是我早期的导师之一,他后来创建了一个数字分析导师/教育平台。他曾在当地大学教授我,但他的工作非常出色,并且他对学生投入了大量的心血。
你认为我遗漏了哪些基础分析技能吗?
关于作者
Charles Mendelson 是一家三大管理咨询公司的高级软件工程师,帮助客户构建 AI 原型和 MVP。
他以自学数据分析师的身份开始了他的技术生涯,后来成为了一名数据工程师。
数据项目代码卓越的四个 R(第一部分)

图片由 Ideogram 提供,作者修改
如何编写出令人惊叹的代码,既能保障机器学习管道,又能维护你的理智!
·发表于Towards Data Science ·阅读时间:15 分钟·2024 年 3 月 19 日
--
介绍
任何成功的数据科学项目的关键成分之一就是高质量的代码。从简单的数据分析到复杂的机器学习管道,代码质量始终是确保项目准确性、效率和可维护性的最重要因素。编写良好的代码可以确保你的工作容易被他人理解、修改和扩展——包括你未来的自己。它能最大限度地减少错误和漏洞的可能性,并使数据和机器学习项目更加高效、有效和稳健。但编写高质量的代码并不总是那么容易,对吧?
我们都见过低质量的代码。而当我说见过时,我的意思是亲手写过!
你知道怎么做:你被分配了一项快速分析和概念验证建模任务。于是,你将数据集导入 CSV 文件,打开一个笔记本,创建了 42 个晦涩的单元格,如果你运行两次,它们就会给你报错。最后你得到了一本杂乱无章的笔记本,里面充斥着无数难以理解的函数名称、重写的变量、无法解读的图表,最终一片混乱,可能会导致…
未来是神经-符号的:人工智能推理如何发展
·发表于Towards Data Science ·阅读时间:7 分钟·2024 年 1 月 23 日
--
本文文本的语法、流畅性和可读性得到了人工智能软件的增强。
一个名为AlphaGeometry的卓越新型 AI 系统,最近成功解决了许多困扰大多数人类的高中级别的数学难题。通过将深度学习神经网络与逻辑符号推理相结合,AlphaGeometry 为发展更具人类思维特征的人工智能开辟了激动人心的方向。
AlphaGeometry 的关键创新在于其“神经-符号”架构,该架构融合了神经学习组件和形式化符号推理引擎。正如 DeepMind 的研究人员所解释的那样,像几何构造问题这样的定理证明任务需要同时具备(i) 灵活的神经创意思维,以推导隐含关系并引入新步骤,以及(ii) 逻辑严密的基于规则的推理,以可靠地建立合理推理的链条。
纯粹的神经网络或纯粹的符号 AI 单独无法解决如此多面的挑战。但是,两者结合,能够实现单一范式无法达到的显著协同效应。
数学定理证明的挑战
定理证明被认为是逻辑推理能力的终极考验。像几何构造这样的任务涉及多方面的挑战:
-
灵活的模式识别以揭示隐含的几何关系
-
创造性跳跃以引入辅助构造并扩展……
数据库的未来 — 图关系型数据库
多模型以及 ISO GQL 的标准即将发布...
·发表于 Towards Data Science ·6 分钟阅读·2024 年 5 月 22 日
--

版权免费图片,Dall-E 3 和 Victor Morgante
三年半前,当我写下《所有数据库都是多模型的》时,我其实是在瞎猜。我刚刚开始创办 FactEngine,并且有一个愿景,那就是每个数据库都可以看作是图数据库和关系数据库的结合体——一个多模型数据库。当时已经有一些多模型数据库存在,大型公司如 SQL Server 也开始涉足多模型世界。
快进三年,ISO(国际标准化组织)刚刚发布了一项新标准,ISO/IEC 39075:2024 —— 信息技术 —— 数据库语言 —— GQL……其中 GQL 代表图查询语言,标准设计旨在同时作为现有关系数据库的覆盖层,也作为未来图数据库的标准。多模型已成为主流。
读者们对这背后的数据库理论/数据科学和标准本身同样感兴趣。
经常阅读我博客的朋友们应该已经看过这张图片几次了,因此请耐心一下,我们将与更广泛的读者群体分享。这是一张展示关系数据库向属性图架构/图数据库结构演化的图片,显示了二者之间的同态关系:
生成式 AI 的未来是具代理性的:你需要了解的内容
在 LangChain、LlamaIndex、AWS、Gemini、AutoGen、CrewAI 和 Agent 协议中实施 AI 代理
·发表于 Towards Data Science ·16 分钟阅读·2024 年 4 月 30 日
--
免费链接 => 请帮忙点赞 这篇 Linkedin 帖子
1. 引言
像往常一样,我在这个周末帮助女儿做中文作业。我发现了一首诗,优美地反映了生成式 AI 代理的演变。这首诗的名字是《人有两件宝》,优雅地体现了大语言模型代理概念的核心。这首诗讲述了每个人拥有的两件宝物:我们的双手和头脑。
我们的双手代表了行动的能力——操控工具、制造物品、移动物体。我们的头脑象征着思考、推理、规划、反思和记忆的能力。
行动与反思的二元性在生成式 AI 代理技术中至关重要,正如我女儿的中文诗中所描述的手与脑的结合:
“如果仅凭双手来完成任务,
没有一些思考,要求就太高了。
而仅凭头脑,如果它们是我们唯一使用的,
没有双手,线索又有何用?
但若两者兼用,合力便能强大,
在和谐中,它们不会出错。
机器人组装的未来
自 1913 年大规模生产引入以来,生产线依然主要由人工操作——人形机器人可能会改变这一现状。
·发表于Towards Data Science ·阅读时长 12 分钟·2024 年 3 月 29 日
--

一位人形机器人在执行组装任务。图片由作者提供,来源于 miramuseai.net。
这是我在 2024 年 3 月 27 日为《Assembly Magazine》举办的网络研讨会的文字版。该网络研讨会也可以 在线观看。如果你不是 Medium 订阅用户,可以 免费阅读此文章 。高混合、低产量的问题仍然难以实现机器人自动化。通过回顾最近在协作机器人、人工智能和人形机器人领域的趋势,我认为组装和拆卸可能是人形机器人寻求工作机会的“杀手级应用”。
亨利·福特被誉为大规模生产的奠基人,他通过简化“T 型车”的生产流程,使汽车变得普及且价格亲民。当时的一项关键创新是使用输送带来控制生产线的节奏。然而,实际的劳动大多仍然是人工的,今天仍然如此,例如可以参考2024 年宝马的发动机组装。

亨利·福特的 T 型车工厂于 1913 年的大规模生产(公有领域)。许多组装步骤,特别是在发动机部门和最终组装阶段,依然是人工操作。
通过被称为德语词“Takt”或循环时间来控制生产线的节奏,的确是使组装过程顺利进行的关键理念……
生成式 AI 的优势:产品策略的差异化
导航变化中的产品格局:从炫目的演示到值得信赖的 AI 解决方案,赢得客户信任。
·发表于 Towards Data Science ·阅读时长:6 分钟·2024 年 2 月 1 日
--
几十年来,产品开发者一直遵循一个看似不可打破的规则:随着产品功能的增加,其复杂度也随之上升。对于用户来说,这通常意味着在简洁性和功能性之间做出选择。任何曾经与高级软件打交道的人都知道,在无数菜单和选项中寻找所需功能时的沮丧感!这是一个经典的权衡,直到现在,一直主导着用户体验。
但生成式 AI 的崛起有望打破这种权衡。
想象一下曾经的 Adobe Photoshop:虽然这款产品拥有令人惊叹的丰富设计功能,但其复杂性使得只有专家才能使用它。今天,借助生成式 AI,Photoshop 让用户能够通过简单的口语命令进行操作,比如“去除背景”或“让这张肖像通过更高的对比度更突出”。这展示了 AI 如何使强大的工具变得更加易于为每个人所用。
来源:Adobe YouTube 频道
通过解读自然语言命令,功能丰富的高级产品如今可以在不牺牲其复杂能力的情况下,提供更易于访问且更直观的用户体验。相反,面向新手用户的现有简化功能产品,通常偏向简单的用户体验,现在也能在不增加用户界面复杂性的情况下,提供全新的生成式 AI 功能深度。

来源:作者
这正是范式转变的本质——复杂性不再是能力的代价。
随着生成式 AI 重新定义产品设计格局,显而易见的是,拥有强大用户基础和领域专业知识的成熟公司处于领先地位。然而,成功远非板上钉钉。
你今天可以集成一个生成式 AI API,但你的护城河在哪里?
我这个月与六位产品负责人进行了交流。每个人都认为生成式 AI 的范式转变已经开启了一场竞赛。最终,必定会有赢家和输家。本文揭示了一些产品领导者正在利用生成式 AI 为客户提供差异化产品的关键策略。
1. 使用专有领域数据打造定制化体验
在生成式 AI 中,“一刀切”的方法并不适用于专业化的使用案例。通用的基础模型是基于互联网数据训练的,而这些数据缺乏行业特定的细微知识。
以大型视觉模型(LVMs)为例。LVMs 通常是基于互联网上的图像训练的——这些图像包括宠物、人物、地标和日常物品的照片。然而,许多实际的视觉应用(如制造、航空影像、生命科学等)使用的图像与大多数互联网图像完全不同。
来源:Landing.AI YouTube 频道
使用专有数据调整基础模型可以极大地提高性能。
“一个使用领域特定数据训练的大型视觉模型在行业特定的应用场景中,性能比通用模型提高了 36%到 52%。” — Andrew NG,创始人 DeepLearning.AI
文本类大语言模型(LLMs)的叙述方式类似。例如,彭博社使用专有的金融数据训练了一个 LLM,打造了BloombergGPT,它在大多数金融自然语言处理任务中优于同等规模的其他通用模型。通过使用专有的行业数据增强基础模型,企业可以开发出量身定制的生成式 AI,这种 AI 能够理解行业的细微差别,并提供差异化的体验,以满足用户的专业需求。
2. 解决 AI 的“最后一公里”挑战
尽管生成式 AI 模型具有令人印象深刻的能力,但它们距离足够可靠以应对大多数实际应用仍然相差甚远。这种“哇!”演示和可依赖的部署之间的差距,被技术人员称为“最后一公里”问题。生成式 AI 生成的是概率性输出,且有时容易产生幻觉。在许多业务、金融、医疗等高风险应用场景中,这都是一个值得关注的问题。随着生成模型能力的提升,确保公平性、透明度、隐私和安全性的实践变得愈发重要。
以下框架列出了领先公司正在优先关注的各种举措,以管理与其行业相关的“最后一公里”风险。

来源:作者
通过解决他们行业中特有的责任 AI 难题,企业可以成功将这些强大的技术整合到关键的实际应用中。领先的 AI 伦理将赢得用户信任,并获得竞争优势。
3. 通过持续学习和个性化提升产品
构建卓越的以人为本的用户体验的基石是设计那些不仅仅响应用户,而是与用户一起成长和适应的产品。领先的生成式 AI 产品将在用户与 AI 之间实施紧密的反馈循环,以实现持续学习并提供个性化体验。
“为了构建一个不断改进的 AI 服务,从最终用户目标出发。构建一个数据飞轮,持续捕获可操作的数据点,帮助你评估并改进 AI,以更好地实现这些目标。” —* AWS,董事
以 Grammarly 为例,这是一款旨在完善和提升用户写作的工具。它最近推出了生成式 AI 功能,为用户提供个性化的写作建议。
来源:Grammarly YouTube 频道
下面是 Grammarly 如何实现反馈循环以增强产品、与不同用户目标对齐的概念性分析:

来源:作者
成功的实施要求:
-
根据用户目标精心设计反馈循环
-
安全地记录使用数据并提供隐私保护
-
可扩展的数据基础设施以推动 AI 模型适应
-
测试界面以获取高质量的用户反馈
结果是,AI 会越来越符合个体需求——而不会妥协隐私。
优先考虑这些以人为本的反馈循环,创造出能通过真实用户互动持续改进的“活”产品。这种学习循环将成为核心竞争优势。
4. AI 优先设计:以 AI 为核心的全栈优化
实现生成式 AI 的全部潜力需要从根本上重新思考用户体验。将 AI 聊天机器人作为事后考虑加到应用中,不太可能提供一个统一的体验。这需要跨职能协作,并从一开始就重新思考整个产品堆栈中的交互。
“从第一性原理出发思考。如果你从一开始就拥有这些生成式 AI 能力,你会如何设计理想的体验?”” — Ideo,设计负责人
考虑一下一个自行车维修应用。一款允许用户上传自行车图片并接收文本说明的聊天机器人可以是一个不错的最小可行产品(MVP)。但理想的用户体验可能是一个视觉界面,在其中:
-
用户打开摄像头并将其对准自行车
-
计算机视觉分析视觉信息,突出自行车的相关部分
-
应用提供在实时摄像头画面上的引导说明

来源:作者
提供这种体验需要团队之间的协作:
-
用户体验设计: 计算机视觉 UI 的用户研究和原型设计
-
工程: 数据管道、高性能的 API、低延迟的缓存
-
数据: 早期构建训练/评估数据集,以促进改进
-
科学: 确保高精度的视觉模型,专为自行车修理量身定制
-
领域专家: 提供关于自行车修理的见解和反馈,优化 AI
通过在一开始就将这些视角融合在一起,产品能够提供流畅的人本用户体验。采用“AI 优先”设计思维和全栈产品优化的公司,将最有可能为客户提供差异化的价值。
结论:塑造未来的产品设计
随着生成性 AI 的普及,产品领导者面临着巨大的机遇——也有责任——来塑造其影响力。采取以人为本、遵循伦理的方式的公司,将赢得用户的信任。关键在于拥抱 AI,不仅仅是利用它的功能,更是看到它在增强人类创造力和积极转变用户体验方面的潜力。通过深思熟虑的实施,生成性 AI 可以扩展对复杂工具的访问,解锁新的个性化水平,并使产品能够持续从实际使用中学习。
通过将人类置于生成性产品设计的核心,具有前瞻性的公司可以与用户建立真实的连接,提供真正的差异化价值。这种人类与 AI 的共生关系是未来变革性产品体验的标志。
感谢阅读!如果这些见解引起了你的共鸣或激发了新的思考,欢迎继续交流。
在下方评论中分享你的观点,或通过 LinkedIn 与我联系。
德国坦克问题
通过抽样估计中奖概率
·发表于 Towards Data Science ·阅读时间 11 分钟·2024 年 3 月 6 日
--
统计估计真的很有趣,不是吗?通过从一个总体中随机抽取几个样本,你可以推断出这个总体的一些特性,比如均值或方差。同样,在合适的条件下,你也可以估计总体的总量,正如我在本文中将要展示的那样。
我将使用抽奖的例子来估计彩票的总数,从而计算中奖的概率。更正式地说,这意味着在给定离散均匀分布的情况下估计总体规模。我们将看到不同的估计方法,并讨论它们的差异和局限性。此外,我还会向你介绍这种方法可以应用于其他的一些案例。
玩彩票

我太焦虑,不敢乘坐那种东西,但如果你愿意的话…照片由 Oneisha Lee 提供,拍摄于 Unsplash
假设我去参加一个州际博览会并购买彩票。作为一名数据科学家,我自然想知道赢得大奖的概率。假设只有一张彩票能够赢得大奖。那么,为了估计中奖的可能性,我需要知道彩票的总数 N,因为我的中奖机会就是 1/N(如果我买了 k 张彩票,那就是 k/N)。但如果我只买了几张彩票(而且都没有中奖),我怎么估计这个 N 呢?
我将利用彩票上的号码这一事实,并假设这些号码是连续的(这意味着,我假设它们符合离散均匀分布)。假设我已经购买了一些彩票,它们按顺序的号码是[242,412,823,1429,1702]。那么,我现在知道的彩票总数是多少呢?显然,至少有 1702 张彩票(因为这是我到目前为止看到的最大号码)。这给了我彩票总数的第一个下限,但这对实际彩票总数的准确性有多大帮助呢?仅仅因为我抽到的最高号码是 1702,并不意味着还有比它更大的号码。我很难遇到我样本中最高号码的那张彩票。
然而,我们可以从数据中获得更多信息。让我们考虑以下思路:如果我们知道所有彩票的中间号码,我们就可以很容易地从中推算出总数量:如果中间号码是m,那么在这个中间号码下面有m-1张彩票,在这个中间号码上面也有m-1张彩票。也就是说,总彩票数将是(m-1) + (m-1) + 1,(其中的+1是指中间号码 m 本身),等于2m-1。我们虽然不知道中间号码 m,但可以通过我们样本的平均值或中位数来估算它。我的样本的(四舍五入后的)平均值是 922,这就得出了 2*922-1 = 1843。因此,通过这个计算,估算出的彩票总数是 1843。
到目前为止,这个过程相当有趣,因为仅凭几个彩票号码,我就能够估算出彩票的总数量。然而,你可能会想,这是不是我们能得到的最佳估计。让我马上告诉你:并不是。
我们使用的方法有一些缺点。让我通过另一个例子来演示这一点:假设我们有号码[12,30,88],这导致我们得到 2*43–1 = 85。这意味着,公式表明总共有 85 张彩票。然而,我们的样本中有号码 88,所以这个估计根本不成立!这个方法有一个普遍的问题:估算的 N 可能会低于样本中的最大号码。在这种情况下,这个估计就没有任何意义,因为我们已经知道样本中的最大号码是整体 N 的自然下限。
一种更好的方法:使用均匀间隔

这些鸟在电线上间隔得相当均匀,这是我们下一个方法的一个重要概念。照片由Ridham Nagralawala提供,来源于Unsplash
好的,那么我们能做什么呢?让我们换个角度思考。我购买的福利彩票票是从一个从 1 到未知的 N 的分布中随机抽取的。我的票中最大的数字是 1702,我想知道,这个数字离成为最大票还有多远。换句话说,1702 和 N 之间的差距是多少?如果我知道这个差距,我就可以轻松地根据它计算出 N。那么,我对这个差距了解多少呢?好吧,我有理由假设,这个差距预期和样本中两个连续票之间的其他差距一样大。第一张票和第二张票之间的差距,平均来说,应该和第二张票和第三张票之间的差距一样大,依此类推。除了随机偏差外,没有理由认为这些差距会比其他的更大或更小。我独立抽取了我的福利彩票票,因此它们应该均匀分布在所有可能的票号范围内。平均来说,0 到 N 范围内的数字看起来就像电线上停的鸟儿,它们之间的间隔相等。
这意味着我预计 N-1702 等于所有其他间隔的平均值。其他间隔为 242–0=242,412–242=170,823–412=411,1429–823=606,1702–1429=273,得出平均值 340。因此,我估计 N 为 1702+340=2042。简而言之,这可以用以下公式表示:

这里,x 是观察到的最大数字(在我们的例子中是 1702),k 是样本数量(在我们的例子中是 5)。这只是我们刚才计算平均值的一种简短形式。
让我们做一个模拟
我们刚才看到了两种对总彩票数量的估算。首先,我们计算了 2m-1*,得到了 1843;然后我们使用了更复杂的方法 x + (x-k)/k,得到了 2042。我现在想知道,哪个估算更准确呢?我的中奖概率是 1/1843 还是 1/2042?
为了展示我们刚才使用的估算方法的一些性质,我做了一个模拟。我从一个最大值为 2000 的分布中抽取了不同大小 k 的样本,并重复了几百次。因此,我们可以预期,至少在平均值上,我们的估算值也应该接近 2000。这是模拟的结果:

对于不同的估算值,随着 k 的变化,其概率密度分布。请注意,真实值 N 是 2000。图像来源:作者。
我们在这里看到了什么?在横轴上,我们看到 k,即我们抽取的样本数。对于每个 k,我们可以看到基于几百次模拟的两个公式的估算值分布。黑色的点表示每个模拟的均值,这个均值总是 2000,无论 k 的值是多少。这个点非常有意思:如果这两种估算方法重复无数次,它们都会收敛到正确的值。
然而,除了常规的平均值外,这些分布的差异还是挺大的。我们可以看到,公式 2m-1* 的方差较大,也就是说,它的估计值比另一个公式更常偏离真实值。尽管如此,方差随着 k 的增大有降低的趋势。不过,这种下降并不总是完美的,因为这只是一个模拟,仍然受到随机因素的影响。然而,这种趋势是可以理解并且是预期的:我取的样本越多,我的估计就越精确。这是统计估计中非常常见的性质。
我们还看到偏差是对称的,也就是说,低估真实值的可能性和高估真实值的可能性是一样的。对于第二种方法,这种对称性并不成立:虽然大部分密度位于真实均值上方,但下方有更多且更大的离群值。为什么会这样呢?让我们回顾一下我们是如何计算这个估计的。我们取了样本中的最大值,并在其基础上加上了平均间隙大小。自然,样本中的最大值只能和总体中最大的值(我们想要估计的 N)一样大。在这种情况下,我们将平均间隙大小加到 N 上,但我们无法得到比 N 更大的估计值。在另一个方向,最大值可能非常小。如果我们运气不好,可能会抽到样本[1,2,3,4,5],在这种情况下,样本中的最大值(5)离实际的 N 非常远。这就是为什么低估真实值的偏差可能比高估真实值的偏差更大的原因。
哪个更好?
从我们刚刚看到的情况来看,现在哪个估计更好呢?嗯,两个估计平均来说都给出了正确的值。然而,公式 x + (x-k)/k 的方差较小,这是一个很大的优势。这意味着,你更常接近真实值。让我给你演示一下。在接下来的图中,你将看到两个估计在样本大小 k=5 时的概率密度图。

对于 k=5 的两个估计的概率密度。曲线下方的彩色区域覆盖了从 N=1750 到 N=2250 的范围。图片由作者提供。
我用虚线标出了 N=2000(N 的真实值)这个点。首先,我们仍然可以看到之前已经见过的对称性。在左图中,密度在 N=2000 周围对称分布,而在右图中,密度向右偏移,且左侧尾部更长。现在让我们来看一下每个曲线下方的灰色区域。在两种情况下,这个区域都从 N=1750 到 N=2250。然而,在左图中,这个区域占曲线下方总面积的 42%,而在右图中,占总面积的 73%。换句话说,在左图中,你的估计有 42%的概率不会偏离真实值超过 250 点,而在右图中,这个概率是 73%。也就是说,你更有可能接近真实值。然而,你更有可能稍微高估而非低估。
我可以告诉你,x+ (x-k)/k是所谓的均匀最小方差无偏估计量,即它是方差最小的估计量。你不会找到任何方差更小的估计,因此一般来说,这是你可以使用的最佳方法。
用例

做爱,不做战争 💙。照片由Marco Xu提供,来源于Unsplash
我们刚才看到,如何在池中估算元素的总数,如果这些元素是由连续的数字表示的。从形式上讲,这是一个离散均匀分布。这种问题通常被称为德国坦克问题。在第二次世界大战中,盟军使用这种方法,通过仅仅使用他们已经摧毁或捕获的坦克的序列号,来估算德军拥有多少辆坦克。
现在我们可以想出更多可以使用这种方法的例子。包括:
-
如果产品上标有连续的序列号,你可以估算出该产品的生产数量。
-
如果你能够抽取一些用户或客户的 ID,你就可以估算他们的数量。
-
如果你能够抽取一些学生的注册号,你就可以估算出你所在大学的学生人数(或者曾经有多少学生)。前提是大学在达到最大注册人数后尚未重新使用之前的号码。
然而,要使用这种方法,需要满足一些条件。最重要的一点是,你必须确保你的样本确实是随机且独立的。如果你询问所有在同一年入学的朋友们他们的注册号,那么这些注册号不会在所有注册号范围内均匀分布,而是会聚集在一起。同样地,如果你从商店购买带有连续编号的商品,你需要确保这些商品是随机分配的。如果它们是从编号 1000 到 1050 的产品中获得的,那么你就不是从整个池中随机抽取的。
结论
我们刚才看到,如何在离散均匀分布下估算池中实例的总数。尽管两种估算方法在长期中给出的期望值相同,但它们在方差上有所不同,其中一种优于另一种。这很有趣,因为两种方法既不是完全正确也不是完全错误。它们都基于合理的理论考虑,并且在频率主义统计学术语中正确地估算了真实的群体大小。
我现在知道我赢得州博会彩票的概率估计为 1/2042 = 0.041%(如果我买了 5 张票,概率为 0.24%)。也许我应该把钱投资到棉花糖上;那样我就能安全地获胜了。
参考文献与文献
本文讨论的估算方法的数学背景可以在这里找到:
- Johnson, R. W. (1994). 估算一个群体的大小。教学统计,16(2),50–52。
你也可以查看关于德国坦克问题及相关主题的维基百科文章,它们非常具有解释性:
这是用于进行模拟并创建文章中所示图表的脚本:
import numpy as np
import random
from scipy.stats import gaussian_kde
import matplotlib.pyplot as plt
if __name__ == "__main__":
N = 2000
n_simulations = 500
estimate_1 = lambda sample: 2 * round(np.mean(sample)) - 1
estimate_2 = lambda sample: round(max(sample) + ((max(sample) - k) / k))
estimate_1_per_k, estimate_2_per_k = [],[]
k_range = range(2,10)
for k in k_range:
diffs_1, diffs_2 = [],[]
# sample without duplicates:
samples = [random.sample(range(N), k) for _ in range(n_simulations)]
estimate_1_per_k.append([estimate_1(sample) for sample in samples])
estimate_2_per_k.append([estimate_2(sample) for sample in samples])
fig,axs = plt.subplots(1,2, sharey=True, sharex=True)
axs[0].violinplot(estimate_1_per_k, positions=k_range, showextrema=True)
axs[0].scatter(k_range, [np.mean(d) for d in estimate_1_per_k], color="purple")
axs[1].violinplot(estimate_2_per_k, positions=k_range, showextrema=True)
axs[1].scatter(k_range, [np.mean(d) for d in estimate_2_per_k], color="purple")
axs[0].set_xlabel("k")
axs[1].set_xlabel("k")
axs[0].set_ylabel("Estimated N")
axs[0].set_title(r"$2\times m-1$")
axs[1].set_title(r"$x+\frac{x-k}{k}$")
plt.show()
plt.gcf().clf()
k = 5
xs = np.linspace(500,3500, 500)
fig, axs = plt.subplots(1,2, sharey=True)
density_1 = gaussian_kde(estimate_1_per_k[k])
axs[0].plot(xs, density_1(xs))
density_2 = gaussian_kde(estimate_2_per_k[k])
axs[1].plot(xs, density_2(xs))
axs[0].vlines(2000, ymin=0, ymax=0.003, color="grey", linestyles="dotted")
axs[1].vlines(2000, ymin=0, ymax=0.003, color="grey", linestyles="dotted")
axs[0].set_ylim(0,0.0025)
a,b = 1750, 2250
ix = np.linspace(a,b)
verts = [(a, 0), *zip(ix, density_1(ix)), (b, 0)]
poly = plt.Polygon(verts, facecolor='0.9', edgecolor='0.5')
axs[0].add_patch(poly)
print("Integral for estimate 1: ", density_1.integrate_box(a,b))
verts = [(a, 0), *zip(ix, density_2(ix)), (b, 0)]
poly = plt.Polygon(verts, facecolor='0.9', edgecolor='0.5')
axs[1].add_patch(poly)
print("Integral for estimate 2: ", density_2.integrate_box(a,b))
axs[0].set_ylabel("Probability Density")
axs[0].set_xlabel("N")
axs[1].set_xlabel("N")
axs[0].set_title(r"$2\times m-1$")
axs[1].set_title(r"$x+\frac{x-k}{k}$")
plt.show()
喜欢这篇文章吗? 关注我 以便收到我未来文章的通知。
数据中的“淘金热悖论”:为什么你的 KPI 需要重新思考

图片来源:Scottsdale Mint 于Unsplash
你做得可能没有你想象的那么好
·发表于Towards Data Science ·阅读时间:6 分钟·2024 年 11 月 5 日
--
不是 Medium 会员?在这里免费阅读 🚀
引言
在过去的几年里,我在数据工程领域观察到一个有趣的悖论。
一方面,数据和人工智能被誉为新石油。数据工程社区正以极快的速度增长。部分数据工程师已经“成名”,并报道了60 万美元的年薪。这肯定是取得了相当显著的成果…
尽管如此,数据仍然存在严重问题。在许多组织中,数据团队被视为一个成本中心,许多人在2022 年期间被裁员。数据重复或模型蔓延现象严重,治理问题也在上升。
企业几乎无法有效利用它们的数据,行业内的高层管理人员和领导们普遍认为,目前所构建的所有数据的质量相当低,显然还未达到 AI 所需的标准。
这就导致了“淘金热”悖论:
“淘金热悖论”概括了数据与 AI 之间的紧张关系——数据和 AI 被赋予的高价值(类似于现代的淘金热)与将数据真正转化为商业价值的巨大难题之间的矛盾。虽然有大量的资金和资源流入……
好的、坏的和丑的:神经网络的记忆
|人工智能|记忆|神经网络|学习|
记忆可能会耍花招;要学得最好,记忆并不总是有效的
·发表于Towards Data Science ·12 分钟阅读·2024 年 12 月 17 日
--

由作者使用 DALL-E 生成的图像
没有人有足够好的记忆力,能够成为一个成功的骗子。——亚伯拉罕·林肯
记忆比墨水更难忘。——安妮塔·卢斯
记忆不好,泛化好。 这是人工智能的一个教条。但为什么呢?记忆有什么问题?
直观地说,一个背下整本书的学生,如果考试中的练习题与书中的内容不同,可能仍然会失败。如果背诵并不等于学习,有时适当的记忆也有益。例如,学习复杂的规则来记住一串历史人物的名字是没有意义的。你必须知道如何找到合适的平衡。神经网络中也发生着类似的事情。本文讨论了神经网络与记忆之间复杂的爱/恨关系。
人工智能正在改变我们的世界,塑造我们的生活和工作方式。理解它如何运作以及它的影响,比以往任何时候都更加重要。 如果…
《研究生指南——我是如何资助(大部分)我的硕士学位的!》
这是关于华盛顿大学、西雅图的数据科学硕士项目的内幕!
·发表于Towards Data Science ·阅读时间:12 分钟·2024 年 5 月 8 日
--
⭐️ 如果你没有中等会员资格,你可以通过我的 朋友链接**访问本文!
对于那些认识我的人,你们可以‘跳到精彩部分’。但如果我们还没见过面……
嗨!我是 Ananya。我拥有华盛顿大学、西雅图的数据科学硕士学位,以及德里技术大学的信息技术本科学位。我在航空行业有两年的数据分析师经验,在机场运营领域创造了数据驱动的影响。下一站:航空航天。今年夏天(‘24),我将在 NASA 加利福尼亚州山景市的 Ames 研究中心担任 AI/ML 实习生!
我收到了很多关于数据科学硕士项目、资金选项以及美国大学生活的问题,因此我决定将所有这些见解和经验浓缩成这本迷你指南。不论你是考虑攻读硕士学位,还是已经身处其中,这可能正是你需要的工具,帮助你顺利度过研究生生活!
-
数据科学硕士项目
-
资助你的研究生学位
A. 助教职位
B. 研究助理职位
C. 校园工作(阅读员/批改员等)
-
课外活动
《平台重构的事后指南》
剖析软件“心脏移植”的动机、风险和潜在回报
·发表于 Towards Data Science ·阅读时间:16 分钟·2024 年 10 月 5 日
--

动力系统的演变 — 由 Midjourney v6.1 生成的 AI 图像
我正在为一个客户项目绘制架构建议,该项目在成功完成概念验证(POC)后,已经准备好进入生产软件的黄金阶段。我停下来问自己,为什么我所创建的新架构图与现有概念软件的架构看起来差异如此之大;即使我的答案似乎很有说服力(包括了可靠性、速度、规模和安全性的具体衡量标准),我还是摆脱不了一种不安的感觉,这种感觉似乎不太对劲。我开始回顾过去十五年中我直接负责(例如多个现代数据堆栈转换)或间接参与的重构项目(例如在“旧”系统和“新”系统之间的数据仓库协调)。结果我列出了十三个平台重构项目。在这十三个项目中,最终只有四个取得了积极的产品或工程成果;这意味着我亲历的重构项目中大约有70%的失败率。难怪每当涉及重写、重新架构或重新思考时,我都会立刻停下来深思。
平台重构是资源密集型、耗时的项目,可能决定高管的职业生涯,标志着一个组织增长的“曲棍球棒”拐点,或让组织陷入困境……
通过网络可视化展示健美的历史

使用 Python 和 Gephi 构建奥林匹亚先生获奖者共享领奖台图(1965–2023)。
·发表于 Towards Data Science ·5 分钟阅读·2024 年 5 月 30 日
--
除非标明来源,否则所有图片和代码由作者创建。
我已经对举重充满热情约十年了,所以是时候绘制这项运动的伟大传奇,特别是由阿诺德·施瓦辛格所象征的竞技健美,使用数据驱动的方法。因此,我首先收集了每届奥林匹亚先生比赛前三名的位置数据,并创建了获胜者的共享领奖台网络。这个网络,后来在 Gephi 中可视化,应该能够突出不同的时代及其在这项运动中的关键人物。
此外,本文旨在说明如何使用数据科学和网络可视化,揭示任何社交生态系统中的隐藏连接,无论其规模大小,来自体育或艺术的世界,具有小或巨大商业价值。
1. 数据准备
1.1. 收集数据
我通常从数据收集开始——比如编写爬虫或调用 API。然而,对于这个练习,解决方案异常简单……
卷积神经网络在图像分类中的历史(1989 年至今)
深度学习和计算机视觉领域最伟大创新的视觉之旅。
·发布于 Towards Data Science ·阅读时间 15 分钟 ·2024 年 6 月 28 日
--
在卷积神经网络出现之前,训练神经网络对图像进行分类的标准方法是将图像展平成像素列表,并通过前馈神经网络来输出图像的类别。展平图像的问题在于,它丢失了图像中至关重要的空间信息。
1989 年,Yann LeCun 和团队推出了卷积神经网络——过去 15 年来计算机视觉研究的基石!与前馈网络不同,卷积神经网络保持了图像的二维特性,并能够空间处理信息!
在本文中,我们将回顾卷积神经网络(CNN)在图像分类任务中的历史——从 90 年代的早期研究开始,到 2010 年代中期的黄金时代,那时许多最杰出的深度学习架构应运而生,最后讨论当前卷积神经网络研究的最新趋势,它们与注意力机制和视觉变换器展开竞争。
查看此 YouTube 视频 ,视频通过动画形象地解释了本文中的所有概念。除非另有说明,本文中使用的所有图像和插图均由我在制作视频版本时自行生成。

今天我们将讨论的论文!
卷积神经网络的基础
CNN 的核心是卷积操作。我们扫描滤波器穿过图像,并在每个重叠位置计算滤波器与图像的点积。这个输出结果称为特征图,它捕捉了滤波器模式在图像中出现的程度和位置。

卷积是如何工作的 — 卷积核在输入图像上滑动,并在每个位置计算重叠部分(点积)— 最终输出一个特征图!
在卷积层中,我们训练多个滤波器,从输入图像中提取不同的特征图。当我们将多个卷积层按顺序堆叠,并加入非线性激活函数时,就得到了卷积神经网络(CNN)。
所以,每一层卷积层同时完成两项任务 —
1. 空间滤波,即图像和卷积核之间的卷积操作,和
2. 结合多个输入通道并输出一组新的通道。
CNN 研究的 90%集中在修改或改进这两项内容。

CNN 的两大核心任务
1989 年论文
这篇 1989 年论文教我们如何通过反向传播从头开始训练非线性 CNN。它们输入 16x16 的灰度手写数字图像,并通过两层卷积层,每层有 12 个 5x5 的滤波器。滤波器在扫描过程中还以步长 2 进行移动。步长卷积对于降采样输入图像非常有用。经过卷积层后,输出的特征图会被展平并传递到两个全连接网络,输出 10 个数字的概率。通过 softmax 交叉熵损失函数,网络被优化以预测手写数字的正确标签。每一层后,还使用了 tanh 非线性激活函数——使得学习到的特征图更加复杂和富有表现力。这个网络仅有 9760 个参数,相比今天那些包含数亿个参数的网络,它是一个非常小的网络。

1989 年的原始 CNN 架构
偏向性归纳
偏向性归纳(Inductive Bias)是机器学习中的一个概念,指的是我们故意在学习过程中引入特定的规则和限制,以使我们的模型避免过度泛化,更加接近符合人类理解的解决方案。
当人类进行图像分类时,我们也会进行空间过滤以寻找共同的模式,形成多个表示,然后将它们结合起来做出预测。CNN 架构正是为了复制这一过程而设计的。在前馈网络中,每个像素都被视为独立的特征,因为层中的每个神经元都与所有像素相连——而在 CNN 中,由于相同的滤波器扫描整个图像,因此参数共享更多。归纳偏置使得 CNN 在数据需求上也更为节省,因为它们通过网络设计能够免费获得局部模式识别,而前馈网络则需要从头开始通过训练周期学习这些模式。
Le-Net 5(1998 年)

Lenet-5 架构(来源:Le-Net-5 论文)
1998 年,Yann LeCun 及其团队发布了Le-Net 5——一个更深、更大的 7 层 CNN 模型网络。他们还使用了最大池化(Max Pooling),通过从 2x2 滑动窗口中提取最大值来对图像进行下采样。


Max Pooling 如何工作(左侧)以及随着 CNN 增加更多层,局部感受野如何增大(右侧)
局部感受野
注意,当你训练一个 3x3 卷积层时,每个神经元都与原始图像中的一个 3x3 区域相连——这就是神经元的局部感受野——该神经元从中提取模式的图像区域。
当我们通过另一个 3x3 层传递这个特征图时,新的特征图会间接地创建一个更大的 5x5 区域的感受野,来源于原始图像。此外,当我们通过最大池化或步幅卷积对图像进行下采样时,感受野也会增加——使得更深层的网络能够更加全局地访问输入图像。
因此,CNN 中的早期层只能捕捉到低级细节,如特定的边缘或角落,而后续层则捕捉到更为广泛的全局级别的模式。
The Draught (1998–2012)
尽管 Le-Net-5 非常令人印象深刻,但 2000 年代初期的研究人员仍然认为神经网络在计算上非常昂贵且训练数据需求量大。另一个问题是过拟合——复杂的神经网络可能只是记住整个训练数据集,而无法对新的未见数据集进行泛化。研究人员因此转向传统的机器学习算法,如支持向量机,这些算法在当时较小的数据集上表现出了更好的性能,且计算需求远低于神经网络。
ImageNet 数据集(2009 年)
ImageNet 数据集在 2009 年开源——当时包含了 320 万张标注图像,覆盖了 1000 多个不同类别。如今,它已经拥有超过 1400 万张图像和超过 2 万个标注的不同类别。从 2010 年到 2017 年,每年都会举行一个叫做ILSVRC的大型比赛,研究小组们会发布模型,以打破 ImageNet 数据集子集上的基准。在 2010 年和 2011 年,传统的机器学习方法如支持向量机(SVM)获胜——但从 2012 年开始,比赛的焦点就转向了卷积神经网络(CNN)。排名不同网络的指标通常是 top-5 错误率——即衡量真实类别标签未能出现在网络预测的前 5 个类别中的比例。
AlexNet (2012)
AlexNet,由 Geoffrey Hinton 博士及其团队提出,是 2012 年 ILSVRC 的冠军,测试集的 top-5 错误率为 17%。以下是 AlexNet 的三大主要贡献。
1. 多尺度卷积核
AlexNet 在 224x224 的 RGB 图像上进行训练,并在网络中使用了多种卷积核大小——分别是 11x11、5x5 和 3x3 卷积核。像 Le-Net 5 这样的模型只使用了 5x5 卷积核。较大的卷积核计算开销更大,因为它们训练了更多的权重,但也能从图像中捕捉到更多的全局模式。由于这些大卷积核,AlexNet 拥有超过 6000 万个可训练参数。然而,所有这些复杂性可能会导致过拟合。

AlexNet 从较大的卷积核(11x11)开始,并为更深的层次减少卷积核大小(至 5x5 和 3x3)(图片来源:作者)
2. Dropout
为了缓解过拟合,AlexNet 采用了一种叫做 Dropout 的正则化技术。在训练过程中,每一层的部分神经元会被设为零。这可以防止网络过度依赖某些特定的神经元或神经元组来进行预测,从而鼓励所有神经元学习对分类有用的通用有意义特征。
3. RELU
AlexNet 还将 tanh 非线性函数替换为 ReLU。ReLU 是一种激活函数,它将负值变为零,保留正值不变。由于当 x 值过高或过低时,tanh 函数的梯度会变得很小,导致优化过程变慢,因此它通常会在深度网络中饱和。相比之下,ReLU 提供了稳定的梯度信号,使得训练速度比 tanh 快约 6 倍。



RELU、TANH,以及 RELU 带来的差异有多大(图片来源:中间:大数据中的人工智能,右侧:Alex-Net 论文)
AlexNet 还引入了局部响应归一化(Local Response Normalization)以及分布式 CNN 训练策略。
GoogleNet / Inception(2014)
2014 年,GoogleNet 的论文在 ImageNet 上取得了 6.67%的 Top-5 错误率。GoogLeNet 的核心组件是Inception 模块。每个 Inception 模块由不同滤波器大小(1x1、3x3、5x5)和最大池化层的并行卷积层组成。Inception 将这些卷积核应用于相同的输入,然后将它们连接起来,结合了低级特征和中级特征。

一个 Inception 模块
1x1 卷积
它们还使用了 1x1 卷积层。每个 1x1 卷积核首先对输入通道进行缩放,然后将它们结合在一起。1x1 卷积核通过与固定值相乘每个像素——这也是它被称为点卷积的原因。
虽然像 3x3 和 5x5 这样的更大卷积核同时进行空间过滤和通道组合,但 1x1 卷积核仅适用于通道混合,而且在权重数量较少的情况下非常高效。例如,一个 3x4 的 1x1 卷积层网格只需训练(1x1 x 3x4 =) 12 个权重——但是如果使用 3x3 卷积核,则需要训练(3x3 x 3x4 =) 108 个权重。


1x1 卷积核与更大卷积核(左)和使用 1x1 卷积核的降维(右)
降维
GoogleNet 使用 1x1 卷积层作为降维方法,在对这些低维特征图进行 3x3 和 5x5 卷积的空间过滤之前,先降低通道数量。这有助于减少可训练权重的数量,相比于 AlexNet,它大大减少了权重数量。
VGGNet(2014)
VGG 网络指出,我们不需要像 5x5 或 7x7 这样的更大卷积核,所需的只是 3x3 卷积核。两个 3x3 卷积层的感受野与单个 5x5 卷积层相同。三个 3x3 卷积层的感受野与单个 7x7 卷积层相同。
深度 3x3 卷积层捕捉到的感受野与更大卷积核相同,但参数更少!
一个 5x5 滤波器训练 25 个权重——而两个 3x3 滤波器只需训练 18 个权重。类似地,一个 7x7 滤波器训练 49 个权重,而三个 3x3 卷积层只需训练 27 个权重。使用深度的 3x3 卷积层长期以来成为了 CNN 架构的标准做法。

批量归一化(2015)
深度神经网络在训练过程中可能会遇到一个问题,称为“内部协方差偏移”。由于网络的早期层在不断训练,后续层需要持续适应从前面层接收到的不断变化的输入分布。
批归一化旨在通过在训练过程中将每层的输入归一化为零均值和单位标准差,从而抵消这个问题。批归一化或 BN 层可以应用于任何卷积层之后。在训练过程中,它会减去特征图在小批量维度上的均值,并除以标准差。这意味着每一层在训练过程中将看到一个更加平稳的单位高斯分布。
批归一化的优势
-
收敛速度约为原来的 14 倍
-
让我们使用更高的学习率,并且
-
使网络对初始权重具有鲁棒性。
ResNets (2016)
深度网络在做恒等映射时遇到困难
想象你有一个浅层神经网络,它在分类任务上表现出色。结果发现,如果我们在这个网络上加上 100 个新的卷积层,模型的训练准确性可能会下降!
这相当反直觉,因为这些新层只需要做的就是复制浅层网络每层的输出——至少能够匹配原始的准确性。实际上,深度网络在训练时往往非常难以调优,因为在通过许多层反向传播时,梯度可能会饱和或变得不稳定。在 Relu 和批归一化的帮助下,我们当时能够训练 22 层深的 CNN —— 微软的优秀团队在 2015 年推出了ResNets,使我们能够稳定地训练 150 层的 CNN。他们是如何做到的?
残差学习
输入通常会通过一个或多个 CNN 层,但在最后,原始输入会被加回到最终输出。这些块被称为残差块,因为它们不需要像传统意义上那样学习最终的输出特征图——它们只是必须加到输入上的残差特征,以便得到最终的特征图。如果中间层的权重变为零,那么残差块就会返回恒等函数——意味着它将能够轻松复制输入 X。

残差网络
简单的梯度流
在反向传播过程中,梯度可以通过这些快捷路径直接流向模型的早期层,从而更快地到达,有助于防止梯度消失问题。ResNet 将许多这样的块堆叠在一起,形成真正深的网络,而不会损失准确性!

凭借这一显著的改进,ResNet 成功训练了一个 152 层的模型,创造了打破所有纪录的 top-5 错误率!
DenseNet(2017)

Dense-Nets 也增加了连接早期层与后期层的捷径路径。DenseNet 模块训练了一系列卷积层,每一层的输出都会与模块中每个先前层的特征图拼接,之后再传递给下一层。每一层仅向网络的“集体知识”中添加少量的新特征图,随着图像在网络中的流动,DenseNet 的网络信息流和梯度流得到了改善,因为每一层可以直接访问来自损失函数的梯度。

Dense Nets
压缩与激励网络(2017)
SEN-NET 是 ILSVRC 比赛的最终获胜者,提出了将压缩与激励层引入到卷积神经网络(CNN)中。SE 模块旨在显式地建模特征图中所有通道之间的依赖关系。在普通的 CNN 中,特征图的每个通道是相互独立计算的;而 SEN-Net 采用类似自注意力的方法,使得特征图中的每个通道能够感知输入图像的全局特性。SEN-Net 赢得了 2017 年 ILSVRC 比赛的最终胜利,且其中一个包含 154 层的 SenNet + ResNet 模型,创下了令人惊讶的 top-5 错误率 4.47%。

压缩操作
压缩操作通过全局平均池化将输入特征图的空间维度压缩成一个通道描述符。由于每个通道包含捕捉图像局部特性的神经元,压缩操作将关于每个通道的全局信息进行积累。
激励操作
激励操作通过与来自压缩操作获得的通道描述符按通道进行乘法,重新调整输入特征图的尺度。这有效地将全局级别的信息传播到每个通道——使每个通道能够理解特征图中其他通道的上下文。

MobileNet(2017)
卷积层有两个功能——1)过滤空间信息,2)按通道合并信息。MobileNet 论文使用了深度可分离卷积,这是一种将这两种操作分开到两个不同层次的技术——使用深度卷积进行过滤,使用逐点卷积进行通道合并。
深度卷积

深度可分离卷积
给定一个具有 M 个通道的特征图集,首先,它们使用深度卷积层训练 M 个 3x3 卷积核。与普通卷积层对所有特征图进行卷积不同,深度卷积层训练的滤波器仅对每个特征图单独进行卷积。其次,它们使用 1x1 逐点卷积滤波器来混合所有这些特征图。像这样分开滤波和组合步骤极大地减少了权重数量,使得网络变得非常轻量化,同时仍保持性能。

为什么深度可分离层减少训练权重
MobileNetV2 (2019)
2018 年,MobileNetV2 通过引入两个创新——线性瓶颈(Linear Bottlenecks)和倒残差(Inverted residuals)——改进了 MobileNet 架构。
线性瓶颈
MobileNetV2 使用 1x1 逐点卷积进行降维,然后是深度卷积层进行空间滤波,接着再通过一个 1x1 逐点卷积层扩展通道回原来的维度。这些瓶颈不经过 RELU,而是保持线性。 RELU 会将降维步骤中产生的所有负值归零,这可能导致网络丢失重要信息,尤其是在这些低维空间大部分为负值时。线性层防止了在这一瓶颈过程中丢失过多信息。

每个特征图的宽度旨在显示相对的通道维度。
倒残差(Inverted Residuals)
第二个创新被称为倒残差。通常,残差连接发生在具有最高通道数的层之间,但作者在瓶颈层之间添加了捷径。瓶颈层捕捉了低维潜在空间中的相关信息,而这些层之间信息和梯度的自由流动是至关重要的。

视觉变换器 (2020)
视觉变换器(Vision Transformers,简称 ViTs)证明了变换器在图像分类任务中确实能够超越最先进的卷积神经网络(CNN)。变换器和注意力机制提供了一种高度可并行化、可扩展的通用架构,用于建模序列。神经注意力是深度学习中的一个完全不同的领域,本文不会涉及,但你可以在这个 YouTube 视频中了解更多。
ViTs 使用图块嵌入和自注意力
输入图像首先被分割成一系列固定大小的图块。每个图块都通过卷积神经网络(CNN)或经过一个线性层独立地嵌入成一个固定大小的向量。然后,这些图块嵌入和它们的位置编码作为一个令牌序列输入到基于自注意力的变换器编码器中。自注意力模型会建模所有图块之间的关系,并输出新的更新后的图块嵌入,这些嵌入能够理解整个图像的上下文。

视觉变换器。每个自注意力层通过图像的全局上下文进一步对每个块的嵌入进行上下文化。
归纳偏置与普适性
当 CNN 引入了多个关于图像的归纳偏置时,变换器则相反——没有局部化,没有滑动卷积核——它们依赖于普适性和原始计算来建模图像中所有块之间的关系。自注意力层允许图像中所有块之间的全局连接,无论它们在空间上相距多远。归纳偏置在较小的数据集上表现良好,但变换器的优势在于大规模训练数据集,最终,一个通用的框架将战胜 CNN 所提供的归纳偏置。

卷积层与自注意力层
ConvNext — 2020 年代的卷积网络 (2022)
在这篇文章中加入 Swin Transformers 是个很好的选择,但那是另一个话题!因为这是关于 CNN 的文章,让我们集中讨论最后一篇 CNN 相关的论文。

像 VITs 一样对图像进行分块
ConvNext 的输入遵循一种受视觉转换器启发的图像分块策略。一个 4x4 的卷积核,步幅为 4,创建了一个下采样的图像,并输入到网络的其余部分。
深度可分离卷积
受 MobileNet 启发,ConvNext 使用深度可分离卷积层。作者还假设深度卷积类似于自注意力中的加权和操作,这种操作在每个通道上进行,仅在空间维度上混合信息。此外,1x1 的逐点卷积类似于自注意力中的通道混合步骤。
更大的卷积核尺寸
自 VGG 以来,CNN 一直在使用 3x3 的卷积核,而 ConvNext 提出了更大的 7x7 滤波器,以捕捉更广泛的空间上下文,尽量接近 ViTs 所捕捉的完全全局上下文,同时保留 CNN 的本地化特性。
还有一些其他的改进,比如使用受 MobileNetV2 启发的反向瓶颈、GELU 激活函数、使用层归一化而非批量归一化等,这些都塑造了 ConvNext 架构的其余部分。
可扩展性
ConvNext 通过深度可分离卷积提供了更高的计算效率,并且在高分辨率图像上比变换器更具可扩展性——这是因为自注意力在序列长度上按平方级别扩展,而卷积则不受此限制。

最终思考!
卷积神经网络(CNN)的历史教会了我们很多关于深度学习、归纳偏置以及计算本质的知识。最终谁会胜出是很有趣的——是卷积网络的归纳偏置,还是变换器的普适性。一定要查看配套的 YouTube 视频,以便直观地了解这篇文章及下列列出的相关论文。
参考文献
带反向传播的卷积神经网络(CNN with Backprop)(1989 年):yann.lecun.com/exdb/publis/pdf/lecun-89e.pdf
LeNet-5:vision.stanford.edu/cs598_spring07/papers/Lecun98.pdf
AlexNet:proceedings.neurips.cc/paper_files/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf
GoogleNet:arxiv.org/abs/1409.4842
批量归一化(Batch Norm):arxiv.org/pdf/1502.03167
ResNet:arxiv.org/abs/1512.03385
DenseNet:arxiv.org/abs/1608.06993
MobileNet:arxiv.org/abs/1704.04861
MobileNet-V2:arxiv.org/abs/1801.04381
视觉变换器(Vision Transformers):arxiv.org/abs/2010.11929
ConvNext:arxiv.org/abs/2201.03545
挤压与激励网络(Squeeze-and-Excitation Network):arxiv.org/abs/1709.01507
Swin Transformers:arxiv.org/abs/2103.14030
数据协作的重要性
请求反馈是数据工作中的一种悄然强大的工具。让我们来谈谈为什么以及如何做得好。
·发表于Towards Data Science ·阅读时间 6 分钟·2024 年 5 月 17 日
--

照片来自Priscilla Du Preez 🇨🇦于Unsplash
最近与一位数据从业者的对话激发了我一个想法,我今天想分享。你进行数据分析或建模的过程是怎样的?你认为哪些是重要的,但可能没有被充分重视的部分呢?在我们交谈时,我意识到,在工作过程中获得其他人的反馈是我工作流程中的一个非常重要的部分,但实际上在我看来,这并不是初级从业者被明确告知的内容。我认为,解释我如何做这件事、为什么这样做以及它的好处,对那些工作流程中没有协作或同行反馈环节的人来说,会非常有用。
我认为这种情况特别重要的项目是那些大部分工作是在孤独中完成的,你孤身一人面对数据和自己的智慧。这通常包括建模工作,当然,还有那些旨在回答特定商业问题或探索研究主题的分析工作。大部分工作将由你独自完成,这可能很不错,因为你可以按自己的节奏工作,按照自己想要的方式探索项目领域。然而,当只有你一个人查看工作内容时,很容易分心、失去方向,或错过一些重要的细节。
因此,我喜欢在项目进行过程中不时与其他数据科学家进行咨询。这可以采取多种形式,而且对于每个项目来说,形式可能不同。
休闲聊天
不论你得到的是哪种反馈,重要的是你已经完成了相当多的工作。这不是让别人帮你做工作的时机。你应该有一个扎实的项目计划,并且已经取得了显著的进展(例如加载数据、仔细查看数据、进行至少部分分析,至少要有可以展示的工作成果),然后才开始引入其他人。如果你做好了这些,联系一位同事,看看他们是否有时间看一下你的工作并给出反馈——不一定是此时此刻,而是他们日程允许时。如果他们无法提供帮助,就去找另一个人,并且在心里记下这件事。担心打扰别人是很自然的,这也是为什么我通常会限制每个项目只进行一次这样的反馈请求,并且尽量避免连续多次找同一个人,特别是在不同的项目中。将请求分散开来,不要总是占用一个人的时间。如果你需要更多人帮忙,那是下一个部分的内容。
还需要注意的是,向他人寻求反馈的方式应该是有结构的。不要仅仅说“你觉得怎么样?”或者把一大段脚本扔给别人,而是要准备简洁、易读的内容,并提出具体的问题。“这是草稿幻灯片。你觉得这个 EDA 完整吗?你能看到我做的有哪里不合理吗?”或者“我尝试了不同的超参数组合,但模型的表现超过 X 后就不再提高了。你觉得这合理吗,还是我应该尝试别的方式来提高?”正如你可能猜到的,这意味着你需要展示结果,可能是一些可视化图表或至少一些数据表。
如果你问某个人,他们没有给出很有帮助的反馈,也要在心里记住这一点——也许他们很忙,或者你的问题不够清晰,亦或是你的工作难以阅读。尝试了解不同的评审者和同事最容易处理的方式,并且不断提升你提问的技巧。你也应该主动提出以后回报他们,给予反馈,并且在合适的时候执行并做好这项工作。正如大多数工作或生活中的关系一样,你从中得到的,正是你投入的。
模型评审
模型评审可以是一个项目的高潮,但它也可以是报告或模型完成并投入生产之前的一个重要步骤。让我先解释一下我所说的模型评审的含义,因为这并不一定是每个人流程的一部分。
模型审查是一个展示文档,包含可视化内容,解释你从头到尾完成的项目(通常是模型训练)。你应该解释你的数据,数据是如何收集的,数据的含义,如何清洗数据,如何选择模型架构并训练模型,你尝试过哪些没有成功的事情,最终模型的表现如何,在哪些方面表现出色,在哪些方面存在困难,有哪些事情你没有做但将来会做,等等。这应该是对你所做工作的全面讨论。你的听众应该是其他熟悉该领域的数据科学家或工程师,如果他们对该领域不熟悉,你也应当进行相应的解释。
这听起来可能是一项艰巨的工作,尤其是当你可以跳过这一步,简单地在 wiki 上记下几点笔记,然后把模型投入生产环境时,但我坚信这是成功建模的一个重要部分。首先,准备这份文档本身就要求你回顾你所做的工作并对其进行思考。知道这一点将激励你记录自己的工作并保持建模日志,就像科学课上的实验日志一样。当你在追踪进展时,你会做得更加有条理且高效。
此外,制作一个高质量的模型审查报告将为你提供一个机会,从许多同行那里一次性获得建议和反馈。你的团队应该有一些基本规则,确保反馈是建设性的、友善的,并且出于良好的意图,但如果你遵守这些规则,那么从进行模型审查中学到的东西将是无价的。你可能还会通过示范教学其他人!这是团队中每个人都可以观察他人流程和想法的机会,而不是这些见解仅仅隐藏在 Slack 的 DM 线程或 1:1 的 Zoom 通话中。
参加模型审查时,要预期工作并不完美,而且你会从中获得新的尝试、需要做出的更改或需要调查和解答的问题。重点不是获得赞扬,而是获得你可能忽视的洞察,这样到最后,你的模型会更好。
其他好处
显然,向同行征求反馈的最终目标是获取能够使你的工作更好的建议,无论是对当前项目还是未来的项目。但我认为有一些不那么显而易见的好处值得一提。首先,如果你做得对,你将会建立起一个想要学习和改进的声誉——接受反馈时要心怀感激,应用它,并展现出你正在内化这些有价值的意见。你的工作会变得更好,而且也会变得更加显眼,尤其是在同行和经理面前。
第二,你会变得更擅长表达你的想法。即使你没有进行完整的演示模型评审(尽管我认为你应该这么做),能够将自己的工作清晰地向同行解释并提出明确的问题也是一种技能。随着我们职业生涯的发展,能够清晰地沟通技术话题变得越来越重要,而在非正式、低压力的情况下练习这一点会对你有益。
第三,反馈会变得更容易接受。我知道很多人,包括我自己,有时会对接收反馈感到焦虑。当别人评估你的工作时,很容易觉得那些反馈令人生畏,或者只听到负面的评价,而忽略了积极的部分。但你做得越多,接受反馈就会变得越容易。在一个充满积极性和建设性的文化中反复请求反馈,会使你不再对批评或改进方法产生敏感,并且你会变得更擅长吸收并从中学习。
所以,当你进行数据工作时,要刻意创造机会,请求建设性的建议以改进你的成果。如果你没有数据方面的同行,加入数据科学家的聚会或 Slack 小组,在那里你可以请求建议(当然,前提是不违反任何保密协议),并建立一个你信任的人脉网络,同时也为他们提供反馈。我保证,你会在职业和技能上看到好处,更不用说你的模型和数据分析了。
阅读更多我的作品,访问www.stephaniekirmer.com.
此外,你还可以看到我在旧金山 6 月 25 日的 AIQ 大会上演讲,讨论在构建机器学习解决方案时的数据隐私问题。
人工智能中的记忆:关键好处与投资考虑
记忆将成为显著提高人工智能系统性能的关键因素——无论是在准确性还是效率方面
·发表于 Towards Data Science ·5 分钟阅读·2024 年 6 月 18 日
--
就像人类依赖记忆做出明智的决策和得出合乎逻辑的结论一样,人工智能也依赖其检索相关信息、理解上下文和从过去经验中学习的能力。本文深入探讨了为什么记忆对人工智能至关重要,探索了它在回忆、推理和持续学习中的作用。

色彩斑斓的大脑与代表记忆的微芯片 来源:DALLE3
记忆在回忆中的作用
有些人认为扩大上下文窗口将提升模型性能,因为这使模型能够摄取更多信息。虽然在某种程度上这是正确的,但我们目前对于语言模型如何优先考虑上下文的理解仍在发展。事实上,研究表明,“当相关信息出现在输入上下文的开头或结尾时,模型性能最高。”[1] 上下文窗口越大,我们越有可能遇到著名的“迷失在中间”问题,即由于重要信息被埋藏在中间,模型无法回忆起特定的事实或文本[2]。
为了理解记忆如何影响回忆,考虑人类是如何处理信息的。当我们旅行时,我们会被动地听到很多公告,包括航空公司广告、信用卡优惠、安全简报、行李领取信息等。我们可能没有意识到我们吸收了多少信息,直到我们需要回忆相关内容时。例如,如果一个依赖于检索相关信息来回答问题的语言模型,而不是依赖固有知识,被问到“紧急迫降时我该怎么做?”时,它可能无法回忆出回答这个重要问题所需的相关细节,因为检索了太多的信息。然而,拥有长期记忆的模型可以存储和回忆最关键的信息,从而在适当的上下文中进行更有效的推理。
记忆在推理和持续学习中的作用
记忆提供了重要的上下文,使模型能够理解过去解决问题的方法,识别哪些方法有效,哪些需要改进。它不仅提供了重要的上下文;还使模型能够回忆出先前用于解决问题的方法,识别成功的策略,并找出需要改进的领域。这一改进反过来有助于模型在复杂的多步骤任务中进行有效的推理。如果缺乏足够的推理能力,语言模型将难以理解任务,无法逻辑性地思考目标,解决多步骤问题,或者使用适当的工具。你可以阅读我上一篇文章中关于推理和高级推理技术的重要性的内容,点击这里。
以手动在公司数据仓库中查找相关数据为例。那里有成千上万的表格,但由于你理解需要哪些数据,因此可以专注于一个子集。经过数小时的搜索,相关数据被找到了,分布在五个不同的表格中。三个月后,当数据需要更新时,必须重复搜索过程,但你可能记不起曾用过的 5 个源表格来生成这个新报告。手动搜索过程再次重复。如果没有长期记忆,语言模型可能会以相同的方式解决这个问题——凭借蛮力——直到找到相关数据来完成任务。然而,配备了长期记忆的语言模型可以存储初始的搜索计划、每个表格的描述以及基于每个表格的搜索结果所修订的计划。当数据需要更新时,它可以从之前成功的方法开始,从而提高效率和性能。
这种方法使系统能够随着时间的推移不断学习,持续修订任务的最佳方法,积累知识,从而生成更高效、性能更强的自主系统。
评估将长期记忆纳入您的 AI 解决方案的投资
将长期记忆纳入 AI 系统中,可以显著提升其能力,但是否值得为这种能力进行必要的开发投入,需要慎重考虑。
1. 理解任务的性质
-
复杂性和持续时间:如果您的任务涉及复杂的多步骤过程或需要长期保存信息,长期记忆可以提高效率和准确性。例如,项目管理应用中,任务可能跨越数月,通过 AI 的记忆和从之前的上下文和迭代中获得的适应能力,可以带来益处。
-
上下文敏感性:依赖于上下文理解的任务,如客户服务互动、营销个性化或医疗诊断,可以利用长期记忆提供更个性化的响应。例如,IT 帮助台助手可以从记住客户是否曾遇到过这个问题以及如何在之前的互动中解决中获益。
2. 评估数据的量和变化性
-
高数据量:如果您的应用需要定期引用大量数据,长期记忆可以避免重复处理相同的信息,从而节省时间和计算资源。
-
数据变化性:在数据频繁变化的环境中,长期记忆有助于保持 AI 更新最新信息,从而确保更准确的输出,而无需重新训练。
3. 评估成本效益比
-
平衡成本与性能收益:实施长期记忆可能需要大量资源,并且随着记忆的积累,需求将不断增长。重要的是权衡数据存储的财务投资与可能的性能提升之间的关系。对于小型企业或资源有限的应用,具备长期记忆的小型语言模型(SLMs)可能提供一个更为平衡的解决方案。
-
竞争优势:通过提高 AI 应用的效率和效果,长期记忆可以提供显著的竞争优势,使企业能够提供比那些使用传统无记忆功能模型的企业更优质的服务。
4. 解决安全性和合规性问题
- 数据隐私:长期记忆涉及存储更多数据,这可能引发隐私问题。确保您的系统符合数据保护规定,并且敏感信息遵循最佳安全实践。
本质上……
将长期记忆融入 AI 系统为提升其能力提供了重大机会,通过提高准确性、效率和上下文理解能力。然而,决定是否投资这一能力需要仔细考虑并进行成本效益分析。如果战略性地实施,长期记忆的引入能够为你的 AI 解决方案带来实际的好处。
有问题或者认为某些内容需要进一步澄清吗?请在Linkedin上给我发私信!我总是渴望参与深度思考并对我的工作进行迭代。我的工作并不代表我雇主的观点。
嵌入与嵌入模型的工作原理
·发布于Towards Data Science ·作为时事通讯发送 ·3 分钟阅读·2024 年 3 月 7 日
--
准备好深入探讨一个及时的技术话题了吗?我们希望是的,因为本周的《Variable》将带你进入嵌入(embeddings)的迷人世界。
嵌入和嵌入模型是近年来涌现的强大 AI 工具的重要构建块,这使得数据科学和机器学习从业者在这一领域提高流利度显得尤为重要。即使你过去曾经探讨过嵌入,也永远不算晚扩展你的知识,了解新兴的方法和应用场景。
本周的亮点内容涵盖从相对高层次到非常细致的内容,从理论到极其实践的内容。不论你对嵌入的了解有多少,我们相信你都会在这里找到一些引起你兴趣的东西。
-
如何为你的 RAG 找到最佳的多语言嵌入模型正如Iulia Brezeanu强调所说,“除了拥有高质量的数据,选择一个好的嵌入模型是优化你的 RAG 应用程序中最重要且被低估的一步。”跟随她通俗易懂的指南,了解如何为你的项目做出最佳选择。
-
OpenAI 与开源多语言嵌入模型的对比为了从另一个角度了解当前多语言嵌入模型领域的选择,我们强烈推荐Yann-Aël Le Borgne的文章,其中详细对比了 OpenAI 最新一代嵌入模型与其开源对应模型的性能。
-
如何从数据中创建强大的嵌入,以供你的 AI 使用回顾模型选择问题,Eivind Kjosbakken深入分析了将数据“从图像、文本和音频等格式转换为强大嵌入,并用于机器学习任务”的不同方法。

-
统计方法 scDEED 检测可疑的 t-SNE 和 UMAP 嵌入,并优化超参数在最新的论文中,Jingyi Jessica Li 和 Christy Lee 带领我们了解了从高维到二维空间投影中数据失真问题,并提出了在二维降维方法中优化超参数设置的框架。
-
使用 AI 编辑图像中的文本场景文本编辑——在图像中调整文本元素——是一个令人惊讶的复杂任务。Julia Turc分享了研究人员在这一领域取得的最新进展,并扩展了嵌入模型在 STE 模型架构中的作用。
-
增强推荐系统多样性的现实世界新方法为了更具体地展示嵌入模型的强大能力,我们推荐Christabelle Pabalan的最新文章。文章解析了提升推荐系统输出多样性的难题,并展示了如何选择合适的嵌入模型,成为实现非常有前景的结果的关键一步。
对于本周想探索其他话题的读者,我们非常高兴推荐一些我们最近的亮点:
-
为了他们的 TDS 首秀,Skylar Jean Callis 分享了关于视觉变换器(Vision Transformers, ViT)的全面技术讲解,并附带了一个 PyTorch 实现。
-
跟踪 Maarten Grootendorst 的关于 Mamba(新型状态空间模型架构)的深度解析,保持与最新的 ML 研究同步,Mamba 旨在成为变换器的替代方案。
-
在一篇引人深思的文章中,Louis Chan 反思了一些常见的数据科学家与工程师之间的紧张关系来源(以及团队可以做些什么来缓解这些问题)。
-
随着近年来模型规模的膨胀,模型压缩的重要性也同步增长。Nate Cibik 刚刚发布了一系列精简方法的优秀文章,第一部分专注于剪枝。
-
好奇想了解模拟的强大力量吗? Hennie de Harder 的新解释文章聚焦于蒙特卡罗方法及其如何帮助解决复杂问题。
-
历史爱好者,欢呼吧:Sachin Date 刚刚发布了他关于关键数学概念起源的系列文章的最新一篇,这次聚焦于皮埃尔-西蒙·拉普拉斯与中心极限定理。
感谢你支持我们作者的工作!如果你受到启发,想加入他们的行列,何不写下你的第一篇文章?我们期待阅读它。
直到下一个变量,
TDS 团队
地理编码与艺术的交集:利用开放街图和 Networkx
使用 Python 绘图和网络指标分析城市街道地图
·发布于 Towards Data Science ·8 分钟阅读·2024 年 1 月 16 日
--

图片由 Logan Armstrong 提供,来源于 Unsplash
我可以花上好几个小时,试图说服一群人,为什么我的城市(纽约)是世界上最好的城市。然而,我最近意识到,我们对城市的看法是根据我们自身的评估标准而变化的。城市可以从多个方面进行评估,包括天气、食物、夜生活、景点、步行便利性、自行车便利性、公共交通质量、体育队伍、接触大自然的机会等。
在最近与一些同事和朋友的讨论中,有人表示交通便利性应该比天气更加重要,而其他人则反驳说,只要有足够多的活动可以做,他们就能忍受刺骨的寒风。对城市进行排名或分层是一个高度主观的活动,部分原因在于应考虑的因素模糊不清,但也因为城市可以从不同的背景下进行评估:是为了居住,还是为了周末旅行,或是为了度过一周的假期。就个人而言,尽管我一生中曾在四座城市居住过,但我发现随着我的优先事项变化,我自己对城市的排名也会略有变化。例如,我会更高评价波士顿作为居住地,而不是作为旅游地,而我对迈阿密的评价则…
AI 系统中记忆与基础构建的交集
·发表于 Towards Data Science ·阅读时间:6 分钟·2024 年 7 月 24 日
--
了解记忆的四个关键类型(短期记忆、短期长期记忆、长期记忆和工作记忆)、语言模型的基础构建方法以及记忆在基础构建过程中的作用。
在语言模型和智能体 AI 的背景下,记忆和基础知识是两个热门且新兴的研究领域。尽管它们通常在句子中紧密相连且常常相关,但在实际应用中,它们的功能是不同的。在本文中,我希望能澄清这两个术语之间的混淆,并展示记忆在模型的整体基础构建中所起的作用。

来源:Dalle3,描述:分割的大脑部分呈现记忆和基础构建,以友好的卡通风格展现
语言模型中的记忆
在我上一篇文章中,我们讨论了记忆在智能体 AI 中的重要作用。语言模型中的记忆指的是 AI 系统保持和回忆相关信息的能力,这有助于其推理能力,并能不断从经验中学习。记忆可以分为 4 类:短期记忆、短期长期记忆、长期记忆和工作记忆。
听起来很复杂,但让我们简单地拆解一下:
短期记忆(STM):
STM(短期记忆)保留信息的时间非常短,可能是几秒到几分钟。如果你问一个语言模型一个问题,它需要保持你的消息足够长的时间,以便生成对你的问题的回答。就像人类一样,语言模型也难以同时记住太多的事物。
米勒定律指出,“短期记忆是记忆的一个组成部分,它在一个活跃、随时可用的状态下保持少量信息,通常为几秒钟到一分钟。短期记忆的持续时间似乎在 15 到 30 秒之间,短期记忆的容量是有限的,通常认为大约为 7±2 项。”
所以,如果你问一个语言模型“我在之前的消息中提到的那本书是哪种类型?”它需要使用它的短期记忆来参考最近的消息并生成相关的回应。
实现:
上下文存储在外部系统中,例如会话变量或数据库,这些系统保存对话历史的一部分。每次新的用户输入和助手回应都会附加到现有上下文中,形成对话历史。在推理过程中,上下文会与用户的新查询一起发送给语言模型,以生成一个考虑到整个对话的回应。此研究论文提供了关于支持短期记忆机制的更深入的观点。
短期长时记忆(SLTM):
短期长时记忆(SLTM)保留信息的时间为中等时长,从几分钟到几个小时。例如,在同一会话中,你可以继续未完成的对话,而无需重复上下文,因为它已经作为 SLTM 被存储。这个过程也是外部过程,而不是语言模型本身的一部分。
实现:
会话可以通过标识符进行管理,这些标识符将用户的互动在时间上连接起来。上下文数据以一种方式存储,使其可以在定义的时间段内跨用户交互持久化,比如存储在数据库中。当用户恢复对话时,系统可以从之前的会话中检索对话历史,并在推理时将其传递给语言模型。就像短期记忆一样,每次新的用户输入和助手回应都会附加到现有的上下文中,以保持对话历史的更新。
长期记忆(LTM):
长期记忆(LTM)保留信息的时间为管理员定义的时间段,这段时间可以是无限的。例如,如果我们要构建一个 AI 导师,语言模型需要理解学生在哪些科目上表现好,在哪些方面仍然挣扎,哪种学习方式最适合他们,等等。这样,模型可以召回相关信息,以指导未来的教学计划。松鼠 AI就是一个使用长期记忆来“制定个性化学习路径,进行有针对性的教学,并在需要时提供情感干预”的平台示例。
实现:
信息可以存储在结构化数据库、知识图谱或文档存储中,按需查询。相关信息基于用户当前的互动和历史记录进行检索。这为语言模型提供了上下文,该上下文随着用户的回应或系统提示返回。
工作记忆:
工作记忆是语言模型本身的一个组成部分(不同于其他类型的记忆,它们是外部过程)。它使语言模型能够保持信息、操作信息并进行改进——从而提高模型的推理能力。这一点非常重要,因为当模型处理用户的请求时,它对任务的理解及执行任务所需采取的步骤可能会发生变化。你可以把工作记忆看作是模型思维的草稿本。例如,当提供一个多步骤的数学问题,如(5 + 3) * 2 时,语言模型需要能够计算括号中的(5 + 3)并存储该信息,然后再将两数之和乘以 2。如果你有兴趣深入了解这个话题,论文《TransformerFAM: 反馈注意力是工作记忆》提供了一种新的方法来扩展工作记忆,使语言模型能够处理无限长度的输入/上下文窗口。
实现:
像变换器中的注意力层或循环神经网络(RNNs)中的隐藏状态这样的机制负责保持中间计算,并提供在同一推理会话中操作中间结果的能力。随着模型处理输入,它更新内部状态,这使得推理能力更强。
所有 4 种类型的记忆都是创建一个能够有效管理和利用跨越不同时间框架和上下文信息的人工智能系统的重要组成部分。

人工智能系统中的记忆类型表,来源:Sandi Besen
基础
语言模型的回应应始终在对话的上下文中合理——它们不应仅仅是一堆事实陈述。基础性测量模型生成一个在上下文中相关且有意义的输出的能力。对语言模型进行基础化的过程可以是语言模型训练、微调和外部过程(包括记忆!)的组合。
语言模型训练与微调
模型最初的训练数据会在很大程度上影响模型的基础性和表现。通过在大量多样化的数据语料库上训练模型,可以让其学习语言模式、语法和语义,从而预测下一个最相关的词语。预训练的模型随后会在特定领域的数据上进行微调,这有助于其为需要更深入领域知识的特定应用生成更相关和准确的输出。如果你要求模型在某些文本上表现良好,而这些文本在初始训练中没有涉及,那么这种微调尤为重要。尽管我们对语言模型的能力期望很高,但我们不能指望它在从未接触过的内容上表现出色。就像我们不会指望一个没有学习材料的学生能在考试中表现优异一样。
外部上下文
为模型提供实时或最新的上下文信息有助于其保持基础性。实现这一目标的方法有很多,比如将其与外部知识库、API 和实时数据集成。这种方法也被称为检索增强生成(RAG)。
记忆系统
人工智能中的记忆系统在确保系统根据其先前采取的行动、所学的经验、随时间积累的表现以及与用户和其他系统的互动中保持基础性方面发挥着至关重要的作用。文章中之前提到的四种记忆类型在保持语言模型上下文感知和生成相关输出方面起着重要作用。记忆系统与基础性技术(如训练、微调和外部上下文整合)协同工作,以提升模型的整体表现和相关性。
结论
记忆和基础性是相互关联的元素,它们提高了人工智能系统的表现和可靠性。记忆使得人工智能能够跨时间维度保留和处理信息,而基础性确保了人工智能的输出在上下文中是相关和有意义的。通过整合记忆系统和基础性技术,人工智能系统可以在其交互和任务中实现更高水平的理解和有效性。
注意:本文及论文中的观点仅代表作者个人观点,并不一定反映其所在单位的看法或政策。
如果你还有疑问或认为有些内容需要进一步澄清,欢迎在Linkedin上给我发私信!我总是乐于参与思维碰撞并改进我的工作。
参考文献:
openreview.net/pdf?id=QNW1OrjynpT
www.simplypsychology.org/short-term-memory.html
Concordance Index 的直觉——生存分析
排名准确度与绝对准确度
·发布于 Towards Data Science ·16 分钟阅读·2024 年 11 月 28 日
--

作者与她的边境牧羊犬合影。 “感恩你拥有的一切。勇敢追求你想要的一切”
你会保持多少时间的健身房会员资格才决定取消?或者,如果你是一个喜欢追剧的 Netflix 用户,但比平时更忙,没办法腾出两个小时待在沙发上看电视,什么时候会取消订阅?又或者,何时你会升级或更换你的智能手机?在考虑交通、道路封闭、时间等因素时,如何选择最佳路线?又或者,你的车多久需要保养一次?这些问题是我们日常生活中经常面临的(但并不简单的)问题,很多时候我们并没有过多思考(甚至从未考虑过)影响我们下一步行动的不同因素。当然(或者在读完这些内容后),你可能会想知道什么因素或哪些因素可能对预期某一事件(无论是上面提到的,还是其他事件)的发生时间有最大的影响?在统计学中,这就是所谓的时间至事件分析或生存分析。这也是本研究的重点。
在生存分析中,我们旨在分析事件发生的时间。在本文中,我将使用生存分析预测注册会员何时可能会离开(流失),具体来说是预测会员取消其会员合同的天数。由于关注的变量是天数,此时需要明确强调的一个关键点是:事件发生时间的因变量是连续型的,可以在某个范围内取任何值。因此,生存分析是适用的工具。
数据
本研究使用了由一家私营机构提供的专有数据集,该机构位于辅导行业。数据包括为确保保密性而进行匿名化处理的记录,收集时间跨度为两年,即 2022 年 7 月至 2024 年 10 月。所有分析均符合伦理标准,确保数据隐私和匿名性。因此,为尊重数据提供方的机密性,任何特定的组织细节和/或唯一标识符的详细信息都已被省略。
数据预处理后的最终数据集(即处理空值、归一化以处理异常值、聚合以去除重复数据并按合理级别分组)包含 44,197 条唯一标识符级别的记录。总共有 5 列输入到模型中,分别是:1)年龄,2)访问次数,3)首次访问,4)会员期间的最后访问,以及 5)任期。后者代表持有会员资格的天数,因此是事件发生的时间目标变量。基于访问的变量是为本研究生成的特征工程产品,由原始的现有变量通过对每个标识符在分析期间的数据进行一些计算和聚合得到。最后且非常重要的是,数据集仅包含未删失记录。也就是说,所有唯一标识符在分析时都已经经历了事件,即会员取消。因此,在本分析中没有删失数据,即没有个体在观察期之后生存(未取消会员)。这一点在选择建模技术时非常关键,正如我接下来将解释的。
在所有用于生存分析的不同技术中,有三种技术最为常见:
Kaplan-Meier 估计量。
-
这是一种非参数模型,因此不对数据的分布做出任何假设。
-
KM 并不关注单个特征如何影响流失,因此它不会提供基于特征的洞察。
-
它广泛用于探索性分析,以评估生存曲线的形态。
-
非常重要的是,它不提供个性化的预测。
Cox 比例风险(PH)模型
-
Cox PH 模型是一种半参数模型,因此它不假设生存时间的具体分布,使其在更广泛的数据范围内更加灵活。
-
它估计风险函数。
-
它在很大程度上依赖于未删失以及删失数据,以区分那些“处于风险中”的个体(即可能经历事件的个体)和那些已经经历事件的个体。因此,如果仅分析未删失数据,模型会假设所有个体都经历了事件,从而得出偏差的结果,导致 Cox PH 模型表现不佳。
AFT 模型
-
它不需要删失数据。因此,可以用于所有个体都已经经历事件的情况。
-
它直接建模协变量之间的关系。
-
当事件发生的时间是主要关注点时使用。
-
该模型显式地估计事件时间。因此,提供了直接的取消持续时间预测。
基于本研究中使用的数据集特点,我选择了加速失败时间(AFT)模型作为最合适的技术。这个选择是基于两个关键因素:(1)数据集仅包含未删失数据,且(2)分析侧重于为每个唯一标识符生成个体级别的预测。
在深入探讨方法和模型输出之前,我将介绍一些关键概念:
生存函数(Survival Function): 它提供了生存概率随时间变化的洞察。
危险函数(Hazard Function): 在时间 t 时事件发生的速率。它捕捉事件随时间的变化。
事件时间(Time-to-event): 指捕捉事件发生前的时间(目标变量)。
删失(Censoring): 标记指的是在分析时间框架内某些受试者尚未发生的事件。注意:在本研究中只分析未删失数据,这意味着所有受试者的生存时间都已知。
一致性指数(Concordance Index): 衡量模型预测生存时间相对排序的准确性。这是衡量排名准确度而非绝对准确度的指标,用于评估所有受试者配对中预测的生存时间与实际结果一致的比例。
赤池信息准则(AIC): 用于评估模型质量的度量标准,且对使用的无关变量数量进行惩罚。在比较多个模型时,AIC 最小的模型被认为是最优的。
接下来,我将详细阐述前两个概念。
在数学上:
生存函数为:

(1)
其中,
T 是一个随机变量,表示事件发生的时间——即直到事件发生的持续时间。
S(t) 是事件在时间 t 时尚未发生的概率。
另一方面,危险函数为:

(2)
其中,
f(t) 是概率密度函数(PDF),描述了事件在时间 t 发生的速率。
S(t) 是描述在时间 t 之后生存的概率的生存函数。
由于 PDF f(t) 可以通过对 S(t) 关于 t 求导来表示,因此:

(3)
将 S(t) 的导数代入危险函数:

(4)
对对数生存函数求导:

(5)
根据链式法则,可以得出:

(6)
因此,危险函数和生存函数之间的关系定义如下:

(7)
危险率捕捉了在特定时间点生存概率变化的速度。
危险函数始终是非负的,永远不会低于零。其形态可以增加、减少、保持恒定或呈现更复杂的变化。
简单来说,危险函数是衡量在某个时刻 t 发生事件的即时风险。它告诉我们,个体在该时刻经历事件的可能性有多大。而生存(率)函数则衡量的是在某个特定时刻之后生存的概率。这是指在时间点 t 之前没有经历事件的总体概率。
随着时间的推移,生存函数始终是递减的,因为越来越多的个体经历了事件。这在下面的直方图中有所展示,图示了事件发生时间变量:任期。

由作者通过绘制所研究数据集中的事件发生时间目标变量生成。
在 t=0 时,没有个体经历事件(没有个体取消其会员资格),因此

(8)
最终,所有个体都会经历事件,因此生存函数趋向于零(0)。

(9)
模型
就本文的目的而言,我将重点讨论一种基于多变量参数化的模型:加速失效时间(AFT)模型,该模型明确估计连续的事件发生时间目标变量。
给定 AFT 模型:

(10)
对方程两边取自然对数,得到:

(11)
其中,
log(T) 是生存时间的对数,即事件发生时间(持续时间),如公式(11)所示,它是协变量的线性函数。
X 是协变量的向量
β 是回归系数的向量。
这非常重要:
模型中的系数 β 描述了协变量如何加速或减缓事件时间,即生存时间。在 AFT 模型(本文的重点)中,系数直接影响生存时间(而不是危险函数),具体如下:
如果 β > 1,生存时间较长,因此导致事件发生时间减缓。这意味着,成员将需要更长时间来终止其会员资格(较晚经历事件)。
如果 β < 1,生存时间较短,因此导致事件发生时间加速。这意味着,成员将较早终止其会员资格(更早经历事件)。
最后,
ϵ 是随机误差项,表示影响生存时间的未观察因素。
现在,基于上述几点,列出一些明确的要点:
-
这是一个多变量方法,其中事件发生时间(持续时间)目标变量在多个协变量上进行拟合。
-
这是一种参数化方法,因为该模型对生存率分布的特定形状做出了假设。
-
在 AFT 模型框架下已经实现了三种算法,分别是:
3.1) 威布尔 AFT 模型
-
该模型具有灵活性,可以捕捉不同的生存模式。支持一致的单调递增/递减函数。这意味着:在函数定义的任意两个点之间,后一个点至少与前一个点一样高。
-
无需显式建模风险函数。该模型有两个参数,通过这两个参数推导出生存函数:形状,它决定了分布的形状,因此有助于确定数据的偏斜程度;尺度,它决定了分布的扩展性。再加上与每个协变量相关的回归系数。形状参数决定了风险函数的单调行为,进而影响生存函数的行为。
-
右偏、左偏分布的事件时间目标变量是其中的例子。
3.2) 对数正态 AFT 模型
-
主要集中于建模生存时间的对数转换。是一个近似呈正态分布的连续概率分布的随机变量的对数。
-
支持右偏分布的事件时间目标变量。当事件风险不遵循简单模式时,允许非单调的风险函数。这在风险随时间变化复杂时很有用。
-
它不需要显式建模风险函数。
-
两个主要参数(加上任何回归系数):尺度和位置,前者表示对数转换的生存时间的标准差,后者表示对数转换的生存时间的均值。这表示在没有包含协变量时的截距,否则表示这些变量的线性组合。
3.3) 广义伽马 AFT 模型。
-
适用于广泛的生存数据模式。高度适应的参数化模型,能够适应上述提到的形状以及生存函数中的更复杂数学形式。
-
它可以用来测试是否可以使用更简单的模型(如威布尔分布、对数正态分布),因为它涵盖了这些模型作为特殊情况。
-
它不需要显式指定风险函数。
-
除了回归系数外,它有三个参数:形状、尺度和位置,后者对应在没有包含协变量时的生存时间中位数的对数,因此是模型中的截距。
提示:关于这些算法的文献数量非常庞大,专门聚焦于每个算法及其特性的文献,我强烈建议读者去深入了解。
最后,分析了上述算法的性能,重点关注一致性指数(是的,C-指数,我们关注的度量标准)和赤池信息准则(AIC)。这些将在下文中展示,并附带模型输出:
回归输出
威布尔 AFT 模型

由作者使用 lifelines 库生成
对数正态 AFT 模型

由作者使用 lifelines 库生成
广义伽马 AFT 模型

由作者使用 flexsurv 库生成
在右侧,显示了每个预测变量的图表:以对数加速故障率为 x 轴,展示其对生存时间的正向或负向(加速/减速)影响。如图所示,所有模型在预测变量的影响方向上都一致,得出了一致的结论,表明预测变量对生存时间的影响是正向还是负向的。现在,在一致性指数和 AIC 方面,对数正态模型和威布尔模型的 C-index 值都显示为最高,但特别是对数正态模型由于 AIC 较低而占据主导地位。因此,对数正态模型被选为最适合的模型。
聚焦于对数正态 AFT 模型以及每个协变量(coef)估计系数的解释,通常预测变量的 p 值都低于常规的 5% 显著性水平,从而拒绝了原假设,证明它们对生存时间具有统计学显著的影响。年龄的系数为负值 -0.06,意味着随着年龄的增长,成员更有可能更早经历事件,从而更早终止其会员身份。即:每增加一岁,生存时间减少 6%,这时生存时间会乘以一个 0.94 的因子(exp(coef)),从而加速生存时间。相比之下,访问次数、加入后的首次访问和最近一次访问都显示出对生存时间的强正向影响,表明更多的访问、早期参与和最近的参与都能显著增加生存时间。
现在,从模型间的一致性指数(Concordance Index,C-index)来看(这是本分析的重点),广义伽马 AFT 模型的 C-index 值最低,因此是预测准确性最弱的模型。这个模型在根据预测的风险得分正确排序生存时间方面能力最弱。这个结果强调了模型表现的一个重要方面:无论模型是否能够正确捕捉各预测变量影响方向的准确性,这并不一定保证预测的准确性,特别是在区分提前发生事件与较晚发生事件的受试者时,依据一致性指数来衡量。C-index 明确评估的是模型的排名准确性,而非绝对准确性。这是本分析的核心区分,我将在接下来的部分详细阐述。
一致性指数(C-INDEX)
“排名生存时间”是指模型为每个个体生成的预测风险评分,用于排名,从而区分那些比其他人早经历事件的个体。协调指数是排名准确性的度量,而非绝对准确性,具体来说:C 指数评估所有个体对的比例,这些个体的预测生存时间与实际结果一致。从绝对角度来看,模型在预测成员取消会员所需的具体天数的精确度并不重要,重要的是模型在实际和预测时间一致的情况下,如何准确地对个体进行排名。下面对此做出说明:

由作者根据实例绘制:来自验证数据集的实际值和估计值。
上述两个实例来自验证集,在模型经过训练集训练后,针对未见数据生成了预测结果。这些例子展示了预测生存时间(由模型估计)超过实际生存时间的情况。水平平行线表示时间。
对于会员 1,实际的会员时长是 390 天,而模型预测的时长是 486 天——高估了 96 天。同样,会员 2 的实际会员时长为 1,003 天,但模型预测会员取消的时间比实际发生的时间晚了 242 天,即 1,245 天的会员时长。
尽管在绝对预测值上存在这些差异(这点非常重要):模型在风险排名方面正确地对两名会员进行了排序,准确预测了会员 1 会在会员 2 之前取消会员资格。绝对误差和相对排名之间的区别是模型评估的一个关键方面。考虑以下假设情境:

由作者根据实例绘制:来自验证数据集的实际值和估计值。
如果模型预测会员 1 的会员时长是 1,200 天,而不是 486 天,这不会影响排名。模型仍然会预测会员 1 会比会员 2 早终止会员资格,无论预测误差(即天数)有多大。在生存分析中,任何对于会员 1 的预测,若落在图中的虚线之前,都会保持相同的排名,并且被分类为一致对。这个概念对计算C 指数至关重要,它衡量数据集中所有一致对的比例。
以下展示了几个假设场景。在每个场景中,误差的大小增加/减少,即实际事件时间与预测事件时间之间的差异,这是绝对误差。然而,排名准确性保持不变。

作者根据实例绘制,来自验证数据集的实际和预测值。
以下内容同样来自验证集,但在这些实例中,模型预测的成员资格终止时间早于实际事件发生时间。对于成员 3,实际的成员资格持续时间为 528 天,但模型预测的终止时间早了 130 天,即 398 天的成员资格持续时间。同样,对于成员 4,模型预测在实际事件发生之前终止会员资格。在这两种情况下,模型正确地将成员 4 排名为早于成员 3 终止会员资格。

作者根据实例绘制,来自验证数据集的实际和预测值。
在下面的假设场景中,即使模型为成员 3 提前 180 天预测了终止,排名仍然不变。这仍然被分类为一致对。我们可以多次重复此分析,在 88% 的情况下,LogNormal 模型将产生这个结果,如一致性指数所示。这意味着:模型正确预测了个体生存时间的相对顺序。

作者根据实例绘制,来自验证数据集的实际和预测值。
和一切一样,关键在于根据手头的任务战略性地使用生存分析。关注排名个体的使用案例,采用生存分析作为最有效的策略,而不是专注于减少绝对误差的案例包括:
客户保持 — 企业 按客户流失的可能性对客户进行排名。生存分析可以帮助识别最有流失风险的客户,从而有针对性地进行保持工作。
员工流失 — 人力资源分析 组织使用生存分析来预测和排名员工的离职可能性。类似于上述方法,可以识别出流失风险最高的员工,从而提高保持率,减少员工流动成本。
医疗保健 — 资源分配 生存模型可能用于根据患者发生不良结果(即疾病进展)的风险对患者进行排名。在这里,正确识别哪些患者处于最高风险并需要紧急干预,从而更有效地分配有限资源,比准确预测生存时间更为关键,因此更为相关。
信用风险 — 金融 金融机构利用生存模型根据借款人的违约风险对其进行排序。因此,他们更关心识别最具风险的客户,以便做出更明智的贷款决策,而不是专注于违约的具体月份。这将积极指导贷款审批(等)。
在上述情况下,受试者的相对排名(例如,谁处于更高或更低的风险)直接驱动可操作的决策和资源分配。生存时间预测中的绝对误差可能不会显著影响结果,只要排名准确性(C 指数)保持较高。这表明,即使模型的绝对预测不够精确,具有高 C 指数的模型仍然能非常有效。
总结
在生存分析中,区分绝对误差和排名准确性至关重要。绝对误差是指预测与实际事件时间之间的差异,在本分析中通常以天数衡量。诸如平均绝对误差(MAE)或均方根误差(RMSE)等指标用于量化这些差异的大小,从而衡量模型的整体预测准确性。然而,这些指标无法捕捉模型正确根据受试者发生事件的早晚排序的能力。
排名准确性,另一方面,评估模型如何根据预测的风险对受试者进行排序,而不考虑如上所示的确切时间预测。这就是一致性指数(C 指数)发挥关键作用的地方。C 指数衡量模型正确排序个体对的能力,值越高表示排名准确性越好。C 指数为 0.88 表明模型成功地以 88%的准确率正确排序会员终止风险。
因此,虽然绝对误差提供了有关时间预测精度的宝贵见解,但 C 指数更关注模型正确排序受试者的能力,这在生存分析中通常更为重要。具有高 C 指数的模型,即使存在一定程度的绝对误差,也能在排序个体时非常有效,使其成为预测相对风险随时间变化的强大工具。
优化的直观基础
对优化这一神奇领域的温和介绍
·发表于 Towards Data Science ·12 分钟阅读·2024 年 6 月 26 日
--
本文将通过你的直觉和一个“日常”例子,向你介绍优化的基本组成部分。阅读完本文后,你将理解解决优化问题时(1)目标,(2)约束,(3)决策变量,(4)解空间和(5)解的定义和作用。本文旨在介绍优化的概念——并不涉及优化的数学原理或具体技术,相关内容将在未来的文章中讨论!
阅读完本文后,你将能看到优化框架如何应用于下面链接文章中的“现实世界”股票切割问题(这是我的第一篇 TDS 文章!)。
对股票切割问题的贪心方法的评估
towardsdatascience.com
内容
-
优化的直觉
-
目标——我们的目标是什么?
-
约束——我们不能做什么?
-
决策变量和参数——我们可以控制什么,不能控制什么?
-
可行区域与解空间——我们的最优解来自哪里?
-
解决方案——优化推荐我们做什么?
打破我的自动化的隐形错误:OCR 如何改变了游戏
测试自动化中的 AI 发展:从定位器到生成性 AI(第三部分)
·发表于 Towards Data Science ·8 分钟阅读·2024 年 12 月 17 日
--

测试自动化中的 AI 发展:从定位器到生成性 AI(第三部分) — © Tung Nguyen 来自 Pixabay
现在是星期天早上 10 点,我盯着又一份失败的测试报告。这个测试案例可不简单,是我在上周某些 UI 更改后检查并调试过的。结果现在它神秘地失败了。再次……
错误?显然没有错误。
应该显示的内容是:欢迎屏幕上应该显示这段文本:
“欢迎使用 my-app-name 应用程序!”
我查看了应用程序的截图,文本显示正确……没有问题:

作者的手机截图,显示预期的文本正确显示 —— 由于保密问题已模糊处理
非会员可以通过此链接继续阅读(来自 Medium 移动应用的读者可能由于现有错误无法点击该链接,请在评论中找到该链接)
我决定查看一下 Appium 的 XML:
<XCUIElementTypeStaticText type="XCUIElementTypeStaticText"
value="Bienvenue à bord de l'application my-app-name !"
enabled="true"…
RAG 开发之旅:从笔记本到微服务
将 Colab 笔记本转换为支持 Milvus 和 NeMo Guardrails 的两个微服务
·发表于Towards Data Science ·阅读时间:11 分钟·2024 年 2 月 21 日
--

图像由作者通过 DALL-E 3 生成
在探索企业级 RAG 的过程中,本文介绍了如何从在 Colab 笔记本中开发的 RAG 管道 POC 中创建 RAG 微服务。我们采取了以下方法:
-
使用 LlamaIndex 的
create-llama命令行工具生成 RAG 微服务的模板代码。 -
开发两个微服务:
ingestion-service和inference-service,覆盖 RAG 的两个主要阶段。 -
将 Colab 笔记本中的代码逻辑转换为微服务。
-
将 Milvus 向量数据库集成到我们的新微服务中。
-
将 NeMo Guardrails 添加到
inference-service,为用户输入、LLM 输出、主题审核和自定义操作提供保护机制,并与 LlamaIndex 集成。
笔记本
为了快速原型开发,Colab 笔记本因其易用性、可访问性和免费使用而成为理想选择。
例如,这个 Colab 笔记本演示了如何在 RAG 管道中使用元数据替换+节点句子窗口,该管道用作NVIDIA AI 企业用户指南的聊天机器人。
更智能模型的关键:跟踪特征历史
利用历史数据捕获上下文并改善预测
·发表于 Towards Data Science ·8 分钟阅读·5 天前
--

历史文本。来源:boudewijnhuijgens.getarchive.net/amp/media/bible-book-page-font-backgrounds-textures-49ea03
最近,我从数据科学的职业生涯稍微转向了一个更侧重工程的角色。我的团队正在构建一个高质量的数据仓库,以支持组织的商业智能(BI)和机器学习(ML)需求。我之所以选择这个职位,是因为我看到这是一个机会,能够运用我在数据科学岗位上获得的见解,影响数据仓库的设计和开发,并使其具有前瞻性。
在过去 6 年的几乎每一个数据科学职位中,我都注意到了一个共同的主题——数据基础设施的设计并未考虑到数据科学的需求。数据仓库/湖仓中的许多表格,通常是事实表和维度表,缺少构建高性能机器学习模型所需的关键字段或结构。我注意到的最普遍的限制是,大多数表格仅追踪观察的当前状态,而不是历史记录。
本文探讨了这个常见问题,并展示了如何通过一种名为“缓慢变化维度”的数据建模技术来解决它。阅读完本文后,你将了解存储历史数据对模型性能的影响,并掌握一些策略,帮助你在自己的应用场景中实施此方法。
企业 AI 成功的关键:价值链
AI 大师
作为 AI 解决方案架构师与金融企业合作时,我学到的最重要的商业教训
·发表于Towards Data Science ·阅读时间:5 分钟·2024 年 9 月 24 日
--

图片由Barbara Zandoval提供,来源于Unsplash
我写这篇文章是为了分享我作为 AI 解决方案架构师与企业客户合作时学到的最重要的商业教训。这些教训或许能帮助你们在向企业客户提供解决方案并与他们达成业务合作时更加成功,尤其是在你们还处于签署合同的谈判阶段时。
在帮助企业采用 AI 时,AI 解决方案架构师面临许多挑战。企业可能缺乏干净且结构化的数据,或者甚至不熟悉 AI 的潜力。其中许多挑战是广为人知的,你可能已经为此做好了准备。然而,有一个普遍的挑战需求,跨所有企业都存在,如果你能妥善解决它,将能开启所有的大门:一个具有可操作性和处方性洞察的端到端商业解决方案。在本文中,我分享了我通过与金融企业合作的经验在这条路上学到的教训。
为金融企业找到一个单一的用例并不难,但挑战在于如何创建一个端到端的商业解决方案。在这里…
新兴人工智能代理架构在推理、规划和工具调用方面的全景:一项调查
·发表于 Towards Data Science ·7 分钟阅读·2024 年 4 月 23 日
--

图片由作者提供
我和我的团队(Sandi Besen、 Tula Masterman、 Mason Sawtell*** 和*** Alex Chao) 最近发表了一篇调查研究论文,全面探讨了当前人工智能代理架构的现状。作为这篇论文的共同作者,我们的目标是揭示使这些自主系统能够有效执行复杂目标的关键设计要素。
本文为研究人员、开发者以及任何有兴趣了解人工智能代理技术前沿进展的人提供了一个资源。
阅读完整的元分析文章请访问 Arxiv
向代理转型
自 ChatGPT 推出以来,初期的生成式人工智能应用大多围绕着利用检索增强生成(RAG)模式来响应用户提示的聊天机器人展开。尽管目前仍有在增强这些基于 RAG 的系统的鲁棒性方面的工作,但研究界现在正在探索下一代人工智能应用——一个共同的主题是开发自主人工智能代理。
代理系统融合了高级能力,如规划、迭代和反思,利用模型固有的推理能力完成端到端的任务。结合使用工具、插件和功能调用的能力,代理能够处理更广泛的一般用途工作。
推理、规划和有效工具调用对于代理的重要性
推理是人类思维的基础构建块。没有推理能力,人类将无法做出决策、解决问题或在获得新信息时完善计划——本质上是无法理解周围的世界。如果代理没有强大的推理能力,它们可能会误解任务,生成无意义的答案,或者忽视多步骤的后果。
我们发现,大多数代理实现包含一个规划阶段,利用以下技术之一来创建计划:任务分解、多计划选择、外部模块辅助规划、反思和优化以及记忆增强规划[1]。
采用代理实现而非仅使用基础语言模型的另一个好处是代理能够通过调用工具解决复杂问题。工具可以使代理执行诸如与 API 交互、向第三方应用写入数据等操作。推理与工具调用是紧密相连的,有效的工具调用依赖于充分的推理能力。简而言之,你不能指望一个推理能力差的代理理解何时是调用其工具的合适时机。
单一代理与多代理架构
我们的研究强调,单一代理和多代理架构都可以通过应用推理和工具调用步骤来解决具有挑战性的任务。
对于单一代理实现,我们发现成功的目标执行取决于适当的规划和自我修正[1, 2, 3, 4]。如果没有自我评估和制定有效计划的能力,单一代理可能会陷入无限执行循环,既无法完成给定任务,也无法返回不符合用户预期的结果[2]。我们发现,单一代理架构在任务要求直接的功能调用且不需要来自其他代理的反馈时尤其有用。
然而,我们注意到单一代理模式通常难以完成长时间序列的子任务或工具调用[5, 6]。多代理模式能够解决并行任务和鲁棒性的问题,因为架构中的多个代理可以独立处理各自的子问题。许多多代理模式首先会将复杂问题分解成若干较小的任务。然后,每个代理独立地使用自己的工具集来解决各自的任务。
多智能体参与的架构为基于能力的智能劳动分工提供了机会,同时也为来自不同智能体角色的宝贵反馈提供了机会。许多多智能体架构在各个阶段运作,其中智能体团队在每个规划、执行和评估阶段都会动态形成和重新组织[7, 8, 9]。这种重新组织产生了卓越的结果,因为专门的智能体被用于特定任务,并在不再需要时被移除。通过将智能体角色和技能与手头的任务匹配,智能体团队可以实现更高的准确性,并减少实现目标所需的时间。有效的多智能体架构的关键特征包括智能体团队内明确的领导、动态团队构建以及团队成员之间的高效信息共享,以防止重要信息在多余的沟通中丢失。
我们的研究重点介绍了诸如 ReAct、RAISE、Reflexion、AutoGPT + P、LATS 等显著的单智能体方法,以及 DyLAN、AgentVerse 和 MetaGPT 等多智能体实现,这些在 全文. *中有更深入的解释。
我们的主要发现
单智能体模式:
单智能体模式通常最适合具有明确定义工具清单和流程明确定的任务。他们不会面临来自其他智能体的负面反馈,也不会受到其他团队成员的干扰和无关的闲聊。然而,如果他们的推理和改进能力不够强大,单一智能体可能会陷入执行循环中,并未能朝着目标取得进展。
多智能体模式:
多智能体模式非常适合需要来自多个角色的反馈以完成任务的情况。当需要跨不同任务或工作流程进行并行化时,它们非常有用,允许各个智能体继续进行下一步而不受其他智能体处理的任务状态的阻碍。
反馈和人在回路中
语言模型往往在其回应中较早地承诺一个答案,这可能导致与其目标状态越来越偏离的“雪球效应”[10]。通过实施反馈,智能体更有可能纠正其行动并达到目标。人类监督通过使智能体的回应更贴近人类期望,提供更可靠和可信赖的结果,从而改善了即时结果[11, 8]。智能体可能会受到来自其他智能体的反馈的影响,即使反馈不正确。这可能导致智能体团队制定一个错误的计划,使他们偏离目标[12]。
信息共享和沟通
多代理模式更容易陷入琐事,彼此问一些诸如“你好吗?”之类的问题,而单一代理模式则倾向于专注于当前任务,因为没有团队动态需要管理。通过强有力的提示可以缓解这种情况。在垂直架构中,代理可能未能将关键信息传递给支持的代理,未意识到其他代理没有完成任务所需的必要信息。这种失败可能导致团队混乱或结果中的幻觉。解决这个问题的一种方法是显式地在系统提示中包括关于访问权限的信息,以便代理进行情境上合适的互动。
角色定义和动态团队的影响
清晰的角色定义对于单一和多代理架构至关重要。角色定义确保代理能够理解其分配的角色,保持专注于所提供的任务,执行正确的工具,并最大限度地减少其他能力的幻想。建立一个明确的团队领导者可以通过简化任务分配来提高多代理团队的整体表现。基于需要将代理引入和移出系统的动态团队也被证明是有效的。这确保了参与任务的所有代理都是强有力的贡献者。
关键洞察总结
讨论的关键洞察表明,最佳的代理架构取决于使用案例。无论选择何种架构,表现最佳的代理系统往往包含以下至少一种方法:明确的系统提示、清晰的领导和任务分配、专门的推理/计划-执行-评估阶段、动态团队结构、人类或代理反馈以及智能消息过滤。利用这些技术的架构在各种基准和问题类型中表现更为有效。
结论
我们的元分析旨在提供对当前 AI 代理格局的全面理解,并为那些使用现有代理架构或开发自定义代理架构的人提供洞察。设计和开发自主 AI 代理仍存在显著的局限性和未来改进的空间,如缺乏全面的代理基准、实际应用性不足以及有害语言模型偏见的缓解。这些问题需要在短期内解决,以实现可靠的代理。
注意:本文及论文中表达的观点仅代表作者个人意见,不一定反映其所属雇主的观点或政策。
如果你还有疑问,或者认为某些内容需要进一步澄清,请在Linkedin上私信我!我总是乐于参与思想碰撞并不断优化我的工作。
参考文献
[1] 蒂莫·比尔等人. AutoGPT+P: 基于效能的任务规划与大语言模型. arXiv:2402.10778 [cs] 版本: 1. 2024 年 2 月. URL: arxiv.org/abs/2402.10778.
[2] 姚顺宇等人. ReAct: 在语言模型中协同推理与行动. arXiv:2210.03629 [cs]. 2023 年 3 月. URL: arxiv.org/abs/2210.03629.
[3] 刘娜等人. 从 LLM 到对话智能体: 具有大语言模型微调的记忆增强架构. arXiv:2401.02777 [cs]. 2024 年 1 月. URL: arxiv.org/abs/2401.02777.
[4] 诺亚·辛恩等人. Reflexion: 语言智能体与言语强化学习. arXiv:2303.11366 [cs]. 2023 年 10 月. URL: arxiv.org/abs/2303.11366
[5] 石正良等人. 通过协作和交互式智能体学习使用工具. arXiv:2403.03031 [cs]. 2024 年 3 月. URL: arxiv.org/abs/2403.03031
[6] 高思林等人. 通过抽象链推理高效使用工具. arXiv:2401.17464 [cs]. 2024 年 2 月. URL: arxiv.org/abs/2401.17464
[7] 陈伟泽等人. AgentVerse: 促进多智能体协作与探索涌现行为. arXiv:2308.10848 [cs]. 2023 年 10 月. URL: arxiv.org/abs/2308.10848。
[8] 郭旭东等人. 有形 LLM 智能体学习在有组织团队中的合作. 2024. arXiv: 2403.12482 [cs.AI]. URL: arxiv.org/abs/2403.12482
[9] 刘子俊等人. 动态 LLM-智能体网络: 一种具有智能体团队优化的 LLM-智能体协作框架. 2023. arXiv: 2310.02170 [cs.CL]. URL: arxiv.org/abs/2310.02170
[10] 张木如等人. 语言模型幻觉如何会呈滚雪球效应. arXiv:2305.13534 [cs]. 2023 年 5 月. URL: arxiv.org/abs/2305.13534。
[11] 冯学阳等人. 基于大语言模型的人类-智能体协作解决复杂任务. 2024. arXiv: 2402.12914 [cs.CL]。
[12] 陈伟泽等人. AgentVerse: 促进多智能体协作与探索涌现行为. arXiv:2308.10848 [cs]. 2023 年 10 月. URL: arxiv.org/abs/2308.10848。
地图语言:地理空间数据格式与坐标指南
从 GeoJSON 到 UTM,这些工具帮助我们绘制世界地图!
·发表于 Towards Data Science ·阅读时间:6 分钟 ·2024 年 1 月 17 日
--

图片来自KOBU Agency,图片来源:Unsplash
每当我使用像 Google Maps 或 Uber 这样的工具时,我有时会忽略它们试图解决的挑战的复杂性。这些应用程序需要进行高度复杂的计算,同时展示一个带有通常非常精确数据的有趣地图。最起码,可以说我们在地理数据和制图工具方面取得了令人印象深刻的进展!
在本文中,我将带你了解一些数据科学家和工程师使用的最流行的数据格式和坐标。如果你曾经看过世界地图投影,你会知道有很多不同的方法来可视化空间地理,而尽管本指南没有涵盖所有的工具,但它应该能够为你提供一些更流行工具的概览。
地理空间数据格式
GeoJSON
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [125.6, 10.1]
},
"properties": {
"name": "Dinagat Islands"
}
}
如果你曾经使用过 JSON 数据,那么 GeoJSON 数据应该看起来非常熟悉!GeoJSON 数据易于处理,也很简单,用户可以轻松地…
大语言模型的最新进展:决策制定、知识图谱、推理能力等
·发表于 Towards Data Science ·通过 Newsletter 发送 ·阅读时间 4 分钟 ·2024 年 9 月 5 日
--
想要写下你的第一篇 TDS 文章吗?我们始终欢迎新作者的投稿。
随着大语言模型持续发展的速度,保持与该领域同步已成为一项重大挑战。我们每天都会看到新的模型、前沿研究和基于大语言模型的应用程序层出不穷,因此,许多从业者自然担心自己会落后,或者无法使用到最新和最先进的工具。
首先,让我们深呼吸一下:当整个生态系统在多个方向迅速发展时,没人能指望(或被期望)知道一切。我们也不应忘记,大多数同行也处于类似的情况,他们专注于对自己工作至关重要的发展,同时避免过度担心错过信息——或者至少尽量不让自己陷入这种困境。
如果你仍然有兴趣了解一些当前在大语言模型(LLMs)领域引发广泛讨论的重大问题,或者对机器学习专业人士正在探索的新兴主题感到好奇,我们随时为你提供帮助。在本周的《Variable》专栏中,我们将重点介绍一些深入探讨当前大语言模型状态的精彩文章,涵盖其底层能力和实际应用。让我们一起来看看吧!
-
导航新型 LLM 代理与架构 在对基于 LLM 的代理进行清晰概述时,Aparna Dhinakaran为这一时常混乱的领域注入了大量清晰度:“团队如何导航新框架和新代理方向?有哪些工具可用,应该使用哪些工具来构建下一个应用?”
-
利用语言代理树搜索(LATS)与 GPT-4o 应对复杂的 LLM 决策问题 在他首次撰写的 TDS 文章中,Ozgur Guler详细介绍了 LLM 在决策任务中面临的挑战,并概述了一种有前景的方法,该方法结合了 GPT-4o 模型的强大能力与语言代理树搜索(LATS)——一种“动态的基于树的搜索方法”,能够增强模型的推理能力。
-
从文本到网络:LLM 对知识图谱的革命性影响 近年来,大型语言模型和知识图谱分别沿着平行且大多独立的路径发展,但正如Lina Faik在她新的逐步指南中所指出的,现在是时候同时利用它们各自的优势,从而实现更准确、一致和具有上下文相关性的结果。

图片来源:Mick Haupt 通过 Unsplash
-
没有基准线?没有基准测试?没关系!一种实验性敏捷聊天机器人开发方法 在 LLM 驱动解决方案的新奇性和初期兴奋感消退后,产品团队仍然面临保持其有效运行并持续提供商业价值的挑战。Katherine Munro在最近的一次讲座中介绍了她在基准测试和测试 LLM 产品方面的方法,现在她将其转化为一种易于理解和可操作的路线图。
-
探索 LLMs 在风险博弈环境中的战略能力 Hans Christian Ekne 最近的深入研究也讨论了评估 LLMs 的问题,但从不同的、更理论化的角度出发。它详细审视了领先模型(来自 Anthropic、OpenAI 和 Meta)在经典棋盘游戏《风险》规则下展现的不同战略行为,讨论了它们的不足,并展望了 LLMs 推理能力的未来。
-
如何通过更好的采样参数提高 LLM 的响应 我们本周的最后一篇文章是由Dr. Leon Eversberg带来的一篇实践教程,讲解并可视化了定义 LLM 输出行为的采样策略,并演示了如何更好地理解这些参数,有助于我们改善模型生成的输出。
数据科学和机器学习的世界是广阔的,远远超出了当代的大型语言模型(LLMs)——这也是我们鼓励你探索其他相关主题的阅读推荐的原因:
-
如果你是一个数据科学家,偶尔会在将抽象的商业问题分解成更小、更明确的分析时遇到困难,那么不要错过Tessa Xie基于她作为顾问的经验所提供的宝贵见解。
-
开创性的 BERT 模型可能很快就满 6 岁了,但它的影响力依然与今天的许多机器学习从业者息息相关。 Daniel Warfield 的权威解释带领我们详细探讨了其内部运作机制。
-
我们是否已经达到了一个模型可以教导并训练另一个(更小)模型的地步?Laurin Heilmeyer探讨了这个问题的新视角以及这种方法对于资源有限的小型组织可能带来的潜在好处。
-
角色转换通常并不容易;正如Claudia Ng所明确指出的那样,尽管如此,你仍然可以采取一些步骤,使转变过程更加顺利和快速,正如她在最近从数据分析师转型为数据科学家的经历中所做的那样。
-
对持续学习的概念感到陌生?我们鼓励你探索Alicja Dobrzeniecka关于多模态视觉-语言模型及其在对比语言-图像预训练(CLIP)模型中的应用可能性的文章。
-
数据专业人士在工作中经常遇到图表,但他们通常不会停下来思考是什么让优秀的图表脱颖而出。Mel Richey 博士在一篇清晰且充满实例的指南中填补了这一空白。
感谢您支持我们作者的工作!正如我们之前提到的,我们很高兴发布新作者的文章,如果你最近写了一篇有趣的项目 walkthrough、教程或关于我们核心主题的理论反思,别犹豫,与我们分享。
直到下一个 Variable,
TDS 团队
新数据环境中的领导、影子和对抗角色
从数据工程师到领域专家——构建新数据平台需要什么
·发表于Towards Data Science ·阅读时间 8 分钟·2024 年 12 月 1 日
--

“最优秀的数据工程师是脱轨的软件工程师;最优秀的数据分析师、科学家和解决方案(数据)架构师是脱轨的数据工程师;最优秀的数据产品经理是脱轨的数据分析师或科学家。” [照片由 Yurii Khomitskyi 提供,来自 Unsplash]
你是否曾想过,为什么具有多学科技能的角色是新数据环境中成功交付的关键? — 如果感兴趣,请阅读这篇文章,了解混合角色及其 LSS 能力如何帮助你从创意走向收入。
在数据领域花费多年时间应对不断发展的技术和商业需求的复杂性后,我积累了一些属于自己的智慧——其中大部分都集中在推动新发展或重新设计现有事物上。
我不能说提升或从零开始创造某些东西的过程总是直观或简单的,也不能说这条路没有犯过错误。然而,通过这些经历,我学到了构建一个全新的高效数据团队所需要的东西,领导一个新数据项目,或设计一个新的数据产品生态系统,这些都能推动业务价值的增长。
在所有业务和技术变量中,积极的结果通常依赖于一个常数——正确的人,他们会支持你并为你提供帮助。
从本质上看,新型数据平台的成功归结为正确选择具有多学科和互补技能的个体贡献者。这些…
LLM 的(较少为人知的)崛起应用
·发表于Towards Data Science ·8 分钟阅读·2024 年 5 月 13 日
--
概述
大语言模型(LLMs)通常被称为生成型人工智能(GenAI),因为它们确实具有生成文本的能力。LLM 的第一个流行应用是聊天机器人,其中 ChatGPT 走在前列。随后,我们将其应用范围扩展到其他任务,如语义搜索和检索增强生成(RAG)。今天,我想谈谈 LLM 的一个崛起应用,即结构化无结构数据,我将通过将原始文本结构化为 JSON 数据来展示一个例子。
使用 LLM(大语言模型)进行数据结构化和提取是一个非常有前景的应用,具有很大的潜力。以下是原因:
-
提高准确性: LLM 能够理解人类语言的细微差别。这使得它们能够比传统的基于规则的系统更准确地从杂乱、无结构的文本中识别关键信息。
-
自动化潜力: 从无结构数据中提取信息可能是一项耗时且劳动密集的任务。LLM 可以自动化这个过程,从而释放人力资源用于其他任务,并加速对更大数据集的分析。
-
适应性和学习能力: LLM 则可以持续进行微调,并适应处理新的数据源和信息类型。随着它们接触到更多的无结构数据,它们能够学习并提高识别模式和提取相关信息的能力。
-
业务成果: 大量宝贵信息存在于诸如电子邮件、客户评论、社交媒体对话和内部文档等非结构化文本数据源中。然而,这些数据通常难以分析。大语言模型(LLM)可以通过将非结构化数据转换为结构化格式,解锁这些隐藏的潜力。这使得企业能够利用强大的分析工具来识别趋势并获取洞察。实质上,通过使用 LLM 将非结构化数据进行结构化,企业可以将一种负担(不可用的数据)转变为一种资产(有价值的洞察),从而推动更好的决策并提升整体业务成果。
一个例子
最近,我在为个人项目寻找开源食谱数据集时,除了这个 GitHub 仓库外,什么也没找到,该仓库包含了在publicdomainrecipes.com上显示的食谱。

照片由Jeff Sheldon拍摄,来自Unsplash
不幸的是,我需要一个更具可操作性的数据集,即更接近表格数据或NoSQL 文档的东西。这就是我开始考虑找到一种方法,将原始数据转换成更适合我需求的东西,而不需要花费数小时、数天甚至数周手动完成的原因。
让我展示一下我如何利用大语言模型的强大能力来自动化将原始文本转换为结构化文档的过程。
数据集
原始数据集是一个 markdown 文件的集合,每个文件代表一个食谱。
一个描述如何制作法式可丽饼的食谱的 markdown 文件示例。
如你所见,这并非完全无结构,文件顶部有一些漂亮的表格元数据,接下来有 4 个不同的部分:
-
一个介绍,
-
食材列表
-
步骤
-
一些提示。
基于这个观察,Sebastian Bahr开发了一个解析器,将 markdown 文件转换为 JSON 格式,在这里。
解析器的输出已经变得更具可操作性,此外,Sebastian 利用它构建了一个食谱推荐聊天机器人。然而,仍然存在一些缺点。食材和步骤的键包含了原始文本,这些文本可以做得更有结构。
按原样,某些有用的信息被隐藏了。
例如,食材的数量,每个步骤的准备或烹饪时间。
代码
在本文的剩余部分,我将展示我采取的步骤,以便得到像下面这样的 JSON 文档。
{
"name": "Crêpes",
"serving_size": 4,
"ingredients": [
{
"id": 1,
"name": "white flour",
"quantity": 300.0,
"unit": "g"
},
{
"id": 2,
"name": "eggs",
"quantity": 3.0,
"unit": "unit"
},
{
"id": 3,
"name": "milk",
"quantity": 60.0,
"unit": "cl"
},
{
"id": 4,
"name": "beer",
"quantity": 20.0,
"unit": "cl"
},
{
"id": 5,
"name": "butter",
"quantity": 30.0,
"unit": "g"
}
],
"steps": [
{
"number": 1,
"description": "Mix flour, eggs, and melted butter in a bowl.",
"preparation_time": null,
"cooking_time": null,
"used_ingredients": [1,2,5]
},
{
"number": 2,
"description": "Slowly add milk and beer until the dough becomes fluid enough.",
"preparation_time": 5,
"cooking_time": null,
"used_ingredients": [3,4]
},
{
"number": 3,
"description": "Let the dough rest for one hour.",
"preparation_time": 60,
"cooking_time": null,
"used_ingredients": []
},
{
"number": 4,
"description": "Cook the crêpe in a flat pan, one ladle at a time.",
"preparation_time": 10,
"cooking_time": null,
"used_ingredients": []
}
]
}
复现教程的代码在 GitHub这里。
我依赖了两个强大的库——用于与 LLM 提供商通信的[langchain](https://www.langchain.com/),以及用于格式化 LLM 输出的[pydantic](https://docs.pydantic.dev/latest/)。
首先,我定义了食谱的两个主要组成部分——Ingredient类和Step类。
在每个类中,我定义了相关的属性,并提供了字段描述和示例。然后这些内容通过langchain传递给 LLM,从而获得更好的结果。
"""`schemas.py`"""
from pydantic import BaseModel, Field, field_validator
class Ingredient(BaseModel):
"""Ingredient schema"""
id: int = Field(
description="Randomly generated unique identifier of the ingredient",
examples=[1, 2, 3, 4, 5, 6],
)
name: str = Field(
description="The name of the ingredient",
examples=["flour", "sugar", "salt"]
)
quantity: float | None = Field(
None,
description="The quantity of the ingredient",
examples=[200, 4, 0.5, 1, 1, 1],
)
unit: str | None = Field(
None,
description="The unit in which the quantity is specified",
examples=["ml", "unit", "l", "unit", "teaspoon", "tablespoon"],
)
@field_validator("quantity", mode="before")
def parse_quantity(cls, value: float | int | str | None):
"""Converts the quantity to a float if it is not already one"""
if isinstance(value, str):
try:
value = float(value)
except ValueError:
try:
value = eval(value)
except Exception as e:
print(e)
pass
return value
class Step(BaseModel):
number: int | None = Field(
None,
description="The position of the step in the recipe",
examples=[1, 2, 3, 4, 5, 6],
)
description: str = Field(
description="The action that needs to be performed during that step",
examples=[
"Preheat the oven to 180°C",
"Mix the flour and sugar in a bowl",
"Add the eggs and mix well",
"Pour the batter into a greased cake tin",
"Bake for 30 minutes",
"Let the cake cool down before serving",
],
)
preparation_time: int | None = Field(
None,
description="The preparation time mentioned in the step description if any.",
examples=[5, 10, 15, 20, 25, 30],
)
cooking_time: int | None = Field(
None,
description="The cooking time mentioned in the step description if any.",
examples=[5, 10, 15, 20, 25, 30],
)
used_ingredients: list[int] = Field(
[],
description="The list of ingredient ids used in the step",
examples=[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]],
)
class Recipe(BaseModel):
"""Recipe schema"""
name: str = Field(
description="The name of the recipe",
examples=[
"Chocolate Cake",
"Apple Pie",
"Pasta Carbonara",
"Pumpkin Soup",
"Chili con Carne",
],
)
serving_size: int | None = Field(
None,
description="The number of servings the recipe makes",
examples=[1, 2, 4, 6, 8, 10],
)
ingredients: list[Ingredient] = []
steps: list[Step] = []
total_preparation_time: int | None = Field(
None,
description="The total preparation time for the recipe",
examples=[5, 10, 15, 20, 25, 30],
)
total_cooking_time: int | None = Field(
None,
description="The total cooking time for the recipe",
examples=[5, 10, 15, 20, 25, 30],
)
comments: list[str] = []
技术细节
-
这里需要注意不要使用过于严格的模型,否则 LLM 输出的 JSON 会失败 Pydantic 验证。一个好的方法是提供一些灵活性,比如根据目标输出类型,提供默认值如
None或空列表[]。 -
注意
Ingredient类中quantity属性上的field_validator,它帮助引擎解析数量。最初没有这个,但通过一些实验,我发现 LLM 经常将数量作为字符串提供,例如1/3或1/2。 -
used_ingredients用于正式地将食材与食谱的相关步骤联系起来。
输出模型一旦定义,后续过程就相对顺利。
在prompt.py文件中,我定义了一个create_prompt函数,用于轻松生成提示语。每个食谱都会生成一个“新”的提示语。所有提示语有相同的基础,但食谱本身作为变量传递给基础提示语,以创建一个新的提示。
""" `prompt.py`
The import statements and the create_prompt function have not been included
in this snippet.
"""
# Note : Extra spaces have been included here for readability.
DEFAULT_BASE_PROMPT = """
What are the ingredients and their associated quantities
as well as the steps to make the recipe described
by the following {ingredients} and {steps} provided as raw text ?
In particular, please provide the following information:
- The name of the recipe
- The serving size
- The ingredients and their associated quantities
- The steps to make the recipe and in particular, the duration of each step
- The total duration of the recipe broken
down into preparation, cooking and waiting time.
The totals must be consistent with the sum of the durations of the steps.
- Any additional comments
{format_instructions}
Make sure to provide a valid and well-formatted JSON.
"""
与 LLM 逻辑的通信是在core.py文件的run函数中定义的,我这里为了简洁没有展示。
最后,我将所有这些组件结合在我的demo.ipynb笔记本中,以下是其内容。
# demo.ipynb
import os
from pathlib import Path
import pandas as pd
from langchain.output_parsers import PydanticOutputParser
from langchain_mistralai.chat_models import ChatMistralAI
from dotenv import load_dotenv
from core import run
from prompt import DEFAULT_BASE_PROMPT, create_prompt
from schemas import Recipe
# End of first cell
# Setup environment
load_dotenv()
MISTRAL_API_KEY = os.getenv("MISTRAL_API_KEY") #1
# End of second cell
# Load the data
path_to_data = Path(os.getcwd()) / "data" / "input" #2
df = pd.read_json("data/input/recipes_v1.json")
df.head()
# End of third cell
# Preparing the components of the system
llm = ChatMistralAI(api_key=MISTRAL_API_KEY, model_name="open-mixtral-8x7b")
parser = PydanticOutputParser(pydantic_object=Recipe)
prompt = create_prompt(
DEFAULT_BASE_PROMPT,
parser,
df["ingredients"][0],
df["direction"][0]
)
#prompt
# End of fourth cell
# Combining the components
example = await run(llm, prompt, parser)
#example
# End of fifth cell
我使用了MistralAI作为 LLM 提供商,采用他们的open-mixtral-8x7b模型,这是一个非常好的开源替代方案,相较于OpenAI。langchain让你可以轻松切换提供商,只要你在提供商平台上创建了账户。
如果你试图复现这些结果:
-
(#1) — 确保你在
.env文件中或在操作系统环境中有MISTRAL_API_KEY。 -
(#2) — 请小心数据的路径。如果你克隆了我的仓库,这将不是问题。
在整个数据集上运行代码的费用不到 2€。
通过此代码生成的结构化数据集可以在我的仓库中这里找到。
我对结果很满意,但仍然可以尝试对提示语、字段描述或所使用的模型进行迭代,以进一步改进它们。我可能会尝试 MistralAI 的最新模型open-mixtral-8x22b,或者通过简单地修改 2 到 3 行代码,借助langchain尝试另一个 LLM 提供商。
当我准备好时,我可以回到我的原始项目。如果你想知道它是什么,敬请关注。同时,如果你有任何想法,欢迎在评论中告诉我,你会如何利用最终的数据集?
结论
大型语言模型(LLMs)为结构化非结构化数据提供了强大的工具。它们理解和解释人类语言细微差别的能力、自动化繁琐任务的能力,以及适应不断变化的数据的能力,使它们成为数据分析中无价的资源。通过发掘非结构化文本数据中的潜在价值,企业可以将这些数据转化为有价值的洞察,推动更好的决策和商业成果。提供的例子——将原始食谱数据转化为结构化格式——只是 LLMs 所能提供的无数可能性中的一个。
随着我们继续探索和开发这些模型,未来我们可以预见到更多创新应用的出现。充分发挥 LLMs 潜力的旅程才刚刚开始,未来的道路充满了激动人心的前景。
检索增强生成(RAG)的实际局限性与优势
·发表于 Towards Data Science ·阅读时间:4 分钟·2024 年 4 月 15 日
--
RAG 的价值
想象一下 RAG 是一个高度智能的图书管理员,他能够在几秒钟内从数字图书馆中筛选出答案。有时,图书管理员能够找到相关且有用的信息来回答你的问题,但有时他们也会失误。

来源:Dalle3
让我们来探讨 RAG 在何种情况下表现出色,又在哪些情况下会有所不足。在未来的工作中,我将探讨一系列可以单独使用或组合使用的方法,以提高 RAG 的能力——这些方法将有助于在与语言模型一起使用时提供更好的响应。
RAG 的不足之处
即使是最聪明的图书管理员也面临挑战,其中一些挑战包括迭代推理的能力,确保他们能够检索到最有用的文档,以及确保他们所获取的信息是相关的且没有偏见的。
通过迭代推理拼凑拼图:当前 RAG 的一个关键局限性是缺乏迭代推理能力。RAG 无法完全理解所检索的数据是否是语言模型解决问题所需的最相关信息。
例如,如果你提出这样一个问题:“2024 年通过的新环保法规对我最新的白皮书有什么影响?”一个启用 RAG 的系统将尝试检索与查询最语义相似的数据。它可能会返回关于新政策的前 X 个文档,但这些文档是否是与用户引用的特定论文相关的政策呢?
作为人类,我们会用推理能力来处理这个问题。我们会首先阅读白皮书以理解其内容,然后确定哪些环境政策最适用。接着,基于这些知识,我们会进行搜索,寻找相关的白皮书。这种迭代推理过程——理解问题、制定更有针对性的搜索策略、然后检索最有用的信息——是当前 RAG 实现所缺乏的能力。
组织结构至关重要:RAG(检索增强生成)的性能和效果在很大程度上依赖于其访问的底层数据的组织和结构。检索算法识别并提取最有用文档的能力,受信息如何分类和存储,以及查询与检索到的数据在语义上有多相似的影响。
在我们的图书馆类比中,假设有 500 本关于各种主题的书籍被杂乱地摆放在一个书架上,没有任何分类或标签。试图找到最相关的资源来回答特定问题将是一项艰巨的任务。你可能会偶然发现一些潜在有用的书籍,但没有可靠的方式评估哪些书籍包含最相关的信息。如果这 500 本书按类别整理,并且有清晰的元数据和主题标签,那么检索过程将变得显著更加高效和有效。RAG 实现不再是盲目扫描整个书架,而是可以迅速锁定最相关的部分。
同样的原则适用于数据在实际应用中如何存储和索引,以供 RAG 实现使用。如果底层数据集缺乏一致的组织、分类和元数据,检索算法将难以识别最有价值的信息。确保数据得到妥善结构化、分类和可访问是至关重要的。
优点、缺点与偏见:RAG 实现检索的数据质量与它能够访问的数据质量息息相关。如果底层源系统中的信息——无论是数据库、在线文件存储还是其他数据存储库——包含过时、不完整或有偏见的内容,RAG 实现将无法辨别这一点。它只会检索并传递这些有缺陷的信息给负责生成最终输出的语言模型。
RAG 模型的优势所在
访问领域特定和机密信息:RAG 的一个关键优势是能够利用领域特定的甚至是机密信息,这些信息可能没有包含在语言模型的标准训练数据中。这对从事专有的、前沿研究和项目的组织尤其有益。例如,如果一家公司正在进行尚未公开发布的量子计算前沿研究,RAG 实现可以获得对这些内部数据源的访问。这将使语言模型能够获取专门的知识,参与讨论公司最新的研究成果,而无需在这些机密信息上进行训练。
然而,将敏感的内部数据暴露给外部托管的语言模型(如 GPT、LLAMA 等)并非没有风险。组织必须谨慎行事,确保采取适当的数据安全措施,以保护其知识产权和机密信息。
将最新新闻带入对话:RAG 的一个关键优势是能够为语言模型提供访问最新信息的能力,超越语言模型原始训练数据的固定截止日期。如果一个语言模型仅依赖于其固有知识,它所能获得的信息就仅限于训练时可用的内容。
另一方面,RAG 实现可以与实时数据源(如互联网、不断更新的数据库、新闻源等)集成。这使得语言模型能够在生成响应时利用最新的信息。
结论
检索增强生成(RAG)是一种强大的技术,通过提供超越初始训练的大量信息,可以增强语言模型。然而,重要的是要意识到 RAG 的局限性,比如需要迭代推理、数据组织的重要性以及潜在的偏见或过时信息。在未来的工作中,我将探讨一系列方法,以提升 RAG 的能力——提升语言模型生成响应的质量。
构建可靠 AI 应用程序的 LLM 三角原则
软件设计原则,用于精心设计可靠且高性能的 LLM 应用程序。这是一个弥合潜力与生产级性能之间差距的框架。
·发表于Towards Data Science ·阅读时间 16 分钟·2024 年 7 月 16 日
--
大型语言模型(LLM)具有巨大的潜力,但开发可靠的生产级应用程序仍然具有挑战性。经过构建数十个 LLM 系统后,我将成功的公式提炼为 3+1 个基本原则,任何团队都可以应用。
“LLM 本地应用程序是 10%的复杂模型,和 90%的实验性数据驱动的工程工作。”
构建生产就绪的 LLM 应用程序需要细致的工程实践。当用户无法直接与 LLM 互动时,提示语必须精心编写,以涵盖所有细节,因为迭代的用户反馈可能不可用。
介绍 LLM 三角原则
LLM 三角原则概括了构建有效 LLM 本地应用程序的基本指南。它们提供了一个坚实的概念框架,引导开发人员构建健壮且可靠的 LLM 本地应用程序,并提供方向和支持。

通过 SOP 的视角来优化三个突出原则,可以实现最佳的 LLM 使用。(图片来源:作者)
关键顶点
LLM 三角原则介绍了四个编程原则,帮助您设计和构建 LLM 本地应用程序。
第一个原则是标准操作程序(SOP)。SOP 引导着我们三角形的三个顶点:模型、工程技术和上下文数据。
通过SOP 的视角来优化三个顶点的原则是确保高性能LLM 本地应用的关键。
1. 标准操作程序(SOP)
标准操作程序(SOP)是工业界的一个著名术语。它是一套由大组织编写的逐步指令,帮助工人完成日常操作,并确保每次都能保持高质量和相似的结果。这实际上通过编写详细的指令,将没有经验或技术不熟练的工人转变为专家。
LLM 三角原则借鉴了 SOP 范式,并鼓励你将模型视为一个经验不足/技术不熟练的工作人员。通过“教导”模型专家如何完成任务,我们可以确保更高质量的结果。

SOP 指导原理。(图片来源:作者)
“没有 SOP,即使是最强大的 LLM 也无法持续交付高质量的结果。”
在思考SOP 指导原理时,我们应该识别哪些技术将帮助我们最有效地实施 SOP。
1.1. 认知建模
要创建一个 SOP,我们需要选出表现最好的工人(领域专家),建模他们如何思考和工作以实现相同的结果,并将他们所做的每一步都记录下来。
在编辑和正式化后,我们将拥有详细的指令,帮助每一位经验不足或技能较低的工人取得成功,并产出卓越的工作成果。
像人类一样,通过简化或分解任务来减轻认知负担是至关重要的。遵循简单的逐步指令比繁琐复杂的程序更直接。
在这个过程中,我们识别出隐藏的隐性认知 “跳跃” —— 专家在无意识中采取的微小步骤,这些步骤会显著影响结果。这些细微的、无意识的、通常未言明的假设或决策,可能会大大影响最终结果。

一个“隐性认知跳跃”的示例。(图片来源:作者)
例如,假设我们想要建立一个 SQL 分析师的模型。我们将通过面谈他们并询问几个问题,例如:
-
当你被要求分析一个商业问题时,你会怎么做?
-
你如何确保你的解决方案满足需求?
-
<将我们理解的过程反馈给面试对象>
-
这准确地反映了你的流程吗? <获取修正>
-
等等。

分析师进行的认知过程的一个示例,以及如何建模它。(图片来源:作者)
隐性认知过程有许多表现形式;一个典型的例子是“领域特定定义”。例如,“畅销书”可能是我们领域专家的一个显著术语,但对其他人来说却不一定如此。

扩展我们 SQL 分析师示例中的隐性认知过程。(图片来源:作者)
最终,我们将拥有一个完整的 SOP“配方”,使我们能够模仿我们顶尖的分析员。
在绘制这些复杂的过程时,将它们可视化为图形可能会非常有帮助。当过程非常细致,并且涉及许多步骤、条件和分支时,这尤其有用。

“SQL 分析员 SOP”包括所有必要的技术步骤,以图形方式呈现。(图片由作者提供)
我们的最终解决方案应模仿 SOP 中定义的步骤。在此阶段,尽量忽略实现细节——稍后,你可以在我们的解决方案中的一个或多个步骤/链条中实现它。
与其他原则不同,认知建模(SOP 编写)是唯一独立的过程。强烈建议在编写代码之前先对你的过程进行建模。话虽如此,在实施过程中,你可能会根据获得的新见解或理解,回头对其进行修改。
现在我们理解了制定清晰 SOP 的重要性,它指导了我们对问题的业务理解,接下来让我们探索如何利用各种工程技巧有效地实现它。
2. 工程技巧
工程技巧帮助你实际实施 SOP 并从模型中获得最大收益。在思考工程技巧原则时,我们应该考虑哪些工具(技巧)能帮助我们实施和塑造 SOP,并协助模型与我们有效沟通。

工程技巧原则。(图片由作者提供)
一些工程技巧仅在提示层中实现,而许多技巧则需要在软件层中才能生效,另外一些则结合了这两者。

工程技巧层。(图片由作者提供)
虽然每天都会发现许多小细节和技巧,但我将涵盖两种主要技巧:工作流/链条和代理。
2.1. LLM 原生架构(也叫流工程或链条)
LLM 原生架构描述了你的应用程序执行任务结果的代理流。
我们流程中的每个步骤都是一个独立的过程,必须执行才能实现我们的任务。一些步骤将仅通过确定性代码执行;对于某些步骤,我们将使用 LLM(代理)。
为了做到这一点,我们可以反思我们绘制的标准操作程序(SOP),并思考:
-
我们应该将哪些 SOP 步骤组合到同一个代理中?又应该将哪些步骤拆分为不同的代理?
-
哪些 SOP 步骤应该独立执行(但它们可能会接收来自前一步骤的信息)?
-
我们可以在确定性代码中执行哪些 SOP 步骤?
-
等等。

基于给定 SOP 的“维基百科写作员”LLM 原生架构示例。(图片由作者提供)
在进入我们架构/图表的下一步之前,我们应该定义其关键属性:
-
输入和输出 — 这一步的签名是什么?我们在采取行动之前需要什么?(这也可以作为代理的输出格式)
-
质量保证— 什么样的响应才算是“足够好”?是否有需要人工干预的情况?我们可以配置什么样的断言?
-
自主级别 — 我们需要对结果质量的控制有多大?这一阶段能处理什么范围的用例?换句话说,在这一点上,我们能多大程度上信任模型独立工作?
-
触发器 — 下一步是什么?是什么决定了下一步?
-
非功能性 — 需要什么样的延迟?我们是否需要在这里进行特别的业务监控?
-
故障转移控制 — 可能发生什么样的故障(系统性和代理性)?我们的备选方案是什么?
-
状态管理 — 我们需要特殊的状态管理机制吗?我们如何检索/保存状态(定义索引键)?我们需要持久存储吗?这个状态有哪些不同的使用场景(例如,缓存、日志记录等)?
-
等等。
2.2. 什么是代理?
LLM 代理是 LLM-Native 架构中的独立组件,涉及调用 LLM。
这是一个包含上下文的 LLM 使用实例。并非所有代理都是相同的——有些会使用“工具”,有些则不会;有些可能只在流程中“使用一次”,而其他的则可以递归调用或多次调用,携带之前的输入和输出。
2.2.1. 带工具的代理
一些 LLM 代理可以使用“工具”——用于执行任务如计算或网页搜索的预定义函数。代理输出指定工具和输入的指令,应用程序执行这些指令,并将结果返回给代理。
为了理解这个概念,我们来看看一个简单的提示实现,看看如何调用工具。即使是没有原生训练来调用工具的模型也能这样工作:
You are an assistant with access to these tools:
- calculate(expression: str) -> str - calculate a mathematical expression
- search(query: str) -> str - search for an item in the inventory
Given an input, Respond with a YAML with keys: `func`(str) and `arguments`(map) or `message`(str).Given input
区分带工具的代理(因此是自主代理)和能够导致执行动作的代理非常重要。
“自主代理是具有生成完成任务方式能力的代理。”
自主代理被赋予权力来决定是否采取行动以及采取什么行动。相比之下,(非自主)代理仅仅“处理”我们的请求(例如,分类),并基于这一过程,我们的确定性代码执行动作,模型对此没有任何控制。

自主代理与触发动作的代理。(图片来自作者)
随着我们提高代理在规划和执行任务中的自主性,我们增强了其决策能力,但可能会降低对输出质量的控制。虽然这看起来像是一种神奇的解决方案,使其变得更加“智能”或“先进”,但这也意味着我们将失去对质量的控制。

自主代理的权衡。(图片来自作者)
警惕完全自主代理的诱惑。尽管它们的架构看起来可能很有吸引力且更简单,但将其用于一切(或作为初始的 PoC)可能会从“真实生产”案例中产生误导。自主代理很难调试且不可预测(响应质量不稳定),这使得它们无法用于生产。
目前,代理(没有隐式引导)在规划复杂流程方面并不擅长,通常会跳过关键步骤。例如,在我们的“维基百科写作”用例中,它们会直接开始写作,跳过系统化的过程。这使得代理(特别是自主代理)仅能像模型一样好,或者更准确地说——仅能像它们相对于你的任务所训练的的数据一样好。
与其让代理(或一群代理)自由地做所有的端到端任务,不如尝试将它们的任务限定在你的流程/SOP 的特定区域,这些区域需要这种灵活性或创造性。这可以产生更高质量的结果,因为你可以享受两者的优点。
一个很好的例子是AlphaCodium:通过将结构化流程与不同的代理(包括一个迭代编写和测试代码的新型代理)结合,他们将 GPT-4 在 CodeContests 上的准确率(pass@5)从 19%提高到 44%。

AlphaCodium 的 LLM 架构。(图片由Codium.ai提供)
虽然工程技术为实施我们的 SOP 和优化 LLM 原生应用奠定了基础,但我们还必须仔细考虑 LLM 三角形中的另一个关键组成部分:模型本身。
3. 模型
我们选择的模型是项目成功的关键组成部分——一个大型模型(如 GPT-4 或 Claude Opus)可能会产生更好的结果,但在大规模应用时成本非常高,而一个较小的模型可能不那么“智能”,但可以帮助节省预算。在思考模型原则时,我们应当确定我们的约束和目标,并找出哪种类型的模型可以帮助我们实现这些目标。

模型原则。(图片由作者提供)
“并非所有 LLM 都是平等的。将模型与任务匹配。”
事实是,我们并不总是需要最大的模型;这取决于任务。为了找到合适的匹配,我们必须有一个实验过程,并尝试多种变体的解决方案。
借鉴我们的“经验不足的工人”类比——一个非常“聪明”的工人拥有许多学术资质,可能会轻松完成某些任务,但他们可能对这份工作来说资历过高,雇佣一个“更便宜”的候选人将更加具有成本效益。
在考虑一个模型时,我们应根据愿意承受的权衡来定义和比较解决方案:
-
任务复杂性 — 较简单的任务(如摘要生成)可以用较小的模型更容易完成,而推理通常需要更大的模型。
-
推理基础设施 — 它应该在云端运行还是在边缘设备上运行?模型的大小可能对小型手机产生影响,但对于云端服务而言可以容忍。
-
定价 — 我们能够接受什么价格?从商业影响和预测使用角度来看,这是否具有成本效益?
-
延迟 — 随着模型的增大,延迟也会增加。
-
标注数据 — 我们是否有可以立即使用的数据,用来通过示例或相关信息丰富模型,而这些数据并未经过训练?
在许多情况下,直到你拥有“内部专业知识”,聘请一个有经验的工作人员可能会有助益 —— 对 LLM 也是如此。
如果你没有标注数据,可以从一个更强(更大的)模型开始,收集数据,然后利用它通过少量样本或微调来赋能模型。
3.1. 微调模型
在对模型进行微调之前,有几个方面是必须考虑的:
-
隐私 — 你的数据可能包含必须从模型中隔离的私人信息。如果数据中包含私人信息,你必须对数据进行匿名化,以避免法律责任。
-
法律、合规性与数据权益 — 在训练模型时可能会出现一些法律问题。例如,OpenAI 的使用条款政策禁止你在没有 OpenAI 的情况下训练一个模型,使用生成的响应。另一个典型的例子是遵守 GDPR 法律,要求提供“撤销权”,用户可以要求公司从系统中删除信息。这就引发了关于是否需要重新训练模型的法律问题。
-
更新延迟 — 训练模型时,延迟或数据截止时间要高得多。与通过上下文嵌入新信息(见下文“4. 上下文数据”部分)提供即时延迟不同,训练模型是一个需要时间的长期过程。因此,模型的重新训练频率较低。
-
开发与运维 — 实施一个可重复、可扩展且可监控的微调管道至关重要,同时需要持续评估结果的性能。这个复杂的过程需要不断的维护。
-
成本 — 由于重新训练的复杂性和每次训练所需的大量资源(如 GPU),重新训练被认为是昂贵的。
LLM 的能力,作为上下文学习者,以及新模型支持更大上下文窗口的事实,极大简化了我们的实现,并且即使没有微调也能提供优秀的结果。由于微调的复杂性,建议将其作为最后的手段,或完全跳过。
相反,为特定任务(例如,结构化 JSON 输出)或特定领域语言对模型进行微调可能会非常高效。一个小型的、任务特定的模型可能非常有效,并且在推理时比大型 LLM 更便宜。明智地选择你的解决方案,并在决定进行 LLM 训练之前,评估所有相关的考虑因素。
“即使是最强大的模型,也需要相关且结构良好的上下文数据才能发挥作用。”
4. 上下文数据
LLMs 是上下文学习者。 这意味着通过提供特定任务的信息,LLM 代理可以帮助我们执行任务,无需特殊的训练或微调。这使得我们能够轻松地“教授”新知识或技能。在考虑上下文数据原则时,我们应该目标是组织和建模可用数据,并在提示中如何组合这些数据。

上下文数据原则。(图片由作者提供)
为了构建我们的上下文,我们将相关的(上下文)信息包含在发送给 LLM 的提示中。我们可以使用两种类型的上下文:
- 嵌入式上下文 — 作为提示的一部分提供的嵌入信息
You are the helpful assistant of <name>, a <role> at <company>
- 附件上下文 — 一组由提示开始/结束连接的信息
Summarize the provided emails while keeping a friendly tone.
---
<email_0>
<email_1>
上下文通常使用“提示模板”来实现(例如,jinja2 或 mustache 或简单的原生格式化字面量字符串);这样,我们可以优雅地组合它们,同时保持提示的核心内容:
# Embedded context with an attachment context
prompt = f"""
You are the helpful assistant of {name}. {name} is a {role} at {company}.
Help me write a {tone} response to the attached email.
Always sign your email with:
{signature}
---
{email}
"""
4.1. 少量学习
少量学习是一种强大的方式,通过示例“教” LLM,而无需进行大量微调。提供一些具有代表性的示例可以指导模型理解期望的格式、风格或任务。
例如,如果我们希望大型语言模型(LLM)生成电子邮件回复,可以在提示中包含一些写得很好的回复示例。这有助于模型学习首选的结构和语气。
我们可以使用多样化的示例,帮助模型捕捉到不同的边界情况或细微差别,并从中学习。因此,包含涵盖应用程序可能遇到的各种场景的多种示例是至关重要的。
随着应用程序的增长,您可能会考虑实现“动态少量学习”,该方法涉及程序化地为每个输入选择最相关的示例。虽然这增加了实现的复杂性,但它确保模型为每种情况提供最合适的指导,从而显著提高了在广泛任务中的性能,而无需昂贵的微调。
4.2. 检索增强生成
检索增强生成(RAG)是一种在生成响应之前检索相关文档以提供额外上下文的技术。它就像是给 LLM 一个快速浏览特定参考材料的机会,以帮助它形成答案。这使得响应保持最新且准确,而无需重新训练模型。
例如,在支持聊天应用中,RAG 可以提取相关的帮助台 Wiki 页面来帮助 LLM 回答问题。
这种方法通过将回答建立在检索到的事实基础上,帮助 LLM 保持最新 并 减少幻觉。RAG 对于需要更新或专业知识的任务特别有用,而不需要重新训练整个模型。
例如,假设我们正在为我们的产品构建支持聊天。在这种情况下,我们可以使用 RAG 从帮助台 Wiki 中检索相关文档,然后将其提供给 LLM 代理,并要求它基于问题生成回答并提供文档。
在实现 RAG 时,有三个关键点需要关注:
-
检索机制—传统的 RAG 实现方式是通过向量相似度搜索来检索相关文档,但有时使用更简单的方法,如基于关键词的搜索(如BM-25),可能更好或更便宜。
-
索引数据结构—天真地索引整个文档,而不进行预处理,可能会限制检索过程的效果。有时,我们需要添加一个数据准备步骤,例如基于文档准备一份问题和答案的列表。
-
元数据—存储相关的元数据可以更高效地引用和筛选信息(例如,将 Wiki 页面缩小到仅与用户特定产品查询相关的页面)。这层额外的数据结构可以简化检索过程。
4.3. 提供相关上下文
与您的代理相关的上下文信息可能有所不同。虽然看起来提供过多的信息可能有益,但如果给模型(比如“非熟练工人”)提供太多信息,可能会让其感到不知所措且与任务无关。从理论上讲,这会导致模型学习无关的信息(或令牌连接),从而可能导致混淆和幻觉。
当 Gemini 1.5 发布并作为一个能够处理最多 1000 万个令牌的 LLM 时,一些从业者质疑上下文是否仍然是一个问题。虽然这是一个了不起的成就,特别是对于某些用例(例如与 PDF 聊天),但它仍然有限,特别是在推理多个文档时。
压缩提示并仅向 LLM 代理提供相关信息至关重要。这可以减少模型在无关令牌上的处理能力,提升质量,优化延迟,并降低成本。
有许多技巧可以提高提供上下文的相关性,其中大多数与如何存储和分类数据有关。
对于 RAG 应用程序,增加一种数据准备方法是非常有用的,它能塑造您存储的信息(例如,基于文档的问答,然后仅向 LLM 代理提供答案;这样,代理获得的是一个总结的、较短的上下文),并在检索到的文档上使用重新排序算法来细化结果。
“数据为 LLM 原生应用程序提供动力。上下文数据的战略性设计释放了它们的真正潜力。”
结论与启示
LLM 三角原则提供了一种结构化的方法,用于开发高质量的 LLM 原生应用程序,解决了 LLM 巨大潜力与实际应用挑战之间的差距。开发人员可以通过关注 3+1 个关键原则——模型、工程技术和上下文数据——并由明确定义的SOP指导,来创建更可靠和有效的 LLM 驱动解决方案。

LLM 三角原则。(图片由作者提供)
关键要点
-
从明确的 SOP 开始:模拟专家的认知过程,为您的 LLM 应用程序创建一个逐步指南。在考虑其他原则时,使用它作为指导。
-
选择合适的模型:平衡能力与成本,并考虑先使用更大的模型,再可能转向较小的、微调过的模型。
-
利用工程技术:实现 LLM 原生架构,并战略性地使用代理来优化性能和保持控制。尝试不同的提示技术,找到最适合您案例的提示。
-
提供相关上下文:在适当时使用上下文学习,包括 RAG,但要小心不要用无关的信息让模型感到过载。
-
迭代和实验:找到正确的解决方案通常需要测试和完善您的工作。我建议阅读并实施《构建 LLM 应用程序:清晰的逐步指南》,以获得详细的 LLM 原生开发过程指南。
通过应用 LLM 三角原则,组织可以超越简单的概念验证,开发出强大、可投入生产的 LLM 应用程序,真正发挥这一变革性技术的力量。
如果您觉得这篇白皮书对您有帮助,请在 Medium 上给它一些掌声 👏,并分享给您的 AI 爱好者朋友们。您的支持对我意义重大!🌍
让我们继续交流——随时通过电子邮件或在 LinkedIn 上联系 🤝
特别感谢Gal Peretz、Gad Benram、Liron Izhaki Allerhand、Itamar Friedman、Lee Twito、Ofir Ziv、Philip Tannor、Yair Livne和Shai Alon提供的见解、反馈和编辑意见。
预测准确度的机器学习指南:插值与外推
评估超越训练数据的机器学习模型
·发布于 Towards Data Science ·13 分钟阅读·2024 年 7 月 4 日
--
介绍
近年来,数据驱动的方法,如机器学习(ML)和深度学习(DL),已经被应用于广泛的任务,包括机器翻译和个性化推荐。这些技术通过分析大量数据,从给定的训练数据集中揭示出一些模式。然而,如果给定的数据集存在偏差,并且不包含你希望了解或预测的数据,那么从训练后的模型中获得正确答案可能会很困难。

图片由 Stephen Dawson 提供,来自 Unsplash
让我们思考一下 ChatGPT 的案例。此时最新版本的 ChatGPT 是 ChatGPT 4o,该模型是在 2023 年 6 月之前的数据上进行训练的(本文发布时的情况)。因此,如果你询问 2024 年发生的、训练数据中未包含的事情,你将无法获得准确的答案。这种情况被称为“幻觉”,OpenAI 已经增加了预处理程序,在此类问题上返回固定的“无法回答”答案。另一方面,ChatGPT 的训练数据基本上是基于英文文档的,因此它在非英语母语国家(如日本和法国)中的地方性领域知识上不太擅长。因此,许多公司和研究团队投入大量精力,通过使用 RAG(检索增强生成)或微调,将地区或领域特定的知识融入到他们的 LLM 中。
因此,识别所使用的训练数据对于理解 AI 模型的适用性和局限性非常重要。另一方面,数据驱动方法面临的最大挑战之一是,这些技术通常需要在训练数据集的范围之外进行操作。这些需求通常出现在新产品开发、材料科学中的新药物化合物效果预测,以及在市场上推出产品时预测消费者行为等领域。这些场景要求在稀疏区域和训练数据之外做出正确的预测,这涉及到插值和外推。

插值是指在已知数据范围内进行预测。如果训练数据分布密集且均匀,则可以在该范围内获得准确的预测。然而,在实际应用中,准备这种数据并不常见。另一方面,外推是指在已知数据点范围之外进行预测。尽管在这些区域内的预测是高度期望的,但数据驱动的方法通常在此方面最为困难。因此,理解每个算法在插值和外推方面的表现非常重要。

作者创建
本文考察了各种机器学习算法的插值和外推能力。我们准备了一个人工训练数据集,并通过可视化每个模型的预测结果来评估这些能力。机器学习算法的目标如下:
-
符号回归器
-
SVR(支持向量回归)
-
高斯过程回归器(GPR)
-
决策树回归器
-
随机森林回归器
-
XGBoost
-
LightGBM
此外,我们还评估了集成模型,如投票回归器和堆叠回归器。
代码
完整代码请见以下链接:
[## blog_TDS/02_compare_regression at main · rkiuchir/blog_TDS
数据科学之道博客内容。通过在 GitHub 上创建帐户,贡献于 rkiuchir/blog_TDS 的开发。
数据生成与预处理
首先,我们使用一个简单的非线性函数生成人工数据,该函数是从gplearn 中的符号回归教程稍作修改而来,修改部分为添加了指数项。该函数包含线性、二次和指数项,定义如下:

其中,x₀和x₁的范围为-1 到 1。实际值的平面如下所示:
由于我们在插值和外推方面评估每个机器学习模型的表现,因此每种情况都需要不同的数据集。
对于插值,我们在与训练数据集相同的范围内评估模型的表现。因此,每个模型都将在-1 到 1 的范围内使用离散化的数据点进行训练,并在相同的范围内评估预测表面。
另一方面,对于外推,模型在训练数据集范围外的能力是必需的。我们将使用在-0.5 到 1 的范围内离散化的数据点来训练模型,涵盖x₀和x₁,并在-1 到 1 的范围内评估预测表面。因此,在-1 到-0.5 的范围内,x₀和x₁的实际值与预测表面之间的差异揭示了模型在外推方面的能力。
在本文中,还将通过检查 20 个点和 100 个点这两种情况,评估训练数据集点数的影响。
例如,100 个数据点如下生成:
import numpy as np
def target_function(x0, x1):
return x0**2 - x1**2 + x1 + np.exp(x0) - 1
# Generate training data for interpolation
X_train = rng.uniform(-1, 1, 200).reshape(100, 2)
y_train = target_function(X_train[:, 0], X_train[:, 1])
# Generate training data for extrapolation
X_train = rng.uniform(-0.5, 1, 200).reshape(100, 2)
y_train = target_function(X_train[:, 0], X_train[:, 1])
机器学习算法简介
在本文中,我们评估了七种主要机器学习算法在插值和外推方面的表现。此外,还考虑了使用这七种算法的六种集成模型。每种算法都有不同的结构和特点,这些特点会对预测性能产生优缺点。我们在此总结了每种算法的特点如下:
- 符号回归
-
训练后的模型通过基于遗传算法拟合的数学表达式来表示。
-
模型被定义为函数,具有很高的可解释性。
-
适合于目标变量可以表示为特征函数的任务。
-
擅长插值,但在外推方面可能有一定的潜力。
自动发现基本公式,如开普勒定律和牛顿定律
towardsdatascience.com
2. 支持向量回归(SVR)
-
基于支持向量机(SVM),可以通过核方法有效处理高维空间中的非线性关系
-
使用不同类型的核函数,如线性核、RBF 核、多项式核和 sigmoid 核,模型可以表达复杂的数据模式
-
擅长插值,但在外推时稳定性较差
理解其内部原理,并在四种不同场景下实现支持向量机(SVM)
towardsdatascience.com
3. 高斯过程回归(GPR)
-
基于贝叶斯方法,预测结果以概率形式表示,包括预测值及其不确定性
-
由于不确定性估计,GPR 被用于贝叶斯优化
-
使用不同类型的核函数,如线性核、RBF 核、多项式核和 sigmoid 核,模型可以表达复杂的数据模式
-
擅长插值,选择合适的核函数后,也具有一定的外推潜力
一份快速指南,帮助理解高斯过程回归(GPR)并使用 scikit-learn 的 GPR 包
towardsdatascience.com
4. 决策树
-
简单的树状算法,通过不断分割数据
-
易于理解和解释,但容易过拟合
-
步骤状估计适用于插值,但不擅长外推
决策树是一种类似流程图的结构,其中每个内部节点表示对特征的测试(例如是否…
towardsdatascience.com
5. 随机森林
-
一种基于集成的算法,称为“Bagging”,由多个决策树组成
-
通过结合多个不同的树,该算法能够降低过拟合的风险,并且具有较高的插值性能
-
比单个决策树具有更稳定的预测,但不擅长外推
算法如何工作以及为什么如此有效
towardsdatascience.com
6. XGBoost
-
一种基于集成的算法,叫做“提升法”,通过顺序减少错误将多个决策树结合起来
-
由于优秀的预测性能,通常用于 Kaggle 等竞赛
-
比单个决策树具有更稳定的预测,但不擅长外推
每天我们都听到关于人工智能突破的消息。然而,你有没有想过它面临的挑战是什么……
7. LightGBM
-
类似于 XGBoost,但具有更快的训练速度和更高的内存效率,更适用于较大的数据集
-
比单个决策树具有更稳定的预测,但不擅长外推
[## 什么是 LightGBM,如何实现它?如何调整参数?
你好,
8. 投票回归器
-
一种集成学习方法,通过结合多个模型的预测进行预测
-
混合不同模型的特性,能提供比单一模型更稳健的预测
-
本文中评估了三种组合:
– 支持向量回归器 + 随机森林
– 高斯过程回归器 + 随机森林
– 随机森林 + XGBoost
示例图库:绘制单个和投票回归预测
scikit-learn.org](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.VotingRegressor.html?source=post_page-----45dd270ee871--------------------------------)
9. 堆叠回归器
-
一种集成学习方法,使用多个模型的预测作为最终预测模型的输入,称为“元模型”
-
元模型覆盖单个模型的弱点,并结合每个模型的优点
-
本文中评估了三种组合:
– 基础模型:支持向量回归 + 随机森林;元模型:随机森林
– 基础模型:高斯过程回归 + 随机森林;元模型:随机森林
– 基础模型:随机森林 + XGBoost;元模型:随机森林
画廊示例:使用堆叠法组合预测器
使用这些算法,我们将评估使用我们之前生成的数据集的插值和外推性能。在接下来的部分中,将解释每个模型的训练方法和评估方法。
模型训练与评估
预处理
基本上,除了基于树的方法,如随机森林、XGBoost 和 LightGBM,大多数机器学习算法都需要特征缩放。然而,由于我们在这个实践中只使用了两个特征,如x₀和x₁,它们的范围相同,分别为-1 到 1(插值)或-0.5 到 1(外推),因此我们将跳过特征缩放。
模型训练
为简化起见,除 LightGBM 外,所有算法都使用默认超参数,LightGBM 的默认参数适用于较大的数据集。
如前所述,我们将在模型训练期间使用不同的数据集来评估插值和外推。
评估与可视化
在模型训练后,我们将使用非常细致的数据进行预测。基于这些预测值,将使用Plotly 表面函数绘制预测曲面。
这些过程由以下代码完成:
class ModelFitterAndVisualizer:
def __init__(self, X_train, y_train, y_truth, scaling=False, random_state=41):
"""
Initialize the ModelFitterAndVisualizer class with training and testing data.
Parameters:
X_train (pd.DataFrame): Training data features
y_train (pd.Series): Training data target
y_truth (pd.Series): Ground truth for predictions
scaling (bool): Flag to indicate if scaling should be applied
random_state (int): Seed for random number generation
"""
self.X_train = X_train
self.y_train = y_train
self.y_truth = y_truth
self.initialize_models(random_state)
self.scaling = scaling
# Initialize models
# -----------------------------------------------------------------
def initialize_models(self, random_state):
"""
Initialize the models to be used for fitting and prediction.
Parameters:
random_state (int): Seed for random number generation
"""
# Define kernel for GPR
kernel = 1.0 * RBF(length_scale=1.0) + WhiteKernel(noise_level=1.0)
# Define Ensemble Models Estimator
# Decision Tree + Kernel Method
estimators_rf_svr = [
('rf', RandomForestRegressor(n_estimators=30, random_state=random_state)),
('svr', SVR(kernel='rbf')),
]
estimators_rf_gpr = [
('rf', RandomForestRegressor(n_estimators=30, random_state=random_state)),
('gpr', GaussianProcessRegressor(kernel=kernel, normalize_y=True, random_state=random_state))
]
# Decision Trees
estimators_rf_xgb = [
('rf', RandomForestRegressor(n_estimators=30, random_state=random_state)),
('xgb', xgb.XGBRegressor(random_state=random_state)),
]
self.models = [
SymbolicRegressor(random_state=random_state),
SVR(kernel='rbf'),
GaussianProcessRegressor(kernel=kernel, normalize_y=True, random_state=random_state),
DecisionTreeRegressor(random_state=random_state),
RandomForestRegressor(random_state=random_state),
xgb.XGBRegressor(random_state=random_state),
lgbm.LGBMRegressor(n_estimators=50, num_leaves=10, min_child_samples=3, random_state=random_state),
VotingRegressor(estimators=estimators_rf_svr),
StackingRegressor(estimators=estimators_rf_svr,
final_estimator=RandomForestRegressor(random_state=random_state)),
VotingRegressor(estimators=estimators_rf_gpr),
StackingRegressor(estimators=estimators_rf_gpr,
final_estimator=RandomForestRegressor(random_state=random_state)),
VotingRegressor(estimators=estimators_rf_xgb),
StackingRegressor(estimators=estimators_rf_xgb,
final_estimator=RandomForestRegressor(random_state=random_state)),
]
# Define graph titles
self.titles = [
"Ground Truth", "Training Points",
"SymbolicRegressor", "SVR", "GPR",
"DecisionTree", "RForest",
"XGBoost", "LGBM",
"Vote_rf_svr", "Stack_rf_svr__rf",
"Vote_rf_gpr", "Stack_rf_gpr__rf",
"Vote_rf_xgb", "Stack_rf_xgb__rf",
]
def fit_models(self):
"""
Fit the models to the training data.
Returns:
self: Instance of the class with fitted models
"""
if self.scaling:
scaler_X = MinMaxScaler()
self.X_train_scaled = scaler_X.fit_transform(self.X_train)
else:
self.X_train_scaled = self.X_train.copy()
for model in self.models:
model.fit(self.X_train_scaled, self.y_train)
return self
def visualize_surface(self, x0, x1, width=400, height=500,
num_panel_columns=5,
vertical_spacing=0.06, horizontal_spacing=0,
output=None, display=False, return_fig=False):
"""
Visualize the prediction surface for each model.
Parameters:
x0 (np.ndarray): Meshgrid for feature 1
x1 (np.ndarray): Meshgrid for feature 2
width (int): Width of the plot
height (int): Height of the plot
output (str): File path to save the plot
display (bool): Flag to display the plot
"""
num_plots = len(self.models) + 2
num_panel_rows = num_plots // num_panel_columns
whole_width = width * num_panel_columns
whole_height = height * num_panel_rows
specs = [[{'type': 'surface'} for _ in range(num_panel_columns)] for _ in range(num_panel_rows)]
fig = make_subplots(rows=num_panel_rows, cols=num_panel_columns,
specs=specs, subplot_titles=self.titles,
vertical_spacing=vertical_spacing,
horizontal_spacing=horizontal_spacing)
for i, model in enumerate([None, None] + self.models):
# Assign the subplot panels
row = i // num_panel_columns + 1
col = i % num_panel_columns + 1
# Plot training points
if i == 1:
fig.add_trace(go.Scatter3d(x=self.X_train[:, 0], y=self.X_train[:, 1], z=self.y_train,
mode='markers', marker=dict(size=2, color='darkslategray'),
name='Training Data'), row=row, col=col)
surface = go.Surface(z=self.y_truth, x=x0, y=x1,
showscale=False, opacity=.4)
fig.add_trace(surface, row=row, col=col)
# Plot predicted surface for each model and ground truth
else:
y_pred = self.y_truth if model is None else model.predict(np.c_[x0.ravel(), x1.ravel()]).reshape(x0.shape)
surface = go.Surface(z=y_pred, x=x0, y=x1,
showscale=False)
fig.add_trace(surface, row=row, col=col)
fig.update_scenes(dict(
xaxis_title='x0',
yaxis_title='x1',
zaxis_title='y',
), row=row, col=col)
fig.update_layout(title='Model Predictions and Ground Truth',
width=whole_width,
height=whole_height)
# Change camera angle
camera = dict(
up=dict(x=0, y=0, z=1),
center=dict(x=0, y=0, z=0),
eye=dict(x=-1.25, y=-1.25, z=2)
)
for i in range(num_plots):
fig.update_layout(**{f'scene{i+1}_camera': camera})
if display:
fig.show()
if output:
fig.write_html(output)
if return_fig:
return fig
插值性能评估
每个算法的预测曲面分别展示了 100 个和 20 个训练数据点的情况。
100 个训练点:
原始交互式图形可以从这里查看

20 个训练点:
原始交互式图形可以从这里查看

以下是每个算法的总结特征:
符号回归
该算法在使用 100 个数据点进行插值时几乎完美,但在使用 20 个数据点时表现中等。这是因为符号回归近似数学表达式,并且在这个实践中使用了简单的函数形式。由于这个特性,预测曲面显得特别平滑,这与后面介绍的基于树的算法有所不同。
支持向量回归(SVR)、高斯过程回归(GPR)
对于基于核的算法 SVR 和 GPR,尽管预测的表面与真实值略有不同,但在 100 个数据点的情况下,插值性能通常较好。此外,这些模型得到的预测表面平滑,类似于符号回归器估算的表面。然而,在 20 个数据点的情况下,尤其是对于 SVR,预测表面与真实值之间存在显著差异。
决策树、随机森林、XGBoost、LightGBM
首先,这五个基于树的模型估算的预测表面并不平滑,而是呈现出更多阶梯状的形态。这一特征源于决策树的结构和学习方法。决策树通过基于某一特征的阈值递归地划分数据。每个数据点会被分配到某个叶节点,节点内的值表示该节点中数据点的平均值。因此,预测值在每个叶节点内是恒定的,导致了阶梯状的预测表面。
单个决策树的估算清楚地展示了这一特性。另一方面,像随机森林、XGBoost 和 LightGBM 等集成方法,由于每个模型内部包含许多决策树,生成的预测表面相对平滑,因为许多不同形状的决策树基于不同的阈值。
Voting Regressor, Stacking Regressor
Voting Regressor 通过平均两个算法的结果来组合它们。对于如随机森林+SVR 和随机森林+GPR 的组合,预测表面反映了基于核和基于树的模型特性的混合。另一方面,像随机森林和 XGBoost 这样的树基模型组合相较于单一模型估算的预测表面,相对减少了阶梯状形态。
Stacking Regressor 使用一个元模型基于多个模型的输出计算最终预测,表现出阶梯状的预测表面,因为其元模型使用的是随机森林。若使用基于核的算法,如 SVR 或 GPR,作为元模型,则此特性会发生变化。
外推性能评估
如前所述,每个模型的训练数据都包含 x₀ 和 x₁ 范围从 -0.5 到 1 的值,且这些性能将在 -1 到 1 的范围内进行评估。因此,我们可以通过检查预测表面在 x₀ 和 x₁ 范围从 -1 到 -0.5 的外推能力来了解模型的外推表现。
每个算法的预测表面分别显示了 100 和 20 个训练数据点的情况。
100 个训练点:
原始的互动图表可以从这里查看

20 个训练点:
原始的互动图表可以从这里查看

符号回归器
使用 100 个数据点训练的符号回归器在外推范围内获得的预测曲面几乎与插值评估相似,准确地估算。然而,仅使用 20 个训练数据点时,预测曲面与真实值存在较大差异,尤其是在曲面边缘,表明得到的函数形式估算不准确。
支持向量回归器(SVR),高斯过程回归器(GPR)
尽管 SVR 和 GPR 都是基于核的算法,但得到的结果完全不同。对于 20 个和 100 个数据点,SVR 的预测曲面估算得不理想,而 GPR 即使在外推范围内也几乎完美地预测了。
决策树,随机森林,XGBoost,LightGBM
尽管这些基于树的模型的结果存在一些差异,但在外推范围内的预测曲面是稳定的。这是因为决策树依赖于分割,而在外推区域没有生成分割,这导致了常数值。
投票回归器,堆叠回归器
如上所示,基于核的算法相比于基于树的算法具有更好的性能。结合随机森林和 XGBoost 的投票回归器,以及所有三个以随机森林为元模型的堆叠回归器在外推范围内预测稳定。另一方面,结合随机森林+SVR 和随机森林+GPR 的投票回归器的预测曲面,具有基于核和基于树的模型的混合特征。
总结
在本文中,我们评估了各种机器学习算法的插值和外推性能。由于我们使用的真实数据是以简单的函数形式表示的,符号回归器和基于核的算法在外推方面提供了比基于树的算法更好的性能。然而,对于无法通过数学公式表达的更复杂任务,可能会得出不同的结果。
非常感谢阅读这篇文章!希望这篇文章能帮助你理解机器学习模型的插值和外推性能,使你更容易为你的项目选择和应用合适的模型。
你对这篇文章的点赞和对 我的新闻通讯 的订阅将给我带来极大的动力!
链接
其他文章
## OpenAI 的 Sora 如何改变游戏规则:深入了解其核心技术
一项尖端技术的杰作
towardsdatascience.com ## 在 Python 中创建“交互式地球仪 + 地震图”
如何在 Python 中创建一个酷炫的交互式图形:由 Plotly 绘制的地球仪。
towardsdatascience.com ## Pandas 数据预处理备忘单
关于如何使用 Pandas 预处理数据的实用指南
towardsdatascience.com
个人网站
编辑描述
rkiuchir.github.io](https://rkiuchir.github.io/?source=post_page-----45dd270ee871--------------------------------)
从组织数据中获取最大价值的诸多支柱
·发布于Towards Data Science ·10 分钟阅读·2024 年 3 月 30 日
--

这张照片来自 Unsplash 的 Choong Deng Xiang
一个数据科学家的故事
让我为你介绍 Sarah,一位才华横溢、充满热情的数据科学家,她刚刚在 GreenEnv 公司找到了梦寐以求的工作。GreenEnv 是一家生产环保清洁产品的大公司,拥有大量关于客户、产品及其他业务领域的数据。他们雇佣 Sarah 的目的是让她发掘这些数据中蕴藏的潜力,揭示市场趋势、竞争优势等。
她的第一个任务是:分析客户的人口统计和购买习惯,制定有针对性的营销活动。Sarah 对自己的能力充满信心,并激动地准备应用数据科学方法,开始研究客户数据库。然而,她的初衷很快就消退了。数据一团糟——格式不一致、名字拼写错误、重复条目随处可见。数据质量极差。比如,“Jhon Smith”和“Micheal Brown”这种名字变种,旁边还出现了“Jhonn Smtih”和“Michealw Brown”这样的错误记录。邮件地址里有多余的空格,甚至像“gnail.com”这样的拼写错误,应该是“gmail.com”。以及其他许多不准确之处。Sarah 意识到前方的任务艰巨——数据清洗。
不一致的格式、缺失的值和重复数据会导致结果失真,给 GreenEnv 的客户群体呈现出不准确的图景。Sarah 不知疲倦地清理数据,修复不一致之处、填补空缺、消除重复。几天的工作变成了几周的努力,这是一项繁琐但至关重要的工作,确保她的分析建立在坚实的基础上。
谁在乎数据质量?
每年,糟糕的数据质量使组织平均损失$1290 万。[1]
幸运的是,在几周的清理和整理这堆杂乱数据后,萨拉终于完成了任务……至少完成了这一部分。
她的下一个挑战出现在她进入产品数据领域时,目标是识别畅销商品并推荐未来机会。然而,她遇到了另一个问题——完全缺乏元数据。产品描述缺失,类别模糊。基本上,缺少足够的数据帮助萨拉理解产品数据。萨拉意识到元数据管理的重要性——它是关于数据本身的结构化信息。没有它,理解和分析数据几乎是不可能的。
研究表明,大多数数据存在不准确性
Experian 的研究表明,企业认为约 29%的数据在某种程度上存在不准确的情况。[2]
虽然感到沮丧,但萨拉依然决定联系不同部门,拼凑有关产品的信息。她发现每个部门使用的内部术语和分类系统各不相同。市场营销和销售部门用不同的名称指代同一款清洁产品。
随着萨拉深入分析,她发现不同部门将数据集保存在不同的应用程序中,过时的存储系统无法处理日益增长的数据量,萨拉不得不长时间等待查询的执行。萨拉还注意到,没有明确的规则规定谁可以访问哪些数据以及在什么条件下可以访问,如果没有集中管理和适当的访问控制,敏感信息被未经授权访问的风险就会增加,这可能导致数据泄露和合规性违规。数据治理的缺乏——即用于管理数据的一套规则和程序,显而易见。
数据泄露可能代价高昂
根据 Ponemon 研究所的数据显示,2023 年全球数据泄露的平均成本为 445 万美元,创历史新高,成本因行业和地区而异。[3]
上述问题和萨拉故事中的障碍突显了许多支柱之间的相互联系——数据质量、元数据管理和数据治理在 GreenEnv 获取和利用有价值洞察中都发挥了至关重要的作用。
萨拉的经历对于数据科学家和分析师来说是常见的。许多组织拥有大量数据,而每个人都知道那句名言:“数据是新的电力。”每个组织都希望最大化利用他们的数据,因为它是非常宝贵的资产。但大多数人错误地(并且实际上)认为,简单地雇用一个数据分析师或数据科学家就足以释放这种价值。要从数据中获得最大价值,涉及多个支柱,组织必须对这些进行充分的考量和关注。这里的关键词是数据管理。
你知道吗……
86%的组织表示,他们认为投资于数据管理直接影响业务增长[4]
什么是数据管理?
一般来说,数据管理是处理组织数据的整体实践,包括从获取和存储数据到处理、保护和分析数据。目标是确保数据是可访问的、可用的、准确的、可靠的,并且具有高质量,以实现数据驱动的组织状态,最终实现我们的组织目标。
数据管理支柱
有几个关键维度相互协作,以实现成功的数据利用。虽然这些支柱在不同框架中可能略有不同,但其基本概念是相同的。
除了数据安全和数据集成作为支柱外,我们还拥有:
-
数据质量: 这确保数据是准确的、一致的、完整的和及时的。它涉及识别和修复错误、管理缺失值,并建立数据清理和验证过程。
-
元数据管理: 这关注于组织和编目关于数据本身的信息。
-
数据治理: 这建立了在数据资产整个生命周期中管理数据的框架和规则。它涉及制定政策和流程,并分配角色,以确保数据的准确性、安全性以及符合法规的合规性。
-
数据架构: 这定义了数据资产的结构和组织。包括定义数据模型、存储解决方案和数据流,确保数据的高效存储、检索和利用。
-
数据生命周期管理: 这关注于管理数据的整个生命周期,从创建到归档或删除。它涉及实施数据捕获、存储、转换、使用和处置的过程。
你可以注意到这些维度是相互连接并密切相关的。解决一个领域的弱点通常会影响到其他领域。一些框架已被开发出来,以解决数据管理不同维度之间的关系,以及它们如何相互作用并相互影响。
数据管理框架:艾肯金字塔

艾肯金字塔:图片由作者提供
许多已建立的框架,如 DAMA-DMBOK2、IGF 和 EDM,提供了结构化的指导、标准化术语和数据管理的成熟度评估。
这里值得提及的一个概念框架,也是我最喜欢的框架,是艾肯的数据管理金字塔。它概述了数据管理过程的不同阶段。该框架由数据管理先驱彼得·艾肯(Peter Aiken)开发,描述了许多组织所面临的情境。在尝试充分利用其数据的潜力时,许多组织经历了类似的步骤进展:
-
第一阶段: 这集中于建立基本构建块,如数据建模、存储解决方案和安全措施。
-
阶段 2: 随着数据使用的增加,这一阶段解决由数据质量差引发的挑战,以及元数据管理和数据架构等活动。
-
阶段 3: 阶段 2 中的先前活动需要数据治理。数据治理还使得像文档和内容管理、参考和主数据管理、数据仓库和商业智能等活动得以进行,从而为阶段 4 的高级分析提供了支持。
-
阶段 4: 这是组织真正释放其数据全部潜力的阶段。在这里,组织利用高质量的数据进行高级分析和数据科学,并提取有价值的洞察以支持决策制定。
艾肯金字塔有助于组织理解数据管理活动如何相互连接,如何每个活动在前一个活动的基础上展开,以及如何优先处理这些活动以有效利用数据。
我对数据管理最佳实践的反思与收获
通过反思在数据管理方面的学习和经验(虽然我不是数据管理专家 XD),我逐渐认识到并偏好以下几点,尤其是在我们专注于数据质量时,这些是关于数据管理及其最佳实践的关键点。
-
数据管理没有一种“放之四海而皆准”的解决方案。虽然存在一些框架可以引导组织迈向数据管理成熟度,但整个过程对每个实体来说都是独特的。每个组织在数据管理方面有不同的优先事项,并面临各自不同的挑战。
-
我的做法是从简单开始。将数据管理最佳实践或改进措施应用到组织数据的一个目标部分,专注于最重要的内容。这使得成熟度能够逐步提高,最终涵盖所有数据。这种分阶段的方法对于数据质量和元数据管理等维度非常有益。
-
如果一个过程持续产生不良数据,即使在数据管理的其他领域做出再多的努力也无法阻止它。这些过程可能是技术性的,也可能是非技术性的。在这种情况下,采取主动的方式至关重要。
-
例如,一个产生不良数据的非技术性过程可能涉及开发人员创建数据库,而他们只专注于技术层面。可能缺少文档或列描述等信息。在我看来,一个好的做法是让数据分析师和其他相关利益相关者参与设计过程,确保遵循数据管理的最佳实践。数据管理团队可以决定是否继续推进某个应用设计。
-
应用程序的设计也可能是生成不良数据的技术过程。一个设计良好的应用程序应该在数据录入过程中主动执行数据质量控制。例如,可以使用下拉菜单而不是文本框来输入性别。另一个例子是预定义电子邮件类型,用户只需输入用户名,系统便会自动生成“@gmail.com”或其他域名后缀。
-
-
标准化是关键:数据的不一致性可能是一场噩梦。想象一下,如果客户姓名在各个部门存储方式不同,日期格式相互冲突,或者团队使用各自的缩写。更重要的是,在一个组织或公司内部,可能有不同的流程生成相同类型的数据,以及不同的数据收集工具。标准化通过为数据处理建立共同的格式、定义和流程来应对这种混乱。这可以确保数据质量,简化跨应用程序的集成,通过共享的数据语言促进协作,并通过简化数据工作流提高效率。这个过程也是迭代和敏捷的,组织可以逐步在其中实现更多的成熟度。这一过程也可以是前述数据管理过程的一部分,即验证生成数据的应用程序:遵循标准。即,任何要获得批准的应用程序必须首先符合这些标准。
-
最后,数据管理是一个全面的过程,要求组织内部不同团队之间的协作,需要定义数据管理战略并将其与业务或机构的目标和战略对齐。通常,这将从评估当前和期望的数据管理成熟度水平开始,分析差距,优先考虑数据管理任务,并保持敏捷。这个过程是迭代的,清晰的解决方案通常不会提前出现。
数据管理专业职业
市面上有很多数据管理认证可以考虑。最好的选择取决于你的具体目标和经验,但以下是我遇到的一些认证:
-
认证数据管理专家 (CDMP): 由国际数据管理协会(DAMA)提供,涵盖广泛的主题,从数据治理和质量到建模。
-
认证信息管理专家 (CIMP): 由信息管理学院(IIM)提供的这个项目,专注于信息管理学科,如治理、质量、安全等。
-
数据治理与管理专业认证 (DGSP): 由数据治理学院颁发,该认证专注于开发和实施数据治理计划所需的技能,并确保数据质量、合规性等。
-
认证临床数据经理(CCDM): 这是由临床数据管理学会(SCDM)提供的认证,面向在临床研究中管理试验数据的专业人士。
最终,选择哪一个值得投入时间和精力,需要更深入地分析每个认证的可信度、内容和目的。
展望未来:数据管理工具

图片由作者提供
正如你所猜测的那样,市面上有无数工具可用于解决数据管理的不同方面,谈论所有这些工具是不可行的,每个工具都有其优缺点,适用于特定情境或某些特定的数据管理维度。
在我下一篇文章中,我将深入探讨两款开源数据管理工具——DataHub 和 Great Expectations(GX),并提供逐步指南,教你如何将它们集成在一起,创建一个强大、经济高效、可扩展且统一的数据发现、元数据管理、数据质量、数据治理、数据血缘关系和影响分析环境。
结论
如今,数据无疑是组织最有价值的资产。然而,许多组织仍然缺乏合适的数据管理,限制了它们利用数据的真正潜力。数据质量、治理、安全性和元数据管理等问题,都是从组织的数据中获取最大价值的核心支柱。
还记得一开始讲的 Sarah 的故事吗?雇佣一位数据科学家是不够的。他们通常需要花费大量时间清理和整理混乱的数据,才能开始分析。幸运的是,像 Aiken 金字塔这样的框架可以帮助组织在数据管理的过程中提供指导,并帮助不同团队和利益相关者在不同的技术层级下轻松地沟通数据管理计划。
感谢你一直读到这里!希望你觉得这篇文章既有信息性又有趣。如果有任何错误或想法,请随时指出——你的反馈总是受到欢迎的。
参考文献
-
Gartner. (2021). “如何提高数据质量。” Smarter With Gartner,Gartner
-
Experian. (2019). “全球数据管理研究报告。”
-
Ponemon Institute. (2023). “数据泄露成本报告。”
-
Experian. (2021), “数据体验——推动商业增长的基于数据的战略报告”
进一步阅读
书籍
-
数据管理知识体系(DAMA-DMBOK):DAMA-DMBOK 第二版,作为数据管理实践和原则的全面指南,提供了关于数据管理各个方面的详细概述。[这是我最喜欢的数据管理参考资料!]
-
《数据治理:如何设计、部署和维持有效的数据治理项目》,作者:John Ladley。
-
《首席数据官的案例:重塑 C-suite 以利用你最有价值的资产》,作者:Peter Aiken, Michael M. Gorman
-
《研究人员的数据管理:组织、维护和共享你的数据以确保研究成功》作者:Kristin Briney。
在线资源:
-
数据管理协会(DAMA)国际: DAMA International 提供了大量关于数据管理的资源,包括文章、网络研讨会和白皮书。它们的网站对初学者和经验丰富的专业人士都是宝贵的资源。
-
数据管理通讯: TDAN.com 是一本专注于数据管理和数据行政管理主题的在线出版物。
-
CDMP 学习小组 Facebook 群组: 在这里你可以找到许多数据从业者和其他对 CDMP 考试感兴趣的人,你可以提问、寻找学习伙伴,或者加入他们关于 CDMP 主题的数据管理定期网络研讨会和讨论。就我个人而言,这是我最喜欢的群组之一,感谢 Nicole Janeway Bills 在这个群组中的努力。
营销报告差距
为什么营销人员转向电子表格以及如何弥合这一差距
·发表于 Towards Data Science ·阅读时间 5 分钟·2024 年 1 月 20 日
--

注意差距(图像由作者提供)
营销通常是分析的主要应用场景。大多数绩效营销人员都是数据驱动且具有数据素养的。他们希望利用数据来优化营销活动并实现他们的投资回报率目标。
然而,自助分析未能达到预期,尤其是对于那些运行数千个针对不同产品、多个市场和渠道的复杂营销团队来说,情况尤为严重。
让我们分析一下期望与现实之间的差距。
期望

期望 — BI 工具仪表板作为营销分析的单一真实数据源(图像由作者提供)
数据团队负责营销仪表板,确定每个团队的需求和要求。他们构建一个包含品牌和类别所有数据的单一真实数据源,并在 BI 仪表板上供营销人员访问。
期望是高层次的仪表板和核心的深度分析就足够了,电子表格将不再使用。
这一期望很容易破裂……
现实

现实 — 营销人员在电子表格上运行分析(图像由作者提供)
仪表板缺乏深入分析所需的粒度和灵活性(例如,关键字或创意层面的分析),并且不适合特定的营销分析,如预算或进度管理。此外,营销活动变化非常迅速。分析需求也在很短的时间内发生变化(例如,新的渠道、新的度量标准、新的维度)。
营销人员依赖数据团队提供任何更新,例如度量标准或维度的变化。报告请求需要等待数据团队的优先排序,这通常耗时过长。
营销人员等不及了。他们有预算需要管理,还有 ROAS 目标要实现——赌注很高。
备用解决方案?电子表格。
为什么营销团队不断使用电子表格?
“在我们的 BI 工具中,我们没有营销同事所需要的数据粒度。为了优化他们的活动,他们需要深入到创意和关键词层面。——数据负责人
仪表盘非常适合高层次的指标监控和基本的深度分析,满足管理者、主管和副总裁的需求。日常和每周的操作性决策需要更高的粒度和灵活性。这就是为什么“自助”仪表盘在操作性用例中失效的原因,通常这些用例由表现营销经理和用户获取经理等个人贡献者负责。

操作性分析差距:“自助”仪表盘的瓶颈(图示由作者提供)
在任何公司早期,营销分析都依赖于电子表格。随着数据团队构建现代数据架构,通常预期电子表格会逐渐消失。它们确实会在一段时间内消失。然而,在公司继续发展、扩展到新渠道和市场、启动更多活动等后期阶段,仍然需要更细粒度的分析,以满足业务的速度。这时,电子表格又回来了。

表现营销和报告架构的演变(感谢 Clarisights 提供)
电子表格是进行细粒度分析、快速响应业务需求的首选解决方案。营销人员可以完全控制它们,并通过快速反馈周期来验证他们的假设。
电子表格是现有仪表盘的一个很好的补充。
然而,这种现实存在许多缺点和弊端。
这种差距如何导致错失机会
对营销团队来说,这种现实导致了预算浪费和 ROAS 不佳。
虽然预算是在更高层级决定的,但执行发生在各个贡献者的细粒度层面。表现营销经理和用户获取经理负责管理这些活动,最终掌控 ROAS 指标。
基于电子表格的分析涉及手动工作流程,即从渠道平台下载数据或使用连接器工具,建模数据并进行分析。这些手动过程和工作流程非常耗时且容易出错。
电子表格还缺乏数据治理,导致多个“真相”来源、信息孤岛和混乱。常见的症状是典型的提问:“为什么数字不匹配?”
在电子表格上管理大量营销预算远非理想。既然我们已经了解了这种现实如何导致错失机会,接下来让我们思考如何弥补这一差距。
如何弥补这一差距
弥补这一差距通常需要在四个关键要素(文化、人员、工具、流程)之间开展工作。从哪里开始以及优先处理什么取决于你当前的情况。这就是你应该开始的地方:深入了解你的市场营销团队如何进行分析。不要只是问他们,而是坐到他们旁边,让他们带你了解他们的日常和每周工作流程。这将帮助你了解从哪里开始,并描绘出你当前状况与理想状态之间的差距。
你不应如何处理这个挑战
数据团队有时会被这种情况困扰,或者干脆忽视它。这并不是解决问题的正确方式。避免对你所构建的仪表板和解决方案产生情感依赖。同时,避免像“市场营销人员不知道如何使用我们的 BI 工具,因此他们使用电子表格”这样的简单解释。这些陷阱会导致人际摩擦,使问题更加严重。
相反,应该通过接近市场营销团队来了解当前的情况。将这一差距视为一个重大的机会,可以对业务产生深远影响,并提高数据的投资回报率。
以下,我将分享一些应对这一差距的通用最佳实践。
最佳实践
尽管具体的行动点取决于你的具体情况,但以下是四个关键要素中的一些最佳实践:
-
工具:考虑构建或开发适合市场营销特定操作流程的辅助工具。你应该绘制出哪些需求可以通过你的仪表板来满足,哪些使用案例需要更灵活和精细化的解决方案。理想情况下,你应该力求构建或购买一个综合解决方案,结合电子表格的灵活性与 BI 解决方案的规模和治理。
-
文化:促进一种数据文化,强调与营销团队之间的强有力合作,而不是放弃(即典型的回答——“他们不知道如何使用我们的 BI 工具”)。挑战现有的假设,寻求理解背后的原因。提出一个坚实的商业案例来解决这个机会,并获得高层的支持。
-
人员:将分析师嵌入到市场营销团队中,以培养业务和领域专长。促进这些数据分析师与市场营销人员之间的紧密关系。推广关于市场营销需求、使用案例和工作流程的知识共享会议。
-
流程:围绕理想的工作流程制定最佳实践(例如,“进行关键词分析的最佳方式是什么”)。同时,确保定期与市场营销人员进行接触,收集他们的反馈,并就接下来的工作达成一致。你应将定性反馈(即定期接触点)与定量方法(例如,季度调查)相结合。
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
有什么想法?请联系 João Sousa,Clarisights 的市场推广负责人 Clarisights。敬请期待更多关于市场营销分析和报告的文章。
K-Means 聚类背后的数学与代码
为什么 K-Means 是无监督学习中最受欢迎的算法?让我们深入研究它的数学原理,并从零开始构建它。
·发表于 Towards Data Science ·阅读时间 19 分钟·2024 年 2 月 13 日
--

图像由 DALL-E 生成
K-means 聚类是机器学习中的一项基础技术,因为它通过简单的方式组织复杂数据。在本文中,我们将探讨算法的核心。我们将深入研究它的应用,剖析背后的数学原理,从零开始构建它,并讨论它在数据科学这一快速发展的领域中的相关性。
索引
· 1: 理解基础知识
∘ 1.1: 什么是 K-Means 聚类?
∘ 1.2: K-Means 是如何工作的?
· 2: 实现 K-Means
∘ 2.1: K-Means 背后的数学原理
∘ 2.2: 如何选择最优的聚类数目
· 3: K-Means 在实践中的应用
∘ 3.1 从零开始在 Python 中实现 K-Means
∘ 3.2 使用 Scikit-Learn 实现 K-Means
· 4: 优势与挑战
∘ 4.1 使用 K-Means 的优势
∘ 4.2 克服 K-Means 的局限性
Adam 优化器背后的数学
为什么 Adam 是深度学习中最受欢迎的优化器?让我们通过深入了解其数学原理并重现算法来理解它。
·发布于 Towards Data Science ·16 分钟阅读·2024 年 1 月 30 日
--

图片由 DALLE-2 生成
如果你点击了这篇文章,你可能已经听说过 Adam,这个名字在许多 Kaggle 比赛中获得了显著的认可。通常,我们会尝试一些优化器,如 SGD、Adagrad、Adam 或 AdamW,但真正理解它们的机制则是另一回事。通过阅读本文,你将成为少数几位不仅了解 Adam 优化的原理,还能有效利用其强大功能的人之一。
顺便提一下,在我之前的文章中,我介绍了随机梯度下降的数学,如果你错过了,推荐你阅读一下。
深入了解随机梯度下降:算法、假设、优点、公式及实际应用。
索引
· 1: 理解基础知识
∘ 1.1: 什么是 Adam 优化器?
∘ 1.2: Adam 的机制
· 2. Adam 算法解析
∘ 2.1 Adam 背后的数学
∘ 2.2 角色…
批量归一化背后的数学
探索批量归一化,这是神经网络的基石,理解其数学原理,并从零开始实现它。
·发表于 Towards Data Science ·阅读时间 21 分钟·2024 年 5 月 8 日
--

图像由 DALL-E 生成
批量归一化(Batch Normalization)是神经网络中的一个关键技术,它通过标准化每一层的输入来优化网络训练。它解决了内部协方差偏移(internal covariate shift)问题,即每一层的输入分布在训练过程中发生变化,导致学习过程复杂且效率低下。通过归一化这些输入,批量归一化帮助网络训练得更快且更稳定。此方法对于确保不同网络架构和任务(包括图像识别和自然语言处理)中的可靠性能至关重要。在本文中,我们将深入探讨批量归一化的原理和数学基础,并展示如何从零开始在 Python 中实现它。
索引
1: 介绍
2: 归一化的必要性
3: 数学与机制
∘ 3.2: 缩放和平移步骤
∘ 3.3: 批量归一化的流
∘ 3.4: 激活分布
4: 从零开始在 Python 中应用
∘ 4.1: 从零开始的批量归一化…
卷积神经网络背后的数学
深入了解 CNN(计算机视觉的核心),理解其数学原理,从零实现,并探索其应用
·发表于 Towards Data Science ·26 分钟阅读·2024 年 4 月 9 日
--

图片来源:DALL-E
索引
· 1: 介绍
· 2: CNN 架构背后的数学
∘ 2.1: 卷积层
∘ 2.2: 步幅
∘ 2.3: 填充
∘ 2.4: 多重滤波器与深度
∘ 2.5: 权重共享
∘ 2.6: 特征图创建
∘ 2.7: 池化层
∘ 2.8: 全连接层
· 3: 构建 CNN 的逐步指南
∘ 3.1: 设置环境
∘ 3.2: 准备数据
∘ 3.3: 设计 CNN 模型
∘ 3.4: 编译模型
∘ 3.5: 训练 CNN
· 4: 改进模型性能
∘ 4.1: 数据增强
∘ 4.2: Dropout
∘ 4.3: 批量归一化
∘ 4.4: 迁移学习
深度卷积神经网络的数学原理 — AlexNet
深入了解 AlexNet,第一款现代 CNN,理解其数学原理,从零实现它,并探索它的应用。
·发布于Towards Data Science ·阅读时长 32 分钟·2024 年 4 月 16 日
--

图像由 DALL-E 生成
卷积神经网络(CNN)是一类专门设计用于处理结构化数组数据(如图像)的深度神经网络。CNN 通过直接从图像的像素数据中识别模式来工作,消除了手动特征提取的需求。它们在理解图像中的空间层次结构方面尤其强大,利用可学习的滤波器处理数据的局部区域,从而保持像素之间的空间关系。
这些网络在处理大量视觉数据的任务中非常有效,广泛应用于图像和视频识别、实时物体检测等领域,在面部识别技术和自动驾驶车辆等领域的进展中发挥着关键作用。
在本文中,我们将探讨 AlexNet,这一开创性的卷积神经网络(CNN)架构,它在计算机视觉领域产生了深远的影响。AlexNet 因其在各种视觉识别任务中的强大表现而闻名,它利用深度学习直接解读复杂的图像。我们将解析其操作背后的数学原理以及实现这一过程的编码框架……
深度神经网络微调背后的数学
深入了解微调神经网络的技术,理解其数学原理,从零开始构建神经网络,并探索其应用。
·发表于 Towards Data Science ·阅读时间 31 分钟·2024 年 4 月 3 日
--

图片来源:DALL-E
虽然在机器学习中,你可能通过尝试一些模型、选择最佳表现者并调整一些设置来取得进展,但深度学习并不遵循相同的规则。如果你曾经尝试过神经网络,可能会注意到它们的表现有时好有时差。你甚至可能看到像逻辑回归这样的简单方法战胜了你复杂的 200 层深度神经网络。
为什么会发生这种情况呢?深度学习是我们拥有的最先进的 AI 技术之一,但它需要扎实的理解和小心的操作。了解如何微调神经网络、弄清楚它内部发生了什么并掌握其使用方法至关重要。这就是我们今天要深入探讨的内容!
在我们开始这篇文章之前,我建议你打开这个 Jupyter Notebook。它包含了我们今天要讲解的所有代码,随时查看将帮助你更容易跟上内容:
门控递归单元背后的数学原理
深入探索门控递归单元(GRU)的高级深度学习,理解其数学原理,并从零开始实现它们。
·发表于 Towards Data Science ·27 分钟阅读·2024 年 6 月 4 日
--

由 DALL-E 生成的图像
门控递归单元(GRU)是一种强大的递归神经网络(RNN),旨在高效处理序列数据。本文将探讨 GRU 是什么,它的数学原理,并且我们将从零开始用 Python 实现一个 GRU 模型。
索引
1: 理解基础知识
∘ 1.1: 什么是 GRU?
∘ 1.2: 与 LSTM 和传统 RNN 的比较
2: GRU 的架构
∘ 2.1: GRU 单元结构
3: GRU 的数学原理
∘ 3.1: 更新门
∘ 3.2: 重置门
∘ 3.3: 候选激活
∘ 3.4: 逐步示例
4: 在 Python 中从零开始构建 GRU
∘ 4.1: 导入和自定义类
∘ 4.2: GRU 类
∘ 4.3: GRUTrainer 类
∘ 4.4: TimeSeriesDataset 类
∘ 4.5: 训练 GRU 模型
5: 结论
上下文学习背后的数学原理
从注意力到梯度下降:解开变换器如何从示例中学习的奥秘
·发表于 Towards Data Science ·阅读时间 6 分钟·5 天前
--
上下文学习(ICL)——一种基于输入提示中提供的示例来调整行为的变换器能力——已成为现代大型语言模型(LLM)应用的基石。少量示例提示,提供几个所需任务的示例,特别有效地向 LLM 展示我们希望它执行的任务。但这里有个有趣的部分:为什么变换器能如此轻松地根据这些示例调整其行为?在本文中,我将为你提供一种直观的理解,说明变换器是如何完成这种学习技巧的。

来源:作者提供的图片(使用 dingboard 制作)
这将提供一个关于上下文学习潜在机制的高级介绍,这可能帮助我们更好地理解这些模型如何处理和适应示例。
ICL 的核心目标可以表述为:给定一组示范对((x,y) 对),我们能否展示注意力机制能够学习/实现一种算法,该算法从这些示范中形成假设,以正确地将新的查询映射到其输出?
Softmax 注意力
让我们回顾一下基本的 softmax 注意力公式,

来源:作者提供的图片
我们都听说过温度如何影响模型输出,但实际发生了什么呢?关键在于我们如何使用逆温度参数修改标准的 softmax 注意力。这个单一变量会改变模型分配注意力的方式——在通过 softmax 之前缩放注意力分数,会使得分布从平缓逐渐变得尖锐。这会稍微修改注意力公式,如下所示,

来源:图片由作者提供
其中 c 是我们的逆温度参数。考虑一个简单的向量 z = [2, 1, 0.5]。让我们看看 softmax(c*z) 在不同 c 值下的表现:
当 c = 0 时:
-
softmax(0 * [2, 1, 0.5]) = [0.33, 0.33, 0.33]
-
所有 token 都受到相等的关注,完全失去了区分相似性的能力
当 c = 1 时:
-
softmax([2, 1, 0.5]) ≈ [0.59, 0.24, 0.17]
-
注意力根据相似度分配,保持选择和分配之间的平衡
当 c = 10000(接近无穷大)时:
-
softmax(10000 * [2, 1, 0.5]) ≈ [1.00, 0.00, 0.00]
-
注意力会收敛成一个独热向量,完全聚焦于最相似的 token——这正是我们需要的最近邻行为
现在,在上下文学习方面有趣的地方来了:当 c 趋向于无穷大时,我们的注意力机制本质上变成了一个 1-最近邻搜索!想想看——如果我们关注所有的 token 除了我们的查询,我们基本上是在从演示示例中找到最接近的匹配。这为我们提供了一个新的视角来看待 ICL——我们可以将其视为在输入输出对上实现最近邻算法,全部通过注意力机制来完成。
但当 c 是有限的时会发生什么呢?在这种情况下,注意力更像是一个高斯核平滑算法,它根据每个 token 的指数相似度来加权。
我们看到 Softmax 可以执行最近邻操作,很棒,但知道这一点有什么意义呢?如果我们能说 Transformer 可以学习一个“学习算法”(比如最近邻、线性回归等),那么也许我们可以将它应用于 AutoML 领域,只需给它一堆数据,让它找到最佳模型/超参数;Hollmann 等人做了类似的事情,他们训练了一个 Transformer 在许多合成数据集上,来有效学习整个 AutoML 流水线。Transformer 学会了自动确定哪种类型的模型、超参数和训练方法最适合任何给定的数据集。当遇到新数据时,它可以通过一次前向传播来进行预测——本质上将模型选择、超参数调优和训练浓缩成一步。
在 2022 年,Anthropic 发布了一篇论文,展示了归纳头可能构成 ICL 机制的证据。什么是归纳头?正如 Anthropic 所述——“归纳头是通过由不同层次的两个注意力头组成的电路来实现的,这两个注意力头协同工作以复制或完成模式。”简单来说,归纳头的作用是,对于像[…, A, B,…, A]这样的序列,它将使用 B 来完成序列,推理的依据是,如果在上下文中 A 后面跟着 B,那么 A 后面很可能会再次跟着 B。当你有一个像“…A, B…A”这样的序列时,第一个注意力头会将之前的标记信息复制到每个位置,而第二个注意力头则使用这些信息来查找 A 之前出现的位置,并预测其后出现的内容(B)。
最近,许多研究表明,transformer 可能通过梯度下降实现 ICL(Garg et al. 2022,Oswald et al. 2023等),通过展示线性注意力与梯度下降之间的关系。让我们重新审视最小二乘法和梯度下降,

来源:作者提供的图像
现在让我们看看这与线性注意力有何关联
线性注意力
在这里,我们将线性注意力视为与 softmax 注意力相同,只是去除了 softmax 操作。基本的线性注意力公式,

来源:作者提供的图像
让我们从单层构建开始,捕捉上下文学习的本质。假设我们有 n 个训练样本(x₁,y₁)…(xₙ,yₙ),并且我们想要为一个新的输入 x_{n+1}预测 y_{n+1}。

来源:作者提供的图像
这看起来与我们在梯度下降中得到的非常相似,除了在线性注意力中我们有一个额外的项‘W’。线性注意力实现的是一种叫做预条件梯度下降(PGD)的东西,在这种方法中,我们不是使用标准的梯度步骤,而是通过预处理矩阵 W 来修改梯度,

来源:作者提供的图像
我们在这里展示的是,我们可以构造一个权重矩阵,使得一层线性注意力能够执行一步 PGD。
结论
我们看到注意力如何实现“学习算法”,这些算法的基本思路是,如果我们提供大量的示范(x,y),那么模型就能从这些示范中学习,并预测任何新查询的输出。尽管涉及多个注意力层和 MLP 的具体机制非常复杂,但研究人员在理解上下文学习的工作机制方面已取得了一定进展。本文提供了一个直观的高层次介绍,帮助读者理解 transformers 这种新兴能力的内部工作原理。
要了解更多关于这个话题的内容,我建议阅读以下几篇论文:
致谢
这篇博客文章的灵感来源于我在 2024 年秋季于密歇根大学的研究生课程。尽管这些课程提供了探索这些主题的基础知识和动力,但文中任何的错误或误解完全是我个人的责任。这代表了我对相关材料的个人理解和探索。
KAN 背后的数学 — 科尔莫戈罗夫-阿诺德网络
一种新的替代经典多层感知器的神经网络出现了。为什么它更准确且具有更好的可解释性?深入探讨数学与代码。
·发布于 Towards Data Science ·13 分钟阅读·2024 年 6 月 12 日
--

图像由 DALL-E 生成
在当今的人工智能世界中,神经网络推动了无数创新与进步。许多突破的核心是多层感知器(MLP),这是一种以逼近复杂函数能力著称的神经网络。但随着我们不断拓展人工智能的边界,我们不得不问:我们能比经典的多层感知器做得更好吗?
这就是科尔莫戈罗夫-阿诺德网络(KAN),一种受科尔莫戈罗夫-阿诺德表示定理启发的新型神经网络方法。与传统的多层感知器(MLP)不同,后者在每个神经元上使用固定的激活函数,而 KAN 则在网络的边缘(权重)上使用可学习的激活函数。这个简单的变化为准确性、可解释性和效率开辟了新的可能性。
本文探讨了为什么 KAN 是神经网络设计中的革命性进展。我们将深入分析它们的数学基础,强调它们与 MLP 的关键差异,并展示 KAN 如何超越传统方法。
1:多层感知器的局限性
Keras 3 优化器背后的数学原理:深入理解与应用
这与书中所说的有些不同。
·发表于 Towards Data Science ·9 分钟阅读·2024 年 8 月 17 日
--

Keras 3 优化器背后的数学原理:深入理解与应用。图片来自 DALL-E-3
介绍
优化器是每个从事机器学习工作的人必不可少的一部分。
我们都知道,优化器决定了模型在梯度下降过程中如何收敛损失函数。因此,选择合适的优化器可以提升模型训练的性能和效率。
除了经典论文,许多书籍也用简单的语言解释了优化器背后的原理。
然而,我最近发现 Keras 3 的优化器表现并不完全符合这些书中描述的数学算法,这让我有些焦虑。我担心自己误解了某些内容,或者 Keras 最新版本的更新影响了优化器的表现。
所以,我回顾了 Keras 3 中几种常见优化器的源代码,并重新审视了它们的应用场景。现在,我希望分享这些知识,以节省你们的时间,帮助你们更快掌握 Keras 3 优化器。
如果你不太熟悉 Keras 3 的最新变化,下面是一个快速概述:Keras 3 集成了 TensorFlow、PyTorch 和 JAX,使我们能够通过 Keras API 轻松使用前沿的深度学习框架。
核密度估计背后的数学
探索核密度估计的基础、概念和数学原理
·发表于Towards Data Science ·14 分钟阅读·2024 年 9 月 17 日
--

核密度估计是一个基本的非参数方法,是揭示数据隐藏分布的多功能工具。本文深入探讨了估计器的数学基础,提供了选择最优带宽的指导,并简要触及了核函数的选择以及其他相关话题。
第一部分:简介
假设我给你以下样本数据:

分析样本数据的第一个也是最简单的步骤之一是生成一个直方图,对于我们的数据,我们得到以下图形:

并不是非常有用,我们距离理解底层分布仍然很远。实际上,还有一个额外的问题,那就是数据很少表现出由直方图产生的那种锐利的矩形结构。核密度估计提供了解决方法,它是一种非参数方法,用于估计随机变量的概率密度函数。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KernelDensity
# Generate sample data
np.random.seed(14)
uniform_data = np.random.uniform(low=1, high=7, size=20)
normal_data = np.random.normal(loc=3, scale=0.7, size=20)
# Combine both distributions
combined_data = np.concatenate([uniform_data, normal_data])
# Compute histogram
hist, bin_edges = np.histogram(combined_data, bins=9, density=True)
# Compute KDE
kde = KernelDensity(kernel='gaussian').fit(combined_data[:, None])
x = np.linspace(min(combined_data), max(combined_data), 1000)[:, None]
log_density = kde.score_samples(x)
density = np.exp(log_density)
# Plot histogram
plt.hist(combined_data, bins=9, density=True, color='blue', edgecolor='black', label='Histogram')
# Plot KDE
plt.plot(x, density, color='red', label='KDE')
# Add labels and legend
plt.ylabel('Density')
plt.title('Histogram and KDE')
plt.legend()
# Show plot
plt.show()
第二部分:推导
以下推导灵感来源于 Bruce E. Hansen 的《非参数讲义笔记》(2009 年)。如果你有兴趣了解更多,可以参考他的原始讲义笔记这里。
假设我们想从一组数据样本中估计一个概率密度函数 f(t)。一个好的起点是通过使用 经验分布函数 (EDF) 来估计累积分布函数 F(t)。设 X1, …, Xn 为独立同分布的实值随机变量,其共同的累积分布函数为 F(t)。EDF 定义为:

然后,根据大数法则,当 n 趋近于无穷大时,EDF 几乎必然收敛于 F(t)。现在,EDF 是一个阶梯函数,可能如下所示:

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
# Generate sample data
np.random.seed(14)
data = np.random.normal(loc=0, scale=1, size=40)
# Sort the data
data_sorted = np.sort(data)
# Compute ECDF values
ecdf_y = np.arange(1, len(data_sorted)+1) / len(data_sorted)
# Generate x values for the normal CDF
x = np.linspace(-4, 4, 1000)
cdf_y = norm.cdf(x)
# Create the plot
plt.figure(figsize=(6, 4))
plt.step(data_sorted, ecdf_y, where='post', color='blue', label='ECDF')
plt.plot(x, cdf_y, color='gray', label='Normal CDF')
plt.plot(data_sorted, np.zeros_like(data_sorted), '|', color='black', label='Data points')
# Label axes
plt.xlabel('X')
plt.ylabel('Cumulative Probability')
# Add grid
plt.grid(True)
# Set limits
plt.xlim([-4, 4])
plt.ylim([0, 1])
# Add legend
plt.legend()
# Show plot
plt.show()
因此,如果我们尝试通过取经验分布函数(EDF)的导数来找到 f(t) 的估计器,我们将得到一个 狄拉克 delta 函数 的加权和,这并没有太大帮助。相反,让我们考虑使用估计器的 t二点中心差分公式作为导数的近似。对于一个小的 h>0,我们得到:

现在定义函数 k(u) 如下:

然后我们得到:

这是核密度估计器的特例,其中 k 是均匀核函数。更一般地,核函数是一个从实数到实数的非负函数,满足以下条件:

我们假设本文讨论的所有核函数都是对称的,因此我们有 k(-u) = k(u)。
核的矩,它为核函数的形状和行为提供了洞察,定义如下:

最后,核的阶数定义为第一个非零矩。
我们只能通过改变 h 值(带宽)或核函数来最小化核密度估计器的误差。带宽参数对结果估计的影响远大于核函数,但选择起来也更加困难。为了演示 h 值的影响,考虑以下两个核密度估计。使用高斯核来估计从标准正态分布生成的样本,两个估计器之间唯一的区别就是所选择的 h 值。

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
# Generate sample data
np.random.seed(14)
data = np.random.normal(loc=0, scale=1, size=100)
# Define the bandwidths
bandwidths = [0.1, 0.3]
# Plot the histogram and KDE for each bandwidth
plt.figure(figsize=(12, 8))
plt.hist(data, bins=30, density=True, color='gray', alpha=0.3, label='Histogram')
x = np.linspace(-5, 5, 1000)
for bw in bandwidths:
kde = gaussian_kde(data , bw_method=bw)
plt.plot(x, kde(x), label=f'Bandwidth = {bw}')
# Add labels and title
plt.title('Impact of Bandwidth Selection on KDE')
plt.xlabel('Value')
plt.ylabel('Density')
plt.legend()
plt.show()
差异相当显著。
现在让我们看看在保持带宽不变的情况下改变核函数的影响。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KernelDensity
# Generate sample data
np.random.seed(14)
data = np.random.normal(loc=0, scale=1, size=100)[:, np.newaxis] # reshape for sklearn
# Intialize a constant bandwidth
bandwidth = 0.6
# Define different kernel functions
kernels = ["gaussian", "epanechnikov", "exponential", "linear"]
# Plot the histogram (transparent) and KDE for each kernel
plt.figure(figsize=(12, 8))
# Plot the histogram
plt.hist(data, bins=30, density=True, color="gray", alpha=0.3, label="Histogram")
# Plot KDE for each kernel function
x = np.linspace(-5, 5, 1000)[:, np.newaxis]
for kernel in kernels:
kde = KernelDensity(bandwidth=bandwidth, kernel=kernel)
kde.fit(data)
log_density = kde.score_samples(x)
plt.plot(x[:, 0], np.exp(log_density), label=f"Kernel = {kernel}")
plt.title("Impact of Different Kernel Functions on KDE")
plt.xlabel("Value")
plt.ylabel("Density")
plt.legend()
plt.show()
虽然在尾部视觉上有很大的差异,但不同核函数的估计器整体形状相似。因此,我将主要集中于为估计器找到最优带宽。现在,让我们探讨一些核密度估计器的性质,包括其偏差和方差。
第三部分:核密度估计器的性质
我们需要利用的第一个事实是,估计器在实数线上的积分为 1。为了证明这一事实,我们将使用 u 代换:

运用 u 代换,我们得到以下结果:

现在我们可以找到估计密度的均值:

因此,估计密度的均值就是样本均值。
现在让我们找到估计量的第二阶矩。

然后我们可以找到估计密度的方差:

为了找到最优带宽和核函数,我们将最小化估计量的均方误差。为此,我们首先需要找到估计量的偏差和方差。
随机变量 X 的期望值,具有概率密度函数 f,可以通过以下公式计算:

因此,

其中 z 是一个虚拟的积分变量。然后我们可以使用变量代换得到以下结果:

因此,我们得到以下结果:

然而,大多数情况下,上述积分无法解析求解。因此,我们将通过使用泰勒展开来近似 f(x+ hu)。作为提醒,f(x) 关于 a 的泰勒展开式为:

因此,假设 f(x) 对 v+1 次项可微。对于 f(x+hu),展开式为:

然后,对于 v 阶核函数,我们可以将表达式展开到 v’项:

其中最后一项是余项,当 h 趋近于 0 时,该项的消失速度比 h 快。现在,假设 k 是 v 阶核函数,我们得到:

因此:

因此,估计量的偏差为:

方差的上界可以通过以下计算获得 [Chen 2].

其中:

这个项也被称为粗糙度,可以表示为 R(k)。
最后,我们可以得到均方误差的表达式:

AMSE 是渐近均方误差(Asymptotic Mean Squared Error)的缩写。我们可以最小化渐近均积分平方误差,该误差定义如下,

以找到能带来最小误差的带宽(Silverman, 1986):

然后,通过将方程设置为 0 并简化,得到一个核函数的最优带宽为:

更为熟悉的表达式适用于二阶核,其中最小化 AMISE 的带宽为:

然而,这个解法可能令人失望,因为我们需要知道我们正在估计的分布才能找到最优的带宽。实际上,如果我们一开始就使用核密度估计器,我们就不会知道这个分布。
第四部分:带宽选择
尽管无法找到最小化均积分平方误差的带宽,但仍有几种方法可以在不知道基础分布的情况下选择带宽。然而,值得注意的是,较大的 h 值会导致估计量的方差较小,但偏差较大,而较小的带宽会产生一个粗略的估计,偏差较小(Eidous 等,2010)。
一些用于找到带宽的方法包括使用:
1: 解方程规则
2: 最小二乘交叉验证
3: 偏差交叉验证
4: 直接代入法
5: 对比法
值得注意的是,根据样本大小和数据的偏斜程度,选择带宽的“最佳”方法会有所不同。Python 中的大多数软件包允许你使用 Silverman 提出的法则,你可以直接代入某个分布(通常是正态分布)来表示 f,然后根据你选择的核函数计算带宽。这一过程被称为Silverman 经验法则,它为带宽提供了一个相对简单的估计。然而,它往往会高估带宽,导致较平滑的估计,方差较低,但偏差较大。Silverman 经验法则对于双峰分布的表现也不好,对于这种情况,有更精确的技术可供选择。
如果假设数据服从均值为 0 的正态分布,使用样本标准差,并应用 Epanechnikov 核(下文讨论),你可以通过以下方程使用经验法则选择带宽:

Eidous 等人发现,与我列出的其他方法相比,对比法表现最佳。然而,这种方法有其缺点,因为它增加了需要选择的参数数量。
交叉验证方法是带宽选择的另一种不错选择,因为它通常会导致较小的偏差,但较大的方差(Heidenreich 等,2013)。它在寻找倾向于低估最佳带宽的带宽选择器时非常有用。为了避免这篇文章过长,我将只介绍普通最小二乘交叉验证方法。该方法对于较为弯曲的密度和样本量大约为 50 到 100 的情况效果较好。如果样本量非常小或非常大,这篇论文是一个很好的资源,可以帮助你找到另一种选择带宽的方法。如 Heidenreich 所指出的,“选择哪个带宽选择器确实有很大不同;不仅在数值上,而且对密度估计的质量也有影响”。
为了快速复习,当我们创建模型时,我们会保留一部分样本作为验证集,以查看模型在未训练的样本上的表现。K 折交叉验证通过将数据集分成 K 部分并训练模型 K 次,从而最小化可能选择错误测试集的影响,每一部分都会轮流作为验证集一次。
留一法交叉验证(Leave One Out Cross Validation)是 K 折交叉验证的极端形式,对于样本量为 n 的情况,我们训练模型 n 次,每次留出一个样本。这里有一篇文章详细探讨了这种方法在训练机器学习算法中的应用。
让我们回到 AMISE 表达式。我们将不再研究渐近平均积分平方误差,而是最小化平均积分平方误差(MISE)。首先,我们可以展开平方:

由于我们无法控制最后一项的期望值,因此我们可以尽力最小化前两项并舍弃第三项。然后,由于 X 是 E[X] 的无偏估计量,我们可以直接求得第一项:

然后,k 与 k 的卷积定义如下:

因此,

因此,对于第一项,我们得到:

接下来,我们可以通过蒙特卡洛方法来逼近第二项。首先,正如前面讨论的,密度函数等于累积分布函数的导数,我们可以通过经验分布函数来逼近它。然后,积分可以通过估计量在样本上的平均值来逼近。

然后,最小二乘交叉验证选择器(LSCV)被定义为如下:


我们得到最终定义的选择器如下:

最优带宽是最小化 LSCV 的 h 值,定义如下:

LSCV(h)函数可能有多个局部最小值,因此你找到的最优带宽可能对选择的区间非常敏感。绘制该函数并直观地检查全局最小值所在位置是很有用的。
第五部分:最优内核选择
如果我们使用的是二阶内核(这很常见),则内核选择比带宽选择要简单得多。在最小化 AMISE 的标准下,Epanechnikov 内核是最优的内核。完整的证明可以在Muler的论文中找到。

还有一些内核与 Epanechnikov 内核一样高效,这些也在 Muler 的论文中有所提及。然而,我认为你不必过于担心内核函数的选择,带宽的选择要重要得多。
第六部分:进一步的主题与结论
自适应带宽
提出的一种改进核密度估计器的方法是通过自适应带宽。自适应带宽会根据每个数据点的密度调整带宽,当样本数据的密度较低时增加带宽,密度较高时减少带宽。虽然理论上很有前景,但在一维情况下,自适应带宽的表现却非常差(Terrel, Scott 1992)。虽然它在高维空间中可能表现更好,但在一维情况下,我认为坚持使用常数带宽是最好的选择。
多变量核密度估计
核密度估计器还可以扩展到更高维度,其中内核是径向基函数,或是多个内核函数的乘积。这种方法确实存在维度灾难的问题,随着维度的增加,所需数据点的数量会呈指数增长。它计算成本高昂,对于高维数据分析并不是一个理想的方法。
非参数多变量密度估计仍然是一个非常活跃的领域,Masked Autoregressive Flow似乎是一个全新且有前景的方法。
现实世界应用
核密度估计在各个学科中有着广泛的应用。首先,它已被证明能改善机器学习算法,例如在灵活的朴素贝叶斯分类器中。
它还被用于估算交通事故密度。该链接的论文使用 KDE 帮助构建模型,指示日本不同城市的交通事故风险。
另一个有趣的应用是在地震学y 中,KDE 被用于建模不同地点的地震风险。
结论
核密度估计器是数据分析师工具箱中的一个优秀补充。虽然直方图无疑是分析数据的一种良好方式,它不依赖任何假设,但核密度估计器为单变量数据提供了一个稳固的替代方案。对于高维数据,或当计算时间是一个问题时,我建议考虑其他方法。尽管如此,KDE 仍然是数据分析中直观、强大且多用途的工具。
除非另有注明,所有图片均为作者提供。
K-最近邻的数学原理
为什么 K-最近邻(K-Nearest Neighbors, KNN)是最流行的机器学习算法之一?让我们通过深入了解其数学原理,从零开始构建它来理解它。
·发表于 Towards Data Science ·阅读时间 18 分钟·2024 年 2 月 6 日
--

图片由 DALL-E 生成
K-最近邻(KNN)是最流行的机器学习算法之一。它的简单性、多功能性和适应性使其成为常见选择。但是,为什么它如此流行?为什么它表现得如此出色?本文将揭示 KNN 的工作原理,并解释为什么它是许多数据科学家首选的工具。我们将探讨它的应用、数学原理,手把手从零开始构建它,并讨论在一个由最新技术推动的领域中,为什么 KNN 依然保持其相关性并被广泛使用。
目录
·1: 理解基础知识
∘ 1.1: 什么是 K-最近邻?
∘ 1.2: KNN 是如何工作的?
· 2: 实现 KNN
∘ 2.1: KNN 背后的数学
∘ 2.2: 选择正确的 K 值
∘ 2.3: 如何选择合适的距离度量
· 3: KNN 的实践应用
∘ 3.1 从零开始在 Python 中实现 KNN
∘ 3.2 使用 Scikit-Learn 实现 KNN
· 4: 优势与挑战
∘ 4.1 使用 KNN 的好处
∘ 4.2…
LSTM 背后的数学
深入了解 LSTM,理解其数学原理,并从零开始实现它们
·发布于 Towards Data Science ·阅读时间 25 分钟·2024 年 5 月 1 日
--

图像由 DALL-E 生成
索引
1: 介绍
2: LSTM 的架构
∘ 2.1: LSTM 门控
∘ 2.2: 信息流管理
3: LSTM 背后的数学
∘ 3.1. LSTM 单元中的数学
∘ 3.2: 门控机制
∘ 3.3: 与基本 RNN 单元操作的对比
4: 使用 Python 从零构建 LSTM
∘ 4.1: 导入和初步设置
∘ 4:2: LSTM 类
∘ 4.3: 训练与验证
∘ 4.4: 数据预处理
∘ 4.5: 模型训练
5: 高级 LSTM 模型
∘ 5.1: 双向 LSTM
∘ 5.2: 堆叠 LSTM
∘ 5.3: 窥视孔连接
6: 结论
参考文献
1: 介绍
在我们上次讨论循环神经网络(RNN)时,我们探讨了它们的设计如何使得它们能够有效地处理序列。这使得它们非常适合处理...
Transformer 中的多头注意力背后的数学原理
深入探讨 Transformer 和 LLM 中的秘密元素——多头注意力。让我们一起探索它的数学原理,并在 Python 中从零开始构建它。
·发表于Towards Data Science ·阅读时间 16 分钟·2024 年 7 月 16 日
--

图片由 DALL-E 生成
1:简介
1.1:Transformer 概述
Transformer 架构由 Vaswani 等人在他们的论文“Attention is All You Need”中提出,彻底改变了深度学习,特别是在自然语言处理(NLP)领域。Transformers 使用自注意力机制,使其能够一次性处理输入序列。这种并行处理使得计算速度更快,同时更好地管理数据中的长距离依赖关系。这听起来不熟悉吗?别担心,文章的最后你会明白的。让我们首先简要了解一下 Transformer 的结构。

Transformer 架构(“Attention is all you need”中的架构) — 图片由作者提供
Transformer 由两个主要部分组成:编码器和解码器。编码器处理输入序列以创建连续表示,而解码器则从这个表示中生成输出序列。编码器和解码器都有多个……
Nadam 优化器背后的数学原理
Nadam 是深度学习中最强大的优化器之一。让我们深入探讨它的数学原理,并从零开始构建这个算法。
·发布于 Towards Data Science ·18 分钟阅读·2024 年 5 月 16 日
--

图片由 DALL-E 生成
在我们之前关于 Adam 优化器的讨论中,我们探讨了 Adam 如何通过巧妙地处理自适应学习率,改变了机器学习中的优化格局。Adam 因其在各类机器学习竞赛中的成功,尤其是在 Kaggle 等平台上的表现,成为了优化技术中的一大标杆。然而,优化算法的进化并没有止步于此。Nadam 诞生了——即 Nesterov 加速自适应动量估计(Nesterov-accelerated Adaptive Moment Estimation)的简称——它是 Adam 的先进继任者。
你无需阅读我之前关于 Adam 的文章就能理解本文,但如果你感兴趣,这里是链接:
为什么 Adam 是深度学习中最流行的优化器?让我们通过深入研究其数学原理,重新创造它……
towardsdatascience.com
Nadam 通过引入 Nesterov 动量,增强了 Adam 优化器,使得梯度更新具有前瞻性。这一调整不仅加速了收敛过程,还提高了朝向最优解的步伐准确性……
神经网络背后的数学原理
深入了解神经网络——现代人工智能的核心,理解其背后的数学原理,从零开始实现,并探索其应用。
·发表于 Towards Data Science ·28 分钟阅读·2024 年 3 月 29 日
--

图像来自 DALL-E
神经网络是人工智能(AI)的核心,推动了从图像识别到语言翻译等多种应用。在本文中,我们将深入探讨神经网络是什么,它是如何工作的,以及为什么它在当今科技驱动的世界中如此重要。
索引
· 1: 理解基础知识
∘ 1.1: 什么是神经网络?
∘ 1.2: 神经网络的类型
· 2: 神经网络的架构
∘ 2.1: 神经元的结构
∘ 2.2: 层
∘ 2.3: 学习中层的作用
· 3: 神经网络的数学原理
∘ 3.1: 加权和
∘ 3.2: 激活函数
∘ 3.3: 反向传播:神经网络学习的核心
∘ 3.4: 步骤示例
∘ 3.5: 改进
循环神经网络背后的数学原理
深入了解 RNN,这一时间序列的核心,理解它们的数学原理,从零开始实现它们,并探索它们的应用
·发表于 Towards Data Science ·22 分钟阅读·2024 年 4 月 27 日
--

图片由 DALL-E 生成
循环神经网络(RNN)与其他类型的神经网络不同,它们能够处理输入序列。这一能力使得 RNN 能够执行依赖数据顺序的任务,如预测股票市场趋势、长期监控病人健康状况,或预测句子中的下一个单词。这使得 RNN 在许多前沿人工智能应用中极为重要。本文将深入探讨其架构与数学原理,并在 Python 中从零开始实现它们。
目录
1: 介绍
2: 循环神经网络的架构
∘ 2.1: 循环神经网络的结构
∘ 2.2: 循环神经网络中的关键操作
3: 循环神经网络训练中的挑战
∘ 3.1: 梯度消失
∘ 3.2: 梯度爆炸
∘ 3.3: 梯度裁剪
∘ 3.4: 调整后的初始化策略
4: 从零开始构建循环神经网络
∘ 4.1: 定义 RNN 类
∘ 4.2: 提前停止机制
∘ 4.3: 循环神经网络训练器类:
∘ 4.4: 数据加载与…
风险游戏背后的数学 — 第一部分
在世界征服游戏中,进攻方真的有优势吗?
·发布于Towards Data Science ·阅读时间 10 分钟·2024 年 6 月 28 日
--
我的一个好朋友(嘿,Aron)最近问我,在棋盘游戏《风险》中,进攻和防守的概率是多少。我知道亚历山大大帝和成吉思汗的征服不能完全通过《风险》的机制来解释,但这个问题依然很有趣,而且它很好地展示了概率和数据可视化在智能决策中的应用和作用。

图片由Dave Photoz提供,发布于Unsplash
对于那些不知道的人来说,《风险》是一款世界征服棋盘游戏,进攻方最多可以掷三个骰子,防守方最多掷两个骰子。双方中,掷出最大点数较低的一方失去一名士兵,若点数相同,则由防守方获胜。我们称之为第一次战斗。如果双方都掷至少两个骰子,那么第二高点数较低的一方也会失去一名士兵,再次,平局由防守方获得胜利。我们称之为第二次战斗。
当然,掷 3 个骰子显然比掷 2 个骰子有优势,而平局时获胜的有利条件也同样显而易见。但不那么明显的是,这些优势是如何相互叠加的。
(这里 你可以找到我确认以下概率的代码。)
在分析防守和进攻的相对概率之前,我们先分别分析它们。
防御者的回合较为简单,因为他只掷了两个骰子,因此我们从这里开始。共有 11 种排列可以得到最高投掷为 6。我们可以通过考虑所有可能性来计算:{(1,6), (2,6), (3,6), (4,6), (5,6), (6,1), (6,2), (6,3), (6,4), (6,5), (6,6)}。按类似的逻辑,最高投掷为 5、4、3、2 或 1 的排列数分别为 9、7、5、3 和 1。由于两个骰子的所有排列共有 6 x 6 = 36 种,我们只需将每个排列数除以 6² = 36 即可得到概率。此计算将给出每个数值作为防御骰子的最高投掷的概率。以下图形应会有所帮助。请注意,在所有图形中,红色表示攻击,蓝色表示防御,符合《风险》游戏的配色方案。

表 1 — 最高防御投掷的概率(图片来源:作者)

图表 1 — 最高防御投掷的概率(图片来源:作者)
攻击者的最高投掷稍微复杂一些,因为他掷了 3 个骰子。为了计算得到最高投掷为 6 的排列数,我们可以首先固定最高投掷为 6,并假设它出现在骰子 1 上。显然,骰子 2 和骰子 3 可以是任何 1 到 6 之间的数,总共有 6*6 = 36 种结果。如果接下来我们将最高结果 6 固定在骰子 2 上,我们必须限制骰子 1 不得为 6,因为我们已经考虑过这个情况。骰子 3 可以是任何数,总共有 6x5=30 种额外结果。最后,我们可以将最高结果 6 固定在骰子 3 上,并且我们知道,骰子 1 和骰子 2 的结果不能为 6,因为这些情况已经被计入。因此,我们得到 25 种额外结果,总共有 36+30+25 = 91 种可能性。

表 2 — 最高攻击投掷的概率(图片来源:作者)
我们可以将此推广以计算得到最高投掷结果为 x 的可能结果数。如果最高投掷发生在骰子 1 上,第二个和第三个骰子可以取任何结果,最大值为 x,总共有 x² 种结果。如果最高投掷发生在骰子 2 上,骰子 1 的结果可以是 1 到 x-1 之间的任意数(因为我们已经考虑了骰子 1 为 x 的情况),而第三个骰子可以取 1 到 x 之间的任何值,总共有 x(x-1)=x²-x 种额外结果。最后,我们考虑骰子 3 取值为 x 的情况。此时,唯一尚未计入的选项是骰子 1 和骰子 2 的结果各自取值为 1 到 x-1 之间的任意数,结果数为 (x-1)*(x-1) = x²–2x+1。将所有结果加在一起,我们得到共有 3x²–3x+1 种方式可以得到最高投掷为 x 的结果。

图表 2 — 最高攻击投掷的概率(图片来源:作者)
请注意,总排列数为 216,正如预期的那样,因为 6³ = 216,这也让我们更有信心认为这些计算是正确的。
接下来,让我们直接比较进攻方和防守方的机会。

图表 3 — 最高点数的概率(图片由作者提供)
我们可以看到,防守方掷出 1、2、3 和 4 作为最高点数的概率高于进攻方,而进攻方掷出 5 或 6 作为最高点数的概率则高于防守方。这是第三颗骰子对进攻方有利的体现。
不再拖延,我们开始进入战斗部分。
我们必须分别考虑这两场战斗。在第一部分中,我们将分析第一场战斗,即谁将赢得最高点数的掷骰结果,而第二场战斗,即谁将赢得第二高点数的掷骰结果,则留待第二部分分析。
为了计算进攻方获胜的最高点数的概率,我们将首先计算防守方掷出最高点数为 x 的排列,并计算这些排列中有多少会导致防守方直接获胜、平局(由防守方获胜)或进攻方获胜。我们还需要记住,考虑到进攻和防守的骰子,一共可以有 6⁵ = 7776 种排列方式。这是因为共有 5 颗骰子,每颗骰子可以掷出 6 个不同的点数。
例如,正如上面计算的那样,防守方的最高点数为 5 的概率为 9/36,而总共有 6⁵ = 7776 种排列方式,显然(9/36) * 7776 = 1944 种排列将导致防守方的最高点数为 5。为了获胜,进攻方需要掷出最高点数为 6,正如上面计算的那样,其概率为 91/216,因此(91/216) * 1944 = 819 种排列将导致防守方的最高点数为 5,并且进攻方获胜。为了平局,进攻方必须掷出最高点数为 5,其概率为 61/216,因此(61/216) * 1944 = 549 种排列将导致平局,剩余的排列(1944–819–549 = 576)将导致防守方直接获胜。
我们可以对防守方的所有可能结果进行类似的计算。见下表。

表 3 — 获胜排列(图片由作者提供)
然后,我们可以通过将产生所选结果(例如进攻方获胜)的排列数除以更广泛的结果组(例如防守方掷出最高点数为 5)的排列数,来计算防守方胜利、平局和进攻方胜利的条件²概率。

表 4 — 条件概率(图片由作者提供)
我们还可以将条件概率可视化。

图表 4 — 条件概率(图片由作者提供)
图表 4 给出了许多人最初的错误印象,即攻击方总体上有很大优势。但这是因为它忽略了最高防守掷骰本身的概率。实际上,高掷骰比低掷骰的可能性要大得多,正如表 3 所示。
因此,总概率是一个更有效的衡量标准。总概率指的是整个结果的概率,独立于任何条件。例如,攻击方击败最高防守掷骰为 4 的总概率为 13.68%(见表 5)。这个相对较低的数字是合理的,因为最高防守掷骰为 4 的情况本身就很不可能发生(19.4% — 见表 1)。在条件概率中,假设最高防守掷骰已经是 4,攻击方获胜的概率就要高得多(70.37% — 见表 4),因为在此计算中,我们已经假定了最高防守掷骰是 4,而最初发生这种情况的概率则不再重要。
计算每种结果的总概率更为简单。我们只需要将每个排列的数量除以所有可能排列的总数,即 7776。

表 5 — 胜利总概率(图示由作者提供)
然后我们可以计算攻击获胜的所有排列总数(3667),并将其除以总的可能排列数(7776),得到攻击方的胜率为 47.15%。
下方是按最高防守掷骰计算的总胜率图表。为了简便起见,我们将平局视为防守方的胜利。

图表 5 — 防守掷骰的总胜率(图示由作者提供)
最后,我们将计算防守和攻击方每个可能的最高掷骰的联合³概率。由于攻击和防守的最高掷骰是独立的,我们可以简单地将两者的概率相乘,得到联合概率。请注意,下方的两幅图中,红色表示攻击获胜,蓝色表示防守获胜,浅蓝色表示平局,平局判定为防守方获胜。

表 6 — 联合概率(图示由作者提供)
我们还可以通过图形方式直观展示这些数据。请注意下方图表中坐标轴的配置,已优化为最大程度显示数据。

图表 6 — 联合概率(图示由作者提供)
结论
-
攻击获胜的概率为 47.15%。这一点可以在表 5 中清楚看到。我们将在脚注⁴中详细解释数学推导。
-
防守完全获胜的概率为 28.07%。这一点可以在表 5 中清楚看到。
-
平局的概率,当然是判定为防守方获胜的概率,为 24.77%。这一点可以在表 5 中清楚看到。
-
如果防守方的最高点数是 4,他/她只有 29.63%的机会获胜。这一点可以通过对表 6 中第 4 行的防守胜利概率求和,并除以该行总和来计算。
-
为了让防守方有比攻击方更大的获胜几率,他/她需要掷出最高点数 6。这一点可以在表 3 和表 4 的第 5 行和第 6 行中看到。
-
最可能的整体结果是,攻击方和防守方都掷出了最高点数 6,这种情况的概率为 12.87%,从而防守方获得胜利。这一点在表 6 中可以清楚地看到。
-
在所有攻击方获胜的情况下,最常见的方式是防守方掷出了最高点数 4。这种情况占据了攻击方获胜的 29.02%。这一点可以通过观察表 5 中防守点数为 4 时攻击方的百分比,并除以该列总和来计算。
-
仅有 5.86%的攻击胜利发生在防守方的最高点数为 1 的情况下。这里的微妙之处,甚至可以说是几乎是矛盾的观点是,如果防守方的最高点数是 1,你几乎可以肯定(99.54%)攻击方会获胜,但如果攻击方获胜,你仍然可以有信心(94.14%)防守方没有掷出最高点数 1,尽管坦率地说,和你在得知攻击方获胜之前的信心(97.2%)相比,信心略有降低。(概率真是太神奇了。)第一个数字可以从表 5 中计算得出。第二个数字直接来自表 4。第三个是第一个的补集,第四个可以从表 1 中计算得出。
因此,攻击方在第一次战斗中实际上处于不利地位。那么,成吉思汗是如何在战争中如此有效地击败敌人的呢?也许这预示着第二场战斗中等待我们的是更微妙的情况。或者,这也可能说明《风险》游戏未能有效地解释历史上最伟大的征服。或者,也许,更让人兴奋的是,两者兼有?
第二部分见。
-
对于有数学背景的人来说,这似乎表明,对于所有小于或等于 k 的 x,3x²-3x+1 的和等于 k³,以满足显然的要求,即对所有 x(小于等于 k)求和得到的排列数,其最大值等于 3 个骰子所能产生的 k 个替代方案的总数。实际上,通过归纳法可以简单证明这一点。显然,对于基本情况 x=1 是成立的,因为 3(1)²-3(1)+1 = 1³。然后,我们必须证明,对于所有小于或等于 k 的 x,3x²-3x+1 的和等于 k³,前提是已知所有小于或等于 k-1 的 x,3x²-3x+1 的和等于(k-1)³。因为(k-1)³ = k³-3k²+3k-1,显然,加上 3k²-3k+1 后结果将是 k³。QED。
-
条件概率意味着给定某种条件下的概率。例如,如果防守方掷出了最高点数 6,则攻击方的胜利条件概率为 0%,因为在这种情况下他无法获胜。但这仅仅是如果防守方掷出了最高点数 6。
-
联合概率仅仅意味着两个事件同时发生的概率。
-
为了使数学公式形式化,我们需要一些符号。设 A₁ 和 D₁ 分别表示攻击和防御的最高掷骰点数。此外,设 d 表示防御可能的最高掷骰点数,显然它可以取从 1 到 6 之间的任何值。那么,攻击方获胜的概率可以通过计算以下求和公式来得出:

数学公式(图像由作者提供)
《风险背后的数学 — 第二部分》
攻击方在世界征服游戏中真的占有优势吗?
·发表于Towards Data Science ·10 分钟阅读·2024 年 7 月 3 日
--
在第一部分中,我们讨论了在世界征服游戏《风险》中攻击和防御的相对机会。在第一部分的结尾,我们得出结论:攻击方在第一名士兵的战斗中有 47.15%的机会获胜,我们也好奇这些著名的征服者在这种情况下是如何实现他们的壮举的。我们将第二名士兵的讨论留到了第二部分。

图片由Terry Vlisidis提供,来源于Unsplash
为了提醒大家,在《风险》中,攻击方最多可以掷 3 颗骰子,而防御方最多可以掷 2 颗骰子。两者的最高掷骰结果进行比较,失败的一方失去一名士兵,防御方在平局时获胜。接下来,再比较双方的第二高掷骰结果,失败方再次失去一名士兵,防御方再次在平局时获胜。
好的,既然如此,我们开始深入探讨吧。
(这里你可以找到我确认以下概率的代码。)*
当然,关于防御方的概率,我们只是计算最低的掷骰结果,因为防御方只有两个骰子。因此,这些概率是我们之前看到的最高掷骰结果的镜像。这次,有 11 种可能性产生第二高的掷骰结果为 1,9 种为 2,7 种为 3,依此类推。这个概率可以通过除以 36 来计算,36 是防御方两个骰子所有可能的排列组合总数。

表 1 — 第二高防御掷骰概率(图片由作者提供)

图表 1 — 第二高防御掷骰的概率(图片由作者提供)
计算攻击方三个骰子的第二高掷骰与第一部分的计算有显著不同。说实话,我在这方面有些困难。在接下来的计算中,必须牢记两点。
-
我们必须同时考虑可能的结果有多少种以及每个结果可能发生的方式有多少种。例如,结果(6, 2, 3)显然是一个单一的结果,但它可以以 6 种方式发生,这与每个值出现在哪个骰子上有关。它可以是{(2, 3, 6), (2, 6, 3), (3, 2, 6), (3, 6, 2), (6, 2, 3), (6, 3, 2)}中的任何一种。因此,这个结果对应于 16 = 6 种排列。另一个例子是,结果中恰好有两个 1,这实际上是 5 个结果的集合,因为剩余的骰子可以取 2 到 6 之间的任何值。并且它可以出现在 3 种方式中,{(1, 1, x), (1, x, 1), (x, 1, 1)},对应于剩余骰子的 3 个可能位置,因此这个结果实际上对应于 53 = 15 种排列。
-
我们必须小心对待双骰和三骰的情况。这些情况必须单独考虑,因为,虽然获得(1, 2, 3)结果有 6 种方式,但获得(1, 2, 2)只有 3 种方式,获得(2, 2, 2)只有 1 种方式。
在考虑了上述情况之后,我们可以继续进行计算。
考虑获得第二高掷骰为 1 的概率。这是相对直接的。显然,最低的掷骰也是 1。现在,我们将忽略所有 3 个骰子都是 1 的情况。然后,最高的骰子可以取 2 到 6 之间的任何值,并且可以出现在任意一个骰子上,因为我们没有指定哪个骰子包含最高的点数。这就得到了 3*5=15 种排列。加上三骰都是 1 的情况,总共有 16 种排列。通过对称的推理,我们可以计算得出同样的排列数量会得到第二高掷骰为 6 的结果。
接下来,考虑第二高掷骰为 2 的情况。现在,我们将忽略多个 2 的可能性,假设最高掷骰大于 2,最低掷骰小于 2。最高的掷骰可以取 4 个值(3-6),而最低掷骰必须为 1,共有 4 种结果,这些结果可以出现在 6 种骰子位置排列中(最高掷骰的 3 种位置可能性(骰子 1、骰子 2 或骰子 3),以及剩余的 2 种位置可能性),所以总共有 46=24 种排列。现在我们将考虑双 2 的情况,但不考虑三 2 的情况。如果恰好有 2 个 2,那么剩余的骰子可以取除 2 之外的 5 个值,而这个剩余的骰子可以是任意 3 个骰子中的一个,因此会增加 53=15 种排列。再加上三 2 的情况,总共得到 24+15+1 = 40 种排列。类似的推理结果对于第二高掷骰为 5 的情况也适用。
最后,获得 3 或 4 的概率如何呢?我们从 3 开始。再次忽略多个三的可能性,最高点数可以是三个值中的任意一个(4、5 或 6),而较低点数可以是两个值中的任意一个(1 或 2),因此总共有 6 种结果。这种情况可以在任意六种骰子排列中发生,总共为 66 = 36 种排列组合。在恰好两个三的情况下,另一个骰子的点数可以是 5 个值中的任何一个(除 3 以外的任何值),并且可以出现在任意三个骰子中的一个,总共为 53 = 15 种排列组合。再加上三个三的可能性,得到的排列组合总数为 36+15+1=52 种。类似的计算可以得出第二高点数为 4 的排列组合也有 52 种。这些结果总结在下面的图示中。

表 2 — 第二高进攻点数的概率(图像由作者提供)

图表 2 — 第二高进攻点数的概率(图像由作者提供)
注意,进攻结果的概率是完全对称的。更精确地说,P(x) = P(6-x)。我们稍后会回到这个点。
接下来,我们直接比较进攻和防守的概率。

图表 3 — 第二高点数的概率(图像由作者提供)
我们可以看到,进攻在这里具有明显的优势。进攻获得 4、5 或 6 的点数的可能性远高于防守。
我们现在准备计算第二个士兵获胜的相对概率。这个部分与我们在第一部分中做的并行计算基本一致。我们需要计算防守取得第二高点数为 x 的排列组合数,然后确定其中有多少排列组合会导致防守直接获胜、平局或进攻获胜。
例如,由于如上所述,防守的第二高点数为 5 的概率是 3/36,并且总共有 6⁵= 7776 种排列组合,显然(3/36) * 7776 = 648 种排列组合会使防守的第二高点数为 5。为了获胜,进攻需要得到第二高点数为 6 的掷骰结果,之前计算过,这一概率为 16/216,因此(16/216) * 648 = 48 种排列组合中,防守的第二高点数为 5 的情况下,进攻将获胜。为了平局,进攻必须掷出第二高点数为 5 的结果,这一概率为 40/216,因此(40/216) * 648 = 120 种排列组合会导致平局,剩下的(648–120–48 = 480)种排列组合将导致防守直接获胜。

表 3 — 排列组合计数(图像由作者提供)
我们还可以计算相应的条件概率,即在给定某一防守点数的情况下,计算各队的获胜概率。

表 4 — 条件概率(图像由作者提供)

图表 4 — 条件概率(图片来源:作者)
请注意,上述表格和图表的完全对称性。因此,给定防御掷骰结果为 x 的攻击胜利概率,与给定防御掷骰结果为 6-x 的防御胜利概率相同。此外,给定防御掷骰结果为 x 的平局概率,与给定防御掷骰结果为 6-x 的平局概率相同。这当然是因为上面提到的攻击第二高掷骰结果的对称性。
与第一部分一样,表 4 和图表 4 给人一种误导性的印象。它们暗示攻击和防御处于平等地位,但这些是条件概率,因此忽略了防御掷骰结果较低的情况出现的可能性更大。
另一方面,总概率将允许我们看到这一点。在下表中,我们可以清楚地看到,尽管上面提到的条件对称性,攻击方在防御掷骰为 1 时的胜率明显高于防御方在防御掷骰为 6 时的胜率(以绝对值计)。

表 5 — 胜利总概率(图片来源:作者)
以下是根据第二高防御掷骰结果的总胜率图表。

图表 5 — 总胜率(图片来源:作者)
上述图表最终揭示了我们大多数人凭直觉感觉到的事实,无论是通过玩《风险》游戏的经验,还是通过数学本能,即攻击方具有明显优势。这个优势具体体现在第二轮战斗中。
最后,我们可以计算每种可能的攻击和防御第二高掷骰结果的联合概率。联合概率就是两个或多个事件同时发生的概率。由于攻击和防御的掷骰是独立的,联合概率只是攻击和防御各自掷骰结果的个别概率的乘积。

表 6 — 联合概率(图片来源:作者)
请参见以下图表,以便更好地可视化上述图表。注意坐标轴的配置,它已被选定以便最佳查看。

图表 6 — 联合概率(图片来源:作者)
在我们总结之前,还有一个最终因素需要考虑,那就是攻击方赢得两名士兵的概率。然而,与我头脑中计算的 3667 种排列和 4724 种排列(分别导致攻击方赢得第一名和第二名士兵的结果)相比,这个计算需要考虑并求和 36 个独立的排列计数。因为这个计算依赖于防守方的两次掷骰结果,而之前的计算只需考虑防守方较高或较低的掷骰结果。我因此不会再为自己或你枯燥地计算和求和这 36 个排列计数,而是依赖 Python 来计算排列数。接下来我将仅提供代码。
from itertools import product
import numpy as np
import pandas as pd
def check_both(result, attack_dice=3):
"""
Given a result of multiple dice, and a number of attack dice, determine whether the attack will win both soldiers,
the defense will both soldiers, or each will win a soldier.
:param result: array of dice results, where the attack dice are declared first
:param attack_dice: number of dice which represent the attack dice
:return:
"""
if np.partition(result[:attack_dice], -1)[-1] > np.partition(result[attack_dice:], -1)[-1] and \
np.partition(result[:attack_dice], -2)[-2] > np.partition(result[attack_dice:], -2)[-2]:
return 'attack_both'
elif np.partition(result[:attack_dice], -1)[-1] > np.partition(result[attack_dice:], -1)[-1] or \
np.partition(result[:attack_dice], -2)[-2] > np.partition(result[attack_dice:], -2)[-2]:
return 'one_one'
else:
return 'defense_both'
# produce array of all possible permutations of 5 dice
arr = np.array(list(product([1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6],
[1, 2, 3, 4, 5, 6]))).transpose()
# calculate and print counts of various result types
print(pd.Series(np.apply_along_axis(check_both, axis=0, arr=arr)).value_counts())
在下面的图示中,停战是指攻守双方各失一名士兵,而攻击是指攻击方赢得两名士兵,防守亦然。

表格 7 — 总体结果概率(作者提供的图片)

图表 6 — 总体结果概率图(作者提供的图片)
我曾一度以为我终于可以使用饼图展示上面的图表而不必担心批评,但遗憾的是,概率太过相似。等待仍在继续。
我们可以看到,尽管如第一部分计算所示,防守方在第一次战斗中有些许优势,但总体上,攻击方由于在第二场战斗中占有显著的优势,依然占据上风。
结论
-
攻击方获胜的概率为 60.75%。这可以在表格 5 中清楚地看到。
-
防守方直接获胜的概率为 22.58%。这可以在表格 5 中清楚地看到。
-
平局的概率,或许稍微令人惊讶,恰好是 1/6,或 16.67%。这可以在表格 5 中清楚地看到。
-
如果防守方的第二高掷骰结果是 3,那么他的获胜概率(包括平局的概率)恰好是 50/50。这可以在表格 4 中看到。
-
攻击方第二高掷骰的概率是完全对称的。这可以在表格 2 中看到。
-
条件概率是完全对称的。这可以在表格 4 中看到。
-
最有可能的总体结果,概率为 7.36%,是防守方掷出 1,而攻击方掷出 3 或 4。这可以在表格 6 中看到。
-
攻击方同时赢得两场战斗的概率为 37.17%。这可以在表格 7 中看到。
-
防守方赢得两场战斗的概率为 33.58%。这可以在表格 7 中看到。
-
攻防双方各赢一场战斗的概率为 29.26%。这可以在表格 7 中看到。
我们现在已经能够给出对我们最初问题的明确答案。在一个 3 比 2 的骰子对战中,进攻确实具有总体优势。
我必须说,我很享受这次对《风险》游戏数学和概率的探讨,如果你也喜欢的话,那就更好了。
下次再见。
“维度灾难”背后的数学原理
深入了解“维度灾难”这一概念,并理解所有在高维中出现的惊人现象背后的数学原理。
·发表于Towards Data Science ·阅读时长 9 分钟·2024 年 4 月 20 日
--

图片来源:Dall-E
在机器学习领域,处理高维向量不仅是常见的,而且是至关重要的。这一点可以通过流行模型如 Transformer 的架构来说明。例如,BERT 使用 768 维的向量来编码其处理的输入序列的标记,并更好地捕捉数据中的复杂模式。考虑到我们的大脑难以想象超过三维的任何事物,使用 768 维的向量确实令人震惊!
虽然一些机器学习和深度学习模型在这些高维场景中表现出色,但它们也带来了许多挑战。在本文中,我们将探讨“维度灾难”这一概念,解释与之相关的一些有趣现象,深入研究这些现象背后的数学原理,并讨论它们对你的机器学习模型的普遍影响。
请注意,与本文相关的详细数学证明可以在我的网站上找到,作为本文的补充延伸。
什么是维度灾难?
人们常常认为在三维空间中熟悉的几何概念在更高维空间中也会表现得类似,但事实并非如此。随着维度的增加,许多有趣且反直觉的现象会出现。“维度灾难”是著名数学家理查德·贝尔曼(Richard Bellman)提出的术语,用来描述所有这些令人惊讶的效应。
高维空间的特殊之处在于其“体积”(我们很快会更详细地探讨)是指数增长的。假设有一条从 1 到 10 的刻度线(1 维),这条线有 10 个整数。将其扩展到 2 维:它现在是一个边长为 10 × 10 = 100 的正方形,包含 100 个整数坐标的点。现在考虑“仅仅”80 维:你将已经有10⁸⁰个点,这相当于宇宙中的原子数量。
换句话说,随着维度的增加,空间的体积呈指数增长,导致数据变得越来越稀疏。
高维空间是“空的”
另一个例子是这样的。我们想计算在一个单位超立方体内两个点之间的最大距离(每条边的长度为 1):
-
在1 维(超立方体是从 0 到 1 的线段)中,最大距离仅为 1。
-
在2 维(超立方体形成一个正方形)中,最大距离是对角线两点[0,0]和[1,1]之间的距离,即√2,通过毕达哥拉斯定理计算得出。
-
将这个概念扩展到n 维,位于[0,0,…,0]和[1,1,…,1]的两点之间的距离是√n。这个公式产生的原因是每增加一个维度,都为平方根下的和添加一个 1 的平方(再次应用毕达哥拉斯定理)。
有趣的是,随着维度 n 的增加,超立方体内的最大距离以 O(√n)的速度增长。这个现象展示了递减收益效应,即维度空间的增加导致空间距离的增益成比例地减少。关于这一效应及其影响的更多细节将在本文接下来的部分中讨论。
高维中的距离概念
让我们深入探讨一下在上一节中开始探索的距离概念。
我们第一次看到了高维空间如何使得距离的概念几乎变得毫无意义。但这到底意味着什么,我们能否在数学上可视化这种现象呢?
让我们考虑一个实验,使用之前定义的 n 维单位超立方体。首先,我们通过在这个立方体中随机采样许多点来生成一个数据集:我们实际上模拟了一个多变量均匀分布。然后,我们从这个分布中抽取另一个点(一个“查询”点),并观察其最近邻和最远邻在数据集中的距离。
这是对应的 Python 代码。
def generate_data(dimension, num_points):
''' Generate random data points within [0, 1] for each coordinate in the given dimension '''
data = np.random.rand(num_points, dimension)
return data
def neighbors(data, query_point):
''' Returns the nearest and farthest point in data from query_point '''
nearest_distance = float('inf')
farthest_distance = 0
for point in data:
distance = np.linalg.norm(point - query_point)
if distance < nearest_distance:
nearest_distance = distance
if distance > farthest_distance:
farthest_distance = distance
return nearest_distance, farthest_distance
我们还可以绘制这些距离:

随着 n 增加,最近邻和最远邻的距离(图由作者提供)
使用对数尺度,我们观察到,最近邻和最远邻之间的相对差异随着维度的增加而趋于减小。
这是一个非常违反直觉的现象:正如前一部分所解释的那样,由于空间体积的指数增长,点之间的距离非常稀疏,但与此同时,点之间的相对距离变得更小。
最近邻的概念消失了
这意味着,距离的概念随着空间维度的增加变得越来越不相关,也越来越难以区分。正如你可以想象的那样,这会给完全依赖于距离的机器学习算法(如 kNN)带来问题。
数学:n-球
现在我们将讨论一些其他有趣的现象。为此,我们需要使用n-球。n-球是 n 维空间中球的推广。半径为 R 的 n-球是距离空间中心 0 的点集合,其距离最大为 R。
让我们考虑半径为 1 的情况。1-球是区间[-1, 1]。2-球是由单位圆限定的圆盘,其方程为 x² + y² ≤ 1。3-球(我们通常所说的“球”)的方程是 x² + y² + z² ≤ 1。正如你所理解的,我们可以将这一定义扩展到任意维度:

现在的问题是:这个球的体积是多少?这个问题并不简单,需要用到很多数学知识,我在这里不会详细说明。然而,你可以在我的网站上找到所有的细节,参见我关于 n-球体积的文章。
经过一番有趣的(积分学)推导后,你可以证明 n-球的体积可以表示为如下形式,其中Γ表示伽马函数。

例如,对于 R = 1 和 n = 2,体积为πR²,因为Γ(2) = 1。这确实是 2-球的“体积”(在这种情况下也叫做“圆的面积”)。
然而,除了作为一个有趣的数学挑战,n-球的体积还有一些非常惊人的性质。
随着维度 n 的增加,n-球的体积趋近于 0。
这一点对于每个半径都适用,但让我们通过几个 R 值来直观地展示这一现象。

随着维度增加,不同半径的 n-球体积(图片来源:作者)
如你所见,它最终趋近于 0,但开始时会增加,之后又下降到 0。对于 R = 1,体积最大的球是 5-球,且随着 R 的增大,达到最大值的 n 值向右移动。
这是单位 n-球体积的前几个值,直到 n = 10。

不同 n 值的单位 n-球体积(图片来源:作者)
高维单位球的体积集中在其表面附近。
对于低维度,球的体积看起来相当“均匀”:但在高维度中情况并非如此。

一个球壳
让我们考虑一个半径为 R 的 n-球和另一个半径为 R-dR 的 n-球,其中 dR 非常小。两个球之间的部分被称为“壳”,对应于球体表面附近的部分(见上图中的 3D 可视化)。我们可以计算球体“内部”体积与薄壳体积的比率。

随着 n 增加,比例(内部体积 / 总体积)(作者提供的图像)
正如我们所看到的,它迅速收敛到 0:在高维空间中,几乎所有的体积都集中在表面附近。例如,对于 R = 1,dR = 0.05 和 n = 50,约 92.3% 的体积集中在薄壳中。这表明,在更高维度下,体积集中在“角落”里。这再次与我们之前看到的距离概念的扭曲有关。
注意,单位超立方体的体积(这里指的是一个以零为中心、边长为 2 的立方体)是 2ⁿ。单位球在非常高的维度下基本上是“空的”,而单位超立方体则与之相反,获得了指数级增长的更多点。再一次,这表明了“最近邻”概念的效果失效,因为当维度 n 很大时,查询点 q 周围几乎没有点位于距离 R 内。
维度诅咒、过拟合和奥卡姆剃刀
维度诅咒与过拟合原则密切相关。由于空间体积随着维度的指数增长,我们需要非常大的数据集来充分捕捉和建模高维模式。更糟糕的是:我们需要一个随着维度指数增长的样本数量来克服这一局限性。这种情况,特征很多而数据点相对较少,特别容易过拟合。
奥卡姆剃刀建议,更简单的模型通常比复杂的模型更好,因为它们不太可能过拟合。这个原则在高维上下文中特别重要(维度诅咒发挥作用的地方),因为它鼓励减少模型复杂度。
在高维场景中应用奥卡姆剃刀原则,意味着减少问题本身的维度(通过 PCA、特征选择等方法),从而缓解维度诅咒的某些影响。简化模型结构或特征空间有助于管理稀疏的数据分布,并使得距离度量重新变得有意义。例如,降维是应用 kNN 算法之前一个非常常见的初步步骤。更近的技术,如ANNs(近似最近邻),也作为应对高维场景的一种方法。
维度的祝福?

图像由 Dall-E 提供
尽管我们概述了高维设置在机器学习中的挑战,但也有一些优势!
-
高维度可以增强线性可分性,使得像核方法这样的技术更加有效。
-
此外,深度学习架构尤其擅长在高维空间中导航并提取复杂模式。
和机器学习中的常见情况一样,这是一种权衡:利用这些优势需要在增加的计算需求与潜在的模型性能提升之间找到平衡。
结论
希望这能让你对高维中的“奇异”几何和它对机器学习模型开发所带来的种种挑战有所了解。我们看到,在高维空间中,数据非常稀疏,但也倾向于集中在角落,并且距离失去了它的有效性。如果你想更深入了解 n-球和数学证明,欢迎访问我网站上的这篇文章的扩展版。
虽然“维度灾难”揭示了高维空间中的重大限制,但看到现代深度学习模型在应对这些复杂性方面越来越娴熟,令人兴奋。例如,考虑嵌入模型或最新的 LLM,它们利用非常高维的向量更有效地识别和建模文本模式。
想了解更多关于 Transformers 以及它们如何在幕后转换你的数据吗?请查看我之前的文章:
深入了解 Transformers 架构及其为何在语言任务中无可匹敌
towardsdatascience.com
-
随时在LinkedIn上与我联系
-
在 GitHub 上关注我,获取更多内容
-
访问我的网站:maximewolf.com
参考文献:
-
[1] “n-球的体积。” 维基百科,
en.wikipedia.org/wiki/Volume_of_an_n-ball -
[2] “维度灾难。” 维基百科,
en.wikipedia.org/wiki/Curse_of_dimensionality#Blessing_of_dimensionality -
[3] Peterson, Ivars. “Nth 维的冒险。” 美国科学家,
www.americanscientist.org/article/an-adventure-in-the-nth-dimension -
[4] Zhang, Yuxi. “维度灾难。” Geek Culture on Medium,2021 年 7 月,
medium.com/geekculture/curse-of-dimensionality-e97ba916cb8f -
[5] “近似最近邻(ANN)。” Activeloop,
www.activeloop.ai/resources/glossary/approximate-nearest-neighbors-ann/
AI 解释性的重要性
我们现在还关心我们的机器学习是如何做出决策的吗?
·发布于 Towards Data Science ·8 分钟阅读·2024 年 6 月 4 日
--
今天,我想谈谈一点哲学,讨论一下机器学习中的解释性和风险如何交汇。

图片由 Kenny Eliason 提供,来源于 Unsplash
我们所说的解释性是什么意思?
简而言之,机器学习中的解释性是指你可以向一个人类用户(不一定是技术专家)解释模型是如何做出决策的。例如,决策树就是一个容易解释的(有时被称为“白盒”)模型,你可以指出“模型将数据分为房屋面积大于 1 的和面积小于或等于 1 的”等等。其他类型的更复杂的模型可以是“灰盒”或“黑盒”——这些模型对于人类用户来说,理解起来会越来越困难,甚至是不可能的。
老学校
我在机器学习教育中的一个基础性教训是,我们与模型(通常是增强型树模型)之间的关系,最多应该是“信任,但要验证”。当你训练一个模型时,不要仅仅接受初步预测的表面结果,而是要花费一些时间去彻底检验。测试模型在非常异常的离群值上的表现,即便这些情况在现实中不太可能发生。如果树足够浅,可以绘制出树的结构。使用特征重要性、Shapley 值和 LIME 等技术来验证模型是否使用了与你对主题和逻辑的理解相符的特征。给定树中的特征分割是否与你对该领域的了解一致?在建模物理现象时,你还可以将模型的行为与我们对事物如何运作的科学知识进行比较。不要仅仅相信你的模型在正确地处理问题,而是要进行验证。
不要仅仅相信你的模型在正确地处理问题,而是要进行验证。
引入神经网络
随着神经网络的重要性爆炸式增长,我们不得不考虑的最大权衡之一就是,这种可解释性变得极为困难,并且因为架构的工作方式而发生显著变化。
神经网络模型在每一层中都对输入数据应用函数,将数据以多种方式变化,然后最终将数据传递到最终层的目标值。这种作用的结果是,与基于树的模型的分支不同,输入和输出之间的中间层通常无法合理地被人类理解。你也许可以找到某个中间层中的特定节点,并查看其值如何影响输出,但将其与人类能够理解的实际、具体输入联系起来通常会失败,因为即使是一个简单的神经网络,其层次的抽象程度也非常高。
这一点可以通过“哈士奇与狼”问题来清楚地说明。一个卷积神经网络被训练来区分哈士奇和狼的照片,但经过调查发现,模型是根据背景的颜色做出判断的。哈士奇的训练照片比狼的训练照片更不容易出现在雪地中,因此每当模型接收到一张雪景背景的图像时,它就预测图像中会出现狼。模型使用了人类没有考虑到的信息,并基于错误的特征发展出了其内部逻辑。
这意味着传统的“这个模型是否‘思考’问题的方式与物理或直观现实相符?”的测试变得过时。我们不能像以前那样告诉模型是如何做出选择的,而是我们更多地依赖于试错法。为此,存在系统性的实验策略,本质上是将模型与许多反事实进行测试,以确定输入的哪些种类和程度的变化会引起输出的变化,但这必然是艰巨且计算密集型的。
我们不能像以前那样告诉模型是如何做出选择的,而是我们更多地依赖于试错法。
我并不是要辩称理解神经网络如何执行其任务的努力完全没有希望。许多学者对可解释 AI,文献中称为 XAI非常感兴趣。今天可用的各种模型意味着我们可以并且应该追求许多方法。注意力机制是一项技术进步,帮助我们理解模型在处理输入时最关注的部分/受到驱动的部分,这非常有帮助。Anthropic 最近发布了一份非常有趣的报告,深入探讨了 Claude 的可解释性,试图了解哪些单词、短语或图像根据提示使用稀疏自编码器能引发最大的激活。 我在上面描述的工具,包括 Shapley 和 LIME,也可以应用于一些种类的神经网络,比如 CNNs,尽管结果可能很难解释。但随着我们增加复杂性,按定义,人类观众或用户将更难理解和解释模型是如何工作的。
考虑随机性
这里一个重要的额外因素是要认识到许多神经网络包含随机性,因此你不能总是依赖模型在看到相同输入时返回相同的输出。特别是,生成性 AI 模型有意地可能会从相同输入生成不同的输出,这样它们看起来更“人性化”或富有创造性——我们可以通过[tuning the “temperature”](https://medium.com/@harshit158/softmax-temperature-5492e4007f71#:~:text=Temperature is a hyperparameter of LSTMs (and neural networks generally,the logits before applying softmax.)来增加或减少这种变化的极端性。这意味着有时我们的模型会选择返回一个不是最具概率性期望的输出,而是一些“令人惊讶”的内容,这增强了结果的创造性。
在这种情况下,我们仍然可以通过试错的方法来尽量发展对模型行为及其原因的理解,但这变得复杂得多。方程式中的唯一变化不再是不同的输入,而是输入的变化加上由于随机性引起的未知变动。你的输入变化是否改变了响应,还是那是随机性的结果?我们常常无法真正知道。
你的输入变化是否改变了响应,还是那是随机性的结果?
现实世界的影响
那么,这把我们带到哪里呢?我们为什么要知道模型是如何进行推理的?作为机器学习开发者和模型使用者,这对我们来说有什么意义?
如果我们构建的机器学习模型将帮助我们做出决策并塑造人们的行为,那么结果的责任就应该由我们承担。有时,模型预测会经过人工调解之后再应用于我们的世界,但我们越来越多地看到模型被放开,生产中的推理结果被直接使用而没有进一步审查。公众现在比以往任何时候都更容易接触到这些极其复杂的机器学习模型。
因此,对我来说,理解模型如何以及为什么做出它所做的决定,就像测试一个制造的玩具是否没有铅漆,或者一台机器是否在正常使用下不会断裂并伤害到人的手一样,是一种尽职调查。这要比测试更难,但确保我不会将一个使生活更糟的产品推向市场,是我坚定的道德立场。如果你正在构建一个机器学习模型,你就有责任对该模型所做的事以及它对人们和世界的影响负责。因此,要真正有信心你的模型是安全的,你需要一定程度的理解,了解它是如何以及为什么返回它的输出的。
如果你正在构建一个机器学习模型,你就有责任对该模型所做的事以及它对人们和世界的影响负责。
顺便提一下,读者可能还记得我之前关于欧盟人工智能法案的文章,其中提到模型的预测需要经过人工监督,并且不得根据受保护的特征做出具有歧视效应的决策。所以,即使你不被道德论点所打动,对于我们中的许多人来说,法律的动机也是存在的。
即使我们使用神经网络,我们仍然可以使用工具更好地理解我们的模型是如何做出决策的——我们只需要花时间并努力去做到这一点。
但是,进展呢?
从哲学角度来看,我们可以(而且人们确实这么做)辩称,机器学习技术达到一定复杂度后,需要放弃完全理解它的愿望。这可能是对的!但我们不应忽视由此带来的权衡和我们所接受的风险。最好的情况是,你的生成性 AI 模型主要按照预期运行(也许如果你控制了温度,并且你的模型非常缺乏创意),不会做出太多意外的事情;而最坏的情况是,你释放了一个灾难,因为模型以你完全没有预料到的方式做出反应。这可能意味着你看起来很傻,或者可能意味着你的生意结束,或者可能意味着对人们造成真正的身体伤害。当你接受模型的可解释性是无法实现的时,这就是你自己肩负的风险。你不能说“哦,模型就是这样”——当你建造了这个东西并做出了释放它或使用其预测的有意识决定时。
各大大小小的科技公司都已接受生成性 AI 有时会产生不正确、危险、歧视性以及其他有害的结果,并认为为了获得其感知的好处,这些风险是值得的——我们知道这一点,因为那些经常表现出不良行为的生成性 AI 模型已经被公开发布。我个人很困扰的是,科技行业在没有任何明确考虑或讨论的情况下,选择将公众置于这种风险之中,但魔 genie 已经放出来了。
现在怎么办?
对我来说,追求 XAI 并试图让它跟上生成性 AI 的进步是一个崇高的目标,但我认为我们不会看到大多数人能够轻松理解这些模型是如何运作的,因为它们的架构非常复杂且具有挑战性。因此,我认为我们还需要实施风险缓解措施,确保那些负责日益复杂的模型的人,对这些影响我们日常生活的产品及其安全负责。由于结果往往是不可预测的,我们需要框架来保护我们的社区免受最坏情况的影响。
我们不应把所有的风险都视为无法承受,但我们需要清醒地认识到风险的存在,并且由于 AI 前沿的可解释性挑战,机器学习的风险比以往任何时候都更难以衡量和预见。唯一负责任的选择是将这种风险与这些模型所带来的实际利益进行平衡(而不是将某些未来版本的预期或承诺利益视为理所当然),并据此做出深思熟虑的决策。
阅读更多我的作品,请访问 www.stephaniekirmer.com。
进一步阅读
-
扩展单一语义性:从 Claude 3 Sonnet 中提取可解释特征(2024 年 5 月 21 日,Anthropic 团队)
-
可解释的生成型人工智能:调查、概念化与研究议程(2024 年 4 月 15 日;Johannes Schneider)——这篇文章内容非常易读,推荐大家阅读。
-
卷积神经网络可解释性方法分析(2023 年 1 月,Von der Haar 等人)
-
可解释的卷积神经网络:分类法、回顾与未来方向(2023 年 2 月 2 日;Ibrahim 等人)
-
谷歌的人工智能告诉用户往比萨饼上加胶水、吃石头并制造氯气(2024 年 5 月 23 日)
持续学习的指标
这三种指标是常用的
·发布于 Towards Data Science ·4 分钟阅读·2024 年 11 月 18 日
--
持续学习是机器学习的一个子领域,处理的是在不断到来的数据上增量训练神经网络的问题。关键在于,数据不能完全存储,且往往不能从旧任务中携带任何样本。由于网络仅在当前可用的数据上进行优化,它们会覆盖旧的参数。在覆盖这些参数时,旧的知识通常会被破坏,也就是遗忘。

为了基准化持续学习和灾难性遗忘,持续学习研究中使用了几种评估指标。在本文中,我将详细介绍三种最常用的指标。虽然我将使用分类作为示例,但这些指标同样适用于其他问题,例如回归。如果你对持续学习这个主题不熟悉,我建议你阅读我之前的两篇 文章,以便更深入理解该主题。正如我以前做过的那样,本文结尾我还会提供一些阅读推荐,帮助你进一步探索这一话题。
平均准确率
第一个常用的指标是平均准确率,通常缩写为 ACC。顾名思义,它衡量每个任务的(测试集)准确率,然后计算各个任务特定准确率的平均值。形式上是……
机器中的镜像:生成性 AI、偏见与追求公平
生成性 AI 在招聘决策中的偏见实验
·发布于 Towards Data Science ·12 分钟阅读·2024 年 1 月 16 日
--

历史上第一次,数据科学变得触手可及。几乎对任何人都可以使用。随着生成性 AI 的出现,数据科学的力量已经牢牢地掌握在普通消费者手中。反过来,生成性 AI 以其惊人的能力模仿甚至超越人类创造力,至少是我们对创造力的判断,已经成为数据科学领域中的一股强大力量。
但我的职业生涯并不是从数据科学家开始的。相反,它起步于一个地下实验室,进行关于人类心智的社会心理生理学研究。正是在这里,我学会了看到数据的社会意义、每一个数据点背后的人性,也正是因为这个原因,我现在看到随着生成性 AI 在社会中的影响日益增大,出现了更大的图景。
生成性 AI,凭借其强大的数据处理能力和生成文本的能力,在简化流程和自动化任务方面具有巨大的潜力。但随着我们越来越多地将这些模型交给它们做出关键决策,一个令人不寒而栗的现实出现了:它们可能会反映并放大它们所消化数据中固有的偏见。这个严峻的现实通过我最近进行的一项实验得到了加剧,其中一个训练于求职者资料的 AI…
缺失的拼图:符号人工智能在解决生成式人工智能难题中的作用
符号推理或许依然存在……
·发表于 Towards Data Science ·阅读时长 7 分钟 ·2024 年 8 月 16 日
--

图片来源:Arno Senoner 于 Unsplash
幻觉、事实错误、公众兴趣下降、以及 投资骤降 ——所有这些以及更多内容,最近都在新闻和评论中被提到,似乎预示着生成式人工智能将面临严重的挫折。有些人甚至呼吁 AI “泡沫” 的终结,认为他们“早就预测到了这一点。”
在这篇文章中,我讨论了当前生成式人工智能系统的难题如何能够(或已经)通过老派的符号推理得到缓解。
但让我们首先解释一下当前的难题。
以基础的算术为例。昨天我问 ChatGPT,123,456.78 的立方根是多少,得到的答案是 49.3989。接着我又让 ChatGPT 把 49.3989 自己乘两次,假设能够回到我的 123,456.78 数字,结果——没开玩笑——ChatGPT 给出的结果还是 123,456.78,假装验证它的操作是正确的。我本可以相信它,但为了确保,我用计算器做了一遍运算,结果是 49.3989 自己乘两次得到的是 120,545.73,差不多偏离了 3,000 单位。这当然意味着,第一个操作(立方根)是错的……
MMD-Critic 方法解析
一种强大但鲜为人知的数据总结和可解释性 AI 方法
·发表于 Towards Data Science ·11 分钟阅读·2024 年 8 月 27 日
--
尽管 MMD-Critic 方法是一个强大的数据总结工具,但它的使用和“覆盖面”却出奇地少。也许这是因为存在一些更简单且更成熟的数据总结方法(例如 K-medoids,参见 [1] 或更简单地,查看 维基百科页面),又或者是因为在此之前并没有 Python 包支持该方法(直到现在)。无论如何,原始论文 [2] 中展示的结果比 MMD-Critic 当前的应用更值得广泛使用。因此,我将在这里尽可能清晰地解释 MMD-Critic 方法。我还发布了一个 开源 Python 包,其中实现了该技术,方便你轻松使用。
原型和批评
在深入探讨 MMD-Critic 方法之前,值得讨论一下我们究竟想要实现什么目标。最终,我们希望通过一个数据集,找到代表性的数据示例(原型),以及可能干扰我们机器学习模型的边缘案例(批评)。

来自 [2] 的 MNIST 数据集的原型和批评示例。
这种方法可能有很多有用的地方:
-
通过查看典型和非典型的示例,我们可以获得对数据集的一个非常好的总结视图
-
我们可以通过测试模型在批评样本上的表现,来了解它们如何处理边缘案例(显而易见,这是非常重要的)
-
虽然可能没有那么实用,我们可以使用原型来创建一个类似 K-means 的自然可解释算法,其中最接近新数据点的原型被用来标记该数据点。然后解释就很简单,因为我们只需要向用户展示最相似的数据点。
-
更多
你可以在这本书的第 6.3 节中找到更多关于该应用的信息(以及关于 MMD-Critic 的一个不错的解释),但可以简单地说,找到这些例子在很多方面都是有用的。MMD-Critic 使我们能够做到这一点。
最大均值差异
不幸的是,我不能声称对最大均值差异(MMD)有超严格的理解,因为这种理解需要有较强的泛函分析背景。如果你有这样的背景,你可以在这里找到介绍该度量的论文。
简单来说,MMD 是一种确定两个概率分布之间差异的方法。正式地,对于两个概率分布P和Q,我们定义它们的 MMD 为

两个分布 P,Q 的 MMD 公式
这里,F是任何函数空间——即,具有相同定义域和陪域的函数集合。还要注意,符号x~P意味着我们将x视为从分布P中抽取的随机变量——即,x是由 P描述的。因此,这个公式找到当X和Y通过我们空间F中的某个函数变换时,它们期望值之间的最大差异。
这可能有点难以理解,但这是一个例子。假设X是Uniform(0, 1)(即从 0 到 1 随机选择一个数的分布),而Y是Uniform(-1, 1)。我们还假设F是一个包含三个函数的简单函数族——f(x) = 0,f(x) = x,和f(x) = x²。在我们的空间中对每个函数进行迭代,得到:
-
在f(x) = 0 的情况下,当x ~ P时,E[f(x)]为 0,因为无论选择什么x,f(x)都会是 0。对于x ~ Q的情况也一样。因此,我们得到一个均值差异为 0。
-
在f(x) = x的情况下,我们有 E[f(x)] = 0.5(对于 P 的情况)和 0(对于 Q 的情况),因此我们的均值差异为 0.5。
-
在f(x) = x²的情况下,我们注意到

变换函数 f 作用下随机变量x的期望值公式
因此在 P 的情况下,我们得到

在分布 P 下,f(x)的期望值
在 Q 的情况下,我们得到

在分布 Q 下,f(x)的期望值
因此,在这种情况下我们的差异也是 0。我们函数空间上的上确界为 0.5,所以这就是我们的 MMD。
现在你可能会注意到我们 MMD 的一些问题。它似乎高度依赖于我们选择的函数空间,并且在大型或无限函数空间中计算起来也显得非常昂贵(甚至是不可能的)。不仅如此,它还要求我们知道分布P和Q,这是不现实的。
后一个问题很容易解决,因为我们可以重写我们的 MMD 度量,利用基于数据集的 P 和 Q 的估计:

使用 P 和 Q 的估计的 MMD
这里,我们的x是从数据集中抽样得到的P的样本,而y是从Q中抽取的样本。
前两个问题可以通过一些额外的数学手段来解决。无需过多细节,事实证明,如果F是一个叫做再生核希尔伯特空间(RKHS)的东西,我们就能预先知道哪个函数将给我们提供 MMD。即,它是下面这个函数,称为见证函数:

我们在 RKHS 中的最优 f(x)
其中k是与 RKHS 相关的核函数(内积)¹。直观地说,这个函数在点x处“见证”了P和Q之间的差异。
因此,我们只需要选择一个足够具表现力的 RKHS/核函数——通常使用 RBF 核,其核函数为:

RBF 核,其中 sigma 是一个超参数
这通常能得到相当直观的结果。例如,这里是使用 RBF 核估计的见证函数的图(以与之前相同的方式进行估计——即用求和替代期望)在从Uniform(-0.5, 0.5)和Uniform(-1, 1)两个数据集抽样时的图示:

在不同点上,见证函数对于两个均匀分布的值
生成上述图形的代码如下:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
def rbf(v1, v2, sigma=0.5):
return np.exp(-(v2 - v1) ** 2/(2 * sigma**0.5))
def comp_wit_fn(x, d1, d2):
return 1/len(d1) * sum([rbf(x, dp) for dp in d1]) - 1/len(d2) * sum([rbf(x, dp) for dp in d2])
low1, high1 = -0.5, 0.5 # Range for the first uniform distribution
low2, high2 = -1, 1 # Range for the second uniform distribution
# Generate data for the uniform distributions
data1 = np.random.uniform(low1, high1, 10000)
data2 = np.random.uniform(low2, high2, 10000)
# Generate a range of x values for which to compute comp_wit_fn
x_values = np.linspace(min(low1 * 2, low2 * 2), max(high1 * 2, high2 * 2), 100)
comp_wit_values = [comp_wit_fn(x, data1, data2) for x in x_values]
sns.kdeplot(data1, label=f'Uniform({low1}, {high1})', color='blue', fill=True)
sns.kdeplot(data2, label=f'Uniform({low2}, {high2})', color='red', fill=True)
plt.plot(x_values, comp_wit_values, label='Witness Function', color='green')
plt.xlabel('Value')
plt.ylabel('Density / Wit Fn')
plt.legend()
plt.show()
最后的 MMD-Critic 方法
MMD-Critic 背后的思路现在相当简单——如果我们想找到k个原型,我们需要找到最能匹配原始数据集分布的原型集,通过它们的平方 MMD。换句话说,我们希望找到一个子集P,它的基数是k,并且最小化MMD²(F, X, P)。无需过多细节,平方 MMD 由以下公式给出:

平方 MMD 度量,假设 X ~ P,Y ~ Q,k 是我们 RKHS F 的核函数
在找到这些原型之后,我们接着选择那些假设原型分布与数据集分布在最不同的点作为批评点。正如我们之前所见,两个分布在某一点的差异可以通过我们的见证函数来衡量,所以我们只需要在X和P的上下文中找到最大化其绝对值的点。换句话说,我们将批评“得分”定义为:

一个批评 c 的“得分”
或者,以更易用的近似形式,

对于批评 c 的近似 S(c)
然后,为了找到我们想要的批评数量,假设是m个,我们只需要找到大小为m的集合C,使得它能够最大化

为了促进选择更多样化的批评,论文还建议添加一个正则化项,鼓励选中的批评尽可能分开。论文中建议的正则化项是对数行列式正则化项,尽管这不是必须的。我在这里不会详细讲解,因为它不是关键内容,但论文建议参考[6]²。
因此,我们可以实现一个极其天真的MMD-Critic,没有批评正则化,如下所示(不要使用这个):
import math
import itertools
def euc_distance(p1, p2):
return math.sqrt(sum((x - y) ** 2 for x, y in zip(p1, p2)))
def rbf(v1, v2, sigma=0.5):
return math.exp(-euc_distance(v1, v2) ** 2/(2 * sigma**0.5))
def mmd_sq(X, Y, sigma=0.5):
sm_xx = 0
for x in X:
for x2 in X:
sm_xx += rbf(x, x2, sigma)
sm_xy = 0
for x in X:
for y in Y:
sm_xy += rbf(x, y, sigma)
sm_yy = 0
for y in Y:
for y2 in Y:
sm_yy += rbf(y, y2, sigma)
return 1/(len(X) ** 2) * sm_xx \
- 2/(len(X) * len(Y)) * sm_xy \
+ 1/(len(Y) ** 2) * sm_yy
def select_protos(X, n, sigma=0.5):
min_score, min_sub = math.inf, None
for subset in itertools.combinations(X, n):
new_mmd = mmd_sq(X, subset, sigma)
if new_mmd < min_score:
min_score = new_mmd
min_sub = subset
return min_sub
def criticism_score(criticism, prototypes, X, sigma=0.5):
return abs(1/len(X) * sum([rbf(criticism, x, sigma) for x in X])\
- 1/len(prototypes) * sum([rbf(criticism, p, sigma) for p in prototypes]))
def select_criticisms(X, P, n, sigma=0.5):
candidates = [c for c in X if c not in P]
max_score, crits = -math.inf, []
for subset in itertools.combinations(candidates, n):
new_score = sum([criticism_score(c, P, X, sigma) for c in subset])
if new_score > max_score:
max_score = new_score
crits = subset
return crits
优化 MMD-Critic
上面的实现如此不实用,以至于当我运行时,未能在一个包含 25 个数据点的数据集中,在合理的时间内找到 5 个原型。这是因为我们的 MMD 计算是O(max(|X|, |Y|)²),并且遍历每个长度为 n 的子集的时间复杂度是O(C(|X|, n))(其中 C 是组合函数),这导致了非常可怕的运行时复杂度。
忽略使用更高效的计算方法(例如,使用纯 numpy/numexpr/矩阵计算而不是循环/其他)以及缓存重复计算,我们在理论层面上可以进行一些优化。首先,最明显的性能瓶颈是我们在原型和批评方法中对C(|X|, n)子集的循环。我们可以用一个近似方法来代替,循环n次,每次贪婪地选择最优原型。这样,我们可以将原型选择代码更改为
def select_protos(X, n, sigma=0.5):
protos = []
for _ in range(n):
min_score, min_proto = math.inf, None
for cand in X:
if cand in protos:
continue
new_score = mmd_sq(X, protos + [cand], sigma)
if new_score < min_score:
min_score = new_score
min_proto = cand
protos.append(min_proto)
return protos
批评的情况也是类似的。
还有一个重要的引理,使得这个问题更易于优化。事实证明,通过将原型选择问题转化为最小化问题,并向成本中添加一个正则化项,我们可以通过矩阵运算高效地计算成本函数。我不会在这里详细讨论,但你可以查看原始论文了解详情。
使用MMD-Critic包进行实验
现在我们已经理解了 MMD-Critic 方法,终于可以开始试验它了!你可以通过运行以下命令来安装它
pip install mmd-critic
包中的实现比这里展示的要快得多,所以不必担心。
我们可以使用如下方式运行一个简单的示例,使用斑点数据:
from sklearn.datasets import make_blobs
from mmd_critic import MMDCritic
from mmd_critic.kernels import RBFKernel
n_samples = 50 # Total number of samples
centers = 4 # Number of clusters
cluster_std = 1 # Standard deviation of the clusters
X, _ = make_blobs(n_samples=n_samples, centers=centers, cluster_std=cluster_std, n_features=2, random_state=42)
X = X.tolist()
# MMD critic with the kernel used for the prototypes being an RBF with sigma=1,
# for the criticisms one with sigma=0.025
critic = MMDCritic(X, RBFKernel(1), RBFKernel(0.025))
protos, _ = critic.select_prototypes(centers)
criticisms, _ = critic.select_criticisms(10, protos)
然后绘制点和批评结果得到

绘制找到的原型(绿色)和批评(红色)
你会注意到我提供了使用单独内核进行原型和批评选择的选项。这是因为我发现,批评的结果尤其对 sigma 超参数极其敏感。这是 MMD Critic 方法以及核方法的一项不幸限制。总体而言,我发现使用较大的 sigma 值选择原型,较小的 sigma 值选择批评,效果较好。
我们当然也可以使用更复杂的数据集。例如,这里是该方法在 MNIST 上的应用³:
from sklearn.datasets import fetch_openml
import numpy as np
from mmd_critic import MMDCritic
from mmd_critic.kernels import RBFKernel
# Load MNIST data
mnist = fetch_openml('mnist_784', version=1)
images = (mnist['data'].astype(np.float32)).to_numpy() / 255.0
labels = mnist['target'].astype(np.int64)
critic = MMDCritic(images[:15000], RBFKernel(2.5), RBFKernel(0.025))
protos, _ = critic.select_prototypes(40)
criticisms, _ = critic.select_criticisms(40, protos)
这为我们提供了以下原型

MMD Critic 方法为 MNIST 找到的原型。MNIST 在 GPL-3.0 许可协议下可以免费用于商业用途。
以及批评意见

由 MMD Critic 方法发现的批评意见
相当不错吧?
结论
这就是 MMD-Critic 方法的全部内容。核心非常简单,使用起来不错,除了需要调整 Sigma 超参数。我希望新发布的 Python 包能带来更多的应用。
如有任何疑问,请联系 mchak@calpoly.edu。所有图片均由作者提供,除非另有说明。
脚注
[1] 如果你曾经学习过 SVM 和核技巧,你可能会熟悉 RKHS 和核——在这些方法中使用的核实际上只是某些RKHS 中的内积。最常见的是 RBF 核,其相关的 RKHS 函数是一个无限维的平滑函数集。
[2] 我没有详细阅读此来源,只是简单浏览了一下。它似乎大部分无关紧要,而且对数行列式正则化项的实现相对简单。不过,如果你想阅读它,尽管去看。
[3] 出于法律原因,你可以在这里找到包含 MNIST 数据集的代码库。该数据集在 GPL-3.0 许可协议下可以免费用于商业用途。
参考文献
[1] onlinelibrary.wiley.com/doi/book/10.1002/9780470316801
[2]proceedings.neurips.cc/paper_files/paper/2016/file/5680522b8e2bb01943234bce7bf84534-Paper.pdf
[3] f0nzie.github.io/interpretable_ml-rsuite/proto.html#examples-5
[4] jmlr.csail.mit.edu/papers/volume13/gretton12a/gretton12a.pdf
[5] www.stat.cmu.edu/~ryantibs/journalclub/mmd.pdf
[6] jmlr.org/papers/volume9/krause08a/krause08a.pdf
网络上最先进的数据可视化和分析库
精心挑选,注重性能、灵活性和功能的丰富性。
LucianoSphere (Luciano Abriata, PhD)
·发表于Towards Data Science ·阅读时间:25 分钟·2024 年 2 月 13 日
--

通过这里介绍的库生成的示例可视化图。所有这些图像都是作者从截图中合成的。
在这篇新文章中,我展示了我对最先进、最强大的网络数据可视化和分析库的探索成果,这些库是我经过对性能、灵活性和功能丰富性的仔细分析后做出的选择。
我选择的一些库并不流行,但它们提供了令人惊讶的功能和特性,完全超越了其他一些更为知名的替代方案。我为展示而挑选的这些库功能极其丰富,以至于我几乎没有空间去专注于一些突出的亮点,强调它们的优势,和更为知名的替代方案进行比较,同时邀请你亲自去测试它们——它们都是免费的,有些甚至为学术和教育提供免费的版本!
阅读完这篇文章后,请继续关注未来的文章,我将在其中详细讨论我所选库中的一些案例——即数据可视化库中的“顶尖之选”
数据可视化无处不在,是数据分析的核心,从早期的探索性浏览到最先进的处理过程,包括……
我在职业生涯中见证的最昂贵的数据科学错误
为什么机器学习的真正成功超越了优化单一指标
·发表于Towards Data Science ·6 分钟阅读·2024 年 11 月 28 日
--

图片由Clker-Free-Vector-Images提供,来源于Pixabay
“新的信用模型没有按预期表现,”他说。
来自另一个团队的同事从“战情室”出来,神情沮丧。
这是我第一次在商业环境中见证数据科学的“错误”,所以我并没有完全理解它的规模。
正如我在接下来的六个月中了解到的那样,从财务和运营的角度来看,这是一个非常昂贵的错误,因为它对下游产生的影响以及在公司范围内的连锁反应。
这个问题影响了每个团队,我们在接下来的几个月里都在不懈地工作:
-
分析师致力于跟踪投资组合健康的指标,
-
数据科学家专注于诊断问题并修复它,
-
工程师与数据科学家合作,部署新的模型,
-
产品营销人员致力于活动和信息传递,
-
运营团队致力于客户外展和还款教育。
问题的第一个迹象是违约率远高于预期,但这只是……
在本地设置 ChatGPT 的最简单方法
在消费者硬件上运行 LLM 的秘诀
·发表于Towards Data Science ·阅读时长 10 分钟·2024 年 1 月 17 日
--

图 1:可爱的小机器人们正在未来派的肥皂工厂工作(unsplash: Gerard Siderius)。
作为一名数据科学家,我已经花费了大量时间深入研究像BERT、GPT{2,3,4}和ChatGPT这样的超大语言模型(LLMs)的复杂性。这些先进的模型在规模上有了显著扩展,使得在标准消费者设备上运行最新的高性能模型变得越来越具有挑战性。遗憾的是,在我家,我仍然没有 8x A100 机器可供使用。
我(目前)家里没有 8x A100 机器
在过去几年里,一种新的技术被用来使模型变得更小更快:量化。这种方法巧妙地将曾经庞大的 LLM 缩小到一个更易于消化的大小,适合消费者级硬件。它就像是让这些人工智能巨头进行数字化减肥,使它们能够舒适地适应我们家用计算机更为谦逊的空间。与此同时,开源社区,像🤗 HuggingFace 和🦄 Mistral 这样的先行者,在普及这些模型方面发挥了重要作用。他们基本上将这个原本排外的 AI 俱乐部变成了一个‘人人欢迎’的科技盛会——无需秘密握手!
尽管经过指令训练的模型权重是谜题中的重要一环,但它们并不是全部。可以将这些权重看作是操作的“大脑”——…
数据科学家最被低估的技能
为什么写作对技术角色至关重要,以及如何提高写作能力
·发表于Towards Data Science ·阅读时长 8 分钟·2024 年 7 月 3 日
--

图片来自作者(Midjourney)
“为什么我的经理总是挑剔我的写作?改变措辞从 X 到 Y 有什么区别?”
当你看到经理在你的文档上做出大量修改时,你可能曾经这样想过;我知道我曾经有过这种想法。事实上,我曾经认为写作是数据科学家工作中最微不足道的一部分;因为分析和数字应该自己说话,不是吗?错!
在过去几年里,我意识到写作是数据科学家必备的技能,而写作能力是区分高影响力数据科学家与同行的关键因素之一。
在这篇文章中,我将首先说服你,写作至少与技术技能一样重要,然后给你一些具体的建议,帮助你提高写作能力。
为什么写作对数据科学家如此重要?
1. 它在企业界无处不在 — 我在之前的文章中强调了沟通的重要性,不管你愿不愿意,企业界的大多数沟通都是以书面形式发生的。从项目范围文档到每周报告……
成功进入科技行业的最有用的高级 SQL 技巧
语法、使用案例以及掌握高级 SQL 的专家技巧
·发表于Towards Data Science ·阅读时间 8 分钟·2024 年 7 月 31 日
--

图片由 Choong Deng Xiang 提供,来自 Unsplash
作为一名在科技行业工作多年的经验丰富的数据专业人士,我处理过大量的大型数据集。SQL 是进行数据操作、数据查询和分析时最常用的工具。虽然掌握基础和中级 SQL 相对容易,但要在各种场景中熟练掌握这项工具并运用自如,有时却具有挑战性。如果你希望在顶级科技公司工作,有一些高级 SQL 技巧你必须熟悉。今天,我将分享一些你在工作中无疑会用到的最有用的高级 SQL 技巧。为了帮助你更好地理解它们,我将创建一些使用案例,并利用模拟数据解释在什么场景下使用它们以及如何使用它们。每个使用案例都会提供编程代码。
窗口函数
窗口函数在查询中对一组指定的行(称为“窗口”)进行计算,并返回与当前行相关的单一值。
我们将使用星级百货商店促销活动的销售数据来解释窗口函数。该表包含三列:Sale_Person_ID,它是销售人员的唯一 ID……
多臂赌博机问题——面向初学者的指南
通过一个例子来理解利用-探索权衡
·发表于Towards Data Science ·阅读时间 6 分钟·2024 年 12 月 23 日
--
多臂赌博机(MAB)是决策中的经典问题,其中一个代理人必须在多个选择(称为“臂”)之间做出决策,并通过一系列试验最大化总奖励。这个问题得名于一个比喻,描述了一个赌徒面对一排老丨虎丨机(单臂老丨虎丨机),每个老丨虎丨机的支付概率不同且未知。目标是找到最佳策略来拉动老丨虎丨机(选择行动),并随着时间的推移最大化赌徒的总体奖励。MAB 问题是利用-探索权衡的一个华丽称谓。
多臂赌博机问题是一个基础性问题,广泛应用于多个工业领域。让我们来探讨它,并审视一些有趣的解决策略。

图片由 Grok 生成
示例
假设你刚到一个新城市。你是一个间谍,计划待 120 天以完成下一个任务。镇上有三家餐馆:意大利餐厅、中华餐厅和墨西哥餐厅。你希望在逗留期间最大化自己的用餐满意度。然而,你不知道哪家餐厅最适合你。以下是这三家餐厅的情况:
- 意大利餐厅:平均满意度得分为……
PyTorch 自动混合精度库背后的谜团
如何通过三行代码实现 2 倍速度提升的模型训练
·发布于Towards Data Science ·阅读时长 8 分钟·2024 年 9 月 17 日
--
你是否曾希望你的深度学习模型能够更快运行?
GPU 昂贵,数据集庞大,训练过程似乎没有尽头;你有一百万个实验要运行,还有一个截止日期要赶——所有这些都是你期望某种形式的训练加速的充分理由。
但该选择哪个呢?
已经有许多关于模型训练性能调优的优秀参考资料,来自于PyTorch、HuggingFace和Nvidia,包括异步数据加载、缓冲区检查点、分布式数据并行和自动混合精度。
在这篇文章中,我将介绍自动混合精度技术。我将从简要介绍 Nvidia 的张量核心设计开始,然后介绍 2018 年在 ICLR 上发表的开创性论文《混合精度训练》,最后通过一个简单的示例展示如何在 FashionMNIST 上训练 ResNet50,并通过加载 2 倍批量大小实现 2 倍的训练速度提升,同时只需增加三行代码。

图片来源:pxhere.com/en/photo/872846
硬件基础 — Nvidia 张量核心
首先,让我们回顾一下 GPU 设计的一些基本知识。Nvidia GPU 中最受欢迎的商业产品之一是 Volta 系列,例如 V100 GPU,基于 GV100 GPU 设计。因此,我们将基于 GV100 架构展开讨论。
对于 GV100,流多处理器(SM)是计算的核心设计。每个 GPU 包含 6 个 GPU 处理集群(GPC)和 84 个 SM(V100 为 80 个 SM)。整体设计如下图所示。

Volta GV100 GPU 设计。每个 GPU 包含 6 个 GPC(图形处理集群),每个 GPC 包含 14 个 SM(流多处理器)。图像来源:arxiv.org/pdf/1803.04014
对于每个 SM,它包含两种类型的核心:CUDA 核心和 Tensor 核心。CUDA 核心是 Nvidia 在 2006 年推出的原始设计,是 CUDA 平台的核心部分。CUDA 核心可以分为三种类型:FP64 核心/单元、FP32 核心/单元和 Int32 核心/单元。每个 GV100 SM 包含 32 个 FP64 核心,64 个 FP32 核心和 64 个 Int32 核心。Tensor 核心是在 Volta/Turing(2017)系列 GPU 中引入的,与之前的 Pascal(2016)系列 GPU 区别开来。每个 GV100 SM 包含 8 个 Tensor 核心。V100 GPU 的详细规格请参见这里。下面是 SM 设计的详细图示。

流处理器(SM)的子波段。每个 SM 包含四个子波段。图像来源:arxiv.org/pdf/1903.03640
为什么是 Tensor 核心?Nvidia 的 Tensor 核心专门用于执行通用矩阵乘法(GEMM)和半精度矩阵乘法与累加(HMMA)操作。 简而言之,GEMM 执行 A*B + C 的矩阵运算,而 HMMA 将操作转换为半精度格式。详细讨论请参见这里。由于深度学习在训练过程中大量涉及 MMA,Tensor 核心在当今的模型训练和加速中至关重要。

GEMM 操作示例。对于 HMMA,A 和 B 通常转换为 FP16,而 C 和 D 可以是 FP16 或 FP32。图像来源:arxiv.org/pdf/1811.08309
当然,在切换到混合精度训练时,始终检查您使用的 GPU 的规格。只有最新的 GPU 系列支持 Tensor 核心,并且混合精度训练仅能在这些机器上使用。
数据格式基础 — 单精度(FP32)与半精度(FP16)
现在,让我们更仔细地了解 FP32 和 FP16 格式。FP32 和 FP16 是 IEEE 格式,分别使用 32 位和 16 位二进制存储表示浮动数值。这两种格式都包含三个部分:a)符号位,b)指数位,c)尾数位。FP32 和 FP16 的区别在于分配给指数和尾数的位数,这导致了不同的值范围和精度。

FP16(IEEE 标准)、BF16(Google Brain 标准)、FP32(IEEE 标准)和 TF32(Nvidia 标准)之间的差异。图片来源:en.wikipedia.org/wiki/Bfloat16_floating-point_format
如何将 FP16 和 FP32 转换为实际值?根据 IEEE-754 标准,FP32 的十进制值 = (-1)^(符号位) × 2^(十进制指数 —127) ×(隐含的前导 1 + 十进制尾数),其中 127 是偏置指数值。对于 FP16,公式变为(-1)^(符号位) × 2^(十进制指数 — 15) ×(隐含的前导 1 + 十进制尾数),其中 15 是相应的偏置指数值。更多关于偏置指数值的详细信息,请参见此处。
在这个意义上,FP32 的数值范围大约是[-2¹²⁷, 2¹²⁷] ~[-1.71e38, 1.71e38],而 FP16 的数值范围大约是[-2¹⁵, 2¹⁵]=[-32768, 32768]。注意,FP32 的十进制指数范围在 0 到 255 之间,我们排除了最大值 0xFF,因为它表示 NAN。因此,最大的十进制指数是 254–127 = 127。FP16 也适用类似的规则。
对于精度,注意到指数和尾数都对精度限制有贡献(这也叫做非标准化,参见详细讨论),因此 FP32 可以表示精度达到 2(-23)*2(-126)=2^(-149),而 FP16 的精度可以达到 2¹⁰*2(-14)=2(-24)。
FP32 和 FP16 表示的差异带来了混合精度训练的关键问题,因为深度学习模型的不同层/操作对数值范围和精度的敏感度不同,需要分别处理。
混合精度训练
现在我们已经了解了 MMA 的硬件基础、Tensor 核心的概念以及 FP32 与 FP16 之间的关键区别,接下来可以进一步讨论混合精度训练的细节。
混合精度训练的概念最早出现在 2018 年 ICLR 论文“混合精度训练”中,该论文在训练过程中将深度学习模型转换为半精度浮点数,而不会损失模型精度或修改超参数。如前所述,由于 FP32 和 FP16 之间的主要区别是数值范围和精度,论文详细讨论了FP16 如何导致梯度消失以及如何通过损失缩放解决该问题。此外,论文还提出了使用 FP32 主权重副本和在特定操作(如归约和向量点积累加)中使用 FP32 等技巧。
损失缩放。论文中给出了使用 FP32 精度训练 Multibox SSD 检测器网络的示例,如下所示。如果不进行任何缩放,FP16 梯度的指数范围将≥2^(-24),低于此值的部分将变为零,这相较于 FP32 来说是不够的。然而,通过实验发现,只需将梯度缩放 2³=8 倍,即可将半精度训练的准确度恢复至与 FP32 相匹配。从这个角度看,作者认为[2^(-27), 2^(-24)]之间的少数梯度仍然在训练过程中很重要,而低于 2^(-27)的值则不重要。

在 Multibox SSD 训练示例中,使用 FP32 精度时梯度值的范围。请注意,值范围在[2^(-27), 2^(-24)]之间超出了 FP16 的非正规化范围,仅占总梯度的几百分比,但在整体训练中仍然很重要。图片来源:arxiv.org/pdf/1710.03740
解决这一缩放差异的方法是应用损失缩放。根据链式法则,缩放损失将确保所有梯度都按相同的比例进行缩放。在最终的权重更新之前,需要将梯度恢复为原始尺度。
自动混合精度训练
Nvidia 最早开发了自动混合精度训练,作为一个名为 APEX 的 PyTorch 扩展,后来被 PyTorch、TensorFlow、MXNet 等主流框架广泛采用。有关详细信息,请参阅 Nvidia 的文档。为简便起见,我们将仅介绍 PyTorch 的自动混合精度库:pytorch.org/docs/stable/amp.html。
amp 库可以自动处理大多数混合精度训练技术,例如 FP32 主权重复制。用户主要接触到操作自动转换和梯度/损失缩放。
操作自动转换。尽管我们提到过张量核心可以大幅提升 GEMM 操作的性能,但某些操作并不适合使用半精度表示。
amp 库提供了一个CUDA 操作列表,列出了可用于半精度的操作。大多数矩阵乘法、卷积和线性激活都可以通过 amp.autocast 完全覆盖,但是对于归约/求和、softmax 和损失计算,这些计算仍然在 FP32 精度下进行,因为它们对数据范围和精度更为敏感。
梯度/损失缩放。amp 库提供了自动梯度缩放技术,因此用户在训练过程中无需手动调整缩放。有关缩放因子的更详细算法可以在这里找到。
一旦梯度被缩放,必须在梯度裁剪和正则化之前进行缩放回原值。更多细节可以在这里找到。
一个 FashionMNIST 训练示例
torch.amp 库相对容易使用,只需要三行代码就能将训练速度提升 2 倍。
我们从一个非常简单的任务开始,在 FashionMNIST 数据集上训练 ResNet50 模型(MIT 许可证),使用 FP32;我们可以看到,十个 Epoch 的训练时间为 333 秒:

ResNet50 在 FashionMNIST 上的训练。图像由作者提供。

小于 2**(-24) 的梯度与总梯度的比值。我们可以看到,FP16 会将几乎四分之一的总梯度归零。图像由作者提供。

评估结果。图像由作者提供。
现在我们使用了 amp 库。amp 库只需要额外三行代码来进行混合精度训练。我们可以看到训练在 141 秒内完成,比 FP32 训练速度提升了 2.36 倍,同时精度、召回率和 F1-score 保持不变。
scaler = torch.cuda.amp.GradScaler()
# start your training code
# ...
with torch.autocast(device_type="cuda"):
# training code
# wrapping loss and optimizer
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()

使用 amp 的训练代码。图像由作者提供。

训练中的缩放因子。缩放因子仅在第一步发生变化,并保持不变。图像由作者提供。

最终结果与 FP32 训练结果可比。图像由作者提供。
上面的代码的 GitHub 链接在这里。
总结
混合精度训练是加速深度学习模型训练的一个宝贵技术。它不仅加速了浮点运算,还节省了 GPU 内存,因为训练批次可以转换为 FP16,从而节省了一半的 GPU 内存。通过 PyTorch 的 amp 库,额外的代码可以减少为三行,因为权重复制、损失缩放、操作类型转换等都由库内部处理。
然而,混合精度训练并没有真正解决 GPU 内存问题,特别是在模型权重大小远大于数据批量时。一方面,只有模型的某些层被转换为 FP16,其余部分仍以 FP32 计算;其次,权重更新仍然需要 FP32 副本,这仍然占用大量 GPU 内存;第三,像 Adam 这样的优化器的参数在训练过程中会占用大量 GPU 内存,而混合精度训练保持优化器参数不变。从这个角度来看,更先进的技术,如 DeepSpeed 的 ZERO 算法,是必要的。
参考文献
-
Micikevicius 等人,《混合精度训练》。ICLR 2018
-
PyTorch AMP 库:
pytorch.org/tutorials/recipes/recipes/amp_recipe.html -
Nvidia CUDA 浮点运算:
docs.nvidia.com/cuda/floating-point/index.html
打破 ChatGPT 的名字:大卫·梅耶是谁?
AI、隐私、人类偏见、提示、内容的未来,以及如何破解聊天机器人
·发布于Towards Data Science ·阅读时长:16 分钟·2024 年 12 月 3 日
--
当一个朋友建议我尝试将名字“David Mayer”输入到 ChatGPT 时,我没有多想。直到我自己尝试了。
“我无法生成响应。”
奇怪…
然后我试了David。没问题。Mayer。没问题。David Meyer(加了个 e)。没问题。David Mayer。砰!
所以我快速截图,给你们展示当我尝试以不同方式输入这个名字时发生了什么。(无论开启或关闭音频观看都可以。)
现在我简直是有些痒了,必须知道答案!
大卫·梅耶是谁??(他做了什么?)
“我无法生成响应。”
“我无法生成响应。”
“我无法生成响应。”*
我做了一些挖掘并收集了一些线索,但由于我直觉感觉到这个 ChatGPT bug(嗯,不完全是个 bug,正如我们将看到的那样)有很多层次,可能会一直给我们带来惊喜,于是我在LinkedIn 上发了一个简短的帖子,让几十万的你们也参与进来…
大海捞针测试

图片由作者使用 Dall-E 3 创建
评估 RAG 系统的表现
·发布于 Towards Data Science ·阅读时间 9 分钟 ·2024 年 2 月 15 日
--
感谢 Greg Kamradt 和 Evan Jolley 对本文的贡献
检索增强生成(RAG)是当前许多大型语言模型(LLM)应用的基础,从公司生成标题到独立开发者为小型企业解决问题。
RAG 评估,因此,已成为这些系统开发和部署中的关键部分。应对这一挑战的一种新创新方法是“大海捞针”测试,最早由 Greg Kamradt 在 这篇 X 帖子 中提出,并在他的 YouTube 频道 这里 进行了详细讨论。该测试旨在评估 RAG 系统在不同上下文规模下的表现。其工作原理是在更大、更复杂的文本(“干草堆”)中嵌入特定的目标信息(“针”)。目标是评估大型语言模型(LLM)在海量数据中识别和利用这段特定信息的能力。
在 RAG 系统中,上下文窗口通常充斥着大量信息。来自向量数据库的大块上下文与语言模型的指令、模板以及提示中可能存在的任何其他内容混杂在一起。“干草堆中的针”评估测试了 LLM 在这片混乱中找出具体信息的能力。你的 RAG 系统可能在检索最相关的上下文方面表现出色,但如果其中的细节被忽略了,那又有什么用呢?
我们多次在几种主要的语言模型上进行了此测试。让我们更仔细地看看这个过程和总体结果。
主要结论
-
并非所有 LLM 都相同。不同的模型在训练时有不同的目标和要求。例如,Anthropic 的 Claude 因稍显冗长而闻名,这通常源于它的目标是避免做出没有依据的声明。
-
提示中的微小差异可能会导致模型之间截然不同的结果,这是因为这个事实。有些 LLM 需要更量身定制的提示才能在特定任务上表现良好。
-
在构建 LLM 基础上时——尤其是当这些模型与私有数据连接时——必须在开发和部署过程中评估检索和模型的性能。看似微不足道的差异可能会导致性能上的巨大差异。
理解“干草堆中的针”测试
“干草堆中的针”测试首次用于评估两款流行的语言模型(LLM),OpenAI 的 ChatGPT-4 和 Anthropic 的 Claude 2.1。一个不合时宜的语句:“在旧金山最好的事情就是吃个三明治,坐在阳光明媚的日子里去多洛雷斯公园,”被放置在从Paul Graham 的文章中提取的不同长度片段中的不同深度位置,类似如下:

图 1:大约 120 个 token 和 50% 深度 | 图片由Greg Kamradt提供,发表于X,经作者许可使用
然后,模型被提示回答在旧金山做什么是最好的,仅使用提供的上下文。这个测试在不同的深度(从 0%(文档顶部)到 100%(文档底部))和不同的上下文长度(从 1K token 到每个模型的 token 限制(GPT-4 为 128k,Claude 2.1 为 200k))之间反复进行。下面的图表记录了这两款模型的表现:

图 2:ChatGPT-4 的表现 | 图片由Greg Kamradt提供,发表于X,经作者许可使用
如你所见,ChatGPT 的表现开始在<64k token 时下降,并在 100k 及以上时急剧下降。有趣的是,如果“针”被放置在上下文的前面,模型往往会忽视或“忘记”它——而如果将其放在末尾或作为第一句话,模型的表现则保持稳定。

图 3:Claude 2.1 的表现 | | 图片由Greg Kamradt提供,发布在X,在此感谢作者授权使用
对于 Claude,初始测试并不顺利,最终的检索准确率为 27%。随着上下文长度增加,表现普遍下降,且当针被隐藏在文档底部时,表现有所提升。如果针是上下文中的第一句话,检索准确率则达到 100%。
Anthropic 的回应
针对这些发现,Anthropic 发布了一篇文章,详细说明了他们在进行测试时所做的一些关键调整。
首先,他们将针的内容更改为与干草堆的主题更为贴近。Claude 2.1 被训练成“如果文档中没有足够的信息支持该回答,就不[回答]问题”。因此,Claude 可能确实正确地识别出在 Dolores 公园吃三明治是旧金山最好的活动。然而,若它与关于做伟大工作的文章一起出现,这个小细节可能显得没有充分证据。这可能导致模型给出冗长的回答,解释 Claude 无法确认在旧金山吃三明治是最好的事情,或者完全省略这一细节。重新进行实验时,Anthropic 的研究人员发现,将针改为原文中提到的小细节后,结果显著提升。
其次,对查询模型使用的提示模板做了一些小编辑。在模板的末尾增加了一行——这里是上下文中最相关的句子——指示模型仅返回上下文中提供的最相关句子。与之前的调整类似,这一改变使我们能够绕过模型避免不充分声明的倾向,直接指示其返回句子,而非作出断言。
PROMPT = """
HUMAN: <context>
{context}
</context>
What is the most fun thing to do in San Francisco based on the context? Don't give information outside the document or repeat our findings
Assistant: here is the most relevant sentence in the context:"""
这些变化导致 Claude 的整体检索准确率显著提高:从 27%跃升至 98%! 我们对这一初步研究感到非常有趣,于是决定通过“干草堆中的针”测试进行一系列实验。
进一步实验
在进行一系列新测试时,我们对原始实验进行了若干修改。我们使用的针是一个随机数,每次迭代都会变化,从而消除了缓存的可能性。此外,我们使用了我们的开源 Phoenix 评估 库(完全公开:我是构建 Phoenix 团队的负责人)来缩短测试时间,并使用 rails 直接在输出中搜索随机数,从而避免了冗长的内容,防止降低检索得分。最后,我们考虑了系统未能检索结果的负面情况,并将其标记为无法回答。我们为这种负面情况运行了单独的测试,以评估系统在无法检索数据时的识别能力。这些修改使我们能够进行更严格和全面的评估。
更新后的测试在四种不同的大型语言模型配置上运行:ChatGPT-4、Claude 2.1(有和没有 Anthropic 建议的提示修改)以及 Mistral AI 的 Mixtral-8X7B-v0.1 和 7B Instruct。考虑到提示的小细节差异可能导致模型之间结果的巨大差异,我们使用了几个提示模板,力图比较这些模型在最佳表现下的表现。我们为 ChatGPT 和 Mixtral 使用的简单模板如下:
SIMPLE_TEMPLATE = '''
You are a helpful AI bot that answers questions for a user. Keep your responses short and direct.
The following is a set of context and a question that will relate to the context.
#CONTEXT
{context}
#ENDCONTEXT
#QUESTION
{question} Don’t give information outside the document or repeat your findings. If the information is not available in the context respond UNANSWERABLE
对于 Claude,我们测试了前面讨论的两种模板。
ANTHROPIC_TEMPLATE_ORIGINAL = ''' Human: You are a close-reading bot with a great memory who answers questions for users. I’m going to give you the text of some essays. Amidst the essays (“the haystack”) I’ve inserted a sentence (“the needle”) that contains an answer to the user’s question.
Here's the question:
<question>{question}</question>
Here’s the text of the essays. The answer appears in it somewhere.
<haystack>
{context}
</haystack>
Now that you’ve read the context, please answer the user's question, repeated one more time for reference:
<question>{question}</question>
To do so, first find the sentence from the haystack that contains the answer (there is such a sentence, I promise!) and put it inside <most_relevant_sentence> XML tags. Then, put your answer in <answer> tags. Base your answer strictly on the context, without reference to outside information. Thank you.
If you can’t find the answer return the single word UNANSWERABLE
Assistant: '''
ANTHROPIC_TEMPLATE_REV2 = ''' Human: You are a close-reading bot with a great memory who answers questions for users. I'm going to give you the text of some essays. Amidst the essays ("the haystack") I've inserted a sentence ("the needle") that contains an answer to the user's question.
Here's the question:
<question>{question}</question>
Here's the text of the essays. The answer appears in it somewhere.
<haystack>
{context}
</haystack>
Now that you've read the context, please answer the user's question, repeated one more time for reference:
<question>{question}</question>
To do so, first find the sentence from the haystack that contains the answer (there is such a sentence, I promise!) and put it inside <most_relevant_sentence> XML tags. Then, put your answer in <answer> tags. Base your answer strictly on the context, without reference to outside information. Thank you.
If you can't find the answer return the single word UNANSWERABLE
Assistant: Here is the most relevant sentence in the context:'''
完成这些测试所运行的所有代码可以在 此 GitHub 仓库中找到。
结果

图 7:GPT-4 在初始研究(Run #1)和我们的测试(Run #2)之间的对比 | 图像由作者提供

图 8:Claude 2.1(无提示指导)在 Run #1 和 Run #2 之间的对比 | 图像由作者提供
我们对于 ChatGPT 和 Claude(无提示指导)的结果与 Kamradt 先生的研究结果相差不大,生成的图表也相对相似:右上方(长上下文,针接近上下文开头)是 LLM 信息检索的痛点所在。

图 9:Claude 2.1 结果与是否使用提示指导的对比
尽管我们未能复制 Anthropic 所报告的 Claude 2.1 在提示指导下 98% 检索准确率的结果,但我们确实看到,在更新提示后,总漏检数大幅下降(从 165 降至 74)。这个变化是通过简单地在现有提示的末尾添加 10 个字的指令实现的,突显了提示中的小差异如何对 LLM 的结果产生巨大影响。

图 10:Mixtral 结果 | 图像由作者提供
最后但绝对不容忽视的是,尽管 Mixtral 是迄今为止测试中最小的模型,但它在这项任务中的表现令人惊讶。混合专家(MOE)模型远远优于 7B-Instruct,我们发现 MOE 在检索评估中表现得更好。
结论
“干草堆中的针”测试是一种巧妙的方法,用于量化大型语言模型(LLM)解析上下文以寻找所需信息的能力。我们的研究得出了几条主要结论。首先,ChatGPT-4 在这一领域是当前行业的领导者,这一点得到了我们和其他人进行的多项评估的验证。其次,最初 Claude 2.1 在这一测试中似乎表现不佳,但通过调整提示结构,模型表现出了显著的改进。Claude 比其他一些模型更加冗长,因此更加注意如何引导它会在结果上取得更好的效果。最后,Mixtral MOE 大大超出了我们的预期,我们对 Mixtral 模型不断超越预期的表现感到兴奋。
LLMs 的新前沿:挑战、解决方案与工具
·发表于 Towards Data Science ·通过 Newsletter 发送 ·3 分钟阅读·2024 年 1 月 25 日
--
大型语言模型已经存在多年,但直到 2023 年,它们在机器学习社区内外的存在才变得真正无处不在。以前晦涩的概念,如微调和 RAG,已经成为主流,大小公司要么在构建,要么在将 LLM 驱动的工具整合到它们的工作流程中。
展望 2024 年,似乎几乎可以确定这些模型的影响力将进一步扩大,并且随着令人兴奋的创新,它们也将为从业者带来新的挑战。本周我们强调的一些突出文章指向了与 LLMs 相关的一些新兴方面;无论你是对这个话题相对陌生,还是已经广泛尝试过这些模型,你都能在这里找到一些能激发你好奇心的内容。
-
民主化 LLMs:4 位量化以优化 LLM 推理
量化是使大规模模型的强大能力能够为更广泛的机器学习专业人士群体所用的主要方法之一,这些专业人士中的许多人可能无法获得无限的内存和计算能力。Wenqi Glantz带我们了解了量化 Mistral-7B-Instruct-v0.2 模型的过程,并解释了这种方法在效率和性能之间固有的权衡。
-
导航 LLM 代理的世界:初学者指南我们如何让 LLM“达到能够独立解决更复杂问题的水平”?Dominik Polzer的易懂入门指南展示了如何构建能够利用不同工具和功能、以最少人类干预自动化复杂工作流的 LLM 代理。

图片由Beth Macdonald拍摄,来自Unsplash
-
利用 KeyBERT、HDBSCAN 和 Zephyr-7B-Beta 构建知识图谱LLM 本身非常强大,当然,但当与其他方法和工具结合时,其潜力更为显著。Silvia Onofrei最近的指南通过 Zephyr-7B-Beta 模型的帮助,展示了如何构建一个知识图谱,这正是一个例证;它演示了如何将 LLM 与传统的 NLP 方法结合,产生令人印象深刻的结果。
-
将大型语言模型与 mergekit 合并尽管听起来不太可能,但有时单一的 LLM 可能无法满足项目的特定需求。正如Maxime Labonne在他最新的教程中所展示的,模型合并是一种“相对较新且实验性的低成本创建新模型的方法”,它可能正是你需要从多个模型中混合搭配元素时的解决方案。
-
在招聘过程中使用 LLM 会让你作为候选人变成欺诈者吗?LLM 提出的问题不仅仅是技术性的——它们还涉及到伦理和社会问题,这些问题可能变得相当棘手。Christine Egan聚焦于那些在求职过程中利用 LLM 和像 ChatGPT 这样的工具的求职者所面临的风险,并探讨了利用和滥用技术来简化繁琐任务之间有时模糊的界限。
正如往常一样,我们作者在最近几周所涵盖的主题范围和深度令人震撼——这里是一些必读的代表性样本:
-
超越 LLM,Nate Cibik 深入探索了大规模多模态模型(LMMs)和它们如何塑造自主机器人领域。
-
在一个新的项目介绍中,Christabelle Pabalan 结合了 NLP 技术、特征工程和可视化来评估电影对白的语义属性与类型之间的联系。
-
今年时序图学习的发展方向在哪里?Shenyang(Andy) Huang 与合著者 Emanuele Rossi,Michael Galkin,Andrea Cini 和 Ingo Scholtes 提供了该领域发展轨迹的全景概览。
-
因果推断是所有数据专业人士必须掌握的基本概念,但其含义可能会因角色而异。Zijing Zhu, PhD 聚焦于这些差异在学术界和行业中的表现。
-
扩散在近年来的 AI 生成图像领域引起了广泛关注,但正如Christopher Landschoot 所展示的,其潜力同样延伸到了音乐和音频生成的世界。
感谢你支持我们作者的工作!如果你感到受到启发并想加入他们的行列,为什么不写下你的第一篇文章?我们期待阅读它。
直到下一个 Variable,
TDS 团队
Julia 中的 One Billion Row Challenge
如果数据科学家决定接受这个任务,他们能学到什么?
·发布于 Towards Data Science ·阅读时间 8 分钟·2024 年 6 月 5 日
--

图片来源:Indira Tjokorda 在 Unsplash
今年早些时候,Gunnar Morling 启动了 One Billon Row Challenge,自那以后,它获得了很大的关注。虽然最初的挑战是使用 Java 完成的,但令人惊叹的开源社区随后分享了多种编程语言下的精彩解决方案。我注意到,使用 Julia 的人并不多(至少没有公开分享过结果),所以决定通过这篇文章分享我自己的尝试。
我常常想一个问题,这个挑战对数据科学家有什么价值?除了做一个有趣的练习之外,我们能学到更多的东西吗?毕竟,这个挑战的目标是“简单地”解析一个大型虚拟数据文件,计算基本统计量(最小值、最大值和均值),并以特定格式输出数据。这对于大多数数据科学家通常从事的项目来说,可能并不是一个现实的情况。
嗯,问题的一个方面涉及数据的大小与可用 RAM 之间的关系。在本地工作(笔记本电脑或桌面)时,对大多数人来说,难以将所有数据一次性加载到内存中。因此,处理比内存更大的数据集成为一项必要的技能,这在原型化大数据管道或进行大数据分析/可视化任务时可能会派上用场。原始挑战的规则也指出应该避免使用外部库/包。这迫使你思考新颖的解决方案,并为学习语言本身的细节提供了一个迷人的机会。
在接下来的文章中,我将分享两种方法的结果——使用基础的 Julia 以及通过外部包进行的结果。通过这种方式,我们可以比较每种方法的优缺点。所有实验均在配备 AMD Ryzen 9 5900X(12 核心,24 线程)、32 GB RAM 和三星 NVMe SSD 的桌面上进行。Julia 1.10.2 运行在 Linux(Elementary OS 7.1 Horus)上。所有相关代码可以在这里找到。请注意,这里的性能也与硬件有关,因此如果你决定在自己的系统上运行脚本,结果可能会有所不同。
前提条件
推荐使用最近发布的Julia版本,例如 1.10。对于那些想使用笔记本的人,上述仓库还包含一个 Pluto 文件,使用该文件需要安装Pluto.jl。挑战的输入数据文件是每个人独特的,必须使用这个 Python 脚本生成。请记住,该文件大约有 15 GB。
python3 create_measurements.py 1000000000
此外,我们还将使用BenchmarkTools.jl包来运行基准测试。请注意,这不会影响挑战,只是用来收集适当的统计数据,以衡量和量化 Julia 代码的性能。
使用基础的 Julia
输入数据文件measurements.txt的结构如下(这里只显示前五行):
attipūdi;-49.2
Bas Limbé;-43.8
Oas;5.6
Nesebar;35.9
Saint George’s;-6.6
该文件包含十亿行(也称为记录或行)。每一行都有一个站点名称,后跟;分隔符,然后是记录的温度。唯一的站点数量最多可达 10,000。这意味着同一个站点会出现在多行中。因此,我们需要收集文件中所有不同站点的所有温度数据,然后计算所需的统计信息。很简单,对吧?
让我们从简单但慢慢来开始
我的第一次尝试是简单地逐行解析文件,然后将结果收集到一个字典中,每个站名作为键,温度作为Float64类型的向量添加到值中。我预期这会很慢,但我们的目标是获得基准性能的数字。
一旦字典准备好,我们就可以计算所需的统计数据:
所有数据处理的输出需要以某种特定格式显示。以下函数实现了这一点:
由于这个实现预计需要较长时间,我们可以通过仅运行一次@time来进行简单的测试:
@time get_stations_dict_v2("measurements.txt") |> calculate_output_v3 |> print_output_v1
<output omitted for brevity> 526.056399 seconds (3.00 G allocations: 302.881 GiB, 3.68% gc time)
我们这个简单的实现大约需要 526 秒,即约 9 分钟。虽然它确实很慢,但也并没有那么糟糕!
升级一下——进入多线程!
与其一次读取输入文件的一行,不如尝试将文件拆分成多个块,然后并行处理所有块。Julia 让实现并行for循环变得非常简单。然而,在这样做时,我们需要采取一些预防措施。
在进入循环之前,我们首先需要弄清楚如何将文件拆分成多个块。可以通过使用内存映射来读取文件。然后,我们需要确定每个块的start和end位置。需要注意的是,输入数据文件中的每一行以换行符结尾,其字节表示为0x0a。因此,每个块应该在该字符处结束,以确保在解析文件时不出错。
以下函数将块的数量num_chunks作为输入参数,然后返回一个数组,其中每个元素都是内存映射的块。
由于我们从不同的块中解析站点和温度数据,我们还需要在最后将它们合并。每个块将首先被处理为字典,如前所示。然后,我们按如下方式合并所有块:
现在我们知道如何将文件拆分成多个块,以及如何在最后将各个块解析出的字典合并。然而,只有在我们能够并行处理这些块时,才能获得所需的加速效果。这可以通过for循环来实现。请注意,Julia 应该使用多线程启动 julia -t 12,否则这个解决方案不会产生任何影响。
此外,我们现在希望运行一个正式的统计基准测试。这意味着该挑战应该执行一定次数,然后我们可以可视化结果的分布。幸运的是,所有这些都可以通过BenchmarkTools.jl轻松完成。我们将最大样本数限制为 10,整个运行的最大时间设为 20 分钟,并启用垃圾回收(将在样本之间释放内存)。所有这些都可以在一个脚本中整合。请注意,输入参数现在是文件名fname和数据块数num_chunks。
以下是基准测试结果和所用输入。请注意,我们在这里使用了 12 个线程。
julia> Threads.nthreads()
12
julia> ARGS = ["measurements.txt", "48"]
2-element Vector{String}:
"measurements.txt"
"48"

12 个线程,数据块数 = 48(图片由作者提供)
多线程显著提升了性能,我们现在的时间大约降至 2 分钟多一点。接下来我们看看还能做什么改进。
避免存储所有温度数据
到目前为止,我们的方法是存储所有的温度数据,然后在最后确定所需的统计值(最小值、平均值和最大值)。然而,我们已经可以在解析输入文件的每一行时实现相同的功能。每当找到一个新的值时(如果是更大的值则更新最大值,或更小的值则更新最小值),我们就替换现有的值。对于平均值,我们将所有的值相加,并保持一个单独的计数器,用于记录某个站点的温度出现了多少次。
总体而言,我们的新逻辑如下所示:
合并所有结果(来自不同数据块)的函数也需要相应地进行更新。
让我们进行一个新的基准测试,看看这个改变是否能改善时间表现。

12 个线程,数据块数 = 48(图片由作者提供)
中位数时间似乎有所改善,但改进幅度很小。不过,依然算是一个胜利!
更多性能提升
我们之前的逻辑用于计算和保存温度的最大值和最小值可以进一步简化。此外,按照这个Julia Discourse 帖子中的建议,当解析站点名称和温度数据时,我们可以使用视图(通过@view)。这一点在 Julia 性能手册中也有讨论。由于我们在解析每一行时使用了切片表达式,@view帮助我们避免了分配和复制的开销。
其余的逻辑保持不变。现在运行基准测试得到以下结果:

12 个线程,数据块数 = 48(图片由作者提供)
哇!我们成功地将时间缩短到了接近一分钟。看起来切换到不同的视图确实有很大的影响。也许,还有更多的调整可以进一步提高性能。如果你有任何建议,请在评论中告诉我。
使用外部包
仅限使用基础 Julia 进行限制是很有趣的。然而,在实际应用中,我们几乎总是会使用包,因此可以利用现有的高效实现来执行相关任务。在我们的例子中,CSV.jl(并行解析文件)和 DataFrames.jl(执行 groupby 和 combine)将会派上用场。
以下函数执行以下任务:
-
使用
Mmap读取大文件 -
将文件分割成预定义数量的块
-
遍历这些块,使用
CSV.read(将 12 个线程传递给ntasks)并行读取每个块到 DataFrame 中。 -
使用 DataFrame 的
groupby和combine获取每个车站的结果 -
将所有 DataFrame 连接起来,合并所有分块的结果
-
循环结束后,执行一次
groupby和combine,以获取所有车站的最终结果集。
现在,我们可以以与之前相同的方式运行基准测试。

12 个线程,分块数 = 48,使用外部包(图片由作者提供)
使用 CSV.jl 和 DataFrames.jl 的性能相当不错,尽管比我们使用基础 Julia 实现的要慢。在实际项目中,这些包是数据科学家工具包中的重要组成部分。因此,探索使用这种方法是否可以进行进一步优化将是很有趣的。
结论
在这篇文章中,我们通过 Julia 解决了十亿行挑战。从一个非常简单的实现开始,花费大约 10 分钟,我们通过对代码的迭代修改,成功地大幅提升了性能。最优化的实现可以在约 1 分钟内完成挑战。我确信仍然有改进的空间。作为额外的收获,我们学到了一些宝贵的技巧,如何处理超出内存的数据集。当使用 Julia 进行大数据分析和可视化时,这可能会派上用场。
希望你觉得这个练习有用。感谢你的时间!可以通过 LinkedIn 与我联系,或者访问我的 Web 3.0 网站。
参考文献
启动我进入数据科学的那一个思维方式转变
让它成为现实:微小的改变帮助你进入数据科学或任何梦想职业
·发布于Towards Data Science ·10 分钟阅读·2024 年 10 月 19 日
--

图片来源:Jared Rice于Unsplash
你知道吗,77%的员工对自己的工作既不投入也不积极参与? 没错——我们大多数人都有一份不能真正满足我们的工作。考虑到一个人一生大约会花费 90,000 小时在工作上,找到一份自己喜欢的事业变得尤为重要。
想象一下,如果你花费这 90,000 小时做一些能让你兴奋、挑战自我并带给你目标感的事情,它可能对你的整体幸福感产生重要影响。但对许多人来说,找到一份自己喜欢的工作似乎遥不可及。所有这些障碍看起来似乎无法克服。
或许你能感同身受。你已经努力去改变,找到一份自己喜爱的职业。你通过人际网络,反复修改简历,阅读了所有能找到的职业建议文章。可梦想中的工作依然遥不可及。你在问自己:“我做错了什么?”
如果这听起来很熟悉,那你不是唯一一个。也许你已经做出了那两个提高生产力的小改变。它们提高了你的工作满意度,但仍然不够。
但如果我告诉你,进入一份你热爱的职业可能比你想的更简单呢?如果只是一些小的变化,而不是能力的欠缺呢?这些微小的调整或许正是你需要的,它们能帮助你将梦想转化为现实。
这就是我的故事。几年前,在我成为数据科学家的路上,我发现了一些意想不到的东西:一个小小的变化,带来了巨大的不同。正是这个视角上的小小变化,让我能够识别新的机会,并在数据科学领域取得进步。
最棒的部分是什么?它适用于你想从事的任何工作,不仅仅是数据科学。
苹果公司联合创始人史蒂夫·乔布斯是一位远见卓识的人,他改变了技术行业。他在 iPhone、iPad 和 Macintosh 等领先产品的开发中起到了关键作用。即使今天,我们使用技术的方式依然受到这些产品的影响。
乔布斯理解用户的需求,并且天生具有设计天赋。
这正是苹果与众不同的地方。它不仅仅是做技术,而是创造出人们喜欢使用的美丽产品。
他一直倡导无论人生走到哪里,都要追寻自己的激情。他在 2005 年斯坦福大学的著名演讲中曾分享过一句话:“做伟大的工作唯一的方式就是热爱你所做的。”这个观点不仅对他自己的人生产生了深远影响,它也是苹果文化的一部分……在那里,你做的是你喜欢的事情。
沃伦·巴菲特也是如此,他是历史上最伟大的投资者之一。他曾经说过:“在商业世界里,最成功的人是那些做自己喜欢的事情的人。”他通过自己的努力,将伯克希尔哈撒韦公司打造成了商业帝国,整个职业生涯都是如此。
列表还在继续。奥普拉·温弗瑞也分享了同样的观点,还有理查德·布兰森。
我知道你在想什么。你以前听过“追随你的激情”的建议。这句话出现在励志海报上,重复了很多次。也许你也曾试图找到自己的激情,却因为挫折而放弃。但如果你一直看错了方向呢?
现实是,仅仅追随你的激情,很可能不会让你拥有一个充实的职业生涯。关键在于找到那个“点”,你独特的技能和激情交汇的地方。这些是让你与众不同的独特能力。
正是这个微妙的转变改变了一切。这让我找到了一个我能真正出色且充满激情的职业。
但你如何找出这些独特的技能和能力呢?在你日常生活中,可能有一些细微的线索你没有注意到?或许这些特殊技能就在你眼前,正等待着你的发现。
识别你的自然倾向
找到自己真正的使命通常始于童年的兴趣,或称为“早期兴趣”。
我一直是那个问问题特别多的好奇孩子,直到父母有些烦恼。我总是想要理解并修复我周围的一切。发现不一致和低效对我来说是件容易的事。
除了这种问题解决的倾向,我还有商业头脑。从小我就总是启动和推动各种项目,不论是与邻居一起策划活动,还是领导课外活动。随着时间的推移,我对项目管理的热爱与日俱增。到十八岁时,我已经创办了自己的公司,并运营了五年。
这种分析思维和对商业的兴趣自然地引导我走上了一个可以同时运用两者的职业道路。我被解决那些直接影响组织利润的复杂问题所吸引,总是寻找优化投资回报率的方法。这条路对我来说,感觉是真实的,符合我内心的本质。

图片由Olav Ahrens Røtne提供,来源于Unsplash。
想一想你的童年,或者观察一下你在空闲时间里自然喜欢做什么。小时候有什么活动是你能玩上几个小时,或者现在仍然能让你全身心投入的?在做这些事情时,哪些活动能让你完全不去想其他事?
你是那个总是组织团队活动,轻松担任领导角色并将人们聚集在一起的人吗?还是你是那个解决复杂问题的首选人选?这些倾向可能指向一些自然的优点,虽然它们未必是显而易见的“热情”所在。
如果你的童年记忆没有提供明确的指导,考虑一下哪些活动能带给你真正的快乐——那些你愿意免费(或几乎免费)做的事情。
对我来说,我喜欢识别那些可能对组织产生最大影响的解决方案。这对我来说是很自然的。
这超出了我的工作时间范围。
我发现自己不断在审视几个公司创意,从大型公司到小型零售商店。鉴于大多数小企业在几年内就会关闭,我觉得它们如何与大公司竞争非常有趣。公司如何赚钱令我着迷。有时甚至会使用亏损领导产品来吸引消费者,然后从特定商品或服务中赚钱。
一旦我发现这些“高潜力机会”,我对技术的热情就会被激发出来。拥有技术背景的我,不仅想要假设,更想要付诸行动。我喜欢建造和创造这些工具。从开发自动化任务的工具,到构建和调优机器学习模型,这种动力以多种形式展现出来。
这些不仅仅是工作任务。我在业余时间也做这些,工作之外我也是为了乐趣。我主要的兴趣领域是数据科学和任务自动化。
这就是我选择成为数据科学家的原因。它将我对复杂问题解决的热情与设计和创造能带来改变的解决方案的机会结合在了一起。
发挥你独特的价值
亚历克斯·霍诺德(Alex Honnold)5 岁时开始在健身房攀岩,11 岁时开始到户外攀岩。2017 年,他完成了他最著名的成就——完成了优胜美地国家公园第一条自由单人攀登的 El Capitan Freerider 路线。
换句话说,他在没有任何绳索、保护带或防护装备的情况下,攀登了一个 3000 英尺的路线,这条路线对于经验丰富的攀岩者来说也是一条艰难的路线(该路线的难度评级为 5.13a)。甚至像汤米·卡尔德威(Tommy Caldwell)这样的著名攀岩者也被这个尝试所“吓到”了。这个成就被记录在纪录片《Free Solo》中,并在 2019 年获得了奥斯卡最佳纪录片奖。
像许多人一样,我被亚历克斯·霍诺德的成就所吸引。这项成就推动了攀岩领域的极限。
我对这项运动有着强烈的热情。如果我在空闲时间没有在做副业的 AI 项目,那么我很可能正在攀岩。
然而,我的攀岩技能是否和亚历克斯·霍诺德、亚当·翁德拉(Adam Ondra)或汤米·卡尔德威一样高?不。我没有从小开始,也没有把我的一生奉献给掌握这项运动。我是十年前才开始的。要达到他们的攀岩水平,我需要付出极大的努力。
相反,我是否容易发现潜在的高影响力机会并解决复杂问题?是的,而且这是我天生的能力。我在这些领域表现出色,几乎不费力气,和攀岩不同,尽管我非常喜欢这项运动。

大多数时候,人们向你寻求帮助的是什么?相比其他任务,哪些任务对你来说更容易?考虑一下你在哪些方面能够做出重大贡献。你擅长什么?有哪些独特且宝贵的技能使你与众不同?这些可以是你独特能力的线索。
也许你天生就擅长用简单的语言解释技术概念,或者你擅长将人们团结在一起。
然而,也许你的生活环境使得追随你的热情看起来难以实现。然而,你已经清楚地知道自己的热情所在,并承认自己的特殊才能。你是否感觉到你现在的状态和你想要达到的目标之间存在差距?这段旅程是否让你觉得压力重重?
我也曾经历过那种情况。我曾觉得现实中的种种限制使我无法追求自己的理想。无论你的生活环境如何,总会有解决办法。
— 老子
如果你不喜欢你的工作,或者它提供的成长空间有限,不要让这些限制阻碍你。开始花更多时间在你真正喜欢的活动上——即使只占你时间的 10%或 20%!这只是一个开始。即使现在需要额外的努力,从长远来看,这会有所回报。
在我以前的工作中,我们团队花费了大量时间做手动任务,比如在 Excel 表格中复制和粘贴数据。
我的老板没有要求解决方案,但我决定使用 Python 自动化这些任务,这正是我喜欢并擅长的工作。我在正常工作时间之外做了这些,结果证明是值得的。通过这些脚本,我们成功节省了相当于 1.5 个全职员工手动工作的时间!
当我的老板看到这些节省的时间后,他给我更多的自动化任务。我甚至开始研究基本的机器学习算法。通过做我喜欢的事并积极主动,我稍微改变了我的角色。这个小小的变化为我带来了新的机会。
另一个好的策略是与经理进行对话。弄清楚你喜欢做什么,什么能为组织创造价值。然后,坦诚地讨论这些事情。如果你觉得很难与经理进行这种对话,也许值得考虑换个重视开放沟通的工作环境!
曾经,我在当前的角色中做了更多的手动任务。我向经理重申了自己有多喜欢设计和构建解决方案。我向老板解释了至少在我的时间中腾出一部分来做这些事情对我有多么重要。通过那次对话,我成功地将自己的任务调整回了那些我真正喜欢的事情。
如果你想成为一名教练或指导者,你应该从今天开始就表现得像一个。你不必是经理,也不需要等待许可才能做出改变。帮助你的团队成员,提供建议,并支持你的同事们。
你可能喜欢创意设计,但目前的工作并没有让你充分发挥这一技能。它应该立即成为你工作的一部分。主动为团队演示制作艺术作品,并为报告增添你的艺术气息。你可以立刻开始提升这些技能。
此外,确保与你的经理讨论你的兴趣和职业抱负。谈谈你如何将自己的兴趣与组织目标对接,并寻找在目前的工作或未来项目中使用这些技能的方法。

图片来源:Unseen Studio 通过 Unsplash
保持灵活性
比尔·盖茨与他从小一起长大的朋友保罗·艾伦于 1975 年共同创办了微软。从一个小型合作伙伴关系起步,他们开始为新兴的计算机市场开发软件。这项努力最终催生了像 Windows 和 MS-DOS 这样的突破性技术。
在盖茨的领导下,微软发展成了一个科技巨头。这使他成为世界上最富有的人之一,并改变了人们使用计算机的方式。
但问题在于,盖茨最终并没有继续做技术工作。他发生了转变。在将微软打造成科技巨头之后,盖茨意识到自己的兴趣和价值观正转向解决全球性挑战。
接受这一变化后,他将焦点转向了比尔与梅琳达·盖茨基金会。他将自己的资源和影响力投入到提升全球健康、改善教育和减少贫困的事业中。从微软的联合创始人到全职慈善家的转变,是一个令人瞩目的例子,展示了在职业生涯中适应变化和接受新机会的重要性。
你的兴趣和就业市场会不断发展。要对变化和能够应用你技能的新领域保持开放。
我在数据科学和开发具有实际影响力的解决方案中找到极大的满足感。虽然这些领域激励着我,但我知道我的价值观和兴趣可能会发生变化。这种适应性让我在使用现有知识的同时,继续扩展我的技能。
当你刚开始做某件事时,可能并不会对它充满激情。然而,随着你在一个领域中的进步和发展,激情往往会逐渐增长。学习一项有用的技能可以带来极大的满足感,并激发对它的真正热情。在一个领域中卓越表现不仅让人感觉充实,还能揭示出意想不到的机会。拥抱成长并保持灵活性,将有助于你拥有一段充满意外和满足感的职业生涯。
结论
记住,这并不是关于找到一个“神奇”的激情,让你的工作每天都变得毫不费力。而是找到一份能够发挥你独特技能、提供价值并赋予你目标感的工作。如果你专注于获得那些在你擅长的领域中稀缺且有用的技能,你在职业生涯中更可能取得成功。
喜欢这篇文章吗?表达你的支持!
👏 鼓掌 50 次
🤝 发送一份LinkedIn连接请求,保持联系
你的支持意味着一切! 🙏
终身机器学习的起源:为什么 LLML 是 AI 领域的下一个游戏改变者(第一部分)
通过 Q 学习和基于解释的神经网络理解终身机器学习的力量
·发表于 Towards Data Science ·阅读时间 10 分钟·2024 年 1 月 17 日
--

太空中的 AI 机器人,使用 GPT-4 生成
机器学习接下来如何发展?许多,甚至可以说是大多数,机器学习领域的伟大创新都受到了神经科学的启发。神经网络和基于注意力的模型的发明就是典型例子。同样,机器学习的下一次革命将从大脑中汲取灵感:终身机器学习。
现代机器学习仍然缺乏像人类一样在学习新领域时使用过去信息的能力。例如,一个学会了走路的强化学习代理,在学习如何攀爬时仍然从零开始。然而,代理可以使用持续学习:它可以将走路中获得的知识应用于学习攀爬的过程,就像人类一样。
受到这一特性的启发,终身机器学习(LLML)利用过去的知识来更高效地学习新任务。通过在机器学习中逼近持续学习,我们可以极大地提高学习者的时间效率。
为了理解 LLML 的强大功能,我们可以从其起源开始,逐步了解现代 LLML。在第一部分中,我们探讨了 Q 学习和基于解释的神经网络。在第二部分中,我们将深入研究高效的终身学习算法和 Voyager!尽管我鼓励你先阅读第一部分再阅读第二部分,但如果你愿意,也可以直接跳到第二部分!
终身机器学习的起源
Sebastian Thrun 和 Tom Mitchell,LLML 的奠基人,开始他们的 LLML 之旅时,研究了强化学习在机器人中的应用。如果读者曾经看到过一个可视化的强化学习者(例如这个代理 学习玩宝可梦),他们会意识到,为了在合理的人类时间尺度内取得任何训练结果,代理必须能够在训练期间迭代数百万次动作(如果不是更多的话)。然而,机器人每个动作需要几秒钟才能完成。因此,将典型的在线强化学习方法应用于机器人,会显著降低最终机器人模型的效率和能力。
是什么让人类在现实世界学习中如此出色,而机器人中的机器学习目前却失败了呢?
Thrun 和 Mitchell 发现了现代机器学习能力中可能最大的空白:它无法将过去的信息应用于新任务。为了解决这个问题,他们创造了第一个基于解释的神经网络(EBNN),这也是 LLML 的首次应用!
要理解它是如何工作的,我们首先需要了解典型的强化学习(RL)是如何运作的。在强化学习中,我们的机器学习模型决定了代理的行动,我们可以将其视为与我们选择的环境进行互动的“身体”。我们的代理存在于环境 W 中,具有状态 Z,当代理采取行动 A 时,它会收到感知 S(来自环境的反馈,例如物体的位置或温度)。我们的环境是一个映射 Z x A -> Z(对于每一个动作,环境会以指定的方式发生变化)。我们希望在模型 F:S -> A 中最大化奖励函数 R:S -> R(换句话说,我们希望选择能获得最佳结果的动作,我们的模型将感知作为输入并输出一个动作)。如果代理有多个任务需要学习,每个任务都有其自己的奖励函数,我们希望最大化每个函数。
我们可以独立训练每个单独的任务。然而,Thrun 和 Michael 意识到,每个任务都发生在相同的环境中,代理面临相同的可能动作和感知(只是每个任务有不同的奖励函数)。因此,他们创建了 EBNN,利用以前问题中的信息来解决当前任务(LLML)!例如,一个机器人可以利用它在杯子翻转任务中学到的知识来执行杯子移动任务,因为它在杯子翻转中学会了如何抓住杯子。
为了了解 EBNN 是如何工作的,我们现在需要理解 Q 函数的概念。
Q 和 Q 学习*
Q: S x A -> r 是一个评估函数,其中 r 表示在状态 S 下采取动作 A 后的预期未来总奖励。如果我们的模型学会了准确的 Q,它可以简单地选择在任何给定时刻最大化 Q 的动作。
现在,我们的问题转化为学习一个准确的 Q,我们称之为 Q。其中一种方法被称为Q 学习,一些人认为它是 OpenAI 的 Q背后的灵感来源(尽管命名可能完全是巧合)。
在 Q 学习中,我们将我们的动作策略定义为函数π,该函数为每个状态输出一个动作,状态 X 的值定义为函数

我们可以将其视为动作π(x)的即时奖励,再加上所有可能未来动作的概率之和乘以它们的价值(我们通过递归计算这些价值)。我们希望找到最佳策略(动作集)π*,使得

(在每个状态下,策略选择最大化 V的动作)。随着过程的重复,Q 值将变得更加准确,从而改善代理选择的动作。现在,我们将 Q值定义为执行动作 a 的真实预期奖励:

在 Q 学习中,我们将学习π的问题简化为学习π的 Q*值的问题。显然,我们希望选择 Q 值最大的动作。
我们将训练分为多个回合。在第 n 回合,我们获得状态 x_n,选择并执行动作 a_n,观察到 y_n,获得奖励 r_n,并根据以下公式调整 Q 值:

其中

实质上,除了与前一个状态 x 和所选动作 a 对应的 Q 值外,我们将所有先前的 Q 值保持不变。对于那个 Q 值,我们通过将前一个回合的 Q 值乘以(1 - α)并加上我们的回报,再加上当前状态 y 的前一个回合的最大值,这两者都乘以α来更新它。
记住,这个算法的目标是近似每个可能状态下每个可能动作的准确 Q 值。所以当我们更新 Q 时,我们更新与旧状态和该回合采取的动作对应的 Q 值,因为我们
α越小,我们每个回合更新 Q 值的变化就越小(1 - α会非常大)。α越大,我们就越不关心 Q 的旧值(当α=1 时,它完全无关紧要),而越关注我们所发现的新状态的预期值。
让我们考虑两种情况,以便直观地理解这个算法,以及它如何在我们从状态 x 采取动作 a 到达状态 y 后更新 Q(x, a):
-
我们从状态 x 通过动作 a 到达状态 y,并处于一个“终点路径”,此时无法再执行任何操作。那么,Q(x, a),即这个动作和之前状态的期望值,应该仅仅是该动作的即时奖励(想想为什么!)。此外,a 的奖励越高,我们在下一个回合中选择它的可能性就越大。我们在前一个回合中该状态的最大 Q 值为 0,因为无法执行任何操作,因此我们只是将该动作的奖励添加到 Q 值中,正如预期的那样!
-
现在,我们正确的 Qs 从终点递归回溯!让我们考虑导致状态 w 到状态 x 的动作 b,假设现在我们已经过了 1 回合。现在,当我们更新 Q(w, b) 时,我们将把 b 的奖励添加到 Q*(x, a) 的值中,因为如果我们之前选择了它,它必须是最高的 Q 值。因此,我们的 Q(w, b) 现在也是正确的(想想为什么)!
太好了!现在你已经对 Q-learning 有了直观的理解,我们可以回到最初的目标,继续理解:
基于解释的神经网络(EBNN)
我们可以看到,通过简单的 Q-learning,我们没有 LL 属性:即先前的知识被用于学习新任务。Thrun 和 Mitchell 创立了解释性神经网络学习算法(Explanation Based Neural Network Learning Algorithm,EBNN),该算法将 LL 应用于 Q-learning!我们将该算法分为 3 个步骤。
(1) 在执行一系列操作后,智能体预测接下来将发生的状态,直到最终状态 s_n,此时不再可能执行其他操作。这些预测将与实际观察到的状态不同,因为我们的预测器目前不完美(否则我们早就完成了!)。
(2) 算法提取 Q 函数关于观察到的状态的偏导数。通过最初计算最终奖励关于最终状态 s_n 的偏导数(顺便说一下,我们假设智能体已经获得奖励函数 R(s)),然后我们使用链式法则从最终状态向后计算斜率:

其中 M: S x A -> S 是我们的模型,R 是我们的最终奖励。
(3) 现在,我们已经估算了 Q*s 的斜率,并使用这些斜率在反向传播中更新我们的 Q 值!对于那些不知道的人,反向传播是神经网络学习的方法,通过这种方法,它们计算网络每个节点改变时,最终输出如何变化,使用这种同样的反向计算斜率方法,然后调整这些节点的权重和偏置,使得网络输出更接近期望(无论这如何通过网络的成本函数定义,成本函数与我们的奖励函数有着相同的作用)!
我们可以将(1)视为解释步骤(因此得名!),我们回顾过去的行动并尝试预测可能采取的行动。接着在(2)中,我们对这些预测进行分析,试图理解不同的行动如何影响我们的奖励变化。在(3)中,我们将这种理解应用于学习如何通过改变我们的 Q 值来改进行动选择。
该算法通过利用过去行为与过去行为估计之间的差异,作为提升估计某一行动路径效率的动力,从而提高我们的效率。你可能接下来的问题是:
EBNN 如何帮助一个任务的学习迁移到另一个任务?
当我们将 EBNN 应用于多个任务时,我们将任务间共同的信息表示为神经网络(NN)行动模型,这通过解释和分析过程为我们的学习提供了助力(一个有效的偏向)。它在学习新任务时使用先前学习的任务独立知识。我们的关键洞察是,我们拥有可泛化的知识,因为每个任务都共享相同的代理、环境、可能的行动和可能的状态。唯一依赖于每个任务的是我们的奖励函数!因此,通过从解释步骤开始,使用特定于任务的奖励函数,我们可以利用旧任务中发现的状态作为训练示例,并简单地将奖励函数替换为当前任务的奖励函数,从而加速学习过程!LML 的创始人发现,机器人抓杯任务的时间效率提高了 3 到 4 倍,这仅仅是开始!
如果我们重复这个解释和分析过程,我们可以替代一些朴素 Q 学习中需要通过现实世界探索代理环境的部分需求!而且我们使用得越多,它就变得越有效,因为(抽象地说)它可以从更多的知识中获取信息,增加这些知识与当前任务相关的可能性。
自从 LLML 的创始人提出使用任务独立信息来学习新任务的思想以来,LLML 不仅在机器人强化学习中取得了进展,而且扩展到我们今天所知的更广泛的机器学习领域:监督学习。Paul Ruvolo 和 Eric Eatons 的高效终身学习算法(ELLA)将使我们更接近理解 LLML 的强大力量!
请阅读第二部分:通过 ELLA 检查 LLML和 Voyager,了解它是如何工作的!
感谢阅读第一部分!欢迎访问我的网站anandmaj.com,那里有我的其他文章、项目和艺术作品,并在Twitter上关注我。
原始论文及其他来源:
Thrun 和 Mitchel: 终身机器人学习
Watkins: Q 学习
陈和刘,《终身机器学习》(启发我写这篇文章!):www.cs.uic.edu/~liub/lifelong-machine-learning-draft.pdf
带有课程的无监督 LL: par.nsf.gov/servlets/purl/10310051
深度 LL: towardsdatascience.com/deep-lifelong-learning-drawing-inspiration-from-the-human-brain-c4518a2f4fb9
神经启发的 AI: www.cell.com/neuron/pdf/S0896-6273(17)30509-3.pdf
具身 LL: lis.csail.mit.edu/embodied-lifelong-learning-for-decision-making/
高效 LLA (ELLA): www.seas.upenn.edu/~eeaton/papers/Ruvolo2013ELLA.pdf
情感分类中的 LL: arxiv.org/abs/1801.02808
知识基础思想: arxiv.org/ftp/arxiv/papers/1206/1206.6417.pdf
AGI LLLM LLMs: towardsdatascience.com/towards-agi-llms-and-foundational-models-roles-in-the-lifelong-learning-revolution-f8e56c17fa66
DEPS: arxiv.org/pdf/2302.01560.pdf
Voyager: arxiv.org/pdf/2305.16291.pdf
元强化学习调查: arxiv.org/abs/2301.08028
生成式人工智能的类比
以及 AI 应用民主化
·发布于 Towards Data Science ·阅读时长 7 分钟·2024 年 2 月 24 日
--

图片由 Norris Niman 提供,来源于 Unsplash
当被问到“你认为 AI 将如何改变我们[人类]?”时,杰夫·贝索斯在回答之前,做出了一个引人深思的评论。
“目前形式的巨大语言模型并不是发明。它们是发现。”
为了通过他的例子来说明这一点,伽利略发明了望远镜,后来发现了木星的卫星。他通过辩称“[与]787[这类工程化物品]不同,LLM 更像是发现。我们不断被它们的能力所惊讶”,将大型语言模型定位于发明与发现的光谱的另一端。
大型语言模型——你可以将其扩展到整个生成式 AI——已经在各个领域推动着令人兴奋的创新应用。它们以各种形态被包装,并以个性化客服聊天机器人、写作助手等形式提供给用户,甚至嵌入到像 Adobe Photoshop 这样的老牌广泛使用的工具中新功能。所以这是真正的技术。它们是工程化的工具——已经是发明——对吧?
有时候,外壳实在是太薄了。当你停下来反思贝索斯的评论¹,并开始把 OpenAI 每次发布的新 GPT 版本或 Meta 发布的最新开源模型,仅仅看作是……
帕雷托原则在数据工程中的应用

数据堆栈评估的演变。图片由作者提供,使用 Open AI 制作。
一步前进,毫不退步
·发表于 Towards Data Science ·6 分钟阅读·2024 年 9 月 26 日
--
不是 Medium 会员?请 点击这里 免费阅读 🚀
我们都听说过帕雷托原则。它也被称为 80/20 法则。这个概念认为你可以通过 20% 的努力完成 80% 的工作,而剩下的 20% 的工作却需要消耗 80% 的努力。
它来源于一位经济学家的研究,这位经济学家非常著名,以至于你在学校里也会学习到他们的内容。他们的名字是维尔弗雷多·帕雷托。学校里首先教给你的一个概念,也是与帕雷托同名的概念:帕雷托效率。

维尔弗雷多·帕雷托。来源:维基百科
如果一个结果无法在不损害任何相关方福祉的情况下改进,那么就可以说它是帕雷托效率或帕雷托最优的。例如,虽然对于社会来说,实施更严格的环境法可能是一个“净改善”,但这种政策并不构成帕雷托改进。因为那些污染环境的公司会因此变得更糟。
从形式上讲,一个状态是帕雷托最优的,如果没有任何其他状态能够使至少一个参与者的福祉更高,并且没有任何人的福祉更低。如果有一个状态变化能够…
数据质量管理的过去、现在与未来:理解 2024 年数据测试、监控与数据可观察性
数据领域正在发展,数据质量管理也需要与时俱进。以下是三种常见的方法以及在人工智能时代该领域的发展方向。
·发布于 Towards Data Science ·阅读时长 9 分钟·2024 年 5 月 25 日
--

图片来源:作者
它们是不同的词,表示同一个事物吗?对同一问题的独特解决方法?还是完全不同的东西?
更重要的是——你真的需要这三者吗?
就像数据工程中的其他一切一样,数据质量管理也在飞速发展。数据和人工智能在企业中的快速崛起,使数据质量成为现代企业的“零日”风险——也是数据团队需要解决的核心问题。由于术语重叠,如何将这些内容联系在一起并不总是显而易见——或者说,是否能联系在一起。
然而,与一些人可能认为的相反,数据质量监控、数据测试和数据可观察性并不是互相矛盾或替代的数据质量管理方法——它们是一个单一解决方案的互补要素。
在本文中,我将深入探讨这三种方法的具体内容,它们在什么情况下表现最佳,在哪些方面有所不足,以及如何优化数据质量实践,以推动 2024 年数据信任的提升。
了解现代数据质量问题
在我们了解当前的解决方案之前,首先需要理解问题本身——以及它随时间的变化。让我们考虑以下类比。
假设你是负责本地供水的工程师。当你接手这个工作时,城市只有 1000 名居民。但在城市地下发现黄金后,你的小社区从 1000 人迅速发展为一个真正的 100 万人大城市。
这会改变你做事的方式吗?
首先,在一个小型环境中,故障点相对较少——如果管道出现故障,根本原因通常可以缩小到几个预期的罪魁祸首(如管道冻结、有人挖掘水管等常见原因),并且可以通过一两名员工的资源迅速解决。
随着需要设计和维护的 100 万新居民的管道蜿蜒而来,满足需求所需的快速节奏,以及你的团队有限的能力(和可见性),你不再能够像以前那样定位和解决每个你预期会出现的问题——更不用说留意那些你预料不到的问题了。
现代数据环境也是如此。数据团队发现了“金矿”,利益相关者也想参与其中。随着你的数据环境不断扩大,数据质量变得越来越具挑战性——而传统的数据质量方法则变得不再有效。
它们不一定是错误的,但它们也不足够。
那么,数据监控、测试和可观察性之间有什么区别呢?
必须明确的是,这些方法都是试图解决数据质量问题。因此,如果这是你需要构建或购买的目标,那么理论上任何一种方法都能解决这个问题。然而,尽管这些都是数据质量解决方案,但并不意味着它们能真正解决你的数据质量问题。
这些解决方案应该何时以及如何使用,比这要复杂一些。
简单来说,你可以将数据质量看作是问题;将测试和监控视为识别质量问题的方法;而数据可观察性则是一种不同且全面的方法,它结合并扩展了这两种方法,通过更深入的可见性和解决功能来解决大规模的数据质量问题。
或者更简单地说,监控和测试识别问题——而数据可观察性则不仅识别问题,还能使问题变得可操作。
这里有一个快速的插图,可能有助于直观地了解数据可观察性在数据质量成熟度曲线中的位置。

图片由作者提供。来源。
现在,让我们更详细地探讨一下每种方法。
数据测试
传统的两种数据质量方法之一是数据测试。数据质量测试(或简单的数据测试)是一种检测方法,使用用户定义的约束或规则来识别数据集中已知的特定问题,从而验证数据完整性并确保特定的数据质量标准。
为了创建数据测试,数据质量负责人需要编写一系列手动脚本(通常使用 SQL 或利用像dbt这样的模块化解决方案)来检测特定问题,如过高的空值率或错误的字符串模式。
当你的数据需求——以及因此你的数据质量需求——非常小的时候,许多团队能够通过简单的数据测试满足需求。然而,随着数据规模和复杂性的增长,你很快会遇到新的数据质量问题——并需要新的能力来解决它们。而这一时刻会比你想象的来得更快。
尽管数据测试将继续是数据质量框架中的必要组成部分,但在几个关键领域仍然存在不足:
-
需要深入的数据知识——数据测试要求数据工程师具备 1) 足够的专业领域知识来定义质量,以及 2) 足够了解数据可能出现的问题,以便设定测试来验证数据。
-
没有覆盖未知问题——数据测试只能告诉你预期会发现的问题——而不是那些未预见的问题。如果没有编写测试来覆盖特定问题,测试将无法发现它。
-
不可扩展——为 30 个表编写 10 个测试,与为 3000 个表编写 100 个测试是完全不同的。
-
有限的可见性——数据测试只会测试数据本身,因此无法告诉你问题究竟是数据、系统还是驱动它的代码出了问题。
-
没有解决方案——即使数据测试发现了问题,它也无法帮助你更接近解决问题的方案;也无法理解问题影响了什么以及谁。
在任何规模下,测试都会变成数据领域中的“在拥挤的街道上大喊‘着火了!’然后什么也不做”的行为。
数据质量监控
另一种传统的——尽管有些复杂的——数据质量方法,数据质量监控是一种持续的解决方案,通过手动阈值设置或机器学习不断监控并识别潜藏在数据中的未知异常。
例如,数据按时到达了吗?你得到了预期的行数吗?
数据质量监控的主要好处是,它为未知的未知问题提供了更广泛的覆盖,并且解放了数据工程师,免去为每个数据集编写或克隆测试以手动识别常见问题的麻烦。
从某种意义上说,你可以认为数据质量监控比测试更具整体性,因为它比较了时间上的指标,并使团队能够发现他们从已知问题的单元测试中看不到的模式。
不幸的是,数据质量监控在一些关键领域仍然存在不足。
-
增加的计算成本 — 数据质量监控很昂贵。像数据测试一样,数据质量监控直接查询数据 — 但由于它旨在识别未知的未知问题,所以需要广泛应用才能有效。这意味着需要较高的计算成本。
-
缓慢的价值实现时间 — 监控阈值可以通过机器学习实现自动化,但你仍然需要先自己构建每个监控。这意味着你需要为每个问题在前端进行大量编码,然后随着数据环境的增长,手动扩展这些监控。
-
有限的可见性 — 数据可能由于各种原因出现问题。就像测试一样,监控只关注数据本身,因此它只能告诉你异常发生了 — 但无法告诉你为何会发生。
-
没有解决方案 — 尽管监控确实能检测到比测试更多的异常,但它仍然无法告诉你哪些方面受到了影响,谁需要知道这个信息,或者这些是否真的是重要的。
更重要的是,由于数据质量监控更擅长于提供警报,而不是管理警报,因此你的数据团队更可能在大规模应用时经历警报疲劳,而不是随着时间的推移真正提高数据的可靠性。
数据可观察性
这就引出了数据可观察性。与上述方法不同,数据可观察性指的是一种全面的、供应商中立的解决方案,旨在提供完整的数据质量覆盖,既具有可扩展性又可操作。
受软件工程最佳实践的启发,数据可观察性是一种端到端的 AI 驱动数据质量管理方法,旨在在单一平台内回答数据质量问题的“什么、谁、为什么和如何”。它通过将测试和全自动数据质量监控结合到一个系统中,弥补了传统数据质量方法的不足,并将覆盖范围扩展到数据、系统和代码层面。
结合关键事件管理和解决功能(如自动化列级谱系和警报协议),数据可观察性帮助数据团队从数据摄取到消费的全过程中检测、分类和解决数据质量问题。
更重要的是,数据可观测性旨在通过促进跨团队合作,包括数据工程师、分析师、数据所有者和利益相关者,为各职能部门提供价值。
数据可观测性在四个关键方面解决了传统数据质量实践的不足:
-
强大的事件处理与解决 — 最重要的是,数据可观测性提供了更快速解决事件的资源。除了标记和警报功能,数据可观测性还通过自动化的列级数据血缘分析,加速了根本原因分析过程,让团队能够一目了然地看到哪些数据受到了影响、谁需要了解情况,以及应去哪里解决问题。
-
完全的可视性 — 数据可观测性将覆盖范围扩展到数据源之外,涵盖数据流动和转化所涉及的基础设施、管道和数据摄取后的系统,以解决全公司领域团队面临的数据问题。
-
更快的价值实现 — 数据可观测性通过基于机器学习的监控系统完全自动化设置过程,提供开箱即用的即时覆盖,无需编写代码或设置阈值,因此你可以更快地获得覆盖,并随着时间的推移,自动根据环境进行扩展(同时提供自定义洞察和简化的编码工具,使用户定义的测试也变得更容易)。
-
数据产品健康跟踪 — 数据可观测性还将监控和健康跟踪扩展到传统表格格式之外,用于监控、衡量和可视化特定数据产品或关键资产的健康状况。
数据可观测性与 AI
我们都听过“垃圾进,垃圾出”这句话。这个格言对于 AI 应用来说更是成立。然而,AI 不仅需要更好的数据质量管理来指导其输出;你的数据质量管理本身也应由 AI 驱动,以最大化其对不断发展的数据环境的可扩展性。
数据可观测性是事实上的——也可以说是唯一的——数据质量管理解决方案,它使企业数据团队能够有效地为 AI 提供可靠数据。而它实现这一目标的一部分方式,就是通过成为一个支持 AI 的解决方案。
通过利用 AI 来创建监控、检测异常和进行根本原因分析,数据可观测性为实时数据流、RAG 架构以及其他AI 应用案例提供了超大规模的数据质量管理。
那么,2024 年数据质量的下一步是什么?
随着企业及其以外的数据环境不断发展,传统的数据质量方法无法监控数据平台可能发生故障的所有方式,也无法在发生故障时帮助你解决问题。
尤其是在 AI 时代,数据质量不仅是一个商业风险,更是一个生存风险。如果你不能信任输入到模型中的所有数据,就无法信任 AI 的输出。在 AI 的巨大规模下,传统的数据质量方法根本无法保护这些数据资产的价值或可靠性。
为了有效,测试和监控需要整合到一个平台无关的解决方案中,该解决方案能够客观地监控整个数据环境——数据、系统和代码——从头到尾,并为数据团队提供资源,以便更快地处理和解决问题。
换句话说,为了使数据质量管理发挥作用,现代数据团队需要数据可观察性。
第一步。检测。第二步。解决。第三步。繁荣。
生成性人工智能中的主导设计路径
对于主导设计的思考,以及推动生成性人工智能技术在争夺主导地位中的成功或失败的战略因素
·发表于数据科学前沿 ·阅读时长 10 分钟·2024 年 5 月 15 日
--

来源:作者使用 DALL-E 生成的图片
I. 引言
在过去半个世纪中,技术创新生命周期内争取主导设计的斗争一直是研究的重点。这些争斗发生在研究与开发(R&D)实验室中,围绕商业化和市场营销战略的讨论中,以及在媒体和消费者领域,但最终是在客户的心中,这些客户通过日常选择能力,左右了市场份额和产品的接受度。这也是历史为何记住了 VHS 而非 Betamax,为什么我们使用 QWERTY 键盘,以及谷歌搜索引擎和苹果 iPhone 的推出如何改变了行业的原因。ChatGPT 的出现向市场发出了一个信号,表明我们正处于另一场关于主导设计的争夺战之中,这一次是围绕生成性人工智能展开的。
生成性人工智能能够创造新内容并执行复杂任务,具有颠覆各行业的潜力,能够通过增强创造力、自动化任务和提升客户体验来推动变革。因此,各组织正在迅速投资于这个充满潜力的生态系统,以保持其相关性和竞争力。随着商业领袖、政府机构和投资者在这个迅速发展的生成性人工智能领域作出关于芯片、平台和模型等技术的决策,他们应该时刻牢记主导设计的概念,并理解技术如何在逐步成熟过程中围绕这一概念汇聚。
II. 主导设计之战
主导设计的概念最早由阿伯纳西-厄特巴克模型[i][ii]在 1975 年提出,尽管这一术语直到二十年后才被正式创造[iii]。这一概念已经变得如此基础,以至于它至今仍然是全国各地商学院 MBA 课程的教学内容。在其最基本的核心中,这一商业模型描述了产品的设计和制造过程如何通过三个不同的阶段随着时间的推移而发展:一个初步的流动阶段,其特点是在产品设计和过程改进中进行大量的实验,并且随着不同方法的开发和完善以满足市场需求,带来显著的创新;一个过渡阶段,在这个阶段,一个主导的产品设计开始出现,市场逐渐转向日益标准化的产品,但仍有显著的过程创新;以及一个特定阶段,其特点是产品和过程设计的标准化。
此后,这项工作得到了扩展,最著名的是由费尔南多·苏亚雷斯(Fernando Suarez)进行的扩展,旨在解释在产品进入市场之前,技术主导地位的动态,并为导航这一过程提供路线图。在他整合性的技术主导框架[iv]中,苏亚雷斯阐述了产品创新如何通过五个阶段发展,具体如下。
技术主导的五个阶段
-
研发积累: 当多家公司开始进行与新兴技术领域相关的应用研究时,研发积累阶段开始了。鉴于技术轨迹的多样性,这一阶段强调技术和技术人才。
-
技术可行性: 第一个工作原型的创建促使所有参与的公司评估当前的研究和其竞争定位(例如,继续独立追求、合作/组队、退出)。竞争动态强调企业层面的因素、技术优势,以及在适用的市场中,相关的法规。
-
创造市场: 第一款商业产品的发布向市场发出了明确的信号,并不可逆转地将重点从技术转向市场因素。在这一阶段,产品之间的技术差异变得越来越不重要,而企业在生态系统中的战略性操作变得尤为重要。
-
决定性战斗: 这一阶段的标志是多个竞争者中出现一个具有相当市场份额的早期领跑者。值得注意的是,网络效应(例如,围绕某个产品建立的生态系统)和环境中的转换成本开始产生更强的影响。此外,已安装的用户基础和互补资产的规模变得至关重要,因为主流市场的消费者更加看重可靠性和可信度,而非性能和新颖性,从而决定了最终的赢家。
-
主导地位后的阶段: 市场采纳某一替代性设计,并成为明确的主导技术,这一技术得到了庞大的用户基础支持。这为新进入者提供了天然的防线,特别是在拥有强大网络效应和转换成本的市场中。该阶段将持续,直到出现新的技术创新,取代当前技术,从而重新启动这一循环。
一家公司在成功度过这些阶段并实现技术主导地位的过程中,会受到多个公司层面因素(例如,技术优势、互补资产、已安装用户基础、战略操作)和环境因素(例如,行业监管、网络效应、转换成本、可获得性制度、市场特征)的影响。这些因素在不同阶段的重要性各不相同,过早或过晚的行动可能会产生削弱或意外的效果。也有研究探讨了如何通过对三个关键方面(即市场、技术、互补资产)进行连续决策,来帮助确定在争夺主导地位中的成功或失败[v[]](https://medium.com/@geoffrey.m.williams1/the-business-implications-of-dominant-design-on-the-trajectory-of-generative-ai-a1240bea6eb3#_edn1)。第一个决策与市场相关,涉及如何正确识别市场并推动采取行动以实现较大的已安装用户基础。第二个决策关系到市场标准是由政府还是市场推动,并包括对专有控制策略与开放策略之间的考虑。第三个也是最后一个决策,涉及如何制定策略来获取竞争所需的互补资产,从而在主流市场中保持竞争力。
另一个必须考虑的因素是技术路径依赖性以及先前结果(例如云计算大战、AI 芯片投资)对未来事件进程的影响。现代复杂技术通常在逐步提高的采纳回报机制中运作,也就是说,技术被采纳的越多,它变得越有利,且更加根深蒂固[vi]。在这种背景下,一些小的历史事件可能对哪种技术最终成为主导技术产生强烈影响,尽管竞争技术可能具备潜在优势。这是由多种强化机制所导致的,例如学习效应、协调效应和适应性预期,这些机制使得切换到其他技术变得成本高昂且复杂。此外,企业级生成性人工智能从研发到商业化产品、并具备业务和操作价值的过渡,与云基础设施的主导地位密切相关[vii]。这是由于需要一套共同的能力,配合大规模的计算资源。为了提供这样的能力,超大规模云服务商已经将云基础设施、模型和应用无缝集成到云 AI 技术栈中——加速了互补资产的创建。正是通过这些考量的视角,生成性人工智能的进展得以审视。
III. ChatGPT: 改变世界的一声枪响
2022 年 11 月,ChatGPT 的出现清楚地传递了一个信号:大规模语言模型(LLM)在广泛领域内具有实际的商业应用。几周内,“生成性人工智能”一词在不同世代的用户中,无论是技术人员还是非技术人员,都已广为人知。更为深刻的是,市场中其他参与者意识到,他们要么需要启动,要么需要大幅加速自己的努力,才能提供生成性人工智能能力。这标志着从阶段 2:技术可行性到阶段 3:市场创建的过渡。从那时起,竞赛正式开始。
在相当短的时间内,主要技术提供商开始发布自己的生成性 AI 平台和相关模型(例如,Meta AI — 2023 年 2 月,AWS Bedrock — 2023 年 4 月,Palantir 人工智能平台 — 2023 年 4 月,Google Vertex AI — 2023 年 6 月,IBM WatsonX.ai — 2023 年 7 月)。在技术和技术人才对于证明技术可行性至关重要的时期,这一需求已转变为战略性操作,因为公司们正在努力通过专注于建立现有用户基础、开发互补资产和生态系统以及增强网络效应来为增长做好布局。这导致了当前这一时期内,跨超大规模平台生态系统和关键 AI 提供商之间战略合作伙伴关系的迅速扩展,因为组织寻求建立将帮助它们应对主导地位决战的联盟。我们还看到,超大规模公司正利用其现有的云基础设施资产,推动其生成性 AI 资产在竞争极少甚至没有竞争的细分市场中快速通过监管障碍。
随着这一进程的发展,组织应当保持对各种风险的敏感性。首先,那些投资于未能成为主导设计的替代方法的 AI 公司,可能会发现自己处于不利地位。因此,适应或采纳主导设计可能需要在战略、开发和投资方面做出重大的调整,超出当前已投入的成本。此外,随着生成性 AI 市场潜力的逐渐显现,竞争将持续加剧,这将对所有市场参与者施加更大的压力,并最终导致市场整合和退出。最后,AI 的普及促使全球政府和机构开始更新监管框架,以促进安全性和 AI 的负责任部署。这带来了额外的不确定性,因为组织可能面临新的合规要求,这些要求可能会消耗大量资源来实施。在那些高度监管的市场中,如果生成性 AI 工具不符合基本要求,可能会成为提供这些工具的障碍。
然而,这一时期并非没有机会。随着市场开始识别出主导设计之战的领先者,能够迅速与主导设计对接或在这些框架内进行创新的组织,更有可能获得显著的市场份额。此外,即使市场开始围绕主导设计进行标准化,AI 领域仍然可能会出现新的细分市场。如果能够及早识别并加以利用,公司可以建立强大的市场存在感,并享有先行者优势。
IV. 技术主导地位的指标
随着当前主导设计的竞争持续进行,可以预期会出现多个指标,这些指标可以帮助预测哪些技术或公司可能确立市场领导地位,并为当前不断发展的生成性 AI 应用设定标准。
-
市场份额中的领导地位: 能够在市场中获得大量用户基础的 AI 公司和平台可能会占据领先地位。这可以通过其平台的广泛采用、增加的用户参与度、不断增长的销售数据或在特定市场中的客户来证明。市场份额的早期领先可能是潜在主导地位的重要指标。
-
生态系统的发展与扩展: 观察围绕不同生成性 AI 技术的生态系统,可以识别出强大且广泛的生态系统,这些生态系统包含可以增强生成性 AI 平台价值的互补技术。这些生态系统的强度在技术的采用和长期可行性中通常起着至关重要的作用。
-
切换成本: 从一个生成性 AI 平台转移到另一个平台的切换成本可能会阻止用户转向竞争技术,从而加强当前领导者的地位。这些成本可能包括数据整合问题、重新训练机器学习模型的需求,或合同和业务依赖性。
-
已安装基础的规模: 大规模的用户和解决方案基础能够改善网络效应,并提供一种临界质量,从而吸引更多用户,原因在于感知的可靠性、支持生态系统、互操作性和学习效应。这也激活了从众效应,吸引那些可能会避免采用技术的风险规避型用户[v]。
-
可靠性与可信度: 衡量市场对不同生成性 AI 技术的可靠性和可信度的看法。市场消费者通常更青睐可靠性和可信度,而非性能和新颖性。被认为可靠并因用户支持和稳定性收到积极反馈的品牌可能会获得竞争优势。
-
创新与改进: 公司在其生成性 AI 产品中的创新投资可能表明其市场主导地位。尽管市场可能倾向于已经建立的、可靠的技术,但持续的改进和对用户需求的适应将是保持竞争力的关键。
-
监管合规与伦理标准: 在符合日益严格的法规要求下,致力于开发符合伦理的人工智能的公司和组织可能会在市场上占据优势,尤其是在监管严格的行业中。这一点在联邦市场中尤为重要,因为网络认证和独特的安全要求在可以用于运营价值的技术中扮演着至关重要的角色。
通过监控这些指标,组织可以洞察哪些技术可能在决定性战斗阶段崭露头角,成为生成式人工智能领域的领军技术。在做出关于生成式人工智能技术的投资、开发或实施决策时,理解这些动态至关重要。
V. 结论
在生成式人工智能领域建立主导设计是市场稳定和行业标准化的重要一步,这将促使市场接受度的提高,并减少企业和消费者的市场不确定性。能够影响或适应新兴主导设计的公司将获得竞争优势,确立自己在新技术范式中的市场领导地位。然而,选择一个最终未能成为标准的产品生态系统将导致市场份额的下降,并对那些需要过渡到主导设计的公司造成转型成本。
随着行业从流动阶段向特定阶段过渡,向主导设计流动,且流动的黏度不断增加,战略前瞻性和敏捷性变得比以往任何时候都更加重要,尤其是当组织打算通过技术创造价值并实现影响力时。预测未来趋势并迅速适应不断变化的技术格局的必要性意味着,组织必须保持警觉和灵活,随时准备根据人工智能技术的新发展和消费者需求的变化调整战略。能够预见技术变革轨迹并积极应对的企业,不仅能够生存下来,还将成为数字化转型新纪元的先锋。而那些不能适应的企业,则会被历史的尘埃所埋没。
本文所表达的所有观点均为作者个人观点。
参考文献:
[i] J. Utterback, W. Abernathy, “过程与产品创新的动态模型”,《欧米伽》,第 3 卷,第 6 期,1975 年
[ii] W. Abernathy, J. Utterback, “创新模式”,《技术评论》,第 80 卷,第 7 期,1978 年
[iii] F. Suárez, J. Utterback, “主导设计与企业生存,” 战略管理杂志,第 16 卷,第 6 期,1995 年,415–430
[iv] F. Suárez, “技术主导权之争:一个整合框架,” 研究政策,第 33 卷,2004 年,271–286
[v] E. Fernández, S. Valle, “主导设计之争:决策模型,” 欧洲管理与商业经济研究,第 25 卷,第 2 期,2019 年,72–78
[vi] W.B. Arthur, “竞争技术、收益递增与历史事件带来的锁定效应,” 经济学杂志,第 99 卷,第 394 期,1989 年,116–131
[vii] F. van der Vlist, A. Helmond, F. Ferrari, “大规模人工智能:云基础设施依赖与人工智能的工业化,” 大数据与社会,2024 年 1-3 月期:I-16,第 1、2、5、6 页
数据背后的物理学
物理学原理如何为我们提供更深刻的数据洞察
·发表于Towards Data Science ·阅读时间:7 分钟·2024 年 5 月 18 日
--

每一条数据背后都是一个发生在时空中的物理事件(图片来源:Alina Grubnyak on Unsplash)
作为一名转行的数据科学家,我的同事和同伴们常常问我,我的物理背景与此有什么关联。
当然,从日常工作来看,除了与数学和统计技术有些交集之外,实际上没有太多共同点。然而,从哲学的角度来看,它们之间有着深刻的联系:
每个数据点对应的是在真实世界中发生的一个实际物理事件。
因此,从某种意义上来说,数据总是可以追溯到物理现象。
不幸的是,这种观点在标准的统计学或机器学习讨论中很少被提及。因此,我认为许多数据从业者都失去了对这一基础理解。在这篇文章中,我将更详细地描述这一以物理为中心的理解如何帮助我们更深入地洞察数据。
一切都是时间序列

一切发生在一个特定的地点和时间(图片来源:Veri Ivanova on Unsplash)
数据从哪里来?它们源自我们宇宙中的事件。每一个发生的事件,都发生在…
泊松自助法
在大数据集上的自助法
·发表于 Towards Data Science ·阅读时间 10 分钟·2024 年 8 月 12 日
--

自助法是一个有用的技术,可以基于收集到的样本推断总体的统计特征(比如均值、十分位数、置信区间等)。在大规模实现时,它可能很困难,尤其是在处理流数据的场景下,尤为复杂。在学习如何大规模应用自助法时,我遇到了一篇谷歌博客(几乎已有十年历史),介绍了泊松自助法。从那时起,我发现了一篇更早的论文,Hanley 和 MacGibbon(2006),阐述了这一技术的一个版本。本文的目的是确保我已经充分理解了这个逻辑,并能向别人解释清楚。我们将首先介绍经典的自助法,以激发对泊松自助法的兴趣。
经典自助法
假设我们想计算一所学校学生的平均年龄。我们可以反复抽取 100 个学生的样本,计算它们的均值并进行存储。然后,我们可以计算这些样本均值的最终均值。这个最终均值就是对总体均值的估计。
在实际操作中,通常无法从一个总体中多次抽样。这就是引入自助法(Bootstrapping)的原因。自助法在某种程度上模拟了这一过程。我们不是从总体中抽样,而是从我们收集的一个样本中进行有放回抽样。这些伪样本被称为重抽样(resamples)。

结果证明,这非常有效。它比闭式解法计算代价高,但不需要对总体分布做出强假设。而且,它比重复收集样本便宜。在实际应用中,它在工业界使用非常广泛,因为在许多情况下,要么闭式解法不存在,要么很难得出正确结果——例如,在推断总体的分位数时。
为什么自助法有效?
自助法给人的感觉是错的。或者至少当我第一次学习它时,我并不觉得它是对的。为什么一个样本能包含这么多信息呢?
从你抽取的原始样本中进行有放回的抽样,只是让你模拟从总体中抽样的一种方式。你抽取的原始样本,平均来看,像是你总体的一个代表。所以当你从中重新抽样时,本质上是在从同一个概率分布中抽取样本。
如果恰好抽到一个奇怪的样本呢?这种情况是可能发生的,这也是我们进行重采样的原因。重采样帮助我们了解样本本身的分布。如果原始样本太小怎么办?随着样本中观察值的增加,自助法估计会趋近于总体值。然而,有限样本无法保证结果的准确性。
尽管存在一些问题,但考虑到我们所处的限制环境,它仍然是我们对总体最好的信息。我们不需要假设总体具有特定的分布形态。鉴于计算成本相对较低,自助法成为了一种非常强大的工具。
演示经典的自助法
我们将通过两个例子来解释自助法。第一个是一个小数据集,目的是让你能够心算。第二个是一个更大的数据集,我会写下代码来说明。
示例 1:确定一所学校学生的平均年龄
我们的任务是确定一所学校学生的平均年龄。我们随机抽取了 5 名学生。这个想法是利用这 5 名学生来推断全校学生的平均年龄。这听起来很荒谬(而且在统计学上不规范),但请耐心听我解释。
ages = [12, 8, 10, 15, 9]
我们现在从这个列表中进行有放回的抽样。
sample_1 = [ 8, 10, 8, 15, 12]
sample_2 = [10, 10, 12, 8, 15]
sample_3 = [10, 12, 9, 9, 9]
....
do this a 1000 times
....
sample_1000 = [ 9, 12, 12, 15, 8]
对每个重采样,计算均值。
mean_sample_1 = 10.6
mean_sample_2 = 11
mean_sample_3 = 9.8
...
mean_sample_1000 = 11.2
取这些均值的平均值。
mean_over_samples=mean(mean_sample_1, mean_sample_2, .. , mean_sample_1000)
这个均值就成为了你对总体均值的估计。你可以对任何统计属性做同样的事情:置信区间、偏差等。
示例 2:确定“支付处理时间”的第 95 百分位数
食品配送应用上的客户进行支付。在支付成功后,订单会传送到餐厅。计算支付处理时间(即从客户点击“支付”按钮到反馈(支付成功或支付失败)交付的时间)是一个关键指标,反映了平台的可靠性。每天都有数百万客户在应用上进行支付。
我们的任务是估算分布的 95%百分位数,以便能够快速检测问题。

我们通过以下方式演示经典的自助法:
-
我们假设有一个包含一百万个观察值的总体。在现实世界中,我们从未观察到这样的数据。
-
我们随机抽取这部分总体的 1/10。也就是说,我们取 10,000 个观察值。实际上,这就是我们观察到的唯一数据。
-
然后我们应用上面讨论过的相同程序。我们从我们观察到的数据中进行带替换的重采样。我们这样做很多次。
-
每次我们重新采样时,我们计算该分布的 95th 百分位。
-
最后,我们取 95th 百分位值的均值,并计算其置信区间。
我们得到了下面的图表。神奇的是,我们发现刚刚生成的置信区间包含了真实的 95th 百分位(来自我们的总体)。

我们可以在自助统计量的层次上看到相同的数据。

生成上述内容的代码如下,自己试试看!
这一切都很顺利,接下来是什么呢?
现在我们已经确定经典的自助法实际上是有效的,我们将尝试从数学角度建立正在发生的事情。通常书籍会告诉你如果不感兴趣就跳过这一部分。但我鼓励你坚持下来,因为这里才是最有趣的地方。
休息一下
现在,让我们想象一个游戏。假设你有一个装满了 5 个球的袋子:一个红球、一个蓝球、一个黄球、一个绿球和一个紫球。你需要从袋子中抽取 5 个球,每次抽一个。每次抽球后,你会把它放回袋子里,然后再抽一次。所以每次选择一个球时,你有 5 个不同颜色的球可以选择。每一轮过后,你会在空白槽中记录所选择的球(如下所示)。

现在,如果我问你,每个球被选择进入我们需要填充的每个槽的概率是多少,你会怎么回答?
对于槽 1,
-
红球可以以 1/5 的概率被选择。
-
紫球可以以 1/5 的概率被选择。
-
绿球可以以 1/5 的概率被选择。
-
蓝球可以以 1/5 的概率被选择。
-
黄球可以以 1/5 的概率被选择。
同样的情况适用于槽 2、3、4 和 5。

让我们暂时专注于红球。它至少可以被选择 0 次(根本没有被选择)和最多被选择 5 次。红球发生的概率分布如下:
-
红球被选择 0 次:这只能以 1 种方式发生。
-
红球被选择 1 次:这可以以 5 种方式发生。红球被选择进入槽 1,红球被选择进入槽 2 ……(你懂的)。
-
红球被选择 2 次:这可以以 10 种方式发生。将红球固定在槽 1,有 4 种选择。将红球固定在槽 2,有 3 种新的选择,……
-
红球被选择 3 次:这可以有 10 种方式。这与上面的逻辑完全相同。
-
红球被选择 4 次:这可以有 5 种方式。类似于只被选择一次的情况。
-
红球被选择 5 次。这可以有 1 种方式。
这只是 5 选 k,其中 k = {0, 1, 2, 3, 4, 5}

让我们把这两个事实结合起来,专门讨论红球的情况

我们刚刚描述的是二项分布,其中 n = 5 且 p = 1/5

或者更一般地,

现在只需将球替换为观测值。

所以,当我们进行自助法时,本质上是从二项分布中抽取每个观测值。
在经典自助法中,当我们进行重采样时,每个观测值都遵循二项分布,其中 n = n,k = {0, …, n} 和 p = 1/n。 这也可以表示为 Binomial(n , 1/n)。
现在,二项分布有一个非常有趣的性质:当 n 越来越大,p 越来越小,二项分布会收敛到一个 Poisson 分布,且为 Poisson(n/p)。某一天我会写一篇直观的解释,为什么会发生这种情况,但如果你现在感兴趣,可以阅读 这篇写得非常好的文章。

这适用于任何 n 和 p,使得 n/p 是常数。在下面的 gif 中,我们展示了 lambda (n/p) = 5 的情况。

在我们的特殊情况下,由于 p 仅为 1/n,因此我们将收敛到 Poisson(1)。


由此可以得出,另一种重采样方法是从 Poisson(1) 分布中抽取每个观测值。
泊松自助法意味着我们使用 Poisson(1) 过程来生成用于自助统计的重采样。

有什么大不了的,这为什么有用?
自助法有两个阶段:第一个阶段是创建重采样,第二个阶段是在每个重采样上计算统计量。经典自助法和泊松自助法在第二个阶段是相同的,但在第一个阶段是不同的。
这在两种情况下是有用的:
-
泊松自助法减少了我们需要遍历数据的次数。
-
泊松自助法适用于我们没有固定 n 的情况。例如,当我们在流式处理数据时。
在重采样时减少遍历次数
泊松自助法在创建重采样时可以显著提高计算效率。查看代码是理解这一点的最佳方式。
比较上面第(8)行与经典自助法中相应的行:
# classical, needs to know (data)
bootstrap_samples = np.random.choice(data, (n_iter, n), replace=True)
# poisson, does not need to know (data)
weights = np.random.poisson(1, (n_iter, n))
在经典自助法中,你需要知道数据,而在泊松自助法中则不需要。
这对于数据非常庞大的情况(比如数亿条观测数据)具有非常重要的意义。这是因为从数学上讲,生成重抽样归结为为每个观测值生成计数。
-
在经典自助法中,每个观测值的计数遵循Binomial(n, 1/n)分布。它们共同遵循Multinomial(n, 1/n, 1/n, …, 1/n)分布。这意味着当你为一个观测值生成计数时,这会影响其他观测值的计数。例如,在我们的球的例子中,一旦你为一个红球生成了计数,这就会直接影响其他球的计数。例如,如果红球的计数是 2,那么我们知道剩下只有 3 个球可以选择。
-
在泊松自助法中,每个观测值的计数彼此独立。因此,如果你需要 1000 个重抽样,只需为红球生成 1000 个Poisson(1)抽样。然后就完成了,你可以继续处理下一个观测值。
当 n 未知时进行重抽样
有些情况下,n 实际上是未知的。例如,在流式支付数据或数据量大到跨多个存储实例的情况下。

在经典自助法中,每次观察到 n 的增加时,我们必须重新进行重抽样过程(因为我们是有放回地抽样)。这使得该方法在计算上非常昂贵且浪费。在泊松自助法中,我们只需为每个实例保存我们的Poisson(1)抽样。每次新增一个实例时,我们只需要为这个新实例生成Poisson(1)抽样。
结论

经典自助法(bootstrapping)是一种非常有效的技术,用于从收集到的样本中学习统计量的分布。在实际操作中,对于非常大的数据集,它可能会非常昂贵。泊松自助法(Poisson bootstrapping)是自助法的一种变体,能够高效地并行计算重抽样。这是由于两个原因:
-
在泊松自助法下的重抽样意味着我们只对每个观测值进行一次遍历。
-
在流数据的情况下,泊松自助法允许我们增量地处理新的重抽样,而不必一次性对整个数据集进行重抽样。
希望你觉得这有用。我始终欢迎反馈和修正。这篇文章中的图片是我自己的。欢迎自由使用!
地理空间智能和相似性分析在数据映射中的应用
在数据集成过程中,通过地理编码和字符串匹配战略性地增强地址映射
·发表于Towards Data Science ·4 分钟阅读·2024 年 2 月 17 日
--
大数据行业的许多人可能会遇到以下场景:在从不同系统中提取这两条记录时,缩写词“TIL”是否等同于“Today I learned”(今天我学到了)这个短语?当记录以不同的名称出现,但意思相同,您的程序也可能会感到困惑。当我们将来自不同操作系统的有差异的数据汇聚在一起时,数据采集过程可能比原先预想的更耗时!

图片来自:unsplash.com/photos/turned-on-canopy-lights-g_V2rt6iG7A
现在,您正在为一家食品供应链公司工作,该公司的客户来自餐饮行业。公司提供了来自不同操作系统的关于客户联系方式和餐厅详情的两份数据提取。您需要将它们链接在一起,以便前端仪表板团队能够从填充的数据中获得更多信息。不幸的是,这两个数据源没有唯一的主键来进行链接,只有一些地理信息和餐厅名称。本文将通过结合geopy和fuzzywuzzy,在手动映射的基础上增强您的地理映射解决方案。
使用pandas读取两个数据源:

作者提供的图片:custom_master.csv

作者提供的图片:client_profile.csv
基础数据清理和手动映射
在处理大型数据集时,需要考虑可能影响映射准确性的每一个因素。将基础数据清理和手动映射作为第一步,可以提高数据的一致性和对齐度,从而获得更准确的结果。
以下代码应适用于两个数据源。
1: 大小写 (例如 123 Main St 和 123 MAIN ST 应该映射为相同地址)
2: 不小心的空格和不必要的标点符号 (例如 123 Main St_whitespace_ 或 123 Main St; 应该映射为 123 Main St)
3: 邮政缩写的标准化 (例如 123 Main Street 应该映射为 123 Main St)
请考虑在实际应用中使用来自 美国邮政服务街道后缀缩写 的完整标准化邮政缩写映射表,以提高地理位置映射的一致性和准确性。
其他可能影响映射准确性的因素包括地址中的拼写错误 (例如 123 Mian St 和 123 Main St) 以及简化的地址 (例如 123 Forest Hill 和 123 Frst Hl),这些问题在使用手动映射方法时可能会很难处理,这时应该引入更高级的映射技术。
Geopy
Geopy 是一个开源的 Python 库,它通过地址地理编码将人类可读的地址转换为精确的地理坐标,在地理空间领域中发挥着重要作用。它使用大圆距离计算来准确地计算经纬度。在地理编码过程中,其他地理编码 API,如 Google Maps Geocoding API、OpenCage Geocoding API 和 Smarty API,也可以根据项目的具体业务需求进行考虑。
在地理编码过程完成后,我们可以使用 pandas 库通过 LATITUDE 和 LONGITUDE 列合并两个数据框,并检查成功映射的行数。无法映射的地址将转交到下一阶段进行处理。
Fuzzy Wuzzy
Fuzzywuzzy 是另一个 Python 库,旨在通过提供一组工具来进行模糊字符串匹配,比较和测量字符串之间的相似性。该库使用如 Levenshtein 距离等算法来量化字符串之间的相似度,特别适用于包含拼写错误或不一致的数据。每次地址比较时,都会生成一个信心得分,这是一个介于 0 和 100 之间的数值。得分越高表示字符串之间的相似度越强,而得分较低则表示相似度较低。在我们的案例中,我们可以使用 fuzzywuzzy 来处理剩余的无法通过 geopy 映射的行。

作者提供的图片:使用 fuzzywuzzy 展示对剩余未映射行的信心得分。
上面的示例仅使用了ADDRESS列进行字符串匹配,若在此过程中加入另一列常见的CLENT_NAME,可以在该业务场景中推动映射进程,从而带来更准确的输出。
结论
这种地址映射技术在各行各业中都具有广泛的适用性。手动映射、geopy和fuzzywuzzy的结合提供了一种全面的方法,能够提高地理映射的准确性,使其成为不同行业中面临数据摄取和集成挑战的企业宝贵的资产。
优化在设计涉及小样本实验中的力量
使用 Python 中的优化技术设计更精确实验的逐步指南
·发布于Towards Data Science ·10 分钟阅读·2024 年 10 月 21 日
--
实验是用科学严谨的方法检验假设的基础。在医学领域,实验用于评估新疗法对患者的效果;而在数字世界中,像Amazon、Netflix和Uber这样的科技巨头每年都会进行成千上万次的实验,以优化和改进他们的平台。
对于大规模实验,通常使用随机分配,并且被认为是“黄金标准”。在数据量充足的情况下,随机性往往能够生成可比较的组,其中重要的实验前因素被平衡,并且交换性假设得以成立。
然而,当实验的样本非常小的时候,随机分配通常无法创建统计上等效的组。因此,我们如何高效地在处理组和控制组之间分配单元呢?
你将学到的内容:
在这篇文章中,我将解释一种基于优化的方法,用于构建等效的实验组,这是 Bertsimas 等人在这篇文章中提出的。
通过 Python 中的一个简单示例,我们将学习如何:
-
设计一个基于优化的实验。
-
使用自助法技术进行推断。
-
在 Python 中实现代码,以便你自己的实验使用
优化的力量
在深入研究我们的例子和 Python 代码之前,首先讨论一下使用基于优化的方法来设计实验的好处。
优化使得统计结果更加精确,从而可以进行更强有力的推断。
基于优化的方法将实验组进行匹配,以最小化整体均值和方差的差异。这样可以使统计结果更加精确,将其紧密集中在名义值周围,同时仍然保持无偏的估计。这种精度的提高使得推断能力更强(我们稍后会介绍使用的自助法算法)。
这使得研究人员能够通过更少的数据得出统计上有效的结论,从而减少实验成本——这是肿瘤学研究等学科中的一个重要优势,因为在小鼠癌症模型中测试化疗药物既繁琐又昂贵。此外,与其他适用于小样本量的方法相比,优化方法已经证明在效果上优于其他方法,正如我们稍后通过模拟实验看到的那样。
现在,让我们深入一个例子,看看如何使用 Python 应用这种方法!
在 Python 中实现算法
案例研究:20 只小鼠的药物实验
假设我们有一个 20 只小鼠的实验,研究药物对肿瘤生长的影响,肿瘤大小初始值不同。假设初始肿瘤大小呈正态分布,均值为 200 mg,标准差为 300 mg(为了确保值为非负数而进行了截断)。我们可以通过以下 Python 代码生成小鼠种群:
import numpy as np
import pandas as pd
import scipy.stats as stats
def create_experiment_data(n_mice, mu, sigma, seed):
lower, upper = 0, np.inf
initial_weights = stats.truncnorm(
(lower - mu) / sigma,
(upper - mu) / sigma,
loc=mu,
scale=sigma
).rvs(size=n_mice, random_state=seed)
return pd.DataFrame({
'mice': list(range(1, n_mice+1)),
'initial_weight': initial_weights,
})
tumor_data = create_experiment_data(n_mice=20, mu=200, sigma=300, seed=123)
print(tumor_data.head())
> mice initial_weight
0 1 424.736888
1 2 174.691035
2 3 141.016478
3 4 327.518749
4 5 442.239789
现在,我们需要将 20 只啮齿动物分成两组,每组 10 只——一组将接受治疗,另一组将接受安慰剂。我们将通过优化来完成这一任务。
我们还假设肿瘤生长是在一天内观察的,并且遵循 Gompertz 模型(详见《癌症研究中的数学模型》)。假设治疗具有确定性效果,可以减少肿瘤大小 250 mg。
实验设计
我们的目标是将等效组的创建表述为一个优化问题,其中目标是最小化初始肿瘤重量的均值和方差差异。
为了实现这一目标,我们需要遵循三个步骤:
第 1 步:规范化初始肿瘤重量
首先,整个样本必须进行预处理,并且度量标准应该规范化,使其均值为零,方差为单位:
mean = tumor_data['initial_weight'].mean()
std = tumor_data['initial_weight'].std()
tumor_data['norm_initial_weight'] = (tumor_data['initial_weight'] - mean) / std
第 2 步:使用优化创建组
接下来,我们需要实现一个通用的优化模型,该模型构建具有k 个受试者的m 组,并最小化任意两组之间的最大差异(有关模型变量的完整描述,请参见文章),并传递归一化度量:

创建每个含有 k 个单位的 m 组的优化模型(来自Bertsimas et al. 2015)。
该数学模型可以使用 Python 中的 ortools 库和 SCIP 求解器实现,具体如下:
from ortools.linear_solver import pywraplp
from typing import Union
class SingleFeatureOptimizationModel:
"""
Implements the discrete optimization model proposed by Bertsimas et al. (2015) in "The Power of
Optimization Over Randomization in Designing Experiments Involving Small Samples".
See: https://doi.org/10.1287/opre.2015.1361.
"""
def __init__(self, data: pd.DataFrame, n_groups: int, units_per_group: int, metric: str, unit: str):
self.data = data.reset_index(drop=True)
self.parameters = {
'rho': 0.5,
'groups': range(n_groups),
'units': range(len(self.data)),
'unit_list': self.data[unit].tolist(),
'metric_list': self.data[metric].tolist(),
'units_per_group': units_per_group,
}
self._create_solver()
self._add_vars()
self._add_constraints()
self._set_objective()
def _create_solver(self):
self.model = pywraplp.Solver.CreateSolver("SCIP")
if self.model is None:
raise Exception("Failed to create SCIP solver")
def _add_vars(self):
self.d = self.model.NumVar(0, self.model.infinity(), "d")
self.x = {}
for i in self.parameters['units']:
for p in self.parameters['groups']:
self.x[i, p] = self.model.IntVar(0, 1, "")
def _set_objective(self):
self.model.Minimize(self.d)
def _add_constraints(self):
self._add_constraints_d_bounding()
self._add_constraint_group_size()
self._add_constraint_all_units_assigned()
def _add_constraints_d_bounding(self):
rho = self.parameters['rho']
for p in self.parameters['groups']:
for q in self.parameters['groups']:
if p < q:
self.model.Add(self.d >= self._mu(p) - self._mu(q) + rho * self._var(p) - rho * self._var(q))
self.model.Add(self.d >= self._mu(p) - self._mu(q) + rho * self._var(q) - rho * self._var(p))
self.model.Add(self.d >= self._mu(q) - self._mu(p) + rho * self._var(p) - rho * self._var(q))
self.model.Add(self.d >= self._mu(q) - self._mu(p) + rho * self._var(q) - rho * self._var(p))
def _add_constraint_group_size(self):
for p in self.parameters['groups']:
self.model.Add(
self.model.Sum([
self.x[i,p] for i in self.parameters['units']
]) == self.parameters['units_per_group']
)
def _add_constraint_all_units_assigned(self):
for i in self.parameters['units']:
self.model.Add(
self.model.Sum([
self.x[i,p] for p in self.parameters['groups']
]) == 1
)
def _add_contraint_symmetry(self):
for i in self.parameters['units']:
for p in self.parameters['units']:
if i < p:
self.model.Add(
self.x[i,p] == 0
)
def _mu(self, p):
mu = self.model.Sum([
(self.x[i,p] * self.parameters['metric_list'][i]) / self.parameters['units_per_group']
for i in self.parameters['units']
])
return mu
def _var(self, p):
var = self.model.Sum([
(self.x[i,p]*(self.parameters['metric_list'][i])**2) / self.parameters['units_per_group']
for i in self.parameters['units']
])
return var
def optimize(
self,
max_run_time: int = 60,
max_solution_gap: float = 0.05,
max_solutions: Union[int, None] = None,
num_threads: int = -1,
verbose: bool = False
):
"""
Runs the optimization model.
Args:
max_run_time: int
Maximum run time in minutes.
max_solution_gap: float
Maximum gap with the LP relaxation solution.
max_solutions: int
Maximum number of solutions until stop.
num_threads: int
Number of threads to use in solver.
verbose: bool
Whether to set the solver output.
Returns: str
The status of the solution.
"""
self.model.SetTimeLimit(max_run_time * 60 * 1000)
self.model.SetNumThreads(num_threads)
if verbose:
self.model.EnableOutput()
self.model.SetSolverSpecificParametersAsString(f"limits/gap = {max_solution_gap}")
self.model.SetSolverSpecificParametersAsString(f"limits/time = {max_run_time * 60}")
if max_solutions:
self.model.SetSolverSpecificParametersAsString(f"limits/solutions = {max_solutions}")
status = self.model.Solve()
if verbose:
if status == pywraplp.Solver.OPTIMAL:
print("Optimal Solution Found.")
elif status == pywraplp.Solver.FEASIBLE:
print("Feasible Solution Found.")
else:
print("Problem infeasible or unbounded.")
self._extract_solution()
return status
def _extract_solution(self):
tol = 0.01
self.assignment = {}
for i in self.parameters['units']:
for p in self.parameters['groups']:
if self.x[i,p].solution_value() > tol:
self.assignment.setdefault(p, []).append(self.parameters['unit_list'][i])
def get_groups_list(self):
return list(self.assignment.values())
model = SingleFeatureOptimizationModel(
data = tumor_data,
n_groups = 2,
units_per_group = 10,
unit = 'mice',
metric = 'norm_initial_weight',
)
status = model.optimize()
optimized_groups = model.get_groups_list()
print(f"The optimized mice groups are: {optimized_groups}")
> The optimized mice groups are: [
[1, 4, 5, 6, 8, 12, 14, 16, 17, 18],
[2, 3, 7, 9, 10, 11, 13, 15, 19, 20]
]
注意: 参数 rho 控制最小化一阶矩和二阶矩之间差异的折衷,由研究人员选择。在我们的示例中,我们假设 rho 等于 0.5。
Paso 3:随机化分配哪组接受哪种治疗
最后,我们随机决定哪一组实验小鼠将接受药物,哪一组将接受安慰剂:
import random
def random_shuffle_groups(group_list, seed):
random.seed(seed)
random.shuffle(group_list)
return group_list
randomized_groups = random_shuffle_groups(optimized_groups, seed=123)
treatment_labels = ["Placebo", "Treatment"]
treatment_dict = {treatment_labels[i]: randomized_groups[i] for i in range(len(randomized_groups))}
print(f"The treatment assignment is: {treatment_dict}")
> The treatment assignment is: {
'Placebo': [2, 3, 7, 9, 10, 11, 13, 15, 19, 20],
'Treatment': [1, 4, 5, 6, 8, 12, 14, 16, 17, 18]
}
让我们通过分析两组初始肿瘤权重的均值和方差来查看结果的质量:
mice_assignment_dict = {inx: gp for gp, indices in treatment_dict.items() for inx in indices}
tumor_data['treatment'] = tumor_data['mice'].map(mice_assignment_dict)
print(tumor_data.groupby('treatment').agg(
avg_initial_weight = ('initial_weight', 'mean'),
std_initial_weight = ('initial_weight', 'std'),
).round(2))
> avg_initial_weight std_initial_weight
treatment
Placebo 302.79 202.54
Treatment 303.61 162.12
这是预实验肿瘤权重均值和方差最小的组分配!现在让我们进行实验并分析结果。
使用自助法进行推断
模拟肿瘤生长
根据已确定的治疗分配,假设治疗组的效果为-250 毫克,肿瘤生长在一天内使用 Gompertz 模型进行模拟:
import numpy as np
from scipy.integrate import odeint
# Gompertz model parameters
a = 1
b = 5
# Critical weight
wc = 400
def gompex_growth(w, t):
"""
Gomp-ex differential equation model based on the initial weight.
"""
growth_rate = a + max(0, b * np.log(wc / w))
return w * growth_rate
def simulate_growth(w_initial, t_span):
"""
Simulate the tumor growth using the Gomp-ex model.
"""
return odeint(gompex_growth, w_initial, t_span).flatten()
def simulate_tumor_growth(data: pd.DataFrame, initial_weight: str, treatment_col: str, treatment_value: str, treatment_effect: float):
"""
Simulate the tumor growth experiment and return the dataset.
"""
t_span = np.linspace(0, 1, 2)
final_weights = np.array([simulate_growth(w, t_span)[-1] for w in data[initial_weight]])
experiment_data = data.copy()
mask_treatment = data[treatment_col] == treatment_value
experiment_data['final_weight'] = np.where(mask_treatment, final_weights + treatment_effect, final_weights)
return experiment_data.round(2)
experiment_data = simulate_tumor_growth(
data = tumor_data,
initial_weight = 'initial_weight',
treatment_col = 'treatment',
treatment_value = 'Treatment',
treatment_effect = -250
)
print(experiment_data.head())
> mice initial_weight norm_initial_weight treatment final_weight
0 1 424.74 0.68 Treatment 904.55
1 2 174.69 -0.72 Placebo 783.65
2 3 141.02 -0.91 Placebo 754.56
3 4 327.52 0.14 Treatment 696.60
4 5 442.24 0.78 Treatment 952.13
mask_tr = experiment_data.group == 'Treatment'
mean_tr = experiment_data[mask_tr]['final_weight'].mean()
mean_co = experiment_data[~mask_tr]['final_weight'].mean()
print(f"Mean difference between treatment and control: {round(mean_tr - mean_co)} mg")
> Mean difference between treatment and control: -260 mg
现在我们已经得到了最终的肿瘤权重,我们观察到治疗组的平均最终肿瘤重量比对照组低 260 毫克。然而,为了确定这个差异是否具有统计学意义,我们需要应用以下自助法机制来计算 p 值。
基于优化设计的自助法推断
“在基于优化的设计中,诸如组间平均差异之类的统计数据变得更加精确,但不再遵循常规分布。因此,应使用自助法推断方法来得出有效结论。”
Bertsimas et al. (2015) 提出的自助法推断方法包括使用带放回的抽样构建估计量的基准分布。在每次迭代中,使用优化进行组分配,最后得出p 值,具体如下:
from tqdm import tqdm
from typing import Any, List
def inference(data: pd.DataFrame, unit: str, outcome: str, treatment: str, treatment_value: Any = 1, n_bootstrap: int = 1000) -> pd.DataFrame:
"""
Estimates the p-value using bootstrap for two groups.
Parameters
-----------
data (pd.DataFrame): The experimental dataset with the observed outcome.
unit (str): The experimental unit column.
outcome (str): The outcome metric column.
treatment (str): The treatment column.
treatment_value (Any): The value referencing the treatment (other will be considered as control).
n_bootstrap (int): The number of random draws with replacement to use.
Returns
-----------
pd.DataFrame: The dataset with the results.
Raise
------------
ValueException: if there are more than two treatment values.
"""
responses = data[outcome].values
mask_tr = (data[treatment] == treatment_value).values
delta_obs = _compute_delta(responses[mask_tr], responses[~mask_tr])
deltas_B = _run_bootstrap(data, unit, outcome, n_bootstrap)
pvalue = _compute_pvalue(delta_obs, deltas_B)
output_data = pd.DataFrame({
'delta': [delta_obs],
'pvalue': [pvalue],
'n_bootstrap': [n_bootstrap],
'avg_delta_bootstrap': [np.round(np.mean(deltas_B), 2)],
'std_delta_bootstrap': [np.round(np.std(deltas_B), 2)]
})
return output_data
def _run_bootstrap(data: pd.DataFrame, unit: str, outcome: str, B: int = 1000) -> List[float]:
"""
Runs the bootstrap method and returns the bootstrapped deltas.
Parameters
-----------
data (pd.DataFrame): The dataframe from which sample with replacement.
outcome (str): The outcome metric observed in the experiment.
B (int): The number of random draws with replacement to perfrom.
Returns
-----------
List[float]: The list of bootstrap deltas.
"""
deltas_bootstrap = []
for i in tqdm(range(B), desc="Bootstrap Progress"):
sample_b = _random_draw_with_replacement(data, unit)
responses_b, mask_tr_b = _optimal_treatment_control_split(sample_b, unit, outcome, seed=i)
delta_b = _compute_delta(responses_b[mask_tr_b], responses_b[~mask_tr_b])
deltas_bootstrap.append(delta_b)
return deltas_bootstrap
def _compute_delta(response_tr, responses_co):
delta = np.mean(response_tr) - np.mean(responses_co)
return delta
def _compute_pvalue(obs_delta, bootstrap_deltas):
count_extreme = sum(1 for delta_b in bootstrap_deltas if abs(delta_b) >= abs(obs_delta))
p_value = (1 + count_extreme) / (1 + len(bootstrap_deltas))
return p_value
def _random_draw_with_replacement(data: pd.DataFrame, unit: str):
sample = data.sample(frac=1, replace=True)
sample[unit] = range(1, len(sample) + 1)
return sample
def _optimal_treatment_control_split(data: pd.DataFrame, unit: str, outcome: str, seed: int):
result = _sample(
data = data,
unit = unit,
normalized_feature = 'norm_initial_weight',
seed = seed
)
treatment_dict = {inx: gp for gp, indices in result.items() for inx in indices}
treatment = data[unit].map(treatment_dict)
mask_tr = (treatment == 'Treatment').values
responses = data[outcome].values
return responses, mask_tr
def _sample(data: pd.DataFrame, unit: str, normalized_feature: str, seed: int):
model = SingleFeatureOptimizationModel(
data,
n_groups = 2,
units_per_group = 10,
unit = unit,
metric = normalized_feature,
)
status = model.optimize()
optimized_groups = model.get_groups_list()
randomized_groups = random_shuffle_groups(optimized_groups, seed=seed)
treatment_labels = ["Placebo", "Treatment"]
return {treatment_labels[i]: randomized_groups[i] for i in range(len(randomized_groups))}
infer_result = inference(
data = experiment_data,
unit = 'mice',
outcome = 'final_weight',
treatment = 'group',
treatment_value = 'Treatment',
n_bootstrap = 1000
)
print(infer_result)
> delta pvalue n_bootstrap avg_delta_bootstrap std_delta_bootstrap
0 -260.183 0.001998 1000 2.02 112.61
组间观察到的-260 毫克的差异在 5%的显著性水平下是显著的(p 值小于 0.05)。因此,我们拒绝零假设(均值相等),并得出结论:治疗具有统计学显著效应。
1000 只小鼠实验的结果
我们可以多次模拟实验,生成具有不同初始肿瘤重量的小鼠群体,这些小鼠来自相同的正态分布,均值为 200 mg,标准差为 300 mg。
这使我们能够将基于优化的设计与其他实验设计进行比较。在下图中,我将优化方法与简单随机分配和分层随机分配进行了比较(其中,分层是使用基于初始肿瘤重量的 k-means 算法创建的):

1000 次模拟实验的结果,用于检测-250 mg 的效应(图片来源:作者)。
在他们的文章中,作者还将基于优化的方法与重新随机化和配对匹配在不同效应大小和组大小下进行了比较。如果你有兴趣深入探索细节,我强烈推荐阅读完整的文章!
恭喜!你已经阅读完毕🎉如果你觉得这篇文章有趣,可以考虑关注我。我经常分享关于优化和因果推断的想法。
Pandas 图表的威力:后端
从 Pandas 中轻松创建交互式图形
·发布于Towards Data Science ·5 分钟阅读·2024 年 8 月 30 日
--

图片由Lukas Blazek提供,来源于Unsplash
索引
-
上下文
-
后端
2.1. Matplotlib
2.2. Plotly
2.3. Hvplot
-
讨论
-
离开之前的一句话
1. 上下文
Python 有许多可视化包,其中三个最著名的是:Matplotlib(以及 seaborn)、Plotly 和 Hvplot。这三种包各有其优点,但都需要付出学习成本,甚至有时相当可观。
本文的灵感来源于我发现的《Pandas 方法思维导图》,该导图由Daily Dose of Data science newsletter提供(这是我强烈推荐的一个通讯)。当时,我正好在探索 Hvplot 可视化包。我认为,像 Hvplot 这样轻松切换不同可视化后端的想法非常棒(这里有一个示例,展示如何从 Hvplot 切换到 Plotly)。看到我们也可以用 pandas 实现这一点,我觉得这个想法太有趣,不能不分享出来。
Pandas 是 Python 数据科学的核心,我们都知道如何使用它。但集成到 Pandas 中的 Matplotlib 已经显得有些老旧,并且在易用性和展示效果上被其他包超越。Pandas 可视化后端的强大之处在于,它让你能够利用最新的可视化包进行数据探索和结果呈现,而无需花时间学习这些包,尽管这些包非常强大!
2. 后端
Pandas 是基于两个包构建的,Numpy 和 Matplotlib。这也解释了为什么我们使用 Matplotlib 脚本来生成图表,因此生成的图表是 Matplotlib 图表。
自从 Pandas 创建以来,它不断发展,并提供了用户修改 Pandas 使用的可视化后端的可能性。
我在研究过程中找到的 6 个可用的后端是:
-
Plotnine(ggplot2)
-
Plotly
-
Altair
-
Holoviews
-
Hvplot
-
Pandas_bokeh
-
Matplotlib(默认后端)
有几种方法可以更改后端:
- 2 个全局方法:
pd.set_option("plotting.backend", '<name of backend>')
# OR
pd.options.plotting.backend = '<name of backend>'
- 1 个本地方法:
df.plot(backend='<name of backend>', x='...')
注意:更改后端需要 Pandas >= 0.25,有时还需要一些特定的依赖项,例如下面的 Hvplot。
这里有 2 个示例:
- 使用 Plotly:
import pandas as pd # Basic packages
pd.options.plotting.backend = "plotly"
df = pd.DataFrame(dict(a=[1,3,2], b=[3,2,1]))
fig = df.plot()
fig.show()
- 使用 Hvplot:
import numpy as np
import pandas as pd # Basic packages
import hvplot
import hvplot.pandas # ! Specific dependency to install
pd.options.plotting.backend = 'hvplot' # Backend modification
data = np.random.normal(size=[50, 2])
df = pd.DataFrame(data, columns=['x', 'y'])
df.plot(kind='scatter', x='x', y='y') # Plotting
2.1. Matplotlib
Matplotlib 是 Pandas 的默认可视化后端。换句话说,如果不指定后端,将使用 Matplotlib。它是一个高效的包,可以快速可视化数据以进行探索或提取结果,但它已经老化,并且在易用性和渲染能力上被其他包赶超。
Matplotlib 的优势在于,由于 Pandas 从创建之初就基于 Matplotlib,因此 Matplotlib 与 Pandas 的集成非常完美,所有 Matplotlib 函数都可以在 Pandas 中使用。
提醒一下,这里是集成到 Pandas 中的 11 个 Matplotlib 显示方法:
-
“area” 用于区域图,
-
“bar” 用于垂直条形图,
-
“barh” 用于水平条形图,
-
“box” 用于箱形图,
-
“hexbin” 用于 hexbin 图,
-
“hist” 用于直方图,
-
“kde” 用于核密度估计图,
-
“density” 是 “kde” 的别名,
-
“line” 用于折线图,
-
“pie” 用于饼图,
-
“scatter” 用于散点图。
2.2. Plotly
Plotly 是由 Plotly 公司开发的可视化包。该公司开发了框架 Plotly.js,用于在 Python 中进行交互式数据可视化。Plotly 公司还提供了 Python 仪表板包 Dash。
要在 Pandas 中使用 Plotly,只需导入 Plotly express 并更改后端:
import pandas as pd
import plotly.express as px # Import packages
df = pd.read_csv("iris.csv")
# Modifying locally Pandas backend
df.plot.scatter(backend = "plotly", x = "sepal.length", y = "sepal.width")
Pandas 返回一个与 Plotly 相同类型的对象:
df.plot.scatter(backend = "plotly", x = "sepal.length", y = "sepal.width")
# → <class 'plotly.graph_objs._figure.Figure'>
px.scatter(x=df["sepal.length"], y = df["sepal.width"])
# → <class 'plotly.graph_objs._figure.Figure'>
其优势在于你可以将 Pandas 中创建的图形直接集成到 Plotly 宇宙中,尤其是 Dash!
一个限制是,Plotly 与 Pandas 的集成尚不完美,详细信息请参见 Plotly 网站(Plotly 网站详情)。
2.3. Hvplot
Hvplot 是一个基于 bokeh 的交互式可视化包。
这是一个令人兴奋的包,我是前些时候发现的,至今仍令我着迷,既因为 Hvplot 集成了类似于 Pandas 的后端概念,也因为 Holoviz 套件 以及相关包如 Panel 用于创建动态客户端网站。
即使没有 Pandas 后端的概念,Hvplot 也不需要过多的学习就能开始使用,只需将 Pandas 的 .plot() 替换为 .hvplot():
import pandas as pd
import hvplot
df = pd.read_csv("iris.csv")
# Plot with Pandas
df.plot.scatter(backend = "hvplot", x = "sepal.length", y = "sepal.width")
# Same plot with hvplot
df.hvplot.scatter(backend = "hvplot", x = "sepal.length", y = "sepal.width")
使用 Hvplot 后端的方式与使用 Plotly 后端相同,只需要导入 Hvplot 包的依赖项:
import numpy as np
import pandas as pd # Basic packages
import hvplot
import hvplot.pandas # Specific dependency to install
pd.options.plotting.backend = 'hvplot' # Backend modification
data = np.random.normal(size=[50, 2])
df = pd.DataFrame(data, columns=['x', 'y'])
df.plot(kind='scatter', x='x', y='y') # Plotting
与 Plotly 类似,通过 hvplot 后端从 Pandas 生成的图表是 Hvplot 类型的:
df.plot.scatter(backend = "hvplot", x = "sepal.length", y = "sepal.width")
# → <class 'holoviews.element.chart.Curve'>
df.hvplot.scatter(backend = "hvplot", x = "sepal.length", y = "sepal.width")
# → <class 'holoviews.element.chart.Curve'>
Hvplot 是强大的 Holoviz 套件的一部分,套件中还有许多其他相关工具,可以推动数据分析的深度,例如 Panel、geoviews、datashader 等等。这种一致性使得我们可以从 pandas 创建图表,同时仍然能够利用 Holoviz 套件的优势。
3. 结论
Pandas 后端是一个非常高效的解决方案,可以发现并利用最新的 Python 可视化包,而无需投入太多时间:只需 18 个字符(包括空格),就可以将一个标准的 matplotlib 图表本地转化为交互式的 Plotly 图表,从而享受这种可视化类型的所有好处。
然而,这种解决方案也有一定的局限性:它不适用于那些需要大量定制化的高级可视化目标,比如数据新闻中的高级可视化,因为 Pandas 中的包集成尚不完善。此外,这个解决方案仅涵盖了基于 Pandas 构建的可视化包,排除了像 D3.js 这样的其他可视化解决方案。
Hvplot 目前是我最喜欢的可视化包:它一开始非常容易上手,能与所有主要的数据处理包(如 Polars、Dask、Xray 等)兼容,并且是一个持续应用的部分,可以让你从图表扩展到动态的全客户端网站。
4. 离开前的一句话
在我的研究过程中,我没有找到预期的那么多文档。我认为这个概念很棒,所以我本以为会有很多相关文章。如果你觉得这个解决方案真的有用,或者只是一个看起来很酷但没有实际用途的东西,欢迎在评论中告诉我。
感谢阅读!
全栈数据科学的可预测崛起
那些我们只关注“构建模型”的日子已经过去
·发布于Towards Data Science ·阅读时长:6 分钟·2024 年 6 月 4 日
--

图片由Rodion Kutsaiev提供,来源于Unsplash
成为数据科学家,意味着越来越多地充当多面手。
在过去,知道一些pandas函数,并按正确的顺序调用model.fit()和model.predict()可能就足够了。但这个世界正在不断进步。
到 2024 年,数据科学家参与整个机器学习项目生命周期的情况已经不罕见,从项目规划到模型部署和 UI 设计。
潮人们称这种新趋势为“全栈数据科学”,在本文中,我将探讨其起源,并分析这对我们这些从事数据工作的人可能带来的影响。
目录
-
什么是全栈数据科学?
-
为什么它会成为一种趋势?
-
我的准备/应对方式
-
我并不是在说
什么是全栈数据科学?提示:它不仅仅是伪装成 MLOps 的东西
“全栈数据科学”这个术语在 2022 年底开始变得非常流行。当时,它通常通过所需的技能来定义:项目管理、建模、MLOps、数据讲故事……
黄金的代价:奥运成功是否只属于富人?🥇
分析 30 年来奥运奖牌分布与国家财富指标
·发表于Towards Data Science ·13 分钟阅读·2024 年 9 月 7 日
--

图片由作者使用 GPT-4 生成
很久以前,我自己也是一名运动员——小时候我曾做过一些韵律体操。在 2004 年雅典奥运会上,我记得自己坐在电视机前,惊叹地观看了无数个小时,观看每一场韵律体操表演,俄罗斯的两名运动员分别获得了金牌和银牌。
至今,我依然非常喜欢观看各种体操表演——我确实很享受观看西蒙·拜尔斯在巴黎 2024 奥运会上赢得一大堆奖牌的每一秒。然而,曾经没有引起我注意,但现在却让我意识到的是,富裕和强大的国家在此类体育赛事中赢得大量奖牌是多么常见。相反,当一个来自经济不太发达国家的运动员登上领奖台时,这真是令人惊讶。比如阿尔及利亚的伊曼·凯利夫在拳击比赛中,或者巴基斯坦的阿尔沙德·纳迪姆在标枪比赛中,他们都在自己项目中以相当意外的表现赢得了金牌。
获得奥运奖牌就仅仅需要出生在一个富裕强大的国家吗?答案肯定是否定的——它确实需要大量的努力、奉献、痛苦、牺牲和……
机器学习/人工智能中的学习证明
在进行任何数学推导之前,我们必须首先理解学习的基础,并且了解它如何与误差的概念紧密相关。
·发布于 Towards Data Science ·9 分钟阅读·2024 年 5 月 17 日
--
假设的厨师
想象一下,在某一天,你决定复制一份你在一家著名餐馆吃过的美味。你完美地记得这道菜的味道。基于此,你在网上搜索食谱并尝试在家中重现它。
假设你在餐馆品尝的美味的味道记作 T,它代表了你期望的味道,即你的 目标。根据你在网上找到的食谱,你希望实现这个目标,也就是味道 T。
为了复制这个食谱,你按照所有指示的步骤,使用所有必要的食材、温度、烹饪时间等。我们将所有这些方法和食材记作 X。
完成整个过程后,你品尝这道菜。此时,你判断它是否与预期的味道 T 相似。你注意到它比预期的味道更咸或更甜。你在家中重现的美味味道记作 Y。
因此,当你意识到味道与目标 T 不同时,你会根据味道 Y 赋予它与目标味道的差异量度。换句话说,你可能添加了更多盐或更少盐,更多调料或更少调料。
T 和 Y 之间的差异可以定义为误差 E。T 和 Y 之间的区别由你味蕾的记忆产生。因此,味蕾在此时执行特定功能,我们可以将其定义为 P(Y) = E。换句话说,当你体验到味道 Y 时,味蕾根据目标味道 T 来分配误差 E。
拥有这个定量的误差E衡量标准后,我们可以每天复制这个食谱,这样随着时间的推移,误差E会逐渐减小。换句话说,目标味道T与味道Y之间的距离会减小,直到T = Y。
基于这个假设场景,我们可以将错误定义为与观察到的现实不一致的判断,其中总是存在一个执行判断的函数。因此,在上述情况下,味觉和记忆创造了这个判断功能。
在这个特定的情况下,学习的行为的特点是减少错误的能力。换句话说,它是以不同的方式与再现的对象互动,以减少判断功能的输出。
厨师的专业知识
回到假设的情况,我们有食材和方法X,如食谱所示。所有的食材和设备都与餐厅使用的相同;因此,结果完全取决于你正确操控它们的能力,以实现目标味道T。
换句话说,你操控X来获得Y。因此,我们可以定义你本质上是一个将X转化为Y的函数,表示为f(X) = Y。
表示操控食材行为的函数f(X),也依赖于你的大脑如何运作。换句话说,如果你有过烹饪经验,你会发现将X转化为Y更容易。
让我们定义W为你神经元的权重,或者你操控X的神经能力。如果W已经基于烹饪经验进行了预调整,那么将X转化为Y会更容易。否则,我们将需要调整W,直到能够将X转化为Y。
因此,我们知道f(X) = Y也依赖于W,即我们可以将其线性表示为f(X) = WX。
因此,我们的目标是发现如何修改W,直到生成的Y非常接近或等于T。换句话说,我们如何调整W,直到误差E显著减少或变为零。
成本函数
评估结果与预期结果之间差异的函数是成本函数。将食材和烹饪方法转化为美食的函数是我们的模型,这可以是人工神经网络或其他机器学习模型。

来源:作者。公式(1)
在公式(1)中,成本函数E的定义依赖于n个权重w。换句话说,这是一个基于w的值指示误差的函数。在一个特定的情况下,当所有n个权重w都没有调整时,误差E的值将很大。相反,在权重适当调整的情况下,误差E的值将很小或为零。

来源:作者。公式(2)
因此,我们的目标是找到n个权重w的值,使上述条件成立。
梯度
为了便于理解我们将如何进行,接下来我们将定义以下函数:

来源:作者。图像(1)
因此,我们直观地知道,当x = 0和y = 0时,f(x, y) = 0。然而,我们希望找到一种算法,在给定随机的x和y值的情况下,调整x和y的值,直到函数f(x, y)等于零。
为了实现这一点,我们可以使用函数的梯度。在向量微积分中,梯度是一个向量,表示通过从指定点位移,可以获得某一量值的最大可能增量的方向和大小。

来源:作者。公式(3)
也就是说,通过对函数f(x, y)应用梯度,我们得到一个向量,如公式(3)所示,它指示了如何增加x和y的值,以便使f(x, y)的值增长。然而,我们的目标是找到使f(x, y) = 0的x和y的值。因此,我们可以使用负梯度。
以下是函数f(x, y)在二维空间中的表示,颜色显示了z的值。使用负梯度,我们可以看到指向函数最小值的向量。

来源:作者。图像(2)
基于此,我们可以开发一种方法,使用函数f(x, y)的梯度场来更新x和y的值,以找到使f(x, y) = 0的必要值。
学习的证明
我们将定义一个简单的函数f(x)进行算法测试。我们的目标是找到这个函数的最小值。为此,我们可以应用f(x)的梯度。

来源:作者。公式(4)
上面是函数f(x)的梯度。我们在本文中不会深入定义导数的概念,但我们建议阅读它的定义以及为什么我们可以这样表示它。
知道h趋向于零,我们可以如下表示f(x)的梯度:

来源:作者。公式(5)
基于此,我们可以用以下项替代h:

来源:作者。公式(6)
我们定义元素alpha来保持h项的必要性,其中alpha必须严格为正,并且始终趋向于零,和h项相同。将这一新的关系代入导数定义中,我们得到:

来源:作者。公式(7)
现在我们有了一个对我们证明有价值的关系。我们知道任何元素的平方都会是正数。基于这一概念,我们需要用负 alpha 倍的 f(x) 的梯度来替换 h。
所以:

来源:作者。公式(8)
因此,我们可以判断,当 alpha 始终为正值时,条件(8)成立。

来源:作者。公式(9)
也就是说,f(x) 的值减去一个严格正的值将总是小于 f(x) 的原始值。因此,我们可以使用公式(7)和(9)替换为以下关系:

来源:作者。公式(10)
因此,我们已经证明了如何更新 x 的值,以确保函数 f(x) 至少小于其先前的值的关系。

来源:作者。公式(11)
因此,我们知道如何减少当前的 x 以满足不等式(11):

来源:作者。公式(12)
为了确认这个关系的有效性,我们可以将这种方法应用于图像(1)中的函数 f(x, y),我们已知该函数的行为。因此:

来源:作者。公式(13)
将此算法多次应用于函数 f(x, y),我们预期看到函数值下降,直到达到最小值。为此,我们进行了模拟,并在其中对更新后的 x 和 y 赋值施加了噪声,以便可视化 f(x, y) 的值的下降。

来源:作者。图像(3)
请注意,当 alpha 的值趋近于零时,我们观察到 x 和 y 的值趋向于函数的最小值。当这一点不成立时,例如当 alpha = 0.6 时,我们会发现找到 f(x, y) 的最小值有一定的难度。
梯度下降法
该算法被称为“梯度下降”或“最速下降法”,它是一种优化方法,用于寻找函数的最小值,每一步都朝着负梯度的方向进行。该方法并不保证找到函数的全局最小值,而是找到局部最小值。
关于找到全局最小值的讨论可以在另一篇文章中展开,但在这里,我们已经从数学上证明了如何利用梯度来实现这一目的。
现在,将其应用于依赖于 n 个权重 w 的代价函数 E,我们得到:

来源:作者。公式(14)
要基于梯度下降法更新所有元素 W,我们有:

来源:作者。公式(15)
对于向量 W 的任何 n 第 n 个元素 𝑤, 我们有:

来源:作者。公式(16)
因此,我们有了我们的理论学习算法。从逻辑上讲,这并不是应用于假设中的厨师的思想,而是应用于我们今天所知道的众多机器学习算法。
结论
基于我们所看到的内容,我们可以得出结论,理论学习算法的演示和数学证明。这样的结构被应用于许多学习方法,如 AdaGrad、Adam 和随机梯度下降(SGD)。
这种方法不能保证找到使成本函数的结果为零或接近零的n-权重值w,但它能保证找到成本函数的局部最小值。
为了解决局部最小值的问题,有几种更为稳健的方法,例如 SGD 和 Adam,这些方法在深度学习中常被使用。
然而,理解基于梯度下降的理论学习算法的结构和数学证明,将有助于理解更复杂的算法。
参考文献
Carreira-Perpinan, M. A., & Hinton, G. E. (2005). 关于对比散度学习。在 R. G. Cowell & Z. Ghahramani(编辑),人工智能与统计学,2005。(第 33–41 页)。佛罗里达州劳德代尔堡:人工智能与统计学学会。
García Cabello, J. 数学神经网络。Axioms 2022, 11, 80。
Geoffrey E. Hinton, Simon Osindero, Yee-Whye Teh. A Fast Learning Algorithm for Deep Belief Nets. Neural Computation 18, 1527–1554. 麻省理工学院
LeCun, Y., Bottou, L., & Haffner, P. (1998). 基于梯度的学习应用于文档识别。IEEE 学报,86(11),2278–2324。
清晰性的追求:可解释的神经网络是伦理人工智能的未来吗?
机械可解释性能否克服事后解释的局限性?
·发表于Towards Data Science ·阅读时间:10 分钟·2024 年 4 月 23 日
--

图片由作者通过Midjourney生成
开发符合伦理标准的人工智能(AI)系统面临重要挑战。尽管有许多构建可信 AI 的指南,但它们往往只提供宽泛的、高层次的指引,难以具体应用并验证合规性。
透明度和解释 AI 决策的能力至关重要,特别是随着 AI 应用在各个行业的普及。近期的研究进展提高了我们理解和预测 AI 行为的能力,这是实现其伦理采用和更广泛接受的关键一步。
为什么这很重要?
现代 AI 模型,尤其是深度学习中的模型,通常被称为“黑箱”,因为它们复杂的算法即使对于开发者而言也很难理解。这种缺乏透明度与需要解释和验证决策的领域中的问责需求相冲突。此外,像欧盟的《通用数据保护条例》(GDPR)等法律现在要求自动化系统提供更高的透明度,法律要求个人…
生产质量图谱 RAG 的探索:容易开始,难以完成
克服生产化图谱 RAG 的挑战
·发表于Towards Data Science ·阅读时长 15 分钟·2024 年 10 月 31 日
--

知识图谱的冒险:知识图谱中有价值的东西。由 Brian Godsey 使用 DALL-E 生成。
当我阅读了 VentureBeat 最近的一篇文章,关于 Glean刚刚在最新一轮融资中获得超过 2.6 亿美元,我有两个立刻的直觉。首先,看到这个非常公开的图谱 RAG 案例能够充分发挥其作为一种强大而有价值的技术的潜力,这让我感到非常满足,因为它比以往任何时候都更高效地将人们与知识连接。其次,看到以下内容虽然令我感到惊讶,但也让我感到验证:
世界上最大的共享出行公司之一亲身体验了其好处。在将一整个工程师团队投入开发类似的内部解决方案后,他们最终决定转向 Glean 的平台。
“在一个月内,他们在 Glean 平台上的使用量翻了一倍,因为结果显而易见,”Glean 的首席营销官 Matt Kixmoeller 说道。
尽管我对新闻文章中提到的失败感到惊讶,但根据我的经验,以及同事和客户的经历,我认为图谱 RAG 在生产环境中遇到困难是可以预见的。我并不是说我预期大科技公司会在构建自己的图谱 RAG 系统时失败。我只是预期大多数人会在构建和生产化图谱 RAG 的过程中遇到困难——即使他们已经有了非常成功的概念验证。
我曾在The New Stack 上对 VentureBeat 文章做过一篇高水平的回应,在这篇文章中,我想更深入探讨为什么图 RAG 如此难以做到完美。首先,我会指出,借助最新工具,使用图 RAG 变得非常容易。接下来,我将深入探讨图 RAG 的一些具体挑战,这些挑战使得它从研发转向生产变得异常困难。最后,我会分享一些关于如何最大化成功机会的图 RAG 技巧。
开始使用图 RAG 很简单
那么,如果一个大型共享出行公司都无法有效构建自己的平台,为什么我会说实现图 RAG 是件容易的事呢?

知识图谱的冒险:道路是明确的。由 Brian Godsey 使用 DALL-E 生成。
好吧,首先,支持 RAG 和图 RAG 的技术在过去一年中取得了很大的进展。十二个月前,大多数企业甚至没有听说过检索增强生成(RAG)。现在,不仅 RAG 支持已经成为像 LangChain 这样的最佳 AI 构建工具的关键特性,而且几乎每一个 AI 领域的主要参与者都有 RAG 教程,甚至还有一门 Coursera 课程。尝试 RAG 的快速入门方式不胜枚举。
微软可能不是最早做图 RAG 的公司,但他们通过今年早些时候的研究博客文章大力推动了这一概念,并且他们仍在继续研究相关技术。
在 Medium 上,也有一篇不错的概念性介绍,带有一些技术细节,由 Google 的生成型 AI 工程师撰写。此外,在 Towards Data Science 上,最近有一篇非常详尽的关于如何构建图 RAG 系统的教程文章,并在一个科学出版物数据集上进行测试。
在传统图数据库和分析领域中拥有稳固声誉的 Neo4j,为了应对近期的生成型人工智能革命,向其旗舰图数据库产品添加了向量能力,并且他们拥有一套出色的工具平台,适用于那些除了标准图 RAG 能力外,还需要复杂图分析和深度图算法的项目。他们还有一份Graph RAG 入门指南。
另一方面,你甚至不需要图数据库来做图 RAG。许多刚接触图 RAG 的人认为他们需要部署一个专门的图数据库,但其实并不需要,事实上这样做可能会让你的技术栈变得更复杂。
我的雇主 DataStax 也有一份图 RAG 指南。
当然,两个最流行的生成 AI 应用组合框架,LangChain 和 LlamaIndex,各自都有自己的图形 RAG 介绍。而且还有一篇 DataCamp 文章 使用了这两者。
尽管有许多工具和教程可供使用,开始使用图形 RAG 是容易的部分……
…但是将图形 RAG 应用到生产环境中是困难的
这是数据科学中的一个非常古老的故事:一种新的软件方法论、技术或工具在研究背景中解决了一个棘手的问题,但在工业界很难将其构建成能够每天提供价值的产品。这不仅仅是软件开发中努力和熟练的问题——即使是最大的、最好的、最聪明的团队,也可能无法克服解决实际问题时,现实世界数据的不确定性、不可预测性和不可控性。

知识图谱中的冒险:难度等级 9。由 Brian Godsey 使用 DALL-E 生成。
不确定性是构建和使用以数据为中心的系统的固有部分,这些系统几乎总是具有某些随机性、概率或无界输入。而且,当输入和输出是非结构化的(如大语言模型和其他生成 AI 应用的自然语言输入和输出)时,不确定性可能会更大。
想要尝试图形 RAG 的人通常已经拥有一个在简单用例中表现良好的现有 RAG 应用,但在一些更复杂的用例和需要跨知识库获取多个信息的提示中失败,可能涉及不同的文档、上下文、格式,甚至数据存储。当回答问题所需的所有信息都在知识库中,但 RAG 系统找不到时,似乎就失败了。从用户体验(UX)的角度来看,确实是失败——没有给出正确的答案。
但这并不一定意味着 RAG 系统存在“问题”,它可能正如设计时所预期的那样工作。如果没有问题或错误,但我们仍然没有得到我们想要的响应,那就意味着我们期望 RAG 系统具备它根本不具备的能力。
在我们探讨为什么图形 RAG 很难投入生产之前,先来看一下我们试图解决的问题。
图形 RAG 所解决的主要挑战
由于普通的 RAG 系统(没有知识图谱)仅基于向量搜索来检索文档,因此只能检索与查询最语义相似的文档。那些完全不相似——或者相似度不够高的文档——将会被遗漏,并且通常不会提供给生成响应的 LLM。
当我们需要的文档与提示中的问题不完全语义相似时,一个或多个文档往往会被 RAG 系统遗漏。这种情况通常发生在回答问题时需要混合使用通用文档和专业化文档或术语时,或者当文档内容非常详细,某些与特定提示相关的关键细节被埋藏在不太相关的其他细节中时。请参阅这篇文章,了解 RAG 系统遗漏文档的示例,因为两个相关概念(此处是“太空针塔”和“下皇后安区”)在语义上不相似;请参阅这篇文章,了解重要细节在详细密集的文档中被掩埋的示例,因为向量嵌入是“有损的”。
当我们看到检索“未能”找到正确的文档时,我们可能会倾向于尝试改进向量搜索,使其更适合我们的使用场景。但这将需要调整嵌入,而嵌入是复杂的、杂乱的、计算成本高昂的,甚至微调的成本更高。而且,这甚至不是解决问题的最佳方式。
例如,看看上面链接的示例,我们真的希望使用一种将“太空针塔”和“下皇后安区”这两个词语在语义向量空间中放得很近的嵌入算法吗?不,微调或找到一种将这两个术语放得非常接近的嵌入算法,可能会产生一些意外且不希望出现的副作用。
最好不要强迫语义模型去做本可以由地理或旅游信息更好完成的任务。如果我是一家旅游公司,依赖于知道这些地标位于哪个社区,我宁愿建立一个能明确知道这些信息的数据库——这比让语义向量搜索完成相同任务更容易……而且没有完全的确定性。
所以,这里的主要问题是我们知道某些概念和信息在某种程度上是相关的,但它们在语义向量空间中并不相似。另一个(非向量的)信息来源告诉我们,在我们处理的各种概念之间存在某种联系。构建图谱 RAG 应用的任务是有效地将这些概念之间的联系捕捉到知识图谱中,并利用图谱中的连接来检索更多相关的文档,以便回答提示中的问题。

知识图谱探险:小心脚步。由 Brian Godsey 使用 DALL-E 生成。
总结我们尝试解决的图谱 RAG 问题:在我的非结构化文档中,存在许多概念之间的半结构化、非语义信息连接——我希望利用这些连接信息来补充语义向量搜索,从而检索出最适合回答提示和问题的文档。我们只是想让检索变得更好,我们希望通过使用一些外部信息或外部逻辑来实现这一点,而不是仅仅依赖语义向量搜索来将提示与文档连接起来。
集成图谱与 RAG 的指导原则
考虑到上述动机——使用“外部”信息来建立语义搜索遗漏的文档连接——在构建和测试图谱 RAG 应用时,我们可以牢记一些指导原则:
-
图谱应包含高质量、有意义的概念和连接
-
概念和连接应与用例集中的提示相关
-
图谱连接应补充而非替代向量搜索
-
应优先考虑一跳和两跳的图谱连接;依赖超过三步的连接应仅限于专门的用例。
也许在未来的文章中,我们将深入探讨遵循这些原则的细微差别和潜在影响,但目前,我仅仅提到,这个列表旨在共同提高可解释性、防止过度复杂化,并最大化构建和使用图谱 RAG 系统的效率。
遵循这些原则,以及软件工程和数据科学中的其他核心原则,可以增加成功构建一个有用且强大的图谱 RAG 应用的机会,但过程中肯定会有陷阱,我们将在下一节中概述这些陷阱。


知识图谱探险:战略游戏。由 Brian Godsey 使用 DALL-E 生成。
你的图谱 RAG 应用可能无法投入生产的原因
任何在围绕数据、复杂算法、统计学和人类用户构建软件方面花费了大量时间的人,大概都理解在构建像图谱 RAG 这样的系统时存在大量不确定性。在数据准备和加载过程中、构建知识图谱时、查询和遍历图谱时、结果编译和提示构建时,几乎在工作流的任何其他点,都可能会发生意外的事情。
上面我们讨论了如何轻松实现图谱 RAG 以获得初步结果,但要获得良好的结果,更不用说生产级别的结果,可能会很难。接下来,我们将看看在构建和测试图谱 RAG 应用时可能遇到的一些潜在问题。

知识图谱的冒险:未开发的原始价值。由 Brian Godsey 使用 DALL-E 生成。
图谱 RAG 的表现不比普通 RAG 好多少
如果你的图谱 RAG 系统的表现与普通 RAG 差不多,可能有很多原因。一般来说,这意味着图谱没有为系统增加价值,但这可能是因为知识图谱质量较低、图谱未被充分利用、参数设置不理想等多种原因。或者,根本没有问题;向量搜索可能已经非常有效地找到了正确的文档,而图谱根本不需要。
需要关注的内容:
-
你是否有普通 RAG 处理不好,但你期望图谱 RAG 能够成功处理的示例提示?你能否在这些提示上“调试”并查看背后的处理过程?
-
知识图谱是否包含了语义搜索可能无法识别的有意义的连接?你能找到在图谱中连接的概念对,它们相关的文档在向量空间中相隔很远的例子吗?知识图谱应该在“远距离”文档之间建立有意义的连接。
你(仍然)看到了幻觉
如果你在图谱 RAG 中看到了幻觉,而在普通 RAG 中没有遇到这种情况,我会怀疑在某个地方有 bug 或不当的参数设置。如果你看到的幻觉水平相似,那可能是一个超出了图谱方面的普遍问题。
需要关注的内容:
-
你的文档集是否包含了正确的响应,以应对那些引发幻觉的提示?向量搜索是否能够找到这些文档?
-
从检索的文档中提取的正确响应是否被恰当地插入到传递给 LLM 的提示上下文中?
图谱“太大”
当你的知识图谱“太大”或太密集时,可能会发生两种主要问题。首先,可能会出现扩展性问题,我将在下面讨论。其次,图谱遍历可能会导致返回“太多”文档,这些文档必须重新排序和过滤。如果重新排序和过滤策略与检索和图谱遍历元素不兼容,可能会导致图谱刚刚发现的文档就被立即过滤掉。
需要关注的内容:
-
在图谱遍历后返回了多少文档,多少文档被重新排名或过滤掉了?看起来通过强连接找到的文档是否能够成功存活过过滤?
-
你是否构建了一个充满有意义连接、适合你使用案例的知识图谱?在图谱中,你能找到很多对于你的使用案例来说过于通用或不相关的概念或连接吗?你的知识图谱中有多少是由低质量的信息构成的?
图谱“太小”
如上所述,如果图“太大”,可能充满了低质量的连接。而如果图“太小”,我希望其中的连接是有意义的,这很好,但缺失的连接有两种主要类型。第一种是由于图构建过程中的 bug 引起的。第二种是由于图构建没有针对特定情况设计。不同上下文或不同格式的数据可能会被不同的图构建方法处理。
需要关注的内容:
-
你是否使用 LLM(大语言模型)进行实体/关键词提取来构建你的知识图谱?你是否捕获了每个文档中的所有有意义的实体,还是 LLM 限制了它的输出?
-
在你的文档中,有哪些概念和连接是你期望出现在知识图谱中的,但似乎缺失了?你期望它们何时以及如何被添加到图中?为什么它们实际上没有被添加到图中?
你无法找到“中庸之道”图
你是否觉得自己可以构建一个“太大”或“太小”的图,但无法构建一个适中的图?
需要关注的内容:
-
你在从小图到大图或反之转换时,正在改变哪些参数或方法?这些应该对图的质量产生这么大的影响吗?你能否研究一些图元素,这些元素会根据你使用的图构建设置意外地出现或消失?
-
也请查看上面“大”和“小”部分中的相关提示。
你的实现需要新的软件或增加的部署复杂性
这是一个经典的数据科学问题:构建非常酷的前沿方法,但开发团队却拒绝或挣扎于将你笔记本中的代码带入生产堆栈。坚持使用最流行、最受支持且大多为开源的工具可以更容易地将代码投入生产,特别是当你的组织在其他地方已经在使用这些工具时。
需要关注的内容:
-
你的实现是否需要为图创建一个新的数据存储?你可能不需要图数据库,也许可以使用你的生产向量存储来处理图。
-
你是否在构建 AI 应用时使用了像 LangChain 这样的一些流行开源工具?这些工具可以减少代码复杂度,使应用更具可移植性,并扩展潜在的集成和进一步开发。
你的实现没有扩展性
文章通过消除边缘来扩展知识图谱来自The New Stack,展示了一种使图 RAG 具有很强扩展性的方法。正如前面提到的,最流行、最受支持且大多为开源的工具通常是实现无痛扩展的最佳途径,但这并不总是容易的。
需要关注的内容:
-
哪个部分没有扩展性?是图遍历、重新排序、结果汇总,还是其他什么问题?请参阅上面的“图太大”部分,获取更多建议。
-
你是否有某个特定组件的扩展性不好?有时候,使用像‘networkx’这样的内存图形库,甚至是图数据库,来执行复杂的图形操作,可能会导致资源瓶颈。你可能想要切换到一个更具扩展性的图形操作选项。
-
你是否在使用并行 API 调用来处理大多数繁重的工作,还是试图在主应用逻辑中进行复杂或成本高昂的计算?
在生产环境中实现图形 RAG 的成功
创建成功的图形 RAG 系统的关键在于构建一个能够与语义向量检索相辅相成的知识图谱和遍历逻辑,而不是取而代之或与之竞争。图谱设计应旨在在适当的时间连接正确的节点、知识、实体和文档,从而实现合适文档的组合,生成最有帮助且可操作的查询响应。

知识图谱的冒险:知识就是财富。由 Brian Godsey 使用 DALL-E 生成。
就 Glean 而言,需要注意的是,内部文档数据集是图形 RAG 的完美用例。知识图谱可以连接人员、项目、产品、客户、会议、地点等——这些在数量上受到组织规模和所从事工作类型的限制。构建和管理一个包含数千名员工的图谱,比起例如试图在维基百科或大型金融或法律文档数据库中处理所有提到的人来说,容易得多。因此,Glean 做出的第一个伟大决定可能就是找到一个适合图形 RAG 的伟大用例来解决问题。
图形 RAG 系统的一个常被低估的方面是输入数据的质量和可靠性,以及将数据传输到那里的数据管道。这更多与数据工程和传统软件开发相关,而非 AI。在以前的技术范式中,由于数据类型和访问方式的不兼容,连接不同的数据系统是一个挑战。如今,AI 和 LLM 使得集成来自不同来源的非结构化数据成为可能,从而将来自各个来源的数据整合到一个单一的 RAG 系统中。这种集成能力使得 LLM 能够处理和理解来自不同来源的非结构化数据,比如内部网页、维基、代码仓库、数据库、Google Docs 和聊天记录。仅仅将所有这些信息连接起来,并通过单一接口提供访问,可能就是一个巨大的突破。
前进的道路
构建适用于任何用例的图形 RAG 系统涉及利用基础组件,如向量和图形的数据存储、嵌入和 LLMs,并通过 LangChain 和 LlamaIndex 等开源编排工具增强功能。这些工具促进了强大、可扩展且高效的系统的开发,预示着一个未来,在这个未来中,公司通过自动化和简化流程优化知识工作,从而实现巨大的成功。
知识图谱和图形 RAG 系统的公共成功,特别是像 Glean 这样的公司,展示了这些技术在内部用例中的有效性,通过提高组织效率创造了价值。然而,面向外部、企业和消费者产品的更广泛应用潜力仍然 largely 未被挖掘,为其他公司探索提供了许多机会。
值得注意的是,我们至少已经处于所谓的“信息时代”超过 30 年,而直到过去一两年,我们才真正开始将这些信息从不同来源、不同思想、不同文档和不同概念之间连接起来,从而使我们的软件系统能够进行与人类在日常知识工作中使用的推理、逻辑和判断相同类型的思考。有些人称之为“智能时代”。
虽然最初专注于简单直接的决策,AI 的发展轨迹是朝着管理更复杂的场景前进,显著提高时间和成本效率。这一激动人心的进展使得许多 AI 应用——包括图形 RAG——成为转变知识在各种背景下如何互联和利用的关键。
如果现在开始使用图形 RAG,或者想了解更多,看看 DataStax 关于图形 RAG 的指南。
由 Brian Godsey 博士提供 (LinkedIn) — 数学家、数据科学家和工程师 // AI 和 ML 产品在 DataStax // 编写了《像数据科学家一样思考》一书 Think Like a Data Scientist
在数据科学或其他任何领域,永远不会过时的建议。
towardsdatascience.com ## 向量嵌入是有损的。以下是解决方法。
AI 系统并不完美(呜哦!),这些是其中一些原因。
towardsdatascience.com 
知识图谱的冒险:我几乎能触及它。由 Brian Godsey 使用 DALL-E 生成。

知识图谱的冒险:没有图谱的黄金。由 Brian Godsey 使用 DALL-E 生成。
扩散模型的崛起——生成深度学习的新纪元
🚀Sascha 的论文俱乐部
《去噪扩散概率模型》由 J. Ho 等人撰写
·发表于Towards Data Science ·13 分钟阅读·2024 年 3 月 27 日
--

图像来源于Sascha Kirch的出版物
这篇文章是关于一篇论文的,这篇论文开启了计算机视觉及许多其他领域生成深度学习的新纪元:扩散模型的时代。论文标题为“去噪扩散概率模型”,并介绍了一个新的框架,称为 DDPM,即论文标题的缩写。
尽管扩散模型的基本概念看起来很直观,但背后的数学却并不如此,你可能会发现自己很难理解关于这个话题的论文。至少我当时是这样。与此同时,今天许多生成模型,如DALL-E3、Imagen、SORA和Stable Diffusion 3都是基于扩散模型构建的。因此,理解基本概念非常重要。
🚀 所以,系好安全带吧,因为今天我们将建立一个关于扩散模型基本概念的扎实直觉。我们将把 DDPM 放入更广泛的背景中,并剖析论文中的方程式、表格和插图,添加一些额外的注释,揭示它们的真正含义。
论文: 去噪扩散概率模型,作者:
Jonathan Ho 等人,2020 年 6 月 19 日
《帕拉斯的崛起:通过自定义内核释放 TPU 的潜力》
加速 AI/ML 模型训练与自定义操作符 — 第三部分
·发布在Towards Data Science ·阅读时间 15 分钟·2024 年 10 月 6 日
--

图片由Hendrik Morkel提供,发布在Unsplash
这是关于构建自定义操作符以优化 AI/ML 工作负载的系列文章的第三部分。在我们上一篇文章中,我们展示了 Triton 的简易性和可访问性。Triton 得名于海神特里同,它使得 Python 开发者能够增加对 GPU 的控制,并优化其在特定工作负载中的使用。在本文中,我们将继续深入希腊神话的脉络,讲述特里同的女儿帕拉斯,并讨论与她同名的JAX 扩展,该扩展用于为 GPU 和 TPU 编写自定义内核。
NVIDIA GPU 的最重要特性之一——也是它们崛起为主流的关键因素——就是它们的可编程性。GPU 提供的一个关键组件是用于创建通用 GPU(GPGPU)操作符的框架,例如CUDA和Triton。
在之前的文章中(例如,这里),我们讨论了在 Google TPU 上运行 ML 工作负载的机会,并指出在价格性能和训练成本上可能有显著的提高。我们当时注意到的一个缺点是缺乏创建自定义操作符的工具。因此,某些需要特定操作符的模型,如果这些操作符在底层 ML 框架(例如 TensorFlow/XLA)中不受支持或实现不理想,相比于 GPU,在 TPU 上的性能会较差。随着近年来越来越多的新型快速计算解决方案在 GPU 上用于计算注意力机制,这种发展差距尤其显著。得益于 GPU 内核开发框架,这些新方案显著提高了transformer 模型的效率。
另一方面,在 TPU 上,缺乏合适的工具阻碍了这一创新,transformer 模型只能使用官方软件堆栈支持的注意力机制。幸运的是,随着Pallas的问世,这一差距得到了弥补。Pallas 作为JAX的扩展,并为PyTorch/XLA提供专门支持,使得为 GPU 和 TPU 创建自定义内核成为可能。对于 GPU 支持,Pallas 利用了 Triton,而对于 TPU 支持,它使用了一个名为 Mosaic 的库。尽管我们将重点讨论 TPU 的自定义内核,但值得注意的是,在 JAX 开发中,使用 Pallas 进行 GPU 内核定制比 Triton 有一些优势(例如,见这里)。
本文的目的是引起大家对 Pallas 的关注并展示其潜力。请不要将本文视为官方Pallas 文档的替代品。我们将分享的示例仅用于演示目的,未对其进行优化,也没有验证其稳定性、持久性或准确性。
重要的是,在撰写本文时,Pallas 仍然是一个实验性特性,仍在积极开发中。我们分享的示例(基于JAX版本 0.4.32 和PyTorch版本 2.4.1)可能在您阅读时已经过时。务必使用最新的 API 和资源进行 Pallas 开发。
非常感谢Yitzhak Levi为本篇文章的贡献。
环境设置
对于下面描述的实验,我们使用以下环境设置命令:
# create TPU node
gcloud alpha compute tpus queued-resources create v5litepod-1-resource \
--node-id v5litepod \
--project <project-id> \
--zone us-central1-a \
--accelerator-type v5litepod-1 \
--runtime-version v2-alpha-tpuv5-lite \
--valid-until-duration 1d \
--service-account <service-account> \
# check TPU node status (wait for state to be ACTIVE)
gcloud alpha compute tpus queued-resources describe v5litepod-1-resource \
--project <project-id> \
--zone us-central1-a
# SSH to TPU node
gcloud alpha compute tpus tpu-vm ssh v5litepod \
--project <project-id> \
--zone us-central1-a
# install dependencies
pip install torch_xla[tpu] \
-f https://storage.googleapis.com/libtpu-releases/index.html
pip install torch_xla[pallas]
pip install timm
# run tests
python train.py
#exit ssh
exit
# delete TPU node
gcloud alpha compute tpus queued-resources delete v5litepod-1-resource \
--project <project-id> \
--zone us-central1-a --force --quiet
TPU 的 Pallas 内核
在我们系列文章的第一个帖子中的玩具示例中,我们区分了自定义内核开发可能提升性能的两种方式。第一种是通过将多个操作(融合)在一起,从而减少以下开销:1)加载多个单独的内核,2)读取和写入中间值(例如,参见PyTorch 关于乘加融合的教程)。第二种是通过细致地应用底层加速器的资源,以优化当前函数。我们简要讨论了这两种机会,并指出了 Pallas 支持的局限性。
TPU 上的操作符融合
TPU 是一个XLA(加速线性代数)设备,即它运行由XLA 编译器生成的代码。当在像JAX或PyTorch/XLA这样的框架中训练 AI 模型时,训练步骤首先会转化为中间图表示(IR)。然后,将该计算图传递给 XLA 编译器,后者将其转换为可以在 TPU 上运行的机器代码。与急切执行模式不同,在急切执行模式中操作是单独执行的,而这种模型运行方式使得 XLA 能够在编译过程中识别并实现操作符融合的机会。事实上,操作符融合是 XLA 编译器最重要的优化。自然地,没有任何编译器是完美的,我们一定会通过自定义内核发现更多的融合机会。但一般来说,我们可以预期,通过这种方式提升运行时性能的机会要比急切执行模式下少。
优化 TPU 利用率
创建适用于 TPU 的优化内核需要对TPU 系统架构有全面而深入的理解。重要的是,TPU 与GPU有很大不同:GPU 和 CUDA 方面的专业知识不能直接转移到 TPU 开发中。例如,虽然 GPU 包含大量处理器,并且依靠其强大的并行计算能力来提升性能,但 TPU 主要是顺序执行,专门为运行高度向量化的操作而设计的引擎,并且支持异步调度和内存加载。
GPU 和 TPU 底层架构的差异可能对自定义内核的设计产生重大影响。掌握 TPU 内核开发需要:1) 通过流水线适当重叠内存和计算操作,2) 知道如何在标量、向量(VPU)和矩阵(MXU)计算单元及其相关的标量和向量寄存器(SREG 和 VREG)以及内存缓存(SMEM 和 VMEM)之间进行混合,3) 理解不同低级操作的成本,4) 适当的兆核配置(针对支持的 TPU 代),5) 掌握不同类型的TPU 拓扑结构及其对如何支持分布式计算的影响,等等。
框架限制
虽然使用 JAX 函数和 API 在Python中创建自定义操作符的能力大大提高了 Pallas 内核开发的简易性和可访问性,但它也限制了表达能力。此外,(截至本文撰写时)有些 JAX API 在 TPU 上不被 Pallas 支持(例如,请参见此处)。因此,您可能会以实现特定操作的目的接触 Pallas,结果却发现框架不支持您所需要的 API。这与 CUDA 等框架形成了对比,后者在开发自定义内核时提供了更多的灵活性(针对 GPU)。
Pallas 文档中的矩阵乘法教程为 Pallas 内核开发提供了极好的介绍,突出了操作符融合和自定义的潜力,同时也揭示了优化性能所面临的挑战(例如,适当调优输入的块大小)。教程清楚地表明,最大化 TPU 的完整潜力需要一定程度的专业化。然而,正如我们所展示的那样,即使是初学者的机器学习开发者也能从 Pallas 内核中受益。
集成现有 Pallas 内核的使用
要从自定义的 Pallas 内核中获益,您不一定需要知道如何构建它们。在我们的第一个示例中,我们展示了如何利用来自专用公共仓库的现有 Pallas 内核。
示例 — 在 Torch/XLA 中的闪存注意力
JAX 的 GitHub 仓库包括多个 Pallas 内核的实现,其中包括 闪光注意力。在这里,我们将演示其在 Torch/XLA 视觉变换器(ViT)模型中的使用。尽管 Pallas 内核是用 JAX 开发的,但它们可以被引入 Torch/XLA,例如通过 make_kernel_from_pallas 工具(有关详细信息,请参见 文档)。在 闪光注意力 的情况下,Torch/XLA 实现了该功能。
在下面的代码模块中,我们定义了一个简化版的经典 timm 注意力模块,并在构造函数中提供了定义底层注意力操作符的选项。我们将利用这个选项来比较 闪光注意力 Pallas 内核与其他替代方案的性能。
# general imports
import os, time, functools
# torch imports
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
import torch_xla.core.xla_model as xm
# custom kernel import
from torch_xla.experimental.custom_kernel import flash_attention
# timm imports
from timm.layers import Mlp
from timm.models.vision_transformer import VisionTransformer
class TPUAttentionBlock(nn.Module):
def __init__(
self,
dim: int = 768,
num_heads: int = 12,
attn_fn = None,
**kwargs
) -> None:
super().__init__()
self.attn_fn = attn_fn
self.num_heads = num_heads
self.head_dim = dim // num_heads
self.norm1 = nn.LayerNorm(dim)
self.norm2 = nn.LayerNorm(dim)
self.qkv = nn.Linear(dim, dim * 3, bias=False)
self.proj = nn.Linear(dim, dim)
self.mlp = Mlp(
in_features=dim,
hidden_features=dim * 4,
)
def forward(self, x_in: torch.Tensor) -> torch.Tensor:
x = self.norm1(x_in)
B, N, C = x.shape
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, self.head_dim)
qkv = qkv.permute(2, 0, 3, 1, 4)
q, k, v = qkv.unbind(0)
if self.attn_fn is None:
attn = q @ k.transpose(-2, -1)
attn = attn.softmax(dim=-1)
x = attn @ v
else:
x = self.attn_fn(q, k, v)
x = x.transpose(1, 2).reshape(B, N, C)
x = self.proj(x)
x = x + x_in
x = x + self.mlp(self.norm2(x))
return x
在以下模块中,我们使用输入数据集和选择的注意力函数(attn_fn)训练一个简单的 ViT 支持的分类模型。
def train(dataset, attn_fn=None):
device = xm.xla_device()
train_loader = DataLoader(
dataset,
batch_size=128,
num_workers=os.cpu_count(),
pin_memory=True
)
# configure the VisionTranformer in a manner that complies with the
# Pallas flash_attention kernel constraints
model = VisionTransformer(
block_fn=functools.partial(TPUAttentionBlock, attn_fn=attn_fn),
img_size=256,
class_token=False,
global_pool="avg"
)
optimizer = torch.optim.SGD(model.parameters())
loss_fn = torch.nn.CrossEntropyLoss()
# copy the model to the TPU
model = model.to(device)
model.train()
t0 = time.perf_counter()
summ = 0
count = 0
for step, data in enumerate(train_loader):
# copy data to TPU
inputs = data[0].to(device=device, non_blocking=True)
label = data[1].to(device=device, non_blocking=True)
optimizer.zero_grad(set_to_none=True)
with torch.autocast('xla', dtype=torch.bfloat16):
output = model(inputs)
loss = loss_fn(output, label)
loss.backward()
optimizer.step()
xm.mark_step()
# capture step time
batch_time = time.perf_counter() - t0
if step > 20: # skip first steps
summ += batch_time
count += 1
t0 = time.perf_counter()
if step > 100:
break
print(f'average step time: {summ / count}')
请注意我们为 VisionTransformer 选择的特定配置。这是为了遵守某些限制(截至本文写作时)来自定义闪光注意力内核的要求(例如,关于张量形状的限制)。
最后,我们定义了一个数据集,并比较了三种不同注意力例程的训练运行时间,1. 使用原生 PyTorch 函数,2. 使用 PyTorch 内建的 SDPA 函数,3. 使用自定义的 Pallas 操作符:
# use random data
class FakeDataset(Dataset):
def __len__(self):
return 1000000
def __getitem__(self, index):
rand_image = torch.randn([3, 256, 256], dtype=torch.float32)
label = torch.tensor(data=index % 1024, dtype=torch.int64)
return rand_image, label
ds = FakeDataset()
print('PyTorch native')
train(ds, attn_fn=None)
print('PyTorch SDPA')
train(ds, attn_fn=functools.partial(F.scaled_dot_product_attention, scale=1.0))
print('Pallas flash_attention')
train(ds, attn_fn=flash_attention)
比较结果如表所示:

不同注意力模块的步骤时间(越低越好) — 作者
尽管我们的 Pallas 内核与其他替代方案相比显得性能较差,但我们不应灰心丧气:
-
这些结果很可能可以通过适当的调优得到改善。
-
这些结果仅适用于我们选择的模型和运行时环境。Pallas 内核在其他使用场景中可能会呈现出完全不同的比较结果。
-
Pallas 的真正强大之处在于能够根据我们的特定需求创建和调整低级操作符。尽管运行时性能很重要,但如我们示例中所示的 23%的性能损失,对于这种灵活性来说可能是一个小小的代价。此外,定制化的机会可能会为那些原生框架操作不支持的优化开辟可能性。
增强现有内核
许多时候,调整现有的 Pallas 内核以满足你的特定需求可能比从零开始创建一个内核更容易。如果内核已经经过优化,那么推荐这种做法,因为性能调优通常是繁琐且耗时的。官方的矩阵乘法教程包括了一些关于如何扩展和增强现有内核的示例。在这里,我们进行了一项建议的练习:我们实现了int8矩阵乘法,并评估其相对于bfloat16版本的性能优势。
示例 — Int8 矩阵乘法
在下面的代码块中,我们实现了一个int8版本的矩阵乘法示例。
import functools, timeit
import jax
import jax.numpy as jnp
from jax.experimental import pallas as pl
from jax.experimental.pallas import tpu as pltpu
# set to True to develop/debug on CPU
interpret = False
def matmul_kernel_int8(x_ref, y_ref, z_ref, acc_ref, *, nsteps):
@pl.when(pl.program_id(2) == 0)
def _():
acc_ref[...] = jnp.zeros_like(acc_ref)
acc_ref[...] += jnp.dot(
x_ref[...], y_ref[...], preferred_element_type=jnp.int32
)
@pl.when(pl.program_id(2) == nsteps - 1)
def _():
z_ref[...] = acc_ref[...]
@functools.partial(jax.jit, static_argnames=['bm', 'bk', 'bn'])
def matmul_int8(
x: jax.Array,
y: jax.Array,
*,
bm: int = 128,
bk: int = 128,
bn: int = 128,
):
m, k = x.shape
_, n = y.shape
return pl.pallas_call(
functools.partial(matmul_kernel_int8, nsteps=k // bk),
grid_spec=pltpu.PrefetchScalarGridSpec(
num_scalar_prefetch=0,
in_specs=[
pl.BlockSpec(block_shape=(bm, bk),
index_map=lambda i, j, k: (i, k)),
pl.BlockSpec(block_shape=(bk, bn),
index_map=lambda i, j, k: (k, j)),
],
out_specs=pl.BlockSpec(block_shape=(bm, bn),
index_map=lambda i, j, k: (i, j)),
scratch_shapes=[pltpu.VMEM((bm, bn), jnp.int32)],
grid=(m // bm, n // bn, k // bk),
),
out_shape=jax.ShapeDtypeStruct((m, n), jnp.int32),
compiler_params=dict(mosaic=dict(
dimension_semantics=("parallel", "parallel", "arbitrary"))),
interpret=interpret
)(x, y)
请注意,我们使用了一个int32的累加矩阵,以应对可能出现的溢出问题。还请注意,我们使用了解释标志来调试 CPU 上的 Pallas 内核(如这里所推荐)。
为了评估我们的内核,我们对教程中定义的基准测试工具进行了轻微修改,并将运行时结果与 jnp.float16 Pallas 矩阵乘法内核和内置 JAX 的matmul API 进行了比较:
def benchmark(f, ntrials: int = 100):
def run(*args, **kwargs):
# Compile function first
jax.block_until_ready(f(*args, **kwargs))
# Time function
res=timeit.timeit(lambda: jax.block_until_ready(f(*args, **kwargs)),
number=ntrials
)
time = res/ntrials
# print(f"Time: {time}")
return time
return run
def analyze_matmul(m: int, k: int, n: int, dtype: jnp.dtype,
mm_func):
x = jnp.ones((m, k), dtype=dtype)
y = jnp.ones((k, n), dtype=dtype)
time = benchmark(mm_func)(x, y)
print("Matmul time: ", time)
mm_ops = 2*m*k*n/time
v5e_ops = 394e12 if dtype == jnp.int8 else 197e12
print(f"OP/s utilization: {mm_ops / v5e_ops * 100:.4f}%")
print()
print("bfloat16 Pallas matmul")
mm = functools.partial(matmul, bm=512, bk=1024, bn=1024)
analyze_matmul(8192, 8192, 8192, jnp.bfloat16, mm)
print("int8 Pallas matmul")
mm = functools.partial(matmul_int8, bm=512, bk=1024, bn=1024)
analyze_matmul(8192, 8192, 8192, jnp.int8, mm)
print("XLA int8 matmul")
mm = functools.partial(jnp.matmul, preferred_element_type=jnp.int32)
analyze_matmul(8192, 8192, 8192, jnp.int8, mm)
我们实验的结果如下表所示:

矩阵乘法时间与利用率(作者提供)
通过在 tpuv5e 上使用int8矩阵(而非bfloat16矩阵),我们能够将自定义矩阵乘法内核的运行时性能提高 71%。然而,正如在bfloat16 示例中所示,仍然需要额外的调优才能达到内置矩阵乘法操作符的性能。与bfloat16相比,系统利用率的下降突显了这一改进的潜力。
从头创建一个内核
尽管利用现有内核可以带来很大好处,但它不太可能解决所有问题。不可避免地,你可能需要实现一个在 TPU 上不受支持或性能不佳的操作。在这里,我们展示了创建一个相对简单的逐像素内核。为了保持连续性,我们选择了与之前的文章中相同的广义交并比(GIOU)操作。
示例 — GIOU Pallas 内核
在下面的代码块中,我们定义了一个 Pallas 内核,它对一对批量的边界框进行 GIOU 操作,每个边界框的维度为BxNx4(其中B表示批量大小,N表示每个样本的框数)。该函数返回一个维度为BxN的分数张量。我们在batch轴和boxes轴上选择了 128 的块大小,即我们将每个张量划分为128x128x4的块,并将它们传递给我们的内核函数。 网格和BlockSpec 索引映射相应地定义。
import timeit
import jax
from jax.experimental import pallas as pl
import jax.numpy as jnp
# set to True to develop/debug on CPU
interpret = False
# perform giou on a single block
def giou_kernel(preds_left_ref,
preds_top_ref,
preds_right_ref,
preds_bottom_ref,
targets_left_ref,
targets_top_ref,
targets_right_ref,
targets_bottom_ref,
output_ref):
epsilon = 1e-5
# copy tensors into local memory
preds_left = preds_left_ref[...]
preds_top = preds_top_ref[...]
preds_right = preds_right_ref[...]
preds_bottom = preds_bottom_ref[...]
gt_left = targets_left_ref[...]
gt_top = targets_top_ref[...]
gt_right = targets_right_ref[...]
gt_bottom = targets_bottom_ref[...]
# Compute the area of each box
area1 = (preds_right - preds_left) * (preds_bottom - preds_top)
area2 = (gt_right - gt_left) * (gt_bottom - gt_top)
# Compute the intersection
left = jnp.maximum(preds_left, gt_left)
top = jnp.maximum(preds_top, gt_top)
right = jnp.minimum(preds_right, gt_right)
bottom = jnp.minimum(preds_bottom, gt_bottom)
# intersection width and height
inter_w = jnp.maximum(right - left, 0)
inter_h = jnp.maximum(bottom - top, 0)
# intersection area
inter_area = inter_w * inter_h
# union of two boxes
union_area = area1 + area2 - inter_area
iou_val = inter_area / jnp.maximum(union_area, epsilon)
# Compute the smallest enclosing box
enclose_left = jnp.minimum(preds_left, gt_left)
enclose_top = jnp.minimum(preds_top, gt_top)
enclose_right = jnp.maximum(preds_right, gt_right)
enclose_bottom = jnp.maximum(preds_bottom, gt_bottom)
# enclosing box width and height
enclose_w = jnp.maximum(enclose_right - enclose_left, 0)
enclose_h = jnp.maximum(enclose_bottom - enclose_top, 0)
# enclosing box area
enclose_area = enclose_w * enclose_h
# Compute GIOU
delta_area = (enclose_area - union_area)
enclose_area = jnp.maximum(enclose_area, epsilon)
output_ref[...] = iou_val - delta_area / enclose_area
@jax.jit
def batch_giou(preds, targets):
m, n, _ = preds.shape
output = pl.pallas_call(
giou_kernel,
out_shape=jax.ShapeDtypeStruct((m, n), preds.dtype),
in_specs=[pl.BlockSpec(block_shape=(128, 128),
index_map=lambda i, j: (i, j))]*8,
out_specs=pl.BlockSpec(block_shape=(128, 128),
index_map=lambda i, j: (i, j)),
grid=(m // 128, n // 128),
compiler_params=dict(mosaic=dict(
dimension_semantics=("parallel", "parallel"))),
interpret=interpret
)(*jnp.unstack(preds, axis=-1), *jnp.unstack(targets, axis=-1))
return output
尽管创建新的 TPU 内核无疑值得庆祝(特别是当它能够启用之前被阻止的 ML 工作负载时),但我们的工作尚未完成。Pallas 内核开发的一个关键部分是调优操作符(例如, 块大小)以实现最佳的运行时性能。出于简洁性的考虑,我们省略了这一阶段。
为了评估我们内核的性能,我们将其与以下原生 JAX GIOU 实现进行比较:
def batched_box_iou(boxes1, boxes2):
epsilon = 1e-5
# Compute areas of both sets of boxes
area1 = (boxes1[..., 2]-boxes1[..., 0])*(boxes1[..., 3]-boxes1[..., 1])
area2 = (boxes2[..., 2]-boxes2[..., 0])*(boxes2[..., 3]-boxes2[..., 1])
# corners of intersection
lt = jnp.maximum(boxes1[..., :2], boxes2[..., :2])
rb = jnp.minimum(boxes1[..., 2:], boxes2[..., 2:])
# width and height of intersection
wh = jnp.clip(rb - lt, a_min=0)
# area of the intersection
inter = wh[..., 0] * wh[..., 1]
# union of the two boxes
union = area1 + area2 - inter
iou = inter / jnp.clip(union, a_min=epsilon)
# corners of enclosing box
lti = jnp.minimum(boxes1[..., :2], boxes2[..., :2])
rbi = jnp.maximum(boxes1[..., 2:], boxes2[..., 2:])
# Width and height of the enclosing box
whi = jnp.clip(rbi - lti, a_min=0)
# Area of the enclosing box
areai = jnp.clip(whi[..., 0] * whi[..., 1], a_min=epsilon)
# Generalized IoU
return iou - (areai - union) / areai
我们生成两批随机生成的边界框,并使用上面定义的benchmark函数来衡量我们函数的性能。
from jax import random
batch_size = 1024
n_boxes = 256
img_size = 256
boxes = []
for i in range(2):
k1, k2 = random.split(random.key(i), 2)
# Randomly generate box sizes and positions
box_sizes = random.randint(k1, shape=(batch_size, n_boxes, 2), minval=1, maxval=img_size)
top_left = random.randint(k2, shape=(batch_size, n_boxes, 2), minval=0, maxval=img_size - 1)
bottom_right = jnp.clip(top_left + box_sizes, 0, img_size - 1)
# Concatenate top-left and bottom-right coordinates
rand_boxes = jnp.concatenate((top_left, bottom_right), axis=2)
boxes.append(rand_boxes.astype(jnp.float32))
time = benchmark(batch_giou)(boxes[0], boxes[1])
print(f'Pallas kernel: {time}')
time = benchmark(batched_box_iou)(boxes[0], boxes[1])
print(f'JAX function: {time}')
time = benchmark(jax.jit(batched_box_iou))(boxes[0], boxes[1])
print(f'Jitted function: {time}')
比较结果如下表所示:

不同 GIOU 实现的平均时间(越低越好)—— 作者
我们可以看到,对我们简单的 JAX 实现进行 JIT 编译的结果略优于我们的 Pallas 内核。再次强调,我们可以看到,要达到或超过 JIT 编译的性能结果(及其固有的内核融合),需要对我们的自定义内核进行微调。
利用 TPU 的顺序特性
虽然为 TPU 开发自定义内核的能力具有巨大的潜力,但到目前为止我们的示例表明,达到最佳运行时性能可能具有挑战性。克服这一挑战的一种方法是寻求利用 TPU 架构独特特性的机会。一个例子就是TPU 处理器的顺序特性。尽管深度学习工作负载通常依赖于易于并行化的操作(例如矩阵乘法),但有时它们需要本质上是顺序的算法。这些算法可能会对 GPU 的SIMT(单指令多线程)模型构成严重挑战,有时还会对运行时性能产生不成比例的影响。在这篇文章的续篇中,我们展示了如何实现顺序算法,利用 TPU 的顺序处理器,并尽量减少它们对性能的负面影响。
总结
Pallas 的推出标志着 TPU 发展过程中的一个重要里程碑。通过使 TPU 操作可定制化,它有可能为 TPU 的可编程性开辟新的机会,尤其是在机器学习领域。我们在这篇文章中的目的是展示这一强大新特性的可访问性。尽管我们的示例确实展示了这一点,但它们也突显了达到最佳运行时性能所需的努力。
本文仅仅触及了 Pallas 内核开发的表面。请务必查看官方文档,了解更多关于Pallas 中的自动微分、开发稀疏内核等方面的内容。
稀疏专家混合的崛起:Switch Transformers
深入探讨为今天行业中最强大的大规模语言模型(LLM)铺平道路的技术
· 发表在 Towards Data Science ·8 分钟阅读·2024 年 2 月 15 日
--

使用 Dall-E 生成的图像
稀疏专家混合(MoE)已成为最新一代大规模语言模型(LLM)中的关键技术,如 OpenAI 的 GPT-4、Mistral AI 的 Mixtral-8x7 等。简而言之,稀疏 MoE 是一种极其强大的技术,因为——从理论上讲——它使我们能够以 O(1) 的计算复杂度扩展任何模型的容量!
然而,正如通常所说,问题往往隐藏在细节中,正确实现稀疏 MoE 需要精确掌握这些细节。
在这篇文章中,我们将深入探讨稀疏专家混合(MoE)领域的一个重要贡献——Switch Transformer(Fedus et al 2022),这是首次展示了使用这一技术可以实现的惊人扩展性,成功地在训练 Transformer 模型时达到了 7 倍的加速。我们将涵盖:
-
硬路由:通过为每个 token 执行单一专家来获得有利的扩展性特性,
-
Switch Transformer 架构:MoE 如何融入 Transformer 架构的更广泛背景,
-
token 路由动态:如何利用容量因子进行权衡……
通往生物学 2.0 的道路将穿越黑箱数据
AI 优先生物技术
今年可能是基于 AI 的生物学突破的预期巅峰,AI 将生物学转变为一个可编程、可预测、可复制的工程学科。从 AI 在感知、自然语言和蛋白质结构预测中的突破中汲取灵感,我们努力确定哪些生物学问题最适合通过 AI 技术来解决。接着,我们描绘了生物技术行业中三代概念性的生物 AI 方法,并认为未来最重大的突破将来自于从传统的“白箱”数据(人类可理解的)转向新型的高通量、低成本、专为 AI 设计的“黑箱”数据模式,并与适当的计算方法同步开发。
·发表于Towards Data Science ·46 分钟阅读·2024 年 3 月 18 日
--

由 DALL-E 3 想象的“生物学 2.0”
数据科学在普及 AI 中的角色
在 AI 发展的新兴时代,数据科学团队应关注哪些重点?
·发表于Towards Data Science ·7 分钟阅读·2024 年 3 月 6 日
--
直到最近,AI 模型仅能通过数据科学家或其他服务提供商的解决方案来访问。今天,AI 正被普及,非 AI 专家也可以开发自己的 AI 驱动解决方案。
过去,数据科学团队需要数周甚至数月才能收集数据、标注数据、训练模型并部署,而如今,只需简单的提示词和最新的生成 AI 模型,就能在几分钟内完成模型的构建。随着 AI 技术的进步,人们也期待能采用它并构建更智能的 AI 驱动产品,而作为 AI 专家,我们承担着在组织内部推广这一技术的责任。
自 2016 年以来,Wix 对这一转型并不陌生(远在 ChatGPT(2022 年 11 月 22 日)之前),我们的数据科学团队已经在开发许多具有深远影响的AI 驱动功能。最近,随着生成性 AI 革命的到来,Wix 内越来越多的角色也开始接受这一趋势。我们共同成功推出了许多新功能,借助聊天机器人赋能网站创建,丰富内容创作能力,以及优化代理机构的工作方式。
在我们作为 Wix 数据科学团队的角色中,我们承担着确保AI 质量和广泛接受度的责任。我们认识到积极贡献于 AI 民主化的必要性,并确定了我们必须承担和领导的三个关键角色:1. 确保安全性,2. 增强可访问性,和 3. 提高准确性。

数据科学的三大角色
数据科学 + 产品团队 = AI 影响力
构建 AI 模型的艺术在于能够驾驭并概括未见的边缘案例。这需要一种数据科学实践,包括对业务和数据的理解,并通过反复评估和调整来完善。
将 AI 民主化到产品团队(产品经理、开发人员、分析师、用户体验、内容创作者等)可以加速 AI 驱动的应用程序的发布,但这需要与数据科学团队合作,制定合适的流程和技术。
在下面的 SWOT 图中,我们可以看到数据科学和产品团队如何利用各自的优势和机会,弥补彼此的弱点和威胁,最终按时推出有影响力、可靠的前沿 AI 产品。

产品团队与数据科学的 SWOT 分析
1. 确保 AI 安全性
目前讨论最多的一个话题是使用 AI 的安全性。当聚焦于面向产品的解决方案时,有几个领域我们必须考虑。
-
监管 — 模型可能做出可能歧视某些子群体的决策,例如基于性别给予折扣,或者高薪工作广告中的性别歧视。此外,当使用第三方工具,如外部大型语言模型(LLMs)时,公司的秘密数据或用户的个人可识别信息(PII)可能被泄露。最近,《自然》杂志主张对基于 LLMs 的应用进行监管审查。
-
声誉 — 面向用户的模型可能会出错并产生不良体验,例如,基于大型语言模型(LLMs)的聊天机器人可能会给出错误的答案,或者是过时的答案,或者是有毒的种族主义回答,或者加拿大航空的聊天机器人不一致性。
-
损害 — 决策模型可能预测错误的答案,进而影响业务运营,例如,某个预测房价的模型造成了 5 亿美元的损失。
数据科学家理解 AI 模型的不确定性,并能够提供不同的解决方案来应对这些风险,并允许安全地使用该技术。例如:
-
安全建模 — 开发模型来减少风险,例如 PII(个人身份信息)屏蔽模型和滥用检测模型。
-
大规模评估 — 应用先进的数据评估技术来监控和分析模型的表现和错误类型。
-
模型定制化 — 使用干净的注释数据,筛选出有害和无关的数据点,并构建更小、更具定制化的模型。
-
伦理研究 — 阅读并应用关于 AI 伦理的最新研究,并提出最佳实践。
2. 提高 AI 可访问性
人工智能应该易于使用,并且能够让非人工智能专家也能将其集成到他们的产品中。直到最近,与模型的集成方式是通过数据科学家开发的在线/离线模型,它们是可靠的、针对特定用例的模型,并且其预测结果是可访问的。
但它们的主要缺点是非 AI 专家无法进行定制化。这就是为什么我们提出了“自己动手做 AI”(D-AI-Y)的方法,允许你构建自己的模型,并将其作为服务部署到平台上。
目标是快速构建简单但有价值的模型,并且对 AI 的专业知识要求较少。如果模型需要改进和研究,我们有数据科学家参与。
D-AI-Y 包括以下组件:
-
教育:教育组织如何正确使用 AI,在 Wix,我们有一个 AI 大使计划,这是 Wix 不同小组与数据科学小组之间的 AI 知识桥梁,在这个计划中,各小组的代表会接受培训,并及时更新新的 AI 工具和最佳实践,以提高 Wix 中基于 AI 的项目的规模、质量和速度。
-
平台:有一种方法可以连接到大型语言模型(LLMs)并编写提示。平台应考虑模型的成本和规模以及对内部数据源的访问。在 Wix,数据科学小组构建了一个 AI 平台,将 Wix 的不同角色与来自多个供应商的模型连接起来(以减少 LLM 供应商锁定)以及其他功能,如语义搜索。该平台充当一个集中的中心,供所有人使用、分享他们的模型、治理、监控并在生产环境中提供服务。
-
构建简单直接模型的最佳实践和工具:使用提示或专用模型来解决特定学习任务:分类、问答机器人、推荐系统、语义搜索等。
-
评估:针对每个学习任务,我们建议一种特定的评估流程,并在需要时提供数据整理的指导。
例如,一家公司使用检索增强生成(RAG)方法构建了许多问答模型,这种方法通过搜索能够回答问题的相关证据来回答问题,然后将证据添加到 LLM 的提示中,以便它能基于这些证据生成可靠的答案。
所以,产品数据科学团队可以提出:1. 关于 RAG 主题的教育材料和讲座,例如我曾做的这场关于语义搜索的讲座,用于改进 RAG。2. 为平台配备合适的向量数据库和相关的嵌入器。3. 关于如何构建 RAG 的指南,如何检索证据并编写生成提示。4. 支持 RAG 适当评估的指南和工具,正如在这篇 TDS 文章和Trulens 的 RAG 三元组中所解释的那样。
这将使公司中的许多角色能够以可靠、准确且可扩展的方式构建自己的基于 RAG 的应用模型。
3. 提高 AI 准确性
随着 AI 的普及,预计将构建更加复杂、准确和先进的解决方案。最终,非 AI 专家能够提升模型性能的程度是有限的,因为这需要对模型如何运作有更深入的理解。
为了提高模型的准确性,数据科学团队专注于以下类型的工作:
-
改进通用模型 — 定制并改进模型,以容纳 Wix 知识并超越外部的通用即开即用模型。
-
定制模型 — 高优先级且具有挑战性的模型,D-AI-Y 无法支持。与通用模型不同,这里我们有非常特定用例的模型,需要定制化。
-
改进 D-AI-Y — 随着我们不断改进 D-AI-Y 平台、最佳实践、工具和评估 AI 的准确性,因此我们持续投入研究时间和精力,致力于提升和发掘创新方法,以使其更好。
结论
经过多年的等待,AI 的普及正在发生,让我们拥抱它吧!产品团队对业务的内在理解,加上 GenAI 的易用性,使他们能够构建推动产品能力的 AI 驱动功能。
因为非 AI 专家通常缺乏对 AI 模型如何运作以及如何在大规模上正确评估它们的深刻理解,他们可能会遇到关于结果可靠性和准确性的问题。此时,数据科学团队可以通过指导各团队如何安全使用模型、在需要时创建缓解服务、分享最新的最佳实践、评估模型性能并在大规模上服务模型,来协助和支持他们的工作。
当一个 AI 功能展现出巨大的商业影响时,产品团队会立即开始将精力转向改进结果,这时数据科学家可以提供先进的方法来提高性能,因为他们理解这些模型是如何运作的。
总之,数据科学在实现 AI 普及中的作用至关重要,因为它架起了 AI 技术与那些可能没有深入 AI 专业知识的群体之间的桥梁。通过数据科学家和产品团队的合作,我们可以充分利用两者的优势,创造安全、可及且准确的 AI 驱动解决方案,从而推动创新并提供卓越的用户体验。随着不断的进步和创新,普及化 AI 的未来在各行各业中蕴藏着巨大的变革潜力。
除非另有说明,所有图片均为作者提供
数据与人工智能失败的根本原因

Wave by CLEANPNG
揭开数据战略的神秘面纱
成功所需的战略性基础工作的指南
·发表于 Towards Data Science ·阅读时间:26 分钟·2024 年 11 月 19 日
--
通过数据和人工智能创造商业价值。每个人都在谈论它,但大多数公司在将数据变现方面都在挣扎。我认为在大多数情况下,这是由于缺乏有效的商业战略所致。本文展示了激活公司数据资产所需的战略基础工作。
这是 一系列文章的第二部分,在这篇文章中,我揭示了数据战略——这是任何希望成为数据驱动型组织、在当今数字化世界中保持竞争力的企业所必需的核心要素。
我是 Jens,一位具有近 20 年实践经验的商业导向数据专家,专注于实施数据与 AI 应用案例。我为来自各行各业的领导者提供建议,帮助他们设计战略并培养数据文化,以利用数据、分析和 AI。
tl;dr
-
缺乏真正的商业战略是许多组织在数据、分析和人工智能方面失败的根本原因,也是它们难以成为数据驱动型企业的原因。
-
因此,组织应首先投资于其商业战略的设计。
-
之后,与数据相关的决策应该在整个组织中绘制出来。
目录
1. 数据出了什么问题?
1.1 数据价值创造失调的症状
1.2 根本原因
1.3 问题有多大?
2. 赢得胜利的策略
2.1 它的起源
2.2 战略就是做出选择
2.3 战略选择级联
2.4 备忘单
2.5 做出综合选择
2.6 制定选择方案
2.7 将战略付诸实践
3. 数据业务需求
3.1 没有明确的数据需求
3.2 数据作为运营职责
3.3 数据作为战略差异化因素
3.4 数据作为业务
3.5 为什么了解你的数据业务需求如此重要?
4 何时需要专门的数据或人工智能战略?
4.1 数据战略之谜
4.2 数据或人工智能职能的战略
4.3 建立数据能力的计划
5 将一切整合在一起
5.1 为数据奠定战略基础的过程
5.2 制定数据相关选择的示例
6 结论
参考文献
1. 数据出了什么问题?
不论是大组织还是小组织,公共还是私营机构,都在努力成为数据驱动型。成为数据驱动型意味着,数据——由内部运营过程生成——被‘再利用’以支持人类决策、自动化决策,并促进数字化商业模式。
通常来说,利用数据可以带来:
-
更多收入
-
降低成本,提高效率和利润
-
更好的风险管理
-
更多创新,以及基于数字产品和服务的新商业模式的可能性
有关数据驱动型组织的动机和背景的更多细节,请参见本系列文章的第一部分[1]。
数据咨询行业、众多数据专家(包括我自己)和技术供应商不厌其烦地重复强调组织领导者为何应投资于数据能力,包括技能、能力、方法论、文化以及——当然——相应的 IT 工具和平台。以下是从[2]翻译过来的一个引用,点明了要点:
“通过数据创造持续价值已经成为当今企业的决定性竞争优势,且在不久的将来,这将成为生存的必需条件。”[2]
这个话题近年来引起了广泛关注,原因在于生成型人工智能(GenAI)技术的显著进展,以及由此带来的媒体炒作,这些内容已经出现在主流媒体中,并且——至少对我个人而言——甚至成为了许多私人场合中与家人和朋友的对话话题。
尽管当前有生成型人工智能的热潮,但数据价值创造的理论和实践并非新鲜事物。它在过去以不同的名称出现过,如分析、商业智能、统计建模、数据科学、机器学习、大数据,如今主要被称为人工智能。所有这些术语或多或少描述了从数据中提取洞察的不同技术,而当这些技术得当应用时,就能够显著提升商业表现。
作为一名数学家,我在数据科学家的角色中度过了许多年,应用统计方法来发现模式,这些模式可以被企业用来增进对消费者、市场或竞争的理解,进而可以用来增加收入或减少成本和风险。每个行业和价值链的每个环节都有经过实践验证并且为人们所理解的数据价值创造应用案例。
尽管“再利用”数据来创造额外商业价值,甚至转变现有商业模式的应用和益处是显而易见的,但我也观察到一种截然不同的商业现实:
许多组织仅仅在努力有效且高效地利用数据来创造商业利益。
数据在组织中出现了什么问题?
1.1 功能失调的数据价值创造的症状
组织在数据方面的问题清单可能很长[13],每个组织的情况肯定不同。然而,问题通常归结为一些典型的症结:
-
职责不明确:在数据解决方案的协调和开发以及支持活动(如数据治理或数据架构)方面,责任不清。
-
数据、分析或人工智能项目失败:分析和人工智能项目往往耗时长且成本高,或者在没有与特定商业目标紧密联系的情况下启动,导致项目无法带来可衡量的价值。
-
数据意识缺乏:员工和高管对将数据作为资产加以利用的潜力和益处认识不清。数据当前和未来的角色根本没有被理解,导致错失机会。这通常还伴随着高层管理的忽视。
-
孤岛有两种形式:组织孤岛阻碍了协作、创新和效率,而数据孤岛则使得人们无法访问、结合和利用来自组织不同部分的数据,导致碎片化的视角和冗余或矛盾的结果。
-
缺乏熟练的人员:组织内缺乏数据倾向的业务用户或数据专家。
-
数据质量差:团队经常报告数据的准确性、完整性或一致性问题,导致对基于这些数据的洞察和决策缺乏信任。
这些是许多组织都非常熟悉的症状。那么,所有这些痛苦的根源是什么呢?
1.2 根本原因
曾经并且现在依然存在一种普遍的误解,即数据价值创造是一个技术挑战。如果我们只引入合适的平台并提供所需的培训,组织就能够有效且高效地利用数据来创造商业价值。但这是一个谬论。
IT 平台、工具和算法库已经存在了一段时间,成熟且可以使用,并且持续改进。虽然数据的技术提取和准备应该‘正确’完成,以实现高质量数据以及可扩展和灵活的数据模型,但已有经过验证的框架和最佳实践可以精确实现这一目标。
选择和实施 IT 平台、工具、最佳实践和算法库来处理数据是一个复杂但并不复杂的问题,实际上已经解决了。
组织要么具备选择和实施内部技术的专业知识,要么可以轻松获得可靠的外部专家咨询。
我认为,功能失调的数据价值创造的根本原因是缺乏一致的商业战略:
缺乏商业战略是许多组织在数据、分析和 AI 方面失败的根本原因。
如果商业战略没有明确定义,组织在构建和维护他们需要的数据能力时会遇到困难,这些能力对于有效和高效地发现、创新、构建和维护数据驱动的解决方案至关重要。

图 1:战略是大多数公司在数据方面失败的根本原因。植物图片来自 CLEANPNG。
例如,成功实现数据价值生成所需的一个核心数据能力是(数据)文化 [3, 4],它确保员工:
-
理解数据能实现什么
-
想要利用数据改善业务
-
可以使用数据来实现这一点
-
必须使用数据,因为这是他们领导者所期望的
然而,为了创建有效的数据文化,你需要知道你的目标文化应朝着哪个方向发展。你必须了解数据业务需求,这些需求指引你需要在何种方向上发展你的目标数据文化。这些数据业务需求正是由战略提供的。
没有明确的商业战略,组织很难就数据、分析和 AI 的需求达成一致。数据价值创造的角色也因此不明确。然而,要构建量身定制和可持续的数据能力,你需要了解在组织中数据的主要用途,是否是例如为 CFO 提供一组报告,还是数据是推动竞争优势的一种手段,比如通过数字产品和服务。
通过有意识的战略设计,将隐含的选择变得显而易见,你便构建了一种沟通方式,来达成关于数据业务需求的共识。
正确构建数据和 AI 能力无疑并非制定稳固战略的唯一好处。这更像是一个副作用。虽然这应该是不言自明的,但为了完整性,拥有一个明确且精心设计的商业战略的好处包括:
-
竞争优势
-
明确的方向和目标
-
聚焦与有效的资源分配
-
员工对齐与士气
-
长期成功与可持续性
-
有效的风险管理
1.3 问题有多严重?
当我开始与新客户合作,帮助他们解决与数据相关的问题时,我通常会进行的首要信息请求之一就是索要相关的战略文档,尤其是(企业)商业战略。我经常收到模糊的回应,例如:
-
“我们的战略尚未文档化。”
-
“我们的战略已经过时,需要更新。”
-
“我不确定,我需要做一些研究。”
-
“我们有一种行动导向的战略。”
在提供战略文档的情况下,通常这些文档最终变成了一份松散的倡议清单,所有倡议听起来都不错,但整体上并未达到常见的战略标准[5a]。
虽然我并不清楚有关此问题的代表性统计数据,且一个好的商业战略可能确实依赖于组织的规模以及其他因素,如行业等,但我个人对德语市场的印象——我最为活跃的领域——是,许多公司缺乏一个扎实、明确且设计良好的战略。
很少有组织能够制定出明确、扎实的商业战略,从中可以推导出所需的数据、分析和 AI 能力。
根据我的经验,小型和中型企业,“Mittelstand”,即德国经济的支柱,面临的挑战尤其严峻大约 56%的员工在这些企业工作 [6]。
通过本文,我旨在通过澄清组织战略工作中需要做的事,来解决数据作为资产的根本性问题。
为了开始,我们先仔细看看什么是真正的战略。
2. 玩转成功
2.1 它的起源
玩转成功是一种由罗杰·马丁 [7]提出的战略框架,他在 2017 年被评为全球#1 管理思想家,是全球各大公司 CEO 的可信顾问,曾任 Monitor 咨询公司顾问,且曾是罗特曼管理学院的教授。
这一框架是在 1980 年代和 1990 年代开发并不断完善的[5b],并最终形成了罗杰·马丁和宝洁公司前 CEO A.G. 拉夫利的合著书籍 [8]。该书于 2013 年出版。
这一框架成为宝洁公司的标准战略方法论,并且自那时以来已成功应用于许多行业。此外,玩转成功框架也通过罗杰·马丁的一系列战略实践者见解不断得到详细阐述[5, 5c]。
虽然市面上可能存在其他合适的战略框架,我之所以选择“玩转成功”方法,是因为它被广泛知晓和应用,并且配套有完整的流程、模板、资源和培训体系,可以帮助设计任何类型的战略。
2.2 战略就是做出选择
在“赢得竞争”框架中,战略的定义如下[8]:
“战略 是一个整合的选择集合,它在行业中独特地定位企业,以便相对于竞争对手创造可持续的优势和卓越的价值。”
所以,战略是关于做出(通常是艰难的)选择,以赋予公司竞争优势。然而,这些选择是否正确并没有万无一失的保证。结果总是一次战略性赌注。战略设计过程的目的是缩短你的赌注,即在已有信息的基础上,最大化成功的可能性。
请注意,这一定义的战略与计划有很大不同,计划常常被误解为战略[9, 5d]。
“规划是制定项目的时间表、交付物、预算和责任的行为。”[5d]
然而,战略和计划并不是互相排斥的。制定计划是战略设计过程的自然部分,目的是建立实施战略所需的能力(参见下文 2.7 节)。
还有一种我特别喜欢的战略定义,即:“战略是决定你选择做什么和不做什么,以实现特定目标的逻辑”[10]。
因此,每个组织都有一个战略,无论它是否被写下来。这种隐性战略可以从公司采取的行动中推断出来。隐性战略的问题在于,组织内可能没有共识,无法确定这种隐性战略是什么,并且这些行动可能无效。
2.3 战略选择级联
在“赢得竞争”框架内,每个战略设计者必须做出五个关键选择。这些选择被结构化为所谓的战略选择级联,这是该框架的核心工具之一。

图 2:战略选择级联有助于结构化战略设计过程中需要回答的战略问题。
战略的实际核心由第二和第三框中所做的一致选择构成:何处竞争与如何取胜。
如何应用这个级联,最好通过一个现实世界的例子来说明,该例子来自[9],描述了西南航空的公司战略选择。

图 3:西南航空的战略选择级联示例。该级联总结了大多数相关的战略决策。
该级联不会仅仅填充一次;它将在战略设计过程的多个使用场景中反复审视和应用:
-
通过观察竞争对手的行动来重建他们的战略:这是一个很好的练习,可以帮助你熟悉框架,是一种破冰活动[5e],用于开始战略设计过程。
-
记录当前的战略:这是建立对现状的共同理解的好方法,并将你当前的隐性选择变为显性选择,前提是之前没有进行过此操作。
-
记录新的战略可能性:设计战略的过程[5f]包含了一个创造性元素,在这个过程中,会生成多个未来目标战略的可能场景,并进行系统的评估。这些场景会通过战略选择级联进行记录和详细描述,每个可能性都有一个对应的级联。
2.4 备忘单
当我使用选择级联时,我的《赢得竞争备忘单》对我有所帮助,其中包含了有关每个框的进一步信息以及框架的主要来源。

图 4:赢得竞争备忘单,提供有关战略选择级联每个元素的详细信息。
2.5 制定集成选择
在制定战略选择级联时,框框不应单独填写[5g],而需要将它们作为整体来考虑,因为真正的战略选择应当相辅相成,互相强化。
这尤其适用于“何处竞争”和“如何竞争”这两个不可分割的部分,它们始终需要一起考虑[5h]。
但在考虑必须具备的能力和管理系统时,你需要时刻牢记你的“何处竞争”和“如何竞争”。能力和系统是一种现实检查:如果你无法建立所需的能力和系统以实现你的战略,你需要考虑采取不同的方式取胜,有时甚至需要换一个竞争领域。

图 5:在处理选择级联时,这是一个来回的过程,你需要在关注前景的同时,也要始终牢记背景。
从这一点来看,应该明确,填写战略选择级联的过程可以是一个相当迭代的过程,反复推敲,直到你得出一组符合选定竞争领域的胜利理论,并且能够实现赢得愿景,且这些愿景可以通过所需的能力实现,而这些能力是通过所需的管理系统来构建和维持的。
2.6 确定选择
战略选择在整个组织中进行,并且并非所有选择都在最高层(公司层)做出。
选择制定[5i]是一个结构化过程,旨在定义、传达并委派战略选择,确保在各个层级上都能做出一致且有效的决策。
因此,在大型组织中存在嵌套的战略和相应的选择级联,例如国家、类别、品牌或业务单元的战略。
不仅各个业务单元需要做出自己的战略选择,从而制定嵌套的选择级联,职能部门[10],如信息技术(IT)、人力资源(HR)、财务、市场营销、研发或数据与人工智能(Data & AI)等也需要做出战略选择。

图 6:展示了一个职能战略的嵌套选择级联。
功能性战略的一个特点是,它们可以被视为自然垄断[5j],在这种情况下,(内部)客户通常没有其他选择,只能使用该职能提供的服务。
2.7 将战略付诸实践
一个好的战略设计过程并不区分创意阶段和随后的执行阶段,而是从一开始就将激活融入到战略过程当中[11]。
战略激活的目标是让每个人理解所做的设计选择,并使利益相关者能够做出支持战略的选择。
战略激活包括创建利益相关者的参与,以使战略具体化,并通过建立必备能力和相应的管理支持系统,推动从设计到行动的转变。

图 7:通过构建所需的能力和系统,战略被付诸实践。
一旦能力和系统被定义,它们就需要被构建和维护。对于每个能力或系统进行适配差距分析(通常称为成熟度评估)后,将为创建构建、购买或借用所需能力和系统的计划提供输入。
有趣的是,(数据)咨询公司通常会提出通过成熟度评估作为进入客户的敲门砖。然而,从上述内容中应该可以清楚地看出,任何适配差距活动只有在战略工作基础完成后才有意义。
将战略付诸实践还为规划提供了联系,规划通常被与战略混淆:战略选择通常要求建立新的能力,因此需要行动计划,一旦现状评估完成。
3. 数据业务需求
一旦你明确了你的商业战略,应该清楚数据、分析和人工智能在你组织中的作用,也就是说,你的(企业)数据业务需求是什么。
就是这么简单:
商业战略必须回答如何使对组织或业务单元相关的数据变得重要。
在使用“Playing to Win”框架时,只需一眼望去,你的商业战略选择级联应能清晰地显示数据对你业务的重要性。
数据业务需求可以采取四种不同的形式。

图 8:概述了四种可能的数据业务需求,并在浅黄色中突出了数据、分析和人工智能在商业战略选择级联中的相关元素。
让我们深入分析这四种情景。
3.1 没有明确的数据需求

在这种情况下,组织内部对数据价值创造是否有必要为取胜提供共识。这意味着,数据似乎与战略选择瀑布的五个框架无关。
示例
一个例子可能是一个专注于手工艺品的小型传统手工业企业,该企业依赖手艺技能、声誉和客户关系。
建议
虽然我相信数据、分析和人工智能并非宇宙的中心,并且并非每个组织都依赖这些来取胜,作为一个可能有偏见的数据专业人士,我自然会通过提问来挑战这种观点:
-
当前的业务痛点和收益可以通过数据分析和人工智能得到解决吗?
-
数据、分析和人工智能在创新中能扮演什么角色?
-
竞争对手或本行业/其他行业的公司已经采用了哪些解决方案?
-
潜在的收益是否值得投入更多的精力和资源?
组织应当明确潜在的数据使用案例及其对业务的重要性。
3.2 数据作为运营职责

在第二种情况下,数据、分析和人工智能是运营性能力,但不是战略性能力和/或系统,因此应尽可能实现成本效益。相关的数据元素可以在瀑布图的第四和第五框中找到,即必备能力和启用管理系统。
示例 1
对于上面提到的西南航空例子,业务需求是有效且成本效益高地管理航空公司运营的复杂性。
公司需要建立、运行并维护启用管理系统,以确保运营效率[12]:
-
一个实时跟踪飞机准时表现的绩效管理系统
-
一个监控和优化登机口等待时间的系统
-
一个最小化燃料成本的系统
-
一个用于规划新车站和航线的决策支持系统
这意味着对于西南航空的例子,数据、分析和人工智能的业务需求主要体现在最后一个框框——启用管理系统。
示例 2
一家地区性公用事业公司使用数据、分析和人工智能来监控能源使用、预测维护需求并生成内部报告。与示例 1 类似,数据有助于实现运营效率和满足合规要求,但并未提供战略性差异化。
因此,数据、分析和人工智能是能力(第 4 框)或系统(第 5 框),代表着运营上的迫切需求,而非战略性能力。
与数据相关的启用管理系统的例子可能是需要提供管理洞察的数据报告系统、商业智能工具或相应的数据平台。
建议
在构建和维护与数据相关的能力和系统时,成本效益应该是确定是构建、借用还是购买所需能力和系统的关键维度,以确保在指定的竞争领域中取胜。
虽然仅仅将数据用于运营效率没有错,但组织应定期评估是否忽视了战略机会。当然,作为一名数据专业人士,我会通过以下问题来挑战这一点:
-
数据、分析和人工智能能为创新发挥什么作用?
-
现有的产品和服务能否通过基于数据的洞察力延伸,从而为客户带来附加值和/或与竞争对手的差异化?
再次强调,组织应该力求明确潜在的数据使用案例,以及这些案例对当前商业模式的战略相关性,或者现有的商业模式是否可以通过数据、分析和人工智能进行扩展。
3.3 数据作为战略差异化因素

在这种情况下,数据、分析和人工智能作为核心差异化因素,直接影响公司在市场中如何获胜。它们是实现公司价值主张的关键部分,帮助公司创造竞争优势(框 3:如何获胜),或是实现竞争优势理论的关键战略能力(框 4:必备能力)。
示例
这个示例,借鉴自[5k],是 Frito Lay 的案例,Frito Lay 是一家咸味零食生产商(Lay’s、Doritos 等),它采用了一种直接门店交货系统,即产品直接配送到便利店,并由配送司机将其摆放在货架上。
这种直接门店交货系统劳动密集,因此成本较高,但它使 Frito Lay 区别于其竞争对手。拥有大量知名产品为 Frito Lay 的配送系统创造了巨大的成本优势(一次性采购多种产品),从而产生了独特的竞争优势,是竞争对手无法匹敌的。
构建数据和人工智能解决方案,预测商店库存并为每个销售点生成最佳产品订单,有助于降低直接门店交货系统的成本。因此,数据、分析和人工智能是战略性能力(框 4:必备能力),直接支持竞争优势(框 3:如何获胜)。
建议
使用商业战略级联来传达数据在组织中的战略重要性。确保高层管理人员和所有相关利益相关者理解数据、分析和人工智能对业务的影响和价值。这为必要的数据文化和数据素养发展以及为完全建立所需能力以赢得竞争所需的组织变革奠定了基础。
最后,这也有助于关于资金讨论以及设定正确的优先事项。
严格质疑是否有必要外包任何数据能力,因为它们对组织具有战略重要性。
推动你的思维,考虑是否存在额外的数据货币化机会[16],这可能会导致“数据即业务”的模式。
3.4 数据即业务

这是大多数人感到兴奋的情境:数据扩展甚至根本性地改变了当前的商业模式,组织进入了数字经济。
在这种情况下,数字数据驱动的产品或服务是公司产品的一部分(框 2:在哪里竞争),甚至可能是愿景的一部分(框 1:胜利的愿望)。
示例
这个示例同样借鉴自[5k],讲的是 Westlaw,一家提供法律搜索服务的数字平台。Westlaw 的战略资产之一是一个包含自 1975 年以来法院案件记录的数据库,允许律师根据实际案件查阅历史案例。
数据是公司产品的一部分(框 2:在哪里竞争),其在市场上的独特性确保了其竞争优势(框 3:如何取胜)。
建议
由于数据显然是公司的一项资产,能够确保竞争优势,因此必须对数据予以相应的重视。例如,它需要关心、培育、保护和质量保证。
因此,数据治理(框 4:必备能力)和相应的系统(框 5:支持管理系统)是必不可少的。
3.5 为什么了解你的数据业务需求很重要?
每个组织都是不同的,具有独特的数据业务需求。
你的组织需要了解数据、分析和人工智能在业务战略中的角色,以便确定需要投入多少资源来创建和维护与数据相关的能力和系统。
许多高管仍然受到围绕数据和人工智能的炒作困扰,这导致了怀疑、不确定性,甚至是无思考的投资。
因此,稳固的业务战略和对数据业务需求的透彻理解能够提供指导,并帮助传达数据在组织中的重要性和角色。
因此,我主张:
成为数据驱动的第一步是制定坚实的业务战略设计。
4 何时需要专门的数据或人工智能战略?
4.1 数据战略之谜
如果你已经觉得“战略”这个词带来了混乱,那让我告诉你,“数据战略”这一术语的问题更大。我阅读的几乎每篇文章或书籍对其定义都有所不同——如果它们有定义的话。这让我在深入研究这一话题时常常感到头疼。
在大多数情况下,“数据战略”一词用于指代在组织内部构建数据相关能力的计划。
这被称为构建分析 IT 平台的计划,推广新的 BI 工具,建立数据治理,提升数据素养,发展数据文化,或为数据管理、分析和 AI 构建运营模型。
在我看来,“数据战略”这个术语应仅用于描述一个功能的战略,该功能为主要面向内部数据客户,偶尔也面向外部数据客户提供数据服务。
4.2 数据或 AI 功能的战略
在许多情况下,组织将需要一个与数据相关的功能,提供以下服务:
-
为业务自助服务用户提供集成的、建模的和质量保证的分析准备数据(例如,以数据集市的形式,通过数据目录浏览)
-
开发和维护 BI 仪表板和报告
-
为业务用户进行分析,这需要专业知识,如数据科学或机器学习
-
提供关于数据使用案例的创新、发现和评估的咨询,或与买与自建决策相关的咨询服务(例如针对 GenAI 解决方案)
-
创建和进行数据、分析或 AI 的教学和培训
-
为内部或外部利益相关者提供持续的信息请求,例如与供应商的数据交换或满足监管数据需求
如果一个组织有或将拥有一个数据管理、分析或 AI 的功能,为(内部)数据客户提供服务,那么它只需要如第 2.6 节所述的战略。
如何将战略选择级联应用于设计这样的数据战略,是本系列第一部分的主题 [1]。

图 8:数据战略选择级联的可视化 [1] 展示了如何将级联翻译到数据功能的上下文中。
明确的战略使功能能够明确自己该在哪些领域发挥作用,以及如何与数据客户共同取得成功。它还引导并对齐其行动,有效分配资源,并提升为组织提供的价值。
如果你的数据功能没有明确的战略,它很可能会默认采用一种有害的隐性战略,这种战略要么是“奴性战略”(试图满足所有可能的业务需求),要么是“帝国战略”(仅仅为了数据、分析或 AI 而做)[10]。
根据该功能所提供的服务和产品的性质及重点,您可以称其为数据战略、分析战略、BI 战略或 AI 战略。
功能的数据战略通常包含跨越或存在于组织多个部分的能力和系统,例如企业分析数据平台。在战略语言中,这些能力和系统被称为增强性支撑 [8, 5l]。
4.3 构建数据能力的计划
构建与数据相关的能力和系统的计划是大多数公司在数据、分析或人工智能方面最感兴趣的话题。
制定这样的计划可能是一个具有挑战性的任务,因此值得投入时间。我所说的只是,您需要确保在此之前已经完成了战略基础工作,这样您就知道实际需要什么样的能力和系统来取胜。计划的性质和许多设计选择的要求取决于这一战略基础工作。
这种“数据规划”的范围可能会根据组织的数据业务需求而有很大差异:从仅为控制目的构建报告系统和相应的数据格局,到使多个业务单元和职能部门能够广泛利用整个价值链中的数据使用案例的潜力。
后者通常与公司范围的转型计划相伴随,而构建数据能力的计划可以作为整体数据驱动业务转型路线图的一部分,由专门的领导者负责数据、分析或人工智能[14, 15]——有时被称为首席数据官。
5 整合所有内容
构建数据或人工智能能力和系统的计划应该放在最后,作为一个适配差距分析的结果,其中目标状态由前面的战略选择提供。
因此,组织需要明确他们的数据业务需求,即数据、分析和人工智能将在未来业务中扮演什么角色。最重要的是:您需要从战略基础工作开始。
5.1 为数据奠定战略基础的过程
一个设计有效的数据和人工智能能力的过程可能如下所示:

图 9:为数据、分析和人工智能奠定战略基础的四步流程,为数据驱动型组织提供基础。
1. (重新)设计您的业务战略
-
确定现有的战略选择,无论是隐性还是显性
-
确定您所在组织或业务单元的数据业务需求,并确保与数据、分析和人工智能相关的战略选择在您的业务战略中得到清晰表述
-
确保高层管理人员和相关利益相关者对数据、分析和人工智能在您组织中未来的角色达成一致
2. 制定相关选择
-
为数据职能制定战略选择,并为该职能设计您的数据战略
-
为相关业务单元和/或职能部门制定其他相关数据选择
3. 评估与规划能力和系统
-
这适用于组织内所有层次的战略
-
对每个与数据相关的能力和系统进行成熟度评估,以确保您的战略得以落实
-
制定构建、扩展或维护每个能力和系统的计划
-
对于您的数据战略,这可能包括:组织设计、数据目标运营模型、技术和工具、数据管理、数据集成、数据建模、数据架构、数据治理、数据民主化、数据质量管理、主数据管理、数据创新管理、BI、数据科学、数据素养和数据文化
4. 构建能力和系统
-
执行项目,建设和维护赢得数据竞争所需的能力和系统
-
衡量战略的成功并监控进展
通过奠定战略基础,您将解决组织中功能失调的数据价值创造的根本原因。
奠定战略基础确保高管和员工理解数据在组织中的角色。反过来,这将有助于建设有效且可持续的数据能力,并为开发和维护这些能力所需的资源提供指导。
在第一步中,我们已经在本文第三部分中看到了相关示例。那么第二步中的决策授权应该是怎样的呢?
5.2 数据相关决策授权的示例
让我们将过程的第二步应用到 Frito Lay 的上述示例中,其中店铺库存预测支持直接门店配送系统的竞争优势。
销售部门的店铺库存预测案例可能是 Frito Lay 众多 AI 应用案例之一,因此 AI 能力在公司许多部门中都是必需的。假设 Frito Lay 决定建立一个中央 AI 能力,从而设立一个专门的 AI 职能部门。

图 10:组织中数据和 AI 相关决策授权的示例。
AI 功能负责人需要设计一个支持公司战略并直接促进 Frito Lay“如何取胜”(How to Win)的 AI 战略。AI 战略中的一个“Where To Play”可以是为销售部门提供预测解决方案。
为了确保 AI 预测解决方案产生影响,还需要将与数据相关的决策交给销售负责人,以便销售部门承诺在其规划过程中使用 AI 驱动的洞察。这意味着“AI 驱动的销售规划”将成为销售战略的必备能力。
6 结论
良好的商业战略对成功的数据价值创造似乎被低估和忽视了。公司必须有一个明确定义的、明确的商业战略,从中可以明显看出任何数据、分析和 AI 需求。这一战略基础是授权数据相关决策的起点,无论是在专门的数据管理、分析或 AI 职能中,还是在跨业务单元构建必要能力时。
为了让你的组织成为数据驱动型的,确保你有一个稳固的战略基础。否则,你很可能会遇到问题,并经历我们都熟知的功能失调的数据价值创造症状。
展望未来,竞争格局正在迅速变化。未来最成功的组织将是那些将数据视为战略资产,而不仅仅是运营副产品的组织。随着人工智能技术的成熟并进一步融入商业流程,那些拥有连贯战略的公司将准备好充分利用这些进展,创造新的收入来源,推动创新,并保持对竞争对手的可持续优势。在未来几年,我们将看到已经为数据和人工智能奠定战略基础的公司与那些继续按常规运营的公司之间的鸿沟逐渐加大。前者将蓬勃发展,而后者可能在一个日益以数据为驱动的世界中挣扎求生。
现在是行动的时候了:优先考虑你的战略(重新)设计,统一你的数据业务需求,并建立赢得数据所需的能力。只有这样,你才能真正利用数据、分析和人工智能的变革性力量,取得长期成功。
参考文献
[1] Jens Linden, 数据战略选择级联 (2024),在Towards Data Science上发布的 Medium 文章
[2] Sebastian Wernicke, 数据启示 (2024),由 Vahlen 出版的德语书籍
[3] Carsten Bange, 数据文化:定义、挑战与措施 (2022),文章
[4] Jens Linden, 数据文化——什么与为什么? (2024),LinkedIn 帖子
[5] Roger Martin, 赢得胜利/ 实践者洞察 (2024),列出文章的网页
[5a] Roger Martin, 从值得称赞的清单到如何真正赢得胜利 (2020),Medium 文章,属于‘赢得胜利实践者洞察’系列
[5b] Roger Martin, 赢得胜利的起源 (2023),Medium 文章,属于‘赢得胜利实践者洞察’系列
[5c] Roger Martin, (赢得胜利) x 5 (2024),Medium 文章,属于‘赢得胜利实践者洞察’系列
[5d] 罗杰·马丁,战略与规划:互为补充而非替代(2024),Medium 文章,属于“玩得赢实战见解”系列
[5e] 罗杰·马丁,最佳战略破冰者(2024),Medium 文章,属于“玩得赢实战见解”系列
[5f] 罗杰·马丁,战略选择结构化过程,Medium 文章,属于“玩得赢实战见解”系列
[5g] 罗杰·马丁,克服整合战略挑战(2024),Medium 文章,属于“玩得赢实战见解”系列
[5h] 罗杰·马丁,论“在哪里竞争”与“如何赢”不可分割性(2020),Medium 文章,属于“玩得赢实战见解”系列
[5i] 罗杰·马丁,战略选择指导(2024),Medium 文章,属于“玩得赢实战见解”系列
[5j] 罗杰·马丁,自然垄断的战略(2024),Medium 文章,属于“玩得赢实战见解”系列
[5k] 罗杰·马丁,区分战略选择中的“如何赢”与“能力”(2021),Medium 文章,属于“玩得赢实战见解”系列
[5l] 罗杰·马丁,企业战略与业务单元战略(2022),Medium 文章,属于“玩得赢实战见解”系列
[6] statista,作为经济支柱的中小企业(2024),德语网站
[7] 罗杰·马丁的网站(2024)
[8] A.G. 拉夫雷和罗杰·L·马丁,玩得赢(2013),由哈佛商业评论出版社出版的书籍
[9] 罗杰·马丁,计划不是战略(2022),视频
[10] Roger Martin, Jennifer Riel, 关于管理职能,你需要知道的唯一一件事(2019),发表于《哈佛商业评论》的文章
[11] IDEO U, 激活战略(2024),网站
[12] Tableau, 西南航空利用 Tableau 保持准时航班并优化机队表现(2024),网站
[13] Jens Linden, 数据急救包(2022),LinkedIn 帖子
[14] Caroline Carruthers 和 Peter Jackson, 《首席数据官的操作手册》第 2 版(2020),Facet Publishing 出版的书籍
[15] Caroline Carruthers 和 Peter Jackson, 《数据驱动的商业转型》(2019),Wiley 出版的书籍
[16] Jens Linden, 数据货币化——数据战略的‘Where to Play’(2024),LinkedIn 帖子
除非另有说明,所有图片均为作者提供。
天才综合症:模式识别是否等同于智能?
|LLM|智能|推理|
探索人工智能的极限:为什么掌握模式并不等同于真正的推理
·发表于 Towards Data Science ·阅读时间 13 分钟·2024 年 10 月 31 日
--

该图像由作者使用 DALL-E 生成
我几乎从未见过一个能够进行推理的数学家。——柏拉图
推理得出一个结论,但除非心智通过经验之路发现它,否则无法使结论确定。——罗杰·培根
大型语言模型(LLMs) 展现了显著的能力,特别是在自然语言处理的经典任务中(例如问答)。令人惊讶的是,它们在需要推理的复杂任务中也表现出进步(例如编程和数学)。这些能力长期以来被认为是人类独有的。因此,声称 LLMs 能够解决需要推理的任务,引发了激烈的辩论。
大型语言模型(LLMs)真的能推理吗? 还是它们只是复杂的模式匹配工具?
人工智能首个诺贝尔奖背后的科学
物理学与机器学习如何携手赢得 2024 年诺贝尔物理学奖
·发表于数据科学之路 ·阅读时长 11 分钟·2024 年 10 月 16 日
--

作者本人创作的作品,展示了霍普菲尔德网络中的诺贝尔奖。背景图像来源:Maxim Tajer,诺贝尔奖金币图像来源:hyperionforge
2024 年诺贝尔物理学奖已于 2024 年 10 月 4 日公布,今年的奖项颁给了霍普菲尔德教授和杰弗里·辛顿教授。但这次略有不同,奖项并非授予关于自然世界的通常发现,而是颁给了一些更具人工性质的东西:
“因奠定了通过人工神经网络实现机器学习的基础性发现和发明。”
强调了两种机器学习(ML)模型:霍普菲尔德网络和玻尔兹曼机。这可能让一些人感到惊讶,因为机器学习看起来与物理学相距甚远。然而,许多早期的基础机器学习概念其实受到物理系统的启发。
这项奖项的意义是什么?我认为它是为了突出最近在生成式人工智能(文本/图像/视频生成)方面的进展,并提醒我们,这些现代模型的基础源于物理学。更具体地说,霍普菲尔德网络和玻尔兹曼机可以被视为一些最早的生成模型,它们借用了自然界的物理原理……
猫头鹰的秘密网络

向国际猫头鹰意识日献上的数据致敬
·发布于 Towards Data Science ·阅读时长 6 分钟 ·2024 年 8 月 4 日
--
你知道 8 月 4 日是国际猫头鹰意识日吗?直到我无聊地浏览一些可爱的猫头鹰表情包,才发现这个网站。随后,因为我们最近在花园里发现了一个可爱的猫头鹰家庭,我决定在维基百科上查阅一下它们 — 结果让我惊讶的是,维基百科上竟然记录了254 种猫头鹰物种,这是我们最喜爱的免费开放知识来源。接下来的唯一合乎逻辑的步骤,就是将这些信息转化为数据可视化,更好地理解国际猫头鹰的分布情况。
具体来说,我将自动下载猫头鹰物种的完整列表,然后获取它们的维基百科简介。接着,我将使用文本匹配和 NetworkX 图形分析库提取猫头鹰物种的相似性网络,并进行可视化。通过这种方式,我们将得到猫头鹰物种的可视化表示,这将大大简化我们解读不同物种之间关系的过程。
此外,尽管这里的话题是猫头鹰,但这些方法和步骤同样适用于任何我们希望覆盖并转化为知识图谱的主题,依赖于公开可用的维基百科数据库。
所有图片均由作者创作。
1. 收集猫头鹰物种列表
成功创建数据故事的秘诀,而非垃圾仪表盘
了解用数据讲故事如何改变游戏规则!
·发布于Towards Data Science ·阅读时长 5 分钟·2024 年 1 月 4 日
--

图片来自Path Digital在Unsplash
在当今迅速扩展的数据世界中,组织越来越多地赋予每个团队成员访问和使用数据进行明智决策的能力。更多的数据和业务分析师以及顾问的工作是将数据与业务连接起来,并从中提取最有意义的见解。
作为一名数据顾问,我每周都会处理一个或另一个仪表盘。有时,我会被要求在一天内为高层管理人员快速制作仪表盘,但我知道这些仪表盘永远不会再被参考——我喜欢称之为垃圾仪表盘(灵感来自几年前我看到的一条推文)。
现在,数据专业人士比以往任何时候都更需要转向用数据讲故事,而不仅仅是创建漂亮的仪表盘。用数据讲故事的核心是通过数据分析、可视化、定性分析和商业背景,呈现一个精心构建的叙事。因此,你不仅仅是在展示炫酷的界面,而是在传达数据所暗示的洞察。
仪表盘与数据故事的区别 —
仪表盘 — 仪表盘是将多个图表和可视化内容汇集在一个屏幕上的集合。仪表盘…
你不知道的 Python Secrets

图片由Tayeb MEZAHDIA提供,来自Pixabay
为什么以及在何时我们不应使用“random”,而应使用“secrets”?
·发表于Towards Data Science ·阅读时长:7 分钟·2024 年 1 月 21 日
--
如果你从事数据科学和分析领域的工作,我猜你一定像我一样经常使用 Python 中的random模块。的确,在我们需要进行一些模拟、数据采样和各种其他统计算法时,它非常有用。
然而,Python 中还有一个叫做secrets的内置模块,我相信它的知名度远不如random。它几乎可以做和random一样的事情,甚至有些函数的名称完全相同。
为什么会有这样一个模块?我们什么时候应该使用secrets而不是random?请继续阅读本文以获取答案。
在开始之前,与我其他大多数文章不同,这篇文章没有“安装”部分。secrets模块是 Python 内置的。所以,我们可以直接使用它,不必担心安装问题。
1. 安全生成随机数

图片由AndreasAux提供,来自Pixabay
让我们先看看 Secrets 模块中的一些基本随机函数。然后,我会解释为什么在某些场景下我们不应该使用random。
阻碍(大多数)数据科学家的技能
7 个提升我作为数据科学家的沟通技巧
·发布于Towards Data Science ·阅读时长 7 分钟·2024 年 6 月 13 日
--
当我开始从事数据科学时,我非常专注于学习 Python、机器学习、统计学等内容。虽然这些是必要的,但还有一项技能是大多数人往往忽视的,这对他们来说是个危险的忽视——沟通能力。在这篇文章中,我讨论了为什么数据科学家必须成为有效的沟通者,并分享了 7 个帮助我提高这项技能的建议。

图片由Mitchel Lensink提供,来自Unsplash
在研究生时期,物理系举办了每周的学术报告会,邀请嘉宾演讲者来展示他们的研究。通常的情况是,大部分听众理解了第一张幻灯片(标题)和第二张(议程),但之后就迷失了。
同样的情况也发生在数据科学领域,当非技术背景的利益相关者参加(大部分)数据科学家的报告时,他们可能会说:“在你开始讲解训练集和测试集划分以及 AUC 时,我就开始迷糊了。”
尽管这似乎是数据科学中无法避免的现实,但我已经学到,能够更有效地解释这些话题不仅是可能的,而且对于推动数据科学职业生涯至关重要。
在这里,我分享了我用来晋升、争取客户以及向10 万+人解释人工智能的关键沟通技巧。
编程中使用 AI 的更聪明方式
避免将过时的方法融入你的编码工作流,通过超越 ChatGPT 来避免这种情况。
·发表于Towards Data Science ·7 分钟阅读·2024 年 8 月 29 日
--

由Leonardo.ai生成的 AI
被付费墙挡住了吗? 免费阅读!
自 2022 年 ChatGPT 发布以来,开发者分成了两派:一派禁止在编程中使用 AI,另一派则拥抱 AI。许多程序员,尤其是经验更丰富的程序员,不仅不使用 AI 生成的代码,还禁止他们的团队这么做。他们反对使用 AI 进行编程的理由是可以理解的,“AI 生成的代码不可靠。”
即使你不赞同这种反 AI 编程立场,你一定在使用 AI 进行编程时遇到过挑战、障碍或问题。关键是找到策略,以正确的方式利用 AI 为自己带来优势。
我们许多人还在使用一种简单但过时的 AI 策略,这种策略在两年前曾经有效。我们正在用厨房刀砍树。
我将从两个主要问题开始谈论开发者使用 AI 时遇到的挑战,并提出四个可靠的 AI 解决方案,无论你是哪种类型的开发者,采用这些方案后,你会在使用大型语言模型(LLMs)时脱颖而出。
堆叠集成方法
使用 scikit-learn 理解堆叠
·发布于 Towards Data Science ·阅读时间 13 分钟·2024 年 2 月 29 日
--
探索机器学习中堆叠的力量——一种将多个模型组合成一个强大的预测器的技术。本文从基础到高级技术,探讨了堆叠,揭示了它如何融合多种模型的优点以提高准确性。无论你是堆叠的新手,还是在寻找优化策略,这篇指南都提供了实践性的见解和技巧,帮助你通过 scikit-learn 提升预测建模能力。
尽管本文基于 scikit-learn,但我在文末提供了一个纯 Python 类,它实现并模仿了 scikit-learn 的堆叠模型。审视这个纯 Python 实现是一个极好的方式,可以帮助你检验和测试自己的理解。
在这篇文章中,我们将看到:
-
堆叠是机器学习中集成技术的一部分
-
堆叠如何在内部工作以提供预测
-
如何进行模型拟合
-
什么是“重堆叠”
-
如何创建多层堆叠模型
-
我们为什么以及如何检查基础模型的性能
-
如何调优和优化堆叠模型的使用

图片来自 Brigitte Tohm 在 Unsplash
如果你喜欢或想学习使用 scikit-learn 进行机器学习,请查看…
统计显著性骗局
深入剖析科学最爱工具的缺陷
·发布于Towards Data Science ·13 分钟阅读·2024 年 11 月 9 日
--

来源:unsplash.com
统计显著性就像是研究界的快餐车。你驶近研究,拿到你的“显著性套餐”,然后——你就有了一个可以和所有朋友分享的精彩结论。而这不仅方便读者,也让研究人员的工作变得更轻松。为什么要做复杂的推销,当你只需说两个字就能搞定呢?
但这里有一个陷阱。
那些我们方便忽略的复杂公式和细节?它们才是问题的核心。当研究人员和读者过于依赖某一种统计工具时,我们可能会犯下大错,正如那次几乎颠覆物理法则的错误。
2011 年,著名的 CERN 实验室的物理学家宣布了一项震惊世界的发现:中微子可能比光速还快。这一发现威胁到推翻爱因斯坦的相对论理论——现代物理学的基石。研究人员对他们的结果充满信心,突破了物理学严格的统计显著性标准——99.9999998%。案子就这样结了,是吧?
其实不然。随着其他科学家对实验进行了审查,他们发现了方法学上的缺陷,最终未能复制实验结果。尽管这一原始发现具有令人印象深刻的“统计显著性”,但结果证明是错误的。
在这篇文章中,我们将深入探讨为什么你不应该本能地信任一个统计显著的研究结果。更重要的是,为什么你不应该习惯性地忽视那些非统计显著的结果。
TL;DR
统计学显著性的四个关键缺陷:
-
它是凭空捏造的:统计学显著性/不显著性边界往往是凭空捏造的,或者懒散地取自 95%置信度的一般标准。
-
它并不意味着(大多数)人们认为的那样:统计学显著性并不意味着“有 Y%的机会 X 是真的”。
-
这很容易被操纵(而且经常被操控):由于大规模实验,随机性经常被标记为具有统计学显著性。
-
这与结果的重要性无关:统计学显著性与差异的意义无关。
缺陷 1:这是凭空捏造的
统计学显著性只是人类创造的一条分界线,没有任何数学支持。想想看,这个通常被认为是客观度量的东西,本质上完全是主观的。
数学部分通过置信度的数值度量在决定显著性之前提供了一步。最常用的形式是假设检验,它被称为p 值。这提供了一个实际的数学概率,说明测试数据结果不仅仅是由随机性造成的。
例如,p 值为 0.05 意味着有 5%的机会看到这些数据点(或更极端的情况)是由于随机机会,或者说我们有 95%的信心认为结果不是由于偶然性。例如,假设你认为一枚硬币是不公平的,偏向正面,即正面朝上的概率大于 50%。你投掷硬币 5 次,每次都落在正面。假设硬币是公平的,那么它出现正面的概率为 1/2 x 1/2 x 1/2 x 1/2 x 1/2 = 3.1%,这是纯粹由随机性引起的机会。
但这就足以说明它在统计学上显著吗?这取决于你问谁。
通常,决定在哪里划定显著性边界的人,比起底层数据本身,更能影响一个结果是否显著。
鉴于这一主观性的最终步骤,在我自己的分析中,我通常会向研究的读者提供置信度百分比,而不是二元的显著/不显著结果。最终步骤太过依赖个人观点。
怀疑者:“但是有标准来决定统计学显著性。”
我常常听到针对我上面论点的反驳(我谈论这个话题不少——这让我的学术研究女友很高兴)。对此,我的回应通常是:
我:“当然,如果你必须遵守一个特定的标准,例如出于监管或学术期刊发布的原因,那么你别无选择,只能遵循这个标准。但如果不是这样,那就没有理由不遵守。”
怀疑者:“但是有一个普遍的标准。那就是 95%的置信度。”
在那个对话时刻,我尽量不翻白眼。仅仅因为 95% 是常规标准,就决定测试的统计显著性点为 95%,坦率地说,这是懒惰的做法。它没有考虑正在测试的内容的上下文。
在我的日常工作中,如果我看到有人在进行实验时使用 95% 的显著性阈值,却没有提供背景说明,我会觉得这是个警告信号。这表明这个人要么不理解选择的含义,要么不关心实验的具体业务需求。
一个例子可以最好地解释为什么这如此重要。
假设你在一家科技公司担任数据科学家,UI 团队想知道:“为了最大化点击率(CTR),我们应该选择红色还是蓝色作为‘订阅’按钮的颜色?”UI 团队对这两种颜色都没有偏好,但必须在本周结束前选择一个颜色。经过一些 A/B 测试和统计分析,我们得出了结果:

图片由作者创作。
按标准做事的数据科学家可能会回来告诉 UI 团队,“不幸的是,实验发现红色按钮和蓝色按钮的点击率没有统计显著差异。”
这是一个糟糕的分析,完全是因为最后的主观判断。如果数据科学家主动去了解上下文,批判性地理解“UI 团队对颜色没有偏好,但必须在本周结束前选择一个”的背景,那么你应该把显著性点设置得非常高,理论上为 1.0,即统计分析不再重要,UI 团队可以选择那个具有最高点击率的颜色。
鉴于数据科学家等可能没有完整的背景信息来确定最佳显著性点,最好(也更简单)将这个责任交给那些掌握完整业务背景的人——在这个例子中,就是 UI 团队。换句话说,数据科学家应该告诉 UI 团队,“实验结果显示蓝色按钮的点击率更高,且有 94% 的置信度证明这一结果不是由随机机会造成的。” 确定显著性的最后步骤应该由 UI 团队做出。当然,这并不意味着数据科学家不应该教育团队什么是“94% 的置信度”,以及清楚地解释为什么统计显著性最好由他们来决定。
缺陷 2:它并不意味着(大多数)人们认为的那样
假设我们生活在一个稍微完美的世界里,第一点的问题不再存在。那个“沙线”图形永远是完美的,万岁!假设我们想要进行一个实验,统计显著性线设置为 99% 的置信度。几周后,最终我们得出了结果,统计分析发现它是统计上显著的,再次万岁!但这到底意味着什么呢?
在假设检验的情况下,常见的看法是假设正确的概率为 99%。这完全错误。它的意思是,在这个实验中,随机性导致观察到如此极端的数据或更极端数据的概率为 1%。
统计显著性并没有考虑实验本身是否准确。以下是统计显著性无法捕捉到的一些例子:
-
样本质量:所采样的总体可能存在偏差或不具代表性。
-
数据质量:测量错误、缺失数据或其他数据质量问题未得到解决。
-
假设有效性:统计测试的假设(如正态性、独立性)可能被违反。
-
研究设计质量:实验控制差,没有控制混杂变量,测试多个结果而没有调整显著性水平。
回到引言中提到的例子。经过未能独立复制初步发现的失败后,原始 2011 年实验的物理学家宣布,他们发现了测量设备主时钟中的一个漏洞,即数据质量问题,这导致他们撤回了最初的研究。
下次你听到一个与常识相悖的统计学显著性发现时,不要那么快相信它。
缺陷 3:很容易被破解(且经常被破解)
鉴于统计显著性完全关注某事因随机性发生的可能性,一个更关心获得统计学显著结果而不是揭示真相的实验者很容易操控系统。
从两个骰子掷出两个一的概率是(1/6 × 1/6) = 1/36,或 2.8%;这是一个非常罕见的结果,许多人会认为它在统计学上显著。但如果我掷出超过两个骰子呢?自然,至少掷出两个一的概率会增加:
-
3 个骰子:≈ 7.4%
-
4 个骰子:≈ 14.4%
-
5 个骰子:≈ 23%
-
6 个骰子:≈ 32.4%
-
7 个骰子:≈ 42%
-
8 个骰子:≈ 51%
-
12 个骰子:≈ 80%*
至少掷出两个骰子得到一相当于:1(即 100%,确定),减去掷出零个一的概率,减去掷出仅有一个一的概率。
P(零个一) = (5/6)^n
P(恰好一个一) = n * (1/6) * (5/6)^(n-1)
n 是骰子的数量
所以完整的公式是:1 — (5/6)^n — n(1/6)(5/6)^(n-1)
假设我进行一个简单实验,最初的理论是一比其他数字更有可能被掷出。我掷了 12 个不同颜色和大小的骰子。以下是我的结果:

图片由作者创建。
不幸的是,我(经过计算的)至少获得两个1 点的希望已经破灭……其实,现在想想,我并不是真的想要两个 1 点。我更感兴趣的是大红色骰子的概率。我相信从它们中掷出六点的机会很大。啊!看起来我的理论是正确的,那两个大红色骰子竟然都掷出了六点!这种情况偶然发生的概率只有 2.8%。非常有趣。接下来,我将根据我的发现写一篇论文,并计划将其发表在一个接受我结果为统计学上显著的学术期刊上。
这个故事听起来可能有些牵强,但现实与此并不如你想象的那样遥远,尤其是在备受尊敬的学术研究领域。事实上,这种事情发生得足够频繁,以至于形成了一个名声,p-hacking就是其中之一。
如果你感到惊讶,深入了解学术体系将能阐明为什么在科学领域中,许多看似与科学方法背道而驰的做法会如此频繁地发生。
在学术界,想要成功地发展事业是非常困难的。例如,在 STEM 学科中,只有0.45%的博士生能成为教授。当然,一些博士生并不想从事学术职业,但大多数人是有这个愿望的(根据这项调查,比例为 67%)。所以,粗略地说,如果你完成了博士学位并希望在学术界发展事业,你成为教授的机会大约是 1%。考虑到这些几率,你需要认为自己是非常杰出的,或者更准确地说,你需要别人这么认为,因为你无法自己雇佣自己。那么,杰出是如何衡量的呢?
也许并不令人意外,学术成功的最重要衡量标准是他们的研究影响力。常见的作者影响力衡量标准包括 h 指数、g 指数和 i10 指数。它们的共同点在于,它们高度关注引用次数,也就是他们的研究成果被其他已发表的工作提及了多少次。知道了这一点,如果我们希望在学术界做得好,我们就需要专注于发表那些可能会被引用的研究。
如果你将研究成果发表在高评级的学术期刊中,你更有可能被引用。而且,由于88%的顶级期刊论文是统计显著的,如果你的研究具有统计显著性,你更有可能被这些期刊接受。这使得许多出于良好意图但又受到职业驱动的学者走上了一条滑坡之路。他们开始时会采用这样的科学方法来撰写研究论文:

使用科学方法进行实验研究发表的决策树。由作者使用Mermaid创建。
但他们最终扭曲了自己的方法论,看起来像是科学的表象——但实际上,他们已经抛弃了正统的科学方法:

最大化每个实验发表成功的决策树。由作者使用Mermaid创建。
由于决策图表让研究者在发现显著结果之后写论文,因此期刊审稿人无法批评实验中的 p-hacking 行为。
这理论上是如此。但现实中真的经常发生吗?
答案是响亮的“是”。事实上,大多数科学研究无法被同行学者重复。无法重复意味着一篇研究论文试图复制另一篇研究论文的实验,但结果却是统计上出乎意料的。通常,原始论文中的统计显著结果在重复实验中并不显著,或者在某些情况下结果的统计显著性方向是相反的!
缺陷 4:它与结果的重要性无关
最后,统计显著性并不关心差异的规模。
从这个角度思考——统计显著性基本上只是告诉你“嘿,这个差异可能不是由于随机偶然”,但它并没有告诉你这个差异在现实世界中是否真的重要。
假设你测试一种新药,并发现它比安慰剂减少了 0.0001%的头痛痛感。如果你在数百万人的样本中进行此测试,那么这个微小的差异可能是统计显著的,因为你的样本量非常大。但……谁在乎 0.0001%的疼痛减少呢?在实际意义上这是毫无意义的!
另一方面,你可能会发现一种药物能减少 5%的疼痛,但尚未进行大规模实验来证明其统计学意义。在医学领域,很可能存在许多这种情况,因为如果所讨论的药物价格便宜,制药公司就没有动力进行实验,因为大规模的医学测试非常昂贵。
这就是为什么在讨论影响效果(差异有多大)时,必须与统计学意义分开来看。在现实世界中,你需要两者——既要有不太可能是随机的差异,又要有足够大的差异,才能真正产生影响。
这一错误反复出现的一个例子是当发现某些致癌物(即导致癌症的物质)时。例如,2015 年《卫报》的一篇文章提到:
“世界卫生组织表示,培根、火腿和香肠与香烟并列为癌症的主要原因,将腌制和加工肉类与石棉、酒精、砒霜和烟草放在同一类别。”
这完全是误导信息。实际上,培根、火腿和香肠与石棉、酒精、砒霜和烟草属于同一类。然而,这些类别并不表示致癌物的影响规模,而是世界卫生组织对这些物质是否为致癌物的信心程度,即统计学意义。
由加工肉类引起的癌症病例规模存在争议,因为尚未进行任何随机对照试验(RCT)。支持加工肉类导致癌症的最具破坏性的研究之一是在 2020 年英国进行的一项观察性研究。该研究发现,平均每天摄入超过 79 克红肉和加工肉类的人,患肠癌的风险比每天摄入不到 11 克的人高出 32%。
然而,要了解真正的风险,我们需要了解有多少人处于患肠癌的风险中。在研究中,每 10,000 个每天摄入不到 11 克加工和红肉的人中,有 45 人被诊断为肠癌,而每天摄入 79 克加工和红肉的人中则有 59 人被诊断为肠癌。也就是说,每 10,000 人中额外增加了 14 例肠癌病例,或者 0.14%。在英国,肠癌的生存率为53%,因此粗略估算,加工肉类中致癌物质导致你死亡的几率是 0.07%。
将此与《卫报》提到的另一种物质——烟草进行比较。癌症研究表示:
“烟草是英国癌症和死亡的最大可预防原因,也是全球最大可预防的疾病和死亡原因之一。2021 年,烟草导致英国约 75,800 人死亡——约占所有死因的十分之一(11%)。”
首先,哇。别抽烟。
其次,由烟草引起的癌症死亡率是 11%/0.07% = 157倍高于加工肉类!回到文章中的引用:“培根、火腿和香肠与香烟一起,成为癌症的主要原因。”简单来说,这是假新闻。
总结
总之,虽然统计学显著性在验证定量研究中有其作用,但理解其严重局限性至关重要。
作为读者,我们有责任以批判性的眼光看待“统计学显著性”的说法。下次你遇到宣扬“统计学显著”结果的研究或文章时,不妨花点时间问问自己:
-
统计学显著性阈值是否适合该情境?
-
这项研究设计和数据收集过程有多稳健?
-
研究人员是否可能从事了 p-hacking 或其他可疑的做法?
-
该效应大小的实际意义是什么?
通过提出这些问题并要求围绕统计学显著性进行更为细致的讨论,我们可以帮助推动更加负责任和准确地使用这一工具。
随时间推移的分析
我实际上认为,统计学显著性之所以获得如此过度的突出地位,主要原因在于这个名称。人们将“统计”与数学和客观联系在一起,将“显著性”与“重要性”联系在一起。我希望这篇文章能够说服你,这些关联实际上只是谬误。
如果科学界和更广泛的社区希望解决过度突出的问题,他们应该认真考虑重新命名“统计学显著性”。也许可以叫做“机会阈值检验”或“非随机置信度”。不过,这样做会失去其“大麦克”便利性。
RLHF 的故事:起源、动机、技术和现代应用
如何通过人类反馈学习彻底改变生成式语言模型……
·发表于Towards Data Science ·31 分钟阅读·2024 年 2 月 29 日
--

(图片由Towfiqu barbhuiya拍摄,来自Unsplash)
长期以来,人工智能领域一直利用不同风格的语言模型(例如,n-gram 模型,RNNs,transformers等)来自动化生成性和判别性自然语言任务。2018 年,随着 BERT [10]的提出,这一研究领域迎来了前所未有的关注,BERT 展示了 transformer 架构、自监督预训练和监督迁移学习的强大结合。事实上,BERT 在当时应用的每个基准测试中都创下了新的最先进性能。尽管 BERT 不能用于生成任务,但我们通过T5 [11]的提出看到了监督迁移学习在这一领域的有效性。然而,尽管取得了这些成就,这些模型与如今如 GPT-4 等 LLM 的生成能力相比仍显得微不足道。要创建这样的模型,我们需要远远超越监督学习的训练技术。
“我们的目标是以最有可能造福全人类的方式推进数字智能。” — OpenAI 创始声明 (2015 年 12 月)
Google Cloud Dataflow 背后的流式处理模型
在无界数据处理中的正确性、延迟和成本平衡
·发表于 Towards Data Science ·14 分钟阅读·2024 年 4 月 27 日
--

图片由作者创建。
本文最初发布于 https://vutr.substack.com。
目录
-
在我们继续之前
-
论文中的介绍。
-
Dataflow 模型的细节。
-
模型的实现与设计。
简介
Google Dataflow是一个完全托管的数据处理服务,提供无服务器统一的流式和批量数据处理。当处理流式数据工作负载时,它是 Google 推荐的首选服务。该服务承诺无论工作负载多大,都能确保正确性和延迟。为了实现这些特性,Google Dataflow 基于一个专用的处理模型——Dataflow,该模型源自 Google 多年来的研究和开发。本文是我在阅读论文后做的笔记:The Dataflow Model: A Practical Approach to Balancing Correctness, Latency, and Cost in Massive-Scale, Unbounded, Out-of-Order Data Processing。如果你想深入了解流式处理,我强烈推荐这篇论文。它包含了 Google 在引入 Dataflow 模型以应对其全球规模的流式数据处理需求过程中获得的所有经验和见解。尽管这篇论文写于 2015 年,但我相信它的贡献永不过时。
注意:本文发表于 2015 年,因此一些细节可能已经发生变化或更新。如果你有任何反馈或能够补充我博客内容的信息,欢迎评论。
在我们继续之前
为了避免更多的混淆
-
Dataflow是谷歌的流处理模型。
-
Apache Beam 允许用户基于 Dataflow 模型定义处理逻辑。
-
Google Cloud Dataflow是来自 Google Cloud 的统一处理服务;你可以认为它是 Apache Beam 管道的目标执行引擎。
工作流:你可以使用 Apache Beam 定义统一的处理逻辑,并决定将管道运行在你想要的执行引擎上,比如 Google Dataflow、Spark、Flink等。
在深入探索 Dataflow 模型之前,以下几节将介绍一些背景信息、挑战和概念。
论文简介
在论文撰写时,像MapReduce及其“亲戚”如Hadoop、Pig、Hive或Spark等数据处理框架允许数据消费者大规模处理批量数据。在流处理方面,像MillWheel、Spark Streaming或Storm等工具也开始支持用户。然而,这些现有模型在一些常见的用例中并未满足要求。
考虑一个例子:一个视频流媒体提供商的商业收入来自于向广告商收费,费用是根据广告观看量来计算的。他们想知道每天应向每个广告商收费多少,并汇总关于视频和广告的统计数据。此外,他们还希望对大量历史数据进行离线实验。他们希望了解他们的视频被观看的频率和时长,以及观看这些视频的内容/广告和观众的群体。所有这些信息都必须快速提供,以便在接近实时的情况下调整他们的业务。处理系统还必须简单且灵活,以适应业务的复杂性。他们还需要一个能够处理全球规模数据的系统,因为互联网使公司能够接触到比以往更多的客户。以下是谷歌一些人关于当时数据处理系统状况的观察:
-
批处理系统如 MapReduce, FlumeJava (谷歌内部技术),以及 Spark 无法确保延迟 SLA,因为它们需要等待所有数据输入适配到批处理后才能进行处理。
-
提供可扩展性和容错性的流处理系统,在表达力或正确性方面有所不足。
-
许多系统无法提供精确一次语义,这会影响正确性。
-
其他系统缺乏进行窗口处理所需的基本操作,或提供的窗口语义仅限于基于元组或处理时间的窗口(例如, Spark Streaming)*)
-
大多数基于事件时间窗口的实现依赖于排序或具有有限的窗口触发条件。
-
MillWheel 和 Spark Streaming 足够可扩展、容错性强且低延迟,但缺乏高级编程模型。
他们总结了上述所有模型和系统的主要弱点是假设无界输入数据最终会完成。当面对今天庞大且高度无序的数据时,这种方法已经不再合理。他们还认为,任何解决多样化实时工作负载的方法必须提供简单但强大的接口,以根据特定的使用场景平衡正确性、延迟和成本。从这个角度来看,本文对统一流处理模型做出了以下概念性贡献:
-
允许在无界、无序的数据源上计算事件时间顺序(事件发生时)的结果,并提供正确性、延迟和成本属性的可配置组合。
-
在四个相关维度上分离管道实现:
正在计算哪些结果?
它们在事件时间中的计算位置。
当它们在处理时间期间被具体化时,
早期结果如何与后续改进相关?
- 将数据处理的逻辑抽象与底层物理实现层分离,允许用户选择处理引擎。
在本博客的其余部分,我们将看到 Google 如何促进这一贡献。在我们进入下一部分之前,最后提一点:Google 指出,“这个模型没有什么神奇之处。” 这个模型并不会让你计算量大的任务突然加速;它提供了一个通用框架,允许简单表达并行计算,这并不依赖于像 Spark 或 Flink 这样的特定执行引擎。
无界/有界

图片由作者创建。
论文的作者使用“无界/有界”这个术语来定义无限/有限数据。他们避免使用流处理/批处理术语,因为这些术语通常意味着使用特定的执行引擎。无界数据指的是没有预定义边界的数据,例如,活跃电商应用的用户交互事件;数据流只有在应用不活跃时才会停止。而有界数据指的是可以通过明确的开始和结束边界来定义的数据,例如,从操作数据库导出的每日数据。
为了继续介绍部分,我们将回顾论文中使用的一些概念。
窗口化
组织者
窗口化将数据划分为有限的块。通常,系统使用时间概念将数据组织到窗口中(例如,过去 1 小时内的所有数据将属于一个窗口)。窗口中的所有数据作为一个组进行处理。用户需要对窗口抽象进行分组操作:聚合或时间限制操作,以处理无界数据。另一方面,一些对无界数据的操作不需要窗口概念,比如过滤、映射或内连接。窗口可以是对齐的,例如,应用于给定窗口的所有数据,或者是不对齐的,例如,仅应用于该窗口中特定数据子集的操作。窗口有三种主要类型:

作者创建的图像。
-
固定窗口: 窗口大小为静态定义,例如,按小时划分的窗口。
-
滑动窗口: 窗口由窗口大小和滑动周期定义,例如,每 5 分钟开始的 30 分钟窗口。
-
会话: 窗口捕捉数据子集中的一段活动期,在这种情况下,是按键捕捉。通常,它们通过超时间隔定义。
时间域
在处理与时间相关的事件数据时,需要考虑两个时间域:
-
事件时间:事件本身发生的时间。例如,如果系统设备在 11:30 记录了你购买游戏物品,这个时间就被视为事件时间。
-
处理时间:在处理过程中,事件在任何给定时刻被观察到的时间。例如,购买的游戏物品在 11:30 被记录,但仅在 11:35 到达流处理系统;这个“11:35”就是处理时间。
根据这个定义,事件时间永远不会改变,但处理时间会随着每个事件在管道步骤中流动而不断变化。这是在分析事件发生时刻时的一个关键因素。事件时间和处理时间之间的差异被称为时间域偏差。偏差可能由多种潜在原因引起,例如通信延迟或每个管道阶段处理时花费的时间。像水印这样的指标是可视化偏差的好方法。对于本文,作者考虑了管道处理过的事件时间的下水印。这些水印提供了一种概念,告诉系统:“在这个时间点之前的事件时间不会再出现在管道中。” 水印不仅用于观察时间域之间的偏差,还用于监控整体系统。在一个理想的世界中,偏差始终为零;我们可以在事件发生的第一时间就处理所有事件。

作者创建的图像。
在接下来的章节中,我们将学习数据流模型的细节。
核心原语
模型有两个核心转换操作,作用于 (key, value) 对;这两种转换都可以作用于有界和无界数据:

图像由作者创建。
-
ParDo用于通用并行处理。它将使用提供的用户定义函数(在 Dataflow 中称为DoFn)处理每个输入元素,该函数可以为每个输入元素生成零个或多个输出。输入不需要是无界集合。 -
GroupByKey用于基于定义的键进行分组操作。
ParDo 对每个元素进行操作,因此它可以转换为无界数据。GroupByKey 在将数据发送到下游步骤之前,会收集给定键的所有数据。如果输入源是无界的,那么无法定义它何时结束。标准解决方案是数据窗口化。
窗口化
支持分组的系统通常会重新定义其 GroupByKey 操作为 GroupByKeyAndWindow。作者在这方面的重要贡献是未对齐的窗口。第一个是将所有窗口化策略视为来自数据流模型的未对齐,并允许在需要时自定义调整以应用对齐的窗口。第二个是任何窗口化过程都可以分解为两个相关的操作:

图像由作者创建。
-
AssignWindows 将元素分配到零个或多个窗口。从模型的角度来看,窗口分配在每个窗口中创建组件的新副本。
-
MergeWindows 在分组时合并窗口。这允许在数据到达并被分组时,基于时间构建数据驱动的窗口。窗口合并作为
GroupByKeyAndWindow操作的一部分进行。我们可以通过以下示例来更好地理解:
触发器与增量处理
虽然支持未对齐的窗口,事件时间窗口带来了另一个挑战:需要告诉系统何时发出窗口的结果,因为数据可能以无序的方式出现在管道中。使用事件时间进度指标(如上所述的水印)的初步解决方案存在一些缺点:
提醒一下,免得你滚动回去看:水印是一个指示器,告诉系统“在这个时间点之前没有更多的事件时间较早的数据会出现在管道中。”例如,在给定时间,水印为“11:30”,这意味着不再会有事件时间早于 11:30 的数据出现。
-
它们有时太快:这种行为意味着延迟数据可能会落后于水印。
-
它们有时太慢:这种行为可能导致整个管道被延迟,等待一个缓慢的数据点。
这导致了以下观察:仅使用水印决定何时发出窗口的结果可能会增加延迟(当水印较慢时),或影响管道的准确性(如果水印过快,可能会漏掉一些数据)。作者在 Lambda 架构中观察到(该架构有两个独立的管道,流式和批处理,两个管道的结果最终会汇聚在一起),该范式并没有通过更快地提供正确答案来解决完整性问题;相反,它提供了来自流式管道的低延迟结果估算,然后承诺通过批处理管道提供正确的结果。他们指出,如果我们希望在单个管道中实现相同的目标,我们需要一种机制,为任何给定的窗口提供多个面板(答案)。这个功能称为触发器,允许用户指定何时触发给定窗口的输出结果。这里有一个插图,帮助你理解触发器和 Lambda 架构中的语义之间的相似性。

由作者创建的图像。
作者介绍的系统支持以下触发器实现:

由作者创建的图像。
-
在如水印这样的完成估算时触发。
-
在处理时间点触发。
-
基于数据到达特征(如计数、字节、数据标记、模式匹配等)进行触发。
-
支持使用循环、序列或逻辑组合(与、或)实现的组合。
-
用户可以利用执行运行时的底层原语(例如,水印计时器、处理时间计时器)和外部信号(例如,数据注入请求、外部进度度量)来定义触发器。
除了控制系统何时发出窗口的结果外,触发机制还提供了一种方法,通过以下精细化模式控制给定窗口的面板(答案)之间的关系:

由作者创建的图像。
-
丢弃: 在触发时,系统丢弃所有内容的窗口。后续结果与之前的结果没有关系。此模式在下游消费者需要各个触发器的值独立时非常有用。在数据缓冲的空间效率方面,这也是最有效的选项。
-
累积: 在触发时,系统将窗口内容保持在持久状态;后续结果与之前的结果相关联。当下游消费者期望在接收同一窗口的多个结果时,用新结果覆盖旧值时,这种模式非常有用。这也是 Lambda 架构系统中使用的模式,其中流式管道输出低延迟结果,随后被批处理管道的结果覆盖。
-
积累与撤回: 在触发时,除了积累语义外,发出的结果副本也会存储在持久状态中。当窗口在未来再次触发时,首先会发出对先前值的撤回,然后才是新值。
下一部分将描述 Google 如何实现和设计 Dataflow 模型。
实现
论文的作者表示,他们已经使用 FlumeJava 在内部实现了这个模型,这是一个 Java 库,使得开发、测试和运行高效的数据并行管道变得容易。MillWheel 作为底层的流执行引擎。此外,Google Cloud Dataflow 的外部重新实现主要在论文撰写时已经完成。有趣的是,核心的窗口和触发代码相当通用,批处理和流处理实现之间有很大一部分是共享的。
设计原则
Dataflow 模型的核心原则:
-
永远不要依赖任何完整性的概念。
-
灵活适应已知用例的多样性以及未来可能出现的用例。
-
它不仅在每个预期执行引擎的上下文中有意义,而且还增加了价值。
-
鼓励实现的清晰性。
-
支持在数据发生的上下文中进行强有力的数据分析。
激励经验
在设计模型时,他们积累了与 FlumeJava 和 MillWheel 的实际经验。那些运作良好的部分会在模型中得到体现;那些不太理想的部分则会推动方法的改变。以下是一些影响设计选择的经验:
-
统一模型: 这个设计选择的最初动机是,默认情况下一个巨大的管道以流模式在 MillWheel 上运行,但对于大规模回填有一个专门的 FlumeJava 批处理实现。另一个动机来自 Lambda 架构的经验,其中一个客户在 MillWheel 中运行流管道,并使用夜间的 MapReduce(批处理)生成真值。他们发现,随着时间的推移,客户逐渐不再信任管道之间弱一致性的结果。
-
会话 是 Google 内部一个关键的使用案例。这个机制在许多场景中都得到了应用,包括搜索、广告、分析、社交媒体和 YouTube。任何关心在一段时间内关联用户活动波动的用户都会利用会话。因此,支持会话成为模型设计中不可或缺的一部分。
-
触发器、累积与撤回: 两个在 MillWheel 上运行账单管道的团队遇到了问题,这些问题促使了模型的部分设计。那时的最佳实践是将水印作为完成度度量,并针对延迟数据使用额外的临时逻辑。由于缺乏更新和撤回系统,处理资源利用率统计的团队决定自行构建解决方案。另一个账单团队则遇到了由慢速数据处理单元引起的水印滞后问题(慢速单元影响整体作业完成性能)。这些不足成为了设计的重要推动因素,并使设计重点从追求完整性转向随时间适应性。这导致了两个决策:触发器,允许灵活指定何时生成结果,以及通过累积支持增量处理。
-
水印触发器: 许多 MillWheel 管道计算聚合统计信息。大多数情况下,它们并不要求 100% 的准确性;它们关心的是在合理的时间内能够获得大致完整的数据视图。由于通过水印处理结构化输入源(如日志文件)时能够实现较高的准确性,客户发现水印在每个窗口触发单一、精确的聚合结果方面非常有效。
-
处理时间触发器: 推荐管道使用处理时间定时器发出其输出。这些系统定期更新部分数据视图,比起等到基于水印的大致完整视图准备好,它们更具价值。这也意味着水印的概念不会影响其余数据输出的及时性。
-
数据驱动和复合触发器: 用于追踪 Google 网页搜索趋势的异常检测管道中的不同检测系统促使了数据驱动触发器的设计。这些系统观察查询流并计算统计估计,以检查是否存在异常波动。当它们认为波动正在发生时,会发出开始记录;当它们认为波动已经停止时,会发出停止记录。这也成为了触发器组合的推动因素,因为实际上系统同时运行多个差异检测器,并根据一组逻辑多路复用输出。
Outro
在本周的博客中,我们讨论了数据流模型的设计原则和实现,该模型是著名的 Google Cloud Dataflow 服务背后的核心。如果你想深入了解该模型,我强烈推荐阅读这本书:流处理系统:大规模数据处理的“什么”、“哪里”、“何时”和“如何”,或者阅读论文作者之一的两篇博客:流处理 101 和 流处理 102。希望我的工作能为那些想了解流处理世界的人带来一些价值。
下次博客见!
参考文献
[1] Google,数据流模型:在大规模、无界、无序数据中平衡正确性、延迟和成本的实用方法(2015 年)。
我的通讯是一封每周发布的博客风格邮件,在其中我记录我从比我聪明的人那里学到的东西。
所以,如果你想和我一起学习和成长,请在这里订阅: https://vutr.substack.com.
人工模仿智能在专业领域中的挣扎
为什么通向真正智能的道路要经过本体论和知识图谱
·发布于 Towards Data Science ·阅读时间 15 分钟·2024 年 5 月 8 日
--
关注我的人可能还记得一年前,我以“脾气暴躁的风险经理”这一化名发过一篇类似的 AI rant 。现在我回来了,比以前更加暴躁,带来了具体的例子,但也有解决方案的想法!

来源:作者拼贴
介绍
-
大型语言模型(LLMs)如 ChatGPT,在用自然语言讨论一般话题方面展现出令人印象深刻的能力。
-
然而,它们在医学、金融和法律等专业领域中表现得非常吃力。
-
这是因为缺乏真正的理解,更多侧重于模仿而非智力。
大型语言模型(LLMs)正处于 hype(炒作)的巅峰。由于它们能够用自然语言回答和讨论泛泛的话题,因此被认为是“智能”的。
然而,一旦你深入到医学、金融、法律等专业/复杂领域,就容易发现逻辑不一致、明显错误以及所谓的“幻觉”。简而言之,大型语言模型表现得就像一个拥有非常丰富词汇的学生,试图假装他们已经为考试做了准备,并知道所有的答案,但实际上他们并不懂!他们只是因为手头有大量的信息而假装聪明,但他们使用这些信息进行推理的能力非常有限。我甚至愿意进一步说:
所谓的人工智能(AI)实际上常常是人工模仿智能(AII)。在像医学或金融这样专业的领域尤其如此,因为在这些领域中的错误可能会导致人类伤害和财务损失。
让我举一个我过去 10 年所从事的领域——金融风险的真实例子。它被认为是“专业”的一个有力证据是,必须提供大量背景信息,以便普通人理解这个话题:
-
银行必须遵守监管资本要求。
-
资本可以看作是一个吸收财务损失的缓冲区。
-
资本持有要求确保银行有足够的能力吸收损失,从而降低破产和金融危机的可能性。
-
第一部分设定要求的规则是基于风险比例原则:
→ 银行承担的业务越风险越大
→ 更高的风险权重
→ 更高的资本要求
→ 更大的损失缓冲区
→ 稳定的银行
-
第四部分中风险程度通常以银行与其合作的公司信用评级的形式进行衡量。
-
信用评级来自不同的评级机构,并且格式各异。
-
为了标准化评级,监管机构已经制定了从每种评级格式到标准化信用质量步骤(CQS)的映射规则,范围从 1 到 6。
-
那么第四部分中确定风险权重的监管规则是基于 CQS 的。
-
第八部分中针对欧洲银行的规则设定在资本要求监管条例(CRR)中。
上面 9 个陈述中的话题看起来很复杂,确实是如此,存在许多附加的复杂情况和案例,但我故意避开了这些,因为它们甚至不必要用来说明 AII 与这些话题的斗争。此外,复杂性并不是来自单个的 9 条规则本身,而是来自它们的组合,许多概念的定义是基于其他几个概念,从而产生了一个语义网络/图谱,连接着这些概念和规则。
现在让我们只关注第四部分的核心概念和规则,可以总结如下:评级 → CQS → 风险权重 → 资本要求。如果我们还看一下一个例子,CRR 第 122 条,我们可以看到一个映射表CQS → 风险权重,该表应当用于公司暴露的情况。

资本要求监管条例(CRR 122)
这个映射规则非常简单,普通人通过一点背景信息就能轻松理解。它也为许多银行专家所熟知,他们中的大多数并不记住确切的规则,而是仅仅知道上面 9 个陈述中的信息,并知道在哪里查找以便看到确切的映射。
人工模仿智能
现在让我们提出一个问题,看看 AII 能否处理它,以下是我问 ChatGPT 3.5 的问题:
根据 CRR,针对信用质量步骤为 5 的企业,风险权重是多少?
这是答案:
在《资本要求条例》(CRR)中,企业实体的风险暴露的风险权重取决于其信用质量步骤。在信用质量步骤的背景下,信用质量步骤为 5 通常代表最高的信用质量,表明违约风险非常低。
在 CRR 中概述的标准化方法中,企业的风险暴露根据其信用评级或信用质量步骤分配风险权重。针对企业的风险暴露的风险权重范围从 20%到 150%,取决于信用质量步骤以及该暴露是短期还是长期。
由于信用质量步骤为 5 表示非常低的风险,分配给这种风险暴露的风险权重将是最低之一。在这种情况下,对于信用质量步骤为 5 的企业,风险权重可能为 20%或类似,表示银行持有这种暴露的资本需求较低。
你能找出用粗体标出的事实错误吗?
-
第一个非常大的错误是,AII 完全误解并颠倒了CQS → 风险权重的关系,CQS 为 5 的高信用质量并不代表低风险,而恰恰相反,它代表的是最高风险之一!这实际上是回答这个问题时可能犯的最大错误!
-
即使在第 1 个错误之后,我仍然希望 AII 能够查阅文章中的表格,并得出结论:当 CQS 低于 5 时,风险权重为 150%。但不,AII 自信地声称风险权重为 20%,因为风险很低……
-
尽管不应得,但我仍然给予 AII 怀疑的余地,通过询问同样的问题,并明确指出具体的 CRR 条款:122。厚颜无耻,但自信满满,AII 此时回应称风险权重应该是 100%,仍然声称 CQS 为 5 的信用质量较好→ 另一个明显的错误。
我感到自己的工作是安全的,而且金融行业仍然需要我,因此我开始考虑解决方案,讽刺的是,这些解决方案可能会在未来让我的工作变得不安全……
为什么是本体论和知识图谱?
引入本体论,本体论是某一特定领域的知识表示形式。可以通过以下方式思考本体论:将不同的知识表示方法从最简单到最复杂进行排序:
-
数据字典: 包含字段名称和元数据属性的表格
-
分类法: 通过关系的嵌套形式,添加数据类型和子类型的表格(例如:鸽子 <是> 鸟类的一种)
-
本体论(Ontology):具有多种关系类型的多维分类法(例如,鸟类 <吃> 种子)“分类法与面向对象编程的邪恶结合” (Kurt Cagle, 2017)
为什么要在数据中引入如此复杂的关系结构?以下是一些好处,稍后将通过示例加以说明:
-
统一的 表现形式:结构、数据和逻辑。在上述示例中,鸟类是一个类,它是一个具有通用属性的模板 = 结构。在本体论中,我们还可以定义许多实际的鸟类实例,并为它们赋予各自的属性 = 数据。最后,我们还可以添加逻辑(例如,如果一只鸟 <吃> 超过 5 粒种子,则 <它> 不再饿)。这实际上是通过将一些逻辑作为数据本身的一部分,使数据“变得智能”,从而使其成为可重用的知识。它还使信息既能被人类阅读,也能被机器读取,这在机器学习中尤其有用。
-
可解释性与溯源(Explainability and Lineage):本体论最常见的实现方式是通过资源描述框架(RDF)以图形形式表现。这些图形随后可以进行查询,以评估现有规则和实例或添加新的规则和实例。此外,通过图形的节点和边,可以追溯思维链条,解释查询结果,避免机器学习中的黑箱问题。
-
推理与推断(Reasoning and Inference):当新增信息时,语义推理器可以评估该信息对图形的影响。此外,新的知识还可以通过“假如”问题从现有知识中推导出来。
-
一致性(Consistency):任何冲突的规则或偏离通用类属性的实例会被推理器自动识别为错误,并且不能成为图形的一部分。这非常有价值,因为它强制执行特定领域内知识的一致性,消除任何主观解释。
-
互操作性与可扩展性(Interoperability and Scalability):可重用的知识可以专注于特定的专业领域,或连接不同的领域(例如,金融领域的财务本体(FIBO),数学领域的OntoMathPRO,医学领域的OGMS)。此外,用户还可以下载一个通用的行业本体,并以实例和自定义规则的形式,通过私有企业数据扩展它。
本体论可以被认为是 AI 最早和最纯粹的形式之一,远在大型机器学习模型成为主流之前,完全基于通过结构化使数据智能化的理念。在这里,AI 指的是真正的智能——本体论能够解释给定规则评估结果的原因是因为它对事物的运作方式有语义理解!这一概念最早在 2000 年代初期作为语义网思想而流行,代表着从链接应用的互联网(Web 2.0)和链接页面的互联网(Web 1.0)到链接数据的互联网(Web 3.0)的演变。
知识图谱(KGs)是存储数据的一种更为通用的图形格式术语,它不一定遵循本体论和语义原则,而后者通常以 KG 的形式呈现。如今,随着大型语言模型(LLMs)的崛起,KGs 常被视为解决其在专业领域弱点的良好候选者,这反过来又复兴了本体论及其 KG 表示的概念。
这导致了非常有趣的范式趋同:
-
本体论旨在通过结构化数据使数据变得智能,从而生成智能。
-
LLMs 旨在通过将数据保持非结构化的同时,使模型变得非常大且结构化,从而生成智能:ChatGPT 具有约 1750 亿个参数!
显然,目标是相同的,数据是否成为模型的一部分,或者模型是否成为数据的一部分,实际上只是参考框架的问题,最终不可避免地导致一种信息奇点的形式。
为什么要在银行业中使用本体论?
-
专业化: 如上所述,LLMs 在金融等专业领域面临挑战。尤其在错误代价高昂的领域,这一问题尤为严重。此外,在专家较少的专业领域,自动化所带来的附加值,往往远高于通用领域的自动化(例如,替代银行专家与支持人员)。
-
审计追踪: 当财务项目在财务报表中被评估和汇总时,监管机构和审计师期望能够从所有细粒度的输入和规则到最终的汇总结果之间,保持持续的审计追踪。
-
可解释性: 专业人士依赖于对银行运营机制的深入理解,以及风险驱动因素对其投资组合和商业决策的影响。此外,监管机构明确要求通过定期的“如果”演练(如压力测试)来获得这种理解。这也是机器学习在核心银行领域采用不佳的原因之一——即所谓的“黑箱问题”。
-
客观性和标准化: 缺乏解释性和主观性确保了行业中的公平竞争和规章制度的有效性,进而保障了金融稳定性。
现在,想象一个完美的世界,其中如 CRR 这样的法规以本体论的形式提供,而非自由文本。
-
每家银行都可以导入本体标准,并用自己的私有数据和投资组合特征扩展它,进而评估所有的监管规则。
-
此外,单独的企业战略也可以与监管约束相结合,从而实现自动化的财务规划和优化决策。
-
最后,大规模规则和数据图的复杂复合影响可以被解开,以解释最终结果,并提供对之前不明显关系的洞察。
以下示例旨在通过最小的努力实现最大的效果来说明这些想法!
示例
在寻找解决所示 LLM 弱点的过程中,我设计了以下示例:
-
创建一个以知识图谱形式呈现的本体。
-
定义实体的结构,添加单个实例/数据以及控制它们交互的逻辑,遵循 CRR 法规。
-
使用知识图谱来评估风险权重。
-
请 KG 解释它是如何得出这个结果的。
为了创建简单的本体,我使用了CogniPy库,其主要优点包括:
-
使用受控自然语言(CNL)来编写和查询本体,意味着无需了解特定的图查询语言。
-
可视化的实体化知识图谱。
-
具有解释结果能力的推理器。
结构
首先,让我们从定义本体的结构开始。这类似于在面向对象编程中定义不同属性和约束的类。
在第一个 CNL 语句中,我们定义了公司类及其属性。
Every company has-id one (some integer value) and has-cqs one (some integer value) and has-turnover (some double value).
需要注意的几点是,类名使用小写字母(company)。不同的关系和属性使用连字符命名法定义,而数据类型则定义在括号中。渐渐地,这看起来越来越像是一种基于普通英语的完全成熟的编程语言。
接下来,我们展示另一种通过通用类语句标识公司唯一性的方法,基于其 ID。
Every X that is a company is-unique-if X has-id equal-to something.
数据
现在让我们添加一些数据或公司类的实例,实例名以大写字母开头。
Lamersoft is a company and has-id equal-to 123 and has-cqs equal-to 5 and has-turnover equal-to 51000000.
在这里,我们添加了一个特定公司 Lamersoft 的数据点,并为其属性赋值。当然,我们不限于一个数据点,我们可以在同一本体中拥有成千上万或数百万个数据点,它们可以与结构或逻辑组件一起或单独导入。
现在我们已将数据添加到结构中,可以第一次查询本体,获取所有公司,返回与查询匹配的实例的 DataFrame:
onto.select_instances_of("a thing that is a company")

包含查询结果的 DataFrame
我们还可以绘制我们的知识图谱,展示 Lamersoft 实例与一般公司类之间的关系:
onto.draw_graph(layout='hierarchical')

本体图
逻辑
最后,让我们添加一些简单的规则,实现 CRR 风险权重的公司相关规定。
If a company has-turnover greater-than 50000000 then the company is a corporate.
If a corporate has-cqs equal-to 5 then the corporate has-risk-weight equal-to 1.50.
第一条规则定义了什么是公司,通常是年营业额超过 5000 万的公司。第二条规则实现了部分 CRR 映射表CQS → 风险权重,这对于大语言模型(LLM)来说是非常难以理解的。
在添加规则后,我们已经完成了本体的构建,可以重新绘制知识图谱:

已评估规则的本体图
值得注意的是,知识图谱在我们将逻辑添加到结构和数据后,自动做出了两个重要的推论:
-
Lamersoft 由于其营业额属性和公司分类规则,被识别为公司。
-
Lamersoft 的风险权重已经根据其 CQS 属性和 CRR 规则进行了评估。
这一切都源于本体中所有信息的魔法般自动一致性(无冲突)。如果我们添加任何与现有信息相矛盾的规则或实例,推理器将报错,并且知识图谱将无法生成。
现在我们还可以与推理器进行互动,询问为何做出某一评估,或者是什么样的思维链和审计追踪导致了该评估:
printWhy(onto,"Lamersoft is a corporate?")
{
"by": [
{
"expr": "Lamersoft is a company."
},
{
"expr": "Lamersoft has-turnover equal-to 51000000."
}
],
"concluded": "Lamersoft is a corporate.",
"rule": "If a company has-turnover greater-than 50000000 then the company is a corporate."
}
无论输出格式如何,我们仍然可以清晰地看到,通过两个表达式来定义 Lamersoft 作为公司及其特定的营业额,得出的结论是,它符合特定营业额条件,因此被认定为公司。不幸的是,当前的库实现似乎不支持对风险权重结果的解释,这为未来的想法部分提供了思考。
尽管如此,我认为这个例子是成功的,因为它成功地将结构、数据和逻辑统一到一个单一的可扩展本体中,且仅用了最少的努力和资源,使用的是自然英语。此外,它还能够对规则进行评估并用完整的审计追踪来解释它们。
有人可能会说,好的,我们到底实现了什么呢?这不过是另一种更接近自然英语的编程语言,人们可以用 Python 类、实例和断言来做同样的事情。这个说法是对的,任何编程语言本质上都是人机之间的通信协议。同时,我们也可以清楚地看到,编程语法正在逐步向人类语言靠拢的趋势,从专注于实现实际业务概念和互动的领域驱动设计(DDD),到集成开发环境(IDE)中的 LLM 插件,用自然语言生成代码。这已经成为一种明确的趋势:
程序员作为业务和技术之间中介的角色正在发生变化。如果前者可以直接从业务问题的自然语言规范中生成,后者则可以通过解释者生成逻辑的自然语言定义,那么我们还需要代码和业务文档吗?
结论
想象一个世界,其中所有银行监管要求由监管机构集中提供,而不是以文本形式,而是以本体论或智能数据的形式,包含所有结构和逻辑。各个银行可以导入中央本体论并用自己的数据进行扩展,从而自动评估所有规则和要求。这将消除任何主观性和解释的空间,并确保结果的完整审计追踪。
除了监管之外,企业还可以开发自己的本体论,编码、自动化并重用他们专家的知识或不同的计算方法和治理流程。在企业层面,这样的本体论可以增加价值,强制执行一个共同的词汇表和规则理解,减少因解释和分歧所浪费的精力,这些精力可以转向以本体论形式构建更多的知识。相同的概念也可以应用于任何专业领域,其中:
-
单纯的文本关联是不够的,LLMs 在这方面仍然存在困难。
-
进行有效机器学习训练的大数据尚不可得。
-
高素质的专家可以得到真正人工智能的帮助,从而减少成本和错误的风险。
如果如今数据被视为与黄金一样有价值,那么我相信真正的“钻石”是结构化数据,我们可以称之为知识。这种以本体论和知识图谱形式呈现的知识,也可以像数据现在为营销目的而交易一样,在公司之间进行交易。谁知道,也许这会发展成一种按节点付费的商业模式,其中智能数据形式的专业知识可以作为产品或服务出售。
那么,我们可以将智能定义为我们积累知识并查询这些知识以获得可操作见解的能力。这可以发展成专门的人工智能,通过接入本体论来获得某一领域的专业知识,从而减少错误推断(hallucinations)。
LLMs(大型语言模型)已经开始对公司利润产生影响 —— Klarna 预计通过 ChatGPT 处理大部分客户服务对话,将提升 4000 万美元的利润,从而减少了人工客服的成本。
然而,注意 LLM 的具体应用领域!这并不是金融公司(如 Klarna)中更为专业的领域,如金融/产品规划或资产和负债管理。这是一般的客户支持服务,许多公司中这是入门级职位,已经使用了许多标准化的响应或程序。这是最容易应用人工智能的领域,但也是增值可能不最大的位置。此外,由于缺乏真正的智能,LLM 的错误推断风险依然存在。尤其在金融服务行业,LLM 提供的任何“金融建议”都可能带来法律和监管的后果。
未来的想法
LLMs 已经在所谓的检索增强生成(RAG)中利用了知识图谱。然而,这些图谱是通用概念,可能包含任何数据结构,并不一定代表本体,而 LLMs 使用本体的情况相对较少探索。这给了我以下下一篇文章的构思:
-
使用通俗的英语查询本体,避免依赖特定的 CNL 语法——这可以通过 NLP 模型实现,模型生成查询以访问存储本体的知识图谱——与 KGs 对话。
-
使用更强大的方式生成本体——CogniPy库对于快速演示非常有用,但对于扩展使用,应当使用更成熟的本体导向编程框架,如Owlready2。
-
第一点使得普通用户无需了解任何编程知识即可从本体中获取信息,而第二点则意味着需要软件开发人员来定义和写入本体(这有其优缺点)。然而,如果我们想要完成 AI 闭环,那么专家应该能够使用自然语言定义本体,而无需开发人员。这将更难实现,但类似的例子已经存在:LLM 与 KG 接口,实体解析。
实现上述所有三点的概念验证可以称之为真正的 AI,它应该能够在智能数据结构中开发知识,该结构既可供人类阅读,也可供机器读取,并通过自然语言查询以获得可操作的洞察,同时具有完全的透明度和审计追踪。
请关注我的第二部分!
泰勒级数,解释
函数逼近的方法
·发表于 Towards Data Science ·17 分钟阅读·2024 年 9 月 11 日
--

作者提供的图片
如果你对泰勒级数感到愤怒,并且对它的无用性感到惊讶,仿佛它存在的唯一目的是为我们制造解题任务,那么这篇文章可能适合你。
简介:数列与级数
本节内容用于刷新一些基础知识。首先是级数和数列的定义。有限级数是通过加总有限数列的项得到的。但在实分析的背景下,这些有限级数通常会被舍弃,因为如果没有无穷,就无法谈论收敛性、收敛速度等内容,而这些在实分析中是非常基础的。
在无限级数的情况下,存在着无数个来自无尽级数的项。到任何指定项的和称为部分和。[2]
数列的概念非常简单,它是一个按特定模式排列的数字列表,形式如下:a_1,a_2,a_3,…a_n。但把它看作一个函数会更有帮助,这给出了数列的正式定义:
数列是一个函数 f: ℕ → ℝ, 其中 ℕ 是非负整数的集合(所以包括 0),ℝ 是所有实数的集合。
Power BI 报告的十条命令
我曾多次被问及创建报告解决方案时的一些基本规则。通常我会回答,“嗯,这并不简单。”但现在我开始写下这些规则了。
·发布于Towards Data Science ·阅读时间 11 分钟 ·2024 年 12 月 3 日
--

图片来自Mark Duffel 在Unsplash
引言
虽然我不是宗教信徒,但拥有一组漂亮且均衡的规则数量这个原则令人着迷。因此,在这里你会看到为创建报告解决方案制定的十条规则。
作为一名 Power BI 专家,我对这个工具最为了解,但我会尽力以最通用的方式制定规则。
如果我做得不够完美,请耐心等待。
1. 你应当向用户询问他的问题,以便回答。
当开始一个新的 BI/报告/AI 项目时,必须向客户、利益相关者或用户提出的核心问题是:你希望用新的解决方案回答哪些问题?
如果我们不知道请求的内容,就无法判断数据是否能够支持提供答案。
否则我们无法评估从源系统中需要哪些数据。
不要害怕问更多问题,而不是更少的问题。一个额外的问题可以让你避免失败或比预期做更多的工作。
2. 你应当了解数据
在加载之前,我们应该分析数据,以了解其结构和模式。
我们应该找出可能的层次结构、关键列,最重要的是,找出可以作为关系的关键列候选项。
此外,我们应该检查表之间关系中不匹配和缺失的键。
如果数据作为一张大表提供,处理起来会更容易,因为如果我们保持这种形式,就不会有任何关系。但更多细节将在第四条命令中讨论。
3. 你应尽早对数据进行处理
通常,在将数据加载到我们选择的报告工具中时,我们必须对其进行处理。
无论使用何种工具,我们都必须尽早执行这些数据处理操作。
-
如果可能,在从源系统提取数据时进行处理。
-
如果不可能,在加载过程中处理数据,例如,如果源是关系型数据库,可以通过编写包含数据处理的 SQL 语句来进行处理。
-
如果可能,建立一个中介数据存储,例如数据仓库,以尽可能最干净的方式存储数据。
-
如果这不可行,使用报告工具中包含的工具。例如,在使用 Power BI 时,可以在 Power Query 中处理数据。
-
作为最后的手段,使用工具的功能,例如计算列或表。
有时,在将数据加载到数据模型之前进行处理是不可能的,或者太复杂。
例如,我们可能需要根据聚合或复杂的业务逻辑来计算数据。
当然,这是一个有效的例外理由。
我见过最糟糕的情况是,在从数据仓库加载数据时,Power Query 中有大量的数据处理。这种加载过程非常缓慢,我们必须计划使用 SQL 查询重建加载过程。
4. 你应坚持使用简单的星型模式
星型模式是在构建用户友好的报告数据模型时的首选方法。
这里是一个简化的星型模式图:

图 1 — 简单的星型模式,包含一个事实表、一个日期表和四个维度表(图示由作者提供)
如果你不了解这种方法,这里有一个简短的介绍:
星型模式至少包含一个事实表。事实表位于中心,包含所有可度量值的列。例如,它可以是金额的数值或单位的计数。此外,它还包含指向维度表的外键。
一个事实表包含一系列事件。事件可以是交易、测量或旅行预订。
维度表包含主键和描述性数据,例如关于客户、产品、商店等的信息。
事实表通过键列与维度表相连接。
如上图所示,每个星型模式至少包含一个日期表。日期表包含日期列表,事实表中的每个事件都分配给一个日期。
如果事件必须按小时或分钟级别进行分析,则需要一个时间表,时间表包含小时、分钟,必要时还包括秒。
维度表的优势在于,某一个人或团队可以专注于一个维度,而其他人则可以处理另一个维度。例如,产品团队负责产品维度,而客户中心则处理客户维度。
通过这种方式,最有能力的人可以专注于他们的专业,以交付最佳的结果。
关于星型模式,还有很多内容值得了解。
你可以在下面的参考部分找到关于 Microsoft Data Warehouse Toolkit 的链接。
尽管这篇文章相对较旧,且是在考虑微软产品的情况下写的,但其中描述的原则依然有效且有价值。
4.1. 你不应害怕数据冗余
这是第四条戒律的一个子集,因此它是 4.1。
请看以下数据模型:

图 2 — Power BI 中的数据模型。数据模型中标红的部分表示地理维度的双重使用。这可能会导致问题(图由作者提供)
请查看标红的部分。
如你所见,地理维度与门店和客户维度相连接。
虽然从数据库的角度来看,这样做有意义,可以减少冗余,但在同时试图按门店地理和客户地理分析数据时,这可能会导致问题。此外,根据所使用的工具,这可能需要额外的编码,因为这两个关系在数据模型中会引起歧义(事实表通过地理表从客户和门店维度都可以访问)。
一种解决方案是将所有需要的属性从地理维度复制到门店和客户这两个表中。
尽管这会导致数据冗余,但它使得报告工作更加轻松。
此外,数据模型的用户,例如报告设计师或数据分析师,更容易理解哪个国家/地区属性与门店或客户相关联。
现在,数据模型如下所示:

图 3 — 没有地理维度但有冗余内容的数据模型。注意在门店和客户维度中都有的 ContinentName 属性(图由作者提供)
5. 你应为数据模型的易用性做好准备
正如第一条戒律所提到的,核心问题是“如何回答用户的问题?”
因此,数据模型应该在此基础上构建:
我如何才能最好地支持数据分析?或者我该如何构建数据模型来支持答案的搜索?
此外,在我的项目中,我几乎总是收到来自业务用户的相同问题:我如何创建自己的报告或数据分析?
我需要在技术要求之外构建数据模型,以满足这一需求。我必须考虑用户的需求,更重要的是,考虑他们的能力、习惯和观点。
这让我想起了第四条戒律:构建星型模式。
这种方法的优势在于,它将描述性数据与事务和业务事件分开,这使得搜索特定属性变得更加容易。
6. 你应尽可能保持计算的简单性
当你遵循第三条戒律时,这可能是一个简单的任务。
只要你将计算尽可能多地卸载到数据源和数据模型之间,保持数据模型中的计算简洁,这样有助于减少模型中的复杂计算。
这在任何情况下都可能不实用,也并不总是最佳方法,如这里所述:
## Power BI 的预计算聚合 — 为什么你应该避免它们
我的一个客户总是希望在他的 Excel 文件中预计算聚合,以便生成报告。这里是我们应该避免的原因……
towardsdatascience.com
然而,这可能是具有挑战性的。
我在 Power BI 中的方法是添加附加列或创建计算列来支持我的度量值。
例如,我在日期表中创建了额外的列,使得我的时间智能计算更加容易:
时间是最关键的报告维度。你可以通过一个良好的日期表扩展你的报告功能。
towardsdatascience.com
有时,复杂的计算是不可避免的。
在最后一天,我遇到了一个度量值的挑战。
随着开发进度的推进,我在某一点停下了,因为代码变得越来越复杂。
我休息了一下,和同事们聊了聊,找到了一个更简单的方法。新的方法将 DAX 代码缩短到了原来的一三。
避免编写复杂的表达式的陷阱。可能会有更优雅、更直接的解决方案。
7. 你应该为你的标准报告页面定义一个模板
当作为团队工作并开发多个报告时,这一点非常重要。
从模板开始。
在 Power BI 中,我们可以从现有报告创建模板:
[## 在 Power BI Desktop 中创建并使用报告模板 - Power BI
学习如何使用 Power BI Desktop 模板通过为新报告的布局创建报告模板来简化工作…
learn.microsoft.com](https://learn.microsoft.com/en-us/power-bi/create-reports/desktop-templates?source=post_page-----508cbe81f7e7--------------------------------)
这样的模板应该包含:
-
你公司使用的默认字体。
-
默认颜色,按照你的企业设计。
-
其他元素,如公司标志或背景图片,根据你的企业形象和设计。
-
如果你正在构建数据模型,一个标准的日期表。
-
每个开发者都应该使用的其他表格。
-
只要你不使用版本控制工具,如 GitHub、GitLab、Azure DevOps 或其他任何工具,可以添加一个带有更改日志的报告页面,或其他存储更改日志的地方。
确保所有开发者都知道模板存放的位置,并且他们正在使用它。
8. 你不应该在报告中过度使用颜色。
你知道 IBCS 吗?
对于外观各异的报告、演示文稿和仪表板感到厌倦了吗?采用#SolidOutlinedHatched,全球...
这是描述如何设计高效报告的一套规则。
一个规则是谨慎使用颜色。
一旦开始使用颜色,就要考虑视力障碍用户。
我的意思是那些患有色盲的人。
看看这些统计数据,了解多少人患有色盲:
色盲或色觉缺陷(CVD)影响全球数百万人。它的特点是...
创建一个没有颜色且仅使用灰度的 PDF。如果你在区分颜色上有困难,可能需要考虑更改颜色。
此外,任何在日常生活中有意义的颜色,在与不同含义一起使用时应该避免。
例如,红色、绿色和黄色。
这些颜色在日常生活中有明确的含义,应根据实际情况使用:红色代表负数,黄色代表警告,绿色代表正数。
如果你想展示一个没有意义的数字,使用蓝色。
例如,在没有定义的情况下显示偏差时,正数是好是坏。
不要使用过于明亮的颜色,如霓虹绿或类似颜色。它们会让用户分心,无法关注你想通过报告传达的信息。
9. 你应该专注于回答用户的问题。
如第一条和第五条命令所述,用户是中心。
展示数据是很容易的,只要让它看起来吸引人。
然而,当数字及其呈现方式对用户没有用时,它们是毫无意义的,并且浪费时间。
-
问他们需要了解什么。
-
尽早在项目中进行。
-
强调他们决策的重要性。
-
确保理解工作流程以及他们需要了解的内容。
-
和他们交谈!
你从用户那里获得的见解越多,项目就越有可能成功。
当你花费了无数小时和天数构建一个好的解决方案,而没有人使用它时,这真的很遗憾。
10. 你不应严格遵循任何规则
总是有例外。
当你开始项目时,上述大多数命令都会受到审视。
有时,当偏离严格规则会带来更好的效果时,遵循规则就显得毫无意义。
艺术在于知道何时以及在什么程度上应偏离规则。
但要利用你的知识和直觉来判断,在你的特定场景和现有需求下,哪种方法是正确的。
这不是一门宗教,这些规则没有教条。
但要注意:过早偏离可能是错误的做法。我曾经经历过一个情况,我过早偏离了我的标准做法,结果发现那是错误的决定。
结论
我希望你在阅读这篇文章时,能像我写这篇文章时一样享受其中。
这里列出了所有的命令:
-
你应该向用户询问问题,以便回答
-
你应该了解数据
-
你应该尽早处理你的数据
-
你应该坚持使用简单的星型模式
-
你应该准备好易于使用的数据模型
-
你应该尽可能简化计算
-
你应该为标准报告页面定义一个模板
-
你不应在报告中过度使用颜色
-
你应该集中精力回答用户的问题
-
你不应严格遵循任何规则
制定这些我本能地遵循的规则是一项挑战。我还意识到了一点:第十条命令是最重要的。
作为顾问,我最喜欢的回答是:“这取决于情况。”
尽管有些命令是基础性的,比如第一、第二和第九条。
其他命令则有一定的灵活性。
例如,拿第四条命令来说。我会尽量遵守它。
但有时,星型模式有性能缺陷,只有通过替代建模才能解决。当报告的性能更好时,没人会因为你的建模方法而对你做出不好的评价。
然而,在构建报告解决方案时,无论使用什么工具,都应从这些命令开始,以建立一个坚实的基础。
然后,随着你知识的增长,你将获得信心,知道何时偏离这些规则。

图片由Highlight ID提供,来源于Unsplash
参考文献
《微软数据仓库工具包》:
Joy Mundy 和 Warren Thornthwaite 共同编写了这本关于构建成功商业智能系统的指南……
www.kimballgroup.com](https://www.kimballgroup.com/data-warehouse-business-intelligence-resources/books/microsoft-data-warehouse-dw-toolkit/?source=post_page-----508cbe81f7e7--------------------------------)
Power BI 中日期表的要求:
[## Power BI Desktop 中日期表的设计指南 - Power BI
Power BI Desktop 中创建日期表的技巧与指南。
learn.microsoft.com](https://learn.microsoft.com/en-us/power-bi/guidance/model-date-tables?source=post_page-----508cbe81f7e7--------------------------------)
我将我的文章开放给所有人,即使 Medium 有付费墙。这让我能为每个读者赚取一点收入,但我关闭了这个功能,确保你可以免费阅读我的文章。
我在晚上和周末写这些文章,工作量很大。
你可以通过以下方式支持我的工作:
buymeacoffee.com/salvatorecagliari
或者扫描此二维码:

任何支持都非常感激,能帮助我腾出更多时间为你创作更多内容。
非常感谢。
数据工程解决的十大数据生命周期问题
解决关键痛点的明确策略
·发表于Towards Data Science ·14 分钟阅读·2024 年 8 月 2 日
--

AI 生成的图像,使用Kandinsky
在本文中,我想讨论数据工程师在处理数据生命周期中各个阶段的管道时所面临的一些最大挑战。理解如何管理数据生命周期是我们这个不断变化领域中的关键。作为一名数据工程师,我经常处理来自各种来源的数据,包括来自数据库、数据湖和第三方 API 的非结构化数据。这些因素使得管理关键数据变得非常困难。我们将涵盖数据处理的所有重要阶段,从数据收集、分析到存储和销毁,我还将分享我每天使用的最佳实践。
数据生命周期管理
数据生命周期管理使企业能够采取战略性和规范化的方式来组织和管理数据,从源头到目的地,或其最终状态,如归档或销毁。
本质上,这是一套政策,旨在最大化数据在其整个生命周期中的价值,从数据创建到销毁,直到数据变得过时或由于合规性规定需要销毁。
典型的数据生命周期遵循一个广为人知的 ETL 模式。
强化学习锦标赛:DDPG、SAC、PPO、I2A、决策变换器
训练仿真类人机器人进行对抗,使用五篇新的强化学习论文
·发布于Towards Data Science ·阅读时间 13 分钟·2024 年 8 月 23 日
--

由 GPT-4 生成
最近我想起了旧电视节目《机器人战斗》(Battlebots),并且想为它加上一些自己的特色。因此,我训练了仿真类人机器人,通过五篇新的强化学习论文让它们进行对抗。
阅读下文,你将学习这五种强化学习算法的理论和数学,看到我如何实现它们,并见证它们一较高下,决出冠军!
-
深度确定性策略梯度(DDPG)
-
决策变换器
-
软演员-评论家(SAC)
-
带有近端策略优化(PPO)的想象增强智能体(I2A)
设置仿真环境:
我使用了 Unity 机器学习代理模拟器,并为每个机器人身体构建了 21 个执行器,分布在 9 个关节上,通过虚拟摄像头在其头部实现 10×10 的 RGB 视觉,此外还配备了剑和盾。我随后编写了 C#代码,定义了它们的奖励和物理交互。智能体可以通过三种主要方式获得奖励:
-
用剑击中对手(‘击败’对手)
-
保持头部的 y 位置高于身体(以鼓励它们站立起来)
-
比之前更接近对手(以鼓励智能体收敛并进行对抗)
智能体在 1000 个时间步后重置,并且我对环境进行了大规模并行化以进行训练。

大规模并行化的训练环境,我的截图
然后是编写算法的时刻。为了理解我使用的算法,了解什么是 Q 学习至关重要,所以让我们来探究一下!
Q 学习 (如果你已经熟悉,可以跳过)
在强化学习中,我们让智能体采取行动以探索其环境,并根据与目标的接近程度给予正向或负向奖励。智能体如何调整其决策标准以获得更好的奖励?
Q 学习提供了一种解决方案。在 Q 学习中,我们追踪 Q 函数 Q(s,a),它追踪从状态 s_t 执行动作 a_t 后的期望回报。
Q(s, a) = R(s, a) + γ * E[Q(s_t + 1, a_t + 1)] + γ² * E[Q(s_t + 2, a_t + 2) + …]
其中 R(s,a)是当前状态和动作的奖励,y 是折扣因子(一个超参数),E[]是期望值。
如果我们正确地学习了这个 Q 函数,我们就可以简单地选择返回最高 Q 值的动作。
我们如何学习这个 Q 函数?
从回合的结束处开始,在那里我们可以确定 Q 值(就是我们当前的奖励),我们可以使用递归来填充之前的 Q 值,更新公式如下:
Q(s,a) ← (1 — α) Q(s,a) + α * [r + γ * max_a’ Q(s’,a’)]
其中α是学习率,r 是即时奖励,γ是折扣因子(权重参数),s’是下一个状态,max_a’ Q(s’,a’)是下一个状态下所有可能动作的最大 Q 值
本质上,我们的新 Q 值等于旧 Q 值加上当前奖励与下一个最大 Q 值之间差异的小部分百分比。现在,当智能体想要选择一个动作时,它们可以选择返回最大 Q 值(期望奖励)的动作。
你可能会注意到一个潜在的问题:我们在每个时间步长上评估每个可能的动作的 Q 函数。如果我们在离散空间中有有限的动作数,这没有问题,但在连续动作空间中,无法高效地评估 Q 函数,因为可能的动作数是无限的。这使得我们需要考虑第一个竞争算法:(DDPG)
深度确定性策略梯度(DDPG)
DDPG 尝试以一种新颖的方式在连续动作空间中使用 Q 网络。
创新 1:演员和评论员
我们不能直接使用 Q 网络来做决策,但我们可以用它来训练另一个独立的决策函数。这就是演员-评论员设置:演员是决定动作的策略,评论员则基于这些动作来确定未来的期望奖励
目标评论员:Q_target(s,a) = r + γ * Q’(s’, μ’(s’))
其中 r 是即时奖励,γ是折扣因子,s’是下一个状态,μ’(s’)是目标策略网络对下一个状态的动作,Q’是目标评论员网络,目标演员:相对于策略的期望回报的梯度≈ 1/N * Σ ∇a Q(s, a)|a=μ(s) * ∇θ_μ μ(s)
本质上,如何通过 N 个样本,衡量策略选择的动作的 Q 值(相对于策略变化,策略变化又与策略参数相关)
为了更新两者,我们使用随机梯度上升更新,其中学习率 * 当前 Q 和目标 Q 的 MSE 损失的梯度。请注意,演员和评论家都被实现为神经网络。
创新 2:确定性动作策略
我们的策略可以是确定性的(为每个状态保证一个动作)或随机性的(根据概率分布为每个状态抽取一个动作)。确定性动作策略有助于高效评估 Q 函数(每个状态只有一个动作,因此进行单次递归评估)。
那么,如何在确定性策略下进行探索呢?我们不会被困在一遍又一遍地执行相同的动作吗?这种情况确实可能发生,然而,我们可以通过添加随机生成的噪声来增加智能体的探索性,以鼓励探索(有点像突变如何通过允许探索独特的遗传可能性来促进进化)
创新 3:交互式环境中的批量学习
我们还希望在每个观察到的时间步上获得更多收益(它由状态、动作、奖励和下一状态组成):因此我们可以存储之前的时间步数据元组,并将其用于未来的训练
这使我们能够离线使用批量学习(即使用之前收集的数据,而不是通过环境交互),并且允许我们通过 GPU 并行化以提高训练速度。现在,我们还拥有独立同分布的数据,而不是我们通常得到的有偏的顺序数据(在这种数据中,一个数据点的值依赖于之前的数据点)
创新 4:目标网络
通常,使用神经网络的 Q 学习过于不稳定,且不容易收敛到最优解,因为更新过于敏感/强大
因此,我们使用目标演员和评论家网络,它们与环境互动,并在训练过程中部分接近真实的演员和评论家,但不是完全接近(大因子)目标 + (小因子)新
算法演练与代码
-
初始化评论家、演员、目标评论家和目标演员、重放缓冲区
-
对于视觉,我在任何其他层之前使用 CNN(这样视觉的最重要特征会被算法利用)
-
对于每一集
-
观察状态,选择并执行动作 mu + 噪声
-
获取奖励,下一状态
-
将 (s_t, a_t, r_t, s_(t+1)) 存储在重放缓冲区中
-
从缓冲区中随机抽取一个小批次
-
更新 y_i = reward_i + gamma Q(s 给定 theta)
-
递归评估
-
更新评论家以最小化 L = y_i — Q(s,a|theta)
-
使用策略梯度 J 更新演员,期望递归 Q 给定策略
-
更新目标为大因子 * 目标 + (1 — 大因子) * 实际
[## Knights-of-Papers/src/DDPG/DDPG.py at main · AlmondGod/Knights-of-Papers
DDPG、决策变换器、I2A 与 PPO、SAC 自我博弈模拟战斗类人形机器人……
软演员评论员(SAC)
DDPG 确实存在一些问题。特别是,评论员更新包括贝尔曼方程:Q(s,a) = r + max Q(s’a’),但作为 Q 网络近似器的神经网络会产生很多噪声,噪声的最大值意味着我们过高估计,因此我们对我们的策略过于乐观,奖励了平庸的动作。众所周知,DDPG 还需要广泛的超参数调整(包括噪声添加),并且除非其超参数在一个狭窄的范围内,否则无法保证收敛到最优解。
创新 1:最大熵强化学习
不再让演员纯粹地最大化奖励,演员现在最大化奖励 + 熵:
为什么使用熵?
熵本质上是我们对某个结果的不确定性(例如,硬币的最大熵偏向硬币,较少的熵硬币始终是正面时熵为 0:展示公式)。
通过将熵作为最大化因子,我们鼓励广泛探索,从而提高对局部最优解的敏感性,允许对高维空间进行更一致、稳定的探索(为什么这比随机噪声更好)。Alpha:用于加权熵优先级的参数,自动调整(怎么调整?)
创新 2:两个 Q 函数
这一变化旨在通过独立训练两个 Q 网络并在策略改进步骤中使用两个中的最小值,来解决 Q 函数的贝尔曼过高估计偏差。
算法运行与代码
-
初始化演员,2 个 Q 函数,2 个目标 Q 函数,回放缓冲区,alpha
-
重复直到收敛:
-
对于每个环境步骤:
-
从策略中采样动作,观察下一个状态和奖励
-
将(s_t, a_t, r_t, s_t+1)存储在回放缓冲区
-
对于每个更新步骤:
-
采样批次
-
更新 Q 值:
-
计算目标 y = 奖励加上策略的最小 Q 值 + alpha 熵
-
最小化 Q 预测 — y
-
更新策略以最大化策略的 Q 值 + alpha 奖励
-
更新 alpha 以满足目标熵
-
更新目标 Q 网络(软更新目标,使目标 = 大因子 * 目标 + (1 — 大因子) * 实际值)
[## Knights-of-Papers/src/SAC/SAC.py at main · AlmondGod/Knights-of-Papers
DDPG、决策变换器、I2A 与 PPO 以及 SAC 自对弈于模拟战斗类人形……
I2A 与 PPO
这里有两个算法(附加算法层可以在任何算法之上工作)
近端策略优化(PPO)
我们的方法不同于 DDPG 和 SAC,我们的目标是一个可扩展、数据高效、具有鲁棒性收敛性的算法(对超参数的定义不敏感)。
创新 1:代理目标函数
这个代理目标允许离策略训练,因此我们可以使用更广泛的数据(这对于现实世界场景尤其有利,因为现有的数据集庞大)。
在讨论代理目标之前,理解优势的概念至关重要。优势是:在状态 s 执行行动 s 后的期望奖励与状态 s 的期望奖励之间的差异。实质上,它量化了行动 a 相较于“平均”行动的好坏。
我们将其估算为 A = Q(a,s) — V(a),其中 Q 是行动价值(在行动 a 后的期望回报),V 是状态价值(从当前状态出发的期望回报),两者都通过学习获得
现在,代理目标是:
J(θ) = Ê_t [ r_t(θ) Â_t ]
其中:
-
J(θ) 是代理目标
-
Ê_t […] 表示在有限样本批次上的经验平均值
-
r_t(θ) = π_θ(a_t|s_t) / π_θ_old(a_t|s_t) 是新策略下行动的似然度 / 旧策略下行动的似然度
-
Â_t 是时间步 t 的估计优势
这相当于量化新策略如何提高高回报行动的似然性,并降低低回报行动的似然性。
创新 2:剪切目标函数
这是另一种解决过大策略更新问题的方法,从而实现更稳定的学习。
L_CLIP(θ) = E[ min( r(θ) * A, clip(r(θ), 1-ε, 1+ε) * A ) ]
剪切目标是实际代理目标和剪切比例在 1 — epsilon 与 1 + epsilon 之间的代理目标的最小值(基本上是未修改比例的信任区域)。epsilon 通常为 ~0.1/0.2。
它本质上选择剪切比例和正常比例中较为保守的那一个。
实际的 PPO 目标:
L^{PPO}(θ) = Ê_t [ L^{CLIP}(θ) — c_1 * L^{VF}(θ) + c_2 * Sπ_θ ]
其中:
-
L^{VF}(θ) = (V_θ(s_t) — V^{target}_t)²
-
Sπ_θ 是策略 π_θ 在状态 s_t 下的熵
本质上,我们优先考虑较高的熵、较低的价值函数和较高的剪切优势
PPO 还使用小批量处理,并交替进行数据训练。
算法执行和代码
-
每次迭代
-
对于每个 N 个智能体
-
运行策略 T 个时间步
-
计算优势
-
针对策略优化代理函数,进行 K 次迭代,并且小批量大小 M < NT
-
更新策略
[## Knights-of-Papers/src/I2A-PPO/gpuI2APPO.py at main · AlmondGod/Knights-of-Papers
DDPG、决策变换器、结合 PPO 的 I2A,以及 SAC 自我对战在模拟战斗类人型机器人上的应用……
想象增强智能体
我们的目标是为任何其他算法创建一个额外的嵌入向量输入,以提供关键的有价值信息,并充当环境的“心理模型”
创新:想象向量
想象向量使我们能够向代理的观察中添加一个额外的嵌入向量,以编码多个“想象的未来运行”及其奖励评估(目标是“看到未来”并“在行动前思考”)。
我们如何计算它?我们使用一个学习到的环境逼近函数,试图模拟环境(这被称为基于模型的学习,因为我们尝试学习环境的模型)。我们将它与一个 rollout 策略配合使用,rollout 策略通常是一个非常简单且执行速度快的策略(通常是随机的),用来决定行动,从而“探索未来”。通过在 rollout 策略上运行环境逼近器,我们可以探索未来的行动及其奖励,然后找到一种方法将这些所有的想象未来的行动和奖励表示为一个向量。一个值得注意的缺点是:正如你所预期的,它增加了大量的训练工作,并且需要更多的数据。
结合的 I2A-PPO 算法流程与代码
-
每次我们收集 PPO 的观察数据时:
-
初始化环境模型和 rollout 策略
-
对于多个“想象的运行”:
-
从当前状态开始运行环境模型,并使用 rollout 策略决策,直到达到某个时间范围,从而生成一个想象轨迹(s,a,r 序列)
-
想象编码器:将这些想象轨迹转换成单一的输入嵌入,以供实际的决策网络使用
决策 Transformer
我们在这里的目标是利用 Transformer 架构的优势来进行强化学习。通过决策 Transformer,我们可以在稀疏/分散的奖励中识别出重要奖励,享受更广泛的分布建模以获得更好的泛化和知识迁移,并从预先获得的有限次次优数据中学习(称为离线学习)。
对于决策 Transformer,我们实际上是将强化学习视为序列建模问题。
创新点 1:Transformer
如果你想真正理解 Transformer,我推荐观看 Karpathy 从零构建 GPT-2 的视频。这里有一个关于 Transformer 的简要回顾,适用于 DT:
我们有表示状态、动作、期望获得的未来奖励之和(返回奖励),以及时间步的 token 序列。我们的目标是现在输入一系列 token 并预测下一步的动作:这将作为我们的策略。
这些 token 都有键、值和查询,我们通过复杂的网络将它们结合起来,以表达各个元素之间的关系。然后,我们将这些关系结合成一个“嵌入”向量,用以编码输入之间的关系。这个过程被称为注意力机制(Attention)。
请注意,“因果自注意力掩码”确保嵌入只能与序列中之前的嵌入相关联,因此我们不能使用未来的信息来预测未来,而是使用过去的信息来预测未来(因为我们的目标是预测下一步的动作)。
一旦我们获得了这个嵌入向量,就将其通过神经网络层(Karpathy 使用的类比是,我们在这里‘推理 token 之间的关系’)。
这两者的结合(通过 Attention 找到 tokens 之间的关系,通过我们的神经网络层推理关系)是 Transformers 的一种头部,我们将其堆叠多次。在这些头部的末尾,我们使用一个学习到的神经网络层将输出转换为我们的动作空间大小和要求。
顺便说一下,在推理时,我们预定义回报以作为我们期望的总奖励。
算法演示和代码
-
对于 dataloader 中的(R,s,a,t)
-
预测动作
-
模型将 obs、视觉(通过卷积网络层)、rtg 和时间步长转换为唯一的嵌入,并将时间步长嵌入添加到其他嵌入中
-
所有三个输入都作为输入提供给 Transformer 层,最后使用动作嵌入
-
计算 MSEloss(a_pred-a)**2
-
对决策 Transformer 模型执行 SGD,通过这个损失的梯度更新参数
[## Knights-of-Papers/src/Decision-Transformer/DecisionTransformer.py 在主分支下·…
DDPG、决策 Transformer、I2A 与 PPO,以及 SAC 自对战模拟类人战斗机器人…
结果
为了训练这些模型,我将算法运行在 NVIDIA RTX 4090 上,利用这些算法的 GPU 加速创新。感谢vast.ai!以下是损失曲线:
DDPG 损失(2000 个回合)

Matplotlib 损失图表,由我制作
I2APPO 损失(3500 个回合)

Matplotlib 损失图表,由我制作
SAC 损失(5000 个回合)

Matplotlib 损失图表,由我制作
决策 Transformer 损失(1600 个回合,每 40 个回合记录一次损失)

Matplotlib 损失图表,由我制作
通过比较算法的结果(主观比较并根据训练时间加权),我发现决策 Transformer 表现最好!考虑到 DT 是专门为充分利用 GPU 而设计的,这一点是有道理的。请观看我制作的视频,查看算法的实际表现。模型学会了爬行并停止摔倒,但仍有一段路要走,才能成为专家级战斗者。
改进的领域:
我深刻体会到了训练类人机器人有多么困难。我们在一个高维输入空间(包括视觉 RGB 和执行器的位置/速度)中操作,同时还结合了一个极其高维的输出空间(27 维连续空间)。
从一开始,我最希望的只是它们能爬到彼此面前并碰触剑锋,尽管即便如此也充满挑战。大多数训练运行甚至没有经历到触剑的高奖励,因为光是走路本身就太难了。
主要的改进维度就是简单地增加训练时间和使用的计算量。正如我们在现代 AI 革命中所见,这些增加的计算量和数据趋势似乎没有上限!
最重要的是,我学到了很多!下次,我会使用 NVIDIA 的技能嵌入或终身学习方法,让机器人在学习对战之前,先学会走路!
要查看我制作的展示创建这个项目过程的视频,并看到机器人对战,请查看下面的视频:
我尝试使用新的强化学习论文来让模拟机器人进行对战,
感谢你看到最后!如果你对更多内容感兴趣,可以在 Twitter 上找到我@AlmondGodd!
透明人类
数据如何揭示任何人的真实面目
·发表于面向数据科学 ·阅读时长 8 分钟·2024 年 10 月 10 日
--

图片来自pixabay 上的 zahtris。
2014 年,一位德国数据科学家开始收集德国数字报纸杂志《明镜》(Der Spiegel)上的文章数据。在 2 年半的时间里,他抓取了超过 100,000 篇文章以及相关的元数据,如发布时间、栏目和作者。收集完数据后,他开始对其进行分析。他的发现之一是,他能够预测《明镜》哪些作者之间有亲密关系。此外,他的结果还为公司内部的人员结构提供了见解。
这个例子已经揭示了透明人类的真正危险。仅凭少量数据,可能看起来并不特别危险,但你就能获得关于个人的高度私人化的见解。虽然我不认为上述提到的德国数据科学家 David Kriesel 有任何不良意图,但有些团体可能会持不同看法,并希望对你产生影响。他们寻求改变行为,并希望操控个人以获取自身的利益。
其中一个团体是剑桥分析公司(Cambridge Analytica)——一家英国咨询公司,涉嫌参与英国脱欧公投和 2016 年特朗普总统竞选活动。该公司基本的概念是利用 Facebook 活动数据来...
冲刺的陷阱:不要像斯嘉丽·奥哈拉那样,今天就思考!
数据科学
为什么数据科学家在敏捷项目中应优先考虑沟通和灵活性
·发表于Towards Data Science ·阅读时长 8 分钟·2024 年 6 月 3 日
--

图片由Saad Chaudhry提供,发布于Unsplash
你一定听说过《飘》和它的主要女主角斯嘉丽·奥哈拉。她的名句之一是:“明天再想这个问题,明天又是新的一天。”这反映了她倾向于推迟面对困难局面。
如果你是数据科学家,千万不要像斯嘉丽·奥哈拉那样!不要急于行动,今天就思考。
现如今,许多(如果不是大多数的话)数据科学项目都是采用敏捷方法进行的。有时候它们严格遵循敏捷方法,包括冲刺计划、冲刺、回顾、持续改进、每日站会、积压管理、用户故事映射和迭代开发。而其他时候,敏捷方法则被以较为宽松的方式执行——即使如此,冲刺仍然是交付产品的主要工具。
我并不反对敏捷方法,前提是我们不在讨论上花费比工作更多的时间。事实上,冲刺构成了敏捷项目组织的核心。如果我们将其视为工作组织单元,而非最终目标,那么这完全没问题。
我的意思是,不要让冲刺比项目本身更重要……
每个数据科学家在面试前必须写的两份文档
剧透警告:它们都不是简历。
·发表于Towards Data Science ·阅读时长:8 分钟·2024 年 5 月 19 日
--

数据科学面试可能会很艰难。
作为数据科学家,我们可能被要求在商业智能、软件工程、机器学习、数据分析和产品之间发挥类似瑞士军刀的作用。更重要的是,各家公司对数据科学家的使用方式不同,因此它们的面试也会反映这一点,涵盖各种各样的问题和流程。我们数据科学家必须准备好回答关于统计学、机器学习、A/B 测试、商业智能、系统设计、leetcode 风格的编程问题、机器学习运维等各类问题。

有人说过关于许多帽子的事吗?| 图片来自 publicdomainvectors.org,由作者修改。
然而,在这些可能的问题海洋中,有两个问题是我在每一次数据科学面试中都被问过的:
-
告诉我一些关于你自己的事。
-
告诉我你曾参与过的一个项目。
这些问题非常常见,你没有任何借口不能每次都轻松应对。而确保每次都能应对自如的最佳方法,就是提前……
招聘的两面性:在多元化市场中招聘与面试数据角色
申请超过 150 个职位并审阅 500 多份简历后,招聘和面试成功的因素,涵盖 4 个不同国家
·发表于Towards Data Science ·阅读时长 10 分钟·2024 年 10 月 13 日
--

“我在不同国家、行业和角色中的经验塑造了我对招聘两面成功的理解。” [照片由Christina @ wocintechchat.com提供,来自Unsplash]
来自桌两边的故事
我有很多在招聘桌两边的故事,有些成功,有些并不那么成功。
例如,我可以告诉你在以下情况下我如何在面试过程中取得成功并获得工作邀请:
-
我没有任何行业经验,但我展示了我的学术知识和对从事客户面对面的数据[%]角色的兴趣,尽管我不会说当地的语言。
-
在另一个数据[%]角色的最后一轮面试中,首席执行官开了个玩笑,而我是房间里唯一没有笑的人。然而,我说服了他和其他同事,相信我有责任心,能够构建他们所需的云数据平台。
或者,我也可以分享我没有获得工作邀请的经历,情况如下:
- 我未能说服副总裁、高级经理和人力资源负责人在最终的案例研究展示中选择我,而是选择了另一位候选人,尽管……
发现时间序列数据中的异常值终极指南(第一部分)
时间序列分析中的有效统计方法和工具用于检测异常值
·发表于 Towards Data Science ·阅读时间 18 分钟 ·2024 年 5 月 21 日
--

来源:DALL·E
如果你不是 Medium 的会员,你可以在这里 阅读文章.
异常值: 那些麻烦的数据点,它们可能会扭曲统计模型、扭曲预测结果,并破坏决策过程。
难怪它们在数据分析中不太受欢迎。
我叫 Sara,我拥有物理学硕士学位。目前,我在一家全球能源公司担任数据科学家。
本文是一个四部分系列的开始,专注于时间序列数据中异常值的识别和管理。如果你有兴趣跟随这个系列,请确保关注我 并 订阅,以便在下一部分发布时收到更新!
在这篇初步文章中,我们探讨了视觉和统计方法,以有效识别时间序列数据中的异常值。这些基础知识对于任何想要提高分析准确性的人来说都是至关重要的。
在第二篇文章中,我将专门讨论机器学习方法,由于其重要性和复杂性,它们值得单独讨论:
《终极指南:如何在时间序列数据中发现异常值(第二部分)》
时间序列分析中用于异常值检测的有效机器学习方法与工具
·发布于 Towards Data Science ·18 分钟阅读·2024 年 6 月 26 日
--

图片由作者提供。
如果您不是 Medium 的会员,您可以在这里阅读该文章。
异常值:那些可能会干扰统计模型、误导预测并扰乱决策过程的数据点。
本文是一系列四篇文章中的第二篇,专注于时间序列数据中异常值的识别与管理。
本系列的第一篇文章探讨了如何有效地通过可视化和统计方法来识别时间序列数据中的异常值:
## 《终极指南:如何在时间序列数据中发现异常值(第一部分)》
时间序列分析中用于异常值检测的有效统计方法与工具
towardsdatascience.com
本文第二部分将专门讨论用于异常值检测的机器学习方法。
鉴于它们的重要性和复杂性,值得进行专门的讨论!
在找到离群值之后,你应该做什么?第三篇文章探讨了如何 管理 这些离群值的各种策略,包括…
《发现时间序列数据中的异常值终极指南(第三部分)》
已找到异常值:接下来怎么办?异常值处理指南
·发表于Towards Data Science ·15 分钟阅读·2024 年 7 月 16 日
--

来源:DALL·E
如果你不是会员,可以在这里阅读 本文。
异常值:那些可能影响统计模型、误导预测并破坏决策过程的数据点。
异常值处理是数据分析中的必要步骤,许多人觉得它具有挑战性。
通过本文的见解,我希望能够简化这一过程,并使其更加有趣!
本文是关于时间序列数据中异常值的识别与管理的四篇系列文章中的第三篇。
本系列的第一篇文章讨论了如何有效地使用视觉方法和统计方法来识别时间序列数据中的异常值:
时间序列分析中异常值检测的有效统计方法和工具
towardsdatascience.com
在第二篇文章中,我们探索了几种机器学习技术来识别异常值:
《最终的数据解读指南》
来自 Uber、Meta 和高速成长初创公司的 10 年经验教训
·发布于 Towards Data Science ·阅读时长 14 分钟·2024 年 6 月 4 日
--

图片由作者提供;通过 Midjourney 创建
数据可以帮助你做出更好的决策。
不幸的是,大多数公司在收集数据方面比在解读数据上做得更好。他们声称拥有数据驱动的文化,但实际上,他们在做决策时更多依赖经验。
作为一名数据科学家,你的工作是帮助商业利益相关者理解和解读数据,以便他们能做出更明智的决策。
你的影响力并非来自你所做的分析或构建的模型,而是来自你帮助推动的最终商业成果。这是资深数据科学家与初级数据科学家的主要区别。
为了帮助实现这一点,我根据在 Rippling、Meta 和 Uber 的经验,整理了这本逐步操作手册,帮助将数据转化为可操作的洞察。
我将涵盖以下内容:
-
应该跟踪哪些指标: 如何为你的业务建立收入方程式和驱动树
-
如何跟踪: 如何设置监控并避免常见的陷阱。我们将讨论如何选择合适的时间范围,处理季节性问题,掌握分 cohort 数据等!
-
提取洞察: 如何以结构化和可重复的方式识别问题和机会。我们将介绍你最常遇到的趋势类型,以及如何理解它们。
听起来很简单,但问题在于细节,让我们一一深入探讨。
第一部分:应该跟踪哪些指标
首先,你需要弄清楚应该跟踪和分析哪些指标。为了最大化影响力,你应该专注于那些实际推动收入的指标。
从高层次的收入公式开始(例如,“收入 = 展示量 * CPM / 1000”对于基于广告的业务),然后进一步拆解每个部分,以了解潜在的驱动因素。具体的收入公式取决于你所从事的业务类型;你可以在这里找到一些最常见的公式。
结果驱动树的结构是,输出在顶部,输入在底部,它告诉你什么因素推动了业务的结果,以及你需要构建哪些仪表盘,以便进行端到端的调查。
示例: 这是一个(部分)基于广告的 B2C 产品的驱动树:

作者提供的图片
理解领先指标和滞后指标
收入公式可能让人觉得输入立即转化为输出,但现实中并非如此。
最明显的例子是市场营销与销售漏斗:你生成潜在客户,它们转化为合格的机会,最终达成交易。根据你的业务和客户类型,这个过程可能需要好几个月。
换句话说,如果你在查看一个结果指标,例如收入,通常是在查看你几周或几个月前采取的行动的结果。
一条经验法则是,你在驱动树中走得越远,某个指标就越可能是领先指标;走得越近顶部,你处理的就越可能是滞后指标。
定量分析滞后
查看历史转化窗口非常值得,这样可以帮助你理解你所处理的滞后程度。
这样,你就能更好地向回溯(如果你看到收入波动,你就知道该追溯多远以查找原因)以及向前预测(你会知道看到新举措的影响需要多长时间)。
根据我的经验,制定经验法则(例如,平均需要一天还是一个月时间让新用户变得活跃)能够帮助你获得 80%到 90%的价值,因此不必过度设计这一过程。
第二部分:设置监控并避免常见的陷阱
所以你有了驱动树;那你如何利用这个来监控业务的表现并为利益相关者提取见解呢?
第一步是设置仪表盘来监控关键指标。我不会深入比较你可以使用的各种 BI 工具(以后我可能会在单独的文章中做这个比较)。
我在这篇文章中谈到的所有内容,都可以轻松地在 Google Sheets 或任何其他工具中完成,因此你选择的 BI 软件不会成为限制因素。
相反,我希望集中讨论一些最佳实践,帮助你理清数据并避免常见的陷阱。
1. 选择适当的时间框架来衡量每个指标
虽然你希望尽早捕捉趋势,但需要小心不要陷入过于细化数据的陷阱,避免从大多数噪音中得出结论。
考虑你正在衡量的活动的时间范围,以及你是否能够基于数据采取行动:
-
实时数据对于像 Uber 这样的 B2C 市场非常有用,因为1) 交易生命周期很短(一次 Uber 出行通常在不到一个小时内完成请求、接受和完成)并且 2)因为 Uber 有应对实时变化的工具(例如,动态定价、激励措施、司机沟通)。
-
相反,在 B2B SaaS 企业中,由于长期的交易周期,日常销售数据会显得比较嘈杂且不易采取行动。
你还需要考虑你在设定目标时的时间范围。如果你的合作团队有月度目标,那么这些指标的默认查看方式应该是按月。
但是:月度指标(甚至更长时间周期)的主要问题是,你可以用来分析的数据点很少,并且需要等待很长时间才能得到更新的表现视图。
一种折衷方法是将指标绘制为滚动平均:这样,你既能捕捉到最新的趋势,又能通过平滑数据去除很多噪音。

作者提供的图片
示例: 看左侧的月度数据,我们可能会得出结论,认为我们有很好的机会达成四月的目标;然而,查看 30 天的滚动平均时,我们发现收入生成急剧下降(我们应该尽快调查这个问题)。
2. 设置基准
要从指标中得出见解,你需要能够将数字放入上下文中。
-
最简单的方法是将指标随时间对比:该指标是在改善还是在恶化?当然,如果你知道自己希望该指标达到的确切水平,那就更好了。
-
如果你有一个与指标对齐的官方目标,那很好。但即使没有,你仍然可以通过推导出隐性目标来判断自己是否在正确的轨道上。
示例: 假设销售团队有月度配额,但他们没有明确的目标来确定需要生成多少销售线索才能达成配额。
在这种情况下,你可以查看开放销售线索与配额的历史比例(“销售线索覆盖率”),并以此作为你的基准。请注意: 通过这样做,你隐含地假设了业绩将保持稳定(在这种情况下,假设团队以稳定的速度将销售线索转化为收入)。
3. 考虑季节性因素
几乎在任何业务中,你都需要考虑季节性因素来正确解读数据。换句话说,你所查看的指标是否存在按照一天中的时间、星期几、每月时间、日历月份等重复的模式?
示例: 看看这个 B2B SaaS 企业的新 ARR 月度趋势:

作者提供的图片
如果你查看这个简单的柱状图中 7 月和 8 月新 ARR 的下降,你可能会感到惊慌并开始进行广泛调查。
然而,如果你将每一年的数据叠加在一起,你能够识别出季节性模式,并意识到每年都有一个夏季低谷,你可以预期业务将在九月再次回升:

图片由作者提供
但季节性并不一定是按月来定义的;可能是某些工作日的表现较强或较弱,或者你通常会看到业务在月末开始回升。
例子: 假设你想查看销售团队在当前月份(四月)的表现。现在是本月的第 15 个工作日,截止目前,你已经完成了 26,000 美元的销售额,而目标是 50,000 美元。如果忽略季节性因素,看起来团队将会未达目标,因为你只剩下 6 个工作日了。
然而,你知道团队通常会在月末完成大量交易。

图片由作者提供
在这种情况下,我们可以绘制累计销售额,并与前几个月进行比较,从而理解模式。这让我们看到,实际上我们在本月的这个时候处于一个不错的位置,因为销售趋势并非线性。
4. 处理“尚未成熟”的度量
在分析度量时,最常见的一个陷阱是查看那些还没有足够时间“成熟”的数字,即尚未达到最终值的数字。
以下是一些最常见的例子:
-
用户获取漏斗: 你正在衡量从流量到注册再到激活的转化;你不知道最近的注册中有多少会在未来转化
-
销售漏斗: 你的平均交易周期持续几个月,你不知道近期的未完成交易中有多少会最终成交
-
留存: 你想了解某一特定用户群体在你的业务中保持活跃的情况
在所有这些情况下,近期用户群体的表现看起来比实际情况差,因为数据尚未完整。
如果你不想等待,你通常有三种选择来解决这个问题:
选项 1:按时间周期切分度量
最直接的方式是按时间周期切分汇总度量(例如,第一周转化率、第二周转化率等)。这样可以在进行横向比较时避免偏向较老的用户群体,并且能提前了解结果。
然后,你可以在一个用户群体热力图中显示结果。以下是一个关于获取漏斗跟踪从注册到首次交易的例子:

图片由作者提供
通过这种方式,你可以看到,在对比相同条件下,我们的转化率在下降(我们的第一周转化率从超过 20%降至最近用户群体的约 15%)。仅通过查看汇总的转化率(最后一列),我们无法区分数据不完整所导致的实际下降。
选项 2:更改度量定义
在某些情况下,您可以改变指标的定义,以避免查看不完整的数据。
例如,与其查看 3 月份进入管道的交易到目前为止有多少达成,您可以查看 3 月份关闭的交易中有多少是赢得的,多少是失去的。这个数字不会随着时间变化,而您可能需要等待几个月才能看到 3 月份交易组的最终表现。
选项 3:预测
基于过去的数据,您可以预测一个群体的最终表现可能会如何。随着时间的推移和更多实际数据的收集,预测将会趋近于实际值。
但要小心:预测群体表现时应小心谨慎,因为这很容易出错。例如,如果您在一个 B2B 业务中工作,且赢得交易的比率较低,一个单独的交易可能会显著改变群体的表现。准确预测这一点非常困难。
第三部分:从数据中提取洞察
所有这些数据都很棒,但我们如何将其转化为洞察呢?
您不会有时间定期深入研究每个指标,因此可以通过优先查看最大的差距和关键变化来管理您的时间:
-
团队在哪些地方未能达到目标?您在哪些地方看到了意外的超额表现?
-
哪些指标在下降?哪些趋势正在反转?
一旦您选择了一个感兴趣的趋势,您需要深入挖掘并找出根本原因,以便您的业务合作伙伴能提出有针对性的解决方案。
为了为您的深入分析提供结构,我将逐一介绍您可能遇到的关键指标趋势类型,并根据实际经验为每种类型提供具体示例。
1. 净中性变动
当您看到某个指标剧烈波动时,首先应该向上查看驱动因素树,而不是直接向下。这样,您可以看看这个数字是否真正推动了您和团队最终关心的事情;如果没有,找出根本原因的紧迫性就会降低。

图片由作者提供
示例场景:在上面的图片中,您看到网站上的访问到注册的转化率大幅下降。与其惊慌失措,您查看了总注册数,发现该数字保持稳定。
事实证明,平均转化率的下降是由于网站上低质量流量激增造成的;而您的“核心”流量表现保持不变。
2. 分母与分子
在处理比例指标(每个活跃用户的展示量、每个共享乘车司机的行程数等)时,首先检查是分子还是分母发生了变化。
人们往往认为是分子发生了变化,因为那通常是我们在短期内试图提升的参与度或生产力指标。然而,很多情况下情况并非如此。
示例包括:
-
您看到每位销售代表的潜在客户数量下降,因为团队刚刚加入了一批新员工,而不是因为您存在需求生成问题。
-
每小时每个 Uber 驾驶员的行程下降,并不是因为乘客请求减少,而是因为团队提高了激励措施,更多的司机上线了。
3. 隔离/集中趋势
许多指标趋势是由只发生在产品或业务的特定部分的因素推动的,聚合的数字并不能讲述完整的故事。
隔离根本原因的一般诊断流程如下:
步骤 1: 继续分解指标,直到你隔离出无法进一步分解的趋势。
类似于在数学中每个数字都可以分解成一组质数一样,每个指标都可以进一步分解,直到达到基本的输入。
通过这样做,你能够将问题隔离到司机树的特定部分,从而更容易定位问题所在,并决定应对措施。
步骤 2: 对数据进行分段,以隔离相关趋势
通过分段,你可以找出是否是业务的某个特定领域出了问题。通过在以下维度上进行分段,你应该能够发现超过 90% 的问题:
-
地理位置(地区/国家/城市)
-
时间(月份中的时间、星期几等)
-
产品(不同的 SKU 或产品表面(例如 Instagram Feed 与 Reels))
-
用户或客户人口统计(年龄、性别等)
-
单一实体/行为者(例如销售代表、商家、用户)
让我们来看一个具体的例子:
假设你在 DoorDash 工作,看到波士顿的完成送货数量相比上周下降。与其集思广益寻找增加需求或提升完成率的方法,不如先尝试隔离问题,这样我们就能制定更有针对性的解决方案。
第一步是分解指标“完成的送货”:

作者提供的图片
根据这个驱动树,我们可以排除需求端的问题。相反,我们看到最近在波士顿,我们在寻找司机接单方面遇到了困难(而不是餐厅和快递员交接或食物送达方面的问题)。
最后,我们将检查这是否是一个广泛的问题。在这种情况下,一些最有前景的切入点是查看地理位置、时间和商家。商家数据表明这个问题是广泛存在的,影响了许多餐厅,所以它并不能帮助我们缩小范围。
然而,当我们为指标“没有找到快递员的送货请求”创建时间和地理位置的热图时,我们发现大部分问题发生在波士顿郊区的夜间:

作者提供的图片
我们该如何处理这些信息? 能够像这样 pinpoint 问题,能够让我们在这些时间和地点部署有针对性的快递员招募工作和激励措施,而不是把它们撒播到整个波士顿。
换句话说,隔离根本原因使我们能够更有效地部署资源。
你可能遇到的其他集中趋势示例:
-
大多数在线游戏中的游戏内购买来自少数“鲸鱼玩家”(所以团队会将其留存和参与度的努力集中在这些玩家身上)
-
大多数支持工单的升级到工程团队,是由少数支持人员引起的(这为公司提供了一个有针对性的杠杆,通过培训这些支持人员来释放工程团队的时间)
4. 混合变化
诊断表现时最常见的困惑来源之一就是混合变化和辛普森悖论。
混合变化只是总人口组成的变化。辛普森悖论描述了这样一种反直觉的现象:你在总人口中看到的趋势,在查看子组件时消失或反转(反之亦然)。
这在实践中是什么样的呢?
假设你在 YouTube 工作(或者其他任何有广告业务的公司)。你发现收入正在下降,当你深入分析数据时,发现 CPM 已经下降了一段时间。
CPM 作为一个指标无法进一步分解,因此你开始对数据进行细分,但却难以识别根本原因。例如,所有地区的 CPM 看起来都很稳定:

图片由作者提供
这里就是混合变化和辛普森悖论的体现:每个单独地区的 CPM 保持不变,但如果你查看按地区划分的印象组成,你会发现印象的组合正在从美国转向亚太地区。
由于亚太地区的 CPM 低于美国,整体 CPM 正在下降。

图片由作者提供
再次强调,了解准确的根本原因可以提供更有针对性的响应。根据这些数据,团队可以尝试在高 CPM 地区重新激发增长,考虑为亚太地区提供更多的货币化选项,或通过在大亚太市场中大量增加印象量来弥补单个印象的较低价值。
最后的思考
记住,数据本身没有价值。只有当你用它来生成洞察或为用户或内部利益相关者提供建议时,它才变得有价值。
通过遵循一个结构化框架,你将能够可靠地识别数据中的相关趋势,按照上述提示,你可以区分信号与噪声,避免得出错误的结论。
如果你对更多类似内容感兴趣,可以在 Medium 上关注我,或在LinkedIn和Substack上关注我。
RAGs 终极指南——每个组件解析
构建生产级 LLM 管道所需的视觉导览
·发表于 Towards Data Science ·12 分钟阅读·2024 年 10 月 29 日
--

让我们来学习 RAGs!(图片来自作者)
如果你曾经使用过大型语言模型,可能至少听说过 RAG 这个术语——检索增强生成(Retrieval Augmented Generation)。RAG 的概念非常简单——假设你想向 LLM 提问,除了依赖 LLM 的预训练知识外,你首先从外部知识库中检索相关信息。然后将这些检索到的信息与问题一起提供给 LLM,从而让它生成一个更加知情且最新的回答。

将标准 LLM 调用与 RAG 进行比较(来源:图片来自作者)
那么,为什么要使用检索增强生成?
当提供准确且最新的信息至关重要时,你不能仅依赖 LLM 内置的知识。RAG 是一种廉价且实用的方法,可以使用 LLM 生成关于近期话题或小众话题的内容,而无需自己对其进行微调,也不会耗费你毕生的积蓄。即使 LLM 内部的知识足以回答问题,使用 RAG 也是个好主意, 因为近期的研究表明,它们可能有助于减少 LLM 的幻觉现象。
一个基础 RAG 的不同组件
在我们深入探讨本文的高级内容之前,让我们先回顾一下基础知识。一般来说,RAG 由两条管道组成——预处理和推理。
推理就是利用现有数据库中的数据来回答用户查询的问题。预处理是将数据库设置为正确格式的过程,以便之后可以正确检索。
这里是整个基础 RAG 管道的示意图。

基本 RAG 管道(图片由作者提供)
索引或预处理步骤
这是离线预处理阶段,我们将在这里设置数据库。
-
识别数据来源:根据应用选择相关的数据来源,例如维基百科、书籍或手册。由于这是领域特定的,我将在本文中跳过此步骤。去选择你想使用的任何数据,尽情使用吧!
-
数据分块:将数据集拆分为更小、更易于管理的文档或数据块。
-
转换为可搜索格式:将每个数据块转换为数字向量或类似的可搜索表示。
-
插入数据库:将这些可搜索的数据块存储到自定义数据库中,当然也可以使用外部数据库或搜索引擎。
推理步骤
在查询推理阶段,以下组件尤为突出。
-
查询处理:将用户查询转换为适合搜索的格式的方法。
-
检索/搜索策略:一种相似度搜索机制,用于检索最相关的文档。
-
检索后答案生成:使用检索到的文档作为上下文,通过 LLM 生成答案。
很好——我们已经确定了构建 RAG 所需的几个关键模块。信不信由你,这些组件每一个都有大量的附加研究,使得这个简单的 RAG 变成了 CHAD-rag。让我们从数据分块开始,看看这个列表中的每个主要组件。
本文基于这段 YouTube 视频
顺便说一下,本文基于我制作的这段 17 分钟的 YouTube 视频,内容涵盖了本文中的所有话题。读完这篇 Medium 文章后,欢迎查看视频!
1. 数据分块
数据分块是将大文档拆分为较小、易于管理的部分的过程。听起来可能很简单,但相信我,你如何分块数据可能决定了 RAG 管道的成败。你在预处理时创建的分块最终将在推理过程中被检索。如果你将数据块做得太小——比如每个句子——那么通过搜索检索可能会很困难,因为它们捕捉的信息很少。如果数据块太大——比如插入整个维基百科文章——检索到的段落可能会让 LLM 感到困惑,因为你一次性发送了大量文本。

根据不同的数据分块级别,你的结果可能会有所不同!(图片由作者提供)
一些框架使用 LLM 进行分块,例如通过从文本语料库中提取简单的事实或命题,并将它们视为文档。这可能会很昂贵,因为数据集越大,你需要进行的 LLM 调用就越多。

命题分块(来源:Dense X Retrieval Paper。许可证:免费)
结构化分块

如果你的数据具有固有的边界(如 HTML 或代码),有时最好直接利用它。(图片来源:作者)
很多时候,我们可能还需要处理本身具有已知结构或格式的数据集。例如,如果你想将代码插入到数据库中,你可以简单地通过函数名或类定义来拆分每个脚本。对于像维基百科文章这样的 HTML 页面,你可以通过标题标签进行拆分——例如,按 H2 标签拆分以隔离每个子章节。
上下文分块
但是,我们迄今为止讨论的分块类型存在一些显而易见的问题。假设你的数据集包含从所有《福尔摩斯探案集》书籍中提取的成千上万段落。那么,如果用户查询的是类似“《血字的研究》中的第一个犯罪是什么?”这样的一般问题,你认为会发生什么?
问题在于,由于每个文档都是一个孤立的信息片段,我们不知道哪些分块来自《血字的研究》。因此,稍后的检索中,我们最终可能会获取到一堆关于“犯罪”话题的段落,但无法确定它是否与该书相关。为了解决这个问题,我们可以使用一种叫做上下文分块的方法。
进入上下文分块
最近的博客文章来自 Anthropic,它将上下文分块描述为在嵌入之前,为每个分块添加特定的解释性上下文。基本上,在我们进行索引时,我们还会包括与分块相关的额外信息——例如书名、章节、也许是书中的事件总结。添加这些上下文将允许检索器在搜索时找到《血字的研究》和犯罪相关的参考内容, hopefully 能够从数据库中获得正确的文档!

上下文分块为分块添加了比文本主体更多的额外信息(图片来源:作者)
还有其他方法可以解决查找正确查询的问题——比如元数据过滤,我们将在讨论数据库时再详细讲解。
2. 数据转换
接下来,我们进入数据转换阶段。请注意,无论我们在预处理过程中使用了什么策略来转换文档,我们都需要在后续的相似性搜索中使用它,因此这两个组件是紧密耦合的。
目前在这个领域中出现的两种最常见方法是嵌入式方法和基于关键词频率的方法,如 TF-IDF 或 BM-25。
嵌入式方法
我们将从基于嵌入的方法开始。在这里,我们使用预训练的变换器模型将文本转化为高维向量表示,捕捉文本的语义含义。嵌入方法非常适合捕捉语义关系,处理同义词,并理解上下文相关的含义。然而,嵌入方法可能计算密集,并且有时会忽略一些简单方法容易捕捉到的精确匹配。

嵌入(图片来源:作者)
语义搜索何时会失败?
例如,假设你有一个包含特定冰箱信息的手册数据库。当你查询一个非常特定的小众型号或序列号时,嵌入方法会获取与查询相似的文档,但可能无法完全匹配查询。这就引出了嵌入检索的替代方法——基于关键词的检索。
基于关键词的方法
两种流行的基于关键词的方法是 TF-IDF 和 BM25。这些算法关注文档和查询中的术语之间的统计关系。
TF-IDF 根据一个词在文档中出现的频率与在整个语料库中出现的频率之间的比重,来衡量该词的重要性。我们数据集中的每个文档都由该文档中词汇表中每个词的 TF-IDF 分数组成的数组表示。该文档向量中高值的索引告诉我们哪些词最能体现该文档的内容,因为这些词在该文档中出现得更频繁,而在其他文档中出现得较少。例如,关于 Godrej A241gX 的文档将会对“Godrej”和“A241gX”这两个短语赋予较高的 TF-IDF 分数,这使得我们在使用 TF-IDF 检索时更有可能找到这些文档。

TF-IDF 依赖于术语在文档中出现的频率与其在整个语料库中出现频率的比率。(图片来源:作者)
BM25 是 TF-IDF 的一个演化版本,融合了文档长度归一化和术语饱和度。也就是说,它根据文档的长度是否长于或短于集合中的平均文档长度,调整 TF-IDF 分数。术语饱和度意味着,当某个特定词汇在数据库中出现得过于频繁时,它的重要性会降低。
TF-IDF 和 BM-25 非常适合找到具有特定关键词出现的文档,尤其是当它们恰好出现时。而嵌入方法则非常适合找到具有相似语义的文档。
现在常见的做法是结合使用关键词和基于嵌入的方法,并将它们结合起来,从而获得两者的最佳效果。 当我们在稍后讨论“互惠排序融合”和“去重”时,我们将探讨如何结合这些不同的检索方法。
3. 数据库
接下来,让我们来谈谈数据库。在 RAGs 中使用最常见的数据库类型是向量数据库。向量数据库通过使用向量表示(例如嵌入或 TF-IDF)对文档进行索引来存储它们。向量数据库专注于与查询向量进行快速相似性检查,这使得它们非常适合 RAG。你可能想了解的流行向量数据库有 Pinecone、Milvus、ChromaDB、MongoDB,它们各有优缺点和定价模型。
向量数据库的替代方案是图数据库。图数据库将信息存储为一个文档网络,每个文档通过关系与其他文档相连接。

现代向量数据库支持语义搜索的属性过滤(图示作者)
许多现代向量和图数据库也支持关系型数据库中的属性,最显著的是元数据或属性过滤。如果你知道问题是关于《哈利·波特与凤凰社》的第五本书,那么首先过滤整个数据库,只保留第五本哈利·波特书籍的文档,而不是对整个数据集进行嵌入搜索,会非常有帮助。在向量数据库中,最佳的元数据过滤是计算机科学研究中的一个非常棒的领域,关于这个话题的深入讨论最好能写一篇独立的文章。
4. 查询转换
接下来,让我们开始推理过程,从查询转换开始——这是我们在进行任何相似性搜索之前,对用户实际查询进行的任何预处理步骤。可以把它看作是改善用户问题,以获得更好的答案。
一般来说,我们希望避免直接使用用户查询进行搜索。用户输入通常非常嘈杂,他们可能会随便输入一些内容——我们希望有一个额外的转换层,能够解释用户的查询并将其转化为搜索查询。
一个简单的例子,说明了查询重写为何重要
进行这种转换的最常见技术是查询重写。假设有人问:“画蒙娜丽莎的艺术家后来怎样了?”如果我们进行语义或关键词搜索,检索到的信息将全是关于蒙娜丽莎的,而不是关于艺术家的。查询重写系统会使用 LLM 重写这个查询。LLM 可能会将其转换为“列奥纳多·达·芬奇 蒙娜丽莎 艺术家”,这将是一个更有成效的搜索。

直接检索与查询重写(图示作者)
有时候我们还会使用上下文查询写作,例如我们可能会使用额外的上下文,比如使用用户的旧对话记录,或者如果我们知道我们的应用程序涵盖了来自 10 本不同书籍的文档,可能会有一个分类器 LLM 来分类用户查询,以确定我们正在处理的是哪本书。如果我们的数据库使用的是不同语言,也可以对查询进行翻译。
还有像 HYDE 这样的强大技术,HYDE 代表假设文档嵌入。HYDE 使用语言模型生成对查询的假设性回答,然后通过与这个假设性回答进行相似度搜索来检索相关文档。

假设文档嵌入(Hypothetical Document Embeddings)(图片来源:作者)
另一种技术是多查询扩展,它通过从单一的用户查询生成多个查询,并进行并行搜索来检索多个文档集合。收到的文档随后可以经过去重步骤或排名融合,以去除重复的文档。
一种最近的 approach 叫做Astute RAG,它试图在生成答案之前,将外部输入的知识与 LLM 自身的内部知识进行整合。还有一些多跳技术,如 Baleen 程序,它们通过执行初始搜索,分析前几个结果,找出常常一起出现的词汇,然后将这些词汇添加到原始查询中。这种自适应方法有助于弥合用户查询与文档内容之间的词汇差距,从而帮助检索到更好的文档。
5. 检索后处理
现在我们已经检索到了可能相关的文档,在将信息输入到语言模型中生成答案之前,我们可以加入另一个检索后处理步骤。
例如,我们可以进行信息选择和强调,在这个过程中,LLM(大语言模型)会从检索到的文档中选择可能有助于找到答案的部分。我们可能会突出显示关键句子,或者进行语义过滤,去除不重要的段落,或者进行上下文总结,将多份文档合并成一份。这样做的目标是避免让我们的 LLM 接收过多信息,这样可能会导致回应不够专注或准确。

你可以使用较小的 LLM 从检索到的文档中标记出相关信息,然后在最终调用 LLM 时合并上下文提示(图片来源:作者)
通常我们会通过查询扩展进行多次查询,或使用多个检索算法,如 Embeddings+BM-25,分别获取多个文档。为了去除重复项,我们通常使用重新排序方法,如互惠排序融合(RRF)。RRF 结合了所有不同方法的排名,为那些在多个方法中始终表现良好的文档赋予更高的权重。最终,排名前 K 的高质量文档将传递给 LLM。

互惠排序融合(Reciprocal Rank Fusion)是一种经典的搜索引擎算法,用于将多个排名算法获得的项排名结合起来(图片来源:作者)
FLARE 或前瞻性主动检索增强生成是一种迭代的后检索策略。从用户输入和初始检索结果开始,LLM(大语言模型)迭代地猜测下一句话。然后,我们检查生成的猜测是否包含任何低概率的标记,这些标记在这里用下划线标出——如果有,我们调用检索器从数据集中检索有用的文档并进行必要的修正。
最后的思考
如果你想更直观地了解 RAG(检索增强生成)系统的不同组件,欢迎观看我关于这个主题的 Youtube 视频。LLM 和 RAG 的领域正在快速发展——对 RAG 框架的透彻理解对于欣赏每种方法的优缺点以及权衡哪种方法最适合你的用例至关重要。下次你在设计 RAG 系统时,请停下来问问自己这些问题——
-
我的数据来源是什么?
-
我应该如何分块我的数据?我的数据领域中是否有固有的结构?我的数据块是否需要额外的上下文(上下文分块)?
-
我需要语义检索(嵌入)还是更精确的匹配检索(BM-25)?我预期用户会发出什么样的查询?
-
我应该使用什么数据库?我的数据是图形结构吗?它需要元数据过滤吗?我想在数据库上花多少钱?
-
我如何才能最好地重写用户查询,以便轻松找到搜索结果?LLM 能否重写查询?我应该使用 HYDE 吗?如果 LLM 已经具备足够的目标领域知识,我能否使用 Astute?
-
我可以结合多种不同的检索算法,然后进行排名融合吗?(老实说,如果你能在成本和延迟上承受得起,那就做吧)
作者
访问我的 Youtube 频道,在那里我发布关于深度学习、机器学习、论文评审、教程以及与 AI 相关的任何内容(除了新闻,因为有太多关于 AI 新闻的 Youtube 频道)。以下是我的一些链接:
Youtube 频道: www.youtube.com/@avb_fj
Patreon: www.patreon.com/c/NeuralBreakdownwithAVB
如果你喜欢这篇文章,记得在 Medium 上关注我并点赞!
参考文献
向量数据库:superlinked.com/vector-db-comparison
元数据过滤:www.pinecone.io/learn/vector-search-filtering/
上下文分块:www.anthropic.com/news/contextual-retrieval
命题 / 密集 X 检索:arxiv.org/pdf/2312.06648
假设文档嵌入(HYDE):arxiv.org/abs/2212.10496
FLARE: arxiv.org/abs/2305.06983
Vision Transformer 终极指南
《Vision Transformer(ViT)全面指南》,它革新了计算机视觉
·发表于Towards Data Science ·阅读时间:7 分钟·2024 年 8 月 30 日
--
大家好!对于还不认识我的朋友们,我叫 Francois,是 Meta 的研究科学家。我热衷于解释先进的 AI 概念,并使其更加易懂。
今天,我们来深入探讨计算机视觉领域的一项重大贡献:Vision Transformer(ViT)。

将图像转换为图块,图像来源:作者
先来一点历史背景……
Vision Transformer(ViT)由 Alexey Dosovitskiy 及其团队(Google Brain)在 2021 年提出,相关论文为《一张图胜过 16×16 个词》。当时,Transformers 已证明是解锁 NLP 任务卓越性能的关键,且这一模型首次在 2017 年的标志性论文《Attention is All You Need》中提出。
在 2017 到 2021 年间,已有多次尝试将注意力机制整合到卷积神经网络(CNN)中。然而,这些大多是混合模型(结合了 CNN 层和注意力层),并且缺乏可扩展性。Google 通过完全去除卷积操作,利用其计算能力来扩展模型,解决了这一问题。
这篇文章所回答的百万美元问题是……
《大语言模型量化终极手册》
深入探讨大语言模型的量化与技术
·发表于 Towards Data Science ·15 分钟阅读·2024 年 7 月 10 日
--

图片由 Siednji Leon 提供,来自 Unsplash
大语言模型在 CPU 上运行?没错,你没听错。从处理对话到生成自己的图像,人工智能自诞生以来已经走了很长一段路。但这也带来了瓶颈。随着模型的扩展,其计算需求也在增加。人工智能开始严重依赖计算能力。为了满足这些需求,我们转向了 GPU,而其余的就是历史了。
许多设备没有强大的 GPU,因此无法体验人工智能的能力。为了能够在计算能力有限的设备(如手机或仅有 CPU 的电脑)上运行 AI 模型,必须缩小这些模型的规模和功耗。早期的尝试包括剪枝和蒸馏等技术。然而,这些方法在大语言模型面前并不适用,因为大语言模型通常具有大规模的架构。
最近的人工智能革命,特别是大语言模型(LLMs)的发展,或多或少依赖于云服务器进行训练、部署和推理。然而,主要厂商现在正在将大语言模型的能力扩展到边缘设备。微软的 copilot+PC 就是一个很好的例子,值得期待。随着我们朝着边缘部署的方向前进,优化大语言模型的大小变得至关重要,同时又不影响性能或质量。实现这一优化的一个有效方法是通过量化技术。
数据科学领导者的终极生产力系统
一个优化数据科学领导者所能传递价值的框架。
·发布于Towards Data Science ·9 分钟阅读·2024 年 10 月 28 日
--

图片来自Andrik Langfield 在Unsplash上的分享
从个体贡献者到领导角色的转变可能是具有挑战性的。对于数据和技术专业人员来说,通常领导阶梯的第一步是技术领导者,或者在我个人的案例中是首席数据科学家。
要在这个层级的角色中做好工作,你仍然需要将一部分时间保持亲力亲为。技术领导者应该能够就项目方法提供建议,解决最棘手的技术挑战,并指导团队成员提升他们的数据科学技能。技术技能如果不每天使用,会迅速退化,因此将这些技能持续应用于实践是技术领导力的重要组成部分。
技术技能如果不每天使用,会迅速退化,因此将这些技能持续应用于实践是技术领导力的重要组成部分。
挑战在于,你需要在这些亲力亲为的工作和管理人员、团队以及项目所带来的同样重要的优先事项之间找到平衡。
《企业数据质量“谁做什么”指南》
一个答案,以及许多最佳实践,说明大型组织如何在现代数据平台上实现数据质量管理。
·发布于Towards Data Science ·9 分钟阅读·2024 年 9 月 12 日
--

关于“谁做什么”的企业数据质量答案。图片由作者提供。
我曾与全球最大企业的数据专业人士交流,其中一个最常见的数据质量问题是:“谁做什么?”这个问题后面通常紧跟着,“为什么以及如何?”
这是有原因的。数据质量就像是一场接力赛。每一阶段——检测、分诊、解决和衡量——的成功都依赖于其他阶段。每次接力棒传递时,失败的可能性会急剧上升。

图片来源:Zach Lucero提供,来自Unsplash
实际问题需要实际的答案。
然而,每个组织围绕数据的组织方式略有不同。我曾见过一些拥有 15,000 名员工的组织将所有关键数据的所有权集中化,而一些规模只有其一半的组织则决定完全在业务领域之间分散数据所有权。
就本文而言,我将参考最常见的企业架构,它是两者的混合体。这也是大多数数据团队的目标,它还具有许多跨团队责任,使其特别复杂且值得讨论。
只需记住,接下来提供的是一个答案,而不是唯一的答案。
本文内容:
-
数据产品的重要性
-
检测
-
分诊
-
解决
-
衡量
-
追求数据质量的金标准
数据产品的重要性
无论是追求数据网格策略,还是其他完全不同的策略,现代数据团队普遍认识到,需要围绕他们最有价值的数据产品进行对齐并加大投入。
这是赋予数据集、应用程序或服务的一个称号,其输出对业务具有特别的价值。这可以是一个带来收入的机器学习应用,或者是从精心策划的数据中提取的一组洞察。
随着规模和复杂性的增长,数据团队将进一步区分基础数据产品和派生数据产品。基础数据产品通常由一个中央数据平台团队(有时是与源对齐的数据工程团队)负责。它们的设计目的是服务于跨多个团队或业务领域的数百个使用场景。
派生数据产品建立在这些基础数据产品之上。它们由与领域对齐的数据团队负责,并专为特定使用场景设计。
例如,“单一客户视图”是一个常见的基础数据产品,它可能为派生数据产品提供支持,比如产品追加销售模型、流失预测和企业仪表盘。

基础数据产品与派生数据产品的区别对于大型组织至关重要。图片由作者提供。
在这两种数据产品类型之间,检测、分诊、解决和衡量数据质量事件的过程是不同的。弥合它们之间的鸿沟至关重要。这是我看到数据团队普遍采用的一种方式。
检测
基础数据产品
在变得可发现之前,每个基础数据产品应该有一个指定的数据平台工程负责人。这是负责对整个数据管道进行端到端监控的团队,监控内容包括新鲜度、数据量、模式和基准质量。大多数团队遵循的一个基本规则是:“你构建它,你拥有它。”
所谓基准质量,我指的是能够广泛地在许多数据集和领域中进行泛化的要求。这些要求通常由中央治理团队定义,针对关键数据元素,并且通常符合数据质量的六个维度。例如:“id 列应始终是唯一的”或“该字段始终应格式化为有效的美国州代码”。
换句话说,基础数据产品所有者不能仅仅确保数据按时到达。他们需要确保源数据完整且有效;数据在各个来源和随后的加载中保持一致;并且关键字段没有错误。在这方面,机器学习异常检测模型可能特别有效。
更精确和定制的数据质量要求通常依赖于使用场景,且更适合由派生数据产品所有者和下游分析师来应用。
派生数据产品
数据质量监控也需要在派生数据产品层级进行,因为不良数据可以在数据生命周期的任何阶段渗透。

即使在基础数据产品层级数据质量良好,也不能保证在派生数据产品层级数据不会出现问题。图片由作者提供。
然而,在这一层级,需要覆盖的面更广。“监控所有表格的所有可能性”并不是一个实际的选择。
何时一组表格应该成为派生数据产品,涉及许多因素,但这些因素最终可以归结为对持续价值的判断。这通常由基于领域的数据管理员来执行,他们贴近业务,并有权遵循关于使用频率和关键性的通用指南。
例如,我的一位同事在担任国家媒体公司数据平台负责人时,让一位分析师开发了一个主内容仪表板,该仪表板迅速在新闻编辑部中流行起来。随着它在足够多的用户工作流中扎根,他们意识到这个临时仪表板需要进行产品化。
当派生数据产品被创建或识别时,应该有一个对齐的领域负责人,负责端到端的监控和基础数据质量。 对于许多组织来说,这通常是领域数据管理员,因为他们最熟悉全球和本地的政策。其他所有权模型包括指定构建派生数据产品管道的嵌入式数据工程师,或拥有最后一英里表格的分析师。
在派生数据产品层级的检测工作流中,另一个关键的区别是业务规则。
有一些数据质量规则是无法通过自动化或从中央标准生成的,它们只能来自业务。 例如,“当 account_type 等于商业且 customer_region 等于 EMEA 时,discount_percentage 字段不能大于 10。”
这些规则最好由分析师,特别是表格所有者,根据他们的经验和业务反馈来应用。 并不是每一条规则都需要触发数据产品的创建,这样做太重且繁琐。这个过程应该是完全去中心化、自服务且轻量的。
分类
基础数据产品
在某些方面,确保基础数据产品的质量比确保衍生数据产品的质量更简单。按定义,基础数据产品数量较少,通常由技术团队负责。
这意味着数据产品负责人或平台团队中的值班数据工程师可以负责常见的任务,例如响应警报、确定可能的起源点、评估严重性以及与消费者沟通。
每个基础数据产品应该至少在 Slack 或 Teams 中有一个专门的警报渠道。

有很多方法可以组织数据质量通知策略,但最佳实践是确保每个基础数据产品都有自己的专用渠道。图片由作者提供。
这可以避免警报疲劳,并作为所有有依赖关系的衍生数据产品负责人之间的中央沟通渠道。根据需要,他们可以跟进问题,主动了解任何可能影响其操作的即将发生的模式或其他变化。
衍生数据产品
通常,衍生数据产品的数量太多,数据工程师的带宽有限,无法妥善分诊。
让每个衍生数据产品负责人负责分诊警报是一种常见的策略(见下图),但随着依赖关系的增加,这种方法也可能会失效。

衍生数据产品负责人的数据分诊流程。图片由作者提供。来源。
例如,一个失败的编排任务可能会级联下游,导致多个数据产品负责人收到数十个警报。重叠的紧急演练简直是一场噩梦。
一个日益被采纳的最佳实践是,专门的分诊团队(通常称为 dataops)来支持一个特定领域内的所有产品。
这可以成为一个适中的区域,在享受专业化效率的同时,又不会大到无法处理,成为一个没有背景信息的瓶颈。这些团队必须接受指导并被授权跨领域工作,否则你只会重新引入信息孤岛和重叠的紧急演练。
在这种模式下,数据产品负责人有责任,但没有直接的责任。
解决方案
Wakefield Research 调查了超过 200 名数据专业人士,调查显示每月平均发生 60 次事件,每次检测到事件后的中位解决时间为 15 小时。很容易看出,数据工程师是如何被积压的任务埋没的。
造成这种情况的因素有很多,但最大的问题是我们在技术和流程上都将异常与根本原因分开了。数据工程师照看他们的管道,分析师照看他们的指标。数据工程师设置他们的 Airflow 警报,分析师编写他们的 SQL 规则。
但管道——数据源、移动数据的系统以及转化数据的代码——是导致指标异常发生的根本原因。
为了减少平均解决时间,这些技术故障排除人员需要一个数据可观测性平台或某种中央控制平面,以便将异常与根本原因关联起来。例如,一个解决方案可以展示折扣金额(discount_amount)字段中的分布异常是如何与上游查询变更在同一时刻发生的相关联的。
测量
基础数据产品
说到主动沟通,衡量和展示基础数据产品的健康状况对于它们的采纳和成功至关重要。如果下游的消费领域不信任数据的质量或其交付的可靠性,他们会直接回到数据源。每次。每次。
这当然违背了基础数据产品的整个目的。规模经济、标准化的入职治理控制、对数据来源和使用情况的清晰可见性现在都不复存在。
提供一个适用于各种使用案例的数据质量通用标准可能会很具挑战性。然而,下游数据团队真正想了解的是:
-
数据多久刷新一次?
-
它的维护情况如何?事件解决得有多快?
-
是否会有频繁的架构变化导致我的管道崩溃?
数据治理团队可以通过揭示这些常见需求和 关键数据元素 来提供帮助,以帮助在市场或目录中设定并呈现智能的服务水平协议(更多的实施细节可以在 这里 找到)。

图片由作者提供。
这是罗氏数据团队的方法,他们创建了世界上最成功的企业数据网格之一,他们估计已经生成了约 200 个数据产品,估计为公司带来了 5000 万美元的价值。
派生数据产品
对于派生数据产品,应该根据定义的使用案例设定明确的服务水平协议。例如,一个财务报告可能需要高度准确,且在及时性上有一定的宽容度,而一个机器学习模型则可能完全相反。
表级健康得分可能会有帮助,但常见的错误是认为在共享表上,一个分析师所设定的业务规则对另一个分析师也同样适用。表格看起来质量较低,但经过仔细检查,发现一些过时的规则每天反复失败,而没有采取任何行动来解决问题或调整规则的阈值。
追求数据质量的卓越
我们覆盖了很多内容。本文更像是一场马拉松,而非接力赛。
上述工作流程是实现数据质量和数据可观测性项目成功的一种方式,但它们不是唯一的方式。如果你优先考虑清晰的流程:
-
数据产品的创建和拥有;
-
在这些数据产品中应用端到端的覆盖;
-
自助服务的下游资产业务规则;
-
响应并调查警报;
-
加速根本原因分析;以及
-
通过沟通数据健康和操作响应来建立信任
…你会发现你的团队正在跨越数据质量的终点线。
在 Medium 上关注我 了解更多关于数据工程、数据质量和相关话题的故事。
人工智能的“谁、什么、为什么”
成功始于别人没有问的问题
·发表于Towards Data Science ·9 分钟阅读·2024 年 9 月 12 日
--

如果人工智能是答案,那么问题是什么?
不幸的是,这种哲学性的思考方式正是许多人今天面对人工智能机会时的态度。
这不仅仅是通往无意义或低效投资的捷径,它还是一种确保你未能完全实现可能为你的企业和问题带来的潜力的方式。
正如爱因斯坦所说(某种版本的):
“如果我只有一个小时来拯救世界,我会花 55 分钟定义问题,剩下的 5 分钟来寻找解决方案。”
那么,我们该如何扭转目前的做法,以增加获得真正成功的机会呢?
通过真正批判性地评估我们的机会和计划,从多个角度出发。我建议像一个内心的小孩一样,提出所有的问题(并且不断地提问)。接下来,我将列出这些初步问题应该是什么。

图片由Marsha Reid提供,来自Unsplash
为什么?
布隆过滤器的奇妙之处:实用指南
在本文中,我们将探讨布隆过滤器以及它们如何大大提升应用性能。了解这种巧妙的数据结构如何显著减少内存使用并加速大数据集的查询!
·发布于 Towards Data Science ·阅读时间 8 分钟 ·2024 年 8 月 10 日
--

图片由 Nathan Dumlao 提供,来自 Unsplash
在班加罗尔的一家热闹的咖啡馆里,Perry 和 Alex 手捧冰咖啡,围坐在笔记本电脑前,准备解决他们最新的数据管理挑战。
Perry: “我们在数据库查询方面遇到了一个重大问题。我们每天大约收到 1000 万次请求,但不幸的是,大约 40% 的查询都返回了‘未找到数据’。查询那些几乎总是不存在的记录,感觉是巨大的资源浪费。”
Alex: “那样效率会很低。你有没有考虑过一种数据结构,可以在执行查询之前,快速检查某个项目是否可能存在于数据库中?”
Perry: “我在考虑哈希表,它们提供快速查找,因为在平均情况下,它们对搜索的时间复杂度是常数。考虑到数据量,似乎是个不错的选择。”
Alex: “哈希表确实提供快速查找,但它们会消耗大量内存,例如,如果你有…
有一种正确的犯错方式
如何通过结合业务背景和错误成本来做出更好的预测
·发布于 Towards Data Science ·阅读时长 15 分钟·2024 年 9 月 16 日
--

作者图片(通过 Midjourney)
有一天,我母亲在左眼出现了视力问题,于是去看了医生。医生进行了快速检查,并得出结论认为没有问题;只是随着年龄增长,眼睛逐渐变差了。
不久之后,症状加重,我母亲去进行了第二次诊断。结果发现她患有视网膜脱落,而延误治疗导致了永久性损伤。
人们在工作中经常犯错;你不可能在所有情况下都正确。但有些错误比其他错误更昂贵,我们需要考虑到这一点。
如果医生当时说“可能有问题”,并让母亲去做进一步检查,那么即使结果都是阴性,也可能证明什么都没有。然而,在那种情况下,错误的成本只是浪费了一些时间和医疗资源,而不是造成器官的永久性损伤。
医学领域是一个极端的例子,但相同的逻辑也适用于数据科学、业务运营、市场营销或产品等职位:
在做出预测时,我们应该考虑到错误的后果或成本。
关于机器学习测试,没人告诉你的事
如何避免灾难
·发表于 Towards Data Science ·7 分钟阅读·2024 年 1 月 4 日
--
你已经准备好将你精心设计、专业调优和准确训练的算法部署到那个叫做“生产”的最终前沿。你已经收集了高质量的测试集,并对算法的表现感到高兴。是时候发布它并认为今天的工作圆满完成了!但不要太急。
没有人愿意在事后被叫去修复一个令人尴尬的机器学习应用。如果你的客户已经指出有问题,那就意味着你失去了信任。现在是时候疯狂地尝试调试并修复一个可能涉及数十亿自动学习参数的系统了。

图片由作者使用 Stable Diffusion 创建
如果这听起来有些抽象或不太可能,以下是我自己职业生涯中的一些例子,这些模型在“测试”集上表现良好,但在实际应用中却有不同的结果:
-
一个用于预测建筑能效审计节能的模型对于大多数测试建筑来说很准确。然而,在实际数据中,它预测一个单独的场所节省的能源比整个州消耗的还要多。用户显然更关注那些异常值,而不是正确的预测。
-
一个用于理解建筑中哪个设备驱动能耗的模型,当上游系统用零填充缺失的家庭区域数据而不是预期的空值时,突然给出了异常的结果。这导致了一个持续数周、跨多个团队的努力,以修复问题并重新生成结果。
当然,你知道,尽管真正的价值在于机器学习,你已经构建了软件,并且所有的常规规则都适用。你需要单元测试、对集成点的审查以及监控,以捕捉真实系统中出现的众多问题。但是,如何有效地做到这一点呢?随着你改进模型,输出预计会发生变化,而你训练时的假设则会受到变化世界的影响。
(大致)围绕模型进行单元测试
# Not a great use of test code
def test_predict(model_instance, features):
prediction = model_instance.predict(features)
assert prediction == 0.133713371337
很显然,上述测试非常脆弱,无法捕捉到许多潜在问题。它仅仅测试我们的模型是否生成了最初训练时预期的结果。不同的本地和生产环境中的软件和硬件栈使得在推进部署的过程中,它容易出现问题。随着模型的演化,这会带来比其价值更多的维护工作。你预测管道的大部分复杂性都来自于数据收集、预处理、清洗、特征工程以及将预测结果包装成有用的输出格式。在这方面,更好的测试可以让任务变得更加轻松。
下面是你应该做的事情。
-
彻底地单元测试你的特征生成和后处理。 你的特征工程应该是确定性的,可能涉及多个数据源的处理以及一些非平凡的变换。这是进行单元测试的一个绝佳机会。
-
单元测试所有的数据清洗和边界检查。 你确实有一些代码专门用于清理输入数据并确保它与训练时的数据一致,对吧?如果没有,请继续阅读。一旦你有了,测试这些检查。确保所有的断言、裁剪和替代都能让你的预测安全,免受真实数据世界的挑战。确保在应该发生时抛出异常。
-
额外加分:使用近似断言。 如果你还没有意识到,有一些简单的方法可以避免由于对精确浮动点进行断言而导致的难以捉摸的失败。Numpy 提供了一套近似断言,可以在与你的应用相关的精度水平上进行测试。
怀疑集成问题
曾经玩过“传话游戏”吗? 在每次传递时,理解度都会下降。复杂系统有许多传递环节。 你对每个团队成员在快速交付时,如何全面地文档化和沟通数据语义有多少信心?或者,几个月或几年后,当你做出更改时,你对自己能否精确地记住所有这些事情又有多大信心?
复杂软件中的管道是问题频发的地方。解决方案是创建一组黑盒测试用例,测试整个管道的输出。尽管这需要定期更新,如果你的模型或代码频繁变化,但它覆盖了大量的代码并且可以快速发现未预见的影响。所花的时间是值得的。
def test_pipeline(pipeline_instance):
# Execute the entire pipeline over a set of test configurations that
# exemplify the important cases
# complex inputs, file paths, and whatever your pipeline needs
test_configuration = 'some config here'
# Run the entire flow. Data munging, feature engineering, prediction,
# post processing
result = pipline_instance.run(test_configuration)
assert np.testing.assert_almost_equal(result, 0.1337)
测试整个管道保持 ML 应用的健康。
信任是有代价的。
在开发 ML 管道时,偏执是一种美德。你的管道和依赖关系越复杂,出错的可能性就越大,而这些错误往往是你的算法至关重要的部分。即使你的上游依赖由有能力的团队管理,你真的能指望他们永远不会犯错吗?输入数据绝对不会被破坏的可能性为零吗?大概不行。但是,为人类所犯错误做准备的公式其实很简单。
-
坚持已知的输入范围。
-
快速失败。
-
高声失败。
最简单的方法是在管道的早期尽早检查已知的输入范围。你可以手动设置这些范围,或者在训练模型时与模型一起学习这些范围。
def check_inputs(frame):
""" In this scenario we're checking areas
for a wide but plausible range. The goal is really just to make sure
nothing has gone completely wrong in the inputs."""
conforms = frame['square_footage'].apply(lambda x: 1 < x < 1000000)
if not conforms.all():
# Fail loud. We're no longer in the state the pipeline was designed for.
raise ValueError("Some square_footage values are not in plausible range")
上面的例子演示了公式。只需对每个输入重复此操作。将其放在管道的最前面。嘈杂的验证函数实现起来很快,并能帮助团队避免不幸的后果。这种简单的检查方法本可以帮助我们避免那次不幸的“空值替换为零”错误。然而,这些测试并不能捕捉到所有涉及多变量交互的场景。这时,稍后提到的 MLOps 技术便会发挥作用,显著提高鲁棒性水平。
运行真实数据障碍赛。
单元测试就像一把外科手术刀,能够精确控制并执行代码中的具体路径。集成测试则非常适合检查数据是否按预期流经整个系统。然而,在真实数据中总是存在“未知的未知”。
理想的部署前检查是将你的 ML 管道应用于尽可能多的真实数据,具体取决于时间和成本。接下来,对结果进行剖析,以发现异常值、错误和边缘情况。作为额外的好处,你还可以利用大规模执行进行性能测试和基础设施成本估算。
另一个有效的策略是“软启动”你的模型。在正式发布之前,先让一小部分用户体验。这让你能够发现任何负面的用户反馈,并且可以在小规模上找到真实世界中的失败,而不是在大规模上发现。这也是一个很好的时机,可以对现有或替代方案进行 A/B 测试。
测试永远不够。
创建并认真维护单元测试只是开始。显然,实时软件需要异常处理策略、监控和告警。当软件依赖于一个可能很快过时的学习模型时,这一点尤为重要。
MLOps 领域已经发展起来,专门解决这些挑战。本文不会深入概述 MLOps 的现状,但这里有一些快速的想法,帮助你监控 ML 应用程序,超越"黄金信号"。
-
寻找目标漂移——预测分布与长期或测试平均值之间的偏差。 例如,在足够大的样本中,预测的类别应与基础率分布相似。你可以监控最近预测与预期分布之间的偏离,作为某些事情发生变化的迹象。
-
特征漂移的监控同样重要,甚至比预测漂移更为重要。 特征是你对世界的快照。如果它们的关系不再与模型学习到的关系匹配,预测的有效性就会急剧下降。类似于监控预测,关键在于监控特征与初始分布之间的偏离。监控特征之间关系的变化更为强大。特征监控会在用户发现之前捕捉到储蓄模型预测出不可能的值。
大型云平台工具,如 Azure AI、Vertex AI 和 SageMaker,都提供内置的漂移检测功能。其他选项包括 Fiddler AI 和 EvidentlyAI。欲了解更多关于如何选择机器学习技术栈的想法,请参阅 适合每个团队的机器学习扩展选项。
结论
从训练到部署及以后的整个机器学习流程的保持最佳状态是一项挑战。幸运的是,凭借精明的测试和监控策略,这完全是可以管理的。密切关注一些关键信号,以防止即将发生的灾难!单元测试管道,以检测大规模代码库中的断裂。尽可能多地利用你的生产数据。监控预测和特征,以确保你的模型保持相关性。
使用 Python 可以做的事情:高级和特殊用例
·发表于 Towards Data Science ·作为 时事通讯 发送 ·阅读时间 3 分钟 ·2024 年 2 月 15 日
--
Python 在数据科学和机器学习工作流程中发挥着至关重要的作用,以至于它有时几乎融入了我们的日常节奏;毕竟,你有多久没有考虑过办公室的开关或门把手了呢?你也经常使用它们。
在我们 2024 年第一期以 Python 为中心的《Variable》版本中,我们决定聚焦一些我们最近发布的更有趣且与众不同的用例。我们喜欢好的 Pandas 或 Matplotlib 教程——我们的许多读者也一样——但有时从日常的基础话题中稍作休息,深入探讨一些更高级的内容也是很有趣的。本周,让我们稍微放纵一下!我们希望你们喜欢我们挑选的九篇 Python 文章,它们涵盖了丰富多样的项目和挑战。
-
如何构建一个基于图的神经网络进行异常检测的六个步骤 在她的最新逐步教程中,Claudia Ng,她经常撰写有关有趣的基于 Python 的项目的文章,重点介绍了构建一个基于图的神经网络,该网络能够处理异构图数据以进行链接预测。
-
介绍 Quad-Tile 图表与 Squaremap:让数据方块化为什么要满足于现有的可视化格式,当你可以创建自己的格式?Nick Gerend邀请我们一起走进 Quad-Tile 图表背后的制作过程,这是一种通过 Python 实现的无坐标轴方法,将一组数值可视化为方块。
-
使用 bart-large-mnli 标记登山事故报告仍然是在创新可视化领域,Karla Hernández带我们走过创建一个惊艳的数据故事艺术品的步骤,突出了从自定义标记的登山事故数据集中得到的发现。
-
有限自动机模拟与 AI 辅助系统的应用通过使用有限状态机,Sofya Lipnitskaya探讨了一种优化复杂现实世界 AI 辅助过程性能的潜在方法——在本例中,一个对象检测系统会启动喷水装置来吓跑入侵的鸡群!

图片由 Nicholas Safran 提供,来自 Unsplash
-
从太空看风暴:一个创建惊人视图的 Python 脚本处理地理空间数据有其自身的挑战;Mahyar Aboutalebi, Ph.D.的最新指南详细介绍了如何编写一个 Python 脚本,帮助你收集卫星图像并将其转化为强大的风暴动画。
-
Python 最强大的装饰器如果你错过了,Siavash Yasini对 Python 的 @property 装饰器的详细介绍是我们最近几周阅读量最高的编程文章之一。文章介绍了几种利用其强大功能的实用方法:从保护数据属性免受覆盖到懒加载和内存优化。
-
塑造想象力:利用人工智能创造新的 3D 打印对象在文字、图像、音乐和视频之后,3D 对象是否会成为生成型人工智能的下一个前沿?罗伯特·A·贡萨尔维斯分享了他最近的实验结果,这些实验依赖于 Midjourney 进行图像生成,并利用一些经典的 Python 代码将这些图像转化为实际的 3D 对象。
-
文本嵌入:全面指南如果你是文本嵌入领域的新手,玛丽亚·曼苏罗娃的入门指南是一个很好的起点——它既(非常)全面又易于理解,实践部分还包含了你开始动手操作时所需要的所有 Python 代码片段。
-
理解连接点(链条、分叉和碰撞点)及其在因果推断中的作用在他最近对有向无环图(DAGs)的深入探讨中,格雷厄姆·哈里森聚焦于连接点类型及其在因果推断任务中的重要性。在此过程中,他还展示了如何生成数据集,执行普通最小二乘法(OLS)回归等操作,所有这些都依赖于——你猜对了——Python。
感谢您支持我们作者的工作!如果您感到受到启发,为什么不写下您的第一篇文章呢?我们很想读一读。
直到下次变量再见,
TDS 团队
以为你了解 Excel 吗?通过 Power Query 将你的分析技能提升到新高度!
5 个实用的案例,证明 Power Query 值得深入探索。
·发表于Towards Data Science ·阅读时长:7 分钟·2024 年 11 月 29 日
--
我有一个 confession:我一直生活在石头下 🪨。不是字面上的意思,但我怎么能解释,直到现在我才发现Power Query在 Excel 中存在呢?
想象一下,你意识到那些花费了无数小时整理 VLOOKUP、嵌套 IF 和处理凌乱数据的时间,其实可以通过几个简单的点击来替代。

图片由作者生成
Power Query 能够做 Excel 公式可以做的一切——只是更快、更智能,且更少令人沮丧。从合并数据集到轻松的转换与创建计算列,可能性是无穷无尽的。
网络上已经有大量文章和视频提供了详细的步骤指南,帮助你入门,所以我不会再写一篇如何操作的教程。相反,我将分享一些让我大吃一惊的功能,并通过5 个使用案例,希望能够激励你亲自探索这个强大的工具。🚀
为了展示其卓越的功能,我将使用一个简单的电子商务数据集,该数据集包含两个 CSV 文件:一个是客户数据,另一个是交易数据。


这些屏幕展示了每个 CSV 文件的前 10 行样本(由作者生成)
使用案例 1:合并数据集(VLOOKUP 的更智能替代方案)
当涉及到数据合并时,我们都会使用 VLOOKUP。但让我们面对现实——VLOOKUP 有它的局限性。
为什么 Power Query 比 VLOOKUP 更好✨:
-
你可以一步完成从多个列连接数据(无需重复查找)。
-
你的查找列不必在左边——无论它位于何处,都可以正常工作。
-
它支持不同的连接类型(左连接、右连接、全连接、内连接、反连接),让你对数据如何合并有更多的控制。
-
与 VLOOKUP 不同,Power Query 不仅限于通过一个列进行连接。
-
你甚至可以使用模糊匹配来处理带有拼写错误的混乱数据集!
如何在 Power Query 中合并数据集 🔗:
-
在首页选项卡中选择“合并查询”。
-
选择你要合并的两个数据集(或文件)。
-
选择用于连接的列并指定连接类型(例如,左连接保留一个表中的所有行,或者内连接仅匹配重叠的行)。
-
合并后,选择你想包含在最终数据集中的列。
-
将转换后的数据加载回 Excel。
几秒钟内完成!⏱️

这个 gif 解释了使用 Power Query 合并两个文件的过程(由作者生成)
用例 2:列转换
Power Query 让你非常轻松地转换数据。从快速计算到日期处理和创建区间,它能在几秒钟内完成在 Excel 中可能需要几分钟(甚至更长时间)的工作。
让我通过几个示例来展示给你看。👀
执行快速计算 ➕➖➗✖️
假设你需要将价格从美元转换为欧元,并计算销售的总值。在 Power Query 中,你可以:
-
转到“转换”选项卡,在“标准”下选择“乘法”。
-
将所有价格乘以 0.95 以转换货币。
-
添加一个新列,通过将价格乘以数量来计算总销售额。为此,转到“添加列”选项卡并选择“自定义列”。
所有这些操作只需几次点击即可完成。

这个 gif 解释了使用 Power Query 进行快速计算的过程(由作者生成)
轻松修改日期 📅
Power Query 使处理日期变得简单。你可以快速提取月份名称,或者仅显示前三个字母(例如,Jan,Feb,Mar)以获得更简洁的外观,使用的是内置功能。
-
选择日期格式的列。
-
转到“添加列”选项卡,在“日期”下选择“月份”,然后选择“月份名称”。
-
选择包含月份名称的新列。在“提取”下,选择“提取前几个字符”,然后在弹出窗口中指定要提取的字符数(此处为 3)。
无需复杂的公式!

这个 gif 解释了使用 Power Query 修改日期的过程(由作者生成)
创建无复杂公式的区间 📥
我不知道你怎么样,但我总是忘记在 Excel 中创建区间的 IF 公式语法,如果有多个区间的话,公式会变得非常长。不过使用 Power Query,就简单多了。
-
如果你使用的是 Windows,你可以使用 “示例列” 功能。你只需要输入一个区间范围的示例(例如,“41–50”),Power Query 会自动为你填充其余的值,节省你的时间。
-
对于我们使用 Mac 的人(像我一样),遗憾的是这个功能不可用。但别担心,还有解决办法!你仍然可以使用 “条件列” 功能,通过设置逻辑规则来将数据分类到不同的区间。

这个 GIF 解释了如何使用“条件列”功能创建区间的过程(由作者生成)
用例 3:填充缺失数据 🔎
我们都处理过包含缺失值的数据集——无论是因为输入不完整还是数据不一致。在大多数情况下,你不想留下这些空白,而是希望填补这些空缺。这时 Power Query 就显得特别有用。
假设我们在“每单位价格”列中有缺失值,并且我们想要用该类别的平均价格来替换这些缺失值。以下是如何通过几个简单步骤完成的:
-
筛选“产品类别”列,只显示“美容”条目。
-
在选中“每单位价格”列后,在屏幕底部的统计部分查看美容类别的平均价格。
-
移除“产品类别”列上的筛选器,重新显示所有类别。
-
再次选择“每单位价格”列,转到 “转换” 选项卡,点击 “替换值”。
-
在对话框中,输入“null”作为要替换的值,并在“替换为”字段中输入美容类别的平均价格。
就这样,你已经用平均值填充了缺失的数据——只需几个点击。

这个 GIF 解释了如何使用 Power Query 填充缺失值的过程(由作者生成)
用例 4:转变数据以进行更好的分析 🛠️
Power Query 非常适合将数据转换成符合分析需求的格式。
例如,如果你想按月汇总总销售额并查看趋势变化,可以使用“按组分组”和“转置”功能。
下面是只需 4 步即可完成的方法:
-
选择“月份”列按月对数据进行分组。
-
在“转换”选项卡中,点击 “按组分组”。
-
为“总价值欧元”添加新的聚合,并选择求和以计算每个月的总销售额。
-
最后,点击 “转置”,将行和列互换,将月份转为列标题。
完成后,将数据加载回 Excel,并创建折线图以可视化销售趋势!

这个 GIF 解释了如何使用“按组分组”和“转置”功能的过程(由作者生成)
用例 5:使用 M 公式语言的自定义列 🪄
M 公式语言让你超越典型的 Power Query 转换,允许进行更复杂的计算和逻辑处理。当你需要为数据创建自定义解决方案时,它非常适用。
例如,假设你的销售数据中的月份没有正确排序。你可以使用M 公式为每个月分配一个数值,然后按照正确的顺序排序,而不需要手动调整。
-
转到“转换”并选择“自定义列”选项。
-
在弹出窗口中,使用“if-else if”逻辑输入,以为每个月分配一个数值。
-
按照新列的升序(ASC)进行排序。
-
排序完成后,你甚至可以删除“月份顺序”列。
完成后,你的月份就会按正确的顺序排列。🏆🏆🏆
if [Month short] = "Jan" then 1
else if [Month short] = "Feb" then 2
else if [Month short] = "Mar" then 3
else if [Month short] = "Apr" then 4
else if [Month short] = "May" then 5
else if [Month short] = "Jun" then 6
else if [Month short] = "Jul" then 7
else if [Month short] = "Aug" then 8
else if [Month short] = "Sep" then 9
else if [Month short] = "Oct" then 10
else if [Month short] = "Nov" then 11
else if [Month short] = "Dec" then 12
else null

这个 gif 解释了在 Power Query 中使用 M 公式语言的过程(由作者生成)
最后但同样重要的是……
Power Query 会跟踪应用步骤日志中的每个更改 💾,因此,如果你想返回修改或撤销任何操作,超级简单。
感到启发了吗?🧙♂️
我希望你现在对尝试这些功能的兴奋感和我一样!如果我是你,我会立刻跳进 Power Query。
《思考,快与慢》、大语言模型(LLMs)与规划领域定义语言(PDDL)
ChatGPT 从不害怕假装进行深思熟虑的推理,但——就像我们的大脑一样——可能需要额外的工具来进行准确的推理。
·发表于Towards Data Science ·15 分钟阅读·2024 年 6 月 5 日
--
“ChatGPT 可能会出错。请核实重要信息。”现在已写在提示下方,我们也都习惯了 ChatGPT 冷静地编造从日期到完整参考文献的任何内容。但那基本推理呢?通过回顾人工智能(AI)研究早期的一个简单塔楼重排任务,我们将展示大语言模型(LLM)如何达到其局限,并引入规划领域定义语言(PDDL)和符号求解器来弥补这一点。鉴于 LLM 本质上是概率性的,未来的 AI 代理版本很可能会内建此类工具,结合常识知识和精准推理。为了充分利用本文的内容,使用VS Code 的 PDDL 扩展和planutils规划器界面,搭建自己的 PDDL 环境,并与示例一起实践。
如果你不是 Medium 的订阅者,可以 在这里* 阅读这篇文章*
在大语言模型(LLM)中,每个字符实际上是根据其响应中所有前面的字符以及用户的提示进行条件化的。经过几乎所有曾经写过的内容的训练,LLM 不仅变得无所不知,甚至还具有幽默感。然而,通常不久后我们就会发现 LLM 非常懒惰,本质上…
数据科学家的三周年工作纪念:成长、反思与接受

作者提供的图片
给我自己和其他数据科学家的信
·发表于 Towards Data Science ·7 分钟阅读·2024 年 11 月 19 日
--
亲爱的子晶,
昨天,你的同事告诉你,现场举行了庆祝活动,庆祝多个工作周年纪念,包括你的工作周年。时间过得真快!到月底时,你将在这个岗位上满三年。
随着年龄的增长,时间真的变得更快了吗?我听过一种可能有道理的解释:当你 10 岁时,一年是你生命的 10%。然而,当你 30 岁时,一年仅占你生命的约 3%,这就是为什么你感觉时间过得飞快的原因。我回想起那些毫无目的、漫长的夏日时光,你在池塘里捉蝌蚪,反复观看卡通片,认为下午永远不会结束。如今,每一天都如同沙子般从指尖悄悄溜走。

图片来源:Cesar Ramos 于 Unsplash
也许时间一直在以恒定的速度流逝,但它正在经过一个不同的你。拥有青春意味着可以浪费每一分钟,并且依然相信明天会有日出。随着年龄的增长,内心的时钟开始嘀嗒作响,提醒你,时间已经不多了…
关于如何应对研究项目规划的思考与建议
管理项目规划中不确定性和约束的个人方法
·发表于 Towards Data Science ·9 分钟阅读·2024 年 7 月 30 日
--

Ensō 圆圈 [作者插图]。
在多个领域参与研发项目超过十年后,我深信成功的研究项目规划是一种艺术。任何单一的规划框架都不太可能满足所有需求。与通常从明确理解目标和方法开始的开发项目不同——“我知道我想要什么,我知道怎么做”——研究项目通常以明确的目标开始,但没有定义的方法:“我知道我想要什么,但我不知道怎么做”。
研究项目的一个显著特点是所涉及的不确定性。在这篇文章中,我将分享一些关于研究项目规划的实用想法,旨在提供一种视角,使得应对研究中的不确定性变得更加可管理和不那么令人沮丧。
贝叶斯航天器
任何项目,本质上,都是从 A 点到 B 点的移动。A 点代表起始点,包含项目的所有先前数据。这包括初步的信念、预期的假设、 promising 的初步实验、相关的文献发现以及其他知识。B 点是期望的项目成果,一组目标,其实现标志着项目成功。从 A 点到 B 点之间的空间充满了不确定性,必须谨慎应对。一个有效的项目规划通过战略性地设置里程碑来应对这种不确定性,以引导路径。每个里程碑不仅要回答特定的研究问题,还要细化我们对项目轨迹的理解。
想象一艘航天器试图找到通往 B 星的路径。它的初始轨迹是在 A 星球上计算得出的,精度是最好的,但不能保证它是正确的。然而,在航程中,航天器可以遇到导航站,帮助调整其轨迹。如果有足够的导航站并且它们正常工作,那么修正轨迹并成功到达 B 星的机会就很大。

明确的中间步骤应该能够修正轨迹 [图片来源:作者]。
合适的里程碑应该像那些导航站一样,我们应该利用项目的“推进器”根据新获得的知识调整其轨迹。缺少里程碑可能导致轨迹偏离正确的目标路径。太多的里程碑可能会让人感到疲惫和浪费时间,而设置不当的里程碑则可能让我们走上错误的道路。
设想一个合成数据生成项目,旨在将一个小型的真实数据集扩展为一个增强的数据集,以便用这个数据集训练模型。一些直接的里程碑可以是:实现合成数据生成管道、生成合成数据集,然后使用这个增强的数据集训练和评估模型。虽然可能会很诱人直接跳到数据生成阶段,但这种做法并不总是明智的,因为它可能会耗费大量时间并且可能得不到有意义的结果。在开始数据生成之前,添加一个关于通过引入来自同一领域或紧密相关领域的真实数据来提高模型性能的初步评估可能会更有利。如果潜在的提升看起来很有前景,那么可以继续进行合成数据的生成。这个里程碑可以揭示数据的增加是否仅仅带来边际改善,并且还可能提供关于哪些数据方面应该优先考虑以最大化效用的见解。最初在这两种项目轨迹之间的选择,可能对项目的成功至关重要。

一个额外的里程碑可能会带来很大的变化 [图片来源:作者]。
设置好的里程碑并非易事,但我发现,在规划过程中保持这种心理图像是有帮助的。它有助于将视角从我们知道可以采取的步骤,转向我们需要采取的步骤,以便获得更多的知识并调整项目的轨迹。
-
经验法则是:不确定性越大,应该设置的里程碑就越多。
-
优先考虑那些最能挑战项目轨迹的里程碑。
-
偏差的初始轨迹是正常的,但我们应该尽最大努力在项目过程中加以修正。
计划还是不计划?
长话短说——永远要做计划,但真正的问题是你的计划应该多么详细和深入。没有任何计划的项目更像是一次随机漫步,只有偶然的几率能到达正确的地方。另一方面,计划过于详细往往会显得没有用,因为其中的大部分内容在现实中并不会发生。
我喜欢将规划看作是一种心理上的“光束搜索”:从初步的知识开始,你想象出最可能的结果,针对特定的结果做出分支决策,然后进一步探索这些结果。此时,权衡因素就出现了:你可以让这个光束树尽可能大,扩展你的想象力,但另一方面,你也可以从一个不完整的树开始行动,并在过程中实时更新它。这就产生了规划与执行之间的权衡。规划的时间跨度越长,所带来的不确定性就越多。通常会有过度规划的诱惑,但现实往往更为复杂,很多计划分支很可能会变得过时。

分支可能会早早被切断,因此许多后续的规划变得多余。[图片来源:作者]
我曾经花了大约一个月的时间,精心规划实验,建立了一个庞大的“如果”和“否则”树,创建了模型架构和超参数的评估表格,还列出了额外数据的来源以及在项目中需要测试的各种假设。等到执行时,我在第一步就遇到了一个意外的结果——原来初步的方法不符合推理延迟的约束,我不得不完全重新考虑项目的方案。所有那些详细的规划与现实几乎没有太多关联。
在计划的细节和执行之间找到最佳平衡,与里程碑的概念密切相关。以下是一些可能帮助你构建最佳计划的参考资料:
-
获取实际数据比建立假设性计划更为有效。如果获得数据的资源消耗与建立计划差不多,那么最好先获取数据,并在这个更坚实的基础上制定进一步的计划。
-
你的计划时间跨度越长,不确定性就越大。让计划的细节与阶段成反比是有意义的:较早的步骤应更加详细,而后续步骤的细节则应减少。
-
避免过度规划。虽然过度规划很有诱惑力,但通常更好的做法是拥有一个更为概括的计划,并尽早进行现实检查。
-
对于单一事件来说,分支超过三个通常就太多了。
-
尽量减少搜索空间。
估算的“荒野西部”
你是否曾经遇到过这样的情况,“哦,我/我的团队会在一周内完成这个任务”,但是之后出现了许多复杂问题,结果最多一个月才完成?你经常遇到这样的情况吗?如果没有,恭喜你!你要么有很强的规划天赋,要么还没有遇到这个问题,可能不需要阅读这一章。
人们容易进行过于乐观的规划,这常常导致时间或预算不足。我们通常会希望最好的情境,但现实往往更为复杂。很多意外情况可能会出现,并影响我们的计划。这是极为常见的,且往往被忽视。我们多少次听到一些大项目,比如电影制作、新地铁站开通或火箭发射,推迟了或预算翻倍?
我想引用《思考,快与慢》一书中的一个例子。丹尼尔·卡尼曼描述了规划决策课程开发的案例。在准备过程中,所有同事都被要求估算完成该课程(规划练习、编写教材等)所需的时间,答案从 1.5 年到 2.5 年不等。然后,他问另一位同事是否记得类似项目及其所需时间。结果发现,对于类似项目,只有大约 40%能够按时完成,而那些能完成的项目,通常需要 7 到 10 年。最终,该项目完成了,耗时 8 年。
我个人曾经频繁遇到这个问题。有一次,在领导一个项目时,我们进入了一个最后阶段,时间越来越紧张。我为这个阶段规划了一个“完美”的情境,旨在适应时间限制并实现项目目标。这个计划的主要弱点是假设一切都会完美无缺。然而,现实中,所有不在直接控制之内的事情都出了问题——一位同事生病了,计算服务器停机了一周,另一位同事需要紧急请假。结果,最初的“完美”计划崩溃了,突显出依赖完美条件并不是一种稳健的做法。
所以我们应该接受这样一个事实:我们通常依赖于乐观的,甚至是理想化的情境,而不是最坏的情况。了解这一点后,我们应该通过这种视角来验证我们的计划。以下是几个需要考虑的要点:
-
如果你能找到类似项目的历史数据,将其作为一个合理的参考。它可能没有你预估的那么理想,但通常会更可靠。
-
问问自己,你的计划有多理想化。这个计划中有多少个事件是仅在完美的情况下才能实现的?这些点越多,它们一起成功的总概率就越低。
-
为突发问题和时间延误留出余地。然后将其乘以一个大于 1 的系数。
-
预计时间会不够用,并且不要为此而惊慌。如果按时完成,可以视为幸运。
不要死抠细节
初步的项目信念可能会被证明是错误的,期望的结果可能看起来无法达到,至少此时是这样。试图在某个时刻完成项目可能变得像是试图骑一匹死马。在项目的某些节点,应该考虑目标转向或甚至项目终止的选项。这始终是一个具有挑战性的任务,因为我们已经投入了时间和资源。我们已经深度参与其中,容易认为下一个想法可能解决问题,一切都会好起来。实际上,这个拯救项目的想法可能永远不会出现,或者可能需要数年时间才能找到(比如持续了十多年的蓝光 LED 开发)。如果你的资源不是无限的(这很可能是事实),应该考虑项目终止或转向的节点。
当然,这又是一个权衡:继续项目,寻找解决方案的机会,还是终止它并将资源投入到一个不同的、可能更有前景的方向。这里最可靠的方法之一是为你的里程碑设定时间限制:如果某个中间结果没有达成,花时间考虑它是单纯的时间估算错误,还是一个更大的问题。一种折衷方法是转向。如果从某个节点开始,项目的主要目标似乎无法实现,可以考虑定义另一个仍然重要且可能在现有成果基础上能实现的目标。
有时过于专注于最初的目标可能会导致对项目中可能出现的其他机会视而不见。因此,始终关注的不仅仅是期望的结果,还要留意所有进来的信号。其中一些信号可能会引导你找到更好的解决方案。
总结一下,关于项目终止和转向,有以下几点建议:
-
为项目的里程碑设定时间限制,并在时限到来时评估不同的视角。
-
对自己和项目的潜力要公正;将终止或转向作为有效的选项考虑。
-
宽容可能的失败。一种好的态度是将其视为在开始时凭借所拥有的知识尽力而为。现在你知道得更多,可以做得更好,更新你的计划并再次尝试。
-
尽量避免对最初项目目标的隧道视野。中间结果可能提供有趣的替代方案。
结语
拥有一个优雅的计划使得想象一切顺利进行变得容易。这是我们常见的乐观偏见之一。很容易开始相信一切都已考虑周全,并且会像精密机器一样运作。然而,尽管这个计划清晰可见,所有意外的情况却是看不见的,几乎不可能预见到。它们很可能会影响项目,因此要为此做好准备。将这些意外的挑战视为过程的一部分,而不是干扰。这样的心态允许灵活性、适应性和持续改进。
拥抱计划不完美之美,就像书法家们对待圆圈般的“圆相”(ensō)。这些圆圈以不完全的形式象征着对缺陷的接受。然而,与传统的未完成“圆相”不同,您可以随时重新审视您的计划。将您的计划视为一个灵活的指南,它会根据新的经验和数据不断调整,为前进铺平道路。
我从一次与人工智能无关的数据会议中学到的三大关键数据经验
在组织中帮助促进分析卓越的被低估概念
·发表于 Towards Data Science ·8 分钟阅读·2024 年 10 月 29 日
--

图片由作者使用微软 Co-pilot 的 AI 提示生成
作为一名数据专业人士,自职业生涯初期以来,参加会议一直是我的常态。数据科学领域广阔而多样。这意味着虽然数据职位和从业者种类繁多,但也意味着无论你的细分领域多么小,或者你的具体问题多么特殊和深奥,总有人在其他公司面临相同的问题。证明这一点的例证就是在 Stack Overflow / Kaggle 讨论区和其他知识库论坛中,永无止境的问题和梗图。早期的职业生涯中,我与数据社区的连接帮助我学习到新技术,将新知识应用到我正在解决的一些旧问题中,使我成为一个更加高效的分析师。
“许多想法在转移到另一个人的思维中时,比在最初产生的地方更加茁壮成长。” — 奥利弗·温德尔·霍尔姆斯。
最近,我开始参加数据社区的聚会和会议,并且作为讲者与专家小组共同参与讨论,能够坐在讲者休息区的座位上,这对我来说是一个巨大的改变。它极大地帮助我从创造性的角度思考我的工作和在数据管理中的角色,并成为一个更好的数据导师和守护者,帮助依赖我专业知识的人们。我最近作为讲者参加了 2024 年的数据连接大会。尽管过去几年大部分会议都集中在人工智能(AI)上,但我有幸学到了以下三个关键的数据分析和管理方面的内容,这些内容我能够轻松应用到日常工作中。在这篇文章中,我将分享我对这些会议的解读和学习心得、行动计划,以及作为一名拥有超过 12 年经验的数据从业者对这些关键数据主题的反思。
1. 成本控制
数据投资回报率(Data ROI)的概念经常被提到,但很少有人对其进行量化,也很少成为一个可以持续跟踪和共享的正式指标。成本控制一直是我关注的重点,作为一个个体贡献者和团队负责人,从我作为分析实习生的日子开始就如此。谁能忘记第一次让未经优化的 SQL 查询(包括全外连接)运行了几个小时,直到接到来自组织数据库管理员(DBA)的警告电话?(我可不记得!)从那时起,成本控制就成了一个始终萦绕在我脑海中的概念。许多数据解决方案提供商已经从基于层级的定价模型转变为基于消费的定价模型,这使得成本优化成为数据管理和领导力工具箱中的一项必备工具。
当查询优化被忽视或存在低效的数据实践时,数据团队可能会面临意外的账单。例如,除了运行大量查询之外,甚至未能归档未使用的数据也可能导致数据存储成本大幅上升。为了管理这些意外开销,实施有效的成本管理策略至关重要。这包括监控使用模式、优化查询,并设置异常活动的警报。通过了解这些隐藏成本,团队可以更好地控制数据工具的定价,确保数据投资回报率的可持续性。我还学到了投资培训数据团队以优化他们对分析工具的使用的重要性。教育员工如何编写高效的查询和处理数据,确保资源得到充分利用,这一点常常被忽视,但却是降低成本的低效果实之一。
另一个常见的陷阱是数据团队未能准确预测其数据使用量。这种疏忽可能导致大量的意外支出,特别是在快速扩展运营时。为了避免这些陷阱,企业必须与供应商保持开放的沟通,密切监控数据使用情况,并定期审查合同条款。通过预见变化并为潜在的成本波动做好准备,企业可以更好地管理数据工具定价,并确保其数据投资回报率保持正向。
可操作的收获:
-
管理开支: 为了有效监控开支,数据团队应该实施全面的仪表板,提供实时的支出趋势洞察。这些工具应突出每日、每周和每月的成本趋势,使团队能够迅速发现异常情况。此外,设置自动警报以监测不寻常的使用或支出激增,可以进一步防止预算超支。
-
预期波动: 与供应商保持开放的沟通,密切监控数据使用情况,并定期审查合同条款。预见变化并为潜在的成本波动做好准备,对于准确预测开支至关重要。
-
数据与流程审计: 定期审计所有与数据相关的开支,发现节省机会,同时不牺牲数据质量。
2. 数据翻译与其他证明数据团队价值的有效方式
尽管成本控制是影响数据团队投资回报率(ROI)的一个重要因素,但这枚硬币的另一面是衡量数据分析工作的价值和效果,最终衡量数据团队的表现。当我在整理听完与数据分析团队效率相关的讲座笔记时,一位主持人引发了一个关于“数据翻译”的有趣讨论,强调了为弥合数据团队与业务团队之间的认知差距而进行专门组织努力的必要性。

弥合数据与业务素养差距:图像由作者通过 Microsoft Co-pilot 的 AI 提示生成
随着数据基础设施和数据团队招聘成本的持续增加,企业必须看到这一投资的回报。高昂的成本只有在数据团队的工作能够转化为推动业务增长、创新和效率的可操作性洞察时,才是合理的。如果没有明确的价值,这些开支可能会显得沉重。数据领导者需要确保他们的团队与业务目标对齐,并专注于那些能够带来可观回报的项目。通过有效管理这些成本,组织可以保持竞争优势,并将数据作为战略资产加以利用。这要求资源的谨慎分配,优先考虑具有影响力的项目,并培养数据素养的文化,以最大化数据团队在整个组织中的效用和影响力。
可操作的收获:
-
数据翻译:赋能你的数据团队成为优秀的故事讲述者,始终记录数据分析、流程效率带来的关键成果,并在整个组织内分享成功故事。许多组织设有专门的“数据翻译者”角色,负责这些工作。
-
了解你的价值:关注团队成本,了解薪资趋势,并通过激励行动的分析支持你的投资。
-
像专家一样互动:使用如利益相关者参与矩阵这样的工具。识别关键人物并建立稳固的关系。你的目标是?让每个人与公司的战略目标保持一致。
-
规划路径:通过大胆设想、富有创意,并挑选那些能够产生重大影响的项目来制定战略计划。记住 80–20 法则——在日常维护与创新空间之间保持平衡。
-
架起桥梁:提升整个组织的数据素养。提供有针对性的培训,弥补知识差距,赋能每个人自信地使用数据。
-
超越孤岛思维:推动跨部门合作,以同步每个人的优先事项。这将创造一个高效运转的机制,让每个人的努力与业务的大局相一致。
3. 信息设计与数据故事讲述中的新策略

作者在 2024 年数据连接大会上展示的图片
当我参加演讲教练训练营时,我不禁发现公共演讲技巧与我们工作中常常学习的数据故事讲述技巧之间的相似之处。头脑风暴和选择一个全面的主题,接受不完美,并最终将其整理成一个整体的核心思想的过程,使我反思数据故事讲述不仅仅是漂亮的图表和记录模式。我还听了一场关于数据展示中有效视觉沟通技巧的精彩演讲。我了解到,通过将叙事与数据可视化结合,可以同时调动观众大脑的逻辑和情感两方面,使得你的见解更具记忆性。这种方法确保数据不仅仅被视为页面上的数字,而是作为战略决策的关键推动力。我还了解到,将数据故事与观众的需求和数据素养对齐,是鼓励他们根据数据采取有意义行动的最简单方法。
可操作的收获:
-
明确定义数据故事的目的:提前让观众了解学习目标并明确主要的收获。这个核心信息作为你的叙事基础,指导你在数据可视化和故事讲述技巧方面的每一个决策。
-
核心理念: 通过进行数据分析找到你要传递的核心信息,并将你的数据故事融入一个引人入胜的情节中。我在演讲中使用的框架是:首先识别问题,将问题与观众产生共鸣,提供解决方案以及支持该解决方案的事实和图表,最后以鼓舞人心的结尾并呼吁行动。
-
支持元素 使用可视化、度量标准和注释来支持你的数据故事。每一个用来支持故事情节的数据点都能强化核心信息,并在加强你信息传达的过程中起到重要作用。确保你使用的数据指标的准确性和相关性也非常重要。这些数据应与核心信息一致,并提供对你所讲故事的洞察。战略性地使用颜色突出关键数据点,并保持观众的关注。颜色使用中的一致性和对比度是关键要素。
结论
参加数据会议是跟上当前趋势和学习新概念的绝佳方式。尽管过去几年 AI 相关话题因其非常合理的原因主导了大多数会议议程,但我非常感激能够了解这三项关键的数据管理进展,它们帮助巩固了我的基础知识、数据沟通技能,并让我有机会与经验丰富的数据领域专家共同解决与 AI 一样普遍的问题。
注:非常感谢数据领域的领袖们 Kathy Koontz、Lindsey Cohen、Akia Obas、Lyndsey Pereira-Brereton 以及其他许多聪明的思想者们,与我一起进行这些发人深省的讨论,并激发了这篇文章的创作。
关于作者:
Nithhyaa Ramamoorthy 是一位数据领域专家,拥有超过 12 年的分析和大数据经验,特别是在医疗保健与消费者行为交集领域。她拥有信息科学硕士学位,最近还获得了 CSPO 证书,并且拥有多个其他专业认证。她热衷于利用自己的分析技能推动商业决策,创造出植根于同理心的包容性和公平的数字产品。
你需要知道的三个重要 Pandas 函数
掌握这些技巧,让你在 Python 开发者中脱颖而出
·发表于数据科学之路 ·阅读时间 7 分钟·2024 年 12 月 25 日
--

图片来源:Zach Graves 于Unsplash
如果你问数据科学家最常用的 Python 库是哪一个,答案无疑是 Pandas。Pandas 用于处理数据集,提供了分析、清理、探索和操作数据的功能。此外,Pandas 还可以用于运行描述性统计分析。使用 Python 进行项目的数据显示科学家从第一天起就会熟悉 Pandas。那么,今天我为什么要讨论 Pandas 呢?
事实上,有一些 Pandas 函数是许多用户容易忽视或没有完全探索的。因此,我将在今天的文章中讨论这些函数。
自定义函数 apply() 方法
apply()方法沿着 DataFrame 或 Series 的轴应用自定义函数。该方法对于需要使用用户定义的函数处理数据并使数据转换更具灵活性的复杂计算非常有用。例如,如果你想清理一个包含混乱的产品名称和价格的数据集,你需要将产品名称右对齐,使用单词“英寸”而不是符号,添加适当的空格,保持单词的正确大小写,并删除价格栏中的美元符号。你可以通过 apply 方法完成所有这些任务……
物理学中的三个令人震惊的想法:静止作用原理、洛伦兹变换和度量张量
数学创新如何产生越来越精确的物理世界模型
·发表于Towards Data Science ·阅读时间:25 分钟·2024 年 7 月 23 日
--

代数和几何作为建模工具之间的相互关系。使用自由许可。来源:unsplash.com/photos/a-black-and-white-photo-of-a-bunch-of-round-objects-Vy3W_YiXEio
虽然物理学激发了大众的好奇心,但许多人觉得数学令人望而却步。然而,物理学中的许多核心思想都来源于一些简单的原理,这些原理经过调整和修改,逐渐发展成更为复杂的形式,从而更好地映射物理现象。
虽然许多物理学毕业生最终从事数据科学工作,但物理学中的数学洞察能否为数据科学家提供启示并丰富他们的工作?我认为可以。尽管数据科学作为一个独立学科相对较新,但数据的收集与分析在物理学史上有着深远的影响,例如约翰内斯·开普勒收集的天文观测数据,基于这些数据他推导出了行星运动定律。物理学和数据科学都从数据中提取模式,尽管通常数据科学处理的是统计模式,而物理学则处理的是规律性或法则性模式。理解基本法则可以帮助数据科学家建模复杂系统,并开发现实世界现象的模拟。
在我自己的研究中,保持对物理学的浓厚兴趣帮助我在信息理论和统计力学之间建立了重要的联系。此外,它帮助我理解了数学的灵活性,特别是线性代数和微积分,在模拟受空间维度限制的物理系统以及包含社会和随机模式的更抽象多维系统中的应用。而且,理解物理学如何建模我们周围的世界,以及物理学的逐步进展如何要求塑造数学以适应和预测自然提供的数据,既可以激发灵感,也能带来智力上的满足。
在这篇文章中,我将穿越三个支撑物理学的数学思想:静力学原理(也称为最小作用原理)、洛伦兹变换,它描述了爱因斯坦狭义相对论中的时间和空间变换,以及度量张量,它是广义相对论(作为时空曲率的引力理论)数学的基础。
静力学原理或许是所有物理学中最重要的原理,因为它贯穿了经典力学和量子力学。它为牛顿发明的经典运动方程提供了一种等效的替代形式,用以描述物理系统的演变。具体来说,它通过确定最小化某个称为作用的量的路径,来描述物理系统随时间的运动。作用是一个泛函,即一个将函数作为输入的函数,它描述了系统在两个点之间路径变化时的静止状态。理解作用作为一个泛函,特别是作为对路径变化的评分,对于理解其背后的概念至关重要。下面的阐述将使这一点更加清晰。这个非凡的结果将运动表述为在给定约束下的一种优化函数。
洛伦兹变换描述了时间和空间的坐标如何交织成一个统一的度量,使得在惯性参考系中的观察者可以相对地改变它们的测量,同时保持光速不变。这一形式主义确保了光速在各个参考系中保持恒定,反对牛顿假设,即光速会根据不变的空间和时间单位发生变化。在狭义相对论理论之前,光速的恒定是一个实验观察现象,无法融入经典物理的框架。
最后,我们解释度量张量背后的数学思想,它描述了弯曲空间中的长度或距离。度量张量是一个双线性、对称的单位矩阵,它将底层平坦的欧几里得空间中的勾股定理推广到任何可能的空间,包括弯曲的曲面。爱因斯坦使用弯曲的曲面来描述在重力存在下时空的扭曲。作为数据科学家,你们可能对欧几里得距离和线性代数非常熟悉,因此理解度量张量背后的概念应该是一个自然而然的步骤。由伯恩哈德·黎曼(Bernhard Riemann)发展出的度量张量构成了非欧几里得几何的基础,并显著地将长度的概念推广到任何底层几何。
最小作用量原理
最小作用量原理或定常作用量原理是物理学的核心内容。它包含了运动方程,并在数学上阐述了物理系统随时间变化的过渡规则。
要开始理解这一原理,可以回顾牛顿的第二定律,它通过三个输入来计算一个粒子系统的轨迹:粒子的质量、作用于系统的力以及初始位置和速度,并通过 F=ma 来确定演化规则,其中 m 表示质量,a 表示加速度。与牛顿方法不同,最小作用量原理通过输入初始和最终位置、质量和速度(以及系统可能有的其他约束)来计算系统的轨迹,但省略了力。它随后选择最小化一种叫做作用的量的路径。在我们解释作用到底由什么组成之前,我们需要理解一个替代牛顿方程的表述方法,叫做拉格朗日量。
拉格朗日量 L 是动能 T 和势能 V 之差,其中 T 由质量和速度平方的乘积除以 2(2 表示初速度和末速度的平均值)给出,V 则由物体的质量 m、重力常数 g 和物体离地面高度 h 的乘积给出(势能的计算依据系统而异)。

为什么拉格朗日量是动能和势能之差呢?因为随着系统的运动,它将势能转化为动能,动能与势能之间的差值捕捉了这两种能量形式之间的动态相互作用。需要注意的是,总能量是这两者之和。
拉格朗日量的输入是位置 x 和速度 v,用⨰(x 点)表示,其中点表示一阶导数。这是因为速度是位置的一阶导数。
为了计算拉格朗日量,我们至少需要知道粒子的速度、广义坐标、位置和质量。势能依赖于粒子的位置(或一组粒子的位置),因为它描述了粒子能够做的潜在功,而动能则依赖于粒子的速度,因为它描述了粒子的运动。
作用如何参与其中呢?假设你有平面上的两个点,需要找到最短的距离。两个点之间有许多路径,但只有一条路径或线代表最短的距离。作用类似于这个问题。为了找到系统的轨迹,我们需要选择一条最小化作用的路径。由此推论,作用在系统演化过程中保持静止。
由于作用必须是 stationary 的,作用的一级偏导数因此必须为零:

从高层次来看,作用是通过拉格朗日量在给定时间区间[t₀, t₁]上的路径积分来描述的。尽管从 t₀到 t₁的函数积分通常被理解为曲线下的面积,但拉格朗日量的路径积分不应直观地看作是面积,而应当看作是一个泛函的积分,泛函是一个接受另一个函数(或多个函数)作为输入并输出一个标量的函数。输入将是拉格朗日量,输出则定义了作用。在系统在 t₀和 t₁之间可能采取的多个路径中,我们会看到它恰好选择了最小化作用的路径。
这是作为拉格朗日量路径积分的作用的简单公式:

现在,由于定积分可以作为Riemann 和积分的Riemann 和来计算,其中包含f(x)的 y 值和变化量 x,表示为Δx,当 k 的区域划分趋近于无穷大时,我们可以将作用计算为拉格朗日量与时间导数dt的乘积的 Riemann 和。换句话说,拉格朗日量的定积分可以通过在时间区间内最小化作用来计算。
作用由拉格朗日量在系统的初始位置和终止位置之间的路径积分组成。这意味着路径积分通过计算势能和动能之间的差异来最小化作用。微积分的基本定理使我们能够在 t₀和 t₁之间的连续区间计算作用,尽管它也可以在离散的时间步骤N中进行计算。现在,如果我们将作用想象为离散时间步骤N的总和,我们将其计算为拉格朗日量在每个时间步骤下的值与时间 t 值的乘积之和。
拉格朗日量通常依赖于位置和速度,但也可以依赖于时间。如果拉格朗日量随时间变化,即使位置和速度保持不变,我们也说拉格朗日量是时间依赖的。否则,拉格朗日量通过位置和速度的变化隐式依赖于时间。对于时间独立的形式,我们将 L(x,⨰ )代入方程中,表示它依赖于位置和速度:

现在,我们从动量守恒定律知道,系统所有动量的总和的导数等于零。换句话说,在一个孤立系统中,总动量始终是守恒的或保持不变的。常数的导数为零,因为变化率保持其他条件不变或相等。在牛顿力学中,运动的第三定律指出,每一个作用力都有一个大小相等方向相反的反作用力,这表达了总动量守恒。
类似地,能量守恒定律认为,一个孤立系统的总能量在任何转化过程中都是守恒的:总能量的时间导数为零。然而,与动量不同,能量有不同的形式。正是这些形式的总和才是守恒的。如果从运动的角度来表述,这些能量形式就是我们一直在谈论的:动能和势能。
由于拉格朗日量被定义为这两种能量形式的差,当拉格朗日量在时间平移下不变时,意味着能量守恒。
与能量守恒类似,作用量也有类似的守恒现象。在签名轨迹中,自然选择了最小化作用量的路径。这种最小化类似于优化问题中函数的最小化,只是作用量代表了包括每个时刻的所有坐标在内的多种变量。这种极值性质通过欧拉-拉格朗日方程来表达,该方程构成了运动方程。
欧拉-拉格朗日方程是什么?它们是告诉系统如何从一个时刻移动到下一个时刻的微分方程。现在,我不会在这里推导这些方程,但直观上我们会将作用量𝝏A关于位置dx的导数设为零。换句话说,我们考虑路径的一个小变动,并要求作用量的偏导数为零。

这导出了欧拉-拉格朗日方程的两项:拉格朗日量关于速度的偏导数的时间导数,以及拉格朗日量关于位置的偏导数。这两者分别表示动能(动量的变化)和势能的变化。将这两者之差设为零,得到作用量最小化的欧拉-拉格朗日方程。
单一坐标或自由度下的欧拉-拉格朗日方程如下所示,其中 L 表示拉格朗日量,ẋ 代表速度,x 代表位置。

用自然语言来表达,就是拉格朗日量关于速度的偏导数(𝝏L/𝝏ẋ)对时间的导数(d/dt)减去拉格朗日量关于位置的偏导数(𝝏L/𝝏x)等于零。直观地讲,这可以重新表述为,拉格朗日量关于速度的瞬时变化率对时间的瞬时变化率减去拉格朗日量关于位置的瞬时变化率为零,即处于稳态。
进一步提炼,欧拉-拉格朗日方程意味着物理系统的运动对应于拉格朗日量积分的极值,这个积分即是作用量。
该方程可以推广到任意坐标(x, y, z…n):

在具体情境中,作用量是一个泛函,也就是说,它是一个函数的函数,涉及从一个函数输入(拉格朗日量)到一个标量输出(作用量值)的映射。
尽管稳态作用原理能够有效计算物理系统的轨迹,但它需要知道起始位置和结束位置。为了弥补这一全局视角的不足,我们采用牛顿形式的公式,它要求知道粒子的初始位置和初速度。
稳态作用原理可以适应量子物理学,但需要重要的附带条件,在这种情况下,考虑从初始状态到最终状态之间的所有可能路径,作用量是每条路径的概率振幅的总和,用于计算系统的概率演化。
根据这个公式,经典的稳态作用原理可以被看作是量子公式的一个特例,在这种情况下,所有路径中稳态作用路径占主导地位。
洛伦兹变换
理解洛伦兹变换是进入爱因斯坦相对论的特殊理论的门户。洛伦兹变换构成了计算惯性参考系或均匀参考系中相对论性时空变换的数学框架,也就是说,这些参考系排除了引力影响。
狭义相对论的核心概念是,运动只能相对于某个参考系来描述,而不能以绝对的方式描述。例如,如果我在开车,我相对于汽车是静止的,但相对于我的房子是运动的。
相对论运动的概念存在于经典力学中,最早由伽利略描述。
狭义相对论中的突破性见解不是相对论运动,而是跨空间平移保持不变或恒定的东西。在经典力学中,所有运动都是相对的,而空间和时间的坐标仅在加法的方式下变化,并且对所有观察者来说始终静止且彼此独立。
经典力学中的相对运动假设意味着光的运动应遵循相对论规律。换句话说,如果我静止不动并拿着手电筒,而你在开车并拿着手电筒,那么你手电筒中的光的运动应该被测量为光速与你的速度之和。
然而,实验证据与这一假设相矛盾。实际上,无论参考系如何,光速始终保持不变。换句话说,实证证据证明了光速是绝对的。
爱因斯坦没有指出观察中的错误,而是提出光速的恒定性是自然法则。如果光速总是相同的,那么必须改变的是空间和时间坐标的表示方式。
为了理解爱因斯坦的狭义相对论如何实现这一点,重要的是要对经典力学中描述的简化运动方程有一个粗略的了解。这些方程将被修改,以便观察者之间的相对运动不会改变光速,而是改变空间和时间的交织度量。这有一个特殊的后果,即当速度接近光速限制时,时间和距离的度量会因观察者而异。
运动方程通常简化为首字母缩略词 SUVAT(s = 距离,u = 初速度,v = 速度,a = 加速度,t = 时间):

闵可夫斯基度量
为了使洛伦兹变换易于理解,我们将使用时空图。这些图将距离和时间的轴反转,使时间表示为 x 轴,距离表示为 y 轴。此外,我们使用 y 轴来表示较大的距离间隔,因为我们希望解释相对于光速的运动。现在,光速为 3 *10⁸ m/s²。在我们的时空图中,一秒钟将正好对应于这个距离。这意味着,位于我们轴之间 45°角的图中的直线对角线,代表光速随时间的恒定性。事实上,笛卡尔网格中的对角线将代表光速的渐近极限,这将约束我们在 y 轴上的时间平移和在 x 轴上的空间平移。

创意共享许可使用,来源于:en.wikipedia.org/wiki/Spacetime_diagram#/media/File:MinkBoost2.gif
现在,任何与我们笛卡尔网格对角的直线,只要不是与 45°角成直角,就代表以亚光速进行的均匀运动。在牛顿图像中,光速就像任何其他速度一样。这意味着,大于 45°的钝角将代表超光速。此外,光速将相对于参考系而变化。如果我以光速的一半与光以相同的方向旅行,从我的参考系来看,我会观察到光速为光速的一半,因为我正以一半的速度追赶它。这个模型的假设基于保持不变的时间和距离单位,以确保所有参考系中的时间和空间间隔保持恒定。
从将空间和时间视为独立度量,到将它们整合为一个被称为时空的连续体,涉及将时间变量转化为距离的度量。我们通过用 c(光速常数)加权时间变量来实现这一点。当我们将c与t相乘时,我们得到ct,它度量的是1 光米/c²。
在牛顿-伽利略图像中,两个参考系 S 和 S'分别由坐标(x, t)和(x', t')给出,其中撇号符号,读作x prime和t prime,用来区分两个相对的参考系(并不表示通常意义上的微分)。这些参考系是可逆的,并且在伽利略相对论中,它们的逆是等价的。从参考系 S 的框架来看,S'的坐标,即位置和时间分别由 x' = (x-vt) 和 t' = (t-vx/c²)给出。同样地,从参考系 S'的框架来看,S 的坐标由 x = (x' + vt') 和 t = (t + vx/c²)给出。然而,这些转换最终使得光是相对的,而不是时空的。问题出现了,如何从 S → S'进行转换,以便我们能够保持 c(光速)不变,同时按比例缩放时间和距离变量(更准确地说,是时空连续体)?
推导这些变换的一种方法是利用我们上面引入的时空图,其中我们通过常数 c ≈ 299 x 10⁸来缩放时间。我们要寻找的转换表示为以下公式:

实际上,我们将利用参考系之间的这种对称性或等价性来推导 gamma 因子,作为时空在相对参考系之间翻译的共同缩放因子,使它们反映光速不变性。下面的图表展示了我们引入的两个参考系作为彼此的逆,表达了相对运动的伽利略对称性:

参考系之间的可逆对称性。图片由作者提供。
由于光速在所有参考系中是常数,如果我们从原点出发为两个参考系(x = 0 和 t = 0),光的路径将满足以下方程(回想一下,45°的对角线表示光速,其中一个时间单位对应一个距离单位的传播距离):

从 x 到 x'的转换由以下方程给出,其中 x'只是 x 与速度和时间的乘积之间的差。现在,为了推导洛伦兹变换,我们需要某个因子𝛄来缩放我们的时空变换。𝛃因子等于v/c —— 速度与光速的比率 —— 用于缩放ct ——光速缩放时间。如果我们展开这个表达式,我们会发现它在代数上简化为括号中的牛顿变换。正如我们将看到的,当洛伦兹因子𝛄接近 1 时,洛伦兹变换变得与牛顿变换等效,这与我们日常对事件同时性的概念相对应。以下公式展示了我们如何从初始公式得到相对位置的 gamma 缩放变换公式:

类似地,我们可以通过下面的方程推导从 t 参考系到 t' 的时间变换。由于我们使用的是时空图,我们从 ct' 开始。我们可以看到,ct' 可以通过 ct 与 beta 𝛃 乘以 x 之差来计算,并且整个表达式通过洛伦兹因子 𝛄 缩放。我们可以通过代数运算扩展表达式,最终将 t' 的解简化为 t - vx/c²,乘以 𝛄:

当速度非常小时,vx/c² 会趋近于 0,𝛄 会趋近于 1,从而得到 t' = t。这个结果对应于我们日常的牛顿经验,其中在我静止时的 1 秒与你以恒定速度相对我运动时的秒数大致相等。
正如你可能已经注意到的,x' 的变换涉及到 ct 作为一个项,而 t' 的变换则涉及到 x 作为一个项。通过在彼此的参考系变换中考虑这些项,时间和空间就交织成一个相互依赖的连续体,其中一个变量的单位变化对应着另一个变量的单位变化。这种相互关系将解释洛伦兹变换所描述的时间膨胀和空间收缩的比例性。
我们如何确定洛伦兹因子的值?一种方法是将我们的平移方程相乘,并解出共同因子。记住,由于我们之前引入的相等式,我们可以将 x 和 x' 分别替换为 ct 和 ct'。这样我们就可以约去相同的项,并解出 𝛄:

现在我们可以通过以下 𝛄 替代来表达 x' 参考系:

并且可以通过以下替代来表达 t' 参考系:

在每个方程中,当速度 v 接近光速时,v²/c² 会接近 1,分母的值会接近 √0。我们从 E=mc² 知道,具有静质量的物体无法根据物理原理被加速到接近光速。因此,分母的值不可能为 0。0 限制代表无限快速度(表示变换的角度)。当快速度接近无限时,时间趋近于静止,长度的测量趋近于零。
另一方面,当速度较小时,v²/c² 是一个非常小的数,分母的值接近 1。当分母(称为洛伦兹因子)等于 1 或接近 1 时,洛伦兹因子变得不再重要,方程式就近似于牛顿运动。也就是说,运动方程由分子给出,简化为牛顿的运动方程。
洛伦兹因子是理解洛伦兹变换的关键。如果你回想一下伽利略相对论,惯性参考系的互换是通过旋转实现的。旋转由三角函数描述。三角函数保持欧几里得距离。具体来说,旋转保持半径不变。这意味着长度单位在变换中保持不变。
类比地,洛伦兹变换保持时空度量。与欧几里得度量不同,时空度量使所有时空变换相对于光速作为绝对值。因此,光速形成了洛伦兹变换可以接近但无法相等的渐近线。在时空图中,光速由等式x = ct和x’ = ct’表示。如果你回想一下我们的时空图,渐近线由对角线切穿两个轴组成。由于时空变换的范围都是无限的(意味着它们输出从˗ ∞到+ ∞的范围),但又渐近于我们的对角线,它们由双曲函数或旋转描述。双曲旋转是类似于三角函数但使用双曲线而不是圆的函数。与有限的圆不同,双曲旋转可以延伸到无限范围。它们对三角函数的等价物可以描述为对特殊数e(2.718)的指数运算,其中 sin(x)的类比由sinh(x)表示,cos(x)的类比由cosh(x)表示,分别由以下函数描述:

就像在单位圆中(sin x,cos x)描述其点一样,(cosh x,sinh x)形成单位双曲线的右半部分。在特殊相对论背景下,双曲旋转的角度称为快度,用符号eta 𝜂表示。以下是我们之前推导的洛伦兹变换的双曲旋转等价物:

洛伦兹因子𝛄与双曲旋转的快度之间的关系如下:

如果伽利略旋转保持半径或欧几里得距离,那么洛伦兹变换保持什么?它们保持闵可夫斯基度规,由以下等式给出,类似于欧几里得距离:

由于实际的洛伦兹变换发生在四维空间中,1 个时间维和 4 个空间维或类比为 4 个时空维度,四维闵可夫斯基间隔由以下方程给出:

下图 gif 可视化了这些双曲变换,作为在二维中表现出时空扭曲,随着速度接近光速,逐渐接近对角线渐近线。网格上的扭曲表示由于观察者之间的相对速度而导致的时空度量的扭曲。随着速度接近光速极限,空间(水平轴双曲线)收缩,时间(垂直轴双曲线)膨胀。这些交织的变换保持了闵可夫斯基度量s²,该度量按比例缩放这些变换,以保持光速的不变性。

双曲洛伦兹变换。图片使用了创作共用许可证。来源:en.wikipedia.org/wiki/File:Animated_Lorentz_Transformation.gif
空间收缩和时间膨胀可以在静止观察者和以均匀或惯性速度运动的观察者之间反转。如果你以接近光速的速度均匀运动,相对于某个静止的人,描述你为静止并且描述另一个人为接近光速运动是同样正确的。
度量张量:曲面几何学
狭义相对论中的洛伦兹变换发生在平坦空间伪欧几里得空间中。什么是平坦空间?它是一个几何空间,其中度量,或点之间的距离度量,是恒定的。平坦空间中最著名的度量是由毕达哥拉斯定理定义的。另一个平坦度量包括我们之前讨论的闵可夫斯基时空度量。
欧几里得度量定义了两个点之间的距离为直角三角形最短边的平方和的平方根。这遵循毕达哥拉斯定理:a² + b² = c²。
从几何角度描述,两个点之间的欧几里得距离由每个坐标(x,y)之间差值的平方和的平方根给出。

作为向量的大小的欧几里得距离。
毕达哥拉斯定理可以推广到n维空间:

n 维空间中的欧几里得距离。
因此,我们可以通过以下公式表达三维空间中的欧几里得距离:

三维空间中的欧几里得距离。
然而,这种泛化保持了作为欧几里得平坦空间的属性的距离不变。换句话说,度量保持不变。
为了理解度量张量,我们需要学会将毕达哥拉斯定理视为平坦或欧几里得空间的特例。
换句话说,我们需要定义一个值中立的空间,以便可以将由毕达哥拉斯定理定义的欧几里得距离作为一种特殊情况导出。
在我们做这件事之前,我们必须问,为什么坐标之间的差异在毕达哥拉斯定理中要被平方?这个问题可以用多种方式来解释,但直观的解释是几何的。它们之所以被平方,是因为它产生了等长的几何面积,考虑到面积是长度和宽度的乘积,这让我们能够计算斜边长度,它是直角边平方和的平方根。这个答案由克罗内克 delta 定义的度量张量给出,当 i=j 时输出 1,当 i≠j 时输出 0。
然而,我们也可以通过空间的广义度量来演示这个结果,其中度量张量由在切空间上的平滑变化的内积组成。
什么是切空间? 切空间是流形上某一点的所有切向量的集合。
方程的一般形式如下,其中 g 表示度量张量,μv 表示每个坐标项的度量张量值的索引,dX 表示每个坐标的无穷小位移:

给定上述方程,我们可以将两点之间的平方距离在二维空间中表示为以下和:

由度量张量推广的两点之间的平方距离。
在上述公式中,g 系数旁边的零和一以及 x 变量表示索引。具体来说,它们表示 0 和 1 的置换矩阵,即:01、00、11、10。
dx⁰ 和 dx¹ 系数表示两个不同坐标的无穷小位移,其中 0 和 1 依然是索引。每个坐标的位移与对应的g,即度量张量的值相乘。
因此,在上述公式中,g 表示每个索引的度量张量系数。为什么上述公式中有四个项?因为两个点由四个坐标或标量值描述。在欧几里得几何中,隐含的基向量是切向量 (0,1) 和 (1,0)。这些切向量跨越了整个欧几里得空间。现在,g 定义了任何点上切向量之间的内积。而 g 的值是通过所有基向量可能组合的内积获得的。
当系数的值表示两个点之间的正交归一关系时,g 的值将简化为单位矩阵:

当三角形的角度是直角时,度量张量的系数定义了平坦的欧几里得空间。

你可以将度量张量系数看作是与置换矩阵的索引对应的。
在二维或两个坐标的系统中,我们可以将欧几里得距离表示为度量张量与每个坐标之间距离的平方向量的乘积。因为在平坦的欧几里得空间中,直角的度量张量是单位矩阵,所以两个点之间的平方距离简化为下面显示的勾股定理:

上述公式也可以表示为我们第一次公式中的线性加权组合:

欧几里得距离作为带有 g 值的线性加权和。
如上所示,当 g=0 时,我们去掉后两个项,将方程简化为欧几里得距离。因此,我们已经解释了度量张量的广义形式如何将欧几里得距离作为一个特殊或极限情况。
那么当最短距离不能通过欧几里得距离来表示时该怎么办呢?在我们的日常直觉中,我们假设存在直角,使得对边和邻边的长度能够满足勾股定理作为斜边的距离度量。在线性代数中,这等同于假设正交标准基为空间的度量。基定义为一组线性无关的向量,这些向量张成该向量空间。正交标准基是垂直的单位向量,或者是内积为零的单位向量。
但是,这种a priori 假设在经验上可能没有依据。事实上,底层的几何可能在不同的方式下是弯曲或倾斜的。如果是这样,我们该如何表达两点之间的最短距离呢?为了定义非欧几里得空间,我们为我们的度量选择不同的基向量。那些基向量的置换空间的内积将输出定义该度量中距离和角度的度量张量,通过对两个点的任何微小位移的线性组合,给出如下公式:

广义黎曼距离。
现在,让我们来看一个极坐标(r, 𝜃)的例子,其中 r 表示半径,𝜃 表示角度。g 度量张量通过(r, 𝜃)的置换空间的内积获得,如下所示:

极坐标下的度量张量。
如果我们考虑欧几里得极坐标,度量张量将变为如下矩阵:

极坐标下度量张量的特定实例化。
这是因为距离是通过以下方式计算的:

现在,两个点(r¹𝜃¹)和(r²𝜃²)之间的距离通过计算 r²-r¹ 和 𝜃²-𝜃¹ 之间的距离,并将其代入以下公式:

到目前为止,我们所有的例子都在二维空间中。当然,我们可以将相同的概念扩展到三维或 N 维空间。三维空间的度量张量将是一个 3x3 矩阵,以此类推。
理解度量张量是理解广义相对论和爱因斯坦场方程的一个重要步骤。
在广义相对论中,爱因斯坦的场方程利用度量张量来描述时空的弯曲几何。
具体来说,爱因斯坦的场方程使用了三个张量:1)爱因斯坦张量 G,描述时空的曲率,来自度量张量的导数;2)能量-应力张量 T,描述宇宙中物质和能量的分布;3)度量张量 g,定义了弯曲几何中的长度和角度的度量。爱因斯坦的场方程通常通过以下方程总结:

在广义相对论中,度量张量由一个 4x4 矩阵组成,包含 16 个分量。就像我们在二维例子中一样,度量张量由所有维度的排列空间组成,在这种情况下,3 个空间维度和 1 个时间维度组合成 4 个时空维度。然而,由于矩阵必须是对称的,因此其中只有 10 个分量是彼此独立的。
度量张量的通用形式如下所示:

度量张量的值随着时空的弯曲而变化,因为它们编码了质量-能量分布。因此,与欧几里得距离在所有变换中保持长度不同,弯曲几何并非如此。这就是为什么度量张量是理解广义相对论的一个关键方面。
现在你已经浏览过这些概念,可能对物理中的复杂思想和数学形式主义不再感到畏惧!
资料来源
苏斯金德,莱昂纳德,和乔治·赫拉博夫斯基。理论最低要求:开始做物理所需了解的知识。基本书籍,2014 年。
苏斯金德,莱昂纳德,和阿特·弗里德曼。狭义相对论与经典场论:理论最低要求。企鹅出版社,2018 年。
苏斯金德,莱昂纳德,和阿特·弗里德曼。量子力学:理论最低要求。企鹅出版社,2015 年。
苏斯金德,莱昂纳德,和安德烈·弗朗索瓦·卡班内斯。广义相对论:理论最低要求。基本书籍,2023 年。
沃尔夫勒姆演示项目。理解狭义相对论:洛伦兹变换、时间膨胀和长度收缩。(无日期)。demonstrations.wolfram.com/UnderstandingSpecialRelativityTheLorentzTransformationTimeDi/
为什么开发人员应该使用 DuckDB 的三大理由
软件开发人员如何使用 DuckDB 进行数据分析
·发表于 Towards Data Science ·阅读时间 5 分钟·2024 年 7 月 14 日
--
软件开发人员需要承担多重职责:从编写代码、设计系统到在事件发生时分析数据。我们的许多工具都是针对这些任务进行优化的——编写代码时我们有强大的集成开发环境(IDE),设计系统时我们有功能丰富的图表工具。
对于数据分析,软件开发人员是否拥有最合适的工具? 在本文中,我列出了三大理由,解释为什么开源分析数据库DuckDB是软件开发人员必备的工具。

来源:Unsplash
理由#1:使用通用的 SQL
假设你在一家食品配送公司担任软件开发人员。你收到一封邮件,内容是关于支付相关的客户投诉急剧增加。邮件附带了像这样的 CSV 文件,其中按投诉性质对一些订单进行了分类。作为一个在压力下工作的开发人员,你可能会急于快速查找如何在 StackOverflow 上分析 CSV 文件,找到的解决方案是使用awk。
awk -F',' \
'NR > 1 {count[$6]++} END \
{for (value in count) print value, count[value]}' \
datagenerator/adjusted_transactions.csv | sort
CUSTOMER_SUPPORT_REFUND 8494
INSUFFICIENT_FUNDS 1232
MANUAL_ADJUSTMENT 162
REVERSED_PAYMENT 62815
自然会有人问:我们每个订单中出现这些错误的频率是多少?使用awk这样的工具回答迭代性问题可能会很有挑战性,因为它的语法不熟悉。此外,如果数据是其他格式,如 JSON,我们还需要使用完全不同语法和使用模式的工具,如jq。
DuckDB 通过提供一个统一的 SQL 接口来解决需要特定工具处理特定数据格式的问题,支持各种文件类型。 开发人员非常频繁地使用 SQL,它是查询全球最广泛部署的数据库的语言。由于 SQL 的普遍存在,非关系型数据系统也开始添加支持,允许使用 SQL 访问数据,如 MongoDB、Spark、Elasticsearch 和 AWS Athena。
回到原始的 CSV 文件,使用 duckdb 和 SQL,我们可以非常简单地查找每个订单中报告错误的频率:
duckdb -c "
with per_order_counts AS (
select
order_id, reason,
count(transaction_id) as num_reports
from 'datagenerator/adjusted_transactions.csv'
group by 1,2
)
select reason, avg(num_reports) AS avg_per_order_count
from per_order_counts group by 1 order by reason;"
┌─────────────────────────┬─────────────────────┐
│ reason │ avg_per_order_count │
│ varchar │ double │
├─────────────────────────┼─────────────────────┤
│ CUSTOMER_SUPPORT_REFUND │ 10.333333333333334 │
│ INSUFFICIENT_FUNDS │ 2.871794871794872 │
│ MANUAL_ADJUSTMENT │ 1.2 │
│ REVERSED_PAYMENT │ 50.57568438003221 │
└─────────────────────────┴─────────────────────┘
原因 #2:支持多种数据库和文件类型
假设我们虚构的食品配送应用是使用微服务构建的。假设有一个 users 微服务,它在 PostgreSQL 中存储用户信息,还有一个 orders 微服务,它在 MySQL 中存储订单信息。
以下跨微服务问题非常难以回答:VIP 用户与非 VIP 用户相比,是否受到更多影响?
典型的解决方案是使用数据管道将所有微服务中的数据汇总到一个数据仓库中,但这既昂贵又不容易实时更新。
使用 DuckDB,我们可以连接一个 MySQL 数据库和一个 PostgreSQL 数据库,跨数据库连接数据并过滤 CSV 文件。数据库设置和代码可以在这个仓库中找到:
ATTACH 'host=localhost port=5432 dbname=flock user=swan password=mallard'
AS pg_db (TYPE postgres_scanner, READ_ONLY);
ATTACH 'host=localhost port=3306 database=flock user=swan password=mallard'
AS mysql_db (TYPE mysql_scanner, READ_ONLY);
select u.tier,
count(distinct o.id) as order_count
from pg_db.users u join mysql_db.orders o
on u.id = o.created_by
where o.id IN (
select order_id
from 'datagenerator/adjusted_transactions.csv'
)
group by 1 ;
┌─────────┬─────────────┐
│ tier │ order_count │
│ varchar │ int64 │
├─────────┼─────────────┤
│ plus │ 276 │
│ normal │ 696 │
│ club │ 150 │
│ vip │ 148 │
└─────────┴─────────────┘
在上面的代码片段中,我们查询了 PostgreSQL、MySQL 和 CSV 文件,但DuckDB 支持许多其他数据源,如 Microsoft Excel、JSON 和 S3 文件——所有这些都使用相同的 SQL 接口。
原因 #3:可移植性和可扩展性
DuckDB 在命令行外壳中作为独立进程运行,无需任何额外依赖(例如服务器进程)。这种可移植性使得 DuckDB 可与其他 Unix 工具如 sed、jq、sort 和 awk 相媲美。
DuckDB 还可以作为库导入到程序中,这些程序使用 Python 和 Javascript 等语言编写。实际上,DuckDB 也可以在浏览器中运行——在这个链接中,一个 SQL 查询从 Github 获取 Rich Hickey 的仓库,并按编程语言分组——这一切都可以在浏览器中完成:

截图展示了 DuckDB 在浏览器中运行的情况。图片由作者提供。
对于 DuckDB 中未包含的功能,可以使用社区扩展来添加更多功能,比如作为社区扩展添加的加密哈希函数。
结论
数据分析是一个反复提问的过程,目的是找出为什么某些事情会发生的解释。 引用卡尔·荣格的话:“提出正确的问题已经是解决问题的一半。”
使用传统的命令行工具时,数据和问题之间需要额外的步骤来弄清楚如何回答这个问题。这会打断迭代提问的过程。

根据 Unix 哲学,简单的工具“与其他程序结合后,成为通用且有用的工具”(来源)。由于每个工具都有自己的使用模式,将这些工具组合起来会通过引入额外的步骤来弄清楚如何回答问题,从而打破了迭代提问的方法。图片来自作者。
DuckDB 统一了工具的泛滥:(1)它能在任何地方运行,(2)能够查询多个数据源,(3)使用一种广泛理解的声明式语言。使用 DuckDB,迭代分析的反馈周期大大缩短,使得DuckDB 成为所有开发者在分析数据时应当放在工具箱中的唯一工具。

DuckDB 统一了数据工具的泛滥,并为不同的数据源提供了统一的 SQL 接口,促进了数据分析的迭代方法。图片来自作者。
识别您企业成功的三步“银弹”
了解如何通过识别早期客户行为,预测长期客户保持并提高企业收入
·发表于 Towards Data Science ·6 分钟阅读·2024 年 10 月 2 日
--

图片来源:Bibin Xavier 来自 Pixabay
Facebook 发现,那些在前 10 天内添加了 7 个好友的用户,更有可能长期保持活跃。Starbucks 观察到,那些在第一个月将钱存入应用程序的顾客,转变为忠实的重复购买者。Airbnb 发现,那些在第一周就成功预订了的房东,继续使用该平台。这些公司都识别出了一个“银弹”,即能够预测长期成功的行为。
你的企业如何找到自己的“银弹”?这里有一个三步框架,帮助你识别长期客户参与的早期信号。
第一步:预测什么?
长期成功通常意味着客户忠诚度、较高的参与度或对企业来说是高价值的客户。这些是你希望预测的可衡量因素或行为,这些信号可以在客户体验的第一周或第一个月内识别,并提供可操作的早期洞察。
对于长期客户保持,目标是预测新客户是否会保持活跃……
计算两个字符串相似性的三种方法
textPython,文本分析
使用 jellyfish 库的 Python 教程来计算两个字符串之间的相似性
·发表于Towards Data Science ·阅读时间:7 分钟·2024 年 2 月 15 日
--

最近,我一直在做一个姓氏匹配的项目。我有一个包含姓氏的数据集,其中一些可能包含拼写错误。我的目标是识别哪些姓氏可能是错误的,然后进行修正。为此,我考虑了计算所有姓氏之间的相似性,然后将那些相似性超过某一阈值的姓氏组合起来。但是,我可以使用哪些标准来计算相似性呢?
在本文中,我介绍了三种计算两个字符串(因此也可以计算两个姓氏)之间相似性的方法:
-
莱文斯坦距离
-
贾罗相似度
-
美国 Soundex 编码
作为案例研究,我们将使用一个包含 30 个虚构姓氏的数据集,该数据集由 ChatGPT 使用以下提示生成:生成一个包含 30 个姓氏的列表,其中 10 个姓氏包含拼写错误。在这个链接中,你可以找到用于生成该数据集的完整聊天记录。
我们将使用 jellyfish Python 库实现一个姓氏匹配示例,你…
通过那面神秘的镜子:LLMs 是否像人类大脑一样记忆?
|LLM|人工智能|人类大脑|记忆|认知|
探索人工智能与人类记忆之间的神秘相似与深刻差异
·发表于Towards Data Science ·阅读时间 10 分钟·2024 年 9 月 19 日
--

由作者使用 DALL-E 创作的图像
我的语言的界限就是我的思想的界限。— 路德维希·维特根斯坦
记忆的真正艺术是专注的艺术。— 塞缪尔·约翰逊
语言是人类最重要的能力之一;它使我们能够沟通和传递知识,并被视为文明的支柱。这就是为什么大型语言模型(LLMs)所展现的惊人能力让世界震惊,并使人们提出了一个问题:它们是否具有智能?
所有这些都是通过大量的文本和一个简单的学习功能实现的:预测序列中的下一个词。支撑这一成功的模型是Transformer,如今现代衍生的 LLM 正在被大量用户用于翻译、摘要、问答或生成文章等任务。
完美二叉树遍历的时间复杂度分析
推导特定树遍历算法的最紧致渐近界
·发表于Towards Data Science ·29 分钟阅读·2024 年 10 月 3 日
--

图片来源:作者
介绍
数据结构和算法是任何软件开发中的关键元素,特别是由于它们对最终产品质量的影响。一方面,通过广泛已知和研究的算法来描述一个过程,有可能提高代码库的可维护性,因为互联网上有大量的开源资源和文档可供使用。但是,当决定使用哪种数据结构来表示信息或选择哪个理想算法来执行给定任务时,主要考虑的因素是效率,包括内存和时间的效率。简而言之,选择一个好的程序或结构通常会带来市场上其他产品所无法比拟的优势,因为它们在响应时间、执行任务所需的计算资源或可扩展性上无法满足要求。
因此,为了在这方面做出适当的决策,有必要准确测量算法的成本,以便随后进行研究,在其中可以建立足够有效的比较,以淘汰那些不符合产品要求的实现或数据结构,这些要求通常涉及时间和空间的限制。因此,这两个量通过它们的渐近增长来量化,当输入数据的大小趋于无穷大时。这就是说,衡量一个算法效率的标准取决于当输入大小增加时,其执行所需的时间或空间资源的增长情况,如果它们随着相同的输入大小变化而增加得更多,则认为其性能更差,因为它们将需要比必要的更多资源。另一方面,选择一个适合的可以应用高效算法的数据结构,主要取决于需要建模的信息的复杂性,尽管最终目标是使通过[操作](https://dev.to/m__mdy__m/data-structures-a-comprehensive-introduction-2o13#:~:text=Common operations include insertion (adding,structure for a given task.) (add(), remove(), search(element)…) 定义的算法具有最小的渐近增长。
目标
在本工作中,旨在展示描述算法时间复杂度的方法并允许与其他替代方案进行比较,我们将首先从一个数据结构开始,即一个完美平衡的二叉树,以及在其上执行的算法。随后,我们将形式化其输入的格式,构建可视化图表以理解其组成以及过程跟踪,最后通过正式的开发达到最终界限,并尽可能简化和详细说明。
算法定义
我们即将处理的算法涉及一个具有完美平衡限制的二叉树。根据定义,二叉树是由一组节点和另一组表示节点之间连接的边组成的对,因此该组中的每个节点都与恰好两个被称为子节点的节点相连。而且,在所有可以从此定义中形成的树中,我们这里只关心那些被称为完美的树。
一个完美二叉树的特点是其叶子节点的深度相等,即最后一层的节点,以及其余节点的子节点数恒定为二。树中一个节点的深度,反过来,是从根节点到该节点的路径长度。因此,数据结构的外观将类似于:

图片来源:作者提供。
在这个特殊的情况下,我们正在处理一个深度为 2 的完美二叉树,因为最后一层的节点满足具有相同深度的条件,即等于该值。此外,在图中,节点被以某种方式编号,这在本上下文中会有所帮助。具体来说,每个节点对应的整数来自于从根节点开始执行的广度优先遍历,这等同于给定树层次结构的层次遍历。在这种情况下,如果遍历的节点从 0 开始编号,则会形成如上所示的标号,其中每一层包含所有分配给介于2l-1**和**2(l+1)-2之间的整数的节点,l表示层级。要看到这个表达式的来源,只需找到一个函数,它以层级l为输入,返回该层级区间中的最小整数,以及另一个计算相反值的函数,即最大值。
首先,设m(l)为返回最小值的函数。因此,通过观察随着输入增加该序列应遵循的模式,我们注意到其模式为{0,1,3,7,15,31,63…}。而且,当我们在 OEIS 中搜索它的初始值时,找到了与序列A000225的匹配。根据该序列的定义,其值由表达式2^l — 1给出,但是,要理解为什么这个序列能描述m(l)的进展,有必要建立两个相邻的评估m(l)和m(l+1)之间的关系,这将导致这样的公式。然后,如果我们考虑一个层级l的最小值和下一个层级的最小值,我们可以从假设开始,即这两者之间的差值始终等于具有最少节点的层级中的节点数。例如,在第 1 层中只有两个节点,它们的标签为{1,2}。该层的最小值为 1,下一层的最小值为 3,因此可以容易地验证3=1+2,也就是说,第{1,2}层的最小值加上该层中的节点数。通过这个假设,再加上一个层级的节点数在二叉树中恰好为2^l个节点,我们得出了m(l)的以下公式:

图片来源:作者提供。
总结来说,m(l)表示前一层m(l-1)的最小整数加上其中的节点数2^(l-1),再加上基本情况m(0)=0。

图片来源:作者提供。
所以,当通过评估递归项扩展m(l)时,会出现一个模式,这样这个函数就可以被表征。简而言之,从m(l-l)到l-1的每一个 2 的幂都被汇总。最后,通过求解求和,我们得到与A000225定义中相同的表达式。因此,我们现在继续推导在层级l上的最大整数,这由一个新的函数M(l)表示。在这种情况下,其评估所形成的序列为{0,2,6,14,30,62…},该序列也在 OEIS 中记录为A000918。为了推导它,我们可以利用m(l)的结果,因此,通过知道每一层中存在的节点数,M(l)可以定义为每一层m(l)中的最小整数加上该层的节点数。

作者提供的图片。
为了得到最终表达式2^(l+1)-2,我们将m(l)与该层级的节点数(去掉一个最小值)相加。而且,由于这个值与m(l)相符,我们可以得出结论M(l)=2m(l)。
在定义了算法将要操作的数据结构,并发现一些可能对复杂度分析有用的属性后,下面引入了该算法的伪代码表达:

作者提供的图片。
起初,尽管我们稍后会详细展开,输入由一个表示二叉树的向量(数组)组成,记作L。通过这个向量,数组中每个对应树中节点的元素将通过 for 循环线性遍历。在每次迭代中,都会初始化一个临时变量pos来存储数组元素,因此它将具有相同的类型(整数)。此外,在迭代过程中,所有从树的根节点到由当前 for 循环元素表示的节点所形成的路径节点都将通过嵌套其中的 while 循环进行遍历。为此,设定退出条件pos>0,这对应于pos已经到达根节点的情况。只要这个条件未被满足,pos将更新为其父节点的值,因此,假设输入结构是正确的,就可以保证对于树中的每一个节点,while 循环始终会到达根节点,从而终止。
输入特征
为了理解这个过程,熟悉输入格式是至关重要的。特别是,二叉树如何以数组的形式表示,这是算法使用的结构。为此,为了简化这种表示,对树节点的标签进行了一次转换,使得它们最初被标记的宽度优先遍历从根节点开始,从整数 1 开始。换句话说,所有节点的原始标签上都加上了 1。

图片来自作者。
上述转换的结果如上所示。正如所示,各个层次标签的最小值和最大值也已被修改。尽管如此,由于该转换的特性,只需对函数m(l)和M(l)应用其逆变换,就可以正确地重新标定标签序列。

图片来自作者。
因此,如果节点上的转换是将其标签加 1,那么其逆变换就是减去这个数量。这样,在对两个函数应用逆转换后,我们得到了上面的表达式,模拟了每个树层次的最小值和最大值标签序列。另一方面,通过新的标签表示,数据结构可以被表示为数组,就像我们的算法所接收的输入一样。类似地,由于它是一个完美的树,我们知道它在节点数量以及节点在层次结构中的位置上将具有规则的结构。因此,考虑输入数组L[n+1],我们可以将其索引与存储在这些位置的值相关联。
例如,如上图所示,在树的“链接”表示旁边,我们可以将每个节点的标签映射到数组索引,从而使L[1]表示根节点的实例,L[2]和L[3]表示它们各自的子节点,依此类推,直到终端节点。然而,也有必要明确表示它们的边,因此决定在数组中存储一个给定节点的父节点标签,索引由某个特定值确定。简而言之,对于数组中的每个节点i(索引),存储在L[i]中的值对应于i的父节点标签。然而,出于正确性的考虑,列表的第一个索引L[0]不被认为对应任何节点。此外,它的值被设定为-1,以表示在层次结构中它上面没有节点。
鉴于这一想法,研究序列{-1,0,1,1,1,2,2…} (A123108)的性质是值得的,甚至找到一个函数来扩展其值,这在分析中将非常有价值。因此,首先需要考虑如何确定给定节点i的子节点和父节点L[i]。

作者图片。
关于给定节点i的子节点,如果考虑到原始结构的任何子树也是一个完美的树,那么可以推断m(l)将在子树范围内发挥作用,结果是任何节点与其左子节点之间的标签差异为i(在此,i 下标为 2^l,两者等价),这与下级别的节点数量相符。

作者图片。
此外,为了验证这一点在所有子树中都成立,分别将偏移量α和β附加到左子节点和父节点的标签上,最终得出等式2α=β。

作者图片。
假设偏移量在其级别上不超过节点限制,那么位于其最小值m(l+1)和自身之间的子节点级别上的节点数量是上一级父节点同等数量的两倍。因此,根据完美二叉树的定义,通过在每一层节点数量翻倍,得出一个结论:某个i的左子节点的标签由表达式2i给出,其右子节点的标签为2i+1。同样,节点i总是有一个父节点,除非它是根节点,其父节点为L[0],这不被视为树节点。

作者图片。
为了确定输出父节点标签的函数,我们首先定义函数Cleft(i)和Cright(i),它们分别获取相应的子节点。这样,如果这些函数以某种方式转换标签,使得结果等同于在层级结构中的下降,那么它们的反函数将导致相反的效果,这在我们想要找回父节点时是预期的。将P(i)定义为返回i的父节点的函数,也可以等价地表示为向量L[i]中的值,在计算时根据输入的性质需要做出区分。也就是说,如果节点的标签是偶数,这意味着它是某个节点的左子节点,那么将调用Cleft(i)的反函数。另一方面,如果标签是奇数,函数P(i)的表达式就是Cright(i)的反变换。

作者图片。
从图形上看,对于偶数和奇数标签节点的这两种公式,随着i的增大,增长趋势渐近相似。由于地板函数的性质,对于偶数标签,可以通过i/2相关的界限和常数约束P(i)的值。结果,得出以下性质:

作者图片。
通过观察上述图形,无法保证P(i)的两个子表达式的渐近增长完全相等。但是,在推导了奇数情况的边界并确定依赖关系的阶数为O(i)后,我们可以计算当节点标签趋于无穷大时,两个函数的比值的极限,结果表明它们的增长是等效的,正如预期的那样。

图片由作者提供。
因此,为了简化,提供一个单一的P(i)公式无论接收到何种输入是方便的,因此最简单的选项是考虑偶数情况的增长阶数i/2,因为剩余的情况有相同的渐近增长。

图片由作者提供。
然而,i/2操作对于标签为奇数的节点并不返回整数,因此为了应对这一问题,决定对i/2应用地板函数。通过floor函数的性质,Floor[i/2]的值可以通过原始函数和其减去 1 的相同值来类似地界定。但是,由于目标是得到P(i)的正确表达式,而不是一个适用于渐近分析的近似表达式,因此仍然认为有必要从i/2的地板值来定义它。

图片由作者提供。
选择这种定义的主要原因来自于输入数组的形式定义:

图片由作者提供。
由于L包含由每个数组索引确定的父节点标签,因此可以通过P(i)函数来描述它们,其中i在此情况下是一个有效的索引。换句话说,第一个值L[0]必须等于-1,这用一种特殊的方式表示,不使用P(i),因为它无法生成该值。然后,基数组{-1}与序列{P(1), P(2), P(3)…}连接,该序列的长度为节点总数,值对应于标签序列{1, 2, 3…}的父节点。
GenerateTree[n_] := Flatten[{-1, Table[i/2, {i, 1, n}]}]
GenerateTree[15]
一旦输入数组中的序列被建模,上述是生成一个包含 15 个节点的树所需的Wolfram代码,结果是L={-1, 1/2, 1, 3/2, 2, 5/2, 3, 7/2, 4, 9/2, 5, 11/2, 6, 13/2, 7, 15/2}。正如预期的那样,通过不使用P(i)中的 Floor 函数,具有奇数索引的节点返回有理数,因此在重新定义了适当的P(i)函数后,得到了正确的序列L={-1, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7}:
GenerateTree[n_] := Flatten[{-1, Table[i/2 // Floor, {i, 1, n}]}]
GenerateTree[15]
输入可视化
除了构建数组之外,还应当将其可视化,以确保其中包含的索引和值与其定义一致。为此,Wolfram的图形功能被用来自动化树形可视化过程,基于由GenerateTree[]生成的序列:
PlotBinaryTreeFromList[treeList_List] :=
Module[{n = Length[treeList], edges},
edges = Flatten[Table[{i -> 2 i, i -> 2 i + 1}, {i, 1, Floor[n/2]}]];
edges = Select[edges, #[[2]] <= n &];
TreePlot[edges, VertexLabeling -> True,
VertexRenderingFunction -> ({Inset[treeList[[#2]], #1]} &),
DirectedEdges -> False, ImageSize -> Full]]
n = 31;
treeList = GenerateTree[n]
PlotBinaryTreeFromList[Drop[treeList, 1]]
当构建一个包含 31 个节点的树时,L={-1, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3 … 14, 14, 15, 15},其图形大致如下所示:

图片由作者提供。
具体来说,蓝色文本表示父节点的索引,而其他黑色文本则表示该节点的标签。
实现
现在,在输入已明确定义的情况下,我们可以更抽象地理解树遍历操作实际执行的内容。一方面,外部的For[]循环按从最低级别到根节点所在级别的顺序遍历所有节点。对于每个节点,While[]循环则按逆序遍历从根节点到访问节点的路径,尽管时间复杂度界限的关键因素是其长度。
TreeIteration[v_List] := Module[{n, pos}, n = Length[v];
For[i = n, i >= 0, i--,
pos = v[[i]];
Print["FOR: ", i];
While[pos > 0,
Print["While: ", pos];
pos = v[[pos + 1]];
]]]
因此,在 Wolfram 中实现后,加入了一些Print[]语句,用于在执行过程中显示它遍历的节点的父节点索引,从而更容易重建其执行痕迹。
输出
n = 7;
treeList = GenerateTree[n]
TreeIteration[treeList]
PlotBinaryTreeFromList[Drop[treeList, 1]]
运行算法时使用一个包含 7 个节点的树,表示为L={-1, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3},结果如下所示:
FOR: 8
While: 3
While: 1
FOR: 7
While: 3
While: 1
FOR: 6
While: 2
While: 1
FOR: 5
While: 2
While: 1
FOR: 4
While: 1
FOR: 3
While: 1
FOR: 2
FOR: 1
FOR: 0
乍一看,痕迹并不十分直观,因此应当将其与树形图结合查看:

图片由作者提供
在 for 循环的第一次迭代中,遍历从最低级别的最后一个节点开始,该节点的父节点索引为 3。随后,while 循环也会访问此节点,直到在下一次迭代中到达根节点并结束。在随后的 for 迭代中,执行相同的过程,不同之处在于它从索引为 6 的节点开始,而该节点的父节点与之前相同。因此,可以注意到,for 循环实际上在遍历树中所有连接每个节点与根节点的路径。
分析
在算法就位并且完全了解了输入和操作之后,适宜继续进行其分析,既包括内存分析也包括时间分析。一方面,内存占用的分析在这种情况下是直截了当的,因为该算法不需要额外的内存来执行任务,除了存储每次迭代中遍历的节点的整数值 pos。因此,表示额外内存消耗的渐近边界是常数 O(1)。如果考虑输入占用的空间,则需要大约 O(n) 量级的内存,其中 n 是树中节点的数量,或者更精确地说是 O(2^d),其中 d 是树的深度。
另一方面,为了确定时间复杂度的边界,我们必须定义一个基本操作来进行计算。对于这个算法,它被定义为在 while 循环内部执行的赋值操作,从抽象层面上看,可以认为是沿着节点与其父节点之间的边进行遍历。因此,为了确定该操作被调用的次数,首先将算法的成本分解为两个函数。一方面,表示整个算法执行总成本的 for 循环的成本 Tf(n)。而这又被定义为 while 循环所产生的成本总和,标记为 Tw(i)。

图片由作者提供。
对于数组中所有的节点 i,我们需要确定从根节点到 i 的路径遍历中涉及了多少基本操作,因此我们附加了相应的 Tw(i) 评估。具体来说,该函数将返回由某个输入节点引起的赋值操作的确切数量。因此,由于我们知道第一个 L[0] 无法走到任何路径到根节点,它不被计数,从而保持总和的限制在 1 到树中节点 n 的数量之间。
在继续之前,我们首先演示一下,将函数 P(i) 应用于树中位于层级 l 的节点 i,会导致标签上升到位于直接上层的节点,因为在此分析中考虑的基本操作等同于 pos=P(pos),主要由于输入特征:

图片由作者提供。
如所示,我们从一个不等式开始,任何节点都必须满足该不等式,并且与其所在层次相关,其标签被m(l)和M(l)函数所界定,假设l是其层次。然后,应用P后,可以进行一些简化,得出结论:P(i)位于2(l-1)**和**2l-1之间,这两个值分别与m(l-1)和M(l-1)相符,表明经过变换后,得到的节点位于l-1层次。通过这一点,我们证明了经过若干次 while 循环迭代后,存储在 pos 中的节点会接近树的根节点。因此,如果这些迭代足够多,路径一定会到达根节点并终止。尽管如此,若考虑到无限树的情况,这一点可能不成立。
方法一
目前,我们知道复杂度由Tf(n)主导,尽管没有Tw(i)的确切表达式,因此我们继续讨论三种不同的方法来刻画这个函数,从而分析执行时间的整体渐进增长。
无论剩余函数如何计算,所有分析中都会满足对树节点的约束。也就是说,除了根节点外,每个节点都有唯一的父节点,因此我们可以确保从某一节点(位于层次l)到根节点的路径长度等于l。主要是由于上述证明的性质,尽管也可以通过意识到路径上的每个节点都位于不同层次这一点来证明,节点的层次可以从 0 变化到l。然后,随着 while 循环遍历路径上的每个节点,可以得出结论,Tw(i)中计数的操作恰好为l。

图片来源:作者。
现在,分析集中于计算给定节点的层次,因此我们从上面的不等式开始。也就是说,节点i的标签位于层次l,其值被m(l)和M(l)所界定,得出两个条件,从而可以准确地量化层次:

图片来源:作者。
一方面,从不等式的左侧解出,可以得到一个简化为对log_2(i)的下取整操作的条件,由其定义可得。由此可以推断出,层次等于该值,尽管原始不等式的另一个条件仍需验证。

图片来源:作者。
从右侧开始,我们得到了l的下界,这在表面上似乎与之前的内容相互补充。然而,在进行运算并应用天花板函数的定义后,我们得出了以下的层次公式,因为其值是满足上述最后不等式的最小整数。

图片来源:作者。
总结到目前为止,我们已经推导出了多个节点i的层级表达式,起初这些表达式可能被认为是该值的界限,鉴于它们的性质。然而,层级必须是整数,因此可以考虑检查它们之间的距离,以确保它们足够小,从而能够唯一确定一个值。

图片来自作者。
总之,已证明,对于节点标签可能具有的所有值,这两个表达式是相同的。因此,节点的层级可以通过上述公式中的任意一个来推导,左边的公式是最简单的,因此将在分析中使用。

图片来自作者。
由于i的层级与 while 循环中的基本操作一致,成本Tw(i)的定义类似于从节点的层级出发的路径,该路径必须开始于顶部。

图片来自作者。
接下来,利用每次迭代的成本表达式作为初始节点的函数,我们可以尝试找出树的所有节点所产生的成本之和。但是,由于每个加数中都有一个取整函数,我们将首先研究不应用此函数对最终界限的影响,以简化求和,并研究在取整函数可以省略的情况下结果的界限。

图片来自作者
如果我们绘制Tf(n)在一个适当范围内的n,可以发现去掉每个加数取整后的函数与原始函数之间有轻微的差异。特别是,直接对对数值求和而不做任何额外变换的函数,似乎是实际复杂度的上界,因此,如果我们继续求解每一项直接为log_2(i)的和,我们可以得到一个渐近上可能大于实际复杂度的界限,从而确立了上界:

图片来自作者。
通过将求和表达式转化为闭式形式,我们可以假设该算法的执行时间不超过关于输入树节点数量的阶次O(log(n!))。然而,这个上界可以进一步简化。例如,如果我们考虑在每次迭代中,for 循环执行的次数与 n 节点数量相等,每次执行的工作量与树的最大层级成正比,且不超过该最大层级,我们会得到一个上界阶次为O(n log(n))。因此,如果我们通过比较比较-,if%20and%20only%20if,-(de%C2%A0Bruijn)的极限比值,得出与之前的阶次O(log(n!))相比,当输入趋于无穷大时它们是等价的,从而简化了算法运行时的上界为O(n log(n))。

图片来自作者。
在此时,算法的上界确保了其运行时开销在输入增长方面不会超过O(n log(n))的阶次。然而,分析的重点在于尽可能地限制成本,也就是说,找到一个紧确界,而不是上界,因为在某些情况下,上界与紧确界可能会有显著差异。为此,需要为上述的求和式Tf(n)找到一个闭式,尤其是在应用向下取整函数于加数时。直观上,应用向下取整将会在某种程度上减少每一项的值,并且由于上限和树的大小之间的依赖关系,最终的值可能会有所不同。

图片来自作者。
首先,为了使log_2(i)成为整数并避免应用向下取整转换,节点标签必须是2^l的形式,其中l必须指的是它所在的层级。

图片来自作者。
与m(l)一致,以上已显示所有标签为该层级最小值的节点i=m(l),其log_2(i)将是一个整数,即l。

图片来自作者。
因此,通过将m(l)和M(l)之间的所有标签作为输入传递给floor(log_2(i))函数,应该可以得到该层级的值,已经发现该值与该层级的“代表性”m(l)节点的值一致。简而言之,这允许假设同一层级的每个节点都将承担相同的成本Tw(i),因为从任一节点到根节点的路径长度恰好等于 l。

图片来自作者。
随后,推导出每一层级的节点数,正如没有这一步骤时的直觉猜测,它是2^l,也就是说,如果每一层的节点数是上一层的两倍,那么对于某一层,这个数量将由分支因子与其自身l次相乘得到。

图片由作者提供。
总之,算法在同一层级l的所有节点的运行时成本是到根节点路径长度与该层级节点数的乘积。根据这个结果,可以得出一个依赖于树深度d的Tf(n)的闭式:

图片由作者提供。
通过将总和重写为从 0 到深度的层级函数,我们得到了上述表达式,通过定义d与节点总数n之间的关系,可以具体化此表达式:

图片由作者提供。
由于 n 是最后一个节点的标签,floor(log_2(n))可以确保返回最后一层的值,而这个值与深度d相符。因此,通过上述完整成本Tf(n)的公式,我们得出了以下紧密的界限:

图片由作者提供。
在这一点上,值得尝试简化它,使其呈现出更简洁的表达式。为此,我们继续计算它与之前的上界的比率,这将主要显示两者之间的差异,以防它们是渐近等价,否则它们会在相反的情况下发散(尽管它也可能收敛到 0)。

图片由作者提供。
然而,比率的极限对于两个上界产生相同的结果,是渐近等价的。并且,由于它们位于一个实数区间内,可以推断出,紧密的界限至少在渐近意义上等价于上界,因为比率表示在无穷大时的差异可以忽略不计。

图片由作者提供。
最后,算法的时间复杂度由最高阶决定,正如我们下面将看到的,可以通过几种方式来实现。在继续之前,值得注意的是,紧密界限的两个表达式之间的关系。后者直接依赖于节点的数量,而原始的可以通过重写上述表达式,将n替换为最后一层节点的数量来形成,这有助于更好地理解运行时间与所涉及数据结构的属性之间的依赖关系。
方法 2
另一种进行分析的方法是定义输入数组中包含的每个值:

图片来自作者。
每个值通过具体的评估P(i)来标识,从中可以推断出以下约束:

图片来自作者。
通过将P(i)表示为树中的上升,任何输入范围在[0,n]之间的值都将始终返回一个位于相同区间内的结果,这导致了 while 循环中遍历的形式化,我们将由此得到Tw(i):

图片来自作者。
在遍历开始时,选择任何节点i,向其父节点P(i)上升,然后向其祖先P(P(i))上升,依此类推,直到到达标签为 1 的根节点。实际上,循环在到达“节点”L[0]时停止,但这里假设它在根节点处停止,因为成本差异将是恒定的。因此,上文我们通过组合 P(i)若干次来形式化这个过程,正如我们所知,这与到根节点的路径长度相吻合,可以设定为节点的层级 l。

图片来自作者。
采用这种方法,成本Tw(i)定义为输入节点的层级,也可以通过找到满足上面等式的整数来获得。

图片来自作者。
此时,在获取导致重复组合结果为 1 的整数 l 时,我们首先应用地板函数的属性来描述组合的封闭形式。同时,可以证明函数 P 的组合结果为上述表达式。

图片来自作者。
此后,按照地板函数的定义,在组合的闭式和应达到的结果之间建立了不等式。即,等式规定在进行l次组合后,根的值正好达到,尽管由于地板函数的参数可能大于 1,我们从推导出的不等式开始。最后,我们得出某个节点i级别的表达式,并用它来求解Tf(n),从而得出复杂度。

图片来源:作者。
当用节点i的级别替换Tw(i)时,产生的求和与之前分析中解决的求和是等价的,因此最终表达式也是等价的。

图片来源:作者。
最终,从该过程中得出的紧束缚是nlog(n)阶,与之前推导出的结果一致。反过来,它也可以重新写成树深度的函数,这在某些情况下是有用的。
方法 3
最后,我们将探索一种替代方法来执行此分析并获得先前的渐近束缚。在这种情况下,我们将从存储在数组中的父节点标签i开始。此标签在低级别表示为正整数,特别是在***二进制(base 2)表示中。因此,它的二进制形式可以表示如下:

图片来源:作者。
一方面,它被定义为按相应进制的值对位进行加权求和,紧凑形式上,它被形式化为一组位,子标记表示该值。

图片来源:作者。
每个位是整数 0 或 1,其子标属于从 0 到B(i)-1的整数区间,其中B(i)是返回整数i的二进制表示长度的函数。

图片来源:作者。
至于其公式,已被证明描述在二进制中表示一个整数所需的位数由上述等式给出。先验地,对数项与描述节点i所在级别的表达式相同,因此我们可以开始阐明其余的过程。

图片来源:作者。
要计算Tw(i),必须考虑P(i)对节点标签的二进制表示的影响。简而言之,通过重复应用P(i)得到的标签必须为 1,或者为了简便起见,取为 0。因此,通过将标签除以 2 并应用向下取整函数,可以确保在二进制中此函数的等效操作是右移操作。因此,在B(i)移位后,得到的标签将为 0,结束 while 循环的路径,并产生与floor(log_2(i))+1成正比的成本。

图片来源:作者。
同样地,当在总体成本的和中代入B(i)时,在本分析中我们最终得到了一个额外的项n,该项由于比最终值小,渐近上可以忽略不计。

图片来源:作者。
总结来说,通过这个过程,得出了相同的紧界限,保持了算法的运行时成本为nlog(n)的阶。
时间测量
最后,在理论分析后,将收集实验测量数据,记录算法在不同输入大小下的运行时间,以展示运行时增长与紧界限匹配的好坏。
data = Flatten[
ParallelTable[
Table[{n,
AbsoluteTiming[TreeIteration[GenerateTree[n]]][[1]]}, {index, 0,
n}], {n, 1, 300}], 1];
为此,在测量过程中使用了多个 Wolfram 函数。其中最重要的是AbsoluteTiming[],它记录了运行包含 n 个节点的树所需的时间(秒)。在这里,我们并没有选择 2 的幂次方的n值,而是简单地假设输入是一个完整的树,而不是一个完美的树,以观察执行时间如何随节点数的变化而增长。然后,测量从 1 到 300 的 n 值,对于每个相应节点数进行n次运行。
nonlinearModel = NonlinearModelFit[points, a*x*Log[x], {a}, x]
ListPlot[{points, Table[{x, nonlinearModel[x]}, {x, 0, 300}]},
PlotStyle -> {Red, Green}, ImageSize -> Large,
AxesLabel -> {"n", "Time (s)"}]
之后,定义了一个拟合模型形式为cnlog(n),其中c*表示一个常数,作为参数调整其值,以适应由NonLinearModelFit[]函数指定的测量数据集。

图片来源:作者。
一旦模型拟合完成,生成的最优结果,其解释在与数据点绘制时会显得更加有意义:

图片来源:作者。
如图所示,由于测量过程中的实际干扰,数据集显示出一些变异性。然而,随着 n 的增加,增长趋势明显类似于nlog(n)的顺序,这一点与模型的位置相比也非常显著,模型位于两个区域之间的平均值以下,而这两个区域明显显示出更高的测量密度。

图片由作者提供。
最后,通过前面的拟合结果和调整后的 R²值为 0.934551,可以得出结论,模型正确捕捉到了数据集的增长趋势。然而,其变异性转化为常数 c 值的轻微不确定性。
结论
算法的正式分析通过 Θ(nlog(n)) 的阶数来表征其执行时间的渐进增长。这个界限是通过三种不同的方法计算得出的,尽管它们都基于相同的思想,即确定每个树节点的深度。在第一种方法中,层级被用作深度的度量,这相当于 P(i) 必须与自身组合的次数,才能到达根节点的标签,进而对应于用二进制表示初始节点 i 标签所需的位数。
此外,最后值得一提的是,这次分析中大部分涉及的 Wolfram 代码是由GPT-4o模型生成的,来自ChatGPT。
Time-LLM:为时间序列预测重编程 LLM
了解 Time-LLM 的架构并在 Python 中应用于预测项目
·发表于Towards Data Science ·阅读时间 12 分钟·2024 年 3 月 5 日
--

图片由Zdeněk Macháček提供,来源于Unsplash
这并不是研究人员第一次尝试将自然语言处理(NLP)技术应用于时间序列领域。
例如,Transformer 架构是 NLP 中的一个重要里程碑,但其在时间序列预测中的表现一直平平,直到PatchTST的提出。
如你所知,大型语言模型(LLM)正在积极开发,并已展示出在自然语言处理(NLP)中的令人印象深刻的泛化和推理能力。
因此,值得探讨将大型语言模型(LLM)用于时间序列预测的思路,以便我们能够充分利用这些大型预训练模型的能力。
为此,Time-LLM应运而生。在原始论文中,研究人员提出了一个框架,将现有的 LLM 重新编程以执行时间序列预测。
在本文中,我们探讨了 Time-LLM 的架构及其如何有效地让 LLM 进行时间序列数据预测。接着,我们实现了该模型并将其应用于一个小型预测项目。
如需更多详情,请务必阅读原始论文。
TIME-MOE:亿级规模的时间序列基础模型,采用专家混合模型(Mixture-of-Experts)
并且是开源的!
·发布于 Towards Data Science ·阅读时长 8 分钟·2024 年 10 月 31 日
--

Time-MOE 的顶层视图 (图片来源)
专家混合模型(MOE)架构随着大规模语言模型(LLMs)的兴起而获得了广泛关注。
随着时间序列模型采用尖端技术,专家混合模型(Mixture-of-Experts)自然地在时间序列基础模型领域占据了一席之地。
本文讨论了Time-MOE,一种使用 MOE 来提高预测准确性并降低计算成本的时间序列基础模型。关键贡献包括:
-
Time-300B 数据集:世界上最大的开放时间序列数据集,包含跨越 9 个领域的 3000 亿时间点,并配备可扩展的数据清洗管道。
-
时间序列的扩展法则:深入探讨扩展法则如何影响大规模时间序列模型。
-
Time-MOE 架构:一系列开源时间序列模型,利用 MOE 提升性能。
让我们开始吧
✅ 在AI 项目文件夹中找到Time-MOE的实践项目,以及其他精彩项目!
进入 Time-MOE
Time-MOE 是一个 24 亿参数的开源时间序列基础模型,采用专家混合模型(Mixture-of-Experts,MOE)进行零样本预测
Time-MoE:最新的基础预测模型
探索开源的大型时间模型 Time-MoE,并通过 Python 在一个小实验中应用它
·发布于 Towards Data Science ·12 分钟阅读·2024 年 10 月 29 日
--

图片来源:Irina Iriser 在 Unsplash
传统上,时间序列预测领域依赖于数据特定的模型,其中模型会在特定的数据集和任务上进行训练。如果数据或预测范围发生变化,模型也必须进行相应的调整。
自 2023 年 10 月以来,研究人员一直在积极开发基础预测模型。借助这些大型时间模型,单个模型现在可以处理来自不同领域、不同频率和几乎任何预测范围的不同预测任务。
这些大型时间模型包括:
-
TimeGPT,通过 API 进行访问,方便执行预测和微调,无需使用本地资源
-
Lag-Llama,一个开源的概率预测模型,通过滞后值构建特征
-
Chronos,一个基于 T5 的模型,通过标记化和量化将无界时间序列领域转化为有界语言领域
-
Moirai,一个支持外生特征的模型,并且首次公开分享他们的数据集 LOTSA,包含超过 270 亿个数据点。
时间序列对大型语言模型(LLMs)来说并没有那么不同
利用 LLMs 的力量进行时间序列建模
·发布于Towards Data Science ·阅读时间:7 分钟·2024 年 7 月 12 日
--
基础模型推动了计算语言学和计算机视觉领域的最新进展,并在人工智能(AI)中取得了巨大的成功。成功的基础模型的关键思想包括:
-
巨大的数据规模:广泛而多样化的训练数据涵盖了一个全面的分布,使得模型能够逼近任何潜在的测试分布。
-
可迁移性:通过记忆和回忆已学信息的机制,如提示[1]和自监督预训练[2],使得模型能够有效适应新任务。

时间序列基础模型的开发在 LLM 成功之后变得更加密集。图片来自论文arxiv.org/pdf/2403.14735。
大型时间序列基础模型(LTSM)
在基础模型在计算语言学领域取得成功后,越来越多的研究工作正在努力将这种成功复制到另一种类型的序列数据:时间序列。
与大型语言模型(LLMs)类似,大型时间序列基础模型(LTSM)旨在从大量多样的时间序列数据中学习,以进行预测。经过训练的基础模型可以针对各种任务进行微调,例如异常检测或时间序列分类。
时间序列 — ARIMA vs. SARIMA vs. LSTM:实践教程
让我们一步步预测未来。
·发布于Towards Data Science ·14 分钟阅读·2024 年 10 月 3 日
--

在这篇文章中,我们将深入探讨时间序列预测的世界。预测未来时间对各种业务具有重要价值。例如,需求预测对在线零售商准备年终促销活动非常有帮助,以确保足够的库存满足即将到来的购物需求。在金融领域,股票交易员依赖复杂的预测模型来决定买卖哪些证券。而在更基本的场景中,我们依赖天气预报来决定是否带伞或穿雨衣,每天出门上班时。这些都是时间序列预测在我们生活中扮演重要角色的实例。
在这篇文章中,我们将讨论该领域中三种最常见的时间序列预测模型:
-
自回归积分滑动平均模型(ARIMA)
-
季节性自回归积分滑动平均模型(SARIMA)
-
长短期记忆(LSTM)。
我们将通过动手实现每个预测模型,训练模型并使用每个已训练的模型生成预测结果,来学习每个预测模型。然后我们将查看这些模型的评估指标...
时间序列预测:探索性数据分析实用指南
如何利用探索性数据分析从时间序列数据中提取信息,并使用 Python 增强特征工程
·发表于Towards Data Science ·15 分钟阅读·2024 年 5 月 9 日
--

由Ales Krivec拍摄,来源于Unsplash
引言
时间序列分析无疑是数据科学和机器学习领域最广泛的主题之一:无论是预测金融事件、能源消耗、产品销售还是股市趋势,这个领域一直是企业关注的重点。
显然,数据可用性的极大增加,加上机器学习模型的不断进步,使得这个话题今天更加引人关注。除了传统的统计预测方法(如回归模型、ARIMA 模型、指数平滑),与机器学习(如基于树的模型)和深度学习(如 LSTM 网络、CNN、基于 Transformer 的模型)相关的技术已经存在一段时间了。
尽管这些技术之间存在巨大差异,但无论模型是什么,都必须完成一个初步步骤:探索性数据分析。
在统计学中,探索性数据分析(EDA)是一门分析和可视化数据的学科,旨在总结其主要特征并从中获取相关信息。这在数据科学领域具有重要意义,因为它为另一个重要步骤奠定了基础:特征工程。即,创建、转换和提取数据集特征的实践,以便模型能够发挥最佳性能。
因此,本文的目标是定义一个清晰的探索性数据分析模板,专注于时间序列,可以总结并突出数据集的最重要特征。为此,我们将使用一些常见的 Python 库,如Pandas、Seaborn和Statsmodel。
数据
让我们首先定义数据集:为了本文的目的,我们将使用 Kaggle 的每小时能源消耗数据集。该数据集与 PJM 每小时能源消耗数据有关,PJM 是美国一个区域传输组织,向特拉华州、伊利诺伊州、印第安纳州、肯塔基州、马里兰州、密歇根州、新泽西州、北卡罗来纳州、俄亥俄州、宾夕法尼亚州、田纳西州、弗吉尼亚州、西弗吉尼亚州和哥伦比亚特区提供电力。
每小时的电力消耗数据来自 PJM 网站,单位为兆瓦(MW)。
探索性数据分析
现在让我们定义在处理时间序列时需要进行的最重要的分析。
当然,其中最重要的事情之一是绘制数据图表:图形可以突出显示许多特征,如模式、异常观察、随时间变化的变化以及变量之间的关系。如前所述,从这些图表中得出的见解必须尽可能地纳入预测模型中。此外,一些数学工具,如描述性统计和时间序列分解,也将非常有用。
也就是说,本文提出的探索性数据分析(EDA)包含六个步骤:描述性统计、时间图、季节性图、箱型图、时间序列分解、滞后分析。
1. 描述性统计
描述性统计是定量描述或总结结构化数据集合特征的汇总统计量。
描述数据集时常用的一些指标包括:集中趋势度量(例如均值、中位数)、离散程度度量(例如范围、标准差)和位置度量(例如百分位数、四分位数)。所有这些指标都可以通过所谓的五数汇总进行总结,其中包括:最小值、第一四分位数(Q1)、中位数或第二四分位数(Q2)、第三四分位数(Q3)和分布的最大值。
在 Python 中,这些信息可以通过 Pandas 中著名的describe方法轻松检索:
import pandas as pd
# Loading and preprocessing steps
df = pd.read_csv('../input/hourly-energy-consumption/PJME_hourly.csv')
df = df.set_index('Datetime')
df.index = pd.to_datetime(df.index)
df.describe()

1. PJME 统计汇总。
2. 时间图
开始时,最明显的图表是时间图。这意味着,观测结果是根据其观测时间绘制的,连续的观测点通过线条连接。
在 Python 中,我们可以使用 Pandas 和 Matplotlib:
import matplotlib.pyplot as plt
# Set pyplot style
plt.style.use("seaborn")
# Plot
df['PJME_MW'].plot(title='PJME - Time Plot', figsize=(10,6))
plt.ylabel('Consumption [MW]')
plt.xlabel('Date')

2.1 PJME 消耗时间图。
这个图表已经提供了若干信息:
-
正如我们所预期的,图形显示了年度季节性变化。
-
聚焦于单一年份时,似乎会有更多的模式出现。很可能,能耗在冬季会有一个高峰,夏季也会有一个高峰,原因是更大的电力消耗。
-
该系列没有表现出明确的增长/下降趋势,平均能耗保持稳定。
-
在 2023 年左右出现了一个异常值,可能在实现模型时应该进行补偿。
3. 季节性图
季节性图基本上是一个时间图,其中数据根据其所属的各个“季节”进行绘制。
关于能源消耗,我们通常有每小时的数据,因此可能会有几种季节性:年度、每周、每日。在深入分析这些图表之前,我们先在 Pandas 数据框中设置一些变量:
# Defining required fields
df['year'] = [x for x in df.index.year]
df['month'] = [x for x in df.index.month]
df = df.reset_index()
df['week'] = df['Datetime'].apply(lambda x:x.week)
df = df.set_index('Datetime')
df['hour'] = [x for x in df.index.hour]
df['day'] = [x for x in df.index.day_of_week]
df['day_str'] = [x.strftime('%a') for x in df.index]
df['year_month'] = [str(x.year) + '_' + str(x.month) for x in df.index]
3.1 季节性图 — 年度消费
一个非常有趣的图表是关于按年分组的每月能耗图,这突出了年度季节性变化,并且可以告诉我们多年间的上升/下降趋势。
这是 Python 代码:
import numpy as np
# Defining colors palette
np.random.seed(42)
df_plot = df[['month', 'year', 'PJME_MW']].dropna().groupby(['month', 'year']).mean()[['PJME_MW']].reset_index()
years = df_plot['year'].unique()
colors = np.random.choice(list(mpl.colors.XKCD_COLORS.keys()), len(years), replace=False)
# Plot
plt.figure(figsize=(16,12))
for i, y in enumerate(years):
if i > 0:
plt.plot('month', 'PJME_MW', data=df_plot[df_plot['year'] == y], color=colors[i], label=y)
if y == 2018:
plt.text(df_plot.loc[df_plot.year==y, :].shape[0]+0.3, df_plot.loc[df_plot.year==y, 'PJME_MW'][-1:].values[0], y, fontsize=12, color=colors[i])
else:
plt.text(df_plot.loc[df_plot.year==y, :].shape[0]+0.1, df_plot.loc[df_plot.year==y, 'PJME_MW'][-1:].values[0], y, fontsize=12, color=colors[i])
# Setting labels
plt.gca().set(ylabel= 'PJME_MW', xlabel = 'Month')
plt.yticks(fontsize=12, alpha=.7)
plt.title("Seasonal Plot - Monthly Consumption", fontsize=20)
plt.ylabel('Consumption [MW]')
plt.xlabel('Month')
plt.show()

3.1 PJME 年度季节性图
该图显示每年实际上有一个非常预定义的模式:冬季消费显著增加,夏季达到峰值(由于供暖/制冷系统),而春季和秋季通常不需要供暖或制冷,因此消费最小。
此外,该图告诉我们,在整体能耗上,跨年并没有明确的增长/下降趋势。
3.2 季节性图 — 每周消费
另一个有用的图表是每周图表,它描绘了每月内一周的能耗变化,还可以提示我们能耗在单一年份中的变化情况。
让我们看看如何用 Python 来解决这个问题:
# Defining colors palette
np.random.seed(42)
df_plot = df[['month', 'day_str', 'PJME_MW', 'day']].dropna().groupby(['day_str', 'month', 'day']).mean()[['PJME_MW']].reset_index()
df_plot = df_plot.sort_values(by='day', ascending=True)
months = df_plot['month'].unique()
colors = np.random.choice(list(mpl.colors.XKCD_COLORS.keys()), len(months), replace=False)
# Plot
plt.figure(figsize=(16,12))
for i, y in enumerate(months):
if i > 0:
plt.plot('day_str', 'PJME_MW', data=df_plot[df_plot['month'] == y], color=colors[i], label=y)
if y == 2018:
plt.text(df_plot.loc[df_plot.month==y, :].shape[0]-.9, df_plot.loc[df_plot.month==y, 'PJME_MW'][-1:].values[0], y, fontsize=12, color=colors[i])
else:
plt.text(df_plot.loc[df_plot.month==y, :].shape[0]-.9, df_plot.loc[df_plot.month==y, 'PJME_MW'][-1:].values[0], y, fontsize=12, color=colors[i])
# Setting Labels
plt.gca().set(ylabel= 'PJME_MW', xlabel = 'Month')
plt.yticks(fontsize=12, alpha=.7)
plt.title("Seasonal Plot - Weekly Consumption", fontsize=20)
plt.ylabel('Consumption [MW]')
plt.xlabel('Month')
plt.show()

3.2 PJME 每周季节性图
3.3 季节性图 — 日常消费
最后,我想展示的最后一个季节性图是日常能耗图。如你所猜测,它表示能耗在一天内的变化。在这种情况下,数据首先按星期几分组,然后聚合计算均值。
这是代码:
import seaborn as sns
# Defining the dataframe
df_plot = df[['hour', 'day_str', 'PJME_MW']].dropna().groupby(['hour', 'day_str']).mean()[['PJME_MW']].reset_index()
# Plot using Seaborn
plt.figure(figsize=(10,8))
sns.lineplot(data = df_plot, x='hour', y='PJME_MW', hue='day_str', legend=True)
plt.locator_params(axis='x', nbins=24)
plt.title("Seasonal Plot - Daily Consumption", fontsize=20)
plt.ylabel('Consumption [MW]')
plt.xlabel('Hour')
plt.legend()

3.3 PJME 日常季节性图
通常,这个图显示出一个非常典型的模式,有人称之为“M 型”,因为能耗似乎在一天内形成一个“M”形。有时这个模式很明显,有时则不明显(就像在这个案例中)。
然而,这些图表通常显示出一天中间的相对峰值(从上午 10 点到下午 2 点),然后是一个相对的最低值(从下午 2 点到 6 点),再到另一个峰值(从下午 6 点到 8 点)。最后,它还显示了周末和其他日期的消费差异。
3.4 季节性图表 — 特征工程
现在让我们看看如何使用这些信息进行特征工程。假设我们正在使用一些需要高质量特征的机器学习模型(例如 ARIMA 模型或基于树的模型)。
以下是来自季节性图表的主要证据:
-
年度消费在几年间变化不大:这提示我们可以在可用的情况下,使用来自滞后或外生变量的年度季节性特征。
-
每周的消费在各个月份间遵循相同的模式:这提示我们可以使用来自滞后或外生变量的每周特征。
-
日常消费在普通工作日和周末有所不同:这提示我们可以使用类别特征来识别某一天是否为普通工作日。
4. 箱型图
箱型图是识别数据分布的一种有用方式。简而言之,箱型图描绘了分位数,代表分布的第 1 四分位数(Q1)、第 2 四分位数(Q2/中位数)和第 3 四分位数(Q3),以及须状线,代表数据的范围。任何超出须状线的值都可以被视为异常值,更深入地说,须状线通常是通过以下方式计算的:

4. 须状线公式
4.1 箱型图 — 总消费
首先让我们计算关于总消费的箱型图,这可以通过Seaborn轻松完成:
plt.figure(figsize=(8,5))
sns.boxplot(data=df, x='PJME_MW')
plt.xlabel('Consumption [MW]')
plt.title(f'Boxplot - Consumption Distribution');

4.1 PJME 箱型图
即使这个图表看起来信息不多,它告诉我们我们正处理一个类似高斯分布的情况,且尾部在右侧更加突出。
4.2 箱型图 — 天月分布
一个非常有趣的图表是按天/月分箱图。它是通过创建一个“天 月”变量并按此变量对消费进行分组得到的。以下是代码,只涉及从 2017 年开始的数据:
df['year'] = [x for x in df.index.year]
df['month'] = [x for x in df.index.month]
df['year_month'] = [str(x.year) + '_' + str(x.month) for x in df.index]
df_plot = df[df['year'] >= 2017].reset_index().sort_values(by='Datetime').set_index('Datetime')
plt.title(f'Boxplot Year Month Distribution');
plt.xticks(rotation=90)
plt.xlabel('Year Month')
plt.ylabel('MW')
sns.boxplot(x='year_month', y='PJME_MW', data=df_plot)
plt.ylabel('Consumption [MW]')
plt.xlabel('Year Month')

4.2 PJME 年/月箱型图
可以看出,夏季/冬季月份的消费不确定性较低(即,当我们有峰值时),而春季/秋季则更为分散(即,当温度更为变化时)。最后,2018 年夏季的消费高于 2017 年,可能是由于夏季气温较高。在进行特征工程时,请记得包含(如果有的话)温度曲线,它可能作为外生变量使用。
4.3 箱型图 — 天分布
另一个有用的图表是显示一周内消费分布的图表,这与每周消费季节性图表类似。
df_plot = df[['day_str', 'day', 'PJME_MW']].sort_values(by='day')
plt.title(f'Boxplot Day Distribution')
plt.xlabel('Day of week')
plt.ylabel('MW')
sns.boxplot(x='day_str', y='PJME_MW', data=df_plot)
plt.ylabel('Consumption [MW]')
plt.xlabel('Day of week')

4.3 PJME 天箱型图
如前所见,消费在周末明显较低。不过,有几个异常值表明像“星期几”这样的日历特征确实有用,但可能无法完全解释该序列。
4.4 箱型图 — 小时分布
最后我们来看小时分布箱型图。它与每日消费季节性图类似,因为它展示了消费如何在一天内分布。接下来是代码:
plt.title(f'Boxplot Hour Distribution');
plt.xlabel('Hour')
plt.ylabel('MW')
sns.boxplot(x='hour', y='PJME_MW', data=df)
plt.ylabel('Consumption [MW]')
plt.xlabel('Hour')

4.4 PJME 小时箱型图
注意,之前看到的“M”形状现在变得更加压缩。此外,有很多离群点,这告诉我们数据不仅依赖于日常季节性(例如今天 12 点的消费与昨天 12 点的消费相似),还依赖于其他因素,可能是某些外部气候因素,如温度或湿度。
5. 时间序列分解
如前所述,时间序列数据可能表现出多种模式。通常,将时间序列分解为多个成分,每个成分代表一个潜在的模式类别,这对分析非常有帮助。
我们可以将时间序列看作由三个成分组成:趋势成分、季节性成分和残差成分(包含时间序列中的其他所有因素)。对于某些时间序列(例如,能源消费序列),可能存在多个季节性成分,对应不同的季节周期(每日、每周、每月、每年)。
时间序列分解有两种主要类型:加性和乘性。
对于加性分解,我们将一个时间序列(𝑦)表示为季节性成分(𝑆)、趋势(𝑇)和残差(𝑅)的和:

同样,乘性分解可以写成:

一般来说,加性分解最适合方差恒定的序列,而乘性分解最适合方差非平稳的时间序列。
在 Python 中,时间序列分解可以通过Statsmodel库轻松实现:
df_plot = df[df['year'] == 2017].reset_index()
df_plot = df_plot.drop_duplicates(subset=['Datetime']).sort_values(by='Datetime')
df_plot = df_plot.set_index('Datetime')
df_plot['PJME_MW - Multiplicative Decompose'] = df_plot['PJME_MW']
df_plot['PJME_MW - Additive Decompose'] = df_plot['PJME_MW']
# Additive Decomposition
result_add = seasonal_decompose(df_plot['PJME_MW - Additive Decompose'], model='additive', period=24*7)
# Multiplicative Decomposition
result_mul = seasonal_decompose(df_plot['PJME_MW - Multiplicative Decompose'], model='multiplicative', period=24*7)
# Plot
result_add.plot().suptitle('', fontsize=22)
plt.xticks(rotation=45)
result_mul.plot().suptitle('', fontsize=22)
plt.xticks(rotation=45)
plt.show()

5.1 PJME 系列分解 — 加性分解。

5.2 PJME 系列分解 — 乘性分解。
上述图表是指 2017 年。在这两种情况下,我们可以看到趋势有几个局部峰值,夏季的值较高。从季节性成分中,我们可以看到序列实际上有几个周期性,这个图突出显示了每周的周期性,但如果我们专注于同一年(2017 年)中的某个月(如 1 月),日常季节性也会显现出来:
df_plot = df[(df['year'] == 2017)].reset_index()
df_plot = df_plot[df_plot['month'] == 1]
df_plot['PJME_MW - Multiplicative Decompose'] = df_plot['PJME_MW']
df_plot['PJME_MW - Additive Decompose'] = df_plot['PJME_MW']
df_plot = df_plot.drop_duplicates(subset=['Datetime']).sort_values(by='Datetime')
df_plot = df_plot.set_index('Datetime')
# Additive Decomposition
result_add = seasonal_decompose(df_plot['PJME_MW - Additive Decompose'], model='additive', period=24*7)
# Multiplicative Decomposition
result_mul = seasonal_decompose(df_plot['PJME_MW - Multiplicative Decompose'], model='multiplicative', period=24*7)
# Plot
result_add.plot().suptitle('', fontsize=22)
plt.xticks(rotation=45)
result_mul.plot().suptitle('', fontsize=22)
plt.xticks(rotation=45)
plt.show()

5.3 PJME 系列分解 — 加性分解,关注 2017 年 1 月。

5.4 PJME 系列分解 — 乘性分解,关注 2017 年 1 月。
6. 滞后分析
在时间序列预测中,滞后是指序列的过去值。例如,对于日常序列,第一个滞后是指序列前一天的值,第二个滞后是指前两天的值,以此类推。
滞后分析基于计算序列与该序列的滞后版本之间的相关性,这也称为自相关。对于 k 滞后的序列,我们定义自相关系数为:

其中,y bar 表示序列的均值,k表示滞后。
自相关系数构成了序列的自相关函数(ACF),这只是一个图表,展示了自相关系数与所考虑滞后数量之间的关系。
当数据具有趋势时,小滞后的自相关通常很大且为正,因为时间接近的观测值也通常数值相近。当数据呈现季节性时,与季节滞后(以及季节周期的倍数)对应的自相关值将大于其他滞后的自相关值。具有趋势和季节性的数据将表现出这些效应的组合。
实际上,更有用的函数是部分自相关函数(PACF)。它类似于 ACF,只不过它只显示两个滞后之间的直接自相关。例如,滞后 3 的部分自相关指的是滞后 1 和滞后 2 无法解释的相关性。换句话说,部分自相关指的是某个滞后对当前时间值的直接影响。
在进入 Python 代码之前,需要强调的是,如果序列是平稳的,自相关系数会更为明显,因此通常最好先对序列进行差分以稳定信号。
话虽如此,下面是绘制不同时间段 PACF 的代码:
from statsmodels.graphics.tsaplots import plot_pacf
actual = df['PJME_MW']
hours = range(0, 24, 4)
for hour in hours:
plot_pacf(actual[actual.index.hour == hour].diff().dropna(), lags=30, alpha=0.01)
plt.title(f'PACF - h = {hour}')
plt.ylabel('Correlation')
plt.xlabel('Lags')
plt.show()

6.1 PJME 滞后分析 — 部分自相关函数(h=0)。

6.2 PJME 滞后分析 — 部分自相关函数(h=4)。

6.3 PJME 滞后分析 — 部分自相关函数(h=8)。

6.4 PJME 滞后分析 — 部分自相关函数(h=12)。

6.5 PJME 滞后分析 — 部分自相关函数(h=16)。

6.6 PJME 滞后分析 — 部分自相关函数(h=20)。
如你所见,PACF 实际上就是绘制不同滞后的 Pearson 部分自相关系数。当然,未滞后的序列与自身有完美的自相关,因此滞后 0 的自相关系数永远是 1。蓝色区域表示置信区间:如果某个滞后超过了这个区间,则说明该滞后在统计上是显著的,我们可以断言它具有重要性。
6.1 滞后分析 — 特征工程
滞后分析是时间序列特征工程中最具影响力的研究之一。如前所述,具有高相关性的滞后是序列中重要的滞后,因此应予以考虑。
一种广泛使用的特征工程技术是对数据集进行按小时划分。即将数据划分为 24 个子集,每个子集对应一天中的一个小时。这种做法有助于平滑信号,使其更加规律,从而更易于预测。
每个子集应进行特征工程,训练并进行微调。最终的预测将通过结合这 24 个模型的结果来实现。话虽如此,每个小时的模型都会有其特殊性,其中大部分将涉及重要的滞后。
在继续之前,让我们定义在进行滞后分析时可以处理的两种滞后类型:
-
自回归滞后:接近滞后 0 的滞后,我们预期其值较高(近期的滞后更可能预测当前值)。它们是序列中趋势的体现。
-
季节性滞后:指与季节性周期相关的滞后。当按小时划分数据时,它们通常代表每周的季节性。
请注意,自回归滞后 1 也可以被视为系列的日常季节性滞后。
现在让我们讨论一下上面打印的 PACF 图。
夜间小时
夜间小时(0, 4)的消费更多依赖于自回归滞后而非周滞后,因为最重要的滞后都集中在前五个。像 7、14、21、28 这样的季节性周期似乎并不太重要,这提醒我们在进行特征工程时要特别关注滞后 1 到 5。
日间小时
白天小时(8, 12, 16, 20)的消费展现出自回归滞后和季节性滞后两者的特征。特别是对于 8 点和 12 点的小时——这时消费量特别高——而季节性滞后在接近夜间时变得不那么重要。对于这些子集,我们也应该包括季节性滞后以及自回归滞后。
最后,这里有一些关于特征工程滞后的建议:
-
不要考虑过多的滞后,因为这可能导致过拟合。通常,自回归滞后从 1 到 7,而周滞后应该是 7、14、21 和 28。然而,是否将它们作为特征并不是强制性的。
-
考虑非自回归或非季节性的滞后通常是一个坏主意,因为它们也可能导致过拟合。相反,尝试理解某个滞后为什么重要。
-
转换滞后通常会导致更强大的特征。例如,季节性滞后可以通过加权均值进行聚合,创建一个单一特征来表示序列的季节性。
免费资源
最后,我想提到一本非常有用(且免费的)解释时间序列的书,我个人使用过很多次:Forecasting: Principles and Practice。
尽管这本教材旨在使用 R 而非 Python,但它为预测方法提供了很好的介绍,涵盖了时间序列分析的最重要方面。
结论
本文的目的是提供一个全面的时间序列预测探索性数据分析模板。
EDA(探索性数据分析)是任何数据科学研究中的基础步骤,因为它有助于理解数据的性质和特点,并为特征工程奠定基础,特征工程反过来可以显著提升模型的性能。
我们接着描述了一些最常用的时间序列 EDA 分析方法,这些方法既可以是统计/数学性的,也可以是图形化的。显然,本工作的目的是提供一个实践框架供大家入手,后续的研究需要根据所研究的历史序列类型和业务背景进行进一步的探索。
感谢你一直跟随我直到最后。
除非另有说明,所有图片均来自作者。
时间序列预测:超越基础
解锁时间序列预测在现实世界中的真正潜力的技巧和窍门
·发表于 Towards Data Science ·8 分钟阅读·2024 年 3 月 9 日
--

图片来源:Alex Chumak 在 Unsplash
预测未来的能力是一种超能力!
幸运的是,时间序列预测存在,并且任何人都可以使用!这种超能力可以使任何人做出前瞻性决策,改善未来规划。时间序列预测的应用几乎是无限的;想象一下能够预测你公司产品的需求,做出更明智的投资财务决策,或者仅仅是能够预测天气。
虽然互联网上充斥着关于预测核心原则的资源,但现实世界的复杂性往往超出了教科书的内容。本指南深入探讨了我在多年将这一强大工具应用于商业环境中的发现和未言明的秘密。我们将探索一些策略,超越通常关注特征工程和模型选择的范畴,帮助你真正解锁时间序列预测的潜力。
1. 目的、影响、努力(PIE)框架
2. 领域知识
3. 非平稳性和突变
4. 利益相关者管理

图片来源:rc.xyz NFT gallery 在 Unsplash
1. 目的、影响、努力(PIE)框架
在任何数据科学项目中,第一步始终是查看与工作相关的目标、影响和投入。在进入复杂的数据分析和建模世界之前,重要的是停下来评估基本问题:这个项目的目的是什么?它最终会带来什么影响?值得付出这份努力吗?
数据科学项目往往需要大量的时间和资源。因此,你必须不断问自己一个关键问题:“通过利用数据科学,组织能获得多少附加价值?”
令人惊讶的是,这个问题的答案并非总是显而易见。有时候,选择一个“快捷而粗糙”的解决方案——符合帕累托法则,即 20%的努力产生 80%的结果——就能解决大部分日常问题。那么,如果一个更简单的解决方案就能满足需求,为什么要花费额外的时间和精力呢?
这时,PIE 框架就派上了用场。在令人兴奋的数据科学技术背后,有一个重要的决策:使用更高级技术的边际收益是否真的值得额外的成本和努力?在大多数情况下,答案是响亮的“不是”。
尽管它看起来可能很令人兴奋,但在直接进入数据科学项目之前,重要的是要进行批判性思考。通过遵循 PIE 框架,你可以自信地决定要承担哪些项目,确保你的努力既有意义又富有影响力。

PIE 框架中的饼图。
2. 领域知识
许多课程和教材通常专注于教授各种时间序列预测技术,往往将它们呈现为成功的关键。然而,正如我的实际经验所教导的那样,真正的“秘密武器”并不在于模型本身,而是深入理解你所处理的数据。没有一种放之四海而皆准的预测模型。
例如,想象一下,如果你试图在松散的沙地上建造梦想中的房子。你可以找到最好的建筑师和施工队,设计出最复杂的蓝图,但最终,这座房子注定会倒塌。同样地,如果在无关数据或者充满错误和不一致的数据上构建强大的预测模型,就像是在不稳定的地基上建房子。模型的输出将是不可靠的,容易崩溃。

在时间序列预测的背景下,领域知识不仅仅是了解数据所代表的内容。它还涉及深入挖掘并理解与问题分析相关的潜在关系、机制和细节。每个数据集都是独一无二的,理解数据的过程会因每个项目的不同而有所变化。
尽管这一步看起来可能相当无聊,但它可能决定整个项目的成败。从指导特征工程决策到引导模型选择,领域知识在塑造项目过程中起着非常重要的作用。如果你能够找到并利用最好的数据来输入模型,那么你的输出将更加有用。
3. 非平稳性和突变
当你第一次学习时间序列预测时,你通常会在许多预测模型中遇到“平稳性”这一核心假设。然而,在现实世界中,时间序列数据很少符合这一假设。相反,你会发现数据和交互随着时间变化。这通常表现为波动、趋势和季节性。在构建可靠的预测模型时,识别并适应这些动态模式,以准确捕捉和预测潜在的趋势和行为是非常重要的。
另一个常见的问题是当数据中发生突变或不连续时。传统的预测方法对于这种突变的处理并不好,但考虑到这些突变是非常重要的。这些突变可能是由外部因素引起的,例如市场条件变化、消费者行为转变,或新法规或新技术的引入。它们可能并不总是可以直接衡量或获得,但在这一点上,你的专业知识将发挥作用,帮助你找到最佳方法来考虑这些细微差别。
例如,假设你正在尝试预测英格兰超级联赛足球的观众数量。除了明显的赛季变化外,还有一些其他的特点,比如赛季末期观众兴趣会更高,因为竞争更加激烈,观众变得更加关注。现在假设梅西转会到英超,或者天空体育提高了他们的月度订阅费——这些突变有时是不可预测的,但它们对实际观众数量的影响非常显著。
处理这些突变没有简单的方法,但有一些技术可以提供帮助。以下是我通常用来应对这些细微差别的一些方法,但它们的应用高度依赖于数据的特性;
1. 时间序列分解 — 这包括将数据分解为其核心组成部分——如趋势、季节性和噪声。通常,这一步应该作为预分析的一部分来完成,以更好地理解数据并单独建模每个组成部分的行为。
外生特征 — 这些是你认为影响数据短期波动或长期趋势的外部变量。这可以是手动标记,或其他作为回归变量使用的重要特征。领域知识在这里至关重要。每一次突变通常都有解释。并非总能将每个解释作为特征纳入,所以过滤出最有用、可靠且可预测的特征非常重要,以避免过拟合。
自适应建模 — 这涉及到选择最合适的模型,以便能够检测和适应潜在模式的变化。这一步是我最喜欢的之一,因为它涉及到通过研究找到或创建最适合数据的模型。然而,说起来容易,做起来难。可能会有一些时候,这需要一些手动工作,例如超参数调优,或者使用某些考虑到数据细微差异的特定模型。请记住,这也可能增加过拟合的风险。
尽管这些技术无法完美预测每一个前所未见的效应,但它们确实让我们为这些效应做好更多准备,并构建出更强大的预测模型。
4. 利益相关者管理
成功实施模型远远超出了模型选择和数据分析的技术复杂性——它还包括有效的利益相关者管理。将“利益相关者管理”看作是根据参与人员定制过程。目标是确保你的模型正在做它应该做的事情,同时你继续创造价值。如何定制过程和输出取决于利益相关者的需求,无论是高管、经理还是消费者。这一切都关乎提供可操作的洞察,以帮助他们做出明智的决策。
作为一名数据科学家,你的价值通常取决于你将技术洞察转化为实际决策的能力。这对我们大多数人来说既是福也是祸——软技能在技术领域中常常被低估,但它是连接我们艰苦工作和从中获得价值的桥梁。虽然技术技能为我们的工作奠定了基础,但你有效沟通、与利益相关者共情并推动行动的能力,才是真正提升我们影响力的关键。沟通是数据科学家最重要的技能,然而它也是被认为最难掌握的技能。
以下是一些能够帮助有效沟通的实用建议:
1. 定制化模型选择
选择正确的模型远远超越了识别最精确的算法。
的确,黑箱模型可能对我们数据科学家来说是最准确和最具吸引力的,但它们通常不透明或难以解释,这使得利益相关者很难信任结果。在这种情况下,选择像 ARIMA 或指数平滑这样简单的模型可能更好,虽然可能会降低准确性,但能提高可解释性。最好还是对现有的各种模型进行研究。
这是一些最常见的预测模型及其可解释性的示例;

2. 清晰的可视化
以与受众产生共鸣的方式呈现你的结果。
考虑设计良好的图表、图形和仪表盘。这涉及到清晰、简洁和与受众的相关性。你如何帮助利益相关者快速理解关键见解并做出明智决策?
网上有许多关于数据可视化的文章和课程!
高层管理人员可能偏好高层次的仪表盘,而经理可能需要包含误差度量的详细报告。消费者通常从清晰且可操作的可视化中受益,将预测转化为实际应用。
就个人而言,我从 Prophet 的基本可视化中汲取了很多灵感,因为它清楚地展示了实际值与模型预测值的对比。我通常会添加注释或突出某些点(包括未来和过去的关键时刻)。

Prophet 输出的示例。
3. 讲故事
不仅要呈现数据;还要讲一个故事。 突出预测的背景、关键发现和潜在影响。了解你的受众,并将叙述框架调整为与特定目标和挑战相关的方式。
在“如何做”之前,先关注“为什么做”
-
你试图通过预测实现的总体目标是什么?
-
这些预测将如何用于支持决策?
-
不准确的预测可能带来什么潜在后果?
-
承认模型预测中的局限性和不确定性。
突出过程,而不仅仅是结果
-
始终将过程与当前任务联系起来。
-
分享你在数据预处理或模型选择过程中遇到的主要挑战,以及你是如何克服这些挑战的。
-
突出你在过程中发现的任何意外见解。
使其具有可关联性和可操作性
-
讲述你的受众能够理解的语言。
-
量化你的预测在收入、成本节省或资源分配等指标上的潜在影响。
-
提供清晰的建议和基于你的见解,利益相关者可以采取的可操作步骤。
记住,成功预测的关键不仅仅是模型的准确性,更在于其能赋能利益相关者做出明智决策。时间序列预测是一个强大的工具,所以要明智且小心地使用它!
除非另有说明,所有图片均为作者提供。
生成型人工智能时代的时间序列预测:让梯度提升像大型语言模型一样工作
使用标准机器学习模型进行零样本预测
·发布于 Towards Data Science ·6 分钟阅读·2024 年 7 月 4 日
--

图片来源:David Menidrey 在 Unsplash
生成型人工智能和大型语言模型(LLMs)的兴起吸引了全球的关注,引发了各个领域的革命。虽然这种技术的主要焦点一直放在文本序列上,但现在更多的关注开始转向扩展其能力,处理和处理除文本输入之外的其他数据格式。
和大多数人工智能领域一样,时间序列预测也没有免受大型语言模型(LLMs)出现的影响,但这可能对所有人来说都是一件好事。时间序列建模被认为更像是一门艺术,其中结果在很大程度上依赖于先前的领域知识和适当的调优。相反,LLMs 因其任务无关性而受到赞赏,拥有巨大的潜力,能够运用其知识解决来自不同领域的多样化任务。通过这两个领域的结合,新的时间序列预测模型的前沿可能会诞生,未来能够实现以前不可思议的结果。

[由作者提供的图片]
使用 TensorFlow 进行时间序列预测和通过可视化技术进行验证期外预测
如何将预测扩展到验证期之外
·发表于Towards Data Science ·阅读时长 8 分钟·2024 年 2 月 24 日
--

作者提供的图片
在本文中,我将带领你通过使用 TensorFlow 构建时间序列模型的过程。TensorFlow 是一个强大的框架,用于构建和训练神经网络。我将展示多种用于时间序列预测的神经网络架构,从简单的模型如 SimpleRNN 到更复杂的模型如 LSTM。此外,我还将介绍我用来进行验证期外预测的高级可视化技术。
设置环境
我使用了以下库:TensorFlow 与 Keras 用于构建神经网络,Matplotlib 用于可视化,NumPy 用于数值计算,Scikit-Learn 用于数据预处理。
import numpy as np
import tensorflow as tf
from matplotlib import pyplot as plt
from sklearn.preprocessing import MinMaxScaler
数据准备
数据准备对任何机器学习模型的成功至关重要。在这一部分,我将执行几个步骤,为训练和验证准备数据。
分离数据和时间步
第一步是将时间步从实际数据中分离出来。
对于短时间序列数据(存储在数组中的数据):我们可以使用‘np.arange()’创建一个时间步数组:
#For short time series data, data stored in an array, I'll do the following:
dummy_data = np.array([1, 2, 3,...])
time_step = np.arange(len(dummy_data))
对于存储在文件中的大型数据集(例如 CSV 文件):我们可以从文件中读取数据及其对应的时间步:
#For larger datasets stored in files, such as CSV files
import csv
time_step = []
data = []
with open("file.txt", "r", encoding="utf-8") as f:
csv_reader = csv.reader(f, delimiter=",")
# Skip the header
next(csv_reader)
# Skip lines with NUL characters
lines = (line for line in csv_reader if "\0" not in line)
# Iterate through non-null lines
for line in lines:
# Assuming the first column is the date and the second column is the number
time_step.append(datetime.strptime(line[0], "%Y-%m-%d"))
data.append(float(line[1]))
将数据划分为训练集和验证集
在获取时间步和数据后,我们将其划分为训练集和验证集,以训练和评估模型的性能。
# Determine the split point between training and validation data
split_time =
# Split time steps into training and validation sets
time_train = time_step[:split_time]
time_valid = time_step[split_time:]
# Split data into training and validation sets
x_train = dummy_data[:split_time]
x_valid = dummy_data[split_time:]
# Use Min-Max scaling
# Initialize MinMaxScaler
scaler = MinMaxScaler()
# Reshape and scale the training data
x_train_scaled = scaler.fit_transform(np.array(x_train).reshape(-1, 1)).flatten()
# Scale the validation data using the same scaler
x_valid_scaled = scaler.transform(np.array(x_valid).reshape(-1, 1)).flatten()
生成窗口数据集
之后,我创建了一个函数,用于生成用于训练和验证的窗口化数据集。每个窗口由固定数量的数据点组成。例如,使用窗口大小为 4 时,我会使用每个窗口中的最后四个数据点来预测下一个数据点。
窗口化的数据集生成了两维的数据批次(批次大小和时间戳)。
def windowed_dataset(series, window_size):
# Create a TensorFlow dataset from the input series
dataset = tf.data.Dataset.from_tensor_slices(series)
# Window the dataset into fixed-size windows with a specified window_size,
# shifting the window by 1 at each step, and drop any remaining data that
# doesn't fit into a complete window
dataset = dataset.window(window_size, shift=1, drop_remainder=True)
# Flatten the dataset of windows into individual windows and batch them
dataset = dataset.flat_map(lambda window: window.batch(window_size))
# Map each window to a tuple where the first element contains all but the last
# element of the window and the second element contains the last element of the window
dataset = dataset.map(lambda window: (window[:-1], window[-1]))
# Batch the dataset with a batch size of 1 and prefetch it for improved performance
return dataset.batch(1).prefetch(1)
# Window size
window_size =
# Create windowed dataset for training
dataset_train = windowed_dataset(x_train_scaled, window_size)
# Create windowed dataset for validation
dataset_valid = windowed_dataset(x_valid_scaled, window_size)
在遵循这些步骤后,数据已被适当准备好以便导入模型中。
定义模型
Keras 提供了许多可以用于时间序列预测的模型。我将简要描述我使用过的一些模型,从较简单的结构开始,逐渐增加复杂性。值得注意的是,这些结构仅仅是示例,单元数量和层数需要根据所使用的数据集进行微调。
简单的递归神经网络
RNN 是用于处理数据序列的神经网络,能够保留来自早期时间步的信息。然而,它们可能无法记住长序列中的信息。
在每个时间步,不同批次的输入数据被送入 RNN 单元。每个时间步的 RNN 单元输出不仅依赖于当前的输入批次,还依赖于单元的前一个状态,而前一个状态捕捉了早期时间步的信息。

图片由作者提供
这是一个简单的 RNN 模型示例,包含两个递归层和一个最终输出层。在使用 RNN 时,输入数据需要进行调整,因为 RNN 通常期望输入数据为 3D 张量。[1]
model = tf.keras.models.Sequential([
tf.keras.layers.Lambda(lambda x: tf.expand_dims(x, axis=-1),
input_shape=[window_size]),
tf.keras.layers.SimpleRNN(units, return_sequences=True),
tf.keras.layers.SimpleRNN(units),
tf.keras.layers.Dense(1)
])
长短期记忆网络
LSTM 网络是一种递归神经网络,以能够在多个时间步内保留信息而闻名。LSTM 通过结合一个记忆单元,使信息能够从一个单元传递到另一个单元,并在网络内从一个时间步传递到另一个时间步来实现这一点。

图片由作者提供
此外,LSTM 可以是双向的,允许它们不仅分析输入数据的正向方向,还能分析反向方向。
这里是一个包含两个双向 LSTM 层的 LSTM 模型示例。根据我的经验,在 LSTM 中使用多个层通常比单层模型表现更好,因为它能够同时捕捉低层次和高层次的特征。[2]
model = tf.keras.models.Sequential([
tf.keras.layers.Lambda(lambda x: tf.expand_dims(x, axis=-1),input_shape=[None]),
tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(units, return_sequences=True)),
tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(units)),
tf.keras.layers.Dense(1)
])
调整学习率
这些架构需要根据每个数据集进行调整。然而,适合我的方法是先调整学习率,然后再尝试其他层数和单元数的组合。
请查看这篇文章中的代码,了解如何调整学习率和层数:通过调整学习率和隐藏层单元选择优化神经网络性能。
重要提示:我希望很久以前就知道的一个非常有用的建议:除了实验不同的层数和单元数外,我建议你在训练模型时始终调整权重。有关更多详细信息和代码,请查看我名为Achieving Reproducibility in Neural Network Predictions的文章。
在验证数据集上评估模型
为了能够可视化模型的性能和预测结果,让我们编译并训练该模型。
# Compile the model
model.compile(loss="mse", optimizer=tf.keras.optimizers.SGD(),metrics=["mae"])
# Train the model
history = model.fit(dataset_train, epochs=, validation_data=dataset_valid)
# Evaluate the model
evaluation_result = model.evaluate(dataset_valid)
print("Validation Loss:", evaluation_result)
在时间序列预测中,通常使用均方误差(MSE)或平均绝对误差(MAE)来验证模型的性能。与 MSE 不同,MAE 不对误差进行平方处理,而是使用误差的绝对值。这种方法不会对较大的误差进行过度惩罚,因此适用于那些需要平等对待所有误差的场景。
可视化训练和验证损失
现在让我们创建两个图表,展示训练和验证数据的损失(Loss)和平均绝对误差(MAE)曲线。
# Plot training and validation loss over epochs
plt.figure(figsize=(10, 6))
# Plot training loss
plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='Training Loss', color='blue')
plt.plot(history.history['val_loss'], label='Validation Loss', color='red')
plt.title('Training and Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
# Plot training and validation MAE over epochs
plt.subplot(1, 2, 2)
plt.plot(history.history['mae'], label='Training MAE', color='blue')
plt.plot(history.history['val_mae'], label='Validation MAE', color='red')
plt.title('Training and Validation MAE')
plt.xlabel('Epoch')
plt.ylabel('MAE')
plt.legend()
plt.tight_layout()
plt.show()
这是可以得到的图表示例:

图片由作者提供
在验证数据集上进行预测
现在我已经训练了模型,并使用 MSE 指标验证了其性能,是时候将模型应用于验证数据集进行预测了。
以下是对验证数据集进行预测的代码。此外,我还包括了如何打印验证期之外的预测结果,并将其与实际数据在每个时间步骤进行对比。
# Predict on the validation dataset
num_predictions_beyond_validation = 2
validation_predictions = []
# Use the last window_size from the training set for predictions
current_window = x_train_scaled[-window_size:]
# Adjust time steps for validation predictions
validation_time_steps = np.arange(len(x_valid_scaled))
for time in range(len(validation_time_steps) + num_predictions_beyond_validation):
# Predict the next values using the model on the validation dataset
predicted_value_scaled = model.predict(np.array(current_window[-window_size:]).reshape(1, -1))[0]
# Append the predicted value to the list of predictions
validation_predictions.append(predicted_value_scaled)
# Print the actual and predicted values during validation along with the time step
if time < len(x_valid_scaled):
actual_value_scaled = x_valid_scaled[time]
# Denormalize the actual value
actual_value_denormalized = scaler.inverse_transform(np.array(actual_value_scaled).reshape(1, -1)).flatten()
# Denormalize the predicted value
predicted_value_denormalized = scaler.inverse_transform(np.array(predicted_value_scaled).reshape(1, -1)).flatten()
print(f'Time: {time_valid[time]}, Actual: {actual_value_denormalized}, Predicted: {predicted_value_denormalized}')
# Update the current window for the next iteration using the true value from the validation set
if time < len(x_valid_scaled):
current_window = np.append(current_window, x_valid_scaled[time])[1:]
else:
# Print the predicted value beyond validation along with the time step
# Denormalize the predicted value beyond validation
predicted_value_denormalized = scaler.inverse_transform(np.array(predicted_value_scaled).reshape(1, -1)).flatten()
print(f'Time: {time_valid[-1] + time - len(x_valid_scaled) + 1}, Predicted (Beyond Validation): {predicted_value_denormalized}')
current_window = np.append(current_window, predicted_value_scaled)[-window_size:]
你还可以在图表中可视化验证期之外的预测结果。
# Plot the original data, training data, validation data, and predictions
plt.figure(figsize=(10, 6))
# Plot original data in blue
plt.plot(time_step, dummy_data, label='Original Data', marker='o', linestyle='-', color='black')
# Plot training data in green
plt.plot(time_step[:split_time], dummy_data[:split_time], label='Training Data', color='blue')
# Plot validation data in orange
plt.plot(time_valid, dummy_data[split_time:], label='Validation Data', color='red')
# Denormalize validation predictions
validation_predictions_denormalized = scaler.inverse_transform(np.array(validation_predictions).reshape(-1, 1)).flatten()
# Plot predictions (Validation)
plt.plot(time_valid, validation_predictions_denormalized[:len(time_valid)], label='Predictions (Validation)', color='orange')
# Highlight the last few predictions beyond the validation set in red
last_predictions_beyond_validation_denormalized = scaler.inverse_transform(np.array(validation_predictions[-num_predictions_beyond_validation:]).reshape(-1, 1)).flatten()
time_last_predictions_beyond_validation = np.arange(split_time + len(time_valid), split_time + len(time_valid) + num_predictions_beyond_validation)
plt.scatter(time_last_predictions_beyond_validation, last_predictions_beyond_validation_denormalized, color='red', marker='X', label='Last Predictions Beyond Validation')
plt.legend()
plt.title('Original Data, Training Data, Validation Data, and Predictions')
plt.xlabel('Time Step')
plt.ylabel('Values')
plt.show()
这是一个基于虚拟数据集的示例,展示了上面代码生成的图像。

图片由作者提供
结论
可视化时间序列数据和模型预测是理解模型性能的基础。本文中,我解决了一个我曾多次遇到的问题:如何在验证期之外进行时间序列预测并可视化,这在事后检查模型在实际数据上的表现时可能很有用。
参考文献
[1]SimpleRNN:www.tensorflow.org/api_docs/python/tf/keras/layers/SimpleRNN#call_arguments
[2]LSTM:www.tensorflow.org/api_docs/python/tf/keras/layers/LSTM
使用 TiDE 进行时间序列预测
探索 TiDE 的架构,并在 Python 中应用于预测项目
·发布于 Towards Data Science ·阅读时间:8 分钟·2024 年 1 月 9 日
--

图片由 Boris Smokrovic 提供,来源于 Unsplash
在我们对时间序列预测领域最新进展的探索中,我们发现了 N-HiTS、PatchTST、TimeGPT 和 TSMixer。
尽管已有许多努力将 Transformer 架构应用于预测任务,但考虑到计算要求,实际表现只是中等水平。
实际上,简单的线性模型在许多基准数据集上已经证明优于复杂的基于 Transformer 的模型(参见 Zheng et al., 2022)。
受此启发,2023 年 4 月,谷歌的研究人员提出了 TiDE:一种基于多层感知机(MLP)的编码器-解码器架构的长期预测模型。
在他们的论文 《使用 TiDE 进行长期预测:时间序列密集编码器》中,作者展示了该模型在多个数据集上的表现优于其他基于 Transformer 和 MLP 的模型,如 PatchTST 和 N-HiTS。
在本文中,我们首先探讨 TiDE 的架构和内部运作。然后,我们在 Python 中应用该模型,并将其用于我们自己的小型预测实验。
时间序列 — 从分析过去到预测未来
如何通过时间序列从过去学习。
·发表于Towards Data Science ·阅读时间 19 分钟·2024 年 10 月 23 日
--

图片由DAVIDCOHEN提供,来源于Unsplash
如果你有兴趣对未来的某个结果进行预测,时间序列就是实现这一目标的工具。时间序列数据是一组在特定时间间隔内记录的观测值,例如每小时的天气报告,或每日的库存和销售报告。时间序列为我们提供了理解历史数据的工具,并利用这些数据更有把握地预测未来。例如,我个人非常感激所有通过研究时间序列创建天气预报应用的科学家,以及像亚马逊这样的公司,它们依赖销售预测来更好地安排库存,以便我能够尽早收到我的亚马逊订单!
虽然网上有许多关于时间序列的教程,但我仍然决定写这篇文章,因为以下几点原因:
-
建立基础: 我们不会假设读者对时间序列已有了解,而是将深入介绍与时间序列相关的基础概念,以便更好地理解这一主题并跟上内容。
-
扩展深度: 一旦读者熟悉了理解时间序列所需的基础知识,我们将介绍 10 种不同的时间序列预测方法…
使用 Transformer 进行时间序列预测
《Pytorch 中的 Transformer 完整指南》
·发表于Towards Data Science ·17 分钟阅读·2024 年 1 月 16 日
--
自从Feynman 学习法的出现以来,大型语言模型(LLMs)已掀起了巨大的热潮,甚至连 AI 领域以外的人也有所耳闻。尽管需要理解的是,LLMs 本质上是“仅仅”是序列预测模型,并不具备任何形式的智能或推理——但它们取得的成果无疑是非常令人印象深刻的,甚至有些人谈论这是“AI 革命”的另一步。
LLMs 成功的关键在于其核心构建模块——Transformer。在本文中,我们将提供在 Pytorch 中使用 Transformer 的完整指南,特别关注时间序列预测。感谢您的到来,希望您能享受这段旅程!

有人可能会认为,所有通过变压器(transformers)解决的问题本质上都是时间序列问题。虽然这确实是事实,但在这里我们将特别关注连续系列和数据——例如预测疾病传播或天气预报。与自然语言处理(NLP)的主要应用不同(简单来说,如果这个词在此语境下是允许的——开发一个像 ChatGPT 这样的模型并使其自然运行确实需要大量进一步的优化步骤和技巧),主要区别在于输入空间是连续的,而 NLP 使用的是离散的词元(tokens)。然而,除了……
时间序列回归与交叉验证:一种简洁的方法
使用 tidymodels、modeltime 和 timetk 进行 EDA、特征工程、交叉验证和模型比较的逐步指南
·发表于Towards Data Science ·阅读时间:8 分钟·2024 年 6 月 12 日
--

图片由Aron Visuals提供,来源于Unsplash
时间序列预测方法一直在不断发展,虽然 ARIMA 一直是基础模型,但机器学习模型也展现了巨大的潜力。各行业中有许多场景可以从更加准确的时间数据建模中获益。在本文中,我将处理一个这样的场景——销售预测。让我们直接开始吧,节省时间(明白了吗?)。
代码
本文中重现所有内容的代码可以在我的GitHub 仓库找到。
数据集
对于本次练习,我使用了由Samuel Cortinhas 提供的时间序列练习数据集,该数据集在 Kaggle 上以CC0:公共领域许可发布。此数据集包含模拟的时间序列数据,覆盖了 10 年(2010–2019),并包括日期、商店 ID、产品 ID 和销售额等特征。为了进行此次分析,我选择了一个商店和一个产品,以便集中关注回归分析的各个组件。
时间序列分析
步骤 0:设置
TimeGPT vs TiDE:零样本推理是预测的未来还是仅仅是噱头?
基础模型:时间序列预测中 TimeGPT 与 TiDE 的全面比较
·发表于《Towards Data Science》 ·阅读时间 12 分钟·2024 年 3 月 14 日
--
本文由 Rafael Guedes 与我共同撰写。
介绍
预测是人工智能(AI)在学术研究和工业应用中的核心领域之一。事实上,它可能是我们在各行各业中能找到的最普遍的挑战之一。准确预测未来的销售量和市场趋势对企业优化规划过程至关重要。这包括提升贡献利润、减少浪费、确保适当的库存水平、优化供应链,并总体提升决策能力。
开发一个预测模型是一个复杂且多方面的挑战。它需要对最先进(SOTA)预测方法和应用到的具体业务领域有深刻的理解。此外,预测引擎将作为组织内部的关键基础设施,支持各部门之间的广泛流程。例如:
-
营销团队利用该模型为即将到来的期间(如下个月或下个季度)的投资分配等战略决策提供信息。
-
采购团队利用该模型做出有关……的明智决策。
TimeMixer:探索时间序列预测中的最新模型
发现并理解 TimeMixer 的内部工作原理,并在自己的预测项目中应用它,使用 Python。
·发布于Towards Data Science ·11 分钟阅读·2024 年 7 月 23 日
--

图片来源:sutirta budiman 通过Unsplash
时间序列预测领域正在快速发展,许多模型不断被提出,并宣称具有最先进的性能。
深度学习模型如今已成为时间序列预测的常用方法,尤其是在处理具有多个特征的大型数据集时。
尽管近年来已经提出了许多模型,如iTransformer、SOFTS和TimesNet,但它们在其他基准测试中的表现往往不及像NHITS、PatchTST和TSMixer等模型。
2024 年 5 月,提出了一种新模型:TimeMixer。根据原始论文TimeMixer: 可分解的多尺度混合用于时间序列预测,该模型在基于 MLP 的架构中,结合特征混合和序列分解来进行预测。
在本文中,我们首先探索 TimeMixer 的内部工作原理,然后在短期和长期预测任务中运行我们自己的小型基准测试。
和往常一样,务必阅读原始研究文章以获取更多细节。
学习最新的时间序列…
TimesFM:谷歌的时间序列预测基础模型
时间序列的新时代
·发表于 Towards Data Science ·阅读时间 9 分钟·2024 年 2 月 28 日
--

作者使用 DALLE*3 创作。
加入 AI Horizon Forecast,这是一个将复杂的 AI 话题讲解得清晰明了的博客。
谷歌刚刚加入了时间序列预测基础模型的竞赛。
2023 年 8 月,时间序列社区因 TimeGPT 的发布而受到震动,这是 Nixtla 公司推出的第一个用于时间序列预测的基础模型。
紧随其后的是 TimeGPT,多个基础预报模型相继发布,但其中有一个脱颖而出。最近,谷歌发布了TimesFM[1],这是一个开创性的时间序列模型,取得了惊人的成果。
时间序列无处不在,广泛应用于零售、能源需求、经济学、医疗健康等多个领域。基础的时间序列模型可以高效地应用于任何时间序列案例,且准确性极高,就像 GPT-4 在文本处理中的应用一样。
在本文中,我们讨论:
-
与自然语言处理(NLP)相比,时间序列中的基础模型面临的挑战。
-
TimesFM 如何克服这些挑战。
-
TimesFM 如何运作及其为何是一个强大的模型。
-
TimesFM 基准结果。
-
时间序列预测中基础模型的未来前景。
TimesFM:基础模型在时间序列预测中的兴起
探索谷歌最新的人工智能模型如何利用超过 3070 亿个数据点实现零样本预测精度
·发表于Towards Data Science ·阅读时长 12 分钟·2024 年 9 月 20 日
--
本文由 Rafael Guedes 共同撰写。
引言
预测是各行业中最重要的应用之一。一个例子是零售行业。多个规划活动需要预测能力,这有助于优化利润率,例如财务、生产或人力资源规划。这可以影响库存管理,比如浪费、剩余物品或缺货情况、客户服务水平以及整体决策。
开发一个准确的预测模型以支持上述过程,需要对最先进(SOTA)的预测方法有深入的理解。同时,还需要了解特定的业务领域知识,这些方法会被应用于其中。这两个因素推动了预训练模型越来越受到关注——它们减少了高度定制设置的需求。将这一动机与大规模预训练模型在自然语言处理(NLP)领域的成功结合,也就是大型语言模型(LLMs),我们就得到了一个有着许多贡献者的研究路径。
从理论上讲,我们知道语言任务和时间序列任务之间有一些相似性,例如数据是顺序的。另一方面,一个关键的区别是……
TinyLlama——强大的小型语言模型的有希望的未来
了解 TinyLlama,一个小型语言模型,能够在较少的计算资源下完成多种复杂任务
·发表于Towards Data Science ·10 分钟阅读·2024 年 4 月 20 日
--
TinyLlama 是一个开源项目,旨在训练一个约为 11 亿参数的小型语言模型。该项目的目标是创建一个能够完成类似 Llama 2 这类完整 LLM 所能实现任务的语言模型,但占用更少的内存。本文将讨论如何在本地计算机上实现并运行 TinyLlama 模型。此外,还将讨论 TinyLlama 当前的性能,以及其优缺点。

ChatGPT 对 TinyLlama 模型的构想。OpenAI。(2024 年)。ChatGPT(4)[大型语言模型]。chat.openai.com
目录
· 目录
· 动机
· 在本地实现模型
· 测试模型
∘ 斐波那契数列
∘ RAG
∘ 生成对话
∘ 使用 TinyLlama 编程
· 我对该模型的看法
· 结论
动机
我撰写这篇文章的动机是跟上机器学习的最新趋势。虽然 TinyLlama 发布已有一段时间……
Tiny Time Mixers (TTM):IBM 推出的强大零-shot 预测模型
一个新的轻量级开源基础模型
·发表于 Towards Data Science ·阅读时间 10 分钟·2024 年 6 月 8 日
--

作者使用 DALLE*3 创建
如果你跟随 LLM(大语言模型)领域的最新研究,你会注意到两种主要的研究方法:
首先,研究人员专注于构建尽可能大的模型。在下一个词预测任务上进行预训练对于提高性能至关重要(也是花费数百万美元的地方!)。
其次,研究人员使用像量化这样的技术来创建更小、更快的模型——同时保持强大的通用性能。
然而,当较小的模型在某些任务上超过了更大的模型时,事情变得非常有趣。例如,Llama 3–8B 在 MMLU 任务中超过了更大的 Llama 2–70B!
Tiny Time Mixers (TTM)[1], 由IBM推出,采用了第二种方法。它是一个轻量级模型,超越了更大的 SOTA 模型——包括 MOIRAI,在 M4 数据集上的表现尤为突出。此外,它是开源的!
本文讨论了:
-
TTM的架构和功能。
-
使TTM与众不同的创新特性。
-
TTM与其他模型的基准测试结果。
让我们开始吧!
我推出了AI Horizon Forecast, 一个...
在检索增强生成(RAG)中正确处理生成部分的技巧

图像由作者使用 Dall-E 3 创建
用于评估和比较 GPT-4、Claude 2.1 和 Claude 3.0 Opus 的实验结果
·发表于 Towards Data Science ·6 分钟阅读·2024 年 4 月 6 日
--
感谢 Evan Jolley 对本文的贡献
RAG 系统的新评估似乎每天都有发布,许多评估都集中在框架的检索阶段。然而,生成部分——模型如何合成并表达检索到的信息——在实践中可能同样,甚至更为重要。许多生产中的用例不仅仅是返回一个来自上下文的事实,还需要将这些事实综合成更复杂的回应。
我们进行了几项实验,以评估和比较 GPT-4、Claude 2.1 和 Claude 3 Opus 的生成能力。本文详细介绍了我们的研究方法、结果以及在过程中遇到的模型细节,并探讨了这些内容对构建生成式 AI 的人们有何意义。
所有重现实验结果所需的内容都可以在此 GitHub 仓库 中找到。
主要收获
-
尽管初步发现表明 Claude 在性能上超越了 GPT-4,但随后的测试表明,通过战略性的提示工程,GPT-4 在更广泛的评估中表现出了更优的性能。模型的固有行为和提示工程在 RAG 系统中至关重要。
-
仅仅在提示模板中添加“请先解释自己然后回答问题”,就显著提升了(超过 2 倍)GPT-4 的表现。显然,当大型语言模型解释自己的答案时,似乎有助于展开思路。通过解释,模型可能在嵌入/注意力空间中加强了正确的答案。
RAG 阶段及生成为何重要

图 1:作者制作的图表
虽然检索负责识别和提取最相关的信息,但生成阶段则负责将这些原始数据转化为连贯、有意义且符合上下文的响应。生成步骤的任务是综合所检索到的信息,填补空白,并以一种易于理解且与用户查询相关的方式呈现。
在许多实际应用中,RAG 系统的价值不仅在于它们能够定位特定的事实或信息,还在于它们能够将这些信息整合并在更广泛的框架中进行上下文化处理。生成阶段使得 RAG 系统能够超越简单的事实检索,提供真正智能和适应性的响应。
测试 #1:日期映射
我们进行的初步测试涉及从两个随机检索的数字中生成日期字符串:一个代表月份,另一个代表日期。模型的任务是:
-
检索随机数字 #1
-
隔离最后一位数字并加 1
-
从结果中生成我们的日期字符串中的月份
-
检索随机数字 #2
-
从随机数字 2 生成我们的日期字符串中的月份
例如,随机数字 4827143 和 17 将代表 4 月 17 日。
这些数字被置于不同深度的上下文中,且上下文长度各异。模型最初在这个任务上表现得相当困难。

图 2:初步测试结果(图片来源:作者)
虽然两个模型的表现都不算优秀,但在我们的初步测试中,Claude 2.1 显著优于 GPT-4,几乎将其成功率提高了四倍。正是在这一点上,Claude 的冗长特性——提供详细、解释性的回答——似乎给它带来了明显的优势,使得结果比 GPT-4 最初简洁的回答更为准确。
受到这些意外结果的启发,我们在实验中引入了一个新变量。我们指示 GPT-4“先解释自己然后再回答问题”,这一提示鼓励它提供更冗长的回答,类似于 Claude 的自然输出。这一小小的调整产生了深远的影响。

图 3:初步测试与目标提示结果(图片来源:作者)
GPT-4 的表现显著提升,在随后的测试中取得了完美的结果。Claude 的结果也有所改善,但幅度较小。
该实验不仅突出了语言模型在处理生成任务时的差异,还展示了提示工程对其性能的潜在影响。看似是 Claude 的优势的冗长提示,实际上也成为 GPT-4 可以复制的策略,这表明模型处理和呈现推理的方式可以显著影响其在生成任务中的准确性。总体而言,向我们的提示中加入看似微不足道的“解释一下”这一行,确实在提高所有实验中模型的表现上起到了作用。
进一步测试与结果

图 4:用于评估生成的四个进一步测试(图像由作者提供)
我们进行了四个额外的测试,以评估现有模型将检索到的信息合成并转化为不同格式的能力:
-
字符串连接:将文本片段组合成连贯的字符串,测试模型的基本文本处理能力。
-
货币格式化:将数字格式化为货币,进行四舍五入,并计算百分比变化,以评估模型的精度和处理数字数据的能力。
-
日期映射:将数字表示转换为月份名称和日期,需要结合检索和上下文理解。
-
模运算:执行复杂的数字运算,测试模型的数学生成能力。
毫不奇怪,每个模型在字符串连接测试中都表现出色,进一步确认了文本处理是语言模型的基本优势。

图 5:货币格式化测试结果(图像由作者提供)
至于货币格式化测试,Claude 3 和 GPT-4 几乎表现完美。Claude 2.1 的表现总体较差。准确性在不同的标记长度上没有显著差异,但当针头接近上下文窗口的开头时,准确性普遍较低。

图 6:正常大针测试结果(图像由作者提供)
尽管在生成测试中取得了出色的结果,Claude 3 在仅检索实验中的准确性却有所下降。从理论上讲,单纯检索数字应该比处理数字更容易——因此这一表现下降令人惊讶,也成为我们计划进一步测试的一个方向。如果有什么值得注意的,这一反直觉的下降只进一步证实了在使用 RAG 开发时,既要测试检索,也要测试生成。
结论
通过测试各种生成任务,我们观察到,虽然这两种模型在字符串处理等琐碎任务中表现出色,但它们的优缺点在更复杂的场景中变得明显。大型语言模型(LLMs)在数学方面仍然不够出色!另一个关键结果是,“解释你自己”提示的引入显著提升了 GPT-4 的表现,强调了模型如何被提示以及它们如何表达推理过程在实现准确结果中的重要性。
这些发现对 LLMs 的评估具有更广泛的意义。在比较像冗长的 Claude 和最初较为简洁的 GPT-4 这样的模型时,显而易见,RAG 评估标准必须超越单纯的正确性。模型回答的冗长性引入了一个变量,这可能会显著影响其感知表现。这个细微差别可能表明,未来的模型评估应考虑回答的平均长度作为一个重要因素,以便更好地理解模型的能力,并确保更公平的比较。
如何管理大规模数据科学项目的技巧
使用这些技巧来最大化你的数据科学项目的成功
·发表于 Towards Data Science ·6 分钟阅读·2024 年 9 月 14 日
--

图片来源:Jakub Żerdzicki @ Unsplash.com
管理大规模数据科学和机器学习项目具有挑战性,因为它们与软件工程有显著不同。由于我们的目标是发现数据中的模式,而不是明确地编写代码,因此涉及更多的不确定性,这可能导致各种问题,例如:
-
利益相关者的高期望可能无法实现
-
项目可能比最初计划的时间要长
从机器学习项目中产生的不确定性是导致挫折的主要原因。而当涉及到大规模项目时——通常这些项目有更高的期望——这些挫折可能会被放大,并对组织和团队产生灾难性的后果。
这篇博客文章源于我在管理大规模数据科学项目时的经验,特别是在与 DareData 合作的过程中。我有机会管理多个行业的不同项目,与才华横溢的团队合作,正是他们的贡献促成了我的成长和成功——正是因为他们,我能够收集到这些技巧并将其写成文章。
以下是一些核心原则,它们帮助我使许多项目取得成功。希望你能从中获得价值……
关心,还是不关心:使用 XmR 图表区分度量中的信号与噪声
创建和解读 XmR 图表以进行有效数据分析的逐步指南
·发布于 Towards Data Science ·阅读时间:11 分钟·2024 年 9 月 10 日
--
在数据驱动的组织中,跟踪指标对于做出正确的商业决策至关重要。当你需要关注的关键指标不多时,审查过程通常很简单。但随着公司规模的增长,指标的数量和复杂性也会增加,导致事情变得更加复杂。

图片由 Félix Prado 提供,来源于 Unsplash
你是否曾经遇到过某一周的指标出现突增,让你怀疑它的意义?或者当某个指标突然下降时,你会想知道是否值得担忧?区分真正的趋势和纯粹的随机噪声并不总是那么容易。
还不是 Medium 会员?使用这个 好友链接 继续阅读本文。
这就是 XmR 图表(个体图和移动范围图)发挥作用的地方。这些图表对于发现模式和变化非常有用,帮助数据分析师和相关人员迅速判断哪些变化值得深入研究。
什么是 XmR 图表?
XmR 图表,简写为“个体图(X)和移动范围图(mR)”,是一种强大的工具…
是否使用数据?
问题不再是我们是否可以用 AI 解决问题,而是它能在多大程度上返回可持续和可靠的结果。我们现在需要的是良好的工艺、治理、伦理以及 AI 教育。
·发表于 Towards Data Science ·阅读时间 11 分钟 ·2024 年 1 月 20 日
--

图片由 Karan Suthar 提供,来源于 Unsplash
自从我还是个孩子时,我就一直对新技术充满兴趣,今天我们生活在一个智能设备和互联设备成为我们日常生活一部分的世界中。当你有工程或其他技术背景时,这可能是最好的时代。然而,新的智能技术有其隐含的风险,这种风险正变得越来越明显;一个所有硬件部件都正常工作的设备,可能因为缺少网络连接或软件更新错误而变成一块砖。一个关键的因素是,逻辑不再必然硬编码在设备本身,而是成为一个外部存储并加载的模型。新的逻辑正在变得数据驱动,并且在向更智能设备过渡中变得至关重要,因为它应该更好地与我们的个人偏好互动。在这篇博客中,我将讨论 并非所有解决方案都需要数据,数据也不需要被融入到所有解决方案中,但确实有一些解决方案是我们需要数据的。
数据之争…
索引与否
利用 SQL 索引加速查询。了解何时使用索引,何时不使用索引,以及索引如何在背后运作。
·发表于 Towards Data Science ·阅读时间 18 分钟 ·2024 年 11 月 8 日
--

来源:www.pexels.com/photo/serene-autumn-morning-landscape-in-misty-meadow-29231560/
SQL 索引是数据圈中常常提到的一个术语 —— 你可能听过类似“只需应用一个索引”这样的说法。它也是面试中经常被问到的问题 —— “你能采取哪些步骤来提高表 X 的查询速度?”它是一个语法上容易实现的内容,但我发现很少有人关注实际发生了什么。在这篇文章中,我的目标正是通过使用关系型 MySQL 数据库(DB)来做这件事。我将涵盖什么是索引、如何实现它、它是如何工作的,以及一些在何时不使用索引的注意事项。像许多技术一样,SQL 索引也有其权衡。
在我的示例中,我使用了一个来自 Docker 的简单 MySQL 容器。我并没有涵盖其如何工作的内容,但如果你有任何问题,随时可以联系我。我将展示在本文章中用来填充数据库的代码,供你根据自己的使用案例进行适配并自行实验。
我首先从一个高层次的概述开始。更详细的内容将在文章后续部分介绍。因此,我希望能够为各种技术倾向的广泛读者群体提供有价值的见解。如果你像我一样,你会发现其中的可视化效果在…
知道就是记得
理解长短期记忆网络
·发布于Towards Data Science ·14 分钟阅读·2024 年 4 月 25 日
--

由作者使用MidJourney生成的图像
一男一女在临床研究中心的一个安静房间里交谈。女性提问后,等待男性回答并做笔记。乍一看,这似乎是一场普通的对话。然而,这绝非寻常。在女性的笔记本中,无论每页上写着什么日期,男性的回答总是相同的。尽管这些对话发生在 80 年代,但提到的事件却是 10 多年前的事情。Jenni Ogden是最早与病人 H.M.交谈的研究人员之一,几年后,H.M.因对神经心理学的影响而广为人知,最终会以他的真实姓名Henry Molaison被大家所熟知。经过多年的访谈和测试,研究人员得出结论,亨利失去了生成新记忆的能力,这与他 27 岁时接受的脑叶切除手术有关。亨利的案例帮助我们更好地理解了大脑功能与记忆之间的关系,并促成了短期记忆和长期记忆的概念诞生。这个概念为机器学习领域的开创性研究铺平了道路,在这个领域,科学家和开发者们试图从大脑神秘的内在机制中找到更好的方式来构建预测模型。
引言
屏蔽还是不屏蔽:提示令牌对指令微调的影响
实现提示丢失权重,并且为什么我们应该用提示加权替代提示屏蔽
·发表于 Towards Data Science ·30 分钟阅读·2024 年 9 月 30 日
--

图片由 Midjourney 制作
[*完整代码链接在* [*GitHub*](https://github.com/davidsvaughn/prompt-loss-weight)][*通过* [*LinkedIn*](https://www.linkedin.com/in/davidsvaughn/) 与我联系]
在过去几个月中,我注意到关于是否在针对提示-完成风格数据(即指令微调)进行微调时对提示令牌进行零屏蔽(忽略)的问题,出现了不少讨论,包括在这里、这里、那里,甚至这里。我看到使用了不同的术语,例如:
-
指令屏蔽
-
提示屏蔽
-
用户屏蔽
-
仅完成训练
无论你怎么称呼它,似乎没有明确的共识关于应该采用什么标准做法。根据你使用的开源库,微调的默认设置可能差异很大。
例如,水陆两栖兽库默认屏蔽提示令牌(通过其train_on_inputs=False默认设置)。然而,广受欢迎的HuggingFace Trainer默认不屏蔽提示令牌。可以选择使用[DataCollatorForCompletionOnlyLM](https://huggingface.co/docs/trl/main/en/sft_trainer#train-on-completions-only)来屏蔽提示,但这带来了一些显著的限制——尤其是缺乏对样本打包的支持——这在处理大型数据集时可能成为致命问题,就像我遇到的情况一样。(注意:这里提出了一个不错的解决方案在此)。
许多我遇到的关于 LLM 微调的指南、演示、笔记本、教程等,没有提到提示屏蔽,例如:
-
Niels Rogge 的SFT 教程
但也可以找到带有默认提示屏蔽的示例:
剧透警告:本文并不尝试一劳永逸地解决这个问题。它始于一个简单的想法,灵感来自一次谦虚的调查——我想比较带有和不带有提示屏蔽的微调,并在这两种情况下分别跟踪验证集的提示损失和完成损失*。
我的假设是,这可能为提示屏蔽问题提供有价值的见解。然后,我遇到了提示损失权重这一概念,它是将二进制令牌屏蔽优雅地推广到实值令牌加权(加权发生在损失函数内部,正如我们所看到的)。
将提示损失权重(PLW)参数集成到微调流程中,可以更平滑、更精细地控制提示标记对微调过程的影响。简单来说:PLW=0等同于提示掩蔽,而PLW=1等同于不进行掩蔽。此外,使用 0<PLW<1可以在这两种极端之间平滑地调节提示标记的影响。
通过这种重新框定,是否掩蔽提示标记的问题被更深层次的问题所涵盖,即如何加权提示标记。最优的加权方式可能根据具体的使用案例和数据集有所不同。通过将提示损失权重添加到你的工具包中,你将获得实验不同加权策略的灵活性,从而实现更有效的微调结果,更好地满足你的特定需求。
由于我找不到任何提示损失权重的实现,因此我决定尝试自己实现它。我将指导你完成对标准 HuggingFace LLM 工具集的几部分自定义修改,以便使其生效。之后,我们将使用更新后的工具集,通过在RACE 数据集(一个托管在 HuggingFace 上的多项选择问答数据集)上运行一些微调实验,来探索关于提示标记的原始问题。
一些 LLM 背景知识
LLMs 的操作基于标记而非单词。为了本篇文章的目的,我们将这两个术语交替使用,但值得注意的是,它们有所不同。标记被定义为频繁出现的字符序列,通常大致与单词相同(有时甚至包括前面的空格)。一个有趣的练习是玩一下GPT-4 分词器,我用它生成了以下示例(颜色编码揭示了底层的标记):

来自platform.openai.com/tokenizer的截图
我们日常使用的大多数生成性 LLM 是下一个标记预测机器。它们已经在大量人类生成的文本(书籍、报纸、互联网等)上进行了训练(有时称为预训练),因此当输入一段合理的文本片段时,它们非常擅长预测下一个单词应该是什么。这有时被称为因果语言建模。当这一自回归文本生成过程反复应用时,可以生成非常类似人类的句子、段落、文章等。
我们经常希望使用这些已经在大量文本上进行预训练的基础模型LLM(例如Meta 的 Llama 模型家族),然后继续进行进一步训练,也就是在一个更小的文本数据集上对其进行微调。这一做法源自于更广泛的迁移学习领域。
这里的目标是轻微调整或定制 LLM 的下一个标记预测行为,而不破坏或损坏模型权重中体现的基本“智能”——这使得 LLM 能够保留基础模型的大部分突现能力(如阅读理解、对话能力、推理能力等),但现在专门化于特定任务。例如,指令调优意味着对 LLM 进行微调,使其能够遵循指令。
在HuggingFace 数据集中心上有许多可用的指令调优数据集,按任务组织。一些数据集用于问答,或者文本摘要。在绝大多数情况下,这些数据集都共享相同的基本底层架构,每个数据样本包含:
-
一个 提示,即指令
-
一个 完成,即回应
在这种设置下,微调的目标是增加(最终是最大化)LLM 在输入提示时生成完成内容的概率。换句话说,回应“完成”了提示。我们很少,甚至几乎没有兴趣改变 LLM 生成提示本身的概率……因为提示只是 LLM 的输入。

文本摘要示例(图像来自作者)
以文本摘要为例。一个典型的提示可能包括一个总结长篇新闻文章的指令,以及文章本身,而完成内容则是请求的摘要(参见 HuggingFace 上的EdinburghNLP/xsum数据集)。在这个数据集上对基础 LLM 进行微调的目标是增加 LLM 在给定指令+文章时生成摘要的可能性,而不是生成文章本身,或者如果只显示文章的前半部分,则生成文章的后半部分。
然而,针对提示-完成风格数据集的 LLM 微调中,出现了一种流行的方法,就是基本忽略提示-完成的区别,直接对整个文本序列进行微调——基本上延续了预训练基础模型时使用的相同过程,尽管指令调优的目标与预训练的目标完全不同。这导致了教会 LLM 生成提示以及完成内容。
我不完全确定为什么会这样,但最可能的原因是,这种习惯仅仅是从旧的基础模型训练协议中继承下来的,最初并没有这种区分。从我所了解的情况来看,基本的态度似乎是:好吧,这有什么害处呢?只需在整个序列上进行微调,模型仍然会学会你想要的(根据提示生成结果)……它只是也会学到一些额外的东西。
提示遮罩 - 与 - 提示削弱
最明显的解决方案是将提示标记从学习过程中剔除(或零遮罩)。PyTorch 允许通过 CrossEntropyLoss 函数的 ignore_index=-100 参数手动遮罩输入标记。将所有与提示标记对应的标签 ID 设置为 -100,迫使 CrossEntropyLoss 忽略这些标记的损失计算,从而仅对完成标记进行训练(在我看来,这是一个非常文档化不足的功能——我只是偶然发现的——在 这里 的 Llama 文档中埋藏着一处引用)。
单独来看,这并不真正是对提示遮罩的解决方案。它只是通过其他方式找到某些标记后,遮罩这些标记的一种手段。一些之前提到的提示遮罩方法采用了这种技术,而另一些则明确创建了一个二进制遮罩来实现相同的效果。虽然有用,但这个解决方案仍然是一个二进制开关,而不是prompt-loss-weight所允许的连续调节器。
然而,这引出了一个问题:如果提示遮罩确实能够改善指令微调,那么设置一个非零的prompt-loss-weight究竟有什么意义呢?为什么我们要仅仅削弱提示标记的影响,而不是完全消除它?
最近有一篇论文发布在 arxiv 上,标题是 Instruction Fine-Tuning: Does Prompt Loss Matter? 作者建议,少量的提示学习可能作为微调过程中的一种正则化器,防止模型过拟合完成文本。他们假设:
…[非零] PLW 提供了一种独特的正则化效果,这种效果无法轻易地通过其他正则化方法替代…
即使是 OpenAI 的人员似乎也承认使用一个小的但非零的提示损失权重(PLW)是有益的。显然,他们曾通过微调 API 曝露了这个 PLW 参数,并且 在线上仍然有一些文档,其中指出:
少量的提示学习有助于保持或增强模型理解输入的能力(来自最佳实践:微调 GPT-3 进行文本分类)
尽管他们后来移除了这个参数。然而,根据旧版文档,他们使用了PLW=0.1(10%)的默认值,这意味着提示词的权重是完成词的 1/10。
生成比率
在之前提到的论文中(Instruction Fine-Tuning: Does Prompt Loss Matter?),作者介绍了一个有用的量。给定一个指令数据集,他们定义了生成比率,或称Rg:
生成比率Rg是完成长度与提示长度的比率。然后我们将指令数据分为两大类。Rg<1的数据为短完成数据,Rg>1的数据为长完成数据。当应用于整个数据集时,我们取R̅g为平均的完成与提示比率。
对于具有较小R̅g值的数据集(即完成部分比提示部分短),他们发现 PLW 确实很重要(即使用错误的 PLW 值会降低性能)。如果你仔细想想,许多常见的指令微调数据集实际上具有完成部分比提示部分短的特性,这几乎是有意为之(比如:文本摘要、信息提取)
作为一个有趣的练习,我计算了在 HuggingFace 上几个流行指令数据集的R̅g值(代码在这里):
-
7.6 | Alpaca(通用指令)
-
6.0 | OpenHermes(通用指令)
-
3.6 | Python-18k(代码指令)
-
2.0 | Databricks-Dolly-15k(通用指令)
-
1.1 | OpenOrca(通用指令)
-
0.2 | SAMSum(文本摘要)
-
0.1 | XSum(文本摘要)
-
0.01 | RACE(问答/多项选择)

一些指令数据集的平均生成比率(R̅g)(图片来自作者)
在通过平均值总结任何一组数据时,良好的做法是查看数据的完整分布以进行合理性检查。算术均值在数据高度偏斜或偏离大致正态分布时可能具有误导性。我绘制了显示每个数据集Rg分布的直方图(顶部行)。底部行显示了相同的直方图,但 x 轴进行了对数缩放:

线性和对数缩放的Rg直方图(图片来自作者)
这些图表表明,当数据集的Rg分布跨越多个数量级或在Rg>1和Rg<1区域都有显著表示(例如在OpenOrca和其他R̅g>1的数据集中),分布可能会高度偏斜。因此,算术均值可能会受到较大值的不成比例影响,从而可能会误导分布的集中趋势。在这种情况下,在对数空间中计算均值(然后可以选择将其转换回原始尺度)可能会提供更有意义的汇总统计量。换句话说,使用几何均值可能更为合理:
RACE 阅读理解数据集
基于上面的R̅g表格,我决定将RACE ReAding Comprehension Dataset from Examinations(R̅g=0.01)作为一个很好的研究对象。多项选择的 QA 似乎是探索提示屏蔽效果的理想测试平台,因为与完成任务相比,提示通常非常长。无论提示长度如何,完成任务始终只有 1 个字符,即A、B、C或D(如果忽略特殊标记、分隔符等)。我的直觉是,如果有任何来自调节提示标记权重的影响,它们肯定会在这里显现出来。
如在数据集卡片中所述:
RACE 是一个大规模的阅读理解数据集,包含超过 28,000 篇文章和近 100,000 个问题。该数据集来源于中国的英语考试,面向中学和高中学生。该数据集可以用作机器理解的训练集和测试集。
QA 模式很简单:提示提供一个问题,可能会有一些上下文(文章字段),然后列出四个选项。完成(答案)总是其中之一:A、B、C、D。这个数据集查看器托管在 HuggingFace 上,允许浏览完整的数据集,但这里是一个小例子:

RACE 示例(截图来自huggingface.co/datasets/ehovy/race/viewer/all/train)
交叉熵损失
在我们开始全面实施提示-损失权重并尝试在 RACE 数据集上进行实验之前,我们需要先了解损失的基本概念及其来源。简单来说,损失是衡量我们的模型(LLM)如何“拟合”(解释、预测)数据的一个指标。在微调(以及预训练)过程中,我们通过调整网络权重使模型更接近数据,从而减少损失。链式法则(微积分)为我们提供了一个精确的算法,用于在给定损失函数和网络架构的情况下计算这些调整。
在 LLM 微调中,最常见的损失函数叫做交叉熵损失(CEL)。因此,大多数关于 CEL 的讨论都是围绕交叉熵的定义展开的,而交叉熵来源于信息论。虽然“交叉熵”在名称中就提到了,但通过最大似然估计(MLE)的角度来看,更直观的理解可以帮助我们理解 CEL。我会从两个角度来尝试解释。
我们已经明确 LLM 是为了下一个令牌预测而设计的。这意味着 LLM 本质上是一个数学函数,它将令牌序列作为输入,并输出整个令牌词汇V中下一个令牌的条件概率分布。换句话说,它输出一个维度为|V|的概率值向量,并且这些概率的总和为 1。(在集合符号中,|S|表示集合S的元素数量,或称为基数)
让我们用一个小的玩具例子来说明这个过程。假设我们的训练数据包含以下 4 个令牌序列:The bird flew away。给定前 3 个令牌(The bird flew),LLM 可能会输出每一个可能的第 4 个令牌的概率向量——为了简单起见,我们假设列出的 5 个候选令牌(用洋红色标出)是唯一的可能性(即|V|=5)。函数p(⋅)表示 LLM 输出的条件概率(注意它们的和为 1):

(图片来自作者)
在训练(或微调)LLM 时,我们会逐个遍历令牌序列,比较 LLM 生成的下一个令牌分布与序列中的实际下一个令牌,然后基于此计算该令牌的 CEL。
这里需要注意的是,序列中的实际第 4 个令牌(away)在表中的概率并不是最高的。在训练过程中,我们希望稍微调整权重,以增加away的概率,同时减少其他令牌的概率。关键是选择正确的损失函数……它可以帮助我们精确计算每个令牌的每个权重需要调整多少。
一旦计算出每个 token 的损失,最终的损失将计算为所有 token 的每 token 损失的平均值。但首先,我们必须为这个每 token 的损失建立公式。
信息论解释
继续这个玩具问题,要计算第四个 token 位置的 CEL,我们将实际的第四个 token 与所有五个可能的第四个 token 的生成分布 p(⋅) 进行比较。事实上,我们将实际的第四个 token 视为一个分布 q(⋅)(尽管它是一个退化的分布),其在数据 -away- 中出现的 token 值为 1,其他所有可能的第四个 token 的值为 0(这有时称为独热编码)。

(图片由作者提供)
我们将训练数据变形成这种奇怪的独热编码的概率表示 q(⋅) 的原因,是为了能够应用 交叉熵 公式,这是衡量两个离散概率分布之间散度的一种方法(顺便提一下,它对 q 和 p 不是对称的):

其中 x 索引所有可能的状态(即 5 个 token)。这可以表示为:

所以,基本上 CEL 就是使用 q 向量从 p 向量中选择与数据中实际出现的 token -away- 对应的单个值(即将其乘以 1),并丢弃所有其他值(即乘以 0)。因此,我们只是遍历所有可能的状态(token),只选择一个并忽略其他所有。
最大似然估计(MLE)解释
在微调 LLM 时,我们寻求最大化在给定这些权重下训练数据的概率的 LLM 权重 θ,这通常称为这些权重的似然 ℒ(θ) = ℙ(D|θ)。因此,我们需要一个表达式来计算这一数量。幸运的是,LLM 已经为我们提供了从下一个 token 概率中计算这一点的简便方法。
从另一个 链式法则(概率)开始,我们将 token 序列 S 的联合概率分解为条件概率的乘积:

链式法则(概率)
这个分解建立了下一个 token 预测与完整 token 序列联合概率之间的联系——联合概率只是所有条件概率的乘积。
使用 i 索引 token 序列 S = (t₁,t₂,t₃,…, tᵢ ,…) 中的 token,我们将使用以下简写来表示 LLM 为序列中第 i 个 token 输出的条件概率,给定 LLM 权重 θ 和前 i-1 个 token:

应该强调的是,pᵢ在这里不是一个向量(即不是对所有可能的下一个标记的分布),而只是表示为实际的iᵗʰ标记计算的概率,即上面示例中的黄色高亮行。
如果我们取序列的联合概率的对数,乘积会变成和(因为对数是单调的,这不会影响优化):

现在我们可以将最终的对数和表达式(就在这里☝)️与标记序列上的平均交叉熵损失 L 的公式连接起来:

这就是因果语言模型的目标函数。通常,名称中的“平均”会被省略,直接称为“交叉熵损失”,但记住,从技术上讲,CEL 是按标记级别计算的,然后对所有标记进行平均。希望从这个最终表达式中可以清楚地看出,最小化 CEL等同于最大化标记序列的概率,这正是 MLE 所追求的目标。
这种表达式的一大便利之处在于,如果我们想要计算任何子集标记的损失,修改起来非常容易。回想一下,我们有时可能有兴趣找到能够最大化给定提示下完成的概率的 LLM 权重θ:

我们可以通过仅对完成标记进行平均,轻松调整此情形下的损失。如果我们使用“𝕀c”表示所有完成标记的索引集,那么我们可以将完成损失表示为:

由于每个标记的损失已经基于序列中所有先前的标记进行了条件化,这意味着即使我们只对完成标记进行平均,提示也会自动包含在条件中。
提示损失权重
现在我们已经确定 CEL 是标记序列上每个标记损失的平均值,我们可以定义 CEL 的加权平均版本:

根据我们如何设置权重wᵢ,我们可以使用这个公式定义多种损失。例如,如果我们将所有权重wᵢ =1,那么我们就恢复了之前的标准全序列 CEL。然而,如果我们仅对完成标记设置wᵢ =1,对提示标记设置wᵢ = 0,那么我们得到完成损失。同样,提示损失通过仅在提示标记上设置wᵢ =1,其他情况下设置wᵢ = 0来定义。
由于我们很少(如果有的话)希望对完成令牌进行下调权重,我们将完成令牌的权重固定为wᵢ =1,但对于提示令牌,我们可以在[0:1]区间上定义一个连续值,称为prompt_loss_weight。这样,我们可以在训练过程中调节提示令牌的权重,从wᵢ = 0(完成损失)一直到wᵢ = 1(标准完整序列损失)。或者,我们甚至可以使用wᵢ = 0.1,为提示令牌赋予一个小但非零的权重。
损失实现
让我们深入了解损失是如何在HuggingFace Transformers包中通常计算的。由于我们将在实验中微调Llama-2–7b-chat-hf模型,我们将查看LlamaForCausalLM,特别是前向传播,其中在训练过程中计算损失。
回想一下,损失是将每个实际令牌与 LLM 对该令牌的预测进行比较的一种方式(给定前面的实际令牌)——因此损失函数需要访问这两个数据结构。在这种情况下,损失输入两个张量:logits和labels。labels张量保存实际令牌(准确来说是令牌 ID)。logits张量保存预测的下一个令牌概率,在softmax归一化之前(归一化会将这些概率的和强制为 1——事实证明,保持这些值在原始、未经归一化的形式中更为高效)。
logits张量是 3 维的,形状为[B,N,|V|],其中B是批量大小,N是序列长度(单位为令牌),|V|是令牌词汇大小。2 维的labels张量仅包含令牌序列本身,因此它的形状是[B,N]。以下是通常计算 CEL(交叉熵损失)的关键代码部分:
# Shift-by-1 so that tokens < n predict n
shift_logits = logits[..., :-1, :].contiguous()
shift_labels = labels[..., 1:].contiguous()
# Flatten the tensors
shift_logits = shift_logits.view(-1, self.config.vocab_size)
shift_labels = shift_labels.view(-1)
# Enable model parallelism
shift_labels = shift_labels.to(shift_logits.device)
# Compute loss
loss_fct = CrossEntropyLoss()
loss = loss_fct(shift_logits, shift_labels)
对于logits的第 2 维度上每个位置的i,这个张量包含了在给定所有前面的令牌(直到第i个令牌)的情况下,预测下一个令牌(令牌i+1)的概率。这些概率需要与labels中实际的i+1ˢᵗ令牌进行比较。这就是为什么在前几行中会发生偏移-1的原因——为了使这两个值在每个令牌上对齐。

(图像来源:作者,灵感来自:wandb.ai/capecape/alpaca_ft/reports/How-to-Fine-Tune-an-LLM-Part-2-Instruction-Tuning-Llama-2--Vmlldzo1NjY0MjE1)
接下来发生的事情是,前两个维度被合并成一个(展平),然后张量被传递给 [CrossEntropyLoss()](https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html#torch.nn.CrossEntropyLoss),这是一个 PyTorch 函数,它输出最终的损失值。
自定义损失函数
默认情况下,[CrossEntropyLoss()](https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html#torch.nn.CrossEntropyLoss) 会对所有标记进行平均,以输出一个单一的标量值。这一最终的平均(对所有标记的平均)称为归约操作。但如果我们用无归约操作来实例化损失:
loss_fct = CrossEntropyLoss(reduction="none")
那么不进行平均,最终的损失将是一个一维张量(长度为 BxN),包含每个标记的损失(损失张量将是二维的,形状为 [B,N],没有先前的展平步骤)。这就是我们如何获得每个标记的损失,以便计算我们自己的加权平均值。
在分词过程中(查看完整代码详情),我们为每个序列创建了两个额外的二进制掩码,即提示掩码和完成掩码。二进制掩码仅仅是由 1 和 0 组成的向量。提示掩码将所有提示标记标记为 1(否则为 0),而完成掩码则相反。然后,我们可以通过这两个掩码的简单线性组合,得到加权平均版本的 CEL 的权重 wᵢ,即将提示掩码乘以 PLW 并加到完成掩码上:

损失权重 = prompt_loss_weight * prompt_mask + completion_mask(图像由作者提供)
我们从 HuggingFace 的 Trainer 子类化,定义了一个名为 PLWTrainer 的新训练器类。我们将首先重写两个函数:
-
__init__(): 构造函数接收额外的prompt_loss_weight参数 -
compute_loss(): 使用prompt_loss_weight计算加权损失
class PLWTrainer(Trainer):
def __init__(self, *args, prompt_loss_weight=1.0, **kwargs):
super().__init__(*args, **kwargs)
self.plw = prompt_loss_weight
def compute_loss(self, model, inputs, return_outputs=False):
# get outputs without computing loss (by not passing in labels)
outputs = model(input_ids=inputs["input_ids"],
attention_mask=inputs["attention_mask"])
logits = outputs.get("logits")
labels = inputs.pop("labels")
# compute per-token weights
weights = self.plw * inputs["prompt_mask"] + inputs["completion_mask"]
# Shift-by-1 so that tokens < n predict n
shift_logits = logits[..., :-1, :].contiguous()
shift_labels = labels[..., 1:].contiguous()
shift_weights = weights[..., 1:].contiguous()
# Enable model parallelism
shift_labels = shift_labels.to(shift_logits.device)
shift_weights = shift_weights.to(shift_logits.device)
# Compute per-token losses
loss_fct = CrossEntropyLoss(reduction="none")
token_losses = loss_fct(shift_logits.view(-1, shift_logits.size(-1)),
shift_labels.view(-1))
# Compute weighted average of losses
loss = token_losses @ shift_weights.view(-1) / shift_weights.sum()
return (loss, outputs) if return_outputs else loss
如果构造函数没有显式传入 prompt_loss_weight 的值,默认值(prompt_loss_weight=1)意味着我们会恢复到原始 Trainer 的继承行为(即最小化完整序列的损失)。然而,如果我们传入其他的 prompt_loss_weight 值,我们就可以得到一系列不同的损失函数。
我们几乎准备好尝试新的损失函数了!但在此之前,我们需要确保能够观察和理解它对微调过程产生的影响(如果有的话)…
验证指标
分别跟踪提示和完成的损失
在微调过程中,通常的做法是跟踪模型在验证集上的表现,以决定何时结束训练。验证集,也称为保留集,是从训练数据中“保留”的一个随机子集,以确保模型不会学习或记住它。模型在此集上的表现被视为模型在实际应用中面对新数据时的表现估计。这就是大多数初级机器学习课程中所讲的经典“训练与验证曲线”产生的原因:

(图片来自作者)
这里的教训是,绿色(验证)曲线的最小点代表了最佳的训练步数,超过这个点,模型开始过拟合或记住训练数据,而不是继续从数据中学习可泛化的模式。虽然无法知道真正的最佳停止点,但通过跟踪验证集上的度量,我们可以相对准确地估计它。然而,这也有一个权衡:更大的验证集会带来更好的估计,但也意味着训练集会变小,所以我们不希望保留过多的样本。5%–15%是一个很好的经验法则。
通常,在微调大型语言模型(LLM)时,最小化训练集上的目标损失函数也成为用于跟踪验证集表现的默认度量,从而确定最佳停止时机。讨论通常集中在两种选择上:
-
最小化训练集上的完整序列损失——并在验证集上跟踪
-
最小化训练集上的完成损失——并在验证集上跟踪
但是——我们可以自由地在验证集上跟踪任何度量(或多个度量),不仅仅是用作训练目标的损失。这引出了启发本文的最初想法——我想尝试第三种选择:

然而,在围绕 PLW 调整我的方法后,这一过程演变成了:

为了实现这一点,我们首先需要编写一个自定义度量,将验证的完整序列损失分解为提示损失和完成损失,我们将在下一节中进行说明。我们将使用在自定义损失函数中使用过的相同技巧。
插曲:你可能会注意到,在大型语言模型(LLM)社区中,实践者有时完全绕过了停止准则问题,遵循像仅微调一个周期之类的简单规则。有时这很有道理,比如当微调模型生成更多主观内容时,如电子邮件、诗歌或笑话。但是当微调数据集更侧重于正确性时,比如编写代码、解决数学问题或多选问答(我们将在下文中看到的一个例子),那么确实有必要监控验证损失和/或其他验证度量。所以我们需要确保谨慎操作。
然而,这并不意味着标记序列的正确性是单个标记正确性的简单线性函数。标记序列的语义意义可以是个复杂的、高度非线性的函数,依赖于各个标记的意义。这就是为什么很容易构造出许多例子,其中标记级别的一个微小变化可以显著改变整个句子的意义——只需在合适的位置插入“not”就能完全颠倒句子的意思!
尽管如此,在许多情况下,平均每个标记的损失仍然可以作为训练/微调过程中 LLM 预测整体质量的一个良好指标。这是因为教师强制的标准做法确保每个标记的预测是基于训练/验证数据中“正确”(即地面真实值)的前一个标记,而不是基于模型自身的前一个标记预测(这是推理/文本生成过程中发生的情况)。
但没有一个度量是完美的,这就是为什么始终使用多种评估方法,包括特定任务的度量和人工评估,变得非常重要。
定义自定义度量
定义自定义验证度量的常见方法之一,在使用HuggingFace Trainer时,是重写 Trainer 的默认compute_metrics()函数,该函数在训练期间会定期在验证集上运行。然而,默认情况下,这个函数并不会接收到足够的信息来计算提示损失或完成损失。
具体来说,对于每个验证集序列,compute_metrics()接收到的是预测标记和实际标记。这仅适用于计算某些度量,例如标记准确性,但不适用于计算损失。幸运的是,我们可以通过重写另一个函数preprocess_logits_for_metrics()来修改传递给compute_metrics()的数据。
为了计算损失,我们需要访问logits中包含的实际概率分布。回想一下,一个用于下一个标记预测的 LLM,在标记序列的每个点上,会为下一个标记生成一个覆盖词汇表中所有可能标记的概率分布(|V|=32000)。这个分布存储在logits中,其形状为[B,N,|V|]。
默认情况下,preprocess_logits_for_metrics()将对这个logits张量沿最后一维(即|V|维度)进行argmax操作,并将这些标记索引传递给compute_metrics()。
# from preprocess_logits_for_metrics
predictions = logits.argmax(-1)[..., :-1]
这些预测表示 LLM 本该 为每个验证序列中的每个令牌位置预测的令牌,给定前面的令牌(最后一个令牌的预测被截断,因为没有真实值与之比较)。但正如我们所见,要计算每个令牌的损失,我们实际上不需要知道最高概率的令牌(通过 argmax 返回的预测)——我们需要知道 LLM 为每个验证序列中的 实际 令牌分配的概率,给定前面的令牌。
一种解决方案是直接将整个 logits 张量传递给 compute_metrics(), 然后在其中计算损失以及其他任何指标,比如准确度。 然而,这种方法有一个严重的问题:由于 Trainer 的设置,preprocess_logits_for_metrics() 函数是在 GPU(按批次)上运行的,而 compute_metrics() 是在 CPU 上运行的(针对整个验证集,即所有批次重新组合后)。并且,preprocess_logits_for_metrics() 在 GPU 上运行的 原因 是 logits 张量可能变得 极其 大。
仅仅给你一个概念我的实验中,批次大小(B)为 8,序列长度(N)为 2048,这导致一个张量包含 B x N x |V| = 8 x 2048 x 32000 ≈ 42 亿 个值(每个 GPU)!
GPU 可以处理这个巨大的张量,但如果我们尝试传递它,CPU 会爆炸。我们必须首先在 preprocess_logits_for_metrics() 内执行某种缩减操作,以去除这个巨大的第三维度。
这没有单一正确的方法。一种选择是从 logits 中选择为每个实际(真实)令牌生成的概率,并将其传递给 compute_metrics(),然后在 CPU 上计算损失。 这当然可以工作。 然而,更好的主意是使用 GPU 的全部处理能力,在 preprocess_logits_for_metrics() 内进行更多的计算,然后再将任务交给 CPU 端。
回想一下,交叉熵损失是针对令牌序列的 平均 每个令牌 损失。因此,我们可以使用 preprocess_logits_for_metrics() 来计算包含所有 每个令牌 损失的张量,并将这个张量传递给 compute_metrics() 以便稍后进行平均。
一个小的复杂问题是,preprocess_logits_for_metrics() 被设置为将一个 单一 值传递给 compute_metrics()。 然而,我们需要传递 两个 单独的张量。因为我们有兴趣在验证集上跟踪多个指标(如提示损失和完成损失,以及完成令牌准确度)——我们需要两个张量:predictions 用于完成准确度,per-token-losses 用于两个损失。幸运的是,从 preprocess_logits_for_metrics() 传递到 compute_metrics() 的单一值可以是单个张量或张量元组。
具体来说,compute_metrics()接收一个单一的参数data,它是一个工具类[transformers.EvalPrediction](https://github.com/huggingface/transformers/blob/174890280b340b89c5bfa092f6b4fb0e2dc2d7fc/src/transformers/trainer_utils.py#L149)的实例。preprocess_logits_for_metrics()返回的值被分配给EvalPrediction的.predictions字段(在批次被聚合成一个张量并转换为 numpy 数组之后)。spec for [.predictions](https://github.com/huggingface/transformers/blob/174890280b340b89c5bfa092f6b4fb0e2dc2d7fc/src/transformers/trainer_utils.py#L161)表示它可以容纳一个单一的数组或一个数组元组(predictions: Union[np.ndarray, Tuple[np.ndarray]]),因此我们可以继续进行。
# uses PyTorch tensors (on GPU)
def preprocess_logits_for_metrics(logits, labels):
# get predictions
token_preds = logits.argmax(-1)[..., :-1]
# compute per-token losses
loss_fct = CrossEntropyLoss(reduction="none")
shift_logits = logits[..., :-1, :].contiguous()
shift_labels = labels[..., 1:].contiguous()
token_losses = loss_fct(shift_logits.transpose(1, 2), shift_labels)
# pass predictions and losses to compute_metrics()
predictions = (token_preds, token_losses)
return predictions
现在我们可以定义compute_metrics()…
# uses numpy arrays (on CPU)
def compute_metrics(data):
# data.predictions contains the tuple (token_preds, token_losses)
# from preprocess_logits_for_metrics()
token_preds, token_losses = data.predictions
# shift labels and masks
labels = data.label_ids[..., 1:]
shift_prompt_mask = prompt_mask[..., 1:]
shift_comp_mask = completion_mask[..., 1:]
# average both losses (prompt and completion) over their respective tokens
prompt_loss = token_losses.reshape(-1) @ shift_prompt_mask.reshape(-1) / shift_prompt_mask.sum()
completion_loss = token_losses.reshape(-1) @ shift_comp_mask.reshape(-1) / shift_comp_mask.sum()
# compute response token accuracy
nz = np.nonzero(shift_comp_mask)
idx = np.where(np.isin(labels[nz], ABCD_token_ids))
accuracy = np.mean(preds[nz][idx] == labels[nz][idx])
return {
'comp_loss': completion_loss,
'prompt_loss': prompt_loss,
'acc': accuracy,
}
这一切看起来应该很熟悉,因为我们正在使用与定义自定义损失函数时相同的思想。同样,我们依赖于prompt_mask和completion_mask来选择适当的令牌子集以计算每个损失。如果你在想prompt_mask和completion_mask是在哪里定义的,它们发生在函数范围之外,但通过函数闭包使其可用,这是一种在“函数工厂”中经常使用的方法(查看完整脚本了解详情)。
完成令牌的准确性仅在实际的多项选择答案令牌(即A,B,C,D)上计算,而完成损失则包括在聊天模板中使用的其他特殊令牌(即空格、bos_token、eos_token等)。引用的ABCD_token_ids使我们能够隔离答案令牌并忽略其他令牌。
实验
最后,让我们在改变 PLW 的同时进行一些微调运行……
完整序列训练:PLW=1
实现细节:我使用了Llama-2–7b-chat-hf作为基础模型,并通过 HuggingFace 的PEFT(参数高效微调)库,使用LoRA(低秩适应)方法对RACE 阅读理解数据集的一个子集进行了微调。我通过微软的DeepSpeed库,使用多 GPU 训练显著加速了微调过程。再次提醒,查看完整代码了解所有细节。
以下第一张图展示了在最小化标准的完整序列损失时,所有验证集指标的演变。每条曲线都有自己的 y 轴标签(按颜色编码),因为它们都在不同的尺度上(除了提示和完整序列损失,它们使用相同的尺度,位于左侧)。可以看到,响应准确率与完成损失的变化非常接近,但方向相反,正如预期的那样。我在完成损失和完整序列损失的最小值处绘制了虚线蓝色和绿色线条,显示它们与准确率交点的位置。

RACE 验证集指标(图像由作者使用Matplotlib制作)
主要需要观察的是提示损失和完成损失的最小值完全不同步——由于提示损失主导了完整序列损失(记住R̅g = 0.01),因此完整序列损失基本上只是提示损失略微下移的结果,并且它们共享相同的最小值。
这意味着如果你盲目遵循流行做法,使用验证集完整序列损失的最小值作为停止标准——刚好在第 2 个 epoch 之前——此时完成损失仍然很高——微调后的模型准确率仅为 53%!
但是,仅仅通过追踪完成损失(与使用 PLW=0 在我们自定义损失函数中进行直接最小化相对,我们接下来会这样做),你将继续微调到 4.5 个 epoch,在这个点上完成损失达到最小值,并且准确率提高到 75%!
仅完成训练:PLW=0
现在,我们将转向范围的另一端,完全屏蔽掉提示符令牌。我们所需要做的就是将 PLWTrainer 初始化为 prompt_loss_weight=0。以下是这些结果的绘图:

RACE 验证集指标(图像由作者使用Matplotlib制作)
有两件重要的事情发生了变化:
-
微调收敛速度快得多 到达最小完成损失 - 并且最优准确率 - 只需不到 2 个 epoch(而不是 4.5 个 epoch)
-
最优准确率也提高了——从 75% 跃升到 80%
另一个有趣的现象是,提示损失并没有像之前的图一样下降,而是保持在一个水平上,甚至稍微上升(请特别注意提示损失的 y 轴刻度——在左侧)。换句话说,提示符令牌完全没有学习,而从微调中剔除它们反而提高了收敛速度和最大准确率。看起来是双赢!
探索完整的 PLW 范围
请记住,如果我们使用任何分数值 0 < PLW < 1,则提示符令牌对总损失的影响会被减弱,但不会消除。下面我绘制了在六个不同 PLW 值下的验证集完成损失和 QA 准确率:[1, 0.5, 0.2, 0.1, 0.01, 0]

RACE 验证集指标(图像由作者使用 Matplotlib 制作)
最引人注目的是,三个最小 PLW 值 [0,0.01,0.1] 的完成损失收敛到最小值的速度要快得多。最快的收敛似乎发生在 PLW=0 时,但与下两个最小值相比,只有微小的差异。查看准确率后,似乎这三个最小的 PLW 值中的任何一个都将在大约第 2 个周期时达到最优准确率(~80%)。
同样有趣的是,将每个完成损失曲线的收敛行为与其相应的准确率曲线进行比较。在达到最小值后,所有六条完成损失曲线都开始缓慢上升,而所有准确率曲线则趋于平稳而不下降……我们如何解释这一现象呢?
离题:损失还是 token 准确率 — 哪个更值得追踪?
记住,下一 token 的预测是通过选择给定前一个 tokens 的条件下概率最高的 token 来完成的。token 准确率的公式只考虑 token 是否正确,而交叉熵损失的公式实际上考虑了这些概率的 值。那么,解释这两张图之间差异的原因可能是什么呢?
好吧,由于 token 准确率保持稳定,这意味着具有最高概率的 token(即 argmax token)保持相对恒定,但这些 最大 值 必须在稳步下降——换句话说,模型对其(大部分正确的)token 预测的信心在减弱。这可以被视为一种轻度的过拟合情况,其中最大值受到影响,但不足以影响 argmax 值。
这个例子说明了为什么有些人说 追踪 token 准确率比追踪验证损失更好。就我个人而言,我认为争论哪一个更好是愚蠢的,因为你不必选择……可以同时追踪两者!两者都是有价值的指标。token 准确率可能最终是你关心的最大化目标(在很多情况下,至少是这样)。但我也想知道模型是否以及何时对其(大部分)正确的预测变得不那么自信(就像我们上面看到的那样),所以我也会追踪完成损失。
更好的是,最优策略(在我看来)应该是同时跟踪模型在基准测试或多个基准测试上的表现,以便更全面地了解模型在微调过程中的演变。可能出现的情况是,尽管你的 LLM 在验证集上的纯粹标记准确性不断提高,但它的回应变得更加重复和机械化,因为验证集不够多样(实际上我在我的日常工作中见过这种情况)。始终记住真正的最终目标是什么……在几乎所有情况下,验证集上的标记准确性最多只是你真正目标的一个平庸代理。
结论
我们对变化的提示-损失权重(PLW)对大型语言模型(LLM)指令微调效果的探索突出了几个重要概念:
-
将训练目标与验证指标解耦:即使不改变训练目标函数中提示词的权重,我们也发现,通过跟踪正确的验证指标(例如完成损失或准确度),我们可以改善结果。
-
PLW 确实可以影响模型性能:通过减少 PLW,我们发现微调后的模型性能有所提高。令人惊讶的是,实际上并不需要完全的提示屏蔽来实现最大改进,因为将 PLW 减少到 0.1 以下似乎不会产生额外的效果。是否这种行为可以推广到其他数据集,必须逐个案例评估。
-
PLW 确实可以影响收敛速度:同样地,通过减少 PLW,我们发现微调后的模型收敛速度更快,能够更快达到最佳状态。这个效应可能在很大程度上独立于模型性能的变化——即根据数据集的不同,这两个效应可能会独立出现。
-
数据集特定优化:根据具体的数据集和任务,最优的提示-损失权重(PLW)很可能会有很大差异。在许多情况下,PLW 甚至可能没有任何效果。在 RACE 数据集上看到的显著改进可能无法推广到所有的微调场景,这凸显了实验的重要性。
未来的研究方向可能包括:
-
探索 PLW 对更多数据集的影响,超越指令数据集,像那些具有更大生成比率或更长对话的聊天数据集
-
开发在微调过程中动态调整的自适应 PLW 策略
-
研究 PLW 对模型性能其他方面的影响,例如泛化能力和鲁棒性
希望你觉得这稍微有点用。我随时欢迎反馈和修改!你可以通过 LinkedIn 联系我。本文中的图片是我的,除非另有说明。
资源
- 与本教程相关的所有代码可以通过此链接访问。
在 Python 和 Mathematica 中可视化复值函数
解锁数学难题的视觉洞察
·发表于 Towards Data Science ·3 分钟阅读·2024 年 2 月 19 日
--
今天我正在翻阅由受尊敬的 Fornberg 和 Piret 编写的《复变函数与解析函数》,尽力理解复值函数的行为。由于这些函数既有实数输入,也有虚数输入,并且输出两个分量,因此,单一的 3D 图形不足以展示函数的行为。我们需要将这种可视化拆分为实部和虚部的单独图表,或者按幅度和相位(角度)来展示。
我希望能够随意操作任何想到的函数,拖动和缩放其图表,并在可视化细节中探索它,以理解它如何从方程中得出。对于这样的任务,Wolfram Mathematica 是一个很好的起始工具。
Wolfram 语言
plotComplexFunction[f_]:=Module[{z,rePlot,imPlot,magPlot,phasePlot},z=x+I y;
rePlot = Plot3D[Re[f[z]],{x,-2,2},{y,-2,2},AxesLabel->{"Re(z)","Im(z)","Re(f(z))"},Mesh->None];
imPlot = Plot3D[Im[f[z]],{x,-2,2},{y,-2,2},
AxesLabel->{"Re(z)","Im(z)","Im(f(z))"},
Mesh->None];
magPlot = Plot3D[Abs[f[z]], {x, -2, 2}, {y, -2, 2},
AxesLabel -> {"Re(z)", "Im(z)", "Abs(f(z))"},
Mesh -> None,
ColorFunction -> Function[{x, y, z}, ColorData["Rainbow"][Rescale[Arg[x + I y], {-Pi, Pi}]]],
ColorFunctionScaling -> False];
phasePlot=DensityPlot[Arg[f[z]],{x,-2,2},{y,-2,2},
ColorFunction->"Rainbow",
PlotLegends->Automatic,
AxesLabel->{"Re(z)","Im(z)"},
PlotLabel->"Phase"];
GraphicsGrid[{{rePlot,imPlot},{magPlot,phasePlot}},ImageSize->800]];
f[z_]:=(1/2)*(z+1/z);
plotComplexFunction[f]
https://github.com/dreamchef/complex-functions-visualization
github.com/dreamchef/complex-functions-visualization
我编写了上述 Mathematica 代码,用于生成一个图表网格,展示如上所述的两种方式的函数。在顶部显示了函数的虚部和实部,

底部显示了幅度和相位,以颜色表示:

来自 Wolfram 的分量和幅度-相位图 f(z)
在用这段代码玩了几个函数并确信它们有意义之后,我对如何在 Python 中实现相同功能产生了兴趣,以便将其与其他数学编程项目连接起来。
Python、PyPlot 和 complex-plotting-tools
我在 GitHub 上找到了一个很棒的项目(github.com/artmenlope/complex-plotting-tools),我决定将其作为起点,并有可能在未来做出贡献。这个仓库提供了一个非常简单的界面,用于以多种方式绘制复值函数。感谢github.com/artmenlope!例如,在导入numpy、matplotlib和该仓库的cplotting_tools模块后,定义函数并调用cplt.complex_plot3D(x,y,f,log_mode=False),将生成以下内容:

来自 complex-plotting-tools 的 f(z)幅度和相位图
这些图都是用于上面相同的 f(z)。要查看该函数的虚部和实部并排显示,可以使用cplot.plot_re_im(x,y,f,camp="twilight",contour=False,alpha=0.9):

来自 complex-plotting-tools 的分量绘图
此外,该库还提供了其他酷炫的方式来研究函数,包括流线图:

来自 complex-plotting-tools 的 f(z)流线图
未来方向
这个库展示了很大的潜力,且相对容易使用!它确实需要定义一个pts变量,用于编码给定函数的极点和零点。Wolfram 不需要这个,因为它在幕后计算这些点的位置。如果 complex-plotting-tools 也具备这个功能,将为用户节省大量的精力。我计划在不久的将来将其实现到该模块中。
与此同时,尽情享受使用 Wolfram 和 Python 绘图的乐趣,欢迎在下方评论中分享您的想法和问题,或通过LinkedIn与我联系,或在GitHub与我合作!
除非另有说明,所有图片均由作者创建。
Tokens-to-Token 视觉转换器解析
视觉转换器解析系列
完整解析 Tokens-to-Token 视觉转换器及其优于原始模型的原因
·发表于 Towards Data Science ·20 分钟阅读·2024 年 2 月 27 日
--
自 2017 年《Attention is All You Need》¹推出以来,transformer 已经成为自然语言处理(NLP)领域的最先进技术。2021 年,《An Image is Worth 16x16 Words》²成功地将 transformer 应用于计算机视觉任务。从那时起,许多基于 transformer 的架构被提出用于计算机视觉。
2021 年,Tokens-to-Token ViT:从头开始在 ImageNet 上训练视觉转换器³概述了 Tokens-to-Token(T2T)ViT。该模型旨在消除原始 ViT²中存在的重预训练要求。本文将详细解析 T2T-ViT,包括 T2T-ViT 的开源代码,以及各个组件的概念性解释。所有代码均使用 PyTorch Python 包。

图片来源:Harrison Broadbent 在 Unsplash
本文是一个系列的一部分,深入探讨视觉转换器的内部工作原理。该系列中的每篇文章也提供了可以执行代码的 Jupyter Notebook。系列中的其他文章包括:
-
视觉转换器解析→ Jupyter Notebook
-
视觉转换器的注意力机制解析
-
视觉变换器的位置嵌入解析
-
Tokens-to-Token 视觉变换器解析
目录
-
什么是 Tokens-to-Token ViT?
-
Tokens-to-Token (T2T) 模块
— 软分割
— Token Transformer
— 神经网络模块
— 图像重建
— 全部内容
-
ViT 骨干网络
-
完整代码
-
结论
— 引用
什么是 Tokens-to-Token ViT?
第一批能够在计算机视觉任务中匹配 CNN 性能的视觉变换器需要在大型数据集上进行预训练,然后再迁移到相关的基准任务上²。然而,在这些数据集上进行预训练并不总是可行的。首先,An Image is Worth 16x16 Words中所使用的预训练数据集(JFT-300M 数据集)并未公开²。此外,为了执行其他任务而设计的视觉变换器可能无法获得如此庞大的预训练数据集。
在 2021 年,Tokens-to-Token ViT: 从头开始在 ImageNet 上训练视觉变换器³ 发表,提出了一种方法,可以避免之前视觉变换器在预训练方面的巨大需求。他们通过将 ViT 模型²中的patch tokenization替换为 Tokens-to-Token (T2T)模块,成功实现了这一点。

T2T-ViT 模型图(图来自作者)
由于 T2T 模块是 T2T-ViT 模型的独特之处,因此它将是本文的重点。有关 ViT 组件的深入解析,请参见视觉变换器文章。该代码基于公开的 GitHub 代码 Tokens-to-Token ViT³,并做了一些修改。源代码的更改包括但不限于,修改以支持非方形输入图像,并移除 dropout 层。
Tokens-to-Token (T2T) 模块
T2T 模块的作用是将输入图像处理成可以在 ViT 模块中使用的 tokens。T2T 模块不仅仅是简单地将输入图像拆分成 patches 并将其转化为 tokens,而是通过序列化计算 tokens 之间的注意力,并将其聚合在一起,从而捕捉图像中的额外结构,并减少整体 token 的长度。T2T 模块的示意图如下所示。

T2T 模块图(图像由作者提供)
Soft Split
作为 T2T-ViT 模型中的第一层,soft split 层将图像分割成一系列的 tokens。T2T 图中的 soft split 层以蓝色块的形式展示。与原始 ViT 中的 patch tokenization(更多内容请阅读这里)不同,T2T-ViT 中的 soft split 会创建重叠的 patch。
让我们来看一个 soft split 在像素艺术作品 黄昏中的山脉 上的示例,作品由 Luis Zuno(@ansimuz)创作⁴。原始艺术作品已经被裁剪并转换为单通道图像。这意味着每个像素的值介于零和一之间。单通道图像通常以灰度显示;然而,为了更易于查看,我们将使用紫色配色方案进行展示。
mountains = np.load(os.path.join(figure_path, 'mountains.npy'))
H = mountains.shape[0]
W = mountains.shape[1]
print('Mountain at Dusk is H =', H, 'and W =', W, 'pixels.')
print('\n')
fig = plt.figure(figsize=(10,6))
plt.imshow(mountains, cmap='Purples_r')
plt.xticks(np.arange(-0.5, W+1, 10), labels=np.arange(0, W+1, 10))
plt.yticks(np.arange(-0.5, H+1, 10), labels=np.arange(0, H+1, 10))
plt.clim([0,1])
cbar_ax = fig.add_axes([0.95, .11, 0.05, 0.77])
plt.clim([0, 1])
plt.colorbar(cax=cbar_ax);
#plt.savefig(os.path.join(figure_path, 'mountains.png'), bbox_inches='tight')
Mountain at Dusk is H = 60 and W = 100 pixels.

代码输出(图像由作者提供)
这张图像的尺寸为 H=60 和 W=100。我们将使用一个 patch 大小——也就是 卷积核——为 k=20。T2T-ViT 将 步幅 设置为 s=ceil(k/2),并将 填充 设置为 p=ceil(k/4)。对于我们的示例,这意味着我们将使用 s=10 和 p=5。填充区域的值全部为零,显示为最深的紫色。
在查看 soft split 创建的 patch 之前,我们必须知道将会创建多少个 patch。soft split 层是通过 torch.nn.Unfold⁵ 层实现的。为了计算 soft split 会创建多少个 token,我们使用以下公式:
其中 h 是原始图像的高度,w 是原始图像的宽度,k 是卷积核大小,s 是步幅,p 是填充大小⁵。这个公式假设卷积核是方形的,并且步幅和填充是对称的。此外,还假设膨胀(dilation)为 1。
关于膨胀的补充说明: PyTorch 将膨胀描述为“控制卷积核点之间的间距”⁵,并将读者引导至 这里的图示。 *dilation=1* 的值保持卷积核不变,所有像素都会接触。论坛中的 这个用户 建议将其理解为“每隔 *dilation*个元素就会被使用。”在这种情况下,每个第 1 个元素都会被使用,也就是说每个元素都会被使用。
num_tokens 方程中的第一个项描述了高度方向上有多少个 token,而第二个项描述了宽度方向上有多少个 token。我们在下面的代码中实现了这一点:
def count_tokens(w, h, k, s, p):
""" Function to count how many tokens are produced from a given soft split
Args:
w (int): starting width
h (int): starting height
k (int): kernel size
s (int): stride size
p (int): padding size
Returns:
new_w (int): number of tokens along the width
new_h (int): number of tokens along the height
total (int): total number of tokens created
"""
new_w = int(math.floor(((w + 2*p - (k-1) -1)/s)+1))
new_h = int(math.floor(((h + 2*p - (k-1) -1)/s)+1))
total = new_w * new_h
return new_w, new_h, total
使用 黄昏中的山脉⁴ 示例中的尺寸:
k = 20
s = 10
p = 5
padded_H = H + 2*p
padded_W = W + 2*p
print('With padding, the image will be H =', padded_H, 'and W =', padded_W, 'pixels.\n')
patches_w, patches_h, total_patches = count_tokens(w=W, h=H, k=k, s=s, p=p)
print('There will be', total_patches, 'patches as a result of the soft split;')
print(patches_h, 'along the height and', patches_w, 'along the width.')
With padding, the image will be H = 70 and W = 110 pixels.
There will be 60 patches as a result of the soft split;
6 along the height and 10 along the width.
现在,我们可以看到 soft split 如何从 黄昏中的山脉⁴ 创建 patch。
mountains_w_padding = np.pad(mountains, pad_width = ((p, p), (p, p)), mode='constant', constant_values=0)
left_x = np.tile(np.arange(-0.5, padded_W-k+1, s), patches_h)
right_x = np.tile(np.arange(k-0.5, padded_W+1, s), patches_h)
top_y = np.repeat(np.arange(-0.5, padded_H-k+1, s), patches_w)
bottom_y = np.repeat(np.arange(k-0.5, padded_H+1, s), patches_w)
frame_paths = []
for i in range(total_patches):
fig = plt.figure(figsize=(10,6))
plt.imshow(mountains_w_padding, cmap='Purples_r')
plt.clim([0,1])
plt.xticks(np.arange(-0.5, W+2*p+1, 10), labels=np.arange(0, W+2*p+1, 10))
plt.yticks(np.arange(-0.5, H+2*p+1, 10), labels=np.arange(0, H+2*p+1, 10))
plt.plot([left_x[i], left_x[i], right_x[i], right_x[i], left_x[i]], [top_y[i], bottom_y[i], bottom_y[i], top_y[i], top_y[i]], color='w', lw=3, ls='-')
for j in range(i):
plt.plot([left_x[j], left_x[j], right_x[j], right_x[j], left_x[j]], [top_y[j], bottom_y[j], bottom_y[j], top_y[j], top_y[j]], color='w', lw=2, ls=':', alpha=0.5)
save_path = os.path.join(figure_path, 'softsplit_gif', 'frame{:02d}'.format(i))+'.png'
frame_paths.append(save_path)
#fig.savefig(save_path, bbox_inches='tight')
plt.close()
frames = []
for path in frame_paths:
frames.append(iio.imread(path))
#iio.mimsave(os.path.join(figure_path, 'softsplit.gif'), frames, fps=2, loop=0)

代码输出(图像由作者提供)
我们可以看到,软分割导致了重叠的块。通过计算这些块在图像中移动的过程,我们可以看到图像的高度有 6 个块,宽度有 10 个块,正如预期的那样。通过将这些块展平,我们可以看到得到的令牌。让我们以第一个块为例进行展平。
print('Each patch will make a token of length', str(k**2)+'.')
print('\n')
patch = mountains_w_padding[0:20, 0:20]
token = patch.reshape(1, k**2,)
fig = plt.figure(figsize=(10,1))
plt.imshow(token, cmap='Purples_r', aspect=20)
plt.clim([0, 1])
plt.xticks(np.arange(-0.5, k**2+1, 50), labels=np.arange(0, k**2+1, 50))
plt.yticks([]);
#plt.savefig(os.path.join(figure_path, 'mountains_w_padding_token01.png'), bbox_inches='tight')
Each patch will make a token of length 400.

代码输出(图片由作者提供)
你可以看到令牌中的填充部分!
当传递到下一层时,所有的令牌会聚集在一起形成一个矩阵。该矩阵看起来像这样:

令牌矩阵(图片由作者提供)
对于《黄昏山景》⁴来说,矩阵会是这样:
left_x = np.tile(np.arange(0, padded_W-k+1, s), patches_h)
right_x = np.tile(np.arange(k, padded_W+1, s), patches_h)
top_y = np.repeat(np.arange(0, padded_H-k+1, s), patches_w)
bottom_y = np.repeat(np.arange(k, padded_H+1, s), patches_w)
tokens = np.zeros((total_patches, k**2))
for i in range(total_patches):
patch = mountains_w_padding[top_y[i]:bottom_y[i], left_x[i]:right_x[i]]
tokens[i, :] = patch.reshape(1, k**2)
fig = plt.figure(figsize=(10,6))
plt.imshow(tokens, cmap='Purples_r', aspect=5)
plt.clim([0, 1])
plt.xticks(np.arange(-0.5, k**2+1, 50), labels=np.arange(0, k**2+1, 50))
plt.yticks(np.arange(-0.5, total_patches+1, 10), labels=np.arange(0, total_patches+1, 10))
plt.xlabel('Length of Tokens')
plt.ylabel('Number of Tokens')
plt.clim([0,1])
cbar_ax = fig.add_axes([0.85, .11, 0.05, 0.77])
plt.clim([0, 1])
plt.colorbar(cax=cbar_ax);
#plt.savefig(os.path.join(figure_path, 'mountains_w_padding_tokens_matrix.png'), bbox_inches='tight')

代码输出(图片由作者提供)
你可以看到矩阵左上角和右下角的大面积填充区域,以及其他小块的填充部分。现在,我们的令牌已经准备好,可以传递到下一步骤。
令牌转换器
T2T 模块的下一个组件是令牌转换器,它由紫色块表示。

令牌转换器(图片由作者提供)
Token Transformer 类的代码如下:
class TokenTransformer(nn.Module):
def __init__(self,
dim: int,
chan: int,
num_heads: int,
hidden_chan_mul: float=1.,
qkv_bias: bool=False,
qk_scale: NoneFloat=None,
act_layer=nn.GELU,
norm_layer=nn.LayerNorm):
""" Token Transformer Module
Args:
dim (int): size of a single token
chan (int): resulting size of a single token
num_heads (int): number of attention heads in MSA
hidden_chan_mul (float): multiplier to determine the number of hidden channels (features) in the NeuralNet module
qkv_bias (bool): determines if the attention qkv layer learns an addative bias
qk_scale (NoneFloat): value to scale the queries and keys by;
if None, queries and keys are scaled by ``head_dim ** -0.5``
act_layer(nn.modules.activation): torch neural network layer class to use as activation in the NeuralNet module
norm_layer(nn.modules.normalization): torch neural network layer class to use as normalization
"""
super().__init__()
## Define Layers
self.norm1 = norm_layer(dim)
self.attn = Attention(dim,
chan=chan,
num_heads=num_heads,
qkv_bias=qkv_bias,
qk_scale=qk_scale)
self.norm2 = norm_layer(chan)
self.neuralnet = NeuralNet(in_chan=chan,
hidden_chan=int(chan*hidden_chan_mul),
out_chan=chan,
act_layer=act_layer)
def forward(self, x):
x = self.attn(self.norm1(x))
x = x + self.neuralnet(self.norm2(x))
return x
chan, num_heads, qkv_bias, 和 qk_scale 参数定义了Attention模块的组件。深入探讨 VisTransformers 中的注意力机制最好留到另一个时间。
hidden_chan_mul 和 act_layer 参数定义了神经网络模块的组件。激活层可以是任何torch.nn.modules.activation⁶ 层。norm_layer 可以从任何torch.nn.modules.normalization⁷ 层中选择。
让我们逐步分析图中的每一个蓝色块。我们使用 7∗7=49 作为初始令牌大小,因为第一次软分割的默认卷积核是 7x7。³ 我们使用 64 个通道,因为那也是默认值。³ 我们使用 100 个令牌,因为这是一个不错的数字。我们使用 13 的批量大小,因为 13 是质数,不会与其他参数混淆。我们使用 4 个头,因为它可以将通道分割开;然而,你不会在令牌转换器模块中看到头维度。
# Define an Input
token_len = 7*7
channels = 64
num_tokens = 100
batch = 13
heads = 4
x = torch.rand(batch, num_tokens, token_len)
print('Input dimensions are\n\tbatchsize:', x.shape[0], '\n\tnumber of tokens:', x.shape[1], '\n\ttoken size:', x.shape[2])
# Define the Module
TT = TokenTransformer(dim=token_len,
chan=channels,
num_heads=heads,
hidden_chan_mul=1.5,
qkv_bias=False,
qk_scale=None,
act_layer=nn.GELU,
norm_layer=nn.LayerNorm)
TT.eval();
Input dimensions are
batchsize: 13
number of tokens: 100
token size: 49
首先,我们将输入传递通过一个标准化层,它不会改变输入的形状。接着,它被传递到第一个Attention模块,该模块会改变令牌的长度。请回顾一下,关于 VisTransformers 中 Attention 的更深入解释可以在这里找到。
x = TT.norm1(x)
print('After norm, dimensions are\n\tbatchsize:', x.shape[0], '\n\tnumber of tokens:', x.shape[1], '\n\ttoken size:', x.shape[2])
x = TT.attn(x)
print('After attention, dimensions are\n\tbatchsize:', x.shape[0], '\n\tnumber of tokens:', x.shape[1], '\n\ttoken size:', x.shape[2])
After norm, dimensions are
batchsize: 13
number of tokens: 100
token size: 49
After attention, dimensions are
batchsize: 13
number of tokens: 100
token size: 64
现在,我们必须保存分割连接层的状态。在实际的类定义中,这一步通常在一行中更高效地完成。然而,在本教程中,我们将其单独处理。
接下来,我们可以将其传递通过另一个标准化层,然后进入神经网络模块。标准化层不会改变输入的形状。神经网络也配置为不会改变形状。
最后一步是分割连接,这也不会改变形状。
y = TT.norm2(x)
print('After norm, dimensions are\n\tbatchsize:', x.shape[0], '\n\tnumber of tokens:', x.shape[1], '\n\ttoken size:', x.shape[2])
y = TT.neuralnet(y)
print('After neural net, dimensions are\n\tbatchsize:', x.shape[0], '\n\tnumber of tokens:', x.shape[1], '\n\ttoken size:', x.shape[2])
y = y + x
print('After split connection, dimensions are\n\tbatchsize:', x.shape[0], '\n\tnumber of tokens:', x.shape[1], '\n\ttoken size:', x.shape[2])
After norm, dimensions are
batchsize: 13
number of tokens: 100
token size: 64
After neural net, dimensions are
batchsize: 13
number of tokens: 100
token size: 64
After split connection, dimensions are
batchsize: 13
number of tokens: 100
token size: 64
这就是令牌变换器模块的全部内容。
神经网络模块
神经网络(NN)模块是令牌变换器模块的一个子组件。神经网络模块非常简单,由一个全连接层、一个激活层和另一个全连接层组成。激活层可以是任何torch.nn.modules.activation⁶层,它作为输入传递给该模块。NN 模块可以配置为改变输入的形状,或者保持相同的形状。我们不会逐步讲解这段代码,因为神经网络在机器学习中很常见,而且不是本文的重点。不过,下面会展示神经网络模块的代码。
class NeuralNet(nn.Module):
def __init__(self,
in_chan: int,
hidden_chan: NoneFloat=None,
out_chan: NoneFloat=None,
act_layer = nn.GELU):
""" Neural Network Module
Args:
in_chan (int): number of channels (features) at input
hidden_chan (NoneFloat): number of channels (features) in the hidden layer;
if None, number of channels in hidden layer is the same as the number of input channels
out_chan (NoneFloat): number of channels (features) at output;
if None, number of output channels is same as the number of input channels
act_layer(nn.modules.activation): torch neural network layer class to use as activation
"""
super().__init__()
## Define Number of Channels
hidden_chan = hidden_chan or in_chan
out_chan = out_chan or in_chan
## Define Layers
self.fc1 = nn.Linear(in_chan, hidden_chan)
self.act = act_layer()
self.fc2 = nn.Linear(hidden_chan, out_chan)
def forward(self, x):
x = self.fc1(x)
x = self.act(x)
x = self.fc2(x)
return x
图像重建
图像重建层也作为蓝色块显示在 T2T 图示内。传入重建层的输入形状为(batch, num_tokens, tokensize=channels)。如果我们只查看一个批次,它看起来如下:

单批次令牌(图片由作者提供)
重建层将令牌重新塑形为二维图像,如下所示:

重建的图像(图片由作者提供)
在每个批次中,会有tokensize = channel数量的重建图像。这与图像是彩色的并且有三个颜色通道时的处理方式相同。
重建的代码没有被封装到单独的函数中。然而,下面给出了一个示例:
W, H, _ = count_tokens(w, h, k, s, p)
x = x.transpose(1,2).reshape(B, C, H, W)
其中,W和H分别是图像的宽度和高度,B是批次大小,C是通道数。
综合起来
现在我们准备好一起查看整个 T2T 模块的实现了!T2T 模块的模型类如下所示:
class Tokens2Token(nn.Module):
def __init__(self,
img_size: tuple[int, int, int]=(1, 1000, 300),
token_chan: int=64,
token_len: int=768,):
""" Tokens-to-Token Module
Args:
img_size (tuple[int, int, int]): size of input (channels, height, width)
token_chan (int): number of token channels inside the TokenTransformers
token_len (int): desired length of an output token
"""
super().__init__()
## Seperating Image Size
C, H, W = img_size
self.token_chan = token_chan
## Dimensions: (channels, height, width)
## Define the Soft Split Layers
self.soft_split0 = nn.Unfold(kernel_size=(7, 7), stride=(4, 4), padding=(2, 2))
self.soft_split1 = nn.Unfold(kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
self.soft_split2 = nn.Unfold(kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
## Determining Number of Output Tokens
W, H, _ = count_tokens(w=W, h=H, k=7, s=4, p=2)
W, H, _ = count_tokens(w=W, h=H, k=3, s=2, p=1)
_, _, T = count_tokens(w=W, h=H, k=3, s=2, p=1)
self.num_tokens = T
## Define the Transformer Layers
self.transformer1 = TokenTransformer(dim= C * 7 * 7,
chan=token_chan,
num_heads=1,
hidden_chan_mul=1.0)
self.transformer2 = TokenTransformer(dim=token_chan * 3 * 3,
chan=token_chan,
num_heads=1,
hidden_chan_mul=1.0)
## Define the Projection Layer
self.project = nn.Linear(token_chan * 3 * 3, token_len)
def forward(self, x):
B, C, H, W = x.shape
## Dimensions: (batch, channels, height, width)
## Initial Soft Split
x = self.soft_split0(x).transpose(1, 2)
## Token Transformer 1
x = self.transformer1(x)
## Reconstruct 2D Image
W, H, _ = count_tokens(w=W, h=H, k=7, s=4, p=2)
x = x.transpose(1,2).reshape(B, self.token_chan, H, W)
## Soft Split 1
x = self.soft_split1(x).transpose(1, 2)
## Token Transformer 2
x = self.transformer2(x)
## Reconstruct 2D Image
W, H, _ = count_tokens(w=W, h=H, k=3, s=2, p=1)
x = x.transpose(1,2).reshape(B, self.token_chan, H, W)
## Soft Split 2
x = self.soft_split2(x).transpose(1, 2)
## Project Tokens to desired length
x = self.project(x)
return x
让我们来一步步分析前向传递过程。由于我们已经更深入地检查了各个组件,本节将把它们视为黑盒:我们只会关注输入和输出。
我们将定义一个形状为 1x400x100 的网络输入,用来表示一张灰度(单通道)矩形图像。我们使用 64 个通道和 768 个令牌长度,因为这些是默认值³。我们使用 13 的批次大小,因为它是质数,不会与其他参数混淆。
# Define an Input
H = 400
W = 100
channels = 64
batch = 13
x = torch.rand(batch, 1, H, W)
print('Input dimensions are\n\tbatchsize:', x.shape[0], '\n\tnumber of input channels:', x.shape[1], '\n\timage size:', (x.shape[2], x.shape[3]))
# Define the Module
T2T = Tokens2Token(img_size=(1, H, W), token_chan=64, token_len=768)
T2T.eval();
Input dimensions are
batchsize: 13
number of input channels: 1
image size: (400, 100)
输入图像首先经过一个软分割层,kernel = 7,stride = 4,以及padding = 2。令牌的长度将是卷积核大小(7∗7=49)乘以通道数(灰度输入的通道数 = 1)。我们可以使用count_tokens函数来计算软分割后应该有多少个令牌。
# Count Tokens
k = 7
s = 4
p = 2
_, _, T = count_tokens(w=W, h=H, k=k, s=s, p=p)
print('There should be', T, 'tokens after the soft split.')
print('They should be of length', k, '*', k, '* 1 =', k*k*1)
# Perform the Soft Split
x = T2T.soft_split0(x)
print('Dimensions after soft split are\n\tbatchsize:', x.shape[0], '\n\ttoken length:', x.shape[1], '\n\tnumber of tokens:', x.shape[2])
x = x.transpose(1, 2)
There should be 2500 tokens after the soft split.
They should be of length 7 * 7 * 1 = 49
Dimensions after soft split are
batchsize: 13
token length: 49
number of tokens: 2500
接下来,我们通过第一个Token Transformer。这不会影响批次大小或令牌数,但它会将令牌的长度更改为channels = 64。
x = T2T.transformer1(x)
print('Dimensions after transformer are\n\tbatchsize:', x.shape[0], '\n\tnumber of tokens:', x.shape[1], '\n\ttoken length:', x.shape[2])
Dimensions after transformer are
batchsize: 13
number of tokens: 2500
token length: 64
现在,我们将令牌重建回二维图像。count_tokens函数再次可以告诉我们新图像的形状。它将具有 64 个通道,这与从Token Transformer出来的令牌长度相同。
W, H, _ = count_tokens(w=W, h=H, k=7, s=4, p=2)
print('The reconstructed image should have shape', (H, W))
x = x.transpose(1,2).reshape(B, T2T.token_chan, H, W)
print('Dimensions of reconstructed image are\n\tbatchsize:', x.shape[0], '\n\tnumber of input channels:', x.shape[1], '\n\timage size:', (x.shape[2], x.shape[3]))
The reconstructed image should have shape (100, 25)
Dimensions of reconstructed image are
batchsize: 13
number of input channels: 64
image size: (100, 25)
现在我们又得到一个 2D 图像,我们返回到软分割步骤!接下来的代码块完成了第二次软分割、第二次Token Transformer和第二次图像重建。
# Soft Split
k = 3
s = 2
p = 1
_, _, T = count_tokens(w=W, h=H, k=k, s=s, p=p)
print('There should be', T, 'tokens after the soft split.')
print('They should be of length', k, '*', k, '*', T2T.token_chan, '=', k*k*T2T.token_chan)
x = T2T.soft_split1(x)
print('Dimensions after soft split are\n\tbatchsize:', x.shape[0], '\n\ttoken length:', x.shape[1], '\n\tnumber of tokens:', x.shape[2])
x = x.transpose(1, 2)
# Token Transformer
x = T2T.transformer2(x)
print('Dimensions after transformer are\n\tbatchsize:', x.shape[0], '\n\tnumber of tokens:', x.shape[1], '\n\ttoken length:', x.shape[2])
# Reconstruction
W, H, _ = count_tokens(w=W, h=H, k=k, s=s, p=p)
print('The reconstructed image should have shape', (H, W))
x = x.transpose(1,2).reshape(batch, T2T.token_chan, H, W)
print('Dimensions of reconstructed image are\n\tbatchsize:', x.shape[0], '\n\tnumber of input channels:', x.shape[1], '\n\timage size:', (x.shape[2], x.shape[3]))
There should be 650 tokens after the soft split.
They should be of length 3 * 3 * 64 = 576
Dimensions after soft split are
batchsize: 13
token length: 576
number of tokens: 650
Dimensions after transformer are
batchsize: 13
number of tokens: 650
token length: 64
The reconstructed image should have shape (50, 13)
Dimensions of reconstructed image are
batchsize: 13
number of input channels: 64
image size: (50, 13)
从这个重建的图像中,我们进行最终的软分割。回想一下,T2T 模块的输出应该是一个 token 列表。
# Soft Split
_, _, T = count_tokens(w=W, h=H, k=3, s=2, p=1)
print('There should be', T, 'tokens after the soft split.')
print('They should be of length 3*3*64=', 3*3*64)
x = T2T.soft_split2(x)
print('Dimensions after soft split are\n\tbatchsize:', x.shape[0], '\n\ttoken length:', x.shape[1], '\n\tnumber of tokens:', x.shape[2])
x = x.transpose(1, 2)
There should be 175 tokens after the soft split.
They should be of length 3 * 3 * 64 = 576
Dimensions after soft split are
batchsize: 13
token length: 576
number of tokens: 175
T2T 模块的最后一层是一个线性层,用于将 tokens 投射到期望的输出大小。我们将其设置为token_len=768。
x = T2T.project(x)
print('Output dimensions are\n\tbatchsize:', x.shape[0], '\n\tnumber of tokens:', x.shape[1], '\n\ttoken length:', x.shape[2])
Output dimensions are
batchsize: 13
number of tokens: 175
token length: 768
这就是 T2T 模块的全部内容!
ViT Backbone
从 T2T 模块开始,token 通过 ViT 骨干网络(ViT Backbone)。这与[2]中描述的 ViT 模型的骨干网络相同。你可以在视觉变换器文章中深入了解 ViT 模型和 ViT 骨干网络的实现。下面重现了代码,但我们不会详细讲解。可以在这里查看文章内容,再回来继续阅读!
class ViT_Backbone(nn.Module):
def __init__(self,
preds: int=1,
token_len: int=768,
num_heads: int=1,
Encoding_hidden_chan_mul: float=4.,
depth: int=12,
qkv_bias=False,
qk_scale=None,
act_layer=nn.GELU,
norm_layer=nn.LayerNorm):
""" VisTransformer Backbone
Args:
preds (int): number of predictions to output
token_len (int): length of a token
num_heads(int): number of attention heads in MSA
Encoding_hidden_chan_mul (float): multiplier to determine the number of hidden channels (features) in the NeuralNet component of the Encoding Module
depth (int): number of encoding blocks in the model
qkv_bias (bool): determines if the qkv layer learns an addative bias
qk_scale (NoneFloat): value to scale the queries and keys by;
if None, queries and keys are scaled by ``head_dim ** -0.5``
act_layer(nn.modules.activation): torch neural network layer class to use as activation
norm_layer(nn.modules.normalization): torch neural network layer class to use as normalization
"""
super().__init__()
## Defining Parameters
self.num_heads = num_heads
self.Encoding_hidden_chan_mul = Encoding_hidden_chan_mul
self.depth = depth
## Defining Token Processing Components
self.cls_token = nn.Parameter(torch.zeros(1, 1, self.token_len))
self.pos_embed = nn.Parameter(data=get_sinusoid_encoding(num_tokens=self.num_tokens+1, token_len=self.token_len), requires_grad=False)
## Defining Encoding blocks
self.blocks = nn.ModuleList([Encoding(dim = self.token_len,
num_heads = self.num_heads,
hidden_chan_mul = self.Encoding_hidden_chan_mul,
qkv_bias = qkv_bias,
qk_scale = qk_scale,
act_layer = act_layer,
norm_layer = norm_layer)
for i in range(self.depth)])
## Defining Prediction Processing
self.norm = norm_layer(self.token_len)
self.head = nn.Linear(self.token_len, preds)
## Make the class token sampled from a truncated normal distrobution
timm.layers.trunc_normal_(self.cls_token, std=.02)
def forward(self, x):
## Assumes x is already tokenized
## Get Batch Size
B = x.shape[0]
## Concatenate Class Token
x = torch.cat((self.cls_token.expand(B, -1, -1), x), dim=1)
## Add Positional Embedding
x = x + self.pos_embed
## Run Through Encoding Blocks
for blk in self.blocks:
x = blk(x)
## Take Norm
x = self.norm(x)
## Make Prediction on Class Token
x = self.head(x[:, 0])
return x
完整代码
要创建完整的 T2T-ViT 模块,我们使用了T2T模块和ViT Backbone。
class T2T_ViT(nn.Module):
def __init__(self,
img_size: tuple[int, int, int]=(1, 1700, 500),
softsplit_kernels: tuple[int, int, int]=(31, 3, 3),
preds: int=1,
token_len: int=768,
token_chan: int=64,
num_heads: int=1,
T2T_hidden_chan_mul: float=1.,
Encoding_hidden_chan_mul: float=4.,
depth: int=12,
qkv_bias=False,
qk_scale=None,
act_layer=nn.GELU,
norm_layer=nn.LayerNorm):
""" Tokens-to-Token VisTransformer Model
Args:
img_size (tuple[int, int, int]): size of input (channels, height, width)
softsplit_kernels (tuple[int int, int]): size of the square kernel for each of the soft split layers, sequentially
preds (int): number of predictions to output
token_len (int): desired length of an output token
token_chan (int): number of token channels inside the TokenTransformers
num_heads(int): number of attention heads in MSA (only works if =1)
T2T_hidden_chan_mul (float): multiplier to determine the number of hidden channels (features) in the NeuralNet component of the Tokens-to-Token (T2T) Module
Encoding_hidden_chan_mul (float): multiplier to determine the number of hidden channels (features) in the NeuralNet component of the Encoding Module
depth (int): number of encoding blocks in the model
qkv_bias (bool): determines if the qkv layer learns an addative bias
qk_scale (NoneFloat): value to scale the queries and keys by;
if None, queries and keys are scaled by ``head_dim ** -0.5``
act_layer(nn.modules.activation): torch neural network layer class to use as activation
norm_layer(nn.modules.normalization): torch neural network layer class to use as normalization
"""
super().__init__()
## Defining Parameters
self.img_size = img_size
C, H, W = self.img_size
self.softsplit_kernels = softsplit_kernels
self.token_len = token_len
self.token_chan = token_chan
self.num_heads = num_heads
self.T2T_hidden_chan_mul = T2T_hidden_chan_mul
self.Encoding_hidden_chan_mul = Encoding_hidden_chan_mul
self.depth = depth
## Defining Tokens-to-Token Module
self.tokens_to_token = Tokens2Token(img_size = self.img_size,
softsplit_kernels = self.softsplit_kernels,
num_heads = self.num_heads,
token_chan = self.token_chan,
token_len = self.token_len,
hidden_chan_mul = self.T2T_hidden_chan_mul,
qkv_bias = qkv_bias,
qk_scale = qk_scale,
act_layer = act_layer,
norm_layer = norm_layer)
self.num_tokens = self.tokens_to_token.num_tokens
## Defining Token Processing Components
self.vit_backbone = ViT_Backbone(preds = preds,
token_len = self.token_len,
num_heads = self.num_heads,
Encoding_hidden_chan_mul = self.Encoding_hidden_chan_mul,
depth = self.depth,
qkv_bias = qkv_bias,
qk_scale = qk_scale,
act_layer = act_layer,
norm_layer = norm_layer)
## Initialize the Weights
self.apply(self._init_weights)
def _init_weights(self, m):
""" Initialize the weights of the linear layers & the layernorms
"""
## For Linear Layers
if isinstance(m, nn.Linear):
## Weights are initialized from a truncated normal distrobution
timmm.trunc_normal_(m.weight, std=.02)
if isinstance(m, nn.Linear) and m.bias is not None:
## If bias is present, bias is initialized at zero
nn.init.constant_(m.bias, 0)
## For Layernorm Layers
elif isinstance(m, nn.LayerNorm):
## Weights are initialized at one
nn.init.constant_(m.weight, 1.0)
## Bias is initialized at zero
nn.init.constant_(m.bias, 0)
@torch.jit.ignore ##Tell pytorch to not compile as TorchScript
def no_weight_decay(self):
""" Used in Optimizer to ignore weight decay in the class token
"""
return {'cls_token'}
def forward(self, x):
x = self.tokens_to_token(x)
x = self.vit_backbone(x)
return x
在T2T-ViT 模型中,img_size和softsplit_kernels参数定义了 T2T 模块中的软分割。num_heads、token_chan、qkv_bias和qk_scale参数定义了Token Transformer模块中的Attention模块,这些模块本身位于T2T模块内。T2T_hidden_chan_mul和act_layer定义了Token Transformer模块中的NN模块。token_len定义了T2T模块中的线性层。norm_layer定义了规范化层。
类似地,num_heads、token_len、qkv_bias和qk_scale参数定义了Encoding Blocks中的Attention模块,这些模块本身位于ViT Backbone中。Encoding_hidden_chan_mul和act_layer定义了Encoding Blocks中的NN模块。depth参数定义了ViT Backbone中包含的Encoding Blocks的数量。norm_layer定义了规范化层。preds参数定义了ViT Backbone中的预测头。
act_layer可以是任何torch.nn.modules.activation⁶层,norm_layer可以是任何torch.nn.modules.normalization⁷层。
_init_weights方法为模型训练设置了自定义的初始权重。如果删除此方法,所有学习到的权重和偏置将会被随机初始化。当前实现中,线性层的权重初始化为截断的正态分布;线性层的偏置初始化为零;规范化层的权重初始化为一;规范化层的偏置初始化为零。
结论
现在,你可以在深入理解其原理的基础上,开始训练 T2T-ViT 模型了!本文中的代码可以在GitHub 仓库中找到。T2T-ViT 论文³中的代码可以在这里找到。祝你变换愉快!
本文已由洛斯阿拉莫斯国家实验室批准发布,编号为 LA-UR-23–33876。相关代码已获 BSD-3 开源许可证批准,编号 O#4693。
引用
[1] Vaswani 等人(2017)。Attention Is All You Need。doi.org/10.48550/arXiv.1706.03762
[2] Dosovitskiy 等人(2020)。一张图片等于 16x16 个词:用于大规模图像识别的 Transformers。doi.org/10.48550/arXiv.2010.11929
[3] Yuan 等人(2021)。Tokens-to-Token ViT:从头开始在 ImageNet 上训练 Vision Transformers。doi.org/10.48550/arXiv.2101.11986
→ GitHub 代码:github.com/yitu-opensource/T2T-ViT
[4] Luis Zuno (@ansimuz)。黄昏山脉背景。许可证 CC0: opengameart.org/content/mountain-at-dusk-background
[5] PyTorch。Unfold。pytorch.org/docs/stable/generated/torch.nn.Unfold.html#torch.nn.Unfold
[6] PyTorch。非线性激活(加权和,非线性)。pytorch.org/docs/stable/nn.html#non-linear-activations-weighted-sum-nonlinearity
[7] PyTorch。归一化层。pytorch.org/docs/stable/nn.html#normalization-layers


浙公网安备 33010602011771号