TowardsDataScience-博客中文翻译-2022-四十-

龙哥盟 / 2024-10-23 / 原文

TowardsDataScience 博客中文翻译 2022(四十)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

峰值单词和单词难度

原文:https://towardsdatascience.com/peak-wordle-word-difficulty-64907be4c177

根据《纽约时报》最近的调整,对 700 万个单词游戏的分析告诉了我们什么关于单词难度

发布到 Twitter 上的 Wordle 游戏蒙太奇。图片作者。

自从最近被纽约时报收购后,关于 Wordle 是否改变了已经有不少报道了。一些人声称游戏变得更难了,关于 NYT 是否修改了游戏使其更具挑战性的猜测越来越多。虽然 NYT 承认对游戏的单词表做了一些小的调整,但他们否认以任何方式改变游戏。

“游戏的玩法没有任何改变,”《纽约时报》通讯主管乔丹·科恩(Jordan Cohen)在一封电子邮件中说。— 卫报

查看游戏的源代码没有任何编码变化的迹象,但是对使用的文字有一些小的调整。例如,“AGORA”这个词被删除了,这引起了一些关注,因为人们仍然在使用旧版本的 Wordle 为“AGORA”演奏,而我们其他人则在闪亮的新 NYT 版本上为“AROMA”演奏。

最近引起一些玩家愤怒的词——ULTRA、CAULK 和犬儒主义者——从一开始就被放进了 Wordle,因此与新的 NYT 版本无关。这是否意味着目前的争议只不过是茶杯里的风暴,或者是人们对变化反应不好的例子,或者只是通常的单词难度的随机波动?

我认为再看看人们在 Twitter 上分享的游戏能告诉我们这方面的信息会很有趣。

R 最近,我写了一篇关于在 Twitter 上分享的 Wordle 游戏的大规模分析的文章(这里是这里是和这里是),使用的是一月份收集的大量数据;Twitter 好心地给了我一张学术许可证,以便大规模收集推文。经过几个星期的游戏,这个数据集现在包括了近 130 万不同玩家玩的 700 多万个游戏。

在最近的一篇文章中,我注意到 Wordle 的用户群在稳步增长,至少在 Twitter 上分享游戏的用户方面是如此。然而,下面更新的图表表明,我们可能正在经历峰值,因为 Twitter 上每日分享的游戏总数在 2 月 1 日达到最大值——NYT 宣布收购的第二天——此后一直在下降。此外,这种下降在 twitter 上发布的游戏总数(下面的橙色+蓝色条)和在 Twitter 上发布第一个游戏的玩家总数(蓝色条)中都很明显。

2021 年 12 月 23 日至 2022 年 2 月 16 日期间在 Twitter 上发布 Wordle 游戏的玩家(新玩家和回归玩家)数量。线形图显示了同一时期每个玩家发布到 Twitter 上的平均游戏数量。作者配图。

我们达到顶峰了吗?或者这仅仅是 Twitter 帖子数量的下降?这很难说,但毫无疑问,时间会证明一切。至少这一结果表明,Wordle 的增长目前可能已经稳定,但我怀疑它将在未来几天回到一个更积极的领域。

但是今天真正的问题是 NYT 的摆弄是否像一些人声称的那样导致了更难的单词被使用。我们能在 Twitter 数据中看到任何证据吗?最近的单词比合理预期的更难解决吗?

在我最近的分析中,我根据 Twitter 数据研究了两种评估单词难度的不同方法:

  1. 最简单的方法是查看每个目标单词的平均游戏长度,理由是更短的游戏指向更简单的单词。问题是,一个简单的平均值可以掩盖很多正在发生的事情。
  2. 一个更有趣且可能有用的方法是比较游戏(最多 3 次猜测的游戏)和游戏(需要 6 次猜测的游戏)的比例。如果一个单词主要与短游戏相关联,那么它比一个主要与长游戏相关联的单词更容易。

下图显示了从 12 月 23 日到 2 月 16 日(昨天撰写本文时)官方目标词的这些衡量标准。所有这三项指标都有相当大的差异。一些单词有许多短游戏而相对较少的长游戏(例如,SIDE,TAPIR,POINT,LIGHT),而另一些单词有大得多的长游戏而较少的短游戏(例如,FERRY,GORGE,ABBEY,PROXY,ELDER,犬儒派)。

从 2021 年 12 月 23 日到 2022 年 2 月 16 日,目标词的短/长游戏和平均游戏长度的分数。作者配图。

要将此转换为单一的难度衡量标准,使用单词的长短局比率是有意义的;我们从结果中减去 1,这样,对于一个给定的单词,难度比> 0 意味着长游戏比短游戏多,反之亦然。

这个难度比显示在下面,根据难度比是正还是负,帮助突出显示最难的单词(红色)和最容易的单词(蓝色);这些条也是用颜色编码的,所以颜色的强度反映了实际难度比的大小。该图还覆盖了每个单词的平均游戏时长和平均游戏时长的 14 天滚动平均值。

2021 年 12 月 23 日至 2022 年 2 月 16 日目标词的难度比。还显示了每个单词的平均游戏长度(虚线)以及平均游戏长度的 14 天滚动平均值(虚线)。作者配图。

现在我们可以清楚地看到最难的单词(渡口、峡谷、修道院、代理人、克诺、长老和愤世嫉俗者)以及几个边界情况(围攻和青睐),它们的难度比率勉强高于 0。

我之前提到过这些难词更有可能有重复字母,最近的难词也是如此(ELDER 和犬儒派)。作为题外话,我之前也向展示过难词更有挑战性,因为它们包括罕见的字母和不寻常的字母组合;犬儒派中的“CY”就是一个很好的例子。

As 是否有证据表明 Wordle 变得更具挑战性?在我看来,这并没有得到 Twitter 数据的支持。游戏时长的 14 天滚动平均值保持非常稳定,但更具挑战性的单词确实会不时出现。一月份有四个,到目前为止,我们在二月的前半个月有两个,尽管二月份的单词(长者和愤世嫉俗者)的难度比率并不是特别高,至少与一月份最具挑战性的单词相比是这样。

当然,这可能是我们从 Wordle 的随机目标词列表中所期待的。事实上,至少对于最近一些更有争议的词汇——ULTRA 和 CAULK 跃入脑海——数据中没有任何东西让它们显得特别困难。事实上,这两个词的短博弈比长博弈多得多,所以大多数人似乎都能应付自如。

如果有什么不同的话,这些结果表明,在一月底和二月初之间,难词变少了。诺尔和艾尔德之间有整整两个星期,这是迄今为止两个难词之间最长的间隔。从随机性的角度来看,这没有什么不寻常的,但也许玩家已经习惯了一串挑战性较小的单词,所以当一个更具挑战性的单词出现时,他们会措手不及。

至少就目前而言,我认为可以有把握地断定新词就像原词。它的包装有点不同,但游戏性是一样的,文字是一样的(除了一些小的例外),明天会和昨天一样有趣。享受吧。

如果你想更多地了解 Wordle 的数据能告诉我们人们如何打球,你可以更有效地打球,那么看看我最近的帖子:

  • 我从玩了一百多万场 Wordle 游戏中学到了什么
  • 如何在 Wordle 中猜对
  • 三百万条 Wordle Tweets 之后:对 Tweets 的分析揭示了关于 Wordle、它的词汇和它的玩家的什么。
  • 沃尔得开局不利的代价以及如何避免。

在断奏中揭开浓缩咖啡萃取的神秘面纱

原文:https://towardsdatascience.com/peeling-back-the-mystery-of-espresso-extraction-in-staccato-14a0645238a4

咖啡数据科学

在断奏中揭开浓缩咖啡萃取的神秘面纱

更深入理解分层的一次性测试

我以前检查过断奏和断奏捣实浓缩咖啡的逐层提取,但那些镜头通常是 3 比 1(输出对输入)。我的大部分镜头都是在 1:1 和剩下的 3:1 之间分割,因为我用的是杠杆机,很难在 1:1 停止拍摄。所以我用两杯,第二杯不喝。

经过一番深思熟虑和排练,我决定在 1:1 的断奏镜头后立即关闭 portafilter。然后我可以切开圆盘,一层一层地测量提取了多少咖啡。

设备/技术

浓缩咖啡机:金特快

咖啡研磨机:小生零

咖啡:家庭烘焙咖啡,中杯(第一口+ 1 分钟)

镜头准备:断奏

预灌注:长

输液:压力脉动

过滤篮 : 20g VST

其他设备: Atago TDS 计、 Acaia Pyxis 秤

这一枪

我准备了一个断奏镜头(细 10.1g,粗 6.6g,中 5.3g)。然后我用 20 秒的预灌注时间开始注射,总共用时 28 秒。

所有图片由作者提供

然后我很快地把便携式过滤器从机器里拿出来,弄得一团糟。

我让冰球晾干,然后我看了看。

我小心翼翼地在托盘上切了几层。

我在 93 度的烤面包机里把它烘干了。

左图:干燥前。右图:干燥后

衡量

总溶解固体量 (TDS)使用折射仪测量,该数值结合咖啡的输出重量和输入重量,用于确定提取到杯中的咖啡百分比,称为提取率(EY)。

【gTDS】咖啡渣 TDS 是通过取一些咖啡渣,将它们放在数字折光仪上(在这种情况下是 Atago),加入少量水,并测量 TDS 来确定的。这让我们知道还有多少可溶物在地下。

我测量了拍摄的 EY,然后我称量了干燥的废地面,以确定每层 EY 的估计值。我还对综合 EY 进行了估算。请记住,拉下 portafilter 不是即时的,所以有一些咖啡没有被收集。因此,这些值应该与从干燥地面测得的总 EY 进行比较。

我还测量了三层的 GTD,并将这些测量值与干燥层进行了比较。较高的 gTDS 表示提取较少。

底部精细,中间粗糙,顶部中等

对于细粒层,gTDS 较高,干 EY 较低。中间层很有趣,因为干 EY 很低,但提取也很低。

这些结果与 3:1 的结果不一致,该结果显示精细层的提取率高于顶部两层。然而,从的另一个实验中,我发现咖啡渣同样可以吸收可溶物,所以很有可能粗中层的可溶物停留在细层。这是很有可能的,因为从 TDS 中,只有 19%被提取出来,或者如果你想考虑杯子中没有收集到的液体,只有 20%。一般来说,这个节目会以 3:1 的比例抽取 28%左右。

这个实验是对冰球的一次小小窥视,以了解发生了什么。更好的办法是在冰球里放一个折射计来测量整个击球过程中的 TDS。espresso puck 的神秘非常引人注目,但要完全理解却非常具有挑战性。

如果你愿意,可以在推特、 YouTube 和 Instagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在中和订阅。

我的进一步阅读:

我未来的书

浓缩咖啡系列文章

工作和学校故事集

个人故事和关注点

乐高故事启动页面

摄影飞溅页面

改善浓缩咖啡

断奏生活方式概述

测量咖啡磨粒分布

浓缩咖啡中的粉末迁移

咖啡萃取

咖啡烘焙

咖啡豆

浓缩咖啡用纸质过滤器

浓缩咖啡篮及相关主题

意式咖啡观点

透明 Portafilter 实验

杠杆机维护

咖啡评论和想法

咖啡实验

你可能认识的人:聚集用户的算法背后

原文:https://towardsdatascience.com/people-you-may-know-behind-the-algorithms-that-bring-users-together-a7e1a2b8fec0

机器学习模型如何向人们推荐人

由创意基督徒在 Unsplash 上拍摄的照片

联系是社交媒体的核心。基本的价值假设是连接的用户更投入,投入的用户更有可能回来。“我们怎样才能让用户联系得更紧密,”因此是社交媒体应用中最关键的问题之一。答案是一类被称为“你可能认识的人”,或 PYMK 的算法,它向你显示你最有可能联系的用户候选人的定期更新列表。

脸书对 PYMK 的解释如下:

“你可能认识的人”是你可能想交朋友的人的列表,因为你们有共同点,比如共同的朋友、学校或工作场所。

LinkedIn 写道:

“我的网络”页面上的“您可能认识的人”功能会向您推荐 LinkedIn 会员。这些推荐是基于你和其他 LinkedIn 成员之间的共同点,以及你从电子邮件和手机通讯录中导入的联系人。

这是推特:

Twitter 的账户建议是基于为你做出个性化建议的算法。

还有抖音:

为了帮助您在抖音上找到并关注您可能认识的人,我们可能:
通过向您显示他们的视频和您个人资料中的“查找朋友”功能,向您推荐帐户。向抖音上的其他人推荐您的帐户,以便他们可以关注您。

但是这些算法到底是如何工作的呢?引擎盖下发生了什么?在这篇文章中,我们将深入探讨

  • PYMK 的基本机器学习模型架构,
  • 模型服务考虑事项,
  • 重新排序方法,以及
  • 对 PYMK 未来的展望。

让我们开始吧(在简短的免责声明之后)。

免责声明:PYMK 是有争议的。一些用户不喜欢 PYMK,因为他们或者不想在他们的社交媒体订阅中看到某些人,或者出于隐私考虑不想向其他人展示,或者两者兼而有之。这些是产品方面的考虑,而不是技术方面的考虑,因此超出了本文的范围。在这里,我们将保持它的技术性。

PYMK 的基本模型架构

与其他排名模型类似,PYMK 算法的一个好主意是两阶段建模方法,由候选人选择(第一阶段)和候选人排名(第二阶段)组成。第一阶段将整个用户群过滤为更易管理的候选数量(例如从 10 亿到 1000),第二阶段根据连接概率,即两个用户连接的可能性,对这些候选进行排序。然后向用户显示排名最高的候选项,其中一个按钮用于“连接”,另一个按钮用于“忽略”。

接下来让我们放大到这两个阶段。

**候选人选择。**也许最简单也是最直观的候选人选择启发法是“朋友的朋友”(FOF):考虑所有二级亲属关系作为可能的 PYMK 候选人。事实证明,这种启发实际上非常强大:脸书 2010 年的公开幻灯片解释说,92%的新连接来自用户的(平均)17K FOFs 之一。换句话说,FOF 候选人选择将 PYMK 候选人池减少了 6 个数量级(假设整个用户数以十亿计),同时仍然保留了 92%的召回率。 LinkedIn 也使用 FOF 候选人选择,他们将它与其他启发式方法相结合,如选择同事的所有关系。

排名。我们可以用二进制分类模型对 PYMK 候选者(在第一阶段提取)进行排名,其中排名仅仅是模型分数本身:分数越高,连接的概率越高。在离线模型训练期间,我们可以训练模型来预测用户之间的现有连接。然后,在推理时,我们可以对用户的所有 PYMK 候选项进行评分,并显示列表中的前 N 名候选项。

为了训练这样一个二元分类器,我们需要一组丰富多样的特征。这些可以包括:

  • 公共连接的数量,
  • 无论两个用户是否在同一雇主处工作,或者过去是否在同一雇主处工作,
  • 这两个用户是否去了同一所学校,
  • 任何共同兴趣、团体、技能或爱好的存在,
  • 两个用户是否有共同的职称,
  • 用户位置之间的物理距离,
  • 两个用户发布的文本内容的语义相似性(使用某种形式的文本嵌入),或者
  • 两个用户在帖子中是否被一起标记。
  • 两个用户是否喜欢对方的帖子。

最后,我们应该跟踪哪些指标来评估 PYMK 管道的绩效?对于候选人选择,我们需要知道召回率,比如上面提到的 FOF 选择的 92%召回率。对于分类器,我们希望使用 ROC-AUC 或 PR-AUC 等指标来量化模型性能。一旦该模型被部署,一个重要的指标可以是点击率(精确度的代表)以及 PYMK 覆盖率,即所有因 PYMK(召回的代表)而建立的连接的比率。一个“完美的”PYMK 算法将预见所有新的连接,并因此实现 100%的 PYMK 覆盖。

模型服务注意事项

在推理时,我们希望向用户显示前 N 名 PYMK 候选人,例如前 10 名。如果用户从该列表中选择或删除了候选人,我们将希望立即从剩余的候选人池中回填该列表。

一种方法是离线计算,我们离线计算每个用户的 PYMK 列表,并将其存储在数据库中。然后,一旦用户访问应用程序,我们可以直接从存储中提取他们的个人 PYMK 列表。这种离线计算可以每天运行。

离线计算的优点是低延迟,因为在服务时间,我们只需要从数据库中读取候选列表,而不是运行排名模型。然而,缺点是我们浪费了计算资源:例如,如果在某一天只有 1%的用户打开他们的应用程序,那么那天用于计算 PYMK 的 99%的计算资源都被浪费了。LinkedIn 的工程师报告说广泛的离线计算对他们来说变得“非常昂贵”,因此他们离开了它。

另一种方法是在线计算,一旦用户打开应用程序,我们就运行整个模型(候选人选择和排名)。这解决了资源浪费的问题:如果用户不打开应用程序,那么就不会为该用户进行计算。然而,在线计算增加了延迟,因此保持排名模型轻量级是很重要的,例如具有很少参数和低延迟的简单逻辑回归模型。深度神经网络在这种情况下可能不会很好地工作,因为它可能会增加太多的延迟。

另一个需要考虑的是多长时间再培训一次 PYMK 排名模型。一般来说,较新的模型比陈旧的模型表现得更好,因为社交图是动态的,而不是静态的:每天都有新用户和新联系加入其中。LinkedIn 的工程师报告说,从历史上看,他们的 PYMK 计算需要几天时间,并被存储到磁盘上,这意味着推荐在推断时已经过期几天了。在一个小规模的概念验证中,他们能够证明具有新数据的推荐表现得更好(“至少在过去三四年中最大的提升之一”)。这一发现激励他们从 Hadoop 中迁移出来,构建 Gaia,这一服务使他们能够以更有效的方式运行重量级的基于图形的算法,如 PYMK。

重新排序方法

这里概述的候选选择+排序方法的问题是,这种方法优化了全局性能,但该算法仍可能导致某些用户不希望的结果。这些不希望的结果可以通过重新排序来解决,我们根据某些启发式规则重新排序 PYMK 列表。换句话说,重新排序是对指定不足的建模目标的特别修正。让我们来看看 LinkedIn 正在生产中使用的两种重新排名算法,印象折扣和参与度重新排名。

**印象折扣。**LinkedIn 的 PYMK 算法的一个问题是,知名度高的用户(如行业影响者)会收到大量的连接请求。我个人可以证实这一点:我已经收到了数百个 LinkedIn 连接请求,这些请求来自我完全没有关系的人。如果这种请求泛滥隐藏了来自我确实认识并且想要联系的人的请求,那就特别麻烦。

LinkedIn 用他们所谓的“印象折扣”来解决这个问题。其基本思想是根据所示候选人的未决请求总数重新排列 PYMK 结果。因此,LinkedIn 不是按 p 给 PYMK 排名,而是按 p/N 之类的东西给他们排名,其中 p 是联系的概率(根据机器学习模型分数计算),N 是用户已经收到的未完成邀请的数量。

印象折扣有用吗?看起来是这样的:在一项 A/B 测试中(我在这里提到过),LinkedIn 显示,印象折扣将“超载”用户的数量减少了 50%,即那些报告他们收到太多邀请的用户。

**定亲。**LinkedIn 的 PYMK 算法的另一个问题是,根据定义,训练数据偏向于高参与度用户(称为频繁会员,FMs),而不利于低参与度用户(称为非频繁会员,IMs)。结果,PYMK 列表将包含比 IMs 多得多的 FM,为 IMs 创建一个恶性循环:低参与度导致更少的连接请求导致更低的参与度,等等。

这个问题可以通过“参与度重新排序”来解决:在我们有了初始的 PYML 列表、向上排序的 im 和向下排序的 FM 之后,向用户显示的最终列表包含 50%的 FM 和 50%的 im。

使用 A/B 测试,LinkedIn 显示,参与度重新排名使发送到即时消息的邀请增加了约 5%。有趣的是,发送给 FMs 的邀请并没有减少:这些用户可能是如此受欢迎,以至于即使人为地将他们在 PYMK 排名靠后也没有改变他们收到的请求数量。因此,总体而言,参与度重新排名解决了它想要解决的问题,即减轻 PYMK 对 IMs 的偏见。

照片由本杰明·戴维斯在 Unsplash 上拍摄

对未来的展望

社交媒体在过去的几年里经历了快速的变化,它的未来还未确定——事实上,它现在正在展现。

抖音的崛起表明,用户实际上可能更喜欢从整个平台上通过算法选择的内容,而不是只来自他们自己的社交网络的内容。企业家 Michael Mignano 称这种发展为“社交媒体的终结和推荐媒体的兴起”。推荐媒体不太强调朋友和社交网络。相反,内容本身,以及它与用户偏好的匹配程度,是在整个平台上分发内容的最重要因素。米格纳米写道:

推荐媒体来了。因此,对于我们如何、何时以及为什么消费内容,我们将做出更少的明确选择(“这些是我的朋友”)和更多的隐含选择(“这是算法建议我应该花费注意力的地方”)。

那么,也许社交媒体的未来没有明确的联系,因此没有 PYMK 算法。

在你走之前…

享受这个内容?在我的 个人资料页面 找到更多我的文章。在 Medium 上关注/订阅,这样你就不会错过我以后写的新帖。 成为中等会员 这样就可以无限查看文章了。并确保在LinkedIn和/或Twitter上关注我!

PeopleSansPeople:在 Unity 中生成虚拟人的合成数据

原文:https://towardsdatascience.com/peoplesanspeople-generating-synthetic-data-of-virtual-human-beings-in-unity-a1847a56895c

为计算机视觉创建人类的 3D 模型从未如此简单

使用 PeopleSansPeople 项目创建的超现实合成场景(由作者创建)。

介绍

获取大量高质量的标注训练数据是很难的。收集高质量的数据、收集足够的数据、招募并培训标注团队、确保标注干净一致、确保其充分代表模型在现实世界中可能遇到的各种情况,等等。需要大量的时间和努力。当这些数据涉及到人类时,事情变得更加棘手。现在,你有责任确保你的数据不侵犯隐私,不包含个人身份信息,你可以与你的标签共享数据,并在年龄,性别,种族,文化等方面保持平衡。你用这些数据训练的任何模型也要尊重这些道德界限。

难怪人们对使用模拟来生成大量合成训练数据的兴趣正在稳步增长。但是计算机渲染的数据能像真实世界一样有效吗?

通过在虚幻引擎中创建高质量的模拟, AI 的研究人员。Reverie 展示了他们可以将所需的真实世界训练数据量减少 90%,同时实现几乎相同的性能。也就是说,在真实世界的数据集中,你需要的每十张图片中就有一张[ 1 ]。谷歌的研究人员证明,通过在一个具有 ImageNet 权重[2的模型上使用合成数据进行微调,可以在没有任何标记的训练数据的情况下近似得到相同的结果*。合成数据的使用甚至已经破坏了行业,包括亚马逊 Go 商店、丰田和 OpenAI 等等。合成数据也被用来补充真实世界的训练数据,以训练谷歌 MediaPipe 框架的一部分 Objectron 中的 3D 边界框检测模型,从而将其准确性提高了 10%。*

为什么是合成数据?

越来越明显的是,投资合成数据可以大大加快你的 ML 研究。以下是学习创建自己的合成数据对您有所帮助的几个原因:

  • 以微不足道的成本生成无限量的训练数据
  • 假设-测试新想法,无需花费时间/金钱来获取和标记数据
  • 标签是由一个确定性的过程生成的,该过程确保标签是正确的(即使是广泛使用的 ML 基准数据集也充满了错误
  • 轻松对传感器、环境和姿势等因素进行消融研究(如 Hinterstoisser 等人[ 2 ])
  • 模拟罕见但重大的“黑天鹅”事件(如车祸)
  • 实现可比的性能,同时大幅减少所需的真实训练数据
  • 通过分布模拟[ 3 ]快速加快强化学习的速度

然而,虽然标记真实世界的数据花费大量的时间和金钱,但是为单个 ML 任务创建模拟也不是一件小事。它需要具备专业技能的工程师和 3D 艺术家的投入,他们了解要解决的问题的复杂性。众所周知,创造虚拟人更加困难(至少是那些避开恐怖谷的人)。有许多公司提供合成数据生成服务,如 AI。遐想、数据生成和动态视觉。我们自己生成合成数据而不必马上参与合作关系,这不是很好吗?

Unity 技术公司

Unity 是世界上最受欢迎的跨平台游戏引擎之一的制造商,最近发布了一个名为 Unity Perception 的开源工具包,将合成标签放在你自己的手中。Unity Perception toolkit 抽象了标签生成过程,甚至允许 Unity 业余爱好者配置 Unity 场景,该场景可以使用 2D 边界框、3D 边界框、语义分割遮罩、实例分割遮罩、对象计数等生成无数图像。

统一计算机视觉:增压你的计算机视觉训练(YouTube)。为了更深入地展示统一感知的可能性,请查看来自标准认知的案例研究。

此外,他们在此基础上再次扩展,创建了people sans people[4](PSP),这是一套建立在感知基础上的额外工具,可以生成 2D 和 3D 边界框,实例和语义分割遮罩,以及用于操纵人类 3D 模型的 COCO 姿势标签。

使用 Unity Perception 和 PeopleSansPeople 生成的合成数据的催眠蒙太奇。

SynthDet:合成数据的早期尝试

如果你看了上面的视频,你可能会问自己:为什么这些合成图像看起来如此…迷幻?合成训练数据不应该看起来更像真实生活吗?答案在 2019 年的一篇论文中,名为“保存的注释是获得的注释:使用完全合成训练进行对象实例检测” 5 。简而言之,作者发现,与其试图逼近目标域(由于神经网络强大的记忆技能,不可避免地会出现不足),不如积极地随机化训练数据更有效。此后不久,Unity 发布了一个名为 SynthDet 的开源项目,该项目使用感知包实现了论文中描述的方法。要深入解释为什么这种通常被称为域随机化的策略如此有效,请看一下Lilian Weng的这篇精彩文章。

在本教程中,我们将进一步探索这一技术,利用 Unity Perception、PeopleSansPeople 和第三个名为 MakeHuman 的工具来为机器学习生成合成人类的标记训练数据。本教程面向 ML 从业者,假设没有 3D 艺术或游戏开发经验。请注意,我们将在本教程中假设一个基于 Windows 的系统,但是所有的工具都应该与 Linux 兼容。本教程也不需要任何编码经验,因为不会有任何代码!让我们开始吧。

制造人类

从 Mixamo 到 Turbosquid , Sketchfab 到 CGTrader ,现在有很多很多的方法来获取 3D 模型。你可以使用一个低级但功能强大的工具,如 Maya 或 Blender,自己制作它们。甚至还有一些专门用于制造人类的专有工具,如 Reallusion 的角色创造者或 Epic Games 的超能力者创造者。然而,当你想尝试看看什么是有效的,为什么不保持快速和负担得起的东西呢?

进入 MakeHuman ,一款免费开源的工具,用于快速创建人类的参数化 3D 模型。这个工具可以追溯到 2000 年,它允许你制作并导出一个全套装备的人体模型,带有高级滑块,就像你在视频游戏中创造一个角色一样。还有一个活跃的社区,以防你需要任何具体的帮助。他们的代码是在 GNU AGPL 许可证下提供的,其产生的所有资产都受到 CC0 许可证的保护(更多细节,请参见此处的)。

设置 MakeHuman

使用 MakeHuman 非常简单。首先通过从下载页面下载稳定版进行安装。在 Windows 上,只需解压下载文件并执行附带的*。exe* 文件。在 Linux 上,有 PPA 安装指令。现在启动程序。你应该会看到一个相当橙色的默认人类和一些滑块在左边,如下所示。

启动 MakeHuman 时首先看到的是。我继续添加衣服到我的,以保持东西 SFW,但我会得到这一点(由作者创建)。

UI 由左右两侧的设置栏和顶部的三个工具栏组成。第一行工具包括常见的新建/保存/打开按钮、撤消/重做,以及一些视图选项,如切换网格或查看网格。第二行是 MakeHuman 中大多数工具的存放位置。创建新模型时,通常会沿着这些选项卡从左向右移动。第三行根据第二行的上下文进行更改。接下来,我们将创建一个非常快速的人类模型进行演示。

创造一个人类

在“建模”选项卡中,您可以编辑主要属性,如身高和年龄,或者更深入地研究面部、躯干、手臂和腿等。请注意当您选择这些选项卡时,右列中的选项是如何变化的。这些给了你对模型非常精细的控制。还有一个随机选项卡,可以随机修改所有这些属性。正如你所料,结果并不总是很好,但是为了时间和乐趣,我们就这样吧。按“随机化”几次,直到你对结果满意。

现在,直接跳到“几何图形”选项卡。在这里,您可以编辑基础服装,并为各种头发、眼睛、牙齿和更多选项选择基础几何体。股票期权是有限的,但是你可以下载更多的期权或者用各种插件比如 Blender MakeClothes 2 插件自己制作。把这些也浏览一遍,挑出你喜欢的。只要确保选择至少一件衣服,因为我将展示我们如何在 Unity 中随机化纹理。

接下来,“材料”这些选项会替换皮肤、头发、眼睛等的纹理。再一次,选择你喜欢的,继续前进。在“姿势/动画”选项卡上,我们只想改变一件事。选择“骨骼”选项卡,并将装备预设更改为“游戏引擎”这个装备将使我们能够统一的动画角色。这样我们就可以完成对我们的基本角色的编辑了。10 分钟 0 美元还不算太寒酸!

一个来自 MakeHuman 的完全建模、装配和纹理化的模型,名为“Mark”(由作者创建)。

输出你的人类

现在让我们导出。在“Files”选项卡下,选择“Save”选项卡来保存我们的模型,以防我们以后需要进行更改。给你的人类一个“人类名”和一个文件名。两个字段我都选了“马克”,当然也选自己喜欢的。如果你想做更多,你可能希望使用一个命名约定。现在选择“导出”选项卡。在“网格格式”下,选择“Filmbox (fbx)”,这是 Autodesk 制作的一种常见文件格式。在右栏中,确保选中“地面英尺”并将“比例单位”设置为“米”,因为 Unity 使用默认的米单位。我们还设置了“脚踏实地”,这样 Unity 中的位置坐标将与他们的脚的位置相对应,使得在场景中放置和移动我们的人变得更加容易。现在输出你的*。fbx* 文件,我们完成了这一步。

人民

如前所述,PeopleSansPeople 是由 Unity Technologies 创建的一个开源工具,用于创建域随机化的合成人的注释数据。我们可以从 GitHub 下载这个工具。进入 GitHub 项目页面和git clone或将该项目下载到您电脑上合适的位置。回想下一步这个项目在哪里。

接下来我们需要安装 Unity,特别是 Unity Hub。如果您已经安装了 Unity Hub,请跳过此部分。

安装 Unity 集线器

Unity Hub 是一个版本和项目管理程序,可组织您电脑上的所有 Unity 项目和版本。强烈建议第一次使用 Unity 的用户使用。从 Unity 下载页面下载 Unity Hub 并启动。您应该看到左边有四个选项卡,包括“项目”,右边有两个按钮,分别是“添加”或“新建”PSP 项目预先配置了 Unity 场景,所以点击“添加”打开之前下载的文件夹,选择子文件夹peoplesanspeople_unity_env,点击“选择文件夹”这可能需要一个你还没有的 Unity 版本(在撰写本文时是 2020.3.20f1 )。Unity Hub 会给你一个安装它的选项。使用默认选项安装,然后打开项目。

PeopleSansPeople 项目

第一次加载这个项目可能需要几分钟,但是一旦完成,你应该会看到 Unity 编辑器。

默认的 Unity 编辑器(由作者创建)。

Unity 是一个复杂而强大的程序,可能有点令人望而生畏,但我会尽量保持它的最小化,只是为了让你开始。有很多关于正确使用的很棒的资源,包括 Unity 自己的教程,如果你更倾向于使用它的话。现在,要知道 Unity 项目被分组在所谓的“场景”中这些基本上是定义对象的布局和行为(2D 或 3D)的设置。想象一下电子游戏中不同的“关卡”。场景可以像视频一样播放,这就是顶部的大播放和暂停按钮的作用。默认情况下,你应该看到一个空的场景。让我们打开这个项目的“人类场景”。在底部的文件管理器中,双击名为“场景”的文件夹,然后双击名为“人类场景”的文件

这就是奇迹发生的地方。点击播放按钮来明白我的意思(光敏警告:场景会以许多不同的颜色快速闪烁)。

播放默认的 PSP 场景(由作者创建)。

这个超现实的场景将重复播放 100 次,生成 RGB 图像、边界框标签、姿势标签和分段蒙版。要找出在哪里,点击左边层次中的“主摄像机”对象。屏幕右侧的 Inspector 选项卡应该会显示您选择的对象的属性。(在 Unity 中,“检查器”面板中的每一行都称为一个“组件”,本质上是附加到对象的类,用于定义其在场景中的行为。)向下滚动到“感知摄像机”组件。这来自感知包,它定义了如何从场景中捕捉帧,应用哪些注释,以及将它们保存在计算机上的什么位置。如果你打算使用感知,这里有许多重要的领域(特别是“相机贴标机”部分,在那里你定义你希望产生的标签和类别的类型),但现在我们将掩饰细节。

感知相机组件,您可以在其中配置感知场景的渲染设置。它位于“主相机”对象上(由作者创建)。

寻找“最后生成的数据集”字段,然后单击“显示文件夹”或“复制路径”以查看文件资源管理器中的数据。请随意浏览这些文件夹,感受一下这个工具可以产生什么样的数据。

添加您的人员

当前场景的一个明显问题是你的人不在里面!要添加您创建的 3D 模型,我们需要对其进行一些更改,以使其与 PSP 兼容。首先,让我们输入你的个人信息。再次查看底部的文件浏览器。(如果它显示的是“控制台”,请单击它旁边的标签“项目”如果不知何故“项目”不在那里,点击应用程序工具栏上的“窗口”>“常规”>“项目”。打开文件夹“资产/人/网格”并拖动“.fbx”文件(以及它旁边的“纹理”文件夹)。Unity 会自动导入。

单击 Unity 文件浏览器中的人体模型,并在文件浏览器中查找“装备”选项卡。将“动画类型”更改为“人形”,然后单击“应用”(如果你得到一个关于“没有足够的骨骼”的错误,你可能忘了在 MakeHuman 中分配“游戏引擎”装备。)这将转换模型的装备,以兼容 Unity 的化身系统。简单地说,这允许为一个人形模型设计的动画与另一个无缝地工作,只要它们每个都共享这个化身系统。如果您想验证它是否正常工作,请单击“配置”。在检查器的“肌肉&设置”面板中,你可以调整滑块来测试模型是否可以正常动画。要返回你的场景,点击最左边“层级”中“头像配置”左边的“<”箭头。

“肌肉&设置”面板允许你预览连接到你的模型(由作者创建)的化身。

现在钻机完成了!让我们看看我们的人。将你的人从底部的文件浏览器拖到左边的“层次”中,添加到场景中。

首次导入时的人员模型。注意粘土般的纹理和不透明的眼睛和眉毛(由作者创作)。

嗯,这看起来不太对。我的人有一个不自然的光泽,眼睛和头发缺乏透明度。我们需要编辑这些材料。材质嵌入在模型中,因此我们必须先提取它们才能编辑它们。在文件检查器(不在层级中)中点击你的人来选择。又是 fbx 文件。这次在检查器中选择“材料”标签。单击“提取材料…”按钮,选择一个文件夹来保存材料。现在,在按钮下方,您应该看到“重新映射的材质”字段现在有了您可以选择和编辑的材质。

其中一个问题是“金属”滑块完全在我们的材料上。我们实际上希望它们都设置为 0。您可以逐个选择它们,也可以一次选择所有材质以同时编辑它们。我还把“平滑度”值设为 0.1,我觉得看起来更自然一点。我们还注意到,这里的“表面类型”设置为“不透明”,即使是头发和眉毛等使用透明度的材质。对于眉毛、睫毛、眼睛和头发材质,您需要将其更改为“透明”。根据您在 MakeHuman 中的选择,这些材质的名称可能有所不同。

具有调整材料的人物模型(由作者创建)。

这些材料选择现在应该可以了。现在,我们需要将一些组件附加到模型上,使其具有我们在领域随机化场景中想要的行为。感知带有几个随机发生器组件,在模拟场景的每次迭代中随机调整位置、旋转、姿势、纹理等内容。我们希望现在添加以下组件:

  • 自定义前景旋转随机发生器标签
  • customforegroundscalenchanozertag
  • 标记
  • 动画随机发生器标签

在“层次结构”中点击你的人的实例,在检查器中选择“添加组件”按钮来添加每个需要的组件。前两个分别随机调整模型的旋转和缩放。第三个为您的模型提供了一个标签方案。这就是如何为模型的标签配置特定的类。最后一个从几个动画中随机选择一个应用于模型。

打开“标签”组件,查看“从现有标签配置”字段。下面的行是已经包含在场景中的标签配置文件。“IdLabelConfig”有一个名为“person”的名称,在这里应该没问题。单击“添加新标签”,将标签名称更改为“个人”这样,你的模型的标签将有“人”的类别附加。

还有一件事。打开“动画随机发生器标签”并选择“添加文件夹”选择“Assets/Animations_all”文件夹,将默认动画添加到您的个人中。现在我们差不多可以开始了。

我们现在需要为我们的人创建一个“预制品”。一个预置本质上是一个可重用的统一对象,我们可以创建它的实例(如果需要可以修改)。本质上,预置是 Unity 中的预置实例,就像类是面向对象编程中的对象一样。将 person 实例拖到“资产/人/预置”文件夹中。在这种情况下,选择“预设变体”,因为原始的,未修改的模型在技术上已经是一个预设。现在,您可以从“层次结构”中禁用或删除您的人员实例。

我们现在处于最后冲刺阶段!我们只需要让我们的模拟意识到我们的新人预设,然后我们就可以开始生成数据。单击层次结构中的“模拟场景”并查看“固定长度场景”组件。这决定了模拟的设置,例如要运行多少场景迭代,每次迭代有多少帧,以及要使用的随机化器。很快的,简单介绍一下背景。我刚才提到的 SynthDet 项目由对象场景组成,这些对象被分为“背景”对象(也称为干扰对象)和“前景”对象,这是我们希望注释的对象。显然,我们的人应该是一个“前景”对象。

打开“CustomForegroundObjectPlacementRandomizer”组件。在“预设”下,点击“添加选项”向列表中添加一个空行。如果您愿意,也可以单击“清除选项”来删除其他选项。将您的联系人拖到这个新位置。现在,您应该准备好开始模拟了。再次点按“播放”按钮,验证您的人是否像您预期的那样四处走动。验证标签是否也显示出来。

带有自定义人物模型的已完成场景(由作者创建)。

结论

这就结束了!这至少可以让你开始任何你想从事的合成人数据项目。Unity Perception 和 PeopleSansPeople 软件包是大规模生成合成数据的强大而灵活的方法。随意试验不同的动画,不同类型的服装,以及不同的 3D 模型来源。如果你最终广泛使用 MakeHuman,我鼓励你支持他们在 Patreon 上的项目。

参考

[1] Jacob Shermeyer,Thomas Hossler,Adam Van Etten,Daniel Hogan,瑞恩·刘易斯,Daeil Kim — 稀有飞机:合成数据飞行 (2020),WACV 2021

[2] Stefan Hinterstoisser,Vincent Lepetit,Paul Wohlhart,Kurt Konolige — 关于深度学习的预训练图像特征和合成图像 (2017),ECCV 2018

[3] Stephen James,Paul Wohlhart,Mrinal Kalakrishnan,Dmitry Kalashnikov,Alex Irpan,Julian Ibarz,Sergey Levine,Raia Hadsell,Konstantinos Bousmalis — 通过 Sim-to-Sim 实现 Sim-to-Real:通过随机到规范的适应网络实现数据高效的机器人抓取 (2018),CVPR 2019

[4]Salehe Erfanian Ebadi,You-Cyuan Jhang,Alex Zook,Saurav Dhakad,Adam Crespi,Pete 帕里西,Steven Borkman,Jonathan Hogins,su joy Ganguly—PeopleSansPeople:以人为中心的计算机视觉的合成数据生成器 (2021),预印本

[5] Stefan Hinterstoisser,Olivier Pauly,Hauke Heibel,Martina Marek,Martin Bokeloh — 保存的注释是获得的注释:使用完全合成训练进行对象实例检测 (2019),ICCV 2019

Python 中完美的、无限精确的游戏物理学(第 1 部分)

原文:https://towardsdatascience.com/perfect-infinite-precision-game-physics-in-python-part-1-698211c08d95

绝对准确,编程“牛顿的摇篮”,“网球和篮球下降”,等等

牛顿的摇篮与 Python——来源:编辑https://openai.com/dall-e-2/

这是向您展示如何用 Python 编写一个完美的物理引擎的四篇文章中的第一篇。这是我将所有物理、数学、甚至哲学转化为编程的宏伟目标中的一小步。通过这个项目,我们将发现惊喜,增加了解,并且(我希望)玩得开心。所有代码都可以在 GitHub 上获得。

我们将在许多方面限制引擎——例如,限制圆和线之间的牛顿碰撞。然而,我们不会限制引擎的准确性。它将用精确的表达式表示所有时间、位置和速度,如8*sqrt(3)/3。换句话说,它避免了所有的数值近似。

结果将是完美的模拟,例如,牛顿的摇篮玩具。(要重放/暂停视频,请按左下角的按钮。声音是有帮助的。)

常规牛顿摇篮(6 视频秒/66 模拟单位)

绝对精确的物理引擎有什么用?除了其他方面,它可以提高我们对物质世界的理解。在本文(第 1 部分)中,我们将看到这些新的发现:

  • 网球速度限制 —在一个流行的物理演示中,你用篮球丢下一个网球。网球弹起的速度比落下的速度快得多。快了多少?我们会看到“上升”速度永远不会超过“下降”速度的三倍。即使我们把篮球做得无限大,这也是成立的。
  • 良好的振动——想象一个移动的网球被困在一个篮球和一堵墙之间。在两帧视频之间的短暂时间里,网球可以弹跳 80 次。使用近似的引擎可能会错过一些反弹。我们的引擎计算每一个。

同样,在下一篇文章中(第二部分):

  • 台球破裂 —即使有无限的精确度,台球破裂实际上也无法逆转。我们会看到,即使我们去除了“蝴蝶效应”和量子不确定性,世界仍然是随机的和不确定的。

我们完美的物理引擎的缺点是速度。对于大多数模拟来说,随着时间的推移,时间、位置和速度的表达式变得越来越复杂。在你可以在第二部中看到的台球世界中,这将我们的模拟限制在 20 个左右的“事件”。(我们将在后面定义“事件”。)能不能提高速度?只会一点点。这将是第四部的主题。

制造引擎

假设我们已经有 Python 函数用于:

  • 给定世界上任意两个(可能移动的)物体,返回它们刚好接触的准确时间跨度。答案可能是“永远不会”。
  • 给定任意两个碰撞的物体,碰撞将如何改变它们的速度和方向?

我们将在第 3 部分中看到如何创建这样的函数。现在,考虑如何将这些功能变成一个完美的物理引擎。

让我们通过观察三个物理世界来研究一种方法,从牛顿的摇篮开始。

牛顿的摇篮

我们引擎的总体想法是找到世界上下一次碰撞的准确时间。向前移动到那个时间。调整碰撞物体的速度。重复一遍。

对于牛顿的摇篮,我们先设置圆圈和墙壁。

from perfect_physics import World, Circle, Wall

circle_list = [Circle(x=1, y=0, r=1, vx=1, vy=0, m=1)]
for i in range(1, 6):
    circle_list.append(Circle(x=i * 2 + 4, y=0, r=1, vx=0, vy=0, m=1))
wall_list = [Wall(x0=0, y0=0, x1=0, y1=1), Wall(x0=20, y0=0, x1=20, y1=1)]
world = World(circle_list, wall_list, xlim=(-1, 21), ylim=(-2, 2))
world.show()

接下来:

  • **在每对物体之间,找出它们碰撞的确切时间跨度(如果有的话)。**忽略其他配对和碰撞。-在这种情况下,第一个(移动的)圆将在时间跨度 3 处与第二个圆碰撞。忽略那次碰撞,它还会在时间跨度 5 处与第三个圆发生碰撞,以此类推。它将在时间跨度 18 处与远壁碰撞。
  • **找出第一次碰撞的时间跨度,并准确地将世界时钟调快。**在这种情况下,3。

  • **调整这些碰撞中所有物体的速度。**如牛顿摇篮所料,第一个圆静止不动,第二个圆开始移动。

  • **根据需要重复。**在本例中,第二个和第三个球将在时间跨度 0 内碰撞。这个视频展示了一个接一个的事件。(“事件”是将时间向前移动到碰撞或根据碰撞调整速度)

牛顿摇篮事件(35 视频秒/16 模拟单元)

为了将这些事件转换成常规视频,我们需要以规则的间隔生成帧,例如,每 1/24 秒。第一帧将显示 0 点的世界。玩了几个选项后,我决定每个模拟时间单位要有 10 秒的视频。因此,第二帧将用于时钟 10/24(也称为 5/12)。我们知道在时钟 3 之前没有碰撞,所以我们可以不考虑碰撞只移动圆。它的位置将是精确的。

一般来说,对于每一帧,我们:

  • 找出该帧的模拟时钟值。
  • 找出该时钟值之前的冲突事件。
  • 从前面碰撞的世界开始,将圆移动到帧的时钟值。(根据设计,在移动过程中,圆圈不会与任何东西发生碰撞。)

所有的位置和时间都是精确的。文章开头的视频展示了结果。

圆形和三角形

我声称时间和位置都是准确的,但是你相信我吗?这是通过一个三角形内接的圆得到的一些证据。首先,这是显示事件的视频。可以看到时钟设置为8*sqrt(3)/3这样的表达式。

三角形事件内接的圆(31 视频秒/48 模拟单位)

这是三角形中圆圈的常规视频:

三角形内接的规则圆(26 视频秒/250 模拟单位)

在这种情况下,模拟陷入一种模式。没有无限的精度,模式可能会被遗漏。

网球和篮球下落

让我们再看一个物理世界,看看无限精度的另一个优势。这个世界包括一个网球和篮球以速度 1 向一面墙移动。篮球的质量是网球的 1000 倍。(我们的世界没有重力。)

from perfect_physics import World, Circle, Wall

big_radius = 10
world_width = 40
folder = root / f"part1/two_size{big_radius}"

big = Circle(x=world_width // 2, y=0, r=big_radius, vx=1, vy=0, m=big_radius**3)
little = Circle(x=big.x - big_radius - 1, y=0, r=1, vx=1, vy=0, m=1)
circle_list = [big, little]
wall_list = [Wall(x0=0, y0=0, x1=0, y1=1), Wall(x0=world_width, y0=0, x1=world_width, y1=1)]
world = World(circle_list, wall_list, xlim=(-1, world_width + 1), ylim=(-big_radius - 1, big_radius + 1))
world.run_in_place(2, show=True)
print([circle.vx for circle in world.circle_list])

在 10 点整,球从右边的墙上弹开。网球的速度从 1 到 2999/1001,而篮球稍微慢了一点,到 997/1001。

不出所料(如果你已经在物理世界尝试过这个),网球加速了。然而,令我惊讶的是,它只快了三倍。

让我们暂时跳过,使用我们将在第 3 部分中开发的工具。这段 Python 代码展示了质量无限大的篮球对网球的影响。网球进去的速度是 1,出来的速度正好是 3。

from sympy import limit, simplify, oo
from perfect_physics import load
cc_velocity_solution = load("data/cc_velocity_solution.sympy")
a_vx, a_vy, b_vx, b_vy  = cc_velocity_solution.subs([("a_x", 10), ("a_y", 0), ("a_r", 10), ("a_vx", -1), ("a_vy", 0),
                            ("b_x", -1), ("b_y", 0), ("b_r", 1), ("b_vx", 1), ("b_vy", 0), ("b_m", 1)])
print(simplify(b_vx))
limit(b_vx, "a_m", oo)
# prints (1 - 3*a_m)/(a_m + 1)
# returns -3

如果像我一样,你期待网球加速到无穷大,那么像我一样,你从这个物理引擎中学到了一些真实世界的物理知识。

当我让发动机运转更长时间时,它让我更加吃惊。看看在视频时间 20 秒(模拟时间 200)发生了什么:

常规网球和篮球投掷(39 视频秒/386 模拟单位)

篮球挤压网球。这导致网球在单个视频帧中来回反弹超过 80 次。网球的速度超过了 31 英里。在视频的音频轨道上,网球的振动产生了超过 2000 Hz(节拍/秒)的音调。

如果不是精确计算,我们只是每帧采样一次,我们将错过(或误算)这一快速动作。

总结第 1 部分

有用!我们有一个完美的物理引擎。我们已经把一点物理变成了程序。

我们假设我们从两个 Python 函数开始,这两个函数告诉我们——对于任何一对对象——两件事:1)直到它们下一次碰撞(如果有的话)的时间,以及 2)碰撞对它们速度的影响。我们看到了如何通过重复将这两个功能转变为完美的物理引擎:在时间上向前移动,直到下一次碰撞,并调整碰撞中涉及的所有对象的速度。

我们通过查找视频帧之前的碰撞来创建视频,然后将时间向前移动(不用担心碰撞)到该帧。

牛顿的摇篮,第一个世界,表现如预期。第二个世界,三角形内接的圆,使用8*sqrt(3)/3等表示时间的表达式等。,发现了一个规律。网球篮球下落揭示了网球速度的一个令我吃惊的极限。此外,它产生的反弹比不太完美的方法所能计算的还要快。

下一篇文章,第二部分,模拟了一次台球休息,带来了令我惊讶的哲学结果。接下来,在第三部分中,我们将看到如何让计算机创建这两个启动函数。最后,在第 4 部分的中,我们将加速引擎一点(但还不够)并讨论局限性。

如果你有想让我运行的模拟的想法,请发给我。它们可能成为第五部分的基础。

你可以从CarlKCarlK/perfect-physics(github.com)下载这段代码。让我知道如果有兴趣,我会创建一个更好的安装程序。

请跟随卡尔·m·卡迪— Medium 获取后续零件的通知。所有部分都将免费阅读。最后,在 YouTube 上,我有更老的(近似的)物理模拟和一些试图幽默的自然视频。

Python 中完美的、无限精确的游戏物理学(第 2 部分)

原文:https://towardsdatascience.com/perfect-infinite-precision-game-physics-in-python-part-2-360cc445a197

用台球把哲学变成编程

一条玩台球的蟒蛇——来源:https://openai.com/dall-e-2/

这是向您展示如何用 Python 编写一个完美的物理引擎的四篇文章中的第二篇。这是我将所有物理、数学、甚至哲学转化为编程的宏伟目标中的一小步。通过这个项目,我们将发现惊喜,增加了解,并且(我希望)玩得开心。所有代码都可以在 GitHub 上获得。

在第一部中,我们开发了顶级的完美物理引擎。然后我们将它应用于牛顿的摇篮,一个三角形内接的圆,以及网球和篮球下落。

在第 2 部分中,我们将把引擎应用到更复杂的事情上:台球休息。(当我看到结果的时候,它们改变了我对世界的哲学理解。)

想象一个母球散射出一个三角形的台球。如果——休息一会儿之后——你可以反转每个球的方向,它们会重新形成一个静止的三角形并吐出母球吗?换句话说,我们的宇宙包括撤销按钮吗?

一个带有蝴蝶的近似物理引擎

六年前,我做了这个实验。我使用了一个不完美的近似物理引擎。如视频所示,实验失败了。三角形确实重新形成了,但它不是静止不动的。(要重放/暂停视频,请按左下角的按钮。)

不完美的 2015 年模拟(画外音包括一首关于数学家亨利·庞加莱的诗)

当时,我把失败归因于蝴蝶效应。蝴蝶效应说,初始条件中非常小的差异(或近似)会导致后来的巨大差异。实验的失败激发了这首诗:

牛顿说一切都是可逆的,庞加莱说不,我们的目标是有限的,我们尝试过,但没有什么是永远不变的。

没有蝴蝶的完美物理引擎

我们新的物理引擎避免了蝴蝶效应。它准确地知道每一个时间、位置和速度。它使用了175–9*sqrt(3)/2这样的表达方式。所以,我们应该可以反向台球,对不对?让我们看看。(要重放/暂停视频,请按左下角的按钮。声音是有帮助的。)

台球休息(11 视频秒/203 模拟单位)

还是不行。三角形再次重新形成,但又一次没有保持在一起。这让我很难过。然而,作为程序员,我们不会沮丧,我们会调试。在这种情况下,我们必须调试世界。

我们将从逐个事件运行模拟开始:

台球休息事件(45 视频秒/177 模拟单位)

对称性的缺乏让你吃惊吗?作为程序员,我们知道我们的下一个调试步骤:创建一个最小可再现的例子。换句话说,找一个说明同样问题的小例子。

考虑一下这个三圈世界。我们运行了两次。

在第三帧中,观察小球的速度箭头。箭头指向不同的方向。世界已经分化。

我们可以追踪原因到第二帧。在时钟=14,cueball 击中其他两个球。但是它首先瞬间击中哪个球呢?无论哪个先击中,都会获得更多的能量,破坏对称性。

我听说你反对模拟器先选择一次碰撞。为什么不通过平均分配能量来保持对称呢?嗯,因为…

  • 我们已经讨论了三种选择——球 1 的最大能量、球 2 的最大能量或相等的能量。然而,总的来说,模拟器面临着无限的选择。它可以在两个球之间以任意比例分配能量,同时仍然保持能量和动量。是什么让“平等”变得特别?
  • 好吧,我承认在这个案例中,“相等”可能是对称的。然而,一般来说,没有对称性可用。考虑三个运动圆之间的不对称碰撞。因此,对称性没有给出答案。

三个圆不对称碰撞

所以,多次碰撞可以导致无限可能的结果,我们不能依赖于对称性。我们能做什么?我不认为在无限可能的结果中有任何“公平”的选择方式。(关于可能的牛顿非决定论的最佳讨论,请参见从第 18 页开始的爱德华·a·李的信息物理系统建模的基本限制。也可以参考 Sammy Gerbil 的这个物理堆栈交换评论和 Google Group sci . Physics . research 中的这个 2003 年的讨论。)

当有疑问时,做一些简单的事情。我让模拟器将多次碰撞视为成对碰撞的列表。然后,它以随机顺序处理每一对。例如,当三个圆(A、B、C)发生碰撞时,我们最多有三个碰撞对(AB、BC、AC ),它们可以排序为 3!,也就是 6 种方式。

即使这种简单的方法也会导致大量可能的结果。在完整的 15 三角球台球世界中,引擎经常看到 5 到 7 对的碰撞(因此,120 到 5,040 个结果)。它还看到一次与 18 对的碰撞(所以 18!或 6,402,373,705,728,000 个结果)。

觉得幸运吗?

这让我想起了一个新问题。如果你非常非常幸运的话,你能逆转一次撞球吗?我相信答案永远是“是”,但我不知道如何证明。然而,对于三个三角形球的世界,我尝试了各种随机种子。我尝试的第 17 粒种子成功了,制作了这个活动视频:

三个三角球的台球事件(27 视频秒/96 模拟单元)

这个普通的视频:

三个三角球的台球(17 视频秒/74 模拟单元)

因此,即使有完美的、无限精确的物理学,这个世界也不是确定的,除非我们非常非常幸运。这个结果不依赖于蝴蝶效应(小差异导致大差异)或者量子不确定性。相反,它依赖于三个或更多的碰撞物体在遵守牛顿物理定律的同时交换能量和动量的无数种方式。

我们现在可以回答可逆性问题:一个完美的、无限精确的世界是可逆的吗?我们已经看到,在实践中,答案是“不”。

令人惊讶的是,物理学家的回答是“是的”。怎么会?偷偷使用“可逆性”的不同定义。据我所知,他们说一个系统是“可逆的”,如果它有任何的机会回到它以前的状态,即使这种机会是 6402373705728000 分之一,或者是在一个无限连续体上选择一个点。

科学哲学家了解这些问题吗?引用斯坦福哲学百科全书:

打破永恒的模型可以在碰撞现象的基础上构建。第一个问题是多粒子碰撞,牛顿粒子力学对这种碰撞没有现成的解释。

爱德华·a·李给出了我所见过的最好的例子。在信息物理系统建模的基本限制中,他展示了这个世界中质量是不相等的,比如 1/5,1 和 5,速度是 1,0,-1。

质量不等,比如 1/5,1,5。

当我们将这个例子通过引擎时,我们得到了[-73/27, -40/27, -5/9][-31/9, -7/9, -2/3]的最终速度,这取决于随机种子。

总结第二部分

实际上,这个世界是不可逆的!用我们完美的物理引擎,我们回答了一个哲学问题。

在第一部中,我们开发了顶级的完美物理引擎,并将其应用于网球和篮球掉落等世界。

在这里,在第 2 部分中,我们开始尝试让台球重新形成一个静止的三角形,然后吐出母球。这在 2015 年失败了,当时我用一个不完美的物理引擎进行了尝试。我们可以把失败归因于蝴蝶效应。我们新的完美物理引擎又失败了。使用我们的调试技巧,我们发现问题是多重冲突的不确定性。只有在非常简单的世界和非常幸运的情况下,我们才能看到可逆性。

接下来,在第 3 部分中,我们将看到如何让计算机创建引擎下的两个 Python 函数。在第四部分的中,我们将加速引擎一点(但还不够)并讨论限制。

如果你有想让我运行的模拟的想法,请发给我。它们可能成为第五部分的基础。

你可以从CarlKCarlK/perfect-physics(github.com)下载这段代码。让我知道如果有兴趣,我会创建一个更好的安装程序。

请按照卡尔·m·卡迪— Medium 获取后续零件的通知。所有部分都将免费阅读。最后,在 YouTube 上,我有更老的(近似的)物理模拟和一些试图幽默的自然视频。

Python 中完美的、无限精确的游戏物理学(第 3 部分)

原文:https://towardsdatascience.com/perfect-infinite-precision-game-physics-in-python-part-3-9ea9043e3969

使用 Python SymPy 将数学和物理转化为编程

一个平衡数学方程的 Python 来源:https://openai.com/dall-e-2/

这是向您展示如何用 Python 编写一个完美的物理引擎的四篇文章中的第三篇。这是我将所有物理、数学、甚至哲学转化为编程的宏伟目标中的一小步。所有代码都可以在 GitHub 上获得。

这篇文章是关于让计算机为我们做数学。不仅仅是数值计算,还有代数和微积分相关的操作。这很有用,因为:

  • 我——也许还有你——不记得我高中和大学的所有数学知识了。
  • 在学校,老师只给我们能解决的问题。我想解决我解决不了的问题。

回想一下,在本系列的第 1 部分和第 2 部分中,我们开发了顶级的完美物理引擎。它使用像175–9*sqrt(3)/2这样的表达式来精确地表示时间、位置和速度,也就是说,没有近似值。我们将该引擎应用于牛顿的摇篮、一个网球和篮球下落以及一次台球休息。这些模拟产生了令我惊讶的关于物理甚至哲学的发现。

在第三部分中,我们将使用计算机从基本物理定律中推导出所需的表达式。我们将分五步完成这项工作:

  1. 模拟两个运动的圆圈。使用 SymPy 精确地找到两个移动的圆何时“刚好接触”。理解为什么 SymPy 返回多个答案。
  2. 模拟一个圆和一面墙。找到他们接触的时间。加一个关于角度的“秘密”第六方程(但不要用角度)。
  3. 尝试用动量和能量守恒来模拟圈与圈之间的碰撞。你不能。加一个关于角度的“秘密”第四方程(但不要用角度)。
  4. 假设墙的质量无限大。从墙上反弹回来的圆违反了动量守恒。用限制来解决这个问题。
  5. 两个物体可以相互接触,也可以相互远离。用极限来找这些。将它们从碰撞对象列表中移除。

我们从两个圆圈开始。

第一步:模拟两个移动的圆圈。使用 SymPy 精确地找到两个移动的圆何时“刚好接触”。理解为什么 SymPy 返回多个答案。

我们将把世界建模为在无限二维平面上运动的圆圈。圆圈匀速运动,直到它们碰撞。圆圈可能会相互碰撞或撞到墙上。墙是(无限长的)线。

我们需要回答的第一个问题是,两个移动的圆何时(如果有的话)会开始接触。为了回答这个问题,我们将设计一组方程式。然后我们会让计算机解方程并给我们答案。

为了设计我们的第一个方程,考虑两个圆, ab 。每个都有一个位置( xy ),一个速度( vxvy ),一个半径, r 。你能预测一个圆在任意时间 t 的未来位置( x ',y' )吗?

我们的第一个等式表示圆 a 在时间 tx 位置是其当前 x 位置加上其 vx 速度乘以时间t的数学表达式:

等式 2 将告诉我们圆圈 a 在未来的 y 位置。等式 3 和 4 会告诉我们 bxy 的未来位置。

作为将数学转化为编程的第一步,我们将用 Python 的 SymPy 包写出这四个方程。SymPy 是一个计算机代数系统,类似于 Mathematica 和 Maple。然而,与这些系统不同,SymPy 是免费的,可以在 Python 中获得。

旁白#1:不要混淆 S y mPy(一个用于符号数学的 Python 包)和 S i mPy(一个用于模拟的 Python 包)。

旁白#2:计算机代数系统将表达式表示为嵌套的计算机对象。这种“符号”表示使一些事情对计算机来说变得容易——例如,处理分数或对其他表达式求导。即使有了符号表示,一些任务对计算机来说仍然是困难的——例如,求积分或解非线性方程组。这与人们容易和困难的事情相一致。我喜欢使用计算机代数系统,因为在大多数任务中,无论简单还是困难,它们都比我强。

以下是我们在 Python & SymPy 中的四个方程:

from sympy import symbols, Eq

# define symbols
t = symbols("t")
a_x, a_y, a_vx, a_vy, a_r, aprime_x, aprime_y = symbols("a_x, a_y, a_vx, a_vy, a_r, a'_x, a'_y")
b_x, b_y, b_vx, b_vy, b_r, bprime_x, bprime_y = symbols("b_x, b_y, b_vx, b_vy, b_r, b'_x, b'_y")

# define equations
eq1 = Eq(aprime_x, a_x + a_vx * t)
eq2 = Eq(aprime_y, a_y + a_vy * t)
eq3 = Eq(bprime_x, b_x + b_vx * t)
eq4 = Eq(bprime_y, b_y + b_vy * t)p

我们仍然需要设计两个圆“刚好接触”的概念。我们用第五个方程来计算。它说,当两个圆的圆心距离等于半径之和时,这两个圆刚好接触。(我们通过对两边求平方来避免使用sqrt函数。)在数学符号中:

在 Python 和 SymPy 中:

eq5 = Eq((aprime_x - bprime_x) ** 2 + (aprime_y - bprime_y) ** 2,
         (a_r + b_r)**2)

如果我们给前四个方程关于两个圆和一个时间的信息,它们会告诉我们当时圆的位置。方程五可以告诉我们,两个圆在那个时候刚好接触。

换句话说,给定一个时间,这些方程可以回答“这两个圆在时间 t 刚好接触吗?”但这不是我们想要的。我们想把里面翻出来,回答这个问题“这两个圆什么时候会接触?”

要回答感兴趣的问题,数学告诉我们只要“求解”方程。可悲的是,套用芭比的话,“数学(求解包含正方形的方程)很难。”幸运的是,数学对 SymPy 来说并不难:

from sympy import nonlinsolve
from perfectphysics import savecc_all_solutions = nonlinsolve([eq1, eq2, eq3, eq4, eq5], t, aprime_x, aprime_y, bprime_x, bprime_y)
cc_time_solutions = [t for t, aprime_x, ap_y, bp_x, bp_y in cc_all_solutions]
save(cc_time_solutions, "cc_time_solutions.sympy")
cc_time_solutions[0]

在大约 30 秒的时间里,SymPy 找到了两个一般性的,象征性的解决方案。这里只是第一个解决方案的一点,cc_time_solutions[0]用数学符号表示:

在我们看来这很复杂。然而,这对计算机来说并不复杂。

为什么有两种解决方案?评估它们可以帮助我们理解。考虑这种情况:两个半径为 1 的圆,蓝球以速度 2 移动:

from perfect_physics import Circle, plot
a = Circle(x=0,y=0,r=1,vx=2,vy=0)
b = Circle(x=4,y=0,r=1,vx=0,vy=0)
plot([a, b], colors=["tab:blue","tab:red"], xlim=(-2,8), ylim=(-2,2), figsize=(4,2), font_scale=1)

我们使用 SymPy .subs(替换)方法找到圆 a 和b刚好接触的两个时间。我们也在这些时候画出圆圈。

from perfect_physics import load
cc_time_solutions = load("cc_time_solutions.sympy")
times = [time_solution.subs([("a_x", a.x), ("a_y", a.y), ("a_r", a.r), ("a_vx", a.vx), ("a_vy", a.vy),
                             ("b_x", b.x), ("b_y", b.y), ("b_r", b.r), ("b_vx", b.vx), ("b_vy", b.vy)])
         for time_solution in cc_time_solutions]
print(times)
for time in times:
    plot([a.tick_clone(time), b.tick_clone(time)], clock=time,
    colors=["tab:blue","tab:red"], xlim=(-2,8), ylim=(-2,2), figsize=(4,2), font_scale=1)

Python 打印[3, 1]并绘图:

换句话说,在时间 3(忽略碰撞),蓝色圆圈将刚好接触红色圆圈。同样,在时间 1。如果我们想知道下一次碰撞之前的时间,我们只需要取较小的时间,对吗?

可悲的是,更小的时间可能也不对。考虑这种情况,其中圆圈彼此远离。他们什么时候会触碰?

a = Circle(x=2,y=2,r=1,vx=1,vy=1)
b = Circle(x=0,y=0,r=1,vx=0,vy=0)
plot([a, b], colors=["tab:blue","tab:red"], xlim=(-2,4), ylim=(-2,4), figsize=(3,3), font_scale=1)
times = [time_solution.subs([("a_x", a.x), ("a_y", a.y), ("a_r", a.r), ("a_vx", a.vx), ("a_vy", a.vy),
                             ("b_x", b.x), ("b_y", b.y), ("b_r", b.r), ("b_vx", b.vx), ("b_vy", b.vy)])
         for time_solution in cc_time_solutions]
print(times)

Python 回答[-2 + sqrt(2), -2 — sqrt(2)]。(注意,它的回答是无限精确的,而不是像[-0.59, -3.41]这样的近似值)。这些时间是负的,意味着圆圈将只接触过去。我们不关心过去的碰撞,所以我们忽略负时间。

总结一下:答案会给我们两次,我们保留最小的,非负的时间,对吗?没有。我们还必须提防另外两种复杂情况。第一,当圆圈静止或以相同速度一起运动时,时间将是[nan, nan],即浮点“不是一个数”。

第二,当圆圈移动时,它们的路径永远不会相交,时间将类似于[2 + sqrt(2)*I, 2 — sqrt(2)*I],包含虚数。

最后,我们的程序是用 SymPy 得到两次,然后:

  • 过滤掉包含虚数的答案
  • 筛选出nan个答案
  • 过滤掉否定的答案
  • 保留最小的剩余答案(如果有)

这个结果会是我们期望的下一次碰撞的无限精确时间吗?差不多了。当它说它们将在时间零点接触时,这两个圆可能会彼此远离。这种情况不算碰撞。我们将在步骤 5 中研究这个问题。

注意,如果我们可以处理两个圆,我们可以处理任意数量的圆。我们只需要查看所有的圆对,并返回最小的时间(如果有的话)。

我认为我们在这一步所做的非常酷。我们只需要设计出五个简单的方程式,关于圆圈在时间中前进并检测距离。Python SymPy 包随后为我们做了困难的计算。它创建了一个函数,精确地告诉我们何时(如果有的话)圆会接触。

在第三步中,我们将看到触摸如何改变每个圆圈的速度。然而,接下来,让我们与 SymPy 合作,找出圆何时会碰壁。

第二步:建立一个圆形和一面墙的模型。找到他们接触的时间。加一个关于角度的“秘密”第六方程(但不要用角度)。

假设我们有圆 a 和墙 w 。和之前一样,圆有位置( xy ),速度( vxvy ),半径, r 。这堵墙是一条(无限长的)线。我们沿着墙指定任意两个不同的点, (x0,y0)(x1,y1)

为了找到圆和墙刚好接触的时间,我们再次模拟圆在时间 t 的位置(x’,y’)。

from sympy import symbols, Eq
t = symbols("t")
a_x, a_y, a_vx, a_vy, a_r, aprime_x, aprime_y = symbols("a_x, a_y, a_vx, a_vy, a_r, a'_x, a'_y")
eq1 = Eq(aprime_x, a_x + a_vx * t)
eq2 = Eq(aprime_y, a_y + a_vy * t)

接下来,我们在墙上的圆和墙接触的地方建模。称此点为 *(x2,y2)。*在壁定义点 (x0,y0)(x1,y1) 之间会有一个比例 p 。例如,如果 p 是 1/2,则 (x2,y2) 是两个壁定义点的中点。此外,通过让 p 的范围小于 0 且大于 1,我们可以将 (x2,y2) 移动到沿墙的任何位置:

p = symbols("p")
x0, y0, x1, y1, x2, y2 = symbols("x_0, y_0, x_1, y_1, x_2, y_2")
eq3 = Eq(x2, x0 + (x1-x0) * p)
eq4 = Eq(y2, y0 + (y1-y0) * p)

当圆的中心点*(x’,y’)*与其接触的墙壁上的位置 (x2,y2) 之间的距离等于圆的半径时,圆将接触墙壁。换句话说:

eq5 = Eq((x2-aprime_x)**2 + (y2-aprime_y)**2, a_r**2)

然而,当我们要求 SymPy 求解并应用这五个方程时,它给出了这样奇怪的解:

SymPy 认为这解决了我们的方程,因为圆的中心离墙上的某个点有一个半径(两个红点)。然而,这不是我们想要的解决方案,因为圆与墙重叠。

第六个方程可以解决这个问题。要求墙壁和白线之间的角度α为 90 度(见上图)。我们可以用斜率来说这个,不需要明确的角度或者三角函数。墙的坡度是(y1-y0)/(x1-x0)。图中白线的斜率为(aprime_y-y2)/(aprime_x-x2)。当一条直线的斜率是另一条直线的负倒数时,两条直线相交于 90 度。在 SymPy 中,我们说:

slope_0 = (y1-y0)/(x1-x0)
slope_1 = (aprime_y-y2)/(aprime_x-x2)
eq6 = Eq(slope_0, -1/slope_1)

我们要求 SymPy 像这样为我们解决这个问题:

from sympy import nonlinsolve
from perfect_physics import save
cw_all_solutions = nonlinsolve([eq1, eq2, eq3, eq4, eq5, eq6], t, p, aprime_x, aprime_y, x2, y2)
cw_time_solutions = [t for t, p, aprime_x, aprime_y, x2, y2 in cw_all_solutions]
save(cw_time_solutions, "cw_time_solutions.sympy")

工作 7 分钟后(比我强吧!),它返回两个解。下面是用数学符号表示的第一个解(比上次更易读):

当我们将这些解决方案应用于上述情况时,Python 返回时间跨度[2, 3]:

先不说:解表达式看起来像是来自 二次方程 的东西。这表明一个优秀的应用数学家可以手动解决这个问题。对他们有好处。然而,我很乐意通过计算机解决这个问题。

我们将应用与步骤 1 中相同的规则,过滤掉负面的、复杂的、nan,并保留最小的时间(如果有的话)。当一个圆圈蹭到一堵墙时,我们可能会遇到一个新问题:

这个世界将会回归[nan, zoo]。其中zoo是复数无穷大的 SymPy 符号。我们会把它过滤掉,因为我们不关心掠射碰撞。

让我们将这个结果添加到步骤 1 的结果中。我们现在可以找到一个圆与一个圆或一面墙之间下一次“接触”的精确的、无限精确的时间。

接下来,让我们弄清楚圆圈-圆圈触摸是如何改变圆圈的速度和方向的。

第三步:尝试用动量和能量守恒来模拟圈与圈之间的碰撞。你不能。加一个关于角度的“秘密”第四方程(但不要用角度)。

我第一次尝试模拟两个圆之间的碰撞时,我以为我只需要两条定律:动量守恒和能量守恒。我错了。我们来看看为什么。

能量守恒说,碰撞前后的能量应该是一样的。对于我们的世界,唯一的能量类型是动能,所以 E= mv /2 ,其中 m 是质量,而 v 是速度。我们假设碰撞是弹性的,也就是说,没有能量转化为热量、声音等。在数学符号中,这变成:

其中圆 a 的质量为 aₘ ,碰撞前的速度为*(aᵥᵧaᵥₓ),碰撞后的速度为(âᵥᵧâᵥₓ)*。圆圈 b 也是如此。

在 SymPy 中,我们写道:

from sympy import symbols, Eq
a_x, a_y, a_vx, a_vy, a_r, a_m, ahat_vx, ahat_vy = symbols("a_x, a_y, a_vx, a_vy, a_r, a_m, ahat_vx, ahat_vy")
b_x, b_y, b_vx, b_vy, b_r, b_m, bhat_vx, bhat_vy = symbols("b_x, b_y, b_vx, b_vy, b_r, b_m, bhat_vx, bhat_vy")
energy_before = a_m * (a_vx**2 + a_vy**2) / 2 + b_m * (b_vx**2 + b_vy**2) / 2
energy_after = a_m * (ahat_vx**2 + ahat_vy**2) / 2 + b_m * (bhat_vx**2 + bhat_vy**2) / 2
eq1 = Eq(energy_before, energy_after)

动量守恒给了我们另外两个方程,一个是关于 x 维度的,一个是关于 y 维度的。动量是质量乘以速度,所以:

eq2 = Eq(a_m * a_vx + b_m * b_vx, a_m * ahat_vx + b_m * bhat_vx)
eq3 = Eq(a_m * a_vy + b_m * b_vy, a_m * ahat_vy + b_m * bhat_vy)

我们现在有三个方程,希望 SymPy 给我们四个未知数的解,即碰撞后速度: âᵥₓ,âᵥᵧ,b ̂ ᵥₓ,b ̂ *ᵥᵧ.*那不行。我们需要和未知数一样多的方程。

我第一次注意到这个丢失的等式时,它难倒了我。我想不出任何其他适用的物理定律。我终于想通了第四个缺失的“定律”:

当圆碰撞时,它们的速度与碰撞方向成 90°不变。

考虑这种情况:

from perfect_physics import Circle, plot
a = Circle(x=0, y=0, r=1, vx=1, vy=2, m=1)
b = Circle(x=2, y=0, r=1, vx=0, vy=0, m=1)
plot([a, b], colors=["tab:red", "tab:blue"], xlim=(-2, 4), ylim=(-2, 2), figsize=(4, 2), font_scale=1)

“撞击方向”是从一个中心到另一个中心的方向,这里是水平方向。圆 a 与水平面成 90°的速度为 aᵥᵧ 为 2。所以,我们期望这个方向的后碰撞速度, âᵥᵧ 也是 2。

但是如果圆心不水平也不垂直呢?考虑这种情况:

from sympy import sqrt
from perfect_physics import Circle, plot
a = Circle(x=0, y=0, r=1, vx=1, vy=0, m=1)
b = Circle(x=sqrt(2), y=sqrt(2), r=1, vx=0, vy=0, m=1)
plot([a, b], colors=["tab:red", "tab:blue"], xlim=(-2, 4), ylim=(-2, 3), figsize=(4, 2), font_scale=1)

我们可以用正弦和余弦函数从撞击方向求出速度 90。然而,用单位向量来做可以让我们避免引入这些函数。我们想要一个向量 < ux,uv > ,它有一个单位长,并且从圆心之间的直线延伸 90 度。我们可以通过除以 d 得到长度为 1,即中心之间的距离。我们可以用第二步中使用的-1/斜率得到我们想要的方向。具体来说,这表示撞击方向的速度 90 不变:

from sympy import sqrt
d = sqrt((b_x-a_x)**2 + (b_y-a_y)**2)
ux = -(b_y-a_y)/d
uy = (b_x-a_x)/d
eq4 = Eq((b_vx - a_vx) * ux + (b_vy - a_vy) * uy, (bhat_vx-ahat_vx) * ux + (bhat_vy-ahat_vy) * uy)

我们已经很好地解决了我们的方程:

from sympy import nonlinsolve
from perfect_physics import save
cc_velocity_solutions = nonlinsolve([eq1, eq2, eq3, eq4], ahat_vx, ahat_vy, bhat_vx, bhat_vy)
cc_velocity_solutions = list(cc_velocity_solutions)
save(cc_velocity_solutions[1], "cc_velocity_solution.sympy")

2 或 3 分钟后,它返回两个解决方案。令人惊讶的是,第一个解说后速度等于前速度。换句话说,这是当两个圈互相错过时的解决方案。

第二个解,我们关心的解,是长的。这里就不展示了。

让我们用上面的两个例子来解决这个问题。在第一个例子中,之前的速度是a.vx=1, a.vy=2, b.vx=0, b.vy=0。他们成为a.vx=0, a.vy=2, b.vx=1, b.vy=0之后。

在第二个例子中,之前的速度是a.vx=1, a.vy=0, b.vx=0, b.vy=0。他们成为a.vx=1/2, a.vy=-1/2, b.vx=1/2, b.vy=1/2后。

酷!所以,我们现在可以预测圆是如何相互弹开的。我们以后会看到,这个解适用于不同质量和不同半径的圆。

接下来,我们试着将这些守恒定律应用到从墙上反弹回来的圆上。令人惊讶的是,反弹似乎违反了“动量守恒”。

第四步:假设墙的质量无限大。从墙上反弹回来的圆违反了动量守恒。用限制来解决这个问题。

对于一个从墙上弹回的球,我们可以作弊。你可能还记得学校里的答案,那就是“入射角等于反射角”:

但是这怎么可能是对的呢?反弹前圆的y-动量(即质量乘以y-速度)为-1。反弹后是 1。这违反了动量守恒。包括墙的动量没有帮助,因为它的质量是无限的,它的速度是 0,给了我们一个未定义的动量。

也许我们可以把墙建模成大圆,然后使用第三步的解决方案。这里我们给大圆半径 20,质量 1000。

a = Circle(x=0, y=1, r=1, vx=1, vy=-1, m=1)
b = Circle(x=0, y=-20, r=20, vx=0, vy=0, m=1000)

这几乎行得通。后速度为a.vx=1, a.vy=999/1001, b.vx=0, b.vy=-2/1001。如果我们把无穷大作为大圆的质量和/或半径。可悲的是,结果是未定义的值。

让我们看看能否从守恒定律入手,找到正确的答案。我们假设墙的质量有限,速度为 0。我们放入能量守恒(方程 1),动量守恒(方程 2,方程 3),圆周沿壁的相对速度不应该改变。

from sympy import symbols, Eq, sqrt
a_x, a_y, a_vx, a_vy, a_r, a_m, ahat_vx, ahat_vy = symbols("a_x, a_y, a_vx, a_vy, a_r, a_m, ahat_vx, ahat_vy")
w_x0, w_y0, w_x1, w_y1, w_m, what_vx, what_vy = symbols("w_x0, w_y0, w_x1, w_y1, w_m, what_vx, what_vy")
energy_before = a_m * (a_vx**2 + a_vy**2) / 2 + w_m * 0
energy_after = a_m * (ahat_vx**2 + ahat_vy**2) / 2 + w_m * (what_vx**2 + what_vy**2) / 2
eq1 = Eq(energy_before, energy_after)
eq2 = Eq(a_m * a_vx + w_m * 0, a_m * ahat_vx + w_m * what_vx)
eq3 = Eq(a_m * a_vy + w_m * 0, a_m * ahat_vy + w_m * what_vy)
d = sqrt((w_x1-w_x0)**2 + (w_y1-w_y0)**2)
ux = (w_x1-w_x0)/d
uy = (w_y1-w_y0)/d
eq4 = Eq(a_vx * ux + a_vy * uy, (ahat_vx-what_vx) * ux + (ahat_vy-what_vy) * uy)

我们再次应用nonlinsolve,得到一个没有命中(忽略)的结果和一个命中的结果。我们的下一步是新的。当墙的质量趋于无穷大时,我们用 SymPy 来取极限。

from sympy import nonlinsolve, limit, oo, Tuple
from perfect_physics import save
no_hit, cw_velocity_solution = nonlinsolve(
    [eq1, eq2, eq3, eq4], ahat_vx, ahat_vy, what_vx, what_vy
)
a_vx_limit, a_vy_limit, w_vx_limit, w_vy_limit = [
    limit(velocity, w_m, oo) for velocity in cw_velocity_solution
]
assert w_vx_limit == 0 and w_vy_limit == 0
cw_velocity_limits = Tuple(a_vx_limit, a_vy_limit)
save(cw_velocity_limits, "cw_velocity_limits.sympy")

结果是在vxvy之后出现了圆圈和墙壁。正如所希望的,墙的速度为零。我们保存了圆的后速度公式。

当我们应用这个公式时,我们得到了“入射角等于反射角”的预期结果:

那么,我们现在准备好解决物理作业问题,创作动画,制作游戏了吗?不完全是。我忽略了一个问题:两个物体接触并不总是意味着它们在碰撞。我们接下来会看到这一点。

第五步:两个物体可以相互接触,也可以相互远离。用极限来找这些。将它们从碰撞对象列表中移除。

我们如何检测两个圆是否在相向运动?我原本以为我们可以只检查他们的相对速度。然而,在这两种情况下,红圈的相对速度是相同的。在第一种情况下,它们朝着彼此移动,而在第二种情况下,它们没有。

一个更好的方法是看这些中心是否越来越靠近。但是什么时候?他们开始时可能会靠得更近,但后来会彼此远离。我们可以问一下他们在零点是否越来越接近了吗?这是行不通的,因为在任何时刻,他们的距离都是不变的。

解决方法是再次使用限制。我们将测试在时间零点之后的瞬间,它们是否越来越近。

from sympy import symbols, limit, sqrt
from perfect_physics import save
a_x, a_y, a_vx, a_vy, b_x, b_y, b_vx, b_vy, t = symbols("a_x, a_y, a_vx, a_vy, b_x, b_y, b_vx, b_vy, t")
d0 = sqrt((a_x - b_x) ** 2 + (a_y - b_y) ** 2)
d1 = sqrt((a_x + t * a_vx - (b_x + t * b_vx)) ** 2 + (a_y + t * a_vy - (b_y + t * b_vy)) ** 2)
speed_toward = (d0 - d1) / t
instant_speed = limit(speed_toward, t, 0)
save(instant_speed, "instant_speed.sympy")

在上面的第一个示例中,应用结果公式返回 1。在第二个示例中,它返回-1。两种结果都在意料之中。

为了观察一个圆是否正在向一面墙移动,我们找到了 (x₂,y₂) ,圆的中心可能会碰到墙上的点。这需要四个可以用 SymPy linsolve求解的方程。然后,我们使用刚刚发现的instant_speed公式,来查看圆朝着或远离该点的速度:

from sympy import symbols, linsolve, Eq, simplify
from perfect_physics import load, save
a_x, a_y, a_vx, a_vy, t = symbols("a_x, a_y, a_vx, a_vy, t")
x0, y0, x1, y1, x2, y2, p = symbols("x_0, y_0, x_1, y_1, x_2, y_2, p")
eq1 = Eq(x2, a_x + a_vx * t)
eq2 = Eq(y2, a_y + a_vy * t)
eq3 = Eq(x2, x0 + (x1-x0) * p)
eq4 = Eq(y2, y0 + (y1-y0) * p)
x2_formula, y2_formula, _, _ = list(linsolve([eq1, eq2, eq3, eq4], (x2, y2, p, t)))[0]
instant_speed = load("instant_speed.sympy")
instant_speed_wall = simplify(instant_speed.subs({"b_x": x2_formula, "b_y": y2_formula, "b_vx": 0, "b_vy": 0}))
save(instant_speed_wall, "instant_speed_wall.sympy")

例如,在这种情况下,我们发现圆圈以速度sqrt(5)向墙移动。

from sympy import sqrt
from perfect_physics import Circle, Wall, plot, load
a = Circle(x=-sqrt(2) / 2, y=sqrt(2) / 2, r=1, vx=2, vy=1)
w = Wall(x0=-1, y0=-1, x1=1, y1=1)
plot(circle_list=[a], wall_list=[w], xlim=(-3, 3), ylim=(-2, 3), figsize=(4, 2), font_scale=1)
instant_speed_wall = load("instant_speed_wall.sympy")
instant_speed_wall.subs({"a_x": a.x, "a_y": a.y, "a_vx": a.vx, "a_vy": a.vy,
                         "x_0": w.x0, "y_0": w.y0, "x_1": w.x1, "y_1": w.y1})

我们现在已经拥有了制造无限精确的物理引擎所需的一切。

总结第三部分

在这一部分中,我们已经看到了如何使用 Python SymPy 包来查找为圆形和墙壁的 2d 世界创建完美的物理引擎所需的低级表达式。我们找到了两个物体接触的时间表达式。当它们接触时,我们找到了它们新速度的表达式。

通过查看细节,我们看到:

  • 工程方程相对容易,但手动求解非线性方程组会很困难。令人高兴的是,计算机为我们做了这项艰苦的工作。
  • 能量和动量守恒不足以预测碰撞后的速度。我们需要第四个方程,说明碰撞产生的速度 90 不变。
  • 我们的方程经常给出奇怪的答案。我们看到了nan,负时间跨度,复数,复数无穷的答案。我们解读这些奇怪的答案,然后处理它们。例如,负的时间跨度意味着碰撞发生在过去,因此可以忽略。
  • 两次我们需要 90 度角。在这两种情况下,我们避免引入正弦、余弦等。通过使用斜率和单位向量。
  • 曾经我们需要一个无限小的时间,曾经我们需要一个无限大的质量。在这两种情况下,SymPy limit 函数让我们使用我们需要的极端时间和质量。

我们现在已经创建了低级表达式,为高级层提供了基础。我们在 Part 1 和 Part 2 中看到了高层。当我们把这两层放在一起,我们得到了一个完美的物理引擎。然而,这将是痛苦而缓慢的。在第 4 部分的中,我们将加速引擎一点(但还不够)并讨论其他限制。

如果你有想让我运行的模拟的想法,请发给我。它们可能成为第五部分的基础。

你可以从CarlKCarlK/perfect-physics(github.com)下载这段代码。让我知道如果有兴趣,我会创建一个更好的安装程序。

跟随卡尔·m·卡迪— Medium 获得下一部分的通知。所有部分都将免费阅读。最后,在 YouTube 上,我有旧的(近似的)物理模拟和一些试图幽默的自然视频。

Python 中完美的、无限精确的游戏物理学(第 4 部分)

原文:https://towardsdatascience.com/perfect-infinite-precision-game-physics-in-python-part-4-9cdd609b3e6c

改进速度和功能,完美和不完美

一条蟒蛇输给了一只乌龟——来源:https://openai.com/dall-e-2/

这是向您展示如何用 Python 编写一个完美的物理引擎的四篇文章中的第四篇。这是我将所有物理、数学、甚至哲学转化为编程的宏伟目标中的一小步。 所有代码在 GitHub 上都有。Part 1介绍了高级引擎,并将其应用于牛顿的摇篮、网球&篮球掉落等物理世界。 第二部 将引擎应用于台球的打破,揭开了意想不到的非决定论。在 第 3 部分 中,我们通过工程方程创建了低级引擎,然后让 SymPy 包做了很难的数学运算。

这篇文章是关于我们完美的物理引擎的局限性。

它是缓慢的。我们将看到如何使它更快一点,但还不够快,无法使它实用。除非……我们用数值近似值代替它的完美计算。我们可以做到这一点,但如果我们这样做,我们将消除引擎存在的理由。

我们还会看看它的许多其他限制——例如,只有 2D,没有重力,等等。对于每个限制,我们将讨论一个可能的扩展来修复它。一些扩展会很容易。其他的就很难了。

速度

当我启动这个物理引擎时,我希望所有的表达式都简化成类似于8*sqrt(3)/3的东西。当我看着这些表情时,我的希望破灭了。考虑三角形内接的三个圆:

三角形内接的三个圆(14 视频秒,63 模拟单位)

我们将查看在一些碰撞事件之后一个球的 x 位置的表达式:

10 次碰撞事件后,表情看起来还不错。40 后,我们看到一个平方根里面有一个平方根,这似乎是不祥之兆。到了 50 个事件,表情爆炸。

下一个图显示了表达式的长度与碰撞事件数的关系。请注意,y-轴是对数轴,因此我们至少看到了指数爆炸。

我实现了以下加速。他们使引擎速度提高了几倍。然而,请注意,一个“快几倍”的指数,仍然是指数缓慢。

  • 在安全的情况下使用浮点:在安全的情况下,将表达式评估为(近似)浮点或复杂。例如,为了检查两个物体是否相向运动,我们找到它们的瞬时相对速度(见第三部分)。我们需要知道speed>0。我假设如果float(speed)<-0.00001,那么速度不是正的。类似地,如果一个速度的(近似)虚部比 0 大 0.00001,我假设速度是复数。这种优化很有帮助。但是,有些值太接近于零而无法使用,仍然需要进行符号评估。
  • 回收一些接下来的碰撞:在下面的世界里,A 和 B 接下来会发生碰撞。稍晚一点,C 会与墙壁相撞。在 A 和 B 碰撞后,我们需要计算它们与场景中每个物体的下一次碰撞。然而,我们不需要重新计算 C 和墙的碰撞,因为它没有参与 A 和 B 的碰撞。在许多情况下,避免这种重新计算可以大大提高速度(从对象数量的平方到线性)。我们还可以通过跳过两对都是静止的线对来避免一些计算。

  • 利用一些并行性:发动机的几个部分的工作可以并行完成。具体来说,为一系列物体对找到下一次碰撞的时间。此外,在保存所有碰撞事件后,我们可以相互独立地渲染视频帧。我使用了 mapreduce1 ,这是我最初为基因组处理编写的多线程/多处理库。令人惊讶的是,在多个进程中运行 SymPy 函数并没有给我带来净收益。我不知道原因,但怀疑 SymPy 的自动缓存。

那么,一个完美的无限精度物理引擎是不是注定不切实际?是啊!然而,不完美和有限精度也是可行的。

数值模拟:我没有实现这个。但是,如果您感兴趣,以下是转换为数值模拟器的一些步骤:

  • 生成数字代码(用高效公共子表达式)。例如,在这里我们为直到下一次圈-圈碰撞的时间生成数字代码。它生成 35 个表达式。
from sympy import cse, numbered_symbols
from perfect_physics import load
cc_time_solutions = load("cc_time_solutions.sympy")
cse(cc_time_solutions, symbols=numbered_symbols("temp"))
# Outputs ...
([(temp0, a_vx**2),
  (temp1, a_vy**2),
  (temp2, b_vx**2),
  (temp3, b_vy**2),
  (temp4, a_vx*b_vx),
...
  • 将每个圆的 x 位置组合成一个单独的 x NumPy 矢量。同样, yvxvymr 。例如,如果我们有六个圆,其中一个以 vx 为 1 移动,其他的都是静止的,我们会说:vx = np.array([1, 0, 0, 0, 0, 0])。使用 NumPy 矩阵和向量运算符自动并行计算。在这里,我们为牛顿摇篮中的所有 36 对圆计算上面的前五个子表达式。我们只需要三个快速的 NumPy 步骤,而不是 5 x 6 x 6 的计算:
import numpy as np

vx = np.array([1, 0, 0, 0, 0, 0])
vy = np.array([0, 0, 0, 0, 0, 0])
temp0 = vx**2 # this covers temp2
temp1 = vy**2 # this covers temp3
temp4 = np.outer(vx, vx)
temp4
# Outputs:
array([[1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0]])

如果我们这样做所有的步骤,我们会得到两个 6 x 6 的矩阵,告诉我们任意两个圆之间碰撞的时间。计算圆圈 AB 以及 BA (以及 AA )碰撞的时间似乎效率很低,但对于 NumPy 来说这是高效的。

除了速度慢,我们的模拟在其他方面也有限制。让我们看看这些限制,并考虑改进的实用性。

限制和扩展

以下是该引擎的一些限制,大致按照我认为可以/应该修复的顺序排列。

显式单位

所有的物理引擎都应该给出单位——米、英尺、米每秒、英里每小时等等。该引擎目前适用于任何一致的单位集,例如 SI 。然而,它并没有明确这些单位。采用 SI 并使这些单位显式化是应该做的,而且应该容易做到。

更有趣的是使用 SymPy 的单元库。我相信这将让引擎使用任何一致的单位系统,并容易地在单位系统之间转换。

简单非弹性碰撞

我们的模拟看起来不现实,因为圆圈从不减速。一个简单的解决方法:每次碰撞后减少一定百分比的动能。这是通过按比例减少vxvy来实现的。

向下恒定重力

现在的模拟器不了解重力。这意味着,例如,网球和篮球“下落”( Part 1 )实际上并没有下落。

没有重力,圆圈在两次碰撞之间沿直线运动。如果我们加上向下不变的重力,圆圈就会以抛物线运动。这将增加我们用 SymPy 求解的方程的非线性。据我所知,SymPy 应该可以处理这些。

粒子

有些物理论文讨论粒子间的碰撞。我曾希望现在的引擎可以把一个粒子模拟成一个半径为零的圆。可悲的是,它失败了,因为碰撞的粒子最终在完全相同的位置,但没有关于粒子的哪一侧接触另一个粒子的信息。我乐观地认为,引擎可以扩展,以解决这个问题。

3D

这将是一个有趣的引擎扩展到三维领域的工作。这样,墙将是由三个不共线的点定义的无限平面。我认为我们的方程(第三部分)可以直接扩展到 3D,SymPy 可以再次为我们解决它们。

为了实现这个扩展,我个人需要温习一下我的平面几何。然而,回想一下,对于 2D,我们使用的是斜率和单位向量,而不是三角函数。我认为这给了我们一条通向 3D 的坦途。此外,我们不应该在现有的xy变量中引入一个新的变量z,而是应该用一个变量 position,一个由三个元素组成的元组来代替这三个变量。(速度也是如此。)

渲染一个 3D 场景会比我们目前的 2D 绘图更复杂,但我认为已经有了相关的软件包。

未绑定变量

我们目前允许圆的属性,比如说vx,是一个表达式,比如3*sqrt(3)/7。然而,我们不允许它停留在vx,也就是保持不受束缚。这阻止了我们解决没有给出或不需要具体数值的物理问题。

我认为我们可以创建一个版本的引擎来处理未绑定的变量。然而,在某些情况下,引擎需要报告它的答案。比如最左边的球要去a_vx,那么最右边的球最后的b_vx会是什么?

如果你回答b_vx=a_vx,那么你记得牛顿的摇篮。然而,更好的答案是:b_vx=a_vx如果a_vx > 0;T5,否则。换句话说,我们必须考虑a_vx为零或负值,并且最左边的球永远不会击中另一个球的情况。

角动量

我想创造一个完美的物理引擎,了解一些关于旋转物体和角动量的东西。我会从一个新的引擎开始,这个引擎只有 T9 知道角动量,而对当前引擎的线动量一无所知。完美地完成直线和角度似乎很难。

其他形状

为什么只是圈子;为什么不是正方形等等。?为什么只是无限的墙;为什么不是有限的墙?

完美地做到这些是非常困难的。有角动量的正方形自旋。有限的墙可能会错过或击中狭窄的道路。正方形和有限墙都会引入凸角。

更多限制

有些限制看起来太难了,以至于我无法想象扩展可以完美地修复它们:

  • 现实摩擦和非弹性碰撞
  • 通过弹簧或其他非刚体的真实碰撞。
  • 相互引力(三体,众所周知非常非常难做到完美)
  • 声速

总结第四部分

因此,我们通过在安全的地方增加数值近似值和回收碰撞信息来提高引擎速度。然而,即使这样,对于大多数应用来说,它的速度还是不够快。我们还列举了通过扩展可以克服的局限性。我最感兴趣的扩展包括:

  • 单位
  • 向下恒定重力
  • 作为墙的三维球体和无限平面

感谢你和我一起踏上创造完美物理引擎的旅程。我鼓励你实现你自己的引擎或者扩展这个引擎。你可以从CarlKCarlK/perfect-physics(github.com)下载代码。让我知道如果有兴趣,我会创建一个更好的安装程序。

关注Carl m . Kadie-Medium获取新文章的通知。在 YouTube 上,我有旧的(近似的)物理模拟和一些试图幽默的自然视频。

性能曲线:比 ROC/PRC 更直观,比阈值指标更少假设

原文:https://towardsdatascience.com/performance-curve-more-intuitive-than-roc-prc-and-less-assumptive-than-threshold-metrics-391e777da566

一种结合两者优点的二元分类器评价方法

杰森·古德曼在 Unsplash 上的照片

在二元分类的上下文中,有两个最常见的评估度量族:

  • 阈值指标(例如:准确度、精确度、召回率、F 值)
  • 排名指标(例如,接收机工作特性(ROC)曲线、精确度-召回率(PR)曲线)

阈值指标的问题

尽管阈值度量很直观,并且易于向非技术受众解释,但它们需要特定的阈值界限来区分积极类和消极类。为了确定这个截止值,我们需要知道:

(1)结果的等级分布(又称患病率):

很多时候,0.5 被用作默认的截止值,但是在结果发生率不正好为 50%的情况下,这是不合理的。

(2)两种错误分类的相对成本(也称为第一类和第二类错误)

  • 第一类错误,即假阳性,发生在模型预测结果为阳性而实际结果为阴性的时候。例如:一名患者被诊断患有癌症,但他们并没有患病。
  • 第二类错误,或假阴性,发生在模型预测结果为阴性而实际上为阳性的时候。银行交易被认为是正常的,但实际上是欺诈性的。

在这两种类型的误差之间总是有一个折衷。当第一类错误的危害大于第二类错误的危害时(例如,从招聘雇主的角度来看,一个糟糕的招聘的成本比没有招聘到合适的员工的成本更高),我们希望提高截止阈值,以便在正面结果预测中非常精确。另一方面,当第二类错误的危害大于第一类错误时(例如,错过欺诈性交易的成本比审查虚假警报的额外时间更昂贵),我们希望降低截止阈值,以扩大正面案例的范围。

为了选择正确的阈值,我们需要能够量化这两种类型的风险。

尽管我们可能对在特定用例中哪种错误类型比另一种更昂贵有一个大致的概念,但是这些危害在实践中很少是可以量化的。依靠单一的临界值,我们错误地传达了这两种风险可以并且已经被量化的信息。

排名指标的问题

与阈值度量不同,现有的排序度量(如 ROC 和 PR 曲线)没有对错误分类错误之间的相对成本做出任何假设。它们在可变阈值上评估分类器。但是,对于非技术受众来说,它们不够直观,并且不能提供任何可操作的见解。达到 90% AUROC 的预测模型的预期效用是什么?不清楚。

尽管阈值和排名指标各有利弊,但它们都有一个吸引人的特性:它们提供了简单的方法来比较多个模型,以及随机和完美的分类器。

图片作者。阈值和排名指标的利弊。

一种改进的二元分类器性能比较方法

有没有一种评估方法代表了两个世界的最佳,即不假设错误分类成本的知识,并提供可操作的见解?答案是肯定的!

主要思想是为预测为阳性的所有案例的不同百分比绘制感兴趣的任何阈值度量的值。

从本文开始,我们将把这种图表称为性能曲线

如何阅读/绘制性能曲线?

对于每条性能曲线,X 轴在每个点显示预测为阳性的案例总数的百分比,假设所有案例都按照我们的模型预测得分的降序排序。这个百分比与区分阳性和阴性类别的截止阈值直接相关。具体来说,较低的阈值意味着预测为阳性的百分比较大,而较高的阈值意味着预测为阳性的百分比较小。虽然得出一个准确和适当的阈值是一项具有挑战性和非直观性的任务,但预测为阳性(即 X 轴上的一个点)的病例的最佳百分比应该被选择来比较模型,这可以由模型用户的操作能力来确定。这个想法在业务用例部分有进一步的解释。

另一方面,Y 轴代表您感兴趣的任何阈值度量。它可以是精确度、召回率、准确度、F1 分数或用户自定义的指标,取决于案例的排序,而不是实际的预测值(因此不需要阈值截止)。在本文中,我们使用精度和召回来演示这个概念,因为这是两个最常用的阈值指标。

随机模型和完美模型的性能曲线是什么样的?

召回:

  • 一个完美的模型会给所有的真阳性案例分配最高分。因此,当模型评估前 x%的病例时,它找到所有阳性病例的 100%(即,达到回忆点= 1 ),其中 x%是阳性类别的发生率。
  • 当随机模型完成对所有案例的评估时,它会达到 100%的标记(即 recall = 1)。

图片作者。回忆不同流行情况下的绩效曲线。

为了精确:

  • 一个完美的模型将所有的真阳性放在排序列表的顶部,给我们 1 的精度,直到我们用完所有的阳性案例。
  • 随机模型的精度保持在与样本人群中阳性类的流行率相似的水平。

图片作者。不同流行情况下的精确性能曲线。

让我们看一个业务用例!

一家保险公司一直在定期开展调查工作,以发现欺诈性保单。到目前为止,他们观察到的欺诈率大约为 0.1%。他们的调查员每周从 25 个新案例中随机抽取 10 个案例进行审计。然而,该公司很快意识到这种做法是次优的,没有效率。他们要求您建立一个 ML 模型来协助他们的调查团队。

问题 1: 假设调查员因工作能力所限,每周最多只能审核 10 个案件。之前,他们随机挑选了 10 个案例。现在,他们根据你的模型选出排名前 10 的案例。您如何确定您的模型是否比他们以前的实践表现得更好,如果更好,又好了多少?

问题 2: 假设公司更关心的是尽快抓住所有的欺诈案件。您如何证明您的模型能够以比以前的实践更有效的方式实现这一点,并量化改进?

这两个问题都非常实用,很容易被许多 ML 模型用户问到。然而,它们不能由 ROC 图或 PRC 图或者甚至任何单独的阈值度量来回答。但是:可以用性能曲线来回答!我们以下面两个情节为例。

图片作者。回忆代表示例用例的性能曲线。

图片作者。代表示例用例的精确性能曲线。

**对 Q1 的回答:**给定每周 40% (=10/25)的当前容量,在您的模型的帮助下,调查团队可以捕获约 95%的真实阳性病例(即召回率),而之前为约 41%。此外,您的模型能够在不牺牲太多精度的情况下实现这一点。事实上,在工作容量为 40%的情况下,您的模型实现了约 12%的准确率,而随机实践产生的准确率为 10%。

**对 Q2 的回答:**为了每周捕获 100%的真阳性病例,调查团队只需审核 70%按我们的模型排名的顶级病例。按照以前的做法,他们必须对 100%的案件进行审计,才能达到同样的比率。这意味着你的模型为他们节省了 30%的工作。

假设前面的实践没有随机分类器那么幼稚,只要您可以构建一个模型来反映所说的实践,您应该能够进行与上面相同的分析,其中您使用性能曲线将您的模型与基准实践进行比较。

摘要

到目前为止,我们已经展示了性能曲线可以满足的以下关键特性:

  • 是一种直观的辅助工具,它概述了模型在考虑不同预测百分比的情况下可以实现的一系列性能。模型性能可以用传统的阈值度量(如精度、召回率、F-1 分数等)来衡量,也可以用任何取决于案例排序的用户自定义度量来衡量。
  • 提供了在单个数据集上在一系列操作条件下对模型进行相互比较以及与随机和完美分类器进行比较的方法
  • 为业务利益相关者提供可行的见解

性能曲线还提供了其他一些好处:

  • 当精度和召回性能曲线与传统的精度-召回(PR)曲线一起被检查时,我们可以确定预测性能的增加(或减少)是由精度、召回还是两者共同贡献的。

例如,在多次尝试改进我们的模型之后,我们注意到最近的迭代增加了模型的 AUPRC。然后,我们检查所有迭代的召回和精确性能曲线。尽管在迭代过程中召回性能曲线看起来相对相似,但是最近一次运行的精度性能曲线看起来明显比以前好(即,更接近完美曲线,而更远离随机曲线)。因此,我们知道 AUPRC 的增加主要是由精度造成的。

如果您喜欢这篇文章,您可能也会喜欢:

使用 Polars 库对航班延误数据集进行数据分析

原文:https://towardsdatascience.com/performing-data-analytics-on-the-flights-delay-dataset-using-the-polars-library-284f385cc497

了解如何使用 Polars 对 2015 年航班延误数据集进行各种数据分析

照片由 CHUTTERSNAP 在 Unsplash 上拍摄

2015 航班延误数据集是数据分析学习者使用的经典数据集。该报告由美国交通部运输统计局发布。该数据集跟踪美国大型航空公司运营的国内航班的准点表现,并包含关于准点、延误、取消和改道航班数量的汇总信息。

在本文中,我将利用这个数据集来展示如何使用 Polars dataframe 库执行数据分析。

下载数据集

你可以从https://www.kaggle.com/datasets/usdot/flight-delays下载数据集。

https://www.kaggle.com/datasets/usdot/flight-delays

许可: CC0:公共领域

数据集包含三个 CSV 文件:

  • airlines.csv
  • airports.csv
  • flights.csv

每个 CSV 文件中的列如下所示:

作者图片

具体来说, flights.csv 文件包含超过 580 万行。这使得它成为展示 Polars 库效率的理想数据集。

加载数据集

首先,使用scan_csv()方法加载 flights.csv 文件,该方法从 csv 文件中缓慢读取:

import polars as plq = (
    pl.scan_csv('flights.csv')
)
q.collect()

数据集的快照如下所示:

作者图片

加载的数据帧包含超过 580 万行和 31 列。

查找所有被取消的航班

接下来,让我们找出所有被取消的航班。您可以通过检查取消的栏来完成。在取消的栏中只有两个可能的值:

q = (
    pl.scan_csv('flights.csv')
 **.select(pl.col('CANCELLED').unique())**
)
q.collect()

作者图片

取消的航班用值 1 表示。因此,您可以使用filter()方法来获取所有值为 1 的行,用于取消的列:

q = (
    pl.scan_csv('flights.csv')
 **.filter((pl.col('CANCELLED') > 0))**
)
q.collect()

结果的快照如下所示:

作者图片

查找每个航空公司取消的航班总数

看看哪个航空公司取消的航班最多会很有意思。您可以这样做,首先使用groupby()方法按航空公司对行进行分组,然后使用agg()方法对所有被取消的航班进行汇总:

q = (
    pl.scan_csv('flights.csv')
 **.groupby('AIRLINE')
    .agg(pl.col('CANCELLED').sum())**
)
q.collect()

作者图片

那么取消航班最多的航空公司是哪家呢?分类吧!

q = (
    pl.scan_csv('flights.csv')
    .groupby('AIRLINE')
    .agg(pl.col('CANCELLED').sum())
 **   .sort(by='CANCELLED',reverse=True)**
)
q.collect()

作者图片

每个航空公司代码代表什么?让我们加载另一个包含航空公司代码和名称的 CSV 文件:

pl.read_csv('airlines.csv')

作者图片

让我们用上面的数据框架来加入我们先前的结果:

q = (
    pl.scan_csv('flights.csv')
    .groupby('AIRLINE')
    .agg(pl.col('CANCELLED').sum())
    .sort(by='CANCELLED',reverse=True)
)**q = (
    pl.scan_csv('airlines.csv')
    .join(q, 'IATA_CODE', 'AIRLINE')
)**q.collect()

你现在知道哪个航空公司取消的航班最多了。😀

作者图片

将年、月和日列合并成一列

flights.csv 文件中,每个航班的日期使用三个不同的列来表示,即。对于执行数据分析,如果可以将三列合并成一个日期列,可能会更容易。为此,您可以使用pl.date()函数,然后使用alias()函数来重命名新创建的列:

q = (
    pl.scan_csv('flights.csv')
 **.with_columns(
        pl.date('YEAR','MONTH','DAY')
        .alias("SCHEDULED_DEPARTURE_DATE")
    )** )
q.collect()

结果现在包含一个新添加的名为SCHEDULED _ department _ DATE的列:

作者图片

with_columns()函数添加或覆盖数据帧中的列。

组合日期和时间列

除了计划出发日期存储在三个单独的列中,您还会注意到有一个名为SCHEDULED _ department的列。此列是一个整数列,存储数字,如 5,10,直到 2359。这些值代表的实际上是 HHMM 格式的出发时间。所以 5 实际上代表 00:05,而 2359 实际上代表 23:59。如果您要对这个列执行分析,您肯定需要进一步处理这个列。

这里我要做的是将四列合并成一个datetime列:

  • 年份
  • 预定 _ 出发

正如我们在上一节中所看到的,将前三者结合起来是很容易的。第四列需要一些处理:

  • 您需要将出发时间格式化为一个字符串,然后提取前两个数字来表示小时(HH)
  • 然后提取代表分钟(MM)的最后两位数字

上述操作可以写成如下形式:

q = (
    pl.scan_csv('flights.csv')
 **.with_columns(
        pl.col('SCHEDULED_DEPARTURE').apply(
          lambda x: ("{0:04d}".format(int(x)))[:2])
        .alias('HH')
    )
    .with_columns(
      pl.col('SCHEDULED_DEPARTURE').apply(
          lambda x: ("{0:04d}".format(int(x)))[2:])
        .alias('MM')
    )   
    .with_columns(
        pl.datetime('YEAR','MONTH','DAY','HH','MM')
        .alias("SCHEDULED_DEPARTURE_DATETIME")
    )** )
q.collect()

最终结果包含一个新添加的名为SCHEDULED _ department _ DATETIME的列:

作者图片

注意,结果还包含两个中间列 HHMM 。最好使用drop()功能将它们放下:

q = (
    pl.scan_csv('flights.csv')
    .with_columns(
        pl.col('SCHEDULED_DEPARTURE').apply(
          lambda x: ("{0:04d}".format(int(x)))[:2])
        .alias('HH')
    )
    .with_columns(
        pl.col('SCHEDULED_DEPARTURE').apply(
          lambda x: ("{0:04d}".format(int(x)))[2:])
        .alias('MM')
    )   
    .with_columns(
        pl.datetime('YEAR','MONTH','DAY','HH','MM')
        .alias("SCHEDULED_DEPARTURE_DATETIME")
    )  
 **.drop(['HH','MM'])**
)
q.collect()

结果中不再有 HHMM 列:

作者图片

显示目的地机场名称、城市和州

假设您想要查找从 CVG 出发的所有航班,并显示目的地的机场名称、城市和州。为此,您需要在两个数据帧之间执行一个连接— airports.csvflights.csv 。您可以使用以下代码片段完成上述任务:

df_airports = pl.scan_csv('airports.csv') 
(
    pl.scan_csv('flights.csv')
    .filter(
        pl.col('ORIGIN_AIRPORT') == 'CVG'    
    )        
    .join(df_airports, 
          left_on='DESTINATION_AIRPORT',
          right_on='IATA_CODE',
          how='left')
    .select(pl.col(['FLIGHT_NUMBER', 'ORIGIN_AIRPORT', 
                    'DESTINATION_AIRPORT', 'AIRPORT', 
                    'CITY', 'STATE']))
).collect()
  • 首先将 airports.csv 文件加载到 dataframe 中,然后是 flights.csv 文件。
  • 使用包含航班详细信息的数据框架,对从 CVG 始发的航班执行过滤。
  • 然后,使用机场数据框在所有来自 CVG 的航班上执行左连接
  • 最后,您只希望结果包含六列— 航班号出发地机场目的地机场机场城市

结果如下:

作者图片

统计每个州的机场数量

由于 airports.csv 文件包含每个州的机场列表,我们可以统计每个州的机场总数:

(
    pl.scan_csv('airports.csv')
    .groupby('STATE')
    .agg(
        pl.col('AIRPORT').count()
    )
).collect()

作者图片

如果您想查看每个州的机场名称列表,请移除count()功能:

(
    pl.scan_csv(‘airports.csv’)
    .groupby(‘STATE’)
    .agg(
        pl.col(‘AIRPORT’)
    )
).collect()

您将看到以下输出:

作者图片

找到每个航空公司的最大和最小延迟

flights.csv 文件中的 ARRIVAL_DELAY 列表示每个航班的延迟时间(分钟)。 ARRIVAL_DELAY 的正值表示航班晚点,负值表示航班提前到达。查看每个航空公司的最大和最小延误时间会很有趣:

(
    pl.scan_csv('flights.csv')
    .groupby('AIRLINE')
    .agg(
        [
            pl.col('ARRIVAL_DELAY').max().alias('MAX_DELAY'),
            pl.col('ARRIVAL_DELAY').min().alias('MIN_DELAY')
        ]
    )
).collect()

作者图片

从结果中可以看到 AA (美国航空公司)的最大延迟为 1971 分钟,而 US (美国航空公司)和 AA 的最小延迟为-87 分钟。

然而,显示最大和最小延迟并不能反映全部情况。我们可以计算到达延迟的平均值和标准偏差:

(
    pl.scan_csv('flights.csv')
    .groupby('AIRLINE')
    .agg(
        [
            pl.col('ARRIVAL_DELAY').max().alias('MAX_DELAY'),
            pl.col('ARRIVAL_DELAY').min().alias('MIN_DELAY'),
 **pl.col('ARRIVAL_DELAY').mean().alias('AVG_DELAY'),
            pl.col('ARRIVAL_DELAY').std().alias('STD_DELAY'),**        ]
    )
).collect()

作者图片

在这里,您将看到(阿拉斯加航空公司)的平均到达延迟最低(-0.976563)。

一个更好的主意是绘制直方图来显示每家航空公司的延误情况:

import plotly.express as pxdef histogram(airline):    
    df = (
        pl.scan_csv('flights.csv')
        .filter(
            pl.col('AIRLINE') == airline
        )
    ).collect()

    # plot a histogram showing the arrival delay for the specific 
    # airline
    fig = px.histogram(df.to_pandas(),
                       x = 'ARRIVAL_DELAY',
                       title=f'Flight delays for {airline}')
    fig.show()airlines = (
    pl.scan_csv('airlines.csv')
).collect()for airline in airlines['IATA_CODE'].to_list():
    histogram(airline)

下图显示了 UA (联合航空公司)的延迟频率:

作者图片

查找每个航空公司的航班号

您可以使用unique()功能列出每个航空公司的唯一航班号:

(
    pl.scan_csv('flights.csv')
    .groupby('AIRLINE')
    .agg(pl.col('FLIGHT_NUMBER').unique())    
).collect()

作者图片

您还可以使用n_unique()功能查找每家航空公司的独特航班数量:

(
    pl.scan_csv('flights.csv')
    .groupby('AIRLINE')
 **.agg(pl.col('FLIGHT_NUMBER').n_unique())** 
    # this also works
    # .agg(pl.col('FLIGHT_NUMBER').unique().count())
).collect()

作者图片

注意,您也可以在unique()功能后使用count()功能。

https://weimenglee.medium.com/membership

摘要

在本文中,我们快速浏览了一些可以在航班延误数据集上执行的数据分析。就我个人而言,在我的电脑上使用这个数据集通常会导致很多问题,因为数据集很大,并且通常需要很多时间,尤其是当你连接列或数据帧时。但是有了 Polars,事情变得更加高效,代码也更加直观。让我知道你的航班延误数据集的经验!

使用 Soda Core 进行大规模数据验证

原文:https://towardsdatascience.com/performing-data-validation-at-scale-with-soda-core-825267bb4714

深入了解数据验证的过去和现在,以及如何利用当今的工具来确保大规模数据质量

照片由 Alex wong 在 Unsplash 上拍摄

随着人们对现代数据堆栈(MDS)及其不断增长的数据技术和 SaaS 产品生态系统的兴趣不断增加,数据团队正在大力押注于它,以打开创新和能力的新大门。但更重要的是,MDS 生态系统有望为不久前做出的昂贵决策提供高效的替代方案。

毕竟,当我们将今天的数据堆栈与“第一波”BI 平台或“第二波”Hadoop 环境进行比较时,很明显,今天我们可以用更少的资源获得更多(特性、功能和价值创造),无论是在工程工作、时间还是基础架构成本方面。这是一个非常合适的时间来增强我们的数据平台,或者更好的是,更新和微调他们现有的组件。

在本文中,我们将详细介绍如何利用这一波新的数据工具,在数据验证领域用更少的资源做更多的事情。

等等,我们对数据验证有明确的定义吗?

数据验证的广义理论定义是静态的:它是一组允许我们确保我们的数据可信并符合预定义的数据质量标准的过程——通常使用 6 个关键维度(准确性、完整性、一致性、及时性、有效性和唯一性)。另一方面,像数据工程中的大多数其他领域一样,我们应用理论定义的技术方法正在快速发展,并朝着不同的方向发展——这使得概念本身相当不稳定。尽管如此,最终目标仍然是一样的: 确保我们可以信任我们正在消费的数据

既然我们已经——在某种程度上——明确了定义,让我们看看几年前我们是如何处理(有时避免)数据验证的。

数据验证的黑暗过去

多年来(想想 2010 年到 2016 年),数据工程师在没有软件工程最佳实践的情况下构建管道——目标是尽可能快地交付尽可能多的数据,然后决定我们想要用这些数据做什么。这在当时没有造成任何直接的问题,因为“大数据”仍然是大多数公司决策的次要因素,因此完全跳过数据验证是可以容忍的。

在那些年,想要确保数据质量的数据工程团队的选择非常有限:

  • 如果使用分布式计算引擎(通常在像 HDFS 这样的分布式文件系统之上)处理数据,那么团队将需要编写专门的任务/作业来清理数据并运行数据质量检查。(例如,如果团队维护进行处理的 Spark 作业,数据验证也将通过专用作业或作为处理作业的一个步骤使用 Spark 进行。)
  • 另一方面,如果数据存在于分布式数据仓库中(比如 Apache Hive),那么团队将需要编写和维护多个 SQL 查询,这些查询在不同的输入表上运行,以执行必要的检查。

在这两种情况下,难以扩展的强力方法是唯一可用的选择。不同的公司致力于构建内部框架和抽象来简化流程,但在数据社区中没有“啊哈”的时刻——数据验证需要大量的工作,因为每个人都是从头开始,所以大多数公司只是把它推到一边。

但是现在,事情发生了变化:数据在任何地方都是一等公民,指标被广泛的用户/团队使用。现在,我们经常发现自己试图理解为什么两个仪表板为同一指标提供不同的值,或者一个管道中的故障会如何影响我们的最终用户。现在,我们正在偿还建设数据管道的逾期债务,却没有考虑数据质量——那么,我们如何用开源的"第三波"工具来解决数据质量问题呢?我们如何利用这些工具来自动化现有流程,并降低大规模实现数据验证组件的成本(无论是在工程工作、时间还是预算方面)?

数据验证的现状

让我们首先从谈论数据验证在过去几年中取得的巨大进步开始我们的回答。无论是通过像 Great Expectations 和 Soda Core(以前的 SodaSQL)这样的开源项目,还是像 MonteCarlo 和 Sifflet 这样专注于更大空间的数据可观察性的 SaaS 平台,数据验证都有了很大的发展。

当我们今天谈论数据验证时,我们谈论的只是包含数据可观察性和数据操作的更广泛且快速成熟的空间中的一个组件,这个空间使我们更容易找到信心说*“是的,数据没问题”*。因此,即使我们专注于开源数据验证工具,我们正在构建的设计也可以扩展到一个详尽的数据可观察性层——但这超出了本文的范围。

然而,在范围内的是 2022 年开源数据质量的状态——所以让我们来看一下土地的布局。

巨大的期望

Great Expectations 可以说是定义了对数据验证工具期望的当前标准的工具:您定义您的检查(或期望)以及您希望如何/何时运行它们,然后您的数据验证组件会处理剩下的事情。相当整洁。

在过去的四年中,该工具在各个方面都得到了扩展:越来越长的集成列表、数据分析功能和内置的数据文档。最重要的是,巨大的期望是一个 Python 库,而你的期望只是 Python 函数。

但是还有什么比维护 Python 函数更简单的呢?YAML 和 SQL。

苏打核心(以前为 SodaSQL)

Soda Core 是另一个开源工具,它提供了确保数据验证的必要功能。尽管该工具本身也是用 Python 编写的(类似于 Great Expectations),但它以不同的方式处理数据验证:作为一名开发人员,您只需要提供一组 YAML 配置文件,告诉 Soda 如何连接到您的数据仓库,以及您希望在不同的表上运行哪些检查。

当管理拥有不同所有者和维护者的数百个表时,这种方法非常方便。最初,Soda 要求每个表有一个 YAML 文件,但是现在随着 SodaCL 的发布,你可以在 YAML 配置中利用循环和定制 Soda 语法来优化你如何定义度量/检查。

Soda 主要优先考虑 CLI 交互来运行检查(使用广泛的命令和选项),但它也提供了丰富的 Python 库,这为自定义使用和直接在 Python 应用程序中利用其检查的输出打开了大门。

具体示例:使用 Soda Core 确保大规模数据质量

为了展示现在设置可伸缩数据验证组件的简单性,让我们来看看将 Soda Core 添加到现有数据平台的不同步骤。

您可能会在各种用例中遇到这种情况,例如需要提高数据质量和增强对数据的信任,或者数据协调项目(确保您从新源使用的数据与您从您想要废弃的旧源使用的数据相匹配)。在这种情况下,添加数据验证组件已经被简化为只提供三个问题的答案。

步骤 1:我们在验证什么?

首先,我们需要从定义我们实际想要测试/验证的资产开始,以及我们想要在这些资产上运行的检查。Soda 使得这个过程非常高效,因为我们不需要编写许多冗余的 SQL 查询,我们只需要挑选我们想要利用的指标(从一长串预定义指标中)和我们想要执行的检查。

然后,我们需要通过 YAML 向苏打水传达我们的选择。我们可以使用循环、列表,甚至自定义的基于 SQL 的指标/检查,确保我们可以在定义检查时实现所有可能的场景,而没有任何冗余。

样品苏打核心检查 YAML 文件。

Soda 进行了大量优化,以最小化添加和维护数据检查所需的成本和工作,在上面的示例中,我们只触及了可用功能的表面(其他值得注意的功能包括内置的新鲜度检查和检查配置)。

使用这种方法,我们可以通过源代码控制来管理我们的数据验证检查(确保版本控制和集中化)。此外,YAML 的使用意味着无论与数据交互的用户背景如何(无论他们是数据科学家、数据工程师、ML 工程师,甚至是项目经理),他们不仅能够理解给定表的现有检查,还能够提出修改建议。不管是好是坏,YAML 设法将自己定位为技术世界的通用配置语言,所以我们不妨充分利用它。

既然我们已经定义了想要运行的检查,那么让我们来讨论一下应该在哪里运行它们。

步骤 2:我们在哪里验证?

考虑到我们正在谈论一个现代数据平台,假设我们正在应用 ELT 设计并在基于云的分布式数据仓库上运行我们的转换。

Soda 提供了到所有“主流”数据仓库的连接器,并对其查询进行了内置优化(如利用缓存),以确保在数据仓库上运行的数据验证查询的最佳性能和最低成本。记住这一点,我们只需要提供一个描述连接和我们想要使用的数据仓库的configuration.yml文件——然后 Soda 将抽象其余的。

Postgres 仓库的示例配置。

同样值得注意的是,有可能将 Soda 用于基于 Spark 的架构,这要感谢功能丰富的 Soda Spark 扩展。

既然 Soda 可以连接到我们的数据仓库并在其上运行查询,那么让我们决定我们实际上想要如何以及何时运行这些查询。

步骤 3:我们如何(以及何时)进行验证?

既然我们有一个现有的数据平台,我们可以假设我们也有一个编排器,它触发我们管道内的不同任务,并确保调度和——不出所料——编排(例子包括 Airflow 和 Dagster)。理想情况下,我们希望在我们的[Dag](https://airflow.apache.org/docs/apache-airflow/stable/concepts/dags.html#:~:text=A%20DAG%20(Directed%20Acyclic%20Graph,say%20how%20they%20should%20run.)内尽快执行数据验证—因此,让我们看看如何通过我们的 orchestrator 实现这一点。

Soda 提供与开箱即用的气流和完美的集成,建议将 Soda 检查作为我们 Dag 内的专门任务来运行。如果我们以 Airflow 为例,这可以通过不同的方式完成,比如利用python virtualenv 操作符,甚至直接通过 Airflow 的 Bash 操作符运行soda scan CLI 命令。

但是,即使我们使用不与 Soda 集成的 orchestrator(不允许我们运行 Python 包),理想的模式可能如下:

  1. 从外部源或通过 EL 组件(例如 Airbyte)接收原始数据。这些数据将被接收到我们的原始层(无论是在数据仓库还是在湖边小屋),接收的完成将反过来触发验证任务。
  2. 在我们的 orchestrator 中使用专门的数据验证任务/步骤,触发执行环境来运行 Soda Core。像 AWS 上的 Lambda 函数这样的无服务器服务(或其他云提供商上的替代服务)非常适合这种情况,因为它消除了在 orchestrator 本身中直接运行 Soda 的需要。无服务器功能将通过 HTTP 请求触发,然后任务的结果将依赖于无服务器功能的响应来确定执行是成功还是失败(这甚至可以仅仅依赖于无服务器功能的响应代码)。
  3. 将数据验证任务生成的指标推送到我们的数据仓库,以便进一步监控或在其上构建专用仪表板。为了检索指标,我们可以使用scan_result对象,它是 Soda 扫描输出的一部分。
  4. 依靠 Soda 检查的输出来确定如何继续执行我们的 DAG。这完全依赖于特定的用例来确定当检查失败时应该发生什么动作。(例如根据检查类型和问题的严重性发出警告或错误,发送警报等。)如果没有阻塞故障,那么我们将转移到管道的数据转换部分,因为我们知道可以信任数据。

Soda 核心任务的数据管道示例。(图片由作者提供)

通过这种设计,我们利用了 Soda 是一个轻量级包的事实,并在一个专用的无服务器环境中运行它,这将潜在问题的风险降至最低。

总结事情

在整篇文章中,我们看到了数据验证在过去几年中是如何发展的,以及今天实现一个专用的数据质量组件仅仅包括通过 YAML 配置回答三个问题。

这种进步极大地降低了确保大规模数据质量的成本,无论是增强现有的数据管道和提高对数据的信任,还是实现像数据协调场景中那样的特定用例。

在数据堆栈的其他部分也可以看到类似的模式,不久前的资源密集型问题变成了可以用最少的资源实现的抽象特性。这让我们回到了文章的第一点: 在期望 MDS 引入新功能或用例之前,为什么不利用其生态系统来改进我们数据堆栈中的现有组件呢?

置换语言建模,解释

原文:https://towardsdatascience.com/permutative-language-modeling-explained-9a7743d979b4

流行的变压器 XLNet 背后的核心功能解释如下

卢克·斯塔克波尔在 Unsplash 上拍摄的照片

在本文中,我将解释您需要了解的关于置换语言建模(PLM)的一切,以及它是如何与整个 XLNet 架构联系在一起的。

在 NLP 中,像 BERT 这样功能强大的通用转换器是最先进的/标准的转换器,许多人将其用于流行的任务,如情感分析、问题回答、语言建模,甚至用于 BERT 必须微调的特殊任务。虽然 BERT 对于某些任务/设置非常好,但是一个强大的、不太为人所知的转换器是 XLNet。XLNet 是最强大的变压器之一,在 20 个不同的任务中超过 BERT。

以前的方法

在最近几年的研究领域中,预训练语言模型因其在下游 NLP 任务中表现出色的能力而广受欢迎。基本上,它们真的很擅长针对非常具体的 NLP 任务进行微调。我说的语言模型是什么意思?在 NLP 中,语言模型是在给定序列中所有前面/其他单词的情况下,预测序列中的下一个单词的模型。通常,像 BERT 这样的现代语言模型是预先训练的,因为它们首先在普通英语语言(在维基百科这样的公共网站上)上进行训练,然后在更具体的下游任务上进行微调,如情感分析、问题回答等。这些语言模型通过为序列中的每个单词生成丰富的单词嵌入来工作,然后它们将这些用于其他特定的任务。

到 XLNet 为止,在语言建模中有两种主要的竞争方法。

自回归

自回归方法使用先前时间步长的观测值来预测序列中下一个时间步长的值。本质上,想想 LSTM/双 LSTM 人做语言建模的方式。它使用序列中已经预测的单词来预测序列中的下一个单词。虽然这种方法是第一种最先进的语言建模方法,但是它有一些局限性。

首先,当预测序列中的下一个单词时,自回归方法依赖于左边的单词或右边的单词。例如,当使用自回归语言模型预测句子中的第 5 个单词时,该模型仅使用信息/依赖于第 5 个单词左边的单词,或者第 5 个单词右边的单词(如果以向后的方式预测)。即使是双 LSTM 也没有真正以上下文相关的方式进行语言建模。双 LSTM 运行常规的从左到右的 LSTM,然后是从右到左的 LSTM,并使用一些线性图层将来自两个 lstm 的信息聚合在一起。它们独立地进行从左到右和从右到左的过程,而不是同时进行。这意味着在双 LSTM 中组合前向和后向信息真正发生在网络的末端,而不是网络内部。下面是一个双 LSTM 的架构。

作者图片

此外,自回归方法的另一个限制是,由于其固有的结构,序列中正确预测的下一个单词完全依赖于正确预测序列中前面单词的模型。举个例子,

P(w1w2w3) = P(w1) * P(w2|w1) * P(w3|w1w2) ... 

正确预测一行中三个单词(w1、w2 和 w3)的概率等于上面的公式。正如您所看到的,正确预测一行中的三个单词有许多依赖关系,每次将一个单词添加到序列中,它被正确预测的概率就会越来越低。我们的目标是最大化这个概率 P(w1w2w3),复合效应使得 LSTMs 的训练速度非常慢,因为每次训练它们时,它们的权重/精度都只有边际上的提高。

自动编码

自动编码方法旨在解决自回归方法中存在的一些限制。使用自动编码方法的最著名的模型是像 BERT 这样的变形金刚。这种方法基于原始的 transformer,旨在通过捕获双方的依赖关系来捕获双向上下文。

使用两种方法的组合对 BERT 进行预训练以学习语言/文本:掩蔽语言建模和下一句预测。虽然对于 BERT 来说,这是一种理解文本和接受英语语言预训练的非常有效的方法,但是自动编码方法的主要局限性在于预训练中使用屏蔽语言建模。如果你不知道什么是屏蔽语言建模,它本质上是一个填空任务,BERT 随机屏蔽输入中的一些标记,并训练自己使用屏蔽标记周围的上下文信息来预测屏蔽标记实际上是什么。这种方法的优势在于,它允许 BERT 使用来自输入中任何地方的信息进行组合,以生成更高级/更丰富的单词嵌入。这种方法的一个限制是,当预测一个屏蔽的标记时,输入中有其他屏蔽的标记,如果两个或更多屏蔽的标记相互依赖,会发生什么?BERT 将无法捕获/学习这两个标记之间的上下文信息,从而使它为这些标记生成的编码/嵌入不够标准。BERT 的另一个限制是它只能接受固定长度的序列:BERT 最多只能接受 512 个令牌作为输入。另一方面,XLNet 没有输入约束。

XLNet

XLNet 是一个自回归模型,旨在解决 BERT 和 LSTMs 带来的限制,它在许多任务上优于 BERT。

就像 BERT 一样,XLNet 这样的语言模型必须能够生成单词嵌入矩阵。如果一个句子被输入到模型中,模型应该能够为每个单词输出一个固定长度的向量嵌入。为了创建这些丰富的、上下文化的嵌入,XLNet 使用置换语言建模。

给定一个序列***【x】,自回归模型(如 LSTM 或 XLNet)是一种计算概率的模型,该概率为P(Xi | x<I)***(|代表给定,因此这实质上意味着该模型在给定序列中所有 0…i 个单词出现的概率的情况下预测序列中第 I 个单词出现的概率)。这类模型(像 LSTM 的)是不对称的,并且没有从语料库中的所有标记关系中学习。LSTMs 仅仅是序列的一种可能排列上的自回归模型:从左到右的记号顺序。

诸如 ELMo 之类的其他类型的自回归模型允许模型学习令牌的上下文表示。在 ELMo 的具体情况下,它可以允许模型以相反的顺序学习 P(xi | x > i) 或上下文信息。与 LSTM 的模型相似,ELMo 模型在序列的两种可能排列上是自回归的,向前和向后。除了 ELMo 之外,还有许多其他可能的组合来查看模型可以在哪里学习一些标记之间的有趣关系。例如, P(xi | xi-1,xi+1) ,可以允许一些有趣的嵌入。本质上,记号的任何组合都可以让自回归模型学习有趣的关系,这是置换语言建模背后的基本基础。

XLNet 本质上做了我上面描述的事情,但是针对一个序列的所有不同排列。考虑一个有 N=4 个标记的序列[狗,毕竟是哺乳动物]。看看 4 个都有的那套!排列 Z = [[1,2,3,4],[1,3,2,4] … [4,3,2,1]]。XLNet 是一个对所有这些排列都是自回归的模型。它预测一个记号的概率,给定所有先前的记号 x < i 并且它可以计算序列的任何排列的概率。

例如,给定前面的元素,它可以计算第 3 个元素的概率,并且它对任何排列都这样做,以便它可以看到前面的元素的哪个组合最有可能(最高概率)产生正确的第 3 个元素,即单词 all 。考虑到所有 4 个!置换,XLNet 考虑了令牌之间所有可能的依赖关系。

虽然置换语言建模是 XLNet 的主要贡献,但它不是 XLNet 的唯一组件。XLNet 还包含像注意力屏蔽这样的东西——这有助于它理解词序——和两个自我注意力流。你可以在这篇由 Borealis AI 撰写的文章中读到更多关于这些概念的内容。

XLNet 的缺点

虽然置换语言建模是论文的主要贡献,并且它确实成功地克服了屏蔽语言建模问题,但是它有一些缺点。首先,也是最明显的,与 BERT 相比,XLNet 通常计算量更大,训练时间更长。特别是在当前的研究环境中,许多关于变形金刚/语言模型的论文被训练得更快、资源更少。这方面的一个例子是像 DistilBERT 这样的模型。XLNet 的另一个弱点是它在较小/较短的输入句子上表现不佳。在 XLNet 的预训练中,它主要是对长输入序列进行预训练,置换语言建模旨在捕捉长期依赖关系,因此 XLNet 不擅长短输入。

我希望您觉得这些内容很容易理解。如果你认为我需要进一步阐述或澄清什么,请在下面留言。

参考

*XLNet:用于语言理解的广义自回归预训练:【https://arxiv.org/abs/1906.08237 *

北极光 AI:了解 XLNet:【https://www.borealisai.com/en/blog/understanding-xlnet/】T4

语言模型的困惑再探

原文:https://towardsdatascience.com/perplexity-of-language-models-revisited-6b9b4cf46792

从基础信息论到实用计算

来源:作者

又一个关于语言模型困惑的帖子?

语言模型 (LM)目前处于 NLP 研究的前沿。基于 Transformer 架构[1]的预训练模型,如 GPT-3 [2]、伯特[3]及其众多变体 XLNET[4]、罗伯塔[5]…通常用作解决各种下游任务的基础,从机器翻译到文档摘要或开放域问题回答。他们的零射击能力似乎很有前途,该领域最大胆的人将它们视为更普遍的认知技能的第一瞥,而不是迄今为止监督学习的狭义概括能力[6]。

从一个更平凡的角度来看,LM 是一个简单的概率分布模型, p ( x ₁, x ₂,…)在一系列标记( x ₁, x ₂,…)上,这些标记组成了给定语言中的可理解文本,希望是你正在阅读的语言。

在过去的几年中,NLP 社区已经设计了一些度量和基准来评估这种 LM 的质量。一种选择是测量下游任务的性能,比如分类精度,任务范围内的性能,这就是 GLUE 基准测试所做的[7]。对于语言生成的更微妙和难以量化的方面,如生成文本的连贯性或可接受性,人们也可以求助于主观人类评价[8]。另一方面,我们发现内在的,独立于用例,像交叉熵(CE)、每字符位数(BPC)或基于信息理论概念的困惑(PP)这样的度量。在这篇短文中,我们将关注困惑

简而言之,当一个语言模型生成一个新的标记时,它的困惑度量了一个 LM 的不确定性程度,这个不确定性是在很长的序列上平均的。因此,PP 越低,LM 越好。显然,PP 将依赖于模型所使用的特定的标记化,因此比较两个 LM 只有在两个模型使用相同的标记化的情况下才有意义。

有很多论文、博客文章和评论试图解释这一指标的直觉和信息理论来源。但是我敢说,除了少数例外[9,10],我发现这种过多的资源相当令人困惑,至少对于像我这样的数学头脑来说是如此。因此,本教学笔记的目标是建立困惑的定义,并以简化的方式解释它,从基本信息和理论概念开始,摒弃任何类型的术语。只是很好的旧数学。关于证明,例如参见[11]。

免责声明:这个笔记不会帮助你成为一个 Kaggle 专家。

单个随机变量的熵和困惑

先说简单的事情。让我们回顾一下如何在有限的𝒳.集合中测量单个随机变量(r.v.) X 的随机性您可能会将 X 视为文本信息的来源,将值 x 视为由此来源生成的标记或单词,将𝒳视为某种标记化过程产生的词汇表。在继续之前,让我们修正一些不言自明的符号:

X定义为(对数的底数为 2,因此 H [ X ]以位为单位测量):

正如经典的信息论【11】告诉我们的那样,这既是对 r.v. X 随机性程度的一个很好的度量,也是对信源 X 产生信息的速率的一个度量。事实上,如果l(x):= |c(x)|表示前缀码 C (粗略地说,这是一种可以被动态解码的码)的编码 C ( x )在𝒳中的长度比 香农的长

此外,对于最佳代码 C *,长度验证达到一位[11]:

这证实了我们的直觉,即频繁出现的令牌应该分配较短的代码。熵是一个深刻而多面的概念,因此我们不会在这篇短文中穷尽它的全部含义,但是这些事实仍然应该说服最怀疑定义(1)的相关性的读者。当 X 为常数时,熵 H [ X 为零,当 X 在𝒳:上均匀分布时,熵取最大值

因此,( 2)中的上限促使将单个随机变量的困惑度定义为:

因为对于一辆统一的房车来说,它只是减少了|𝒳|可供选择的箱子数量。对于一个非均匀的 r.v. X 我们可以把 PP[ X ]解释为我们面对的一个有效的不确定性,我们应该猜测它的值 x 。我们还需要两个 r.v. XY关节条件 的定义:

上面的第一个定义很容易暗示熵是两个独立随机变量的可加量。

第二个将条件熵定义为条件分布的熵,在条件 y 上平均。让我们假设对于一个源,我们有一个未知分布 P 和一个模型 Q 来近似它。然后我们将源 P 相对于模型 Q交叉熵 CE[ PQ ]定义为:

KL 是众所周知的 Kullback-Leibler 散度,它是概率分布之间的近似性的几种可能定义之一。CE[P,Q]和 KL[PQ]在码长方面都有很好的解释。CE 是当令牌 x 由源 P 产生,但是它们的编码被选择为对于 Q 最优时,编码长度 l ( x 的期望值。情商。(8)由此可见,KL[PQ]就是这么说的我们在使用错误编码时必须付出的代价。

平稳遍历过程的熵和困惑

一个随机过程 (SP)是一个 r.v .的索引集。为了我们的目的,这个索引将是一个整数,你可以把它解释为一个记号在一个随机记号序列中的位置:( X ₁, X ₂,…)。最简单的 SP 是一组从同一分布 P 中提取的 i.i.d. r.v。假设我们有一个样本 x ₁, x ₂,……从这样一个 SP 中抽取,我们可以将它的经验熵定义为:

弱的大数定律随即意味着相应的估计量趋向于 P 的熵 H [ X :

也许用更直观的术语来说,这意味着对于足够大的样本,我们有一个近似值:

或者等同地

从这个基本观察开始,通过定义所谓的典型序列的集合为那些经验熵不太远离真实熵的序列,信息论的基本结果可以被证明[11](其中 SNCT 在上面),但是我们在这里不会被这些问题困扰。

在 NLP 中,我们感兴趣的是的随机来源。r.v. ( X ₁,x₂……)的序列,因为有意义的文本中出现的单词肯定不是独立的。我们将把𝜒称为这样一个物种。那么,对于一个长句子来说,概率 p ( x ₁, x ₂,…)的近似值(6)是什么呢?实际上,我们必须在这里对 SP 𝜒:=( X ₁,x₂……)做一个简化的假设,假设它是静止的**,我们的意思是**

对于令牌的所有序列( x ₁, x ₂,…)以及所有时移 t 。严格地说,对于文本文档来说,这当然不是正确的,因为单词在文本的开头和结尾的分布是不同的。但这是我们前进时必须做出的一个近似值。对于这样的平稳随机过程,我们可以考虑以至少两种方式定义熵率(即每个令牌的熵)。这里有一个将熵率定义为非常长的序列的每个令牌的平均熵:

这是另一个定义,它是基于前一个记号的最后一个记号的平均熵,同样对于非常长的序列:

将我们的注意力限制在定态粒子上的全部意义在于,可以证明[11]这两个极限是一致的,从而为我们提供了定态粒子𝜒.的熵率h[𝜒]的一个很好的定义

最后,我们可以类似于(3)将静态 SP 的困惑度定义为:

这种解释很简单,也是我们从一开始就试图抓住的。它是稳定的 SP 𝜒.的每个令牌的不确定性有没有一种近似法可以把方程(7)推广到稳态速度?可惜,一般的没有!我们必须对 SP 𝜒.做一个额外的技术假设也就是说,我们必须假设 SP 𝜒是遍历的**。对遍历性的详细解释会把我们引入歧途,但感兴趣的读者可以参见[11]中的第 16 章。非常粗略地说,遍历性条件确保了任何单个 r.v. X ₁在过程𝜒的分布 P 上的期望𝔼[ X ₁可以用从𝜒 ( 比尔科夫遍历定理 )中抽取的单个很长序列( x ₁,x₂……)的时间平均值来代替:**

对于遍历过程,斯朗大数定律成立。

因此,如果我们假设我们的源𝜒确实是稳定的和遍历的(这在实践中对于文本可能仅是近似正确的),那么(7)的以下概括成立( 香农,麦克米兰,布雷曼定理 (SMB) [11]):

因此我们看到,要计算一个遍历过程𝜒的熵率h【𝜒】(或困惑 PP[𝜒】),我们只需要画一个很长的序列,计算它的负对数概率,我们就完成了!不需要执行大量的求和。所以让我们欢呼吧!(11)背后的直觉是,在某种程度上,一个无限长的序列实际上包含了它们全部。

界定未知过程的困惑

如果我们知道相应的概率分布 p ( x ₁, x ₂,…),所有这些对于计算像英语这样的语言的熵(或困惑)将是完美的。但不幸的是我们没有,因此我们必须求助于语言模型 q ( x ₁,x₂……)作为近似。幸运的是,我们将能够为 p 构建熵率的上限。这个上限将会是模型 Q (语言模型)相对于源 P (实际语言)的交叉熵。它的定义与 SP 的熵率(8,9)和两个普通分布的交叉熵(4)直接相似:

因此,当面对由源 P 产生的令牌时,它是模型 Q 的每个令牌的不确定性。第二个等式是一个类似于为熵率建立等式(8)和(9)的定理。语言的未知熵的承诺界限是简单的[9]:

最后,对于被视为未知源 SP P 的语言,模型 Q困惑度被定义为:

换句话说:当由语言 P 生成时,模型 Q 不确定接下来会出现哪个标记,就好像它必须在 PP[ PQ 选项中进行猜测一样。为了计算 PP[ PQ ]或 CE[ PQ ,我们可以使用 SMB 定理[9]的扩展:

最后:语言模型的困惑

为了具体起见,假设我们有一个语言模型,它的概率 q ( x ₁, x ₂,…)由一个类似 LSTM 的 RNN 定义:

SMB 结果(13)然后告诉我们,我们可以通过采样任何足够长的令牌序列并计算其对数概率来估计 CE[ PQ 。将 RNN 分布(14)的显式表达式代入(13)以获得(12)中 CE[ PQ ]的近似值,我们最终获得语言模型 Q 相对于语言源 P 的困惑度的显式公式:

作为 a 数值的一个例子,GPT-2 在维基百科数据集上实现了每个字符 1 比特(=令牌),因此具有字符困惑度 2 =2。英语单词的平均长度等于 5,这就相当于单词困惑度等于 2⁵=32.

困惑的实用计算

我们在实践中可以使用(15)来计算困惑度的序列的长度 n 受到由 LM 定义的序列的最大长度的限制。例如,GPT-2 的最大长度等于 1024 个令牌。为了获得困惑的可靠近似值,最好的办法似乎是使用滑动窗口,正如这里的所示【10】。为了提高性能,也可以使用大于 1 的步幅。拥抱脸文档[10]有更多的细节。

瞧啊!

参考

[1] Ashish Vaswani,Noam Shazeer,Niki Parmar,Jakob Uszkoreit,Llion Jones,Aidan N. Gomez,ukasz Kaiser,Illia Polosukhin,注意力是你所需要的一切,神经信息处理系统的进展 30 (NIPS 2017)。

[2] Tom Brown 等人语言模型是很少出手的学习者,神经信息处理系统进展 33 (NeurIPS 2020)。

[3] Jacob Devlin,Ming-Wei Chang,Kenton Lee,Kristina Toutanova, BERT:用于语言理解的深度双向转换器的预训练,计算语言学协会北美分会 2019 年会议录:人类语言技术,第 1 卷(长和短论文)。

[4]杨,戴子航,,Jaime Carbonell,Russ R. Salakhutdinov,Quoc V. Le,XLNet: 用于语言理解的广义自回归预训练,神经信息处理系统进展 32 (NeurIPS 2019)。

[5]刘,迈勒奥特,纳曼戈亚尔,杜,曼达尔乔希,陈,奥梅尔列维,,卢克泽特勒莫耶,韦塞林斯托扬诺夫,罗伯塔:一种稳健优化的伯特预训练方法, , (2019)。

[6] Takeshi,Shane Gu,Machel Reid,Yutaka Matsuo,Yusuke Iwasawa,大型语言模型是零射击推理器,论文代码(2022 年 5 月)

[7]王敬实,阿曼普里特·辛格,朱利安·迈克尔,菲利克斯·希尔,奥梅尔·利维,塞缪尔·r·鲍曼, GLUE:一个多任务的自然语言理解基准和分析平台, arXiv:1804.07461 。

[8]欧阳龙等用人类反馈训练语言模型遵循指令,(2022 年 3 月)

[9] Peter F. Brown,Vincent J. Della Pietra,Robert L. Mercer,Stephen A. Della Pietra,Jennifer C. Lai,*英语熵的一个上界估计,*计算语言学,第 18 卷,第 1 期,1992 年 3 月。

【10】抱脸文档, 定长模特的困惑

[11]托马斯·m·盖,乔伊·a·托马斯,信息论要素,第 2 版,威利 2006。

图像、视频和实时流中的面部遮罩

原文:https://towardsdatascience.com/personal-identity-masking-in-images-videos-and-live-stream-28892f9db46f

一个简短的教程,教你如何有效地模糊人脸以去除个人身份

图片由布鲁诺·山崎在 Unsplash 上拍摄

个人身份屏蔽技术已经在计算机视觉和自然语言处理中得到应用。在这里,在计算机视觉中,当这些媒体中的其他信息优先于所涉及的个人身份时,面部掩蔽或个人去识别通常被部署在图像、视频和直播流中。

例如,如果受访者不希望在新闻采访中被认出,他或她的选择可以通过在视频会议期间模糊她的脸来尊重。在其他应用中,例如由外部方捕获身份证中的某些非个人信息,也可以编写脚本来迭代地自动掩盖每个卡中的人脸。

在本教程中,我们将看到如何在 Python 中实现这些。我们开始吧!

1.简介—面部遮盖的步骤

建立人脸掩蔽模型的第一步是选择人脸检测模型。面部检测模型必须能够以合理的推理速度和接近 100%的准确度在图像或视频帧中挑选出多个面部,以便易于部署。关于人脸检测的话题在我之前的教程中已经详尽地讨论过了,在这里你可以探索各种人脸检测模型,以及它们的优缺点:

https://medium.com/artificialis/real-time-face-detection-with-dual-shot-face-detector-dsfd-c37e19e5af3d

选择合适的人脸检测模型后,在以下步骤中:

  1. 给定由人脸检测模型提取的坐标,将每个人脸提取为感兴趣区域(ROI)框。
  2. 高斯模糊整个面部 ROI。在某种程度上,我们可以满足于简单的面膜。但是模糊外盒对于媒体呈现来说在美学上可能是不有用的。
  3. 创建一个遮罩:创建一个与 ROI 尺寸相似的黑色方框,并在方框边缘画一个白色椭圆。
  4. 在将遮罩应用于 ROI 和高斯模糊的 ROI 之后,我们然后在原始 ROI 上实现了模糊的椭圆。
  5. 用更改后的 ROI 替换图像或视频帧中的 ROI。

注意,当我们应用 OpenCV 高斯模糊模块时,我们必须为它的操作指定一个内核大小。如果 ROI 的分辨率增加(因为人脸可能变得更近或更大),较小的核维数可能无法实现类似程度的掩蔽。因此,我们添加了代码,根据 ROI 的大小在算法上增加了内核的维数。

最后,让我们演示代码和最终输出。

2.演示:视频和实时流中的面部遮罩

应用 DNN 人脸检测模型的实时人脸掩蔽。

代码应用 DNN 与面部掩蔽的网络摄像头。按“q”结束视频会话。

3.演示:图像中的面部遮罩

DSFD 人脸检测身份证图像人脸掩蔽。其他敏感文本信息被手动屏蔽。图片作者。

用于在终端上应用具有面部掩蔽的 DSFD 的代码。

4.最后的想法

我希望你和我一样喜欢这个简短的教程,并希望介绍的技术在不久的将来对你的探索是有用和有趣的。代码和 requirements.txt (包含必要的 Python 包)文件都在我的 GitHub 中。请在评论区告诉我你的想法,以及我们如何进一步改进面部掩蔽算法!

因为我会定期撰写文章,所以我也希望您能跟随我一起踏上学习数据科学的旅程。如果你愿意,可以看看这里教你如何有效地学习数据科学或任何其他学科的文章:

再见,祝你学习愉快。干杯!_/_

感谢阅读!如果您喜欢这些内容,请在 媒体 上阅读我的其他文章,并在LinkedIn上关注我。

支持我!——如果你没有订阅 Medium,并且喜欢我的内容,请考虑通过我的推荐链接加入 Medium 来支持我。

https://tanpengshi.medium.com/membership

个人知识图表

原文:https://towardsdatascience.com/personal-knowledge-graphs-9a23a0b099af

新一代笔记工具帮助我们快速地将想法组织成知识图表

个人知识图的演变。图片由作者提供。

去年,我的老朋友阿伦·巴特丘向我介绍了新一代创建个人知识图表的软件(PKG)。虽然 PKG 的概念可以追溯到 2019 ,但这个术语最近变得流行起来,因为新一代的笔记软件使用了知识图表表示。

这篇博客将向您介绍 pkg,然后讨论 pkg 可能如何影响您的整体企业知识管理战略,以及它们最终可能如何与 企业知识图协同工作。

剧透预警:我上钩了!

什么是个人知识图谱?

PKG 是一种新的软件,它允许你以灵活的非线性知识图表的形式有效地做笔记。pkg 是从旧的线性笔记和概述软件发展而来的。pkg 很快在学生、研究人员、软件开发人员、博客作者和创造性内容作者中流行起来。

使用链接便笺记笔记的简史

早在 16 世纪,德国的研究人员开发了一种叫做 Zettelkasten 的知识获取系统。它本质上是一种将想法放在索引卡上的方式,显示想法或概念如何与其他概念相关联。Zettlekasten 提出的“一卡一概念”和“概念链接”的概念在 20 世纪 60 年代通过“超媒体”的概念转变为电子形式,其中文档的任何部分都可以链接到该文档或任何其他文档的任何其他部分。超媒体演变成超文本,成为超文本标记语言(HTML)的基础。2001 年,万维网(蒂姆·伯纳斯·李)的作者们也试图给这些链接添加关系类型,这成为了语义网的基础。语义网试图使在网页上存储分散但仍然相连的信息变得更容易。

在概念链接发展的同时,我们看到了个人笔记软件行业的增长。这些电子工具,如概述工具、思维导图工具和跨平台工具,如 Evernote ,允许个人在许多不同的设备上创建和保留他们的笔记,如手机和平板电脑。这些不同的设备都被绑定到一个云连接存储系统,这样你所有设备上的所有笔记都可以保持同步。

知识获取和知识交换降价的兴起

示例备忘单显示了常见的降价语法—图片由作者提供。

尽管这些笔记工具很方便,但它们缺乏一种简单的方法来捕捉和交换系统之间的关联知识。像 HTML 这样的标记语法是可移植的,但是大多数用户不想用超文本来记录他们的笔记,因为格式化像到其他概念的链接这样的东西需要太多的工作。我们需要的是一个更容易输入的“轻量级”HTML 版本。

2004 年,减价模式就是为了这个目的而创立的。一种用户快速输入知识的轻量级方式,不需要他们创建高度结构化的标记。任何文本编辑器(比如记事本)都可以用来创建标记,将 Markdown 转换成 HTML 只需要几百行 Python 代码。**自 2004 年以来,Markdown 已经稳步发展成为软件工程师和数据科学家创建代码文档的首选方式。**现在,这是在 GitHub 上用 Jupyter Notebooks 在数据科学中捕获文档,以及用 Microsites 创建项目文档的默认方式。甚至传统产品如谷歌文档也在增加降价功能。

将“智能自动完成”添加到像 VisualStudio 这样的 ide 中,可以更容易地创建和编辑标记。想指出你的回购中的一个图像吗?只需输入一个"![](“visual studio 将开始建议图像文件的路径!

组合超链接、降价和图表

我们看到了结合了概念链接、降价和知识图表的新一代个人笔记工具。这些新工具正在快速发展,现有的笔记工具开始增加更多的图形感知功能。这个新的过程和软件类别被称为个人知识图(PKG)空间。它始于创新项目,如漫游研究和开源产品 T2 黑曜石。

为什么个人知识图这么受欢迎?第二大脑理论。

“第二个大脑”经常在 PKG 社区出现。随着时间的推移保留这些知识的想法是一个流行的卖点。图片来自黑曜石文档网站。作者截屏。

几乎任何研究领域的新信息量都呈指数增长。对一个主题进行简单的谷歌搜索就像用消防水管喝水一样——很难在源源不断的信息中找出什么是必要的。这里有一些人们告诉我他们喜欢他们的 pkg 的常见原因。

优势一:快速整理思路

pkg 允许你快速组织来自这些信息流的信息,找出什么是重要的,在现有主题之间建立新的联系,并创造你的洞察力。您可以导入任何文本,只需突出显示一个单词或短语并键入“[[”,它就会神奇地变成概念图中的一个顶点!当你输入的时候,“自动完成”可以告诉你你的 PKG 中是否已经有了它,这样你就可以快速地将条目链接在一起。

优势 2:降低你的认知负荷

在使用 pkg 之前,我经常会在连续不断的会议日程中感到紧张。我很少有时间在会议间隙写下我的想法,并在我学到新内容时尝试重新安排任务的优先顺序。现在,我既可以在会议中做笔记,也可以将笔记链接到我现有的知识库。我的压力水平已经下降了!

问题是我们大脑的短期记忆只能容纳固定数量的东西。如果我们不能让他们致力于文件,他们就会消失。我记得几天或几周后,我忘记写下这些新想法。

其中许多都与“卸载”记忆概念及其与另一个系统的关系的任务有关。这些通常被称为关于 pkg 为什么有用的“第二大脑”论点。你的 PKG 成为了你真实大脑的“持久影子”——它以一种非线性结构——一种图表——存储了关键概念及其关系,类似于我们大脑的工作方式。

优势 3:促进非线性思维

我经常看到的第三件事是,pkg 帮助我们脱离“线性思维的暴政”。遍历的顺序通常是固定的。这与使用包含固定顺序的项目列表的文档的传统笔记形成对比。文档顺序要求我们有一系列的小节。

但是你在记笔记时遇到信息的固有顺序可能而不是是你洞察世界的真正方式。大纲工具可以让你重新安排主题的顺序,并弹出和向下弹出章节的顺序。尽管如此,它远不如将每个主题连接到现有的知识图那样灵活和强大!

优势四:长期坚持

似乎公司每隔几个月就会改变存储知识的方式。这一年可能是存储在文件夹中的 MS Word,第二年可能是 wiki,然后可能是 Microsoft Sharepoint,现在我们看到 GitHub 上的微型网站的转移。每当一个组织改变它的工具,知识就会丢失。因为它们是围绕像 easy edit Markdown 这样的标准构建的,所以我们现在希望这些数据可以长期存在。虽然像概念链接、标签和别名这样的东西还没有跨平台标准化,但我们希望轻量级 PKI 不可操作性标准不需要实现完整的 DocBook 兼容性清单。

优势五:打破写作障碍

我的目标之一是尝试每隔一周写一篇博客。有时候我有个想法,写第一段,就被堵死了。我想不出如何组织我的思想,让我的读者感到有些连贯。如果我为想法准备了很多小图表,我可以看到它们随着时间的推移而增长。比喻就是种植你的“数字创意花园”,让它们自然生长。当我在一个子图中有十几个好主意时,可能是时候把它变成一个真正的博客了。

这些优点是我个人对如何使用 pkg 的看法。如果你在谷歌上搜索,你会发现学生、研究人员、创意作家和博客作者都在以不同的方式使用 pkg。

PKG 工具的挑战

个人知识图表博客的早期图表的屏幕图像。请注意,这个渲染是“平面”的,没有显示底部的概念是如何相互关联的——图片由作者提供。

尽管 PKG 的工具如漫游研究和黑曜石相对较新,但它们正在迅速增加新功能(免费和收费)。我最大的挑战是试图与他人分享我的 PKG 的子部分,并在微型网站上发布子图布局。现在,我们需要进行原油进出口操作,将我们的知识图表整合到一个可以合作的地方。然而,我怀疑这些新功能会很快出现。

我对当前这一代工具的最大挑战是我生成子图可视化的方式有限。就像使用强大的 GraphViz 库一样,我希望能够尝试多种受约束的布局算法,并使用自上而下和从左至右的布局。我想用一种类似于点的语言来指定这些布局规则。我想“固定”布局中各种节点的排列,这样它们就不会在我添加新项目时随机改变。我相信所有这些功能都会随着时间的推移而出现。现在,我们可以编写 Python 脚本,将 Markdown 转换成美人鱼结构,用于我们发布的微型网站的自动布局。

这些工具也不允许我向我的关系添加类型或属性。这也可能出现在未来的版本中。请记住,一点点的语义大有帮助!

对企业知识图市场的影响

顶点级基于角色的访问控制(RBAC)是 TigerGraph 或 Smartlogic 的本体编辑工具等企业级知识共享系统的标志。

现在,这些 PKG 系统都是云支持的桌面工具或网站。他们没有基于角色的细粒度访问控制规则,因此我可以授予我的团队对我的 PKG 子集的读或写访问权限。

然而,我不相信快速增长的 PKG 市场会找不到想要结合笔记自由流动的本质和正式策划和批准的知识图表系统的客户。随着 API 的发展,我可以很容易地看到一些功能,比如“将这个子图发布到公司的本体服务器进行审查。”

法律问题:谁拥有你的 PKG?

我不禁想到,人们构建他们的超高质量 pkg 的复杂性将如何与对组织的知识产权的关注重叠。20 世纪 80 年代,当我在美国电话电报公司·贝尔实验室工作时,我们所有的书面工程笔记都需要用标准化的美国电话电报公司·贝尔实验室蓝线笔记本完成,并标上单独的页码。我们被要求记录每一个条目的日期,这样我们就可以使用这些正式的笔记系统来保护我们的知识产权和专利。我们被告知,添加一个日期、几行文本和简单的图表可以为公司节省数百万美元。然而“个人”这个词意味着你拥有自己的第二个大脑。这将与公司法律人员保护其知识产权的担忧背道而驰。

入门指南

我最后的评论是,你可能需要一段时间来适应这些新工具。它们有点粗糙,还没有更成熟的笔记工具那样的表达方式和灵活性。我的建议是要有耐心,给自己几天/几周的时间来调整工具,以符合自己做笔记和发布的风格。尝试不同的组织概念的方法,看看你是否能找到一种有效的模式。

尽情享受吧!

最近邻的照片镶嵌:数字艺术的机器学习

原文:https://towardsdatascience.com/photo-mosaics-with-nearest-neighbors-machine-learning-for-digital-art-79ec51c55fe1

数字艺术的机器学习

k-最近邻帮助我们高效地在多维色彩空间中搜索合适的图像,以重新调整旧图像的用途并生成美丽的马赛克。

照片马赛克(图片由作者提供)

TLDR

  • 机器学习可以艺术地用于创建照片马赛克
  • 当不可能总是精确匹配时,用 K-最近邻建模提供了一种健壮的搜索方法。
  • 查看照片拼接动画的完整目录
  • 在这里获得你自己的马赛克!

什么是照片马赛克

下面举个例子!

放大动画,推荐全屏观看。(作者视频)

技术创新正在快速增长,并使数字存储变得极其廉价和容易获取。此外,现在大多数人都有带摄像头的手机,可以拍摄高质量的图像。大多数拍摄的图像被浏览几次,然后被发送到硬盘或一些云存储服务上。这导致人们积累了大量的图片库。我也不例外,因为我在新冠肺炎封锁期间有一些额外的时间,我想出了一些软件来给人们图书馆里的照片第二次生命。这个软件创建照片马赛克。

照片马赛克是通过将许多较小的图像拼接在一起而创建的图像或照片。在上面的 gif 和视频中,很容易看到有多少小的图像方块被用来创建一个更大的图片。创建这些马赛克有几种方法;例如,可以基于颜色或图像结构选择较小的图像方块或图像块并将其放置在较大的图像中。在这篇文章中,我们将重点讨论颜色匹配策略,这是数据科学或机器学习受众特别感兴趣的,因为它利用了 K-最近邻(KNN)建模方法。

框定问题

与往常一样,解构问题并围绕完成目标所需的各种组件构建功能是一种很好的做法。首先,我们需要将较大的或封面图像分割成较小的子部分(见图 1)。下面)。一旦我们有了更小的子部分,我们就可以找到匹配每个子部分的图像块。我们使用颜色匹配方法来构建马赛克,因此我们可以将问题形式化如下:对于每个封面图像子部分,找到与颜色最匹配的图块图像。

图 1: 在这里,我们将封面图像分成由白线表示的正方形子部分。(图片由作者提供)

彩色图像的结构

数字彩色图像由不同的像素颜色通道组成,这些通道组合在一起会生成彩色图像。常见的格式是红绿蓝或 RGB 图像格式,其中彩色图像由红色、绿色和蓝色通道组成。图像通常以 8 位存储,这意味着每个颜色通道的像素值范围在 0 到 255 (2⁸-1).)之间

图 2: 这里,我们捕获了子部分(顶部)和图块(底部)的平均 RGB 值。库中没有与该子部分完全匹配的内容,但已经很接近了。(图片由作者提供)

如果我们取每幅图像的平均红色、绿色和蓝色像素值,我们可以用三个数字来描述每幅图像的总体颜色。因此,为了找到适合每个子部分的图块,我们需要找到具有相同平均 RGB 像素值的图块。我们可以首先为封面图像中的每个子部分创建一个平均 RGB 像素值列表。然后,我们可以从我们的图块照片库中制作一个平均 RGB 像素值列表。一旦创建了这两个列表,我们可以迭代子部分列表并搜索图块列表库以找到匹配的平均 RGB 像素值。

通过巧妙的动态编程和高效的数据结构,这种搜索可以相对较快地执行。然而,这种方法有一个需要解决的主要缺陷。可能的情况是,您的图块图像库不完整,并且没有代表每个平均 RGB 像素值。因此,搜索具有精确平均 RGB 值的图块将一无所获,见图 2 。解决方案是找到具有最接近匹配平均 RGB 值的下一个图像块。广泛的研究已经开始确定颜色的“接近度”。例如,众所周知,黑色和白色一点也不接近。然而,我们的解决方案很简单,它利用了机器学习。

K 近邻的最接近颜色

每个子部分和单幅图块由三个数字表示,分别代表红色、绿色和蓝色的平均值。我们可以将这三种颜色值视为一个单独的维度,因此,我们可以创建一个 3D 绘图,其中每种颜色都是一个维度或轴。如果我们绘制子部分的值,我们正在为其寻找匹配的图块,在我们的库中有可能的图块,我们可以看到哪些图块具有最接近的颜色值。

图 3: 分段的平均 RGB 值以红色绘制。RGB 值相近的图块以橙色绘制。使用 KNN,我们可以找到用黄色“X”表示的最接近的匹配。(图片由作者提供)

KNN 算法可以评估库中的所有图块,并对平均像素值进行建模。然后,当搜索每个子部分的匹配分块时,模型将返回颜色值最匹配的最近邻居或分块。这个搜索如图 3 所示。使用 KNN 比强力搜索精确值更可靠,因为有些情况下精确值可能不存在。在引擎盖下,可以使用树状结构构建 KNN 模型,这使得搜索或遍历它相对高效。因此,性能也可与我们提到的其他解决方案相媲美。一旦为每个子部分颜色找到了所有的拼贴块,就可以使用简单的阵列/矩阵索引来将所有的拼贴块图像缝合在一起,以创建封面照片的最终马赛克。

关闭

如果你想要自己的照片拼图,或者需要帮助制作,请随时联系我们。我们可以改变硬盘上积满灰尘的旧图片的用途,给它们第二次机会去创造美好的事物。嘿,也许他们甚至可以成为 NFT!

让我们来看另一个使用不同动画的照片镶嵌示例。所有的动画都是通过编程制作的,将在下面的帖子中详细介绍。

分散动画,推荐全屏观看。(作者视频)

原载于 2022 年 7 月 3 日 https://www.lambertleong.comhttps://www.lambertleong.com/projects/photo-mosaic

协方差矩阵的物理意义

原文:https://towardsdatascience.com/physical-significance-of-covariance-matrix-e83110dddf27

特征向量的几何意义

绘图是查看数据本质的最佳方式之一。变量之间的变化不用任何数学计算就可以很容易地看出来。尽管如此,您可能希望深入细节,从数学上解释这些变化,并决定使用协方差矩阵。虽然协方差矩阵看起来只是一个显示变量间方差的矩阵,但它包含了解释数据趋势的基本信息。通过协方差矩阵的特征向量,可以找到变化的方向、斜率和幅度等属性。然而,解释特征向量的值比解释散点图更难。所以,知道特征向量在几何上是什么意思很重要。它有助于理解协方差矩阵的物理意义。在本文中,我将解释基于特征值和特征向量的协方差矩阵的物理意义。在本文中,我们将讨论:

  • 寻找变化的方向、斜率和幅度等属性
  • 解释变化的方向、斜率和幅度
  • 用特征向量在散点图上创建方向、斜率和大小的可视化表达。

由夏嫣·胡伊津加在 Unsplash 上拍摄的照片

用协方差解释散点图

在我们进入协方差矩阵的细节之前,让我们试着理解我们可以从变异散点图中猜测出什么。图 1 中的散点图显示了 x 和 y 变量的数据点。基于协方差矩阵,我们至少可以解释三个物理属性。

  • 最大变化的幅度
  • 向量的斜率取决于最大变化。
  • 垂直于最大变化方向上的变化幅度

这些属性在图 1 的右侧用蓝色和橙色线条表示。这些线是在没有任何计算的情况下,通过猜测可能的最小和最大变化而画出的。蓝线表示数据点最易受变化影响的位置,橙线表示垂直于蓝线的数据点不易受变化影响的位置。

图 1 —两个变量的散点图(图片由作者提供)

然而,变量“x”和“y”之间存在正的变化/关系。尽管如此,我们并不确切知道蓝线和橙线大小的差别,正如我们不知道它们的斜率和位置一样。了解这些性质将使我们能够从数学上定义正负变化,并理解一个变量的值如何根据另一个变量的变化而变化。

特征向量的几何意义

公式 1 显示了 x 和 y 变量的协方差矩阵的特征向量和特征值。如果你是解释特征值和特征向量的新手,这些值应该不会在你的脑海中响起。但是,当你看到散点图上这些数字是如何转化为物理向量的,你就会清楚地明白它们的含义了。在我们进入几何之前,让我们看看公式 1 中显示的矩阵。

λ代表特征值,v 代表特征向量。列 v1 是第一特征向量,列 v2 是第二特征向量。这些特征向量是归一化的(单位)向量。当它们乘以相应的λ值时,我们将得到它们相对于数据点的实际大小。因此,特征向量代表归一化向量,而特征值代表特征向量的大小。然而,表示特征向量的矩阵的对角线值具有相同的大小,但是符号相反,正的和负的。这给了我们关于这两个向量是否相互垂直的线索,如图 1 所示。这也意味着 EV1 和 EV2 是分别代表负变化和正变化的向量。

公式 1—协方差矩阵、特征值和特征向量

那么,这些值在几何上意味着什么呢?特征向量的值分别代表坐标系上的点xy。从公式 2 可以看出,特征向量的第一个和第二个元素表示从中心((0,0)或变量的平均值)到点(-0.9192, 0.3937)的向量。同样,从中心到点(-0.3937, -0.9192)的线表示第二个特征向量。

公式 2 —特征向量的坐标

让我们在 2D 坐标系上显示这些向量。

图 2-在 2D 坐标系上显示单位特征向量

由于特征向量 1 (EV1)和特征向量 2 (EV2)是归一化的单位向量,因此它们看起来大小相同,但彼此垂直。这意味着这些特征向量的斜率解释了不同方向的变化。第一个结论是 EV1 在“x”轴上比在“y”轴上覆盖更多的空间。因此,EV1 的单位变化将使“x”值的变化大于“y”值的变化,反之亦然。当 EV1 向量增加一个单位时,“y”和“x”值会以相反的方向变化。然而,当 EV2 向量增加一个单位时,“y”和“x”的值以相同的方向变化。既增又减。

特征向量的斜率

使用特征向量的“x”和“y”点可以找到特征向量的斜率。由于斜率定义为“y”的变化除以“x”的变化,因此我们可以用以下公式计算 EV1 的斜率。

Slope EV1: Δy/Δx= (-0.9192 - 0) / (0.3937 - 0) = -2.334

和 EV2

Slope EV2: Δy/Δx= (-0.9192 - 0) / (-0.3937 - 0) = 2.334

EV1 的斜率为负,而 EV2 的斜率为正。这再次表明,EV1 是表示负变化的向量,EV2 是正变化的向量。

特征向量的大小

向量的大小证明了两个变量之间的变化在正方向还是负方向上占主导地位。将这些特征值乘以对应的特征向量,有助于我们比较 EV1 和 EV2,找到占优势的变化方向。下图 3 显示了实际的向量大小。

图 3 —乘以相应特征值后的 EV1 和 EV2

将每个向量与相应的特征值相乘后,可以在图 3 中看到特征向量之间的差异。这个结果使我们想到向量 EV2 代表较高的变化,这是一个正的变化。为了清楚地理解它,可以通过将中心点从(0,0)更改为“x”和“y”的平均值,将这些向量投影到散点图上,如图 4 所示。

图 4 —将特征向量投影到散点图上

EV2 大约是向量 EV1 的 25 倍。意味着“x”和“y”之间存在高度正相关关系。

结论

协方差值本身代表了太多的信息。正/负和协方差值的大小分别代表变化的方向和强度。在本文中,我们深入研究了协方差矩阵,并提取了一些基本属性,从中我们可以看出这些数字在物理/几何上代表了什么。了解如何提取和解释这些属性将有助于您理解数据的特征。

参考

**【1】**特征向量概述:【https://wiki.pathmind.com/eigenvector】
**【2】**求向量的斜率:https://www . ck12 . org/book/CK-12-三角学-概念/section/5.18/
**【3】**散点图完全指南:https://chartio.com/learn/charts/what-is-a-scatter-plot/

物理学和人工智能:物理学通知神经网络导论

原文:https://towardsdatascience.com/physics-and-artificial-intelligence-introduction-to-physics-informed-neural-networks-24548438f2d5

这里是什么是物理学通知神经网络,为什么他们是有益的

马克·柯尼希在 Unsplash 上的照片

注:这篇文章从物理学的角度探讨了物理学通知神经网络,并引导读者**从物理学到人工智能。**真正好的论文是那种反其道而行之的(从 AI 到物理)是下面的一个。对这篇惊人的文章大声喊出来!:)

让我们从这个开始:

我们通过物理学了解世界是如何运作的

使用科学的方法,我们就某一现象如何起作用提出我们的假设,建立一个受控的实验室实验,并用数据证实/否定我们的假设。

更具体地说,物理学与自然过程的进化有关。我记得在我以前的大学里,一位教授以如下方式开始了一场会议:

赫拉克利特说“万物流动”。我也相信。但是如何?

这就是物理学试图回答的问题:

“万物流动”到底是怎么回事?

万物流动的方式由一些特殊类型的方程描述,称为**微分方程。**让我们试着理解它们是什么:)

1.物理学和微分方程

单词“微分”暗示着与“减法”有关的东西,这是真的。我们再深入一点。

1.1 导数的概念

函数的导数在物理学中有特定的作用。例如,速度只不过是空间对时间的导数。让我们考虑下面的实验,其中我们有一个沿着 1D 棒移动的物质点

作者图片

所以让我们把注意力放在沿着 x 轴移动的蓝色球上。特别的,假设起点是我们的 0。当球移动时,它的位置会随着**时间而改变。**具体来说,假设位置与时间的关系曲线如下:

作者图片

所以让我们更仔细地描述一下:

A.从时间 0 到时间 5 位置从 0 到 9 变化:球向前移动

  1. 从时间 5 到时间 15 位置完全不变:球静止不动。
  2. 从时间 15 到时间 17 位置从 9 到 3 变化:球向后移动。
  3. 从时间 17 到时间 47 位置从 3 变为 6:球再次向前移动。

现在,如果有一个量告诉你球何时以及如何改变它的位置,不是很好吗?知道位置变化有多大不是很好吗?这个信息就是速度,它有如下表达式:

方程式(1)

我知道这听起来令人困惑,但请听我说完。
速度只不过是两个非常接近的时间内位置的(这就是为什么这个方程被称为微分方程)除以这个非常接近的差(也就是为什么我们有h 的极限,它趋向于 0 )。换句话说,它是由时间距离归一化的位置的即时变化。

如果在某个时刻(t1),你有一个非常重要的位置增加,这意味着导数非常大,而且是正的。如果在那一瞬间位置不变,导数为零。如果变化是负的(数量减少),导数是负的。

在我们的例子中,位置按部分线性变化**,这意味着从 0 到 5、从 5 到 15、从 15 到 17 以及从 17 到 47,变化的实体是相同的。**

这是因为 t 在 0 和 5 之间的瞬时变化与 t = 0 到 t =5 的变化相同,是 9/5,同样的推理适用于上述函数中描述的所有其他时间。

1.2 数值解

现在,我们的情况非常简单。让我们考虑下面的过程:

作者图片

现在,这个轨迹更加难以建模,因为没有办法解析地计算导数**:你可以用数字代替**。这意味着您只需将等式(1)中给出的导数定义应用于时域中的所有点。顺便说一句,我想你终于准备好面对残酷的事实了:

所有真实世界的微分方程都用数值软件求解

问题是数值解可能需要进行成千上万次迭代。此外,它们需要一种智能的方法来求解微分方程(一种非常众所周知的方法被称为 龙格-库塔 ),这种方法通常被集成到更复杂的软件中(例如 POGO 用于有限元方法)。这些软件:

  1. 计算成本高吗
  2. 钱贵吗:)
  3. 需要领域知识
  4. 通常需要长时间运行(变化很大,但对于 FEM 示例,每次模拟从几分钟到几小时不等)

1.3 不适定问题

这还不是最糟糕的。一些问题被认为是不适定的。让我告诉你这是什么意思。

假设问题是“求 x 和 y”。

嗯,这很简单,对吧?

  • x+y=4,所以 x+2y=x+y+y=8
  • 4+y=8,所以 y=4
  • x=0

解是(x,y) = (0,4)。现在这个问题是而不是不适定了。对于那组条件,只有一个解决方案(我们找到的)可以满足这个问题。

现在,让我们来看看这个问题:

第一个和第二个方程基本相同!所以(x,y) = (0,4)仍然是一个解,但是(1,3)也是一个解,实际上有个无限解。这意味着这个问题是不适定的。对于一个单一定义的问题有不止一个解(实际上有无限个!).

如果我给你下列问题:

方程式(2)

所谓的反演问题把你从位移带到速度图。该逆问题用于使用超声波测量来表征材料中的腐蚀缺陷(例如,阅读 HARBUT 算法此处为)。现在已经证明,即使有一个完美的实验装置(无限数量的传感器),这个问题仍然是不适定的。这意味着这些信息仍然不足以给你一个独特而稳定的速度图( v )。

2.人工智能和神经网络

再说说 AI。介绍人工智能最简单的方法是这样说:

人工智能算法在没有明确编程的情况下执行某项任务。

自动驾驶汽车没有经过数学和明确的训练,当世界上所有的人都走到它面前时,它会停下来,但它确实会停下来。它确实会停下来,因为它以前见过数百万人,它被“训练成在看到有人在它面前走过时停下来。

特别是,所有的人工智能算法都依赖于一个损失函数。
这意味着它们被优化以计算某个函数的最小值,该最小值是算法的目标(期望输出)和输出之间的

想象一下,给定一栋房子的某些特征,你想预测它的成本(顺便说一下,这是非常著名的房屋数据集问题)。这是一个回归任务(从输入空间到连续空间)。

作者图片

如果您预测成本为 130,而实际成本为 160,则平均绝对误差(MAE)定义为:

是 30。

在我们的例子中我们的模型是一个神经网络,即 F ,处理输入,即 x ,并输出一个预测值 y= F(x) 。在这个例子中,y=130k,而目标值 t=160k。

更一般地说,我们会有一吨房子 x_1,x_2,…,x_N,并且会有 t_1,t_2,…,t_N 组值要预测。这意味着全局损失函数将是这样的:

这个损失函数取决于你的模型的参数 W 的设置。当然,最低的是损失函数,最好的
由于这个原因,损失函数经过了优化,也就是说,损失函数必须尽可能低。因此,参数被迭代改变,以给出尽可能最小的损失函数(即,落入损失函数的局部最小值)。

3.AI +物理学=物理学通知神经网络

现在,如果你读了所有这些:

  1. 你值得一片掌声:)
  2. 你可能会问,像人工智能(神经网络)和微分方程(物理)这两种不同的东西是如何相互交流的。

为了回答这个问题,我们需要添加另一个概念,即正则化。

3.1 规范一个问题

在第二章中,我们已经看到,每一个机器学习算法最终都是一个**优化问题。**这意味着您想要找到一组最佳参数 W_opt ,即最小化损失函数的参数。

问题是你可能会收敛到一个在你的训练集中最优的解,但是这个解不够一般化,并且在测试集上表现很差(过拟合)。换句话说,你的最优值可能是一个局部最优值。

让我更好地解释一下:

假设你的宝宝模型是由两个参数(w1 和 w2)构成的。您正在探索以下空间来寻找解决方案:

作者图片

使用训练集上的损失函数可以得到接近于 0 的损失值。

想象一下,在测试集(新数据集)上采用这种参数组合,损失函数会变得非常大。这意味着损失函数没有针对您的问题进行很好的定义,对于给定的模型,解决它的实际最佳方法是这样的:

作者图片

现在,怎样才能落在绿点而不是落在**红点?**通过限制算法的搜索区域。大概是这样的:

作者图片

现在,如果算法只能“看”到绿色的圆圈,就不可能会陷入红色的局部最优:)

这就是**正则化的概念:**操纵损失函数,使得解的空间受到限制,你更容易陷入全局最优而不是局部最优。

3.2 物理学通知神经网络=正则化!

你还记得等式(2)的反演问题吗?
嗯,这些家伙试图解决:)

基本上,他们有某个特定位置的位移( u ),他们想通过在算法不知道的所有位置内插 u 来知道 v 。换句话说,给定一个新的 t,y 和 x,他们想找到新的位移,然后是新的速度图。

图片取自本研究论文

现在,关于解决方案有很多争议,因为正如我们之前所说,问题是**不适定的(**或不适定的)。这意味着即使我们找到了解决方案,我们也不知道它是否是唯一的。此外,还有一些物理限制无法解决(更多关于这个这里)。让我们忽略所有这些,直奔主题。

他们想要产生位移,位移必须满足波动方程(方程 2) 。他们将这些信息纳入损失函数:

其中:

并且:

现在:

A.MSE_u 就是预测位移和目标位移的均方误差
B,MSE_f 是一个必须尽可能接近 0 的量。

简而言之,他们在做什么?无非就是一个正规化:

他们通过限制空间(惩罚不遵守微分方程的解)来帮助算法找到更好的解

为什么说神经网络是“物理学通”?因为正则化位是一个微分方程。:)

4.结论

在这篇文章的最后,我希望有一件事是清楚的:

物理学通知的神经网络只不过是一种神经网络,其具有作为损失函数中的正则化项的微分方程

如果你花了 9 分钟阅读这篇文章(恭喜你,谢谢你❤),你应该知道:

A.什么是损失函数(第 2 章)

B.什么是正则化术语(第 3.1 章)

C.什么是微分方程(第一章。)

D.为什么它是物理学知识(第 3.2 章)

如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:

A.关注我在 Linkedin 上的https://www.linkedin.com/in/pieropaialunga/,在那里我发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于现有最新技术的文章。

用于不确定性预测的物理学指导的机器学习模型

原文:https://towardsdatascience.com/physics-guided-machine-learning-model-for-uncertainty-prediction-1e3b733fd798

室内温度模型化选择最佳供热功率的应用实例

物理学指导的机器学习

虽然机器学习在许多科学领域取得了巨大成功,但遵循基本物理定律仍然是一个巨大的挑战。多个例子表明,仅仅是数据驱动的解决方案不符合物理定律。与此同时,科学中一些发达甚至成熟的理论将有助于指导 AI 收敛到更好的物理上有意义的解决方案。

与此同时,大多数物理模型不提供任何不确定性的框架,而机器学习中的不确定性量化方法有助于将不确定性引入现有的物理定律,这可能有助于我们理解数据中隐藏的相关性甚至因果关系以及简化的物理/数学模型不能完全捕捉的复杂动力学。

本文旨在证明,将物理约束纳入数据驱动的预测和不确定性量化估计模型是一种提高模型性能的有前途的方法。

下一篇文章中的示例侧重于由物理指导的机器学习模型建模的室内温度的不确定性预测。

室内温度的物理模型

在旧的加热器坏了之后,我需要为我的房子买一个新的加热器。我需要的力量是什么?太大,浪费力量;太小,不够热。在全球能源危机和气候变化持续的背景下,无论从保护环境还是省钱的目的,这个问题都值得一问。

在回答这个问题之前,我们需要了解温度的动态变化。让我们假设热量在室内空气和能储存大量能量的墙壁之间传递。同时,热量也在墙体和室外空气之间传递。见下图的插图。

作者图片:一栋建筑的热传递图

室内温度以及壁温和室外温度可以由牛顿冷却定律给出的以下常微分方程(ODE)来描述:

带加热功率的温度模型

其中 T_,T_,T_是室内、墙壁、室外温度,C_,C_是室内空气和墙壁的热容量(KJ/K ), U _ ,U_是墙壁和室外空气的传热系数(KW/K)。这里,P 是加热系统的功率。当然,当室内暖气关闭时,等式变成:

无加热电源的温度模型

上面的等式很容易理解:温度变化的速率与温度差成正比,并由热传递和系统中不同物体的热容量决定。

如果我们想评估 P 的最小值,也就是房子应该拥有的供热功率,我们必须知道热容量和传热系数的值。

室内温度的贝叶斯建模

请注意,当我们测量不同人体的温度时,不确定性是不可避免的,我们将建立一个室内温度动态网络以及贝叶斯推理来学习有关参数的后验。我们想建立一个模型如下:

  1. 用经典的龙格-库塔积分来描述常微分方程,这是一种数值方法,涉及以 dy/dx=f(x,y)形式求解常微分方程的逐次逼近法。
  2. 给定参数的先验,用 马尔可夫链蒙特卡罗 ( MCMC )学习后验概率。这个步骤可以通过封装 pymc 来实现。

该模型将在一个数据集上进行训练,该数据集是在我的房间里 24 小时内每 5 分钟测量一次的温度,我整天都在发抖,因为暖气是关着的:(室内和室外温度每 5 分钟测量一次。每小时的室外温度从 MeteoFrance 获得,并被插值为每 5 分钟一次的时间粒度。

作者图片:我房间的室内、墙壁和室外温度

Python 实现

本帖执行情况可以在笔记本中找到。让我们快速看一下实现的关键部分:

温度模型的 Pymc 实现

该代码包含三个部分:

  1. 第 6-12 行定义了参数的先验:热容量、传热系数和温度噪声。请注意,我重新调整了正确数量级的值。
  2. 第 14- 35 行使用 Runge-kutta 方法来更新向量 y_mean 的值,稍后会用到它。
  3. 第 37 行定义了一个正态分布,其平均值是 y_mean。该正态分布将用于采样以学习所有参数。

现在让 pymc 做它的工作,从后面抽取样本。这是我们想要学习的 U_、U_和噪声的轨迹图。

作者图片:模型参数的后验概率

我需要什么样的加热功率这个问题现在很容易回答:想象一下,在最糟糕的情况下,我的房子比墙的温度低 10 摄氏度,在 95%的置信度下,我需要大约 2.1 千瓦的最小加热功率。

结论

在这篇文章中,我们使用了一个简单的室内温度来演示物理定律如何指导数据驱动的机器学习模型在不确定性预测中获得良好的性能。作者认为,这种方法提供了一种有前途的方法,将现有的发展理论纳入人工智能,并将引入跨领域的合作,以提高许多估计模型的性能。

我期待你的反馈和更深入的讨论。

物理学通知神经网络(PINNs):直观指南

原文:https://towardsdatascience.com/physics-informed-neural-networks-pinns-an-intuitive-guide-fff138069563

物理学的(可读的)什么,如何,为什么通知神经网络

作者图片

如果你曾经试图阅读现有的关于物理学通知神经网络(PINNs)的文献,这是一个艰难的阅读!要么大量的方程对大多数人来说是陌生的,并假设你已经是所有概念的专家,要么过于简单而无法很好地理解。这篇文章旨在以直观的方式浏览 PINNs,并对当前的文献提出一些改进。

传统物理模型创建是领域专家的任务,他将物理模型参数化,使其最适合感兴趣的系统。例如,使用阻力、升力、重力、推力等方程创建飞机动力学模型。以及对模型进行参数化,以试图使模型与特定的飞机紧密匹配。

纯粹的数据驱动神经网络方法是尝试使用神经网络的监督学习从特定系统获得的数据来学习模型。

物理学通知神经网络 (PINNs)位于两者的交叉点。使用数据驱动的受监督的神经网络来学习模型,但是也使用给予模型的物理方程来鼓励与系统的已知物理的一致性。它们的优势在于既可以由数据驱动来学习模型,又能够确保与物理学的一致性,还能够在可用数据之外进行精确的外推。因此,PINNs 能够用更少的数据生成更健壮的模型。

PINNs 位于神经网络和物理学的交叉点。作者图片

理解神经网络、运动学和普通和偏微分方程将非常有助于完全消化本页内容,但并非能够获得直观理解的必要条件。

文献中大多数 PINNs 的例子是基于物理方程,如流体运动(Navier–Stokes)、光和波传播(非线性薛定谔方程,korte weg–De Vries)或其他此类函数,并考虑关于时间的函数[1]。

为了获得一些直觉,我们将利用运动定律来探索针。更具体地说,我们将使用抛射体运动作为一个例子,因为它提供了一个简单的例子来探索,但足够复杂以涵盖 PINNs 的各个方面。

到本文结束时,我们将了解 PINN 如何工作,以及 PINNs、纯数据驱动的神经网络和纯物理函数之间的权衡和差异。

抛射体运动(基于时间)

让我们考虑抛体运动(一个自由落体的抛体)在重力和阻力作用下的函数。根据描述抛射体运动的物理方程以及它们之间的关系,我们已经对这个问题有所了解。

射弹在时间 t 的位移矢量(位置)由以下函数定义:

位移的一阶导数给出速度矢量,定义为:

二阶导数给出加速度,定义为:

射弹的加速度由下式给出:

其中μ为阻力系数(未知变量),g 为重力矢量(已知变量)。本质上,在给定的时间点上,抛射体的加速度是相对于其在相反的行进方向(阻力)上的当前速度,并且被重力拉下。

直观地说,导数告诉我们函数的变化率。举个例子,当速度(加速度)的导数为 0 时,就告诉我们速度没有变化。当加速度为正时,速度增加。当加速度为负时,速度减小。

我们可以注意到加速度取决于速度。因此,位移没有闭合形式的解,因为不存在给出给定时间 t 的位移的有限方程。我们需要求助于数值积分方法,如龙格-库塔。

在重力和阻力的影响下,以初始位置和速度运动的射弹(轨迹)的单一地面真实位移。作者图片

在理想的世界里…

训练神经网络的基本方法包括普通的监督学习,使用整个感兴趣领域的完美数据。

在这里,网络的目标是学习

注意:本页所有的 gif 都显示了通过训练学习的函数的输出。

根据来自整个领域的完美数据训练神经网络。神经网络能够快速学习轨迹的精确模型。作者图片

问题解决了,对吧?如果你曾经将机器学习应用于现实世界的问题,你现在肯定会大笑:)

实际上…

然而,访问整个领域的完整数据是很难实现的。更现实的是,我们可以访问嘈杂、稀疏和不完整的数据。在这种情况下,采用监督学习的基本方法会给我们带来不理想的模型。

针对嘈杂、稀疏和不完整的数据训练神经网络。神经网络完全按照要求去做,并根据我们提供的数据拟合一个函数。然而,该函数对于预测射弹位移的预期用途并不十分有用。我们可以说它“过拟合”。作者图片

规范化

解决这个问题的一个典型方法是使用规范化。

L2 正规化由以下因素提供:

它具有惩罚网络中的大权重的效果,并且直观地惩罚局部梯度专门化。通过应用 L2 正则化,我们鼓励网络拟合从的数据,而不是从的数据。

对于我们有数据的领域,L2 正则化提高了我们模型的有用性。然而,在缺乏跨越整个领域的数据的情况下,该模型不能推断出超出可用数据的太多内容。

使用 L2 正则化对噪声、稀疏和不完整的数据训练神经网络。该模型在存在数据的领域更有用,但不能跨域进行外推。作者图片

进入物理学通知神经网络

与 L2 正则化训练非常相似,PINN 将数据损失降至最低,但也使用已知的物理作为额外的正则化项。

本质上,我们可以说“拟合数据,但要确保解与我们已知的物理方程一致”。

该网络的目标是使用对可用数据的监督学习来学习 f (t),因此我们有回归目标和预测值之间的均方误差(MSE)的标准数据损失。

然而,我们也知道抛射体的加速度由下式给出

我们可以将这些知识整合到神经网络的训练中。

由于常微分方程(ODEs)

给我们这个方程所需要的值,我们可以使用我们的机器学习(ML)库的自动微分功能来获得网络相对于网络输入(时间 t)的一阶和二阶导数(梯度),以获得速度和加速度向量。然后,我们可以将这些导数代入等式,得到以下结果:

所以我们在说,为了我们的网络尊重已知的物理,我们的网络对时间(加速度)(左手边)的二阶导数,应该等于一阶导数(速度)(右手边)的函数。换句话说,它们之间应该没有差别:

我们如何将这些知识应用到培训过程中?我们可以简单地将这个方程的误差(MSE)最小化为一个额外的损耗。

这给了我们一个物理损失项,它试图最小化网络梯度(速度和加速度)和已知物理给出的相应方程之间的误差。最小化数据损失和(加权)物理损失给了我们一个 PINN。这就是它的全部;).数据损失使用来自训练数据的样本进行训练,物理损失使用来自感兴趣的整个领域(我们指定)的样本进行训练。由于物理损失只需要跨域的输入,而不需要目标,因此我们可以自由选择任何适当的采样策略(例如,均匀采样是一种简单的策略)。

然而,有一点仍然存在,即μ(阻力系数)是一个我们不知道的变量。但是我们可以简单地使μ和网络参数一起成为可训练变量,这意味着μ将在训练过程中被发现,也就是说,我们可以从数据中得知我们的弹丸的阻力系数是多少。

对于贯穿这些例子的所有神经网络,网络使用 128 个神经元的 2 个完全连接层的架构,使用高斯误差线性单元(GELU)激活。尽管文献中的大多数例子使用 tanh 激活,我们发现 GELU 提供了更多的理论和经验上的好处[2] [3] [4](试着运行任何开源的 PINN 例子,把 tanh 换成 GELU)。因为我们正在最小化网络梯度的损失,所以我们使用的任何激活函数都需要对适当的连续梯度处处可微,这排除了诸如标准 ReLU (ReLU 是分段线性的,因此给出恒定梯度)的激活。

使用物理正则化(PINN)对噪声、稀疏和不完整的数据训练神经网络。物理损失允许网络通过数据点进行正则化,以及以与已知物理一致的方式在训练数据之外进行外推。由于噪音数据量很低,在这里不可能找到地面真相的解决方案。随着更多的数据和/或更少的噪声,网络能够以极高的准确度学习地面实况解。作者图片

数据损失用于确保通过训练数据的拟合(顶部),物理损失用于确保与整个领域的已知物理的一致性(底部)。作者图片

如果你更喜欢代码而不是方程,这里有一段使用 TensorFlow 实现上述训练的代码:

...[@tf](http://twitter.com/tf).function
def train(t_train, s_train, t_phys):
    # Data loss

    # predict displacement
    s_train_hat = net.predict(t_train)
    # MSE loss between training data and predictions
    data_loss = tf.math.reduce_mean(
        tf.math.square(s_train - s_train_hat)
    ) # Physics loss

    # predict displacement
    s_phys_hat = net.predict(t_phys)
    # split into individual x and y components
    s_x = s_phys_hat[:, 0]
    s_y = s_phys_hat[:, 1]
    # take the gradients to get predicted velocity and acceleration
    v_x = tf.gradients(s_x, t_phys)[0]
    v_y = tf.gradients(s_y, t_phys)[0]
    a_x = tf.gradients(v_x, t_phys)[0]
    a_y = tf.gradients(v_y, t_phys)[0]
    # combine individual x and y components into velocity and
    # acceleration vectors
    v = tf.concat([v_x, v_y], axis=1)
    a = tf.concat([a_x, a_y], axis=1)
    # as acceleration is the known equation, this is what we want to
    # perform gradient descent on.
    # therefore, prevent any gradients flowing through the higher
    # order (velocity) terms
    v = tf.stop_gradient(v)
    # define speed (velocity norm, the ||v|| in the equation) and
    # gravity vector for physics equation
    speed = tf.norm(v, axis=1, keepdims=True)
    g = [[0.0, 9.81]]
    # MSE between known physics equation and network gradients
    phys_loss = tf.math.reduce_mean(
        tf.math.square(-mu * speed * v - g - a)
    ) # Total loss

    loss = data_weight * data_loss + phys_weight * phys_loss

    # Gradient step

    # minimise the combined loss with respect to both the neural
    # network parameters and the unknown physics variable, mu
    gradients = tf.gradients(loss, net.train_vars + [mu])
    optimiser.apply_gradients(zip(gradients, net.train_vars + [mu]))...

这个简单的例子展示了 PINNs 的有效性。利用少量的噪声数据,我们已经能够学习射弹的鲁棒和精确的模型。PINN 也在这个过程中学习了抛射体的物理参数(阻力系数)。此外,由于抛体位移不存在封闭解,我们利用 PINN 得到了该函数的精确近似解析解和封闭解。

抛射体运动(基于状态)

同样,文献中的大多数例子集中在关于时间的函数上(加上其他变量)。但是关于系统的当前状态而不是时间的模型呢?换句话说,一个时间步进模型。

让我们把抛体运动问题重新表述为一个函数,它取系统的当前状态,在这种情况下是速度,并给出加速度:

我们可以定义两个函数;一个接受速度 x 和 y 分量,给出加速度 x 分量,另一个接受速度 x 和 y 分量,给出加速度 y 分量:

为了获得一些直觉,让我们考虑遵循这些函数的下列轨迹。我们从初始位移和速度开始,并查询函数以获得在该时间点施加的加速度。然后,我们使用数值积分方法(如龙格-库塔)将加速度加到速度上,并再次类似地得到位移。然后,我们重复这一过程,以获得一段时间内的轨迹。

按照这种方法,下面的位移图(顶部)显示了随时间变化的轨迹。加速度矢量场图(左下角)显示了域中任何给定速度的加速度,加速度 x 和 y 分量图(右下角)显示了矢量场的各个加速度分量(函数 fh )。

运行由 fh 组成的时间步进模型以产生轨迹的可视化。作者图片

物理学和梯度下降

我们可能会问自己,如果我们能够学习物理函数的参数值,为什么我们甚至需要涉及神经网络呢?毕竟,我们正在使用已知的物理方法来规范网络。为什么不直接在物理函数上使用梯度下降?

如果我们知道管理数据的函数由以下公式给出:

并且我们可以学习未知参数μ,那么我们可以直接在物理函数上使用梯度下降。

使用梯度下降直接训练物理函数。地面实况函数(灰色线框),具有由该函数生成的可用训练数据(灰点),以及通过梯度下降直接学习的物理函数(洋红色线框)。作者图片

在这种情况下,我们能够通过梯度下降完美地学习物理函数的精确参数化。为什么我们不这样做呢?

事实上有几个原因。正如我们在上面已经看到的,并不总是有封闭形式的解决方案可以使用,这在许多情况下妨碍了这种方法。

即使我们有可用的封闭形式的解,由于物理函数受其参数化的限制,误差梯度很可能是非单调的。这意味着学习将很有可能收敛到局部最优而不是全局最优。

考虑下面的函数 f ,我们有它的数据,我们试图通过梯度下降学习函数 g 的参数化。在任何方向将可训练参数 a 从 0 移开都会导致正误差梯度(误差变得更差)。因此 0 是一个局部最优解,梯度下降将收敛于此。然而,如果我们将 a 一直向右移动,我们会发现全局最优值,而梯度下降不会从其原始初始化值中发现全局最优值。

直接在函数上使用梯度下降无法发现全局最优参数的函数示例。(图片由作者提供,使用德斯莫斯图形计算器https://www.desmos.com/calculator创建)

当然,除了梯度下降法,还有其他优化方法…

然而,还有一个进一步的问题,这意味着即使这种方法是成功的,它可能仍然不会给我们一个有用的模型。

在前面的所有例子中,我们使用的物理函数完美地代表了产生数据的函数。然而,即使是物理函数也只是模型,是真实潜在动力学的简化。

我们所用的抛体运动方程只考虑恒定阻力和重力。那么升力、方向和高度相关的阻力、推力、科里奥利力等呢??除非我们能够精确地指定基本的物理函数,否则不可能学习一个精确模拟可用数据的参数化。

作为一个例子,让我们将一些简单的提升添加到我们用来生成训练数据的等式中:

其中 Cl 是升力系数。现在让我们试着学习一下我们之前用过的物理函数的参数化(没有升力)。

根据不同函数(有升力)产生的数据训练物理函数(无升力)。已知物理函数的学习参数化不能给我们一个潜在数据的有用模型。作者图片

啊…不理想。不存在精确模拟我们数据的可能参数化。

物理通知神经网络

我们可以应用与之前相同的技术来训练 PINN 人完成这项任务的时间步进版本。而在我们应用常微分方程(ODEs)之前,我们只有一个独立变量(时间),现在我们有多个独立变量(速度 x 和 y 分量)。

我们可以将多个功能实现为单个多输出神经网络。

我们还可以使用偏微分方程(PDEs)来获得该函数相对于独立变量的梯度:

加速度 x 分量相对于速度 x 分量的梯度。

加速度 y 分量相对于速度 y 分量的梯度。

加速度 x 分量相对于速度 y 分量的梯度。

加速度 y 分量相对于速度 x 分量的梯度。

如下图所示:

有趣的事实:加速度的一阶导数叫做 jerk,二阶、三阶、四阶导数是 snap、crackle、pop:)

根据上述方程的偏导数。作者图片

通过这样做,我们得到了已知物理的偏导数。与前面类似,我们可以用公式表示一个损失函数,它使我们的网络输出(因变量)相对于输入(自变量)的梯度与这些已知的物理方程之间的误差最小化。最小化数据损失和(加权)物理损失给了我们一个 PINN。直观地说,加权项表示我们希望对符合物理学和可用数据的重视程度。

通过在任务中训练这个 PINN,我们能够使网络与数据相适应,而且能够基于给定的已知物理规律进行正则化和外推,以及学习物理函数的参数化(在这种情况下是阻力系数)。我们能够产生精确的模型,但是 PINN 不存在需要封闭形式的解决方案的问题,并且由于使用神经网络来学习函数而不会收敛于局部最优。虽然可用的数据由单个轨迹组成,但我们已经能够在整个领域(即,从任何状态)获得射弹的精确模型,而不仅仅是接近训练数据轨迹。

训练 PINN 完成任务。地面实况函数(灰色线框)、地面实况函数产生的训练数据(灰点)、由 PINN 学习的变量参数化的物理函数(洋红色线框)和 PINN 解(绿色表面)。作者图片

正如我们之前使用梯度下降直接训练物理函数时看到的,除非给定的已知物理与产生数据的函数精确匹配,否则不可能产生有用的模型。然而,当 PINN 生成一个基于可用数据已知物理的模型时,我们能够生成一个结合了两者优点的模型。

让我们重温一下从包含 lift 的函数生成训练数据的示例:

但是为 PINN 提供了不包括升力的已知物理特性:

PINN 能够学习适合训练数据的函数(从具有升力的地面实况函数),但也确保与已知物理尽可能多的一致性。在我们有数据的地方,数据损失确保与数据相符,物理损失作为正则化项。在我们没有数据的情况下,物理损失允许基于已知物理函数的梯度进行外推。

根据不同功能(带升力,灰色线框)产生的数据(灰点)训练 PINN,我们已经通知 PINN(绿色表面)关于(洋红色线框)。作者图片

我们可以看到,PINN(绿色)比最适合物理函数(洋红色)的地面真实(灰色)模型更有用。PINN 能够拟合升力产生的数据点,但仍然能够将其与最佳拟合学习参数化已知物理函数的梯度的正则化和外推混合,而没有升力。我们还可以采取通过训练 PINN 来学习物理函数的参数的方法,并且根据我们的需要直接使用所学习的参数化物理模型。这需要权衡较高的可解释性/透明度,因为推断是通过已知的物理学进行的,而潜在的保真度较低的模型,因为物理学可能无法准确拟合数据。

结论

通过阅读这篇文章,我们已经了解了如何和为什么使用物理通知神经网络,以及使用不同方法的差异。PINNs 提供了一种学习健壮和准确的系统模型的方法,我们能够以管理数据的已知方程的形式提供现有的领域知识,即使在方程与数据不完全匹配的情况下。额外的物理信息允许发现已知方程中的变量,并允许用比单纯的数据驱动学习少得多的数据来学习物理一致的解决方案。

1【https://maziarraissi.github.io/PINNs/T4

2https://arxiv.org/abs/1606.08415

[3](GELU TDS)https://towards data science . com/on-the-disparity-with-GELU-1 DDD 902d 64 b

4【https://mlfromscratch.com/activation-functions-explained/

基于神经网络的浮游植物种类图像分类

原文:https://towardsdatascience.com/phytoplankton-species-image-classification-using-neural-networks-c80481a8c82a

在微观海洋图像中识别浮游植物物种以了解水下生态系统

疾控中心在 Unsplash 拍摄的照片

地球上的海洋是环境中最有趣和生物最活跃的部分。不幸的是,这些地区的极端压力和温度等条件使得收集这些生态系统的信息变得困难。这一挫折使得分析数据和得出关于海洋生物和水生生物的发现变得具有挑战性。

然而,最近的研究表明,水中细菌的存在,特别是单细胞生物浮游植物的存在,可以帮助我们了解海洋活动和海洋部分地区的生态系统。由于浮游生物处于水生食物链的底部,这些细菌的不同物种可以对水下系统产生深远的影响,并可能使我们能够调查人类无法到达的海洋区域。

来自数据集的角毛藻细菌的图像

这个项目中使用的数据是伍兹霍尔海洋研究所收集的 100 多种不同种类的浮游植物的图像。这些数据与玛莎葡萄园岛海岸天文台收集的实时图像一起公开发布。据报道,总共有 350 万张原始图像可供数据分析,最新的分类数据来自 2014 年。在该模型中,数据集“”2014 年标注的 IFCB 图像 ”用于分类。在麻省理工学院的许可下,这些数据可用于商业用途,您可以在此处 找到更多详细信息

首先,我们使用 Open-CV 和 OS 命令来分析所有数据的图像像素数据,允许我们以 CNN 模型可以理解的格式对所有图像进行编码。我们可以通过操作 Open-CV 库功能来改变边界或裁剪图像,并使用灰度图像格式来获得单个数值,以表示数据中 28x28 图像的每个像素的光照水平。

在导出图像的实际细菌分类和像素数据值的 NumPy 数组后,我们可以使用 Keras 开发一个顺序 CNN 模型,该模型可以基于每个图像的灰度值生成一个模型。CNN 模型的工作原理是将 28×28 像素的图像分块成更小的部分,例如 16 个更小的 7×7 像素的图像,并使用可变的权重和偏差来评估分块图像代表每个细菌群的程度。然后,模型的结构对图像的不同区域进行加权,并重复分块每个图像并评估结果的过程,直到原始图像可以与单个细菌物种相关联。

通过这一过程,该模型可以评估给定图像与一种细菌的相似程度。这种模型的创建可以帮助我们了解海洋中无法到达的部分,自动对浮游植物进行分类。通过找到海洋食物链基础的物种,我们可以推断细菌所具有的特性和不同的生物特征,从而发现其他水生生物如何在深海中行为和进化。

探索广袤的地球及其奇观有助于我们了解和研究自然现象,并让我们通过科学研究推动社会进步。海洋和其他水体是自然界的一个重要话题,了解其中的事件和生态系统可以带来新的发现,为日常生活的创新和改善开辟空间。这个项目中开发的模型可以帮助我们通过对生态系统食物链中发现的细菌进行分类来探索海洋,这可能使我们有可能探索海洋的更多部分。

Yannis Papanastasopoulos 在 Unsplash 拍摄的照片

该程序允许通过神经网络建模对细菌种类进行分类。该项目的代码可以在我的 GitHub 个人资料中找到,链接如下:

mg 343/浮游植物-探测(github.com)

挑选你的深度学习工具

原文:https://towardsdatascience.com/pick-your-deep-learning-tool-d01fcfb86845

为什么您的工具可以依赖于您组织的团队结构

塞萨尔·卡利瓦里诺·阿拉贡在 Unsplash 上拍照

免责声明:本文的观点是我自己的,并不一定反映我的组织或使用或开发所述代码库的其他人的观点。

介绍

机器学习模型的生命周期漫长、复杂且容易出错。ML 和数据科学家需要使用工具来轻松准备数据、定义模型、执行他们想要的训练循环并为模型服务。与此同时,一切都应该是可复制的,以便在它们的生命周期中追踪模型问题是可行的。

Jupiter 笔记本是数据和 ml 从业者学习的第一批工具之一,用于管理整个生命周期,同时保留支持理解的周围文本。因为它们提供了快速的反馈回路,所以它们是一个很好的学习工具。然而,当从学习/原型环境转移到将机器学习模型部署到生产的需求时,它们变得越来越不有用。

当谈到生产 ML 模型时,控制数据和模型的版本非常重要,以便知道我们运行的模型来自哪里,以及控制实验运行以防止在相同的实验上浪费更多的计算。此外,有用的 ML 模型是在大量数据上训练的,需要以一致和自动化的方式在集群或云上训练,而 Jupiter 笔记本不是实现这一目标的最佳工具。

当科学家处于探索阶段时,可以在他们的笔记本电脑中进行原型化的训练运行,但随后需要将它们移动到支持长时间高计算资源工作的环境中。在转移培训时,需要远程复制本地执行环境,以确保一切正常,这通常是使用 docker 容器和 python 环境来完成的,因此不需要进一步的返工。

科学家需要实验,然后他们需要工具,这些工具包括他们任务的领域逻辑,并且容易扩展他们的实验,同时遵循良好的工程实践。

特定领域的 DL 工具

大多数从业者使用 ML 的方式是为他们的深度学习分支使用特定领域的工具。这种工具包括用于数据准备、训练和评估模型、在新数据上使用它们(推断)、后处理它们的结果以及在某些情况下部署它们的代码。这种方法非常普遍,因为它非常方便:

  • 在模型生命周期中执行不同的步骤只需要执行一个 CLI 命令
  • 该领域的最新研究改进了代码,所有团队成员都从中受益
  • 由于代码是特定于领域的,对于一个拥有领域知识的科学家来说,几乎没有额外的认知负担,他可以很容易地将概念映射到代码。
  • 团队的最佳实践通常被编码在工具中。想想数据清理、训练超参数和缺省值。

在我攻读机器翻译博士学位期间,这也是我最喜欢的解决方案,也是我所知道的唯一解决方案。我用的是 Nematus (当时还是基于 Theano!)、 OpenNMT-py 和 Fairseq ,以此顺序,在分叉 Fairseq 之前进行语音翻译支持。

对我来说,主要的优势是使用基于社区的工具,快速实现最新的研究成果,从而更快地产生研究成果。

然而,与任何工具类似,使用这种平台也有缺点。

首先,这种代码库的目标是正确快速地实现最新的研究成果,而不总是为未来提供稳定的基础。
当底层深度学习框架出现版本跳转,打破追溯兼容性时,问题就出现了。从 Tensorflow 切换到 Tensorflow 2 时发生了这种情况,Pytorch APIs 长时间不稳定,Theano 被放弃,迫使依赖它的项目做出艰难的决定。在这种情况下,如果维护人员不想使用废弃的软件,他们必须用更新的 API 重写(大部分)代码库,切换框架,如从 Theano 迁移到 Tensorflow 的 Nematus,甚至获得废弃软件的所有权,但这肯定是最昂贵的选择,因为它还需要数据/ML 科学家不具备的技能。

不幸的是,快速变化的框架问题的唯一解决方案是自己编写所有的深度学习操作。Marian-nmt 是我所知道的唯一一个这样做的工具,它的结果非常好,因为它可以优化代码的任何方面。缺点是它需要 C++技能,这在科学家中也不是很常见,并且可能需要更高的开发工作,而该工具是特定于一个应用领域(机器翻译)的。

第二个问题:**所有权。**小型研究小组可以选择实用的方法,使用最适合他们需求的开源工具。我在读博士期间就是这么做的,这让我可以专注于我的研究,而不是软件开发。另一方面,无法控制主要工作工具的开发会隐藏巨大的成本。

有很多这样的例子,这些项目用一个巨大的公关和没有通知打破了它们的追溯兼容性。当这种情况发生时,如果你的研究分支偏离了主分支,并且与新的设计不再兼容,你就有大麻烦了。举个例子,我的分支 FBK-Fairseq-ST (不再维护)与当前的主分支不兼容,主要是因为在此期间对 Fairseq 进行了重大重写,当前的维护者必须跟上它。

解决方案是开发你自己的独立项目,但是这会增加开发成本,而且世界上其他开发最新特性的人不会给你免费的午餐。对于小团队来说,这可能很难实现,但是减少我们自己对其他项目的依赖是值得的,特别是当它们处于活跃的开发阶段并且不稳定的时候。

特定领域工具的另一个缺点是,组织中有多个团队在为不同的任务进行深度学习。不同的团队将最终为他们的深度学习生命周期使用不同的代码库。这样的代码库是独立的,但不是完全不相关的,因为它们主要是关于训练和测试 DL 模型的。这种情况导致工作重复,一方很少或没有分享最佳做法;当一个团队的成员被要求使用另一个团队的工具时,会有很大的认知负荷。

重复的工作出现在训练循环中,最近的优化器仍然不是框架的一部分,还有 transformer 层的实现。认知负荷来自理解代码的深度学习部分,但对特定领域了解甚少。项目之间不同的代码结构和架构,以及个人对代码的期望,都放大了这种混乱。

当谈到让团队通过共享平台进行交流时,一个可能的解决方案是使用公共的机器学习引擎。

机器学习引擎

机器学习引擎的主要思想是,它支持数据加载和批处理、模型定义、训练和推理的广泛通用操作,以便它可以被从事不同任务的多个团队使用和优化。每个团队将只需要关注数据处理的代码,这取决于任务,并且不够通用,不能在引擎代码中完成。

这类软件的唯一例子是 Returnn ,这是一个考虑到序列间问题而开发的机器学习引擎。Returnn 基于 Tensorflow,但在兼容模式下使用其低级 API,不太依赖于开发趋势。

此外,通过使用低级 API,它生成包含自己的搜索算法的模型图,这些算法也在 Returnn 中定义,因此不需要额外的代码来服务它们。

其工作方式是,引擎将定义训练和验证数据、模型定义和训练超参数的配置文件作为输入。然后,团队的责任是准备他们需要的特定任务处理的数据,并维护模型。数据加载、训练和推理都由任务无关引擎管理。

模型被定义在一个由引擎解释的高级抽象中,但并不真正需要它作为一个依赖。在版本控制下,配置文件可以很容易地存储在团队拥有的存储库中,也许最重要的是,它们可以很容易地在团队之间共享,因为底层平台是完全相同的。

一个相关的优点在于可维护性。团队不需要维护他们自己的机器学习工具。这种方法可以防止一些工具被放弃,然后最终使用旧的 tensorflow 或 pytorch 版本,使它们难以更新。使用 Returnn,配置文件很难变旧,而且当它们变旧时,更新它们的工作量比更新整个平台要少得多,而所有的维护工作都是保持 Returnn 最新。

主要的问题是,由于所有的训练周期和从配置中创建的模型都需要返回,所以在不修改代码的情况下做一些高度实验性的事情会变得很困难。

此外,为了维护团队之间的公共平台,它的维护应该是团队之间的共同努力,或者委托给专门的团队。在这两种情况下,所有涉众之间都需要强有力的沟通,以保持工具的相关性和对每个人都有用。如果做不到这一点,软件就不再对每个人都有用,但当它成功完成时,它可以从团队中释放出很多能量,因为他们可以专注于解决他们的问题,而不是开发通用的深度学习代码。

最后,需要将它与 Keras 这样的高级工具进行比较。Keras 为整个深度学习生命周期提供了许多功能,也包括 Returnn 的优点。它们的设计是不同的,因为 Returnn 是一个独立的软件,而 Keras 是一个开发深度学习工具的框架。然后,Keras 将被用于构建特定任务的工具,回到我们上面提到的同样的问题,但是维护工作要少得多,因为它是由 Google 支持的开源软件。

综合

总的来说,这是一个团队组织和优先事项的问题。如果有可能在团队间共享单一工具,那么共同工作的中央引擎可以让科学家们更多地关注他们的问题。它允许轻松地共享代码和最佳实践,但它需要团队之间的高度沟通。另一方面,如果团队差异太大而不能使用共享代码,或者不可能有专门的人来开发引擎,那么特定于任务的工具将确保更大的灵活性。

我的经历

直到两年前,我只知道特定于任务的工具。我在三年内换了三次工具,试图通过寻找更快的模型训练或更容易的开发来最大化我的生产力。与旧的 Nematus 相比,OpenNMT-py 在质量上是一个巨大的飞跃,首先是因为 pytorch 是一个比 Theano 更快(就模型训练而言)的框架,还因为代码的结构更好,更灵活。Fairseq 实现了比 OpenNMT-py 更多的特性(值得注意的是,它是 Transformer 的一个工作实现,OpenNMT-py 仍然缺乏它),并且训练速度也更快。然而,我认为开发者在一年内做了两个大的突破性的改变。这真的令人沮丧。

当我获得博士学位后开始目前的工作时,我接触了 Returnn。最初,很难理解这种不同的哲学,但我开始越来越欣赏它,原因有几个:

  1. 你的模型可以作为另一个团队发现的直接结果而改进
  2. 次要任务的工具可以留在开发中,而 Returnn 中的代码不能
  3. 模型网络可以作为人工制品存储,并且完全独立于运行它的代码
  4. 我在不同任务和团队的交叉点上工作,对每种任务使用一个工具真的很好,不需要上下文切换
  5. 得到的模型仍然是 Tensorflow 模型,没有深奥的格式,仍然可以很容易地集成到不同的应用程序中。

结论

当前可应用的深度学习前景由围绕深度学习框架构建的工具统治,这些工具专门用于单个或几个任务。它们在实现框架、一些设计选择和对外部依赖的依赖上有所不同。但是,它们在主要的设计思想上都是相似的。

在这篇文章中,我想分享另一种构建深度学习工具的选择,使用 Returnn 的例子,这是我所知道的唯一一个深度学习引擎。它展示了开发深度学习工具的另一种方式,这种方式在多个团队从事不同任务但在同一代码库上协作的环境中更有意义。

中等会员

你喜欢我的文章吗?你是否正在考虑申请一个中级会员来无限制地阅读我的文章?

如果您决定通过此链接订阅,您将通过您的订阅支持我,无需为您支付额外费用https://medium.com/@mattiadigangi/membership

2021 年在 CPU 上使用 OpenCV 的快速人物检测器

原文:https://towardsdatascience.com/picking-fast-people-detector-working-with-opencv-on-cpu-in-2021-ff8d752088af

最近,我需要为一个新的宠物项目探测人。我有一段时间没有这样做了,决定看看现在什么是最好的选择。

因此,我通读了一些论文,并进行了基本测试来衡量准确性和性能。

我有要求——物体足够大,但环境可能非常不同,探测器应该尽可能快地工作。因此,我需要一个具有合理权衡的快速检测器。

此外,我在普通云上运行我的项目,大多数云单元只提供 CPU 硬件。最后,我使用 OpenCV-DNN 框架来处理神经网络。

我发现有一篇文章对探测器的发展进行了回顾。简而言之,有三个大时代:

  • 传统的计算机视觉方法——2001 年的 Viola-Jones(又名 Haar's cascade)和 2006 年的梯度方向直方图(HOG)。
  • 基于卷积神经网络的早期解决方案,其中 YOLO 和 MobileSSD 取得了最佳结果(2014–2017)
  • YOLO 及其后裔统治世界(自 2018 年起)

他们的结论是 YOLOv4 最快(还有第二种意见——yolov 5 最快)。

我发现了一个很好的关于旧时代的系列:

  • 老派方法速度快,可靠性低
  • 第一个神经网络(到 2018 年),其中最快的是 MobileSSD

然后让我们比较一下 OpenCV 中提到的方法。

不幸的是,YOLOv5 仍然不能与 OpenCV 一起工作——看起来这是一个很长的故事,并且对它们的兼容性进行了一些修复,但目前最新的 YOLOv5-ONNX 模型没有加载到当前的 OpenCV 实现中。

另一方面,OpenCV 包含 Viola-Jones 和 HOG 实现,因此它们将被添加到竞赛中。

作为一个例子,我在股票上发现了一个吸引人的图片:

https://unsplash.com/photos/PhhtSyCeN0I

环境

  • python 3 64 位
  • OpenCV python
  • MobileSSD(最新一个和 OpenCV 合作的是 SSD _ mobilenet _ v1 _ coco _ 2017 _ 11 _ 17)
  • YOLOv4(普通和微型版本)

试验

基准测试运行之后,我得到了意想不到的结果:

**yolo** found *12* persons in **1.97** seconds avg**tiny_yolo** found *8* persons in **0.22** seconds avg**ssd** found *14* persons in **0.1** seconds avg**hog** found *2* persons in **0.18** seconds avg**haar** found *0* persons in **0.07** seconds avg

YOLOv4

Yolo 展示了检测方面的最佳结果,但执行速度非常慢——每张图像 2 秒。他们宣称高端 GPU 上 100 FPS,但弱 CPU 不是他们的领域。

YOLOv4

小小的 YOLO

TinyYolo 漏掉了一些小物件却原来是很多(差不多 10 次!)更快。这一结果与其他来源一致:

YOLOv4-tiny 的 FPS(每秒帧数)大约是 YOLOv4 的八倍。然而,在 MS COCO 数据集上测试时,YOLOv4-tiny 的精度是 YOLOv4 的 2/3。

小尤洛

移动固态硬盘

相当老的 MobileSSD 检测器比 TinyYOLO 快两倍。这是意料之外的。我能有的唯一解释是,当 YOLO(即使是一个微小的版本)是为 GPU 设计的时候,MobileSSD 被优化为在 CPU 上工作。

HOG 比 MobileSSD 慢,比 TinyYolo 略快,但检测结果令人失望。

维奥拉-琼斯

快速工作,但一无所获。

结论

明显的赢家是 MobileSSD。这是一个相当出乎意料的结果。虽然所有测试的神经网络在检测方面都很好,但 MobileSSD 远远优于 YOLO 实现。

老派的方法根本不起作用。

链接

  • 2021 年探测器
  • 传统计算机视觉探测器
  • 第一个基于 CNN 的探测器
  • YOLOv4
  • OpenCV DNN
  • OpenCV 猪
  • OpenCV Viola-Jones
  • 在 Github 上测试源代码

管道和定制转换器,并附有 Python 实践案例研究

原文:https://towardsdatascience.com/pipeline-and-custom-transformer-with-a-hands-on-case-study-in-python-c416731e6158

使用定制和 scikit-learn 管道

机器学习中的管道涉及将端到端的工作流转换为一组代码,以自动化整个数据处理和模型开发过程。我们可以使用管道依次应用一系列转换来准备数据,并在最后安装一个估计器。管道有助于简化将一系列操作组合在一起的过程。仅当转换和模型及其超参数被预先识别时,才使用管道。

图一。说明了转换器和估算器如何为管道工作的流程图。作者使用 PowerPoint 制作的图片

管道是如何工作的?

管道由一系列拟合和转换操作组成,这些操作因训练数据和测试数据而异。通常,在训练集上使用拟合和变换或者拟合函数;在测试集上只使用变换。拟合和转换操作首先识别关于数据分布的重要信息,然后根据这些信息转换数据。fit()通过研究数据分布来获取相应属性的中值,而 transform()则用从数据中获取的中值替换缺失值。举例来说,我们使用一个 SimpleImputer(),一个用于缺失值处理的 python 包,用中间值替换缺失值。

另一方面,测试数据只使用转换操作。这主要是为了避免在构建模型时出现任何数据泄漏。其思想是基于从训练集中学习到的信息对测试数据执行预处理,而不是将整个数据作为一个整体进行清理。传入数据流中任何缺失的信息都需要在输入预测模型之前进行处理。这一点很重要,尤其是当您将模型投入生产时。

工作示例

我们的想法是建立一个客户流失模型,使我们能够预测对预测客户流失有影响的驱动因素。我们不会专注于数据探索或建立一个高精度的模型;相反,我们将检查数据组件,并确定如何使用管道开发整个过程。用于此分析的数据集取自 Kaggle ,并对任何人开放(公开)使用(Kaggle,2018)。数据字典和业务上下文可以在上面的链接中找到。

# To help with reading and manipulating data
import pandas as pd
import numpy as np# To help with data visualization
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns# To be used for missing value imputation
from sklearn.impute import SimpleImputer# To help with model building
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import (
 AdaBoostClassifier,
 GradientBoostingClassifier,
 RandomForestClassifier,
 BaggingClassifier,
)
from xgboost import XGBClassifier# To get different metric scores, and split data
from sklearn import metrics
from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score
from sklearn.metrics import (
 f1_score,
 accuracy_score,
 recall_score,
 precision_score,
 confusion_matrix,
 roc_auc_score,
 plot_confusion_matrix,
)# To be used for data scaling and one hot encoding
# from sklearn.preprocessing import StandardScaler, MinMaxScaler, OneHotEncoder# To be used for tuning the model
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV# To be used for creating pipelines and personalizing them
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer# To define maximum number of columns to be displayed in a dataframe
pd.set_option(“display.max_columns”, None)# To supress warnings
import warningswarnings.filterwarnings(“ignore”)

我们来读数据。

data=pd.read_csv(“WA_Fn-UseC_-Telco-Customer-Churn.csv”)
data.head()

图二。展示了数据的前五行。快照取自 Jupyter 笔记本。

data[‘MultipleLines’].value_counts()

我们期望属性“MultipleLines”是二进制的;然而,这里有三个不同的值。

  1. 是~3.5K 记录
  2. 不~ 3K 唱片
  3. 没有电话服务~0.5K 记录

当使用 Jupyter Notebook 时,我们总是可以将“无电话服务”转换为“无”,然而,当建立管道时,这些转换需要在管道本身内发生。因此,我们需要定义一个自定义转换器,可以将“无电话服务”转换为“无”。下面是一个自定义转换器的示例。

#Custom Transformer that extracts columns passed as argument to its constructorclass NPSTF():

    #Class Constructor 

    def __init__( self, feature_names):
        self._feature_names = feature_names 

    #Return self nothing else to do here    

    def fit( self, X, y = None ):
        return self

    #Method that describes what we need this transformer to do

    def transform(self, X, y = None ):

        def map_values(val):

            if val in ["No phone service"]:return 'No'
            else:
                return val

        X_=X.copy()

        X_[self._feature_names] = X_[self._feature_names].apply(map_values)

        return  X_

让我们试着验证一下定制转换器是否能处理虚拟数据。

data1=[(0, “No”),(0, “Yes”), (1, “No”), (1,”No phone service”)]df = pd.DataFrame(data1, columns=[‘Label’, ‘MultipleLines’])df.head()

pipeline=Pipeline(steps=[(“CT”, NPSTF(“MultipleLines”))])pipeline.fit_transform(df)

现在,我们将开发一个端到端的流程来使用管道。请注意,我们对数据做了以下假设。

  1. 因为我们正在构建非参数模型,所以我们不会检查数据分布。
  2. 所有分类列都需要进行一次性编码,数字列中缺失的条目需要进行估算。
  3. 我们将实现一个 XGBoost()模型和管道。理想情况下,我们需要找出最佳模型,然后才能沿着管道实现它。
cat_columns=list(data.select_dtypes(include=["object"]).columns)
print(cat_columns)numeric_columns=list(data.select_dtypes(include=["int64", "float64"]).columns)
print(numeric_columns)

建立管道。

from imblearn.pipeline import Pipelinefrom sklearn.preprocessing import OneHotEncoder# To oversample and undersample data
from imblearn.under_sampling import NearMiss
from imblearn.under_sampling import RandomUnderSampler# creating a list of numerical variables
numerical_features = numeric_columns# creating a transformer for numerical variables, which will apply simple imputer on the numerical variables
numeric_transformer = Pipeline(steps=[("imputer", SimpleImputer(strategy="median"))])# creating a list of categorical variables
categorical_features = cat_columns.remove('Churn')# creating a transformer for categorical variables, which will first apply simple imputer and 
#then do one hot encoding for categorical variables
categorical_transformer = Pipeline(
    steps=[
        ("imputer", SimpleImputer(strategy="most_frequent")),
        ("onehot", OneHotEncoder(handle_unknown="ignore")),
    ]
)# handle_unknown = "ignore", allows model to handle any unknown category in the test data# combining categorical transformer and numerical transformer using a column transformerpreprocessor = ColumnTransformer(
    transformers=[
        ("num", numeric_transformer, numerical_features),
        ("cat", categorical_transformer, categorical_features),
    ],
    remainder="passthrough",
)# remainder = "passthrough" has been used, it will allow variables that are present in original data 
# but not in "numerical_columns" and "categorical_columns" to pass through the column transformer without any changesmodel = Pipeline(
    steps=[
        ("CT", NPSTF("MultipleLines")),
        ("pre", preprocessor),
        ("class balance", NearMiss(version=1)),
        (
            "XGB",
            XGBClassifier(random_state=1,subsample= 0.9, reg_lambda= 5, n_estimators= 50, \
                          learning_rate= 0.1, gamma= 1, eval_metric='logloss'),
        )
    ]
)

我们正在实施数据管道。

# Separating target variable and other variables## Encoding Existing and Attrited customers to 0 and 1 respectively, for analysis.
data["Churn"].replace("No", 0, inplace=True)
data["Churn"].replace("Yes", 1, inplace=True)X = data.drop(columns="Churn")
Y = data["Churn"]# Splitting the data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(
    X, Y, test_size=0.20, random_state=1, stratify=Y
)
print(X_train.shape, X_test.shape)# Fit the model on training data
model.fit(X_train, y_train)

一旦我们在训练数据上拟合了管道,我们就可以在验证数据上测试它。这个想法是,一旦数据清理过程得到处理,并且确定了最适合的模型,就实现管道。管道应该处理原始数据本身;因此,上面的示例是在考虑部署场景的情况下构建的。

参考

  1. Github。(2021 年 11 月 29 日)。使用 Cloud Pak 上的 Watson 机器学习和 Jupyter 笔记本预测客户流失数据。检索于 2022 年 1 月 14 日,来自 GitHub 网站:https://GitHub . com/IBM/Telco-Customer-churn-on-ICP 4d/blob/master/data/Telco-Customer-churn . CSV
  2. 卡格尔。(2018).电信客户流失。从 kaggle.com 网站检索到:【https://www.kaggle.com/blastchar/telco-customer-churn

关于作者:高级分析专家和管理顾问,帮助公司通过商业、技术和组织数据的数学组合找到各种问题的解决方案。一个数据科学爱好者,在这里分享、学习、贡献;可以和我在 上联系 推特

推销你的数据策略:为管理层和用户翻译技术对话

原文:https://towardsdatascience.com/pitching-your-data-strategy-translating-tech-talk-for-management-and-users-bbd044872ee4

三维简单性:数据、技术和人员…以及一个在整个过程中提供帮助的工具

图片作者:Alexandre Allouin

在之前的文章中,我倡导了一种与高管或非技术人员沟通的简单方式,通过阐述任何数据计划——从将您的组织转变为数据驱动的实体到开发新的数据项目——仅用三个维度:数据、技术和人员(“ 3D 数据故事”),因为归根结底,这就是它的全部!

“生活真的很简单,但我们坚持要把它变得复杂。”—孔子

在最近一个关于这三个维度的专业社交媒体的讨论中,一些人建议增加更多内容,以更好地反映业务需求。我们所有的数据人员都喜欢设计复杂的模型,开发精细的策略或复杂的架构,但我们有失去受众的风险。三维的主要思想是保持我们的信息简单;管理层和最终用户都容易记住的信息:数据、技术和人员。轻松点。这种简单性是大多数员工/用户能够拥有它来培育组织文化的关键。许多文章和研究表明,我们的短期记忆实际上只能存储三到五个项目。许多故事或口号都是基于这个三法则。有趣的是,这个方法非常有效,以至于亚历山大·仲马把他的书命名为三个火枪手,而他们实际上只有四个(好吧,这最后一个例子可以被看作是“确认偏差”)😉).

根据上一篇文章,我想通过提出一个 3D 数据成熟度评估工具来使这篇文章更加具体。该工具允许您确定您的组织的当前状况,以及要达到下一个级别需要做哪些工作。该评估仅基于 18 个问题。如果我看到有人对这个工具感兴趣——我将使用中间页面来跟踪它的活动——我将通过开发更多的问题并添加一组建议来使它更强大,以达到特定于您当前情况的下一个级别——一种简单的路线图。

简化您与管理层的沟通方式很重要,但它显然需要与您组织的战略目标保持一致。如果是数据项目,这正是数据策略或业务案例的目的。我不会在这里详述,因为它已经在介质上有很好的记录。相反,我将把重点放在电梯推销上(但不是那种能在 30 秒内到达 500 米的现代电梯!换句话说,你的项目还没有被批准,你想提出你的理由。

3D 数据成熟度

无论是为了您的数据策略还是为了您的业务案例,您都必须以您想要达到的数据成熟度级别为目标。理想情况下,为了保持简单的交流,您希望从数据、技术和人员方面定义您的成熟度期望。你可以将你的 3D 维恩图与你对你的组织现在所处位置的评估结合起来,然后找出实现目标所缺少的东西。让我们从数据成熟度级别开始。有很多款,最好的是你最舒服的那款。我发现戴尔数据成熟度模型简单而实用,所以我将在本文的其余部分以及工具中使用它。

戴尔数据成熟度模型|作者的图片灵感来自首席信息官

模型的每一层都建立在前一层的基础上。你不可能一夜之间成为数据驱动型企业;这是一个需要投入和精力的迭代过程!下面是每个级别含义的高级概述。

1.数据感知

数据

  • 由个人或团队管理的孤立数据
  • 来自多个系统的多个数据源
  • 没有单一的真理来源

技术

  • 多个 BI 系统(通常基于 MS Excel)
  • 手动编制的非标准化报告
  • 没有一致和结构化的数据存储方法
  • 没有应用程序/系统集成

  • 不信任数据质量和他们自己的报告
  • 临时报告
  • 在筒仓中工作

2.精通数据

数据

  • 数据孤立,但数据库设计/标准化部分实施
  • 使用/操作非结构化数据的挑战

技术

  • 数据仓库存在,但不完整
  • 组织级别可用的 BI 系统
  • 可接受的报告标准化水平

  • 员工质疑数据质量
  • 跟踪第一个组织 KPI
  • 准备试点数据计划
  • 缺乏管理层的支持
  • 关键人员/角色了解数据对决策的价值

3.数据悟性

数据

  • 打破组织和数据孤岛
  • 建立数据战略
  • 建立了单一的真相来源
  • 作为产品的数据:统计模型/预测分析

科技

  • 关注新技术和数据集成
  • 按需数据
  • 开发数据仓库/数据湖(非结构化数据)

  • 用于为关键计划做出关键业务决策的数据
  • 管理层的支持已经到位
  • 支持和实施战略的数据团队
  • 合作伙伴 IT 和业务部门
  • 组织文化/对数据重要性的理解
  • 提供员工培训

4.数据驱动

数据

  • 扩展数据战略,同时降低成本
  • 所有数据源的集成

技术

  • 高级分析平台(从描述性和预测性分析转向规定性分析—机器学习)
  • 不同功能角色的面向用例的架构

  • 没有数据意味着没有决策
  • IT 和业务团队作为一个整体运作
  • 嵌入业务流程的分析
  • 数据技能作为员工课程/评估的一部分:数据素养证书

数据扫盲证书(CDL)是对模型的补充,因为人的因素往往被忽略。因此,在一个完全由数据驱动的组织中,我认为数据素养应该是每个员工课程的一部分。因此,我鼓励组织实施强制性 CDL,这将大大有助于发展企业文化。

正如本文前面提到的,为了帮助您评估您组织的成熟度水平,这里有一个简单的 Google 电子表格工具您可以直接使用——但是如果您复制它会更好,因为它不支持多重访问——或者修改它并使其适应您的需求。这里的目的不是重新发明轮子,而是按照 3D 方法构建数据成熟度模型,以支持您与高管或其他利益相关者的故事。

3D 数据成熟度评估工具|图片由 Alexandre Allouin 提供

确定你的成熟度等级需要一个诚实的自我评估,以对你当前的状态产生一个现实的看法。这将告诉您为了达到下一个数据成熟度级别,或者您的数据项目的目标,需要实现的步骤。人的因素,即人的方面,往往落后了。改变文化比技术转变需要更多的精力。

“购买和使用分析工具并不难,改变行为才难。”*

*** 德勤洞察|分析和人工智能驱动的企业在时代中茁壮成长(2019)

一个好的方法是同时进行员工/用户调查。一方面,它会让你对他们的看法有所了解,并可以用来调整人的维度。另一方面,它将作为您最终评估项目的参考,从而衡量其影响。

在这个阶段,你将能够建立一个计划草案,最重要的是,它的相关费用。现在最棘手的部分是估计组织将从实施项目中获得的利益。它们可以采取不同的形式:降低成本、提高绩效、优化流程、提高敏捷性或创新等。如果你不能清楚地确立收益并量化它,你就得质疑自己。即使项目从数据的角度来看是有意义的,如果业务价值不能被估计,并且以后不能使用双方同意的度量标准来度量,那么您的项目就有风险。

完成你的故事

现在,你可以用一些更具体和可测量的元素来完成你的初始故事。

  1. 首先,你需要专注于远景——大图——这将激励决策者:为什么你的提议是相关的?它如何与战略目标保持一致?
  2. 然后,你必须将这个愿景转化为更具体的东西,一个他们可以快速掌握的高级计划:3D 维恩图。概括地说,我们应该把精力放在哪里才能使这个项目成功?这应该会引发关于提案成本的讨论。
  3. 最后,随着困难的部分被覆盖,你可以看到快乐的结局:增加到项目中的价值。会有什么好处及其投资回报?

你的故事几乎可以放入三张幻灯片,最后一张可以总结如下:

图片作者:Alexandre Allouin

参考文献和致谢

  • 孔子;-)
  • 戴尔数据成熟度模型
  • Hesa 工具包
  • 富士通数据成熟度自我评估
  • 感谢维杰·亚达夫为起了个名字(3D……)
  • 感谢莎伦一直以来的支持

BigQuery 中的 Pivot 和 Unpivot 函数用于更好的数据操作

原文:https://towardsdatascience.com/pivot-and-unpivot-functions-in-bigquery-for-better-data-manipulation-f0230295bd5e

马龙·玛雅在 Unsplash 上的照片

详细的教程

Pivot 是 BigQuery 中一个非常简单的函数,当您需要将行旋转成列时,它会非常有用。它对行使用聚合函数,并将行中的类别转换为列。有一个 unpivot 函数做完全相反的操作。如果你是 Google 云平台的大查询用户,还没有用过 Pivot 和 Unpivot 函数,那么很值得学习。

本文将重点用例子解释 Pivot 和 Unpivot 函数。为此,我们将创建一个虚拟表,如下所示:

WITH Produce AS (SELECT 'Kale' as product, 51 as sales, 'Q1' as quarter, 2020 as year UNION ALLSELECT 'Kale', 23, 'Q2', 2020 UNION ALLSELECT 'Kale', 45, 'Q3', 2020 UNION ALLWITH sale AS (Select 'Laptop' as Item, 'Miami' as City, 10 as No_of_Items, 'January' as Month UNION ALLSELECT 'Mobile', 'Houston', 25, 'March' UNION ALLSELECT 'Laptop', 'Miami', 8, 'March' UNION ALLSELECT 'TV', 'Austin', 7, 'February' UNION ALLSELECT 'Mobile', 'Austin', 18, 'January' UNION ALLSELECT 'Mobile', 'Miami', 22, 'June' UNION ALLSELECT 'TV', 'Houston', 9, 'May' UNION ALLSELECT 'Laptop', 'Austin', 11, 'April' UNION ALLSELECT 'Mobile', 'Miami', 15, 'May')SELECT * from sale;

请随意将其保存为视图,以便在下面的示例中使用该表。否则,您需要在我们稍后使用的每个查询中使用这个“WITH”子句。

在这里,我们将项目作为行。每行代表关于笔记本电脑、手机或电视的信息。如果您希望这些项目作为列,pivot 函数会有所帮助。

在第一个练习中,我们将使用“SUM”汇总每个项目的项目数,并将项目作为列:

SELECT * FROM sale pivot(sum(No_of_Items) for Item in ('Laptop', 'Mobile', 'TV'));

在这里,我们的透视表列是 No_of_Items 和 Items。pivot 函数中没有提到另外两列(城市和月份)。因此,默认情况下,透视函数使用城市和月份列进行分组。

还可以使用另一个 select 语句来指定要透视的列和要分组的列。

select * from (select Item, No_of_Items, Month from sale)pivot(sum(No_of_Items) for Item in ('Laptop', 'Mobile', 'TV'));

在这个查询中,我们使用 select 语句选择三列:Item、No_of_Items 和 Month。No_of_Items 和 Items 用作 pivot 函数中的 pivot 列,而 Month 列仅用于分组,我们使用 sum 作为聚合函数。

我们在项目栏中有三个项目:笔记本电脑、手机和电视。在前面的所有查询中,这三项都被转换为列。如有必要,您也可以排除某个项目。例如,您可以使用:

select * from (select Item, No_of_Items, Month from sale)pivot(sum(No_of_Items) for Item in ('Laptop', 'Mobile'));

pivot 函数中也可以使用多个聚合函数:

select * from (select No_of_Items, Item, City from sale)pivot(sum(No_of_Items) Total_num, AVG(No_of_Items) Avg_numfor Item in ('Laptop', 'Mobile'))

这里我们使用“sum”和“average”作为聚合函数。注意我们在这里使用了一个别名。当您想要使用多个聚合函数时,pivot 函数会要求别名。

让我们拆开

在大型查询环境中,还有一个 unpivot 函数,它的作用正好相反。现在让我们以另一种方式制作数据集:

WITH sale AS (Select 'January' as Month, 31 as Laptop, 42 as TV, 75 as Mobile UNION ALLselect 'February', 35, 34, 61 UNION ALLselect 'March', 23, 23, 66 UNION ALLselect 'April', 29, 25, 55)select * from sale;

这次我们使用笔记本电脑、电视和手机的数量作为列。让我们把它们排成行:

select * from saleunpivot(Sales_No for Items in (Laptop, TV, Mobile))

注意,它是如何重新排列的。你可以更进一步。我将用另外一张表重新创建该表:

WITH sale AS (Select 'January' as Month, 31 as Laptop, 42 as TV,75 as Mobile, 58 as Tablet UNION ALLselect 'February', 35, 34, 61, 73 UNION ALLselect 'March', 23, 23, 66, 63 UNION ALLselect 'April', 29, 25, 55, 45)select * from saleunpivot((Category1, Category2)for Seriesin ((Laptop, TV) as 'S1', (Tablet, Mobile) as 'S2'))

现在我们有四样东西:笔记本电脑、手机、电视和平板电脑。我想把它们分成两类。类别 1 和类别 2 成为两列。同时,我们将它们分为两个系列,并以行的形式显示。我不得不再添加一项,因为当我们将它们分为两类时,我们需要每个类别中相同数量的列。

结论

不使用 Pivot 和 Unpivot 函数也可以达到同样的效果。但那将是一段漫长的路。这两个函数可以在必要时使您的查询更简短、更优雅。

更多阅读:

https://pub.towardsai.net/an-overview-of-the-major-sql-query-clauses-and-most-commonly-used-functions-60720e2a20d7 https://pub.towardsai.net/data-analysis-91a38207c92b

数据透视表概念

原文:https://towardsdatascience.com/pivot-table-concepts-report-analyze-story-tell-e3c2e2d36036

报道、分析、讲述故事

阿德里安·特林考斯在 Unsplash 上拍摄的照片

介绍

给定数据集,数据透视表是分析、回答问题和讲述故事的强大工具。透视并不局限于 Excel 中的分析,而是一种转换数据的通用技术,通常转换成更易理解的格式。

章节参考

  1. 为什么是 pivot?
  2. 宽数据与长数据
  3. 例如:Google Sheets、Python 和 PostgresSQL
  4. 聚合函数
  5. 缺失数据
  6. 外卖食品

为什么是 Pivot?

如果我们正在制造某样东西(任何东西),每次其中一件东西从装配线上下来时,我们都会记录关于这件东西的一些基本信息:

原始“数据集”

我们可以用数据透视表来回答这个问题

  • 我们每样东西做了多少种?
  • 每种东西的平均长度是多少?
  • 每种事物的最快和最慢速度是多少?

使用数据透视表总结每种类型的属性(下面包含代码示例)

最重要的是,我们可以根据目标受众的偏好,以多种形式给出这些问题的答案。我们可以做一个小小的调整,每次测量显示一行,并在输入数据集中为每种类型创建一列,而不是每种类型一行。

完全相同的信息以不同的方式呈现。只需要改变一个关键词。

宽数据与长数据

旋转是将“长数据”转换为“宽数据”的行为。宽数据格式和长数据格式有不同的用途。首先考虑如何收集数据通常会有所帮助。

在单个二维表中表示潜在的多维数据可能需要一些折衷。有些数据会重复(长数据格式),或者数据集可能需要空白单元格(宽数据)。

我们经常需要将长的数据(一个观察列表)转化为信息(一个摘要,通常是广泛的数据)以供人们解读。这就是旋转数据的用处。

长数据。观察多年来不同省份的月汽车销量。每个年、月、地域、车辆类型、燃料类型组一个。这在软件中解析起来很简单,但对于人工比较来说并不理想。该表用作本文示例的“原始”数据(来源https://www . stat can . GC . ca

我们可以获取上面的长数据,然后将它转换成一种更容易被人使用的宽格式。

现在我们来看看如何用 3 种不同的方式来做到这一点:Python(带熊猫)、Google Sheets 和 PostgreSQL

带熊猫的蟒蛇皮旋转

从加载到df_long_data的原始数据集开始

请注意这里的关键论点。

  1. Index 决定了结果最左边一列的唯一性。
  2. 为输入表的“车辆类型”和“年份”列中的每个唯一值创建一列。
  3. 定义在输出表的单元格中放置什么。
  4. aggfunc 定义如何组合值(通常为 sum,但也使用其他聚合函数:min、max、mean、95%百分点、mode)。您还可以定义自己的聚合函数并传入该函数。

上面 Python 代码的输出。

在谷歌工作表中旋转

Google sheets 中的旋转和我们上面的 python 代码做着完全一样的事情,但是是以图形化的方式。我们没有为pivot_table函数指定参数,而是从下拉列表中选择。但是要记住的重要一点是,pivoting 并不“属于”任何特定的软件,它是一种处理数据的通用方法。

在谷歌工作表中旋转。请注意列的顺序是多么重要。在 Python 示例中,“车辆类型”在“年份”之前,这改变了分组。

PostgreSQL 中的分组

许多关系数据库没有内置的 pivot 函数。我们可以编写一个接近预期结果的查询,但是它需要一些人工干预来定义可能的组。

聚合函数

Sum 通常用于合并数据透视表中的数据,但数据透视表比简单的求和灵活得多。不同的工具会提供自己选择的**聚合函数。**例如,熊猫提供:min, max, first, last, unique, std (standard deviation), var (variance), count, unique, quantile等等。我们还可以定义自己的聚合函数,并将多个不同的聚合函数传递给同一透视表中的同一列或不同列。

例如,这个调用将*minmax聚合函数都映射到我们的数据,提供两组列来显示每年给定月份的最小和最大汽车销售量。*

对数据透视表中的同一列使用多个聚合

输出

缺失数据

与任何聚合一样,必须处理缺失数据。在这里使用的示例数据中,有许多月份都有0销售额。最可能的解释是没有收集那几个月的数据,而不是实际上没有销售。还有一种情况是数据完全丢失,“单元格”为空或者包含一个null值。无论什么程序,你都需要接受(并意识到)这些情况下的默认行为,或者指定要做什么。

(5, 7) = 6的平均值,而是(5, 0, 0, 7) = 3的平均值和(5,null, 7) = ?的平均值。缺失的数据有时会以微妙的方式影响你的结果。

外卖食品

数据透视表是一个强大的概念,可以帮助汇总、探索和组织原始数据到信息中。它们可以用来弄清楚数据集中发生了什么,或者浓缩信息与他人分享。

  • 当你需要用数据做一份报告或回答一个问题时,从考虑现有数据的形状开始。
  • 你的变量是列名还是行名?
  • 你是按观察*(大致,长)一排还是按主题(大致,宽)一排?*
  • 您的主题*(索引)是否在不止一行中重复。*
  • 汇总您的数据(最小值、平均值、最大值、分位数、计数、唯一值)能否回答您的问题?
  • 你能添加一个变量作为列,然后让你旋转来回答你的问题吗?
  • 长而宽的数据不是绝对的。
  • 您可以用同一索引一次聚合多个值。或者具有多个聚合函数的相同值。

像素级密集对比学习

原文:https://towardsdatascience.com/pixel-level-dense-contrastive-learning-6558691eeb67

基于主动采样策略的密集对比学习

汤姆·温克尔斯在 Unsplash 上的照片

对比学习是一种无标签的自我监督学习过程。由于它能够经济有效地提高模型性能,近年来被越来越多的深度学习项目用作预训练过程,形成了无监督预训练和有监督微调的模型训练范式。

然而,几乎所有应用良好的 CL 算法都是为全局特征设计的,这可以提高依赖于全局特征的任务(例如图像分类)的模型性能。而对于其他需要局部特征的任务,比如语义分割和对象检测,这些算法就不那么有效。

对于那些对 CL 算法基础感兴趣的人,我介绍我以前的博客理解对比学习和 MoCo 。在本文中,我将简要介绍全局级 CL 算法的局限性,并讨论区域级 CL 算法作为解决方案。

全球对比学习(GCL)

无监督的 CL 算法是自学的学习者,其中正对被拉在一起,负对被推开。例如,输入图像及其增强版本形成正对,而任意两个不同的(增强)图像形成负对。结果,相似图像的特征向量在潜在空间中形成一个簇,这导致了无监督的聚类效果。

GCL 的极限

上面讨论的 CL 算法使用全局汇集的特征进行学习。首先将特征向量归一化到单位超球面上,然后将正对的特征向量拉在一起,将负对的特征向量推开。由于局部细节被排除在外,训练好的网络很难进行微调,以实现高密度任务的高精度。

为了实现高密度任务的高性能,我们最好保留本地特性。然而,包含局部细节的特征图对于对比学习来说太大了。回想一下,在 GCL,使用空间大小为 1×1 的全局特征向量来计算对比损失,如下所示,其中 sim 是两个向量的内积。

对比损失

**注意,如果我们使用空间大小为 HxW 的特征图来计算损失,复杂度得到二次改善。**此外,负对的数量可能非常多,在 SimCLR 中大约为 4000 ~ 8000 个。因此,用大的空间特征图计算对比损失是不可行的。

区域对比学习(RCL)

由于上述问题,保留空间特征而不将它们全部用于 CL 是至关重要的。这里可以使用主动采样策略,其中对于 CL 只采样一小部分空间像素。

(记录)

在 2022 年 ICLR 奥运会上,伦敦帝国理工学院的研究人员提出了一种叫做 ReCo 的区域 CL 新方法。通过主动采样策略,学习过程需要更少的内存并且变得高效。与传统的 CL 不同,由于正负对是在像素级构建的, ReCo 是监督的 CL,其中每个图像像素的标签都是必需的

像素级对比损失

根据上述损失函数,在学习到的潜在空间中,对于每个查询像素向量,正键是其语义标签与查询相同的所有像素向量的平均值。另一方面,负关键字是具有来自查询的不同标签的像素向量。总损失是从小批量中的所有类采样的所有查询像素的总和。

主动采样策略

虽然计算高分辨率图像的所有像素的损失是昂贵的,但作者以主动学习的方式引入了硬负挖掘,以将学习资源集中在稀疏的像素集合上。

为了选择一个稀疏的像素集,使训练过程有效和高效,我们应该考虑主动采样的两个部分:关键采样和查询采样。

活动键采样

主动关键采样是对某个查询像素的关键像素进行采样。回想一下,正关键字是其标签与查询像素相同的所有像素向量的平均值,正关键字是固定的。因此,只对负键进行采样。

负键包括硬样本和易样本。对于一个查询像素,如果它与某一类的负关键字之间的距离已经足够远,那么该类的负关键字就是简单样本,不需要再进行采样。另一方面,如果距离很近,将它们区分开来将是模糊的,并且该类的负键是硬样本,并且需要被采样以学习更准确的判定边界。

4 个类的成对类关系图

因此,在关键采样之前,知道每对不同类别向量之间的距离是很重要的。作者使用每一个小批量更新的成对类关系图。查询类别和不同类别之间的关系是这两个类别的平均潜在向量之间的 softmax 归一化内积。对于如上所示的 4 个类的例子,在查询类为 0 且负关键类为 1 的以水颜色标记的位置,其关系值在右侧计算。因为关系值是 softmax 归一化的,所以该值只是显示两个类别的学习的潜在向量的相似性的概率。高概率意味着相似性很高,应该从该类中采样更多的负对,而低概率意味着相似性已经很低,来自该类的少量负样本就足够了。结果,降低了概率值以使不同类别的潜在向量可区分。

因为我们只考虑不同类之间的关系,所以不使用上述 4x4 表的对角线,并且用 x 标记。其他元素可以类似于上面讨论的 water color 元素来计算。

主动查询采样

由于语义分割任务中的类别不平衡,采用所有像素进行查询会使普通类别(包括背景类别)过拟合。此外,没有必要这样做,因为大多数像素容易被很好地分类。关注一小部分硬像素对于模型性能至关重要。

根据置信度图的简单和困难查询

如上所示,作者建议使用分类置信度图和阈值来过滤硬查询。我们可以注意到,硬查询位于不同语义区域的边界,而简单查询位于每个区域的内部。在训练过程中,我们可以动态地增加阈值来关注硬查询。

一种半监督学习算法

由于语义分割数据集的标记非常耗时,作者还提出了一种半监督学习算法来利用标记和未标记的图像。**这很有帮助,如果你有自己的未标记数据集,但仍想尝试这种算法,你可以与另一个具有相似语义标签的已标记数据集相结合。**半监督学习算法基于著名的均值教师框架,其中使用了师生模型。

半监督学习框架

如上图从左到右,学习框架包含三个部分:针对标注数据的监督语义切分学习;无标记数据的无监督语义分割学习:和像素级对比学习识别。相应地,损失函数也包含三个部分。

最终损失函数

由于无监督部分没有地面真实标签,生成的高置信度伪标签用于 ReCo 部分,使得 ReCo 学习可靠。

RCL 的重要性

通过 RCL,我们可以对模型进行密集对比学习,这有利于下游的密集任务。ReCo 在这里只是语义分割的一个例子。在不久的将来,更具体的密集任务如对象检测、关键点检测等。会在对比学习文献中被考虑,这对一般的人工智能发展是重要的。

参考

均值教师是更好的榜样:加权平均一致性目标提高半监督深度学习结果,2018

视觉表征对比学习的简单框架,2020

理解对比学习和 MoCo,2021

带区域对比的自举语义分割,2022

https://dushuchen.medium.com/membership

人工智能(游戏)模型—测验你自己

原文:https://towardsdatascience.com/pl-ai-play-models-quiz-yourself-9ca35acd5192

摆弄分子和 DNA 的人工智能模型

去飞溅

这一次,我想我们可以从人工智能分子和 DNA 模型中获得一些乐趣。我为你做了一个简短的自我评估测验来测试你的理解能力。下面是四幅图,对应四个问题,描述了人工智能架构中的生物信息学应用。每个插图都有一些编号为 1 到 10 的缺失组件。你能为每个插图填上缺少的部分吗?答案和解释如下。

问题:

**问题 1/4:多模态神经网络。**该架构预测药物反应。给定一个分子结构(药物)和基因表达(DNA 序列),什么类型的编码器(编号 1,2)将代表这些输入结构?

问题 1:哪种编码器产生药物和细胞系的嵌入,然后输入预测器以估计药物反应?

**问题 2/4:级联模型。**给定一个药物分子,将哪个编码器(编号为 3、4)级联输入预测器以对药物分子进行分类?

问题 2

问题 3/4: 给定一个穿着溜冰鞋、戴着帽子和眼镜的站立的 DNA 双螺旋(“输入字符”)。哪个模型(编号 7)重建了精确的“输入字符”?检测到哪个表示(编号 5),编码器提取的数据是什么(编号 6)?

问题 3

问题 4/4: 这个网络识别出一个假分子(戴着帽子),返回一个毒分子。该模型中包含哪些子模型(编号为 8a、8b)?输出返回哪些数据(编号为 9)?哪个型号的编号是 10?

问题 4

答案:

请注意,问题 1-4 的答案是多样的,而问题 5-10 是非常标准的。

解释:

问题 1:多维度

通过连接两个模态:基因表达和一个分子(下图)。 (2) RNN 代表一个分子 (1) CNN 一个基因图谱。通过连接这些模型的输出,生成了包含顺序数据和卷积数据的组合表示。

问题二: 级联模型

CNN (3) 的输出是 RNN (4) 的输入。这个级联模型就像一个描述符,它在全局(全局特征发现的 RNN 影响)和局部(局部特征选择的 CNN 影响)定义药物。这里,我们假设局部特征代表原子和官能团的类型,而全局特征是原子排列。

问题 3: 变分自动编码器 VAE (7,8a)

自动编码器(AE)的主要目标是构建压缩表示的低维潜在空间(5) ,使得每个输入可以被重构为原始输入。将高维的原始输入数据映射到低维表示的模块称为编码器,而实现映射并从低维表示重构原始输入的模块称为解码器。编码器和解码器通常是具有 RNN 和 CNN 架构的神经网络。随着分子表示的计算,利用 AE 对分子生成的典型数据处理过程从将输入编码到低维潜在空间开始。在潜在空间内,来自输入的变量(6) 的轴可以被编码。

问题 4:对抗性自动编码器(AAE)

在生成化学中, GAN (8b) 使用潜在随机输入(6) ,根据分子表示的选择,生成字符串、分子图或指纹。所产生的分子与真实化合物的样品混合以供给鉴别器(10) 。计算鉴别损失是为了评估鉴别者是否能够从生成的化合物中区分真实的化合物,而计算生成损失(9) 是为了评估生成器是否能够通过生成不可区分的分子来欺骗鉴别者。这两个损失函数表明,即使是公认的鉴别者也可能被误导,将生成的分子分类为真实分子,因为生成器已经学习并积累了真实的数据模式来创建新的化合物。为了对抗鉴别器并最小化生成损失,生成器只能探索由真实化合物定义的有限化学空间。因此,由产生的分子覆盖的受限化学空间可能是一种限制。

对抗自动编码器 AAE 的架构与 变分自动编码器【8a】非常相似,除了附加的鉴别器网络(10) 。AAE 训练三个模块:一个编码器,一个解码器,和一个鉴别器(10) 。编码器学习输入数据,并根据分布将分子映射到潜在空间。解码器通过从*遵循概率解码分布(6)的潜在空间采样来重建分子。*鉴别器区分潜在空间的分布和先验分布。编码器在训练迭代期间被一致地修改,以通过遵循特定的分布来最小化鉴别器的成本。一个简单的先验,如高斯分布,是假定在 VAE,而替代先验可以存在于现实世界的做法。

注:

  1. 如果你喜欢这篇文章,并想了解更多关于药物发现的知识,我推荐你阅读我的博客你应该问基于人工智能的药物发现公司的问题。
  2. 我在博客上投入了很多心血。请给我写一封电子邮件(miritrope@gmail)并通过 LinkedIn 联系,告诉我你喜欢我的作品。
  3. 作者画的所有图。

使用变形金刚检测剽窃

原文:https://towardsdatascience.com/plagiarism-detection-using-transformers-b9e7ed5c2a1f

使用基于 transformer 的模型构建更强大的剽窃检测器的完整指南。

动机

抄袭是许多行业最大的问题之一,尤其是在学术界。随着互联网和开放信息的兴起,这种现象变得更加严重,任何人都可以通过点击某个特定主题来获取任何信息。

基于这一观察,研究人员一直试图用不同的文本分析方法来解决这个问题。在这篇概念性的文章中,我们将尝试解决剽窃检测工具的两个主要局限性:(1) 内容改写剽窃,以及(2) 内容翻译剽窃

(1)传统工具很难捕捉到重组的内容 ,因为它们没有考虑到整体上下文的同义词和反义词。

***【2】***用不同于原始语言的语言编写的内容 也是即使最先进的基于机器学习的工具面临的大问题,因为上下文被完全转移到另一种语言。

在这个概念博客中,我们将解释如何使用基于 transformer 的模型以创新的方式解决这两个挑战。首先,我们将向您介绍描述整个工作流程的分析方法,从数据收集到性能分析。然后,在展示最终结果之前,我们将深入研究该解决方案的科学/技术实现。

问题陈述

假设您对构建一个学术内容管理平台感兴趣。您可能希望只接受尚未在您的平台上共享的文章。在这种情况下,您的目标将是拒绝所有与现有文章相似的新文章。

为了说明这个场景,我们将使用cord-19 数据集,这是一个开放的研究挑战数据,由艾伦人工智能研究所在 Kaggle 上免费提供。

分析方法

在进一步分析之前,让我们从下面的问题中弄清楚我们想要达到的目的:

问题:我们能在数据库中找到一个或多个与新提交的文档相似的文档吗?

以下工作流程强调了更好地回答这个问题所需的所有主要步骤。

抄袭检测系统工作流程(图片由作者提供)

让我们理解这里发生了什么💡。

在收集了源数据之后,我们首先对内容进行预处理,然后从 BERT 创建一个向量数据库。

然后,每当我们有新的文档时,我们检查语言并执行剽窃检测。更多细节将在本文后面给出。

科学实施

这一部分集中于分析方法中每个部分的技术实现。

数据预处理

我们只对源数据的 摘要 列感兴趣,而且,为了简单起见,我们将只使用 100 个观察值来加速预处理。

source _ data _ processing . py

下面是来自源数据集的五个随机观察。

来自源数据的五个随机观察(图片由作者提供)

文档矢量器

关注 BERT 和机器翻译模型(图片由作者提供)

简介中观察到的挑战导致分别选择以下两种基于变压器的模型:

(1)BERT 模型 :解决第一个限制,因为它提供了文本信息的更好的上下文表示。为此,我们将:

  • create_vector_from_text:用于生成单个文档的矢量表示。
  • create_vector_database:负责为每个文档创建一个包含相应向量的数据库。

bert_model_vectors.py

第 94 行显示了来自向量数据库的五个随机观察值,带有新的向量列。

来自向量数据库的五篇随机文章(图片由作者提供)

(2)机器翻译 转换器模型用于将输入文档的语言翻译成英语,因为在我们的例子中,源文档是英语的。只有当文档的语言是以下五种语言之一时,才会执行翻译:德语、法语、日语、希腊语和俄语。下面是使用MarianMT模型实现这个逻辑的助手函数。

document _ translation . py

抄袭分析器

当传入文档的向量在某个阈值水平上与数据库向量之一相似时,就存在剽窃。

聚焦抄袭分析器(图片由作者提供)

但是,什么时候两个向量是相似的?
→当它们大小相同、方向相同时。

这个定义要求我们的向量具有相同的大小,这可能是一个问题,因为文档向量的维数取决于文档的长度。幸运的是,我们有多种相似性度量方法可以用来克服这个问题,其中之一是余弦相似性,它将在我们的案例中使用。

如果你对其他方法感兴趣,可以参考詹姆斯·布里格斯的这篇惊人的内容。他解释了每种方法的工作原理及其优点,并指导您实现它们。

使用run_plagiarism_analysis函数执行抄袭分析。我们首先使用check_incoming_document函数检查文档语言,以便在需要时执行正确的翻译。

最终结果是一个包含四个主要值的字典:

  • similarity_score:新文章与数据库中最相似的现有文章之间的得分。
  • is_plagiarism:无论相似度得分是否等于或超过阈值,该值都为真。否则就是假的。
  • most_similar_article:最相似文章的文本信息。
  • article_submitted:提交审批的文章。

抄袭 _ 分析. py

系统实验

我们已经覆盖并实现了工作流的所有组件。现在,是时候使用我们系统接受的三种语言来测试我们的系统了:德语、法语、日语、希腊语和俄语

候选文章及其提交评估

这些是我们要检查作者是否抄袭的文章的摘要文本。

英文文章

这篇文章实际上是一个来自源数据的例子。

**english_article_to_check** = "The need for multidisciplinary research to address today's complex health and environmental challenges has never been greater. The One Health (OH) approach to research ensures that human, animal, and environmental health questions are evaluated in an integrated and holistic manner to provide a more comprehensive understanding of the problem and potential solutions than would be possible with siloed approaches. However, the OH approach is complex, and there is limited guidance available for investigators regarding the practical design and implementation of OH research. In this paper we provide a framework to guide researchers through conceptualizing and planning an OH study. We discuss key steps in designing an OH study, including conceptualization of hypotheses and study aims, identification of collaborators for a multi-disciplinary research team, study design options, data sources and collection methods, and analytical methods. We illustrate these concepts through the presentation of a case study of health impacts associated with land application of biosolids. Finally, we discuss opportunities for applying an OH approach to identify solutions to current global health issues, and the need for cross-disciplinary funding sources to foster an OH approach to research."

100 _ percent _ similarity . py

抄袭粘贴文章的抄袭检测结果(图片由作者提供)

运行该系统后,我们得到的相似性得分为 1,与现有文章 100%匹配。这是显而易见的,因为我们从数据库中提取了完全相同的文章。

法文文章

这篇文章可以从法国农业网站免费获得。

**french_article_to_check = """**Les Réseaux d’Innovation et de Transfert Agricole (RITA) ont été créés en 2011 pour mieux connecter la recherche et le développement agricole, intra et inter-DOM, avec un objectif d’accompagnement de la diversification des productions locales. Le CGAAER a été chargé d'analyser ce dispositif et de proposer des pistes d'action pour améliorer la chaine Recherche – Formation – Innovation – Développement – Transfert dans les outre-mer dans un contexte d'agriculture durable, au profit de l'accroissement de l'autonomie alimentaire."""

抄袭 _ 分析 _ 法语 _ 文章. py

法语文章抄袭检测结果(图片由作者提供)

这种情况下不存在抄袭,因为相似度得分小于阈值。

德文篇

让我们想象一下,有些人非常喜欢数据库中的第五篇文章,并决定将其翻译成德语。现在来看看系统会如何评判那篇文章。

**german_article_to_check** = """Derzeit ist eine Reihe strukturell und funktionell unterschiedlicher temperaturempfindlicher Elemente wie RNA-Thermometer bekannt, die eine Vielzahl biologischer Prozesse in Bakterien, einschließlich der Virulenz, steuern. Auf der Grundlage einer Computer- und thermodynamischen Analyse der vollständig sequenzierten Genome von 25 Salmonella enterica-Isolaten wurden ein Algorithmus und Kriterien für die Suche nach potenziellen RNA-Thermometern entwickelt. Er wird es ermöglichen, die Suche nach potentiellen Riboschaltern im Genom anderer gesellschaftlich wichtiger Krankheitserreger durchzuführen. Für S. enterica wurden neben dem bekannten 4U-RNA-Thermometer vier Hairpin-Loop-Strukturen identifiziert, die wahrscheinlich als weitere RNA-Thermometer fungieren. Sie erfüllen die notwendigen und hinreichenden Bedingungen für die Bildung von RNA-Thermometern und sind hochkonservative nichtkanonische Strukturen, da diese hochkonservativen Strukturen im Genom aller 25 Isolate von S. enterica gefunden wurden. Die Hairpins, die eine kreuzförmige Struktur in der supergewickelten pUC8-DNA bilden, wurden mit Hilfe der Rasterkraftmikroskopie sichtbar gemacht."""

抄袭 _ 分析 _ 德语 _ 文章. py

德国文章抄袭检测结果(图片由作者提供)

97%的相似度——这就是模型所捕捉到的!结果相当可观。这篇文章绝对是个败笔。

结论

恭喜你,现在你有了所有的工具来构建一个更健壮的抄袭检测系统,使用 BERT 和机器翻译模型结合余弦相似度。

如果你喜欢阅读我的故事,并希望支持我的写作,考虑成为一个媒体成员。每月支付 5 美元,你就可以无限制地阅读媒体上的故事。

欢迎在 YouTube 、 Medium 、 Twitter 上关注我,或者在 LinkedIn 上问好。讨论人工智能、人工智能、数据科学、自然语言处理和人工智能是一种乐趣!

额外资源

来自拥抱脸的 MarianMT 模型

文章的源代码

艾伦人工智能研究所

使用 NetworkX 和 OpenStreetMap 规划完美的徒步旅行

原文:https://towardsdatascience.com/planning-the-perfect-hike-with-networkx-and-openstreetmap-2fbeaded3cc6

从美国科罗拉多州斯诺马斯荒野的 Trail Rider Pass 观看(图片由作者提供)。

如何建立一个开源的徒步旅行计划应用程序和路线优化引擎

任何一个狂热的徒步旅行者都会告诉你,在旅途中获得良好体验的关键是充分的准备。我应该带雨具吗?我需要在水源之间走多长时间?什么是适合地形和季节的最佳避难所?

最重要的问题也是最简单的——我要去哪里?

导游手册提供了关于某个特定公园或地区的主要景点的有用信息,但细节可能会过时或难以适应您的喜好。像 AllTrails 这样的应用通过众包热门路线、照片和评论彻底改变了徒步旅行计划。然而,他们的许多“高级”功能,如打印地图或下载地图供离线使用的能力,需要付费订阅。此外,通过将一个复杂的步道系统提炼为几个策划好的徒步旅行,我经常感觉受到 AllTrails 搜索结果中列出的选项的限制。

因此,我开始构建自己的开源徒步规划应用和路线优化引擎,以补充专有网站的功能。在本文中,我将介绍(双关语)系统的独立组件,并展示它们如何在演示中组合在一起。只是想看看代码?点击这里查看仓库。

数据源

说到路线规划的免费数据集, OpenStreetMap 是明确的选择。事实上,即使是 AllTrails 也从 OSM 的天桥 API 中改编了他们的全球步道数据库,只过滤可步行的路段。

使用地理空间数据的一个难点是,它有很多种文件格式,每种格式都有自己的优点和缺点。在这个项目中,我使用不同的数据结构来存储、可视化和分析踪迹地图。

对于路线规划任务,基于图的表示是有意义的;步道和道路一样,由节点(交叉点)和边(路径段)组成。这使我们能够在路线优化功能中利用像 Djikstra 算法这样的图形算法。

我对这个应用程序的特定组件的策略并不新鲜,因为已经有一个广泛的生态系统,其中有专门用于基于图形的路径规划的教程和库。像 OSMnx 这样的工具提供了一个高级接口,通过地理编码(例如“美国弗吉尼亚州谢南多厄国家公园”)查询 OSM 数据集,并将响应转换成 NetworkX 图形。下面是我用来下载轨迹数据并将几何信息保存为 graph(为了高效计算)和 GeoJSON(可视化)格式的load_map 函数。

然而,规划徒步旅行路线的一个独特方面是,除了纬度和经度之外,海拔是设计空间的第三个重要维度。像任何好的轨迹地图一样,我们的工具应该敏锐地意识到地形特征。

我的自定义高度 API 使用了奋进号航天飞机在 2000 年收集的高分辨率雷达数据。对于大约 18GB 的硬盘空间, SRTM GL3 数据集覆盖了近 1.2 亿平方公里的地球陆地,增量为 90m。使用 rasterio 库,我们可以从路径段中提取高程剖面,而无需将整个地图加载到内存中:

不错吧?有趣的是,谷歌目前对他们的 Elevation API 的每 1000 个请求收取 5 美元,但多亏了 NASA 和几行代码,我们可以免费获得同样高质量的结果。

应用和用户界面

Dash 是我用 Python 构建交互式仪表盘的首选框架。通过将前端抽象为可定制的组件,它允许数据科学家专注于其用例的功能,而不是设计。由于已经有几十个优秀的 Dash 教程和例子,我将把重点放在我的具体应用程序的布局和逻辑上。

如你所料,用户界面聚焦在地图上,它利用了 Mapbox 的地形 tileset。下拉菜单允许您从缓存的路径系统中进行选择,或者您可以通过地理编码下载新的路径系统。

一旦选择了一个区域,就可以使用 dash-leaflet 库来可视化 OSM 踪迹网络。当您计划的路线在二维地图上具体化时(下一节将详细介绍),相应的高程剖面将实时绘制。

查看围绕旧 Rag 山的所有 3 个维度的循环。在谢南多厄国家公园(图片由作者提供)。

路线规划引擎

我的应用程序允许用户使用两种“模式”中的一种来计划他们的徒步旅行。在“自定义”模式下,你只需点击地图上的十字路口,就能看到你的路线栩栩如生,一次一段。在幕后,我们尝试使用最短路径算法智能地连接各个点。在下面的动画中,我们看到一条用户定义的路线在凯霍加国家公园成形。

点击凯霍加国际公园的一条路线(图片由作者提供)。

自动模式更有趣一点。我的目标是创建一个约束优化函数,它考虑了我在规划循环时要寻找的主要因素:

  • 仰角增益
  • 距离
  • 穿越尽可能多的独特路线(例如,在徒步旅行中的多个地点,最大限度地减少原路返回的次数)

数学上,最佳回路将满足以下条件:

其中 P 是路径, d(P) 是路径距离, D 是目标距离, r(P) 是唯一的遍历节点的分数(例如,一次往返的 r 值为 0.5), e(P) 是路径的仰角增益, E 是目标仰角增益。

遍历循环图来寻找可用的循环也是一项重要的任务。最后,我选择了一种结合了最短路径和简单循环的方法,这些路径不包含任何重复节点。

点击轨迹交叉点附近的地图定义了find_best_loop功能的根节点,即循环的终点。这使得用户能够自动生成在关键位置开始和结束的徒步旅行,比如停车场或小路。下面,该算法建议在谢南多厄国家公园的旧拉格山进行一次严格的跋涉,以满足所提供的约束。

使用路线优化引擎和用户定义的约束来自动生成完美的循环(图片由作者提供)。

关于计算累积仰角增益的一个有趣的旁白——令人惊讶的是,没有明确的数学定义。你可以想象,如果你包括每一段正斜率,即使相对平坦,但崎岖不平的道路可能会导致显着的海拔变化。关键是通过添加一个滚动阈值(一个用户定义的超参数)来平滑曲线,如这篇文章中所述。

将路径导出到导航工具

一旦你计划了一次完美的徒步旅行,你会想把它导出为一种定向运动友好的格式。对于硬拷贝版本,可以直接从用户界面打印地图和高程剖面图。

然而,用户也可以将他们的路线导出为 GPX 文件(另一种地理空间格式),并上传到 AllTrails、Gaia、谷歌地图和其他 GPS 跟踪应用程序。

将路线导出为 GPX 文件并上传到 AllTrails(图片由作者提供)。

现在,你可以通过手机导航你的定制路线,就像任何其他策划的 AllTrails 远足一样!

利用 AllTrails 的移动应用程序来跟踪我的定制徒步旅行(图片由作者提供)。

包扎

在这个演示中,我们利用来自 OSM 和 NASA 的开源地理数据集构建了一个实用的徒步旅行计划应用程序。使用图论、数值分析和领域专业知识的组合,我们提出了一个数学上合理的路线优化引擎,以自动找到满足各个目标的完整回路。

我期待着不断改进 UI 和底层算法。如果你想自己运行代码,可以在 Github 上查看完整的库。

参考

王高·a·哈格伯格、丹尼尔·a·舒尔特和彼得·j·斯沃特,“使用 NetworkX 探索网络结构、动力学和功能”,载于第七届 Python 科学大会会议论文集(SciPy2008) ,格尔·瓦洛夸、特拉维斯·沃特和贾罗德·米尔曼(编辑),(美国加利福尼亚州帕萨迪纳),第 11–15 页,2008 年 8 月

地图数据版权归 OpenStreetMap 贡献者所有,可从https://www.openstreetmap.org获得

美国航天局航天飞机雷达地形学任务(SRTM)(2013 年)。航天飞机雷达地形学任务(SRTM)全球。由 OpenTopography 分发。https://doi.org/10.5069/G9445JDF.访问时间:2022–08–30

机器学习的平台产品

原文:https://towardsdatascience.com/platform-products-for-machine-learning-3d3749443d2

如何让采用机器学习的组织中的价值交付团队更容易使用平台产品进行部署,从而为他们提供最佳机会?

使用(代码)忍者开发和操作机器学习应用程序是不可扩展的。将精心策划的、灵活的平台开发成产品,并使它们易于为 ML 团队所用,是更加务实的。来自 Pexels 的 Mikhail Fesenko 的照片。

在最近发表的一篇文章机器学习的团队拓扑中,我建议组织在他们的机器学习(ML)之旅中应该采用由四种类型的团队组成的团队拓扑,如图 1 所示。团队类型包括流程一致的 ML、ML 支持、数据/基础设施子系统和 ML 平台团队。要了解这些团队的概况,请查看这篇文章。在这篇文章中,我们深入探讨了 ML 平台团队。特别是,我们更详细地解释以下几点。

  • 为什么需要 ML 平台团队?
  • 这些团队应该开发/维护什么样的产品?
  • 我们如何开始使用这些产品/团队?

图 1:交付机器学习解决方案的组织的团队拓扑。有四种类型的团队,如四个彩色框所示:流程一致的 ML、ML 支持、数据/基础设施子系统和 ML 平台团队。每个彩色的盒子代表一个单独的团队。每个圆形虚线框代表一个特定团队类型的分组。每组的队伍数量不是用彩色方框的数量来体现的;它可以变化。细黑箭头表示团队之间没有太多交互的直接服务使用。较粗的箭头表示团队之间参与度更高,协作性更强。作者创造的形象。

为什么选择 ML 平台团队?

每个 ML 团队都需要一个或多个 ML 平台来开发、维护和交付 ML 解决方案。通常,团队的目标是将团队交互的平台数量保持在较低水平。然而,由于我们倡导 DevOps 文化,团队很难接受这种文化。问题是团队是购买还是建立自己的平台。答案是两者都不是。使用认知负荷来解释推理更容易,认知负荷是我们处理信息能力的极限,正如乔治·a·米勒在 1956 年提出的。对于一个 ML 团队,可以有三种类型的认知负荷,改编自管理团队学习的认知负荷。

  1. *内在:*与基础数据/ML 任务相关,例如,如何编写 Spark 转换、制定 ML 管道等。
  2. *密切相关:*与需要特别关注的复杂数据/ML 任务相关,例如,如何为特定的操作使用准备特性,如何在使用之前/期间/之后解释模型性能?
  3. 无关:与执行数据/ML 任务的环境有关。例如,在哪里执行代码质量分析,如何在那个环境中执行基本代码质量分析,在哪里管理建模活动,以及如何在那个环境中执行常见的模型管理任务?

内在认知与流程一致的 ML 团队中的基本技能有关,应该通过招聘、培训、结对/mob 编程、黑客马拉松等来处理。此类负载的减少工作应该由工程/数据科学/分析经理来推动。密切相关的认知负荷与流对齐团队中的高级任务有关,可以通过足够的机会解决此类问题和审查专家的支持来减少。然而,额外的认知负荷与日常多余的 MLOps 任务有关,这些任务对保持工作记忆没有什么价值。其中大部分与平台管理有关。如果流对齐的 ML 团队可以将这个问题卸载到另一个团队,那么团队就可以获得额外的能力来处理相关的认知负载,从而驱动关键的增值任务。

简而言之,当我们为流对齐的 ML 团队采用认知负荷的概念时,我们需要保持团队的 ML 系统的规模,包括应用程序和平台,基于团队的认知能力。团队需要考虑的平台管理越少,团队就越能承担增值的认知负荷。确保这一点的唯一方法是组建团队,开发和管理一套精心策划、更新、发展并最终被抛弃的平台。

有哪些 ML 平台产品?

常见的 ML 平台活动如下:

  1. 数据/ML 探索:探索数据访问/争论,ML 方法,可视化,…
  2. 代码管理:源代码编辑,版本控制,持续代码质量评估,持续代码发布,…
  3. 数据/功能管理:数据管道编排、数据快照、功能等
  4. 模型管理:ML 管道编排,跟踪实验,处理模型生命周期,模型服务,…
  5. 报告:仪表板、错误记录、监控……

很难找到涵盖上述所有任务的平台。更实际的做法是购买/开发平台组件,这些组件通过显式 API 调用以可组合的方式相互协作。我们推荐以下类型的平台来支持上述任务:

  1. 像 JupyterHub 这样的笔记本环境支持
  • 使用流行的数据/ML 语言进行编程,比如 Python、Spark 和 R
  • 访问不同类型的数据源和格式
  • 使用可视化和验证来发现数据中的洞察力
  • 使用最先进的框架和库组合解决方案组件

2.一个源代码管理平台,如 GitHub ,支持

  • 与流行的本地集成开发环境集成
  • 实施标准的 DevOps 风格的代码管理策略,如 Gitflow、持续集成和部署等。,适合分布式开发人员
  • 提醒开发人员代码管理行为的状态,例如持续集成阶段的失败、合并拒绝等。

3.大数据管理平台,如 Databricks ,支持

  • 在一个或多个 blob/file/… store 系统中读取/写入大规模数据
  • 以编程方式组成数据管道
  • 通过 API 和预定触发器触发数据管道作业
  • 当作业阶段失败或整体作业成功时发出警报
  • 将弹性集群附加到数据管道作业
  • 执行数据验证、快照等。,以编程方式

4.一个 ML 平台,如顶点 AI ,支持

  • 在如上所述的数据管理平台中执行任务,但是适用于 ML 管道而不是数据管道
  • 存储关于 ML 培训工作的元信息,以便于理解运行状态
  • 保存模型并更改其生命周期,例如,试运行、生产、归档等。
  • 在允许预测作业以无缝、高效的方式使用模型或其推理结果的环境中服务模型

5.一套脱节的(Azure/GCP/AWS)云原生平台工具,支持

  • 将运行结果/分析作为仪表板发布,供风险承担者研究
  • 记录/观察每次运行的详细中间状态,以便在事情没有按预期进行时进行根本原因分析
  • 监控模型、应用程序、代码、系统性能随时间的变化

显然,我们可以使用相互集成的开源解决方案来开发所有这些平台。我们还可以使用/购买托管平台,这些平台隐藏了许多平台内部问题,只暴露与价值交付相关的功能。我建议在这两个极端之间找到一个平衡点来构建平台堆栈。我建议使用平台团队的认知能力和快速流动能力作为指导机制。如果您选择购买,请选择那些众所周知的平台,这些平台能够很好地支持一些任务的关键方面,同时为其他功能提供良好的 API 集成。

我个人比较喜欢团队拓扑这本书里提倡的平台定义。调整他们对平台作为产品的定义,我们建议 ML 团队应该消费来自自助服务平台的服务。这些平台不应该臃肿,以保持它们易于开发、更新和丢弃。这些平台应该被开发成最薄的可行平台,它包含最少的 API、工具、培训、文档和支持系统。

如何组建平台产品团队?

组建平台团队与其说是科学,不如说是一门艺术,但是我们可以使用科学的方法结合我们的经验来完善我们组织的艺术。在这种特殊情况下,从组织的经验中提取数据将是正确的科学方法。以下是一些可用于组建平台团队的技术:

  1. 从符合组织的 IT/云治理策略的平台开始。如果为某个组织推荐的云是 Azure,目标是购买一个平台,首先尝试从 Azure 中选择。当你用尽所有其他选项时,转到其他选项。多云集成是混乱的。为了实现数据驱动,我们应该为组织制定一个高级的技术雷达,包括当前使用的、废弃的和未来的平台。标出有不合规风险的平台。
  2. 尝试选择一个在组织中已经有用户群的平台。这样的选择使得平台产品的开发和采用变得容易。同样的规则也适用于内部组件。在这种特殊情况下,群体的智慧通常会战胜个人的偏好。为了确保数据驱动,我们应该在前面提到的技术雷达中包括广泛使用的平台。这些平台的受欢迎程度应该反映在科技雷达上。此外,平台的高级用法应该被编目,并在推荐和反模式实践中突出显示。
  3. 创建针对团队角色的平台。如果组织的路线图包括创建许多新的 ML 解决方案,从而产生许多新的与流程一致的 ML 团队,那么最好将重点放在对代码、数据和模型管理有一些最小支持的探索平台上。这将允许团队在他们的探索工作中是有效的,并且以轻量级的方式转移到生产阶段。另一方面,如果路线图包括使现有的 ML 团队走向成熟,以进行大规模或更复杂的部署,那么优先考虑适合大容量工件/信息处理的编排、服务和报告机制是务实的。此外,如果有很多团队工程能力低下,那么最好优先考虑支持低代码实践的平台。为了以数据为导向,形成一个组织图,列出所有过去、现在和未来的利益相关者,根据他们在 ML 旅程中所处的阶段以及团队成员的构成,按照他们的角色进行分类。
  4. 避免创建非常大的平台团队或太多的平台团队。两者都使得流程对齐团队和平台团队之间的沟通模式变得复杂,并最终导致平台臃肿。我个人喜欢有 4-6 名工程师的团队。要做到数据驱动,基于软件通信模式绘制通信模式。在绘制这种模式时,从圈的复杂性中获得灵感。如果这样的模式为流程对齐的团队总结了复杂的依赖关系,这通常是平台团队边界对于流程对齐的团队来说太复杂的迹象,并且将无法实现快速流程。
  5. 每个平台团队都应该有自己的使命、目标、时间表和免责声明,并得到利益相关方的确认。没有利益相关者的平台团队是不应该存在的,即使是在早期。由于缺乏利益相关者,许多组织很难将他们的 ML PoCs 转化为产品。虽然认为 ML 团队无论如何都需要平台,并且一旦平台建立起来,团队就会使用它们是很方便的,但是每个人都应该知道最终所有的软件产品都是可选的。不尽早让利益相关者参与进来肯定会让平台不受欢迎。
  6. 每个平台都应该交付成果而不是产出。确保团队采用最大化易于衡量的结果的指标。比如关闭的票数是一个输出的衡量标准,它可能或者可能预示着一个平台的成功。然而,与流程一致的团队的生产力的增加,例如在生产中交付的模型的数量,清楚地表明一个平台可能已经使团队成功地实现了他们的目标。一旦选择了指标,就要对能够最大化指标的平台特性进行优先级排序。在付诸实施之前,确保与利益相关者一起验证指标和特性。为了实现数据驱动,与可能成为客户的流程一致的团队一起执行旅程规划,并定义取得成功的里程碑,并从中得出 KPI。在运营时,建立 KPI 的基线,并跟踪 KPI 随时间的变化。

备注

您是否正在经历类似的旅程:加入/组建 ML 平台团队?你同意/不同意以上观点吗?我确信我可能有盲点,这可以通过公开的建设性对话来解决。请通过评论或分享你偶然发现的文章来分享你的观点。

使用 Python 实现 PLAXIS 输出可视化

原文:https://towardsdatascience.com/plaxis-output-visualisation-using-python-a144d2367094

PLAXIS 自动化系列

逐步走向自动化的指南

卢克·切瑟在 Unsplash 上的照片

PLAXIS 输出应用程序提供了使用内置绘图工具比较不同阶段输出的可能性。虽然它提供了一些基本的绘图功能,允许用户可视化结果,但人们经常使用 Excel,因为它允许在绘图格式、数据操作和结果共享方面的灵活性。

毫无疑问,Excel 是一个强大而著名的数据可视化软件。但是,PLAXIS 没有提供与 Excel 的完全集成,这使得提取和可视化过程非常耗时。在数据可视化方面,有三个可能的改进领域:

  1. 将所有数据合并到一个工作表中进行绘图
  2. 创建绘图时,在 Excel 中自动选择数据系列
  3. 使用定制的系列名称和轴标题创建绘图

本教程旨在扩展从第二教程中学到的教训。我们将提取多个阶段的输出,并使用图来比较结果。这可以通过以下三个步骤来完成:

  1. 将外部模块安装到 PLAXIS 环境中
  2. 提取多个阶段的结果
  3. 使用 Excel 中的图表比较不同阶段的结果
  4. 用 Openpyxl 格式化

和以前一样,本教程要求读者已经安装了 VS 代码和 PLAXIS 环境。如果您不熟悉这个页面,请按照下面文章中的说明进行操作。我们开始吧!

https://medium.com/@philip.studio11/start-using-python-to-automate-plaxis-35a5297321e7

PLAXIS 示例模型

我们将使用第二节课中相同的 PLAXIS 2D 模型。如果您还没有这样做,请按照下面的说明来设置和计算模型。

https://medium.com/@philip.studio11/goodbye-boring-plaxis-output-with-python-fc5c8b26cdb2

与上一个教程类似,我们将使用“open_output.py”打开 PLAXIS 文件,这样就可以启用 API 服务器。

概括一下,我们使用下面的代码来打开文件:

作者截图自 VS 代码

运行上面的代码后,PLAXIS 2D 输入和输出应用程序应该会自动打开。从输出应用程序中,您应该看到“服务器在端口 10001 上活动”。

作者截图自 PLAXIS

步骤 1:将外部模块安装到 PLAXIS 环境中

我们的目标是从 PLAXIS 中提取结果,并在 excel 中创建比较图。为此,我们需要三个外部模块:

  • 熊猫
  • Xlsxwriter
  • Openpyxl

通过 PLAXIS 命令提示符安装外部模块的过程已经在第二篇教程中详细描述了。在继续以下步骤之前,请确保您已经完成了该流程。

从上一个教程来看,熊猫和 xlsxwriter 都应该安装。请注意,Python 模块是版本敏感的,这是因为一些模块与其他模块有连接,因此版本更新可能会导致故障。这对于在 PLAXIS 环境中保持一致的模块依赖关系至关重要。

我在本教程中使用的模块版本如下:

pandas == 1.5.2
xlsxwriter == 3.0.3
openpyxl == 3.0.9
defusedxml == 0.7.1

要检查模块的当前版本,我们可以在 PLAXIS 命令提示符下使用以下命令:

python -m pip show pandas

作者截图自 PLAXIS

如果版本不一致,我们可以使用以下命令来升级/降级版本:

python -m pip install --upgrade pandas==1.5.2

安装其余模块:

python -m pip install xlsxwriter==3.0.3
python -m pip install openpyxl==3.0.9
python -m pip install defusedxml==0.7.1

在某些情况下,您可能会在安装 defusedxml 时遇到错误

ERROR: Could not install packages due to an OSError: [WinError 5] Access is denied

如果是这种情况,请尝试以下代码:

python -m pip install --upgrade defusedxml==0.7.1 --user

现在我们已经安装了所有需要的模块。我们现在可以使用 Python 脚本从 PLAXIS 的不同阶段提取结果了。

步骤 2:提取多个阶段的结果

步骤 2 的主要目标是从“挖掘”模型的三个阶段中提取结果(弯矩)。然后,我们会将结果导出到 Excel 中一个名为“combined_Plate_1”的工作表中,该工作表包含以下各列:

作者截图自 excel

首先,我们创建一个空的 python 文件,并将其命名为“plot_plate_combine.py”。

导入模块并启动服务器

from plxscripting.easy import *
import math
import pandas as pd
from openpyxl import load_workbook
from openpyxl.chart import (
    ScatterChart,
    Reference,
    Series,
)
###############################################
PORT_i = 10000  # Define a port number.
PORT_o = 10001
PASSWORD = 'SxDBR<TYKRAX834~'  # Define a password.

# Start the scripting server.
s_i, g_i = new_server('localhost', PORT_i, password=PASSWORD)
s_o, g_o = new_server('localhost', PORT_o, password=PASSWORD)

定义文件名

文件位置:C:\ Users \ phtsang \ Desktop \ PLAXIS _ v 22 \ Script

文件名:可以是您想要的任何名称

EXCEL_PATH=r'C:\Users\phtsang\Desktop\PLAXIS_V22\Script\\'
EXCEL_NAME='Plate_y.xlsx'

FILENAME=EXCEL_PATH+EXCEL_NAME

输入定义

在这种情况下,我们将从以下阶段中提取‘Plate _ 1’的弯矩:

  • 安装支柱[阶段 3]'
  • 第二(水下)开挖阶段[第 4 阶段]'
  • '第三挖掘阶段[第五阶段]'
###############################################
#Inputs:
plate_input=['Plate_1']
phase_input=['Installation of strut [Phase_3]','Second (submerged) excavation stage [Phase_4]','Third excavation stage [Phase_5]']

您的脚本应该是这样的:

作者截图自 VS 代码

结果提取

在预处理之后,我们将定义一个函数来提取弯矩(上一个教程的简化版),并将其命名为“get_plate()”。

def get_plate(plate_o,phase_o):

    plateY=g_o.getresults(plate_o,phase_o,g_o.ResultTypes.Plate.Y, "node")
    plateM=g_o.getresults(plate_o,phase_o,g_o.ResultTypes.Plate.M2D, "node")

    phasename=str(phase_o.Identification).split('[')[0]
    col1='Bending Moment [kNm/m]'+'_'+phasename

    results = {'Y': plateY,col1: plateM}

    plateresults=pd.DataFrame(results)
    plateresults = plateresults.sort_values(by=['Y'],ascending=False)

    return plateresults

作者截图自 VS 代码

导出到 Excel

与上一个教程不同,我们想要从多个阶段而不是单个阶段提取结果。因此,需要修改“export_excel()”函数。涉及三个动作:

  1. 遍历当前模型中现有阶段的名称
  2. 与用户输入的阶段(即阶段 3、4 和 5)进行交叉检查。
  3. 如果匹配,则提取结果并导出到单个工作表(例如 Plate_1_Phase_3)
  4. 将结果合并到新的数据框架中,并导出到工作表(' combined_Plate_1 ')

这些操作可以用下面的代码来完成。我们来分解一下!

def export_excel(plate_input,phase_input,filename):
    writer = pd.ExcelWriter(filename, engine='xlsxwriter')
    combined=[]
    for i in range(len(phase)):
        for j in range(len(phase_input)):
            if phase[i].Identification == phase_input[j]:
                name=str(phase[i].Identification).split(' [')[1]
                name=name.split(']')[0]
                sheet_name = "%s_%s" % (plate[0].Name, name)
                results = get_plate(plate[0], phase[i])
                combined.append(results)
                results.to_excel(writer,sheet_name=sheet_name,index=False)

    combinedsheet='combined'+'_'+str(plate[0].Name)
    combined=pd.concat(combined, axis=1)
    combined.to_excel(writer,sheet_name=combinedsheet,index=False)
    writer.save()

export_excel(plate_input,phase_input,FILENAME)
  • 我们需要首先定义一个空列表“combined=”。这允许我们将每个阶段提取的结果附加到一个列表中,这样我们可以在最后一步将它们连接起来。
  • 下一步是遍历模型的现有阶段,看看哪个阶段与我们的输入相匹配。这是使用 PLAXIS 命令“阶段[i]”完成的。“标识”,给出每个阶段的全名。
for i in range(len(phase)):
        for j in range(len(phase_input)):
            if phase[i].Identification == phase_input[j]:
  • 以下代码用于命名目的。例如,“相位[0]”。“识别”将给出“支柱安装[阶段 3]”。我想得到位于方括号之间的“Phase_3”。然后,我将“Plate_1”和“Phase_3”组合起来,形成工作表名称。
name=str(phase[i].Identification).split(' [')[1]
name=name.split(']')[0]
sheet_name = "%s_%s" % (plate[0].Name, name)
  • 使用“get_plate()”函数提取 PLate_1 在每个阶段的弯矩,并将其存储为结果。然后通过“combined.append(results)”方法将结果追加到“组合”列表中。最后,使用“results.to_excel(writer,sheet_name=sheet_name,index=False)”将每个阶段的结果导出到单个工作表中。
results = get_plate(plate[0], phase[i])
combined.append(results)
results.to_excel(writer,sheet_name=sheet_name,index=False)
  • 最后四行旨在合并“合并”列表中的结果,并导出到“合并 _ 板 _1”工作表。这是通过 pandas 的方法' concat()'完成的。输入“axis=1”作为参数是很重要的,因为它告诉 pandas 水平组合结果(按行)。默认情况下,“轴=0”垂直组合结果(按列)。
combinedsheet='combined'+'_'+str(plate[0].Name
combined=pd.concat(combined, axis=1)
combined.to_excel(writer,sheet_name=combinedsheet,index=False)
writer.save())

您的最终脚本应该是这样的:

作者截图自 VS 代码

既然我们已经从所有阶段提取了结果,那么我们可以继续使用 Python 在 Excel 中创建图。

步骤 3:使用 Excel 中的绘图和 Openpyxl 比较不同阶段的结果

最后,我们将学习 Openpyxl,它是一个允许我们创建 Excel 绘图的模块。

我们创建一个函数,称它为“get_combined_plot()”。

  • df_inter 存储从“combined_Plate_1”获得的数据帧。工作表是我指定工作表' combined_Plate_1 '的地方,这样我可以在那里创建绘图。
def get_combined_plot(filename,sheetname):
    df_inter = pd.read_excel(filename, sheet_name = sheetname,engine="openpyxl")
    wb=load_workbook(filename)
    sheet=wb[sheetname]
  • 然后,创建一个名为“chart1”的图表对象,这是一个散点图。之后,我们使用 openpyxl 方法(x_axis.title 和 y_axis.title)分配轴标题。
  • yvalue 使用方法“Reference()”存储结果的 Y 坐标。
  • 位置 & 船头是我指定剧情的位置。
 chart1=ScatterChart()
    chart1.x_axis.title = 'Bending Moment (kNm/m)'
    chart1.y_axis.title = 'RL (m)'
    chart={'chart1':chart1} 
    yvalue=Reference(sheet,min_col=1,min_row=2,max_row=len(df_inter)+1)
    position='G'
    prow=1
  • 然后,从第二列中提取弯矩值,并将其存储在中。
  • 一旦我们有了 x 轴和 y 轴的数据,我们使用' Series()'创建一个系列,并使用' chart1.series.append(series)'将它分配给' chart1 '。
 if df_inter.columns.values[1].split(' [')[0] == 'Bending Moment'
        value=Reference(sheet,min_col=2,min_row=2,max_row=len(df_inter)+1)
        series=Series(yvalue,value,title=list(df_inter.columns.values)[1])

        chart1.series.append(series):
  • 以下代码主要用于设置绘图样式的格式,如高度、刻度框和图例位置等(类似于典型的 excel 绘图设置)。官方文档中概述了更多的格式细节:
 charts='chart1'
    chart[charts].height=15
    chart[charts].y_axis.tickLblPos = 'low'
    chart[charts].legend.position = 'b'

    if ord(position)<89 and prow<=2:
        sheet.add_chart(chart[charts], position+str(1))
    position=chr(ord(position)+10)
    prow=prow+1
    wb.save(filename)
  • 上述步骤使用前两列(即“Y”和“弯矩[kNm/m]_ 支柱安装”)创建了绘图
  • 最后一步是遍历其余的列,并将它们作为两个额外的系列添加到现有的绘图中。
  • 为了识别柱中是否包含 Y 坐标或弯矩,我们需要使用 if-else 语句。
  • 如果列标题是' Y ',它将把列值存储到 y (即 Y 轴)。如果列标题包含“弯矩”,它会将列值存储到(即 x 轴)。
 for i in range(3,len(df_inter.columns)+1):
        if df_inter.columns.values[i-1].split('.')[0] != 'Y':
            if df_inter.columns.values[i-1].split(' [')[0] == 'Bending Moment':
                value=Reference(sheet,min_col=i,min_row=2,max_row=len(df_inter)+1)
                series=Series(yvalue,value,title=list(df_inter.columns.values)[i-1])
                chart1.series.append(series)
        elif df_inter.columns.values[i-1].split('.')[0] == 'Y':
            yvalue=Reference(sheet,min_col=i,min_row=2,max_row=len(df_inter)+1)      
    wb.save(filename)

最终的脚本如下所示:

def get_combined_plot(filename,sheetname):
    df_inter = pd.read_excel(filename, sheet_name = sheetname,engine="openpyxl")
    wb=load_workbook(filename)
    sheet=wb[sheetname]

    chart1=ScatterChart()
    chart1.x_axis.title = 'Bending Moment (kNm/m)'
    chart1.y_axis.title = 'RL (m)'
    chart={'chart1':chart1} 
    xvalue=Reference(sheet,min_col=1,min_row=2,max_row=len(df_inter)+1)
    position='G'
    prow=1

    if df_inter.columns.values[1].split(' [')[0] == 'Bending Moment':
        value=Reference(sheet,min_col=2,min_row=2,max_row=len(df_inter)+1)
        series=Series(xvalue,value,title=list(df_inter.columns.values)[1])

        chart1.series.append(series)

    charts='chart1'
    chart[charts].height=15
    chart[charts].y_axis.tickLblPos = 'low'
    chart[charts].legend.position = 'b'

    if ord(position)<89 and prow<=2:
        sheet.add_chart(chart[charts], position+str(1))
    position=chr(ord(position)+10)
    prow=prow+1
    wb.save(filename)

    for i in range(3,len(df_inter.columns)+1):
        if df_inter.columns.values[i-1].split('.')[0] != 'Y':
            if df_inter.columns.values[i-1].split(' [')[0] == 'Bending Moment':
                value=Reference(sheet,min_col=i,min_row=2,max_row=len(df_inter)+1)
                series=Series(xvalue,value,title=list(df_inter.columns.values)[i-1])
                chart1.series.append(series)
        elif df_inter.columns.values[i-1].split('.')[0] == 'Y':
            xvalue=Reference(sheet,min_col=i,min_row=2,max_row=len(df_inter)+1)      
    wb.save(filename)                    

combinedsheet='combined_Plate_1'
get_combined_plot(FILENAME,combinedsheet)

作者截图自 VS 代码

作者截图自 VS 代码

使用以下命令运行脚本。

(PLAXIS) C:\Users\phtsang\Desktop\PLAXIS_V22\Script>python plot_plate_combine.py

现在,如果您在之前指定的位置打开 excel 电子表格,并转到“combined_Plate_1”工作表。你可以看到我们已经提取了所有三个相位的 Y 坐标和弯矩。更重要的是,我们有一个包含所有结果的图,可以让我们比较不同阶段的弯矩!

作者截图自 excel

太棒了。您已经成功地从 PLAXIS 中提取了结果,并使用它们通过 Python 在 Excel 中创建了一个图。

结论

以上是关于使用 Python 实现 PLAXIS 输出可视化的第三篇教程。到目前为止,您应该能够提取多个阶段的结果,并在 excel 中创建图表以进行结果比较。这可以进一步扩展到包括不同阶段的多个结构元素,这意味着整个输出提取过程可以自动化。我将在以后的教程中进一步讨论这一点。

如果你喜欢阅读这类内容,请随时关注我的页面。我将继续发布这一系列关于用 Python 自动化 PLAXIS 的教程。除此之外,我还热衷于分享关于如何使用 Python 来自动化工程中的工作流的知识。

用离线强化学习下棋

原文:https://towardsdatascience.com/playing-chess-with-offline-reinforcement-learning-411edc5efd5f

为我们的广义人工智能添加离线强化学习

作者图片

大家好,今天,我们将改进我们在我的博客文章中创建的广义人工智能,与广义人工智能下棋。为此,我们将对我们的培训流程进行两项重大升级。首先,我们将利用离线强化学习(RL)。其次,我们将对训练主干层时使用的数据做一个小小的改动。

在我们的人工智能的最初实现中,我们使用了一种叫做在线学习的训练方法。在线学习是一种学习形式,我们通过与环境的直接交互来收集训练数据,并且在训练期间只处理一次。

在线强化学习|作者图片

以前的培训设置

在我们最初的实现中,每次动作后,我们都将所有游戏状态保存到缓存中。然后在游戏结束时,我们用那个缓存训练所有的模型层。

初始训练图|图片由作者提供

我们在游戏结束时更新模型,而不是在每个动作之后,以确保模型可以学习零和游戏(国际象棋、围棋、跳棋)。零和游戏没有直接的回报,这使得它们在每次行动后都很难训练。这源于这样一个事实,即直到游戏结束,你才知道代理人选择的行动是好是坏。这就是为什么我们将所有的行动存储在缓存中,因为在游戏结束时,我们可以假设赢家的所有行动都对获胜产生了积极的影响,而输家的所有行动都损害了我们获胜的机会。

当前问题

当在训练期间分析损失值时,我注意到许多层之间的不稳定。

制图表达图层损失值|按作者分类的图像

主干层损失值|作者图片

价值头部损失价值|作者图片

保单头损值|作者图片

奖励人头损失值|图片作者

下一个国家元首损失值|作者图片

在 RL 中,不稳定是一件有些熟悉的事情,由于我们的模型使用了穆泽罗的论文作为参考,我们可以推断这种不稳定会在某个点上收敛,允许模型像原始论文一样有效地学习掌握游戏。为了支持这一推论,我们可以看到明确的证据,即模型正在学习,因为最新的模型持续优于先前的模型。然而,通过解决这个不稳定的问题,我相信我们可以提高这种趋同的速度。

很难说不稳定的确切原因是什么。但是,我认为这部分是由于使用在线 RL 重新训练整个模型造成的。更具体地说,在每场比赛后使用在线 RL 训练代表层和骨干层。这种想法的逻辑是,我们没有给模型足够的数据来学习有效的编码,在每场比赛后重新训练表现层和主干层。上面的主干损耗图支持了这一观点。该图显示主干层在创建一致的游戏编码时遇到了困难。从该图中损失值的可变性可以明显看出这一点。无效的游戏编码对整体模型健康是有害的,因为它会迫使模型的头部产生不准确的预测,因为它不能推断出适当的信息。因此,我们的主要焦点将是改进表示层和主干层。通过这样做,我相信我们的完整模型将会获得显著的收益。

改进的培训设置

如简介中所述,我们将实现的第一个改进是使用离线 RL 。离线 RL 是一种使用在探索期间发现的记录数据来训练 RL 代理的方法。

离线强化学习图|图片作者

对于我们的情况,这将是积极的,因为我们可以在训练期间向主干层提供更多的数据,因为我们不再需要扔掉所有以前的游戏。

我们的新方法将不会独立地使用离线 RL,而是离线和在线 RL 的混合方法。首先,我们将在每场游戏后使用在线 RL 微调模型的头部,同时也记录游戏以供将来分析。通过在每场比赛后微调模型的头部,我们让模型尝试新学到的动作,从而帮助改进我们的数据探索。

在玩了 x 次(我在测试中用了 20 次)游戏后,使用所有记录的游戏数据通过离线学习更新了完整的模型。我们在玩完第 x 个游戏后重新训练所有层,而不是每一个游戏,因为训练所有记录的游戏数据是非常计算密集型的。因为计算需要钱,我们认为最好是在最少的时间内训练模型,而不是最少的游戏。我们选择在这个阶段离线重新训练完整的模型,而不是继续在线训练,因为我们相信,考虑到大数据集,这有助于模型不要把太多的重量放在它的最后一个游戏上,并给它更多的例子来创建更好的游戏编码。

为了减少偏向某些游戏状态的机会,所有重复的游戏状态都将被丢弃,保留最近出现的游戏状态。保持游戏状态的最新实例也应该有助于通过提供模型认为在探索时什么是更好的选择的例子来防止模型倒退。

这些修改将使我们的新方法如下图所示。

新训练图|作者图片

我们要做的第二个改进是训练主干层时使用的实际数据。之前,为了训练模型,我们使用了来自模型的不同头部的损失函数的组合。

老骨干流失功能|图片作者

这个损失函数并不是完全无效的,所以并不清楚它是否需要改变。尽管在思考了这一层的目的之后,我意识到我没有尽可能有效地训练它。

主干层的目的是创建游戏状态编码,这意味着它需要为所有游戏状态产生一致的编码。这意味着游戏状态和没有动作必须具有与前一个游戏状态相同的编码,并且相应的动作会导致该游戏状态。

游戏状态编码定义|作者图片

上面的等式代表了我们一致的编码逻辑的思想。利用上面的等式,我们可以利用自我监督学习来更有效地训练主干层,使得我们的新主干层损失函数看起来像这样。

新的主干损失函数|图片由作者提供

有了这两个新的升级,这里是我们新的损失价值已经成为。

新的制图表达图层损失值|作者提供的图像

新主干层损失值|作者图片

价值头部损失价值|作者图片

保单头损值|作者图片

奖励人头损失值|图片作者

下一状态损失值|作者图片

正如你在上面的图表中看到的,我们看到我们的损失值更加稳定。特别是在表示层和主干层,我们认为这是问题的根源。

随着模型的更新,我们也看到了培训时间的改进。为了验证这一说法,我对三个不同的模型进行了一场小型比赛,这些模型具有相同数量的参数,但都使用不同的方法进行了训练。使用旧的训练方法和旧的骨干损失函数对第一个模型(旧的)进行更长时间的训练。使用新的训练方法和新的骨干损失函数,在更短的时间内创建了第二个模型(新的)。最后用更少的时间创建了第三个模型(全离线),用最新的骨干丢失功能在每场比赛后使用离线 RL。

锦标赛结果|作者图片

基于上表中的结果,我们可以肯定地说,新模型在训练时间更短的情况下表现更好,从而验证了新的训练方法。然而,从这个实验中一个有趣的观察结果是,我的完全离线模型在训练时间更少的情况下也表现得与旧方法相似。很难确切知道是哪种变化导致了这种情况。因为这可能意味着离线方法优于完全在线方法,新的主干损失函数优于旧的损失函数,或者完全离线和最新损失函数的组合有所帮助。

模型训练时间对比|作者图片

上面我展示了另一组训练统计数据,以帮助说明新的和完全离线训练方法之间的差异,从而更好地理解为什么新方法在更短的时间内表现更好。上表显示,新方法强调探索,因为它比完全离线模型具有更长的探索时间,同时保持较低的总训练时间。新模式进一步显示了对探索的重视,比完全离线模式玩了更多的游戏。我相信这种对探索的强调赋予了新方法力量。此外,这种对探索的强调允许在更短的时间内获得更大的数据集,这将使所有模型层受益。

我相信 DeepMind 的新龙猫论文有助于支持更多数据非常重要的想法,因为它深入探索了基于变压器的模型的概念。我在这里不打算深入讨论 Chinchilla 的论文,但一个快速的总结是,我们的比例定律是错误的,应该根据模型中的参数数量提供更多的数据。

谢谢

现在你知道了,我们已经成功地升级了我们的通用人工智能,让它可以更有效地训练。你可以在我的 GitHub 这里查看完整版本的代码。请自行下载并试用。

感谢阅读。如果你喜欢这样,可以考虑订阅我的账户,以便在我最近发帖时得到通知。

参考

  • https://towards data science . com/playing-chess-with-a-generalized-ai-b 83d 64 AC 71 Fe
  • https://arxiv.org/abs/2201.13425
  • https://arxiv.org/abs/1911.08265
  • https://arxiv.org/abs/2203.15556
  • https://arxiv.org/abs/1706.03762

玩钱球:用线性规划创建一个高效的 NBA 团队

原文:https://towardsdatascience.com/playing-moneyball-creating-an-efficient-nba-team-with-linear-programming-ef14f6383861

鸣谢: Unsplash 上的 JC Gellidon (照片在 Unsplash 许可下免费使用)

玩钱球:用线性规划打造一支高效的 NBA 球队

对于任何热衷于分析的体育迷来说,班尼特·米勒执导的 2011 年美国体育剧《T2》是一部必看的电影。根据 IMDB 中的描述,在这里我们可以看到“奥克兰运动家队总经理比利·比恩通过使用计算机生成的分析来获得新球员,成功地尝试以精简的预算组建一支棒球队”的故事。即使没有在专业运动队担任数据科学家的工作(肯定是我梦想的工作之一),我们也可以用这部电影来学习线性编程。

大英百科全书将线性规划定义为“一种数学建模技术,其中一个线性函数在受到各种约束时被最大化或最小化”。因此,我们将尝试优化一个线性函数(我们称之为目标函数)。但是,我们的解决方案必须有一定的条件,称为约束(重要的是要提到,这些约束也将是线性函数)。考虑到这一点,我们可以说线性规划是在规定分析中使用的一种方法,因为它明确地告诉我们哪一个是我们可以优化特定指标的最佳解决方案。由于目标函数和约束都将由决策变量组成(每个变量代表一个要做出的单独决策),为了解决我们的问题,我们将有三个步骤:变量定义、约束创建和目标函数定义。

问题和变量定义

我们将尝试使用从 basketball-reference.com 检索的标准统计数据、高级统计数据和工资信息,并使用谷歌开发的开源优化库 OR Tools,来建立一支高效的 NBA 球队,而不是建立一支棒球队。然而,在开始定义我们的相关变量之前,我们做了一些数据预处理,创建了一个字典,其中包含每个球员的重要信息,包括他的姓名、位置、三分球命中率、工资、每场比赛的得分和 per(根据维基百科,这是“霍林格的全能篮球评级,试图将球员的所有贡献归结为一个数字”),以及一个包含每个位置上的球员信息的字典。

*# We import the libraries we will use*
**import** os 
**from** ortools.linear_solver **import** pywraplp
**import** pandas **as** pd
**import** numpy **as** np*# We will use three DFs: one containing "advanced" NBA stats, one that will have "standard" stats and* 
*# one that has salaries information*
cwd **=** os**.**getcwd()
cwd **=** cwd**.**replace("code", "data")

df_advanced **=** pd**.**read_csv(f"{cwd}/nba_2021_advanced_stats.csv", sep**=**";")
df_standard **=** pd**.**read_csv(f"{cwd}/nba_2021_standard_stats.csv", sep**=**";")
df_salaries **=** pd**.**read_csv(f"{cwd}/nba_2022_salaries.csv", sep**=**";")*# We filter relevant columns*
df_advanced **=** df_advanced[["Player", "Code", "Pos", "PER"]]
df_standard **=** df_standard[["Player", "Code", "G", "MP", "3P%", "PTS"]]
df_salaries **=** df_salaries[["Player", "Code", "2021-22"]]**.**rename(columns**=**{"2021-22": "Salary"})*# We merge all dataframes*
df **=** pd**.**merge(df_advanced, df_standard, how**=**"inner", on**=**["Player", "Code"])
df **=** pd**.**merge(df, df_salaries, how**=**"inner", on**=**["Player", "Code"])
df **=** df**.**drop_duplicates()**.**reset_index(drop**=True**)*# We filter rows, keeping players that played at least ten matches, and played at least 5 minutes per game*
df **=** df[(df["G"] **>=** 10) **&** (df["MP"] **>=** 5)]*# We create an index column and filter Pos column*
df["idx"] **=** df**.**index
df["Pos"] **=** df["Pos"]**.**str**.**slice(stop**=**2)
df["Pos"] **=** df["Pos"]**.**str**.**replace('-','')*# We create a dictionary that has information about the players, with a key indicating the index in df*
*# Also, we create a dictionary that has information on each player's position*
players_dict **=** {}
positions_dict **=** {}

*# We fill information on players and positions*
**for** index, row **in** df**.**iterrows():
    players_dict[row["idx"]] **=** {
        "name": row["Player"],
        "Pos": row["Pos"],
        "PER": row["PER"],
        "3P%": row["3P%"],
        "PTS": row["PTS"],
        "Salary": row["Salary"]
    }
    pos **=** row["Pos"]
    **if** pos **not** **in** positions_dict**.**keys():
        positions_dict[pos] **=** [row["idx"]]
    **else**:
        positions_dict[pos]**.**append(row["idx"])

为了建立我们的团队,我们将决定哪些球员包括在内。因此,我们的决策变量,称为 xi ,将是布尔型的,并且如果玩家 i 被选入我们的团队的话,将等于 1。既然我们已经定义了它,我们可以创建我们的求解器并创建一个字典,我们将在其中保存我们的布尔决策变量:

*# We create our variables and add it to a dictionary*
**def** create_solver_and_player_assignment_variable(df):
    *# We create the solver*
    solver **=** pywraplp**.**Solver('simple_mip_program',
                             pywraplp**.**Solver**.**CBC_MIXED_INTEGER_PROGRAMMING)
    *# We create the variables*
    x_var_dict **=** {}
    **for** index, row **in** df**.**iterrows():
        x_var_dict[row['idx']] **=** solver**.**BoolVar(str('x_'**+**str(row['idx'])))
    **return** x_var_dict, solver

约束创建

现在我们已经创建了线性规划问题和与之相关的变量,我们将定义我们的解决方案必须满足的条件或约束。记住这些约束必须是线性的,所以变量只能乘以一个常数,而不能平方,等等。

首先,我们必须拥有所需的总玩家数量,在 13 到 15 人之间。在数学上,这可以用下面的等式来表示:

在这个方程中,我们可以确定方程的三个不同的“部分”:一个下限(13),一个上限(15),以及决策变量的线性组合。当我们使用 OR 工具创建约束时,我们分两步完成:首先,我们定义约束,选择边界,并可选地选择约束的名称,然后我们“填充”决策变量的线性组合,选择变量和相关系数(这里,由于每个变量都是直接添加的,因此该系数等于 1)。这可以在下面的代码中看到:

**def** total_players_constraint(solver, x_var_dict):
    ct **=** solver**.**Constraint(13, 15, 'TotalPlayers')
    **for** x **in** x_var_dict**.**keys():
        ct**.**SetCoefficient(x_var_dict[x], 1)
    **return** solver

即使我们拥有所需数量的球员,我们也必须为所有位置配备球员。假设对于每个位置,我们必须至少有两个球员。因此,我们可以用数学方法来表达这一点,例如,对于在控卫位置上打球的一组球员 PG、:

注意,这里我们没有像前一个例子那样的上限。但是,由于我们的玩家不能超过 15 人,我们将使用该数字作为上限:

**def** players_per_position(solver, x_var_dict, positions_dict):
    **for** position **in** positions_dict**.**keys():
        ct **=** solver**.**Constraint(2, 15, f'Players_Pos_{position}')
        **for** x **in** positions_dict[position]:
            ct**.**SetCoefficient(x_var_dict[x], 1)
    **return** solver

现在,我们在总数和每个位置上都有了所需的球员数量。然而,由于 NBA 有工资帽,球员工资总额不能超过 1.124 亿美元。由于这是我们团队必须满足的一个条件,我们将把它包含在我们的模型中,并以数学方式表示如下:

请注意,当我们创建此约束时,与每个决策变量相关联的系数并不像前面的情况那样是 1,而是每个玩家的薪金,如下面的代码所示:

**def** total_salary(solver, x_var_dict, players_dict):
    ct **=** solver**.**Constraint(0, 112400000, 'TotalSalary')
    **for** x **in** x_var_dict**.**keys():
        ct**.**SetCoefficient(x_var_dict[x], players_dict[x]["Salary"])
    **return** solver

我们现在有了所有的约束,让我们建立一个有效的团队。然而,线性规划的一个积极的方面是,由于它是一个实际问题的数学抽象,它非常灵活,如果我们想的话,它允许我们轻松地包含新的和定制的约束。例如,考虑到近年来,三分球在联盟中变得越来越重要,让我们假设我们希望我们的球队中至少有 4 名优秀的三分球射手(假设优秀的三分球射手是投篮命中率大于 40%的人)。如果这个三分射手在 set T 中,我们可以用数学方法来表示:

该约束将按如下方式实现:

**def** three_points_shooters(solver, x_var_dict, players_dict):
    ct **=** solver**.**Constraint(4, 15, 'ThreePointsShooters')
    **for** x **in** x_var_dict**.**keys():
        **if** players_dict[x]["3P%"] **>** 0.4:
            ct**.**SetCoefficient(x_var_dict[x], 1)
    **return** solver

建立我们的目标函数

既然我们已经具备了解决方案必须满足的条件(三个必须具备,一个可选),那么我们就可以定义我们的目标了。请记住,我们正努力打造一支尽可能高效的团队。回想一下,每个玩家都有一个与其相关联的 PER,这是一个总结其表现的单一指标。考虑到更大的 PER 会导致更高的预期效率,我们应该尝试拥有一个 PER 尽可能高的团队。这可以用以下方式进行数学表达:

要使用或工具实现这一点,过程将类似于我们在定义约束时执行的过程,只是做了一些修改。首先,我们将定义目标函数,并用决策变量和相关系数(这里是每个玩家的 PER)填充它。最后,我们将定义我们正在尝试做的事情,即最大化目标函数(在其他一些情况下,例如,当我们在目标函数中表示成本时,我们将尝试最小化它)。这可以在下面的代码中看到:

**def** set_obj_function(solver, x_var_dict, players_dict):
    objective **=** solver**.**Objective()
    **for** x **in** x_var_dict**.**keys():
        objective**.**SetCoefficient(x_var_dict[x], players_dict[x]["PER"])
    objective**.**SetMaximization()
    solver**.**Solve()
    **return** solver, x_var_dict, objective

分析我们的解决方案和结束语

现在我们已经定义了一切,我们可以解决我们的模型,并获得我们的完美团队!为了让事情变得更有趣,我们将运行模型的两个版本:一个不包含需要 4 个三分射手的约束,另一个包含。首先,让我们来看看这支不要求三分射手最低数量的球队:

考虑到 NBA 的最高工资约为 4500 万美元,我们可以看到我们的团队由两个主要群体组成:年轻的明星球员,他们的工资通常低于其他全明星球员(如锡安·威廉森、卢卡·东契奇和特雷·扬),以及固定角色球员(如德维恩·戴德蒙和伊内斯·坎特)。唯一的“异数”是尼古拉·约基奇,他的薪水最高(也是最高的 PER 值)。

然而,当我们“激活”我们的三分射手限制时,我们的团队发生了一点变化:

在我们的新团队中,Paul Reed 被 Drew Eubanks 取代,因此总 PER 从 349.7 和 346.4 降低。这是一个重要的见解:当我们在创建我们团队的约束时要求更高(我们不仅仅想要一个有效的团队,我们还想要一个至少有三分射手的团队),合乎逻辑的是我们的解决方案的客观价值恶化。

总之,我们已经了解了线性编程问题的基础,并用 python 和 OR 工具构建了一个与体育相关的实现。我们还学习了这种方法的灵活性,并分析了带有和不带有定制约束的解决方案。最后,我留给您一个存储库,您可以在其中找到我用来构建它的代码和数据:

https://github.com/nicogarciaara/nba_moneyball

ploomber:vs code 和 PyCharm 中的模块化、生产就绪的数据管道

原文:https://towardsdatascience.com/ploomber-modular-production-ready-data-pipelines-in-vscode-and-pycharm-2b5cd4091aeb

数据科学软件工程

使用您最喜欢的编辑器开发模块化管道变得容易多了

使用 Ploomber 和您最喜欢的编辑器来开发模块化管道。图片作者。

在 Ploomber 0.14中,我们引入了一个新特性,允许 VSCode 和 PyCharm 等编辑器的用户使用.py脚本开发模块化和可维护的管道。这篇博文总结了这种集成是如何工作的。

互动。百分比格式的 py 文件

Jupyter 是开发数据科学项目最流行的工具;它提供了一个交互式环境来编写操作数据的有文化的程序。此外,其他编辑器和 ide 也接受了这个想法,只是做了一些修改。

例如, Spyder 引入了百分比格式,允许用户通过使用特殊的# %%语法拆分单元格来表示.py文件中的*【笔记本】*,以表示每个单元格:

像 VSCode 和 PyCharm 这样的 IDE 工具已经采用了 percent 格式,因为它在常规的非交互式.py脚本和.ipynb笔记本之间提供了一种平衡的体验。

我们的集成是如何工作的

Ploomber 帮助用户开发模块化和可维护的管道,其中每个工作单元都可以是一个脚本、笔记本或函数。例如,使用两个输入数据集的管道可能如下所示:

管道允许你模块化你的工作。图片作者。

除了加载数据的任务之外,所有任务都依赖于上游任务;例如,Clean A使用Load A的输出作为输入。因此,当从命令行执行管道时,我们的框架会自动注入一个新的单元。因此,Clean A任务知道它的输入位置(即Load A的输出)。例如,Clean A脚本在运行时可能是这样的:

然而,当交互开发时,人们仍然希望得到注入的细胞。由于我们的 Jupyter 集成,当用户打开一个文件时,细胞注入过程自动发生,我们现在将该功能引入其他编辑器。有了我们新的集成,用户可以通过运行一个命令在他们所有的脚本中注入单元:

之后,您可以交互式地以 percent 格式运行您的.py,因为它现在包含了相应输入的路径——不需要硬编码!下面是注入单元格并以百分比格式运行.py后的 VSCode 截图:

运行 Ploomber 管道的 VSCode。代码在左边,结果在右边。图片作者。

如果您想了解更多信息,请查看用户指南。

开始

从几个简单的步骤开始:

现在打开fit.py,开始交互式运行它!

结束语

百分比格式是交互进行数据分析的好方法,并且保留了使用.py文件的好处(比如运行git diff)。此外,通过在您的工作流中采用 Ploomber,您现在可以将您的分析逻辑分解成多个步骤,以产生可维护的工作并增加您的团队的协作!

通过这种新的集成,我们希望支持来自其他编辑器和 ide 的用户,因此每个团队成员都可以使用他们最喜欢的任何工具,并顺利协作;如果您有任何问题或建议,请加入我们的社区!

最初发布于ploomber . io

如何用 Python 中的 Matplotlib 绘制对数轴

原文:https://towardsdatascience.com/plot-logarithmic-axes-matplotlib-python-bb8533f430c0

了解何时以及如何在对数标度上可视化数据

罗伯特·索林在 Unsplash 上的照片

介绍

当可视化数据时,确保数据点以一种我们试图告诉读者的“故事”足够清晰的方式绘制在数字或图表中是很重要的。这意味着根据数据的性质,我们需要选择最合适的参数。在这种情况下,最重要的一个方面是扩展。

在今天的文章中,我们将讨论在对数标度上可视化数据的几个原因。此外,我们将展示如何使用 Python 和matplotlib包绘制带有对数轴的图形,并了解使用哪种方法取决于您是使用 Pyplot 还是面向对象的接口。

回到学校——对数

首先,让我们回忆一下,确保我们都明白对数是什么意思。对数是表示指数方程的另一种方式。例如,等式 2⁴ = 16 也可以表示为

来源:作者

对数是指数(即上面例子中的4),等式意味着当基数2被提升到指数4时,结果将等于16

你什么时候会使用对数标度

第一个——可能也是最明显的——你应该考虑在对数尺度上可视化的原因与数据点的范围有关。对数标度允许显示大范围的数据点,而不会在图形的两端压缩非常小和/或大的值。例如,让我们考虑这样一个场景,我们想要为特定银行的客户绘制可用余额。显然,我们可能有一些拥有数百万甚至数十亿资产的客户,但我们大多数客户群的可用资金会少得多。因此,对数标度肯定会帮助我们处理向更大值的偏斜。

时间序列也是另一种情况,对数标度可以帮助我们以更直观的方式解释数据,因为对数标度可以帮助我们更准确地可视化随时间的分数变化。例如,考虑一段时间内股票价格的可视化。股票价格从 10 美元涨到 15 美元和从 20 美元涨到 25 美元将通过线性标度上的相同向上移动来绘制,因为在这两种情况下,涨幅都等于 5 美元。然而,在对数标度上,从 10 美元到 15 美元的第一次变化代表股票价格上涨 50%,而从 20 美元到 25 美元的第二次变动对应于价格上涨 25%,因此对数标度将能够更清楚地显示这一细节(事实上,第一次价格变动的涨幅/幅度将是第二次价格变动的两倍)。

最后,您可能考虑使用对数标度的另一个原因是当数据点更自然地以几何形式表示时

用 Python 中的 matplotlib 绘制对数标度的图形

现在让我们看看如何使用 Python 中的matplotlib包在对数标度上绘制图形。

如果您正在使用matplotlib中的面向对象接口,您可以使用[matplotlib.axes.Axes.set_xscale('log')](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_xscale.html)[matplotlib.axes.Axes.set_yscale('log')](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_yscale.html)分别用于 X 轴或 Y 轴。

import matplotlib.pyplot as plt data = [pow(10, i) for i in range(10)]fig, ax = plt.subplots()
ax.plot(data)
**ax.set_yscale('log')**
plt.show()

结果图的 Y 轴将以对数标度显示,如下图所示

对数标度上 Y 轴的示例图(使用面向对象的界面)—来源:作者

另一方面,如果您正在使用 Pyplot 接口,您可以使用[matplotlib.pyplot.xscale('log')](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.xscale.html)[matplotlib.pyplot.yscale('log')](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.yscale.html)分别用于 X 轴和 Y 轴。

import matplotlib.pyplot as plt data = [pow(10, i) for i in range(10)]plt.plot(data)
**plt.yscale('log')**
plt.show()

下面显示了将呈现在屏幕上的结果输出;

对数标度上 Y 轴的示例图(使用 Pyplot 界面)—来源:作者

在线性标度上(即不使用plt.yscale('log')ax.set_yscale('log'),数据将显示如下:

同样的情节,线性比例——来源:作者

最后的想法

在今天的文章中,我们讨论了对数,以及当我们需要处理向小值或大值倾斜的数据时,需要以对数标度绘制图形。对数标度有用的另一个场景是时间序列表示(例如当可视化股票价格运动时)。最后,我们展示了如何使用面向对象和 Pyplot 接口在 Python 和matplotlib中绘制对数标度的图形。

成为会员 阅读媒体上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

你可能也会喜欢

使用 Python 在同一个图形中绘制多个图形

原文:https://towardsdatascience.com/plot-multiple-graphics-in-the-same-figure-using-python-1cbe5a23e89f

了解如何在同一个 matplotlib 图形中绘制多个图形

Pawel Czerwinski 在 Unsplash 上的照片

如果你使用 Jupyter 笔记本,你会知道它的右侧在图形方面变得非常少。发生这种情况是因为你通常创建一个单元格的图形,它不会超过你的屏幕的一半。

然而,有时需要并排绘制两个图形,这不仅是为了更好地利用空间,而且主要是因为我们作为数据科学家,经常需要比较图形。

假设您正在分析一家出租车公司的行程分布,并且您想知道在曼哈顿和布鲁克林接送乘客的行程之间的距离差异。

让我们加载数据集。我将使用来自名为 *Taxis 的seaborn包中的一个样本数据集。*导入 seaborn 后即可加载。

import seaborn as sns# Load dataset
df = sns.load_dataset('taxis')

Seaborn 的出租车数据集。图片由作者提供。

那我们就把从曼哈顿和布鲁克林开始的旅行分离出来。

# Brooklyn travels
df_bklyn = df.query(' pickup_borough == "Brooklyn" ')# Manhattan Travels
df_mhtn = df.query(' pickup_borough == "Manhattan" ')

Seaborn 的多个地块

现在是时候看看奇迹是如何发生的了。首先,我们创建一个图形,并指出我们想要的行数和列数(在下面的例子中,同一行有两个图)。

然后,我们应该创建我们的图,并使用切片符号指示这些图在网格中的位置。如果只有一行,那么只需要一对方括号(my_grid[0])。当您有多行和多列时,使用两对方括号(my_grid[0][0]表示绘制在第一行第一列)。

# Creating a grid figure with matplotlib
fig, my_grid = plt.subplots(nrows=1, ncols=2, figsize=(18,8))# Histograms# Plot 1
g1 = sns.histplot(data=df_bklyn, x='distance', ax=my_grid[0])# Title of the Plot 1
g1.set_title('Histogram of the travels beginning in Brooklyn') # Plot 2
g2 = sns.histplot(data=df_mhtn, x='distance', ax=my_grid[1])# Title of the Plot 2
g2.set_title('Histogram of the travels beginning in Manhattan');

同一行上的两个图。图片由作者提供。

使用 Matplotlib 的多个图

带有matplotlib的多重情节非常相似,但是让我们看看编码时的细微差别。

你会注意到,当我们创建网格时,我们必须使用元组和列表。如果我们只有一行,你可以只用一个元组。

# Creating a grid figure with matplotlib SINGLE ROW EXAMPLE
fig, (g1, g2, g3) = plt.subplots(nrows=1, ncols=3, figsize=(18,8))

但是如果您有多行,请使用元组列表,每行一个元组。

# Creating a grid figure with matplotlib MULTIPLE ROWS EXAMPLE
fig, [(g1, g2, g3), (g4, g5, g6)] = plt.subplots(nrows=2, ncols=3, figsize=(18,8))

让我们来看一个纽约市 4 个不同街区距离直方图的实例。注意,这里我们不再使用ax参数。

# Creating a grid figure with matplotlib
fig, [(g1, g2), (g3, g4)] = plt.subplots(nrows=2, ncols=2, figsize=(18,8))**# Histograms****# Plot 1**
g1.hist(data=df_bklyn, x='distance')# Title of the Plot 1
g1.set_title('Histogram of the travels beginning in Brooklyn')**# Plot 2**
g2.hist(data=df_mhtn, x='distance')# Title of the Plot 2
g2.set_title('Histogram of the travels beginning in Manhattan')**# Plot 3**
g3.hist(data=df_queens, x='distance')# Title of the Plot 3
g3.set_title('Histogram of the travels beginning in Queens')**# Plot 4**
g4.hist(data=df_bronx, x='distance')# Title of the Plot 4
g4.set_title('Histogram of the travels beginning in Bronx');

滑行距离的 2x2 多幅图。图片由作者提供。

在你走之前

快速发布,但非常有用。我记得我在笔记本上记下了这一点,并经常回来提醒我该怎么做。

也就是说,我希望这篇文章起到同样的作用,帮助初学者轻松地在同一个图形中绘制多个图形。

记得关注我了解更多。

https://gustavorsantos.medium.com/

如果你想订阅 Medium,这是我的推荐链接。

https://gustavorsantos.medium.com/membership

Plotly 符合科学可视化

原文:https://towardsdatascience.com/plotly-meets-scientific-visualization-8c2074f032cb

虽然静态情节是科学展示的默认选项,但交互性也有它自己的力量

西格蒙德在 Unsplash 上拍照

虽然交互式图形已经广泛应用于金融等领域,但静态图形仍然是科学可视化的主流。原因很简单,科学强调的是真理,而不是表象。除此之外,传统的科学出版物和演示文稿依赖于印刷文本,这使得交互性似乎没有那么有用。

然而,我最近发现,当我制作模型原型,只是想知道我的结果会是什么样子时,交互式绘图会非常方便。在某些情况下,交互式绘图可以节省时间,因为它允许我一次浏览“成千上万”的静态绘图。

如果你以前读过我以前的教程(为科学出版物制作静态图),你可能知道我总是使用虚拟的例子来帮助你理解底层的编程原理,这允许你完全知道如何使用一个包,而不是仅仅通过提供几行代码让你复制和粘贴来触及表面。

并不是每一个交互图在科学可视化中都有用,我发现最有用的是散点图。最好的说明方法是画一个网络(图),因为它由线和点组成。今天让我来引导您了解两个流行的 Python 包— networkxplotly。前者用于表示图形,后者用于生成交互式图形。

代码可从以下位置获得:https://github . com/frankligy/python _ visualization _ tutorial/blob/main/plotly/plotly . py

如何表示一个网络(图)?

网络(图)是节点和边的集合,每个节点和边可以有关联的属性。表示它们的两种流行方式是(a)边列表,(b)邻接矩阵。

表示网络的边列表或邻接矩阵(图片由作者提供)

如您所见,它们都可以被视为一个数据框。所以我们首先创建两个熊猫数据框,我们将把它们转换成networkx图形对象。

import networkx as nx
import pandas as pd

我希望代码本身是可自我解释的,我们用from_pandas_edgelist如果你想把边列表转换成G,用from_pandas_adjacency转换邻接矩阵。这里最后一个参数create_using指定了我们想要构建什么类型的图。networkx有以下几种类型:

  1. nx.Graph:节点间的单条边,无向
  2. nx.DiGraph:节点间的单边,有向
  3. nx.MultiGraph:节点间的多条边,无向
  4. nx.MultiDiGraph:节点间的多条边,有向

我们从最简单的nx.Graph开始。

现在让我们以G1为例,检查一下图表的结构:

## For nodes
type(G1.nodes())
# networkx.classes.reportviews.NodeView
list(G1.nodes())
# ['node1', 'node2', 'node3']
type(G1.nodes(data=True))
# networkx.classes.reportviews.NodeDataView
list(G1.nodes(data=True))
# [('node1', {}), ('node2', {}), ('node3', {})]
G1.nodes['node1']
# {}

对于节点,使用nodes方法访问所有信息。请记住data参数将决定节点属性(作为 python 字典)是否显示(结果将是普通列表或嵌套列表)。这些节点可以被视为一个列表,您可以对其进行循环,也可以被视为一个可映射的对象,您可以使用节点名称来访问其关联的属性。

## For edges
type(G1.edges())
# networkx.classes.reportviews.EdgeView
list(G1.edges())
# [('node1', 'node2'), ('node1', 'node3'), ('node2', 'node3')]
type(G1.edges(data=True))
# networkx.classes.reportviews.EdgeDataView
list(G1.edges(data=True))
# [('node1', 'node2', {'weight': 4.5}),
 ('node1', 'node3', {'weight': 4.5}),
 ('node2', 'node3', {'weight': 4.5})]
G1.edges[('node1','node2')]
# {'weight': 4.5}

对于边,同样,data参数决定是否显示边的属性。边也有双重性,你可以用一个元组像字典一样访问,或者用一个列表迭代。

最后,作为networkx中最后一个重要的基本操作是如何将setget的节点和边属性化。由于该属性只是一个字典,因此设置就像创建一个字典并将其分配给 graph 对象一样简单:

## node
node_color_dict = {'node1':'green','node2':'blue','node3':'red'}
nx.set_node_attributes(G1,node_color_dict,'color')
list(G1.nodes(data=True))
# [('node1', {'color': 'green'}),
 ('node2', {'color': 'blue'}),
 ('node3', {'color': 'red'})]## edge 
nx.get_edge_attributes(G1,'weight')
# {('node1', 'node2'): 4.5, ('node1', 'node3'): 4.5, ('node2', 'node3'): 4.5}

networkx包中当然有许多更有用的功能(见 API ),但是底层的数据结构与我们在这里展示的是一样的。有了这些信息,我们可以前往Plotly包。

Plotly:三层抽象

虽然有无数的 Plotly 教程,但我发现没有一个真正适合我,因为它们没有真正说明原理(即底层数据对象),而是触及了表面(即如何制作条形图?).科学可视化的一个关键特征是理解可视化包的来龙去脉,以便我们可以随心所欲地操纵它。所以这将是我在这篇文章中的主要关注点。

总而言之,Plotly包可以用于三个不同的级别(从高级或最抽象到低级或最不抽象):

  1. 这是一个高级 API,允许在一行代码中制作散点图、条形图等图形。然而,我发现它没有那么灵活,尽管这是 Plotly 开发者推荐的方式。
  2. plotly.graph_objects:这是一个中级 API,为各种剧情提供了更通用的抽象,这里我们会遇到tracefigurelayout这样的概念。我发现这一关是科学剧情生成的绝佳界面。
  3. dictionary is everything:这是底层 API,前两层最终会转换成字典,然后转向 JSON,因为Plotly使用 javascript 进行最终可视化。虽然这个级别提供了最大的灵活性,我们当然可以在需要时使用它,但它会使您的代码非常笨拙。此外,调试会更加困难,因为他们不希望用户经常使用这一层。

因此,我们将演示如何使用来自graph_object层的Plotly

使用 Plotly 的最佳实践

关于plotly有一点让人不知所措,那就是做同一件事往往有多种方式。对于初学者来说,这可能会造成困难,而不是提供方便。这最终将取决于个人的编程偏好,我们可以选择一个最适合我们的。在这里,我要和你分享我的最佳实践,那就是把一个情节分解成几个部分。

网络图可以分为以下几个部分:

  1. 节点
  2. 优势
  3. 绘图布局(x 轴、y 轴、标题、图例等)

Plotly图形对象层为顺序创建上述图形元素提供了极大的灵活性。首先,由于我们要将网络显示到 2D 空间中,我们需要计算每个节点的布局(每个节点在 2D 空间中的坐标)。

import plotly.graph_objects as go
import networkx as nx## step1: compute the graph layout (coordinates of nodes in 2D)
coords_dict = nx.spring_layout(G1,seed=42)# {'node1': array([-0.33486468,  0.96136632]),
 'node2': array([ 1\.        , -0.19068184]),
 'node3': array([-0.66513532, -0.77068448])}## step2: set the dictionary as the attribute of the node
nx.set_node_attributes(G1,coords_dict,'coord')## step3: validate
G1.nodes['node1']#{'color': 'green', 'coord': array([-0.33486468,  0.96136632])}

我们首先绘制节点,在plotly中我们称之为节点或边trace

node_x = []   # store x coordinates
node_y = []   # store y coordinates
node_text = [] # store text when mouse hovers over the node
for node,node_attr_dict in G1.nodes(data=True):  # recall anatomy 
    x,y = node_attr_dict['coord']
    node_x.append(x)
    node_y.append(y)
    node_text.append(node)
node_trace = go.Scatter(name='nodes',x=node_x,y=node_y,mode='markers',hoverinfo='text',text=node_text,marker={'color':'green','size':5})

让我们试着反向理解代码,为了绘制节点的轨迹,我们实例化了一个go.Scatter对象。思路是,先想好要画哪个元素(trace,或者 layout,或者其他),在plotly中实例化哪个 graph 对象,然后引用 API 引用检查可用参数。一个单独的教程永远无法涵盖所有的提示,但我相信这是你未来所有任务最可持续的方式。

正如参考文献所说,我们给这个轨迹起了一个名字,这个名字将有助于自动填充图例标签。我们需要一个存储 x 坐标的列表,对于 y 坐标也是如此。我们选择mode作为markers,因为我们正在画点,而不是线。text参数是一个当鼠标悬停在点上时可以显示的列表,这是通过设置hoverinfo='text'将这两个链接起来实现的。现在,对于点的属性,我们可以提供一个字典,因为 Plotly 底层的所有东西都是字典或一个go.scatter.marker对象,我们将在绘制边缘时演示后者。这两种手段完全一样。

然后我们画出边缘,

edge_x = []
edge_y = []
for edge_end1,edge_end2,edge_attr_dict in G1.edges(data=True):
    x0,y0 = G1.nodes[edge_end1]['coord']
    x1,y1 = G1.nodes[edge_end2]['coord']
    x2,y2 = None,None
    for x,y in zip([x0,x1,x2],[y0,y1,y2]):
        edge_x.append(x)
        edge_y.append(y)
edge_trace = go.Scatter(name='lines',x=edge_x,y=edge_y,mode='lines',line=go.scatter.Line(color='black',width=2))

通过利用我们在networkx部分学到的知识,理解我们如何提取我们需要的信息,我们循环遍历边,返回位于边两端的节点名称。然后,可以使用节点名作为关键字来找出坐标信息。作为一种特质,我们添加了[None,None]作为分隔符,告诉 Plotly 在节点之间留一个间隙。同样,我们实例化了go.Scatter对象,这里line参数作为go.scatter.Line对象传递,你也可以像line=dict('color':'black','width':2).一样使用原始字典

接下来,我们绘制布局,

fig_layout = go.Layout(showlegend=True,title='network',xaxis=dict(title_text='coordinate x'))

布局包含图形解剖像图例,标题,以及如何装饰 x 轴和 y 轴。同样,我们应该参考 API 参考来理解参数的用法。

最后,我们可以把它们组合成一个图形,

fig = go.Figure(data=[node_trace,edge_trace],layout=fig_layout)

我们可以将它导出为一个 HTML 页面,include_plotlyjs决定 javascript 脚本需要如何继续执行。由于 Plotly 依赖于 javascript,我们可以将 javascript 代码放到输出中,这会大大增加 HTML 文件的大小。或者,我们可以使用cdn来指示 web 浏览器中的引擎查看存储在某处的 javascript 代码的副本,这样它们就不需要包含在您生成的每个 HTML 文件中。

 fig.write_html('./network.html',include_plotlyjs='cdn')

现在我们来看剧情:

互动网络图(图片由作者提供)

我刚刚将这个 HTML 页面部署到我的 GitHub 页面,你可以做同样的事情来与他人分享你的交互图。这非常简单,只需将 HTML 文件上传到你的 GitHub 页面,进入你的设置,并启用 GitHub 页面,然后你可以通过一个唯一的 URL 导航到你的 HTML。要进入这个互动的情节,让我们来这里吧!对我来说,最重要的功能是(a)将鼠标悬停在节点上,(b)放大和缩小图形,(c)平移(向右或向左移动图形),这些都可以通过右上角的标签访问。

代码可在:https://github . com/frank ligy/python _ visualization _ tutorial/blob/main/plotly/plotly . py获得

结论

我个人认为散点图是调试和原型制作时最方便的科学可视化工具。理解如何制作一个静态的剧情仍然是非常重要的,但是既然我们已经有了像Plotly这样简单易用的工具,为什么不花几分钟时间掌握它呢?

差不多就是这样!我希望你觉得这篇文章有趣和有用,感谢阅读!如果你喜欢这篇文章,请在 medium 上关注我,非常感谢你的支持。在我的 Twitter 或 LinkedIn 上联系我,也请让我知道你是否有任何问题或你希望在未来看到什么样的教程!

Plotly 带有 Streamlit、Dash 或 Flask

原文:https://towardsdatascience.com/plotly-with-streamlit-dash-or-flask-4d78fa025ea2

数据可视化

哪种框架最适合您的数据科学应用

作者图片

如果我说你可以简单地通过交换你使用的框架来减少一半的应用程序,你会怎么说?

我通过在 Dash、Flask 和 Streamlit 中编写相同的简单应用程序进行了一个快速实验,结果很有启发性。

破折号

该应用程序复制自 Dash 文档,代码如下:

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import pandas as pdapp = dash.Dash(__name__)df = pd.DataFrame({
   'Fruit': ['Apples', 'Oranges', 'Bananas', 'Apples', 'Oranges', 'Bananas'],
   'Amount': [4, 1, 2, 2, 4, 5],
   'City': ['SF', 'SF', 'SF', 'Montreal', 'Montreal', 'Montreal']
})fig = px.bar(df, x='Fruit', y='Amount', color='City',  
   barmode='group')app.layout = html.Div(children=[
   html.H1(children='Hello Dash'),
   html.Div(children='''
   Dash: A web application framework for Python.
   '''),
   dcc.Graph(
      id='example-graph',
      figure=fig
   )
])if __name__ == '__main__':
   app.run_server(debug=True)

代码转载由 Plotly 提供—版权所有 2019 Plotly, 麻省理工学院许可

这并不难做到:首先我们导入库,然后设置 Flask 应用程序,接下来我们定义 dataframe,接下来是实际绘制图形的线条。app.layout部分本质上是 Dash 内部编写的 HTML,最后一行运行应用程序。

它会生成一个如下所示的网页:

作者图片

由于 Dash 基本上是一个使用 Plotly 的 Flask app,所以我编写了一个用 Flask 和 Plotly 构造的等价 app(参见我的文章Web Visualization with Plotly 和 Flask ) ,并将其与 Dash 版本进行了比较。

我个人的观点是我的 Flask/Plotly 应用程序更容易编写,也更灵活(你的观点可能不同)。

一个简单的烧瓶+ Plotly 应用程序

这个应用程序有两部分:Flask 应用程序本身和一个 HTML 模板。Flask 应用程序的工作类似于 Dash 应用程序,但不构建实际的网页。网页是一个 HTML 模板,我们将 Plotly 数据从 Flask 应用程序传递给它,这样它就可以显示图表。

烧瓶部分具有与 Dash 相似的结构:

from flask import Flask, render_template
import pandas as pd
import json
import plotly
import plotly.express as pxapp = Flask(__name__)**@**app.route('/')
def notdash():
   df = pd.DataFrame({
      'Fruit': ['Apples', 'Oranges', 'Bananas', 'Apples', 'Oranges', 
      'Bananas'],
      'Amount': [4, 1, 2, 2, 4, 5],
      'City': ['SF', 'SF', 'SF', 'Montreal', 'Montreal', 'Montreal']
   })fig = px.bar(df, x='Fruit', y='Amount', color='City', 
      barmode='group')graphJSON = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)return render_template('notdash.html', graphJSON=graphJSON)

模板是将要显示的网页。它相当于 Dash 应用程序的布局,但我们将它写在一个 HTML 文件中。

<!doctype *html*>
<html>
 <body>
  <h1>Hello Plotly (but not Dash)</h1>
  <div *id*='chart' *class*='chart'”></div>
</body><script *src*='https://cdn.plot.ly/plotly-latest.min.js'></script>
<script *type*='text/javascript'>
  var graphs = {{graphJSON | safe}};
  Plotly.plot('chart',graphs,{});
</script></html>

你可以在原文章中找到 app 的描述。

它看起来是这样的:

作者图片

和原版非常相似。

流线型+plottly

自从我写了那篇文章之后,我发现了 Streamlit。Streamlit 是一个非常易于使用的框架,允许您编写包含图表、数据框架和一些非常易于使用的 UI 元素(如选择框、按钮、文本字段等)的数据科学应用程序。

而且是 100% Python。

以下是用 Streamlit 编写的等效应用程序:

import pandas as pd
import streamlit as st
import plotly.express as pxdf = pd.DataFrame({
    "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges",   
    "Bananas"],
    "Amount": [4, 1, 2, 2, 4, 5],
    "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"]
    })fig = px.bar(df, x="Fruit", y="Amount", color="City",
                 barmode="group")st.title('Hello Streamlit')
st.write('''
    Streamlit: A web application framework for Python.
''')
st.plotly_chart(fig)

你应该注意到的是绝对的简单。除了最后三行之外,代码与其他应用程序非常相似——它必须是用于声明熊猫数据帧和绘制 Plotly 图形的 Python 代码。

但是应用程序的其余部分,渲染网页的部分只有三行代码!

它看起来确实与其他应用程序略有不同,这是因为 Streamlit 在默认情况下会给你的应用程序添加一个视图。

作者图片

不得不说,Streamlit 在网页设计方面更具限制性,因为它不允许你像 Dash 或 Flask 应用程序那样拥有 HTML 的全部功能。但是有一些用 Streamlit 制作的非常好看的应用程序,这个非常简单的应用程序的代码是其他版本的一半大小。这可能不是所有情况下的解决方案,但肯定值得考虑。

一如既往,感谢阅读。这是对 Streamlit 在创建数据科学应用程序方面的优势的简单介绍,您可以在我的 Github repo 中找到代码。

您可以在我的网站上找到关于 Streamlit 和其他主题的其他文章:

https://alanjones2.github.io/

使用 SQL 用户定义函数在 BigQuery 中绘制条形图(UDF)

原文:https://towardsdatascience.com/plotting-bar-charts-in-bigquery-using-a-sql-user-defined-function-udf-ca6056aac680

最大限度地减少上下文切换,加快工作流程

在 Unsplash 上由 Chase Clark 拍摄的照片

有许多令人敬畏的新工具旨在为 SQL 工作流带来更多的交互性、可视化、协作和共享: HEX 、 Count 和 Hyperquery 是三个很好的例子。

但有时你只想专注于在一个基本的控制台上工作,用一些额外的功能来帮助加快你的洞察力和工作流程。我喜欢将 BigQuery 查询编辑器想象成类似于命令行记事本——您可以使用数据定义语言( DDL )使用类似命令行的功能,但是比命令行更加灵活。

本文将解释如何使用纯 SQL 构建一个条形图,然后将它打包成一个简单的函数,您可以在数据探索中使用它。我们将通过将函数构建为简单的脚本查询来开始这个过程,并一步一步地添加到查询中,直到我们有一个工作的、有用的函数。

输入

我们要做的第一件事是考虑我们想要传递给函数的输入,因为这决定了它将如何工作。SQL 中的脚本和函数开发可能有点不直观,所以最好的方法是从最简单的版本开始,然后根据需要变得更复杂。

为此,我希望我的函数接受一个数值,并返回字符串中的确切字符数。我不想限制太多,所以我将接受整数浮点数,在这个简单的例子中,我想将它们限制为正数,这样我就不需要处理移动轴或其他复杂的东西。

因此,我将这些输入声明为变量,需要在任何 BigQuery 脚本的开头声明。

DECLARE input_value FLOAT64;
DECLARE character STRING;

事实上,因为我在测试这个函数,所以我要用一些测试值来初始化它们。这意味着对于我的函数的输出,我想要一个包含 100 个竖线的字符串。

DECLARE input_value FLOAT64 DEFAULT 100;
DECLARE character STRING DEFAULT "|";

现在,从哪里开始?

方法

首先,我想强调几件事:

  • 您可以在用户定义的函数(UDF)中编写 SQL,只要它返回标量值(即它不能返回许多行和列,它需要是单个值)。实际上,您可以通过使用结构和数组聚合来解决这个问题,但是在这种情况下,我们并不要求这样做。
  • 您应该使用公共表表达式(cte)来编写 SQL,因为它使您的代码更具可读性,更易于理解、调试和扩展。好的代码应该清晰地传达逻辑,不要让读者感到困惑。
  • 您可以在 SQL 脚本中使用可以在查询中引用的变量,但是在编写本文时,您不能将其保存为视图。但是,您可以在 UDF 中使用变量,这些变量将作为参数传递给函数。

还要注意,所有代码块都可以在您的控制台中复制和执行。

构建查询

我要做的第一件事是使用 ABS 函数,确保负输入将被转换为正输入,并且不会产生错误。将来我可能想做一些更聪明的事情,但是让我们从简单的开始。我将通过将默认的输入值更改为-100 来测试它的工作情况:

DECLARE input_value FLOAT64 DEFAULT -100;
DECLARE character STRING DEFAULT "|";WITH 
**convert_input_to_positive AS (
SELECT ABS(input_value)
)**SELECT *
FROM convert_input_to_positive

太好了,这个管用。现在我想创建 100 个字符,我最终可以将它们构建成一个字符串。我不能在 UDF 中使用像循环这样的控制结构,但是我可以使用数组,这可能有点难以理解,但是非常强大。为了从输入值生成一个数组,我将使用 GENERATE_ARRAY 函数,在 1 和我的输入值之间,步长为 1:

DECLARE input_value FLOAT64 DEFAULT 100;
DECLARE character STRING DEFAULT "|";WITH 
convert_input_to_positive AS (
SELECT ABS(input_value) AS abs_input_value
),**generate_numbers_array AS (
SELECT 
GENERATE_ARRAY(1, abs_input_value, 1) AS numbers_array
FROM convert_input_to_positive
)**SELECT *
FROM generate_numbers_array

太棒了,我现在有一个 100 项的数组(包含升序数字),但是由于我想将每个数组元素转换为一个字符(最终),我需要访问每个元素,这意味着我需要取消数组嵌套。

UNNEST 有一点奇怪的语法,需要一点时间来适应,但是在处理嵌套结构时,这是一个基本的模式。这些在 Google Analytics 和 Firebase 数据中非常常见,因此值得考虑一下,尤其是在处理这些类型的数据源时。

DECLARE input_value FLOAT64 DEFAULT 100;
DECLARE character STRING DEFAULT "|";WITH 
convert_input_to_positive AS (
SELECT ABS(input_value) AS abs_input_value
),generate_numbers_array AS (
SELECT 
GENERATE_ARRAY(1, abs_input_value, 1) AS numbers_array
FROM convert_input_to_positive
),**unnest_numbers_array AS (
SELECT numbers
FROM generate_numbers_array 
CROSS JOIN UNNEST(numbers_array) AS numbers
)**SELECT * 
FROM unnest_numbers_array

您还可以用左连接替换交叉连接(如果数据中可能有空值,这是更好的选择),或者您也可以使用逗号作为隐式交叉连接,但我认为这可能会造成混乱,所以我更喜欢显式连接。

前面的结果在一行中给出了一个数组,但是现在我有 100 行,所以我可以直接访问它们,并把它们转换成我需要的输出格式。可能有一百万种不同的方法可以做到这一点,但是因为我想要做的是将所有的行更改为相同的值,所以我将从使用非常方便的 LEAST 函数开始。这将返回较低的值(人们通常认为 MIN 会这样做,但这并不是这样,因为它是一个聚合函数。这是一种奇特的说法,它跨,而不是列进行操作。

DECLARE input_value FLOAT64 DEFAULT 100;
DECLARE character STRING DEFAULT "|";WITH 
convert_input_to_positive AS (
SELECT ABS(input_value) AS abs_input_value
),generate_numbers_array AS (
SELECT 
GENERATE_ARRAY(1, abs_input_value, 1) AS numbers_array
FROM convert_input_to_positive
),unnest_numbers_array AS (
SELECT numbers
FROM generate_numbers_array 
CROSS JOIN UNNEST(numbers_array) AS numbers
),**set_all_numbers_to_ones AS (
SELECT
LEAST(1, numbers) AS ones
FROM unnest_numbers_array
)**SELECT *
FROM set_all_numbers_to_ones

下一步是必需的,因为数组由 FLOAT64 数据类型组成,我想在下一步中用字符串替换它们。这意味着我需要转换值,但是我使用 SAFE_CAST 是因为这是一个好习惯。如果 CAST 遇到一个无法转换为目标类型的值,查询将失败,但是 SAFE_CAST 将简单地返回 NULL,查询将成功。

在这种情况下不是绝对必要的,但仍然是一个好习惯。该查询的输出将是 100 行,每行包含数字 1 的字符串表示。

DECLARE input_value FLOAT64 DEFAULT 100;
DECLARE character STRING DEFAULT "|";WITH 
convert_input_to_positive AS (
SELECT ABS(input_value) AS abs_input_value
),generate_numbers_array AS (
SELECT 
GENERATE_ARRAY(1, abs_input_value, 1) AS numbers_array
FROM convert_input_to_positive
),unnest_numbers_array AS (
SELECT numbers
FROM generate_numbers_array 
CROSS JOIN UNNEST(numbers_array) AS numbers
),set_all_numbers_to_ones AS (
SELECT
LEAST(1, numbers) AS ones
FROM unnest_numbers_array
),**cast_all_numbers_to_strings AS (
SELECT 
SAFE_CAST(ones AS STRING) AS ones_strings
FROM set_all_numbers_to_ones
)**SELECT *
FROM cast_all_numbers_to_strings

现在,我可以使用一个简单的 CASE 语句,用我的变量定义的字符替换字符串,我们就快完成了:

DECLARE input_value FLOAT64 DEFAULT 100;
DECLARE character STRING DEFAULT "|";WITH 
convert_input_to_positive AS (
SELECT ABS(input_value) AS abs_input_value
),generate_numbers_array AS (
SELECT 
GENERATE_ARRAY(1, abs_input_value, 1) AS numbers_array
FROM convert_input_to_positive
),unnest_numbers_array AS (
SELECT numbers
FROM generate_numbers_array 
CROSS JOIN UNNEST(numbers_array) AS numbers
),set_all_numbers_to_ones AS (
SELECT
LEAST(1, numbers) AS ones
FROM unnest_numbers_array
),cast_all_numbers_to_strings AS (
SELECT 
SAFE_CAST(ones AS STRING) AS ones_strings
FROM set_all_numbers_to_ones
),**replace_ones_with_specified_character AS (
SELECT 
CASE WHEN ones_strings = '1' THEN character END AS characters
FROM cast_all_numbers_to_strings
)**SELECT *
FROM replace_ones_with_specified_character

最后一个技巧非常有用,尤其是如果您正在构建从各种INFORMATION _ SCHEMAbig query 选项编写 SQL 的函数。本质上,您要将一个特定列中的所有值聚合到一个数组中,然后从所有数组元素创建一个字符串,但在一个空字符串上分隔。这将具有将一行中的值转换成水平连接的字符串的效果,这正是我们在这里试图实现的。

DECLARE input_value FLOAT64 DEFAULT 100;
DECLARE character STRING DEFAULT "|";WITH 
convert_input_to_positive AS (
SELECT ABS(input_value) AS abs_input_value
),generate_numbers_array AS (
SELECT 
GENERATE_ARRAY(1, abs_input_value, 1) AS numbers_array
FROM convert_input_to_positive
),unnest_numbers_array AS (
SELECT numbers
FROM generate_numbers_array 
CROSS JOIN UNNEST(numbers_array) AS numbers
),set_all_numbers_to_ones AS (
SELECT
LEAST(1, numbers) AS ones
FROM unnest_numbers_array
),cast_all_numbers_to_strings AS (
SELECT 
SAFE_CAST(ones AS STRING) AS ones_strings
FROM set_all_numbers_to_ones
),replace_ones_with_specified_character AS (
SELECT 
CASE WHEN ones_strings = '1' THEN character END AS characters
FROM cast_all_numbers_to_strings
),**aggregate_characters_into_array AS (
SELECT ARRAY_AGG(characters) AS characters_array
FROM replace_ones_with_specified_character
),****convert_array_to_string AS (
SELECT ARRAY_TO_STRING(characters_array, "") AS bar
FROM aggregate_characters_into_array
)**SELECT bar
FROM convert_array_to_string

这些通常会合并到一个步骤中,但是为了清晰和一致,我将它们分开。执行这段代码,您将看到长度为 100 的单个条被写入控制台中的查询结果。太棒了。

然而,我到底该怎么用它呢?我不想为了绘制一个单行条形图而不得不重新复制这段代码!

再走一步,我们就可以把它简化成一行代码和一个可移植的、可重用的函数。

打包成用户定义的函数(UDF)

将查询转换成 UDF 非常简单,因为我们已经定义了参数名称和数据类型,并且查询返回一个标量值。

我们只需要使用 CREATE[或 REPLACE]函数语法,定义输入参数,并确保将 SQL 查询放在双括号中,因为它是一个标量子查询:

**CREATE OR REPLACE FUNCTION flowfunctions.plot.bar(
input_value FLOAT64, character STRING
)
AS ((**WITH 
convert_input_to_positive AS (
SELECT ABS(input_value) AS abs_input_value
),generate_numbers_array AS (
SELECT 
GENERATE_ARRAY(1, abs_input_value, 1) AS numbers_array
FROM convert_input_to_positive
),unnest_numbers_array AS (
SELECT numbers
FROM generate_numbers_array 
CROSS JOIN UNNEST(numbers_array) AS numbers
),set_all_numbers_to_ones AS (
SELECT
LEAST(1, numbers) AS ones
FROM unnest_numbers_array
),cast_all_numbers_to_strings AS (
SELECT 
SAFE_CAST(ones AS STRING) AS ones_strings
FROM set_all_numbers_to_ones
),replace_ones_with_specified_character AS (
SELECT 
CASE WHEN ones_strings = '1' THEN character END AS characters
FROM cast_all_numbers_to_strings
),aggregate_characters_into_array AS (
SELECT ARRAY_AGG(characters) AS characters_array
FROM replace_ones_with_specified_character
),convert_array_to_string AS (
SELECT ARRAY_TO_STRING(characters_array, "") AS bar
FROM aggregate_characters_into_array
)SELECT bar
FROM convert_array_to_string
**))**

我们结束了。运行此语句将创建函数,可以使用典型的 SELECT 语法调用该函数,并传递之前定义为变量的参数:

SELECT flowfunctions.plot.bar(100, "|")

并且结果将与上面的完整查询相同!最好的一点是,您可以使用该函数快速直观地查看概要数据,因为将为每一行生成一个条形。请注意,这最适合 100(即百分比)以下的值,否则条形可能会超出屏幕。

试试这件,看看大小

SELECT 
animal, count,
flowfunctions.plot.bar(count, '|') As count_bar
FROM `flowfunctions.examples.zoo_animal_counts`

一个稍微琐碎的例子,但你得到了要点。

同样,如果你想制作一个由大象(或任何其他表情符号)组成的条,你可以简单地将它作为参数传递。我女儿喜欢大象。

SELECT animal, count, 
flowfunctions.plot.bar(count, emoji) As count_bar
FROM `flowfunctions.examples.zoo_animal_summary`

好的,这很酷。

这个示例数据和函数应该可供美国地区的任何经过身份验证的 BigQuery 用户使用,所以请随意尝试(或者如果您在欧盟,请用 floweu 替换 flowfunctions ),但是这个库正在积极开发中,因此可能随时发生变化。

要在您自己的项目中复制该函数,只需更新创建中的引用或将函数代码替换为您的 project_id ,确保数据集绘图存在于正确的区域中。

仅此而已。

如果您觉得这(以及其他相关材料)有用和/或有趣,请跟我来!

如果您还不是会员,请加入 Medium这个活跃、充满活力和激情的数据人社区,每月只需 5 美元,就能获得无限的故事。也有很多其他人,但是如果你对数据感兴趣,那么这里就是你要去的地方…

使用 Bokeh、Folium 和 hvPlot 在 Python 中绘制热图

原文:https://towardsdatascience.com/plotting-heat-maps-in-python-using-bokeh-folium-and-hvplot-eb7c7f49dbc6

热图是一种有用的可视化工具,Python 有几个很好的工具来制作它们

凯尔西·奈特在 Unsplash 上的照片

我一直很喜欢地图,但是,为空间数据创建引人注目的可视化效果是一项挑战。对于 Python 程序员来说,一个特别困难的领域是当我们的数据有很多点时创建地图。太多的点会很快变得不方便查看和浏览器渲染。

热图在这种情况下特别有用,因为它们可以快速给出数据的密度和空间分布,而无需离散绘制每个点。这与散点图形成对比,散点图本质上只是叠加在地图顶部的点的散点图。散点图很容易制作,但是当你有很多数据点时,由于过度绘制,它们很难查看。

在本帖中,我们将学习如何使用散景、树叶和 hvPlot 来可视化全美 180 万起野火的发生。我们将讨论每个库的优缺点,并找出在 2022 年使用哪个库最好。

我们的数据

我们正在处理的数据记录了从 1992 年到 2015 年发生在美国的超过 180 万起野火。每个火灾记录包含位置、最终火灾规模、发现日期等信息。这是一个有趣的数据集,足够大,可以展示可视化大数据集的一些挑战,而不会太大,让大多数笔记本电脑无法使用。

让我们将 wildfire 数据从 SQLite 数据库快速导入到 Pandas 数据框架中。

由作者创建

数据聚合

有许多方法可以聚集数据,以便我们可以更有效地将其可视化;一些图书馆甚至可以为我们处理所有这些。首先,让我们试着将发生在彼此附近的火灾进行分组。

我们可以通过截断纬度和经度值,创建一个合并截断的纬度-经度值的新列,然后按照新列对数据帧进行分组来实现这一点。一旦你看到代码就会明白:

由作者创建

我们现在有 64,250 组火灾,而不是 180 万个单独的火灾。这有助于我们减少要渲染的数据点的数量,现在我们可以聚合信息,如火灾计数或每个组中的平均火灾大小。

由作者创建

这是一种非常简单的将我们的火聚集成组的方法,但我认为它有助于展示使用更大的空间数据集的一些挑战。在保留信息的同时减少视觉混乱(这会使地图难以阅读)是一种平衡。

散景

**注:**互动剧情可以在这个 直播笔记本 上找到。

Bokeh 是一个很棒的 Python 绘图库,可以很好地制作可以在线共享的绘图。散景的优势之一是它的交互性很强,允许缩放、平移和搜索特定坐标。我们可以使用聚合数据集创建散点图,其功能类似于热图:

由作者创建

这还不算太糟!我让第一个图保持简单,以强调 Colorcet 的冷色图。此外,由于野火已经在美国各地发生,我们不一定需要一张底图来理解我们正在看的东西。

当然,这个图在它实际能够教给我们的方面是非常有限的,而且有更好的方法来制作这种热图,而不是绘制 64,000 个离散的点——即使 64,000 个点少于 1,800,000 个点。我们很可能画得太多了,所以我们失去了细节,因为有这么多的点放在彼此的顶部。它看起来很酷,但我们将在以后看到更多有用的情节。

让我们制作第二个图,因为我们已经准备好了数据:

由作者创建

虽然这个图非常相似,但比较这两个图确实有助于我们理解美国西部的火灾往往更大,尽管它们发生的频率更低。

薄层

follow 是优秀的 JavaScript 库 fleet . js 的 Python 包装器。虽然它可能受到包装器库的一些限制——比直接使用 fleet 更少的定制——但 follow 仍然将 fleet . js 的许多优势带入了 Python 生态系统。

让我们展示一个更独特的热图,将我们的数据转换成适合我们的梯度。将离散数据点转换为网格值(图像)的过程称为光栅化。这样,我们可以将离散的火灾数据点聚合成一个数值网格。虽然我们失去了每一场火灾的信息,但我们得到了一张反应更灵敏的地图。

由作者创建

我应该注意到,我们可能会通过调整梯度来彻底改变这个图,这很容易做到。这个图看起来可能有很多代码,但是,这个图非常灵敏,内置的HeatMap函数是一个很好的工具,可以轻松地从离散的数据点创建适当的连续可视化。

hvPlot

叶是一个强大的图书馆,但我再次发现我最喜欢的任务工具来自全息视图。我使用 Holoviews、hvPlot、Colorcet、Datashader 和 Geoviews 在更短的时间内不断获得更好的绘图。

这听起来像是很多库,但是你不必与它们中的大多数进行交互,除非你需要一些特定的东西;否则一切都会在你的引擎盖下发生。让我们使用 Holoviews 中的工具重新创建我们的情节。

由作者创建

这绝对是最好的热图。我需要将弹出窗口中的 x/y 标签调整为 lat/long,但除此之外,hvPlot 为我做了我想要的一切。它甚至给了我一个像样的彩条作为传说,这可能是一个痛苦与其他图书馆。

您会注意到,我确实不得不从几个库中把它放在一起:

  • 全息视图用于访问底图图块。
  • Datashader 用于栅格化,并给了我一个简单的方法将经度和纬度转换成米,这是 hvPlot 的首选。
  • Colorcet 为彩色地图。

所有这些的关键部分是rasterize=True,它告诉 hvPlot 让 DataShader 栅格化数据,使其更容易处理。Datashader 确实很有意思,但是值得有自己的帖子来深入讨论它是如何工作的;现在,重要的是要知道,它正在处理将我们的许多离散数据点转换成更容易呈现和更容易可视化的网格值。

包扎

令人惊讶的是 Python 的空间可视化工具在过去几年中取得了如此大的进步,并且有许多很好的选项可供选择来创建您心目中的情节。

Holoviews 能够提供的特性和易用性给我留下了深刻的印象,而不会因为混乱的堆栈跟踪而导致无尽的错误。制作新的图表和仪表盘很有趣,而且一路上很少遇到问题。

资源

  • 互动剧情直播笔记本
  • HV plot 简介
  • 全息视图文档
  • 叶文件
  • 散景文档

**注意:**如果你喜欢在 Medium 上阅读我和其他人的内容,考虑使用下面的链接订阅,以支持这样的内容创建,并解锁无限的故事!

https://medium.com/@willmnorris/membership

引文

  • 野火数据 —公共域(CC0)

短,凯伦 C. 2017。美国 1992–2015 年的空间野火发生数据[FPA FOD 20170508]。第四版。柯林斯堡,CO:林务局研究数据档案。【https://doi.org/10.2737/RDS-2013-0009.4

用 Python 绘制两幅海冰浓度图

原文:https://towardsdatascience.com/plotting-sea-ice-concentration-with-2-graphs-using-python-394bf4e8f361

用散点图和热图显示海冰浓度

丹尼尔·巴恩斯在 Unsplash 上的照片

气候变化,一种全球现象,影响着地球的天气模式。它产生了诸如沙漠扩大和热浪变得更加频繁的后果。北极温度的升高也导致了永久冻土的融化和海冰的减少。

再来说说海冰。极地放大是一种现象,其中地球极点附近的温度比地球其他地方的温度增强得更大。这导致了过去几十年海冰的消失。

为了应对这些问题,全世界都在努力减缓气候变化。监控和记录是帮助我们分析变化过程的方法。本文将展示两个图表,一个散点图和一个热图,用 Python 来可视化海冰的浓度。

我们开始吧

第一张图片:显示 2021 年 12 月北极圈上方海冰平均百分比浓度的散点图。第二张图片:同时显示海冰的热图。作者图片。

数据来源

下载的数据集名为“从 1979 年至今从卫星观测获得的海冰浓度每日网格数据”(链接)版权所有(2022) EUMETSAT。数据集的时间从 2010 年 1 月到 2021 年 12 月,共 12 年,来自哥白尼网站。

使用的产品是欧洲气象卫星应用组织海洋和海冰卫星应用设施制作的全球海冰密集度气候数据记录。

为了获得数据集,我遵循了这两篇文章中颇有见地的步骤:

  • 用 Python ( link )将 ERA5 直接读入内存
  • 天气记录最佳免费 API:era 5!(链接

现场视察 SAF 产品的所有知识产权属于欧洲气象卫星应用组织。关于数据集所有权、版权、鸣谢和引用的更多信息可以在这里找到:链接和这里:链接。

下载到您的计算机后,我建议将文件保存在一个文件夹中,以便在下一步中轻松导入。

输入数据

netCDF4 库用于读取 NetCDF (网络通用数据格式)格式的下载文件。我们将要处理的总文件大小有点大。因此,需要一些时间来处理。tqdm 库可以向我们展示进度条。

要读取文件,我们需要知道文件名。下面的代码用于获取保存它们的文件夹中的每个文件名。

可以注意到,文件名包含日期戳。这可用于为每个数据集分配日期。创建日期列表。

浏览数据

在继续之前,让我们探索一下每个文件包含的变量。

有关更多信息,请使用下面的代码从每个变量中获取更多详细信息。

为了直观显示海冰浓度,将使用三个变量:

  • 纬度:纬度
  • 经度
  • ice _ conc:filter ssea _ ice _ area _ fraction;使用亮度温度和开放水域的大气修正完全过滤的海冰浓度

创建数据框架

我们将读取 NetCDF 文件,并将它们转换成数据帧,以便使用 Python。定义快速创建数据帧的函数。

应用函数。仅选择高于 50 的百分比浓度,以避免过多的数据。对了,数字是可以修改的。

注意:如果出现一个关于内存的错误,我建议做一个列表切片。将数据分成两个或三个部分来处理小数据,并用 DataFrame.to_csv 保存输出数据帧。重复这个过程,直到完成列表。之后,读取每个数据帧并用 pandas.concat 连接它们。

描述性分析

本文将主要关注大约在北纬 66 度的北极圈上方的冰海。如果对南极圈下面的冰海感兴趣,可以修改下面代码中的纬度值。按年份和月份过滤数据框和分组。

从数据框中画出时间序列图。

时间序列图显示了 2010 年至 2021 年的月平均百分比浓度。图片由作者提供。

时序图中的线条显示,8 月是平均百分比浓度达到最低点的月份。这是有道理的,因为北半球现在是夏天。

海冰可视化

本文将介绍如何使用极轴散点图和热图来可视化数据。

在继续之前,所获得的数据帧还没有准备好用于绘图。需要将纬度从[66,90]缩放到[0,1],以便于数据可视化。此外,经度范围包含从[-180,180]开始的负值,需要将这些值转换为正值范围。让我们定义一个函数来转换这些值。

创建年份和月份列表以过滤数据框架。

下一步是应用该函数并使用年和月的列表进行过滤。

极轴散点图

这个想法是从北极上方看。因此,我们将在极轴上绘制散点图来模拟观察点。请注意,结果将保存到您的计算机上,以供以后使用。

极轴散点图示例显示了 2021 年 12 月北极圈上方海冰的平均百分比浓度。图片由作者提供。

创建一个 GIF 文件

制作动画是使情节看起来有趣的另一种方法。因此,让我们把这些图组合起来,并把它们转换成一个 GIF 文件。例如,我将结合 2020 年和 2021 年每个月的散点图。

瞧啊。!….

一个 GIF 文件,综合了 2020 年 1 月到 2021 年 12 月极轴上的所有散点图。

从保存的输出中,我们可以创建一个 GIF 文件,只选择同一个月来比较每年的数据。例如,下面的代码显示了如何组合九月的每个情节。

2010 年至 2021 年每年 9 月在极轴上组合所有散点图的 GIF 文件。图片由作者提供。

热图

为了在世界地图上创建热图,follow是一个功能强大的 Python 库,用于处理地理空间数据。从导入库开始。

我们需要在用叶子申请前准备好资料。这次选择的是赤道线以上的全部数据。之后执行 groupby,计算月平均值。然后,选择要绘图的月份。例如,下面的数据集来自 2021 年 12 月。

从数据框中,创建一个用于绘图的数据和颜色列表。

用叶库绘制热图。

使用叶子显示 2021 年 12 月北极圈上方海冰平均百分比浓度的热图示例。图片由作者提供。

随时间变化的热图

Folium 还有一个名为 HeatMapWithTime 的功能,用于通过组合多个热图来创建动画。我会绘制 2019 年 1 月到 2021 年 12 月的热图,24 个月。

用 HeatMapWithTime 绘制数据。

热图的动画显示了从 2019 年 1 月到 2021 年 12 月北极圈上方海冰的平均百分比浓度,使用的是来自 follow 的 HeatMapWithTime。图片由作者提供。

摘要

本文展示了海冰数据源以及如何导入和准备数据。用于显示海冰密集度的两个图表是极轴散点图和热图。可以通过创建动画来改进这两个图形,使结果看起来更有趣。

本文主要关注北极圈上空的海冰。顺便说一句,该方法可以应用于南半球的海冰,在南极圈下方。

如果您有任何建议或问题,请随时评论。

感谢阅读

这些是关于数据可视化的其他文章,您可能会感兴趣。

用 NASA 数据和 Python ( 链接)可视化看不见的 SO2

8 用 Python 处理多个时序数据的可视化(链接)

用 Python 可视化光速(链接

参考

  • 欧洲气象卫星应用组织海洋和海冰卫星应用设施,1979-2015 年全球海冰密集度气候数据记录(v2.0,2017),OSI-450,doi: 10.15770/EUM_SAF_OSI_0008,2022-06-01 数据摘自 https://cds.climate.copernicus.eu/cdsapp#![哥白尼气候变化服务气候数据仓库/数据集/卫星-海冰-浓度](https://cds.climate.copernicus.eu/cdsapp#!/dataset/satellite-sea-ice-concentration)
  • 欧洲气象卫星应用组织海洋和海冰卫星应用设施,2016 年以来全球海冰密集度气候数据记录(v2.0,2017),OSI-450,doi: 10.15770/EUM_SAF_OSI_0008,2022 年 6 月 1 日从 https://cds.climate.copernicus.eu/cdsapp#!哥白尼气候变化服务气候数据存储中提取的数据/数据集/卫星-海冰-浓度
  • 维基媒体基金会。(2022 年 6 月 7 日)。气候变化。维基百科。于 2022 年 6 月 8 日从https://en.wikipedia.org/wiki/Climate_change检索
  • 维基媒体基金会。(2022 年 2 月 4 日)。极坐标放大。维基百科。2022 年 6 月 8 日检索,来自https://en . Wikipedia . org/wiki/Polar _ amplification #:~:text = Polar % 20 amplification % 20 is % 20 the % 20 phenomenon,Polar % 20 warming % 20 to % 20 tropical % 20 warming。

绘制时间序列箱线图

原文:https://towardsdatascience.com/plotting-time-series-boxplots-5a21f2b76cfe

了解如何使用 matplotlib 和 Seaborn 绘制时间序列箱线图

阿格巴洛斯在 Unsplash 上拍摄的照片

时间序列数据集是在一段时间内按时间索引收集的数据集合。使用时间序列,您可以绘制有趣的可视化图形,来说明一段时间内被研究对象的值的变化。一种特殊类型的时间序列图是时间序列箱线图

当您在特定时间间隔内有多个数据点时,时间序列箱线图是一种非常有用的数据集可视化方法。例如,您在一个月的时间内每小时收集一次某个位置的温度。您希望使用箱线图来查看每天的平均温度如何变化,以及每天的温度离差。这就是时间序列箱线图的用处。

因此,在本文中,我将向您介绍绘制时间序列箱线图的一些基础知识——从使用 Pandas Series 和 DataFrame 设置简单数据集,到加载真实数据集,并向您展示如何根据您的要求绘制时间序列箱线图。

使用熊猫系列绘制时间系列箱线图

我想说明的第一个简单的例子是如何使用熊猫系列进行绘图。首先,让我们创建一个包含日期范围的DatetimeIndex对象:

import pandas as pd
import numpy as npdate_range = pd.date_range(start = "2022-01-01", 
                           end   = "2022-02-28 23:59:00",
                           freq  = "H")

这里,date_range是一个开始日期为2022–01–01 00:00:002022–02–28 23:00:00DatetimeIndex对象。注意每个项目间隔 1 小时(freq=’H’):

DatetimeIndex(['2022-01-01 00:00:00', '2022-01-01 01:00:00',
               '2022-01-01 02:00:00', '2022-01-01 03:00:00',
               '2022-01-01 04:00:00', '2022-01-01 05:00:00',
               '2022-01-01 06:00:00', '2022-01-01 07:00:00',
               '2022-01-01 08:00:00', '2022-01-01 09:00:00',
               ...
               '2022-02-28 14:00:00', '2022-02-28 15:00:00',
               '2022-02-28 16:00:00', '2022-02-28 17:00:00',
               '2022-02-28 18:00:00', '2022-02-28 19:00:00',
               '2022-02-28 20:00:00', '2022-02-28 21:00:00',
               '2022-02-28 22:00:00', '2022-02-28 23:00:00'],
              dtype='datetime64[ns]', length=1416, freq='H')

现在,您可以使用date_range变量作为索引来创建熊猫系列。对于该值,我们使用一个随机数生成器:

ts = pd.Series(list(np.random.randn(len(date_range))),
               index = date_range)

下面是现在ts的内容:

2022-01-01 00:00:00   -0.869078
2022-01-01 01:00:00    1.742324
2022-01-01 02:00:00    0.937706
2022-01-01 03:00:00    0.366969
2022-01-01 04:00:00    1.841110
                         ...   
2022-02-28 19:00:00    0.061070
2022-02-28 20:00:00    0.354997
2022-02-28 21:00:00    1.102489
2022-02-28 22:00:00   -1.299513
2022-02-28 23:00:00   -0.452864
Freq: H, Length: 1416, dtype: float64

现在,您可以使用 matplotlibSeaborn 绘制时间序列箱线图了:

import matplotlib.pyplot as plt
import seabornfig, ax = plt.subplots(figsize=(20,5))
seaborn.boxplot(x = ts.index.dayofyear, 
                y = ts, 
                ax = ax)

您将看到下面的时间序列箱线图:

作者图片

使用 Pandas 数据框架绘制时间序列箱线图

第二个例子是创建一个 DataFrame,将date_range对象设置为索引:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborndate_range = pd.date_range(start = "2022-01-01", 
                           end   = "2022-02-28 23:59:00",
                           freq  = "H")
df = pd.DataFrame(
    {
        'temp':np.random.randn(len(date_range))
    }, **index = date_range**)
df

数据帧如下所示:

要查看 2 个月中每天的箱线图,请使用DatetimeIndex类型的dayofyear属性(df.index):

fig, ax = plt.subplots(figsize=(20,5))
seaborn.boxplot(x = df.index.**dayofyear**, 
                y = df['temp'], 
                ax = ax)

您将看到如下图:

作者图片

绘制每个月的时间序列箱线图

对于下面的例子,我将从一个 CSV 文件中加载数据,您可以从以下 URL 获得该文件:【https://data.gov.sg/dataset/wet-bulb-temperature-hourly。

单击下载按钮下载数据集

该数据受 新加坡开放数据许可证 的约束,可在https://data.gov.sg/open-data-licence查阅。

该数据包含樟宜气候站记录的每小时湿球温度。

湿球温度是绝热饱和温度。这是暴露在气流中的湿温度计球指示的温度。湿球温度可以用湿平纹细布包裹的温度计测量。来源:气温—干球/湿球/露点 https://www.weather.gov 朱 dry_wet_bulb_definition

让我们将 CSV 文件加载到数据帧中:

df = pd.read_csv('wet-bulb-temperature-hourly.csv', 
                 parse_dates = ['wbt_date'], 
                 index_col='wbt_date')
df

这是数据帧的样子:

作者图片

数据的日期从 1982 年开始到 2022 年。让我们看看每个的温度是如何变化的。为此,我们将使用DatetimeIndex类型的**month**属性:

fig, ax = plt.subplots(figsize=(12,5))seaborn.boxplot(x = df.index.**month**,
                y = df['wet_bulb_temperature'], 
                ax = ax)

剧情如下:

作者图片

正如你所看到的,五月似乎是每年最热的一个月。

绘制每年的时间序列箱线图

最热的一年怎么样?为此,我们将使用DatetimeIndex类型的**year**属性:

fig, ax = plt.subplots(figsize=(24,10))
seaborn.boxplot(x = df.index.**year**,
                y = df['wet_bulb_temperature'], 
                ax = ax)

你可以看到 1998 年是最热的一年:

作者图片

注意到 x 记号有点挤。让我们把它旋转 30 度:

fig, ax = plt.subplots(figsize=(24,10))
seaborn.boxplot(x = df.index.year,
                y = df['wet_bulb_temperature'], 
                ax = ax)
**_ = ax.set_xticklabels(ax.get_xticklabels(), rotation = 30)**

现在,您可以看到 x 刻度更加分散了:

作者图片

绘制特定月份中每天的时间序列箱线图

如果你想知道某年某月的温度,比如说 1982 年 1 月,你可以在绘图前先对数据帧进行过滤,如下所示:

fig, ax = plt.subplots(figsize=(24,10))
seaborn.boxplot(
    x = df[**'1982-01-01':'1982-01-31'**].index.day,
    y = df[**'1982-01-01':'1982-01-31'**]['wet_bulb_temperature'], 
    ax = ax)

看起来 1982 年 1 月 1 日是这个月最热的一天:

作者图片

为一年中的每一天绘制时间序列箱线图

最后,如果您想查看某一年的温度读数,比如说 1982 年的**,您可以使用DatetimeIndex类型的**dayofyear**属性:**

fig, ax = plt.subplots(figsize=(150,10))seaborn.boxplot(
    x = df['**1982-01-01':'1982-12-31**'].index.**dayofyear**,
    y = df['**1982-01-01':'1982-12-31**']['wet_bulb_temperature'], 
    ax = ax)fig.savefig('temp.jpg')

因为图表会很大,我使用了savefig()功能将图表保存到文件中。图表看起来像这样:

如果你放大,你会看到 x 记号只是一些连续的数字:

作者图片

您可以使用set_xticklabels()功能格式化 x 刻度:

fig, ax = plt.subplots(figsize=(150,10))seaborn.boxplot(
    x = df['1982-01-01':'1982-12-31'].index.dayofyear,
    y = df['1982-01-01':'1982-12-31']['wet_bulb_temperature'], 
    ax = ax)**ax.set_xticklabels(labels = 
    df['1982-01-01':'1982-12-31'].index.strftime(
        '%Y-%m-%d').sort_values().unique(), 
    rotation=45, ha='right')****f**ig.savefig('temp.jpg')

x 记号现在是实际日期:

作者图片

如果你想要一些奇特的日期格式,使用strftime()功能定制它们。

摘要

这是一个如何为时间相关的数据创建时间序列箱线图的快速概述。尝试各种组合来创建您需要的图表!

**https://weimenglee.medium.com/membership **

熊猫管道

原文:https://towardsdatascience.com/plydata-piping-for-pandas-e7ed4c2d8906

你能为熊猫数据帧使用管道操作符吗?

熊猫的春天来了吗?照片由 Biegun Wschodni 在 Unsplash 上拍摄

介绍

管道是探索数据框的强大工具。r 公司的%>%管道操作员很好地展示了这一点。但是 Python 和pandas数据帧呢?为什么它不提供这样的东西呢?

大多数从 R 转到 Python 的人(像我一样)都觉得 Python 缺少一个管道操作符,或者至少缺少一个像 R 的%>%一样强大的管道操作符。在 R 中,它被包含在[tidyverse](https://doi.org/10.21105/joss.01686) (Wickham,2019)中,这是一组极其强大的数据操作工具——尽管事实是它是在[magrittr](https://CRAN.R-project.org/package=magrittr) R 包中引入的。

事实是 Python 确实有类似的东西,尽管(还)没有那么强大。事实上,有几个模块为管道操作员提供了工作。一个有趣的例子是[Pipe](https://github.com/JulienPalard/Pipe)包,它旨在提供与函数式编程语言类似的管道,比如 F#或 Haskell。但是,它不是为处理数据框而设计的;因此,由于它在其他一些情况下是有趣和有用的,我们将在其他时间谈论它。然而,今天我们谈论的是数据框架。R 中的%>% 操作符——如此强大,如此受人喜爱——是为处理数据帧(以及类似的类型,如[tibble](https://CRAN.R-project.org/package=tibble)、Müller 和 Wickham 2021)而设计的,因此它不像函数式语言中的管道操作符那样工作。

事实上,有几种方法可以实现这一点,其中一个非常有趣的方法是[plydata](https://github.com/has2k1/plydata)。今天,我们将讨论它的基础知识和它的管道操作符>>。在这个基本的介绍之后,你应该准备好决定你是否喜欢这个解决方案。

进入 plydata 的第一步

当然,我们的第一步应该是安装带有所需模块的虚拟环境。我将使用pydataset==0.2.0(即使是旧的,包仍然提供数据集,所以它应该工作良好)和plydata==0.4.3plydata也变得有点旧了,因为上一个版本是 2020 年的,但我看到它的资源库仍然活跃,所以我认为我们应该很快就会有更新。

一旦我们用这些包创建了虚拟环境,我们就可以使用它的 Python 安装了。这是我们需要的:

我将使用iris数据集,这个数据集在各个学科都非常流行,包括分类和数据可视化。我在我写的许多关于统计和数据可视化的科学文章中使用过它;我甚至与人合写了一篇关于这个数据集的文章(科萨克和奥托卡,2013)。

plydata提供了很多有用的函数,但是在这篇介绍性文章中,我们将只讨论其中的几个。首先,让我们从pydataset包中导入数据集:

在数据中,150 个观察结果被分为三个鸢尾物种( I )。云芝海滨锦葵Isetosa ,每一个都有四个变量。现在,我们第一次使用plydata:让我们打印每个物种的前五行*:*

或者,对我来说更有可读性,

这只是开始。稍后我将解释group_by()函数,尽管我认为它的名字说明了它的作用。pandas用户会立刻知道它讲的是什么;事实上,pandas用户通常会很快理解plydata函数的作用。

上面,head()函数获取前n行数据。让我们继续,但我们必须首先重命名列。为此我们当然不需要plydata:

当然,我们可以用很多方法来实现,比如用pandas.DataFrame.rename()方法。好了,我们差不多准备好了,但是因为我们要计算变异系数,我们首先需要为此定义一个函数:

现在,言归正传。下面的简短代码展示了plydata的强大功能和可读性:

在进一步阅读之前,请花一点时间阅读上面的代码,了解它是做什么的;目前,请忽略我注释掉的行,我们稍后将返回到它。

关键在于管道操作符>>,它和 R 的管道操作符magrittr起的作用是一样的,就是%>%。是的,我从magrittr开始写,因为这个包引入了%>%操作符,所以被tidyverse环境大量使用。

一般来说,管道可以帮助您在数据集上按顺序运行函数。你知道这个想法,因为 Pythonistas 可以在类似的链中使用点,比如在my_string.lower.replace('gz', 'z')pandas中。我不认为我必须让你相信这样的功能会很方便,尽管它有一个很大的限制,我将在后面讨论。

当您思考完代码后,让我们一行一行地浏览它:

  • iris表示我们从iris数据帧开始(是的,plydata 适用于pandas.DataFrame s,因此它不适用于发电机)。

*当然,define()函数定义了新的变量;这里,sepal_ratiopetal_ratio。在这里,它还重新定义了species,在开头添加了“I .”一词:生物学家就是这样写物种名称的。

*注释行:实际上,我们不需要在这里选择任何变量,但是我想向您展示,如果我们想要删除其中的一些变量,我们可以这样做(就像我们经常做的那样)。取消对这一行的注释不会改变什么,因为在iris数据集中没有额外的变量。这里,我们将删除名称中带有widthlength的列。

现在我们按species对数据进行分组。这八个字组合成group_by()提供了难以想象的强大力量。它的作用是:在那一行之后要做的所有事情都将按组完成。因此,如果我们计算一个变量的平均值,我们将得到每个物种的平均值(因为物种组成群体)。*

  • summarize()旨在,不出所料,总结数据。这里,我们确定平均值(来自统计模块)和变异系数(使用上面定义的函数)。

看看结果:

我不需要query()函数,但是既然它如此重要,让我向你展示它是如何工作的。假设我们想找到那些萼片相对较宽的样本;这里,我们将查询至少 75%的长度:

我们可以添加其他条件,使用and(当然,您也可以使用or)操作符(因为许多行满足这个条件,所以我们只取前三个):

我很难评估这样的plydata代码对你来说可读性如何,因为我已经使用 R 的管道很长时间了。因此,我读它就像读一个书面文本,虽然我记得起初我需要习惯这种语法。这些天来,我真的很清楚,至少在这个层次上——因为它可能更难理解(和调试!)与复杂函数结合使用时。

限制

我刚刚提到了管道的最大限制:调试。事实上,调试可能很困难。有时候最好解绑代码;您还可以添加一些print,但是使用管道进行交互式调试需要更多的工作。

另外,有时候你可以用pandas做更多的事情,尤其是在你对plydata的知识还比较贫乏的时候。坦白地说,我经常以代码混淆了plydatapandas语法而告终。也许这样的代码不如纯粹的plydata代码好,但它仍然比纯粹的pandas代码好,也更清晰。

结论

几年前,R 通过引入管道操作符%>%改变了数据探索世界。它如此强大,提供了如此多的可能性,以至于很难想象现在的 R 没有dplyr和管道 R 命令。在我看来,这甚至是 R 历史上最大的游戏改变者。

为什么 Python 的pandas让我们过上了没有管道运营商的生活?当我从 R 迁移到 Python 时,对我来说是一个巨大的惊喜,而且不是一个好的惊喜。尽管如此,由于其他人已经解决了这个问题,我们在这里用plydata和它的>>操作符来帮助我们操作pandas数据帧。

熊猫管道:plydata。来源:作者

我认为通过管道链接命令的强大之处在于使用一个赋值而不是多个。因为在这篇文章中我已经展示了plydata的基础,所以我没有包括长链。然而,事实是,即使是很长的链也可以很容易阅读,而不是使用,比如说,十个任务,你只需要一个。此外,命令的垂直组织(除了非常短的命令,它们可以组织在一行中)有助于我们的眼睛一步一步地跟踪正在做的事情,这是一种组织良好且易于阅读的形式。注意(例如,在上图中),这种代码的组织也符合 Python 的缩进规则。

我不得不承认,对我来说,R 用%>%操作符链接命令的语法似乎比用plydata>>操作符更简单、更好。然而,这可能是我多年来用 r 编程的结果。但是使用管道对数据帧进行操作要容易得多,所以学习这个功能是非常值得的,即使乍一看有点复杂。但是花在plydata上的时间迟早有一天会得到回报。

虽然plydata在开发中仍然落后于tidyverse环境,但是如果与pandas一起工作的数据科学家开始大量使用它,我相信它的开发将会迅速加速;然后,谁知道呢,也许在某个时候,管道将成为 Python 中处理数据帧的主要工具,就像它在 r 中一样。这对我们所有的数据科学家都有好处,原因很简单,管道使我们能够用如此可读的语法进行令人难以置信的数据操作。

我向您展示了使用plydata和数据帧管道的一些基础知识,我希望我已经让您相信它在探索数据帧的过程中很有帮助。在接下来的文章中,我们将更深入地研究这个包,因为管道操作符构成了本质,但是plydata当然提供了更多。它提供了各种功能,使用户能够以各种方式操作数据帧。

参考文献

s . t . bache 和 h . Wickham(2020 年)。magritter:R . R 包版本 2.0.1 的前向管道操作符。https://CRAN.R-project.org/package=magrittr

m .科萨克和 b .奥托卡(2013 年)。关于著名的虹膜数据我们应该知道什么?当代科学,104(5),579-580 页。[此处提供](https://tst www . current science . AC . in/Volumes/104/05/0579 . pdf)

米勒和韦克汉(2021 年)。tibble:简单的数据帧。r 包版本 3.1.2。https://CRAN.R-project.org/package=tibble

威克姆等人(2019)。欢迎来到 tidyverse。《开放源码软件杂志》,4 卷 43 期,1686 页,【https://doi.org/10.21105/joss.01686

https://medium.com/@nyggus/membership

点云配准:经典方法

原文:https://towardsdatascience.com/point-cloud-registration-classic-approaches-d6191302b0b2

迭代最近点(ICP)和相干点漂移(CPD)方法简介

Ellen Qin 在 Unsplash 上拍照

在我作为算法开发人员的工作中,我使用点云作为 3D 表示来解决几个问题。我的一些项目需要将两组点彼此对齐,这并不简单。

这个问题叫做点云配准。给定不同坐标系中的几组点,配准的目标是将它们彼此对齐。通过找到使它们之间的差异最小化的变换来完成对齐。

像许多计算机视觉任务一样,注册问题并不容易,并且具有许多挑战。例如,在某些真实场景中,点云具有不同的密度和有限的重叠。在其他情况下,点集可能是对称的或不完整的。这些例子影响了结果的准确性和效率。已经进行了广泛的研究来提高点云配准的准确性、效率和鲁棒性。

在这篇文章中,我将描述两种主要的经典和流行的建议方法。

迭代最近点(ICP)

给定两组点 P 和 Q,ICP 优化刚性变换以将 P 与 Q 对齐:

ICP 从初始对准开始,并在两个步骤之间迭代

  • 对应步骤 —为 p 中的每个点 p_j 寻找 Q 中最近的点 q_i
  • 对准步骤 —通过最小化对应点之间的 L2 距离来更新变换。

对于理解和实现来说,这是一个简单明了的算法。同时,它也有几个局限性。

ICP 算法的局限性

  • 结果的准确性强烈依赖于良好的初始对准。
  • 倾向于收敛到局部最小值。
  • 对异常值和部分重叠敏感。
  • 仅适用于刚性变换。

在这一领域进行了大量的研究,并且针对 ICP 算法提出了许多变体来解决上述限制。

一种不同的注册方法是概率注册方法。这些方法使用对应关系的软分配,这意味着根据某种概率分配所有点之间的对应关系。这与使用二进制赋值的 ICP 算法相反。这种算法的一个例子是相干点漂移(CPD)。

相干点漂移

相干点漂移是一种用于配准问题的概率方法,它对刚性和非刚性变换都有效。正如作者在原始论文中解释的那样:

两个点集的对齐被认为是一个概率密度估计问题。一个点集代表高斯混合模型 (GMM)质心,另一个点集代表数据点。通过最大化似然性将 GMM 质心拟合到数据来完成配准

与 ICP 相比,CPD 对噪声、异常值和缺失点更具鲁棒性。

CPD 算法的局限性

  • 使用关于噪声和异常值数量的假设。不正确的假设将导致注册不良。
  • 对于具有密度变化的数据,配准趋向于收敛到局部极值(局部最小值或局部最大值)

摘要

注册任务是计算机视觉领域中的一个热门任务。大多数方法依赖于寻找两点云之间的对应关系。提出了两种主要的方法——像 ICP 中使用的硬对应,以及像 CPD 中使用的对应的软分配。每种方法都有许多不同的变体,各有利弊。

哪个最适合你?这取决于您的数据、您是需要刚性方法还是非刚性方法、噪声和异常值的数量、密度变化等等。正如我之前提到的,这些方法有很多变体。我建议找到最适合您的任务和数据的一个。

参考

这是我在这篇文章中使用的论文列表。您可以阅读它们以了解更多信息。

https://ieeexplore.ieee.org/abstract/document/8490968 https://ieeexplore.ieee.org/abstract/document/9336308 https://ieeexplore.ieee.org/abstract/document/5432191 https://link.springer.com/article/10.1007/s11432-011-4465-7 https://ieeexplore.ieee.org/abstract/document/8897021

用于语义分割的点网络

原文:https://towardsdatascience.com/point-net-for-semantic-segmentation-3eea48715a62

如何训练点网进行点云分割

格兰特·波特在 Unsplash 上拍摄的照片

介绍

这是点网系列的第四部分:

  1. 点网直观介绍
  2. 点网从无到有
  3. 用于分类的点网
  4. 用于语义分割的点网

在本教程中,我们将学习如何在斯坦福 3D 室内场景数据集( S3DIS )上训练点网进行语义分割。S3DIS 是一个 3D 数据集,包含多个建筑物的室内空间的点云,覆盖面积超过 6000m [ 1 ]。点网络是一种新颖的架构,它消耗整个点云,并能够进行分类和分割任务[ 2 ]。如果你一直在关注点网系列,你已经知道它如何工作和如何编码。

在之前的教程中,我们学习了如何在迷你版的 shapenet 数据集上为分类训练点网。在本教程中,我们将使用 S3DIS 数据集训练语义分割的点网络。本教程的代码位于这个库中,我们将在这个笔记本中工作。

以下是对未来的概述:

  • 数据集概述
  • 方法学
  • 模特培训
  • 模型评估
  • 结论
  • 参考

S3DIS 数据集

概述

中使用的完整 S3DIS 数据集可通过请求访问此处下载。数据集被分成对应于不同建筑物的六个不同区域,在每个区域内有对应于不同房间(例如办公室或会议室)的不同室内空间。这个数据集有两个版本,原始的和对齐的,我们选择使用对齐的版本。对齐版本与原始版本相同,只是旋转了每个点云,使得 x 轴沿房间的入口对齐,y 轴垂直于入口墙,而 z 轴保持垂直轴。这种排列形成了一个规范的(即公共的)坐标系,该坐标系允许我们利用在每个点云中发现的一致结构[ 1 ]。

数据整理

数据集在磁盘上将近 30GB(6GB 压缩),但是我们有一个压缩版本,解压缩后只占用大约 6GB。在数据简化过程中,真实数据点颜色已被移除,所有数据点已被转换为 float32,留给我们的是包含(x,y,z)点的 Nx4 数组和一个类。每个空间都被分割成大约 1x1 米的子空间,并保存为 hdf5 文件。这个过程超出了本教程的范围,但是这里有一个笔记本用来生成简化的数据集。

数据超参数

当谈到数据时,我们可能不会经常想到超参数,但增强(甚至规范化)的类型实际上是超参数,因为它们在学习过程中起着重要作用[ 3 ]。我们的数据超参数可以分为两类:实际变换本身(例如图像旋转与图像扭曲)和控制变换的参数(例如图像旋转角度)。模型不能直接学习这些东西,我们通常根据验证性能调整这些东西,就像我们对模型超参数所做的一样(例如学习率、批量)。还值得注意的是,数据超参数可以大大提高模型的学习能力,这可以通过经验来验证。

在训练和验证集中,我们添加了具有 0.01 标准偏差的随机高斯噪声。仅在训练集中,我们以 0.25 的概率围绕垂直轴随机旋转。限制围绕垂直轴的旋转允许基础结构发生变化,而地板、墙壁和天花板(背景)在所有分区中都保持类似的关系。对于所有分割,我们执行最小/最大归一化,以便每个分区的范围从 0 到 1。与[ 2 类似,在训练和验证期间,我们对每个分区随机抽取 4096 个点。在测试过程中,我们倾向于使用更多的点来获得对模型性能的更好理解,所以我们用 15000 个点进行下采样。

PyTorch 数据集脚本位于这里,请跟随笔记本来看看如何生成数据集。对于我们的划分,我们使用区域 1-4 进行培训,区域 5 进行验证,区域 6 进行测试。

数据探索

图 1 显示了一个完整空间的示例。而图 2 中示出了规则 VS 旋转分区的示例。

图一。用颜色表示不同等级的完整空间。来源:作者。

图二。常规与循环训练分区。来源:作者。

现在让我们探索一下培训课程的频率,它们显示在图 3 中。我们可以看到,这个数据集非常不平衡,一些类别似乎构成了背景类别(天花板、地板、墙壁)。我们应该注意到,clutter 类实际上是任何其他杂项对象的一个类别,例如白板或墙上的图片,或者桌上的打印机。

图 3。S3DIS 数据集的类频率。来源:作者。

方法学

问题定义

当你听到语义分割时,你可能会想到图像,因为它是识别给定图像中每个像素的概念。分割可以推广到高维空间,对于 3D 点云,这是为每个 3D 点分配一个类别的概念。为了更好地理解这个问题,我们应该很好地理解什么是点云实际上是。让我们考虑一下我们想要划分的类,如果你看图 2,你会注意到每个类(除了杂乱的)都有独特和一致的结构。即墙壁、地板和天花板是平坦且连续的平面;像椅子和书柜这样的东西,在许多不同的领域也应该有一致的结构。我们希望我们的模型能够在一定程度上准确地识别不同类的不同结构。我们将需要构建一个损失函数来诱使我们的模型以一种有用的方式学习这些结构。

损失函数

在图 2 中,我们可以清楚地看到这个数据集是不平衡的。我们以类似于分类教程的方式解决这个问题。我们加入了平衡的聚焦损失,它是基于交叉熵损失加上一些额外的项来衡量它。第一个比例因子是类别权重(alpha ),它决定了每个类别的重要性,这就是“平衡”一词的来源。我们可以使用逆类权重或者手动将其设置为超参数。第二项是将平衡的交叉熵损失转换为平衡的焦点损失,这一项被认为是一个调节因素,它迫使模型关注困难的类别,即那些以低置信度预测的类别[ 5 ]。如图 4 所示,调制因子通过超参数γ来控制。伽玛项的范围可能从 0 到 5,但实际上取决于具体情况。

图 4。t 类的聚焦损失。α是类权重,γ幂的项是调制项,对数项是交叉熵损失。来源:[ 5 ]

[ 1 的作者建议,语义分割问题实际上作为检测问题比分割问题更好处理。我们不会在这里展开太多,但是我们会尝试用我们的损失函数来考虑整体的职业结构。我们的模型需要学习类结构的基本表示,它需要学习它们是连续的而不是稀疏的。我们可以结合骰子损失来帮助说明这一点。Dice 分数量化了我们预测的类别与实际情况的重叠程度。骰子损失正好是 1 减去骰子系数,如图 5 所示,我们添加了ε以避免被零除[ 6 ]。

图 5。骰子损失来源[ 6 ]。

我们引入骰子损失来阻止模型预测稀疏结构分类。也就是说,我们更喜欢分割整面墙,而不是墙和杂物的混合体。在训练过程中,我们将焦点损失和骰子损失相加,并将其作为我们的损失。损失函数的代码在这里可用,PyTorch 中的骰子损失代码如下:

@staticmethod
def dice_loss(predictions, targets, eps=1):

    targets = targets.reshape(-1)
    predictions = predictions.reshape(-1)

    cats = torch.unique(targets)

    top = 0
    bot = 0
    for i, c in enumerate(cats):
        locs = targets == c

        y_tru = targets[locs]
        y_hat = predictions[locs]

        top += torch.sum(y_hat == y_tru)
        bot += len(y_tru) + len(y_hat)

    return 1 - 2*((top + eps)/(bot + eps))

注意,作者在他们的实现中,没有使用特征矩阵正则化进行语义分割,所以我们也不会使用它。

模特培训

模型超参数

模型超参数在下面的训练设置代码中列出,同样,笔记本在这里是。

import torch.optim as optim
from point_net_loss import PointNetSegLoss

EPOCHS = 100
LR = 0.0001

# manually set alpha weights
alpha = np.ones(len(CATEGORIES))
alpha[0:3] *= 0.25 # balance background classes
alpha[-1] *= 0.75  # balance clutter class

gamma = 1

optimizer = optim.Adam(seg_model.parameters(), lr=LR)
scheduler = torch.optim.lr_scheduler.CyclicLR(optimizer, base_lr=0.0001, max_lr=0.01, 
                                              step_size_up=2000, cycle_momentum=False)
criterion = PointNetSegLoss(alpha=alpha, gamma=gamma, dice=True).to(DEVICE)

我们手动加权背景和杂波类别,并将焦点损失的 gamma 设置为 1。我们使用一个 Adam 优化器和一个循环学习率调度器。[ 7 ]的作者指出,学习率是最重要的超参数,并建议循环学习率(CLR)可以更快地产生更好的结果,而无需大量调整学习率。我们采用了 CLR 方法,还应该注意到,本实验的大部分超参数调整工作都集中在数据超参数上。然而,我们应该注意到,与静态学习率相比,使用 CLR 可以提高模型性能。

培训结果

在训练期间,我们跟踪了损失、准确性、Mathews 相关系数( MCC )和联合交集(IOU)。训练结果如图 6 所示。

图 6。培训指标。来源:作者。

我们看到在第 30 代左右,验证损失开始变得不稳定,尽管如此,指标仍在改善。指标的参差不齐是循环学习率的典型特征,因为指标往往在每个周期结束时达到峰值[ 7 ]。我们从分类教程中得知,MCC 通常比 F1 分数或准确度[ 8 ]更能代表分类。即使我们正在进行细分培训,这仍然是一个很好的观察指标。我们真正感兴趣的是 IOU(或 Jaccard Index )。这是因为类不仅仅是类别,它们是包含在点云中的连续结构。我们希望看到我们的预测与 IOU 量化的基本事实的重叠百分比。图 6 显示了如何根据集合计算 IOU。

图 6。Jaccard 索引(并集上的交集)。来源:[ 9 ]

我们通过下式计算 PyTorch 中的 IOU:

def compute_iou(targets, predictions):

    targets = targets.reshape(-1)
    predictions = predictions.reshape(-1)

    intersection = torch.sum(predictions == targets) # true positives
    union = len(predictions) + len(targets) - intersection

    return intersection / union 

模型评估

试验结果

从我们的训练中,我们发现在第 68 个历元上训练的模型在测试集上产生最好的 IOU 性能。区域 6 的测试指标如下图 7 所示,对于该区域,我们使用每个分区 15000 个点,而不是 4096 个点来进行培训和验证。由模型学习的权重延续到更密集的测试点云,因为所有分割都具有相似的结构。

图 7。模型 68 的测试指标。来源:作者。

分割结果

为了真正评估我们的模型,我们在数据加载器中创建了一个特殊的函数来获取构成完整空间的分区。然后,我们可以将这些分区缝合在一起,以获得完整的空间。通过这种方式,我们可以看到整个预测空间是如何与地面事实相比较的。同样,数据集的代码位于这里的处,我们可以使用以下内容获取一个随机空间:

points, targets = s3dis_test.get_random_partitioned_space()

图 8 显示了几个完整测试空间的结果。这是一个在几个办公室布局中获得良好分割结果的例子。您可以看到,杂波(黑色)似乎是随机分配给预测点云中的区域。

图 8。两个随机测试空间的分割结果。来源:作者。

完整的视图很好看,但是检查模型在每个分区上的执行情况仍然很重要。这让我们真正看到模型学习类的结构有多好。各种分区的分割结果如图 9 所示。

图 9。各种测试分区的分割结果。来源:作者。

在图 9 右上角的分区示例中。你会发现这个模型很难定义杂乱(黑色)和桌子(浅绿色)的边界。一个普遍的观察是,任何过度的扰动都倾向于被称为杂波。总的来说,该模型的性能相当好,因为它能够获得合理的分割性能,如 IOU 所量化的。我们还可以在测试空间中观察到一些相当合理的性能,如图 8 所示。

临界集

如果你还记得介绍点网的文章,点网能够学习点云结构的基本框架,也就是[ 2 提到的临界集。在分类教程中,我们能够查看学到的临界集,我们将在本教程中做同样的事情。我们对每个分区使用 1024 个点,因为这是模型学习的全局特征的维度。下面给出了缝合在一起并显示整个空间的临界集的代码。详情请见笔记本。

points = points.to('cpu')
crit_idxs = crit_idxs.to('cpu')
targets = targets.to('cpu')

pcds = []
for i in range(points.shape[0]):

    pts = points[i, :]
    cdx = crit_idxs[i, :]
    tgt = targets[i, :]

    critical_points = pts[cdx, :]
    critical_point_colors = np.vstack(v_map_colors(tgt[cdx])).T/255

    pcd = o3.geometry.PointCloud()
    pcd.points = o3.utility.Vector3dVector(critical_points)
    pcd.colors = o3.utility.Vector3dVector(critical_point_colors)

    pcds.append(pcd)

# o3.visualization.draw_plotly([pcds]) # works in Colab
draw(pcds, point_size=5) # Non-Colab

我们使用颜色的真实标签来显示临界集,结果如图 9 所示。

图 9。随机测试空间的基本事实和学习临界集之间的比较。来源:作者。

另一个随机临界集的 GIF 如图 10 所示。由此可以更清楚地看出,临界集维持着室内空间的基本结构。

图 10。随机测试空间的临界集。来源:作者。

结论

在本教程中,我们学习了 S3DIS 数据集以及如何在其上训练点网。我们已经学习了如何组合损失函数以实现良好的分割性能。即使我们在空间的分区上进行训练,我们也能够将这些分区缝合在一起,并在我们观察到良好性能的测试集上可视化它们的性能。我们能够查看学习到的临界集,并确认该模型实际上正在学习室内空间的底层结构。尽管这个模型已经表现得相当好了,我们仍然有更多的改进空间,下面是对未来工作的一些建议。

对未来工作的建议

  • 使用不同的损失函数
    -对焦点和骰子损失应用不同的权重
  • 使用区域 1-5 的 k-fold 交叉验证来调整超参数
    -一旦发现超参数就在区域 1-5 训练
    -使用区域 6 进行测试
  • 随着训练时期的增加,增加增强强度
  • 尝试不同的模型
  • 按照[ 2 的附录 D 所述实施检测管道

参考

[1] Armeni,I .、Sener,o .、Zamir,A. R .、Jiang,h .、Brilakis,I .、Fischer,m .、& Savarese,S. (2016)。大规模室内空间的 3D 语义解析。 2016 年 IEEE 计算机视觉与模式识别大会(CVPR) 。https://doi.org/10.1109/cvpr.2016.170

[2]查尔斯,R. Q .,苏,h .,凯春,m .,&吉巴斯,L. J. (2017)。PointNet:用于 3D 分类和分割的点集深度学习。 2017 年 IEEE 计算机视觉与模式识别大会(CVPR) 。https://doi.org/10.1109/cvpr.2017.16

[3]奥托尼,A. L .,德阿莫林,R. M .,诺沃,M. S .,&科斯塔,D. B. (2022 年)。调整深度学习中的数据增强超参数以利用小数据集进行建筑物构造图像分类。国际机器学习与控制论杂志。https://doi.org/10.1007/s13042-022-01555-1

[4]王.语义切分。【www.cs.toronto.edu】T21。2022 年 12 月 17 日检索,来自https://www . cs . Toronto . edu/~ ting Wu Wang/semantic _ segmentation . pdf

[5]林,t-y,戈亚尔,p .,吉尔希克,r .,何,k .,&美元,P. (2017)。密集物体探测的聚焦损失。 2017 IEEE 计算机视觉国际会议(ICCV) 。https://doi.org/10.1109/iccv.2017.324

[6]周,t,阮,s .,&卡努,S. (2019)。基于深度学习的多模态融合医学图像分割综述。数组3–4,100004。https://doi.org/10.1016/j.array.2019.100004

[7]史密斯律师事务所(2017 年)。训练神经网络的循环学习率。 2017 年 IEEE 计算机视觉应用冬季会议(WACV) 。https://doi.org/10.1109/wacv.2017.58

[8]奇科博士和朱尔曼博士(2020 年)。马修斯相关系数(MCC)在二分类评估中相对于 F1 分数和准确性的优势。 BMC 基因组21 (1)。https://doi.org/10.1186/s12864-019-6413-7

[9]维基媒体基金会。(2022 年 10 月 6 日)。 Jaccard 索引。维基百科。检索于 2022 年 12 月 17 日,来自https://en.wikipedia.org/wiki/Jaccard_index

PointGAN:可能的最简单 GAN 的分解

原文:https://towardsdatascience.com/pointgan-a-breakdown-of-the-simplest-gan-possible-3a15244dc508

教机器生成一个单点有多难

乔纳森·博尔巴在 Unsplash 上的照片

介绍

本文描述了生成器-鉴别器对的创建,它将是一致的和健壮的(或多或少)。此外,我的目标是获得一个特定的鉴别器函数,它将确保生成器的一致学习(见下图)。

最简单的 GAN 的任务是生成一个点。一个数字,比如“1”生成器将是具有一个权重和一个偏差值的单个神经元。因此,鉴别器必须是钟形的,就像这样:

x 轴表示鉴频器的输入,y —是该输入可信的概率。

值“1”被认为是实数,接近“1”的值在某种程度上可能是实数;更远的值——可能是假的。

1.基线训练

1.1 常用术语

我应该从描述生成器和鉴别器应该是什么样子以及为什么它们应该是这样开始这一部分。

首先,让我们再想想什么是发电机。这是一种将随机输入转换成真实信号的功能。如引言中所述,真实的信号将是单个值“1”对于输入,我将取-1 到+1 之间的一个随机值。

函数本身将是线性的,因为这个例子旨在简单:

y = w * x + b

其中 y 是输出,x 是输入;w 是神经网络权重,b —是其偏差。这个问题的解决方案是:

w = 0
b = 1

在这种情况下,不管“x”是什么,“y”总是“1”。在图形上,它由一条垂直线表示,如下图所示。

这篇文章中有很多情节显示了名为“重量”和“偏差”的训练过程这些值代表我刚刚描述的“w”和“b”。因此,当我们看这些图时,当“权重”收敛于 0 而“偏差”收敛于 1 时,我们应该感到高兴。

第二,鉴别器。这是另一个函数,它输出其输入可信的概率。在我们的例子中,给定输入“1”,它应该输出接近“1”的值,否则应该输出“0”对于 GAN 网络,它还充当发生器的损耗函数,因此它应该是平滑的。下面是我期望得到的生成器和鉴别器的图:

鉴别器函数(橙色)同上,发生器函数(蓝色)为“y = w * x + b”形式。

就公式而言,鉴别器应尽可能简单。然而它不可能是线性的,因为我们的目标是得到一个钟形。因此,最简单的可能解决方案是具有两个隐藏节点的多层感知器:

顺便说一下,如果我想看看我的生成器和鉴别器函数是什么样子,我只需通过它们传递一系列数字:

事实上,我就是这样得到上面的图的。

1.2 GAN 培训

在我们开始第一次训练之前,我想讨论一下模型将如何被训练,以及我将如何可视化它。请看下图:

此图像显示了训练开始时的生成器和鉴别器,因此它们的参数是随机的。

由蓝线表示的函数旨在将随机输入转换为实际值(现在它也输出随机值,因为它未经训练)。例如,如果我们生成随机值[-2.05,0.1,2.5],这个函数会将它们转换为(大约)[-0.2,0.3,0.49]:

然后将这些值传递给鉴别器(注意刻度差异,纵轴上每个值 2 个方块,横轴上每个值 1 个方块):

然后收集由鉴别器输出的分数。用真实值重复该过程:

假值的平均输出为 0.48,真值的平均输出为 0.56。之后,发生器和鉴别器将采取不同的行动,以便进行训练。

鉴别者的观点。

鉴别器试图通过分数来区分真假值。在这种情况下,可以通过使函数更陡峭来实现:

你可能会注意到,我并没有在上图中定义什么是“更好”。稍后我将使用二进制交叉熵来实现这个目的,但是它不会计算原始输出的平均值。现在不要太关注它,因为它只是这个过程的一个粗略说明。甚至这一整节的数字都是编的。

生成器的观点。

反过来,生成器试图以这样一种方式更新其函数,即当生成的值通过鉴别器时,它们的得分会更高。这可以通过简单地产生更大的值来实现:

注意,同样的输入[-2.05,0.1,2.5],输出更大:[0.2,0.4,0.75]。对于新值,鉴别器输出的平均值为 0.51,这更好。

请注意,与上图相比,下图中的输入向右移动。

结合两个模型的训练步骤,我们得到以下转换:

然后用新的随机值作为发生器输入重复该过程。整体训练通过了下图所示的阶段。

训练进度:a)初始状态。b)生成器试图生成更大的值,鉴别器认为更大的值更真实。c)、d)、e)发电机开始输出大于“1”的值鉴别器也开始将大值评定为假值。f)无论输入是什么,发生器输出“1 ”,鉴别器只将“1”作为最真实的值。

因为我们会有很多这样的小步骤,所以将它们组合成一个动画会很方便,如下所示:

培训期间发生器(蓝色)和鉴别器(橙色)模型的演变。

出于演示的目的,我会大量使用这样的动画。生成它的代码将在下面描述。

1.3 基线代码

我们从模型的代码开始,因为我已经在前一节中描述了它们。这基本上是上面的公式,转换成 PyTorch 代码:

该代码将保存在 models.py 文件中。所以每当我导入模型时,我指的是这段代码。

接下来是训练代码,这有点棘手。我们需要两次遍历数据:一次用于生成器训练,一次用于鉴别器。我会用简单的方法来做。我会让他们轮流。鉴别器训练时,发生器将等待,反之亦然:

至于损耗函数,我不会使用常规的 GAN 损耗,只是为了更简单地理解,并表明它不一定是这样的。然而,失败背后的想法将保持不变。给定其输出,生成器希望鉴别器输出 1:

通过 random_input,我将从 0 到 1 取值,生成方式如下:

鉴别器收集来自生成器的输出和真实值,并试图将它们分开:

代码的其余部分是常规例程:

代码已经包含了日志记录,这将在下面描述。

1.4 记录

有一件事我想添加到培训-使其生成动画,这是在“1.2 甘培训”一节中描述的除了看起来很酷之外,它还能很好地理解内部的情况。这样一个简单的模型有什么好处呢——我们可以负担得起这种类型的可视化。

我将使用的动画类型是 matplotlib 的 FuncAnimation。想法是我创建一个图形和一个更新图形的函数;该库将为每一帧调用该函数,并生成一个动画:

在某些系统上,可能不存在生成视频所需的某些库。在这种情况下,可以尝试生成 gif:

除了动画日志,我想看看发电机的重量和偏差如何变化,看看训练是否朝着正确的方向移动。我在这个例子中使用了权重和偏差,但是其他选择,比如 MlFlow,也是可以接受的。

训练代码产生以下训练过程(我改变了模型的学习率,并使用不同的随机种子运行代码):

发电机重量和偏差的平均值和标准偏差。

请注意,正确的过程最终会将权重设置为“0”,将偏差设置为“1”

培训大部分是成功的,其中一些描述如下:

但是有几个不好的例子:

在视觉表现方面,gif,这里有一些例子:

培训期间发生器(蓝色)和鉴别器(橙色)模型的演变。

由于大多数培训都达到了目标,因此可以认为该准则是“有效的”,但人们可能不同意这一点。让我们看看哪里出了问题,以及如何解决它。

2.基线的问题

只有两件事让我质疑我的训练是否正确。第一,它有时确实会失败,所以它肯定有些不稳定。其次,是鉴别器功能。只有少数情况下,它看起来像我期待的那样。下面来考察一下我遇到的问题。

2.1 发电机正常

在某个时刻,生成器开始输出真实值(因为我们训练它这样做),鉴别器没有办法找到差异。但是网络不知道这一点,所以它继续训练。由于输入已经是现实的,所以是无效的。而且我们都知道,针对无效数据训练的网络是不充分的。

2.2 损失功能不起作用

为了找出问题所在,我从一个失败的案例中取出了一个生成器,并绘制了一些鉴别器参数以及它产生的损耗值。

生成器已经生成了接近真实值的值,在[-0.59,+0.62]的范围内。

我的发现相当令人惊讶,因为一个更好的鉴别函数(作为一个人,我知道更好)——实际上给出了更差的损失值:

不同鉴别器功能的损失值

这是因为生成的值接近真实值。因此,正确的鉴别器对它们的评估大致相同,只有微小的差异。另一方面,不正确的鉴别器会通过如上图所示的彻底改变来略微提高其性能。

首先想到的是,这个鉴别器以 0.5 的概率将真实的例子评估为真实的。这很容易通过给损失函数加权来解决。这个实验和所有其他实验将在下一节描述。但长话短说,没成功。

2.3 设置实验

在继续讨论可行的解决方案之前,我想分享一下基于我上面所做的假设的实验结果。

  1. 损失功能缓解

当前的交叉熵损失函数希望网络输出为 0 和 1,这使得激活前的值(sigmoid)非常大的正值或负值。因为我们的鉴别器是灵活的,大值不是我们想要的。一个解决办法是让目标不要太极端。我会将它们设置为 0.1 和 0.9,而不是 0 和 1。从而重量不会被迫变大。最后,我们需要从鉴别器得到的只是梯度。

在代码中,我将把鉴别器的目标改为这样(改变缓动参数):

再次训练后,将产生这些曲线:

发电机重量和偏差的平均值和标准偏差。

这看起来更稳定,但我仍然不喜欢 gif:

培训期间发生器(蓝色)和鉴别器(橙色)模型的演变。

2.加权真实事例

在代码中,我将为假示例设置较小的权重(这里权重是一个小值):

再次运行培训,获得以下图像。简而言之,没用:

发电机重量和偏差的平均值和标准偏差。

3.体重衰减

训练可能失败的原因之一是鉴别器可能在尝试对来自未训练的生成器的输出进行分类时走得太远。尤其是当鉴别器的学习速度明显快于生成器时。

所以给定一个这样的起点

训练开始时的发生器(蓝色)和鉴别器(橙色)功能

鉴别器将很快学习这样一个函数:

训练结束时,发生器(蓝色)和鉴别器(橙色)起作用

请注意鉴频器功能的清晰度。这意味着它的权重很大,并且(小的)弯曲区域之外的梯度接近于零;所以模型学习非常慢(如果有的话)。

因此,我们的想法是添加一个权重衰减来防止模型达到这种状态:

它给出了以下训练统计数据:

发电机重量和偏差的平均值和标准偏差。

并且可视化如下:

重量衰减= 0.1

重量衰减= 1e-3

重量衰减= 1e-5

人们可能会看到这种方法能够改善训练,但我仍然对训练过程不满意。

3.修复

3.1 基本理念

让我们再来看看生成器输出现实答案但鉴别器仍然要学习一些东西的情况。这个问题的一个解决方案是使发电机的输出无效。当然,只是在鉴别训练期间。这类似于某种形式的辍学。这应该是可行的,因为实际的压差是可行的,但是发生器中的参数太少了。如果我们把其中的一个清零,输出会变化太大。

我想出的解决方案包括在生成器的参数(权重和偏差)中添加高斯噪声。这样,即使生成器完全正确,它也会为鉴别器生成稍微无效的数据,以便它可以学习:

剩下的问题是训练噪音太大,因为梯度随着每个优化步骤快速变化。因此,在优化之前,我决定用不同的噪声进行一系列评估——在批次内进行某种批次评估:

这改进了培训过程:

发电机重量和偏差的平均值和标准偏差。

最后生成一个漂亮的生成器函数:

培训期间发生器(蓝色)和鉴别器(橙色)模型的演变。

但是,有些训练并不顺利。以下是培训日志:

3.2 改进

不得不提的是,如果权值的噪声太小,我在“损失函数不起作用”一节中描述的情况也可能出现在这里。它只是没有将模型推进到足以产生一个明显错误的例子。所以我决定提高噪音水平:

NOISE_SCALE = 1.5  # Instead of 0.5

这一步改进了统计数据,但仍然有失败:

发电机重量和偏差的平均值和标准偏差。

我应用的下一个改进是权重衰减,原因我在上面的“设置实验”部分的“3。体重衰减。”这产生了以下统计信息:

发电机重量和偏差的平均值和标准偏差。

这意味着没有失败的运行。权重衰减有一个副作用,即鉴别器函数变得更平滑,因此 gif 看起来很漂亮:

培训期间发生器(蓝色)和鉴别器(橙色)模型的演变。

在我的例子中,我将重量衰减值取为 1e-1:

但请记住,这取决于重量噪音水平。如果它很小,你可能需要减少重量衰减。否则,鉴别器将变平。

更新后的完整代码如下所示:

结论

这段代码似乎是一致的和健壮的,至少对于这样一个简单的任务是如此。一些参数仍然需要调整,如内部批量大小或重量衰减值,但总的来说,我对结果感到满意。代码的最终版本不需要像第一个版本那样费力,所以我认为它是成功的。

希望对你有帮助,编码愉快!

强化学习中的策略梯度解释

原文:https://towardsdatascience.com/policy-gradients-in-reinforcement-learning-explained-ecec7df94245

了解所有基于似然比的策略梯度算法(强化):直觉、推导、对数技巧,以及高斯和 softmax 策略的更新规则。

斯科特·韦伯在 Unsplash 上拍摄的照片

当我第一次研究政策梯度算法时,我并不觉得它们特别容易理解。直觉上,它们看起来足够简单——采取行动,观察奖励,调整政策——但在最初的想法之后,是许多冗长的推导、我早已忘记的微积分技巧和大量的注释。在某一点上,它只是变成了一个模糊的概率分布和梯度。

在这篇文章中,我试图一步一步地解释这个概念,包括关键的思维过程和数学运算。诚然,这是一个有点长的阅读,并需要一定的强化学习(RL)的初步知识,但希望它揭示了一些政策梯度背后的想法。重点是似然比策略梯度,这是诸如增强/普通策略梯度等经典算法的基础。

考虑到篇幅,我们先来构建这篇文章:

  1. 数值逼近:学习确定性策略
  2. 数值逼近:学习确定性策略
  3. 策略近似方法:转向随机策略
  4. 建立目标函数
  5. 定义轨迹概率
  6. 推导政策梯度
  7. 对数概率函数的梯度
  8. 近似梯度
  9. 定义更新规则
  10. 示例:Softmax 和高斯策略
  11. 损失函数和自动梯度计算
  12. 算法实现(加强)

一.价值近似值:学习确定性政策

RL 的目标是学习一个好的决策政策π,使回报随时间最大化。虽然(确定性)策略π的概念一开始看起来有点抽象,但它只是一个基于问题状态 sπ 😒→a 返回动作 a 的函数。

如果你对 RL 有一些经验,你可能从值近似值开始。这类 RL 保持接近动态编程范式,旨在近似值函数——反映下游回报的 Q 值——而不是递归求解贝尔曼方程达到最优。

对于值近似值方法,确定性策略工作得很好。通常,我们用概率 1-ϵ选择最佳行动(给定我们的 q 值),用概率ϵ选择随机行动,允许对新行动进行一些探索。我们比较 r(t)+Q_t+1 和 Q_t,并使用观察到的误差来改进价值函数。整个概念非常接近贝尔曼的最优条件。

想了解更多关于 RL 各种类的内容?看看这篇文章:

[## 强化学习的四个策略类别

towardsdatascience.com](/the-four-policy-classes-of-reinforcement-learning-38185daa6c8a)

二。策略近似方法:转向随机策略

在策略近似方法中,我们省略了学习值函数的概念,而是直接调整策略。我们用一组参数θ对策略进行参数化——例如,这些参数可以是神经网络权重——并调整θ以改进策略π_θ

这听起来很合理,但是我们如何评估一个给定政策的质量呢?我们如何更新θ?没有将相应的性能与其他东西进行对比的能力,就没有办法分辨。像ϵ-greedy 的价值逼近方法一样,我们需要一些探索机制

有许多策略近似方法(例如,遗传算法、爬山法),但是策略梯度由于其效率而被使用得最多。政策梯度算法有多种形式(例如,有限差分法对θ添加小扰动并测量差异),但本文仅关注似然比政策梯度

核心思想是用一个参数化的概率分布π_θ(a | s)= P(a | s;θ).我们从θ调整的概率分布中抽取动作,而不是返回单个动作。

随机策略可能看起来不方便,但它为优化策略提供了基础。我们将很快进入数学领域,但是让我们从一个手动的直觉开始。各种政策样本使我们能够对比与某些行动相关的奖励。我们用这些样本来改变θ,增加获得高回报的概率。这就是似然比梯度政策的本质。

三。建立目标函数

当在一个连续的决策过程中移动时,我们遵循一个状态-行动轨迹τ=(S1,a1,…,s_T,a_T)。通过对行为进行抽样,策略影响了我们观察时间范围内每个可能的状态和行为序列的概率。每一条轨迹都有一个对应的概率 P(τ)和一个累积奖励 R(τ)=∑γ^t R_t(奖励 R_t 的序列用γ折现)。

为了简单起见(不是必须的),我们假设一组有限的轨迹,这样我们可以对概率求和而不是积分。

有了这些要素,我们就可以正式确定我们的目标,即随着时间的推移使预期回报最大化。这里,τ~π_θ形式化了占优策略下的轨迹分布。同样,我们可以对所有轨迹概率求和,并乘以相应的回报。

目标函数 J(θ)如下:

似然比策略梯度法的目标函数。由于抽样提供了对期望值的无偏估计,我们可以使用模拟来近似估计。

相应的最大化问题表示为:

政策近似的最大化问题。通过调整θ,我们的目标是增加遵循产生高回报的轨迹τ的概率。

四。定义轨迹概率

从最大化问题来看,调整θ显然会影响轨迹概率。下一个问题是:**如何计算概率 P(τ;θ)?**回想一下,这个客观项受政策π_θ的影响。通过增加高回报轨迹的概率,我们提高了预期回报。

如果你想要更多关于这部分的理论背景,值得阅读似然比方法、评分函数重要性抽样。对于本文来说,当前的详细程度已经足够了。

在完全确定的环境中,我们可以计算每个策略π_θ产生的轨迹,并找到产生最高累积回报的策略。然而,大多数 RL 问题不完全是确定性的,而是具有(实质上的)随机成分。因此,轨迹概率受政策的影响,但不完全由政策决定。给定我们的政策,我们计算某个回报轨迹发生的概率。

总而言之,我们处理两种概率分布:

  • 保单本身就是一个概率分布π_θ(a | s)= P(a | s;θ) .该策略规定了在给定状态下选择每个动作的概率,并且取决于参数设置θ。
  • 一个转移概率分布 P(s_t+1|s_t,a_t)描述了环境中的状态转移。注意,这个概率分布部分受π_θ(行动选择)影响,部分受外源信息影响。

有了这些信息,让我们试着计算在策略π_θ(a|s)下轨迹τ出现的概率。每一时间步将采样动作π_θ(a_t|s_t)的概率乘以转移概率 P(s_t+1|s_t,a_t)。随后,我们将每个时间步长的这些概率相乘,以找到完整轨迹实现的概率:

轨迹概率是动作概率(由策略π_θ决定)和状态转移概率(由转移函数 P(s_t+1)决定)的乘积。

麻烦的部分是转移函数 P(s_t+1)。这就是环境的模型,往好里说是工作复杂,往坏里说是完全未知。

这个模型的另一个缺点是,它是概率的产品。对于很长的时间范围和很小的概率,轨迹概率变得非常小。由于计算机语言只提供有限精度的浮点数,这导致了数值的不稳定性。

让我们以后再担心这些事情,首先重新考虑我们实际上旨在优化的函数。

动词 (verb 的缩写)引入政策梯度

如前所述,我们寻求最大化我们的预期回报 J(θ)。如何优化该函数,例如,确定使目标函数最大化的参数θ?

现在我们已经做了一些有益的观察。通过采用随机策略,我们对产生不同轨迹τ的各种行为进行采样,使我们能够看到哪些行为产生最佳回报。换句话说,采样观测为策略参数θ提供了更新方向

是时候具体化这个“方向”了。高中的时候,我们学过如何取函数 f(x)关于 x 的导数 δf/δx 向其最大值(或最小值)移动。如果导数很大,就意味着斜率很陡——我们可能离最优值很远。如果它是 0,我们降落在局部最优。我们可以对 J(θ)做同样的事情,求目标函数 J 对θ的导数。

使用切线的导数斜率动画[图片来自维基百科

一个梯度概括了导数的概念,仅仅是偏导数的一个向量。由于θ通常是一个向量θ=[θ_1,θ_2,…θ_N],我们可以看到如何计算每个参数的偏导数(例如,δJ(θ)/δ θ_1):

参数化累积报酬函数 J(θ)的梯度。梯度是向量θ中每个参数θ_n 的偏导数的向量。

为了计算梯度,我们必须能够对函数J(θ)求导。我们看到改变πθ(a | s)会影响轨迹概率 P(τ;θ).然而,我们还没有解决如何计算这些轨迹概率;记住我们可能甚至不知道转移函数 P(s_t+1|s_t,a_t)!

一些数学的时间到了。

不及物动词推导政策梯度

我们需要检索目标函数的显式梯度。让我们一步一步来。我们从预期回报的梯度开始:

第一步:表示为预期报酬的梯度

如前所述,我们可以将其改写为所有轨迹概率的总和乘以轨迹回报:

步骤 2:表示为概率加权回报轨迹的梯度

总和的梯度等于梯度总和,因此我们可以在总和内移动梯度。

步骤 3:重写为渐变总和

下一步需要更多的关注。让我们回顾一下对梯度政策定理至关重要的一个恒等式——对数导数技巧。

对数求导技巧

数学中一个常见的技巧是将一个表达式乘以 1,这显然不会改变它。在这种情况下,我们乘以P(τ;θ)/P(τ;θ) ,这样表达式变成:

步骤 4a:将表达式乘以 P(τ;θ)/P(τ;θ)(等于 1)

我们可以重新排列成:

步骤 4b:重新排列表达式

并随后:

步骤 4c:再次重新排列表达式

何必呢?嗯,这是因为我们现在可以应用下面的恒等式:

日志标识。这个至关重要的结果——也称为似然比或得分函数——有助于轨迹概率函数的关键转换。

这个恒等式是整个定理的基石。其实,这个结果就是似然比

我将不再详细解释——本文中已经有足够的数学知识了——但是可以使用(I)对数函数的导数和(ii)链式法则来重建恒等式。

使用对数导数技巧,我们将表达式改写为

步骤 4:使用对数导数技巧重写

这个表达是个好消息,原因有很多!我们现在将把自己限制在一个原因上,那就是项∑_θ(P(τ;θ).为什么这是好消息?在对数变换之前,我们对概率的梯度求和,现在我们对概率本身求和。有了这个结果,我们可以**把我们的梯度改写成期望值。**最后一步变成了

第五步:按照预期重写

值得停下来评估一下我们的成就。我们已经知道我们可以把目标函数写成期望值,但是现在我们发现也可以把梯度本身写成期望值。这一点对于应用 RL 等基于采样的方法至关重要。

不幸的是,我们仍然不知道如何处理 P(τ;θ),(即如何实际计算梯度)但我们很快就会到达那里。

七。对数概率函数的梯度

还记得我们之前纠结轨迹概率函数吗,因为它需要环境的显式模型(P(s_t+1|s_t,a_t)部分)?原来我们已经解决了使用日志技巧!

让我们写出梯度部分∇_θ对数 p(τ;θ).首先,我们简单地用 P(τ;θ)我们之前已经确定:

步骤 1:用轨迹概率代替显式表达式

我们可以重写为对数概率,而不是取整个概率乘积的对数:

第二步:使用对数概率重写

对数概率的一个方便的特性是它们是相加的而不是相乘的。对于数值稳定性来说,这是非常令人愉快的(考虑到浮点数的有限精度)。它还有助于防止通常困扰 RL 算法的爆炸/消失梯度。

然而,主要的结果是我们有效地将转移函数 P(s_t+1) 与策略 π_θ(a_t)解耦。因为梯度是相对于θ取的,而 P(s_t+1|s_t,a_t)不依赖于θ,所以我们简单地取它!

步骤 3a:移除状态转换功能。因为它与第二项分开,不依赖于θ,所以对梯度没有影响。

产生的结果看起来更清晰,我们的梯度只取决于政策:

步骤 3b:移除过渡函数后的梯度

最后一步,我们在求和中引入梯度符号(‘和的梯度=梯度的和’),这样我们可以计算每个时间步长的梯度(例如,对于单个动作)。最终结果:

步骤 4:表示为梯度和

八。近似梯度

我们快到了。然而,到目前为止的计算涉及到真实的预期,包括所有可能的轨迹。典型的 RL 问题在计算上是难以处理的(否则我们可以只应用动态编程),所以我们需要使用轨迹的样本

事实上,我们基于有限数量的样本来计算一个近似梯度。幸运的是,我们已经建立了梯度也是一个期望值,我们可以用模拟来估计。表达式看起来是这样的:

基于有限数量的轨迹样本 m 的梯度近似

正如你所看到的,这个表达式是完全容易处理的。我们可以对轨迹和相应的回报进行采样,即使我们不知道环境模型。我们所需要的是一个明确定义的政策,可微分的 w.r.t. θ。

为了简单起见,我将在本文的剩余部分继续讨论渐变,但是请记住,我们实际上使用的是近似 渐变

九。定义更新规则

一旦我们计算了(近似的)梯度,我们如何应用它来更新我们的策略?

基于采样观察,我们希望逐渐更新策略参数。为此,我们定义了一个学习率α∈(0,1),表示放在计算的梯度上的权重,以更新现有的参数向量。相应的更新规则如下所示:

策略梯度更新规则。新的政策参数是旧参数和目标函数梯度的加权组合。

通常,更新规则由θ应改变的增量表示:δθ=α∇_θj(θ)。

记住梯度只是偏导数的向量。因此,更新规则为θ中的每个元素提供了唯一的权重更新。通过这种方式,更新与单个功能的影响保持一致。

X.示例:Softmax 和 Gaussian 策略

我们已经达成了一个明确的更新规则,但如果你对所有的曲折感到有点茫然,这是可以理解的。

让我们看看是否可以将我们的抽象结果具体化,为离散和连续动作空间提供规范的策略。

一些符号。设ϕ(s,a)是基函数的向量(例如,从状态动作对导出的解释变量的向量)。如前所述,θ是相应的一组参数,可以解释为每个变量的权重。我们假设一个简单的线性函数ϕ(s,a)^⊤⋅θ——注意,我们可以很容易地用θ参数化的神经网络来代替它。

对于离散动作,最常用的是 softmax 策略。其定义如下:

Softmax 政策。这个策略通常用于离散的动作空间

其梯度(此处的推导)如下:

softmax 策略的梯度

结果可以解释为所有动作的观察到的特征向量(来自采样动作)减去预期的特征向量。因此,如果奖励信号很高,并且观察向量与预期向量相差很大,则提供了更新该动作的概率的强烈动机。

对于连续动作空间,高斯策略最常见。这里,我们从参数化的正态分布中绘制动作。分布的平均值由μ_θ=ϕ(s,a)^⊤⋅θ.表示

高斯政策。这个策略通常用于连续动作空间

相应的梯度:

高斯政策的梯度

同样在这里,可以看出,在高回报的情况下,远离平均值的行为触发了强烈的更新信号。由于概率总和必须为 1,增加某些轨迹的概率意味着减少其他轨迹的概率。因此,调整政策会影响预期回报。

XI。损失函数和自动梯度计算

尽管策略需要是可微分的,并且梯度可以使用微积分来计算,但是手动计算偏导数是相当麻烦的。特别是当策略是深度神经网络时——其中θ表示网络权重——我们通常依赖于自动梯度计算

对于自动梯度,我们只需定义一个损失函数并让计算机求解所有导数。损失函数有效地代表了更新信号。我们添加一个负号(因为训练依赖于梯度下降而不是- 上升),并定义规范损失函数如下:

策略梯度算法的损失函数。大多数实现都提供了自动微分,这样就可以计算出梯度。

十二。算法实现(加强)

本文提供的信息解释了似然比策略梯度方法的背景,如 Williams 的经典加强算法

让我们把这些放在一起:

加强算法,也称为普通策略梯度或似然比策略梯度[图片由作者提供,基于 Williams (1992)]

虽然这需要一些数学知识,但实际的算法是简洁明了的。我们所需要的只是抽样奖励和我们政策的梯度。

Python 实现(例如,使用 TensorFlow 定义的策略网络)也不需要太多代码——查看我下面的文章中关于连续和离散变体的例子。

如果你想更进一步,也可以阅读我关于自然梯度和 TRPO 的文章,它们构成了当代政策梯度算法的基础:

如果你成功了,恭喜你!掌握策略梯度算法需要一些时间,但一旦掌握,它们就为混合方法(如行动者-批评家方法)以及更高级的方法(如近似策略优化)打开了大门。因此,他们的理解对任何 RL 从业者都是至关重要的。

摘要

  • 给定政策下的预期回报由国家行动轨迹乘以相应回报的概率来定义。似然比政策梯度通过增加高回报轨迹的概率,部署由θ参数化的随机政策,建立在这一定义之上。
  • 我们可能不知道环境的转变和回报功能。然而,在经过对数变换之后,我们使用的是加法(对数)概率,而不是概率的乘法。这种转换将策略从(可能未知的)状态转换功能中分离出来。
  • 如果我们有一个关于它的参数化θ的可微策略,我们可以计算它的梯度。因为我们可以将这个梯度表示为一个期望值,所以我们可以使用模拟来近似它。典型的 RL 实现利用损失函数,从损失函数自动导出梯度。
  • 在诸如加强的算法中,我们从环境中采样转换和回报(使用随机策略),并将轨迹回报乘以对数策略的梯度来更新参数θ。

进一步阅读

加固算法

威廉姆斯,R. J. (1992 年)。联结主义强化学习的简单统计梯度跟踪算法。机器学习8 (3),229–256。

不同政策梯度方法解释

里德米勒,m .,彼得斯,j .,,S. (2007 年 4 月)。在 cart-pole 基准上评估政策梯度方法和变体。在 2007 年 IEEE 近似动态规划和强化学习国际研讨会上(第 254–261 页)。IEEE。

有限差分、似然比和自然政策梯度的描述

http://www.scholarpedia.org/article/Policy_gradient_methods

政策梯度介绍(许宗盛)

https://Jonathan-hui . medium . com/rl-policy-gradients-explained-9b 13 b 688 b 146

政策梯度推导(Chris Yoon)

https://medium . com/@ thechrisyoon/derivating-policy-gradients-and-implementing-reinforce-f 887949 BD 63

对数导数技巧的解释(大卫·迈耶):

https://davidmeyer.github.io/ml/log_derivative_trick.pdf

似然比政策梯度(戴维·迈耶):

https://David Meyer . github . io/ml/policy _ gradient _ methods _ for _ robotics . pdf

政策梯度定理的证明和大量的政策梯度算法(Lilian Weng):

https://lilian Weng . github . io/posts/2018-04-08-policy-gradient/

似然比梯度(蒂姆·维埃拉):

https://timvieira . github . io/blog/post/2019/04/20/the-likelihood-ratio-gradient/

Sergey Levine(伯克利)关于政策梯度的演讲幻灯片

http://rail . eecs . Berkeley . edu/deeprlcourse-fa17/f17 docs/lecture _ 4 _ policy _ gradient . pdf

David Silver (Deepmind)关于政策梯度的演讲幻灯片

https://www . David silver . uk/WP-content/uploads/2020/03/pg . pdf

彼得·阿比尔的 4A 深度 RL 训练营演讲:政策梯度

伯克利政策梯度讲座

David Silver 的 RL 课程——第 7 讲:政策梯度方法

关于政策梯度的 DeepMind 讲座

多态性:理解在 Scikit-Learn 中创建自定义类(如自定义转换器)背后的概念

原文:https://towardsdatascience.com/polymorphism-understand-the-concept-behind-creating-custom-classes-like-custom-transformers-in-eb9d8b4f5e30

曾经想知道为什么一些 Python 类从无到有的地方调用方法吗?还是实现一些方法只是为了过关?

如果你曾经遇到过 Scikit-Learn 定制转换器,你很可能对上面的现象很熟悉。如果是这样的话,这篇文章是给你的。我们将深入研究支持这种行为的叫做多态性的概念,并且我们将构建一些定制类来获得一些实践经验和更深入的理解。

照片由Meagan car science在 Unsplash 上拍摄

Scikit-learn transformers 是为生产中的数据准备建立管道的一套很棒的工具。虽然内置的转换器列表非常详尽,但是构建您的定制转换器是自动化定制特性转换和实验的一个很好的方式。如果您曾经使用过 scikit-learn transformer,您很可能会遇到以下常用模式:

# defining a custom transformer
    class CustomTransformer(BaseEstimator, TransformerMixin):
        def fit(self, X, y=None):
            return self  
        def transform(self, X):
            ...... # calling fit_transform() 
  customTransformer = CustomTransformer()
  data = customTransformer.fit_transform(data)

但是如果你来自非编程背景,那么fit_transform()方法没有在CustomTransformer类中定义,却可以从该类中调用,这似乎有点令人困惑。此外,如果您已经知道该方法来自上面的某个类,但是想到它如何能够使用没有在它所属的同一个类中定义的方法,您可能会感到困惑。例如,在官方 GitHub 库这里中检查TransformerMixin类的脚本,你不会在TransformerMixin类中找到任何为fit()transform()定义的方法。

在本文中,我们将尝试理解这些概念:多态和鸭类型,这些概念支持这些行为。我们还会做一些动手练习来加深理解。

什么是多态性?

一般来说,多态性意味着一个对象采取不同形式的能力。一个具体的例子是,不同类的对象包含相同的方法,但表现出不同的行为。

例如,在 Python 中,我们可以运行类似于1+2'a' + 'b'的操作,并分别得到3ab的结果。Python 在幕后调用了一个已经在 string 和 integer 类中实现的名为__add__()魔法方法。关于 Python 如何将这些核心语法转换成特殊方法的详细信息,请查看我上一篇关于 Python 核心语法的文章。

https://betterprogramming.pub/python-core-syntax-and-the-magic-behind-them-3c912985b87c

这个神奇的方法——__add__()是多态性的一个例子——它是同一个方法,但是根据调用它的类对象,它调整它的行为,从累加数字到连接字符串。

在 Python 类上下文中,我们可以通过两种方式实现多态性:继承和 Duck 类型化。

遗传多态性

继承在面向对象的编程环境中,意味着我们从另一个类继承或接收类属性。我们继承的类叫做超类,我们继承的属性叫做子类。因为这篇文章的重点不是继承,我们将跳到一个例子中,希望这个概念在我们进行的过程中有意义。

但是如果他们不知道或者你需要快速复习,请随意阅读我以前关于理解继承和子类的文章。

对于我们的例子,我们将创建一个名为InheritList的超类和三个子类:DefaultListEvenListOddList来运行继承和多态的例子。

示例超类 01

示例子类

遗产

在上面的代码块中,注意我们没有在DefaultList类中实现任何方法。请注意,在下面的代码块中,我们还可以从该类创建的实例中调用方法(例如add_value()get_list())。因为DefaultList子类从它的超类- InheritList继承了这些方法。这是继承在起作用。nums = [1,2,3,4,5]

defaultNumList = DefaultList()[defaultNumList.add_value(i) for i in nums]print(f"List with all added values: {defaultNumList.get_list()}")​# removes the last item from the listdefaultNumList.remove_value()print(f"List after removing the last item: {defaultNumList.get_list()}")>>List with all added values: [1, 2, 3, 4, 5]
>>List after removing the last item: [1, 2, 3, 4]

上面的例子展示了基本的继承——我们从超类中获得所有的属性,并按原样使用它们。但是我们可以改变或更新子类中继承的方法,就像我们在其他两个子类中所做的一样— EvenListOddList

方法覆盖

EvenListOddList类中,我们修改了remove_value()方法,这样EvenList类将从构建的列表中移除所有奇数值,而OddList将移除所有偶数值。通过这样做,我们将引入多态性——其中remove_value()在两种情况下会有不同的表现。

演示:方法重写

>>evenNumList with all the values: [1, 2, 3, 4, 5]
>>evenNumList after applying remove_value(): [2, 4]

>>oddNumList with all the values: [1, 2, 3, 4, 5]
>>oddNumList after applying remove_value(): [1, 3, 5]

鸭分型多态性

在详细介绍 Duck 类型之前,让我们先谈谈在超类InheritList中实现的另一个方法do_all()。它接受一个值作为输入,将其添加到列表中,从列表中删除不需要的值,并返回最终的列表。要完成所有这些任务,需要依靠其他内部方法:add_value()remove_value()get_list()。看看下面的演示。

print(f"evenNumList after calling do_call(58): {evenNumList.do_all(58)}")print(f"oddNumList after calling do_call(58): {oddNumList.do_all(55)}")>>evenNumList after calling do_call(58): [2, 4, 58, 58]
>>oddNumList after calling do_call(58): [1, 3, 5, 55, 55]

但是 Python 允许我们更灵活地实现这一点。例如,我们可以从超类中完全移除remove_value()方法,创建一个单独的只有combine_all()方法的类,并且仍然能够毫无问题地使用它。全拜鸭子打字所赐!

基本上,我们不关心依赖属性是否来自同一个类。只要依赖属性可用,我们就很好。这基本上反映了代表鸭子类型的广泛使用的引用:

“如果它像鸭子一样走路,像鸭子一样游泳,像鸭子一样嘎嘎叫,那么它很可能就是一只鸭子。”

为了演示,让我们创建一个名为ComboFunc的新类,它只有一个方法combine_all(),这个方法将执行与do_all()方法相同的功能。同样,让我们创建一个新的子类,它将拥有一个先前创建的子类— EvenList和这个新类作为超类。

注意,我们没有在这两个类中定义任何依赖方法(add_value()remove_value()get_list())。然而,我们将能够成功地从GenDuckList类的实例中调用combine_all()方法。因为依赖方法将从EvenList类继承而来,而combine_all()方法并不关心它们来自哪里,只要它们存在。

>>Initial list: [1, 2, 3, 4, 5]
>>Final list: [2, 4, 40]

请注意,我们也可以通过其他方式完成上述任务,

  1. 如果我们需要定制的东西,我们也可以完全避免从EvenList类继承任何东西,并在类内部实现依赖方法。或者,
  2. 我们可以把它作为一个超类,但覆盖任何特定的依赖方法,使它更加定制化。总的来说,多态性让我们变得更加灵活,可以轻松地重用已经实现的方法。或者,
  3. 我们可以从超类中移除remove_value()并在我们的GenDuckList类中实现它,但仍然能够执行相同的任务。

因此,为了完成这个循环,当我们使用BaseEstimatorTransformerMixin类作为超类在 scikit-learn 中构建自定义转换器时,我们基本上应用 duck typing 来实现多态。与之相关的是,您可以将GenDuckList视为一个虚拟的定制 transformer 类,ComboFunc视为一个虚拟的TransformerMixin类,EvenList视为一个虚拟的BaseEstimator类。上面的 duck 类型化示例和开始的 transformer 示例之间的实现级别差异是,我们从一个超类继承了remove_value()方法,而在 custom transformer 中,我们在 custom 类中定义它——上面提到的第三种替代方式。

感谢您阅读这篇文章。希望它能帮助您理解 Python 类上下文中多态性的概念。如果你喜欢这篇文章,请考虑关注我的个人资料,以获得关于我未来文章的通知。

https://levelup.gitconnected.com/use-modules-to-better-organize-your-python-code-75690ba6b6e https://levelup.gitconnected.com/use-modules-to-better-organize-your-python-code-75690ba6b6e

多项式自回归:在 2 分钟内改善你的预测

原文:https://towardsdatascience.com/polynomial-autoregression-improve-your-forecasts-in-2-minutes-746d8b57d896

完全初学者的非线性时间序列+代码

by sebo106

在人工智能和自动化解决方案的时代,人们相对容易忘记经常产生可比结果的简单技术。你还记得非线性回归吗?

虽然它因其漂亮的分析公式而在学术界非常受欢迎,但自从机器学习爆发以来,它并没有获得太多的关注。尽管我 100%确定你不是真的喜欢数学,但我会告诉你一个非常简单的方法来提高你的时间序列预测的准确性,只需多花两分钟的时间。

有很多看起来很丑的总结,但是如果你对 r 过敏,你可以跳过其中的大部分。

如果你对非线性时间序列完全陌生,我强烈建议你从对整个主题的直观介绍开始:

多项式回归

如果你熟悉多项式回归,你可以跳过这一部分。

让我简单介绍一下多项式回归背后的思想。线性回归之所以这样叫,是因为它反映了数据之间的线性关系。当数据之间的关系不是线性的时,使用多项式回归。让我们稍微想象一下:

按作者,用 NumPy 生成

如果我们想要对左边的数据建模,我们将使用标准线性回归:

这显然是我们在学校都学过的经典f(x)=ax+b函数。第二张图显示了二次关系。这意味着,我们的回归函数将扩展到:

这个方程的参数α可以用简单的 OLS 很容易地估计出来——这正是我们下一步要做的!

认真工作

我认为用 R 来做是最舒服的,然而,我要用的所有工具在 Python、Julia 或其他什么语言中都很容易访问。在我们开始之前,有一个问题我们必须回答:

我们如何知道我们的时间序列是非线性的?

虽然我们可以做一些奇特的统计测试,但我保证这个故事是针对完全初学者的,所以我们保持简单。我希望你们使用的工具非常简单——这是一个滞后图:

data <- data.frame(y=log10(datasets::lynx))  #log10 for statistical reasons

{par(mfrow=c(2,2))
  scatter.smooth(lag(train$y, 1), train$y, span = 2/3, degree = 1,
                 family = c("symmetric", "gaussian"), evaluation = 50, xlab = "t-1", ylab = "t")
  scatter.smooth(lag(train$y, 2), train$y, span = 2/3, degree = 1,
                 family = c("symmetric", "gaussian"), evaluation = 50, xlab = "t-2", ylab = "t")
  scatter.smooth(lag(train$y, 3), train$y, span = 2/3, degree = 1,
                 family = c("symmetric", "gaussian"), evaluation = 50, xlab = "t-3", ylab = "t")
  scatter.smooth(lag(train$y, 4), train$y, span = 2/3, degree = 1,
                 family = c("symmetric", "gaussian"), evaluation = 50, xlab = "t-4", ylab = "t")
}

按作者

虽然第一个滞后看起来是线性的,但从第二个滞后开始,我们可以清楚地看到非线性关系正在发生。这是使用非线性模型的充分理由。记住——不要在没有看到滞后图的情况下进行时间序列分析!忽略这一点,你可能会被错误地识别。

让我们从正确的数据准备开始建模。为了包含非线性项,我们需要添加多项式特征,然后确保它们都具有相似的方差。我们将通过标准化来实现:

th <- 81
for (i in 1:4){ 
  data[paste0("L", i)] <- Hmisc::Lag(data1$y, i) #here we are adding lags
  data[paste0("L.2.", i)] <- Hmisc::Lag(data1$y, i)^2 #from here on we are adding polynomial lags
  data[paste0("L.3.", i)] <- Hmisc::Lag(data1$y, i)^3
  data[paste0("L.4.", i)] <- Hmisc::Lag(data1$y, i)^4
}

data <- data %>% mutate_at(names(data), ~(scale(.) %>% as.vector))  #standarization
data <- na.omit(data)  #get rid of the NAs
boxplot(data)

按作者

看起来效果不错。现在,我们将数据集分为训练集和测试集,我们将训练 ARIMA 模型:

train <- data[1:80,]
test <- data[th:110,]

ar <- forecast::auto.arima(train$y)
summary(ar)
Series: train$y 
ARIMA(4,0,1) with zero mean 

Coefficients:
         ar1     ar2      ar3      ar4     ma1
      0.3917  0.4609  -0.4490  -0.2687  0.9339
s.e.  0.1164  0.1346   0.1244   0.1102  0.0619

sigma^2 = 0.1606:  log likelihood = -39.59
AIC=91.18   AICc=92.33   BIC=105.47

Training set error measures:
                      ME      RMSE       MAE     MPE     MAPE      MASE        ACF1
Training set -0.03339086 0.3880593 0.3254522 3.37014 107.7749 0.5795365 -0.01482778

ARIMA 模型的最大滞后是 4(为了简单起见,我将它作为最大滞后)。现在让我们训练我们的多项式模型。我们正在做的是:

  1. 创建一个包含所有变量+截距的线性回归模型
  2. 使用 AIC 网格搜索选择,以找出哪些变量要删除。这也可以通过其他功能选择来完成,但 AIC 是最受欢迎的一个,所以我现在不把它搞得太复杂了。
model <- lm(y ~ ., train)

model2 <- stepAIC(model, k=2) # k=log(nrow(train)) - if you want to use BIC
summary(model2)
Call:
lm(formula = y ~ L1 + L.2.1 + L.3.1 + L.3.2 + L.4.2 + L.2.3 + 
    L.3.3 + L.4.3 + L.3.4, data = train)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.89423 -0.19751 -0.01561  0.24469  0.70938 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)   
(Intercept)  -0.04549    0.03999  -1.137  0.25921   
L1            5.97418    2.63906   2.264  0.02669 * 
L.2.1       -11.85976    5.75545  -2.061  0.04306 * 
L.3.1         7.11220    3.13333   2.270  0.02630 * 
L.3.2         1.39683    0.87505   1.596  0.11493   
L.4.2        -1.95094    0.79889  -2.442  0.01713 * 
L.2.3         7.10425    2.95244   2.406  0.01876 * 
L.3.3       -16.10242    6.24565  -2.578  0.01204 * 
L.4.3         9.00962    3.36456   2.678  0.00923 **
L.3.4        -0.17323    0.11475  -1.510  0.13564   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3505 on 70 degrees of freedom
Multiple R-squared:  0.8973, Adjusted R-squared:  0.8841 
F-statistic: 67.97 on 9 and 70 DF,  p-value: < 2.2e-16

你很容易注意到的有趣细节是,第一个延迟是唯一一个被认为是线性的——回到延迟图,你会明白为什么会发生这种情况。

现在,哪种模式更好?我们来做预测吧!

refit <- forecast::Arima(data$y, model=ar)
fcast <- predict(model2, test)

plot(test$y, type='l')
lines(refit$fitted[th:110], col='purple')
lines(fcast, col='red')

按作者

你可以很容易地看到,多项式 AR 只是更好地拟合数据集——它不像 ARIMA 的预测那样跳跃,特别是在第 15 次观察之后。它的 MSE 怎么样?

mean(sum((test$y - refit$fitted[th:110])^2))  #for ARIMA
mean(sum((test$y - fcast)^2))  #for Polynomial AR
ARIMA: 5.749183
Polynomial AR: 5.119633

如您所见,我们已经显著改进了样本外预测**,而没有向数据集添加任何额外信息**!

摘要

我刚刚展示给你的是可能是最简单同时也是最强大的非线性时间序列技术,你可以在两分钟内完成。然而,实际上,非线性有不同的类型——不幸的是,多项式无法涵盖所有类型,因为我相信你已经想到了。举例?没问题:

按作者,用 NumPy 生成

有人会认为这是撒旦的创造。其中一个可能是对的。即使一开始看起来无法解决,但还是有模型可以处理这种非线性。如果你对此感到好奇,请查看:

如果你有任何问题,请在评论中提问!你也可以通过 LinkedIn 联系我。

将链接到数据(GNU)。

多项式回归——神经网络的替代方案?

原文:https://towardsdatascience.com/polynomial-regression-an-alternative-for-neural-networks-c4bd30fa6cf6

多项式和神经网络的讨论,理论上它们都能无限逼近连续函数

使用三种不同次数的多项式对数据集执行多项式回归的示例。理论上,多项式可以实现无限紧密的配合。[图片来自维基媒体作者肖巴

首先,这更像是一篇讨论文章。虽然我将尝试比较多项式和神经网络,但没有大量的数学严谨性或文献支持来支持这篇文章。它也不是教程:这次没有 Python 实现或数值实验(至少我自己没有)。最后,我也不是试图说服你放弃神经网络,接受多项式回归。

说完这些,让我们说说我想用这篇文章做什么。几乎每个人都在生活中的某个时候学会了设计多项式,他们拟合任何函数的能力在很久以前就被证明了。添加这两个应该会产生有趣的用例,但是对数据科学中的多项式回归的关注是相当不深刻的。如果这篇文章成功地引发了一些思考,并恢复了对古老的多项式设计艺术的关注,那就足够了。

神经网络

先说神经网络,以及我们为什么喜欢它。从数学的角度来看,神经网络的巨大吸引力在于它们可以无限逼近任何连续函数。原则上,即使是简单的单层网络也能完成这项任务。是的,有严格的理论要求,你可能永远无法在实践中达到完美的契合,但它仍然是一个令人放心的属性。

【提供一些 泛逼近定理的背景*:George Cybenko(1989)证明了具有 sigmoid 激活函数的单层网络可以无限逼近任何连续函数。Hornik 等人(1989)表明,该结果也适用于多层网络中的其他激活函数。从那时起,定理已经扩展到其他方向,例如,任意层宽度和深度,各种激活函数,非连续函数等。同样,这些结果不能提供任何实际的保证,因为条件可能是不现实的。】*

神经网络的示例。多个层通常捕获数据集的特定特征。[图片由open clipbart在 FreeSVG 上提供]

我知道这不是对神经网络最详尽的描述,但是我们还有很多材料要介绍。我们继续吧。

多项式

另一类能够拟合几乎任何数据集的函数似乎几乎被遗忘了——至少在数据科学界——但与神经网络具有相似的属性(Stone-Weierstrass 定理)。正如你可能已经从标题中猜测到的,那就是多项式类**。**

很可能,你高中的时候有过一些多项式。然后,取决于你之后的方向,你可能研究了很多,也可能根本没有研究。无论如何,它们在当今的数据科学中似乎并不太受欢迎。

为了提供一个简短的复习,多项式被定义为:

由不定项(也称为变量)和系数组成的表达式,只涉及变量的加**、和非负整数取幂运算—维基百科**

多项式的一个例子可能是:

f(x) = (x − 3)(x − 2)(x − 1)(x)(x + 1)(x + 2)(x + 3)

相应的图如下所示:

7 次多项式的示例。高次多项式可以拟合非常复杂的模式。[图片来自维基媒体作者梅利坎普

请注意,多项式可以通过将各项相乘来写出。多项式总是可以改写成标准形式:

注意,虽然特征x^k, k>1是非线性的,但是作为统计估计问题是线性的**(特征的加权线性组合),并且可以用基本的线性回归技术来解决。这是一个强大的属性,使生活变得容易得多。**

在数据科学中,我们通常处理多个特征(例如,一个以上的解释变量,比如说x_1, x_2, x_3),转化为一个多变量多项式**。例如,像这样的术语可能代表一个特征:**

x_1x_2x_3²

或者如果你喜欢更具描述性的东西:

x_years_experience * x_salary_last_year * x_num_past_jobs²

这只是众多可能特征中的一个。可以想象,多项式很快变得相当混乱和复杂。两个方面决定了多项式的复杂性:

  • 变量的数量。表达式2x_1²x_2⁴ - 3x_1x_2² + x_1 + 8包含两个解释变量,x_1x_2。具有两个变量的多项式可以在 3D 绘图中可视化,在更高的维度上,我们人类的大脑往往会有所欠缺。
  • 多项式的次**。这是标准形式表达式中的最高幂(乘以变量时指数的总和)。例如,在2x_1²x_2⁴ - 3x_1x_2² + x_1 + 8示例中,度数将是 6(即 2+4)。**

此时,您可能会发现一个潜在的问题。带有一个变量的 5 次多项式(如w_5x⁵ + w_4x⁴ + w_3x³ + w_2x² + w_1x + w_0)似乎是可行的,带有五个变量的 1 次多项式(如w_1x_1 + w_2x_2 + w_3x_3 + w_4x_4 + w_5x_5 + w_0)也是可行的。

但是,如果我们同时增加次数和变量的数量,特征的数量可能会爆炸。当我们转向像x_1⁴x_2x_3³x_4²这样的装置时,我们意识到有很多高度多变量特征我们可以设计来捕捉复杂的交互。首先,我们不知道哪些特性是相关的——不管我们有多少领域知识。在精神上,我们可以概念化基本的相互作用和二次效应,但除此之外就没什么了。

总之,我们有两个问题要处理:(1)包括哪些特征,以及(2)我们应该在多大程度上捕捉相关效应。在面对这些挑战之前,让我们暂时回到神经网络。

多项式和神经网络之间的联系

我们很少费心以数学形式明确写出神经网络,但我们可以。本质上,每个隐藏节点将前一层的加权组合作为输入,根据某个激活函数(例如,sigmoid 或 ReLU)对其进行变换,并将输出传递给下一层。

就像一个高次多项式取一些初始解释变量x_1,x_2,x_3并嵌入到类似x_1²x_2x_3⁴的复杂特征中一样,一个神经网络也转换输入并隐式提取特征。你看这是怎么回事。根据加州大学和斯坦福大学的研究人员(Cheng et al .,2021),神经网络实际上是一类特殊的多项式。每一层都增加了多项式的复杂性。

如果激活函数是任何多项式,或者由一个多项式实现,则 NN 精确地执行多项式回归。

而且,多项式的次数会逐层递增。”—程等,2019

这是一个迷人的结果。事实证明,我们并没有真正讨论对立的解决方法。如果有什么不同的话,神经网络为多项式回归的总体解决方案类增加了特定的角度和概念化。

为了避免问题过于复杂:在剩余部分,我将继续使用术语多项式表示典型的多项式回归,术语神经网络表示神经网络。

【程等论文注:仅讨论简单人工神经网络;特殊口味,如循环、通用、卷积等。不包括在内。同样值得指出的是,这篇论文没有提供严格的数学证明。最后,到目前为止,这份手稿还没有被同行评审或发表。]

多项式回归-特征选择和数据拟合

回到多项式。我们现在知道了它们的样子,但是我们如何在数据科学环境中使用它们呢?为了解决这个问题,让我们假设我们使用一个给定的数据集来构建一个既适合训练数据又能很好地解释未知数据的模型。

虽然有很多方法可以将变量组合成高级术语,但是现在创建特性并不太复杂。虽然我不会在这里深入探讨,但是类似于sklearn.PolynomialFeatures的东西可以在一定程度上生成所有的特性。例如,对于两个变量和 2 度,我们将生成[1, x_1, x_2, x_1², x_1x_2, x_2²]

****拟合多项式本质上也不具有挑战性。例如,numpy.Polynomial功能包含许多常见的多项式(切比雪夫多项式、埃尔米特多项式、勒让德多项式等。)并且拟合是在一行代码中完成的:

output = Polynomial.fit(x_data, y_data, deg=3)

真正的挑战在于找到正确的特征子集**。也许x_1⁷x_2³x_3²证明了一个深刻的预测,或者也许x_1⁵x_2⁴x_3²是。即使对于更合理的程度,也经常存在如此多的可能性,以至于对所有模型(即,特征的组合)进行穷举搜索是不可行的。**

一些技术可能有助于处理大量特征,例如:

  • 主成分分析 (PCA)以在设计多项式之前降低特征集的维数;
  • ****卡方检验检验特征在预测结果中的显著性;
  • 决策树(或随机森林)选择成功特征;
  • 弹性网回归(结合套索和脊线)剔除低显著性特征。

我承认:不一定容易。然而,请记住,神经网络训练中的认真努力还需要特征选择、架构选择和超参数调整。这两种解决方法都不会立即产生一个完美的模型。

另一个挑战在于过度拟合**。一个 20 次多项式可能捕捉到许多特殊的效果、异常值等等。尽管我们不一定想要对训练数据有一个完美的拟合;我们希望这个模型能够很好地处理以前没有见过的数据。前述的弹性网有所帮助,避免高次多项式也是如此。与神经网络不同——神经网络也容易过度拟合——对于多项式回归,我们可以计算方差膨胀因子— ,因为我们仍在执行普通最小二乘(OLS)回归——当我们即将在模型中引入多重共线性时会发出警告。**

总之:我们有所有必要的工具来将复杂的多项式拟合到复杂的数据集中。这并不是说多项式是数据科学的灵丹妙药。这并不是说拟合一个合适的多项式将是一件轻而易举的事。它绝对没有说我们会找到理论上可以达到的完美匹配。这也不是令人难以置信的黯淡前景。

多项式和神经网络的优势

神经网络的最大好处之一是它们可以有效地捕捉层次结构。[照片由尤金·特卡琴科在 Unsplash 拍摄]

多项式的起源可以追溯到几个世纪以前;到目前为止,它们已经被很好地研究和理解了。显然,它们确实有一些缺点——否则,一开始就没有人会费心研究新技术。神经网络在该领域赢得了应有的地位。

也许神经网络最大的吸引力在于它们捕捉层级效应的能力。这是他们在计算机视觉上工作得如此好的原因之一,特定的层专用于大形状、小形状、颜色等。由于分层架构,他们通常能够很好地概括**,引入处理新数据所需的归纳偏差。**

此外,神经网络在许多方面都便于使用。是的,当网络变得更先进时,你必须研究层和节点的数量、学习速率和额外的超参数。找到合适的架构需要时间和精力。

然而,就功能设计而言,很多工作是从你手中拿走的。基本上,你输入一些解释变量,网络就会自己计算出相关的相互作用和高阶效应。有了所有可用的库和复杂的梯度下降算法**,定制一个神经网络是完全可行的。**

相比之下,对于多项式回归训练本身是直接的(基本回归)。挑战在于选择合适的特征并将它们组合成一个合理的模型。这里有一个小小的帮助,就是前面提到的多重共线性警告**,它比神经网络更容易避免过度拟合。离群点检测有点问题。在线性回归中,离群值很容易识别;在这里,它们可能被认为是某种奇怪的非线性模式的一部分。**

在库的帮助下,这两种解决方法现在都很容易管理。拟合多项式不需要成为数学家,训练神经网络也不需要成为机器学习工程师。

一些结果(来自程等人)

最终,相关的问题是‘哪一个更好?’。希望你不要期望在这里找到一个结论性的答案。**

然而,值得注意的是,Cheng 等人(2019)在多个数据集(分类和回归任务)上比较了两者的性能。有趣的是,他们发现多项式回归的表现与神经网络一样好,甚至更好。此外,他们的实验表明多项式很少需要高于二阶或三阶。因此,它们也是可以解释的。

“由于这个和其他原因,例如数据边缘的大拟合值,许多作者建议不要使用高于 2 或 3 次的多项式模型,事实上,在本文的实证实验中,我们很少发现有必要使用更高的次数。”—程等,2019

直觉上,这是有道理的。相互作用效应在高阶时会急剧减弱。将两个或三个变量相乘会产生相关的见解,捕捉线性和二次效应也是如此。但是在现实生活中,我们真的经常遇到有意义的 20 次方效应或十几个变量之间的相互作用吗?如果有什么不同的话,这个结果意味着神经网络对于手头的任务来说经常是不必要的复杂,导致了臭名昭著的过度拟合效应。

“最重要的是,我们已经表明,公关应该是一个有效的替代无核武器国家。我们使用各种类型的各种数据进行了实验,在所有情况下,PR 的性能与 NN 相似,或者明显优于 NN,而没有 NN 试图找到调整参数的良好组合的麻烦。

在某些情况下,PR 实际上优于 nn 的事实反映了 nn 通常被过度参数化的事实,本质上是拟合比它们应该拟合的更高次的多项式。”—程等,2019

尽管测试了各种数据集,但声称多项式回归优于神经网络拟合还是言过其实。当然——特别是在充分微调的情况下——很容易找到神经网络优于多项式回归的反例。我还怀疑多项式在完成超级马里奥游戏或识别人脸(即更专业化的神经网络)方面是否同样成功。

对于典型的数据科学任务——估计工资、预测旅行时间、按流派对歌曲进行分类——多项式显然比神经网络更合理。由于现实的过度简化,线性回归往往不尽人意,然而深度神经网络可能会寻找根本不存在的高度复杂的模式。

结束语

正如本文开头所提到的,这更多的是一篇讨论文章。它不打算是完整的或严格的,也不主张多项式回归取代神经网络。

尽管如此,看到多项式回归与神经网络的竞争有多激烈肯定是很有趣的。后者取得的进展非常有趣,但大多数数据科学任务都不在一般的 DeepMind 挑战水平上。

虽然深度学习对许多人有强烈的吸引力,但它是否总是最适合这项工作的方法是值得怀疑的。多项式提供了非常丰富的现实表现;即使低度数和交互效果也能捕捉大部分真实世界的效果。在那种形式下,它们相对紧凑和全面,避免了神经网络容易遭受的【黑箱】效应。

现代工具包消除了过去多项式回归遇到的许多实际问题。理论上,多项式可以实现与神经网络相同的“完美”拟合,同时只需要比基本线性回归多一点的知识。

总而言之,也许是时候给多项式第二次机会了。

参考

Cheng,x .,Khomtchouk,b .,Matloff,n .,& Mohanty,P. (2019)。多项式回归作为神经网络的替代。 ArXiv 预印本。https://arxiv.org/abs/1806.06850v2

乔治·西本科(1989 年)。sigmoidal 函数的叠加逼近。控制、信号与系统的数学2 (4),303–314。

霍尼克,k .,斯廷奇科姆,m .,,怀特,H. (1989)。多层前馈网络是通用逼近器。神经网络2 (5),359–366。

M. H .斯通(1948 年)。广义维尔斯特拉斯逼近定理。数学杂志21 (5),237–254。

魏尔斯特拉斯,K. (1885)。因此,分析师们将库利尔的工作放在一个更低的位置。柏林科学研究院院长会议2 ,633–639。

**https://en.wikipedia.org/wiki/Polynomial https://en.wikipedia.org/wiki/Polynomial_regression https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html **

Python 中的多项式回归

原文:https://towardsdatascience.com/polynomial-regression-in-python-dd655a7d9f2b

对非线性数据使用更复杂的回归方法

叶夫根尼·特卡琴科在 Unsplash 上的照片

介绍

传统的线性回归是一种广泛使用的统计工具,用于确定两个变量之间的线性关系,使分析师能够做出推断,并从数据中提取良好的洞察力,包括预测。

然而,并不只有线性数据。并非所有数据集都具有线性模式。有些情况下,它们*快到了,*但是我们需要进行转换来“帮助”它们适应线性算法。

一种可能性是幂变换,例如,使二次或三次方程表现得像线性方程一样。通过向数据添加一个变换层,我们可以更好地拟合它,正如我们将要看到的。

多项式

在数学中,多项式是由变量(x,y,z)和系数(乘以变量的数字)组成的方程。

简单的线性回归是一次多项式,其中我们有乘以变量 x 的系数,简单明了。你一定见过很多次了,下面是简单的线性回归公式。

第二、第三或第 n 次多项式类似,但在这种情况下,系数乘以变量的二次、三次或第 n 次幂。例如,在下面的二次公式中,β乘以平方变量,β1 乘以非平方变量。因为这里的最高幂是 2,所以多项式是二次的。如果我们有一个三次变量,它将是 3 次,以此类推。

很好。现在我们知道如何识别多项式的次数。让我们继续,看看 it 在数据中的影响。

数据有哪些变化?

查看我们的数据图以了解其形状以及线性回归如何拟合是很重要的。或者,更好的是,如果它是最合适的。

我们来看不同次数多项式的形状。

不同次数多项式的曲线形状。图片由作者提供。

观察到每个度数的增加使得数据产生更多的曲线。度 1 是一条线,正如所料,度 2 是一条曲线,之后的其他是“S”形或其他曲线。

知道数据不再是一条直线,使用简单的线性回归就不太合适了。取决于曲线有多亮,你仍然可能得到一些有趣的结果,但是总会有一些点非常偏离。

让我们看看如何处理这些情况。

多项式特征

Scikit-Learn 有一个类名PolynomialFeatures(),用于处理线性回归拟合高次多项式的情况。

事实上,它所做的是转换你的数据,有点像在数据上添加一个,帮助LinearRegression()算法识别所需的正确曲线度数。它以我们需要的度数计算分数。

二次数据

让我们从二次方程开始。我们可以创建一个数据集。

# Dataset
X = 8 * np.random.rand(500, 1)
y = 1 + X**2 + X + 2 + np.random.randn(500,1)# Plot
plt.scatter(X,y, alpha=0.5);

二次方程。图片由作者提供。

让我们导入所需的模块。

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures

接下来,我们可以拟合一个线性模型。只是为了展示发生了什么。

# Linear Regression
linear_model = LinearRegression().fit(X,y)
preds = linear_model.predict(X)

这将产生接下来的情节。

二次多项式拟合的线性回归。图片由作者提供。

嗯……有几次我们会是正确的,其他时候我们会很接近,但是大多数预测不会太好。我们来评价一下模型。

# y_mean
label_mean =  np.mean(y)
print('label mean:', label_mean )# RMSE
rmse = np.sqrt( mean_squared_error(y, preds))
print('RMSE:', rmse )# % Off
print('% off:',rmse/label_mean)**[OUT]:** label mean: 26.91768042533155 
RMSE: 4.937613270465381 
% off: 0.18343383205555547

平均打八折。如果我们给它linear_model.score(X,y)打分,我们会得到 94%的 R。

现在,我们将转换数据以反映二次曲线,并再次拟合模型。

# Instance
poly2 = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly2.fit_transform(X)# Fit Linear model with poly features
poly_model = LinearRegression().fit(X_poly,y)
poly_pred = poly_model.predict(X_poly)# Plot
plt.scatter(X,y, alpha=0.5)
plt.plot(X, poly_pred, color='red', linestyle='', marker='.', lw=0.1);

这就是结果(99% R)。

拟合二次数据的二次线性回归。图片由作者提供。

哇!现在看起来真不错。我们将对其进行评估。

label mean: 26.91768042533155 
RMSE: 1.0254085813750857 
% off: 0.038094240111792826

我们将误差降低到平均值的 3%方差。

测试多重转换

我们可以测试多个变换,看看拟合值的效果如何。你会注意到,随着我们越来越接近函数的次数,曲线越来越符合这些值。有时,它甚至会过度拟合数据。

我们将创建这个函数,它采用解释变量( X )和响应变量( y ),并通过管道运行数据,该管道在一个循环中为用户指定的值拟合不同程度的线性回归,并绘制结果。我将把这个函数放在我的 GitHub 库中。

fit_polynomials(X2, y2, from_= 1, to_= 4)**[OUT]: Results for each (degree, R²)** [(1, 0.042197674876638835),  
 (2, 0.808477636439972),  
 (3, 0.8463294262006292),
 (4, 0.9999999996536807)]

请注意,我们开始时拟合度很差,只有 4%的 R,结束时模型拟合度非常好,达到了 99%。

黑点是真正的 y。红点非常适合它们。图片由作者提供。

请看上图,其中 1 度(蓝点)功能确实非常关闭,4 度(红点)可能是过度拟合的模型(真正的 Y 是黑点)。此图显示了多项式变换拟合指数数据的能力。

我们应该注意的另一件事是,如果我们从PolynomialFeatures开始不断增加degree参数,数据会变得越来越过度拟合,直到对于非常高的值,它会开始降低分数,因为它会更适合噪声,而不是数据。

fit_polynomials(X2, y2, from_=10, to_=30, step=10)**[OUT]: Results for each (degree, R²)** [(10, 0.9999999996655948),
 (20, 0.981729752828847), 
 (30, 0.9246351850951822)]

我们可以看到,随着我们的增加,R 在下降,点不再适合。

较高的度数值会过度拟合,并且可能会模拟噪波。图片由作者提供。

在你走之前

这是 Scikit 的另一个好工具——Learn。PolynomialFeatures()

它用于将非线性数据转换为可以通过线性回归建模的新数据。

随着度数的增加,回归线越来越适合数据。

如果你喜欢这个内容,请关注我的博客。

http://gustavorsantos.medium.com/

如果你正在考虑加入 Medium 成为会员,这是我的推荐代码,其中部分价值与我分享,所以你也可以激励我。

在 Linkedin 上找到我。

参考

https://en.wikipedia.org/wiki/Polynomial https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html

总体与样本:我们真的能看到一切吗?

原文:https://towardsdatascience.com/population-versus-sample-b3199d6f1521

Python 中的统计实验

照片由岩田良治在 Unsplash 上拍摄

我们大多数人在学校或大学已经学过概率论和统计学。我发现这门学科的难点在于,尽管在实际应用中非常有用,但理论很难理解。其中一个原因是,更仔细地观察一些引入的概念会引出一些近乎哲学的问题。统计学中的人口是什么?基本上我们想看的所有东西。什么是一切?真的能什么都看吗?

第二个挑战是概念的堆积。从随机实验中抽取样本本身就是随机实验。来自总体的样本也遵循概率分布,这与我们想要在总体中得到的统计分布无关。绕过背后苛刻理论的一个方法就是进行实验。这就是我们要做的,来更好地理解总体与样本。

填充我们的国家

我们从产生 100 万居民开始。我们想看看不同的理论分布:

  • 对于正态分布,我们以身高为例。我国人民的平均身高为 176 厘米,标准差为 6 厘米。
  • 为了均匀分布,我们所有的居民扔一个六面骰子,记住结果。
  • 对于指数分布,我们假设下一条消息的平均等待时间为 4 分钟。

注意,我们只使用 NumPy,这使得操作速度极快,内存开销很小。NumPy 还提供了大量的统计功能,因此不需要使用像 Pandas 这样更复杂的结构。

我们可以在一台简单的笔记本电脑上用几行 NumPy 代码轻松填充我们的国家。

population_size = 1000000population_normal = np.random.normal(loc=176, scale=6, size=population_size)population_uniform = np.random.choice(6, size=population_size) + 1population_exp = np.random.exponential(4, size=population_size)

看看我们的国家

当我们的国家人口增加后,我们可以很容易地看到整个人口;我们甚至可以立即进行人口普查,从而获得真实的人口参数的平均值和方差。请注意,当谈论现实中的实际人口时,这实际上是不可能的。一般来说,做一次全面的人口普查既费时又费力。这就是为什么我们需要统计数据。

请注意,尽管我们有一个相当大的百万人口,我们人口中的参数均值并不完全是我们在随机生成人口时指定的。随机发生器给了我们这些偏差。世界并不完美,即使在电脑里也是如此。

print("Population Mean (normal): %.10f / Population Variance (normal): %.10f" % 
      (np.mean(population_normal), np.var(population_normal)))print("Population Mean (uniform): %.10f / Population Variance (uniform): %.10f" % 
      (np.mean(population_uniform), np.var(population_uniform)))print("Population Mean (exponential): %.10f / Population Variance (exponential): %.10f" % 
      (np.mean(population_exp), np.var(population_exp)))Population Mean (normal): 175.9963010045 / Population Variance (normal): 35.9864004931
Population Mean (uniform): 3.5020720000 / Population Variance (uniform): 2.9197437068
Population Mean (exponential): 4.0014707139 / Population Variance (exponential): 15.9569737141

大量的统计数据

现在我们开始实际做统计。我们不是观察整个人口,而是取样。与挨家挨户询问 100 万人相比,这是获得大量人口信息的更现实的过程。

最重要的是要明白,每个样本都是随机实验。理想情况下,样本是完全随机的,以避免任何偏差。在我们的随机实验中,情况就是这样。在现实生活中,这种情况更为罕见。除了如果不小心的话可能会引入偏差之外,这并不重要:每个样本都是随机实验。

设计实验

我们实验的目的不在于获得人口统计数据,而主要是为了理解整个过程。因为我们有非常有效的方法来获得关于我们的人工种群的信息,所以我们可以做很多实验。

首先,我们将使用三种不同的样本规模:10 人、100 人和 1000 人。请注意,即使是最大的样本也只占总人口的 1%,这让我们节省了 99%的精力。因为我们想了解我们的随机样本在统计上代表我们的总体有多好,所以我们将不止询问一个样本,而是每个样本大小的 1,000 个样本。

在现实生活中,我们通常只取一个样本。这可能是我们一千个样本中的任何一个。如果我们观察 1,000 个样本的分布,我们可以得到一个提示,即选择具有特定统计结果的特定样本的概率。

实施实验

想法是在 Python 中使用相对简单的结构。它也只是一个用来玩概念游戏的笔记本。使用函数重构代码并减少笔记本中的复制和粘贴重用将是一个值得做的练习,但我没有做。

在设定了实验次数和样本量之后,我们把我们所知道的关于总体的所有信息打包到一个叫做分布的列表中。为了存储我们的实验结果,我们准备了 NumPy 数组,有足够的空间来保存我们所有实验的结果。我们需要 3 个分布乘以 3 个样本大小的 NumPy 数组。现在我们感兴趣的是随机实验的均值和方差。

在所有这些准备工作之后,它只是一组嵌套的 for 循环来完成所有的工作。再次使用 NumPy 的好处是代码非常快,因为我们可以通过调用 NumPy 函数来完成所有困难的工作。

no_experiments = 1000
sample_sizes = [10, 100, 1000]
distributions = [population_normal, population_uniform, population_exp]
# initialize the lists of lists using np.zeros(no_experiments)
sample_means = [...]
sample_variances = [...]s=0
for sample_size in sample_sizes:
    d=0
    for distro in distributions:
        for i in range(no_experiments):
            sample_normal = np.random.choice(distro, sample_size)
            sample_means[s][d][i] = np.mean(sample_normal)
            sample_variances[s][d][i] = np.var(sample_normal)
        d = d+1
    s = s+1

注意,代码被缩短了。如果您想详细查看代码,可以在 Github 上获得整个 Python 笔记本。

评估方法

在做了大量随机实验后,我们想看看结果。我们使用 Matplotlib 来处理我们的 NumPy 数据结构。请记住,我们有意对总体参数采用三种不同的分布。让我们看看我们得到的关于人口的均值和方差的统计数据。

正态分布的平均值

我们首先关注的是人口的高度。我们国家有 100 万人口,平均身高 176 厘米,标准偏差 6 厘米。但现在,我们正在查看分布在 50 个柱状图箱中的 1000 个随机实验的结果。我们查看所有样本大小的直方图。

我们很容易看到我们的结果大致呈正态分布。这与身高在我们人群中也是正态分布的事实无关。正如我们将很快看到的,我们的随机实验将总是正态分布的。因此,从正态或其他分布的总体中抽取随机样本通常会给出正态分布的结果或统计值。

观察三种样本大小,有一些更值得注意的观察结果:

  • 直方图箱将总是加起来有 1000 个实验。所以我们总是在柱状图中看到所有的实验。
  • 样本越大,我们的直方图越接近正态分布。
  • 样本量越大,我们的实验就越接近总体均值。
  • 由于 x 轴的标度是绝对的,并且具有较大样本大小的实验的结果不是分散的,所以箱也较小。

我们再次看到,对 1%的人口进行抽样,可以得到非常精确的总体均值结果。实际上所有的实验都在实际群体平均值的 1 厘米以内。由于正态分布不仅非常普遍,而且在数学上也很容易理解,所以你可以计算出在某个区间内你的实验次数。这里我们不做任何数学,只是用 Python 和 NumPy 做实验。

fig, ax = plt.subplots(1, 1)
plt.hist(sample_means[0][0], **kwargs, color='g', label=10)
plt.hist(sample_means[1][0], **kwargs, color='b', label=100)
plt.hist(sample_means[2][0], **kwargs, color='r', label=1000)
plt.vlines(176, 0, 1, transform=ax.get_xaxis_transform(), 
           linestyles ="dotted", colors ="k")
plt.gca().set(title='Histograms of Sample Means', 
              xlabel='Height', ylabel='Frequency')
plt.legend()
plt.show()

所有其他直方图的代码遵循相同的模式,将被省略。如果您想详细查看代码,可以在 Github 上获得整个 Python 笔记本。

离散均匀分布的平均值

现在我们有了人口平均身高的统计数据。现在我们看看我们国家的每个居民扔的六面骰子。每个桌游玩家都应该知道,人口平均值是 3.5。三种样本大小的 1000 次实验的直方图形状也大致呈正态。这是正态分布实际用途的另一个暗示。在这里,我们掷骰子的均匀分布和获得统计平均值的实验的正态分布之间显然没有直接联系。

我们掷骰子的离散性质只给了我们六种可能的结果,再加上大量的条块,给了我们一个样本大小为 10 的奇怪的直方图。原因是投掷六面骰子十次的结果数量有限,即 10 到 60 之间的所有自然数。这在直方图中给了我们一些间隙,但是形状仍然类似于正态分布。

同样,正态分布随着样本数量的增加而变得越来越窄,在 1000 个样本的情况下,我们实际上已经非常接近所有实验中的总体均值 3.5。然而,正如曲线的锯齿状所显示的那样,仍然存在相当多的随机性,就像高度的连续正态分布一样。在我们的设置中,我们可以通过用不同的样本大小做 10,000 次甚至 100,000 次实验来减少这种情况。即使是旧笔记本电脑,这也很容易做到。这在真实人群中是不实际的,因为你最好采取大样本,而不是用小样本做大量的调查。如果你算一下,你会很容易发现,与其问 1000 个随机样本 1000 次,你还不如做一次全面的人口普查,问每个人。

指数分布的平均值

图片开始重复,我们得到了非常相似的结果,抽样指数分布的等待时间为下一个信使消息在我们的人口。但是,如果你仔细观察,你会发现 10 人的小样本并不倾向于以 4 人的总体均值为中心。原因是,与均匀分布和正态分布相反,指数分布是非对称的。如果我们在一个随机实验中得到其中一个,我们会得到一些很长的等待时间,这扭曲了我们的平均值。在小样本中,这些异常值会扭曲平均值。在较大的样本中,它是均匀的,我们来看看样本平均值的典型正态分布。

认识到这些异常值实际上是我们数据的一部分是很重要的。我们人群中的一些人等待下一条消息等了 40 分钟,而平均等待时间只有 4 分钟。所以我们不是在处理错误的测量。然而,当处理小样本时,消除这些异常值以获得实际平均值的更精确的图像可能仍然是有益的。当我们不知道你们人口中的分布时,这个问题更难判断,因为我们知道我们正在处理一个指数分布。

评估差异

目前,我们正在研究人口平均值,这通常是最相关的信息。还有更多相关的统计参数。最大值或最小值在样本中对我们没有太大帮助,除了检查上面讨论的潜在异常值。我们在样本中获得实际人口最大值或最小值的机会非常小。为了理解我们人口中的分布,方差可能是有帮助的。样本方差是一个合理的计算值。

数学

方差的公式为:

你可能还记得一些关于方差的事情:

  • 标准差是方差的平方根。我们在这里不需要它,但重要的是要知道我们谈论的基本上是同一件事。
  • 样本方差的计算方式不同。我们使用 1*/(𝑁*1)来计算样本方差。

这实际上更复杂,因为我们需要在查看样本方差时考虑自由度。在我们的实验内,每个实验我们都可以用 1,所以我们就不麻烦了。米歇尔·莱什在这个视频中很好地解释了数学背景。

实验

看看我们的实验,总结是:你可以忘记数学。除以 1000 或者 999 反正差别不大。理论上,样本量为 10 时,我们应该从总体方差中平均抽取 10%的样本。但是,一旦我们观察样本均值的分布,以及在样本量为 10 的 1000 次实验中样本方差是如何分布的,我们的结论是,相差 10%一点也不坏。我们大多数样本大小为 10 的实验都远远偏离了实际的总体方差。

从不同的角度来看

在看到大量近似正态分布的直方图后,我们可以尝试另一种可视化。我们的方差实验结果类似于近似正态分布的均值,更接近于样本量较大时的总体方差。为了更好地理解样本大小对方差统计估计质量的影响,我们使用了箱线图。

箱线图给出了 1000 次实验的平均值,加上偏离统计平均值的标准偏差范围。虽然一切听起来都很自然,但更详细的观察是值得的:

  • 我们正在使用样本估计方差。
  • 对于每个样本量,我们进行 1000 次方差估计。
  • 通过实验,我们得到了 1000 个估计值的统计方差的平均值。
  • 1000 次实验也有一个方差,就是标准差的平方。

所以这个方框图显示了,在一定样本量下,我们对总体方差估计的方差。当进行实验时,这是很自然的。对正在发生的事情进行理论上的抽象思考并不会让它变得更容易。

结果看起来是合理的:随着样本量的增加,我们得到的总体方差的统计估计值越来越小。有了 1000 个样本,我们非常接近总体方差。箱线图比重叠直方图更清楚地显示了这一点。然而,我们正在处理的近似正态分布随机实验的信息却丢失了。通常,尝试不同的可视化是值得的,因为它们往往会对数据给出不同的见解。

fig, ax = plt.subplots(1, 1)
plt.boxplot([sample_variances[0][1], sample_variances[1][1],
             sample_variances[2][1]])
ax.set_xticklabels(['10', '100','1000'])
plt.gca().set(title='Box Plots of Sample Variances', 
              xlabel='Sample size', 
              ylabel='Sample variance')
plt.show()

极端值

正如我们在均匀分布的方差箱线图中已经看到的,在我们的实验中也有异常值。对于均匀分布,这是很少的,我们有一个大致相似的所有样本大小的数字。对于非常不对称的指数分布,这是非常不同的。同样,使用小样本量的平均值,我们也差得很远。

如果我们得到一个更极端的值,其他样本不能补偿它,我们得到平均值或方差的极端结果。这产生了大量的异常值。如果我们有更大的样本量,在计算均值或方差时,我们有更好的机会让其他样本补偿极值。箱线图明确显示了这一点,而在处理直方图时,尤其是离群值往往会被忽略。

结论

从这篇文章中要记住的是,当试图理解复杂的理论事物时,做一些实验是值得的。特别是在概率论和统计学中,这是很容易做到的。Python 是一个很好的工具,因为有许多库可以覆盖甚至最高级的构造。但是即使 Excel 也支持相当大的一组统计函数,并且可以用于实验。

除了对理论有所了解,对实验所用工具的熟练程度也会提高。一旦你有了基本的要素,你就可以很容易地改变你的实验。例如,试着做 100,000 次实验,而不是 1,000 次。你仍然会做随机实验,但是结果的分布将会更接近理论分布。

再次记住,你可以在 Github 上获得整个 Python 笔记本。

展示您的数据技能的组合产品

原文:https://towardsdatascience.com/portfolio-products-to-showcase-your-data-skills-b573e538e094

调整课程活动,为竞争激烈的就业市场做准备

中国云南红河哈尼族彝族自治州。图片由作者提供。

我教授数据分析和可视化的入门课程。我的一些学生将从事研究工作,但他们中的大多数人将在基于社区的机构中使用他们的数据技能,这些机构对实用数据技能有强烈的需求。我看到了比以往任何时候都多的利用数据解决社会、公共健康和环境问题的工作机会。随着提供数据专业培训的大学项目和在线项目的增长,就业市场也非常具有竞争力。

如今,每个人似乎都表示他们知道如何处理数据。毕竟,在电子表格中输入一些数字并声称知道 Excel 有多难?随着就业市场的发展,列出你的软件经验是不够的。雇主希望看到简历上所列技能的证据。这个请求是很好的实践,因为我不知道一个行项目意味着某人知道软件或语言,类似于声称精通微软 Word 。雇主想知道——也想看看——你能用这些工具做什么。

我鼓励我的学生将课程作业转化为投资组合产品,或产品,以应对市场变化。投资组合产品是你可以与潜在雇主分享的东西,它展示了你在简历和求职信之外的技能和能力。创建组合产品需要时间和精力,但这是一项重大的职业投资。学生们努力满足他们的日常需求,因此这自然会干扰长期游戏的基本活动。为了应对不断发展的就业市场和对学生义务的认识,我一直在重新设计作业和项目,所以最终提交的是作品集产品。

在这篇文章中,我将引导你思考可以用来展示数据技能的课程作业的方法。我将为你提供一些关于就业市场策略的建议,以及一个我在数据可视化课程中使用的以投资组合为中心的作业的例子。我所在的社会科学领域的工作可能不直接适用于你的领域或技能水平。然而,我提供的战略思维策略可以在许多领域推广。

你的就业市场策略

你应该仔细规划你的就业市场策略。毕竟,你已经投入了无数个小时来准备。你不想输掉最后一局。许多学生采取基于垃圾邮件的方法,在时间允许的情况下,向尽可能多的职位发布批量发送相同的材料。基于垃圾邮件的应用程序非常突出,很少受到重视。在竞争激烈的就业市场上,这可不是你想要的那种关注。

花时间为你想要的工作定制你的申请材料。定制材料的一部分需要你了解你的受众。谁会看你的材料?他们的背景是什么?他们需要的具体技能是什么?积极思考这些问题可以为你如何准备材料提供战略性的见解。

作为策略的一部分,你还必须考虑打包和分享你的产品组合。如果你正在大学学习,你可能已经有了一个投资组合平台。但是,如果毕业后您的访问权限有限,您可能会想要迁移出这个平台。花点时间让你自己熟悉服务你的投资组合的不同方法和关于这个主题的现有文章。找到一个适合您的需求,但更重要的是,易于访问和导航的目标受众。

投资组合产品示例

正如我提到的,我教的学生都是数据处理新手。新手中的一个常见误区是,最好的投资组合产品是复杂的统计和可视化。这是对一般数据和特定组合产品的错误思考方式。数据可视化代表了一种交流形式——一些我见过的使用简单描述性统计的最令人印象深刻的可视化。优雅和影响力来自数据质量和引人注目的故事情节。

我鼓励学生在他们的技能发展中立即准备组合产品。开发组合产品是困难的,但是通过实践,它们会变得更容易和更好。以下部分提供了一个课程活动的例子,该活动产生了与我的研究领域(应用社会科学)的学生相关的作品集。认识到创建投资组合产品是一项技能,我通过为新的学习者提供一个建议的工作流程来结束这篇文章。这个工作流程可以推广到其他组合产品。我认识到这项活动可能不直接适用于你的领域或技能水平,所以请随意做出相应的调整。

活动概述

如下图所示,报告事例计数的标准做法是滚动平均,也称为移动平均或窗口平均。

来自谷歌搜索结果的冠状病毒病例截图。图片作者。

尽管这是一种常见的做法,但并不是每个人都知道什么是滚动平均值,也不知道为什么我们使用滚动平均值——一般来说,或者专门用于冠状病毒计数。针对这一困惑,本次作业的可交付成果是一张单页信息图,它解决了以下两个问题:

什么是滚动平均?

为什么使用滚动平均值来总结冠状病毒病例数(而不是原始的每日计数)?

除了信息图之外,你还应该包括对活动的简短回顾。这种反思的目的是帮助你认识和描述你用来创建可视化和信息图的技能。花时间思考技能。

  • 哪些不同类别的技能可能会吸引你的听众?
  • 获取和准备数据涉及哪些技能?分析数据?可视化数据?交流数据?设计信息图?

思考超越任务要求。把这当成一个机会,帮助你思考——并获得反馈——高风险面试问题的答案。

活动详情

**数据来源。**请使用《纽约时报》数据集中的冠状病毒病例数数据。这些数据是开放的,可以从纽约时报的 Github 网站上免费获得。将数据筛选到单个州或单个县。

NYT Github 冠状病毒案例库截图。图片由作者提供。

**观众是谁?**对于您的所有沟通,包括数据可视化等视觉沟通,您必须始终关注最终用户,即您的受众。您有一个信息图受众(针对此活动),它不同于您的就业受众。展示与不同受众沟通的能力是你的作品集中需要展示的一项重要技能。想办法用你的设计技能向展示你的技能。记住,你不是在为自己设计。你是在为观众设计。

**软件。**您可以使用任何软件进行分析和设计,但不允许使用信息图表软件或预建模板。从一块空白画布开始,使用网格系统来布置信息图的所有元素。请记住,软件只不过是一种工具。寻找设计解决方案,而不是软件解决方案。假设交互特性是合理的,欢迎具有高级技能的学生创建交互可视化。注意不要过分强调你的视觉化。

发布您的信息图。如何以及在哪里发布信息图由你决定。最低要求是创建一个到这个特定组合产品的链接,以及一个高分辨率的静态图像。你的工作质量几乎完全取决于你的创造力、技术技能和设计技能,而不是软件。您可以创建一个优雅而有效的信息图,并使用 Google Sheets 轻松分享:

谷歌工作表菜单截图。图片由作者提供。

评价。在这项任务中,你最大的障碍是将你的注意力从分数转移到就业市场上。如果你创造了一个适合就业市场的高质量的投资组合产品,你自然会满足分配要求。我提供以下评估标准来帮助指导你的思考。

  • 该信息图解释了什么是移动平均值;
  • 该信息图解释了为什么我们使用滚动平均值来报告冠状病毒病例数;
  • 解释用通俗易懂的语言,并为你的目标受众量身定制;
  • 数据可视化在视觉上强化了解释;
  • 数据可视化遵循设计的最佳实践;
  • 信息图遵循设计的最佳实践;
  • 信息图是独立的,意味着目标受众无需搜索更多信息就能理解信息图的主要目标。

新学员的建议工作流程

如果您是新手,这里有一个建议的工作流程:

1。理解任务。不要直接进入数据。相反,确保你理解了可交付物的所有方面。如果您是数据处理新手,并且没有统计学背景,您可能不知道滚动平均值。花点时间熟悉这个问题。在推进之前,确保你理解了任务。这里有一些给正在发展数据技能的学生的提示。

**2。准备好你的资料。**数据准备是所有数据项目中最耗时的任务。花点时间设置你的数据。查看您的数据,确保您知道列、行和单元格的含义。

3。分析你的数据。在你的数据整理好之后,继续进行你的分析。新用户应该做好准备,开始时会有被卡住的感觉。努力解决你的问题,而不是寻找神奇的公式。在这篇文章中,我提供了多种解决数据问题的策略。一定要想好窗口计算是怎么规定的。你需要平均每天落后 n 天。不要计算滚动平均值,好像你在展望未来一样!

**4。可视化您的数据。**获得使用数据准备的一组初始可视化。但是,不要创建一个完美的集合——花时间查看数据。你想重复你的故事。

**5。构建你的故事。**你需要用你的数据讲述一个故事。仅仅因为你有几年的数据并不意味着你应该使用所有的数据。当你有很多变化时,滚动平均特别有用。深入研究您的数据,找出滚动平均值可能会影响我们根据数据做出决策的时间段。创造形象来加强你的解释。请记住,对于冠状病毒计数,标准通常是 5 天或 7 天的滚动平均值。你可以提供不同价值观的例子,但要确保你是在帮助人们理解当前的最佳实践。

截图来自谷歌,基于《纽约时报》的数据。图片由作者提供。

即使你可以访问大约两年的数据,你也应该对你要分析的数据部分有所考虑。当计数非常稳定时,滚动平均不会提供任何信息增益,因此考虑深入到滚动平均最有益的时段。

**6。收集你的元素。**此时,你需要收集信息图的基本要素。你需要一个标题,可能还需要一个副标题(可选),文本描述和你的可视化。不要担心最终的格式,把所有的元素放在一起。

7。布局你的元素。避免直接进入软件来构建你的图形。考虑手绘一些缩略图布局。确保你正在使用一个网格系统来帮助你考虑以一种支持你的信息的方式来划分你的画布。请记住,您的受众可能会进入左上角的信息图,然后从左到右和从上到下阅读。网格可以帮助您为信息创建一个可视化的层次结构。

**8。设计您的图形。这里是学生误入歧途的部分。他们认为对 viz 进行样式化包括使可视化看起来更有吸引力,这导致了对颜色、图标和字体的无限制使用。有意并一致地设计所有元素的样式。我最近发表了一篇关于 高风险职业材料的设计 的文章,涵盖了与此相关的设计原则。我坚信形式服从功能。

**9。反馈和迭代。**在设计可视化、信息图表和其他职业资料时,注意不要为自己设计。当你处于一个项目的杂草中时,一切对你来说都是有意义的。一切都很清楚。视觉效果很好。一切可能对你有用,但你不是观众。你的工作清楚吗?它是否实现了您预期的信息目标?寻求反馈,而不是拉拉队。

一种自言自语的方法可能是有益的。请人们解释他们是如何理解文本和数据的。他们的眼睛画在哪里?他们如何在视觉化图像中移动?这种方法有助于发现错误和改进工作的机会。对于就业市场,你想通过展示你最好的工作来突出你的知识和技能,而不是开发中的产品或有错误的产品。

10。最终检查。发布信息图时,请检查两次链接。在多种浏览器和设备上查看您的作品。确保生成最高分辨率的文件,最好是 PDF 格式。

我建议把这个工作流程看作是一套指导方针,而不是规则。关键的一点是,你对你正在生产的东西有一个明确的方向,并把项目分解成离散的步骤。

计算机视觉开发人员的 PostGIS

原文:https://towardsdatascience.com/postgis-for-computer-vision-developers-5e703e37ad55

直接在 SQL 中分析几何对象,无需额外的 Python 代码

马克斯·博廷格在 Unsplash 上拍摄的照片

当我们谈论计算机视觉算法的输入和输出时,我们主要谈论两件事——图像和注释。虽然图像大多存储在云存储中,但注释有更广泛的存储选项。在我的例子中,所有的注释都存储在数据库中。

我是一个 Python 爱好者。作为一名计算机视觉开发人员,我的大部分工作都与 Python 有关。但是正如我所说的,注释是我的数据不可分割的一部分,存储在 PostgreSQL 数据库中。我曾经编写简单的查询来提取我的所有数据,然后使用 Pandas/OpenCV/Shapely 或任何其他相关的库在 Python 中进行分析。然后有一天,我被介绍给波斯特吉斯。

在这篇文章中,我想继续介绍 PostGIS——python 的替代品,用于分析几何结果(PostgreSQL 数据库)。

波斯特吉斯

https://postgis.net/

PostGIS 是 PostgreSQL 对象关系数据库的空间数据库扩展器。它增加了对地理对象的支持,允许在 SQL 中运行位置查询。

在我们的例子中,计算机视觉任务,PostGIS 允许几何对象表示,如点,线,多边形。使用 PostGIS 的几何表示,我们可以轻松地在 SQL 查询中直接应用几何操作和变换,而无需额外的 Python 代码。

一些 PostGIS 方法相当于 Python 中的 GeoPandas 和 Shapely 。

数据概述

我在数据库里有什么

  1. 数据注释 —我们所有的图像注释都存储在数据库中。注记工具直接连接到数据库,每当注记作业完成时,所有数据都会以 PostGIS 格式写入数据库。
  2. 算法结果 —像分割一样,算法结果也存储在数据库中。无论是生产环境的结果还是研究环境的结果。

图像注释和图像算法结果之间的连接可以通过根据图像 ID 进行连接来容易地完成。

在对所有相关数据应用查询后,我们可以使用 PostGIS 轻松操作它。例如,为分段任务计算 IoU。使用 PostGIS,我们不需要编写 Python 脚本,可以在查询中以方便的方式直接分析数据。

当我们想要添加分析层并将分析结果保存回数据库时,它会更加有用。不需要在 Python 中进行分析(需要触发不同的过程,然后写入数据库),而是可以直接使用 SQL 来完成。

应该如何表示

  • **到几何:**为了使用 PostGIS,你所有的对象(多边形、线、点)都应该用 PostGIS 几何格式表示。
# Point
SELECT **ST_GeomFromGeoJSON**(‘{“type”:”*Point*”,”coordinates”:[0,1]}’)# Line
SELECT **ST_GeomFromGeoJSON**('{"type":"*LineString*","coordinates":[[0,1], [15,35], [43.2,5.1]]}'))# Polygon
SELECT **ST_GeomFromGeoJSON**
    (
        '{
            "type":"*Polygon*",
            "coordinates":[[
                [7.7, 51.8],
                [3.8,48.3],
                [7.2,43.5],
                [18.6,43.8],
                [17.9,50.2],
                [13.7,54.0],
                [7.7, 51.8]
            ]]
        }'
    )SELECT 'POLYGON((0 0, 0 1000012333334.34545678, 1.0000001 1, 1.0000001 0, 0 0))'::*geometry*

其他现有的制图表达包括点、线串、面、多点、多线串、多面和几何集合。

请注意,与 Python 相反,当使用 PostGIS 面表示时,第一个和最后一个点必须相同,以便闭合面并获得有效的表示。

  • **从几何:**几何类型可以转换回数组和文本 ST_AsTextST_AsGeoJSON
SELECT **ST_AsGeoJSON**('LINESTRING(1 2, 4 5)');
---------------------------
{"type":"LineString","coordinates":[[1,2],[4,5]]}SELECT **ST_AsGeoJSON**('LINESTRING(1 2, 4 5)')::json->>'coordinates';
---------------------------
[[1,2],[4,5]]=======================SELECT **ST_AsText**('POINT(111.1111111 1.1111111)'));
---------------------------
POINT(111.1111111 1.1111111)

计算机视觉的邮政地理信息系统

分段—并集交集(IoU)指标

作者图片

# Area
**ST_Area**(l.polygon)# Union
**ST_Union**(l1.polygon, l2.polygon)# Intersection
**ST_Intersection**(l1.polygon, l2.polygon)# IoU
**ST_Area**(**ST_Intersection**(l1.polygon, l2.polygon)) /
**ST_Area**(**ST_Union**(l1.polygon, l2.polygon))

搞定了。

姿势估计-检测到的关节的百分比(PDJ)度量

如果预测关节和真实关节之间的距离在边界框对角线的某个分数之内,则检测到的关节被认为是正确的。

卡尔·巴塞洛在 Unsplash 上的照片。作者添加了注释。

作者图片

  • ||d_pred — d_gt|| —真实关键点和预测关键点之间的欧几里德距离
**ST_Distance**(**ST_Point**( GTi_x,GTi_y), **ST_Point**( predi_x,predi_y))
  • 对角线-边界框的对角线距离
-- create bounding box
**ST_MakeBox2D**(**ST_Point**(x1,y1),**ST_Point**(x2,y2))-- get diaginal as line string
**ST_BoundingDiagonal**(**ST_MakeBox2D**(**ST_Point**(x1,y1),**ST_Point**(x2,y2)))-- calculate legnth
**ST_Length**(**ST_BoundingDiagonal**(**ST_MakeBox2D**(**ST_Point**(x1,y1),**ST_Point**(x2,y2))))
  • 阈值-对角线的分数。

最终查询:对于本例,阈值= 0.05。

**ST_Distance**(**ST_Point**( GTi_x,GTi_y), **ST_Point**( predi_x,predi_y)) < 0.05 * **ST_Length**(**ST_BoundingDiagonal**(**ST_MakeBox2D**(**ST_Point**(x1,y1),**ST_Point**(x2,y2))))

检测-多边形的边界框标注

作者图片

在我的例子中,我没有绑定框注释。我有用于分割任务的多边形注释。我可以使用 PostGIS 轻松地从多边形创建检测注释。

  • 选项 1-获得左上角和右下角的点
SELECT **Box2D**('POLYGON((7.7 51.8,3.8 48.3,7.2 43.5,18.6 43.8,17.9 50.2,13.7 54,7.7 51.8))'::geometry)
----------------------------
BOX(**3.8 43.5,18.6 54**)
  • 选项 2-获取边界框的所有四个点
SELECT **ST_Envelope**('POLYGON((7.7 51.8,3.8 48.3,7.2 43.5,18.6 43.8,17.9 50.2,13.7 54,7.7 51.8))'::geometry)SELECT **ST_AsText**(**ST_Envelope**('POLYGON((7.7 51.8,3.8 48.3,7.2 43.5,18.6 43.8,17.9 50.2,13.7 54,7.7 51.8))'::geometry))
----------------------------
POLYGON((**3.8 43.5**,3.8 54,**18.6 54**,18.6 43.5,3.8 43.5))
--- We have 5 coordinates and the last coordinate is always the first one as it is a polygon representation.

多边形变换

是否要将多边形旋转 30 度?你想把它放大两倍吗?轻松点。PostGIS 有几个转换选项。

# Rotate 30 degrees counter-clockwise with origin x=50, y=160
SELECT **ST_Rotate**(l.polygon, pi()/6, 50, 160));# Scale X,Y
SELECT **ST_Scale**(l.polygon, 0.5, 0.75)# Scale X,Y,Z
SELECT **ST_Scale**(l.polygon, 0.5, 0.75, 0.8)# Translate X, Y
SELECT **ST_Translate**(l.polygon ,1, 0);

多边形可视化

想要向用户显示您生成的巨大多边形,但不想显示所有的数百个点?使用 ST_Simplify

ST_Simplify 使用 Douglas-Peucker 算法返回给定几何体的“简化”版本,包含较少的点。

SELECT **ST_Simplify**(geom,<tolerance>)

更多选项

还有更多选择:

  • 想计算多边形的凸包?ST _ 凸包
  • 想要检查多边形是否重叠? ST_Overlaps
  • 检查一个点是否在多边形内? ST_Within
  • 需要计算几何图形(多边形、线、点)之间的距离吗? ST_Distance
  • 检查自相交? ST_IsValid。
  • 移除自相交? ST_MakeValid

如果你坚持 Python…

如果我没有让您相信 PostGIS 很棒,并且您仍然想要 Python 中的这一功能,请查看:

[## 匀称的文档

shapely.readthedocs.io](https://shapely.readthedocs.io/en/stable/#) https://geopandas.org/en/stable/

收场白

我希望我说服了你,PostGIS 不仅对于数据分析师,而且对于计算机视觉开发人员都是一个非常有用的扩展。它提供了丰富多样的选项和几何操作。

它帮助我升级了我的查询,并节省了我使用 SQL 提取数据和 Python 分析数据的开销。现在,我可以在一个地方完成这两项工作,并将结果直接保存回数据库。

希望对你也有帮助!

用 Airflow 将数据从 Postgres 加载到 BigQuery

原文:https://towardsdatascience.com/postgres-bigquery-airflow-e857e3c6aa7a

使用 Apache Airflow 从 PostgreSQL 到 BigQuery 数据摄取—分步指南

在 Unsplash 上 Venti Views 拍摄的照片

将数据从 Postgres 数据库(本地托管)接收到 Google Cloud BigQuery 的一种方法是使用 Airflow,它提供了大量可用于数据接收和集成过程的操作符。

支持使用这些操作符也很重要,因为它们可以帮助我们编写更少更简单的代码,以便在更广泛的数据工程环境中执行基本操作。

现在,为了从 Postgres 表中提取数据并将其加载到 BigQuery 中,我们可以编写一个管道,首先使用正确的操作符将 Postgres 中的数据以 csv 格式提取到 Google 云存储中,然后将 GCS 中的 csv 文件加载到 BigQuery 中。

在今天的教程中,我们将一步一步地介绍如何使用 Apache Airflow 将数据从 Postgres 数据库导入 Google Cloud BigQuery。我们将遵循三个单独的步骤,最终让我们从 Postgres 执行完全加载到 Google 云平台上的数据仓库,该数据仓库每天都会执行。

步骤 1:将 Postgres 表提取到 Google 云存储中

因此,第一步涉及一个过程,我们将数据从源 Postgres 表转移到 Google 云存储中。换句话说,我们将以 csv 格式将数据从源表导出到对象存储中。

在深入研究细节之前,首先需要确保已经创建了用于连接 Postgres 数据库的连接。这可以通过 Airflow UI(管理->连接)来实现。或者,为了更安全和可扩展地管理连接,您可以参考我在 Medium 上的一篇最新文章,在这篇文章中,我讨论了如何使用 Airflow 设置 HashiCorp Vault。

现在,假设我们有一个有效的 Postgres 连接,我们现在可以利用包含在apache-airflow-providers-google包中的[PostgresToGCSOperator](https://airflow.apache.org/docs/apache-airflow-providers-google/stable/_api/airflow/providers/google/cloud/transfers/postgres_to_gcs/index.html),将数据从源数据库传输到 Google 云存储中。

from airflow.providers.google.cloud.transfers.postgres_to_gcs import PostgresToGCSOperatorGCS_BUCKET = 'my-bucket'
GCS_OBJECT_PATH = 'postgres-test'
SOURCE_TABLE_NAME = 'mytable'
POSTGRESS_CONNECTION_ID = 'postgres' postgres_to_gcs_task = PostgresToGCSOperator(
    task_id=f'postgres_to_gcs',
    postgres_conn_id=POSTGRES_CONNECTION_ID,
    sql=f'SELECT * FROM {SOURCE_TABLE_NAME};',
    bucket=GCS_BUCKET,
    filename=f'{GCS_OBJECT_PATH}/{SOURCE_TABLE_NAME}.{FILE_FORMAT}',
    export_format='csv',
    gzip=False,
    use_server_side_cursor=False,
)

本质上,这个任务将把数据从名为'mytable'的源 Postgres 表传输到 Google 云存储中,更具体地说是在gs://my-bucket/postgres-test/mytable.csv下。

步骤 2:将数据从 GCS 加载到 BigQuery

既然我们已经将数据从 Postgres 复制到 Google 云存储中,我们就可以利用另一个名为[GCSToBigQueryOperator](https://airflow.apache.org/docs/apache-airflow-providers-google/stable/_api/airflow/providers/google/cloud/transfers/gcs_to_bigquery/index.html)的操作符将数据从云存储转移到 BigQuery。

from airflow.providers.google.cloud.transfers.gcs_to_bigquery import GCSToBigQueryOperator BQ_DS = 'my_dataset'
BQ_PROJECT = 'my-project'schema = [
    {
        'name': 'id',
        'type': 'STRING',
        'mode': 'NULLABLE',
    },
    {
        'name': 'name',
        'type': 'STRING',
        'mode': 'NULLABLE',
    },
    {
        'name': 'age',
        'type': 'INTEGER',
        'mode': 'NULLABLE',
    },
    {
        'name': 'is_active',
        'type': 'BOOLEAN',
        'mode': 'NULLABLE',
    },
]gcs_to_bq_task = GCSToBigQueryOperator(
    task_id=f'gcs_to_bq',
    bucket=GCS_BUCKET,
    source_objects=[f'{GCS_OBJECT_PATH}/{SOURCE_TABLE_NAME}.csv'],
    destination_project_dataset_table='.'.join(
        [BQ_PROJECT, BQ_DS, SOURCE_TABLE_NAME]
    ),
    schema_fields=schema,
    create_disposition='CREATE_IF_NEEDED',
    write_disposition='WRITE_TRUNCATE',
    skip_leading_rows=1,
    allow_quoted_newlines=True,
)

上面的代码将把 bucket gs://my-bucket/postgres-test/mytable.csv上的 Google 云存储中的数据加载到 BigQuery 表my-project.my_dataset.mytable中。

请注意,我们还必须指定 BigQuery 表的所需模式——您可能希望对此进行调整,以适应您的特定用例。还要注意,我们指示操作符跳过 GCS 上源文件中的第一行(skip_leading_rows=1),因为该行对应于我们并不真正需要的表列,假设我们已经在 BigQuery 上为目标表指定了所需的模式。

第三步:清理混乱

现在,下一步涉及删除我们存储在 Google 云存储中的中间文件,这些文件是为了将数据从 Postgres 转移到 BigQuery 而创建的。

为此,我们可以再一次使用一个名为GCSDeleteObjectsOperator的操作符,该操作符用于从 Google 云存储桶中删除对象,或者从对象名称的显式列表中删除对象,或者从匹配前缀的所有对象中删除对象。

from airflow.providers.google.cloud.operators.gcs import GCSDeleteObjectsOperator cleanup_task = GCSDeleteObjectsOperator(
    task_id='cleanup',
    bucket_name=GCS_BUCKET,
    objects=[f'{GCS_OBJECT_PATH}/{SOURCE_TABLE_NAME}.csv'],
)

步骤 4:编写最终气流 DAG

现在,我们需要做的就是创建一个 DAG,它将包含我们在本教程的上一部分中创建的三个任务。请注意,在最后,我们还指定了任务之间的依赖关系,以便它们可以按顺序执行。

from datetime import timedeltawith DAG(
    dag_id='load_postgres_into_bq',
    start_date=days_ago(1),
    default_args={
        'owner': 'airflow',
        'retries': 2,
        'retry_delay': timedelta(minutes=5),
    },
    schedule_interval='0 9 * * *',
    max_active_runs=1,
) as dag:
    postgres_to_gcs_task = PostgresToGCSOperator(
        task_id=f'postgres_to_gcs',
        postgres_conn_id=POSTGRES_CONNECTION_ID,
        sql=f'SELECT * FROM {SOURCE_TABLE_NAME};',
        bucket=GCS_BUCKET,
        filename=f'{GCS_OBJECT_PATH}/{SOURCE_TABLE_NAME}.{FILE_FORMAT}',
        export_format='csv',
        gzip=False,
        use_server_side_cursor=False,
    ) gcs_to_bq_task = return GCSToBigQueryOperator(
        task_id=f'gcs_to_bq',
        bucket=GCS_BUCKET,
        source_objects=[f'{GCS_OBJECT_PATH}/{SOURCE_TABLE_NAME}.csv'],
        destination_project_dataset_table='.'.join(
            [BQ_PROJECT, BQ_DS, SOURCE_TABLE_NAME]
        ),
        schema_fields=schema,
        create_disposition='CREATE_IF_NEEDED',
        write_disposition='WRITE_TRUNCATE',
        skip_leading_rows=1,
        allow_quoted_newlines=True,
    ) cleanup_task = GCSDeleteObjectsOperator(
        task_id='cleanup',
        bucket_name=GCS_BUCKET,
        objects=[f'{GCS_OBJECT_PATH}/{SOURCE_TABLE_NAME}.csv'],
    ) postgres_to_gcs_task >> gcs_to_bq_task >> cleanup_task

完整代码

你可以在下面分享的 GitHub Gist 上找到这个教程的完整代码。

本教程中使用的完整代码——如何将数据从 Postgres 数据移动到 Google Cloud BigQuery

最后的想法

在今天的教程中,我们演示了一个将数据从 Postgres 数据库摄取到 Google Cloud BigQuery 的逐步方法。因为我们不能将 Postgres 上的源表中的数据加载到 BigQuery 中,所以我们使用 Google 云存储作为中间对象存储,Postgres 表将被提取到 csv 文件中,然后该文件将被加载到 Google Cloud BigQuery 上的目标表中。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章你可能也喜欢

带有 pg_trgm 的 Postgres 模糊搜索:智能数据库猜测你想要什么,当没有找到“宠物食品”时返回“猫粮”

原文:https://towardsdatascience.com/postgres-fuzzy-search-with-pg-trgm-smart-database-guesses-what-you-want-and-returns-cat-food-4b174d9bede8

Postgres 中基于 pg_trgm 和 Levenshtein 距离的智能搜索

文章中的所有图片均由作者提供

在数据库搜索中,有三种常见的场景超出了传统通配符查询的能力。

首先,当用户搜索“宠物食品”但在您的数据库中没有找到名为“宠物食品”的产品时,数据库会智能地返回“猫粮”或“狗粮”来代替吗?

第二,用户将“chocolate”拼错为“chacolate”,数据库能否尝试猜测用户想要什么并返回最接近的匹配?

第三,词序不同的词仍然可以指代同一事物,如“芒果蛋糕”、“芒果蛋糕”。当单词顺序不同时,数据库能够将搜索字符串与产品名称配对吗?

在传统数据库中,通配符搜索“咖啡机”不会得到“咖啡机”,查找“羊角面包”(拼写错误)不会找到“羊角面包”,查询“洗涤剂手盘”不会返回“手盘洗涤剂”。但是通过 Postgres 中的模糊匹配,pg_trgm 模块支持对相似字符串的快速搜索,并实现了灵活和智能的搜索性能。一些查询结果的对比可以在下图中找到。

搜索“宠物食品”的比较

搜索“洗涤剂手盘”的比较

拼写错误的单词“chacolate”的搜索比较

大纲

  • 什么是 pg_trgm 和三元模型
  • 用 pg_trgm 进行模糊搜索
  • Postgres 中的 Levenshtein 距离
  • Postgres 中的模糊搜索有什么局限性

什么是 pg_trgm

Postgres 的pg_trgm模块基于三元组匹配搜索相似的字符串。pg_trgm 中的三元组是忽略非单词字符的三个字符的组。两个给定字符串的相似性可以通过它们共享多少三元组来表示。

给定字符串中的三元模型

用例子来理解一个概念总是比较容易的。字符串“mango”中的三元组是

m, ma, man, ang, ngo, go

“show_trgm”函数返回给定字符串中的所有三元组,它通常用于实际调试。

show_trgm '函数

在另一个示例“the|bay”中,我们可以看到只考虑字母数字文本,它们是

t, th, the, he, b, ba, bay, ay

仅考虑字母数字文本

pg_trgm 相似性评分

两个给定字符串的相似性得分可以通过它们共享的三元模型的数量来衡量。例如,“ 【芒果蛋糕】 和一串不同顺序的“ 芒果蛋糕” 之间的similarity得分为 0.68,“ 猫粮“ ”和“ 宠物粮“ ”得分为 0.38,“ 椰子“ 【T21”以及拼错的单词“ ciconuts

功能similarity

相似的字符串

拼写错误的单词

pg_gram 模糊搜索

首先,为 pg_trgm 创建一个扩展,如果它还没有被加载的话。然后搜索与“咖啡机”相似的目标,并根据相似性得分对结果进行降序排序。如下图截图所示,‘咖啡机’是与‘咖啡机’最相似的产品。

**CREATE EXTENSION IF NOT EXISTS pg_trgm;** 
select distinct product_sub_species, 
**similarity**(product_sub_species, 'coffee machine') as score from 
ct where product_sub_species % 'coffee machine'
order by score desc; 

pg_trgm 模糊搜索

让我们来看另一个在查询目标中有拼写错误的例子。用户想要搜索“砧板”,但却输入了“切肉板”。我们可以看到 pg_trgm 搜索能够返回“切菜板”作为最接近的匹配,这正是用户想要的。

pg_trgm 模糊搜索

Postgres 中的 Levenshtein 距离

与 pg_trgm 不同,Levenshtein 距离通过查看两个字符串的差异来衡量相似性。正如维基百科、中所定义的,“非正式地,两个单词之间的 Levenshtein 距离是将一个单词变成另一个单词所需的单个字符编辑(插入、删除或替换)的最小数量。”

Levenshtein 距离越小,两个字符串越相似。以下示例显示“猫玩具”和“宠物玩具”之间的 Levenshtein 距离为 4,这表明“猫玩具”比“狗玩具”更接近匹配。

**CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;**
SELECT distinct
 product_sub_species,
    **LEVENSHTEIN**(product_sub_species, 'pet toy')
FROM ct
ORDER BY LEVENSHTEIN(product_sub_species, 'pet toy') ASC

“猫玩具”是具有最小 Levenshtein 距离的最接近匹配。

Postgres 中模糊搜索的局限性是什么

Postgres 的模糊搜索方法有两个主要的局限性。首先,Postgres 中的字符串匹配不是基于语义的方法。pg_trgm 和 Levenshtein distance,或 soundex,metaphone 都是字符级的比较。例如,SQL 查询搜索“狗咀嚼物”将不会找到“宠物零食”,因为缺少常见的三元模型。

其次,Postgres 中的模糊匹配不能应用于监督学习场景。例如,有两个数据集具有一些公共列:“雇员姓名”、“电子邮件”和“地址”。我们需要将这些数据集中的雇员配对,以加入关于同一个人的信息。典型的解决方案是计算每一列的相似性得分,然后运行随机森林或其他分类器来学习潜在的规则,以从不同的数据集中检测同一个人。这超出了 Postgres。

结论

在某些情况下,智能数据库搜索应该表现得像一个搜索引擎,而不是命令行工具。模糊数据库搜索不是向用户抛出“未找到”,而是试图猜测用户想要什么,并返回最接近的匹配。

在本文中,我们通过使用相似的字符串、不同顺序的字符串和拼写错误的字符串进行数据库搜索的例子,讨论了 pg_trgm 和 Levenshtein 距离方法。此外,我们还讨论了模糊匹配在 Postgres 中的局限性。请将这项技术应用到您的项目中,如果您有任何问题,请随时联系我们。🐸 🐣 🐼

参考资料:

  • F.33. pg_trgm
  • F.15 .模糊 strmatch
  • 在大型数据集上使用 Python 进行模糊字符串匹配,以及为什么不应该使用 FuzzyWuzzy

特别感谢faiz an Qazi进行校对。🔆 ✔️ 🐳

面向数据科学家的功耗分析

原文:https://towardsdatascience.com/power-analysis-17636d3f059b

确定统计分析所需的样本量

克劳迪奥·施瓦兹在 Unsplash 上的照片

功效分析是每个数据科学家和统计学家都应该熟悉的统计方法之一。

功率分析的主要目标或用途是:

  1. 确定一个统计检验是否有足够的概率(或功效)在有效应的情况下找到一个效应(真阳性)?
  2. 假设我们有显著性水平、效应大小和统计功效,估计实验所需的样本量。

确定正确的(或最少的)样本对于任何统计测试或实验都是至关重要的。使用人口数据几乎是不可能的,收集人口数据也很昂贵;在这种情况下,我们从总体中随机收集样本,以代表基本的统计测量或分布,用于统计测试或建模——既经济又实用。

假设检验(图片由作者提供)

通常,统计检验或建模涉及假设检验,以确定两组是否显著不同(真实效果或无效果)。该假设包括:

  • 零假设
  • 另一假设

零假设是指没有找到足够的证据来验证替代假设。替代假设是指有足够的证据支持替代假设。例如,与安慰剂相比,药物的效果。同样,功效分析是这样一个概念,即当效果实际上是正面的时,通过假设计算预测正面效果的概率。

另一个合适的例子是垃圾邮件的检测。零假设将声明垃圾邮件和非垃圾邮件之间没有区别(都属于同一类别),这两个组之间缺乏统计意义。另一种假设是垃圾邮件和非垃圾邮件之间存在显著差异。

假设检验(图片由作者提供)

  • 真阳性:有效果时发现效果的概率。
  • 真阴性:没有效果时发现不了效果的概率。
  • 假阳性(I 型错误)):在没有效果的情况下发现效果的概率。错误地拒绝零假设或接受替代假设。I 类误差表示为α*(*α),也称为显著性水平。默认情况下,它被设置为 0.05%—当它为真时,有 5%的机会错误地拒绝零假设。
  • 假阴性(第二类错误):当有效应时,发现不了效应的概率。错误地接受零假设或拒绝替代假设。第二类误差表示为β*(*β),1-β称为统计功效。默认情况下,它被设置为 0.20% —当假设为假时,有 20%的机会错误地接受零假设。

统计功效也被称为真正 而功效取决于β的值(ⅱ型误差)。β取决于各种因素,这些因素也决定了实验的功效,这将在下一节详细讨论。对于一组固定的数据,第一类和第二类误差成反比——第一类误差的增加导致第二类误差的减少,反之亦然。有多种方法可以减少这两种误差,但需要改变数据量或采样技术,这将在下一节讨论。

影响实验功效的因素:

有 4 个主要因素影响实验的功效

  • 显著性水平(alpha): 显著性水平越高,功效越高。较高的显著性水平增加了第一类错误概率,降低了第二类错误概率(意味着较高的功效)。
  • **样本量:**样本量的增加会导致分布的标准差减小。对于零假设中提出的相同限制,类型 II 错误的概率降低,并且实验的功效增加。请注意,第一类错误的概率也随着样本量的增加而降低。这是一个实验唯一可控的参数。
  • **可变性:**当所收集数据的方差较低时,可以观察到与大样本量相似的效果。第一类误差和第二类误差都减小了,实验的功效增加了。
  • **效应大小:**效应大小是样本或总体之间结果的差异。效应大小取决于两个样本的平均值和样本的方差。它是两个样本的平均值之差与样本标准差之比。当效应大小高时,实验的功效也高。

样本量的确定:

不可能用总体来进行推断和实验。因此,知道进行统计实验或建模需要多少数据是很重要的。这就是电源分析发挥作用的地方。利用检验的功效、统计显著性和效应大小,我们可以确定检验所需的最小数据量。

数据的最小大小[1]

效果大小[1]

n 是达到所需功率或更大功率的试验所需的最小数据。

示例:

一名研究人员假设,在没有糖尿病的人群中,每天喝至少两杯咖啡的人的空腹血糖(冠心病的一个风险因素)较高。一项横断面研究计划评估每天至少喝两杯咖啡的人的平均空腹血糖水平。据报道,没有糖尿病的人的平均空腹血糖水平为 95.0 毫克/分升,标准偏差为 9.8 毫克/分升。7 如果每天至少喝两杯咖啡的人的平均血糖水平为 100 毫克/分升,这在临床上将具有重要意义。应该招募多少患者参与研究,以确保检测的功效为 80%来检测这种差异?将使用显著性水平为 5%的双边检验。[1]

效果大小= 0.51,n = 30

参考资料:

  1. https://SPH web . bumc . bu . edu/otlt/mph-modules/bs/bs 704 _ power/bs 704 _ power _ print . html

Power BI 聚合——终极指南

原文:https://towardsdatascience.com/power-bi-aggregations-the-ultimate-guide-abd38fee0e78

聚合是 Power BI 中最强大的特性之一!了解如何利用这一特性来提高您的 Power BI 解决方案的性能!

作者图片

在上一篇文章中,在解释复合模型特性时,我们为另一个极其重要和强大的概念——聚合——打下了坚实的基础!这是因为在许多场景中,尤其是在企业级模型中,聚合是复合模型的自然“成分”。

然而,由于复合模型特性也可以在不涉及聚合的情况下得到利用,我认为将这两个概念分开放在不同的文章中是有意义的。

在我们解释聚合如何在 Power BI 中工作并查看一些具体的用例之前,让我们首先回答以下问题:

为什么我们首先需要聚合?模型中有两个具有相同数据的表有什么好处?

在我们阐明这两点之前,重要的是要记住在 Power BI 中有两种不同类型的聚合。

  • 用户定义的聚合 直到几个月前还是 Power BI 中唯一的聚合类型。在这里,您负责定义和管理聚合,尽管 Power BI 稍后会在执行查询时自动识别聚合。
  • 自动聚合 是 Power BI 中较新的特性之一,最近刚刚作为通用特性发布。启用自动聚合功能后,您可以喝杯咖啡,坐下来放松一下,因为机器学习算法将收集您报告中最常运行的查询的数据,并自动构建聚合来支持这些查询。

这两种类型之间的重要区别是许可限制,当然,除了自动聚合之外,您不需要做任何事情,只需在您的租户中打开该功能。虽然用户定义的聚合可用于高级版和专业版,但目前自动聚合需要高级版许可证。

从现在开始,我们将只讨论用户定义的聚合,请记住这一点。

好了,下面是对聚合以及它们在 Power BI 中的工作方式的简短解释。场景是这样的:您有一个非常大的事实表,它可能包含数亿甚至数十亿行。那么,如何处理如此大量数据的分析请求呢?

作者图片

您只需创建聚合表!在现实中,这是一种非常罕见的情况,或者说这是一种例外而不是规则,分析要求是将单个交易或单个记录视为最低级别的细节。在大多数情况下,您希望对汇总数据进行分析:比如,我们在某一天有多少收入?或者,产品 X 的总销售额是多少?进一步说,顾客 X 总共花了多少钱?

此外,您可以聚合多个属性的数据(这是通常的情况),并汇总特定日期、客户和产品的数据。

作者图片

如果您想知道聚集数据有什么意义,那么,最终目标是通过预先准备数据来减少行数,从而减少整个数据模型的大小。

因此,如果我需要查看客户 X 在今年第一季度在产品 Y 上花费的总销售额,我可以利用已经预先汇总的数据。

关键“成分”——让 Power BI“意识到”聚合!

好了,这是故事的一面。现在更有趣的部分来了。创建聚合本身并不足以加快 Power BI 报告的速度,您需要让 Power BI 知道聚合!

在我们进一步讨论之前,有一点需要注意:只有当原始事实表使用 DirectQuery 存储模式时,聚合意识才会起作用。我们将很快解释如何设计和管理聚合,以及如何为表设置适当的存储模式。此时,只需记住原始事实表应该处于 DirectQuery 模式。

让我们开始构建我们的聚合吧!

作者图片

如上图所示,我们的模型相当简单,由一个事实表(FactOnlineSales)和三个维度(DimDate、DimStore 和 DimProduct)组成。所有表当前都使用 DirectQuery 存储模式。

让我们创建另外两个表,我们将使用它们作为聚合表:第一个表将根据日期和产品对数据进行分组,而另一个表将使用日期和商店进行分组:

/*Table 1: Agg Data per Date & Product */
SELECT DateKey
       ,ProductKey
       ,SUM(SalesAmount) AS SalesAmount
       ,SUM(SalesQuantity) AS SalesQuantity 
FROM FactOnlineSales 
GROUP BY DateKey
        ,ProductKey/*Table 2: Agg Data per Date & Store */
SELECT DateKey
       ,StoreKey
       ,SUM(SalesAmount) AS SalesAmount
       ,SUM(SalesQuantity) AS SalesQuantity 
FROM FactOnlineSales 
GROUP BY DateKey
        ,StoreKey

作者图片

我已经将这些查询分别重命名为销售产品 Agg 和销售商店 Agg,并关闭了超级查询编辑器。

由于我们希望大多数查询(这些查询检索按日期和/或产品/商店汇总的数据)获得最佳性能,我将把新创建的聚合表的存储模式从 DirectQuery 切换到 Import:

作者图片

现在,这些表被加载到缓存中,但是它们仍然没有连接到我们现有的维度表。让我们创建维度和聚合表之间的关系:

作者图片

在我们继续之前,让我停下来解释一下当我们建立关系时发生了什么。如果您还记得我们以前的文章,我提到过在 Power BI 中有两种类型的关系:常规的和有限的。这一点很重要:每当来自不同源组的表之间存在关系时(导入模式是一个源组,DirectQuery 是另一个源组),您将拥有有限的关系!尽管有种种限制和约束。

但是,我有好消息告诉你!如果我将维度表的存储模式切换到 Dual,这意味着它们也将被加载到高速缓存中,并且根据查询时哪个事实表提供数据,维度表将表现为导入模式(如果查询的目标是导入模式事实表)或 DirectQuery(如果查询从 DirectQuery 中的原始事实表检索数据):

作者图片

正如你可能注意到的,不再有有限的关系,这太棒了!

总而言之,我们的模型配置如下:

  • 原始 FactOnlineSales 表(包含所有详细数据)— DirectQuery
  • 维度表(DimDate、DimProduct、dim store)-Dual
  • 汇总表(销售产品汇总和销售商店汇总)—导入

厉害!现在我们有了聚合表,查询应该运行得更快了,对吗?哔!不对!

作者图片

Table visual 正好包含我们在销售产品 Agg 表中预聚合的这些列——那么,Power BI 究竟为什么运行 DirectQuery 而不是从导入的表中获取数据呢?这是一个公平的问题!

还记得我在开始时告诉你,我们需要让 Power BI 知道聚合表的 ,这样它就可以在查询中使用了。

让我们回到 Power BI Desktop,这样做:

作者图片

右键单击销售产品聚合表,然后选择管理聚合选项:

作者图片

这里有一些重要的说明:为了让聚合工作,原始事实表和聚合表中的列之间的数据类型必须匹配!在我的例子中,我必须将聚合表中 SalesAmount 列的数据类型从“十进制数”更改为“固定十进制数”。

此外,您会看到以红色书写的消息:这意味着,一旦您创建了一个聚合表,它将对最终用户隐藏!我对第二个聚合表(Store)应用了完全相同的步骤,现在这些表被隐藏了:

作者图片

现在让我们返回并刷新我们的报告页面,看看是否发生了变化:

作者图片

不错!这一次没有使用 DirectQuery,呈现这个视觉效果几乎需要 2 秒钟,而这次只花了 58 毫秒!此外,如果我抓取查询并转到 DAX Studio 查看发生了什么…

作者图片

如您所见,原始查询被映射到导入模式下的聚集表,消息“Match found”清楚地表明可视化数据来自销售产品聚集表!尽管我们的用户甚至不知道这个表在模型中存在!

即使在这个相对较小的数据集上,性能上的差异也是巨大的!

多个聚合表

现在,您可能想知道为什么我创建了两个不同的聚合表。好吧,假设我有一个显示各种商店数据的查询,也是按日期维度分组的。在 DirectQuery 模式下,不必扫描 1260 万行,引擎可以很容易地从缓存中,从只有几千行的表中提供数字!

本质上,您可以在数据模型中创建多个聚合表——不仅仅是组合两个分组属性(就像我们在这里使用的 Date+Product 或 Date+Store ),而是包括附加属性(例如,在一个聚合表中包括 Date 以及 Product 和 Store)。这样,您将增加表的粒度,但是如果您的视觉需要显示产品和商店的数字,您将只能从缓存中检索结果!

在我们的示例中,由于我没有包含产品和商店的预聚合数据,如果我在表中包含一个商店,我将失去拥有聚合表的好处:

作者图片

因此,为了利用聚合,您需要按照视觉要求在完全相同的粒度级别上定义它们!

聚合优先级

使用聚合时,还有一个更重要的属性需要理解——优先级!当“管理聚合”对话框打开时,有一个设置聚合优先级的选项:

作者图片

这个值“指示”Power BI 在多个不同的聚合可以满足查询的情况下使用哪个聚合表!默认情况下,它设置为 0,但您可以更改该值。数字越大,该聚合的优先级越高。

为什么这很重要?好吧,想象一个场景,你有一个有十亿行的主事实表。而且,您可以在不同的粒度上创建多个聚合表:

  1. 汇总表 1:按日期级别对数据进行分组—有大约 2000 行(5 年的日期)
  2. 汇总表 2:按日期和产品级别对数据进行分组—大约有 100,000 行(5 年的日期 x 50 个产品)
  3. 汇总表 3:按日期、产品和商店级别对数据进行分组——约有 5,000,000 行(100,000 行来自以前的 grain x 50 家商店)

现在,让我们假设报表可视化只显示日期级别的聚合数据。你怎么看:扫描表 1 (2.000 行)好还是表 3(500 万行)好?相信你知道答案:)理论上从两个表都可以满足查询,那么为什么要依赖幂 BI 的任意选择呢?!

相反,当您创建多个具有不同粒度级别的聚合表时,请确保设置优先级值,使粒度较低的表获得优先级!

结论

聚合是 Power BI 中最强大的特性之一,尤其是在具有大型数据集的场景中!尽管复合模型特性和聚合可以彼此独立使用,但这两者通常会协同使用,以在性能和拥有所有可用数据细节之间提供最佳平衡!

感谢阅读!

预订 DP-500 培训!

订阅,不要错过任何媒体上的故事!

power BI——您需要了解的所有重要滤波器

原文:https://towardsdatascience.com/power-bi-all-the-important-filters-you-need-to-know-583fec7c0f8c

…当然还有例子!

哈尔·盖特伍德在 Unsplash 上拍摄的照片

Power BI 是一个基于表之间的关系和连接的工具。

它使用 DAX 来构建度量,在度量中,需要一个过滤上下文来实现正确的计算。

本文将为您提供主要的过滤器功能和示例,以便您可以快速将其投入使用!

首先,这是一张数据表。

作者图片

这是一个简单的表格,我称之为[Table_Sample_Filters],但它将非常有助于帮助您了解过滤函数是如何工作的。

在这张表中,有一个层次结构。在级别 1 中,大写字母和级别 2 是下一个级别。为了便于解释,sales 列都是 1。

现在过滤器起作用了。

保持过滤器

这是一个重要的问题,你可能会用得最多。

在这里,这个函数对您指定的列的值进行“过滤”。

这里我在列[Level_2]上保留了一个等于“a”的过滤器

KEEPFILTERS = 
CALCULATE (   [Table_Sample_Sales_Measure],
    KEEPFILTERS ( Table_Sample_Filters[Level_2] = "a" )
)

这些是结果。

作者图片

您也可以组合它,使[Level _ 1]等于“A ”,或者[Level_2]等于“A”

如果你想把这个语句变成 AND,那么你可以用' && '代替' || '作为运算符

KEEPFILTERS_OR =
CALCULATE (   [Table_Sample_Sales_Measure],
    KEEPFILTERS ( Table_Sample_Filters[Level_2] = "a"
        || Table_Sample_Filters[Level_1] = "D" )
)

这是结果。

现在这里包括一个用黄色突出显示的附加结果。

作者图片

移除过滤器

这是另一个你可能会用到的方法,它需要一些思考。

在你遇到的每个表中,都有行。

让我们再次参考我们的桌子。

作者图片

这里对于第一行,[ Level_1]等于‘A’,[Level_2]等于‘A’,销售额为‘1’。

在 Power BI 中,如果去掉列【Level_2】,会发生什么?

这就是将要发生的事情。

作者图片

移除[Level_2]将提供累计至[Level_1]的销售汇总。

这正是 REMOVEFILTER 所做的。

REMOVEFILTERS =
CALCULATE (    [Table_Sample_Sales_Measure],
    REMOVEFILTERS ( Table_Sample_Filters[Level_2] )
)

在这个画面中,你会看到正在发生的事情。

作者图片

REMOVEFILTERS 删除了[Level_2]中的筛选器,取而代之的是按[Level_1]和 A、B、C、d 的类别来汇总总数。

由于该表中的每一行都有一个 Level_1 类别,REMOVEFILTERS 返回多个总计。

如果我移除桌面上的[Level_2],它会将我们带回之前看到的画面。

作者图片

所有例外

ALLEXCEPT 函数遵循与 remove filters 相同的逻辑。

此处的 ALLEXCEPT 表示删除除您指定的列之外的所有列上的筛选器。

ALLEXCEPT =
CALCULATE (
    [Table_Sample_Sales_Measure],
    ALLEXCEPT ( Table_Sample_Filters, Table_Sample_Filters[Level_1] )
)

这将是结果。

作者图片

在这里的度量中,它类似于在[Level_1]中进行“分组”。

这个功能帮助我摆脱了很多困境。

全部

在这里,它使所有应用于视觉的滤镜对滤镜“免疫”。这就是为什么它叫全部。

ALL_Measure = 
CALCULATE ( [Table_Sample_Sales_Measure], 
ALL ( Table_Sample_Filters ) )

在这里,我筛选了[Level_1]中的视觉“A ”,无论我做什么,ALL 度量都将返回 11,这是整个表的总和。

作者图片

当我过滤掉 B 的时候

作者图片

现在 ALL 有了其他参数,您可以只对一列使用它。它显示为删除该列的过滤器。

它的作用与 Level_2 的 REMOVEFILTER 相同。

ALL_Level_2 = 
CALCULATE (
    [Table_Sample_Sales_Measure],
    ALL ( Table_Sample_Filters[Level_2] )
)

这是结果。

作者图片

全部选定

这里,过滤器取决于所选择的内容。因此 ALL-SELECTED☺

这里我选择[Level_1]等于“A”

作者图片

在这里,我选择[Level_1]等于“A”和“B”

作者图片

注意所有基于用户选择的更改?

这就是 ALLSELECTED 所做的,如果用作分母,它会特别有用。

这些功能将带你走很长的路。

现在,如果您希望返回一个用于报告目的的表,将 CALCULATE 交换为 CALCULATETABLE,您将返回一个虚拟表。

如果你需要更多关于如何建立数据模型的细节,这里有一篇文章教你如何建立一个数据模型。这是另一个帮助你建立它的附加工具。

希望这篇文章对您有所帮助,祝您在数据之旅中好运!

力量 BI——与纸杯蛋糕的类比!

原文:https://towardsdatascience.com/power-bi-an-analogy-with-cup-cakes-3e92a0090600

Power BI 建模——继承混乱的模型

原文:https://towardsdatascience.com/power-bi-modelling-inheriting-a-messy-model-76e235fb851

在这些情况下该怎么做

尼古拉斯·皮卡德在 Unsplash 上拍摄的照片

你有没有继承过乱七八糟的模式?就像陷入蜘蛛网,被整个吞噬。

我说的吞噬整体是什么意思?

这意味着,如果你没有问正确的问题,你可能会花一整天的时间来制定措施和查询,却一事无成!

我去过那里。

图片胜过千言万语。

看看这种类型的模型。这里的每个“正方形”都是一张桌子。

作者图片

我记得有人开玩笑地称这种数据建模风格为“蜘蛛网模式”。😀

在这种情况下你能做什么?

你可以做几件事。在深入细节之前,先问一些宽泛的问题。

怎么办。

  1. 确定并理解报告的目的
  2. 确定事实表
  3. 确定与事实表相关的过滤表
  4. 复制度量标准和视觉效果来测试您的理解
  5. 查看与事实表相关的超级查询步骤

理解报告的目的

首先,在你做任何事情之前,询问一下报告的目的。这是一个很重要的问题。这将为你接下来需要做的事情打下基础。

记下受众是谁,他们阅读此报告的频率以及原因。这背后的解释是什么?您将需要这些信息来帮助您理解数据模型。

如果可能的话,在报告的每一页,询问每一页的主旨。

在浏览模型时,请将这些作为您的心理笔记。

确定事实表。

既然您已经了解了报告的内容,那么是时候深入研究模型本身了。

让我们确定事实表。事实表应该比其他表有更多的连接。

再看关系,他们很可能处于一对多关系的多端。

确定了事实表后,您可以进行列检查,以了解报告的内容。您可以通过浏览实际的报告和事实表来做到这一点。

试着记住报告的目的。在任何时候,如果你发现事实表与实际的报告不相关,如果可能的话,是时候回到以前的开发者那里,问问他们为什么在那里。

您会发现,很多时候这里有事实表用于特定的报告目的。它只在那里被报告一两次,但它留在那里只是为了以防有额外的请求。

记下这些事实表。

这是为了让您了解什么被认为是“重要”的表,并且大多数度量也是围绕这个表构建的。

作者图片

用红色突出显示的很可能是事实表。即使他们不是,这也是一个很好的问题,问为什么有这么多反对它的加入。毕竟,如果它是一个不包含重要信息的表,为什么会有这么多的连接呢?

现在你可能会想,我只是假设在你继承的模型中实际上有事实表。如果它非常复杂,以至于没有事实表或者没有人知道它们是什么,该怎么办?

在这种情况下,请记住报表的目的,并在报表中查找引用这些表的度量。那些可能是你的事实表。

在下面的测量中,它会给你很多线索。例如,如果是总计的总和,总计表在哪里?它非常重要,足以在报表度量中使用,因此值得一看。第二部分是 KEEPFILTERS。这可能是一个过滤表。

Measure = CALCULATE(SUM(Totals), KEEPFILTERS(Store == A))

请记住,报告的目的是提供关于事实表位置的关键提示。

识别过滤表

仔细看看事实表。应该有其他的表加入进来反对它。这些可能是过滤表。您可以通过转到主报告并检查过滤器来验证这一点。

是否有针对最终用户的过滤器?他们用什么桌子?很可能这些都是过滤表。

记下这些过滤表,它们在与利益相关者的会议中对你有所帮助。你可以参考这些表格,它们很可能是你可以用来“切割数据”的表格。

这会给你一些谈判的权力。假设有人要求以某种方式切割数据,您可以问自己过滤表是否存在,或者您可能需要更多的工作来获得这样的表。

作者图片

黄色的表可能是过滤表。

仔细检查报告

既然您已经理解了报告的目的,并且已经确定了一些事实表(其中一些可能已经过时了)和过滤表,那么是时候通过问自己一些问题来再次检查您对报告的理解了。

这是有趣的部分!

看看你能否复制一些报告的视觉效果,最好是最重要的,以及你是否理解它们是如何创建的。您可能会遇到某些可视级别的过滤器以及“内置”了过滤器的度量

这些是问前一个开发者的好问题。

“为什么这个视觉效果只有 2021 年 4 月 1 日之后才有视觉级别过滤器,而其他视觉效果没有?”

"为什么有一个测量 XYZ 的过滤器?"

"为什么在 Power Query 中而不是 DAX 中这样做?"

这些都是要问的问题。记得回顾报告的目的。

比较表格时使用 TREATAS、INTERSECT 和 EXCEPT。这些函数将帮助您比较表,而不必建立实际的关系。这样做将使模型在导航时保持不变。如果你想了解更多关于这些功能的信息,请点击这里查看这篇文章。

查看与事实表相关的超级查询步骤

现在,您已经知道了事实表和过滤表在哪里,以及如何再现一些度量和可视化,是时候看看 Power Query 了。

作者图片

回顾 Power Query 中的转换过程,特别注意构建事实表的过程。您可能想知道为什么创建事实表过程中的每一步都是必要的。这需要一些时间。

检查时,总是复制查询。您也不想意外删除步骤。尽量用你理解的方式重命名这些步骤。它将帮助您跟踪您已经检查过的步骤,以及哪些步骤还需要检查。

如果其他的一切都失败了,你真的对报告一无所知,并且收到了前一个开发人员令人困惑的回复?

这份报告一提出来你就取消会议了吗?

你被告知去解决它?😤

只需向每个人展示蜘蛛网模式,并告诉每个人你将需要重建一切。很可能从一开始就错了!

电力 BI —表格、清单和记录

原文:https://towardsdatascience.com/power-bi-table-lists-and-records-4731fa6f0081

…使用这些数据类型协助数据建模

杰克·亨特在 Unsplash 上的照片

电力查询是一个非常强大的工具。不仅因为您可以进行转换,还因为它使用了 3 种主要的数据类型。

一旦你知道如何使用这三种数据类型,你会更加喜欢 power query。

那么这三种数据类型是什么呢?它们是表格、列表和记录。

在微软网站上,它为你提供了非常全面的每种类型的可用功能,但是将你的数据转换成这些数据类型可能会造成混乱。

让我们看看如何获得这些数据类型并应用一个函数,这样您就可以在日常工作中这样做了。

如何转换您的数据以便获得这些数据类型?

使用分组依据获取数据类型

在 Power Query 中,您可以对数据进行分组,并使用 all 行返回一列表。

这是我们正在使用的数据。

作者图片

让我们使用 group by 并将 Level_1 分组在一起。

作者图片

一旦我开始对数据进行分组,Power Query 就会返回一列表。

作者图片

这是获得表数据类型的一种方法。

需要注意的一件重要事情是,这些列是按照所有行的 Level_1 进行分组的。没有任何聚合应用于它。

现在你能用这列表格做什么呢?

将表格函数应用于表格

在具有表数据类型的[Tables_Grouped]列中,您现在可以将表函数应用于这些分组表中的每一个。

在这里,我添加了一个名为“应用 _ 表格 _ 函数”的新列,并使用了表格。SelectRows 选择[Sales]超过 3 的行。

作者图片

这是你悬停在桌子上的结果。

作者图片

这里的数据预览显示,在 Level_1 = B 分组数据中,只有大于 3 的销售额被捕获。因此有了 4 和 6。

让我们试试另一个例子,添加一个名为“Table”的新列。FindText "并使用表。FindText

作者图片

这是结果。

作者图片

“桌子在这里。FindText”找到包含字母“a”的行,并返回一个表。

这是在 Power Query 中使用表的一种方式。列表怎么样?

将列表函数应用于列表

您可以使用[表格][列]作为格式在超级查询中获取列表。一旦你在那里,你可以应用列表函数。

这里我用的是列表。Sum 获得 Level_1 分组中每笔销售额的总和。

作者图片

让我们再举一个例子。你一定会喜欢这些名单的。

比方说,我想从 target 列中了解达到的计数目标。

这里我们可以使用列表。相交用于演示目的。你当然可以使用列表。选择大于 11 的

作者图片

如果这里有一个数字 11 的交叉点。会算的。

列表还有一些更复杂的用法,我无法在这里介绍,但是对于绝大多数情况,一旦您将数据类型添加到列表中,列表函数就可以工作了。

有几个怪癖不会在本文中讨论,比如在计算中使用变量,但是它们将在以后讨论。

微软网站上还有许多其他的列表功能。要点是一旦你知道如何得到列表数据类型,你现在可以应用这些函数。

记录

很多时候,您会发现列表和表格数据类型是您将使用的主要数据类型。记录数据类型不常用。

要访问单个记录,可以使用大括号。

作者图片

超级查询是零索引的,所以这样做将返回列表的第一条记录。

概括一下,

这里的关键是这个。

【表格】【列】{记录}

使用“分组依据”和“所有行”来获取表格格式。这样做将返回表。【表】

使用另一个[]括号来引用表中的列。这将返回一个列表。【专栏】

返回列表时,使用括号返回列表中的项目。{记录}

只要记住这一点,就可以用 Power Query 做很多繁重的工作。

探索它,玩它,测试它。

如果您知道如何清理表格并将其转换成几乎任何您想要的形状,使用列表函数提取,您已经赢得了 80%的数据建模战!剩下的就是将表放入星型模式中!

Power BI 提示和技巧:按经理层级过滤

原文:https://towardsdatascience.com/power-bi-tips-tricks-filter-by-manager-hierarchy-14e9a490b4d9

在 Power BI 中创建一个切片器,根据经理的控制范围进行过滤。

作者图片

问题简介

在 Power BI 中创建仪表板时,通常会有一个请求,要求查看领导控制范围内的数据。这意味着我们需要使用管理器层次结构创建一个切片器。因为我们不仅要包括经理的直接下属,还要包括他们的直接下属的报告(等等),所以我们需要采取一些额外的步骤。

过程

  1. 创建一个包含员工 ID 和经理 ID 的文件
  2. 使用 PATH()函数确定员工的管理链
  3. 确定管理链的长度
  4. 为链中的每个级别创建一列
  5. 添加切片器

1.创建文件

您的文件至少需要包含员工姓名、员工 ID 和经理 ID。

有几件事要记住:

  • 员工 ID 在每一行中应该是唯一的值
  • 每个经理 ID 需要有一行(每个经理也应该作为雇员出现在文件中)
  • 层次结构顶部的人员(例如,CEO)应该有一个空白的经理 ID 列

作者图片

2.识别管理链

使用 PATH()函数创建一个列来标识每个员工的管理链(他们的经理、他们经理的经理等)。)到层级的顶端。

作者图片

Moira 的管理链中只有她自己,因为她处于领导层级的顶端,而 David 的管理链中有三个人,因为他(1)向 Johnny (2)报告,Johnny 向 Moira (3)报告。

3.确定管理链的长度

虽然这是一个小数据集,我们可以清楚地确定最长管理链的长度(3 ),但对于较大的数据集,这要困难得多。幸运的是,我们可以使用 PATHLENGTH()函数来计算它。

作者图片

我们这样做是为了确定需要添加多少列,以便组织的每个级别都有一个列。在我们的数据集中,有三个级别,所以我们要添加三列。

要查找最大路径长度,您可以按链长列对数据进行排序,或者创建一个度量值*“最大链长=最大(示例[链长])”*

您还可以限制列的数量,以便只能筛选组织中的特定级别。例如,如果您只想筛选 CEO 以下的两个级别,那么您只需创建三列。要点是,您不必为组织中的每个级别创建一个列,如果您愿意,可以限制它。

4.为每个级别创建一列

现在,您将为要筛选的每个级别添加一列。您可以对每个列使用相同的 DAX,只需更新 Lvlnumber 变量以匹配您所在的级别。例如,对于层次结构的第一级,Lvlnumber 将是 1,对于层次结构的第二级,Lvlnumber 将是 2,依此类推。

level 1 =
var Lvlnumber = 1
var lvl key = path item(Example[管理链],lvl number,INTEGER)
var lvl Name = lookup value(Example[员工姓名],Example[员工 ID],lvl key)
var Result = lvl Name
返回结果

作者图片

事实:DAX 正在寻找给定级别的员工 ID,在本例中是 1。因此,它获取管理链中的第一个员工 ID 4001,然后查找该员工的姓名 Moira Rose。

5.添加切片器

最后,您已经准备好为您的经理层次结构创建切片器。在“报表”视图中,添加一个切片器视图,然后添加您创建的希望能够作为筛选依据的每个级别列。

作者图片

旁注,每个级别中的“(空白)”包括该个人。例如,约翰尼下面的“(空白)”包括约翰尼。这是一个有用的功能,您可以为领导过滤数据,但取消选中(空白)则领导看不到他们自己的数据。

现在你知道了!您现在可以通过领导者的控制范围来过滤您的数据。

如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。这是 5 美元一个月,我赚一小笔佣金,如果你使用我的推荐链接。

https://jeagleson.medium.com/membership

詹娜·伊格尔森(Jenna eagle son) 我的背景是工业组织心理学,我已经在人物分析中找到了自己的家。数据使我的作品变得生动。我主要使用 Power BI,但偶尔也会使用 Tableau 和其他工具。我很想听到更多关于你的旅程!在LinkedinTwitter上联系我。

Power BI XMLA 端点,非神秘化

原文:https://towardsdatascience.com/power-bi-xmla-endpoints-demystified-ebfd3c8772e6

在 Power BI 的发展过程中有许多改变游戏规则的因素。XMLA 端点可用性一直是前三名之一。

由 Isaac Smith 在 Unsplash 上拍摄的照片

在微软 Power BI 作为独立产品的发展过程中(2015 年),从 BI 分析领域的一个害羞的挑战者到不可否认的领导者,有许多“游戏规则改变者”——这些功能带来了革命而不是发展!复合模型、数据流、人工智能视觉、混合表、书签……应有尽有!

然而,真正开启了一个全新的可能性世界的特性之一是——使用 XMLA endpoint 访问底层的 Power BI 数据集,但这一特性在有经验的 Power BI 实践者社区之外可能仍然“不为人知”!

虽然很多人认为 Power BI 是一个数据可视化工具,但是我不得不承认 Power BI 首先是一个数据建模工具 !这就是为什么理解什么是 XMLA 端点以及为什么应该关注它们是极其重要的。

XMLA 是什么?

让我们从基础开始。你可能听说过 XML,但是现在到底什么是 XMLA?!不要害怕——这只是——XML for Analysis!与其引用 XMLA 枯燥的定义,不如让我来说明这个“东西”是如何工作的…

作者图片

简单地说,Analysis Services 的实例“公开”了一个端点,然后可以由各种客户端工具使用,如表格编辑器、SSMS、Azure Data Studio、SQL Server 数据工具等。与 Analysis Services 实例“对话”。如果你想知道——好的,Nikola,能够与 AS 实例“交谈”有什么特别之处……嗯,就像在常规的现实生活交谈中,你可以指导某人做什么,这同样适用于与 Analysis Services 实例的“交谈”:使用客户端工具,你可以读取 AS 实例的内容,但更重要的是,你还可以操作存储在 Analysis Services 实例中的 TOM(表格对象模型),并修改其默认行为!

历史课——SSAS 公司

如果您认为 XMLA endpoint 是某种全新的、强大的双独占特性,那就大错特错了!对于我们这些曾经和 SSAS 多维一起工作的人来说,使用 XMLA 是一个众所周知的概念。所以,这是分析服务和 XMLA 之间的长期关系…现在,你可能会问自己:为什么他坚持重复“分析服务”,“分析服务”?我是一名 Power BI 开发人员,我有兴趣了解这个“XMLA 东西”在 Power BI 中是如何工作的!

有道理,所以我们来解释一下这个。当您打开 Power BI Desktop 来创建一个解决方案时(我特意使用了 word solution 来代替报告),Power BI 将启动一个 Analysis Services 的本地实例(您又开始使用 Analysis Services 了!)在幕后存储您在解决方案中使用的数据—当然,假设您对数据使用导入模式。一旦完成,您将部署您的解决方案到 Power BI 服务——您可能会注意到两个工件被一起部署——报告和数据集。

Power BI 数据集只不过是 Analysis Services 实例中的一个数据库!

类似地,Power BI workspace 就是一个 Analysis Services 实例,负责管理所有不同的数据库(Power BI 数据集)。

为什么 XMLA 端点可用性对 Power BI 很重要?

好了,我们已经解释了什么是 XMLA 以及它与 Power BI 的关系。现在,让我们试着理解为什么拥有这个特性真的会改变游戏规则!

在 XMLA endpoint avail ability for Power BI 解决方案出现之前,通常的开发流程如下所示:

您在 Power BI Desktop 中创建一个解决方案,一旦完成,您就可以将其部署到服务中。如果必须应用一些更改,您应该在 Power BI 桌面中再次获取该文件,进行所需的更改,然后再次发布它。

而且,在 XMLA 端点可用之前,这是一种“全有或全无”的方法——如果不重新处理整个数据集,就不可能更改一个表!但是,我们可以以某种方式接受这一点…但是,某些数据模型属性完全无法从 Power BI 桌面访问,并且没有办法改变它们的默认行为。

现在,让我们想象这样一个场景:您有一个巨大的 Power BI 报告,有 20 个表,您只需要对其中一个表进行微小的更改。通过利用 XMLA 端点,您可以只重新处理您修改过的表,而不是重新处理整个模型!

在我向您展示如何做到这一点之前,需要提前完成 Power BI 租户的一些设置。首先,在编写的时候, XMLA 读/写需要某种高级许可 ,要么是按容量,要么是按用户。

作者图片

启用后,我将获取我的高级工作区的连接字符串:

作者图片

我现在将打开一个客户端工具,在这个例子中,我将使用表格编辑器 2(免费版),但你也可以使用 SSMS,并获得相同的结果。一旦我连接到 Analysis Services 实例(不要忘记,Power BI 工作区就是 Analysis Services 的实例),我将能够选择一个我想要连接的数据库(Power BI 数据集):

作者图片

我选第三个,叫测试。一旦连接到数据库,我就可以访问大量的属性,不仅仅是在模型级别,还可以在单个表级别。

假设我想在我的 FactInternetSales 表中创建一个新的度量,但是我不想处理整个数据模型,而只想处理我已经更改的表:

作者图片

您可以选择不同的刷新模式。解释刷新模式超出了本文的范围,但是我建议您查看表格编辑器文档中的详细信息。这将只触发所选表的重新处理,而不是整个数据模型!

显然,通过能够使用脚本来细粒度地刷新数据,您甚至可以通过利用 PowerShell、Azure 函数等来进一步增强整个过程。

想象一下这样的场景:您有一个包含大量历史数据的表,但您不想每次都刷新整个表。您可以将表分成多个分区(再次使用 XMLA endpoint),然后为最新的分区设置增量刷新,同时不再重新加载旧的分区!

何时使用 XMLA 端点?

说实话,可能性真的是无限的!除了我之前展示的在更细粒度级别进行处理的示例之外,您还可以使用 XMLA 端点来查询数据、添加新列或以编程方式修改现有列:

作者图片

您还可以切换 Power BI 报告的数据源,或者添加一个明确的数据源定义,如这里的所述。

此外,您从表格编辑器执行的所有操作,比如定义对象级安全、计算组,创建分区,透视图等。都是通过 XMLA 端点完成的!

显然,可能性是无限的,可能可以写一整本书,涵盖使用 XMLA 端点和 Power BI 时所有可用的用例…

重要提示: 请记住,一旦您通过 XMLA 端点进行了更改,并将 Power BI 解决方案的更新“版本”部署到服务中,您将无法再下载 pbix 文件!因此,请始终保留文件的备份版本,以防您需要应用一些额外的修改。

结论

在 Power BI 的发展过程中有许多“游戏规则改变者”。他们中有些人没有辜负他们的高期望,有些人没有。但是,如果你问我,为 Power BI 数据集启用 XMLA endpoint 可能是迄今为止改变游戏规则的三大因素之一!即使它(仍然)只是高级功能,即使它在部署修改版本后对 pbix 下载有严重限制,这仍然是赋予您根据您的需求定制 Power BI 数据集的巨大能力的功能之一。

感谢阅读!

预订 DP-500 培训!

成为会员,阅读 Medium 上的每一个故事!

通过仿真进行功耗计算

原文:https://towardsdatascience.com/power-calculations-with-simulation-49d2f959161c

用 R 中的例子来说明

由 Unsplash 上的 mk. s 拍摄的照片

介绍

功效计算是实验设计中的一个必要步骤。它在估计检测给定效应所需的样本量,或者在给定固定样本量和其他设计参数的情况下,我们可能期望的最小检测效应方面起着关键作用。在本文中,我将通过 r。

设计的力量在于,对于给定的效应大小和给定的统计显著性水平,我们能够拒绝零效应假设的概率(Duflo 等人,2007)。换句话说,功效是在一个治疗效果存在时(即零假设为假时),正确拒绝零假设的概率。

基本功率计算的组件

我认为理解功耗计算的最简单方法是使用最小可检测效应(MDE)的封闭公式:

  • MDE :给定我们的设计参数,我们可以从零区分的最小效应大小,我们可以检测的效应大小越小,我们的统计能力就越强。
  • P: 将样本的比例分配到治疗组。
  • 检验的显著性水平或大小(α): 概率 I 型错误或假阳性:当无效假设为真时,拒绝无效的无效假设(即,当无效时,断定治疗有效)。它通常设置为 5%。
  • 功效(1-κ): κ是第二类错误或假阴性的概率:当零假设为假时,未能拒绝零假设(即当存在一个时,未能检测到效应)。通常设置为 0.8。
  • N: 样本量
  • σ: 结果变量的标准差

从等式中我们可以了解到几个关系:

  • 它还隐含地定义了实现所需功率水平所需的样本大小 N。
  • 增加样本量会降低 MDE 并提高功效。
  • 降低结果方差(潜在人群中的变化较小)会降低 MDE 并增加功效。
  • 在力量和大小之间有一个权衡。当α增加时,对于给定的功率水平,MDE 增加。在错误地得出治疗有效而治疗无效的概率和错误地得出治疗无效的概率之间存在权衡。
  • 治疗臂之间的等分通常使功率最大化。

接下来,我们将这些东西放入 R:

运行上面的代码将返回下面的结果。它表明,偏离 50/50 治疗/控制分配增加了 MDE,增加样本量大大降低了 MDE,正如我们所预期的。我用runif()来创建从均匀分布中抽取的随机变量,我们可以看到,std。当我使用同一个runif(100,0,5)从一个从 0 到 5、随runif(1000,0,1)减少、随runif(10,0,8).增加的均匀分布中产生 100 个随机均匀分布时,偏差不变。当方差减少时,MDE 减少。

它还表明,如果我们希望能够以 80%的功效检测到效应大小≥0.25(80%的机会检测到该效应),给定结果变量分布,我们需要 1000 的样本大小。如果我们只收集了 100 个单位的样本,我们将无法区分一个效应<0.8 from zero even if it is.

baseline: sample size: 100 std dev: 1.424976 MDE: 0.8023922decrease treatment allocation to 20%: sample size: 100 std dev: 1.424976 MDE is bigger: 1.00299increase treatment allocation to 70%: sample size: 100 std dev: 1.424976 MDE is bigger: 1.00299decrease outcome variable variance: sample size: 100 std dev: 0.2874839 MDE is smaller: 0.1618798increase outcome variable variance: sample size: 100 std dev: 2.35792 MDE is bigger: 1.327725increase sample size: sample size: 1000 std dev: 1.424976 MDE is smaller: 0.2526117decrease sample size: sample size: 10 std dev: 1.424976 MDE is bigger: 2.67033

Power Calculation using Simulation

The basic idea of a simulated power analysis is to construct some data either through simulation or existing historical data, pre-set the sample size and effect size (or any other parameters we would like to test), use an analytical method such as regression, run it a bunch of times so wen can see the sampling variation of the power.

The code below does the following: I assumed a list of sample size and effect size I would like to try, created random variable X from a uniform distribution and Y by adding additional random noise to effect*X, ran a simple linear regression and got the probability of correctly rejecting the null when null is false at a significance level of 5% , then I ran the same process 500 times to get the sampling variation of the statistical power from which I calculated the mean of it.

At the end of the program, I created the graph below that shows **对于一个给定的功率水平,例如虚线标记的 80%,最小可检测大小或最小样本大小。**例如,为了能够在 80%的功率下检测到 1.0–1.5 的效果大小(80%的情况下我们可以发现这种效果),我们需要至少 500 个样本。给定 250 个样本,我们只能检测到效果> ~1.8。换句话说,我们需要一个大于 1.8 的效应,才能有 80%的机会找到有意义的结果。如果我们认为影响实际上不可能那么大,我们需要调整我们的实验设计——增加样本,使用更精确的估计方法。

最后,这里列出了功耗分析的一些好参考:

艾希、苏珊和圭多·伊本斯。2017."随机实验的计量经济学."野外实验手册,73–140。

《社会研究随机实验的核心分析》《社会研究方法圣人手册》(2008):115–133 页。

杜弗洛、埃丝特、雷切尔·格伦纳斯特和迈克尔·克雷默。2007."在发展经济学研究中使用随机化:工具包."发展经济学手册,3895-3962。

吉尔曼,安德鲁和珍妮弗·希尔。2006.使用回归和多级/层次模型的*数据分析中的“样本大小和功效计算”,*437–454。剑桥大学出版社:英国剑桥。http://www . stat . Columbia . edu/Gelman/stuff _ for _ blog/chap 20 . pdf。

List、John A、Azeem M. Shaikh 和 Yang Xu。2019."实验经济学中的多重假设检验."实验经济学,22:773–793。

麦康奈尔、布兰登和马科斯·维拉-埃尔南德斯。2015."超越简单的样本量计算:从业者指南."住区中心联工作文件 W15/17。【https://ifs.org.uk/publications/7844】T4。

奥兹勒,伯克。"什么时候你应该分配更多的单位到一个研究组?"世界银行发展影响(博客),2021 年 6 月 21 日。https://blogs . world bank . org/impact evaluations/when-you-should-assign-more-units-study-arm?CID = WBW _ AL _ BlogNotification _ EN _ EXT。最后一次访问时间是 2021 年 8 月 3 日。

尼克·亨廷顿·克莱因。“电源分析模拟”,2021 年 12 月。https://nickch-k . github . io/econometrics slides/Week _ 08/Power _ simulations . html

专业人员使用的 PowerBI 性能改进策略

原文:https://towardsdatascience.com/powerbi-performance-improvement-strategies-used-by-professionals-48b1a0b504fd

提高 PowerBI 性能的技巧(第 1 部分)

活动发起人在 Unsplash 上的照片

创建报告很简单。谁都可以做。然而,将专业人士与其他人区分开来的是报告的表现。在提交报告时,性能至关重要。提高 Power BI 的性能有几个好处。首先,它可以使工具响应更快、更容易使用,从而提高用户满意度和生产率。其次,更快的性能可以支持更复杂和数据密集型的操作,从而实现更高级的分析和洞察。总的来说,提高 Power BI 的性能有助于确保该工具在支持用户需求及其数据分析任务方面有效且高效。

在我们开始之前,让我们回顾一下 Power BI 快速总结。Power BI 是一个数据分析和可视化平台,可以收集、分析和可视化来自各种来源的数据。它包括用于数据获取、清理和转换的工具,以及一系列可用于从数据中获得洞察力的预置可视化和仪表板。Power BI 还提供了与各种数据源的连接,包括数据库、云服务和 web APIs,这使得集成来自多个来源的数据并在单个平台上进行分析变得非常容易。借助 Power BI,用户可以收集、清理和转换数据,创建交互式可视化和仪表盘,并与组织中的其他人分享他们的见解。

让我们开始吧。我将报告创建过程分为四个步骤。它们如下:

  • **数据采集:**将数据导入 PowerBI
  • **数据建模:**建立数据表之间的关系映射
  • **模型推进:**如果需要,创建度量和新列
  • **报告创建(添加可视化):**将所有可视化内容放在一起

这篇文章将关注第一个部分,即数据采集。包括数据连接和数据加载。我们将探讨数据连接中的连接类型、直接查询和导入模式。在数据加载部分,我们将探讨完全加载和增量加载之间的区别,以及数据连接和 Dat 加载的推荐做法。

*** *注意:**数据建模、模型改进和报告创建的性能改进将在后续文章中发布。

报告生成流程

1.数据采集

将数据导入 PowerBI 称为数据采集。我将数据采集过程分为两个逻辑分区。首先,创建一个与数据源的连接,然后定义 powerBI 将如何在刷新时将数据加载到系统中。

1.1:数据连接的类型

可以通过两种方式在 powerBI 中获取数据:

一、直接查询

直接查询是您的仪表板在运行时直接查询数据源的方法。每个过滤器和与报告的交互都会引发进一步的查询。

直接查询图

使用直接查询的好处:

  • 它使您能够与数据实时交互,确保您始终拥有最新的信息。这在处理大量不断变化的数据集时尤其方便。
  • 您可以连接到广泛的数据源。这包括关系数据库、大数据源,甚至是流数据。

使用 DirectQuery 时要考虑的事项(性能方面):

-后端源响应查询结果所用的时间决定了所需的视觉刷新时间,因为所有 DirectQuery 请求都被转发到源数据库。使用 DirectQuery 进行可视化时,建议的响应时间(返回请求的数据)少于 5 秒。

数据源快速响应是非常重要的。如果我必须在 SQL 数据库上进行直接查询,我总是从编写 SQL 查询开始,以获得特定表的总行数。如果耗时超过五秒,我会考虑数据库性能调优。这里有两种优化性能的方法。

  • 使用索引:
    -
    索引是数据库性能调优的一个重要方面,因为它们允许数据库系统快速发现并从表中获取数据,这大大提高了查询效率。数据库可能会快速搜索索引并发现表
    中的相关行,而不是扫描整个表——但是,在某些情况下,索引可能不会提高数据库性能,甚至可能会对性能产生负面影响。例如,如果查询与索引列无关,则不会使用索引,查询将必须扫描整个数据库以检索必要的数据。如果一个索引包含太多的行,它可能不会提供太多的性能提升,因为数据库仍然必须扫描大量的行来识别相关数据。
  • 联接类型:
    -
    联接是数据库性能调优的重要组成部分,因为它们会极大地影响使用多个表的查询的性能。
    -有几种不同类型的表连接,包括内部连接、外部连接和交叉连接。内部联接是最常见的,它返回在两个表中都有匹配值的行。另一方面,外部联接返回一个表中的所有行和另一个表中的匹配行。交叉联接返回两个表中行的所有可能组合。通过优化表的连接方式,可以提高这些查询的效率和性能。
  • 创建查询计划:
    ——查询计划是对数据库系统执行 SQL 查询所要采取的动作的详细描述。它描述了操作的顺序、要使用的数据结构和算法,以及关于如何进行查询的其他参数。查询计划是数据库性能调优的一个重要部分,因为它们提供了有关查询将如何运行的信息,并且可以帮助发现需要改进的地方。
    -查询计划性能调优经常涉及改变查询、数据库设计或数据库配置,以提高查询性能。这可能包括优化索引的使用、更改模式以优化数据访问,或者使用其他方法来提高查询效率。
    -理解和分析查询计划是数据库性能调优的重要组成部分,因为它有助于保证高效地执行查询。
  • 查询提示 :
    -查询提示是在 SQL 查询中使用的指令,用于向数据库系统提供关于如何执行查询的附加信息或指令。查询提示可以用来影响查询策略,从而影响查询的性能。当数据库优化器无法为查询选择最佳计划,而用户希望为系统提供进一步的指导时,通常会使用查询提示。
    -例如,查询提示可用于识别某个用于查询的索引,或指定如何处理查询。
    -使用查询提示进行性能调优是提高单个查询性能的有效技术,但是必须小心处理。过度使用查询提示会导致复杂而脆弱的查询,难以维护和理解,并且在不同的情况下可能无法很好地执行。
    -一般来说,查询提示 应该只作为最后的手段使用 ,毕竟其他的性能调优策略已经探索出来了。

二。进口

导入会复制数据源并创建快照。无论何时请求数据,它都直接与存储的数据一起使用,而不是连接到直接的数据源。

导入模式图

使用导入的好处:

  • 因为数据被缓存,所以性能比直接查询好。
  • 完全支持所有 DAX 表达式。

使用导入模式时的注意事项:

  • 没有刷新,数据就不是实时的。
  • 根据导入数据的大小,需要大量内存和磁盘空间。在您的计算机上(实施期间),在云/本地服务器上(发布时)。
  • PowerBI 文件大小限制为 1 GB 如果文件较大,请升级到高级许可证,允许 50 GB 的数据。

总而言之

如果数据大小适中,导入模式是最好的,但是在数据刷新之前,您将无法访问最新的数据。在这种情况下,DirectQuery 是最好的选择,但是您必须确保您的数据源性能良好。

克劳迪奥·施瓦茨在 Unsplash 上拍摄的照片

1.2:数据加载

对于报告来说,数据加载过程至关重要。典型的场景是从云中加载大型数据集,然后从数据源加载到报表中。

powerBI 中的数据源可以是 Excel 电子表格(例如)、JSON 文件(例如)或任何类型的输入文件,如 XML、CSV 等。必须使用 Power BI“主页”选项卡中“数据源”窗格上的“加载数据源”按钮将此数据源加载到 Power BI 中;这是创建数据集的第一步。此步骤需要一些时间,如果您没有访问允许加载的数据源的权限,则可能需要额外的步骤。

确保使用有效的方法将数据加载到报告中。这将有助于提高整体性能。powerBI 中有两种主要的负载类型。

一、满载

这是 powerBI 中的默认负载类型。满载意味着每次检索完整的数据集并丢弃以前的数据。这可能有点麻烦,尤其是如果您想要生成一个不限于数据集的特定子集的报告。满负荷可能不适用于所有数据集,但它将适用于它们!

导入模式的满负荷

如果数据量很小且数据变化频率很高,则首选满载,但是,如果数据量很大,则成本会很高,因为每次都会完全擦除和加载数据。这就是增量加载的用处所在。

二。增量负载

假设你有五年前的数据。只有最近的数据更改,其他数据保持不变。在这种情况下,增量负载是理想的选择。它只刷新最近的数据,不刷新以前的数据。

当您应用增量负载时,它会在您的数据上创建一个动态分区。由于这种分区,对数据的读取操作变得更快,从而提高了性能。该分区将需要定期刷新的数据与刷新频率较低的数据分开。

因为,增量加载有助于减少刷新数据集所需的时间和资源,并且可以提高 PowerBI 报告的响应速度。下图显示了增量加载的刷新周期。

增量负载

若要使用增量加载,首先需要确定数据集中的哪些列包含将用于确定哪些记录是新的或更新的信息。这通常是时间戳或唯一标识符。然后,您可以配置 PowerBI,只加载这些列中的值大于上一次加载的最大值的记录。

例如,假设您有一个包含客户订单的数据集,并且您希望使用增量加载来只加载新订单。您可以使用订单日期作为列来确定哪些记录是新的。在第一次加载时,PowerBI 将加载数据集中的所有订单。在后续装载中,PowerBI 将只装载日期晚于前一次装载的最大日期的订单。

一言以蔽之

如果数据很小并且不经常更改,请使用满载。但是,如果您有大量数据,并且大部分历史记录没有更改,增量刷新是最佳选择。增量刷新将减少加载时间,从而提高性能。

在本文中,我们探讨了提高 PowerBI 报告性能的数据获取注意事项。我们讨论了数据连接,包括导入模式和直接查询,以及数据加载,包括完全加载和增量加载。在下面的帖子中,我们将讨论在开发数据模型以获得最佳性能时需要考虑的因素。

如果您喜欢这篇文章,并且想阅读本系列的下一部分,请关注我,以便在文章发布时收到通知。你也可以通过点击这里订阅 Medium,在你方便的时候阅读这样有趣的文章。

形象学分

所有图片,除非特别注明,均为作者所有。

每个数据科学家都应该知道的强大的一句话

原文:https://towardsdatascience.com/powerful-one-liners-in-pandas-every-data-scientist-should-know-737e721b81b6

使用熊猫可以在一行中完成的事情

照片由克里斯汀玛丽在 Unsplash 上拍摄

训练数据驱动的机器学习模型从未像今天这样简单。例如,假设你正在训练一个普通的神经网络。这里,针对隐藏层的数量及其维度调整架构、调整超参数或更改损失函数都可以通过对模型定义或其优化器进行轻微修改来完成。

一方面,这是有利的,因为它减少了从零开始花费时间设计架构的繁重工作。然而,这往往导致机器学习从业者/研究人员忽视了数据可视化和分析的重要性——导致他们直接训练深度模型,而没有对他们的数据建立清晰的理解。

因此,在这篇文章中,我想向您介绍一些专门针对使用 Pandas 的表格数据的重要而强大的一行程序,这将帮助您更好地理解您的数据,并最终(希望)帮助您设计和构建更好的机器学习模型。

资料组

在这篇文章中,我将使用一个由一千名员工组成的虚拟数据集进行实验,这个数据集是我自己用 Python 创建的。下图概述了我们正在试验的数据集。

数据框的前五行(图片由作者提供)

下面的代码块演示了我的实现:

熊猫的俏皮话

接下来,让我们讨论熊猫中可用的一些流行功能,以便对可用数据进行有意义的理解。

# 1n-系列中的最大值

假设我们想从找出这个数据集中薪酬最高的前 n 个角色开始。你可以在 Pandas 中使用[nlargest()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.nlargest.html)方法来实现。该方法返回列中具有最大值的前n行,按降序排序。

请注意,nlargest()返回整个数据帧,也就是说,该函数还返回未指定排序的列。但是,它们不用于对数据帧进行排序。下面的代码片段描述了在我们的数据帧上使用nlargest()方法。

nlargest 方法的输出(图片由作者提供)

当存在重复值时,我们需要指定在最终输出中需要哪些特定的行。这是使用keep参数完成的,该参数可以取以下值:

  1. keep = "first":优先考虑第一个出现的**。**
  2. keep = "last":优先处理最后一次发生的**。**
  3. keep = "all":不删除任何重复项,即使这意味着选择超过 n 个项目(如上图所示)。

人们经常误认为nlargest()正好等同于使用如下的sort_values()方法:

sort_values 方法的输出(图片由作者提供)

然而,nlargest()中使用的keep参数使一切都不同了。考虑到上面的例子,带有keep=”all"nlargest()也会返回潜在的重复项。另一方面,这在sort_values()方法的情况下是做不到的。

# 2n-系列中的最小值

类似于上面讨论的nlargest()方法,您可以在 Pandas 中使用nsmallest()方法找到对应于最低 n 值的行。该方法返回列中具有最小值的前n行,按升序排列。这里传递的参数与在nlargest()方法中指定的参数相同。下面的代码片段描述了在我们的数据帧上使用nsmallest()方法。

nsmallest 方法的输出(图片由作者提供)

#3 交叉表

Crosstab 允许您计算两个(或更多)列/系列的交叉列表,并默认返回每个组合的频率。换句话说,[crosstab()](https://pandas.pydata.org/docs/reference/api/pandas.crosstab.html)接受一个列/列表,将其唯一值显示为索引,然后接受另一个列/列表,将其唯一值显示为列标题。单个单元格中的值是使用聚合函数计算的。默认情况下,它们表示同现频率。

比方说,我们希望计算每家公司内每个地点的员工人数。这可以通过以下方式完成:

计算共现频率的交叉表输出(图片由作者提供)

由于很难解释交叉表中的数值(并使其更具视觉吸引力),我们可以从如下所示的交叉表中生成一个热图:

描绘同现数据框架的热图(图片由作者提供)

如果您希望在组成索引和列标题的列之外的其他列上计算聚合,可以通过将聚合列传递给crosstab()values参数来实现,如下所示:

描述平均工资的热图(图片由作者提供)

#4 数据透视表

数据透视表是 Excel 中常用的数据分析工具。与上面讨论的交叉表类似,Pandas 中的数据透视表提供了一种交叉制表的方法。

虽然它们有许多相似之处,并且在熊猫的上下文中概念上是相同的,但是有一些实现上的差异使它们不同(此处进一步阅读)。下面的代码片段演示了如何使用[pivot_table()](https://pandas.pydata.org/docs/reference/api/pandas.pivot_table.html)方法来查找“公司名称”和“位置”之间的共现频率:

计算共现频率的数据透视表的输出(图片由作者提供)

与我们在 Crosstab 中所做的类似,我们可以创建一个热图,使其在视觉上更具吸引力,并且更容易理解。如代码片段所示,这可以生成以下热图:

描绘同现数据框架的热图(图片由作者提供)

#5 处理重复数据

除了常规的数据分析之外,适当地处理数据中的重复值在构建数据管道中也起着至关重要的作用。数据中有副本的一个主要警告是,它们会占用不必要的存储空间,并通过获取资源来降低计算速度。此外,重复数据会扭曲分析结果,导致我们得出错误的见解。因此,删除或处理数据中的重复项极其重要。

首先,让我们看看如何在数据帧中标记重复值。为此,我们将在熊猫身上使用[duplicated()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.duplicated.html)方法。这将返回一个指示重复行的布尔序列。出于演示的目的,我将只使用 10 行原始 salary 数据集的随机样本,其中最后两行是有意复制的。下图显示了采样的行。

带有副本的数据帧(图片由作者提供)

  • 标记重复的行

Pandas 允许您根据重复的所有列(或列的子集)为行分配布尔标签。这可以使用熊猫的[duplicated()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.duplicated.html)方法来完成,如下所示:

当有重复值时,keep用于指示标记哪些具体的重复值。

  1. keep = "first":(默认)将所有重复标记为True,第一个出现的除外。
  2. keep = "last":将所有重复标记为True,最后一个出现的除外。
  3. keep = False:将所有重复标记为True

您可以过滤所有只出现一次的行,方法是将布尔序列作为过滤熊猫数据帧的标志,如下所示:

无重复的过滤数据帧(图片由作者提供)

要检查列子集上的重复项,请将列列表作为duplicated()方法的subset参数传递,如下所示:

如下所示,使用上述布尔序列过滤数据帧,输出代码后的数据帧:

考虑两列的重复数据的过滤数据帧(图片由作者提供)

  • 删除重复项

除了使用上面讨论的布尔标签来标记潜在的重复之外,可能还需要去除重复。重申一下,我特别为“处理重复数据”一节引用的数据只有十行。如下所示:

带有副本的数据帧(图片由作者提供)

您可以使用如下所示的drop_duplicates()方法,根据所有列中的值或列的子集来删除重复的行:

删除重复行后的数据帧(作者图片)

duplicated()类似,keep参数用于指示您想要保留哪些特定的副本。

  1. keep = "first":(默认)删除除第一个出现的之外的所有重复。
  2. keep = "last":删除除最后一次出现的之外的所有重复。
  3. keep = False:删除所有重复项。

要根据列子集中的值删除重复项,请将列列表作为subset参数传递给drop_duplicates()方法:

删除重复行后的数据帧,考虑两列(图片由作者提供)

总之,在这篇文章中,我介绍了 Pandas 中一些流行的方法,用于表格数据的有效数据分析。虽然这篇文章将有助于您熟悉这些方法的语法,但我强烈建议您自己下载一个数据集,并在 jupyter 笔记本上进行实验。

此外,没有比参考熊猫官方文件更好的地方了这里获得熊猫各种有效方法的基础和实用知识。Pandas 的官方文档提供了一个函数接受的每个参数的详细解释,以及一个实际的例子,在我看来,这是获得初级和高级 Pandas 专业知识的一个很好的方法。

附注:我在这篇文章中只能介绍五种方法。我将很快在另一个帖子中发布下一组有效数据分析的 Pandas 方法:)。同时,如果你喜欢读这篇文章,我相信你也会喜欢下面的文章:

</20-of-pandas-functions-that-data-scientists-use-80-of-the-time-a4ff1b694707> https://medium.com/@avi_chawla/top-ai-resources-you-must-follow-if-you-are-into-ai-19f657697c41

感谢阅读。

您可能不知道的强大的 SQL 查询

原文:https://towardsdatascience.com/powerful-sql-queries-that-you-might-not-know-c9953ea4bf20

一些可以将您的 SQL 带到另一个层次的查询和技术

博伊图梅洛·菲特拉在 Unsplash 拍摄的照片

当我大学毕业时,我对 SQL 非常有信心。会有多难呢?只是选择、连接和分组。窗口函数一开始有点难以理解,但我认为这是它所能达到的最大挑战。我不知道有一个残酷的世界在等着我。SQL 的世界是巨大的,如果你没有在实践中使用它来解决现实世界的问题,你就不能 100%理解其中的一些条款和概念。

成为一名分析工程师意味着 SQL 现在已经成为我的日常语言(也许比英语用得更多?!).下面我分享一些 SQL 查询和技术,这些是我在解决了一些实际问题后学到的,我觉得它们是对你的 SQL 库的很好的补充。

注意,我主要研究雪花方言。查询也可以存在于不同的方言中,但是具有不同的语法,或者在某些方言中根本不被支持。务必事先检查文档。

晋级

假设您有一个客户下订单的表。每个客户可能有多个订单。现在您想从每个客户那里获得最新的订单。

date customer _ id order _ id price
2022-01-01 001 123 100
2022-01-02 002 432 250
2022-01-03 002 212 350
2022-01-04

一个简单的窗口函数和 CTE 可以做到这一点:

with order_order as
(
select
    date,
    customer_id,
    order_id,
    price,
    row_number() over (partition by customer_id order by date desc)    
    as order_of_orders
from customer_order_table 
)
select
     *
from order_order
where order_of_orders = 1
; Results:
| date       | customer_id | order_id | price |
|------------|-------------|----------|-------|
| 2022-01-03 | 002         | 212      | 350   |
| 2022-01-06 | 005         | 982      | 300   |
| 2022-01-07 | 001         | 109      | 120   |

尽管这样做很好,但这不是编写这个查询的最佳方式。这就是qualify派上用场的地方。

QUALIFY 子句过滤窗口函数的结果。限定具有窗口功能的 does,就像具有的对由分组的所做的那样。因此,在执行顺序中,QUALIFY 是在窗口函数之后计算的。
来源:雪花文档

select
    date,
    customer_id,
    order_id,
    price
from customer_order_table
qualify row_number() over (partition by customer_id order by date desc) = 1
;Results:
| date       | customer_id | order_id | price |
|------------|-------------|----------|-------|
| 2022-01-03 | 002         | 212      | 350   |
| 2022-01-06 | 005         | 982      | 300   |
| 2022-01-07 | 001         | 109      | 120   |

在回答像获取每个类别的最大 XXX 值这样的问题时,QUALIFY 特别有用。

阵列

当您希望将多个值作为一个数组放入一个单元格中时,Array 非常有用。我发现最有用的数组函数是array_aggarray_sizearray_contains

例如,我们有一个 order_item 表,它列出了每个订单项目的品牌、类别和销售价格。

| brand  | order_item_number | category   | sales |
| ------ | ------------------| ---------- | ----- |
| Levi's |  A123879320931    | jeans      | 30    |
| Uniqlo |  A123879320932    | t-shirts   | 120   |
| Uniqlo |  A123879320933    | shirts     | 150   |
| COS    |  A123879320934    | trousers   | 347   |
| Levi's |  A123879320935    | jacket     | 672   |
| Arket  |  A123879320936    | jacket     | 40    |
| Uniqlo |  A123879320937    | vest       | 65    |
| COS    |  A123879320938    | shirts     | 78    |
| COS    |  A123879320939    | vest       | 100   |

数组 _ 聚集

ARRAY_AGG 返回输入值并将它们旋转到一个数组中。例如,今天你想知道每个品牌在网站上都有哪些品类。你可以做一个简单的group by作为

select
    brand,
    category
from order_item
group by brand, category
order by brand, category
;Results:| brand  | category   | 
| ------ | ---------- | 
| Arket  | jacket     |
| COS    | shirts     |
| COS    | trousers   | 
| COS    | vest       |
| Levi's | jacket     |
| Levi's | jeans      |
| Uniqlo | shirts     |
| Uniqlo | t-shirts   | 
| Uniqlo | vest       |

这种方法的问题是,如果每个品牌有很多类别,行数将变得非常多,以至于很难检查每个品牌。而且也不容易看到一个品牌的所有品类。这两个问题都可以通过使用array_agg并在函数中指定 distinct 来解决:

select
   brand,
   array_agg(distinct category) as all_categories
from order_item
group by brand
order by brand
;Results:| brand  | all_categories               | 
| ------ | ---------------------------- | 
| Arket  | ['jacket']                   |
| COS    | ['shirts','trousers','vest'] |
| Levi's | ['jacket','jeans']           |
| Uniqlo | ['shirts','t-shirts','vest'] |

通过将这些值转换成一个数组,现在检查每一项就容易多了。它还为使用其他数组函数来回答带有简单子句的更复杂的问题提供了机会。

数组大小

array_size函数将一个数组或变量作为输入,并返回数组/变量中的项数(文档)。通过使用它,我们可以很容易地回答诸如“每个品牌有多少个类别”这样的问题。

select
   brand,
   array_agg(distinct category) as all_categories,
   array_size(all_categories) as no_of_cat
from order_item
group by brand
order by brand
;Results:| brand  | all_categories               | no_of_cat |
| ------ | ---------------------------  | --------- |
| Arket  | ['jacket']                   | 1         |
| COS    | ['shirts','trousers','vest'] | 3         |
| Levi's | ['jacket','jeans']           | 2         |
| Uniqlo | ['shirts','t-shirts','vest'] | 3         |

数组包含

ARRAY_CONTAINS检查一个 变量 是否包含在一个数组中,并返回一个布尔值。请注意,您需要首先将您想要检查的项目转换为变量(文档)。语法:*array_contains(variant, array)* 问:什么品牌有外套?

select
   brand,
   array_agg(distinct category) as all_categories,
   array_size(all_categories) as no_of_cat,
   array_contains('jacket'::variant,all_categories) as has_jacket
from order_item
group by brand
order by brand
;Results:| brand  | all_categories               | no_of_cat | has_jacket |
| ------ | ---------------------------  | --------- | ---------- |
| Arket  | ['jacket']                   | 1         | true       |
| COS    | ['shirts','trousers','vest'] | 3         | false      |
| Levi's | ['jacket','jeans']           | 2         | true       |
| Uniqlo | ['shirts','t-shirts','vest'] | 3         | false      |

其中 1=1

从技术上讲,这不是一个查询,而更像是一个约定或技巧。如果您没有深入研究 SQL,这可能不太直观。我花了一段时间才真正理解为什么工程师总是加一个1=1或者true作为第一个 where 子句。

假设您有一组 where 子句,您希望在不同的条件下应用它们。诸如

select
     *
from XXX_table
where 
  (if condition A) clause 1 
  (if condition B) and clause 2 
  (if condition C) and clause 3
;

在这种情况下,如果不满足条件 A,子句 1 将为空,查询中的 where 子句将变为where *and* clause 2 and clause 3,这将导致语法错误。为了防止错误发生,通常在开头添加一个1=1,因此它变成了

select
     *
from XXX_table
where 1=1
    (if condition A) and clause 1 
    (if condition B) and clause 2 
    (if condition C) and clause 3
;

因为1=1总是为真,即使所有的条件都不满足,查询仍然会工作。

另一个原因是让开发查询更加顺畅。在开发 SQL 查询时,我们经常需要更改 where 子句来测试不同的场景。在普通的 where 子句中,如

where 
    clause 1 
    and clause 2 
    and clause 3

如果我们要对第 1 条进行注释,那么我们也需要删除第 2 条中的“and”。通过在开头使用1=1true,所有的子句都将有一个‘and’,所以当注释掉任何 where 子句时,我们不需要担心其他事情。

where 1=1
   -- and clause 1
   and clause 2
   and clause 3

此外,它更美观,因为您可以将所有 where 子句排成一行。好吧,我承认这有点强迫症,但是一直在你的 where 子句开头加1=1或者true是个好习惯。

自然加入

自然联接假定不同表中同名的列是联接键。因此,您不需要指定连接键,只需select * from table_a natural join table_b。与其他联接不同,如果没有在 select 中指定列,自然联接将只包括一次公共列,即联接键。如果未指定,自然联接将作为内部联接执行,但是,它也可以与外部联接组合。你不能使用natural join table_b **on** ...,因为当你使用自然连接时,连接键已经被隐含了。
来源: 雪花加入

示例:

table_a:| id | name  |
|----|-------|
| 1  | Jenny |
| 2  | Jeff  |
| 3  | Brian |table_b:| id | age |
|----|-----|
| 1  | 30  |
| 2  | 21  |
| 4  | 50  | select
    *
from table_a
natural join table_b
;Results:
| id | name  | age |
|----|-------|-----|
| 1  | Jenny | 30  |
| 2  | Jeff  | 21  | select
    *
from table_a
natural outer join table_b
;Results:
| id | name  | age  |
|----|-------|------|
| 1  | Jenny | 30   |
| 2  | Jeff  | 21   |
| 3  | Brian | null |
| 4  | null  | 50   |

在以下情况下,您应该考虑使用自然联接:

  1. 多个表中有许多同名的公共列,它们都被用作连接键。
  2. 您不希望为了避免多次输出相同的列而在 select 中键入所有的公共列。

总流量

计算运行总数是你可以从涉众那里得到的一个非常普遍的需求。当我第一次接近这个问题时,我花了很多时间思考如何使用窗口函数和 LAG/LEAD 来完成工作。直到我了解到一个简单的加窗函数就能解决问题。让我们再次以此表为例:

| name  | date       | sales |
| ----- | -----------| ----- |
| James | 2021-01-01 | 30    |
| Nina  | 2021-01-02 | 120   |
| Mike  | 2021-01-02 | 150   |
| James | 2021-01-03 | 347   |
| James | 2021-01-05 | 672   |
| Mike  | 2021-01-03 | 40    |
| Nina  | 2021-01-05 | 65    |
| Mike  | 2021-01-08 | 78    |
| Mike  | 2021-01-09 | 100   |

要计算每个人的运行总数,按日期排序,只需将这个要求放在over子句中,并使用sum()作为聚合函数。

select
    name,
    date,
    sales,
    sum(sales) over (partition by name order by date) as running_sum
from TABLE_NAME
;Results:| name  | date       | sales | running_sum |
| ----- | -----------| ----- | ----------- |
| James | 2021-01-01 | 30    |30           |
| James | 2021-01-03 | 347   |377          |
| James | 2021-01-05 | 672   |1,049        |
| Mike  | 2021-01-02 | 150   |150          |
| Mike  | 2021-01-03 | 40    |190          |
| Mike  | 2021-01-08 | 78    |268          |
| Mike  | 2021-01-09 | 100   |368          |
| Nina  | 2021-01-02 | 120   |120          |
| Nina  | 2021-01-05 | 54    |274          |

您也可以使用相同的逻辑来计算移动平均值。

select
    name,
    date,
    sales,
    sum(sales) over (partition by name order by date) as running_sum,
    avg(sales) over (partition by name order by date) as running_avg
from TABLE_NAME
;Results:| name  | date       | sales | running_sum | running_avg |
| ----- | -----------| ----- | ----------- | ----------- |
| James | 2021-01-01 | 30    |30           | 30          |
| James | 2021-01-03 | 347   |377          | 188         |
| James | 2021-01-05 | 672   |1,049        | 350         |
| Mike  | 2021-01-02 | 150   |150          | 150         |
| Mike  | 2021-01-03 | 40    |190          | 95          |
| Mike  | 2021-01-08 | 78    |268          | 89          |
| Mike  | 2021-01-09 | 100   |368          | 92          |
| Nina  | 2021-01-02 | 120   |120          | 120         |
| Nina  | 2021-01-05 | 54    |274          | 137         |

预付款:移动平均线

获得最近 n 天的平均值,即移动平均线,是人们喜欢问的另一个流行问题。通过查看过去 n 天的平均值,而不是只查看每天的数字,我们可以更好地了解趋势,减少偏差。这是防止异常值影响结果的好方法。例如,如果在几天的低病例数后,新冠肺炎病例数有一天出现高峰,仅从高峰来看,情况可能很糟糕。但看看最近的 7 天平均线,峰值会被平均掉,数字会更好地反映当前的情况。

SQL 中的移动平均并不难。一开始这可能有点令人生畏,但是一旦你理解了它背后的概念,它就是一个非常强大的工具。我们使用rows between n preceding and n following来指定我们想要用来计算平均值的窗口。您也可以使用CURRENT ROW来确定当前行,使用UNBOUNDED来不设置边界。

来源: SQL 窗口函数备忘单

让我们用这个表来看看一些实际的代码示例。

table_daily_sales:| date       | sales |
|------------|-------|
| 2022-01-01 | 100   |
| 2022-01-02 | 300   |
| 2022-01-03 | 210   |
| 2022-01-04 | 250   |
| 2022-01-05 | 400   |
| 2022-01-06 | 1200  |
| 2022-01-07 | 220   |

如果我们想计算过去 3 天(包括今天)的移动平均值,我们希望在窗口中显示今天(当前行)、昨天和前天。

select
   date,
   sales,
   avg(sales) over (order by date 
       rows between 2 preceding and current row) as moving_avg
from table_daily_sales
;Results:
| date       | sales | moving_avg |
|------------|-------|------------|
| 2022-01-01 | 100   | 100        | =(100)/1
| 2022-01-02 | 300   | 200        | =(100+300)/2
| 2022-01-03 | 210   | 203        | =(100+300+210)/3
| 2022-01-04 | 250   | 253        | =(250+210+300)/3
| 2022-01-05 | 400   | 286        | =(400+250+210)/3
| 2022-01-06 | 1200  | 616        | =(1200+400+250)/3
| 2022-01-07 | 220   | 606        | =(220+1200+400)/3

注意,前几行会相应地改变分母,因为之前没有足够的日期。也许你认为今天还没有结束,所以我们应该只包括从 4 天前到昨天。这很简单,只需根据您想要的逻辑更改您的窗口:

order by date rows between 3 preceding and current row -1

使用UNBOUNDED允许您不设置边界:

order by date rows between UNBOUNDED preceding and current row将给出从表格的最早日期直到当前行。

SQL 还有很多东西要学,就像我说的世界是巨大的。你觉得哪个最有用?你有什么“隐藏的宝石”SQL 查询想分享吗?评论一下,让我知道!

用最先进的嵌入技术支持计算机视觉中的语义相似性搜索

原文:https://towardsdatascience.com/powering-semantic-similarity-search-in-computer-vision-with-state-of-the-art-embeddings-f6c183fff134

执行图像到图像和文本到图像相似性搜索的最简单方法

厉害的力量!由狮式战斗机·珀奇克在 Unsplash 上拍摄的照片

自从人类文明诞生以来,多达 90%的数据是在过去两年中产生的!随着社交媒体和物联网(IoT)等数字技术的激增,以及 5G 等速度更快的无线通信技术的发展,数据创建率不断提高。然而,大多数新创建的数据都是“非结构化的,如文本、图像、音频和视频来源。

非结构化数据之所以得名,是因为它没有固有的结构,不像表格那样由行和列组成。相反,非结构化数据包含几种可能格式之一的信息。例如,电子商务图像、客户评论、社交媒体帖子、监控视频、语音命令等。,是丰富的信息源,不遵循传统的表格数据格式。

人工智能(AI)和机器学习(ML)技术的最新进展创造了一种通过使用“嵌入”以可扩展的方式从非结构化数据源中提取有用信息的方法。将非结构化数据转换为嵌入数据,并将其存储在向量数据库中,如 Milvus 已经实现了几个优秀的应用程序,这在几年前是不可想象的。一些示例应用是视觉图像搜索、语义文本搜索、推荐引擎、对抗错误信息、药物发现等。!

在本帖中,我们将讨论以下内容。点击链接,跳转到以下部分:

  1. 什么是嵌入
  2. 用 Kaggle API 加载一些数据
  3. 拉平原始像素值
  4. 针对分类目标预训练的卷积神经网络:Towhee by Zilliz
  5. 用度量学习目标预训练的卷积神经网络:谷歌的 SimCLR
  6. 用度量学习目标预训练的图像-文本多模态神经网络:由 OpenAI 剪辑
  7. 结论

在这篇文章中,我们将使用 Kaggle 提供的 Digikala 产品颜色分类数据集在这里构建一个简单的基于图像的电子商务相似产品搜索服务。数据集是在 GPL 2 许可下授权的。

什么是嵌入

我们的计算机不能像人类那样直接理解图像或文本。但是,计算机擅长理解数字!因此,为了帮助我们的计算机理解图像或文本的内容,我们需要将它们转换成数字表示。例如,如果我们考虑图像用例,我们本质上是将图像的上下文和场景“编码”或“嵌入”到一系列矢量形式的数字中。

“嵌入”向量是我们图像数据的数字表示,以便我们的计算机可以理解我们图像的上下文和场景。

计算机不能直接理解图像,但是它们可以很好地理解数字!作者图片

几个 Python 库允许我们从图像中生成嵌入。一般来说,我们可以将这些库分为两大类。

  1. **提供具有预训练模型的现成 API 的库:**对于许多涉及日常物品图像的现实世界问题,我们可能不需要训练任何模型。相反,我们可以依赖全球研究人员开源的许多高质量的预训练模型。研究人员已经训练这些模型从 ImageNet 数据集中识别和聚类几个日常物体。
  2. **允许我们训练和微调我们的模型的库:**顾名思义,对于这些模型,我们可以从零开始带来我们的数据和训练模型,或者专门针对我们的用例微调预训练的模型。如果预先训练的模型没有为我们的问题领域提供良好的嵌入,我们只需要沿着这条路走下去。

让我们在这篇文章中看看其中的几个库。但是首先,让我们加载一些图像数据来定性地评估我们在相似性搜索应用程序中的嵌入。

加载一些数据

我们首先需要加载一些图像数据来测试各种嵌入策略。在这篇文章中,让我们使用来自 Kaggle 的 Digikala 产品颜色分类数据集这里。该数据集包含超过 6K 个电子商务产品的图像,非常适合测试基于电子商务图像的类似产品搜索服务。

步骤 1:设置 Kaggle 环境

  1. 在kaggle.com上创建账户
  2. 单击您的个人资料图片,然后从下拉菜单中单击“帐户”。
  3. 向下滚动到“API”部分。
  4. 单击下图所示的“创建新的 API 令牌”按钮,下载一个新的令牌,作为一个 JSON 文件,其中包含用户名和 API 密钥。
  5. 如果您使用的是 macOS 或 Linux,将 JSON 文件复制到~/.kaggle/目录。在 Windows 系统上,转到您的根目录,然后转到.kaggle文件夹,并将下载的文件复制到该文件夹。如果.kaggle目录不存在,请创建它并将 JSON 文件复制到其中。

创建一个新的 Kaggle API 令牌。图片作者。

步骤 2:从 Kaggle 下载数据

我们将使用 Anaconda 来管理这个项目的虚拟环境。你可以从这里安装 Anaconda。一旦您下载并安装了 Anaconda,我们就可以设置一个名为semantic_similarity的新环境,安装必要的库,如kagglepandas,并通过在终端窗口中运行以下命令从kaggle.com下载整个数据集。如果不想使用 Anaconda,也可以使用 python 的venv为这个项目创建和管理一个虚拟环境。

作者代码

该数据包含各种电子商务产品的 6K 多张图片。在下图中,我们可以看到数据集中的一些样本图像。正如您所注意到的,数据集包含各种时尚产品,如男装、女装、包、珠宝、手表等。

数据集中可用的示例产品。作者使用来自拥有 GPL 2 许可证的 Digikala 产品颜色分类数据集的图像合成图像。

步骤 3:将所有图像从每个文件夹移动到父文件夹

让我们在semantic_similarity/notebooks目录中创建一个新的 jupyter 笔记本来测试各种嵌入策略。首先,让我们导入必要的库。

作者代码

下载的图像位于几个子文件夹中。让我们将它们全部移动到主父目录中,这样我们就可以很容易地获得它们的所有路径。

来自堆栈溢出的代码,由作者修改

步骤 4:将图像路径加载到熊猫数据帧中

接下来,让我们将所有图像文件路径的列表加载到 pandas 数据帧中。

作者代码

数据帧中的前 5 行。作者图片

生成嵌入的策略

计算机视觉技术的最新进展开辟了许多将图像转换成数字嵌入的方法。让我们来看看其中的几个。点击链接跳转到相关部分。

  1. 拉平原始像素值
  2. 根据分类目标预先训练的卷积神经网络
  3. 用度量学习目标预训练的卷积神经网络
  4. 用度量学习目标预训练的图文多模态神经网络

拉平原始像素值

彩色图像由三维像素阵列组成。第一维是图像的高度,第二维是图像的宽度,最后第三维是颜色通道,统称为 RGB,包含红色、绿色和蓝色,如下图所示。每个像素的值是 0 到 255 之间的整数,255 是可能的最高亮度。

因此,( 0,0,0)的 RGB 值是全黑或纯黑像素,而(255,255,255)是全饱和纯白像素。我们图像中可见的所有其他颜色都是由 RGB 这三个基本值的各种组合构成的。在 RapidTables 网站上的 RGB 颜色代码表允许你选择任何颜色来查看它的 RGB 值,一定要试试!

彩色图像由 3 个通道组成,一个用于红色,一个用于绿色,一个用于蓝色。作者图片

假设图像是 3D 数组格式的一系列数字,使用reshape操作将它们转换成 1D 向量是很简单的,如下图所示。我们也可以通过将每个像素的值除以 255 来归一化 0 到 1 之间的像素。我们将在代码中这样做。

拼合会将 3D 数组转换为 1D 矢量。作者图片

作者代码

这种方法的问题

尽管这种方法易于理解并且易于实现,但是这种将图像“嵌入”到矢量中的方法有一些严重的缺点。

  1. **巨大的矢量:**我们从 Kaggle 下载的图像尺寸非常小[224 x 224 x 3],对应于[高 x 宽 x 通道],将这个 3D 数组转换为 1D 矢量的结果是一个大小为 150,528 的矢量!对于如此小的图像来说,这是一个巨大的矢量!在我们整个数据集上生成这个向量时,我的计算机崩溃了好几次。我最后只在一个较小的子集(1K 行)上运行它来说明这种方法。
  2. **带有大量空白(白色)空间的稀疏向量:**视觉检查我们时尚数据集中的图像,我们注意到图片中有大片白色区域。因此,这个 150,528 元素向量的许多元素只是值 255(对于白色),并没有添加任何与图像中的对象相关的信息。换句话说,这种“嵌入”方案不能有效地编码图像的对象,而是包括许多无用的空白。
  3. **缺乏局部结构:**最后,直接展平一幅图像,失去了画面的所有局部结构。例如,我们通过眼睛、耳朵、鼻子和嘴巴的相对位置来识别人脸的图像。这些是各种“特征”级别的信息,如果我们一次只看一行像素,我们会完全忽略这些信息。这种损失的影响是颠倒的脸将具有与正面朝上的脸非常不同的平坦嵌入,即使两者都是同一张人脸的照片!

随着基于卷积神经网络 CNN 和变换器架构的新型神经网络架构的出现,我们已经基本克服了这些问题。这篇文章的其余部分深入探讨了使用这些神经网络将我们的图像转换成嵌入图像的方法。

基于分类目标预训练的卷积神经网络

也许最著名的计算机视觉任务之一是将图像分类成不同的类别。通常,对于这项任务,我们将使用 CNN 模型,如 ResNet 作为编码器,将图像转换为向量,然后将该向量通过多层感知器(MLP)模型来确定图像的类别,如下图所示。研究人员将使用交叉熵损失来训练这种 CNN + MLP 模型,以准确地对图像类别进行分类。

图像分类管道。CNN 将图像转换为嵌入向量。MLP 用这个向量来预测图片的类别。图片作者。

这种方法提供了最先进的精确度,甚至超过了大多数人的能力。在训练了这样的模型之后,我们可以省去 MLP 层,直接将 CNN 编码器的输出作为每个输入图像的嵌入向量。

在现实中,我们不需要为许多现实世界的问题从头训练我们自己的 CNN 模型。相反,我们直接下载并使用已经训练好的模型来识别日常对象,如 ImageNet 数据集中的那些类别。Towhee是一个 python 库,使用这些预先训练好的模型快速生成嵌入。让我们来看看如何做到这一点。

Towhee 管道

Towhee 是一个 python 库,提供了极其易用的嵌入生成管道。我们可以使用 towhee 将一个图像转换成一个嵌入,只需要不到五行的代码!首先,让我们在终端窗口中使用pip安装towhee

作者代码

接下来,在 Jupyter 笔记本单元格中,让我们导入库并实例化一个管道对象。

作者代码

接下来,让我们在一行代码中使用管道将图像转换为嵌入内容!嵌入管道的输出有几个额外的维度,我们可以用np.squeeze去掉。

作者代码

数据帧的前 5 行用于嵌入创建。作者图片

在我们继续之前,让我们创建一个助手函数,它接受嵌入列的名称、用作查询图像的数据框的索引以及要搜索的相似图像的k数量。该函数计算查询图像的嵌入和数据帧中所有其他图像的嵌入之间的余弦相似度,以找到顶部k最相似的图像并显示它们。

作者代码

我们现在可以通过从数据帧中查询随机图像,并使用上面的辅助函数显示k最相似的图像,来测试 towhee 的嵌入质量。如下所示,对于我们运行的每个查询,towhee 嵌入都非常准确,以便从包含几个不同产品(如服装、手表、包和配饰)的整个图像集中找到相似的图片!考虑到我们只用三行代码就生成了这些嵌入,这就更令人印象深刻了!

作者代码

随机查询图片和来自 towhee 嵌入的前 5 个最相似的图片。作者使用具有 GPL 2 许可证的 Digikala 产品颜色分类数据集中的图像制作图片。

从结果中,我们可以得出结论,towhee 是快速生成相似性搜索应用程序的嵌入的一个很好的起点。然而,我们没有明确地训练这些模型来确保相似的图像具有彼此相同的嵌入。因此,在相似性搜索的上下文中,来自这种模型的嵌入可能不是对所有用例都是最准确的。你现在可能会问的自然问题是,*“有没有一种方法可以训练模型,使得相似的图像具有彼此相似的嵌入?”*谢天谢地,有!

用度量学习目标预训练的卷积神经网络

进入度量学习,这是生成嵌入的最有前途的方法之一,尤其是对于相似性搜索应用。在最基本的层面上,在度量学习中,

  1. 我们使用诸如 CNN 或 Transformer 网络之类的神经网络来将图像转换成嵌入。
  2. 我们构建这些嵌入,使得语义相似的图像彼此聚集得更近,而不相似的图像则相距更远。

向量空间中的手写数字图像示例。图片来源:媒体博文作者布莱恩·威廉斯。经允许重新发布。

训练度量学习模型需要数据处理方式和模型训练方式的创新。

  1. **数据:**在度量学习中,对于每个称为“锚”图像的源图像,我们需要至少一个称为“正”的相似图像我们也可以合并一个不同的第三个图像,称为“负”,以改善嵌入表示。在最近针对每个源图像的度量学习方法中,我们使用各种数据扩充综合生成一个“锚”和一个“正面”图像,如下图所示。由于两个图像都是同一源图像的变体,因此将它们标记为锚定阳性对是合乎逻辑的。另一方面,我们通过拍摄同一批图像中除“锚”以外的所有图像,合成生成“底片”。
  2. **模型:**度量学习模型大多有一个连体网络架构。锚定图像、正图像和负图像依次通过相同的模型来生成嵌入,然后我们使用特殊的损失函数来比较这些嵌入。这样的损失函数之一被称为对比损失,其中模型的目标是将锚图像和正图像的嵌入移动得更近,使得它们之间的距离接近 0。相反,该模型旨在将锚和负嵌入彼此远离,使得它们之间的距离很大。

锚点和正片都是同一源图像的增强版本。图片由作者使用具有 GPL 2 许可证的 Digikala 产品颜色分类数据集中的图片制作。受谷歌人工智能博客的启发

在用这种方法训练模型之后,我们可以通过使用诸如余弦距离之类的度量以数学方式计算它们的嵌入向量之间的距离来找到任意两个图像之间的相似性。如这篇中型博客文章所示,存在几种距离度量,余弦距离常用于比较嵌入。

SimCLR:简单对比学习

SimCLR 代表视觉表征对比学习的简单框架。这是使用一种称为对比学习的度量学习来生成图像嵌入的流行方法之一。在对比学习中,对比损失函数比较两个嵌入是相似的(0)还是不相似的(1)。

SimCLR 的伟大之处在于它是一个简单的自我监督算法(我们不需要图像类的任何标签!)实现了与一些监督方法相当的性能,如下图所示!

自监督 SimCLR 4X 实现了与完全监督 ResNet50 相似的精度!图片来源:arxiv.org 的 SimCLR 论文

可能的图像数据增强的例子。图片来源:arxiv.org 的 SimCLR 论文

SimCLR 的基本思想如下。

  1. 给定一幅图像,创建同一幅图像的两个增强版本。这些增强可以是裁剪和调整大小、颜色失真、旋转、添加噪声等。上图展示了一些增强的例子。
  2. 一批中所有图像的增强版本通过 CNN 编码器,将图像转换为嵌入内容。这些 CNN 嵌入然后通过一个简单的只有一个隐藏层的多层感知器(MLP)将它们转换到另一个空间。
  3. 最后,使用余弦距离对 MLP 输出端的嵌入进行相互比较。该模型期望来自同一图像的增强的余弦距离为 0,而来自不同图像的增强的余弦距离为 1。损失函数然后更新 CNN 和 MLP 的参数,使得嵌入更接近我们的期望。
  4. 一旦训练完成,我们不再需要 MLP,直接使用 CNN 编码器的输出作为我们的嵌入。

下图从概念上解释了整个过程。更多细节,请看这篇谷歌博客文章。

SimCLR 概念性解释。图片由作者使用来自 Digikala 产品颜色分类数据集的图片,拥有 GPL 2 许可证。受谷歌人工智能博客的启发

作者进行了几次实验,并确定随机裁剪和颜色失真是增强的最佳组合,如下所示。

不同数据集扩充的下游模型精度。图片来源:arxiv.org 的 SimCLR 论文

像 towhee 一样,我们使用其他研究人员在 ImageNet 上预先训练的模型来直接提取 SimCLR 嵌入。然而,在撰写本文时,为了获得 SimCLR 预训练嵌入,我们需要使用 Pytorch Lightning Bolts 库编写几行代码。我改编了官方闪电文件中的以下内容。首先,在终端窗口中使用pip安装必要的库。

作者代码

接下来,在 Jupyter 笔记本单元中,让我们导入必要的库,并根据您的计算机是否有 GPU 将设备设置为cudacpu

作者代码

接下来,让我们加载预先在 ImageNet 上训练的 SimCLR 模型,并将其设置为 eval 模式,因为我们只想从模型中获得嵌入,而不想再训练它。

作者代码

以下两个步骤是 Pytorch 特有的;用于实现模型的基础库。我们首先创建一个数据集,它可以将我们的 dataframe 作为输入,从img_path列中读取图像,应用一些转换,最后创建我们可以输入到模型中的批量图像。

作者代码

最后,我们可以迭代 dataloader 中的批处理,为所有图像生成嵌入,并将它们作为一列存储回我们的 dataframe。

作者代码

SimCLR 嵌入生成后数据帧的前 5 行。作者图片

现在有趣的部分来了!我们可以用相同的帮助函数测试 SimCLR 嵌入的质量。我们从数据框中查询随机图像并显示k最相似的图像。如下所示,SimCLR 嵌入也能准确地为我们运行的每个查询找到相似的图像!

作者代码

随机查询图像和来自 SimCLR 嵌入的前 5 个最相似的图像。作者使用具有 GPL 2 许可证的 Digikala 产品颜色分类数据集中的图像制作图片。

用度量学习目标预训练的图文多模态神经网络

最后,将图像和文本嵌入到统一嵌入空间的模型有了巨大的改进[ Open AI 的回形针, Google 的 ALIGN Paper ,微软的 PixelBERT Paper ],这开启了图像到文本和文本到图像相似性搜索的几个优秀应用。这种范式中最流行的模型之一是 CLIP(对比语言-图像预训练)。

CLIP 是建立在度量学习框架上的神经网络。CLIP 使用图像作为锚点,相应的文本描述作为正面来构建图像-文本对,而不是在纯粹的图像锚点-正面对上进行训练。我们可以在多种应用中使用 CLIP,包括文本到图像、图像到文本、图像到图像和文本到文本的相似性搜索。

图像通过 ResNet 或 ViT 编码器生成图像嵌入。文本描述通过基于转换器的编码器来生成文本嵌入。CLIP 联合训练图像和文本编码器,以便在一批 N 个图像-文本对中,嵌入第图像与嵌入第个文本的点积最高,如下图所示。

CLIP 的对比预训练。图片来源:回形针arxiv.org

训练完成后,我们可以找到与查询图像最相似的文本行,只需将两者转换为各自的嵌入,然后使用点积或余弦距离进行比较,如下图所示。相反,我们也可以用同样的方式搜索给定查询文本的最相似的图像。让我们看看如何在我们的示例问题上实现这一点。

预测期间剪辑。图片来源:回形针arxiv.org

句子变形金刚

使用优秀的句子-变形库,在我们的数据上生成剪辑嵌入非常简单。然而,由于操作系统对我们一次可以打开的文件数量的限制,当处理成千上万的图像时,我们需要编写几行样板代码。首先,在终端窗口中使用pip安装必要的库。

作者代码

接下来,在 Jupyter 笔记本单元格中,让我们导入库并实例化一个剪辑模型。

作者代码

接下来,我们需要迭代 10K 映像,以绕过操作系统对一次可以打开的文件数量的限制。我们在每次迭代中加载所有图像,并生成剪辑嵌入。

作者代码

数据帧后剪辑图像嵌入生成的前 5 行。作者图片

现在我们有了所有图像的剪辑嵌入,我们可以使用相同的辅助函数来测试嵌入的质量。我们从数据帧中查询随机图像,并显示k个最相似的图像。如下所示,剪辑嵌入也非常准确,可以为我们运行的每个查询找到相似的图像!

作者代码

随机查询图像和来自剪辑图像嵌入的前 5 个最相似的图像。图片由作者使用具有 GPL 2 许可证的 Digikala 产品颜色分类数据集中的图片制作。

虽然我们不得不编写一些额外的代码来生成剪辑嵌入,但它提供的一个显著优势是文本到图像的搜索。换句话说,我们可以搜索匹配给定文本描述的所有图像。下面我们来看看这个。

由于我们已经将图像转换为剪辑嵌入,现在我们只需要将文本查询转换为剪辑嵌入。然后,我们可以通过使用文本嵌入和数据帧中所有图像嵌入之间的余弦相似性来搜索相似产品。我们将编写一个简单的助手函数来为我们完成所有这些工作,如下所示。最后,我们将绘制所有相似的k产品图片。

作者代码

现在,我们可以使用助手函数来测试示例文本查询。如下图所示,如果我们的测试查询是“一件女装的照片”,那么最相似的产品都是女装!即使每个产品的标题没有明确指定单词“连衣裙”,剪辑模型也能够仅从文本和图像嵌入中推断出这些图像与查询“一件女装的照片”最相关。继续用其他查询进行尝试吧!

作者代码

随机查询图像和来自剪辑文本嵌入的前 5 个最相似的图像。图片由作者使用具有 GPL 2 许可证的 Digikala 产品颜色分类数据集中的图片制作。

结论

深度学习研究和开源代码库的当前技术水平已经开辟了许多从图像和文本数据生成高质量嵌入的简单方法。这些现成的嵌入是为许多现实世界的问题构建原型的极好起点!下面的流程图可以帮助选择要使用的初始嵌入。然而,在将任何一个单一的查询部署到生产环境之前,要不断地评估嵌入模型在一些复杂的样本查询上的准确性!

谈到生产,我们在这里使用的数据集是一个只有 6K 图像的玩具数据集。在现实世界的应用程序中,例如电子商务商店,您需要在几分之一秒内嵌入、存储和执行最近邻搜索数亿张产品图像!这个问题的规模需要使用强大的矢量搜索数据库,如 Milvus !你可以在这里了解更多关于矢量数据库的信息。

请访问 Zilliz 的“学习”频道以获得更多关于嵌入和向量相似性数据库的各种酷应用的灵感!

选择嵌入方法。作者图片

原载于 2022 年 1 月 24 日 https://zilliz.com**

实用数据科学:推广有效性和推广计划—第一部分

原文:https://towardsdatascience.com/practical-data-science-promotion-effectiveness-and-promotion-planning-part-1-52f6707a59b9

6 步端到端流程,构建促销效果衡量和规划解决方案

业务问题|关键概念|我们为什么要关注?

卢卡斯·布拉塞克在 Unsplash 上拍摄的照片

对于机器学习应用来说,找到端到端整体视图的真实世界用例并不容易。许多学术项目提供机器学习算法和建模技术方面的大量培训。然而,预测建模之外的实际方面,如某个 ML 用例需要什么原始数据,创建建模数据集需要什么功能工程,以及开发预测建模后需要什么软件应用程序以确保使用和最终业务用户采用,往往是特定于用例的。因此,在典型的数据科学和机器学习课程中,它们没有被广泛教授。

在关于“实用数据科学”的一系列文章中,我将采用用例方法,并描述预测建模之外的端到端图景。为了实现切实的业务影响,了解业务问题(价值来源)、数据需求和潜在数据源、数据管道、功能工程、预测模型构建后的说明性分析、面向最终用户的软件解决方案的设计以及如何最好地传达结果非常重要。我将从“促销效果和促销计划”开始,作为第一个用例。

在本文中,我将讨论:

(1)业务问题:推广效果和推广策划

(2)关键概念和背景信息

(3)为什么要关心?

商业问题:促销效果和促销计划

促销被认为是市场营销中 4p(产品、定价、促销和布局)的关键支柱之一。零售商和制造商都对衡量促销活动的 ROI 和规划未来的促销活动非常感兴趣,以便最好地利用他们的促销资金。促销有效性和促销计划中的关键业务问题可以分为(1)事后分析(即回顾过去):衡量和了解过去促销对业务的影响,以及(2)前瞻性计划:促销哪种产品,何时促销,折扣多少,使用什么促销策略,以及在哪里促销。图 1 总结了这些问题。

图一。图片由黄敏哈提供

(1) 推广效果:回顾(事后分析)

推广 ROI :第一个关键的业务问题是关于以往推广活动的绩效衡量。促销需要投资(例如,价格折扣带来的收入损失、打印优惠券的成本和设置过道尽头的展示)。因此,商家自然会考虑他们能从促销中获得什么好处。有了促销,就可以有“增量”单位销售(在没有促销的情况下,超出预期单位销售水平的额外单位销售)。这在概念上听起来很简单,但“增量”单位销售并不能从数据中直接观察到。对于特定产品、商店和日期的促销活动,我们可以看到促销的实际单位销售额。然而,没有观察到相同产品/商店/日期的反事实结果基准单位销售(即无促销单位销售)。这种缺失数据问题是对具有观察数据的因果推断问题的典型挑战。如果我们能以某种方式衡量“增量”单位销售额(我将在后面描述“如何”),就可以计算过去特定促销活动的增量收入和利润。一旦我们有了“增量”单位销售额,计算“增量”收入或“增量”利润就相当简单了。通过比较增量利润(即回报)和所需的促销成本(即投资),我们可以评估促销活动的投资回报率。

促销 DNA :一旦从过去的促销活动中测量增量单位销售和 ROI 成为可能,思考为什么某些促销活动做得很好以及为什么其他促销活动没有带来好的 ROI 是非常自然的。驱动因素分析(又称 DNA)将增量单位销售和利润分解为每个驱动因素的贡献,有助于回答为什么某些类型的促销活动比其他活动更有利可图的主要原因。关键驱动因素可以包括以下类型。

  • 外部因素:季节性、节假日、趋势、天气、关键外部事件、商店位置、商店人口统计特征、商店周围的竞争强度
  • 自己的促销:促销类型(例如,特色、展示、优惠券)、促销策略(例如,买一送一、简单价格折扣、必须买 2)、降价程度(例如,5%折扣、15%折扣)、产品组合中同时促销的其他产品
  • 竞争对手因素:竞争对手的促销活动
  • 成本因素:原材料成本、促销执行成本、供应商资金(针对零售商)

模式识别:在积累了增量单位销售、增量收入、增量利润和过去促销活动的投资回报率以及 2-3 年的潜在驱动因素数据后,也有可能确定成功和失败促销活动的共同模式。从这种分析中得出的见解对未来的促销计划是有用的。

(2) 推广策划:前瞻性

反事实模拟(即“假设”分析/情景分析):对过去促销活动的绩效评估是一个很好的起点。一旦组织接受了度量方法,并理解了度量的价值和洞察力,他们就想将底层的建模方法应用于未来的推广计划。与已知所有促销驱动因素的以往测量相比,规划增加了对关键驱动因素进行假设的额外复杂性(例如,我们应该对竞争对手的价格和促销进行假设)。将有多个“假设”场景需要测试,这就需要构建一个“促销模拟器”这可以是简单的交互式应用程序(使用 Shiny/R 或 Dash/Python 构建)或企业级软件,具体取决于所需的规模和复杂程度。促销模拟器为给定的促销方案产生预期的增量单位销售、收入、利润和 ROI。具有直观用户界面的促销模拟器也有助于通过模拟引擎将建模结果部署给不太懂技术的最终业务用户。

促销优化:更成熟的零售商和制造商希望超越基于促销模拟器的场景分析。在这种情况下,可以应用规定的数据科学技术(例如优化)来找出特定产品在给定时间、给定地点对整个产品组合的最佳促销。基于优化结果,可以推荐最佳的 N 个促销候选,用于促销计划者的最终审查。

促销日历:为了管理不同产品和促销活动随时间的相互依赖性,可以使用包含这些相互依赖性的更复杂的优化技术来支持多种产品随时间发展的促销日历。这也处理购买加速或库存堆积现象。

为了回答关于促销有效性和计划的关键业务问题,我们需要建立的主干是因果计量经济学或机器学习需求模型,该模型将促销活动特征映射到单位销售(即,量),以及其他控制变量。我将在下一节描述因果机器学习需求模型背后的关键概念。

关键概念和背景信息

(1)关键概念和直觉:反事实基线的缺失数据问题

如果你试着衡量促销的投资回报率,你很快就会意识到,除非你进行精心设计的实地实验,否则这是不容易做到的。核心问题是我们没有关于反事实结果的数据(没有促销的基线单位销售),尽管我们确实观察到了特定产品、商店和日期的实际单位销售和促销。因此,我们必须建立一个因果需求模型,将促销作为关键驱动因素之一。此需求模型可用于模拟反事实的基准单位销售,因为您可以关闭给定产品、商店和周的促销活动,并模拟基准单位销售。

图二。图片由黄敏哈提供

为了使这个想法更具体,让我们考虑一个商店中单一产品的简单玩具例子。图 2 显示了该产品的单位销售额随时间的变化。通过观察时间序列图,您可以合理地猜测基线单位销售额大约为 100。通过第 6 周的促销活动,单位销售额增加到 140。因此,“增量”单位销售额在第 6 周(即促销周)可能是 140–100 = 40。这叫做总升力。然而,随着时间的推移,替代品和其他产品会带来更多的复杂性。消费者可能会在促销活动前后推迟购买或加快购买,如图 2 中第 5 周和第 7 周的下降所示。净升力(修正购买加速度或减速度后)可以更小:(140–100)—(100–90)-(100–95)= 25(而不是 40)。对于产品组合中的多种产品,您还需要进行调整,因为其他产品的单位销售可能会随着某个重点产品的促销而减少(即同类产品的相互竞争)。

根据量的来源,零售商和制造商对某个促销活动有不同的偏好。如果增量单位销售主要来自非买家(即品类扩张),零售商和制造商都会很高兴。如果增量单位销售主要来自自己产品组合中的其他产品,制造商和零售商都会不高兴。如果大部分增量单位销售来自其他制造业竞争者(即竞争性吸引),制造商会很高兴,但零售商可能不会高兴。复杂的因果需求模型也允许用户进行来源量分析。

图 3。图片由黄敏哈提供

图 3 显示了酸奶在特定促销数据下的实际市场份额数据。红线显示一段时间内的价格,蓝线显示一段时间内该产品的市场份额。您可以清楚地看到功能、展示、降价和优惠券方面的促销活动(即销售激增)。销售也会减少,这可能是由其他产品的影响或购买加速或减速造成的。最后,可能存在趋势和季节性,如果我们绘制 2-3 年的数据,这更容易识别。

(2)背景信息:不同的推广类型

促销可以包括价格折扣和非价格活动。图 4 显示了超市等杂货零售商最典型的促销类型。价格折扣通常用销售标志来表示。它可以是对每个人都一样的销售价格(10%的折扣),有购买要求的销售价格(买 10 个可以享受 10 美元的折扣),有针对性的价格折扣(只为某些类型的消费者发放折扣券)。)至于非价格促销活动,可以设置过道末端展示,以吸引消费者的注意力。特写传单可以和报纸一起分发。为了鼓励试用新产品,可以提供免费样品。

图 4。图片由黄敏哈提供

我们为什么要关心?

我们应该关注数据科学的促销效果和计划,主要有三个原因。

(1)对于消费品公司和零售商来说,贸易促进是一项大开支项目。全球的 CPG 公司将收入的 20%投资于促销活动(零售商也是一样)。)然而,59%的促销活动赔钱(美国为 72%)。同类最佳 CPG 促销的回报是最低效促销的 5 倍。

(2)数据和机器学习现在已经足够成熟,可以产生可扩展的影响。由于缺乏专家、定制模型和计算资源限制,这曾经是一个利基领域。随着计量经济学或因果机器学习模型的开源包(例如,用于线性混合效应建模的 PyStan 或 lme4,可解释的增强机器)与云计算基础设施一起,该方法现在更容易使用。事实上,大型数据科学咨询公司正在将此作为关键服务线,并制造 SaaS 产品。

(3)存在巨大影响的可能性。如果以前没有用数据测量价格弹性,价格弹性模型可以通过调整常规价格水平使收入或利润增加 12%。通过优化促销,促销效果测量和促销计划可以使收入或利润额外增加 12%。请注意,相同的基本因果需求建模既可以用于促销用例,也可以用于定价用例。

在接下来的文章中,我将描述(1)一个 6 步端到端的过程,以建立推广效果的衡量和规划解决方案,以及(2)如何建立一个数据基础的推广建模(原始数据的要求和如何准备建模数据集。)

参考:分析如何推动消费品贸易促进的增长,《麦肯锡季刊》,2019 年 10 月(黄敏哈、瑞恩·墨菲和阿卜杜勒·瓦哈布·谢赫)

实用数据科学:推广有效性和推广计划—第二部分

原文:https://towardsdatascience.com/practical-data-science-promotion-effectiveness-and-promotion-planning-part-2-f54e9454a151

6 步流程|步骤 1 —构建数据基础

照片由娜塔莉亚·罗莎在 Unsplash 上拍摄

欢迎回到推广效果衡量和推广策划系列文章!在我的上一篇文章中,我谈到了业务问题、关键概念以及这个用例的重要性。今天,我将描述(1)一个 6 步流程来构建一个推广效果衡量和规划解决方案,以及(2)整个流程的步骤 1:构建数据基础

推广策划方案构建:6 个步骤

图 1:图片由黄敏哈拍摄

促销计划解决方案构建的 6 个步骤如下:

(1)建立数据基础

(2)准备建模数据集

(3)因果需求模型:因果需求模型的主要输出是价格弹性、促销策略提升和非价格促销提升

(4)综合建模见解

(5)建立促销模拟器

(6)推广(再)规划

图 1 总结了每个步骤的关键活动。我将在这一系列文章中描述每个步骤的细节。在本文(第 2 部分)中,我将关注“构建数据基础”

步骤 1:建立数据基础

这一步是基础,也是导致大多数错误和问题的原因。最近,数据基础的重要性正在得到更多的认可和强调,如 Andrew 的“以数据为中心的 AI ”所示。Ng。就所需的工作而言,这一步需要 1 到 2 名全职数据工程师花费 2 到 6 个月的时间,他们由更高级的数据架构师(兼职)和从事因果需求建模的数据科学家指导。请注意,您必须计划足够的缓冲区,尤其是在过去没有完成这一步的情况下。在大多数情况下,可能还没有建立具有促销效果建模所需的所有数据的中央数据湖,因此负责的数据工程师和数据科学家需要联系多个内部部门的多个数据所有者以及外部联合数据提供商和数据供应商(如果可用)。

数据基础往往是特定于用例的。因此,这通常被认为是将从在职培训中获得的业务领域知识。因此,除了技术 SQL 培训之外,这是学术课程中讲授得最少的。更糟糕的是,许多公司没有为许多内部数据库维护高质量的数据模式和数据字典。我注意到,许多初级数据科学家和数据工程师在这一步上最困难,因为很少提供明确的指导方针和实用技巧。

原始数据往往是针对公司和具体情况的。因此,没有一成不变的解决方案可以应用。也就是说,一旦您清楚地了解了哪些数据是必不可少的,哪些数据是值得拥有的,您就可以避免被大量的数据库、表和列所淹没。我将介绍两个最常见的案例:(1)零售商(例如,超市如 Albertsons、Safeway、Kroger,百货公司如 Macy's,药店如 CVS 和 Walgreens,便利店如 7 Eleven),以及(2)制造商(例如,Kellogg 和 Proctor & Gamble)。就制造商而言,我将更多地关注 IRI 和尼尔森等联合数据提供商可用的行业领域:即经常购买的包装消费品(即 CPG 公司。)请注意,中央数据湖中的数据基础通常在比最终建模粒度级别更低的聚集级别(例如,商店而不是促销区)维护。

建立数据基础:零售商案例

(1)中央促销效果数据库

图 2:图片由黄敏哈拍摄

图 2 显示了中央数据存储库的 ERD(实体关系图),您需要构建它来获得关于促销有效性的 360 度视图。构建这个中央数据基础被认为是一个巨大的胜利,因为所需的数据集通常分散在多个部门和 IT 系统中。在详细讨论每个数据域之前,让我先提供一些提示。

1–1。主键:对于一个有效的中央数据存储库,理解什么是主键以及哪些主键将用于跨表连接数据是很重要的。在这种情况下,有 4 个主键:UPC 键(即,基于 UPC 代码的产品键)、商店 ID(即,每个商店的唯一键)、日或周键(即,每周或每天的唯一编号)和活动优惠 ID(即,每个促销活动的唯一 ID)。考虑这个问题的一种方法是想象一个三维数据立方体(product | store | time ),用附加的键将促销活动和相关信息附加到这个三维数据立方体上。请注意,在图 2 所示的 ERD 中,我用蓝色突出显示了主键。

1–2聚合级别和聚合偏差:对于一个中央数据仓库,建议保持数据聚合级别尽可能低。始终可以在后期进行聚合(例如,用于建模数据集的特征工程)。但是,如果您已经将数据汇总到过高的级别,则以后不可能再进行分解。此外,数据集的不恰当聚集会导致因果效应估计的偏差。聚合偏差的一个众所周知的例子是,当您聚合具有不同促销活动的商店的数据时。另一个不太为人知的例子是“分割周”偏差,当您应用一个通用的周定义来将每日数据聚合到周中时。因为促销日历在当地市场可能有不同的促销周定义(例如,周六周日与周四 Fri),这将导致非促销周和促销周的数据混合在某些市场的通用周定义中。如果一周通常被定义为整个数据的周日至周六,并且有一些当地市场遵循 Fri 至周四的促销日历,那么这种混合将发生在当地市场中遵循 Fri 至周四促销日历的所有商店。有点令人惊讶的是,这种情况在实践中经常发生(甚至对于来自联合数据提供者的数据也是如此。)我将在稍后的建模阶段给出更多的细节。****

1–3数据持续时间:至少需要 2-3 年的数据才能正确反映季节性和趋势。Covid 在 2020 年和 2021 年的锁定扭曲了正常的季节性和趋势,因此需要格外小心处理这些时期的数据。

1–4已执行与计划:很多时候可以有内部推广计划数据库,里面有推广计划。人们可能会尝试使用这个数据库作为“因果”表的来源。然而,在最后一刻改变计划是很常见的。因此,执行的促销价格和策略可能与最初的计划大相径庭。因此,强烈建议使用“执行的”促销价格和策略,而不是“计划的”促销价格和策略。

1–5多价位促销:除了简单的单一价位促销(如九折),还有复杂的多价位促销,有一定的要求(通常以多次购买要求的形式获得数量折扣)。)对于具有购买要求的复杂促销,消费者可以选择(1)购买少量并支付常规价格,或者(2)购买大量并支付促销价格。在图 3 所示的例子中,如果消费者决定购买多于(或等于)4 个,他们只需要为每个支付 3.00 美元。然而,如果消费者决定购买少于 4 个,他们要么需要支付每个 4.99 美元的常规价格(如果他们没有忠诚卡),要么支付每个 3.49 美元的备用促销价格(如果他们有忠诚卡)。)您可以看到,在这个示例中有 3 个可能的价格点。该产品给定一周在商店级别汇总的净价将是这 3 个价格点的加权平均值。

图 3:图片由黄敏哈拍摄

1–6篮子范围或品类级别促销:存在篮子范围或品类范围的促销活动,这使得很难计算每个商品级别的折扣金额。在这些情况下,您可能需要决定某些促销折扣分配规则。例如,Safeway 可以在葡萄酒类别中开展“Mix & Match”促销活动,如果您购买 6 瓶以上,可以获得额外的 10%折扣。在这种情况下,除了产品级别的促销折扣,如果消费者决定购买 6 瓶以上,还可以享受 10%的额外折扣。

1–7个性化推广:过去的优惠券通常是通过发放到消费者的邮箱里,平均分配给所有的消费者。然而,更成熟的零售商会采用个性化和更有针对性的优惠券促销。这种促销的主要特点是只有一部分消费者享受促销折扣。没有收到个性化优惠券的其他消费者仍然在个性化优惠券促销之前支付常规价格或特定于产品的折扣价。消费者中多种有效价格的存在使得我们在这些情况下决定应该使用什么样的有效促销价格(在商店或促销区等汇总级别)来进行促销有效性建模变得更加复杂。

(2)关键工具:源到目标映射

从事这一步工作的数据工程师和数据科学家必须经过多个 IT 系统,并与多个利益相关者进行讨论。很容易忘记所有相关的事实、讨论、细微差别和注意事项。一个名为“源到目标映射”的工具可以极大地帮助跟踪关键信息,并在这个混乱的阶段带来整体进展的透明度。这是可以做到的,甚至作为一个 excel 文件,有多个关键数据类型的标签。如果想更系统化,可以做网络版,方便分享和更新。图 4 显示了您在这个工具中需要的关键列,以 revenue 作为样本数据列。

图 4:图片由黄敏哈拍摄

该工具使数据工程师和数据科学家更容易理解源数据系统/表/数据列如何映射到目标数据系统/表/数据列。此外,指定转换规则可以清楚地说明如何使用源数据列来计算目标数据列。您可以输入(1)数学公式或(2) SQL 代码。此外,该工具有助于澄清(1)主键,(2)目标列是否允许 null 作为数据,(3)数据类型,以及(4)数据湖系统。通常,源系统是遗留的内部 IT 系统(甚至是来自某些部门的多个 excel 文件。)相比之下,目标系统最有可能是由云服务提供商提供支持的数据湖。

(3)关键数据域详情

在这一节中,我将详细讨论图 2 中所示的关键数据域。

3 比 1。产品主数据:产品主数据表包含以 UPC 密钥为主密钥的产品详细信息。您可以将产品属性(例如,制造商、品牌、包装尺寸、口味)与产品层次结构(类别、子类别、部门、通道)结合在一起。)几乎总是会有促销组或定价组,其中一组产品将共享相同的定价或促销折扣。捕捉这些映射是很好。体积当量需要额外解释。例如,可乐类别可以有 2 种不同罐数的产品:12 包/ 12 液体盎司罐与 24 包/ 12 液体盎司罐。将每个产品计为一个单位是不可比的,因此我们需要“体积当量”来将不同包装尺寸的产品单位转换为可比单位。如果我们将“12 液盎司”定义为 1 体积当量,则 1 单位 12 包/ 12 液盎司罐表示为 12 体积当量。类似地,1 单位 24 包/ 12 流体盎司罐表示为 24 体积当量。我们不使用原始单位,而是使用“体积当量”来衡量体积(或单位销售额)。

3–2。商店主数据:商店主数据表提供了商店的详细信息。该表的主键是“商店 ID”。除了连锁横幅(例如,Safeway、Vons)和(本地)市场,您还可以包括(1)商店特征(例如,商店规模),(2)商店人口统计数据(例如,平均家庭收入、平均年龄),以及(3)零售竞争强度(例如,竞争超市的数量)。)区域定价和区域促销相当普遍。因此,包含此映射信息是有帮助的。

3–3。销售点(POS) :这是最关键的表格,包含实际单位销售数据以及零售价格和折扣。主键是(1)商店 ID,(2)日/周键,(3) UPC 键,(4)活动优惠 ID。活动优惠 ID 提供了从“因果”表到促销活动/策略详细信息的映射。该表通常由 POS 交易数据库制成。

3–4。因果表:因果表提供了 POS 交易数据中没有的价格和非价格(如功能、展示)促销的详细信息。主键是(1)商店 ID,(2)日/周键,(3) UPC 键,(4)活动优惠 ID。例如,交易数据库提供任何折扣前的(总)收入和总折扣金额。然而,仅基于交易数据库,很难知道购买要求(例如,4 个才有资格参加“必须购买 4 个”促销)、促销策略(例如,买 1 送 1,必须购买 2 个)、促销价格(每个 2.49 美元)、过道末端展示或特色广告的存在。

3–5。财务:财务表包含产品的财务数据,这些数据会随着时间而变化。主键是(1) UPC 键,(2)日/周键,(3)活动优惠 ID。该表提供了财务部的关键成本信息,有助于衡量收入之外的利润。请注意,基于作业的成本会计通常用于将某些成本要素分配到产品层面。就零售商而言,促销可以得到制造商的额外资助(即供应商资助)。因此,获取供应商资金以计算开展促销活动的真实成本非常重要。将销售成本从促销成本中分离出来有助于我们了解成本的主要驱动因素是什么。

建立数据基础:制造商案例

图 5:图片由黄敏哈拍摄

对于制造商来说,关键数据领域是相似的。有几个关键的区别,我在下面指出:

(1)联合数据提供商:在消费品行业,和尼尔森通过他们的零售合作伙伴关系,从超市(如艾伯森、西夫韦、克罗格)、药店(如沃尔格林、CVS)、大型零售商(如沃尔玛、塔吉特)和仓储俱乐部(如好市多、山姆俱乐部)渠道的零售商那里收集数据,处理数据,并在收取订阅费后向制造商提供数据。通常,零售数据被汇总到由连锁横幅/市场组合定义的账户级别(例如,Kroger-Dallas)。)因此,这将是最低级别的数据粒度。请注意,CPG 制造商可以了解他们的竞争对手,因为联合数据提供商也提供竞争对手的数据。此外,至少对于“特色”促销,CPG 制造商可以从名为分子的服务中收集促销策略细节(例如,必须以 2.99 美元购买 4 个)。分子扫描特色广告插页,并为特色促销生成数据表。联合数据还提供产品主数据、市场人口统计数据和连锁特征。这种情况在其他行业(例如消费电子或高科技)非常不同,在这些行业中这些类型的服务是不可用的。在这些情况下,制造商是半盲的,因为他们可以跟踪自己的促销数据,但不能跟踪竞争对手的促销数据。高科技或消费电子行业的最佳外部数据由 NPD 以家庭面板数据(而非来自零售商的 POS 数据)的形式提供。)如果制造商是精选连锁店的品类负责人,这可能是促销效果衡量的另一个好的数据来源。

(2)售入与售出:制造商可能会倾向于使用对零售商的出货数据(售入)而不是零售数据(售出)。)由于从零售商那里提前购买,这是一个坏主意。当有好的交易时,零售商可以储存他们的存货,但这可能不会从零售商转移到消费者身上。因此,使用零售销售数据而不是工厂/配送中心装运数据非常重要。

(3)批发价与零售价:厂家直接销售给消费者的情况较少(即 DTC)。制造商先卖给零售商,零售商再卖给最终消费者。图中还可以有中间批发商/分销商。因此,制造商的最高价格将不同于最终零售价格。零售商在制造商利润的基础上增加额外的零售利润(即双重边缘化)。对于制造商而言,了解制造商利润和零售商利润对于与零售商进行数据驱动的讨论非常重要。因此,在财务报表中,每单位制造商的毛/净收入与零售商利润一起包括在内。跟踪常规降价折让(EDLP 折让)、固定交易成本和可变交易成本也很重要。

希望你觉得这个过程的第一步有用。在下一篇文章(第 3 部分)中,我将关注第 2 步,并讨论我们如何从我在第 2 部分中描述的中央数据存储库创建建模数据集。

实用数据科学:推广有效性和推广计划——第三部分

原文:https://towardsdatascience.com/practical-data-science-promotion-effectiveness-and-promotion-planning-part-3-7207deeacc49

6 步流程|步骤 2-准备建模数据集(1/2)

Aexander Sinn 在 Unsplash 上拍摄的照片

欢迎回到推广效果衡量和推广策划系列文章!在第 2 部分中,我讨论了整个流程的步骤 1:(1)构建数据基础。今天继续讨论下一步— 第二步:(2)准备建模数据集

如果您错过了前面的文章,下面显示了本系列第 1 部分和第 2 部分的链接。

实用数据科学:推广有效性和推广计划—第 1 部分| Minha Hwang |迈向数据科学

实用数据科学:推广有效性和推广计划—第二部分| Minha Hwang | 2022 年 11 月|迈向数据科学

第二步:准备建模数据集

在中央数据存储库中收集的用于促销有效性建模的基础数据的粒度低于所需的建模数据集。因此,我们需要在建模粒度的层次上正确地聚集数据。这种设计:较低粒度的基础数据集和较高粒度的建模数据集,旨在确保在建模数据集准备阶段选择建模粒度和特征工程的灵活性。总有可能汇总数据,但一旦进行汇总,我们就无法分解数据。

除了聚合之外,我们还需要创建新的数据列(即新特性),这些列符合建模规范。因此,从基础数据到建模数据集需要聚合、过滤和特征工程。该步骤包括(1) 数据质量检查,例如异常值分析和汇总统计,以及视觉检查,(2) (商店)聚类分析(如果之前没有定义促销区定价区),(3) 数据过滤聚合 , (4) 特征工程创建派生列,以及(5) 建模后数据集检查在详细描述这些之前,我总结了建模数据集的设计考虑事项

  1. 建模颗粒(适当的聚集)

有 3 个主要维度定义了建模粒度的聚集级别。我在下面描述了推荐的选择。

(1)时间— 日对周:周数据是业内很常见的选择,但我会推荐日数据。每周数据会很好地工作,并有助于低销售速度产品的建模。然而,如果不小心的话,这可能会产生“聚合偏差”。这在多市场零售商或全国性制造商中更为常见。多市场零售商(如 Kroger)通常会随着并购而增长,而本地连锁横幅往往会坚持不同的促销日历。当某些本地市场链的促销日历周(例如,周三至周二)与公共数据库周(例如,周日至周六)不对齐时,使用每周 POS 数据会导致对促销 ROI 和价格弹性的有偏见的估计(通常被低估)。这在图 1 中通过将具有分裂周问题的典型 IRI 或 AC Nielsen 数据与真实数据(从另一个具有适当促销周定义的每日数据集聚集而成)进行比较来直观地展示。)

我们称这种数据聚集偏差问题为“分割周”问题,因为真实的促销周数据被人为地分割成两个数据库周。上面的图 1 显示了南加州 Ralphs 连锁店谷类产品类别的实际消毒数据。我们将 IRI 的单位销售和价格数据(即错位数据)与促销周一致的数据(代表来自 Kroger: 84.51 的正确汇总数据)进行比较:由于 Kroger 84.51 数据不可用于研究,我们在本例中使用家庭面板数据为两者创建了代理。然而,这是基于多个咨询项目的经验。)随着促销周数据与非促销周数据的混合,促销高峰量在 IRI 数据中被低估。相比之下,非促销销售(促销周之后的一周)在 IRI 数据中被夸大了。在 IRI 的数据中,净价在促销周被高估,在非促销周被低估。这些模式通常会导致促销投资回报率的低估和价格弹性的向下偏差。更有甚者,如图 1 所示,来自 IRI 或 AC Nielsen 数据的净价与存在“分割周”问题的账户中的实际常规价格或促销价格不太一致。

图 1:图片由黄敏哈拍摄

此外,每日交易最近变得更加普遍,这导致了部分周促销。每日数据周将更能抵御每日交易的汇总偏差。如果您与零售商或制造商合作,他们只有一个促销周定义,没有每日交易,那么每周数据适合作为时间颗粒。

(2)产品— 品牌、UPC 与品牌包装:大多数关于促销建模的学术研究使用品牌作为分析的层次(UPC 扫描仪数据的商业使用:行业和学术视角,营销科学,1999)。这部分是由于 2000 年初推广建模在营销科学中流行时计算能力有限。然而,您必须管理和衡量的真正产品是在 UPC 级别。因此, UPC 作为产品的颗粒是更自然的选择。此外,你可以发现价格弹性确实会因同一品牌内不同的包装尺寸(例如,2L 瓶与 12 盎司 12 包罐)和口味(例如,普通与低热量)而变化。对于某些产品类别,产品线定价是常见的,其中同一产品线内的所有 UPC 具有相同的常规价格和促销价格。只要价格和非价格营销支持是常见的,就可以将 UPC 聚合到更高的水平。例如,对于软饮料类别,当包装尺寸相同时,所有不同口味的 UPC(可口可乐健怡、可口可乐普通)的普通价格和促销价格保持相同。在这种情况下,品牌/包装尺寸合计可能是合适的。

(3)位置— 商店与区域:这里最简单的选择是使用“商店”作为一个词,因为一个商店应该有相同的促销活动。这也让您可以灵活地在商店层面定制促销或定价。另一方面,商店级别的产品单位销售非常嘈杂(尤其是每天或每周经常零销售的尾部 UPC)。因此,在大多数情况下,只要促销在地理单元内是同质的,就优选将商店级数据聚集到更高的地理单元。同样,如果你不小心用不同的促销时间表聚集商店,会导致聚集偏差,这将扭曲促销 ROI 测量和价格弹性估计(使用市场级数据来理解非线性模型中的促销效果,市场研究杂志,1997)大多数多市场零售商为商店分配促销区(和价格区)。在相同的促销区域内,促销价格是相同的。同样,在同一价格区内,正常价格也是一样的。定价部门(负责常规价格)通常与促销部门(负责促销价格)分开,因此可以观察到定价区和促销区的差异。理想情况下,一个共同的区域,无论是正常价格/促销价格是首选,但你可以不时的差异。因此,您可以使用(1)促销区域作为建模粒度(如果定价/促销区域相同),或者(2)定价/促销区域组合作为建模粒度(如果定价区域与促销区域不同)。)这将确保您可以避免“聚合”偏差,并使数据更加稳健,因为商店/产品级别的随机噪声将与更高级别的聚合相抵消。完成数据汇总后,比较汇总前后的总体汇总统计数据(例如,独特天数、独特产品数量、总收入、总单位销售额)非常重要,以确保没有重大数据丢失或 SQL 编码错误。它还有助于绘制时间序列,以确保数据中没有不规则的异常。在下一篇文章中,我将通过实际的 POS 数据和代码示例使这一点更加具体。

2。数据过滤

对于 POS 数据这样的业务数据集,出现异常值是很常见的。因此,需要执行异常值检测(基于具有预先指定阈值的典型异常检测算法)来滤除异常值,这些异常值很可能是基于不正确的数据条目。一些 UPC 是失败的产品,几乎没有真正的销售。这些 UPC 增加了噪音,实际上与业务无关。因此,通常应用单位销售阈值来过滤掉销售水平非常低的 UPC。上一季的某些 UPC 只能有 1 或 2 个库存,这实际上也是无关紧要的。您也可以通过检查单位销售的周数来过滤掉这些 UPC。在使用几个标准进行筛选后,您可以检查保持了多少%的收入或(等价的)单位销售额,以确保您没有过多地筛选数据。通常情况下,经过适当的数据过滤后,95% — 99%的收入将得以保持。在下一篇文章中,我将使用实际的 POS 数据和 Python 代码示例演示过滤。

3。特征工程(衍生栏目)

除了聚合和筛选之外,建模数据集生成的一个主要任务是创建派生的特征列,这些特征列被映射到因果需求模型规范。下面显示的是您需要为作为玩具示例的最简单模型创建的典型列。

(1) 主键 : UPC,日,促销区 ID

(2) 数量(以当量单位计):单位销售额 x 数量当量

(3) 自有价格:焦点 UPC 的单价

(4) 促销战术:初始分类变量(必买 2、买 1 送 1、%折扣、绝对美元折扣等。)这将是一个热编码,为每种促销策略类型创建单独的虚拟变量。

(5) 非价格促销:特色指标(0/1),展示级别(高/中/低),优惠券指标(0/1)。二进制指示器是最简单的。如果需要更细致的测量,可以将封面从内页和封底分开。显示器上的情况(即连续变量)可以用来代替显示级别。众所周知,周日的头版特写更有效,尤其是大尺寸的产品广告。然而,这将花费更多。

(6) 竞品价格:理想情况下,你要弄清楚哪些套的产品被认为是相似的,并把这些产品的价格,这些价格会随着 focal UPC 的变化而变化。然而,这是很困难的,除非你有其他数据集,提供产品相似性的信息。产品相似性数据可以来自零售商忠诚卡的产品转换数据、家庭面板数据集(例如 Nielsen Homescan)、亚马逊搜索数据或产品描述的文本挖掘。由于自身价格弹性(平均为-2.5)比交叉价格弹性(来自竞争产品价格变化的影响,0.1 ~ 0.3)大一个数量级,因此在初始促销建模过程中,只控制总价格指数中的竞争价格就可以了。在排除焦点产品之后,可以通过计算(同一子类别中产品的收入)/(同一子类别中产品的单位销售)来计算总竞争价格指数。使用恒定权重更健壮,但是如果您想保持简单,这种简单的加权也很好。对于大规模应用,如果对最终结果的影响很小而计算成本很大(即工程决策),有时可以有意避免更严格的计算方式。)然而,这确实需要仔细的测试,以确保鲁棒性的潜在损失是小的和可管理的。

(7) 每周虚拟变量:为了恰当地控制每周的季节性,我们还可以包含一次性编码的每周虚拟变量。在类别(或子类别级别)指定这一点可以平衡变量数量和建模灵活性

(8) 工作日指标:如果你使用的是每日数据,那么包含工作日指标(如周一、周二和周日)是一个不错的主意,可以捕捉工作日不同的购物模式。

(9) 趋势变量:有潜在趋势是相当常见的。你能做的最简单的事情是包括周数变量及其多项式(例如,周数,周数的平方。)也可以应用黄土过程或霍尔特-温特斯过程(即指数平滑)来分解价格控制后的季节性和趋势,作为预处理。

(10) 成本:为了计算促销 ROI,产品的成本(即,零售商的批发价,或制造商的 COGS)和促销成本应包括在建模数据集中。

(11) 对数转换:在实践中工作良好的最简单的工作马模型是混合效应对数-对数需求模型。该模型使用 log(等量单位销售量)作为因变量,log(单价)和价格变量。因此,您需要将对数转换应用于价格和单位销售变量。)

(12) (可选)产品属性:如果您可以访问产品属性数据(例如,IRI 的产品存根文件),并清楚地知道什么是重要的产品属性,您也可以包括产品属性。这将允许您对新产品进行预测,只要新产品可以表示为现有产品属性的组合,这些新产品以前从未销售过。包括产品属性也会让你知道什么产品属性更受欢迎,什么不太受欢迎。

4。要注意什么

(1) 检查价格变化:一个常见的错误是将数据记录的大小等同于数据集的丰富程度。对于价格和促销建模,更重要的是,对于大部分产品来说,随着时间的推移,你是否有良好的价格变化(在同一 UPC/商店内)。在图 2 的例子中,沃尔玛有一个更大的数据集,但是由于频繁的价格变化,Best-buy 数据集会更有用。您可以计算的一个有用的汇总统计数据是价格的变异系数(即相对标准偏差:平均值/标准偏差 X 100%)。在一个极端的例子中,在一元店的 15,000 种产品中,只有 300 种产品的价格有意义的变化,这对我的一个咨询项目中的价格建模有用。

图 2:图片由黄敏哈拍摄

(2) 保证足够的历史:季节性和趋势性很常见。要正确控制它们,你需要至少 2-3 年的数据。Covid 导致了结构性突变,这使得利用最近的数据来识别季节性变得更加困难。

(3) 聚集偏差:在上面关于建模颗粒的讨论中,你可能已经注意到中心主题是避免“聚集”偏差。只要促销条件相同,就可以进行汇总。然而,如果你开始汇总不同促销条件下的数据,你就开始诱发偏差(通常是沿着低估价格弹性的方向)。)

(4) 仅横截面:可以请求仅使用横截面数据(单个时间段的多个地理单元/产品)进行因果促销需求建模。这将非常困难,需要特殊的建模技术(如空间计量经济学)和强有力的假设。数据中最有用的(因果)价格变化来自产品/商店内部的时间变化。没有足够历史记录的多种价格变化,就不可能恰当地衡量促销投资回报率或价格弹性。

(5) 与建模选择的依赖:模型规格说明和建模数据集创建之间有很强的依赖性。因此,明确您将尝试什么样的模型规范,并对要创建的潜在特性列更加包容,这一点非常重要。如果没有适当的规划,您可能不得不返回到建模数据集的创建并重新进行这项工作。如果您必须进行数据融合,并将数据集与不同级别的数据(例如,来自积分卡数据+商店级 POS 数据的产品相似性)相结合,这可能会更加困难。)

在下一篇文章中,我将通过利用公共 POS 数据( Dominick 的数据集,芝加哥大学市场营销中心的 Kilts)和 SQL / Python 代码样本,使这些想法更加具体。

在 1999 年左右,推广建模被认为是学术界中一个基本解决的问题。除了将因果需求建模方法从对数-对数模型改为总体离散选择模型之外,关于促销的学术研究最近不太活跃。相比之下,美国各大咨询公司在 2014 年左右才开始大规模采用推广 ROI 测量和推广建模作为解决方案。这些模型以前已经实施过,但是范围有限:只有少数零售商或制造商(例如 CVS、Proctor & Gamble。)随着计算能力的提高和更多的数据教育(滞后 10-20 年),这是从学术界到行业采用新方法的一种非常常见的模式。)现实情况是,如果你试图大规模实现推广建模,它远不是一个已解决的问题。因果 ML 需求建模与促销优化之间的联系在学术界和实践中都没有得到广泛研究。

参考

  1. 8451:克罗格提供的第一方零售数据平台。包括商店级 POS 数据集和来自优惠卡的汇总汇总,https://www.8451.com/
  2. Randolph E. Bucklin 和 Sunil Gupta,UPC 扫描仪数据的商业使用:行业和学术观点,营销科学,1999 年
  3. Markus Christen,Sachin Gupta,John C. Porter,Richard Staelin 和 Dick R. Wittink,使用市场水平数据理解非线性模型中的促销效果,市场研究杂志,1997 年
  4. 多明尼克的数据集,苏格兰裙营销中心,芝加哥大学

真实量子计算机上的实用误差抑制

原文:https://towardsdatascience.com/practical-error-mitigation-on-a-real-quantum-computer-41a99dddf740

使用 Clifford 数据回归方法

量子机器学习要不要入门?看看 动手量子机器学习用 Python

量子计算机天生不可靠。它们容易出错,从而破坏任何有意义的计算。由于我们目前的设备太小,无法纠正错误,我们今天能做的最好的事情就是减少它们的影响。

作者图片

不出所料,量子误差缓解的研究是一个重要的关注点。一个最近的和有希望的误差减轻方法是 Clifford 数据回归(CDR)。P. Czarnik 等人在《Clifford 量子电路数据的误差缓解》中提出了这一点,Quantum 5,592 (2021) 。在这种方法中,我们创建了一个机器学习模型,通过使用来自经典模拟的量子电路的数据,我们可以使用该模型来预测和减轻噪声。

在之前的帖子中,我们已经看过了(CDR)。此外,我们使用开源库 Mitiq 在另一篇文章的中的一个示例量子电路上应用了这种方法。为此,我们在量子模拟器中加入了噪声。然而,噪音是相当普遍的,所以很难对这种方法的有效性得出任何结论。

因此,在这篇文章中,我们修改了我们的代码,以使用一个真实的量子设备。

我们从连接到我们的 IBMQ 帐户开始。在下面的清单中,用实际的 API 令牌替换TOKEN。如果你还没有,这篇文章解释了如何开始使用 IBM Quantum 以及如何获得 API 令牌。

在真正的量子计算机上运行代码是一项耗时的工作。IBM 允许公众访问他们的设备,所以我们不是唯一运行我们代码的人。一旦你发送了一个电路,它在被执行之前会在队列中等待很长一段时间。

所以,让我们插入一个中间步骤,创建一个更真实的量子模拟。为此,我们不制作定制的噪声模型,而是从真实的量子器件中获取。

如果你关注我的每周帖子,你会知道我们正在努力参与 IBM 的量子开放科学奖。他们希望我们在他们的 7 量子位 Jakarta 设备上使用 Trotterization 模拟一个三粒子系统的海森堡模型哈密顿量。所以,我今天就用这个设备。如果您尚未注册参加此次挑战,您将无权使用此设备。在这种情况下,我建议也使用我们在这篇文章中使用的 Quito 系统。

下面的清单显示了如何从现有硬件加载噪声模型。

首先,我们需要获得一个允许我们访问后端的提供者(您需要使用不同的参数值来连接到ibmq_quito)。

在这个中间步骤中,我们使用后端作为NoiseModel.from_backend的输入。结果是一个有效的 Qiskit 噪声模型,我们可以将其输入到量子模拟器中。

在我们的例子中,我们将噪声模型提供给从mitiq导入的execute_with_shots_and_noise函数。我们创建一个函数(sim_jakarta),它采用一个量子电路并产生最终的期望值。我们使用与上一篇相同的可观察值。

我们可以直接用这个函数来获得我们的可观测值的期望值。请注意,结果值会受到噪声的影响。我们看到它与理想的测量值有很大的不同。

ideal_measurement =  1.0599428216452071
unmitigated_measurement =  0.8542528961820239

在上面的代码中,我们使用了前一篇文章中介绍的另外两个函数。为了完整起见,这些是创建量子电路的get_circuit和无噪声模拟器sim

我们现在已经准备好使用模拟的雅加达后端运行完整的 CDR。与前一个应用程序的唯一区别是,我们使用sim_jakarta函数作为噪声执行器参数。

mitigated_measurement =  1.0895031224814202

将减少误差的结果与未减少误差的结果进行比较,误差减少了近 90%。这与我们在一般噪声模型中看到的改进相当。

Error (unmitigated): 0.2056899254631832
Error (mitigated with CDR): 0.029560300836213083
Relative error (unmitigated): 0.19405756731662027
Relative error (mitigated with CDR): 0.027888580622047698
Error reduction with CDR: 85.6%.

现在,让我们直接接触真正的量子设备。问题是如何将调用集成到真正的后端。将量子电路发送到真实设备相对容易。无论如何,让我们检查它是否工作。

我们创造了一个简单的量子电路,它由一个量子位和一个哈达玛门组成。使用模拟器的不同之处在于以下两个步骤。在将电路输入后端的run方法之前,我们需要传输和组装电路。一旦运行了这段代码,登录到 IBM 量子控制台是个好主意。

在那里,打开“作业”页面,查看您的所有作业及其状态。在我的情况下,我必须等待大约两个小时才能运行作业。

接下来,我们需要编写一个不是模拟器而是连接到真实量子设备的执行器。到目前为止,我们已经使用了从 Mitiq 的 Qiskit 接口导入的execute_with_noise_and_shots函数。让我们看看这个函数,看看它是如何运行电路的。您可以在公共存储库中找到源代码。

在第 166 行,你可以看到它是如何运行量子电路的。

*# execution of the experiment*
job **=** qiskit.execute(
    circ,
    backend**=**qiskit.Aer.get_backend("aer_simulator"),
    backend_options**=**{"method": "density_matrix"},
    noise_model**=**noise_model,
    *# we want all gates to be actually applied,*
    *# so we skip any circuit optimization*
    basis_gates**=**basis_gates,
    optimization_level**=**0,
    shots**=**shots,
    seed_simulator**=**seed,
)
counts **=** job.result().get_counts()

他们使用 Qiskit 的 execute 函数并获得一个job对象。这与我们在真实设备上运行电路时得到的是同一个对象。见我之前的帖子。

因此,让我们重新编写这个函数,不运行本地 Qiskit execute,而是在真实的量子设备后端传输、组装和运行电路。

我们删除了参数noiseseed,因为我们不再使用模拟。此外,我们用上面提到的过程替换了qiskit.execute调用。函数的其余部分保持不变。

此外,我们编写另一个函数real_jakarta,它采用量子电路并返回计算出的期望值。它的工作原理类似于以前使用的模拟器。因此,让我们用它来运行电路,并计算未优化的测量值。

real unmitigated_measurement =  0.17124424416552098

最后,我们可以使用真正的 Jakarta 后端运行 CDR。当您启动它时,请考虑 CDR 运行几个回路来训练模型。如果每次执行需要等待两个小时,那么计算所有电路将需要一整天。

因此,在所有这些等待时间之后,结果显示 CDR 减少了由噪声引起的几乎 90%的误差。这些结果与模拟结果相当。

今天,我们了解到修改 Mitiq 代码以与真正的 Jakarta 设备一起工作是非常简单的。

量子机器学习要不要入门?看看 动手量子机器学习用 Python

在这里免费获得前三章。

使用 Azure 机器学习的实用联邦学习

原文:https://towardsdatascience.com/practical-federated-learning-with-azure-machine-learning-8807f9bd1a7e

不共享数据的协作机器学习

由哈姆克·阿尔克马德和安德里亚斯·科普

医学图像的联合学习(来源:Shutterstock)

2021 年 12 月,我们发布了几项资产,以支持使用 Azure 机器学习的医学成像。巨大的兴趣和众多的询问让我们非常惊讶。它再次表明,人工智能应用在医疗实践中变得越来越重要。

通过联合学习,我们今天向我们的产品组合引入了一个令人兴奋的新成员,这对医疗保健行业内外保护数据和知识产权具有重大意义。

下图概述了我们的演示用例,包括我们最新添加的联合学习。

医学影像存储库用例

在演示场景中,您可以与美国、欧洲和亚洲的模拟参与医院一起构建一个全球联合学习场景,以开发一个用于在 X 射线图像中检测肺炎的通用 ML 模型。在本文中,我们描述了联邦学习的概念基础,并介绍了演示的关键要素。

查看库以尝试这个用例或其他场景,并根据您自己的业务场景进行调整。

联邦学习概念

可推广的深度学习模型的关键成功因素是广泛和异构训练数据的可用性。可靠的癌症检测模型应该基于成千上万的医学图像来训练,这些医学图像显示健康组织和肿瘤的对比。这也应该代表患者的性别、年龄和其他人口统计学特征的真实世界范围。此外,不同成像技术所产生的视觉特征也应该表现出来。所需的各种代表性根本不是一个单一机构能够涵盖的。在罕见疾病的情况下尤其如此,在这种情况下,一个组织中只有几个数据点可用。

因此,许多不同的医院贡献他们自己的数据的合作 ML 开发是显而易见的。不幸的是,由于数据保护问题,这在实践中经常失败,因为在大多数国家,患者数据受到严格的数据保护法的管制(理由充分)。

联合学习通过允许多方协作训练机器学习模型而无需共享数据来解决这一问题,如下图所示:

联邦学习概念

虽然对等拓扑在联合学习中是可能的,但大多数实现使用中央服务器来进行模型参数聚合和流程编排。

联合学习的关键特征是参与者的本地数据从不共享。每个客户定期收到全球模型的副本,并使用本地数据进行本地培训。唯一共享的信息是从训练中获得的“见解”,即模型参数更新。服务器的一个任务是将所有参与客户端的权重聚集到新的模型版本中。然后,更新后的模型被重新分发给客户端,以启动下一轮联合训练。重复这个过程,直到模型收敛。通过利用参与客户在生产阶段获得的新数据点,它还可用于持续培训。

下图说明了基于服务器的模型的一轮培训的工作流程:

联合学习工作流

让我们沿着模型分布、局部训练、权重聚集和模型更新的步骤来看一些关键的考虑事项。

第一个联合学习应用程序是智能键盘场景,用于数百万部参与的智能手机(“跨设备”)完成句子。相比之下,组织之间的联合学习场景(“跨竖井”)具有不同的特征和要求。通常情况下,他们涉及到中等数量的参与者,如数十家医院。通常,存在与安全性、隐私和合规性相关的严格要求。联合学习为这样的组织用例提供了控制选项。其中之一是能够定义允许哪些客户端参与并接收全局模型的副本。

跨竖井方法的一大优势是参与者拥有 IT 基础设施,可以利用强大的 GPU 资源来支持医学成像或其他深度学习用例中常见的大型模型。我们的演示展示了按需使用 Azure CPU 或 GPU 计算资源进行联邦学习。

关于数据注释标准的明确协议是多方培训设置的关键要求,特别是对于像图像分割这样的高级任务。标签实践或质量的系统性偏差可能会对最终模型产生负面影响。此外,在联邦学习概念中,没有关于数据或注释的集中质量保证,因为数据是不共享的。

随着联合学习在企业场景中获得更多的牵引力,权重聚合方法变得更加复杂,以满足企业需求。一种直观的聚合方法称为联邦聚合:每个参数都根据本地数据点的数量进行加权。例如,如果医院 A 的更新基于 100 幅图像,而医院 B 基于 200 幅图像进行训练,那么医院 B 的贡献将是医院 A 的两倍。存在联合聚合的几种变体,以在偏离分布的情况下改善收敛,或者改善隐私和安全性。一个重要的扩展是安全聚合,它保护通信免受中间人攻击,并防止服务器以明文形式访问来自各个客户端的更新。它基于一种加密协议,该协议确保一个客户端的贡献看起来像随机噪声,除非与其他客户端的贡献合并在一起。

对于端到端的隐私保护解决方案,我们还需要考虑由此产生的模型。今天的大型深度学习模型倾向于记忆单个数据点。例如,研究人员已经表明,从经过训练的人脸检测分类模型中重建人脸的可识别图像是可能的。这种模型反演攻击的风险并没有随着联合学习而降低,因为它保护了输入数据**,但没有保护结果模型**。因此,联合学习通常与差分隐私相结合,通过在训练过程中添加定义量的统计噪声来保护结果模型。这大大降低了重建单个数据点或链接攻击的风险。如果你想了解这个概念的更多信息,请查看我们知识库的差分隐私演示。

演示架构

医学影像存储库中的演示包括一个联合学习场景,其中多家医院使用自己的私有数据集训练一个中央模型。该方法使用中央服务器进行聚合,这意味着服务器向不同的医院发出指令,并在本地训练运行结束后收集模型权重。在中央服务器上汇总后,更新的模型被发送到医院,作为下一轮的起点。

在架构方面,完整的设置是使用 Azure 组件实现的。医院由部署在不同地区的机器学习工作区来代表,以创建一个全球设置。联邦服务器是使用 Azure 数据科学虚拟机实现的。对于配置和编排,使用 NVIDIA Flare 包。

该模型在公共 Kaggle 数据集上进行训练,该数据集包含有肺炎和无肺炎患者的 x 射线图像。使用该数据集,二进制分类模型被训练以将 x 射线图像分类为“肺炎”或“正常”类别。

付诸行动

如果您喜欢自己重新创建演示设置,请查看我们的分步指南。为了使解决方案易于部署,提供了一些 GitHub 动作工作流来准备运行实验的环境。

存储库中可用的工作流之一创建 Azure 资源,主要是一个具有特定配置的数据科学虚拟机和三个不同的 Azure 机器学习工作区。还为每个工作区创建了一个计算实例。计算实例将运行由 NVIDIA Flare 生成的客户端包。客户端包用于启动与服务器和管理客户端的通信,这些客户端将在 Data Science 虚拟机上运行。

还提供了从 Kaggle 下载肺炎数据集并对其进行重新采样的工作流。重采样后,数据被分成三个不同的部分,为不同的客户端创建私有数据集。准备阶段的最后一步是使用 Azure ML CLI 将私有数据集作为数据资产上传和注册到不同的工作区。使用这种方法,我们可以使用在不同客户端上运行的训练脚本中的训练数据。

在设置好基础设施之后,我们可以启动将要上传到客户端和服务器的包。为了生成它们,我们使用登录到虚拟机,例如通过本地终端建立 SSH 连接。然后,我们在虚拟机上克隆医学影像存储库。

存储库中的 federatedlearning-folder 包含一个名为 project.yml 的文件。该文件包含服务器、客户机的设置以及更多用于联邦设置的配置选项。更多详情,见本页。

可以使用以下命令运行 NVFlare 资源调配工具:

provision -p project.yml

这将为每个参与者生成一个启动工具包,默认情况下,在运行 provisioning 命令的工作区文件夹中项目名称的文件夹内,在前缀为“prod_”的目录中创建启动工具包。客户端包被复制到创建的不同机器学习工作空间。服务器和管理包都保留在联邦服务器上,但是被移动到虚拟机的主目录中。

进行实验

可以使用管理客户端控制实验。客户端和服务器包可以在同一个虚拟机上运行。要启动服务器和客户端之间的连接,首先在服务器虚拟机上运行 start 命令。我们通过导航到服务器启动工具包中的启动文件夹并运行 bash start.sh 来实现这一点。

当服务器启动时,客户机上的启动脚本可以运行。这应该会导致服务器和客户端之间的成功连接。在服务器的终端中,输出应该如下所示:

在为三个不同的客户机运行启动脚本之后,我们可以使用管理客户机验证连接是否成功。为此,我们打开另一个终端,并运行 admin 文件夹中的启动脚本。使用 admin@nvidia.com 的用户名登录。

通过键入“?”,列出了管理客户端的不同命令。其中一些与培训周期相关,如“部署应用程序”和“启动应用程序”。其他命令主要集中在解决方案的 IT 管理部分。

通过运行 check_status 命令,我们可以验证我们的三个客户机是否成功连接到联邦服务器。

将客户端连接到服务器后,就可以开始实验了。

该存储库包含两个应用,我们在 NVFlare 中称之为它们,可用于运行实验。两者都使用相同的模型架构和训练逻辑。主要区别在于,一个应用程序训练一个联邦模型,而另一个训练一个中央模型。这允许我们进行实验,比较集中式运行和联合运行的性能。

为了准备我们的运行,我们需要为所有客户机设置运行号,然后运行 check_client 命令来验证:

我们需要运行的下一个命令是upload _ app pneumonia-federated。这将把我们的应用程序上传到联邦服务器的 transfer 文件夹,准备好复制到不同的客户端。

为了将应用程序部署到不同的客户端,我们使用命令deploy _ app pneumonia-federated all。这将把培训说明复制到不同的客户端和服务器。如果成功,客户端将向服务器返回 OK 状态。

我们需要的最后一个命令是 start_app all。这启动了针对不同客户的培训运行:

可以使用运行服务器启动脚本的终端,或者使用不同客户端的终端来跟踪训练运行的进度。

一个明显的问题是联合培训与传统(集中)培训相比如何。为了回答这个问题,可以在联合实验之后进行集中培训。在这个实验中,将在完整的肺炎数据集上训练相同的算法,但这次我们只使用一个客户端。用于准备数据的 GitHub Actions 工作流包含将完整数据集注册到亚洲工作空间的步骤。因此,我们在这一步中使用该客户端。

通过在管理终端中运行以下命令,可以启动集中式实验:

set_run_number 2
upload_app pneumonia-central
deploy_app pneumonia-central client FL-Asia-Hospital
start_app server
start_app client FL-Asia-Hospital

查看结果

在客户端,在训练运行期间创建事件,并将其流式传输到服务器。这些指标可以在服务器上名为“run_ ”的文件夹中找到。在我们 repo 中存在的应用程序中,跟踪指标的方式是,可以使用 TensorBoard 来使用它们。Tensorboard 为机器学习实验提供交互式可视化功能。

要在联合服务器上运行 TensorBoard,请运行以下命令:

tensorboard --logdir=fedserver --bind_all

-bind_all 参数使应用程序可以从外部访问,如果您想从本地机器访问仪表板而不利用远程桌面选项,这是必需的。从网络角度来看,我们还需要打开 TensorBoard 正在使用的端口(默认为 6006)。

下面是我们 TensorBoard 的截图。在左侧幻灯片中,我们可以选择过滤我们想要可视化的不同运行。这对于比较不同参数运行之间的性能非常有用。可视化的值是训练损失和验证准确性。训练损失将指示不同客户的模型能够在本地数据集上概括的有多好。验证准确性显示了局部模型在全局验证数据集上的性能。

张量板中的联邦学习曲线

基于这两个实验设置,我们可以比较集中运行和联合运行的结果。从一些实验中可以看出,集中运行可以获得稍高的精度。在某些情况下,使用这两种设置可以获得相同的性能,尽管联合训练运行收敛需要更长的时间。

下图显示了集中式运行和联合运行之间的比较,从中我们可以看到集中式运行带来了更高的准确性。应该使用其他参数(例如,学习率、时期数)进行进一步的实验,以得出最终结论,这些结论也不应该在不同的用例中推广。

集中培训和联合培训的融合

结论

在训练数据不能共享的情况下,联合学习对于 ML 解决方案的共同开发是一个令人信服的概念。一个明显的应用领域是隐私保护的 ML 培训。其他例子如用于药物发现的 MELLODY 项目展示了一些应用,在这些应用中,相互竞争的公司开发了一个联合模型,而不必担心他们的知识产权受到损害。

随着这一概念的成熟,包括对安全和健壮的联合聚合的支持,它在具有特定需求的企业和公共部门组织中变得更加普遍。

Azure ML 和 NVIDIA NVFlare 的结合甚至可以快速实现全球联合学习部署,并在合规性、数据保护、可管理性和可追溯性方面提供显著优势。

端到端隐私保护解决方案需要将联邦学习与差分隐私等技术相结合。

除非另有说明,所有图片均由作者所有

[1]肺炎数据集:Paul Mooney。2018.胸部 x 光图像(肺炎)。执照: CC BY 4.0 。来源:胸部 x 光图像(肺炎)| Kaggle

DQN 实用指南

原文:https://towardsdatascience.com/practical-guide-for-dqn-3b70b1d759bf

DQN 在强化学习中的实现

照片由 JJ 英在 Unsplash

“实践你所知道的,这将有助于弄清楚你现在所不知道的”
伦勃朗

概观

Mnih 等人[2015]提出的深度 Q-网络是许多深度强化学习算法的跳转起点和构建点。然而,尽管它表面上很简单,然而,当实现它和寻找问题的解决方案时,它代表了一些挑战。

本文将使用 Tensorflow.js 作为实现技术,原因很明显:
——你需要安装的东西越多,你就越不可能努力编写真正的算法。

  • Javascript 是一种很好的流行语言,对于熟悉 C++、C#、Java 等的人来说非常直观……
    -在网上分享演示既简单又直接。

该算法

如前所述,该算法本身非常简单,可以通过下图进行总结

DQN 工作流程(图片由作者提供)

以下步骤描述了该算法的实际工作原理:

1-创建 Q-网络和目标-网络
2-使用 Q-网络
用数据填充经验缓冲区 3-重复以下步骤足够的次数
4-从经验缓冲区获得随机样本
5-将样本作为输入馈送到 Q-网络和目标网络
6-使用目标网络的输出来训练 Q-网络(即,目标网络的输出将在 标准的监督学习场景)
7-应用探索/开发策略(例如:ε贪婪)
8-如果选择了探索,则生成随机动作,否则,如果选择了开发,则向 Q 网络馈送当前状态,并从输出中推断动作。
9-将动作应用于环境,获得奖励,以及新状态
10-将旧状态、动作、奖励和新状态存储在经验缓冲区(也称为重放记忆)
11-每隔几集,将权重从 Q-网络复制到目标-网络

为什么是两个网络?

很明显,我们想知道,为什么我们需要两个网络?
实际上,Q 学习公式如下:

q-学习:非策略 TD 控制(摘自萨顿和巴尔托的书:强化学习,介绍)

我们注意到,为了使 Q(S,A)达到它的最终值,它必须变得几乎等于 R' + γ max Q(S ',A)。因此,为了使 Q(S,A)向 R' + γ max Q(S ',A)移动,我们将问题转化为某种监督学习,其中我们估计 Q(S,A)和 R' + γ max Q(S ',A)成为“标签”。
用于估计 Q(S,A)的网络权重将通过损失函数和反向传播的组合来更新。

但是我们不知道最大 Q(S ',a),我们简单地使用相同的神经网络来估计它,然而,该网络的权重不断地变化,这在每次迭代中给出不同的最大 Q(S ',a)值。这使得对 Q(S,A)的估计可以追踪一个移动目标。

为了解决这个问题,我们创建了一个相同的神经网络,我们称之为目标网络,而计算 Q(S,A)所需的第一个网络将被称为在线网络。目标网络的目的是在一段时间内具有固定的权重,以便稳定 max Q(S’,a)的计算。然后,在一些迭代之后,目标网络通过在线网络的权重的简单拷贝来更新其权重,在线网络有机会学习这些权重。

当心一些实现陷阱

  • 标准化输入。这可能是显而易见的,但是初学者可能很容易忘记这个重要的特性。这将对输出产生重大影响,因为一些输入可能具有较大的值范围,而其他输入则具有较小的值范围。这并不意味着后者不如前者重要。然而,从计算的角度来看,大值会压倒小值。因此,如果您看到一些明显的输入有奇怪的值,这是一个很好的条件反射,检查输入是否已经被规范化。
  • 同步两个神经网络。DQN 使用两个神经网络,一个是“在线”的,这意味着在每次迭代中计算 Q 值,另一个是“目标”网络,它计算 Q 值,但基于一组稳定的权重。
    “目标”网络表示表达式 R + γ max Q(St+1)。“在线”和“目标”网络的输出用于计算均方误差(MSE ),然后通过反向传播更新“在线”网络的权重。
    每一定次数的迭代,“在线”网络的权重应该被复制到“目标”网络,以便“目标”获得由“在线”网络学习的经验。
    这里的重要值是设置复制权重的迭代次数的正确值。太小的值,使系统不稳定,太大的值使系统无法学习。所以当事情没有朝着正确的方向发展时,检查这个参数是很重要的。
  • 填补经验缓冲。在过程开始时,重要的是通过让代理与环境交互来填充经验缓冲区,并在经验缓冲区中存储当前状态、动作、奖励、下一状态、终端状态,以便当学习过程从该经验中生成随机样本时。
  • 抽样规模。将采样大小设置为经验缓冲区大小的一半左右是一个很好的做法,因为小的采样大小会使从经验中学习的效率低下,而大的采样大小(最大值附近)会在学习中引入您不想要的偏差。例如,如果你有一辆自动驾驶汽车,并且由于大多数时间道路是直的,过度地从直路经验中采样将使 AI 倾向于最小化转弯的影响。

扁担例子

DQN 的一个经典实现是 CartPole 游戏,在这个游戏中,人工智能代理应该以某种方式移动手推车,以保持杆子接近垂直。
电杆与垂直方向的夹角不得超过 30 °,手推车不得触及边缘。每当人工智能代理收集到 300 个奖励时,就取得了胜利。

你可以在 https://rl-lab.com/cartpole/查看游戏的真实实现

图片显示了弹球游戏的配置(图片由作者提供)

以下是算法主要部分的代码片段。它包含主循环、播放步骤和基于批量样本的训练。

train()方法包含训练的主循环。它首先初始化代理,并用经验填充重放内存缓冲区,然后开始循环:训练,然后单步执行。

train(agent, maxIterations, batchSize, gamma, learningRate, syncEveryFrames) { // init the agent
  agent.reset(); // at first fill the replay memory with experience by playing
  // without any training
  for (let i = 0; i < agent.replayBufferSize; ++i) {
    agent.playStep();
  } // create optimizer for the training  
  const optimizer = tf.train.adam(learningRate); // loop until all iterations are done
  let counter = maxIterations; while (counter > 0) { // train the agent by extracting samples from replay memory and 
    // use them as input for the DQN   
    agent.trainOnReplayBatch(batchSize, gamma, optimizer); // after each training, play one step of the game    
    agent.playStep(); if (agent.frameCount % syncEveryFrames === 0) {
       copyWeights(agent.targetNN, agent.onlineNN);
    } counter--;
  }
}

playStep()方法使代理只玩一步游戏。它使用ε贪婪策略,使它在部分时间选择随机动作,在其余时间选择返回最大 Q 值的动作。

// play one step of the game and store result in replay memory
playStep() {...
  let action;
  let actionIndex = 0; const state = this.game.getState(); if (Math.random() < this.epsilon) {
    // Pick an action at random.
    actionIndex = this.getRandomAction();
    action = ALL_ACTIONS[actionIndex];
  } else {
  // Greedily pick an action based on online DQN output.
  // use tf.tidy() to clean up after finishing
     tf.tidy(() => {
       const stateTensor =  getStateTensor(state);
       actionIndex = this.onlineNN.predict(stateTensor)
                              .argMax(-1).dataSync()[0]; action = ALL_ACTIONS[actionIndex];
     });
  } // play one step of the game and get the results
  const result = this.game.step(action); // store experience in replay Memory
  this.replayMemory.append([state, actionIndex, result.reward,
                                 result.state, result.status]); // if terminal state, reset the game
  if (r.status != 0) {this.reset();}}

trainOnReplayBatch()从 replayMemory 中提取一个样本,并将其作为 DQN 的输入,然后使用均方误差作为损失函数来更新权重。

// This method is used to train the online network by using batch
// samples from the memory replaytrainOnReplayBatch(batchSize, gamma, optimizer){// Get a batch of examples from the replay buffer.
const batch = this.replayMemory.sample(batchSize);//define the loss function
const lossFunction = () => tf.tidy(() => { // example[0] is the state
  // example[1] is the action
  // example[2] is the reward
  // example[3] is the next state
  // example[4] indicates if this is a terminal state const stateTensor = getStateTensor(batch.map(
                                     example =>   example[0]));
  const actionTensor = tf.tensor1d(batch.map(
                                   example => example[1]), 'int32');

  // compute Q value of the current state
  // note that we use apply() instead of predict
  // because apply() allow access to the gradient
  const online = this.onlineNN.apply(stateTensor, {training: true});
  const oneHot = tf.oneHot(actionTensor, NUM_ACTIONS);
  const qs = online.mul(oneHot).sum(-1);  // compute the Q value of the next state. 
  // it is R if the next state is terminal
  // R + max Q(next_state) if the next state is not terminal const rewardTensor = tf.tensor1d(batch.map(
                                         example => example[2]));
  const nextStateTensor = getStateTensor(batch.map(
                                         example => example[3]));
  const nextMaxQTensor =  this.targetNN.predict(
                                         nextStateTensor).max(-1);
  const status = tf.tensor1d(batch.map(
                          example => example[4])).asType('float32'); // if terminal state then status = 1 => doneMask = 0
  // if not terminal then status = 0 => doneMask = 1
  // this will make nextMaxQTensor.mul(doneMask) either 0 or not
  const doneMask = tf.scalar(1).sub(status);
  const targetQs = rewardTensor.add(
                           nextMaxQTensor.mul(doneMask).mul(gamma));  // define the mean square error between Q value of current state
  // and target Q value
  const mse = tf.losses.meanSquaredError(targetQs, qs);
  return mse;
});// Calculate the gradients of the loss function with respect 
// to the weights of the online DQN.const grads = tf.variableGrads(lossFunction);// Use the gradients to update the online DQN's weights.optimizer.applyGradients(grads.grads);tf.dispose(grads);
}

最后,这里是一个视频,其中 DQN 代理人通过达到 300 英镑的奖励来玩并赢得了掷骰子游戏。

展示 DQN 算法玩钢管舞的视频(视频由作者提供)

结论

DQN 是最流行的深度强化学习算法之一。它第一次在 Atari 游戏上实现了超人水平的性能。
随着时间的推移,它经历了多次改进,这使得它仍然是 DRL 表现最好的代理商之一。

用于多类图像分类的 TensorFlow 迁移学习实用指南

原文:https://towardsdatascience.com/practical-guide-to-transfer-learning-in-tensorflow-for-multiclass-image-classification-d35fab7b28c0

在图像分类中实现迁移学习的详细讲解的分步教程

Jason Yuen 在 Unsplash 上的照片

通常我们无法获得大量的标记数据或计算能力来从头构建图像分类深度学习模型。

幸运的是,迁移学习使我们能够为我们的特定分类任务开发鲁棒的图像分类器,即使我们的资源有限。

在这个简单易懂的演练中,我们将了解如何利用预先训练的模型作为 TensorFlow 中迁移学习的一部分来有效和高效地对图像进行分类。

目录

(1)迁移学习的动机和好处 (可选)(2)关于数据集(3)步骤**

本文附带的 GitHub repo 可以在这里找到。

(1)迁移学习的动机和好处

可选阅读

迁移学习是一种强大的方法,它使用预先训练的模型作为创建新模型的起点,而不是从头开始构建新模型。

这些预先训练好的模型由学术或大型技术研究人员建立,他们开发新的深度学习模型架构,并在强大计算能力的帮助下在大型数据集上训练它们。

几个重要的好处推动了迁移学习的流行:

  • ****节省大量时间和资源因为从零开始训练深度学习模型是耗时和资源密集型的
  • ****更好的性能因为预训练模型在对大型数据集进行大量训练后已经了解了重要的特征
  • 需要更少的标记数据,因为我们可以利用预训练模型所拥有的“知识”的坚实基础
  • 减少过度拟合,因为从头构建的模型可能对较小训练数据集的特定特征过于敏感

照片由蒂姆·莫斯霍尔德在 Unsplash 上拍摄

(2)关于数据集

我们将使用牛津-IIIT pet 数据集来完成这个图像分类任务。这是一个 37 类 pet 图像数据集,每类约 200 张图像。

该数据集非常适合本教程,因为:

  • 它相对较小(总共约 800Mb)
  • 在知识共享署名-类似共享许可下,它可用于商业和研究目的
  • 大量的类是测试多类图像分类的迁移学习的一个很好的方法
  • 它是tensorflow_datasets ( [tfds](https://www.tensorflow.org/datasets/api_docs/python/tfds))中许多现成可用的数据集之一(我们将在本教程中直接加载)

牛津-IIIT pet 数据集样本|作者图片

以下注释可用于每个图像:

  • 物种(猫或狗)和品种名称
  • 动物头部周围的紧密包围盒
  • 像素级前景-背景分割

本教程将重点关注品种名称作为我们要预测的标签。

如果我们希望获得 TensorFlow 之外的数据,我们可以执行以下任一操作:

  • T21【BitTorrent】与学术洪流
  • 直接下载 数据集【images.tar.gz】【annotations.tar.gz】

(3)逐步指导

查看 已完成的笔记本 以跟随本演练。

步骤 1 —初始设置

在本教程中,我们将使用 Google Colab ,因为它允许我们免费访问 GPU,并且默认环境具有必要的 Python 依赖性。

虽然大量的计算能力是不必要的,但在 GPU 上运行迁移学习仍然至关重要,因为我们正在处理深度学习模型。

重要 :记得从顶部菜单栏将 硬件加速器 切换到GPU****:运行时>改变运行时类型。****

在发布新的 Colab 笔记本时,我们导入以下包:

***import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

import tensorflow as tf
import tensorflow_datasets as tfds
from tensorflow import keras
from tensorflow.keras import layers
from keras import callbacks***

步骤 2-通过训练-验证-测试分割加载数据

如前所述,我们将使用 TensorFlow 数据集组件tfds来加载图像和相应的元数据。

***(train_raw, val_raw, test_raw), ds_info = tfds.load(
                                          name='oxford_iiit_pet',
                                          split=['train[:90%]', 
                                                'train[90%:]', 
                                                'test'], 
                                          shuffle_files=True,
                                          as_supervised=True,
                                          with_info=True
                                          )***

tfds.load函数自动从数据源下载数据,并将其作为一个tf.data.Dataset对象返回。

  • name:tensor flow 数据集集合中的数据集名称。在这种情况下,它被命名为oxford_iiit_pet
  • split:指定数据分割的参数。由于原始数据中只有两个分裂(训练和测试),我们将划分 10%的训练集来创建一个新的验证集
  • shuffle_files:如果设置为真,输入数据的顺序会被打乱(以促进更好的模型泛化)
  • as_supervised:如果设置为 True,返回的tf.data.Dataset对象将有一个元组结构(图片,标签),其中标签指的是图片中的宠物品种。
  • with_info:如果设置为 True,元数据将作为DatasetInfo对象与图像数据集对象一起返回

由于我们设置了with_info=True,我们将获得两个输出对象:

  1. 基于三个拆分的一组三个数据集对象(train_rawval_rawtest_raw)
  2. 包含数据集元数据的DatasetInfo对象(ds_info

如果我们有想要加载到tfds的自定义数据集(例如 CSV 文件),我们可以在这里和这里参考文档。

步骤 3-浏览数据

有多种方法可以更好地理解数据集。

㈠元数据

如果可以的话,我们可以通过在一个单元格中运行它来仔细查看存储在ds_info中的元数据。

牛津-IIIT Pet 数据集的数据集信息(也称为元数据)|作者图片

从输出中,我们可以发现诸如图像特征的类型、类的数量和分割的大小等信息。

要直接获得类的数量和分割大小,我们可以运行以下代码:

***# Get number of classes
num_classes = ds_info.features['label'].num_classes
print('Number of classes:', num_classes)

# Get split sizes (aka cardinality)
num_train_examples = tf.data.experimental.cardinality(train_raw).numpy()
num_val_examples = tf.data.experimental.cardinality(val_raw).numpy()
num_test_examples = tf.data.experimental.cardinality(test_raw).numpy()

print('Number of training samples:', num_train_examples)
print('Number of validation samples:', num_val_examples)
print('Number of test samples:', num_test_examples)***

㈡获得价值计数

理解数据的一个快速方法是检查图像标签的分布。我们可以通过自定义的值计数函数来实现:

***def get_value_counts(ds):
  label_list = []
  for images, labels in ds: 
    label_list.append(labels.numpy()) # Convert tensor to numpy array
  label_counts = pd.Series(label_list).value_counts(sort=True)

  print(label_counts)***

如果我们在训练集上运行它,我们可以看到图像相对均匀地分布在每个类中。

训练集中标签的值计数|按作者排序的图像

(三)图像示例

鉴于我们正在处理图像,直接将它们可视化总是一个好主意。

我们可以使用下面的自定义函数显示一个随机图像及其相应的标签,包括将标签从整数形式转换为实际的品种名称。

***# Obtain name for label integer
get_label_name = ds_info.features['label'].int2str

def view_single_image(ds):
  image, label = next(iter(ds)) # Get next image (random)
  print('Image shape: ', image.shape) # Get shape of image
  plt.imshow(image)
  _ = plt.title(get_label_name(label))***

来自数据集的单个随机图像|作者提供的图像

内置的show_examples方法也允许我们可视化图像的随机子集。

对于这个特定的数据集,我们需要指定image_key='image'来检索实际的图像,因为存在多个图像特征。

***tfds.show_examples(train_raw, ds_info, image_key='image')***

来自 tdfs.show_examples|作者图片的随机子集

步骤 4 —准备数据

下一步至关重要,因为它涉及在迁移学习发生之前将图像预处理成适当的格式。

(一)图像尺寸调整

深度学习模型期望输入图像具有相同的形状,以实现一致性、兼容性和训练效率。

鉴于我们的原始图像有不同的大小,我们需要调整它们的大小,以具有相同的长度和宽度。

***IMG_SIZE = 224

train_ds = train_raw.map(lambda x, y: (tf.image.resize(x, (IMG_SIZE, IMG_SIZE)), y))
val_ds = val_raw.map(lambda x, y: (tf.image.resize(x, (IMG_SIZE, IMG_SIZE)), y))
test_ds = test_raw.map(lambda x, y: (tf.image.resize(x, (IMG_SIZE, IMG_SIZE)), y))***

选择 224x224 的调整后分辨率是因为我们稍后将使用的预训练模型期望输入图像是这种特定的形状。

(二)标签的一次性编码

我们的数据集有 37 个用于多类图像分类的类。因此,我们对标签进行一次热编码,以获得每个类的长度为 37 的输出张量。

基于类别数量的一键编码输出张量图解|作者图片

***def one_hot_encode(image, label):
    label = tf.one_hot(label, num_classes)
    return image, label

train_ds = train_ds.map(one_hot_encode)
val_ds = val_ds.map(one_hot_encode)
test_ds = test_ds.map(one_hot_encode)***

这一步很重要,因为我们将使用分类准确性来衡量模型性能,计算预测与这些实际的一次性标签匹配的频率。

㈢图像增强

虽然迁移学习减少了所需的数据量,但良好的性能仍然需要足够高质量、数量和种类的数据集。

图像增强是一种通过生成原始图像的修改副本来人为增加训练集大小的技术。

为了执行增强,我们使用 Keras 预处理层 API。我们想要应用的每种图像增强都被定义为 Keras 序列类中的一个*。***

为简单起见,我们将仅对图像进行随机水平翻转,但请注意,大范围的增强是可用的。

***data_augmentation = keras.Sequential(
                [layers.RandomFlip('horizontal'), 
                #  layers.RandomRotation(factor=(-0.025, 0.025)),
                #  layers.RandomTranslation(height_factor=0.1, width_factor=0.1),
                #  layers.RandomContrast(factor=0.1),
                ])***

我们可以用下面的代码来查看增强的效果:

***for image, label in train_ds.take(1): # Iterate and get set of image and label from train_ds generator
    plt.figure(figsize=(10, 10))
    for i in range(4):  # Display augmented images in 2x2 grid
        ax = plt.subplot(2, 2, i+1)
        aug_img = data_augmentation(tf.expand_dims(image, axis=0))
        plt.imshow(aug_img[0].numpy().astype('uint8')) # Retrieve raw pixel value
        plt.title(get_label_name(int(label[0]))) # Get corresponding label
        plt.axis('off')***

说明水平翻转增强的影响|作者图片

*** ***

(四)批处理和预取

下一步是设置批处理和预取,这是优化模型训练效率的技术。

  • 批处理将训练数据的子集分组为小批,以便训练可以并行化(利用 GPU 硬件加速),同时保持准确的梯度估计。
  • 预取将模型计算与输入数据的获取重叠,允许在检索下一批图像时继续训练。

虽然批量大小是一个可以调整的超参数,但我们可以先将其设置为标准默认值 32

***BATCH_SIZE = 32

train_ds = train_ds.batch(batch_size=BATCH_SIZE, 
                          drop_remainder=True).prefetch(tf.data.AUTOTUNE)

val_ds = val_ds.batch(batch_size=BATCH_SIZE, 
                      drop_remainder=True).prefetch(tf.data.AUTOTUNE)

test_ds = test_ds.batch(batch_size=BATCH_SIZE, 
                        drop_remainder=True).prefetch(tf.data.AUTOTUNE)***

我们来看看上面批处理/预取代码的参数:

  • drop_remainder=True表示最后一批小于 32 的图像(由于分割不均)被丢弃。当模型依赖于具有相同外部尺寸的批次时,这很有用
  • prefetch()方法中的tf.data.AUTOTUNE意味着[tf.data](https://www.tensorflow.org/api_docs/python/tf/data)运行时将动态调整每个训练步骤要预取的元素数量

步骤 5 —建立模型

迁移学习背后的核心概念是利用预先训练的模型作为构建定制图像分类器的基础。

Keras 中提供了许多预训练的深度学习模型(以及预训练的权重),完整的列表可以在这里找到。

对于本教程,我们将使用 ResNet50V2 模型,因为它提供了模型大小、准确性、信誉和推理速度的完美平衡。

ResNet50 模型架构图|根据维基共享许可使用的图像

(一)设置基础模型

我们首先使用keras.applications.ResNet50V2从 Keras 应用程序加载 ResNet50V2 模型。

请注意,ResNet50V2 预训练模型是在 ImageNet 数据集上训练的,这是一个包含数百万张图像和数千个类的大规模图像数据集。

***base_model = keras.applications.ResNet50V2(
                        include_top=False, # Exclude ImageNet classifier at the top
                        weights='imagenet',
                        input_shape=(IMG_SIZE, IMG_SIZE, 3)
                        )***

以下是对所用参数的解释:

  • include_top:如果设置为 False,我们排除 ImageNet 分类器作为顶层。这很重要,因为我们想引入自己的顶层来分类宠物品种,而不是最初的 ImageNet 类
  • weights:通过将其设置为'imagenet',我们将使用在 ImageNet 上训练的预训练重量。这正是我们想要的,因为我们正在执行图像分类,从 ImageNet 学到的功能是相关的
  • input_shape:我们设置输入形状元组来匹配 ResNet 架构需要的,即(224, 224, 3)(其中3的最后一个元素代表颜色通道的数量)

(ii)冻结基础模型的预训练权重

一旦我们加载了预训练的模型,我们想要固定* ( 又名冻结 ) 层和权重,因为我们不想丢失已经学习的有价值的信息。***

***# Freeze base_model
base_model.trainable = False***

通过将trainable属性设置为False,可训练的重量变为不可训练,并且在训练过程中不会更新。一般来说,各层的所有重量都被认为是可训练的。

事实上,唯一具有不可训练权重的内置层是[BatchNormalization](https://www.tensorflow.org/api_docs/python/tf/keras/layers/BatchNormalization),因为它使用不可训练权重来跟踪训练期间输入的均值和方差。

㈢实例化和修改输入

在此步骤中,我们对输入(即图像)进行预处理,以符合预训练的 ResNet50v2 架构的预期。

我们首先使用keras.Input实例化一个 Keras 张量来表示输入图像的“对象结构”。

***# Setup inputs based on input image shape
inputs = keras.Input(shape=(IMG_SIZE, IMG_SIZE, 3))***

接下来,我们应用前面设置的数据扩充步骤,并将输出存储在一个表示新输入转换的新变量x中。

***x = data_augmentation(inputs)***

此外,Keras 中的每个预训练模型都需要特定类型的输入预处理,这是在早期数据准备之上的。

我们通过在将输入传递给模型之前对输入运行[resnet_v2.preprocess_input](https://www.tensorflow.org/api_docs/python/tf/keras/applications/resnet_v2/preprocess_input)来做到这一点。

***# Apply specific pre-processing function for ResNet v2
x = keras.applications.resnet_v2.preprocess_input(x)***

特别是对于 ResNet50 V2,预处理步骤将输入像素从范围[0,255]缩放到范围[-1,1],以便输入与 ResNet 架构兼容。

(iv)处理批量标准化层

BatchNormalization层稍微有点棘手,因为它有不可训练的权重来跟踪输入的均值和方差。

因为我们稍后将解冻整个模型(在步骤 8 —微调模型中),我们需要包含额外的代码来保持BatchNormalization层处于推理模式(即保持不可训练)。

***# Keep base model batch normalization layers in inference mode (instead of training mode)
x = base_model(x, training=False)***

注意,这一步是针对BatchNormalization层的,是在之前完成的基础模型冻结(base_model.trainable=False)之上。

如果这一步被省略,微调步骤将是一个烂摊子,因为不可训练的重量将被删除和撤消。

㈤重建顶层

现在我们已经整理了预训练模型的早期层,我们可以添加新的可训练层,以便我们的模型可以有效地学习分类我们想要的标签,即宠物品种。

***# Rebuild top layers
x = layers.GlobalAveragePooling2D()(x) # Average pooling operation
x = layers.BatchNormalization()(x) # Introduce batch norm
x = layers.Dropout(0.2)(x)  # Regularize with dropout

# Flattening to final layer - Dense classifier with 37 units (multi-class classification)
outputs = layers.Dense(num_classes, activation='softmax')(x)***

新图层包括以下内容:

  • 全球平均池
  • 批量归一化
  • 辍学(概率为 20%)
  • 最终输出密集层激活 softmax,输出维数为 37(反映 37 类宠物品种)

(vi)创建新的 Keras 模型对象

我们用前面定义的更新的输入和输出实例化一个新的 Keras 模型。

***# Instantiate final Keras model with updated inputs and outputs
model = keras.Model(inputs, outputs)***

我们可以使用model.summary()查看我们更新后的模型的结构:

模型摘要|作者图片

㈦编制模型

最后,我们通过编译模型进行最后的润色,这涉及到配置评估指标和随机梯度下降优化器等设置。

***model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.CategoricalCrossentropy(),
              metrics=[keras.metrics.CategoricalAccuracy()]
              )***

因为我们正在处理多类分类,所以使用分类交叉熵作为损失度量,使用分类准确度作为性能度量是有意义的。

我们还包括一个提前停止回调以避免在训练期间过度适应。

***earlystopping = callbacks.EarlyStopping(monitor='val_loss', 
                                        mode='min', 
                                        patience=5, 
                                        restore_best_weights=True)***

步骤 6-运行模型培训

随着我们模型的更新和编译,我们准备在 pet 图像数据集上训练它。

我们以 25 的历元计数开始,尽管当检测到过度拟合的迹象时,早期停止回调将有助于停止训练。

我们运行model.fit()来启动训练,并将训练输出存储在一个名为history的变量中。

***EPOCHS = 25

history = model.fit(train_ds, 
                    epochs=EPOCHS, 
                    validation_data=val_ds, 
                    verbose=1,
                    callbacks =[earlystopping])***

历元训练输出|作者图片

步骤 7 —评估模型

一旦培训完成,是时候了解我们的模型进展如何了。

㈠绘图训练和验证集

我们可以在增加的时期内,在训练集和验证集上绘制分类准确度。

***def plot_hist(hist):
    plt.plot(hist.history['categorical_accuracy'])
    plt.plot(hist.history['val_categorical_accuracy'])
    plt.title('Categorical accuracy')
    plt.ylabel('accuracy')
    plt.xlabel('epoch')
    plt.legend(['train', 'validation'], loc='upper left')
    plt.show()

plot_hist(history)***

各时期的训练和验证准确度分数|按作者分类的图像

我们从图中看到,只需要几个时期就可以过拟合训练数据集,并且在五个时期后验证精度没有提高。

(ii)对测试数据集进行预测和评估

为了评估测试集上的模型预测,我们使用model.evaluate()

***result = model.evaluate(test_ds)
dict(zip(model.metrics_names, result))***

来自模型|作者图片的评估结果

结果表明,分类准确率达到了惊人的数值 86.1%

在此基础上,我们通过从 ResNet50 V2 预训练模型建立用于图像分类的深度学习模型,完成了迁移学习。

步骤 8 —微调模型(可选)

作为可选步骤,我们可以通过解冻和重新训练模型中的所有权重来进一步优化模型。

这个过程被称为微调*,我们解冻所有或部分早期预训练层,并重新训练新数据的权重。***

虽然这种技术可以提高性能,但它有可能很快过度拟合。

微调的一个关键部分是使用非常低的学习率*(例如 0.00001)来重新训练模型。***

基本原理是这些预先训练的层已经学习了用于图像分类的有价值的特征。因此,低学习率确保模型仅进行细微的参数调整,而没有显著的中断。

在我们的例子中,我们通过将trainable属性设置为True来解冻整个模型的顶部 15 层,排除了我们需要保持冻结的BatchNormalization层。

***for layer in model.layers[-15:]:
    if not isinstance(layer, layers.BatchNormalization):
        layer.trainable = True***

在这些变化之后,我们需要重新编译模型,在这里我们将学习率从默认的 0.001 大幅降低到 0.00001

***model.compile(optimizer=keras.optimizers.Adam(learning_rate=1e-5), # Set a very low learning rate
              loss=keras.losses.CategoricalCrossentropy(),
              metrics=[keras.metrics.CategoricalAccuracy()]
              )***

我们重新训练模型,并在最后的步骤中检索评估指标。考虑到过度拟合的风险,我们在更少的时期内运行训练。

***EPOCHS = 5

history_2 = model.fit(train_ds, 
                      epochs=EPOCHS, 
                      validation_data=val_ds, 
                      verbose=1,
                      callbacks =[earlystopping])

result_2 = model.evaluate(test_ds)

dict(zip(model.metrics_names, result_2))***

微调模型的评估结果|作者提供的图片

上面的评估输出表明,微调方法有助于将分类准确率从的 86.1%提高到的 87.2%。

最后,为了检索测试集上的实际预测,我们可以使用model.predict()函数:

***preds = model.predict(test_ds)***

(4)包装

本演练涵盖了开始学习用于图像分类的迁移学习的实际步骤。

迁移学习,加上微调,是一种有效的技术,使我们能够在有限的时间和资源下建立鲁棒的图像分类器。

鉴于迁移学习的实用性,它成为行业中解决业务问题的一种普遍技术也就不足为奇了。

完整笔记本的链接可以在 这里 找到。

在你走之前

欢迎您加入我的数据科学学习之旅!点击此媒体页面,查看我的 GitHub ,了解更多令人兴奋的实用数据科学内容。同时,享受在 TensorFlow 中实现图像分类迁移学习的乐趣!

*** ***

加速材料科学研究的实用机器学习技术

原文:https://towardsdatascience.com/practical-machine-learning-techniques-to-accelerate-materials-science-research-9dc9f62f33e8

使用回归技术、特征选择和选择标准预测超导体的临界温度

美国公共电力协会在 Unsplash 上拍摄的照片

根据 EIA 的估计,由于输电线路的电阻损耗,美国能源网损失了大约 5%的电力。如果我们能找到一种方法来消除这一切呢?事实证明,有一种非常酷的材料叫做超导体——可以零电阻导电的材料。如果没有电阻,传输线就没有电阻损耗。我承认,对于超导现象究竟是如何发生的,我不是专家。我所知道的是,只有当给定的材料变得非常冷时,这种情况才会发生——我们说的是开尔文的个位数。在室温下,这些材料就像你的典型导体一样,只有在低于这个“临界温度”后,它们才会表现出这种超导特性。近年来,已经取得了进展,并且发现了在更合理的条件下工作的新材料。然而,“高温”超导体通常被认为是临界温度高于 77K 或液氮温度的材料。有了整个周期表,我们有没有办法使用机器学习技术来加速新材料的发现?虽然我们距离发现在传输线条件下工作的材料可能还有很长的路要走(更不用说为如此长的传输线找到一种经济有效的材料),但有一些有趣的 ML 技术可以应用于所有类型的材料科学研究。

在我讲得太远之前,你可以很容易地跟随我的代码,贴在我的 Github repo 上。那里展示了更多的情节和代码;本文中我只展示亮点。公众可以从 UCI 机器学习知识库[1]获得这些数据。我将在超导数据集上解决一个多元回归问题(可在 CC0:公共领域许可下获得)。Kam Hamidieh[2]发表的文章中有一些关于数据集的精彩工作,值得一读,因为它比我在这里或随附的笔记本中所做的更深入地了解了超导和所用的特性。好了,一切都结束了,让我们开始吧!

特征探索

我们从 81 个特征开始,我们想要预测材料的临界温度。此外,我们有化学公式,并能够使用一个名为chemparse的便捷包提取每个元素的计数。如果你想看一下单个的分布图,那就打开笔记本吧…有这么多的特性,有相当多的信息需要解开。另一件我们可以开始了解不同参数如何相互作用的事情是查看相关性和一些聚类。这些步骤对于很好地理解我们正在处理的数据尤其重要,尤其是在不熟悉的主题中。

超导体数据集中特征之间的相关性。作者的情节

从该图中,我们可以看到高度相关和高度不相关的特征(值 1 表示精确相关,-1 表示完全反向相关,0 表示不相关)。也许减少我们拥有的功能数量的第一个方法是删除高度相关的功能…但是我们现在会保留所有的功能,看看我们会得到什么样的初步结果。

要素的 t-SNE 聚类,用颜色表示临界温度范围。由作者策划。

这里显示的聚类是一种可视化高维数据的有用方法…81 个特征当然是高维的!不幸的是,至少在最初的检查中,星团和临界温度范围之间没有明确的关联。

模型准备

我们总是可以添加更多从原始特征中提取的特征(例如,通过平方或对数缩放特征,或者寻找特征之间的比率)。然而,在提取原子计数之后,我们已经有 150 多个特征来训练模型。然而,我们还想做最后一个转变。我们希望预测对数转换值,而不是将目标设定为临界温度。虽然在大多数情况下这并不是绝对必要的,但对于我们的临界温度目标值来说,这是有意义的,因为我们无法在物理上低于 0 K。对数标度确保预测值将始终为正值。让我们快速地看一下我们的目标分布是如何随着这个日志转换而变化的:

初始临界温度和对数标度临界温度的分布。由作者策划。

有了所有这些预备工作,我们接下来可以快速地通过模型准备的典型阶段,例如定义我们的特性和目标,执行训练/测试分割,以及缩放。

*# Define target and potential features*
target **=** 'critical_temp'
features **=** list(df**.**columns)
features**.**remove(target)
features**.**remove('material')

*# Input and output values*
X **=** df[features]
y **=** np**.**log10(df[target]) *# log scale forces values to always be positive and non-zero*

*# Train/test split*
X_train_unscaled, X_test_unscaled, y_train, y_test **=** train_test_split(X, y, test_size**=**0.2, shuffle**=True**)

*# Scale*
s **=** MinMaxScaler()
s**.**fit(X_train_unscaled)
X_train **=** s**.**transform(X_train_unscaled)
X_test **=** s**.**transform(X_test_unscaled)

回归模型

我们将查看线性回归模型的 3 种不同变化,然后比较梯度推进模型的性能。从一个简单的回归模型开始,我们启动、训练和预测,然后存储我们的预测供以后比较,并绘制结果。

*# Initiate and fit linear regression model*
lr **=** LinearRegression()
lr**.**fit(X_train, y_train)

*# Predict on test data*
y_pred **=** lr**.**predict(X_test)

线性回归结果。由作者策划。

正如我们所看到的,预测值和真实值之间肯定存在一般的相关性,但是 R 值相当差。我们可以尝试一个套索或脊模型,以帮助任何可能发生的过度拟合,并获得一些关于一些特征重要性的见解。我们将展示岭回归的代码,并查看套索回归的笔记本。

*# Range of alphas (regularization strength)*
alphas **=** np**.**logspace(**-**4,3,20)
*# dataframe to store coefficients*
ridge_coefs **=** pd**.**DataFrame(index**=**alphas,columns**=**features)
*# list to store mse*
ridge_mse **=** []

**for** alpha **in** tqdm(alphas):
    *# Initiate and fit model with given alpha*
    ridge **=** Ridge(alpha**=**alpha)
    ridge**.**fit(X_train, y_train)

    *# Save coefficients*
    ridge_coefs**.**loc[alpha] **=** ridge**.**coef_

    *# Predict on test data*
    y_pred **=** ridge**.**predict(X_test)

通过回归,我们希望稳定地增加正则项。随着正则项的增加,每个输入特征的权重向 0 递减。这个概念主要用于防止过度拟合,但是,只要我们对输入数据进行了缩放,我们也能够根据权重的绝对值来查看相对特征重要性。我在 Jupyter 笔记本中有一个很好的交互式绘图,您可以将鼠标悬停在线上来查看显示的功能。下面,我们可以看到基于正则化强度的性能,以及实际性能。

基于岭回归正则化强度的预测精度:请注意,最佳性能约为 0.1。由作者策划。

最优岭正则化的线性回归结果。由作者策划。

不幸的是,结果看起来并没有好到哪里去,我们也没有看到比简单的线性回归更好的结果。然而,在我们继续之前,一定要在笔记本上用套索回归检查一下重量是如何变化的——你会看到一些有趣的趋势!(思考问题:当你使用 150 多种功能时,最重要的功能是什么?如果你只有 5 个特征,最重要的是什么?)

Lasso 回归基于正则化强度的要素权重交互式绘图的屏幕截图。请注意基于正则化强度改变权重的有趣行为。由作者策划。

虽然我们通过查看特征权重获得了一些关于特征重要性的有趣见解,但是让我们转向一个更复杂的模型:梯度增强(我们特别喜欢 XGBoost)。我特别喜欢这个模型,不仅因为它在应用程序中的出色性能,而且它也是在我的母校华盛顿大学提出和开发的(见出版物此处)。

*# Initialize and fit model*
xgb **=** XGBRegressor()
xgb**.**fit(X_train, y_train)

*# Make and store predictions*
y_pred **=** xgb**.**predict(X_test)
predictions['xgb_raw'] **=** 10******y_pred

以下是 XGBoost 模型的结果:

XGBoost 模型结果。由作者策划。

总有改进的空间,但鉴于这是一个开箱即用的模型,它看起来更有前途。我们将继续下去,并对其进行迭代。

特征选择

基于各种正则化强度的权重,Lasso 和 Ridge 模型给出了要素重要性的良好初步指示。然而,这些模型仍然表现不佳。XGBRegressor 远远优于任何其他回归算法。有许多选项可以用来查看特征的重要性,但我们将使用的是递归特征消除(RFE)。这将针对所有输入要素训练指定的模型(在本例中为 XGBRegressor ),并显示结果预测性能,然后迭代移除最不重要的要素,并针对要素子集重新训练模型。理想情况下,我们会做交叉验证 RFE,但这种方法已经在计算上很昂贵,所以现在我们将只使用常规 RFE。

# Define step size and search space
step = 5
max_features = X_train.shape[1]
min_features = 5
search_space = range(min_features,max_features,step)
# List to store MSE
rmse = []

for n_features in tqdm(search_space):
    # RFE to select n_features
    selector = RFE(estimator = XGBRegressor(), 
                   n_features_to_select = n_features,
                   step = step)
    selector.fit(X_train, y_train)
    # Predict on feature subset and store prediction
    y_pred = selector.predict(X_test) rmse.append(mean_squared_error(10**y_pred,10**y_test,
                                   squared=False))

如果我们想获得更详细的信息,我们可以改变步骤,这样在每次迭代中只有一个特征被删除,如果我们做交叉验证,我们可以获得统计数据…但是每一个都增加了计算时间。就我们的目的而言,看到一般趋势就足够了:

基于要素数量的预测 RMSE,由递归要素消除确定。作者的情节

正如我们所看到的,在大约 20 个功能之后,我们在性能上没有得到太大的改善。我们可以很容易地看到特性排名并打印出来(查看完整打印输出的代码)。你注意到了哪些有趣的观察结果?

# Display the rankings
rankings = selector.ranking_
for rank in range(1,max(rankings)):
    print(rank)
    features_index = [i for i, x in enumerate(rankings) if x == rank]
    features_list = [features[i] for i in features_index]
    print(features_list)>>> 1
['gmean_Density', 'wtd_gmean_ElectronAffinity', 'wtd_gmean_ThermalConductivity', 'range_ThermalConductivity', 'wtd_range_ThermalConductivity', 'wtd_mean_Valence', 'wtd_gmean_Valence', 'entropy_Valence', 'wtd_std_Valence', 'Ba', 'Cu', 'Ca', 'Ti', 'Fe', 'Zn', 'Pr', 'Nb', 'Ce', 'Mg', 'Pd']
2
['mean_Density', 'Gd', 'Ni']
3
['range_ElectronAffinity', 'Bi', 'Sn', 'Si', 'Tc']
...
...
...
27
['mean_atomic_mass', 'std_Valence', 'Er', 'H', 'Tb']
28
['number_of_elements', 'Ag', 'I', 'B', 'Be']

超参数优化

使用我们的 20 个最重要特征的子集,我们现在可以执行最佳模型的超参数优化。回想一下,我们在最初的结果中使用了开箱即用的模型。顺便提一下,我们可能会将特征选择与超参数优化结合在一起,事实上,这对于最终模型来说是可取的,但这在计算上要昂贵得多。

我们将使用hyperopt包对搜索空间进行贝叶斯优化。我之前写过一篇关于它的文章,你可以在这里找到,所以我就不赘述了。下面是一些重要的代码片段,用来定义我们的超参数搜索空间,建立我们的模型,并运行测试:

# Define search space for the hyperparameters of interest
space = {'eta' : hp.uniform('eta',0.01,0.3),
         'gamma' : scope.int(hp.quniform('gamma',0,100,1)),
         'max_depth' : scope.int(hp.quniform('max_depth',2,15,1)),
         'subsample' : hp.uniform('subsample',0.5,1.0),
         'lambda' : hp.loguniform('lambda', -3, 3),
         'colsample_bytree' : hp.uniform('colsample_bytree',0.5,1.0)
        }def xgboost(params):
    # Initialize model with hyperparameters
    xgb = XGBRegressor(eta = params['eta'],
                       gamma = params['gamma'],
                       max_depth = params['max_depth'],
                       subsample = params['subsample'],
                       reg_lambda = params['lambda'],
                       colsample_bytree = params['colsample_bytree']
                      )

    # Split into validation set
    X_train_hyper, X_val, y_train_hyper, y_val = train_test_split(
                 X_train_sub, y_train, test_size=0.2, shuffle=True)

    # Fit
    xgb.fit(X_train_hyper, y_train_hyper)

    # Predict on validation set
    y_pred = xgb.predict(X_val)

    # calculate validation loss
    validation_loss = mean_squared_error(10**y_pred, 10**y_val, 
                                         squared=False)

    return {'loss': validation_loss, 'status': STATUS_OK, 
            'model': xgb, 'params': params}
##################################################################### Initiate trials and start hyperparameter search space for 250   
        iterations
trials = Trials()
best = fmin(xgboost, space, algo=tpe.suggest, max_evals=250, trials=trials)# Find the best model and set of hyperparameters
best_model = trials.results[np.argmin([r['loss'] for r in trials.results])]['model']
best_params = trials.results[np.argmin([r['loss'] for r in trials.results])]['params']

真正有趣的是,我们可以根据给定的超参数绘制结果。在某些情况下,我们可以清楚地看到性能和所选超参数值之间的相关性(例如,低伽马和较高 eta 是理想的)。有关这些超参数的深入描述,请参见该来源。

基于超参数值的预测临界温度的 MSE 值。橙色值是最佳值。由作者策划。

在这一点上,我们已经尝试了 6 种不同类型的模型:线性回归,以及 Ridge 和 Lasso 变体,以及使用所有 150 多个特征的现成 XGBoost,使用 20 个特征的最优子集的现成 XGBoost,以及使用 20 个特征的最优子集和超参数优化的 XGBoost 模型(值得一提的是,该图还包括具有最差超参数配置的 XGBoost 模型,这样您就可以看到

RMSE 对各类模特的表演训练有素。作者的情节

识别高性能材料

此时,我们有了一个模型,可以用来预测临界温度。重要的是,我们从 150 多种可能性中确定了 20 种最重要的特征,因此当我们获得新材料时,我们不需要在对其性能有一个好主意之前进行无休止的测量。仅此一项就能节省大量的时间和金钱。但是我们是否也可以利用我们的知识来开发我们可能想要尝试的新材料呢?有无穷无尽的新材料可以探索,但大多数都无助于我们朝着更高临界温度超导体的目标努力。

我正在构建的场景是,我们有大约 200 种潜在的材料,我们希望快速找到具有高临界温度的材料,高于某个阈值(假设我们只对临界温度> 100K 的材料感兴趣)。我们可以随机开始尝试材料,或者创建某种选择函数来寻找最有可能实现高性能的样本。

我开始研究一些聚类算法,但没有一个算法能有效地实现我的目标。所以我想出了一个“伪 k 均值”选择标准。k-means 是一种聚类算法,它基于相似样本的特征和相似性度量来查找相似样本。不幸的是,大多数 k-means 实现使用欧几里德距离,这遭受了维数灾难。即使我们将输入特征从 158 个减少到 20 个,它仍然是相当高维的。虽然余弦相似性在高维度上也有问题(特别是对于稀疏数据),但它往往更好。因此,该方法是根据与其他已知高性能材料的余弦相似性来选择性能最高的材料。根据本文中的工作流程,我们接着对样本进行评估,如果符合标准,我们就完成了;否则,我们将样本添加到数据集并重新训练模型。然后,我们可以评估找到具有所需临界温度的样品需要多长时间。

我承认,这里的方法需要做很多工作。我最想尝试一下,也许在某个时候我可以改进它。现在我们有了一个选择函数,所以是时候看看它与随机抽样相比表现如何了。我们在不同的子集上尝试这个场景 50 次,这样我们就有了统计数据。理想情况下,我们希望我们的选择标准能够帮助我们在更少的尝试中找到高性能的材料。

使用余弦相似性搜索标准和随机搜索,在找到临界温度高于给定阈值(100 K)的材料之前尝试的样本数量。由作者策划。

我们确实看到余弦相似性度量有助于我们更快地找到高性能样本,因为较低的值具有较高的峰值。然而,显然还有改进的余地。也许如果我们把门槛设得高一些,我们会看到更大的进步。这是一个非常新的想法,我正在探索,但是,我真的很有兴趣听听你是否见过或做过这些方面的事情,以及它是如何为你工作的!

结论

这里面有很多东西,我希望它能给你在你自己的项目或工作中一些想法。虽然机器学习经常被用作进行预测的“黑盒”解决方案,但我们可以利用这些原则和技术在研究实验室或材料科学发展中做出明智的决策。这有几个方面,包括特征选择和重要性排序以消除不必要的测量,以及聚类策略以帮助确定潜在的高性能候选。这些方法可以帮助我们节省时间和金钱,因为我们继续探索我们周围的广阔世界的持续可能性。感谢你阅读这篇文章——我很想听听你对这个话题的想法和主意!请随时在 LinkedIn 上联系我。

参考

[1]Dua d .和 Graff c .(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院。

[2] Hamidieh,Kam,预测超导体临界温度的数据驱动统计模型,计算材料科学,第 154 卷,2018 年 11 月,第 346–354 页

Python 基础:数据科学家实用技巧

原文:https://towardsdatascience.com/practical-python-essentials-for-data-scientists-d97fcc6ed53

在不太遥远的将来,你完全有可能成为一名拥有基本(或没有)编程技能的数据分析师或 ML 从业者。不过,就目前而言,扎实的编码能力对于任何从事数据工作的人来说仍然是一笔宝贵的财富,无论是在工业界还是学术界 Python 仍然是数据专业人员事实上的通用语言。

本周,我们将重点介绍几篇突出的帖子,这些帖子关注 Python 在数据科学任务中的实用方面。让我们进入节目吧(明)!

  • 碰到编码墙 怎么办。高说,编程时“没有明确的目标,很容易掉进兔子洞,浪费时间”。她的详细指南介绍了在 Python 工作流中寻找和解决瓶颈的战略方法,一直到优化输出。
  • 提高自己作为程序员的解题技巧 。您编写代码来解决特定于上下文的挑战——无论是部署模型、生成可视化还是分析结果。Kurtis Pykes 的最新文章采用了一个简单的例子,并用它来勾勒出一个创建有效的、集中的代码的路线图。

Leone Venter 在 Unsplash 上拍照

  • 建立坚实的 Python 基础永远不会太晚,也不会太早 。无论你是编程新手还是老手, Philip Wilkinson 关于字典的扎实讲解,Python 内置的基本数据结构,是你随身携带的有用资源。
  • 掌握版本控制的要领 。如果你对 GitHub 的介绍是偶然的,或者仅限于特定的用例,你会欣赏 Vatsal 耐心的、实际操作的广泛使用的平台纲要——它是专门面向数据科学家的。

既然我们已经看够了 Python,让我们转向其他一些话题。在过去的几周里,我们有幸发表了一些优秀的、内容丰富的文章;这里有一些你真的不应该错过的。

  • 想要获得有益的灵感,请前往艾伦·图灵研究所应用研究中心的数据科学家瓦什塔·谢尔博士最近的问答环节。对话涵盖了谢尔博士的职业道路、研究兴趣和写作策略。
  • 你在市场上的 NLP 深潜?留出 15 分钟的时间来阅读纳安妮塔·罗伊的最新作品,她研究了单词嵌入在文本分类模型中的作用和影响。
  • 看到其他学科的专家如何处理数据科学相关的话题总是很有启发性的。理论物理学家出身的数据科学家 Tim Lou 博士将这两个领域联系起来,探索机器学习的热力学。
  • 聊天机器人可能是大型语言模型在现实世界中的一个普遍应用,但是正如泰斯·德·科克指出的那样,基于提示的 GPT 模型对于其他一系列下游的自然语言处理任务来说是很有用的。
  • Weronika Gawarska-Tywonek 继续她的关于在数据可视化中深思熟虑地使用颜色的系列文章——最新的一期涵盖了连续调色板的本质。

如果你正在寻找一种方式来支持你最喜欢的 TDS 作者,考虑成为媒体成员。我们感谢你支持我们工作的所有方式,无论是阅读、分享还是与我们发布的文章互动。

直到下一个变量,

TDS 编辑

用实用程序破解 SQL 面试问题

原文:https://towardsdatascience.com/practical-sql-questions-for-data-science-interview-3b5c9d726baa

逐步解决 SQL 面试问题

Andrea Davis 在 Unsplash 上拍摄的照片

在本文中,我们将回顾 Airbnb 数据科学访谈中的一个 SQL 问题。我希望本文中解释的过程能够帮助您更有效地编写 SQL 查询。

SQL 问题:

**Host Popularity Rental Prices**You’re given a table of rental property searches by users. The table consists of search results and outputs host information for searchers. Find the minimum, average, maximum rental prices for each host’s popularity rating. The host’s popularity rating is defined as below: 
0 reviews: New 
1 to 5 reviews: Rising 
6 to 15 reviews: Trending Up 
16 to 40 reviews: Popular 
more than 40 reviews: HotSource: [stratascratch.com](https://platform.stratascratch.com/coding/9632-host-popularity-rental-prices?code_type=1)

表格:airbnb _ 主机 _ 搜索

作者图片

第一步:在我们开始编写查询之前,我们需要检查原始数据并提出问题,以便更好地理解相关信息。这不是大学考试,我们可以向面试官或数据制作者提出任何合理的问题和假设。比如,

  • 数据集是否包含重复的属性?我们如何唯一地识别一个属性?我们可以使用字段“ID”来唯一地标识一个属性吗?在本练习中,ID 表示搜索 ID,它与属性 ID 无关,因此,在本练习中我们可以忽略该字段。是的,数据集中有重复的属性,因为用户可能会从不同的搜索中查看相同的属性,并且所有搜索输出(包括重复的)都将记录在原始数据中。我们需要向面试官提出重复的问题,因为包括重复会产生一个扭曲的结果。所以我们需要想办法自己去鉴定一个房产(下一步会多讲)。
  • 这个问题似乎表明,主持人的受欢迎程度是根据评论的数量来定义的。原始数据是否包括给定物业的历史和最近评论数?如果是这样,我们需要澄清如何确定最近的审查数量。例如,“搜索日期”是否可用?但是对于这个练习,数据集只包含一个属性最近的评论数量,因此,我们不需要担心找到最近的评论数量。

步骤 2 :并非原始数据中的所有字段都会被使用。我们需要确定相关变量和任何额外的变量,我们需要自己创建这些变量来解决问题。在这个练习中,我们需要以下变量。

  • 属性 ID:我们需要找到唯一标识属性的方法。一种连接几个特征变量的常见方法,如价格、房间类型、host_since、邮政编码和评论数量。通过组合不同的字段,我们应该能够唯一地标识一个属性。
  • 主持人的受欢迎程度评级:根据评论的数量,酒店评级分为“新”、“上升”、“上升趋势”、“受欢迎”和“热门”。
  • 价格:这个变量在原始数据中很容易得到。基于原始价格信息,我们可以计算给定流行度评级类别的最小值、平均值和最大值。

第三步:我们需要准备数据,为分析做好准备。在这一步中,我们将专注于保存和构建我们在第 2 步中确定的变量。

我们可以使用CONCAT(price, room_type, host_since, zipcode, number_of_reviews)来创建属性 ID。

主持人的受欢迎程度评级需要做一些工作,因为它将根据评论的数量被分配不同的值(即,“新”、“上升”、“趋势上升”、“受欢迎”和“热门”)。CAEE-WHEN-END函数是处理多个条件语句的完美函数。

原始数据中存在重复数据,我们需要通过删除重复观测值来准备数据,否则,在计算平均值、最小值和最大值时,我们会得到不准确的结果。增加DISTINCT可以解决这个问题。

我们可以运行下面的查询来准备数据。

SELECT **DISTINCT** CONCAT(price, room_type, host_since, zipcode, number_of_reviews) AS **id**, **price**,CASEWHEN number_of_reviews = 0 THEN 'New'WHEN number_of_reviews >=1  AND number_of_reviews <=5  THEN 'Rising'WHEN number_of_reviews >=6  AND number_of_reviews <=15 THEN 'Trending Up'WHEN number_of_reviews >=16 AND number_of_reviews <=40 THEN 'Popular'WHEN number_of_reviews >40 THEN 'Hot'END AS **popularity_rating**FROM airbnb_host_searches

我们期望得到的初步数据集类似于下表。我们有一个物业 ID,它代表一个独特的物业及其租赁价格和受欢迎程度。

作者图片

第四步:找到编写查询的最有效方法。子查询和公用表表达式(CTE)都是处理复杂 SQL 查询的有用工具。我发现 CTE 比子查询更有效,更有优势。

根据设计,CTE 是可重复使用的。不必在需要使用子查询的每个地方都声明相同的子查询,可以使用 CTE 定义一次临时表,然后在需要时引用它。

CTE 比子查询更具可读性。因为 CTE 可以重用,所以使用 CTE 比使用子查询可以编写更少的代码。此外,人们倾向于按照顺序而不是嵌套的方式更容易地遵循逻辑和思想。编写查询时,使用 CTE 可以更容易地将复杂的查询分解成较小的部分。

对于本练习,我们可以使用WITH语句准备步骤 3 中的数据,并将其存储在一个名为 cte 的临时表中。然后,我们将使用 SQL 聚合函数、MIN()AVG()MAX()来计算带有GROUP BY的每个人气评级类别的最低、平均和最高租赁价格。

最终的解决方案如下所示:

WITH cte AS(SELECT DISTINCT CONCAT(price, room_type, host_since, zipcode, number_of_reviews) AS id, price,CASEWHEN number_of_reviews = 0 THEN 'New'WHEN number_of_reviews >=1  AND number_of_reviews <=5  THEN 'Rising'WHEN number_of_reviews >=6  AND number_of_reviews <=15 THEN 'Trending Up'WHEN number_of_reviews >=16 AND number_of_reviews <=40 THEN 'Popular'WHEN number_of_reviews >40 THEN 'Hot'END AS popularity_ratingFROM airbnb_host_searches)SELECT popularity_rating,MIN(price) price_min,AVG(price) price_avg,MAX(price) price_maxFROM cteGROUP BY popularity_rating

该查询将产生答案:

作者图片

解决 SQL 问题类似于侦探工作。你被测试把分散的拼图拼在一起,还需要逻辑合理地解释步骤。这里的关键是让面试官或你的同事参与到这个过程中来,这样他们就能理解你的观点和你做出的任何假设。

如果你想探索更多的 SQL 面试问题,请查看我的文章:

  • 综合 SQL 小抄
  • 破解 SQL 面试问题的有用程序
  • 破解 SQL 面试问题:子查询 vs CTE
  • 破解 SQL 面试问题:Join vs Case-When 语句
  • 破解 SQL 面试题:带分区的窗口函数——By
  • 破解 SQL 面试问题:Date_Part 函数
  • 破解 SQL 面试题:ROW_NUMBER、RANK 和 DENSE_RANK
  • 破解 SQL 面试问题:UNNEST,STRING_TO_ARRAY
  • 破解 SQL 面试问题:GENERATE_SERIES,STRING_AGG,SPLIT_PART
  • 破解 SQL 面试问题:自连接和非等同连接
  • 破解 SQL 面试问题:任意运算符
  • 破解 SQL 面试问题:子查询

感谢您的阅读!!!

如果你喜欢这篇文章,并且想**请我喝杯咖啡,**请点击这里。

您可以注册一个 会员 来解锁我的文章的全部访问权限,并且可以无限制地访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请订阅。

Power BI 的预计算聚合—为什么要避免它们

原文:https://towardsdatascience.com/pre-calculated-aggregations-for-power-bi-why-should-you-avoid-them-371abdec5bb4

我的一个客户总是希望在他的 Excel 文件中预先计算聚合,然后在 Power BI 中使用。以下是我们应该避免这样做的原因。

由万花筒在 Unsplash 上拍摄的照片

介绍

不久前,我开始为一个新客户工作。

我的任务之一是向他们咨询如何使用 Power BI 和解决挑战。

他们大部分时间从应用程序或其他数据库导出 Excel 和 CSV 数据。我的客户决定获取聚合数据,以避免大型数据集并简化他的 DAX 计算。

事实证明,这个决定并不那么好。

什么是预聚合

我的客户开始从他的源系统提取数据到 Excel 文件中。

接下来,他开始使用 Excel 宏将数据预聚合为 YTD 和 YE 值。或者,他从一开始就以汇总的方式导出数据。

之后,他将汇总的数据导入 Power BI,并开始设计报告。

但是,我不得不说,他的大多数数据都是快照数据,他只需选择一个快照就可以看到所需的结果。

另一种预汇总数据的方法是消除数据中的细节。

例如,查看 Contoso 数据集的以下数据模型:

图 Contoso 数据集的互联网销售部分(图片由作者提供)

现在,假设我对每个客户的销售细节不感兴趣,而只对每个地理区域感兴趣。

我可以从客户表中删除客户属性,只保留地理信息,如国家、地区、城市、邮政编码等。

通过删除客户属性,我可以将在线销售表中的数据聚合到邮政编码级别,并减少数据量。

结果,我丢失了所有客户的详细信息。

预聚集的缺点

在大多数情况下,我的客户的方法满足了他的要求。

但是,一旦他想做一些时间智能或其他复杂的计算,他就必须在 Excel 文件中添加越来越多的复杂性来满足需求。

当您预汇总数据时,您必须考虑所有需要的维度。维度越多,可以聚合的数据就越少。

此外,当您必须添加一个维度或增加粒度(添加更多细节)时,您必须重新开始并重新计算所有的聚合。

预聚合的优势

预聚合数据的主要好处是减少数据量。我在上面的例子中通过消除客户的详细属性展示了这种效果。

但这仅在原始数据包含数千万或数亿行时才有意义。

预聚合几千行数据是没有意义的,因为 power BI 认为即使是几十万行也是小数据。

我会考虑 Power BI 中的聚合表特性,而不是手动预聚合我的数据。

在我看来,手动预聚合数据并没有真正的好处。

DAX 指标的复杂性

我的客户抱怨在处理原始数据而不是预汇总数据时测量的复杂性。

然后,发生了一件事。

我们开始处理一个新的报告,它有许多表格,但是仍然有少量的数据(几千行)。

他开始在 Excel 中创建预先聚合和预先计算的表格,以保持 DAX 指标的简单性。

他创建了具有月度年末(YE)和年初至今(YTD)值的列,并要求我开发需求所要求的所有 DAX 度量。

因此,我不能用一个简单的 SUM()函数来创建度量。我必须开发股票度量来获得数据的最近一年或年初至今的值。

您可以阅读以下文章,了解使用股票(半累加性)度量时会发生什么:

现在,查看以下度量,从预聚合数据中获取最新的 YTD 行:

Value =
 VAR LastDateOfData = LASTNONBLANK (SUM(YTD_ValueColumn)
 ,’SourceTable’[DateColumn])RETURN
 CALCULATE (SUM(YTD_ValueColumn)
 ,LastDateOfData
 )

然后,看看这个衡量标准,如果我有访问原始数据的权限,我会使用它:

Value = TOTALYTD(SUM(ValueColumn)
 ,’Date’[Date])

如您所见,从原始数据计算 YTD 比从数据中检索最后一个预聚合值要容易得多。

我说服了我的客户不要预先汇总数据,用这个论点来处理原始数据。

日期的问题

使用预聚合数据时还有另一个问题。

在我的例子中,我的客户通过按天消除粒度来计算聚合。

他总是将数据汇总到月或年级别。

平心而论,他的一些数据是预测数据,不是日常才有的。

我们必须使用时间智能和与时间相关的报告,并且我们必须确保所有行都指向每月相同的日期。例如,我们将所有数据设置为每月的第一天。

幸运的是,当您转换一个月以来的列时,Power BI 会自动完成这项工作。

例如:

数据看起来是这样的:2022–04

当您将这样的列转换为日期数据类型时,结果是:2022–04–01

听起来很容易。

但是,当我的客户准备一些数据指向这个月的最后一天时,事情变得复杂了。

我不得不返回到 Power Query,将这些列上的日期更改为所有表的同一天。

同样,当我们使用原始数据时,这种复杂性是不必要的。

结论

如果您很难将原始数据存储在 Power BI 或其他具有内存数据存储的报告工具中,那么预聚合数据可能是一个好主意。

在这种情况下,预聚合数据可能是提高报表性能或能够将数据存储在数据模型中的有效方法。

在任何情况下,我都强烈建议考虑日期因素,将数据保存在一天的级别上,或者在每个月的同一天汇总所有数据。

这种方法将允许您编写更简单的时间智能度量。

如果你对数据量没有问题,我建议避免预先汇总你的数据。

在处理原始数据时,您将获得更直接、更简单的测量方法。

埃利斯·陈嘉炜在 Unsplash 上拍摄的照片

参考

我使用 Contoso 样本数据集,就像我以前的文章一样。你可以从微软这里免费下载 ContosoRetailDW 数据集。

Contoso 数据可以在 MIT 许可下自由使用,如这里的所述。

https://medium.com/@salvatorecagliari/membership

如何学习精确和回忆的定义(永久)

原文:https://towardsdatascience.com/precision-and-recall-88a3776c8007

为什么我总是记不住这些的意思?!

奥利弗·布赫曼在 Unsplash 上拍摄的照片

介绍

也许你是数据科学的新手,或者像我一样,你已经有了一些数据科学的经验和 T2 糟糕的记忆。想象一下…你正在训练模型,运行实验,并准备进行部署。你身边有一个产品团队,他们开发了一个新的应用程序功能,你希望他们将新的分类模型集成到下一个版本中。你已经分享了你的一些指标。然后灾难来袭;有人问你什么是精度和召回……

总有一些术语和概念我根本记不住。 精度召回就是很好的例子。这篇文章主要是让我最终学会这些定义,但希望你能从我糟糕的记忆力中受益!

本文涵盖了学习和交流这些概念的不同方法。为了从这篇文章中获得最大的收获,您应该有一些对简单分类模型进行训练和评分的经验。

目录

  • 从混淆矩阵开始
    • 提醒计算
  • 作为一个句子
  • 打个比方
  • 把它放在上下文中
  • 再来几个视觉效果
  • 结论

从混淆矩阵开始

不管你最终用什么方法来解释或记忆精度回忆,一个必不可少的起点是混淆矩阵。

标准混淆矩阵。作者图片

计算的提示

我们现在可以写出与上述矩阵有关的计算结果。

精度

精度的计算方法是将真阳性除以任何被预测为阳性的值。

召回

召回(或真阳性率)是通过将真阳性除以任何本应被预测为阳性的值来计算的。

假阳性率

为了完整起见,我们还来看看假阳性率的计算。

通过圈出相关字段,我们可以直观地将它们与混淆矩阵联系起来。

我们现在已经定义了精度召回,并将它们关联回混淆矩阵。在这一点上,我已经解释了这些指标,并开始用一些可视化的方法来记忆它们。现在让我们明确地看一下这些术语,如果你不是视觉学习者,这可能会有帮助。

作为一个句子

Deepai.org 的有很好的定义,我已经改编并引用在下面。

我们也可以把这些问题框起来,我发现这是记忆这些问题的一种特别有用的方式。

精确

模型检索的个实例中与相关的实例的数量。

有多少检索到的项目是相关的?

回忆

在全部相关实例中,模型正确识别为相关的实例数量。

检索到多少个相关项目?

类比

当向非技术用户解释概念时,类比总是一种有用的方法。对一些人来说,这也是记忆一些东西的好方法。Reddit 用户 u/question_23 在这篇文章中以一种非常好的方式使用了一个关于精确度和召回率的常见类比,我将它转述如下:

像用网捕鱼一样解释。你用一张大网,在一个湖里捕 100 条鱼中的 80 条。这是 80%的召回率。但是你也能在你的网中得到 80 块石头。这意味着 50%的精度,一半的网络内容是垃圾。

你可以用一个小一点的网,瞄准湖中有很多鱼但没有石头的地方,但是你可能为了得到 0 块石头只能得到 20 条鱼。也就是 20%的召回率和 100%的准确率。

将它放入上下文中

充分理解一个主题有助于记忆,让我们来看看一些与精确度和回忆有关的常见情况。

计算

假设您正试图预测客户流失,使用分类模型和一些数据。您已经训练了模型,并根据测试数据集进行了一些预测。这些是结果:

客户流失模型的结果。图片作者。

精度 = 300/(300+15) = 95.2%

回忆 = 300/(300+35) = 89.6%

百分百是什么样子的?

从上面继续,这两个指标的 100%会是什么样子?

**100%精度:**无假阳性,每一个阳性预测都是正确的。

**100%召回:**没有假阴性,每一个阴性预测都是正确的。

与其他通用指标的关系

F1 得分

F1 分数只是精确度和回忆的调和平均值。我们也可以从真正的正面和负面来考虑这个问题。

F1 得分公式。图片作者。

ROC AUC

ROC(接收器操作特性)曲线是可视化分类模型性能的一种很好的方式,曲线下面积(AUC)是一种非常常用的度量,用于比较不同的机器学习模型。

为了绘制 ROC 曲线,我们绘制了不同分类概率阈值的真阳性率对假阳性率(例如,> 50% = 对> 90%= )。

记住,真正的阳性率和回忆是一样的。

何时使用这些指标?

neptune.ai 的这篇博客文章对何时使用这些不同的指标进行了很好的讨论。

https://neptune.ai/blog/f1-score-accuracy-roc-auc-pr-auc

再来几张图片

我还在网上搜索了一些其他的好方法来形象化这些概念。希望这些真的能说明问题。

维基百科使用圆形、方形和颜色进行了很好的可视化(注意本文中特异性和精确性的区别):

https://en.wikipedia.org/wiki/Sensitivity_and_specificity

这篇中型文章也有一些非常好的文氏图:

https://medium.com/@klintcho/explaining-precision-and-recall-c770eb9c69e9

这也是一个很好的例子和解释

https://jamesmccaffrey.wordpress.com/2016/10/24/precision-and-recall-in-information-retrieval/

结论

这篇文章的目的是让我最终记住精确和回忆,老实说,我认为这很有帮助。看看这些定义、问题和观想,看看是什么帮助你在大脑中巩固这些知识。

有没有什么机器学习概念是你永远记不住,但应该记住的?请在评论中告诉我!

了解更多信息

精确度和召回率——包含实例的综合指南

原文:https://towardsdatascience.com/precision-and-recall-a-comprehensive-guide-with-practical-examples-71d614e3fc43

所有你需要知道的关于准确性,精确性,回忆,F 分数,班级不平衡和混乱矩阵

在 Unsplash 上由 Fer Troulik 拍摄的照片

假设我的工作是为一家银行开发一个信用卡欺诈检测模型。现实中,假设只有 0.1%的交易是欺诈性的。我聪明地假设没有交易是欺诈性的,并以 99.9%的模型准确率(即正确分类的百分比)而自豪。我拍拍自己的背,打开一瓶冰啤酒。

然而,我的雇主不高兴了。尽管它是一个高度准确的模型,但它并不做任何事情来检测欺诈,因此也不能解决潜在的业务问题。似乎我们需要另一个性能指标。

那么为什么准确性在这里不起作用呢?机器学习问题的一个共同特点是,我们必须处理类不平衡。一个类别(正)中的数据点数量压倒性地超过另一个类别(负)中的数量是非常典型的。然而,我们试图检测的往往是异常情况:疾病、欺诈案件、安全隐患等。

由于这里精确度不够,让我们看看我们需要什么。

语境

首先,关于使用什么样的绩效指标的问题应该总是由你想要解决的问题、你试图获得的洞察力来驱动。虽然这听起来很明显,但是仍然有太多的人专注于某些指标,而没有解决为什么的问题。

真与假,正与负

是时候了解一些术语了。通常,我们会将我们感兴趣的数据(欺诈案件、某个死者、猫的照片)标识为阳性,而将其余数据标识为阴性。我们可以很容易地将它扩展到多个类(例如,猫、狗、大象、老鼠),但是这里让我们坚持使用二进制方法。

我们的模型可能正确地()或不正确地()预测实际类别,无论它是正的还是负的。因此,我们有四种类型,它们通常被总结在一个混乱矩阵中:

混淆矩阵将模型预测的类别与现实中的实际类别进行对比。[图片由作者提供]

绝对数字和百分比传达了很多信息,但并不能说明全部情况。在实践中,并不是所有象限的权重都相同——这取决于前面提到的上下文。例如,在医疗环境中假阴性(未诊断的疾病)可能会产生灾难性的后果,而假阳性可能会在后续检查中得到纠正(尽管需要额外的费用)。准确性并没有捕捉到这些洞见。

长话短说:我们需要一些新的指标。

准确(性)

准确性仅仅考虑所有数据的正确分类比率。在术语范围内,我们可以将其定义如下:

请注意,许多损失函数和误差指标取决于精度。默认情况下,每一个错误的分类通常会受到同等的惩罚。正如欺诈检测示例中已经显示的,准确性可能会对模型的性能提供非常差的洞察,尤其是在处理类别不平衡时。

精确

精度与阳性和所有阳性的比例有关:

它说明了结果的有效性。换句话说,它说明了有多少检索到的元素是相关的。高精度表明,如果算法规定一个元素是正的,它可能确实是正的。

【为了记忆,我有时发现 P 精度只涉及 P 预测 阳性】

回到欺诈的例子:precision 将告诉我们被识别的案例中有多少是欺诈性的。假设我们正确识别了 800 ( 真阳性),错误识别了另外 700 ( 假阳性);我们的精度是 0.53。

欺诈检测设置的精确度示例。它会考虑所有被标记为欺诈的案例[图片由作者提供]

回忆

回忆描述了阳性和所有阳性(即包括假阴性)的比例:

此指标告诉我们结果的完整程度,例如,检索到多少相关元素。高召回率意味着捕获了大部分真正积极的元素。

Recall 可能是考虑到Real 阳性(注意假阴性实际上是阳性)】**

再次考虑欺诈的例子。假设我们从 1000 个实例中识别出 800 个实例(真阳性)(包括 200 个假阴性*)。我们的召回率是 0.8。*

欺诈检测环境中的召回示例。它处理所有真实的欺诈案件[图片由作者提供]

请注意,通过简单地将许多元素标记为阳性,您可以获得高召回率,但这通常是不可取的。假设我们将大量交易标记为欺诈。该集合可能确实包含所有欺诈交易,但也包含许多需要人工处理的误报(=成本)。请注意,我们正在处理精确度和召回率之间的权衡

f 分数

我们认为精确度和召回率都提供了有用的见解,但是用一个数字来表达它们之间的平衡会更方便。这就是 F 分数的作用。更准确地说,它是精确度和召回率的调和平均值。最简单(也是最常见)的 F 分是平衡 F 分,也称为F_1。1 表示它同等重视精确度和召回率。它由表示

请注意,通过乘以精度和召回率(分子),两个指标之间的差异将被扣分。如果我们的精度是 0.8,召回率是 0.2,那么 F 值只有 0.32。如果两者都是 0.5,F 值也是 0.5。可选的 F 值(例如,F_0.5F_2)更重视精确度或召回率。

精确回忆曲线

另一种表达权衡的方式是精确召回曲线。一般来说,你发现的真阳性越多,随之而来的假结果就越多。把分类器算法想象成一种看门人,使用一定的接受阈值。一个非常关键的人可能在非常确定自己的情况下(高精度*)只让某个元素通过,冒着拒绝真阳性的风险。更宽松的方法可能会传递更多的元素,捕获更多的总人口(高召回),但也会引入更多不正确的分类。*

精确度-召回率曲线示例,显示了各种分类阈值的精确度和召回率之间的权衡。曲线下的面积作为一个性能指标[图片来自作者]

曲线下面积( AUC )提供了对模型质量的洞察。一个完美的模型有AUC=1,精度和召回率都永远是 1.0。一个“好”的模型会弯曲到(1,1)点,覆盖很大的区域。

如何应对阶层失衡?

现在我们知道如何在阶级不平衡的情况下衡量绩效,但是我们该如何利用这些信息呢?假设我们注意到我们的欺诈检测模型召回率很低,我们如何解决这个问题?

通常情况下,召回和/或精度的问题源于阶级的不平衡。如果一个集合中的元素数量绝大多数是负数,并且我们优化了准确性,那么一小部分正数几乎不会影响整体性能。记住,一个简单的损失函数平等地测量所有不正确的分类(假阴性假阳性*)。*

全面的评估超出了这里的范围,但是有两个一般的解决方向:(I)我们对一些错误分类的加权比其他的更重,以及(ii)我们对包含较少否定的子集进行训练。

首先,让我们检查称重。通过调整损失函数,我们可以影响训练。在欺诈检测案例中,我们可能更喜欢将太多案例标记为潜在欺诈,而不是太少案例。换句话说,我们朝着高回忆驶去。通过更严厉地惩罚假阴性,我们训练算法将更多病例标记为阳性(冒着增加假阳性的风险)。

反之亦然。如果我们设计一个垃圾邮件过滤器,我们最好确定检测到的电子邮件确实是垃圾邮件。当有疑问时,最好让用户决定电子邮件是否相关。在这里,我们更喜欢高精度*,并会惩罚误报(不是垃圾邮件,但被标记为垃圾邮件)最多。*

第二种解决方案需要重新调整样本。这比我在这里提出的要复杂一点,但机器学习算法分类 1000 个正面和 1000 个负面比 1000 个正面和 100 万个负面更容易。在后一种情况下,该模型可能会针对数量大得多的负数进行定制。或者,您可以从少数类中复制样本来重新平衡集合。

最后的话

“哪些错误分类最具现实影响?”

对于任何机器学习问题,问题应该总是与模型的目的相关。光背度量对你帮助不大。相反,应该关注哪些错误分类更有害,以及如何尽可能地防止它们,同时在精确度和召回率之间保持健康的平衡。最终,记住机器学习模型是达到目的的手段,而不是最终目标本身。

列出所有潜在的分类并强调哪个错误分类最有害通常是有用的[图片由作者提供]

关键要点

  • 当数据集不平衡时,准确性(正确分类的总百分比)通常不是一个有意义的性能指标
  • 阶级不平衡暗示一个阶级比其他阶级更经常出现。惩罚某些错误分类并对数据集进行重新采样有助于解决失衡问题。
  • 精度是一个惩罚误报的指标。因此,具有高精度的模型谨慎地将元素标记为正。
  • 召回是一个惩罚假阴性的指标。当有疑问时,具有高召回率的模型倾向于积极的分类。
  • F-scores精度-召回曲线为平衡精度和召回提供指导。

进一步阅读

*http://www.chioka.in/class-imbalance-problem/ https://en.wikipedia.org/wiki/Precision_and_recall https://machinelearningmastery.com/roc-curves-and-precision-recall-curves-for-classification-in-python/ https://machinelearningmastery.com/random-oversampling-and-undersampling-for-imbalanced-classification/ *

深入了解多类分类的精确度、召回率和 F1 值

原文:https://towardsdatascience.com/precision-recall-and-f1-score-of-multiclass-classification-learn-in-depth-6c194b217629

维克多·瓦西塞克在 Unsplash 上的照片

从混淆矩阵手动计算和 sklearn 库的语法

精确度、召回率和 f1 值是评估分类算法时非常常用的指标。现在使用库或包来计算它们是非常容易的。但是我相信理解幕后发生的事情对于真正理解输出也是很重要的。

如果这些概念对您来说是全新的,我建议您先阅读这篇文章,其中详细解释了精确度、召回率和 f1 分数的概念。

本文将关注多类分类模型的精确度、召回率和 f1 值。

精确

作为复习,精度是真阳性的数量除以总阳性预测的数量。换句话说,precision 找出了实际上有多少预测值是正数。

下面是精度的公式:

作者图片

这里,

TP =真阳性

FP =假阳性

我们将看到如何从一个多分类模型的混淆矩阵中计算精度。考虑这个混淆矩阵:

作者图片

如你所见,这个混淆矩阵是一个 10 x 10 的矩阵。因为这个模型有 10 个类。当我们处理二进制分类时,混淆矩阵是 2 x 2,因为二进制分类有 2 个类。

我将这个混淆矩阵表示为热图,以便更好地了解实际标签在 x 轴上的位置以及预测标签在 y 轴上的位置。该模型有 10 个类,用数字 0 到 9 表示。

这里的真阳性和假阳性是什么?

我们将通过几个例子来理解它。让我们以标签 9 为例进行演示。标签 9 的真正阳性应该是实际上是 9 并且预测也是 9 的样本。在上面的热图中,947(看右下角的单元格)是真正的正数,因为它们被预测为 9,而实际标签也是 9。

我们来看看什么是误报。看,当我们处理 9 号标签时,只有 9 号标签是阳性的,其他标签都是阴性的。在预测标签为 9 的列中,仅对于 947 个数据,实际标签也是 9。该列中的其余数据(用红色标记)被模型错误地预测为 9。他们的实际标签不是 9。所以标签 9 的误报是(1+38+40+2)。

标签 9 的精度为:

947/ (947 + 1 + 38 + 40 + 2) = 0.92

标签 9 的精度是 0.92,非常高。因为差不多接近 1 了。

如果模型是完美的,就不应该有任何假阳性。如果误报为 0,精度将为 TP/TP,即 1。

我们将再举一个例子。让我们也计算一下标签 2 的精度。

首先,从热图中找到实际标签和预测标签都为 2 交叉单元格。看第二栏。是 762(浅色的细胞)。其余细胞为假阳性。当我们考虑标签 2 时,只有标签 2 是正的,所有其他标签都是负的。因此,在第 2 列中,标签 2 的所有其他值实际上都是负值,但是我们的模型错误地将它们预测为标签 2。

标签 2 的精度:762/(762+18+4+16+72+105+9)= 0.77

同样,您可以计算每个标签的精度。

回忆

召回是真阳性除以真阳性和假阴性。换句话说,回忆衡量的是模型预测积极事物的能力。

公式如下:

作者图片

我们将再次计算标签 9 和标签 2 的召回。所以,真正的积极因素是一样的。但这次我们需要找出假阴性。我建议试着先想想什么可能是假阴性,然后看看这里的解释。

这又是热图:

作者图片

看这里,红色的矩形有不同的方向。我们来看看为什么。

什么是假阴性?

假阴性是实际上是阳性但被预测为阴性的样本。让我们考虑标签 9。提醒一下,当我们处理标签 9 时,标签 9 是唯一的阳性,其余的标签都是阴性。看第九排。所有的样本实际上都是阳性的。但是有 947 个样本被预测为阳性。但是 14 + 36 + 3 个样本被预测为阴性。这些是标签 9 的假阴性。

标签 9 的召回:947 / (947 + 14 + 36 + 3) = 0.947

同样,标签 2 的召回是:

762 / (762 + 14 + 2 + 13 + 122 + 75 + 12) = 0.762

您可以使用同样的方法计算每个标签的召回率。

F1 分数

F1 分数是精确度和召回率的调和平均值。只是提醒一下,这不是算术平均值。如果 precision 为 0,recall 为 1,f1 分数将为 0,而不是 0.5。公式如下:

作者图片

让我们使用标签 9 和标签 2 的精度和召回,并使用此公式找出 f1 分数。

标签 9 的 F1 分数:2 * 0.92 * 0.947/(0.92+0.947)= 0.933

标签 2 的 F1 分数:2 * 0.77 * 0.762/(0.77+0.762)= 0.766

我相信你现在已经知道如何计算多类分类问题中每个标签的精度、召回率和 f1 值。

但是我们仍然需要一个模型的单精度、召回率和 f1 值。我们如何得到它?

宏观平均和加权平均精度、召回率和 F1 分数

有两种不同的方法可以获得模型的单一精度、召回率和 f1 值。

**先说精度。**我们需要所有标签的精度来找出模型的单精度。但是我们在这里只展示了标签 9 和 2 的精度。请随意使用我们在此演示的相同方法计算所有标签的精度。

在下表中,我列出了所有标签的精确度、召回率和 f1 值。

作者图片

宏平均精度是所有标签精度的简单算术平均值。因此,该模型的宏观平均精度为:

精度=(0.80+0.95+0.77+0.88+0.75+0.95+0.68+0.90+0.93+0.92)/10 = 0.853

请随意以同样的方式计算该模型的宏观平均召回率和宏观平均 f1 分数。

加权平均精度还考虑了每个标签的样本数。

该数据集中每个标签的样本数如下:

0 — — 760

1 — — 900

2 — — 535

3 — — 843

4 — — 801

5 — — 779

6 — — 640

7 — — 791

8 — — 921

9 — — 576

该模型的加权平均精度是样本数乘以单个标签的精度,再除以样本总数。

样本总数将是所有单个样本的总和:

760 + 900 + 535 + 848 + 801 + 779 + 640 + 791 + 921 + 576 = 7546

为了计算加权平均精度,我们将每个标签的精度乘以它们的样本大小,然后除以我们刚刚找到的样本总数。

(7600.80 + 9000.95 +5350.77 + 8430.88 + 8010.75 + 7790.95 + 6400.68 + 7910.90 + 9210.93 + 5760.92) / 7546 = 0.86

如你所见,算术平均值和加权平均值略有不同。如果单个标签的样本量相同,算术平均值将与加权平均值完全相同。

Sklearn 函数

好消息是,你不需要用这种方式计算精确度、召回率和 f1 分数。Scikit-learn library 有一个函数**‘classification _ report’**,它分别为您提供每个标签的精确度、召回率和 f1 分数,以及模型的精确度分数、单个宏平均值和加权平均值精确度、召回率和 f1 分数。下面是语法:

from sklearn import metrics
print(metrics.classification_report(y_test, y_pred))

这里,y_test 是测试数据的原始标签,y_pred 是使用模型预测的标签。

该函数还为您提供了一个名为“support”的列,它是每个标签的单个样本大小。对应于精确度、宏平均值和加权平均值的支持值是数据集的总样本大小。

作者图片

上图可以看到支持值都是 1000。这意味着所有标签的样本量是 1000。

我上面说过,如果每个标签的样本量相同,那么宏观平均和加权平均也就相同。你可以看到,在这张图中,宏观平均和加权平均都是一样的。

您将在此链接中找到分类项目的完整代码以及我是如何获得上表的:

https://github.com/rashida048/Few-Machine-Learning-projects/blob/master/image_classification_problems.ipynb

结论

本文解释了如何从给定的混淆矩阵手动计算多类分类的单个标签的精度、召回率和 f1 值,以及多类分类模型的单精度、召回率和 f1 值。我们还讨论了如何使用 scikit-learn 库中的一行代码非常容易地获得它们。希望对你有帮助。

下面的视频解释了同样的概念:

欢迎在推特上关注我,喜欢我的 T2 脸书页面。

更多阅读

https://pub.towardsai.net/complete-detailed-tutorial-on-linear-regression-in-python-for-beginners-f9fa3f65faca https://pub.towardsai.net/data-analysis-91a38207c92b </6-tips-for-dealing-with-null-values-e16d1d1a1b33>

通过基于用户的协作过滤预测电影分级

原文:https://towardsdatascience.com/predict-movie-ratings-with-user-based-collaborative-filtering-392304b988af

Python 中协作过滤的全面介绍

动机

假设您有一个来自不同用户的电影评级数据库。系统里有一些你的用户没看过的电影。你如何利用这些数据向你的用户推荐新的相关电影?

作者图片

一种直接的方法是使用用户 2 对同一电影的评价来猜测用户 1 对该电影的评价。为了增加猜对的机会,用户 2 应该有与用户 1 相似的评分行为。

作者 GIF

在预测了所有的评级之后,我们将向用户 1 推荐被用户 1 预测为高度评级的电影。

上面的方法是基于用户的协作过滤的简化版本,它使用用户之间的相似性来提供推荐。

本文将向您展示如何使用协同过滤来填补真实电影数据库中缺失的评级。

获取数据

我们将通过 Creative Common License 使用从 Kaggle 上的电影数据集中收集的数据。本文中用作示例的数据子集可以从这里下载。

将下载的文件另存为small_movie_ratings.csv,然后加载数据:

作者图片

请注意,该数据中有一些缺失值。我们将从寻找每对用户之间的相似性开始。

寻找用户之间的相似之处

我们将使用皮尔逊相关系数根据用户对电影的评分来找出他们之间的相关性。

如果两个用户给出完全相同的评级,则皮尔逊相关性为 1。

1.0

如果两个用户的相对电影评级以相同的速率增加,相关性仍然是 1。

1.0

如果两个用户的评分完全相反,则相关性为-1。

-1

让我们使用这个方法来创建一个函数,用于查找两个用户之间的相关性:

创建一个矩阵,显示所有用户对之间的相似之处:

作者图片

获得相似的用户

假设我们希望根据其他用户的评级来预测用户 3 对电影 1 的评级。我们首先希望只选择对电影 1 进行了评级的用户。

作者 GIF

接下来,我们只挑选与用户 3 最相似的 k 个用户。我们称这些 k 数量的用户为用户 3 的邻居

在下面的例子中,我们将邻居的数量设置为 2。

作者 GIF

获取相似用户对一部电影的评分

由于不同的用户可能对他们喜欢的电影有不同的评分标准,我们希望通过从用户对所有电影的平均评分中减去用户对电影的评分来调整这种偏差。

作者图片

这意味着调整偏差后,用户𝑣对电影《𝑖》的评级为:

作者图片

在下面的 GIF 中,由于用户 1 对所有电影的平均评分为 1,我们将用户 1 对电影 1 的评分减去 1。因为用户 2 对所有电影的平均评分是 4,所以我们用 4 减去用户 2 对电影 1 的评分。

因此,在调整偏差后,用户 1 和 2 对电影 1 的新评级将分别是 2 星和 1 星。

作者 GIF

基于相似用户的评级来获取用户对电影的评级

在调整偏差之前获得评级

在对偏差进行调整之前,用户𝑢对电影𝑖的评级是通过取所有邻居𝑣.对电影𝑖的评级的加权平均值来计算的

作者图片

例如,用户 1 和用户 3 之间的相似度是 0.8。用户 1 对电影 1 的评价是 2。我们将用户 1 的调整评级乘以他们的相似性 0.8。

我们可以对用户 2 进行类似的计算。下表总结了这些计算。

作者图片

然后,我们将这些加权评分的总和除以 0.8 和 0.5。

该计算的结果是在调整偏差之前用户 3 对电影 1 的评级。

作者 GIF

调整偏差后获得评级

还记得我们用平均评分减去每个邻居对电影的评分来消除他们的偏见吗?

为了获得用户𝑢对电影《𝑖》的预期评级,在调整偏差之前,我们将把用户𝑢的平均评级加到用户𝑢对电影《𝑖》的预期评级上。

作者图片

在下面的 GIF 中,在调整偏差之前,用户 3 对电影 1 的预期评级为 1.61,用户 3 对所有电影的平均评级为 2。取 1.61 和 2 之和,得出 3.61。

作者 GIF

获取所有用户的缺失评分

现在我们准备将上面显示的所有函数放入函数predict_rating中。该功能根据用户邻居的评分来预测用户对某部电影的评分。

使用predict_rating功能预测所有缺失评分的评分:

Top 2 neighbors of user 30, Barry Lyndon: ['547', '509']
Top 2 neighbors of user 311, License to Wed: ['547', '509']
Top 2 neighbors of user 311, Sleepless in Seattle: ['547', '509']
Top 2 neighbors of user 452, 88 Minutes: ['547', '509']
Top 2 neighbors of user 452, Gleaming the Cube: ['547', '509']
Top 2 neighbors of user 452, The Endless Summer: ['547', '509']
Top 2 neighbors of user 452, The Forbidden Kingdom: ['547', '509']
Top 2 neighbors of user 452, The Terminal: ['547', '509']
Top 2 neighbors of user 468, Rain Man: ['547', '509']
Top 2 neighbors of user 547, A Time to Kill: ['509', '468']
Top 2 neighbors of user 547, Mr. Holland's Opus: ['509', '468']
Top 2 neighbors of user 547, The 39 Steps: ['509', '468']
Top 2 neighbors of user 624, Judgment Night: ['547', '509']
Top 2 neighbors of user 624, Lonely Hearts: ['547', '509']
Top 2 neighbors of user 73, Psycho: ['547', '509']

在填写完所有缺失的评分后,我们来看看评分数据库:

作者图片

相当酷!现在,我们可以向以前没有看过这些电影的用户推荐预测评级很高的电影。

结论

恭喜你!您刚刚学习了如何根据基于用户的协作过滤来预测电影评级。我希望这篇文章能激励你创建你的推荐系统。

随意发挥,并在这里叉这篇文章的源代码:

https://github.com/khuyentran1401/Data-science/blob/master/machine-learning/collaborative_filtering/collaborative_filtering.ipynb

我喜欢写一些基本的数据科学概念,并尝试不同的数据科学工具。你可以在 LinkedIn 和 T2 Twitter 上与我联系。

星这个回购如果你想检查我写的所有文章的代码。在 Medium 上关注我,了解我的最新数据科学文章,例如:

参考

电影数据集。2018–09.鲁纳克巴尼克。
CC0:公共领域。从https://www . ka ggle . com/datasets/rounakbanik/the-movies-dataset检索到 2022–03–18

Katsov,I. (2018)。算法营销简介。伊利亚·卡佐夫。

使用大查询 ML 预测您网站上的交易

原文:https://towardsdatascience.com/predict-transactions-on-your-website-using-big-query-ml-c365f58d29ca

基于谷歌分析数据训练模型

皮卡伍德在 Unsplash 拍摄的照片

如果你在网上销售产品,预测哪些用户会转化可以帮助你增加收入。例如,它可以让您:

  • 创建定制广告受众
  • 基于用户行为生成动态产品推荐
  • 自定义现有用户的电子邮件工作流

对于这个分析,我们将使用google_analytics_sample公共数据集,您可以在这里找到。

大查询公共数据集是实践数据科学的惊人资源。然而,如果你有自己的网站,用你的网站数据训练你的模型会更有趣!

步骤 1:了解数据集

当我们研究这个模式时,我们可以看到我们有一个列 visitId ,这意味着我们每个会话有一行。

会话

用户在您的网站或应用上活跃的时间段。默认情况下,如果用户处于非活动状态达 30 分钟或更长时间,任何未来的活动都将归于新的会话。离开您的站点并在 30 分钟内返回的用户被算作原始会话的一部分。

来源:谷歌分析文档

我们还有一个列 transactions,,它是一个整数列,对应于会话中的事务数量。我们并不真正关心实际的数字,我们只是想检查用户是否转换。

然后,我们有我们的一系列功能:

  • totals 包含一些有趣的指标,如页面浏览量、网站停留时间…
  • trafficSource 包含关于源(用户来自哪里)的数据
  • 设备包含所用设备的信息
  • 地理网络包含地理信息
  • hits 包含事件信息,甚至是交易信息。

这里我们有一个小问题:hit 包含关于事务的数据,这可能是数据泄漏。ML 模型应该只访问在事务之前发生的事情。

为了避免数据泄露,我们将去掉 hits 列。当然,对于包含在其他指标中的事务数据,我们可能仍然有一些问题。例如,会话的持续时间包括结帐过程的持续时间,这需要一些时间。页面浏览量也是如此。理想情况下,我们需要从这些指标中减去结帐页面发生的事情,但是我们现在忽略这一点。

步骤 2:将数据分为训练集和测试集

大查询允许我们在创建模型时分割数据,但我们也希望避免查看测试数据,因此我们将把数据集分割成一个训练和测试表。

我们的表有 2556 行,因此 80–20%的分割将得到:

  • 列车拆分的 2044 行
  • 512 行用于测试分割

为此,我们需要生成随机数。SQL 没有本地随机函数,所以我们将使用 Javascript 函数。这是我喜欢大查询的原因之一,你可以对任何列应用 JavaScript 函数!

这给了我们:

是的,有效!现在让我们创建我们的表。首先,让我们创建保存随机数的表:

然后,我们将创建我们的训练和测试集。

步骤 3:探索 Data Studio 中的训练集

让我们在 Data Studio 上创建一个新报告,并选择 Big Query 作为源。

我们需要知道的第一件事是交易的访问份额。

作者的数据工作室报告

这显然是一个不平衡的数据集,我们必须记住这一点!

现在让我们试着看看购买者和非购买者的页面浏览量和网站停留时间的影响。

作者的数据工作室报告

这里显然有一个模式:似乎买家比非买家在网站上花费更多的时间,浏览更多的页面,这是有道理的。

最后,让我们看看来源和转换之间的关系。这次我们将使用频道分组。

频道分组

频道分组是基于规则的流量来源分组。在整个分析报告中,你可以看到你的数据根据默认的渠道分组进行组织,这是一个最常见的流量来源分组,如付费搜索直接。这使您可以快速检查每个流量通道的性能。

来源:谷歌分析文档

作者的数据工作室报告

频道分组似乎有影响,但是我们必须小心,因为我们没有很多数据。

步骤 4:构建我们的 ML 模型

让我们试试逻辑回归,这是最常见的分类模型之一。如果您不知道逻辑回归,我在参考资料中提供了一些 StatQuest 视频的链接。

在大查询上创建模型的语法很简单:

步骤 5:评估我们的模型

现在我们已经创建了模型,是时候看看它在我们的测试集上是否表现良好了!

由于我们的数据是不平衡的,准确性将不是一个评估模型的好指标:仅仅通过每次预测错误,我们将获得超过 98%的准确性。

因此,我们将使用 ROC 曲线来预测我们的模型。

让我们编写一个查询来评估我们的逻辑回归:

这说明用途:

  • 我们识别 50%的交易。
  • 在我们确定为事务的会话中,实际上只有 22%是事务。

即使这些数字看起来不怎么样,但当你想到一个用户只有 1.7%的转化机会时,也没那么糟糕。这给了我们几乎 95%的 ROC_AUC。

同样,我们可能没有足够的数据来构建一个强大的模型,但是想象一下用成千上万的会话来做这件事!

步骤 6:使用模型

在现实生活中,我们可以通过多种方式使用这个模型。

一种方法是实时使用 ML 模型来确定用户将要购买的概率:

  • 如果概率很高,我们什么都不做。
  • 如果概率为中等,我们会显示一个弹出窗口,显示只在今天有效的折扣代码,试图说服用户购买。
  • 如果概率低,我们什么都不做。

这种模式将帮助我们说服犹豫不决的用户。

使用这种模式的另一种方法是建立观众群;我们把概率最高但是还没买或者很长时间没买的用户拿过来,我们用他们来创造一个定制的广告受众,或者我们给他们发邮件。

资源

  • 大查询公共数据集
  • 谷歌分析文档
  • 大查询 ML 文档
  • 大查询用户自定义函数文档
  • Data Studio 文档
  • 统计任务:逻辑回归
  • StatQuest: ROC 和 AUC,解释清楚!

预测你的模型的性能(不需要等待控制组)

原文:https://towardsdatascience.com/predict-your-models-performance-without-waiting-for-the-control-group-3f5c9363a7da

NannyML 的一种新算法允许在基础事实可用之前估计 ML 模型的性能。这是它的工作原理。

[图片由作者提供]

现在,你的预测模型已经建立并开始运行了。您已经开发、测试并最终部署了它。在向利益相关者展示时,您提到该模型在回溯测试数据上实现了 75%的精确度和 25%的召回率。他们会问:“但是在目前活跃的客户中表现如何呢?”。重点是:

没人对你的模型在旧数据上的表现感兴趣。唯一重要的是该模型将如何处理新数据

但是,为了知道这一点,你必须等待观察目标变量,这可能需要很长时间。我们可以称之为“延迟表现的诅咒”:你需要现在就做出决定,但是你只能在未来获得完整的信息。

我以为这个问题无解,直到最近看了 NannyML (一个专注于部署后数据科学的开源库)的这篇文章。他们提出了一种叫做“基于信心的性能评估”(CBPE)的算法。该方法允许**通过使用预测概率作为唯一的输入(即,当基础事实不可用时)**可靠地估计模型的性能。

因为我认为他们的想法非常聪明,所以在这篇文章中,我将描述他们的算法是如何工作的。

预测性能的需要

假设你的公司要求你预测下个月内每个客户的流失概率。因此,你开发了一个机器学习管道,在对保留数据进行验证后,你就可以部署它了。

管道的输出是这样一个表:

ML 管道的输出。[图片由作者提供]

通常,您会将一小组(随机选择的)客户分开。我们的想法是让这个群体保持完整,这样一个月后你就会知道如果你对你的客户群什么都不做会发生什么。如同在临床试验中一样,这个组被称为“对照组”。

随机选择一个对照组。[图片由作者提供]

一个月后,你终于知道对照组的哪些客户翻炒了。因此,您可以计算控制组中模型的任何性能指标:ROC 曲线下面积、平均精度、F1 分数等等。

延迟(观察到的)模型性能。[图片由作者提供]

为此,你必须等待一个月。

相反,NannyML 提出的算法允许你立即获得模型性能的可靠估计,而不必等待控制组。此外,这些预期指标是基于整个客户群(而不仅仅是控制组)计算的。

NannyML 的算法允许在服务时获得预期的模型性能。[图片由作者提供]

有很多好处:

  • 立即做出更好的决策,允许进行假设分析(例如:如果我将概率阈值设置为 50%,预期的精确度/召回率是多少?如果我把门槛设成 80%呢?).
  • 对你的模型有一个早期危险信号,这样你就可以在为时已晚之前修复它。例如,如果你的信用评分模型正在系统地失败,你甚至不需要等一年(并损失数百万)才意识到这一点。
  • 对你的模型在当前数据上的表现有一个现实的结果,因为有时等待控制组是不可行的,例如在长期预测中(例如房屋抵押贷款违约)或当获得标签很昂贵时。

当然,一种方法并不排斥另一种:你可以估算出预期的模型性能,然后等待一个月,观察实际性能,最后进行比较。

预期模型性能与观察到的模型性能。[图片由作者提供]

现在我们知道了为什么这是有用的,让我们试着理解算法背后的魔力。

警告,小心处理!

为了使算法产生可靠的结果,必须满足两个先决条件:

  • 模型产生的概率必须正确校准。
  • 不能有概念漂移(但是,数据漂移不是问题)。

概率校准是一个直观的概念:如果你有一群预测概率为 20%的人,那么其中大约 20%实际上应该是正面的。这里就不赘述了,但是有校准你模型的技巧“事后”。如果想了解更多,可以看我的文章《Python 的 predict_proba 其实并不预测概率》。

关于概念漂移,它是指你的特征和目标变量之间的关系随着时间的推移而变化。这是机器学习中可能发生的最可怕的事情之一。如果发生这种情况,你当然不能指望可靠地预测你的模型的性能。

说到这里,我们继续有趣的部分。

从概率到混淆矩阵

为了确定想法,让我们以 5 个人为例,他们有不同的预测概率:10%、25%、40%、70%和 85%。

5 个人的预测概率。[图片由作者提供]

但是,通常情况下,概率并不像现在这样被使用。事实上,最终,我们感兴趣的是找出哪些人会是积极的,哪些人会是消极的。要做到这一点,我们需要设置一个阈值:高于该阈值的观察结果将被归类为积极的,低于该阈值的将被归类为消极的。

例如,如果我们将阈值设置为 50%,前 3 个人将被分类为阴性(标签 0),而后 2 个人将被分类为阳性(标签 1)。

从预测概率到预测标签,基于阈值。[图片由作者提供]

现在让我们考虑最后一个人,他的预测概率是 85%。由于该点高于阈值,该个体被分类为阳性。然而,由于我们的概率是经过校准的,我们可以合理地预期:

  • 85%的情况下,这个人实际上会变成阳性(“真阳性”);
  • 15%的情况下,这个人会变成阴性(【假阳性】)。

现在让我们看看第一个个体。其预测概率为 10%,因此被归类为负面。根据同样的推理:

  • 90%的时候,这个人实际上会变成阴性(“真阴性”);
  • 10%的情况下,这个人会变成阳性(“假阴性”)。

很容易从几何上形象化所有观测值的这些量:

基于预测概率和阈值来估计混淆矩阵。[图片由作者提供]

每个彩色部分代表个体成为真/假阳性或真/假阴性的概率。

因此,如果我们对每种颜色的所有片段的长度求和,我们就分别获得了真阳性、假阳性、假阴性和真阴性的预期数量

同样,从图中可以清楚地看到TP + FP + FN + TN = n,其中n是个体的数量。注意,这个性质是“常规”混淆矩阵所共有的。

唯一的区别是,在我们的例子中,TP、FP、FN 和 TN 的值不是整数值。这是有意义的,因为它们是期望值。事实上,在这个例子中,我们得到的是:

预期混淆矩阵。[图片由作者提供]

注意,为了获得这个预期混淆矩阵,我们只需要知道(校准的)预测概率和阈值。到目前为止,我们看到的所有 Python 代码的对应代码是:

pred = proba >= thres

tp = np.sum((pred == 1) * proba)
fp = np.sum((pred == 1) * (1 - proba))
fn = np.sum((pred == 0) * proba)
tn = np.sum((pred == 0) * (1 - proba))

既然我们知道了预期混淆矩阵,我们就可以计算许多性能指标,比如精确度、召回率、F1 分数、平均精确度、ROC 曲线下的面积等等。

得到预期的 ROC 曲线

例如,让我们关注 ROC 曲线下的区域。ROC 曲线的两个成分是真阳性率和假阳性率。从图形上看,TPR 和 FPR 是:

真阳性率和假阳性率的图形解释。[图片由作者提供]

然而,阈值的选择是任意的。因此,为了获得模型性能的全局概念,ROC 曲线被定义为所有可能阈值的所有 TPR/FPR 的集合。在这种情况下,有 5 个不同的值,有 6 个可能的阈值。

不同阈值的假阳性率和真阳性率。[图片由作者提供]

因此,在这一点上,我们有 6 对假阳性/真阳性率:每个阈值一对。然后通过绘制所有对来获得 ROC 曲线,假阳性率在 x 轴上,真阳性率在 y 轴上。

从 6 对 FPR/TPR 获得的 ROC 曲线。[图片由作者提供]

现在,roc_auc_score就是这条曲线下的面积,这很容易计算:Scikit-learn 还提供了一个函数,可以直接计算任何曲线下的面积。在我们的例子中, x- 坐标由假阳性率给出, y- 坐标由真阳性率给出,因此:

from sklearn.metrics import aucfpr = [0.0, 0.06, 0.17, 0.39, 0.67, 1.0]
tpr = [0.0, 0.37, 0.67, 0.85, 0.96, 1.0]roc_auc = auc(x=fpr, y=tpr)

结果是 0.81。这意味着我们可以对该数据集上模型的 ROC 下面积做的最佳估计是 81%。

就是这样。我们只是根据预测的概率计算了 ROC 曲线下的预期面积,而不知道事实真相!

总结

在本文中,我们已经看到了当基础事实不可用时,如何可靠地预测机器学习模型的预期性能(例如 ROC 下的面积)。

这种算法被称为“基于信心的性能评估”(CBPE),由 NannyML (一个专注于部署后数据科学的开源库)在本文中首次提出。他们刚刚开始开源,你可以在他们的 Github 页面上看看他们的代码和文档:https://github.com/NannyML/nannyml。

感谢您的阅读!我希望你喜欢这篇文章。如果你愿意, 在 Linkedin 上加我

在 BigQuery 中从 Google 商品商店的 Google 分析数据集预测转换事件

原文:https://towardsdatascience.com/predicting-conversion-events-from-google-analytics-dataset-for-google-merchandise-store-in-bigquery-1673f7b074fc

步骤 2—大查询 ML 逻辑回归

通过 BigQuery ML 使用 GA 数据预测 Google 商品商店转化率。图片作者。

在我们的上一篇文章中,我们介绍了使用谷歌商品商店的谷歌分析数据集在 BigQuery 和 DataStudio 中进行探索性数据分析的基础知识。作为该流程的一部分,我们考察了各种维度——营销渠道、州、操作系统、浏览器、设备类型、星期几、小时、月份等等。正在考虑的指标包括页面浏览量、访问量、订单、销售额、现场时间。此外,我们采取了一些数据清理步骤来删除错误标记为美国的地区,通过选择感兴趣的维度和指标来执行基本的数据准备,并计算其他指标,如转换率、跳出率、每位访问者的销售额、每次访问的销售额、平均订单价值,并根据上述参与度和维度进一步总结我们的数据。我们现在已经准备好开始预测我们的哪些 web 会话将会看到访问者更有可能进行购买。幸运的是, BigQuery ML 提供了一个自动化的机器学习解决方案,使用逻辑回归实现了这样的预测。

工作中的 BigQuery ML 示例

步骤 1 —数据分析

除了比我们已经完成的更彻底的 EDA 之外,我们可以使用下面的查询对数据集运行一些基本的描述性统计数据:

描述性统计

在这里,我们试图通过显示感兴趣的指标来总结我们的数据——订单数[11,420]、会话数[364,210]、访客数[251,257]、页面浏览量[> 200 万]、网站停留时间[2.2 年!]我们还可以计算衍生指标,如转换率[3.14%]、每个用户的会话数[1.45]、每个访问者的页面浏览量[8.06]、每个访问者在网站上的平均时间[4.61 分钟。]下一步是计算参与度和转化率指标的最小/最大/范围/平均/标准偏差和方差的值。现场时间标准差【7.36 分钟】两个以上(!)倍于平均会话持续时间[3.18 分钟。]同样,页面浏览量[8.89]的标准差是 1.6(!)比 5.57 的平均页面浏览量高一倍,表明这些指标的实际值分布得相当广泛,而不是集中在平均值周围。注意,除了范围(它只是最小值和最大值之间的差),所有其他计算都内置在 BigQuery 的 SQL 风格中。除此之外,我们还能够估计 BigQuery 中的一些统计聚合函数:

方差

相关性 —皮尔森相关系数——似乎网站时间与订单的相关性较弱[0.34],而页面浏览量的相关性则更为适中,为 0.45。请记住,我们首先需要指定我们的因变量[订单]。]

协方差[Pop] —显示两个随机变量的联合可变性。如果该值大于 0,则一个变量的较大值对应于另一个变量的较大值。在我们的例子中,页面浏览量对订单的协方差[0.76]比网站时间 0.47 大得多:

描述性统计汇总表。图片作者。

这里有两个要点:参与度指标似乎确实与订单有相关性和协方差关系;其中这些关系在页面视图中比在网站上的时间更明显。我们现在准备开始构建模型以进行进一步评估。

步骤 2——模型创建

“厨房水槽”型号。

BigQuery ML 的神奇之处在于,我们能够用几行简单的 SQL 语句运行相当强大的算法,如下所示:

让我们解开这段代码。我们从创建(或者替换已经存在的模型)开始,然后通过 BigQuery ML 指定一些可用的选项:对于 model_type 参数,我们选择逻辑回归作为参数,告诉 BigQuery 我们想要运行哪种类型的模型。我们为我们的编码方法选择了哑元编码,它使用了 N-1 个哑元变量 vs N 个哑元变量,这些哑元变量在 One_Hot_Encoding 方法下会被使用。举例来说,对于设备类型的桌面、移动和平板电脑,假设平板电脑是网站访问者最不喜欢的设备,我们的功能栏如下所示。请注意,当桌面和移动设备都设置为 false 时,意味着平板设备:

类别编码的类型。图片作者。

下一步是启用 CALCULATE_P_VALUES 选项,我们将在稍后使用 ML_ADVANCED_WEIGHTS 命令时感谢它。我们要激活的最后一个选项是 ENABLE_GLOBAL_EXPLAIN,如果我们想在以后汇总评估数据的解释,这个选项会很有用。

然后,BigQuery ML 语法要求指定 Google 称之为“标签”的内容,这个概念您可能已经看到过,也被称为独立/预测/结果/响应变量。我们的目标是预测订单放置转换事件;换句话说,我们正在寻找已下单的会话,而不管是否有人下了 1 个或 25 个(数据发布)订单。因此,我们将使用 Orders≥ 1 条件来定义下订单的 TRUE/1 事件,如果没有下订单,则为 False/0。

我将把这个模型— v1 称为“厨房水槽”类型的模型,因为我们将在我们的算法中使用一个全面的、尽管数量有些过多的不同维度和度量:*设备、操作系统、浏览器、频道、持续时间分钟、小时 24、星期几、星期几、月份、状态、访问量、页面浏览量、来源、媒介。*在我们之前的帖子中,我们再次证明了这些维度和指标在 EDA 阶段确实显示了值得注意的结果。

我们建模过程的最后一步是我试图保持事情的真实性,而不是仅仅根据历史数据假设性地评估我们的结果,选择使用一部分数据作为我们的训练数据集,剩余部分作为测试数据。为了帮助我们预测的准确性,我们希望向模型提供尽可能多的数据,因此让我们使用前 11 个月的数据来训练我们的模型。然后,让我们使用过去 32 天的 GA 数据(对我们的目的来说足够有代表性)来确认该模型是有效的(预测有序和无序事件)还是无效的(不运行有意义的预测)。)

BigQuery ML 的另一个好处是,我们可以有效地大规模迭代,并运行各种版本的模型来确定“获胜”的模型。此外,现实世界中经常出现一些警告,使得继续使用性能最佳的模型变得不切实际,因此可能需要不同的备用选项。毕竟,我们真的要通过一个广泛的清单来运行我们的访问者,以确定更高的交易可能性,或者有一个足够好的最佳点,让我们接近最佳结果,而不会使我们的任务过于复杂。我建议多运行 10 个模型:

v2T6 用于频道,时长分钟,小时 24,星期几,访问量,浏览量

v3 用于频道、时长分钟、星期几、访问量、浏览量

v4

v5

V6T22 时长分钟,浏览量

V7T26 时长分钟

V8T30 浏览量

v9T34 持续时间 _Bucket,浏览量 _Bucket

v10为持续时间 _ 铲斗

v11T42 浏览量 _ 斗

第三步——评估我们的模型

就我个人而言,我支持 v6 版本,因为根据我阅读的一些研究、我们 EDA 实践的结果以及个人轶事信念,我相信参与度指标让我们更接近可靠的预测,而不会因为考虑其他指标或维度而使我们的生活变得过于困难。好消息是,我们不必仅仅依靠我的直觉,因为 BigQuery 包括 ML。评估命令就是这样做的— 评估我们模型的性能。

在继续下一步之前,让我们确保理解我们将在后面提到的重要术语:

真阳性—模型正确预测条件成立时的结果。对我们来说,这描述了一个订单被预测和订单实际发生的情况。

真阴性—模型正确预测条件不成立的结果。在我们的例子中,这指的是一个订单没有被预测的情况,一个订单实际上没有发生。

假阳性又名。类型 1 错误】——显示事件存在的(不正确)测试结果,而这种情况并不存在。在这里,我们会看到一个预测的秩序,但秩序实际上并没有发生。

假阴性又名。类型 2 错误遗漏错误】 —错误指示条件不成立的测试结果。对我们来说,这意味着秩序不是预测出来的,而是实际发生的。

在 BigQuery ML 的快速帮助下,我们可以检索以下内容:

精度 —预测阳性与实际阳性的比例 =真阳性/(真阳性+假阳性)。让我们将这种测量放在上下文中:虽然在识别真阳性与所有阳性的比率方面肯定有用,但如果我们有过多的假阴性,这种度量本身并不能提供足够的分析,因此可能会影响对我们能够分类的一些事件的准确识别,但会错过我们未能识别的大部分事件。

回忆 —预测阳性的实际阳性比例 =真阳性/(真阳性+假阴性)。这项措施解决了我们对精确度的担忧,却忽略了误报,这可能会影响我们的整体精确度。请注意,该指标也被称为敏感度TPR (真实阳性率。)

准确度 —给定正确标签的预测分数 **=(真阳性+真阴性)/(假阳性+假阴性)。**当然,这是迄今为止我们在这一点上考虑的最有希望的指标,但是,我们需要注意我们的真实和虚假事件的不均衡分布,这可能是典型的电子商务网站的情况——我们的 web 会话中只有一小部分(准确地说是 Google 商品商店的 3%)有订单事件,留给我们的是非事件的巨大份额[每次网站访问的 100 次中有 97 次。]因此,具有 o 订单的会话的准确性与没有订单的会话的准确性明显不同,但这一重要的细微差别在汇总期间丢失了[97%的非事件准确性将使 3%的“多事”会话的准确性相形见绌。]

F1 得分 —精度和召回率的调和平均值**= 2 精度召回率/(精度+召回率)**这是一个数学“胶水”,帮助我们看精度和召回率的综合效果。

对数损失—0(完美)到 1 之间的模型性能的度量。对数损失越大,预测概率与实际标签的偏差就越大。

ROC AUC —受试者操作特征曲线下面积。 ROC 曲线 —受试者工作特征(ROC)曲线显示了真阳性率和假阳性率之间的权衡。较低的阈值导致较高的真阳性率(和较高的假阳性率),而较高的阈值导致较低的真阳性率(和较低的假阳性率)

…下面是一次对所有 11 个模型运行评估的 SQL 代码,没有树受损/ 字节用于运行它:

评估车型

除了简单地查看结果,我还会推荐按顺序排列。这应该有助于我们更容易地识别最佳性能模型。请注意,与所有其他指标不同,Log_Loss 应该按照升序排序,因为最低值代表最佳结果。排名的其他计算包括计算【排名】分数——较低的结果表示表现更好的模型;和平均排名。

看下面的结果,很不幸,我们不得不忽略模型 v9、v10 和 v11 的 Precision、Recall 和 F1_Score 结果,因为这些模型只包含分类值,并且无法计算非标准化截距的回归统计。

毫不奇怪,我们的“厨房水槽”模型/v1 表现相当好,尽管它没有占据模型 v8 的第一名,模型 V8 只关注浏览量。[虽然 v1 在精确度、召回率和 F1_Score 方面击败了 v8,但它在精确度、对数损失和 ROC_AUC 方面有所欠缺。有趣的是,我最喜欢的 v6 模式[页面浏览量和网站停留时间]有点落后,排在第四位,与 v8 模式相比表现不佳,略落后于 V5[页面浏览量、网站停留时间和访问次数。]

当然,考虑到上述因素,我们可以根据指标的作用选择不同的权重——例如,在我们的用例中,精度可能没有召回重要。但是这是一个单独讨论的主题,集中在我们以后可能承担的特征工程任务上。

模型评估结果。图片作者。

之前已经设置了阶段并在创建模型阶段选中了适当的框,我们准备使用高级权重计算来检索大量有趣的摘要。为了更全面地了解功能,让我们使用一个具有分类维度的模型,例如模型 v11 中的 PageViews_Bucket :

毫升。advanced _ WEIGHTS检索与 ML 相同的字段。重量还有一些:

Processed_Input — 模型特征输入的名称。 ( 如果特性为非数字,那么将有多个具有相同字符串的行,每个特性类别一行。)

类别 —输入列为非数字时的类别名称(对于数字列为空。)

权重 —每个特征的权重/系数。训练线性模型的目标是确定每个特征的理想权重。如果权重为 0,则它对应的特征对模型没有贡献。对于数字列,weight 包含一个值,并且 category_weights 列为空。

标准误差—重量的标准误差(抽样分布的标准偏差。)

p 值——针对零假设的测试,表明观察到的结果并非偶然,事实上具有统计学意义(p 值≤0.05)

对于模型 v11 ,我们显示“退回”的会话(浏览量= 1)具有负权重,或者显示这样的会话不太可能产生订单事件。同时,浏览超过 25 页的会话、21-25 页和 16-20 页的会话更有可能与转换相关联:

高级权重检索。图片作者。

在为我们的 BigQuery 模型设置选项时做好充分准备的另一个好处是我们能够运行 GLOBAL_EXPLAIN 命令,该命令使用 Google 专有的explable AI生成结果。使用 v5 型号示例:

特征简单地列出我们特征的名称,而属性显示相应特征对模型整体的重要性。对于模型 v5 我们可以清楚地证明,在考虑的三个特性中,页面浏览量的权重最大,它对模型的重要性是在站点上的时间和 46.3(!)次比更重要的访问编号:

为考虑的特征建模权重。图片作者。

BigQuery ML 还支持基于 GUI 的模型性能可视化摘要。我们可以探索培训、评估和可解释性选项卡,如下所示:

用于模型评估/可解释性的 GUI 界面。图片作者。

训练 选项卡显示损失、每次迭代持续时间和学习率的图形表示。可解释性有效地让我们无需运行一行代码就能运行 GLOBAL_EXPLAIN:

模型性能 GUI 下的培训和可解释性标签。图片作者。

聚合指标保持不变,显示我们模型的所有评估指标,无需运行任何 SQL。

Precision-recall by threshold—显示您的模型在整个置信度阈值范围内的最高得分标签上的表现。较高的置信度阈值会产生较少的假阳性,从而提高精确度。较低的置信度阈值会产生较少的假阴性,从而提高召回率。

精确-召回曲线——显示了在不同置信阈值下精确和召回之间的权衡。较低的阈值导致较高的召回率但通常较低的精确度,而较高的阈值导致较低的召回率但通常较高的精确度。

ROC 曲线 —受试者工作特征(ROC)曲线显示了真阳性率和假阳性率之间的权衡。较低的阈值导致较高的真阳性率(和较高的假阳性率),而较高的阈值导致较低的真阳性率(和较低的假阳性率)

模型评估 GUI(已扩展。)图片由作者提供。

第 4 步——运行我们的预测

因为我们没有那么幸运地将数据分成训练集和测试集;我们可以比较订单 预测与实际订单的结果。为此,我们需要调用 ML。预测命令并传递所有必需的参数以满足我们模型的参数。[如果模型 v6 是使用 Duration_MinutesPageViews 指标构建的,那么我们必须确保我们的预测调用包含这两个指标。]此外,期望的粒度级别使用会话级汇总,因此我们的代码模拟了这一需求。

提醒一下,我们所有的 11 个模型都是使用从 2016 年 8 月 1 日开始到 2017 年 6 月 30 日结束这段时间的数据构建的。因此,我们的模型还没有遇到包含 2017 年 7 月 1 日到 2017 年 8 月 1 日测试,我们将使用它来运行我们的算法。除了执行我们的预测,我们还可以使用源表中的剩余数据将预测结果与实际事件状态进行比较。在下面的代码中,我试图清楚地描述我们的预测器标签与通过 SELECT 语句检索的实际订单之间的区别。让我们使用创建的三个模型来预测我们的订单:v1(“厨房水槽”)、v8(使用评估测量和我们的总结的组合排名最佳)和 v6(我最喜欢的,它仅稍微落后于 v5,并且需要少一个指标。):**

在分析我们的计算结果之前,让我们回忆一下定义,并尝试清楚地说明包含真阳性、真阴性、假阳性和假阴性的记录。我们将使用模型 v1: 的第一个查询结果

TP、TN、FP、FN 图解。图片作者。

步骤 5—分析结果

我们知道有些会话似乎有多个订单,而且我们之前的目标是预测有订单的会话,而不是订单总数,因此我们将订单事件标准化为 1,029,而不是总数 1,072。使用本文前面列出的精确度/召回率/准确度/F1 分数的公式,我们现在可以并排计算我们的模型的真实性能结果:

模型结果——观察与评估。图片作者。

准确性在这里是一个相当重要的衡量标准,尽管注意它在所有三个被分析的模型中几乎是一样的。尽管有之前的模型评估和我们的分析,但在现实条件下,模型 v1 确实比 v8 表现得更好。与准确性同样重要的是,有序会话和非有序会话的分布不成比例,这不是我们需要关注的唯一因素。总体而言, v1 在所有感兴趣的指标中明显胜出。尽管模型估计显示其在精确度【96.63%对 96.83%】方面比 v8 稍差,但使用我们的测试数据,我们能够获得 97.01%对 96.96%的结果。

精度不太一致,但在所考虑的车型中仍具有可比性。鉴于相同的数据集模型 v1 检索到 172 个真阳性(准确的订单预测),而模型 v6 检索到 98 个真阳性,即使当精度结果分别从 52.28%交换到 47.12%时,模型 v1 仍然是实际的赢家。即使 Precision 对模型 v6、有一个完美的分数,我们将检索到 208 个真阳性,这仅比模型 v6 的 172 个真阳性高一点,精度为 52%……显然,总阳性预测的数量也很重要,模型 v6 根本没有检测到足够多的阳性病例。我甚至认为,假设我们有模型的召回测量,我们可能想要完全跳过精度以避免在我们的分析中引入噪声。

召回显示车型 v1 的价值为 16.72%,这比我们预测的赢家——V8 车型的 11.47%的性能高出很多。当然,不到 17%的订单事件发生的会话被我们的算法正确识别,这听起来不像是值得大书特书的事情,但是考虑到随机预测订单事件的概率为 3%,我们的预测是 5.5(!)比靠运气有效几倍。即使有了模型 v3,我们的机会也增加了三倍。

F1_Score 显示了 PrecisionRecall 度量的代数组合行为,验证了我们到目前为止所见证的最佳性能模型的相同等级。

**我们可以使用公式计算的其他指标是特异性/TNRFPR 😗*

特异性,又称 TNR (真阴性率)计算为真阴性/(真阴性+假阳性)。)这两种模型都将我们预测无序事件的几率提高了 3%【1.03%对随机选择——在模型 v6 下,99.67%对 97%的几率。]请注意,在此参数下,型号 v6 的性能优于 v8v1 。这可能是因为它低于订单事件的数量。

**FPR ,或假阳性率通过以下公式计算:**假阳性/(假阳性+真阴性)1 — 特异性

鉴于页面浏览量的重要性,我们可以运行额外的分析来显示购买与非购买会话的页面浏览量的差异,以及这些会话之间描述性统计的差异。请注意,虽然十分之四的购买会议的访问者浏览了超过 25 页的页面,但只有五十分之一的非购买会议看到了这种参与度:

购买与非购买会话的浏览量。图片作者。

请随意分享您对所讨论的分析和建模的想法。您希望我们考虑哪些其他因素?什么样的特征工程步骤可以提高我们的性能?

在 BigQuery 中从 Google 商品商店的 Google 分析数据集预测转换事件

原文:https://towardsdatascience.com/predicting-conversion-events-from-google-analytics-dataset-for-google-merchandise-store-in-bigquery-a9cfb3b6087e

步骤 1 —探索性数据分析

按州划分的每次会话收入——通过 BigQuery 和 DataStudio 进行分析。图片作者

为了实践他们的技能,有抱负的数据分析师可以毫不费力地找到各种公共数据集,包括托管在 BigQuery 上的数据集;然而,对于好奇的人来说,第一方真实世界的商业数据并不容易获得。见见 BigQuery 的 Google Analytics 样本数据集,这是一个公共数据集,它提供了来自 Google 商品商店的 Google Analytics 360 点击流数据的模糊视图,该商店拥有知识共享属性 4.0 许可证。从 2016 年 8 月 1 日到 2017 年 8 月 1 日,12 个月的访客浏览和购买活动通过 366 个单独的表格以相应的每日增量提供。在由两部分组成的系列教程中,我们将利用这些数据来预测哪些网站访问者可能会进行购买。在这篇文章中,我们将探索我们的数据,并使用 BigQuery 和 DataStudio 执行基本的 EDA。

技术说明: 早在 2016 年,Google Analytics 只有 Universal Analytics 可供其支配,它计划在 2023 年 7 月日落,并被一个更新的版本取代——Analytics 4。

  1. 数据探索

由于该数据库包含来自真实电子商务网站的记录,我们可以访问网站属性的综合列表,包括收购、行为和最重要的转化等维度和指标。收购报告为我们提供了关于流量来源的信息,从营销渠道开始,并进一步深入到来源、媒介和活动流量驱动因素,这有助于确定最佳付费和有机渠道。行为特征为我们提供了一个关于访客量、页面浏览量、网站流量、花费时间、登录/退出页面导航、网站速度和许多其他重要指标的视角,这些指标可以帮助我们确定 UX 增强的领域。最后,但并非最不重要的是,转换部分包括网站目标和交易,最终可以帮助我们优化转化率。

根据数据字典有 292(!)该表中的字段在达到粒度级别。每当访问者导航到某个页面和/或完成导致所查看的 URL 发生变化的事件时,点击流软件(如 Google Analytics)会记录并跟踪此类操作(即点击)。)此类数据的主要局限性在于,默认情况下,它不区分 web 导航的方式——点击超链接、按下按钮、在地址栏中键入 URL 或检索书签。更复杂的解决方案能够识别专门与激活它们的点击类型相关联的特定 URL(例如,用于按下按钮的定制标签)。)查看在点击级别的属性可以帮助我们将一个完整的访客故事缝合在一起,从访问的页面、查看的图像、点击的按钮、填写的表格、搜索并添加到购物车的产品、完成的结账过程的步骤数等等。除此之外,没有比 BigQuery 更适合这项工作的工具了。尽管如此,在这篇文章中,我们还是要抵制诱惑,在汇总的层面上进行分析,谷歌称之为 total 视图。

所分析数据集的字段、数据类型和描述的部分列表。作者图片

2。数据准备

为了保持一致性,让我们将我们的记录限制为仅来自美国的访问者。此外,根据 GA 的最佳实践,让我们也删除内部流量**[source<>' mail . Google plex . com '。]** 接下来,由于 BigQuery 对每个查询列收费,所以限制查询中的字段数量也是明智的。让我们捕获访问的日期和时间、会话 Id(访问者和访问 Id 的连接)、访问次数、来源、媒体、设备、浏览器、营销渠道、州、下订单的数量、浏览的页面数量、站点访问的数量、花费在站点上的时间以及产生的销售额。这些维度和指标为我们提供了各种流量驱动因素、时间表和参与绩效的全面快照。假设我们已经在我们的 tutorials-777 项目下创建了TDS _ GA _ Google merchandise数据集,并且希望格式化数值,那么生成我们数据的所有 364,744 记录的 SQL 代码可能如下所示。注意,根据 Google 的文档,我们需要将销售额除以 1,000,000。虽然时间戳似乎是按照 UTC(T21)时区设置的,但我无法核实谷歌是否遵循了自己的最佳实践,并将其转换为当地时区——PST(太平洋标准时间)。

为我们的分析生成数据。

3。数据清理

浏览记录,我们可以注意到一些根本不在美国的美国“区域”(读状态)。创建状态表**【TBL _ us States】**后,我们可以交叉引用这些数据来执行急需的数据清理:

美国各州的数据清理。

除了位于我们之外的许多明显错误的区域,我们还必须担心该数据集的混淆性质,以及 GA 对其捕获用户信息的能力的限制——演示数据集中没有**,以及**(未设置)。此外,似乎我也忽略了在我的表格中考虑到 DC:

要在我们的数据集中排除和包含的区域列表。作者图片

我们可以通过运行下面的代码来执行这个基本的数据清理练习,删除 534 条记录,现在记录计数为 364,210 :

删除非美国地区。

4。数据扩充

使用数据的美妙之处在于我们能够通过导出或计算的值生成更多的数据。为了进一步分析,我们可以从时间戳中解析出时间和日期值,确定一天中的小时、一周中的天、月、季度、年等等;全部通过使用一个数据点。同样,我们可以根据网站停留时间、浏览页面和访问次数将我们的指标汇总到不同的容器中。知道了电子商务销售是由流量乘以转换率再乘以平均订单价值驱动的,我们就能更好地描绘出是什么在驱动我们的顶线。举例来说,如果我们比较我们营销活动的表现,我们可能会观察到展示渠道驱动的访客是付费搜索的两倍,然而,如果搜索的转换率是展示渠道的四倍,这种质量流量的差异会使搜索的收入比展示渠道高两倍。

让我们从创建缺失字段并运行下面的 DDL 代码开始:

添加缺失数据字段

下面是我们的查询,通过进行各种转换和计算来填充上面的字段,主要是针对日期/时间维度,但也针对持续时间、页面浏览量和访问量进行转换和计算。

填充缺失的数据字段。图片作者。

虽然谷歌警告我们,这个 BigQuery 数据集可能会显示与通过他们的演示帐户直接使用谷歌分析界面不同的结果,但我确实想通过比较我们的结果来看看我们会接近多少。单从检索2016 年 9 月 1 日的数据来看,我们做得相当好,根本不用担心我们数据的准确性:

BigQuery vs. GA 结果。作者图片

5。数据分析

现在,我们已经生成了分析所需的所有数据,我们正准备进行有趣的部分——实际分析。为了节省时间(处理)和金钱(BigQuery 成本),我将创建一个聚集所有感兴趣的数据点的帮助表。完全由您来定义指标,也许还有感兴趣的 KPI,我的一般经验是主要关注效率指标,这些指标有助于我们以相对方式评估绩效,然后通过调整基于数量的指标来更好地评估我们调查结果的业务价值,从而结束循环。毕竟,如果我们显示来自某个特定地区的访问者在网站上的转换率低于平均水平,而流量却比来自可比地区的低几十倍;或许在这一点上解决流量是比转化更重要的任务。同样,在相反的情况下,一个非常受欢迎的交通司机的低转化率肯定尖叫一个商机。

例如,我们可以统计访问者的数量,计算新访问者的百分比,确定参与度指标——每个会话的页面数、每个访问者的会话数、web 会话数(访问量)、页面浏览量,估计跳出率(访问者仅浏览一个页面后离开的访问百分比,或者换句话说,在没有真正浏览或购买的情况下从网站弹回的百分比),跟踪平均会话持续时间。我们可以通过总结转换事件——订单、转换率、AOV、每次会话的销售额和每个访问者的销售额来完成我们的分析。我试图将适当的数据类型应用到我们的计算中,特别是在调试完浏览量 AOV 计算中的一个被零除的错误后,让我执行一个安全的除法:

生成汇总表进行数据分析。图片作者。

…就这样,我们从使用一个 100 MB 的桌子,变成了一个“重量”只有 32 KB 的桌子;从的 364210 个记录到仅仅的 255 个记录。现在,我们已经为 EDA 做好了准备——在这个练习中,您可以随意与相应的 工作表 进行互动。

在我们继续之前,让我们确保每个人都明白一些指标(比如访问者)取决于我们数据的粒度级别。如果同一个人在一周的不同日子访问我们,我们将每周分别计算他们,这同样适用于所有其他维度和使用的箱。在我们的数据集中,这意味着虽然总的来说我们有 251,527 个独立访问者,但是当在设备平台上汇总这些访问者时,这个数字上升到 304,992 ,最终达到 334,820 个访问者,因为一天中有几个小时重复两次/三次计数。

星期几维度的数据汇总。作者图片

6。可视化探索性数据分析

上面的快照显示了我们对一周中的摘要的查询和结果。请注意,BigQuery 允许我们在 DataStudio 中执行快速数据分析——参见 ExploreData 按钮。DataStudio 让我们能够即时可视化我们的 EDA 流程——总结上述数据,我们可以看到,虽然大多数网站访问发生在周三,但最高的转化率发生在周五,而周末的转化率则大幅下降(也许人们在工作时更容易冲动购买,或者他们可能会为同事订购商品?)

一周中各天的时段和转换率。作者图片

请记住,这些数据是近 6 年前的,我们还可以注意到,虽然移动设备占总流量的四分之一以上,但只有 7%的订单是在移动设备上下单的。这是对我们一般假设的一个很好的描述,表明光是销量并不是销售结果的最佳预测,毕竟所有的桌面设备都享受了近 5(!)的转化率比移动端高出一倍[分别为 4.1%对 0.85。]

按设备划分的会话和订单。图片作者。

就会话和订单而言,Mac 似乎是最受欢迎的操作系统,其次是 Windows。MAC 在所有设备中也显示出最高的转换率[5.3%],Windows 设备的 CVR 低两倍,加上同组中最高的 AOV[209.20 美元],高出一倍半,在 Windows 上产生 1.32 美元的每次会话销售结果(汇总的所有设备中的最低数字),而 MAC 为 1.63 美元。

按操作系统划分的会话和订单。图片作者。

我们是否应该考虑开展地理定位活动?有趣的是,密歇根州在美国各州中拥有最高的每次会话销售额[$12.71],可惜它只占所有流量的 0.7%。说到 AOV,威斯康星州以 400.89 美元领先,但是这个结论可能为时过早,因为只有一个来自威斯康星州的订单。弗吉尼亚人在 AOV 是一个更现实的领导者,他们下了 309 美元和 73 份订单(占总数的 0.6%)。指出数据捕获限制也很重要,这会导致一半的会话在数据集中没有可用的状态信息。

各州每时段销售额。图片作者。

常识表明,用户参与度(每次会话的页面数、网站停留时间、每位访问者的访问次数等)是销售的最佳预测指标。让我们总结一下我们之前创建的站点时间箱。注意,虽然 60% (!)的会议持续了不到一分钟,他们只带来了 0.06%的订单。当谈到超过 20 分钟的时段时,情况正好相反——虽然这些访问者负责 26 分之一的网站访问,但他们带来了三分之一的订单。这是由 25.4%的最高 CVR、218.49 美元的最高 AOV 和 55.57 美元的最高单日销售额实现的。这些指标的第二名由15-20 分钟组别占据,而10-15 分钟组别获得铜牌:

按持续时段划分的绩效。图片作者。

基于我们的 EDA,我们能够验证 CVR 周末的工作日差异(数量和效率均下降)、桌面设备(尤其是 Macintosh)不成比例的更高 CVR、来自某些州的用户每次购买花费更多并表现出更高的交易倾向(密歇根州、纽约州等)。)我们还证明了更高水平的参与度指标(如页面浏览量或网站停留时间)与所下订单的相关性。现在,我们已经准备好验证这些客户和平台属性中的哪一个最适合识别具有事务的会话。

敬请关注下一篇文章,在这篇文章中,我们将在 BigQuery ML 中运行逻辑回归来运行我们的预测。

哪个发现最能引起你的共鸣?请在评论区分享一些你的有趣观察。

用机器学习预测糖尿病——第一部分

原文:https://towardsdatascience.com/predicting-diabetes-with-machine-learning-part-i-f151cb764aee

预测糖尿病的不同 ML 模型综述

Towfiqu barbhuiya 在 Unsplash 上的照片

本文是我将使用不同的机器学习模型分析 scikit-learn 提供的“糖尿病数据集”的两篇系列文章中的第一篇。这些文章的目的是让读者了解在做 DS 项目时如何分析数据。此外,我想给出当我们获得一些数字结果时,我们应该期望的‘数字意义’;事实上,当我们计算一些指标来评估我们的模型时,我们如何知道我们获得的数字有多好来评估一个模型?

此外,在第二篇文章写完后,我会提供到我的 GITHUB 库的链接和完整的代码。

在分析数据之前,重要的是要理解我们要做什么,给机器学习做什么一个“实际意义”。这个数据集是一个数据集合,其中我们可以看到一些与人相关的指标(年龄、性别、身体质量指数等)..)已经被赋予了对应于糖尿病的某一发展的值。找到一个预测糖尿病进展的好的 ML 模型可能是有用的,例如,在未来开发一个应用程序,我们在其中提供输入(年龄、性别、身体质量指数等),由于开发的 ML 模型,应用程序会告诉你糖尿病的进展。这可能对人们有所帮助,因为我们可以在进行临床试验之前预测糖尿病的某种进展。

1.探索性数据分析

让我们导入所有必要的库,并做一些 EDA 来理解数据:

import pandas as pd
import numpy as np#plotting
import seaborn as sns
import matplotlib.pyplot as plt#sklearn
from sklearn.datasets import load_diabetes #importing data
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn import linear_model
from sklearn import metrics
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import KFold
from sklearn.linear_model import Lasso
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import RepeatedKFold
from sklearn.model_selection import RandomizedSearchCV
from sklearn.preprocessing import PolynomialFeatures#scipy
from scipy.stats import loguniform

从 scikit-learn 导入数据:

#importing dataset
diab = load_diabetes()#defininf feature and label
X = diab['data']
y = diab['target']#printing shapes
print(X.shape, y.shape)------------------------>>> (442, 10) (442,)

所以,X 有 442 行 10 列;y 有 442 行(和 1 列)。现在,我想从 X 和 y 创建一个数据框:

#creating dataframe from X
df = pd.DataFrame(X, columns=["age","sex","bmi","bp", "tc", "ldl", "hdl","tch", "ltg", "glu"])#adding 'progression' from y
df['progression'] = diab['target']#showing head
df.head()

糖尿病数据框。图片作者。

现在,我们检查空值:

#showing dataframe info
df.isnull().sum()------------------>>>age            0
sex            0
bmi            0
bp             0
tc             0
ldl            0
hdl            0
tch            0
ltg            0
glu            0
progression    0
dtype: int64

没有 Nans,可以用df.describe()看到一些统计数据。从该函数中,我们可以看到,除了“progression”之外,所有列都具有相同的标准偏差:这意味着它们都具有相同的均值偏差(这也应该是已知的,因为在 scikit-learn 中提供的文档中;比如试试print(diab[‘DESCR'])看描述)。此外,除了几个例外,我们看到所有列的所有值(平均值、最小值、最大值和百分位数)都具有相同的数量级(不包括“级数”)。

因此,现在我们可以继续绘制相关矩阵,以查看数据中的最终相关性:

#figure size
plt.figure(figsize=(10, 7))#correlation matrix
dataplot = sns.heatmap(df.corr(), annot=True, fmt='.2f')

相关矩阵。图片作者。

糖尿病的进展和各种特征之间似乎没有很大的相关性。与“ltg”或“拉莫三嗪”中度相关,相关系数为 0.57,与身体质量指数或“体重指数”中度相关,相关系数为 0.59。

所以我想看看是否有可能在身体质量指数和 ltg 与疾病进展之间找到某种联系:

#figure size
plt.figure(figsize=(10, 7))#plotting graphs
sns.pairplot(df[['bmi', 'ltg', 'progression']])

“bmi”、“ltg”和“进展”之间的关系。图片作者。

在上面的图表中,我们可以看到 ltg 和级数以及身体质量指数和级数之间的关系。有数据增厚,但也能给出关系类型是线性的想法。让我们试试线性回归图:

#figure size
plt.figure(figsize=(10, 7))#regression between bmi and progression
sns.regplot(data=df, x='bmi', y='progression',line_kws={"color": "red"})#labeling
plt.title('BMI VS PROGRESSION PLOT')
plt.xlabel('BMI')
plt.ylabel('PROGRESSION')

MBI 与级数的关系。图片作者。

用类似的代码:

LTG 与级数的关系。图片作者。

在这两种情况下,线周围的数据的离散度有点太大,但另一方面,已经可以看出相关性是适度的。

在上面看到的分析之后,我继续选择学习模型,将所有值列视为特征(X ),将最后一列视为标签(y ),其指示疾病的进展(progression)。这是一个回归问题,因为 y 中的值是连续的。

让我们试着应用线性回归模型,从简单开始,看看我们会得到什么。

2.简单线性回归模型

首先,我们必须注意,无论是谁发布了数据集,这些特性都已经被缩放了(参见 scikit-learn 上的数据文档),因此没有必要这样做。

现在,让我们定义特征和标注,并拟合线性回归模型:

#defining features
X = df.iloc[:,:-1]#defining label
y = df['progression']#splitting
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,random_state=42)#fitting the model
reg = LinearRegression().fit(X_train, y_train)#predictions
y_test_pred = reg.predict(X_test)
y_train_pred = reg.predict(X_train)

然后,让我们打印一些指标:

#intercept
print(f'the intercept is:{reg.intercept_: .2f}')#slope
print(f'the slope is:{reg.coef_}')#R^2
print(f'Coeff. of determination on train set:{reg.score(X_train, y_train): .2f}') #train set
print(f'Coeff. of determination on test set:{reg.score(X_test, y_test): .2f}') #test set------------------->>>the intercept is: 151.35the slope is:[  37.90031426 -241.96624835  542.42575342  347.70830529 -931.46126093
  518.04405547  163.40353476  275.31003837  736.18909839   48.67112488]Coeff. of determination on train set: 0.53
Coeff. of determination on test set: 0.45

r,或者说决定系数,告诉我们数据的可变性和模型的正确性之间的联系;R 越接近 1 越好,因为这意味着模型是准确的。获得的结果不令人满意,R 接近 0.5,即使测试和训练组之间的值相似是好的。这是在告诉我们,这种类型的模型不适合这种类型的问题。无论如何,我想调查几个指标:

#model metricsprint(f'The mean absolute error is:{metrics.mean_absolute_error(y_test, y_test_pred): .2f}')print(f'The root mean squared error is:{np.sqrt(metrics.mean_squared_error(y_test, y_test_pred)): .2f}')---------------------->>>The mean absolute error is: 42.79
The root mean squared error is: 53.85

这些都是高值 (MAE 应该“尽可能接近”0),这继续让我对这个模型的使用感到气馁…但是我想做几个可视化。

首先,我想做一个真实值相对于预测值的散点图:

#image dimensions
plt.figure(figsize=(10, 7))#scatterplot of y_test and y_test_pred
plt.scatter(y_test, y_test_pred)
plt.plot(y_test, y_test, color='r')#labeling
plt.title('ACTUAL VS PREDICTED VALUES (TEST SET)')
plt.xlabel('ACTUAL VALUES')
plt.ylabel('PREDICTED VALUES')#showig plot
plt.show()

线性回归模型:实际值与预测值。图片作者。

从上图中可以看出,斑点没有明显的线状分布趋势,这证实了线性回归不是一个好的模型。

无论如何,我还希望看到核密度估计的图表,这有助于我们直观地看到随机变量的概率密度,介于真实值和线性模型预测的值之间:

#image dimensions
plt.figure(figsize=(10, 7))#Kernel Density Estimation plot
ax = sns.kdeplot(y_test, color="r", label="Actual Value") #actual values
sns.kdeplot(y_test_pred, color="b", label="Predicted Values", ax=ax) #predicted values#showing title
plt.title('Actual vs Precited values')
#showing legend
plt.legend()
#showing plot
plt.show()

线性回归模型的 KDE。图片作者。

从图中可以看出,预测值的概率密度与真实值的概率密度并不十分接近。

所以,最后,我们必须换一种不同的模式。

在这些文章的下一个系列中,我们将会看到其他的 ML 模型,并且我们将会像我们在这里所做的那样,基于一个完整的分析,为这个 ML 问题选择最好的一个。你在这里找到第二部分。

需要 Python 和数据科学方面的内容来开始或促进你的职业生涯吗?下面是我的一些文章,可以帮到你:

蟒蛇:

  • Python 中的循环和语句:深入理解(附示例)
  • Python 循环:如何在 Python 中迭代的完整指南
  • 学习 5 个 Python 库,开始你的数据科学生涯
  • 数据科学如何学习 Python

数据科学:

  • 即使全职工作(或学习)也要如何学习数据科学
  • 如何处理数据科学中的缺失值
  • 如何在数据科学项目中进行特征选择
  • 如何检测数据科学项目中的异常值
  • 执行图形残差分析的两种方法
  • 条形图和柱状图有什么区别?
  • 相关和回归的区别
  • 了解 l1 和 l2 正规化
  • 逻辑回归:让我们搞清楚吧!
  • 什么是训练有素的模特?
  • 如何利用学习曲线轻松验证您的 ML 模型

考虑成为会员:你可以免费支持我和其他像我一样的作家。点击 这里的成为会员。

用机器学习预测糖尿病——第二部分

原文:https://towardsdatascience.com/predicting-diabetes-with-machine-learning-part-ii-a6f703e8cf04

预测糖尿病的不同 ML 模型概述的最终部分

迈肯齐·约翰逊在 Unsplash 上拍摄的照片

这是不同机器学习模型概述的第二部分,我使用 scikit-learn 库提供的著名的“糖尿病数据集”来比较它们在预测糖尿病方面的差异。

你可以在这里找到第一部《T4》。因为我已经通过不同的 ML 模型来比较它们,所以你先读第一部分是有准备的。此外,在第一部分,你会发现完整的探索性数据分析。

此外,在本文的最后,您会发现我的 GitHub 存储库,我在那里存储了该分析的完整代码。

我们在第一部分的结论中说,简单的线性回归模型对于这个 ML 问题不是一个好的模型;让我们看看如果我们尝试正则化模型会发生什么

[EDIT 04/06/2022]我要感谢 Francis Van Schie 与我联系,表明我在拟合多项式方法时犯了一个错误。现在错误已经被修正了。

1.线性正则化回归模型:Lasso 模型

我想尝试线性回归的正则化模型,我选择 Lasso 回归,因为当变量之间存在高度相关性时会使用 Ridge,但相关矩阵表明情况并非如此。

#defining the lasso model
model = Lasso()#define model evaluation method
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)# efine grid
grid = dict()
grid['alpha'] = np.arange(0, 1, 0.01)#define search
search = GridSearchCV(model, grid, scoring='neg_mean_absolute_error', cv=cv, n_jobs=-1)#performing the search on the train dataset
results = search.fit(X_train, y_train)#printing
print(f'MAE:{results.best_score_: .2f}')
print(f'Best Alpha:{results.best_params_}')------------------------>>>MAE:-44.86
Best Alpha:{'alpha': 0.01}

最好的 alpha 值是 0.01,它给了我一个相当高的平均绝对误差(MAE 为负的事实并不重要:sklearn 因为其优化原因而使其为负;在任何情况下,要成为一个好值,它应该“更接近”于 0)。还需要补充的是,alpha = 0.01 是一个“非常小”的值;alpha = 0 是正态(非正则化)回归的情况,除了高 MAE,告诉我这个模型对于解决这种类型的 ML 问题不是太好。

除了数字的符号之外,MAE 实际上与简单回归方法相同。在上面的例子中,网格是均匀的;现在,我想尝试使用“loguniform”方法来扩展它:

# define model
model = Lasso()# define evaluation
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)# define search space
space = dict()
space['alpha'] = loguniform(1e-5, 100)
space['fit_intercept'] = [True, False]
space['normalize'] = [True, False]#define search
search = RandomizedSearchCV(model, space, n_iter=500, scoring='neg_mean_absolute_error', n_jobs=-1, cv=cv, random_state=1)# execute search
result = search.fit(X, y)#printing
print(f'MAE:{results.best_score_: .2f}')
print(f'Best Alpha:{results.best_params_}')----------------------->>>MAE:-44.86
Best Alpha:{'alpha': 0.01}

我发现与之前相同的最佳 alpha 值,因此我将使用 alpha = 0.01 进行拟合,以评估 Lasso 类型的正则化回归模型的性能。我预计性能会很差,因为 MAE 实际上与简单线性回归模型中看到的值相同,但是让我们来看看:

#lasso with best alpha
model_best = Lasso(alpha=0.01).fit(X_train, y_train)#predictions
y_test_pred = model_best.predict(X_test)
y_train_pred = model_best.predict(X_train)#R^2
print(f'Coeff. of determination on train set:{model_best.score(X_train, y_train): .2f}') #train set
print(f'Coeff. of determination on test set:{model_best.score(X_test, y_test): .2f}') #test set--------------------------->>>Coeff. of determination on train set: 0.53
Coeff. of determination on test set: 0.46

这些值与用简单线性回归模型获得的值太相似了;这告诉我们,即使是正则化的方法也不是一个好方法。这是意料之中的,因为最好的 alpha 是 0.01,我们必须记住 alpha=0 是简单的线性回归情况。

无论如何,让我们来看几个形象化的例子:

#figure size
plt.figure(figsize=(10, 7))#scatterplot of y_test and y_test_pred
plt.scatter(y_test, y_test_pred)
plt.plot(y_test, y_test, color='r')#labeling
plt.title('ACTUAL VS PREDICTED VALUES (TEST SET)')
plt.xlabel('ACTUAL VALUES')
plt.ylabel('PREDICTED VALUES')#showig plot
plt.show()

线性回归的实际值与预测值(通过 Lasso 模型)。图片作者。

从上图可以看出,光斑没有明显的分布在线周围的趋势。现在,让我们来看看 KDE:

实际值和预测值(通过 Lasso 模型)的 KDE。图片作者。

从图中可以看出,预测值的概率密度根本不接近真实值的概率密度。

最后,我对残差进行图形分析:

#figure size
plt.figure(figsize=(10, 7))#residual plot
sns.residplot(x=y_test, y=y_test_pred)#labeling
plt.title('REDISUALS VS PREDICTED VALUES')
plt.xlabel('PREDICTED VALUES (DIABETES PROGRESSION)')
plt.ylabel('REDISUALS')

残差与预测值(通过 Lasso 模型)。图片作者。

残差是随机分布的(上面的图中没有明确的模式),这告诉我们选择的模型并不完全不好,但残差的高值(甚至超过 100)太多了,这意味着模型的误差很高。没有低估或高估价值的特别倾向;然而,有一点倾向于具有高误差,特别是在具有低疾病进展值的区域,而对于高进展值,误差稍微减小,除了一些异常值。

因此,该图也证实了这样一个事实,即线性回归模型(尽管是规则化的)对于这个 ML 问题不是一个好的模型,必须寻找另一个模型。因此,我们必须尝试不同的模型:让我们尝试多项式回归方法。

2.多项式回归方法

考虑到 MSE 和 RSME 的值以及所看到的图形,我尝试了增加多项式次数的方法;也就是我尝试多项式回归。

考虑到之前获得的结果,我将直接使用 3 次多项式,因为 2 次对我来说似乎有点短。然而,我不想通过插入一个太高的程度来夸大其词,因为这里是一个通过转换可用数据直接进行拟合,然后使用线性回归中已经看到的函数的问题;在实践中:如果我使用一个过高的多项式次数,我会冒在训练集上过度拟合的风险。

我创建了三次和分裂多项式函数:

#creating the 3rd degree polinomial
poly = PolynomialFeatures(degree=3, include_bias=False)#transforming the values in all X
poly_features = poly.fit_transform(X)#splitting
X_train3, X_test3, y_train3, y_test3 = train_test_split(poly_features,y, test_size=0.2,random_state=42)

创建多项式回归:

#creating the polynomial regression
poly_reg = LinearRegression()#fitting
poly_reg.fit(X_train3, y_train3)#predictions
y_test3_pred = poly_reg.predict(X_test3)
y_train3_pred = poly_reg.predict(X_train3)

打印指标:

#R^2#train set
print(f'Coeff. of determination on train set:{poly_reg.score(X_train3, y_train3): .2f}') #test set
print(f'Coeff. of determination on test set:{poly_reg.score(X_test3, y_test3): .2f}')--------------->>>Coeff. of determination on train set: 0.88
Coeff. of determination on test set: -17.42

该模型在训练集上的决定系数比线性回归模型好得多。但是,测试集上的决定系数在值上下降(相对于测试集上的决定系数),这使我想到这里出现的一个明显的情况:过拟合(在测试集上)!让我们看看其他指标:

#model metricsprint(f'The mean absolute error is:{metrics.mean_absolute_error(y_test3, y_test3_pred): .2f}')
print(f'The root mean squared error is:{np.sqrt(metrics.mean_squared_error(y_test3, y_test3_pred)): .2f}')------------>>>The mean absolute error is: 169.65
The root mean squared error is: 312.43

MAE 和 MSE 比以前的线性模型计算出的要高得多!!我制作了一张实际值与预测值比较的散点图:

实际值与预测值(通过多项式模型)的对比。图片作者。

记住,我们已经用三次多项式函数变换了起始域,正如我们现在可以看到的,数据在一条线(变换域中的一条线)周围非常明显地变粗了,但是有很高的异常值,这使我认为这个模型并不像我认为的那样适合这个特定的 ML 问题。最后,我也想和 KDE 一起做一个视觉化的展示:

实际值与预测值(通过多项式模型)的 KDE 图。图片作者。

KDE 证实了多项式回归确实不是这里使用的好模型。此外,由于过度拟合,这是目前看到的最差的模型。

当我向一位资深数据科学家展示这项工作时,他告诉我:“很好;但是你知道一个 ML 模型,它‘转换’你的数据,在它们之间留下线性关系吗?”

我的回答是“是的!”模型是支持向量回归机。我试过 SVR,但是效果很差;既然您已经理解了这个方法,我就不在这里赘述了:如果您想看一看(和/或自己尝试一下),您可以在我的 GitHub repo 中找到结果。

结论

在这一系列文章中,我们已经看到了不同的模型如何在给定的数据集上执行,以及我们可以从所使用的度量中得到什么;尝试不同的 ML 模型是我们必须做的,以找到一个可以给我们最好的预测;在这种情况下,我们发现线性回归模型在我们尝试的所有模型中是最好的,但确实不是绝对好;所以必须尝试其他模型来解决这个 ML 问题。

感谢阅读!

你可以在这里找到我的 GitHub repo 的完整代码。

让我们连在一起!

中型

LINKEDIN(向我发送连接请求)

如果你愿意,你可以 订阅我的邮件列表这样你就可以一直保持更新了!

考虑成为会员:你可以免费支持我和其他像我一样的作家。点击 这里成为会员。

用随机森林分类器预测糖尿病

原文:https://towardsdatascience.com/predicting-diabetes-with-random-forest-classifier-c62f2e319c6e

建立机器学习模型,预测患者是否患有糖尿病。

医疗诊断对于医疗专业人员来说是一个非常重要和关键的方面。特别是,糖尿病患者的分类非常复杂。糖尿病的早期识别对控制糖尿病非常重要。患者必须经过几次测试,之后专业人员很难在诊断过程中跟踪多个因素,这可能导致不准确的结果,使得检测非常具有挑战性。由于最先进的技术,尤其是机器学习算法,非常有利于在医疗保健行业快速准确地预测疾病。

来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

照片由大卫·莫鲁齐在 Unsplash 上拍摄

概观

该数据集最初由“国家糖尿病、消化和肾脏疾病研究所”收集和传播,该数据集可在 Kaggle 以 皮马印第安人糖尿病数据库 的名称获得。主要目的是基于在数据库中收集的诊断测量来预测患者是否患有糖尿病。所有患者都属于皮马印第安遗产,并且是 21 岁以上的女性。

我们首先将 Pandas 和 NumPy 导入到我们的 python 环境中,并将 加载一个. csv 数据集 到名为 df 的 pandas dataframe 中。为了查看数据集中的前五条记录,我们使用 pandasdf . head()函数。我们还将使用 seaborn 和 matplotlib 进行可视化。本文中的每一个例子都在 Jupyter 笔记本上得到验证。

该数据集包含 768 个可观测值,其中有八个特征变量和一个目标变量。在开始分析数据并得出任何结论之前,有必要了解任何数据集中是否存在缺失值。要做到这一点,最简单的方法是使用 df.info() 函数,该函数将为我们提供列名以及每列中非空值的数量。

根据输出,我们没有观察到任何空值。但是有五个特征如葡萄糖、血压、皮肤厚度、胰岛素和身体质量指数包含零值,这在医学史上是不可能的。我们将这些值视为缺失值。我们将把零值替换为 NaN,然后用它们的平均值估算它们。

数据可视化

每个列之间的关联使用热图可视化。从输出来看,颜色越浅表示相关性越强。我们注意到成对特征之间的相关性,比如年龄和怀孕,或者身体质量指数和皮肤厚度等。

sns.heatmap(df.corr())

为了在数据集中绘制成对关系,我们使用 sns.pairplot() 函数,并根据目标变量类标记数据点..

sns.pairplot(df,hue='Outcome ')

分类

我们需要将数据集分成特征和目标变量。按照流行的惯例,我们称带有特征变量的数据帧为 X,带有目标变量的数据帧为 y。

X=df.drop('Outcome',axis=1)
y=df['Outcome']

让我们将目标变量可视化,看看数据集中有多少人患有糖尿病,有多少人没有。

使用 sklearn 的 train_test_split,我们将特征(X)和目标(y)数据帧分成训练集(80%)和测试集(20%)。训练集用于建立分类模型,测试集用于评估模型的性能。

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=0.2,random_state=0)

在实现分类算法之前,我们使用 sklearn 的 StandardScaler() 函数缩放我们数据集的特征变量。该函数通过移除平均值并缩放至单位方差来标准化特征。

from sklearn.preprocessing import StandardScaler
scaling_x=StandardScaler()
X_train=scaling_x.fit_transform(X_train)
X_test=scaling_x.transform(X_test)

培训和评估模型

我们将使用一个名为 随机森林分类器 的机器简单学习模型。我们使用训练数据集用标准参数训练模型。训练好的模型保存为“RCF”。我们使用测试数据集来评估我们的模型的性能。我们的模型有 80.5%的分类准确率。

**from** sklearn.ensemble **import** RandomForestClassifier
rfc **=** RandomForestClassifier()
rfc**.**fit(X_train, y_train)
rfc**.**predict(X_test)
rfc**.**score(X_test, y_test)

输出:

0.8051948051948052

绘制决策边界

决策边界图仅适用于两个要素。我们的数据有八个特征,但我们仍然可以通过选择使用哪些特征来绘制决策边界。我们为每两个可能的特征绘制决策边界,并观察模型对患者的分类效果。

**from** mlxtend.plotting **import** plot_decision_regions
**def** classify_with_rfc(X,Y):
    x **=** df[[X,Y]]**.**values
    y **=** df['Outcome']**.**astype(int)**.**values
    rfc **=** RandomForestClassifier()
    rfc**.**fit(x,y)
    *# Plotting decision region*
    plot_decision_regions(x, y, clf**=**rfc, legend**=**2)
    *# Adding axes annotations*
    plt**.**xlabel(X)
    plt**.**ylabel(Y)
    plt**.**show()

feat **=** ['Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness', 'Insulin','BMI', 'DiabetesPedigreeFunction', 'Age']
size **=** len(feat)
**for** i **in** range(0,size):
    **for** j **in** range(i**+**1,size):
        classify_with_rfc(feat[i],feat[j])

注意:0 — Non Diabetic and 1 — Diabetic

分布显示我们的模型对病人的分类非常好。为了详细评估我们的模型,我们看一下 混淆矩阵

**from** sklearn.metrics **import** confusion_matrix
mat **=** confusion_matrix(y_test, y_pred)
plt**.**figure(figsize**=**(7, 5))
sns**.**heatmap(mat, annot**=True**)

**from** sklearn.metrics **import** classification_report
target_names **=** ['Diabetes', 'Normal']
print(classification_report(y_test, y_pred, target_names**=**target_names))**Output**: precision    recall  f1-score   support

    Diabetes       0.86      0.86      0.86       107
      Normal       0.68      0.68      0.68        47

    accuracy                           0.81       154
   macro avg       0.77      0.77      0.77       154
weighted avg       0.81      0.81      0.81       154

ROC 曲线

**from** sklearn.metrics **import** roc_curve
y_pred_proba **=** rfc**.**predict_proba(X_test)[:,1]
fpr, tpr, thresholds **=** roc_curve(y_test, y_pred_probaplt**.**plot([0,1],[0,1],'k-')
plt**.**plot(fpr,tpr, label**=**'Knn')
plt**.**xlabel('fpr')
plt**.**ylabel('tpr')
plt**.**title('ROC curve')
plt**.**show()**Output**:

**from** sklearn.metrics **import** roc_auc_score
roc_auc_score(y_test,y_pred_proba)**Output:**
0.8535494134022669

对于我们的模型,受试者工作特征曲线( ROC AUC )得分下的面积为 85%。这意味着分类模型足够好来检测糖尿病患者。

真实值与预测值:

摘要

  • 我们建立了一个基于机器学习的分类器,根据数据库中提供的信息预测患者是否患有糖尿病。
  • 构建此预测器时,我们了解了常见的预处理步骤,如要素缩放和输入缺失值。关于在 python 中处理缺失值的更多详细解释,请参考我的文章这里。
  • 我们实现了随机森林算法,使用准确度分数评估了性能,比较了训练和测试数据之间的性能。您还可以调整参数,并尝试提高准确性得分 AUC。
  • 完整的 python 脚本可以在 Github 中找到。

这就把我们带到了本文的结尾。感谢您通读这篇文章。如果你有任何疑问,欢迎在评论区留言。

通过 MLP +伯特预测论文有效性

原文:https://towardsdatascience.com/predicting-essay-effectiveness-through-mlp-bert-4e8e8d6562ad

利用 BERT 的分类和数值特征改进模型

照片由弗洛里安·克劳耶在 Unsplash 拍摄

有效写作一直是确保有效交流思想、批判性思维和说服力的关键。写作作为一种技能是在现代社会取得成功的必要条件。然而,尽管全球识字水平正在缓慢提高,但各国和社会人口群体之间的增长并不均衡。例如,多项研究表明,美国存在教育不平等的例子。这种现象最终可能导致基尼系数不断扩大的恶性循环。

解决这个问题的一个办法是让学生评估自己的写作效果。通过开发自动指导,学生可以评估他们自己的写作效果,这使他们能够用更少的专用资源更高效地学习。这个解决方案可以是一个网络应用程序的形式,学生上传他们的议论文,并给出一个输出来评估论文的有效性

这项比赛的目标是将学生写作中的议论文要素分为“有效”、“足够”或“无效”。

数据集

数据集由 Kaggle 通过佐治亚州立大学提供。本文中不会有数据分布。数据可以在卡格尔上找到。

数据探索

从表面上看,所提供的数据集只包含一列文本语料库。然而,我们可以通过上下文知识设计多个特征,这将使我们的模型表现得更好。

引文

每个大学生都知道引用对提高你的文章的有效性有多重要。因此,如果在文本正文中检测到任何“源”,我们创建一个标记真/假的特征。

很明显,有效的文章有“源”在场。

拼写错误

利用 python 库 tqdm 识别最常见的拼写错误。我没有把它做成一个二元特征(是/没有拼写错误),而是把它做成一个带有拼写错误数量的数字特征。

虽然所有目标特征的中值分数大致相同,但显然无效文章的分布范围更广。

字数

与其他课程相比,有效的文章有相对较高的单词中位数

极性和主观性

极性和主观性很大程度上源自 python 包 textblob ,它提供了一个简单的 NLP API。文本语料库的极性被评分在-1 到 1 之间,其中-1 表示负面情绪,+1 表示正面情绪。

文本语料库的主观性评分在 0 到 1 之间,其中 0 代表客观性,1 代表主观性。基于数据集,与其他班级相比,有效论文通常倾向于具有稍高的中位数。

数据建模

用橙色突出显示的是通过多模态工具包处理的工具包。数据预处理代码片段可以在下面找到。我对分类特征使用了一键编码,对数字特征使用了 yeo-johnson 变换,因为数字特征中的一些数据可能非常小或者是负数。

默认变换是 One Hot 编码和 yeo-johnson 变换

定义指标

因为这是一个多类、分类问题。使用准确度、F1 分数和训练损失来评估结果。结果在准确性方面实现了边际增加(0.63 到 0.68),而在 F1 方面实现了更高的增益(0.46 到 0.59)。

损失函数表明存在收敛,但在 10k 步后达到稳态。

不同训练的评估损失。

结论

在第一轮比赛中,我采用了一个普通的 BERT 模型,并在语料库上对它进行了训练。然而,结果并不一致,结果很差。

香草伯特模型的第一次迭代

在第二次迭代中,我创建了额外的功能,为问题陈述提供了背景,很像教师在评估论文有效性时直观使用的评分标准或清单。这使得所有性能指标的模型性能都有了很大的提高。如果我们使用多类对数损失对其进行评估,结果将更容易解释,因为它输出了每类的概率。有关性能评估的更多信息,请参考我的 github repo ,我在那里上传了 EDA 和建模笔记本。

另一个可以实现的关键改进是将话语类型作为特征分析的一部分。如果不包含这个特性,它就类似于用反驳的方式来评估引导段落。

消息来源

[1]数据集:https://www . ka ggle . com/competitions/feedback-prize-effectiveness。

[2]造型:https://github.com/georgian-io/Multimodal-Toolkit

使用 PyTorch 预测 RNN、LSTM 和 GRU 的未来值

原文:https://towardsdatascience.com/predicting-future-values-with-rnn-lstm-and-gru-using-pytorch-d9ef50991ec7

恩库鲁列科·乔纳斯在 Unsplash 上的照片

使用 PyTorch 预测 RNN、LSTM 和 GRU 的未来值

将算法用于预测未来价值

在我之前的博文中,我帮助你开始使用 PyTorch 构建一些递归神经网络(RNN),比如香草 RNN、LSTM 和 GRU。如果您还没有看过,我强烈建议您先看一下,因为我将基于我在那里提供的一些概念和代码进行构建。同样,在这篇文章中,我也将坚持使用相同的数据集, PJM 的每小时能耗。上次我们停止了对测试集进行预测,并根据实际值评估模型性能。尽管有一个好的开始,我们还是错过了拼图的一部分。

如果我们被要求对没有实际值的时间步长进行预测,该怎么办?这通常是时间序列预测的情况;我们从历史时间序列数据开始,预测接下来会发生什么。这篇文章将向你展示如何使用我们之前创建的 RNN、LSTM 和 GRU 模型来预测未来的价值。所以,不像那一个,这将是一个相对较短的一个——我希望。

使用日期时间功能进行预测

为了生成对未来的预测,我向类优化添加了一个新函数,该函数与方法评估一样,采用预测 _ 加载器批量 _ 大小n _ 特征,并生成对未来的n _ 步数的预测。与评估不同,生成的预测不受数据帧中实际值的约束,而是受作为输入给出的步数的约束。当然,这只是一种方法,但是你也可以通过限制你的预测日期来尝试一下。

根据我们正在处理的数据集的频率,我们可以选择每小时、每天或每月进行预测。一种方法是将这些参数手动传递给我们的预测函数。嗯,我想这还不够有趣。我们还可以编写一个简单的函数来获取数据集的开始日期和时间频率。它只是获取最后一个 DateTimeIndex 值,并加上前一个索引之间的时间间隔。至于频率,类似地,我们将返回这两点之间的时间间隔。使用这两个值, start_datefreq ,我们可以生成跨越预测期的 DateTimeIndex

就像我们之前对历史数据进行预测一样,我们现在将使用日期时间要素、一键编码、循环要素和节假日来生成要素。

图片作者。

现在,是时候为预测值创建一个数据加载器实例了。你可能已经在想了,“没有实际值,我们到底要怎么填写目标值(y)。”我找到了一个快速解决问题的方法,只需将所有预测指数的列设置为 0,并忽略后面阶段的指数。

如果您检查类优化中的函数,名为 *forecast_with_predictors,*您可能会注意到我忽略了 DataLoader 实例的目标值部分。而且,公平地说,我不确定这是否接近最佳实践,所以如果您能想到更优雅的方法来解决它,请随时联系我。

接下来,我们调用优化forecast _ with _ predictors函数来生成预测。如您所料,该函数返回根据您在上一步中选择的变换缩放的值。

然后,我们需要将缩放后的预测值逆变换为原始目标值,就像我们之前使用 inverse_transform 所做的那样

图片作者。

我将使用 Plotly 包来绘制预测值,如下所示。正如你在下面看到的,这可能不是最好的预测。从这里开始,您可以通过许多方式来探索如何改善结果,无论是功能工程、超参数调整还是选择完全不同的模型。看你的了!

图片作者。

图片作者。

具有滞后特征的预测

用滞后观测值预测时间序列,或简称滚动时间序列,需要一种稍微不同的方法。与具有日期时间特征的时间序列不同,我们不能简单地填充未来的滞后时间观测值。相反,我们需要用每个新的预测更新下一步,并滚动时间窗口。所以,如果这部分看起来或多或少像是一个入门级的 CS 编程练习,相信你的直觉。

同样,我们将另一个方法 forecast_with_lag 添加到优化类中,以使用滚动逻辑生成预测。

DataLoader 类表示数据集上的 Python iterable。因此,下面一行通过迭代整个数据集来帮助获得一个数据加载器实例中的最后一个时间步长。

*_, (X, y) = test_loader_iter

下面一行将张量的第二维值移动一位,这样张量[[[x1, x2, x3, ... , xn ]]]就变成了[[[xn, x1, x2, ... , x(n-1)]]]

X = torch.roll(X, shifts=1, dims=2)

下面的行从 3D 张量的最后一个维度中选择第一个元素,并将该项设置为存储在NumPyndarray(yhat),[[xn+1]]中的预测值。然后,新的输入张量变成[[[x(n+1), x1, x2, ... , x(n-1)]]]

X[..., -1, 0] = yhat.item(0)

就像我们之前做的那样,我们可以为带有滞后观测值的时间序列生成预测,并绘制预测的未来值。为了避免重复,我将只分享几个时代的训练后的一些样本结果。但是你可以看看我在这篇文章中分享的 Google Colab 笔记本是如何做到的。

图片作者。

图片作者。

根据模型预测的值进行多次单步预测可能会在短期内产生合理的结果。随着预测周期的增加,预测变得越来越不准确,因此也越来越不适合预测。

此外,为了进行多次单步预测并在每次预测后更新输入,我们必须一个接一个地遍历数据集,就像我们在测试集上遍历 for 循环一样。毫不奇怪,这使我们失去了矩阵运算和小批量训练为我们提供的所有计算优势。

总而言之,我非常怀疑基于滚动的预测方法是一个好的实践。此外, LSTMGRU 可以很好地将过去的观测结果带到下一次观测,而不会遇到递减/爆炸梯度的问题。

最后的话

我没想到我会花这么长时间来跟进我的上一篇文章,但我很高兴我最终做到了。我希望这篇文章能帮助你更好地理解如何使用两种不同的方法来预测未来值。如果有任何你同意或不同意的地方,请不要犹豫评论或联系我。

最后,这里有一个链接到 Google Colab 笔记本,如果你想看完整的带有新预测方法的笔记本。在下一篇文章中,我计划回顾一些正则化技术及其在时间序列预测中的应用。但是我们会看到它如何发展。

用 PySpark 预测心脏病

原文:https://towardsdatascience.com/predicting-heart-disease-with-pyspark-5a871286946e

PySpark 二进制分类教程

照片来自 Unsplash 上的 Robina Weermeijer

简介

本指南将从头到尾向您展示如何构建和运行 PySpark 二元分类模型。

这里使用的数据集是来自 UCI 机器学习库的心脏病数据集。阿尔,1988)。如果在出版物中使用,关于该数据集的唯一说明/许可信息是引用作者。这是一个二元分类数据集。我们今天将使用 PySpark 来构建各种分类模型。

我最近发布了这个指南,展示如何从本地计算机连接 Jupyter 笔记本会话到 Linux 托管的 Apache Spark 独立集群。

今天,我还将向您展示如何在您的本地 Mac 笔记本电脑上设置并连接到一个独立的集群,供那些无法访问虚拟机的人使用。当然,在 Mac 上启动本地 PySpark 会话可能更容易,而不需要设置集群。但是,如果您想将 Mac 用作主节点并添加工作节点,这将向您展示如何操作。

设置独立集群

  1. 转到 Mac 终端窗口中的%SPARK_HOME%文件夹,并运行:
./sbin/start-master.sh

2.在另一个终端窗口中,转到%SPARK_HOME%文件夹并运行:

./sbin/start-worker.sh spark://ip:port

您可以使用相同的策略将网络上的任何工作节点添加到主节点。

注意:启动主节点后,如果在浏览器中转到 http://localhost:8080,就可以获得 spark://ip:port。

设置 Jupyter 笔记本

  1. 在 Mac 上的另一个终端窗口中运行:
jupyter notebook 

这将在浏览器中打开一个 Jupyter 笔记本会话。

将 Jupyter 笔记本连接到 Spark 集群。

1.在 Jupyter 笔记本中打开一个新的 Python 3 内核并运行:

import pyspark
import findspark
from pyspark import SparkConf, SparkContext
from pyspark.sql import SQLContext
from pyspark.sql.types import *
from pyspark import SparkConf, SparkContextfindspark.init('/path_to_spark/spark-3.1.2-bin-hadoop3.2') 
#The key here is putting the path to the spark download on your Mac
VM.sc=pyspark.SparkContext(master='spark://ip:port',appName='Heart_Disease_Example')
#Use the same 'spark://ip:port' from connecting the worker(s) to the master node. 
spark = SQLContext(sc)
spark

现在我们连接到我们的火花簇。开始建模吧。

建模

数据

我们的数据集是来自 UCI 机器学习知识库的心脏病数据集(Dubois 2008)。你可以在这里下载 csv 文件。

from pyspark.sql import functions as F
from pyspark.ml.feature import OneHotEncoder, StringIndexer, VectorAssembler
from pyspark.ml.feature import CountVectorizerfrom pyspark.ml.tuning import ParamGridBuilder
from pyspark.ml.evaluation import BinaryClassificationEvaluator
import numpy as np
from pyspark.ml.tuning import CrossValidator
import plotly.graph_objects as godf=spark.read.csv('heart.csv', inferSchema=True, header=True)
df.count()
len(df.columns)

作者图片

我们的数据集有 303 行和 14 列。是的,这种规模的数据集不需要 Spark。这个小数据集只是为了便于运行示例模型。

df.dtypes

作者图片

我们的数据都是整数/双精度,所以我们不需要为任何分类变量编码。

特征信息:

**1。年龄:**以年为单位的人的年龄

**2。性别:**人的性别(1 =男性,0 =女性)

3。cp: 所经历的胸痛(0 =典型心绞痛,1=非典型心绞痛,2=非心绞痛性疼痛,3 =无症状)

4。trestbps :患者的静息血压(入院时为毫米汞柱)

**5。**胆固醇:人的胆固醇测量值,单位为毫克/分升

6。fbs: 此人的空腹血糖(> 120 mg/dl,1 =真;0 =假)。

**7。静息心电图:**静息心电图测量(0 =正常,1 = ST-T 波异常,2 =根据 Estes 标准显示可能或明确的左心室肥大)

8。thalach: 人达到的最大心率

9。exang: 运动诱发心绞痛(1 =是;0 =否)

10。oldpeak: 运动相对于休息诱发的 ST 段压低

11。斜率:运动 ST 段峰值的斜率(0 =上升,1 =平缓,2 =下降)

12。ca: 主要血管的数量(0-4)

13。地中海贫血:一种叫做地中海贫血的血液疾病(3 =正常;6 =修复缺陷;7 =可逆转的缺陷)

14。目标:心脏病(0 =否,1 =是)

检查缺失值:

from pyspark.sql.functions import col,sum
df.select(*(sum(col(c).isNull().cast("int")).alias(c) for c in df.columns)).show()

作者图片

太好了。没有缺失值,因此我们不需要执行任何插补方法。

数据集概要

df.describe().toPandas().transpose()

作者图片

目标变量饼图:

df2=df.toPandas()
df22=df2.groupby('target').count().reset_index()[['target','age']].rename(columns={'age':'counts'})
colors = ['gold', 'mediumturquoise', 'darkorange', 'lightgreen']fig = go.Figure(data=[go.Pie(labels=df22.target,
                             values=df22.counts)])
fig.update_traces(hoverinfo='label+percent', textinfo='value+percent', textfont_size=20, textfont_color='black',
                  marker=dict(colors=colors, line=dict(color='#000000', width=2)))
# fig.show()
fig.update_layout(title='Heart Disease vs. Absence of Heart Disease', title_x=0.5)

作者图片

特征变量直方图:

from plotly.subplots import make_subplotsfig = make_subplots(rows=4, cols=4, start_cell="top-left",
                   subplot_titles=df2.columns[:-1])fig.add_trace(go.Histogram(x=df2.age, name='age'),
              row=1, col=1)fig.add_trace(go.Histogram(x=df2.sex, name='sex'),
              row=1, col=2)fig.add_trace(go.Histogram(x=df2.cp, name='cp'),
              row=1, col=3)fig.add_trace(go.Histogram(x=df2.trestbps, name='trestbps'),
              row=1, col=4)fig.add_trace(go.Histogram(x=df2.chol, name='chol'),
              row=2, col=1)fig.add_trace(go.Histogram(x=df2.fbs, name='fbs'),
              row=2, col=2)fig.add_trace(go.Histogram(x=df2.restecg, name='restecg'),
              row=2, col=3)fig.add_trace(go.Histogram(x=df2.thalach, name='thalach'),
              row=2, col=4)fig.add_trace(go.Histogram(x=df2.exang, name='exang'),
              row=3, col=1)fig.add_trace(go.Histogram(x=df2.oldpeak, name='oldpeak'),
              row=3, col=2)fig.add_trace(go.Histogram(x=df2.slope, name='slope'),
              row=3, col=3)fig.add_trace(go.Histogram(x=df2.thalach, name='ca'),
              row=3, col=4)fig.add_trace(go.Histogram(x=df2.thal, name='thal'),
              row=4, col=1)fig.update_layout(title='Histograms of Variables', title_x=0.5)

作者图片

Oldpeak 明显右倾。为了解决这个问题,我可以做一个 log(x+1)转换。由于我更倾向于使用基于树的模型来解决这个问题,所以我不太关心将数据转换成正态分布。这是因为它不是一个假设,例如,随机森林或梯度推进。但是对于像逻辑回归这样的东西,让数据正态分布是一个假设。不管怎样,让我们继续改造它吧。

df3=df.withColumn('oldpeaklog', F.log(df['oldpeak']+1))
df33=df3.toPandas()fig = make_subplots(rows=1, cols=2, start_cell="top-left",
                   subplot_titles=['oldpeak','oldpeaklog'])fig.add_trace(go.Histogram(x=df33.oldpeak, name='oldpeak'),
              row=1, col=1)fig.add_trace(go.Histogram(x=df33.oldpeaklog, name='oldpeaklog'),
              row=1, col=2)fig.update_layout(title='Transforming oldpeak', title_x=0.5

作者图片

经过转换后,它看起来更正态分布。

关联矩阵热图:

corr = df33.corr()
fig = go.Figure(data=go.Heatmap(z=corr.values,
 x=corr.index.values,
 y=corr.columns.values,
 text=np.round(corr.values,2),
 texttemplate=”%{text}”))fig.update_layout(title=dict(text=’Correlation Matrix Heatmap’,font=dict(size=20), x=0.5))

作者图片

斜率和 oldpeaklog 之间的最高相关性为-.59,或者斜率和 oldpeak 之间的最高相关性为-.58。其他都低于 0.5 或高于 0.5,即更接近于 0。因此,我决定保留模型中的所有变量。

初始化阶段

#Initialize stages
stages = []#Target column
label_stringIdx = StringIndexer(inputCol = 'target', outputCol = 'label')
stages += [label_stringIdx]#Numeric Columns
numericCols = ['age',
 'sex',
 'cp',
 'trestbps',
 'chol',
 'fbs',
 'restecg',
 'thalach',
 'exang',
 'slope',
 'ca',
 'thal',
 'oldpeaklog'] #Create a vector assembler
assemblerInputs = numericCols 
assembler = VectorAssembler(inputCols=assemblerInputs, outputCol="features").setHandleInvalid('keep')
stages += [assembler]

架设管道

from pyspark.ml import Pipeline
pipeline = Pipeline(stages = stages)
pipelineModel = pipeline.fit(df3)
df3 = pipelineModel.transform(df3)
selectedCols = ['label', 'features'] + 
['age',
 'sex',
 'cp',
 'trestbps',
 'chol',
 'fbs',
 'restecg',
 'thalach',
 'exang',
 'slope',
 'ca',
 'thal',
 'oldpeaklog','target']df3 = df3.select(selectedCols)
df3.printSchema()

作者图片

分为训练和测试

train, test = df3.randomSplit([0.7, 0.3], seed = 2018)
train.groupby('target').count().show()
test.groupby('target').count().show()

作者图片

对于培训和测试来说,这些课程看起来相对平衡。所以,我们不需要做任何的阶级平衡。让我们做一些模型。

型号

随机森林

from pyspark.ml.classification import RandomForestClassifierrf = RandomForestClassifier(featuresCol = 'features', labelCol = 'label', seed=101)
rfModel = rf.fit(train)
predictions_rf=rfModel.transform(test)

模型的整体精度

from pyspark.ml.evaluation import MulticlassClassificationEvaluatorevaluator_rf = MulticlassClassificationEvaluator(predictionCol=”prediction”)
evaluator_rf.evaluate(predictions_rf)

作者图片

混淆矩阵

predictions_rf.crosstab('label','prediction').show()

作者图片

ROC &精度召回曲线

from handyspark import *# Creates instance of extended version of BinaryClassificationMetrics
# using a DataFrame and its probability and label columns, as the output
# from the classifier
bcm = BinaryClassificationMetrics(predictions_rf, scoreCol='probability', labelCol='label')# Get metrics from evaluator
print("Area under ROC Curve: {:.4f}".format(bcm.areaUnderROC))
print("Area under PR Curve: {:.4f}".format(bcm.areaUnderPR))# Plot both ROC and PR curves
fig, axs = plt.subplots(1, 2, figsize=(12, 4))
bcm.plot_roc_curve(ax=axs[0])
bcm.plot_pr_curve(ax=axs[1])

作者图片

作者图片

测试各种阈值

这是一个基于任何输入的阈值得到混淆矩阵的函数。

split1_udf = F.udf(lambda value: value[0].item(), FloatType())
split2_udf = F.udf(lambda value: value[1].item(), FloatType())def test_threshold(model, prob):
    output2 = model.select('rawPrediction','target','probability',split1_udf('probability').alias('class_0'), split2_udf('probability').alias('class_1'))
    from pyspark.sql.functions import col, when
    output2=output2.withColumn('prediction', when(col('class_0')> prob, 1).otherwise(0))
    output2.crosstab('prediction','target').show()

使用这个定制的 test_threshold 函数,您可以查看混淆矩阵,这取决于您是否想要更高的精度或更高的召回率等。

test_threshold(predictions_rf,.6)

作者图片

test_threshold(predictions_rf,.7)

作者图片

特征重要性

feat_imps=rfModel.featureImportances
x_values = list(range(len(feat_imps)))
plt.bar(x_values, feat_imps, orientation = 'vertical')
plt.xticks(x_values, ['age','sex','cp','trestbps','chol','fbs','restecg','thalach','exang','slope','ca','thal','oldpeaklog'], rotation=40)
plt.ylabel('Importance')
plt.xlabel('Feature')
plt.title('Feature Importances')

作者图片

使用这个,我们可以看到对预测最重要的变量依次是 cp、thalach、ca 和 oldpeaklog。

调谐超参数

paramGrid_rf = ParamGridBuilder() \
    .addGrid(rf.numTrees, [int(x) for x in np.arange(200,221,10)]) \
    .addGrid(rf.maxDepth, [int(x) for x in np.arange(10,11,10)]) \
    .addGrid(rf.featureSubsetStrategy, [x for x in ["sqrt", "log2", "onethird"]]) \
    .addGrid(rf.impurity, [x for x in ['gini','entropy']]) \
    .addGrid(rf.maxBins, [int(x) for x in np.arange(22, 42, 10)]) \
    .build()
evaluator = BinaryClassificationEvaluator()rf_crossval = CrossValidator(estimator=rf,
                          estimatorParamMaps=paramGrid_rf,
                          evaluator=evaluator,
                          numFolds=3)
rf_cvModel = rf_crossval.fit(train)
predictions_rf_cv = rf_cvModel.transform(test)

最佳 CV 模型的总体精度

evaluator_rf_cv = MulticlassClassificationEvaluator(predictionCol="prediction")
evaluator_rf_cv.evaluate(predictions_rf_cv)

作者图片

变好了!准确率从 79.6%提高到 82.8%。现在,什么特征是最重要的,最佳模型使用了什么超参数?

最佳模型的特征重要性

import matplotlib.pyplot as plt
feat_imps=rf_cvModel.bestModel.featureImportances
x_values = list(range(len(feat_imps)))
plt.bar(x_values, feat_imps, orientation = 'vertical')
plt.xticks(x_values, ['age','sex','cp','trestbps','chol','fbs','restecg','thalach','exang','slope','ca','thal','oldpeaklog'], rotation=40)
plt.ylabel('Importance')
plt.xlabel('Feature')
plt.title('Feature Importances')

作者图片

获取最佳随机森林 CV 模型的超参数值

print('Num Trees: ' + str(rf_cvModel.bestModel.getNumTrees))
print('Max Depth: ' + str(rf_cvModel.bestModel.getMaxDepth()))
print('Feature Subset Strategy: ' + str(rf_cvModel.bestModel.getFeatureSubsetStrategy()))
print('Impurity: ' + str(rf_cvModel.bestModel.getImpurity()))
print('Max Bins: ' + str(rf_cvModel.bestModel.getMaxBins()))

作者图片

逻辑回归

from pyspark.ml.classification import LogisticRegressionlr = LogisticRegression(maxIter=20, regParam=0.3, elasticNetParam=0,featuresCol = 'features', labelCol = 'label')
lrModel = lr.fit(train)
predictions_lr = lrModel.transform(test)

模型的整体精度

evaluator_lr = MulticlassClassificationEvaluator(predictionCol="prediction")
evaluator_lr.evaluate(predictions_lr)

作者图片

打印逻辑回归的系数和截距

# Print the coefficients and intercept for logistic regression
print("Coefficients: " + str(lrModel.coefficients))
print("Intercept: " + str(lrModel.intercept))

作者图片

混淆矩阵

predictions_lr.crosstab('label','prediction').show()

作者图片

ROC & PR 曲线

# Creates instance of extended version of BinaryClassificationMetrics
# using a DataFrame and its probability and label columns, as the output
# from the classifier
bcm = BinaryClassificationMetrics(predictions_lr, scoreCol='probability', labelCol='label')# Get metrics from evaluator
print("Area under ROC Curve: {:.4f}".format(bcm.areaUnderROC))
print("Area under PR Curve: {:.4f}".format(bcm.areaUnderPR))# Plot both ROC and PR curves
fig, axs = plt.subplots(1, 2, figsize=(12, 4))
bcm.plot_roc_curve(ax=axs[0])
bcm.plot_pr_curve(ax=axs[1])

作者图片

作者图片

调整超参数

paramGrid_lr = ParamGridBuilder() \
    .addGrid(lr.maxIter, [int(x) for x in np.arange(10,30,10)]) \
    .addGrid(lr.regParam, [int(x) for x in np.arange(.1,.5,.1)]) \
    .addGrid(lr.elasticNetParam, [int(x) for x in np.arange(0,.2,.1)]) \
    .build()
evaluator = BinaryClassificationEvaluator()
lr_crossval = CrossValidator(estimator=lr,
                          estimatorParamMaps=paramGrid_lr,
                          evaluator=evaluator,
                          numFolds=3)
lr_cvModel = lr_crossval.fit(train)
predictions_lr_cv = lr_cvModel.transform(test)

最佳 CV 模型的总体精度

evaluator_lr_cv = MulticlassClassificationEvaluator(predictionCol="prediction")
evaluator_lr_cv.evaluate(predictions_lr_cv)

作者图片

看起来精度变好了!从 78.2%上升到 81.8%。

逻辑回归 V.2

我在上面的逻辑回归文档中找不到关于如何获得系数和 P 值的任何地方。因此,我在 pyspark.ml.regression 包中找到了这个独立的逻辑回归模型。我这样做是因为在我看来,没有 p 值的系数是没有用的。

from pyspark.ml.regression import GeneralizedLinearRegression
glr = GeneralizedLinearRegression(family="binomial", link="logit", maxIter=10, 
regParam=0.0)
model = glr.fit(train)
summary = model.summary
print('Variables:' + str(train.columns[2:-1]))
print("Coefficient Standard Errors: " + str(summary.coefficientStandardErrors))
print("T Values: " + str(summary.tValues))
print("P Values: " + str(summary.pValues))

作者图片

使用这些源文档你可以获得超参数名称,并重复上述步骤进行网格搜索和准确性。对于这个例子,我不会重复这些步骤,我会留给你。

朴素贝叶斯

from pyspark.ml.classification import NaiveBayes
nb = NaiveBayes(featuresCol = 'features', labelCol = 'label')
nb_model = nb.fit(train)
predictions_nb=nb_model.transform(test)

模型的整体精度

evaluator_nb = MulticlassClassificationEvaluator(predictionCol="prediction")
evaluator_nb.evaluate(predictions_nb)

作者图片

混淆矩阵

predictions_nb.crosstab('label','prediction').show()

作者图片

ROC 和 PR 曲线

from handyspark import *
from matplotlib import pyplot as plt
%matplotlib inline
# Creates instance of extended version of BinaryClassificationMetrics
# using a DataFrame and its probability and label columns, as the output
# from the classifier
bcm = BinaryClassificationMetrics(predictions_nb, scoreCol='probability', labelCol='label')# Get metrics from evaluator
print("Area under ROC Curve: {:.4f}".format(bcm.areaUnderROC))
print("Area under PR Curve: {:.4f}".format(bcm.areaUnderPR))# Plot both ROC and PR curves
fig, axs = plt.subplots(1, 2, figsize=(12, 4))
bcm.plot_roc_curve(ax=axs[0])
bcm.plot_pr_curve(ax=axs[1])

作者图片

作者图片

调谐超参数

paramGrid_nb = ParamGridBuilder() \
    .addGrid(nb.smoothing, [int(x) for x in np.arange(1,10,1)]) \
    .build()
evaluator = BinaryClassificationEvaluator()
nb_crossval = CrossValidator(estimator=nb,
                          estimatorParamMaps=paramGrid_nb,
                          evaluator=evaluator,
                          numFolds=3)
nb_cvModel = nb_crossval.fit(train)
predictions_nb_cv = nb_cvModel.transform(test)

评估最佳简历模型

evaluator_nb_cv = MulticlassClassificationEvaluator(predictionCol="prediction")
evaluator_nb_cv.evaluate(predictions_nb_cv)

作者图片

结论

看起来随机森林在这里表现最好,网格搜索后的准确率为 82.8%。逻辑回归以 81.8%排在第二位,朴素贝叶斯以 69.8%排在最后。

让我们解释一下随机森林最重要的变量,因为它有最好的预测精度。查看 cp、thalach 和 ca :

cp: 所经历的胸痛(值 1:典型心绞痛,值 2:非典型心绞痛,值 3:非心绞痛性疼痛,值 4:无症状)

df3.groupby('target','cp').count().show()

作者图片

Random Forests 表示,大多数被归类为患有心脏病的人患有 CP = 1/非典型心绞痛和 CP = 2/非心绞痛,而大多数未被归类为患有心脏病的人患有 CP = 0/典型心绞痛。

thalach: 该人达到的最大心率

df34=df33.loc[df33['target']==0,'thalach']
df35=df33.loc[df33['target']==1,'thalach']fig = go.Figure()
fig.add_trace(go.Histogram(x=df34, name='target:0'))
fig.add_trace(go.Histogram(x=df35,name='target:1'))# Overlay both histograms
fig.update_layout(barmode='overlay')
# Reduce opacity to see both histograms
fig.update_traces(opacity=.9)
fig.update_xaxes(title='Thalach Level')
fig.update_yaxes(dtick=5, range=[0,30], title='Count')
fig.update_layout(title='Comparing Thalach Levels', title_x=0.5)

作者图片

根据该图,较高的 Thalach 或最大心率水平对于患有心脏病的分类是重要的,而较低的水平被分类为没有心脏病。

ca: 主要血管的数量(0-4)

df3.groupby('target','ca').count().show()

作者图片

Random Forests 表示,大多数被归类为患有心脏病的人有 0 条主要血管,而大多数被归类为没有心脏病的人有更多的主要血管,即> 0 条。

这些变量的分布似乎有直观的意义。另外,我们能够从我们的模型中推断出这一点!

我希望这个 PySpark 模型的例子对您的工作有所帮助。谢谢你的阅读。请随意留下任何评论。

参考文献:

克里斯托弗·迪布瓦和史密斯(2008 年)。UCI 网络数据仓库[http://Network Data . ics . UCI . edu]。加州欧文:加州大学信息与计算机科学学院。

雅诺西,安朵斯,斯坦布伦,威廉,普菲斯特勒,马蒂亚斯,德特拉诺,罗伯特和医学博士,医学博士..(1988).心脏病。UCI 机器学习知识库。

从抄本预测医学专业:使用 ULMFiT 的完整演练

原文:https://towardsdatascience.com/predicting-medical-specialities-from-transcripts-a-complete-walkthrough-using-ulmfit-b8a075777723

我解释了在这个 NLP 任务中,我如何一步一步地利用 ULMFiT 方法从患者的转录本中预测医学专业

图片来自通讯社,来自 Unsplash

介绍

医学上通过自我诊断错误预测自己疾病的问题是非常现实的。在《每日电讯报》的一篇报道中,近四分之一的人进行自我诊断,而不是去看医生。误诊的人中,将近一半的人把自己的病误诊了。虽然这个问题可能有多种根本原因,但这可能源于普遍不愿意也没有能力寻求专业帮助。

例如,11%的受访者无法及时找到约会。这意味着在医疗的筛查阶段损失了关键时间,并且没有实现早期诊断,而早期诊断本可以导致疾病更早得到治疗。

有了关注哪个医学专业领域的知识,患者可以通过咨询专科医生更快地获得有针对性的帮助。为了减少等待时间并预测医疗专业的重点领域,我们可以利用自然语言处理(NLP)来解决这一任务。

给定任何医疗记录或患者状况,该软件将预测患者应在寻求帮助的医疗专业。理想情况下,给定一个足够全面的转录本(和数据集),人们将能够准确预测他患的是哪种疾病。

在这种情况下,我们需要一个文本分类模型来对文本抄本对应的医学专业进行分类。

在本文中,我将介绍几个主要部分:

  1. 理工大栈
  2. 通用语言模型微调介绍(ULMFiT)
  3. 创建语言模型 + 语言模型中有什么?
  4. 对 ULMFiT 方法的更深入的解释
  5. 实施 ULMFiT 方法进入给定任务(预测医疗专科)
  6. 学习点+夜莺 OS +未来计划

技术堆栈

进入技术栈:自然语言处理(NLP)和递归神经网络(RNNs)。自然语言处理是自我监督学习的子集。正如在 fast.ai 中定义的,自我监督学习使用已经预嵌入自变量中的标签来训练模型,而不需要外部标签。例如,这将是训练一个模型来预测文本中的下一个单词。这似乎是一项非常艰巨的任务。尽管如此,OpenAI 的研究人员最近完全发布了 GPT-3,,这是一种语言模型,可以以惊人的准确性预测文本中的下一个单词,供开发人员和企业使用。

为什么我们要使用这种技术来帮助一个分类任务,而这个任务只需要基于因变量的预测?如果我们从逻辑上考虑,我们不需要一个可以预测接下来几个单词的语言模型,我们只需要一个文本分类器模型。你可以在这里找到这个任务的代码(带部分注释)。

ULMFiT 简介:大幅提高精确度

通过使用杰瑞米·霍华德和塞巴斯蒂安·鲁德提出的乌尔姆菲特方法,这是一种归纳迁移学习的形式。它“在六个文本分类任务上明显优于最先进的[截至 2018 年],在大多数数据集上减少了 18-24%的错误。”(霍华德和鲁德,2018)。基本上,根据经验,这种方法可以显著提高精确度。

在深入研究语言模型一般如何工作的更多技术细节之前,我将给出 ULMFiT 如何工作的高层次概述。ULMFiT 架构基本上遵循这样的逻辑:[ 步骤 1 ]采用预训练的语言模型(例如,WikiText 103,其可以基于提示输入生成文本),[ 步骤 2 ]在将该预训练的语言模型保存为编码器之前,微调该预训练的语言模型,以及[ 步骤 3 ]利用该编码的学习器来训练文本分类器学习器。我们将分析在这 3 个步骤中使用的实践,以更大程度地提高准确性。

预测医学专业的 ULMFiT 管道。图片作者。

创建语言模型(步骤 2)

我们首先使用 kaggle API 导入数据:

并相应地解压缩 csv 文件(在本例中为单个 csv 文件):

作为预处理步骤,我们删除了几行不相关的信息,这些信息没有合法的医学专业知识附在患者的成绩单上:

语言模型中包含什么?

创建语言模型需要几个关键步骤。

  1. 标记化:将文本转换成单词列表
  2. 数字化:列出所有出现的独特单词(一个词汇列表列表),并通过在词汇列表中查找索引将每个单词转换成数字
  3. 将数字标记加载到语言模型数据加载器中

最终目标是让我们的语言模型预测一个句子中的下一个单词,(即给出提示“我的名字是”,模型应该能够输出一个名字。).

**理解标记化:**标记基本上是由标记化过程创建的列表的元素。它可以是一个单词、单词的一部分或单个字符。我们想利用单词标记化来完成这个任务,它为每个单词创建标记。这将导致一些非常有趣的输出,如下所示:

图片作者。

不是显示文本,而是产生“xxbox”和“xxmaj”这样的“单词”。这是怎么回事?

原来,那些前面带有字符“xx”的特殊“单词”被称为特殊记号(由 fastai 创建),其中表示某种重要意义的部分单词(句号、大写字母、感叹号等)。)可能很难被计算机解释。例如,“xxmaj”意味着下一个单词以大写字母开头,因为我们已经将整个文本文件中的所有字母都转换成了小写字母。

**理解数值化:**我们基本上需要把我们看到的这个记号数组变成一个张量,它给每个唯一的记号分配一个索引。例如,标记“xxmaj”是一个索引,标记“chest”是另一个索引。由于我们的模型只能接收这样一个整数张量,所以我们必须这样做。

那么我们如何着手做这件事呢?

事实证明,fastai 有一个有用的捷径来绕过这种繁琐的预处理。在创建 DataLoaders 对象之前,我们可以使用 TextBlock 传递到 DataBlock,如下所示:

图片作者。

接下来,我们可以创建一个语言 _ 模型 _ 学习器,利用 AWD_LSTM 架构,并创建一个 vocab 列表,我们可以在稍后创建实际的文本 _ 分类器 _ 学习器时使用。这种语言模型学习者将利用递归神经网络(RNN)进行训练,要进行超级深入的讨论,你可以查看 FastAI 对 RNNs 和 LSTMs 的深入解释这里。

然后,我们训练这个 language_model_learner ,在将其保存为编码器之前,通过尝试预测下一个单词来提高准确性,这意味着该模型不包括特定于任务的最终层。在这种情况下,这意味着语言模型的最终输出层(其基于提示预测并创建单词)被省略允许我们对我们的任务的最后一层进行微调,该层将情感分析和提示分类输出到适当的医学专业领域。

这是有意义的,因为我们不希望我们的文本分类器是预测单词的*,我们希望它是基于医疗记录分类医学专业。*

我们开始训练语言模型,如下所示:

图片作者。

我们实现了大约 65%的准确率,这意味着基于提示,我们的模型将能够基于这个数据集在 65%的时间内正确准确地预测下一个单词是什么。这似乎令人印象深刻,所以让我们开始实际的工作。

在我们继续讨论创建文本分类器学习器所使用的技术之前,这里有一个关于 ULMFiT 技术的更深入的解释。

ULMFiT 的全面概述

在 Jeremy 和 Sebastian 的突破性论文中,有三个补充练习与 ULMFiT 结合使用:

  1. 区别微调[用于步骤 2/3]
  2. 倾斜三角形学习率[目前未在本项目中使用]
  3. 逐步解冻[在步骤 3 中使用]

通过结合这些实践,他们能够提高训练的预测准确性。让我们一条一条地分解这些术语。在本文的后面部分,我将包含我自己的代码来演示如何在这个特殊的医疗转录任务中实现这些实践。

**区别性微调:**区别性微调基本上意味着以不同的速率训练模型的不同层,以说明所捕获的不同类型的信息,而不是以相同的学习速率训练整个模型。为此,[Howard 和 Rudder]根据经验发现,首先选择如下学习速率是有效的:

图片作者。

其中:

图片作者。

**斜三角学习率:**定义为“先线性增加学习率,再根据一个【数学函数】线性衰减”的方法。这最终会产生一个学习率查找器,如下所示:

图片作者。

**逐步解冻:**要理解解冻,首先要理解基本冻结的概念。冻结基本上防止训练有素的权重被而不是改变某些层的权重所修改。在这种情况下,逐步解冻意味着“首先解冻最后一层,并微调所有解冻的层(即微调最后一层)一个时期,然后解冻下一层,并重复该过程,直到最后一次迭代收敛。”

通过利用这三种技术的组合,经验上发现极大地增加了语言模型的准确性。对于有区别的微调和逐步解冻,杰里米提供了为什么使用这两种实践的高层次直觉。由于被训练来预测的语言模型的早期层很可能包含以与情感分析/分类相同的方式使用的权重(例如语法规则,强调哪个动词/名词),所以这些早期层应该以较低的学习速率进行微调,以防止准确性的急剧下降。

我们如何着手使用这些技术对文本进行分类?

我们必须保存我们作为编码器训练的语言模型,这意味着除了最后一层之外,所有层都被保存:

图片作者。

我们首先创建一个文本数据块,然后将它从数据集传递到 dataloader,这与我们前面看到的略有不同。这与我们之前创建的数据加载器的不同之处在于,我们现在使用经典的 get_x 和 get_y 函数让模型进行分类。此外,我们还将已知令牌(或 vocabs)的列表输入到数据加载器中。

图片作者。

然后,我们创建我们的学习者,它利用 AWD LSTM 框架,如前所示。我们现在为一个时期训练这个模型,为我们的分类器建立 56%的基线准确度。

图片作者。

有趣的部分来了。

以前我在训练模型的时候,没有使用逐步解冻区别微调的技术。接下来发生的是准确性不断下降,尽管我用恒定的学习速率训练了多次。这是我以前从未遇到过的事情,我认为这是我通过尝试 ULMFiT 技术来解决这个问题的好时机。令人惊讶的是,它起作用了,经过几次训练后,准确率开始提高。

在下面显示的代码中:

learn.freeze_to(-2)

我们现在做的基本是冻结除了最后 2 个参数组以外的所有图层,基本是逐步解冻的做法。

这种情况下什么是判别微调?答案就在下一行代码中。

learn.fit_one_cycle(1, slice(1e-2/(2.6**4),1e-2))

这一行代码基本上采用(1e-2 除以 2.6⁴)的开始学习速率,并具有 1e-2 的结束学习速率,运行 1 个时期。

按作者分类的图片——我们最终实现了大约 61%的文本分类器模型准确率!

然后,我们运行这两行相同的代码,同时慢慢解冻后面的层,并改变学习率,从而使我们最初训练的一个时期的分类器模型提高了大约 5%。

我们可以通过调用 learner.predict(" ")来查看我们的模型的输出,然后将相关的患者成绩单输入其中。

图片作者。

一些学习要点:

对医疗保健和人工智能之间的交叉充满热情是需要长时间培养的。就我个人而言,这种兴趣的增长是因为我可以接触到好的数据集,无论是在 Kaggle 上还是在互联网上。

然而,这些数据集有巨大的局限性——模型的准确性和数据的偏差最终可能会使 ML 系统的准确性略好于在现实世界中抛硬币。实际上,我相信对于一个崭露头角的 ML 学生来说,开发自己的项目,在没有模型固有的局限性的情况下对世界产生巨大的影响,这将是一个挑战。随着健康数据被少数公司垄断,以及患者对隐私问题的担忧,寻找数据集来训练 SOTA 模型以惠及更广泛的社会变得越来越困难。

进入南丁格尔开放科学

幸运的是,我们有像南丁格尔开放科学这样的组织,他们寻求提供计算医学的途径。对我来说,最突出的是进入这样一个平台的安全和道德保证——在这个平台上,只允许对数据集进行非商业使用,以便从处理这些数据集中产生的知识可以惠及每个人。

前进

我将在未来探索人工智能和医疗保健之间的更多交集。我接下来几个月的一个里程碑是探索 Nightingale 提供的数据集,并继续写我的收获。我想做的一件事是探索米哈埃拉·范德沙尔的一些论文,尤其是她的一些大创意

附注:通过 FastAI 使用 ULMFiT 技术让我在这场 Kaggle 比赛中获得一枚铜牌——这是我成为 Kaggle 专家的一大步!

如果这篇文章的任何部分引起了你的共鸣/启发,请随时联系我这里!!

用机器学习预测我的下一次锻炼|第 1 部分:数据

原文:https://towardsdatascience.com/predicting-my-next-workout-with-machine-learning-part-1-2fdb07f9ded2

从数据收集到模型部署,端到端的 phyton 项目查看来自 Apple Watch 的锻炼数据

照片由布洛克斯弗莱彻在 Unsplash

在这一系列帖子中,我将经历一个端到端机器学习项目的所有步骤。从数据提取和准备,到使用 API 部署模型,最后到创建一个前端来实际解决帮助决策的问题。每一个的主题是:

  1. 项目设置、数据处理和数据探索
  2. 模型实验
  3. 模型部署
  4. 数据应用程序创建

让我们从第一部分开始:

项目设置、数据探索和数据处理

任何项目的第一个也是最关键的部分是明确定义你要解决什么问题。如果你对它没有一个清晰的定义,你可能应该回去头脑风暴一下为什么你会有这个想法,如果这真的不仅仅是你的问题。在产品领域有很多方法,我不会在本文中介绍来帮助这一步。现在,我们将只专注于定义当前的问题。

问题是

我希望,就像大多数人一样,我很难保持每天锻炼的充分动力。一个有时对我有帮助的工具是有一个智能手表来跟踪我的进度,并帮助查看朋友的锻炼和比赛等功能。然而,这些还不够,我的动力仍然起伏不定。这就是为什么我作为一名数据科学家,想要研究我过去的锻炼,以找出过去激励我的主要变量是什么,并预测我未来达到锻炼目标的概率。

用一句话来定义它:

我的问题是长期保持锻炼的动力

现在我们已经定义了我们想要解决的问题,我们可以开始设置我们的项目和我们的解决方案。

项目设置

在常规的数据科学项目中,我们必须遵循几个初始步骤。

  1. Git 储存库设置
  2. 基础设施供应
  3. 环境设置

在任何软件项目中,能够创建项目和代码的版本是非常重要的。因此,我们的第一步将是创建一个 GitHub 库,以便存储和创建您的代码版本。GitHub 的另一个有趣的特性是共享和贡献他人代码的能力。

我不会一步一步地讲述如何创建存储库;只需输入“如何创建 Github 库”就可以了。对于这个项目,我使用的是这个库。

第二部分是在云上提供基础设施,以开发并随后部署您的解决方案。目前,我不需要云基础设施,因为我的笔记本电脑可以很好地容纳数据量来进行初步分析。当我们开始创建实验和调整我们的超级参数时,我将向您展示如何在谷歌云平台上完成这些工作,特别是使用 Vertex AI。

最后一部分是创建一个虚拟的开发环境。我喜欢用 pyenv 做这项工作。要安装 pyenv 看这里。最后,有很多操作系统可以使用,但我个人更喜欢使用基于 Unix 的,如 MacOS,或者如果你有 Windows,你可以为 Linux 安装一个 windows 子系统。环境的另一部分是通过 requirements.txt 文件跟踪您的库。项目的 GitHub 库中有一个例子。

数据

现在,为了获得我们需要的数据,我们必须从 iPhone 上的健康应用程序中导出数据。这真的很容易做到,所以只需看看这里如何做到这一点。

现在我们可以(终于)开始编码了。

导出文件是一个 zip 文件,其中包含一个文件夹,里面有路线、心电图和一个包含所有健康数据的 XML。下面的代码将解压文件夹,解析 XML 并保存为 CSV 格式。

这是我们数据处理管道的第一部分。如果我们希望共享这一功能或者只是添加新的数据,那么拥有一个结构化的代码来处理数据是必不可少的。请注意,代码是作为一个函数来构造的。这将给我们的管道带来灵活性和模块化。

现在我们有了下面的数据帧准备建模

作者图片

哈哈,开个玩笑。

在现实生活中,数据几乎永远不会像 Kaggle 数据集那样随时可用。在这种情况下,我们遇到了数据格式的问题,元数据条目存储在列表中,日期必须被转换,这只是我们必须首先处理的一些事情。

做了什么:

  1. 仅过滤锻炼时间数据
  2. 将日期转换为日期时间格式
  3. 将值转换为浮点型
  4. 创建不带时间的日期列,只带天数
  5. 将每天锻炼分钟数分组

现在我们有了每天锻炼时间的时间序列。我选择了锻炼时间而不是消耗的卡路里,因为这衡量的是我锻炼的天数而不是消耗的卡路里。这就是我们所说的项目的一个前提。跟踪这些前提并将其与问题陈述一起记录下来是非常重要的。

好的,我们现在取得了一些进展。所以现在我们可以开始创建模型了,对吗?

在那之前我们还有几件事要做。首先,我们需要检查数据的质量,然后我们将创建一些要素,并在建模之前做一些探索性的绘图以产生一些见解。

数据质量检查

当我们谈论数据质量时,我们应该深入到数据是如何收集的,并考虑在这个过程中可能发生的一些问题。由于这些数据是在我的 Apple Watch 上收集的,我们首先应该探索的是,在我不戴手表的日子里会发生什么?

这可以归结为我们在数据中必须检查的两件事:

  1. 缺失数据
  2. 极端值

就 NAs 而言,没有丢失数据。然而,有 167 次观察以 0 作为锻炼分钟数,这似乎是他们记录没有手表的日子的方式。我们可以在这张直方图中清楚地看到:

作者图片

搜索异常值,我们可以看到有几个异常值,但我们会保留它们,因为它们与现实相符,而不是异常值。

作者图片

我们还可以做很多其他检查来验证数据质量,但是对于这个例子,我们不会深入讨论,因为这个数据源是标准化的,非常可靠。

我们从数据中收集了一些重要信息:

  1. 有 1.737 个观测值(天);
  2. 167 个观测值以 0 为当天的运动分钟;
  3. 日期从 2017 年 9 月 25 日到 2022 年 6 月 27 日;
  4. 在此范围内没有遗漏的日期。

特征工程

现在我们可以玩些有趣的东西了。特征工程步骤是我们创建对模型有用的特征的假设。这是一个迭代过程,所以我们将在这里创建一些,稍后验证它们,并添加或删除功能。

我的一些猜测来自经典的时间序列特征。它们是:

  1. 日期属性(日、工作日、月、年、季)
  2. 滞后特征(最近一段时间消耗了多少卡路里)
  3. 滚动窗口特征(移动平均值、标准偏差、最大值、最小值)

在下一部分,我们将添加一些其他数据,如睡眠质量。

代码如下:

另一个重要的转换是创建月份特性的循环编码。这是对具有周期的时间特征进行编码的一个很好的技巧。这是通过获取每个月的正弦和余弦来实现的,最后,我们会得到这样的结果:

作者图片

我们可以看到 12 月和 1 月彼此更接近,而不是相距更远的 1 和 12。

探索性分析

现在我们将进行一些简单但有力的分析。这一步可以并且经常应该在特征工程之前执行,但是在这种情况下,我需要一些特征用于绘图。

记住:这是一个反复的过程

在一个长期的项目中,在得到最终的解决方案之前,你会经历很多次这样的循环。

我们已经在上一步中查看了数据的分布,因此现在我们可以看到锻炼时间是如何随着一些时间特征而变化的。

先说年份:

作者图片

我们可以看到,2020 年停止了我的趋势,主要是因为科维德疫情的锁定。

我们可以比较每个月的数据分布。

作者图片

在这里我们可以清楚地看到,十二月不是我最好的朋友。主要原因很容易识别:年终聚会、假期、圣诞节,我一般会去度假。

另一件值得注意的事情是我的锻炼在一周的不同日子里是如何变化的。在此分析中,我们认为周一为标签 0,周日为标签 6。

作者图片

锻炼时间的中位数与一周中的其他日子相差不远,然而,在周末进行大规模锻炼的情况更为罕见。

你可以用你的数据创造出无限的可视化效果。现在,我们将停止在那些上面。这里重要的是理解您的数据、分布以及它在不同聚合中的表现。

您还可以基于这些分析创建决策。这里的一个例子是看周末数据趋势更低。一个可能的决定是制定一条规则,规定我只有在周末锻炼时才能喝酒。

建模前我们要做的最后一件事是分解我们的时间序列。哦,我忘了说了,我们这里有一个时间序列。定义如下:

时间序列是在一定时间间隔内,以相等(规则/均匀间隔)的时间间隔进行的一系列重复观测

我们可以分解一个时间序列来理解两件非常有用的事情:

  1. 趋势
  2. 季节性

一个时间序列由这两者的结合和一些残差组成。有几种方法来分解它,这里我们将使用加法。时间序列是一个庞大的主题,所以如果你想深入了解,请点击这里。

作者图片

这是大量的信息,让我们深入研究一下。

第一个情节是原始时间序列。第二个是趋势成分,第三个是季节性,最后一个是残差。

我们从她那里得到的重要信息是:

  1. 我们可以确定一个与周相关的季节性,但在月与月之间没有太大关系;
  2. 残差似乎是均匀分布的。

这对我们有两点帮助。在特征工程步骤中,我们创建特征来捕捉正确的季节性,如果我们想要应用经典的时间序列模型,我们必须在参数中插入季节性。

暂时就这样了。

关键要点

这部分的主要要点是:

  1. 定义问题
  2. 设置您的环境,不要忘记记录您的软件包版本;
  3. 记录你的项目前提
  4. 将您的数据处理组织成函数,这些函数可以在以后与新数据一起重用;
  5. 了解并清理您的数据;
  6. 如果可能的话,创建一些已经可以生成决策的描述性分析。

在下一部分:

  1. 我们将使用 MLFlow 建立一个实验框架来记录和比较我们的模型
  2. 我们将创建多个模型并对它们进行比较
  3. 我们将选择一个模型并优化其超参数

感谢阅读!

如果你喜欢这篇文章,你可以看看我做的其他一些项目这里。

此外,考虑订阅,以获得本系列的所有部分,当他们出来。

使用 CNN 预测造纸机中的纸张断裂

原文:https://towardsdatascience.com/predicting-paper-breaks-in-a-paper-machine-using-cnns-80d9b53e8402

使用图像分类预测即将发生的过程故障

照片由在 Unsplash 上拍摄

介绍

这篇文章展示了使用 CNN(卷积神经网络)来预测一个即将发生的过程故障在造纸机使用一个现实世界的数据集。

这是通过将表示几个连续时间片的数据样本排列成图像来实现的,该图像是过去和当前状态的快照。如果故障前的条件不同于正常操作,则该条件的图像可以警告即将发生的故障。目的不是将故障与正常操作进行分类,而是将运行状态分类为正常或即将发生故障的警告。

通过几个跨越一个月过程数据的图表,讨论了图像构造、模型性能和见解。

为什么要这么做?

工业系统依靠过程控制运行。这些使系统在参数范围内运行。然而,系统关闭了。排除其他因素,如手动关闭或设备故障,扰乱仍会发生。识别即将发生的故障可以给操作人员时间来防止故障。此外,以这种方式分析过程可以提供对为什么或什么事件组合导致异常的洞察,并提供调整控制系统的机会。

资料组

本文中使用的数据来自 ProcessMiner Inc .,可以通过链接找到论文“多元时间序列中的罕见事件分类”[1]中的下载申请表,经许可使用。

该数据集覆盖了造纸机大约一个月的操作,每两分钟记录一次样本。这些特征没有用具体的过程变量名来标识,并且数据可能已经为 IP 目的进行了调整。有一个分类特征似乎将数据分成八个离散的操作场景,这可能与纸张的类型或重量或一些其他参数有关。我从该特征中选择了构成 36%数据的最频繁子集(其中特征 X28 是 96),并通过数据分析和使用特征和排列重要性减少了特征的数量。了解具体特征可以改进这一步。本文中的图表来自性能最好的模型和特性子集,并对其他版本的结果进行了一些比较。

下面显示了碎纸和特性 X28。分页符是时间上的垂直线。蓝线是特征 X28。值 96 出现在月初、月中和月末,如箭头所示。

作者图片

更多的数据和对每个特征背后含义的了解可能会产生一个更好的模型,这个模型也可以用每个月的新数据来改进。其他 X28 值的单独模型可以用更多的数据来构建,或者这些样本可以通过具有专家过程知识的特征转换来包含。

该数据集只有每两分钟记录的过程值。原始原始数据在时间上大概更详细。不同或多个时间间隔可用于其他数据集,以获得洞察力或提供短期或长期预测。

需要六个样本来制作一幅图像,因此在中断之间少于六个样本的任何数据都被剔除。

将数据转换为图像

数据被重新标记,从 0 和 1 表示正常和缺纸,到 0 表示正常,1 表示警告,2 表示缺纸。在每次纸张断裂之前的五个样本被设置为标签 1。

从过程测量值中提取两个时间导数,并作为新的数据表保存在内存中。这给出了过程测量的变化率,即相对于位置的“速度”和相对于速度的“加速度”。我的想法是,即使特征值在设定点范围内,当变化率很高时,这些衍生样本可能表示纸张断裂。

然后将三个数据表在 0 到 1 的范围内进行缩放,并将 6 个时间段的数据分配给覆盖 12 分钟时间段的图像。每个连续的图像与前一个图像重叠 5 个时间段。每个图像都由对应于该图像中最后一个时间片的标签进行分类:正常、警告或断纸。

图像分为 3 层,第一层为过程变量值或“位置”。过程值的导数或“速度”是第二层,第三层是“加速度”值。

一旦故障时间片位于图像的最后一行,该过程就重复进行,即从上一个故障时间片之后的下 6 个时间段开始构建下一个图像。

下图显示了从导致纸张断裂的数据集构建的十二幅连续图像,最后一幅图像代表重启机器后的情况。控制点或测量点值(位置)、“速度”和“加速度”带在每个图像中都是可见的。图像顺序是按行从左到右,左上角是第一个图像,右下角是最后一个图像。左边的数字是对应于每个图像中最后一个时间样本的原始正常(0)和断纸(1)标签。右边的数字是调整后的标签,包括正常(0)、警告(1)和纸张断裂(2)。前五幅图像是正常操作(00)。接下来的五个是实际纸张断裂之前的五个样本的警告条件(01)。接下来是纸张断裂(12),随后是正常重启(00)。所有 6168 个样本都是以这种方式创建的。

5 个正常图像,然后是 5 个警告图像、纸张断裂图像和作者的重启图像

模型
我用三组不同的图像训练了三个不同的模型。这些模型在为图像构造选择的特征上有所不同。EDA /位置特征重要性模型在随机森林模型上使用特征重要性,该模型仅包含离散样本上的原始位置值(不使用具有堆叠样本的图像)。这是我使用的第一个模型/特性列表。特征重要性和排列重要性模型使用从 RF 模型中高度分级的特征,该 RF 模型是根据离散样本上组合的位置、速度和加速度构建的。我保留了相同的图像构造基础,因此位置、速度和加速度值将保留给任何排名较高的特征,无论它是原始位置还是派生的速度或加速度特征。这是 5 小时内最佳模型的样本图表。关于最佳型号性能的更多图表将在后面列出。(上轨道:正常 0,警告 1,断纸 2)

样本性能(如下图 3)-作者图片

培养

训练类似于原型 CNN 模型。我使用了一个顺序 CNN 模型,其中有两个 Conv2D ,两个 MaxPooling ,一个 Flatten layer 馈入一个带有 Dropout三层128–64–3 网络。我没有对正常类进行欠采样,以保持正常条件“看起来”的可变性。因此,初始精度高。详情可以在 Github 这里 找到。

特征重要性模型训练,按作者分类的图像

结果

由于数据集中的不平衡,我将自定义分类器应用于模型概率预测(predict_proba ),而不是使用标准的 argmax 预测。大多数图像(约 94%)来自正常类,警告和断纸图像分别约占 5%和 1%。如果预测的类概率低于该类在数据集中的出现频率,则自定义分类器会删除该类。例如,如果警告和断纸概率分别为 8%和 3%,则正常概率为 89%,然后被排除。该预测是剩余两个概率的 argmax,并且将是一个警告。自定义分类器混淆矩阵在左侧,标准 argmax 预测在右侧。显示了三种不同模型的结果。

作者图片

与 Argmax 相比,自定义分类器对警告状态的真阳性率更高,但假阳性率更高,根据模型的不同,预测正常状态条件警告的频率要高 4 到 9 倍。

目标是在纸张断裂发生之前预测它,以便有时间进行干预。因此,在这个阶段,定制分类器(调整后的预测)的更高的真阳性性能是有吸引力的,即使是以更高的假阳性警告状态为代价。尽管我们希望减少生产模型中的误报(有害警报)数量,但这并不在那个状态。在这种情况下,与简单地向前跳跃并采用具有标准 Argmax 分类的要素重要性模型的低错误警告相比,可以从错误警告中学到很多东西。使用相同的模型参考下面的两个图表。第一个使用自定义分类器,第二个使用标准 Argmax 选择。Argmax 忽略了流程看起来不稳定并最终出现纸张断裂(用圆圈突出显示)的时间,避免了“错误警告”并错过了许多指定的警告,而定制分类器显示了几乎所有的警告。这些样品与长时间稳定运行后纸张断裂前的样品之间的真正区别是什么?

可能的不稳定操作自定义分类器显示警告-图片作者

操作可能不稳定,Argmax 分类器显示少量警告-图片由作者提供

查看混淆矩阵的结果和下面的图表,我发现这些模型和数据集可能会发生三种情况。

首先,模型可能表现得不是很好。

其次,记住原始数据只有正常类和断纸类。我在纸张断裂之前任意选择了五个样本作为警告类。可能有许多“正常”的样本,其中过程状态类似于故障前的条件,并且一些警告的误报实际上是系统接近纸张断裂但恢复到正常操作水平而没有导致断裂的时候。这将是有见地的,可能是获得最高价值的地方。例如,利用操作来探索实际故障前样本和其他“正常”样本的相似性,或者理解即使特征值在设定点限制内,特征的变化率是否更能指示问题。数据集特征是匿名的,在不了解这些特征并且不是造纸机专家的情况下,对我的结果的解释是有限的。通过良好的迭代实践和与领域专家(即,造纸机专家)的审查,对实际特征的了解将确认洞察力或识别建模问题。

具有假阳性的初始模型对于理解过程或过程控制系统中的细微差别是有价值的。换句话说,即使样品被标记为正常,也不意味着它没有接近或趋向于纸张断裂(或其他系统中的过程故障)。

第三,我标记警告状态的方式可能存在偏见,导致更高的误报率。我选择了实际休息前的 5 个时间段(10 分钟)作为警告,为时间干预过程提供一些实用性。造纸机中的条件可能发展到更快的纸断裂,因此在一些或所有断裂之前的 2 或 3 个警告状态可能不代表过程偏差。因此,当数据完全正常时,将数据标记为警告状态可能导致模型预测小的过程偏差作为即将发生纸张断裂的证据和/或降低测试集中预测警告的性能。在纸张断裂之前减少警告状态的数量可以改进模型,但是也可以减少干预以防止断裂的时间。

图表

我提供了几个图表,概述了模型的运行情况,以及不同时期的数据如何显示为平稳运行的机器或有点不稳定的机器,有时是在启动后。图表来自使用调整后的预测值(基于样本集中类的概率和类的频率进行分类)对具有位置和两个导数的样本运行的特征重要性所生成的模型。更多图表在我 Github 上的 Jupyter 笔记本里。

图表显示了按样本日期和时间一起绘制的测试和训练结果(通过最终模型运行训练集)。我以这种方式呈现它们,是因为按样本连续排序的训练集和测试集的图并没有显示出一个月内的性能变化,因为样本被打乱了。此外,按时间划分的测试集和训练集的单独图与测试集或训练集中的缺失数据有差距。

这些地块有两条轨迹。上面的轨迹具有来自模型的预测类别、正常(0)、警告(1)和断纸(2)。这条轨道也有实际的警告标签,如圆圈,纸张破裂,如钻石。测试预测是蓝点,训练预测是蓝 X。时间间隔越短,就越容易看出预测值和实际值的一致性。

下面的轨迹具有为测试和训练预测绘制的类概率(0 到 1)。这里测试结果是点,训练结果是 X。分类概率绿色表示正常,黄色表示警告,红色表示纸张断裂(也称为故障)。

图表 1 显示了样本子集的分布,该数据子集中有 58 篇论文。

图表 1 —作者图片

图表 2 是 5 月 5 日从 13:00 到 17:30 的 4 个半小时的运行周期。这显示了正常概率低于 94%的小幅度下降是如何伴随着高于其在数据中出现频率的警告概率的,因此是警告预测。有几个“假”警告可能是由于不规则的操作条件造成的。这些高“假”警告期往往在临近纸张断裂时或从启动到纸张断裂运行时间较短时发生。图表中的两个断点被预测为警告对断点。

图表 2 —作者提供的图片

图表 3 有一个缺口,其中缺少 68 分钟的数据。看起来机器一直在运转,直到 23:00 纸张断裂。这个时间间隔的错误警告比上面的图表 2 要少得多。

图表 3 —作者提供的图片

图表 4 显示了大约 28 小时的平稳运行时间。在机器重启后(左侧)和每月 14 日 15:00 左右纸张断裂前,错误警告更加频繁。

图表 4 —作者图片

图表 5 显示了图表 6 之前的 11 个小时的干净运行时间。

图表 5 —作者图片

图 6 在时间上跟随上面的图 5,并且显示了在纸张断裂之前持续预测警告的模型。

图表 6 —作者图片

图表 6a 是来自基础模型(EDA /位置特征重要性模型)的相应结果。该模型使用了不同的特征,比上面的特征重要性模型(在特征选择中使用位置、速度和加速度)有更多的“错误警告”。这些具有不同特征的错误警告可能有价值,但是需要特征的知识来确定洞察力和差的模型性能之间的差异。

图表 6a —作者图片

概述

目标不是将纸张断裂与正常运行条件进行分类,因为在纸张断裂发生后识别纸张断裂没有任何运行益处。识别可能导致纸张断裂的操作条件变化,对于操作人员干预、改进过程控制逻辑或理解多个过程测量中的二阶或复合效应具有潜在价值。

这项工作表明,CNN 模型可以用来预测或警告现实世界数据集上即将发生的过程故障。该模型可以用更多的数据和关于产生该数据的实际造纸机的知识来改进。这种方法可以用于其他过程,不一定作为控制系统,但探索扰乱和微调系统。

这不是一个生产级模型,在生产级模型中,最大限度地减少错误警告非常重要。挖掘假阳性是恢复的偏差情况的可能性(如上所述)是开发生产模型所需的迭代方法的一部分,包括在过程设计和操作中咨询主题专家。模型的训练和部署不是一步到位的。在多学科方法中可以获得洞察力,以了解模型是否不好,或者模型是否告诉你一些东西,并利用这些来改进模型或理解工业过程。

欢迎大家的评论。

特别感谢

特别感谢 ProcessMiner Inc .的科学主管 Chitta Ranjan,感谢他慷慨地允许我发表这篇文章并分享我的方法和结果。

相关文章

这是一篇原型文章的扩展,在这篇文章中,我创建了一个合成数据集,并在表示正常情况和故障前时间片作为警告状态的图像上训练了一个 CNN 图像分类模型。

参考

[1]Chitta Ranjan,Markku Mustonen,Kamran Paynabar,Karim Pourak,《多元时间序列中的罕见事件分类,2018,arXiv:1809.10717,数据集使用权限。

用 Python 预测需求的价格弹性(实现 STP 框架——第 4/5 部分)

原文:https://towardsdatascience.com/predicting-price-elasticity-of-demand-with-python-implementing-stp-framework-part-4-646b025b8b34

实施逻辑回归预测需求价格弹性

Artem Beliaikin 在 Unsplash 上拍摄的照片

在本系列中,我们将学习 STP 框架(细分、定位和营销),这是最流行的营销方法之一。在前三篇文章中,我们已经学会了使用细分来了解我们的客户,并根据他们的人口统计学、心理学和行为特征将我们的客户群分为四个部分。这涵盖了 STP 框架的第一步。

我们的四个客户群是:

  1. 标准
  2. 以职业为中心
  3. 机会更少
  4. 富裕的

在目标确定阶段,我们根据整体吸引力、战略方向、市场专业知识、未来潜力等为我们的客户群定义战略。由于这更多地属于营销部门的公司战略,我们将在这个博客系列中跳过这一步,转到第三步,定位。

和往常一样,笔记本和数据集在 Deepnote 工作区中可用。

配置

定位是营销战略的重要组成部分,尤其是当公司在竞争激烈的市场中运作时。我们需要了解消费者如何看待产品,以及它与其他竞争产品有何不同。定价和折扣在影响客户购买决策方面起着至关重要的作用。

在本帖中,我们将运用逻辑回归来理解产品价格如何影响购买决策,以及我们是否有价格弹性。

但在此之前,让我们先了解一下数据集。我们将使用为此实验准备的新数据集,它利用了我们在上一部分中执行的分段步骤。

数据探索

我们将使用一个数据集来表示零售店的客户购买活动。该数据集与我们已经熟悉的客户详细信息数据集相链接,我们已经在本系列的前三部分中使用过该数据集(第 1 部分、第 2 部分和第 3 部分)。该数据集中的每条记录都代表一个客户的购买历史。

以下是每个属性的详细描述:

属性描述(图片由作者提供)

其他计算属性:

附加计算属性(图片由作者提供)

平均价格将是我们寻找价格弹性实验的主要特征。 Has_PromotionAction_Price 是可能帮助我们改进模型的次要功能。我们走着瞧。

让我们从每个细分市场的独立客户数量开始我们的数据探索。我们看到,最大的细分市场是“机会较少”的细分市场(38%),其他细分市场的权重几乎相同(约 20%)。这是我们正在处理的一个平衡的数据集。

就实际购买记录的数量而言,每个细分市场看起来相当均衡(发生率= 1)。

现在,当我们看每个细分市场的平均价格时,它们看起来非常相似。都在 2.0 左右的水平。但是,当我们缩小关注范围并查看实际购买价格时,我们可以看到*“富裕”细分市场的平均价格点更高(2.2),而“事业型”*客户的平均价格点更高(2.65)。

这些信息与我们在本博客系列的细分部分获得的知识一致。现在让我们继续机器学习部分,我们试图理解价格弹性的范围。

逻辑回归模型

逻辑回归是一种分类算法。它估计事件发生的概率,如贷款违约预测。由于结果是概率,它可以是 0 或 1,对于二元分类,小于 0.5 的概率将预测 0,而大于 0 的概率将预测 1。

在我们的例子中,我们实际上不会使用二元分类模型(无论客户是否购买)。相反,我们将估计需求的价格弹性,并预测在不损害市场的情况下我们可以提高多少价格?

了解有关逻辑回归的更多信息。

从模型系数来看,很明显,平均价格和购买事件之间存在反比关系。如果均价降低,购买概率会增加。

现在让我们建立在这个前提上。
我们看到糖果的价格(在所有品牌中)从 1.1 到 2.8 不等。所以,保留一些缓冲,让我们取价格范围从 0.5 到 3.5,每次增加 0.01,并检查购买事件的概率如何变化。

结论

正如所料,我们观察到,随着平均价格的增加,购买的机会减少。

我们将在下一篇文章中继续我们的价格弹性预测实验,并了解在不破坏需求的情况下我们可以将价格提高多少。

感谢阅读!如果你喜欢这篇文章一定要给 鼓掌(最多 50!) 让我们 连接上*LinkedIn 在 Medium 上关注我 保持更新我的新文章*

通过此 推荐链接 加入 Medium,免费支持我。

*https://analyticsoul.medium.com/membership *

用 Python 预测需求的价格弹性(实现 STP 框架-第 5/5 部分)

原文:https://towardsdatascience.com/predicting-price-elasticity-of-demand-with-python-implementing-stp-framework-part-5-5-8383ecc4ae68

实施逻辑回归预测需求价格弹性

活动发起人在 Unsplash 上的照片

在本系列中,我们将学习 STP 框架(细分、定位和营销),这是最流行的营销方法之一。在前三篇文章(第一部分、第二部分和第三部分)中,我们已经学会了使用细分来了解我们的客户,并根据他们的人口统计、心理特征和行为特征将我们的客户群分为四个部分。这在本系列的第一个主要部分中已经讨论过了。

我们的四类客户是

  1. 标准
  2. 以职业为中心
  3. 机会更少
  4. 富裕的

在上一篇文章(第 4 部分)中,我们开始了实现 STP 框架的第二个主要部分。在那里,我们训练了一个逻辑回归模型,发现平均价格购买概率之间存在反比关系。这意味着随着平均价格的下降,客户购买的可能性增加。

现在,在此基础上,我们试图确定需求的实际价格弹性。我们将努力确定在不损害需求的情况下我们可以提高的价格,并检查价格上涨在什么时候开始影响市场。

如果想刷新本系列之前的文章:

  1. STP 框架介绍及利用分层聚类实现客户细分。
  2. 使用 k 均值聚类进行客户细分(基础模型)
  3. 改进的 k-means + PCA 分割模型
  4. 用逻辑回归预测需求的价格弹性

和往常一样,笔记本和数据集在 Deepnote 工作区中可用。

需求价格弹性

商品的需求价格弹性(PED)是衡量需求数量对价格的敏感程度。当价格上涨时,几乎所有商品的需求量都会下降,但对某些商品来说下降的幅度比其他商品更大。

如果我们把价格提高百分之一,价格弹性预测的是需求量的百分比变化,假设其他一切都不变。在现实生活中,需求量可能会因为其他变量而发生变化,但在计算 PED 时,我们必须假设其他变量保持不变。

需求的价格弹性=需求量的百分比变化/价格的百分比变化

  1. 如果 PED 大于 1(PED > 1),它被称为“弹性”,这意味着价格的变化会导致需求的显著变化。
  2. 如果 PED 等于 1 (PED = 1),那么这意味着价格的任何变化都会引起需求的同等变化。
  3. 如果 PED 小于 1(PED < 1),它被称为“非弹性的”。这意味着价格变化不会对需求产生太大影响。
  4. 如果 PED 等于 0 (PED = 0),这被称为“完全无弹性”,这意味着价格的任何变化都不会导致需求的变化。

经济学家应用这一点来理解当一种产品的价格变化时,供给和需求是如何变化的。

如果我们在 3 类或 4 类,我们可以在不伤害需求的情况下提高价格。但是如果我们属于第 1 类或第 2 类,我们就有价格弹性,我们必须分析价格变化对需求的影响。

让我们简化我们场景的等式。

我们可以用代码实现的简化版本如下:

PED =贝塔价格 (1 -购买概率)

这里,β是逻辑回归模型的系数。

履行

简单回顾一下我们正在使用的代码。

price_range 是一个数组,它保存的糖果价格略高于(两边)任何品牌糖果的最低和最高价格。我们准备了这个数组来计算每个价格点的 PED,并确定价格弹性边界的确切点。

purchase_proba 是在相应的平均价格( price_range )上发生购买行为的概率。

我们在这张图表中看到了完整的弹性曲线及其与价格区间的关系。价格弹性随着价格的上升而下降。这意味着产品价格越高,对需求的影响就越小。由于价格和需求之间存在反比关系,随着价格的上涨,顾客购买的可能性越小。

从定义中我们知道,如果弹性的绝对值小于 1,则为非弹性,如果大于 1,则为弹性。

查看图表(也是数据框架),我们看到平均价格为 1.25 时,价格弹性为-1.0。这意味着,在这个价格点之前,如果我们将价格提高 1 %,需求将减少不到 1%。因此,购买概率将是无弹性的。另一方面,在该价格点(1.25)之后,如果我们将价格提高 1%,需求将减少 1%以上。这使得情况缺乏弹性。

这使得 1.25 成为转变点,因为对于非弹性值,一般建议提高价格,因为它不会导致购买概率的显著下降。另一方面,如果我们有弹性,我们应该降低价格来增加需求。

按细分市场比较价格弹性

这是我们的基线,整个数据集平均价格的价格弹性。现在让我们比较一下每个细分市场的价格弹性,因为这将让我们深入了解每个客户细分市场的价格弹性。有些部分可能比其他部分更灵活。

我们将采用逻辑回归模型的新实例,然后用特定的数据段对其进行训练,并像以前一样计算弹性。让我们看一下图表。

观察

我们在图表中(也在数据框架中)看到,富裕的群体和以职业为中心的群体的平均无弹性价格比机会较少的群体标准群体高出约 14%。只要 PED 没有弹性,我们就可以提高早期产品的价格。

我们可以做出的另一个推论是,线的陡度在图形的右侧变化(超过价格点 1.5)。它表明了每个细分市场的不同弹性水平,并告诉需求将如何随着价格的上涨而变化。我们必须小心调整价格,以保持健康的需求水平,而 PED 是有弹性的。似乎机会较少的群体比其他群体对价格更敏感。

至此,我结束了我们用 Python 实现了 STP 框架的这个博客系列。

如果你有任何问题,请写在评论里,我会尽我所能尽快回答。

整个笔记本和数据集都可以在 Deepnote 工作区中获得。

感谢阅读!如果你喜欢这篇文章一定要 鼓掌(最多 50!) 让我们 连接上*LinkedIn 在 Medium 上关注我,随时更新我的新文章。*

通过此 推荐链接 加入 Medium,免费支持我。

*https://analyticsoul.medium.com/membership *

用机器学习预测降雨

原文:https://towardsdatascience.com/predicting-rain-with-machine-learning-2acf80017c44

机器学习

明天会下雨吗?让我们建立一个人工智能模型来回答这个问题。

照片由Atilla bingl在 Unsplash 上拍摄

寻找有趣的挑战来提高您的数据科学技能?

这是一个现实世界的问题。

问题陈述

天气对农业有着重要的影响,因此,预测天气有助于农民的日常决策,例如如何有效地计划、最小化成本和最大化产量。

一家大型农业公司需要您帮助他们最大限度地提高增长效率、节约资源和优化生产。

为了实现这些目标,该公司需要有一个准确的天气预测算法,这将改善他们对典型农业活动(如种植和灌溉)的决策。

利用他们所在地区的历史天气信息,你能预测未来几天的天气吗

来源

你现在有了一个明确的目标。

目标🥅

根据三个标签预测第二天的天气。

  • N —无雨
  • L —小雨
  • H —暴雨

现在让我们来看看数据

数据💾

📂 **train**
 ├── region_A_train.csv
 ├── region_B_train.csv
 ├── region_C_train.csv
 ├── region_D_train.csv
 ├── region_E_train.csv
 ├── solution_format.csv
 └── solution_train.csv📂 **test**
 ├── region_A_test.csv
 ├── region_B_test.csv
 ├── region_C_test.csv
 ├── region_D_test.csv
 └── region_E_test.csv

数据被方便地分成训练和测试数据集。

在每一次训练和测试中,您都将获得天气数据,这些数据由命名为区域 A 到区域 E 的匿名位置组成,它们都是相邻的区域。

下面来看看region_A_train.csv的前五排

您应该注意到的第一件事是date列不是日期,而是被匿名化为某个随机值。

共有 10 个特征,由温度、降水、风速、风速方向和大气压力组成

然后,看着solution_format.csv

我们可以利用日期列将它与训练数据连接起来,并构建一个模型。

现在你对目标有了一个想法,对给你的数据有了一些了解,是时候动手了。

通过注册获得数据,参加数据科学竞赛并跟随本文!

本文代码→Google collabdeep note

安装库和模型

首先,安装我们选择的武器:[lightgbm](https://lightgbm.readthedocs.io/en/latest/)

加载库

接下来,我们加载一些用于可视化和机器学习的基本库。

加载数据

让我们读入所有的数据。

探索性数据分析

有趣的部分到了,可视化数据。

将所有区域连接在一起

由于所有的区域数据共享一个主键date,我们可以将它们与 pandas 中的[concat()](https://pandas.pydata.org/docs/reference/api/pandas.concat.html)连接起来,并将这些键设置为区域名。

我不希望区域作为索引,所以我们可以重置索引,然后重命名一些列,以获得正确形状的数据。

目标阶层分布

让我们首先想象一下我们的目标类。

看起来我们手里有一个不平衡的职业,因为N标签统治了其余的职业。

为什么这是一个问题?

模型会偏向于样本量较大的类。

发生这种情况是因为分类器具有关于具有更多样本的类的更多信息,因此它学习如何更好地预测这些类,而它在较小的类上保持较弱。

在我们的例子中,标签N会比其他职业更容易被预测到。

让我们记住这一点,并向更多的可视化前进。

绘制特征

我们在每个地区都有十个特色。

是时候编造一些情节了。

因为所有区域都用于预测第二天的天气,所以让我们看看是否所有区域都具有相似的特征模式,以及是否存在任何异常值或异常值。

从图中,我们看到数据中的模式非常相似,除了区域 C、D、E 的min.wind.speedavg.wind.speed在较低的比例上。

现在我们已经对数据进行了一些探索,我们检查数据中缺失的值。

缺少值

使用我的自定义助手函数,似乎有大量数据丢失给了min.atmos.pressure变量

我们还可以使用热图来可视化该列中缺失的数据。

我们有多种方法可以处理丢失的数据。

让我们先来看看缺少值的列的分布。

由于列的分布非常正态,让我们用平均值来估算缺失的数据。

然后,我们对测试数据做同样的事情(笔记本中的全部代码)

特征预处理和工程

转换数据类型

让我们首先将标签添加到数据中。

然后我们看一下数据集的分类列。

我们必须将分类特征(包括目标变量)转换成数字格式。

让我们使用 scikit-learn 的标签编码器来完成这项工作。

这里有一个在标签列上使用LabelEncoder()的例子

创建新的天气特征

既然我们已经有了一些天气特征,我们还可以设计一些有趣的新特征。

一个例子是蒲福风级,这是“一种将风速与观察到的海上或陆地条件联系起来的经验测量方法。”

下面是一个功能,做我们的特征工程。

我们可以轻松地在训练和测试数据集上应用特征工程步骤。

准备列车数据

让我们看看目前为止的训练数据。

是时候将我们的数据转换成更长的格式了,这意味着区域不再是一列,每个特性都有自己的区域,比如avg.temp_Aavg.temp_B,直到avg.temp_E剩余的特性。

这样,数据将处于构建模型的正确状态。

我们可以使用熊猫的[pivot_table](https://pandas.pydata.org/docs/reference/api/pandas.pivot_table.html)功能来实现这一点。

列名不理想,所以我写了一个函数来解决这个问题。

这是目前为止的训练数据。

在我们对测试数据做了同样的操作后,就该拆分训练数据了。

LightGBM 喜欢给分类特征指定分类数据类型,所以我们就这么做吧。

我们可以使用[train_test_split](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html)将我们的数据分成训练集和评估集。

是时候构建 LightGBM 模型了!

LightGBM

让我们为一个简单的预测创建一个基础[lgb.LGBMClassifier](https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.LGBMClassifier.html)

然后我们可以fit训练数据。

然后我们对评估数据调用predict函数

模型性能

让我们看看一个基本的 LightGBM 分类器是如何工作的。

对于不平衡的数据集,99%的准确率可能毫无意义,因此我们需要更合适的指标,如精度、召回率和混淆矩阵。

混淆矩阵

让我们为我们的模型预测创建一个混淆矩阵。

首先,我们需要获得标签编码器给出的类名和标签,这样我们的图就可以显示标签名。

然后我们绘制一个非标准化和标准化的混淆矩阵。

正如你从图的阴影中看到的,我们的模型比其他模型更能预测标签N

有了你在上图中看到的值,我们可以计算出一些指标,告诉我们我们的模型做得有多好。

分类报告

分类报告衡量分类算法的预测质量。

它回答了多少预测是真的,多少是假的问题。

更具体地说,它使用真阳性、假阳性、真阴性和假阴性来计算精确度、召回率和 f1 值

让我们来分解输出。

你的预测有百分之多少是正确的?
Recall你们抓到的阳性病例占百分之多少?
F1-score
support每个给定类别的出现次数

如果你仍然很难理解精确和回忆,阅读这个伟大的解释。

计算指标

以标签为H的类 0 为例,让我们根据混淆矩阵中的值来计算精确度、召回率和 f1 值

TP - True Positive | FP - False Positive
FN - False Negative | TN - True NegativePrecision = TP/(FP+TP) = 3/(3+4+2) = 3/9 = 0.33
Recall = TP/(TP+FN) = 3/(3+1+0) = 3/4 = 0.75F1-score = 2 * (Recall * Precision) / (Recall + Precision) 
         = 2 * 0.33 * 0.75 / (0.33 + 0.75)
         = 0.495 / 1.08
         = 0.458

宏观还是加权?

您可能会注意到,F1 总分有三个值0.640.530.65。你应该关注哪个价值?

在所有类都同等重要的不平衡数据集中,macro average是一个很好的选择,因为它平等地对待所有类。

但是,如果您希望将较大的权重分配给数据中样本较多的类,则首选加权平均值。

您可以使用的另一个度量标准是 Precision-Recall ,这是在类非常不平衡时预测成功的一个有用的度量。

特征重要性

让我们也画出特性的重要性,看看哪些特性更重要。

从上图中可以看出,风速特征和降水量是很好的预测因子。

我们创建的 Beaufort 比例特征似乎不太重要,因此最好将其移除。

有趣的是,A 地区的min.atmos.pressure最重要,而其他地区的min.atmos.pressure重要性最低。

保存模型

当你对你的模型满意时,你可以用joblib把它保存为一个 pickle 文件

根据测试数据进行预测

我们做了一个简单的模型。是时候在测试数据上预测一下了。

让我们看一下最终的提交文件。

标签仍被编码为数值;让我们把实际的标签名称带回来。

因为我们已经有了标签名称到数值的映射字典,即‘H’ : 0,我们可以颠倒上面的字典来将数字映射到名称

保存预测文件。

后续步骤

基础模型不足以做出好的预测;下面是改进给定方法的一些后续步骤。

  1. 更多特征预处理和工程
  2. 使用交叉验证来更好地衡量性能。
  3. 使用远视或 Optuna 来调节 LightGBM 的参数
  4. 直接调整 LightGBM 的**class_weight**参数以处理不平衡等级
  5. 通过利用欠采样(从多数样本中选取较小的样本以匹配较小的样本)或重采样(使用 SMOTE 等算法用虚假数据扩充数据集)来平衡数据集
  6. 测试其他算法,如 KNN,SVM,XGBoost,Catboost 等。
  7. 加入 bitgrit discord 服务器 与其他数据科学家讨论挑战

资源

一般

  • 应对机器学习数据集中不平衡类别的 8 种策略
  • 多类分类的提示和技巧
  • 如何减轻处理不平衡数据的痛苦

超参数调谐

  • 使用 Optuna 调整 LGBM 超参数🏄🏻‍♂️
  • 使用 Hyperopt 对 XGBoost、LightGBM 和 CatBoost 进行超参数优化的示例

多类度量

  • 简化多类指标,第一部分:精度和召回率
  • 简化多类别指标,第二部分:F1-得分
  • 简化的多类别指标,第三部分:Kappa 得分(又名 Cohen 的 Kappa 系数)
  • 微观,宏观&F1 得分的加权平均值,清楚地解释了

祝你在比赛中玩得开心!

感谢阅读!

喜欢这篇文章吗?这里有三篇文章你可能会喜欢:

  • NCATS 发起的 LitCoin NLP 挑战
  • 利用数据科学预测病毒性推文
  • 建立 XGBoost 模型预测视频流行度

喜欢我的写作吗?通过我的推荐链接加入 Medium,你将直接支持我🤗

https://benedictxneo.medium.com/membership

使用机器学习技术预测太阳能输出

原文:https://towardsdatascience.com/predicting-solar-power-output-using-machine-learning-techniques-56e7959acb1f

应用数据科学解决方案应对可再生能源挑战。

安德烈亚斯·古尔霍恩在 Unsplash 上拍摄的照片

介绍

太阳能是世界上领先的可再生能源之一,并且还在继续增长。然而,它依赖于阳光,阳光是一种间歇性的自然资源。这使得电力输出的可预测性对于将太阳能光伏并入传统电网系统至关重要。

在当前的分析中,预测了安装在北半球 12 个位置的水平光伏的功率输出。只有位置和天气数据被使用,没有关于辐照度的信息。虽然辐照度是太阳能输出的一个强有力的预测指标,但是收集关于一个位置的这种信息通常是乏味的,并且它的估计可能具有显著的误差。因此,需要进一步探索在没有辐照度数据的情况下预测功率输出的能力,以节省时间、精力和成本,同时不显著损失精度。

这些数据在 Kaggle 上公开,包括 14 个月的电力输出、位置和天气数据。此外,用于该分析的笔记本可在 Github 上获得。本文的其余部分将涵盖数据处理、建模、结果和结论。

数据处理

在传递给机器学习算法之前,对数据中的可用变量进行探索、可视化和预处理。

数据探索

数据集由 21,045 行和 17 列组成。让我们使用 pandas- Python 数据分析库中的函数来研究数据集中可用的列。

作者图片

如上图,数据集中没有缺失值(耶!!).此外,删除了“YRMODAHRMI”列,因为它不直观,也没有提供描述。目标变量是 PolyPwr(功率输出)。

此外,数据集的前五行如下所示:

作者图片

接下来,让我们检查目标变量的分布。尽管 30 W 以上的功率输出表示有限,但下面的直方图并未显示出明显的偏斜。因此,由于目标变量在可用数据集中的分布,预计在预测目标变量时不会有额外的困难。

整个数据集中功率输出变量的直方图。(图片由作者提供)

到目前为止一切都很好。现在,让我们想象一下可用功能和功率输出之间的关系。

可用功能和功率输出之间的相关性。(图片由作者提供)

从相关图来看,环境温度、云顶和湿度是与太阳能输出最相关的三大特征。还应该注意的是,纬度与功率输出具有显著的相关性,而经度没有显示出相同的行为。因此,经度被从建模过程中删除。海拔也下降了,因为它与气压有很好的相关性,但不会因给定位置而变化。

特色工程

在这里,让我们从现有的特征中创建新的特征,以使分类变量在我们的机器学习算法中可用,并在数据中捕捉更多的模式。

首先,我们使用一次性编码方法对分类变量(即位置和季节)进行编码。

# Encode location data
df_with_location_en = pd.get_dummies(df, columns=['Location'], drop_first=True)# Encode season data
df_with_loc_season_en = pd.get_dummies(df_with_location_en, columns=['Season'], drop_first=True)

其次,让我们使用月和小时数据创建循环特征。应该注意的是,只有上午 10 点到下午 3 点之间的数据是可用的,这排除了系统不预期发电的时段。

# Define time bounds in data
min_hour_of_interest = 10
max_hour_of_interest = 15# Calculate time lapse since onset of power generation
df_with_loc_season_en['delta_hr']= df_with_loc_season_en.Hour -
                                   min_hour_of_interest# Create cyclic month features
df_with_loc_season_en['sine_mon']=
                  np.sin((df_with_loc_season_en.Month - 1)*np.pi/11)
df_with_loc_season_en['cos_mon']= 
                  np.cos((df_with_loc_season_en.Month - 1)*np.pi/11)# Create cyclic hour features
df_with_loc_season_en['sine_hr']= 
  np.sin((df_with_loc_season_en.delta_hr*np.pi/(max_hour_of_interest
  - min_hour_of_interest)))
df_with_loc_season_en['cos_hr']= 
  np.cos((df_with_loc_season_en.delta_hr*np.pi/(max_hour_of_interest
  - min_hour_of_interest)))

包括新创建的特征在内的附加相关性分析显示了日期特征的余弦与其实际值(月和小时)之间的完美相关性。因此,月和小时特征在建模过程中被丢弃。

用于建模的最终特征列表如下所示:

selected_columns = ['Latitude', 'Humidity', 'AmbientTemp',  
                    'PolyPwr', 'Wind.Speed', 'Visibility', 
                    'Pressure', 'Cloud.Ceiling', 'Location_Grissom', 
                    'Location_Hill Weber', 'Location_JDMT', 
                    'Location_Kahului', 'Location_MNANG', 
                    'Location_Malmstrom', 'Location_March AFB', 
                    'Location_Offutt', 'Location_Peterson', 
                    'Location_Travis', 
                    'Location_USAFA','Season_Spring', 
                    'Season_Summer', 'Season_Winter', 'sine_mon', 
                    'cos_mon', 'sine_hr', 'cos_hr']

数据拆分

整个数据集分为 80%的训练数据和 20%的测试数据。在不同模型的超参数调整和训练过程中,测试数据一直存在且不可见。

建模

开发了三个模型(随机森林-RF、光梯度推进机 LGBM 和深度神经网络-DNN)和一个堆叠集成,并与基线(K 最近邻-KNN)模型进行了比较。

指标

在此分析中,R 平方度量是选择性能最佳模型的最终度量。用于评估所选模型性能的其他指标包括均方根误差(RMSE)和平均绝对误差(MAE)。

r 平方

作者图片

均方根误差

作者图片

平均绝对误差

作者图片

R 平方的值从 0 到 1,越高越好,而 RMSE 和 MAE 的值与功率输出(W)的单位相同,越小越好。

超参数调谐

每个模型都使用随机搜索交叉验证方法进行了调整,该方法能够根据模型在多次分割训练数据时的性能选择最佳超参数组合。

特别地,超参数的 1000 个排列被选择并应用于训练数据的 4 个分裂。测试数据仍然不可见,并将用于对不同算法选择的模型进行最终评估。

四重交叉验证(作者图片)

模型堆叠

使用 Scikit-learn- python 机器学习库中的堆叠回归器模块将四个不同的模型(KNN、DNN、RF 和 LGBM)组合在一起。一个简单的线性回归模型被用作元学习器,并根据基本模型以及原始输入特征的 4 倍交叉验证预测对其进行训练。

堆叠回归器使用 cross_val_predict 函数,该函数为定型数据中的每个示例返回该示例位于验证集中时获得的预测。这些跨不同基础模型的预测被用作元学习者的输入(参见 Sklearn【3.1.1.2 用户指南】了解详情)。这种方法降低了过度拟合的风险。

模型堆叠(图片由作者提供)

结果

交叉验证分数

不同算法的 1000 个超参数随机排列的交叉验证(CV) R 平方得分显示在下面的箱线图中:

不同算法 1000 次迭代的 CV 分数。(图片由作者提供)

如箱线图所示,LGBM 模型对超参数选择最敏感,而 KNN 最不敏感。

接下来,每种算法类型的最佳 CV 分数显示如下:

四重 CV 最佳成绩。(图片由作者提供)

结果显示,最佳 RF 模型在所有研究的算法中具有最高的 CV 分数。

测试数据得分

使用占整个数据集 20%的保留集来评估每个模型的性能。结果总结如下:

根据看不见的测试数据建立性能模型。(图片由作者提供)

与 KNN(基线)模型相比,堆叠模型的整体性能最佳,提高了 10%。此外,LGBM 模型是基于所有考虑指标的最佳基础模型。

应该注意的是,所有模型都可以很好地推广到未知的测试集,CV 和测试分数之间的性能相当。

特征重要性

使用能够计算特征重要性的 LGBM 和 RF 模型,下表显示了用于预测太阳能输出的前 5 个特征的重要程度。

使用树方法的前 5 个分级特征重要性。(图片由作者提供)

环境温度、湿度、云顶和压力出现在 LGBM 和 RF 模型的前 5 个特征中。使用 RF 模型获得的特征重要性排序与 Pasion 等人报道的前 5 个结果一致。艾尔。, 2020.

结论

标准数据科学技术已被应用于预测 12 个不同地点的太阳能输出。结果表明,LGBM 是最好的基础模型,而堆叠模型的整体性能最好。

最佳模型可能会针对个别位置进行进一步训练,以获得更好的分数,但这不在本分析范围内。

最后,本文所采用的方法可以作为解决可再生能源领域预测分析问题的标准程序。

什么更有趣?你可以通过我下面的推荐链接订阅 Medium 来获得更多我和其他作者的启发性文章,这也支持我的写作。

https://aolaoye.medium.com/membership

参考

帕西恩角;t .瓦格纳;科斯尼克,c;舒尔特公司;使用天气和位置数据对水平光伏电池进行机器学习建模。能量 2020,13,2570;doi:10.3390/en13102570。

Scikit-learn 1.0.2 用户指南

用一个简单的 Python 模型预测 2022 年世界杯

原文:https://towardsdatascience.com/predicting-the-fifa-world-cup-2022-with-a-simple-model-using-python-6b34bdd4f2a5

获胜者是…

图片来自 Shutterstock,授权给 Frank Andrade(由 Canva 编辑)

很多人(包括我)称足球为“不可预测的比赛”,因为一场足球比赛有不同的因素可以改变最终的比分。

那是真的…在某种程度上。

很难预测一场比赛的最终比分或获胜者,但在预测一场比赛的获胜者时就不是这样了。在过去的 5 年里,拜仁慕尼黑赢得了所有德甲冠军,而曼城赢得了 4 个英超联赛冠军。

巧合吗?我不这么认为。

事实上,在 20-21 赛季中期,我创建了一个模型来预测英超、西甲、意甲和德甲的冠军,它成功地预测了所有冠军。

这个预测并不难做出,因为那时已经打了 19 场比赛。现在我运行同样的模型来预测 2022 年世界杯。

以下是我如何使用 Python 预测世界杯的(关于代码的更多细节查看我的 1 小时视频教程

我们如何预测比赛?

做预测有不同的方法。我可以建立一个奇特的机器学习模型,并输入多个变量,但在阅读了一些论文后,我决定给泊松分布一个机会。

为什么?让我们来看看泊松分布的定义。

泊松分布是一种离散的概率分布,描述在固定的时间间隔或机会区域内发生的事件数量。

如果我们把一个进球想象成一场足球比赛 90 分钟内可能发生的事件,我们可以计算出 A 队和 b 队在一场比赛中可能进球的概率。

但这还不够。我们仍然需要满足泊松分布的假设。

  1. 可以计算比赛项目的数量(一场比赛可以有 1、2、3 个或更多目标)
  2. 事件的发生是独立的(一个目标的发生不应影响另一个目标的概率)
  3. 事件发生的速率是恒定的(在某个时间间隔内目标发生的概率应该与相同长度的其他时间间隔完全相同)
  4. 两件事不可能在同一时刻发生(两个目标不可能同时发生)

毫无疑问,假设 1 和 4 得到满足,但假设 2 和 3 部分成立。也就是说,让我们假设假设 2 和 3 总是正确的。

当我预测欧洲顶级联赛的冠军时,我绘制了过去 5 年中 4 大联赛每场比赛进球数的直方图。

4 个联赛的进球数直方图

如果你看看任何联盟的拟合曲线,它看起来像泊松分布。

现在我们可以说,可以使用泊松分布来计算一场比赛中可能进球的概率。

这是泊松分布的公式。

为了做出预测,我考虑了:

lambda:90 分钟进球数中值(A 队和 B 队)
x:A 队和 B 队在一场比赛中可以进的球数

为了计算 lambda,我们需要每个国家队的平均进球/失球数。这就引出了下一点。

每个国家队的进球/失球

在收集了从 1930 年到 2018 年所有世界杯比赛的数据后,我可以计算出每个国家队的平均进球和失球。

在我对欧洲四大联赛的预测中,我考虑了主客场因素,但由于世界杯几乎所有球队都在中立的球场比赛,所以我在这次分析中没有考虑这个因素。

一旦我有了每个国家队的得分/失球,我就创建了一个函数来预测每个队在小组赛中的得分。

预测小组赛阶段

下面是我用来预测每个国家队在小组赛中能拿到多少分的代码。它看起来令人生畏,但它只有我提到的许多东西,直到这一点转化为代码。

def predict_points(home, away):
    if home in df_team_strength.index and away in df_team_strength.index:
        lamb_home = df_team_strength.at[home,'GoalsScored'] * df_team_strength.at[away,'GoalsConceded']
        lamb_away = df_team_strength.at[away,'GoalsScored'] * df_team_strength.at[home,'GoalsConceded']
        prob_home, prob_away, prob_draw = 0, 0, 0
        for x in range(0,11): #number of goals home team
            for y in range(0, 11): #number of goals away team
                p = poisson.pmf(x, lamb_home) * poisson.pmf(y, lamb_away)
                if x == y:
                    prob_draw += p
                elif x > y:
                    prob_home += p
                else:
                    prob_away += p

        points_home = 3 * prob_home + prob_draw
        points_away = 3 * prob_away + prob_draw
        return (points_home, points_away)
    else:
        return (0, 0)

简单地说,predict_points计算主队和客队能得多少分。为此,我使用公式average_goals_scored * average_goals_conceded计算了每个团队的 lambda。

然后,我模拟了一场比赛从 0–0 到 10–10 的所有可能得分(最后一个得分只是我目标范围的极限)。一旦我有了λ和 x,我就用泊松分布的公式来计算p

比方说,如果比赛分别以 1-0(主场获胜)、1-1(平局)或 0-1(客场获胜)结束,prob_homeprob_drawprob_away累加p的值。最后,用下面的公式计算分数。

points_home = 3 * prob_home + prob_draw
points_away = 3 * prob_away + prob_draw

如果我们用predict_points来预测英格兰对美国的比赛,我们会得到这个。

>>> predict_points('England', 'United States')
(2.2356147635326007, 0.5922397535606193)

这意味着英国将得到 2.23 分,而美国将得到 0.59 分。我得到小数是因为我在用概率。

如果我们将此predict_points函数应用于小组赛的所有比赛,我们将获得每个小组的第一和第二名,从而获得接下来的淘汰赛。

图片由作者用 Canva 编辑

预测击倒

对于淘汰赛,我不需要预测积分,而是每个括号中的获胜者。这就是为什么我在之前的predict_points函数的基础上创建了一个新的get_winner函数。

def get_winner(df_fixture_updated):
    for index, row in df_fixture_updated.iterrows():
        home, away = row['home'], row['away']
        points_home, points_away = predict_points(home, away)
        if points_home > points_away:
            winner = home
        else:
            winner = away
        df_fixture_updated.loc[index, 'winner'] = winner
    return df_fixture_updated

简单来说,如果points_home大于points_away则胜者为主队,否则,胜者为客队。

感谢get_winner函数,我可以得到前面括号中的结果。

图片由作者用 Canva 编辑

预测四分之一决赛、半决赛和决赛

如果我再用一次get_winner,我就能预测出世界杯的冠军。下面是最终结果!!

图片由作者用 Canva 编辑

通过再次运行这个函数,我得到获胜者是…

巴西!

就是这样!我就是这样用 Python 和泊松分布预测 2022 年世界杯的。要查看完整的代码,请查看我的 GitHub 。你也可以查看我的介质列表,查看与这个 Python 项目相关的所有文章。

用 Python 学习数据科学? 通过加入我的 20k+人电子邮件列表,获得我的免费 Python for Data Science 备忘单。

如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,让您可以无限制地访问数以千计的 Python 指南和数据科学文章。如果你用我的链接注册,我会赚一小笔佣金,不需要你额外付费。

https://frank-andrade.medium.com/membership

用机器学习预测 NBA MVP

原文:https://towardsdatascience.com/predicting-the-nba-mvp-with-machine-learning-c3e5b755f42e

建立一个机器学习模型来预测 NBA MVP 并分析最具影响力的变量。

(照片由黛安·皮凯蒂诺在 Unsplash 上拍摄)

每个赛季,总会有一场关于 NBA 最有价值球员的大讨论,这是一个篮球运动员可以获得的最大个人奖项。对于一个不熟悉这项运动的人来说,很难解释这个奖项的标准。

已经让一些人感到困惑的是,MVP 不是对最佳球员的奖励,而是对常规赛最有价值的球员的奖励。那么,问题来了:变得更有价值意味着什么?

意思是对其团队有最大正面影响的球员。这意味着团队的表现也是影响个人奖项的一个变量,因为它必须有良好的记录来证实这种影响。为此,球队必须有一个好的支持名单来帮助 MVP,因为他不能靠自己赢得每一场比赛。然而,名单不可能那么好,因为在这种情况下,他们不需要 MVP 来获得好的种子。

你已经可以注意到这个奖项是多么令人困惑和主观。

考虑到这一点,我决定应用机器学习技术来观察 MVP 选择逻辑中的模式,验证在这个选择中最重要的统计数据,以及是否有可能创建一个模型,在 2022-23 常规赛结束时,可以在官方结果公布之前预测 MVP

所有用到的代码和数据都可以在 GitHub 上找到。

这段文字是用葡萄牙语写的原文的翻译,可用 此处

理解问题

在此之前,了解奖项的投票系统是如何工作的,以尽可能最好的方式处理数据是很重要的。

我们想要问题的答案:谁会成为 NBA 的 MVP?

所以,有人可能会认为这是一个分类的问题,选项是 MVP 或者非 MVP 。然而,这种方法会遇到几个问题,因为非 MVP 球员的数量比 MVP 球员多得多,使得模型的训练和评估很困难。

因此,我们应该将此视为一个回归问题。但是用什么变量作为目标呢?让我们了解投票是如何决定的。

目前,MVP 奖项的投票由不隶属于球队或球员的媒体成员进行,每个人选择一个第一名(10 分),第二名(7 分),第三名(5 分),第四名(3 分)和第五名(1 分)。

得分最高的球员被选为最有价值球员。然而,这不是我们将在回归中使用的总点数,因为选民的数量在不同的年份可能会有所不同,这可能会改变总点数。为了消除这个问题,我们将尝试预测的变量是 MVP 份额

MVP 份额无非是球员获得的分数除以该年可能获得的最大总分数,数值从 0 到 1。

MVP 份额公式(图片由作者提供)

因此,MVP 份额最高的球员将被模型定义为 MVP。

数据

使用的单项统计数据是:

  • 平均每场统计
  • ****总计统计
  • ****高级统计

还使用了:

  • 落点(种子)团队胜率
  • MVP 投票结果( MVP 份额)

总的来说,这项研究对每个玩家有 71 个变量。

上一个问题的答案"更有价值是什么意思?“T29”还有另一种程度的可变性:它趋向于随着时间而变化。使用非常老的赛季的数据最终会导致最近几个赛季的预测结果更差,因为关于 MVP 的想法和讨论不再完全相同。

因为目标是预测 2022-23 赛季的 MVP,所以从 2006-07 赛季开始(直到 2021-22)的数据将用于这项研究,总共 16 个赛季。

上面引用的数据是针对赛季所有进场至少 1 分钟的球员收集的,取自 篮球参考

数据处理

即使考虑到回归问题,MVP 份额非零的球员数量仍然很少,通常每个赛季在大约 450 名球员中有 10 到 14 名。这表征了一个不平衡的数据集。

因此,为了实现模型的良好性能,我们需要做出一些考虑。一种可能是只选择那些在众所周知的 MVP 竞赛 中的球员,这是一个非官方的 10 名球员排名,在赛季中更新。或者,只有那些非零 MVP 的份额(仅用于训练,因为在真实情况下我们不会有这些信息)。

然而,这将在某种程度上促进模型的部分工作,已经选择了最好的球员和最有可能赢得奖金。由于这项研究的想法是,该模型可以预测所有可能的球员的 MVP,而无需任何外部帮助,这不是我们将要做的。

那么,你如何缩小玩家的数量,对他们进行筛选,以确保没有可能获奖的人被删除?在这个阶段,我们看到了拥有领域知识以最佳方式指导项目的重要性。

想象一下:一个场均只有 10 分的球员是不会赢得 MVP 的,对吗?以便玩家可以毫无问题地从基座上移除。

过去 16 个赛季的 MVP 统计(图片由作者提供)

或者,一个 82 场比赛只打了 30 场的球员,或者没有进季后赛,等等。因此,我们可以为被考虑的玩家建立一些最低标准。

考虑到这一点,我对历史上 MVP 的最低平均分和统计数据进行了调查以建立这些最低标准( 统计使用 使这个过程变得容易得多):

  • 卡尔·马龙是 98-99 赛季的 MVP,打了 49 场比赛
  • 韦斯·昂赛尔德是 68-69 赛季的 MVP,PPG 13.8 分,FGA 10.9 分,T21 19 分
  • 史蒂夫·纳什是 04-05 赛季的 MVP,3.3 个篮板球
  • 摩西·马龙在 82-83 赛季以 1.3 秒的优势获得 MVP
  • 鲍勃·库西以 37.8%的命中率成为 56-57 赛季的 MVP
  • 扬尼斯·阿德托昆博在 19-20 赛季以 30.4 分钟的成绩获得 MVP
  • 卡里姆·阿布杜尔·贾巴尔是 1976 年唯一没有进入季后赛的 MVP
  • 戴夫·考恩斯是 72-73 赛季的 MVP,根据 18.1 的获得
  • 从来没有一个 MVP 在赛季中途被交易,他赢得了这个奖项

以这些数值为基数,我们可以过滤掉绝大多数球员,每个赛季只留下 20 到 30。这是在几乎普遍的前提下实现的,没有放弃公正性。

这些过滤器将使评估模型在训练和测试中的性能变得更加容易。

现在,数据库准备好了,我们可以开始创建模型了。

建模

最初,使用了几种回归模型:

  • 支持向量机(SVM)
  • 弹力网
  • 随机森林
  • AdaBoost
  • 梯度增强
  • 光梯度推进机(LGBM)

使用均方根误差( RMSE )和决定系数( R )来评估它们中每一个的性能。

RMSE 公式(左)和 R 公式(右)(图片由作者提供)

RMSE 计算实际值和预测值之间的均方根误差。它被广泛使用,因为它是一个惩罚大误差的度量(平方时),但它与感兴趣的变量在同一单位(求根时)。即其值越低越好。

然而,与变量使用相同的单位会使定义什么是好的可接受的 RMSE 值变得困难。因此,我们也将使用 R 作为替代指标。**

R 代表模型解释的方差比例,范围从 0 到 1。也就是说,越接近 1,模型可以解释的数据的可变性越大。

结果

起初,为了更好地评估每个型号的能力,2021-22 赛季被留出用于测试,而剩余的 15 个赛季被用作训练

这样,除了为研究的其余部分定义优化参数之外,还获得了每个模型的 RMSE 和 R 值。

在该过程结束时,获得了以下结果:

2021-22 赛季不同车型的 RMSE 和 R 值(图片由作者提供)

SVM随机森林LGBM 分别获得了最低的 RMSE 值和最高的 R 值 AdaBoost梯度增强也有足够的结果,其中弹性网在使用的模型中具有最差的度量。

****现在来看最后的排名,非常积极的一点:6 款中有 5 款拿到了 Nikola Joki 作为 MVP 😗***

2021-22 赛季不同车型 MVP 之争前三名(图片由作者提供)

梯度推进是唯一一个把扬尼斯·阿德托昆博列为 MVP 的。 AdaBoost 排在第三位Luka don CII(他排在第五位)。

除此之外,所有其他的都答对了前 3 名。然而,模型将吉安尼斯列为争议中的第二名,将乔尔·恩比德列为第三名,而实际排名恰恰相反。

这可以用一个事实来解释,尽管詹尼斯有一个出色的赛季,他的团队也有良好的表现(雄鹿队与 76 人队有着相同的竞选活动,按照决胜局标准排在前面),但他患有所谓的 选民疲劳

詹尼斯最近两次获得 MVP,公众和选民都倾向于,甚至是无意识地,更喜欢一个有争议的新兴球员(比如恩比德)而不是一个已经获得该奖项的人(尤其是如果不止一次)。

勒布朗已经遭受了这一点,并且已经预料到下赛季会发生什么,仅仅是过去两年的获奖者乔基奇连续第三次赢得 MVP 的一次历史性表演。

变量分析

可视化模型预测中最重要的变量的一种方法是使用 SHAP 值(SHapleyAadditive exP**注释)。SHAP 是一种基于博弈论的技术,它允许你合理地解释每个变量对模型预测的影响程度。****

让我们以 SVM 模型为例,它获得了最佳指标:

与 SVM 模型相关的 SHAP 图表(图片由作者提供)

这个图表有很多信息,我们来理解一下:

Y 轴上,我们有对 SVM 模型影响最大的 20 个变量。每个点代表一个玩家,对于这些变量中的每一个,都有一个从红色到蓝色的颜色渐变,其中蓝色表示有问题的变量的低值红色高值****

在 X 轴上的是获得的 SHAP 值。越靠右越大,该变量对兴趣变量(MVP 份额)的正面影响越靠左对 MVP 份额变量的负面影响越大。

例如,对于种子变量:种子的高值(糟糕的排名)对 MVP 份额产生负面影响,而低值(良好的排名)对其产生正面影响,这是有意义的。

通过这张图表,我们可以得出一些重要的结论:

  • 最有影响力的三个个人变量是: ( 玩家效率等级)、 WS/48 ( 每 48 分钟赢股)和 BPM ( 框加减)
  • 两个集体变量、种子百分比(胜率)是 6 个最有影响力的变量(分别为第二和第六)
  • 11 个最重要的变量中有 7 个是高级统计
  • 在三个最受欢迎的统计数据(场均得分、篮板和助攻)中,只有场均得分出现在榜单上,排在第 16 位。

有趣的是观察到高级属性在普通属性上的优势,因为它们更好地区分了玩家的良好表现,而不管他们打什么位置。

例如,一个中锋往往比一个控球后卫有更高的篮板平均数,而控球后卫又往往有更高的助攻平均数。因此,模型不能单独用这些变量来区分 MVP 球员。

赢得份额例如,这是一种统计数据,旨在衡量团队胜利中每个球员的信用,在这种意义上证明更有效(该统计数据的计算方法可以在 这里找到 )。

根据玩家效率等级,是模型中最有影响力的变量。这是一个试图显示球员每分钟生产率的数字,也是当今最好的统计数据之一。我们可以看到,在右边的红点中,该模型非常重视高 PER 玩家。更详细的计算程序可以在 这里找到

另一个有趣的点是模型在 ( 失误)中发现的关系。直觉上,我们认为一个场均失误率高的球员成为 MVP 的几率会更低,对吗?

然而,由于 MVP 通常手里有很多球,他每场比赛的失误次数往往远大于零,这一行为被模型捕捉到了,这与常识相反。**

附加结果

通过最近一个赛季的参数优化和对最重要的统计数据的分析,这项研究的目的已经部分完成,现在只等待 2022-23 NBA 常规赛结束(4 月/23 日),以便模型可以预测 MVP。

但是为什么不多探索一下他们,看看他们在老赛季的表现如何?

从 2006 年到 2007 年所有季节获得的结果(图片由作者提供)

在 MVP 的确定方面,你可以看到模型表现良好,在过去的 16 个 MVP 中,所有模型都答对了 12 个,只有随机森林例外,他在 16 个 MVP 中只答对了 10 个。****

RMSE 和 R 指标的下降也很显著,这是奖项标准随着时间推移而自然变化的原因之一,如前所述。

在过去 16 个赛季的不同模型中获得的 RMSE 和 R 值的平均值(图片由作者提供)

只有 3 个赛季大部分车型(少于 3 个)无缘 MVP:2016–172010–112007–08****

也许最令人惊讶的是 2016-17 赛季,拉塞尔·维斯特布鲁克是自奥斯卡·罗伯特森 1961-62 赛季以来第一个场均三双的球员,在凯文·杜兰特离开后,他带领 OKC 进入了季后赛。毫无疑问,这个奖项应该颁给他,但模特们选择了勒布朗·詹姆斯、科怀·伦纳德和詹姆斯·哈登,只有一个人选择了威斯布鲁克作为 MVP。其中一个因素可能是球队的低种子(联盟第 10),正如我们所见,这是对结果影响最大的变量之一。

同样的事情也发生在2010-11德里克·罗斯身上,但这一次四个弄错的模特汇聚在勒布朗·詹姆斯身上赢得了这个奖项。****

2007-08 赛季,科比是唯一一个没有入选 MVP 的赛季:他们又选了勒布朗。

在我看来,这比其他球员更能说明勒布朗。这表明,根据最有价值球员的标准,他总是在竞争中,可能应该赢得更多的奖项。

(照片由 Howard Bouchevereau 在 Unsplash 上拍摄)

结论

本该只是一个预测下一届 MVP 的模型创造,却变成了一个关于这个奖项的定义和历史的非常有趣的研究。我希望这是有用的,并有助于提高对该奖项和所使用的机器学习工具的理解。

预测体育运动总是一件困难的事情。然而结果非常令人满意,模型被证明是预测 MVP 的有效方法,重点是最近一个赛季表现更好的 SVM ,以及在旧赛季表现更好的梯度推进****

值得注意的是,总有改进的余地。例如,可以添加更多的变量,如 NBA 经历的赛季、选秀次数和身体信息(身高、体重、翼展)。例如,用已经赢得的最有价值球员的数量来标记,可以帮助观察到的选民疲劳问题。

同样,使用的所有代码和基础都可以在 GitHub 上获得。

2023 年 4 月,我们将用全新的数据更新模型。

会做对吗?我们拭目以待……

(照片由 JC Gellidon 在 Unsplash 上拍摄)

我在我的频道上总是有空的(LinkedInGitHub )。

感谢您的关注!👏

加布里埃尔·斯佩兰萨·帕斯托雷罗

利用机器学习预测下一届 NBA MVP

原文:https://towardsdatascience.com/predicting-the-next-nba-mvp-using-machine-learning-62615bfcff75

在 NBA 历史 MVP 候选人的数据上训练和验证机器学习模型,以预测 2021-2022 赛季 MVP

图片来自 Unsplash

最有价值球员奖是 NBA 球员职业生涯中所能获得的最有声望的荣誉之一。在每个常规赛结束时(通常在四月/五月),MVP 奖会颁发给一个被认为配得上这个称号的球员。

然而,通过所有的媒体,仍然有许多关于最有价值球员定义的问题和争论。是一个取得杰出个人统计成功的球员吗?是常规赛最伟大球队的最佳球员吗?还是说这一切都是数字无法定义的叙事/故事线?

虽然这个讨论可能没有明确的答案,但可以利用诸如机器学习模型以及相关统计数据等工具来找到一些模式,并洞察 NBA MVP 评选的逻辑。

在这个项目中,以往赛季的 MVP 候选人的历史数据将在这种背景下进行研究。正如许多常见的数据科学实验一样,在选择最可行的模型之前,将训练和评估各种机器学习模型。然后,选择的模型将被用来预测本赛季(2021-2022)最有价值的球员。

所有相关的代码和数据都存储在这个 Github 库 中,实验的快捷方式就在这个 Jupyter 笔记本文件 中。

目录

  • 方法:说明对该问题采取的方法
  • 数据:数据来源和数据处理方法
  • 实验:实验和假设的描述
  • 结果:实验的最终结果
  • 预测 2022 年 MVP :利用最佳模型预测本季 MVP

方法

在开始实验之前,必须了解 NBA 是如何决定最有价值球员的,以及如何利用统计数据。

联盟历史上的一个共同主题是,投票系统是由“主题专家”进行的,他们会投票选出他们认为最有价值球员奖应该奖励的人。然而,随着时间的推移,那些决策者应该是谁已经发生了一些变化(可以在此链接中了解这些变化)。

目前,投票者是 100 名独立媒体成员,他们既不属于任何球队,也不属于任何球员。每个成员在加权投票系统中选择玩家:第一名投票(10 分),第二名投票(7 分),第三名投票(5 分),第四名投票(3 分),第五名投票(1 分)。获得最多“分数”的球员被选为该赛季最有价值球员。

这就引出了这个实验的方法。各种统计数据将被用作模型使用 MVP 投票点预测 MVP 的“特征”。

为了在评估这些数据时保持一致,每个特定球员的 MVP 投票点数可以除以该赛季的 MVP 总投票点数。这个指标通常被称为 MVP 份额

MVP 份额=(特定球员的 MVP 分数)/(总 MVP 分数)

拥有最高 MVP 份额的球员赢得常规赛 MVP 奖项

2021 赛季 MVP 投票前 3 名候选人;请注意,最大可能点数是 1010(图片由作者提供)

首先,这个练习可以被视为一个回归问题(预测数值变量作为目标),在这种情况下,机器学习模型试图预测的目标变量是 MVP 份额指标。这将在每一个常规赛中分别进行,利用该赛季的 MVP 候选人名单。

然后,具有最高预测 MVP 份额指标的球员被模型标记为预测 MVP。将该赛季实际 MVP 的名称与预测 MVP 进行比较,以验证模型是否正确

(图片来自UnsplashJC gelli don

为了评估所有季节的模型(如果有可用的相关数据),可以使用受“洗牌”启发的方法。例如,对于每个完成的季节(从 1980 年到 2021 年),模型将针对一个特定季节的数据进行测试,并根据未选择的其他季节的数据进行训练。这将重复进行,直到所有单个季节的数据都经过测试和预测,以计算整体准确性。

下面是一个迭代的例子,其中 训练数据 被设置为 1980–1981 和 1983–2021的 MVP 候选人; 测试数据 被设置为 1982 赛季的 MVP 候选人其中模型试图预测 MVP 份额

1 次迭代的例子:训练1980–1981&1983–2021 MVP 候选人,测试1982 MVP 候选人(图片来自作者)

:以上可视化只是一个 的例子,一次迭代 ,在实验过程中,每年轮流作为测试数据进行评估,其余年份作为训练

由于 1982 年的实际 MVP 份额是已知的,它可以用来与预测的 MVP 份额进行比较。通常,MAE(平均绝对误差)和 R(决定系数)是评估回归模型的一些流行的度量。 MAE 是成对观测值之间误差的度量R 是数据与模型拟合程度的统计度量,或者模型能够解释多少方差/不确定性。这两个指标的公式如下所示:

: MAE(平均绝对误差)公式;右侧 : R(决定系数)公式

对于性能良好的模型,期望 MAE 的值较低(范围从 0 到∞**), R 的值****较高(范围从 0 到 1)。**

虽然在这种情况下,这两个指标并不是唯一需要考虑的重要因素。例如,当在 MVP 竞争非常接近前两名候选人的赛季进行测试时,预测的 MVP 份额可能非常接近这两名候选人的实际 MVP 份额。然而,获得模型最高预测 MVP 份额的候选人可能在实际比赛中没有获得最高 MVP 份额。

(来自 Unsplash 的 Markus Spiske 摄影)

如何确定最佳模式?重申“洗牌”启发的方法,每次迭代将把一年作为测试数据,其他年份不被选为训练数据。然后将每年获得最高预测 MVP 份额的候选人与实际 MVP(获得最高实际 MVP 份额的候选人)进行比较。将为每年创建一个标签(正确/不正确),如下所示

训练/测试后所有迭代的模型总结;所选年份的测试未选年份的训练(图片由作者提供)

使用这些标签,可以创建准确性度量。

准确度=(正确的标签)/(正确的标签+不正确的标签)

从 1980 年到 2021 年总共有 42 个季节。例如,如果模型预测所有赛季球员的 MVP 份额,并且 MVP 中的 39 个被正确地识别为在他们各自的年份中具有最高预测份额,则模型的准确性将是(39 / 42) = 92.8%。

这个项目的目标已经确定。主要目标是构建产生最高精度**【最正确的标签】的模型,次要目标是验证这些模型具有最小化的平均 MAE* (所有年份的平均 MAE)和最大化的 R (所有年份的平均 R)。*

数据

需要两个主要的数据实体;(1)MVP 候选人历史统计包括所有被考虑的赢家和输家以及(2) 当前 2021-2022 赛季 MVP 候选人统计

(1)MVP 候选人的历史统计来自Basketball-Reference.com。每个赛季的 MVP 赛都有一个网页(即 2021 MVP 赛)。虽然 NBA 可以追溯到 1949-1950 赛季,但历史数据是为现代地区收集的(1979-1980 至 2020-2021 赛季)

(2)统计当前 2021–2022 赛季 MVP 候选人,候选人姓名摘自 NBA.com 的 MVP 竞赛。赛季期间,NBA 每周都会发布一篇关于该奖项前 10 名候选人的新文章。对于这些候选人,这些球员的统计数据是从 Basketball-Reference 中提取的。

个人基本和高级统计以及团队统计被映射到适当的球员和赛季年。利用熊猫 HTML 表格抓取功能、美汤和这个篮球参考抓取工具的组合来抓取原始数据。

探索性数据分析

检查数据时,必须观察与响应变量(MVP 份额)的关系。

一些一般特征(x 轴)相对于 MVP 份额(y 轴)绘制。团队获胜的数量团队种子通常是决定 MVP 讨论的一些关键因素。此外,还绘制了一个名为 VORP (替代球员价值:用于衡量球员对球队的整体贡献)的高级统计数据。

散点代表从 1980 年到 2021 年的每个候选人,实际的 MVP 获得者用蓝色突出显示

Wins 与 MVP 份额(图片由作者提供)

种子与 MVP 份额(图片由作者提供)

替代球员的价值与 MVP 份额(图片由作者提供)

另一个有趣(但可能很明显)的特性是使用率。使用率是对一名球员参与的团队比赛百分比的估计。

使用百分比与 MVP 份额(图片由作者提供)

在这一点上,这些变量似乎与 MVP 份额指标有某种线性关系。在进行实验时,将它们视为模型特征是有效的。

查看完整 EDA,查看 Jupyter 笔记本文件

实验

对于实验,有一个特征部分和建模的来回迭代。特征各种组合被选择并用作模型的输入。重复这个过程,直到建立了合理的模型。

功能选择

主表一度有多达 45 列(包括球员和球队名称等基本信息),但只有几列用作功能。仅利用相关特征将有助于降低噪声并提高机器学习模型的性能

识别这一点的一种方法是通过一种叫做互信息的方法。互信息是测量特征和目标之间关联的函数,它看起来类似于检测线性关系的相关性。然而,这种度量能够检测任何类型的关系(而不仅仅是线性的)。在链接中有简单的解释,在链接中有更多的统计解释。

功能与 MVP 份额的互信息得分如下所示。

所有特征的互信息分数(图片由作者提供)

许多突出团队和个人玩家成功和/或效率的高级统计在这种相互信息分数可视化上排名很高。然而,有趣的是,几个主要的计数框得分统计(助攻,盖帽,抢断)在这个名单的底部。一种理论可能是,因为历史上 MVP 球员已经担任了各种位置的角色,并拥有不同的比赛风格,这些盒子得分统计数据的信息通常不会揭示 MVP 份额变量的许多不确定性。

互信息的免责声明是,得分高的特征并不总是影响模型性能。互信息的有用性可能因模型类型而异

这个工具与人类直觉和领域知识一起被用来试验特性的组合。经过多次反复试验和错误,最终特性如下:

最终选定特征的互信息分数(图片由作者提供)

许多包含重复信息的功能被删除。例如,“赢球份额”只是“每 48 分钟赢球份额”的一个统计版本,这是一名球员对球队的进攻和防守贡献的比率。选择速度指标而不是计数指标的一个原因是因为 模型将用于预测当前 2022 赛季 的 MVP。因为本赛季仍在进行中,今年的统计数据还不完整。对于基于速率的指标来说,这不是什么大问题。

一个例外是超过替换球员(VORP)指标的值。该特性在建模过程中被证明是一个强有力的指标。然而,因为这是一个计数统计,2022 赛季的 MVP 候选人不会有完整的值。为了这个项目,这可以调整为某种程度上的预测。这将在预测部分详细提及。

建模

选择了几个回归模型:

  • 线性回归
  • 随机森林回归量
  • XGBoost 回归器(极限梯度推进)
  • LightGBM 回归器(光梯度推进机)

选择线性回归是因为许多特征似乎与响应变量有线性关系。使用了三种基于树的模型(Random Forest、XGBoost、LightGBM)。随机森林是一种集成学习方法,通过构造大量决策树来操作。XGBoost 和 LightGBM 是树学习模型的几种更有效、更强大的变体。对于这些基于树的模型,进行了各种参数调整工作来寻找将输出最高性能的最佳参数。(注:数据仅用于线性回归;树模型不需要缩放

结果呢

各种型号的型号性能总结(图片由作者提供)

在四个模型中, XGBoost 模型的性能最好,准确率为 83.33% (在总共 42 个季节中猜对了 35 个季节),平均平均误差最低。LGBM 模型具有最高的平均 R,但错过了另外三个错误标记的季节(总共 42 个季节中的 32 个正确)。线性模型在所有三个类别中表现最差,将从分析中排除。

对于本分析的其余部分,XGBoost 模型的结果将比其他模型的结果更详细一些,从它的特性重要性开始。

来自 XGBoost 模型的特征重要性分数(图片由作者提供)

正如 EDA 和功能选择部分所预期的,高级统计数据如 VORP每 48 分钟赢得份额玩家效率等级以及团队统计数据如种子赢/输% 在功能重要性上得分较高。唯一剩下的常规盒子得分统计是每场比赛的点数(PTS ),这仍然对算法有一点贡献。该模型严重依赖于个人效率统计数据以及团队绩效指标。虽然使用率并不能突出球员的效率,但它可以强调一个球队在整个赛季对球员的依赖。

确认

为了验证模型,不同年份的 MVP 比赛将被检查模型什么时候表现好,什么时候表现不好。

好人

(照片由艾迪·拉克曼从 Unsplash 拍摄)

所有迭代后的 XGBoost 模型摘要:最近 13 年标记正确(图片由作者提供)

如前所述,根据 XGBoost 模型摘要,总共 42 个赛季的 MVP 中有 35 个被正确猜测,准确率为 83.33%。此外,最近 13 年的预测是正确的。(完整的 XGBoost 车型汇总表,请参见此链接)这包括几个有争议的年份,如 2011 (德里克·罗斯)和 2017 (威斯布鲁克)。

2011 年 MVP 赛

2011 年比赛的前三名候选人:模型特征,实际和预测的 MVP 份额(图片由作者提供)

2011 年,许多人认为勒布朗·詹姆斯甚至德怀特·霍华德比德里克·罗斯更应该获奖。事实上,勒布朗·詹姆斯在几乎所有的得分和高级统计数据上都超过了罗斯,这也支持了这个观点。然而,德里克·罗斯的使用率和团队记录略高,帮助他挤掉了勒布朗。

这也可以通过 SHAP 值的可视化来观察,这在理解一个复杂模型如何预测产量时很有用。 SHAP 价值观 衡量决策过程中每个特征的影响 。(关于 SHAP 值的详细解释可以在这里找到)

2011 年竞选的前三名候选人:SHAP 价值观可视化;注意,轴没有被一起缩放(作者图像);点击放大图像

红色条表示有助于每个候选人预测高 MVP 份额的特征。相反,蓝色条表示正好相反的情况,或者损害其案例的特征。这对于 MVP 份额的影响程度可以通过每个特征的条形段的长度来观察。

对于德里克·罗斯和勒布朗詹姆斯来说,模型中没有任何特征会影响他们的机会。然而,似乎成为头号种子队对罗斯来说意义重大。对于现实生活中排名第二的德怀特·霍华德来说,排名更低(第四)和其他一些数据降低了他的价值。

2017 MVP 赛

2017 年比赛的前 3 名候选人:模型特征,实际和预测的 MVP 份额(图片由作者提供)

2017 年竞选的前 3 名候选人:可视化的 SHAP 价值观;注意,轴没有被一起缩放(作者图像);点击放大图像

2017 年是一场混乱的比赛,R 指标非常低,为 0.01。自 2000 年以来,威斯布鲁克是第一个来自 6 号种子队的 MVP(从 SHAP 视觉化,这可以通过种子= 6 的最大蓝色条块观察到,降低了他的价值)。尽管如此,该模型利用球员效率评级、VORP 和威斯布鲁克占主导地位的使用率指标(41.65%的单赛季纪录)进行了正确预测。与此同时,该模型预测凯文·杜兰特和斯蒂芬·库里的高 MVP 份额(以上均未显示),这损害了 MAE 和 R。

坏了

(图片来自 Unsplash 善良好奇

所有迭代后的 XGBoost 模型摘要:过滤到标签不正确的年份(图片由作者提供)

2005 年& 2006 年 MVP 赛

可以将摘要过滤到不正确的标签,以查看模型出错的七年。最大的不幸发生在 2005 年和 2006 年,史蒂夫·纳什连续两届获奖。模型不仅猜错了玩家,MAE 也很高,R 是负数。这两年的 MVP 比赛对模型来说是绝对的灾难,在其他年份的训练后预测。

2005 年 MVP 候选人(左)和 2006 年 MVP 候选人(右):球员、种子、输赢百分比、实际份额、预测份额(图片由作者提供)

仔细观察这场比赛,模型预测史蒂夫纳什的 MVP 份额非常低。不仅如此,预测模型还支持其他候选人,如德维恩·韦德(2005)和德克·诺维茨基(2006)。通过媒体,有人说这两个奖项都是有争议的,人们认为沙奎尔·奥尼尔(2005)和科比(2006)比纳什更应该获奖。这可能是统计数据不能说明全部情况的几个案例之一,因为许多球迷都知道纳什对他的球队的进攻体系产生的无形影响。

2001 年 MVP 赛

2001 年比赛的前三名候选人:模型特征,实际和预测的 MVP 份额(图片由作者提供)

该模型也很难预测 2001 年,支持沙奎尔·奥尼尔(预测的获胜者)超过艾弗森(实际的获胜者)和蒂姆·邓肯(实际的第二名)。沙克在那一年拥有最好的先进数据,在球队胜率和输球率上与艾弗森持平。艾弗森唯一勉强超过的功能是播种和使用率。根据当前选定的特征,很难准确预测这一特定年份。

可以观察到,尽管能够以 83.33%的准确率猜测每年的第一名,但该模型通常不能很好地按正确的顺序预测所有候选人的 MVP 份额。这可能是因为具有最高精度的模型不一定具有最低的 MAE 或最高的 R。

预测 2022 年 MVP

(照片由来自 Unsplash 的科利昂·布朗拍摄)

这些模型现在可以用来预测 2022 年的 MVP 了。然而,还有一个指标需要预测(替补球员的价值),因为这一统计数据对于 2022 年仍在进行赛季的候选人来说并不完整。

为了规划这个“调整后的 VORP”,将需要当前的 VORP 统计和比赛数量的信息。

*调整后的 VORP =((当前 VORP) /(已玩游戏数)剩余游戏数)+(当前 VORP)

这种方法既不是传统的也不是理想的,但是它假设了一个球员在赛季末的 VORP(假设他在赛季剩余的比赛中有稳定的统计表现)。同样,这可能不会非常接近赛季结束时的实际 VORP,但每个 2022 年球员的 VORP 都会以相同的方式转变,以保持一致性。

最后,最好的 3 个模型(XGboost,LightGBM,Random Forest)根据历史数据(从 1980 年到 2021 年)进行训练,并用于预测 2021 年到 2022 年的 MVP 候选数据。结果如下:

模型结果:2021–2022 赛季 MVP 比赛预测(图片由作者提供)

(注意,这些是基于 nba.com 的 NBA MVP 阶梯候选人名单和 2021-2022 常规赛 结束时 的球员/球队统计的最终结果)

有趣的是,所有三个模型都预测尼古拉·约基奇将成为 2022 年的 MVP。

对于 XGBoost 模型,前 3 名候选人的 SHAP 值如下:

2022 年竞选前三名候选人预测:SHAP 价值观可视化;注意,轴没有被一起缩放(作者图像);点击放大图像

预测的 MVP 份额对 Joki 来说很高(预测份额= 0.68),因为他在高级统计方面表现出色,尽管他的球队是第六种子。

Giannis (预测份额= 0.57)在比赛中非常接近,但与 Joki 相比,他的统计数据稍逊一筹。

在 2 月和 3 月期间领先的恩比德*(预测份额= 0.43)在排名中下跌,很可能是因为他的受伤导致他的球队跌至第四名。***

更新 5/6/2022 :在整个 2021-2022 常规赛中,这是一场相当激烈的比赛,主要是在上述 3 名球员之间。当这项研究最初于 2022 年 1 月发布时,詹尼斯最初主导了模型预测。然后在 2 月和 3 月,恩比德在所有车型中领先。然而,随着本赛季在 4 月份结束,约基奇以其出色的先进统计数据脱颖而出。

这表明,模型很难准确预测直播赛季的 MVP(球员和球队的统计数据并不是一成不变的)。然而,当常规赛数据最终确定时,这个模型似乎做得并不坏。

结论

(照片由迪安·班尼特从 Unsplash 拍摄)

预测体育运动是一项非常困难的任务,充满了不确定性。在这个实验中,预测 NBA 最有价值球员奖的预测模型被建立和分析。虽然数字肯定不能说明全部情况,但有一些有趣的度量和统计模式对模型有所贡献。最后,利用这些模型,对 2022 年 MVP 进行了预测。在未来,看看这些模型是否能够执行并调整预测,以接近媒体和主题专家的意见,这将是非常有趣的。

预测 YouTube 视频上不喜欢的数量。第 2 部分—模型

原文:https://towardsdatascience.com/predicting-the-number-of-dislikes-on-youtube-videos-part-2-model-aa981a69a8b2

如何(尝试)使用 Tensorflow/Keras 建立深度神经网络来预测不喜欢的数量

预览。作者图片

在本文中,您将学习如何:

  • 使用 NLTK 预处理文本数据;
  • 在 TensorFlow/Keras 嵌入层中使用预训练的单词嵌入;
  • 使用 TensorFlow Functional API 构建非时序神经网络架构;
  • 使用tensorflow.keras.utils.plot_model()和 Netron 可视化神经网络结构。

这是下一篇文章的逻辑延续— 预测 YouTube 视频上不喜欢的数量。第 1 部分—数据集,我使用 YouTube 数据 API v3 收集数据集。

我在本文中使用的所有代码都可以在同一个 GitLab 资源库中获得;但是,请记住,本文和存储库中的代码可能略有不同,因为为了简化,这里我没有使用函数。

免责声明

老实对你和我自己说,我必须说我没能成功地解决这个问题。但是我认为不好的经历也是一种经历,这就是为什么我要和你分享它。最后,我会告诉你为什么会这样。

资料组

我在这个项目的第一部分详细描述了收集数据集的过程。现在我想记下我将使用其中的哪些字段进行预测:

  • video_id -唯一视频 ID -不作为特征使用,因为它不包含信息
  • title -视频标题-用作特写
  • channel_id -频道 ID -不作为特征使用,因为它不包含信息
  • channel_title -频道标题-用作特写
  • published_at -视频发布日期-不作为特征使用,因为所有视频属于同一时间段(2021)
  • view_count -视图数量-用作特征
  • likes -点赞数-用作特征
  • dislikes -不喜欢的数量-用作目标标签
  • comment_count -评论数量-用作特性
  • tags -作为一个字符串的视频标签-用作特性
  • description -视频描述-用作功能
  • comments - 20 个视频评论作为一个字符串-用作特征

因此,我们面临一个回归任务,有 3 个数字输入特性(view_countlikescomment_count)和 5 个文本输入特性(titlechannel_titletagsdescriptioncomments)。

数据准备

在来自数据集(37k)的所有视频中,我决定从那些在美国流行的视频开始——这大约是 16k 行。

因为不需要准备数字特性,所以我们需要做的就是分割所需的列,并将它们转换成 numpy 数组。代码非常简单:

清除文本数据

对于文本数据,一切都更复杂。首先,我创建了一个新的author_text特性,包含作者提供的所有文本信息。它由以下特征的串联组成:titlechannel_titletagsdescription。这样我们将把文本特征的数量从五个减少到两个,这将大大减少后面的模型参数的数量。

但在此之前,我们至少需要做一点数据清理。我决定去掉titlechannel_titletags的标点符号,去掉descriptioncomments栏的无意义词。

我使用了 NLTK 库来做这件事。为了第一次运行下面的代码,您必须下载一些 NLTK 数据——通过运行ntlk.download('something')命令记住这一点。

我们将使用 pandas 应用函数来清理数据,这允许我们为数据集的每一行应用一些函数。剩下的只是编写一个函数,它将返回一个干净的输入版本。

为了避免废话,我们只保留那些包含在nltk.corpus.words.words()中的单词。这套包含 235,000 个英语单词——请随意复制和探索下面的代码。

执行上述代码的结果。作者图片

函数将这个集合中出现的所有单词连接起来。它接收一个输入句子(当前行的一个元素)并返回它的干净版本。它将应用于某一列的所有行。

注意,nltk.tokenize.word_tokenize(sentence)将一个sentence字符串转换成一个单词数组,其中每个元素都是一个单词或一个标点符号。

执行nltk.tokenize.word_tokenize()功能的结果。作者图片

当我们删除标点符号时,我们应该只保留那些只包含数字和字母的单词。这也将删除在单词中间包含特殊字符的单词(例如“c@t”),但是这些单词将在嵌入阶段被忽略,所以这不是一个大问题。

使用预训练的单词嵌入将文本转换为矢量

现在我们需要将文本转换成计算机和我们的模型可以理解的数字。这里最重要的两个参数是:

  • max_tokens——词汇量的大小。TextVectorization层只会考虑max_tokens热门词汇;
  • sentence_length -一句话的最大字数。较长的句子将被截断,较短的句子将用特殊的<pad>符号填充。

如果max_tokens参数对性能影响不大,通常等于一个很大的数(10k,20k 等等),sentence_length对它影响很大。稍后我们将看到,该值是矩阵的维度之一,如果它太大,矩阵将会很大,但很稀疏,如果它非常小,我们就有丢失大量信息的风险。

选择此参数的一个好方法是建立一个直方图,即句子中单词数量的直方图,并计算一些分位数。之后,您可以选择一个覆盖数据集 80%或 95%的值。

评论和作者 _ 文本的句子字数直方图。作者图片

您可以在下面看到创建这些直方图的代码(这是data_transformations.ipynb笔记本的一部分):

让句子成为文本数据集的一个单元。例如,您将通过执行df['comments'][0]命令获得一个句子。

最初,我们正在处理大小为(n, ?)(其中n是数据集的大小)的张量,因为所有的句子都有不同的长度。我们必须将这些句子转换成固定长度的单词数组。在标记化之后(我们在上面看到的nltk.tokenize.word_tokenize(sentence)),短于sentence_length的数组用一个特殊的填充符号填充,长于sentence_length的数组被截断。现在我们正在处理一个大小为(n, sentence_length)的张量。

之后,我们需要给句子中的每个单词分配一个向量——这就是嵌入。如果不使用预训练的单词嵌入,这些向量将在学习过程中由网络学习,以及其他参数。但是获得代表词嵌入是一项相当困难的任务,需要大量的数据和计算能力。给每个单词分配一个长度为emb_dim(嵌入维数)的向量,我们将得到一个大小为(n, sentence_length, emb_dim)的张量。

我在这里描述的过程正式记录在[pretrained_word_embeddings.ipynb](https://gitlab.com/Winston-90/youtube_dislikes/-/blob/main/pretrained_word_embeddings.ipynb) 笔记本中。基于这段代码,我创作了下面这张图片,与文本相比,它会更加清晰。

使用单词嵌入预处理文本。作者图片

这个玩具数据集包含 8 个独特的词:“我”,“喜欢”,“猫”,“和”,“狗”,“apdnv”,“是”,“什么”。所有这些词都存在于从 8+2 ≤ max_tokens(设置为 10)以来的数据集中学习到的词汇中。同时,词汇表的第一个元素(’’)是<pad>符号,第二个元素(’[UNK]’ )—未知单词的特殊符号(在这种情况下,如果我们将max_tokens <设为 10,就会用到它)。这两个符号总是出现在词汇表中。

请注意,单词“I”、“like”和“cats”的单词嵌入与第一句和第二句相似,也与embeddings\glove.6B\glove.6B.50d.txt文件中的相应行匹配。此外,单词“apdnv”被嵌入到零向量中,因为嵌入字典不包含这个单词。

确保嵌入匹配。作者图片

您可以从下面的链接下载 40 多种语言的免费预训练单词嵌入。

建筑模型

最后,我们可以去网络。为什么是神经网络?

这项任务中的主要信息由文本特征携带——它们具有最强的预测能力。时间表明,经典的机器学习算法处理文本处理的能力比神经网络差得多。我们说的是 NLP — 自然语言处理。这个领域包括各种各样的任务——从命名实体识别词性标注文本摘要机器翻译。当前的任务最类似于情感分析——预测文本的“情绪”。

神经网络体系结构

让我们转到最有趣的部分——构建模型的架构。

让我描述一下我在构建模型架构时使用的主要启发方法:

  1. 为了将文本转换成向量,我们将使用预训练的单词嵌入
  2. 由于任务不是严格顺序的,而且整个文本对模型是可见的,我建议用双向 LSTM 代替单向;
  3. 为了从文本中提取更多的信息,我们将使用多层 LSTM 。同时我们会用跳过连接来简化学习过程;
  4. 对于评论和作者的数据,我们将进行类似的分层,但权重不同,因为作者/观众的观点可能会有很大差异;
  5. 模型的数值参数不会通过深层网络传递,不会使结构复杂化;
  6. 在从文本中提取特征后,我们将组合所有可用的信息,并将其发送到一个小小的前馈神经网络的输入端。

下面可以看到网络结构和生成它的详细代码(是model.ipynb笔记本的一部分)。

神经网络架构。作者图片

可视化神经网络架构

下面你可以看到绘制网络结构的tensorflow.keras.utils.plot_model()函数的输出。

神经网络架构——plot_model()函数的输出。作者图片

要在项目中使用此功能,请执行以下操作:

  1. 确保您使用 TensorFlow 版或更高版本(您可以通过打印tf.__version__来实现),因为该功能仅在软件包的更高版本中可用。如果您使用的是 TensorFlow 的早期版本,请更新软件包(pip install --upgrade tensorflow)。
  2. 你需要安装 pydot 和 Graphviz 来执行这个功能。对于 pydot 运行pip install pydot,对于 Graphviz 遵循官方文档。

也可以用 Netron 。这种方法在处理大型网络时效果较差,并且在处理网络架构时不太方便,因为您需要保存网络并使用应用程序打开它,但它可以创建非常漂亮的可视化效果,并允许您详细浏览网络,甚至下载特定图层的权重。一定要试试!

神经网络架构——由 Netron 进行可视化。作者图片

结论,以及可以做得更好的地方

你已经看到了最上面的免责声明,你已经知道我失败了。好吧,虽然我希望并且努力到最后,但是是时候接受它了。我得到的最好的质量是关于一个 6k 验证错误(MAE) 。没有想象中的那么糟糕,但是一点也不好。

什么可以做得更好?是的,事实上,几乎一切都是因为任务仍然没有解决。但是让我分享一些关于如何提高模型质量的想法:

  1. 使用更强大的单词嵌入——例如,用 BERT 代替 GloVe
  2. 使用注意机制代替双向 LSTM;
  3. 使用更强大的模型——更多的LSTM_and_skip_connection模块、类似变压器的架构等。

看起来有点奇怪,不是吗?在这里,我列出了可以解决问题的特定猜测,那么我为什么不这样做呢?为什么我没有尝试所有这些并告诉你结果呢?

实际上我试过了。最初,我使用 50-dim 手套嵌入,但也尝试了 fastText 和 ELMo。我尝试了更多的区块,更多的层次,更多的训练时期。我删除了功能(尝试了没有author_text或没有comments),改变了数据预处理和准备,没有任何帮助。但同时,我确信架构和嵌入工作得很好(例如,它能够使用这个情感分析数据集成功地解决分类问题)。

机器学习中有一个流行的原则(另一个变体垃圾输入——垃圾输出):

如果领域专家不能从数据中得出结论,机器学习模型很可能也不能做到这一点。

当然,我不是专家,但评论数据是如此嘈杂和不确定,以至于我无法近似预测不喜欢的数量,甚至无法说用户是否喜欢某个特定的视频!

数据不具有代表性——这是主要问题。这并不是因为我收集不正确,但这可能也是我的错。也许,使用一两条最受欢迎的评论是比 20 条更好的选择,这样可以提高质量。但无论如何,要训练一个好的网络,你需要花费大量的资源主要在数据收集上。而且很遗憾,我没有这样的资源。

负面体验也是一种体验。此外,这是一个比积极的经验更有用的经验。所以我才和你分享。这个项目教会了我很多。希望对你也有用,至少有一点。

参考

https://keras.io/examples/nlp/pretrained_word_embeddings/

感谢您的阅读!

  • 我希望这些材料对你有用。在 Medium 上关注我,获取更多类似的文章。
  • 如果您有任何问题或意见,我将很高兴得到任何反馈。在评论中问我,或者通过 LinkedIn 或 Twitter 联系我。
  • 为了支持我作为一名作家,并获得数以千计的其他媒体文章,使用我的推荐链接获得媒体会员资格(不收取额外费用)。

任何回归模型的保形预测区间

原文:https://towardsdatascience.com/prediction-intervals-for-any-regression-model-306930d5ad9a

Julia 中的共形预测——第三部分

不同覆盖率的适形预测区间。随着覆盖范围的扩大,预测区间的宽度也在扩大。图片作者。

这是使用[ConformalPrediction.jl](https://github.com/pat-alt/ConformalPrediction.jl)在 Julia 中介绍共形预测的系列文章的第三部分(现在是最后一部分)。第一篇帖子介绍了监督分类任务的保形预测:我们知道保形分类器产生集值预测,保证以一定的概率包含新样本的真实标签。在第二篇帖子中,我们将这些想法应用于一个更具实践性的例子:我们看到了使用[ConformalPrediction.jl](https://github.com/pat-alt/ConformalPrediction.jl)来整合深度学习图像分类器是多么容易。

在这篇文章中,我们将着眼于回归问题,即涉及连续结果变量的监督学习任务。回归任务和分类任务一样无处不在。例如,我们可能对使用机器学习模型来预测房价或欧元的通货膨胀率或下一个大型语言模型的参数大小感兴趣。事实上,与分类相比,许多读者可能更熟悉回归模型,在这种情况下,您也可能更容易在此背景下理解保形预测(CP)。

📖背景

在我们开始之前,让我们简单回顾一下什么是 CP。别担心,我们不会深究方法论。但是先给你一个高层次的描述:

共形预测(又名共形推理)是一种用户友好的范式,用于为这种模型的预测创建统计上严格的不确定性集/区间。至关重要的是,这些集合在无分布的意义上是有效的:即使没有分布假设或模型假设,它们也拥有明确的非渐近保证。

安热洛普洛斯和贝茨(2021 年)

直观地说,CP 的工作前提是通过重复采样或使用专用校准数据,将启发式的不确定性概念转化为严格的不确定性估计。

在接下来的内容中,我们将通过使用[MLJ.jl](https://alan-turing-institute.github.io/MLJ.jl/dev/)[ConformalPrediction.jl](https://github.com/pat-alt/ConformalPrediction.jl)完成一个标准的机器学习工作流来探索 CP 能做什么。这里不太关注 CP 是如何工作的,但是参考资料会给你提供额外的资源。

💡💡💡 互动版

这个帖子也可以作为一个完全互动的[Pluto.jl](https://github.com/fonsp/Pluto.jl)🎈笔记本:点击此处。根据我自己的经验,这可能需要一些时间来加载,当然足够长的时间让你自己喝一杯热饮料☕,并首先在这里阅读。但是我向你保证,等待是值得的!

📈数据

大多数机器学习工作流都是从数据开始的。为了便于说明,我们将使用合成数据。下面的帮助函数可以用来生成一些回归数据。

function get_data(;N=1000, xmax=3.0, noise=0.5, fun::Function=fun(X) = X * sin(X))
    # Inputs:
    d = Distributions.Uniform(-xmax, xmax)
    X = rand(d, N)
    X = MLJBase.table(reshape(X, :, 1))

    # Outputs:
    ε = randn(N) .* noise
    y = @.(fun(X.x1)) + ε
    y = vec(y)
    return X, y
end

图 1 展示了我们的观察(点)以及从输入到输出(线)的基本事实映射。我们将映射 f: 𝒳 ↦ 𝒴定义如下:

f(X) = X * cos(X)

图 1:一些合成回归数据。观察结果显示为点。从输入到输出的基本事实映射显示为虚线。图片作者。

🏋️模型训练使用[MLJ](https://alan-turing-institute.github.io/MLJ.jl/dev/)

[ConformalPrediction.jl](https://www.paltmeyer.com/blog/posts/conformal-regression/(https://github.com/pat-alt/ConformalPrediction.jl))[MLJ.jl](https://alan-turing-institute.github.io/MLJ.jl/dev/) (Blaom et al. 2020)接口:一个为 Julia 设计的全面的机器学习框架。MLJ.jl提供了一个庞大且不断增长的流行机器学习模型套件,可用于监督和非监督任务。保形预测是一种模型不可知的不确定性量化方法,因此它可以应用于任何常见的监督机器学习模型。

因此,MLJ.jl的接口看起来很自然:任何(受监督的)MLJ.jl模型现在都可以使用ConformalPrediction.jl来整合。通过利用现有的MLJ.jl功能完成训练、预测和模型评估等常见任务,这个包是轻量级的,可扩展的。现在让我们看看所有这些是如何工作的...

首先,让我们将数据分成训练集和测试集:

train, test = partition(eachindex(y), 0.4, 0.4, shuffle= true)

现在让我们为我们的回归任务定义一个模型:

Model = @load KNNRegressor pkg = NearestNeighborModels
model = Model()

💡💡💡 随你便!

觉得这个数据集太简单?想知道为什么我不使用 XGBoost 来完成这个任务吗?在本帖的互动版本中,你可以完全控制数据和模型。试试吧!

使用标准的MLJ.jl工作流程,现在让我们首先训练不整合模型。我们首先用数据包装我们的模型:

mach_raw = machine(model, X, y)

然后我们让机器适应训练数据:

MLJBase.fit!(mach_raw, rows=train, verbosity= 0)

下面的图 2 显示了测试数据集的结果点预测:

图 2:我们的机器学习模型的点预测。图片作者。

我们的模型做得怎么样?当然,它从来都不完全正确,因为预测是估计,因此是不确定的。让我们看看如何使用共形预测来表达不确定性。

🔥整合模型

我们可以用一行代码将我们的model变成一个一致化的模型:

conf_model = conformal_model(model)

默认情况下,当在<:Deterministic模型上调用时,conformal_model会创建一个归纳共形回归器(更多信息见下文)。这种行为可以通过使用可选的method键参数来改变。

为了训练我们的共形模型,我们可以再次依靠标准的MLJ.jl工作流程。我们首先用数据包装我们的模型:

mach = machine(conf_model, X, y)

然后我们让机器适应数据:

MLJBase.fit!(mach, rows=train, verbosity= 0)

现在让我们再次看看对我们的测试数据的预测。下图显示了我们的构象化模型的结果。来自保形回归的预测是范围值:对于每个新样本,模型返回一个区间(yₗ,yᵤ) ∈ 𝒴,该区间以用户指定的概率(1-α)覆盖测试样本,其中α是预期误差率。这就是所谓的边际覆盖保证,在假设训练和测试数据可交换的情况下,它被证明是成立的。

图 3:我们的共形机器学习模型的预测区间。图片作者。

直观上,较高的覆盖率导致较大的预测区间:由于较大的区间覆盖了较大的𝒴子空间,因此它更有可能覆盖真实值。

我不指望你相信我,边际覆盖性质真的成立。事实上,当我第一次知道这件事的时候,我自己都不敢相信。如果你喜欢数学证明,你可以在这个教程里找到一个,比如。如果你喜欢通过经验观察来说服自己,请阅读下面的内容…

🧐评估

为了从经验上验证边际覆盖率属性,我们可以查看我们的保形预测器的经验覆盖率(详见教程的第 3 节)。为此,我们的产品包提供了与MLJ.jl模型评估工作流程兼容的自定义性能指标emp_coverage。特别是,我们将使用emp_coverage作为我们的性能度量,在我们的共形模型上调用evaluate!。由此得出的经验覆盖率应该接近期望的覆盖率水平。

model_evaluation =
    evaluate!(mach, operation=MLJBase.predict, measure=emp_coverage, verbosity=0)
println("Empirical coverage: $(round(model_evaluation.measurement[1], digits=3))")
println("Coverage per fold: $(round.(model_evaluation.per_fold[1], digits=3))")
Empirical coverage: 0.902 
Coverage per fold: [0.94, 0.904, 0.874, 0.874, 0.898, 0.922]

✅ ✅ ✅ 伟大!我们得到的经验覆盖率略高于预期😁 …但是为什么不完全一样呢?

在大多数情况下,它会略高于预期,因为(1-α)是一个较低的界限。但是注意,它也可以比期望的稍低。这是因为覆盖属性是“边际”的,即概率是数据中随机性的平均值。在大多数情况下,足够大的校准集大小(n>1000)足以减少随机性。根据您以上的选择,校准设置可能非常小(设置为 500),这可能导致覆盖松弛(参见教程的第 3 节)。

引擎盖下发生了什么?

概括地说,归纳共形预测(也称为分裂共形预测)的工作原理如下:

  1. 将训练分成适当的训练集和单独的校准集
  2. 在适当的训练集上训练机器学习模型。
  3. 使用一些启发式的不确定性概念(例如,回归情况下的绝对误差),使用校准数据和拟合模型计算不合格分数。
  4. 对于给定的覆盖率,计算不符合项分数经验分布的相应分位数 q
  5. 对于给定的分位数和测试样本 X ,形成相应的保形预测集如下:C(X)={*y:*s(X,y ) ≤ q }

🔃概述

这是一次超级快速的[ConformalPrediction.jl](https://github.com/pat-alt/ConformalPrediction.jl)之旅。我们已经看到该软件包如何与[MLJ.jl](https://alan-turing-institute.github.io/MLJ.jl/dev/)自然集成,允许用户为任何受监督的机器学习模型生成严格的预测不确定性估计。

我们结束了吗?

很酷,对吧?使用单个 API 调用,我们能够为各种不同的回归模型生成严格的预测区间。我们是否已经一劳永逸地解决了预测不确定性量化问题?我们还需要担心其他事情吗?保形预测是一个非常有用的工具,但像许多其他事情一样,它不是我们所有问题的最终答案。其实还是看看能不能把 CP 发挥到极致吧。

从上面生成数据的 helper 函数带有一个可选参数xmax。通过增加该值,我们有效地扩展了输入的范围。让我们这样做,看看我们的共形模型如何处理这种新的域外数据。

图 4:我们应用于域外数据的整合机器学习模型的预测区间。图片作者。

呜哇呜哇🤕 …看起来我们有麻烦了:在图 4 中,预测区间没有很好地覆盖域外测试样本。这里发生了什么?

通过扩大输入的范围,我们违反了可交换性假设。当违反该假设时,边际覆盖属性不成立。但是不要绝望!有很多方法可以解决这个问题。

📚继续读

如果你想了解更多,请务必继续阅读文档。也有许多有用的资源来学习更多关于保形预测的知识,我在下面列出了其中的一些:

  • 由 Angelopoulos 和 Bates ( 2022 )对保形预测和无分布不确定性量化的温和介绍。
  • 由 Manokhin ( 2022 )完成的令人敬畏的保形预测知识库
  • MAPIE :用于保形预测的综合 Python 库。
  • 我之前的两篇博文。

尽情享受吧!

参考

安吉洛普洛斯,阿纳斯塔西奥斯 n,斯蒂芬贝茨。2021."保形预测和无分布不确定性量化的简明介绍."【https://arxiv.org/abs/2107.07511】T4。

布劳姆、安东尼·d .、弗朗茨·基拉里、蒂博特·利纳特、亚尼斯·斯米兰利德斯、迭戈·阿里纳斯和塞巴斯蒂安·沃尔默。2020." MLJ:用于可组合机器学习的 Julia 包."开源软件杂志 5 (55): 2704。https://doi.org/10.21105/joss.02704。

原载于 2022 年 12 月 12 日 https://www.paltmeyer.comhttps://www.paltmeyer.com/blog/posts/conformal-regression/

Python 中的预测区间

原文:https://towardsdatascience.com/prediction-intervals-in-python-64b992317b1a

图为舒巴姆·达吉在 Unsplash

数据科学基础

了解获得预测区间的三种方法

如果让你猜我过去一周看了多少部电影,你会觉得猜“2 到 6”还是“3”更有把握?我们可能会同意,猜测一个范围比猜测一个数字更有可能是正确的。同样,与单值预测相比,预测区间为我们提供了更可靠、更透明的估计。在本帖中,我们将学习在 Python 中获得预测区间的三种方法。

照片由法库里安设计在 Unsplash 上拍摄

📦 0.设置

我们将从加载必要的库和样本数据开始。我们将使用 Scikit-learn 关于糖尿病的内置数据集(该数据在 BSD 许可下可用)。如果您想了解更多关于数据集的信息,请使用print(diabetes[‘DESCR’])查看它的描述。

import numpy as np
np.set_printoptions(
    formatter={'float': lambda x: "{:.4f}".format(x)}
)
import pandas as pd
pd.options.display.float_format = "{:.4f}".format
from scipy.stats import t
import statsmodels.api as sm
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import GradientBoostingRegressorimport matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='darkgrid', context='talk')diabetes = load_diabetes(as_frame=True)
df = diabetes['data']
df['target'] = diabetes['target']
df.info()

让我们将数据分成训练集和测试集:

train, test = train_test_split(df, test_size=0.1, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(
    df.drop(columns='target'), df['target'], test_size=0.1, 
    random_state=42
)
x_train = X_train['bmi']
x_test = X_test['bmi']print(f"X_train shape: {X_train.shape}")
print(f"x_train shape: {x_train.shape}")
print(f"y_train shape: {y_train.shape}")
print("\n========== Training data ==========")
display(train[['target']].describe().T)print(f"X_test shape: {X_test.shape}")
print(f"x_test shape: {x_test.shape}")
print(f"y_test shape: {y_test.shape}")print("\n========== Test data ==========")
test[['target']].describe().T

目标范围从 25 到 350,平均值约为 150,中位数约为 130-140。

📍 1.预测数的变化范围

我们现在来看看获得预测区间的三种方法。

💡 1.1.使用标准误差

让我们使用bmi建立一个简单的线性回归来预测target

model = LinearRegression()
model.fit(x_train.values.reshape(-1, 1), y_train)
print(f"Intercept: {model.intercept_:.2f}")
print(f"Slope: {model.coef_[0]:.2f}")
print(model.predict(x_test.values.reshape(-1, 1))[:5])

我们可以看到预测,我们最好的猜测。使用下面的公式,我们可以计算标准误差并获得预测区间:

绿框中的变量是针对我们正在进行预测的特定观察的,而其余的是根据训练数据计算的。

该公式可以翻译成如下代码。我们使用自定义对象,因为它比函数更灵活:

class CustomLinearRegression:
    def __init__(self):
        pass

    def fit(self, x, y):
        # Calculate stats
        self.n = len(x)
        self.x_mean = np.mean(x)
        self.y_mean = np.mean(y)
        self.x_gap = x-self.x_mean
        self.y_gap = y-self.y_mean
        self.ss = np.square(self.x_gap).sum()

        # Find coefficients
        self.slope = np.dot(self.x_gap, self.y_gap)/self.ss
        self.intercept = self.y_mean-self.slope*self.x_mean

        # Find training error
        y_pred = self.intercept+self.slope*x
        self.se_regression = np.sqrt(
            np.square(y-y_pred).sum()/(self.n-2)
        )

    def predict(self, x):
        y_pred = self.intercept+self.slope*x
        return y_pred

    def predict_interval(self, x, alpha=0.1):
        t_stat = t.ppf(1-alpha/2, df=self.n-2)

        # Calculate interval upper and lower boundaries
        df = pd.DataFrame({'x': x})
        for i, value in df['x'].iteritems():
            se = self.se_regression * np.sqrt(
                1+1/self.n+np.square(value-self.x_mean)/self.ss
            )
            df.loc[i, 'y_pred'] = self.intercept+self.slope*value
            df.loc[i, 'lower'] = df.loc[i, 'y_pred']-t_stat*se
            df.loc[i, 'upper'] = df.loc[i, 'y_pred']+t_stat*se
        return df

custom_model = CustomLinearRegression()     
custom_model.fit(x_train, y_train)
print(f"Intercept: {custom_model.intercept:.2f}")
print(f"Slope: {custom_model.slope:.2f}")
custom_pred = custom_model.predict_interval(x_test)
custom_pred.head()

让我们来理解这个输出。在线性回归中,预测代表条件平均目标值。因此y_pred,我们的预测栏,告诉我们给定特征的估计平均目标。预测区间告诉我们对于一个给定的记录目标可以取值的范围。我们可以从lowerupper列中看到预测区间的上下边界。这是一个 90%的预测区间,因为我们选择了alpha=0.1。我们将在这篇文章的剩余部分使用相同的 alpha 值。

如果你很好奇,这里有一些解释预测区间的方法:

  • 记录 287 的实际目标值有 90%的可能性在 42.8550 和 249.4799 之间。
  • 根据记录 287 的bmi值,我们有 90%的把握认为其实际目标值将介于 42.8550 和 249.4799 之间。
  • 大约 90%的预测区间将包含实际值。

让我们检查一下测试数据中的目标值在预测区间内的百分比:

custom_correct = np.mean(
    (custom_pred['lower']<y_test) & (y_test<custom_pred['upper'])
)
print(f"{custom_correct:.2%} of the prediction intervals contain true target.")

这大概是 90%。虽然手动计算有助于我们理解幕后发生的事情,但更实际的是,我们可以使用库并简化我们的工作。下面是我们如何使用statsmodels包来获得相同的预测间隔:

sm_model = sm.OLS(y_train, sm.add_constant(x_train)).fit()
print(f"Intercept: {sm_model.params[0]:.2f}")
print(f"Slope: {sm_model.params[1]:.2f}")sm_pred = sm_model.get_prediction(sm.add_constant(x_test))\
                  .summary_frame(alpha=0.1)
sm_pred.head()

这个输出提供了一些额外的输出,让我们来理解其中的关键:
◼️ mean:预测,与前面的y_pred相同。
◼️ mean_ci_lower & mean_ci_upper:置信区间边界
◼️ obs_ci_lower & obs_ci_upper:预测区间边界,同前面的lowerupper

我们可以检查预测和预测间隔是否与手动计算的相匹配:

np.mean(np.isclose(
    custom_pred.drop(columns='x').values, 
    sm_pred[['mean', 'obs_ci_lower', 'obs_ci_upper']]), 
        axis=0)

可爱,很配!

现在,你可能想知道置信区间和预测区间的区别。虽然这些术语相互关联,听起来有些相似,但它们指的是两个不同的区间,不应互换使用:
◼️置信区间用于均值预测。与预测区间不同,置信区间并没有告诉我们一个观察可以采用的目标值的范围。相反,它告诉我们目标平均值的范围。下面是一个解释示例:有 90%的可能性,特征值与记录 287 相同的记录的平均目标值将落在 140.9452 和 151.3897 之间。
◼️虽然两个区间都以预测为中心,但预测区间的标准误差大于置信区间的标准误差。因此,预测区间比置信区间宽。

我们通过简单的线性回归来了解这个概念,但是在实践中更常见的是有多个特征。是时候扩展我们的示例以使用全部功能了:

ols = sm.OLS(y_train, sm.add_constant(X_train)).fit()
test[['ols_lower', 'ols_upper']] = (ols
    .get_prediction(sm.add_constant(X_test))
    .summary_frame(alpha=0.1)[['obs_ci_lower', 'obs_ci_upper']])
columns = ['target', 'ols_lower', 'ols_upper']
test[columns].head()

流程与使用单个特征完全相同。如果你很想知道在多重特征存在的情况下公式是如何变化的,请查看本指南。让我们评估一下我们的时间间隔:

ols_correct = np.mean(
    test['target'].between(test['ols_lower'], test['ols_upper'])
)
print(f"{ols_correct:.2%} of the prediction intervals contain true target.")

1.2.来自分位数回归

使用分位数回归,我们可以预测目标的条件分位数,而不是条件均值目标。为了获得 90%的预测区间,我们将构建两个分位数回归,一个预测第 5 个百分位数,另一个预测第 95 个百分位数。

alpha = 0.1
quant_lower = sm.QuantReg(
    y_train, sm.add_constant(X_train)
).fit(q=alpha/2)
test['quant_lower'] = quant_lower.predict(
    sm.add_constant(X_test)
)quant_upper = sm.QuantReg(
    y_train, sm.add_constant(X_train)
).fit(q=1-alpha/2)
test['quant_upper'] = quant_upper.predict(
    sm.add_constant(X_test)
)
columns.extend(['quant_lower', 'quant_upper'])
test[columns].head()

这里,quant_lower模型预测不足,而quant_upper模型预测过度。现在让我们检查在新间隔内的预测的百分比:

quant_correct = np.mean(
    test['target'].between(test['quant_lower'], test['quant_upper'])
)
print(f"{quant_correct:.2%} of the prediction intervals contain true target.")

略低于 90%,覆盖率略低于以前。

1.3.从有分位数损失的 GBM

最后一种方法与前一种方法非常相似。我们将使用分位数损失的GradientBoostingRegressor,通过两个模型获得 90%的预测区间:

gbm_lower = GradientBoostingRegressor(
    loss="quantile", alpha=alpha/2, random_state=0
)
gbm_upper = GradientBoostingRegressor(
    loss="quantile", alpha=1-alpha/2, random_state=0
)gbm_lower.fit(X_train, y_train)
gbm_upper.fit(X_train, y_train)test['gbm_lower'] = gbm_lower.predict(X_test)
test['gbm_upper'] = gbm_upper.predict(X_test)
columns.extend(['gbm_lower', 'gbm_upper'])
test[columns].head()

评估时间间隔覆盖范围的时间:

gbm_correct = np.mean(
    test['target'].between(test['gbm_lower'], test['gbm_upper'])
)
print(f"{gbm_correct:.2%} of the prediction intervals contain true target.")

与以前的区间相比,这一数值较低。查看上面的示例记录,与前两种方法相比,间隔看起来稍微窄一些。对于我们在这篇文章中学到的任何方法,我们可以通过减少 alpha 来增加覆盖率。然而,这意味着间隔可能变得太宽,以至于它们可能不太能提供信息或对决策没有帮助。因此,在使用预测区间时,我们必须找到正确的平衡点。

现在让我们直观地比较所有三个区间:

test = test.sort_values('target').reset_index()plt.figure(figsize=(10,6))
sns.scatterplot(data=test, x=test.index, y='target',
                color='grey')
sns.lineplot(data=test, x=test.index, y='ols_lower', 
             color='hotpink')
sns.lineplot(data=test, x=test.index, y='ols_upper', 
             color='hotpink', label='Linear Regression')sns.lineplot(data=test, x=test.index, y='quant_lower', 
             color='blue')
sns.lineplot(data=test, x=test.index, y='quant_upper', 
             color='blue', label='Quantile Regression')sns.lineplot(data=test, x=test.index, y='gbm_lower', 
             color='green')
sns.lineplot(data=test, x=test.index, y='gbm_upper', 
             color='green', label='Gradient Boosting Machine')
plt.xticks([]);

越接近最小值和最大值的值越有可能超出预测区间。让我们进一步研究这些错误。我们将查看具有最高目标的 5 项记录:

test[columns].nlargest(5, 'target')

现在,让我们看看另一端:

test[columns].nsmallest(5, 'target')

线性回归预测区间的下限有时是负值。这是需要注意的事情。如果我们知道目标总是正的,我们可以使用包装函数用最低可能的值覆盖这些负边界。

瞧,这就是计算预测区间的三种方法!希望您能在下一个回归用例中使用这些方法。

塞巴斯蒂安·斯文森在 Unsplash 上的照片

您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果你使用 我的推荐链接成为会员,你的一部分会费会直接去支持我。

谢谢你看我的帖子。如果你感兴趣,这里有我的一些帖子的链接:

◼️️ 管道、ColumnTransformer 和 FeatureUnion 解释
◼️️ FeatureUnion、ColumnTransformer &管道用于预处理文本数据
◼️ 用这些提示丰富您的 Jupyter 笔记本
◼️ 用这些提示组织您的 Jupyter 笔记本
◼️ 解释 Scikit-用 SHAP 学习模型
◼️️ 在 scikit 中选择特性

再见🏃💨

2022 年对于几何和图形 ML 来说意味着什么?

原文:https://towardsdatascience.com/predictions-and-hopes-for-geometric-graph-ml-in-2022-aa3b8b79f5cc

2021 年回顾与 2022 年预测

2022 年对于几何和图形 ML 来说意味着什么?

去年,我征求了 Graph ML 领先研究人员的意见,以预测该领域的未来发展。今年,我们与 Petar Velič ković合作,采访了一批杰出和多产的专家,试图总结过去一年的亮点,并预测 2022 年的情况。

图片:Shutterstock

本文作者是 Petar Velič ković。另请参阅我的 去年的预测 、迈克尔·高尔金的精彩帖子《图 ML 中的 时事 、更深入的探究 子图 GNNs 、技法启发PDEs 微分几何和

在领先领域专家的帮助下,总结对 2021 年的印象并预测未来一年是一项值得进行的有益尝试,因为它提供了一系列不同的见解和观点供学习。用我们一位密切关注整个机器学习领域的顶级新兴趋势的受访者的话说:

“随着几何和基于图形的最大似然方法从利基市场发展成为当今人工智能研究的最热门领域之一,想象未来 12 个月将会发生什么令人兴奋。”——内森·贝奈希(Nathan Benaich),航空街资本的普通合伙人,也是《人工智能现状报告的合著者

回想起来,这一努力并不像我们最初认为的那样容易,因为该领域在过去一年中有了巨大的发展——这可以从这篇文章的长度中看出来,这是我们迄今为止写得最长的一篇。

快速带回家的信息(如果你懒得看剩下的)

  1. 几何在 ML **中变得日益重要。**微分几何和同源领域带来了新的想法,包括新的等变 GNN 架构,该架构利用了对称性和曲率的图形类似物,以及理解和利用深度学习模型中的不确定性。
  2. 消息传递仍然是 GNNs **中的主导范式。**2020 年,社区接受了消息传递 GNNs 的局限性,并超越这一范式寻找更具表现力的架构。2021 年,很明显消息传递仍然有几个王牌,因为几个作品显示了将 GNNs 应用于子图来获得更好的表达能力的可能性。
  3. 微分方程催生新 GNN 架构 **。**从 NeuralODEs 开始的趋势扩展到了图形。一些工作显示了如何将 GNN 模型公式化为连续微分方程的离散化。在短期内,这将导致新的架构,避免 gnn 的常见困境,如过度平滑和过度抑制。从长远来看,我们可能会更好地理解 gnn 是如何工作的,以及如何使它们更具表现力和可解释性。
  4. 旧的观念从信号处理、神经科学和物理学中获得新的生命 。许多人认为图形信号处理重新点燃了最近对图形 ML 的兴趣,并提供了第一套工具,如广义傅立叶变换和图形卷积。经典信号处理和物理学所依赖的其他基本技术,如表示理论,已经在 2021 年取得了一些重要进展,并且仍有许多东西有待开发。
  5. 建模复杂系统需要超越图形 。2021 年诺贝尔物理学奖授予乔治·帕里西,以表彰他对复杂系统的研究。虽然在基本的抽象层次上,这样的系统通常可以被描述为图形,但是人们必须考虑更复杂的结构,例如非成对关系和动态行为。2021 年的多项工作致力于动态关系系统,并展示了如何将 GNNs 扩展到更高阶的结构,如传统上在代数拓扑领域处理的单元和单纯复形。我们很可能会在 ML 中看到来自这个领域的其他更奇特的对象。
  6. 推理、公理化、泛化在 Graph ML **中仍然是大的开放题。**在过去的一年中,我们看到了受算法推理启发的 GNN 架构的持续进步,以及针对图结构任务的更强大的非分布概括。我们现在有知识图推理机,它们明确地与广义的贝尔曼-福特算法一致,还有图分类器,它们利用分布变化的明确因果模型。可以说,这样的方向为更健壮和更通用的 gnn 揭示了未来方向的金矿;很有可能在 2022 年对其中的许多进行彻底的探索。
  7. 图在强化学习中越来越受欢迎,但大概还有一段路要走 **。**也许毫不奇怪,强化学习充满了图形和对称:通常要么在 RL 代理的结构中,要么在环境的表示中。2021 年出现了几个试图利用这种结构的研究方向,取得了不同程度的成功。我们现在对如何利用 RL 中的这些对称性有了更好的理解,包括在多代理系统中;然而,将代理建模为图似乎并不要求严格使用图结构。尽管有这种复杂的感觉,我们相信 2022 年是图形和几何驱动的 RL 充满希望的一年。
  8. AlphaFold 2 是几何 ML 的一次胜利,也是结构生物学的一次范式转变 **。**预测蛋白质三维折叠结构的可能性是由诺贝尔化学奖获得者克里斯蒂安·安芬森在 20 世纪 70 年代提出的。这是一个非常困难的计算任务,是结构生物学的圣杯。2021 年,DeepMind 的 AlphaFold 2 打破了上一版本的记录,实现了令领域专家信服的准确性,并刺激了广泛的采用。其核心,AlphaFold 2 是一个基于等变注意力的几何架构。
  9. 药物发现和设计得益于 GNNs 及其与变形金刚 **的合流。**ML 社区中很少有人不知道 GNNs 的起源可以追溯到 20 世纪 90 年代的计算化学工作。因此,毫不奇怪,分子图的分析是 GNNs 最受欢迎的应用之一。2021 年,这一领域取得了持续的重大进展,出现了数十种新架构和几项超越基准的成果。获胜者之一是将 Transformers 改编为 graphs,这有望模仿 NLP 中这些架构成功的关键:能够跨任务概括的大型预训练模型。
  10. AI-首次药物发现越来越多地使用几何和图 ML 。AlphaFold 2 和分子 GNNs 的成功使人工智能设计的新药物的梦想更近了一步。Alphabet 的新公司同构实验室表明了行业对这项技术的押注。然而,为了实现这些希望,模拟分子间的相互作用是必须跨越的重要前沿。
  11. Quantum ML 得益于基于图的方法 **。**对于该领域的大多数专家来说,量子 ML 仍然是一个奇特的利基,但随着量子计算硬件的出现,它正在迅速成为现实。Alphabet X 最近的工作显示了量子 ML 架构中图形结构归纳偏差的优势,结合了这些显然不相关的领域。从长远来看,几何可能会扮演更重要的角色,因为量子物理系统通常拥有丰富而深奥的群对称,可以用于量子架构设计。

2021 年,基于几何和图形的 ML 方法在高规格应用中大放异彩。图片:自然/科学。

几何学在 ML 中变得越来越重要

如果我们必须选择一个在 2021 年持续渗透到图形表示学习的几乎每个领域的词,毫无疑问几何将是主要候选词[1]。我们去年写了关于这个的文章,我们的受访者似乎完全同意——超过一半的人以这样或那样的方式喊出了这个关键词。

“在过去的一年里,我们看到了许多经典几何思想在 Graph ML 中以新的方式使用。” —梅勒妮·韦伯,牛津大学数学研究所胡克研究员

Melanie 补充说:“值得注意的例子包括利用对称性更有效地学习模型,应用最佳运输的思想,或者在表示学习中使用微分几何的曲率概念。

最近,人们对理解关系数据的几何以及利用这些见解来学习好的(欧几里德或非欧几里德)表示的兴趣激增[1]。这就产生了许多编码特定几何图形的 GNN 体系结构。值得注意的例子是双曲 GNN 模型[2],该模型于 2019 年底首次推出,作为学习分层数据有效表示的工具。在过去的一年里,我们看到了新模型和架构的激增,这些模型和架构能够更有效地学习双曲线表示,或者能够捕捉更复杂的几何特征[3–4]。另一项工作是利用等变和对称形式的几何信息[5]。"

今年,在图形神经网络的背景下,我们看到了几何技术的激增,例如等变信息传递,这证明了它在从小分子性质预测到蛋白质折叠的生物化学应用中至关重要。图片来自 Satorras 等人[5]。

elanie 在微分几何上加倍努力,表明了它在 2022 年的许多潜在应用:“离散微分几何,研究像图或单纯复形这样的离散结构的几何,已经被用来深入了解 GNNs。曲率的离散概念是描述离散结构的局部和整体几何性质的重要工具。最近在[6]中提出了曲率在图 ML 中的一个值得注意的应用,其中在图重新布线的背景下研究了离散 Ricci 曲率,提出了一种减轻 GNNs 中过度挤压效应的新方法。在未来,离散曲率很可能会与图 ML 中的其他结构和拓扑问题联系起来。

我预计这些主题将在 2022 年继续影响该领域,并进入 Graph ML 的更多应用领域。这可以推动计算方面的进步,目的是减轻与使用传统上根据欧几里德数据设计的工具实现非欧几里德算法相关的计算挑战。此外,离散曲率等几何工具的计算成本可能很高,因此很难将其集成到大规模应用中。计算机技术的进步或专业图书馆的发展可能会让从业者更容易理解这些几何概念。"

我们还听到了近年来 GNNs 通过这种几何透镜获得的许多新超能力的简要介绍:

“图形神经网络设计者越来越认识到图形丰富的对称结构。” —皮姆·德·汉,阿姆斯特丹大学博士生

GNNs 传统上执行置换不变消息传递,后来使用群和表示理论来构建节点置换群的表示之间的等变映射[7]。最近,与流形的局部对称性(称为“规范对称性”[8])非常相似,我们开始研究由同构子图产生的图中的局部对称性。我们发现这些形成的不是一个组,而是一类对称[9],将这些整合到神经网络架构中可以提高图形 ML 任务的性能,如分子预测[10]”,Pim 概述道。

Graph ML 的研究人员开发了更丰富的图的对称结构。图片来自 de Haan 等人[9]。

受这些重要发现的启发,皮姆提出了他对 2022 年的预测:“在新的一年里,我希望看到范畴理论作为神经网络的设计语言得到广泛传播。这将给我们一种正式的语言来讨论和利用比以前更复杂的对称性。特别是,我很高兴看到它被用来处理图的局部和近似对称性,结合点云的几何和组合结构,并帮助我们研究因果图中的对称性。”

到现在,应该很清楚,如果 gnn 接受大多数图起源于或者可以被解释为一些连续流形的离散化的事实,它们将获得很多。我们就此征求了一位从微分几何学家转向机器学习者的专家意见——他是这么说的:

虽然图是不可微的,但是许多已经成功应用于流形分析的想法现在正在 GNN 领域浮出水面 Twitter 的 ML 研究员弗朗西斯科·迪·乔瓦尼

Francesco 叙述并预测了该领域的一些关键工作:“我对 PDE 方法特别感兴趣,这些方法最初是从表面研究中借用来处理图像的[11–12])。后者还探索了图形重新布线的想法——这个术语指的是底层邻接关系的修改——这属于几何流方法的更大范围,其中给定的对象被适当地修改以帮助分类。这一原则也指导了我们最近的工作 [6],其中我们利用了基于边的曲率的新概念来研究 GNNs 中的过度挤压问题(首先由 Uri Alon [13]观察到),并提出了一种旨在消除导致此类问题的瓶颈的图重布线方法。几何在对称性保持和破坏形式中的作用现在也被认为是将 GNNs 应用于分子的一个关键因素,例如在[14]中。

我相信我们只是朝着这个新方向迈出了第一步。如上所述的图重连技术将潜在地在解决消息传递的一些主要限制方面发挥作用,这表现在嗜异性数据集上的性能和处理长距离依赖性。我也希望我们将很快在图上的卷积和流形上的卷积之间架起一座概念上的桥梁,这将导致下一代的 GNNs。最后,我很高兴看到几何变分方法进一步揭示了 GNNs 的内在动力学,并有望产生更具原则性的方法来设计新的 GNNs 架构并比较现有架构。"

微分几何中的概念,如 Ricci 曲率和几何流,被用于图 ML 上下文中,以改善 GNNs 中的信息流。图片来自 J. Topping 等人[6]。

在机器学习的更广泛背景下,微分几何不仅仅是改进 gnn:

“微分几何和它的数学兄弟经常被使用,希望为那些精确的公式产生非线性几何的问题提供有根据的解决方案。” — Aasa Feragen,哥本哈根大学助理教授

Aasa 特别概述了她对微分几何在估计不确定性方面的热情:“2021 年的一个教训是,微分几何在理解和利用深度学习模型中的不确定性方面发挥着重要作用。一个很好的例子是使用模型不确定性来产生数据的几何表示,揭示在标准欧几里得表示中仍然模糊的生物信息[15]。另一个例子是利用由局部方向数据编码的黎曼几何,允许对估计的大脑结构连通性的不确定性进行自然量化[16]。”

最终,Aasa 希望这一领域在 2022 年增长:“我原本预计 2021 年将更广泛地解决几何机器学习中的不确定性量化。几何模型通常应用于经过大量预处理以揭示其几何结构的数据。换句话说,我们的数据往往是从原始数据中估计出来的,这就带来了误差和不确定性。我认为是时候开始评估这些原始数据不确定性对我们视为数据的对象的影响,以及这种不确定性应该如何传播到我们的模型中。这是我对 2022 年的主要希望,我们转而将测量误差恰当地纳入我们对非欧几里德数据的分析。如果我们——作为科学家和我们自己科学学科的代表——努力打破统计和深度学习之间的社区鸿沟,这可能会获得更多的关注。”

消息传递仍然是 GNNs 中的主导范式

由于与 Weisfeiler-Lehman 同构测试等价,Graph ML 领域开始接受“消息传递范式的基本限制”(引用 Will Hamilton 的话),Michael 在 2021 年的帖子中预测,“进步将需要打破 2020 年及之前主导该领域的消息传递模式。”这一预测只是部分具体化了:虽然 2021 年带来了更多关于更具表现力的 GNN 建筑的作品,但它们中的大多数仍然在很大程度上属于消息传递范式的职权范围。

一个这样的例子是最近一系列关于使用子图来提高 GNNs 表达能力的并行工作。Haggai Maron 也在去年的帖子中提供了他的意见,他解释道:

“子图 GNNs”背后的思想是将图表示为其子结构的集合,这一主题可以在 Kelly 和 Ulam 在 20 世纪 60 年代关于图重构猜想的著名著作中找到[17]。如今,同样的想法被用于构建表达性 GNNs,这反过来又提出了新的、更精确的重构猜想。”

“我期望子图 GNNs 和相应的重构猜想在未来几年是一个卓有成效的研究方向。” — Haggai Maron,Nvidia 研究科学家

微分方程产生了新的 GNN 架构

2021 年展示了从离散扩散型偏微分方程导出图形神经网络的几种方法。图片由 James Rowbottom 根据[12]绘制。

2021 年的另一个趋势是通过物理系统的动力学来重新定义对图形的学习,用微分方程来表示。与常微分方程作为理解残差神经网络的强大工具的方式相同(“神经微分方程”在 NeurIPS 2019 上被评为最佳论文),偏微分方程可以在图上模拟信息传播,允许恢复许多标准 GNN 架构作为求解此类偏微分方程的迭代数值方案【18】。在这个公式中,图形被认为仅仅是一个连续物体的离散化,用我们记者的话说:

“这为 GNNs 如何用于提取下游 ML 任务的有意义信息带来了一种替代和令人耳目一新的观点,并将焦点从支持信息的领域转移到使用图形作为信号计算的支持”——EPFL 大学教授 Pierre Vandergheynst

Pierre 认为这是未来一年的一个更广泛的方向:“2022 年,我预计这将成为一种新的趋势:使用图形作为一种机制来执行局部一致性计算,在数据集上交换关于所述计算的信息,并使用它作为一种机制来关注数据的整体属性。这将是一个令人兴奋的无监督或零射击学习的途径。”

来自信号处理、神经科学和物理学的旧思想获得了新生

许多现代 gnn 都源于最初在信号处理领域开发的方法。Pierre Vandergheynst,图形信号处理(GSP)的创始人之一[19],从这个角度提供了一个关于图形 ML 方法进展的有趣观点:

“图形信号处理从两个方向开始,以丰富数字信号处理。第一个是将支持信息的域一般化,从低维欧几里得空间的传统设置中移出,并允许在更复杂但结构化的对象上定义信号,这些对象可以用图形(网络、网格表面等)来表示。).GSP 的第二个动机是远离结构化领域,直接在一些数据集上工作,使用一个图(某种最近邻)来表示样本之间的相似性。潜在的想法是标签字段继承一些规则,这些规则可以在图形上定义,并通过适当的转换来捕获。从这个意义上说,该图成为整个数据集的局部计算的支持。GNNs 中一些有趣的想法植根于这些早期的动机,有趣的是,2021 产生了延续这一趋势的亮点。”

“经典线性变换(例如傅立叶或小波)的吸引力之一是它们提供了一个带有数学属性的通用“潜在”空间:例如平滑信号具有低频傅立叶系数,而分段平滑信号具有稀疏和局部化的小波系数”**—EPFL 大学教授 Pierre Vandergheynst

Pierre 进一步回忆道:“过去有一整套通过构造线性变换来揭示信号特性的传统。物理学家在基于群作用为不同对称性设计等变变换方面尤其领先,例如仿射群(或高维相似群)的连续小波变换、 Weyl-Heisenberg 群的线性时频分析等等。数学物理中相干态(有些神秘)领域的文献提供了一个通用的方法:通过用群表示参数化一个函数系统来构造一个线性变换。添加一个非线性和可学习的参数函数,你会接近 2021 年的一些最好的论文,赋予 gnn 对称性,使它们对物理或化学问题非常有用[20]。”

分组表示是一种在信号处理和物理学中传统使用的工具,允许推导出可应用于流形的无坐标深度学习架构。图片来自魏勒等人。

Pierre 预计,构建结构化潜在空间的趋势将在 2022 年继续,“部分受应用需求的驱动,但也受交易适应性和可解释性的愿望的驱动:结构化变换域不是自适应的,但非常可读,GNNs 可能有助于实现良好的平衡。”

传统上与信号处理密切相关的一个科学领域是神经科学;事实上,我们对动物如何感知周围世界的大部分了解都是通过分析大脑传递的电信号来实现的。我们采访了一位在机器学习和神经科学交叉领域的杰出研究人员,她欣然陈述了图表在这一特定领域的重要性:

“我的背景是计算神经科学,我最初接触图形是因为我想表现人类和动物如何学习结构。”—金·斯坦菲尔德,DeepMind 的研究科学家

“图表是方便的数学对象,用于捕捉人类和动物如何表示通过孤立的经验片段获得的相关概念,并将其缝合成全球一致的综合知识体。”,金解释道。

Kim 分享了其他人对几何的兴奋,同时也强调了图形信号处理和相关神经科学研究之间的联系:

“我真正感到兴奋的一个研究领域是将神经网络中的局部操作与底层或内在几何图形的表示相结合的工作,这一领域今年取得了很大进展。突出的例子包括 GNNs [9,5]中今年关于等方差的一些新工作,这些工作允许 GNN 模型利用图形结构外部的几何和对称性。

这方面的另一项工作使用图形拉普拉斯特征向量作为图形转换器的位置编码,允许 GNN 访问关于固有的低维几何形状的信息,而不受其过度约束【21】。除了有深厚的数学渊源和长期以来一直是 GNN 文学的一部分[22–23]之外,他们还与几何、关系推理的神经科学研究有联系[24–26]。"

除此之外,金对 2021 年基因神经网络的各种应用公开表示兴奋,无论是在神经科学领域还是更广泛的领域:

“我对 GNNs 越来越多的不同应用感到非常兴奋,特别是对非常大规模的真实世界数据。示例包括使用 GNNs 来预测交通结果[27],模拟复杂的物理动态[28],以及解决特别大规模的图形问题[29]。我也很高兴看到人们对将 GNNs 应用于神经数据分析越来越感兴趣[30–32]。这些问题具有现实世界的影响,它们挑战我们的模型有效地扩展和概括,同时仍然捕捉真正复杂的动态——这正是 GNNs 优化的结构和表达能力的平衡。”

复杂系统的建模需要超越图表

“2021 年诺贝尔物理学奖的主题是复杂系统的研究。在最基本的层面上,复杂系统由实体及其交互组成。复杂系统通常被表示为复杂网络,这推动了图形 ML 的工作。 — Tina Eliassi-Rad,东北大学教授

“随着 graph ML 的成熟,我们需要仔细检查可以以不同风格(子集、时间和空间)表现出来的系统依赖性,共同的数学表示(图、单纯复形和超图),它们的潜在假设,以及它们编码的依赖性[33]。数学表示法的选择很重要,因为当我们将数据从一种表示法转换为另一种表示法时,信息可能会丢失(或被估算)。

没有完美的方法来表示一个复杂的系统,并且在检查来自一个系统的数据集时做出的建模决策不一定可以转移到另一个系统,或者甚至转移到来自同一系统的另一个数据集。然而,当我们考虑与我们选择的数学表示相关的系统依赖性时,激动人心的研究机会为图 ML 打开了大门[33–36]。"

ierre Vandergheynst 强调,图并不总是提供复杂系统的适当模型,人们可能必须超越图:“一些漂亮的论文带来了新的结构化信息领域,这些领域可以通过图的概括来捕捉。一个突出的例子是使用单纯复形和代数拓扑的其他思想来构建新的神经网络,该网络在实验上和可证明地改进了 GNNs [37]。这种趋势肯定会在 2022 年继续下去,因为我们学会了挖掘代数拓扑或微分几何提供的大量结构化数学对象。”

将图提升到细胞或单纯复合体允许更复杂的拓扑信息传递,产生超越 Weisfeiler-Lehman 测试表达能力的 GNN 架构。图片来自博德纳尔等人。

C 里斯蒂安·博德纳尔对代数拓扑和图 ML 之间的联系同样充满热情:“在过去的一年里,单纯和细胞复合体上的卷积[39–41]和消息传递[37–38,42–43]模型解决了 gnn 的许多限制,如检测某些子结构,捕捉长程和高阶相互作用,处理高阶特征和逃离 WL 层级。在实践中,他们在分子问题[38]和轨迹预测和分类[37,41]任务中获得最先进的经验结果。2022 年,我预计这些方法将扩展到令人兴奋的新应用,如计算代数拓扑中的难题[44]、链接预测[45]和计算机图形学[46]。”

像 Pierre 一样,他认为代数拓扑仍然可以为机器学习提供更多的东西:

“我们可能会看到更多奇特的数学对象被采用,这些对象迄今为止相对来说还没有被探索过,比如细胞束[47–48]和颤动[49]。同时,我相信这些拓扑方法将为分析和理解 GNNs 提供一套新颖的数学工具。”–克里斯蒂安·博德纳尔,剑桥大学博士生

复杂网络系统的另一个非常重要的例子是时空图,它的结构随着时间而演变。我们征求了该领域一位多产研究人员的意见,了解她对 2022 年的想法和希望:

“我对 Graph ML 在学习时空动力学中的作用感到非常兴奋。”— Rose Yu,加州大学圣地亚哥分校助理教授

Rose 阐述道:“预测[50-Wu 等,2021]、交通预测[51-尚等,2021]和轨迹建模[52-Walters 等,2021]中的应用需要捕捉高度结构化的时间序列数据的复杂动态。图 ML 提供了捕捉空间依赖性、时间序列之间的相互作用以及动态中的相关性的能力。

2022 年,我会很高兴看到时间序列和动力系统的思想与 Graph ML 的融合。这些想法有望产生新的模型设计、训练算法,以及对复杂动力系统内部机制的严格理解。"

此外,Rose 认为对称性发现是图表示学习中一个被忽视的重要公开问题:

“图形神经网络包含置换对称(不变性或等方差)。但这种全球对称性可能会产生根本性的限制。有许多优秀的论文将图神经网络推广到置换以外的对称群和局部对称(例如[9,5,52])。

在 2022 年,我将有兴趣看到更多关于图形神经网络对称性的研究。例如,包括自动对称发现[53]在内的等变网络的新发展可以转化为图 ML。"

推理、公理化和一般化仍然是图形 ML 中的大问题

近年来,随着越来越多的标准 GNN 基准变得饱和,针对 GNN 层的全新感应偏置的发展普遍放缓。事实上,甚至 Open Graph Benchmark 提供的一些任务现在似乎也依赖于非架构技巧(如外部数据或输出后处理)来推进。

上述情况的一个显著例外来自推理问题;在这里,边不仅仅是聚合的简单指南,而是如何在图中传递数据的诀窍。此外,对于真正的推理能力,我们通常还需要一定程度的概括,这在图形任务中很少需要,尤其是直推式任务。显然,现成的消息传递模型将在这里挣扎;因此,推理和它的近亲模拟都是 2021 年 GNN 更新的一些最有趣的小说的元凶。

B 因为这个领域是如此的新生,有许多方法可以鼓励推广;尤其是不发行的。我们与 2021 年提出的一个非常令人兴奋的方向的支持者之一取得了联系。他的回答首先让我们回到过去:

“我窥视未来(2022 年)的尝试必须从探究遥远的过去及其与 2021 年论文的联系开始。” —布鲁诺·里贝罗,美国普渡大学助理教授

“卡尔·皮尔逊、R. A .费舍尔和查尔斯·斯皮尔曼(现代统计学的一些创始人)有两个证据充分的盲点:(一)因果关系(皮尔逊有句名言,因果关系只需要一些类似相关性的东西);以及(ii)公理概率(由俄罗斯的 Kolmogorov 并行开发)。他们的盲点在今天的 Graph ML 中有着有趣的分支。”

在 2021 年,因果关系已经成为一种令人兴奋的观点,被 GNNs 推广。布鲁诺解释道:“因果关系是最大似然法和图形最大似然法的盲点。2021 年,这个领域出现了几个 Graph ML 作品。因果关系的一个最简单的应用是处理分布外(OOD)任务。在 ML 部署中,可以看到不同分布的测试和训练数据。在不访问测试分布数据的情况下,对这些分布变化的鲁棒性要求分类器对变化保持不变。这种不变性现在被称为反事实不变性。例如,[56,59]表明,虽然 GNNs 可以创建任何大小的图的表示,但专门在小(大)图上训练的图分类通常在大(小)测试图上表现不佳(OOD 任务)。专注于反事实不变性,[56]使用 Lovász & Szegedy 图极限结果[60]来表明,对于一族图(graphons),可以使用诱导子图表示来获得上述 OOD 任务的大小不变的全图表示。在不同的路线中,[61]提出基于任务相关子图来学习图表示,该任务相关子图对于虚假(任务无关)子图是不变的,这允许图分类器在这种任务中经受住虚假 OOD 子图分布变化。我预计 2022 年将在图形 ML 和因果关系之间带来新的令人兴奋的联系。”

Bevilacqua 等人【56】的工作已经证明了如何通过使用一个显式因果模型(此处描述)来模拟这种分布变化,从而为图形分类任务实现大小不变的 OOD 一般化。

除了因果关系及其对面向对象设计的影响,Bruno 还强调了通过仔细的公理化在支撑 GNN 架构方面取得的进展:“公理化概率通过陈述产生数据的分布的属性而不是给它一个函数形式来工作。例如,我们可以说随机变量序列的分布是置换不变的(可交换的),这就是我们对数据的全部了解。皮尔森、费舍尔和斯皮尔曼可能会感到困惑,人们可以创建一个只假设排列不变性(或等方差)的预测模型。

“事实上,在 2013 年至 2020 年期间,Graph ML 社区学会了部分填补这一盲点,这为 Graph ML 在 ML 内部的快速增长铺平了道路。实际上,这种几何公理化尚未完成。最先进的结果仍然需要构建神经架构,其设计尚未完全由已知的数据属性(公理)驱动,因此毫不奇怪,SOTA 现在部分由数据后处理驱动(以使数据符合架构)。但是 2021 年在进一步的几何公理化方面取得了一些进展:图表示体系结构被证明得到了额外公理的帮助(例如,与诱导子图和重构猜想[62–66])并且现在可以学习使用图的拓扑曲率来避免信息瓶颈[6]。

2022 年,我们将有望看到图形 ML 架构公理化的进一步发展。一个有趣的方向是链接预测的公理化,以填补 Spearman 留下的空白,如 2020 年由[67]基本描述为联合结构表示,2021 年由[68–69]和其他人进一步描述了方法,这些方法在 2022 年可能在寻求因果链接预测方法(例如,推荐系统[70])中发挥作用。"

从推理和归纳中获益良多的机器学习的另一个领域是知识图推理。KGs 在人工智能的早期就已经存在了,鉴于其巨大的工业影响,它们肯定会继续存在。在最近的工作中,汤集安利用寻路算法的直接知识构建了一种最先进的 KG 推理方法。简在给我们的回复中讲述了这个项目:

“我们在 2021 年观察到知识图推理的重大进展。现有的知识图推理算法通常只适用于直推式环境,而不适用于归纳式环境。在去年的 NeurIPS 中,我们提出了一个基于 GNNs 的通用和灵活的框架,用于图形上的链接预测,称为神经贝尔曼-福特网络(NBFNet) [71]。NBFNet 非常通用,涵盖了许多传统的基于路径的方法,并且可以应用于直推和归纳设置中的同构图和多关系图(例如,知识图)。在直推式和感应式设置中,它都大大优于现有的方法,实现了新的最先进的结果。我相信 NBFNet 将来会成为图上链接预测的标准框架。”

图形在强化学习中变得越来越流行,但可能还有一段路要走

强化学习仍然是当今人工智能研究中最突出和最活跃的领域之一。方便的是,也许并不令人惊讶的是,它充满了图形和对称:通常要么是在 RL 代理的结构中,要么是在环境的表示中。但是,即使是 RL 中的中心数学对象——马尔可夫决策过程(MDP ),也是状态和动作的有效图形。去年,Petar 预测,2021 年我们将看到 GNNs 在 RL 中的全面表现。这个预测变成现实了吗?

令人惊讶的是,我们在 RL 的几何方法的两个通讯从业者对此有截然相反的结论:

“2021 年,图形成为 RL 的正式玩家。” —维塔利·库林,牛津大学博士生

“图形神经网络已经对机器学习的许多领域产生了巨大影响。一个明显的例外是强化学习,在 2021 年,香草图神经网络仍然没有引起巨大的轰动,尽管几年前开始乐观[72-74]。” —埃莉斯·范德波尔,阿姆斯特丹大学博士生

尽管结论不同,但两者所强调的工作大体上是相似的。

维塔利强调:“2021 年,图表通过两个值得注意的组出现在 RL 中。第一种使用图形表示(包括基于注意力的模型)来建立 RL 基准,以提高概括/转移能力。例子包括我们在连续控制[75–76]、多智能体 RL 研究[77–78]和机器人协同适应[79–80]方面的工作。第二组让我非常兴奋,它将 RL 应用领域扩展到了具有基于图的状态和动作空间的环境。这个组包括电网管理[81],组合优化[82],在图论中寻找反例打开猜想[83]。”

Elise 评论说:“Kurin 等人[75]的工作提出了经验结果,表明即使在底层环境是图结构的情况下,普通图网络在强化学习方面的表现也不如基于转换器的方法。虽然图网络还没有成为强化学习的一个更重要的组成部分,但 2021 年的几部作品强调了强化学习可以受益于图网络的特定场景。亮点包括隐式规划与自我监督学习的结合[84],使用 GNNs 包括归纳偏差[85],以及网络规划[86]和编译器优化[87]等应用。此外,在 2021 年,我们还看到了一些对 Transformer [8]方法的初步研究,这些方法利用了强化学习任务的顺序结构[89–90]。在多智能体强化学习[92–93]中也有关于使用变压器和 GAT [91]方法的工作。此外,我们看到更多的 GNNs 与更多类型的等变集成,例如在我们的等变多代理协调工作中[94]。”

2021 年见证了几何深度学习在多智能体强化学习系统中令人兴奋的应用。它支持与任务的全局对称性等价的策略功能,同时仍然支持分布式执行。图片来自 van der Pol 等人[94]。

D 尽管他们在总结 2021 年时存在分歧,但他们都对图形机器学习将如何在未来一年塑造 RL 持乐观态度!

维塔利声称:“对我来说,图表是将 RL 带到现实世界的关键,因为它们在工业(芯片设计、代码建模、药物发现)和科学应用(物理、化学、生物、经济学)中无处不在。我相信 2022 年将见证新 RL 基准的寒武纪大爆发,这将把 RL 研究引向更实用的方向,并激励基础研究,同时揭示我们对 RL 的想法的不一致性。”

“我对 2022 年的预期是,基于变形金刚和类似 GAT 的方法将在强化学习中变得更加突出,因为它们在普通图形网络上取得了初步成功。图形网络、等方差和强化学习的组合优化也有很大的潜力,例如在分子设计、网络规划和芯片设计中。最后,在互模拟和 MDP 同态以及基于 GNN 的模型之间还有更多相互作用的空间,特别是在发现潜在(分解的)强化学习问题的抽象图,以及学习以有原则的方式划分 MDP 的时候。2022 年将成为图形网络成为强化学习中必不可少的组成部分的一年吗?”,问题伊莉斯。

AlphaFold 2 是几何 ML 的一次胜利,也是结构生物学的一次范式转变

正如几何深度学习可能是过去一年的定义性趋势之一(我们借此机会无耻地宣传我们自己在这一方向的努力,最终导致我们与琼·布鲁纳和塔科·科恩一起写的同名原型书),2021 年 GDL 的一个特别的开创性实例几乎没有疑问。当然,我们谈论的是 AlphaFold 2 [95]:一种蛋白质结构预测的架构,它主导了蛋白质结构预测的关键评估(CASP14)竞赛,这是结构生物学家的一种“ImageNet moment”。AlphaFold 2 模块的核心是不变点注意力,它明确考虑了蛋白质的几何形状。我们采访了 AlphaFold 2 的联合第一作者之一,以获得这项成就和后续工作的第一手资料。

“在过去的几年里,深度学习已经成为蛋白质结构预测的核心工具。” — Alexander Pritzel,DeepMind 的研究科学家。

“2021 年,我们发布了我们的模型 AlphaFold 2 [95]以及源代码和大量模型生物的预测数据库,我们计划将这些模型生物扩展到大部分已知蛋白质。通过允许研究人员使用预测的结构来解释他们的数据,这加快了实验蛋白质结构测定的进展[96]。在绘制蛋白质之间的相互作用网络方面也有了初步进展,例如应用 AlphaFold 2 来理解真核生物的相互作用以及人类蛋白质的相互作用[97–98]。在 2022 年,我预计非常精确的蛋白质结构预测的非常大的数据集的可用性将刺激使用蛋白质结构预测蛋白质功能属性的新研究。

我希望看到进步的另一个领域是图形推理。对于许多现实世界的问题,潜在的图形是未知的先验的或者像分子图这样的自然图形不是用于消息传递的正确图形,所以图形是预测的一部分。通过使用模糊的传递性概念来推断图形的想法是 AlphaFold 的核心架构组件之一的动机。

总之,在过去的一年里,我们已经看到 ML 成为生物学中的核心工具,我希望在这个领域看到更多的工作,希望通过采用机器学习和实验方法的结合,为细胞生物学的原子理解铺平道路。我也希望看到 ML 成为许多其他科学研究领域的核心工具。"

AlphaFold 2 带来了蛋白质折叠结构预测的突破。该架构的核心是实现几何等变的“不变点注意”模块。图片来自 Jumper 等人[95]。

Talpha fold 2 的成功引起了许多从业者——事实上,我们的许多受访者——反思并重新评估他们对几何深度学习的一些假设。Aasa Feragen 对这一突破对受微分几何启发的方法的影响进行了某种程度上的哲学思考:

“2021 已经看到了一些微分几何问题如何被提供成功解决方案的例子。一个引人注目的例子是 AlphaFold 2 [95]及其不变点注意。在其他情况下,没有明确使用微分几何:例如,拓扑感知损失函数[99]和配准模型[100],它们在没有实际量化拓扑的情况下惩罚保留拓扑的失败;或者在不明确使用几何的情况下学习几何不变性[101]。

我发现这是一个有趣的教训:虽然微分几何对于理解和表达一个问题可能是基本的和有用的,但没有它也能经常找到有效的和广泛可用的解决方案。这是否表明复杂的数学模型在解决问题时比最终解决问题时发挥了更大的作用?或者这是一个信号,表明我们因为允许实践和理论社区之间的持续分裂而错过了更好的模型?"

虽然 AlphaFold 2 可能是 2021 年讨论最多的 ML 模型之一,但这篇博客的作者指出,2021 年大卫·贝克的实验室也有一项令人印象深刻的工作,名为 RosettaFold [102]。它受 AlphaFold 的思想和成果启发,也是基于 SE(3)-equivariance [103]的几何深度学习架构。

药物发现和设计受益于 GNNs 及其与变压器的融合

AlphaFold 2 的预测非常准确,被誉为蛋白质折叠问题的“解决方案”。这样,模拟大分子的基础就被动摇了。对于仍然是药物治疗的战马的小分子,我们能说些什么呢?

G 神经网络在应用于小分子时产生了一些最初的显著影响:推断它们的溶解度或毒性[104],它们的量子化学性质[105],或它们作为候选药物的效力[106]。正如我们的一位撰稿人简明扼要地总结的那样,这一浪潮将持续到 2021 年:

“graph ml 不断推进分子建模领域,在药物和材料发现方面都有应用。” —汤集安,Mila 助理教授

“特别是,我对 GemNet 算法[107]感到非常兴奋,它为 3D 分子结构建模提供了一个通用的表达框架,并在包括分子动力学模拟和开放式催化剂发现竞赛在内的许多任务中实现了最先进的性能。在 2022 年,我相信我们会在这个方向上看到更多令人兴奋的进展,例如蛋白质建模,”Jian 总结道。

GemNet 架构是 2021 年图形神经网络分子建模热门领域的亮点之一。图片来自克利茨佩拉等人。

用于模拟小分子的 gnn 研究背后的关键驱动力之一是它对药物研发管道的直接适用性。我们联系了这一特定交叉点的专家进行评论,重申了这一立场:

“药物发现是 GNN 研究的最热门话题之一,因为分子显示出图形结构,知识图形在研究蛋白质功能和疾病中无处不在。” — Dominique Beaini,Valence Discovery 首席深度学习研究员

“在药物研发中,理解化学最具挑战性的方面之一是从分子图预测量子相互作用和 3D 结构。尽管在这个问题上已经有了大量的工作[108–109],但是这些模型通常过于具体,因为它们仅仅是为了解决这个问题而设计的,并且不清楚 3D 知识可以在多大程度上转移到不相关的任务[110]。相反,一个能够学习量子力学的通用 GNN 将能够将学到的嵌入转移到任何新的任务中,但这只有在完全连接的变压器能够捕捉分子图之外的相互作用的情况下才有可能。”

2021 年,一系列并行研究成功地将变形金刚归纳为图形。多米尼克阐述道:

“在 NeurIPS 2021 上,我们已经看到了前两个在图形数据上表现良好的转换器,即 SAN [111]和 graph former[112]。后者表明,通过赢得 OGB·LSC 竞赛[29]和脸书的催化剂竞赛[113],变形金刚可以更好地捕捉量子力学,并且当它们在 OGB-莫尔斯巴[112]的排行榜上占据主导地位时,学到的嵌入可以转移到无关的生物任务中。”

小分子建模似乎是图形转换器作为一种最先进的方法出现的领域——无论是理论上还是经验上。我们联系了竞赛获奖产品 Graphormer architecture 的作者之一,以获得进一步的见解:

“在 2021 年,已经取得了令人兴奋的进展,通过采用不同的方法合并图形结构信息,使变压器架构适应图形 ML 域[111–112,114–115]。” —蔡天乐,普林斯顿大学博士生

天乐概述了 2022 年我们可以进一步推进图形转换器的几个方向。其中之一是解决科学问题的 GNN:

“Graphormer [112]在量子化学(KDD 杯-OGB-LSC)和分子动力学任务(开放催化剂挑战)方面的成功显示了 GNNs 在科学计算方面的能力和潜力。此外,GNNs 可以固有地被设计[5]以享受几个对称属性,这将使模型从物理学的角度来看起来更好。

Transformer 架构在自然语言处理中的流行在很大程度上归功于模型的可用性,如 BERT、GPT、ViT 或对大型数据集进行预处理的 CLIP,这些模型似乎可以很好地概括。Graph Transformer 架构具有强大的表达能力,但它的泛化能力应该由足够多的数据来保证。"

全图注意力的计算复杂性是另一个问题:“变压器具有相对于节点数量的二次时间和空间复杂性,这使得它在大型图中难以处理。应该开发降低这种复杂性的技术,以便能够在更大的图形上使用图形转换器。”

Graphormer 拥有熟悉的 Transformer 主干,同时充满了特定于图形的创新,是 2021 年最成功的 gnn 之一,尤其是在计算化学大规模挑战方面。图来自 Ying 等人[112]。

天乐很高兴看到图形转换器的进一步发展,特别是在科学领域:“我认为从数据角度(通过对更大的数据集进行预训练或使用 Sim2Real 的思想来生成更多数据)和模型角度(通过设计更有效的架构,如线性图形转换器,或引入图形粗化或采样技术)进一步扩大当前方法是很重要的。”

D ominique 肯定了围绕图形转换器的更多根本性挑战将在 2022 年得到解决的预期:

“尽管早期的图形转换器取得了成功,但它们在表达能力、对图形的结构理解以及对更大图形的可扩展性方面仍然有限,但我们希望在 2022 年解决这些问题。变形金刚将改变我们捕捉量子和 3D 信息的方式,我们生成分子的方式,以及我们在化学任务中传递知识的方式。”

人工智能-首次药物发现越来越多地使用几何和图形 ML

虽然小分子建模方面的进步已经为虚拟药物筛选管道[106]带来了突破,但将它们与 AlphaFold 的结构生物学结果相结合,可以打开整个药物发现管道中大量重要应用的大门。药物发现领域的许多新兴创业公司都认识到了这一点,并表示“人工智能优先”的药物发现方法是可以实现的。专注于人工智能优先公司的 Air Street Capital 公司的计算生物学家兼风险投资人内森·贝纳奇(Nathan Benaich)从企业家的角度阐述了这一点:

“我们已经看到几何和图形 ML 方法在诸如基于网格的模拟[38]和 mrna[117]和蛋白质[95]的结构预测等问题上颠覆并取代了经典方法。展望未来,我期待几何和图形 ML 能够解决自然科学和物理科学领域越来越多的问题,帮助我们破解它们的复杂性。实际上,这可能意味着人工智能优先的药物和材料设计活动的有效性发生了重大变化,特别是在多参数优化、性质预测和结合方面。

更重要的是,我希望进步集中在克服大型几何和图形 ML 的内存瓶颈,以及解决深度对模型性能的影响和对更好架构的需求。"

Dominique Beaini 反映了他的观点,他在一家人工智能优先的药物开发初创公司领导着一个应用研究团队:

“AlphaFold 最近成功预测了蛋白质折叠,这给社区带来了希望,即机器学习将极大地影响生物学研究[95],Valence Discovery 等众多公司正在构建强大的药物发现平台,这些平台由 GNNs 的最新进展提供支持。”

融合这些进展的一个最直接的方法是提供更精确的模型,来描述大小分子如何相互作用。到目前为止,大多数新兴技术都集中在分析分子,就好像它们在真空中一样。打破这种假设可能会导致图形表示学习的一些最令人兴奋的应用,正如去年最活跃的 GNN 社区成员之一所强调的那样:

“Graph ML 已经改变了单个蛋白质,尤其是小分子的分析。但是如何模拟他们的互动呢?” —汉尼斯·斯塔克,慕尼黑工业大学理学硕士。

汉尼斯首先详细叙述并放大了我们的其他通讯记者强调的许多进展:“图形 ML 在分子预测方面的能力已经得到很好的证实[26,117–118],GNNs 在抗生素发现等有影响力的领域取得了令人印象深刻的成功[106]。利用分子的 3D 几何结构,Graph ML 还通过 3D GNNs(如 GemNet [107]或 NequIP 的分子动力学[119])推进了量子化学。e(3)-等变 GNNs [5]和几何深度学习已经成为在分子上学习的重要组成部分[120]。

类似地,3D GNNs 显示了蛋白质表达学习的强大结果,其方法使得它们的使用对于这些大分子在计算上是可行的[121]。关于蛋白质的图 ML,我当然也对 AlphaFold2 [95]感到高兴,它极大地推进了蛋白质结构预测,因此也推进了整个分子生物学领域。"

在这些基础上,汉尼斯继续概述了围绕分子相互作用的激动人心的事情,以及它们对药物发现的意义:“考虑到图 ML 现在对预测单个分子的重要作用,我认为在模拟分子相互作用方面即将取得突破。虽然预测分子的内在属性很有趣,但我认为了解蛋白质如何与我们体内的其他蛋白质相互作用,或者分子如何与病毒相互作用以抑制其功能,往往会有更大的现实意义。该研究方向已经在 2021 年开始成型,采用几何深度学习方法来预测蛋白质与小分子[122]或其他蛋白质[14]相互作用的原子构型。

我对图形 ML 的潜力感到兴奋,例如,几何意识结合预测或预测多个分子的原子将如何移动以及当它们相互作用时它们的构象将如何改变。这些对不止一个分子进行建模的应用为以有原则的方式包括 3D 信息带来了额外的困难。因此,对于创造性的想法来说,有许多开放的途径来捕捉对称性和关于特定分子相互作用的先验几何知识。我认为 2022 年完全有可能发现其中一些,并且该领域具有巨大的积极影响潜力,我迫不及待地想看看 Graph ML 如何推进分子相互作用预测。"

Quantum ML 受益于基于图形的方法

我们以一个对大多数 ML 人来说仍然有点陌生的领域结束,但很快变得不那么陌生了,整个行业都在打赌看到更多的是“何时”而不是“如果”的问题。这当然是量子计算,以及它在机器学习方面的最新应用。这和图表有什么关系?从事这两个领域交叉研究的 Guillaume Verdon 解释道:

“量子机器学习(QML) [123],是 ML 和量子计算交叉的新兴研究领域,近年来学术界和工业界的兴趣激增,特别是随着经典不可模拟量子计算机的出现[124]。QML 的主要吸引力之一是它能够从量子数据中学习量子模型和表示。由于自然本身是量子力学的,学习数据的量子表示可以帮助我们扩展经典 ML 的范围,以在基础水平上模拟自然。2021 年,谷歌在 Sycamore 处理器[125]上通过实验证明了 Quantum ML 量子数据的指数级扩展优势,这是该领域一个充满希望的里程碑。”

在这个早期的演示中,QML 仍然面临几个挑战,一个主要的障碍是可扩展、可训练和提供强泛化能力的量子神经网络架构的设计[126]。受经典 GNN 的启发,量子神经架构最近出现[127]来解决这个问题,并在过去的一年中成功地在量子硬件中实现[128]。

量子系统的演化通常由所谓的哈密顿量控制,对于大多数系统来说,哈密顿量具有对应于图的子系统之间的耦合局域性。因此,将图结构归纳偏差添加到我们的量子架构中对于模拟量子系统的动力学[127]和平衡性质[129–130]是非常自然的。"

因此,我们以乐观和雄心勃勃的口吻总结:

“2022 年扩展这种图形量子 ML 研究的一个令人兴奋的方向将是探索图形之外的几何深度学习理论的量子版本,因为量子物理系统通常拥有丰富而深奥的群对称性,这可以用于量子架构设计,从而进一步提高我们使用量子计算机生成性地模拟这种系统的能力。”— Guillaume Verdon,Quantum ML Lead,字母表 X

量子计算机会是几何 ML 的下一个前沿吗?图片:Shutterstock。

[1] M .博古等,网络几何 (2021)自然评论物理学 3:114–135。

[2] Q. Liu,M. Nickel,D. Kiela,双曲图神经网络(2019) NeurIPS。

[3]法学硕士。超双曲神经网络。

[4] Y .张等,洛仑兹图卷积网络(2021)WWW .

[5] V. G. Satorras,E. Hoogeboom,M. Welling,E(n)等变图神经网络(2021) ICML

[6] J. Topping 等人,通过 curvatur 理解图的过度挤压和瓶颈 e (2022) ICLR。另见附带的博客文章。

[7] H. Maron 等人,不变和等变图网络 (2019) ICLR。

[8] T. Cohen 等人,规范等变卷积网络和二十面体 CNN (2019) ICML。

[9] P. de Haan、T. Cohen 和 M. Welling,《自然图网络》( 2020 年)。

[10] E. H .、w .周和 R. Kondor,《高速公路:基于自同构的图形神经网络》( 2021 年)。

[11] E. Moshe 等人,PDE-GCN:由偏微分方程激发的图形神经网络的新架构(2021)。

[12] B. P. Chamberlain 等人,Beltrami 流和图形上的神经扩散(2021),NeurIPS。

[13] U. Alon 和 E. Yahav,关于图神经网络的瓶颈及其实际意义 (2021) ICLR。

[14] O.-E .加内亚等人,端到端刚性蛋白质对接的独立 SE(3)-等变模型(2022) ICLR。

[15] N. S. Detlefsen,S. Hauberg 和 W. Boomsma,什么是蛋白质序列的有意义的表示?(2021) arXiv:2012.02679。

[16] R. Sengers,L. Florack 和 A. Fuster 扩散磁共振成像(2021) IPMI 不确定性量化的测地线管。

[17] P. J .凯利,关于等距变换(1942 年)的博士论文。

[18] B. Chamberlain 等人, GRAND: Graph 神经扩散 (2021) ICML。另见一篇附带的博文。

[19] D. I .舒曼等人,图形信号处理的新兴领域(2013),IEEE 信号处理杂志 30(3):83–98。

[20] M .魏勒等人,坐标独立卷积网络-黎曼流形上的等距和规范等变卷积(2021),arXiv:2106.06020。

[21] D. Kreutzer 等人,重新思考具有光谱注意力的图形转换器(2021),NeurIPS。

[22] M. M .布朗斯坦等,几何深度学习:超越欧几里德数据 (2017),IEEE 信号处理杂志 34(4):18–42。

[23] J .布鲁纳等人,图的谱网络和局部连通网络 (2014) ICLR。

[24] D. C. McNamee 等人,内嗅-海马系统中序列生成的灵活调节 (2021)《自然神经科学》24:851–862。

[25] P. Piray 和 N. D. Daw,规划、网格领域和认知控制中的线性强化学习 (2021)《自然通讯》12。

[26] K. L. Stachenfeld,M. M. Botvinick 和 S. J. Gershman,海马作为预测图 (2017)《自然神经科学》20:1643–1653。

[27] A .德罗-皮农等人,谷歌地图中用图形神经网络进行 ETA 预测(2021) arXiv:2108.11482。另请参见随附的博文。

[28] T .普法夫等人,用图形网络学习基于网格的模拟(2020) NeurIPS。

[29] W. Hu 等,图机器学习面临的大规模挑战(2021) arXiv:2103.09430。

[30] J. Wang 等, scGNN 是一种用于单细胞 RNA-Seq 分析的新型图神经网络框架(2021)Nature communication s 12。

[31] A. Bessadok,M. A. Mahjoub 和 I. Rekik,使用拓扑感知对抗图神经网络的大脑多图预测(2021) arXiv:2105.02565。

[32] X .李等,BrainGNN:用于 fMRI 分析的可解释脑图神经网络(2021),医学图像分析 74。

[33] L. Torres 等人,复杂系统表示的原因、方式和时间(2021),SIAM Review 63(3):435–485。

[34] F .巴蒂斯顿等人,超越成对相互作用的网络:结构和动力学(2020)物理学报告 874:1–92。

[35] A. R. Benson、D. F. Gleich 和 D. J. Higham:高阶网络分析在经典思想和新数据的推动下起飞(2021 年),《暹罗新闻》(另见扩展版)。

[36] T. Eliassi-Rad 等人,高阶图模型:从理论基础到机器学习(2021)Dagstuhl 研讨会 21352 的报告。

[37] C .博德纳尔,f .弗拉斯卡等,魏斯费勒和雷曼 go 拓扑:消息传递单纯网络 (2021) ICML。

[38] C .博德纳尔、f .弗拉斯卡等人,魏斯费勒和雷曼 go cellular:CW Networks(2021)neur IPS。

[39] S. Ebli、M. Defferrard 和 G. Spreemann,《单纯神经网络》( 2020 年), NeurIPS 拓扑数据分析研讨会及其他。

[40] E. Bunch 等人,Simplicial 2-Complex 卷积神经网络(2020 年),NeurIPS 拓扑数据分析研讨会及其他。

[41] T. M. Roddenberry、N. Glaze 和 Santiago Segarra,《用于轨迹预测的简单神经网络原理》( 2021 年), ICML。

[42] M. Hajij、K. Istvan 和 G. Zamzmi,细胞复合神经网络(2020) NeurIPS 拓扑数据分析及其他研讨会。

[43] M. Hajij,G. Zamzmi 和 X. Cai,单纯复表示学习(2021) arXiv:2103.04046。

[44] A. D. Keros,V. Nanda 和 K. Subr,Dist2Cycle:用于同源定位的单纯神经网络(2021 年)AAAI。

[45] Y. Chen,Y. R. Gel 和 H. V. Poor,BScNets:块单纯复杂神经网络(2022).

[46] J. G. Lambourne 等人,BRepNet:用于实体模型的拓扑信息传递系统(2021 年),CVPR。

[47] J. M. Curry,绳轮、共沉和应用 ( 2014)博士论文。

[48] J. Hansen 和 T. Gebhart,Sheaf neuro Networks(2020)neur IPS 拓扑数据分析及其他研讨会。

[49] A. Parada-Mayorga 等人,颤动信号处理(2020 年)arXiv:2010.11525。

[50]吴等,深层时空预测中的不确定性量化(2021).

[51] C .尚,j .陈,和 j .毕,用于预测多个时间序列的离散图结构学习(2020).

[52] R. Walters,J. Li,,利用等变连续卷积进行轨迹预测(2021).

[53] N. Dehmami 等人,利用李代数卷积网络的自动对称性发现(2021),NeurIPS。

[54] A. D'Amour 等人,欠指定对现代机器学习中的可信度提出了挑战(2020) arXiv:2011.03395。

[55] W. Hu 等人,开放图基准:关于图的机器学习的数据集(2021) NeurIPS。

[56] B. Bevilacqua、Y. Zhou 和 B. Ribeiro,《图分类外推的尺寸不变图表示法》( 2021 年)。

[57] S. C. Mouli 和 B. Ribeiro,《从单一环境中学习反事实 G 不变性的神经网络》( 2021 年), ICLR。

[58] V. Veitch 等人,《文本分类中虚假相关性的反事实不变性》( 2021 年)。

[59] G. Yehudai 等人,从图形神经网络中的局部结构到尺寸泛化(2021 年),ICML。

[60] L. Lovasz 和 B. Szegedy,《密集图序列的极限》( 2006 年)组合理论杂志,系列 b

[61]匿名,发现图形神经网络的不变推理(2021),https://openreview.net/forum?id=hGXij5rfiHw

[62] L. Cotta、C. Morris 和 B. Ribeiro,《强大图形表示的重建》( 2021 年)。

[63] B. Bevilacqua 等人,等变子图聚合网络(2022 年),ICLR。

[64] P. A. Papp 等人, DropGNN:随机丢失增加了图神经网络的表达能力 (2021) arXiv:2111.06283。

[65] G. Bouritsas 等,通过子图同构计数提高图神经网络表达能力(2020)arXiv:2006.09252;参见随附的帖子。

[66] L .赵等,从星到子图:用局部结构意识提升任何一个 GNN(2021)arXiv:2110.03753。

[67] B. Srinivasan 和 B. Ribeiro,关于位置节点嵌入和结构图表示之间的等价性(2020 年)ICLR 2020 年。

[68] M. Zhang 等,标记技巧:使用图神经网络进行多节点表征学习的理论(2021) NeurIPS。

[69] V. P. Dwivedi 等人,具有可学习的结构和位置表示的图形神经网络(2021) arXiv:2110.07875。

[70] T. Joachims 等人,作为治疗的建议(2021 年)AI 杂志,42(3):19-30。

[71] Z. Zhu 等,神经贝尔曼-福特网络:用于链路预测的一般图神经网络框架(2021) NeurIPS。

[72] A. Tamar 等人,价值迭代网络(2016 年)。

[73] G. Farquhar 等人,TreeQN 和 ATreeC:深度强化学习的可微分树形结构模型(2018) ICLR。

[74] T. Wang 等,Nervenet:用图神经网络学习结构化政策(2018).

[75]: V. Kurin 等人,我的身体是一个笼子:形态学在基于图的不相容控制中的作用(2021) ICLR。

[76] C. Blake 等人,“雪花:通过参数冻结将 GNNs 扩展到高维连续控制”( 2021 年)。

[77] W .尚等,多智能体强化学习的智能体中心表示(2021) arXiv:2104.09402 .

[78] S. Li 等,多智能体强化学习的深度隐式协调图(2021) AAMAS。

[79] K. S .勒克,r .卡兰德拉和 m .密斯特里,我需要什么机器人?使用图形神经网络的形态学和控制的快速共同适应。

[80] D. J. Hejna III,P. Abbeel 和 L. Pinto:任务不可知的形态学进化(2021) ICLR。

[81] D. Yoon 等人,赢得 L2RPN 挑战:通过半马尔可夫后状态行动者-评论家进行电网管理(2020),ICLR。

[82] Q. Cappart 等人,用图形神经网络进行组合优化和推理(2021) arXiv:2102.09544。

[83] A. Z. Wagner,通过神经网络的组合学构造(2021) arXiv:2104.14516。

[84] A. I. Deac 等人,神经算法推理器是隐式规划器(2021) NeurIPS。

[85] Z. Jiang 等,网格到图形:用于强化学习的灵活空间关系归纳偏差(2021) AAMAS。

[86] H. Zhu 等,具有深度强化学习的网络规划(2021) SIGCOMM。

[87] C. Cummins 等人,编译器体育馆:用于人工智能研究的健壮、高性能编译器优化环境(2021) arXiv:2109.08267。

[88] A. Vaswani 等人,注意力是你所需要的一切 (2017) NIPS。

[89] L. Chen 等,决策转换器:通过序列建模进行强化学习(2021) arXiv:2106.01345 .

[90] M. Janner、Q. Li 和 S. Levine,将离线强化学习作为一个大序列建模问题(2021) NeurIPS。

[91]p . veli kovi 等人,图形注意力网络(2018 年),ICLR。

[92] S. Hu 等,UPDeT:通过政策与变压器解耦实现通用多智能体 RL(2021).

[93] Y. Niu,R. Paleja 和 M. Gombolay,多主体图形-注意力交流和团队合作(2021 年)。

[94] E. van der Pol 等人,多智能体 MDP 同态网络(2022 年),ICLR。

[95] J. Jumper 等人,用 AlphaFold 进行高度精确的蛋白质结构预测,Nature 596:583–589,2021。

[96] G. Masrati 等,精确结构预测时代的整合结构生物学(2021)《分子生物学杂志》433(20)。

[97] I. R. Humpreys 等人,核心真核蛋白质复合物的计算结构 (2021)科学 374(6573)。

[98] D. F. Burke 等人,建立结构解析的人类蛋白质相互作用网络(2021)bioarXiv:2021 . 11 . 08 . 467664。

[99] S. Shit 等人,cl dice-一种用于管状结构分割的新型拓扑保持损失函数(2021),CVPR。

[100] P. S. Czolbe、A. Feragen 和 O. Krause 指出了差异:通过几何排列检测拓扑变化(2021)。

[101]p . e . Schw bel 等人,不变性学习的最后一层边际似然性(2021) arXiv:2106.07512。

[102] M. Baek 等人,使用三轨道神经网络准确预测蛋白质结构和相互作用,科学 373:871–876,2021。

[103] F. B. Fuchs 等人,SE(3)-变形金刚:3D 旋转翻译等变注意力网络(2020 年)。

[104] D. Duvenaud 等人,用于学习分子指纹图的卷积网络(2015) NeurIPS。

[105] J. Gilmer 等人,量子化学的神经信息传递(2017) ICML

[106] J. M. Stokes 等人,抗生素发现的深度学习方法(2020)Cell 180(4):688–702。

[107] J .克利茨佩拉,f .贝克尔和 s .居内曼, GemNet:分子的通用方向图神经网络 (2021) arXiv:2106.08903。

[108] S. Luo 等人,通过动态图形得分匹配预测分子构象(2021) NeurIPS。

[109] C .施 C 等,用于分子构象生成的学习梯度场(2021).

[110]h . strk 等人,3D Infomax 改进用于分子性质预测的 GNNs(2021)arXiv:2110.04126。

[111] D .克罗伊泽 D .等人,《用光谱注意力重新思考图形转换器》( 2021 年)。

[112] C. Ying C 等,变形金刚对图形表示真的表现不好吗?(2021) NeurIPS。

[113] L. Chanussot 等人,开放催化剂 2020 (OC20)数据集和社区挑战(2021) ACS 目录 11:6059–6072。

[114] V. P. Dwivedi 和 X. Bresson 将变压器网络推广到图(2021)关于图的深度学习的研讨会:方法和应用。

[115] G. Mialon 等人,Graphit:在变压器中编码图形结构(2021) arXiv:2106.05667。

[116] T .普法夫等人,《用图形网络学习基于网格的模拟》( 2021 年)ICLR。

[117] J. Pan,深度学习助推 RNA 预测(2021)《自然计算科学》1(564)。

[117] K. Yang 等人,分析用于性质预测的习得性分子表征(2019) J. Chem。Inf。型号 59(8):3370–3388。

[118] O.-E .加内亚等人,GeoMol:分子 3D 构象异构体系综的扭转几何生成(2021) NeurIPS。

[119] S. Batzner 等人,E(3)-用于数据高效和精确的原子间势的等变图神经网络(2021) arXiv:2101.03164。

[120] K. Atz 等人,关于分子表示的几何深度学习(2021)《自然机器智能》3:1023–1032。

[121] V. R. Somnath 等人,蛋白质的多尺度表征学习(2021) NeurIPS。

[122] O. Méndez-Lucio 等人,预测生物活性分子结合构象的几何深度学习方法(2021)Nature Machine Intelligence 3:1033–1039。

[123] M .布劳顿等,张量流量子:量子机器学习的软件框架(202) arXiv:2003.02989。

[124] F. Arute 等人,使用可编程超导处理器的量子优势(2019),《自然》574(7779):505–510。

[125]黄等.从实验中学习的量子优势(2021). arXiv:2112.00778 .

[126] J. R. McClean 等人,量子神经网络训练景观中的贫瘠高原(2018 年),《自然通讯》9:1–6。

[127] G. Verdon 等人,量子图神经网络(2019) arXiv:1909.12264。

[128] L.-P. Henry 等人,量子进化核:具有可编程量子位阵列的图上的机器学习(2021)《物理评论》A 104(3)。

[129] G. Verdon 等人,基于量子哈密顿量的模型和变分量子热化器算法(2019) arXiv:1910.02071。

[130] C. Zoufal,A. Lucchi 和 S. Woerner,变分量子玻尔兹曼机器(2021)量子机器智能 3(1):1–15。

本文与 Petar Velič ković共同撰写,基于 Dominique Beaini(Valence Discovery)、Nathan Benaich (Air Street Capital)、Cristian(剑桥大学)、Tianle Cai(普林斯顿大学)、Tina Eliassi-Rad(东北大学)、Aasa Feragen(哥本哈根大学)、Pim de Haan(阿姆斯特丹大学)、Francesco Di Giovanni(推特)、Vitaly Kurin(牛津大学)、Haggai Maron (Nvidia)、Alexander Pritzel (DeepMind)、Bruno Ribeiro(pur Elise van der Pol(阿姆斯特丹大学)、Pierre Vandergheynst (EPFL)、Guillaume Verdon(字母表 X)、Rose Yu(加州大学圣地亚哥分校)和 Melanie Weber(牛津大学)。 不用说,所有的荣誉都属于前面提到的人,而任何批评都应该是作者的唯一责任。我们也非常感谢本·张伯伦、费比诺·弗拉斯卡、玛利亚·戈里诺瓦和伊曼纽·罗西校对了这篇文章。

关于图形深度学习的其他文章,请参见 Michael 的 其他帖子 在《走向数据科学》中, 订阅 到他的帖子和 YouTube 频道 ,获取 中等会员资格 ,或者关注 Michael

逻辑回归中的预测参数:理解一切

原文:https://towardsdatascience.com/predictive-parameters-in-a-logistic-regression-making-sense-of-it-all-476bde9825f3

所有图片由作者提供

获得对超出典型优势比解释的 logit 模型参数的深入理解

L 逻辑回归,也称为 logit 模型,是线性回归的强大替代品,它允许人们对二分、二进制结果(即 0 或 1)进行建模,并在给定观察值的情况下对所述结果发生的概率提供非常准确的预测。logit 模型中的参数估计可以提供关于不同解释变量或特征如何影响模型预测的见解。许多读者可能都熟悉用优势比来解释 logit 模型参数(如果不熟悉,不用担心,我会在下面简要介绍)。然而,根据概率对这些参数的解释并不简单,但是对如何解释这些参数的充分理解可以提供巨大的直觉来解释模型的潜在预测行为。

做出预测是非常强大的,但是直观地解释模型的预测组件可以将您的项目分析带到下一个级别。

在这篇文章的结尾,你会以一种新的视角来看待逻辑回归,并且理解如何用惊人的直觉来解释模型参数。本文假设对 logit 模型有一个简单的基础知识,从而以一种可理解的方式更专注于解释模型参数。然而,我们将首先简要讨论 logit 模型背后的理论。然后,我们将深入讨论如何将模型参数解释为边际效应。最后,我们将介绍一个利用以下 Kaggle 数据集预测欺诈性信用卡交易的实例。

逻辑回归速成班

Logit 模型属于更广泛的广义线性模型 (GLMs)家族,简而言之,当感兴趣的结果遵循不同于高斯的基本分布时,允许灵活拟合线性模型,并通过链接函数将线性模型与感兴趣的结果相关联。标准线性回归是一个特例,其中连接函数是单位函数。在二元结果的情况下,线性回归(称为线性概率模型)可以提供小于 0 或大于 1 的预测值(见图 1)。这显然提出了问题,因为概率自然地限制在 0 和 1 之间。然而,GLM 提供了一个方便的框架来解决这个问题!

logit 模型是一个特例,它允许对遵循伯努利分布的二元结果进行建模。logit 模型特别有吸引力,因为使用的链接函数(logit 函数)在 0 和 1 之间。因此,如概率空间中所预期的,所有模型预测都被限制在 0 和 1 之间。下面的图 1 提供了一个两变量情况下线性概率模型和逻辑回归模型拟合之间的直观比较。

图 1

在数学上,logit 模型的特征是:

其中 X 表示模型中观察到的解释变量或特征的矩阵,而 p(X) 表示 y 取值为 1 的概率。给定具有 y 分布伯努利的模型设置,logit 模型估计的目标是最大化以下似然函数,这是我们的联合分布:

简而言之,我们的优化问题寻求选择将最大化(2)的(1)中的参数(即 β )。注意,当估计的概率对于具有 y = 1 的个体接近 1 并且对于具有 y = 0 的个体接近 0 时,将(2)最大化。为此,可以采用似然函数的对数来获得对数似然,并使用梯度下降或相关算法来解决这个问题。关于逻辑回归的 wiki 页面提供了对 logit 模型估计的深入探讨。

将 Logit 参数解释为边际效应

边际效应可以被认为是由感兴趣的解释变量(或特征)的变化对结果(或目标)变量产生的平均(或边际)效应。这可以类似地概念化如下:在平均(或 【边际】 观察/个体,改变一个解释变量对结果的效果是什么。在我们的二元变量的例子中,这类似于估计改变一个解释变量对观察结果的概率的平均影响。

注意 😗* 边际效应必须仅解释为关联而非**因果关系。因果关系需要额外的识别假设,我在最近的一篇文章中提到了这一点。

通过认识到边际效应仅仅是一个变化率,人们可以立即注意到这归结为对解释变量求导。为了简单起见,我们首先从简单的线性回归开始。假设我们有以下线性回归:

为了找到任何变量的边际效应,我们可以对(3)中感兴趣的 x 求导。对任何 x* 的导数简单来说就是:

注意,在这种情况下,我们有一个恒定的边际效应,这是有意义的,因为线性回归是 y 到 x 的线性投影。

释义: 平均起来,x* 增加一个单位就是 关联 与y 增加一个β*变化。

现在,细心的读者可能会注意到,这个导数对于 logit 模型来说并不是微不足道的(见下文对对数优势和优势比的讨论)。考虑等式 2 中描述的逻辑模型。(1).关于任意 x* 的导数可以利用链和商法则求解。因此,我们可以发现 x* 对 y 发生概率的边际效应如下:

这里我们可以看到边际效应现在是 x 的本身的值的函数。这也是有意义的,因为 logit 函数是非线性的(见图 1)。这给了我们评估任何x组合的边际效应的能力。然而,如果我们想总结总体边际效应,我们有两个选择:

  1. 计算平均值边际效应 —这需要对每个观察值使用(5)计算边际效应,然后计算平均值
  2. 计算平均边际效应**——这需要将所有解释变量的平均值代入(5)并计算边际效应**

一个比另一个没有立竿见影的好处,两者在不同的背景下提供不同的解释。然而,平均边际效应提供了最清晰的解释,因此将是我们在这篇文章的剩余部分所要处理的。

请注意,所有的计算都可以很容易地扩展到计算边际效应,不仅在解释变量的平均值,但在任何组合的价值。我将把这个留给感兴趣的读者,下一节中提供的代码可以很容易地扩充到这样做(即,将您感兴趣的每个变量的值插入到(5)中,以获得该观察的边际效应)。这可以提供非常强大的洞察力,了解预测参数边际效应如何因特定类型的个人/观察而变化!

然而,对 logit 模型中平均边际效应的解释如下:

平均边际效应:

释义: 平均而言,x*每增加一个单位, 关联 与 y 发生概率的{计算值}个百分点变化。

对数赔率、赔率和赔率比

在我们提供一个实际应用的数字示例之前,讨论 logit 模型、对数优势、优势和优势比之间的关系是很重要的。逻辑回归结果用概率来解释是很常见的,这是因为~经过一些代数~我们可以将(1)改写为:

左手边是对数优势。因此,逻辑回归在对数优势方面具有恒定的边际效应,其中:

然而,就对数优势而言,边际效应与任何直觉都相去甚远。因此,我们可以通过取(6)的指数来根据几率求解模型:

然后,通常逻辑回归参数通过计算优势比以优势来解释,其中,使用(8),我们获得:

请注意,在二元变量的情况下, x 的分母值为 0,因此我们比较等于 1 和等于 0 的指标比率(即男性与女性的比率)。在二元或连续情况下,解释如下:*

*解释:**平均而言,x每增加一个单位,与将 y 发生的几率乘以{计算值}相关联

在我看来,对这些的解释并不总是像概率解释那样清晰,除非一个人经常接触和使用对数优势、优势和优势比。然而,(7-9)可以提供对 x 分别对对数优势、优势和优势比的边际效应的洞察。*

可选:非线性和相互作用

假设我们有以下两个信念: x 很可能与 y 有二次关系,我们相信效果会因性别*而不同。我们可以扩充我们的 logit 模型,以包括两个额外的工程特性,如下所示:**

其中,我们包含了 x 的平方项,并使用虚拟变量 x 对该个体是否为男性进行交互。因此,我们对边际效应的解释现在会稍微少一些细微差别。**

注意,无论何时我们包含一个交互项,我们都必须注意首先在模型中包含每个未交互的变量(也就是说,也单独包含虚拟的)。否则,相互作用项将吃掉性别对 y 的原始影响,而实际上相互作用项可能是多余的。

现在相对于 x 对(10)进行微分,我们得到:*

我们现在可以看到,*,由于非线性,边际效应将根据 x 的值以及该个体是男性还是女性而进一步变化。这可以让我们通过计算每个男性和女性的平均边际效应来计算男性和女性的平均边际效应。我们可以在根据概率求解(10)之后,类似地计算(9)中的概率比。这些例子将留给感兴趣的读者,我们到目前为止所涵盖的内容应该足以计算这些。

预测信用卡欺诈的边际效应

为了演示我们上面讨论的具体示例,我们将利用以下关于信用卡交易的 Kaggle 数据集,目的是建立一个模型来预测交易是否是欺诈性的。数据集在每个事务上都有以下变量:

  • 距离 _ 从 _ 家 (连续)
  • 距离 _ 从 _ 最后 _ 事务 (连续)
  • 比率 _ 对 _ 中值 _ 购买 _ 价格 (连续)
  • 【重复 _ 零售商(1/0) (二进制)
  • 【已用 _ 芯片(1/0)】(二进制)
  • 【used _ pin _ number】(1/0)(二进制)
  • 【在线 _ 订单】 (1/0) (二进制)
  • 目标: 欺诈*(1/0)*(二进制)

首先,让我们导入数据并生成汇总统计数据:

**import pandas as pd

# Import Data
fraud = pd.read_csv("card_transdata.csv")

# Summary Statistics
fraud.describe().round(3)**

从中我们可以获得以下汇总统计数据:

图 2

我们现在将使用 scikit-learn 构建一个逻辑回归模型。假设我们已经完成了训练和验证模型的适当步骤,并且已经确定了适当的模型。我们的最终模型如下:

**from sklearn.compose import ColumnTransformer 
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

# Build Model Pipeline
features = list(fraud.iloc[:,0:7].columns)

cont_feat = features[:3] # Continuous Features
bin_feat = features[3:] # Binary Features

standardize = ColumnTransformer([
    ('cont', StandardScaler(), cont_feat), # Standardize continuous features
    ('binary','passthrough',bin_feat)
    ])

pipeline = Pipeline([
    ('standardize',standardize),
    ('logit',LogisticRegression())
    ])

# Fit Pipeline
model = pipeline.fit(fraud[features],fraud['fraud'])

# Pull Final Model
final_mod = model._final_estimator**

我们建立了 logit 模型来预测信用卡交易是否是欺诈性的。现在,让我们转到解释模型参数,以了解模型的内部工作方式以及每个功能在推动预测方面的后续作用。我们将定义一个函数来计算逻辑回归在概率和赔率方面的边际效应:

**import numpy as np
import pandas as pd

def logit_margeff(model, X, features, kind='probability'):

    coef = model.coef_
    intercept = model.intercept_

    if kind == 'probability':

        logodds = intercept+np.dot(X,coef.T)

        marg_effects=[]
        for i in range(coef.size):
            marg_eff = np.mean(coef[0,i]*np.exp(-logodds)/(1+np.exp(-logodds))**2).round(3)
            marg_effects.append(marg_eff)

    elif kind == "odds":

        marg_effects=[]
        for i in range(coef.size):
            marg_eff = (np.exp(coef[0,i])).round(3)
            marg_effects.append(marg_eff)

    marginal_effects = {}
    marginal_effects['features'] = features
    marginal_effects[f'marginal_effects_{kind}'] = marg_effects

    df = pd.DataFrame(marginal_effects)

    return df**

注意,第 14 行是使用(5)计算的平均边际效应,第 21 行是使用(9)计算的优势比。

在我们定义了这个函数之后,我们要做的就是输入我们建立的 logit 模型和特征矩阵。让我们首先用概率来解释输出:

**logit_margeff(final_mod,fraud[features],features,kind='probability')**

其中我们有每个特征的边际效应的相应输出:

**回想一下,我们已经标准化了所有连续特征,因此一个单位的增加对应于一个标准差的增加。我们将解释一个连续特征、离家距离、和一个二元特征、已用个人识别码的估计平均边际效应。

解释(distance_from_home): 平均而言,交易发生地距离持卡人家庭住址的距离每增加一个标准差(65.391),交易欺诈的概率就会增加 2.4 个百分点。****

解释(used_pin_number): 平均而言,包括使用 pin 码的信用卡交易与交易欺诈的概率降低 32.3 个百分点****

现在,就几率而言:

******logit_margeff(final_mod,fraud[features],features,kind='odds')******

每个特征具有相应的边际效应输出:

以赔率表示的相同特征的解释如下:

解释(distance_from_home): 平均而言,交易发生地与持卡人家庭地址之间的距离每增加一个标准差(65.391),交易被欺诈的几率就会增加 2.7 倍。

解释(used_pin_number): 平均而言,包括使用 pin 号的信用卡交易与将交易欺诈的几率乘以 0 相关联。换句话说,平均来说,使用 pin 码几乎可以完美地预测交易可能是而不是欺诈。

讨论

我希望这篇文章有助于明确如何从 logit 模型参数中提取出 非常有意义的见解 。很明显,根据概率的边际效应解释提供了大量的直觉和在 logit 模型框架下预测机制的可解释性。一般来说,这些参数解释了模型如何进行预测,以及解释了目标和特征之间的关联。然而,在额外的识别假设下,我们可以做出更有力的声明,将模型参数解释为某些特征和目标之间的因果关系(我在之前的帖子中简要讨论了这一点)。我希望这篇文章增加了你对逻辑回归的了解和欣赏!

资料组

可在 Kaggle 上获得:信用卡欺诈 ( 许可证: CC0:公共领域)

相关职位

  • 控制“X”

感谢你阅读我的帖子!我在 Medium 上的帖子试图利用 计量经济学 统计/机器学习 技术来探索现实世界和理论应用。此外,我试图通过理论和模拟提供某些方法论的理论基础。最重要的是,我写作是为了学习!我希望把复杂的话题变得更容易理解。如果你喜欢这个帖子,请考虑 跟我上媒