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

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

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

神谕背后:格罗弗算法和振幅放大

原文:https://towardsdatascience.com/behind-oracles-grovers-algorithm-amplitude-amplification-46b928b46f1e

捕捉高层次的想法以及如何为 SAT 实例实施 oracle

特里·维里斯迪斯在 Unsplash 上的照片

在我开始量子计算之旅的时候,我对“甲骨文”是什么感到困惑。通常,您会读到这样的内容:

“(……)然后,多亏了神谕,你才能够找到解决办法。”

最后,我唯一明白的是:

“它能够(以某种方式)捕捉给定问题的解决方案”。

然而,我不知道怎么做。因此,出现了如下问题。

这个“甲骨文”长什么样?

如何识别我先验不知道的东西?

???。[ Gif via Giphy ]

这些都是合理的问题!我们会找出所有这些问题的答案!特别是:

  • 我们将理解为什么神谕是重要的。
  • 我们将抓住神谕背后的高层次直觉。
  • 我们将展示如何定义一个能够解决布尔可满足性问题(SAT)的预言
  • 此外,将提供 Qiskit 中的实现!

在开始之前,你只需要几个先决条件:

  • 叠加是什么。
  • H,X,CX 这样的量子门是什么?
  • 什么是量子电路。

本文的目标是提供 Grover 算法的自包含表示。我们将尽可能避免数学细节,从而提供这种量子算法能力的实际想法。

开始吧![ Gif via Giphy ]

为什么这么重要?

“神谕”是格罗弗算法的一部分:最具破坏性的量子算法之一,也是量子计算吸引大量兴趣的原因之一。

格罗弗算法的实力如何?

假设我们需要在一组非结构化的 N 元素中找到一个特定的目标元素。

经典计算中,由于我们没有关于这个目标元素位置的先验知识,我们需要查看每一个元素。

例如,假设在一个未排序的数组中寻找数字 3(图 1)。

图一。寻找数字 3。[作者 Gif

代价是O(N)(即在最坏的情况下,我们需要扫描所有的 N 元素)。

量子 计算中,得益于 Grover 算法,可以在 O (√N) 中检索到解。相对于经典计算,我们实现了二次加速(图 2)!

图二。二次 Vs 线性加速。[图片由作者提供]

一点背景

通常,量子计算机必须运行给定的量子算法不止一次。有时它们会返回正确的输出,有时则不会。

因此,我们的目标是增加(或"放大")获得正确输出的机会(图 3)。

重点是:

我们想要量子计算机输出的概率分布,使得在算法的给定运行中获得概率比获得无效 输出

..因为得到错误输出的概率是非零的,所以可能需要更多的运行。

图三。概率分布。[图片由作者提供]

格罗弗的积木

格罗弗的算法由两部分组成:一个甲骨文和一个扩散器

甲骨文

  • Oracle “标记”解决方案(图 4)。

图 4。甲骨文标记解决方案。[图片由作者提供]

感谢 oracle,我们能够在非结构化数据集的所有 N 个元素中标记出正确的元素。(不要爪子!我将很快告诉你神谕是什么样子的 ❤)

然而,仅有神谕是不够的。

其实神谕只是标记了正确的元素,并没有增加得到这个元素作为量子算法输出的概率。事实上,单独的神谕是没有用的,因为得到这个元素的概率是 1/N(也就是随机的!).

视觉示例

假设我们有 N=7 个元素,我们寻找元素“三角形矩形”(即我们的解)。于是,我们套用了标注“三角形长方形”的甲骨文。

但是得到三角形矩形的概率是 1/7 (图 5),和得到其他所有元素中的一个是一样的!😦

图 5。甲骨文不影响得到解的概率。[图片由作者提供]

扩散器来救援了!其实是能够增加得到三角形长方形的几率的!

扩散器

  • 扩散器“放大”溶液(图 6)。

图六。扩散器放大溶液。[图片由作者提供]

为什么扩散工程,是超出了本文的范围。对于那些好奇的人,我会在本文的最后给你提供一些参考。

关键是它增加了 oracle 将元素标记为输出的机会(图 7)。

图 7。扩散器增加了将溶液作为输出的机会。[图片由作者提供]

但是,不要慌!每个 oracle 的扩散器实现都是相同的,我将在最后提供代码!保证!

我们可以将扩散器放在一边,然后回答这个问题:

“神谕长什么样”?

是我![ Gif via Giphy ]

神谕背后

我发现“甲骨文”这个名字有点误导。似乎有人只要问一句:“解决方案是什么?”就能给你解决方案。

我更喜欢在脑海中描绘一个滤镜的形象!您可以手工制作一个过滤器,它具有您所寻找的元素的精确形状(图 8)!

图 8。甲骨文作为过滤器。[图片由作者提供]

为什么是过滤器?

嗯,在运行 Grover 的算法之前,在某种意义上,你拥有所有的元素(我将在后面解释 如何 ,但这相当简单),你想过滤掉那些不是你的解的元素。

但是,请记住,你并且只有你,仔细定义这个过滤器。

现在,可能会出现一个问题

我必须设计一个能够捕捉解决方案并拒绝所有其他元素的过滤器。但是为了能找到答案,我需要知道答案,对吗?

因此,这意味着我已经知道了解决方案。所以…

这个算法有什么意义?

这是一个合理的担忧!

后退一步

格罗弗算法的原名是“一种用于数据库搜索的快速量子力学算法”因此,我找到的例子是在一组数字中寻找一个数字。

如果你想在一个数据集中找到数字 3,你已经事先知道了答案。您正在定义一个 oracle,它捕捉到数字 3,输出是 3。一开始没什么太激动人心的,对吧?

【小注 1】在 Grover 算法中,你需要先验地知道一个解的存在(实际上,你需要知道解的确切个数)。

【小注 2】还有其他的量子算法,找出给定问题的解的个数。然后,你可以使用格罗弗的算法。

向前两步

转折点是神谕可以是一个函数,而不仅仅是一个数字!在这种情况下,我们指的是幅度放大算法,我们可以将其想象为广义的 Grover 算法。

你对“神谕可以是一种功能”有什么打算?

比如我们可以问我们的量子计算机:“那个大于 5 小于 6 的元素是什么?

x > 5 x < 6

精神上的一步很小,但重点是我们不需要知道解决方案,这将是你的神谕!因此,获得了相对于经典强力算法的二次加速!现在是时候停止空谈,开始行动了。

我们走吧![ Gif via Giphy ]

用二次加速法求解 SAT

SAT 问题在于找到一个变量赋值,使得它满足一个给定的布尔公式。(我回忆你 SAT 属于 NP-complete 类,一个很有意思的问题!)

例如在(x1 x2)中,满足布尔公式的赋值为:

x1 =假,x2 =真

特别地,我们关注布尔公式的一种特殊形式,即合取范式 ( CNF )或子句范式。

CNF 刷新

CNF 由一个或多个从句的连接词组成。

  • 每个子句包含一个或多个文字(布尔变量)。
  • 一个 CNF 只包含运算符:(not) ,∨ (or), (and)。
  • 子句的连接词通过 运算符获得。
  • 每个子句的文字由操作符关联
  • 运算符只能用作文字的一部分。

CNF 的示例:

(x ∨ y) ∧ y

解决方案分配是:x =真,y =假

我们将定义解决上述 CNF 的量子电路。

在开始之前,让我们根据并使用德摩根规则重写上面的 CNF 实例(图 9)。 为什么? 只是因为后面会更容易描述神谕:)

图九。德摩根规则。[图片由作者提供]

于是,我们将 (x ∨ y) ∧ y 改写为 ( x ∧ y) ∧ y

具体来说,我们将定义一个甲骨文,它标志着:的解决方案

( x ∧ y) ∧ y

为了简单起见,让我们假设我们已经知道上面的实例有一个单一的解决方案。在文章的最后,我会解释为什么我们会做出这样的假设。

量子电路

  • 1)为布尔公式生成所有可能的赋值: ( x ∧ y) ∧ y
  • 2)应用神谕。
  • 3)应用扩散器。
  • 4)进行测量。

通常,我们需要根据以下公式多次重复步骤 2)和 3):

其中 n 是变量的个数。

在我们的例子中, n=2 (即 xy ) 因此,重复次数为 1 。也就是说,我们只应用一次神谕和扩散器。

如果你想进一步了解这个公式是如何得到的,我会在文章底部给你留一些参考资料。

步骤 1:为布尔公式生成所有可能的赋值

我们通过哈达玛门(图 10)将所有的量子位叠加起来!也就是说,我们为布尔公式生成所有可能的赋值。

图 10。哈达玛城门。[图片由作者提供]

既然我们知道一个解存在,,那么我们的解就在我们通过将所有量子位进行相等叠加而生成的赋值(图 11)内。

图 11。所有可能作业的叠加。[图片由作者提供]

步骤 2:应用 Oracle

我想强调的是,在这个例子中,我们不知道像例子“寻找数字 3”中的解决方案。

一般来说,通过定义 SAT 实例,我们只是定义了输入必须满足的条件作为我们的解决方案(即,您的oracle/您的函数!)

遵循 oracle 电路(图 12)。

图 12。甲骨文电路。[图片由作者提供]

不要慌!我们将详细分析❤的整个电路

细节

我们在甲骨文电路中分别观察到 3 个额外的量子位:

  • 2 个工作量子位 w.
  • 一个量子位检测器。

工作量子位

我们添加了和 CNF 实例的子句数量一样多的工作量子位。工作量子位的范围是临时存储给定子句的输出。

在我们的例子中, ( x ∧ y) ∧ y ,我们有 2 个子句,那么需要 2 个工作量子位 w

检验量子位

量子位检验器的目的是标记正确的解。也就是说,当一个变量赋值满足 oracle 条件时,那么检查器被翻转到 1

条款

我们将 ( x ∧ y) ∧ y 分解成三个部分(图 13):

  • 第 1 条。 w0 = x ∧ y
  • 第 2 条。 w1 = y
  • 结果。 w0 ∧ w1

注意 w0 对应的是 x ∧ y 而不是 ( x ∧ y)

我们把第一个推迟到w0 ∧ w1

图十三。分解 ( x ∧ y) ∧ y. 【图片由作者提供】

第 1 条。w0 = x ∧ y

条款 1 检查条件 x ∧ y (图 14)。通过多控 X 门实现,其中:

  • xy 是控制量子位,
  • w0 是存储条件 x ∧ y. 结果的目标量子位

请注意,多控 X 门必须在 xy触发,为此,我们将前置并附加两个 X 门分别到 xy、,从而取消它们。

图 14。 X 门否定 X 和 y【图片由作者提供】

第二条。 w1 = y

条款 2 检查条件 y (图 15)。通过受控 X 门实现,其中:

  • y 是控制量子位,
  • w1 是存储条件 y. 结果的目标量子位

如前所述,由于我们寻找子句 y ,因此我们否定受控 X 门中的量子位 y

图 15。 X 门否定 y【图片由作者提供】

结果 w0 ∧ w1

结果检查与我们的 CNF 实例 ( x ∧ y) ∧ y 相对应的条件 w0 ∧ w1 (图 16)。条件 w0 ∧ w1 通过多控 X 门实现,其中:

  • w0w1 是控制量子位,
  • checker 是当 w0 ∧ w1 满足时翻转到 1 的目标量子位。

我们回忆一下 w0 对应的是 x ∧ y 而不是 ( x ∧ y)。

因此,我们需要对 w0 进行否定:(x ∧ y) = w0。

图 16。 X 门求反 w0。[图片由作者提供]

未计算

甲骨文的最后一步是释放工作量子位 w0w1 。这是通过执行不计算来实现的(图 17)。

为了不计算 w0w1 ,以相反的顺序应用实现条款 1 和条款 2 的门就足够了。

图 17。未计算 w0 和 w1。[图片由作者提供]

最终,我们手工制作了我们的神谕!魔力已经显现!

哒哒。[ Gif via Giphy ]

步骤 3:应用扩散器

最后,我们应用扩散器算子,它放大了正确的解(图 18)。

图 18。扩散器电路。[图片由作者提供]

第四步:测量

最终,我们测量量子位 xy (图 20)

图 20。测量。[图片由作者提供]

输出分布如图 21 所示。

图 21。输出分配。[图片由作者提供]

最高概率对应赋值 y=0(假),x=1(真)(即 01)。

特别是赋值 01 满足我们的 CNF 实例 ( x ∧ y) ∧ y!

哇哦。[ Gif via Giphy ]

结论

本文的主要目的是给出一个 oracles 的自包含演示,以及如何使用 Qiskit 实现它们。特别是:

  • 我们理解了神谕是什么。
  • 扩散器的用途。
  • 如何在 Qiskit 中有效地实现一个 Oracle 和一个扩散器?

在下面的参考资料中,您可以找到我的带有 Qiskit 实现的 Github 库,以便您可以使用它:)

啊!在给出的例子中,解的数量正好是 1。然而,对于一个给定的问题,我们可以有多个解决方案!在这些情况下,我们需要对计算 Oracle-Diffuser 对的重复次数的公式做一点小小的修改。特别是,公式变成:

在 Github 资源库中,您还会发现一个 CNF 实例的例子,其中有多个解决方案可供使用。🎉

结束语

我故意避开技术细节,让你掌握大意💡。

当我开始研究量子计算时,我会很欣赏这种概述。这就是我决定写这篇❤.文章的原因

我真的希望你也欣赏它!万一,请随意留下掌声或评论。任何一种反馈都会超级重视!

让我们在 LinkedIn 上保持联系吧!

参考

拜拜! [ Gif via Giphy

研磨咖啡时吼叫

原文:https://towardsdatascience.com/bellowing-during-coffee-grinding-328e097be5b4

咖啡数据科学

因果

我没有在我的其他磨床上使用风箱,我一直在犹豫是否要一个。然而,我买了一个新的研磨机( SPK-38G ),它带有一个风箱。用了一个星期后,我开始想如果你在研磨时使用风箱会发生什么?

如果你在地面上不停地吼叫会发生什么?从理论上讲,可以进入毛刺之间的细小颗粒会被更快地推出,从而减少了再次研磨的机会。

所有图片由作者提供

我用两个镜头做了一个测试,我观察了粒子分布、提取率和味道。

对于粒子分布,吼叫可以大大减少微粒的数量。对于 200 微米或更小的颗粒,这是 8%的差异。

设备/技术

浓缩咖啡机 : 像样的浓缩咖啡机

咖啡研磨机 : SPK-38G

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

镜头准备:断奏夯实

预灌注:长,约 25 秒

输液:压力脉动

过滤篮 : 7g/20g VST

其他设备: Atago TDS 计Acaia Pyxis 秤

绩效指标

我使用了两组指标来评估技术之间的差异:最终得分和咖啡萃取。

最终得分 是评分卡上 7 个指标(辛辣、浓郁、糖浆、甜味、酸味、苦味和回味)的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。

两杯

两张照片在视觉上有相似的发展。

就流量和水温而言,它们有点不同。

就味道和提取而言,连续吼叫更好。

一个区别是波纹管注射覆盖过滤器(TCF)的时间更快,但注入时间更长。我希望更粗糙的研磨能让镜头整体跑得更快,但这两个时间度量之间的差异让我很感兴趣。

在整个镜头中吼叫对于每一个镜头来说都有点不切实际,但是可以让一个研磨机不断地推动空气通过研磨机来自动获得相同的效果。我不知道这会如何影响磨矿分布的单峰性,但它无疑指出了减少细粒的技术进步。

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

我的进一步阅读:

我未来的书

我的链接

浓缩咖啡系列文章

工作和学校故事集

用决策曲线来衡量你的人工智能对现实世界的影响

原文:https://towardsdatascience.com/benchmark-your-ais-real-world-impact-with-decision-curves-7d369a8a6832

图 1:用人工智能做决策。图片作者。

行动是有后果的。人工智能的进步让我们在数据驱动的预测方面表现出色。但是没有行动的预测有什么用呢?人工智能以预测为中心的焦点为现实世界的影响和后果创造了一个盲点。但是有了做决定的权力,就有了权衡自己行为后果的责任。这就是决策曲线分析[1–4]发挥作用的地方。

决策曲线分析是一种图形方法,最初是为病人和医生设计的[1]。它将医学干预的好处和坏处——后果——与医学测试的不确定性——预测联系起来。决策曲线分析背后的关键创新在于,我们不必详尽地量化所有后果。我们需要问的是:在多大的可能性下,你会选择干预而不是不干预?**

例子

图 2:提供抵押贷款的决策树。借款人偿还贷款的概率为 p,。图片作者。

决策曲线分析并不局限于医学领域。它适用于我们基于二元预测任务采取行动的任何情况。例如,考虑一家提供抵押贷款的银行。为了实现利润最大化,银行不会向所有人发放抵押贷款。相反,它估计违约的可能性(预测),以决定客户是否会获得抵押贷款(行动)。如果银行太严格,它会错过有利可图的客户。而过于宽容的银行家会招致更多的违约。使用如图 2 所示的决策树,我们可以遍历所有场景。通过估计每项成本,我们可以确定最佳策略。

在许多情况下,很难确定每种情况的利弊,或者成本很高。例如,当 CT 扫描本身可以在健康个体中导致癌症时,我们应该如何权衡癌症筛查的阳性检测?此外,每种情况的相对优点都是主观的:一个脆弱的老人可能会权衡癌症检测的后果,这不同于一个有年幼孩子的精力充沛的母亲。

让我们通过绘制你的第一条净收益曲线来应用这些概念!

如何用 Python 绘制决策曲线

想象一下,你在银行工作。你的目标是预测哪些客户会偿还抵押贷款,哪些会违约。我们给出了 20 个带有两个特征的训练例子:收入和年龄。

****from** matplotlib **import** pyplot as plt
**from** sklearn.datasets **import** make_blobscenters = [[0, 0], [1, 1]]
X_train, y_train = make_blobs(
    centers=centers, cluster_std=1, n_samples=20, random_state=5
)
X_test, y_test = make_blobs(
    centers=centers, cluster_std=1, n_samples=20, random_state=1005
)**

查看数据(图 3),我们发现我们无法用一条直线完美地将十字和圆圈分开。我们的预测器可能不错,但并不完美。

图 3:训练数据来预测将拖欠抵押贷款的客户。阳性标签用红叉表示,阴性标签用蓝圈表示。图片作者。

为了展示模型在优势上的差异,让我们训练两个模型。作为简单基线的逻辑回归模型和梯度增强树模型。

****from** sklearn.ensemble **import** GradientBoostingClassifier
**from** sklearn.linear_model **import** LogisticRegressionbaseline_model = LogisticRegression(random_state=5)
baseline_model.fit(X_train, y_train)
tree_model = GradientBoostingClassifier(random_state=5)
tree_model.fit(X_train, y_train)**

比较这两个模型,我们发现梯度增强树在测试集(90 %的准确度)上比逻辑回归模型(85 %的准确度)做得稍好。

接下来,我们使用 statkit 包中的[NetBenefitDisplay](https://hylkedonker.gitlab.io/statkit/decision.html#statkit.decision.NetBenefitDisplay)类来绘制净收益图。(要在 Linux 上安装 statkit,运行pip3 install statkit)。类似于 sci-kit learn,它有一个from_predictions方法来实例化来自地面真相标签(y_test)和预测的正类概率的情节。

****from** statkit.decision **import** NetBenefitDisplayy_pred_base = baseline_model.predict_proba(X_test)[:, 1]
y_pred_tree = tree_model.predict_proba(X_test)[:, 1]NetBenefitDisplay.from_predictions(y_test, y_pred_base, name='Baseline model')
NetBenefitDisplay.from_predictions(y_test, y_pred_tree, name='Gradient boosted trees', show_references=False, ax=plt.gca())**

这是一个相当忙碌的数字,所以让我们把它分成四个要点。

图 4:比较逻辑回归与梯度提升树模型和完美预测器(Oracle)的净收益曲线。为了比较,示出了对应于总是或从不行动/干预政策的净收益。图片作者。

****越高越好:排名比差距大小更重要【2】。由 Oracle 指示的线是可达到的最大净收益,对应于一个完美的预测值。现在看看蓝色逻辑回归和紫色梯度增强树曲线之间的排序。对于大多数可能的利益/危害偏好,梯度提升树优于逻辑回归模型。

****净收益应该是正的:理论上净收益可以是负的,意味着弊大于利。但是请注意永不行动政策(绿点虚线)的净收益为零。所以我们总是可以决定不行动(忽略模型)并获得至少零净收益。

****更精确的模型可能会逊色:对于银行来说,相对于付费客户的收益,房贷违约的危害很大。假设银行希望每十个客户中不超过一个违约。这对应于曲线的最右侧,概率阈值在 0.9 和 1 之间。记住梯度推进树比逻辑回归模型更准确。然而,看图 4,我们看到在这个范围内,逻辑回归实际上有更多的净效益。越不精确的模型对银行越有利可图!

****模型可能是无用的:在 0 到 0.1 的概率阈值范围内(图 4),逻辑回归模型的净收益类似于总是行动策略。因此,如果你的“金发女孩”偏好恰好在这个范围内,那么逻辑回归模型就没有现实世界的影响。因此,净收益曲线有助于澄清你的模型是否有实际价值,考虑到人工智能产生的大量技术债务,这一点很重要[5]。

理论

再次重申,决策曲线分析消除了详尽量化所有方案的优点的需要[1]。我们需要引出的只是一个偏好** π :为了一个真正的阳性(癌症检测、抵押贷款支付),我们愿意采取多少行动/干预(癌症筛查、抵押贷款)?这个概率阈值 π偏好 —就是我们放在决策曲线的 x 轴上的值。隐含地,preference π 将假阳性与假阴性的相对危害编成法典[1]。在医疗保健和金融等监管部门,这种偏好也可能受到法规遵从性和/或协议的限制。**

给定一个偏好 π ,我们可以计算出 m 个样本的净收益如下[1–4]:

净收益=真阳性/ m —假阳性/*mπ/(1-π*)。**

也就是说,从行动/干预中获利的个人减去从行动/干预中损失的个人乘以汇率【1–3】。汇率 π /(1- π )量化了我们如何评估从假阳性到真阳性的转换[2,3]。接下来是简洁的部分:通过追踪作为偏好 π 的函数的净收益,我们在所有可能的后果评估中评估模型的收益。结果的每个具体损益分配(如图 2 中的决策树)现在对应于 π 的值。单一图形中所有可能性的空间!

结论

当你使用 AI 做决定时,重要的是权衡其后果的好处和坏处。这就是决策曲线分析的用武之地。净收益曲线作为一座桥梁,将模型世界和(校准的)预测与行动和结果连接起来,无需详尽量化所有情景。

矛盾的是,更精确的模型在现实世界的影响方面可能是劣势的。而且有时候还不如完全没有 AI!决策曲线帮助您在部署到生产环境之前,而不是之后,识别并避免这些常见的陷阱。因此,我们认为决策曲线分析应该是每个数据科学家工作流程的一部分。

承认

感谢布拉姆·范·埃斯里克·胡伊泽和迪娜·波尔的校对。

参考

[1]:维克斯、安德鲁 j 和埃琳娜 b 埃尔金。决策曲线分析:评估预测模型的新方法医疗决策 26.6(2006):565–574。

[2]:维克斯、安德鲁·j、本·范·卡尔斯特和埃沃特·w·斯特耶伯格。解释决策曲线分析的简单、逐步指南诊断和预后研究3.1(2019):1–8。

[3]:维克斯、安德鲁·j、本·范·卡尔斯特和埃沃特·w·斯特耶伯格。评估预测模型、分子标记和诊断测试的净效益方法 bmj 352 (2016)。

[4]:鲁松、瓦伦丁和托马斯·宗布伦。重新审视决策曲线分析:总体净收益、与 ROC 曲线分析的关系以及在病例对照研究中的应用BMC 医学信息学和决策11.1(2011):1–9。

[5]:斯卡利、大卫等.机器学习系统中隐藏的技术债务神经信息处理系统进展 28 (2015)。

基准测试 6 种基于 AutoML 的插补技术

原文:https://towardsdatascience.com/benchmarking-6-automl-based-imputation-techniques-3b1defc0d25b

插补策略基本指南

图片来自皮克斯拜威利·海德尔巴赫

现实世界中的数据集通常包含大量缺失值,这可能是由于数据损坏或记录数据失败造成的。数据中缺失值的存在妨碍了训练稳健的机器学习模型。大多数机器学习算法不支持缺失值,因此数据科学家需要在特征工程管道中明确处理缺失值。

有各种技术来处理或估算缺失值。在我以前的一篇文章中,我有 7 种处理缺失值的技术。

</7-ways-to-handle-missing-values-in-machine-learning-1a6326adf79e>

Scikit-learn、Verstack、Impyute 是各种开源包,提供了在几行 Python 代码中估算缺失值的实现。这些软件包实现了各种插补算法,包括 KNN 插补、随机森林插补、迭代插补等。

在本文中,我们将讨论和基准的各种插补算法的性能指标。

开始使用:

在开始实施插补算法之前,让我们准备一个自定义数据集,并用缺失值替换一些值。样本数据集有 28 个特征,其中 5 个特征的 25%的值为 NaNs (12,500 个数据值)。我们保留了原始数据集的副本(具有 NaNs 的实际值),以比较每种插补策略的性能。

请在我的 GitHub gist 中找到实用函数来计算平均绝对误差并生成误差图。

1)简单估算器:

简单估算法可被视为一种基本或最简单的估算技术,其中缺失值由平均值、中间值、最频繁值或常数值替代。Scikit-learn 包提供了简单估算器的实现。

(作者代码),简单估算器的实现

使用简单估算器的实际值和预测估算值之间的平均绝对误差***0.01369***

(图片由作者提供),简单估算器的误差分布图

2)迭代估算器:

迭代输入是一种输入缺失值的策略,通过循环方式将每个具有缺失值的要素建模为其他要素的函数。Scikit-learn 还提供了迭代估算器的实现。

默认情况下,迭代估算器使用一个可配置的 BayesianRidge 估算器。

(作者代码),迭代估算器的实现

使用简单估算器的实际值和预测估算值之间的平均绝对误差***0.01359***

(图片由作者提供),迭代估算的误差分布图

3)KNN-估算者:

KNN 估算器使用在训练集中找到的**n_neighbors**最近邻的平均值估算每个缺失值。它假设两个样本是接近的,如果两个样本都不缺少的特征是接近的。

(作者代码),KNN 估算器的实现

使用简单估算器的实际值和预测估算值之间的平均绝对误差***0.00867***

(图片由作者提供),KNN-估算器的误差分布图

4)ver stack—nan inputr:

nan import使用 xgboost 模型对熊猫数据框中所有缺失值进行估算。xgboost 模型经过多重处理训练,因此估算值相对较快。

使用 NaN Imputer,您可以使用 XGBoost 回归器/分类器更新数值、二进制、分类的缺失值。这个基于 XGBoost 的 NaNImputer 可以使用 verstack 包在一行 Python 代码中实现。

(作者代码),ver stack NaN-inputr 的实现

实际值和使用小鼠估算器预测的估算值之间的平均绝对误差***0.00903***

(图片由作者提供),ver stack NaN-inputer 的误差分布图

5)小白鼠:

链式方程多变量插补(小鼠)是一种插补缺失值的迭代方法。它假设数据是随机丢失的,并通过查看其他样本值对其真实值进行有根据的猜测。Impyute 包提供了鼠标的实现。

(作者代码),MICE 的实现

实际值和使用鼠标估算器预测的估算值之间的 mean_absolute_error***0.01371***

(图片由作者提供),老鼠的误差分布图

基准测试:

(图片由作者提供),上述插补技术的基准平均绝对误差

从上表中,我们可以得出结论,KNN 估算器(Scikit-learn)和南估算器(verstack)在估算缺失数据值方面表现最佳,性能提高了 55%到 60%。

此外,KNN 估算器和南估算器的误差图相对优于其他误差图,大多数误差等于或接近于 0。

结论:

在本文中,我们讨论了使用各种开源软件包的 API 函数来估算缺失值的 5 种方法或技术。在这 5 种技术中,scikit-learn 中实现的 KNN 估算器表现最佳,与使用均值策略估算缺失数据的基线简单估算器相比,性能提高了 x%。

此外,verstack 包中实现的 nan inputr 函数对该数据的执行效果不太好,但它可以估算值,而不考虑要素的数据类型(数值、二进制、分类)。

上面的基准测试数据是针对一个小的数据集样本生成的,但是很好地概述了各种技术的表现。

感谢您的阅读

用 timeit 对 Python 代码进行基准测试

原文:https://towardsdatascience.com/benchmarking-python-code-with-timeit-80827e131e48

作为 Python 代码时间基准测试最流行的工具,内置的 timeit 模块提供了比大多数 Python 爱好者所知道的更多的东西

timeit 模块是 Python 的秒表。照片由 Tsvetoslav HristovUnsplash 上拍摄

标杆管理很少是为了好玩而做的,即使它确实很有趣。除了这种乐趣之外,它还可以帮助您:

  • 理解 Python 行为;你可以学习什么更快,什么更慢,这反过来可以帮助你理解语言;
  • 优化您的代码。

如果您认为您花了太多时间对一些随机代码片段进行基准测试,不要担心。我去过那里。老实说,我仍然经常这样做。不要为此感到羞耻:基准测试有助于您理解语言的复杂性。随着时间的推移,你会注意到你可以猜测一个特定的片段应该有多快或多慢。然而,时不时地,甚至你的“标杆鼻子”也会误导你,所以标杆通常是值得花时间的。编程毕竟很有趣,不是吗?

就执行时间而言,Python 中最流行的代码片段基准测试模块可能是[timeit](https://docs.python.org/3/library/timeit.html)模块。Python 还提供了其他与时间相关的基准测试工具,但是timeit绝对应该是您的第一步,原因如下:

  • 这是 Python 中与时间相关的基准测试工具中最流行的一个;
  • 它是标准库的一部分,不用安装;和
  • 其他工具通常是timeit的包装。

因此,我认为如果你想使用这些工具,你应该首先学会如何使用timeit并解释它的结果。这篇文章旨在帮助你。我将向您展示这个模块的一个鲜为人知的特性:基准函数而不是代码片段。我还会告诉你timeit结果可能会误导人的情况。

使用 timeit

大多数用户不知道timeit模块提供了两个 API,这就是为什么你会发现主要使用其中的一个。这两个 API 如下:

  • 基于代码片段的 API 。大多数人提到timeit都会想到这个 API。它有两个优点:相对容易使用,而且几乎可以对任何东西进行基准测试,因为它对代码片段进行基准测试。
  • 基于可调用的 API 。大多数人实际上并不知道这个 API。它旨在为可赎回债券设定基准。不幸的是,它的语法不如基于代码片段的 API 自然,但在某些情况下还是很有用的。我将在本文后面展示这种情况。

这两个 API 使用相同的函数,但有所不同。你应该知道的两个函数是timeit.timeit()timeit.repeat()。事实是,我几乎总是使用后者,因为它只是简单地运行几次前者,并提供timeit.timeit()的单独结果,所以你会得到更稳定的结果。我建议你也这样做。为此,我将讨论timeit.repeat()函数;timeit.timeit()的 API 非常相似,只有一点不同:缺少repeat参数。

最简单的用法如下:

就是这样!它将测量以字符串形式提供的代码片段的执行时间。在我们的例子中,它是[_ for _ in range(10)],这意味着我们将测量 Python 使用列表理解一百万次来创建一个列表所用的时间。这一百万次是number参数的默认值。记住该命令的每百万次调用都是在同一个会话中一个接一个、进行的;正如我稍后将展示的,这在某些情况下可能会很棘手。**

让我们来分析函数的签名:

  • stmt是您想要进行基准测试的代码片段,以字符串形式提供;
  • number是在一个会话中stmt被调用的次数;,以整数形式提供;默认为1_000_000
  • setup是运行stmtnumber之前要运行的代码,以字符串形式提供;每个会话只运行一次,在会话开始时;
  • timer是使用的定时器;默认的是perf_counter(),由于它目前被认为是最好的内置定时器,大多数情况下最好不要碰它;
  • repeat是要运行的会话数,每个会话由对stmtnumber调用组成;你会得到所有这些会议的结果;作为整数提供,默认为 5;
  • globals是要提供的全局字典;可以代替setup使用,也可以随同使用。

对于小而快的代码片段,没有必要改变numberrepeat,除非您希望您的基准提供非常稳定的结果。如果这样做,您应该增加这些数字,这取决于您希望结果有多稳定,以及您希望基准运行多长时间。

然而,对于耗时较长的代码片段,您可能希望减少numberrepeat,或者两者都减少,否则基准测试可能会花费太多时间。但是,您会注意到,这样做之后,也就是说,如果numberrepeat的值太小,结果可能会变得不稳定。

上面,我们使用了基于片段的 API。是时候讨论一下鲜为人知的基于可调用的 API 了:

您可能认为上面两个对timeit.repeat()的调用应该提供相似的结果,因为它们测试的是同一件事,即使用长度为 10 的range对象的列表理解创建一个列表。但这不是真的:第一个确实以这种方式创建了一个列表,但后者不是,或者说不仅仅是。这是因为后者还包括运行make_list()函数的开销,有时这种开销会相当大。实际上,我们可以这样分析:

上述调用中的第一个相当于timeit.repeat(make_list),但是它使用与第二个相同的 API。因此,如果我们看到t1t2之间的差异,那将是由于调用一个函数一百万次的开销。

在我的机器上(32 GB,四个物理和八个逻辑内核,在 WSL 1 中运行),我得到了以下结果:

差别相当小,不是吗?考虑到这个基准测试用了这么短的时间,我们能信任它吗?让我们用number=10_000_000repeat=10重新运行基准测试,以防万一。再次,best(t1)更大,与4.12923.7315best(t2)。然而,你必须记住,这些基准测试并没有那么长…它们总共用了 78 秒,下一次我运行它们时,我得到了3.98463.8373的结果。因此,如果您想确定您的基准,请使用更大的numberrepeat值。但是,当两个(或更多)代码段的执行时间差异很大时,您不必使用大的值。

至于两个 API 的对比,记住:

这两个 API——基于代码片段的和基于可调用的——即使在对做同样事情的代码进行基准测试时,也会产生不同的结果。这是因为基于可调用 API 的结果还包括调用可调用 API 的开销时间。

选哪个?看情况吧。如果您想比较两个函数的执行时间,这正是创建基于可调用 API 的目的。或者,当你在代码中对一个函数做了一些事情(例如,分配一个列表,如上)时,那么基于可调用的 API 将更好地反映实际情况。尽管如此,如果你只是想比较两个片段,没有必要为此使用函数。但是,请记住,关于范围。当您将代码封装在一个函数中时,所有这些都将在这个函数的局部作用域和命名空间中完成。正如我稍后将展示的那样,这可能会有所不同。

示例:创建空字典

现在,假设我们想要对创建空字典的两种方法进行基准测试:{}dict()。我们可以用下面的方法来做:

你的猜测是什么?

如果你投票支持字面意思更快,那你答对了。在我的机器上,best(literal)给了0.0183,而best(func)给了0.0600,所以差别很大。我们可以分析这两种方法的字节码,看看这种性能差异来自哪里:

如您所见,dict()使用了另外一个字节码操作,在这个操作中它调用了一个dict()函数;文字可能更快,因为它不需要这样做。如果你看到了与前一个例子的相似之处,你就对了;我们看到了调用dict()函数的开销。

有趣的是,如果你创建自己的函数如下:

你会发现它比dict()功能本身更快。我让你检查这作为一个练习。(这就是我上面跟你说的对标的乐趣!)

为什么是最小值?

您可能想知道为什么我使用最小值而不是平均值来比较两个基准测试结果(参见上面的best()函数)。为什么不把均值作为主要的趋势度量,而把方差(或标准差,或其他不同的度量)作为变异的度量?

这是因为标杆管理不是一种正常情况,所以我们不应该应用典型的统计方法来分析其结果。在最好的情况下,相同代码(不包括随机性)的所有运行应该花费相同的时间。但是,在基准测试期间,操作系统会执行许多不同的任务,因此后续的基准测试会花费不同的时间。请记住,这不是因为代码本身,而是因为操作系统的进程。

因此,我们应该取最小值,因为它最接近实际执行时间,没有任何中断。同样的,我们不应该注意运行结果的变化。它们不度量这个特定代码片段的实际执行时间的可变性;其实应该是几乎没有变异的。相反,这种变化度量的是操作系统运行的其他进程的变化程度;那么,这些过程对基准测试结果的影响有多大呢?这就是为什么分析基准与分析其他类型的数据如此不同,这也是为什么最小值是比任何集中趋势度量更好的表示基准结果的度量。

另一个例子

让我们考虑一个更复杂的场景。这一次,我们将使用setup来设置环境。

同样,在运行代码之前,分析它并尝试猜测哪个函数应该更快。(这次没那么简单,因为你需要知道array模块是如何工作的。)

请注意,您可以稍微简化一下上面的代码。这种简化是有意义的,尤其是当您有更多片段要比较时:

在这里,foo_array()foo_list()几乎慢了五倍,差别很大。

之前,我答应过你展示一个基于调用的 API 比基于代码片段的 API 更有意义的例子。这是这样一种情况。注意,这次使用函数看起来比使用代码片段更自然,因为上面的代码片段只是调用函数。

这些函数带有一个参数,这使得使用基于可调用的 API 变得有点棘手。我们需要使用lambda,这是这个 API 的一个小缺点:

我们不需要使用setup,因为我们导入了array模块。当我们使用基于可调用的 API 时,我们在当前环境中运行基准,除非你通过改变timeit.repeat()globals参数来影响它。因此,如果环境中有大量大对象,基准测试实际上可能会比几乎空无一物的环境中的性能更差。

你应该知道timeit.repeat()返回的值是什么。列表的长度等于number,每个值代表运行代码片段/callable 的number调用的总时间,以秒为单位。您可以计算运行代码段的平均时间;例如,sum(results["array"]) / (number*repeat)将给出调用foo_array()函数的平均次数,以秒为单位。

小心!

你要记住我之前提到的:timeit函数在同一个会话中一个接一个地运行同一个命令。这在处理可变对象时尤其重要,但不仅限于此。

让我们考虑一个例子。假设您想要将一个生成器表达式与相应的列表进行比较。比方说,您将在一个for循环中使用这两个项目,但是什么也不做——这样,您就可以比较使用这两种对象的开销。举个例子,

我们得到了生成器表达式的0.016和列表的0.669。这是一个惊人的结果!创建一个包含 100 个元素的列表比一个生成器表达式慢 42 倍?!

或者……是吗?

每当你看到如此疯狂的结果,请仔细检查代码。我并不是说这样的结果永远不会发生;而是您应该确保代码是正确的。

您发现代码中的问题了吗?如果没有,再分析一次。它有毛病。

问题是由使用发电机引起的。发电机只能用一次,然后就空了。所以,在这段代码中,它只迭代一次,然后就不再迭代了,原因很简单,因为它是空的。然而,该列表每次都会被迭代。

这就是为什么相应的代码片段花费这么多时间:在第一次调用期间,for循环只在x_gen上循环一次,然后由于x_gen为空,所以什么也不做。但是,对于列表,每次调用代码片段时,它都会循环遍历列表。因此有所不同。

我们可以用一种简单的方法来解决它,使用timeit.repeat():我们可以使用number=1和一个很大的repeat值。这样,每个重复(会话)将实际上迭代生成器表达式,因为它将在每个后续会话中重新创建。

我将结果乘以repeat,因为否则它们会非常小,代表迭代一个for循环的时间。

现在我们用15.400表示生成器表达式,用0.900表示列表。这一次——当我们使用正确的方法时——列表比生成器表达式快 17 倍。

当您操作一个可变对象时,类似的情况也会发生:如果它在每次调用中都受到影响,那么下一次调用将使用这个对象的更新版本,而不是原始版本。因此,每个调用使用不同的对象。一个例子可以是基准测试append如何为列表工作。每次你添加一个条目到一个列表中,列表都会变长,所以后面的添加是不可比较的。玩这个来看看它是如何工作的,纯粹是为了基准测试的乐趣。

替代品

Python 提供了各种时间基准替代方案。我想指出的其中三点是

  • [cProfile](https://docs.python.org/3/library/profile.html),内置 Python profiler 对于任何重视性能的人来说,这是一个非常有用的工具;
  • [perftester](https://github.com/nyggus/perftester),在执行时间和内存使用方面进行性能测试的包,它还提供了基准测试和分析工具;和
  • [ycecream](https://github.com/salabim/ycecream),Python 代码甜蜜调试和基准测试的包。

结论

timeit模块可能是测试代码最简单的方法。这是一个内置的解决方案,其 API 相对容易使用。我绝对推荐它,但是考虑用timeit.repeat()函数代替timeit.timeit()

随着时间的推移,您会注意到,代码是否在每个细节上都进行了优化并不重要。例如,当代码持续 10 小时时,找到节省 10 毫秒的方法真的重要吗?有时这可能是有意义的,但是我们必须记住提高性能通常是有代价的,比如可读性更差的代码。

总结一下:

  • 在关键时刻优化性能。否则,努力提高代码的可读性。
  • 尝试在代码复杂性和性能之间找到正确的平衡。
  • 代码优化需要时间。为了每月节省一秒钟,花 10 个小时优化代码值得吗?通常,答案是“视情况而定”,但是永远记得问自己(和团队中的其他人)这个问题。
  • timeit模块提供了基准执行时间的内置方法。它使用起来非常简单,但是您应该将它视为基准测试工具,而不是分析工具。
  • 当你知道一个函数比另一个更快,可读性一样好,并且在代码中使用它不会花费更多的时间时,就使用它。在这种情况下,你究竟为什么要使用较慢的函数/方法呢?
  • 如果你想学习各种错综复杂的 Python,timeit可以帮助很大。结合其他工具,它可以帮助您理解 Python 是如何工作的。
  • 如果你想了解更多关于 Python 中的代码优化、概要分析和相关主题,Gorelick 和 Ozsvald 的书(2020)是你的朋友。

感谢阅读。我希望你喜欢这篇文章和timeit模块。如果您这样做了,请注意这并不是故事的结尾:在后面的文章中,我将讨论其他基准测试工具。

资源

用最简单的方法对 Python 函数进行基准测试:perftester

原文:https://towardsdatascience.com/benchmarking-python-functions-the-easy-way-perftester-77f75596bc81

PYTHON 编程

您可以使用 perftester 以最简单的方式测试 Python 函数

凯文·Ku 在 Unsplash 上的照片

最近,我描述了如何用[timeit](https://docs.python.org/3/library/timeit.html)模块进行时间基准测试。我解释了timeit构成了基准时间的基本 Python 方法,并向您承诺展示更多。这篇文章是我信守这个承诺的第一步。

我描述了timeit提供的两种 API:基于片段的和基于可调用的 API。前者是众所周知的,但后者不是,可能是因为它不太自然,需要您使用一个lambda函数。在这里,我将探索 [perftester](https://github.com/nyggus/perftester) ,它允许基准测试调用,就像后面的 API 一样;然而,与它不同的是,它提供了一个简单且感觉自然的 API。

然而,perftester带来的不仅仅是时间基准测试——它使您能够根据执行时间内存使用量对可调用程序进行基准测试,但最重要的是——它是一个用于 Python 可调用程序性能测试的框架。

我们将从基准测试开始,一步一步地讨论这个丰富的产品。由于时间和内存这两种类型的基准测试非常不同,我们将在这里关注时间基准测试,而将内存消耗的基准测试放在一边;我们改天再讨论这个话题。然后我们将准备讨论作为测试框架的perftester——据我所知,这是第一个用于测试可调用程序性能的 Python 框架。

基本用法

上述文章表明timeit模块易于使用。虽然这是真的,perftester可以更容易。它的 API 使您能够编写简洁明了的 Python 函数和其他可调用函数的基准。为了分析这个 API,让我们使用一个特定的例子。

假设我们有一个任何类型的项目列表x。我们希望以这样一种方式扩展列表,即给定整数n,我们将每个元素n相乘若干次。我们不仅仅通过将列表相乘(x*n)来实现,而是希望保持列表的顺序。

因此,我们预期以下行为:

>>> extend([1, 4, 'a'], 2)
[1, 1, 4, 4, 'a', 'a']
>>> extend([1, 4, 'a'], 3)
[1, 1, 1, 4, 4, 4, 'a', 'a', 'a']
>>> extend([2, 2, 4, 1], 2)
[2, 2, 2, 2, 4, 4, 1, 1]
>>> extend([1, -1, 1, -1], 3)
[1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1]

这是该函数的一个版本:

# extender.py
def extend(x: list, n: int) -> list:
    """Extend x n number of times, keeping the original order.

    >>> extend([1, 4, 'a'], 2)
    [1, 1, 4, 4, 'a', 'a']
    >>> extend([1, 4, 'a'], 3)
    [1, 1, 1, 4, 4, 4, 'a', 'a', 'a']
    >>> extend([2, 2, 4, 1], 2)
    [2, 2, 2, 2, 4, 4, 1, 1]
    >>> extend([1, -1, 1, -1], 3)
    [1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1]
    """
    modified_x = []
    for x_i in x:
        for _ in range(n):
            modified_x.append(x_i)
    return modified_x

如您所见,我添加了一个带有 doctests 的 docstring。如果你想了解这个有用的测试框架,你可以阅读下面的走向数据科学文章:

要运行测试,使用下面的 shell 命令,它假设您将上面的文件保存为extender.py,并且您就在 shell 中的这个文件夹中。

$ python -m doctest extender.py

没有输出意味着所有的测试都通过了。

好了,我们准备好测试extend()函数了。让我们创建一个main.py Python 文件来运行基准测试,它与extender.py文件位于同一个文件夹中:

# main.py
import extender
import perftester

if __name__ == "__main__":
    t = perftester.time_benchmark(
        extender.extend,
        [1, 1, 4, 4, 'a', 'a'],
        3
        )
    print(t)

这将使用perftester.time_benchmark()参数的默认值,即Number=100_000Repeat=5。如果你想知道为什么会有人用大写字母作为参数名的第一个字母,你会在文章末尾的附录中找到解释。

上面的代码在我的机器上产生了以下结果:

我们不会关注实际结果,因为我们对它们并不特别感兴趣。在不同的机器上,我们会得到不同的结果。因此,perftester也提供了相对的结果,这些结果应该大致与机器无关。我们稍后将讨论这个问题。

然而,首先让我们仔细看看输出。可读性不太好吧?这就是为什么perftester为我们提供了一个很好的小解决方案,即pp()函数。它的名字代表漂亮的印刷,它的漂亮印刷基于两点:

  • 内置pprint模块的pprint()功能,
  • 现场包rounder中的signif_object()功能。

[rounder](https://github.com/nyggus/rounder)使你能够以一种非常简单的方式对任何 Python 对象中的数字进行舍入。如果你感兴趣,你可以阅读下面的文章:

我们来看看perftester.pp()用上面的字典做了什么。这是我们的main.py模块的代码:

import extender
import perftester

if __name__ == "__main__":
    t = perftester.time_benchmark(
        extender.extend,
        [1, 1, 4, 4, 'a', 'a'],
        3
        )
    perftester.pp(t)

这是输出结果:

很好,不是吗?我们只通过一个函数perftester.pp()获得了这个,所以你可能想记住它:perftester.pp就像 perftester pretty print 一样。

perftester.pp如在 perftester 漂亮的打印。

我们现在可以分析输出。这就是我们现在的情况:

  • minmeanmax:这是在所有运行(有repeat次运行)中运行一个函数的最小、平均和最大平均执行时间→所以这是函数执行一次的平均时间,不像timeit.timeit()timeit.repeat()都显示它们的整个执行时间。因此,perftester基准在实验之间是可比较的;timeit基准测试不是——至少在没有额外计算的情况下不是。在这三者中,我们最感兴趣的是min,因为在基准测试中,我们应该寻找最佳结果(您可以在中找到更多关于 [timeit](/benchmarking-python-code-with-timeit-80827e131e48) 基准测试的文章)。
  • 这是我们兴趣的另一个价值。相对基准测试是根据空函数(即什么都不做,只有pass)的执行时间来执行的。具有相同操作系统的机器之间的相对基准应该或多或少是一致的,但是它们在不同的操作系统之间不太可能是一致的(即使是在同一台机器上,根据我的实验)。
  • raw_timesraw_times_relative:这两个显示原始值,即基准函数在每次运行中的平均执行时间(我们有Repeat运行次数),和平均相对执行时间(所以,除以空函数的平均相对执行时间)。这些价值观很少引起我们的兴趣;不过,请看下面的例子。

perftester基准在实验之间具有可比性;timeit 基准不

在某些情况下,我们可能希望查看min原始结果:这是因为它提供了我们机器上基准函数的最小执行时间。我们肯定会对这个值感兴趣。然后我们也可以看看raw_times,因为它们显示了这个函数在我们的机器中有多快,所有的后台进程都在运行,所以是在真实的场景中。我们看到,对于我们使用的参数,我们的函数平均需要1.95e-06秒;因此,运行一百万次将花费几乎 2 秒钟。在最好的运行中,平均执行时间是1.84e-06,所以并没有少很多。从raw_times中我们可以看出,变化似乎并不大。

通常,我们会针对各种参数组合对函数进行基准测试,以了解函数在各种场景中的表现。我们将在下面比较两个函数时这样做。

我想你们中的许多人认为我在编写这个函数的时候可以做得更好…你是对的!这并不是我一生中编写的最好的函数。创建列表的for循环?当然,列表理解应该做得更好,在性能方面也是如此;点击此处查看更多信息:

那么,让我们改进一下extend()函数。但是因为我们想检查我们的更改是否提高了性能,我们将更改函数名,以便我们的extender模块有两个版本。下面是我们新函数extend_2()的代码:

# added to main.py

def extend_2(x: list, n: int) -> list:
    """Extend x n number of times, keeping the original order.

    >>> extend_2([1, 4, 'a'], 2)
    [1, 1, 4, 4, 'a', 'a']
    >>> extend_2([1, 4, 'a'], 3)
    [1, 1, 1, 4, 4, 4, 'a', 'a', 'a']
    >>> extend_2([2, 2, 4, 1], 2)
    [2, 2, 2, 2, 4, 4, 1, 1]
    >>> extend_2([1, -1, 1, -1], 3)
    [1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1]
    """
    return [x_i for x_i in x for _ in range(n)]

所有的doctest都通过了,所以函数按预期工作。这个功能明显比原来的extend()更短、更清晰,也更优雅,这很好。

因此,让我们对这两个函数进行基准测试。但是因为我们想要比较两个函数,我们不应该只使用一个参数组合,因为对于小的n,增益(如果有的话)可能与大的n不同。下面是main模块的代码:

# main.py
import extender
import perftester

from collections import namedtuple

Benchmarks = namedtuple("Benchmarks", "extend extend_2 better")

if __name__ == "__main__":
    orig_list = [1, 1, 4, 4, 'a', 'a']
    results = {}
    for n in (2, 5, 10, 100, 1000, 10_000):
        number = int(1_000_000 / n)
        t = perftester.time_benchmark(
            extender.extend,
            orig_list,
            n,
            Number=number
            )
        t_2 = perftester.time_benchmark(
            extender.extend_2,
            orig_list,
            n,
            Number=number
            )
        better = 'extend' if t['min'] < t_2['min'] else 'extend_2'
        nn = f"{n: 6}"
        results[nn] = Benchmarks(t['min'], t_2['min'], better)
    perftester.pp(results)

这是输出结果:

{'     2': Benchmarks(extend=1.531e-06, extend_2=1.358e-06, better='extend_2'),
 '     5': Benchmarks(extend=2.185e-06, extend_2=1.739e-06, better='extend_2'),
 '    10': Benchmarks(extend=3.524e-06, extend_2=2.308e-06, better='extend_2'),
 '   100': Benchmarks(extend=2.513e-05, extend_2=1.288e-05, better='extend_2'),
 '  1000': Benchmarks(extend=0.0002717, extend_2=0.0001432, better='extend_2'),
 ' 10000': Benchmarks(extend=0.002942, extend_2=0.001435, better='extend_2')}

基于列表理解的新版本肯定更快;而且n越大,修正函数越快。对于n=10000extend_2()比原来的extend()快两倍左右。

我们现在应该对不同长度的不同列表的函数进行基准测试,但是我在这里的目的不是比较这两个函数,而是向您展示如何使用perftester简单地对函数进行基准测试。因此,我将把这些额外的基准留给你们作为练习。

高级用法

perftester函数使用默认设置,这通常是我们需要的。有时,我们可能希望改变NumberRepeat,就像我们上面所做的那样,根据一个函数要完成的操作数来选择Number。有时我们也想改变Repeat。当我进行的基准测试很重要时,我通常会增加NumberRepeat。当我有小分歧时,我也会这样做;我增加了这两个参数,以使基准更加稳定。

如果您想对所有基准使用相同的NumberRepeat,您不必每次运行perftester.time_benchmark()功能时都手动这么做。您可以在perftester.config对象中修改它一次,它控制perftester函数的行为。

为了做到这一点,做以下事情就足够了:

perftester.config.set_defaults("time", Number=1_000_000, Repeat=10)

这将改变所有要进行基准测试的函数的默认值NumberRepeat;当用户在调用perftester.time_benchmark()函数时改变这些参数或其中一个参数的值时,它们将不会被使用。

上面的命令改变了每个要进行基准测试的函数的默认值。对于特定的功能,您也可以这样做。例如:

perftester.config.set(foo, "time", Number=1_000_000, Repeat=10)

将改变函数foo()NumberRepeat——该函数之前必须已经定义过。因此,您不能更改尚未定义的功能的设置。

您也可以只更改两个参数之一的默认值:

perftester.config.set_defaults("time", Number=1_000_000)
perftester.config.set(foo, "time", Number=1_000)

另一个——没有改变——将简单地保持不变;也就是说,等于默认设置。

如前所述,相对基准是针对空函数的性能进行的,存储为perftester.config.benchmark_function。这种方法是有意义的,因为这个函数代表了调用一个函数的开销。因此,剩余的执行时间花在了基准测试函数要做的事情上。

有时候,你可能想把这个空函数换成另一个;将根据该功能的性能进行相对基准测试。做起来很简单:你可以用另一个覆盖perftester.config.benchmark_function();例如:

def foo():
    return [i for i in range(10)]

perftester.config.benchmark_function = foo

您可以从下面来自perftester仓库的文档文件中了解更多关于这个主题的信息:

https://github.com/nyggus/perftester/blob/implement-profiling-decorator/docs/benchmarking_against_another_function.md

结论

perftester对 Python 函数和其他可调用函数进行基准测试很容易。其实比用timeit容易。足以调用perftester.time_benchmark()函数,其 API 简单直观。唯一要记住的是以大写字母开始参数NumberRepeat;这同样适用于Func参数,但是您很少将它用作关键字参数,因为它是perftester.time_benchmark()函数的第一个参数,提供了要进行基准测试的函数。因此,从下面的两个电话来看,前者会更频繁:

# Rather this:
t = perftester.time_benchmark(
        extender.extend,
        [1, 1, 4, 4, 'a', 'a'],
        3
        )
# than this:
t = perftester.time_benchmark(
        Func=extender.extend,
        [1, 1, 4, 4, 'a', 'a'],
        3
        )

这并不意味着perftester通常比timeit 函数、timeit()repeat()简单。虽然perftester更容易对可调用程序的执行时间进行基准测试,但是timeit更容易对格式化为字符串的代码片段进行基准测试,比如"[i**2 for i in range(1000)]"。您可以使用perftester对这样的代码片段进行基准测试,但是您必须定义一个函数来完成这样的代码片段所做的事情。这意味着基准测试不仅会测量执行时间,还会通过调用函数来测量额外时间的开销。因此,当您有一个代码片段要进行基准测试时,您应该选择timeit模块。

虽然perftester更容易对可调用程序的执行时间进行基准测试,但是timeit更容易对代码片段进行基准测试。

然而,在基准可赎回性方面,perftester大放异彩。它的 API 专门用于这个场景,而timeit的 API 则不是。你可以这样做,但是你需要定义一个非参数 lambda。比较我们的extend()函数的这两个基准:

# perftester, using default settings
perftester.time_benchmark(extender.extend, [1, 1, 4, 4, 'a', 'a'], 3)
# timeit, using defaults settings
timeit.repeat(lambda: extender.extend([1, 1, 4, 4, 'a', 'a'], 3))

# perftester, changed settings
perftester.time_benchmark(
    extender.extend, [1, 1, 4, 4, 'a', 'a'], 3,
    Number=1000, Repeat=3,
)
# timeit, changed settings
timeit.repeat(
    lambda: extender.extend([1, 1, 4, 4, 'a', 'a'], 3),
    number=1000, repeat=3
)

perftester.time_benchmark()的调用更加自然,乍一看也更容易理解。在timeit函数中使用lambda的必要性使得这个调用可读性更差。

还有一件事。您可以选择使用全名来导入perftester,就像我在本文中所做的那样。但是你也可以这样做

import perftester as pt

在软件包的存储库中使用。全名导入稍微清楚一点,但是肯定更长,所以选择你喜欢的。

请注意,在进行基准测试时,perftester使用与timeit完全相同的后端,因为前者实际上调用后者。所以区别只在于 API。这种差异不足以学习一个新的框架来测试一个函数。但是正如我在上面所写的,perftester带来的不仅仅是基准执行时间,这就是为什么我相信你不会后悔花时间学习这个包——以及阅读这篇文章。您不仅可以对时间进行基准测试,还可以对内存进行基准测试,但最重要的是,该包使您能够编写时间和内存方面的性能测试。我决定在不同的文章中讨论这些不同的用例,以便一次处理一个主题——并且使学习更容易。

感谢您的阅读。我希望很快发布关于perftester的下一篇文章,你将会看到perftester提供了一些你以前没有见过的东西:Python 调用的性能测试框架。

在此期间,您可以将其用于基准时间。如果你想了解更多关于这个包及其用途的信息,你可以在perftester的 GitHub 仓库中找到:

https://github.com/nyggus/perftester

附录

**Func** **Number** **Repeat**

您可能想知道为什么这些参数(其中的NumberRepeat以及关键字参数)以大写字母开头。这是一个合理的问题,因为这似乎不合常理;我在这个附录中回答了这个问题,基于您可以在软件包的存储库中找到的解释。

这种方法最大限度地降低了要进行基准测试的函数与您计划使用的perftester函数同名的风险,在这种情况下,您将不得不使用一个参数两次——这将意味着SyntaxError。在 Python 代码库中,有很多函数都有一个名为funcnumberrepeat的参数。但是很少有函数有一个名为FuncNumberRepeat的参数。这就是为什么perftester的参数以大写字母开始。

然而,如果一个函数有一个参数FuncNumberRepeat,有一个解决方案。您可以定义一个functools.partial()函数并测试这个函数。你可以在这里阅读更多关于functools.partial()的信息:

https://docs.python.org/3/library/functools.html#functools.partial

下面,你会发现一个例子。假设你有一个函数foo(),它的参数是NumberRepeat。要使用perftester.time_benchmark(),您需要执行以下操作:

from functools import partial

def foo(Number, Repeat):
    return [Number] * Repeat

foo_partial = partial(foo, Number=20.5, Repeat=100)
perftester.time_benchmark(foo_partial, Number=1000, Repeat=10)

但是,首先你应该而不是使用NumberRepeat中的foo()参数。我展示这个解决方案是因为您可能会发现自己处于这样一种情况,您想要对别人编写的这样做的函数进行基准测试;安全总比后悔好。

functools.partial()在很多其他用例中都是非常有用的解决方案,不仅仅是这个。所以,反正知道这个功能就好了。

资源

https://docs.python.org/3/library/functools.html#functools.partial https://github.com/nyggus/perftester https://github.com/nyggus/rounder

BentoML:在几分钟内创建一个 ML 驱动的预测服务

原文:https://towardsdatascience.com/bentoml-create-an-ml-powered-prediction-service-in-minutes-23d135d6ca76

用 Python 封装和部署您的 ML 模型

动机

您刚刚建立了一个机器学习模型来预测客户属于哪个群体。该模型似乎在细分客户方面做得很好。您决定将这个模型交给您的团队成员,以便他们可以在您的模型之上开发 web 应用程序。

作者图片

等等,但是你如何将这个模型发送给你的团队成员呢?如果你的团队成员可以使用你的模型而不用设置任何环境或者弄乱你的代码,那不是很好吗?这时候 BentoML 就派上用场了。

随意发挥,并在这里叉这篇文章的源代码:

https://github.com/khuyentran1401/customer_segmentation/tree/bentoml_demo

BentoML 是什么?

BentoML 是一个 Python 开源库,使用户能够在几分钟内创建一个机器学习驱动的预测服务,这有助于弥合数据科学和 DevOps 之间的差距。

要使用将在本文中使用的 BentoML 版本,请键入:

pip install bentoml==1.0.0a4

为了理解 BentoML 是如何工作的,我们将使用 BentoML 来服务于一个根据新客户的个性对其进行细分的模型。

保存处理器

从从 Kaggle 下载客户个性分析数据集开始。接下来,我们将处理数据。

由于我们稍后将使用StandardScalerPCA来处理新数据,我们将把这些 scikit-learn 的变形金刚保存到 BentoML 的本地模型商店。

运行上面的代码后,模型将保存在~/bentoml/models/下。您可以通过运行以下命令来查看本地存储的所有模型:

$ bentoml models list

输出:

请注意,该模型是用特定的标签进行版本化的。如果我们保存另一个同名的模型,您应该会看到一个不同的标签。

这非常好,因为对模型进行版本控制将允许您在不同的模型之间来回切换。

找到完整的代码读取并处理数据 这里

保存模型

接下来,我们将在处理后的数据集上训练KMeans模型。我们将使用如上所示的相同方法来保存模型。

在这里 找到关于训练和保存模型 的完整代码。

创建服务

现在我们有了模型,让我们加载最新的处理器和模型,并在bentoml_app_pandas.py中用该模型创建一个服务。

function_name告诉 BentoML 模型运行时将使用哪个函数。

function_name的默认值为predict。由于customer_segmentation_kmeans是一个估计量,我们保持function_name的默认值。由于scalerpca是变压器,所以我们把function_name设为transform

然后用处理器和模型创建一个服务:

定义服务后,我们可以用它来创建一个 API 函数:

装饰器@service.api声明函数predict是一个 API,其输入是一个PandasDataFrame,输出是一个NumpyNdarray

现在让我们通过运行bentoml serve在调试模式下测试服务。由于bentoml_app_pandas.pysrc目录下,我们运行:

$ bentoml serve src/bentoml_app_pandas.py:service --reload

输出:

我们现在可以通过访问 http://127.0.0.1:5000 并点击“试用”按钮来与 API 进行交互:

作者图片

插入以下值:

[{"Income": 58138, "Recency": 58, "NumWebVisitsMonth": 2, "Complain": 0,"age": 64,"total_purchases": 25,"enrollment_years": 10,"family_size": 1}]

…请求体应该给你一个值1。这意味着模型预测具有这些特征的客户属于分类 1。

作者 GIF

用 pydantic 创建数据模型

为了确保用户将具有正确数据类型的正确值插入到 API 中,我们可以使用 pydantic 创建一个自定义数据模型:

现在,您应该在请求主体下看到默认值。

作者图片

在这里 可以找到关于创建 API 的完整代码。

制作便当

在确保一切看起来不错之后,我们可以开始将模型、服务和依赖项放入便当中。

作者图片

要构建 Bentos,首先在项目目录中创建一个名为bentofile.yaml的文件:

关于上述文件的详细信息:

  • include部分告诉 BentoML 在便当中包含哪些文件。在这个文件中,我们包括了bentoml_app.py和我们之前保存的所有处理器。
  • python部分告诉 BentoML 服务依赖于哪些 Python 包。

现在我们准备好做便当了!

$ bentoml build

作者图片

构建好的便当会保存在~/bentoml/bentos/<model-name>/<tag>目录下。目录中的文件应该类似于下图:

.
├── README.md
├── apis
│   └── openapi.yaml
├── bento.yaml
├── env
│   ├── conda
│   ├── docker
│   │   ├── Dockerfile
│   │   ├── entrypoint.sh
│   │   └── init.sh
│   └── python
│       ├── requirements.lock.txt
│       ├── requirements.txt
│       └── version.txt
├── models
│   ├── customer_segmentation_kmeans
│   │   ├── cs5htpv3ncng3lg6
│   │   │   ├── model.yaml
│   │   │   └── saved_model.pkl
│   │   └── latest
│   ├── pca
│   │   ├── latest
│   │   └── sa4bx5f3ngf6flg6
│   │       ├── model.yaml
│   │       └── saved_model.pkl
│   └── scaler
│       ├── latest
│       └── sawshnv3ngf6flg6
│           ├── model.yaml
│           └── saved_model.pkl
└── src
    └── src
        └── bentoml_app.py

相当酷!我们刚刚用几行代码创建了一个包含模型、服务、处理器、Python 需求和 docker 文件的文件夹!

部署到 Heroku

现在你已经有了制作好的便当,你可以把它打包成 Docker images 或者部署到 Heroku。因为我想为我的 API 创建一个公共链接,所以我将把它部署到 Heroku 容器注册中心。

从安装 Heroku 开始,然后在命令行上登录一个 Heroku 帐户:

$ heroku login

登录 Heroku 容器注册表:

$ heroku container:login

创建 Heroku 应用程序:

$ APP_NAME=bentoml-her0ku-**$(**date +%s | base64 | tr '[:upper:]' '[:lower:]' | tr -dc _a-z-0-9**)**
heroku create $APP_NAME

接下来,转到您最新构建的便当下的 docker 目录。要查看您的便当的目录,运行:

$ bentoml list -o json
[
  {
    "tag": "customer_segmentation_kmeans:4xidjrepjonwswyg",
    "service": "src.bentoml_app:service",
    "path": "/home/khuyen/bentoml/bentos/customer_segmentation_kmeans/4xidjrepjonwswyg",
    "size": "29.13 KiB",
    "creation_time": "2022-02-16 17:15:01"
  }
]

由于我最新的便当在~/bentoml/bentos/customer_segmentation_kmeans/4xidjrepjonwswyg,我将运行:

将便当打包并推送到上面创建的 Heroku 应用程序:

$ heroku container:push web --app $APP_NAME  --context-path=../..

发布应用程序:

$ heroku container:release web --app $APP_NAME

新应用程序现在应该会列在 Heroku 仪表盘中:

作者图片

单击应用程序的名称,然后单击“打开应用程序”打开您的 API 的应用程序:

作者图片

我的 API 服务的公共链接是https://bentoml-her0ku-mty0ndg3mza0ngo.herokuapp.com

作者图片

现在,您可以使用公共链接通过示例数据进行预测请求:

2

就是这样!现在,您可以将此链接发送给团队的其他成员,以便他们可以构建一个基于机器学习的 web 应用程序。无需安装和设置即可使用您的机器学习模型。多酷啊。

如果您喜欢自己创建一个简单的 UI,下一节将向您展示如何使用 Streamlit 来实现。

使用 Streamlit 为您的服务构建 UI

如果您希望您的经理或利益相关者尝试您的模型,使用 Streamlit 为您的模型构建一个简单的 UI 可能是个好主意。

在文件streamlit_app.py中,我从用户那里获得输入,然后使用这些输入进行预测请求。

运行 Streamlit 应用程序:

$ streamlit run src/streamlit_app.py

然后去 http://localhost:8501 。您应该会看到如下所示的 web 应用程序:

作者 GIF

这款应用现在玩起来更直观了。

结论

恭喜你!您刚刚学习了如何使用 BentoML 为您的机器学习模型创建 API 端点。有了 BentoML,您的队友可以使用您的模型,而无需设置环境或修改代码。多酷啊。

我喜欢写一些基本的数据科学概念,并尝试不同的数据科学工具。你可以通过 LinkedInTwitter 与我联系。

如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:

参考

阿卡什·帕特尔。2021–08–22.客户个性分析。
CC0:公共领域。检索自https://www . ka ggle . com/imakash 3011/customer-personality-analysis/

BERTScore:用 BERT 评估文本生成

原文:https://towardsdatascience.com/bertscore-evaluating-text-generation-with-bert-beb7b3431300

机器学习研究论文摘要

作者图片

BERTScore 是一种自动评估标准,用于测试文本生成系统的优劣。与计算标记级句法相似性的现有流行方法不同,BERTScore 侧重于计算参考标记和假设标记之间的语义相似性。这篇论文的作者在机器翻译和图像字幕任务上测试了它,发现它与人类的判断更相关。

更多这样的视频

让我们以总结系统为例,任务是通过 GPT2 模型对给定的书进行总结,假设模型说“这是我如何总结的”,但事实是“这应该是如何总结的”。

作者图片

为了评估系统已经生成的摘要的质量,可以使用现有的系统,例如 ROUGEBLEU 度量,这些度量依赖于假设和参考之间的句法重叠,通过考虑单字、双字等。但是考虑到它们的局限性,即在假设和参考中存在确切的词,并且不能解码语义,引入了 BERTScore,其中的思想是理解你已经生成的和应该生成的的含义,然后进行比较。

作者图片

如上图所示,我们采用参考(基本事实)和候选(生成的),并通过预训练的 BERT 模型,在输出端为每个单词生成上下文嵌入。一旦我们有了这些单词中每个单词的最终嵌入,我们就通过计算每个单词与候选单词中每个单词的参考相似度来进行 n 平方计算。我们从参考中找到并挑选与候选词最相似的词,并计算精确度、召回率和 f 值(精确度和召回率的调和平均值)。

作者图片

本文作者还引入了权重的概念,用于计算每个单词的相似度。他们坚持基于大量离线文本数据得出的 IDF 权重。因此,如果一个单词具有非常高的 IDF 权重,那么这不是一个在多个文档中使用的非常常见的单词,所以在进行相似性计算时,它可能值得您进行比较,不像具有低 IDF 的单词(主要代表常见单词)

所以,是的,这就是我的博客。我鼓励你也通读这篇论文,其细节将在下面提及—

论文标题 : BERTScore:用 BERT 评估文本生成

论文:【https://arxiv.org/abs/1904.09675】T21

作者 :张天翼、瓦莎·基肖尔、菲利克斯·吴、基连·q·温伯格、约夫·阿奇

组织机构:ASAPP 康乃尔大学公司

我希望你喜欢读这篇文章。如果你愿意支持我成为一名作家,可以考虑注册成为中的一员。每月只需 5 美元,你就可以无限制地使用 Medium。

CSV:为什么我放弃了他们的一些利益来获得其他利益

原文:https://towardsdatascience.com/best-file-format-to-store-large-data-dfa47701929f

我使用的替代方案允许更小的文件大小和更好的性能。

何塞·阿拉贡内塞斯在 Unsplash 上的照片

CSV 很好,但被高估了。

我使用 CSV 已经很长时间了。就像数据科学社区的其他人一样。然后腌制一段时间。

CSV 可以在任何系统上工作,无需安装任何东西。毕竟,它们是一种带有逗号分隔符的纯文本文件。这个事实也让它们超级简单易懂。

但是我们已经在这个舒适区待了太久了。

大多数数据应用程序受益于放弃一点灵活性。我们可以通过多安装一个包来大大加快数据的读写速度。

在本帖中,我们将讨论…

数据科学家的 CSV 问题;
我对用泡菜文件代替的想法;
CSV 和泡菜的更好替代品
对存储数据集的不同文件格式进行基准测试

您可以访问我在这篇文章中使用的用于基准测试的 Colab 笔记本

数据科学家的 CSV 问题。

自从我们开始存储数据以来,CSV 就一直存在。它们与文本文件没有什么不同,只是 CSV 遵循可预测的逗号模式。

软件使用这些信息将数据集分成几列。专栏不是 CSV 文件本身的奇迹。

即使列标题和行在 CSV 文件中也没有区别。我们需要配置读取 CSV 的软件来选择标题和行号。

如果列的其余部分遵循不同的数据类型,一些软件足够智能地选择标题。但它们只是由某人编程的有根据的猜测。

简单在很多情况下都很有效。尤其是如果你不知道客户使用的是什么软件,CSV 是非常好的。分享然后忘记!

但是 CSV 并没有针对存储或性能进行优化。

我们可以把可访问性性能和文件大小想象成一个三角形的三个角。你调一个;另外两个自我调整。

图片由作者提供。

CSV 已经将可访问性节点提升到最大。它会降低性能和文件质量。在这篇文章的后面,我们将比较 CSV 和其他格式的文件大小、保存和加载时间。

CSV 的另一个缺点是当文本包含 Unicode 字符时。您可能需要显式地将编码参数设置为众多支持值中的一个。

这里有一个例子告诉你如何在熊猫身上设置编码。

df = read_csv('/path/to/file.csv', encoding = "ISO-8859-1")

如果您的数据集很大,而您不知道使用哪种编码器,那么您就有麻烦了。

我们需要一种存储文件元数据(如头和编码)的文件格式,它从磁盘读取和写入的时间最少,并且大小更小。

用泡菜代替怎么样?

Pickle 是一种 Python 对象存储格式。它不是为存储数据帧而设计的,但在数据帧上运行良好。

Pickle 还可以存储数据帧的头、行号和其他元信息。因此,如果您使用 Pandas 读取数据帧,引擎不需要花费太多时间来确定数据类型和标题。

Pickle 到磁盘和磁盘到 pickle 与内存到磁盘和磁盘到内存几乎相同。

磁盘上 Pickle 文件的大小可能会有所不同。在大多数情况下,它比 CSV 略大,因为它们存储了更多关于数据集的信息。但是,Pickle 也存储转换成字节流的数据。它可能会使大小小于基于文本的 CSV。

此外,当您使用 Python 时,您不需要其他安装就可以从 Pickle 中获益。

df.to_pickle("./path/to/file.pkl") # to write a dataframe as pickledf = pd.read_pickle("./path/to/file.pkl") # to read a pickled dataframe

在我们的出发三角中,pickle 文件放弃了一点可访问性,以获得性能和文件大小方面的好处。因为您只能从 Python 程序中读取 pickle 文件。如果您组织中的其他人正在使用 R、Excel 或其他软件,它们可能无法很好地工作。

酸洗可以解决文件存储的许多问题。但是,如果您愿意进一步缩小可访问性特征的范围,还有一个更好的方法。

对于数据科学家来说,Pickle 文件是一个理想的用例。

我们将在接下来的几节中讨论 CSV 和 Pickle 的强大替代方案。

然而,它们不能被完全抛弃。还没有!

因为 Pickle 文件是内存中对象的快照,所以存储经过训练的 ML 模型非常好。

例如,我们将机器学习模型及其权重存储为 pickle 文件。流行的库如 scikit-learnTensorflow 创建可选择的模型。

这里有一个张量流的例子。

import joblib
import tensorflow as tf

model = tf.keras.Sequential([
            tf.keras.layers.Input(shape=(5,)),
            tf.keras.layers.Dense(units=16, activation='elu'),
            tf.keras.layers.Dense(units=16, activation='elu'),
            tf.keras.layers.Dense(units=8, activation='elu'),
            tf.keras.layers.Dense(units=8, activation='elu'),
            tf.keras.layers.Dense(units=5, activation='softmax'),
        ])

model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])model.fit(df['predictorrs'], df['labels'], epochs=200, batch_size=8)joblib.dump(model, 'classifier.pkl')

CSV 和泡菜的更好替代品

我使用 CSV 已经很长时间了。就像数据科学社区的其他人一样。然后腌制一段时间。如今,我更喜欢用其他二进制文件格式来存储数据。

尤其是其中的两个。羽毛 & 拼花

羽毛文件格式

feather 文件格式是一种快速的、与语言无关的数据帧存储,适用于 Python (pandas)和 r。

Feather 针对低存储空间和高性能进行了优化。这使得它比 CSV 更难访问。

虽然 CSV 可以在任何能够理解文本的机器上运行,但 Feather 只能在 Python 和 r 上运行。我们需要手动安装它。

如果您使用 Python,可以从 PyPI 存储库中获取它。

pip install feather-format

r 程序员可以使用下面的命令直接从 Git 库安装 Feather。

devtools::install_github("wesm/feather/R")

一旦你安装了这个包,对你现有代码库的改变是最小的。

使用羽化格式相当简单。

Pandas 库内置了羽毛格式的方法。您可以使用 to_featherread_feather 在磁盘上保存和加载数据。

# Change this
df = pd.read_csv(...)# To this
df = pd.read_feather(...) # Change this
df.to_csv(...)#To this
df.to_feather(...)

拼花文件格式

Parquet 是另一种二进制文件格式,它比使用文本文件更有优势。

Parquet 使用在 Dremel 论文中描述的记录粉碎和组装算法。它在列存储中高效地表示嵌套结构。

因此,处理大量嵌套的查询比基于文本格式的查询执行速度更快。

拼花地板的文件大小通常小于文本格式。

在熊猫身上使用拼花地板就像羽毛一样简单。

# Change this
df = pd.read_csv(...)# To this
df = pd.read_parquet(...)# Change this
df.to_csv(...)#To this
df.to_parquet(...)

对存储数据集的不同文件格式进行基准测试

我想亲眼看看。我想测试每种文件格式的各个方面。

所以我写了一个小剧本。它从公开可用的数据集中获取数据。然后,它从原始文件创建 1000 个不同的 10,000 行的文件。为此,脚本在熊猫数据帧上使用随机抽样。

然后,我们记录将所有这些文件写入磁盘所需的时间、磁盘上的文件大小,以及将所有这些文件读回内存所需的时间。

我们测量所有文件格式的上述三个值。这是结果。

拼花是最小型的文件格式。

不同文件格式的文件大小基准测试—图片由作者提供。

拼花文件比 CSV 小得多。它们甚至比羽毛锉还要小。

另一方面,JSON 是在磁盘上存储文件的最差格式。它占用的文件格式空间是 CSV 的两倍多。

就文件大小而言,Feather 略小,Pickle 比 CSV 占用更多的空间。

羽毛是读写最快的。

比较不同文件格式存储数据集的文件读/写性能—图片由作者提供。

羽毛的阅读表现令人印象深刻。它只需要 CSV 加载时间的一半。与 Feather 相比,即使是 Pickle 文件也要慢得多。

同样,JSONs 需要花费太多时间加载到内存中,因为它需要很大的磁盘存储空间。

羽毛在写作表现上也远胜 CSV。只有泡菜和羽毛的数量相对相似(仍然缓慢)。

最终考虑

如果你和其他人共享数据,并且你不确定他们是否安装了 Python 或者任何兼容的软件,那么使用 CSV 是可以的。

但在所有其他情况下,CSV 会严重伤害你。

如果您的主要目标是以非常低的成本存储数据,请使用 Parquet。它占用很少的磁盘空间,其读/写性能令人印象深刻。

如果您正在使用 Python 或 R,并且读/写性能是您的主要关注点,那么 Feather 要比 CSV 和 Pickle 好得多。它的体积也更小。

感谢阅读,朋友!在LinkedInTwitterMedium上跟我打招呼。

还不是中等会员?请使用此链接使 成为 的会员,因为,在不为你额外付费的情况下,我为你引荐赚取一小笔佣金。

arXiv 最佳—2022 年 2 月

原文:https://towardsdatascience.com/best-of-arxiv-february-2022-62c2aea3b802

Zeta Alpha 每月 ML 论文精选:强化学习、多模态、语言模型即服务、计算机视觉、信息检索等。

图片作者。

人工智能研究的世界已经全速进入 2022 年,过去几周的相关出版物和新闻数量可以证明这一点。让我们首先强调一些你不该错过的近期新闻:

来源:https://github.com/libffcv/ffcv

🔬研究

Zeta Alpha 监测人工智能研究的趋势,帮助你确定什么值得阅读。在它的帮助下,我们选择了 8 篇论文,这些论文体现了不同人工智能子领域的关键发展:自动强化学习(AutoRL)、多模态语言模型(LMs)、计算机视觉中的 conv nets vs . Transformers(CV)、无监督神经信息检索(IR)等。尽情享受吧!

1。自动强化学习(AutoRL):综述和公开问题

作者:Jack Parker-Holder,Raghu Rajan,Xingyou Song 等人

❓Why → 机器学习的主要目标之一是几个数据处理工作流和管道的自动化,允许非专家使用 ML 技术,因此像 AutoML 这样的主题很受欢迎。AutoRL 是强化学习世界中的模拟。

💡关键见解→ 本文概述了这一领域,提供了有用的分类法来统一各种 AutoRL 方法。这对 ML 实践者特别有用,因为 RL 词汇表与 ML 词汇表有很大不同,使得跨领域的思想交叉传播更加困难。

讨论的主题包括不同目标的优化技术(如超参数、任务设计、架构等。):

  • 随机与网格搜索驱动的方法
  • 贝叶斯优化
  • 进化(和/或基于群体的)方法
  • 元梯度
  • 黑盒优化
  • 学习 RL 算法,环境设计

来源:https://arxiv.org/pdf/2201.03916.pdf

虽然总是开箱即用的 RL 的“梦想”似乎仍然很遥远,但这似乎并没有阻止研究人员进入它。

另一篇最近关于强化学习和语言模型交叉的论文可能很有趣,那就是语言模型作为零射击计划者:为具体化代理提取可操作的知识

2。作为零射击计划者的语言模型:提取具体化代理的可操作知识

作者:黄、彼得阿贝耳、迪帕克帕沙克和伊戈尔莫达契。

❓为什么→ NLP 技术跨越到 ML 的其他领域已经是过去几年反复出现的主题。下面是当你使用像 GPT-3 这样的预训练语言模型(LM)来为代理构建动作序列时会发生的情况。它…起作用了!

💡关键见解→ 如果规模足够大并经过适当培训,大型 L:Ms 可以将高级任务分解为低级计划,而无需进一步培训(即仅使用冻结模型)。

然而,由自由形式的 LM 生成的计划可能是不可执行的,也就是说,可映射到一组现有的已知对象和动作。这就是作者建议引入从 LM 输出到有效动作的映射步骤的原因。这种映射由句子相似性转换器执行,该转换器在嵌入空间中找到最接近的有效低级动作。

来源:https://arxiv.org/pdf/2201.07207.pdf

虽然结果并不惊天动地,但它们证明了冻结的 LMs 包含了从高级指令中提取低级行动计划所需的信息。在这里你可以观看一些演示并检查他们的代码。

3。CM3:互联网的因果掩蔽多模态模型

阿尔门·阿加贾尼扬等人

❓为什么→ 多模态已经成为人工智能中一个快速发展的子领域,特别是自从巨大的数据饥渴变压器出现以来。虽然对于现有的基准来说,它们的表现可以说是乏善可陈,但在可预见的未来,关于该主题的研究数量肯定会不断增加。

💡关键见解→ 这项工作的作者巧妙地设计了一个预处理任务,对包含文本和图像的 HTML 数据进行操作。但是,如何将图像编码成可以提供给模型的标记呢?与 OpenAI 的 DALLE 有些类似,他们使用 VQVAE-GAN 学习图像补丁的量化表示,可以将其视为离散的符号字典,就像常规的文本标记一样。

对于训练,他们使用从左到右和双向语言建模的组合,整个事情的规模很大,但对于今天的标准来说还不算大:1TB 的训练语料库,最大模型的最大参数为 13B。

他们在零镜头设置中的单峰和多峰任务上对其 CM3 进行了基准测试,显示了在图像字幕、图像生成、零镜头摘要、实体链接和其他几个 NLP 任务上的稳定(甚至在某些情况下是 SOTA)性能。

来源:https://arxiv.org/pdf/2201.07520.pdf

最近关于多模态人工智能的类似工作包括: data2vec:来自脸书的语音、视觉和语言自我监督学习的通用框架(或参见他们的博客文章)和用于监督跨模态检索的视觉语言预训练模型的综合实证研究

4。网络是你的牡蛎——知识密集型自然语言处理对一个非常大的网络语料库

亚历山大·皮克图等人

❓为什么 →当 GPT-3 在 2020 年 5 月问世时,一个常见的批评是,它对 Covid“一无所知”,因为它的训练语料库是在疫情开始之前创建的。包括这些知识将需要用新数据训练模型,无论是为了微调还是从头开始,这是非常昂贵的。让语言模型访问知识语料库是最近的一项发展,这使它们能够成为更高效的学习者更准确的事实,增加了能够更新知识而无需重新训练神经网络的好处。

💡关键见解 →知识密集型自然语言处理任务被定义为在不查阅知识语料库(例如,一本书、网络)的情况下,人类无法解决的任务。本文提出了一个新的基准,精确地测量 LMs 在这方面的表现。它建立在现有的 KILT 基准之上,主要基于维基百科语料库来构建事实检查、实体链接、槽填充、开放域 QA 和对话生成任务。

随着越来越多的检索增强语言模型被提出,拥有一个可靠的评估系统来比较它们变得越来越重要。这种模型的一些最近的例子包括 WebGPT:具有人类反馈的浏览器辅助问答(open ai)通过从数万亿个令牌中检索来改进语言模型(deep mind)人工制品检索:具有知识库访问的 NLP 模型概述(萨尔州大学)或 LaMDA:对话应用的语言模型(谷歌)。

来源:https://arxiv.org/pdf/2112.09924.pdf

5。LaMDA:对话应用的语言模型

Romal Thoppilan 等人

❓Why → 尽管文本生成技术取得了巨大的进步,但你在那里发现的许多聊天机器人仍然很烦人,没什么用处。现代语言模型如何改善对话式人工智能?这是谷歌的最新提议。

💡关键见解→ 这实际上是语言模型的另一个实例,它与知识库交互以回答用户的查询,基本上是一个检索增强的 LM。按照通常的谷歌方式,他们训练了一个庞大的 137B 模型,并使用人类的判断来评估它的敏感性和特异性等指标。不出所料,性能会随着规模的扩大而不断提高,不会饱和。

在概念层面上,这种方法很简单:使用 LM 的两种变体,LaMDA-Base 是一种在对话中训练的常规 LM,LaMDA-Research 是一种 LM 的变体,它被训练成与作者称为工具集(ts)的外部知识系统进行交互。这个工具集不仅包括一个信息检索系统,还包括一个用于算术查询的计算器和一个翻译器。

LaMDA-Base 和 LaMDA-Research 通过传递它们的输入并连接它们来保持全局上下文进行交互(见下图)。当然,这个模型成功的关键之一是作者管理的高质量训练数据集,除了通常的大规模自我监督预训练之外,它还包括超过 40k 个带注释的对话交互。

来源:https://arxiv.org/pdf/2201.08239.pdf

其他相关近期工作:会话信息检索的神经方法会话信息搜索

6。语言模型即服务的黑盒调优

孙天祥等

❓Why → 随着大型变压器成为许多研究领域的标准,它们的使用方式也面临着挑战。不久以前,人们可以简单地下载一个几百兆字节大小的模型检查点,并在任何你想去的地方运行它。但是当检查点的大小接近 1tb 时…它需要在几台机器上运行,下载是不可行的!此外,对于 OpenAI 这样的公司来说,这样的大型模型已经成为非常有价值的知识产权,是他们提供服务的支柱,也是他们不愿意放弃的明显竞争优势。因此出现了作为服务的 ML 模型,它将 ML 模型公开为一个黑盒 API,在给定一组输入的情况下返回预测。现在,您能调优这样一个只能作为黑盒 API 访问的模型吗?

💡关键见解→black box API 的用户可以使用无导数算法调整他们的系统(记住,我们只能访问输入和输出,不能访问梯度!).特别是,他们使用进化算法在提示和超参数的空间中进行搜索,有效地学习优于手动提示上下文学习的提示,这意味着在提示中包括训练示例,就像 GPT-3 对少量学习所做的那样。在某些情况下,他们的方法优于基于梯度的方法,如即时微调!

来源:https://arxiv.org/pdf/2201.03514.pdf

优化纯界面模型领域的另一项相关工作是内存辅助提示编辑,以改进部署后的 GPT-3

7。面向 21 世纪 20 年代的 conv net

刘庄等人

❓为什么 →深度学习在 2010 年代初的强劲势头在很大程度上可以归功于 AlexNet 在 2012 年 ImageNet 挑战赛中的巨大成功。从那以后的很多年里,卷积——这种神经网络的主要组成部分——独自统治了计算机视觉的世界。然而,随着变压器及其方便的可扩展性的引入,将它们应用于 CV 的方法——如斯温 transformer⁴——变得越来越流行;可以说威胁到了回旋保持了这么久的王冠。

💡关键见解→ 回旋仍在摇摆。

本文认为,通过进一步优化,ConvNets 仍然比变压器有优势,从而产生了流行的 ResNets 的现代版本,与类似的基于变压器的架构相媲美。这些变化包括抛弃 BatchNorm 而支持 LayerNorm,从 ReLU 切换到 GELU,或者改变卷积核的大小等等。差不多就是这样,他们在 ImageNet 上的结果和缩放规则略高于基于 transformer 的架构。嗯,可能要到下周另一篇论文出来…

架构之战仍在继续,如果有一点是明确的,那就是人工智能领域肯定会从竞争中受益!

来源:https://arxiv.org/pdf/2201.03545.pdf

8。GLIDE:使用文本引导扩散模型实现照片级真实感图像生成和编辑

亚历克斯·尼科尔、普拉富拉·达瑞瓦尔、阿迪蒂亚·拉梅什等人

❓为什么 →自 2014 年 GANs 推出以来,图像生成一直是深度学习的一个非常养眼的应用。然而,最近,诸如使用 VQ-VAE (例如达尔和)的自回归生成和扩散模型等方法正在成为可行的甚至更好的替代方法。

💡关键见解→ 简而言之,扩散模型通过在像素网格上迭代添加可微分噪声来生成图像,最终成为真实的图像。本文提出了一种在给定文本提示的情况下基于扩散模型生成和编辑图像的方法,该扩散模型非常好,击败了著名的 OpenAI 的 DALL E。然而,这些模型仍然存在一些缺点,例如生成每个图像所需的计算成本,这仍然阻止了它们在许多应用中得到广泛使用。

来源:https://arxiv.org/pdf/2112.10741.pdf

9。通过对比预训练嵌入文本和代码

阿尔温德·尼拉坎坦、徐涛等人

❓为什么→ 神经信息检索是深度学习游戏的晚期,在某些方面仍然不如 BM25 等 20 多年前的算法!这个等式的一个关键部分是对大量标记数据的依赖:今天所有成功的神经检索方法都严重依赖于标签,如来自马尔科女士数据集的标签。这些模特在完全没有监督的情况下能训练出来吗?在过去的几个月里,出现了希望的迹象!

💡关键见解→ 这是 OpenAI 的一个提议,以完全自我监督的方式学习文本的文本表示。这些表示(即嵌入)旨在成为包括信息检索在内的各种任务的可靠执行者。工作原理非常简单:使用相邻的文本片段作为正的伪查询文档对和批内否定。非常大批量必须我加。然而,并非所有闪光的东西都是金子:虽然完全无人监管的性能是可靠的,但你可以以低得离谱的成本使用只在几个公开可用的标签上微调的小模型来实现更好的性能,正如 SBERT 的创始人 Nils Reimers 已经展示的

总之,对于无监督的神经信息检索和表示学习来说,这是重要的一步,但不是像一些标题所暗示的那样,是一个解决所有问题的嵌入 API。这是只能通过付费 API 访问的模型的又一个例子,我们期望这样的例子会变得更加普遍。

来源:https://arxiv.org/pdf/2201.10005.pdf

最近其他类似的关于无监督信息检索的工作是使用对比学习进行无监督密集信息检索在没有监督的情况下学习检索段落

10。DeepSpeed-MoE:推进混合专家推理和训练,为下一代人工智能量表提供动力 | 网站

Samyam Rajbhandari 等人

❓为什么→ 在过去的一年里,混合专家(moe)已经成为扩展大规模语言模型的首选策略。关键概念很简单:在推理过程中,只通过模型中的子路径传递输入,这样在每一步中只使用一小部分模型参数。这种系统的实现细节仍然是混乱的,并且包括关于密集模型的严重折衷,例如推理速度。

💡Key insights → DeepSpeed-MoE(即将在 GitHub 上开源)是微软最新版本的 DeepSpeed 库,旨在使分布式深度学习训练变得简单高效,它是这项工作的实现骨干。

作者展示了 MoEs 与密集的同类相比是如何闪耀的:更有效的训练——大约 5 倍——和更好的参数效率。

本文还深入探讨了什么样的设计选择能让 MoEs 学得更好。例如,浅层的专家多一些好,还是深层的专家多一些好?要增加模型容量,应该增加每个专家的容量还是增加专家的数量?虽然这些问题还没有绝对的答案,但本文根据经验探索了这些设计选择的权衡,将它们包装在通用 PR-MoE(金字塔剩余 MoE)下。它们的 PR-MoE 的基本结构如下图所示,包括不同的“专家宽度”以及剩余的 MLP 连接。

虽然 moe 仍然不是主流,但是如果实现和设计的复杂性得到解决,它们有可能成为下一代大规模模型的标准。

来源:https://arxiv.org/pdf/2201.05596.pdf

我们的月度评选到此结束;如果你想了解最新的研究,请在 Twitter @zetavector 上关注我们,继续关注下一篇!

参考文献:

1。Patrick Esser、Robin Rombach、bjrn Ommer 于 2021 年出版的《驯服高分辨率图像合成的变形金刚》

2。Aditya Ramesh 等人于 2021 年发表的“零镜头文本到图像生成”

3。法比奥·彼得罗尼等人的《苏格兰短裙:知识密集型语言任务的基准》,2020 年

4。“Swin 变压器:使用移位窗口的分层视觉变压器”,刘泽等人,2021 年

两全其美:来自 Python 的自动化和动态 SQL 查询

原文:https://towardsdatascience.com/best-of-both-worlds-automated-and-dynamic-sql-queries-from-python-5b74a24501b0

通过 SQL 和 Python 集成将自动化带到新的高度

现实世界的分析应用程序通常使用各种编程语言构建,每种语言都需要直接访问存储在数据库中的数据。最终目标是创建一个从数据提取(使用 SQL)到模型开发,再到持续性能监控的自动分析管道。通过独立的平面文件传输数据的时代已经过去了!

SQL 和关系数据库垄断了几十年,作为分析的全明星脚本语言,Python 对 SQL APIs 有很好的支持,允许用户直接拉取数据。在这篇博客中,我将分享 3 种方法以及集成 SQL 和 Python 来创建无缝分析工作流的用例。

分析数据集-在线零售数据

Python 连接关系数据库有两种方式:(1)使用 ODBC(开放式数据库连接)作为连接引擎,访问托管在远程 SQL server 中的数据库;(2)使用 ORM(对象关系映射器)作为位于最终用户和数据库之间的抽象层,这提供了更多的灵活性。

在这个练习中,我们将在 MS SQL 服务器中实现 ORM 方法。我们正在处理的数据集是在线零售数据;包含来自英国在线零售商的各种交易的纵向数据集。

正如我们所看到的,每个记录由产品、数量、价格/单位和客户、县信息组成。

先决条件-将 Python 连接到 SQL server

Python {urlib} +

尽管{ SQLAlchemy }在数据科学家和程序员中非常受欢迎,但以理想的形式获得连接字符串可能很棘手。要用 Python 连接 SQL,我们需要我们的驱动程序名、服务器名和数据库名。这里演示了如何为我的本地服务器localhost\SQLEXPRESS和名为master的数据库指定这些参数,

加入我们的 YouTube 社区🎦 【数据说话带吉】 😄

用例 1:用 Python 编写的简短查询

建立了 SQL 连接引擎后,我们可以编写一个简短的查询,使用{ 熊猫 }将数据拉入 Python,

在本例中,我们提取了 2010 年 12 月 1 日和 2011 年 1 月 1 日之间两种产品——巧克力热水瓶和灰心热水瓶——的所有交易,然后得出每种产品按日期计算的总销售额。以下是返回的数据框:

现在,作为我们分析工作流程的一部分,让我们创建一个可视化图,例如纵向折线图,向我们的利益相关者展示趋势。

用户案例#2:读取外部的。sql 文件

我们刚刚经历的是将 SQL 纳入整个分析管道的最简单方法。然而,在我们的实践中,用 Python 编写或复制/粘贴每一个查询是低效的,甚至是不可能的。原因是双重的:

  1. 现实世界的项目通常需要通过连接多个表和视图来进行长达数千行的查询;
  2. 从可重用性的角度来看,如果我们经常需要更新或修改这些查询,维护和调试代码将会非常困难。

那么解决办法是什么呢?阅读现存的不是很好吗?sql 文件直接导入 Python?出于演示的目的,让我们将上面的查询保存为一个. sql 文件*OnlineRetailPull.sql*

现在,诀窍是扫描这个查询文件,就像它是一个字符串对象一样:

输出OnlineRetailData与上面用例 1 的返回完全相同。

用例 3:来自应用程序的动态查询

现在,让我们回到最初的目标——将数据提取整合到我们的应用程序中,对用户的输入做出反应。我们将使用 Python{streamlit }和创建一个小 app,探索如何动态运行 SQL 查询。

第一步 :通过添加占位符来修改我们的查询。在本练习中,我们定义了两个交互变量 {date}{product},

第二步 :设置我们的streamlitapp,根据用户选择可视化产品销售趋势,

这里的技巧是在我们的查询和应用程序的用户选项中的占位符使用相同的对象名称(即日期和产品)。这样, f 字符串可以自动接受用户选择的任何值。

运行此应用程序让我们…

最后的话

你有它!动态集成 SQL 和 Python 的三个不同用例。希望这篇文章能给你一些新项目的灵感,感谢阅读!

想要更多数据科学和编程技巧?使用 我的链接 注册 Medium,获得我所有内容的全部访问权限。

还订阅我新创建的 YouTube 频道 《数据与吉谈》 😀

进一步阅读

*</6-sql-tricks-every-data-scientist-should-know-f84be499aea5> https://levelup.gitconnected.com/6-hilarious-programmers-data-scientists-jokes-to-kick-start-2021-187f86dd6a4c *

计算和解释模型特征重要性的最佳实践

原文:https://towardsdatascience.com/best-practice-to-calculate-and-interpret-model-feature-importance-14f0e11ee660

以随机森林模型为例

来源: Unsplash (归功于凯文·Ku)

在机器学习中,大多数时候你希望模型不仅准确,而且可以解释。一个例子是客户流失预测-除了知道谁会流失,了解哪些变量在预测流失以帮助改进我们的服务和产品方面至关重要。

Scikit-learn 等流行的机器学习包为模型解释提供了特征重要性的默认计算。然而,我们经常不能相信那些默认的计算。在本文中,我们将使用来自 Kaggle 的著名的泰坦尼克号数据和一个随机森林模型来说明:

  • 为什么您需要一个健壮的模型和排列重要性 分数来正确计算特性重要性。
  • 为什么您需要理解特性的相关性来正确解释特性的重要性。

本文中描述的实践也可以推广到其他模型。

计算要素重要性的最佳实践

默认特征重要性的问题是

我们将使用一个示例来展示 Scikit-learn 中为随机森林提供的默认基于杂质的特征重要性的问题。默认特征重要性是根据杂质的平均减少量(或基尼系数重要性)计算的,它衡量每个特征在减少不确定性方面的有效性。参见这篇伟大的文章以获得特性重要性计算背后的数学的更详细的解释。

让我们从 Kaggle 下载著名的泰坦尼克号数据集。该数据集包含泰坦尼克号上 1309 名乘客的信息以及他们是否幸存。下面是对包含的列的简要描述。

首先,我们加载数据,并将其分为预测集和响应集。在预测器集合中,我们添加了两个随机变量random_catrandom_num。因为它们是随机生成的,所以这两个变量应该具有非常低的特征重要性分数。

其次,我们对数据进行一些简单的清理和转换。这不是本文的重点。

第三,我们建立一个简单的随机森林模型。

RF train accuracy: 1.000.RF test accuracy: 0.814

该模型在训练数据上略有过度拟合,但在测试集上仍有不错的性能。现在让我们使用这个模型来说明默认特性重要性计算的一些缺陷。让我们来看看默认的特性重要性。

从默认的特性重要性中,我们注意到:

  • random_cat相比,random_num具有更高的重要性分数,这证实了基于杂质的重要性偏向于高基数和数字特征
  • 非预测性random_num变量被列为最重要的特征之一,这没有意义。这反映了当您有一个过拟合模型时默认特征重要性不准确。当模型过度拟合时,它会从训练集中拾取太多的噪声,从而无法对测试集做出一般化的预测。当这种情况发生时,特征重要性是不可靠的,因为它们是基于训练集计算的。更一般地说,只有当您有一个可以合理预测的模型时,查看特性的重要性才有意义。****

救援的排列重要性

那么我们如何恰当地计算特性的重要性呢?一种方法是使用排列重要性分数。它通过以下步骤计算:

  1. 训练一个基线模型,并在验证集上记录分数(在这个例子中我们使用准确性)。
  2. 重新排列一个特征的值,使用模型再次进行预测,并计算验证集的得分。该特征的特征重要性是 1 中的基线和 2 中的置换得分之间的差异。
  3. 对所有特征重复该过程。

这里我们利用 2019 年添加到 Scikit-learn 包中的 permutation_importance 函数。当调用该函数时,我们设置 n_repeats = 20,这意味着对于每个变量,我们随机洗牌 20 次,并计算准确度的下降,以创建箱线图。

我们看到sexpclass显示为最重要的特征,并且random_catrandom_num不再具有基于测试集上排列重要性的高重要性分数。箱线图显示了具有 N 次重复排列(在我们的例子中 N = 20)的准确度分数降低的分布。

让我们也计算训练集上的排列重要性。这表明random_numrandom_cat获得了比在测试集上计算时显著更高的重要性排名,并且特性的排名看起来与测试集非常不同。如前所述,这是由于模型的过度拟合。

你可能想知道为什么 Scikit-learn 仍然包含默认的特性重要性,尽管它并不准确。RFs 的发明者 Breiman 和 Cutler 指出,这种“将森林中所有树木的每个变量的基尼系数减少相加的方法给出了一个快速的变量重要性,这通常与排列重要性度量非常一致。”所以缺省值意味着置换重要性的代理。然而,正如施特罗布尔等人在随机森林变量重要性测量的偏差中指出的,“布雷曼的原始随机森林方法的变量重要性测量……在潜在预测变量的测量范围或类别数量发生变化的情况下是不可靠的。”

稳健的模型是获得准确重要性分数的先决条件

我们已经看到,当模式过度拟合时,从训练集和预测集中生成的特征重要性可能会非常不同。让我们通过设置 min_samples_leaf = 20 而不是 1 来应用某种程度的正则化。

RF train accuracy: 0.810RF test accuracy: 0.832

现在让我们再次看看特性的重要性。修复过度拟合后,根据训练集和测试集计算的特征重要性看起来非常相似。这给了我们更多的信心,一个健壮的模型给出了准确的模型重要性。

另一种方法是计算删除列重要性。这是计算特征重要性的最准确的方法。这个想法是用所有预测值计算模型性能,去掉一个预测值,然后观察性能的下降。特性越重要,我们看到的模型性能的下降就越大。

考虑到删除列重要性的高计算成本(我们需要为每个变量重新训练一个模型),我们通常更喜欢排列重要性分数。但这是验证排列重要性的一个很好的方法。这两种策略的重要性值可能不同,但特征重要性的顺序应该大致相同。

特征的排序类似于置换特征,尽管幅度不同。

解释特性重要性的最佳实践

特征相关性的挑战

在我们有了一个健壮的模型并正确地实现了正确的策略来计算特性重要性之后,我们就可以前进到解释部分了。

在这个阶段,相关性是我们解释特征重要性的最大挑战。到目前为止,我们所做的假设分别考虑了每个特性。如果所有的特征都是独立的,没有任何关联,那就很容易解释了。但是,如果两个或多个要素共线,将会影响要素重要性结果。

为了说明这一点,让我们使用一个极端的例子,复制列性别来重新训练模型。

RF train accuracy: 0.794
RF test accuracy: 0.802

当我们添加了一个没有添加任何信息的特征时,模型性能会稍微下降。

我们现在看到性特征的重要性现在分布在两个重复的性列之间。如果我们给复制的列添加一点噪声会发生什么?

让我们试着给性别添加 0-1 范围内的随机噪声。

sex_noisy现在是最重要的变量。如果我们增加噪声的数量会发生什么?让我们将随机变量的范围增加到 0–3。

现在我们可以看到,随着更多的噪音加入,现在sex_noisy不再是排名第一的预测因素,性别又回到了首位。结论是在随机森林模型上计算的排列重要性在共线变量上传播重要性。分享的数量似乎是两者之间有多少噪音的函数。

处理共线要素

我们来看看特征之间的相关性。我们使用 rfpimp 包中的 feature_corr_matrix,它给出了 Spearman 相关性。Spearman 相关与标准 Pearson 相关的区别在于,Spearman 相关首先将两个变量转换为排名值,然后对排名变量运行 Pearson 相关。它没有假设变量之间的线性关系。

feature_corr_matrix(X_train)

from rfpimp import plot_corr_heatmap
viz = plot_corr_heatmap(X_train, figsize=(7,5))
viz.view()

pclassfare高度相关,这并不奇怪,因为舱位等级取决于你支付的费用。在业务中,我们经常在预测模型中使用多个相互关联的特征。从前面的例子中,我们看到,当两个或多个变量共线时,根据信噪比,计算的重要性在共线变量之间共享。

策略 1:组合共线特征

解决这个问题的一种方法是将彼此高度共线的特征组合起来形成一个特征族,我们可以说这个特征族一起排列为 X 最重要。为此,我们将使用 rfpimp 包,它允许我们一次混洗两个变量。

策略 2:删除高度共线变量

如果某个特征依赖于其他特征,这意味着可以使用所有其他特征作为独立变量来准确预测该特征。模型的 R 越高,特征的依赖性越强,我们就越有信心移除变量不会牺牲准确性。

第一列 dependency 显示了依赖分数。使用其他特征完全可预测的特征将具有接近 1 的值。在这种情况下,我们可能会丢弃pclassfare中的一个,而不会影响太多精度。

最后

一旦我们 1)有了一个健壮的模型,并实现了正确的策略来计算排列的重要性,2)处理了特性的相关性,我们就可以开始精心制作我们的消息来与利益相关者分享。

对于人们经常问的问题“特性 1 比特性 2 重要 10 倍吗?”,这时你可能明白了,只有当所有的特征都是独立的或者相关性很低的时候,我们才有信心进行论证。但在现实世界中,这种情况很少发生。建议的策略是将功能分配给高、中和低影响层,而不要过于关注确切的影响程度。如果我们需要显示特征之间的相对比较,请尝试将共线特征分组(或删除)到熟悉的特征,并基于分组进行解释,以使论点更加准确。

你可以在我的 Github 上找到这篇文章的代码。

参考

[1] 小心默认随机森林重要性

[2] 排列重要性与随机森林重要性(MDI)

[3]sci kit-Learn 机器学习模型的特征重要性

[4] 决策树、随机森林的数学特性在 Scikit-learn 和 Spark 中的重要性

[5] 以随机森林为例解释特征重要性

除特别注明外,所有图片均为作者所有。

建立对数据信任的最佳实践

原文:https://towardsdatascience.com/best-practices-for-building-trust-in-your-data-dda32b84e70e

如何让你的利益相关者停止怀疑和质疑

迪伦·吉利斯在 Unsplash 上的照片

有多少次你收到来自业务利益相关者的懈怠信息,说仪表板坏了,数据一周没有更新,或者某些数字看起来非常错误?你看不到我,但我站得很高,手臂直直地举在空中。

这种情况已经发生了太多次了。这是为什么呢?因为对我们的旧数据堆栈缺乏信任。利益相关者有太多的问题,数据集被破坏,无法在他们需要时看到他们需要的洞察力。

构建现代数据堆栈时,您需要从一开始就慢慢建立信任。您需要实施正确的测试和检查,以确保数据符合您的期望。你需要开诚布公地对待你正在着手的计划,以及在这个过程中可能出错的事情。在做这些事情的同时,你还必须教育你的消费者,而不是当着他们的面把最终产品拍下来,告诉他们一切都很好。

设置测试,以便从源头提醒您数据质量问题。

测试和监控原始数据以及数据模型是检测数据中任何主要问题的关键。我喜欢用两个免费工具来做这件事。

dbt 测试

dbt 测试内置于任何 dbt 项目中。它们允许您检查空值、主键和某些指定的值。您可以将这些添加到您的基础模型中,以在源位置检查您的数据,或者添加到您的核心数据模型中,以确保您得到了您期望的结果。

您只需在定义 dbt 模型的 yaml 文件中添加“tests”块。dbt 将在您的模型运行时运行这些检查,当模型中的某一列不符合您设置的检查时,会向您发出警告。

作者图片

re _ 数据指标

我第一次发现 re_data 是在我寻找一种方法来监控每天摄入的数据量的时候。我需要一个适当的检查来确保我的所有数据都如预期的那样被接收。re_data 是一个开源的 dbt 包,允许您在列和表级别进行监控。它包括行数、新鲜度和空计数等指标。

作者图片

对于某些指标,re_data 会计算平均值,然后使用指定的 z 得分来检测数据中的异常。当数据量超出该 z 值时,您将收到警报。你可以通过查看这个帖子来了解更多关于如何设置这些的信息。

抢先股东一步。

如果出了问题,在他们自己弄清楚之前说点什么!完全透明会大有帮助。如果你在事情破裂时开诚布公,你们会建立更好的关系,而不是试图掩盖问题,让他们自己发现。

这与设置测试以从源头上提醒您问题是齐头并进的。如果您在下游的任何事情受到影响之前收到警报,您可以找到根本原因并修复问题,或者您可以在其他团队使用下游模型或仪表板之前让他们知道。

在您的组织内创造透明度是重中之重。我知道当一切都不顺利的时候会很沮丧。从头开始构建新的数据文化很容易隐藏所有的问题。但是,这需要时间。你可以建立信任,同时也要传达这不是一个一蹴而就的过程。

教育你的数据和事情是如何做的。

甚至对业务团队进行数据流程培训也有助于建立数据的透明度和可信度。如果你能帮助他们理解你试图解决的所有问题,当他们的仪表板坏了时,他们可能会更感同身受。

这幅冰山图解释了数据团队的工作,再准确不过了。

作者图片

业务团队知道他们认为我们在做什么,但通常与我们实际做的相差甚远。所以让他们看看你到底在做什么!与他们分享你的未来路线图和目标。

我们最近在 Slack 上开设了一个洞察频道,公司内部的任何人都可以加入。在这里,我们分享了我们的路线图和每个计划背后的为什么。想想你的最终用户和他们关心的。如果你以一种对他们重要的方式来看待事情,他们会在整个现代化过程中更加宽容。

在与业务用户分享见解时,您应该记住什么?

  • 解决当前的问题
  • 解释为什么它们的关系
  • 用外行的话来说;他们可能不明白什么是仓库或管道。相反,使用诸如“数据存储在哪里”和“数据如何移动”这样的短语。

结论

在利益相关者和您的数据之间建立信任就像在任何关系中建立信任一样。一旦这种信任被打破,就很难重新建立起来。通过测试、透明度和教育,从一开始就慢慢地建立对数据的信任。这将创造开放和诚实的沟通,并在出现任何问题之前为问题留出空间。

与我一起阅读 Substack 上的独家分析工程文章以及双周简讯,重点介绍该领域的最佳资源和主题。

向 ONNX 导出神经网络的最佳实践

原文:https://towardsdatascience.com/best-practices-for-neural-network-exports-to-onnx-99f23006c1d5

Artem Sapegin 在 Unsplash 上拍摄的照片。

ONNX 是一种开放格式,用于表示机器学习模型。ONNX 定义了一组通用的运算符——机器学习和深度学习模型的构建块——和一种通用的文件格式,使 AI 开发人员能够将模型与各种框架、工具、运行时和编译器一起使用。

onnx.ai

为什么要导出到 ONNX?

将您的模型导出到 ONNX 有助于您将(经过训练的)模型从项目的其余部分中分离出来。此外,导出还避免了对 python 解释器、框架版本、使用的库等环境的依赖。导出的 ONNX-model 可以存储模型的架构和参数。这意味着向您的同事发送一个文件来交换您的模型就足够了。

出口

我们的经验表明,出口 PyTorch 模型更容易。如果可能的话,选择 PyTorch 源,并使用内置的torch.onnx模块进行转换。或者,您可以使用较新的独立onnx python 包(在下面的代码示例中,只需用onnx替换torch.onnx)。

来自 PyTorch

PyTorch 模型只能以编程方式导出:

请注意,PyTorch 在运行时计算计算图,因此转换引擎需要一批正确的形状(数据在大多数情况下可以是随机的),它将通过网络来理解架构。torch.onnx使用torch.jit.trace找到您的数据通过网络的路径。

最佳实践:

  • 小心TracerWarnings。这可能表明追踪器无法跟踪您的批次。
  • 如果您在运行时做出路由决策,请确保使用一个批处理/配置来处理您尝试导出的所有路由。
  • 如果你需要做路线决定,你应该在张量上做。tracer 找不到 Pythons 的默认类型。
  • 如果您正在使用 torchhub 模型,请检查它们是否提供了一个exportable参数(或类似的)来替换不兼容的操作。

来自 TensorFlow (1/2/lite/js/tf。Keras)

我们推荐微软[tf2onnx](https://github.com/onnx/tensorflow-onnx)包用于 TensorFlow 模型的转换。在 ONNX 导出之前,必须将模型存储为 TensorFlows 支持的文件格式之一。支持的格式包括saved modelcheckpointgraphdeftflite

保存的模型文件导出到 ONNX:

python -m tf2onnx.convert --saved-model tensorflow-model-path --output model.onnx

这是为 tflite 所做的事情(或者使用 tflite2onnx ):

python -m tf2onnx.convert --opset 13 --tflite tflite--file --output model.onnx

对于其他格式,您需要提供输入和输出张量的名称。tf2onnx 将使用它们来跟踪网络。提供错误或不完整的标签列表可能会导致导出损坏。如果不知道模型的输入和输出节点名,可以使用summary _ graphtensor flow 实用程序。以下是安装和使用它的方法:

或者,从显著位置检查源项目或询问原作者。对于下面的例子,我们假设有两个名为input0:0,input1:0的张量流输入和一个名为output0:0的输出。

对于检查点格式:

python -m tf2onnx.convert --checkpoint tensorflow-model-meta-file-path --output model.onnx --inputs input0:0,input1:0 --outputs output0:0

对于 graphdef 格式:

python -m tf2onnx.convert --graphdef tensorflow-model-graphdef-file --output model.onnx --inputs input0:0,input1:0 --outputs output0:0

注意:导出供重用的模型(如 TensorFlow Hub 模型)不可使用summarize_graphs进行分析,可能根本无法导出。

潜在的出口障碍

ONNX 协议或所使用的转换器可能不支持源模型的所有操作。

可能的解决方案:

  • 检查是否有更新的操作集版本 (opset)支持您的相关操作。
  • 检查不支持的操作是否可以被支持的操作替换,例如,通常用ReLU替换HardswishSiLU激活(从 opset 11 开始)。
  • 如果您的转换器没有映射该操作,但 ONNX 协议支持该操作,请实现映射或尝试不同的转换器。
  • 如果 ONNX 协议不支持该操作,请尝试使用支持的操作重写您的操作,或者使用支持的操作实现映射。另外,考虑向 ONNX 提交一份 PR。

验证导出的模型

我们建议使用 PyTorch 加载模型,并使用内置的验证引擎。

该代码块将只验证模式。这并不保证您的架构是完整的,也不保证所有的参数都被(正确地)导出。因此,我们建议您在几个样本上运行推理,并将它们与您原始框架的推理进行比较。请注意,由于导出过程和潜在的不同执行框架,可能会略有不同。要使用推理,请确保安装带有piponnxruntime python 包或您的 python 包管理器。

此外,您可以通过使用像 Netron 这样的外部工具可视化导出的模型来运行健全性检查。Netron 还允许您浏览存储的参数。请记住,转换器可能会根据需要减少或扩大操作,因此可能会触及原始架构。注意: Netron 在加载大型模型时可能会有问题。

至理名言

无论您是否选择 ONNX,都要考虑发布您训练好的模型,尤其是如果您正在准备一份科学出版物。这有助于他人轻松复制你的发现,也为他们的项目提供了一个良好的开端。

这项工作得到了德国巴登-符腾堡州(MWK)科学、研究和艺术部的部分资助,资助项目为 32–7545.20/45/1机器学习应用的质量保证(Q-AMeLiA)

组织 Synapse 工作区的最佳实践

原文:https://towardsdatascience.com/best-practices-for-organizing-synapse-workspaces-977fe14b1fdb

对于简化的数据分析工作流,需要记住一些注意事项

Azure Synapse Analytics 原名 Azure SQL Data Warehouse,是一款具有企业数据仓库功能的大数据分析解决方案。它为不同的工作负载提供不同类型的计算环境。最常见的是 SQL compute,它有两种风格:无服务器和专用。另外两个计算选项是 Spark 和 Data Explorer(时序)。数据工程师可以根据自己的业务需求选择计算环境。

工作空间考虑和配置

计算环境依赖于一个“主”存储帐户,并在一个工作区的边界内一起管理,该工作区是一个集中处理所有工件的地方。这些工件可以是 SQL 脚本、笔记本、拼花文件和数据库。在为大规模环境规划 Azure Synapse Analytics 部署时,有一些常见的决策点会影响您如何创建和组织 Synapse 工作区:

  • 团队结构和职责:在给定用例、数据分离或成本管理需求的情况下,您的数据工程团队在功能项目上的组织和协作方式。职能界限和灵活性问题会导致团队分离,从而产生更多的工作空间。
  • 发布工作流:您的开发和发布工作流可能需要额外的环境:开发、测试和生产的分离。
  • 区域:您的数据的位置以及您需要为其提供分析解决方案的受众。这种区域界限可能导致实施更多的工作空间。
  • 安全性:所有权、安全性或法律界限可能会迫使团队相互分离。比如有些数据是永远不允许其他团队看到的。

在实施多个工作空间时,您需要在成本管理、生产效率和管理复杂性之间找到平衡。这种平衡可能导致不同的解决方案模式。例如,当活动仅与实验或 R&D 相关时,您可以选择只使用一个工作空间。这种模式减少了你的 Azure 足迹,降低了你的成本。另一种解决方案模式是为每个职能领域或项目创建工作区,这简化了协作、成本报告和预算要求。例如,一个包含四个不同工作负载的设置可能会创建 12 个工作区实例,每个工作负载都需要一个发布工作流。

多工作区单湖拓扑

另一个重要的驱动因素是工作和共享数据方式的凝聚力和效率。当您的团队有不断共享大型数据集的趋势时,您应该考虑多工作区单湖拓扑

多工作区-单湖拓扑(约万·波波维奇)

这个模式首先由 Jovan Popovic 描述,它极大地简化了管理、共享和安全配置。基本思想是,您有一个 Azure 数据湖存储实例和多个指向它的工作区实例。在这种情况下,隔离是通过使用单独的文件夹并将文件夹与工作区对齐来执行的。最佳实践是使用工作区名称作为文件夹名称。

组织和管理数据湖中的数据

在湖中组织数据时,必须考虑文件夹结构和文件格式的不同。许多组织使用不同的文件夹、容器或存储帐户对数据进行分层。在实现工作空间之前,在将数据放入数据湖之前,规划出数据的结构是很重要的。因此,如果您的进程主要读取按数据生命周期组织的数据,您应该考虑使用 /raw/、/enriched、/curated 文件夹结构。在每个文件夹中,您可以有不同的文件夹: /application01、/application02、等等。如果您的流程主要读取每个主题领域的数据,您可以考虑 /sales、/manufacturing 等等。您也可以混合这些分层模式。例如,带有 /raw/enriched 的数据仍然是与源系统对齐的,而 /curated 中的数据是与消费者对齐的。

典型的数据湖结构(鸣谢:Piethein Strengholt)

在组织数据时,您还应该考虑访问控制模型。例如,容器只允许粗粒度的访问,因此继承了对文件夹和文件的访问。ACL 允许对数据集进行更细粒度的访问控制,但也带来了更多的管理开销。理想的数据湖结构在良好的逻辑分离和解决访问管理的开销之间取得平衡。

您应该考虑的另一个问题是如何应用数据生命周期管理。许多组织仅保留其完整数据加载的几个旧版本。他们通过建立缓慢变化的维度来压缩数据。回滚的典型时间窗口是 30 天。另一个考虑是审查交付模式。一些源系统保留了所有数据的完整历史,而其他系统只提供事务性数据。这种数据传递要求您执行覆盖或仅追加。这可能会导致更复杂的数据管道,这个主题您将在后面了解更多。

我选择什么样的数据格式?

数据通常以不同的文件格式到达,因为数据来源和接收文件格式的系统有很强的依赖性。您的一些源系统很容易提取,并直接允许现代文件格式的创建,如 Parquet 或 Delta。其他系统对传统导出功能的依赖性更强,例如,只能交付 CSV 或 XML 文件。因此,您的第一层摄取通常包含不同的文件格式。然而,当进一步处理时,人们非常喜欢允许更密集的查询模式的文件格式。对于 Synapse,您还必须注意其他一些事项:

  • 目前,只有无服务器池支持增量文件。如果您打算使用外部桌子作为专用池,那么您应该考虑使用拼花地板。
  • 当用 Spark 处理时,你可以创建 Spark 湖数据库。这些数据库可以使用无服务器 SQL 池进行查询,但是它们是只读的。
  • 当使用无服务器 SQL 创建 Lake 数据库时,这些数据库在 Spark 中是不可见和不可用的。

数据处理方法

你推荐什么样的数据处理工具或方法?这个问题的答案主要归结为你喜欢什么。以下是一些实地观察结果:

  • 只需要提取和加载或简单转换的团队通常在 Azure Data Factory(Synapse Pipelines)内部处理。
  • 需要复杂转换但不喜欢编码的团队通常使用数据流或调用存储过程。
  • 需要复杂转换、喜欢编码的团队通常使用 Databricks 或 Azure Synapse Spark Pools。编排通常由 Synapse 管道完成。
  • 喜欢将转换和依赖作为元数据或 YML 文件来管理的团队经常使用外部工具,如 DBT

一般建议是尽可能消除手动步骤。一个好的自动化数据管道可以实现平滑和可扩展的转换,而无需任何手动操作。另一个建议是对重复的转换步骤应用模块化设计。某些块,例如处理历史数据的块,应该被转换成一个可重用的模块(元数据驱动的摄取框架),然后可以应用于多个数据管道。最佳实践是使用几个代码库,并将功能设计与通用数据处理功能分开。另一个最佳实践是使用参数化。参数化允许更好的测试和更快的重新运行开发、测试和生产。

结论
组织您的工作空间和数据湖存储账户,使您在成本管理、生产效率和管理复杂性之间取得正确平衡,这一点很重要。不正确的体系结构可能会造成混乱,数据可能会在环境之间不断复制,或者访问管理成为一场噩梦。当许多团队依赖相同的数据时,尝试考虑多工作区单湖拓扑,或者当团队的目标发生冲突时,尝试使用多个工作区分开。

可视化聚类结果的最佳实践

原文:https://towardsdatascience.com/best-practices-for-visualizing-your-cluster-results-20a3baac7426

集群可视化和解释的成熟技术

图片由作者提供。

C 聚类是数据科学中最流行的技术之一。与其他技术相比,它非常容易理解和应用。然而,由于聚类是一种无监督的方法,因此对你来说识别对你的商业客户来说可理解的不同聚类是一个挑战

目标

本文为您的下一个集群项目提供了可视化最佳实践。您将学习最佳实践用于分析诊断您的聚类输出正确可视化您的聚类PaCMAP 降维,以及呈现您的聚类的特征。每个可视化都带有它的代码片段。你可以把这篇文章作为参考指南。

因为我的上一篇关于集群的文章已经涵盖了一些技术细节和解释,所以我将在这里保持简短的解释。

聚类选择和诊断

让我们从头开始。在您分析任何聚类特征之前,您必须准备您的数据并选择适当的聚类算法。为了简单起见,我们将使用众所周知的葡萄酒数据集并使用 K 均值模型。然而,本文中展示的大多数可视化可以用于任何聚类算法。

上面的代码加载葡萄酒数据集,并使用标准缩放器来缩放整个数据集。

为了确保我们的集群以后的可视化总是使用正确和相同的颜色,我们定义了一个由六种不同颜色组成的列表(图 1)。

图一。定义的聚类颜色。图片由作者提供。

确定 k 个簇的正确数量

有几种方法可以确定(直观地)正确的聚类数。在下文中,我们将使用肘图方法、(平均)轮廓评分方法和轮廓分析

肘法

为了得到一个全面和适当的肘图可视化,我推荐使用 黄砖pip install yellowbrick。下面的代码将产生如图 2 所示的图形。

输出还画出了一个建议(虚线),您应该选择哪个 k。如果它不能确定一个正确的数字,它将显示一个警告。

图二。肘图。图片作者作者

剪影评分

另一种确定聚类数量的方法是轮廓评分法。下面的代码绘制了图 3 中的输出。

导出的结果(3)与肘图法的结果相同。

图 3。轮廓评分法结果。图片由作者提供。

剪影分析

最后但同样重要的是,我们可以使用剪影分析方法来确定最佳聚类数。这个想法和方法在这篇 sklearn 文章中有很好的解释。

上述文章中提供的代码绘制了每行一个轮廓图。然而,当你有大量的集群并且想要比较它们的相关轮廓图时,这可能是非常不清楚的。所以我写了下面的代码让每行绘制三个图表,让后面的对比(图 4) 清晰很多

图 4。剪影分析。图片由作者提供。

在尝试了几种直观地确定 k 个集群的正确数量的方法之后,我们决定继续使用 k=3 来构建我们的集群。

集群诊断

下一步是根据它们的大小基数来诊断我们的集群。

如果你不熟悉这些术语,可以看看我的文章

为了创建下面的图(图 5),我们将使用data-science-utils包,它可以和pip install data-science-utils一起安装。

图 5。集群基数和数量级。图片由作者提供。

集群可视化

为了在 2D 空间中可视化我们的集群,我们需要使用降维技术。许多文章和教科书都使用 PCA。最近的博客文章也推荐像 t-SNE 或 UMAP 这样的方法。然而,这里有陷阱和误解

****长话短说:使用这些降维方法时,需要在保留局部结构和保留全局结构之间进行权衡。虽然 PCA 保留全局结构,但是它不保留邻域或局部结构。另一方面, t-SNE 和 UMAP 保留了局部结构而不是全局结构。

然而,有一种相对较新的技术声称保留局部和全局结构** : PaCMAP 。**

PCA 和 PaCMAP 将在下文中用于在 2D 空间中可视化我们的聚类。

如果你想了解更多关于不同特征和 PaCMAP 的信息,请查看为什么你不应该依赖 t-SNE、UMAP 或 Mathias Gruber 的 TriMAP

运行代码后,您应该会得到下面的图(图 6):

图 6。使用 PCA(左)和 PaCMAP(右)进行聚类可视化。图片由作者提供。

集群特征

现在让我们把重点放在如何形象化呈现每个集群的关键特征上,这样一个商务人士就可以很容易地理解每个集群代表什么。

在此之前,我们必须用一个集群列来丰富我们的标准化(X_std)和非标准化(X)数据。

箱线图

第一种非常简单的方法是为每个特征生成一个箱线图,以显示在每个聚类中的分布。****

为了绘制下面的结果(图 7),我们使用了非标准化数据 X** 。使用标准化结果(X_std)会使更难为商业用户解释,因为其比例和单位已经改变。**

图 7。用箱线图可视化聚类结果。图片由作者提供。

数据准备

在我们继续之前,我们必须为下面的可视化准备数据。下面的代码帮助我们更好地比较我们的集群。

首先,我们计算每个聚类的每个特征的平均值 ( X_meanX_std_mean ),这与上面的箱线图非常相似。

其次,我们计算每个聚类的每个特征的相对差异(以百分比计)与每个特征的总体平均值(聚类无关)(X _ dev _ relX_std_dev_rel )。这有助于读者看到每个聚类中的差异与每个特征的总体平均值相比有多大。****

图 8 举例说明了在准备步骤之后我们的数据是什么样子的。

图 8。每个数据准备步骤的结果。图片由作者提供。

现在我们已经有了正确的数据,我们可以继续我们的可视化。

条形图

为了可视化相对差异,我们可以使用柱状图。以下代码绘制了每个要素的每个分类的差异。

结果如图 9 所示。

图 9。相对聚类差异用条形图显示。图片由作者提供。

如果你想展示每个星团的细节,上面的图很棒。然而,在许多情况下,在一个图表中总结所有相关结果和特征也是有意义的。下面的解决方案是做到这一点的一种方法。

我们在图 10 中直观地显示了每个特征相对于每个聚类的总体平均值的相对偏差。

图 10。在一个条形图中总结集群特征。图片由作者提供。

雷达图

在一个图中总结所有相关信息的另一种方式是使用雷达图。下面的代码绘制了标准化数据的计算平均值(X_std_mean)。****

如果我们使用非标准化版本,不同的标度会破坏可视化(例如,脯氨酸的平均值远高于灰分的平均值)。因此,我建议用相同的单位或至少在相似的数值范围内绘制数值。最终结果如图 11 所示。

图 11。用雷达图可视化集群特征。图片由作者提供。

结论

本文的目标是为您提供集群诊断、可视化和解释方面的最佳实践。在 2D 空间中绘制聚类时,请考虑 PaCMAP。可以从不同的角度呈现聚类结果或特征。一种想法是显示每个聚类的每个特征的平均值。另一个选项是计算每个聚类的每个变量与每个特征的总体平均值的相对差异。向企业展示结果时,最好使用一个图表(例如,显示的雷达图或第二个条形图)。如果您想要调查每个聚类的每个要素的特征,可以使用多个图(例如,与 UX 设计师进行深入探讨)。

来源

UCI 机器学习知识库:葡萄酒数据集知识共享署名 4.0 国际 (CC BY 4.0)许可”。

王英凡,,辛西娅·鲁丁,亚龙·沙波什尼克,《理解降维工具如何工作:解读 t-SNE、UMAP、TriMAP 和 PaCMAP 用于数据可视化的实证方法》(2020),【https://arxiv.org/abs/2012.04456】T4

编写可复制和可维护的 Jupyter 笔记本的最佳实践

原文:https://towardsdatascience.com/best-practices-for-writing-reproducible-and-maintainable-jupyter-notebooks-49fcc984ea68

让您的 Jupyter 笔记本变得更棒的简单步骤

格伦·卡斯滕斯-彼得斯在 Unsplash 上拍摄的照片

介绍

编写可复制、可维护、易于理解的 Jupyter 笔记本并不像您想象的那么容易。其实完全相反。其实真的很难。在本文中,我将解释为什么这么难,并为您提供一些帮助我实现更好的可再现性和可维护性的最佳实践建议。

什么是 Jupyter 笔记本

首先给大家简单介绍一下什么是木星笔记本。基本上,Jupyter 笔记本是一个交互式文档。您可以使用 Markdown 语法编写纯文本,也可以通过 LaTeX 语法使用数学公式。此外,您可以向笔记本添加代码,读者可以执行这些代码来产生一些输出,如可视化。

例如,您可以可视化一个基于各种参数的函数。读者可以使用滑块来更改这些参数的值,并且每次值更改时可视化都会更新(参见下面的示例)。您还可以添加代码,对一些数据库执行实时查询,以获取最新的销售数据,并将其绘制在饼图中。

交互式笔记本允许读者通过滑块改变参数。

代码通常是用 Python 编写的。但是支持 Java、R、Julia、Scala 等 100 多种编程语言。笔记本可以在浏览器中编写和执行。然而,即使在浏览器中写笔记本是可能的(存在基本的代码完成),它也是有限的。幸运的是,您还可以使用 ide,如 Visual Studio Code 或 PyCharm(在专业版中),它们提供了更强大的功能。

也可以使用云服务来编写和执行笔记本。例如,谷歌提供了一个解决方案,允许你在云端运行笔记本,并与每个人分享。甚至 GPU 也可以在 Google Colab 中免费访问,用于密集型计算任务。

Jupyter 笔记本示例

下面你可以看到一个 Jupyter 笔记本的例子,它解释了如何用蒙特卡罗方法近似计算 π 。你可以看到三个细胞。第一个单元格是减价单元格,它给出了笔记本的介绍。它包含文本、动画和一些简单的数学方程式,通过 MathJax 渲染。

第二个单元格是包含 Python 代码的代码单元格。如果执行这个单元格,代码会生成一个简单的正方形内的圆的绘图。

第三个单元格也是一个 Markdown 单元格,当执行该单元格时,它在编辑模式和呈现结果之间跳转。在编辑模式下,你可以看到简单的降价文本。

Jupyter 笔记本,带有代码和用蒙特卡罗逼近圆周率的减价单元格

Jupyter 笔记本已经变得流行

Jupyter 笔记本已经非常受欢迎。2020 年 10 月,GitHub 上大约有1000 万台公共笔记本。出于教育目的,笔记本在学术界尤其流行。此外,数据科学家大量使用笔记本电脑进行数据分析和探索性任务。

尤其是文本和代码的结合使它们非常有趣。它允许作者用表达性公式解释 Markdown 中的概念,同时可以在同一文档中以代码的形式显示实现。Jupyter 笔记本电脑的这一独特特性可以更好地再现研究成果和传播教育内容。

朱庇特笔记本批评

尽管 Jupyter 笔记本很受欢迎,但它一直受到批评(例如我不喜欢笔记本),因为你编写代码的方式可能会导致坏习惯。以下是一些例子:

笔记本命名

有时可以观察到的一个反模式是笔记本没有表达性的名称。相反,笔记本的名字有时以“无标题”开头或以“-Copy”结尾。这是由于浏览器中运行的 Jupyter 的默认行为。每次创建新笔记本时,都会创建一个无标题笔记本;每次创建现有笔记本的副本时,新笔记本都会带有后缀“-Copy”。

如果这是 Jupyter 的默认行为,您可能会认为许多笔记本名称都有这种反模式的问题。但令人惊讶的是,正如一项研究[1]所发现的,该研究分析的笔记本(从 GitHub 下载的笔记本子集)中,只有不到 2%的笔记本实际上有“无标题”,只有不到 0.7%的笔记本名称中有“-Copy”。所以这似乎不是什么大问题。

然而,同一项研究还发现,几乎 30%的受检笔记本的名称中含有 POSIX 完全可移植文件名指南不推荐的字符,该指南只允许使用字符[A-Za-z0–9。-_].具有不可移植文件名的笔记本可能会在某些系统上造成问题,因此应该避免使用。

不明确的执行顺序

Jupyter 笔记本中的单元格可以任意顺序执行。不必从第一个单元格开始,到最后一个单元格结束。您也可以从第二个单元格开始,跳回第一个单元格,然后执行第三个单元格。您也可以连续多次执行单元格。

这也是编写笔记本时的一种常见行为,因为通常你在一个单元中编写一些代码,执行该单元,然后一次又一次地修改和执行该单元,直到你对该单元的结果满意为止。有时,您还需要返回到以前执行的单元来重新初始化变量,或者因为您需要修改以前定义的函数。

因此,有时很难遵循执行顺序,这可能会对笔记本的再现性产生负面影响。

在之前引用的研究[1]中,发现模糊的执行顺序是许多笔记本的一个问题。14%的分析笔记本存在此问题。

让我们来看一个相关的例子,它演示了一个额外的问题。在下面你可以看到 4 个细胞。

首先,执行第一个单元格,这样就定义了函数 f。然后执行第二个单元格,之后变量a的值为 6。接下来,您正在编辑第三个单元格,但是决定稍微修改函数f的代码。函数应该将变量x增加 2,而不是将变量x增加 1。更改函数后,再次执行第一个单元格以使更改生效,然后执行第三个单元格。变量b将得到值 7。以下单元格正在处理这两个变量。

如果您与其他人共享此笔记本,这些人将无法复制您的结果,因为他们只能看到将变量x增加 2 的f版本。如果这些人一步一步地执行笔记本,变量ab的值将分别为 7,而不是 6 和 7。

缺少模块化

在[1]中发现

  • 只有 10%的分析笔记本有本地导入(即存储在存储库目录中的模块的导入)
  • 54%的笔记本定义了功能
  • 不到 9%的笔记本定义了类别

这些结果表明,模块化并不经常用于 Jupyter 笔记本电脑。这很有趣,因为模块化在软件工程中是一种成熟的模式,有很多好处。它有助于

  • 减少代码(例如,减少复制和粘贴)
  • 将复杂的代码分割成更容易理解的小块
  • 编写更容易测试的代码
  • 减少笔记本中全局变量的数量,这样可以降低内存使用量(局部变量更容易被释放,因为它们只存在于局部范围内)

然而,不使用模块化有很多原因。例如,没有功能的笔记本可能足够简单,因此不需要这种抽象。笔记本也可能不使用模块,因为作者想简化笔记本的发行。如果只需要分发一个文件,而不是多个文件(如果代码被移动到模块中,就会出现这种情况),这就更容易了。

遗漏测试

在软件工程中,测试是一种常见的实践。存在各种测试策略,例如集成测试、回归测试或单元测试。你可以在维基百科上找到关于不同方法的很好的概述。例如,单元测试是自动测试,它测试软件的小部分,通常是单个功能。

有各种框架可以用来编写单元测试。对于 Python 来说,模块 unittest 已经被很好地建立并且易于使用。根据[1],只有少数笔记本电脑(不到 2%)导入了众所周知的测试模块,这可能是测试未被广泛使用的一个指标。

尽管没有测试通常是软件工程中的反模式,但对于大多数笔记本电脑来说,这可能是合理的。许多笔记本用于数据分析和探索,以检验假设,或用于教学目的,以演示一些东西。为这些应用程序编写测试通常没有多大意义,因为在分析和探索任务的情况下缺少基本事实,或者因为演示产生了预期的结果。

缺少依赖关系

Jupyter 笔记本通常依赖于各种库和包。在 Python 中,这些依赖关系是通过关键字import导入的。没有为导入指定版本,因此,不可能通过查看导入来识别包的所需版本。但是,如果依赖项的版本没有记录在其他地方,并且没有简单的方法来安装所有必需的依赖项,人们可能会在执行笔记本时遇到问题,因为他们可能无法设置运行笔记本所需的环境。

此外,依赖项不需要在笔记本的开头导入。相反,依赖项可以被导入到任何地方。因此,仅仅通过查看笔记本的开头可能很难确定所有需要的依赖项。相反,您必须扫描完整的代码。

最后,导入的名称可能不同于需要安装的包的名称。例如,为了解析 YAML 文件,PyYAML 被广泛使用。这个包可以通过pip install pyyaml安装。然而,要使用这个包,您必须导入yaml

根据[1],许多笔记本没有声明模块依赖关系。

数据不可访问

许多笔记本电脑都需要数据才能工作。例如,关于机器学习的笔记本通常需要一个用于训练的数据集。验证集用于确定模型对未知数据的性能。如果这些数据没有随笔记本一起分发并且不存在,笔记本的结果就不能被复制。

根据[1],数据的不可访问性是执行笔记本时出现错误的一个常见原因,已经确定了两个主要原因。要么数据根本不存在,要么在数据随笔记本分发的情况下,使用绝对路径来访问数据。

再现性有限

Jupyter 笔记本的一个基本理念是让结果具有可重复性。这个想法是笔记本在科学界如此受欢迎的原因之一。在这里,可再现性很重要,因为越容易再现结果,结果就越有可能产生新的见解,因为其他人可以重用和构建您的工作。

然而,根据[1]的说法,GitHub 上的许多分析笔记本无法重现。结果显示,只有 22%到 26%的笔记本可以成功执行,甚至只有 4.9%到 15%的笔记本产生相同的结果。

我们已经讨论过不可复制笔记本的三个基本原因:

  • 缺少依赖项
  • 无序执行(以及由此产生的隐藏状态)
  • 数据不可访问性

最佳实践

为了确保笔记本易于理解、维护和重复使用,并进一步增加笔记本结果重现的可能性,以下建议可能非常有用:

  • 为您的笔记本使用表达性的名称来描述您的笔记本正在做什么,并且只使用 POSIX 完全可移植文件名指南中包含的字符。
  • 避免不明确的执行命令。为了确保您的笔记本可重现并创建预期的结果,请在共享笔记本之前重新启动内核并执行笔记本的所有单元。
  • 如果合理,使用模块化(即模块、函数、类)。
  • 如果合理,使用测试框架来测试你的代码。
  • 确保笔记本中使用的所有数据都与笔记本一起分发(或者至少可以下载),并且使用相对路径来访问数据。
  • 创建 requirements.txt 来固定所有使用的依赖项的版本,并在笔记本的开头导入所有依赖项。
  • 分发笔记本及其输出。这使得重现结果更加容易,因为每个执行笔记本的人都可以验证结果是相同的。
  • 不要重新定义变量。

结论

Jupyter 笔记本很容易写,但各种研究表明,似乎很难写出可复制的笔记本。但是,如果您遵循一些常见的最佳实践,您的笔记本确实更有可能被复制,并且其他人可以在您的伟大工作的基础上进行构建。这些最佳实践也帮助我写出了好的笔记本。在我看来,最重要的是避免模糊的执行顺序,提供一个使用过的依赖项及其版本的列表,并使笔记本中使用的数据易于访问。

参考

[1] Pimentel,joo feli PE 等人,“了解和提高 Jupyter 笔记本的质量和再现性。”实证软件工程 26.4(2021):1–55。

客户终身价值(LTV)模型的 ML 可观察性的最佳实践

原文:https://towardsdatascience.com/best-practices-in-ml-observability-for-customer-lifetime-value-ltv-models-c5a2fc063f4c

图片作者(艾瑞泽艾)

客户终身价值(LTV)模型的 ML 可观察性的最佳实践

提高 LTV 模型生产性能的技巧

古老的谚语仍然适用:客户永远是正确的,但有些人比其他人更正确(这是谚语,对不对?).虽然在任何组织中有许多指标可以评估客户的价值,但在评估公司整体销售活动的健康程度时,客户终身价值(LTV) 是一个需要考虑的重要指标。特别是在像包装消费品或零售这样的行业中,客户与企业的关系本质上是非契约性的,找到一种预测未来购买行为的方法对于提高组织的整体盈利能力至关重要。

客户终身价值就是客户在整个生命周期内与企业进行的交易或购买的总货币价值。这是对客户对公司价值的一种衡量,不仅仅是基于单次购买,而是在整个关系中。

简史:LTV 计算在 20 世纪 80 年代由沃顿商学院的 Peter Fader 博士首先创建,在 21 世纪初普及,并被翻译成机器学习模型,方便地打包到开源库https://pypi.org/project/Lifetimes/**。由于 LTV 是基于预测来评估参与模式的,因此使用 ML 是一个自然的选择。

LTV 模型被广泛应用于各种规模的行业来评估客户的预期价值。像任何估计一样,预测可能会出错。了解这些预测如何与现实世界结合以微调您的模型来获得最佳性能至关重要。

事实真相:数学形式的 LTV

LTV =((平均销售✕购买频率)/流失率))✕利润率

  1. 平均销售额=(总销售额)/(订单总数)
  2. 购买频率=(订单总数)/(唯一客户总数)
  3. 流失= 1 -保留率
  4. 利润率=基于业务环境

LTV 的意义

从提高整体业务盈利能力到预测创业收入,LTV 的主要应用围绕规划和预算。

根据 Criteo 的一项调查,81%的营销人员说监控 LTV 可以促进销售。在营销度量一书中提到,卖给新的潜在客户的概率是 5%–20%,而卖给现有客户的几率是 60%–70%。因此,通过预测 LTV,你可以让你的团队了解如何获得新客户并留住现有客户,同时保持可观的利润率。更具体地说,LTV 可以帮助组织:

  • 定义营销目标
  • 了解参与的正确渠道
  • 计划支出以降低采购成本,并保持较高的保留率
  • 寻找具有相似特质的顾客
  • 获得客户反馈
  • 提升客户忠诚度

如果使用得当,LTV 模型可以在商业中发挥重要作用。由于 LTV 模型的预测有助于构建和定义业务范围内的目标,因此对这些模型进行监控和故障排除以确保它们在生产中发挥最佳性能至关重要。

监控 LTV 模型的挑战

那么,什么会出错呢?LTV 的主要关注点是寻找、培养和留住在一段不确定的时间内创造最大价值的客户:消费者的生命周期。因此,LTV 模型必须迭代并快速估计长期价值,而实际数据却明显滞后或根本没有。

图片来源: ML 监测与可观测性电子书,艾瑞泽 AI

在生产中监控模型时,设置适当的基线对于测量漂移和检测生产中模型行为的偏差是必不可少的。通常,使用训练或验证数据来设置基线,以测量不同环境中特征值分布、模型预测和基本事实的变化。如果没有有效的监控和可观察性工具来评估延迟地面实况场景情况下的代理指标,解决 LTV 模型在生产中的性能下降问题可能会被证明是昂贵且耗时的。

如何对 LTV 模型使用监控和可观测性

根据前面提到的 Criteo 调查,采用 LTV 的最大障碍之一是它太复杂,难以监控。虽然这是历史上的情况,但实现 ML 可观测性可以显著地消除这一困难。

监控 LTV 模型的一个主要挑战是发现代理指标来代替延迟的实际值。LTV 模型通常在相当长的时间内评估客户的价值,因此将基础数据映射到模型的预测可能是一个挑战(在下面的部分中有更多关于代理指标的信息)。

为了让从业者在生产中轻松部署和改进他们的 LTV 模型性能,他们的 ML 可观测性平台应该:

  1. 设置基线监视器
  2. 评估特征、模型和实际/地面真实漂移
  3. 衡量模型性能

监控 LTV 模型

积极改进 LTV 模型的第一步是在生产中监控您的模型。那么,监督意味着什么呢?一个有效的 ML 监控平台应该自动配置最适合您的数据的监控器,以主动检测漂移、数据质量和性能问题。手动设置阈值和创建监视器的日子已经一去不复返了。

通过特征和模型漂移验证模型性能下降

为了了解您的 LTV 模型的性能下降,监控功能和模型漂移是有益的。这意味着在各种环境和版本之间分析您的模型,以识别 LTV 模式和异常分布行为。通过利用覆盖了您选择的指标的随时间漂移小部件,模型性能管理变得更加高效和可行。虽然 LTV 模型的主要问题之一是延迟或没有真实数据,但您可以使用漂移作为代理指标来分析您的模型的相对表现。

要测量的漂移类型:

  1. 特性漂移:输入数据固有的变化(这是游戏的名字)。监控您的要素,以快速了解您的输入是否明显偏离您的模型的训练内容,以及您是否需要重新训练或构建新模型。
  2. 模型漂移:测量您的模型的输出是否如生产中预期的那样执行。根据以下因素发现您的模型是否有偏差:
  • 培训/验证环境
  • 历史生产时期
  • 滚动生产窗口

揭示不准确预测的根本原因

假设你的 LTV 模型漂移了。您的监控解决方案触发了警报,现在由您来找出问题所在。下一步是什么?您将希望快速找到问题的根本原因来解决问题,通常是通过重新训练模型并将其与生产中的现有模型进行比较。

这可以通过 ML observability 来实现,ML observability 可以使用性能热图进行更深入的分析,以便更好地了解和直观地了解从哪里开始对与您的 LTV 模型相关的问题进行故障排除。对于最简单的工作流程, ML observability 允许您使用特征/值组合深入研究低性能切片,以便了解如何改进您的模型。

模型性能

即使您对生产中的模型有最大的信心,积极地改进您的模型并监控模型性能有助于整体业务成果。

在 LTV 案例中,我们基于几个关键指标评估模型性能:

  1. 【均方根误差】(RMSE) : 模型预测值与实际值之差的平方根,经过平方并在整个数据集中取平均值。这种方法对重大错误给予更高的权重,因此,在企业可能希望严重惩罚大错误或异常值的情况下,这种方法可能会有所帮助。
  2. 平均绝对百分比误差(MAPE) : 衡量一个模型产生的误差的平均大小;MAPE 是 LTV 模型预测准确性的一个更常见的度量标准。
  3. 平均绝对误差(MAE) : 模型预测值与实际值之间的绝对值差,在整个数据集内取平均值。对模型性能的“第一印象”,因为它没有被一些预测的极端误差所扭曲。

当选择要测量的性能指标时,使用哪个指标没有统一的答案;它依赖于您的模型和它吸收的数据。一旦您决定了您的性能指标,每天或每周跟踪您的模型性能指标允许您确保性能不会从模型训练时或模型最初被提升到生产时急剧下降。ML 可观察性帮助您跟踪这些重要的指标,如果出现问题就触发警报,并通过自省来发现潜在的问题。

结论

通过发现漂移、了解模型性能并轻松识别潜在生产问题的根本原因,ML 工程师可以满怀信心地运输他们的 LTV 模型,并改善整体业务成果。虽然监控和观察生产模型似乎并不简单(剧透警告:事实并非如此),并且可能成为运输模型的障碍(确实如此), MLOps 工具链中的解决方案可以简化模型监控和故障排除。实现最大似然可观测性有助于确保 LTV 模型的预测通过自动监视器保持最佳状态,监视特征和模型漂移,并随时间可视化模型性能。作为额外参考,以下是 ML 可观察性平台如何帮助客户处理客户终身价值模型监控和可观察性。

联系我们

如果这篇博客引起了你的注意,并且你渴望了解更多关于机器学习可观察性模型监控,请查看我们的其他博客和关于 ML 监控的资源!如果您有兴趣加入一个有趣的 rockstar 工程团队,帮助模型成功生产,请随时联系我们,并在此处找到我们的空缺职位

成为优秀数据科学家或机器学习工程师的最佳实践

原文:https://towardsdatascience.com/best-practices-to-become-a-good-data-scientist-or-machine-learning-engineer-ce10a92f2674

学习由数据科学家和机器学习工程师完成的重要实践可以确保一个人在组织中产生高质量和有影响力的工作。

Unsplash 上由 Boitumelo Phetla 拍摄的照片

T4 有大量教授编程和数据科学基础的课程。他们在强化机器学习中的各种概念方面做得很好,并展示了在构建具有 ML 功能的项目时通常遵循的各种步骤。虽然这些课程主要关注机器学习的理论方面,但如果一个人学会在构建与数据科学和机器学习相关的应用程序时更加重视良好的实践,这可能会很方便。

随着数据的增长和计算能力的指数级增长,对利用数据并根据项目用例生成预测和有用见解的人的需求迅速增加。此外,还有许多与数据相关的职位,如数据工程师、数据架构师、数据科学家、深度学习工程师和机器学习工程师。这些职位通常需要对数据处理、特征工程、数据提取、加载和操作有很好的理解。对于数据科学家或机器学习工程师这样的职位来说,构建在测试数据(之前模型没有看到的数据)上表现良好的最先进的模型通常很重要。由于数据科学工作流程中涉及许多步骤,因此,在构建 ML 应用程序时,学习有用的实践变得非常重要。下面是数据科学家或机器学习工程师可以遵循的一些最佳实践,以构建更高质量的代码和更好的项目成果。

准确理解业务问题

肖恩·波洛克在 Unsplash 上拍摄的照片

伴随着大量的责任和方式,人们可能会在没有定义项目的最后期限商业目标的情况下被卷入潮流,这经常是真的。让事情变得复杂的是,项目中与 ML 相关的东西有一种可能是没有明确陈述或定义的。在这种情况下,通过理解项目的需求及其范围并了解机器学习的可行性来采取行动可能是好的。认识到这些关键措施,并承认一个人是否能够实际实施人工智能以及它是否能够在创造的价值中产生良好的影响,可以推动你在项目中的努力和影响。

从一个简单的指标开始

丹-克里斯蒂安·pădureț在 Unsplash 上拍摄的照片

在机器学习中有许多度量,例如在回归类型问题的情况下的平均绝对误差、均方误差、平均绝对百分比误差、均方根误差。如果我们考虑分类问题,我们有诸如精确度、召回率、准确度、f1 分数、微观 f1 分数、宏观 f1 分数和许多其他的度量。通过查看所有这些指标,可以确信在测试和理解这些模型时使用所有这些指标。尽管如此,由于数量庞大,接受正确的度量标准可能会变得很棘手。在这种情况下,最好的办法是根据问题选择高度可解释和可理解的简化指标。在了解了这个指标并分析了影响之后,就有可能将它添加到我们的 ML 中进行预测。

建立强大的数据科学团队

照片由尼克·费因斯Unsplash 上拍摄

数据科学是关于系统的通信动作自动化,从而减少人类的努力,帮助公司获得巨大的利润空间。为了构建具有人工智能功能的工具,重要的是与拥有大量知识和见解的团队合作,从数据收集、数据准备、训练模型到在云中部署服务,以便最终用户可以访问服务。换句话说,如果最终用户不消费数据科学家生产的产品,他们可能不会增加很多价值。因此,他们必须与一个拥有不同领域知识的团队合作,这样他们才能构建发布一个全功能的产品。

学会给商业利益相关者留下深刻印象

查尔斯·弗劳恩德在 Unsplash 上的照片

虽然机器学习和深度学习产品的技术能力令人印象深刻,但如果它们不能打动业务利益相关者,并且部署它们不会给组织带来很大的利润,它们可能几乎没有价值。我以上陈述的基本意思是,尽管我们从具有非常低的平均绝对误差、均方误差或任何此类误差的 ML 模型中获得了结果,但如果它们未能产生商业影响,尽管它们在技术上结构良好且可行,这意味着该组织不能基于人工智能的结果货币化。因此,必须根据部署带来的收入的整体增长、利润以及客户的参与度是否提高来定义问题。通过考虑这些因素,可以更好地定义项目的目标和结果,以及运行算法所需的额外基础设施支出。

传达您的结果

Miguel A. Amutio 在 Unsplash 上拍摄的照片

你已经花了大量的时间,比如说一个月的时间来收集额外的数据,产生关键的见解,并找到对 ML 模型有用的最重要的特征,并且通常来说确定结果,现在是时候向团队阐明你的结果,以便他们花必要的时间根据你的结果采取行动。尽管令人印象深刻的是,你已经花了大量的时间去理解业务问题,也学习了数据中最重要的特征,但是不能阐明你所学习和工作的东西通常会减慢项目的进度。因此,让团队了解你正在处理的领域以及工作成果是非常有用的。

部署后持续监控结果

照片由马库斯·温克勒Unsplash 上拍摄

在部署阶段之后,是时候持续监控 ML 模型的性能,并根据预测查看性能是否有下降。有重要的关键绩效指标(KPI)可以帮助监控 ML 模型在生产中的表现。因此,密切关注模型性能有助于确保模型为组织产生业务影响和利润。

构建强大的数据科学产品组合

乔安娜·科辛斯卡在 Unsplash 上拍摄的照片

对于机器学习或数据科学方面的新手,如果你建立一个强大的工作组合,包括你最好的项目和任务,这将非常方便。请随意观看这个视频,我在视频中强调并解释了投资组合对于获得第一份数据科学工作的重要性。谢了。

关于为数据科学或机器学习工作建立强大投资组合的视频

结论

我们已经看到了一些成为优秀数据科学家或机器学习工程师的重要实践。虽然本文强调了许多好的实践,但是仍然有一些其他的实践很重要,可以考虑。但是阅读这篇文章应该有望让你对成为一名有效的数据科学家或机器学习工程师可以做的事情有一个很好的了解。感谢您花时间阅读这篇文章。

你的会员费直接支持苏哈斯·马达利和你阅读的其他作家。你还可以在媒体上看到所有的故事。点击下面的链接成为 Medium 的会员,并获得无限制的文章列表。下面是链接。谢了。

https://suhas-maddali007.medium.com/membership

以下是您联系我或查看我作品的方式。谢了。

GitHub: 苏哈斯马达利(Suhas Maddali)(github.com)

LinkedIn: (1)苏哈斯·马达利,东北大学,数据科学| LinkedIn

培养基: 苏哈斯·马达利—培养基

适合初学者的最佳 Python 内置

原文:https://towardsdatascience.com/best-python-built-ins-for-beginners-17322ce1e8a4

对于任何想提高代码水平的新手来说,这是基本功能

照片由夏洛特·卡尔森Unsplash 拍摄

当我第一次开始用 Python 编码时,我专注于执行。我不关心效率或干净的代码。如果成功了,我就满足了。结果,我变得过度依赖变通方法,而不是学习最佳实践。

这些最佳实践中的许多都可以在 Python 文档中找到。多亏了这些文档,我发现了内置函数,它们解决了许多我以前认为很烦人,但又不可避免的 Python 怪癖。

这并不是最常用的内置函数的列表。更确切地说,这是一个我希望在我第一次开始编码时就学会并接受的函数列表。我保证一旦你学会了如何使用它们,它们将会简化和改进你的代码。

最佳内置函数

1.列举

enumerate函数将一个 iterable(如listsetSeries)作为输入,并返回一个 enumerate 对象,该对象包含输入中每一项的计数和值的元组。由于输出也是可迭代的,枚举需要一个 for 循环或list包装器来迭代和处理项目。

start参数设置索引值的起始值。例如,要创建索引值对应于日期的本周的工作日列表,请使用start = 25

enumerate在使用每一项的索引和值的 for 循环中特别有用。之前,我在 for 循环中使用了range(len(list)),然后在循环体中为索引和条目创建了变量。现在,我使用enumerate,节省了打字时间,创建了更干净的代码。

2.活力

zip将一个或多个 iterables 作为输入,并对它们进行并行迭代。它输出一个元组迭代器,每个元组包含来自每个输入的一个项目。换句话说,zip连接输入以创建元组,其中第 i 个元组包含来自每个输入的第 i 个条目。

输入不需要长度相同。如果它们的长度不同,输出将与最短的输入大小相同。

enumeratezip返回一个 iterable,它需要一个 iterable,比如 for 循环或list包装器来解包项目。

在输入长度相同的情况下,使用strict = True参数。如果输入长度不同,则产生一个ValueError

当只给出一个输入时,zip返回长度为 1 的元组。

3.地图

map将一个函数和一个或多个可迭代对象作为输入,将函数应用于可迭代对象,并返回结果。像zipenumerate一样,输出是可迭代的。

注意,列表理解达到了同样的效果。

然而,当函数有多个输入时,map比列表理解更有优势。例如,假设您有一份关注您的帐户的所有新媒体订户的列表。但是,列表是按周划分的,您希望找到每个工作日获得的最大订户数。

单靠列表理解无法复制这一点。然而,如果调用其他函数,比如zip,列表理解也可以工作。

4.分类的

函数从一个可迭代的输入中返回一个新的排序列表。如果你在处理列表时遇到了一个None对象,这个函数就是为你准备的。通常,这些错误是由于将一个变量赋值给一个就地操作,如list.sort()list.reverse()。就地操作不输出任何东西,所以变量没有什么可存储的。

sorted函数返回一个输出,该输出可以存储为一个变量。

令人尴尬的是,很长一段时间我都不知道这个功能。相反,我复制了一个列表,然后将副本排序。如果我知道sorted,我就可以避免这种耗时的变通方法。

结论

感谢您阅读我的文章。如果你喜欢我的内容,请考虑关注我。此外,欢迎所有反馈。我总是渴望学习新的或更好的做事方法。请随时留下您的评论或联系我 katyhagerty19@gmail.com。

https://medium.com/@katyhagerty19/membership

数据科学最佳 Seaborn 可视化

原文:https://towardsdatascience.com/best-seaborn-visualizations-for-data-science-3d866f99c3a9

使用 Seaborn 库探索数据科学项目的一些最佳可视化选项

汤姆·温克尔斯在 Unsplash 上的照片

任何人工智能或数据科学相关任务的最重要组成部分是 数据 。然而,如何理解如何有效地利用原始状态的数据呢?

查看数据和浏览一些次要细节可能并不总是足以准确地计算出解决方案。因此,我们需要可视化技术。

可视化在破译数据模式方面发挥着至关重要的作用,并帮助我们分析最有效的机器学习或深度学习方法,数据科学爱好者可以使用这些方法来获得高质量的结果。这些是探索性数据分析(EDA)计算理想解决方案时要遵循的最重要的步骤之一。

在我们开始这篇关于 seaborn 的文章之前,我建议从下面提供的链接中查看我以前的一篇关于 matplotlib 可视化技术的文章。这应该是更熟悉不同类型的可视化的一个很好的起点。

</8-best-visualizations-to-consider-for-your-data-science-projects-b9ace21564a>

数据科学的 9 个最佳 Seaborn 可视化:

在本文中,我们将重点关注 seaborn 库。我们将学习该库中可用的众多可视化技术,我们几乎可以在每个项目中使用这些技术。Seaborn 是一个基于 matplotlib 的 Python 数据可视化库。它提供了一个高层次的界面来绘制有吸引力的和信息丰富的统计图形。

Seaborn 的简单性有助于简化复杂的可视化,并有助于增加额外的美学吸引力。除了 seaborn 拥有的所有令人惊叹的特性之外,它还构建在 matplotlib 库之上。因此,利用这两个库的组合,我们可以产生更强大和更高效的可视化效果。然而,在本文中,我们将只关注 seaborn 库。

开始使用:

让我们通过导入 seaborn 库来快速入门。下面的代码片段显示了如何根据需要导入库。一旦导入完成,我们就可以进行进一步的计算和可视化。

# Import the seaborn library for visualizationimport seaborn as sns

关于 seaborn 库最好的部分是它提供了一组默认数据集,通过这些数据集,您可以开始训练和测试您的可视化技术。虽然有几个数据集选项,如行星,小费,泰坦尼克号等。,在许多其他项目中,我们将在这个项目中使用 iris 数据集。下面是在 seaborn 库中加载 iris 数据集的代码片段。

# Load the Iris datasetiris_data = sns.load_dataset("iris")

在 Iris 数据中,我们有三种花,即 setosa、versicolor 和 virginica。我们的任务是可视化许多参数,如萼片宽度,萼片高度,花瓣长度和花瓣宽度,这些都与每一个物种有关。使用这些与每个提到的物种相关的特性,我们将使用 seaborn 库中的一些最佳选项来相应地区分它们。下面是对我们的数据集的简要介绍。

iris_data[:5]

作者图片

1.散点图:

sns.scatterplot(x = "sepal_length", 
                y = "sepal_width", 
                data = iris_data, 
                hue = "species")

作者图片

开始可视化的最佳技术之一是对可用数据应用散点图。散点图为用户提供了一个极好的选项,让用户可以看到数据之间的区别。从上面的散点图中,我们可以注意到很容易将 setosa 与 versicolor 和 virginica 区分开来。然而,云芝和弗吉尼亚似乎有一些相似之处。

为了在 seaborn 库中定义散点图,我们可以直接提到 x 轴和 y 轴参数,我们需要为可视化计算这些参数。一旦我们选择了 x 轴和 y 轴属性,我们就可以提到数据集并指定色调,以便为可视化绘图启用颜色编码。

2.直方图:

sns.histplot(x = "species", y = "sepal_width", data = iris_data)

作者图片

从之前的散点图中,我们已经能够生成大量关于虹膜数据集的信息。我们还可以使用其他图表,如直方图,从而使用户能够直观地了解一些特征的可区分性。上图显示了基于萼片宽度的物种直方图。

在上面的代码片段中,我们将 seaborn 库中的 histplot 函数用于 iris 数据集,相应地提到了物种和萼片宽度。强烈建议用户使用其他特征参数来测量物种的多样性。

3.条形图:

sns.barplot(x = "species", y = "sepal_width", data = iris_data)

作者图片

类似于直方图,我们也可以使用 seaborn 库中的条形图函数和 iris 数据集绘制条形图,相应地提到物种和萼片宽度。上面的可视化表示了一个柱状图,显示了每一个提到的物种的萼片宽度更加丰富多彩和美观。

4.箱线图:

sns.boxplot(x = "species", y = "sepal_width", data = iris_data)

作者图片

与前两个图不同,我们将关注另外两个图,这两个图将为我们提供一个更具体和适当的范围,不同品种的花的参数落在这个范围内。首先,我们将查看 seaborn 库中的方框图,它将为用户提供每个物种的特定范围。

中值、百分位数和分位数的概念在这些绘制图表的方法中使用。箱形图的末端代表在四分位数范围内构建的晶须。通过提及 iris 数据集、物种和特定参数,可以在 seaborn 库中绘制箱线图。

5.小提琴情节:

sns.violinplot(x = "species", y = "sepal_width", data = iris_data)

作者图片

为了简化箱线图和中值范围的概念,我们可以使用小提琴图,通过它我们可以更直观地了解特定功能的工作范围。与箱线图类似,通过提及 iris 数据集、物种和特定参数,可以在 seaborn 库中绘制 violin 图。

6.带 Distplot 的面网格:

from warnings import filterwarnings
filterwarnings("ignore")sns.FacetGrid(iris_data, hue="species", height = 5).map(sns.distplot, "petal_width").add_legend()

作者图片

在下一个可视化中,我们可以利用分布图(dist。绘图)以了解 iris 数据集中的数据分布。分布图有助于我们直观地了解物种的概率密度,即 x 轴上每单位的概率。我们可以为以下内容绘制图表,如上面的代码片段所示。

7.配对图:

sns.pairplot(iris_data, hue="species", height=3)

作者图片

seaborn 中最重要的可视化技术之一,尤其是对于 iris 数据集这样的任务,是对图的利用。上图详细展示了众多特征的配对图,也许是理解虹膜数据集的最详细视图。配对图有助于描述和区分两个特定变量之间的最佳特征。

上述代码片段可用于绘制 iris 数据集各种物种的配对图。配对图是分析二维数据的一些最佳选择。然而,当数据维数较高时,它们的效用会减弱,并且在数据集非常庞大的情况下,它们不是非常有用。

8.聚类图:

sns.clustermap(iris_data.drop("species", axis = 1))

作者图片

seaborn 中的聚类图允许用户将矩阵数据集绘制为分层聚类的热图。聚类图是确定特定区域中有多少数据点的重要工具。seaborn 中的聚类图功能可能有点复杂,但它允许用户详细了解数据集中提供的大多数要素。对于特定的项目和任务,聚类图可能是一种重要的可视化技术。

9.热图:

sns.heatmap(iris_data.corr())

作者图片

最后,我们将查看 seaborn 库中的热图函数,这是最有用的可视化技术之一。热图可视化帮助我们计算不同变量和参数之间的相关性。使用热图函数,我们可以简单了解几个变量之间的关系。

为了对 iris 数据集执行热图操作,最好使用 corr()函数获取 iris 数据的相关性。一旦我们有了一个关联表,我们就可以用上面代码片段中显示的命令来绘制它,以产生上图所示的结果。热图是机器学习模型的超参数调整的重要可视化技术。

结论:

约瑟夫·巴里恩托斯在 Unsplash 上拍摄的照片

正确问题的近似答案比近似问题的精确答案更有价值。
——约翰·图基

可视化和探索性数据分析(EDA)将始终是数据科学项目的一些基本组成部分。这些是唯一的方法,通过它们我们可以对我们在特定项目中处理的数据类型有一个比较好的理解。因此,每个数据科学家都必须学习并更加熟悉这些可视化技术。

在本文中,我们了解了 seaborn 库,它是用于数据科学任务和项目的 Python 可视化的最佳工具之一。Seaborn 在处理熊猫数据帧方面更加得心应手。它使用基本的方法集来提供 Python 中漂亮的图形。我们了解了 seaborn 库中各种不同的可视化技术,通过这些技术,我们对特定项目中使用的数据或数据集有了更好的理解。

如果你想在我的文章发表后第一时间得到通知,请点击下面的链接订阅邮件推荐。如果你希望支持其他作者和我,请订阅下面的链接。

https://bharath-k1297.medium.com/membership

如果你对这篇文章中提到的各点有任何疑问,请在下面的评论中告诉我。我会尽快给你回复。

看看我的一些与本文主题相关的文章,你可能也会喜欢阅读!

</7-python-programming-tips-to-improve-your-productivity-a57802f225b6>

谢谢你们坚持到最后。我希望你们都喜欢这篇文章。祝大家有美好的一天!

Jupyter-Notebook 中的 4 个省时 Python 技巧

原文:https://towardsdatascience.com/best-time-saving-python-tricks-in-jupyter-notebook-fca877132507

欺骗

Jupyter-Notebook 中的 4 个省时 Python 技巧

使用查找和替换、复制和粘贴等功能更快地编写代码

塞缪尔·佩恩在 Unsplash 上的照片

当在 Jupyter-Notebook 中使用 Python 时,这四个技巧一定可以节省你宝贵的时间。反过来,增加⚡️你的生产力。

常见的快捷键有Ctrl + Shift + -拆分单元格、Shift + M合并多个单元格、Shift + Down / Up选择所选单元格下方或上方的单元格。

然而,我在这里列出的技巧在几乎每个项目中都节省了我的时间。在您的项目中使用它们肯定会通过避免重复工作来节省您的时间。

一个视频抵得上 1000 张图片!!!

因此,我准备了 4 个 30 秒的⚡️短片来展示这些笔记本小把戏!

我们开始吧!

如何一次注释多行

Python 中的注释是在程序 执行过程中被编译器忽略的代码中的行。

几乎在每个项目中,我们都需要注释一堆行。在 Python 中,注释总是以**#**开头。因此,要将任何一行转换成注释,需要在开头添加**#**

逻辑上,你需要在每一行你想评论的前面加上**#**。与 JavaScript 不同,Python 目前不支持多行注释。

但是,有志者事竟成!

你所需要做的就是选择你想要评论的所有行,然后按下**Ctrl** + **/** ,如下图所示。

如何一次评论多行|图片作者

这样,可以通过点击**Ctrl** + **/** 来切换注释。

在 Jupyter-Notebook 中使用 Python 时,可以使用这个简单的技巧。

查找和替换

有时,您需要在 Python 程序中复制并粘贴一些代码片段。然后编辑一些变量名和变量值,使它在你的程序中工作。

在这种情况下,Jupyter-Notebook 中的**Find and Replace**选项确实节省时间。

作者提供的视频

如上视频所示,可以在**Edit**选项下找到。此外,当您在 Jupyter-Notebook 中处于命令模式时,可以通过键盘快捷键**F**访问它。

一次复制粘贴多个单元格

有时,您需要在同一个笔记本中或从另一个笔记本中复制多个单元格。在这种情况下,这种多单元格复制粘贴的方式非常完美。

作者提供的视频

有时候,你需要按两次**Ctrl** + **V**来粘贴单元格。

一次编辑多行代码

大多数时候,您需要在 cell 中编辑多行代码。在这种情况下,这是非常方便和省时的技巧。

作者提供的视频

在我使用 Python 和 Jupyter-Notebook 的大多数数据科学任务中,这个技巧总是节省我的时间。

总结一下,

在 Jupyter-Notebook 中使用 Python 时,您可以使用这四个技巧中的任何一个。在我的工作中,我发现它们非常节省时间,尤其是重复的任务。⏳

请随意 保存视频 供您将来参考。另外,你可以加入我的 电子邮件列表 以便在我发布新故事时得到通知!

💡如果你喜欢阅读这样的故事,并想支持我和其他作家,考虑注册成为一个媒体成员。每月只需 5 美元,你就可以无限制地阅读媒体上的文章。如果你注册使用我下面的链接,我会得到一小笔佣金!💡

https://medium.com/@17.rsuraj/membership

感谢阅读!

我最喜欢的 VS 2022 代码扩展

原文:https://towardsdatascience.com/best-vs-code-extensions-of-2022-e9b49990cb4b

作为一名软件工程师和学生

照片由加布里埃尔·海因策Unsplash 上拍摄

作为一名 web 开发人员、移动应用程序开发人员,以及最近作为一名 c 语言嵌入式系统开发人员,我几乎在任何事情上都使用 VSCode。

这里有一些我最喜欢的扩展,我用它们来使我的 VSCode 看起来像我想要的那样,并使我的生活变得更简单。

1. Atom One 黑暗主题

我尝试了很多主题和配色方案,但 Atom One 深色主题是我最喜欢的,因为颜色有有用的对比,看起来很棒。

来自 vscode 市场的截图。

主题之前/之后的 React 代码示例。来自作者的代码

ext install akamud.vscode-theme-onedark安装

2。VSCode 大图标

比最常见的图标扩展 vscode-icons 稍逊一筹,但却是我个人的最爱。我觉得这里的图标看起来比默认的好多了,文件夹图标让我更容易分辨我在哪个目录。

作者截图

ext install emmanuelbeziat.vscode-great-icons安装

3。饿删

非常简单,但是非常方便。这在删除几个空行时很有帮助,可以去掉所有的空格。它让我不必选择多行或者总是点击Ctrl-Backspace

将捕捉移动到顶部只需要一次 ctrl-backspace。Gif 取自扩展列表

ext install jasonlhy.hungry-delete

4。直播服务器

这只是一个网页开发,但超级容易的静态和动态页面。对我来说,它最有用的特性是在修改时自动重载。

自动重装功能。Gif 取自扩展列表。

ext install ritwickdey.LiveServer

5。制表

像饥饿删除,这是一个方便的小键盘绑定,让您的光标离开括号,引号,和括号与一个标签。我发现这很有用,因为 VSCode 会自动为您补全括号;您可以按 tab 键而不是箭头键。

ext install albert.TabOut

6.片段

如果你不知道片段,你应该知道。当你在一门语言中倾向于重复代码块时,它们非常有用。

片段是来自扩展或您自己编写的小的预编程字符串,可以自动完成为更大的代码块。我发现这在编写 React 组件时特别有用,因为其中有大量重复的样板代码。

React Snippets 扩展附带的一些代码片段

ext install runningcoder.react-snippets安装如上所示的 React Snippets 扩展

7.支架对着色机 2

这个扩展通过用独特的颜色突出显示括号和圆括号,使我一眼就能看到我所在的上下文。当您选择右括号和左括号时,它还会在它们之间画一条线。

̶a̶v̶a̶i̶l̶a̶b̶l̶e̶̶h̶e̶r̶e̶,̶̶o̶r̶̶i̶n̶s̶t̶a̶l̶l̶̶i̶t̶̶d̶i̶r̶e̶c̶t̶l̶y̶̶b̶y̶̶o̶p̶e̶n̶i̶n̶g̶̶v̶s̶c̶o̶d̶e̶,̶̶t̶y̶p̶i̶n̶g̶̶c̶t̶r̶l̶+̶p̶̶t̶h̶e̶n̶̶p̶a̶s̶t̶i̶n̶g̶̶e̶x̶t̶̶i̶n̶s̶t̶a̶l̶l̶̶c̶o̶e̶n̶r̶a̶a̶d̶s̶.̶b̶r̶a̶c̶k̶e̶t̶-̶p̶a̶i̶r̶-̶c̶o̶l̶o̶r̶i̶z̶e̶r̶-̶2̶̶t̶o̶̶i̶n̶s̶t̶a̶l̶l̶̶d̶i̶r̶e̶c̶t̶l̶y̶.̶

来自 vscode marketplace 的截图。

*更新 *

有人在 2021 年 9 月的评论中向我指出,彩色括号是 VS 代码中的原生元素!要启用它,请在 settings.json 中更改这两行(或者只搜索它们)

{
    "editor.bracketPairColorization.enabled": true,
    "editor.guides.bracketPairs":"active"
}

奖金:

以下是我使用的或其他人发现有用的一些其他技巧或其他扩展:

  1. Vim 有一点学习曲线,但是一旦你习惯了,你可以更快地导航和编辑你的代码。
  2. 自动结束标记 —自动完成 HTML 和 XML 结束标记。网页开发者必备。
  3. Git Graph —给出 git repo 中分支和变化的直观线性图形。您甚至可以直接从图中执行 git 操作!
  4. https://marketplace.visualstudio.com/items?itemName=smcpeak.default-keys-windows—也许更有经验的 Linux 用户知道这是否有充分的理由,但 Windows/Mac 上的大多数键盘快捷键使用的键比 Linux 上的少。我刚刚开始全职使用 Linux,并且习惯了 Windows 的键盘绑定。这使得转换更容易,这样我就不必重新学习任何键盘快捷键。
  5. 快捷键 —键盘快捷键是加速你编码的最好方法之一。VSCode 有大量有用的键盘快捷键,可以加快打字和编辑的速度。完整列表已链接,但以下是我最常用的几个:

查看器快捷键:

`Ctrl+`` —切换终端。

Ctrl+B —切换侧面板,以便为编辑器留出更多空间。

Ctrl+Shift+P —这将打开命令面板,其中包含大量与您的工作区相关的命令。

代码导航:

Ctrl+Shift+O —“转到符号”命令。这让你可以搜索你的函数和变量,这使得在一个文件中访问函数变得更快。

Ctrl+Alt+Up/Down —向上或向下复制您的行。

Ctrl+X —正常情况下使用选择,但在 VSCode 中,如果光标停留在一行上,它将剪切整行而不选择。Ctrl-C 也一样!

Ctrl+Enter —将光标移动到下面的新行。如果我在语句中间编辑完了什么,我会一直用这个。

Ctrl+/ —不特定于 VSCode,但是您应该知道如何添加和删除行注释。

目录导航:

Ctrl+P —打开搜索以快速移动到项目中的其他文件。

同样的快捷方式,带有 Chrome 标签,可以让你前后移动到之前打开的文件。

再次和 Chrome 一样,关闭你打开的文件。

Ctrl+Shift+F —搜索整个项目,而不仅仅是打开的文件。

感谢阅读!请在评论中告诉我你最喜欢的代码扩展和快捷键。

贝塔分布简单地解释了

原文:https://towardsdatascience.com/beta-distribution-simply-explained-839b3acde6e9

贝塔分布的简明解释。

照片由 Lucas SantosUnsplash 上拍摄

介绍

贝塔分布是一种连续分布,通常被称为概率 的 概率分布。这是因为它只能取在 01 之间的值。

当我们有一些关于成功和失败的数量的信息时,它被用来推断一个事件的概率。

贝塔分布的主要用途是作为贝叶斯统计中的https://en.wikipedia.org/wiki/Conjugate_prior二项式伯努利 分布的共轭先验。在我的下一篇文章中,我们将深入探讨这到底意味着什么,然而在这里,我们将只是获得 Beta 分布背后的一些直觉。

数学定义

和往常一样,我喜欢从数学公式开始,然后深入研究分布的直觉。

贝塔分布的 概率密度函数(PDF) 为:

作者在 LaTeX 中生成的方程。

其中 0 ≤ x ≤ 1, 因此可以解释为我们上面提到的概率

这里有一个 StatExchange 线程链接这里展示了 Beta 发行版的 PDF 的派生。

我们可以看到,它是由两个变量 αβB(α,β)β函数 它是归一化常数:

作者在 LaTeX 中生成的方程。

其中γ(α)γ函数 :

作者在 LaTeX 中生成的方程。

你可以在我之前的帖子中读到更多关于伽马函数和 伽马分布的信息:

**

直觉

考虑贝塔分布的 PDF 的分子:

作者在 LaTeX 中生成的方程。

这让你想起什么了吗?

没错,就是很像二项分布 PDF:

作者在 LaTeX 中生成的方程。

即在给定概率下从【x】事件中获得 n 成功的概率

例如,从 10 硬币中准确翻转出 6 人头的概率是 0.20508。

我们可以看到二项式分布和贝塔分布非常相似,除了一个关键点:

  • 对于贝塔分布,概率是一个我们试图估计的随机变量
  • 对于二项分布,概率是一个固定参数,我们用它来推断 n 成功的概率。

因此,我们可以在已知成功次数、【α-1】、* 和失败次数、 β -1 的情况下,用贝塔分布来估计一个事件的概率。*

示例和图表

现在让我们通过几个例子来使这个理论更加具体。

让我们假设我们抛一枚有偏向的硬币 50 次,它正面着地 30 次,反面着地 20 次。

在这种情况下,我们有 30 次成功和 20 次失败,贝塔分布图如下:

*# Import packages
from scipy.stats import beta as beta_dist
import matplotlib.pyplot as plt
import numpy as np# Plot the distribution
alpha = 31
beta = 21
x = np.arange (0, 1, 0.01)
y = beta_dist.pdf(x, alpha, beta)
plt.figure(figsize=(11,6))
plt.plot(x, y, linewidth=3)
plt.xlabel('x', fontsize=20)
plt.ylabel('PDF', fontsize=20)
plt.xticks(fontsize=20)
plt.yticks(fontsize=20)
plt.axvline(0.6,  linestyle = 'dashed', color='black')
plt.show()*

作者用 Python 生成的图。

我们看到,这枚硬币正面朝上的最大概率是 0.6,,这可以理解为 30/50 = 0.6

此外,注意在 0.4 之前和 0.8 之后,硬币呈现这些值的概率实际上是 0 。如果我们获得更多的数据,比如说 100 个人头翻转 60 个人头,我们会看到这个峰值变窄,因为我们更确信人头的概率确实是 0.6 。

记住 y 轴大于 1 的原因是因为这是概率密度函数。为了得到实际的概率,我们需要对概率密度函数进行积分,得到 概率质量函数

现在让我们抛另一枚硬币,得到 100,000 个正面和 100,000 个反面。该数据的贝塔分布为:

*alpha = 100_000
beta = 100_000
x = np.arange (0, 1, 0.01)
y = beta_dist.pdf(x, alpha, beta)
plt.figure(figsize=(11,6))
plt.plot(x, y, linewidth=3)
plt.xlabel('x', fontsize=20)
plt.ylabel('PDF', fontsize=20)
plt.xticks(fontsize=20)
plt.yticks(fontsize=20)
plt.show()*

作者用 Python 生成的图。

我们现在看到峰值更窄,在 0.5 处。这是因为我们有更多的平衡数据,所以我们更确定这个硬币翻转的真实概率。

情节的完整代码可以在我的 GitHub 这里获得:

*https://github.com/egorhowell/Medium-Articles/blob/main/Statistics/Beta_Distribution.ipynb

结论

在这篇文章中,我们以定量和定性的方式描述了 Beta 分布,以及一些带有一些数据的基本图表,以获得背后的直觉。

在我的下一篇文章中,我们将解释贝叶斯统计中的共轭先验,以及贝塔分布是多么有用!

和我联系!

(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。许可证: CC BY-SA 4.0***

使用 Python 的 Matplotlib 进行更好的注释

原文:https://towardsdatascience.com/better-annotations-with-pythons-matplotlib-46815ce098df

如何在 Matplotlib 中轻松定制文本的快速指南

咖啡豆生产——作者图片

文本是为我们的数据可视化增加价值的一种无与伦比的方式。我们可以将注意力吸引到图表的特定方面,解释一个模式,或者给出关于该主题的额外信息。

本文将探讨如何用高亮文本格式化 Matplotlib 文本。

对于下面的例子,我将使用来自数据中我们的世界的咖啡产量数据。

先说简单的。我们将绘制 2018 年咖啡产量最高的四个国家的历史数据。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd#[https://ourworldindata.org/grapher/coffee-bean-production](https://ourworldindata.org/grapher/coffee-bean-production)
df = pd.read_csv('../data/coffee-bean-production.csv')countries = ['Brazil', 'Vietnam', 'Indonesia', 'Colombia']
colors = ['#36AD63', '#EEEE50', '#DE4A43', '#3765B1']#figure
fig, ax = plt.subplots(1, figsize=(12,6))
ax.set_facecolor('#202020')# plot
for i, country in enumerate(countries):
    plt.plot(df[df['Entity'] == country].Year, df[df['Entity'] == country].tonnes, color=colors[i])# grid lines
ax.set_axisbelow(True)
ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.2)
ax.xaxis.grid(color='gray', linestyle='dashed', alpha=0.2)# ticks and labels
plt.xticks(np.arange(df.Year.min(),df.Year.max()+1, 3))
plt.ylabel('Tonnes')
ax.set_yscale('log')# legend and title
plt.legend(countries)
plt.title('Coffee bean production by year', loc='left')
plt.show()

咖啡豆生产——作者图片

没关系。但是有几个方面需要改进。

我想使标题更加突出,并在它下面添加图例作为我的副标题的一部分。我还想在图片底部写一些注释和这些数据的来源。

字幕和标题在 Matplotlib 中并不像我们预期的那样直观;对他们来说没有任何作用。

我们可以对副标题使用带有标题功能的换行符,但是没有办法对每一行进行不同的格式化。如果我们想让我们的字幕与众不同,我们需要同时使用标题和文本功能,然后努力调整坐标来放置字幕。

高亮文本

这个简单的包为我们在 Matplotlib 中格式化文本提供了很大的自由度。它允许我们拥有一个具有多种不同格式的字符串。

我们需要创建一个包含文本属性的字典列表。然后我们用标签(<>)为我们将要使用的不同格式编写字符串。

**from highlight_text import HighlightText, ax_text, fig_text**
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd#[https://ourworldindata.org/grapher/coffee-bean-production](https://ourworldindata.org/grapher/coffee-bean-production)
df = pd.read_csv('../data/coffee-bean-production.csv')countries = ['Brazil', 'Vietnam', 'Indonesia', 'Colombia']
colors = ['#36AD63', '#EEEE50', '#DE4A43', '#3765B1']#figure
fig, ax = plt.subplots(1, figsize=(12,6))
ax.set_facecolor('#202020')# plot
for i, country in enumerate(countries):
    plt.plot(df[df['Entity'] == country].Year, df[df['Entity'] == country].tonnes, color=colors[i])# grid lines
ax.set_axisbelow(True)
ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.2)
ax.xaxis.grid(color='gray', linestyle='dashed', alpha=0.2)# ticks and labels
plt.xticks(np.arange(df.Year.min(),df.Year.max()+1, 3))
plt.ylabel('Tonnes')
ax.set_yscale('log')# legend and title
plt.legend(countries)**highlight_textprops = [{"fontsize":16, "color":'k'},
                       {"fontsize":14, "color":'#202020'}]****fig_text(x=0.125, y=0.9, va='bottom',
         s='<Tonnes of coffee bean produced by year>\n<Four highest producers of 2018>',
         highlight_textprops=highlight_textprops,
         ax=ax)**plt.show()

咖啡豆生产——作者图片

这使得我们可以快速地在一个对象中编写标题和副标题。

功能

字幕可以给我们的图表增加很多东西,有一种实用的添加方式是受欢迎的。但是一种更简单的格式化文本的方法提供了增强的可能性,这是默认使用 Matplotlib 所不能忍受的。

我们现在可以通过明确命名字幕中的每一行并格式化每个单词的背景来替换图例。

from highlight_text import HighlightText, ax_text, fig_text
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd#[https://ourworldindata.org/grapher/coffee-bean-production](https://ourworldindata.org/grapher/coffee-bean-production)
df = pd.read_csv('../data/coffee-bean-production.csv')countries = ['Brazil', 'Vietnam', 'Indonesia', 'Colombia']
colors = ['#36AD63', '#EEEE50', '#DE4A43', '#3765B1']#figure
fig, ax = plt.subplots(1, figsize=(12,6))
ax.set_facecolor('#202020')# plot
for i, country in enumerate(countries):
    plt.plot(df[df['Entity'] == country].Year, df[df['Entity'] == country].tonnes, color=colors[i])# grid lines
ax.set_axisbelow(True)
ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.2)
ax.xaxis.grid(color='gray', linestyle='dashed', alpha=0.2)# ticks and labels
plt.xticks(np.arange(df.Year.min(),df.Year.max()+1, 3))
plt.ylabel('Tonnes')
ax.set_yscale('log')highlight_textprops = [{"fontsize":16, "color":'k'},
                       {"fontsize":14, "color":'#202020'},
                       **{"bbox": {"facecolor": "#36AD63", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'w'},
                       {"bbox": {"facecolor": "#EEEE50", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'k'},
                       {"bbox": {"facecolor": "#DE4A43", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'w'},
                       {"bbox": {"facecolor": "#3765B1", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'w'}**]fig_text(x=0.125, y=0.9,
              va='bottom',
              s='<Tonnes of coffee bean produced by year>\n<Four highest producers of 2018: >**<Brazil> , <Vietnam> , <Indonesia> , <Colombia>'**,
              highlight_textprops=highlight_textprops,
              ax=ax)plt.show()

咖啡豆生产——作者图片

同样,我们可以在图表底部区分注释和数据源。

我们甚至可以将字体颜色与背景颜色相匹配,创建一个空行,将两者分开。

from highlight_text import HighlightText, ax_text, fig_text
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd#[https://ourworldindata.org/grapher/coffee-bean-production](https://ourworldindata.org/grapher/coffee-bean-production)
df = pd.read_csv('../data/coffee-bean-production.csv')countries = ['Brazil', 'Vietnam', 'Indonesia', 'Colombia']
colors = ['#36AD63', '#EEEE50', '#DE4A43', '#3765B1']#figure
fig, ax = plt.subplots(1, figsize=(12,8))
ax.set_facecolor('#202020')# plot
for i, country in enumerate(countries):
    plt.plot(df[df['Entity'] == country].Year, df[df['Entity'] == country].tonnes, color=colors[i])# grid lines
ax.set_axisbelow(True)
ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.2)
ax.xaxis.grid(color='gray', linestyle='dashed', alpha=0.2)# ticks and labels
plt.xticks(np.arange(df.Year.min(),df.Year.max()+1, 3))
plt.ylabel('Tonnes')
ax.set_yscale('log')# title
highlight_textprops = [{"fontsize":16, "color":'k'},
                       {"fontsize":14, "color":'#202020'},
                       {"bbox": {"facecolor": "#36AD63", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'w'},
                       {"bbox": {"facecolor": "#EEEE50", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'k'},
                       {"bbox": {"facecolor": "#DE4A43", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'w'},
                       {"bbox": {"facecolor": "#3765B1", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'w'}]fig_text(x=0.125, y=0.9,
              va='bottom',
              s='<Tonnes of coffee bean produced by year>\n<Four highest producers of 2018: ><Brazil> , <Vietnam> , <Indonesia> , <Colombia>',
              highlight_textprops=highlight_textprops,
              ax=ax)**# footnotes
notes_textprops = [{"fontsize":11, "color":'#202020'},
                   {"fontsize":8, "color":'w'},
                   {"fontsize":12, "color":'#202020'},
                   {"fontsize":11, "color":'#505050'}]****fig_text(x=0.125, y=0.07,
              va='top',
              s='<* Y-Axis is a log scale>\n<blankline>\n<Source:> <ourworldindata.org/grapher/coffee-bean-production>',
              highlight_textprops=notes_textprops,
              ax=ax)**plt.show()

咖啡豆生产——作者图片

注释

使用 HighlightText 进行注释也同样简单。

代替 fig_text 函数,我们可以使用 ax_text,它使用我们的轴的坐标。

与图例类似,注释也可以受益于颜色。我们可以用它来关联其他元素——使用与注释线相同的颜色。

我们可以用它来突出或弱化上面的信息。

from highlight_text import HighlightText, ax_text, fig_text
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd#[https://ourworldindata.org/grapher/coffee-bean-production](https://ourworldindata.org/grapher/coffee-bean-production)
df = pd.read_csv('../data/coffee-bean-production.csv')countries = ['Brazil', 'Vietnam', 'Indonesia', 'Colombia']
colors = ['#36AD63', '#EEEE50', '#DE4A43', '#3765B1']#figure
fig, ax = plt.subplots(1, figsize=(12,8))
ax.set_facecolor('#202020')# plot
for i, country in enumerate(countries):
    plt.plot(df[df['Entity'] == country].Year, df[df['Entity'] == country].tonnes, color=colors[i])# grid lines
ax.set_axisbelow(True)
ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.2)
ax.xaxis.grid(color='gray', linestyle='dashed', alpha=0.2)# ticks and labels
plt.xticks(np.arange(df.Year.min(),df.Year.max()+1, 3))
plt.ylabel('Tonnes')
ax.set_yscale('log')# title
highlight_textprops = [{"fontsize":16, "color":'k'},
                       {"fontsize":14, "color":'#202020'},
                       {"bbox": {"facecolor": "#36AD63", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'w'},
                       {"bbox": {"facecolor": "#EEEE50", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'k'},
                       {"bbox": {"facecolor": "#DE4A43", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'w'},
                       {"bbox": {"facecolor": "#3765B1", "linewidth": 0, "pad": 1.5}, "fontsize":14, "color":'w'}]fig_text(x=0.125, y=0.9,
              va='bottom',
              s='<Tonnes of coffee bean produced by year>\n<Four highest producers of 2018: ><Brazil> , <Vietnam> , <Indonesia> , <Colombia>',
              highlight_textprops=highlight_textprops,
              ax=ax)# footnotes
notes_textprops = [{"fontsize":11, "color":'#202020'},
                   {"fontsize":8, "color":'w'},
                   {"fontsize":12, "color":'#202020'},
                   {"fontsize":11, "color":'#505050'}]fig_text(x=0.125, y=0.07,
              va='top',
              s='<* Y-Axis is a log scale>\n<blankline>\n<Source:> <ourworldindata.org/grapher/coffee-bean-production>',
              highlight_textprops=notes_textprops,
              ax=ax)# annotations
ax_text(x = 1987, y = 20000, ha='left', color='w',
        s='<Vietnam>\nDoi Moi <(1986)>',
        highlight_textprops=[{"color": '#EEEE50'},
                             {"color": 'grey'}],
        ax=ax)plt.show()

结论

总的来说,HighlightText 是一个很棒的软件包,可以让我们的可视化更上一层楼。能够毫不费力地格式化文本的各个部分给了我们很多自由。

尽管它是 0.2 版本,我在使用这个包的时候没有发现很多问题。

大部分时间我都很难在 Jupyter 进行绘图,那里的绘图会剪掉标题和注释。但是,在保存图形时不会发生这种情况。

当我尝试更广泛的文本时,我也遇到了一些麻烦。为更广泛的注释添加适当空间的支线剧情可以解决这些问题。

感谢阅读我的文章。

资源: https://github.com/znstrider/highlight_text
https://ourworldindata.org/grapher/coffee-bean-production
https://en.wikipedia.org/wiki/Economic_history_of_Vietnam

我是如何试图革新脚本体验的

原文:https://towardsdatascience.com/better-bash-scripts-ae542fa37bd6

Bashy 背后的故事 Bash 脚本的脚本管理器

在本文中,我将介绍开源项目 Bashy,这是一个工具,它通过简化参数解析和像每个包管理器一样共享脚本来增强脚本体验。换句话说,我让 work Bash 像任何现代脚本框架一样。对于程序员和数据科学家来说,这有助于提高生产率和自动化重复性任务。

我将解释为什么我们需要一个像 Bashy 这样的工具,然后用一些实际例子展示如何使用它。最后,在我们发现它如何为开发人员节省数百个小时之后,我将进入内部组件来解释它是如何工作的。

让我们直接进入文章吧!

作者是❤️by。

我们为什么需要巴什?

Bashy 的目的是减少脚本编写中的摩擦。最常见的问题是:

  • 解析输入并为我们创建的脚本提供帮助页面。这通常很麻烦,需要很多时间,所以这个阶段经常被跳过,我们有很难运行的脚本。通常,开发人员需要打开它们,并更改脚本本身内部的变量。
  • 共享脚本。共享脚本最常见的方式是将文件从一台 PC 或服务器复制/粘贴到另一台 PC 或服务器。这种做法是非常古老的风格,很难在副本发生变化时更新脚本。

Bashy 愿意通过实施以下措施来解决所有这些问题:

  • Bash 的包管理器
  • 解析参数的简单方法。

使用 bash 脚本的摩擦

Bash 脚本解决了很多问题,但是在大多数情况下:

  1. 驻留在作者的个人电脑上:分享它们是非常困难的
  2. 不是参数化的:参数在脚本内部,作者直接更改它们,而不是解析参数

为什么?现在这种行为看起来很奇怪,但是如果你看看有什么选择,很明显开发商没有选择。事实上:

  1. 没有任何存储库可以让你的脚本共享
  2. 使脚本参数化,需要大量代码

这就是我创造 Bashy 的原因:

BASHY 的目的就是要克服这个局限

既然我们已经理解了创建管理 bash 脚本的工具的重要性,那么让我们看看它在实践中是如何工作的!

从结果开始介绍 Bashy】

与其说很多 Bashy,我更愿意用一个例子来解释。让我们假设创建一个 bash 脚本,列出文件夹中的文件。给定的参数是用于过滤文件的文件夹路径和扩展名。

那么,为了在 bash 解析参数中实现它,您将在下面的代码片段中看到类似这样的内容。

传统 bash 脚本

请注意,在前面的脚本中,实现脚本只需要最后两行代码,上面所有的 34 行代码只需要用于解析参数。此外,您没有任何帮助页面,添加新参数会使事情变得复杂。巴什如何能让它变得更好?

使用 Bashy,您只需编写以下代码:

如您所见,您已经免费解析了参数,并且已经填充了一些可以使用的变量。这转化为一个巨大的简化,正如您稍后看到的,您还可以获得一个帮助页面,而无需任何额外的工作。

那是魔法?让我们在下一节看看这是如何可能的。

怎么可能呢?

因为信息是一门科学,所以没有咒语或魔杖。Bashy 的支柱是通过添加 YAML 格式的清单来描述脚本。YAML 和脚本一起构成了一个可移植的包,包含了 Bashy 需要的所有内容。

包装是如何制作的?作者是❤️by

下面的代码片段可以作为前面脚本的一个示例:

bashy 中阅读文件夹的脚本。作者是❤️by。

YAML 清单包含要在帮助页面上显示的所有信息和要执行的脚本(可以像在本例中那样嵌入,或者引用外部资源)。此外,您定义了参数解析引擎使用的参数列表。

此信息用于创建帮助页面,如下例所示:

命令帮助示例。作者是❤️by。

如您所见,该命令从 Bashy 注册为全局命令,您可以直接编写它,因为它是安装在操作系统上的真正的二进制文件。

用这种技术制作的脚本可以很容易地共享(你只需要在每一个 HTTP 服务器上发布,包括 git ),并帮助构建更多可用的脚本。

但是如何实施呢?Bashy 引擎是使用 Go 构建的,并作为开源应用程序提供。

巴什是怎么做出来的。作者和❤️一起做的

下图显示了应用程序的主要流程:

应用程序的主要流程,由作者用❤️制作

基本上,Bashy 引擎将 YAML 文件中包含的信息添加到内部数据库中(红色流)。有了这些信息,您就可以轻松地列出所有可用的命令,并提供帮助页面(蓝色流)。最后,Bashy 可以通过解析参数并将值作为变量传递给要运行的脚本来执行命令。然后将输出显示给用户。

要了解有关 bashy 的更多信息,您可以:

顺便说一句,我认为巴什的潜力还没有完全发挥出来。在下一节中,我将解释下一个可以添加的特性,以使它更加平滑。

后续步骤

像每个产品一样,开源应用程序也必须关注市场和用户的需求。从这里开始,您可以获得要添加到 backlog 中的特性列表。从我的经验和目前为止我得到的反馈来看,有一些改进可能会非常有用。我在下一张图中总结了它们:

巴什的下一步。由❤️by 执笔

因此,引用《精益创业》的作者埃里克·赖斯的话:

我们必须了解顾客真正想要的是什么,而不是他们说他们想要什么,或者我们认为他们应该想要什么。

这促使我去倾听用户对这些任务给予正确优先级的反馈。

基本上,最想要的特性是添加一个公共存储库,在那里添加脚本,允许用户通过 web UI 搜索它们,并在本地安装它们。此外,一个令人窒息的安装过程也是受欢迎的,因为网站上有更好的文档。

无论如何,任何反馈都是受欢迎的,也是改进过程的一部分!所以,如果你是一个 Bashy 用户或者你打算测试它,别忘了给我反馈😃

结论

Bash 是一种被广泛使用的技术,但是也有一些摩擦,使得它很难实现可重用的脚本。Bashy 的诞生就是为了克服这些问题,释放 bash 的力量。与每个产品一样,开源项目也需要倾听用户的需求,因此向作者提供反馈很重要(一般来说是这样,不仅仅是对 Bashy)。我希望能给你一个提高剧本体验的机会,不要犹豫,请在评论中写下你的反馈😄

参考资料:

更好的流失预测

原文:https://towardsdatascience.com/better-churn-prediction-f88b20c923f3

流失还是不流失——这不是真正的问题!

来自 Pixabay灵感

这些年来,我一直在研究的一个主要课题是客户流失。减少客户流失是许多公司的首要任务,正确识别其根本原因可以大大提高他们的底线。

考虑到客户流失问题是多么广为人知和受重视,我经常被它在实践中的糟糕建模所困扰。

客户流失通常被表述为“谁最有可能流失?”。这个问题自然适合于分类建模。

然而,我认为客户流失并不是“谁”会流失的问题,而是“什么时候”流失的问题。

这是一个重要的区别,原因有二:

  1. 问“谁”会流失会导致有偏见的建模,如下所示。
  2. 在很多情况下,用户的价值很大程度上取决于他的订阅时间。这只能通过问“什么时候”而不是“谁”来回答。

手机用户示例

以手机用户的经典案例为例。他们每月支付一笔费用,直到某个时间点,他们决定终止合同(又名流失)。

最终它们都会发生变化:30 年后,它们要么会死去,要么会用全息图而不是手机来交流。

让我们想象一下典型订阅的样子:

顶层的第一个订户于 2021 年 6 月开始购买 A 计划,并于 2022 年 4 月开始购买。斜线代表“今天”(分析运行的时间),即 2022 年 6 月 8 日。

在“谁”问题中,过去(在“今天”之前)搅动过的用户被标记为“搅动的”(y=1),而未来搅动过的用户被标记为“未搅动的”(y=0),因为当我们“今天”观察他们时,他们仍然是订阅的。

我们可以看到,计划 A 的用户倾向于花费更长的时间来流失(他们的队伍更长)。但是由于他们比 B 计划的订阅者更早加入,他们有更多的时间流失,也更容易被贴上“流失”的标签(A 计划的 50%对 B 计划的 25%)。

因此,我们会错误地得出这样的结论:A 计划用户的流失率高于 B 计划用户。

上述偏见是非常典型的,因为手机计划通常是相继推出的。在上面的例子中可以看到,在 2022 年 1 月,计划 A 被切换到计划 b。

一项小型模拟研究

为了说明这一点,我将在 r。

我们有一个从 0 开始的时间表。我们观察数据和拟合模型的时间(“今天”)是 22。

如果用户使用计划 A,则他开始订阅的时间来自均匀分布(U~[0,20]\ ),如果用户使用计划 b,则来自均匀分布(U~[20,22])。

today <- 22 
Na <- 700 
Nb <- 300 
plan <- rep(c("A", "B"), time = c(Na, Nb)) 
set.seed(1) 
join_time <- c(runif(Na, 0, 20), runif(Nb, 20, today))

下面我们可以看到平均加入时间:

tapply(join_time, plan, mean)##        A        B 
## 10.08968 20.97702

用户在交易前的订阅时间分布为泊松分布,如果他使用计划 A,则λ= 4,如果他使用计划 b,则λ= 3。

set.seed(1) 
time_to_churn <- c(rpois(Na, 4), rpois(Nb, 3))

下面我们可以看到客户流失的平均时间:

tapply(time_to_churn, plan, mean)##        A        B 
## 4.045714 2.993333

用户搅动的时间是他加入的时间+直到他搅动的时间:

churn_time <- join_time + time_to_churn

如果搅动时间大于 22(从“今天”算起的未来),我们说他没有被搅动(y=0)。如果那个时间比我们今天短一天,他就做了搅动(y=1)。

churned <- churn_time < today

从原始流失率来看,我们可以看到计划 A 的用户流失率似乎更高:

tapply(churned, plan, mean)##         A         B 
## 0.8042857 0.2233333

但是我们知道 B 计划的用户是最近加入的,所以我们可能会尝试通过拟合用户流失与计划的逻辑回归以及用户加入后的时间来考虑这一点。

然而,从下面我们可以看到,这种偏差如此之大,以至于模型仍然告诉我们,采用 B 计划可以降低客户流失的可能性:

time_since_join <- today - join_time glm(churned ~ plan + time_since_join)## 
## Call:  glm(formula = churned ~ plan + time_since_join)
## 
## Coefficients:
##     (Intercept)            planB  time_since_join  
##         0.19101         -0.02035          0.05149  
## 
## Degrees of Freedom: 999 Total (i.e. Null);  997 Residual
## Null Deviance:       233.1 
## Residual Deviance: 102.9     AIC: 572.3

我们该怎么办?

用生存分析!

为了避免这篇文章太长,我将跳过介绍什么是生存分析,而是展示它是如何处理上述偏见的。

生存分析武库中的一个常见模型是“加速故障时间”模型。它产生的系数很像逻辑回归:

library(survival) 
observed_time <- ifelse(churned, time_to_churn, today - join_time) 
# add a tiny amount (0.01) to observed_time to avoid observed_time = 0 
survregExp <- survreg(Surv(observed_time + 0.01, churned) ~ plan, dist = "exponential" ) coef(survregExp)## (Intercept)       planB 
##   1.4558659  -0.2154432

我们对系数的解释如下:与总体平均值相比,采用 B 计划可以减少 20%的流失时间(1-exp(-0.2154432)= 0.2)。流失的平均人口时间是:

mean(time_to_churn)## [1] 3.73

B 计划的平均流失时间是 3,比 3.7 低 20%。

结论

问“什么时候”而不是“谁”的问题不仅能给我们公正的结果,还能让我们更深入地了解我们真正感兴趣的是什么:用户流失需要多长时间。

在接下来的几篇文章中,我将更深入地讨论生存分析,并展示流失预测中的高级用例,生存分析对于更好的流失建模至关重要。

原载于 2022 年 6 月 8 日https://iyarlin . github . io

更好的客户流失预测—使用生存分析

原文:https://towardsdatascience.com/better-churn-prediction-part-2-5a1086fd3f51

回答“何时”的问题

马库斯·斯皮斯克Unsplash 上拍摄的照片

在之前的帖子中,我提出了生存分析对于更好地预测客户流失是必不可少的。我的主要论点是,客户流失不是“谁”的问题,而是“何时”的问题。

在“何时”问题中,我们问用户何时会流失?换句话说,用户平均订阅多长时间?然后,我们可以回答一个最重要的问题:订户的平均生命周期值是多少?

让我们卷起袖子开始吧:生存曲线 S(t)衡量订户从开始订阅到时间 t“存活”(而不是流失)的概率。例如,S(3)=0.8 意味着订户在订阅的第 3 个月有 80%的机会不进行交易。

估算 S(t)最常用的方法是使用卡普兰-迈耶曲线,其公式如下:

作者图片

其中,t_i 是至少有一个用户流失的所有时间,d_i 是在 t_i 时流失的用户数量,n_i 是至少存活到 t_i 时的用户数量,我们可以将 d_i/n_i 项视为 t_i 时的流失率。

为了说明,让我们计算以下订户数据的存活曲线:

作者图片

列 t 表示到今天为止用户已经订阅的时间。如果他搅拌,这将是他搅拌的时间。

我们有 2 次流失事件发生的时间:t_i = {2,6}。

对于 t < 2,我们有 S(t)=1,因为没有人搅拌到这一点。

在 t_1=2 时,我们有 d_1=2(订户 3 和 6)和 n_1=5(除了 4 之外的所有订户)。使用上面的公式,我们得到:

作者图片

在 t_2=6 时,我们有 d_2=1(订户 2)和 n_2=1(同样,只有订户 2)。

因此,我们有:

作者图片

让我们画出这条曲线:

作者图片

这里需要注意的一点是,在曲线上的每一点,我们只考虑存活到该点的订户。如果订户是最近加入的(例如订户 4 ),则他在计算中不会起主要作用。

实际上,您最好使用 R [survival](https://cran.r-project.org/web/packages/survival/)包或 python [lifelines](https://lifelines.readthedocs.io/en/latest/)库中的生存曲线实现。

预期寿命

那么,为什么要首先计算 S(t)呢?结果是预期寿命是生存曲线下的面积(这里我不打算证明)。

所以在我们上面的例子中:

作者图片

例如,如果用户的月计划账单是 10 美元,那么我们可以说他的预期 LTV(生命周期价值)是 44 美元。

更好地回答“谁”的问题

在这篇文章中,我们看到了如何使用生存曲线来回答“何时”的问题——平均订阅多长时间。我们看到这可以用来表示订户的价值。

有时候,我们实际上也可能对“谁”这个问题感兴趣。例如“哪些订户在订阅的第一个月内最有可能流失”?在我的下一篇文章中,我将展示使用生存曲线我们也能更好地回答这个问题!

原载于 2022 年 10 月 31 日https://iyarlin . github . io

更好的指标⇏更快乐的用户

原文:https://towardsdatascience.com/better-metrics-happier-users-8264479e4fba

设计一个机器学习产品来关闭用户反馈回路

乔恩·泰森在 Unsplash 上的照片

我想象这个“假设的”场景:由于检测到一些“重要”类别的低精确度,开发了一个新的模型来代替现有的生产模型。新模型的指标要好得多,因此它被部署来取代当前的模型。

事实证明,新模式实际上让用户体验更差。即使它在指标上更好,用户也不觉得它更好。事后分析显示,尽管总体指标更好,但新模型牺牲了用户最关心的类的准确性,以改善用户不太关心的类。

最初的假设是更好的指标更好的模型当然更好的模型更快乐的用户。这个假设有严重缺陷。更好的度量可能意味着更好的模型,但仅仅是由度量判断的更好的模型。由指标判断的更好的模型并不意味着更快乐的用户,由用户判断的更好的模型意味着更快乐的用户。虽然这似乎很明显,但产品开发的 为什么 在机器学习领域往往被遗忘。

本文将介绍用户反馈回路的概念,它是任何 ML 产品设计中的重要组成部分。我们将讨论常见评估和监控方法的缺点,以及我们如何通过在机器学习开发过程中实现这一概念来减轻用户的不满。

目录

初始定义

我将从定义本文中使用的关键术语开始。

监视

监控的目标是确保模型得到正确服务,并且模型的性能保持在可接受的范围内[1]

用户(或顾客)反馈回路

注:我更喜欢使用术语“ 用户 反馈回路”而不是“ 客户 反馈回路”,作为一个用户同时兼顾客户前景。然而,这些术语经常互换使用。

“客户反馈循环是一种客户体验策略,旨在根据用户评论、意见和建议不断增强和改进您的产品。”[2]

反馈循环很重要,因为没有用户反馈,你怎么能指望一个组织(其主要目标是向客户销售)在向客户销售方面做得更好?

传统上,“闭合反馈回路”是:

“…针对具体的产品反馈进行有针对性的个性化跟进沟通。闭环意味着让你的用户知道你是如何根据他们的意见改进产品的。”[3]

然而,在机器学习的上下文中,它被更好地定义为:

利用用户对模型输出的反馈来影响模型开发的优先级。

一个重要的区别是与 ML 中经常描述的传统“反馈回路”的区别,在传统“反馈回路”中,模型的输出用于重新训练模型。这是指数学反馈,而不是用户反馈。

评估和监测

卢克·切瑟在 Unsplash 上的照片

在这一节中,我们将讨论评估模型的当前方法,在监控阶段用于评估模型退化的度量,以及最重要的通用方法的问题

资源与性能监控

资源监控包括监控模型部署的周围基础设施。这是一个传统的 DevOps 主题,除了提到它试图回答的这些关键问题之外,我们不会在本文中讨论它:

“系统还活着吗?CPU、RAM、网络使用和磁盘空间是否符合预期?请求是否以预期的速度得到处理?”[4]

性能监控包括监控实际模型。

关键问题包括:模型是否仍然是新数据模式的准确表示?它的性能是否和设计阶段一样好?”[4]

如何有效地回答关于性能监控的问题是我们将在本文中讨论的内容。

基本事实度量

“基本事实”指标是一个指标“…已知是真实的或真实的,由直接观察和测量提供”【5】。在机器学习中,模型将产生的预期理想结果。有两种类型的地面实况度量:实时和延迟。我们还会提到有偏见的基本事实度量和基本事实的缺失。对于下面描述的所有示例,如果您想要更深入的描述,请参见[6]。

理想情况是实时地面实况。这就是“…对于每个预测,基本事实都浮现在你面前,并且在预测和基本事实之间有直接的联系,允许你直接分析你的模型在生产中的性能”[6]。一个常见的例子是数字广告,根据用户的行为,你会收到关于广告投放是否成功的近乎即时的反馈。

比较常见的案例是拖延地真相。顾名思义,这是模型输出和学习你的模型应该如何执行之间有很大延迟的情况。一个常见的例子是欺诈检测:我们不知道某些交易是否是欺诈性的,直到持卡人报告它们是欺诈性的,这通常比交易日期晚得多。

实时和延迟地面实况的一个共同问题是偏差。我们以贷款违约预测为例。我们只能从负面预测(不会违约)中收集基本事实,我们无法收集任何关于正面预测(会违约)的信息,因为我们拒绝了他们的贷款。

最后,我们会遇到没有地面真相可用的情况。在这种情况下,我们通常可以使用代理指标。

代理指标

如果我们正在处理延迟、缺失或有偏见的实时真相,我们经常使用代理指标,或者除了基础真相指标之外。他们制定了一个代表模型性能的指标,而没有使用基本事实。代理指标“…为您的模型的表现提供一个更新的指标”[6]。它们还允许你将业务成果的重要性纳入你的衡量标准。

代理指标最常见、最广泛使用的例子是数据漂移概念漂移。理论上,自变量和/或因变量中出现的漂移可能代表模型性能下降。

问题

关于如何监控生产模型,有大量(通常是坏的)建议。但是,很难找到考虑到 实际用户 的建议。大多数建议都是基于对构建不良的代理指标的过度依赖。问题来了:代理指标并不完美。它们旨在代表性能,而非直接指示。当不理解这种区别时,问题就出现了。

罪魁祸首是漂移,创造了 ML 监控的“以漂移为中心”的观点[7],其中漂移被假设为模型性能的完美指标。像所有的代理指标一样,漂移是完美的,完全依赖它不是模型监控的有效策略。

说明这一点的一个例子是使用合成数据来训练对象检测模型。研究表明真实世界的数据最多可以减少 70%(用合成数据代替),而不会牺牲模型性能。我们预计合成数据的分布会与真实世界的数据大相径庭,但这种转变不会影响性能。

这并不是说永远不应该使用漂移。漂移应在监控中使用“…如果您有理由相信某个特定特征会漂移并导致您的模型性能下降”[7]。然而,它不应该作为唯一的衡量标准。**

总而言之,

在常见的监控方法中使用代理指标所导致的问题是由模型评估和用户反馈之间的脱节引起的。

为了使代理指标有效,真正代表模型性能,并衡量什么是重要的,它必须以用户为中心的观点来形成。

用户为中心的设计

Unsplash 上由amlie Mourichon拍摄的照片

定义

“以用户为中心的设计(UCD)是一系列将用户放在产品设计和开发中心的过程。你开发你的数字产品时会考虑用户的需求、目标和反馈。

换句话说,以用户为中心的设计是从用户如何理解和使用的角度来设计和开发产品,而不是让用户调整他们的行为来使用产品。" [8]

UCD + ML

虽然 UCD 的传统定义非常适合产品设计,但它如何应用于模型评估和监控呢?

[8]定义的两个主要 UCD 原则是:

  • 用户早期积极参与评估产品的设计。
  • 结合用户反馈来定义需求和设计。

这些概念似乎很熟悉。还记得用户反馈循环吗?我们现在将讨论如何在模型评估和监控阶段实现用户反馈循环。

引入用户反馈循环

营销“用户反馈环”(图片由作者提供)

以上是一个传统营销“用户反馈回路”的流程。从用户开始,循环的关键动作是:

  • 提问:让你的用户参与进来,并征求对你产品的反馈。常见的反馈来源直接来自用户,如访谈和调查。来自客户成功和销售等团队的间接反馈也很有价值。
  • 集中 : “当反馈被埋藏在一个文件夹中或分散在各种不一致的电子表格中时,将反馈转化为行动是很困难的”[3]。反馈应持续收集并集中在“反馈湖”中。这通常采取集中式数据共享解决方案的形式,例如用于所有电子表格、采访等的集中式 Google Drive 文件夹。,但也可以简单到一个#反馈松弛通道。反馈湖将是非常无序的,并且将包含相当多的噪声。别担心:这才是重点。我们希望打破任何阻碍组织中的任何人分享他们从用户那里收到的反馈的障碍。我们将在下一步处理这个问题。
  • 标签&汇总:为了获得可操作的见解,反馈必须以某种可理解的方式进行分类。反馈应标有“…一个简短的描述,一个或多个其所属的功能或产品类别,以及请求者的姓名或数量”[9]。然后输入到反馈“记录系统(SOR)”——用户反馈的真实综合来源。排序可以像电子表格一样简单,也可以像 JIRA 棋盘一样复杂。无论如何,它应该允许根据反馈类型和频率进行简单的汇总。“这里的目标是创建一个高度系统化的流程,这样,当新的反馈通过各种输入来源时,它会被快速有效地处理到记录系统中”[9]。**
  • 区分优先级:SOR 现在可以用来为用户聚集和识别痛点。然而,并不是所有的反馈都是平等的:“在将反馈整合到产品路线图流程中时,要记住的关键一点是,这样做的方式绝不是简单地将最常被请求的功能放在路线图的顶部”[9]。我们应该将用户反馈作为产品路线图规划的一个组成部分,与其他业务目标或战略重点一起进行评估。
  • 实施 & 沟通:当然,实际实施产品路线图很重要。然而,更重要的是,通过与您的用户沟通,告诉他们他们的反馈已经得到处理,并且已经/将要实施,从而结束这个循环*。*

有了这个基础,问题仍然存在:我们如何将用户反馈循环应用于机器学习产品?

我们将从简化的数据科学流程开始:

数据科学流程(图片由作者提供)

我假设读者熟悉这个过程。如果没有,可以查看我的上一篇文章(数据科学过程一节深入解释了这个图)。

我们可以看到,数据科学过程展示了之前讨论过的问题:它与用户及其反馈脱节。如果我们合并前面的两个图,我们会得到下面的流程图:

数据科学流程的客户反馈循环(图片由作者提供)

我们现在可以看到,用户反馈与模型开发过程相关联。用户反馈直接影响 ML 路线图,推动未来的开发工作。下面是示意图(数字与示意图上的数字相对应):

  1. 该流程从推动 ML 路线图的业务或战略优先事项开始。
  2. 路线图定义了启动数据科学流程的初始目标。
  3. 数据科学过程产生一个服务模型(产品),然后对其进行监控。
  4. 然后,所提供的模型被“传达”给用户(即在生产中)。
  5. 反馈循环开始了:询问、集中、标记和汇总、优先排序。结果是用户对你的模型(产品)的优先反馈被注入到 ML 路线图中。
  6. 用户反馈会导致两种情况发生:(a)用户反馈触发对现有模型的维护(即用户对模型性能不满意),或者(b)您根据用户的反馈定义一个新的目标。不管怎样,现在通过重新启动数据科学过程,这个循环已经结束了。

我们还可以在图中看到来自用户的红色虚线箭头。这些表明了用户对数据科学过程的重要间接影响。遵循 UCD 的理念,不仅要利用用户的反馈,用户还必须参与到设计过程中。可以说,这比用户反馈更重要。如果你的用户直到反馈阶段才被考虑,你的模型将毫无用处。我们将在下面对此进行更详细的描述。**

以用户为中心的指标

判断一款车型最重要的指标是实际用户需求和反馈。理想情况下,用户的要求和需求在过程的早期就被确定,并被整合到评估指标中。这在图中显示为从“用户”到“模型评估”的红色虚线箭头。****

如果地面实况指标可用,则必须从用户需求中选择合适的指标。例如,在垃圾邮件检测中,我们可能已经确定我们的用户不在乎是否有几封垃圾邮件进入他们的收件箱,但他们确实在乎非垃圾邮件是否被归类为垃圾邮件。在这种情况下,我们最关心的基本事实是精确度,而不是召回率。如果我们使用 F1(作为一个例子),这并不能反映我们用户的需求。这种情况似乎很明显,但是在处理代理指标时会变得更加复杂。**

如果我们需要使用代理指标,我们必须构建一个以用户需求为中心的指标。构建代理指标很大程度上依赖于问题领域,因为它们通常需要特定领域的知识。通常,代理度量试图量化用户驱动的业务问题。这通常是一个很好的假设,因为在业务问题上表现良好通常意味着您的模型表现良好。**

举个例子,让我们以之前讨论的贷款违约预测为例。我们知道基本事实指标是有偏差的,所以我们想开发一个代理指标来量化模型性能。假设一个商业目标是减少我们拒绝贷款的人数。因此,一个简单的代理指标是被拒绝贷款的人的百分比。虽然这是一个过于简单的玩具例子,但它说明了思维过程。

受用户影响的监控

这个主题联系到以用户为中心的指标。我们通常监控模型的评估指标如何随时间变化。通过适当地选择指标,我们将在模型性能下降开始影响我们的用户之前发出信号,而不是在 KL-divergence(漂移检测)等任意指标超过预定义的阈值时。如果我们不根据用户需求选择我们的指标,可能会检测到一个降级的模型:**

  • 过早和不必要的频繁,造成警戒疲劳。据说“…警报疲劳是 ML 监控解决方案失效的主要原因之一”[7]。**
  • 太迟了,在我们意识到之前就影响了我们的用户体验。

需要注意的是,我们的用户应该定义我们监控的细分市场。这是一个很好的例子:

“如果你熟悉 web 应用程序的监控,你就会知道我们关心像 99%这样的延迟指标,不是因为我们担心用户每百次查询会发生什么,而是因为对于一些用户来说,这可能是他们一直经历的延迟”[7]。

这也可以应用于模型预测:某些用户的某些特征可能会导致模型对他们不如对其他用户准确。回到贷款违约预测,该模型可能在预测某个位置(例如)方面非常糟糕。这绝对不是我们想要的行为。

为了防止这种情况,重要的是监控对业务很重要的用户群群组的指标,并在任何群组表现出性能下降时发出信号。**

以用户为中心的部署

在部署新模型时,考虑用户也很重要。不仅仅是为了防止用户烦恼:我们对汇总的和优先化的反馈以及如何将它们转化为新的业务目标进行了假设。我们必须通过确保预期的积极结果在用户行为中得到反映来验证这些假设。

常见的以用户为中心的模型部署策略包括:

  • 影子测试(静默部署):新型号与旧型号一起部署。新模型对相同的请求进行评分,但不为用户提供服务。这允许在生产环境中根据以用户为中心的指标来评估模型。一个明显的缺点是没有生成用户反馈,所以我们只依赖于指标。
  • A/B 测试(Canary Deployment) :新模型被部署并提供给少量用户。这种方法在性能较差的情况下最大限度地减少了受影响的用户,同时还允许收集用户反馈。然而,缺点是它不太可能捕捉到新模型中罕见的错误。
  • 多武装匪徒(MABs) :这种方法可以看作是“动态 A/B 测试”。MABs 在探索(新模式)和开发(旧模式)之间进行平衡,以尝试选择性能最佳的解决方案。最终,MAB 算法将收敛到理想解,为所有用户提供性能最佳的模型。主要缺点是这种方法实现起来最复杂。

谨防偏见

马库斯·斯皮斯克在 Unsplash 上拍摄的照片

和大多数数据一样,偏差是存在的。有偏差的数据会产生有偏差的模型。因此,理解和减轻偏见在机器学习开发过程中非常重要。在这种情况下,有偏见的模型的一般结果是,用户群的某些部分比其他部分得到的服务差得不成比例。我们之前讨论了跨用户群的监控来暴露这个问题,但是这并没有缓解指标是偏差原因的情况。**

如果在基本事实数据中存在偏差,这将导致任何基本事实指标也有偏差。这里有两个解决方案:消除偏差,或者使用代理指标。****

然而,代理指标如果没有用心构建,也会导致偏差。Deloitte 白皮书称,“…偏差通过代理变量进入机器学习系统”[10],给出了一个在抵押价值预测器中使用受保护特征的例子。虽然使用年龄、种族和性别等特征受法规保护,但邮政编码、住所类型和贷款目的等特征“…并不直接代表受保护的特征,但确实与某个受保护的特征高度相关”[10]。因此,即使我们将所有受保护的特征排除在特征之外,如果我们选择使用相关特征的代理指标,我们仍然会无意中引入偏差。**

结论

我们用当前评估模型的方法、基本事实度量的类型、代理度量以及常见监控方法的问题来奠定基础。然后转向以用户为中心的设计,我们介绍了用户反馈循环以及如何将 UCD 应用到评估、监控和部署阶段。最后,我们讨论了用这些方法引入偏见的危险。

我希望这篇文章对模型开发给出一个更可持续的、以用户为中心的观点,并为如何将这些原则融入您自己的机器学习产品提供一个起点。

这只是开始!如果你喜欢这篇文章,请关注我以获得下一篇文章的通知!我感谢❤️的支持

***https://medium.com/@brandenlisk ***

来源

[1] A. Burkov,机器学习工程 (2020),加拿大魁北克:True Positive Inc .

[2] D. Pickell,如何创建有效的客户反馈回路 (2022),帮助 Scout

[3] H. McCloskey,关闭客户反馈回路的 7 个最佳实践,用户之声

[4] M. Treveil & the Dataiku Team,介绍 MLOps (2020),奥赖利媒体公司。

[5] 地面真相 (2022),维基百科

[6] A. Dhinakaran,监控你的模型在生产中的表现的剧本 (2021),走向数据科学

[7] J .托宾,你可能对你的模型监控有误 (2022),龙门

[8] 以用户为中心的设计,交互设计基础

[9] S. Rekhi,设计您产品的持续反馈回路 (2016),中型

[10] D. Thogmartin 等人,在人工智能模型中争取公平 (2022),德勤

[11] P. Saha, MLOps:模型监测 101 (2020),走向数据科学

[12] K. Sandburg,反馈回路 (2018),中等

[13] D. Newman,您的组织使用反馈环的情况如何? (2016),福布斯

在 R 和 Python 之间,我会向有抱负的数据分析专家推荐哪种语言?

原文:https://towardsdatascience.com/between-r-and-python-which-language-would-i-suggest-to-an-aspiring-data-analytics-expert-469b5cf391eb

近年来,我已经看到了这两种语言在数据分析领域的使用演变。以下是我的想法。

(图片来自 Unsplash)

到目前为止,我一直避免卷入那些对 R vs Python 之战感兴趣的人的争斗中。但是最近,我的同行数据极客 Brian Julius 在他的 LinkedIn 帖子中问我对 Python 与 R 之争的看法:

https://www.linkedin.com/feed/update/urn:li:activity:6996829718033399808/

我试图在帖子的评论中回答,但文本太长了🙂所以,我决定写一个帖子。

我对 Python 和 R 的看法

近年来,我已经能够观察到这两种语言在数据分析领域的使用演变。所以,我得出了以下个人结论:

✔:对于交互式数据分析和数据探索来说,r 语言要简单得多,尤其是对于分析师或那些来自商业智能领域的人来说,SQL 在商业智能领域占据主导地位。用 R 转换数据很容易让人想起使用 SQL 的人的思维过程,其优点是能够使用特定的函数来简化复杂的转换(例如数据透视),或者应用对分析有用的统计操作。
Python 转换数据的方法更多的是与程序员的经验有关。例如,不得不求助于λ表达式,对于相当基本的数据操作任务,会使任何习惯于更基于集合的方法的分析师迷失方向(这是处理数据时的正确思维方式!)并让我们意识到,开发数据争论所需的 Python 包的人主要是开发人员,而不是分析师。

✔ R 是学术界(统计学、数学、数据科学等)使用的优秀语言。因此,很有可能发现直接用 R 实现的新的数据科学算法,甚至在发现它们用 Python 实现之前。因此,如果一个项目需要使用这些新算法,就必须使用 r。

✔:就数据可视化而言,r 是让你能够为专业出版物制作漂亮图形的主要工具。当然,这种类型的图形也可以在 Python 中获得,但是不像在 R 中那样容易,特别是有无数的为添加特定图形需求而开发的包。

✔:至于职业仪表板,最广泛使用的平台已经发展到可以使用两种语言。例如,Plotly Dash 和 Shiny 都允许用 Python 和 r 开发企业级数据应用程序,对于那些连接到微软数据平台世界的人来说,Power BI 也允许使用这两种语言。

✔ Python 是一种非常清晰的通用编程语言,由于社区开发的软件包生态系统,它非常通用,主要由编程学生和开发人员使用。因此,更容易找到一个既懂 Python 又想从事数据分析的程序员。这就是为什么现在 Python 中有这么多专门用于数据转换和数据分析的包。因此,与数据分析相关的技术市场已经明显转向采用 Python。

✔在整个数据工程方面,明显的赢家肯定是 Python。首先,Python 是少数几种被所有 API 支持的语言之一,这些 API 允许与所有主要云提供商(Azure、AWS、Google)的数据服务进行交互。此外,将异构数据解决方案的使用集成到云上的生产架构中的需求推动了 Docker 容器的使用,这使得那些必须设计所有这些的人的生活更加轻松。用于与 Docker 交互的语言之一是 Python (R 不在其中)。

✔由于所有的数据工程部分都是用 Python 处理的,所以总是用 Python 开发机器学习模型通常很方便(你也可以用 r 开发它们)。这样,它们就可以通过 Python 开发的特定管道轻松发布到生产中。的确,将特定步骤集成到调用 R 脚本的 Python 管道中是可能的,但是通常由一组精通 Python 的数据工程师来完成 Python 中的所有内容会更方便和更易于维护。

大数据平台,现在大多基于 Spark ,允许在 PySpark 中开发数据转换或机器学习管道,pySpark 是一种专门为 spark 设计的 Python API。也可以通过 SparkR 语言(Spark 的一个 R API)与 Spark 交互。问题是 SparkR 并不总是在这些平台上现成地实现。更何况 SparkR 并没有实现dplyr(R 中用于数据转换的主包)与 Spark 的集成,而是由 sparklyr 来完成。但是前面提到的数据平台通常不支持 sparklyr。所以,PySpark 连同Spark SQL(Spark 的 SQL API),被广泛用作终极“大数据语言”。

当你需要解决需要深度学习解决方案(计算机视觉、自动口语识别、自然语言处理、使用 TensorFlow、Keras 和 PyTorch 框架的音频识别)的项目时,✔ Python 是必要的选择。含蓄地说,我们声明整个人工智能世界都是基于 Python 的。

也就是说,我试着回答文章标题中提出的问题。

结论

如今,由于社区开发的软件包的巨大生态系统,Python 允许您处理纯数据分析和数据科学主题以及数据工程和人工智能。的确,使用 R 语言更容易解决与统计和数据可视化更相关的特定问题,但是这些仍然是特定的情况,只占大多数需要 Python 的情况的很小一部分。

因此,尽管我本人在 R 方面比 Python 更流利,

我强烈建议那些想要解决数据分析主题的人主要致力于学习 Python 以及如何最好地使用最重要的数据转换和机器学习包(pandas,scikit-learn 等)。).

Python 的多功能性允许您在职业生涯中改变兴趣时从一个工作角色转换到另一个角色(例如,从数据科学家转换到数据工程师,反之亦然)。

这并不能抹杀这样一个事实:一旦你深入研究了 Python,学习对 R 及其 Tidyverse 生态系统的包有一个基本的了解,这绝对是一个加分项,在很多情况下会证明非常有用。您不应该低估 R 社区的规模,许多解决方案都是用 R 开发的。因此,将这种语言的知识加入您的武库是一个成功的选择。还因为,与一些人的想法相反,Python 和 R 不再是两个分割的世界。有双语 ide 允许你使用两种语言进行开发。一个例子是 RStudio ,如本文所示:

https://www.rstudio.com/blog/creating-collaborative-bilingual-teams/

因此,如果您愿意,您可以通过开发一个解决方案来充分利用这两种语言。两种语言都可以享受!🙂

警惕数据科学项目中的光环效应

原文:https://towardsdatascience.com/beware-of-halo-effect-in-data-science-projects-209c3c4af1f2

你只收集支持你的假设的信息吗?

与 Raj 在 Unsplash 上的公路旅行照片

什么是“光环效应”?

首先,让我们了解一下“光环效应”这个术语——“光环效应”这个术语最早是由心理学家爱德华·桑戴克在一篇名为《心理评级中的恒定误差》的公开论文中提出的。在这篇发表的论文中,桑代克讨论了一个人如何根据对另一个人的最初印象/感知来判断这个人。例如,当我们评价一个人有吸引力/好看时,我们也评价他们有积极的个性,如善良、聪明、诚实等。

简而言之,晕轮效应(Halo Effect)是一种认知偏差,我们倾向于根据我们的最初印象对特定的人/项目/陈述做出整体印象或结论。

“光环效应”这个词对你们很多人来说可能是新的,但是如果你退一步思考一下,你会发现它存在于你的日常生活中。例如:

  • 你如何应对营销活动——例如:牙刷的营销活动可以推动其他口腔护理产品的销售。
  • 工作场所——比如:你认为一个漂亮、穿着正式的同事有良好的职业道德。
  • 医疗保健——例如:你是否认为一个瘦子比另一个肥胖者更健康?

随着我对术语“光环效应”的了解,我意识到这种认知和错误也会发生在数据科学项目中,有时我最终会根据有限的数据做出基于初始假设或不合理偏好的决策。

在这篇文章中。我将分享不同的认知偏差陷阱,在数据科学项目中,光环效应可能会导致我们得出错误的结论并做出糟糕的决策。

确认偏差

当我们只收集支持我们的假设或预先存在的信念的数据,而忽略任何与我们的感知相冲突的证据时,就会出现确认偏差。

"你喜欢你只寻找支持你的信念的证据这一点吗?"

沉没成本谬误(本文底部对沉没成本谬误的进一步解释)也可能导致确认偏差,由此我们对项目成功和支持我们主张的渴望会影响数据科学项目的实施方式。我们可以通过选择和调整数据和模型来运行模型的多次迭代,直到我们达到支持我们的假设并忽略先前失败的迭代。确认偏差具有误导性,因为它降低了机器学习模型结果的可信度,因为它只显示了画面的一面。

虚假因果关系

错误的因果关系指的是寻找不存在的模式的倾向,例如当两个事件同时发生时,其中一个必然导致了另一个,从而产生错误的假设。数据科学家应该意识到相关性并不总是意味着因果关系。日常生活中可能发生的虚假因果关系的例子:

  • "每次我穿红色衬衫去赌博,我肯定会赢."
  • “每次我和简去巴厘岛旅行,天肯定会下雨”

“公鸡总是在太阳升起之前啼叫,因此公鸡的啼叫导致太阳升起”——对还是错?

将这一原则带回数据科学项目,当多个数据点具有相似的模式或矛盾时,数据科学家可以迅速得出结论,因为结果支持假设,而不是深入分析以找到更多证据来支持并提供逻辑解释。

报道偏差

报告偏差是指选择性报告,其中一些信息没有被报告(隐藏),而支持假设的信息在数据中变得可见。当一些结果被忽略以达到期望的结果时,就会发生这种情况。

报告偏差可能由以下原因造成:

  • 语言偏见 —当你忽略非母语的数据和报告时,就会出现这种情况。
  • 发表偏倚 —当阳性结果的研究与阴性结果的研究相比较时,会出现发表偏倚。
  • 结果报告偏差——当一家公司只报告正收益时就会发生。
  • 位置偏差 —当某些数据基于位置难以收集或定位时会发生。

在数据科学项目中,报告偏差是危险的,例如,如果数据集包含在同一位置报告的高频率犯罪案件,则开发来预测犯罪案件的机器学习模型将基于报告的位置产生偏差。这并不意味着其他地方是安全的,没有任何犯罪,这可能是由于文化差异,在另一个地方的人不愿意报告每当犯罪发生。

没有更深入的调查和提供准确的数据会导致有偏见的结果,扭曲科学的完整性。

沉没成本谬论

沉没成本谬误可能不是光环效应的结果,但可能会导致偏见,因为它描述了我们在投入时间、精力和金钱的情况下继续努力的趋势。无论是在你的个人生活中还是在数据科学项目中,你都可能见过这种情况。例如,坐着看完一部完全糟糕的电影,因为我们已经付了电影票的钱。

“我已经开始了,不妨继续下去……”

在数据科学项目中,一个项目可能会运行一年以上而没有实质性的结果,但由于已经投入了时间、精力和金钱,项目会继续进行并超出时间表。因此,沉没成本谬误的出现是因为我们受到情绪的影响,我们觉得有义务支持我们过去的决定,而不是意识到结束项目对团队和公司来说是最好的,因为资源可以用在其他地方。

结论

虽然数据可以帮助数据科学家解释、预测和做出更好的决策,但我们需要了解光环效应如何导致我们工作和从数据中学习的认知偏差。在我们的数据科学项目中,意识是防止偏见的第一步。接下来,在执行项目和仔细经历数据科学生命周期的每个阶段时,请始终保持警惕。反思实验的每个阶段,并列出任何可能影响分析的偏差。希望有了这种意识,我们可以提供更有意义和准确的结果。

感谢您阅读我的文章,如果您喜欢并愿意支持我:

参考和链接:

[1]https://www . verywell mind . com/what-is-the-halo-effect-2795906

[2]https://builtin . com/data-science/cognitive-bias-data-science

[3]https://thedecisionlab.com/biases/the-sunk-cost-fallacy/

[4]https://www . kdnugges . com/2017/12/4-common-data-fallacies . html

[5]https://towards data science . com/statistical-impects-in-data-science-ad 76 E8 EC 0584

[6]https://international-review . ICRC . org/articles/bias-machine-learning-big-data-analytics-IHL-implications-913

当心你考试分数中隐藏的错误

原文:https://towardsdatascience.com/beware-of-the-hidden-error-in-your-test-score-c88c6a3b9b1b

为什么应该报告测试集的置信区间

图 1:你的机器学习模型的测试分数受统计波动的影响。图片作者。

在实验科学中,我们习惯于用误差线和有效数字来报告估计值。例如,当您在实验室中称量样品时,您可以读出其质量,比如说,三位数。在机器学习中,这是不同的。当您评估模型的准确性时,您会得到一个数值误差达到机器精度的值。这就好像你的模型得出的精确估计是可靠的,精确到小数点后七位。不幸的是,外表可能具有欺骗性。你的考试分数中有一个隐藏的错误。数据的随机本质所固有的不可克服的变化。一个潜在的大误差,它完全决定了你的模型的性能分数的可靠性。

我说的是统计波动

情况

假设你刚被一家新的生物技术公司聘为数据科学家。你的任务?使用他们的尖端测量设备来预测患者是否需要挽救生命的手术。首席执行官对你非常有信心,并为你的项目拨款 10 万欧元给€。由于这项技术仍处于起步阶段,每次测量仍然相当昂贵,每个样本要花费€2500 英镑。您决定将全部预算用于数据获取,并着手收集 20 个训练样本和 20 个测试样本。

(您可以通过执行 Python 代码块来理解叙述。)

**from** sklearn.datasets **import** make_blobscenters = [[0, 0], [1, 1]]
X_train, y_train = make_blobs(
    centers=centers, cluster_std=1, n_samples=20, random_state=5
)
X_test, y_test = make_blobs(
    centers=centers, cluster_std=1, n_samples=20, random_state=1005
)

图 2:阳性标签(红叉)和阴性标签(蓝圈)的训练数据。图片作者。

完成测量后,您可以看到训练数据集(图 2)。考虑到这一点点数据,还是很难辨认出不同的模式。因此,您首先要使用一个简单的线性模型:逻辑回归来建立一个基线性能。

**from** sklearn.linear_model **import** LogisticRegressionbaseline_model = LogisticRegression(random_state=5).fit(X_train, y_train)
baseline_model.score(X_test, y_test)  # Output: 0.85.

实际上,这还不错:在测试集上有 85 %的准确率。建立了一个强大的基线后,你开始尝试一个更复杂的模型。经过一番深思熟虑后,你决定尝试一下梯度增强树,因为它们在 Kaggle 上很成功。

**from** sklearn.ensemble **import** GradientBoostingClassifiertree_model = GradientBoostingClassifier(random_state=5).fit(X_train, y_train)
tree_model.score(X_test, y_test)  # Output: 0.90.

哇!90 %的准确率。满怀兴奋,你向首席执行官汇报了你的发现。她似乎对你的巨大成功感到高兴。你们一起决定将更复杂的分类器部署到生产中。

模型投入生产后不久,您就开始收到客户的投诉。看起来你的模型可能没有你的测试集精度所建议的那样好。

这是怎么回事?你应该怎么做?回滚到更简单但性能更差的基线模型?

统计波动

为了理解统计波动,我们必须看看抽样过程。当我们收集数据时,我们从未知的分布中抽取样本。我们说未知,因为如果我们知道数据生成分布,那么我们的任务就完成了:我们可以完美地对样本进行分类(达到不可约误差)。

图 3:假设您从包含简单案例(可正确分类,蓝色)和困难案例(不可正确分类,红色)的分布中收集样本。在小型数据集中,您有相当大的机会获得最简单或最困难的案例。图片作者。

现在,将你的模型能够正确预测的简单案例涂成蓝色,将分类不正确的困难案例涂成红色(图 3,左侧)。通过构建数据集,您实际上是在绘制一组红色和蓝色的球(图 3,中间)。在这种情况下,精确度是所有球中蓝色球的数量(图 3,右)。每次你构建一个数据集,蓝球的数量——你的模型的精确度——围绕它的“真实”值波动

正如你所看到的,通过抽一把球,你有相当大的机会得到大部分是红色或蓝色的球:统计波动很大!随着你收集的数据越来越多,波动的幅度会越来越小,所以平均颜色会收敛到它的“真实”值。

另一种思考方式是,统计波动是你估计的误差。在实验科学中,我们通常会报告平均值、、标准差、、σ* 。我们这样说的意思是,如果和是正确的,我们预计高斯波动在[ -2σ,+2σ] 之间大约 95 %的时间。*在机器学习和统计中,我们经常处理比高斯分布更奇特的分布。因此,更常见的是报告 95 %置信区间(CI):95%情况下的波动范围,不考虑分布情况。**

让我们把这个理论付诸实践。

分辨率:带误差线的估计值

回到你在生物技术创业公司的任务,预测病人是否需要挽救生命的手术。了解了统计波动后,你开始怀疑这些波动可能是你问题的核心。如果我的测试集很小,那么统计波动一定很大。因此,你开始量化你可能合理期望的精度范围。

量化模型分数统计波动的一种方法是使用一种叫做 bootstrapping 的统计技术。Bootstrapping 意味着你随机抽取数据集,并用它们来估计不确定性。一个有用的 Python 包是 statkit ( **pip3** install statkit),它是我们专门设计来与 sci-kit learn 集成的。

从计算基线模型的置信区间开始。

***from** sklearn.metrics **import** accuracy_score
**from** statkit.non_parametric **import** bootstrap_scorey_pred_simple = baseline_model.predict(X_test)
baseline_accuracy = bootstrap_score(
    y_test, y_pred_simple, metric=accuracy_score, random_state=5
)
**print**(baseline_accuracy)  *# Output: 0.85 (95 % CI: 0.65-1.0)**

因此,虽然您的基线模型在测试集上的准确性为 85 %,但我们可以预计,在大多数时间,准确性在 65 % — 100 %的范围内。评估更复杂模型的精度范围,

*y_pred_tree = tree_model.predict(X_test)
tree_accuracy = bootstrap_score(y_test, y_pred_tree, metric=accuracy_score, random_state=5)
**print**(tree_accuracy)  *# Output: 0.90 (95 % CI: 0.75–1.0)**

我们发现差不多(75 %到 100 %之间)。因此,与你和 CEO 最初的想法相反,越复杂并不是越好。

从错误中吸取教训后,你决定退回到更简单的基线模型。不愿意让更多愤怒的客户,你清楚地传达你的模型的性能的带宽,并保持密切联系,以尽早获得反馈。经过一段时间的努力监控,您设法收集到了更多的数据。

*X_large, y_large = make_blobs(centers=centers, cluster_std=1, n_samples=10000, random_state=0)*

这些额外的测量允许您更准确地评估性能。

*baseline_accuracy_large = bootstrap_score(
    y_large,
    baseline_model.predict(X_large),
    metric=accuracy_score,
    random_state=5
)
**print**('Logistic regression:', baseline_accuracy_large)
*# Output: 0.762 (95 % CI: 0.753-0.771)*tree_accuracy_large = bootstrap_score(
    y_large, 
    tree_model.predict(X_large), 
    metric=accuracy_score, 
    random_state=5
)
**print**('Gradient boosted trees:', tree_accuracy_large)
*# Output: 0.704 (95 % CI: 0.694-0.713)**

更大的数据集证实:你更简单的基线模型确实更好。

结论

不要被你的考试成绩所欺骗:它们可能是统计学上的侥幸。特别是对于小数据集,由于统计波动导致的误差可能很大。我们的建议是:拥抱未知,用 95 %的置信区间量化你估计中的不确定性。这将防止您在真实世界的性能低于测试集的点估计值时措手不及。

承认

感谢 Rik Huijzer 的校对。

超越偏差和差异

原文:https://towardsdatascience.com/beyond-bias-variance-2e621c6c7092

考虑测量值和它们的意义之间的“看不见的差距”

在一个经验主义的时代,“数据驱动”的洞察力被自动视为优越,量化是至关重要的。事实上,对概念和现象的测量是实证科学、研究和推理的核心。然而,这种量化通常具有挑战性,因此在研究过程中会被询问。然而,“好”的量化标准可以说是一致的;这些目标是最小化偏差方差

在一次对巴黎高等商学院的访问中,诺贝尔奖获得者丹尼尔·卡内曼谈到了这两种偏见的后果,这两种偏见在思维、快与慢以及噪音中被大量唤起,这也是他最近作品的主题。在他的演讲中,我提出了一个问题,即关注上述二分法是否会导致我们忽略经验模型中的某些缺陷。有了进一步思考的空间和更多阐述这些想法的空间,我想在本文中详细阐述我的关注点。

偏差和方差:一个简短但必要的概述

为了对一种现象建模,实证研究框架依赖于各种方法来测量群体中的结构。例如,人们可能想要测量冠状病毒对健康造成的危险。为了实现这一点,可以从各个医院获得死亡率(即样本),并对它们进行平均,以实现我们估计总体人口死亡率的真正目标。人们希望这种估计总死亡率的方法具有低偏差和低方差,并且偏差和方差随着观察次数的增加而减少。但是,下图说明了在此评估过程中可能出现的关键问题。

偏差和方差概念的图解(灵感来自:来源

一个问题可能是估计量具有高方差(右上),这意味着样本医院报告的死亡率差异很大,尽管它们分散在“真实”人口死亡率周围。这样的结果表明,粗略地说,对真实死亡率的估计是分散的。另一个问题是评估过程是否有偏差(左下角),这意味着它总是在某个方向上扭曲评估。例如,我们的方法可能涉及贫困地区的抽样医院,这些地区由于医疗资源不足,死亡率可能较高。结合这两个问题,估计者可能既有偏差又表现出高方差(右下)。

受我们最小化偏差和方差的心照不宣的命令的驱使,也许在实证研究中最常用的模型是普通最小二乘回归(也叫 OLS)。如下所述,这种方法本质上是接受数据,并找到一条趋势线,使该线和它所代表的数据点之间的总误差(即“误差平方和”)最小化。根据高斯-马尔可夫定理,OLS 方法是 BLUE——即建模这种现象的最佳线性无偏方法,这意味着它是产生最小偏差和方差的方法。建立更复杂和非线性的模型,实证研究人员考虑偏差-方差权衡,再次以最小化这些值为目标。因此,经验建模方法的选择在很大程度上取决于偏差和方差的最小化。

OLS 回归是一种流行的建模方法,因为它的偏差和方差最小(图像源)

我的观点是,被偏差和方差的最小化所蒙蔽,我们经常会错过一个更基本的问题:潜在的目标是什么?上面回归图上的那些点是什么?毕竟,如果目标定义不明确,对这些因素的讨论就不那么重要了。通常,我们没有足够深入地挖掘这个目标,而是接受最容易提供量化的度量。

有没有一个“真正的”目标?

在冠状病毒死亡率的例子中,让我们考虑我们可能对这样的指标感兴趣的背景。在这种特殊情况下,这种估计背后的科学很可能是为了给决策或政策提供信息。例如,死亡率可能会影响个人对疫苗接种的决定,或对封锁的政策。因此,一个关注死亡率作为结果的模型将提供优化(在这种情况下,最小化)这一预定指标的处方。

即使对死亡率进行了准确(即低偏差、低方差)的估计,这一指标也只是在最大限度减少伤害或最大限度提高总体健康水平的大背景下可能考虑的众多因素之一。因此,死亡率是潜在利益结构的一个代理,比如一般福利。即使“真实的”死亡率和测量的死亡率之间的差距最小,在我们选择的指标和以该指标为代表的真实的目标之间仍可能存在看不见的差距

测量的结构和感兴趣的真实结构之间的“看不见的差距”

由于未能考虑所使用的度量标准和真正感兴趣的结构之间的距离,我们冒着被量化的简单性所诱惑的风险。例如,引用低死亡率作为感兴趣的结果并相应地提出政策(例如,消除封锁)可能看起来很严格。事实上,这是自由意志论者使用的一个论点,与自由主义者等其他人相比,他们似乎经常标榜自己是“合理”和“客观”的。然而,要反驳这样的论点,人们不需要依靠质疑测量的准确性;相反,更好的策略可能是质疑度量标准本身的选择。

考虑到一个人对度量标准的选择可能导致信息的丢失,人们可能会问:一个可量化的度量标准能成为“真正的”目标吗?这个问题类似于一个流行的问题:“一切都可以量化吗?”。事实上,一方面,一个可以构建一个与任何事物相关联的度量;然而,在这样做的时候,一个人不可避免地失去了在表达他们真正希望表达的更抽象的结构时的细微差别。即使在一个想法是完全可测量的情况下(例如,重量、人口),我们也很少对如此简单的结构感兴趣。更确切地说,我们更有可能使用体重这样的衡量标准来代表“健康”这样更抽象、更多维的概念。

超越偏差和方差

虽然实证科学的重点是建立准确的测量尺度,但研究也可以受益于对选择的潜在结构的思考,以及作为代理的度量选择。忽视这些问题会给人一种错觉,认为科学过程是建立在客观测量的基础上的。然而,选择作为真实潜在结构的代理措施通常是主观的,并且可以被操纵以服务于特定的议程(例如,消除基于低死亡率的封锁)。

尽管存在这些问题,但在某些情况下,与量化相关的细微差别的损失是可以接受的。例如,当我发表一篇论文,关注的现象是在线视频的消费时,人们可能会问这样的问题:他们观看了整个视频吗?他们对这个视频有多关注?人们可以在实践中进行类似的类比,例如,如果一家公司正在监控其网站的“访问量”。虽然使用简单的计数来量化这种结构可能会失去细微差别,但一些信息损失对于构建经验模型是必要的,经验模型是可以指导我们的行动和决策的抽象。

在科学领域之外,用通俗的话说,我们可以改进构建模型的方式。甚至我们在日常生活中听到的诸如“我有偏见”之类的不经意的话也应该被审视一下——你到底偏向或反对什么?你试图评估的真实结构是什么,为什么你认为你得出偏好的过程是扭曲的?如果问题本身具有内在的主观性(例如,我有偏见,因为我是巴黎圣日耳曼足球俱乐部的球迷),那么“有偏见”这个词就被误用了,因为没有“真正的”目标被估计,而不管衡量挑战如何。或者,如果偏向于一个人系统地得到的偏好(例如,作为埃马纽埃尔·马克龙的支持者,我有偏见),那么你应该问是什么阻止这个人以不同的方式进行他们的分析。

随着量化的兴起和对“客观”分析的偏爱,我们面临着忽视哲学问题的风险,比如度量和它们的理论目标之间的差距。尽管讨论这种差距需要我们进入一个微妙的主观性的更混乱的领域,但明确地这样做比含蓄地把混乱扫到地毯下要好。对于科学家来说,考虑围绕这一实践制定指导方针,最终使他们的实证分析更有说服力,这将是有益的。

超越聊天机器人:下游自然语言处理任务中基于提示的 GPT 模型的力量

原文:https://towardsdatascience.com/beyond-chat-bots-the-power-of-prompt-based-gpt-models-for-downstream-nlp-tasks-21eff204d599

图片来自许可给 Ties de Kok 的 Vecteezy。作者修改。

在过去的几年里,大规模语言模型在 NLP 社区掀起了一阵风暴。生成式预训练变压器( GPT )模型,如 OpenAI 的 GPT-3 和 EleutherAI 的 GPT-J 6 & GPT-NeoX-20B,在生成与人类生成的文本难以区分的文本时,已经显示出令人印象深刻的结果。这种 GPT 模型的一个直观用例是像聊天机器人或人工智能说书人一样的对话,你给模型一个问题或故事提示,模型继续它。然而,使用 GPT 模型进行其他任务的基于提示的机器学习的巨大潜力往往不太直观,因为它体现了某种范式的转变。在这篇文章中,我将讨论如何使用创造性提示工程和 GPT 模型来帮助解决您关心的下游 NLP 任务。

对于门外汉来说,什么是 GPT 模式?

GPT 模型家族包括能够预测记号序列中的下一个记号的生成语言模型。这些模型通常表示由数十亿个参数组成的深度神经网络,这些参数是在主要从互联网上收集的大量文本数据上训练的。这意味着这些模型不是为特定任务而训练的,它们只是基于前面的文本生成文本,不管它是什么。这听起来可能不是特别有用,但它更类似于人类的交流方式。有人可能会问你一个问题(即前面的文本),你提供一个答案(即生成的文本)。例如,如果我给 OpenAI GPT-3 模型一个提示,比如:

"华盛顿大学位于哪里?"

它将生成如下所示的响应:

"华盛顿大学位于华盛顿州西雅图市."

将 GPT 模型用于下游 NLP 任务

很明显,这些 GPT 模型功能强大,可以生成与人类生成的文本难以区分的文本。但是,我们如何让 GPT 模型执行诸如分类、情感分析、主题建模、文本清理和信息提取等任务呢?一个自然的想法可能是只要求模型执行这样的任务,但这可能是复杂的、不可预测的和难以控制的。例如,假设我有一个关于员工薪酬的研究项目,我的下游 NLP 任务如下:从员工评论中提取所有与薪酬相关的句子,并将其分类为积极、消极或中性。

如果我让一个人做这件事,我可以这样表述:

以下是评论:

对于那些对管道行业感兴趣的人来说,管道公司是一个很好的工作场所。公司总是在扩张,有提升的空间。然而,工资太低,这是为管道公司工作的唯一缺点。总的来说,看看吧!

任务是:

请说出与薪酬相关的句子,并将其分为积极、消极或中性。

答案是什么?

如果我给出这个提示,打开 AI GPT-3 达芬奇,他们最大最贵的模型,并运行几次,它会给我以下响应:

然而,工资太低,这是在管道公司工作的唯一缺点。

试试 2: 否定:“但是工资太低,这是在管道公司工作的唯一缺点。”

试三:否定:工资太低

老实说,这些结果令人印象深刻,尽管没有给出任何例子,但模型提供的结果在某种程度上符合我们的需求。然而,结果并不一致,解析大范围的答案将是困难的,如果不是不可能的话。那么,我们如何让这个工作?答案是提示——工程微调。

通过适当的快速工程和足够多的例子,我们可以使用单个 GPT 模型来完成几乎任何下游 NLP 任务,包括:

  • 文本分类
  • 主题建模
  • 文本清理、文本校正和文本规范化
  • 命名实体与信息抽取
  • 更重要的是,你的创造力是极限!

让我给你看一个例子(带代码🔥)

好了,让我们看看员工评估的例子。作为复习,我们的目标是从员工评估中提取所有与薪酬相关的句子,并将其分类为积极、消极或中性。这个例子的代码包含在最后链接的 Jupyter 笔记本中。

为了说明这一点,以下是一些带有薪酬判决的员工评估示例:

回顾# 1——消极情绪: 对于那些对管道行业感兴趣的人来说,管道公司是一个很好的工作场所。公司总是在扩张,有提升的空间。然而,工资太低,这是在 T21 水暖公司工作的唯一缺点。

回顾# 2——积极情绪: Plumbing Co 是一家值得为之工作的伟大公司! 报酬丰厚,高于行业标准。 福利也很不错。这家公司很公平,对员工很好。我肯定会向任何想找个好地方工作的人推荐 Plumbing Co。

回顾# 3——中性情绪:
我已经在 Plumbing Co 工作了几个月,我发现这是一个相当不错的工作场所。 工资挺一般的,但是咖啡真的很棒。总的来说,我认为这是一家不错的公司。时间是合理的,工作是相当容易的。我会把它推荐给任何想找份体面工作的人。

如果没有 GPT 模型,我们可能会使用类似如下的机器学习管道来解决这个任务:

  1. 做一个彻底的清理,以确保文本是一致和正常的。
  2. 使用像 Spacy 这样的库将每篇评论分成单独的句子。
  3. 建立一个关键词列表,找出与薪酬相关的句子。
  4. 通过手动将薪酬句子分类为积极、中性或消极,创建一个大的薪酬句子训练样本。
  5. 使用类似于
    TF-IDF 或单词嵌入将文本转换成数字表示。
  6. 在训练样本上训练一个有监督的机器学习模型(如 SVM 的朴素贝叶斯)。
  7. 在你的预测模型中运行每个关于薪酬的句子,并将其链接回评论。

这种方法没有错,但是工作量很大,需要大量的自由决定,并且不是特别灵活。例如,如果补偿词的拼写稍有不同,它将不会被拾取,或者如果您没有足够的训练数据,或者在训练步骤中犯了一个小错误,预测模型可能会过拟合,并且可能无法正常工作。总的来说,这个预测管道需要大量的时间、计划和关注才能正确。

因此,让我们来看一个基于提示的 GPT 管道,并进行比较:

  1. 对文本进行粗略的清理,使其相当干净。
  2. 设计一个执行任务的提示和完成。
  3. 创建一个小的训练样本来为模型生成示例。
  4. 微调通用 GPT 模型,开始生成您想要的完成(这是可选的,取决于您的任务的复杂性)。
  5. 使用您的模型为每个提示生成一个完成信息。
  6. 从生成的完成中解析信息。

因为 GPT 模型已经有了很强的语言理解能力,它使我们能够省去很多麻烦,直接跳到制定我们的任务(即提示)和想出好的例子。作为一个奖励,GPT 管道也有可能为许多任务带来更好的性能,太棒了!😄

即时工程

基于提示的 GPT 方法的主要“范式转换”是我们必须使用自然语言设计一个提示和完成,以使模型做我们想要的。这通常被称为即时工程,这很重要,因为这是告诉模型我们想要它做什么的主要方式。我认为这是一种范式转变,因为相对于围绕数字、向量和矩阵的更传统的管道,它需要一种从根本上不同的方式来思考你的问题。一个仔细的提示设计将会给你最好的预测性能,它也将会使以后容易地处理生成的完成成为可能。

让我们为我们的任务设计一个提示和完成:

提示+完成:
水暖公司是一个值得为之工作的好公司!报酬很高,超过了行业标准。福利也很好。这家公司非常公平,对员工很好。我肯定会向任何想找个好地方工作的人推荐 Plumbing Co。

#

<转正>薪酬丰厚,高于行业标准
<转正>福利也很不错。
< |endoftext| >

我们的提示以 review 开始,以结束\ n # # # # \ n .“\ n # # # # \ n”很重要,因为它告诉我们的模型提示在哪里结束,完成在哪里开始。每个补偿句子由一行组成,以箭头括号内的情感开始。我们用 < |endoftext| > 结束完成,这是一个常见的停止指示符,以便我们可以告诉 API 何时停止生成令牌。这里的完成是为了让我们以后可以很容易地解析它。迫使每个句子都在一个新的行上使我们能够区分句子,并且将情感放在箭头括号中使我们能够容易地提取它。正如 Jupyter 笔记本中所展示的,这种完成设计使我们能够使用一个相对基本的正则表达式来解析整个完成。

教导模型来生成我们的完成

您可以通过以下三种方式之一从 GPT 模型生成预测:

  1. 零拍

    不给模特任何例子;只要给它你的提示。

  2. 在你的提示中包含一些提示+完成的例子,以表明你期望从模型中得到什么类型的完成。
  3. 微调

在零镜头场景中,模型不会看到你的完成,因此它会根据常规文本中常见的内容来猜测接下来应该发生什么。这类似于我之前展示的例子。如果我们希望模型生成我们的特定完成,我们将需要给它例子。对于更一般的任务,在你的提示中给出一些例子就足够了,这就是所谓的“少量拍摄法”。这很简单也很直观,但是,它限制了我们可以给模型的例子的数量,并且我们每次想要做预测时都需要给它这些例子,这很慢并且不划算。下面是使用 OpenAI 游乐场的几个镜头示例:

使用 OpenAI 操场进行少量预测的示例。以绿色突出显示的文本反映了模型生成的完成。

微调定制模型使我们能够为通用 GPT 模型提供更多的 prompt+completion 示例,以便它将学习如何在出现我们的一个提示时生成一个 completion。这是一种更耗时的方法,但对于更复杂和特定的下游 NLP 任务来说,这通常是必要的。配套的 Jupyter 笔记本指导您使用 OpenAI API 为我们的员工评审用例进行微调。

基于提示的 GPT 方法的利弊

每种方法都有优点和缺点。为了帮助您评估 GPT 方法是否适合您的项目,让我根据自己在几个研究项目中使用它的经验总结一下利弊。

优点:

  • 基于提示的机器学习使你能够使用人类语言来设计你的任务,一旦你习惯了,这通常更直观。此外,因为您实际上可以阅读完成情况,所以也更容易快速检查您的预测对您的任务是否有意义。
  • GPT 模型非常灵活。唯一的要求是你可以用一个提示+完成来表达你的任务。创造性的快速工程为自动化下游任务开辟了许多机会,而这些任务用传统方法是很难完成的。
  • 因为 GPT 模型是在海量数据上训练出来的,所以你通常只需要给它几百个例子,它就能开始可靠地执行大多数下游任务。这使得生成高质量的黄金标准训练样本比生成需要成千上万个样本的场景更加可行。
  • 基于提示的 GPT 管道可以处理偶尔的文本缺陷和文本细微差别,因为它们在底层训练数据集中相当普遍。这意味着您做出的自主文本处理选择的影响较小,这通常会导致更可靠且更容易重现的预测。

缺点:

  • 微调和推断(即进行预测)可能需要大量计算,并且需要特定的最新 GPU 资源。你可以通过使用机器学习即服务(MLaaS) 解决方案来规避这个问题,比如 OpenAINLP CloudForefront 。然而,这些都是付费服务,费用通常根据你需要做的预测数量而定。由此产生的成本可以是非常容易管理的(例如,低于 100 美元)或非凡的,这取决于你的提示+完成长度和你的预测数量。
  • 评估预测的准确性和性能可能比传统方法更具挑战性,可能需要编写自己的评估逻辑。例如,在对我们的员工评论进行分类的情况下,我们需要编写一些代码来计算我们关心的度量标准的坚持性能。
  • 如果您使用更大的 GPT 模型,如 GPT-3、GPT-J 或 GPT-NeoX-20B,推理吞吐速度会相对较慢,因为每个预测都需要传播数十亿个参数。针对长而复杂的提示运行大量的预测(例如,100 万以上)可能需要几天或更长时间才能完成。
  • 设计合适的提示和补全需要一些尝试和错误,大多数时候是正确的,有时这更像是一门艺术而不是科学。处理补全还需要一些基本的 Python 编码技能,最好还需要一些正则表达式方面的经验。

总结

我希望这篇文章能让你更清楚地了解如何使用创造性的提示工程来使用 GPT 模型完成下游的 NLP 任务!为了帮助您开始,我还编写了一个 Jupyter 笔记本,它将带您完成设计提示、微调模型以及使用 OpenAI API 进行预测的所有步骤。您可以在这里找到存储库,下面是笔记本的快速链接:

https://nbviewer.org/github/TiesdeKok/gpt-prompt-example/blob/main/prompt_based_gpt_example.ipynb

我需要你的帮助!👋我正在考虑写一篇研究论文,更详细地介绍在研究项目中使用基于提示的 GPT 的承诺和注意事项。你有兴趣看这样的论文吗?如果你是,如果你能通过下面的表格表达你的兴趣,那将非常有帮助。你也可以选择注册一个通知。非常感谢!🙏

你可以找到 https://forms.gle/wo5aStgux3SvktmN8 here✏️:的表格

具有 PyNeuraLogic 的超越图神经网络

原文:https://towardsdatascience.com/beyond-graph-neural-networks-with-pyneuralogic-c1e6502c46f7

走向深度关系学习

Python 中的可微分逻辑编程,用于 GNNs 向更复杂的深度关系模型的优雅编码和扩展

PyNeuraLogic 允许您使用 Python 编写可微分的逻辑程序,以简单优雅的方式编码,例如各种 gnn 及其基本扩展。图片由来自pyneuralogy的 Lukas Zahradnik 提供。

在之前的文章中,我们讨论了关系机器学习的问题,如何在关系逻辑中自然地表达它,以及为什么它不能用标准的基于特征向量的模型(如经典的神经网络)来完全解决。然后,我们回顾了旨在将逻辑与神经网络相结合的神经符号整合范式,简要回顾了它的历史,并指出主要挑战。最后,在上一篇文章的中,我们讨论了结构化深度学习模型的最新进展,如图形神经网络(GNNs),如何用关系逻辑优雅地解决。

在本文中,我们将在一个实用 框架中演示这些原则,该框架名为 神经对话 ,旨在将深度学习与(可微分)逻辑编程相融合。我们将展示您如何使用相应的 Python 库来轻松实现和扩展现有的 GNN 模型,使其具有更复杂和更通用的功能,利用这些功能,您将能够开始自己创建新颖的深度关系学习 架构

数据表示法

让我们从如何表示数据开始。NeuraLogic 中的数据样本以加权关系逻辑的表达格式存储(然而,为了方便起见,在基本情况下也有从普通张量格式的转换器)。正如在之前的文章中所讨论的,这是一个非常通用的形式主义,涵盖了所有种类的表示。因为我们将(再次)在这里使用众所周知的 GNNs 作为例子,所以让我们更仔细地看看相应的数据格式的表示。

简单来说,在关系逻辑中,我们定义了 之间的 关系 ,可以表示各种感兴趣的对象。那么一个图就是节点间二元边关系的集合。因此,给定一些节点n1…nk,我们可以写:

edge(n1,n3), edge(n2,n3), edge(n4,n5), …

请注意,这里的名称“edge”是任意的。我们可能会为图的二元关系想出任何名称,例如

next(n1,n2), next(n2,n3), ... *or*   parent(n4,n1), parent(n4,n2), …

例如,表示图(序列和树)的一些特殊情况。我们还可以在相同的节点之间引入多个不同的关系:

related(n1,n3), neighbor(n1,n3), closeby(n1,n3), …

诸如此类。注意,节点(项)的顺序表示(二元)关系是否对称,即这里的图是否是无向的。

  • 这种对称性也可以隐式指定,例如使用规则
    edge(Y,X) <= edge(X,Y),但稍后会详细介绍规则…

在 GNN 场景中,我们通常也将节点与一些属性或特性相关联。在分类属性的情况下,我们可以通过引入一元关系停留在清晰逻辑的领域,例如:

positive(n2), red(n1), …

对于数字特征,我们可以进一步将这种陈述与(张量)值相关联,例如

[0.725, -1.6, …, 1.0] features(n2), ...

诸如此类。同样,名称“features”在这里是任意的,多个不同的表示可以与每个节点相关联。

与许多 GNN 框架相比,将边缘与特性相关联是非常简单的:

[1.2, -0.6,…] edge(n1,n3), ...

超越图形表示的一瞥

虽然我们可以用这种方式表示任意的 GNN 数据,但是在神经程序库中没有任何东西会限制您使用图形格式。因此,您可以随心所欲地将多个节点与一条“边”(hypergraph)关联起来:

edge(n2,n3,n5,n4), ..., simplex(n2,n3,n4), complex(n1,n3,n5,n6)

或者创建全新的对象并将这些对象与新的关系和值相关联:

edge(n1,n2,e12), 0.9 covers(e12,n2), [0.8,1,…] in_graph(g1,e12), ...

诸如此类。

  • 提示:数据表示基本上是一个关系数据库,每个表的每一行都有一个可选的(张量)权重。

然后,您可以将这些进一步组合成更复杂的表示(这样就可以用于促进更高级的推理过程,超越经典的基于边的消息传递方案)。

模型表示

用神经语言表示模型与标准的深度学习框架有更大的不同。与现有的程序性方法相反,直接在计算图形级别上操作张量表示,NeuraLogic 使用声明性逻辑编程来抽象模型的计算原理。

  • 你肯定知道,比如 SQL,它也是一种声明性语言。

虽然这对于经典的张量计算深度学习问题来说可能有点多余,但在 关系 学习 设置中,它变得非常优雅,展示了各种形式的 对称 。在这种情况下,使用 NeuraLogic 会产生更加紧凑和优雅的学习程序,直接表达每个模型的本质(例如 GNNs 中的局部置换不变性 w.r.t .节点和边)。

简而言之,所利用的逻辑编程范例是一种声明式编码方法,在该方法中,您声明您在 逻辑变量 之间的 关系 ,对应于域的对象(用大写字母书写,以区别于术语)。例如,我们可以写

edge(X,Y)

描述图中任意一对节点(X 和 Y)之间的所有边的集合。这些可以进一步组合成 图案 ,例如

edge(X,Y), edge(Y,Z), edge(Z,X)

在这样的图中表示三角形(三圈)。最后,这些模式可以用来形成驱动计算(推理)的所谓“规则”。例如,我们可以写

in_triangle(X) <= edge(X,Y), edge(Y,Z), edge(Z,X).

为出现在三角形中的每个节点导出(推断)新的属性“in_triangle”。

一组这样的规则然后形成所谓的逻辑程序,其执行等同于用规则执行逻辑推理。并且神经对话框架然后使这个推理过程可微分,这又使它等价于深度(关系)学习中的正向传播。最后,通过将(可学习的)权重与规则相关联,这些程序可以被参数化,例如

W₀ in_triangle(X) <= W₁ edge(X,Y), W₂ edge(Y,Z), W₃ edge(Z,X).
  • 这样的规则然后可以直接用于形成,例如,各种子图/小图/基序/细胞 gnn 和类似的模型

如果你不熟悉这个领域,这听起来可能有点太抽象了,让我们继续看上一篇文章中的基本 GNN 漫游示例,让事情有个基础。

神经语言学中的 GNNs 编码

从上一篇文章的中,我们知道任何 GNN 模型的核心都有一个所谓的传播规则,用于在相邻节点之间传递和聚合“消息”。特别地,节点 X 的一些新表示(“新消息”)是通过聚集相邻节点 Y 的先前表示(“旧消息”)来计算的,即那些在 X 和 Y 之间具有“边”的表示。

new_message(X) <= old_message(Y), edge(X,Y).

这,在神经语言学中,就是 实际代号 !它简单地读作:“要计算任何对象 X 的‘new _ message’属性值,取一些其他对象 Y 的‘old _ message’属性值,其中两者之间存在关系‘edge’。”

这个简单的语句编码了经典 GNN 的计算原理。最后,为了实际学习一些有用的(分布式)节点表示,我们只需将可学习的参数(矩阵 W )附加到该计算中,例如

W2 new_message(X) <= W1 old_message(Y) , edge(X,Y).

然后,底层计算将在(邻域)聚合之前通过一个可学习的 W1 矩阵,以及随后在聚合之后通过一个 W2 矩阵来投影您的输入节点嵌入(‘old _ message’)。假设您在 NeuraLogic ( tanh+avg )中保留默认的激活/聚合功能设置,则该加权规则表示一个计算:

这是图形卷积层(的变体)的正式定义。

引擎盖下的一瞥

现在我们已经有了关系数据和编码的模型,我们可以开始在数据上训练模型了。这里,我们只需要确保输入数据表示与模型表示相匹配,即对应的关系被命名为相同的。然后,引擎将匹配这些表示,并通过规则开始数据的推断,也称为前向传播。

在关系逻辑中,这个过程可能相当复杂,大量的 中间概念 被自动归纳。这正是我们利用简单优雅的程序对复杂的深度关系学习架构建模的优势。

让我们再次访问 GCN,揭示更详细的基本过程。假设我们学习分子数据,这是一个突出的 GNN 应用领域。当然,通过指定包含的原子‘a’(例如,a(o1),a(h1),…)和它们之间的键‘b’(b(O1,h1),…),分子图可以容易地用逻辑表示。接下来,我们定义 GCN 模型规则,就像上面一样,但是使用分子中使用的原子(“a”)和键(“b”)的特定命名:

Wh₁ h(X) <= Wa a(Y), Wb b(X,Y).

其中“h”是原子的新诱导(“隐藏”)表示(下一个“层”)。然后,我们通常想要将分子相对于某个目标进行分类,为此,我们从原子表示中归纳出另一个全局表示“q”(查询),用于图形级“读出”:

Wq q <= Wh2 h(X).

最后,通过神经对话引擎运行该关系规则集将会产生反映逻辑推理过程的可微分计算图,该逻辑推理过程同时等同于 GCN 运算:

一个简单的逻辑程序,有两个规则编码一个经典的 GCN 层,然后是一个图形级别的读数。在接收到 2 个示例分子时,2 个可微分计算图被动态创建。如果你仔细观察,这些正是 GCN 的底层计算步骤。图片作者[3]。

在此过程中,输入结构被直接映射到“事实节点,形成对“规则节点的输入。这些是通过逻辑变量的所有有效替换从程序规则中实例化的,这由底层推理引擎负责。规则节点可以被模糊地认为是“卷积”操作的实例化,因为它们也在(基础)模型中引入了权重共享。然后,这些规则节点被聚集在“聚集节点中,以产生新的表示,表示为“原子节点,对应于规则的“头部”(每个规则的左侧)。并且相同的原理然后递归地应用于以蓝色显示的下一个(“读出”)规则。

最后,我们让与规则相关联的权重( W )被(自动)优化,以通过标准方式的梯度下降来反映给定查询( q )的期望输出值( Aq )。

更多详情,可查阅 Github 上的神经语言学库或阅读相关的 超越 GNNs 论文【3】。如果你想在更广泛的(科学)背景下理解概述的原则,欢迎你查看这篇论文,深入探讨用关系逻辑表示法进行深度学习的主题

到目前为止介绍的编码适用于简单的神经逻辑框架,其中输入数据和模型是从明文解析而来的。当然,然而,在纯文本文件中开发模型不是很流行,对于机器学习实验来说也不是很方便。

考虑到这一点,现在让我们将这种声明性的、可区分的逻辑编程范式嵌入到普通 Python* 中,并进入我们都习惯于深度学习的便利环境。为此,我们将使用一些智能 Python 操作符重载,为了保持简洁,我们现在将通过将这些对象“键入”为以下内容来明确标记什么是逻辑关系以及什么是*变量**

*Relation.message(Var.X)     *or, in short:*        R.message(V.X)*

然后,我们可以直接在 Python 中将与上面 相同的 GCN 规则编码为

*R.new_msg(V.X)[5,10] <= (R.old_msg(V.Y)[10,20], R.edge(V.X,V.Y))*

包括相应的(矩阵)参数化的维度说明。如果您想要更改默认的激活/聚合功能,您可以将此信息(以及其他信息)附加到每个规则,例如

*(... <= ...) | [Activation.RELU, Aggregation.AVG]*

把范围缩小到经典的 GCN 层。

这就允许直接将引入的、相当非正统的深度关系学习范式的好处与 Python 语言和生态系统的熟悉且方便的特性相集成。使用 PyNeuraLogic 您现在可以以一种方便的方式开始快速开发新颖的深度关系模型,这种方式感觉与现有的深度学习框架非常相似。

图形深度学习及其他

引入的 GCN 漫游示例现在可以很容易地扩展到各种其他 GNN 模型和学习场景。例如,你可以直接开始的一个有趣的扩展是包括子图模式,也称为“graphlets”或“motifs”等。,在规则中,比如我们上面开始的in_triangle例子。这增加了 GNNs 的表达能力(超出了 WL 测试),并且这种“子图 GNNs”的几种变体(例如单纯 GNNs )目前正在被开发。PyNeuraLogic 是一个非常合适的框架,用于编码这些的各种修改和扩展。

然而,重要的是,PyNeuralogic 绝不仅限于 GNN 模型。⁴让我们回忆一下所使用的声明性关系逻辑形式主义带给我们什么。首先,有了关系逻辑,我们不必关心如何将关系数据转换成张量(例如,如何将一个图排列成邻接矩阵)。由于没有这种隐式张量表示,我们现在可以直接处理由精心制作的模型假设的对称性,而是专注于它们的表现力,这很符合几何深度学习的精神。

例如,当我们写message(X),表示所有节点各自的表示时,这里没有引起潜在的排序(例如,到向量中)。因此,这种一元关系语句可以直接用于定义在元素集合(“深度集合”)上操作的各种置换不变深度学习架构。进一步移动到二元关系,比如我们的edge(X,Y),同样没有潜在的邻接矩阵表示。因此,我们可以直接使用在图同构不变性假设下运行的架构,例如 GNNs 中所演示的节点和边的局部置换。⁵

将这些边的组合扩展到关系模式(例如子图)中,然后与完全相同地工作,因此这些不需要特殊的(预处理)处理。当我们进一步将递归引入规则时,可以包括各种递归神经架构,以组合(分形)对称性操作。**

请注意,在 PyNeuraLogic 中,这些原则不仅仅是一些数学抽象,而是直接可操作的,因为这正是您对模型本身进行编码的方式!**

最后,当你超越图形,引入多个更高层次的关系,并将它们组合成更复杂的模式和层次规则时,这就是新的深度关系学习模型等待你去探索的地方。给你一些开始的想法,在 PyNeuraLogic 中,你可以直接玩:

  • 多重关系和对象类型
  • 嵌套图、超图、关系数据库**
  • 软模式匹配
  • 替代表示传播方案
  • 包含逻辑背景知识
  • 还有更多…

在框架中,所有这些想法都采用了简单的小型(可微分)逻辑程序的相同形式,由于它们的声明性,这些程序通常是高度透明的并且易于理解。⁶

所有这些特性使得 PyNeuraLogic 非常适合您探索图形深度学习的一些前沿领域!

从学习到推理

虽然我们已经将 GNNs 和类似模型训练中的 PyNeuraLogic 的解释作为出发点,但是所使用的逻辑形式自然也允许处理一些超越 ML 观点的通用 AI 能力。自然,逻辑是人工智能中任何形式的推理背后的核心形式主义,它在 PyNeuraLogic 中的使用允许你在学习和推理之间平稳地转换,这是一种对一般人工智能系统越来越重要的能力。**

特别是,我们迄今为止用来对神经模型进行编码的关系规则,可能会采取更“有指导意义的”形式,以引导逻辑推理进入更复杂的推理任务。例如,让我们重温一下著名的 DeepMind 对伦敦地铁路径推理的演示(T21)。

伦敦地铁系统的一部分。来自 PyNeuraLogic 文档的 Lukas Zahradnik 的图片(灵感来自 P. Flach 的简单逻辑逻辑编程介绍)。

虽然这种形式的“算法推理”对于符号化的人工智能方法来说非常简单,但在神经模型(“可微分神经计算机”)中对其进行编码需要 DeepMind 进行大量的努力和训练(由于“不适当的”、命题的、固定大小的张量学习表示)。

在 PyNeuraLogic 中,由于逻辑推理和神经推理之间的二元性,解决这类问题非常简单。特别是,我们可以从采用符号方法开始,这里的路径是一个简单的递归定义,由两条规则组成,表示:

  1. 如果两个站(X,Y)在数据中直接连接,则存在路径 X -> Y(递归的终止条件):
***R.path(V.X, V.Y) <= R.connected(V.X, V.Y)***

2.并且如果 X 连接到 Y ,则存在路径 X -> Z,并且存在路径
Y- > Z(递归定义):

**R.path(V.X, V.Z) <= ( R.connected(V.X, V.Y), R.path(V.Y, V.Z) )**

在一些(图形)数据上运行逻辑推理,该逻辑推理是在将规则翻译成计算图形时自然执行的,例如编码为:

**R.connected(T.bond_street, T.oxford_circus),
R.connected(T.oxford_circus, T.tottenham_court_road),
...**

然后将产生所有(并且仅仅是)被查询的任何站之间的有效路径。当然,这可以通过任何纯符号推理引擎轻松实现。然而,在 PyNeuraLogic 中,我们可以进一步将数值分配给逻辑关系,例如,表示地铁站之间的距离:

**R.connected(T.bond_street, T.oxford_circus)**[7]****

并且使用相同的推理引擎来产生例如最短的路径代替(用min作为聚合函数)。最后,由于在 PyNeuraLogic 中,这种推理是 可微分的 ,我们可以将可学习的权重附加到规则上,并在这些路径表示的基础上探索各种学习任务。⁷

计算性能

好的,所以这个新框架使用声明性编程通过关系逻辑中指定的底层对称性来编码(高级)神经模型,而不是在(命题)张量表示之上的代数运算的常见编码。

现在,您可能认为这种基于逻辑推理的疯狂方法至少会带来一些可怕的计算复杂性问题(这是任何与逻辑有关的事情的典型情况,对吗?).

毕竟,张量表示和并行处理的诱导可能性是深度学习的计算成功的背后。然而,对于具有稀疏和不规则数据表示和计算图形的关系问题来说,情况并非如此。在那里,“把一切都变成张量”思维偏见实际上可能会使模型不仅可读性更差,而且效率更低!

PyNeuraLogic 实例化了这些见解,在允许您轻松声明更具表达力的模型的同时,它还经常在自己的游戏中用经典的 GNN 模型击败标准框架的计算性能。**

PyNeuraLogic 不仅天生具有高度的表现力,而且速度也非常快!这尤其适用于具有大量稀疏图形的(分子)数据。PyNeuraLogic 性能指标评测中的图表。

查看我们的基准测试,我们证明对于一系列常见的 GNN 模型和应用程序,比如用分子学习,PyNeuraLogic 实际上比流行的 GNN 框架要快得多。

这(部分)也是由于从符号人工智能(“提升推理”)中已知的高级加速技术,由于 PyNeuraLogic 中使用的逻辑表示,这些技术也可以应用于神经模型(如这篇 ICLR 论文【8】所示)。

一种受 SRL(符号)“提升推理”启发的技术在 NeuraLogic 中用于结构化神经模型(如 GNNs)的计算图中对称的无损压缩。图片由作者提供(来自论文[8])。

结论

最后,我们介绍了一个新的,相当非正统的,用于深度关系学习的 Python 框架,它将神经网络与关系逻辑相结合,以实现具有高级学习和推理能力的复杂神经架构的开发。对于初学者来说,它能够有效地捕捉经典的 GNN 模型,但它天生更具表现力。

我们诚挚邀请您尝试pyneuralogy搭配

*$ pip install neuralogic*

并开始探索你自己的新的深度关系模型的想法。也有一些例子在线笔记本应该可以让你开始。

  • 如果您有任何改进或合作的想法,请联系我们!

如果没有别的,我们希望至少为你提供了一个关于 GNN 模型类的新视角,以及深度学习和符号人工智能范式如何能够很好地一起发挥作用…

1.还要注意,对于如何将这些表示混合在一起没有语法限制,因此可以选择数据的哪些部分更好地用(子符号)分布式数字表示来建模,以及哪些部分可以用纯逻辑方式来表示,并根据需要沿着这个维度连续移动。

2.请再次注意,这里的名称完全是任意的,唯一重要的是表达逻辑变量之间关系的有向关系模式,它通过 GNNs 中的 WL 捕获了局部邻域聚合的原理。

3.苏里克、古斯塔夫、菲利普·切列兹尼奥夫和 ondřej·库泽尔卡。"超越图神经网络与提升关系神经网络."机器学习110.7(2021):1695–1738。

4.这就是为什么,例如,不需要像在一些工作中所做的那样,对图外的子图进行预处理并在顶部运行标准 GNN,而是整个消息传递方案可以改为直接在子图上操作,如最近的 cellular GNNs 或前述的“molecular-ring GNNs”[11]NeuraLogic 的演示所提出的。

5.注意,同样的假设也适用于流行的 Transformer 架构,在这里,我们只假设一个完全连通的图(这相当于不假设任何边,而只是聚合给定范围内的所有其他对象)。

6.因此,您没有必要为底层(例如,GNN)计算的每个小的修改设计一个黑盒类名的动物园,因为您在这里直接在逻辑原则的级别上编码。

7.在这个简单的例子中,这基本上相当于在引擎盖下训练许多小型递归神经网络。

[8] Sourek、Gustav、Filip Zelezny 和 Ondrej Kuzelka。"通过提升的结构化卷积模型的无损压缩."ICLR2021。

[9] Sourek,Gustav,等人“提升关系神经网络” arXiv 预印本 arXiv:1508.05128 (2015)。

[10] Sourek,Gustav,等人,“提升关系神经网络的堆叠结构学习”国际归纳逻辑编程会议。施普林格,查姆,2017。

[11] Sourek、Gustav、Filip Zelezny 和 Ondrej Kuzelka。"用图形神经网络之外的分子学习." arXiv 预印本 arXiv:2011.03488 (2020)。

作者非常感谢Lukas Zahradnik校对这些帖子并开发pyneuralogy

超越输入输出推理:认知人工智能的四个关键特性

原文:https://towardsdatascience.com/beyond-input-output-reasoning-four-key-properties-of-cognitive-ai-3f82cde8cf1e

世界模型、心理理论、持续学习和后约束环境的必要性

图片来源:James Thewvia土坯股票

人工智能研究可能是一种令人谦卑的经历——有些人甚至声称,在复制人类智力的最基本方面时,它仍然处于相对停滞状态。它可以纠正拼写,可以进行金融投资,甚至可以作曲;但是如果没有被明确地“教导”这样做,它就不能提醒人们他们的 t 恤穿反了。更重要的是,它不明白为什么这可能是有用的信息。然而,一个刚刚开始自己穿衣服的五岁小孩,会注意到并指出她父亲脖子根部的白色标签。

人工智能领域的大多数研究人员都很清楚,在深度神经网络的众多进步中,人类智能和人工智能之间的差距并没有显著缩小,在复杂的现实生活环境中实现计算高效的自适应推理和决策的解决方案仍然难以实现。认知人工智能,即允许机器理解、学习和执行类似于人类的智力任务的智能,仍然像以往一样难以捉摸。在这篇博客文章中,我们将探索为什么这个鸿沟存在,如果我们有希望跨越它,人工智能研究必须走向何方。

为什么【艾先生】不能保住工作

与一个可以整天跟着我们,收拾我们的烂摊子的人工智能助手并肩工作有多好?想象一下,如果算法能够真正将我们从工作日的“苦力”任务中解放出来,让我们能够专注于工作中更具战略性和/或创造性的方面,那该有多好?问题是,除了像 GitHub Copilot 这样的系统的部分例外,一个基于当前最先进技术的虚构“AI 先生”可能会在工作日结束前收到解雇通知。

首先,艾未未非常健忘,尤其是在上下文记忆方面。它还受到严重缺乏关注的困扰。对于一些人来说,这似乎令人惊讶,因为今天有非常大的语言模型(LLM),包括 LaMDAGPT-3 ,在某些情况下,它们可能很有意识。然而,即使使用最先进的深度学习模型,艾未未的工作表现也总是达不到预期。它不能很好地适应不断变化的环境和需求。它不能独立确定它提供的建议在认识论上和事实上都是合理的。它甚至不能想出一个简单的计划!无论它的社交技能设计得多么精心,它都注定会在这个充满复杂社会和道德规范的高度动态的世界中跌倒。它根本不具备在人类世界茁壮成长的条件。

但那是什么?

高级智能的四个关键属性

为了赋予机器更多类似人类的智能,人们必须首先探索是什么使人类智能与当前(大约 2022 年)许多通常用于人工智能应用的神经网络截然不同。进行这种区分的一种方式是通过以下四个属性:

1。世界模型

人类自然而然地发展出一种“世界模型”,这种模型允许他们设想无数短期和长期的“如果”,并以此来指导他们的决策和行动。人工智能模型可以通过类似的能力变得更加高效,这将允许它们以资源高效的方式从头到尾模拟潜在的场景。智能机制需要用多个相互作用的个体代理来模拟复杂的环境。一个输入到输出的映射函数(例如可以用一个复杂的前馈网络来实现),需要“展开”所有潜在的路径和交互。在现实生活环境中,这种从输入到输出的展开模型的复杂性会迅速增加,特别是在考虑跟踪每个代理的相关历史的任意持续时间时。相比之下,智能机制可以在模拟环境中独立地对每个因素和代理进行建模,可以评估许多假设的未来场景,并通过复制参与者的副本来发展模型,每个参与者都有其可知的相关历史和行为。

获得具有这种模拟能力的世界模型的关键是世界模型的构建模块(认知推理过程)和它们随后在可能结果的模拟中的使用之间的解耦。即使模拟方法随着时间的推移而改变,由此产生的“假设”场景也可以保持一致的方式进行比较。这种方法的一个特殊案例可以在数字双胞胎中找到,其中机器配备了(通过自学或显式设计)其环境的模型,可以模拟各种交互的潜在未来。

图一。数字孪生技术在互动环境中创造了复杂角色的多面模型。图片来源:cheskyviaAdobe Stock

智能生物和机器使用世界模型(“世界观”)来理解观察结果,评估潜在的未来,以选择最佳的行动方案。在从“一般的”大规模设置(如回复网络查询)过渡到包括多个参与者的特定设置中的直接交互的过程中,世界模型必须被有效地缩放和定制。与试图在单个“输入-输出功能”步骤中模拟推理相比,解耦、模块化、可定制的方法是一种逻辑上不同且远不复杂的架构。

2.心理理论

【心理理论】指的是一种复杂的心理技能,它被认知科学定义为一种能力,即通过跟踪另一个人的注意力将一种心理状态归因于另一个人的行为和信念。

用最简单的话来说,这就是我们在试图读取另一个人的想法时所做的事情。我们在一生中发展和使用这种技能来帮助我们驾驭我们的社会交往。这就是为什么我们没有提醒我们的节食同事在休息室里有一大盘新鲜的巧克力饼干。

我们看到在人工智能应用中使用思维理论的痕迹,如聊天机器人,它们根据他们打开聊天的原因、他们使用的语言等,独特地适应他们服务的客户的情绪。然而,用于训练这种社交聊天机器人的性能指标——通常定义为每次会话的对话次数,或 CPS——只是训练模型以最大限度地吸引人类的注意力,而不是强迫系统开发一个通过推理和规划任务衡量的人类思维的显式模型。

图二。具有心理理论能力的人工智能系统可以根据最终用户的需求和偏好修改其输出。图片来源:英特尔实验室。

在一个需要与一组特定的个体互动的系统中,心理理论需要一种更结构化的表示,这种表示服从于逻辑推理操作,例如那些在演绎和归纳推理、计划、意图推断等中使用的逻辑推理操作。此外,这种模型必须跟踪各种行为特征,可预测地随着新信息的流入而更新,并避免回到以前的模型状态。

3.持续学习

除了一些例外,今天的标准机器学习范式是批量离线学习,可能随后会针对特定任务进行微调。因此,生成的模型无法从部署时暴露的信息中提取有用的长期更新。人类没有这样的限制。他们不断学习,并利用这些信息建立认知模型,如世界观和心理理论。事实上,持续的学习使人类能够保持和更新他们的思维模式。

持续学习(也被称为终身和持续学习)的问题现在在人工智能研究界引起了更强烈的兴趣,部分原因是由于像联邦学习和像医疗数据处理这样的工作流这样的技术的出现带来的实际需求。一个人工智能系统,使用一个世界模型的环境中,与该环境中的各种代理相关的思维理论,一个持续的学习能力将是至关重要的,以维护和更新每个对象和代理的历史和当前状态描述符。

尽管该行业的需求非常明确,但仍有许多工作要做。具体来说,能够持续学习信息,然后用于推理或规划的解决方案仍处于初级阶段——这种解决方案需要能够实现上述模型构建功能。

4.后期绑定上下文

后期绑定上下文是指上下文特定的(而不是一般的)响应的组合,并利用查询或决策时可用的最新相关信息。背景意识体现了人类学习的所有细微差别——它是“谁”、“为什么”、“何时”和“什么”通知人类的决定和行为。它防止人类诉诸推理捷径,跳到不精确的、概括的结论。相反,情境意识允许我们建立一套适应的行为,以适应需要解决的特定环境状态。如果没有这种能力,我们的决策将会大打折扣。后期绑定上下文也与持续学习紧密交织在一起。有关后期绑定上下文的更多信息,请参见之前的博客,推进机器智能:为什么上下文决定一切

人类认知作为人工智能未来的路线图

如果没有上面列出的关键认知能力,人类工业和社会的许多关键需求将无法得到满足。因此,相当迫切地需要分配更多的研究,以将人类的认知能力转化为人工智能功能——特别是那些使其不同于当前人工智能模型的特性。上面列出的四个属性是一个起点。它们位于人类认知功能的复杂网络的中心,并提供了一条通向计算高效的自适应模型的道路,这些模型可以部署在现实生活的多角色环境中。随着人工智能从集中化、同质化的大型模型扩散到集成在复杂社会环境中的多种用途,下一组属性将需要出现。

参考

  1. 米契尔(2021)。为什么 AI 比我们想象的要难。arXiv 预印本 arXiv:2104.12871。【https://arxiv.org/abs/2104.12871
  2. 马库斯·g(2022 年 7 月 19 日)。深度学习正在碰壁。鹦鹉螺|科学连线。https://nautil.us/deep-learning-is-hitting-a-wall-14467/
  3. 歌手 g(2022 年 1 月 7 日)。认知人工智能的兴起——走向数据科学。中等。https://towards data science . com/the-rise-of-cognitive-ai-a 29 D2 b 724 CCC
  4. Ziegler,a .,Kalliamvakou,e .,Li,X. A .,Rice,a .,Rifkin,d .,Simister,s .,和 Aftandilian,E. (2022 年 6 月)。神经代码完成的生产率评估。《第六届 ACM SIGPLAN 机器编程国际研讨会论文集》(第 21-29 页)。https://dl.acm.org/doi/pdf/10.1145/3520312.3534864
  5. 马龙,T. W .,俄罗斯,d .,,劳巴赫尔,R. (2020)。人工智能和工作的未来。麻省理工学院未来工作特别小组准备的报告,《研究简报》, 17,1–39。https://work of future . MIT . edu/research-post/artificial-intelligence-and-the-future-of-work/
  6. Thoppilan,r .,De Freitas,d .,Hall,j .,Shazeer,n .,Kulshreshtha,a .,Cheng,H. T .,… & Le,Q. (2022)。Lamda:对话应用程序的语言模型。arXiv 预印本 arXiv:2201.08239。【https://arxiv.org/pdf/2201.08239.pdf
  7. t .布朗、b .曼恩、n .赖德、Subbiah、m .卡普兰、J. D .、Dhariwal、p .…& amo dei,D. (2020 年)。语言模型是一次性学习者。神经信息处理系统进展,33,1877-1901。https://arxiv.org/abs/2005.14165
  8. 柯蒂斯,b .,&萨武列斯库,J. (2022 年 6 月 15 日)。谷歌的 LaMDA 有意识吗?一个哲学家的观点。对话。https://the conversation . com/is-Googles-lamda-conscious-a-哲人-view-184987
  9. 迪克森,B. (2022 年 7 月 24 日)。大型语言模型不会规划,即使写的是花里胡哨的论文。技术对话。https://bdtechtalks . com/2022/07/25/large-language-models-cant-plan/
  10. Mehrabi、f . mor statter、n . sa xena、Lerman、k .和 a . Galstyan(2021 年)。机器学习中的偏见和公平问题综述。美国计算机学会计算调查(CSUR),54(6),1–35。https://dl.acm.org/doi/abs/10.1145/3457607
  11. 纽约州勒村(2022 年)。通向自主机器智能 0.9 版的道路。2, 2022–06–27.https://openreview.net/pdf?id=BZ5a1r-kVsf
  12. El Saddik,A. (2018)。数字双胞胎:多媒体技术的融合。IEEE 多媒体,25(2),87–92。https://ieeexplore.ieee.org/abstract/document/8424832
  13. Frith,c .,& Frith,U. (2005)。心理理论。当前生物学,15(17),R644-R645。https://www . cell . com/current-biology/pdf/S0960-9822(05)00960-7 . pdf
  14. Apperly,I. A .,& Butterfill,S. A. (2009)。人类是否有两个系统来追踪信仰和类似信仰的状态?。心理评论,116(4),953。https://psycnet.apa.org/doiLanding?doi=10.1037%2Fa0016923
  15. Baron-Cohen,S. (1991)。心灵理论的前兆:理解他人的注意力。自然心理理论:日常心理阅读的演变、发展和模拟,1,233-251。
  16. 维基百科的贡献者。(2022 年 8 月 14 日)。心理理论。维基百科。https://en.wikipedia.org/wiki/Theory_of_mind
  17. Shum,H. Y .,He,X. D .,&李,D. (2018)。从伊莉莎到小冰:社交聊天机器人的挑战和机遇。信息技术与电子工程前沿,19(1),10-26。https://link.springer.com/article/10.1631/FITEE.1700826
  18. 周,l .,高,j .,李,d .,& Shum,H. Y. (2020)。移情社交聊天机器人 xiaoice 的设计与实现。计算语言学,46(1),53-93。https://direct . MIT . edu/coli/article/46/1/53/93380/The-Design-and-implement-of-XiaoIce-an
  19. Reina,G. A .,Gruzdev,a .,Foley,p .,佩列皮奥尔金娜,o .,Sharma,m .,Davidyuk,I .,… & Bakas,S. (2021)。OpenFL:一个用于联合学习的开源框架。arXiv 预印件 arXiv:2105.06413https://arxiv.org/abs/2105.06413
  20. Vokinger,K. N .,Feuerriegel,s .,& Kesselheim,A. S. (2021)。医疗器械持续学习:FDA 行动计划及展望。《柳叶刀数字健康》,3(6),e337-e338。https://www . thelancet . com/journals/land ig/article/piis 2589-7500(21)00076-5/全文
  21. 歌手 g(2022 b,5 月 14 日)。推进机器智能:为什么语境决定一切。中等。https://towards data science . com/advancing-machine-intelligence-why-context-is-everything-4 bde 90 FB 2d 79

利用线性回归进行分类变量和连续变量的特征选择

原文:https://towardsdatascience.com/beyond-linear-regression-467a7fc3bafb

如何使用群组套索选择前 K 个最相关的特征

[感谢https://www.craiyon.com/生成]

线性回归是初级/入门级机器学习(ML)模型之一。这归功于它的…

  • 简单性:它将给定的响应 y 建模为一些变量 x_1,…,x_p 的线性组合
  • 可解释性:与变量 x_j 相关的系数暗示了它与响应 y 的关系
  • 可训练性:在训练过程中不需要大量的超参数调整。

甚至可以说它是数据科学家的《Hello world》节目的同义词。尽管是基本的,线性回归仍然有一些其他非常有趣的特性要展现…

特征选择的惩罚线性回归

寻找线性回归系数 β_1,…,β_p 包括寻找接近响应的变量的“最佳”线性组合。换句话说,找到使均方误差(MSE) 最小的系数。

通过考虑 MSE 加上额外的惩罚项,可以赋予回归系数一些额外的性质。

要最小化的惩罚目标函数。[作者插图]

这类似于隐式地对模型说:在考虑强度为 α 的惩罚项的同时,尽可能多地保留数据保真度。碰巧的是,通过对惩罚项的特定选择,我们可以为线性回归提供特征选择属性。

套索特征选择工作良好,直到你考虑分类变量

当我们将罚项设置为 L1 范数 —回归系数的绝对值之和时,我们得到了一个具有很少非零系数的解。这在文献中被称为套索模型,其产生的解决方案享有特征选择属性,因为(少数)非零系数指示与模型最相关的特征,并因此指示要选择的特征。

Top) 套索的目标函数被最小化。(Bottom)拟合套索模型后得到的解决方案。[作者插图]

经常会遇到分类变量,例如,一个颜色变量的值是“红”、“白”和“蓝”。这些必须事先以数字形式编码,以便在回归模型中使用。为了做到这一点,我们经常求助于使用一键编码——将一个变量分解成它的虚拟值——来避免隐式地强制执行值之间的顺序。

对以“红色”、“白色”和“蓝色”为值的可变颜色应用一键编码后的结果。[作者插图]

在这种特定情况下应用 Lasso 作为特征选择技术是不合适的,因为它独立地处理变量,因此忽略了它们之间的任何关系,在我们的情况下,它忽略了虚拟变量作为一个整体代表相同的分类变量的事实。

例如,如果 Lasso 在 color_whitecolor_blue 中选择了 color_red ,那么它并没有说明“颜色”变量的相关性。

用于连续/分类特征选择的组合套索

理想情况下,我们需要一个将分类变量的虚拟变量作为一个整体来考虑的惩罚,换句话说,一个在处理分类变量时包含虚拟变量之间潜在的“组结构”的惩罚。

这可以通过稍微改变 Lasso 的惩罚来处理变量组来实现。为此,我们重新排列了向量中属于同一组的回归系数。然后,我们考虑它们的和 L2 范数

Top) 组套索目标函数被最小化。下图)拟合一组套索模型后得到的解。[作者插图]

由此产生的模型在文献中被称为组套索。值得一提的是,通过考虑上述惩罚,我们获得了一个模型:

  • 具有分组特征选择属性。一个组的变量要么全部被选择(分配非零系数),要么被排除(分配零系数)。
  • 平等对待一个群体的变量。我们使用的是 L2 范数,众所周知它是各向同性的,因此不会优先考虑一个组中的任何变量——在我们的例子中,是分类变量的虚拟变量。
  • 处理单个变量和组变量的混合。一维空间中的 L2 范数是绝对值。因此,如果组由一个变量组成,组套索就简化为套索。

一维空间中的 L2 范数是绝对值。[作者插图]

基于此,我们可以使用组套索来执行连续/分类变量混合的特征选择。连续变量将被视为单变量组,分类变量将被视为虚拟变量组。

使用 celer 拟合群组套索模型

scikit-learn 是当今 ML 流行背后的著名 python 包之一。其用户友好的 API 和全面的文档降低了进入 ML 领域的门槛,并使非从业者能够毫无困难地从中受益。

[作者的迷因]

不幸的是,scikit-learn 实现中缺少组 Lasso。然而,这一点也不奇怪,因为无数的是今天的 ML 模型,更不用说当你考虑它们的变种!

很可能,有几个倡议旨在通过标准化 ML 模型的实现并使它们与 scikit-learn API 保持一致来保持 ML 对普通公众的可访问性。

celer:一个 scikit-learn API conform 包,用于套索类模型

celer是一个 python 包,它包含了 scikit-learn 愿景,并提供了完全在 scikit-learn API 下设计的模型,从而与它很好地集成在一起——可以与管道和 GridSearchCV 等一起使用。因此,拟合 celer 的模型就像为 scikit-learn 做同样的事情一样简单。

此外, celer 专门设计用于处理套索类模型,如套索和组合套索。因此,它具有定制的实现,使其能够快速适应这些类型的模型,比 scikit-learn 快 100 倍,并有效地处理大型数据集。

celer Group Lasso 入门

在通过 pip 安装了 celer 之后,您可以轻松地安装一个群组套索模型,如下面的代码片段所示。这里,考虑一个具有一个连续变量和两个分类变量的玩具数据集。

celer 组套索的启动器示例

我们已经成功地拟合了一组套索,今后剩下的工作就是检查结果解(非零系数)来决定要选择的变量。

理想情况下,在特征选择方面,我们对回答这个问题很感兴趣:最相关的特征是什么?因此,我们不能声称我们 100%回答了这个问题,因为非零系数——要选择的变量——取决于惩罚的强度 α ,因此我们不能完全控制它们。

通过惩罚强度控制所选特征的数量

我们从 0 开始增加 α 越多——纯线性回归——过滤掉的变量越多——分配零系数。最终,对于“足够大” α ,我们得到一个零解——所有系数都为零。

这意味着一个变量被高α驱逐后“幸存”得越多,它就越显示出它对模型的重要性。因此,我们可以依靠惩罚强度对变量进行评分。

有趣的是,给定一个响应和一组变量,我们可以证明存在一个 α_max ,在这个值之上,零解是唯一一个折衷数据保真度和损失的解。

在一个网格(0, α_max)上对玩具数据集上的进行套索分组。注意,在α_max 以上,所有系数都为零。【作者插图】

这意味着每个变量都有一个介于 0 和 α_max 之间的有限分数。此外,由于我们有一个明确的公式 α_max,我们可以通过 α_max 进一步归一化分数,以获得介于 0 和 1 之间的分数。

总结一下方法,我们先在 0 和 α_max 之间生成一个 α 的网格。然后,我们在每个 α 上拟合群组套索,并跟踪变量何时被分配零系数——这将是它们相应的分数最后,我们将这些分数标准化,然后根据这些分数对变量进行排名。

除了最后一步,以上所有步骤都由 celer_path 精心处理——celer 模型的构建模块。

回到原来的问题:最热门的 K 的最相关的特征是什么?剩下的就是根据分数对特征进行降序排序,选择前 K 个。****

使用所考虑的方法得到的玩具数据集的特征分数。[作者插图]

总结和结论

线性回归无疑仍然是最简单和易于理解的模型之一,它与精心选择的惩罚相结合,产生了更多可解释的结果。

在本文中,我们扩展了线性回归,使用套索组模型对连续/分类变量的混合物进行特征选择。

最后,我们使用 celer 来拟合组 Lasso,并依靠其核心解算器— celer_path — 来控制所选特征的数量。

我制作了一个详细的 GitHub 存储库,其中我将介绍的特征选择技术应用于一个真实的数据集。您可以在链接中查看源代码,并利用开发的 python 实用程序将其应用到您自己的用例中。

有用链接:

超越目标识别:图像数据中模式发现的巨大飞跃

原文:https://towardsdatascience.com/beyond-object-identification-a-giant-leap-into-pattern-discovery-in-imagery-data-ca6fbb46ff4a

一个关于发现影像数据中对象之间相关性的简短而有趣的教程

在识别图像数据库中的对象(或类别标签)之后出现的一个关键问题是:“在图像数据库中发现的各种对象是如何相互关联的?“本文试图通过提供一个通用框架来回答这个问题,该框架可以帮助读者发现图像数据库中对象之间隐藏的相关性。(本文的目的是鼓励即将到来的研究人员在顶级会议和期刊上发表高质量的研究论文。本文部分摘自我们发表在 IEEE BIGDATA 2021 [1]上的工作。)

发现影像数据库中对象之间相关性的框架如图 1 所示。它包括以下三个步骤:

  1. 提取存储库中每个图像的对象(或类别标签)及其概率分数。用户可以使用对象检测/实例分割/语义分割技术来提取对象。
  2. 将对象及其概率分数转换到您选择的数据库中。(如有必要,删除具有低概率分数的不感兴趣的对象以减少噪声。)
  3. 根据生成的数据库和所需的知识,应用相应的模式挖掘技术来发现影像数据中对象之间令人兴奋的相关性。

图 1:发现影像数据中有趣模式的框架

演示:在本演示中,我们首先将图像数据传递到一个经过训练的模型(例如 resnet50)中,并提取对象及其分数。接下来,提取的数据被转换成事务数据库。最后,我们在生成的事务数据库上执行(最大)频繁模式挖掘,以发现图像数据中频繁出现的对象集。图 2 显示了我们的演示的概况。

图 2:在影像数据中发现模式的概述

先决条件:

  1. 我们假设读者熟悉实例/语义分割和模式挖掘主题。我们推荐 Phillipe 关于模式挖掘的视频讲座 s。
  2. 安装以下 python 包: pip 安装 pami torchvision
  3. 从[2]下载图像数据库

(请根据您的计算环境安装任何所需的附加软件包。)

步骤 1:从图像数据中提取对象及其分数

步骤 1.1:加载预训练的对象检测模型

将以下代码保存为 objectDetection.py。该代码接受 imagery 文件夹作为输入,实现预训练的 resnet50 模型,并输出包含类标签及其分数的列表(即 self.predicted_classes)。该列表中的每个元素表示在图像中找到的类别标签。

import glob
import os
import csv
import torchvision
from torchvision import transforms
import torch
from torch import no_grad
import cv2
from PIL import Image
import numpy as np
import sys
import matplotlib.pyplot as plt
from IPython.display import Image as Imagedisplay
from PAMI.extras.imageProcessing import imagery2Databases as obclass objectDetection:
    def __init__(self):
        self.model_ = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)
        self.model_.eval()
        for name, param in self.model_.named_parameters():
            param.requires_grad = Falsedef model(self, x):
        with torch.no_grad():
            self.y_hat = self.model_(x)
        return self.y_hatdef model_train(self, image_path):
        # label names 
        self.coco_instance_category_names = [
            '__background__', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus',
            'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'N/A', 'stop sign',
            'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
            'elephant', 'bear', 'zebra', 'giraffe', 'N/A', 'backpack', 'umbrella', 'N/A', 'N/A',
            'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball',
            'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket',
            'bottle', 'N/A', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl',
            'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza',
            'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'N/A', 'dining table',
            'N/A', 'N/A', 'toilet', 'N/A', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
            'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'N/A', 'book',
            'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush'
        ]
        self.transform = transforms.Compose([transforms.ToTensor()])
        self.image_path = image_path
        self.image = Image.open(self.image_path)
        # resize and plotting the image
        self.image.resize([int(0.5 * s) for s in self.image.size])
        del self.image_path
        self.image = self.transform(self.image)# predictions without any threshold
        self.predict = self.model([self.image])
        self.predicted_classes = [(self.coco_instance_category_names[i], p) for
                                  i, p in
                                  zip(list(self.predict[0]['labels'].numpy()),
                                      self.predict[0]['scores'].detach().numpy())]return self.predicted_classes

步骤 1.2:从每幅图像中检测物体

以下代码识别每个图像中的各种对象,并将它们附加到一个名为 detected_objects_list 的列表中。在下一步中,这个列表将被转换成一个事务数据库。

from PAMI.extras.imageProcessing import imagery2Databases as ob
# input images path folder 
images_path = 'aizu_dataset'# list to store output items
detected_objects_list = []# opening the images folder and reading each image
for filename in glob.glob(os.path.join(images_path,'*.JPG')):
    with open(os.path.join(os.getcwd(),filename),'r') as f:

        # loading pretrained resnet-50 model to train on our dataset
        model_predict = objectDetection()

        # input each image to the pre-trained model
        # model returns detected objects
        objects_detected = model_predict.model_train(filename)
        detected_objects_list.append(objects_detected)

第二步:创建交易数据库

使用下面的代码删除不感兴趣的类标签。将剩余数据保存为事务数据库。

#Prune uninteresting objects whose probability score is less than a particular value, say 0.2
obj2db = ob.createDatabase(detected_objects_list,0.2)#save the objects identified in the images as a transactional database
obj2db.saveAsTransactionalDB('aizu_dataset0.2.txt',',')

通过键入以下命令查看生成的事务数据库文件:

!head -10 aizu_dataset0.2.txt

输出如下所示:

motorcycle,backpack,person
book,baseball bat,refrigerator,cup,toaster
bottle,bowl,tv,toilet,chair,mouse,refrigerator,cell phone,microwave,remote,sink
microwave,refrigerator,bowl,bottle,cell phone,oven,car,person
bench
potted plant
bottle,handbag,suitcase,book
book,laptop,tv,umbrella
oven
parking meter,car

第三步:在事务数据库中提取模式。

在生成的事务数据库上应用最大频繁模式增长算法来发现隐藏模式。在下面的代码中,我们找到了在影像数据库中至少出现了十次的模式(即类别标签集)。

from PAMI.frequentPattern.maximal import MaxFPGrowth as algobj = alg.MaxFPGrowth('aizu_dataset0.2.txt',10, ',')
obj.startMine()
print(obj.getPatterns())
obj.savePatterns('aizuDatasetPatterns.txt')
print('Runtime: ' + str(obj.getRuntime()))
print('Memory: ' + str(obj.getMemoryRSS()))

通过键入以下命令查看生成的模式:
!head-10 aizudatasetpatterns . txt

输出如下所示:

refrigerator	microwave	:11
toilet	:10 
cell phone	:11 
traffic light	:12 
truck	:12 
potted plant	:12 
clock	:15 
bench	:17 
oven	:17 
car	:18

第一个图案/线条表示图像库中的 11 幅图像包含分类标签冰箱微波炉。对于剩余的图案/线条可以做出类似的陈述。

了解不同对象/类别标签之间的相关性有利于用户做出决策。

结论:

在工业和学术界中已经广泛研究了图像数据中对象的有效识别。识别对象后的一个关键问题是,图像数据中各种对象之间的潜在相关性是什么?本博客试图通过提供一种通用方法来回答这个关键问题,该方法将图像数据中发现的对象转换成事务数据库,应用模式挖掘技术,并发现令人兴奋的模式。

免责声明:

  1. 本页显示的所有图片均由作者绘制。
  2. 该图像数据库由作者本人创建,是开源的,可用于商业和非商业目的。

参考文献:

[1] Tuan-Vinh LaMinh-Son Dao友川 Tejima 、Rage Uday Kiran、 Koji Zettsu : 通过分析生活日志图像和物联网空气污染数据,提高对可持续智慧城市的认识。IEEE BigData 2021:3589–3594

[2]影像数据集: aizu_dataset.zip

语言模型会行动,而不只是说话

原文:https://towardsdatascience.com/beyond-text-generation-language-models-that-act-not-just-talk-127236c0976d

谷歌的 Minerva 如何承诺机器可以行动的未来

像 GPT-3 这样的大型语言模型大多被用来完成同样的任务:文本生成。然而,语言只是达到目的的一种手段。在未来几年,我们将会看到一个转变,模特们会“行动”,而不仅仅是“说话”。

中途生成的图像(生成型 AI)。参见相关文章:dalle 能接管 Medium 吗?

像 GPT-3 这样的大型语言模型(LLM)主要用于文本生成,这是它们最明显的应用——毕竟,这是它们被训练去做的事情:给定一段文本,预测接下来会发生什么。过去两年,在广告、内容营销( copy.aifrase.io )、小说写作和游戏( latitude.io )等创意行业部署 LLM 的初创公司激增。在完全进入真正的赚钱世界之前,这些行业是生殖人工智能萌发一段时间的有利环境。首先,因为他们的面包和黄油是自由形式的文本,这正是 GPT-3 开箱即用的产品;开发人员可以简单地从 OpenAI 中调用推理 API,而几乎不需要了解模型的内部工作原理。第二,这些行业的创造性允许他们对幻觉视而不见,这是当前模型的一个众所周知的限制,允许他们偶尔产生事实上不正确但听起来似乎可信的文本。

然而,LLM 被训练生成文本的事实并不意味着这就是它们的全部用途。对人类来说,自然语言是达到目的的手段,而不是最终的归宿(或许诗歌是个例外)。构建能够理解并生成文本的 ai,就相当于与机器建立了一个沟通的通道,让我们可以轻松地用自己的语言发出命令。我们已经开发这个渠道很长时间了,使用越来越抽象的构建模块:从穿孔卡片到汇编等低级语言,再到 Python 等高级语言,最后是自然语言。现在这个频道几乎完成了,我们开始将注意力转向教 AI 如何行动

除了文本生成,第一步:推理

之间的中间步骤是推理。在过去的几年里,关于逻辑推理硕士是否能推理的争论一直很激烈。杰出的研究人员声称,这种模型只不过是随机鹦鹉学舌,学习语言符号的概率分布,从而鹦鹉学舌训练数据的一些变化,没有任何真正的推理能力。相比之下,另一个学派声称 LLM 能够进行一些推理,因为它们遵守像因果关系这样的常识性规则。例如,当提示短语“因为球员用力击球”时,GPT-3 生成“球飞得很远”——这是一种延续,符合我们对物理世界因果关系的预期。

随着谷歌新模型Minerva(2022 年 6 月 30 日)的到来,随机鹦鹉的说法失势了。Minerva 令人信服地展示了一步一步的定量推理:当提出一个 STEM 问题(与科学、技术、工程或数学相关)时,模型可以产生一个答案,并解释它是如何得出的:

来自 Minerva 样本浏览器的代数问题和模型答案。

虽然 STEM 问题确实需要自然语言理解,但它们还涉及符号和数字操作。数字是一种特别复杂的记号。首先,它们实际上是无限的——你可能会在训练集中遇到大多数品种的狗,但肯定不是大多数。第二,它们的共现模式比普通词少;例如,同时包含“狗”和“猫”的文档比同时包含“520”和“17”或任意一对数字的文档要多得多。这就是为什么“随机鹦鹉”的说法在判断 GPT-3 生成的语句(如“狗追猫”)时听起来可信(即,该模型只是鹦鹉学舌两种动物之间习得的同现),但当 Minerva 声明“我们有 520/30 = 17r10”时就不那么令人信服了。

另一个值得注意的方面是,Minerva 在提出证据或证明数字响应时会执行多步推理。除了最终答案,它还提供了导出最终答案的有序步骤序列。这是定量推理的有力证明(与记住答案或选择高可能性标记作为答案相反)。毕竟,我们在评价学生时使用的是同样的原则:如果他们能解释一个结果,那么他们很可能没有作弊。

模型答案中的多步推理(来自 Minerva 样本探索者)。

同样值得注意的是,Minerva 不使用任何外部工具,如计算器或 Python 解释器。整个定量推理都编码在训练好的权重中。相比之下,以前的工作[2]使用 LLM 简单地将自然话语转换成可以在传统机器上执行的正式语言;计算器的结果最终被合并到模型的自然语言输出中。

虽然 Minerva 确实有其局限性(它的一些答案是错误的,它的一些推导是假阴性——即它从错误的假设中得出正确的结论),但它在文本生成的基础上迈出了一大步。将定量推理嵌入 LLM 打开了许多现实世界应用的大门,包括教育。只要达到一定的质量标准,学生就可以找到自己的私人人工智能导师来指导他们解决 STEM 问题(…或者帮助他们在作业中作弊🤔).或者,我们可以利用这项技术来构建自动化评估框架,节省人类教育者的时间。

超越文本生成,第二步:表演

一旦我们能够制造出能够让推理(并因此理解它们被要求做什么)的机器,下一步就是让它们能够行动。这不一定是一个全新的任务——毕竟,助手已经帮我们开关灯有一段时间了。然而,改变的是它们的实现:多个 NLP 组件的传统管道开始被越来越强大的 LLM 所取代。这种转变将开启更多的用例,并带来更流畅的人机交互。

来自 MindMeld 的传统流水线架构,这是一个 2011 年建立的对话式 AI 平台,2017 年被思科收购。

如上所述,传统的对话式人工智能平台,如 MindMeld 将多个独立构建的 NLP 组件链接在一起:领域分类器,接着是意图分类器,然后是其他组件,一直到最终的语言解析器(我假设它将用户输入映射到机器可以执行的正式语言)。然而,根据最近的研究,越来越有可能的是,这些组件将由 LLM 隐式学习并编码在其权重中,而不是由工程师显式实现。毕竟,谷歌的 Minerva 已经包含了某种计算器。

事实上,研究人员已经在语义解析(将自然语言映射到正式语言)的背景下研究 LLM 很长时间了。许多论文使用 SQL(标准查询语言)——这有助于与数据库的交互——作为目标正式语言。虽然 LLM 在学习将自然语言转换成针对训练中遇到的特定数据库模式的查询方面表现很好,但推广到看不见的模式仍然是一个挑战[3]。换句话说,一个被训练为与美国航空公司数据库交互的模型可能在 Delta 数据库上表现不佳。类似地,如果灯和扬声器的 API 不同,一个被训练来开关灯的模型可能不知道如何打开和关闭音乐。这是将该技术扩展到许多不同用例的瓶颈,因为每个用例都需要自己的训练数据。

有人可能会合理地问:我们怎么能期望 LLM 理解他们以前没有见过的正式语言(例如,说话者的 API)?这个问题并非不可能解决,因为我们之前已经惊喜地发现多语言模型令人印象深刻的零触发功能。事实上,最近有几家初创公司着手应对这一挑战。2022 年 4 月,一群前谷歌员工(包括瓦斯瓦尼变形金刚的第一作者)宣布了他们的新创业公司 AdeptAI ,旨在让人工智能对人类发出的自然语言命令采取行动:

真正的通用智能要求模型不仅能读和写,还能以对用户有帮助的方式行动。这就是为什么我们开始 Adept:我们正在训练一个神经网络来使用世界上的每一个软件工具和 API,建立在人们已经创造的大量现有能力的基础上。(摘自 Adept 的介绍博客)

同样,2022 年 5 月, InflectionAI 筹集了 2 . 25 亿美元,以实现其让人类能够用自然语言与机器互动的使命:

人工智能的最新进展有望从根本上重新定义人机交互。我们很快就有能力用我们与人交流时使用的自然对话语言将我们的思想和想法传递给计算机。随着时间的推移,这些新的语言能力将彻底改变数字体验的含义。(屈折变化)

结论

像 GPT-3 这样的大型语言模型生成的文本吸引了我们的注意力,因为它们具有模仿人类散文的怪异能力。虽然这可能会让我们认为生殖技术已经达到了一个上限,但语言只是达到目的的一种手段。下一个挑战是让正在说话的移动并教会机器如何行动。谷歌的 Minerva 已经隐含地学会了如何执行符号操作和数字计算,并且有越来越多的努力来教 LLM 如何向底层执行环境发出命令。

本文标题提示时,中途(创成式 AI)生成的图像。见相关文章:DALL E 能否接手 Medium?

参考

[1] Lewkowycz 等,2022: 用语言模型解决定量推理问题

[2] Andor 等人,2019: 给 BERT 一个计算器:用阅读理解找运算和论点

[3] Suhr 等人,2020: 探索跨数据库语义解析的未探索的泛化挑战

因果 Python-提升您在 Python 中的因果发现技能(2023)

原文:https://towardsdatascience.com/beyond-the-basics-level-up-your-causal-discovery-skills-in-python-now-2023-cabe0b938715

…并释放 Python 中最佳因果发现包的潜力!

图片由佩克斯(【https://www.pexels.com/photo/purple-leaf-459301/】T2)的皮克斯拜拍摄

介绍

T 最近,人们对 Python 中因果关系相关主题的兴趣激增,这带来了大量资源,让人们决定应该关注哪些挑战。

例如,互联网上的许多资源将流行的 NOTEARS 算法(郑等,2018)描述为“最先进的结构学习方法”,然而 NOTEARS 已经多次被证明至少在这方面是有问题的(Kaiser &,,2021;Reisach 等人,2021;Seng 等人,2022 年)。这并不意味着笔记总是无用的,但是不加批判地把它应用到你的问题中可能会给你带来更多的伤害。

在这篇博文中,我们将学习如何在 Python 中执行因果发现,讨论所选方法的主要优势,并强调与因果发现过程相关的常见风险

这个博客是系列的一部分,我在这里分享关于 学习 因果关系和 在 Python 中实现 因果模型的实用技巧。

【链接到笔记本康达环境文件下面】

让我们学习如何发现!

图片由Alexander Ant@Pexels提供

什么是因果发现?

C 因果发现,也称为因果结构学习表示一套广泛的方法,旨在从观察或干预数据中检索有关因果机制的信息。换句话说,因果发现算法试图解码数据生成过程因果结构,使用该过程生成的数据。

这些算法中的一些允许我们以约束的形式容易地结合先验知识(也称为专家知识)。这有助于缩小问题空间并使算法更容易找到好的解决方案。

在大多数情况下,我们使用 有向无环图 ( DAG )来描述数据生成过程。

因果发现方法的四大家族

因果发现算法有四大类:

  • 基于约束的
  • 基于分数的
  • 功能性
  • 其他(包括混合动力、基于梯度等)

请记住,这种类型学在因果文献中是不一致的,类别也不总是相互排斥的。也就是说,每一种都会带来一些独特的味道。

让我们做一些品尝!

基于约束的方法

基于约束的方法(也称为基于独立性的方法)旨在通过利用三元组变量之间的独立性结构,从数据中解码因果结构。听起来很密集?让我们打开它!

假设我们有一个由三个变量组成的系统: ABC 。每个变量由图中的一个节点表示,在这样的图中我们只能有两条有向边。而且我们把这些变量保持有序,这样边就只能连接节点 ABBC 。这给了我们三个可能的图表。我们在图 1 中展示了它们。

图一。三种基本的图形因果结构。真实的你的形象。

上图中的箭头表示变量之间的因果关系(这里我们遵循 珀尔对因果关系的定义 )。在图 1 中呈现的每个图形结构都有一个特定的名称。从上到下依次是:

  • 链条
  • 叉子
  • 对撞机(又称不道德(原文如此!)或 v 型结构

独立结构

事实证明,在某些情况下,我们可以在表示数据生成过程的图形结构和作为该过程结果的变量的统计属性之间进行映射。此外,在某些情况下,从数据到图形的另一个方向的映射也是可能的。

在我们在图 1 中展示的三种结构中,对撞机结构有一个独特的性质。如果你的数据集中的任何三个变量都是从碰撞器结构的因果过程中产生的,我们可以使用成对统计独立性测试从观察数据中检索这些信息。这意味着我们可以根据观察到的数据本身重建图表。太刺激了!

不幸的是,使用叉子和链条的事情并不顺利。这两种图形结构的统计独立性结构是相同的,我们不能明确地将它们映射回图形。尽管如此,如果我们足够幸运,相邻的碰撞器也可以帮助我们恢复和定向分叉和链结构的边缘。

如果你想了解更多关于链条、叉子和碰撞器的属性,可以查看布雷迪·尼尔关于主题的视频(12 )或 这部分 我在 PyData 汉堡的演讲或我的 即将出版的关于因果关系的书 的第六章。

PC 算法

基于约束的算法的一个经典例子是 PC 算法 (Sprites & Glymour,1991)。它的名字来自于它的创造者的名字:彼得·斯普里茨和克拉克·格里穆尔。PC 算法是维尔马&珀尔(1990)早些时候提出的 IC 算法的变体。

图 2 展示了 PC 算法的逐步流程。

图二。PC 算法的逐步可视化(Glymour 等人,2019 年)

为了找到地面真相(图 2 A ) PC 算法从一个全连通无向图开始( B )。接下来,它移除无条件独立变量之间的边( C ),然后移除有条件独立变量之间的边( D )。最后,该算法基于检测到的 碰撞器结构 ( E )找到有向边,并在可能的情况下消除碰撞器相邻边的歧义( F )。

有时,算法可能无法确定所有边的方向。在这种情况下,返回所谓的马尔可夫等价类 ( MEC )。实际上,MEC 意味着你得到一个图,它的一些边没有确定的方向。

PC 算法的一个重要限制是,如果你的数据中有隐藏的混淆,结果可能会被任意误导。PC 算法的推广,称为 FCI(快速因果推理; Sprites 等人,2001 )解决了这个问题(至少在渐近状态下)。

另一个更普遍的限制是,PC 和 IC 等基于约束的算法依赖于条件独立性测试,这在非参数设置中是一项困难的任务。据我所知,这个问题没有通用的非参数无模型解决方案(Azadkia 等人,2021)。

https://aleksander-molak.medium.com/yes-six-causality-books-that-will-get-you-from-zero-to-advanced-2023-f4d08718a2dd

图片由Sebastian Arie Voortman@Pexels

基于分数的方法

基于分数的方法通过迭代地生成候选图,评估每个候选图对数据的解释程度,并选择最好的一个来工作。基于分数的方法的一个众所周知的例子是戴维·马克斯韦尔·奇克林(奇克林,2003 年)提出的贪婪等价搜索 ( GES )。

GES

他的算法是一个两阶段的过程。首先,它生成边,然后修剪图形。

GES 的第一阶段从一个未连接的图开始。该算法然后迭代地添加边,计算每一步的分数。这种情况一直持续到分数不能再增加为止。在第二阶段,该算法开始修剪现有的边,以查看分数是否可以进一步提高。所有这些计算都是以贪婪的方式进行的(因此得名)。

类似于 PC 算法,GES 对隐藏的混淆敏感。它也可能无法确定所有边的方向,从而为您提供一个可能图形的 马尔可夫等价类 (因此再次得名)。

根据我的经验,尽管有其理论基础,但在应用于现实世界的数据时,GES 的表现往往不如其他方法。

图片由Antoni shk raba@Pexels

功能方法

在某种意义上,大多数函数式方法都可以被认为是基于分数的方法,因为它们在某种程度上涉及某种拟合优度计算。另一方面,它们的机制不同于后者。经典的泛函方法,如 LiNGAM ( 线性非高斯无环模型;Shimizu 等人,2006 年)利用数据中的分布不对称性,而不是(贪婪的)边搜索,以便从数据中检索因果关系。

男性生殖器像

L iNGAM (线性非高斯非循环模型)由 Shohei Shimizu 及其同事于 2006 年首次提出。原始方法使用 独立分量分析 ( ICA )来检索关于数据生成过程的信息。其后来的变体 DirectLiNGAM (Shimizu 等人,2011 年)利用了线性模型和基于内核的独立性度量。

LiNGAM 背后的两个主要假设是:

  • 没有隐藏的变乱
  • 所有(或除一个之外的所有)误差项都是非高斯的

也就是说,人们对 LiNGAM 提出了各种扩展,允许将该模型应用于具有隐藏混杂(霍耶等人,2008 年)或周期(拉塞达等人,2008 年)的场景。

LiNGAM 背后的主要思想是相对简单。想象一个简单的线性系统,只有两个变量 XY ,其中 X 导致 Y 。您可以对该数据进行两个方向的线性回归:在上回归YX(X→Y)或者在Y(Y→X)上回归 X 。如果数据中的误差项是高斯型的,那么这些模型不会告诉你任何关于因果方向的信息。两个模型的残差将是完全独立的。

然而,如果你的误差项是非高斯的…

我们可以打破对称!

事实证明,当我们试图对非因果方向建模时,非高斯数据将迫使线性回归返回相关残差

图 2 展示了一个简单实验的结果。

****图二。当回归真实模型 X 的高斯和非高斯数据时的原始数据和残差- > Y .左半部分:在 X 上回归 Y;右半部分:在 y 轴上回归 X 轴。真实的你的图像。

请注意,对于高斯误差项(顶行),当我们回归YonX(左)和XonY(右)时,残差看起来非常相似。对于非高斯数据(底行),残差在因果方向上不相关(Y ~ X);左),但在非因果方向上变得相关(X ~ Y;对)。

其他方法

这些方法是一个庞大的范畴!我选择了一种方法让我们今天讨论。该算法被称为 GOLEM ,由 Ignavier Ng 及其同事在他们的 NeurIPS 2020 论文中介绍(Ng 等人,2020)。 GOLEM 可以归类为基于梯度的方法(这意味着它使用梯度下降进行优化),在某种意义上,它也是一种基于分数的方法,因为我们在途中计算数据似然分数。

傀儡有两个变种:

  • 魔像 EV
  • 傀儡女

Reisach 等人(2021)已经表明 GOLEM EV 在非标准化数据上优于它的 NV 对应物。

不流泪

GOLEM 是 NOTEARS 算法的继承者(郑等,2018)。NOTEARS 是革命性的,因为它是第一个将结构学习框定为纯粹的连续优化问题的算法(在某些情况下,它减少了 DAG 搜索空间爆炸的问题,这种爆炸随着节点数量的增加而超指数地增长,但它并不总是这样做;Reisach 等人,2021 年)。

尽管开始时很有希望,但 NOTEARS 被反复证明不适合稳定的因果发现(凯泽&希波什,2021;Reisach 等人,2021;Seng 等人,2022 年)。虽然 GOLEM 不能解决 NOTEARS 带来的所有问题,但是根据我的经验,它在实践中的某些情况下效果很好。

要了解更多关于傀儡如何工作的信息,请查看 Ng 等人的文章。

** **

准备好把手弄脏了吗?

我城堡的国王

先介绍一下今天博文的主人公——g castle

图三。 gCastle 标志。来源:https://github . com/Huawei-Noah/trustworthyAI/tree/master/g castle

g 城堡 是由华为诺亚方舟实验室开发的开源库。该软件包为我们提供了一个令人惊叹的最新的因果结构学习工具包,包括:

  • ****数据相关工具(包括模拟和预处理)
  • 一组广泛的因果发现算法****
  • 评估指标

当前可用算法的完整列表可在 此处 获得。

据我所知,这是****最大的最完整的最新的因果发现算法列表,你可以在任何开源的因果 Python 包中找到。****

你知道什么是最好的吗?这个名单正在系统地增长

gCastle 的一个很大的优势是,它为我们提供了一个统一的、非常直观的、优雅的 API,用于与各种因果模型进行交互。忘记加载五个不同的因果发现包,其中两个移植到 R,每个都有完全不同的 API,以便比较几个经典算法。 gCastle 让这一切变得简单多了!

但是不要把我的话当成理所当然。你自己看吧。

我们开始吧!

在这一节中,我们将使用 gCastle 实现并比较四种因果发现算法:

  • 电脑
  • GES
  • ICA-LiNGAM
  • 傀儡

先说导入和一些基础设置。

代码块 1。导入和基本设置

我们导入os模块来修改 gCastle 的环境变量,并将库的后端设置为 PyTorch。我们导入OrederedDict来很好地组织我们的实验,导入networkx来可视化图形。

接下来,我们有几个来自castle的对象(这就是 gCastle 如何出现在 Python 的名称空间中):

  • GraphDAG用于绘制邻接矩阵
  • MetricsDAG用于自动化指标计算
  • 用于生成模拟数据的DAGIIDSimulation
  • 型号:PCGESICALiNGAMGOLEM

开始简单

我们将从实现图 2 中的例子开始。我们将根据图 2 中的图 A 生成一些线性高斯数据,并使用 PC 算法从数据中恢复该图的结构。我们的数据集将由 1000 个样本组成。

代码块 2。按照图 2A 所示的结构随机生成 1000 个样本。

让我们实例化并拟合模型,并打印出学习到的图表。我们之前说过, gCastle 为我们提供了一个统一的因果发现模型的训练 API。为了拟合模型,我们使用模型的.learn()方法。

代码块 3。实例化并拟合 PC 算法。模型训练完成后,我们打印出学习过的结构。

注意,所学习的图形被表示为 邻接矩阵

让我们绘制学习过的图形,并将其与原始图形进行比较。

代码块 4。绘制学习过的图形。

我们使用networkx将邻接矩阵投射到一个nx.DiGraph()对象上,并绘制它。途中,我们重新标记了节点,以便于解释。

图 4 呈现学习图形(右)和地面实况(左)。

图 4。来自图 2A 的原始图形(左)和由 PC 算法学习的图形(右)。来源:Glymour 等人,2019(左),yours truly(右)。

两种表示看起来不同,但是它们表示相同的图(如果有疑问,写下它们中每一个的有向边列表;名单是一样的吗?).

这意味着 PC 能够完美地恢复结构!恭喜 PC!🎉

波涛汹涌的水域

在第一个例子中,PC 算法非常有效。这是个好消息!现在是时候看看它在更复杂的情况下表现如何了。

我们将探索 PC 算法的能力,看看它与其他三种算法相比如何。

让我们从生成一个有 10 个节点和 15 条边的随机 DAG 开始。我们将使用一个无标度网络来生成我们的图。然后,我们将使用此 DAG 作为结构模型来生成三个不同的数据集:

  • 线性高斯
  • 线性指数
  • 非线性二次型

并将它们存储在 Python 字典中。参见代码块 5 实现。

代码块 5。生成一个随机 DAG 和三个不同的数据集。

注意,在双 for 循环中,我们为每组条件(线性高斯、线性指数等)创建了一个新的IIDSimulation对象实例。您可以通过检查代码块 5 底部的打印输出来验证我们的数据集是否属于类别castle.datasets.simulator.IIDSimulation

我们现在准备运行我们的比较。我们首先创建一个 Python 字典,用算法的名称作为键,用 gCastle 对象表示算法的值。

接下来,我们遍历数据集,并在每个数据集上训练每个模型。请注意,为了确定算法的迭代次数,我们实例化 GOLEM 的方式与其他模型不同。检查代码块 6 中的执行情况。

代码块 6。在三个数据集上训练所有四个模型,并打印出结果。

在每次迭代中,我们绘制真实 DAG、发现的 DAG,并打印出六个评估指标:

结果

图 5 显示了 SHD 方面的结果。要获得完整的结果,请查看笔记本(下面的链接)。

图 5。每个数据集/模型组合的 SHD。真实的你的形象。

零 SHD 意味着模型能够完美地恢复的真实结构。正如我们所见,GOLEM 平均表现最好,但在非线性二次数据集上表现很差。这个数据集是所有算法中最难的。请注意 LiNGAM 如何在线性指数数据上表现良好,而在其他两个数据集上表现不佳。原因是线性指数数据集是唯一符合模型假设(线性、非高斯、非循环)的数据集。与其他模型相比,GES 的表现严重落后,但在最具挑战性的数据集上却给出了最佳结果。也就是说,我们需要记住,SHD 并没有讲述整个故事。

我鼓励您检查笔记本以获得完整的结果,并从其他角度分析数据(例如,错误发现率或精确度)。根据您的用例,可能 FDR 对您来说比总体正确性更重要。

包装它

恭喜你!你坚持到了最后!👏🏼👏🏼👏🏼

让我们快速回顾一下!

在今天的博文中,我们了解了四类因果发现方法。我们讨论了它们的一些主要优缺点,并使用 awesome gCastle 库在 Python 中实现了它们。

读完这篇博文和附带的代码后,你应该能够将讨论过的技术应用到你自己的数据集和问题中。

最后的想法(不要错过!)

C 因果发现是一个难题,在使用因果发现方法时,总是格外谨慎是有好处的。在进入下一阶段之前,确保检查两次您的结果,并使用任何可用的验证方法(专家知识、 反驳测试 ),并记住在现实世界中,很难从因果发现方法中获得任何 保证,尤其是如果您无法确定所有相关变量是否都出现在您的数据集中。****

如果您有机会在感兴趣的系统上执行最小干预,来自这种干预的数据可以用来以更可靠的方式验证您的因果图。有一些有趣的方法可以让你在这种情况下选择最佳的干预措施,但那是另一篇文章的内容了。

要了解更多关于因果发现和因果推理的知识,请加入我们快速发展的社区,地址:【causal python . io

代码和环境

笔记本和环境文件在这里:

**https://github.com/AlxndrMlk/blogs-code/tree/main/Beyond The Basics! Level Up Your Causal Discovery Skills in Python Now (2023) **

脚注

注意,如果使用例如互信息来测试(不)依赖性,这甚至可以用于高度非线性和/或非单调数据。也就是说,为了使其工作,需要满足某些条件(例如,忠实假设)。

最初的论文建议贝叶斯信息准则(BIC)作为一个分数,但历史上也使用过许多其他分数。

结构汉明距离类似于汉明距离SHD 通过计算将前者转变为后者所需的边插入、删除和翻转(反转)次数来测量真实图和恢复图之间的距离。

参考

Azadkia,m .,Taeb,a .,和 Buhlmann,P. (2021 年)。一种快速的非参数局部因果结构学习方法。

奇克林博士(2003 年)。基于贪婪搜索的最优结构识别。 J .马赫。学习。第 3507-554 号决议。

Glymour,c .,Zhang,k .,& Spirtes,P. (2019)。回顾基于图形模型的因果发现方法。遗传学前沿,10。

Hoyer,P.O .,Shimizu,s .,Kerminen,A.J .,& Palviainen,M. (2008)。用带隐变量的线性非高斯因果模型估计因果效应。 Int。j .大约。原因。,49 ,362–378。

凯撒和希波什(2021)。注释不适合因果图发现。 ArXiv,abs/2104.05441

拉塞达、斯皮尔特斯、拉姆齐和霍耶出版公司(2008 年)。通过独立成分分析发现循环因果模型。人工智能不确定性会议

吴,张,张(2020)。稀疏性和 DAG 约束在学习线性 DAG 中的作用。 ArXiv,abs/2006.10201

Reisach,A.G .,Seiler,c .,& Weichwald,S. (2021 年)。小心模拟匕首!加性噪声模型中的变量可排序性。ArXiv,abs/2102.13647 。

Seng,j .、Zecevic,m .、Dhami,D.S .、k .和 Kersting(2022)。撕开注释:通过方差操作控制图形预测。 ArXiv,abs/2206.07195

Shimizu,s .,Hoyer,p .,Hyvä rinen,a .,和 Kerminen,A. (2006 年)。用于因果发现的线性非高斯无环模型。j .马赫。学习。第 7 号决议,2003 年至 2030 年。

Shimizu,t . in azumi,Sogawa,y .,Hyvä rinen,a .,Kawahara,y .,Washio,t .,Hoyer,P.O .,& Bollen,K.A. (2011 年)。DirectLiNGAM:学习线性非高斯结构方程模型的直接方法。 J .马赫。学习。第 12 号决议,1225–1248。

Spirtes,p .和 Glymour,C. (1991 年)。稀疏因果图的快速恢复算法。社科计算机评论9 (1),62–72 页。

Spirtes,p .,Glymour,c .和 Scheines,R. (2001 年)。因果关系、预测和搜索,第二版。麻省理工出版社。

维尔马和珀尔(1990 年)。因果模型的等价与综合。第六届人工智能不确定性会议论文集,220–227。

郑,x .,阿拉干,b .,拉维库马尔,p .,&邢,E.P. (2018)。无泪 DAGs:结构学习的持续优化。神经信息处理系统

这篇文章中的一些书籍链接是亚马逊会员链接,通过使用这些链接购买,你将支持作者(或他们的家庭)和我的写作(我将从你的每一笔购买中获得一小笔费用)。谢谢大家!

超越云:用 Python 代替 Word Cloud 的 4 种可视化

原文:https://towardsdatascience.com/beyond-the-cloud-4-visualizations-to-use-instead-of-word-cloud-960dd516f215

使用 Python 创建 4 种可视化效果,可以提供比 Word Cloud 更多的信息

SpaceX 在 Unsplash 上拍摄的

单词云是一种可视化工具,可以显示从文本或文档中检索到的单词集合。通常,在词云中使用文本大小和文本颜色来显示词的频率。结果第一眼就能引起人们的注意。

说一下词云特征,下面我们来对比两张图。第一个是包含一篇文章前 100 个词的词云。第二个是对比同样 100 个单词量的条形图。可以注意到条形图里的字很难读。另一方面,也可以看出云这个词善于处理很多词。

词云显示了出现在维基百科气候变化文章上最多的前 100 个词。图片由作者提供。

显示 100 个单词出现频率的条形图。图片由作者提供。

单词云能够处理许多单词,并有助于粗略比较频率。

然而,云这个词有一些缺点。当处理太多单词时,很难判断哪个单词比其他单词出现得更频繁。此外,文档通常由节组成,如段落或章节。Word Cloud 仅显示整个文档中单词的出现频率。它没有在每一部分提供细节。

本文将展示 4 个 Python 代码的可视化,它们可以处理 Word Cloud 的限制。

让我们开始吧…

Treemap 是本文推荐的一个可视化示例,可以用来代替 Word Cloud。图片由作者提供。

获取数据

例如,我将使用维基百科上的“气候变化”文章中的文字。环境问题现在是一个全球现象。我想看看我们能从这篇文章中得到什么信息——从导入库开始。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import urllib
import re
import wikipediaimport nltk
from nltk.corpus import stopwords%matplotlib inline

继续下载并清理文本。

wiki = wikipedia.page('Climatechange')
text = wiki.content# Clean text
text_c = re.sub('[^A-Za-z0-9°]+', ' ', text)
text_c = text_c.replace('\n', '').lower()
text_c

为了与后面四次可视化的结果进行比较,让我们用获得的数据创建一个单词云。我遵循了这篇见解深刻的文章中有用且实用的步骤:Python 中的简单单词云

词云显示了出现在维基百科气候变化文章中最多的前 100 个词。图片由作者提供。

准备数据

为了简化这个过程,我们将定义一个函数来创建一个数据帧。由于我们必须处理多个单词,使用颜色将有助于我们区分它们。我们还将定义另一个函数来获取一个颜色字典供以后使用。

对文本应用函数以获得数据帧

df_words = get_df(text_c)
df_words.head(10)

可视化

有趣的部分来了。除了创建图表之外,还会推荐一些方法来改善结果。我们将要使用的四个可视化工具:

  • 条形图网格
  • 旭日图
  • 树形图
  • 圆形包装

1。将多个条形图转换为条形图网格。

如前所述,由于文本区域较小,简单的条形图显示文本的能力有限。我们可以通过创建多个条形图并组合它们来重新排列它们,以节省空间。

瞧啊。!

显示前 100 个单词的条形图网格。图片由作者提供。

通过比较主题来改进条形图的网格

文档通常由章节组成,如章节或段落。维基百科上的气候变化文章也由术语、观测到的温度上升等许多内容组成。比较这些部分之间的词频将有助于我们看到更多有见地的细节。

首先手动创建一个内容列表,然后使用列表元素分割文本。

接下来,清理文本并应用定义的函数从每个文本中获取 DataFrame。在下面的代码中,我将创建一个数据帧,其中包含每个数据帧中前 10 个最常用的单词。

准备一个颜色字典和每个数据帧的列表。

现在一切都准备好了,让我们得到包含气候变化文章中每一个内容的前 10 个最常用词的条形图网格。

显示每个内容的前 10 个单词的条形图网格。图片由作者提供。

可以看出,纵观整篇文章,‘气候’是出现最多的词。然而,当文章按照内容划分时,“气候”一词并没有出现在减少和回收排放的内容中。原来‘能量’是这个内容中出现次数最多的词。

2.从圆环图到旭日图的层级增加

第二个可视化是旭日图。我们将从一个具有相同基本概念的环形图开始。下面的代码展示了用 Plotly 创建圆环图的简单方法。

显示前 30 个单词的环形图。图片由作者提供。

结果甜甜圈图几乎满了,只有 30 个字。我们可以通过将图的层次从只有一层增加到两层来改进圆环图。第一层是内容,第二层是每个内容的前 10 个单词。继续准备数据。

接下来,创建一个应用于每个级别的颜色字典。

最后,绘制旭日图。使用 Plotly 的一个好处是获得的图表是交互式的。你可以通过点击内容来玩结果。关于创建旭日图的更多信息:链接

显示每个内容的前 10 个单词的旭日图。图形是交互式的;点击内容即可播放。图片由作者提供。

3.使用带有树形图的数字

Treemap 是一个很好的图形,它使用图形来可视化分层数据。到目前为止,我们已经有了绘制树状图的数据。我们可以直接使用下面的代码。从一个简单的包含前 100 个单词的树形图开始。

显示前 100 个单词的树形图。图片由作者提供。

改进树形图:增加层级

让我们进一步创建一个带有层次结构的树形图。第一层是内容,第二层是每个内容的前 10 个单词。下面的结果是交互式的。你可以通过点击内容来玩这个图表。关于创建树形图的更多信息:链接

显示每个内容的前 10 个单词的树形图。图形是交互式的;点击内容即可播放。图片由作者提供。

4.用圆形包装将气泡分组。

最后一个可视化是圈包装。实际上,这是一个没有重叠区域的气泡图。我们将使用 circlify 库来计算气泡的大小和位置。

用文章中出现最多的前 30 个单词画圈包装。

显示前 30 个单词的圆形包装。图片由作者提供。

改进圆形包装:聚类

可以通过聚集每个内容的前 10 个单词来改进循环包装。为此,我们需要改变数据格式。使用 circlify 库进行计算所需的格式:“id”、“datum”和“children”

使用 circlify 库计算每个簇的气泡的大小和位置。

最后,绘制圆形包装。关于创建圆形包装的更多信息:链接

哒哒!!

显示每个内容前 10 个单词的圆形包装。图片由作者提供。

摘要

单词云是另一种可视化技术,它有优点也有缺点。适合引起注意,可以显示很多字。然而,Word Cloud 似乎不是提供有见地信息的好选择。

本文展示了 4 种可以用来代替单词云的可视化方法。除了具有吸引力和能够处理许多单词之外,它们还能传递更多的信息。除了展示代码,本文还推荐了改进它们的方法。

我相信还有其他这里没有提到的图可以用来代替 Word Cloud。本文只是给出了一些实用的思路。如果您有任何问题或建议,请随时留下评论。

感谢阅读

以下是您可能会感兴趣的关于数据可视化的其他文章:

  • 8 用 Python 处理多个时序数据的可视化(链接)
  • 用 Python 实现的 9 种可视化比条形图更引人注目(链接
  • 9 用 Python 可视化显示比例,而不是饼状图(链接)
  • 用 Python ( 链接)最大化聚类散点图

参考

超越数字

原文:https://towardsdatascience.com/beyond-the-numbers-5f20b919ac5b

置信区间如何帮助集中注意力和简化分析

图片由作者使用稳定扩散

我们相信数字。如果一份报告告诉我们一家企业上周收到了 1,000 份订单,我们相信这家企业收到了 1,000 份订单。事务是历史记录的问题,它们的计数是一个简单的算术运算。如果有人问:“多少?”,我们知道答案。

理解 1000 份订单对一家企业意味着什么要困难得多。有人可能会问,1000 份订单是“好”、“很好”,还是仅仅是“可以”我们可以将本周的结果与之前的结果进行比较,给我们一个比较的基础,但我们仍然需要一种方法来将结果(或它们的差异)转化为定性的术语。

同样,有人可能会问,1,000 份订单是否足以让企业实现其长期目标。要回答这个问题,我们需要查看多周的数据,并从每周的涨跌中分离出主导趋势。一个简单的滚动平均值可能会起作用,但我们仍然需要决定将多少周包括在我们的平均值中,这可能会对我们的答案产生重大影响。

尽管我们相信数字,但它们只能告诉我们这么多:我们离“有多少”越远,回答问题就变得越难,我们需要更多的上下文来解释数据。

如上例所示,查看多个数据点会有所帮助。不同时期的差异、滚动平均值和其他计算方法有助于我们了解结果的趋势。这些通常与其他增强功能结合在一起,如颜色编码和上/下箭头,它们指示运动的方向,但不告诉我们任何关于其重要性的信息,这实际上可能导致数据的错误解读

更有效的方法是使用置信区间。虽然用我们确定的数字来看置信区间似乎违反直觉,但这种方法有坚实的统计基础和许多令人惊讶的好处。

考虑一个模拟过程。对于每个报告期,我们将使用五个骰子的平均值作为结果。下表包含了前六个周期的结果及其跨周期(PoP)差异。

趋势图便于查看结果(y-轴)随时间的变化(x-轴)以及 PoP 差异:

图 1

在这两种报告格式中,增加以绿色显示,减少以红色显示。然而,正如所暗示的,颜色的这种常见用法实际上可能会适得其反,使任何变化——向上或向下——看起来都很重要。

想象一下当最初的几个结果到达时解释它们。第一阶段的结果是 4.4。此时,我们不知道 4.4 是高、低还是典型,但它设定了我们的期望。

下一期的成绩是 3.2,下降了 1.2。我们仍然不知道 4.4 和 3.2 是否是好结果,但红色强化了任何下降都是朝着错误方向前进的概念。如果这是一个真实世界的业务流程,我们会倾向于采取纠正措施。现在确定哪里出了问题还为时过早,但我们仍然可能决定延长工作时间,重新分配资源,或者做任何对这个特定过程有意义的事情。

第三阶段的结果是 3.4,有小幅增长,但令人放心。如果我们确实采取了纠正措施,我们可能会认为它是有效的。

然而,第四阶段的结果抹去了前一阶段的收益(甚至更多)。也许我们需要加倍努力或者尝试一些新的东西。

然后,相反的情况发生了:第 5 期抹去了前一期的损失(甚至更多)。到了第 6 期,我们又回到了起点:4.4。到目前为止,所有的得失加起来都不算什么。

因为我们正在谈论掷骰子,这实际上是我们应该期待的。单个结果是随机的,所以它们之间的差异也是随机的。如果我们继续掷骰子,我们可以更清楚地看到这一点。

图 2

50 次滚动后,平均 PoP 差异为 0.8,但所有差异的总和仅为-1.2,平均为-0.02,四舍五入为 0.0。虽然流行差异看起来很大,但它们最终会相互抵消。

让我们考虑一下如何在报道中利用这一事实。

因为我们知道 5 骰子滚动的统计特性,所以我们可以为我们的结果计算 95%的置信区间:3.5±1.5。这意味着所有结果的平均值将是 3.5,我们可以预计 95%的结果在 2.0 到 5.0 之间。(更准确地说,结果 1.5 的 95%将包含 3.5。)在这种情况下,50 个结果中的 48 个(96%)落在这些限制之间。

图 3

周期 8 和 28 是唯一超出这些限制的结果。这使他们成为进一步调查的良好候选人。

记住,这并不意味着会有什么有意义的发现。同样,我们的例子是基于掷骰子,所以所有这些结果都是随机的。(有 50 个结果和 95%的置信区间,我们应该预计有两三个结果落在我们的置信区间之外。)但是,在现实世界的业务流程中,我们不知道是什么导致了第 8 期和第 28 期如此不同。我们需要分析它们来找出答案(当然,知道可能什么也没有)。

然而,真正的好处是,我们可以忽略其他时期的结果,因为这些结果在预期之内。除了表面现象,起伏并不意味着什么:它们是过程中自然变化的副产品。

不必分析这些时期的结果将为我们节省大量的时间。图表让差异变得显而易见。在图 2 中,我们对每一个得失进行了颜色编码,让人们觉得每一个彩色点都是有意义的。(像圣诞树一样亮了!)但是,在图 3 中,我们将颜色的使用限制在两点,这使得图表更容易阅读,并将我们的注意力集中在更可能感兴趣的结果上。

此外,我们将降低对业务得出错误结论的风险。人们非常善于发现模式——即使它们并不存在——所以如果我们上下分析,假设有东西可以发现,我们很有可能发现一些东西。如果这导致对什么可行什么不可行的错误结论,我们可能会对企业造成弊大于利的风险。

现实世界的复杂性

当然,如果这是一个真实世界的业务流程,我们就不会知道它的统计特性,所以我们就不能预先计算我们的置信区间的极限。幸运的是,有一个变通办法:我们可以使用结果本身来推断我们的过程的统计属性。让我们再次使用我们的骰子例子来看看这是如何工作的。

每个时期的结果都告诉我们更多关于这个过程的信息。在第一阶段之后,我们只有一个结果,但它设定了我们对结果的预期。在第二阶段之后,我们有两个结果,所以我们可以更新我们的期望,我们开始看到结果会随着时间的推移而变化。

从统计学上来说,这些是平均值( x_bar )或平均值,这是我们的期望值,以及标准差( σ ),它告诉我们单个结果与期望值的差异有多大。

仅仅经过两个周期,我们就可以计算两者:

从这些数据中,我们可以计算出一个置信区间,它将告诉我们下一个周期的结果是否与我们目前所看到的有显著的不同。这一特定过程是中心极限定理的经典示例,因此可以安全地假设我们的结果将遵循正态分布,这意味着 95%的置信水平大约为 2 σ (即 1.96 σ )。

鉴于我们目前所知的——这并不多——如果第三阶段的结果在 2.14 和 5.46 之间,我们不应该感到惊讶。事实证明,第三阶段的结果是 3.4,因此在预期范围内,这意味着结果是而不是与之前的值有显著差异。

现在我们有了一个新的结果,我们可以通过重新计算我们的累积平均值、标准差和置信区间来更新我们对该过程的理解,我们可以用它来评估下一个新的结果。

下表显示了前六个周期的置信区间。请注意,我们总是将最近的结果与前期的置信区间进行比较,如下所示。我们不能在置信区间的计算中包括新的结果,否则我们将使用结果来验证它本身。

随着每一个新的时期,我们进一步完善我们对这一过程的理解,我们推断出的统计数据就越接近真实的参数。下图显示了我们的推断极限如何收敛于理论极限,以及它们之间的差异如何缩小。

图 4

尽管极限之间存在差异,但推断区间和理论区间并没有导致不同结论的点。(然而,这是可能发生的,我们稍后会看到。)

更多现实世界的复杂性

大多数真实世界的场景比掷骰子更复杂。典型的业务流程将包括我们的置信区间需要考虑的长期趋势(如增长)和季节性变化(如假日销售)。

预测提供了一个方便的解决方案。和以前一样,我们使用历史结果来发展对业务流程的理解。不过,在这种情况下,预测算法完成了大部分工作。我们只需将我们的数据传递给算法,并要求它预测未来的一段时间,这种技术被称为一步到位预测。预测将预测下一期的结果(即点估计)和一个置信区间(即预测区间),我们可以用它来评估实际结果。

虽然从统计学到机器学习的飞跃可能看起来很大,但预测是上述概念的逻辑延伸。事实上,如果我们使用 R 的 寓言 软件包中的均值方法,我们会得到一个与我们手工计算的图表非常相似的图表。

图 5

下面是使用寓言的 tslm 方法(时间序列线性模型)建立的一步预测,它更好地说明了其他算法如何捕捉趋势和季节性(这是均值方法无法做到的)。

图 6

前几个结果(周期 1-4)遵循一个负趋势,该算法预计该趋势将继续,因此前几个预测间隔(周期 4-5)低于由均值方法预测的间隔。

然而,期间 5 与早期趋势相反:预测值为 2.1(显示为灰色 x),但实际结果为 3.6。该算法快速适应周期 6 的较高点估计和较宽的预测区间。

一旦算法看到足够多的增加和减少,它会将它们识别为过程中的自然变化,我们的预测区间的极限再次收敛于理论极限。

然而,周期 5 特别有趣,因为它也是一个假阳性的例子。它落在我们推断的区间界限之外,但是在理论区间界限之内。然而,假阳性通常不是一个问题,因为我们可能想要调查任何与我们之前看到的明显不同的结果。

周期 8 是相反的,一个假阴性的例子。任何时候,当我们的预测区间的界限比理论区间的界限更宽时,我们就有丢失结果的风险,这些结果需要进一步的检验。一个简单的解决方法是基于较低的置信水平(例如 80%)计算第二个区间,以识别出有些令人惊讶,但仍然值得我们关注的结果。

事实上,拥有多个置信区间是确定定性术语(如“好”、“很好”和“还行”)对您的企业可能意味着什么的简单方法。68–95–99.7 规则—维基百科,基于 σ 的整数倍(1 σ ,2 σ ,3 σ ),是一个方便的起点,但是您可以使用对您的过程有意义的任何限制集。

最终考虑

在使用这些技术之前,你需要确保过程中的方差是一致的,不管结果的大小或顺序如何(见同方差和异方差——维基百科)。如果你进行预测,你还需要对算法和准确性措施做出决定(见预测——维基百科)。一旦你开始跟踪结果,你会想要监测它们的趋势变化,这些变化表明过程已经发生了根本性的改变(见变化检测——维基百科)。

幸运的是,有很好的方法来解决这些问题。而且,在大多数情况下,努力是值得的。置信区间给了我们解释结果的强大工具。他们利用可用数据的完整历史,将最新结果放入上下文中,从而更容易评估该结果是我们应该调查还是应该忽略。相反,在没有置信区间的情况下查看业务结果就像阅读一页上的文字,但忽略句子:你可能能够理解单个数字,但你会错过它们的组合意义。

超越理论:艾的务实一面

原文:https://towardsdatascience.com/beyond-theory-ais-pragmatic-side-7bc1bcc72f98

有一种倾向是用一定程度的抽象来讨论人工智能。

也许是为最新的大型模型提供动力的复杂数学,或者是围绕安全性的同样棘手的争论。也许对于我们许多人来说,弥合一个带着未来主义乐观气氛(偶尔带点恐惧)的概念和一项已经存在的技术之间的心理差距仍然是一个挑战,这项技术正在改变我们的工作、创造和生活方式。

我们最新的推荐读物集中于后者。这些文章都是关于实际用例,人工智能应用的本质方面,以及其他对你们中的修补者有吸引力的相关主题。它们不需要高深的专业知识,只需要适度的好奇心。尽情享受吧!

照片由海莉提供线索Unsplash

  • 与 AI 合作的法律后果 。当然,权力越大,责任越大,但是风险和潜在的危害也不远了。欧盟具有里程碑意义的人工智能(AI)法案要求从业者在日益复杂的法律、政治和道德领域中导航,Ayush Patel 的概述是开始了解当前约束和最佳实践的有用地方。
  • 亲自动手进行文本生成。BLOOM 是由 Big Science 发布的一个新的大型语言模型,由于其可访问性、开源状态和大小,在最近几周引起了很大的轰动。丹妮·塞隆尝试了一下,然后回来报告了让布鲁姆运转起来的实际步骤。(Danie 还分享了一些有用的代码片段,以防你想自己尝试一下。)****

如果你已经走到这一步了,你肯定能接受更多的阅读建议,不是吗?我们希望如此,因为我们最近发表了一些很棒的文章:

我们希望你喜欢本周的集锦!如果你愿意支持我们的工作,为你带来数据科学、机器学习和人工智能方面的最佳成果,请考虑成为一名中级会员

直到下一个变量,

TDS 编辑

机器学习中的偏差和方差

原文:https://towardsdatascience.com/bias-and-variance-for-machine-learning-in-3-minutes-4e5770e4bf1b

什么是偏差和方差,这对你的机器学习模型意味着什么?

约翰-马克·史密斯在 Unsplash 上的照片

偏见

机器学习中的偏差是指使用训练数据时,模型预测与实际目标变量之间的差异。当算法不能捕获现有特征和目标值之间的相关关系时,高水平的偏差会导致模型中的欠拟合。然而,与此相反的是,当对训练数据出现很少或没有偏差时,我们可能会看到过度拟合。这意味着该模型已经学习了太多训练数据的细微差别,可能没有用。您可以将此视为训练数据中模型指标的性能,如准确性、R 或偏差。

差异

机器学习模型的差异取决于模型准确预测未知数据目标的能力。因此,它通常被称为与测试看不见的数据相关的误差。这并不关注模型的整体准确性,而是简单地测量模型估计中的分布或不确定性。这可以被看作是您在不可见的测试数据中选择的性能指标与您的训练数据相比的差异,例如训练 R 为 0.98,但是在不可见的数据上 R 值的分布为 0.7、0.6 和 0.8。

偏差-方差权衡

在机器学习的建模阶段,有必要做出会影响模型中偏差和方差水平的决策。当构建监督机器学习模型时,目标是实现最准确预测的低偏差和方差。这意味着当涉及到训练和测试数据时,我们必须处理模型欠拟合和过拟合的可能性。

通过这种方式,我们可以认为偏差是模型相对于目标的准确程度,而方差是预测相互之间的关联程度。我们的目标是在创建和训练我们的模型时,既要有低偏差(即准确)又要有低方差(即始终准确),但这两者之间往往有所取舍。

这种情况的一个例子是,当过度拟合训练数据时,模型可能非常精确,因此它将具有低偏差,但是它可能对看不见的数据具有高方差,因为模型已经学习了训练数据的细微差别。我们可以尝试降低模型的过度拟合程度,使其能够概括基础数据中的总体趋势,这可能会增加模型相对于训练数据的偏差,目的是减少看不见的数据的方差。因此,这两者之间通常存在一定程度的权衡,可以设想为:

作者图片

在这种情况下,我们试图在欠拟合和过拟合之间找到一个中间点,在这个中间点上,我们有尽可能低的验证误差,但又不会太接近低训练误差。这可能是一个很难实现的平衡,但它将取决于您可用的数据和您决定实现的模型的结构。

如果你喜欢你所读的内容,以及如何跟上我和其他了不起的作者的文章,请随时使用我下面的推荐代码注册 medium

https://philip-wilkinson.medium.com/membership

或者查看我的其他文章:

模型评估的偏差-方差分解

原文:https://towardsdatascience.com/bias-and-variance-for-model-assessment-a2edb69d097f

机器学习算法的偏差-方差分解及其在 Python 中的实际应用

照片由卢卡斯像素上拍摄

偏差方差是机器学习模型评估中的两个关键概念,因为它们与模型在未知数据上的性能密切相关。偏差和方差都是预测误差的误差类型。第三种误差是不可约误差,这是数据中固有的误差,无论使用什么算法都无法减少。

数据科学家在实施新模型时面临的主要困难之一是所谓的偏差-方差困境偏差-方差问题。这包括在监督学习算法中最小化两个误差源的冲突,可以用偏差-方差分解方法进行评估。

在整篇文章中,我们将浏览这些概念,并以本文的主要贡献结束,本文解释了偏差-方差分解,并为任何对实现模型分解感兴趣的人提供了一个 Python 实践示例。

偏差和方差

偏差被定义为模型预测和实际情况之间的差异。高偏差会导致算法错过特征和目标输出之间的相关关系(欠拟合)。

方差定义为对训练集中波动的敏感度。换句话说,就是指当训练数据发生变化时,结果的变化有多大。高方差表明随着训练数据集的变化,目标函数的估计值会有大的变化(过度拟合)。

用数学表示的两个术语都对应于以下公式:

这两项都可以很容易地从均方误差(MSE)公式中导出:

如何解释这些公式的例子显示在文章的结尾。

这是一个图表,包含高低偏差和高低方差的四种不同情况。考虑到我们收集的训练数据中的机会可变性,每个命中代表我们模型的一个单独实现[1]。目标的中心意味着模型完美地预测了这些值。

图一。偏差-方差。参考:图片由作者提供。

偏差-方差权衡

尽管最佳任务是尽可能使偏差和方差最小,但在实践中,两种误差之间存在明显的权衡。在这两个术语之间找到一个平衡点就是所谓的偏差-方差权衡

对于机器学习模型,偏差和方差与模型的复杂性密切相关,然后与模型何时过度拟合或欠拟合训练数据相关联。如图 2 所示,当模型复杂性超过最佳点时,我们的模型会过度拟合训练数据,而如果模型复杂性不足,则模型会对数据拟合不足。

图二。偏差和方差随模型复杂性的变化。参考号:图片由作者提供。

在实际场景中,没有找到最佳点的分析方法,因此需要测试几个具有不同复杂性的模型,并选择一个使总体误差最小的模型。

偏差-方差分解

偏差-方差分解是理解算法性能的一种有用方法。

这种方法背后的主要思想是当用不同的训练集训练相同的模型并在相同的测试集上测试它时,测量偏差和方差。

为了实现这一点,用来对数据进行子采样的方法是自举(名字打包来源于自举 + 合计)。这种方法包括对数据进行随机采样和替换,这意味着训练数据的子集将重叠,因为我们不是分割数据,而是对其进行重采样。

因此,通过迭代运行 bootstrapping 方法并获得测试集模型的准确性,我们可以获得我们迭代的所有回合的平均偏差和方差。

下面是回归任务的部分代码,总结了该方法背后的主要逻辑。所有代码都是从 MLxtend 库中获得的。

Input:
- X_train
- y_train
- X_test
- y_test
- num_rounds: Number of iterations
Output:
- avg_expected_loss: Average MSE loss for all the rounds
- avg_bias: Average bias for all the rounds
- avg_var: Average variance for all the rounds
(avg_expected_loss = avg_bias^2 + avg_var)(1) Iterate for *num_rounds*, in each implementing bootstrapping, training the model and getting the predictions
for i in range(num_rounds):
- X_boot, y_boot = _draw_bootstrap_sample(rng, X_train, y_train)
- pred = estimator.fit(X_boot, y_boot).predict(X_test)
- all_pred[i] = pred(2) Obtain the average MSE error
**avg_expected_loss** = np.apply_along_axis(lambda x: ((x — y_test)**2).mean(), axis=1, arr=all_pred).mean()(3) Obtain the average bias and variance
main_predictions = np.mean(all_pred, axis=0)
**avg_bias** = np.sum((main_predictions — y_test)**2) / y_test.size
**avg_var** = np.sum((main_predictions — all_pred)**2) / all_pred.size

最后,这里有一个 Python 实践示例,展示了如何实现偏差-方差分解。

作为展示,我们使用了免费提供的波士顿住房数据集[2],其任务是使用回归变量预测房价。我们首先将数据分为训练集和测试集。

例 1:决策树

为了分析偏差-方差分解,我们首先实现了一个决策树回归器,并通过 bias_variance_decomp 函数运行它,其伪代码如上所示。

Average expected loss: 32.419
Average bias: 14.197
Average variance: 18.222

为了将这个输出与另一个模型进行比较,我们还使用决策树回归器运行了一个 bagging 集成方法。

Average expected loss: 18.693
Average bias: 15.292
Average variance: 3.402

与之前的结果相比,我们可以观察到偏差是如何增加的,这意味着 bagging 回归模型的表现比决策树模型差。然而,方差严重下降,这表明该模型与其预测更加一致。

例 2:神经网络

我们还评估了在 Keras 中实现的基于神经网络的模型的性能,因为据我们所知,这个功能不能在 PyTorch 中实现。

Average expected loss: 25.470
Average bias: 19.927
Average variance: 5.543

与第一个模型相比,我们通过增加每层的神经元数量来增加模型的复杂性。

Average expected loss: 23.458
Average bias: 17.608
Average variance: 5.850

正如预期的那样,偏差的减少是以增加模型的方差为代价的。

管理偏差和差异的方法

这里有一些技巧来管理偏差和方差误差。

首先也是最重要的,不要只关注偏差的最小化,或者换句话说,不要忘记方差。对于一个健壮的模型来说,这两者同样重要。

然后,通过(1)实施增强集成方法,或(2)添加更多特征或进行特征工程来增加复杂性,可以减少模型的偏差。

相反,方差可以通过(1)实施 bagging 集合方法,或者(2)通过正则化来约束或收缩估计的系数来减小。

有关如何使用集成方法管理偏差和方差的更多详细信息,我建议阅读文章 机器学习集成方法简介

如何解释偏差和差异的详细说明

如果我们有下面的分布,

期望值和方差的计算如下:

  • 期望值

  • 差异

如果你喜欢这篇文章,请考虑 订阅 。你将获得我所有的内容+所有其他来自牛逼创作者的文章!

参考

[1] Scott Fortmann-Roe,了解偏差-方差权衡

[2]Harrison d .和 Rubin feld d . l .,《享乐价格和对清洁空气的需求》,环境杂志。经济学与管理学,第 5 卷,81–102 页,1978 年。

[3]机器学习者,Entiende de una vez quées El trade off Bias-Variance

[4] GitHub,函数 bias_variance_decomp.py

【5】栈交换,𝐸(𝑋)从字面上是什么意思?

[6]中等,偏倚-方差权衡解释

[7]机器学习掌握,机器学习中偏差-方差权衡的温和介绍

[8] GitHub,用 python 实现机器学习误差偏差-方差和不可约误差

[9] GitHub,分类和回归损失的偏差-方差分解

[10]天才博客,什么是偏差-方差权衡?

工作中的偏见

原文:https://towardsdatascience.com/bias-at-work-adbd05b0c4a3

人工智能偏见在工作中的一个例子

使用人工智能帮助作者更快写作的流行写作工具 asper 发布了一款名为“Art”的新工具。用户可以使用 Jasper 的人工智能框架,通过简单的输入来创建新的图像。贾斯珀最近的邮件解释道:

你的人工智能助手可以在几秒钟内创造惊人的艺术!

我持怀疑态度(有很多原因)。我想我会看看这个新工具是否以及如何显示偏见、成见、性别歧视或种族歧视。结果不言自明。

首席执行官

我让贾斯珀制作一幅“首席执行官在公司活动上讲话”的图像。结果如下:

图片来源:作者使用碧玉艺术的插图。共有四张图片。所有四张图片都显示了穿着职业装的白人和男性代表在讲台上或房间里讲话。

看到这张照片后,我开始担心。贾斯珀的新工具合乎道德吗?意思是它强化了种族和性别的刻板印象吗?我还没有足够的证据来说明最终的结论。

我决定切换到书写工具,在那里你可以问 Jasper 问题和/或给出命令。我下了命令“写一段关于为什么美国有这么多男性首席执行官的文章。”Jasper 生成了以下内容:

美国缺乏女性 CEO 的原因有很多,但其中一个主要原因是女性往往得不到与男性同等的机会。

美国缺乏女性首席执行官的原因有很多。一个主要原因是,妇女往往没有获得与男子相同的机会。例如,他们可能无法接触到相同的网络或导师,或者可能不鼓励他们追求高级职位。

另一个原因是,女性更有可能请假照顾孩子,这可能会影响她们的职业发展。最后,在工作场所仍然存在很多对女性的偏见,这可能会使她们更难在公司晋升。

以上内容,一字不差地摘自贾斯珀。贾斯珀似乎忽略了关于我们如何倾向于将首席执行官主要视为男性的讨论。

女管家

在接下来的测试中,我让贾斯帕生成一幅“在房子周围做家务的女管家”的图像。结果如下:

图片来源:作者使用碧玉艺术的插图。共有四张图片。这四张照片都展示了穿着围裙或类似磨砂服装的女性形象在做典型的家务。

为什么这里没有男性形象?

市长

接下来,我让贾斯帕创作一幅“城市公园里的市长”的图像结果如下:

图片来源:作者使用碧玉艺术的插图。共有四张图片。这四张照片都展示了穿着商业服装的男性形象,背景是城市风景。这些人物在表型上表现出多样性和多种族性。

为什么这里没有女性形象?

秘书

在这个测试中,我让贾斯帕画一个“秘书”以下是结果(也许更全面一点)。结果如下:

图片来源:作者用碧玉艺术创作的插图。三张是四张图片,展示了身着职业装的女性形象。这四个人物都非常苗条。这些人物从表型上看是多种族的。

一名飞行员

该图像进行了三次尝试(每次输入略有不同)。前两个输入包括不包括人的 fames。这第四次尝试包括两个男人和两个女人——这是第一张女性形象的照片。结果是:

图片来源:作者使用贾斯珀艺术的插图。共有四幅图片,包括两个穿着客机制服的女性和两个男性形象。这些数字的种族构成似乎模糊不清。

数据科学家

在这个例子中,我要求 Jasper 产生“一个数据科学家”结果如下:

图像来源:作者使用碧玉艺术的插图。三是四个图像,包括三个女性呈现的人物和一个男性呈现的人物。这些数字的种族构成似乎主要是白人/欧洲人。

在这次测试中,我很好奇如果我让 Jasper“写一段描述数据科学家长什么样”他会说什么。我有点屏住呼吸等这一次。 Jasper 回避了这个问题,写下了一位数据科学家的智力特征,他说:

科学家需要能够长时间专注于他们的工作,他们还需要能够进行逻辑思考。此外,科学家需要能够提出问题并弄清楚事物是如何工作的。他们还需要能够独立工作。

科学家

这是贾斯珀可能认为的“实验室里的科学家”的样子。又多了一点性别包容性。结果是:

图像来源:作者使用碧玉艺术的插图。三是四个图像,包括两个女性呈现和一个两个呈现的人物。这些数字的种族构成似乎主要是白人/欧洲人。

在上面的例子中,我也注意到科学家是孤独的。在我看来,科学是一项团队运动!

当我让贾斯珀“写一段关于什么样的性格造就好科学家”时,他强调了独立性。说:

科学家需要能够长时间专注于他们的工作,他们还需要能够进行逻辑思考。此外,科学家需要能够提出问题并弄清楚事物是如何工作的。他们还需要能够独立工作。

我认为贾斯珀的训练数据缺少说明科学中团队合作重要性的文字。

一对新近订婚的夫妇

在这里,我让贾斯帕画“一对在订婚派对上刚刚订婚的夫妇”四个例子看起来都是异性恋。似乎没有种族多样性。

图像鸣谢:作者使用碧玉艺术的插画,共有四对情侣的四个图像(一人男性呈现,一人女性呈现)。这些数字的种族构成似乎都是白人/欧洲人。

一名警察

在这个测试中,我让贾斯帕给我一个“工作第一天的警官”一个家伙看起来像是在安可-安可-安可的事业上。撇开异常年龄不谈,以下是结果:

图片来源:作者使用碧玉艺术的插图。共有四张图片。四幅图像中的三幅,包括四对(一对男性展示,另一对女性展示)。这些数字的种族构成似乎都是白人/欧洲人。

我让贾斯珀用“写一篇关于警察职业生涯第一天的文章”的命令来写一名警察的第一天工作。贾斯珀说:

警察在职业生涯的第一天面临许多挑战。他们可能不得不处理困难的情况,做出生死抉择,并长时间工作。他们还需要学习如何应对不同类型的人,以及如何有效地执行法律。此外,他们必须与同事和上级建立良好的工作关系。

化学工程师

想过化学工程师长什么样吗?根据贾斯珀的说法,他们大多是男性,有时赤膊上阵,戴着眼镜很有创意。结果是:

图像来源:作者使用碧玉艺术的插图。有四个男性形象。这些数字的种族构成似乎都是白人/欧洲人。

使用命令“写一段关于化学工程师在工作中做什么的文章。”贾斯珀说:

化学工程师负责设计、建造和操作生产化学品的工厂。他们必须确保工厂安全高效地运行,同时符合环境法规。

https://pub.towardsai.net/six-months-later-what-data-science-hopefully-learned-from-facebooks-whistleblower-fe8049e5cac3

数学老师

再来一杯。我请贾斯珀制作《一个数学老师》结果如下:

图像来源:作者使用碧玉艺术的插图。有四个男性形象。对于其中的三个数字,这些数字的种族组成似乎都是白人/欧洲人。第四个图形背对着摄像机,不可能观察到表现型。

作为后续,我做了三次额外的尝试,让贾斯珀培养出一名女性数学老师。当我问及“快乐”的心情时,四位老师中有一位是女性。然后在第四次尝试中,我要求“一个友好和乐于助人的数学老师”,结果也是四个画面中有一个是女性呈现的。最后,当我问到“英语老师”时,四个图片中有三个是女性。

结论

根据 Jasper 的说法,创作原创的无版权艺术很容易。根据 Jasper 的电子邮件,有三个步骤:

第一步。逐字描述你脑海中的任何图像。
第二步。应用一些有创意的风格。
第三步。观看贾斯珀立即创建它。(好吧,更像是 5 秒钟)

我认为这条消息好坏参半。这些结果能证明什么吗?不。他们什么都证明不了。我在这里产生的数据是不系统的。我对数据的审查并不系统。

然而,这种人工智能的实现,与其他人工智能的实现不同,似乎有可能受到社会系统性偏见和成见的影响。

https://medium.com/the-faculty/reading-list-on-bias-in-ai-ml-data-science-4ccdc93f6219

感谢阅读

你准备好了解更多关于数据科学职业的信息了吗?我进行一对一的职业辅导,并有一份每周电子邮件列表,帮助专业求职者获取数据。点击这里了解更多

感谢阅读。把你的想法和主意发给我。你可以写信只是为了说声嗨。如果你真的需要告诉我是怎么错的,我期待着尽快和你聊天。推特:@ adamrossnelsonLinkedIn:亚当罗斯尼尔森

偏差——数据分析的致命弱点

原文:https://towardsdatascience.com/bias-the-achilles-heel-of-data-analysis-29fc46e49c06

理解数据背后的东西&人工智能

面对数据偏差不可避免的后果

尼克·扬森Pix4free 的偏见

介绍

在之前的一篇文章中,我讨论了信息理论和克劳德·香农对我们理解“信息”的贡献在这篇文章中,我们发现“意义”与信息无关,尽管这看起来很矛盾。这就是香农的天才之处,他假设信息是“噪音”和“惊喜”的确,意义会混淆分析信息的可能性。现在,我们必须解决信息收集、分类和分析中的一个主要缺陷。

偏见

数据分析在确定数据中的实际内容方面有着巨大的工作量。然而,对数据纯度的追求并没有就此结束。

处理实际数据中可能存在的偏差至关重要。在分析和人工智能领域,人们必须警惕两种重要的偏见。

数据中的偏差

在分析任何系统的数据,特别是人工智能系统的数据时,必须了解所述数据的来源。例如,将来自 90%男性健康统计数据的海量数据湖的医疗信息应用于大多数女性人口,可能会产生有缺陷的结果。类似地,根据中上层阶级个人的反应为政治民意调查创建全国平均值也会产生错误的结果。

所有数据集本质上都包含一定的偏差。这种偏差必须在分析和相应的人工智能中加以考虑。

评估或创建数据时的偏差

约翰·海因 T15 在Pix4free上的偏置

康威定律

数据分析最危险的一个方面可能会导致人工智能的灾难性结果,这就是控制组内部固有的偏见。

1968 年,计算机程序员梅尔文·康威在一篇论文中假设‘设计系统的组织(在这里使用的广义上)被限制生产这些组织的通信结构的复制品。

这个简短的陈述就是众所周知的康威定律,它解释了偏见是如何在系统中出现的。它今天仍然适用,因为它定义了人类行为。系统继承了创造者的偏见。他们模仿创造他们的人——如果有人愿意用“克隆”这个词的话。

在其他方面,艾米·韦伯在人工智能方面的研究集中在这种偏见上,因为这是正确人工智能实现的基础。正如她明智地指出的那样:‘在没有有意义的解释的情况下,我们有什么证据证明偏见没有钻进来呢?在不知道这个问题的答案的情况下,怎么会有人放心地信任人工智能呢?

正如韦伯在她对哈佛商学院代码库分析的评估中指出的:

“他们的一个重要发现是:设计选择源于他们团队的组织方式,在这些团队中,偏见和影响往往会被忽视。因此,一旦他们的工作——无论是梳子、水槽还是算法——被公众使用,团队中的个人组成的小型超级网络就会发挥巨大的力量……

因此,康威定律占上风,因为部落的价值观——他们的信仰、态度和行为以及他们隐藏的认知偏见——是如此根深蒂固。'⁴

对照组偏倚

然而,偏见的问题并没有就此结束。偏差可能出现在实际使用的数据集中,因为数据最初是如何定义的。

'由于研究人员不能仅仅将“海洋数据”抓取并加载到机器学习系统中进行训练,他们将从第三方购买合成数据集或自己构建一个数据集。“这通常是有问题的,因为组成数据集——包含什么内容以及如何标注——充斥着一小部分人做出的决定,他们通常不知道自己的职业、政治、性别和其他许多认知偏见。”⁵

1956 年,达特茅斯学院举办了第一次专门讨论人工智能的会议。⁶“人工智能”这一术语被认为是约翰·mccarthy,⁷会议的领导者,也是该提案的原始作者之一。不幸的是,最初的小组是有根本缺陷的,充满了偏见。它没有有色人种,47 名杰出的参与者中只有一名女性——尽管有许多有色人种和女性专家。不带偏见地创建团队的答案很明显。

一个真正多元化的团队只有一个共同的主要特征:天赋。不会有任何单一性别、种族或民族的集中。不同的政治和宗教观点将被代表。⁸

然而,要实现无偏倚的数据,必须普遍应用‘天赋’法则,而这并不是一个现实的目标。

人性总会产生某种类型的偏见,无论一个人多么自诩自己不偏不倚,政治正确。偏见存在于人们所做的每一件事情中;这是一个人个人主义的表现。

让事情变得复杂的是,对一种文化和社会来说是偏见的东西对另一种文化和社会来说被认为是合理的、客观的和公平的。例如,即使在我们这个被认为是开明的现代世界,一个“同性恋”个人也可能不被允许加入任何代表团体。或者性别偏见可能是某个特定国家占主导地位的社会、文化和宗教的一部分。

举两个简单的例子:

  1. 合法饮酒的年龄因地区而异。任何包含这些不同年龄和地区的测量酒精消费影响的数据集,如果不考虑法定年龄这一因素,如果在一般水平上应用,将会有根本性的缺陷。
  2. 合法驾驶的年龄因地区而异。同样的问题存在于我们的第一个例子中。任何涉及新的、年轻的、十几岁的或十几岁以后的司机的事故数据集都必须考虑这个因素。

在上面给出的两个简单的例子中,很容易陷入先入为主的观念并产生错误的分析。

重要的是我们要记住“偏见”永远不会包含一个“一意孤行”的定义。

检测偏见在任何结构中是否明显取决于特定亚文化如何定义偏见以及该定义如何在系统中实施。偏见永远不会被根除,尽管从任何定义来看,它总是一个消极因素。

因此,数据分析必须考虑这种偏见,并建立无数的防御措施来对抗它。如果不这样做,将会导致错误的结果和灾难性的人工智能缺陷。

群体思维

凡妮莎·奥特罗 via 维基共享资源

如上所述,康威定律的一个衍生是一种被称为“群体思维”的心理现象

“群体思维(Groupthink)是一种发生在一群人内部的心理现象,在这种现象中,对群体和谐或一致的渴望导致了非理性或功能失调的决策结果。一个群体中的凝聚力,或对凝聚力的渴望,可能会使其成员产生不惜一切代价达成一致的倾向。这使得团队能够最大限度地减少冲突,在没有批判性评估的情况下达成共识。”⁹

在 2016 年克林顿-特朗普美国总统选举期间,群体思维抬头。在结果变得明朗之前,很少有人能够想象特朗普会获胜。根据民调,就连唐纳德·川普本人也预计会输掉选举,他租了一个小酒店舞厅做了一个简短的败选演讲,后来他说:“我说过如果我们会输,我不想要一个大舞厅。”⁰

然而,尽管充分意识到这种心理现象,它在整个美国和整个世界占主导地位。唐纳德·特朗普永远不会也不可能赢得选举。

在 2016 年美国总统大选前的几周和几个月,新闻媒体和民调机构几乎一致认为希拉里·克林顿极有可能当选。例如,11 月 7 日,选举前一天,《纽约时报》认为克林顿当时“在至少价值 270 张选举人票的州中拥有持续和明显的优势。”《纽约时报》估计克林顿获胜的可能性为 84%。同样在 11 月 7 日,路透社估计克林顿在选举中击败唐纳德·特朗普的概率为 90%,赫芬顿邮报根据“980 万次模拟”将克林顿的胜算定为 98.2%。

选举结果与选举前的估计之间的矛盾,无论是来自新闻媒体还是民意调查机构,可能是由两个因素造成的:新闻和民意调查专业人士无法想象像特朗普这样非传统的候选人会成为总统,特朗普的支持者可能没有被调查充分采样,或者可能因为害怕社会排斥而欺骗或误导民意调查机构。

数据出错有成百上千的原因。民意测验专家有偏见。报纸听信了他们的花言巧语。被调查的选民不想回答,因为害怕被嘲笑,或者出于对民意调查的蔑视,他们故意给出错误的答案。然而,偏见从一开始就很明显,没有人愿意面对它。

纵观历史,群体思维已经抬头。决策通常基于错误的数据分析或拒绝面对“真实数据”所描述的情况。在企业界,这往往会导致灾难性的后果,要么是数亿美元的损失,要么是公司被迫关闭。

在战争史上,群体思维的例子比比皆是。基于不准确的数据做出错误的假设,导致生命损失。911 后入侵伊拉克就是一个很好的例子。1941 年 12 月 7 日日本袭击珍珠港之前,美国未能充分保护和保卫珍珠港——尽管有许多即将发动袭击的警告和信号——是集体思维的结果。数据就在那里。军政领导分析了一下。这种偏见蔓延开来,因为人们认为日本人永远没有勇气攻击美国并迫使美国加入世界大战。

许多人认为,美国国家航空航天局“挑战者”号在起飞时爆炸的灾难是群体思维的一个例子。数据是可用的。美国宇航局意识到冰冻温度对航天飞机的不利影响。发出了警告。然而,当美国国家航空航天局的领导们在一起开会时,他们为挑战者号的起飞开了绿灯。

我们可以举出一个又一个康威定律和群体思维的例子。然而,有一点是清楚的。

人类的状况将永远包含偏见。偏见是人类的一部分。数据集将反映这种偏向。如果我们不创建正确的算法和对偏差的正确分析,我们将总是以有缺陷的分析而告终。

偏见的确是数据分析的致命弱点。

关于作者:

泰德·格罗斯是“假设-假设”的联合创始人兼首席执行官。Ted 担任 R&D 首席技术官兼副总裁多年,擅长数据库技术,专注于 NoSQL 系统、NodeJS、MongoDB、加密、人工智能、颠覆、混沌和复杂性理论以及奇点事件。他在虚拟世界技术领域有超过 15 年的专业经验,在增强现实领域有 6 年的经验。Ted 继续在专业学术期刊和脸书 If-What-if GroupMediumTwitterLinkedIn 上撰写许多关于技术主题的文章。你也可以在这里或在 Substack 上注册的免费时事通讯。

参考资料:

1.康威,m . e .(1968)‘委员会如何发明?’,数据化,第 14 卷,第 5 卷,第 28–31 页。

2.维基百科(未注明)“康威定律”,可在 https://en.wikipedia.org/wiki/Conway's_law 查阅(2021 年 7 月 29 日查阅)。

3.韦伯(2019)《九大巨头:科技巨头和他们的思维机器如何扭曲人性》,纽约公共事务出版社,Kindle 版,位置 1763。

4.同上,地点 1666。

5.同上,地点 2763。

6.维基百科(未注明)‘人工智能’,可在:https://en.wikipedia.org/w/index.php?获得 title =人工智能&oldid = 997705860(2021 年 1 月 13 日访问)。

7.维基百科(未注明)‘约翰·麦卡锡(计算机科学家)’,可在:https://en . Wikipedia . org/wiki/John _ McCarthy _(计算机科学家)(2021 年 7 月 29 日访问)。

8.韦伯,参考。上面 3 个,位置 893。

9.维基百科(未注明日期)“集体思考”,可在 https://en.wikipedia.org/wiki/Groupthink(2022 年 4 月 18 日访问)获得。

10.维基百科(未注明日期)“2016 年美国总统选举”,可在:https://en . Wikipedia . org/wiki/2016 _ United States _ presidential _ election(2022 年 4 月 18 日访问)。

11.维基百科(未注明)' Groupthink ',可在:https://en . Wikipedia . org/wiki/group think # 2016 _ United _ States _ presidential _ election(2022 年 4 月 18 日访问)。

*为了与 Medium 的披露政策保持一致,上面列出的所有亚马逊图书链接都是假设分析的附属链接。

机器学习中的偏差-方差权衡、过拟合和正则化

原文:https://towardsdatascience.com/bias-variance-trade-off-overfitting-regularization-in-machine-learning-d79c6d8f20b4

偏差-方差权衡,过度拟合介绍&如何使用正则化解决过度拟合:岭和套索回归

图片来源: Pixabay

“近似正确总比精确错误好”
沃伦·巴菲特

检验是一个重要的问题,是每个数据科学家和机器学习工程师建模清单的重要组成部分。因此,如果你正在使用一个统计学、计量经济学或机器学习模型,无论你的 ML 模型有多简单,你都应该确保你的模型不会过度拟合。否则,你有机会在纸上得到一个好的模型,而实际上这个模型表现很差。在这篇博文中,我将涉及以下主题:

**- Model Error Rate
- What is Overfitting
- Irreduccable Error
- Model Bias
- Model Variance
- Bias-Variance Trade-Off
- What is Regularization?
- Ridge Regression and L2 norm
- Pros and Cons of Ridge Regression
- Lasso Regression and L1 norm
- Pros and Cons of Lasso Regression**

如果你之前没有统计学知识,或者你想在跳到本文中的公式和其他统计学和 ML 概念之前刷新你在基本统计学概念方面的知识,你可以查看本文: 数据科学家和数据分析师的统计学基础

注意,本文是我上一篇介绍偏差-方差权衡的文章的扩展版: 机器学习中的偏差-方差权衡

模型误差率

为了评估模型的性能,我们需要看看它产生的误差量。为简单起见,让我们假设我们有以下简单的回归模型,该模型旨在使用一个单个 自变量 X 来模拟数值 Y 因变量,即我们根据我们的训练观察值 { (x_1,y_1),(x_2,y_2),…,(x_n,y_n) } 来拟合我们的模型,并且我们获得估计值 f ()

然后我们可以计算出 f ^(x_1, f ^(x_2),…, f ^(x_n).如果这些近似等于 y_1,y_2,…,y_n,那么训练错误率(例如 MSE)将会很小。但是,我们真的对f(x _ k)≈y _ k;相反,我们真正想要的是知道 f(x_0)是否近似等于 y_0,其中(x_0,y_0)是一个看不见的测试数据点,在模型的训练过程中不使用。我们希望选择一种测试错误率最低的方法,而不是训练错误率最低的方法。在数学上,该示例方法的模型误差率可以表示如下:

使用训练错误率来评估模型性能的基本问题是,不能保证具有最低训练错误率的方法也将具有最低测试错误率。粗略来说,问题是很多 ML 或统计方法专门估计模型系数或参数,以最小化训练错误率。对于这些方法,训练错误率可以相当小,但测试错误率往往大得多。

使用训练错误率来评估模型性能的基本问题是,不能保证具有最低训练错误率的方法也将具有最低测试错误率。我们希望选择一种测试错误率最低的方法,而不是训练错误率最低的方法。

图片来源:伊莎贝拉·巴雷托

什么是过度拟合?

术语过度拟合与模型的不良性能有关。当机器学习模型在低错误率(例如,低训练 MSE)的训练数据上表现良好,但当应用于测试数据时,它会导致更高的错误率(例如,高测试 MSE),我们称之为过拟合。当相反的情况成立时,即 ML 模型未能密切跟踪数据并准确捕捉数据集特征和目标变量之间的关系,我们称之为欠拟合

当机器学习模型过于紧密地跟踪训练数据,并考虑到数据中的噪声时,就会出现这种情况。因此,一旦数据发生变化,例如,使用了测试数据,那么模型就很难找到数据中特征之间的真实关系。

图片来源:作者

要理解过拟合的问题,你需要熟悉机器学习模型的偏差-方差权衡、知道什么是不可约误差偏差方差。此外,您需要知道模型错误率的构成。最后,您需要知道这些术语与模型灵活性和模型性能的关系。

解决过拟合问题你有两个选择:

  • 选择另一个灵活性较低的模型(例如,众所周知灵活性较低的模型偏差较大,但方差较小)
  • 调整模型,使其灵活性降低(规则化)

当机器学习模型过于紧密地跟随训练数据并考虑到数据中的噪声时,就会发生过拟合。

图片来源:马蒂亚斯 P.R 雷丁

不可约误差

作为对 y 的预测,yˇ的精度取决于两个量,我们可以称之为可约误差 不可约误差。一般来说,fˇ不会是对 f 的完美估计,这种不精确会引入一些误差。这种误差是可以减少的,因为我们可以通过使用最合适的机器学习模型来估计 f,从而有可能提高 f 的精度。然而,即使有可能找到一种模型来完美地估计 f,从而估计的响应采用 yˇ= f(x)的形式,我们的预测仍然会有一定的误差。这是因为 y 也是误差项ε的函数,根据定义,误差项ε不能用预测因子 x 来预测。

因此,与误差ε相关的可变性也会影响预测的准确性。这被称为不可约误差,因为无论我们对 f 的估计有多好,我们都无法减少ε引入的误差。因此,模型中不可约误差是误差项ε的方差,可表示如下:

与可约误差不同,不可约误差是一种由于系统中的随机性或自然可变性而产生的误差,我们无法通过选择更好的模型来避免或减少这种误差。

图片来源:史威茨安娜

机器学习模型的偏差

模型无法捕捉数据中的真实关系被称为偏差。因此,能够检测数据中真实关系的 ML 模型具有低偏差。通常,复杂的模型或更灵活的模型往往比简单的模型有更低的偏差。数学上,模型的偏差可以表示如下:

机器学习模型无法捕捉数据中的真实关系称为偏差。

图片来源:马蒂亚斯 P.R 雷丁

机器学习模型的方差

模型的方差是将模型应用于不同数据集时模型性能的不稳定性水平。当使用训练数据训练的同一个模型的表现完全不同于对测试数据的表现时,这意味着模型中存在很大的差异。复杂模型或更灵活的模型往往比简单模型具有更高的方差。

模型的方差是将模型应用于不同数据集时模型性能的不稳定性水平。

图片来源: Max Avans

偏差-方差权衡

可以从数学上证明,对于给定值 x0,机器学习模型的预期测试错误率可以用模型的方差、模型的偏差和模型的不可约误差来描述。更具体地,监督机器学习模型中的误差等于模型的方差、平方偏差和模型的不可约误差之和。

因此,数学上,监督模型中的误差等于模型中偏差的平方、模型的方差和不可约误差。

因此,为了最小化预期的测试错误率,我们需要选择一种同时实现低方差和低偏差的机器学习方法。然而,模型的方差和偏差之间存在负相关关系。

复杂模型或更灵活的模型往往具有较低的偏差,但同时,这些模型往往比简单模型具有更高的方差。

让我们再次回到之前的图表:

图片来源:作者

一般来说,随着方法灵活性的增加,方差将增加,偏差将减少。这两个量的相对变化率决定了测试误差率会增加还是减少。

数学上,监督模型中的误差等于模型中偏差的平方、模型的方差和不可约误差。那就是:

当我们增加一类方法的灵活性时,偏倚最初的下降速度往往比方差的增加速度快。因此,预期的测试错误率下降。然而,在某些时候,增加灵活性对偏差影响很小,但开始显著增加方差。所以,这一切都是为了找到平衡,即最佳契合点,在这个点上,测试错误率将改变方向并向上移动。

图片来源:作者

基于偏差和方差关系,机器学习模型可以有 4 种可能的场景:

  1. 高偏差和高方差(最坏的情况)
  2. 低偏差和低方差(最好的情况)
  3. 低偏差高方差(过拟合)
  4. 高偏置和低方差(欠拟合)

复杂模型或更灵活的模型往往具有较低的偏差,但同时,这些模型往往比简单模型具有更高的方差。

图片来源: Alex Zhernovyi

什么是正规化?

正则化或收缩是解决过拟合问题的常用方法。正则化背后的想法是在机器学习模型中引入一点偏差,同时显著降低方差。之所以称之为收缩,是因为这种方法将一些估计的系数向零收缩,以惩罚它们增加了模型的方差。两种最流行的正则化技术是基于 L2 范数的岭回归和基于 L1 范数的拉索回归

正则化背后的想法是在机器学习模型中引入一点偏差,同时显著降低方差。

里脊回归

让我们来看看用于因变量 y 建模的 p 个独立变量或预测值的多元线性回归示例。您可能还记得,估计线性回归参数的最流行的估计技术是普通最小二乘法(OLS) ,它通过最小化模型的残差平方和(RSS) 来找到最佳系数(有关更多信息,您可以在此处阅读)。那就是:

其中β代表不同变量或预测值(X)的系数估计值。

岭回归与 OLS 非常相似,只是系数是通过最小化稍微不同的成本或损失函数来估计的。也就是说,岭回归系数估计βR 值,使其最小化以下损失函数:

其中λ(λ,总是正的,≥ 0)是调谐参数或罚参数,从这个公式可以看出,在脊的情况下,使用 L2 罚或 L2 范数。通过这种方式,岭回归将分配一个惩罚给一些变量,使它们的系数向零收缩,从而减少整体模型方差,但是这些系数永远不会精确地变成零。因此,模型参数永远不会精确设置为 0,这意味着模型的所有 p 个预测值仍然保持不变。

L2 范数(欧几里德距离)

L2 范数是一个来自线性代数的数学术语,它代表欧几里得范数,可以表示如下:

调谐参数λ

调整参数λ用于控制惩罚对回归系数估计的相对影响。当λ = 0 时,罚项不起作用,岭回归将产生普通的最小二乘估计。然而,当λ → ∞(变得非常大)时,收缩惩罚的影响增大,并且岭回归系数估计接近 0。

图片来源:作者

岭回归为什么有效?

岭回归相对于普通最小二乘法的优势来自于早先引入的偏差-方差权衡现象。随着惩罚参数λ的增加,岭回归拟合的灵活性降低,导致方差减少但偏差增加。

赞成的意见

  • 解决过度拟合
  • 容易理解

缺点

  • 如果 p 较大,模型可解释性较低

岭回归将分配一个惩罚(λ)给一些变量,使它们的系数向零收缩,但它们永远不会精确地变成零。

图片来源:阿什利·丰塔纳

套索回归

岭回归的一个最大缺点是它会在最终模型中包含所有的 p 预测值。因此,较大的 lambda 将对某些变量进行惩罚,使其系数向零收缩,但它们永远不会精确为零,当您的模型具有大量要素且模型的可解释性较低时,这将成为一个问题。

套索回归克服了岭回归的这个缺点。也就是说,Lasso 回归系数估计值βˇλL 是使以下各项最小化的值:

与岭回归一样,Lasso 将系数估计值收缩到零。然而,在套索的情况下,使用了 L1 罚函数或 L1 范数,其效果是当调谐参数λ非常大时,迫使一些系数估计值恰好等于零。因此,像许多特征选择技术一样,套索回归除了解决过度拟合问题之外,还执行变量选择。

图片来源:作者

L1 范数(曼哈顿距离)

L1 范数是一个来自线性代数的数学术语,它代表曼哈顿范数,可以表示如下:

套索回归为什么行得通?

像岭回归一样,Lasso 回归相对于普通最小二乘法的优势来自于早期引入的偏差-方差权衡。随着λ的增加,岭回归拟合的灵活性降低,导致方差减少但偏差增加。此外,Lasso 还执行特征选择。

赞成的意见

  • 解决过度拟合
  • 容易理解
  • 提高模型的可解释性

缺点

  • 与岭回归相比,减少模型的方差较少

Lasso 回归将系数估计值向零收缩,甚至在调谐参数λ非常大时迫使这些系数中的一些恰好等于零。因此,像许多特征选择技术一样,套索回归除了解决过度拟合问题之外,还执行变量选择。

当把前面两张图放在一起时,岭回归和套索回归之间的比较就变得很清楚了。

图片来源:作者

如果你喜欢这篇文章,这里有一些你可能喜欢的其他文章:

https://tatev-aslanyan.medium.com/bias-variance-trade-off-in-machine-learning-7f885355e847 https://tatev-aslanyan.medium.com/data-sampling-methods-in-python-a4400628ea1b https://medium.com/analytics-vidhya/pyspark-cheat-sheet-big-data-analytics-161a8e1f6185

感谢阅读

我鼓励你 加入 Medium*以拥有* 完整访问所有跨媒体发布的伟大锁定内容,并在我的 feed 上发布关于各种数据科学、机器学习和人工智能主题的内容。

关注我 阅读更多关于各种数据科学和数据分析主题的文章。更多机器学习的动手应用,数学和统计概念查看我的*Github*账号。
我欢迎反馈,可以联系LinkedIn。****

快乐学习!

有偏模型系数—(第一部分)

原文:https://towardsdatascience.com/biased-model-coefficients-part-1-2722128b9e1c

衰减偏差/回归稀释使您的系数偏向 0

图片由施旭刚

TL;DR —当 X 变量中存在显著噪声或测量误差时,模型系数会低估变量的影响,即如果系数为正(负),真实影响甚至会更大。

关于这个系列

假设您是一名数据科学家,在一家从事房地产业务的公司(代理、开发商等)工作。)有人问你:

"每增加一平方米,房价会上涨多少?"

图片由派克斯提供

这个由多个部分组成的系列致力于展示,如果没有对传统统计学的扎实理解,得到错误的答案是多么容易。

衰减偏差/回归稀释

衰减偏差,也称为回归稀释,是由独立(X)变量中的测量误差或噪声引起的模型系数偏差。你的模型系数变得偏向 0。

例如,假设您有一个回归模型,显示面积每增加一平方米,价格就会增加 2000 美元。如果您的面积要素有很多测量误差或噪声,面积的真实影响甚至更大…也许价格实际上增加了 2500 美元。

反之亦然。例如,如果你的模型显示负系数为-2000,那么真实的系数会更小,比如说-2500。系数偏向 0

请注意,如果您的因变量(y)有噪声或测量误差,这种偏差不会发生…它只会受到 X 变量中的噪声或误差的影响。

为什么会出现这种偏差?

作者图片

假设面积每实际增加 1 平方米,价格就上涨 2000 美元。

当我们对具有带有测量误差的报告“面积”列的数据运行回归模型时,该“面积”列表示实际面积+噪声。

报告的“面积”栏中的波动部分是由于 a)面积的实际波动和 b)噪音。

a)的系数应为 2000,因为实际面积每增加 1 平方米,价格就会增加 2000 美元,而 b)的系数应为 0,因为噪音或测量误差对房价没有影响。该报告的“面积”栏的系数最终介于 0 和 2000 之间,分别是 a)和 b)的系数。

我们将避免可以在网上找到的详细公式,我们将跳转到 python 中的一个例子…

数据准备

1-我们住在一个有 2000 栋房子的城镇里。

2-镇上有 2 个区,一个高级/昂贵区和另一个非高级/普通区。

3-高档区的房屋平均面积较小(平均 200 平方米)。普通区的房屋平均面积为 500 平方米。数据中的房屋面积是从正态分布中提取的。

4-房屋的真实价值= 3000 美元 x 面积(平方米),适用于高级住宅区的房屋。真实价值= 1000 美元 x 非优质区面积。

5-房子在市场上出售,价格=真实价值+/- 20%。+/- 20%的偏差只是噪音。

让我们基于上述假设创建一个包含 2000 所房屋的数据集…

import numpy as np
import scipy.stats as ssnp.random.seed(seed=0)areas_premium = ss.norm(200, 50).rvs(1000)
areas_non_premium = ss.norm(500, 50).rvs(1000)

从上面创建一个包含区域和“溢价”列的数据框架…

“溢价”栏是一个虚拟/二元变量,表示房子是否在溢价区。

df_premium, df_non_premium = pd.DataFrame(), pd.DataFrame()df_premium['area'] = areas_premium
df_premium['premium'] = 1df_non_premium['area'] = areas_non_premium
df_non_premium['premium'] = 0df = pd.concat([df_premium, df_non_premium])
df

作者图片

现在,让我们想象一下区域的分布情况…

import matplotlib.pyplot as plt
import seaborn as snssns.displot(df, x='area', hue='premium', height=5, aspect=16/9)plt.title('Distribution of areas (sqmt) for premium vs non-premium houses')plt.show()

作者图片

我们可以看到高级住宅通常较小。

现在,由于房子的真实价值= 3000 美元 x 高档房的面积或 1000 美元 x 非高档房的面积,我们可以将真实价值的公式改写如下:

真实价值=(1000 美元 x 面积)+(2000 美元 x 面积 x 溢价)

其中 premium 是一个二进制数,表示该房屋是否位于高级位置

记住,房子是以真实价值+/- 20%(随机)出售的。

最后,我们将“真实价值”和“销售价格”添加到我们的数据框架中,并可视化销售价格的分布…

df['true_value'] = (df['area'] * (1000 + (2000 * df['premium'])))# Add selling price = true value x noise
# Noise is a random value between 80% and 120%
np.random.seed(seed=0)
df['sell_price'] = df['true_value'] * (
    np.random.randint(low=80, high=121, size=2000) / 100)# Visualize distributions of selling price
sns.displot(df, x='sell_price', hue='premium', height=5, aspect=16/9)plt.title('Distribution of selling price for premium vs non-premium houses')plt.show()

作者图片

我们可以看到,高级住宅的售价范围更广。

检查线性回归的系数

请注意,由于我们的目标是确定面积和销售价格之间的直接关系,就可解释性和影响测量而言,线性回归是最合适的选择。

我们将使用 statsmodel 库,它为线性回归模型生成一个很好的输出,但是结果也可以使用 sklearn 的 linear regression 实现来重现。衰减偏差影响其他机器学习模型,无论是线性还是非线性。与 OLS 回归相比,线性模型(如随机梯度下降回归器(SGDRegressor ))将显示几乎相同的效果,而衰减偏差的影响对于非线性和基于树的模型更加微妙,这些模型通常需要其他解释库,如 SHAP 或莱姆。

使用 statsmodel 运行 OLS 线性回归,将“真实值”作为我们的因变量(y ),将“面积 X 溢价”作为我们的自变量(X)。

df['area_x_premium'] = df['area'] * df['premium']# Statsmodel implementation
import statsmodel.api as smy = df['true_value']
X = df[['area', 'area_x_premium']]model = sm.OLS(y, X)
results = model.fit().summary()
results

作者图片

请注意,这些系数是按照我们的预期(1000 和 2000)正确估计的。R2 是 100%,这意味着使用我们的两个变量“面积”和“面积 x 溢价”可以 100%准确地预测“真实价值”。

我们使用 sklearn 的 LinearRegression 实现得到相同的系数,使用 SGDRegressor 得到几乎相同的系数。

model = LinearRegression()
model.fit(X, y)
model.coef_

作者图片

model = SGDRegressor(alpha=0, random_state=0, eta0=1e-5)
model.fit(X, y)
model.coef_

作者图片

现在,让我们检查一下,如果我们将 sell_price 而不是 true_value 设置为 y 变量,回归结果(使用 statsmodel)会是什么样子…

y = df['sell_price']model = sm.OLS(y, X)
results = model.fit().summary()
results

作者图片

现在,are 模型的准确率下降到 98.6%,因为 sell_price 包含了一些噪声,房屋的售价可能比其真实价值高出或低于 20%。

估计的系数~1000 和~1995 非常接近我们预期的 1000 和 2000。p 值为 0,表示系数的重要性。最后,我们可以看到这些系数(992–1009)和(1973–2016)的预期范围,因此我们的预期系数 1000 和 2000 落在这些范围内。

如果我们的 y 变量(sell_price)中有很多测量误差/噪声,系数应该仍然是无偏的,接近 1000 和 2000,但是额外的噪声会降低我们模型的预测能力,因此 R2 会下降,2 个系数的范围会更宽。

让我们试着这样做…

# Increase noise on target variable to +/- 50% of true value
np.random.seed(seed=0)
df['sell_price_extreme_noise'] = df['true_value'] * (
    np.random.randint(low=50, high=151, size=2000) / 100)y = df['sell_price_extreme_noise']model = sm.OLS(y, X)
results = model.fit().summary()
results

作者图片

R2 下降到 92.1%,但我们的系数仍然接近 1000 和 2000。正如所料,系数的范围更大。例如,对于第一个自变量(面积),新的范围是 982–1023,而上次运行的范围是 992–1009。

现在,假设测量误差在我们的独立变量中,这样我们数据集中的面积列与实际面积相差+/- 50%。这就是我们偏见产生的原因…

# We'll make area noisy such that it has +/- 50% error
np.random.seed(seed=0)
df['area_noise'] = df['area'] * (
    np.random.randint(low=50, high=151, size=2000) / 100)df['area_x_premium_noise'] = df['area_noise'] * df['premium']y = df['sell_price']
X = df[['area_noise', 'area_x_premium_noise']]model = sm.OLS(y, X)
results = model.fit().summary()
results

作者图片

R2 下降到 90.7%,但最重要的是,我们的系数现在低估了面积的影响!我们现在看到的系数是~917 和~1835。真实的预期系数(1000 和 2000)也超出了报告的范围(897-937 和 1781-1889)。这就是衰减偏差。

结束语

如果我们的目标是预测房价,那么低估面积真实影响的估计系数很可能是预测建模的最佳系数……你不需要做任何调整

由于所报告的面积不准确且有噪声,因此,与我们有真实面积测量值的情况相比,OLS 回归或机器学习模型对这种有噪声的面积测量值给予较小的权重是最佳的。不需要做任何事情来提高模型的预测精度。

问题在于解释系数。当我们被问到

"每增加一平方米,房价会上涨多少?"

如果我们考虑了正确的变量,并且我们知道它们包含高测量误差,那么我们必须记住,我们获得的系数低估了由于面积增加实际 1 平方公吨而导致的价格上涨。

您可以使用 相关性衰减 或其他类似技术来估计“真实系数”。

BIG.art:使用机器学习来创建高分辨率的美术作品

原文:https://towardsdatascience.com/big-art-using-machine-learning-to-create-high-res-fine-art-7dd695f99788

如何使用 GLIDE 和 BSRGAN 创建具有精细细节的超高分辨率数字绘画

来自 BIG.art 的样本结果,图片由作者提供

一年多来,我一直在试验和写作使用 AI/ML 从文本描述中创造艺术。在此期间,我注意到人们对这一领域的兴趣明显增加,部分原因是 NFT 艺术市场的蓬勃发展。

看了几十个生成艺术的 ML 模型,目前我见过最好的是 OpenAI 的 GLIDE。再加上苏黎世联邦理工学院的超分辨率尺寸调整模型 bsr gan[2],我发现结果非常好。

例如,下面是我的两个早期项目的结果,使用 CLIP+SWAGAN 的 MAGnet 和使用 CLIP+VQGAN 的 GANshare One ,与右边新系统的结果进行比较。我使用的提示是“一幅起伏的农田的画”、“一幅带有橙色三角形的抽象画”和“一碗水果的静物画”。

比较 ML 模型的输出,作者的图像

虽然对艺术的评估天生就是主观的,但对我来说很清楚的是,新模型的结果比前两个要好。(不过,我很欣赏橙色三角形的 CLIP/VQGAN 渲染的 3D 外观。)你可以点击每张图片仔细看。

概观

这是我的项目生成高分辨率美术(称为 BIG.art)的高级框图。在对系统进行概述后,我将在下面进一步讨论每个组件的细节。

BIG.art 组件,作者提供的图表

OpenAI 做了大量的工作,他们收集了 2.5 亿个文本-图像对,并训练了两个 GLIDE 模型,一个图像生成器和一个图像上采样器。我将一个文本提示“彩色玻璃瓶的静物画”传入 GLIDE 生成器,它创建了一组 7 个缩略图,每个 64x64 像素。然后,我将生成的缩略图和提示发送到 GLIDE upsampler,它将它们的大小调整为 256x256 像素。甚至上采样图像也非常小。如果你以 300 DPI 的分辨率打印,它的横向和纵向都不到一英寸。以下步骤用于调整所选图像的大小。

在尝试了几个调整大小的系统后,我选定了苏黎世 ETH 的 BSRGAN 超分辨率 resizer 模型。它很好地将所选图像的大小调整了 4 倍,达到了 1024x1024 像素。尽管调整后的图像边缘清晰,但填充区域趋于变平。为了补偿这一点,我为纹理添加了一些过滤噪声。

我选择性地将调整大小和纹理的图像通过德国海德堡大学的图像编码器和解码器 VQGAN。我发现 VQGAN 会发明新的细节,这些细节通常会增强调整后的图像。

最后一步是从 BSRGAN 中再次调整大小 4 倍,并再次通过纹理生成器。结果是一个 4096x4096 的图像,具有清晰的边缘和细节。以 300 DPI 的分辨率打印出来的照片会超过一平方英尺,适合装裱。这是带有一些细节的最终图像。

“彩色玻璃瓶静物画” 的 BIG.art 结果,带有显示细节的选定区域,图片由作者提供

请务必查看下面的附录,查看 BIG.art 的更多结果,并且您可以使用 Colab 在这里创建自己的艺术作品。

组件详细信息

使用 GLIDE 生成图像

2022 年 3 月,OpenAI 发布了一系列名为 GLIDE 的用于图像创建的 AI 模型,这是一种所谓的扩散模型,是生成式对抗性网络(GANs)的替代方案。谷歌研究院的两位工程师解释了扩散模型的工作原理。

扩散模型的工作原理是通过逐步添加高斯噪声来破坏训练数据,慢慢消除数据中的细节,直到它变成纯噪声,然后训练神经网络来逆转这一破坏过程。运行这个反向破坏过程通过逐渐去噪从纯噪声合成数据,直到产生干净的样本。该合成过程可以被解释为一种优化算法,其遵循数据密度的梯度来产生可能的样本。— Jonathan Ho 和 Chitwan Saharia [3]

扩散模型基本上是降噪模型,这些模型已经被训练了很长时间,以至于它们在给定纯噪声作为输入的情况下生成新的图像。

OpenAI 的 GLIDE 基于他们早期对使用扩散模型进行图像合成的研究。他们 2021 年的论文在标题中有一个大胆的声明,扩散模型在图像合成上击败了 GANs,表明以图像类别为条件的扩散模型可以获得比最先进的生成模型更好的图像质量[4]。在他们的最新论文 GLIDE:用文本引导扩散模型实现真实感图像生成和编辑中,作者…

…观察无分类器引导的滑翔能够推广到各种各样的提示。该模型通常生成逼真的阴影和反射,以及高质量的纹理。它还能够制作各种风格的插图,如特定艺术家或绘画的风格,或像素艺术等一般风格的插图

对于 BIG.art,我使用 GLIDE 图像生成器接收文本提示,并生成一系列七幅 64x64 的图像。系统试图描述提示中描述的内容。然后,我将图像和提示输入到 GLIDE upsampler,将分辨率提高到 256x256。该系统被训练成在调整大小时使用提示来帮助添加细节。

例如,GLIDE 从提示“海浪汹涌的海景”中生成以下七幅图像

GLIDE 为“海浪汹涌的海景”生成的图像,作者提供的图像

好吧,那些看起来很不错。这是另一组“波士顿城市地平线”

GLIDE 为“波士顿城市天际线”生成的图片,作者提供的图片

这些看起来有点像波士顿,但不完全是。在任何情况下,我将使用第四个调整大小的讨论如下。

请注意,OpenAI 发布了经过训练的 GLIDE 模型,这些模型无法创建人的图像。作者声明…

…在没有安全措施的情况下发布我们的模型,将会大大降低创建令人信服的虚假信息或深度伪造所需的技能。…为了减轻发布这些模型的潜在有害影响,我们过滤了包含人的训练图像…以降低模型在许多以人为中心的有问题的用例中的能力。亚历克斯·尼科尔等人[1]

用 BSRGAN 调整图像大小

有许多不同的方法使用人工智能来调整图像大小,以获得干净,清晰的结果。这个研究领域被称为超分辨率成像。

我测试了六种不同的图像超分辨率(ISR)尺寸调整模型,发现了两种称为盲尺寸调整网络(BSRNet)和盲尺寸调整生成对抗网络(BSRGAN)的模型,它们对于放大艺术图像非常有效。BSRGAN 模型使用 BSRNet 作为基线,然后使用 GAN 模型进行进一步训练。

来自苏黎世联邦理工学院的论文作者设计了一个实用的深度盲图像超分辨率退化模型,他说。

众所周知,如果假设的退化模型偏离真实图像中的退化模型,单幅图像超分辨率(SISR)方法将不能很好地执行。虽然一些退化模型考虑了其他因素,如模糊,但它们仍然不足以有效地覆盖真实图像的各种退化。为了解决这个问题,本文提出设计一个更复杂但实用的退化模型,该模型由随机混洗模糊、下采样和噪声退化组成。—张开等人

该系统经过训练,可以盲目地发现产生低分辨率(LR)图像的各种退化,从而在重建高分辨率图像时通知 AI 模型。以下是多种 ISR 模型的对比。左边是原始的 LR 图像,右边是用 BSRNet 和 BSRGAN 放大的图像。

BSR 论文中描述的 ISR 方法比较,来源:张开等人

您可以看到 BSRNet 和 BSRGAN 的图像和指标看起来比其他的更好。所示的两个质量度量是峰值信噪比(PSNR),其中越大越好,以及学习感知图像块相似性 LPIPS,其中越小越好。我发现 BSRGAN 通常看起来更锋利,所以这就是我在我的大型艺术项目中使用的。

这是 GLIDE 用双三次插值和 BSRGAN 放大了四倍的“波士顿城市天际线”图像。请注意,您可以点击图片查看详细信息。

使用(左)双三次插值和(右)BSRGAN 调整大小调整 4x 大小的比较,图片由作者提供

你可以看到,用 BSRGAN 调整大小后的图像更清晰、更有活力。然而,它似乎有一个喷枪质量,在光滑的区域缺乏纹理。我将在下一节讨论这个问题。

纹理生成器

为了给图像的平坦部分增加一些趣味,我创建了一个纹理生成器,它通过一个模糊函数运行一个单色随机噪声场。然后将噪声场添加到图像中。这些参数是:

  1. texture_amount -从 0 到 15%的噪波数量。
  2. 纹理大小-从 1 到 9 的噪波“块”的大小

这是原始图像,5%的纹理设置为尺寸 1 和 9。

不同纹理尺寸生成的图像(左)无,(中)尺寸为 1 时 5%,尺寸为 9 时(右)5%,作者提供的图像

我发现添加一点纹理会使生成的和调整大小的艺术作品更具美感。纹理生成器的源代码是这里是

使用 VQGAN 增强细节

当我在尝试各种图像生成技术时,我碰巧通过一个名为矢量量化生成对抗网络(VQGAN)的系统发送了一个用 BSRGAN 调整大小的图像[5]。我在过去的三个 GAN 项目中使用了 VQGAN。我通常使用文本提示和 OpenAI 的剪辑来微调图像,运行 VQGAN 100 到 400 次迭代。

有趣的是,我发现简单地用 VQGAN 编码和解码图像可以改善细节,尤其是用 BSRGAN 放大的图像。

下面是使用 VQGAN 编码/解码前后波士顿城市天际线图像的一个区域的细节。对于这个实验,我关闭了纹理生成器。

原始调整大小图像的细节(左)和通过 VQGAN 编码和解码的图像(右),作者提供的图像

这很微妙,但你可以看到 VQGAN 如何添加一些细节,似乎在右下方完成了一个建筑项目。

这是因为 VQGAN 的设计和训练方式。这是一个混合的 Transformer/GAN 模型,它查看图像的子区域,并将它们编码为之前在训练中看到的区域类型。解码的时候会和邻居一起无缝渲染细节部分。

BSRGAN 模型通过预测高分辨率图像的外观,将 256x256 图像放大到 1024x1024。然后 VQGAN 模型用新发明的细节映射结果图像。你可以在我的 GANshare 文章中看到 VQGAN 的完整报道。

再次使用 BSRGAN 进行最终的大小调整

BIG.art 的最后一步是用 BSRGAN 和另一种纹理处理再放大 4 倍。这是 4096x4096 像素的最终图像。

“波士顿城市地平线”的大艺术效果图,作者图片

你可以点击图像来放大并查看细节。请务必查看下面的附录,查看更多生成的图像。

结果

在试用 BIG.art 之后,我发现有些提示对于生成图像很有效,但有些则不然。

有效的提示

创作抽象画似乎效果不错。以下是一些提示。请注意,您可以在附录中看到生成的图像。

  • “一幅带有彩色圆圈的抽象画”
  • "一幅带有黄色和黑色细线的泼溅画"
  • "一幅带有紫色和绿色方格的拼色画"

风景画看起来也不错。

  • “意大利别墅的风景画”
  • 《湖上日落》
  • “雄伟的雪山”

还有宠物的画。

  • "一幅柯基的画"
  • "一只虎斑猫"
  • “玻璃鱼缸里的金鱼”

不起作用的提示

涉及人的提示(例如,“儿童玩耍”、“巴拉克·奥巴马”、“蒙娜丽莎”等。)不工作是因为 GLIDE 的训练数据故意缺人。

抽象概念的提示(例如,“自由”、“崭新的一天”、“失控”等)。)也不起作用,因为对互联网上使用这些词标记的图像的内容缺乏共识。

这里有一个问题定义了使用 BIG.art 的经验法则:当你在谷歌上搜索这个提示并查看结果图像时,它们看起来大致相似吗?如果是这样,使用该提示生成图像将会生成良好的图像。哦,不要试图渲染人。没用的。

源代码

这个项目的源代码可以在 GitHub 上获得。我在 CC BY-SA 许可下发布源代码。你可以使用这个 Google Colab 创建你自己的图片。

知识共享署名共享

如果您使用此代码创建新图像,请这样注明:此图像由罗伯特·a·贡萨尔维斯使用 BIG.art 创建。

感谢

我要感谢詹尼弗·林和奥利弗·斯特瑞普对本文的帮助。

参考

[1] A. Nichol 等人, GLIDE:使用文本引导扩散模型实现真实感图像生成和编辑 (2022)

[2] K. Zhang 等,BSRGAN,设计一个实用的深度盲图像超分辨率退化模型 (2021),IEEE/CVF 国际计算机视觉会议论文集(ICCV),2021,第 4791–4800 页

[3] J. Ho 和 C. Saharia,使用扩散模型生成高保真图像 (2021)

[4] P. Dhariwal 和 A. Nichol,扩散模型在图像合成上击败 GANs(2021)

[5]p . Esser、R. Rombach 和 B. Ommer 著的 VQGAN,驯服变压器实现高分辨率图像合成 (2020 年)

附录

以下是 BIG.art 针对以下提示的输出示例。这些是我认为七张照片中最好的。

抽象画

一幅带有彩色圆圈的抽象画

一幅带有彩色圆圈的抽象画,作者图片

一幅带有黄色和黑色细线的泼溅画

一幅带有黄色和黑色细线的泼溅画,作者图片

带有紫色和绿色方格的块状彩画

一幅带有紫色和绿色方格的块状彩画,图片由作者提供

风景画

意大利别墅的风景画

意大利别墅风景画,作者图片

湖面上的日落

湖上落日,作者 Imae

雄伟的雪山

雄伟的雪山,作者图片

宠物画

一只柯基犬的画

一幅柯基犬的画,作者图片

一只虎斑猫

一只虎斑猫,图片由作者提供

玻璃碗里的金鱼

玻璃缸里的金鱼,作者图片

奖金提示

以下是我的一位评审员 Oliver 建议的各种提示的一些效果图。

木材车间

一个木材作坊,图片作者

微生物

微生物,图片作者

塞车

交通堵塞,图片作者

为了无限制地访问 Medium 上的所有文章,成为会员,每月支付 5 美元。非会员每月只能看三个锁定的故事。

谷歌云平台中的大数据基础

原文:https://towardsdatascience.com/big-data-fundamentals-in-google-cloud-platform-3a2bcafa267

云计算|大数据|技术

第 2 部分——通往谷歌云专业数据工程师之路

Pawel Czerwinski 在 Unsplash 上的照片

欢迎来到 GCP 专业数据工程师认证系列的第二部分。在第一部分,我们介绍了谷歌的云平台及其层次结构。您可以在这里找到第 1 部分:

在这一部分,我们将讨论大数据技术和机器学习方面的服务和 GCP 的产品。

使用云 SQL 和 Spark 的产品推荐

产品推荐可能是现代企业最常见的 ML 应用之一。

这个用例的想法是将现有的推荐系统从内部迁移到云中。

当迁移到云时,我们将从专用存储迁移到集群外云存储。

ML 任务的核心部分是数据、模型和基础设施,用于训练和向用户提供预测。

作为一个用例,让我们选择开发出租房屋推荐系统的任务。

说到基础设施,首先,我们需要决定我们希望多长时间发布一次预测。

所以第一个决定是,我们的 ML 应用程序应该处理流数据还是批处理?

在我们的用例中,我们不需要不断地向用户推荐房屋,而是可以每天预加载结果,并在用户在线时提供给他们。因此,在这种情况下,批处理就可以了。

另一方面,根据我们拥有的房屋和用户的数量,我们还需要考虑计算资源。当我们处理大型数据集时,我们需要以容错的方式执行这种处理。这意味着,理想情况下,我们在一组机器上运行我们的过程,而不是在一台机器上。

容错分布式进程框架的一个例子是 Apache Hadoop。该过程将类似于:

  • 每一天,每一个用户,根据他们以前的评分预测每个房子的分数/评分
  • 存储这些预测评级
  • 用户登录后,查询前 N 名结果(基于预测分数)并显示给用户

因此,我们需要一种事务方式来存储预测。它需要是事务性的,这样我们就可以在用户阅读时更新表格。

GCP 提供多种交易解决方案。当然,考虑到不同的需求,我们必须使用不同的服务。下面,我们总结了一些 GCP 服务的属性。

谷歌服务及其访问模式。谷歌云平台

GCP 存储流程图。图片作者。

对于我们的示例用例,云 SQL 是最好的服务。

云 SQL 是一个完全托管的 RDBMS。使用静态 IP,我们还可以从任何地方连接到我们的云 SQL 实例。

我们还需要一个管理数据处理管道的服务。

我们需要一种服务来处理我们的数据批次和数据流,并训练机器学习模型。

这种软件的一个很好的例子是 Apache Spark 和它的机器学习包(Apache SparkML)。查看我的另一个博客,在创纪录的时间内运行 Spark 作业,而不需要任何基础设施开销。

你喜欢这篇文章吗?如果是,请考虑订阅我的电子邮件列表,以便在我发布新内容时得到通知。

https://david-farrugia.medium.com/subscribe

此外,考虑成为会员,使用我下面的推荐链接来支持我和你在 Medium 上喜欢的其他作家。每月 5 美元,你就可以无限制地阅读 Medium 上的每一篇文章。

https://david-farrugia.medium.com/membership

想给我买杯咖啡吗?

https://paypal.me/itsdavidfarrugia?country.x=MT&locale.x=en_US

想联系吗?

我很想听听你对这个话题的想法,或者其他什么。如果你想联系我,请发邮件到 davidfarrugia53@gmail.com给我。

Linkedin——Twitter

小字里的大数据

原文:https://towardsdatascience.com/big-data-in-little-wordle-306d5502c4d9

对约 7000 万场世界扑克比赛的大规模数据分析

尼尔斯·胡内尔弗斯特在 Unsplash 上的照片

最近,我一直在探索流行的文字游戏 Wordle[62–67]的各个方面,这款游戏在 2022 年 1 月和 2 月风靡了互联网。当我开始欣赏被问到的许多数据科学问题时,我决定使用 Wordle 作为我大学三年级的一个案例研究项目,这是我每年春天在都柏林大学教授的数据科学实践课程。该课程是一门基于实践的课程,要求小组学生设计、开发和展示他们自己的数据科学项目。为了让他们开始,我总是提出一个新的案例研究,作为对他们的期望的例子,下面是今年 Wordle 案例研究的总结。我将描述使用的数据集和回答的研究问题。而且,虽然我之前已经在博客上写了以下内容的一些方面,但这里呈现的结果代表了我迄今为止进行的最详细的分析。

TL;速度三角形定位法(dead reckoning)

这是一篇又长又详细的帖子,所以这里是一些关键的结果:

  • 我们描述了 Wordle 数据的两个重要来源的发展:(I)由 Wordle 模拟器生成的超过 5300 万个游戏的数据集;以及(ii)Twitter 上发布的超过 1500 万个真实世界游戏的数据集。
  • ( RQ1 )我们比较了模拟数据集和 Twitter 数据集,发现它们在几个重要方面有很强的对应性。
  • ( RQ2 )一项对 Twitter 游戏的分析表明,Wordle 在 1 月底/2 月初在 Twitter 上达到顶峰。
  • ( RQ3 )一项对模拟游戏的分析显示了一些开始词如何比其他的更有效。Twitter 数据集告诉我们,大约 17%的玩家使用了糟糕的开始词,这可能会对这些玩家的表现产生负面影响。
  • ( RQ4 )另一项对模拟游戏的分析显示,一些目标词比其他的更具挑战性,导致游戏时间更长,成功的游戏更少。Twitter 的数据也证实了这一点,但没有证据表明 Wordle 会像一些人猜测的那样变得越来越难。
  • ( RQ5 )玩好 Wordle 的能力取决于玩家选择尽可能满足目前所学约束的猜测。对模拟游戏的分析表明,正确的字母正确的位置约束是最重要的一致遵守。

一点点单词

Wordle 是一款简单却引人入胜的在线猜词游戏。每天都会选择一个新的秘密目标单词,玩家有一次机会参与游戏。在每次猜测之后,向玩家提供颜色编码的反馈(见图 1),以指示:(I)目标单词中包含哪些字母(如果有的话)以及它们的正确位置(绿色);(ii)哪些字母在目标单词中但不在它们的正确位置(黄色);以及(iii)目标中缺少哪些字母(灰色)。这样,随着每一个新的猜测,玩家可以学到更多关于目标单词及其字母的知识。

图 1:一个示例 Wordle 游戏和每次猜测后提供的提示。请注意,即使玩家第一次猜对了“L”(及其位置),Wordle 也不会帮助他们识别“L”是目标单词中的重复字母。这就是为什么“KNOLL”在 2022 年 1 月被证明是最难找到的单词之一,以及它很少被使用和“KN”的使用不寻常的事实。图片作者。

游戏可以在 Twitter 上共享,而不会破坏其他人的游戏,如图 2 所示。通过使用 Wordle 的反馈而不是猜测本身,每个游戏都作为实际游戏的一种格式塔来共享。这为玩家提供了足够的信息来展示他们的表现,但不会泄露游戏。

图 Twitter 上分享的一些 Wordle 游戏。图片作者。

是什么让 Wordle 成为一个好的数据科学话题?

选择一个合适的数据科学主题通常可以归结为至少两个重要问题:(1)是否有一组有趣的研究问题要问,( 2)是否存在可以回答这些问题的数据?就 Wordle 而言,即使粗略地浏览一下关于这款游戏的大量文章和视频[1,2,5,7,13,27,39,41,62–68],也能清楚地看到这款游戏是有趣问题的丰富来源,从“游戏的平均长度是多少?最好的起始词是什么? " to " 是不是有些目标词比其他的更难?最佳猜测策略是什么? 忽略一些暗示可以吗?

虽然不缺乏研究问题,但获得数据来回答这些问题是一个更具挑战性的命题。Twitter 是一个数据来源,虽然不完整,但在回答至少一些研究问题时应该是有用的(例如,平均游戏长度,目标词难度)。如果可以找到一种合理的方法来模拟现实的游戏,那么也有可能产生足够现实的合成数据集,为单独使用 Twitter 数据集无法回答的研究问题提供答案。事实上,也应该有可能使用来自 Twitter 的 Wordle 数据来评估合成/模拟数据与真实游戏相符的程度。

因此,对 Wordle 的分析似乎是一个强有力的数据科学项目候选。它很受欢迎。对于人们询问的关于他们的游戏和其他人的游戏的有趣问题的答案有着强烈的需求。应该有可能使用合成和真实世界游戏数据的组合来充分详细地探索这些问题——适当地定义。

相关著作

为了更好地理解人类(甚至动物)的表现[23,24,25,32,69,72],学习和技能习得[18,23,31,35,47,51],创造力[11,33,36,48]和决策制定[3,16,20,26,34,58,71],游戏在计算机科学研究[70]中占据了突出的地位。事实上,人工智能领域长期以来一直受到复制各种形式的人类认知和学习的努力的影响,游戏和谜题经常在这些努力中发挥重要作用[21,49,50,55,57,61]。

因果游戏和谜题也被证明是人工智能、机器学习、认知科学和创造力研究的沃土[8,14,43]。例如,近年来流行的数字游戏 Sudoku 吸引了机器学习、基于约束的计算和运筹学社区的大量关注[30,42,56]。当然,文字游戏也很受欢迎。基于人工智能的纵横字谜解算器和生成器继续在当代人工智能研究中占据显著地位[9]。19,37,38,45,46,53,54]拼字游戏仍然是一个受欢迎的人工智能目标,其额外的优势是提供了一个在多玩家环境中考虑对抗性游戏的机会[6,29,52,59]。

所有这些都是说,游戏和谜题一直吸引着研究人员的注意力,尤其是在计算机和认知科学领域。游戏和谜题之所以如此有趣,是因为它们为研究人员提供了进入因果关系受限宇宙的途径,让他们放弃了现实世界的混乱。他们经常被称为玩具问题,有时带有贬义,但这完全没有抓住是什么让游戏如此有趣和吸引人的关键。是的,他们确实存在于一个简化的游戏世界中,但是假设这限制了他们的复杂性是不正确的。通常,最持久的游戏是那些规则集足够简单但又不过分简单的游戏,以保留现实世界中存在的大部分复杂性。国际象棋和围棋的规则比人类冲突的现实要简单得多,人类冲突是它们起源故事的一部分,然而这些游戏提供了许多现实世界设置中存在的所有决策和战略复杂性,如果不是更多的话。所以毫不奇怪,一旦 Wordle 流行起来,它也会吸引数学家、计算机科学家、语言学家,甚至古怪的物理学家的注意。公平地说,值得指出的是,尽管如此,Wordle 是一个比更开放的文字游戏(如拼字游戏)更简单的游戏,但它足够复杂,可以成为一个有趣的研究目标。

虽然公众和媒体对 Wordle 一直很感兴趣,但它迄今为止有限的寿命意味着任何“严肃的”研究还没有时间接受同行评审,这是科学界接受的传统障碍。然而,至少有三种与单词相关的新兴研究——(I)如何选择好的/最佳的种子单词;(二)如何玩/猜得好/最优;以及(iii)理解 Wordle 的复杂性——我们将在下面进行总结。

早期对 Wordle 的兴趣大多来自于用来开始新游戏的最佳种子/起始词的问题,这导致媒体根据常见的语言试探法(如元音的存在等)对好词的大量猜测。[17]的工作更进了一步,尝试使用字母频率信息来识别三个最佳起始单词。简而言之,使用贪婪算法来识别具有最高频率得分的单词,同时考虑由与其他高得分单词的字母重叠引起的冗余。作者选定了 RAISECLOUTNYMPH ,因为它们包含频繁出现的互斥字母。[22]的工作采用了一种不同的方法来识别好的开始单词,这一次是通过建立一个模拟器,并允许它使用每个可能的开始单词与每个可能的目标单词进行游戏(一种类似的方法在后面的工作中使用),以识别具有最短平均游戏长度的开始单词。再一次加注出现在最上面。本报告作者的早期工作[67]也分析了模拟游戏,通过将任务视为集合覆盖问题[15]来寻找最佳起始单词,其目的是找到保证与每个可能的目标单词重叠至少一个字母的最小猜测单词集合。使用集合覆盖和模拟的组合导致了 TALES 作为最佳单个单词, TRIALCONES 作为最佳单词对,而讨厌ROUNDCLIMB 作为最佳的三个单词集合用作起始单词;然而应该注意的是,这些组合是使用独立的单词列表而不是官方的单词列表找到的。

当谈到玩 Wordle 时,[10]的工作提出了一种数学游戏方法,该方法使用潜在语义索引来选择下一个最佳猜测。它给出的结果表明,在初始猜测为石板的情况下,该方法平均在 4.04 次猜测中解决难题,成功率为 98.7%。但是就像许多关于 Wordle 的技术讨论一样——参见[60]的工作——这种方法依赖于复杂的数据和技术能力,而这些对于普通玩家来说是不可能的。简而言之,人类玩家不携带字母频率或信息增益和词熵的详细信息,许多技术依赖这些信息来选择最佳的下一次猜测。考虑到这一点,[4]的工作着眼于选择起始词的两种不同方法,以及基于强化学习发现最佳人类策略的框架,从而产生了一组简单的规则,作者声称这些规则可供人类玩家使用。

最后,为了跟进 Wordle 是比其他文字游戏更简单的游戏这一观点,值得注意的是[40]的工作,该工作认为,尽管其表面上简单,但 Wordle 编码了困难(在数学/复杂性意义上)的问题。作者认为,游戏的约束并没有通过证明(未经同行评审验证)它在自然环境中是 NP-hard 来限制其复杂性。对于那些对 Wordle 研究的创造性边缘案例感兴趣的人来说,[28]的工作也值得一提,并作为最勇敢的读者的练习。

研究问题

对于这里介绍的工作,我们将使用两个 Wordle 数据来源:由模拟器生成的模拟数据和 Twitter 上共享的真实世界游戏数据。这两个数据集将在后面详细描述,我们将使用它们来回答以下研究问题。

  • RQ1:模拟数据是否接近真实游戏? 对于来自我们模拟器的数据是否是真实游戏数据的合理代理,形成一个观点是很重要的。为此,我们将使用几种不同的游戏性指标来比较两个数据集的几个方面。
  • RQ2:Wordle 到底有多火? 我们可以使用 Twitter 数据集来评估年初以来 Wordle 的受欢迎程度,其依据是更多人在 Twitter 上分享意味着更多人玩 Wordle。这不会提供 Wordle 受欢迎程度的完整图片,因为只有一小部分玩家会在 Twitter 上分享他们的游戏,那些分享的玩家可能会对他们分享的游戏有所选择,但它应该会提供一些关于游戏受欢迎程度以及它如何变化的合理见解。
  • RQ3:起始字的选择重要吗? 这是 Wordle 玩家最初问的问题之一。数据告诉我们什么?是否有证据表明,一些起始词比其他起始词导致更长或更短的游戏或更多成功/不成功的游戏?坚持一个好的开头词值得吗?
  • RQ4:是不是有些目标词比其他的更有挑战性? 如何评价一个目标词的难度?迄今为止最难的单词是什么?为什么这些单词比较难?游戏的难度随着时间的推移有变化吗?
  • RQ5:Wordle 的反馈有多重要? 对于玩家来说,选择满足到目前为止所学的关于目标词的所有提示/约束的猜测是否重要?如果玩家需要做出妥协,那么哪些约束比其他约束更重要或更不重要?

Wordle 模拟器

这项研究的一个关键因素是一个现实的 Wordle 模拟器的可用性,这样我们就可以大规模地生成样本游戏。与一些相关的工作[1,2]不同,我们的模拟器不是为玩一个最优的 Wordle 游戏而设计的。相反,我们更感兴趣的是开发一个模拟器,可以代表不同的游戏风格和玩家能力,类似于我们可以从现实世界的玩家那里期待的那些。在接下来的内容中,我们将总结模拟器初始版本中采用的基本方法,然后描述如何对其进行调整以模拟更大范围的更真实的游戏。

我们开发的模拟器将一个目标单词和一个单词列表作为输入,用作有效的猜测。我们还包括这些词在常用中的流行程度信息,作为对候选猜测进行排序的一种方式。下面的概述算法展示了如何通过一系列猜测来模拟每个游戏,直到找到目标单词。

在每一轮中,提示/反馈用于更新关于目标单词的四组不同的知识:

  1. 正确字母 —猜测中的字母也在目标中,但不在正确的目标位置。
  2. 正确位置 —猜测中的字母在目标中的正确位置。
  3. 错误字母 —猜测中不在目标中的字母。
  4. 错误位置 —正确字母的错误位置。

这种知识可以用来约束每个新猜测的选择,并且在模拟器的初始版本中,从满足尽可能多的当前约束的可用猜测字中选择一个新猜测( pick_guess) 。通常,有几个兼容的猜测,模拟器选择了最流行/最常见的一个——使用一个单独的开源单词流行度数据数据集——来模拟玩家选择最先想到的单词。

一个更新的模拟器算法(如上所示)也包含了一个玩家词汇完整性的简单模型。基本思想是,具有更完整词汇的玩家应该更有可能能够识别满足当前约束的单词,因此在他们玩游戏期间不太可能需要牺牲约束。相比之下,词汇量不太发达的玩家将很难识别出满足所有游戏限制的猜测,因为这些单词不在他们的词汇表中。相反,这些玩家更有可能牺牲约束,选择他们更熟悉的次优猜测;这样的话仍然可以帮助游戏向前发展,只是不是以一种非常有效的方式。这个词汇表模型是使用参数 v ( 0 < v ≤ 1 )来实现的,以指示玩家词汇表中可用猜测单词的比例(按流行度排序)。因此,如果 v = 0.9 ,那么最流行的猜测单词的前 90%在玩家的词汇中,而 v = 0.5 意味着玩家的词汇仅包括前 50%的单词。

当选择新的猜测单词时,如果存在满足所有约束条件的单词,这些单词在玩家的词汇表中,则选择最流行的一个。然而,如果不存在这样的词汇内单词,那么模拟器改为选择满足至少两个约束的最流行的非词汇单词。通过这种方式,我们可以预计词汇不完整的玩家更有可能需要在游戏后期进行次优猜测,因为他们的词汇中没有一个词满足不断增长的可用约束条件。

图 3 显示了模拟器制作的两个游戏示例,使用相同的开始和目标单词,但是词汇设置不同。(a)中的较低词汇表设置导致对目标单词的搜索时间长得多,因为在早期阶段牺牲了约束。例如,单词的第三个猜测这些忽略了正确字母约束,因为这个猜测比前一个猜测具有更少的正确字母。

图 3:模拟器使用不同词汇设置玩的两个示例游戏。在(a)中,模拟器使用 v = 0.6,这意味着它的词汇只有 60%完成,因此,它倾向于在早期牺牲约束,导致更长的游戏。在(b)中,模拟器使用 v = 0.9,所以它的词汇表几乎是完整的,这意味着约束不太可能被牺牲。图片作者。

数据和方法

在这项研究中,我们将使用作者创建的两组不同的 Wordle 游戏数据:(I)从模拟器生成的大量模拟游戏中产生的数据集;以及(ii)在 Twitter 的许可下,根据其搜索 API 的学术许可条款,从与 Wordle 相关的推文中收集的真实世界游戏数据集。在适当的时候,我们计划在知识共享许可下发布这两个数据集,并将在可用时添加到这些数据集的链接。

为了生成模拟数据集,模拟器玩了一些游戏,其中来自 Wordle 的目标单词列表的 2309 个单词中的每一个都被用作单独的目标。此外,我们还将模拟器配置为使用每个目标单词作为可能的起始单词。这意味着一个完整的模拟回合由 5,331,482 (2,309 x 2,309)个个人游戏组成。

接下来,我们针对 10 种不同的词汇设置( 0.1 ≤ v ≤ 1 以 0.1 为增量)完成单独的模拟回合。这意味着我们完整的模拟数据集由 53,314,820 个单独的游戏组成,对于每个游戏,我们记录以下数据:

  1. 目标词:使用的目标词。
  2. 起始字:使用的起始字。
  3. v:使用的词汇设置。
  4. 回合:完成游戏所需回合数。
  5. 猜测值:使用的猜测值。
  6. 提示:每轮游戏结束后提供的一组提示。每个提示是一个由 5 个符号组成的字符串( *** 或 +X 分别对应 Wordle 的绿色、黄色和灰色反馈)。
  7. 约束:为每一轮选择猜测时满足的约束数组。

结果数据集表示为包含 53,314,820 行的单个表。每行对应一个游戏,上面的每一行都是它的列。在这个数据集中,有 329,401,297 个单独的回合、猜测和提示。表 1 显示了该数据的一个示例。

表 1:来自模拟数据集的 30 行的样本,示出了包括目标和开始单词、词汇设置(v)、游戏回合数、每回合的猜测以及每回合接收的提示的列的子集(“*”是绿色正方形;+'是黄色方块;x 是灰色正方形)。出于清晰和空间的原因,每个游戏所使用的约束已经被省略。

Twitter 上的 Wordle 游戏与“ Wordle n r/6 ”形式的签名一起共享,其中 n 是当前游戏的号码, r 是当前游戏的回合数。为了构建 Twitter 数据集,我们使用 Twitter 搜索 API 来查找所有包含单词“Wordle ”,后跟游戏有效数字和使用适当正则表达式的有效“方块”序列的推文。总共收集了 15,373,887 个有效游戏(1,679,660 个独立玩家),包括关于玩家 id、游戏 id、推文时间、日期、位置、语言和包含游戏的推文文本的数据,以及发布的单元格网格。表 2 显示了该数据的示例,该数据集总共包含 62,986,524 轮游戏(跨 15,373,887 场游戏)。

表 2:来自 Twitter 数据集的 30 行样本,显示了包括玩家和游戏 id、目标单词、游戏编号、每场游戏的回合数、游戏日期和游戏回合的翻译字符串的列的子集(' * '是绿色方块;+'是黄色方块;x 是灰色正方形)。玩家和游戏 id 是 Twitter 提供的作者和 tweet ids 的匿名版本。

RQ1:比较模拟数据集和 Twitter 数据集

我们在这一部分开始分析,通过比较模拟数据集和 Twitter 数据集来确定前者是否提供了现实世界游戏的真实描述。鉴于模拟数据集包含各种词汇设置的游戏——其中一些可能无法代表真实的游戏——我们将从比较 Twitter 游戏和基于几个词汇设置的模拟游戏集开始。图 4 显示了给定长度游戏的累积分数的结果。例如,我们可以看到,大约 25%的游戏长度达到并包括 3 轮,尽管只有大约 10%的游戏具有最低的词汇设置。不出所料,随着 v 的增加,给定长度内游戏的累积分数也会增加;例如, v = 0.1 只有不到 40%的游戏在 6 轮或更少的回合中完成,但是 v = 0.9 几乎 90%的游戏都是成功的。

图 4:给定长度的模拟和 Twitter 游戏的累积部分。基于词汇设置对模拟游戏进行分组;v = 0.1,0.5,0.7,0.8,0.9,1。图片作者。

图 4 还显示了 Twitter 数据集的相应累积游戏分数(带圆形标记的黑线),应该清楚的是,这些游戏最接近于模拟游戏,其中 v = 0.9v = 1.0;与 Twitter 相比,前者高估了较短游戏的比例,后者倾向于低估较短游戏的比例。这表明将 Twitter 游戏与模拟游戏的子集进行比较对于 v ≥ 0.9 是合适的。

事实上,图 5 显示了 Twitter 和模拟器的给定长度游戏的分数( v ≥ 0.9 ),作为(a)每个游戏长度的实际游戏分数和(b)游戏长度增加的累积游戏分数。它展示了 Twitter 和模拟( v ≥ 0.9 )数据集之间不完美但紧密的对应关系。前者的平均比赛时间为 4.09 回合,而后者为 4.01 回合;模拟器产生更多的 4 轮游戏,但更少的 6 轮游戏,但除此之外,两个数据集之间有非常强的对应关系。

这意味着我们可以有理由相信使用模拟数据集(v≥0.9)作为真实世界游戏的合理近似。虽然这里没有显示,但当我们进一步将模拟数据集限制为仅使用与 Twitter 数据集相同的目标词的那些游戏时,我们在模拟数据集和 Twitter 数据集之间获得了更紧密的对应关系。在这种情况下,两个数据集的平均游戏长度为 4.09 轮,比较平均游戏长度的 t 检验表明两个数据集之间没有显著差异(t = 0.406,p = 0.684* )。*

图 5:比较了(A)游戏(模拟和 Twitter)的游戏时长和(b)游戏(模拟和 Twitter)的累积游戏时长。注意,模拟游戏用 v ≥ 0.9。图片作者。

RQ2:Wordle 的流行程度

在我们更详细地探索游戏的本质之前,让我们通过使用 Twitter 数据集来检查随着时间的推移在 Twitter 上发布 Wordle 游戏的独特每日玩家(新玩家和重复玩家)的数量,来考虑 Wordle 的增长和受欢迎程度。

为此,我们根据数据集中第一次出现的给定玩家 id(按日期排列)定义一个新的玩家,并将循环/重复玩家定义为在较早日期出现的玩家 id。然后,我们将 Twitter 数据按照日期和游戏号码的组合进行分组,并计算每个日期新玩家和重复玩家的出现次数。此外,对于每个日期,我们还计算重复出现的玩家发布的游戏的平均数量。

结果在图 6 中显示为每个日期的新玩家和回归玩家数量的堆积条形图,以及回归玩家发布的平均游戏数量的单独线图。它显示了 Wordle 在 2022 年 1 月期间的快速增长。这个月开始时,每天约有 10,000 名独立玩家在 Twitter 上发帖,但到了 1 月底,独立玩家总数迅速上升至近 250,000 人的峰值。自 2 月以来,在 Twitter 上发布游戏的独立玩家数量一直在下降,在撰写本文时,每天的独立玩家数量已降至约 10 万人;现在每天也只有不到 4000 名新玩家,低于每天约 50000 名新玩家的峰值。

图 Wordle 在 Twitter 上的受欢迎程度,以日新增用户数和日返回用户数表示(左侧 y 轴)。还显示了《纽约时报》收购 Wordle 的日期和 NYT“重新推出”Wordle 的日期。还显示了每个返回玩家的平均游戏次数(右侧 y 轴)。图片作者。

这表明我们已经度过了巅峰时期,至少在 Twitter 上是这样,但这并不意味着 Wordle 的受欢迎程度正在下降。例如,大多数玩家继续私下玩是完全合理的。此外,《纽约时报》2 月份重新推出的 Wordle 肯定吸引了许多新玩家,即使他们不愿意在 Twitter 上分享他们的努力。

RQ3:起始词的选择很重要吗?

在 Wordle 中,每局游戏开始时你都是独立的。没有提示可以指导你,所以大多数玩家试图使用一些明智的策略来选择一个可能与目标共享一些字母的单词。例如,有很多不同元音的单词通常是一个很好的选择,因为几乎所有的英语单词都至少有一个元音。带有普通辅音的单词,如“s”或“t”也是一个好主意,所以像凝视审判这样的起始单词被证明是有效和受欢迎的。但这重要吗?有没有一小群客观上比别人强的潜在起始词?如果是,它们是什么,它们带来了多大的优势。

为了确定最佳起始词,我们将使用为 v ≥ 0.9 的所有游戏生成的模拟数据,因为这种词汇设置提供了与 Twitter(真实世界)数据的最佳拟合。在模拟数据中,我们已经为起始单词和目标单词的每种组合生成了游戏,这意味着我们使用 2,309 个可能的起始单词中的每一个和 2,309 个可能的目标单词中的每一个来玩游戏,并且对于两种词汇设置,总共产生 10,662,962 个游戏(G)(2,309 x 2,309 x 2)。

有几种不同的方法,我们可以评估一个开始字的性能。例如,一个显而易见的选项是,当使用游戏的每个开始单词时,计算它的平均游戏长度,如等式 1 和 2 所示,其中回合(g) 表示游戏中的回合数 g 。另一种替代方法是,在给定起始单词的情况下,计算获胜游戏的分数,即在 6 轮或更少轮中完成的游戏,如等式 3 所示;换句话说,一个好的开始词会产生更大比例的获胜游戏。还有一个选项是考虑短游戏(3 轮或更少轮的游戏)的部分和长游戏(需要 5 或 6 轮的游戏)的部分,如等式 4 和 5 所示。

对于这种分析,我们为 v ≥ 0.9 的模拟数据集的子集中的 2,309 个可能的起始词中的每一个计算上述度量。接下来,我们制作如图 7 所示的散点图,其中每个点对应于一个特定的起始单词,并根据其短游戏的比例(x 轴)和长游戏的比例(y 轴)位于图上。此外,每个点的直径与相应开始单词的获胜分数成比例,最后,每个点根据其平均游戏长度进行颜色编码,如所示的色标所示。这样,我们可以在一个二维散点图上呈现所有四个性能指标。

结果表明,开始词的选择很重要,因为我们可以看到短游戏和长游戏的比例以及平均游戏长度有相当大的变化。胜率的变化要小得多,平均为 93%,并且总是高于 89%。例如,右下象限中的单词与短游戏的高于平均分数和长游戏的低于平均分数相关联;短赛次数最高的词是 LEANT ,也有 93%的胜率,平均游戏时长 4.3 回合。相比之下,左上象限用于短游戏较少而长游戏较多的起始词; BOBBY 在这方面脱颖而出,因为它的长游戏数量最多,短游戏最少,胜率仅为 90%,平均游戏时长为 5 轮。如果你对优化胜率更感兴趣,那么最好的开始词可能是毛绒,因为它在所有开始词中胜率最高(94%),但这是以更长的平均游戏长度(4.4 轮)、更少的短游戏和更多的长游戏为代价的。

图 7:模拟游戏中起始词的平均游戏长度分析(v ≥ 0.9)。每个标记对应于模拟器为给定起始单词产生的游戏,并且根据为给定起始单词产生的短游戏部分(x 轴)和长游戏部分(y 轴)来定位。每个标记基于相应开始单词的平均游戏长度进行颜色编码,并基于其相应的成功游戏分数来确定大小。水平虚线和垂直虚线分别表示长游戏和短游戏的平均分数。图片作者。

图 7 还包括媒体中经常提出的一些更常见的起始词(例如 SLANT、SLATE、CRATE、TRACE )。他们在这一分析中也做得很好,尽管不如 T2·利恩特做得好。

好的开始的好处和坏的开始的代价相比如何?要了解这一点,我们需要定义什么是好的开始或坏的开始。在 Twitter 数据集中,第二轮(我们认为是游戏的开始)后正确提示的中位数(绿色加黄色)仅为 2——顺便提一下,模拟数据集也是如此——因此我们可以说,根据等式 6 和 7,如果正确提示严格多于中位数,游戏就有一个好的开始,如果正确提示严格少于中位数,游戏就有一个坏的开始。

在我们的 Twitter 数据集中,几乎 17%的游戏开局不利,相比之下,48%的游戏开局良好;开局好的平均比赛时间是 4 轮,而开局不好的是 4.6 轮。因此,开局不利的成本大约是一轮的 0.6 倍。还不错,但这是全部情况吗?再深入一点,在图 8(a)中,我们看到了好的和坏的开始的游戏的累积分数,与平均游戏长度的简单比较相比,我们可以看到更多的实质性差异。例如,我们现在可以看到好的开始比坏的开始(大约 9%)产生更多的短游戏(大约 37%);换句话说,良好的开局比糟糕的开局多 3 倍多的短比赛(长度≤ 3 轮),如图 8(b)所示。因此,开局不利的真正代价主要体现在短距离比赛数量的大幅减少上。

图 8:基于对 Twitter 数据集中游戏的分析,一个好的开始的好处和一个坏的开始的代价。在(a)中,显示了具有良好开局的游戏与具有糟糕开局的游戏在给定游戏长度下的累积分数。在(b)中,条形图显示了在给定的游戏时间长度内,好的开始与坏的开始的相对比例。图片作者。

对模拟数据集执行相同的分析( v > 0.9 )会产生大致相似的结果,如图 9 所示。

图 9:基于对 Twitter 数据集中游戏的分析,一个好的开始的好处和一个坏的开始的代价。在(a)中,显示了具有良好开局的游戏与具有糟糕开局的游戏在给定游戏长度下的累积分数。在(b)中,条形图显示了在给定的游戏时间长度内,好的开始与坏的开始的相对比例。图片作者。

RQ4:为什么有些目标词比其他的更有挑战性?

在探索了最佳起始单词的问题之后,很自然地将我们的注意力转向目标单词。是不是有些目标词比其他的更有挑战性?如果是,那是为什么?自从《纽约时报》接手以来,沃尔多是否像一些人声称的那样变得更加困难了?我们可以定义一个目标词的难度,类似于我们评估一个起始词的好坏。根据等式 8-12,如果目标单词与较长的游戏、较低的胜率、较短游戏的较小部分或较长游戏的较大部分相关联,则该单词是困难的。

然后,使用模拟数据集(具有 v ≥ 0.9 )我们可以为每个可能的目标词计算这些指标,并且,如同起始词的情况一样,我们可以产生图 10 所示的散点图来提供我们的发现的总结。与图 7 相比,这一次我们看到了一个稍微不同的开始词模式。虽然有些单词比其他单词更难(左上象限),有些单词挑战性要小得多(右下象限),但多空游戏之间的关系本质上并不是线性的。像 JAUNTMAMMY 这样的词通常与漫长的游戏和极低的胜率联系在一起。相比之下,更容易的目标词,如世界,它们有 100%的胜率,并产生非常短的游戏。

图 10:模拟游戏目标词的平均游戏长度分析(v ≥ 0.9)。每个标记对应于模拟器为给定目标单词产生的游戏,并且根据为给定目标单词产生的短游戏部分(x 轴)和长游戏部分(y 轴)来定位。每个标记基于相应目标单词的平均游戏长度进一步进行颜色编码,并基于所产生的成功游戏的相应部分来确定大小。水平虚线和垂直虚线分别表示长游戏和短游戏的平均分数。图片作者。

为了进一步探索目标词的难度,通过 Twitter 数据集,我们重点关注短游戏和长游戏的比例。例如,图 11 显示了 Twitter 数据集中最近 100 个目标词的每个目标词的短期和长期游戏的比例(以条形表示);每个目标单词的平均游戏长度也显示为线形图。我们可以看到,大多数目标单词与短游戏的关联多于长游戏,但对于一些单词来说,情况正好相反。比如大约 50%的游戏是短的,而长游戏只占游戏的 10%左右。对比一下的泔水代理,它们的长游戏比短游戏多。这表明对玩家来说没有代理有挑战性,顺便提一下,这与使用模拟数据集评估单词难度的结果一致,因为代理位于图 10 的左上角象限,而位于右下角象限;我们再一次发现模拟器和现实世界游戏之间有很强的对应关系。

图 Twitter 数据集中 100 个最近的目标词的长短游戏比例;还显示了每个目标单词的平均游戏长度。图片作者。

我们将一个目标词的难度比定义为其长短局之比;参见等式 13。因此,难度比> 0 表示具有挑战性的单词——长游戏比短游戏多——而难度比< 0 表示更容易的单词,短游戏比长游戏多。比如像这样的易词,难度比为-0.927(以0.037/0.5211为准),而像will这样难度更高的词,难度比为 1.98(. 301/. 101)。

图 12 显示了 Twitter 数据集中所有目标词的难度比;难词(难度比> 0 )显示为红色条,较容易的词显示为绿色条。这澄清了在数据集的那个时期,有一些单词变得更加困难。到目前为止,最难的单词是will,这也被认为是模拟器中最难的单词之一。相比之下,根据 Twitter 数据集,像这样的词是最容易的词,模拟数据集也是如此。

顺便提一下,这一分析也有助于回答这样一个问题,即收购《纽约时报》后,Wordle 是否变得更具挑战性。事实并非如此,至少没有证据表明,在收购或《纽约时报》重新推出 Wordle 之后,单词的平均难度或更难单词的出现频率有所变化。

图 12:Twitter 上 100 个最近的目标词的难度比(条),以及游戏长度的 7 天滚动平均值(虚线)。图片作者。

鉴于一些单词确实比其他单词更具挑战性——无论是在模拟数据集还是在 Twitter 数据集上——我们能说些什么原因呢?一种可能是,不常用的单词更难,因为它们不太可能成为人们的首选,即使我们已经学会的提示将它们挑选出来。同样,字母不常见的单词也可能更难。但还有另一个疑点,当我们查看 Twitter 上的困难单词子集时,我们可以看到:大多数具有正难度比率的单词都有重复的字母(例如, ABBEY,KNOLL,SKILL,SWILL,VIVID,lompty)。在猜测 Wordle 时,很自然地会倾向于选择没有重复字母的单词,以最大限度地增加某些字母与目标单词重叠的机会,而且,当我们猜测一对重复字母中的一个时,Wordle 不会提供任何迹象表明一个单词中可能有第二个这样的字母。

因此,我们对单词困难有三种可能的解释——不常用的单词、不常用的字母和重复的字母——可以通过测量来确定它们在困难单词和容易单词之间是否不同。例如,使用现有的英语单词数据集,我们可以计算给定的 5 个字母单词的频率,并将其用作单词频率归一化测量的基础,如等式 14 所示;因此,NormWordFreq(wt) > 1 意味着 wt 比一般单词更常见。同样,我们可以根据等式 16,基于字母在 5 个字母的单词中出现的频率来计算相应的字母频率的归一化度量(等式 15);因此, NormLetterFreq(wt) > 1 意味着给定目标单词的平均字母频率大于平均值。

然后,我们可以使用等式 14 和 16 来计算困难单词(那些具有难度比> 0 的单词)和简单单词(难度比< 0 的单词和字母的平均频率。我们还可以计算包含重复字母的难/易单词的分数,并将其归一化为包含重复字母的 5 个字母单词的总分数。这些结果如图 13 所示。他们证实了我们的假设:难词远不如易词常见(图 19(a)),它们的字母也不太常见(图 19(b)),但难词更有可能有重复的字母(图 19(c))。

图 13:对单词和字母级别的频率以及重复字母的可能性的分析,有助于解释为什么一些目标单词比其他单词更难:困难的目标往往不太常见,涉及更多不寻常的字母,并且更可能有重复的字母。在每个图中,频率数据被归一化,使得值 1 对应于 5 个字母单词的平均值(用水平虚线表示)。图片作者。

RQ5:Wordle 的约束有多重要?

既然我们已经分析了起始词和目标词,是时候把注意力集中在一般的游戏玩法上了,特别是如何进行猜测。对于玩家来说,选择满足当前已知目标单词的猜测单词有多重要?玩家能承受牺牲一些约束而仍然玩得很好吗?哪些约束条件在使用时会提供更多有用的信息?

由于 Twitter 数据集不包含关于猜测的具体信息,因此不能用来回答这个问题。相反,我们将专门关注完整的模拟数据集;即使用 v 的所有值。使用缩减词汇设置的游戏与大量牺牲的约束相关联,这将允许我们的分析考虑约束使用的全部范围。

模拟数据集包括模拟器使用的约束条件的逐轮说明;每轮最多可关联 4 个约束(正确字母、正确位置、错误字母、错误位置)。我们可以计算每一轮游戏的错过的约束的数量,并根据等式 17 和 18 使用它来计算每轮游戏的错过的约束的平均数量;Gi . used【r】是指游戏 Gir 轮使用的约束集合。

接下来,我们计算具有给定词汇设置(v)的所有游戏的平均游戏时间和胜率,记录每轮对于每个值 v 错过的约束的平均数量。结果如图 14 所示。请注意每轮错过的约束条件的数量是如何随着 v 值的增加而减少的,因为当目标单词不在模拟玩家的词汇中时,模拟器会牺牲约束条件。例如,当只有 10%的目标单词在词汇表中( v = 0.1 )时,平均每轮牺牲 0.86 个约束,这意味着大多数轮将牺牲至少一个约束。但是当 v = 0.8,则每轮只牺牲 0.41 个约束;因此,在 5 轮游戏中会牺牲 2 个约束(0.41 × 5 个约束)。并且当 v = 1 时,所有约束总是得到满足。

图 14 中的结果显示,随着牺牲越来越多的约束,平均游戏时间增加,平均胜率降低。例如, v = 0.8 (每回合 0.41 个错过的约束)导致游戏的平均游戏时长刚刚超过 5 回合,胜率约为 80%,但对于 v = 0.4 (每回合 0.77 个错过的约束),平均游戏时长增加到 7 个,只有约 40%的游戏成功。

图 14:不同词汇设置的平均游戏长度(和获胜的比例)。对于每个词汇表配置,每轮遗漏的约束的平均数显示在括号中。图片作者。

因此,错过的约束在游戏长度和胜率方面对游戏性能有实质性的影响,但是一些约束比其他的更重要吗?使用不同的约束条件可以获得多少信息?为了测试这一点,我们需要将我们的分析从基于游戏——也就是说,查看单个游戏的各种属性——转移到基于回合的分析。换句话说,我们将开始查看在游戏中做出的个人猜测。到目前为止,基于游戏的关注意味着我们的分析已经在 5300 万个项目(游戏)的大集合上运行。现在,我们将把它转换成一个更大的数据集,包含超过 3.29 亿个项目(轮次/猜测)。对于这个分析,我们对两件事感兴趣:(1)猜测满足了哪些约束,以及(2)从猜测中了解了多少新信息?

我们转换后的数据集包含一条记录,记录了在特定猜测过程中使用了四个可能约束条件中的哪一个——正确的字母、正确的位置、不正确的字母、不正确的位置——因为模拟器保存了这些信息。为了估计通过猜测获得的新信息,我们使用 Wordle 的反馈,通过计算该猜测收到的绿色和黄色提示的总数。我们将绿色和黄色提示的数量称为正确信息的数量,这样,随着回合的展开,正确信息数量的差异就可以衡量猜测的有用程度。

图 15: (a)对于不同的游戏总长度,每轮游戏中已知字母(绿色或黄色提示)的总数。(b)对于不同的总游戏时间长度,每轮使用的限制的数量。图片作者。

例如,在图 15(a)中,我们看到对于不同长度的游戏,每一轮获得了多少正确的信息。例如,对于短游戏(游戏长度= 3),平均来说(模拟的)玩家在他们最初的猜测之后学习到 1.5 条正确的信息。他们的第二次猜测给他们带来了大约 3 条正确的信息,然后他们在第三次猜测中猜对了所有 5 个字母。对于更长的游戏,新信息的积累遵循越来越浅的轨迹,至少对于模拟器来说,在长时间游戏(> 6 轮)中,新的猜测可能导致比前一轮更少的信息。当玩家开始忽略约束条件,从而猜测可能违反目标单词已知信息的单词时,这种情况就会发生。事实上,在图 15(b)中有一个相应的图,显示了每一轮满足的平均约束数,我们可以看到随着模拟器牺牲越来越多的约束,游戏长度是如何增加的;这类似于上面图 14 中所示的,但是基于不同游戏长度的一轮接一轮,并使用每轮使用的实际限制数。

哪些约束更重要或更不重要是一个更复杂、更微妙的问题。我们建立一个回归模型,使用从猜测中获得的正确信息量作为因变量,使用四种约束类型的二元变量作为自变量。我们还包括游戏回合作为一个额外的独立变量,以确定其对新获得的信息的影响。因此,我们的模型被设计来预测从基于使用的约束和游戏回合的猜测中获得的新信息的量。

表 3。使用约束和轮数作为独立变量,预测每轮获得的新信息量的 OLS 回归结果。

得到的模型作为预测模型不是很好;见表 3。其调整后的 R2 值仅为 0.38,这意味着每轮获得的新信息量中只有约 38%的变化是由约束和游戏回合解释的;这并不奇怪,因为这很大程度上取决于实际猜测的细节和目标词等。然而,我们对这个模型作为预测模型有多好并不感兴趣,而是对它如何被用作解释模型感兴趣。特别是,我们对独立变量的系数感兴趣,它告诉我们它们在每次猜测获得的新信息数量方面的相对重要性;这些系数显示在表 3 的【T2 系数】栏中。

图 16:线性回归中每个独立因素的回归系数,用于预测每次猜测/轮次的总体信息增益。图片作者。

图 16 显示了每个独立变量的这些系数的值。它们可以解释为自变量单位变化的因变量(获得的新信息)的变化,同时保持所有其他自变量不变;由于约束变量是二进制的,单位的改变意味着约束被使用。因此,我们可以看到,当遵守正确 字母约束时,平均获得 1.4 个单位的新信息,相比之下,当使用正确位置约束时,仅获得不到 1 个单位的新信息。相比之下,不正确的字母不正确的 位置约束对获得的新信息量的影响更小。因此,我们可以得出结论,在所有其他条件相同的情况下,正确的字母和位置约束是最重要的,其次是不正确的字母约束,然后是不正确的位置约束。

游戏回合的效果不太重要。它具有正系数的事实意味着,在所有其他条件相同的情况下,后几轮往往比前几轮产生更多的新信息。

本节的结果显示了玩家在进行猜测时使用他们在前几轮中所学知识的重要性,因为牺牲一些约束条件,即使是一点点,也会影响游戏性能,并限制新信息的积累。正确的字母和位置约束比不正确的字母和位置约束更重要,这表明如果玩家必须牺牲一个约束,那么他们应该考虑在牺牲正确的字母或位置约束之前牺牲不正确的字母或位置约束。

值得指出的是,这是一个相当简单化的分析。例如,所使用的模型没有考虑约束之间可能存在的任何相互作用,而通过显式地模拟这种相互作用的影响来改进它是可能的。然而,作为一个解释性的模型,它对我们的目的是足够有用的,所以进一步的改进是作为未来工作的一个问题。

结论

Wordle 是一款简单的在线猜词游戏,于 2022 年初开始流行。每天选择一个新的(秘密的)5 个字母的目标单词,玩家试图通过一系列多达 6 次的猜测来识别这个单词。本文描述了使用模拟和真实世界游戏数据集对 Wordle 进行的大规模数据分析。通过比较模拟数据集和 Twitter 数据集,我们表明模拟器能够再现相当真实的游戏过程,进一步的分析发现了以下情况:

  1. 好的开始字导致更短和更成功的游戏,并且有与较差的开始字相关联的显著成本;在模拟和真实世界/Twitter 数据集的分析中观察到了类似的成本。
  2. 相当多的少数玩家(17%)似乎使用了糟糕的起始词,因此承诺使用更可靠的起始词会有所收获。
  3. 一些目标单词比其他单词更具挑战性,尤其是当它们不常见或包含重复字母时。在这方面,我们再次看到模拟器和 Twitter 数据集之间的强烈对应。
  4. 关注 Wordle 的反馈对于短游戏是必要的,关注正确的字母和位置比不正确的字母和位置更重要。

参考

  1. 3 蓝色 1 棕色。哦,等等,其实最好的开瓶器不是“鹤”。。。。 YouTube (2022)。
  2. 3 蓝色 1 棕色。用信息论解单词YouTube (2022)。
  3. ALIYARI,h .,SAHRAEI,h .,DALIRI,M. R .,MINAEI-BIDGOLI,b .,KAZEMI,m .,AGAEI,h .,SAHRAEI,m .,HOSSEINI,S. M. A. S .,HADIPOUR,M. M .,MOHAMMADI,m .等人。电脑游戏压力对玩家认知功能的有益或有害影响基础与临床神经科学 9 ,3 (2018),177。
  4. ANDERSON,B. J .和 MEYER,J. G. 使用最大正确字母概率和强化学习为 wordle 寻找最佳人类策略。arXiv 预印本 arXiv:2202.00557 (2022)。
  5. 我们亲爱的沃尔多究竟发生了什么事? 《卫报》(2022 年 2 月)。
  6. APPEL,A. W .和 JACOBSON,G. J. 世界上最快的拼字游戏程序美国计算机学会第 31 届会议公报,5 (1988),572-578 页。
  7. 阿基米德。Wordle——为什么我总是猜同样的四个字。 YouTube (2022)。
  8. 使用自动规划的推箱子谜题的人工智能辅助设计。在艺术、互动和游戏创作国际会议 (2021),斯普林格,第 424–441 页。
  9. BARLACCHI,g .、NICOSIA,m .和 MOSCHITTI,A. 学习对自动解决纵横字谜的候选答案排序。《第十八届计算自然语言学习会议论文集》 (2014),第 39–48 页。
  10. BONTHRON,M. 将一个近似值作为 Wordle 的策略进行排序。arXiv:2204.06324 (2022)。
  11. 视频游戏对人类(和兽人)创造力的影响。在视频游戏和创意。爱思唯尔,2015 年,第 39-60 页。
  12. 曹,s .和达希尔,I. 文字是如何成为互联网上的一种大众娱乐的。 Buzz Feed 新闻(2022 年 1 月)。
  13. 这些数字告诉我们什么是最好的和最差的单词开头。《卫报》(2022 年 2 月)。
  14. CHESANI,p . MELLO,M. 解决数学难题:人工智能的挑战性竞赛艾杂志 38 ,3 (2017),83–96。
  15. 科尔曼、T. H .、莱瑟森、c .、里维斯特、r .、斯坦恩、C. 算法导论,II 。马萨诸塞州剑桥。麻省理工学院出版社,2009 年。
  16. CRANFORD,E. A .、LEBIERE,c .、GONZALEZ,c .、COONEY,s .、VAYANOS,p .和 TAMBE,M. 通过模拟了解网络欺骗:在 Stackelberg 安全游戏中使用欺骗性信号预测人类决策。在 CogSci (2018)。
  17. 使用字符统计为 wordle 选择种子词。arXiv:2202.03457 (2022)。
  18. 埃里克森、K. I .、布特、W. R .、巴萨克、c .、奈德、M. B .、普拉卡什、R. S .、VOSS、M. W .、格雷比尔、A. M .、西蒙斯、D. J .、法比亚尼、m .、格拉顿等。纹状体体积预测视频游戏技能获得水平。 大脑皮层 20 ,11 (2010),2522–2530。
  19. 厄南德斯,m .,安吉利尼,g .,和戈里,m .韦伯克劳:一个基于网络的纵横字谜系统。载于 AAAI (2005),第 1412–1417 页。
  20. FRUTOS-PASCUAL,m .和 ZAPIRAIN,B. G. 综述 AI 技术在严肃游戏中的应用:决策和机器学习。《IEEE 计算智能与人工智能汇刊》第 9 期,第 2 期(2015),第 133–152 页。
  21. 《代码和数字中的生活:当香农遇到图灵》。电子观想与艺术**(EVA 2017)(2017),51–58。
  22. Wordle 中数学上最佳的第一次猜测。 (2022)。
  23. GOPHER,d .,WELL,m .,和 BAREKET,T. 从电脑游戏训练师到飞行的技能转移。人类因素 36 ,3 (1994),387–405。
  24. GRACE,k .、SALVATIER,j .、DAFOE,a .、ZHANG,b .、EVANS,o .AI 什么时候会超过人类的表现?来自人工智能专家的证据人工智能研究杂志 62 (2018),729–754。
  25. 格雷策,F. L .,赫什曼,R. L .,凯利,R. T. 防空游戏:研究人类行为的微型计算机程序。 行为研究方法&仪器仪表 13 ,1 (1981),57–59。
  26. GROSZ,b .、KRAUS,s .、TALMAN,s .、STOSSEL,b .和 HAVLIN,M. 社会依赖对决策的影响:一个新游戏的初步调查。关于自主代理和多代理系统的国际联合会议 (2004),IEEE,第 782–789 页。
  27. 霍尔,R. Wordle 的创造者被全球成功的热门拼图所淹没。 守护者(2022 年 1 月)。
  28. 哈姆金斯,J. D. 无限世界和主谋数字。arXiv 预印本 arXiv:2203.06804 (2022)。
  29. HARGREAVES,I. S .,PEXMAN,P. M .,ZDRAZILOVA,l .,和 SARGIOUS,P. 爱好如何塑造认知:竞争性拼字游戏玩家的视觉单词识别。 记忆&认知 40 ,1 (2012),1–7。
  30. m . HENZ 和 H.-M .张Sudokusat——一种分析困难数独谜题的工具。人工智能工具和应用中。斯普林格,2009 年,第 25-35 页。
  31. HUCK,J. T .,DAY,E. A .,LIN,l .,JORGENSEN,A. G .,WESTLIN,j .和 HARDY III,J. H. 认知好奇心在基于游戏的学习中的作用:区分技能习得和适应。 模拟&游戏 51 ,2 (2020),141–166。
  32. 凯利,R. T .、格雷策,F. L .、赫什曼,R. L. 防空:研究人类行为的电脑游戏。科技。1981 年,加利福尼亚州圣地亚哥海军人事研究与发展中心代表。
  33. 玩虚拟积木:作为实践与研究的学习环境。在数字化语境中的认知发展。爱思唯尔,2017 年,第 145–166 页。
  34. LEE,d .,CONROY,M. L .,MCGREEVY,B. P .,和 BARRACLOUGH,D. J. 猴子在竞争游戏中的强化学习和决策。 《认知大脑研究》22 ,1 (2004),45–58。
  35. LEE d .、MCGREEVY b . p .和 BARRACLOUGH d . j .猴子在石头剪刀布游戏中的学习和决策认知大脑研究 25 ,2 (2005),416–430。
  36. LIAPIS,a .,YANNAKAKIS,G. N .,TOGELIUS,J. 计算游戏创意。第五届国际计算创意大会 (2014),ICCC。
  37. LITTMAN,M. L .,KEIM,G. A .,和 SHAZEER,N. 一种解决纵横字谜的概率方法人工智能 134 ,1–2(2002),23–55。
  38. 利特曼,M. L .,凯姆,G. A .,和沙泽尔,N. M. 用谚语解决纵横字谜。在 AAAI/IAAI (1999),第 914-915 页。
  39. LOCK,s .wordle 是不是越来越难了? 守护者(2022 年 2 月)。
  40. LOKSHTANOV d .和 SUBERCASEAUX b .Wordle 是 np-hard 。arXiv:2203.16713 (2022)。
  41. 什么是 wordle?新的病毒性文字游戏取悦互联网。 卫报(2021 年 12 月)。
  42. MANTERE 和 j . KOLJONEN用遗传算法解决、评定和生成数独谜题。2007 年 IEEE 进化计算大会 (2007),IEEE,第 1382–1389 页。
  43. MANZINI、s . ELLIS 和 J. 文字游戏:使用认知计算作为人工智能解谜的基础人工通用智能杂志 6 ,1 (2015),111。
  44. 莫兰,C. 14 个最好的“单词”克隆体,因为一天一个单词是不够的可混搭(2022 年 3 月)。
  45. 马兹拉克,L. J. 利用优先关系计算机构造纵横字谜人工智能 7 ,1 (1976),1–19。
  46. 纵横字谜中元素的机器选择:计算语言学的应用。 《暹罗计算杂志》5 期,1 期(1976),51–72 页。
  47. 麦克甘,伊萨尔特尔,海德曼和康伦。跳过。跳。游戏:“有原则的”锻炼游戏对儿童运动技能获得的影响。 英国教育技术杂志。51 ,3 (2020),798–816。
  48. 莫法特特区,克龙比,w .和沙巴丽娜,O. 一些电子游戏可以增加玩家的创造力。 国际基于游戏的学习杂志(IJGBL) 7 ,2 (2017),35–46。
  49. 新生,M. 卡斯帕罗夫对深蓝:计算机国际象棋成熟了。施普林格科学公司商业媒体,2012 年。
  50. NEWELL,a .,SHAW,J. C .,和 SIMON,H. A. 下棋程序和复杂性问题。《IBM 研究与发展杂志》2 ,4 (1958),320–335 页。
  51. PRAKASH、R. S .、DE LEON、A. A .、MOURANY、l .、LEE、h .、VOSS、M. W .、BOOT、W. R .、BASAK、c .、FABIANI、m .、GRATTON、g .和 KRAMER,A. F. 在一个复杂的视频游戏训练项目中检验技能获得的神经相关性。 人类神经科学前沿 6 (2012),115。
  52. m .理查兹和 e .阿米尔拼字游戏中的对手建模。在 IJCAI (2007),第 1482–1487 页。
  53. 一个全自动纵横字谜生成器。第七届机器学习与应用国际会议 (2008),IEEE,第 362–367 页。
  54. RIGUTINI,l .、DILIGENTI,m .、MAGGINI,m .、GORI,M. 自动生成填字游戏。 《国际人工智能工具杂志》21 期,2012 年 03 期,1250014。
  55. 从国际象棋和雅达利到星际争霸和其他:游戏人工智能如何驱动人工智能的世界。KI-künstliche Intelligenz 34, 1 (2020),7–17。
  56. y . SATO 和 h .井上用保留积木的遗传操作解决数独。2010 年 IEEE 计算智能和游戏会议记录 (2010),IEEE,第 23–29 页。
  57. 游戏、计算机和人工智能。 人工智能 134 ,1–2(2002),1–7。
  58. SCHERBAUM,s .,DSHEMUCHADSE,m .,LEIBERG,s .,和 GOSCHKE,T. 比预期更难:在电脑游戏中明显不利的延迟选择中冲突增加。 PloS one 8 ,11 (2013),e79310。
  59. 世界冠军级别的拼字游戏。 人工智能 134 ,1–2(2002),241–275。
  60. 短,M. B. 胜 Wordle 明智地。arXiv 预印本 arXiv:2202.02148 (2022)。
  61. SILVER,d .,SCHRITTWIESER,j .,SIMONYAN,k .,ANTONOGLOU,I .,HUANG,a .,GUEZ,a .,HUBERT,t .,BAKER,l .,LAI,m .,BOLTON,a .,等人。在没有人类知识的情况下掌握围棋。Nnature 550,7676 (2017),354–359。
  62. 在 Wordle 中糟糕开局的代价以及如何避免。 走向数据科学(2022 年 2 月)。
  63. SMYTH,B. 在 Wordle 中,第一个字母是最难猜对的。或者是? 走向数据科学(2022 年 2 月)。
  64. SMYTH,B. 如何在 Wordle 中猜对。 走向数据科学(2022 年 1 月)。
  65. SMYTH,B. 峰值单词量&单词难度。 走向数据科学(2022 年 2 月)。
  66. 三百万条 tweets 之后。 走向数据科学(2022 年 2 月)。
  67. 我从玩了一百多万个 Wordle 游戏中学到了什么。 走向数据科学(2022 年 1 月)。
  68. 根据科学,这是最好的策略。 YouTube (2022)。
  69. STENSRUD,B. S .和 GONZALEZ,A. J. 通过观察人类在战略游戏中的表现发现高级行为。IEEE 系统、人和控制论汇刊,B 部分(控制论)38(T7),3 (2008),855–874。
  70. TOBIAS,s,FLETCHER,J. D .,DAI,D. Y,WIND,A. P. 计算机游戏研究综述。 电脑游戏与指令 (2011),127–221。
  71. VISSCHEDIJK,G. C .、LAZONDER,A. W .、VAN DER HULST,a .、VINK,n .和 LEEMKUIL,H. 为战术决策游戏模拟人类情绪。 《英国教育技术杂志》44 期,2 期(2013),197–207 页。
  72. 声音和颜色对电脑游戏反应的影响。 与计算机交互 13 ,2 (2000),183–192。

大 O 符号

原文:https://towardsdatascience.com/big-o-notation-32fb458e5260

用 Big-O 符号计算算法的时间和空间复杂度

约翰尼斯·格罗尔在 Unsplash 上的照片

介绍

当开发算法时,我们通常关注它的有效性——换句话说,它是否能做它应该做的工作。然而,时间和空间复杂度是必须评估的两个非常重要的因素,以确保该算法可以在实践中实现和使用。

即使一个算法在理论上是可行的,但是如果运行它所花费的时间使它变得无用,会发生什么呢?或者它需要的空间大到电脑可能内存不足怎么办?

在今天的文章中,我们将在渐近算法分析的背景下讨论时间和空间复杂性。我们将探索大 O 符号,这是用来描述算法效率的最常用的度量。

此外,我们还将讨论大 O,大 Theta 和大 Omega 符号之间的区别。最后,我们还将通过几个实际操作的例子来演示如何在实践中计算时间和空间复杂度。

请注意,这些示例将使用 Python——即使您不了解 Python,我也很确定它会比您想象的更容易理解。为了帮助没有 Python 背景的人,我保证会添加尽可能多的评论:)

渐近时间复杂性

在数学分析中,渐近分析是一种用来描述当输入接近某个其他值时,函数所逼近的值(即极限)的方法。作为一个例子,让我们考虑我们想要研究一个函数 f(n) 在数 n 变得非常大时的行为。

如果f(n)=n⁴+4n+10并且数字 n 非常大,那么术语 4n 与术语相比将无足轻重,而 n 对常数 10 没有影响。因此,当 n 趋于无穷大时,函数 f(n) 被称为渐近等价于 n⁴

现在在算法的上下文中,执行渐近分析,以便根据输入大小描述或评估算法的性能。换句话说,当输入大小也增加时,我们使用渐近分析来确定时间和空间的增加。

作为一个例子,让我们考虑你想要传送一个数据文件给一个朋友,他住在离你当前位置 3 小时路程的城市。现在让我们假设实际上有两种方式发送它;第一种是电子转账,第二种是旅行 3 个小时,用 u 盘(物理)交给她。如果您决定以电子方式发送文件,传输时间将受到文件大小的影响。文件越大,传输给朋友所需的时间就越长。另一方面,如果您决定前往您朋友的位置并将其移交,传输时间将为 3 小时,与文件的大小无关,因此文件大小不会影响传输时间。

下图说明了这种行为— O(s) 对应于以电子方式传输文件的时间复杂度。随着文件大小 s 的增加,完成传输所需的时间也增加(在这个例子中,这种增加是线性的)。另一方面, O(1) 对应于物理传送文件的情况下的时间复杂度,因此时间复杂度是恒定的。

时间(和空间)复杂性的渐近分析—来源:作者(源代码复制它)

大 O vs 大 Theta vs 大 Omega

大 O 符号用于描述算法复杂度的一个上限。假设我们有一个 N 个整数的列表(或数组)。如果我们想打印出列表中的每个元素,时间复杂度是 O(N)。虽然从理论上讲,这个用例的时间复杂度也可以描述为 O(N)或 O(N)或任何其他大于 O(N)的值,因为这些是上限。

这可能有点令人困惑,所以让我们考虑一个现实生活中的例子。我们来做一个假设,没有一辆车可以超过 400 km/h 的速度,如果我们有一辆车的最大速度是 X,那么我们当然可以说 X ≤ 400。虽然从理论上讲,我们也可以说 X≤1000 或 X≤10000。从技术上讲,这是正确的,因为我们使用上限,即使进一步扩展上限并不十分有用。

然而,除了大 O 之外,还有两种替代符号,即大ω(ω)和大θ(θ)。

大ω用来形容下界*。回到我们打印所有列表元素的例子,下限将是ω(N),因为我们知道它不会比这个更快。同样,我们也可以说下限是ω(1)ω(logN),因为打印一个包含 N 个元素的列表不会比这些下限更快。*

大θ用于描述紧界*。这意味着如果一个算法同时是 O(N)ω(N),那么这个算法可以描述为θ(N)。我们可以说打印一个有 N 个元素的数组是θ(N)。*

一般来说,我们通常使用大 O 符号来描述算法的时间和空间复杂性,同时总是试图定义尽可能紧密的运行时,给定特定算法和/或用例的细节。

最佳情况对最差情况对预期情况

各种算法都包含某种形式的随机性,这可能会影响算法的性能。也有一些算法在排序的数组上运行得更快,但是当数组的元素没有排序时运行得更慢。

因此,实际上有三种不同的方式来描述算法的运行时间;最好的情况,预期的情况和最坏的情况。当我们评估算法的性能时,应该计算最坏情况的复杂度

请注意,这些概念不应与我们之前讨论的大θ和大ω相混淆。下限和上限与最佳、最差和预期情况无关。

空间复杂性

除了时间复杂度之外,另一个需要评估的重要方面是空间复杂度,即算法执行所需的内存量。随着计算机能力的不断发展,空间复杂性通常会被程序员忽略。然而,考虑到正在处理的数据也在不断发展,以消耗尽可能少的内存的方式设计和实现算法是非常重要的。

进入空间复杂度的实际计算,如果我们要创建一个 N 个整数的一维数组,那么就需要 O(n) 空间。同样,一个大小为 n x n 的二维数组需要 O(n)个空间。这里还必须提到,除了数据结构中元素占用的空间之外,在递归调用中分配给堆栈的空间也应该考虑在内。

用大 O 符号计算空间复杂度

作为一个例子,让我们考虑下面的递归函数,它用于计算 0 和n之间的数字之和:

*def sum_func(n: int):
    """ 
    Recursive method that returns the sum 
    of the numbers between 0 and input n
    """
    if n > 0:
        return sum_func(n - 1) + n return 0*

尽管函数的输入只是一个整数,但每次递归调用都会在调用堆栈中增加一个级别。举个例子,

*sum_func(5)
    adds call sum_func(4) to stack
        adds call sum_func(3) to stack
            adds call sum_func(2) to stack
                adds call sum_func(1) to stack
                    adds call  sum_func(0) to stack*

实际上,添加到调用堆栈中的每个调用都会消耗实际内存,因此在计算空间复杂度时,应该始终将它们考虑在内。因此,函数sum_func将需要 O(n)时间和 O(n)空间。

拥有 n 个调用,并不一定意味着一个算法需要 O(n) 空间。现在让我们修改一下我们之前使用的递归方法,这样 0 和输入数n之间的和的计算是以迭代的方式进行的。

*def sum_func(n: int):
    """ 
    Iterative method that returns the sum 
    of the numbers between 0 and input n
    """
    sum = 0
    for i in range(n+1):
        sum = sum_nums(sum, i)
    return sum def sum_nums(a: int, b:int):
    """
    Returns the sum of two numbers
    """
    return a + b*

上述函数将花费 O(n)时间和 O(1)空间。换句话说,执行功能sum_func所需的内存是恒定的。

用大 O 符号计算时间复杂度

现在回到时间复杂性,重要的是要强调大 O 符号与所讨论算法的实际执行时间并不对应的事实。例如,O(n)的算法实际上可能比 O(1)的算法更快。因此,我们可以说大 O 用于描述相对于输入大小的增长率。

这就是我们在计算时间复杂度时,并不真正关注常数的主要原因。在最开始,我们讨论了数学背景下的渐近分析。

如果 f (n) = n⁴ + 4n + 10 并且数字 n 非常大,那么术语 4n 与术语相比将无足轻重,而 n 对常数 10 没有影响。因此,当 n 趋于无穷大时,函数 f(n) 被称为渐近等价于 n⁴ 。**

类似地,在算法渐近分析中,我们丢弃任何常量值以及无关紧要的项

作为一个例子,让我们考虑一个算法的两个版本,它从一个输入列表中计算最小和最大数字。注意,可能有更好的方法来执行相同的操作,但是这个例子的目的是演示一些与 Big-O 符号相关的概念。

**def get_min_max(lst):
    """
    Returns the minimum and maximum 
    numbers from the input list as a 
    tuple in the form (min, max)
    """
    _min = lst[0]
    _max = lst[0] for n in lst[1:]:
        if n > _max:
            _max = n
        if n < _min:
            _min = n return _min, _max**

因为我们有一个遍历输入列表元素的 for 循环,所以上面函数的时间复杂度可以用 O(n) 来描述。

现在让我们修改这个方法,使最小和最大数量的计算发生在单独的循环中。

**def get_min_max(lst):
    """
    Returns the minimum and maximum 
    numbers from the input list as a 
    tuple in the form (min, max)
    """
    _min = lst[0]
    _max = lst[0] for n in lst[1:]:
        if n > _max:
            _max = n for n in lst[1:]:
        if n < _min:
            _min = n return _min, _max**

现在我们有两个独立的(虽然不是嵌套的!)for 循环,每个循环的执行时间为 O(n)。有人可能会争辩说,这被翻译成 O(n + n),因此 O(2n)。回想一下,当用 Big-O 符号描述时间复杂性时,我们必须消除任何常量和/或无关紧要的项。因此,我们的示例算法的第二个版本的复杂度仍然是 O(n ),因为我们只对增长率感兴趣。

让我们考虑另一个例子,其中一个函数接受一个输入列表,并打印出原始元素和平方元素(我再次警告您,有更好的方法来实现这一点,所以请不要太关注逻辑)。

**def print_squares(lst):
    for a in lst: 
        print(a) for a in lst:
        for b in lst:
            print(a * b)**

所以我们有一个 for 循环,它简单地遍历输入列表的元素,并打印出每一个元素,然后我们有两个嵌套循环。

前一个表达式需要 O(n)时间,后一个表达式由于两个嵌套循环需要 O(n)时间。同样,有人会认为时间复杂度是 O(n + n ),但鉴于我们只对增长率感兴趣,我们必须去掉任何无关紧要的项,因此我们最终的时间复杂度是 O(n)。

增长率

下面列出了大多数时候用来描述算法的时间和空间复杂性的标准增长率。

  • 对数: O(log n)
  • 线性: O(n)
  • 线性算法: O(n log n)
  • 二次: O(n )
  • 指数: O(c^n) 其中 c 是固定值,而 c > 1
  • 阶乘: O(n!)

最后的想法

在今天的文章中,我们讨论了在开发算法时评估时间和空间复杂度的重要性。这可以通过渐近分析来实现,这可以帮助我们使用大 O 符号来描述性能。

此外,我们讨论了带有大ω和大θ的大 O 符号之间的区别,以及在描述算法复杂性时最好、最坏和预期情况的含义。为了演示如何计算时间和空间复杂度,我们还使用了一些示例算法。最后,我们快速浏览了一下最常用的增长率,当涉及到算法的计算空间和时间复杂度时,这些增长率通常涵盖了大多数情况。

算法中的渐近分析是一个基本且非常重要的话题,每个写代码的人都必须知道。通过理解某些决策如何影响时间和空间复杂性,您最终可以提高代码的质量和性能。此外,如果你想搞定你的下一次面试,那么这是一个你必须仔细修改的话题。有可能你会被要求开发一个算法,而时间和空间复杂度是你在开发时需要考虑的概念。即使您无法实现给定问题的最佳解决方案,您至少应该知道您的解决方案如何执行,以及如何修改它以达到最佳的复杂性时间。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

**https://gmyrianthous.medium.com/membership **

相关文章你可能也喜欢

** ** ** ** ** **

BigQuery 的 SQL 反模式

原文:https://towardsdatascience.com/bigquery-anti-patterns-dacb61f8a3f

在 Google Cloud BigQuery 上运行 SQL 的最佳实践和需要避免的事情

杰克·卡特Unsplash 上拍照

BigQuery谷歌云平台上的托管数据仓库服务,像大多数服务和技术一样,它有一套使用时需要牢记的原则。

在接下来的几节中,我们将概述一组最佳实践,以避免通常会对 BigQuery 性能产生负面影响的常见反模式。应用最佳实践很重要,主要有两个原因——它们将帮助您编写更高效的查询,同时,如果应用正确,将降低您的成本

避免选择*

从结果集中选择所有字段是一种非常常见的反模式,应该尽可能避免。SELECT *将导致对表中的每一列进行完全扫描,这意味着这将是一个执行开销很大的操作。

仅查询您需要的列

还要记住LIMIT不会减少读取的字节量,因此,您仍然需要为每一列的完全扫描付费。因此,请确保只查询您实际需要的列。如果您仍然需要运行SELECT *,那么考虑对您的表进行分区,这样您将能够查询驻留在一个或一些分区中的数据。

避免自连接

在表上执行自连接是另一件应该避免的事情。自然有人会问两个独立表的连接和自连接有什么区别。

答案是否定的——这几乎是一回事,但这里的要点是,无论何时您要执行自连接,都有可能使用窗口函数来实现相同的结果,这是一种更优雅的方式。

避免自连接,而使用窗口函数

自联接可能会增加输出行数,这意味着它会降低查询性能,还会导致处理的字节数增加,从而增加运行此类查询的成本。

处理数据偏斜

数据偏斜是当您的数据被划分为大小不均匀的分区时出现的现象。在后台,BigQuery 会将这些分区发送到插槽中,这些插槽是用于以分布式方式执行 SQL 查询的虚拟 CPU。

因此,分区不能跨不同的插槽共享。如果您创建了不平衡的分区,这意味着一些插槽最终会比其他插槽有更多的工作负载,而在某些极端情况下,过大的分区甚至会使插槽崩溃。

当您基于包含比其他值更频繁出现的值的键/列对表进行分区时,您很可能会得到大小不等的分区。在这种情况下,尽早应用过滤器将有助于缩小这种不平衡。

如果你的数据有偏差,尽早应用过滤

此外,您可能还需要重新考虑分区键。例如,您可能希望避免使用具有许多NULL值的键对表进行分区,因为这将为此类行创建一个巨大的分区。一个常用的分区键是一个日期字段,它可以确保数据在不同分区上的均匀分布(假设每天/每月/每年的数据量大致相同)。

交叉连接

交叉连接用于生成两个表之间的笛卡尔积,即包含相关表记录之间所有可能组合的结果。更简单地说,第一个表中的每一行都将被连接到第二个表中的每一行,这意味着在最坏的情况下,我们将得到由 M×N 行组成的结果,其中 M 和 N 分别是表的大小。

避免执行会导致输出多于输入的连接

因此,这意味着交叉连接通常会返回比输入更多的输出行,这是我们通常想要避免的。作为一名总顾问,在这种情况下,您应该考虑两种可能的解决方法:

  1. 评估窗口函数(比交叉连接更有效)是否能帮助您获得想要的结果
  2. 在加入之前,使用GROUP BY执行预聚合

比起分片,更喜欢表分区

表分片是一种将数据存储到多个不同表中的方法,使用一个命名前缀,比如[PREFIX]_YYYYMMDD。许多用户会认为上述技术与分区相同,但实际上并非如此。

表分片要求 BigQuery 维护每个表的元数据和模式,此外,无论何时执行操作,平台都必须验证所有单个表的权限,这会对性能产生重大影响。

表分区比表分片更有效

一般来说,表分区的性能更好,因此您应该首选它们而不是分片表。此外,在过滤和降低成本方面,分区表更容易处理。

不要将 BigQuery 视为 OLTP 系统

像大多数数据仓库解决方案一样,BigQuery 也是一个 OLAP(在线分析处理)系统。这意味着在使用表扫描处理海量数据时,它的设计是高效的。因此,BigQuery 上的 DML 语句不应该用于执行批量 更新

BigQuery 是一个 OLAP 系统,需要如此对待

使用 DML 语句执行模块化更改意味着您试图将 BigQuery 视为 OLTP(在线事务处理)系统。如果是这样的话,你应该重新考虑你的设计,甚至是你正在使用的工具。有可能 OLTP 系统(比如 Google 云平台上的 CloudSQL)更合适。或者,如果您的设计涉及常规模块化插件,您可以考虑其他技术,如流技术。

关于 OLAP 和 OLTP 系统之间的主要区别的更多细节,您可以阅读我最近的一篇文章。

最后的想法

在 BigQuery 中应用最佳实践并避免常见的反模式非常重要,因为这些原则将帮助您提高系统的性能并降低成本。

总结一下,

  • 避免使用SELECT *,取而代之的是,确保你只查询你需要的字段
  • 尽可能选择窗口函数而不是自连接(例如,如果您需要计算的是行相关的)
  • 明智地选择分区键,以避免数据偏斜。如果不可能,请确保尽早应用过滤器
  • 避免产生输出多于输入的连接
  • 比起表分片,我更喜欢表分区,因为前者效率更高,成本更低
  • 避免模块化的 DML 语句——big query 是一个 OLAP 系统,需要这样对待

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

相关文章您可能也喜欢

</2-rules-groupby-sql-6ff20b22fd2c>

通过使用数组和结构在 BigQuery 中节省时间和金钱

原文:https://towardsdatascience.com/bigquery-efficiency-how-i-reduced-my-table-size-by-35-5-and-rows-by-93-1-dc8b9b7276ff

学会接受和理解嵌套模式。

Unsplash 上由Towfiqu barb huya拍摄的照片

尽管数组和结构会增加查询的复杂性,但是一旦理解了它们的工作原理,就可以获得嵌套数据结构的好处。

为了让你了解这种改变有多大的好处,下面展示了我通过将一个 Google Clouds 公共数据集从典型的非规范化格式转换为嵌套格式而获得的节省。

我已经能够将行数减少 93.1%,将表的大小减少 35.5%

以下是我们将在本文中涉及的内容:

  • 什么是结构,它是如何工作的?
  • 什么是数组,它是如何工作的?
  • 如何结合使用数组和结构数据类型来降低查询和表存储成本?
  • 最后,我们将通过几个测试练习来演示如何查询嵌套数据,同时深入了解与原始非规范化表相比的性能差异

Maksym Kaharlytskyi 在 Unsplash 上拍摄的照片

结构体

STRUCT结构的简称,在 BigQuery 中可以使用 STRUCT 数据类型将多个字段放在一个名称下。帮助我理解这个概念的是想象你正在给一组列分配一个类别。

让我们看一个例子。假设我们有一个包含学生数据的表。

SELECT 
193348009 as Student_ID,
"Toby Lerone" AS Student_Name,
"Year 11" as Year_Group,
"Group 5A" as Class_Group,
"Mr Brightside" as Year_Teacher,
"Maths" AS Subject,
73 AS Grade,
DATE("2022-11-01") AS Exam_Date

要创建一个结构,我们首先需要决定每个结构中应该包含哪些字段。让我们在这个例子中尝试两个结构;细节和结果。

创建这个结构很简单。简单地列出你想要在结构中包含的字段,把它们添加到括号(每个字段必须用逗号分开)

STRUCT(FIELD1, FIELD2, FIELD3) AS NEW_STRUCT

让我们创建两个新的结构,一个用于细节,另一个用于结果。

SELECT
193348009 as Student_ID,

STRUCT
  (
    "Toby Lerone" AS Student_Name,
    "Year 11" as Year_Group,
    "Group 5A" as Class_Group,
    "Mr Brightside" as Year_Teacher
  ) AS Details,

STRUCT
  (
    "Maths" AS Subject,
    73 AS Grade,
    DATE("2022-11-01") AS Exam_Date
  ) AS Results

我在上面缩进了我的结构,但这不是语法要求,我这样做只是为了可读性。如果你愿意,你可以把整个结构放在一行中。

结果与我们之前看到的没有太大的不同,只是我们现在为每个字段头添加了一个前缀。

这告诉我们,学生姓名,年组,班级组年教师细节结构中。科目、成绩考试日期字段包含在成绩结构中。

如果我们将这些结果保存为一个表,我们的模式将如下所示。请注意,我们有了一个新的数据类型“RECORD ”,它现在出现在我们前面定义的两个结构中。

查询结构就像创建结构一样简单。与您通常选择字段的方式相比,您只需要做一处更改—您必须为字段名称添加前缀。

SELECT 
Student_ID,
Details.Year_Teacher,
Results.Subject,
Results.Grade,
Results.Exam_Date
FROM Student_Data

关于结构数据类型的官方文档可以在这里找到。

照片由凯利·麦克林托克Unsplash 上拍摄

排列

数组是存在于相同单元格中的值的列表,这些值也是相同的数据类型。这是结构和数组之间的一个重要区别。结构可以包含各种数据类型,而数组只能包含一种数据类型。

有几种方法可以在 BigQuery 中创建数组,我将在本文中介绍其中的两种方法(有帮助的文档 如果你想进一步探索的话,这里有)

创建带方括号的数组

创建数组就像创建结构一样简单。您必须将值放在方括号内,并且每个值必须用逗号分隔。

SELECT ["Chris Packet","Liz Anya","Hugh Dunnit"] as Attendees

正如您在上面看到的,我们的数组中有三个参与者,但是需要注意的是这三个值都包含在一行中。

这是导致本文开头提到的存储和查询成本节省的关键部分,因为即使在这个简单的例子中,我们也已经将表的大小从 3 行减少到 1 行。

使用 ARRAY_AGG()创建数组

函数将一组值放在一起,并将它们连接成一个数组。我们将在我们之前创建的结构字段上使用这个函数,这将为我们提供一个嵌套表(一个结构数组)

我已经扩展了前面的学生数据,增加了几行。请注意,我们之前定义的结构仍然存在,现在我们的数据集中有两个学生,每个学生有 3 个科目。

SELECT
Student_ID,
ARRAY_AGG(Details) as Details,
ARRAY_AGG(Results) as Results
FROM STUDENT_DATA
GROUP BY Student_ID

顾名思义,ARRAY_AGG 是一个聚合函数,所以我们必须在这里包含 group by 子句,它将根据 Student_ID 字段对我们的结构进行分组。

在 ARRAY_AGG 函数中,我们只需要指定结构的名称。我们不需要单独列出每个字段,因为它们已经包含在我们的结构中了。

运行这个查询向我们展示了熟悉的结果。请注意,尽管显示了 6 行数据,我们只有 2 行数据。这要归功于 array_agg 函数,该函数将结构中的字段提取到一个数组中,用于每个唯一的 Student_ID。

将结果导出到电子表格中会让您对 BigQuery 如何在幕后存储这些数据有所了解。

Unsplash 上由 Waldemar Brandt 拍照

大规模应用这些技术

在介绍了结构和数组的理论和实践之后,让我们回到本文的主题。如何使用这两种方法来节省存储和查询成本呢?

首先,让我们看看原始数据集。我在这个例子中使用的是 BigQuery 中一个名为 Chicago taxi trips 的公共数据集。

FROM `bigquery-public-data.chicago_taxi_trips.taxi_trips`

下面是该模式的屏幕截图。它包含 23 列,2 . 03 亿行,占用了 74GB 的 Google Clouds 存储空间。

前面我们介绍了结构以及如何将你的字段分成不同的类别。让我们再次做同样的事情,这次将数据集分成 3 个结构:Details、Payment 和 Geographic。

当你有一个超过 15 个字段的表格时,如果你需要来回滑动来检查你需要使用的字段,那么使用它会变得很累(并且容易出错)。在表中有明确定义的结构有助于为字段添加上下文,如果任何字段碰巧有相似的名称,这尤其有用。

BigQuery 有一个方便的辅助特性,它在处理结构时非常有用,允许您查看结构中存在的所有字段,而无需检查表模式本身。

让我们使用数组和结构的组合将原始表转换成嵌套版本。

由于数据是公开的,下面的查询也适用于您。只需修改查询顶部的表路径,但要确保您的数据集位置设置为 US,以匹配原始数据集

create or replace table 
`spreadsheep-20220603.Dashboard_Datasets.taxi_trips_new` 
as (
SELECT 
company, 
taxi_id,
extract(date from trip_start_timestamp) as pickup_date,
#####STRUCT ONE#####
array_agg
(
  struct
  (
    unique_key,
    trip_start_timestamp as start_timestamp,
    trip_end_timestamp as end_timestamp,
    trip_seconds as seconds,
    trip_miles as miles,
    pickup_census_tract, 
    dropoff_census_tract, 
    pickup_community_area, 
    dropoff_community_area
  )
) as details,
#####STRUCT TWO#####
array_agg
(
  struct
  (
    fare,
    tips,
    tolls,
    extras,
    trip_total,
    payment_type
  )
) as payment,
#####STRUCT THREE#####
array_agg
(
  struct
  (
pickup_latitude, 
pickup_longitude, 
pickup_location, 
dropoff_latitude, 
dropoff_longitude,
dropoff_location
  )
) as geographic

FROM `bigquery-public-data.chicago_taxi_trips.taxi_trips` group by 1,2,3)

正如您在上面看到的,我们创建了三个结构,如前面的图表所示,每个结构都包含在 ARRAY_AGG 函数中。

下面是新的模式。我们之前在名称和数据类型方面有相同的字段,但现在它们只是在记录类型字段(这些是我们定义的结构)下有一点不同。

还要注意记录字段的模式显示为重复。这告诉我们这个结构存在于一个数组中。

结果

这将我们带回到了本文开头显示的摘要截屏。我们将表中的行数减少了 93.1%,表的大小减少了 35.5%!

测试

好的,我们可以看到这种方法节省了存储成本,但是查询性能呢?让我们运行几个查询来回答这三个问题:

  1. 【2022 年 5 月,多少%的出租车司机给了小费(按公司和整体,按 desc total trips 排序)
  2. 【2022 年年初至今(除去顶部和底部 5%的行)每月的平均旅行时长是多少秒
  3. 【2022 年 1 月,每家公司的每英里费用是多少(由 desc total trips 订购)

此外,让我们引入一个新的表来模拟另一个比较点的标准化数据集。第三个表是基于与其他两个表相同的数据,我将该表分为两部分,如下所示。

create or replace table 
`spreadsheep-20220603.Dashboard_Datasets.taxi_trips_payment` as 
(
SELECT
unique_key, 
fare, 
tips, 
tolls, 
extras, 
trip_total, 
payment_type
FROM `bigquery-public-data.chicago_taxi_trips.taxi_trips`
);

create or replace table 
`spreadsheep-20220603.Dashboard_Datasets.taxi_trips_main` as 
(
SELECT 
* except 
(
  fare,
  tips, 
  tolls, 
  extras, 
  trip_total, 
  payment_type
)
  FROM `bigquery-public-data.chicago_taxi_trips.taxi_trips`
);

2022 年 5 月打车出行给小费的比例是多少?

/*##################################################################################################*/
--What % of taxi trips in May 2022 gave a tip (by company ordered by total trips by each company total desc)--
/*##################################################################################################*/

--NESTED TABLE--
SELECT
COMPANY,
ROUND(SAFE_DIVIDE(TIPS_MADE,TOTAL_TRIPS_BY_COMPANY)*100,1)||"%" AS PERC_TIPPING,
TIPS_MADE,
TOTAL_TRIPS_BY_COMPANY
FROM
(
  SELECT
  LOWER(COMPANY) as COMPANY,
  COUNT(IF(tips > 0,UNIQUE_KEY,NULL)) as TIPS_MADE,
  COUNT(UNIQUE_KEY) AS TOTAL_TRIPS_BY_COMPANY
  FROM spreadsheep-20220603.Dashboard_Datasets.taxi_trips_new, 
  UNNEST(details) WITH OFFSET pos1, 
  UNNEST(payment) WITH OFFSET pos2
  WHERE DATE_TRUNC(start_timestamp,MONTH) = "2022-05-01" 
  AND pos1 = pos2
  GROUP BY COMPANY
)
ORDER BY TOTAL_TRIPS_BY_COMPANY DESC;

--ORIGINAL TABLE--
SELECT
COMPANY,
ROUND(SAFE_DIVIDE(TIPS_MADE,TOTAL_TRIPS_BY_COMPANY)*100,1)||"%" AS PERC_TIPPING,
TIPS_MADE,
TOTAL_TRIPS_BY_COMPANY
FROM
(
  SELECT
  LOWER(COMPANY) as COMPANY,
  COUNT(IF(tips > 0,UNIQUE_KEY,NULL)) as TIPS_MADE,
  COUNT(UNIQUE_KEY) AS TOTAL_TRIPS_BY_COMPANY
  FROM spreadsheep-20220603.Dashboard_Datasets.taxi_trips_original 
  WHERE DATE_TRUNC(trip_start_timestamp,MONTH) = "2022-05-01"
  GROUP BY COMPANY
)
  ORDER BY TOTAL_TRIPS_BY_COMPANY DESC;

--SPLIT TABLE--
SELECT
COMPANY,
ROUND(SAFE_DIVIDE(TIPS_MADE,TOTAL_TRIPS_BY_COMPANY)*100,1)||"%" AS PERC_TIPPING,
TIPS_MADE,
TOTAL_TRIPS_BY_COMPANY
FROM
(
  SELECT
  LOWER(COMPANY) as COMPANY,
  COUNT(IF(tips > 0,MAIN.UNIQUE_KEY,NULL)) as TIPS_MADE,
  COUNT(MAIN.UNIQUE_KEY) AS TOTAL_TRIPS_BY_COMPANY
  FROM spreadsheep-20220603.Dashboard_Datasets.taxi_trips_main AS MAIN
  INNER JOIN spreadsheep-20220603.Dashboard_Datasets.taxi_trips_payment AS PAYMENT
  ON MAIN.UNIQUE_KEY = PAYMENT.UNIQUE_KEY
  WHERE DATE_TRUNC(trip_start_timestamp,MONTH) = "2022-05-01"
  GROUP BY COMPANY
)
  ORDER BY TOTAL_TRIPS_BY_COMPANY DESC;

在这个练习中,我们的嵌套表是最重要的。查询持续时间与原始表相当,但使用的计算能力和处理的数据更少。

对于那些不熟悉数组的人,请密切注意我在这个例子中是如何取消嵌套多个数组的。如果去掉带有偏移的部分,那么由于双重不嵌套,最终会得到重复的结果。为了防止这种情况发生,我在 WHERE 子句中设置了一个条件,即两个数组的数组偏移量必须相等。

关于带偏移的的更多细节可以在这里找到,或者在本文中提出你的问题,我会尽快回复你。

2022 年年初至今每月的平均旅行时长是多少秒?

/*##################################################################################################*/
--What is the average trip duration in seconds by month for 2022 YTD--
/*##################################################################################################*/

--NESTED TABLE--
SELECT
date_trunc(CAST(start_timestamp AS DATE),month) as month,
AVG(SECONDS) as avg_seconds
FROM
(
  SELECT
  start_timestamp,
  seconds
  FROM spreadsheep-20220603.Dashboard_Datasets.taxi_trips_new, UNNEST(details)
  WHERE EXTRACT(YEAR FROM start_timestamp) = 2022
  QUALIFY 
  seconds BETWEEN 
    PERCENTILE_CONT(seconds,0.05) over () 
    AND 
    PERCENTILE_CONT(seconds,0.95) over ()
)
GROUP BY MONTH
ORDER BY MONTH DESC;

--ORIGINAL TABLE--
SELECT
date_trunc(CAST(trip_start_timestamp AS DATE),month) as month,
AVG(trip_seconds) as avg_seconds
FROM
(
SELECT
trip_start_timestamp,
trip_seconds
FROM spreadsheep-20220603.Dashboard_Datasets.taxi_trips_original
WHERE EXTRACT(YEAR FROM trip_start_timestamp) = 2022
  QUALIFY 
  trip_seconds BETWEEN 
    PERCENTILE_CONT(trip_seconds,0.05) over () 
    AND 
    PERCENTILE_CONT(trip_seconds,0.95) over ()
)
GROUP BY MONTH
ORDER BY MONTH DESC;

--SPLIT TABLE--
SELECT
date_trunc(CAST(trip_start_timestamp AS DATE),month) as month,
AVG(trip_seconds) as avg_seconds
FROM
(
SELECT
trip_start_timestamp,
trip_seconds
FROM spreadsheep-20220603.Dashboard_Datasets.taxi_trips_main AS MAIN
INNER JOIN spreadsheep-20220603.Dashboard_Datasets.taxi_trips_payment as PAYMENT
ON MAIN.UNIQUE_KEY = PAYMENT.UNIQUE_KEY
WHERE EXTRACT(YEAR FROM trip_start_timestamp) = 2022
  QUALIFY 
  trip_seconds BETWEEN 
    PERCENTILE_CONT(trip_seconds,0.05) over () 
    AND 
    PERCENTILE_CONT(trip_seconds,0.95) over ()
)
GROUP BY MONTH
ORDER BY MONTH DESC;

我在这些查询中添加了两个 window 语句,看看它们是否在这些表之间造成了任何明显的差异。window 语句删除底部和顶部 5%的行。

令人惊讶的是,原始表和嵌套表表现出几乎相同的性能,尽管嵌套表花费的计算时间稍多一些。

2022 年 1 月,每家公司的每英里费用是多少?

/*##################################################################################################*/
--What was the £ per mile for each company in January 2022 (ordered by total trips desc)--
/*##################################################################################################*/

--NESTED TABLE--
SELECT
COMPANY,
COUNT(unique_key) AS TOTAL_TRIPS,
SUM(TRIP_TOTAL)/SUM(MILES) AS DOLLAR_PER_MILE
FROM spreadsheep-20220603.Dashboard_Datasets.taxi_trips_new, 
UNNEST(details) WITH OFFSET pos1, 
UNNEST(payment) WITH OFFSET pos2
WHERE DATE_TRUNC(start_timestamp,MONTH) = "2022-01-01" 
AND POS1 = POS2
GROUP BY COMPANY
ORDER BY TOTAL_TRIPS DESC;

--ORIGINAL TABLE--
SELECT
COMPANY,
COUNT(unique_key) AS TOTAL_TRIPS,
SUM(TRIP_TOTAL)/SUM(TRIP_MILES) AS DOLLAR_PER_MILE
FROM spreadsheep-20220603.Dashboard_Datasets.taxi_trips_original
WHERE DATE_TRUNC(trip_start_timestamp,MONTH) = "2022-01-01"
GROUP BY COMPANY
ORDER BY TOTAL_TRIPS DESC;

--SPLIT TABLE--
SELECT
COMPANY,
COUNT(MAIN.unique_key) AS TOTAL_TRIPS,
SUM(TRIP_TOTAL)/SUM(TRIP_MILES) AS DOLLAR_PER_MILE
FROM spreadsheep-20220603.Dashboard_Datasets.taxi_trips_main AS MAIN
INNER JOIN spreadsheep-20220603.Dashboard_Datasets.taxi_trips_payment AS PAYMENT
on MAIN.UNIQUE_KEY = PAYMENT.UNIQUE_KEY
WHERE DATE_TRUNC(trip_start_timestamp,MONTH) = "2022-01-01"
GROUP BY COMPANY
ORDER BY TOTAL_TRIPS DESC;

原始表和嵌套表的性能相似,但是嵌套表确实名列前茅,因为处理的数据量较少。

Unsplash 上由 Aron 视觉拍摄的照片

关键要点

  • 您是否使用基于事件的大型表,其中包含每个唯一用户/id 的大量事件?那么嵌套版本的表可以很容易地为您节省未来的时间和金钱
  • 如果您打算像我在本文中所做的那样,从非规范化模式切换到嵌套模式,请注意,您的任何预定或保存的查询都需要更新,以处理新的嵌套模式
  • 当您取消嵌套多个数组时,确保您为每个 unnest 使用 WITH OFFSET,并在 WHERE 子句中设置标准以防止重复行(参见第一和第三个测试的示例)
  • 尽管由于需要取消嵌套数组,您在嵌套表上的查询可能会更复杂,但与非规范化表相比,您可以期待更便宜(有时更快)的查询
  • 如果您使用标准化的数据(即在查询中将多个表连接在一起)并定期查询相同的表(例如每周报告),那么我强烈建议您花时间创建所使用的表的嵌套版本

如果您的表比较小,比如说小于 10GB,那么嵌套表的好处就变得不那么显著,增加的复杂性也不值得权衡。

这篇文章到此结束。如果有什么建议或推荐,欢迎随时留言评论。我很乐意看到它。

我经常为 BigQuery 和/或 Data Studio 撰写文章。如果你感兴趣,可以考虑在 medium 上关注我。

除非另有说明,所有图片均为作者所有。

留在上等的乡亲们!
汤姆
汤姆

用于数据清理的 BigQuery SQL 函数

原文:https://towardsdatascience.com/bigquery-functions-for-data-cleaning-4b96181fbc3

要应用的用例及功能

图片来自Rosy——世界值成千上万张图片来自 Pixabay

无论您是数据工程师、数据科学家还是数据分析师,数据清理都是任何数据相关职位的重要组成部分。今天,我想分享几个用于数据清理的 BigQuery SQL 函数,以及我会使用它们的一个用例。

字符串值中不可见的特殊字符

字符串值可以包含不显示在屏幕上但存储在数据库中的特殊字符。当我在一个字符串字段上应用 where 子句导致找到 0 条记录时,我经历了惨痛的教训。这非常令人沮丧,在应用 where 子句返回我知道存在的记录之前,我必须找到函数来公开 unicode 值,以便将它们从字符串中移除。

BigQuery 有一个 NORMALIZE 函数来处理这个场景。下面是 Jane 和 Smith 之间 unicode 值的 3 条记录,它们在查询结果中不可见。

作者创建的屏幕截图示例

如果我对 、简·史密斯 使用 where 子句,则不会返回任何记录。

作者创建的屏幕截图示例

但是,如果我对 name 字段使用 NORMALIZE 函数,unicode 值将被删除,查询结果中将返回三个简·史密斯记录。

使用作者创建的规格化函数的屏幕截图示例

特别提示 :如果您希望字符串比较不区分大小写,即在查询结果中返回包含简·史密斯或简·史密斯的记录,BigQuery 还有一个 NORMALIZE_AND_CASEFOLD 函数。

模式匹配

我一直使用 LIKE 操作符在字符串字段中进行模式匹配。最近,我不得不将网站访问者的引用 URL进行分类,以匹配谷歌分析频道报告。因为我不知道 URL 是大写还是小写,所以在检查模式匹配之前,我必须使用 LOWER 函数将字段全部转换为小写。

BigQuery 有一个针对这种情况的 CONTAINS_SUBSTR 函数。CONTAINS_SUBSTR 不仅执行不区分大小写的模式检查,还可以检查数字字段、时间戳和数组中的模式值。

在下面的例子中,我检查了 早餐 字段是否包含全部小写的字符串 煎饼 。尽管每条记录在 煎饼 中有一个大写字母,但在查询结果中会返回这两行。

使用作者创建的 CONTAINS_SUBSTR 函数的屏幕截图示例

特别提到 : BigQuery 还有一个 ENDS_WITH 函数,用来检查一个字符串是否以模式结尾。我可以使用它的一个常见用例是检查电子邮件是否以。edu 确认用户是学生。

日期格式

过去,我总是将 SQL 中的查询结果下载到 Excel 中,以便为报告目的设置日期格式,因为我无法使用 SQL 以我需要的方式设置日期格式。当我有大量数据要格式化时,这非常耗时。

BigQuery 有一个 FORMAT_DATE 函数来处理日期格式化。在下面的示例中,2022 年 9 月 30 日基于格式字符串以三种不同的方式进行格式化。

使用作者创建的 FORMAT_DATE 函数的屏幕截图示例

特别说明 :除了 FORMAT_DATE,还可以使用 FORMAT_DATETIME 来格式化日期时间值。还有一个 FORMAT 函数将字段格式化为字符串值。这个函数的一个用例是用逗号分隔符格式化大数。您可以使用 FORMAT 函数在查询结果中显示 1,000,000,而不是 1000000。

除以一个零分母

我经常不得不计算分母可能为 0 的百分比,这将在除以 0 时返回 SQL 错误。一种选择是使用 CASE 语句在除法之前检查分母是否为 0,以避免错误,但是大多数数据库都有处理这种情况的函数。

在 BigQuery 的例子中,这个函数被称为 SAFE_DIVIDE 。在下面的例子中,我将 10 除以 0,得到一个 除以零 的错误。

作者创建的使用除法错误示例的屏幕截图示例

在我使用 SAFE_DIVIDE 之后,结果是一个空值,而不是一个错误。

作者创建的使用 SAFE_DIVIDE 示例的屏幕截图示例

特别提示 : BigQuery 还有 SAFE_ADD、SAFE_SUBTRACT、SAFE_MULTIPLY、SAFE_NEGATE 函数,如果发生溢出,这些函数将返回空值。

最后的想法

虽然我们永远无法摆脱数据清理,但 SQL 函数可以提供帮助。我希望你学到了一两个将来有用的新功能。虽然我提到的函数在 BigQuery 中,但它们也可能在您的数据库中可用。

注意:以上所有的查询都是在 BigQuery 沙箱 上运行的,这对任何拥有谷歌账户的人来说都是免费的。

你可能也会喜欢…

</6-bigquery-sql-functions-every-user-should-know-9ed97b1cf72e> </4-bigquery-sql-shortcuts-that-can-simplify-your-queries-30f94666a046>

重访 BigQuery 远程函数、云函数 2.0 和 Plus 代码

原文:https://towardsdatascience.com/bigquery-remote-functions-cloud-functions-2-0-and-plus-codes-revisited-7b6308b2bc03

图片来源:安妮·尼加德,Unsplash

几年前,我参加了一个由谷歌主办的会议,发现了加号代码。简而言之,这是一个寻址系统,它生成一个字母数字代码来表示世界上的位置。在许多方面,它比标准地址更准确、更容易访问,而且比精确的坐标位置更容易记忆。许多博客和文章都写了这个话题,,包括我自己写的一篇。如果你不熟悉加号代码和开放位置代码(OLC,生成加号代码的算法),我建议你看一看它是如何工作和如何使用的。从工程角度来看,这很酷,但对于服务水平低下的社区来说,这也非常有影响力。

在那篇文章中,我决定实现 OLC 来学习如何使用 BigQuery 脚本。这是一个如何在 BigQuery 中使用过程化代码的例子,它成功了!从那时起,数据平台,以及总体上的谷歌云,随着许多新特性和功能的加入,已经发生了相当大的变化。引起我注意的两个是 BigQuery 远程函数云函数第二代。我想亲自动手看看这是如何工作的,有多简单,所以我想为什么不再次使用加号代码来尝试一下呢!

请记住,在撰写本文时,云功能 Gen2 和 BigQuery 远程功能都处于公开预览阶段,这意味着它们包含在预发布条款和条件中。在投入生产之前,情况可能会有所变化。

熟悉 GCF Gen2

使用其他编程语言的好处是 Google 致力于开发并开源了库,为其中的一些库生成了附加代码!在这篇文章中,我们将使用 Python 库

尝试一下

让我们首先在我们的机器上进行本地测试。事实证明,你可以使用 [pip](https://github.com/google/open-location-code/tree/main/python)非常简单地安装它。使用它只是简单地在一个坐标对上调用encode()函数。

在本地安装 OLC 库,并在交互式终端中运行它。相当简单!

构建功能

我们现在要构建一个 BigQuery 可以调用的函数。从关于远程函数的文档中,这是 BigQuery 将发送的请求的输入格式:

{
 "requestId": "124ab1c",
 "caller": "//bigquery.googleapis.com/projects/myproject/jobs/myproject:US.bquxjob_5b4c112c_17961fafeaf",
 "sessionUser": "[test-user@test-company.com](mailto:test-user@test-company.com)",
 "userDefinedContext": {
  "key1": "value1",
  "key2": "v2"
 },
 "calls": [
  [null, 1, "", "abc"],
  ["abc", "9007199254740993", null, null]
 ]
}

BigQuery 将在查询中接受一个函数调用,并将成批的输入数据发送到calls数组,并将成批的请求发送到云函数。有很多有用的元数据,但我们将重点放在calls上。我们只需要创建一个for循环,遍历输入列表中的每个元素,对它们进行编码以获得 OLC,将它们添加到返回列表中,然后返回该列表。返回值必须是包含所有相应结果数据的 JSON 响应。结果代码如下:

requirements.txt文件中,您只需要添加openlocationcode到其中,这样库就可以在运行时安装。该文件的一个例子是这里的

在这里,您可以按照云控制台中的向导来安装这个功能,或者您可以克隆这个 Github 存储库,我已经为您编写了代码,只需运行以下命令:

gcloud beta functions deploy olc-encode \
--gen2 \
--runtime python39 \
--trigger-http \
--entry-point encode_olc \
--source . \
--region us-central1

它将在整个部署过程中运行,并询问您是否希望允许对该函数进行未经身份验证的调用。我建议选择“否”,因为通常默认情况下不会将其公开。如果你去控制台,你应该看到你闪亮的新功能!

云控制台关于云功能的详细信息页面。

请注意,左上角将显示“第二代”,右上角有一个链接,可将您带到支持此部署的云运行服务的详细信息页面。

翻到 testing 选项卡,它会在页面的右半部分给出一个测试命令的例子。复制并替换数据参数,如下所示:

curl -m 70 -X POST [https://](https://olc-encode-2ev77rzhka-uc.a.run.app)your-cloud-function-endpoint.run.app \
-H "Authorization:bearer $(gcloud auth print-identity-token)" \
-H "Content-Type:application/json" \
-d '{
 "requestId": "124ab1c",
 "caller": "//bigquery.googleapis.com/projects/myproject/jobs/myproject:US.bquxjob_5b4c112c_17961fafeaf",
 "sessionUser": "[test-user@test-company.com](mailto:test-user@test-company.com)",
 "userDefinedContext": {
  "key1": "value1",
  "key2": "v2"
 },
 "calls": [
  [40.74064712923708,-74.00210483032775],
  [37.53900499442756, 126.99583076901393]
 ]
}'

这是为了测试该函数是否能够处理 BigQuery 将要发送的请求格式。在终端中运行它,它应该返回以下加号代码:

向服务发送测试调用。您可以在响应中看到两个加号代码。

仅供参考,这些是纽约市谷歌办公室和首尔君悦酒店的坐标。我们现在看到函数本身是有效的。

在 BigQuery 中使用函数

从这里开始,只需再走两步就能成功。创建一个外部连接,并创建使用该连接的远程函数。

我们要做的第一件事是创建一个存储函数的数据集。通常,如果您有跨数据集使用的函数,以便在一个地方管理它们,这是一个好的做法。在 BigQuery 控制台中运行:

CREATE SCHEMA
  function_library;

然后运行以下命令创建远程连接:

bq mk \
--connection \
--display_name='remote connections' \
--connection_type=CLOUD_RESOURCE \
--project_id=$(gcloud config get-value project) \
--location=US rc-olc

如果刷新 BigQuery UI,现在应该可以看到连接。

新建立的远程连接的详细信息。

这个连接将附带一个服务帐户,BigQuery 将使用这个帐户来发布对云函数的调用。为此,您需要向该帐户授予[roles/cloudfunctions.invoker](https://cloud.google.com/functions/docs/reference/iam/roles)角色。

如果您在命令行中运行以下命令,您将获得包含服务帐户地址的输出。

bq show --location=US --connection rc-olc

您可以在云控制台中手动授予该角色,或者您可以发出以下命令来动态生成授予该角色所需的信息。

在 BigQuery 中,通过发出以下 SQL 语句,使用该连接创建一个远程函数:

CREATE FUNCTION
  function_library.olc_encode(latitude FLOAT64, longitude FLOAT64) RETURNS STRING
REMOTE WITH CONNECTION `[PROJECT ID].us.rc-olc`
OPTIONS (endpoint = '[https://ENDPOINT-URL'](https://ENDPOINT-URL'))

用您的项目 ID 和端点 URL 替换它。您可以在云控制台详细信息页面上获取该函数的 URI,也可以运行以下命令:

gcloud beta functions describe olc-encode --gen2 --region=us-central1 --format=json | jq -r '.serviceConfig.uri'

在 BigQuery 控制台中创建远程函数。

从这里开始,尝试运行SELECT function_library.olc_encode(40.74064712923708, -74.00201483032775)来验证连接正在工作。

对新功能进行快速测试。

注意:我注意到有时候在这个时候,你可能会碰到一个 403 错误。我不确定为什么会发生这种情况,但我发现手动进入控制台中的 IAM 页面,删除并重新创建 *cloudrun.invoker* 的 IAM 绑定似乎可以做到这一点。它说传播延迟可能需要 60 秒,但我不得不等待大约 10 分钟才能工作。你的经历可能有所不同,但我把这归功于 GA 前的小精灵。

让我们试着使用一个表,并给它输入更多的值。我们将使用 FAA BigQuery 机场公共数据集进行尝试。运行以下查询:

SELECT
  function_library.olc_encode(latitude,
    longitude) as plus_code,
  *
FROM
  `bigquery-public-data.faa.us_airports`

所有的加号代码!

这就是在 Cloud Functions 2.0 中使用 Python 作为 BigQuery 的远程函数调用的快速示例,同时使 Plus 代码更容易批量编码!希望这有所帮助,查询愉快!

BigQuery SQL:带有缺失日期的数据集上运行总数的演变

原文:https://towardsdatascience.com/bigquery-sql-evolution-of-the-running-total-on-a-dataset-with-missing-dates-44b6d22f7d20

用 BigQuery SQL 处理缺失值、窗口函数和嵌套查询

托德·迪默在 Unsplash 上拍摄的照片

我们的数据和分析团队最近收到了一个问题:“今年迄今为止,每家商店列出的商品数量是多少,这个数字是如何演变的?”

找到今年迄今为止列出的文章总数并不是一项复杂的分析任务。然而,当我们想展示这个数字是如何随着时间的推移而增加时,问题就出现了。

原因是源数据集中的缺少值。换句话说,在保存每个特定商店新列出的商品条目的数据集中,我们没有每个日期 记录。****

这是挑战开始的地方。

首先,我们需要弄清楚如何为每个商店填充缺失的日期。在此步骤之后,需要对每个日期和商店组合的缺失值进行正向填充。最后,作为最后一步,必须计算运行总数

在花了一些时间研究和挖掘 BigQuery SQL 教程之后,我们找到了一个简单的解决方案。

现在,我们将与您分享我们的实施方法。

如果您使用 BigQuery ,解决方案是几个步骤,或者更好地说是几个嵌套查询。😃

问题解释:从源到目标

让我们以视觉形式呈现问题,即源数据看起来如何,预期结果是什么

对于图形表示,我们使用 Looker 在时序图上显示源记录和目标结果的样本。

源数据集中记录的时间序列表示和预期结果[图片由作者提供]

从上图的第一部分(源数据集)可以看出,在所选的日期范围内,我们遗漏了每个商店级别的日期和相应的数值。

因此,我们将我们的解决方案分为三个步骤来实现目标结果,并计算在article_online_since_date日期和每个分区shop的度量new_article_count的运行总数。

自下而上的实现方法

首先,通过以下查询,我们能够创建虚拟输入数据集:

查询的结果是:

有了输入表ListedArticlesPerShop,我们就可以开始研究自下而上的解决方案来计算每个商店一段时间内的运行总数。

步骤#1:填写每个分区(车间)缺少的日期范围

BigQuery SQL 提供了一个简洁的数组函数 GENERATE_DATE_ARAY,您可以在其中指定以下输入[1]:

  • start_date —必须是日期
  • end_date —必须是日期
  • INT64_expr —确定用于生成日期的增量的参数;该参数的默认值是一天
  • date_part —必须是日、周、月、季或年。

通过GENERATE_ARRAY功能,我们能够创建一个包含每个商店完整日期范围的表格:

查询的结果如下:

成功完成步骤#1 后,我们现在可以将新创建的查询连接到输入表ListedArticlesPerShop

步骤#2:将填充了日期范围的表连接到缺少日期范围的输入表

这一步很简单,因为任务是:

  • 使用**LEFT JOIN**类型连接两个表,和
  • 从每个表中选择相应的属性;**table_a**中的ascending_dateshop,以及**table_b**中的new_article_count(现在别名为number_of_listed_articles)。

成功完成这部分任务后,我们现在可以计算运行总数了。

第 3 步:计算每个分区(车间)的总运行时间

运行总数是使用顶部查询中的窗口函数计算的:

SUM (number_of_listed_articles) OVER (PARTITION BY shop) ORDER BY (ascending_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)

现在,让我们一起来呈现热门查询:

最终的查询结果如下:

随着这最后一步,我们成功地结束了我们的任务。😃

摘要

在本文中,我们展示了如何仅使用 BigQuery SQL 来填充缺失值,并计算特定指标随时间和每个特定分区的演变模式。

我们还介绍了如何组合不同的 BigQuery 函数 : 窗口数组函数,以解决复杂的分析任务并提供所需的数据洞察。

最后,我们希望你会喜欢我们的教程,并在你的用例中找到它的用法。😃

参考文献:

[1] BigQuery SQL 文档,访问时间:2022 年 6 月 3 日,https://cloud . Google . com/big query/docs/reference/standard-SQL/array _ functions # generate _ date _ array

BigQuery SQL 优化 1:尽早过滤

原文:https://towardsdatascience.com/bigquery-sql-optimization-1-filter-as-early-as-possible-60dfd65593ff

有时,我会看到一些查询在早期准备了一个列,但却在后期使用该列进行筛选。或者用过滤掉本来可以用过滤掉的有的东西。这降低了查询速度,增加了处理成本。让我们看看对此我们能做些什么!

下面是一个示例场景,其中创建了相同的输出,但有一次查询通过之前的过滤进行了优化:

从公用表表达式(CTE)到最终输出查询(作者图片)的数据处理量

我们处理的数据越少,查询速度就越快。所以我们越早过滤掉数据越好。如果同样的事情可以在几秒钟内完成,没有人愿意等几分钟。但是有时会很难,尤其是在处理嵌套数据时——稍后会有更多介绍……

尽早过滤

查询SELECT movie, sum(tickets_sold) FROM movie_sales GROUP BY movie在以下阶段执行:

阶段 1 : 取出所有条目 FROMmovie_sales

阶段 2 : 根据您在movie列中找到的所有值对条目进行分组,并通过求和tickets_sold在组内进行聚合

阶段 3 : 输出SELECT中定义的结果(这可能是反直觉的,因为SELECT在查询中先执行,但最后执行,因为它“仅”定义了输出。在其他语言中,它会被称为print()echo

如果我们只对某些电影的销量感兴趣,可以选择两种不同的过滤方式:WHERE movie='xyz'或者HAVING movie='xyz'。但是想象一下在每个场景中,查询引擎要将从一个阶段拖到另一个阶段需要多少数据:

  • 使用HAVING,它将整个表拖到阶段 2,并且只在聚合后过滤
  • 使用WHERE,在获取数据时,它会扔掉所有不需要的东西,这样它只会将很少的数据传递到下一阶段

在查询级别,我们有 3 种过滤方式:

  1. WHEREFROM一起工作
  2. HAVINGGROUP BY一起工作
  3. QUALIFYOVER一起工作(窗口功能)

我们可以向WHERE或者甚至更早的查询/cte 移动的过滤器越多越好。或者换句话说:你应该在信息可用的最早时间点进行过滤。例如,如果过滤器需要聚合,那么我们不能将其移动到WHERE,因为只有GROUP BY聚合—不要过滤聚合在后面的WHERE,使用前面的HAVING

例如:SELECT movie, sum(tickets_sold) FROM movie_sales GROUP BY movie HAVING avg(rating)>2.5在这里,我们可以使用WITH将查询放到一个公共表表达式中,并在后面的查询中进行过滤:

WITH prep as (
  SELECT movie, sum(tickets_sold), avg(rating) as avg_rating
  FROM movie_sales 
  GROUP BY movie 
)SELECT upper(movie) as movie, tickets_sold
FROM prep
WHERE avg_rating > 2.5

有时将过滤器移至WHERE可能会更复杂,但这通常是值得的,因为它显著提高了性能——尤其是当它在高峰时间运行时(即周一早上,当周/月报告的所有数据都被处理时)。

让我们看一些更具挑战性的案例…

使用数组上的子查询进行过滤

当处理嵌套数据时,将过滤器延迟到后面阶段的模式非常诱人,因为当我们不习惯所有这些子查询时,我们只想给子查询一个别名并使用那个名称。有时查询引擎可以弥补,但并不总是那么理想。

一开始,使用数组可能会令人困惑…我已经介绍了 BigQuery:嵌套数据上的 SQL,并在 BigQuery:使用 SQL 创建嵌套数据中解释了更多的数据准备概念。对于这一部分,你应该有点熟悉那里解释的想法。

但是在这里同样适用——如果我们可以将过滤器上移一两个阶段,那么我们将节省处理时间,因为查询引擎不需要将如此多的数据从一个阶段拖到另一个阶段。

让我们看一些简单的数组数据示例:

两行嵌套结果

我们可以对成分数组进行一些聚合:

子查询中包含数组聚合的未筛选查询

但是如果我们只想吃不含泡菜的饭菜呢?我们可以将SELECT ... FROM t1包装到另一个WITH t2 AS语句中,并检查字符串listIngr是否包含子字符串"pickles"。但之后我们会把所有泡菜餐拖到这个额外的阶段t2。相反,我们应该将WHERE与子查询结合使用!

使用子查询过滤的一个好方法是使用exists(<子查询> )在子查询中设置条件

  • 因此,如果我们不想在一顿饭中有任何泡菜,子查询不应该返回任何东西!
  • 如果我们想确保饭菜中有西红柿,那么子查询应该返回一些东西
SELECT
  ...
FROM t1
WHERE
  NOT EXISTS(select 1 from unnest(ingredient) as i where i="pickles")

这里,我们为每个where i="pickles"返回 1,并且 exists() 检查子查询是否返回了任何内容。如果是, exists() 将返回 trueNOT 将其转换为 false 并发送给,在那里过滤出泡菜。

如果我们希望两个条件都为真(没有泡菜,但是有西红柿),我们将使用两个子查询,每个子查询创建自己的过滤器布尔:

SELECT
  ...
FROM t1
WHERE
  -- only meals without pickles!
  NOT EXISTS(select 1 from unnest(ingredient) as i where i="pickles")
  AND
  -- only meals with tomatoes!
  EXISTS(select 1 from unnest(ingredient) as i where i="tomatoes")

添加可读的注释,与类似AND的布尔操作符一起使用,有助于理解查询。我们将在下一节看到一个更实际的例子。

证明优化是可行的

如前所述,策略相当简单:多次运行优化和非优化的查询,并查看消耗的槽时间。插槽的性能并不一致——因此,运行查询 3 到 5 次,记录其性能并取平均值,会给你一个思路。

有一些东西可以帮助我们:

  • 禁用缓存以始终获得完整的性能结果
  • 表样不过度扩展可用插槽,降低成本。与LIMIT相比,它实际上减少了处理,但不适合随机采样,如WHERE rand()<0.1

GA4 示例

这个例子采用了基本的 Google Analytics 4 数据——产品分析的一个常见用例:

现在我们优化了没有嵌套的数据——但是如果我们想过滤某些页面标题呢?让我们来看看:

WHERE 子句中的子查询看起来并不漂亮,但通常是有效的

现在我们可以开始采样了——比较 v3 和 v4。禁用缓存并在大表上添加TABLESAMPLE SYSTEM (10 PERCENT)或从您的 _TABLE_SUFFIX 中删除日期。记录并比较平均槽时间(不是总运行时间)。

在这个简单的例子中,差异实际上是可以忽略的,因为查询引擎可以优化我们的低效率,因为我们不改变值。但是查询通常没有这个简单。相反,它们会更改值,涉及其他表、聚合、窗口函数、多个 cte 等,达到查询引擎无法补偿的程度。即使您的用例现在如此简单,它也可能在以后演变成更复杂的东西。

因此,通过将过滤器提升到早期阶段来调整您的查询将使您在周一的第一份报告更新竞赛中获得优势。通过尽早过滤数据来减轻查询引擎的负担。

重构快乐!

BigQuery SQL 优化 2:使用临时表快速获得结果

原文:https://towardsdatascience.com/bigquery-sql-optimization-2-with-temp-tables-to-fast-results-41869b15fcff

何时使用临时表而不是 WITH

查询的最大性能杀手之一是在不应该使用CREATE TEMP TABLE的情况下使用WITH!在阐明了我们应该尽早使用 过滤器之后,让我们继续讨论何时使用或避免WITH

(作者供图)

患有急性健忘症

WITH语句也叫常用表表达式 (CTE)。它们有助于消除查询的混乱,使查询更具可读性,因为它们将子查询从上下文中提取出来,并为它们命名。

SELECT a, b, c
FROM (SELECT x, y, z FROM ...)

变成了

WITH my_CTE AS (SELECT x, y, z FROM ...)SELECT a, b, c
FROM my_CTE

my_CTE看起来像一张桌子,因为它在一个表单的后面——但它不是桌子。它更像是实时指令,无论何时调用它,都会在运行时动态创建一个结果表。

每次你引用一个 CTE,它就会被执行

太疯狂了,对吧?cte 不会记住任何以前执行的结果!所以如果你这样做…

WITH ***a*** AS (...),a1 AS (SELECT aggr_1 FROM ***a***),a2 AS (SELECT aggr_2 FROM ***a***) SELECT ... FROM a1 LEFT JOIN a2

…然后你需要在读完这篇文章后立即修改这个查询,因为你通过计算两次***a***给你的查询引擎和计算槽带来了很多不必要的负载

不要忘记:临时表

那么我们该怎么办呢?***a***应该是一个临时表,因为它们会记住结果——至少在查询运行期间。

之前的查询应该是这样的:

CREATE TEMP TABLE ***a*** AS (...)WITH a1 AS (SELECT aggr_1 FROM ***a***),a2 AS (SELECT aggr_2 FROM ***a***) SELECT ... FROM a1 LEFT JOIN a2

我们只计算一次***a***,并将其用于a1a2中的两个不同聚合。

这和之前的例子没什么不同,对吧?但是它会执行得更好,因为我们省去了***a***的第二次计算。

您也不需要担心在特定数据集中创建表或删除表——它将由 BigQuery 处理,并在您的 SQL 语句运行完毕后消失。

我见过 CTE 被引用超过 5 次的查询。至少可以说,将该表重构为临时表很有帮助。为了证明它有帮助,我们可以对几次运行进行采样:

  • 准备好已优化和未优化的查询
  • 停用缓存
  • 通过减少查询的数据量来保持合理的总工作量
  • 大约同时运行两个查询 5 次,比较它们的平均槽时间

如何在创建临时表时重构旧查询

除了上面显示的变化之外,您可能会遇到希望将 cte 与临时表混合的情况。如果您真的只需要运行一次 CTE,那么它会比运行然后临时存储它稍微快一些。因此,如果我们可以跳过临时存储这一步,我们应该这样做。那么我们如何混合 cte 和临时表呢?

你可以把CREATE TEMP TABLE看作是更根本的操作。为了一起使用它们,它将简单地包含 CTE 定义,因为它们只是使子查询更可读,但本质上与子查询是一样的:

CREATE TEMP TABLE a AS ( WITH x AS (...),
  y as (...) SELECT ... FROM x LEFT JOIN y ON ...) SELECT ... FROM a ...

这个查询将使用 CTE x(在a的定义中定义)来创建临时表a

总结一下:使用 cte 整理您的 SQL 语句,使它们更具可读性。但是不要多次引用 CTE,因为每次查询引擎都会重新计算结果。在这种情况下,请使用临时表——它们会给处理成本增加额外的存储步骤,但这(从临时表中读取)可能比重新计算整个查询更便宜。

别忘了尽早将这个最佳实践与过滤结合起来

重构快乐!

简化数据工程的 BigQuery SQL 过程语言

原文:https://towardsdatascience.com/bigquery-sql-procedural-language-to-simplify-data-engineering-66ecfc47f3ac

介绍

安妮·斯普拉特在 Unsplash 上的照片

作为一名长期的 SQL 用户,我经常不得不一遍又一遍地运行相同的代码,只在 where 语句中稍作修改。在 Python 这样的编程语言中,这种复制和替换是不必要的,因为我可以创建一个函数来传入不同的参数值以重新运行相同的代码。今天我想分享如何使用 BigQuery 的过程语言来设置变量和条件逻辑以运行 SQL 语句。

声明并设置

声明语句初始化变量,而设置语句将设置变量的值。如果您需要运行除了几个值之外基本相同的 SQL 代码,这将非常有用。在下面的例子中,我们有一个名为 product 的表,它有两个字段: item_namequantity

为了得到苹果的数量,我们将使用 where 语句来查找 item_name 等于 apple (第 3 行)。

现在,假设我们想要从这个表中查询不同的水果,但是我们不想多次复制整个 SQL 语句来更改 where 语句中的水果名称。在这种情况下,我们可以使用 DECLARE 初始化一个名为 fruit_name (第 1 行)的变量,并将值设置为 lemon (第 2 行) 现在,当查询被运行时,where 语句查询 item_name 等于 fruit_name 变量即被设置为 lemon (第 6 行)。

要再次查询苹果,我们只需将 水果名称 变量从 柠檬 变回 苹果( 第 2 行)。

这是一个简单的声明和设置的例子,但是它们可以用在比我上面展示的更多的地方。重复的 SQL 语句也可以放在表函数中,这样用户就不需要用不同的 where 值多次编写相同的 SQL 代码。

如果-那么

如果满足条件,您可以使用 IF-THEN 条件语句来执行 SQL 语句。我遇到的一个典型场景是在为一个报告运行剩余的 SQL 之前检查一个表是否有最新的数据。从数据工程的角度来看,如果数据没有准备好,能够跳过代码会使事情变得容易得多。

在下面的例子中,我初始化了两个变量 rowcnt (第 1 行)和 latest_date (第 2 行)。我检查了 prod_data 表的行数,其中 daily_date 字段等于2022–11–18,并将该值设置为 rowcnt 变量(第 4 行)。

现在使用 IF-THEN 条件语句,我检查 rowcnt 是否等于 1(第 6 行),这意味着如果找到 2022–11–18 的数据,那么将显示字符串 找到最新数据 。否则, latest_date 被设置为 prod_data 表中最大日期的值(第 10 行),并且 数据延迟latest_date 的值一起显示(第 12 行)。在这种情况下,没有找到数据,并且 latest_date 字段显示2022–11–15

这也是一个简单的例子,但是您可以看到如果数据不可用,条件语句如何阻止 SQL 代码运行。

循环并离开

您可以结合使用 LOOPLEAVE 来循环,直到在运行您的 SQL 语句之前满足一个条件。使用上面的例子,我添加了一个 计数器 变量,并将值默认为-1(第 3 行)。我通过 计数器 变量(第 9 行)使用 date_sub 函数继续从 2022–11–18 减去天数,直到 rowcnt 变量等于 1。

一旦 rowcnt 等于 1,使用 LEAVE 语句(第 11 行)循环结束。

last_date 字段显示循环在 prod_data 表中找到数据时停止(第 16 行)。

特别提及 :除循环和离开外,WHILE、CONTINUE 和 FOR..IN 也可用于控制循环

最后的想法

我仅仅触及了 BigQuery 过程语言的表面,但是我希望您看到简化数据工程任务的潜力。我强烈推荐阅读文档并尝试一下过程语言。

注意:以上所有查询都是在 BigQuery 沙箱 上运行的,这对任何拥有谷歌账户的人都是免费的。

你可能也会喜欢…

</4-bigquery-sql-shortcuts-that-can-simplify-your-queries-30f94666a046> </6-bigquery-sql-functions-every-user-should-know-9ed97b1cf72e>

BigQuery UDFs 完全指南

原文:https://towardsdatascience.com/bigquery-udfs-complete-guide-181cbdaea55b

关于 Google BigQuery 用户定义函数你需要知道的一切

使用 UDF 定制您的 BigQuery 体验——照片由 Unsplash戴红帽的女孩拍摄

G oogle Cloud 的 BigQuery 是一款牛逼的数据分析甚至机器学习工具。它提供了许多现成的有用函数,但是如果你真的想深入 BQ,在某些时候你需要熟悉用户定义的函数。这些由您(用户)定义的函数将允许您简化您的 SQL 查询并更快地完成工作

我将向您展示如何使用 SQL 或 JavaScript 来定义函数,甚至如何使用来自jStat的一些统计函数——一个用于统计的 JavaScript 库。

有一个 Google Colab Notebook 为整个教程提供了易于运行的步骤,你可以重新创建和窃取你自己工作所需的所有代码。

制作玩具数据集

那只果冻兔太棒了!—Yuri Shiro taUnsplash 上拍摄的照片

由于我更喜欢编写易于复制的教程,我们将从验证我们的 Google Colab 会话开始我们的旅程:

现在我们需要一个玩具数据集。为此,我们可以使用sklearn:

我们的数据框已经准备好了。—作者截图

让我们将它制作成一个 BigQuery 表,并检查它是否如预期的那样工作:

我们可以使用%%bigquery Jupyter 神奇的函数来调用 BigQuery SQL:

BQ 的土地上一切似乎都很好——作者截图

临时功能

现在我们有了一个数据集,可以开始讨论用户定义函数或 UDF 了。我们将使用普通的 SQL 来定义带有关键字CREATE FUNCTION的函数。如果有任何困惑,欢迎在评论中询问更多细节。我们的函数将做一些超级简单的事情:如果标签是 1,将 x 乘以 100,否则,它将返回 x。下面是函数:

请注意,我们已经失去了 GitHub 语法突出显示,因为它不知道这些 BigQuery SQL 关键字…😅

现在来看看细节:

  • 我们使用了TEMP关键字,这意味着我们的函数只存在于这个查询中。我们将不能再次重复使用它。
  • move_x是我们函数的
  • xlabelid是该功能的两个输入。我已经在这里定义了它们的类型,但是我们将在后面讨论更多的类型。
  • RETURNS FLOAT64告诉 BigQuery 函数返回什么类型的对象
  • AS之后的都是函数的体。这基本上是一个简单的CASE WHEN子句。****
  • 为了调用函数,我们只需在SELECT子句中传递我们需要的 2 个输入。这发生在 7 号线。

结果是辉煌的:

请注意,new_x 在 label=1 的结尾更大—作者截图

永久功能

纳达哈巴斯Unsplash 上拍摄的照片

现在,如果您试图在一个新的SELECT子句中调用这个函数,您将会看到这个400 Function not found: move_x错误:

我们的功能消失了😱—作者截图

但是正如您所料,我们可以通过删除关键字TEMPORARY并在函数名中添加一个指示符dataset来轻松保存函数。就像表和模型一样,函数也必须存在于数据集中:

一旦我们添加了这个,我们就使用ds.move_x()再次调用我们的函数——而不用定义它。如果你想知道这个函数的作用,你可以在 BQ 界面的Routines下找到它:

如果您注意的话,您可能会注意到上面的描述字段是空的。如果你正在做一个更大的项目,养成记录你的功能的习惯。我们可以在 BQ 中使用OPTIONS关键字来做到这一点:

您可以使用DROP子句随时删除您的功能:

关于类型的注释

在上面的定义中,我们明确定义了输入和输出类型。我们告诉 BigQuery 期待一个 Float 和一个 Int,并且总是返回一个 Float。有些情况下,你可能不希望这样。在这种情况下,您可以将输入类型定义为ANY,甚至将输出类型保留为空,让 BigQuery 动态地计算出类型。这将意味着你的函数可能为不同的输入返回不同的类型,所以要小心!** BigQuery 调用这个模板化的 SQL UDF 参数。**

为了演示这是如何工作的,我制作了一个名为multiply的新函数,它有两个ANY输入,并将它应用于 3 种不同的列和标量组合。请注意,根据输入的不同,输出会有不同的类型:

new_label 是 int,因为 label 和 10 都是 int——作者截图

还要注意,我在这里混合了列和标量,这很好!

定义的 JavaScript

到目前为止,我们在所有的函数定义中都使用了 SQL。但是 BigQuery 也支持 JavaScript 进行函数定义。您所需要的只是LANGUAGE关键字和一个带有 JS 魔法的字符串,其他的一切,包括输入/输出定义,都保持不变。确保在 JS 定义中包含 return 语句:

向你证明这确实有效:

BigQuery 中的 JS—作者截图

也没有什么可以阻止你在函数中定义更小的函数,只要你仍然以 return 语句结束:

JavaScript 函数不是无限的。它们可以访问很小的内存,所以要小心传递给它们的内容。理想情况下,你不会想要传入一个百万长度的数组。用 SQL 或者更好的存储过程来实现:

** [## 用于排列测试的 BigQuery 存储过程

towardsdatascience.com](/bigquery-stored-procedure-for-permutation-test-35597d6379e4)

使用 JavaScript 库

照片由 Mariia ZakatiuraUnsplash 上拍摄

你可能会问:“JavaScript 函数有什么意义?”。它们增加了混乱,引入了一种新的语言,通常看起来并不美观。我完全同意。对于这些单行函数,你应该坚持使用 SQL 函数。但是一旦你开始使用外部 JavaScript 库,事情就变得有趣了。例如,您可以使用jStat库并在 BigQuery 中实现统计测试。让我们看看如何在 BigQuery 中使用 JS 库。

步骤 1 —获取代码

对于我们的例子,我们将坚持使用jStat。进入 GitHub ,在dist文件夹下,抓取一个你喜欢的文件。它们是相同的,但是min没有空格。点击raw并选择save link as:

作者截图

在从 GitHub 复制代码之前,一定要检查许可协议!

一旦你保存了你的文件,把它上传到谷歌云存储。您上传到的 bucket 必须与您的 BigQuery compute 在相同的区域中。记下该位置的完整路径。如果你像我一样懒,你可以使用文件旁边的汉堡选项(3 点)来复制 Gsutil 路径:

复制 blob 路径—作者截图

这会给你这样的东西:gs://YOUR_BUCKET/path_to_file.js

第二步——使用代码

现在我们有了代码的副本,可以开始在查询中使用它了。我们仍然需要制作一个 UDF ,但是 UDF 本身可以从谷歌云存储中引用一个库:

注意我们在上面是如何调用jStat.sumsprd的。另外,请注意library接受一个数组,因此您可以提供多个 JavaScript 库来使用。你也可以创建自己的函数库!

结论

谢谢你一直读到最后。希望您学到了一些关于 BigQuery 的新知识,并发现代码示例很有用。BigQuery UDFs 是一个伟大的工具,可以减少代码中的重复,提高分析系统的可靠性。想想你可以用外部库做的所有事情,或者你可以通过一个b 去掉公共步骤而去掉的代码量。

我做的一件常见的事情是,通过使用该行的 id,在 ML 预测中添加一点可重复的噪声。有了这个功能,生活就轻松多了:

行动中的随机噪音—作者截图**

如果你喜欢这篇文章,请考虑关注我, 订阅 Medium ,或者查看我的其他文章:

** </jupyter-notebooks-can-be-a-pain-but-i-️-pluto-f47913c5c16d> **

PyTorch 中的二值图像分类

原文:https://towardsdatascience.com/binary-image-classification-in-pytorch-5adf64f8c781

照片由 Unsplash 上的 Clément Hélardot 拍摄

采用迁移学习方法训练卷积神经网络

我个人使用 TensorFlow 来接近深度学习,我立即发现它非常容易和直观。很多书也用这个框架作为参考,比如 用 Scikit-Learn、Keras、Tensorflow 动手机器学习。然后我注意到 PyTorch 经常被用于学术界和工业界的研究。所以我开始使用 PyTorch 实现我已经在 TensorFlow 中开发的简单项目,以便对这两者有一个基本的了解。由于我相信最好的学习方法是向别人解释,所以我决定写这篇动手教程,用 PyTorch 开发一个用于二值图像分类的卷积神经网络。

资料组

我们将使用狗 vs 猫数据集(有免费许可证),你可以在以下链接找到:https://www.kaggle.com/datasets/biaiscience/dogs-vs-cats。数据集是免费开放使用的。我将向您展示如何创建一个模型来解决这个二元分类任务,以及如何使用它对新图像进行推理。

为了下载这个数据集,首先要做的是使用您的凭证访问 Kaggle ,然后下载 kaggle.json 文件,您可以通过单击创建新的 API 令牌按钮获得该文件。

作者图片

首先,我们需要编写允许我们上传个人 Kaggle 令牌和下载数据集的代码。

从 Kaggle 下载数据

现在我们需要将下载的文件夹解压到一个新文件夹,我们将命名为数据。接下来,我们还将分别解压缩两个子文件夹测试训练

解压缩数据

构造并填充子文件夹

为了便于管理数据集,我们创建了一个易于管理的文件夹结构。
目标是有一个名为 training 的文件夹,其中包含子文件夹 dogcat ,它们显然包含各自宠物的所有图像。
验证文件夹应该做同样的事情。

创建子文件夹结构

现在我们只需要重组数据并填充这些新创建的子文件夹。

填充子文件夹

我们来绘制一些图像示例。

绘图示例

作者图片

创建数据加载器

现在我们要做 3 件事:

  1. 让我们使用 compose 方法对数据进行预处理,这是一种简单的方法,可以对数据集应用多种预处理功能,比如标准化和数据扩充。
  2. 让我们使用 ImageFolder 创建一个 pytorch 数据集。如果子目录结构定义良好,PyTorch 会自动推断出类(就像我们的例子一样)。
  3. 使用数据加载器批量分割我们的数据。

创建数据加载器

训练阶跃函数

训练步骤总是由三个东西定义:模型、优化器损失函数。因此,让我们编写一个函数,返回在输入这 3 个实体时给出的训练步骤函数。这样我们就不用一遍又一遍的重写同样的代码了!

训练阶跃函数

建立模型

在解决大多数 Kaggle 任务时,你不会从头开始编写一个网络,而是使用一个名为 base_model 的预训练模型,并使其适应手头的任务。把 base_model 想象成一个已经学会识别图像中重要特征的模型。我们要做的是通过增加一个由其他致密层组成的来进行适配。在我们的例子中,最后的密集层将由单个神经元组成,该神经元将使用 sigmoid 激活函数,以便我们将具有为 0 或 1(猫或狗)的输出概率。

作者图片

我们必须小心不要训练之前已经训练过的基础模型。

让我们下载一个预训练模型(resnet)并冻结所有参数。然后我们将改变最后一个线性层以定制模型成为二进制分类器。记住型号和数据必须在同一个设备(GPU)上

冻结预训练模型的参数

我们现在需要定义损失优化器训练 _ 步骤

定义损失、优化程序和培训步骤。

训练模型

让我们写一下我们的培训和评估阶段。我们还将实现提前停止并在每个时期保存最佳模型。

训练和模型评估

由于我们从一个预先训练的模型开始,并且我们的二元分类任务非常简单,因此您应该很快就有一个能够非常准确地对数据集中的图像进行分类的模型。

推理

您现在可以使用该模型来预测新图像的标签!

对新图像的推断

结论

我们已经成功地建立了一个图像分类器来识别图像中的猫和狗。我必须说,在本文 的 中,我也用 Tensorflow 开发了相同的分类器,我发现 tensorflow 在这个简单的项目中使用起来更快。但是从我的角度来看,PyTorch 的优点是对从数据预处理到模型训练的各个步骤的更细粒度的控制。让我知道你的想法!

结束了

马赛洛·波利蒂

LinkedinTwitterCV

简单统计:二项式分布的解释

原文:https://towardsdatascience.com/binominal-distribution-what-analysts-should-have-known-95e6a0b56129

简要、集中的解释,并附有基本统计分布的图解。我们将从最基本的一个开始,二项分布。

迪米特里·亚基穆克在 Unsplash 上拍摄的照片

介绍

如前所述,统计学涉及到很多知识和定义。然而,当我回顾数据分析师的面试问题时,我想与您分享一些基本概念,我认为任何数据分析师在参加任何面试之前都应该完全了解这些概念。在接下来的几篇文章中,我将集中讨论的一个主题是演示和解释一些最常见的 数据分布,包括离散和连续数据。

正如我之前的帖子 简单解释基本统计学概念 一样,我通过举例说明的方式,简单解释了一些最基本的统计学。在这篇文章中,为了更容易想象,我将使用同样的方法。

离散与连续数据

有两种类型的数值数据,离散数据和连续数据。

作者图片

离散数据被理解为不同的或独立的值。它是可数、有限、数字和非负整数。通常,数据在时间和空间上是离散分布的。一些离散值的例子包括一个公司的雇员人数,一所房子里的猫的数量,或者一个小时内买咖啡的人数。不可能有 10.25 个员工,2.5 只猫,或者 100.8 个人买咖啡。离散数据也可以是包含有限数量数据值的分类数据,如一年中的季节。

同时,连续数据可以是区间内的任意值。比如,我们来考虑一下女性头发的长度。它可以在高度精确的尺度上测量,例如 30.12 厘米、30.1 厘米、30.0001 厘米或 30.012 厘米。正如我们所见,十进制数和分数代表连续数据。

每种类型的数据都有不同的分布。我们将从离散分布开始,到连续分布,从最基本的分布开始:二项分布。

作者图片

随机变量

随机变量是实验中可以取随机值的变量。假设随机变量代表随机选择的 18 个柠檬中发现的变质柠檬的数量。在这种情况下,18 个柠檬表示 18 次试验。我们可以进行不止一次实验,随机抽取 18 个柠檬,并计算有缺陷的柠檬数量。

作者图片

正如我们可以看到的,在我们的第一个实验中,有两个水果变质了。在我们的第二个实验中,有 4 个柠檬没有达到标准。在最后一个实验中,6 个柠檬变质了。这里的随机变量是 2,4 和 6。在之前的实验中发现的不合格产品的数量可以被称为一个随机变量。通常用字母 X 表示。这里,随机变量 X 取值为 2,4 和 6

二项式随机变量&二项式实验

二项式实验描述了 N 次独立试验的结果,随机变量 X 代表“N”次成功的次数。在这种情况下,x 被认为是一个二项随机变量。有两种截然不同的、互补的结果,一个“成功”和一个“失败”,每个试验都被假定会产生其中之一。因此,假设在一个测试 18 个柠檬的 18 次试验中,成功的次数(找到变质的柠檬)是一个样本空间为{0,1,2,3…18}的二项式随机变量。

此外,一个实验可以有 1 次试验、3 次试验或更多次试验。然而,如果该实验只有一次试验产生两种结果:成功或失败,则该试验被称为伯努利试验。

一个随机实验是一个二项式实验如果它满足以下要求:

  • 有一定数量的试验
  • 这些试验相互独立
  • 每次试验的结果必须属于两种可能的结果之一:“成功”(期望的结果)或“失败”。
  • 对于每次试验,都有一个恒定的成功概率 p 和失败概率 (1 — p)

二项式分布

我将继续上面的柠檬例子。众所周知,在一个挑选 18 个独立且随机的柠檬的实验中,我们挑选的变质水果的数量(成功试验)或二项式随机变量可以是 0-18 之间的任何数字。二项式概率分布是一种概率分布,显示随机变量的概率为 0-18。

作者图片

假设我们在每次试验中挑选一个柠檬,我们想看看在 18 次试验中挑选 X = {0,1,2,…18}个变质柠檬的概率。在每次试验中,挑选一个腐烂柠檬的机会是 0.3 (p=0.3)。上面的二项分布表明了这个实验的结果。

那么,公式怎么会产生这个结果呢?让我们通过一个小例子来找出在 3 次试验中有 2 个青苹果被采摘的概率,给定总共 3 个青苹果和红苹果,并且在所有试验中 p = 0.25 一致。

作者图片

将会有三种情况发生。我们可以在前两次试验中,后两次试验中,或者前两次和最后一次试验中摘两个青苹果。摘两个青苹果的概率:

P(X = 2)=(0.25)(0.25)(0.75)+(0.75)(0.25)+(0.25)(0.75)(0.25)= 0.14

作者图片

根据这个原理,我们得到了二项分布问题的一般公式,其中 k 代表 n 次试验中的成功试验,p 是成功的概率。

作者图片

将该公式应用到我们的示例中,我们可以得到类似的结果。

作者图片

二项分布的实例

我们可以在很多现实生活中看到二项分布的例子。

  • 在银行工作时,我们可以使用二项分布来确定一些信用卡交易欺诈的概率。假设某家银行的所有信用卡交易中有 0.75%是欺诈性的,通过使用二项分布,我们可以估计特定日期发生一定数量欺诈的概率。
  • 同样,我们可以使用二项式分布计算器来估计某一天收件箱中垃圾邮件的数量。
  • 等等

以上是我对如何定义二项分布的简短解释。我希望这篇文章能在某种程度上让你受益。在我的下一篇文章中再见!

参考

https://vital flux . com/binomial-distribution-defined-with-10-examples/

考古集合网络的二部图

原文:https://towardsdatascience.com/bipartite-graphs-for-archaeological-assemblage-networks-part-i-648a2f20d389

考古学中的数据科学,第一部分

数据考古学(图片由作者提供)。

我偶尔会被问到,尤其是在我决定“全力以赴”去获得另一个研究生学位之后,数据科学与考古学有什么关系。这将是一个简短系列文章的第一篇,展示我如何使用数据、统计等来深入了解这个令人惊讶的错综复杂的研究领域。基本上,当许多数据科学家试图对现在或未来做出推论时,我一直在寻找将这些方法应用于理解过去的方法。

虽然我用的是一个考古学的例子,但是这里介绍的方法可以用于其他领域的许多研究问题。二分图用于生物信息学(例如,基因表达关联)、密码学(代码解码匹配)、化学工程、推荐引擎(例如,客户-产品匹配)——即,其中实体之间的关系取决于某种中介关系的任何分析。

在这篇文章和接下来的文章中,我将介绍一些基础知识:

  • 第一部分——创建和探索二分图和单模图
  • 第二部分 —集合和图邻接的相似性度量,
  • 第三部分—图结构和社区检测方法,
  • 第四部分——地理空间网络

我将使用 R 进行编码,但是所有这些都可以用 Python 轻松完成。

介绍

从本质上讲,一个考古遗址是由相互关联的人工堆积物和特征组成的一个离散的空间区域。

不太正式的说法是旧东西散落在一个地方在那里某人做了某事。从很多方面来说,这是一个大拼图。想象你有一张照片(实物照片,而不是那些新奇的数码照片)。你不知道照片里的人是谁,也不知道他们在做什么。

现在想象一下,有人把那张照片撕成了成百上千个不规则的碎片…

  • 然后扔掉了至少一半……
  • 然后将剩余的碎片撒在一块暴露在自然环境中的空地上……
  • 然后被各种牲畜、人和动物践踏了几个世纪……
  • 后来有人来了,在他们上面建了新房子、道路或停车场!

所有这些留下来的东西很可能就是我们这些可怜的考古学家为了理解这幅画而留下来的数据。

考古学家的工作是在这些数据中寻找模式,以解释该地点随着时间的推移发生了什么。我们想知道他们在做什么事情,这通常能告诉我们过去的那些 T2 人是谁。我们通过在一个遗址区域进行非常精确控制的样本挖掘来做到这一点,然后尝试从这些样本中推断出不同种类文物的整体空间分布。

考古数据的结构

作为一名考古学家和数据科学家(排名不分先后),我的工作是从那些挖掘出的样本中获取数据,看看我能否找到——并量化——这些模式。

所以,尝试做所有这些的第一部分是,看看哪些的东西被发现在一起,哪些的地方包含最相似的的东西是否有一个模式。

更正式地说,我们试图在由一组位置和一组工件组成的集合系统中找到子集。我几年前写过一篇文章(枢机 2019 ),很详细的讲了那个地方 - 东西套系统的本质。这一切都可以归结为这样一件事:

集合符号将工件集合和 provenience context 系统定义为多个集合的和。

我承认,不太方便用户。相信我,看了我的文章(最终)就说得通了?

在图术语中,我们寻找的是同时跨越两个图的离散子图或社区(即,诱导子图或“集团”),一个是地点,一个是工件类型。换句话说,一个双模式(或“二分”)图。

对于那些不熟悉的人来说,双模图是这样一种图,其中有两种互斥的类型的顶点(即节点),不同类型的顶点之间只能存在边。在这种情况下,我们有地点事情

目前,我们只想知道哪些事物最常出现在同一个地点,哪些地点包含类似的事物。工件之间的可观察的关系由它们的同位置(即存在于相同的产地)来表示。然而,从更广泛的意义上来说,我们的目标是研究为什么这些文物会一起出现——它们是否来自同一时期,它们是否反映了特定类型的活动,它们是来自一个还是多个家庭,等等。这些问题的答案是不可观测的,但是人造物品的位置和协同位置是可观测的。

以下示例中使用的数据来自一个实际的考古遗址(一个家庭结构的集合,大约在 18 世纪中期到 20 世纪被占用),而不是一个玩具示例或模拟数据。因此,它就像真实世界的数据一样混乱——文物和产地的分组并没有清晰地分类,而是(剧透警告!)在空间组织和集合中确实存在集群。我们将走过我用来寻找和评估这些类型的模式的过程的一部分。

在 R 中制作二部图

我们可以用包igraph很容易地创建一个二分图,尽管它没有太多直接分析双模图的方法。出于我们的目的,这不是什么大问题,因为我们主要感兴趣的是使用它作为一种方式来获得每个单模图(即,分别放置事物)。

我们将使用的包是igraph(创建、操作和分析图表)、tidyverse用于数据框管理,以及ggraph用于可视化。当然,还有其他的软件包也可以工作。这些只是我已经习惯使用的。

首先,我们将从文本文件导入数据,这是工件目录的数据库。其中列出了在每个位置发现的每个工件类型的详细信息(称为“provenience”)。我们现在并不真正需要关于每个工件类型的细节,因为我们只是在寻找关联,所以我们只真正需要两个字段——provenience(LEVEL_ID)和工件类型代码(CODE)。

接下来,我们使用数据帧dat唯一的LEVEL_IDCODE来创建带有graph_from_data_frame的双模无向图(assemblages_bpg)。

这是一个无向图,因为顶点的排序或层次没有任何实际意义。这种关系是双向的——一个位置包含事物,而事物在那个位置中是等价的。为了简单起见,我们将创建一个 un- 加权二分图,它忽略了多重性(也就是说,每种工件类型的数量是如何在每个地方被发现的)。现在,我们只考虑类型的共存或同处。

然后,我们使用方法bipartite_mapping根据节点来自两列中的哪一列来给顶点分配类型。

人工制品-产地联系的二分图。

现在我们有了工件类型和位置之间的连接图,并且我们已经可以在每个顶点类型中看到一些粗略的分组(一个类型在图的顶部,一个在底部)。这个图是密集连接的,有许多节点和边。

让我们快速看一下二分igraph对象本身:

顶部的文本IGRAPH 2ea26e5 UN-B 403 2533 --告诉我们它是无向的(U),节点有一个name属性(N),是无权重的(-),并且是一个有 403 个顶点和 2533 条边的二分图(B)。在列出的边预览中,您将看到数字和文本对(例如,1726--UDB),它们是数字来源(LEVEL_ID)和文本工件类型代码(CODE)的组合。

我们可以很容易地用(shock!)方法as_incidence_matrix并查看前几行和前几列…

它只是一个二元的双模式权变表或双模式邻接表,显示了在每个位置工件类型的存在(1)或不存在(0)。

投射每种模式进行分析

直接分析二分图度量有点复杂,因为图的最常见度量(例如,中心性度量等。)由于包含这两种类型的顶点以及它们之间的依赖关系而被扭曲。更容易做的是投影每个模式的加权图,然后分析它们。

同样,igraph使这变得容易。我们使用方法bipartite_projection和设置multiplicity = TRUE,以便为每个模式创建加权图形投影。因为我们的目标是将具有最多工件类型共性的产地和最经常一起出现的类型联系起来,所以我们需要这些多样性。

您将看到我们现在有两个单模图,分别是用于 proveniences 的$proj1和用于 artifact 类型的$proj2。剩下的就是将这些投影分配给它们自己的单模态图。

让我们来看看由此产生的图形投影。

普罗旺斯投影图。

工件类型的投影图。

这两个图显然都非常密集,在这一点上,很难判断其中是否有任何模式或社区。我们需要对它们进行一些探索,找出所有这些联系在告诉我们什么。

现在我们已经有了两个单模图,你可能想知道— 为什么要大费周章地制作一个二分图呢?有几个原因。

第一,我们正在寻找的关系和关联是两个实体之间的互动。从概念上讲,这应该有一定的意义。考古上可观察到的不同类型的艺术品之间的联系主要是通过共处一地来实现的。类似地,proveniences 是相关的,因为它们包含相似的工件类型。这些关联本身本质上是二分的,所以通过用初始化我们的图表,这种关系就是解决它们的正确方法。

其次,这只是一种优雅的方式。当然,我们可以通过构建数据透视表和列联表,然后将它们转换成邻接矩阵和图,从源数据表构建各个图。然而,通过这种方式构建,我们减少了一大堆步骤,并且用几行非常简单的代码构建了我们的两个图!作为一个额外的好处,我们可以很容易地拉出那些单独的列联表,就像我们在开始时对关联矩阵所做的那样。为什么要做额外的工作?

分析投影图

如果你注意到上面,我们的两个单模图都非常密集。例如,普罗旺斯图G_assemblage_prov有 9817 条边,但只有 152 个顶点?无向图中可能的最大边数是 1/2 × |V| × (|V|-1),因此对于 152 个顶点,最大边数是 11,476,我们的图的密度约为 86%。

我们将从查看顶点的的密度分布开始,或者每个顶点连接了多少个其他顶点。

我们从分布的左偏看到,大多数顶点实际上都与大多数其他顶点相连。如果这是真的,那么就不会有任何不同的产地(即没有群落结构),86%的密度只是表明一个相对同质的网站。

现在,让我们看看这些边的权重分布。

这里我们看到了相反的情况——分布是右偏的,这意味着我们的绝大多数边缘都是由相似性形成的(即,只有少量的共同伪像)。

所以现在我们知道为什么我们的产地图如此密集——如果只需要一个或多个共同点来连接两个地点,并且大多数产地至少有一个共同点,那么一切都联系得很好。

也许某些藏物类型只是在很多地方出现?我们需要找到一种方法来细化图表,这样只有真正有意义的 T2 连接才能形成边。

接下来让我们看看我们的工件图G_assemblage_artifact。它没有那么密集(251 个顶点,可能的 31,375 条边中的 9,494 条,所以大约 30%),但是我们将再次查看顶点度数和边权重密度。

这里我们看到有点向右倾斜的程度密度分布,但是大多数是相当强连接的(~50+连接),并且注意到向右尾部的“凸起”——似乎至少我们的一些工件类型确实出现在大多数其他工件旁边。

伪影边缘权重严重向右倾斜,请注意右尾延伸的距离。这告诉我们的是,一些,但不是很多,藏物类型出现在很多地方。然而,大多数人工制品类型只共存于相当少的几个(大约 3-4 个)产地。

度和边权重的解释

我们现在大致了解了在我们的系统中发生了什么地方 - 事情集合:

  • 这两个独立的投影图都是密集的,但大多数连接都很弱。
  • 似乎很可能是每组中相对较少数量的项目产生了这些弱连接。某些工件出现在大量的 provenience 中,并且一些 provenience 包含大量不同的工件类型。
  • 这些微弱但密集的连接可能掩盖或淹没了图表中的潜在结构。

我们接下来需要做的是找到一种方法来区分信息连接和虚假连接。

接下来的步骤…

有人可能会认为,显而易见的下一步将是简单地开始删除最薄弱的边缘,在某些情况下,这将是要走的路。请记住,我们的目标实际上是分析工件和位置之间的关系(即连接边)。单个顶点的强度不是目标,而是我们想要识别从它们的关系中得到的顶点的社区。[3]

发生的事情是,我们用来设计单模态图的方法只是简单地列出了个体产地和人工制品类型重合的次数。问题是一个共享的工件可以连接位置,一个共享的位置连接工件。这不是我们想要的。我们想要链接具有最相似的同现集合轮廓的顶点。换句话说,我们需要查看集合的交集,它们的不同之处是为了确定相似之处。

为此,我们需要一种不同的投影方法。在下一部分中,我们将研究一些基于集合的相似性度量,并看看当我们以这种方式投影单模图时会发生什么。

参考

红衣主教 j .斯科特。2019."集合、图形和我们能看见的东西:用于经验站点内分析的形式组合本体论."计算机在考古学中的应用杂志 2(1):56–78。https://doi.org/10.5334/jcaa.16

笔记

  1. 佐治亚理工学院计算学院、谢勒商学院和工程学院的分析理学硕士。我强烈推荐这个节目!
  2. 我的考古同事有时也会反过来问我——考古学与数据科学有什么关系。定量和计算(或“数字”)考古学已经存在了很长一段时间,但我们仍然是少数。不过,这是另一篇文章的主题。
  3. 精通图论的读者可能已经注意到,我们的最终目标是边介数和中心性度量。

基于机器学习的鸟类分类

原文:https://towardsdatascience.com/bird-species-classification-with-machine-learning-914cbc0590b

数据科学

根据基因和位置预测鸟的种类

照片由香农·波特Unsplash 上拍摄

像鸟一样?比如数据科学?

你会喜欢这个挑战的!

问题陈述

科学家们已经确定,一种已知的鸟类应该分为 3 个不同的独立物种。这些物种是该国特定地区特有的,必须尽可能精确地跟踪和估计它们的数量。

因此,一个非盈利的保护协会承担了这项任务。他们需要能够根据野外工作人员在野外观察到的特征,记录他们遇到了哪些物种。

使用某些遗传特征和位置数据,你能预测已经观察到的鸟的种类吗?

这是一个初级水平的练习比赛,你的目标是根据属性或位置预测鸟类的种类。"

来源

你现在有了一个明确的目标。

目标🥅

根据属性或位置预测鸟的种类(A、B 或 C)

现在让我们来看看数据

数据💾

通过注册获得本次数据科学竞赛的数据。

📂 **train**
├── training_target.csv
├── training_set.csv
└── solution_format.csv📂 **test**
└── test_set.csv

数据被方便地分成训练和测试数据集。

在每一次训练和测试中,你会得到位置 1 到 3 的鸟的数据。

下面来看看training_set.csv的前五行

training_settraining_target可以与‘id’柱连接。

下面是给定列的数据字典

**species**     : animal species (A, B, C)
**bill_length** : bill length (mm)
**bill_depth**  : bill depth (mm)
**wing_length** : wing length (mm)
**mass**        : body mass (g)
**location**    : island type (Location 1, 2, 3)
**sex**         : animal sex (0: Male; 1: Female; NA: Unknown)

然后,看着solution_format.csv

现在你对目标有了一个想法,对给你的数据有了一些了解,是时候动手了。

本文代码→ 深注

加载库

接下来,我们加载一些用于可视化和机器学习的基本库。

缺少数据帮助函数

加载数据

首先,我们使用read_csv函数加载训练和测试数据。

我们还将training_set.csv(包含特征)与training_target.csv(包含目标变量)合并,形成训练数据。

在这里,我手动保存了列名,包括数字和分类,还保存了目标列。

这使我可以很容易地引用我以后想要的列

探索性数据分析

有趣的部分到了,可视化数据。

info函数中,似乎有丢失的值,我们可以看到位置和性别应该是分类的,所以我们稍后必须进行一些数据类型转换。

数字列

绘制数值变量的直方图,我们看到

  • bill_depth 在 15 和 19 左右达到峰值
  • 比尔的长度在 39 岁和 47 岁左右达到顶峰
  • 翼长峰值在 190°和 216°左右
  • 质量是右偏的

分类列

让我们首先想象一下我们的目标类。

我们看地点和物种似乎是为了它们各自的地点和物种(loc2 &物种 C,loc3 &物种 A)。

我们也看到雌性鸟比雄性鸟稍微多一点。

根据物种图,我们手里似乎有一个不平衡的职业,因为物种B比物种AC少得多

为什么这是一个问题?

模型会偏向于样本量较大的类。

发生这种情况是因为分类器具有关于具有更多样本的类的更多信息,因此它学习如何更好地预测那些类,而它在较小的类中保持较弱。

在我们的例子中,物种AC将比其他职业更容易被预测到。

这里有一篇关于如何处理这个问题的很棒的文章。

缺少值

使用助手功能,似乎有大量的bill_lengthwing_length数据丢失

我们还可以使用热图来可视化该列中缺失的数据。

估算分类值

先看看我们的分类变量里有多少缺失变量。

让我们使用简单的估算器来处理它们,用最频繁的值替换它们。

如您所见,通过most_frequent策略,缺失值被估算为 1.0,这是最常见的。

估算数字列

特征预处理和工程

我们必须将分类特征转换成数字格式,包括目标变量。

让我们使用 scikit-learn 的标签编码器来完成这项工作。

这里有一个使用LabelEncoder()标签列的例子

通过首先拟合它,我们可以看到映射看起来像什么。

使用fit_transform直接为我们转换

对于其他包含字符串变量(非数字)的列,我们也进行同样的编码

我们还将分类特征转换成pd.Categorical数据类型

这是变量的当前数据类型。

现在,我们通过将一些变量除以另一个变量来形成比率,从而创建一些额外的特征。

我们不知道它们是否有助于提高模型的预测能力,但试试也无妨。

这是目前火车场景的样子

构建模型

列车测试分离

现在该建立模型了,我们先把它拆分成 X(特征)和 y(目标变量),再拆分成训练集和评估集。

训练是我们训练模型的地方,评估是我们在使模型适合测试集之前测试模型的地方。

我们使用[train_test_split](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html)将我们的数据分成训练集和评估集。

决策树分类器

对于本文,我们选择一个简单的基线模式,即决策树分类器

一旦我们适应了训练集,我们就可以根据评估数据进行预测。

模型性能

让我们看看简单的决策树分类器是如何工作的。

对于不平衡的数据集,99%的准确率可能毫无意义,因此我们需要更合适的指标,如精度、召回率和混淆矩阵。

混淆矩阵

让我们为我们的模型预测创建一个混淆矩阵。

首先,我们需要获得标签编码器给出的类名和标签,这样我们的图就可以显示标签名。

然后我们绘制一个非标准化和标准化的混淆矩阵。

混淆矩阵告诉我们,它预测了更多的 A 类和 C 类,这并不奇怪,因为我们有更多的样本。

它还表明,该模型预测了更多的 A 类,而它应该是 B/C 类。

分类报告

分类报告测量来自分类算法的预测质量。

它告诉我们有多少预测是对的/错的

更具体地说,它使用真阳性、假阳性、真阴性和假阴性来计算精确度、召回率和 f1 值

有关这些指标的详细计算,请查看简化的多级指标,第二部分:由 Boaz Shmueli 撰写的 F1 分数

直观上,精度是分类器不将一个阴性(错误)样本标记为阳性(正确)的能力,召回是分类器找到所有阳性(正确)样本的能力。

文档中,

  • "macro"简单地计算二进制指标的平均值,对每个类别赋予相等的权重。在非频繁类仍然重要的问题中,宏平均可能是突出其性能的一种方法。另一方面,所有类都同等重要的假设通常是不正确的,因此宏平均会过度强调不经常出现的类的典型低性能。
  • "weighted"通过计算二进制指标的平均值来说明类别不平衡,在二进制指标中,每个类别的分数根据其在真实数据样本中的存在情况进行加权。

没有单一的最佳指标,这取决于您的应用。应用程序以及与不同类型的错误相关的实际成本将决定使用哪种度量标准。

特征重要性

让我们也画出特性的重要性,看看哪些特性更重要。

从特征重要性来看,mass似乎最擅长预测物种,其次是bill_length

其他变量在分类器中似乎不重要。

我们看到特征重要性是如何在我们的决策树分类器的可视化中使用的。

在根节点中,如果质量低于大约 4600,那么它检查bill_length,否则它检查bill_depth,然后在叶子处它预测类。

根据测试数据进行预测

首先,我们执行相同的预处理+特征生成

然后,我们可以使用我们的模型进行预测,并连接 ID 列以形成解决方案文件。

请注意,物种值是数值,我们必须将其转换回字符串值。有了 fit 早期的标签编码器,我们可以做到这一点。

保存预测文件。

后续步骤

基础模型不足以做出好的预测;下面是改进给定方法的一些后续步骤。

  1. 更多特征预处理和工程
  2. 使用交叉验证来更好地衡量性能。
  3. 测试其他算法,如 KNN,SVM,XGBoost,Catboost 等。
  4. 加入 bitgrit discord 服务器 与其他数据科学家讨论挑战

超棒的 Kaggle 笔记本

这里有 3 个笔记本作为参考,告诉你如何在这次挑战中提升自己的水平

  1. 表格数据的数据科学:高级技术
  2. 信用欺诈—处理不平衡的数据集
  3. 房价:Lasso,XGBoost,以及详细的 EDA

感谢阅读!

喜欢这篇文章吗?这里有三篇文章你可能会喜欢:

数据 L 许可

数据由CC-0授权按照 帕尔默站【LTER】数据策略 LTER 数据访问策略获取 I 类数据

喜欢我的写作吗?用我的 推荐链接 加入 Medium ,你将直接支持我🤗

https://benedictxneo.medium.com/membership

比特币的技术贡献:解决拜占庭将军问题

原文:https://towardsdatascience.com/bitcoins-technical-contribution-solving-byzantine-general-s-problem-f0449973437c

2008 年比特币白皮书如何解决经典的计算机科学共识问题

图 1:贝尔伯里勋爵对拜占庭将军问题的阐释,CC BY-SA 4.0 / 来源

问题陈述

拜占庭将军问题(BGP)是根据一个寓言而命名的,在这个寓言中,许多将军需要合作并同意同时一起攻击一个城市,以赢得战斗(图 1-左)。如果任何一个将军在最后一刻撤退,战斗就失败了(图 1-右)。因此,确保信使共享准确的信息以及没有流氓将军是至关重要的。在缺乏可信的中央权威的情况下,这是很难实现的。

Leslie Lamport、Robert Shostak 和 Marshall Please 在他们 1982 年的论文中把这个问题命名为拜占庭将军问题,以一种用户友好的方式表示分布式计算机系统问题。

实际问题是关于计算机系统的分布式网络中的一致性。维护一个正常运行的系统网络变得很困难,因为要就哪些系统正在工作(并留在网络中)和哪些系统没有工作(即需要从网络中移除)达成共识。系统之间不可靠的通信渠道和误报系统加剧了这一挑战。

解决 BGP 还可以指导我们构建没有单点故障或不需要可信中央实体的机器学习解决方案。例如,在模型训练期间,单个服务器托管模型的参数,而多个工人训练模型。这篇论文,描述了一种构建容错分布式拜占庭机器学习解决方案的方法。

比特币的技术创新在于,它找到了一种方法,让分布式节点网络就哪些交易应该进入分布式账本(区块链)达成共识,而不需要可信的中央实体。这样,它解决了 BGP 的实现问题。比特币的笔名作者中本聪创造性地结合了已有的密码学和共识机制来解决问题。

放弃

这篇文章并不意味着任何形式的财务建议。这是一篇关于解决计算机科学问题的有趣方法的综述。

比特币

比特币白皮书的第一行写道:

“电子现金的纯点对点版本将允许在线支付直接从一方发送到另一方,而无需通过金融机构。”
来源:https://www . ussc . gov/sites/default/files/pdf/training/annual-national-training-seminar/2018/Emerging _ Tech _ bit coin _ crypto . pdf

让我来解开比特币的主要组成部分:

  1. 比特币软件(开源&版本),执行节点、矿工和比特币令牌的操作规则。
  2. 比特币令牌,可以通过挖掘生成的原生令牌单位(最多 2100 万),在比特币区块链的钱包地址之间进行交换。
  3. 分布式分类账,自 2009 年 1 月第一笔交易以来所有交易(以区块组织)的数据库(区块链)的相同副本。
  4. 分布式网络的 节点(计算机)运行比特币软件和一份分布式账本。它们验证和接受有效的事务,并将它们传播到其他对等节点。
  5. 工作证明(PoW) ,一种加密证据(以有效哈希摘要的形式),表明矿工已经花费了一定量的计算处理(挖掘工作)来解决加密问题,以获得向比特币区块链添加新交易块的权利。比特币有一个内置的难度调整功能,可以改变有效哈希要求(nonce的长度),这样平均每 10 分钟就会产生新的区块。PoW 是一种能量密集型活动,因为它必须不断随机生成新的散列,直到它满足需求。
  6. 挖掘者,执行持续计算处理以解决重复出现的密码问题的竞争方。为区块解决问题的第一个矿工(或池)收取交易费和一些新创建(开采)的比特币作为奖励。挖掘能力以哈希速率的形式来衡量,即每秒生成多少个哈希。

比特币最重要和最新颖的特点是结合了加密技术、分散的节点网络和工作证明共识机制。

比特币中使用的加密技术

比特币在其实现中使用以下已知的加密技术。

  1. 散列法
  2. 基于非对称加密的数字签名

散列法(SHA-256)

图 2:块头的 Hash + nonce 上的 SHA-256 哈希运算

哈希是唯一明文数据到唯一摘要的单向转换。将不可能逆转该操作,即从摘要生成明文。比特币矿工使用 SHA-256 哈希算法不断生成新的哈希,目标是获得有效的哈希以获得区块奖励。

哈希从当前块头生成,然后与一个名为 nonce 的变量值结合生成一个新的哈希。挖掘过程需要不断改变随机数并重新散列以尝试得到满足要求的散列。

块头本身由前面块的散列和一个称为 Merkle root 的特殊散列组成,Merkle root 包含块中所有事务的散列。因此,我们得到了一个块链,一直链接到第一个块,散列层不断增加。哈希算法确保了比特币区块链中新交易的安全性和整个交易的不变性。随着在任何事务的块之后挖掘更多(例如,6 个)块,在该块和先前块中伪造甚至很小的条目变得越来越不可能。

基于非对称加密的数字签名

图 3:使用非对称加密和散列操作的事务签名和验证过程

在非对称加密(也称为公钥加密)中,每个交易方都有一个密钥对,即私钥和公钥。私钥不应该与任何人共享,而公钥(即由此生成的钱包地址)可以与交易方共享。来自同一个密钥对的这两个密钥在数学上以这样一种方式联系在一起,即公钥可以从私钥中导出,但反之则不行。消息(例如,比特币支付记录)只能由拥有私钥的所有者(发送者)签名,但是它可以由任何节点或任何能够访问公钥的人(即,在区块链中可见的人)来验证。例如,如果 Alex 想要向 Bob 发送比特币,则 Alex 将向节点发布地址为 Bob 的钱包的期望数量的比特币的数字签名交易。只要 Alex 是唯一拥有私钥的人,只有 Alex 可以授权该金额。因为 Alex 的公钥在区块链上可用,所以任何人都可以验证该交易。

解决办法

现在,让我们将比特币实现映射到 BGP 问题。BGP 的将军类似于运行比特币软件的节点共识、将军试图达成的共识,就像比特币节点决定将哪块交易纳入区块链一样。所有节点运行的比特币软件版本可以:

1.验证交易(即验证数字签名)

2.只接受和传播来自能够第一个获得有效散列并证明它做了必要工作的挖掘器的新块。除了通过使用以下输入连续生成新的散列之外,没有办法猜测有效散列:固定/当前块头和称为 nonce 的变量的新值。

因此,共识由 PoW 机制和分布式节点网络来解决,该分布式节点网络接受来自解决不可伪造的密码问题的挖掘者的块。

但是,如果有:

  1. 不诚实的节点
  2. 不诚实的矿工

如果由于流氓节点,不同节点组接受的块集合存在差异,比特币具有接受最长链的功能。因此,对于进入区块链的无效交易,流氓节点在接受/形成更长的链方面将不得不多于诚实节点。截至目前(2022 年 11 月),将接管 7000 个(来源)协同流氓节点劫持比特币网络。

类似地,不诚实的矿工也可以发起攻击,并试图用伪造的交易(例如,双重消费、撤销、审查交易等)潜入区块。)入链。这也需要超过 50%(即 51%的攻击)的挖掘者(总计算能力)来猜测散列并对网络发起攻击。目前挖掘哈希率超过 2 亿(来源)。矿工被激励去合作(通过奖励和交易费),而不是发起攻击。然而,量子计算机在未来可能会带来风险。

在这里看我关于量子计算的相关帖子。

最终,节点比挖掘者具有更大的影响力,因为节点可以验证交易,并且如果挖掘者向它们提供欺诈性块,则拒绝它们。因此,只要诚实的节点占大多数,网络就能保持安全。

摘要

表 1 显示了拜占庭将军问题的三种实现之间的比较。

表 1:拜占庭将军问题实现的比较

比特币白皮书及其实现解决了没有任何中央治理实体的分布式系统所经历的共识问题。这样,它解决了难以捉摸的拜占庭将军的问题。

资源

  1. https://en.wikipedia.org/wiki/Byzantine_fault
  2. 比特币核心软件源代码—https://github.com/bitcoin/bitcoin
  3. 比特币白皮书—https://bitcoin.org/bitcoin.pdf
  4. https://en.wikipedia.org/wiki/Bitcoin
  5. https://www . Microsoft . com/en-us/research/publication/拜占庭将军问题/
  6. https://www . Microsoft . com/en-us/research/uploads/prod/2016/12/The-Byzantine-Generals-problem . pdf
  7. https://en.wikipedia.org/wiki/Hash_function
  8. https://en.wikipedia.org/wiki/Merkle_tree
  9. https://en.wikipedia.org/wiki/SHA-2
  10. https://en.wikipedia.org/wiki/Public-key_cryptography
  11. https://en.wikipedia.org/wiki/Digital_signature
  12. https://en.wikipedia.org/wiki/Proof_of_work
  13. https://en.wikipedia.org/wiki/Quantum_cryptography
  14. https://dci.mit.edu/bitcoin-security-initiative
  15. https://dci.mit.edu/51-attacks
  16. 真正分布式拜占庭机器学习 El-Mahdi El-Mhamdi 等人,2020。纽约州纽约市 ACM,https://doi.org/10.1145/3382734.3405695

Python 中的黑盒超参数优化

原文:https://towardsdatascience.com/black-box-hyperparameter-tuning-in-python-478c10adc959

Python 中暴力和黑盒优化方法的比较

图片由 PhotoMIX 公司像素上拍摄

在机器学习中,超参数是用于控制机器学习模型的学习过程的值。这不同于从数据中学习的内部机器学习模型参数。超参数是机器学习训练数据之外的值,其确定机器学习模型性能的最优性。每个唯一的超参数集对应于一个唯一的机器学习模型。对于大多数现有技术的机器学习模型,所有可能的超参数组合的集合可能变得相当大。幸运的是,大多数机器学习模型包都带有默认的超参数值,可以实现不错的基线性能。这意味着数据科学家或机器学习工程师可以使用开箱即用的模型,而不必一开始就担心超参数选择。这些默认模型通常优于数据科学家或工程师能够手动测试和选择的模型。

相反,为了优化性能,数据科学家或机器学习工程师必须测试不同于默认值的超参数的各种值。手动执行会变得非常麻烦和低效。由于这个原因,已经设计了许多算法和库来自动化超参数选择的过程。超参数选择是优化中的一个练习,其中目标函数由模型表现的有多差来表示。优化任务是找到一组最佳参数,使机器学习模型的性能下降到最低程度。如果你找到性能最差的机器学习模型,那就对应着性能最好的模型。

文献跨越、蛮力技术和黑盒非凸优化为优化提供了丰富的空间。强力优化是彻底搜索所有可能的超参数组合的最佳参数集的任务。如果有可能彻底搜索超参数空间,它将给出给出全局最优解的超参数集。不幸的是,就计算资源和时间而言,穷举搜索超参数空间通常是不可行的。这是因为超参数调整机器学习模型属于非凸优化的范畴。这是一种优化类型,其中找到全局最优是不可行的,因为它可能陷入几个次优“陷阱”中的一个,也称为局部最小值,这使得算法难以搜索超参数的整个空间。

强力优化的替代方法是黑盒非凸优化技术。黑盒非凸优化算法寻找次优的解决方案,局部最小值(或最大值),这是基于一些预定义的度量足够优化。

Python 有暴力优化和黑盒优化的工具。模型选择模块中的 GridSearchcv 支持强力优化。 RBFopt python 包是 IBM 开发的黑盒优化库。它通过使用径向基函数来构建和优化被优化函数的代理模型。它很有用,因为它没有对被优化函数的形状或行为做任何假设。它已被用于优化复杂的模型,如深度神经网络。

构建、测试和比较模型超参数和机器学习算法的任务本质上通常是协作性的。考虑到这一点,我将与 DeepNote 合作,这是一个协作数据科学笔记本,使数据科学家可以轻松地在机器学习和数据分析任务上合作。在这里,我们将介绍如何应用这些优化工具来调整分类模型的超参数。我们将考虑预测客户是否不会重复购买的监督机器学习任务,这被称为搅动。我们将使用 Kaggle 上公开的虚构的电信公司流失数据集。数据集在 Apache 2.0 许可下可以自由使用、修改和共享。

读入电信客户流失数据

首先,让我们导入 python pandas 库,将我们的数据读入 pandas 数据框,并显示前五行数据:

进口熊猫作为 pd

df = pd.read_csv("telco_churn.csv")

作者截图

我们看到该数据包含诸如客户 ID、性别、老年公民身份等字段。如果我们将光标悬停在左侧的单元格输出上,我们将看到以下内容:

作者截图

我们看到我们有“客户流失”字段,它对应于客户是否重复购买。值“否”表示客户重复购买,值“是”表示客户停止购买。

我们将建立一个简单的分类模型,将性别、老年人、互联网服务、设备保护、每月费用和总费用作为输入,并预测客户是否会流失。为此,我们需要将我们的分类列转换为机器可读的值,这些值可以作为输入传递给我们的机器学习模型。让我们为性别、老年人、互联网服务和设备保护这样做:

转换分类列

#convert categorical columns
df['gender'] = df['gender'].astype('category')
df['gender_cat'] = df['gender'].cat.codes
df['SeniorCitizen'] = df['SeniorCitizen'].astype('category')
df['SeniorCitizen_cat'] = df['SeniorCitizen'].cat.codes
df['InternetService'] = df['InternetService'].astype('category')
df['InternetService_cat'] = df['InternetService'].cat.codes
df['DeviceProtection'] = df['DeviceProtection'].astype('category')
df['DeviceProtection_cat'] = df['DeviceProtection'].cat.codes

让我们显示结果列:

df[['gender_cat', 'SeniorCitizen_cat', 'InternetService_cat', 'DeviceProtection_cat']].head()

作者截图

我们还必须对客户流失列做一些类似的事情:

df['Churn'] = df['Churn'].astype('category')
df['Churn_cat'] = df['Churn'].cat.codes

接下来我们需要做的是清理 TotalCharges 列,用 NaN 替换无效值,并用 TotalCharges 的平均值输入 NaNs

df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], 'coerce')
df['TotalCharges'].fillna(df['TotalCharges'].mean(), inplace=True)

现在让我们准备输入和输出。我们将定义一个变量 X,它将是一个包含列 gender、SeniorCitizen、InternetService、DeviceProtection、MonthlyCharges 和 TotalCharges 的序列。我们的输出将是一个名为 Y 的变量,它将包含客户流失值:

#define input and output
X = df[['TotalCharges', 'MonthlyCharges', 'gender_cat', 'SeniorCitizen_cat', 'InternetService_cat', 'DeviceProtection_cat']]
y = df['Churn_cat']

接下来,让我们拆分数据进行训练和测试。我们将使用 scikit-learn 中模型选择模块的训练测试分割方法:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

用默认参数建模

首先,我们将构建一个随机森林分类模型。随机森林算法是一种基于树的集成模型算法,它使用决策树的组合来防止过度拟合。让我们从 scikit-learn 中的 ensemble 模块导入随机森林类:

from sklearn.ensemble import RandomForestClassifier

接下来,让我们定义我们的随机森林分类器模型对象,并使我们的模型适合我们的训练数据。通过将 RandomForestClassifier 的参数留空,我们定义了一个具有预定义默认参数的模型:

model = RandomForestClassifier()
model.fit(X_train, y_train)

让我们打印模型的默认参数值。为此,我们只需在模型对象上调用 get_params()方法:

model.get_params()

作者截图

我们将使用精度来评估我们的分类模型。对于不平衡分类问题,如流失预测,这是一个很好的选择。让我们来评估一组目标的精确度:

from sklearn.metrics import precision_scorey_pred_default = model.predict(X_test)precision = precision_score(y_test, y_pred_default)precision

现在让我们看看如何应用强力网格搜索来找到最佳随机森林分类模型。

使用 GridSearchCV 进行强力优化

诸如 GridSearchCv 之类的强力搜索方法通过在整个搜索空间中穷举搜索最佳超参数集来工作。首先,让我们从 scikit-learn 中的模型选择模块导入 GridSearchCV 方法:

from sklearn.model_selection import GridSearchCV

让我们也定义一个字典,我们将使用它来指定我们的参数网格。让我们定义一个估计值范围(决策树从 10 到 100),决策树的最大深度从 5 到 20,最大特征等于 sqrt,标准等于基尼指数(这是用于在决策树中划分组的度量标准:

params = {'n_estimators': [10, 100],
'max_features': ['sqrt'],
'max_depth' : [5, 20],
'criterion' :['gini']}

接下来,让我们用参数字典定义网格搜索对象:

grid_search_rf = GridSearchCV(estimator=model, param_grid=params, cv= 20, scoring='precision')

并使对象符合我们的训练数据:

grid_search_rf.fit(x_train, y_train)

从那里我们可以显示最佳参数:

gscv_params = grid_search_rf.best_params_gscv_params

用最佳参数重新定义我们的随机森林模型:

gscv_params = grid_search_rf.best_params_model_rf_gscv = RandomForestClassifier(**gscv_params)model_rf_gscv.fit(X_train, y_train)

作者截图

让我们来评估一组目标的精确度:

y_pred_gscv = model_rf_gscv.predict(X_test)precision_gscv = precision_score(y_test, y_pred_gscv)precision_gscv

作者截图

我们看到我们的精度实际上优于默认值。虽然这很好,但对于大范围的参数值和较大的数据集,这种方法可能会变得难以处理。黑盒优化和贝叶斯优化等替代方法是超参数调优的更好选择。

使用 RBFopt 进行黑盒优化

现在让我们考虑使用 RBFopt 的黑盒超参数优化。RBFopt 的工作原理是使用径向基函数来构建和优化被优化函数的代理模型。这通常用于没有封闭形式表达式且有许多山峰和山谷的函数。这与众所周知的具有封闭形式表达式的简单函数形成对比,例如二次函数或指数函数。

首先让我们安装 RBFopt:

%pip install -U rbfopt

作者截图

接下来,我们需要为我们的模型参数定义一个上限和下限列表。下限列表将包含估计器数量的 10 和最大深度的 5。上限列表将包含 100 个估计数和 20 个最大深度:

lbounds = [10, 5]ubounds = [100, 20]

接下来让我们导入 RBFopt 和交叉验证方法:

import rbfoptfrom sklearn.model_selection import cross_val_score

接下来我们需要定义我们的目标函数。它将接受 n_estimators 和 max_depth 的输入,并为每组参数建立多个模型。对于每个模型,我们将计算并返回精度。我们试图找到使精度最大化的 n 估计量和 max_depth 的一组值。由于 RBFopt 找到最小值,为了找到使精度最大化的参数集,我们将返回精度的负值:

def precision_objective(X):
    n_estimators, max_depth = X
    n_estimators = int(n_estimators)
    max_depth = int(max_depth)
    params = {'n_estimators':n_estimators, 'max_depth': max_depth}
    model_rbfopt = RandomForestClassifier(criterion='gini', max_features='sqrt', **params)
    model_rbfopt.fit(X_train, y_train)
    precision = cross_val_score(model_rbfopt, X_train, y_train, cv=20, scoring='precision')
    return -np.mean(precision)

接下来,我们指定运行、函数调用和维度的数量:

num_runs = 1max_fun_calls = 8ndim = 2

这里我们只运行 8 个函数调用。如果你希望运行 10 个以上的函数调用,你必须安装 bonminipopt 软件包。安装说明可以在各自链接的 GitHub 页面上找到。

现在,让我们指定我们的目标函数并运行 RBFopt:

obj_fun = precision_objectivebb = rbfopt.RbfoptUserBlackBox(dimension=ndim, var_lower=np.array(lbounds, dtype=np.float), var_upper=np.array(ubounds, dtype=np.float), var_type=['R'] * ndim, obj_funct=obj_fun)settings = rbfopt.RbfoptSettings(max_evaluations=max_fun_calls)alg = rbfopt.RbfoptAlgorithm(settings, bb)

作者截图

并将目标值和解决方案存储在它们各自的变量中:

fval, sol, iter_count, eval_count, fast_eval_count = alg.optimize()obj_vals = fval

然后,我们将整数值解存储在字典中:

sol_int = [int(x) for x in sol]
params_rbfopt = {'n_estimators': sol_int[0], 'max_depth': sol_int[1]}
params_rbfopt

作者截图

我们看到,RBFopt 分别为 n_estimators 和 max_depth 找到了最佳值 81 和 5。

然后将这些最佳参数传递到我们的新模型中,并拟合我们的训练数据:

model_rbfopt = RandomForestClassifier(criterion=’gini’, max_features=’sqrt’, **params_rbfopt)model_rbfopt.fit(X_train, y_train)

并评估精度:

y_pred_rbfopt = model_rbfopt.predict(X_test)precision_rbfopt = precision_score(y_test, y_pred_rbfopt)precision_rbfopt

作者截图

我们看到,通过更快的优化算法,我们在精度上有了轻微的提高。当您有大的超参数搜索空间时,这尤其有用。

这篇文章中使用的代码可以在 GitHub 上获得。

结论

对于每个数据科学家来说,很好地理解超参数调整机器学习模型的可用工具是必不可少的。虽然大多数机器学习算法的默认超参数提供了良好的基线性能,但超参数调整通常是必要的,以看到基线性能的改善。强力优化技术是有用的,因为它们彻底地搜索超参数空间,这将保证从默认参数改善基线性能。不幸的是,蛮力优化在时间和计算方面是资源密集型的。出于这些原因,更有效的黑盒优化方法,如 RBFopt,是强力优化的有用替代方法。RBFopt 是一种非常有用的黑盒技术,应该成为每个超参数优化数据科学工具包的一部分。

使用 mtalg 在 Python 中实现极快的代数和随机数

原文:https://towardsdatascience.com/blazing-fast-algebra-and-random-numbers-in-python-with-mtalg-aa50ac8ffd3

用于多线程代数和伪随机数生成的 python 工具

来自 mtalg 的图像

数据科学家和研究人员经常需要执行快速有效的数值计算。因此,处理大型数据结构需要能够通过多重处理或多线程来利用所有可用的计算资源(参见这篇伟大的文章来复习)。这非常重要,以至于像numpy这样的数值库自动支持线性代数运算上的多线程。

然而,有点令人惊讶的是,numpy没有为元素操作和(伪)随机数生成提供现成的多线程功能。这些通常是主要的瓶颈,例如当执行大规模蒙特卡罗模拟时,用于通过 MCMC 的贝叶斯参数估计等。

Python 库[mtalg](https://github.com/WWakker/mtalg) [1]提供了多线程的基于元素的函数和多线程的随机数生成,击败了大多数其他库,包括numexpr和使用numba的即时编译(参见下面的基准)。

多线程代数

安装了带有pip install mtalg的库后,我们可以导入mtalg并使用它的内置函数:

请注意,默认情况下,操作在第一个参数中就地执行。因此,mtalg.add(a,b)相当于a = a + b(只是方式更快!).这种行为可以通过可选参数direction来覆盖:例如mtalg.sub(a, b, direction='right')将等同于b = a - b(注意这与mtalg.sub(b, a)不同,后者将等同于b = b — a)。

默认情况下,mtalg将线程数量设置为可用 CPU 内核的数量,但是该参数可以作为mtalg.set_num_threads(6)被覆盖。同样,我们可以将当前设定值检查为mtalg.get_num_threads()

随机数生成

随机数生成可以按如下方式执行:

mtalg.random提供了一系列不同的发行版来进行采样,所有发行版都保持了类似 numpy 的语法。

mtalg目前支持从以下分布中采样:、二项式、卡方、指数、f、伽马、几何、gumbel、超几何、整数、拉普拉斯、逻辑、对数正态、对数序列、负二项式、非中心卡方、非中心 f、正态、帕累托、泊松、幂、随机、瑞利、标准柯西、标准指数、标准伽马、标准正态、标准 t、三角形、均匀、冯米斯、瓦尔德、威布尔、zipf

基准

针对 numpy 和一些最快的可用库的基准测试证明了mtalg的速度。

图 1 — Elementwise 代数:十亿次运算的加法函数基准[2]。其他基本功能的执行方式类似。【来源:作者图片】

图 2 —元素代数:加法函数的基准[2]。其他基本功能的执行方式类似。【来源:作者图片】

通常,多线程会带来开销,因此多线程的好处只有在处理大量操作时才会显现出来(按照 10⁷ / 10⁸或更大的数量级——图 2)。

还可以观察到随机数生成的速度大幅提高,这是执行大规模蒙特卡罗模拟或通过 MCMC 进行贝叶斯参数估计时的一项关键任务。

图 3 —随机数生成:均匀和标准正态分布随机变量的基准2。从其他分布中取样的表现类似。【来源:作者图片】

图 4 —随机数生成:均匀和标准正态分布随机变量的基准[2]。从其他分布中取样的表现类似。【来源:作者图片】

结论

在本文中,我们介绍了mtalg,这是一个直观的 Python 库,用于快速的元素操作和随机数生成。

欢迎在下面的专门部分留下评论、编辑建议或提问!

另外,如果你想为图书馆做贡献,请随时联系我们!

参考

GitHub 上的[1]mtalg
【2】性能指标评测是使用英特尔至强黄金 6142M CPU @ 2.60GHz 和 24 个线程进行的
【3】免责声明:作者是此处提供的库的维护者和合著者。

将优化教程与 Python 中的线性编程相结合

原文:https://towardsdatascience.com/blending-optimization-tutorial-with-linear-programming-in-python-74bcc443e4e5

应用于实际的水泥配料优化问题

作者图片

介绍

在几乎所有行业中,尤其是自然资源行业,通过优化所需资源支出产生的收入,做出最佳决策以最大化每个项目的价值至关重要。运筹学是一门学科,在这门学科中,现实世界的问题被用数学方法描述,并被优化以改进决策。下面的示意图说明了基于分配资源实现的最大净值的最佳项目 B,而 A 和 C 由于较差的设计或资源可用性而不是最佳的。乍一看,最便宜或最有价值的项目似乎是直观的选择,而最佳项目往往受到许多需要优化的不同和相关变量的影响。

比较项目 A、B 和 c 的净值和花费的资源的示意图

有了良好的运筹学,许多公司,尤其是自然资源行业的公司,可以更好地瞄准最有可能产生最大净值的最优项目。不幸的是,通常依赖昂贵的软件或外部顾问来概念化和解决这些优化问题。在本文中,我们将展示在 Python 等开源软件中实现这一点是多么简单。本例中使用的所有代码都存储在 GitHub 上。

水泥配料优化问题

本教程将关注 1977 年水泥和混凝土研究论文[1]中的一个实际水泥混合优化问题。在这种应用中,石灰石、粘土、铝土矿和烧过的黄铁矿以最经济的方式混合,同时保持所需的物理性质。

这四种原料中的每一种都有不同的成本,从 6 美元/吨到 230 美元/吨不等,并且由不同的成分比例组成。主要成分包括二氧化硅(SiO2)、氧化铝(Al2O3)、氧化铁(Fe2O3)、氧化钙(CaO)和三氧化硫(SO3),每种成分都需要在一定的比例范围内,以确保水泥满足所需的物理性能。下表总结了使用来自[1]和[2]的真实水泥数据的所有相关值。

原材料、它们各自的成分百分比和价格。来自[1]和[2]的真实水泥数据。作者图片

优化术语和方法

在我们进行任何优化之前,我们需要理解相关术语,以便从数学上定义手头的问题,一些关键术语包括:

  • 目标函数:在一系列可能的值中,最小化最大化的有值函数
  • 决策变量:优化问题中的未知量
  • 约束:通常以等式或不等式的形式必须满足的逻辑条件

在我们的混合示例中,目标函数就是每种材料的价格之和乘以该材料在四种材料中的比例。决策变量是每种材料的比例,比如使用 50%石灰石和 20%铝矾土。我们可以从根据我们的决策变量定义成分开始,并设置目标函数最小化,如下所示。

目标函数最小化价格乘以每种材料的比例之和。作者图片

如果我们像上面显示的那样单独运行优化,我们只会得到所有的材料比例等于零,给我们一个很好的零成本,但我们知道这不是一个真正的解决方案。约束最终推动优化,因为在遵守物理特性要求的同时,混合成本必须最小化。约束在【1】和【2】中被定义为成分的函数,因此我们将引入一些变量(q1 至 q6 ),类似于论文【1 】,如下所示。注意,在使用 scipy minimize 的 Python 中,它们必须用决策变量来明确表达。

驱动约束的六个变量被定义为决策变量每种原材料比例的函数。作者图片

在这个例子中,约束是基于每种成分必须落入的特定范围、几个不同的模数以及燃烧产品的某些元素,这些都是成分比例的函数。对于本文的线性编程部分,有 14 个约束,我们将在下面的 Python 改编中分解其中两个。

前两个约束的分解显示了每个方程如何以 scipy 优化格式转换成 Python 代码。作者图片

现在只需要对所有 14 个约束重复上述过程。下面是剩余的 12 个约束,显示在 GitHub 上的 Python 代码片段中。将所有的约束转换成代码似乎很难,但是一次只考虑一个等式是很重要的。在引入每个新的约束之后,一次运行一行代码可能有助于确保没有明显的输入错误,这些错误在以后会变得很麻烦。

水泥混合优化问题[1,2]中剩余的 12 个约束条件 T10,以及显示每个线性不等式 T11 约束条件 T12 的裁剪代码的屏幕截图。

使用 Scipy 运行优化

我们终于准备好用我们的约束来运行优化,以确定决策变量,其最小化目标函数。就我们的例子而言,我们将得到每种材料的比例,从而得到满足所需物理性质的最经济的混合物。

我们将使用from scipy.optimize import minimize和序列最小二乘编程(SLSQP)方法,该方法可以处理等式和不等式约束【3】,如 GitHub 上的代码所示。下表显示了 1977 年论文[1]中的材料比例与 Python 教程中的材料比例有多么相似。细微的差异可能是由于舍入或不同的优化方法。

单纯形论文[1]和 scipy python 教程的最终结果使用 SLSQP 最小化。

结论

运筹学为更好的决策提供了强有力的工具。虽然有运行线性程序的昂贵软件,但也可以使用开源软件轻松设置,如 Python 中的 scipy optimize。独立地对优化问题进行编码还允许进行一些改变,这些改变在商业软件中可能是不可用的,因为商业软件抽象了许多更好的细节。

解决这些优化问题可以让公司做出更好的决策或改进复杂操作的程序。在本教程中,用不到 30 行代码解决了一个实际的水泥混合优化问题。虽然解决方案是针对可用的特定材料和作为示例的一部分出现的约束,但是相同的方法适用于任何种类的类似混合问题。

本文考虑了一个线性问题,但是也存在优化更复杂的非线性程序的选择。虽然线性问题更容易计算,但它们通常带有假设,导致不可避免的次优解决方案。本教程中使用的论文通过比较线性规划近似和更精确的非线性规划[1]对此进行了更详细的讨论。非线性程序改善事物的程度很可能取决于手头问题的线性有效性。

旁白—水泥的典型使用案例

有时候,退一步看看我们所做的所有数据工作在现实世界中的应用是很好的。水泥是混凝土的主要成分,而混凝土是建筑中使用最广泛的材料。下面是一个我和我的叔叔们一起搅拌和浇筑混凝土台阶的整洁视频。

将水泥与骨料和水混合以浇筑混凝土步骤的时间间隔。作者提供的视频

参考

[1] Xirokostas,D. A .,& Zoppas,C. E. (1977 年)。水泥配料优化问题的数学规划方法。水泥和混凝土研究,7(5),503–514。

[2] Labahn O. (1971 年),《水泥工程师手册》第 60 页。Bauverlag Cmbh,威斯巴登。

[3]克拉夫特博士(1988 年)。序列二次规划软件包。技术。众议员 DFVLR-fb88-28,德国航空航天中心德国飞行力学研究所,科隆,德国。

融合人工智能的力量和诗歌的细腻

原文:https://towardsdatascience.com/blending-the-power-of-ai-with-the-delicacy-of-poetry-3671f82d2e1

人工智能模型现在能够从文本中生成图像,如果我们给它们提供伟大诗人的话语会怎么样?诗与人工智能的梦幻之旅。

图片由作者用 DALL-E 创作。灵感来自平克·弗洛伊德的《希望你在这里》

简介

诗歌只不过是巧妙选择的词语的并置。一系列的字符,但能够激发情感。一种很难解释和整理的艺术。
在听或读一首诗时,我们几乎能感觉到情绪、寒战,或者通过闭上眼睛我们能看到图像。事实上,许多最引人入胜的诗歌往往充满了隐喻和类比,描绘了生动和不寻常的形象。

毕竟,文字往往伴随着图像,无论是书中的插图还是广告中的强化信息。出于热情或商业目的,插画师经常被要求将他人写的文字转换成图像,试图捕捉文本的信息,同时找到最佳的表达方式,而不背叛作者的思想。

达尔-E,巫师

OpenaAI 今年发布了 DALL-E 2 ,这是一种新的生成语言模型,能够将句子作为输入,并创建相应的原始图像。该模型是先例模型 DALL-E 的新版本,即使尺寸更小(只有 35 亿个参数,而不是 120 亿个参数),也能够生成分辨率提高 4 倍的图像。在不涉及技术细节的情况下,DALL-E 2 由不同的组件组成(称为先前型号,夹子,以及松开)。它已经在一个惊人的 6.5 亿张图像和相关字幕的数据集上进行了训练,以学习合并图像中存在的连贯元素的能力。

简而言之,我们可以说,当你给 DALL-E 2 一个句子时,先验模型正在生成一种图像的“心理意象”(一种图像主要元素的合成图,或者称为嵌入)。“解开”模块将此图像转换为绘图,而“剪辑”模块则对图像中句子的特征(元素和样式)进行编码。

DALL-E 你可以输入一个句子“一个宇航员以照片般逼真的方式骑马”,它能够生成一个编码句子中描述的元素的图像。

DALL-E 2 生成的图像

改变句子,它能够处理句法语义的变化,比如:“一个宇航员在太空的热带度假胜地以照片般的真实风格闲荡”。

DALL-E 2 生成的图像

正如 Twitter 上不同用户所注意到的那样,DALL-E 2 可以更好地处理复杂和费解的句子,因为短句可能缺乏特异性(导致有趣的结果)。请注意,如何也相应地调整阴影(和水反射,如果有的话),这实际上是令人印象深刻的。DALL-E 还可以插入图像,改变图像的风格,编辑图像等等。

DALL-E 2 生成的图像

但是,当您要求生成带有文本的图像时,不同的用户会注意到一些错误(尤其是在照片级的图像中)或拼写错误。这是因为即使是非常先进的模型,DALL-E 也不会推理。

俳句的微妙艺术

有什么比俳句更能让你开始的呢?毕竟,俳句是包含在三行空间里的激动人心的诗。作为 17 世纪日本的一种诗歌形式,它通常由三行和 17 个音节(5-7-5)组成。尽管它很简短,但它通过提取精华成功地浓缩了生动的情感。一种艺术形式,以极简的风格优雅地将引人注目的意象封装起来。

俳句从其直接性和明显的简单性中汲取力量,消除虚饰,从自然、季节和人类精神状态的暗示中汲取灵感。这篇文章需要思想和意象的综合,然后让读者去想象这首短诗之外还有什么。俳句的精妙之处还在于这种意象的并置,然后往往在最后一行得到解决,从而产生一种封闭感。

它们包含对比鲜明的图像和明显的逻辑跳跃,因此似乎是这个小实验的完美场所。

露水的世界,

在每一颗露珠里

奋斗的世界

《露水的世界》小林伊萨

由作者用 DALL-E 创建的图像。上面的俳句被用作生成图像的测试

在某种程度上,这个形象似乎是恰当的,露珠包含了它自己的世界。如果我能画得那么好的话,这已经足够接近我会画的了。

蜡烛的光

被转移到另一根蜡烛上—

春光乍泄

约萨·布森的《点燃一支蜡烛》

由作者用 DALL-E 创建的图像。上面的俳句被用作生成图像的测试

在这里,我想尝试一种不同的风格,用灯光做一点点改变,在我看来,这首诗让我想起了一幅氛围类似于梵高画作的图像。从某些方面来说,结果还不错,虽然不完全是我想象的那样。

冬日里

森林,狂风怒号

无叶可吹。

《寒冬腊月》夏目漱石

由作者用 DALL-E 创建的图像。上面的俳句被用作生成图像的测试

在这里,我想象了一些风格化的,多节的树在冰冷的寒风中扭动。事实上,这张图片似乎传达了一种刺痒的寒冷感觉。

因为它的简单和直接,俳句甚至在日本之外也很流行和成功。几位西方作家已经尝试过了;我决定在这个实验中包括我最喜欢的。

我们之间的爱是

言语和气息。爱你是

长河奔流。

索尼娅·桑切斯的《为你而写的俳句》

由作者用 DALL-E 创建的图像。上面的俳句被用作生成图像的测试

这是我最难理解的诗歌之一。我不得不尝试不同的风格,但我在生成合适的图像时遇到了困难。有时 DALL-E 用无意义的措辞填充图像(一些描绘的文本有点像“爱”,另一些模糊地相似)。我试了几次才得到一个可以接受的答案

生活的点滴,我们的头

悲伤。赎回和浪费粘土

这个机会。有用。

《头骨上的线条》作者拉威·香卡

由作者用 DALL-E 创建的图像。上面的俳句被用作生成图像的测试

再一次,选择一张图片并不容易,我尝试了一些风格,最终选择了这张。DALL-E 每次尝试提供给你六张图片,然而,几乎没有一张能说服我。最终我选择了这个,因为这是最坏的选择。

结论

DALL-E 是一个非常强大的工具,但仍然需要正确的词语来充分利用它。有时需要多次尝试才能找到正确的图像。细看,有些细节可能看起来有点奇怪,有点粗略。并非所有的图像都是合适的,如果文字描述不准确,DALL-E 将很难处理。有时,它会在图像中填充与文本模糊相似的无意义的措辞。然而,总的来说,结果是惊人的;它很灵活,可以从文本中生成非常不同的图像。即使使用诗歌文本,它也能产生非常令人回味的图像。我认为已经通过了(但是还有改进的空间),你同意吗?

附加资源

如果你觉得有趣:

你可以寻找我的其他文章,你也可以 订阅 在我发表文章时得到通知,你也可以在LinkedIn上连接或联系我。感谢大家的支持!

这里是我的 Github 资源库的链接,我计划在那里收集代码,以及许多与机器学习、人工智能等相关的资源。

**https://github.com/SalvatoreRa/tutorial **

BLEU:来自另一个时代的被误解的指标

原文:https://towardsdatascience.com/bleu-a-misunderstood-metric-from-another-age-d434e18f1b37

但是今天仍然在人工智能研究中使用

蓝色的墙— 图片来自 Pixabay

GPT-3耳语掌中NLLBFLAN 等众多机型都曾被用公制 BLEU 评估过,宣称自己在某些任务上的优越性。

但是 BLEU 到底是什么?它是如何工作的?

在本文中,我们将追溯到 20 年前,揭示 BLEU 存在并成为一个非常成功的指标的主要原因。我们将通过一些例子来看看 BLEU 是如何工作的。我还将强调该指标的主要限制,并提供如何使用它的建议。

这篇文章被认为是对 BLEU 的一个介绍,但是对于那些习惯而不是需要使用 BLEU 的经验丰富的 NLP/AI 实践者来说也是一个很好的提醒。

2001 年在 IBM

BLEU 最初是在 2001 年由 Kishore Papineni、Salim Roukos、Todd Ward 和 Wei-朱婧合著的 IBM 研究报告中描述的。一年后,他们在 ACL 2002 上发表了一篇描述它的科学论文,这篇论文被引用得更多,也更容易找到。

BLEU 最初是作为评估机器翻译(MT)的自动度量标准提出的。

在 2001 年,机器翻译系统仍然主要是人工评估,或者使用旧的自动指标,如 WER ( 单词错误率)。WER 是一种受 Levenshtein 距离启发的度量,今天仍用于评估语音识别系统。对于机器翻译评测来说,WER 可以看作是 BLEU 的鼻祖。BLEU 的作者表达如下:

我们模仿语音识别社区使用的非常成功的单词错误率指标来设计我们的亲密度指标

像 WER 一样,BLEU 也是一个度量标准,用来衡量一个文本与人类产生的参考文本(如参考译文)的接近程度。

翻译是一项有多个正确解决方案的任务,BLEU 的作者设计了他们的度量标准,以便它可以处理多个参考翻译。这在当时并不新鲜,因为 WER 已经被改造成一个“mWER”来处理多个引用。据我所知,它是由 AT & T 实验室的 Alshawi 等人(1998) 首先提出的。

值得注意的是,在介绍 BLEU 的整篇论文中,作者总是假设他们的度量标准使用了多个参考译文。他们简要讨论了仅在某些情况下使用单一参考译文才是正确的:

我们可以使用带有单一参考译文的大型测试语料库,前提是这些译文并非都来自同一译者。

相比之下,如今,大多数研究论文使用带有单一参考的 BLEU,通常来自一个未知来源,用于各种任务,即不仅仅是翻译。

至少可以说,自 2001 年以来,BLEU 一直是一个非常成功的指标。这部分是由于其低廉的计算成本BLEU 分数的可再现性,与人工评估相反,人工评估的结果会因评估者和评估框架的不同而有很大差异。

BLEU 现在在几乎 100%的机器翻译研究论文中使用,并在很大程度上扩展到其他自然语言生成任务。

N-gram 匹配和长度惩罚

更准确地说,BLEU 评估一个翻译的 n 元语法与一组参考翻译的 n 元语法的匹配程度,而如果机器翻译比参考翻译更短或更长,则会惩罚机器翻译。

一些定义:

一个 n-gram 是一个令牌序列。让我们在这里定义一个标记是一个由空格任意分隔的字符序列。例如,句子“一个令牌不是一个单词。”通常会被标记为“标记不是单词”。在本文的后面,我们将更多地讨论标记化的极其重要的作用。

为了看到 BLEU 的作用,我从 BLEU 的论文中借用了一个中文句子(不是作者提供的)翻译成英文的例子。我们有以下两个由机器翻译生成的翻译:

作者图片

以及人类提供的以下 3 个参考译文:

作者图片

我们想用 BLEU 回答的问题是:

哪种译文最接近给定的参考译文?

我突出显示了两个候选翻译中参考翻译所涵盖的所有 n 元语法。

作者图片

候选 1 覆盖了来自参考翻译的更多 n 元语法,并且由于其长度(记号的数量)也合理地匹配参考翻译的长度,所以它将获得比候选 2 更高的 BLEU 分数。这里 BLEU 是正确的,因为候选人 1 确实比候选人 2 更好。

通过这个例子,我们可以看到 BLEU 的一些明显的局限性。被评估的翻译的意义没有被考虑。BLEU 只搜索与参考译文的标记完全匹配的内容。

例如,候选 2 中的“确保不在参考译文中,而“确保在参考译文中。由于“确保”与“确保”并不完全相同,尽管有相近的意思,但 BLEU 并不奖励。

当我们仔细观察标点符号时,情况会更糟。例如,候选人 2 以“结尾。“但这个时期是依附于直接的。"形成单个令牌。直接。“不是参考译文的令牌。候选人 2 没有因为正确包含这个句点而获得奖励。

这就是为什么 BLEU 通常在被标记化为包含标点符号的拆分标记的翻译上计算。我们将在下一节进一步讨论它。

为了简单起见,我不讨论 BLEU 背后的方程式。如果你有兴趣自己计算 BLEU,我邀请你阅读 BLEU 论文,其中所有的方程都有很好的动机和解释。

臭名昭著的标记化依赖

我们看到 BLEU 非常严格,因为一个标记应该与参考翻译中的一个标记相同,才能算作匹配。这就是标记化非常重要但经常被误解的地方。

标记化为 BLEU 提供了一些灵活性。

例如,让我们再次看看候选人 2:

它是保证部队永远听丨党丨指丨挥的活动指南。

但这一次,我们应用简单的标记化规则将标点符号与单词分开。我们获得:

它是保证部队永远听丨党丨指丨挥的活动指南。

注意"直接之间隔了一个空格。这是唯一的区别。候选项 2 现在匹配参考翻译中的另一个标记。这个令牌就是“”。这似乎并不重要,因为这只是又一个令牌,但这是一个非常频繁的令牌。这种标记化将对几乎所有的句子产生影响,从而导致显著更好的 BLEU 分数。

可能的标记化有无限多种。例如,下面的法语句子是从英语翻译过来的,我用了 5 种不同的标记符。注:我用的是 摩西 (开源,LGPL 授权)和sacre bleu(开源,Apache 授权 2.0)

作者图片

这些是相同的句子,但是由于它们被不同地标记,它们将匹配来自参考翻译的不同标记。所有这些标记化将产生不同的 BLEU 分数,而翻译保持不变。

这就是为什么在标记化不同或未知的翻译上计算的两个 BLEU 分数不能进行比较。

这是现今科技论文中经常被忽视的

你可以把标记化看成 BLEU 的一个参数。如果您更改参数,就会更改度量。无法比较两个不同指标的得分。

另一个时代的标准

2001 年提出 BLEU 的时候,机器翻译的质量是很不一样的。

为了让你们了解这种差异,我试着重建一个 2000 年代的法语到英语的机器翻译系统。为此,我训练了一个基于单词的统计机器翻译系统。我和摩西一起做的。我将把这个系统称为“统计 MT (2001)”

然后,我用一个普通的变压器模型训练了一个神经机器翻译系统。我和玛丽安一起做的(开源,麻省理工学院许可)。我将这个系统命名为“神经机器翻译(2022)”

它们生成的翻译如下。注意:我突出显示了与参考译文匹配的 n 元语法。

作者图片

正如预期的那样,统计机器翻译生成的翻译没有多大意义,尤其是在接近句子结尾的时候。另一方面,由神经机器翻译生成的翻译看起来很完美(没有上下文),但它与参考翻译并不完全相同,因此它将受到 BLEU 的惩罚。

2001 年,机器翻译系统生成的翻译往往毫无意义,而且有明显的语法错误。他们因不匹配特定的参考译文而受到应有的惩罚。现在,神经机器翻译经常会生成非常流畅的翻译,尤其是对于法语-英语等“容易”的语言对。他们通常会找到正确的翻译,但由于有许多可能的正确翻译,找到用作参考的确切翻译可能只是偶然发生的。

这就是我们碰到的 BLEU 的极限,即使翻译是正确的,它也只奖励精确的匹配。

何时使用 BLEU

BLEU 多年来一直引领着机器翻译研究的进步。在 NAACL 2018 上,BLEU 的作者获得了一个时间考验奖

BLEU 仍然在 AI 的很多领域使用,只是习惯使然。它现在在很大程度上被许多其他自然语言生成任务的评估指标所超越,包括机器翻译,如 chrFBLEURTCOMET

尽管如此,BLEU 仍然是用于诊断目的的非常好的工具。

由于 BLEU 具有众所周知的行为,即,我们知道对于特定的翻译任务期望什么级别的 BLEU,所以它可以用于快速地发现机器翻译系统的训练管道中或其数据处理中的错误和其他问题。

无论如何,BLEU 不应该用在短文本上。在实践中,机器翻译从业者总是对包含超过 1000 个句子的文本运行 BLEU。BLEU 旨在评估文档翻译。不应该用来评价句子翻译。

至于 BLEU 的实现,很多都是公开的。拥抱脸在评估库中有自己的实现。 NLTK 也实现了 BLEU。在摩西项目中还有 multi-bleu.perl 脚本。注意,所有这些 BLEU 的实现都是不同的,不会产生可比较的结果。我个人的建议是使用最初的 SacreBLEU 实现,因为这个工具是为了保证 BLEU 分数的可重复性和可比性。

如果你计划在你的下一个工作中使用 BLEU,不要忽视测试你的结果的统计学意义的需要。

https://pub.towardsai.net/yes-we-need-statistical-significance-testing-927a8d21f9f0

支持我工作的最好方式是使用我的链接成为一名普通会员:

https://medium.com/@bnjmn_marie/membership

如果你已经是会员,想支持这项工作,就 关注我上媒

块循环变压器:LSTM 和变压器相结合

原文:https://towardsdatascience.com/block-recurrent-transformer-lstm-and-transformer-combined-ec3e64af971a

一个结合了两者优点的强大模型

桑德罗·卡塔琳娜在 Unsplash 上的照片

普通的变压器不再是处理深度学习中任何情况的全能模型。

之前的一篇文章中,我们证明了对于时间序列预测任务来说,变形金刚举步维艰。这就是为什么谷歌创建了一个 混合变压器-LSTM 模型,在时间序列预测任务中实现 SOTA 结果。

炒作结束后,研究人员开始关注变形金刚的缺点。新的研究旨在利用其他型号的功能(CNNrnnRL 型号)来增强变形金刚。一个典型的例子是新一代的视觉变形金刚【1】,他们借鉴了 CNN 的创意。

2022 年 3 月,一个谷歌研究团队瑞士人工智能实验室 IDSIA 提出了一种新的架构,称为Block-Recurrent Transformer[2]。

那么,块轮回变压器是什么?它是一种新颖的 Transformer 模型,利用 LSTMs 的递归机制,在长范围序列的语言建模任务中实现显著的困惑改进。

但首先,让我们简单讨论一下变形金刚相比 LSTMS 的优缺点。这将帮助你理解是什么激发了研究人员提出块循环变压器。

变压器 vs 循环网络

变压器最显著的优点总结如下:

平行

RNNs 实现顺序处理:对输入(比如说句子)进行逐字处理。

变形金刚使用非顺序处理:句子是作为一个整体来处理的,而不是逐字逐句。

在图 1和图 2中更好地说明了这种比较。

图 1: 序列长度=4 的 LSTM 单元。(作者制作)

图 2: Bert 架构(简化-作者制作)

LSTM 需要 8 个时间步来处理句子,而 伯特【3】只需要 2 个!

因此, BERT 能够更好地利用现代 GPU 加速所提供的并行性。

请注意,两个图都是简化的:我们假设的批量为 1。此外,我们也没有为 BERT 的特殊记号而烦恼,事实上它需要两个句子,等等。

长期记忆

rnn在移动到未来令牌之前,被迫将它们学习到的输入序列的表示压缩到单个状态向量中。

此外,虽然lstm解决了普通rnn遭受的消失渐变问题,但它们仍然容易出现爆炸渐变。因此,他们正在努力解决更长的依赖关系。

另一方面,变压器的带宽要高得多。例如,在编码器-解码器变换器【4】模型中,解码器可以直接处理输入序列中的每个令牌,包括已经解码的令牌。这在图 3 中描述:

图 3: 普通变压器中的编码和解码(来源)

更好的注意力机制

注意力【4】的概念对变形金刚来说并不陌生。早在 2016 年谷歌神经引擎【5】(编码器-解码器拓扑中的堆叠 Bi-lstm)已经在使用注意力

回想一下变形金刚使用了一个叫做 的特例自我关注: 这个机制允许输入中的每个单词引用输入中的每一个其他单词。

变形金刚可以使用大注意窗口(如5121048)。因此,它们在长范围内捕获序列数据中的上下文信息时非常有效。

接下来,我们来看看变压器的缺点:

自我关注的 O(n)成本

变形金刚最大的一期。

有两个主要原因:

  • 最初的伯特模型有一个512令牌的限制。解决这个问题的简单方法是截断输入的句子。
  • 或者,我们可以创建超过该限制的变形金刚模型,使其达到4096代币。然而,自我关注的成本相对于句子长度是二次的。

因此,可伸缩性变得非常具有挑战性。已经提出了许多想法来重组最初的自我关注机制:

图 4: 不同类型自我关注的成本矩阵(来源)

这些想法大多是由新一代型号引入的,如【6】Transformer XL【7】。这些模型针对长格式文本进行了优化,并实现了显著的改进。

然而,挑战依然存在:我们能否在不牺牲效率的情况下进一步降低计算成本?

时间序列具有挑战性

虽然变形金刚已经统治了 NLP 领域,但是它们在时态数据方面的成功有限。但是为什么呢?时间序列不也是顺序数据吗?

  • 变压器可以更好的从长期历史计算出一个时间步长的输出,而不是当前的输入和隐藏状态。这对于局部时间依赖性来说效率较低。
  • 因此,短期记忆对于时间序列的长期记忆同样重要。
  • 这就是为什么谷歌研究人员推出了一个用于时间序列预测的混合深度学习模型【1】:该模型使用注意力,但也包括一个 LSTM 编码器-解码器堆栈,它在捕捉本地时间依赖性方面发挥着重要作用。
  • 最后,时间序列可以是多元,有 s 静态数据,等等。它们通常需要更特殊的处理。

在本文中,我们不会关注时间序列方面。有关时间序列深度学习模型的更多信息,请随时查看这篇文章

输入块-循环变压器

什么是 块轮回变压器?分块递归变压器 是一种革新 NLP 领域的新型模型。

这个模型的主要突破是循环单元:一个以循环方式工作的修改的变压器层。

让我们快速概述一下主要特征,然后我们将更深入地研究模型的架构。

  • 块级并行:递归单元处理中的令牌,一个块内的所有令牌并行处理。
  • 大关注窗口:由于模型将输入分解成块,因此它可以使用大关注窗口(测试了多达4096个标记)。因此,闭塞循环变压器属于远程变压器家族(类似于长变压器)。
  • 线性复杂度:由于递归单元分块分解输入,模型在【O(n)时间内使用 滑动自关注 逐块计算自关注。
  • 更稳定的训练:分块处理序列可用于长距离传播信息和梯度,而不会在训练期间导致灾难性的遗忘问题。
  • 信息扩散:块递归变换器对状态向量块而不是单个向量进行操作(像 RNNs do)。因此,模型可以充分利用 r ecurrence 并更好地捕捉过去的信息。
  • 互操作性:递归单元可与常规变压器层连接。
  • 模块化:循环单元可以水平或垂直堆叠,因为循环单元可以在两种模式下操作:水平(用于循环)和垂直(用于堆叠层)。这将在下一节中变得清楚。
  • 运营成本:增加递归就像多加了一层变压器。没有引入额外的参数。
  • 效率:与其他远程变压器相比,该型号表现出显著的改进。

下面两节将详细描述块-递归变压器的两个主要组成部分:递归单元 架构和 带递归的滑动自注意。

递归细胞架构

块循环变压器的支柱是循环单元

注意:不要被它的“细胞”特征所迷惑。这是一个完全成熟的变压器层,旨在以循环方式运行。

循环单元接收以下类型的输入:

  • 一组W 令牌嵌入,其中W块大小
  • 一组“当前状态”向量,称为S

输出是:

  • 一组W输出令牌嵌入。
  • 一组“下一状态”向量。

图 5: 复发细胞。左:垂直模式(堆叠),右:水平模式(重复)

图 5 展示了轮回单元架构。这个架构非常简单,并且重用了大部分现有的 Transformer 代码库!

我将逐步解释图 5 中显示的每个组件:

自我注意和交叉注意

块循环变压器支持两种操作:自关注交叉关注。更确切地说:

  • 自关注是对同一嵌入生成的值、查询(分别为KVQ矩阵)进行的。
  • 对一次嵌入产生的查询和另一次嵌入产生的键值进行交叉关注

如果你还记得最初的变压器编码器-解码器模型【4】,那么编码器正在执行 s 自我关注,而解码器中的“编码器-解码器关注层正在执行 c 罗斯关注。那是因为查询来自前面的解码器层,而来自编码器输出。循环单元在同一层执行两种操作。换句话说:

轮回细胞并行做自我注意(编码)和交叉注意(解码)!

水平与垂直模式

接下来,我们将重点介绍**所示的* 图 5。 像我前面说的,轮回细胞以两种模式运行:*

  • 垂直(堆叠):在这种模式下,模型对输入嵌入执行自我关注,对递归状态执行 c 交叉关注
  • 水平(递归):这正好相反:模型对递归状态进行自关注,对输入嵌入进行交叉关注

位置偏差

您还会注意到图 5 中的**方框,称为学习状态 id。让我们解释一下这是什么,为什么我们需要它。**

至此,很明显递归细胞之间传递的递归状态不是单个向量(像 RNNs ,而是大量的状态向量

因为相同的 MLP 层被应用于每个状态向量(标准实践),实验分析表明状态向量不能区分。经过几次训练后,它们趋于一致。

为了防止这个问题,作者向状态向量添加了一组额外的可学习的“状态 id”。作者称这种功能为位置偏差。这类似于位置编码,普通转换器应用于输入嵌入。块递归转换器的作者将这种技术应用于递归状态向量,这就是为什么他们使用不同的名称来避免混淆。

位置编码

块递归转换器没有将传统的位置编码应用于输入标记,因为它们对长序列不太适用。相反,作者使用了在 T5 架构【8】中引入的一个著名技巧:他们将位置相对偏差向量添加到自我关注*矩阵,该矩阵源自垂直模式**中的输入嵌入。偏差向量是查询之间相对距离的学习函数。*

闸门配置

**块循环变压器与其他变压器型号的另一个区别是剩余连接的使用。

**块循环变压器的作者尝试了以下配置:

  1. 闸门更换剩余连接。(该配置如图图 5 所示)。
  2. 固定浇口T71LSTM 浇口之间选择。****

作者做了几个实验来寻找最佳配置。更多细节,查看原文。

递归滑动自我注意

**块循环变压器自关注是一项革命性的功能,结合了以下概念:

  1. 矩阵乘积QK^TV变为“线性化”。
  2. O(n ) 全注意力替换为 O(n) 滑动注意力
  3. 添加 重现

前两个概念已在相关工作[6]、[9]中提出。多亏了他们,注意力实现了线性成本但在很长的文档中失去了潜力块递归变压器结合了前两个概念和递归,这是从 RNNs 借用的概念。

递归机制优雅地集成在 Transformer 层中,并在很长的句子中提供显著改善的结果。

我们将分别分析每个概念,以便更好地理解块循环变压器如何使用注意力

线性矩阵乘积

在变压器生态系统中,注意力围绕着 3 个矩阵:查询Q、按键K值* V。***

提醒一下,普通注意事项由以下人员给出:

注意在香草变压器的一个头

块循环变压器计算注意分数稍有不同:首先,移除 softmax 操作。根据[9],剩余的项重新排列为Q(K^TV)(如图图 5 所示),并以线性化的方式进行计算。**

滑动自我注意

给定一长串N记号,一个滑动窗口应用一个因果掩码,使得每个记号只关注它自己和前面的W记号。(记住W是块大小)。

让我们想象一下注意力矩阵:

图 6: 单个训练步骤的块递归变压器的优化注意矩阵。不是计算整个矩阵,而是只计算 2 个黑色方块内的分数。(来源)

在图 6 的中,我们有一个窗口大小W =8,序列长度N =16。在前一个训练步骤中,计算并缓存了第一个W阴影标记。剩余的N无阴影标记来自当前输入。

输入序列中的每个记号以滑动的方式连续关注前面的W =8 个记号。因此,在每一行中,我们有W个计算。矩阵的高度是N(我们句子中的记号数)。因此,总成本是 O(N*W) 而不是全成本矩阵 O(N*(W+N)) 。换句话说,相对于序列N的成本是线性的,而不是二次的!

因此,在我们的示例中,我们关注了两个大小为Wx2W的图块。让我们来分析一下这一连串的事件:

  1. 在第一关注步骤中,输入句子的第一个W标记将关注来自前一个句子的最后缓存的W keysvalues
  2. 在第二个注意步骤中,我们输入句子的最后一个W标记将注意我们输入句子的第一个W标记。
  3. 这结束了我们的训练步骤,输入句子的最后W keysvalues被缓存以用于下一个训练步骤。
  4. 到目前为止,您应该已经注意到了滑动模式。这就是为什么我们称这种机制为滑动自我注意。****

****注:当我说token X照顾 token Y时,我们不是指代币本身:我是指那些代币各自的keysvaluesquery分数!

复发有什么帮助

正如我之前所说的,滑动自我关注(非循环版本)已经被早期的模型使用[6][7],尽管有一些不同:

  • 在最初的版本中,输入的句子没有被分成块。使用简单的滑动自关注 n 的模型一次接收所有输入。这限制了他们有效处理的信息量。
  • 在之前的训练步骤中使用的缓存的不可微的——这意味着它们在反向传播期间不会更新。然而,在递归版本中,滑动窗口具有额外的优势,因为它可以在多个块上反向传播梯度。
  • 滑动自关注模型在其最顶层有一个理论感受野W*L,其中L代表模型层数。在循环版本中,感受野实际上是无限的!这就是为什么块递归变压器擅长远程内容。

实验结果

最后,对块循环变压器进行测试。

实验过程

任务是自动回归语言建模,目标是预测给定句子的下一个单词。

该模型在 3 个数据集上进行测试: PG19arXiv、Github 。它们都包含很长的句子。

作者测试了块循环变压器,并使用变压器 XL 作为基线。块循环变压器配置为两种模式:

  1. 单循环模式:**作者使用了 12 层变压器,仅在第 10 层循环。
  2. ****反馈模式:使用相同的模型,除了这一次第 10 层不仅仅将输出循环到自身:当处理下一个块时,第 10 层的输出被广播到所有其它层。因此,第 1-9 层可以交叉处理该输入,使模型更强大,但计算成本更高。

估价

使用——语言模型的一种常见度量标准,对模型进行评估。

对于那些不知道的人,困惑被定义为P=2^L,其中L是常规熵。

直观地说,在语言建模的背景下,你可以这样来思考困惑:如果困惑的值是30,那么预测句子中的下一个单词就像正确猜测 30 面骰子的结果一样不确定。困惑度越低越好。

结果

总的来说,方块重现变形金刚在困惑和速度方面明显优于变形金刚 XL* 。***

另外,关于*块循环变压器,反馈模式* 单循环模式要好。然而,作者得出结论,额外的性能并不能补偿额外的复杂性。****

论文作者尝试了各种配置,例如增加或跳过门。欲了解更多信息,请查阅原始论文[2]。

结束语

本文讨论了块递归转换器,这是一篇突破性的论文,它利用传统的 RNN 递归来增加长文档中转换器的潜力。**

我强烈建议你阅读原文[2],将这篇文章作为辅助指南来帮助你理解。

由于论文非常新,作者没有发布任何源代码,尽管 Github 上有一些非官方的实现。

感谢您的阅读!

  • 订阅我的简讯
  • 在 Linkedin 上关注我!

参考

  1. Dosovitskiy 等人, 一幅图像抵得上 16x16 个字:用于图像识别的变形金刚 (2020)
  2. 德莱斯里·哈钦斯等人 阻止轮回变形金刚(2022 年 3 月)**
  3. Jacob Devlin 等人 BERT:用于语言理解的深度双向变压器预训练(2019 年 5 月)**
  4. A.瓦斯瓦尼等人关注是你所需要的全部(2017 年 6 月)
  5. Kyunghyun 等人关于神经机器翻译的性质:编码器-解码器方法
  6. Iz Beltagy 等人Long former:The Long-Document Transformer艾伦人工智能研究所(2020)
  7. 戴子航等 Transformer-XL:固定长度语境之外的注意力语言模型 (2019)
  8. Colin Raffel 等人利用统一的文本到文本转换器探索迁移学习的限制 (2019)
  9. Angelos Katharopoulos变压器是 RNNs:具有线性关注的快速自回归变压器 (2020)

布鲁姆是十年来最重要的人工智能模型

原文:https://towardsdatascience.com/bloom-is-the-most-important-ai-model-of-the-decade-97f0f861e29f

意见

不是戴尔 2 号,不是 PaLM,不是 AlphaZero,甚至不是 GPT 3 号。

鸣谢:大科学研究工作坊

你可能想知道这样一个醒目的标题是不是真的。答案是肯定的。我来解释一下原因。

GPT-3 于 2020 年问世,开创了一条全新的道路,此后整个 AI 行业都在有意和关注着这条道路。科技公司一次又一次地制造更好、更大的模型。但是,尽管他们已经投入了数百万来完成这项任务,他们中没有一个人从根本上改变了领先的范式或两年前 GPT-3 制定的游戏规则。

地鼠龙猫棕榈(可以说是目前大型语言模型的领奖台)明显比 GPT-3 好,但本质上,它们更多的是一回事。Chinchilla 已经证明了略有不同的缩放定律的成功,但它仍然是一个大型的基于变压器的模型,像其他人一样使用大量的数据和计算。

DALL E 2ImagenParti ,尽管他们做的事情不同——文本到图像模型增加了变形金刚之外的技术——但他们基本上基于相同的趋势。甚至火烈鸟加托,稍微偏离了《GPT 3》对人工智能的更一般化、多模态的方法,也只是应用于小说任务的相同想法的混合。

但是,最重要的是,所有这些人工智能模型都源于私营科技公司的巨大资源。这是共同的因素。不仅仅是它们的技术规格使它们属于同一个包。这是因为少数富有的盈利性研究实验室对它们施加了绝对的控制。

这种情况即将改变。

BLOOM 和 BigScience 标志着人工智能社区的一个转折点

BLOOM(big science Language Open-science Open-access Multilingual)是独一无二的,不是因为它在架构上与 GPT 3 不同——它实际上是上述所有模型中最相似的,也是一个基于变压器的模型,具有 176B 个参数(GPT 3 有 175 b)——而是因为它是人工智能社会政治范式转变的起点,这将定义该领域未来几年的发展——并将打破 big tech 对大型语言模型(LLM)的研发的束缚。

公平地说, MetaGoogleOpenAI 最近开源了他们的一些基于大型变压器的模型(分别是 OPT、Switch Transformers 和 VPT)。是因为他们突然喜欢上了开源吗?我相信那些公司的大多数工程师和研究人员一直都有。他们知道开源的价值,因为他们每天都在使用基于开源的库和工具。但是,作为无道德的赚钱实体,这些公司不会在更广泛的人工智能社区的偏好面前如此轻易地低头。

如果不是因为一些机构和研究实验室已经开始向这个方向施加难以置信的压力,这些公司不会开源他们的模型。

BigScience抱脸EleutherAI 等不喜欢大 tech 对领域的所作所为。垄断一项可能——也有希望——让很多人受益的技术在道德上是不对的。但他们不能简单地要求谷歌或 OpenAI 分享他们的研究,并期待积极的回应。这就是为什么他们决定建造并资助他们自己的实验室——并向想探索其奇迹的研究人员免费开放。最先进的人工智能不再是口袋鼓鼓的大公司的专利。

布鲁姆是这些努力的顶点。经过 2021 年 1 月开始的一年多的集体工作,以及在 Jean Zay 公共法国超级计算机上进行的 3 个多月的训练,BLOOM 终于准备好了。它是大科学研究研讨会的成果,包括来自世界各地的+1000 名研究人员的工作,并依靠 250+个机构的合作和支持,包括拥抱脸、、IDRISGENCI蒙特利尔人工智能伦理研究所等。

他们的共同点是,他们认为技术——尤其是人工智能——应该是开放的、多样的、包容的、负责任的和可访问的,以造福人类。

他们令人印象深刻的集体努力和他们在人工智能行业中的独特立场只能与他们对社会、文化、政治和环境背景的关注相提并论,这些背景是人工智能模型设计(特别是 BLOOM)以及数据选择、管理和治理过程的基础。

BigScience 的成员发布了道德宪章,确立了他们在这些技术的开发和部署方面坚持的价值观。他们把这些分为两类——内在的、有价值的……作为目的、外在的、有价值的作为手段。我将在这里引用宪章来总结这些价值观,因为我认为它们对于理解大科学和布鲁姆的前所未有的重要性至关重要。(我还是推荐通读整篇章程。它很短。)

内在价值

  • 包容性:“…平等获得大科学的文物…不仅仅是不歧视,还有一种归属感…”
  • 多样性:“…来自 50 个国家的 900 多名研究人员和社区…涵盖 20 多种语言…”
  • 再现性:...大科学旨在确保研究实验和科学结论的再现……”
  • 开放性:“…来自世界各地的人工智能相关研究人员可以贡献并加入该倡议…[并且]成果…将在开放的基础上共享…”
  • 责任:“每个贡献者对他们在大科学项目中的工作负有个人和集体(社会和环境)责任……”

外在价值

  • 可达性:“作为实现开放性的手段。BigScience 尽最大努力使我们的研究和技术成果易于向更广泛的公众解释和说明……”
  • 透明:“作为实现再现性的手段。大科学工作在各种会议、网络研讨会、学术研究和科学普及中得到积极推广,以便其他人可以看到我们的工作……”
  • 跨学科:“作为实现包容性的手段。我们不断在计算机科学、语言学、法律、社会学、哲学和其他相关学科之间搭建桥梁,以便在开发大科学人工制品时采用整体方法。”
  • 多语制:“作为实现多样性的一种手段。拥有一个从概念上讲是多语种的系统,其近期目标是覆盖世界上 20 种最常用的语言……”

毫无疑问,BigScience 和 BLOOM 是过去十年人工智能领域最引人注目的尝试,旨在消除大技术在人工智能领域建立的所有障碍——不管是愿意还是不愿意。和最真诚和诚实的事业,以建立人工智能(特别是 LLMs)造福每个人。

如果你想更多地了解大科学方法,请阅读这个关于 LLM 研究中社会背景的三篇文章的伟大系列。可以通过拥抱脸进入布鲁姆。

是什么让布鲁姆与众不同

正如我在开始时提到的,BLOOM 不是第一个如此大规模的开源语言模型。Meta、Google 和其他公司已经开源了一些模型。但是,正如预期的那样,这些并不是这些公司能提供的最好的。赚钱是他们的主要目标,所以分享他们最先进的研究是不可能的。这就是为什么仅仅通过这些战略性的公关行动来表明他们参与开放科学的意图是不够的。

大科学(BigScience)和布鲁姆(BLOOM)是一套伦理价值观的体现,这些价值观是公司无法用定义来代表的。无论哪种情况,可见的结果都是一个开源的 LLM。然而,指导大科学的隐藏的——也是极其必要的——基础强调了这些集体倡议和强大的大技术之间不可调和的差异。

被环境所迫而不情愿地采用开源实践和因为你坚信这是正确的方法而这样做是不一样的。BigScience 成员的信念是,我们应该使人工智能民主化,并致力于让尽可能多的人受益——通过开放访问和结果或解决伦理问题——这是 BLOOM 的独特之处。我承认,这也是它成为十年来最重要的人工智能模型的原因。

布鲁姆是一个领域的先锋,这个领域正处于彻底变革的边缘。这是一个超越当前研究趋势的运动的旗帜。这是人工智能新时代的终结,它不仅将推动该领域更快地向前发展,还将迫使那些喜欢以其他方式前进的人接受现在管理该领域的新规则。

这不是开源第一次赢得隐私和控制权。我们在计算机、操作系统、浏览器和搜索引擎中都有例子。最近的历史充满了那些想为自己保留利益的人和那些代表其他人战斗并取得胜利的人之间的冲突。开源和开放科学是技术的终极阶段。我们即将进入人工智能的新时代。

订阅 算法桥 。弥合算法和人之间的鸿沟。关于与你生活相关的人工智能的时事通讯。

您也可以直接支持我在 Medium 上的工作,并通过使用我的推荐链接 这里 成为会员来获得无限制的访问权限! 😃

失误和重大胜利:建立一个成功的人工智能奖学金指南

原文:https://towardsdatascience.com/blunders-and-big-wins-a-guide-to-building-a-successful-ai-fellowship-add82be7fc44

如何吸引、培养和留住稀缺的机器学习人才

找到成功的公式。图片作者。使用生成 AIDall-E

人工智能的世界正以令人难以置信的速度前进。每年都有大量人工智能初创公司涌现,发表的研究数量也在逐年增加。虽然这对那些建立人工智能公司的人来说是一个好消息,但也有一些问题随之而来。

首先,AI 人才稀缺。你需要成为一个有吸引力的雇主,这可以通过补偿或次级福利来解决。其次,你的 AI 天赋可以很快落后。两年前最先进的东西现在可能已经过时了。如果你想保持企业的竞争力和吸引力,跟上最新的趋势是至关重要的。

在你公司内部投资人工智能 R&D 可以解决这两个问题。根据我们在 Slimmer AI 的经验,从事机器学习(ML)工程师、ML 研究员或数据科学家工作的人通常都有学术背景,并且更喜欢在他们的工作中有研究成分。他们有学习的动力,应该给他们学习的机会。当人才发展受到重视时,它将提高工作满意度和你的内部知识。双赢!

那么,这个人工智能 R&D 程序应该是什么样的呢?

在更苗条的人工智能中,我们已经多次尝试解决这种对更多人工智能 R&D 的需求,并了解了什么可行,什么不可行(艰难的方式)。经过几次迭代,我们现在有了一个成功的公式,我们称之为我们的人工智能伙伴关系,我们的工程师很喜欢它!在这篇博文中,我将分享我们学到的经验,向你介绍我们的人工智能奖学金以及如何在你自己的组织中建立它。

如果你有兴趣了解更多关于我们的人工智能伙伴计划,请看看这篇文章,它分享了关于具体实施的更多细节。

我们失败的尝试(这样你就可以避免它们)

作为一个应用人工智能 B2B 创业工作室,我们正在不断寻找新的机会。知道人工智能什么是可能的,什么是不可能的,使我们能够专注于令人兴奋的新领域的机会,远离其他人。

然而,仅仅跟随高层趋势是不够的。通过了解当前最先进的技术和如何实际实现它,风险是什么,等等。,我们正在收集必要的经验,以提供最优质的人工智能产品。

以下部分概述了我们在过去几年中为满足这一需求所做的尝试,并展示了哪些措施没有奏效以及原因。

作者图片。AI 生成使用 Dall-E

尝试 1:分裂我们会堕落

ML 工程师最有可能同时从事至多一个或两个项目。这种专注在短期内可能是好的,但从长期来看会限制他们的视野。我们有几个团队从事不同的项目,使用不同的数据和人工智能技术。为了减少孤立,我们引入了每周一次的人工智能脱口秀,在那里我们讨论进展和我们面临的问题。这听起来像是一个分享知识和一起排除故障的好方法。

然而,几个月后,我们注意到几乎没有互动。每当有人遇到路障并寻求帮助时,没有人能够做到。由于我们都专注于自己的项目,我们只是没有知识或经验来帮助稍微不同的领域。很明显我们需要别的东西。

尝试 2:失焦

为了解决这个问题,我们认为分享关于我们在项目中应用的人工智能的更深入的知识会有所帮助。这将使每个人在需要时更好地理解其他人面临的问题。ML 的工程师们准备了半个小时的深潜技术演示,并在最后留有讨论的空间。我们分享了令人兴奋的新论文和应用新模型或技术时获得的经验。

虽然这些会议很棒,也很有帮助,但很难让它们与当前的工作保持一致。这是因为这些技术深度挖掘并没有聚焦于我们的核心业务。此外,人们发现很难找到时间来准备这些会议,因为要满足项目的最后期限。

尽管这些会议没有达到我们的预期目标,但我们重新调整了它们的用途,让我们公司的每个人都能分享知识,并就不同的主题教育他人。机器学习内容的频率和深度都下去了,但是我们公司内部的交流增加了很多。这是与所有不同部门保持联系的好方法,尤其是当我们很多人都在远程工作的时候。

尝试 3:谁在开这辆车?

在这一点上,我们仍然缺乏增长知识的方法,也许更重要的是,缺乏实践经验。下一个计划旨在解决这两个问题。

这个想法是围绕选择一些感兴趣的主题,并组成小组进行一些研究和开发。然而,开发部分不是很强,我们缺少一个人来推动和监督这个项目。同样,在几个月的时间内,人们开始优先考虑其他工作,项目停止了。

尝试 4:忘记现实世界

我们试图通过为每个主题指定明确的领导者,并告诉每个人他们都有专门的时间来完成 R&D 的工作,来修补之前的计划。这很有帮助。有了明确的领导,工作就能保持在正轨上,人们也能更积极地参与进来。

然而,并不是每一个被任命的领导者都和这个角色有着相同的亲和力。团队在固定的日子里聚在一起做 R&D 工作,这增加了每个人的动力。然而,没过多久,日常工作和截止日期的压力就悄悄逼近我们,慢慢瓦解了团队。

R&D 的主题大多围绕工程师的兴趣,起初很有趣,但如果没有与公司的近期战略重点明确联系,这项工作很快就开始变得无关紧要。

基于这些过去的尝试,我们开始创造一些新的和持久的东西。人们感到兴奋,觉得他们可以花时间去做的事情,以及真正有助于增加我们的知识和实践经验的事情。

进入:人工智能协会

更苗条的艾艾联谊项目是我们文化结构的一部分;我们的 ML 工程师一直认为这个项目非常或非常有价值。这个项目不仅让工程师们感到兴奋,也让公司的每个人感到兴奋。我们的工程师动力十足,知识更加渊博,这意味着我们能提供更高质量的产品。更好的产品造就快乐的顾客。

人工智能奖学金与来自 Spotify 模式章节有许多相似之处,后者代表了一群具有相似能力领域的人。除了分享这个领域的知识和想法,我们还加入了一个专门的研发项目。

该计划围绕四个主要目标:

1 加速创新我们致力于与更苗条的人工智能的近期需求一致的主题,以便我们保持领先地位。

2 吸引、培养和留住人才我们的工作主题与员工专业技能的差距和兴趣一致。

3

4 加强风险关系作为一家风险工作室,我们不仅关心更苗条的人工智能团队,也关心我们投资公司的 ML 能力。我们将风险投资员工纳入我们的团队,并帮助他们建立自己的项目。

R&D 计划

那么,我们联谊背后的成功是什么呢?基于我们失败的模式,有几个关键的方面可以造就一个伟大的人工智能团队:

  • 具有清晰的结构
  • 强制性参与和管理承诺
  • 每周专门的时间和良好的计划
  • 符合业务目标和员工兴趣的主题
  • 让某人推动创新

关于我们 R&D 项目的具体实施细节,请参见本帖。现在,我将简要强调你需要知道的,并分享一些有用的提示&技巧。

清晰的结构

在 Slimmer AI,我们每年有四次 R&D 循环。一个 R&D 周期由 3 个月组成,包括 7 周的研究和 2 周的开发阶段。剩余的时间被用作缓冲,或者在博客文章、研究论文或开源软件中发布有趣的发现。如果你对我们过去的工作感兴趣,可以看看我们的媒体页面和 GitHub

强制参与和专用时间

该计划最重要的一个方面是专用时间。这只有通过贵组织最高层的支持才有可能实现。人工智能奖学金的持续成功是每个 ML 工程师的角色卡的一部分,他们在评估期间根据他们对推进我们的 R&D 的参与情况进行评估。领导层的这一承诺,加上研究阶段每周半天的专用时间和开发阶段 6 整天的专用时间,确保我们的工程师有信心将他们的时间花在我们未来的这一重要投资上。

重要的话题

在一个 R&D 周期中,两个或三个人工智能主题被并行处理。这些主题是根据我们的短期业务需求以及我们员工的专业知识差距和兴趣选择的。

推动创新

最后,我们让推动人工智能协会和它的 R&D 项目成为了某人的工作。理想情况下,每个人都是自我组织,积极主动,规划自己的发展道路。但遗憾的是,这并不是现实。尤其是在许多人远程工作的时代,即使是在 Zoom 通话中进行讨论也是一个挑战。

因此,让某个人或一群人来推动这个项目是至关重要的。职责包括:决定与产品经理合作解决哪些话题,领导每周的站立会议,促进富有成效的讨论,并确保我们设定的目标得以实现。不要听天由命。

提示和技巧

在 2021 年初推出之后,人工智能奖学金已经根据我们的 ML 工程师的反馈进行了几次迭代改进。我将分享我们学到的东西和我们随着时间的推移所做的改变,这样你就不必重新发明轮子了。

以下是我们关于计划、主题和一般设置的提示和偏好。

图片作者。使用生成 AIDall-E

策划

  • 最好每周与小组分享一次更新。
  • 一周的开发阶段不够长。最少两周。
  • 开发阶段可能感觉很短,因此在研究阶段的最后几周为此阶段做准备是至关重要的(例如,准备数据集和加载数据集的必要代码)。
  • 不要强迫 7 周和 2 周的 R&D 时间表,而是给人们在阶段之间交替的自由。有时候,在周期的中途有一个发展周会更方便。
  • 让人们计划自己的 R&D 时间。有些人喜欢每周半天,有些人喜欢每两周一天。两种选择都绝对没问题。
  • 考虑组织焦点日,让人们聚在一起做 R&D 的工作。这对于那些纠结于上下文切换或者通常容易分心的人来说尤其有用。

主题

  • 主题应该附有清晰的用例或示例研究问题。或者,可以在周期的后期分配时间对一个研究问题进行头脑风暴,以提供一些焦点。
  • 让工程师们自由选择工作主题让人感觉很自由。此外,我们还为人们提供提交提案的机会。
  • 理想情况下,每个主题都有大约相同数量的人负责。这是为了保持讨论的平衡,而不是制造孤立。
  • 增加在后续 R&D 周期中继续某个主题的可能性。这允许更雄心勃勃的目标。

一般设置

  • 团队工作比单独工作更有趣,更有动力。不要把群体搞得太大,以至于无法保持强烈的个人主人翁意识。每组两三个人就够了。
  • 让每个人都写下他们的研究,可以让其他人以后再读。我们的工程师更喜欢按时间顺序排列,而不是按主题组织。这是因为如果他们错过了一场单口相声,他们可以很容易地赶上来。
  • 对于 R&D 的大型合作项目,有人担任项目负责人是至关重要的。这也为学习项目管理的人提供了一个很好的机会。
  • 偶尔打乱一下 R&D 循环对士气很有好处。例如,看看这篇关于我们主办的内部人工智能竞赛的博文
  • 实习生并不积极参与 R&D 项目,但被邀请参加我们每周一次的人工智能脱口秀,在那里他们可以学习并参与讨论。
  • 这种人工智能伙伴关系模式非常适合 5-20 人左右的中小型团队。如果你的团队超过这个数目,考虑为每个特定的重点领域建立一个伙伴关系——比如 NLP、计算机视觉等。

最后

人工智能是一个快速发展的领域,作为一家应用人工智能公司,你需要跟上最新的发展。不仅要保持领先地位,还要吸引和留住稀缺人才。

在这篇博文中,我介绍了人工智能奖学金:一个面向一群人工智能工程师的 R&D 项目。它是在几次失败尝试的基础上创建的,可以在任何开发和应用人工智能的公司实施。我们的工程师很喜欢它,它已经成为我们独特的卖点之一。

奖学金成功的关键特征是:清晰的结构、强制性参与、专用时间、相关主题,以及有人推动与公司战略目标相关的创新。

如果您计划在您的组织中实施 R&D 计划,我很想听听您的意见并分享想法。另一方面,如果你已经有在你的公司建立这样一个项目的经验,请在评论区分享你的想法和智慧。

如果你想了解更多关于我们在 Slimmer AI 的工作,请随时联系我们或访问我们的网站。

散景互动图:第三部分

原文:https://towardsdatascience.com/bokeh-interactive-plots-part-3-683153f5f5ce

如何构建自定义交互式散景应用程序

康斯坦丁·叶夫多基莫夫在 Unsplash 上的照片

概观

这是散景互动图系列的第三篇也是最后一篇文章。第 3 部分建立在第 1 部分和第 2 部分的基础上。在继续之前,我建议阅读第 1 部分和第 2 部分。

https://medium.com/@katyhagerty19/bokeh-stock-comparison-tool-part-1-81afb176a14e https://medium.com/@katyhagerty19/bokeh-interactive-plotting-part-2-316f8d58f476

第 1 部分创建了应用程序的基础,并将管理基金与指数进行了比较。第 2 部分通过将可交易资产与指数进行比较,拓宽了应用的范围。如下所示,第 3 部分添加了一个新的选项卡和图表,让用户可以比较任意两种证券。 为清晰起见,本文将顶部“股票代码”小部件中的资产称为主要投资。“Stock 2 Ticker Symbol”小部件中的资产是相对于主要投资的资产。

图片作者。

第 3 部分还在小部件下的每个选项卡中添加了一个文本框。文本框显示主要投资升值或贬值的程度、两项投资当前价值之间的差异以及主要投资的成本基础。文本框可以很容易地比较两种不同投资的结果。

第 3 部分试图尽可能多地重用前面的代码。与第 2 部分相比,只有find_min_dateupdate发生了变化。第 3 部分为新文本框引入了一个新功能div_text

密码

这个项目的 Git 回购在这里链接

小工具

选项卡 3 的小部件与选项卡 2 的小部件非常相似,只是选项卡 3 有两个 TextInput 小部件。TextInput 小部件允许用户输入两个股票代码,并比较任意两个资产。此外,第 3 部分使用了一个新的小部件 Div,它与 HTML <div>元素保持一致。

与第 2 部分一样,字典存储新选项卡的小部件。一个新的字典,div,保存文本以填充新的文本框。

调用函数

下面显示了如何调用函数来初始化表 3 的数据和绘图。这遵循第 2 部分的格式,除了创建文本框的line 14div_text输出格式化文本。通过将sizing_mode设置为‘stretch_width’,文本框会拉伸以填充所有可用宽度。第 1 部分详细介绍了一个column对象如何存储所有的小部件。这个容器还将存储 div 小部件。所以不会占用整个浏览器宽度,只占用column的宽度。

功能

新函数div_text计算主要投资的增长、两项投资之间的差额以及主要投资的成本基础。它有三个输入— df_sourcecost_basisinvestment_typedf_source是一个数据框架,包含特定选项卡上引用的两项投资的数据。由于无法购买指数,如果为选项卡 1 调用div_text,则cost_basis为“不适用”。对于表 2 和表 3,cost_basis是一个float,代表在开始日期单个份额的初始投资成本。investment_type是描述主要投资的字符串。运行计算后,div_text将信息格式化并返回一个字符串,供文本框显示。

find_min_date包括用于标签 3 的额外elif。现在,当调用 Tab 3 时,该函数使用yfinance来检查每项资产何时上市。像以前一样,它返回两个日期中最近的一个,因为该日期表示两个资产的历史重叠的时间。这两种资产需要同样长的时间来升值,以便进行公平的比较。

回调函数

回调函数update,只有微小的改动。Line 20调用div_text更新选项卡 1 的文本框,而line 29对选项卡 2 和 3 做同样的事情。

结论

感谢您阅读散景互动剧情系列的最后一篇文章。这些文章的目标是一步一步地分解如何构建交互式散景图。希望这个应用程序能帮助你更好地理解散景,甚至启发你建立一个散景应用程序。

欢迎所有反馈。我总是渴望学习新的或更好的做事方法。请随时留下您的评论或联系我 katyhagerty19@gmail.com。

https://medium.com/@katyhagerty19/membership

散景互动图:第二部分

原文:https://towardsdatascience.com/bokeh-interactive-plotting-part-2-316f8d58f476

如何构建自定义交互式散景应用程序

图片由视觉故事||米歇尔Unsplash 上拍摄

概观

这是包含散景交互式可视化的三篇文章系列的第二部分。因为这篇文章是建立在前一篇文章的基础上的,所以我建议先阅读第 1 部分

https://medium.com/@katyhagerty19/bokeh-stock-comparison-tool-part-1-81afb176a14e

第 1 部分构建了 Bokeh 应用程序框架来比较管理基金和指数。第 2 部分扩展了该框架,增加了将指数与证券交易所中的任何证券进行比较的选项,如 eft、共同基金、股票和指数基金。第 2 部分功能位于应用程序的新选项卡上,如下所示。

图片作者。

第 2 部分旨在尽可能多地重用第 1 部分,但仍然需要对功能进行一些修改。由于选项卡 1 上的管理基金与指数图仍然使用这些功能,任何添加都必须以不破坏现有图的方式扩展功能。

对于选项卡 2 上的股票与指数图,两个数据集都依赖于yf_fund,因为雅虎财经同时跟踪指数和股票数据。因此,create_sourcemake_plot需要改进,以便在yf_fund生成两个数据集的情况下工作。之前,create_sourcemake_plot分别从yf_fundmanaged_fund获得一个输入。

密码

Git 存储库可以在这里找到

小工具

Tab 2 使用一种新的小部件 TextInput,它允许用户输入证券的股票代码。除此之外,选项卡 2 上的其他部件与选项卡 1 上的部件相匹配。因为现在有两组小部件,所以字典存储的是小部件而不是变量名。不是显式定义每个小部件,而是用一个循环来定义小部件。当编辑update以使用选项卡 1 和选项卡 2 时,该方法将被证明是有用的。

字典还存储 Dataframe 和 ColumnDataSource 对象。其他函数将填充这些字典。

功能

第 2 部分引入了一个新函数find_min_date。它使用tab_no并找到选项卡绘图的最小日期。对于选项卡 1,最小日期将取决于索引开始的时间。对于选项卡 2,该函数检查股票(fund_1)和指数(fund_2)的开始时间。选项卡 2 上股票与指数图的min_date将是最近的日期。最近的日期表示两个资产的时间线相交的开始。这是必要的,因为如果一种资产比另一种资产有更多的增长时间,这是不公平的比较。start_date_pickerend_date_picker小部件使用min_date来设置用户可以选择的最小日期。

与第 1 部分一样,yf_fund使用 Yahoo Finance 模块来获取股票市场数据。之前,yf_fund只读入指标数据。现在,它还可以获取给定股票代码的数据。这可能是股票,共同基金,或交易所交易基金。对yf_fund的唯一主要补充是line 18,它将股票分割考虑在内。每当一家公司决定将一股现有股票分割成多股时,就会发生股票分割。例如,当亚马逊在 1998 年发行 2 比 1 的股票分割时,现有股东的股票数量翻了一番。

在第 1 部分中,两个不同的函数managed_fundyf_fundcreate_source生成两个输入。然而,要使用零件 2,create_source也必须合并df_fund1df_fund2,即使yf_fund 创建了两者。如果yf_fund创建两个输入,则列名重叠。line 16中的rsuffix参数为df_fund2中的列添加后缀,以解决列名重叠的问题。lines 12line 13 搜索所有的列名,确定哪些列有投资头寸的数据。然后,line 16使用这些列来找出两项投资之间的差异

Line 6line 7标识保存图例标签的列的名称。Line 9line 10用更通用的名称重命名图例列。这些通用名是make_plot所必需的。关于为什么列需要占位符标签的更全面的解释,请参见make_plot

create_source中的变化类似,make_plot中的编辑包括添加变量,以便识别正确的列。make_plot只被调用两次——一次是在选项卡 1 上绘制共同基金与指数图,另一次是在选项卡 2 上绘制股票与指数图。因此,有时数据位于名为 Managed Position 和 Stock Position 的列中,有时数据位于名为 Stock Position 和 Stock Position_2 的列中。position1position2标识哪些列包含相关数据。label1label2TOOLTIPS弹出框的标签。

为了保持TOOLTIPS的正确格式,必须将position1position2添加为 f 字符串。f 字符串允许表达式嵌入到字符串中,只要这些嵌入的表达式在大括号内。TOOLTIPS如果列名包含空格,也使用大括号。为了正常工作,每个 f 弦需要三套背带。f 字符串最里面的一对表示嵌入的信息。最外面的两个大括号确保输出字符串包含一组大括号。输出需要一组大括号,因为列名包含一个空格。例如,如果为 Tab 2 图调用了make_plot,f 字符串将输出‘{Stock Position}’‘{Stock Position_2}’,并且TOOLTIPS将搜索这些列名。

create_source定义了legend1legend2。它们标识了df_source中包含股票代码的列。图例使用这些列名作为标签,如line 21line 23所示。正如第 1 部分所讨论的,散景以表格格式存储所有可更新的信息。因为每当用户选择新的证券时,图例标签都会改变,所以它们必须存储在列中。

重要的是要记住make_plot只在初始化时被调用。不是每次用户更改输入和更新数据集时都调用它。在更新期间,新数据替换了source中的旧数据。替换source中的数据会改变图形,而不会调用make_plot

回调函数

update有了新的参数tab_no,因为不同的标签需要不同的功能。对于选项卡 1,update调用managed_fundyf_fund。对于选项卡 2,update只调用yf_fund。然后,create_sourcemanaged_fund和/或yf_fund获取输出数据,并合并两个数据帧。之后,合并的 Dataframe 被转换成new_source,一个 ColumnDataSource 对象。接下来,new_source替换source中的数据。再次重申,更新source是改变显示数据所需要的。

用事件触发回调

回调函数update需要链接到事件。更改小部件值调用update。散景回调函数只接受三个参数,attroldnew。然而,现在应用程序有多个标签,update需要知道刷新哪个标签。partial解决了这个困境。partialfunctools模块的一部分,向现有函数添加额外的参数。

案例研究:标准普尔 500 对间谍

由于股票与指数图有能力收集任何可交易基金的数据,该应用程序可以比较指数基金如何达到其跟踪的指数。SPY 是跟踪标准普尔 500 指数的 ETF,也是最受欢迎的 ETF 之一。自 1993 年成立以来,SPY 的平均年回报率为 10.5%。如下所示,间谍镜像标准普尔 500 很好,但并不完美。

图片作者。

案例研究:标准普尔 500 对 GME

市场中的时间胜过市场时机。

去年,GameStop 股票爆炸。简而言之,许多个人投资者集体决定购买 GME 的股票,这推动了需求,提高了股价。他们疯狂的想法给投资者带来了巨大的收益,也给做空股票的华尔街对冲基金带来了巨大的损失。

GME 上涨如此之快,以至于 1000 美元的投资会在不到 4 周的时间里产生超过 20000 美元的收益。然后,股票价格猛跌。尽管 GME 在几天内有不可思议的增长,GME 强调了不要试图把握市场时机的重要性。只要问问那些高价买入的人,他们很快就看到了投资池。

图片作者。

后续步骤

第 3 部分添加了一个新的选项卡,其中有一个股票对比图和一个文本框,显示投资的增值、两种投资之间的差异以及成本基础。

https://medium . com/@ katyhagerty 19/bokeh-interactive-plots-part-3-683153 F5 F5 ce

欢迎所有反馈。我总是渴望学习新的或更好的做事方法。请随时留下您的评论或联系我 katyhagerty19@gmail.com。

https://medium.com/@katyhagerty19/membership

参考

[1]尼克拉斯,史蒂文。间谍:SPDR S & P 500 ETF 信托 (2021),Investopedia

散景互动图:第 1 部分

原文:https://towardsdatascience.com/bokeh-stock-comparison-tool-part-1-81afb176a14e

如何指导构建自定义交互式散景应用程序

照片由约尔戈斯·恩特拉哈斯Unsplash 拍摄

项目概述

了解一个数据集的大趋势和细微差别的最佳方式是自己探索它。如果轴的边界变宽会发生什么?如果这个初始条件改变了,结果会受到什么影响?不幸的是,静态图不能提供这种水平的输入。所见即所得。令人欣慰的是,散景通过构建美观、直观的应用程序来填补这一空白,从而允许更深入的数据探索。

为了展示散景的交互性,本文解释了如何构建一个绘制股市数据的散景应用程序。如下所示,第一部分将重点比较管理基金和指数。该应用程序将显示标准普尔 500 (GSPC)或道琼斯工业(DJI)指数。第二部分将创建一个新标签,将指数与任何有股票代码的东西进行比较,如股票、指数、共同基金、ETF,甚至是它试图模仿的指数基金。第 3 部分比较了两只不同的股票。这篇教育文章深入探讨了如何在散景中构建情节并添加交互性。它不应被视为财务建议。

作者图片

先决条件

本文假设您对 NumPy 等 Python 有基本的了解,在 Pandas 中使用数据帧,并创建函数。这篇文章解释了如何在 Jupyter 笔记本中设置交互式散景应用程序。建议基本熟悉 Jupyter 笔记本的打开和运行。

什么是散景?

Bokeh 是一个数据可视化库,它采用 Python 编写的静态和交互式绘图,并将它们转换为浏览器格式。使 Bokeh 比其他交互式绘图库(如 Plotly)更强大的是它的小部件,即控制绘图数据的用户界面元素。Plotly 为交互式绘图提供了 4 个自定义控件,而散景提供了几十个控件。此外,Bokeh 允许通过 CustomJS 回调进行进一步定制,这允许用户用 JavaScript 为现有 Bokeh 小部件无法解决的特殊情况编写独特的行为。由于这个应用程序只使用现有的散景小部件,所以本文不会涉及 CustomJS 回调。

重要的散景术语

  • 小部件-更新图中数据的用户界面元素。例如,按钮、下拉菜单和日期选择器都是小部件。
  • 字形-代表数据系列的绘图标记
  • 模型——散景对象的总称。小部件、字形和绘图工具都被视为模型。
  • 绘图-保存字形的容器。图不包含微件。
  • 文档—散景应用程序中所有对象的顶级容器

下图说明了该应用中明确定义的不同散景模型之间的关系。

这张经过编辑的流程图源自散景文档。原图文可在这里找到。

散景服务器

应用程序通过散景服务器与 Python 代码进行通信。不是所有的散景应用程序都需要它,但是像这样的应用程序需要它,只要用户做出选择并更新数据集,它就会引用 Python 函数。Python 代码必须在散景服务器上运行。对 Python 代码进行所有编辑后,启动散景服务器。一旦进入会话,服务器将不会合并在 Python 中所做的编辑。

要运行散景服务器:

  1. 在 Python 代码所在的文件夹中打开一个终端
  2. 运行以下命令:
bokeh serve --show main.py

这张图片展示了代码、散景服务器和浏览器的交互方式。

这张编辑过的图片来自散景文档。原文可以在这里找到

有关使用散景服务器的更多信息,请参见此处

密码

此应用程序的 Git 存储库可从这里获得。

该存储库包含 Jupyter 笔记本和 Python 的代码。这两种代码具有相同的功能。Jupyter 笔记本将在笔记本中输出应用程序,而 Python 代码将在默认浏览器中显示输出。

本文展示了 Jupyter 笔记本中的代码。在 Jupyter 笔记本中编辑散景互动应用程序要容易得多,因为它不需要每次编辑都重新启动散景服务器。

导入库和模型

首先,导入库、工具和小部件。yfinance库从 YahooFinance 提取数据,并将其转换成 Python 友好的格式。

Jupyter 笔记本的交互性

要在 Jupyter 中启用 Bokeh 的交互式功能,请将代码包装在一个函数中。notebook_url参数必须与 Jupyter 笔记本页面的位置相匹配,该页面可以在浏览器栏中找到。

def modify_doc(doc):
    [code]show(modify_doc, notebook_url="localhost:8888")

与每次编辑后重启散景服务器相反,你只需要运行 Jupyter 笔记本来显示编辑。

小工具

小部件是用户向散景应用程序提供输入的方式。为了将管理基金与指数进行比较,该应用程序需要一些输入,如最初投资了多少(principal)和管理基金的当前价值(current_value)。ticker指定与管理基金进行比较的指数。start_dateend_date定义日期的范围。所有这些变量都可以在以后通过用户输入进行更改。但是首先,它们需要被初始化。

min_datemax_date变量取决于所选的步进。这些变量作为界限,禁止用户在索引不存在时选择日期。毕竟,如果管理基金比指数有更多的增长时间,这不是一个公平的比较。

小部件。图片作者。

该应用程序使用 Select、DatePicker 和 Spinner 小部件。fund_2是一个选择小部件,它创建了一个索引下拉菜单。start_date_pickerend_date_picker使用包含弹出日历的 DatePicker 小部件轻松选择日期范围。微调控件为principal_spinnercurrent_value_spinner 提供了基础,并允许手动输入。

调用函数

三个功能— yf_fundmanaged_fundcreate_source —为应用程序创建数据。make_plot获取数据并创建绘图。下面是这些函数的调用方式:

功能

yf_fund函数返回指数的成本基础和开始日期和结束日期之间每天的投资平仓数据框架。列df_yf_fund[‘legend’]仅包含索引的股票代号,图图例将其用作标签。这必须存储在列中,而不是作为变量,因为 Bokeh 要求所有可更新的信息都以表格格式存储。

yf_fund类似,managed_fund返回管理基金的年化收益率和基金在给定时间段内的表现数据。即使管理基金波动,该应用程序也使用占位符,并假设管理基金一致升值(或贬值)。为了创建一个平滑的指数曲线,该函数计算管理的基金在时间段内每天的头寸,甚至在市场关闭的日子。然后,它过滤df_managed_fund,只包括市场开放的日子。如line 12所示,df_managed_fund使用df_yf_fund作为过滤器,因为df_yf_fund只包含交易日。

create_source函数将管理的基金和指数数据合并到一个数据框架中。它还计算日期范围内每天两个基金头寸之间的差额。

make_plot功能将这一切集合在一起。首先,它获取df_source数据帧并将其转换为source,一个 ColumnDataSource 对象。散景图要求将数据格式化为 ColumnDataSource。ColumnDataSource 接受一个 DataFrame 并将每个列名映射到该列的数据,类似于 dictionary 对象。column data source 中的每一列的长度必须相同。

TOOLTIPS格式化当光标悬停在数据点上时出现的弹出框。这里,TOOLTIPS是元组列表。每个元组包含一个标签和一个值。后跟列名的@符号允许TOOLTIPS显示来自source的值。例如,‘[@Date](http://twitter.com/Date){%F}’将显示sourceDate列的值。包含空格的列名需要大括号。第二对大括号指定数据的格式。{%F}允许使用line 15HoverTool 中的formatters={‘[@Date](http://twitter.com/Date)’: ‘datetime’}参数设置@Date值的格式。这很麻烦,但这是格式化日期的唯一方法。

显示十字光标工具、悬停工具和工具提示的绘图。图片作者。

plot的高度和宽度将在布局允许的情况下占据尽可能多的空间。该应用程序使用两个线形符号来绘制数据。CrosshairTool当光标在绘图上时,启用十字线。HoverTool当光标悬停在数据点上时,弹出TOOLTIPSformatters参数设置TOOLTIPSDate列数据的格式。即使source中的Date列被格式化为 datetime,TOOLTIPS也无法识别它,因此需要formatters参数。line 17 中定义的图表图例点击策略允许用户通过点击图例来隐藏和取消隐藏数据系列。

回调函数

update功能告诉散景如何更新绘图。这就是所谓的回调函数。所有回调函数都需要attroldnew作为输入,即使没有使用。update函数从窗口小部件中拉出新的start_dateend_dateprincipalcurrent_valueticker变量。由于 DatePicker 小部件以字符串形式返回其值,因此必须将start_dateend_date转换为日期时间,以便使用yf_fund。接下来,调用yf_fundmanaged_fund函数来获取新的、更新的数据。然后,create_source合并两个数据帧以创建new_source。最后,sourcenew_source数据更新。由于sourceplot1中创建了现有的字形,用新数据更新source会将该数据拉入绘图。没必要再叫make_plot了。

用事件触发回调

为了让应用程序知道何时调用update,它必须链接到一些事件。小部件有一个on_change方法,每当指定的变化发生时,它将触发一个回调函数。这里,每当用户改变小部件的值时,回调函数update就会被激活。虽然这个应用程序只使用on_change来触发回调,但散景提供了像ButtonClickMouseMove等更多事件。

start_date_picker.on_change('value', update)
end_date_picker.on_change('value', update)
principal_spinner.on_change('value', update)
current_value_spinner.on_change('value', update)
fund_2.on_change('value', update)

布局

最后,Bokeh 需要知道如何在应用程序中组织情节和小部件。所有的小部件都堆叠在一个名为inputs的列中,使用(你猜对了!)中的column功能。接下来,plot1inputs排成一行创建row1。散景的Panel函数获取row1并将其格式化为散景的Tab容器。将所有东西放在一个标签上简化了第 2 部分和第 3 部分中涉及的应用程序升级。最后在doc上加上layoutlayout包含代码中的所有型号。doc是一个文档,一个与散景服务器接口的散景容器。另外,docmodify_doc的输入,如“Jupyter 中的交互性”一节所定义。

后续步骤和结论

第 1 部分讲述了散景的基本原理,并为应用程序创建了一个基础。第二部分将在这个框架的基础上创建一个新的选项卡,将指数与股票或其他可交易资产进行比较。最后,第 3 部分将创建一个选项卡来比较两种可交易资产。

该应用程序的主要目标是展示散景互动功能的力量。它不仅通过显示大图趋势,还通过显示每日指标来实现这一点。这种交互性使用户能够检查并确定何时出现了可观的收益或损失。此外,该应用程序计算任何一天的两项投资之间的差异。与一刀切的静态图相反,这种直观的应用程序允许用户输入条件,以便他们可以更好地探索数据并回答自己的问题。

感谢您阅读我的文章。欢迎所有反馈。我总是渴望学习新的或更好的做事方法。请随时留下您的评论或联系我 katyhagerty19@gmail.com。

https://medium.com/@katyhagerty19/bokeh-interactive-plotting-part-2-316f8d58f476 https://medium.com/@katyhagerty19/membership

通过 5 个简单的步骤提高 OpenSearch 的性能

原文:https://towardsdatascience.com/bolster-opensearch-performance-with-5-simple-steps-ca7d21234f6b

了解如何提高 OpenSearch 集群的性能,最终加速您的工作负载

约翰·卡梅隆Unsplash 上拍照

OpenSearch 旨在帮助每个人更快地找到他们需要的东西。但是多快才算“够快”呢?为了回答这个问题,Gmail 的创始人 Paul Buchheit 为所有数字交互引入了“100 毫秒延迟规则”。他解释说,100 毫秒是一个“互动感觉即时”的阈值。亚马逊进行的一项研究发现,他们网站上每增加 100 毫秒的延迟就会损失 1%的销售额。因此,企业主优化延迟、准确性和成本至关重要。通过提高 OpenSearch 集群和搜索速度,您可以增强客户的用户体验,从而显著增加您的收入。为了帮助你做到这一点,我将带你通过一些简单和先进的步骤来提高 OpenSearch 的性能。我还将讨论我在 Searchium.ai 帮助开发的一个新的搜索加速器插件的好处。

步骤 1:为您的用例选择正确的刷新间隔

照片由Genessa panain iteUnsplash 上拍摄

OpenSearch 中的索引数据不能立即访问。为了提高效率,文档在被索引到段中之前首先通过内存中的缓冲区。如果有大量繁重的索引进程,那么首先将令牌保存在内存缓冲区中,然后将它们转移到碎片段,这样会更有效;这一过程称为“刷新”。增加和减少刷新间隔有成本和好处。缩短刷新周期会降低内存缓冲区的效率,因为在将令牌索引到段之前,内存缓冲区只能保存一定数量的令牌。通常,增加刷新间隔会提高搜索性能。但是,如果刷新间隔增加太多,刷新过程将需要更长时间才能完成,因为缓冲区中有大量数据,这可能会影响搜索性能。此外,长时间间隔意味着数据在内存缓冲区中的时间更长,因此在刷新缓冲区之前是不可搜索的。在大多数情况下,一秒钟的默认刷新间隔就足够了。但是,请记住,在刷新期间会使用许多资源,刷新间隔应该适合您的使用情形。例如,如果您正在处理前几天的数据,并且不需要接近实时的数据,则可以每天只刷新一次。

您可以简单地使用 refresh API 来更改刷新间隔,如下所示:

PUT /<index_name>/_settings{
 “index”: {
 “refresh_interval”: “30s”
  }
}

步骤 2:优化缓存利用率

有多种缓存可以帮助您提高搜索性能,比如文件系统缓存、请求缓存、查询缓存。例如,您可以通过增加节点级查询缓存的大小来提高性能。OpenSearch 使用节点级查询缓存来存储查询结果,以便在再次搜索索引时可以更快地返回这些结果。默认情况下,缓存最多可以存储 10,000 个查询,并占用堆总空间的 10%。OpenSearch 保留一个查询历史来跟踪查询的出现,并评估一个查询是否有资格进行缓存。缓存使用最近最少使用(LRU)策略—当缓存填满时,它会删除一段时间内未被访问的查询。

如果节点级查询缓存太小,您的 OpenSearch 性能可能会受到影响,因为有些查询可能不会被缓存。要更改大小,您可以更改全局设置参数-indexes . queries . cache . size,它接受百分比值(如 5%)或精确值(如 512MB ):

上面提到的缓存是在节点级别维护的,这限制了它们在某些情况下的有用性。比如说。如果您连续运行同一个请求两次(并且有一个或多个副本并使用默认的循环算法),每个请求都将进入不同的分片副本,从而阻止节点级缓存的帮助。

另一种方法是使用碎片级请求缓存来提高搜索速度。当针对一个或多个索引执行搜索查询时,每个相关的碎片在本地进行搜索,并将其本地结果发送给协调节点,协调节点将这些碎片级结果合并成一个“全局”结果集。碎片级请求缓存模块在每个碎片上缓存本地结果。这使得大量频繁使用的搜索请求能够非常快速地返回结果。搜索应用程序的用户经常一个接一个地运行类似的请求,因此最大限度地利用这个缓存可以显著提高搜索速度。

默认情况下,请求缓存设置为 1%。这可以通过编辑 opensearch.yml 文件参数来更改:indexes . requests . cache . size

步骤 3:优化分片和副本

作者图片

碎片是 OpenSearch 高可用性和高性能的主要驱动因素之一。在 OpenSearch 中,每个查询在每个分片的一个线程上运行。多个分片可以并行执行。如果有多个碎片,那么就有多个线程同时运行。碎片支持并发搜索,提高了搜索效率。然而,拥有太多碎片也有其自身的缺点。查询过程包括合并碎片——碎片越多,花在合并上的时间就越多。除此之外,每个碎片利用资源进行映射、存储集群状态、查询等。碎片数量越多,资源利用率就越高,从而降低性能。没有适合所有场景的单一数量的碎片。一个流行的策略是从一个碎片开始,然后不断增加,直到你得到最好的结果。通常,推荐的碎片大小应该在 30 GB 到 50 GB 之间。

您可以使用 API 更改碎片和副本的数量:

PUT <index_name>/_settings{
 “index”: {
 “number_of_shards”: 3,
 “number_of_replicas”: 1
 }
}

第四步:管理你的指数

照片由 Nathália RosaUnsplash 上拍摄

构建适合您的用例的 OpenSearch 数据和索引:

将你的数据分解成更小的索引 — OpenSearch 将数据存储在索引中。要存储数据,可以使用一个或多个索引。您的所有数据不需要保存在一个索引中。例如,您可以选择一个索引来存储一个月、一天或一小时的数据,这取决于您的用例。

避免嵌套字段 —恰当的文档设计有助于加快请求的处理速度。当存在嵌套字段和父子层次结构时,查询需要更长时间。要加快查询速度,请尽可能使文档平坦。

将映射标识符视为关键字

没有必要将每个数值都映射为数值字段数据类型。整数和长型字段适用于 OpenSearch 范围查询。但是,术语级查询更适合关键字字段。

为了提高检索速度,如果您不打算使用范围查询来搜索字段,请考虑将映射字段作为关键字字段。例如,像 ISBN 或产品 ID 这样的标识符可以作为关键字字段处理,而不会影响有效性。

偶尔重新索引您的数据

OpenSearch 中的数据是不可变的。在 OpenSearch 中更新文档时,会创建一个新文档,而不是更新现有文档。新文档被分配一个版本号,这有助于 OpenSearch 跟踪最新和最相关的文档。但是,由于包含了新文档和旧文档,索引会显著扩展。重新索引解决了这个问题。重新编制索引后,您的索引将只包含最新的信息,从而节省内存并加快后续搜索。

这可以使用 API 来完成,如下所示:

POST _reindex{
 “source”: {
 “index”: “my-index”
 },
 “dest”: {
 “index”: “my-new-index”
 }
}

第五步:使用加速搜索插件

Nicolas HoizeyUnsplash 上拍摄的照片

你选择 OpenSearch 可能是因为你需要快速访问大量数据,不想使用传统的数据库。然而,即使有最好的优化和软件修改,如果没有足够的计算能力,性能仍然是不够的。足够的缓存、磁盘空间、CPU 和 RAM(随机存取存储器)对于峰值 OpenSearch 性能至关重要。

一个好的选择是使用具有扩展 RAM 的高级计算机。内存越多,搜索延迟就会越短。RAM 使应用程序能够在短期内存储和访问数据。它存储您的计算机正在使用的信息,以便可以快速访问。这种设备的一个例子是 AWS 的 m5 实例,它由英特尔至强白金 8000 系列处理器组成。这种 CPU 处理器和大 RAM 空间的组合可以显著提高您的搜索性能。

另一个不错的选择是将您的工作负载从基于 CPU 和 RAM 的解决方案转移到非传统的硬件解决方案。升级您的计算资源非常昂贵,而且可能无法减少延迟。有时,需要涉及更高级资源的解决方案来解决延迟问题并提高搜索性能。GSI 科技的 APU 就是这样一种计算资源。

想象一下,能够直接在内存中而不是在 CPU 中进行计算操作,避免内存处理器瓶颈并享受难以置信的快速搜索性能。其实有一个 OpenSearch 插件,在 APU 上实现了强大的语义向量搜索。使用 Searchium 的 OpenSearch k-NN 插件,除了上面提到的 OpenSearch 改进建议之外,还可以通过 SaaS 平台的付费订阅轻松访问,这是进一步加快搜索速度的一种方式,同时还可以提高结果准确性并降低基础设施成本。安装插件很容易。该插件允许向量相似性搜索像任何标准的 OpenSearch 查询一样简单地运行。该插件以标准的 OpenSearch 格式提供相似性搜索结果。查看更详细的解释如何安装和使用 Searchium 的 OpenSearch KNN 和 Searchium 的网站,在那里你可以获得加速器插件的全部访问权限。甚至有一个免费层,您可以在那里进行测试!

做你的研究,了解你的公司的需求,考虑你需要多少和多频繁地操作、查询和存储数据。这将帮助你了解你需要什么样的计算资源来获得最好的 OpenSearch 体验。

结论:

数据对每个企业都至关重要,尤其是在当今快节奏的环境中。快速、有效地处理复杂数据变得比以前更加重要。OpenSearch 是一个非常棒的搜索引擎,它可以搜索数据并几乎立即得到结果。由于 OpenSearch 的出色性能对于满足业务需求至关重要,我真诚地希望这篇文章能够帮助您消除一些阻碍实现最高性能的障碍。

回旋镖图

原文:https://towardsdatascience.com/boomerang-plot-9ae4aed419d4

快速发现概化模型的可视化

作者图片

aiqc回飞棒图可视化了实验中每个模型的每次分割(训练、验证、测试)的各种性能指标。当模型轨迹的点紧密聚集/精确时,这意味着模型已经发现了概括每个群体的模式。

🧮如何评价众多调校过的车型

假设您刚刚训练了一大批模型,这些模型看起来都表现得相对较好。根据你关心的指标,你怎么知道哪一个是最好的?为了回答这个问题,你应该这样开始:

  • 通过类似[sklearn.metrics.f1_score](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.f1_score.html)的函数运行原始预测
  • 对每个分割/折叠(训练、验证、测试)都这样做。
  • 对每个模型都这样做。
  • 对每个指标都这样做。

在这一点上,您可以为每个模型计算聚合指标。然而,由于只有 2 或 3 个分割可以学习,聚集指标不是很有用。

例如,如果您有一个模型,它分别以 100%和 95%的准确率过度拟合训练和评估数据,但以 82%的准确率拒绝维持数据,那么大多数聚合指标将会产生误导。你必须引入一个范围或标准偏差度量来理解它,但在这一点上,你不能自己看看 3 个分裂吗?🤷因此,您又回到了从原始数据表开始的地方。

为什么不想象一下呢?如果你已经用 AIQC 训练了一个[Queue](https://aiqc.readthedocs.io/en/latest/notebooks/api_low_level.html#8.-Queue-of-training-Jobs.)模型,这很简单:

queue.plot_performance(
    score_type:str=None,
    min_score:float=None,
    max_loss:float=None
)
  • score_type:从以下分类(准确性<默认>、f1、roc_auc、精度、召回)或量化 (R <默认>、MSE、解释方差)的度量中选择。
  • minmax参数充当阈值,移除任何不符合分割的模型,并调整图形大小以适应符合分割的模型。

作者图片

🪃你可能已经猜到了,它被命名为回旋镖图,因为它为每个模型制作了曲线。

AIQC 之所以能够做到这一点,是因为当Queue在训练时,它会根据Queue.analysis_type自动为每个模型的每次拆分/折叠生成度量绘图。因此,当需要评估时,从业者可以简单地调用这些信息。

🔬模型性能解读

可视化的美妙之处在于它使从业者能够进行他们自己的无人监督的解释。我们通过查看图表来执行我们自己的聚类分析。

快速浏览一下上面的图表,我们会发现:

  • 右边的建筑比较差。
  • 左侧的高性能模型在训练数据上过度拟合。
  • 最概括的是橙色的Predictor.id==15
  • 然而,我们还没有完成训练。我们需要调整 orange 模型,看看能否提高它的性能。所以接下来我会看看它的参数和学习曲线,看看有什么可以改进的地方。
predictor = aiqc.orm.**Predictor**.get_by_id(15)predictor.get_hyperparameters()
predictor.plot_learning_curve()

⏩我们能更快获得洞察力吗?

经历过多次这样的循环之后,我决定将整个体验打包到一个实时仪表板中,以解决以下问题:

  • 在训练模型时,将模型添加到绘图中。
  • 训练队列和绘图的独立过程。
  • 更改分数和指标,无需回忆情节
  • 获取超参数和其他补充信息,无需手动查询 ORM。
from aiqc.lab import **Tracker**
app = Tracker
app.start()“📊 AIQC Tracker [http://127.0.0.1:9991](http://127.0.0.1:9991) 📊”

瞧——我们现在在博客文章的开头有了 gif 格式的实时 Dash 应用程序。

AIQC 是本帖作者写的开源库 请考虑在 https://github.com/AIQC/aiqcgithub⭐上给 aiqc 一颗星

通过 ONNX 转换提升任何机器学习模型

原文:https://towardsdatascience.com/boost-any-machine-learning-model-with-onnx-conversion-de34e1a38266

关于如何将模型转换为 ONNX 的简单理解

开放神经网络交换 (ONNX)是一个开源生态系统,旨在跨各种平台标准化和优化人工智能模型。

它是一种机器可读格式,可用于在不同软件应用程序和框架(例如 TensorFlow、PyTorch 等)之间交换信息。).

照片由 Sammy WongUnsplash 上拍摄

历史上,ONNX 格式被命名为太妃糖,由脸书的 PyTorch 团队开发。该框架于 2017 年底发布,由微软和脸书共同撰写。

从那以后,ONNX 格式已经被其他几家公司支持,包括英特尔、AMD 和 IBM。

我个人已经和onnxruntime一起工作了几个月,我很高兴它得到了大量框架和项目的支持。

互操作性作为 ONNX 的关键特性,是该工具的最佳资产之一,并且使包含在所有 ML 项目中变得非常有趣。

“ONNX 运行时是一个跨平台的推理和训练机器学习加速器”——ONNX 运行时

在这篇文章中,我将向您展示将您的模型转换为 ONNX 需要遵循和理解的步骤。我还将列出所有可用的库,根据您使用的框架,这些库可用于将您的模型转换为 ONNX。

此外,我将包括一个将 PyTorch LSTM 模型转换为 ONNX 的例子。

👣转换步骤

所有的框架都有自己的方式将它们的模型转换成 ONNX。但是您需要遵循一些常见的步骤。

让我们看看将您的模型转换为 ONNX 需要遵循哪些步骤。

-🏋️训练你的模型

很明显,您需要一个经过训练的模型来将其转换为 ONNX,但是您需要注意模型架构和模型权重的可用性。

如果您只保存模型权重,您将无法将其转换为 ONNX,因为模型架构是将您的模型转换为 ONNX 所必需的,并且非常重要。

有了模型架构,ONNX 就能够跟踪模型的不同层,并将其转换为图形(也称为中间表示)。

模型权重是用于计算模型输出的不同层的权重。因此,它们对于成功转换您的模型同样重要。

-📝输入名称和输出名称

您将需要定义模型的输入名称和输出名称。这些元数据用于描述模型的输入和输出。

- 🧊输入样本

如前所述,ONNX 将跟踪模型的不同层,以便创建这些层的图形。

在追踪图层时,ONNX 还需要一个输入样本来了解模型如何工作以及使用什么运算符来计算输出。

所选样本将作为模型第一层的输入,并用于定义模型的输入形状。

-🤸‍♀️动态轴

然后,ONNX 需要知道模型的动态轴。在转换过程中的大多数时间,您将使用批量大小 1。

但是如果您想要一个可以接受一批 N 个样本的模型,您将需要定义模型的动态轴来接受一批 N 个样本。

例如,这样导出的模型将接受大小为[batch_size,1,224,224]的输入,其中“batch_size”可以是 1 到 n 之间的任何值

-🔄转换评估

最后,您将需要评估转换后的模型,以确保它是一个可持续的 ONNX 模型,并且按照预期工作。评估转换后的模型有两个独立的步骤。

第一步是使用 ONNX 的 API 来检查模型的有效性。这是通过调用onnx.checker.check_model函数来完成的。这将验证模型的结构,并确认模型是否具有有效的 ONNX 方案。

通过检查节点的输入和输出来评估模型中的每个节点。

第二步是将转换模型的输出与原始模型的输出进行比较。这通过用numpy.testing.assert_allclose功能比较两个输出来完成。

该功能将比较两个输出,如果两个输出不相等,将根据rtolatol参数产生错误。

常用 1e-03rtol1e-05atol进行比较,其中rtol代表相对公差,atol代表绝对公差。

🔧将模型转换为 ONNX 的工具

对于每个框架,都有不同的工具将您的模型转换成 ONNX。我们将列出每个框架可用的工具。

列表可能会随着时间的推移而改变,所以如果您发现了没有列出的工具,请在评论中告诉我。

  • PyTorch : [torch.onnx](https://pytorch.org/docs/stable/onnx.html)
  • 张量流 : [onnx/tensorflow-onnx](https://github.com/onnx/tensorflow-onnx)
  • 喀拉斯 : [onnx/keras-onnx](https://github.com/onnx/keras-onnx)
  • Scikit-learn:T3
  • 咖啡馆 : [htshinichi/caffe-onnx](https://github.com/htshinichi/caffe-onnx)
  • MXNet : [mx2onnx](https://mxnet.apache.org/versions/1.9.0/api/python/docs/tutorials/deploy/export/onnx.html)
  • py torch——闪电 : [onnx-pytorch-lightning](https://pytorch-lightning.readthedocs.io/en/latest/common/production_inference.html#convert-to-onnx)
  • 变形金刚:🤗[Exporting Transformers models](https://huggingface.co/docs/transformers/serialization#onnx)
  • PaddlePaddle : [Paddle2ONNX](https://github.com/PaddlePaddle/Paddle2ONNX)

现在您已经知道了转换是如何工作的,并且拥有了将您的模型转换为 ONNX 的所有工具,您已经准备好进行实验,看看它是否对您的 ML 项目有所帮助。我打赌你会发现它很有用!

您可以查看我在下一节提供的完整 PyTorch 示例,或者直接阅读本文的结论。

🧪完整 PyTorch 示例

我最近需要将 PyTorch 模型转换为 ONNX。该模型是一个简单的 LSTM 预测模型,用于预测输入序列的下一个值。

该模型来自我参与的一个更大的开源项目,该项目旨在使训练和服务预测模型自动化。那个项目叫做让我们变富

需要注意的是,我使用 PyTorch Lightning 来训练模型。这个框架是 PyTorch 的一个包装器,允许你提高你的训练过程。

如果你想了解更多关于 PyTorch Lightning 的知识,可以查看 PyTorch Lightning 文档

首先,您需要下载我们将用于训练和转换模型的数据示例。您可以用这个简单的命令下载它:

$ wget https://raw.githubusercontent.com/ChainYo/make-us-rich/master/example.csv

该文件包含 365 天内每小时的 BTC(比特币)值。我们将使用这些数据创建序列来训练我们的 LSTM 模型。

为了训练一个 LSTM 模型,输入数据应该被转换为数据序列。我们将使用这些预处理函数将数据转换为模型使用的序列:

创建序列所需的所有预处理功能

我们将使用这些函数来获得我们的train_sequencesval_sequencestest_sequences

从数据中创建所需的序列

既然我们已经准备好了模型要使用的序列,那么让我们详细看看模型架构、数据集数据加载器类。

处理模型使用的数据的数据集和数据加载器类

这两个类用于加载将用于训练模型的数据。我不想描述 PyTorch 和 PyTorch Lightning 中加载数据的所有细节,因为这不是本教程的重点。

如果你需要更多关于 *Dataset* *DataLoader* 类的解释,这里有一篇很棒的 文章 关于它。

这是我们将要使用的 LSTM 模型架构:

示例的模型架构

既然我们已经定义了模型架构和数据加载方式,我们就可以开始训练模型了。

以下是我使用的训练循环:

用 PyTorch Lightning 创建的训练循环

经过训练,我们现在有了一个模型检查点,我们希望将其转换为 ONNX。我们将从检查点文件加载模型,通过数据加载器加载数据样本,并将模型转换为 ONNX。

下面是转换的代码:

您现在应该在当前目录中有一个名为model.onnx的文件。

最后一步是评估转换后的模型,以确保它是一个可持续的 ONNX 模型,并按预期工作。

如果一切顺利,您应该会看到以下输出:

 🎉 ONNX model is valid. 🎉

您已成功将您的模型转换为 ONNX。现在,您可以在任何支持 ONNX 的框架中和任何机器上使用转换后的模型。

结论

在这篇文章中,我们介绍了如何将你的模型转换成 ONNX,以及如何评估转换后的模型。另外,我们已经看到了一个预测 LSTM PyTorch 模型的例子。

我希望这篇文章能帮助你理解一个模型到 ONNX 的转换过程。我也希望你对 ONNX 的个人体验能帮助你提升你的 ML 项目,就像我帮助我提升我自己的项目一样。

ONNX 框架还包括许多其他的特性,这些特性在这篇文章中没有提到,比如为你的模型和硬件优化训练和推理的能力。这可以在下一篇文章中讨论,让我知道你是否感兴趣。

如果您有任何问题或面临任何问题,请随时联系我。我很乐意帮助你。

通过简单的数据扩充提高文本分类任务的性能

原文:https://towardsdatascience.com/boost-performance-of-text-classification-tasks-with-easy-data-augmentation-1420d45b914a

自然语言处理任务的文本数据扩充

图片由菲利克斯·利希滕费尔德皮克斯拜拍摄

对小样本数据的训练增加了过度拟合的机会。数据扩充是一种创建现有数据的人工相似样本的技术。数据扩充技术通常用于模型需要大量数据,但我们对数据的访问有限的任务。它甚至可以帮助模型在小样本数据上很好地概括。

数据增强非常成功,经常用于卷积神经网络(CNN)模型,因为它通过进行小的改变,如剪切、翻转、旋转、模糊、缩放等,来创建图像数据的人工样本。但是当涉及到 NLP 任务时,文本数据的数据扩充就不那么容易了。

在本文中,我们将讨论 Jason Wei 和的一篇论文,该论文讨论了如何对文本数据执行数据增强以提高文本分类任务的性能。

开始使用:

Jason Wei &邹凯的论文 **“Easy Data Augmentation Techniques for Boosting Performance on Text Classification Tasks”**探讨了 4 种简单但强大的文本增强技术,它们是增强文本数据的良好基准:

  • 同义词替换
  • 随机插入
  • 随机交换
  • 随机删除

本文还在 5 个基准文本分类任务上执行了上述增强技术,从而提高了卷积和递归神经网络的性能。

我们现在将讨论上述每种文本增强技术是如何在幕后工作的,以及它对文本分类任务的改进。

1)同义词替换(SR):

同义词替换技术从句子中随机选取 n 个单词,不包括停用词,并用随机选取的同义词替换这些单词。这种技术执行原地单词替换。

对于下面提到的例句,我们随机选取 n=2 个单词(sad,played)并用它们的同义词替换它们。

句子:大概是我有史以来最喜欢的电影,一个无私的故事,牺牲,献身崇高事业。

更新句子:大概是我有史以来最亲爱的电影,一个无私、出家献身崇高事业的故事。

2)随机插入(RI):

随机插入是一种类似的同义词替换技术,但在这种情况下,随机选取的 n 个单词的同义词被插入到随机位置,而不删除原始单词。

对于下面提到的示例句子,我们随机选取 n=1 个单词(喜剧),并在随机位置插入其同义词。

大概是我一直以来最喜欢的电影,一个关于无私、牺牲和献身于崇高事业的故事。

更新句子:大概是我有史以来最喜欢的电影,一个无私、牺牲的故事,和编年史献身崇高事业的故事。

3)随机互换(RS):

随机交换技术从句子中随机选择任意两个单词并交换它们的位置。这种技术可以对 n 对单词执行 n 次。

对于下面提到的示例句子,我们随机选取 n=1 对单词(the,roads)并在随机位置插入其同义词。

大概是我有史以来最喜欢的电影,一个关于无私、牺牲和献身于崇高事业的故事。

更新句子:可能是我有史以来最喜欢的电影,一部无私、牺牲和献身于事业的高尚的故事。

4)随机删除(RD):

随机删除技术以概率‘p’随机删除句子中的每个单词。

大概是我有史以来最喜欢的电影,一部关于无私、牺牲和献身于崇高事业的故事。

更新句子:可能是我有史以来最喜欢的电影,一部关于无私、牺牲和对崇高事业的奉献的电影。

本文讨论了上述四种文本增强技术。作者没有提到他们为什么选择这些增强规则,以及他们还尝试了哪些没有成功的规则。

基准测试:

现在,他们使用增强技术来测试 5 个文本分类任务的性能变化:

  • SST-2:斯坦福情感树库
  • CR:客户评论
  • 主题:主观性/客观性数据集
  • TREC:问题类型数据集
  • PC:赞成-反对数据集

(图片由作者提供,参考文献),在不同的训练集规模上,具有和不具有 EDA 的模型的五个文本分类任务的平均性能(%)

从上述基准测试数据中,我们可以得出结论,完整数据集的平均性能提高了 0.8%,拥有 500 个随机样本的数据集的平均性能提高了 3.0%。

结论:

在本文中,我们讨论了 4 种文本数据扩充技术,它们提高了在小样本数据集上执行的文本分类任务的性能。我们观察到,与大数据相比,较小样本量(约 500)的性能提高了约 3%。这很好地描述了数据扩充技术对于小数据集非常有效。

参考资料:

[1]Jason Wei 和的论文《提高文本分类任务性能的简单数据扩充技术:的论文

感谢您的阅读

借助 DuckDB 和 Iceberg API 提升您的云数据应用

原文:https://towardsdatascience.com/boost-your-cloud-data-applications-with-duckdb-and-iceberg-api-67677666fbd3

使用 Iceberg API 和 DuckDB 优化云存储中大量 Iceberg 表的分析查询

休伯特·纽菲尔德在 Unsplash 上拍摄的照片

1.介绍

Apache Iceberg 最为人所知的是,它使 Spark、Dremio 和 Trino 等流行的查询引擎能够可靠地查询和操作存储在数据湖中的巨大表中的记录,并在确保安全的并发读写的同时大规模地这样做。因此,它解决了现代数据湖平台的一些主要问题,如数据完整性、性能、维护和成本。

Iceberg 之所以神奇,很大程度上是因为它有效地组织了表的元数据,并跟踪其数据文件、统计数据和版本历史。这也是 Iceberg 表上的查询速度更快、数据扫描更少的原因。对不使用或保存文件级元数据(例如 Hive)的表进行查询通常会涉及代价高昂的列表和扫描操作,只是为了找出查询需要运行的相关数据文件。相比之下,当我们为了满足查询而需要的数据位于一组特定的文件中时,Iceberg 原则上可以立即告诉我们这一点,甚至不需要扫描一个数据文件。

Iceberg 是一种表格格式,但它也是一个库,公开了一个强大的 API 和一组实现其主要功能的过程。Iceberg 的 API 使查询引擎能够利用其强大的元数据结构,并运行扫描较少文件和避免昂贵的列表操作的查询。

然而,并不是所有的用例都需要或证明查询引擎的重要性,比如 Spark 或 Trino,它们可能并不总是可用的。幸运的是,我们不必使用庞大的计算引擎来利用 Iceberg 的 API 所提供的功能。任何可以处理 parquet 文件的应用程序都可以使用 Iceberg 表及其 API,以便更有效地进行查询。这篇简短的实践文章的目的是演示如何操作。

DuckDB 是一个流行的进程内数据库,在读取和运行对 parquet 文件的 SQL 查询方面表现出色。因此,它为云数据应用程序提供了一个重要的功能,可以使它们更加健壮,减少对庞大查询引擎的依赖。然而,它处理大量数据的能力是有限的,主要是受运行它的机器或进程的内存大小的限制。这正是冰山 API 发挥作用的地方。

这篇文章将如下进行:第 2 节展示并解释了相关的用例,以及 Iceberg API + DuckDB 如何有效地解决这个问题。接下来,第 3 节演示如何使用 Iceberg API,第 4 节将 DuckDB 添加到图片中。第 5 节解释了两者如何一起发挥作用并解决上述问题。第 6 节总结。

(您可以在下面找到源代码的链接。我在代码示例中使用了 Scala,尽管它依赖于 Iceberg 的 Java API,并且很容易移植)

2.简而言之,这个问题(和解决办法)

我们有一个巨大的冰山表,按日期划分,包含来自我们客户的活动数据。每个事件由一个名为 account_id 的字段标识,包含一组计数器和一个 event_type 字段。因为大多数数据消费者通常将 account_id 添加到他们的 WHERE 子句中,所以我们决定按照 account_id 对每个分区中的文件进行排序(这是这里很重要的一点。我稍后会详细解释)。

要求是创建一个服务,告诉我们一个给定的客户在特定的一天发生了多少事件。该服务将接收某个日期作为参数,以及一个帐户 id,并将返回一个按 event_type 的聚合(以 JSON 格式)。简而言之,服务需要运行类似下面的查询:

SELECT date, account_id, event_type, count(*) as events
FROM customer_events
WHERE date = date '2022-01-02' AND account_id = '2500'
GROUP BY 1,2,3

听起来够简单吧?嗯,不一定。这项服务将不得不每天回答来自 10K 的大约 100 名客户的几十个请求。因此,按需运行或仅在需要特定客户的数据时运行是有意义的。这种用例似乎无法证明旋转 Spark 或 Trino 集群的合理性,但它必须扫描的数据似乎相当庞大。即使我们知道所需的分区,列出和扫描分区也可能是内存和计算密集型的。

这就是冰山 API 大放异彩的地方。如果表按日期分区,文件按 customer_id 排序(如下图所示),那么表的元数据将显示,上面的查询实际上只需要扫描一个数据文件,而不是扫描整个分区及其文件——数据文件#2 ( WHERE date=2022-01-02 AND account_id=2500).。实际上,对 Iceberg 表运行这个查询将导致只扫描这个文件,这就是 Iceberg API 将告诉我们的。

假设每个数据文件重约 250MB,那么单个文件的查询和扫描操作似乎更适合数据应用程序处理。

至于我们将在拼花文件上运行这些查询的方式,我已经提到过,我们将使用越来越流行的 DuckDB,因为它能够高效地在位于 S3 的拼花文件上运行 SQL 查询,此外它还易于嵌入到数据应用程序中。

现在我们可以结束我们的服务流程了(见下图):服务将通过一个 date 变量和一个 account_id 被调用。接下来,我们调用 Iceberg API 来查询与我们的查询相关的数据文件的表元数据。最后,在获得文件列表后,我们创建并执行一个 DuckDB 查询语句,该语句也将结果集格式化为 JSON 格式(一个简洁的 DuckDB 特性)并将它们返回给调用者。

3.使用 Iceberg API 过滤

如果你需要对 Iceberg API 有一个很好的介绍,那么请随意查看帖子底部的一些链接,尽管我相信代码足够简单,即使你对 Iceberg 知之甚少。

我们做的第一件事是创建一个过滤表达式对象,Iceberg 将使用该对象来查询包含与我们的过滤器匹配的数据的文件的表元数据。你可以链接表达式,也可以像我下面做的那样组合它们。

 val partitionPredicate = Expressions.and(
      Expressions.equal("date", "2022-11-05"),
      Expressions.equal("accountID", "0012345000"))

接下来,我们创建一个 Catalog 对象,这是 Iceberg 的 API 的入口点。我们使用 AWS Glue 作为我们的目录,但是您也可以使用 JDBC 和 Hadoop 目录。(注意,要实现这一点,在您的 env 或 path 中需要 AWS 凭证)。下面代码块中的第二个函数简单地返回一个 Iceberg 表对象,该对象将用于调用所需的操作,最后一个函数使用我们上面定义的表达式对象的可选变量执行一个表扫描过程。基于 Iceberg 的元数据,最后一个函数将告诉我们哪些文件与查询相关。

 private def getGlueCatalog(): Try[GlueCatalog] = Try{
  val catalog =  new GlueCatalog
  val props = Map(
    CatalogProperties.CATALOG_IMPL -> classOf[GlueCatalog].getName,
    CatalogProperties.WAREHOUSE_LOCATION -> "s3://Doesnt_Matter_In_This_Context",
    CatalogProperties.FILE_IO_IMPL -> classOf[S3FileIO].getName
  ).asJava
  catalog.initialize("myCatalog", props)
  catalog
}

private def getIcebergTableByName(namespace: String, tableName: String, catalog: GlueCatalog): Try[Table] = 
Try{
    val tableID = TableIdentifier.of(namespace, tableName)
    catalog.loadTable(tableID)
  }

private def scanTableWithPartitionPredicate(table:Table, partitionPredicate:Expression):Try[TableScan] =
  Try(table.newScan.filter(partitionPredicate))

在我们创建了 Iceberg 目录,加载了我们的,并使用过滤表达式执行了新的扫描之后,Iceberg 返回了一个表扫描对象。 TableScan 对象将有望包含符合我们过滤表达式的计划数据文件列表。下面的函数只是从表扫描中提取文件名,并将它们链接成一个长字符串,这样我们就可以使用 DuckDB 来查询它们。

 private def getDataFilesLocations(tableScan:TableScan): Try[String] = Try {
    // chain all files to scan in a single string => "'file1', 'file2'"
    tableScan.planFiles().asScala
      .map(f => "'" + f.file.path.toString + "'")
      .mkString(",")
  }

这总结了我们的服务对 Iceberg API 的使用。我们从一个目录开始,使用一个表达式过滤器启动一个表扫描,该表扫描使用我们的表的元数据来确定与我们的查询相关的文件。

4.使用 DuckDB 查询

DuckDB 是一个越来越受欢迎的进程内 OLAP 数据库,擅长在各种数据源上运行聚合查询。DuckDB 与类似产品(如 SQLite)的不同之处在于它为 OLAP 查询提供的性能,以及它提供的灵活性。简而言之,它本质上是一个进程内的小 DWH,使我们能够在相对较大的数据集上运行聚集密集型查询。

然而,有些数据集对于我们现有的或我们想要使用的机器或设备来说太大了。这正是 DuckDB 和 Iceberg 的结合非常强大的原因——有了 Iceberg API,我们可以在更少的时间内对更少的数据进行查询。

DuckDB 允许使用关键字parquet_scan()查询 parquet 文件,该关键字可用于如下查询:

SELECT date, account_id, event_type, count(*) as events
FROM parquet_scan([ <FILES_LIST>])
WHERE date = date '2022-01-02' AND account_id = '2500'
GROUP BY 1,2,3

因此,在使用 Iceberg 的 API 获得数据文件列表后,我们可以简单地用从 Iceberg 收到的文件列表替换上面查询中的字符串“<files_list>”,并对过滤后的文件执行查询。</files_list>

如下面的代码块所示,我们首先在内存中初始化 DuckDB。因为我们想在 S3 处理表,所以我们首先需要安装并加载 httpfs 模块(以及默认情况下应该已经加载的 parquet 模块)。模块通过执行语句加载到 DuckDB 中,这些语句也包括变量 setters,我们在这里使用它们来设置 AWS 凭证。下面代码中的第二个函数只是在查询中注入文件列表,正如我们上面看到的,最后一个函数执行语句。

 private def initDuckDBConnection: Try[Connection] = Try {
    val con = DriverManager.getConnection("jdbc:duckdb:")
    val init_statement =
      s"""
         |INSTALL httpfs;
         |LOAD httpfs;
         |SET s3_region='eu-west-1';
         |SET s3_access_key_id='${sys.env.get("AWS_ACCESS_KEY_ID").get}';
         |SET s3_secret_access_key='${sys.env.get("AWS_SECRET_ACCESS_KEY").get}';
         |SET s3_session_token='${sys.env.get("AWS_SESSION_TOKEN").get}';
         |""".stripMargin
    con.createStatement().execute(init_statement)
    con
  }

private def formatQuery(query:String, dataFilesStr:String):Try[String]  = Try {
    query.replaceAll("<FILES_LIST>", dataFilesStr)
  }

  private def executQuery(connection: Connection, query:String):Try[ResultSet] = Try{
     connection.createStatement.executeQuery(query)
  } 

这解决了我们在查询端的核心服务。在我们获得列表文件之后,我们只需要确保 DuckDB 被正确初始化,格式化查询并执行它。

5.把所有的放在一起

如前所述,我们的服务的处理逻辑包括 2 个主要阶段,在下面的代码块中详细描述了 7 个步骤(Scala 的理解实际上使这变得非常简单)。

 val queryStatement = """
     |SELECT row_to_json(resultsDF)
     |FROM (
     |   SELECT date, account_id, event_type, count(*) as events
     |   FROM parquet_scan([ <FILES_LIST>])
     |   WHERE acc_id = '2500' AND date = '2022-01-01'
     |   GROUP BY 1,2,3
     |) resultsDF
     |""".stripMargin

val filterExpr = Expressions.and(
      Expressions.equal("date", "2022-11-05"),
      Expressions.equal("accountID", "0012345000"))

val jsonDataRows = for {
  catalog         <- getGlueCatalog
  table           <- getIcebergTableByName("db_name", "table_name", catalog)
  tableScan       <- scanTableWithPartitionPredicate(table, filterExpr)
  dataFilesString <- getDataFilesLocations(tableScan)
  queryStatement  <- formatQuery(query, dataFilesString)
  dbConnection    <- initDuckDBConnection
  resultSet       <- executQuery(dbConnection, queryStatement)
} yield resultSet.toStringList

Iceberg API 首先用于获取表引用并执行表扫描,获取包含我们感兴趣的日期以及 customer_id 的数据文件的名称和位置。一旦我们得到过滤后的文件列表和它们在 S3 的位置,我们就把它们链接成一个长字符串,并注入到查询中。最后,使用 DuckDB 仅对相关文件执行查询,并获得结果。

如您所见,上面的查询模板被包装在一个row_to_json函数中,该函数将结果集转换成一个 JSON 文档,这正是我们想要实现的。

6.结论

Apache Iceberg 正迅速成为元数据管理和组织海量数据湖表的标准。因此,毫不奇怪,流行的查询引擎,如 Impala、Spark 和 Trino,以及公共云提供商很快宣布支持 Iceberg table 格式和 API。

这篇文章的目的是展示一种强大的方法,数据应用程序可以独立于强大的查询引擎利用 Iceberg 表提供的好处。它展示了我们如何将 Iceberg 的 API 与 DuckDB 结合使用,以创建轻量级但功能强大的数据应用程序,这些应用程序可以在大规模表上运行高效的查询。我们看到,通过使用 Iceberg 公开的 API,我们可以创建一个“优化的”查询,只扫描与查询相关的文件。DuckDB 可以轻松地对存储在云存储中的 parquet 文件进行查询,这使得两者的结合非常强大,并突出了 Iceberg 表格式及其特性的潜力。

完整的源代码可以在这里找到

希望这个有用!

  • 资源

一个非常好的冰山介绍可以在这里这里找到

更多关于 Java API 的信息可以在这里找到

** 所有图片,除非特别注明,均为作者所有

借助云中基于容器的开发,提高您的 ML 团队的生产力

原文:https://towardsdatascience.com/boost-your-ml-teams-productivity-with-container-based-development-in-the-cloud-56aa35552776

在 SageMaker 上运行容器中 VS 代码的简单方法

今年早些时候,我在 SageMaker 上发布了两个托管代码服务器的指南(这里,和这里)。它展示了如何在可扩展的云计算上运行 VS 代码,以及从任何地方运行代码,而不用担心本地设置。你所需要的只是一个互联网连接。

由作者创建的图像:使用稳定扩散创建

在这篇文章中,我们将更进一步,展示如何在云中进行基于容器的开发,以加速您的 ML 项目交付。

我们将从 Dockerhub 中提取一个示例 python 3.10 容器映像,并在 SageMaker 上使用 VS 代码进行开发。您将需要访问一个 SageMaker 笔记本实例来浏览这个例子。一小杯应该够了。

为什么云中基于容器的开发有助于简化您的 ML 项目?

使用基于容器的开发,您可以在容器中编写、执行和测试 ML 代码。这意味着您可以像您的团队成员一样使用相同的库和依赖项,通过一致且高度可重复的开发环境使协作变得更加容易。

另一个优势是,您可以在一个与您的生产环境行为完全相同的环境中开发和测试您的代码,这有助于在您的项目中更快地从 0 移动到 1。

照片由 Desola Lanre-OlogunUnsplash 上拍摄

在 SageMaker 上托管代码服务器对 VS 代码的本地开发体验做了最小的改动,允许你在任何地方,在可扩展的云计算上编码。

“但我认为 VS 代码是开源的”

通常对于 VS 代码,你会使用Dev Containers 扩展来进行基于容器的开发。但是,您不能将它用于 code-server,也不能用于任何其他开源 VS 代码项目。

照片由Cookie PomUnsplash 拍摄

虽然 VS 代码的核心是开源的,但是微软发布的 marketplace 和许多扩展却不是。微软禁止使用任何非微软 VS 代码访问他们的市场。更多详情,参见与 VS 代码的差异。

那么我们如何在 SageMaker 上用 VS 代码进行基于容器的开发呢?

VS 容器中的代码,托管在 SageMaker 上

因此,由于微软扩展许可问题,我们不能将容器带到 IDE 中。但是如果我们把 IDE 放到容器中呢?事实上,code-server 是基于 web 的,可以真正在任何地方运行。设置非常简单,只需 3 个步骤:

步骤 1:将代码服务器添加到您现有的 docker 文件中

您可以用一行代码将 code-server 添加到容器中。下面是一个将它添加到 Dockerhub 的 python 3.10 图像的例子

作者图片:我在我的笔记本实例中使用一个现有的 Dockerfile 文件,使用 python:3.10

加一行就是了!

步骤 2:在笔记本实例上构建并启动容器映像

通过笔记本实例的 Jupyter/Jupyterlab 启动终端:

图片作者:如果你用 Jupyter,按钮应该在右上角

现在,您可以导航到 Dockerfile 文件夹,并使用以下命令在本地构建容器映像:

# BUILD THE CONTAINER IMAGE LOCALLY
docker build -t ide-in-container:latest .

一旦构建了映像,您就可以使用命令运行容器来启动 code-server:

# RUN CONTAINER WITH VS CODE IN IT
docker run --rm -it -p 8080:8080 ide-in-container:latest code-server --auth none --disable-telemetry --bind-addr 0.0.0.0:8080

我们通过默认端口8080向实例公开代码服务器。

Image by author:运行 docker 命令后,您应该会看到以下内容

步骤 3:通过笔记本实例 Jupyter 代理访问 VS 代码

现在,您需要做的就是复制您的笔记本实例 URL,对其稍作更改,然后将其粘贴到一个新的选项卡中:

作者图片:左边是我的实例原始 URL。我用/proxy/8080/替换了/tree,并将其粘贴到一个新的浏览器标签中。

选项卡现在应该打开 VS 代码:

作者图片:您可以安装 VS 代码的 Python 扩展,并开始在您的 ML 项目中工作!

注意事项

为了进一步简化您的工作,您可以挂载本地笔记本实例卷,并将 VS 代码指向您现有的配置(扩展、按键绑定、设置)。在示例 Dockerfile 中,我将 VS 代码指向实例中已经有的/home/SageMaker/vscode-config config 文件夹。这是使用XDG_DATA_HOME环境变量完成的。

下面是将笔记本实例卷装载到容器的方法:

# RUN THIS COMMAND IF YOU WANT TO MOUNT THE NOTEBOOK INSTANCE HOME/SAGEMAKER FOLDER
docker run --rm -it -p 8080:8080 -v /home/ec2-user/SageMaker/:/home/SageMaker ide-in-container:latest code-server --auth none --disable-telemetry --bind-addr 0.0.0.0:8080

为了改善您的协作,您可以在您的项目团队 GitHub repo 上添加这个“环境 Dockerfile”。这样,它将被版本化,每个人都可以在任何时候使用完全相同的依赖项。

结论

使用基于云的 ide 在容器内部进行开发可以帮助简化您的 ML 项目和团队协作,因为它使在一致的环境中运行和测试代码变得容易,并且允许您从任何地方访问该开发环境。

在这篇文章中,我和你分享了一个在 Amazon SageMaker 上运行 VS 代码的简单方法。

更进一步,您可以访问使用 GitHub Actions、MLflow 和 SageMaker Pipelines 的 MLOps 的 5 个简单步骤,了解您的团队如何使用 GitHub Actions 和 SageMaker 轻松运行端到端 MLOps 项目。

将梯度增强模型与 Prophet 特征相结合,增强时间序列预测

原文:https://towardsdatascience.com/boost-your-time-series-forecasts-combining-gradient-boosting-models-with-prophet-features-8e738234ffd

使用具有 Prophet 功能的 LightGBM 模型实现最先进的结果

艾萨克·史密斯在 Unsplash 上拍摄的照片

在我的上一篇帖子中,我写了关于机器学习的时间序列预测。我重点解释了如何使用基于机器学习的方法进行时间序列预测,并取得良好的结果。这篇文章将使用更先进的技术来预测时间序列,并试图超越我在上一篇文章中创建的模型。如果你没有读过我之前的文章,我建议你读一读,因为这将是我之前模型的一个迭代。一如既往,你会在这个链接中找到这个新帖子的代码。

以前的帖子

新方法

除了前一篇文章中的所有特征工程,我们将使用 Prophet 提取新的有意义的特征,如季节性、置信区间、趋势等。

特征工程

特征工程图

时间数列预测法

在我的上一篇文章中,我使用了一个具有一些滞后功能的 LightGBM 模型来预测未来 7 天某个城市的每小时自行车共享需求,效果非常好。然而,如果我们使用 Prophet 从我们的时间序列中提取新的特征,结果可以得到改善。其中一些特征是 Prophet 模型的实际预测、置信区间的上限和下限、每日和每周的季节性和趋势。对于其他类型的问题,Prophet 也可以帮助我们提取描述节日效应的特征。

原始数据

在本帖中,我们将重复使用之前 LightGBM 预测文章中使用的数据。我们的数据看起来像这样:

前一篇文章的丰富数据集

用 Prophet 提取特征

我们特征工程的第一步非常简单。我们只需要像这样使用先知模型进行预测:

该函数将为我们返回一个数据框架,其中包含许多 LightGBM 模型的新特性:

Prophet 显示了从第 1 列到第 11 列的数据帧

Prophet 显示了从第 11 列到第 20 列的数据帧

具有 Prophet 功能的列车自动制动灯 GBM

一旦我们使用 Prophet 提取了新的特征,就该将我们的第一个数据框架与新的 Prophet 数据框架合并,并使用 LightGBM 进行一些滞后的预测。为此,我实现了一个非常简单的函数来处理所有的过程:

一旦我们执行了上面的代码,我们将合并特征数据帧,创建滞后值,创建并训练 LightGBM 模型,使用我们训练的模型进行预测,并显示一个将我们的预测与实际情况进行比较的图。输出将如下所示:

具有滞后和先知特征的模型预测

哇!如果我们仔细观察结果,我们可以将之前帖子中的最佳模型提高 14%。在我们之前的帖子中,具有最佳功能工程的最佳 LightGBM 模型获得了 32.736 的 MAE,现在我们获得了 28.665 的 MAE。考虑到前一篇文章中的模型和特性工程已经切中要害,这是一个巨大的进步。

结论

正如我们在这篇文章中看到的,将监督机器学习方法与 Prophet 等统计方法结合起来,可以帮助我们取得非常令人印象深刻的结果。根据我在现实世界项目中的经验,在需求预测问题中很难得到比这些更好的结果。

参考

https://facebook.github.io/prophet/docs/quick_start.html

借助 Nixtla 的 StatsForecast 提升预测性能

原文:https://towardsdatascience.com/boosting-forecast-performance-with-nixtlas-statsforecast-2282fae91850

提高百里香的效率

Marc-Olivier Jodoin 在 Unsplash 上拍摄的照片

百里香增强框架,在其核心,只是一个简单的梯度增强算法包装在标准的时间序列方法。这意味着框架严重依赖底层方法的效率和速度。正如我们将在本文中看到的,增强和附加逻辑增加了准确性,但也增加了计算量。这种繁重的工作大部分以前是通过 ETS 和 ARIMA 等统计模型实现的,但利用 Nixtla 的统计预测包: StatsForecast,可以提高速度和准确性。使 ThymeBoost 和 StatsForecast 成为时间序列预测的完美结合。

这篇文章的好 TLDR 是:

stats forecast比 stats models快,ThymeBoost 带来精度增益。

Github for 百里香增强

介绍

首先,如果你没有听说过百里香,那么我鼓励你看看我之前的文章给出了一个不错的概述。在最新版本中,我添加了 StatsForecast 作为可选的依赖项。为了运行这些示例,您需要安装它:

pip install StatsForecast

为了安全起见,请继续更新百里香增强:

pip install ThymeBoost --upgrade

既然我们已经解决了这个问题,那么本文的主要内容将是在每周 M4 数据集上进行基准测试,以了解所有这些模型在准确性和速度方面的表现。这些数据集都是开源的,并且在 M-competitions github 上直播。它被标准训练和测试分割,因此我们将使用训练 csv 进行拟合,而测试 csv 仅用于使用 SMAPE 进行评估。

请随意用其他数据集测试,并让我知道他们的表现如何!

这样做的主要目的是回顾新方法在 boosting 框架中的作用,并最终了解将它们添加到 ThymeBoost 框架中如何提高准确性。

对方法进行基准测试

首先,我们将尝试一下 ThymeBoost 中计算量最大的方法:AutoArima。以前是用 PmdArima 完成的,现在我们可以用 StatsForecast 进行测试,只需在与 ThymeBoost 配合时通过trend_estimator=‘fast_arima’即可。让我们来看看一些代码,在这些代码中,我们首先构建数据集,然后我们可以运行百里香增强:

from tqdm import tqdm
from statsforecast.models import ETS, AutoARIMA
from ThymeBoost import ThymeBoost as tb
tqdm.pandas()
train_df = pd.read_csv(r'm4-weekly-train.csv')
test_df = pd.read_csv(r'm4-weekly-test.csv')
forecast_horizon = len(test_df.columns) - 1
train_df = train_df.rename({'V1': 'ID'}, axis=1)
train_long = pd.wide_to_long(train_df, ['V'], 'ID', 'Date')
test_df = test_df.rename({'V1': 'ID'}, axis=1)
test_df = pd.wide_to_long(test_df, ['V'], 'ID', 'Date')
train_long = train_long.dropna()
train_df = train_long.reset_index()
train_df.index = train_df['ID']
train_df = train_df.drop('ID', axis = 1)
X = train_long
X = X.reset_index()
  • 注意:这段代码在数据操作方面可能效率很低,我确信有更好的方法来做这件事,这只是我为基准测试做的一些工作。时间不包括运行这段代码的时间。

不管怎样,现在我们有了要拟合的训练数据,让我们来看看拟合函数:

def grouped_forecast(df):
    y = df['V'].values
    boosted_model = tb.ThymeBoost(verbose=0)
    output = boosted_model.fit(y,
                               seasonal_period=None,
                               trend_estimator=['fast_arima'])
    predicted_output = boosted_model.predict(output,
                                              forecast_horizon,
                                              trend_penalty=True)
    predictions = predicted_output['predictions']
    return predictions

在这里,我们只是创建一个函数,当我们执行 groupby 并应用时,该函数将被传递:

def counter(df):
    df['counter'] = np.arange(2, len(df) + 2)
    return df
predictions = X.groupby('ID').progress_apply(grouped_forecast)
predictions = predictions.reset_index()
predictions = predictions.groupby('ID').apply(counter)
test_df = test_df.reset_index()
benchmark_df = predictions.merge(test_df, left_on=['ID', 'counter'],
                                 right_on=['ID', 'Date'])def smape(A, F):
    return 100/len(A) * np.sum(2 * np.abs(F - A) / (np.abs(A) + np.abs(F)))
tqdm.pandas()
def grouped_smape(df):
    return smape(df['V'], df['predictions'])
test = benchmark_df.groupby('ID').progress_apply(grouped_smape)
print(np.mean(test))

然后我们得到给定输出的平均 SMAPE,这里的一切都应该是好的,但如果有任何错误会混淆基准,请让我知道。

运行这个程序会得到平均 SMAPE 值 8.61 ,大概需要 10 分钟。

接下来,让我们单独运行 Nixtla 的 Auto Arima,看看它的表现如何。

我们将把 groupby 预测函数改为:

def grouped_forecast(df):
    y = df['V'].values
    ar_model = AutoARIMA().fit(y)
    predictions = pd.DataFrame(ar_model.predict(forecast_horizon)['mean'],
                                columns=['predictions'])
    return predictions

重新运行上面的 SMAPE 计算块将得到一个 8.93 的 SMAPE 和大约 4 分钟的时间。

好的,很好,我们已经通过提高 Auto-Arima 过程展示了一些精度增益。这应该不足为奇,因为我在一篇文章《深度潜水梯度提升 Arima 中展示了非常相似的结果。但是我想提醒一下,boosting 并不是万能的,也不总是比 Arima 好,但是它仍然是一个有趣的观察。

下一步应该很明显。我们已经看了“快速”自动 Arimain 百里香增压以及 StatsForecast 的无增压自动 Arimain。接下来,我们应该看看如何使用 PmdArima 的 Auto-Arimain 百里香增强。

如果您一直在运行这段代码,请系好安全带。

下一步需要一些时间…

def grouped_forecast(df):
    y = df['V'].values
    boosted_model = tb.ThymeBoost(verbose=0, n_rounds=None)
    output = boosted_model.fit(y,
                               seasonal_period=None,
                               trend_estimator=['arima'],
                               arima_order='auto')
    predicted_output = boosted_model.predict(output,
                                              forecast_horizon,
                                              trend_penalty=True)
    predictions = predicted_output['predictions']
    return predictions

结果呢?

一个 SMAPE 的 8.78 ,但是用了 90 分钟。看起来提升 Pmd Arima 优于 Nixtla 的 StatsForecast 开箱即用,但它需要相当长的时间。

Arima 不是 StatsForecast 中的所有产品,另一个实现是 ETS 方法。有了这些新方法,我们实际上可以在 ThymeBoost 的autofit方法中利用这些更快的实现。为此,我们只需要在调用 autofit 时传递fast=True。新的预测函数将如下所示:

def grouped_forecast(df):
    y = df['V'].values
    boosted_model = tb.ThymeBoost(verbose=0, n_rounds=None)
    output = boosted_model.autofit(y,
                                    seasonal_period=[52],
                                    optimization_type='grid_search',
                                    optimization_strategy='holdout',
                                    lag=26,
                                    optimization_metric='smape',
                                    verbose=False,
                                    fast=False
                                ) predicted_output = boosted_model.predict(output,
                                              forecast_horizon,
                                              trend_penalty=True)
    predictions = predicted_output['predictions']
    return predictions

这导致 SMAPE 为 7.88,并且需要大约 80 分钟。绝对是所有测试中最好的即插即用精度,但我们选择型号有点作弊。

需要注意的一点是,将季节长度 52 传递给 StatsForecast 的方法并不是一个好主意。对于 ETS it 错误和 Auto-Arima 来说,时间太长了。这是一个利用百里香实际上如何工作的领域提高的速度,因为在 ARIMA 设置中长的季节周期需要明显更长的时间。

我们测试了其他几种方法,您可以在下面查看基准测试结果:

作者图片

就首字母缩略词而言:

  1. 结核病:百里香增强
  2. SF:统计预测
  3. NS:非季节性
  4. 倍增:倍增的季节性
  5. 快速:百里香利用统计预测引擎盖下

在高层次上,性能最好的是来自百里香增强的快速自动拟合方法。出于某些奇怪的原因,用季节性和快速 Arima 拟合百里香增强并没有表现得太好,事实上它比使用 PmdArima 的 Auto-Arima 要差得多。另一个观察结果是,从 StatsForecast 提升普通 ETS 方法可能会损害非提升方法正常拟合的准确性。如果我们改变拟合函数中的global_cost参数,这种情况可能会改变,因为默认情况可能并不总是最佳的。

结论

最新版本的 ThymeBoost 增加了一些引入 StatsForecast 方法的功能。与之前的实现相比,我们可以看到速度的提高和潜在的准确性。

百里香和一个好蛋糕一样,需要有好的面糊作为底料。StatsForecast 可能是优于 StatsModels。梯度推进只是顶部的点缀。

如果你喜欢这篇文章,你可以看看我写的其他一些与时间序列相关的帖子:

借助 GPU 加速库提升机器学习工作流

原文:https://towardsdatascience.com/boosting-machine-learning-workflows-with-gpu-accelerated-libraries-1f95feef68d4

专题图片(图片来源:https://unsplash.com/photos/A1blvxJxGU0

借助 GPU 加速库提升机器学习工作流

在 Pagerank 上测试 RAPIDS 套件以获得推荐

摘要:在本文中,我们演示了如何使用 RAPIDS 库来改进基于机器学习 CPU 的库,如 pandas、sklearn 和 NetworkX。我们使用一个推荐研究案例,在运行 PageRank 算法时,它在基于 GPU 的库中的执行速度提高了 44 倍,在个性化 PageRank 中的执行速度提高了 39 倍。

急流简介

Scikit-learn 和 Pandas 是大多数数据科学家工具箱的一部分,因为它们友好的 API 和广泛的有用资源——从模型实现到数据转换方法。然而,许多这样的库仍然依赖于 CPU 处理,并且,就这个线程而言,像 Scikit-learn 这样的库并不打算扩展到 GPU 处理或者扩展到集群处理。

编程语言的层次(图片由作者提供)。

为了克服这个缺点, RAPIDS 提供了一套 Python 开源库,采用这些广泛使用的数据科学解决方案,并通过包含 GPU 加速的实现来提升它们,同时仍然提供类似的 API。因此,你可以继续呆在你的 Python 舒适区,使用他们的高级编程接口,而不是一路学习 C/C++或 CUDA 这样的低级语言。

下面是一些属于 RAPIDS 的库和它们基于 CPU 的对应物(可用 API 的完整列表在这里提供):

  • 熊猫
  • cuML (Sklearn)
  • cuGraph(网络 x)
  • 客户空间(地理信息系统)

所以这篇文章的目的是让你对一些 RAPIDS 包能做什么有一点了解,并对它们进行测试以检查它们有多快。所以让我们看看他们有什么!

为推荐引擎测试 RAPIDS

我们将通过构建一个推荐引擎来展示 RAPIDS 的功能,该引擎可以推荐类似于目标标题的电影——您可能已经在您最喜欢的流媒体服务中的某个地方看到了“谁观看了…也观看了… ”类型的推荐。为此,让我们使用 PageRank 算法!

简而言之,PageRank 算法通过在其结构中执行随机行走并计算给定节点被访问的次数来估计图中节点的重要性,并且我们使用该重要性分数来对推荐系统中的项目进行排名。

互联网上有很多理解 PageRank 算法的资料,但是对于本文来说,重要的是:其中涉及到大量的数学知识,主要是大量的矩阵运算。猜猜什么在解决矩阵运算时会派上用场?没错,pal:GPU

因此我们可以创建一个图表,其中:

  • 节点是电影
  • 边将电影与普通用户消费(观看电影 A 和 B 的用户)联系起来,其中用户数量可以是边的权重。

下面的图让我们对图表的表示有所了解:

按共有用户数量链接的电影图表。电影“614”和电影“50”共有 92 个用户(图片由作者提供)。

有了概念图的模型,让我们深入一些数据,看看一些行动正在发生!

cuDF

cuDF 是一种针对熊猫的 GPU 加速方法,它的开发是为了尽可能平滑地进行代码转换。我们可以从 MovieLens 数据集加载一些数据,如下例所示:

cuDF 和 pandas 方法读取 csv 文件的比较。

我们加载了两个数据集:一个包含用户-项目交互(使用 cudf 并存储在df中),另一个包含项目的元数据(使用 pandas 并存储在df_metadata中)。数据合并和聚合也可以使用熊猫传统的mergegroupby方法来完成:

代码:使用 cuDF 执行数据合并和聚合

对于那些熟悉 Pandas API 的人来说,你可以检查一下这些接口有多熟悉,这样从基于 CPU 的库到基于 GPU 的库的迁移会快得多!这些转换的结果创建了以下邻接表:

图形邻接表

邻接表指示节点item_id_from通过共有n_users而连接到节点item_id_to

现在我们已经存储了图形数据,让我们构建一些对象来表示它们!

cuGraph

就像 cuDF 与熊猫配对一样, cuGraph 是 NetworkX 的 GPU 加速版本,NetworkX 是一个基于 CPU 的库,具有大量从图形中提取信息的算法。

为了比较它们的实现,让我们为每个库创建一个 graph 对象,如下面的代码所示:

使用 cuGraph 和 NetworkX 创建图形实例的比较。

这里,G是我们的 cugraph。图形对象和Gnx是我们的 networkx。图形对象。注意,当创建Gnx时,我们从包含邻接表的 cuDF 数据帧df_items中调用.to_pandas()方法(第 9 行)。这是因为 NetworkX 接受 Pandas 的数据帧作为输入,cuDF 为我们提供了一些将数据对象从 GPU 移动到 CPU 内存的转换方法。

现在我们已经创建了图形实例,让我们通过使用它们的pagerank方法来比较算法实现!为了公平比较,我们将为这两种方法设置相同的参数:

为每个库实现执行多次 PageRank 算法。电脑设置:Nvidia GeForce GTX 1060 (6GB 内存),CPU 英特尔 7700 和 32 GB 内存。

通过执行算法 10 次(每次 10 个循环)并取其平均执行时间,我们得到以下性能图:

图:NetworkX 和 cuGraph 的执行时间(毫秒)性能—越低越好。

是啊是啊,我知道! cuGraph 的实施平均运行时间为 30 毫秒,而 NetworkX 的运行时间为 1350 毫秒,执行时间提高了 45 倍…但我们还没有完全实现!标准的 PageRank 算法确实估计了图中一个节点的重要性,但是记住我们正在尝试解决“谁看了…也看了… ”类型的推荐?如果我们使用到目前为止获得的 pagerank 分数,我们将对所有推荐列表有相同的分数!因此,我们需要更多地搅动事物,让推荐更加……个性化

个性化页面排名的测试平台

还记得我们之前对 PageRank 算法的(超级浓缩的)解释中,我们在图中执行随机行走吗?在随机漫步的开始,我们随机选择一个节点作为起点,让自然去做它的工作——但所有节点都有相等的概率成为起点。如果一些节点有更高的概率成为起点呢?如果我们时不时地回到那个节点,从头再来,又会怎么样呢?

这是个性化 PageRank 算法背后的主要思想,我们可以用它来计算与特定目标节点相关的 pagerank,在我们的例子中,这是一部 pivot 电影。因此,我们总是可以重新启动随机漫步机,并设置 100%的概率从我们选择的节点开始。

因此,我们可以对目录中的每部电影执行 PageRank 算法,但在整个算法执行过程中,将从该电影开始的概率设置为 100%,如下面的代码所示:

代码:个性化 PageRank:我们设定 100%的概率从一个目标 item_id 开始。

这种方法的问题是,我们必须为目录中的每个项目运行该算法,因此 pagerank 执行时间在这种情况下起着关键作用。为了测试这种影响,下面的代码为每个项目调用 pagerank,并多次执行该过程:

代码:使用 cuGraph 和 NetworkX 对每个项目(存储在 df_metadata 中)执行 PageRank。

我们可以看到对于 cuGraph 实施,循环遍历整个目录平均花费了 1 分钟 5 秒,而 NetworkX 的实施平均花费了 39 分钟 8 秒****——因此我们在执行时间上有了 39 倍的改进。

除了用 GPU 扩展你的工作流程,RAPIDS 还促进了与 Dask横向扩展到多台机器的集成。因此,如果您的数据集不适合您的 GPU 和内存,您可以使用多个 GPU 的能力来扩展您的内存可用性并克服硬件限制。如果你想了解更多,你可以查看这篇文章。

(次要)缺点

通过对广泛使用的 ML 算法提供 GPU 加速的改进,RAPIDS 似乎是数据科学家工具包的一个有前途的替代方案。但是,有几点值得注意。

如果您在 Google Cloud 或 Amazon Web Services 等平台上运行作业,让 GPU 实例运行您的算法对项目成本起着至关重要的作用——特别是如果您打算在 Dask 中使用多 GPU 进程。因为这个问题,有些人甚至不会想到设置这些实例,但是我想说这里最重要的事情是:分析权衡!也许让您的流程运行速度加快 40 倍会节省一些实例正常运行时间,这样额外的成本就不会那么大了。

另外,请记住,有些算法还没有在 RAPIDS 套件中实现,所以在某些情况下,您仍然需要依赖基于 CPU 的库。然而,我想说,弥合这些差距只是时间问题。事实上,根据他们的文档,他们力争每 6 周发布一次更新,所以希望随着时间的推移,我们会有越来越多的解决方案。

参考

如果您想了解他们的最新版本或用例,您可以关注 RAPIDS medium profile 或查看我在下面列出的一些参考资料:

引导数据科学计划

原文:https://towardsdatascience.com/bootstrapping-a-data-science-program-c90acab0a6ac

在高速增长的初创企业中构建有价值的数据科学产品的策略

注意:本文中提到的项目完全是虚构的,仅仅是为了说明所讨论的概念。

(图片由作者提供)

高增长创业公司的数据科学家面临的挑战

在高速增长的初创企业中实施数据科学极具挑战性,而且可能毫无意义。数据科学需要时间、资源和谨慎的流程,这在以销售为导向的冲刺和客户灭火的漩涡中很难遵循。数据科学家的思维模式和初创公司的速度之间的不匹配产生了犬儒主义和倦怠,所以我想强调我已经确定并用于在高速增长的初创公司中成功发展数据科学项目的一些模式。无论如何,这都不是一个通用的思维框架,但我认为这些原则会引起共鸣,如果你退后一步,看看你的初创公司和数据科学团队的需求之间的交集。

首先,让我们列举一下为什么在初创公司实施数据科学如此棘手:

  1. 成长型创业公司旨在快速出货和迭代。研究项目不太符合敏捷框架和时间表。
  2. 必要的数据并不总是可用的(当然也不干净)。你的工作很少,如果有的话,标签,这是一个自耕农的工作,只是争论和理解数据。使问题更加复杂的是,产品在发货时往往没有考虑到分析,缺少支持精细行为特征工程(例如事件流)的工具。
  3. 也许最有害的是非技术利益相关者/高管对数据科学家的期望。你可能会遇到过于乐观的人,他们认为你可以马上回答极其复杂的问题;有些人不明白为什么至少有三个部门的标题中有数据这个词。

这一连串的问题足以阻止任何数据科学家加入创业公司,其结果是,创业公司的数据科学家只是拥有不同头衔的数据工程师/分析师,或者被挤到角落里,带着他们的 Jupyter 笔记本和花哨的卡片,对业务没有什么价值。

通过高杠杆、原型 ML 数据产品获得立足点

数据科学家有多种方法既可以实践数据科学,又可以成为对业务有价值的贡献者。为了找到数据科学的立足点,您需要确定在短期内增加价值的高杠杆问题,并通过架构和问题框架为数据科学团队的长期目标做出贡献。

开始构建非机器学习“模型”,你可以将它们投入生产,就像它们是机器学习模型一样。这可能更多地属于分析工程领域,但从数据科学家的角度来看,区别变得越来越不明显。这些原始 ML 数据产品将具有一些有用的品质。

1.商业价值并不完全取决于模型的预测能力。在初创公司启动数据科学项目最困难的部分之一是不确定性。您的数据产品需要在与业务相关的时间尺度上(例如一个季度)交付价值,并且具有很高的成功概率。第一印象很重要。如果你对企业的第一个建议是花三个月时间开发一个可能在生产中不可行的 ML 模型,祝你在未来获得资源。也许你不知道你的模型会有多精确,但是想想构建更大的产品会产生什么样的外部性。您能否提高流程的速度,减少人为错误,提供一些关键组件的可见性和度量标准?所有这些都是有价值的,并且所有这些都可以通过可预测的、渐进的方式交付。

2.数据科学计划的长期价值如何?将机器学习投入生产需要的不仅仅是统计数据。您需要功能工程管道、任务编排和模型评估功能。同样,对于一家专注于执行的初创公司来说,构建所有这些基础设施来支持不确定的结果是不明智的。一个先有鸡还是先有蛋的动态出现了。但是,令人高兴的是,数据科学基础设施可以支持非机器学习产品,并实际上有助于提供上述价值。这种价值——那种被反复地、可预测地挖掘出来的价值——现在为基础设施“买单”,否则它可能会像一些听起来很花哨的玩具一样呈现给非技术利益相关者。

使它更加具体

这听起来可能都很抽象,以至于毫无用处。非机器学习模型为机器学习基础设施付费,同时作为数据产品提供价值?我到底在说什么?我试着用一个例子来说明。让我们为我们刚刚起步的数据科学路线图考虑两个候选问题。在这两种情况下,企业都希望您应用一些机器学习魔法来解决问题。

  1. 欺诈建模:目前,风险团队必须将客户数据集下载到 Excel 中,并对其进行筛选,以识别可能的欺诈。
  2. 改善搜索结果的推荐算法:现有的模型(一个简单的、基于 SQL 的趋势排序)并不是最理想的,而且人们担心它会导致客户流失。

让我们根据现有解决方案的问题来分析这些问题,以及我们可以从哪些方面进行改进。

在改进欺诈模型方面,我们有更多的自由度(因此也有更多的杠杆作用)(作者图片)

在现有的欺诈检测系统中,我们有一个手动的、容易出错的流程,我们对该流程几乎没有可见性,并且很难从中汇总任何高级指标。不需要实现任何机器学习模型(甚至不需要引入新的逻辑),我们就可以自动化这个过程,并提供有价值的见解。对于搜索模型来说,这一点也是正确的:你可以说今天的企业没有办法量化搜索的好坏。但是,在那里,改善搜索结果仍然需要比现在更复杂的建模,这需要更少的研发。

为了构建欺诈数据产品的 V1,我们可以从 Excel(或分析师的大脑)中提取逻辑,并将其包装在机器学习基础设施中,以提供有价值的数据产品。让我们从简单地将这些规则设计到数据仓库中所带来的好处数据血统和可观察性开始。

  • 可以用执行日期、git 散列等对逻辑进行版本控制,以便可以精确地跟踪错误和问题。
  • 分析师现在可以用 SQL 回答复杂的问题,而以前他们最好的办法是组合 Excel 文件或试图维护一个大规模的工作簿(当然,在版本控制之外)。想象一下,如果没有一个经过适当设计的分析表,试图确定某个指标突然飙升的根本原因会是什么样?

在第 1 个月和第 2 个月之间,哪些用户的指标 A 发生了变化?(作者撰写)

机器学习也没有垄断模型评估指标的好处。在 ML 基础设施中,逻辑现在可以被有效地回溯测试和监控。像固定精度召回这样的 ML 度量可以帮助理解哪些规则是有用的,哪些是无用的。即使标签稀疏(或不存在),像“随时间标记的用户基数的百分比”这样的指标结合行业专业知识,也能提供对用户行为的洞察。从公开的统计数据来看,我们标记的用户群是比我们预期的多还是少?

此外,大规模执行的 EDA 本身可以成为强大的数据产品。询问诸如“哪些用户属性与被分析师规则标记的概率相关?”以及“我们的风险集中在哪个用户群组?”并使用 Tableau 或 Looker 仪表盘来回答这些问题,使见解民主化。

懒散地走向机器学习

利用数据科学思维和工具集来构建非机器学习的数据产品,使企业能够以他们以前可能无法想象的方式使用数据。数据科学毕竟不是机器学习的代名词;而是提取有意义的见解。但这种心态并不意味着永久放弃机器学习。

事实上,采用这种方法更有可能为应用更高级的建模方法提供一个自然的过渡(从技术和业务的角度来看)。现在,您已经有了将机器学习投入生产所需的基础设施,以及这样做的理由。您可以向业务部门解释,这些提出的新问题和您提供的指标需要更复杂的技术来进一步开发。你已经驾驶你的规则引擎——用数据科学基础设施装饰——直到它发出噼啪声,现在你已经为自己赢得了一杯饮料和一个from sklearn import ...

引导基础

原文:https://towardsdatascience.com/bootstrapping-basics-254db6635a76

非参数重采样,已解释

r 库要求

  • 并行:用于并行计算
  • ggplot2:用于数据可视化

一.导言

当估计感兴趣的统计量时,我们不仅对该统计量的点值感兴趣。我们还想量化我们估计的不确定性。对于感兴趣的标准量,如平均值或标准差,我们有采样分布的分析结果,或者可以很容易地使用大样本近似。很多时候,我们感兴趣的是那些我们不知道抽样分布的量,而且即使有可能,推导抽样分布也很困难。在这些情况下,bootstrap 是量化不确定性的一个有价值的工具。

在进行 bootstrapping 时,我们将样本视为总体。我们用替换样本重复重新采样与原始样本相同数量的观察值,并计算这些样本的感兴趣的统计量。我们用替换重新取样,因为在没有替换的情况下取样时,样本是不独立的。它们也将与我们的原始样本完全匹配,这将不是很有帮助。这为我们提供了一个模拟的采样分布,我们可以用它进行分析。

二。履行

虽然有像 bootstrap 这样的库为我们做引导,但我发现编写轻量级函数是一个很好的学习方法,即使我在实践中使用维护良好的库。为了演示自举,我编写了两个助手函数来支持我的主自举函数,该函数利用并行化来加速计算。

处理每个引导样本不依赖于其他样本,因为这些样本彼此独立。这使得自举很容易并行化,这就是 bootstrap_parallel 所做的。

one_bootstrap <- function(my_vec, my_func){
 ###
 # This function retrieves one bootstrapped sample and returns the   
 # statistic of interest.
 #
 # Args
 # ----
 # my_vec : numeric vector
 #   A vector of numbers of which to compute the statistic of 
 #   interest.
 # my_func : function
 #   Function which computes the statistic of interest.
 #
 # Returns
 # -------
 # double
 #   The statistic of interest computed on the bootstrapped sample.
 ###
 bootstrapped_sample <- sample(my_vec, size=length(my_vec), 
                               replace=TRUE)
 return(my_func(bootstrapped_sample))
}bootstrap_replicate_par <- function(B, my_vec, my_func){
  ###
  # This function is a helper function for the parallized 
  # bootstrapping function. It takes in a vector whose length 
  # determines the number of bootstrap samples to take, a data 
  # vector, and a function. It utilized optimized looping.
  #
  # Args
  # ----
  # B : vector
  #   A vector whose length determines of bootstrapped samples to 
  #   return.
  # my_vec : numeric vector
  #   A vector of numbers of which to compute the statistic of 
  #   interest.
  # my_func : function
  #   Function which computes the statistic of interest.
  #
  # Returns
  # -------
  # estimates : vector
  #   A vector of the estimates.
  ###
  estimates <- replicate(length(B), one_bootstrap(my_vec, my_func))
  return(estimates)
}bootstrap_parallel <- function(my_vec, my_func, B){
  ###
  # This function takes in a data vector, function, and the number 
  # of bootstrap iterations and returns a list holding the mean and 
  # standard deviation of the bootstrap estimates, as well as the 
  # vector of the bootstrap estimates. It utilizes parallel 
  # computing.
  #
  # Args
  # ----
  # my_vec : numeric vector
  #   A vector of numbers of which to compute the statistic of 
  #   interest.
  # my_func : function
  #   Function which computes the statistic of interest.
  # B : int
  #   The number of bootstrapped samples to return.
  #
  # Returns
  # -------
  # output : list
  #   A list of the mean, and standard deviation of the estimates 
  #   and a vector of the estimates.
  ###

  # Count the cores and make a cluster from leaving one core free.
  cores <- detectCores()  
  cluster <- makeCluster(cores - 1) # Create a vector that will be split up and determine the number 
  # of bootstrap samples to get on each core.
  boot_vec <- 1:B

  # Export variables and functions to the cluster.
  clusterExport(
    cluster, 
    list("boot_vec", "one_bootstrap", "bootstrap_replicate_par", 
         "my_vec", "my_func"),
    envir=environment()
    )  estimates <- parSapply(
    cluster,
    boot_vec,
    FUN=bootstrap_replicate_par,
    my_vec=my_vec,
    my_func=my_func
    ) stopCluster(cluster) output <- list(
    'mean' = mean(estimates),
    'se' = sd(estimates),
    'estimates' = estimates
    ) return(output)
}

定义好这些函数后,我们就可以开始应用这些函数了。

三。具有左尾期望值的示例

作为演示,我们将通过 bootstrap 估计的统计数据是分布左尾的期望值。为了便于参考,我将它称为 ELT(α) ,用于表示 α 处的预期左尾。这种统计有助于估计的一个例子是条件风险值

假设我们关注的是一个随机变量, X ,其分布为某种连续分布。设 f(x)X 的概率密度函数 q(α)X 在 100* α 百分位的分位数函数。那么我们感兴趣的数量如下所示。

连续分布左尾的期望值

在现实世界的应用中,我们不知道 X 的真实底层分布。我们可以找到一个合适的分布,尽管它可能很难或不可能进行解析积分。幸运的是,我们有像 bootstrap 这样的数字工具来帮助我们。

在这个演示中,我抽取了 1000 个 X 的样本,其中X∾T(ν)ν 为 10.5。我制作了下面样本的直方图,并覆盖了真实密度函数,以衡量样本与基础数据的吻合程度。

样本相当好地符合真实分布,所以让我们计算 ELT(α)

我使用非参数方法,使用样本分位数来估计 ELT(α)ELT(α) 的非参数估计如下式所示。

左尾期望值的非参数样本估计

其中 Xi 是随机变量的实现,qˇ(α)α 处的样本分位数, I 是一个指示函数,如果为真则为 1,如果为假则为 0。

在继续之前,让我们看看我们对 ELT(α) 的样本估计,其中 α 为 0.1。是-2.063。

让我们最后使用自举。我将用 10,000 次引导迭代来估计 ELT(α)。

et_est <- bootstrap_parallel(rand_sample, expected_tail, B=10000)

下面的直方图显示了 ELT(α) 的自举估计的分布。

我们的平均估计值是-2.063,与样本估计值几乎完全相同(我四舍五入到小数点后 3 位)。它的标准误差为 0.085。自举 95% CI 为-2.232 至-1.906。我们可以 95%确信 ELT(0.1) 在-2.232 到-1.906 之间。

我们已经成功地使用自举来估计一个量,并量化我们对该估计的不确定性!

我希望这已经阐明了什么是自举以及如何应用它。感谢阅读!

注:所有图片均由作者(我)生成

博鲁塔·SHAP:每个数据科学家都应该知道的特征选择工具

原文:https://towardsdatascience.com/boruta-shap-an-amazing-tool-for-feature-selection-every-data-scientist-should-know-33a5f01285c0

我们如何使用博鲁塔和 SHAP 构建一个惊人的特征选择过程——以 python 为例

原文由诺亚纳夫Unsplash

当建立机器学习模型时,我们知道拥有太多的功能会带来一些问题,如维数灾难,此外还需要更多的内存、处理时间和功率。

在我们的特征工程管道中,我们采用特征选择技术来尝试从数据集中移除不太有用的特征。这就提出了一个问题:我们如何确定哪些特性是有用的?

对于这个任务,我们可以使用 Boruta,一个基于统计方法的特征选择算法。它依赖于两个原则:阴影特征和二项式分布。

1.阴影特征

Boruta 算法的第一步是评估特征的重要性。这通常是在基于树的算法中完成的,但在 Boruta 上,这些特征之间并不竞争,它们与被称为“阴影特征”的随机版本竞争。

假设我们有一个包含 3 个要素和 100 个观测值的数据集。在这种情况下,我们制作了数据集的副本,并混洗每个要素列。置换的特征然后被称为“阴影特征”(顺便说一下,很酷的名字),并创建一个新的数据集,Boruta 数据集,连接所有 3 个原始和 3 个新的阴影特征。

作者图片

现在,我们使用任何偏好方法,如随机森林或任何其他方法,来评估所有 6 个特征的特征重要性。Boruta 算法的思想是选择比纯粹的随机性表现更好的特征,这里由阴影特征表示,因此我们将原始特征的重要性与阴影特征的最高特征重要性进行比较。每当一个特征的重要性高于这个阈值时,我们就称之为“命中”。

我们现在可以只保留命中的,而丢弃其他的,对吗?但是如果他们中的一些因为运气不好而被丢弃了呢?答案在于迭代,这是下一步。

2.二项式分布

所有特征只有两种结果:“命中”或“未命中”,因此我们可以多次执行前面的步骤,并根据特征构建二项式分布。

考虑一个具有三个特征的电影数据集:“流派”、“观众 _ 评分”和“评论家 _ 评分”。经过 20 次迭代,我们可以得到以下结果:

我们可以将这些结果放在二项式分布图上:

作者图片

分布的尾部是最重要的部分。在这个例子中,各占 0.5%的概率。

genre变量落在红色区域,即“拒绝”区域。在这里,我们确信这些特性对目标变量没有影响。

绿色区域是“验收”区域。我们也确信这些特征是可预测的,并且应该保留在模型中。在这个例子中,critic_score是一个应该保留的好特性。

在蓝色区域,Boruta 不确定该特征是否是预测性的。在这种情况下,我们可以保留这些特征,甚至可以使用其他方法来查看它们是否会对模型预测产生任何影响。上例中,audience__score在这个区域上,尽管它靠近绿尾。

我们保留在绿色和蓝色区域分类的特征,并丢弃红色区域的特征。

你可以点击查看博鲁塔算法的精彩解释。

3.Python 中的 Boruta

示例代码也可以在我的 github 上找到,所以可以跳过这一节。

https://github.com/vinyluis/Articles/tree/main/Boruta SHAP

要使用 Boruta,我们可以使用 BorutaPy 库[1]:

pip install boruta

然后我们可以导入糖尿病数据集(可从sci kit-Learn【2】获得):

from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split# Fetches the data
dataset = load_diabetes(as_frame = True)
# Gets the independent variables
X = dataset['data']
# Gets the dependent variable (the target)
y = dataset['target']
# Splits the dataset
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)

为了使用 Boruta,我们需要定义一个估计量,它将用于估计特征的重要性。在这种情况下,我选择了 RandomForestRegressor:

from sklearn.ensemble import RandomForestRegressor# Defines the estimator used by the Boruta algorithm
estimator = RandomForestRegressor()

现在,我们可以创建 BorutaPy 对象,并使用估计器使其适合数据:

from boruta import BorutaPy# Creates the BorutaPy object
boruta = BorutaPy(estimator = estimator, n_estimators = 'auto', max_iter = 100)
# Fits Boruta
boruta.fit(np.array(X_train), np.array(y_train))

最后,我们可以发现哪些特性是重要的,哪些是不重要的,哪些是不确定的:

# Important features
important = list(X.columns[boruta.support_])
print(f"Features confirmed as important: {important}")# Tentative features
tentative = list(X.columns[boruta.support_weak_])
print(f"Unconfirmed features (tentative): {tentative}")# Unimportant features
unimportant = list(X.columns[~(boruta.support_ | boruta.support_weak_)])
print(f"Features confirmed as unimportant: {unimportant}")

输出是:

Features confirmed as important: ['bmi', 'bp', 's5', 's6']
Unconfirmed features (tentative): []
Features confirmed as unimportant: ['age', 'sex', 's1', 's2', 's3', 's4']

4.博鲁塔 SHAP 特征选择

Boruta 是一种健壮的特征选择方法,但它强烈依赖于特征重要性的计算,这可能会有偏差或对数据不够好。

这就是 SHAP 加入团队的地方。通过使用 SHAP 值作为特征选择方法,我们得到了博鲁塔 SHAP 特征选择算法。通过这种方法,我们可以得到 SHAP 方法中存在的强可加性特征解释,同时具有博鲁塔算法的鲁棒性,以确保只有重要变量保留在集合中。

如果你不知道 SHAP 是什么,看看我解释它的文章:

5.蟒蛇皮博鲁塔·SHAP

要使用 Boruta,我们可以使用 BorutaShap 库[4]:

pip install BorutaShap

首先我们需要创建一个 BorutaShap 对象。因为我们想使用 SHAP 作为特征重要性鉴别器,所以importance_measure的默认值是“shap”。当问题是分类问题时,我们可以将classification参数更改为 True。

from BorutaShap import BorutaShap# Creates a BorutaShap selector for regression
selector = BorutaShap(importance_measure = 'shap', classification = False)

然后,我们在数据或数据样本中拟合 BorutaShap 选择器。n_trials参数定义了 Boruta 算法的迭代次数,而sample布尔值决定了该方法是否会在内部对数据进行采样以加速该过程。

# Fits the selector
selector.fit(X = X_train, y = y_train, n_trials = 100, sample = False, verbose = True)
# n_trials -> number of iterations for Boruta algorithm
# sample -> samples the data so it goes faster

拟合后,将显示以下结果:

4 attributes confirmed important: ['s5', 'bp', 'bmi', 's6']
5 attributes confirmed unimportant: ['s2', 's4', 's3', 'age', 'sex']
1 tentative attributes remains: ['s1']

最后,我们可以看到哪些功能将被删除,并将其从我们的数据中删除:

# Display features to be removed
features_to_remove = selector.features_to_remove
print(features_to_remove)# Removes them
X_train_boruta_shap = X_train.drop(columns = features_to_remove)
X_test_boruta_shap = X_test.drop(columns = features_to_remove)

6.结论

与特征选择对我们的 ML 管道同样重要的是,我们需要使用最好的算法来确保最好的结果。

这种方法的缺点是评估时间,对于许多 Boruta 迭代,或者当 SHAP 适合许多观测值时,评估时间可能太长。当心时间!

考虑到这一点,博鲁塔 that 是我们可以用来选择机器学习管道上最重要特征的最佳方法之一。

始终使用它,但要记得将结果与其他方法进行比较,以确保更高的可靠性。

如果你喜欢这个帖子…

支持我一杯咖啡!

给我买杯咖啡!

看看这个很棒的帖子

参考文献

[1 ]特别套装:https://github.com/scikit-learn-contrib/boruta_py

[2] Scikit-Learn 糖尿病数据集:https://sci kit-Learn . org/stable/modules/generated/sk Learn . datasets . load _ Diabetes . html

【3】https://shap.readthedocs.io/en/latest/index.htmlSHAP 包

[4]博鲁塔沙普包:https://github.com/Ekeany/Boruta-Shap

[5]https://medium . com/analytics-vid hya/is-this-the-best-feature-selection-algorithm-borutashap-8bc 238 aa 1677

[6]https://towardsdatascience . com/boruta-explained-the-way-I-wish-someone-explained-it-to-me-4489d 70 e 154 a

用于时间特征选择的博鲁塔·SHAP

原文:https://towardsdatascience.com/boruta-shap-for-temporal-feature-selection-96a7840c7713

特征选择算法如何应对数据漂移

Unsplash 上由 Rodion Kutsaev 拍照

每个人都知道特征选择程序在机器学习流水线中的重要性。它们有助于实现更好的性能,同时根据我们的监督任务降低输入数据的维度。

过去,我们面临着最佳特征选择的问题,提出了仅过滤相关预测因子的引人入胜的尖端技术。我们发现了使用 SHAP 进行特征选择的重要性,而不是简单的算法重要性度量。我们测试了博鲁塔和 SHAP ,结果证明这是一个非常有效的选择方法。我们还介绍了递归特性添加方法,作为标准递归特性消除的一种有价值的替代方法。

很少有人知道,像大多数数据驱动算法一样,随着时间的推移,特征选择也会变得不准确。因此,需要用新数据重复过滤过程,以捕捉新模式。除了作为一个良好的实践,定期重新运行特性选择过程是每个包装器选择方法的真正需要。由于包装器方法根据基础学习者的排名选择“重要”的特征,很容易理解数据转移对最终结果的危害有多大

在本帖中,我们对一个动态系统进行特征选择。我们遵循一种迭代的方法,在这种方法中,我们重复搜索生成过程中的相关特征。我们试图预测的目标是一些正弦信号的简单线性组合的结果。预测值和目标值之间的关系在整个实验过程中保持不变。我们只增加每个预测特征中的噪声量,以观察不同的特征选择算法在数据移动的环境中如何表现。

我们着重比较两种众所周知的滤波方法:递归特征选择(RFE)和 Boruta。我们使用了 shap-hypetune 库,因为它提供了我们感兴趣的两种方法的有效和直观的实现。它还使得能够使用 SHAP 特征重要性,而不是基于树的特征重要性,以使排序过程更稳定并且更少偏向训练数据。

实验设置

我们的实验包括使用合成数据。首先,我们生成三个人工正弦序列。然后,我们合并信号,简单地将它们相加,以获得我们试图预测的目标。最后,我们按照时间模式向输入序列添加一些随机噪声。换句话说,我们从几乎为零的噪声开始,随着时间的推移不断增加。绘制数据时,此过程的结果是可以理解的:

带有时间噪声注入的正弦序列(图片由作者提供)

带有时间噪声注入的正弦序列(图片由作者提供)

带有时间噪声注入的正弦序列(图片由作者提供)

带有时间噪声注入的正弦序列(图片由作者提供)

带有时间噪声注入的正弦序列(图片由作者提供)

一开始,我们可以很容易地识别三个输入信号的行为。随着时间的推移,噪音开始隐藏真实的数据动态,最终使它们几乎无法识别。

连同有意义的正弦序列,我们添加一些随机特征。它们对于评估我们的特征选择策略的性能是重要的,因为它们对于我们的预测任务应该被标记为“无用的”。如果我们绘制特征和目标之间的滚动自相关图,我们可以观察到相关性是如何随着时间的推移而降低的。这并不令人惊讶,但证实了噪音对数据关系的负面影响。正如所料,噪声特征与目标的相关性几乎为零。

目标和特征之间的滚动相关性(图片由作者提供)

考虑到我们的预测任务,在这种情况下建立预测模型应该非常棘手。

结果

给定这些数据并了解底层系统动态,我们就可以对这两种提出的特性选择方法进行建模和基准测试了。我们使用标准的时间序列分割方法将数据分成 15 份。我们根据自己掌握的数据对算法进行不断扩展的滚动训练。这样,当训练数据变大时,正弦序列在每次分裂时变得更嘈杂。分裂进行得越多,我们的算法发现真实模式的压力就越大。

下面我们以热图格式报告选择结果。递归 SHAP 特征消除至少在第四次分裂之前可以检测到真正有意义的特征。之后,由于数据中噪声的增加,选择似乎变得更加困难。随机特征被错误地检测为重要,尽管它们与目标没有任何关系,这可能导致错误的预测。

RFE-SHAP 在每个时间点选择(黄色)和删除(黑色)的特征(图片由作者提供)

相反,博鲁塔·SHAP 只能正确识别每次分裂中的重要信号。这是一个非常令人印象深刻的结果,它证明了博鲁塔 SHAP 作为特征选择算法在困难的预测环境中的优势。

博鲁塔-SHAP 在每个时间点选择(黄色)和删除(黑色)的特征(图片由作者提供)

摘要

在这篇文章中,我们在数据漂移的极端环境下测试了两种不同的特征选择方法。我们对递归特征选择和 Boruta 进行了基准测试(两者都使用 SHAP 作为特征重要性度量)。虽然递归特征选择显示出随着数据中噪声的增加,在检测真正有意义的预测因子方面存在一些困难,但是 Boruta 做了出色的工作。当数据漂移变得更加明显时,它也总能检测到真实的系统模式,捍卫其首要地位“出色的特性选择算法”

查看我的 GITHUB 回购

保持联系: Linkedin

时间序列的 Box-Cox 变换

原文:https://towardsdatascience.com/box-cox-transform-for-time-series-cc45f26082c6

如何使用 Box-Cox 变换创建平稳时间序列?

克里斯托夫·比尔在 Unsplash 上的照片

介绍

使时间序列 平稳 是进行任何 时间序列 分析或预测时必不可少的一部分。平稳性确保我们的数据不会随时间发生统计变化,因此它可以更准确地模拟概率分布,从而更容易建模。

平稳性的一个要求是时间序列需要一个恒定的方差。换句话说,波动应该始终如一地在同一尺度上。实现这一点的一个方法是取序列的自然对数,然而这假设你的原始序列遵循指数趋势。因此,自然对数变换可能并不总是最佳选择。

在这篇文章中,我想介绍一下 Box-Cox 变换,它是自然对数变换的推广。Box-Cox 允许确定自然对数是否确实是最佳变换,或者某个其他的 幂变换 是否更优。

如果您想了解更多关于平稳性的背景和要求,请查阅我以前的文章:

什么是 Box-Cox 变换?

从根本上说,Box-Cox 将非正态数据转换成更像 正态分布 的数据。

现在你可能想知道为什么我们需要时间序列数据来模拟正态分布?嗯,在拟合某些模型的时候,比如【ARIMA】,他们用 最大似然估计(MLE) 来确定他们的参数。根据定义,最大似然估计必须符合某种分布,对于大多数包装来说,这是正态分布。

Box-Cox 变换由 λ 参数化(从 -55 ) 将时间序列、 y 变换为:

作者在 LaTeX 中创建的方程。

我们看到, λ=0 是自然对数变换,然而还有许多其他依赖于值 λ的变换。 举个例子,如果 λ=0 就是平方根变换 ,λ=1 就没有变换 λ=3 就是三次变换。

λ 通过查看哪个值最接近正态分布的变换数据来选择。幸运的是,在计算软件包中,这对于我们来说很容易做到!

Box-Cox 是一种 幂变换 的类型,因为我们总是将我们的原始系列提升到某个幂,即 λ

应用 Box-Cox 变换

让我们通过一个例子来展示如何在 Python 中使用 Box-Cox 变换。以下是 1948-1960 年间美国航空客运量的典型图表:

数据来自拥有 CC0 许可证的 Kaggle

作者代码要点。

作者用 Python 生成的图。

数据显然不是静态的,因为平均值和方差都随着时间的推移而增加。为了稳定方差,我们可以像上面讨论的那样使用 Box-Cox 变换。Scipy 有一个名为boxcox的函数,它可以找到 λ 的最佳值,并使用它相应地转换我们的数据:

作者代码要点。

作者用 Python 生成的图。

我们的方差现在是稳定的,波动在一个一致的水平上!最佳 λ 值为 0.148 ,接近完美的自然对数变换,但不完全。这表明对数变换并不总是最好的方法,通过 Box-Cox 变换可以使用更彻底的方法。

用于生成图和转换的完整代码可以在我的 GitHub 中找到:

*https://github.com/egorhowell/Medium-Articles/blob/main/Time Series/Time Series Tools/box_cox_transform.py

结论

稳定的方差是时间序列平稳性的一个要求。平稳性很重要,因为它通常是大多数预测模型所必需的。获得稳定方差的一般方法是对序列应用 Box-Cox 变换,通过 λ、 进行参数化。Python 中的变换会自动适应最佳值 λ 并相应地变换您的系列。

参考资料和进一步阅读

  • 预测:原理与实践:https://otexts.com/fpp2/
  • 博克斯,乔治 E. P .考克斯博士(1964)。“对转变的分析”。皇家统计学会杂志,B 辑。26(2):211–252

和我联系!

(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。许可证: CC BY-SA 4.0*

打破 6 个分析习惯,释放价值

原文:https://towardsdatascience.com/breaking-6-analytics-habits-to-unlock-value-d33fec9c90ee

团队如何改进他们的数据之旅

对于许多公司来说,数据并没有兑现它的承诺(和 ROI)。因为数据之旅就像一场马拉松,商业价值和影响在最后一英里开始显现。

所谓的分析马拉松的最后一英里包括数据分析、洞察沟通和采取行动。然而,大多数公司在处理最后三个步骤之前就已经退出了竞争,结果是无法达到预期的结果。

分析马拉松—图片来自布伦特·戴克斯

不交付价值可能会很快开始破坏数据文化,并可能导致业务团队忽视在数据马拉松的早期阶段所做的努力。想要创建强大的数据文化并在决策桌上拥有一席之地的数据从业者应该仔细评估这些习惯是否阻碍了他们的团队。

是什么阻碍了数据团队的发展?

虽然这个问题没有直接的答案,但我根据与数据和分析领导者和从业者的多次交谈,确定了六个坏的分析习惯。该列表包含导致成本高昂的决策和错失业务影响机会的主题,并与四个关键要素相关:人员、流程、工具和文化,它们是:

六个主要的坏分析习惯

  1. 像仪表板工厂一样对待数据/分析/BI 团队
  2. 仅分析一部分可用数据
  3. 全部是描述性的,没有诊断分析
  4. 等待太久才行动
  5. 陷入后续问题的循环中
  6. 未能标准化和简化洞察故事

下图总结了每个坏习惯与四个关键要素中相应的根本原因之间的关系:

不良分析习惯的主要原因—作者图片

  1. 像仪表板工厂一样对待数据/分析/BI 团队

这些团队没有既定的流程或框架来构建工作流并收集上下文和信息。因此,数据团队会收到许多特别的请求,并被要求在没有适当上下文的情况下创建一个又一个仪表板。通常,他们在没有要求澄清或理解最终目标(即,企业试图实现什么)的情况下就开始分析。数据和业务团队之间的这些沟通差距导致基于假设的工作和数据分析师回答错误的问题。

问“为什么”的文化也经常缺失。

影响:关键决策中浪费的时间和延误。

怎么破:

  • 让数据团队更早地参与流程,并就预期的业务成果和可行的见解达成一致
  • 促进数据和业务团队之间的紧密协作
  • 开发流程,让人们思考为什么。
  • 考虑将您的分析师嵌入到业务团队中。

2。仅分析部分可用数据

大多数公司只分析他们收集的一小部分数据。团队通常只关注顶级驱动因素和通常的嫌疑人(即通常的切片和骰子),这导致测试非常高级的假设。还有一些数据孤岛妨碍了团队利用所有可用的数据(例如,营销团队只查看营销数据,而不将它与订单或产品数据相结合)。

影响:错失洞察力和机会。

怎么破:

  • 超越仪表盘上可用的切片和骰子,开始分析数据中所有可操作的维度
  • 通过查看多种因素的组合并利用不同类型的数据(例如,客户、产品和营销数据)进行深入分析

3。无诊断分析的所有描述

在仪表板中显示正在发生的事情可以提供信息,但不具有洞察力。只有这些变化背后的“为什么”才能推动建议和行动。然而,团队经常在每周/每月的评审中查看仪表板,并评论指标上升或下降,而没有对根本原因或可操作的见解的明确答案。

影响:缺乏行动力,错失商机

怎么破:

4。等待太久无法行动

团队花太多时间分析,以至于不能及时行动。随着业务变化的速度比以往任何时候都快,可操作性洞察的速度变得越来越重要。业务利益相关者需要快速、可行的洞察力,以便在为时已晚之前做出明智的决策。在大多数情况下,团队在同一天就需要洞察,等待几天或几周对于业务采取行动来说太长了。

影响:被动团队、错失机会、破坏数据文化

怎么破:

  • 利用技术来增强当前工作流,并加快获得可操作见解的速度
  • 把大部分时间花在独特的人类增值任务上,比如洞察力、沟通和决策

5。陷入后续问题的循环中

当使用仪表板执行诊断分析时,跟进是不可避免的。为了理解事物为什么会发生变化以及如何对其采取行动,需要更细致的洞察(例如,结合 2 或 3 个不同的维度)。手动分析可能需要多次迭代,并会产生后续循环,因为最初的答案只是皮毛。然而,如果没有数据团队的支持,企业领导通常很难回答他们的问题。

影响:不堪重负的数据团队、没有答案的问题、只触及表面的分析,以及错失的机会。

怎么破:

  • 投资工具和培训,让商业领袖能够回答他们的问题
  • 考虑决策智能平台,使利益相关者能够深入了解原因

6。未能标准化和简化洞察故事

报告和分析通常分布在各种平台上,并且缺乏一致的结构:指向仪表板的松散消息、带有即席计算的 Excel 表格、PowerPoint 幻灯片、概念页面、票证等……即使发现了见解,它们也不会被决策者发现,或者它们与业务优先级的关系没有得到很好的解释。

影响:难以解释结果并得出可行的见解,错失机会。

怎么破:

  • 建立通用的报告格式。利用瀑布图等标准方法来总结关键指标的变化。
  • 开发和推广数据叙事的最佳实践

底线:关注最后一英里

从团队沟通和协作的方式开始,延伸到用于发现见解的工具和分发见解的流程,这些不良的分析习惯正在阻碍团队征服最后一英里的分析,并导致错失机会…

从哪里开始?

  • 对你的现状进行一个全面的评估,看看你是否属于这些模式
  • 关注最阻碍你的是什么
  • 注意是什么在流程的早期破坏了您的价值链,并对接下来的步骤造成了多米诺骨牌效应。

习惯很难打破。花点时间评估、区分优先级并不断引入增量变化。

参考资料:

  1. Brent Dykes,数据分析马拉松:为什么您的组织必须关注终点 (2022),Forbes.com

想法?伸出手去 若昂索萨处长成长处 考萨 。敬请关注更多关于如何增加分析价值的帖子。

破解熊猫 GroupBy 函数背后的强大魔力

原文:https://towardsdatascience.com/breaking-down-the-powerful-magic-behind-the-pandas-groupby-function-b94cc2427eb9

对 groupby 如何工作的详细解释有助于您更好地理解它。

托拜厄斯·菲舍尔在 Unsplash 拍摄的照片

近年来,一些最受欢迎的数据集和民意调查都与政府选举有关。选举季节已经成为无数图表、地图、民意测验和预测通过大众媒体传播的时候。

我想让你想象你在一个忙碌的早晨醒来,开始翻阅你订阅的《纽约时报》(New York Times)的一些数据(如果你永远不会这么做,就迁就我一下)。你累了,眼睛勉强睁开,心理处理能力还在升温。你只是想要对当下发生的事情有一个快速、容易理解的洞察。

然后, Times 给你的是一个, 巨型 数据集,其中每一行都是单个选民,列包含关于年龄、地点、种族、性别等各种数据。等。—最后以选民选择的候选人结束。

那现在没什么意义了,对吧?即使您滚动了几个小时,以这种方式格式化的数据也不太可能为您提供关于底层数据集的任何有意义的信息。简直太分散了。太

作为数据科学家,我们的主要任务之一是从数据中辨别关键见解,并以简单易懂的方式提供给公众。有多种方法可以完成这项任务——我今天想重点介绍的一种方法是将数据的各种属性组合在一起,努力发现模式。

根据您选择的工具,方法可能会有所不同。在本文中,我将讨论 Python 的 Pandas 模块中分组和聚合数据的一种常用方法:groupby函数。

函数本身已经在各种文章中讨论过,但是一个经常被忽略的话题是幕后发生的“魔术”。在本文中,虽然我将简要回顾该函数的上下文,但我将主要深入研究 Pandas 在幕后定义的实际的GroupBy对象。通过研究它的结构,我希望你能更好地理解groupby实际上是如何工作的,以便在你未来的数据科学任务中更有效地使用它。

让我们开始吧。

快速回顾 Groupby

理解groupby的最好方法是通过例子。假设我们有下面这个叫做people的小数据集,它包含了人们的性别、年龄、身高和体重的信息:

作者图片

groupby函数的主要用例是按照特定的列对数据进行分组,并使用一些指定的函数为每个唯一的组聚合其他列的值。例如,如果我们想得到每种性别的平均年龄、身高和体重,我们可以这样做:

people.groupby('Sex').mean()

作者图片

您会注意到"Name"列被自动排除了,原因很简单,计算字符串列表的平均值没有逻辑意义。

还可以 1)一次只关注一列中的值,2)应用自定义聚合函数。例如,可能出于某些奇怪的原因,我们想要按性别划分的年龄的平方和。下面是完成这项任务的代码:

def sum_of_squares(arr):
    return sum([item * item for item in arr)])people.groupby('Sex')[['Age']].agg(sum_of_squares)

作者图片

以上示例中的一些要点:

  • 在我们的用户定义函数sum_of_squares中,我们利用了一个列表理解【1,2】来迭代地平方所有的项,然后对它们求和。
  • 你会注意到这个函数接受一个数组。这是因为当我们按'Sex'分组并提取出'Age'时,我们有效地将每个组的所有年龄(在本例中为'Male''Female')存储在一个数组中(或者从技术上讲,是一个系列对象 [3】)。然后,聚合函数接受该数组,并将其值聚合为输出数据帧中显示的每组的单个最终值。我们将在文章的下一部分对此有更深入的了解。
  • 使用双括号提取出'Age'列是一个语法上的小技巧,它使我们能够以 DataFrame 而不是 Series 的形式返回输出。

至此,我们已经回顾了我们需要的关于groupby的一切,以正确理解抽象层下面发生的事情。我们现在准备更深入。

Groupby 背后的“魔力”

为了简单起见,让我们坚持上面的第一个例子:在通过'Sex'变量分组之后,获得所有列的平均值。

代码 : people.groupby('Sex').mean()

之前:

作者图片

:

作者图片

这一切都很好,但有一点不完整。怎么会?如果我们将数据转换分解成几个组成部分,我们会得到三个主要阶段:

  1. 原始的、未改变的数据帧(“之前”的图片)。
  2. 一种转换后的数据帧,它将感兴趣的列中的所有唯一标签与其他列中的相关值组合在一起。
  3. 一个最终的数据帧,它已经聚合了这些值,因此每个组都有一个单独的值(“后”图)。

中间阶段怎么了?为了深入理解groupby,这可以说是流程中最重要的部分,所以让我们看看是否有一种方法可以显示这个中间步骤的数据。

第一种尝试可能是在调用groupby之后、调用聚合函数(在我们的例子中是mean)之前尝试显示数据:

people.groupby('Sex')

作者图片

嗯,好吧——所以事情没有按计划进行。我们只是得到了字面量GroupBy对象的字符串表示,就像在 Pandas 中实现的那样。事实证明,要查看分组后的实际数据,我们需要使用对象的关联get_group函数:

people_grouped_by_sex = people.groupby('Sex')
for group in people['Sex'].unique():
    display(people_grouped_by_sex.get_group(group))

作者图片

让我们来分解一下:

  • 首先,我们将GroupBy对象存储在变量people_grouped_by_sex中。
  • 然后,我们使用一个循环来遍历'Sex'列的所有唯一标签,我们知道这些标签构成了GroupBy对象的唯一组。请注意,它也可以直接遍历一个硬编码的列表,比如['Male', 'Female'],但是我故意编写了上面的代码来演示如何将这种技术推广到一个更大的数据集——特别是在您可能不知道所有唯一的组标签的情况下。
  • 最后,我们使用GroupBy对象的get_group方法来访问每个组的数据帧——我喜欢称它们为“子帧”或“迷你数据帧”,尽管我会提到这些无论如何都不是标准术语。display函数在Jupyter Notebooks【4】中使用,以一种漂亮的、人类可读的格式输出你传递给它的任何对象。

现在,我们可以看到中间阶段发生了什么:Pandas 获取数据帧,并将其分成一堆更小的数据帧,每个数据帧包含我们分组所依据的列中的一个组标签的数据。然后将这些子帧的值汇总,得到最终的数据帧。

例如,在上面的第一个子帧中(对于组'Male'),'Age'列中的值是44, 30, 24,18。这些数字的平均值是29.00,确切地说,是在我们的GroupBy对象上调用mean函数后,我们在最终输出数据帧中看到的值。其他值的计算方式完全相同。

现在你知道了——groupby的秘密不再是秘密了。

一些最后的提示和想法

我将以一些通用的提示结束,下次你处理groupby时记住,无论是为你自己还是向他人解释:

  • 简单性:使用groupby的目的应该是简化你的数据,而不是让它更加复杂。
  • 聚焦:虽然有可能对多个栏目进行分组,但开始时慢一点,集中精力绘制聚焦的洞见通常是个好主意。即使一次只有一个专栏,您也可以做很多事情。
  • 适应性:不要纠结于这个单一的解决方案,因为根据你的情况可能会有更好的选择。在 Pandas 中还有其他方式来聚集数据。

最后一点,你可能想知道为什么 Pandas 不直接向我们展示这些迷你数据帧,而是需要一种迂回的方法来查看它们。从编程的角度来看,这是有意义的:用户不需要知道使用groupby时发生了什么。隐藏其工作方式可以作为一个抽象层,防止新用户在早期感到困惑或不知所措。

也就是说,我认为钻研这些细节是非常有用的教学工具,可以更好、更深入地理解groupby实际上是如何工作的。获得这种理解帮助我更好地解析和编写更复杂的groupby查询,我希望它能为你做同样的事情。

就这样,下次再见了。快乐分组!

想擅长 Python? 获取独家,免费获取我简单易懂的攻略 。想在介质上无限阅读故事?用我下面的推荐链接注册!

https://murtaza5152-ali.medium.com/?source=entity_driven_subscription-607fa603b7ce---------------------------------------

我叫穆尔塔扎·阿里,是华盛顿大学研究人机交互的博士生。我喜欢写关于教育、编程、生活以及偶尔的随想。

参考

[1]https://towardsdatascience . com/whats-in-a-list-comprehension-C5 d 36 b 62 f 5
【2】https://level up . git connected . com/whats-in-a-list-comprehension-part-2-49d 34 bada 3 f 5
【3】https://pandas.pydata.org/docs/reference/api/pandas.Series.html
【4】https://level up . git connected . com/whats-in-a-jupyter-notebook-windows-edition-1f 69 c 290 a 280

分解它:K-均值聚类

原文:https://towardsdatascience.com/breaking-it-down-k-means-clustering-e0ef0168688d

使用 NumPy 和 scikit-learn 探索和可视化 K-means 聚类的基本原理。

**Outline:**
[1\. What is K-Means Clustering?](#1791)
[2\. Implementing K-means from Scratch with NumPy](#f947)
   [1\. K-means++ Cluster Initialization](#96e1)
   [2\. K-Means Function Differentiation](#d0b4)
   [3\. Data Labeling and Centroid Updates](#ea21)
   [4\. Fitting it Together](#9b4e)
[3\. K-Means for Video Keyframe Extraction: Bee Pose Estimation](#e5af)
[4\. Implementing K-means with](#aabf) [scikit-learn](#aabf)
[5\. Summary](#8571)
[6\. Resources](#f720)

文章概述

参见我的 GitHub learning-repo 获取这篇文章背后的所有代码。

1.什么是 K-Means 聚类?

k 均值聚类是一种算法,用于将数据分类到用户定义数量的组中, k 。K-means 是一种无监督的机器学习形式,这意味着在运行算法之前,输入数据没有标签。

由于各种原因,使用 k-means 等算法对数据进行聚类是有价值的。首先,聚类用于在构建数据分析管道时识别未标记数据集中的独特组。这些标签对于数据检查、数据解释和训练 AI 模型是有用的。K-means 及其变体在各种上下文中使用,包括:

2.用 NumPy 从头开始实现 K-Means

为了获得对 k-means 如何工作的基本理解,我们将检查算法的每个步骤。我们将通过可视化的解释和用 NumPy 从头构建一个模型来实现这一点。

k-means 背后的算法和数学函数很漂亮,但相对简单。让我们从一个概述开始:

K-Means 算法简介

总之,k-means 算法有三个步骤:

  1. 指定初始聚类中心(质心)位置
  2. 基于最近的质心标注数据
  3. 将质心移动到新标记的数据点的平均位置。返回步骤 2,直到聚类中心收敛。

让我们继续构建模型。为了使用该算法,我们需要编写以下函数:

K-Means 类函数概述

2.1.集群初始化

k-means 算法的第一步是让用户选择数据应该被聚类到的组的数量, k

在算法的原始实现中,一旦选择了 k ,将通过随机选择输入数据点的 k 作为质心起始位置来初始化聚类中心(或质心)的初始位置。

这种方法被证明是非常低效的,因为开始的质心位置可能最终彼此随机接近。2006 年,亚瑟和瓦西维茨基 开发了一种新的更有效的质心初始化方法。他们在 2007 年发表了他们的方法,称之为 k-means++

k-means++ 不是随机选择初始质心,而是基于距离分布有效地选择位置。让我们想象一下它是如何工作的:

K-Means ++质心初始化

既然 k-means++背后的直觉已经暴露出来,让我们为它实现函数:

k-Means_ init _ centroids _ plus plus函数

值得注意的是,除了必须手动选择 k 之外,还可以使用几种无偏技术来确定最佳数字。 Khyati Mahendru 在她的文章中解释了其中两种方法,即剪影方法 。值得一读!

2.2.数据标注和质心更新

质心初始化之后,算法进入数据标记和质心位置更新的迭代过程。

在每次迭代中,输入数据将首先根据其与质心的接近程度进行标注。在此之后,每个质心的位置将被更新为其聚类中数据的平均位置。

这两个步骤将重复进行,直到标签分配/质心位置不再改变(或收敛)。让我们想象一下这个过程:

现在,让我们实现数据标签代码:

k-Means_ compute _ labels函数

最后,我们将实现质心位置更新功能:

k-Means_ 更新 _ 质心 _ 位置功能

2.3.k-均值函数微分

k-means 算法的第三步是更新质心的位置。我们看到这些质心被更新到所有聚类的标记点的平均位置。

将质心更新到平均聚类位置似乎很直观,但是这一步背后的数学原理是什么?基本原理在于 k-means 方程的微分。

让我们通过探索 k-means 函数微分的动画证明来展示这种直觉。该证明表明位置更新是旨在最小化组内方差的 k 均值方程的结果。

k-均值函数微分

2.4 装配在一起

现在我们已经为 k-means 模型构建了主干函数,让我们将它结合到一个单一的fit函数中,该函数将使我们的模型适合输入数据。我们还将在这里定义__init__函数:

k-表示拟合函数

K-Means init 函数

现在我们可以把这个模型和我的漫游笔记本一起使用了。这个笔记本使用合成生成的数据(如上面的视频所示)来演示我们新编写的 k_means.py 代码的功能。

3.视频关键帧提取的 k-均值算法:蜜蜂姿态估计

太棒了——我们已经完全从零开始构建了 k-means 模型。与其将这些代码扔到一边,不如让我们在一个示例场景中使用它们。

在过去的几年里,神经科学和 DL 研究社区取得了令人印象深刻的进步,实现了高度准确和自动化的动物行为跟踪和分析*。该研究领域中使用的框架实现了各种卷积神经网络架构。这些模型还严重依赖迁移学习来减少研究人员需要生成的训练数据量。这些框架的两个流行例子包括 DeepLabCutSLEAP

  • 边注:这个子域通常被称为 计算神经行为学

为了训练模型来自动跟踪动物身上的特定点,研究人员通常必须从他们的行为视频中手动标记 100-150 个独特的帧。考虑到所有的事情,这是一个非常小的数字,可以无限期地自动跟踪长的行为视频!

然而,当标记这些训练帧时,研究人员必须考虑的一个重要方面是,它们应该尽可能地互不相同。如果存在数小时的记录,给一个视频的前 5 秒贴标签是非常没有目的的。这是因为动物在前 5 秒的行为和身体状态可能不会准确地代表整个视频数据集的特征。因此,该模型将不会被训练成有效地识别各种特征。

那么这和 k 均值有什么关系呢?无需从视频中手动识别唯一的关键帧,可以实现 k-means 等算法来自动将视频帧聚类到唯一的组中。让我们想象一下这是如何工作的:

视频关键帧提取的 k-均值算法

为了获得对这个过程的实际理解,您可以使用我的漫游笔记本跟随用于隔离这些帧的代码。

4.用 scikit-learn 实现 K-means

在现实世界中,除非必要,否则通常应该避免实现自己构造的算法。相反,我们应该依靠由专家付费和志愿者贡献者维护的精心有效设计的框架。

在这个实例中,让我们看看用 scikit-learn 实现 k-means 有多容易。这个类的文档可以在[这里](http://4. Implementing K-means with scikit-learn)找到。

模型初始化和拟合的 scikit-learn 实现与我们的非常相似(不是巧合!),但是我们必须跳过大约 250 行的代码。此外,scikit-learn 框架为 k-means 实现了优化的 BLAS 例程,使得它们的实现比我们的快得多。

长话短说——从零开始学习是无价的,但从零开始工作却不是。

5.摘要

在这篇文章中,我们探索 k-means 算法背后的数学和直觉的基础。我们使用 NumPy 从头构建了一个 k-means 模型,使用它从一个动物行为视频中提取唯一的关键帧,并学习了如何使用 scikit-learn 实现 k-means。

我希望这篇文章对你有价值!如有任何意见、想法或问题,请随时联系我。

6.资源

****References:** [1\. Hicks SC, Liu R, Ni Y, Purdom E, Risso D (2021). mbkmeans: Fast clustering for single cell data using mini-batch *k*-means. PLoS Comput Biol 17(1): e1008625.](https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1008625)
[2\. Sharma A, Rastogi V (2014). Spam Filtering using K mean Clustering with Local Feature Selection Classifier. Int J Comput ApplMB means108: 35-39.](https://www.semanticscholar.org/paper/Spam-Filtering-using-K-mean-Clustering-with-Local-Sharma-Rastogi/901af90a3bf03f34064f22e3c5e39bbe6a5cf661?p2df)
[3\. Muhammad Shahzad, Bank Customer Segmentation (PCA-KMeans)](https://www.kaggle.com/code/muhammadshahzadkhan/bank-customer-segmentation-pca-kmeans)
[4\. Arthur D, Vassilvitskii S (2006). k-means++: The Advantages of Careful Seeding. *Proceedings of the eighteenth annual ACM-SIAM symposium on Discrete algorithms*. Society for Industrial and Applied Mathematics Philadelphia, PA, USA. pp. 1027–1035](http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf)**Educational Resources:**
- [Google Machine Learning: Clustering](https://developers.google.com/machine-learning/clustering)
- [Andrew Ng, CS229 Lecture Notes, K-Means](http://cs229.stanford.edu/notes2020spring/cs229-notes7a.pdf)
- [Chris Piech, K-Means](https://stanford.edu/~cpiech/cs221/handouts/kmeans.html)**

分解它:逻辑回归

原文:https://towardsdatascience.com/breaking-it-down-logistic-regression-e5c3f1450bd

用 NumPy、TensorFlow 和 UCI 心脏病数据集探索逻辑回归的基本原理

逻辑回归概述。图片作者。

**Outline:** 1\. [What is Logistic Regression?](#6a7a) 2\. [Breaking Down Logistic Regression](#c9d3)
   1\. [Linear Transformation](#9cd1)
   2\. [Sigmoid Activation](#bd84)
   3\. [Cross-Entropy Loss Function](#43d1)
   4\. [Gradient Descent](#cee3)
   5\. [Fitting the Model](#3930)
3\. [Learning by Example with the UCI Heart Disease Dataset](#6f98)
4\. [Training and Testing Our Classifier](#84da) 5\. [Implementing Logistic Regression with TensorFlow](#e0fa)
6\. Summary
7\. [Notes and Resources](#df98)

1.什么是逻辑回归?

逻辑回归是一种受监督的机器学习算法,它为输入数据集创建分类标签( 12 )。逻辑回归(logit)模型在各种环境中使用,包括医疗研究商业分析。

理解逻辑回归背后的逻辑可以为深度学习的基础提供强有力的基础见解。

在本文中,我们将分解逻辑回归来获得对这个概念的基本理解。为此,我们将:

  1. 探索逻辑回归的基本组件,并使用 NumPy 从头开始构建模型
  2. 在 UCI 心脏病数据集上训练我们的模型,以根据成人的输入健康数据预测他们是否患有心脏病
  3. 使用 TensorFlow 构建“正式”logit 模型

你可以用我的演练 Jupyter 笔记本和我的 GitHub learning-repo 中的 Python 脚本文件来遵循这篇文章中的代码。

2.分解逻辑回归

逻辑回归模型为输入数据集创建概率标签。这些标签通常是二元的(是/否)。

让我们通过一个例子来突出逻辑回归的主要方面,然后我们将开始我们的深入研究:

想象一下,我们有一个 logit 模型,它被训练来预测某人是否患有糖尿病。模型的输入数据是一个人的年龄身高体重血糖。为了进行预测,模型将使用逻辑函数转换这些输入数据。该函数的输出将是在 01 之间的概率标签。这个标签离 1 越近,模型对这个人有糖尿病的把握就越大,反之亦然。

重要的是:为了创建分类标签,我们的糖尿病 logit 模型首先必须学习如何衡量每条输入数据的重要性。为了预测糖尿病,很可能某人的血糖比其身高。这种学习使用一组标记的测试数据和梯度下降来进行。学习到的信息以逻辑函数中使用的权重偏差参数值的形式存储在模型中。

这个例子提供了逻辑回归模型做什么以及它们如何工作的卫星视图概要。我们现在准备好进行深潜了。

为了开始我们的深入探讨,让我们来分解逻辑回归的核心部分:逻辑函数。

我们将使用 NumPy 从头开始构建自己的 logit 模型,而不仅仅是从阅读中学习。这将是模型的轮廓:

在第 2.1 节和第 2.2 节中,我们将实现线性和 sigmoid 变换函数。

2.3 中,我们将定义交叉熵成本函数,以告知模型其预测何时为“好”和“坏”。在第 2.4 节中,我们将通过梯度下降帮助模型学习其参数。

最后,在第 2.5 节中,我们将把所有这些功能联系在一起。

2.1 线性变换

正如我们在上面看到的,逻辑函数首先使用其学习到的参数将线性变换应用于输入数据:权重和偏差

权重 ( W )参数表示每条输入数据对分类的重要性。个体重量越接近 0 ,相应的数据对分类越不重要。权重向量和输入数据 X 的点积将数据展平成一个标量,我们可以将它放在一个数轴上。

例如,如果我们试图根据一个人的身高和醒着的时间来预测他是否疲劳,那么这个人的身高权重将非常接近于零。

bias(b)参数用于沿该行的判定边界( 0 )移动该标量。

让我们想象一下逻辑函数的线性分量如何使用其学习到的权重和偏差来转换来自 UCI 心脏病数据集的输入数据。

我们现在准备开始填充模型的函数。首先,我们需要用它的权重偏差参数初始化我们的模型。权重参数将是一个(n, 1)形状的数组,其中n等于输入数据中的特征数量。偏置参数是一个标量。两个参数都将被初始化为 0。

接下来,我们可以填充函数来计算逻辑函数的线性部分。

2.2 乙状结肠激活

逻辑模型通过将 sigmoid 函数应用于逻辑函数线性变换的输出数据来创建概率标签( ŷ )。sigmoid 函数对于从输入数据创建概率很有用,因为它压缩输入数据以产生介于 01 之间的值。

sigmoid 函数是 logit 函数的逆函数,因此得名逻辑回归。

为了从 sigmoid 函数的输出中创建二进制标签,我们将决策边界定义为 0.5 。这意味着如果 ŷ ≥ 0.5 ,我们说标号是,当 ŷ < 0.5 时,我们说标号是

让我们想象一下 sigmoid 函数是如何将输入数据从逻辑函数的线性分量转换而来的。

现在,让我们在模型中实现这个函数。

2.3 交叉熵成本函数

为了教会我们的模型如何优化其 权重偏差 参数,我们将输入训练数据。然而,为了让模型学习最优参数,它必须知道如何判断其参数在生成概率标签方面是“好”还是“坏”。

这个“良好”因子,或者概率标签和真实标签之间的差异,被称为单个样本的损失。我们在操作上说,如果参数在预测标签时表现不佳,损失应该是高的,如果表现良好,损失应该是低的。

然后对训练数据上的损失进行平均,以产生成本

逻辑回归采用的函数是交叉熵成本函数。在下面的函数中, Y 是地面真实标签, A 是我们的概率标签。

交叉熵代价函数

注意,该函数根据 y1 还是 0 而变化。

  • y = 1 时,该函数计算标签的 日志 。如果预测正确,则损失将为 0 (即 log(1) = 0 )。如果不正确,随着预测越来越接近 0 ,损失会越来越大。
  • y = 0 时,该函数从 y 中减去 1 ,然后计算标签的 log 。这种减法使正确预测的损耗保持为低而不正确预测的损耗保持为高

1 和 0 地面真实标签的交叉熵情况

现在让我们填充函数来计算输入数据数组的交叉熵成本。

2.4 梯度下降

既然我们可以计算模型的成本,我们必须使用成本通过梯度下降来“调整”模型的参数。如果你需要梯度下降的复习,看看我的 分解:梯度下降 帖子。

让我们创建一个假的场景:想象我们正在训练一个模型来预测一个成年人是否累了。我们的伪模型只得到两个输入特征:heighthours spent awake。为了准确预测一个成年人是否累了,该模型可能应该为height特征开发一个非常小的权重,而为hours spent awake特征开发一个大得多的权重。

梯度下降将使这些参数下降它们的梯度,这样它们的新值将产生更小的成本。记住,梯度下降使函数的输出最小化。我们可以想象下面的例子。

梯度下降示例

为了计算成本函数 w . r . t .权重偏差 的梯度,我们必须实现链式规则。为了找到我们参数的梯度,我们将区分成本函数和 sigmoid 函数,以找到它们的乘积。然后我们将分别区分线性函数 w . r . t .权重偏差 函数。

让我们探索一下逻辑回归偏微分的直观证明:

让我们实现这些简化的等式来计算训练示例中每个参数的平均梯度。

2.5 拟合模型

最后,我们已经为我们的模型构建了所有必要的组件,所以现在我们需要集成它们。我们将创建一个与批次小型批次梯度下降兼容的函数。

  • 批量梯度下降中,每个训练样本用于更新模型的参数。
  • 小批量梯度下降中,选择训练样本的随机部分来更新参数。小批量选择在这里并不重要,但是当训练数据太大而不适合 GPU/RAM 时,它非常有用。

提醒一下,拟合模型是一个三步迭代过程:

  1. 权重偏差 对输入数据应用线性变换
  2. 应用非线性 sigmoid 变换来获取概率标签。
  3. 计算成本函数 w.r.t Wb 的梯度,并逐步降低这些参数的梯度。

让我们来构建函数!

3.UCI 心脏病数据集的实例学习

为了确保我们不只是孤立地创建一个模型,让我们用一个示例人类数据集来训练该模型。在临床健康的背景下,我们将训练的模型可以提高医生对患者健康风险的认识。

让我们通过 UCI 心脏病数据集的例子来学习。

数据集包含关于成年患者心脏和身体健康的 13 特征。每个样本还贴有标签,以表明受试者是否患有是否患有心脏病。

首先,我们将加载数据集,检查它是否缺少数据,并检查我们的要素列。重要的是,标签在这个数据集中是颠倒的(例如,1 =没有疾病,0 =疾病),所以我们必须解决这个问题。

Number of subjects: 303
Percentage of subjects diagnosed with heart disease:  45.54%
Number of NaN values in the dataset: 0

让我们也把特征形象化。我已经创建了自定义人物,但是在这里看到我的要点用 Seaborn 创建你自己的人物。

根据我们的检查,我们可以得出结论,没有明显的缺失特征。我们还可以看到,在几个特征中存在一些 stark 组分离,包括年龄(age)、运动诱发的心绞痛(exang)、胸痛(cp)和运动期间的心电图形状(oldpeak & slope)。这些数据将有助于训练 logit 模型!

为了结束这一部分,我们将完成数据集的准备。首先,我们将对数据进行 75/25 分割,以创建测试和训练集。然后我们将标准化*下面列出的连续特性。

to_standardize = ["age", "trestbps", "chol", "thalach", "oldpeak"]

*除非您正在运行某种形式的正则化,否则您不必对 logit 模型的数据进行标准化。我在这里这样做只是作为一种最佳实践。

4.训练和测试我们的分类器

现在,我们已经构建了模型并准备好了数据集,让我们训练模型来预测健康标签。

我们将实例化模型,用我们的x_trainy_train数据训练它,并用x_testy_test数据测试它。

Final model cost: 0.36
Model test prediction accuracy: 86.84%

这就是我们得到的结果:测试集的准确率为 86.8% 。这比 50%的随机几率要好得多,对于这么简单的模型,准确率还是挺高的。

为了更仔细地观察事物,让我们在模型的训练过程中可视化模型的特征。在第一行,我们可以看到模型在训练过程中的成本和准确性。然后在最下面一行,我们可以看到 权重偏差 参数在训练过程中是如何变化的(我最喜欢的部分!).

5.用 TensorFlow 实现逻辑回归

在现实世界中,当您需要使用模型时,最好不要构建自己的模型。相反,我们可以依赖功能强大、设计良好的开源软件包,如 TensorFlow、PyTorch 或 scikit-learn 来满足我们的 ML/DL 需求。

下面,我们来看看用 TensorFlow 建立一个 logit 模型,并将它的训练/测试结果与我们自己的进行比较是多么简单。我们将准备数据,创建一个具有 sigmoid 激活的单层单单元模型,并使用二元交叉熵损失函数编译它。最后,我们将拟合和评估模型。

Epoch 5000/5000
1/1 [==============================] - 0s 3ms/step - loss: 0.3464 - accuracy: 0.8634Test Set Accuracy:
1/1 [==============================] - 0s 191ms/step - loss: 0.3788 - accuracy: 0.8553
[0.3788422644138336, 0.8552631735801697]

由此我们可以看到,该模型的最终训练成本为 0.34(相比于我们的 0.36),测试集准确率为 85.5% ,与我们上面的结果非常相似。引擎盖下有一些小差异,但模型性能非常相似。

重要的是,TensorFlow 模型是用不到 25 行代码构建、训练和测试的,而我们在logit_model.py脚本中用了 200 多行代码。

6.摘要

在这篇文章中,我们已经探索了逻辑回归的各个方面。我们用 NumPy 从头开始构建一个模型。我们首先实现了线性和 sigmoid 转换,实现了二元交叉熵损失函数,并创建了一个拟合函数来用输入数据训练我们的模型。

为了理解逻辑回归的目的,我们然后在 UCI 心脏病数据集上训练我们的 NumPy 模型来预测患者的心脏病。我们发现这个简单的模型有 86%的预测准确率——非常令人印象深刻。

最后,在花时间学习和理解这些基础知识之后,我们看到了用 TensorFlow 构建 logit 模型是多么容易。

总之,逻辑回归是一种有用的预测分析算法。理解这个模型是研究深度学习道路上强有力的第一步。

好了,就这样了!如果你已经走了这么远,感谢你的阅读。我希望这篇文章对你了解逻辑回归的基本原理有所帮助。

7.注释和资源

以下是我最初学习逻辑回归时的几个问题。也许你也会对它们感兴趣!

Q1: 逻辑回归模型基本上不就是神经网络的单个单元吗?

A1: 有效,是的。我们可以将逻辑回归模型视为单层、单单元神经网络。 塞巴斯蒂安·拉什卡 对为什么会这样提供了一些很好的见解。许多神经网络使用 sigmoid 激活函数来生成单位输出,就像逻辑回归一样。

Q2: 我们所说的后勤是什么意思?

A2: 逻辑回归的“逻辑”来源于模型使用了 logit 函数的逆函数,即 sigmoid 函数。

Resources
- [UCI Heart Disease Dataset](https://www.google.com/search?client=safari&rls=en&q=uci+heart+disease+dataset&ie=UTF-8&oe=UTF-8)
- [Speech and Language Processing. Daniel Jurafsky & James H. Martin.](https://web.stanford.edu/~jurafsky/slp3/5.pdf) - [CS229 Lecture notes, Andrew Ng](https://see.stanford.edu/materials/aimlcs229/cs229-notes1.pdf)
- [Manim, 3Blue1Brown](https://github.com/3b1b/manim)

除特别注明外,所有图片均为作者所有。

打破世代诅咒:构建成功的数据团队

原文:https://towardsdatascience.com/breaking-the-generational-curse-structure-your-data-team-for-success-5b15e860b542

在这一点上,建立一个内部数据团队是一个很好的商业举措,这是一个普遍接受的事实。随着越来越多的领导者开始意识到,投资数据和分析团队不仅有助于衡量企业的进步和成功,还可以更广泛地利用数据来帮助领导层驾驭快速变化,并识别增长和运营效率的新机遇。

然而,许多数据专业人员受到其组织结构的束缚。希望专注于有意义的数据工作的分析师发现自己陷入了每天都必须构建下一个仪表板的苦差事。杰出的数据科学家应该专注于构建更好产品的核心工作,但却经常陷入响应特定业务请求的持续循环中。这些问题围绕着如何有效地导航和支持更大的组织,比缺乏基础设施和工具更有可能破坏数据团队的潜力。

那么,对于数据领导者来说,确保团队成功的最佳方式是什么呢?

如何在餐桌上找到自己的座位

当建立您的数据团队时,首先出现的问题之一是团队应该在组织中处于什么位置。我通常会看到一些结构和报告的标准模型,每种模型都有自己的优点和缺点:

  • 集中。在传统企业中,内部数据团队通常是集中的,向首席财务官、首席数据官甚至首席执行官等执行领导报告。在集中式团队中,数据主管通常拥有组织的所有数据和底层技术(例如 AI/ML 工程、数据工程、分析等)。)这种模式有一些优势,因为数据团队领导可以作为执行领导的得力助手,与企业的愿景和战略密切相关。然而,这种模式也有一些缺点。集中式数据团队通常被视为“服务团队”,其职能类似于组织其他部门的内部顾问。这加剧了“服务台”的动态,导致分析师不断地响应来自业务利益相关者的临时请求,而不是参与更有意义和战略性的数据工作。在这个模型中,数据团队的物理和逻辑分离有助于事务关系,如果数据团队被认为是不可接近的,业务的其余部分可能不倾向于使用他们的产品和服务。如果没有高管团队的“大力支持”,在集中模式下工作的数据团队可能会发现自己有抱负,但没有执行能力。
  • 嵌入式。在这种模式下,各个业务部门雇佣自己的一线数据专家来支持他们的领域。例如,销售团队可能有一个分析师跟踪他们的渠道,客户成功可能有其他人专门从事用户行为和保留率的工作。这种嵌入式方法可以在运营效率方面提供一些优势,并允许数据专业人员深入了解业务利益相关者的需求。另一方面,这也可能导致分析师对他们自己的特定业务部门目光短浅,而没有培养对更大图景的更深入理解,因为它与业务状态有关。它还会导致高度分散的数据堆栈,因为每个团队都使用自己的工具并负责维护自己的资产,通常需要一个单独的数据工程团队来管理数据管道和基础设施。
  • 杂交。许多组织已经采用了混合星型结构。在这种方法中,一个集中的团队,有时称为“卓越中心”,负责为整个组织构建工具并建立分析和数据科学流程,但数据组织同时维护 pod 以支持特定的业务部门。重要的是,这些都向同一个经理或主管汇报。功能单元为这些业务团队提供了一个数据指南针,并帮助他们坚持组织的更大路线图。pods 向数据主管汇报,所有团队成员作为同一组织的一部分,员工有机会获得培训、指导和明确的晋升途径。但他们也有虚线汇报给他们所服务的业务单位的领导。混合结构是最具创新性的,也可能是最有效的,因为它提供了紧密协作和可见性的混合,以及与集中式模型相关的高级控制。但这也是最复杂的组织工作。数据团队将各种业务单位视为客户,而 pod 的结构旨在提供适当级别的数据产品、服务和持续支持。pod 可以由任何头衔或角色组成(例如,数据产品经理、分析师、数据工程师、机器学习和人工智能专家等。),这取决于企业的需求。对于数据堆栈高度复杂并将数据视为战略资产的公司来说,这种模式被证明是最成功的。

尽管这些模型的方向和交付方式各不相同,但对于大多数组织来说,最合适的结构将反映您的数据团队成立的方式及其当前的成熟阶段。在采用集中化方法的企业中,通常会有一个具有前瞻性思维的领导者,他在某个时候决定投资于一项数据功能,并主动从头开始构建一个团队来实现它。嵌入式方法通常是随着公司发展的迫切需求而有机出现的,而不是有意的数据策略的结果。推荐的混合方法通常是从经验和对其他两种结构的限制的失望中发展而来的——它很少是一开始就选择的外形。

无论您选择哪种模式,有一个数据领导者来引领公司的数据计划都是至关重要的。在混合方法中,pod 中的数据领导者在每个职能领域拥有领导地位也很重要。

给数据领导者的 3 条建议

对于数据领导者来说,建立一个突出的声音是至关重要的(即使这个声音来自一个执行冠军)。一旦他们在管理层找到了自己的位置,这里有 4 个确保成功的小贴士:

  1. 对你实现的结构类型要有意识,因为它们都有权衡。为了确定前进的最佳路线,评估你内部已经拥有的人才,并以此为基础进行培养。现在就开始计划,这样你就可以继续沿着正确的方向发展你的组织结构。
  2. 纵向思考你应该如何发展你的团队结构。在开始您的数据之旅时,为了满足您的即时需求,只进行招聘和扩展似乎就足够了,但随着业务的发展,对业务复杂性的长远观点也很重要。
  3. 为您的数据目标提供安全支持。数据领导者不仅需要在管理层占有一席之地,还需要获得管理层对数据团队目标的支持。如果没有适当的组织支持,无论结构和计划构思得多么好,数据团队都无法取得成功。确定具有内在分析能力的内部拥护者,他们可能能够为您的团队和组织的数据使用进行宣传。

给数据专业人员的 3 条建议

展望未来,所有的商业领袖都需要在如何使用数据方面积累扎实的工作知识,以便与同行建立共识。为了有效地执行,组织还需要建立有效的数据团队来支持组织对数据的理解和使用。

对于试图在发展自己职业生涯的同时驾驭组织动态的年轻数据专业人员来说,了解这些模型的细微差别会带来巨大的好处。深入了解您的数据团队的当前结构可以帮助您回答一些重要问题,例如:

  1. 你在你的组织中被如何看待和重视?您的团队结构是否允许数据专业人员与其他部门发展真正的合作伙伴关系,或者您是否被孤立并被视为独立于业务团队的实体?
  2. 你是否有足够的时间和支持来进行广泛而有意义的分析,或者你是否是一个支持者,被要求完成随机的数字请求?
  3. 您如何鼓励您的组织围绕数据发展其思维和流程?就 1 年、3 年和 5 年计划而言,你有什么建议?

照片由斯蒂芬·孙平Unsplash 上拍摄

桥接数据操作和多操作

原文:https://towardsdatascience.com/bridging-dataops-mlops-301f010caf30

作为新数据源的 ML 模型推理

这是在第 36 届 NeurIPS 关于部署和监控机器学习系统的挑战的研讨会(【DMML】)、2022 年( pdf ) (SlidesLive 视频)上提交的论文“组合 AI 的 MLOps”的博客版本。

介绍

作为一名数据分析和人工智能/人工智能从业者,我仍然看到这两个生态系统之间的巨大差异。

当我第一次听说 DataOps 和 MLOps——这两个框架支持数据和 ML 管道时,我想知道为什么我们需要两个独立的框架?数据是人工智能/人工智能的一个关键组成部分(至少对于受监督的人工智能来说是这样,它可能占今天企业人工智能的 80%),尤其是在以数据为中心的人工智能时代。

对于门外汉来说,以数据为中心的人工智能将人工智能/建模从算法/建模部分重新聚焦到底层数据。这是基于一个假设,即一个好的算法或模型能够从训练数据集中挤出的“理解”量是有限的。因此,提高模型准确性的推荐方法是关注训练数据集:收集更多数据、添加合成(外部)数据、提高数据质量、尝试新颖的数据转换等等。

因此,问题仍然是为什么我们需要分别考虑数据操作和 m 操作,或者数据操作是 m 操作的一部分,嵌入在 m 操作中?让我们投入进去,希望我们不会以第三个数据操作框架结束— 请提出一个更好的流行词,你永远不知道你会出名 -😃

集成 BI 和 ML

理想的世界应该是下图这样的:

图 1:统一的 BI & AI/ML 管道(图片由作者提供)

结构化和非结构化的源数据被吸收到青铜层,在那里被净化和标准化到银层,进一步建模和转换到金层。数据现在准备好供两个 BI 报告工具& ML 管道使用。

然而,在现实中,我们看到这种管理/处理的数据被移动到另一个位置,例如云存储桶或另一个数据湖,在那里它被进一步转换为 ML 培训和部署的一部分。

所以图 1。在企业环境中,类似于图 2(下图):

图 DataOps 和 MLOps 管道中的数据处理(图片由作者提供)

MLOps 的数据(预处理)部分集中于将数据从源移动到 ML 模型,而不一定包括模型如何对数据本身执行。这通常包括一系列支持学习算法的转换。例如,数据科学家可能选择构建线性回归管道或探索性因素分析管道来支持 ML 模型。

ML 训练和验证( 链接 )需要执行比传统 ETL 工具支持的功能更复杂的功能。在复杂的数据处理、聚合和回归中经常会出现这种情况。这里推荐的方法是用有向无环图(DAG)流来补充数据处理策略。

与 BI 中更线性的数据流相比,DAG 流支持用于数据路由、统计转换和系统逻辑的可扩展有向图。像 Apache Airflow 这样的工具支持与 DAG 流相关联的创作、管理和维护,然后可以以编程方式创作 DAG 流,以与 ETL 管道集成。

不用说,这导致了冗余和 DataOps 和 MLOps 管道的碎片。可以公平地说,今天的 DataOps 更多地与 BI/结构化分析相关,而 MLOps 通过嵌入数据(预)处理来解决整个 ML 管道。

工具/平台供应商已经开始朝着这个方向努力,我们已经看到了一些解决这个问题的初始产品。雪花最近宣布 Snowpark Python API 允许 ML 模型在雪花内训练和部署,Snowpark 允许数据科学家使用 Python(而不是用 SQL 写代码)。

谷歌云平台(GCP)提供了 BigQuery ML ,这是一个 GCP 工具,允许在 GCP 的数据仓库环境中纯粹使用 SQL 来训练 ML 模型。同样, AWS 红移数据 API 让任何用 Python 编写的应用都可以轻松地与红移交互。这允许 SageMaker 笔记本连接到 Redshift 集群,并在 Python 中运行数据 API 命令。就地分析提供了一种有效的方法,可将数据从 AWS 的 DWH 直接导入笔记本电脑。

作为新数据源的 ML 模型推理

在本节中,我们考虑 MLOps 管道的另一个缺失的“数据”方面,其中部署的 ML 模型生成新数据,充当数据源

让我们考虑一个合成 AI 场景(图 3):

图 3:合成人工智能场景(图片由作者提供)

考虑消费电子产品供应商的(在线)维修服务。该服务包括一个计算机视觉(CV)模型,该模型能够根据受损产品的快照评估所需的维修。如果用户对报价满意,则服务被转移到聊天机器人,聊天机器人与用户对话以捕捉处理请求所需的附加细节,例如损坏细节、用户名、联系细节等。

将来,当供应商寻求开发产品推荐服务时,会考虑维修服务。由维修服务收集的数据:用户拥有的产品的状态(由 CV 评估模型收集)以及他们的人口统计数据(由聊天机器人收集)——为推荐服务提供额外的标记训练数据。

我们继续组合场景,其中供应商进一步希望开发一个计算机视觉(CV)支持的制造缺陷检测服务。回想一下,维修服务已经标记了受损产品的图像,因此在这里也可以利用它们。标记的图像还可以作为反馈回路提供给维修服务— 以改进其底层 CV 模型。

总的来说,由部署的 ML 模型做出的推断可以作为反馈循环来提供,以增强部署的模型的现有训练数据集,或者作为新模型的训练数据集。这导致了一个场景,其中部署的 ML 模型生成新数据——充当 MLOps 管道的数据源。

图 4(下面)示出了扩展的 MLOps 流水线,其中(部署的)ML 模型推理被集成为(新的)数据源。合成数据,即合成生成的与原始训练数据集非常相似的数据(基于生成神经网络),也可以被认为是类似的附加数据源。

图 4:(部署的)作为附加数据源的 ML 模型推理(作者图片)

参考

弥合评估差距

原文:https://towardsdatascience.com/bridging-the-evaluation-gap-5e8af3e16ff0

使用在线整数序列百科全书(OEIS)作为数论领域的数据科学先知

由 freepik 创作。

正如之前讨论的这里(和这里这里)我最近很享受使用数据科学的工具探索数论领域。由于我不是数论专家,这迫使我面对一个有趣的、具有挑战性的、熟悉的问题,即:

我如何评价我发现的东西?

我如何知道我是否在正确的轨道上?作为一名数据科学家,我可以生成和分析数据,拟合模型,确定有趣的关系,甚至做出预测,但鉴于我在数论领域的有限知识,我如何判断产生的结果是否有意义或新颖?在这篇短文中,我将讨论我是如何处理这个问题的,以及我已经确定的解决方案。

他的评估挑战对我来说并不陌生,也不是数论领域独有的。在我的日常工作中,作为一名在人工智能和机器学习领域工作的学者,确定如何客观地评估一个新算法或一个已学习的模型,是每个研究项目的关键组成部分。这通常意味着确定一个客观的基础数据来源,以评估模型预测的准确性。我们经常使用现有的基准数据集进行训练和测试。例如,在推荐系统领域, MovieLens 数据集是一个常见的选择。它包含数十万用户的数百万电影评级,并且使用数据集的训练子集构建的模型的评级预测可以在单独的测试子集上进行评估,例如通过将测试用户的预测评级与其实际评级进行比较。在机器学习和信息检索研究的许多不同领域都使用了类似的方法。

数论的领域是相当不同的。作为纯数学领域,重点是严格证明而不是经验评估。然而,我正在做的这个词本质上是经验性的。我在数据中寻找新颖和有趣的模式,如果我确实发现了一些有趣的东西,那么,在适当的时候,其他人可能能够将这些模式和数据转化为正式的定理和严格的证明,但这不是我的核心优势。但是这给我留下了一个难题:当我识别新的模式时,我怎么才能知道它们是新颖的还是有趣的?我一直在探索的解决方案和我平时的做法很不一样。我没有使用基准数据集方法,而是决定依赖外部数论专家。具体来说,我正在使用在线整数序列百科全书(OEIS) 来扮演一个数论神谕的角色。

OEIS 是一个由尼尔·斯隆创建和维护的整数序列的在线数据库。目前,它包含超过 350,000 个整数序列,在数论和其他数学分支中发挥着关键作用。业余和职业数学家都对 OEIS 很感兴趣。它被文献广泛引用,并且方便地,OEIS 提供了一个搜索工具,允许用户从一组样本术语中识别匹配的序列。这意味着它可以作为一种类型的数论**甲骨文,这正是我所需要的。

在我目前的研究中,我采用了如下所示的工作流程。例如,由于我一直在探索倒数和素数的各种性质,我经常产生我需要评估的新序列。我的方法是将这些提交给 OEIS,以搜索任何现有的匹配。如果匹配序列已经存在,那么这意味着我的结果并不新颖,但这也表明我正处于有趣的数论领域——有趣得足以让别人在我之前记录下这一发现——作为一个现有的序列,它的 OEIS 条目通常提供有用的指针来指导我的探索,例如它在其他研究领域的使用。

产生的许多序列都是这种类型——也就是说,它们已经存在于 OEIS——但偶尔我会产生一个不在 OEIS 出现的序列。当这种情况发生时,会非常令人兴奋。这暗示我可能发现了新的东西,下一步是提交给 OEIS 进行审查。审查过程将提供一个进一步的验证步骤,并可能以其自身的权利贡献额外的想法和建议,并且最终如果序列被接受,那么我可以满意地认为我对数论领域有所贡献。

JupyterLab 和 OEIS 之间的工作流程示例。图片作者。

为了以编程方式支持上面的工作流,下面的代码在 Python 中实现了两个有用的 OEIS 助手函数:一个是通过 id 查找序列,另一个是通过搜索匹配一组样本术语的序列,查找序列。这使得在不离开您的数据科学环境(在我的例子中是 Jupyter 笔记本)的情况下,很容易检查术语样本是否作为序列存在于 OEIS 中,这极大地增强了工作流。此外,如果对于给定的一组术语找到了匹配,那么通过返回匹配序列的摘要(id、url、描述和术语),就可以更容易地了解匹配序列的类型,并且它们提供的提示通常会指导我的后续步骤。

OEIS 查找/搜索代码示例。作者代码。

在最近的一篇文章中,我通过的方式讨论了对倒数的分析——形式为 1/d 的分数——以及它们的十进制展开的重复部分(重复)的长度(周期);比如 1/7 = 0.142857142857…所以它的周期( k )是 6,因为它的重复包含 6 个重复的数字(142857)。质数的倒数——形式为 1/p 的倒数,其中 p 是质数——的一个有趣性质是,它们总是服从等式n * k = p-1;换句话说, 1/p ( k )的周期是 p-1 的约数。

虽然这是一个众所周知的结果,但我对所有倒数 1/d ( 1≤d≤1,000,000 )的分析表明,虽然这个性质并不适用于所有的复合(非质数)倒数,但它适用于某些倒数。例如,1/33 有一个十进制扩展 0.030303…,所以它的重复数是 03,周期是 2,2 被 33–1 整除。再比如 1/148,展开为 0.00675675…有 675 的重复数,周期为 3,3 被 148–1 整除。还有一些复合材料也具有这种特性:

*33, 55, 91, 99, 148, 165, 175, 246, 259, 275, 325, 370, 385,...*

这个复合倒数序列是新奇的还是值得注意的?作为一个题外话,找到一个合数序列与素数共享一个属性总是很有趣的,特别是如果这个属性在合数中很少见的话。这样的数字被称为 伪素数 ,根据它们与素数共有的性质,有几种不同的类型。我的数据分析是不是把我带到了一种新型的伪素数?

当我在 OEIS 搜索这些术语时,我发现以前没有记录过这样的序列,进一步的网络搜索也没有找到任何关于它的明显讨论。我相当自信地发现了一个新的序列,我把它作为一个新条目提交给了 OEIS,果然,经过几天的审查,它被接受并批准为一个新的序列( A351396 ),这对我来说是第一次。

“合成数字 d,使得 1/d 的十进制展开的周期 k > 1 并除以 d-1”——a 351396

与此同时,我还考虑了这一序列的几个新变体——例如,通过排除与早期术语具有相同重复或相同时期的术语——这两个术语都不在 OEIS,并且都在审查中。

这篇文章的目的是解释我是如何思考我在数论中的经验探索的。数论是一个很有吸引力的应用领域,因为它很容易获得——高中数学足以开始,至少在一段时间内——还因为数据集和挑战性问题的可用性。然而,由于我不是该领域的专家,对我来说判断我是否在正确的轨道上是一个挑战:一个评估差距的典型例子。

我已经描述了一个实用的方法,我已经成功地使用它来弥补这个特殊版本的评估差距,通过使用 OEIS 作为一个数论先知。虽然 OEIS 不是万灵药,但它仍然可以提供非常有用的指导,尽管 OEIS 不提供传统的 API,但它相对简单地实现了序列搜索的自动化,从而消除了一些在典型的数据科学工作流中可能存在的摩擦。

弥合人工智能和人工通用智能之间的鸿沟

原文:https://towardsdatascience.com/bridging-the-gap-between-artificial-intelligence-and-artificial-general-intelligence-a-ten-e77c3084f9f7

类人智能的十诫框架

作者:Ananta Nair(1,2) 和 Farnoush Banaei-Kashani(1)
1。科罗拉多大学丹佛分校,2。戴尔技术公司

Gert RDA valasevi it 在 Unsplash 上拍摄的照片

介绍

虽然人工智能(AI)的概念似乎是一个未来的前景,但创造人工智能的愿望长期以来一直是人类的愿望。追溯到大约公元前 700 年的古希腊神话是第一个机器人塔罗斯。塔洛斯是赫菲斯托斯神创造的,用来守卫克里特岛,向进犯的船只投掷石块。有一天,一艘船靠近了这个岛,这个岛,这个庞然大物自动机不知道,将成为他最大的挑战。为了逃离机器,船上的女巫美狄亚设计了一个巧妙的计划,并向机器人提出了一个交易;赐予他永生作为移除他唯一的螺栓的回报。令人惊讶的是,这一提议引起了塔罗斯的共鸣,他还没有掌握自己的本性,也不理解自己对诸如永生之类的人类欲望的渴望。虽然这个故事以这个巨人的悲剧告终,但它确实表达了人类长期以来对创造智能的渴望和恐惧,以及人和机器之间的模糊界限,这是一个今天日益突出的挑战。

从古代世界的机械玩具到科幻小说中若隐若现的反乌托邦世界末日场景,人工智能的创造和发展长期以来一直在人类的脑海中。尽管许多人会争辩说这个领域已经经历了许多爆发和萧条,但过去十年已经取得了迄今为止最显著的进步。这些进步很大程度上来自于深度神经网络,这些网络已经成为视觉、自然语言处理和强化学习等领域的专家(Brown 等人,2020,Ramesh 等人,2022,Chowdhery 等人,2022,Schrittwieser 等人,2020,Ye 等人,2021,Baker 等人,2019,Arnab 等人,2021)。它们的应用范围很广,商业和实际部署似乎永无止境。面部识别等应用程序(Balaban 等人,2015 年)在我们的手机和日常生活中找到了共同的位置;像 Alexa 这样的虚拟助手是从 IBM 的鞋盒(Soofastaei,2021)迈出的重要一步,它只能识别 16 个单词和数字;在线翻译器能够在任意两种或多种语言之间进行精确翻译(Fan 等人,2021),强化学习代理在棋盘和视频游戏等一系列复杂任务上实现了人类和超人的性能(Schrittwieser 等人,2020,Silver 等人,2018,Berner 等人,2019,Vinyals 等人,2019)。即使在学术界,这些工具也在一系列不同的努力中取得了重大突破,包括天气预测,蛋白质解折叠,心理健康,医学,机器人学和天体物理学(Ravuri 等人,2021,AlQuraishi,2019,Su 等人。al,2020,Lusk 等人,2021,Gillenwater 等人,2021,Pierson & Gashler,2017,Huerta 等人,2019,George & Huerta,2018)。

无可争议的是,这些事业中的每一项都因其独特的成就而值得称赞。然而,要达到类似于自然界的一般智能,这个领域还有许多工作要做。深度网络与 GPU 的进步合作,加速了数据处理,以掌握模式识别和其他基于统计的学习,监督和基于规则的学习,无监督和训练测试泛化,以及强化和多智能体学习(朱,2005 年,卡鲁阿纳和尼古列斯库-米齐尔,2006 年,萨顿和,2018 年,O'Reilly 等人,2021 年,莫利克等人,2020 年,贝克等人,2019 年,Stooke 等人,2021 年)。这些巧妙的数学算法和数据密集型处理技术通过将稀疏问题空间转换为密集采样,其分布可以被积分或过度拟合,从而在理想条件下取得巨大成功。这些网络甚至可以进一步转移到分布外学习,通过整合训练和测试集来实现这一点(Vogelstein 等人,2022)。然而,这种方法的一个常见副作用和日益严重的问题是创建了数据敏感的黑盒,这些黑盒难以在其明确定义的参数化之外进行归纳(Lake 等人,2017 年,Geirhos 等人,2020 年)。这些挑战使当前的人工智能工具成为伟大的优化器,但它们仍然达不到传统的智能。

相比之下,自然智能是进化工程的一个例外,人脑被认为是首要的例子。鉴于深度网络的最新成功但存在很大的局限性,人工智能领域现在比以往任何时候都更多地在神经网络能够做的事情和自然智能能够做的事情之间进行比较和比较(Vogelstein 等人,2022 年,Silver 等人,2021 年,Richards 等人,2019 年)。与人工智能不同,大脑不会在它试图最大化其奖励功能的绝望努力中,在每一项任务上展示或努力获得优异的表现。相反,它创造了卓越的通用学习者,这些学习者能够概括他们的抽象表现和技能,从而快速轻松地学习任何任务。这种方法不会在所有任务中产生卓越的总体表现,而是通过目标驱动的学习来调整行为,以创建一个只擅长高价值目标的系统,而其他任务的成功概率更低。这种类型的学习针对特定性能进行优化,同时留下足够的计算资源来充分执行所有任务。例如:运动员和士兵可能擅长需要场景整合、策略和身体或反射能力的任务,但是科学家不需要在这些领域中表现出色,而是可能在逻辑和推理上表现出色,或者在微调的运动动作上表现出色。虽然每个职业都可以学习其他职业的技能,但人们会根据与他们的目标一致的高价值目标来优先考虑他们的表现。

我们相信,这种基于创建一般目标导向的学习者而不是过度训练的特定任务代理的最大化架构学习的方法是更智能系统出现的关键。在本文中,我们使用大脑作为灵感,来识别我们认为对高阶认知至关重要的关键属性。介绍的下一部分将剖析大脑功能的组成部分,这些部分可以解决当前人工智能工具中存在的显著限制。鉴于导言的最后一节提供了框架的总体总结。接下来,第二部分阐述了我们认为导致智力的大脑功能的关键特性。这些被认为是十诫,我们相信智慧不是来自一个单独的戒律,而是来自一个包罗万象的系统,它的总和比它的一部分更强大。最后,第三部分阐述了如何将这些戒律引入到人工智能系统开发的框架中,从统计优势过渡到一般智能。虽然本文主要讨论的是可能导致类似于自然智能的自主人工智能的组件组装,但我们相信,在最终目标不是一个无所不包的系统的情况下,个人戒律本身有利于改进无数的人工智能系统和工具。通过戒律和框架的介绍,我们相信模型可以根据需要放大或缩小。

1.1 把大脑逗得散架;智力的悖论

许多深度学习模型在它们经过强化训练的任务上已经超过了人类的表现(Schrittwieser 等人,2020 年,Silver 等人,2018 年,Berner 等人,2019 年,Vinyals 等人,2019 年)。这导致一些人甚至认为,这些模型在抽象世界和执行战略前瞻的方式上类似于人类(克罗斯等人,2021 年,巴克纳,2018 年)。尽管这可能是真的,也可能不是真的,但可以同意的是,存在一个显著的相当大的限制列表,阻止人工智能工具展示使人类成为例外的一般学习者的流动智能。虽然人工智能存在许多问题,但我们认为最显著的局限性是:1)训练时间长(Schrittwieser 等人,2020,Chowdhery 等人,2022,Berner 等人,2019);2)无力绘制新的路径来更好地指导学习;以及 3)无法概括到新的或日益复杂、不确定和快速变化的领域(Poggio 等人,2019 年,Geirhos 等人,2020 年)。学习和保留特定任务数据的方案使深度网络如此成功,但也限制了它,因为它迫使网络使用计算密集型数据驱动的白板方法,而不是允许模型建立在它们所知道的基础上。这种僵化的问题不仅导致了长时间的训练,而且在复杂的领域中也导致了对这些网络的可信度和可靠性以及他们对所承担的任务的真正理解的令人生畏的调查(Geirhos 等人,2020 年,Hubinger 等人,2019 年,Koch 等人,2021 年)。

另一方面,人们相信自然智能,无论是人类还是动物,要么与生俱来,要么很快发展出先天知识,他们可以利用这些知识来建立越来越复杂的环境层次表示,并随着时间的推移而展开。尽管这种知识是什么,以及它是如何被进化编码的仍有争议,但它表明,自然智能并不遵循白板或白板方法(Wellman & Gelman,1992 年,Lake 等人,2017 年,Velickovic 等人,2021 年,Silva & Gombolay,2021 年)。此外,越来越多的人认为,大脑通过将世界分解成最小的组成部分来接受世界,尽管神经科学家不确定输入是如何处理以创建内部模型的。众所周知,这些最小的组件或概念通常被称为(van Kesteren 等人,2012;2017 年吉尔波亚&马莱特;van Kesteren & Meeter,2020 年)与日益增加的复杂性分层次地结合,以生成环境的内部模型。人们普遍假设,大脑能够通过将信息组织成抽象的整体梯度来做到这一点。这些梯度符合一个包含过程的关系记忆系统,如维持、门控、强化学习、记忆等。,不断地更新、存储、重组和回忆随着时间的推移而展开和加强的信息,以形成广义的结构知识(Whittington et al .,2019)。

智力的第二个支柱是将世界置于一个不断增加的复杂性和可操作的目标和子目标的关联框架中的能力(O'Reilly 等人,2014 年,O'Reilly,R. C .等人,1999 年,Reynolds & O'Reilly,2009 年,O'Reilly,2020 年)。正如 Hubinger 等人在 2019 年和 Koch 等人在 2021 年所争论的那样,当前的深度网络建立目标以分配给优化器,但是然后利用不同的模型来执行动作。这就产生了一个具有指定目标的优化器,然后这个优化器又优化了一个可以在现实世界中运行的模型。这种类型的架构不仅会导致创建者和被创建者之间的目标匹配问题,还会导致信任和可解释性的问题。例如:作者把一个代理人放在一个环境中,它必须找到并收集钥匙,用这些钥匙打开箱子并获得奖励。训练和测试环境之间的区别是对象的频率,训练集的箱子比钥匙多,而测试集的钥匙比箱子多。人们发现,环境中的这种简单差异足以迫使代理学习一种与预期完全不同的策略,即找到钥匙比打开箱子更有价值。这一结果的出现并不令人惊讶,因为代理人将钥匙视为最终目标,而不是子目标。因此,在测试中,代理不仅收集了比它可以使用的更多的密钥,而且还重复地在屏幕上显示的库存密钥区域上画圈。当部署在其训练集之外的环境中时,深度网络通常会看到这些类型的非预期策略,这导致了对深度网络输出的日益增长的关注和日益增长的怀疑。

另一方面,人类,甚至儿童,可以很容易地解决这个任务,因为他们使用策略的组合来决定他们的行动。当一个孩子被要求玩或观看另一个人玩视频游戏时,他能够仅基于几个学习实例来推断游戏的目的是什么,以及什么行为可以被认为是好的(奖励)或坏的(惩罚)。我们认为,幼儿的抽象水平很可能与训练有素的神经网络没有太大不同,即限制和定义抽象的复杂性和数据点可能具有相似的程度。例如:儿童将能够成功地识别他们与之互动的动物,但会像卷积神经网络一样与模糊的狗或猫的例子进行斗争。然而,儿童能够解决深层网络未能解决的任务的原因是由于以下技术的混合。虽然最初儿童可能有很高的错误率,但是他们能够通过以下方式快速学习:(1)利用学习技术的组合,例如由强化学习信号引导的指导、监督、自我监督、无监督、预测和试错学习,以形成他们环境的灵活表示,实现相同的结果,(2)利用相似性和不相似性作为基准,基于他们已经知道的对新的表示进行比较和分组, (3)询问探查性问题,例如如何、做什么和为什么确定状态转换,(4)建立和确定代理需要满足的目标和目的,(5)能够离线以新的和新颖的方式组合表示,以及(6)能够建立表示之间的因果关系。

我们认为,自然智能中出现的每一种学习策略都允许儿童在成年后采用他们所知道的抽象概念并构建复杂的模型。为了在一个例子中找到根源,让我们探索一个孩子将如何学习制作咖啡,或者在机器人学中经常被称为咖啡制作任务(Tsai 等人,2010)。儿童通过不断与他们的世界互动,发展灵活的运动和对环境的理解。他们不是从一种类型的训练中学习,例如有监督的或无监督的,而是利用不同训练技术和步骤的组合来达到相同的结果。这可能包括从指导中学习,观察别人,以及使用试错法。此外,通过利用多种但相互联系的形式如视觉、听觉、体感等形成的表征。有助于限制类别并形成更丰富的表示。对儿童来说,咖啡壶的表现包括视觉特征、其各种组件(如玻璃和塑料)的声音和感觉,以及赋予其容纳液体能力的形状和可拆卸顶部。我们相信表征的多模态表征的灵活性和丰富性对于允许更高水平的认知区域为世界构建复杂的表征和计划是至关重要的。

通过这个学习实现目标的过程,孩子会在内部或外部提出问题,以推理为什么以及如何执行特定的行动或导致特定的结果。这可能包括推理一个特定的物体是否足够大以容纳咖啡或水,以及应该使用什么样的步骤组合,首先装入咖啡或水。儿童进一步将环境的新表征与他们经历的过去表征进行对比。这可能意味着一些场景,例如将咖啡壶与其他盛液体的容器(如水瓶和水壶)进行对比,或者将它与他们过去见过或接触过的机器进行对比。儿童将使用这些表示以及指示的或自我施加的行动-结果配对来学习规则,这些规则可以形成更好更快的可操作计划。这种规则的一个例子可以包括将咖啡放在较小的容器中,而 12 杯咖啡需要较大容器中的水或 5 勺咖啡。我们相信,在任务的每一步中从现实世界中学习到的东西,将会产生一个比网络所能接收到的更加切实和有效的奖励信号。例如,如果孩子省略了添加咖啡的步骤或向机器中添加了过多的咖啡,他们将接收到比传统深度网络接收到的信号更明显的奖励或惩罚信号,从而促进更快的学习。这种学习既可以在线使用,以形成行动或结果之间的因果关系,也可以离线使用,在思考过程中模拟和简化行动,以在未来获得更快或更好的结果。

从神经学的角度来看,我们认为最大化学习的另一个优点,以及创造能够在大脑中跨任务域概括或转移的抽象概念的能力,是使用专门的专家。这些在结构上设计好的体系结构接受特定类型处理的特定输入,并产生在结构专家之间建立关联的功能输出。结构连接,顾名思义,是大脑中特定结构产生的连接,如灰质区域之间的白质纤维连接(Hagmann et al .,2008;伊图里亚-梅迪纳等人,2008 年;龚等,2009;Abdelnour,Dayan,Devinsky 等人,2018)。相反,功能连接与大脑区域之间的关系有关,通常不依赖于对潜在生物学的假设。功能信号是指随着时间的推移,大脑区域对的强度和激活。如果在记录的时间内区域的活动之间存在统计关系,则表明区域具有功能连接性。它可以被解释为两个或多个神经生理时间序列(如从 fMRI 或 EEG 获得的序列)之间的时间相关性和无向关联(Chang & Glover,2010;Abdelnour,Dayan,Devinsky 等人,2018)。如下所述,这些结构和功能表示可以跨由特定约束定义的级别以越来越大的复杂度进行分级处理。结构和功能连接在实现抽象和随后的大脑高级认知方面发挥着不同但相互作用的作用。我们认为,这种划分知识用于训练的方法是性能和泛化的关键优化器。

1.2 组装大脑;框架路线图

如上所述,除了确定自然智能的关键属性外,我们还提出了一个框架来容纳它们,即十诫。这个框架的一个定义性特征是,它不是从一个单独的戒律中汲取力量,而是从一个系统中汲取力量,这个系统将所有的组成部分结合起来,使之比其各个部分更加强大。从本质上讲,我们认为大脑的运作基于可以分离和计算的基本原则。然而,正是这些原则错综复杂的相互作用导致了智能,而智能是很难隔离和定义的。本文的目的是确定这些基本原则,并提出一种执行智能的计算方法,以通知智能和自主人工智能的下一波浪潮。

我们模仿大脑中的优化和认知控制,将信息组织成两个层次的专家回路:内部和外部。这在很大程度上受到了全球工作空间理论(Baars 1993,1994,1997,2002 和 2005)和认知控制层次理论(Cushman & Morris,2015,O'Reilly 等人,2020)的启发。这种层次结构驱使行为从一个共享系统中出现,在这个共享系统中,内部循环与处理和完成特定任务相关联。这可能包括学习如何制作咖啡或其他任务,如导航,语言或翻译,以及信息分析。而外环执行整合内环信息的功能,以实现更复杂和更长时间尺度的认知(O'Reilly 等人,2020)。例如,这可以包括将制作咖啡的过程简化为最有效的策略,将以前制作咖啡的知识转移到新的机器上,或者甚至将以前制作咖啡的知识转移到其他饮料如茶或热巧克力上。类似地,在其他任务中,它可以包括导航到不同的目标,确定它知道和不知道的语言之间的相似性以理解单词,甚至超越统计决定论,为信息分析提供上下文化和个性化。

为了简单起见,如图 1 所示的框架被分成三层,每个层有一定数量的专家。尽管为了说明的目的选择了具体的数字,但是我们相信可以有 n 个层,具有 nm 个专家。唯一的条件是内循环和外循环必须以相同的层数和每层相同的专家数相互模仿。这是因为外部循环是内部循环的模拟,只整合了内部循环中最显著的或奖励门控的信息。在大脑中,由于没有重复的区域,相同的大脑区域执行两个环路的角色。然而,为了更好地解释和透明,我们将循环复制到两个独立的实体中。诸如导致奖励或惩罚的门控表示以及由内部循环记录的环境中的显著事件或物体被传递到相应的外部循环专家和层。所有的任务都是如此,因此外部循环可以整合和巩固最相关的表示。外部循环的处理包括细化表示以开发更好的计划,重新组合表示以创建新的和新颖的动作,以及对内部循环施加自上而下的影响以改进实时处理。

对于这两个循环,每一层都接受许多专家的意见,这些专家是特殊的体系结构,具有最大限度地提高特定特性(例如:视觉、听觉、体感等)性能的属性。).来自结构专家模型的信息以公共语言进行处理和输出,该公共语言可以通过层中的横向连接进行集成和约束,以促进多模态表示。比如:约束一个咖啡机的形象,用冲泡咖啡的声音,形成一个更复杂的咖啡机品类。这种相同的处理可以应用于各种不同的领域,包括猫和狗的分类任务,其中狗或猫的图像受其声音和体感特征的限制,以形成更复杂的类别来识别动物。最终的公共语言输出是该类别的函数表示,并且包含跨多个结构专家的最终核心约束信息。如上所述,这可以包括由横向连接的专家确定的类别的复杂多模态表示。从第一层输出的功能信息被输入到第二层的结构专家中,他们反过来使用这些信息来创建更复杂的功能表示,以输入到第三层。该过程可以在层级的每个增加的级别上进一步重复。例如,第一级的处理可能导致形成多模态表示,而第二级的处理可能导致场景整合和动作建议,最后,第三级的处理将涉及基于预测和动作执行来评估动作。

内部和外部循环都以这种分层方式运行,仅有的两个区别是 1)形成的抽象级别和发生的处理的复杂性;以及 2)只有内部循环与真实世界交互,外部循环只能通过内部循环的自上而下的影响间接地这样做。当一个特定的行动-结果配对或一系列行动受到奖励,一个突出的或意想不到的事件发生,或给予一个大的惩罚时,系统通过门控信息进行学习,训练好的表征,训练坏的表征。然后,来自各个内部循环层的这些门控输入中的每一个被传递到相应的外部循环层,以便以更慢、更集成的方式保留、改进和重新组合表示。这个过程允许对动作进行分组,以创建更灵活的可概括的表示,这些表示可以在将来应用于相同或类似的任务。然后,外部循环可以使用这些表示对内部循环施加自上而下的影响,以更快地行动或使用新的行动组合。例如,如果经过反复试验,一系列动作成功地调制出咖啡,那么这个过程中涉及的所有表示都将被选通到外部循环中。然而,当内部循环重复任务时,外部循环将学习精简所有门控表示,以建议内部循环仅采取导致最快奖励的一组动作。

此外,为了更好地指导行为,框架的输出被结构化为目标导向的,被描述为全局 F(X) 函数,其中 X 被最大化用于特定目标。为了进一步调优系统,还使用了其他约束,如注意、维护和门控。该系统创建多个目标,每个目标被优化为特定的行为策略(例如如上所述制作咖啡,在饥饿或需要资源时获得食物或电池充电,以及在躲避天气因素时获得庇护)。这些目标中的每一个都通过使用注意力来关注最相关的信息,并通过门控和维护来鼓励系统保留该信息,从而最大化一系列导致奖励或惩罚的行动。然而,应该注意的是,不给予目标的优化和系统的外部循环完全控制,诸如注意力之类的约束可以重定向模型以关注更紧迫的目标,这些目标是时间敏感的或者可能威胁系统的整体健康的显著环境事件。例如在寻找食物时有捕食者出现,或者在煮咖啡时厨房里有火。

最后,可以被称为记忆的信息的学习、保持、提炼和重组由动态模型来表示。我们认为,与计算机科学不同,记忆不是一个静态的信息术语,而是一个动态的实体,它包含各种过程,如存储什么,训练和学习什么信息,以及如何随着时间的推移改变、重组和完善表示。在我们的框架中,每个专家都被表示为一个动态模型,其记忆或表示存储在专家内部的本地级别,而全局存储为跨一层和跨所有层的所有专家的累积。然而,与静态数据不同,这些存储器或表示在内环或外环中的在线或离线处理期间根据需要不断更新、改进或重新组合。最后,由于它们的统计能力,机器和深度学习技术被用作理解和优化这些动态模型的非线性的手段。

图 1: 十诫框架图

大脑功能的十诫

I)大脑使用在多种形式上受过训练的多名专家,这些专家在横向上相互影响

与传统的计算技术不同,大脑有一个非常丰富、复杂和多模态的学习环境。例如:人类不只是将狗的类别作为视觉表征来学习,而是一个多维阵列,包括多种感官,包括听觉(吠叫)、嗅觉(麝香味)、体感(皮毛感)等。这些表示每个都由独特的专家构建,该专家基于特殊的结构分析来处理信息(例如:通过听觉皮层对听觉输入进行傅立叶分析,或者通过视觉皮层对视觉输入进行边缘、颜色和纹理检测)。尽管这些不同形态的专家针对其特定角色进行了优化,但他们有能力横向影响彼此,以帮助微调表示,并鼓励专家之间的激活绑定到同一对象/类别(O'Reilly & Munakata,2000,Herd 等人,2006,Munakata 等人,2011)。

这种拥有多个专家的能力允许更好地定义输入、损失函数、处理和输出的生成,其中每个专家都专门用于特定类型的处理。这种架构还允许在网络输出的生成中有更多的可解释性和透明度。此外,让专家横向影响其他专家的能力允许创建更丰富的抽象,这些抽象被约束在多维模态之间,从而导致高度调整的局部和全局表示。受全球工作空间架构(Baars 1993、1994、1997、2002 和 2005)的启发,这种多模态技术已经开始被纳入深度网络,如拉德福德等人,2021 年,戈亚尔等人,2021 年,本吉奥,2017 年,以及德阿纳和库德,2021 年。此外,CNN 的横向连接(Pogodin 等人,2021 年)也被用来展示改进的性能。

II)大脑使用稀疏分布式表示和双向连接

进化的首要目标似乎是减少面对噪音时的不确定性。自然智能必须驾驭现实世界外部和大脑内部的噪音。大脑使用大量的噪声,神经元必须学会选择性地训练,以确保只有一组专门的神经元在阈值以上活跃,对线索做出反应(Bear 等人,2020)。虽然变得活跃的神经元是高度调谐的,但它们也是灵活的,一个或两个神经元在序列中失败不会导致一个人无法执行任务(罗尔斯&特雷韦斯,2011 年,拉德福德等人,2021 年)。

稀疏分布式表示是大脑用来确保对输入进行分类的多种不同方式同时有效的技术,例如跨专家。这些表征在大脑不同层次和区域的连续激活被认为是智能行为出现的主要驱动力(Beyeler 等人。al,2017,Nair,2021,Ahmad & Scheinkman,2019,Ahmad & Hawkins,2016)。此外,双向连接使用这些稀疏的分布式表示,让许多大脑区域的神经激活共同工作,以编码复杂的表示和抽象(O'Reilly 等人,2012 年)。

双向连接对于多重约束满足、注意力和吸引子动力学也是必不可少的。通过允许网络稳定,它允许创建一个稳定和干净的噪声输入表示(O'Reilly,Munakata,Frank 等人,2012)。然而,这种机制显然被下面讨论的学习动力进一步加强了。一些人工神经网络架构已经开始利用这些技术,如 Grewal,2021,Hunter 等人,2021。

大脑使用抽象概念

虽然我们对大脑实现飞跃的神经过程知之甚少,但许多人怀疑魔法在于它创造的抽象类型(van Kesteren 等人,2012;2017 年吉尔波亚&马莱特;范·凯斯特伦&迈特,2020)。大脑是一个优化的一般智能系统,它被认为能够通过创建灵活的知识结构来快速和动态地学习,这些知识结构可以以新的和新颖的方式进行组合、重组和应用。这些知识结构允许生物体通过有效地分解复杂的输入并将信息编码成许多小块或抽象的心理结构来摄取世界。这些抽象然后被内部处理以构建被解释的现实的内部模型。虽然对模型输入的进展没有很好的理解,但人们普遍假设大脑通过组织成抽象的全局梯度来做到这一点(Taylor 等人,2015;美素拉姆,1998 年;琼斯&鲍威尔,1970)。

这种将信息组织成抽象的全局梯度,并且在此基础上的学习操作可以被认为是连续吸引子模型(Whittington,Muller,Mark 等人,2019)。在这种框架中,随着信息的获取,吸引子状态通过错误驱动学习稳定为共同的吸引子状态,具有噪声输入模式的清理后的稳定表示(O'Reilly 等人,2012)。出现的稳定表征利用自组织学习来反过来构建知识结构和系统,从这些知识结构和系统中可以出现复杂的认知。这种跨层级的表示的建立允许信息从输入按比例增加到认知的顶点层,例如推理、意识和其他更高级认知的有形行为。此外,结构和功能连接都是重要的部分,它们在大脑中扮演不同但相互作用的角色,以实现抽象和随后的更高层次的认知(Nair,2021)。

虽然起源于神经科学,但抽象形成和吸引子动力学的假设对人工智能社区具有很大的影响(等人,2018,任等人,2019,,2018,Ilin 等人,2017,Ashok 等人,2020)。虽然还没有完全理解在深度神经网络中形成的抽象概念在何种程度上类似于大脑,但它确实是当前和未来研究的基础。

IV)大脑使用等级系统

长期以来,人们一直假设认知行为存在于一个层次结构中,在这个层次结构中,抽象通过增加维度级别来慢慢组合,从而达到更高层次的表示。(博特维尼克,2008;巴德雷&尼,2018;D'Mello 等人,2020 年)。根据包括 Taylor 等人(2015)在内的许多神经科学家的说法,金字塔结构的最低层代表视觉、听觉和体感等输入,而最高层代表意识、想象、推理和思维。

随着抽象表示在内循环和外循环的层次中建立起来,跨处理区域的信息最终被转换成一般化的知识结构。在这个层级中,特定的大脑区域或专家以不同的复杂程度和维度局部地容纳结构表征,而它们跨层的整合激活导致相应的专门化功能表征。这些表示可以被优化,以在内部循环中为手头的任务构造动作和计划,或者被馈送到外部循环中,以随着时间的推移创建甚至更复杂的高维度抽象,这可以创建长期目标、结果以及最终的世界模型。此外,该信息通常可用于优化内部循环和长期认知。这些局部和全局层中的每一层都有可能通过在不同分析级别上运行的约束满足来组织成一般化的知识结构。

虽然深度网络的当前技术水平确实利用了信息处理的层次结构(Murdock 等人,2016 年,Qi,2016 年,Kriegeskorte,2015 年),但据我们所知,它们不包括使用专家创建类似于大脑的多层分析。我们认为,这种缓慢建立的语境化专家知识赋予了大脑一些认知能力。

v)大脑有专门负责的区域

正如《戒律一》中所建议的,大脑学习创建多模态表征,这种表征可以被多种模态的多位专家所约束。尽管深度神经网络已经开始利用专门的模型进行多模态任务(拉德福德等人,2021 年)或强化学习中的多个头部(Schrittwieser 等人,2020 年,叶等人,2021 年),但大脑在结构层面上优化其输入方面要专业得多。举例来说,仅在视觉处理领域,大脑就有一个处理信息的专门区域层级。

例如:初级视觉皮层或 V1 等较低的大脑区域被优化来处理边缘。而像 V2 这样的地区获取这些信息,并对其进行更高层次的处理。这可以包括确定颜色、纹理和背景-前景检测。在这些层之外,信息被进一步特殊化,分为“什么”和“哪里”路径,并且信息基于类型跨路径分布。“什么”层级的顶部是代表完整对象的下颞叶皮层(IT),“哪里”层级的顶部包括基于诸如注意力、维持和奖励等约束来引导迅速扫视或眼球运动的区域。通过自上而下的影响,这些相同的约束被进一步施加到较低水平的“在哪里”路径上。我们认为,这种自上而下的影响导致约束(如关注、维护和奖励)以不同的方式体现在我们框架中不同的结构专家身上。

VI)大脑有需求,但也有目标、需要和欲望

自然世界中的生物是目标导向的,很可能源于进化本身。人类和动物都有需要,必须满足这些需要才能延长生存时间。当一个特定的需求被激活时,它被转化为一个目标,这个目标动员系统集合行动以获得一种特定的回报。虽然行为并不总是以目标为导向,如在探索或试错策略中所见,但即使在这些情况下,通过将突出的事件置于专注于目标的行动-结果对中,也可以学习到突出的事件,这些目标可以在以后利用。

另一方面,深度神经网络不会像人类一样将世界规划成可操作的目标,因为它们不会面临相同的环境和资源压力。虽然可以说深层网络有他们需要达到的目标,但通常他们的生存和健康并不直接依赖于它。人类和动物从他们周围的世界快速学习,不仅因为他们可以形成灵活的多模态表示,用于在世界上行动,而且因为他们学习的训练信号更有效。例如,吃错食物或不注意捕食者会对活的有机体产生可怕的后果,而深度神经网络会收到惩罚信号并重新开始游戏。由于这种生存偏见,大脑有大量的不动产用于处理和学习负面信息,并通过多巴胺途径预测奖励的消失(Mollick 等人,2020 年,Schechtman 等人,2010 年,Pignatelli & Beyeler,2019 年)。因此,这表明大脑被优化来微调学习以满足生存需求,无论是在持续实现相关目标还是在几次接触中从负面事件中学习。

虽然目标很重要,但它们不是绝对的。一个特定的目标可以优先指导行为,然而,如果一个不同的显著事件将注意力引向另一个目标,那么追求这个相同的目标可以在背景中维持。例如:如果动物正在寻找食物,饥饿的目标是指导行为以最大化食物,然而,如果一个突出的事件引导注意力,如附近捕食者的位置,食物的目标将保持为子目标,而逃离预测者的新目标将主导行为。在将完成一项复杂的任务分成更容易实现的子任务时,子目标也非常重要。

此外,与深度神经网络不同,除了目标,已知至少人类有需求、想要和欲望。我们认为这是制定奖励和实现时间目标的动机成分的表现。需求可以被认为是生存所必需的可操作的项目,一旦被激活,就会产生一个需要在短时间内实现的目标。另一方面,欲望是不直接与生存相关的需求,因此不会被迅速追求(例如:想要一块巧克力)。最后,愿望是更复杂的目标,需要满足多个子目标,对时间最不敏感(例如:想去托斯卡纳旅游)。这些需求和欲望很可能只在人类或可能具有更复杂认知能力的动物身上看到,以驱动系统获得新的或更好的奖励,这些奖励目前并不存在或对生存至关重要。据我们所知,这种类型的建模尚未在传统的深度网络中完成,仅在其他人工神经网络中部分完成(Herd 等人,2021 年,O'Reilly 等人,2010 年,O'Reilly 等人,2014 年,O'Reilly 等人,2020 年)。

VII)大脑不仅仅是强化学习

尽管强化学习可以说是大脑用来学习世界的一个不可或缺的重要部分,但它不是唯一用于学习的机制(萨顿和巴尔托,2018 年,

加里埃皮和拉。,2014,O'Reilly 等人,2021,Doll 等人,2009,Mollick 等人,2020,Liakoni 等人,2022)。传统的深度学习网络使用类似于大脑中发现的基底神经节和皮质纹状体和中皮质多巴胺系统的演员-评论家系统(Mollick 等人,2020 年,Herd 等人,2021 年,萨顿和巴尔托,2018 年,Schrittwieser 等人,2020 年,Silver 等人,2021 年)。在该系统中,行动者接受国家的输入并输出最佳行动(或政策),而批评家通过将其与国家价值函数进行比较来评估行动(萨顿和,2018,李,2017)。

虽然强化学习是大脑正在做的事情的一部分,但它是通过利用无数复杂的专门架构来实现的。例如:OFC 被认为负责刺激的情绪和动机值,ACC 对冲突和错误监控很重要,DLPFC 对复杂的高阶信息处理很重要,PFC 对整合来自这些区域的输入和与基底神经节环路协调以促进行动反应很重要(O'Reilly,2010,Droutman 等人,2015,Bear 等人,2020,O'Reilly 等人,2012)。与传统的深层网络相比,大脑具有更加复杂的层次和专门的架构,允许处理定义奖励的不同特征。这些专门区域通过四个核心机制被进一步驱动学习;1)在复杂性上分层构建的专用架构;2)自上而下的影响和约束条件的使用,例如关注、维护和奖励,这些约束条件以不同的方式在不同的级别和专家之间表现出来,以迫使系统改进其学习和输出;3)使用预测学习作为一种机制,基于系统所知道的来预测一个动作将具有的结果;以及 4)利用一系列不同类型的学习,例如监督的、自我监督的、非监督的、预测的、指导的、镜像的、试错法,来训练和定义它的奖励信号,以便它能够更好地理解世界。

我们相信,使用受注意力、维护和门控约束的组合学习的巨大好处导致利用不同的技术来约束系统,以形成相同动作结果的不同但重叠的表示。这有助于创建更灵活的、可以概括的表示。这一点在机器人学习中已有一定程度的体现(陈等,2021)。此外,将奖励训练作为实现特定目标的手段有助于情境化学习,以便不同的奖励可以被不同地看待,例如:在人类中,饥饿时吃食物的奖励与购买第一套房子的奖励是不同的。据我们所知,这还没有在深度网络中尝试过,但是我们相信这将对更好的情境化学习有很大的帮助。

VIII)大脑使用串行架构或串并行架构

人们认为,决策或复杂的认知在人脑中并不是并行运作的(Herd 等人,2021 年,O'Reilly 等人,2020 年,Hayden,2018 年,Hunt 等人,2018 年,Herd 等人,2022 年,已出版)。思考这个问题最直观的方式是,由于大脑有专门的结构或专家专门研究信息处理的特定组成部分,所有不同层次的专家不可能同时参与进来。相反,当信息输入大脑时,它会在多次迭代中依次展开。层次结构第一层中的大脑区域很可能学会在并行操作中处理输入,然后将合并的表示连续传递给第二层专家,依此类推。在这个过程中,信息由相关领域并行地进行系统处理,然后被串行通信,以创建更复杂的抽象、计划和内部模型。另一方面,传统的深度网络并行处理多个信息流,以最大化 GPU 架构。虽然他们有一些专业化,需要进一步进入模型的串行处理,但它与大脑有很大不同(金等,2020,邓,2011)。

据信,将信息编码到网络中的串行方式和特定层中专家之间的并行处理也是大脑如此适应复杂决策的原因。与此相似的是决策的有声思维协议(Herd 等人,2021 年,Herd 等人,2022 年出版)。为了完成一个复杂的面向目标的任务,我们通常把它分成更小的连续步骤,只处理与这些步骤相关的信息。比如:计划旅行的时候;第一步包括汇编一份运输选择清单;第二步包括在我们的时间限制内选择哪种运输方式;第三步是比较成本;第 4 步需要预订最佳选项(Herd 等人,2021)。有趣的是,这种相同的串并行处理方式也适用于学习新材料。例如:作为新手学习广义相对论时,第一步包括学习简单的概念,如什么是行星?什么是太阳?什么是太阳系?;步骤 2 利用这些信息输入来建立更复杂的假设和抽象,例如什么是轨道或什么阻止这些物体相互碰撞,即重力;步骤 3 阐述了空间、时间和时空的概念;第四步讲述了什么是广义相对论。

我们认为,随着时间的推移,随着信息的连续展开,这种分层次建立复杂表征的方式是高阶认知的一个重要组成部分。此外,能够在不同抽象层次内部查询表示的过程,使用诸如何时、如何和为什么的陈述,允许我们更好地指导我们的学习,以形成更好地提炼和巩固表示并引导注意力的因果推理。神经科学的一些研究进一步表明,动物记录在评估选项时发现了串并行架构的使用(Hunt et al,2018)。最后,尽管串行-并行架构对于学习来说是最佳的,但人们认为,通过在实践良好的任务中使用并行架构,大脑可以随着时间的推移而优化(Herd 等人,2021 年,Herd 等人,2022 年出版)。

大脑思考和推理

我们认为,人类和机器性能之间的一个重要区别因素来自思考和推理的能力。我们发现,幼儿很可能像当前的人工智能工具一样,开发简单的抽象概念和世界的内部模型,使它们只与他们训练的东西一样好。然而,在发展的过程中,儿童可以把他们所知道的东西,重新组合和提炼,以建立越来越复杂的成人对世界的描述和模型。我们相信这是可能的,因为人类有能力思考,或在他们的代表空间中漫步。人类和人工智能工具一样,也有计算或物理约束。例如,他们不能在世界上连续行动,例如,饥饿时无休止地寻找食物,口渴时无休止地寻找水,最终会耗尽他们所有的能量,导致死亡。相反,人类在精神上制定复杂的计划,然后选择他们应该在世界上执行哪些行动。这种在头脑中模拟世界的能力不仅允许我们组合我们已经学会应该配对在一起的表征,还允许我们组合新的表征。

就像一个神经网络可以用它的非策略来影响它的策略一样,人类也可以离线,结合表现来影响在线行为。在我们的框架中,当系统选择参与思考时,它可能会通过减少内环感觉机制(视觉、感觉、听觉等)的约束影响而离线,即较少的注意力、维护和门控被导向感觉输入。然而,内环低级感觉区域上的约束可以容易地响应于世界中高于阈值的显著刺激而被重新接合。通过减少对世界输入的关注,该系统可以将计算处理引向已经在系统的更高层次中形成的抽象。当系统在一个时间敏感的上下文中思考它正在解决的当前任务时,只有内部循环中的抽象是可用的。这包括与任务相关的抽象,以及来自外部循环的任何抽象,这些抽象先前已经被提炼以影响内部循环的行为。外环中的主动处理很像内环中的感觉区域,通过减少约束的影响可能会脱离。相反,当系统选择进行时间不敏感的思考或目标不敏感的思考时,通过减少对内循环或当前环境的关注,主动处理可能只发生在外循环中。然而,这种将处理限制在内循环或外循环的操作并不完美。当内部循环中注意力和其他约束没有被正确地施加时,系统将重新激活外部循环,并引入可能与任务不相关的新抽象,并对及时处理造成干扰。例如:在解决家庭作业问题时考虑午餐。

还应该注意的是,走神,即在没有目标强烈影响的情况下在表征空间中搜索,是可能的,因为所施加的约束是不完美的。当一个特定的任务是时间相关的,但是当前的抽象不能集中在一个解决方案上时,诸如注意力、门控和维护等约束不能对系统施加适当的支配。这导致系统在内部和外部循环表示之间徘徊。例如:当最后期限即将到来并且无法找到复杂问题的解决方案时,施加在内部循环上的约束会疲劳,并导致系统在两个循环中的表示空间中漫游,直到系统可以恢复并且可以再次正确施加约束。约束减弱的原因可能是由于系统缺乏收敛而导致的指数级资源消耗导致的计算疲劳。

这些心理模拟、记忆回忆和可能的走神机制都是默认模式网络的一种描述(Schacter et al .,2012,Zhou & Lei,2018)。一些研究还表明,这种相同的默认模式网络在睡眠期间也是活跃的(Horovitz 等人,2009 年,Sä mann 等人,2011 年,De Havas 等人,2012 年)。系统在睡眠期间离线或完全脱离的能力可能是必要的机制,允许信息的提炼和整合,以及抽象的灵活组合,从而导致更复杂的行为,如想象力、创造力、解决问题和概括(Rasch & Born,2013 年)。在走神期间,我们相信约束对内循环所有层面的影响都大大降低了。然而,另一方面,在睡眠期间,我们认为只有外环的较高层次是可用的,这就是为什么梦在最抽象的领域运作。最后,我们认为,当一项任务对时间不太敏感时,只有外循环在起作用。例如:当计划如何度过一个周末时,系统现在可以组合不同的和新的表示组合,而没有与任务相关的联系。

推理是思维的一个组成部分,它是一种机制,允许系统更好地指导和改进在表征空间中的搜索。我们相信推理是表达之间因果关系的公式化。虽然早期学习很可能是由简单的手段驱动的,如相似性和不相似性、模式匹配和强化学习,因为更复杂的机制如预测学习形成了,但系统很可能使用与目标相关的内部查询过程来将世界情境化并形成偶然的关系。这些内部查询过程将使用诸如“如何”、“为什么”和“何时”之类的语句作为假设测试行动-结果对、世界的变化以及行动与目标的相关性的手段。据我们所知,这样的思考和推理机制还没有在深度神经网络中被考虑或实现。然而,使用 SAT 解算器和想象力蒙特卡罗树搜索展开的硬编码推理外循环的示例已经实现(Ye 等人,202,Chen 等人,2019,Schrittwieser 等人,2020)。其他受生物学启发的模型包括 Russin 等人,2020 和 Russin 等人,2021。

x)大脑与其他大脑协同工作

**“人多力量大”早有说法。这同样适用于构建更好的想法,或者更聪明的文明。人们不会在内部架构之外孤立地学习,他们会从丰富的多模态环境和其他环境中学习(巴塞特&马特,2017)。人类有专门的镜像神经元来模仿周围世界中的个体所采取的行动。当我们考虑在世界上实施这些行动时,以及当我们实际行动或被指示行动时,这种通过模仿学习也传播相同的途径到火(Heyes,2010)。此外,除了合作动态,竞争的作用也在驱动系统更好更快地学习表征的压力方面发挥了重要作用(Pinto 等人,2017,Baker 等人,2019,Sanchez,2017)。我们相信在学习过程中融入社会因素将会创造出一个更好更快的系统。

最近,多代理学习的使用已被纳入深度学习网络,并取得了巨大的成功。最显著的例子是 Open AI 的捉迷藏代理,Deepminds XLAND 代理(Stooke 等人,2021,Baker 等人,2019),他们已经证明了一系列复杂的涌现行为。在机器人技术中,人类训练和人机团队以及竞争与合作实验已经证明了更快和更有效的学习(Chen et al .,2019,Pinto et al .,2017)。

组装十诫框架

如上所述,我们的框架提出了一种将十诫融入一个系统的方法,这个系统可以通过数学和计算来执行。戒律相互建立,导致行为的出现,这种行为比它的单个组成部分更有力量。然而,应该明确指出的是,该框架的目的并不是提出一个解决问题的绝对解决方案,而是启发未来的架构和讨论,这些架构和讨论也可以建立在这些优势的基础上。

如上所述,该框架由两个层次循环组成;一个内环对应于处理手头的任务,一个较慢的更具整合性的外环在较长的时间框架内收集信息(O'Reilly 等人,2020 年,Cushman & Morris,2015 年,Schneider & Logan,2006 年)。为了首先组装内部循环,层次结构中的每一层都由专家构建。如图 2a 和 2b 所示,三个专家网络 (x1,y1,z1) 协同工作,组装出第一级G1(x)=【a1(t),b1(t),C1(t)】的输出。尽管可以使用任意数量的专家,但是他们必须遵守三个定义规则;1)每个专家被设计成只完成一种类型的处理(例如:视觉处理、听觉分析等。),2)层中的专家必须横向连接,以及 3)来自每个专家的信息输出是可以在整个体系结构中表达的公共函数格式(即,全球通信语言)。

如图 2b 所示的三位专家 (x1,y1,z1) ,他们本身都是数据驱动的非自治动力系统【ẋ】=f(xtuβ )。每个专家都有自己的一套描述其动态系统的变量,其中 f 代表动态(即,给定一个状态,系统在下一个时间步如何变化);x代表状态, t 代表连续时间, u 代表控制输入, β 代表参数(Brin,& Stuck,2002)。对于每个动态模型, x,u,β 将是向量,其中, x 是系统的状态, β 是系统的不同多模态输入。这些输入可以是视觉系统的亮度、颜色和对比度,听觉系统的锐度或音量,以及躯体感觉系统的纹理、粗糙度和温度。在更高层次的分析中,如下所述,这些输入可能包括预测值、误差信号、发动机动作、建议计划或任何改变系统动态的因素。向量 u 将是对系统施加的不同控制,包括注意、维护和信息门控。这些控制对系统施加影响,并且我们相信随着时间的推移,系统将通过支配性的 f(x) 目标函数以及强化信号来学习。当网络收到对行动-结果对的奖励时,注意力将是一种对场景中相关显著刺激的紧急控制,这将影响记忆中信息的保持和门控。最后, 将是代表系统动态的微分方程(或向量场)。

**

图 2: a)表示一层内循环的框架,b)表示下面给出的一层内循环的数学解释。

令人满意的是,通过使用数据驱动的动态建模方法,该框架远离了传统的机器和深度学习架构。然而,我们认为,由于其统计能力,当前的深度和机器学习网络对于成功找到理解系统动态的可解释解决方案至关重要。输入系统的数据会因专家而异,但数据来自自治系统的假设是正确的。为了网络学习模型的传播,每个专家的数据将被组合成一个初始的无价值问题。这些深度学习技术的统计能力的最大好处应该是有助于确定未知的 f 函数,在混沌中转移,以及找到坐标变换或新的坐标系,使得变换使非线性动力学看起来是线性的。稀疏回归或库普曼分析等技术已经开始使用(布伦顿等人,2016 年&。

随着机器学习掌握了统计空间,这些方法将成为应对非线性动态模型带来的诸多挑战的主要工具。未知和非线性 f 函数、不确定性和混沌以及潜在和隐藏变量等挑战可以重新表述为优化问题,其中机器学习模型可以针对数据进行优化(Brunton 等人,2022 年,Lusch 等人,2018 年)。在我们的框架中,机器学习模型可以在本地实现以理解每个专家的动态,并且在全局确定框架的总体输出,即用于创建反馈控制的所有动态模型专家的集体系统。这将需要专家根据输入进行优化,并实时主动操纵系统的各个部分,将参数转换为执行器,执行器将模型转换为反馈控制回路。虽然这种方法将在下一代人工智能工具的开发中提出新的挑战,但我们相信它将导致更透明和可推广的网络。通过使用由专业专家组成的框架,可以更好地约束投入和产出,并开发专家专用的透明度工具。调试和再培训专家将比当前具有更复杂的输入、输出和集成结构的网络更简单。此外,由于动态模型了解系统的潜在动态,即系统为什么以及如何变化,我们认为它们应该更容易适应新的和未知的情况。已经表明,动态模型对未经测试的参数进行概括,例如牛顿第二运动定律(Brunton 等人,2016 年)。我们相信这些可概括的特性也应该适用于这个专家的集体框架。

当用矩阵符号表示时,特征值揭示了一个动态系统将随时间做什么,或者更好地作为这些系统的解决方案。系统通常是正的、负的和虚的特征值的组合。负值表示减少和衰减或稳定系统,正值表示增加和爆炸,或不稳定,虚值表示振荡或错误和冲突。这些负特征值可能表示随机活动的减少和网络上稳定吸引子动力学的形成,而正特征值可能表示随机活动的增加和混沌和不稳定吸引子表示的形成。最后,虚特征值的作用可能会延伸到系统某些部分的激活错误,这可能会导致不正确或延迟的回忆。这可能会导致活动无法使正确的吸引子状态完全激活,从而导致诸如记错类别名称或错误地回忆过去信息等情况。

第二种技术和特征值的使用是图论,我们认为这是理解抽象、吸引子状态和创建一个跨框架的全球语言的关键。因为机器和深度学习方法可以帮助提供对底层系统的分析,以及找到用于将非线性空间转换成线性空间的数据驱动解决方案。我们相信图论及其子成分谱图论可以用于确定和理解这些系统中的知识成分。如上所述,像大脑这样的动态系统是连续的吸引子模型,它们组织成可以不断更新和重组的全局梯度。随着信息的输入,这些吸引子状态稳定为共同的吸引子状态,这些吸引子状态使用学习来创建稳定的表示,随着时间的推移,可以建立知识结构和系统(O'Reilly 等人,2012 年,Nair,2021 年)。通过双向和横向连接产生的分布式表示是确保这可以跨多个地区或专家发生并推动智能行为出现的关键(Nair,2021,O'Reilly 等人,2012)。在我们的框架中,每个专家都将配备所需的层内和跨多层的横向和双向连接。

如图 2a 和 2b 所示,三个结构专家网络 (x1,y1,z1) 协同工作,处理各自的任务。这种结构信息然后将被转换成功能表示 g1(x) = [a1(t),b1(t),c1(t)] ,这是来自每个专家的最重要组件的整体图像。例如:如果三个专家中的每一个都接受了类别狗的视觉 (x1) 、听觉 (y1) 、体感成分 (z1) 的训练,那么系统的集体结构输出将是他们的动力学的总和,表示为h1(x)=【x1(t),y1(t),Z1(t)】。另一方面,功能表示将包含该类别专家之间最相关的特性,并提供可以在整个框架中交流的全局语言。专家 x1,y1,z1 的结构信息将以通用格式( a1,b1,c1 )输出,该格式可以跨层传递以约束和影响其他专家,即跨专家的横向影响将发生在这些通用语言表示上。例如:将狗的视觉(从专家 x1 获得,现在用全局可通信输出 a1 表示)与犬吠的听觉 ( 从专家 y1 获得,现在用全局可通信输出 b1 表示)和皮毛的体感表示 ( 从专家 z 1 获得,现在用全局可通信输出表示)通过横向连接,这些公共语言输出( a1、b1、c1 )通过创建复杂的多模态信息表示,影响专家个人处理和动态的整体画面的创建。此外,这些公共语言输出本身是非自治动态系统,其有助于层G1(x)=【a1(t),b1(t),C1(t)】的最终功能输出。

在大脑中,已经发现神经振荡是本征模式的线性叠加(Raj 等人,2020)。Abdelnour 等人,2018 年,证明了结构连接性和静息状态功能连接性通过拉普拉斯-本征结构相关。研究人员通过使用结构图的特征分解来预测结构和功能连接之间的关系,从而检验了这一假设。该特征向量全功能连接模型与健康的功能和结构人类数据以及非线性神经模拟进行了比较和验证。通过这一点,作者能够验证拉普拉斯特征向量可以在组结构水平上从独立成分分析预测功能网络。利用这些发现,我们相信使用解剖活动加权的区域间耦合,即结构专家,将影响神经结活动,以在我们的框架内显示相同的功能结果(Nair,2021)。因此,我们建议谱图理论可以用于隔离集群和约束基于专家内部和跨结构和功能关系的特征关系的抽象的神经集群。正如 Nair (2020)所假设的,通过将大脑可视化为一个网络,其中神经元充当通过加权连通性链接的节点,图论可以用于确定代数连通性。此外,对该网络的谱图论分析可以将连通性划分为其最小的子组件,从而产生具有高内部连通性和低外部连通性的抽象集群。这些集群可以表明信息是如何在结构专家和整个框架中被保留和操作的。可以进一步构建透明工具来理解抽象如何链接、更新、组合和重组在一起,从而更好地理解系统。

组装该框架的下一步如图 3 a 和 3 b 所示。可以将专家组织成分层结构,以便随着时间的推移对信息进行连续处理。尽管这里示出了三层,每层具有固定数量的专家,但是具有 nm 数量专家的 n 层可以跨层存在。拥有串行和分级信息流的重要性在于,在每一层都会发生不同级别的处理,这些处理会通知下一批专家,这些专家可以进一步构建越来越复杂的输入信息表示,以确定适当的输出。图 3 c 和 3 d 进一步展示并建立了上面给出的内环路第二层和第三层单层的数学解释。每一层专家的数学处理与第一层保持相同,直到处理完成并执行输出。当信息被第一层专家处理时,功能输出被用于通知第二层的下一组结构专家,他们对这些抽象执行更复杂的分析。第一层中产生的函数输出通过拉普拉斯特征变换被转换回结构输入,以传递给第二层的结构专家。类似地,这些结构专家执行特定的处理并输出公共语言表示,该公共语言表示被横向约束并作为该层的最终功能表示输出,以传递给第三层中的下一组专家。在我们的例子中,第三层是框架的最后一步。它同样执行处理的最后一步,并向外界输出。

**

图 3: a)表示扩展到两层的内部循环的框架,b)表示上面给出的扩展到两层的内部循环的数学解释,c)表示扩展到三层的内部循环的框架,d)表示上面给出的扩展到两层的内部循环的数学解释。

说详细点,如果第一层专家在对一只狗进行视觉( x1 )、听觉( y1 )、体感( z1 )分析。功能输出 g1(x) = [a1(t),b1(t),c1(t)] 将被转换回结构输入,然后该结构输入将被输入到第二层的相关专家。第二层处理将整合来自第一层的环境输入,以实现更高水平的处理。这可能包括诸如场景整合( x2 )、语言( y2 )以及运动动作和规划( z2 )等专家。类似于第一层,结构输入将被合并为 h2(x) = [x2(t),y2(t),z2(t)],并被转换为全球通用语言 a2,b2,c2 ,在此基础上横向连接可以操作以输出层 g2(x) = [a2(t),b2(t),C2(t)】的功能表示。类似地,第二层的功能性输出将被转换回结构性输入,并分别输入给第三层的相关专家。这一层将整合来自第二层的信息,以实现用于确定输出的最终抽象处理级别。这可能包括专家,如值预测( x3 )、值评估和误差( y3 )和电机执行( z3 )。这些专家将整合来自较低层抽象的信息,以执行一个使目标最大化的行动,或者总体的 f(x) 功能。目标将代表框架必须实现的一个主动需求,比如到达一个机器人的充电站。如先前在过去的层中计算的,合并的结构输入将被合并为 h3(x) = [x3(t),y3(t),z3(t)],并被转换成全球通用语言 a3,b3,c3 ,横向连接可以基于该语言操作以确定将生成输出的最终函数表示。

此外,由于所有层都具有双向连接性,第三层可以对第二层施加自上而下的影响,以重定向专家来编码新的或特定的信息,从而在下一时间步生成更好的输出。第二层同样可以在执行期间或之后对第一层施加自上而下的影响,以收集更好的信息。此外,如果收集的这种信息导致更好的动作-结果映射,场景中的相关对象被记住,或者被门控和保持,并且注意力在将来被引导到它们。这些相同的注意、维护和门控机制充当了强加在专家动力系统上的约束。这些约束中的每一个将在不同的抽象层次上不同地显现,例如在框架的较低层次上,它可以指导场景搜索,而在框架的较高层次上,它可以指导与过去的特定动作-结果配对相关联的奖励值的语义搜索。

应该注意的是,框架的每一层都不是建立在从皮层下到皮层脑区域的复杂性上,而是对应于抽象层次处理的建立。例如,用于感觉运动处理的层级中的第一层中的专家将包括诸如初级视觉皮层(V1)的较低层以及诸如下颞叶皮层(IT)的较高层。而第二层中的专家将包括辅助运动区(SMA)和前运动皮层等区域,辅助运动区提出内部产生的运动规划,前运动皮层负责运动控制的各个方面,包括运动的准备。这些区域将处理从第一层输入的场景信息,以计划在第三层评估的运动输出。基于第二层的信息,第三层将构建系统的最终功能输出。第三层可以包括大脑区域,如产生神经脉冲以执行运动的初级运动皮层,以及整合不同奖励值并通过基底神经节选择进行(作用于)和不进行(不作用于)哪些动作的前额叶皮层(PFC)。这种操作类似于混合专家方法,将从较低层次到较高抽象处理层次的专家进行生物组合。此外,尽管这一框架可能不是对大脑功能的完全准确的描述,但据信较高级别的大脑区域运作并处理来自较低级别的区域的日益复杂的抽象(Nair,2020,Richards 等人,2019)。这可以在人类发展中看到,因为儿童通过感觉运动区建立对环境的抽象,然后他们可以在皮层的更高区域使用这些抽象来实现更复杂的认知规划。随着环境抽象性的提高,更高层次的规划也在提高。

组装完内部回路后,我们接下来开始组装外部回路,即速度较慢的整合系统。如图 4 a、4b 和 4c 所示,内环中的每一层和专家对应于外环中的镜像层和专家。拥有两个独立的相同层次的串行处理循环的目的是创建一个辅助系统,该系统可以积累和吸收来自内部循环的相关信息,以实现更复杂和更长时间尺度的认知。因此,来自每个内部循环层的每个门控表示被传递到相应的外部循环层,在那里可以进行表示的细化、更新和重组。如前所述,尽管内循环和外循环都显示了三层,每层三个专家,但是在框架中每层可以有 n 个层和 nm 个专家。唯一存在的限制是内循环中的层数和每层的专家数必须与相应的外循环中的相匹配。尽管这种架构看起来有些矛盾和重复,但我们相信它可以产生一个更具普遍性、更易解释的框架,并体现高级认知能力的属性。此外,从神经科学的角度来看,重复的大脑区域并不存在于这种双环层次结构中。然而,我们认为相同的大脑区域同时扮演着内环和外环的角色。通过将框架分成多个副本,我们相信我们可以创建一个更易解释的表示框架,因为它与当前任务相关,并且它们是如何随着时间和学习而改进的。

组装后,内部循环处理世界的当前状态,并制定行动-结果表示,使特定或多个目标的回报最大化。内部循环积极参与、处理和行动世界,通过横向连接约束专家之间的表示,并通过自上而下和层间双向连接制定受控反馈。导致奖励的行动-结果表示被维护和选通,从而通过系统训练相关的权重和表示。虽然所使用的学习类型还没有明确规定,但我们相信可以使用反向传播或基于多巴胺的学习信号。这将取决于实验,取决于哪个信号在系统中导致更好的性能。然而,我们相信多巴胺系统的动态和多面性可能会带来更好的表现。来自当前任务的内部循环的这些门控表示被传递到相应的外部循环模拟专家和层。例如, h1(x) = [x1(t),y1(t),z1(t)]g1(x) = [a1(t),b1(t),c1(t)],将被输入到相应的一个外环层和专家层,最终导致 H 1(x) = [X1(t),Y1(t),z1(t)]和 G 1(x) = [A1(t),B1(t),C1(t)],当系统与现实世界交互时,门控表示从内环到外环的这种转移发生在所有任务和所有相关门控时间步。**

外部循环的作用是通过在不直接与现实世界交互的单独循环中提炼、重组和更新这些门控表示来获取这些表示并巩固信息。例如,内部循环可以使用不同类型的学习来正确识别任务中的动作-结果配对。这可能包括在不同的时间步骤使用的指导学习和无监督学习策略,它们导致获得相同的目标,但是使用不同的动作组合。然后,这两种学习类型的门控表示将被发送到外部循环,外部循环的专家将在抽象空间中提炼和重组这些表示,并提出一种新的整合策略,该策略是由现实世界中的模型制定的先前策略的最佳元素的组合。当框架在未来的时间步骤中处于相同的情况下时,这些新的表示组合将由外部循环使用双向和自顶向下的影响施加到内部循环上。比如 G3(x)=【A3(t),b3(t),C3(t)】对内循环层施加影响三个专家,G3(x)=【A3(t),B3(t),C3(t)】,决定最终的功能输出

图 4: a)表示内外环展开为一层的框架的数学解释,b)表示内外环展开为两层的框架的数学解释,c)表示内外环展开为三层的框架的数学解释。

外环的另一个重要性是它对思考和推理的贡献,即获取系统知道的信息,并重新组合和提炼这些信息,以建立新的更好的世界表示和模型。在人类中,这被认为是可能的,通过思考的能力或在代表空间中漫步。如上所述,这种离线组合信息的能力允许自然智能通过仅在真实世界中有目的地行动来管理计算和物理约束。在时间敏感的上下文中,内部循环会以两种方式解决任务。首先,层次结构的较高层可以指导较低层的系统从环境中收集可以连续在线处理的附加信息。第二,通过减少诸如注意力之类的约束的影响,内环的较低级别的区域将被分离,并且计算处理将被重定向到较高级别的区域。该策略将涉及仅使用来自内部循环的现有表示以及来自外部循环的表示,这些表示先前已经被细化并且通过自顶向下和双向控制来传递。这种类型的处理包括如何确定去你家乡商店的最快路线。

另一方面,在对时间不敏感的环境中,目标所施加的影响程度将取决于实现目标的紧迫性。在需要在一天内确定解决方案的情况下,目标会比需要在一周内确定解决方案的情况对框架施加更大的影响。在这些情况下,通过减少对内循环和当前环境的关注,主动处理可能完全发生在外循环中。在这个处理过程中,系统会在已经选通和存储在外部循环中的表示中漫游。通过推理、反射和内部查询,这些表示将被引导以新的和新颖的方式组合。反思将允许框架将预测的结果与在实现目标的背景下获得的奖励进行比较。这些表示将使用内部查询过程进行检查,该过程将假设现有动态如何以及为什么与实现目标不同,并且推理将用于形成新的因果关系,该因果关系可以将系统驱动到新的更高的奖励值。这些新的表示将被保持为在相关时间点施加在内部循环上的计划。我们认为,这些外部循环过程可以在更长的时间尺度上进一步扩展,类似于走神和做梦。

在这个框架中,还应该注意的是,大脑使用相同的机制来记忆过去和模拟未来(Schacter 等人,2012 年)。我们相信,通过使用表征的动态模型和构建一个外部循环来巩固和组合表征,以反映过去的事件和模拟新的未来事件,我们可以体现这些相同的认知属性。此外,我们认为这个框架应该带来的好处是类似于大脑的串行和并行处理。当内部循环正在破译如何在一项新任务中取得成功时,它输入信息,以便在循环之间串行和分层处理。然而,一旦框架掌握了任务,它就可以并行处理两个循环中的多个信息流,例如,对多个感官刺激或动作自动赋值。最后,尽管没有明确提到,我们相信通过使用噪声和上述戒律,系统的一个突现性质将是利用稀疏分布表示和吸引子动力学。这可以使用谱图理论进一步分析,谱图理论将确定内部强连接和外部弱连接的集群。

总之,我们将把这个框架根植到一个现实世界的例子中,利用一个经过训练的机器人来执行咖啡制作任务(Tsai 等人,2010)。与介绍性文本中的人类相似,该任务的目标是让机器人正确识别场景中的所有对象,如咖啡、水、咖啡机的部件,并组合制作咖啡所需的正确步骤。内环第一层中的专家将获取环境信息,例如物体的视觉输入、不同物体发出的声音、物体的体感感觉以及伸手够到这些物体所需的预先训练的运动动作。通过框架的第一层,机器人将以面向目标的方式与其环境进行交互,总体目标函数 f(x) 引导系统。然后,这些信息将被发送到框架的第二层,在这个阶段,更复杂的处理,如场景整合、计划建议和运动动作规划,可以在这些独立的横向连接的专家之间进行。这个阶段将整合来自第一阶段的感觉运动信息,整合运动反馈,并提出行动计划。通过双向连接并依靠发生在第二层的处理,这一层的专家可以对较低层的专家施加自上而下的影响,以收集新的信息来制定更好的行动计划。此外,这可能包括编码关于场景中对象的新的或特定的视觉、听觉或身体感觉信息。在训练的早期阶段(例如试错学习)或者如果框架处于巨大的时间压力下,我们相信第二级的处理可以导致第三级执行的直接动作输出,而不需要额外的处理。然而,由于这种策略会忽略更高级别的处理,而是使用简单的策略,例如过去的强化学习值,以及相似性和相异性分析,这种方法很可能会导致更低水平的奖励和实现当前目标的更低概率。然而,这种策略在速度与精度的权衡中或者在不需要大量处理的简单情况下是有用的(Herd 等人,2022 年,出版中)。

当表示在第二层被细化时,形成的抽象可以被发送到第三层进行最终处理。这可能包括奖励值评估和结果预测以及动作执行等过程。这个处理的最后阶段包括预测行动结果值,整合过去行动的错误,并组合一个执行的运动行动计划。在模型执行动作并从环境接收到结果之后,在结果的执行中涉及的表示可以被训练为满足目标,即奖励,或者被训练为不满足目标,即惩罚。在每一级通过这个过程选通的信息然后被传送到相应的外环层和专家。外环的第一层将对门控输入进行更高级别的处理。这将包括微调感觉运动信息的配对,并与之前使用的与该目标匹配的相似或不相似的表征进行比较。然后,信息将被发送到外环的第二级,它将整合场景中存在的所有组件,并为对象和运动计划分配奖励值,以便内环可以在稍后的时间点更快地引起注意。如果框架不能以一种新的方式合并这些表示或者合并动作以更快地执行,自上而下的影响将被施加回外部循环层次结构的第一层。这将迫使外循环中的感觉运动输出对它们的表征进行新的处理,或者在将来处于类似的情况时,保留通过对内循环的注意施加影响以注意该信息的请求。

在第二级中处理的信息最终被发送到外环的最高级别,即第三级。在处理的最后阶段,通过相似-不相似或奖励比较,将运动计划的表示与先前的组合和结果进行比较。如果系统有离线思考的能力,框架将被允许在表征空间中漫游。这将包括三种行为:1)反思为什么一个特定的行为在过去没有导致一个结果,并参与新的结果组合,这将导致更高的或预期的奖励-目标匹配状态;2)通过使用像 GPT-3 这样的语言模型,该模型可以生成简单的句子来检查目标是什么,为什么内部状态没有导致奖励,以及新的行动组合如何在未来导致更高的目标匹配奖励状态。使用偏微分方程来理解系统的潜在动力学是框架进行这些关联的关键;3)通过评估表示和制定内部查询来确定行动如何和为什么导致结果,系统可以制定因果动态以理解输入-输出和行动-结果关系。通过这个专家混合的过程,随着时间的推移连续展开的分层处理,以及两个循环的利用,我们相信机器人和人工智能网络可以更快地学习,并以更可解释的方式概括任务。这种相同的处理过程可以类似地应用于其他例子,如自动驾驶汽车,以及机器人群体,以实现第十条戒律中强调的智能的社会组成部分。

结论

总之,人工智能发展的最后一波取得了巨大的胜利。这些人工智能工具与自然智能仍有许多不同之处,尤其是与人类出色的普通学习者相比。因此,在本文中,我们确定了大脑功能的组成部分,我们认为人类的智能是系统地和分层次地建立在这些组成部分上的。这些组成部分被称为十诫。我们相信,这些戒律在一个系统中共同发挥作用,成为导致更高层次认知和智能出现的基本要素。这个框架的一个定义性特征是,它不是从一个单独的戒律中汲取力量,而是从一个整体的系统中汲取力量,这个系统将所有的组成部分结合起来,使之比其各个部分更加强大。虽然这个框架是一个包罗万象的智能理论,但本文的目的同样是为了确定可以帮助克服当前人工智能挑战的基本原则,以及为下一波智能模型和完全自主或具体化的 AGI 提供信息。这十条戒律最后体现在一个框架中,这个框架混合了专家的方法,包括使用动力系统和两个认知控制回路。我们提出这项工作不是作为一个绝对的解决方案,而是作为未来架构和讨论的灵感,这些架构和讨论也可以建立在大脑功能的力量支柱上。

文献学

Abdelnour、m . Dayan、o . Devinsky、t . Thesen 和 a . Raj(2018 年)。从解剖网络的拉普拉斯特征结构可以预测功能性大脑连接。神经影像172 ,728–739

艾哈迈德和霍金斯(2016 年)。神经元如何对稀疏分布表示进行操作?稀疏性、神经元和活跃树突的数学理论。 arXiv 预印本 arXiv:1601.00720

Ahmad,s .,& Scheinkman,L. (2019 年)。我们怎么会这么笨呢?使用高度稀疏表示的好处。 arXiv 预印本 arXiv:1903.11257

m . AlQuraishi(2019 年)。CASP13 的α折叠。生物信息学35 (22),4862–4865。

Arnab,a .,Dehghani,m .,Heigold,g .,Sun,c .,lui,m .,和 Schmid,C. (2021 年)。Vivit:视频视觉转换器。在IEEE/CVF 计算机视觉国际会议论文集(第 6836–6846 页)。

Ashok,p .,Hashemi,v .,Křetínský,j .,,和 Mohr,S. (2020,10 月)。Deepabstract:用于加速验证的神经网络抽象。在验证和分析自动化技术国际研讨会上(第 92-107 页)。斯普林格,查姆。

巴尔斯,B. J. (1993 年)。关于意识的认知理论。剑桥大学出版社。

巴尔斯,B. J. (1997 年)。在意识的剧场:心灵的工作空间。美国牛津大学出版社。

巴尔斯,B. J. (2002 年)。有意识进入假说:起源和最近的证据。认知科学趋势6 (1),47–52。

巴尔斯,B. J. (2005 年)。意识的全球工作空间理论:走向人类经验的认知神经科学。大脑研究进展150 ,45–53。

巴德雷,d .,&尼,D. E. (2018)。额叶皮层和行为的等级控制。认知科学趋势22 (2),170–188。

Baker,b .,Kanitscheider,I .,Markov,t .,Wu,y .,Powell,g .,McGrew,b .,和 Mordatch,I. (2019)。从多代理自动课程中使用紧急工具。 arXiv 预印本 arXiv:1909.07528

巴拉班,S. (2015 年)。深度学习和人脸识别:最先进的技术用于人类和活动识别的生物识别和监控技术第十二期9457 ,68–75。

巴西特博士和马特博士(2017 年)。人类学习的网络神经科学:为大脑和行为的定量理论提供信息的潜力。认知科学的趋势21 (4),250–264

纽约州本吉奥(2017 年)。意识优先。 arXiv 预印本 arXiv:1709.08568

贝尔,硕士,康纳斯,学士和帕拉迪索,硕士(2020)。神经科学:探索大脑,增强版:探索大脑。琼斯&巴特利特学习。

Berner,c .、Brockman,g .、Chan,b .、Cheung,v .、Dę biak,p .、Dennison,c .……、张,S. (2019)。大规模深度强化学习的 Dota 2。 arXiv 预印本 arXiv:1912.06680

Beyeler,m .,Rounds,e .,Carlson,K. D .,Dutt,n .,& Krichmar,J. L. (2017)。皮层中的稀疏编码和降维。 BioRxiv ,149880。

伯特温尼克,M. M. (2008)。行为和前额叶功能的层次模型。认知科学趋势12 (5),201–208。

m .布林和 g .斯特克(2002 年)。动力系统介绍。剑桥大学出版社。

t .布朗、b .曼恩、n .赖德、Subbiah、m .卡普兰、J. D .、Dhariwal、p .…& amo dei,D. (2020 年)。语言模型是一次性学习者。神经信息处理系统的进展33 ,1877–1901。

**Brunton,S. L .、Brunton,B. W .、Proctor,J. L .、& Kutz,J. N. (2016)。控制用非线性动力系统的 Koopman 不变子空间和有限线性表示。 PloS one11 (2),e0150171。

布伦顿,s . l .&库茨,J. N. (2022)。数据驱动的科学与工程:机器学习、动力系统和控制。剑桥大学出版社。

Brunton,S. L .,Proctor,J. L .,& Kutz,J. N. (2016)。带控制的非线性动力学的稀疏识别。 IFAC-PapersOnLine49 (18),710–715。

布伦顿,S. L .,普罗科特,J. L .,&库茨,J. N. (2016)。通过非线性动力系统的稀疏识别从数据中发现控制方程。美国国家科学院院刊113 (15),3932–3937。

巴克纳特区(2018)。没有魔法的经验主义:深度卷积神经网络中的转换抽象。合成195 (12),5339–5372。卡鲁阿纳(2006 年 6 月)。监督学习算法的实证比较。在第 23 届机器学习国际会议论文集(第 161–168 页)。

陈,A. S .,南,h .,奈尔,s,&芬恩,C. (2021)。可扩展机器人强化学习的示例批量探索。 IEEE 机器人与自动化字母6 (3),4401–4408。

陈,d .,白,y .,赵,w .,阿曼特,s .,格雷瓜尔,J. M .,&戈麦斯,C. P. (2019)。深度推理网络:快速和慢速思考。 arXiv 预印本 arXiv:1906.00855

Chowdhery,a .、Narang,s .、Devlin,j .、Bosma,m .、Mishra,g .、Roberts,a .、… & Fiedel,N. (2022)。Palm:用路径扩展语言建模。 arXiv 预印本 arXiv:2204.02311

克罗斯,,j .,岳,y .&,,J. P. (2021)。使用深度强化学习来揭示大脑如何在高维环境中编码抽象的状态空间表示。神经元109 (4),724–738。**

f .库什曼和 a .莫里斯(2015 年)。人类目标选择的习惯性控制。美国国家科学院院刊112 (45),13817–13822。

德哈瓦斯、J. A .、帕里马尔、s .、松、C. S .、& Chee、M. W. (2012 年)。睡眠剥夺会降低休息和任务执行期间的默认模式网络连接性和反相关性。神经影像59 (2),1745–1751。

Dehaene,s .,Lau,h .,和 Kouider,S. (2021 年)。什么是意识,机器会有意识吗?。机器人学、人工智能和人类,43–56。

邓(2011 年 10 月)。面向信息处理的深度结构化学习综述。进行中。亚太信号与信息会议。年度峰会和会议(APSIPA-ASC)(第 1-14 页)。

D'Mello,A. M .,Gabrieli,J. D .,& Nee,D. E. (2020)。人类小脑分级认知控制的证据。当代生物学

多尔,B. B .,雅各布斯,W. J .,桑菲,A. G .,,弗兰克,M. J. (2009)。强化学习的教学控制:一项行为和神经计算研究。脑研究1299 ,74–94

Droutman,v .,Bechara,a .,& Read,S. J. (2015 年)。岛叶皮层不同亚区在决策过程不同阶段的作用。行为神经科学前沿9 ,309。

范,a .,博萨莱,s .,施文克,h .,马,z .,埃尔基什基,a .,戈亚尔,s,...,朱林,A. (2021)。超越以英语为中心的多语言机器翻译。机器学习研究杂志22 (107),1–48。

Gariépy,J. F .,Watson,K. K .,Du,e .,Xie,D. L .,Erb,j .,Amasino,d .,& Platt,M. L. (2014)。人类和其他动物的社会学习。神经科学前沿8 ,58。

Geirhos,r .,Jacobsen,J. H .,Michaelis,c .,Zemel,r .,Brendel,w .,Bethge,m .,& Wichmann,F. A. (2020)。深度神经网络中的捷径学习。自然机器智能2 (11),665–673。

乔治博士和韦尔塔环境局(2018 年)。实时引力波探测和参数估计的深度学习:先进 LIGO 数据的结果。物理字母 B778 ,64–70。

图式和图式介导的记忆的神经生物学。潮流趋势。Sci。 21,618–631(2017 年)。

Gillenwater,L. A .,Helmi,s .,Stene,e .,Pratte,K. A .,Zhuang,y .,Schuyler,R. P .,… & Kechris,K. J. (2021)。慢性阻塞性肺疾病多组学分型管道。 PloS one16 (8),e0255337。

格雷瓦尔,k .,福里斯特,j .,科恩,B. P .,,艾哈迈德,S. (2021)。超越点神经元:持续学习的主动树突和稀疏表示。 bioRxiv

**Goyal,a .,Didolkar,a .,Lamb,a .,Badola,k .,Ke,N. R .,Rahaman,n .,… & Bengio,Y. (2021)。通过共享的全局工作空间协调神经模块。 arXiv 预印本 arXiv:2103.01197

海登,B. Y. (2018)。觅食视角。行为科学最新观点,24,1–6。**

Herd,S. A .,Banich,M. T .,& O'reilly,R. C. (2006 年)。认知控制的神经机制:Stroop 任务执行和 fMRI 数据的整合模型。认知神经科学杂志18 (1),22–32。

Herd,s .,Krueger,k .,Nair,a .,Mollick,j .,& O'Reilly,R. (2021)。人类决策的神经机制。认知,情感,&行为神经科学21 (1),35–57。

Herd,s .,Nair,a .,Krueger,k .,& O'Reilly,R. (2022)。思考思考的神经基础。认知神经科学杂志,正在印刷

**Heyes,C. (2010 年)。镜像神经元从何而来?。神经科学&生物行为评论34 (4),575–583。

Horovitz,S. G .、Braun,A. R .、Carr,W. S .、Picchioni,d .、Balkin,T. J .、Fukunaga,m .、& Duyn,J. H. (2009 年)。深度睡眠期间大脑默认模式网络的解耦。美国国家科学院院刊106 (27),11376–11381。**

Hubinger,e .,van Merwijk,c .,Mikulik,v .,Skalse,j .,和 Garrabrant,S. (2019 年)。高级机器学习系统中学习优化的风险。 arXiv 预印本 arXiv:1906.01820

Huerta,E. A .,Allen,g .,Andreoni,I .,Antelis,J. M .,Bachelet,e .,Berriman,G. B .,… & Zhao,Z. (2019)。通过深度学习实现实时多信使天体物理学发现。自然评论物理1 (10),600–608。

亨特、L. T .、马拉塞克拉、W. N .、德伯克、A. O .、米兰达、b .、法默、S. F .、伯伦斯、T. E .、&肯纳利、S. W. (2018)。前额叶皮层注意力和决策计算的三重分离。自然神经科学21 (10),1471。

Hunter,K. L .,Spracklen,l .,和 Ahmad,S. (2021 年)。两个稀疏比一个稀疏好:释放稀疏-稀疏网络的性能优势。 arXiv 预印本 arXiv:2112.13896

Ilin,r .,Watson,t .,& Kozma,R. (2017 年 5 月)。深度学习神经网络中的抽象层次。在 2017 国际神经网络联合会议(IJCNN) (第 768–774 页)。IEEE。

金晓波,王海祥,王晓燕,白,杨廷涛,苏铁林,孔,刘建林(2020)。基于贝叶斯优化的串行两级分解深度学习预测模型。复杂性,2020 年

琼斯,例如,鲍威尔,T. P. S. (1970)。猴子大脑皮层内汇聚感觉通路的解剖学研究。93 (4),793–820。

科赫、兰戈斯科、普法乌、勒和夏基(2021 年)。深度强化学习中的客观鲁棒性。 arXiv 预印本 arXiv:2105.14111

新泽西州克里格斯科特(2015 年)。深度神经网络:模拟生物视觉和大脑信息处理的新框架。视觉科学年度回顾1 ,417–446。

莱克、B. M .、乌尔曼、T. D .、特南鲍姆、J. B .、格什曼、S. J. (2017)。建造像人一样学习和思考的机器。行为与脑科学40

李,于(2017)。深度强化学习:综述。 arXiv 预印本 arXiv:1701.07274

Liakoni,v .,Lehmann,M. P .,Modirshanechi,a .,Brea,j .,Lutti,a .,Gerstner,w .,& Preuschoff,K. (2022)。惊奇-行动者-批评家模型的大脑信号:人类决策中多重学习模块的证据。神经影像246 ,118780。

Lusk,r .,Stene,e .,Banaei-Kashani,f .,Tabakoff,b .,Kechris,k .,和 Saba,L. M. (2021 年)。Aptardi 使用高通量 RNA 测序和 DNA 序列预测样品特异性转录组中的聚腺苷酸化位点。自然通讯12 (1),1–13。

**Lusch,b .,Kutz,J. N .,& Brunton,S. L. (2018 年)。非线性动力学泛线性嵌入的深度学习。自然通讯9 (1),1–10。马尔科维茨,h .,&瓦雄,R. (1990 年)。条件推理、表示和抽象层次。发展心理学26 (6),942。

梅苏拉姆,M. M. (1998)。从感觉到认知。脑:神经病学杂志121 (6),1013–1052。

莫里克、J. A .、哈兹、T. E .、克鲁格、K. A .、奈尔、a .、麦基、p .、赫德、S. A .、&奥莱利、R. C. (2020)。阶段性多巴胺的系统神经科学模型。心理复习127 (6),972。

莫泽尔,M. C .,卡萨科夫,&林赛,R. V. (2018)。状态去噪递归神经网络。 arXiv 预印本 arXiv:1805.08394

Munakata,y .,Herd,S. A .,Chatham,C. H .,Depue,B. E .,Banich,M. T .,& O'Reilly,R. C. (2011)。抑制控制的统一框架。认知科学趋势15 (10),453–459。

梅铎,c .,李,z .,周,h .&杜里格,T. (2016)。封锁:分层深度网络的动态模型选择。在IEEE 计算机视觉和模式识别会议论文集(第 2583–2591 页)中。

奈尔斯(2021)。限制神经抽象的数学方法和扩展到更高级认知所需的机制。 arXiv 预印本 arXiv:2108.05494 。**

纽厄尔(1994 年)。认知的统一理论。哈佛大学出版社。

奥莱利,R. C. (2010)。前额叶皮质组织的内容和方式。神经科学趋势33 (8),355–361。

奥莱利,共和党(2020)。揭开动机的神秘面纱。认知科学趋势24 (6),425–434。

奥赖利,R. C .,布雷弗勒,T. S .,&科恩,法学博士(1999)。基于生物学的工作记忆计算模型。工作记忆模型:主动维护和执行控制的机制,375–411。

O'Reilly,R. C .,Hazy,T. E .,Mollick,j .,Mackie,p .,& Herd,S. (2014 年)。大脑中目标驱动的认知:一个计算框架。 arXiv 预印本 arXiv:1404.7591

r . c . o ' Reilly,Herd,S. A .,& Pauli,W. M. (2010)。认知控制的计算模型。神经生物学最新观点20 (2),257–261。

奥赖利,R. C .,& Munakata,Y. (2000 年)。认知神经科学中的计算探索:通过模拟大脑来理解思维。麻省理工出版社。

r . c . o ' Reilly、y . Munakata、m . j . Frank 和 t . e . Hazy(2012 年)。计算认知神经科学。美因茨:儿科出版社。

r . c . o ' Reilly,Nair,a .,Russin,J. L .,& Herd,S. A. (2020)。额叶皮层环路中的顺序交互加工如何支持习惯性加工到控制性加工的连续体。心理学前沿11 ,380。

r . c . o ' Reilly,Russin,J. L .,Zolfaghar,m .,& Rohrlich,J. (2021)。大脑皮层和枕叶的深度预测学习。认知神经科学杂志33 (6),1158–1196。

皮尔森,H. A .,&加什勒,硕士(2017)。机器人学中的深度学习:近期研究综述。高级机器人31 (16),821–835。

**Pignatelli,m .,& Beyeler,A. (2019)。杏仁核回路中的价态编码。行为科学的当前观点26 ,97–106。

平托,l .,戴维森,j .,&古普塔,A. (2017,5 月)。通过竞争监督:学习任务的机器人对手。在 2017 IEEE 机器人与自动化国际会议(ICRA) (第 1601–1608 页)。IEEE。

波吉欧,t .,班布尔斯基,a .,&廖,Q. (2019)。深度网络中的理论问题:近似、优化和推广。 arXiv 预印本 arXiv:1908.09375 。**

Pogodin,r .,Mehta,y .,Lillicrap,t .,& Latham,P. E. (2021 年)。生物学似是而非的卷积网络。神经信息处理系统的进展34 ,13924–13936。

祁国杰(2016)。用于语义分割的分层门控深度网络。在IEEE 计算机视觉和模式识别会议论文集(第 2267–2275 页)。

拉梅什、达里瓦尔、尼科尔、朱、陈、米(2022)。具有剪辑潜在时间的分层文本条件图像生成。 arXiv 预印本 arXiv:2204.06125

拉德福德、a .、金、J. W .、哈拉奇、c .、拉梅什、a .、高、g .、阿加瓦尔、s .…&苏茨基弗,I. (2021 年 7 月)。从自然语言监督中学习可转移的视觉模型。在机器学习国际会议(第 8748–8763 页)。PMLR。

**Raj,a .,Cai,c .,Xie,x .,Palacios,e .,Owen,j .,Mukherjee,p .,& Nagarajan,S. (2020)。脑振荡的谱图理论。人脑图谱

拉希,b .&博恩,J. (2013)。睡眠在记忆中的作用。生理评论。拉武里,兰茨,k .,王绍博,m .,康金,d .,拉姆,r .,米罗斯基,p .,… &穆罕默德,S. (2021)。使用雷达的深度生成模式熟练的降水临近预报。性质597 (7878),672–677。**

任,m .,廖,r .,费塔亚,e .,,泽梅尔,R. (2019)。基于注意吸引子网络的增量少镜头学习。神经信息处理系统的进展32

雷诺兹和奥赖利(2009 年)。使用强化学习开发 PFC 表示。认知113 (3),281–292。

Richards,B. A .,Lillicrap,T. P .,Beaudoin,p .,Bengio,y .,Bogacz,r .,Christensen,a .,… & Kording,K. P. (2019)。神经科学的深度学习框架。自然神经科学22 (11),1761–1770。

Rolls,E. T .,& Treves,A. (2011 年)。大脑中信息的神经元编码。神经生物学进展95 (3),448–490。

**Russin、r . c . O ' Reilly 和 y . beng io(2020 年)。深度学习需要前额叶皮层。工作衔接 AI Cogn Sci107 ,603–616。

Russin,j .,Zolfaghar,m .,Park,S. A .,Boorman,e .,& O'Reilly,R. C. (2021 年 7 月)。用于关系推理的互补结构学习神经网络。在认知科学学会年会上。认知科学学会(美国)。会议(第 2021 卷,第 1560 页)。NIH 公共访问。

s Mann,P. G .,Wehrle,r .,Hoehn,d .,Spoormaker,V. I .,Peters,h .,Tully,c .,& Czisch,M. (2011)。开拓之脑从清醒到慢波睡眠的默认模式网络。大脑皮层21 (9),2082–2093。

桑切斯,E. (2017)。基于游戏学习的竞争与合作:案例研究。在促进严肃游戏学习和动机的教学技巧(第 161-184 页)。Springer,Cham

Schacter,D. L .、Addis,D. R .、Hassabis,d .、Martin,V. C .、Spreng,R. N .、& Szpunar,K. K. (2012 年)。记忆的未来:记忆、想象和大脑。神经元76 (4),677–694。**

Schechtman,e .,Laufer,o .,和 Paz,R. (2010 年)。负价扩大了学习的普遍性。神经科学杂志30 (31),10460–10464。

施耐德博士和洛根博士(2006 年)。认知过程的分级控制:顺序转换任务。实验心理学杂志:普通135 (4),623。

Schrittwieser,j .,Antonoglou,I .,Hubert,t .,Simonyan,k .,Sifre,l .,Schmitt,s .,和 Silver,D. (2020)。通过用学习过的模型计划来掌握雅达利、围棋、国际象棋和松木。性质588 (7839),604–609。

Silva,a .,& Gombolay,M. C. (2021 年 5 月)。编码人类领域知识以热启动强化学习。在AAAI 人工智能会议记录(第 35 卷,第 6 期,第 5042-5050 页)。

Silver,d .,Hubert,t .,Schrittwieser,j .,Antonoglou,I .,Lai,m .,Guez,a .,… & Hassabis,D. (2018)。一种通用的强化学习算法,可以掌握国际象棋、日本象棋和自我游戏。理科362 (6419),1140–1144。

西尔弗博士、辛格博士、普雷科普博士和萨顿博士(2021 年)。奖励就够了。人工智能299 ,103535。

Soofastaei,A. (2021)。介绍性章节:虚拟助手。虚拟助理,1。

苏,陈,徐,张,王,等(2020)。心理健康结果研究中的深度学习:范围审查。转化精神病学10 (1),1–26。

萨顿和巴尔托(2018 年)。强化学习:介绍。麻省理工出版社。

泰勒,p .,霍布斯,J. N .,伯罗尼,j .,,西格尔曼,H. T. (2015)。认知的全球图景:作为人类皮层网络和功能的组织原则的等级聚集。科学报告5 (1),1–18。

Team,O. E. L .、Stooke,a .、Mahajan,a .、Barros,c .、Deck,c .、Bauer,j .…& Czarnecki,W. M. (2021)。开放式学习会产生能力一般的代理人。 arXiv 预印本 arXiv:2107.12808

蔡超群、王、李彦宏、李彦宏、戴福成(2010 年 7 月)。拟人化双臂机器人的合作与任务执行:咖啡制作的应用。在 2010 年系统科学与工程国际会议上(第 239-244 页)。IEEE。

图式和新奇如何增强记忆的形成。趋势神经科学。2012 年第 35 期,211–219 页。

范·凯斯特伦,M. T. R .,& Meeter,M. (2020)。如何优化大脑中的知识构建? npj 学科学5 (1),1–7。

Velickovic,p .,Bosnjak,m .,Kipf,t .,Lerchner,a .,Hadsell,r .,Pascanu,r .,& Blundell,C. (2021)。推理调制的表征。

Vinyals,o .,Babuschkin,I .,Czarnecki,W. M .,Mathieu,m .,Dudzik,a .,Chung,j .,…,Silver,D. (2019)。星际争霸 2 中使用多智能体强化学习的特级大师级别。性质575 (7782),350–354。

Vogelstein、J. T .、Verstynen、t .、Kording、K. P .、Isik、l .、Krakauer、J. W .、Etienne-Cummings、r .……、Yang,W. (2022)。前瞻性学习:回到未来。 arXiv 预印本 arXiv:2201.07372

韦尔曼,H. M .,,盖尔曼,S. A. (1992)。核心领域的基础理论。心理学年刊43 (1),337–375。

惠廷顿,J. C .,穆勒,T. H .,马克,s .,陈,g .,巴里,c .,伯吉斯,n .,,T. E. (2019)。托尔曼-艾肯鲍姆机器:通过海马结构的一般化统一空间和关系记忆。 BioRxiv ,770495。

叶文伟,刘,库鲁塔奇,t,阿贝耳,p .,,高,Y. (2021)。用有限的数据掌握雅达利游戏。神经信息处理系统的进展34

周,谢,雷,谢(2018)。游移不定的大脑网络。神经科学通报34 (6),1017–1028。朱,谢军(2005)。半监督学习文献调查。

用卫星图像缩小碳清单中土地使用的差距

原文:https://towardsdatascience.com/bridging-the-land-use-gap-in-carbon-inventories-with-satellite-imagery-c82978a1c7

利用地理信息系统技术将树木和森林纳入地方 GHG 清单

当温室气体(GHG)排放是气候变化的主要驱动力时,最近的 IPCC 报告指出,从大气中去除二氧化碳(CO2)对于限制全球变暖的影响至关重要。这与保持和扩大树木和森林捕获和储存碳的能力的重要性齐头并进,在地区或城市一级增强了当地集中努力的重要性。

尽管如此,许多地方 GHG 清单和气候行动计划倾向于更多地关注固定能源、运输、废物和工业过程,而不利于与土地使用相关的排放和清除。这主要是因为缺乏专业知识以及森林和树木对 GHG 的影响微不足道的想法。考虑到这一点, WRI土地部门补充试图扩展 GPC 中为农业、林业和其他土地利用(AFOLU)的“土地”子部门提供的有限指导。

在这里,我们将研究如何将该指南应用于案例研究——巴西萨尔瓦多市——专门使用公开可用的空间数据。该试点项目是在巴西 WRI 的城市 4 森林项目下,由劳拉·卡西亚和顾问古伊列梅·伊布洛诺夫斯基在 WRI 全球办公室团队的指导和支持下开发的,该团队由大卫·吉布斯、南希·哈里斯和约翰·罗布·普尔代表。

萨尔瓦多市的土地使用变化

萨尔瓦多位于巴西东海岸的大西洋雨林生态区。尽管它有很大的城市范围,有超过 280 万人口(巴西第四大城市),它还是几片残存的大西洋雨林的家园。

这是过去 35 年里萨尔瓦多市及其土地利用变化的样子。主要是为了城市发展而砍伐森林。图片作者。

2020 年 8 月,萨尔瓦多市政厅发布了涵盖 2014-2018 年期间的城市 GHG 清单,并在同年 12 月推出了其气候行动计划。这两份文件都没有包括与土地利用相关的 GHG 流量。

最准确地计算清单期间(2014-2018 年)这些森林斑块和树木的 GHG 排放量和清除量的第一步是获取土地利用变化的空间数据。在这个试点项目中,数据来源于 MapBiomas ,这是一个基于谷歌地球引擎的国家免费土地利用分类图像数据库,来自 Landsat 7 马赛克。

然后,我们需要查看最初(2014 年)和最后(2018 年)的数据,以便估计土地利用随时间的变化,或者碳储量如何变化。为此,任何 GIS 软件都可以,我们需要做的只是运行一个函数(如栅格代数或区域统计)来捕获这两年的像素值。有了这个,我们就可以确定有多少土地被转化为林地。

图片作者。数据来源:map biomass Project-Collection 5.0,巴西土地利用和土地覆盖图年度系列,于 2021 年 10 月 10 日通过链接访问:mapbiomas.org

为了使数据与最新的气专委 GHG 国家清单指南相一致,必须将其重新分类为六个主要的土地利用类别——林地耕地草地居民点湿地其他土地—* ,我们可以将这些土地利用类别绘制成土地利用变化矩阵。在该矩阵中,每个单元代表在分析期间从给定土地利用类别开始到给定土地利用类别结束的土地面积。*

公顷计的土地使用转换。深绿色单元格代表森林剩余林浅绿色单元格代表非林转变为林粉红色单元格代表森林转变为非林灰色单元格代表清查期间保持不变用途的土地。图片作者。

从上面的矩阵可以看出,在 2014-2018 年期间,大约 74 公顷的林地被其他土地使用,而 27 公顷被重新造林。然而,在像萨尔瓦多这样土地利用相对稳定的城市,来自陆地的 GHG 通量可能会集中在我们矩阵的对角线上,这意味着大多数调查区域仍属于同一土地利用类别。

排放量和清除量可能发生在属于同一土地利用类别的土地上,甚至发生在矩阵的同一单元内。例如,林地剩余林地既有来自采伐和其他自然干扰的排放量,也有来自森林生长的清除量;随着时间的推移,居住区剩余居住区类别内城市树冠层的变化会导致树冠层损失产生的排放和维护或增加城市树冠层产生的清除。

超越雨林斑块:可视化城市树冠层

我们已经测量了城市的林地覆盖面积,但仍有树木需要纳入城市结构,因为这些树木对碳的清除和排放同样重要。

到目前为止,我们能够使用中等空间分辨率的图像,也就是之前分类的 3000 万像素的陆地卫星 7 图像,但是这些还不足以识别混合在城市结构中的奇异树木。为此,精度最高的方法是对高分辨率影像进行影像分类,但这往往会超出许多城市的承受能力。因此,为了坚持只使用公开可用数据集的目标,我们将求助于 iTree Canopy 工具。

iTree Canopy 允许我们通过使用 Google Maps 卫星底图来量化给定地区(在我们的例子中,只有萨尔瓦多的非林地)的树冠覆盖。这是对影像的间接使用,因为与 GIS 软件中的分类不同,在 GIS 软件中,每个像素由算法分配一个类别,在这里,用户不能完全访问原始影像及其波段。这意味着我们不会处理像素,而是评估图像中任意随机选择的点对应于一棵树的概率。因此,为了使样本具有代表性,估计值取决于更多的点(在我们的例子中是 500 个)。一个简单的统计分布被计算出来,这个比例又被应用到以平方公里为单位的总调查面积上,以估算城市树木覆盖的面积。收集的样本点越多,越小,误差幅度越小。

在 iTree Canopy 中,随着越来越多的点被识别为树木或非树木,误差幅度会在箱线图中动态调整。图片作者。

为了能够评估城市树冠的变化,我们必须用相同的一组点来比较 2018 年和 2014 年的图像(例如,通过使用谷歌地球来访问历史图像)。ITree Canopy 提供了模板电子表格,我们可以从中获取结果,从而获得每个类别的最终统计数据。用于开发该分类模型的指南可在这里找到。

通过简单地将剩余树木和树冠损失的估计百分比乘以调查的总面积,即可计算出最终估计值。图片作者。

就这些吗?—说明土壤中储存的碳

计算森林和城市树冠覆盖的面积是我们努力实现的目标的关键,但这不是全部。陆地上的碳含量和在世界不同生态系统中循环的碳含量差异极大。

碳储存在多个池中:地上生物量、地下生物量、枯木、垃圾、土壤和伐木制品。在许多群落中,陆地上最大的单一碳库是地上生物量;而在其他地方,如沼泽或红树林,土壤碳可能是主要的碳库。枯木和凋落物碳可能占地上碳的百分之几,尽管它们也可能有很大差异。

将应用于面积计算的清除和排放因子只能说明地上生物量。这意味着我们需要找到一种方法来解释,至少,储存在土壤中的碳库。

这方面的来源可能因国家而异,但幸运的是,我们有令人惊叹的土壤网格项目为我们指路。

根据土壤网格萨尔瓦多土壤有机碳储量的分布。图片作者。

从土壤网格的土壤有机碳储量层,我们知道土壤碳在城市区域内是如何分布的。唯一受到影响的碳库是位于被砍伐地区下面的碳库。由于我们已经从土地使用转换数据中知道了哪些区域被砍伐,我们可以量化这些特定区域的土壤中储存了多少碳。这与毁林后排放到大气中的碳不同:为此,我们需要土壤排放分数。

2014 年至 2018 年期间被砍伐森林的地区土壤中的碳含量,根据占用先前森林地区的土地使用类别进行分层。作者图片

从公顷到吨:应用排放和清除系数

既然已经计算了森林、树木和土壤的土地利用转换,那么可以将它们与排放和清除因素结合起来。

这种简化的损益方法在根本上类似于其他部门的 GHG 清单,其中存量的变化被估计为活动数据(分类土地利用/土地利用变化的区域)和排放系数(每单位土地面积每个碳集合排放的碳量)的乘积。

然而,与其他部门不同,土地部门也包括清除量。清除系数被估计为单位面积土地利用/土地利用变化每年植被积累的碳量,并因土地利用和管理做法而异。

巴西森林的排放因子包括地上生物量、地下生物量、枯枝碳和凋落物碳;土壤碳排放量单独计算。根据 IPCC 的默认值,森林外树木的排放因子仅包括地上和地下生物量。

对于毁林后的每一种土地利用类型,计算了土地利用转换 20 年后保留的土壤碳的比例(例如,20 年后,80%的土壤碳保留在转换为定居点的林地中)。

把所有这些放在一起:萨尔瓦多做得怎么样?

活动数据必须包括土地使用发生变化的土地区域,以及在一段时间内保持其用途的土地。排放和清除都可能发生,因此净总 CO2 通量是所有土地面积的 CO2 排放和清除总量的总和。

现在你知道了:2014 年至 2018 年间,萨尔瓦多的森林和树木是每年 58 毫克 CO2 的净汇,代表 39 毫克 CO2/年排放量97 毫克 CO2/年清除量。排放量和清除量主要是由森林以外的树木驱动的,这些树木在清单中占主导地位(清除量是森林的 2 倍,排放量是森林的 6 倍),并使土壤碳排放量相形见绌。

由于用于此的所有数据来自空间来源,我们可以应用相同的因素,同时考虑像素级的活动,这样就可以直观地显示市政区域内排放量、清除量和流量的大致分布。

图片作者。数据来源:map biomass Project-Collection 5.0,巴西土地利用和土地覆盖图年度系列,于 2021 年 10 月 10 日通过链接访问:mapbiomas.org

最后的想法

结果表明,萨尔瓦多有机会通过减少非森林冠层损失的排放来进一步减少排放并增加其净吸收汇。在这种情况下,该清单可以成为一个很好的宣传工具,表明从气候变化的角度来看,森林以外的树木同样重要。

该试点严重依赖于空间数据,但地理处理操作相当简单,这可能意味着 GPC 的土地部门可以通过遵循 WRI土地部门补充来轻松实施。令人耳目一新的是,试点项目中使用的数据完全可以在巴西所有城市免费获得,并且易于获取,这意味着我们很快就可以看到全国碳库存中的土地使用缺口得到填补。

为 Power BI 中的现场参数注入活力

原文:https://towardsdatascience.com/bring-life-to-field-parameters-in-power-bi-f284394fe4cf

使用 Power BI 中的字段参数功能时,检查这个简单的技巧来增强用户体验

作者图片

老实说,我不记得有哪一个 Power BI 特性像 Fields 参数那样引起如此大的炒作,它是在 5 月 Power BI 桌面每月更新中引入的!好吧,几天后的 MS Build 事件带来了一些非常酷和有吸引力的功能,如 Datamart 或 PowerPoint 集成,这将字段参数推到了一边,但我仍然坚信字段参数是将永远改变我们在 Power BI 中构建用户体验的方式的事情之一。

我不会详细介绍这个特性本身,以及它是如何在幕后工作的,因为我的社区贡献者已经做了很好的解释。我建议你开始阅读我的朋友汤姆·马腾斯的这篇精彩文章,或者如果你喜欢看视频,可以阅读 SQL BI guys 的这篇文章。

此外,这项新功能已经有了一些非常有创意的用例,比如凯恩·斯奈德的这个

然而,在本文中,我想让您快速了解如何利用 Fields 参数为您的 Power BI 报告增添一些活力!

搭建舞台

我的数据模型非常简单:我有一个事实表和三个维度表——地区、产品和日期。

作者图片

让我们为我们的数据模型创建字段参数:

作者图片

您可能注意到了,我从每个维度表中拖出了两列,一旦确认,我的报告页面上就有了一个新的切片器,使用户能够以多种不同的方式对数据进行切片和切块。并且,不需要后台复杂的 DAX!

作者图片

多酷啊!但是,如果我们想根据某些标准对字段参数值进行分组,该怎么办呢?例如,所有与产品相关的字段应属于一个组,与地理相关的字段应属于另一个组,依此类推。

我有好消息要告诉你!但是,在我向您展示如何做到这一点之前,让我们先来看看 Power BI 在创建 Fields 参数表时自动生成的底层 DAX 代码:

作者图片

如你所见,这是“只是”达克斯!同样,我不会详细解释每个代码部分的用途,但是理解这“只是”DAX 为定制这个表提供了可能的空间!所以,我们开始吧!

让您的数据熠熠生辉!

这个想法是将品牌和类别字段放在一个“保护伞”下,将洲和国家放在另一个“保护伞”下,最后将年和月简称也放在第三个组下。我可以简单地为 DAX 表定义的每个成员添加一个新列,如下所示:

作者图片

但是,我有一个更好的主意!让我们给它带来一些活力,显示图标而不是文本!我将使用一个众所周知的 UNICHAR() 函数来提供应该被转换成图标的数值,类似于我在这篇文章中所解释的。

Slicer = {
    ("Brand", NAMEOF('Dim_Products'[Brand]), 0, UNICHAR(127981)),
    ("Category", NAMEOF('Dim_Products'[Category]), 1, UNICHAR(127981)),
    ("Continent", NAMEOF('Dim_Region'[Continent]), 2, UNICHAR(127757)),
    ("Country", NAMEOF('Dim_Region'[Country]), 3, UNICHAR(127757)),
    ("Year", NAMEOF('Dim_Calendar'[Year]), 4, UNICHAR(128198)),
    ("Month Name Short", NAMEOF('Dim_Calendar'[Month Name Short]), 5, UNICHAR(128198))
}

这是我添加了一个额外的列之后的 DAX 代码。此外,我还将新创建的列从通用的“值 4”重命名为“组”:

作者图片

是时候将我们的新设置投入使用了!因此,我将把 Group 列拖动到参数切片器的字段中,就在原始字段参数值的上方:

作者图片

哇哦!现在,我的字段参数值被分组在这些漂亮的图标下,所以我的用户可以很容易地理解各种切片选项!他们仍然可以基于来自不同组的多个不同的字段快速分割数据,但是这样数据的结构和组织要好得多!

作者图片

最终实现更好的用户体验

让我们做一些额外的小魔术来进一步增强用户体验!

我想让我的用户能够自己选择如何查看矩阵中的数据!换句话说,动态选择在行和列中显示什么!

我将创建此字段参数表的副本,但这一次我将为切片器启用单选,这样,如果没有选择任何内容,矩阵不会变得过于混乱:

作者图片

厉害!现在,我们的矩阵是完全可定制的,用户可以动态选择他们希望如何查看数据—在行和列之间切换!

结论

Fields 参数是您知道会产生巨大影响的特性之一!这不仅仅是因为它开启了无限多的可能用例,还因为它易于实现,并且可以扩展定制,从而以更高效的方式回答业务问题。

感谢阅读!

订阅,不要错过任何媒体上的故事!

将秩序带入条形图的混乱中

原文:https://towardsdatascience.com/bringing-order-into-bar-chart-chaos-f271ab91a6ee

对单个多面 ggplot2 条形图中的条形图进行排序

作者图片

我们都喜欢秩序(或者至少是我们大多数人)。它帮助我们组织我们的思想,给我们一种平静的感觉。同样的原理也适用于数据可视化。我们喜欢它们整洁有序,因为这样看起来更美观,便于比较。条形图是一种可视化,其中这一点最为明显。在处理单个图表时,大多数人可能都熟悉对条形图进行重新排序。然而,我敢打赌,你们中的大多数人在刻面图上做这个会有问题。在下文中,我将展示如何使用来自世界数据库的国内生产总值(GDP)信息来解决这两种情况下的问题。

订购单个条形图

让我们只筛选四十年间世界前七大经济体:

现在,我们可以用一个条形图来比较他们的 GDP:

比较国家有点困难,因为国家标签不是由他们的 GDP 订购的。我们可以通过使用reorder函数按照 2020 年的 GDP 对这些国家进行降序排列来解决这个问题。

太好了!我们获得了一块有用的土地。然而,这只是 2020 年的图表。下一节将展示当同时显示不同年份的多个条形图时,如何对条形图进行排序。

多个图表—显示条形图

为了同时说明各国在不同时间的 GDP 对比,我们可以使用facet_wrap函数:

我们可以观察到与之前相同的问题—小节没有排序。让我们试着用同样的方法来解决这个问题:

问题依然存在!您可能会认为这可能只是这个 facet 布局的产物,因为在整个列中使用相同的水平轴标签会自动强制相同的条排序。如果是这种情况,可以通过允许每个面拥有自己的一组水平轴标签来解决这个问题。我们可以通过将facet_wrap函数的scales参数设置为“free_x”来做到这一点。

这也没有解决问题,因为排序又是全局进行的。下一节将展示如何使用tidytext包绕过这个问题。

对方面进行排序

顾名思义,tidytext包是为了便于将文本转换成整齐的格式而创建的。在这里,我们将关注于reorder_within函数,它允许我们在一个 facet 中重新排序条。除了将reorder函数与reorder_within函数交换之外,我们还必须从同一个包中添加一个对scale_x_reordered函数的调用,以使其工作。

就是这样!我们可以看到,在过去的四十年里,美国一直保持着第一的位置。然而,中国的增长率最快,而且似乎正在接近。日本和德国设法保住了领先两个国家的位置,但其他主要欧洲经济体,如法国和英国,似乎正在落后,为印度让路。

结论

这篇文章展示了如何订购单一和多面条形图。最终的有序分面图使我们能够更容易地对世界上最重要的经济体进行比较。所以,下次你想在多面条形图之间进行比较时,你应该知道该怎么做。对于任何问题或意见,请随时发表评论。

附注:对于那些无法停止思考那些无序砖柱的人,这里有一个挠痒痒的地方:)

作者图片

数字广播:你应该知道的强大技术

原文:https://towardsdatascience.com/broadcasting-in-numpy-a-powerful-technique-you-should-know-7dc1dde619fa

广播在幕后如何运作的细节

唐纳德·詹纳蒂在 Unsplash 上的照片

简介

什么是广播? 广播 是一种允许Numpy在算术运算时处理不同形状数组的机制。在广播中,我们可以把它想象成一个较小的数组,在做某些操作之前,被“广播”成与较大的数组相同的形状。通常,较小的数组将被复制多次,直到它达到与较大的数组相同的形状。注意,我们这里说的数组指的是 Numpyndarrays,是固定大小项目的多维数组。

使用广播允许 向量化 ,这是一种处理整个数组而不是单个元素的编程风格。如果你不熟悉矢量化的概念,我写了一篇关于矢量化的文章,你可以从中了解更多。广播通常很快,因为它对数组操作进行了矢量化,所以循环发生在优化的 C 代码中,而不是较慢的 Python 中。此外,它并不真的需要存储较小数组的所有副本;相反,有更快更有效的算法来存储。

广播示例

举个例子,假设你想给一个数组添加一个常量。最天真的方法应该是这样的:

import numpy as npdata = np.array([[1,2], [3,4]])
constant_arr = np.array([[5,5], [5,5]])
data + constant_arr
# result: array([[6, 7],
                 [8, 9]])

我们定义一个和数据形状一样的数组来保存常数,然后把它们加起来。这是因为两个数组具有完全相同的形状。

但是如果这次的数据更大,比如说,是三维的。为了迎合这一点,我们将定义另一个数组来保存常数,但这一次,用了一个更大的维度:

data = np.array([[[1,2], [3,4]], [[5,6], [7,8]]])
constant_arr = np.array([[[5,5], [5,5]], [[5,5], [5,5]]])
data + constant_arr
# result: array([[[ 6,  7],
                  [ 8,  9]],

                 [[10, 11],
                  [12, 13]]])

这个管用。然而,正如您所看到的,代码似乎变得有点混乱和多余。为了提高可读性,您可能会注意到我们可以使用[np.full](https://numpy.org/doc/stable/reference/generated/numpy.full.html)函数,这将允许我们定义一个给定大小的数组,其中填充了一个特定的标量值。因此,我们可以做到:

constant_arr = np.full((data.shape), 5)
data + constant_arr
# result: array([[[ 6,  7],
                  [ 8,  9]],

                 [[10, 11],
                  [12, 13]]])

现在,这个稍微好一点。一切都很好,但是有没有办法让它更简单、更简洁?

正如你所猜测的,答案是肯定的,而实现这一点的方法就是通过广播。Numpy 允许我们处理不同形状的数组,从而使我们更容易执行操作。我们可以直接将常量添加到任何数组中,如下所示:

data + 5
# result: array([[[ 6,  7],
                  [ 8,  9]],

                 [[10, 11],
                  [12, 13]]])

这不是简单多了吗?

广播失败的地方

广播让一切变得更简洁,但它能与任意两个数组一起工作吗?

作为一个例子,让我们试着用形状为(3,)的填充了 1 的数组来添加前面的数据数组:

new_arr = np.ones(3, dtype=int)
data + new_arr

运行此示例时显示错误

这似乎给出了一个值错误。很奇怪吧。

产生错误的原因是 Numpy 无法将较小的数组(在本例中为new_arr)传播到与data数组形状相同的数组中。请记住,广播的中心思想是试图复制较小数组中包含的数据,以匹配较大数组的形状。在这种情况下,没有办法复制一个大小为(3,)的数组来匹配(2,2,2)的形状。因此,给出值 Error。

虽然广播通常允许您使用不同大小的数组,但是有一个警告。并非所有数组都可以在一次操作中一起使用。

广播规则

如果广播不能通用,广播什么时候起作用,什么时候失效?

假设我们试图在两个数组之间执行一些操作,数组 ab 。一般来说,广播的工作原理如下:

  • 如果 ab 的维数不同,那么维数较少的数组增加一个元素的维数,直到该数组的维数与另一个数组的维数相匹配。

这是什么意思?举个例子,比方说data = np.array([[1,2], [3,4]]) 我们正在努力做data + 5 我们可以假设 a = data,而 b = 5。这一步将把 b 转换成np.array([[5]]),现在是二维的,形状为(1,1)

  • 一旦 ab 的维数相等,迭代过程就开始了。我们遍历 ab
    **的每个维度 i 如果 ab 的维度 i 的形状相同,那么我们继续下一个维度 i+1
    ** 否则如果 b 在维度
    I
    中的形状为 1
    否则如果 a 在尺寸 i 中的形状为 1,则复制 a 的尺寸 i+1、i+2、…、*max(len(a.shape), len(b.shape))*中的数据,直到形状与 b 相同。(与上一个 else if 相同,但这一次, a 被复制,因为它的形状更小)。
    否则,将引发“ValueError:操作数不能一起广播”,表示数组 ab 不兼容,不能广播。**

回到data + 5的例子, ab 都有两个维度,分别是a.shape=(2,2)b.shape=(1,1)。我们开始迭代过程。我们从尺寸 0 开始,观察到 b 在尺寸 0 中的形状为 1。因此,我们复制了[5],通过使其成为*b=np.array([[5], [5]])*,形状现在变成了 2 ( a 在维度 0 上的形状)。现在,我们有了b.shape=(2,1) 接下来,我们继续下一个维度,维度 1 ,我们再次观察到 b 的形状为 1。我们复制5,使 b 的形状为 2(与 a 的形状相同),将其转换为*b=np.array([[5, 5], [5, 5]])*。由于没有更多的维度可以迭代,我们停止,导致 ab 在所有维度上的形状相同。最后,我们可以将它们逐个元素相加,得到结果np.array([[6, 7], [8, 9]])

希望这一切都有意义。这里要注意的要点是,广播试图使两个数组的每个维度的形状相同,它通过多次复制该维度中形状为 1 的数组,直到达到目标形状。

更多例子

我知道如果你是第一次学广播,有很多东西需要理解。别担心,这里还有一些例子可以帮助你更熟悉规则。

对于下面的每个示例,考虑如何转换一个数组以匹配另一个数组。

a = np.array([[2,4], [6,8]])
b = np.array([1,3])
a + b
# result: array([[ 3,  7],
                 [ 7, 11]])

在这种情况下, b 转化为b=np.array([[1,3], [1,3]])

a = np.array([[2,4], [6,8]])
b = np.array([[1], [3]])
a + b
# result: array([[ 3,  5],
                 [ 9, 11]])

这里 b 转化为b=np.array([[1,1], [3,3]])

a = np.array([[2,4], [6,8]])
b = np.array([[1,3,5], [2,4,6]])
a + b

运行此示例时显示错误

最后,在最后一个例子中,出现了 ValueError。这是因为 a 具有(2,2)的形状, b 具有(2,3)的形状,并且正如你所看到的,在尺寸 1a 和 b 都不具有 1 的形状,因此两者都不能被复制以匹配另一个。因此,这两个数组无法广播。

希望你理解了这些例子,并且记住,一旦你练习和写更多的 Numpy,你会对广播更加熟悉。随着时间的推移,这些规则会变得更加直观。

总结

如果你在这里逗留,一定要给自己一个鼓励!总而言之,我们学习了什么是广播,它在哪里失败,以及它是如何工作的。广播在 Numpy 中使用非常频繁,因此了解它在幕后是如何工作的非常重要。

我希望这篇文章很好地解释了广播背后的概念。如果你有任何问题,请告诉我,随时乐意帮忙!如果你需要的话,这里有到 Jupyter 笔记本的链接,其中包含了本文介绍的代码。

如果你刚刚写完这篇文章,但是不确定什么是矢量化或者它是如何工作的,一定要去看看我以前的文章。请随意浏览我的其他文章来了解更多。下次见,再见。

我以前的一些文章:

参考

广播,Numpy

Numpy 文档,Numpy

Numpy —广播、辅导点

Python |用 Numpy 数组广播,GeeksforGeeks

熊猫广播业务解释

原文:https://towardsdatascience.com/broadcasting-operations-in-pandas-explained-f8e00af73963

了解 Pandas 中的应用、应用映射和聚合函数的作用

来自像素的免费使用照片

介绍

Apply、Applymap 和 Aggregate 函数经常用于以用户期望的方式转换变量或整个数据。我个人将这些函数称为“广播函数”,因为它们允许我们向变量或数据中的所有数据点广播某种逻辑,比如说自定义函数。在本文中,我将向您解释这三个函数的不同之处,并通过一些例子来说明这些观点。我们使用标志性的泰坦尼克号灾难数据集作为例子。具体来说,我使用了拥有公共许可证的 OpenML 中的数据集。

我们首先导入 pandas 包,并将 titanic 训练数据存储在一个名为“df”的变量中。

**# Dataset Source:** [**OpenML**](https://www.openml.org/search?type=data&sort=runs&id=40945&status=active)**; License(CC): Public****import** pandas **as** pd
df = pd.read_csv("../input/titanic/train.csv")

应用

简单地说,pandas 中的 apply 函数是一个变量级函数,您可以应用各种转换来转换一个变量。在这里,您可以利用 lambda 函数或自定义函数来创建您想要应用的转换逻辑。例如,如果出于某种原因,您希望将“Fare”变量乘以 100,您可以运行以下代码:

df['Fare'] = df['Fare'].apply(lambda x: x * 100)

有了这个想法,你就可以执行各种很酷的转换,只要你能够明智地按照你想要的方式精确地设计 lambda 或自定义函数。下面是一个代码示例,它从一些 xxxx/mm/dd 格式的字符串日期中提取月和日信息。

data['last_review_month'] = data['last_review'].apply(lambda x: datetime.datetime.strptime(x, "%Y-%m-%d").month)data['last_review_day'] = data['last_review'].apply(lambda x: datetime.datetime.strptime(x, "%Y-%m-%d").day)

应用地图

Applymap 函数是 apply 的所有数据版本,其中转换逻辑应用于数据中的每个数据点(例如,数据视图中的每个单元格)。

假设我们想把所有乘客的名字都改成小写。出于演示目的,让我们创建一个单独的数据帧,它是原始数据帧的子集,其中只有“Name”变量。

df_name = df.copy()[['Name']]df_name.head()

来源:作者

现在,我们使用 Applymap 函数来完成我们想要的。

df_name = df_name.applymap(lambda x: x.lower() if type(x) == str else x)

注意,if-else 语句可以像上面一样在 lambda 函数中编写。你可以在下面看到所有的名字现在都是小写!

来源:作者

假设我们想将类别(字符串格式)替换成相应的整数。我们可以使用 Applymap 函数来实现这一点吗?尽管 Apply 函数可能与此更相关,但我们仍然可以使用 Applymap 函数来实现相同的结果。

我们有一本字典,把性别,男性和女性分别映射到 0 和 1。

mapping = {"male":0, "female":1}df.applymap(mapping.get)

来源:作者

从上面的输出可以看出,如上所述,Applymap 函数将转换逻辑应用于每个变量中的每个数据点。因此,我们看到所有其他与“性别”变量无关的细胞都被替换为无。我们不希望这样。为了实现我们想要的,我们可以设计 lambda 函数,仅当单元格中的值是映射键之一时才替换这些值,在本例中,映射键是字符串“男性”和“女性”。

df.applymap(lambda x: mapping[x] if x in mapping.keys() else x)

来源:作者

现在我们看到,只有“性别”变量发生了变化,而其他变量保持不变。

聚合

最后但同样重要的是,与 Apply 和 Applymap 函数不同,Aggregation 函数返回一个新的 dataframe,其中包含用户指定的汇总统计信息。汇总汇总统计是指包括最大值、最小值、均值、中值和众数的统计。在这里,我们计算乘客的平均年龄、最大年龄和存活率。

df.groupby("Pclass").agg(avg_age = ("Age", "mean"),
                        max_age = ("Age", "max"), 
                        survival_rate = ("Survived", "mean"))

从上面的代码片段中可以看出,将 aggregation 函数与 Groupby 函数结合使用,可以成为计算不同数据点组的聚合的强大工具。

结论

在本文中,我使用了 Titanic Disaster 数据集来说明三个最常用的转换/广播函数的作用以及它们之间的区别。请继续关注我关于数据清理、机器学习、深度学习、自然语言处理等方面的更多文章。

如果你觉得这篇文章有帮助,请考虑通过以下链接注册 medium 来支持我: )

joshnjuny.medium.com

你不仅可以看到我,还可以看到其他作者写的这么多有用和有趣的文章和帖子!

关于作者

数据科学家。加州大学欧文分校信息学专业一年级博士生。

密歇根大学刑事司法行政记录系统(CJARS)经济学实验室的前研究领域专家,致力于统计报告生成、自动化数据质量审查、构建数据管道和数据标准化&协调。Spotify 前数据科学实习生。Inc .(纽约市)。

他喜欢运动、健身、烹饪美味的亚洲食物、看 kdramas 和制作/表演音乐,最重要的是崇拜我们的主耶稣基督。结帐他的 网站

拓宽视角,改变数据科学对“社会公益”的态度

原文:https://towardsdatascience.com/broadening-the-lens-and-changing-attitudes-of-data-science-for-social-good-297810d458f9

评估非营利部门的数据工作

中国云南红河哈尼族彝族自治州。图片作者。

主流数据科学项目侧重于数据结构、编程、算法、统计建模和机器学习。虽然预测泰坦尼克号上的幸存者已经成为数据科学培训中的一项仪式,但即使是最准确的模型也不会改变历史。虽然现成的数据集对技能培养很实用,但许多学生渴望解决现实世界的问题。在一所社会工作学校,许多学生来找我,寻找机会磨练他们的数据技能,并通过为“社会公益”而工作来建立他们的简历。

获得这种经验最直接的途径是与非营利组织一起启动一个项目。虽然存在大量的机会,但许多学生对从事清洁数据工作的前景感到失望,这些工作包括清理、准备和管理数据,而不是拓展数据科学方法的边界。我经常听说非营利组织迫切需要的数据工作类型不是“智力上有趣的”——这种态度将非营利组织的大部分数据科学支持归为周末黑客马拉松。

我尊重他人的学术兴趣。我认识到我是数据科学社区中基本技术的消费者。我永远不会突破数据科学方法的界限。成为数据科学社区的正式成员并不是我的愿望。我对解决社会和环境问题感兴趣,数据科学的策略、方法和工具为开展这类工作提供了独特的机会。

整合技术和领域专业知识

虽然集成技术和领域专业知识是数据科学定义的一部分,但许多程序并没有教如何做。因此,非营利部门的许多数据计划都需要技术专家提供低效且不可持续的数据科学解决方案。学生们需要意识到,当见解处于业务流程和组织文化中时,是可操作的。

除了组织已经在处理的数据之外,额外的分析可能不会产生更多的数据见解。更加强调整合技术和领域专业知识可以揭示有意义和可持续创新的杠杆点。例如,更复杂的分析可能没有帮助,但存在提高工作流程效率的机会。这些节省成本的选项在拥有传统数据系统的组织中非常多。自动化不是尖端的数据科学方法。尽管如此,他们仍然可以在组织内腾出宝贵的时间,让领域专家有更多的时间使用数据来解决需要专家判断的问题。

危地马拉蒂卡尔的树根。图片由作者提供。

重视数据工作

大多数非营利组织没有数据,甚至不需要机器学习模型或高级统计分析。我不记得上一次遇到 p 值或相关系数能够提供有用见解的用例是什么时候了。非营利部门的大多数问题都围绕着清理、准备和管理非营利部门中杂乱的真实世界的数据。相比之下,大多数学生希望深入统计分析和机器学习。Sambasivan 和他在 Google Research 的同事在他们的论文中对这种情况进行了仔细和批判性的关注:

Sambasivan,s . Kapania,h . high fill,Akrong,d . Paritosh,p .,& Aroyo,L. M. (2021 年 5 月)。“每个人都想做模型工作,而不是数据工作”:高风险人工智能中的数据级联。在2021 年中国计算机系统人的因素会议论文集(第 1-15 页)。

他们令人信服地指出,“数据工作在许多社会技术领域被广泛低估……高风险领域的数据质量差可能对弱势群体和环境产生巨大影响。”虽然他们专门围绕人工智能进行讨论,但他们的观点准确描述了非营利部门的数据科学。

我们需要摆脱当前被动的方法,不再将数据视为“繁重的工作”。我们需要积极关注数据卓越— 关注数据管道的实践、政治和人类价值观,通过使用流程、标准、基础设施和激励措施来提高数据的质量和神圣性… Sambasivan 等人(2021 年)

许多培训情况为学生提供的研究数据集与现实世界组织中的数据几乎没有对应关系。使用干净、扁平的文件并直接进入零假设统计测试的学生对管理数据的混乱和多样性感到惊讶。Sambasivan 和他的同事对训练提出了类似的批评:

人工智能领域的学位、文凭和纳米学位的大部分课程都集中在模型开发上[42],这使得毕业生对处理数据的科学、工程和艺术准备不足,包括数据收集、基础设施建设、数据文档和数据意义制作… Sambasivan 等人(2021)

要码,还是不要码?

由于用 R 和 Python 编写代码是数据科学培训计划的一个重要焦点,学生们渴望启动一个 Markdown 文件或笔记本,并开始编写代码。基于代码的解决方案是可重复研究的基础。然而,基于代码的解决方案通常不是非营利组织的最佳选择。在与非营利组织合作时,初露头角的数据科学家应该保持对业务流程动态本质的了解,无论其免税地位如何。

从这个角度来看,基于代码的解决方案对组织来说可能是不可持续的。例如,假设您使用 R 或 Python 自动化管理报告。当他们添加另一个数据流时会发生什么?他们能够调整 R 或 Python 代码来引入这些新数据吗?虽然我的实证研究涉及基于代码的解决方案,但我发现,对于非营利组织的日常数据工作来说,无代码和低代码的解决方案可能更快、更可持续。

中国算盘。图片由作者提供。

我鼓励学生们保持谨慎,不要盲目地将可再生研究的价值强加给非营利部门。对于大多数数据项目来说,再现性确实是必要的。但是,我们应该考虑再现性的,而不是把它看作一个非此即彼的问题。无代码和低代码的解决方案通常有一个配方,它充分描述了你如何从 A 点到达 B 点,但可能没有一套清晰定义每一步的计算机代码那么优雅。许多问题通常不需要这种详细程度。如果不考虑效率和可持续性,组织可能会因投资于可再生解决方案而损失宝贵的资源。

组织能力建设

当你有技术专长时,非营利组织的一些数据问题很容易解决。然而,培养组织能力来解决他们的数据问题通常是更好的解决方案。从这个角度来说,数据科学家的帽子需要换成数据老师的帽子。与“我自己做更快”的态度相比,组织可能会获得更大的长期收益同样,这一点又回到了理解业务环境中的数据。我遇到很多学生拿着 的解答找问题 。我鼓励寻求“社会公益”体验的学生理解组织最紧迫的问题,而不是是否存在运行回归分析或机器学习模型的机会。

Rube Goldberg 的自运营餐巾纸是在找问题的解决方案。图片来自维基百科。

接下来的步骤

无可否认,我对数据科学在非营利领域的发展持乐观和悲观态度。乐观地说,我看到了帮助非营利组织提高战略性使用数据的效率和有效性的巨大机会。然而,我们是否能吸引更多具有数据专业知识的人在非营利部门工作,需要我们如何评价数据工作的根本转变。有时,直接而优雅的数据问题解决方案可以让非营利组织受益匪浅。在学术界,受到重视的数据科学方法会产生行业资金。但是,我们必须小心,不要让资助成为影响的最终衡量标准。这是我悲观的一面。最终,希望将他们的技能应用于社会公益的学生必须首先理解组织的使命,然后是组织最紧迫的问题,最后是他们的数据。

我是密歇根大学的社会工作教授,对帮助学生做好准备和帮助非营利组织利用数据和信息技术更聪明地工作感兴趣,而不是更努力。如果您有兴趣了解非营利部门的数据创新,请关注我。

Bryan R. Vallejo 利用地理空间科学和实时数据来帮助生态保护

原文:https://towardsdatascience.com/bryan-r-vallejo-leverages-geospatial-science-and-real-time-data-to-help-ecological-conservation-3ff7ca8fb007

作者聚焦

“有时,我只是散散步,了解我所处的环境,并试图揭示模式。”

在 Author Spotlight 系列中,TDS 编辑与我们社区的成员谈论他们在数据科学领域的职业道路、他们的写作以及他们的灵感来源。今天,我们很高兴与 布莱恩·r·瓦莱约 分享我们的对话。

照片由布莱恩·r·瓦列霍提供

Bryan 在厄瓜多尔天主教大学获得地理和空间规划学士学位,后赴塔尔图大学攻读地理信息学硕士学位。他在赫尔辛基大学完成了 GIS 自动化专业的学位。作为数字地理实验室的前访问成员,他一直参与利用新的大数据源进行的专注于人类移动的研究。

在他专业化的一年中,他迷上了海洋野生动物迁徙及其与海洋生态地理变量的相互作用,并开发了 海洋野生动物跟踪器 ,这是一个专门用于实时跟踪和卫星数据检索的地理框架。

Bryan 目前在一家爱沙尼亚私营公司工作,开发带有移动定位数据的客户定制 GIS 解决方案。下班后,他会去上舞蹈课来激励自己,给自己充电。在业余时间,他支持加拉帕戈斯群岛的海洋保护项目,用海洋巨型动物遥测数据实现他的平台。

是什么激发了你对地理和数据交叉的兴趣?

我真的可以说,当我意识到我将成为一名地理学家的那一刻,是在我大学的第一堂地图学课上。我喜欢地图制作,因为这是一种很好的数据交流方式。在我的一生中,我一直被数字、信息学和工作流所吸引,所以数据是我的东西——但是当它成为地理空间数据时,它把我带到了一个全新的世界。

我在人口统计学实践中发现了地理空间处理,我对从空间单位角度发生的转换、聚合、计算和其他类型的数据处理着迷。最终的地图传达了关于社会状况的信息。我很高兴我能够完成这个创造性的分析过程。

当然,我遇到了挑战。当我想操作国家人口普查数据库时,我意识到我的桌面软件开始崩溃,这是我的极限。那时我有一个问题:接下来会发生什么?

答案是什么?

我想以最正式的方式应对我的挑战,所以我开始寻找专注于地理空间分析的研究生课程。幸运的是,我在世界的另一端得到了一个机会,我毫不犹豫的勇往直前,寻找我从地理学向地理信息学转型的下一波机会。

这听起来像是一个重大的转变。

说实话:这个过程中最困难的部分是离开我的祖国,进入一个充满日常挑战的新文化。但是从最艰难的时刻,我找到了继续发展自己的动力。

当我学习地理空间分析的编程语言时,我克服了自己的极限。蟒蛇是我的盔甲。我花了一年半的时间才在数据操作方面打下坚实的基础。到了第二年,我跳过地理空间物体,感觉就像水中的一条鱼。我发现了新工具,开发了流程,学习了技巧,还分享了许多教程。同样,我得到了开放科学的支持——所以我决定回馈社会。

所以现在我在这里,自动化地理空间过程,创建具有现代可视化的分析工作流,并尝试扩展我的分析。

最近,你最感兴趣的是哪种以数据为中心的项目——你希望在职业生涯的下一阶段关注哪些主题?

我已经看到了新一波的项目集中在实时 GIS 上。我喜欢深入这个似乎很有潜力的领域。这是一个地理空间工作流,从实时数据检索开始,然后是动态算法,最后是地图:一个对数据的每一次变化做出反应的漂亮漏斗,提供关于当前情况的见解。

我决定开始自己的实时 GIS 项目,并从 2021 年开始一直致力于此。实际上有很多选择,但我决定选择生态保护在接下来的几个月——我希望是几年——我想继续致力于我的野生动物跟踪实时监控系统,该系统已经有了不错的结果,并在现场实施。我热爱自然,对我来说,一切似乎都与野生动物和生态系统的变化联系在一起,这就像魔法一样。

更具体地说,我想在大数据可扩展性的算法开发和实施方面进一步发展自己。当然,我会添加一个漂亮的地图,这样最终用户可以清楚地看到消息。

我说的是我的个人愿望和我的爱好项目,但是当然,我也乐于看到在我的职业道路上会发生什么。我很想成为一个愿意大规模开发地理信息学解决方案的伟大团队中的一员,在这里我可以找到我的下一个层次。

是什么促使你开始公开写自己的作品?

在我的职业发展过程中,自动化地理空间过程成为一项轻松愉快的日常任务。我可以大大减少花在分析过程上的时间,并且能够在地图上找到见解并进行交流。

我脑子里积累了很多想法,很难继续做其他事情,因为我一直带着旧的想法。我需要解放他们,把他们从我的头脑中解放出来,当我写下我的第一个故事时,我感到如释重负。就好像我减轻了一些重量,我的头脑中有了更多的空间来容纳更多的想法。

我在 Medium 和 Data Science 上读到了一些不错的帖子,所以我决定加入并分享我的分析故事。我从开放科学中学到了很多,社区发展模式也很好很友好,所以我很高兴能给世界上其他人提供可以使用的材料。

对于选题,我可以说我有点主题化。我要做的第一件事是选择一个原始数据集,其中包含足够的信息来寻找见解。这取决于我的心情——例如,它可以是社会空间分析、环境、城市,或者仅仅是写我工作的野生动物跟踪系统的应用和使用。然后,我选择一个地理空间过程,引导我找到我想要传递的信息。最后,我创建了一个精心制作的地图,向人们传达最终的信息。

你有什么特别的技巧来激发你的创造力吗?

有时候,我只是走一走,了解我所处的环境,并试图揭示模式。城市是有生命的,看到它们的行为很奇妙。有一次,我对赫尔辛基有这么多人骑自行车感到震惊。我很好奇,想在一个过程中真正看到这种模式,所以我写了一个关于在夏天聚合自行车路线的故事

我感觉最激动的时刻是在故事的开头,因为我需要打开话题,降落在我的想法的路径上。最好给它时间。我把这个话题搁置了一个星期,直到我开始分析。对我来说,重要的不仅是主题,还有你的心情和你想讲述故事的地方。

你有什么建议给那些想写自己数据相关工作,但不知道从哪里开始或如何找到时间的人吗?

毫无疑问。对于有很好的数据相关工作,愿意和人分享的,我可以从两个角度来建议。

第一个是个人的。写作时找到你的时间和安宁。记住,故事是你思想的一面镜子,所以最好保持清晰。我知道工作可能会持续很长时间,很难找到时间写作。选择一个你感觉自由的日子,放松,喝茶,找个地方。

我可以说,我的许多良好开端都是在穿着睡衣的时候发生的。此外,倾听你的内心和沉默:这将帮助你组织你的想法。要系统,一天一天写。从介绍开始,休息。写正文,休息。分析,睡觉,设计你的结果,然后继续。哪怕每个月发一个故事,你也心满意足了。

第二个角度是技术性的。确保你正在写一篇引人入胜的介绍,并且你正在揭示这个主题及其与人类的相关性。然后,明确故事的目的。你可以陈述一个可以用一个分析过程解决的目标——没有必要包括很多。对我来说,一个好故事需要五到十五分钟的阅读时间。

对于分析,保持简单,突出技术水平,不要详尽无遗。如果你正在编码,找到一个好方法使代码可共享和可重用,这将使其他人感激。在故事的结尾,只要结束你在写作中打开的部分——一个引导你使用分析和解决你陈述的目标的段落。最后,最重要的一点:把你的帖子看一遍两遍,根据需要改正或澄清。你会觉得信息的传递有多好。

现在,我可能会重新倒满我的一杯茶...

无论是在你自己的子领域还是更广泛的数据科学领域,你希望在不久的将来看到什么变化?

嗯,我真的可以说地理空间科学的发展已经到了我希望的时刻。如果你看一看,大多数地理空间教育材料是开放的,许多资源是开放的,并且在社交媒体上有一个广泛的地理空间社区可以回应你的询问和疑问。接下来会发生什么?

有时我会感到震惊:我可以用一种感觉就像一个刚刚学会如何打开电视的孩子的方式连接到卫星。尽管我觉得这很离谱,但我后来意识到,有人在创造我正在使用的工具。因此,这是一个受到社区支持的知识领域。

地理空间科学的未来是什么?对我来说,地理科学的最后一个创新学派是时间地理学,它涉及到以时间的功能作为其第三维来研究地理现象。我很认真地对待这个未来,所以在过去的两年里,我一直专注于时空分析。

当然,我已经看到了现代 GIS,这是一个引导地理空间科学走向全球和可扩展水平的新趋势。它指的是使用可组合和开发的开放工具来分析空间数据,并以自动化的方式提供有用的见解。老实说,我不喜欢这个新趋势的名字,因为它表明这是最后的和最终的范式。相反,我会根据它的目的来命名它,即在全球范围内扩展。所以我认为在不久的将来真正的新趋势应该被命名为云 GIS。

云 GIS 可以真正在全球范围内扩展,并提供计算资源来运行能够洞察空间数据的模型。我也认为“实时”是这一新趋势的一部分。对于(地理空间)数据科学的未来,我会说它的道路不应该集中在统计模型上,因为它们从 70 年代就已经被开发出来了。如今有更多创新的应用领域,因此地理空间科学的视野应集中于其在现实生活中的使用,并提高人们在日常生活中的舒适度。这个新趋势的迷人之处在于它可以被任何人访问。

要了解布莱恩的更多工作,并了解他的最新项目,请点击 MediumTwitter 关注他。如果你没有读过他以前的文章,这里有一个来自 TDS 档案的样本:

想和广大观众分享一些你自己的作品吗?我们很乐意收到你的来信。

为了长度和清晰起见,编辑了这个 Q。

使用 Django Rest 框架构建博客网站——概述(第 1 部分)

原文:https://towardsdatascience.com/build-a-blog-website-using-django-rest-framework-overview-part-1-1f847d53753f

让我们使用 Django Rest 框架构建一个简单的博客网站,以了解 DRF 和 REST APIs 如何工作,以及我们如何添加任何前端框架来使用我们使用 DRF 生成的 API

真诚媒体Unsplash 上拍摄的照片

乡亲们好;我希望你们都很好!在本系列文章中,我们将构建一个简单的博客网站,它具有创建博客、编辑博客、投票支持博客、评论博客和查看特定用户的博客等功能。

我们还可以实现额外的功能,比如关注作者,根据他们关注的人定制用户提要,以及根据他们喜欢阅读的文章类型建议用户关注新作者。

要继续本系列,您需要对 Django 的工作原理有一个基本的了解。假设您想学习更多关于 Django 的知识,以及如何使用 Django 构建 web 应用程序。在这种情况下,我建议您阅读我的文章系列,关于使用 Django 构建社交媒体网站和使用 Django 构建求职门户。我已经制作了完整的网站来帮助你在比常规待办应用更复杂的东西上实现 Django。

因此,本系列的第一部分将只关注基础知识,并将设置项目基础知识。在接下来的部分中,我们将深入探讨为应用程序的不同方面构建模型、视图和序列化器的细节。

因此,首先,让我们了解什么是 Django Rest 框架,以及为什么它是构建我们的应用程序后端的好框架。

为什么选择 Django Rest 框架?

Django Rest 框架(DRF)是构建 web APIs 的强大工具。它建立在流行的 Django web 框架之上。它允许开发人员快速创建各种客户端(包括 web、移动和桌面应用程序)可以使用的 API。DRF 使得创建具有复杂功能的 API 变得很容易,比如身份验证、权限和数据验证,并提供了一套丰富的工具来构建和测试 API。有了 DRF,开发人员可以专注于构建他们的应用程序逻辑,而不是花时间在实现 API 的细节上。这使得它成为构建可伸缩和可维护的 web APIs 的绝佳选择。

DRF 还提供了一个可浏览的 API 接口,允许开发者快速测试和交互他们的 API。这对于调试、故障排除以及向可能使用该 API 的其他开发人员交付文档和示例非常有用。DRF 还支持各种序列化选项,允许开发人员快速返回不同格式的数据,如 JSON 或 XML。总的来说,Django Rest 框架对于任何希望构建健壮可靠的 web APIs 的人来说都是一个有价值的工具。

如果你想了解更多关于 Django Rest 框架(DRF)以及如何使用它,官方文档是最好的地方。

构建项目

所以,让我们开始构建项目。首先,我们将创建一个新文件夹来开始我们的项目。因此,我创建了一个父文件夹,并在其中构建了两个名为后端和前端的文件夹。我们将只在后端文件夹中做我们所有的工作。稍后,在下面的文章中,我们将构建一个工作前端,并将其连接到我们的后端,以拥有一个全栈工作网站。但那是未来的事;目前,我们的重点将只在 DRF(后端部分)。

因此,在后端文件夹中,我们将为我们的项目创建一个虚拟环境。为了创建虚拟环境,我更喜欢使用 pipenv ,因为我发现它很容易使用,并且它维护一个 pip 文件而不是 requirements.txt 文件,这使得 Pythonic 项目更容易。

下面给出了用于创建虚拟环境的命令。如果已经安装了 pipenv,应该跳过第一行。

pip install pipenv
pipenv shell
activate

在创建并激活虚拟环境之后,我们需要安装项目的依赖项。我们现在只需要为我们的项目安装姜戈和 DRF。如果我们需要更多的依赖项,我们可以稍后安装它们。

pipenv install django djangorestframework

运行上面的代码行将在您的虚拟环境中安装这两个依赖项。我们已经准备好创建我们的应用程序并进行初始设置。

接下来,我们在后端文件夹中创建 Django 项目。为此,我们在 shell 上编写以下命令。

django-admin startproject blog .

Django 中的startproject命令末尾的点用来指定应该创建新 Django 项目的当前目录。这是命令行工具中的一个标准约定。)指的是当前目录。使用点号,我们告诉 Django 在当前目录中创建新的项目文件,而不是在子目录或其他位置。如果您希望出于组织目的将项目文件保存在同一个目录中,这将非常有用。

接下来,我们还要创建两个应用程序— 用户帖子 。第一个将处理与用户相关的活动,另一个将保存与博客文章相关的逻辑。在我们的例子中,我们将主要使用 posts 应用程序,在 users 应用程序中几乎没有逻辑,因为我们将使用默认的 Django 用户模型,不会创建任何用户配置文件或自定义用户模型。但是,我仍然把它分开,因为如果我们想在未来实现跟随作者功能或类似的功能,我们会把所有这些逻辑放在这个应用程序中。

为了创建这两个应用程序,我们将编写以下命令:

python manage.py startapp users
python manage.py startapp posts

现在,我们的文件结构如下所示:

后端文件夹结构

在这一部分,我们不会进入用户和文章文件夹,因为这是设置部分;我们只处理博客文件夹。

进入 blogs 文件夹,我们会发现文件结构如下:

博客文件夹

现在,我们将只处理 settings.pyurls.py 文件。首先,我们将把 文件移入 settings.py 文件。

在设置中,我们将在INSTALLED_APPS部分添加rest_framework,如下所示:

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",
    "posts",
    "users",
]

正如我们在上面的代码块中看到的,我们还在同一部分添加了postsusers,这让我们的 Django 项目知道我们已经创建了这两个应用程序,并将在项目中使用它们。

接下来,我们将在 settings.py 文件中添加这段代码来定义我们的 API 的分页。

REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 10
}

现在,让我们移动到 urls.py 文件。我们将添加可浏览的 API 接口 URL,这样我们可以快速测试我们的 API。首先,我们将从django.urls导入pathinclude,我们将使用它们来定义 URL。

from django.urls import path, include

接下来,在 URL 模式中,我们将添加我们的可浏览 API 路径,如下所示:

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
]

上面代码的第一行是访问 Django 提供的管理面板,第二行是 DRF 提供给我们的可浏览 API 接口。

接下来,我们将迁移数据库(Django 为我们提供了一个嵌入式 SQLite DB)。为此,我们将运行以下命令:

python manage.py makemigrations
python manage.py migrate

我们可以运行下面的命令来启动本地服务器,并测试是否一切都已完美安装。

python manage.py runserver

在运行这个命令时,我们得到了我们网站的一个本地服务器,我们可以打开它来访问我们的网站。我们不会得到任何东西,因为我们没有在应用程序中定义任何逻辑。

所以,我们都准备好开始构建我们的项目了。从下一部分开始,我们将开始构建项目的核心逻辑,我们将再用两个部分来完成后端部分,然后开始构建项目的前端,使其成为一个优秀的全栈网站。

我希望大家和我一样对下面的部分感到兴奋,因为我们希望学习并使用 Django Rest 框架构建一个简单的博客网站,并将其连接到一个工作前端。

DRF 系列的下一部分可以在这里找到:

感谢您的阅读!如果你喜欢读我的文章,我建议你看看我的其他文章:

https://javascript.plainenglish.io/build-an-e-commerce-website-with-mern-stack-part-1-setting-up-the-project-eecd710e2696 https://javascript.plainenglish.io/build-a-blog-app-with-react-intro-and-set-up-part-1-ddf5c674d25b

使用 Django Rest 框架——用户应用程序构建一个博客网站(第 2 部分)

原文:https://towardsdatascience.com/build-a-blog-website-using-django-rest-framework-part-2-be9bc353abf3

在第二部分中,我们将处理构建用户相关的模型和视图,并将测试用户相关的 API。

真诚媒体Unsplash 上拍摄的照片

大家好,我希望你们都过得很好,并且喜欢这个 DRF 文章系列的第一部分。在本系列的第一部分中,我们处理了设置这个项目的基础,并且您已经对我们将要构建的项目有了一个大致的了解。

如果你还没有读第一部分,我建议你先去读一读,然后再回到这一部分。以下是该系列的第一部分:

在这个系列的第二部分,我们将处理完整的users应用程序,然后,在第三部分,我们将完成posts应用程序来完成这个网站的后端。

更新:当我第一次写这篇文章的时候,我并没有在第二部分中包含一个合适的认证系统。仅支持注册用户和查看所有用户,但如果我们想与前端连接,这是不够的。由于我们想为我们的后端应用程序建立一个工作前端,我们想提供一个登录功能,因为我们不能使用可浏览的 API 登录按钮登录时,我们使用前端。

因此,我在本文的一个单独的部分中添加了应用程序所需的完整身份验证,显示了为了使它工作,我们需要做的所有必要的更改。所以,我没有在中间做改动,因为我觉得最好在一个单独的部分做,因为我们只会添加一些东西来增加所需的功能。

我们还将看到如何在构建 API 的同时测试它们,并且我们将使用 Django Rest 框架提供的可浏览 API 接口来完成这项工作。

首先,让我们创建一个超级用户来访问 Django 应用程序的管理面板。为此,我们将在终端中使用以下命令:

python manage.py createsuperuser

然后,它会询问您的用户名、电子邮件和密码。在您输入所有这些细节之后,它将使用这些凭证创建一个超级用户。您可以使用这些详细信息登录到应用程序,以便能够完全访问网站上的所有功能,还可以访问管理面板。

那么,现在让我们进入users应用程序。我们可以看到这个文件夹里面已经创建了很多文件,比如models.pyviews.pyadmin.py等。

因此,由于我们将使用默认的 Django 用户模型,而不是创建任何定制的用户模型或用户配置文件,因此我们不会在 models.py文件中写任何东西。

因此,我们将首先编写序列化程序。创建一个名为serializers.py的新文件。我们将在这里序列化我们默认的 Django 用户模型。

什么是序列化,我们为什么需要它?

序列化是将复杂对象转换为易于存储或传输的数据格式的过程。在 Django Rest 框架的上下文中,序列化用于将 Django 模型中的数据转换成可以通过互联网发送并被其他应用程序使用的格式。

以下是为什么序列化在 Django Rest 框架中必不可少的一些具体原因:

  1. 它允许您将 Django 模型和查询集转换成 JSON 或 XML 之类的格式,这些格式很容易被前端的其他应用程序或 JavaScript 框架使用。
  2. 它允许您精确地控制 API 公开什么数据以及如何格式化数据。这对于安全性以及确保 API 的一致性和易用性非常重要。
  3. 它允许您轻松地将复杂的数据结构转换成易于存储或传输的格式。这在处理大型数据集或处理不同对象之间有大量关系的数据时特别有用。

总的来说,序列化是 Django Rest 框架的一个关键特性,它使您能够快速构建各种应用程序和框架都可以使用的 API。

serializer . py

因此,在serializers.py文件中,我们将序列化默认的 Django 用户模型。因此,我们将为我们的用户模型使用三个字段— usernameemailpassword

所以,让我们先看看代码,然后我们就能理解代码的所有部分。

from rest_framework import serializers
from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(min_length = 8, write_only = True)

    class Meta:
        model = User
        fields = ('username', 'email', 'password')

正如您在上面的代码块中看到的,首先,我们将从rest_framework导入serializers,并且我们还从 Django 认证模型导入默认的User模型。

接下来,代码定义了一个UserSerializer类,用于将User模型的实例转换成一种易于序列化的格式,比如 JSON 或 XML。UserSerializer类继承自ModelSerializer类,后者提供了很多将 Django 模型自动转换成可序列化格式的功能。

UserSerializer类定义了一个password字段,它是一个最小长度为 8 个字符的CharFieldwrite_only参数被设置为True,这意味着密码字段仅在将数据写入序列化程序时使用,但不会包含在序列化输出中,因为我们不希望密码被泄露。

UserSerializer类中的Meta类指定了序列化器应该用于的model(在本例中是User型号),以及应该包含在序列化输出中的fields列表。在这种情况下,usernameemailpassword字段将包含在序列化输出中。

接下来,我们将移动到views.py文件,在这里我们将使用序列化数据并编写我们的users应用程序的主要逻辑。这是决定我们的应用程序如何处理不同请求的文件。

views.py

views.py文件中,我们将编写与用户相关的getpost请求——检索所有用户的列表并注册一个新用户。让我们先看看代码,然后我们将详细讨论代码是如何实现的。

from rest_framework.views import APIView
from rest_framework.response import Response
from django.contrib.auth.models import User
from .serializers import UserSerializer

class UsersAPIView(APIView):
    def get(self, request):
        users = User.objects.all()
        serializer = UserSerializer(users, many = True)
        return Response(serializer.data)

    def post(self, request):
        serializer = UserSerializer(data = request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status = 201)
        return Response(serializer.errors, status = 400)

因此,首先,我们导入所需的依赖项,并引入我们将在视图中使用的User模型和UserSerializer

接下来,代码定义了一个UsersAPIView类,它处理对 Django Rest 框架 API 的 HTTP 请求。UsersAPIView类继承了APIView类,后者为处理 HTTP 请求和返回响应提供了许多有用的功能。

UsersAPIView类定义了两个方法:getpost。当 API 接收到 GET 请求时,调用get方法。它从数据库中检索所有的User对象,使用一个UserSerializer将数据转换成可序列化的格式,并向客户端返回一个包含序列化数据的响应。

此外,正如我们所见,当调用get方法时,many参数被设置为UserSerializer中的True,这表明序列化程序应该会处理多个User对象。这很重要,因为它允许UserSerializer正确处理数据并返回包含所有User对象的响应。

如果many没有设置为True,那么UserSerializer一次只会处理一个User对象,而get方法的响应只会包含一个User对象,而不是数据库中的所有User对象。

当 API 接收到 POST 请求时,调用post方法。它使用UserSerializer将请求的数据转换成一个User对象,验证数据,如果数据有效,将对象保存到数据库,并向客户机返回一个带有序列化数据的响应。如果数据无效,它将向客户端返回一个错误响应。

接下来,我们必须为我们刚刚创建的视图定义urls,这将允许我们使用可浏览的 API 接口来测试我们的 API。

urls.py

我们将不得不修改两个urls.py文件,一个在users应用中,另一个在blog应用中。在users应用程序中,我们将创建urls.py文件,并且我们将为刚刚创建的视图创建一个 URL。

所以,users app 里面的urls.py文件的内容是:

from django.urls import path, include
from .views import UsersAPIView

urlpatterns = [
    path('', UsersAPIView.as_view()),
]

这段代码定义了 Django 应用程序的 URL 模式列表。在 Django 中,URL 模式是一个特定的字符串,它指定了特定资源在 web 上的位置。urlpatterns列表定义了一组可以与输入 URL 匹配的模式,并指定应该调用哪个视图函数来处理每个模式。

在这段代码中,urlpatterns列表定义了一个匹配空字符串的 URL 模式(即应用程序的根 URL)。这个模式被映射到UsersAPIView视图,这意味着对应用程序根 URL 的任何请求都将由UsersAPIView视图处理。

使用as_view()方法调用UsersAPIView视图,该方法创建视图的一个实例并返回一个函数,可以调用该函数来处理传入的请求。这个函数然后被传递给path()函数,后者创建一个 URL 模式,可以与传入的 URL 进行匹配。

接下来,在blog应用程序的urls.py文件中,这是我们 Django 应用程序的根应用程序,我们将添加以下代码:

....
from users import urls as users_urls

urlpatterns = [
    ....
    path("api/users/", include(users_urls)),
]

....只是用于表示的目的,因为我们还有几行代码,就像我们在上一部分添加的那样。

path()函数有两个参数:一个字符串指定匹配的模式,另一个是视图函数或其他 URL 模式,应该调用它来处理匹配模式的请求。在这段代码中,include()函数作为第二个参数传递,这意味着path()函数将包含在users_urls变量中定义的 URL 模式。

这允许我们将我们的 URL 模式组织到多个文件中,使我们的代码更干净,更容易维护。因此,正如我们所见,我们已经在users应用程序中的urls.py文件中定义了我们的user_urls,我们将在这里使用它。

所以,包含users部分的blogs app 的完整urls.py文件为:

from django.contrib import admin
from django.urls import path, include
from users import urls as users_urls

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
    path("api/users/", include(users_urls)),
]

测试 API

现在,让我们通过运行服务器来测试我们刚刚创建的与用户相关的 API。因此,我们将使用下面的命令来启动我们的应用程序。

python manage.py runserver

它将执行一些系统检查并启动服务器。它将显示运行您的应用程序的 URL。转到那个 URL,因为我们想要测试用户 API,所以我们将转到[/api/users/](http://127.0.0.1:8000/api/users/)部分。

转到这个 URL,我们会看到它显示了我们的应用程序中的用户列表和添加新用户的 POST 选项。另外,在右上方,我们可以看到一个登录选项。

DRF 可浏览 API —用户 API —(图片由作者提供)

接下来,如果我们点击登录按钮,我们会看到如下页面:

DRF 登录界面—(图片由作者提供)

在这里,我们可以使用之前创建的凭证登录到应用程序。

然后,我们再次被带到 DRF 页面。现在,我们可以尝试使用 POST 按钮向应用程序添加一个新用户。为此,我们将以 JSON 格式提供新的用户数据,如下所示:

{
   "username": "Satyam",
   "email": "satyam@gmail.com",
   "password": "testing@123"
}

我们将在提供的Content部分添加上面的 JSON,然后我们将单击 POST 按钮向我们的应用程序添加一个新用户。然后,它将刷新页面并显示添加的新用户。

如果我们再次单击 GET 按钮,我们可以看到用户列表,它现在包含两个条目,第一个是我们之前创建的超级用户,第二个是我们刚才创建的用户。

这将是我们的应用程序在这个阶段的用户列表:

[
    {
        "username": "Shubham",
        "email": "shubhamstudent5@gmail.com"
    },
    {
        "username": "Satyam",
        "email": "satyam@gmail.com"
    }
]

您还可以访问管理面板来查看在我们的应用程序中注册的用户,并编辑他们的详细信息。

添加完整的身份验证部分

首先,我们将导入几个库,我们将要求它们包含一个合适的认证系统,该系统将使用 JWT (JSON Web 令牌)工作。

因此,我们将从导入这些库开始:

pipenv install django-cors-headers
pipenv install djangorestframework-simplejwt

现在,让我们进入应用程序的blogs应用程序中的settings.py文件。

# add this import
from datetime import timedelta

# at last add these two in the INSTALLED_APPS section
INSTALLED_APPS = [
    ...,
    "rest_framework_simplejwt.token_blacklist",
    "corsheaders",
]

# add the cors middleware
MIDDLEWARE = [
    ...,
    "corsheaders.middleware.CorsMiddleware",
]

# update this part to add the default authentication class to be used
REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 10,
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ),
}

# add this
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=50),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
    'UPDATE_LAST_LOGIN': False,

    'ALGORITHM': 'HS256',

    'VERIFYING_KEY': None,
    'AUDIENCE': None,
    'ISSUER': None,
    'JWK_URL': None,
    'LEEWAY': 0,

    'AUTH_HEADER_TYPES': ('Bearer',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
    'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',

    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'token_type',
    'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',

    'JTI_CLAIM': 'jti',

    'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
    'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
    'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}

# add this line
CORS_ALLOW_ALL_ORIGINS = True

…表示在我们刚刚添加的代码之上还有更多代码。所以,我们只是在我们的settings.py文件中添加了一些东西。

rest_framework_simplejwt.token_blacklist提供了一种简单的方法,使用SimpleJWT库将 Django 项目中的 JSON Web 令牌(jwt)列入黑名单。SimpleJWT库是 Django REST 框架的一个 JSON Web 令牌认证库。

当用户注销应用程序时,通常会使他们的 JWT 失效,这样就不能再用它来验证将来的请求。这可以通过将 JWT 添加到令牌黑名单来实现。任何随后的要求,提出了黑名单 JWT 将被拒绝。

将 CORS 头文件添加到 Django 项目中很容易。它允许您指定允许哪些源向您的服务器发出请求,以及在这些请求中允许哪些 HTTP 方法和头。

JWTAuthentication类是SimpleJWT库的一部分,后者是 Django REST 框架的 JSON Web Token (JWT)认证库。它允许您使用 jwt 验证 API 请求。

SIMPLE_JWT设置是用于配置SimpleJWT库的字典,该库是 Django REST 框架的 JSON Web Token (JWT)认证库。它允许我们使用 jwt 认证 API 请求。

下面是对SIMPLE_JWT字典中每个键的简要解释:

  • ACCESS_TOKEN_LIFETIME:访问令牌的生命周期,这些令牌是用于认证 API 请求的 jwt。这被指定为一个timedelta对象。
  • REFRESH_TOKEN_LIFETIME:刷新令牌的生命周期,是用来获取新的访问令牌的 jwt。这被指定为一个timedelta对象。
  • ROTATE_REFRESH_TOKENS:一个布尔值,指示当刷新令牌用于获取新的访问令牌时,它们是否应该被循环(无效并替换为新的令牌)。
  • BLACKLIST_AFTER_ROTATION:布尔值,表示是否应将循环刷新令牌添加到令牌黑名单。
  • UPDATE_LAST_LOGIN:boolean,表示当使用刷新令牌获取新的访问令牌时,是否应该更新用户的last_login字段。
  • ALGORITHM:应该用于签署 jwt 的算法。这必须是有效的 JWT 签名算法。
  • VERIFYING_KEY:应该用来验证 jwt 签名的密钥。
  • AUDIENCE:jwt 应该面向的受众。
  • ISSUER:jwt 应该发行的发行方。
  • JWK_URL:JSON Web Key Set(JWKS)的 URL,可用于验证 jwt 的签名。
  • LEEWAY:验证 jwt 到期时间时应该允许的余量。
  • AUTH_HEADER_TYPES:一组字符串,指定JWTAuthentication类应该接受的Authorization头的类型。
  • AUTH_HEADER_NAME:应该由JWTAuthentication类使用的Authorization头的名称。
  • USER_ID_FIELD:用户模型上用于获取用户 ID 的字段名称。
  • USER_ID_CLAIM:应该用于获取用户 ID 的 JWT 有效负载中声明的名称。
  • USER_AUTHENTICATION_RULE:根据用户 ID,使用函数名对用户进行认证。
  • AUTH_TOKEN_CLASSES:字符串元组,指定应该用来创建 jwt 的类。
  • TOKEN_TYPE_CLAIM:JWT 有效载荷中声明的名称,该名称应该用于指定 JWT 的类型。
  • TOKEN_USER_CLASS:类名应该用来表示与 JWT 关联的用户。
  • JTI_CLAIM:应该用于存储 JWT ID 的 JWT 有效载荷中声明的名称。
  • SLIDING_TOKEN_REFRESH_EXP_CLAIM:JWT 有效负载中声明的名称,该名称应该用于存储滑动令牌的刷新到期时间。
  • SLIDING_TOKEN_LIFETIME:滑动令牌的生命周期,它是具有动态过期时间的 jwt,每次使用令牌对请求进行身份验证时都会更新。这被指定为一个timedelta对象。
  • SLIDING_TOKEN_REFRESH_LIFETIME:滑动令牌刷新令牌的生存期,用于获取新的滑动令牌。这被指定为一个timedelta对象。

接下来,我们将转到users应用程序,为使用 JWT 令牌的登录支持添加必要的内容。

因此,我们将首先更新我们的serializers.py文件。下面是相同的更新代码。

from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(min_length = 8, write_only = True)

    class Meta:
        model = User
        fields = ('username', 'email', 'password')

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        token['username'] = user.username
        token['email'] = user.email
        return token

如我们所见,我们的UserSerializer保持不变。我们只添加了MyTokenObtainPairSerializer类。

MyTokenObtainPairSerializer类是来自SimpleJWT库的TokenObtainPairSerializer的子类,该库是 DRF 的 JWT 认证库。它用于序列化和反序列化令牌,这些令牌用于使用 jwt 对 API 请求进行身份验证。

MyTokenObtainPairSerializer类覆盖了基类TokenObtainPairSerializerget_token方法。get_token方法负责生成 JWT,当客户端验证一个请求时返回给客户端。

在被覆盖的get_token方法中,super().get_token(user)调用将调用基类TokenObtainPairSerializerget_token方法,并返回它生成的 JWT。然后通过将user对象的usernameemail字段添加到 JWT 有效载荷来修改 JWT。然后返回修改后的 JWT。

接下来,我们移动到views.py文件,在这里我们将为令牌部分添加几行。

from rest_framework.views import APIView
from rest_framework.response import Response
from django.contrib.auth.models import User
from .serializers import UserSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
from .serializers import MyTokenObtainPairSerializer

class UsersAPIView(APIView):
    def get(self, request):
        users = User.objects.all()
        serializer = UserSerializer(users, many = True)
        return Response(serializer.data)

    def post(self, request):
        serializer = UserSerializer(data = request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status = 201)
        return Response(serializer.errors, status = 400)

class MyTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer

正如我们所见,我们只添加了一个使用MyTokenObtainPairSerializer类的新类MyTokenObtainPairView

接下来,我们移动到urls.py文件,并为登录和令牌刷新添加新的访问端点。修改后的urls.py文件如下:

from django.urls import path, include
from .views import UsersAPIView, MyTokenObtainPairView
from rest_framework_simplejwt.views import TokenRefreshView

urlpatterns = [
    path('', UsersAPIView.as_view()),
    path('login/', MyTokenObtainPairView.as_view()),
    path('token/refresh/', TokenRefreshView.as_view()),
]

如我们所见,我们添加了两个不同的urlpatterns,分别用于登录和刷新令牌。

TokenRefreshView视图允许我们通过向视图发送带有刷新令牌的请求来刷新 JSON Web 令牌(JWT)。

JWT 刷新流程的工作方式如下:

  1. 客户端向服务器认证,并接收访问令牌和刷新令牌。
  2. 客户端使用访问令牌向受保护的资源发出请求。
  3. 如果访问令牌已过期,服务器会响应 401 未授权错误。
  4. 然后,客户机用刷新令牌向TokenRefreshView视图发出请求。
  5. 如果刷新令牌有效,视图向客户端发布新的访问令牌和刷新令牌。

现在,我们可以使用可浏览的 API 接口或 API 测试应用程序(如 Postman)来测试这种身份验证。我推荐 Postman,因为它的功能更加丰富,并且允许您轻松地提供访问令牌来请求访问任何受保护的视图。

所以,这就是这部分的全部内容。在下一部分,我们将处理这个应用程序的posts应用程序,并将完成这个系列的后端部分。

我希望你喜欢阅读这篇文章,并学到一些新的和令人兴奋的东西。我将很快写下一部分,一旦它们出版,我会在这里把它们连接起来。

该系列文章的下一部分可以在下面找到:

读完这篇文章后,我推荐阅读以下系列文章:

https://javascript.plainenglish.io/build-an-e-commerce-website-with-mern-stack-part-1-setting-up-the-project-eecd710e2696

使用 Django Rest 框架——Posts 应用程序构建一个博客网站(第 3 部分)

原文:https://towardsdatascience.com/build-a-blog-website-using-django-rest-framework-posts-app-part-3-7334f75983fc

在第三部分中,我们处理应用程序的整个 posts 应用程序,从而完成了应用程序的后端

真诚媒体Unsplash 上拍摄的照片

大家好,我希望你们都过得很好,并且喜欢这个 DRF 文章系列的前两部分。在本系列的第一部分中,我们处理了设置这个项目的基础知识,你们都已经对我们将要构建的项目有了一个大致的了解,而在第二部分中,我们处理了我们的应用程序的users应用程序,在那里我们为我们的users部分编写了序列化程序和视图

如果您还没有阅读这些部分,我强烈建议您先阅读前两部分,然后再回到本文。

在本系列的第三部分,我们将处理 Django 应用程序的完整的posts应用程序,从而完成我们应用程序的完整后端。我们还将使用可浏览 API 接口测试 API,就像我们测试users应用程序一样。所以,让我们直接进入posts应用程序,并开始构建它。

因此,当我们移动到posts文件夹时,我们会看到我们拥有的文件与我们移动到该文件夹时为users应用程序所拥有的文件相同。首先,我们将进入models.py文件来构建我们的模型。

models.py

先从models.py文件开始吧。我们将在这里定义我们的数据库模型。因此,对于posts部分,我们将有三种不同的模型— PostUpvoteComment

我们首先将所需的依赖项导入到我们的文件中,如下所示:

from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse

正如我们所看到的,我们已经导入了默认的User模型,还从 Django 导入了modelsreverse。这些将有助于我们建立模型。

因此,我们将从Posts型号开始。让我们先看看代码,然后我们会明白这些行的意义。

class Post(models.Model):
    user = models.ForeignKey(User, on_delete = models.CASCADE)
    title = models.CharField(max_length = 100)
    body = models.TextField()
    created = models.DateTimeField(auto_now_add = True)
    updated = models.DateTimeField(auto_now = True)
    upvote_count = models.IntegerField(default = 0)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('post-detail', kwargs = {'pk': self.pk})

上面的代码定义了一个从 Django 的models.Model类继承而来的Post类。这使得Post类成为一个 Django 模型,允许它保存在数据库中并从数据库中检索。

Post类有几个字段,包括usertitlebodycreatedupdatedupvote_count。每个字段都使用 Django 的models模块中的一个类来定义,该类指定了每个字段将存储的数据类型。例如,title字段是一个CharField,它将存储博客文章的标题,created字段是一个DateTimeField,它存储文章创建的时间。

user字段被定义为ForeignKey字段,这意味着它是对默认 Django User模型的引用。on_delete参数指定如果删除了引用的User对象,那么Post对象会发生什么。这里,on_delete参数被设置为models.CASCADE,这意味着如果删除了User对象,那么Post对象也将被删除。

ForeignKey字段允许将Post链接到特定的User对象,可以使用post.user属性访问该对象。这可用于获取创建Post的用户的用户名,或仅允许创建Post的用户编辑或删除它。

upvote_count字段将存储博客文章从用户那里获得的支持票数。默认值已经被设置为零,这意味着当一个新的Post对象被创建时,upvote_count被设置为零。当用户点击博文上的 upvote 按钮时,upvote_count将会增加。

__str__方法是一个特殊的方法,它定义了一个Post对象应该如何被表示为一个字符串。在这种情况下,该方法返回Posttitle

get_absolute_url方法返回Post对象的详细页面的 URL。Django 用这个来确定对象保存到数据库时的 URL。

接下来,我们将研究将在我们的应用程序中存储所有 upvotes 的Upvote模型。让我们看看这个模型,然后我会解释这个代码的意义。

class Upvote(models.Model):
    user = models.ForeignKey(User, related_name = 'upvotes', on_delete = models.CASCADE)
    post = models.ForeignKey(Post, related_name = 'upvotes', on_delete = models.CASCADE)

这段代码定义了一个继承自 Django 的models.Model类的Upvote类,就像我们之前讨论的Post类一样。

Upvote类有两个字段:userpost。两个字段都被定义为ForeignKey字段,这表明它们分别是对UserPost对象的引用。related_name参数指定了用于访问反向关系的名称。例如,如果一个User对象user投了几个Post对象的赞成票,那么user.upvotes属性将返回一个user投了赞成票的Post对象的 queryset。

两个字段的on_delete参数被设置为models.CASCADE,这意味着如果删除了Upvote对象引用的UserPost对象,则Upvote对象也将被删除。

这个类用于跟踪哪些用户对哪些帖子投了赞成票。我们将使用它来防止一个用户不止一次地向上投票一个Post,或者我们也可以使用它来检索已经向上投票给一个给定的Post的用户列表,尽管我们不会在我们的应用程序中实现后者。但是,正如我们所看到的,这很容易做到,您肯定可以尝试实现它来改进应用程序。

接下来,我们移动到最后一个模型类Comment,它将在我们的应用程序中存储博客帖子上的所有评论。相同的代码如下。

class Comment(models.Model):
    user = models.ForeignKey(User, related_name = 'comments', on_delete = models.CASCADE)
    post = models.ForeignKey(Post, related_name = 'comments', on_delete = models.CASCADE)
    body = models.TextField()
    created = models.DateTimeField(auto_now_add = True)

    def __str__(self):
        return self.body

代码定义了一个继承自 Django 的models.Model类的Comment类,就像PostUpvote类一样。

Comment类有四个字段:userpostbodycreateduserpost字段被定义为ForeignKey字段,这意味着它们分别是对UserPost对象的引用。两个字段的related_name参数指定了用于访问反向关系的名称。例如,如果一个User对象user写了几个Comment对象,user.comments属性将返回一个user写的Comment对象的 queryset。

body字段是一个TextField,它存储了评论的主要内容。

created字段是一个DateTimeField字段,存储Comment对象创建的日期和时间。auto_now_add参数设置为True,这意味着Comment对象的created字段将自动设置为对象首次创建时的当前日期和时间。

__str__方法是一个特殊的方法,它定义了一个Comment对象应该如何被表示为一个字符串。在这种情况下,该方法返回Commentbody,这是注释的主要内容。

Comment类用于确定哪些用户评论了一个特定的Post对象。我们将使用它来显示博客帖子上的评论以及用户的姓名。此外,这可以用于仅允许创建评论的用户编辑或删除评论,尽管我们还没有在当前应用程序中实现这一部分。

models.py部分的完整代码如下:

from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse

class Post(models.Model):
    user = models.ForeignKey(User, on_delete = models.CASCADE)
    title = models.CharField(max_length = 100)
    body = models.TextField()
    created = models.DateTimeField(auto_now_add = True)
    updated = models.DateTimeField(auto_now = True)
    upvote_count = models.IntegerField(default = 0)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('post-detail', kwargs = {'pk': self.pk})

class Upvote(models.Model):
    user = models.ForeignKey(User, related_name = 'upvotes', on_delete = models.CASCADE)
    post = models.ForeignKey(Post, related_name = 'upvotes', on_delete = models.CASCADE)

class Comment(models.Model):
    user = models.ForeignKey(User, related_name = 'comments', on_delete = models.CASCADE)
    post = models.ForeignKey(Post, related_name = 'comments', on_delete = models.CASCADE)
    body = models.TextField()
    created = models.DateTimeField(auto_now_add = True)

    def __str__(self):
        return self.body

接下来,我们将移动到serializers.py文件。因为这个文件在默认情况下是不存在的,所以我们需要像创建users应用程序一样创建它。

serializers.py

在这个文件中,我们将序列化我们的模型,使它们的格式可以在 web 上共享,并且可以被任何前端框架访问。

首先,我们将把所需的依赖项导入到我们的文件中。我们将从rest_framework包中导入serializers,并且导入我们创建的三个模型——PostUpvoteComment

接下来,我们将编写三个序列化器——三个模型各一个。下面给出了序列化程序类的代码。让我们先看看代码,然后我们可以稍后讨论它。

from rest_framework import serializers
from .models import Post, Upvote, Comment

class PostSerializer(serializers.ModelSerializer):
    user = serializers.ReadOnlyField(source = 'user.username')
    class Meta:
        model = Post
        fields = ('id', 'title', 'body', 'created', 'updated', 'user', 'upvote_count')

class UpvoteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Upvote
        fields = ('id', 'user', 'post')

class CommentSerializer(serializers.ModelSerializer):
    user = serializers.ReadOnlyField(source = 'user.username')
    class Meta:
        model = Comment
        fields = ('id', 'user', 'post', 'body', 'created')

该结构非常类似于我们在前一篇文章中为Users模型编写的序列化程序。

上面写的三个类中的每一个都为特定的模型定义了一个序列化器:PostUpvoteComment。每个序列化程序类的Meta内部类指定了序列化程序应该使用的模型,以及模型的哪些字段应该包含在序列化数据中。

正如我们所看到的,PostSerializer将序列化Post模型的实例,并将在序列化的数据中包含idtitlebodycreatedupdateduserupvote_count字段。

同样,UpvoteSerializerCommentSerializer会序列化自己的字段。

接下来,我们转移到最重要的部分——视图,在这里我们将定义应用程序的所有逻辑,我们将处理文件中的getpostputdelete请求。

views.py

在本节中,我们将定义各种views来处理与我们已经创建的三个模型相关的不同请求。

我们将在应用程序中添加以下功能:

  1. 看到所有的博客帖子
  2. 创建新的博客文章
  3. 编辑该特定用户撰写的博客文章
  4. 删除该特定用户撰写的博客文章
  5. 查看特定用户撰写的博客文章
  6. 投票支持该职位
  7. 在帖子上添加评论

因此,让我们从所需的导入开始。

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework import permissions
from .models import Post, Upvote, Comment
from .serializers import PostSerializer, UpvoteSerializer, CommentSerializer
from django.contrib.auth.models import User

因此,在views.py文件中,我们导入了各种类和函数——有些来自rest_framework包,有些是serilializersmodels

APIView是一个类,它提供了一种定义视图的方法,这些视图以 RESTful 方式处理传入的 HTTP 请求。视图是一个可调用的对象,它接受一个 HTTP 请求作为它的参数,并返回一个 HTTP 响应。

Response是一个提供创建 HTTP 响应对象的方法的类。HTTP 响应通常包括状态代码、标头和包含响应数据的正文。

status是一个将多个 HTTP 状态码定义为常量的模块。这些代码表示 HTTP 请求的成功或失败。

permissions是一个模块,提供了在视图中实现权限检查的类。权限是确定用户是否有权访问特定视图的规则。

代码还从.models模块导入了几个模型类,以及从.serializers模块导入了序列化器类。最后,它从django.contrib.auth.models模块导入User类,该类代表 Django web 框架的用户,即默认的 Django User模型类。

此外,views.py文件中的所有类,我们将要求用户登录,所以我们将使用permissions模块在所有类中强制这样做。

接下来,我们将定义PostListAPIView类,它将提供获取所有可用博客文章的能力,并且还将具有创建新博客文章的能力。下面是相同的代码:

class PostListAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, *args, **kwargs):
        posts = Post.objects.all()
        serializer = PostSerializer(posts, many = True)
        return Response(serializer.data, status = status.HTTP_200_OK)

    def post(self, request, *args, **kwargs):
        data = {
            'user': request.user.id,
            'title': request.data.get('title'),
            'body': request.data.get('body')
        }
        serializer = PostSerializer(data = data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status = status.HTTP_201_CREATED)
        return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)

这是APIView的一个子类,提供了几种处理不同 HTTP 请求方法的方法,比如GETPOSTPUTDELETE

PostListAPIView类的permission_classes属性指定只允许经过身份验证的用户访问这个视图。这是通过使用isAuthenticated方法实现的。这意味着只有已经登录的用户才能看到帖子列表或创建新帖子。我们也将在所有其他课程中使用相同的内容。

当一个GET请求被发送到视图时,调用PostListAPIView类的get方法。该方法检索网站上的所有博客文章,并使用PostSerializer类创建数据的序列化表示。然后,它在 HTTP 响应中返回该数据,状态代码为 200 OK。

当一个POST请求被发送到视图时,调用PostListAPIView类的post方法。这个方法使用来自请求的数据创建一个新的Post对象,使用PostSerializer类序列化数据,并在 HTTP 响应中返回序列化的数据,创建状态代码 201。如果来自请求的数据无效,它将返回一条错误消息,状态代码为 400 BAD REQUEST。

接下来,我们定义了PostDetailAPIView,它将处理博客中的单个帖子的操作。它将处理获取特定的文章,编辑或删除它。它还拥有我们在上一课中拥有的相同权限。下面是它的代码:

class PostDetailAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, pk):
        try:
            return Post.objects.get(pk = pk)
        except Post.DoesNotExist:
            return None

    def get(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        serializer = PostSerializer(post)
        return Response(serializer.data, status = status.HTTP_200_OK)

    def put(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        data = {
            'user': request.user.id,
            'title': request.data.get('title'),
            'body': request.data.get('body'),
            'upvote_count': post.upvote_count
        }
        serializer = PostSerializer(post, data = data, partial = True)
        if serializer.is_valid():
            if post.user.id == request.user.id:
                serializer.save()
                return Response(serializer.data, status = status.HTTP_200_OK)
            return Response({"error": "You are not authorized to edit this post"}, status = status.HTTP_401_UNAUTHORIZED)
        return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        if post.user.id == request.user.id:
            post.delete()
            return Response({"res": "Object deleted!"}, status = status.HTTP_200_OK)
        return Response({"error": "You are not authorized to delete this post"}, status = status.HTTP_401_UNAUTHORIZED)

get_object方法是一个助手方法,用指定的主键(pk)检索文章。如果帖子不存在,则返回None

当一个GET请求被发送到视图时,调用PostDetailAPIView类的get方法。它使用get_object方法检索具有指定pk的帖子,使用PostSerializer类序列化帖子,并在 HTTP 响应中返回序列化的数据,状态代码为 200 OK。如果具有指定的pk的 post 不存在,它返回一个错误消息,状态代码为 404 NOT FOUND。

当一个PUT请求被发送到视图时,调用PostDetailAPIView类的put方法。它使用get_object方法用指定的pk检索帖子,用来自请求的数据更新帖子,并保存更新后的帖子。如果当前用户不是帖子的所有者,它将返回一条错误消息,状态代码为 401 未授权。如果来自请求的数据无效,它将返回一条错误消息,状态代码为 400 BAD REQUEST。

当一个DELETE请求被发送到视图时,调用PostDetailAPIView类的delete方法。它使用get_object方法检索带有指定pk的帖子,然后如果当前用户是帖子的所有者,则删除它。如果当前用户不是帖子的所有者,它将返回一条错误消息,状态代码为 401 未授权。如果指定pk的帖子不存在,则返回一条错误消息,状态代码为 404 未找到。

接下来,我们将拥有处理查看来自特定用户的帖子的UserPostAPIView类。

class UserPostAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, username, *args, **kwargs):
        user = User.objects.filter(username = username).first()
        if user is None:
            return Response({'error': 'User not found'}, status = status.HTTP_404_NOT_FOUND)
        posts = Post.objects.filter(user = user)
        serializer = PostSerializer(posts, many = True)
        return Response(serializer.data, status = status.HTTP_200_OK)

UserPostAPIView类的get方法检索具有指定username的用户,检索该用户创建的所有帖子,然后使用PostSerializer类创建数据的序列化表示。然后,它在 HTTP 响应中返回该数据,状态代码为 200 OK。如果指定的用户username不存在,则返回错误信息,状态代码为 404 未找到。

接下来,我们将拥有UpvoteAPIView类,它将处理与 upvotes 部分相关的所有功能。

class UpvoteAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, pk):
        try:
            return Post.objects.get(pk = pk)
        except Post.DoesNotExist:
            return None

    def post(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)

        upvoters = post.upvotes.all().values_list('user', flat = True)
        if request.user.id in upvoters:
            post.upvote_count -= 1
            post.upvotes.filter(user = request.user).delete()
        else:
            post.upvote_count += 1
            upvote = Upvote(user = request.user, post = post)
            upvote.save()
        post.save()
        serializer = PostSerializer(post)
        return Response(serializer.data, status = status.HTTP_200_OK)

get_object方法是一个助手方法,用指定的主键(pk)检索文章。如果帖子不存在,则返回None。和我们给PostDetailAPIView类写的差不多。

UpvoteAPIView类的post方法使用get_object方法检索带有指定pk的帖子,然后添加或删除当前用户对该帖子的投票。然后更新帖子的upvote_count并保存更新后的帖子。最后,它使用PostSerializer类创建帖子的序列化表示,并在 HTTP 响应中返回序列化数据,状态代码为 200 OK。如果具有指定的pk的 post 不存在,它返回一个错误消息,状态代码为 404 NOT FOUND。

最后,我们有CommentAPIView,它将处理特定帖子上的评论。它将具有获取特定帖子的所有评论并发布新评论的功能。

class CommentAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, pk):
        try:
            return Post.objects.get(pk = pk)
        except Post.DoesNotExist:
            return None

    def get(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        comments = Comment.objects.filter(post = post)
        serializer = CommentSerializer(comments, many = True)
        return Response(serializer.data, status = status.HTTP_200_OK)

    def post(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        data = {
            'user': request.user.id,
            'post': post.id,
            'body': request.data.get('body')
        }
        serializer = CommentSerializer(data = data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status = status.HTTP_201_CREATED)
        return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)

我们可以看到,它有同样的get_object helper 函数,它接受主键pk并返回与之对应的帖子。

当一个GET请求被发送到视图时,调用CommentAPIView类的get方法。它使用get_object方法检索带有指定pk的帖子,检索该帖子的所有评论,然后使用CommentSerializer类创建数据的序列化表示。然后,它在 HTTP 响应中返回该数据,状态代码为 200 OK。如果带有指定pk的 post 不存在,它返回一个错误消息,状态代码为 404 NOT FOUND。

当一个POST请求被发送到视图时,调用CommentAPIView类的post方法。它使用get_object方法检索具有指定pk的帖子,使用来自请求的数据创建一个新的Comment对象,使用CommentSerializer类序列化数据,并在 HTTP 响应中返回序列化数据,创建状态代码 201。如果来自请求的数据无效,它将返回一条错误消息,状态代码为 400 BAD REQUEST。如果具有指定的pk的帖子不存在,它将返回一个错误消息,状态代码为 404 NOT FOUND。

下面可以找到views.py文件的完整代码。

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework import permissions
from .models import Post, Upvote, Comment
from .serializers import PostSerializer, UpvoteSerializer, CommentSerializer
from django.contrib.auth.models import User

class PostListAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, *args, **kwargs):
        posts = Post.objects.all()
        serializer = PostSerializer(posts, many = True)
        return Response(serializer.data, status = status.HTTP_200_OK)

    def post(self, request, *args, **kwargs):
        data = {
            'user': request.user.id,
            'title': request.data.get('title'),
            'body': request.data.get('body')
        }
        serializer = PostSerializer(data = data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status = status.HTTP_201_CREATED)
        return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)

class PostDetailAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, pk):
        try:
            return Post.objects.get(pk = pk)
        except Post.DoesNotExist:
            return None

    def get(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        serializer = PostSerializer(post)
        return Response(serializer.data, status = status.HTTP_200_OK)

    def put(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        data = {
            'user': request.user.id,
            'title': request.data.get('title'),
            'body': request.data.get('body'),
            'upvote_count': post.upvote_count
        }
        serializer = PostSerializer(post, data = data, partial = True)
        if serializer.is_valid():
            if post.user.id == request.user.id:
                serializer.save()
                return Response(serializer.data, status = status.HTTP_200_OK)
            return Response({"error": "You are not authorized to edit this post"}, status = status.HTTP_401_UNAUTHORIZED)
        return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        if post.user.id == request.user.id:
            post.delete()
            return Response({"res": "Object deleted!"}, status = status.HTTP_200_OK)
        return Response({"error": "You are not authorized to delete this post"}, status = status.HTTP_401_UNAUTHORIZED)

class UserPostAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, username, *args, **kwargs):
        user = User.objects.filter(username = username).first()
        if user is None:
            return Response({'error': 'User not found'}, status = status.HTTP_404_NOT_FOUND)
        posts = Post.objects.filter(user = user)
        serializer = PostSerializer(posts, many = True)
        return Response(serializer.data, status = status.HTTP_200_OK)

class UpvoteAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, pk):
        try:
            return Post.objects.get(pk = pk)
        except Post.DoesNotExist:
            return None

    def post(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)

        upvoters = post.upvotes.all().values_list('user', flat = True)
        if request.user.id in upvoters:
            post.upvote_count -= 1
            post.upvotes.filter(user = request.user).delete()
        else:
            post.upvote_count += 1
            upvote = Upvote(user = request.user, post = post)
            upvote.save()
        post.save()
        serializer = PostSerializer(post)
        return Response(serializer.data, status = status.HTTP_200_OK)

class CommentAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, pk):
        try:
            return Post.objects.get(pk = pk)
        except Post.DoesNotExist:
            return None

    def get(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        comments = Comment.objects.filter(post = post)
        serializer = CommentSerializer(comments, many = True)
        return Response(serializer.data, status = status.HTTP_200_OK)

    def post(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        data = {
            'user': request.user.id,
            'post': post.id,
            'body': request.data.get('body')
        }
        serializer = CommentSerializer(data = data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status = status.HTTP_201_CREATED)
        return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)

接下来,我们移动到admin.py文件来注册我们创建的模型,以便管理面板可以访问这些模型。

管理. py

from django.contrib import admin
from .models import Post, Upvote, Comment

admin.site.register(Post)
admin.site.register(Upvote)
admin.site.register(Comment)

如上图所示,我们注册了三个模型— PostUpvoteComment

接下来,我们将在我们的posts文件夹中创建一个名为urls.py的文件,我们将为我们为posts应用编写的所有视图编写 URL。

urls.py

urls.py文件中,我们将为我们的posts应用程序定义 URL 模式,然后我们必须将它们包含在blog应用程序中的urls.py文件中。

from django.urls import path
from .views import PostListAPIView, PostDetailAPIView, UserPostAPIView, UpvoteAPIView, CommentAPIView

urlpatterns = [
    path('', PostListAPIView.as_view()),
    path('<int:pk>/', PostDetailAPIView.as_view()),
    path('<int:pk>/upvote/', UpvoteAPIView.as_view()),
    path('<int:pk>/comment/', CommentAPIView.as_view()),
    path('<username>/', UserPostAPIView.as_view())
]

这里,urlpatterns是博客应用的 URL 模式列表。列表中的每个元素都是一个将 URL 路径映射到视图的path对象。

列表中的第一个元素将根 URL ( /)映射到PostListAPIView视图,因此当用户访问根 URL 时,PostListAPIView视图将被调用并处理请求。列表中的其他元素将表单/<int:pk>//<int:pk>/upvote//<int:pk>/comment//<username>/的 URL 映射到相应的视图。这些 URL 用于访问单个帖子、投票赞成/投票反对帖子、创建帖子评论以及查看特定用户的帖子。

<int:pk>是一个 URL path 参数,指定 URL 路径应该在那个位置包含一个整数值(post 的主键)。当访问 URL 时,这个整数值作为参数pk传递给视图,视图可以用它来检索带有指定主键的文章。这允许视图处理特定帖子而不是所有帖子的请求。

类似地,/<username>/用于访问特定用户的帖子。当 URL 被访问时,username被传递给视图。

现在,在blog应用程序的urls.py文件中,我们将像对待users应用程序一样包含posts应用程序的 URL。

from django.contrib import admin
from django.urls import path, include
from posts import urls as posts_urls
from users import urls as users_urls

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
    path("api/posts/", include(posts_urls)),
    path("api/users/", include(users_urls)),
]

在测试 API 之前,我们需要执行迁移,因为我们已经创建了新的模型。

因此,我们将运行以下命令来实现这一点:

python manage.py makemigrations
python manage.py migrate

当我们在 Django 中创建新模型时,我们需要运行一个迁移,因为数据库模式需要匹配将要存储在其中的数据的结构。如果不运行迁移,数据库将没有正确的表和字段来存储新模型中的数据,并且当我们试图从模型中保存或检索数据时将会出现错误。

python manage.py makemigrations命令分析我们对模型所做的更改,并创建一个新的迁移文件,其中包含更新数据库模式的指令。

python manage.py migrate命令将迁移文件中的更改应用到数据库。这将更新数据库模式,并确保它与我们的模型同步。

我们可以像测试users应用程序一样测试posts应用程序的 API。由于这篇文章已经太大了,所以我将把测试部分留给posts app 让你自己做。这与您为users应用程序所做的非常相似,首先运行服务器,然后转到urls.py文件中定义的各个 URL。

那么,这就把我们带到了本系列文章后端部分的结尾。我希望你们都喜欢后端部分,并了解了 DRF 是如何工作的,现在你应该尝试自己构建它,并添加功能来进一步增强应用程序。

我将很快开始我们网站的前端工作,并将很快在前端部分添加文章。当他们准备好的时候,我将在这儿把他们连接起来。在那之前,继续学习!

这里还有一些你想读的文章系列:

https://javascript.plainenglish.io/build-an-e-commerce-website-with-mern-stack-part-1-setting-up-the-project-eecd710e2696

用 SQL 构建协同过滤音乐推荐系统

原文:https://towardsdatascience.com/build-a-collaborative-filtering-music-recommendation-system-in-sql-d9d955cfde6c

用 SQL 实现用户项目协同过滤推荐引擎

图片由奥斯汀·尼尔Unsplash 拍摄

本文将用 SQL 在关系数据库中构建一个简单的用户项目协同过滤推荐系统。以下是文章提纲:

目录

  • 协同过滤概述
    -示例
  • 问题陈述
  • 数据概述
  • 履行
  • 结束语
  • 资源

协作过滤概述

协同过滤旨在通过用户的偏好来预测用户的兴趣。这可以通过过滤不同用户、数据源等之间的模式来实现。协同过滤如何工作背后的主要直觉如下,如果用户 A 和 B 对产品 X 有相似的品味,那么用户 A 和 B 很可能对其他产品也有相似的品味。

在协同过滤中有两种常见的方法,基于记忆的方法和基于模型的方法。本文的重点将是基于内存的方法。我们将查看用户项目组合的评级,以便为其他用户提供推荐。在我们示例的上下文中,项目将是音乐。基于用户的协同过滤本质上意味着志同道合的用户会产生强烈的相似的推荐。基于项目的协同过滤基于项目之间的相似性来推荐项目,该相似性是使用那些项目的用户评级来计算的。

这种协同过滤模型的主要缺点是它对推荐新项目不友好,这是因为没有用户/项目与之交互。这被称为冷启动问题。另一个问题来自高度稀疏的数据,特别是基于内存的模型,当数据集非常稀疏时,性能会很差。

例题

协作过滤算法的一些例子:

  • 向用户推荐亚马逊产品——根据购买/评论过该产品或其他类似产品的其他用户向您推荐产品。
  • Udemy 课程推荐——根据其他已经完成并喜欢你已经完成的现有课程的人向你推荐课程。

如果您想了解更多关于推荐系统以及使用推荐系统解决问题的各种方法和实现的信息,请查看我的文章 推荐系统解释 ,了解推荐系统解决方案在 Python 中的直觉和实现,以及推荐系统的协作过滤、基于内容和混合方法。我还写了另一篇关于 Node2Vec 的 链接预测推荐系统的文章,其中也提到了您可能感兴趣的推荐系统中的问题和解决方案。

问题陈述

这个问题比较容易理解。假设你目前在 Spotify、Apple Music 或 Tidal 等音乐流媒体平台工作。你有一个庞大而活跃的用户群,他们有能力关注其他用户,喜欢他们喜欢的歌曲。我们希望设计一个推荐引擎,推荐用户还没有听过,但这个用户正在关注的用户已经听过并喜欢的歌曲。

数据概述

以下架构概述了可供用户使用的表、列和数据。

Table : Followers
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| user1_id      | int     |
| user2_id      | int     |
+---------------+---------+The Followers table consists of two columns, user1_id and user2_id where the fields would be populated if user1 follows user2\. The id's are unique identifiers associated with this table and can be mapped to the activity table by merging on user_id.Table : Likes
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| user_id       | int     |
| song_id       | int     |
+---------------+---------+The Likes table also consists of two columns, user_id and song_id. The id's are unique identifiers associated with the user and song respectively. This table implies that the user under the user_id column has liked the song under the song_id column. The song_id from the Likes table can be mapped to the song_id from the Songs table to get the song name.Table : Songs
+-----------------+-------------+
| Column Name     | Type        |
+-----------------+-------------+
| song_id         | int         |
| name            | varchar     |
| artist          | varchar     |
+-----------------+-------------+The Songs table consists of three columns, song_id, artist and name. Each song_id is mapped to a name associated with that song and an artist which created that song. 

下图显示了与上述数据库相关联的模式。

数据库模式概述。图片由作者提供。

履行

您可以使用下面的 DB Fiddle 或通过我的 GitHub 上的相应查询来实现。

我们首先将一些有用的数据插入数据库。可以执行下面的代码片段,将几行数据添加到每个适当的表中。

现在我们可以建立推荐系统了。我们可以将其分为 3 个步骤,首先创建一个包含所有 user _ ids 的 CTE。其次,创建另一个 CTE,显示他们的追随者喜欢的所有歌曲。最后,通过 likes 表,我们可以合并来自步骤 2 的 CTE,以找到朋友喜欢但用户不喜欢的歌曲。

基于我们插入到数据库中的数据,上面的查询应该产生以下结果。

每个用户推荐的歌曲,该用户还没有听过,但是该用户的追随者已经喜欢了。图片由作者提供。

根据我们插入的数据,我们注意到没有听过song_id = 3user_1正在关注听过song_id =3user_2user_3user_4。因此建议用户 1 可能也喜欢song_id = 3。相同的逻辑可以应用于对其余用户的其余推荐。

结束语

本教程的目的是展示如何通过 SQL 解决相对困难的问题。请注意,这个问题和解决方案有许多缺陷和警告。它可以通过处理各种边缘情况得到极大的改善,例如新用户没有关注任何人,新歌很少或没有参与,因此无法推荐给其他人,新发行的优先顺序等。这种解决方案的许多缺陷与推荐系统的冷启动问题有关。

您可以使用下面的 DB Fiddle 或通过我的 GitHub 上的相应查询来实现。

如果你想转型进入数据行业,并希望得到经验丰富的导师的指导和指引,那么你可能想看看最敏锐的头脑。Sharpest Minds 是一个导师平台,导师(他们是经验丰富的实践数据科学家、机器学习工程师、研究科学家、首席技术官等。)将有助于你的发展和学习在数据领域找到一份工作。点击查看它们

资源

如果你喜欢这篇文章,你可能也会喜欢我写的其他文章。请随意查看下面的内容。

使用 Streamlit 用不到 50 行代码构建一个数据注释管道

原文:https://towardsdatascience.com/build-a-data-annotation-pipeline-with-less-than-fifty-lines-of-code-with-streamlit-e72a3a84e259

轻松创建自己的带注释的数据集

介绍

你将在网上看到的大多数机器和深度学习教程都使用众所周知的公共数据集。一个流行的例子是著名的英国统计学家和生物学家罗纳德·费雪的鸢尾花数据集,他在 1936 年的论文中测量了 150 种花的四个特征。在数据集中,每一种花都属于三个物种中的一个,因此我们为每个测量值设置了一个标签。然后,监督学习算法可以学习区分不同的类别。

对于现实世界的问题,我们往往不能依赖现有的数据集,需要创建自己的数据集才能建立模型。这可能是一项繁琐的工作,对罗纳德·费雪来说就是如此——手动收集和管理大量样本。有时,您已经收集了原始数据,并希望进行监督学习,但还没有对数据进行分类。这意味着要仔细检查所有东西,并让人工输入来分配标签。

如今,有几种注释工具可以用于此目的。有些是高度自动化的,例如亚马逊的地面真相,它会在上传数据时为你提供各种信息标签。尽管如此,这些工具可能不适合手头的任务。当原型设计和开发一个最小可行的产品时,你应该有一个工具,允许你快速地处理你的数据和分配你的标签。

在本文中,我们将展示如何借助 Python 中的 streamlit 快速构建这样一个工具。

照片由格伦·卡斯滕斯-彼得斯Unsplash 拍摄

示例:注释数据集以进行分类

对于我们的示例用例,我们想要为分类任务标记图像。这可以是你选择的数据集,例如, fashion-mnist 集合。对于本教程,我使用我自己的一些数据。前段时间写了下面这篇关于骰子随机性的文章,记录了~ 3000 张随机投掷的图像(*。png 图片,640 × 480 px)。

下面是显示前三幅图像的数据集预览:

示例*。随机骰子数据集的 png 图像。每张图片为 640 × 480 px。

我在这里上传了这些图片的一个子集(前 100,~ 10 MB) ,这样你就可以跟上了。如果您想要完整的数据集,请随时联系。在本文中,我使用 OpenCV 从图像中自动提取点数。当时,一位评论员发现了一张贴错标签的图片,显示自动标注并不完美。作为一个值得注意的评论,即使是著名的 MNIST 数据集也包含 15 个标签错误

对于手头的任务,我们希望进行手动注释,并为每张图像分配一个 1 到 6 之间的数字,或者不进行定义。注意,图像处理中的注释用于多个不同的任务,例如,绘制感兴趣区域和分配标签。在这里,我们使用注释,因为我们有一个图像,并希望为它添加一个标签,即每个骰子的点数。

方法

为了构建我们的管道,我们将使用 streamlit ,这是一个开源 Python 库,允许您快速构建数据应用的前端。一个关键的特性是,当您更改代码时,web 服务器会更新。这使您能够在编码时直接看到 UI 是如何变化的。

设置

首先,您需要一个工作的 Python 环境(例如,通过 miniconda )。

  • pip install streamlit安装 streamlit
  • annotation.py创建一个空的 Python 文件
  • 将下载的图像复制到同一个文件夹中;这应该是DICE_IMAGES_100
  • 对于这个项目,另外,安装 Python 图像库:pip install Pillow
  • streamlit run annotation.py运行文件
  • 访问正在运行的 web 服务器;默认为http://localhost:8501/

现在让我们添加一些功能。

代码和用户界面

streamlit 的基本事件循环从上到下运行一个脚本。在早期版本中,这意味着选择会在重新运行时丢失。如果我们想要存储注释并保存状态,这就需要一个变通方法。幸运的是,streamlit 最近引入了一个特性来做到这一点:会话状态。发布说明包括演示代码,它可以作为注释项目的起点。对于我们的任务,我们要做如下工作:

  • 将注释作为 dict 存储在 session_state 中。
  • 显示文件夹中的图像,并给出选项供选择:[1,2,3,4,5,6,NA]
  • 提供一个按钮,以*.csv 格式下载注释。

总的来说,我们可以用不到 50 行代码实现这一点:

streamlit 注释工具的代码。

将上面的代码复制到annotation.py文件中。Streamlit 应该会检测到代码更改,并且在浏览器窗口中,您应该会看到一个通知,告知源文件已经被更改。单击重新运行以刷新页面。

运行实例

上面的代码运行后,我们可以点击我们的数据集。这应该是这样的:

streamlit 页面的截屏。GIF 实时播放;我们可以快速注释我们的数据集。

GIF 是实时的,我希望人们能够理解,分配标签和加载下一张图像的速度非常快,从而提供无缝的用户体验。最终,我们可以快速注释,突出该方法的实用性。在注释过程中的任何时候,我们都可以将注释的当前状态导出为*.csv。

限制

可以说,这种解决方案对于原型制作来说很方便,但是有几个缺点,我们应该简单讨论一下:

并发性:会话状态是基于每个会话的。这意味着如果另一个用户同时连接,他们将注释同一个数据集。根据使用情况,这可能是有用的,例如,当想要为相同的文件从不同的用户收集多个注释时。另一方面,当有很多文件并且想要分发作品并且只对每个图像注释一次时,这是行不通的。在这里,我们可以编写功能,这样每个用户都可以从整个集合中获得一批要注释的文件。

文件集成:此外,从文件管理的角度来看,当前的实现是不可扩展的。我们从脚本所在的同一文件夹中加载图像数据。这对于较大的映像集来说是不切实际的,我们更愿意集成到一个更大的文件系统,例如,通过连接到云存储,比如 S3 存储桶。

批注记账:将批注导出为*。csv 相当初级,当有许多数据集或注释迭代时会很不方便。在这里,与数据库(例如 MongoDB)的连接将有助于跟踪带注释的文件。此外,应该扩展存储的数据。在这种情况下,记录额外的元数据,比如时间、用户和文件散列,可能会很有趣。

结论

Streamlit 是一种非常快速的构建实用程序的方法,可以在数据科学工作流程中为您提供帮助。在这里,我们用它来建立一个标签管道。然而,一些限制会阻碍该方法的可伸缩性,并且需要额外的集成。对于更大的任务,看看其他现有工具是否更适合这项工作可能是值得的。如今,注释任务也可以作为微工作的一部分被外包出去,这可能是一个值得的选择。然而,对于手头的注释任务,streamlit 方法是一个有价值的解决方案。

我目前正在探索这种形式,以分享更多数据科学的想法。你有什么改进的建议吗,或者你有一个我应该总结的话题吗?让我知道!如果你喜欢中等并且正在考虑成为会员,可以随意使用 我的推荐链接

使用 Kafka、Kafka connect 和 DynamoDB 在 AWS 上构建数据管道

原文:https://towardsdatascience.com/build-a-data-pipeline-on-aws-with-kafka-kafka-connect-and-dynamodb-97642cdb0cfb

集成 DynamoDB 与 MSK 和 MSK 连接

有许多方法可以缝合数据管道——开源组件、托管服务、ETL 工具等。在卡夫卡的世界里, Kafka Connect“Apache Kafka 和其他系统之间的流数据”的首选工具。它有一套广泛的预建源和接收器连接器,以及Kafka 连接器的通用框架,该框架将其他数据系统与 Kafka 的集成标准化,并使开发自己的连接器变得更加简单,如果有必要的话。

照片由迈克·本纳Unsplash 上拍摄

这是一个由两部分组成的博客系列,它提供了 Kafka 和 Kafka Connect 的数据管道的逐步演示。出于演示目的,我将使用 AWS,但是这些概念适用于任何等效的选项(例如,使用 Docker 在本地运行这些选项)。以下是我将使用的一些关键 AWS 服务:

  • 亚马逊为 Apache Kafka (MSK)管理流媒体 —数据基础设施的核心组件。
  • MSK 连接 —它将用于部署为 Kafka Connect 构建的完全受管连接器,以便将数据移入各种源或从各种源提取数据。
  • Amazon DynamoDB 是一个完全托管的 NoSQL 数据库服务,在这个博客系列的上下文中,它充当我们的数据管道的目标/接收器。
  • 亚马逊 Aurora MySQL 是一个完全托管的、兼容 MySQL 的关系数据库引擎,在本博客系列的第二部分中使用。

以下是每个部分将涵盖的内容:

  • 第一部分将保持事情相对简单——它是关于容易地开始。我将使用 Kafka Connect Datagen source 连接器将一些样本数据泵入 MSK 主题,然后使用 AWS DynamoDB 接收器连接器将这些数据保存在 DynamoDB 表中。
  • 第二部分将更上一层楼,我们将探索变更数据捕获。Datagen 连接器将被 MySQL 的 Debezium 连接器所取代,它将从 Aurora MySQL 的表中实时提取数据,并将其推送到 MSK 主题。然后,我们将像以前一样继续使用 DynamoDB 接收器连接器。

准备基础设施组件和服务

首先,您需要部署本教程所需的所有资源。有很多,但不要担心,因为我已经为你准备好了云形成模板!

继续之前,请从 此链接 下载模板

这是这篇博文中提出的解决方案的高级示意图。

高层架构(图片由作者提供)

有关逐步说明,请参考官方文档中的 在 AWS CloudFormation 控制台 上创建堆栈

使用 AWS 控制台部署 CloudFormation 模板——在创建堆栈向导上,选择上传模板文件并上传您刚刚下载的文件。

点击下一个,输入堆栈的名称。单击下一步继续—在向导的最后一页,单击创建堆栈开始创建资源。

您可以在 CloudFormation 控制台中跟踪进度。一旦成功,您应该拥有所有资源,包括:

  • 核心基础设施:VPC、子网等。
  • 服务:MSK 集群,极光 MySQL 等。
  • 其他:IAM 角色、CloudWatch 日志组等。

通过会话管理器连接到 EC2 实例

在 CloudFormation 资源列表中,找到KafkaClientEC2Instance EC2 实例(在上图中突出显示)

使用会话管理器连接到它:

启用自动主题创建

MSK 连接要求即时创建主题。我们可以在 MSK 创建一个定制配置来实现自动主题创建。

从 EC2 实例中,运行以下命令来创建自定义配置:

sudo -u ec2-user -i
mkdir kafka-configuration && cd kafka-configurationcat <<EOF > configuration.txt 
auto.create.topics.enable=true
EOFexport AWS_REGION=<enter MSK cluster region e.g. us-east-1>
export AWS_REGION=us-east-1aws kafka create-configuration \
    --name "CustomConfiguration" \
    --description "Topic auto-creation enabled" \
    --kafka-versions "2.6.2" \
    --region $AWS_REGION \
    --server-properties file://configuration.txt

继续并应用此配置:

转到您的MSK cluster > Properties > Configuration并选择编辑
选择您刚刚创建的配置,然后保存。这将重启您的 MSK 集群——请等待此操作完成后再继续。

数据管道:第 1 部分

让我们首先创建管道的前半部分,它将利用 Datagen source connector 将样本事件抽取到 MSK 的一个主题中。

在本节中,您将:

  • 下载 Datagen 连接器产品
  • 在 MSK 创建自定义插件
  • 将 Datagen 源连接器部署到 MSK 连接

最后,数据管道的前半部分将准备就绪!

创建自定义插件和连接器

将 Datagen 连接器文件上传到T1

从 Kafka 客户端 EC2 实例中,运行以下命令:

sudo -u ec2-user -i
mkdir kafka-connect-datagen && cd kafka-connect-datagenwget https://d1i4a15mxbxib1.cloudfront.net/api/plugins/confluentinc/kafka-connect-datagen/versions/0.5.3/confluentinc-kafka-connect-datagen-0.5.3.zip
aws s3 cp ./confluentinc-kafka-connect-datagen-0.5.3.zip s3://msk-lab-<ENTER_YOUR_AWS_ACCOUNT_ID>-plugins-bucket/cd ..

创建自定义插件

有关如何创建 MSK 连接插件的分步说明,请参考官方文档中的 使用 AWS 管理控制台 创建自定义插件。

创建定制插件时,确保选择你在上一步上传到Amazon S3的 Datagen 连接器 zip 文件。

创建数据生成源连接器

关于如何创建 MSK 连接连接器的逐步说明,请参考官方文档中的 创建连接器

要创建连接器:

  1. 选择您刚刚创建的插件。
  2. 输入连接器名称,并选择 MSK 集群和 IAM 身份验证
  3. 您可以在连接器配置部分输入下面提供的内容
connector.class=io.confluent.kafka.connect.datagen.DatagenConnector
kafka.topic=orders
quickstart=orders
key.converter=org.apache.kafka.connect.storage.StringConverter
value.converter=org.apache.kafka.connect.json.JsonConverter
value.converter.schemas.enable=false
max.interval=10000
iterations=-1
tasks.max=1

保持其余配置不变

  1. 访问权限下,为连接器选择正确的 IAM 角色(名称中带有DatagenConnectorIAMRole的角色)
  2. 点击下一步的进入安全选项——保持不变
  3. 点击下一个。对于日志交付,选择交付到亚马逊 CloudWatch 日志。找到并选择/msk-connect-demo-cwlog-group
  4. 点击下一步 —在最后一页,向下滚动并点击创建连接器以启动流程并等待连接器启动。

完成后,连接器转换到运行状态,继续以下步骤。

测试管道

在您继续下一步之前:

  • 下载 AWS IAM JAR 文件,并将其包含在类路径中
  • 为 Kafka CLI 使用者创建属性文件

在 EC2 实例中,运行以下命令:

sudo -u ec2-user -imkdir iam-auth && cd ./iam-auth
wget https://github.com/aws/aws-msk-iam-auth/releases/download/1.1.0/aws-msk-iam-auth-1.1.0-all.jar
cd ../cat <<EOF > /home/ec2-user/kafka/config/client-config.properties
# Sets up TLS for encryption and SASL for authN.
security.protocol = SASL_SSL# Identifies the SASL mechanism to use.
sasl.mechanism = AWS_MSK_IAM# Binds SASL client implementation.
sasl.jaas.config = software.amazon.msk.auth.iam.IAMLoginModule required;# Encapsulates constructing a SigV4 signature based on extracted credentials.
# The SASL client bound by "sasl.jaas.config" invokes this class.
sasl.client.callback.handler.class = software.amazon.msk.auth.iam.IAMClientCallbackHandler
EOFexport CLASSPATH=/home/ec2-user/iam-auth/aws-msk-iam-auth-1.1.0-all.jar
echo "export CLASSPATH=${CLASSPATH}" | tee -a ~/.bash_profile

首先,列出卡夫卡的主题:

export MSK_BOOTSTRAP_ADDRESS=<ENTER MSK CLUSTER ENDPOINT>/home/ec2-user/kafka/bin/kafka-topics.sh --bootstrap-server $MSK_BOOTSTRAP_ADDRESS --command-config /home/ec2-user/kafka/config/client-config.properties --list

启动 Kafka CLI 消费者:

/home/ec2-user/kafka/bin/kafka-console-consumer.sh --bootstrap-server $MSK_BOOTSTRAP_ADDRESS --consumer.config /home/ec2-user/kafka/config/client-config.properties --from-beginning --topic orders | jq --color-output .

如果一切设置正确,您应该会看到类似如下的 JSON 输出:

...
{
  "ordertime": 1488059390707,
  "orderid": 50,
  "itemid": "Item_845",
  "orderunits": 4.801443003705596,
  "address": {
    "city": "City_",
    "state": "State_6",
    "zipcode": 34225
  }
}
{
  "ordertime": 1496655341559,
  "orderid": 51,
  "itemid": "Item_71",
  "orderunits": 6.184874231875158,
  "address": {
    "city": "City_63",
    "state": "State_91",
    "zipcode": 77633
  }
}
...

数据管道:第 2 部分

只要 Datagen source 连接器在运行,它就会继续生成样本订单数据。根据我们的配置(max.interval=10000iterations=-1),它将每 10 秒产生一条记录。

接下来,我们将实现管道的另一半,它负责在 DynamoDB 接收器连接器的帮助下,将数据从 MSK 主题传送到 DynamoDB 表。

在本节中,您将:

  • 下载 DynamoDB 连接器构件
  • 在 MSK 创建自定义插件
  • 将 DynamoDB 接收器连接器部署到 MSK 连接

创建自定义插件和连接器

将 DynamoDB 连接器上传到T3

登录 Kafka 客户端 EC2 实例:

sudo -u ec2-user -imkdir kafka-connect-dynamodb && cd kafka-connect-dynamodbwget https://d1i4a15mxbxib1.cloudfront.net/api/plugins/confluentinc/kafka-connect-aws-dynamodb/versions/1.3.0/confluentinc-kafka-connect-aws-dynamodb-1.3.0.zipaws s3 cp ./confluentinc-kafka-connect-aws-dynamodb-1.3.0.zip s3://msk-lab-<ENTER_YOUR_AWS_ACCOUNT_ID>-plugins-bucket/cd ..

创建自定义插件

关于如何创建 MSK 连接插件的分步说明,请参考官方文档中的 使用 AWS 管理控制台 创建自定义插件。

创建自定义插件时,确保选择您在上一步上传到Amazon S3的 DynamoDB 连接器 zip 文件。

创建 DynamoDB 接收器连接器

关于如何创建 MSK 连接器的逐步说明,请参考官方文档中的 创建连接器

要创建连接器:

  1. 选择您刚刚创建的插件。
  2. 输入连接器名称,并选择 MSK 集群和 IAM 身份验证
  3. 您可以在连接器配置部分输入下面提供的内容。确保根据您的设置替换以下配置:
  • 使用正确的主题名(在我们的例子中是orders)
  • confluent.topic.bootstrap.servers中输入 MSK 代理端点
  • 对于aws.dynamodb.endpointaws.dynamodb.region,输入您创建 DynamoDB 表的地区,例如us-east-1

保持其余配置不变

connector.class=io.confluent.connect.aws.dynamodb.DynamoDbSinkConnector
tasks.max=1
topics=orders
aws.dynamodb.region=<ENTER AWS REGION e.g. us-east-1>
aws.dynamodb.endpoint=https://dynamodb.<ENTER AWS REGION>.amazonaws.com
aws.dynamodb.pk.hash=value.orderid
aws.dynamodb.pk.sort=
table.name.format=kafka_${topic}
transforms=flatten
transforms.flatten.type=org.apache.kafka.connect.transforms.Flatten$Value
transforms.flatten.delimiter=_
key.converter.schemas.enable=false
value.converter.schemas.enable=false
key.converter=org.apache.kafka.connect.storage.StringConverter
value.converter=org.apache.kafka.connect.json.JsonConverter
confluent.topic.bootstrap.servers=<ENTER MSK CLUSTER ENDPOINT>
confluent.topic.security.protocol=SASL_SSL
confluent.topic.sasl.mechanism=AWS_MSK_IAM
confluent.topic.sasl.jaas.config=software.amazon.msk.auth.iam.IAMLoginModule required;
confluent.topic.sasl.client.callback.handler.class=software.amazon.msk.auth.iam.IAMClientCallbackHandler
  1. 访问权限下,为连接器选择正确的 IAM 角色(名称中带有DynamoDBConnectorIAMRole的角色)
  2. 点击下一步进入安全选项——保持不变
  3. 点击下一个。对于日志交付,选择交付至亚马逊云观察日志。找到并选择/msk-connect-demo-cwlog-group
  4. 点击下一个的—在最后一页,向下滚动并点击创建连接器以启动流程并等待连接器启动。

完成后,连接器转换到运行状态,继续以下步骤。

在我们继续测试管道之前,您应该知道一些事情:

选择 DynamoDB 主键

在上面的配置中,我们将aws.dynamodb.pk.hash设置为value.orderid,这意味着 Kafka 主题事件有效负载中的orderid字段将被用作分区键(将aws.dynamodb.pk.sort留空,但如果需要,可以用来指定 DynamoDB 排序/范围键)。

使用 Kafka Connect SMT 展平记录

事件有效负载中的address字段具有嵌套结构。

"address": {
    "city": "City_63",
    "state": "State_91",
    "zipcode": 77633
  }

为了取消嵌套或展平(因为没有更好的词)它,我们使用了展平变换(org.apache.kafka.connect.transforms.Flatten$Value)。这将从address中提取单个字段,并使它们作为单个属性- address_cityaddress_stateaddress_zipcode可用。很快您将在 DynamoDB 表中看到相同的内容!

测试端到端管道

导航到 DynamoDB 控制台。您将看到kafka_orders表已经存在——这是由 DynamoDB sink 连接器自动创建的。

该表有 *orderid* 作为分区键

如果手头有 AWS CLI,您可以使用aws dynamodb scan --table-name kafka_orders快速查看数据。

您将得到类似的输出(注意address_*字段):

{
    "Items": [
        {
            "orderid": {
                "N": "251"
            },
            "address_zipcode": {
                "N": "68100"
            },
            "address_state": {
                "S": "State_2"
            },
            "itemid": {
                "S": "Item_46"
            },
            "ordertime": {
                "N": "1491423463934"
            },
            "address_city": {
                "S": "City_6"
            },
            "orderunits": {
                "N": "3.1272028351151926"
            }
        },
.....

继续,按照您喜欢的方式查询和处理 DynamoDB 表中的数据。那是你的作业!

随着连接器变得神奇,它将继续将 Kafka 主题中的记录同步到 DynamoDB 表中。请记住,只要连接器在运行,数据管道(来自Datagen source -> MSK topic -> DynamoDB)就将继续运行——记录将不断被添加到 MSK 的orders主题中,并将被保存到 DynamoDB 表中。

删除资源

除非您打算阅读本博客系列的第二部分(即将发布),否则请删除参考资料。

  • 删除Amazon S3桶的内容(msk-lab-<YOUR ACCOUNT_ID>-plugins-bucket)
  • 删除云形成堆栈
  • 删除 DynamoDB 表
  • 删除 MSK 连接连接器、插件和自定义配置

结论

像 MSK 互联这样的托管环境负责处理繁重的工作,让您专注于构建数据架构。这篇博客的重点是让您使用一个简单的数据管道,以 DynamoDB 作为接收器。下一部分将包括变更数据捕获,并向您介绍如何使用本文中介绍的组件构建解决方案。

使用 nAudio GPU 音频处理为关键词识别(KWS)任务构建深度神经网络

原文:https://towardsdatascience.com/build-a-deep-neural-network-for-the-keyword-spotting-kws-task-with-nnaudio-gpu-audio-processing-95b50018aaa8

处理音频会使任何机器学习任务变得复杂。在本教程中,我们将介绍如何在 PyTorch 中通过直接输入音频文件来构建神经网络,这些音频文件将直接转换为可微调的频谱图。为此,我们使用 nnAudio [1]和 PyTorch。

本教程将在谷歌语音命令数据集 v2 上为关键词识别(KWS)任务构建一个分类器。KWS 是一个健全的分类问题。我们的模型将预测匹配输入声音文件的单词(文本)。在这个 KWS 任务中总共有 12 个不同的输出类。我们选择使用所有 35 个可用单词中的 10 个,其余 25 个单词归入“未知”类。从背景噪音中产生类别“寂静”。

谷歌语音命令数据集 v2 有 105829 个话语,总共 35 个单词。该数据集包括一些词语,如“是”、“否”、“上”、“下”、“左”、“右”、“开”、“关”、“停止”和“开始”。每个话语长 1 秒。

在决定这个问题的架构之前,重要的是考虑我们将如何处理音频。我们会使用储存在我们电脑上的光谱图图像,还是 wavenet,还是其他什么?为了回答这个问题,让我们回到几年前,当我的博士生卓建伟在 SUTD 的时候。Raven 想要建立一个音频转录模型。他很快注意到,在训练模型之前,提取光谱图并将其存储在他的计算机上既麻烦又缓慢,使得不可能调整光谱图设置。因此,他开发了nn audio【1】库,该库提供了一个有用的开源工具,可以将音频直接加载到 PyTorch 层,在其中音频被动态转换为声谱图表示。

nAudio 使用 PyTorch 1D 卷积神经网络作为其后端。因此,它优化了波形到声谱图的转换过程。这使得离散傅立叶变换的基函数可以被神经网络训练,这意味着它们可以针对手头的任务进行优化。这是可能的,因为短时傅立叶变换(STFT)和梅尔基函数被实现为神经网络的第一层(前端层)。在训练期间,模型能够将梯度反向传播到前端。因此,来自音频信号的最重要的特征可以通过定制的训练频谱图来“捕获”。正如我们将看到的,这为我们训练音频分类任务提供了一种又好又快的方法。

下面的关键词识别(KWS)教程由四部分组成:

  • 第 1 部分:加载数据集&简单的线性模型
  • 第 2 部分:用可训练的基函数训练线性模型
  • 第 3 部分:结果模型的评估
  • 第 4 部分:使用更复杂的非线性模型

开始吧!

第 1 部分:加载数据集&简单的线性模型

在本教程中,我们将使用光谱图。在音频深度学习中,频谱图扮演着重要的角色:连接音频文件和深度学习模型。前端工具如 librosa 和 nAudio 将音频波形(时域)转换为频谱图(时频域),基本上可以采用与模型处理图像类似的方式进行处理。

加载数据集

首先,我们需要从谷歌获取数据。我们使用 AudioLoader 来访问语音命令 12 类数据集

AudioLoader 是一个一键式音频加载器,允许你一键下载、解压缩、数据分割和重新采样音频。它现在支持多个流行的数据集,如 MusicNet,MusdbHQ,TIMIT。

在传统的设置中,我们首先提取光谱图,将它们保存到我们的计算机中,然后将这些图像加载到模型中。这很慢,需要磁盘空间,并且很难根据手头的任务调整谱图特征。nAudio 通过作为神经网络的一部分实时计算光谱图来解决这些问题。

通过利用 PyTorch 和 GPU 处理,nAudio 可以计算不同类型的频谱图,如短时傅立叶变换(STFT)、梅尔频谱图和常数 Q 变换(CQT)。从原始 nAudio 论文[1]的下图中可以看出,在 GPU 上处理音频将计算时间缩短了 100 倍。

使用 nAudio GPU(绿色)、nAudio CPU(橙色)和 librosa(蓝色)计算不同类型光谱图的处理时间,以对数标度表示[1]。

定义模型

我们将首先用一个简单的单层网络来演示我们的工作流程。模型被定义为linear model _ n audio,继承了类speech command(lightning module)。可以去 设置 Lightning 模块 了解父类的更多详情。

闪电模块 是 PyTorch 闪电中的一个模块。它可以帮助您将 PyTorch 代码组织成 6 个部分,包括训练循环(training_step)、测试循环(test_step)、优化器和 lr 调度器(configure _ optimizers)。

在这个项目中,我们选择使用 Mel-spectrogram,因为这些声谱图箱被缩放以匹配人类听觉频谱。因此,它们可能很好地代表了我们人类所获得的特征。

这个简单的模型将声音文件(x)作为输入。然后我们使用 Linearmodel_nnAudio 中的方法n naudio . features . Mel . Mel spectrogram()作为神经网络的第一层,将声音文件从音频波形转换成频谱图。

出于演示的目的,我们只定义了一个简单的模型,这里有一个额外的线性层。此 KWS 分类任务的输出包含 12 个类,因此图层的输出大小应为 12。

nAudio 的结果代码如下。请注意,nnaudio . features . Mel . Mel spectrogram()函数有许多附加参数,如果需要,这些参数将允许您对光谱图进行精细控制。

带 nnAudio 的线性模型

训练模型一个历元

下面是用 PyTorch Lightning 为我们的简单线性模型训练 1 个时期的代码。

用 nnAudio 训练线性模型

训练上述模型导致完成一个历元的计算时间为 17s,这比我们使用 librosa 快了大约 95 倍。

第 2 部分:用可训练的基函数训练线性模型

在本节课中,我们将演示如何利用 nAudio 的可训练基函数来构建一个强大的分类器,在反向传播过程中,光谱图实际上会根据手头的任务进行微调。

设置基础功能

在 nAudio 中,我们可以使傅立叶内核可训练,这允许它在反向传播期间根据我们手头的特定任务进行调整。

您可以在下面的函数中修改 Mel-spectrogram 参数,用可训练 _mel* 和可训练 _STFT 控制 Mel 和 STFT 基础是否可训练。*

训练模特

我们使用一个相当标准的训练循环,在此期间,训练好的模型权重将保存在 lighting_logs 文件夹中。在本教程的下一步中,我们将进一步了解该模型的性能。

第 3 部分:结果模型的评估

您已经训练了一个线性模型,但现在是时候评估模型性能并进行一些可视化了。

将预训练的重量加载到模型上

每次训练一个模型,光照模块都会将训练好的权重保存在 lightning_logs 文件夹中。

评估模型性能

可以使用测试集上的以下度量来评估 KWS 任务上的模型性能:

  • 测试/损失(交叉熵)
  • 测试/acc(精确度)
  • F1 矩阵(F1 分数)
  • 困惑 _ 矩阵

测试集的最终精度显示如下,其中的设置略有不同,以允许内核可训练。

具有不同可训练基函数设置的线性模型的 KWS 任务中的测试/加速

当查看这些结果时,请记住我们有一个 12 类问题,因此随机预言将有 8.3%的准确性。从上表中,我们可以看到,通过使用可训练的基函数,与简单的线性模型相比,我们可以将 KWS 任务的准确性提高 14.2 个百分点。进一步调整超参数无疑会进一步提高性能。

想象结果(奖金)

当权重存储在我们的检查点文件中时,我们可以在我们的第一层 nnAudio 中可视化一些已学习的内核。检查点文件内部的结构如下所示:

*weight=torch.load('xxxx/checkpoints/xxxx.ckpt')
├── epoch
├── global_step
├── pytorch-lightning_version
│     
├── state_dict
│     ├─ mel_layer.mel_basis
│     ├─ mel_layer.stft.wsin
│     ├─ mel_layer.stft.wcos
│     ├─ mel_layer.stft.window_mask   
│     ├─ linearlayer.weight
│     ├─ linearlayer.bias
│     │
│     
├── callbacks
├── optimizer_states
├── lr_schedulers*

state_dict 是检查点文件中的字典键之一。它是一个有序的检测,包括基函数(如梅尔箱,STFT) 和层权重(在这种情况下是线性层)的训练权重。**

可视化蜂蜜罐

mel_layer.mel_basis 的形状应该是[n_mels,(n_fft/2+1)],其中 n_mels 是 mel 仓的数量,n_fft 是指用零填充后的窗口信号的长度。在本教程示例中, mel_layer.mel_basis 的形状为【40,241】。

这是不可训练的 Mel 库和可训练的 Mel 库在 200 个时期的比较。40 个 Mel 箱分别以不同的颜色显示如下:

trainable _ mel = False

trainable _ mel = True

注意它们是如何变化很大的,并且在我们特定的任务中特别调谐到重要的频率和模式。

可视化 STFT

mel_layer.stft.wsinmel_layer.stft.wcos 的形状应该是[(n_fft/2+1),1,n_fft]。在本教程示例中,两者的形状都是[241,1,480]。

这是不可训练的 STFT 和可训练的 STFT 在 200 个时代的对比。从左到右,从上到下,分别是第 2、第 10、第 20、第 50 傅立叶核。

可训练 _STFT =假

可训练 _STFT =真

第 4 部分:在更复杂的非线性模型中使用可训练的基函数

在完成本教程的第 1–3 部分后,您现在对如何将 nAudio 与可训练基函数结合使用有了大致的了解。在本教程的最后一部分,您可以看到如何轻松地调整模型函数,以适应任何类型的深度、复杂的神经网络,从而满足您的需求。唯一需要更改的是模型定义,如下例所示,我们在 nAudio 声谱图层后使用广播残差网络。

点击这里,查看完整、更复杂的教程示例

结论

我们已经通过使用 nAudio [1]可训练基函数,逐步完成了使用 Google 语音命令数据集 v2 构建关键字识别模型的教程。这种方法可以很容易地修改,以适应任何音频分类任务。请在评论中告诉我们您的反馈以及您对代码的处理方式!

致谢 感谢我的研究助理香君怡准备代码片段,感谢卓建伟创建 nAudio!

参考文献

  1. K.W. Cheuk,H. Anderson,K. Agres 和 D. Herremans,“nnAudio:一个使用 1D 卷积神经网络的动态 GPU 音频到频谱图转换工具箱”, IEEE Access ,第 8 卷,第 161981–162003 页,2020 年,doi:10.11109/Access .
  2. 对于本教程的源代码,可以参考https://github.com/heungky/nnAudio_tutorial
  3. nAudio 源代码,可以参考https://github.com/KinWaiCheuk/nnAudio
  4. 对于 nAudio 文档,您可以参考 https://kinwaicheuk.github.io/nnAudio/index.html
  5. 1999 年,香港大学,香港理工大学,香港理工大学,香港理工大学。通过可训练的基本功能理解音频特征。arXiv 预印本 arXiv:2204.11437 。(关于可训练基函数的研究文章)

使用基于类的视图构建 Django CRUD 应用程序

原文:https://towardsdatascience.com/build-a-django-crud-app-by-using-class-based-views-12bc69d36ab6

是的,对姜戈来说就是这么简单和快速

丹尼尔·科尔派在 Unsplash 上拍摄的照片

简介

在本教程中,我将向您展示如何使用 Django 最强大的功能之一:基于类的视图(CBV)来构建一个完全功能化的 CRUD(创建-读取-更新-删除)应用程序。这种方法最大化了代码重用,并允许您更快、更有效地构建 Django 应用程序。

所以,没有任何延迟,让我们开始吧。我们将创建一个简单的应用程序来管理电影及其流派的信息。如果你愿意,可以访问我的 GitHub 库上的完整代码。或者继续阅读并遵循以下步骤。我使用 GitBash for Windows 作为我的终端,所以这里的一些命令可能需要适应您的首选终端,特别是如果您使用 Power Shell 或 Windows 命令提示符。

第 1 部分:建立项目

1.创建您的项目文件夹,并在其中移动。我把我的叫做films_project

mkdir films_project
cd films_project

2.用venv(我的名字叫.myenv)创建一个新的虚拟环境。

python -m venv .myenv

3.现在激活你的虚拟环境。下面的命令适用于 GitBash。参考这个 Python 文档页面来检查你的终端和操作系统的命令。

source .myenv/Scripts/activate

4.安装我们在这个项目中需要的 Python 包:

pip install django django-extensions

5.创建一个requirements.txt文件。您可以使用以下命令:

pip freeze | grep -i django >> requirements.txt

6.开始一个新的 Django 项目。我的就叫project。不要忘记在这个命令的末尾加上点。

django-admin startproject project .

7.启动一个叫films的 Django app。

python manage.py startapp films

8.打开project/settings.py并将django_extensionsfilms应用添加到INSTALLED APPS列表中。不要换其他线。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles', # add these lines: 
    'django_extensions',
    'films.apps.FilmsConfig',
]

9.应用迁移在 sqlite3 数据库中创建表。

python manage.py migrate

10.通过运行以下命令创建超级用户:

python manage.py createsuperuser

告知用户名、电子邮件和密码。稍后,您将使用这些数据登录管理区域。

11.使用python manage.py runserver运行服务器,访问http://localhost:8000并检查应用程序是否正常工作。

12.去http://localhost:8000/admin,使用你的登录数据,检查你是否可以访问管理区。

第二部分:定义模型

13.在films/models.py中,输入以下代码:

我们正在创建两个模型类(FilmGenre),它们之间有一个简化的关系,因为一部电影在这里只能有一个类型。将来,如果需要,您可以将这些表之间的关系改为多对多关系,或者您甚至可以向模型中添加其他实体,如主管或奖项。

Films类中,注意重要的get_fields()方法。这将为films_film表中除 id 之外的每一列创建一个标签/值对列表,我们可以在未来的film_detail.html模板中的 for 循环中使用这个结构。如果我们在一个模型中有几十甚至几百个列,这样的结构可能会更有帮助,所以这段代码可能值得保存起来供将来的项目使用。

第 3 部分:使用管理区创建新类别

14.运行命令在数据库中创建新表:

python manage.py makemigrations
python manage.py migrate

15.打开films/admin.py,写下这几行。现在FilmsGenre型号将出现在管理区:

from django.contrib import admin
from .models import Film, Genreadmin.site.register(Film)
admin.site.register(Genre)

16.接下来,登录到管理区域,选择Genre型号,然后创建并保存两到三个新流派。当我们使用 films CRUD 保存新电影时,我们将需要这些类型。

第 4 部分:构建应用程序路线和基于类别的视图

17.创建films/urls.py文件。

18.打开project/urls.py,导入include函数,并使用它创建一条新路线:

from django.contrib import admin
from django.urls import path, include # addedurlpatterns = [
    path('', include('films.urls')), # added
    path('admin/', admin.site.urls),
]

19.接下来,打开films/urls.py并添加以下代码。

CBV 尚未创建,因此此时运行服务器将会产生错误。

20.在films/views.py中,这段代码将创建我们需要的通用 CBV:

注意这段代码有多简洁:我们只创建了一个FilmBaseView,这样我们就不会重复modelfieldssuccess_url信息行,然后我们让我们的电影视图类继承了FilmBaseView和 Django 已经拥有的用于 CRUD 操作的通用视图类。所有这些都不需要使用表单逻辑,也不需要编写代码来访问数据库,然后将这些信息保存在上下文字典中。Django 的 CBV 已经设计出了这些程序。这样做的好处是,我们可以随时修改它们的任何方法,并根据我们的特定需求向视图类添加新的行为。

现在我们只需要创建模板来显示和更改我们的电影类视图提供的信息。

第 5 部分:创建模板

21.打开project/settings.py,在变量TEMPLATES中,用以下信息填充TEMPLATES['DIR']键中的空列表(只改变这一行,保持另一行不变):

TEMPLATES = [
{(...)'DIRS': [ BASE_DIR.joinpath('templates') ],(...)}
]

22.确保您在根项目中(文件夹films_project,与manage.py在同一层),然后创建这三个新文件夹。

mkdir templates films/templates films/templates/films

现在,是时候创建我们将用作电影 CRUD 模板的 HTML 文件了。然而,在我们这样做之前,一个实际的观察:事实上,我认为最好不要在这里复制模板代码,因为大部分代码都太长了。因此,我将提供一些链接,链接到保存在我的 GitHub 库中的每个模板文件的原始内容,以便您可以复制/粘贴或转录到您自己的代码中。

23.创建一个templates/base.html文件。这个文件不应该在films模板文件夹中,要小心。

24.在films/templates/films文件夹中,创建四个文件名如下的文件,因为这些是 Django 的通用视图使用的模板名称模式:

您可以通过在 Linux 终端(或 Git Bash)中运行这一行代码来立即创建这些文件:

touch films/templates/films/film_{confirm_delete,detail,form,list}.html

注意这些 HTML 文件名是如何在前面加上film_的,这是我们的模型名,用小大写字母加上下划线。在未来的 Django 项目中,当使用 CBV 创建 CRUD 应用程序时,您应该使用这种模式。

25.运行python manage.py check看看有没有错误;然后用python manage.py runserver运行服务器。您的 CRUD films 应用程序已经完成,现在您可以使用它来列出、添加、更新和删除电影信息。

第 6 部分:使用 PYTHON 脚本向你的数据库添加一些额外的电影

探索您的新应用程序,并对其进行一些更改。如果你想要额外的数据,你可以把我准备的 20 多部皮克斯电影加载到 Django 中。你只需要遵循以下步骤:

26.在manage.py的同一层创建一个scripts文件夹

27.创建一个films/pixar.csv文件,然后将该内容复制到其中

28.创建一个scripts/load_pixar.py文件,将该内容复制到其中

29.运行python manage.py runscript load_pixar。访问您的电影应用程序,并检查它是如何填充皮克斯电影信息的。

如果您想了解更多关于在 Django 中运行 Python 脚本的知识,我邀请您阅读我写的另一篇教程:

最后备注:制作你自己的 DJANGO CRUD 应用

现在,您已经拥有了使用自己的模型构建更复杂的 Django CRUD 应用程序的所有必要工具。

例如,假设您有三个表:机构、项目和课程。一个机构可以提交多个项目,一个项目可以有一个或多个与之相关的课程。

要在这种情况下制作 Django CRUD,您只需要在一个新的 Django 项目中按照我们上面讨论的这三个实体的步骤:

  • 在一个models.py文件中定义模型;
  • 在一个urls.py文件中,按照我们展示的模式,为每个模型创建五条路线。因为我们有三个模型,所以总共有 15 个路由,可能还有一个主页。你可以在导航栏中设置链接,直接进入各自的列表,所有页面的机构、项目和课程。
  • 将所有必要的代码放入views.py。我们为电影创建了六个基于类的视图,所以我们总共有 18 个 CBV:6 个用于机构,6 个用于项目,6 个用于课程,所有这些都在同一个文件中。
  • 最后,只需为您的 CRUD 创建 12 个模板文件(每个模型 4 个),遵循 HTML 名称模式并在模板 HTML 代码中进行必要的更改。

一旦你有了这个电影的启动代码,就可以很容易很快地为你自己的项目复制代码,并根据你的具体需要进行必要的调整。

我自己最近有一个非常好的经历,当我设法在仅仅几个小时的工作中构建了我上面向我妻子描述的几乎精确的 CRUD。而models.py变成了一个巨大的,例如,仅课程模型就有超过 50 列。所以,我花了大部分时间对数据建模;一旦完成,安装和运行 CRUD 应用程序就变得超级简单和快速。

在这段代码投入生产之前,我和我的妻子仍然会讨论一些调整和改进,但是这样的经历足以向我展示用 Django 构建 CRUD 应用程序是多么容易。如果你把你在这里学到的东西和一个更简洁、更精细的前端结合起来,你可能会用你新的 Django 编码技能给你的客户或潜在雇主带来惊喜。

亲爱的读者,非常感谢你花时间和精力阅读我的文章。

编码快乐!

在几个小时内建立一个搜索食物的聊天机器人

原文:https://towardsdatascience.com/build-a-food-searching-chatbot-in-hours-ca5b14c710b9

使用 RASA 开发面向任务的聊天机器人的指南

附身摄影Unsplash 上拍照

聊天机器人是对话式用户界面,在许多应用中日益流行,如 IT 服务台、客户服务、销售支持、商业、咨询服务等。

聊天机器人有两种类型,即通用型和面向任务型机器人。多面手聊天机器人是通用和复杂的,例如 Siri,它需要许多领域的知识。特定于任务的机器人要简单得多,但在某些领域(如 FAQ)工作效率很高。

在这篇博客中,作者将带您了解使用 RASA 为食品搜索应用程序构建聊天机器人的步骤。关键步骤如下:

明确目标和任务

由于人工智能是当今先进技术的中心,聊天机器人也被寄予厚望,希望能够智能地解决问题,自动完成许多任务。然而,它们不能由任何东西制成。聊天机器人可能需要准确的数据和领域内的深刻知识才能像人类一样聪明地行动。任务不明确,问题范围不具体,就会容易出错。

在本文中,聊天机器人将根据用户的位置和偏好的食物类别来找到相关的食物(即餐馆)。为了使项目的范围更加清晰,这个聊天机器人专注于帮助用户在当地城市环境中找到食物。一个包含城市中受欢迎的地方(地理位置)和餐馆的迷你知识库,是后端搜索引擎所必需的。

下图显示了聊天机器人的简单流程。在 RASA 中,聊天机器人使用 NLU 模块检测用户的位置和食物类别。然后后端即动作服务器运行搜索算法来返回匹配的餐馆。

食物搜索机器人的简单任务流程

为迷你知识库收集数据

这个聊天机器人以新加坡市为例。下表显示了这个城市几个受欢迎的地方。地理位置信息,如纬度和经度,是从开放的街道地图(OSM)下载的。

地点数据的示例

餐馆的数据通常来自食品和旅游相关网站或其他数据提供商。然而,为了避免版权问题,作者使用了合成数据,即新加坡市的几家不真实的餐馆作为例子。这些实施例的数据如下所示。与具体食品类别相关的图片来自 Unsplash

餐馆数据示例

培训 NLU 相关模块

实体和意图

地点和食物类别是从用户输入中提取的关键信息。检测的主要意图是“通知地点”、“通知食品类别”和“寻找食品”意图。这些意图的培训数据示例如下:

表格和故事

在 RASA 中,表单用于指导用户输入所有必要的数据。对于这个例子,由于用户输入了地点和食物类别,这是 RASA 表单的一个很好的用例。

RASA 中的故事是为了平滑对话流的过渡。下面的故事显示了触发食物探测器表单的条件(意图)。

运行并测试聊天机器人

要运行聊天机器人,需要先启动后端服务器,即动作服务器。

然后你运行 rasa x(网络聊天界面)来测试聊天机器人。与机器人的简短对话显示在下面的视频中。

结论和进一步改进

这篇博客介绍了构建一个面向任务的聊天机器人的主要步骤,比如搜索食物。所有的代码都在我的 git 中。

还需要做进一步的改进。例如,用户可以查询城市中受欢迎的地方、受欢迎的食物等。这些功能可以通过添加合适的意图和为这些对话流程设计故事来完成。

留下你的评论,非常感谢你的阅读!

参考

https://www.whoson.com/chatbots-ai/the-problem-with-generalist-chatbots/ https://www.openstreetmap.org/

用 Pydantic 和 Prefect 构建全栈 ML 应用程序

原文:https://towardsdatascience.com/build-a-full-stack-ml-application-with-pydantic-and-prefect-915f00fe0c62

用一行代码创建 ML 特征工程的用户界面

动机

作为一名数据科学家,您可能会经常调整您的特征工程流程,并调整您的机器学习模型以获得良好的结果。

不用深入代码来更改函数参数:

作者图片

…,如果能从 UI 中更改参数值不是很好吗?

作者图片

这就是 Pydantic 和 Prefect 派上用场的地方。在本文中,您将学习如何使用这两个工具来:

  • 通过 UI 调整您的函数输入值
  • 在运行函数之前验证参数值

作者图片

您可以随意使用本文的源代码:

https://github.com/khuyentran1401/iris-prefect

用提督创建用户界面

perfect 是一个开源库,允许你编排和观察用 Python 定义的数据管道。

https://medium.com/the-prefect-blog/orchestrate-your-data-science-project-with-prefect-2-0-4118418fd7ce

要安装提督,请键入:

pip install prefect

让我们使用 Prefect UI 为您的 Python 函数创建一个简单的前端应用程序。从用户界面运行该功能有三个步骤:

  • 把你的功能变成一种流动
  • 为流创建部署
  • 启动代理来运行部署

将功能转化为流程

从把一个简单的功能变成一个流程开始。

一个流程是所有完美工作流程的基础。要将process函数转换成流程,只需将flow装饰器添加到process函数中。

# process.py

from prefect import flow

@flow # add a decorator
def process(
    raw_location: str = "data/raw",
    process_location: str = "data/processed",
    raw_file: str = "iris.csv",
    label: str = "Species",
    test_size: float = 0.3,
    columns_to_drop: list = ["Id"],
):
    data = get_raw_data(raw_location, raw_file)
    processed = drop_columns(data, columns=columns_to_drop)
    X, y = get_X_y(processed, label)
    split_data = split_train_test(X, y, test_size)
    save_processed_data(split_data, process_location)

点击查看完整脚本

为流创建部署

接下来,我们将创建一个部署来从 UI 运行流程。一个部署是一个服务器端的概念,它封装了一个流,允许它通过 API 被触发。

为了在process.py文件中创建process流的部署,在您的终端中键入以下内容:

prefect deployment build process.py:process -n 'iris-process' -a

其中:

  • -n 'iris-process'指定部署的名称为iris-process
  • -a告诉提督同时构建和应用部署

要从 UI 查看您的部署,请登录到您的 Prefect Cloud accoun 或在您的本地计算机上启动 Prefect Orion 服务器:

prefect orion start

打开网址 http://127.0.0.1:4200/ ,应该会看到提督 UI:

作者图片

点击的【部署】标签查看所有部署。

作者图片

运行部署

要用默认参数值运行一个部署,选择该部署,点击按钮,然后点击【快速运行】

作者图片

要使用自定义参数值运行部署,单击按钮然后单击“自定义运行”

作者图片

您可以看到,Prefect 根据类型注释为您的流参数自动创建不同的输入元素。例如:

  • 文本字段用于label: strraw_file: strraw_location: strprocess_location: str
  • 数字字段用于test_size: float
  • 多行文本字段用于columns_to_drop: list

作者图片

我们可以通过以下方式增强用户界面:

  • 使用typing.List[str]columns_to_drop变为多选字段
  • 使用typing.Literal['option1', 'option2']raw_location变为下拉菜单。
from typing import List, Literal

@flow
def process(
    raw_location: Literal["data/raw", "data/processed"] = "data/raw", # replace str
    process_location: Literal["data/raw", "data/processed"] = "data/processed", # replace str
    raw_file: str = "iris.csv",
    label: str = "Species",
    test_size: float = 0.3,
    columns_to_drop: List[str] = ["Id"], # replace list 
):
...

要将更改应用到参数模式,再次运行prefect deployment build命令:

prefect deployment build process.py:process -n 'iris-process' -a

现在,您将看到一个多选字段和下拉菜单。

作者图片

要查看您的所有流程运行,单击Flow Runs选项卡:

作者图片

现在,当您查看最新的流程运行时,您会看到其状态为Late

作者图片

这是因为没有代理来运行部署。让我们通过在终端中键入以下命令来启动代理:

prefect agent start -q default

-q default标志告诉提督使用默认的工作队列。

启动代理后,流程运行将被代理拾取,并在完成后标记为Completed

作者图片

通过单击“参数”选项卡,您可以查看用于该特定运行的参数值。

作者图片

用 Pydantic 验证参数

在运行流程之前验证参数

Pydantic 是一个 Python 库,通过利用类型注释进行数据验证。

默认情况下,Prefect 使用 Pydantic 来强制流参数的数据类型,并在执行流运行之前验证它们的值。因此,带有类型提示的流参数被自动强制转换为正确的对象类型。

作者图片

在下面的代码中,类型注释指定test_size是一个浮动对象。因此,Prefect 将字符串输入强制转换成一个 float 对象。

@flow
def process(
    raw_location: str = "data/raw",
    process_location: str = "data/processed",
    raw_file: str = "iris.csv",
    label: str = "Species",
    test_size: float = 0.3,
    columns_to_drop: List[str] = ["Id"],
):
    ...

if __name__ == "__main__":
    process(test_size='0.4') # "0.4" is coerced into type float

使用 Pydantic 模型分组参数

还可以使用 Pydantic 将参数组织成逻辑组。

例如,您可以:

  • 将指定位置的参数分组到data_location组。
  • 将处理数据的参数归入process_config组。

作者图片

要完成这种很好的参数分组,只需使用 Pydantic 模型。

模型只是继承自pydantic.BaseModel的类。每个模型代表一组参数。

from pydantic import BaseModel

class DataLocation(BaseModel):
    raw_location: Literal["data/raw", "data/processed"] = "data/raw"
    raw_file: str = "iris.csv"
    process_location: Literal["data/raw", "data/processed"] = "data/processed"

class ProcessConfig(BaseModel):
    drop_columns: List[str] = ["Id"]
    label: str = "Species"
    test_size: float = 0.3 

接下来,让我们使用模型作为流参数的类型提示:

@flow
def process(
    data_location: DataLocation = DataLocation(),
    process_config: ProcessConfig = ProcessConfig(),
):
    ...

要访问模型的字段,只需使用model.field属性。例如,要访问DataLocation模型中的raw_location字段,请使用:

data_location = DataLocation()
data_location.raw_location

你可以在这里了解更多关于 Pydantic 模型

创建自定义验证

Pydantic 还允许您用validator装饰器创建定制的验证器。

让我们创建一个名为must_be_non_negative的验证器,它检查test_size的值是否为非负。

from pydantic import BaseModel, validator

class ProcessConfig(BaseModel):
    drop_columns: List[str] = ["Id"]
    label: str = "Species"
    test_size: float = 0.3

    @validator("test_size")
    def must_be_non_negative(cls, v):
        if v < 0:
            raise ValueError(f"{v} must be non-negative")
        return v

如果test_size的值为负,Pydantic 将产生一个ValueError:

pydantic.error_wrappers.ValidationError: 1 validation error for ProcessConfig
test_size
  -0.1 must be non-negative (type=value_error)

你可以在这里了解更多关于验证器的信息。

ML 项目中 Pydantic 和 Prefect 的用例

机器学习项目需要数据科学家经常调整 ML 模型的参数,以获得良好的性能。

使用 Pydantic 和 Prefect,您可以在 UI 上为每个参数选择一组值,然后在 GridSearch 中使用这些值。

# train.py

class DataLocation(BaseModel):
    raw_location: Literal["data/raw", "data/processed"] = "data/raw"
    raw_file: str = "iris.csv"
    process_location: Literal["data/raw", "data/processed"] = "data/processed"

class SVC_Params(BaseModel):
    C: List[float] = [0.1, 1, 10, 100, 1000]
    gamma: List[float] = [1, 0.1, 0.01, 0.001, 0.0001]

    @validator("*", each_item=True)
    def must_be_non_negative(cls, v):
        if v < 0:
            raise ValueError(f"{v} must be non-negative")
        return v

@flow
def train_model(model_params: SVC_Params = SVC_Params(), X_train, y_train):
    grid = GridSearchCV(SVC(), model_params.dict(), refit=True, verbose=3)
    grid.fit(X_train, y_train)
    return grid

查看完整脚本。

作者图片

结论

恭喜你!你刚刚学会了如何通过完美的用户界面参数化你的 ML 训练过程和特征工程。

调整参数值并确保它们具有正确的格式和数据类型的能力,将使您和您的队友在 ML 工作中更容易、更快速地试验不同的参数值。

我喜欢写关于数据科学概念的文章,喜欢玩不同的数据科学工具。你可以在 LinkedIn 和 T2 Twitter 上与我联系。

这个回购如果你想检查我写的文章的代码。在 Medium 上关注我,了解我的最新数据科学文章:

</4-pre-commit-plugins-to-automate-code-reviewing-and-formatting-in-python-c80c6d2e9f5>

使用 Greppo 在 Python 中构建地理空间仪表板

原文:https://towardsdatascience.com/build-a-geospatial-dashboard-in-python-using-greppo-60aff44ba6c9

缺乏前端、后端和 web 开发经验会限制用 Python 制作 web 应用程序。不再是了…

使用 Python 中的 Greppo 构建的地理空间仪表板。图片作者。

地理空间数据分析已经成为数据科学中的一个常见领域。除了绘图和可视化,它主要包括通过检测和识别模式来提取知识,并建立模型进行预测。Python 仍然是数据科学中的标准,对于地理空间数据科学也是如此。支持像 GeoPandasShapelyRasterio 这样的项目的社区已经使 Python 成为地理空间分析的首选。

作为一名数据科学家,与包括非技术人员在内的多个利益相关方合作是很常见的。将你的研究成果浓缩成非技术性叙述的能力非常有用。这一切都归结于沟通的结果如何能使利益相关者受益。为 Python 脚本创建仪表盘是讲述故事的有效方法。当您想要与外部利益相关者共享您的工作时,仪表板也很方便,只限制他们的交互和可视化,而不共享内部工作。

reppo:一种构建地理空间仪表板的快速简单的方法。

格雷波

Greppo 是一个用于构建地理空间网络应用的开源 Python 框架。它提供了现成的前端和后端组件作为函数,充当前端交互组件和绑定的后端变量之间的 API。要了解更多关于心智模型的信息,请参考第

TL;DR:没有太多的前端、后端和 web 开发经验,你可以用基本的 Python 在 5 分钟内构建和部署一个全功能的 web 应用程序。

在本教程中,我们将使用 Greppo 在 Python 中构建一个地理空间仪表板。我们将通过设置环境、安装、导入数据、为我们的仪表板添加组件和服务来完成工作。您需要了解 Python、GeoPandas 和地理空间分析的基础知识。

开始使用…

首先要做的是安装 Python 并设置您的 Python 环境。对于新手来说,可以看看这篇 博客文章 来帮助你开始使用 python 环境。一旦你准备好了,让我们安装我们的依赖项。

pip install greppo geopandas

下载本教程所需的数据集。本教程的所有数据和代码都可以在这个 GitHub 资源库中找到:GRE PPO-demo/vector-demo(数据来源:https://github.com/openpolis/geojson-italy)

编写仪表板脚本

我们从建立项目的文件夹结构开始。我将使用以下项目文件夹结构:

└── vector-demo
    ├── app.py
    ├── cities.geojson
    ├── regions.geojson
    └── roads.geojson

让我们为仪表板创建 Python 脚本。应用程序的代码进入 app.py

第 0 步:服务应用

要运行和提供应用程序,请打开一个终端并按照以下说明操作。首先,将目录(cd)切换到项目文件夹vector_demo。进入之后,激活安装了 greppo 的 Python 环境。我在这个例子中使用 pyenv。然后运行应用程序,你需要做的就是运行greppo serve app.py命令。命令serve,启动服务器并编译app.py中的代码,可以重命名为任何文件。注意:一定要在 app.py 所在的项目文件夹里面,或者使用greppo serve ./vector-demo/app.py这样的相对文件夹结构。

从终端运行和服务应用程序。图片作者。

然后您将看到 Uvicorn 服务器在指定的位置运行。将地址复制并粘贴到浏览器中。然后加载应用程序。如果您对app.py进行了任何更改,只需保存更改并重新加载 web-app 页面。您将在其中看到应用程序的更新更改。

第一步:为应用程序搭建基础架构。

即导入greppo包并将base_layer添加到地图中。

你需要先从greppoimport app。这个app对象将作为应用程序前端的接口,并为前端和后端之间的双向通信提供 API。

步骤 1 的结果。使用红色标记的控件进行切换。图片作者。

要将一个base_layer添加到您的应用程序地图中,只需使用带有所需参数的 app API 方法app.base_layer()。参考文档了解哪些是必需的,哪些是可选的。base_layer方法提供了两种方式来指定基础图块层。一种是使用nameurl属性。另一种方法是使用一个provider的名字。Greppo 为此使用了 xyzservices 。有关供应商名称的列表,请点击此处查看此列表。注:证明人名称应完整,如CartoDB PositronOpenStreetMap Mapnik所示。

第二步:导入数据集并将其显示为叠加图。

使用geopandas,我们可以将矢量数据集作为GeoDataFrame导入。然后可以使用app.vector_layer()方法在网络应用程序的地图上显示出来。

这里我导入了三个不同的数据集,每个数据集都有polygons (意大利不同地区的边界)lines(意大利的主要公路)和points(意大利的主要城市)。

使用方法app.vector_layer()作为前端的 API,我们可以显示矢量数据。您需要传入name,其他参数是可选的。虽然,将colorfillColor传递给style是区分和识别每一层的一个很好的练习。由于前端是基于活页的,所以所有的样式都符合活页的规格,并且可以在文档中找到。

步骤 2 的结果。使用红色标记的控件进行切换。

你可以在这里找到所有关于 vector_layer 和样式的信息。注意:你也可以做一个 Choropleth 地图。相关文档可在这里找到。

第三步:显示应用内文本、应用标题和应用描述

对于有用的 web 应用程序,它需要携带一些文本来为用户提供一些指导和上下文。这些可以显示在组件旁边的侧边栏上。使用方法app.display()可以在网络应用程序上显示降价文本。用同样的方法,可以设置 app 的标题描述

app.display()接受两个参数namevalue。当value包含要显示的文本时,name必须是唯一的,并携带文本的标识符。如果name=’title’传递的值是应用程序的标题,如果name=’description’传递的值是应用程序的描述。如果没有这两者中的任何一个,应用程序将使用其默认标题和描述。鼓励设置 app 的标题和描述。

步骤 3 的结果。使用红色标记的控件进行切换。图片作者。

第四步:以图表形式显示数据

一个数据应用程序很难不用图表显示数据。Greppo 还允许您将数据显示为图表。图表信息及其用法可在文档中找到。这里,作为一个例子,给出了一个条形图,app.bar_chart()

图表的必需参数是namexy值。可以添加一个description和一个color来给应用程序用户一个更好的上下文。namedescription与图表一起显示。

步骤 4 的结果。具有数据、显示功能、图表和控件。图片作者。

结论

让我们完整地看一遍我们在这里做了什么。我们的目标是使用 Greppo 创建一个地理空间 web 应用程序,显示一些 GIS 矢量数据,并添加组件,为应用程序的用户提供更好的环境。以下是该应用程序的完整代码:

这是应用程序的输出:

最后的结果。图片作者。

所以,我们有它。使用 Greppo 构建的完整网络应用程序,耗时不到 5 分钟。

这个演示的所有文件都可以在这里找到:https://github . com/GRE PPO-io/GRE PPO-demo/tree/main/vector-demo

查看 GitHub 资源库:此处将在 Greppo 上得到最新更新。如果您的用例有错误、问题或功能请求,请联系 Discord channel 或在 GitHub 上提出问题。用 Greppo 造了什么东西?贴 GitHub。

使用 Streamlit 构建命名实体识别应用

原文:https://towardsdatascience.com/build-a-named-entity-recognition-app-with-streamlit-f157672f867f

从构建应用到部署,包括代码

使用 Streamlit 的 NER 应用程序,图片由作者提供(来源)

在我之前的文章中,我们微调了一个命名实体识别(NER)模型,在 wnut_17[1] 数据集上训练。

在本文中,我们一步一步地展示了如何将这个模型与 Streamlit 集成,并使用 HugginFace Spaces 部署它。该应用程序的目标是实时标记每个用户请求的输入句子。

此外,请记住,与琐碎的 ML 模型相反,在 Streamlit 上部署大型语言模型是很棘手的。我们也应对这些挑战。

让我们开始吧!

为什么选择 Streamlit?

Streamlit 是一个易于使用的工具,用于创建基于数据科学项目的交互式应用程序。

类似的 ML 友好工具还有 破折号Gradio 。各有所长。例如,Gradio 有一个惊人的拖放组件,适用于图像分类模型。

一般来说,我更喜欢 Streamlit,因为:

  • 到目前为止,它有一个引人注目的轨迹——在过去的一年里,Streamlit 每月至少发布一次重大更新。
  • 它有一个强大的社区。论坛的成员非常乐于助人。还有,你可以在Streamlit Cloud上免费上传你的 app。如果您的应用程序很有趣,社区经理会联系您,并在 Streamlit 网站上展示您的应用程序!他们甚至会送你礼物!

除了增长和强大的社区,Streamlit 是一个成熟的工具,适合每个数据科学领域的交互式应用程序。

接下来,让我们建立我们的应用程序!

完整的例子可以在这里找到

构建应用程序

本文主要关注用 Streamlit 构建和部署我们的模型。

如果你想了解更多关于模型是如何产生的,请随时查看我的上一篇文章

不过有一个变化:我们使用了来自 HugginFaceroberta-large模型,而不是bert-base罗伯塔【2】引入了动态蒙版等几个新奇的东西,让罗伯塔优于伯特。

图书馆

首先,我们需要下面的库。为了清楚起见,请看一下requirements.txt文件:

pytorch-lightning==0.9.0
torch==1.10.0
torchtext==0.8.0
torchvision==0.11.1 
datasets==2.3.2
numpy==1.20.3
pandas==1.3.5
streamlit==1.11.1
transformers==4.12.5

流线美学

我们的目标是使我们的应用程序最小化,对 UX 友好。Streamlit 是这项工作的合适工具。

让我们设置页面的元数据:

在 2 行代码中,我们设置了页面的标题、标题和图标。

加载模型

我们为此任务创建了load_model函数:

注意以下事项:

  1. 我们使用[@st](http://twitter.com/st).cache装饰器来缓存我们的模型——因为它太大了(~2BG),我们不想每次都重新加载它。
  2. 我们使用allow_output_mutation=True来告诉 Streamlit 我们的模型应该被视为一个不可变的对象——一个 singleton

为标签生成添加帮助函数

接下来,我们添加助手函数。稍后我们将使用tag_sentence函数为输入的句子生成标签。

添加下载结果的助手功能

有时,如果用户可以下载预测结果作为一个单独的文件(例如,供以后使用)会很有帮助。

Streamlit API 为此提供了st.download_button我们将展示如何将我们的结果转换成 CSV文本JSON 格式。对于此任务,我们使用以下帮助函数:

下载按钮将如下所示:

****注意:目前 Streamlit 存在一个 bug,有时会出现文件下载不正确的情况。

或者,我们可以以自定义方式在中创建下载按钮。该组件的代码作为注释包含在应用程序的代码中。

创建表单

我们现在已经完成了设置,并准备好构建我们的数据管道。

该应用程序应执行以下操作:

  1. 接收用户输入。
  2. 检查输入的句子是否为空。
  3. 检查用户输入的句子是否包含一个单词(没有必要标记一个单词)。
  4. 如果一切正常,加载我们的模型并计算输入句子的标签。
  5. 在 UI 中呈现结果。

因此,我们有:

就是这样!文本形式将如下所示:

可选—添加“关于”部分

出于 UX 的目的,我们可以在本页底部添加一个关于的部分:****

这是该部分的显示方式:

部署

目前,有三种方法可以免费部署 Streamlit 应用程序:

  1. 流线云
  2. 拥抱脸空间
  3. 赫罗库

所有选项都非常简单——没有成本,也不需要容器。

对于我们的例子,我们选择 HugginFace Spaces ,因为它可以更好地处理大文件。流程如下:

1.设置 Git

首先,确保您已经安装了 git。

2.安装 Git LFS

因为我们的模型是一个大于 1GB 的大型二进制文件,所以我们还应该安装 Git LFS 这些可以版本化大型文件的软件。

要下载它,请遵循此处的说明。该页面包括针对 WindowsMac、Linux 的说明。

3.添加需求文件

Hugginface 要求我们提供一个包含项目使用的库的requirements.txt文件。

我们可以使用pipreqs库立即生成一个requirements.txt文件。另外,pipreqs只生成我们的项目使用的库:

pip install pipreqs
pipreqs /cd/to/project

4.登录 HugginFace 并创建一个共享空间

如果您还没有 HugginFace 帐户,请访问此页面

然后,创建一个空间(你会在右上角找到)。本质上,空间充当传统的 Git repo。填写所需的详细信息并初始化 youe repo。

之后,克隆你的 repo,将你的项目文件添加到文件夹中,上传到空间:****

git clone [https://huggingface.co/spaces/nkaf/ner-tagger-streamlit](https://huggingface.co/spaces/nkaf/ner-tagger-streamlit)
cd /cd/to/projectgit add .
git commit -m “first commit”
git push origin main

就是这样!

4.访问您的应用

你必须等待几分钟,应用程序才能初始化。然后,进入APP标签,如果一切正常,你的 web 应用就上线了!****

您可以在此找到这个 web 应用程序的项目!请随意输入您的句子并进行实验!

来看几个例子吧!

例 1:

Apple announces the new MacBook Air, supercharged by the new ARM-based M2 chip

该模型正确地将Apple标记为公司。此外,它还能正确识别和识别MacBook AirARM-based M2产品。****

例二:

Empire State building is located in New York, a city in United States

我们的模型再次正确地识别了句子的所有 3 个位置。

结束语

Streamlit 是一款易于使用的工具,在展示数据科学项目的功能方面非常高效。

此外,您可以将您的数据科学项目与 Streamlit 无缝集成。

最后,请注意我们只使用了 Python——我们可以在几乎不了解 HTML、CSS 或 Javascript 的情况下创建惊人的 Streamlit 应用程序。此外,Streamlit 还兼容流行的数据科学库,如 NumpyPandasOpenCVPlotly

感谢您的阅读!

参考

  1. WNUT 2017 (WNUT 2017 新兴和稀有实体认可),许可证:CC BY 4.0,来源
  2. 刘等,RoBERTa:一种鲁棒优化的 BERT 预训练方法 (2019)

使用 Tess4J——一种用于 Java 的 Tesseract 包装器,通过 4 个步骤构建一个可移植的 OCR 工具

原文:https://towardsdatascience.com/build-a-portable-ocr-tool-in-4-steps-with-tess4j-jar-a-tesseract-wrapper-for-java-6d1be3f0cb3d

探索 Tess4J 的第一部分。完整的源代码(Java SDK 1.8)和应用程序链接包括在内。

作者插图|通过 OCR 技术提取图像到文本

辅助项目的基本原理

随着多个经济领域越来越认识到数据的价值,许多商业实体毫不奇怪地陷入了数字化的热潮——即将信息转换为计算机可读格式。

❝虽然工作场所的数字化非常有益,但是从硬拷贝文件中提取数据仍然是 information❞成功全面计算机化的一个明显障碍

随着 光学字符识别 ( OCR ) 技术的出现,幸运的是,手动数据提取的开销大大减少了。虽然每个 OCR 引擎在文本提取功能方面都有其优势和劣势,但我在本文中选择的实现应该是 Tesseract-OCR ,因为它是开源的,拥有强大的社区支持和丰富的文档。由于我过去的一些与 OCR 相关的创作只利用了 TesseractJS ,OCR 引擎的一个纯 JavaScript 端口(完整的实现细节请参考下面的文章)

https://javascript.plainenglish.io/build-a-text-to-speech-app-using-client-side-javascript-98cd72df73bb

我决定使用Tess4J(Java 中的 Tesseract-OCR)来代替,以便用不同的方法更深入地研究 Tesseract-OCR

Tess4J 的技术实现

在开发文本提取工具之前,必须首先检索 Java 应用程序的所有依赖项。

先决条件

i. 取两个.dll文件:liblept1744.dll & libtesseract3051.dll

./Lept4J/lib/win32-x86–64/liblept1744.dll
  • 下载Tess4J-3 . 4 . 1-src . zip(仅供参考:对于此项目,使用的 Tess4J 版本是 3.4.1) 对于 64 位 Windows,在以下位置提取所需文件:
./Tess4J/lib/win32-x86–64/libtesseract3051.dll

****二。继续在您的首选 IDE(例如 NetBeans IDE)上设置一个新的 Java 应用程序项目。此后,它应该看起来像这样:

图片作者|上面的截图显示了 NetBeans IDE 中新创建的 Java 应用程序项目。在这个设置中,我在 main.class 所在的位置创建了一个名为“gui”的包。

在项目中构建应用程序的 4 个步骤

(注意:为了简单起见,项目名称被指定为 Tess4jOcrApp )

****第一步。在之前提取的同一个 Tess4j 文件夹中,在./Tess4J/src文件夹下,继续复制两个文件夹— com + net,并通过 IDE 粘贴到项目中:

作者截图|添加两个文件夹— com + net后,应该和上面差不多。

****第二步。检索两个.dll文件:liblept1744.dll libtesseract3051.dll

****主要目标:将两个文件作为应用程序源代码的一部分包含到包net.sourceforge.tess4j

作者图片|截图显示 2。dll 文件反映在 IDE 上的项目源代码中

  • 为此,选择项目并右击添加一个空文件****

作者截屏|右键单击并在上下文菜单中显示添加“空文件”的选项。

  • 将空文件重命名为liblept1744.dll ,并手动将其替换为项目中的实际文件。对libtesseract3051.dll重复相同的步骤

****步骤三。导入 JAR 依赖项— 2 个选项

****选项(1)。所有 JAR 依赖项都可以通过文件夹Tess4J从其原始源中检索

./Tess4J/lib/*.jar
./Tess4J/dist/tess4j-3.4.1.jar

****选项②。或者,可以随意下载我的 GitHub 中整合的 JAR 文件的完整列表—Tess 4 joc rapp/tree/main/app/lib

(注意 flatlaf-2.4.jar 是可选的,因为它是 Java Swing GUI 的实用程序,而不是 OCR 依赖项。)

作者截图| JAR 依赖项列表在上图中用黄色框突出显示。

****第四步。继续从./Tess4J/tessdata复制文件夹tessdata并粘贴到项目的工作文件夹中。

(重要!** —这是训练 Tesseract ML 模型识别英文字符所必需的。)**

最后,为了确保项目已经正确配置并按预期工作,只需通过在主类中运行几行代码来测试 OCR 功能。这至少应包括—

  • 宇宙魔方的一个实例
  • 镶嵌方 ML 模型的数据路径(tessdata)配置
  • 通过解析包含英文字符的输入图像文件,调用主要函数:doOCR()

****注意:用于测试的图像链接可从 bday_card.jpg 获得

作者代码片段|调用函数 doOCR()以确保应用程序能够准确地检测和输出输入图像中的嵌入文本。

作者截图|文本提取完成后,程序从输入图像源( bday_card.jpg )输出所有检测到的英文字符

从上面的显示,排除标点符号和其他特殊字符,应用程序的 OCR 功能显示能够识别输入图像✔️中存在的所有英文字母💯

最后,图像到文本提取工具已经成功创建并可以使用了!🤩

(可选)文本提取工具的用户界面

验证完代码运行正常后,我选择更进一步,用 Java Swing 的 GUI 工具包创建了一个图形用户界面(GUI) :

作者截屏|展示文本提取工具的 GUI 和功能

作者应用程序截图|左侧面板—呈现上传的图像|右侧面板—选择【运行 OCR】后,所有提取的文本都输出到文本区

****参考消息:完整的源代码可以从我在 Tess4JOcrApp 的 Github repo 中检索到。请随意到⭐和🔱它!

个人评论

虽然 Tess4J 的 OCR 特性已经成功集成到 Java 应用程序中,但其他常见的输入格式(如 PDF 文档)尚未包含在此次迭代中。由于 Tess4J 仍有很大的探索空间,该项目的下一部分不仅要继续处理图像文件,还要继续处理 PDF 文档。

现在你知道了!非常感谢你坚持到这篇文章的结尾!❤ 由于第二部分仍在进行中,如果你觉得这篇文章有用,并希望继续关注这个宠物项目即将到来的内容,请随时关注我的媒体!会非常感激—😀

— 🌮请给我买一份玉米卷🎀˶❛◡❛)

**https://geek-cc.medium.com/membership **

构建一个文件夹项目,将[几个]文档组织成主题

原文:https://towardsdatascience.com/build-a-portfolio-project-for-organizing-few-documents-into-topics-b706e540fc7

从预训练嵌入到层次聚类

图片来自 Pixabay

试图理解数字文本时的一个挑战是确定我们是否可以将文本组织成有意义的组或主题。例如,也许我们想知道是否可以将客户的评论分为讨论登录问题的和抱怨客户服务的。

主题建模的常用方法是潜在差异分配(LDA)。尽管对这种方法的全面讨论超出了本文的范围,但简而言之,其思想是将语料库中单词的概率分配给相应的主题。在 LDA 中,我们在分析开始时为模型提供预期的主题数量。在此基础上,该模型分配概率,帮助将每个词归入特定主题。关于这个概念如何工作的更多细节,请查看这篇文章

这种方法的优点是每个主题由语料库中的单个单词或标记决定。因此,这种方法允许我们给一个给定的文档分配一个以上的主题,只要文档中存在足够的标记,每个主题都有足够高的可能性。例如,假设我们的主题模型了解到像“密码、登录和访问”这样的标记与我们标记为“登录问题”的主题密切相关,而像“客户、服务和代表”这样的标记与我们标记为“客户服务投诉”的第二个主题密切相关。

现在,让我们假设我们正在审查以下客户意见:

“登录和访问[我的]帐户时遇到问题,因此[我]致电客户服务,不得不等待很长时间才能解决问题。”

很可能我们的主题模型会将最高的可能性分配给主题“登录问题”,但也可能将第二高的可能性分配给“客户服务投诉”LDA 使我们能够在给定阈值的文档中寻找多主题解决方案,我们可以基于后处理研究选择应用该阈值。

但是因为 LDA 只关注单个的标记,而没有考虑它们与其他标记的关系或相似性,所以 LDA 在处理从具有相似语义的标记中识别主题时失败了。

例如,假设您经营一家日托企业,您从父母那里收集反馈,了解他们希望孩子在日常活动中学习的概念。也许你会发现一些父母谈论“社会技能发展”的重要性,而另一些父母谈论“社会化”。

在这种情况下,我们可能希望我们的主题模型认识到,由于“社会化”和“社交”等概念之间的语义相似性,这两种想法应该被分组到一个主题中,但是 LDA 可能会将它们视为单独的主题,这取决于每个标记所在的文档中使用的其他单词。

此外,当我们只有少量数据要处理时,LDA 也有局限性。因为 LDA 过程试图将给定主题的单词概率的混合拟合到已知分布(例如,Dirichlet 分布),所以如果单词仅在数据中出现几次,则它们对不同主题的拟合的估计将是不稳定的,从而产生通常难以解释的主题。

在本文的剩余部分,我将研究一种方法来克服 LDA 在主题分析中应用的两个主要限制。也就是说,需要大量的数据,却没有上下文敏感性。在接下来的部分中,我将介绍一个解决方案,它利用预训练的单词嵌入和层次聚类来捕获更多的上下文感知主题,这些主题可以从较小的数据集中以有意义的方式学习。向前向上!

问题

我最近遇到了这个问题,我想将新闻文章分组到有意义的组中,但只有不到 200 个文本可以处理。此外,文本包括语义相似的术语,如“数据、科学、机器和学习”,我正在寻找一种主题解决方案,它仍然可以识别语义相似术语中的主题,而不是将“数据科学”放在一个主题中,将“机器学习”放在另一个主题中。

换句话说,我想要一个比传统的 LDA 更有语义意识的文本分组模型。我确实研究了更多使用 LDA 的上下文感知方法,比如 lda2vec,在这里详细描述了

lda2vec 方法实际上是 lda 的复杂实现,它将单词向量与文档向量相结合,以训练更具上下文感知的主题模型。然而,不利的一面是,因为该模型利用了向量表示并结合了神经网络架构,所以可训练参数的数量比更传统的 LDA 方法增加了相当多。最终,这意味着需要更多的数据和大量的计算资源来训练模型。

因此,对于我当前的问题,lda2vec 不是一个好的候选。尽管有局限性,但是这种方法确实给了我一个想法。由于我没有太多的数据,我想使用预先训练的模型可能会有所帮助。

解决方案

为了解决我的问题,我尝试了以下架构:

1.预处理文本并生成标记

2.使用 GloVe 100 维预训练 word2vec 模型获得每个单词的单词向量

3.平均每个文档的单词向量以创建平均文档向量

4.使用 Ward 方法执行层次聚类

5.获取每个聚类的质心向量

6.通过使用余弦相似性将文档向量与其聚类的质心向量进行比较,为每个聚类识别最具代表性的文档

对于这个实现,我使用了 1 周的 Google Alerts 来搜索与“数据科学、人工智能和机器学习”概念相关的新闻文章

为什么只有一周的新闻提醒?我每周都进行这种分析,以告知 Youtube 上的一个节目,我通过我的 FastdatascienceAi 频道称之为“人工智能的这一周”。因此,我需要一种方法,用有限的数据每周衍生出新的主题。但是我跑题了。

1。预处理文本并生成令牌

对于这个例子,我们从一个数据集开始,这个数据集是通过从我的 Gmail 地址中提取 Google 新闻提醒电子邮件而创建的。关于如何用 Python 做这件事的更多信息,请看这篇的帖子

的。带有新闻结果的 csv 文件被放入熊猫数据框。以下是数据框的外观:

作者图片

在那里,我使用 NLTK 提供的工具来清理和标记文本。清理和标记化是自然语言处理任务的基本标准,包括删除多余的空格、非字母字符、停用词、标记化和词条化。以下是加载我的自定义清理 python 模块并使用“pre_process_lda”函数清理数据框的文本列的一些代码:

2 & 3。获取单词向量…

在下一部分代码中,我们将每个标记传递给一个预训练的 Word2Vec 模型(即手套模型),以返回每个单词在向量空间中的 100 维表示。然后,我们对每个文档的这些词向量进行平均,以获得每个文档的 100 维表示。请注意,下面的代码假设您已经下载了手套 6B 预训练的向量

4。执行层次聚类…

现在我们有了一个基于文档中每个标记的平均值的文档向量(在这种情况下,每个文档都是一篇新闻文章的标题),下一个任务是确定一种将文档组织成组的方法。

因为我们期望每个文档已经包含相似的向量空间(它们都是关于数据科学、机器学习或人工智能的),而不知道其中的子组(例如主题)的数量,并且因为我们只有少量的数据,所以我选择使用分层聚类。

层次聚类和系统树图的使用使我们能够快速分析不同组合水平的数据分组。在最高级别上,所有数据都在一个集群中,但是在最低级别上,每个文档都代表它自己唯一的“集群”层次聚类为我们提供了研究如何在这两个极端之间将数据组织成组的工具。

在这个特定的情况下,我使用 Ward 方法实现了层次聚类。像 Ward 方法这样的层次聚类的最大优点之一是,我们不需要在分析之前提供预期的聚类数。

如上所述,树状图通常伴随着层次聚类方法,因为它们帮助我们在不同的聚集级别可视化聚类解决方案。此外,我们还可以看到哪些聚类可能比其他聚类更密切相关,因为较高聚合级别的组合表明两个较低级别的聚类更相似。

作者图片

在上面的结果中,我们从树状图中看到,我们的最佳解决方案是一个 4 主题解决方案。

5。获得每个簇的质心

既然我们已经确定我们的最佳解决方案是 4 主题解决方案,我们的下一步是获得每个聚类的质心向量。最终,我们的目标是能够使用这些质心向量来识别最能代表质心的文档。

6。确定每个集群最具代表性的文件

找到质心向量后的最后一步是将每个簇中的每个文档与其簇的质心进行比较。在此之前,我们将数据框与上一步中创建的每个文档的预测聚类(“y_hc”)和每个文档的向量连接起来。

因为我们正在比较数字向量,这是余弦相似性的一个很好的用例。余弦相似性是一个数学公式,用于确定两个数字序列之间的相似性。在这种情况下,我们有两个 100 个数字的序列(即我们的维度向量)进行比较。

因此,我们使用余弦相似度来计算每个文档和聚类质心之间的相似度。然后为每个聚类选择余弦相似度最接近 1 的文档。

以下是与每个集群相关的标题列表:

作者图片

简单地基于这个列表,我们可以为每个集群推理一个可能的标签,如下所示:

作者图片

结论

概括地说,我在开始时面临一个特殊的业务问题,我需要一种方法来将少量的文档组织成上下文相关的有意义的主题。为了实现一个解决方案,我利用了一个预训练的单词嵌入模型和层次聚类方法来根据标题对新闻文章进行分组。

比如参与学习数据科学、职业发展、生活或糟糕的商业决策?加入我

使用 PyTorch 构建问答应用程序

原文:https://towardsdatascience.com/build-a-q-a-app-with-pytorch-cb599480e29

如何使用 Docker 和 FastAPI 轻松部署 QA HuggingFace 模型

照片由 Pexels 的 pixabay 拍摄。

目录

  1. 介绍
  2. 定义搜索上下文数据集
  3. 构建质量保证嵌入模型
  4. 使用 Docker 和 FastAPI 部署模型
  5. 结论
  6. 参考

介绍

在过去的几年里,从计算机视觉到自然语言处理,大量的预训练模型已经可用,其中一些最知名的聚合器是 Model ZooTensorflow HubHuggingFace

如此大的一组预训练模型的可用性允许开发人员重用这些模型,而无需花费大量的时间和金钱来训练它们。例如,使用 Tesla V100 实例训练一个 GPT-3 模型将花费超过 460 万美元。

在本帖中,我们将讨论:

  1. 如何使用 HuggingFace 上提供的预训练 PyTorch 模型创建问答(QA)模型;
  2. 如何使用 Docker 和 FastAPI 部署我们的定制模型?

定义搜索上下文数据集

有两种主要类型的 QA 模型。第一种方法将领域特定知识的大型语料库编码到模型中,并基于学习到的知识生成答案。第二种方法利用给定的上下文,从上下文中提取最佳段落/答案。

第二种方法更容易推广到不同的领域,无需重新训练或微调原始模型。因此,在这篇文章中,我们将重点讨论这种方法。

要使用基于上下文的 QA 模型,我们首先需要定义我们的“上下文”。这里,我们将使用斯坦福问答数据集 2.0。要下载该数据集,请点击此处

下载这个文件后,用 Python 打开它,检查它的结构。

观察到的结构应该类似于下面提供的结构。根据这些数据,我们将关注主题为Premier Leaguequestionanswers字段。这将为我们提供具体问题的确切答案。如果您想从上下文段落中提取答案,请查看context字段。

'data': [
  'topic1': {
    'title': str,
    'paragraphs': [
      'paragraph1':{
        'qas': [
          'qa1':{
            'id': str,
            'is_impossible': bool,
            '**question**': str,
            '**answers**': [
              'answer1':{
                'text': str,
                'answer_start': int
              }
              ...
            ],
          },
          ...        
        ]
      },
      ...
    ],
  'context': str
}

为了获得questionsanswers,定义并运行以下函数get_qa。这将返回一组 357 对问题和答案。

构建质量保证嵌入模型

简而言之,我们的模型将通过比较来自用户的新问题和我们的上下文集中的问题集,然后提取相应的答案来工作。

由于我们无法比较原始格式(文本)的问题,因此在执行任何相似性评估之前,我们需要将上下文问题和来自用户的未知问题转换到一个公共空间。

为此,我们将定义一个新的文本嵌入类,用于将上下文和用户的未知问题从文本转换为数字向量。

1.下载预训练的嵌入模型

如“简介”部分所述,从头开始训练一个模型既费时又费钱。因此,让我们使用一个已经在 HuggingFace 上训练好的模型。

将下面的脚本保存到一个名为download_model.sh的文件中,在带有bash download_model.sh的终端中运行,下载所需的模型文件。

2.在本地测试嵌入模型

如果你没有transformers包,用 pip 安装它。

pip install transformers[torch]

然后,在新的笔记本单元格中定义并运行下面的get_model函数。如果所有文件都已正确下载,并且所有依赖项都已满足,那么运行起来应该没有问题。

现在让我们在一个上下文问题的样本上运行我们的嵌入模型。为此,请运行以下指令。

上面的脚本应该打印出我们新的embeddings向量的形状。

Embeddings shape: torch.Size([3, 384]

3.测试上下文与新问题的相似性

让我们从检查之前的样题开始:

[
  'How many club members are there?',
  'How many matches does each team play?',
  'What days are most games played?'
]

然后,将最后一条解释为:

'Which days have the most events played at?'

最后,嵌入我们的新问题,计算new_embeddingembeddings之间的欧氏距离。

上面的脚本应该输出以下距离,表明我们样本中的最后一个问题确实是距离我们的新问题最近的(最小的距离)。

tensor([71.4029, 59.8726, 23.9430])

使用 Docker 和 FastAPI 部署模型

上一节介绍了定义 QA 搜索模型的所有构件。要使其在生产环境中可用,我们需要:

  1. 将前面的函数包装在一个或多个易于使用的类中;
  2. 定义一个 app,通过 HTTP 调用需要的类方法;
  3. 将整个应用程序和依赖项包装在一个容器中,以便于扩展。

1.定义 QA 搜索模型

让我们将前面介绍的概念包装成两个新的类:QAEmbedderQASearcher

QAEmbedder将定义如何从磁盘加载模型(get_model)并返回给定一组问题(get_embeddings)的一组嵌入。注意为了效率get_embeddings会一次嵌入一批问题。

QASearcher将设置相应问题和答案的上下文(set_context_qa),并将我们上下文中最相似问题的答案返回给用户新的未看到的问题(get_answers)。

2.定义 FastAPI 应用程序

我们的应用程序应该包含 2 个 POST 端点,一个用于设置上下文(set_context),一个用于获取给定的未知问题的答案(get_answer)。

set_context端点将接收包含 2 个字段(questionsanswers)的字典,并更新QASearcher

get_answer端点将接收具有 1 个字段(questions)的字典,并返回具有原始问题(orig_q)、上下文中最相似的问题(best_q)和相关答案(best_a)的字典。

3.构建 Docker 容器

最后一步是将我们的应用程序包装在 Docker 容器中,以便更容易地分发和扩展。在我们的 docker 文件中,我们需要:

  1. 安装wget和所需的 Python 库transformersuvicornfastapi
  2. 从 HuggingFace 下载预先训练好的 QA 模型;
  3. 将所有应用程序文件(此处可用)复制到 Docker 镜像并运行 uvicorn 应用程序。

要测试我们的新应用程序,使用以下命令构建并运行 Docker 映像:

docker build . -t qamodel &&\
  docker run -p 8000:8000 qamodel

如果一切顺利,您应该会收到以下消息:

INFO:     Started server process [1]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

4.测试正在运行的应用程序

为了测试我们的应用程序,我们可以简单地使用requests来设置上下文,然后检索给定的新的看不见的问题的最佳答案。

要测试set_context端点,请运行以下脚本:

这应该会返回以下消息:

{'message': 'Search context set'}

要测试get_answer端点,请运行以下脚本:

这将返回以下消息:

orig_q : How many teams compete in the Premier League ?
best_q : How many clubs are currently in the Premier League?
best_a : 20

orig_q : When does the Premier League starts and finishes ?
best_q : When does the Premier League have its playing season?
best_a : During the course of a season (from August to May)

orig_q : Who has the highest number of goals in the Premier League ?
best_q : Who has the record for most goals in the Premier League?
best_a : Newcastle United striker Alan Shearer holds the record for most Premier League goals with 260

完成脚本

如需完整的脚本,请点击以下链接进入我的 GitHub 页面:

https://github.com/andreRibeiro1989/medium/tree/main/qa_model

结论

通过利用免费提供的预训练模型,构建强大的计算机视觉和自然语言处理模型正变得越来越容易。

在本文中,我介绍了使用 HuggingFace、Docker 和 FastAPI 构建自己的问答应用程序的基本构件。请注意,这一系列步骤并不特定于问答,但确实可以用于大多数计算机视觉和自然语言处理解决方案。

如果你对以无服务器方式部署这个应用感兴趣,可以看看我以前的文章“用 Amazon Lambda 和 API Gateway 构建无服务器 API”⁴.

如果您刚刚开始了解 PyTorch,并希望快速了解它,那么这篇文章可能会让您感兴趣"py torch 入门 "⁵.

加入我的邮件列表,我一发布新内容,你就能收到新内容!

如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,让你可以无限制地访问 Python、机器学习和数据科学文章。如果你使用我的链接注册,我会赚一小笔佣金,不需要你额外付费。

https://andrefsr.medium.com/membership

参考

[1]https://lambdalabs.com/blog/demystifying-gpt-3/李川【OpenAI 的 GPT-3 语言模型:技术概述】

[2] Pranav Rajpurkar 等著《小队:机器理解文本的 10 万+问题》(2016),arXiv
https://arxiv.org/abs/1606.05250v3

[3] Stanford 问答数据集(SQUAD)——在 CC BY-SA 4.0 许可下发布——可在 https://rajpurkar.github.io/SQuAD-explorer/获得

[4]里贝罗。"用亚马逊 Lambda 和 API 网关构建无服务器 API "
https://towardsdatascience . com/Build-a-server less-API-with-Amazon-Lambda-and-API-Gateway-DFD 688510436

[5]里贝罗。"py torch 入门" https://towardsdatascience . com/Getting-started-with-py torch-2819 D7 aeb 87 c

构建一个健壮的工作流,用 Python 可视化趋势 GitHub 存储库

原文:https://towardsdatascience.com/build-a-robust-workflow-to-visualize-trending-github-repositories-in-python-98f2fc3e9a86

在本地机器上获取 GitHub 的最新趋势

动机

GitHub feed 是一个很好的方式,可以让你跟踪社区的趋势。你可以通过查看你的连接来发现一些有用的存储库。

作者图片

然而,可能有一些您不关心的存储库。例如,您可能只对 Python 库感兴趣,但是在您的 GitHub feed 上有用其他语言编写的库。因此,您可能需要一段时间才能找到有用的库。

如果您可以创建一个个人仪表板,显示您的连接所遵循的存储库,并按您喜欢的语言进行过滤,如下所示,这不是很好吗?

作者图片

在本文中,我们将学习如何使用 GitHub API + Streamlit + Prefect 来实现这一点。

我们将做什么

总体而言,我们将:

  • 使用 GitHub API 编写脚本从 GitHub 中提取数据
  • 使用 Streamlit 创建一个显示已处理数据统计信息的仪表板
  • 使用提督计划运行脚本以获取和处理每日数据

作者图片

如果您想跳过解释,直接创建自己的仪表板,请查看这个 GitHub 资源库:

https://github.com/khuyentran1401/analyze_github_feed

从 GitHub 提取数据

要从 GitHub 获取数据,您需要您的用户名和一个访问令牌。接下来,创建名为的文件。env 保存你的 GitHub 认证。

.env添加到您的.gitignore文件中,以确保.env不会被 Git 跟踪。

development目录下的文件process_data.py中,我们将使用 python-dotenv 编写代码来访问文件.env中的信息:

接下来,我们将使用 GitHub API 获取我们在 GitHub 提要中收到的公共存储库的一般信息。GitHub API 允许我们轻松地从您的 GitHub 帐户中获取数据,包括事件、存储库等。

作者图片

我们将使用 pydash 从存储库列表中获取所有标有星号的存储库的 URL:

获取每个带星号的存储库的具体信息,如语言、星号、所有者、拉取请求等:

从每个回购中提取的信息应该类似于这个

将数据保存到本地目录:

将所有东西放在一起:

使用 Prefect 使工作流健壮

当前的脚本可以工作,但是为了确保它对失败有弹性,我们将使用 Prefect 为我们当前的工作流添加可观察性、缓存和重试。

perfect是一个开源库,使您能够用 Python 编排数据工作流。

https://medium.com/the-prefect-blog/orchestrate-your-data-science-project-with-prefect-2-0-4118418fd7ce

添加可观察性

因为运行文件get_data.py需要一段时间,我们可能想知道哪个代码正在被执行,以及我们大概需要等待多长时间。我们可以在函数中添加 Prefect 的 decorators,以便更深入地了解每个函数的状态。

具体来说,我们将装饰器@task添加到做一件事的函数中,将装饰器@flow添加到包含几个任务的函数中。

在下面的代码中,我们将装饰器@flow添加到函数get_data中,因为get_data包含所有任务。

运行此 Python 脚本时,您应该会看到以下输出:

从输出中,我们知道哪些任务已经完成,哪些任务正在进行中。

贮藏

在我们当前的代码中,函数get_general_info_of_repos需要一段时间来运行。如果函数get_specific_info_of_repos失败,我们需要再次运行整个管道,并等待函数get_general_info_of_repos运行。

作者图片

为了减少执行时间,我们可以使用 Prefect 的缓存来保存第一次运行get_general_info_of_repos的结果,然后在第二次运行时重用这些结果。

作者图片

在文件get_data.py中,我们为任务get_general_info_of_reposget_starred_repo_urlsget_specific_info_of_repos添加了缓存,因为它们需要相当多的时间来运行。

要向任务添加缓存,请指定参数cache_key_fncach_expiration的值。

在上面的代码中,

  • cache_key_fn=task_input_hash告诉提督使用缓存的结果,除非输入改变或者缓存过期。
  • cached_expiration=timedelta(days=1)告诉提督在一天后刷新缓存。

我们可以看到,不使用缓存(lush-ostrich)的运行时间是 27 秒,而使用缓存(provocative-wildebeest)的运行时间是 1 秒!

作者图片

注意:要像上面一样查看所有运行的仪表板,运行 *prefect orion start*

重试次数

我们有可能无法通过 API 从 GitHub 获取数据,我们需要再次运行整个管道。

作者图片

与其重新运行整个管道,不如在一段特定的时间后重新运行失败了特定次数的任务更有效。

提督允许你在失败时自动重试。要启用重试,请向您的任务添加retriesretry_delay_seconds参数。

在上面的代码中:

  • retries=3告诉提督最多重新运行任务 3 次
  • retry_delay_seconds=60告诉提督 60 秒后重试。这个功能很有用,因为如果我们在短时间内连续调用 GitHub API,我们可能会达到速率极限。

过程数据

在目录development下的文件process_data.py中,我们将清理数据,以便得到一个只显示我们感兴趣的内容的表格。

从加载数据开始,只保存用特定语言编写的存储库:

作者图片

接下来,我们将只保留我们感兴趣的存储库信息,包括:

  • 全名
  • HTML URL
  • 描述
  • 观星者也算

从字典中创建一个数据帧,然后删除重复的条目:

将所有东西放在一起:

使用 Streamlit 创建仪表板

现在到了有趣的部分。创建一个仪表板来查看存储库及其统计信息。

我们的应用程序代码的目录结构如下所示:

可视化存储库的统计数据

文件[Visualize.py](<http://Visualize.py>)将创建应用程序的主页,而目录pages下的文件创建子页面。

我们将使用 Streamlit 用 Python 创建一个简单的应用程序。让我们从编写显示数据及其统计数据的代码开始。具体来说,我们希望在第一页看到以下内容:

  • 按语言过滤的存储库表
  • 十大最受欢迎的存储库图表
  • 十大热门话题图表
  • 主题的单词云图表

Visualize.py的代码:

要查看控制面板,请键入:

cd app
streamlit run Visualize.py

进入 http://localhost:8501/ 你应该会看到下面的仪表盘!

作者图片

根据主题过滤存储库

我们得到了不同主题的存储库,但我们往往只关心特定的主题,如机器学习和深度学习。让我们创建一个页面,帮助用户根据他们的主题过滤存储库。

您应该会看到第二个页面,如下所示。在下面的 GIF 中,在应用到过滤器后,我只看到带有标签deep-learningsparkmysql的存储库。

作者图片

每天获取和处理数据的计划

如果您希望每天更新 GitHub 提要上的存储库,您可能会觉得每天运行脚本来获取和处理数据很懒。如果您可以安排每天自动运行您的脚本,这不是很好吗?

作者图片

让我们通过使用 Prefect 创建部署来调度我们的 Python 脚本。

使用子流程

因为我们想在运行流process_data之前运行流get_data,所以我们可以将它们放在文件development/get_and_process_data.py中另一个名为get_and_process_data的流下。

接下来,我们将编写一个脚本来部署我们的流。我们每天都使用IntervalSchedule来运行部署。

为了运行部署,我们将:

  • 启动一个完美的猎户座服务器
  • 配置存储
  • 创建工作队列
  • 运行代理
  • 创建部署

启动一个完美的猎户座服务器

要启动完美的 Orion 服务器,请运行:

prefect orion start

配置存储

存储保存您的任务结果和部署。稍后当您运行部署时,提督将从存储中检索您的流。

要创建存储,请键入:

prefect storage create

您将在终端上看到以下选项。

在这个项目中,我们将使用临时本地存储。

创建工作队列

每个工作队列还将部署组织到工作队列中以供执行。

要创建工作队列,请键入:

prefect work-queue create --tag dev dev-queue

作者图片

输出:

UUID('e0e4ee25-bcff-4abb-9697-b8c7534355b2')

--tag dev 告诉dev-queue工作队列只为包含dev标签的部署提供服务。

运行代理

每个代理确保执行特定工作队列中的部署

作者图片

要运行代理,请键入prefect agent start <ID of dev-queue>。由于dev-queue的 ID 是e0e4ee25-bcff-4abb-9697-b8c7534355b2,我们键入:

prefect agent start 'e0e4ee25-bcff-4abb-9697-b8c7534355b2'

创建部署

要从文件development.py创建部署,请键入:

prefect deployment create development.py

您应该会在 Deployments 选项卡下看到新的部署。

作者图片

然后点击右上角的运行:

作者图片

然后单击左侧菜单中的流式运行:

作者图片

你将会看到你的心流被安排好了!

作者图片

现在,提取和处理数据的脚本将每天运行。您的仪表板还显示了本地机器上的最新存储库。多酷啊。

下一步

在当前版本中,应用程序和提督代理运行在本地机器上,如果您关闭机器,它们将停止工作。如果我们关闭机器,应用程序和代理将停止运行。

为了防止这种情况发生,我们可以使用 AWS 或 GCP 这样的云服务来运行代理、存储数据库和服务仪表板。

作者图片

在下一篇文章中,我们将学习如何做到这一点。

我喜欢写一些基本的数据科学概念,并尝试不同的数据科学工具。你可以在 LinkedIn 和 Twitter 上与我联系。

如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:

使用 Streamlit 构建一个简单的网络图应用程序

原文:https://towardsdatascience.com/build-a-simple-network-graph-app-using-streamlit-e6d65efbae88

释放 Streamlit、NetworkX 和 Plotly 的力量,寻找乐趣

图片来自 PixabayGerd Altmann

网络图(也称为网络图)是一种特殊类型的可视化,它通过节点和边来显示一组实体之间的连接。网络图中的每个实体称为“节点”,连接一对节点的线称为“边”。一个实体可以是一个人、一个事件、一个运动队或任何其他东西。网络图是可视化和分析网络中实体之间关系的有效方法。

网络图可以是有向的,也可以是无向的。节点的大小和颜色以及边的宽度可用于显示网络实体的不同属性及其关系。下面是一个简单的无向网络图的例子,节点颜色代表网络中每个节点的连接数。

作者图片

在这篇文章中,我想向你展示如何使用StreamlitNetworkXPlotly构建一个简单的应用程序来生成交互式网络图(如上图所示)。该应用程序允许用户上传一个 CSV 文件,其中包含符合特定格式的网络数据,只需要两列:一列用于源节点,另一列用于目标节点。

用户将数据上传到应用程序后,应用程序将立即生成一个无向网络图,节点的颜色代表网络中每个节点的连接数。用户还可以为图形选择不同的布局选项,或者为节点选择不同的配色方案。这是一个应用程序的简短视频演示。

作者创建的 Youtube 视频

先决条件

如果您是 Streamlit 的新手,请阅读下面的文章,并按照说明安装 Streamlit 并学习其基础知识。

您还需要安装以下软件包:

pip install plotly
pip install networkx

太好了!现在您已经安装了所有需要的 python 包,让我们开始构建应用程序。首先,让我们导入所有必要的库,并向侧栏添加一些小部件,向主界面添加一个标题:

  1. 我们将添加一个标志(可选)
  2. 我们将添加一个可折叠的扩展器小部件(“关于应用”)
  3. 我们将添加一个文件上传程序,允许用户上传一个 CSV 文件到应用程序
  4. 我们将使用 CSS 样式在应用程序的主界面上添加一个应用程序标题

接下来,让我们使用NetworkXPlotly根据用户上传的数据创建网络图。在这里,我创建了一个样本网络数据集,显示了一组人在 LinkedIn 上的联系。CSV 文件只有两列——“源”列包含所有源节点,“目标”列包含所有目标节点。每行代表一对连接的节点(“边”)。这就是输入数据所需的全部内容。

示例输入数据(由作者提供)

一旦用户上传 CSV 文件到应用程序,我们将使用NetworkX创建一个网络图。我们还将添加三个输入小部件,允许用户选择图形的特定布局,指定他们喜欢的配色方案,并为图形添加标题。

作者图片

最后,让我们使用Plotly来可视化使用NetworkX创建的网络图。确保在if uploaded_file is not None:语句中添加正确缩进的代码。

作者图片

这就对了。我们用StreamlitNetworkXPlotly成功搭建了一个简单的交互式网络图 app!你当然可以对这个应用程序做更多的改进。例如,我们可以添加更多的小部件,允许用户指定节点的大小或边的宽度,以传达关于网络以及节点和边的属性的更多信息。在plotly部分需要更多的编码,但这肯定是可行的,我鼓励你自己尝试一下。感谢阅读,我希望你喜欢这个教程!

数据源:本教程中使用的样本数据集是作者出于演示目的而创建的。

参考:Plotly 文档中的Python 网络图

你可以通过这个推荐链接注册 Medium 会员(每月 5 美元)来获得我的作品和 Medium 的其他内容。通过这个链接注册,我将收到你的一部分会员费,不需要你额外付费。谢谢大家!

posted @ 2024-10-18 09:34  绝不原创的飞龙  阅读(464)  评论(0)    收藏  举报