TowardsDataScience-博客中文翻译-2022-五十七-
TowardsDataScience 博客中文翻译 2022(五十七)
为什么熊猫式界面对于分布式计算来说不是最佳的
深入了解熊猫界面的假设
这是我们最近的皮肯谈话的书面版本。

照片由 Jukan Tateisi 在 Unsplash 上拍摄
分布式计算的熊猫式框架
在过去的一年半时间里,我们与想要将 Pandas 代码转移到 Dask 或 Spark 以利用分布式计算资源的数据从业者进行了交谈。他们的工作负载很快变得过于计算密集型,或者他们的数据集不再适合 Pandas,Pandas 只能在一台机器上运行。
在我们的对话中,一个反复出现的主题是像考拉(更名为 PySpark Pandas)和 Modin 这样的工具,它们旨在使用相同的 Pandas 接口将工作负载引入 Dask、Ray 或 Spark,只需改变 import 语句(大部分)。
例如,PySpark Pandas 的替代产品可能是:
# import pandas as pd
import pyspark.pandas as pd
据推测,所有东西都应该在 Spark 上运行。已经有一些博客表明这并不完全正确(截至 2022 年 5 月)。这里和那里都有一些小问题,但我们在这里不是为了谈论微小的差异。这篇文章讲的是由于分布式计算的细微差别而一直存在的根本差异,而熊猫与分布式计算并不兼容。
类似熊猫的框架之所以流行,是因为很多数据科学家抗拒改变(我自己也经历过!).但是仅仅改变导入语句会使用户避免理解分布式系统中真正发生的事情,缺乏理解会导致无效的使用。
我们将看到,试图与 Pandas API 实现 1:1 对等将需要在性能和功能上做出妥协。
基准数据
我们创建了一个具有以下结构的数据帧。列a和b是字符串列。列c和d是数值。这个数据帧将有一百万行(但是我们也会在某些情况下改变它)。
我们将在 Pandas、Modin (on Ray)、PySpark Pandas 和 Dask 中创建这个数据帧。对于每个后端,我们将对不同案例的操作进行计时。这一点在讨论完第一个问题后应该会更加清晰。

按作者分类的图像-示例数据
问题 1: Pandas 假设数据在物理上是在一起的
最常用的熊猫方法之一是iloc。这依赖于数据的隐式全局排序。这就是为什么 Pandas 可以快速检索给定索引值集中的行。它知道在哪里访问它需要检索的行的内存。
以下面代码片段中的 5 种情况为例,我们将评估每种操作相对于情况 1 的速度。我们不进行跨框架的比较。我们希望看到每个框架的不同性能。下面的案例 3–5 是基于位置访问行和列。情况 5 特别是在数据帧的中间。我们将在熊猫、摩丁、PySpark 熊猫(也称为考拉)和 Dask 上运行这五个案例。
在下面的基准测试中,Pandas 在使用整数位置值访问数据时速度加快了。这是因为在单台机器上访问内存中的数据相对便宜。在各种情况下,Modin 在提供一致的性能方面做得很好,但是当访问数据帧的中间时(情况 5),速度降低了 2 倍。

按作者分类的图片—数据访问比较
PySpark Pandas(表中标为 Spark)和 Dask 给出了有趣的结果。Spark 在所有情况下都有明显的减速。得到头部是相对优化的,但是其他的都是性能较差的。事实上,获取数据帧的尾部或中部会导致 15 倍于获取头部的持续时间(情况 1)。
Dask 实际上不允许在行上使用iloc。为了让iloc表现得像熊猫一样,必须对性能做出妥协,以维持全球秩序。这是一个有意的设计决策,偏离了熊猫的语义以保持性能。
PySpark Pandas 以牺牲性能为代价,优先考虑维持熊猫的平价。与此同时,Dask 对防止不良行为更加敏感。对比这些框架向我们展示了设计理念的不同。这也是第一次表明统一界面并不意味着一致的性能特征。
问题 2:熊猫认为数据洗牌很便宜
在分布式环境中,数据存在于多台机器上。有时,需要跨机器重新安排数据,以便每个工人拥有属于一个逻辑组的所有数据。这种数据移动被称为“洗牌”,是分布式计算中不可避免但代价高昂的一部分。
取两个等价的运算。目标是为每个d值保留具有最高值c的行。注意 a groupby-max并不保存整行。案例 1 执行全局排序,然后删除重复项以保留最后一行。另一方面,情况 2 使用groupby-idxmax操作来保持最大行。然后较小的数据帧被合并回原始数据帧。该基准测试使用了 100k 行,而不是 100 万行。
对于熊猫来说,情况 2 实际上比情况 1 慢,如下表所示。所有的分布式计算框架在第二种情况下都要快得多,因为它们避免了全局排序。相反,groupby-idxmax是一个优化的操作,首先发生在每个工作机器上,连接将发生在一个更小的数据帧上。可以优化小数据帧和大数据帧之间的连接(例如,广播连接)。

作者提供的图片—随机比较
这是一个很常见的熊猫代码片段的例子,它不能很好地翻译成分布式设置。类似于问题 1 中的全局排序讨论,进行全局排序是一个非常昂贵的操作。
熊猫式框架的问题在于,用户最终会以相同的本地计算思维方式处理大数据问题。如果用户在迁移到分布式设置时不更改代码,很容易遇到耗时过长的次优操作。
问题 3:熊猫认为指数是有益的
熊猫思维中根深蒂固的核心概念之一是指数。如果用户有熊猫背景,他们会认为这个指数是有益的,值得设置或重置。让我们看看这如何转化为其他后端。
以下面的代码片段为例。我们筛选给定的组,然后计算这些记录的总和。案例 1 没有索引,案例 2 使用了索引。
具体来说,set_index没有包含在基准测试中。这是因为set_index有自己的开销。结果如下所示:

按作者分类的图像—索引行为比较
对于熊猫来说,当数据帧被a索引时,速度会加快。对于摩丁或者火花来说,并没有什么改善。Dask 有显著的改进。
同样,统一的界面并不意味着一致的性能特征。通常,某些操作无法满足用户期望。这个也没办法有好的直觉。我们已经知道,为了支持 Pandas API 的分布式版本,必须做出一些妥协,但是很难知道那些设计决策到底是什么。每一个类似熊猫的框架都需要在不同的方向进行特定的优化。
还要注意,对于上面提到的所有类似 Pandas 的框架,MultiIndex 并不完全受支持。
问题 4:急切与懒惰的评估(第一部分)
惰性评估是分布式计算框架的一个关键特性。当在数据帧上调用操作时,会构建一个计算图。只有在执行需要数据的操作时,操作才会发生。
在下面的代码片段中,案例 1 读取文件并计算所有列的最小值。案例 2 读取文件并计算两列的最小值。对于这个问题,我们将使用不同的数据集。这个新的有 40 列和 200 万行随机数。在这个一行表达式中有两三个步骤:加载文件,过滤列,然后得到最小值。
结果如下所示。因为熊猫和摩丁对事物的评价都是热切的,所以案例 2 只是比案例 1 略有缩减。这是因为最小值的计算量较少(两列而不是全部)。但是加速并不多,因为在过滤所需的列之前,先读取全部数据。
另一方面,PySpark Pandas 和 Dask 对这个操作有巨大的加速。这是因为他们知道最终只需要两列,所以他们只从 parquet 加载这两列(parquet 相对于 csv 文件的一个好处)。对于三个操作(load、filter、min),PySpark Pandas 和 Dask 能够通过最小化磁盘 I/O 来优化计算,这是由于它们的惰性。

作者图片—懒评对比 1
摩丁特别选择优化迭代工作负载的体验,并且还匹配熊猫的行为。另一方面,PySpark 熊猫选择了和 Spark 一样的懒评价。即使它们都是“分布式熊猫”的一种形式,它们也有非常不同的性能特征。
问题 4:急切与懒惰的评估(第二部分)
在这里,我们看到一个热切评价帮助用户的案例。但是当实践者不理解懒惰评估时,也很容易遇到重复工作。
请参见以下案例,案例 1 仅获取两列的最小值,而案例 2 获取最小值、最大值和平均值。
在下面的结果中,熊猫和摩丁似乎没有在案例 2 中发生任何重新计算。sub被读取后已经保存在内存中。这是意料之中的,因为我们在上一个基准中看到熊猫和摩丁热切地评价。另一方面,PySpark Pandas 和 Dask 表明sub被计算了多次,因为我们没有明确地持久化 sub。

作者图片—懒评对比 2
在第 4 期中,我们看到了懒惰评估的两面性。我们看到了一个场景,其中它导致了急剧的加速,在最后一个场景中,当使用不当时,它会导致速度变慢。这并不意味着急切或懒惰的评估更好,更重要的是当我们处理大数据以获得最佳结果时,我们需要注意框架正在做什么。
这是一个常见的陷阱,因为 Pandas 没有让用户注意到分布式计算复杂性的语法。从熊猫来的人不知道persist行动。
结论
Pandas 非常适合本地计算(除了有太多的方法来做一些操作)。但是我们需要认识到该接口的固有局限性,并且理解它不是为在几台机器上扩展而构建的。 Pandas 不是为分布式计算设计的接口。
如果你想尝试另一个不像熊猫的语义层,赋格采取了不同的方法。Fugue 是一个用于分布式计算的开源抽象层。虽然它可以将 Pandas 代码引入 Spark 和 Dask,但它有意与 Pandas 接口解耦,以避免面临 Pandas 类框架所面临的妥协。
为什么 PCA 看起来是三角形的
原文:https://towardsdatascience.com/why-pca-looks-triangular-a642daac721a
生命科学的数理统计和机器学习
通常在计算生物学中

对 1000 个基因组遗传变异的主成分分析(左),对微生物丰度 HMP 数据的主成分分析(右)。作者图片
这是我的专栏 生命科学的数理统计和机器学习 中的第二十四篇文章,在这里我用通俗易懂的语言讨论了计算生物学和生命科学中一些有趣的分析方法。主成分分析广泛应用于计算生物学,特别是群体遗传学、微生物生态学、单细胞分析,用于建立样本与细胞之间的关联性。PCA 经常呈现出一种奇特的三角形或拱形或马蹄形形状,这可以用数据中一些有趣的潜在模式来解释。在这篇文章中,我将讨论哪些数据可以导致三角 PCA 结构,以及为什么理解这种效应对于仔细解释 PCA 图很重要。
为什么 PCA 而不是 tSNE 用于群体遗传学?
从遗传变异和微生物组数据(上图)到单细胞基因表达分析(下图),显著的三角形 PCA 模式在生命科学中随处可见。

PCA 对来自癌症相关成纤维细胞(左)和先天淋巴细胞(右)的基因表达的影响
然而,正如人们所看到的,PCA 似乎没有提供足够的分辨率来辨别单细胞基因表达分析中的不同细胞类型,而 tSNE 或 UMAP 通常更能提供这方面的信息。相比之下,群体遗传学和基因组学研究人员很少(或几乎从不)使用 tSNE 和 UMAP,样本之间的遗传相似性通常通过 PCA 或系统发育树来建立。为什么这两个领域在选择降维技术上有如此大的差异?一种解释似乎是tSNE / UMAP 提供样本聚类之间可靠相似性的内在困难,这使得使用 tSNE/UMAP 进行群体遗传学分析非常成问题,其中一个非常重要的目标是建立不同群体之间的遗传距离。相比之下,主成分分析可以提供更准确的样本组之间的关系,但对于发现样本之间的异质性(即样本总体)可能不是那么方便。另一方面,在计算 PCA 之前,从人类学和考古学研究中,群体通常在群体遗传学中是已知的,因此这里的目标不是发现它们,而是理解群体之间的相互关系。
遗传距离和地理距离之间的联系
通过 PCA 可视化精确的遗传相似性对于群体遗传学如此重要的原因之一是,人们很早就认识到世界人口的遗传距离和地理距离之间存在某种相关性。Menozzi、Piazza 和 Cavalli-Sforza 在 1978 年注意到了这一趋势,并建议将新石器时代的农业迁移作为这一效应的可能解释。

Menozzi,Piazza 和 Cavalli-Sforza,科学,第 201 卷,第 786 页,1978 年
后来,随着下一代测序技术的兴起,当更全面的采样变得可用时,约翰·十一月布雷和合著者提出,欧洲的地理地图自然出现,作为欧洲人遗传变异的有效二维 PCA 总结。

J. Novembre 等人,自然,第 456 卷,第 98 页,2008 年
事实上,从上图可以看出,根据欧洲人的基因变异计算出的五氯苯甲醚图可以很好地反映欧洲地图。此外,有人提出,PCA 成分可用于准确预测欧洲人的血统(即原产地)的。也请注意上图中 PCA 图壮观的三角形形状。
随机数据的主成分分析
我们看到 PCA 是建立样本组之间相似性的重要工具,在群体遗传学的情况下,PCA 的三角形形状在关联遗传和地理距离时可能是有意义的。然而,事实证明,生命科学中的某些数据类型能够产生三角形或楔形的 PCA 图,这是因为数据的非高斯性质,即如果在计算 PCA 之前没有对数据进行适当的转换。为了证明这一点,让我们使用不同的概率分布,如泊松分布、二项式分布等,生成样本之间没有结构或相关性的随机数据矩阵。这在生命科学中是常见的,并在模拟数据上计算 PCA。

作者图片
这里,人们可以观察到,通常没有任何相关结构的分布数据应该给出围绕 PCA 图原点的数据点的近似球形分布。遵循均匀分布的随机数据产生了一个有趣的方形外观的主成分分析,就好像在两个正交的方向上拉动前面图上的高斯圆。非常值得注意的是,泊松、对数正态和负二项分布数据往往在 PCA 图中形成三角形或楔形结构。甚至,二项式分布的数据有时会给出一个看起来像楔形或三角形的 PCA,见上图。事实是,模拟的统计观察应该是独立于的,也就是说,随机数据矩阵中没有相关性,并且样本不应该形成聚类。然而,稍微发挥一下想象力,人们就可以将三角形/楔形 PCA 形状的顶点中的点误解为属于不同细胞群或人类群体的。****
这些三角形或楔形结构可以部分地由计算生物学中许多类型数据的非高斯性来解释,这似乎违反了 PCA 的一个非常基本的假设。也就是说,PCA 可以被看作是用两个矩阵(加载和得分)的乘积来逼近一个数据集,数据和逼近之间的平方误差的以下隐式最小化。平方误差的最小化意味着假设近似误差或残差的正态/高斯分布,如果数据矩阵 X 不遵循高斯分布,这可能难以满足。有人会问,在非正态分布的数据上使用 PCA 是否可行?实际上,如果在计算 PCA 之前已经应用了一些数据** 变换,PCA 通常在非正态分布的数据上运行。**

作者图片
标准化和对数变换通常在 PCA 之前应用于许多生命科学数据,即屏蔽** 数据的非高斯性并满足 PCA 的正态性假设。在实践中,这通常会导致五氯苯甲醚图呈不太明显的三角形。让我们将标准化和对数变换应用于先前模拟的数据矩阵,并观察差异。**

作者图片
我们可以观察到,一旦应用了对数变换和标准化,PCA 图中数据点的分布变得越来越呈球形。这直观地意味着在高维空间中没有数据的优选取向的方向,即所有方向都具有同等的信息性/重要性。****
上面的例子处理的是纯随机数据,样本之间没有任何相关结构。然而,通常在基因表达或微生物丰度数据中存在大量相关性,其中样品或细胞可能不是独立的并形成集群。在这种情况下,可以获得有意义的三角形 PCA 图,其中三角形的顶点通常表示不同的样本总体。下面讨论一个有趣的例子,叫做拱门或马蹄效应。
具有块结构的数据,以及拱形效应
就我的经验而言,拱形效应或马蹄形效应在遗传学或单细胞分析中讨论得不多,但在微生物生态学中却很有名。人们需要检查例如一些旧的讨论来获得关于效果的更多信息。还有这篇优秀论文提供了关于马蹄效应起源的见解,我强烈推荐阅读。
事实证明,当统计观察值之间存在强相关性时,即当样品/细胞属于不同的簇/群时,可以得到三角形的 PCA。在这种情况下,数据矩阵具有特殊的形式:人们可以直观地观察到对于一些样本和一些变量具有大值的元素的块,而低值零在矩阵中的其他任何地方。让我们用两个这样的块模拟一个数据矩阵,并将矩阵的元素可视化为热图。

作者图片
上面模拟的数据矩阵包含 200 个样本(S0,…,S199)和 200 个变量(V0,…,V199)。热图中较暗的区域对应于矩阵的零个元素,而较亮的颜色对应于矩阵元素的较大值。因此,数据矩阵由** 两个空白块或簇或总体组成,其中样本 S0、…、S99 看起来完全与样本 S100、…、S199 解耦,因为它们没有公共变量,即变量 V0、…、V99 仅对于样本 S0、…、S99 具有非零值,而变量 V100、…、V199 仅对于样本 S100、…、S199 具有非零值这是一个极端的解耦,对于现实世界的项目来说,这种情况很少发生,但是这个模拟将帮助我们建立我们的 直觉关于是什么导致了 PCA 图中的三角形结构。在微生物生态学中,研究人员通常谈论导致这种块状数据(pH、温度等)的环境** 梯度。).通过对两个块中变量的元素进行平均,我们确实可以查看数据矩阵,就好像两个不相互作用的物种存在于不共享环境的数据中,即无法找到两个物种都存在的样本。****

作者图片
因此,人们可以得出这样的结论:如果样品碰巧是按照强** 环境梯度排序的,这可能会导致某种物种在一组样品中大量存在,而在其他组中完全不存在的情况。例如,土壤微生物组可能与海洋微生物组非常不同。在这种情况下,计算来自 极端不同环境的样本之间的正确**相似性通常变得越来越成问题。然而,在这里,为了演示和建立我们的直觉,让我们在块数据矩阵上计算 PCA,并可视化 PCA 图和显示由每个主成分解释的方差的图。****

图片作者。添加红色虚线(右图)是为了突出潜在的三角形
这里我们可以观察到数据中几乎所有的方差都被第一主成分捕获。这并不奇怪,因为我们知道在样本中有两个集群的非常强的主导分组效应(梯度)。因此,人们可以将领先 PC 的数量 k(解释了数据中的大部分变化)视为一种快速方法,以将数据中的聚类数量估计为 k+1 个聚类。
接下来,我们或许可以观察到,使用两块矩阵,著名的 PCA 三角形开始变得更加明显,不过,通过一些想象,我用红色虚线突出显示了它。我们可以在 PCA 图中清楚地看到两个集群。然而,如果我们增加数据矩阵中的块数,会发生什么呢?让我们模拟一个具有三个块的数据矩阵,并可视化每个 PC 解释的方差以及 PCA 和 tSNE 图,以进行比较。

作者图片
正如所料,我们看到两个 PC解释了数据中的大部分方差,并且三组样本在 PCA 和 tSNE 图中都清晰可见。然而,当集群数量增加更多时,即当我们拥有超过 2–3 个集群时,情况会发生巨大变化。现在让我们用 10 个不同的样本群体来模拟一个块矩阵。

作者图片
值得注意的是,我们似乎不能区分 2D PCA 图中的** 10 个集群,事实上一些种群似乎聚集在一起,以至于从 PCA 图的视觉检查中检测不到超过 4 个清晰的集群。这意味着,即使我们提供了数据中样本总体的准确数量和,任何聚类算法都很难根据两个主成分对样本进行分组。因此,需要考虑两个以上的主成分来获得样本的正确聚类,然而,这将越来越难以可视化,甚至更难在降维和聚类之间达成一致。引人注目的是, tSNE 似乎只用两个潜在的 tSNE 变量就能轻松解决所有 10 个集群 。即使总的来说这不是一个好主意,但对于这种特殊的情况,在 2D tSNE 图上进行聚类将是成功的,也就是说,人们可以很容易地将所有样本归入它们的 10 个群体。**
具有模糊块和马蹄铁效应的数据
上一节所示的极端** 块结构在现实世界数据中很少见到。通常,样本聚类不太透明,人们可能会认为具有更多模糊块 结构的数据矩阵对于生命科学项目来说非常典型。给先前的块矩阵增加一些“模糊性”的一种方法是创建一个带数据矩阵,其中只有对角线周围的元素是非零的。这里,我们仍然怀疑一些样本块,但是它们不像上一节中的那样透明。让我们用两个“模糊”模块模拟并可视化一个典型的波段数据矩阵。**

图片作者。添加绿色和蓝色虚线是为了突出显示模糊的块
这种带矩阵可能会在计算生物学项目中出现,当样本有很强的空间或时间排序时,通常他们说有一个梯度影响样本中的丰度。这种梯度的例子可以是 pH 、温度、空间坐标、采样的时间周期,也可以有诸如批次(实验室、技术、国家)的技术梯度。使用上一节中关于在特定环境中存在但在其他环境中不存在的物种的直觉,我们可以再次对以下变量组中的矩阵元素进行平均:V0、…、V99 ( “物种 1”)和 V100、…、V199 ( “物种 2”),并观察到从一种环境改变到另一种环境时物种 1 和 2 丰度的更为渐进的,即不太突然的变化在下文中,我们可以观察到,例如,物种 1 也可以存在于上一节中仅由物种 2 占据的环境中,但是丰度较低。

作者图片
一个 PCA 用于波段矩阵怎么样?下面,我们将计算由主成分解释的方差以及带数据矩阵的 PCA 图,该带数据矩阵具有两个可能的模糊聚类。

作者图片
这里我们观察 PCA 图中著名的拱门或马蹄铁。数据点分布的形状仍然有点像三角形,拱形或马蹄形的两个“翅膀”对应于两个不同的簇。另外,请注意由主成分图解释的方差的逐渐变化。对于干净的块结构矩阵,有几个同样重要的** 主导 PC,而现在有一个主导 PC ,其通常对应于主导带数据矩阵的值的强梯度(例如时间)。通常,他们说马蹄形 PCA 图中的样本按时间从早到晚排序、即时间排序,或从地理西到地理东(空间排序)。在单细胞分析中,当监测干细胞分化时,换句话说,当细胞沿着所谓的 细胞命运轨迹 排序时,这种有序的细胞“线”是典型的。通常, 扩散图 (另一种降维技术)在解析细胞轨迹方面表现出色,在单细胞生物学中与细胞发育一起工作时,广泛使用代替 PCA** 。****
PCA 图中奇特的拱形或马蹄形形状的形成与数学上已知的 李沙育曲线 有关。这也在群体遗传学文献中被注意到,例如见 2008 年 11 月 Stephens 在《自然遗传学》中的文章。

从具有马蹄形效应的 PCA 中可见的利萨如曲线。遗传学 2008
当样本已经沿着环境梯度排序时,如果我们将每个主成分绘制为样本数量的函数,则可以看到那些曲线。让我们检查这些曲线从模拟带矩阵上计算的 PCA 中是否可见:

的确,非常有趣和奇特的形状!请注意,PC1 似乎与环境梯度密切相关。现在,如果我们考虑三个“模糊”样本总体,会发生什么?

作者图片
我们可以看到马蹄铁的两端开始向质心弯曲。我们可以观察到的另一件事是深绿色和品红色样本群体看起来彼此非常遥远,而红色样本群体是共同的邻居。如果我们查看波段数据矩阵并认识到前 100 个(深绿)和后 100 个(洋红)样本几乎不共享任何变量,而红色样本(波段矩阵中间的 100 个样本)与洋红和深绿样本群体共享大量 V 变量,则可以直观地理解这种效应。现在,我们将更多地增加“模糊”块的数量,看看对于 10 个样本群体的 PCA 和 tSNE 图会发生什么。

作者图片
这里一个明显的观察结果是,PCA 图中马蹄铁的两端现在向马蹄铁的质心弯曲了很多。因此,这里我们有一个反直觉的效果:马蹄形两端 的金色和深绿色样本群看起来比马蹄形中间的青色和蓝色样本群彼此更接近,并且如果我们增加数据矩阵中模糊块的数量,它们将彼此更加接近。现在,假设有一个时间梯度沿着马蹄形起作用,即样本从早时间到晚时间排序,深绿色样本最年轻,金色样本最老,而青色和蓝色样本来自一些中间时间点。这种马蹄铁两端的相互靠近意味着最小的和最大的样本与来自中间时间点的样本相比变得非常相似,这很奇怪。人们会期望相对于来自中间时间点的样本,最年轻和最老的样本彼此更加不相似。********
在比较世界人口的遗传变异时,对五氯苯甲醚图中马蹄形的解释变得更加棘手。如果存在样本的空间梯度 【地理分离】****其中遗传变异被测量为条带矩阵中的 V-变量,那么从 PCA 图看起来,来自远东的种群与来自远西的种群在遗传上比它们的共同邻居更相似。因此,拱形和马蹄形效应通常被认为是微生物生态学中的一种假象,最近在群体遗传学中也是如此,在群体遗传学中发展了特殊的方法来最小化这些效应,例如参见 Francois & Jay 在《自然通讯 2020》中的因子分析方法。

消除五氯苯甲醚马蹄形效应的因子分析,Francois & Jay,Nat。通信 2020
摘要
在本文中,我们了解到,在生命科学的各个领域,如微生物生态学、群体遗传学和单细胞生物学中,经常可以看到特殊的三角形 PCA 结构。我们讨论过,假设数据的概率分布不远离正态分布,这对于 PCA 是必不可少的,PCA 图中数据点的三角形分布通常意味着数据中存在相关结构。特别是,当数据矩阵由不同的或模糊的样本块组成时,它会导致一个拱形(T20)或马蹄形效应,有时会导致对 PCA 图中聚类的错误生物学解释。尽管如此,PCA 似乎仍然比 tSNE 和 UMAP 更准确,更适用于群体遗传学,其中样本(群体)聚类之间的遗传相关性至关重要。
像往常一样,让我在下面的评论中知道生命科学和计算生物学的哪些分析方法对你来说似乎特别神秘,我将在这个专栏中尝试解决它们。在我的 github 上查看帖子使用的文件。在MediumNikolay Oskolkov关注我,在Twitter@ NikolayOskolkov 关注我,并通过 Linkedin 连接。在下一篇文章中,我们将讨论如何在 UMAP 空间聚集,敬请关注。
为什么 Python 比 R 更适合数据科学
原文:https://towardsdatascience.com/why-python-is-better-than-r-for-data-science-ed2c9f5242b8
选择 Python 作为你的第一语言,并一路学习 R

在 Unsplash 上由 Hitesh Choudhary 拍摄的照片
介绍
在我的本科数学学位期间,我几乎只能使用 R。我在读数据科学硕士期间也不得不使用它。
然而,在我读硕士期间,我发现自己使用 Python 比使用 R 更频繁;我发现在数据科学内外使用起来更直观,学习起来也更愉快。
在干草叉出现之前,请记住这两种工具仍然是数据科学中常用的。然而,希望从事数据科学职业的人可能会从使用 Python 中受益更多,原因我将在下面讨论。
如果你想看些别的,你可以看看我下面的视频。
你可以看我在 YouTube 上关于这个话题的视频,而不是看这篇文章。
通用语言

Python 是从头开始构建的通用编程语言。这意味着你可以用它来做各种各样的任务。例如,您可以将 Python 用于:
- 数据科学。
- 网页和游戏开发。
- 网络应用。
- 机器人技术。
相反,R 主要用于分析和统计。它不是一种通用语言。我见过的几乎每一个做学术研究的统计学家都会坚持使用 R,原因是:在它被设计用来做的事情上,它非常高效。
但是随着我进一步探索数据科学,我发现自己想要使用一些灵活的东西。Python 为我的数据科学职业生涯提供了更多的机会,因为它已经被用于数据科学内外的许多事情。
从爱好的角度来看,Python 也让我更容易探索其他领域,如游戏或应用程序开发;虽然这些与数据科学无关,但它们给了我用 R 无法做到的方式练习编码的动力。我感觉数据科学初学者经常忽略这一点。
可读性

阿曼达·琼斯在 Unsplash 上拍摄的照片
Python 非常容易阅读,因为它是一种读起来像演讲的语言。所以毫无疑问,它是目前最受儿童欢迎的编程语言之一。
就我个人而言,我来自非编码背景,R 是我的第一门编程语言。它的语法对我来说非常难学。几年后,当我转向 Python 时,它就像一股新鲜空气。
Python 是一种高级语言。这意味着它抽象了更多的细节,更易于阅读。相反,像 R 和 C++这样的语言被认为是低级语言。因此,用 R 编写一个过程比用 Python 编写一个过程需要更多的代码。
最后,还有一件事是我在开始申请数据科学工作之前没有真正考虑过的:可读性可能是编写面试代码的一个优势。
易于编写的代码意味着你可以更快地解决问题。易读也意味着面试官更容易理解你的代码。即使我的主要编程语言是另一种语言,这也是我在编码面试中坚持使用 Python 的一个重要原因。
流行

Anthony DELANOIX 在 Unsplash 上拍摄的照片
大多数人已经知道 Python 是世界上最流行的语言之一。正如我们已经讨论过的,这在很大程度上归功于它的灵活性和可读性。
这种流行导致了一个非常大的 Python 社区。因此,如果你有问题,你更有可能找到可以帮助你的人。
如果您对数据科学的更多技术方面感兴趣,这将变得非常方便。花更少的时间解决问题意味着花更多的时间推动业务发展。由于 Python 的灵活性,你可以把你学到的东西转移到其他学科。
然而,来自 R 的 Python 似乎缺少一个非常专业的统计学家和统计研究人员社区。
我记得在 r 中做更复杂的统计时感觉更好。我发现更容易找到更具体的统计帮助。对我帮助最大的也是许多已经有丰富 R 经验的统计学教授。他们非常了解统计函数是如何工作的,这一点我发现在 Python 社区中并不常见。
因此,如果你的目标是成为一名专业的统计学家或统计研究员,那么你就很适合 R 社区。不过对于其他东西,我会建议使用 Python,因为它在大多数其他方面都有更大的社区。
深度学习

托马斯·福斯特在 Unsplash 拍摄的照片
很多关键的深度学习项目都是先用 Python 做的。这意味着如果你觉得深度学习有趣,Python 可能是最有意义的。
现在,R 肯定也用于深度学习。然而,像 Tensorflow、Keras 和 PyTorch 这样的库是在它们的 R 版本出现之前首先用 Python 编写或表达的。
此外,因为 Python 是一种通用语言,所以更容易将基于 Python 的模型部署到常用且受良好支持的 Python 框架中。大多数公司不太可能已经有 R 框架,所以如果你的角色涉及模型部署,R 将会限制你的机会。
结论
对于很多数据科学初学者来说,选择一种编程语言就像选择一种真正的语言一样。它变成了你的母语,以后要换成另一种语言就变得更难了。
在学术界使用过 R 之后,坚持使用 R 对于学术研究者来说很有意义。也就是说,对于大多数普通的数据科学从业者来说,使用 Python 的其他好处可能超过 r。
所以记住这一点,选择 Python 作为你的第一语言。您可以在统计和探索性数据分析的过程中学习一些 R。在我看来,这将更有利于您的数据科学之旅,也是我希望多年前就能做到的。实际上,我已经在上有一个视频,关于我如何为数据科学再次学习 Python。
一如既往,如果你喜欢这篇文章,你可以看看我在 YouTube 上的其他视频。如果你想通过电子邮件了解我在做什么,你可以考虑注册我的简讯!
原载于 2022 年 1 月 30 日 https://leonlok.co.uk**。
为什么强化学习不需要贝尔曼方程
原文:https://towardsdatascience.com/why-reinforcement-learning-doesnt-need-bellman-s-equation-c9c2e51a0b7
强化学习中著名的贝尔曼方程和 MDP 公式的再评价

理查德·贝尔曼在圣莫尼卡的兰德公司工作时,创立了动态编程和他著名的递归方程。照片由 Sung Shin 在 Unsplash 上拍摄
在学术界,将强化学习(RL)算法与马尔可夫决策过程(MDP)公式和著名的贝尔曼方程联系起来经常是样板文件。乍一看,这是有意义的,因为我们通常旨在为 MDP 找到近似最优的策略。然而,在许多方面,RL 已经偏离了动态编程的起源如此之远,以至于人们可能会想,在我们的问题定义中,我们是否真的需要贝尔曼方程。
这篇文章并不是对贝尔曼方程本身的批评。正如我们将很快看到的,这是一个非常优雅的公式,在一行数学中捕捉到甚至最混乱的问题。考虑到许多现实世界的组合问题的问题状态比宇宙中的原子还多,这是一个相当大的壮举。
当你理解了顺序决策问题的复杂性和重要性,你可能会像我一样欣赏贝尔曼的贡献。然而,这并不意味着我们需要在 RL 中使用它们。
什么是动态编程?

动态编程起源于 20 世纪 50 年代。当时,理查德·贝尔曼在圣莫尼卡的兰德公司工作。就在那里,坐在一棵棕榈树下——或者我是这么想的——理查德·贝尔曼发明了他著名的方程式。
虽然我不会在这里深入解释动态编程,但关键的概念是,跨越一个时间范围的大问题可以分解成一系列嵌套的小问题。这些较小的问题可以递归地解决,产生一个最优的决策策略,为每个问题状态提供最佳的行动。
要部署动态规划,我们必须能够将问题定义为 MDP,包括(I)状态空间、(ii)动作空间、(iii)转移函数和(iv)奖励函数(可能还有(v)贴现率)的形式化。决策必须只依赖于当前状态——所谓的马尔可夫(或无记忆)性质。如果条件满足,我们可以(理论上)使用动态规划来解决问题。
贝尔曼认识到,对于每个状态,可以定义一个价值函数,捕捉一个行为的直接回报/成本和(贴现的)下游价值。例如,我们可能决定每天储备多少产品,预测概率销售情况。
考虑一个三态问题。在每个状态下,我们可以采取一个动作a∈A。然后,以概率p_s,s’,a,我们移动到另一个具有相关价值函数V(s’)的状态。我们可以将每个状态的值函数定义为一个方程组:

三态问题的价值函数系统[图片由作者提供]
最大化每个价值函数优化了策略。简而言之,如果我们知道最优价值函数,我们总是可以选择最大化预期累积回报的行动。简单地说,这就是贝尔曼的解决方案。
价值函数方法的美妙之处在于,我们不需要知道任何关于实际问题结构或决策规则的东西。知道最优值函数就等于知道最优策略。
寻找最优值函数的经典动态规划求解方法有值迭代和策略迭代。您可以在以下文章中详细了解它们:
贝尔曼方程和维数灾难
刚才讨论的三态问题非常容易处理,但是如果有一百万个状态,方程组就会变得一团糟。幸运的是,我们不需要手写出所有的方程。事实上,整个系统可以被压缩成一行至高无上的数学优雅:

贝尔曼方程。由于价值函数之间的相互引用,系统是递归的。可以使用诸如值迭代之类的动态编程技术来找到最佳值函数。
想想就觉得很刺眼。一个状态s可以是一个包含元素的巨大向量,甚至可能包含随机知识。动作a可能受到许多约束,并且只能通过求解复杂的数学程序来识别。看似简单的概率函数P可能隐藏着一个怪诞的随机系统,导致许多可能的结果状态s’,它们都需要明确的评估。随机环境中的顺序优化通常是非常非常混乱的。然而,贝尔曼设法将所有这些动态捕捉到一个单一的,易于理解的线。这是一件艺术品,真的。
不幸的是,模拟一个问题的能力并不意味着我们能够真正解决这个问题。还记得那个三态方程组吗?事实上,这个问题不会只包含三种状态,而是更多。假设我们有一百万个状态,每个状态有一百万个可能的动作,以及一百万个状态(结果)我们可以在之后到达。这需要考虑 10 种⁸可能性,大致相当于地球上沙粒的数量。那些熟悉组合优化的人知道解决如此大规模的问题有多容易,甚至更难。
理查德·贝尔曼本人非常清楚他的解决方法在计算上的局限性,他创造了术语‘维数灾难’来强调他的方法缺乏可扩展性。其实可能是三咒 : 状态空间,动作空间,结果空间。
对于许多问题,无论我们有多少计算能力,我们都无法将 MDP 分解为最优。这就是强化学习的用武之地。
价值函数逼近
如果我们希望解决贝尔曼方程,我们必须找到每个状态的价值函数s∈S。对于每个单独的价值函数,我们应该评估与每个潜在结果s’∈S’相关联的价值函数(结果空间S’很可能等同于完整的状态空间S)。对于大的状态空间,这根本行不通。
典型的强化学习方法是(I)重复采样随机状态转换(而不是穷尽地评估每个可能的结果),以及(ii)用一组特征代替完整的状态描述(避免需要为每个单独的状态定义值函数)。因此,我们的目标是找到一个有希望接近最优的政策。但是,有多种方法可以实现这一点。
为了接近动态规划方法,我们可以求助于价值函数逼近。通过这种方法,我们明确地试图通过一个近似函数对V(s)建模。例子有 Q-tables ( SARSA,Q-learning ),基于特征的线性函数逼近,或者 critic networks 。请注意,贝尔曼方程——当然是以近似形式——用于表达决策政策:

价值函数近似的策略定义。对于这个解决方案类,政策制定和贝尔曼方程之间有一个可见的联系,尽管递归方面已经消失了。

线性值函数近似值的示例。这里,我们用特征θ_f(s,a)和相应的权重ϕ_f.的线性组合来代替“真实”值函数
你可能会说我们近似贝尔曼方程,这种类比并不太牵强。
尽管如此,这个类比并不适用于强化学习的整个领域,也不适用于人类的整体决策。如果你接近一个十字路口,你可能会想到‘左’‘右’‘直’而不是去思考更远的路的下游值。许多经理完全能够在日常基础上做出连续决策,甚至没有听说过贝尔曼方程。无数的 RL 算法没有尝试明确地对价值函数建模。
简而言之,价值函数不是制定决策政策的必需品。
目标函数
我们正在慢慢了解这个故事的关键(是的,终于)。如果有这么多不需要定义价值函数的解决方案,为什么我们一直把它们包含在我们的问题公式中?
似乎一个普遍的误解是这个惯例的根源。简而言之:贝尔曼方程是而不是一个目标函数。这是一个最优条件。如果我们找到了最优值函数,我们就找到了最优策略。然而,和 MDP 的定义无论如何都不需要值函数。
混淆的根源很容易看出:暗示目标的最大化函数,动态规划基础,最优值函数的最优政策保证。但是,我再重复一遍:贝尔曼方程只是一个最优性条件。可以使用动态规划来求解该问题,以找到 MDP 的最优策略。不多不少。
如果目标不是贝尔曼方程,那是什么?
答案在奖励函数(这是任何 MDP 的强制组件)。鉴于价值功能是一个有点抽象和人为的概念,回报功能为我们采取的每一个行动定义了一个非常实际的回报(或成本)。当我们在一个(可能是无限的)时间范围内进行优化时,将我们的目标建立在累积回报的基础上是有意义的。
对于有限的决策范围,我们可以简单地将奖励的总和作为我们的目标函数(例如,最大化下周的销售额)。对于无限的地平线问题,我们可以采用贴现报酬序列或者简单的平均报酬(理查德·萨顿建议后者)。请注意,这些是非常自然的目标,符合人类通常的决策方式。

有限视野目标函数的例子。在这种情况下,我们只是随着时间的推移最大化总报酬。注意我们不需要价值函数。
其他强化学习课程
随着贝尔曼方程的消失,常见的 RL 方法如策略梯度算法更有意义。事实上,在沃伦·鲍威尔提出的四类政策中,只有一类(价值函数逼近)与贝尔曼方程密切相关。在如下概述的备选策略类中,我们看不到动态编程的痕迹:

策略函数近似(PFA)提供了基于状态返回决策的直接表达式。

成本函数逼近(CFA)增加了奖励函数,以纳入下游效应,而没有为此目的公开定义价值函数。
你可以看到强化学习当然不需要求解贝尔曼方程,甚至不需要近似求解。
[## 强化学习的四个策略类别
towardsdatascience.com](/the-four-policy-classes-of-reinforcement-learning-38185daa6c8a)
结束语
是时候完成这个循环了。强化学习是解决具有计算挑战性的顺序决策问题的框架。由于它们可能变得相当复杂,我们需要精确和明确的问题定义来与利益相关者交流并找到解决方案。出于这个目的,使用 MDP 符号惯例是非常合理的。
尽管相当一部分 RL 算法试图逼近贝尔曼方程中假设的值函数,但许多解决方案根本不遵循动态编程范式。由于贝尔曼方程是一个最优条件,而不是一个目标函数,包含它在最好的情况下是多余的,在最坏的情况下是误导的。如果有的话,假设我们遵循基于价值的方法,RL 决策政策可能是贝尔曼方程的近似。
一个 MDP 只需要四个成分:(I)状态空间,(ii)动作空间,(iii)转移函数和(iv)奖励函数。贝尔曼方程或价值函数无处可寻。RL 需要一个目标函数来处理,但这只是一个简单的奖励的总和或平均序列。如果我们不逼近价值函数,贝尔曼方程就没有作用。
动态规划和相应的递归值函数系统是一块数学上的辉煌。贝尔曼的工作是顺序决策的一个突破,即使在今天,它仍然保留了很大的理论和实践影响。
和任何东西一样,只是有一个使用它的时间和地点。
外卖食品
- 贝尔曼方程是求解 MDPs 的一个最优性条件,而不是目标函数。潜在的概念是,拥有最优价值函数等同于拥有最优策略。价值函数系统可以使用动态规划来求解。
- 典型的目标函数只是将累积奖励最大化,例如,采用贴现奖励流或一段时间内的平均值。这些只需要奖励函数,不需要值函数。
- 许多强化学习解决方案与动态编程没有直接联系。贝尔曼方程仅明确用于四个策略类别中的一个,即价值函数逼近。
- 贝尔曼所宣扬的价值函数并不一定反映人类如何做出决策。最大化(折扣)累积或平均奖励是一种更自然的沟通决策的方式。
进一步阅读
贝尔曼河(1961 年)。自适应控制过程:指导游览。普林斯顿大学出版社。
鲍威尔,W.B. (2019)。随机优化的统一框架。欧洲运筹学杂志275.3(2019):795–821。
萨顿和巴尔托公司(2018 年)。强化学习:简介。麻省理工出版社。
鲍威尔和梅塞尔(2015 年)。能源中的随机优化教程——第二部分:能量存储图解。 IEEE 电力系统汇刊31.2(2015):1468–1475。
纳伊克,a .,沙里夫,r .,Yasui,n .,姚,h .和萨顿,R. (2019)。折扣强化学习不是一个优化问题。【https://arxiv.org/pdf/1910.02140.pdf
维基百科(2022)。贝尔曼方程。https://en.wikipedia.org/wiki/Bellman_equation
维基百科(2022)。动态编程。https://en.wikipedia.org/wiki/Dynamic_programming
维基百科(2022)。马尔可夫决策过程。https://en.wikipedia.org/wiki/Markov_decision_process
维基百科(2022)。理查德·贝尔曼。https://en.wikipedia.org/wiki/Richard_E._Bellman
为什么 SHAP 价值观可能不完美
原文:https://towardsdatascience.com/why-shap-values-might-not-be-perfect-cbc8056056be
SHAP 价值观弱点的两个例子和可能的解决方案概述
SHAP 值似乎消除了机器学习模型的复杂性和解释难度之间的权衡,鼓励研究人员和数据科学家设计算法,而不用担心如何理解任何黑盒给出的预测。但是 SHAP 能解释所有的财产吗?
在这篇文章中,我们将通过一些例子来讨论 SHAP 价值观的一个重要弱点。可能的解决方案概述也将很快提交。
SHAP 价值观及其局限性
SHAP 值基于合作博弈理论,通过在特征之间公平分配“支出”来量化导致预测的特征之间的交互。
然而,Frye 等人[1]认为,SHAP 值有一个明显的局限性,因为它们忽略了数据中的所有因果结构。更准确地说,这种框架在模型解释中将所有特征放在同等的地位上,要求属性平等地分布在信息相同的特征上。
例如,考虑一个将资历和工资作为输入并预测此人是否能从银行获得贷款的模型。SHAP 看重的一个形象告诉我们,他的资历和薪水都有很大的 SHAP 价值,这有助于他成功申请。然而,他的工资主要来自他的资历,在这种情况下,没有理由因为他的资历而给他的工资分配一个大的 SHAP 值。
SHAP 值框架中因果结构的缺失可能是严重的,在某些用例中会导致错误的决策,因为考虑用大 SHAP 值改进某个特征,而这些值可能只是其他特征的结果。回到贷款的例子,形象 SHAP 告诉一个人,你没有得到贷款,因为你的工资低,他引诱你换一份工资更高的工作。然而,申请仍然可能被拒绝,因为新工作会导致资历变浅。
我们将在接下来的章节中提供一些更详细的例子。
熊猫图像示例
本节考虑一个用于图像分类的预训练深度学习模型的示例,Inception V1,具有 1001 个类别标签,包括大熊猫、树蛙等。在tensor flow hub上可用。详细的实现在笔记本中提供。
以下实验考虑包含 5 幅大熊猫图像的以下集合,每幅图像被预处理为大小为(500,500,3)范围从 0 到 255 的数组,并且我们希望计算由初始 V1 模型给出的图像的前三分之一可能类别的 SHAP 值,以了解哪些像素对最终分类结果的贡献更大。

作者图片:一组熊猫图片
为此,我们编写了下面几行代码:
计算 SHAP 值
前两行除了重新缩放图像数组以适应模型的输入之外什么都不做。我们使用与输入图像大小相同的遮罩作为 SHAP 值计算的背景。 image_plot 函数给出了结果的直观图示:

图片作者:SHAP 价值观的大熊猫形象
深入查看结果,尤其是最后两张图像,我们立即注意到,熊猫身体中的像素具有较大的 SHAP 值,即我们标记了一个圆的位置:

具有与熊猫无关的大 SHAP 值的像素
这种归因可能有一个直接的解释:注意到这些像素形成了大熊猫最喜欢的竹子,我们认为它们的频繁存在带来了这种有偏见的重要性。如果我们知道熊猫的存在是竹子的因果祖先,那么将竹子的重要性归因于熊猫身体当然更有意义,而不幸的是,SHAP 值无法通过假设所有特征都是独立的来捕捉如此重要的信息。
可能的解决方案
好消息是,研究人员已经注意到,在当前的 SHAP 价值观框架中,这种因果结构并不存在。这里我们给出了两种方法:非对称 SHAP 值[1]和偶然 SHAP 值[2]。
非对称 SHAP 值(asv)主要消除了当前 SHAP 值框架的对称性,即如果两个特征值对所有可能的联盟的贡献相等,则它们的贡献应该相同。相比之下,当将所有“收益”加在一起时,引入了包含特征因果关系的权重作为概率度量。这样的设计更强调从根本原因方面的解释,而不是对直接原因的解释。
另一方面,Heskes 等人[2]指出“不需要借助非对称的 Shapley 值来整合因果知识”,并提出了一个因果 SHAP 值(CSVs)框架。该定义只不过是通过用 Pearl[3]的微积分定义价值函数来合并干预。这种设计确实将一个特征的重要性分解为两部分:直接和间接,并且通过考虑这两种不同类型对最终预测的影响,将导致更好的决策。
我邀请读者关注我以后的文章,以获得关于 CSV 的具体示例的更详细的解释和实现。
参考
[1] C. Frye、C. Rowat 和 I .格非,“不对称的 shapley 值:将因果知识纳入模型不可知的可解释性”,载于神经信息处理系统进展 33:神经信息处理系统 2020 年年度会议,NeurIPS 2020,2020 年 12 月 6-12 日,virtual ,H. Larochelle,M. Ranzato,R. Hadsell,M. Balcan 和 H. Lin 编辑。, 2020.
[2] D. Janzing,L. Minorics 和 P. Blöbaum,“可解释人工智能中的特征相关性量化:一个因果问题”,载于第 23 届人工智能和统计国际会议,AISTATS 2020,2020 年 8 月 26-28 日,在线【意大利西西里岛巴勒莫】,ser。机器学习研究会议录。,第 108 卷。
[3]Do-Calculus 重温朱迪亚珍珠主题演讲,2012 年 8 月 17 日,UAI-2012 年会议,加利福尼亚州卡特琳娜【https://ftp.cs.ucla.edu/pub/stat_ser/r402.pdf
为什么一家数据驱动型公司应该符合 GDPR 标准?
原文:https://towardsdatascience.com/why-should-a-data-driven-company-be-gdpr-compliant-3778b7df1663
不遵守 GDPR 可能会让您损失 2100 万美元

如果你从事机器学习、人工智能或数据科学领域的工作,你很可能在你的模型和算法中至少使用过一次用户的个人或可识别信息。或者您可能仍在处理这种类型的数据。
如果你的目标完全合乎道德,你只想开发报告或高级功能,这并不重要。如果你的意图不是侵犯用户的隐私,这并不重要。如果您处理来自欧盟用户的数据,您必须遵守欧洲隐私保护法规 GDPR。
不遵守 GDPR 可能会让您损失高达 2100 万美元。如果是公司的话,就更!幸运的是,你可以通过遵守 GDPR 法规来避免这样的罚款。
在这里,你会明白什么是 GDPR。此外,您将了解为什么 GDPR 合规性如此重要,但却不容易实现。
让我们深入了解 GDPR 合规背后的“原因”。
免责声明:我不提供任何法律建议,只是 GDPR 在行业中的概述。
什么是 GDPR?
GDPR,简称通用数据保护条例,是一项于 2018 年 5 月生效的条例,更新和规范了整个欧盟(EU)的数据隐私法律。官网将其定义为“世界上最严厉的隐私和安全法”。
具体来说,GDPR 的一些隐私和数据保护要求包括:
- 要求用户同意处理他们的数据。
- 匿名化从用户处收集的数据以保护隐私。
- 在数据泄露的情况下向您的用户提供通知。
- 安全管理从一个国家到另一个国家的数据传输。
简而言之,GDPR 为欧盟公民的数据处理强加了标准。请注意,GDPR 对任何地方的公司和个人都施加了义务。所以,你或你的公司在哪里并不重要。只要您收集或处理有关居住在欧盟的人的数据,您就必须遵守 GDPR 法规。
请记住,GDPR 不仅仅是一个遵守的义务。让我们了解一下为什么 GDPR 合规性会带来诸多好处。
GDPR 合规的三大优势
让我们看一下 GDPR 合规性如此重要的三大原因。
1.提高信任度、声誉和可信度
近年来,人们越来越意识到与滥用个人数据相关的危险。换句话说,消费者现在比以往任何时候都更加怀疑他们的数据是如何被管理的。
为了遵守 GDPR,您必须获得用户的同意,然后才能使用他们的数据。在此过程中,您需要清楚地解释您计划如何使用他们的个人信息。这给了用户更多的选择,也让他们对你打算如何处理他们的数据放心。
因此,如果你是一家企业,或者只是有一个通过 cookies 收集数据的网站,符合 GDPR 标准会让你在用户眼中更加可信。另一方面,如果你是一名 ML 工程师、数据科学家或研究人员,遵守 GDPR 将帮助你产生隐私安全的报告或提高你在科学界的声誉。
2.提高营销效果
GDPR 要求您实施选择加入政策。这意味着您的用户可以随时要求您删除他们的所有个人数据。丢了这个数据就不是剧了。相反,这将帮助您减少数据中的噪音,并支持您的营销目标。我们用一个例子来理解为什么。
假设你经营一份时事通讯。给你的读者选择的机会会帮助你精简你的邮件列表。只有真正对你的产品、服务或新闻感兴趣的用户才不会退订。这将使你能够更有效地与他们沟通,并相应地提高转化率。
此外,请记住,让用户取消订阅可以为您提供数据来生成关键业务 KPI。具体来说,您将能够确定有多少用户决定取消订阅以及为什么会发生这种情况。由于这个原因,大多数简讯退订页面都要求用户在告别前留下反馈。

Mailchimp 的退订表格
3.增强的数据管理
遵守隐私法规迫使您改进数据管理流程。这将有助于您遵守 GDPR 施加的限制。此外,拥有更好的数据管理流程更容易证明您符合 GDPR 标准。
具体来说,无论你是大公司还是个人,采用创新技术将有助于避免数据隐私争议。例如,您应该能够与您的 ML、数据科学或分析团队共享您的生产数据,同时保护用户个人数据。
如果您不符合 GDPR 标准,会发生什么?
不遵守 GDPR 协议是有代价的。如第 83(5) 条所述,如果作为个人未能遵守 GDPR,最高可被罚款 2000 万欧元(2100 万美元)。就公司而言,罚款可达全球年营业额的 4%。现在应该很清楚了,你必须认真对待 GDPR。
请注意,GDPR 适用于所有人,无论企业规模如何。比如亚马逊因违反欧盟数据保护规则被罚款 8.65 亿美元。同样, Meta 因泄露 5 亿用户数据被欧盟罚款 2.77 亿美元。同样的事情也发生在谷歌身上,因为 GDPR 的侵权行为,谷歌不得不支付超过 5000 万美元。
因此,在 GDPR,即使是大公司也会失败。这是因为确保数据隐私并不容易。特别是,从文档、数据库和多媒体文件中识别和删除敏感数据是一个需要手动操作的复杂问题。幸运的是,这不再是真的了!
得益于机器学习和自然语言处理服务和技术,你可以 https://www.private-ai.com/asr/ 自动从文本文档和多媒体文件中删除个人数据。具体来说,如果你在谷歌学术找“gdpr 的机器学习”,你会找到超过 37k 的结果。所有这些方法都有助于您自动实现 GDPR 合规性。
结论
在本文中,您了解了什么是 GDPR,为什么您必须遵从 GDPR,以及如果您不遵从 GDPR 将会面临什么。正如在这里了解到的,GDPR 保护您的用户的隐私,也为您和您的企业带来了一些好处。
与此同时,提取和识别个人信息可能具有挑战性。幸运的是,你可以通过基于机器学习和人工智能的工具来实现自动化。所以,技术是来帮助你的!
感谢阅读!我希望这篇文章对你有所帮助。如果有任何问题、意见或建议,请随时联系我。
为什么要用正交多项式?
原文:https://towardsdatascience.com/why-should-we-use-orthogonal-polynomials-b42b36f158a7
数据科学的正交多项式

这张图片是作者使用人工智能工具生成的。

1 至 6 次勒让德多项式:图片由作者生成
正交多项式是求解和解释许多次微分方程的有用工具。此外,它们是函数的最小二乘近似、差分方程和傅立叶级数的方便的数学工具。正交多项式的另一个重要应用是纠错码和球封装。正交多项式的一些其他模糊应用是图的匹配多项式和随机矩阵理论。
为了更好地理解正交多项式,我们首先需要理解向量、内积、正交性、Gram-Schmidt 正交归一化和希尔伯特空间。接下来的几段给出了这些预备知识的简要概述,以便理解正交多项式。
预赛
向量
矢量是有方向和大小的物体。考虑一下你开车的速度。如果我们不量化汽车的行驶方向,那么它就不是一个矢量。但是如果我们指定汽车向东行驶,那么它可能是一个矢量,因此它将被称为速度。几何上,它是一条线段,箭头指向某个方向。在印刷文本中,可以用粗体字母表示,如 a 或普通字母上的箭头。在本文中,我们将使用粗体字母。矢量 a 的大小由∨a∨给出,如图 1 所示。一般来说,∨一个∨也叫一个范数。在二维坐标空间中,点由有序对 (x,y) 表示。向量的每一端可以由两个这样的有序对 (x,y) 和 (u,v) 来定义。由所有这样的有序对组成的平面称为向量空间。对于所有的实数,二维向量空间用ℝ表示。

图一。一个向量
内部产品
内积是将两个向量相乘的一种方式,结果是一个标量(即,只是一个大小,没有方向的概念)。如果两个向量是用有序对 (x,y)**(u,v) 表示的 a 和 b ,那么它们的内积就是 xu + yv** 其中 u* 是 u 的复共轭, v* 是 v 的复共轭。当然,当这些数是实数时,那么复共轭就和数本身一样。内积也用÷a,b 表示。
正交性
两个向量 a 和 b 正交,如果它们的内积〈T4〉a,b 〉= 0。在这种情况下,一个向量在另一个向量上的投影折叠成一个点,我们得到的距离为零。如果一个向量列表中的向量是成对正交的,并且每个向量的范数等于 1,那么这个向量列表称为正交的。
格拉姆-施密特正交归一化
Gram-Schmidt 正交归一化过程将一列线性无关向量转换成正交向量。我将用一个例子来解释这个过程。
考虑三维向量空间中的三个向量: a (1,-1,1)b(1,0,1)c(1,1,2)。我们寻找的新的向量列表是[ u , v ,w】。
- u = a = (1,-1,1)。
- v = b-(****b,u〉⋅u/| | u | |)=(1/3,2/3,1/3)
- w=c-(〉c,u〉⋅u/| | u | |)-(〉c,v〉⋅v/| | v |)=(-1/2,0,1/2)
这些是正交向量。现在,我们将它们归一化,用它们除以它们的范数得到标准正交向量,从而得到 u (√3/3,-√3/3,√3/3), v (√6/6,√6/3,√6/6), w (-√2/2,0,√2/2)。
现在,回到正题。
正交多项式
多项式也可以以类似于向量的方式使用,即,它们在给定范围【a,b】上服从类似于正交向量的正交关系。在这个意义上,对于一个多项式 p(x) ,和一个变量 x 中的 q(x) ,我们可以将它们的内积定义为

等式 1。两个多项式的内积
其中 w(x) 为【a,b】范围内的任意非负函数。有趣的是,我们还可以对多项式执行 Gram-Schmidt 过程来获得正交多项式。作为例子,我们可以对【1,x,x,…】进行正交归一,得到一族正交多项式。根据 w(x) 的选择,我们可以得到不同的多项式族。例如:
- 对于区间 [-1,1] 上的 w(x) = 1 ,得到勒让德多项式。
- 对于 [-1,-1] 上的 w(x) = 1/sqrt(1-x ,我们得到第一类切比雪夫多项式。
- 对于 [-1,1] 上的 w(x) = sqrt(1-x ) 我们得到第二类切比雪夫多项式。
- 对于 w(x) = exp(-x) 关于 [0,∞] 我们得到拉盖尔多项式。
正交多项式的一些性质;
- 如果 pₙ(x) 是 n 次多项式,则

因此, pₙ(x) 正交于所有次数小于 n-1 的多项式。
2.进一步地,随着系数的选择 aₙ 和 bₙ 取决于你选择w(x)我们有下面的递推关系:

性质 2 在法瓦德定理中陈述,给定如上所述的三项递推,存在 w(x) ,反之亦然。
正交多项式的一些应用
高斯求积逼近定积分
高斯求积是计算定积分的一种方法

【a,b】范围内的定积分
我们考虑一个变量代换 x = ( b — a)y /2 + (a + b) y/2,和g(y)=(b—a) f(x)/2*将上述定积分转换为

虽然中点规则和梯形规则在数值计算上述定积分时是最简单的,但有一个规则可以精确计算上述积分。在数值上,上述积分可写成

这需要选择合适的 yᵢ 和 wᵢ 。在这种情况下,卡尔·弗里德里希·高斯设计出,如果我们选择 yᵢ 作为与 w(y) 相关联的正交多项式pₙ(y的根,那么我们就可以精确地积分次数为 2n-1 的多项式。这条规则被称为高斯求积规则。显然,最简单的情况可能是当 wᵢ = 1 时,这就是勒让德多项式的情况。利用勒让德多项式,我们得到高斯-勒让德求积法则。
正如劳埃德·n·特雷费森在https://epubs.siam.org/doi/abs/10.1137/060659831中 所研究的那样,高斯求积极其有效。
正交多项式回归
通常在线性回归模型方法中,选择合适的多项式阶是必要的。在模型构建策略中,我们以递增的顺序将数据拟合到模型中,并在模型拟合的每一步测试回归系数的显著性。这就是正向选择法。我们不断增加顺序,直到最高顺序的 t 检验不显著。
我们还有一个反向消除方法,我们从一个适当的最高阶模型开始,开始消除每个最高阶项,直到我们获得剩余最高阶项的显著 t 检验统计量。
向前选择法和向后淘汰法不一定导致相同的模型。然而,我们可以寻找一个模型,在这个模型中,增加一个新的高阶项只是改进了模型,而不需要重新计算。这不能通过变量 x 的连续幂来实现。但是它可以通过正交多项式系统来实现。如果线性回归具有形式 y = Xβ + ε ,那么等价的正交多项式回归模型由下式给出

我希望正交多项式的主题对对数值分析和模型拟合技术感兴趣的读者有所帮助。如果你有兴趣阅读更多类似的话题,请通过https://rahulbhadani.medium.com/membership订阅 Medium。虽然只是 5 美元/月,但对我有很大的帮助,因为 Medium 支付你的订阅费的一部分给作家。
参考
- https://mathworld . wolfram . com/Gram-schmidtortonormalization . html
- https://Twitter . com/sandwich maker/status/1477449233405861889
- https://github.com/sigma-py/orthopy
- https://math . okstate . edu/people/yq Wang/teaching/math 4513 _ fall 12/Notes/Gaussian . pdf
- https://docs . scipy . org/doc/scipy/reference/generated/scipy . integrate . quadrature . html
- https://people.sc.fsu.edu/~jpeterson/numerical_quadrature
你为什么要在业余时间学习 SQL?
原文:https://towardsdatascience.com/why-should-you-pick-up-sql-in-your-spare-time-65634dbb6eb1
SQL 简单易学,可用于各种业务应用。它是一种用于商业数据分析的强大语言。不费吹灰之力,每个人都可以学会使用 SQL。

凯文·Ku 在 Unsplash 上的照片
什么是 SQL,它做什么?
SQL(发音为“sequel”或“S-Q-L”)代表结构化查询语言。它是一种用于与数据库通信的标准计算机语言。换句话说,它允许您与数据库对话以提取数据或进行更改。看看最近的招聘信息,你会发现对 SQL 技能的需求越来越大。这是因为数据在商业中变得越来越关键,SQL 有利于每个人都知道,而不仅仅是数据专业人员。
两个主要群体将 SQL 用于不同的目的。第一种是开发人员使用 SQL 构建数据库或与数据库交互。你电脑上的大部分应用程序或者你日常使用的网络应用程序都是由数据库驱动的。当您访问应用程序或输入数据时,SQL 代码会在后端执行,以向数据库读取或写入数据。
- 将数据插入数据库:如果你正在开发一个应用程序,你需要将数据插入数据库。例如,当新用户注册您的应用程序时,他们的信息需要添加到数据库中。
- 更新数据库中的数据:这是开发人员的另一项常见任务。例如,如果用户更改了他们的地址,您必须相应地更新数据库。
- 从数据库中删除数据:您可能还需要从数据库中删除数据。例如,如果用户删除了他们的帐户,您必须从数据库中删除他们的数据。
第二个用户组是从数据库中检索数据以进行业务分析的业务人员。这些将是业务人员将执行的最受欢迎的用例。知道如何从数据库中提取数据并进行简单的数据分析将在你的职业生涯中非常有用。活动示例包括:
- 从数据库中检索数据:这是使用 SQL 可以做的最基本的事情。您可以使用各种筛选器来准确指定要检索的数据。例如,您可以检索上个月在加利福尼亚购物的客户的所有数据。
- 执行数据分析:SQL 也常用于数据分析。例如,您可以使用 SQL 来计算加利福尼亚所有客户的平均购买量。
为什么了解 SQL 有好处?

塞巴斯蒂安·赫尔曼在 Unsplash 上的照片
作为一名商务人士,学习 SQL 可以让你在决策时更加以数据为导向。在当今的商业世界中,数据变得越来越重要。不用等别人给你提供数据,你可以自己访问数据,迅速得到你需要的答案。
自己处理数据也会让你更懂数据。这意味着当数据呈现给你时,你将能够更好地理解数据,并且你也将能够更有效地向他人传达你的数据需求。那些能够有效分析数据的人将比那些不能有效分析数据的人有明显的优势。
SQL 也是简历中一项有价值的技能。如前所述,SQL 技能的需求越来越大,了解 SQL 会让你在潜在雇主面前更有市场。
我已经知道 Excel 了。何必学习呢?

对于小数据集,Excel 是一个优秀的工具,但是当数据变大时,它很快就变得很麻烦。每个 Excel 文件都有行数限制(大约一百万行)。当数据超过这个限制时,您需要将数据分割成多个文件或使用其他工具。您组织的数据通常也不会存储在您的计算机中。因此,将不同数据源的数据收集到您的计算机中进行分析既费时又麻烦。另一方面,SQL 是为处理大型数据集而设计的,比 Excel 快 10-100 倍。
除了数据大小限制之外,Excel 的另一个问题是它是为单用户使用而设计的。换句话说,它假设只有一个人同时使用数据。数据存储在本地计算机上,如果两个人在处理同一个数据集,他们需要手动合并他们的更改。这可能会导致错误和数据不一致。相反,SQL 数据库是为多用户并发使用而设计的。用户可以在不影响彼此工作的情况下查询集中存储的数据。
Excel 的另一个问题是不同文件中的数据不容易合并。如果在两个不同的 Excel 文件中有数据,则必须手动将数据从一个文件复制并粘贴到另一个文件中。这不仅耗时,而且容易出错。最糟糕的是,如果你在分析中犯了一个错误,由于数据分散在不同的步骤中,很难追踪到错误。即使您找到了错误,您仍然需要再次执行所有的 VLOOKUP 和复制粘贴。

作者图片:SQL 与 Excel 常见问题
怎样才能开始学习 SQL?

如果你想学习 SQL,有两个主要步骤:学习语言和使用语言。在学习部分,有大量资源可供您开始学习:
除了这些资源,你还可以在 YouTube、书籍和训练营上找到指导视频。
一旦你掌握了技术部分,是时候开始应用你所学的知识来解决现实世界的商业问题了。最好的方法就是找到你感兴趣的数据,然后开始摆弄它。您的组织可能已经有一个数据仓库,或者您可以寻找公共数据集。这是一个重要的步骤,因为它允许你以一种有意义的方式应用你所学的概念。做好这一点,你会发现你很快就精通 SQL 了。
您为此使用的数据不需要很大。事实上,从小型且结构良好的数据开始往往更容易。寻找小型且结构良好的数据集的绝佳场所。在下一篇文章中,我将讨论如何通过在商业环境中使用语言来学习 SQL。
总结
随着业务越来越受数据驱动,对 SQL 技能的需求也在增加。SQL 是一种通用的技能,任何想要检索和分析数据的人都可以使用。查看我的下一篇文章,关于如何在 Google BigQuery 上用真实世界的数据集学习 SQL。
开始学习 SQL,给自己一个优势。
为什么 SMOTE 不一定是不平衡数据集的答案
人人都爱 SMOTE,但它真的是银弹吗?

埃伯哈德·🖐·格罗斯加斯泰格在 Unsplash 上拍摄的照片
当不平衡分类的话题出现时,许多人称赞 SMOTE 是首选方法。事实上,流行的算法在社区的进化选择过程中幸存了下来。然而,我们仍然不应该盲目地对我们的问题提出现成的解决方案。为了证明这一点,我们可以构造让 SMOTE 惨败的实际玩具例子。在此之前,让我们简单回顾一下不平衡问题和算法本身。
SMOTE 打算如何解决不平衡分类的问题
社区数据科学的大部分对不平衡数据的叙述如下:
如果数据集中的一个或多个类严重不足,分类算法在训练后通常无法识别这些类。为了解决这个问题,我们使用一些重采样技术来创建一个新的数据集,其中所有的类大致相等。SMOTE 算法是迄今为止经受住时间考验的重采样技术之一。
总之,SMOTE 通过在少数群体的域间隙中随机创建合成观测值来对代表性不足的类进行过采样。考虑一个 1D 的例子,其中你的少数类实例都位于区间[0,1]中。SMOTE 现在通过随机插入两个现有的数据点,在此间隔内创建更多的少数实例。
这里不再赘述,但如果想了解更多,推荐阅读原文。值得一提的是,如果输入数据不连续,原始的 SMOTE 算法将不起作用。考虑一次性编码数据——随机插值步骤将创建非二进制合成数据,这显然是错误的。幸运的是,不平衡学习库包含了一个适合这种情况的选择。
创建一个简单的例子,事情在 SMOTE 中上升
我们可以很容易地构造一个 SMOTE 有用的反例。考虑以下数据生成过程:

(图片由作者提供)
原始数据——一切看起来都很好
逻辑回归模型应该很容易学习条件类分布。由于输入变量X的变化,我们可以预期类别y=0的代表性不足。让我们用 Python 绘制这个例子:

一个简单的例子,其中少数类仅占总数据集的 4.5%。由于足够大的训练集,逻辑回归模型可以容易地学习条件类概率。(图片由作者提供)
正如我们所见,逻辑回归模型能够很好地学习潜在的类别概率。
过采样让事情变得更糟
现在,假设您不能像我们在这个例子中那样很好地检查数据。您可能只看到您的数据集不平衡,并迅速开始实施 SMOTE 解决方案。
让我们为上面的例子绘制这种方法的结果:

使用 SMOTE 后的相同型号。逻辑回归模型预测的类别概率现在比之前差得多。(图片由作者提供)
在应用 SMOTE 之后,我们的模型的性能显著下降。这很糟糕,因为 SMOTE 是处理不平衡数据的最佳解决方案。
哪里出了问题?
上述示例的结构很容易向我们指出潜在的问题:
SMOTE 隐含地假设 类分布在少数类实例周围的一些邻域中足够均匀。
简单地说:如果数据生成过程在你的输入域中的类之间频繁地“跳跃”,你会有一段不好的时间。
我们可以用另一个情节来验证这一说法:

根据数据生成流程,少数民族类出现为*abs(X)>3*。然而,使用 SMOTE 破坏了这个简单的模式。(图片由作者提供)
在这个例子中,在每个“少数面元”中出现少于五个数据点。由于 SMOTE(默认设置)在每个少数点的五个最近邻点之间进行插值,因此该算法将合并来自远处聚类的样本。这显然打破了这个玩具例子中的模式。
一旦少数聚类大于 knn 步中的邻居数量,情况又变得合理了:

一旦少数聚类包含足够多的数据点,SMOTE 将再次按预期工作。(图片由作者提供)
是不是总的来说注定要失败?
像往常一样,我们不应该根据一个简单的例子就得出算法有用的结论。与其他方法一样,SMOTE 的有用性在很大程度上取决于手头的问题。毕竟原始论文是 2002 年的,所以算法到目前为止绝对是经得起时间考验的。
但是,您应该始终记住,每个数据科学问题都是不同的,即使是最受称赞的算法也可能无法完成您的任务。另一方面,这篇文章提供了一些有用的经验法则,告诉你什么时候应该使用 SMOTE,什么时候不应该。
另一个例子,不幸的是,我在这一点上只有经验证据,是硬类边界的可区分分类器。SMOTEed 数据似乎“硬化”并改善了另一个玩具分类问题的逻辑回归模型的类别边界:

无(左)和有(右)SMOTE 应用的 Logistic 回归。应用 SMOTE 会使模型的硬类边界(橙色线,通过舍入类概率获得)非常接近真实的类边界(蓝色线)(图片由作者提供)
这表明 SMOTE 可能对不平衡图像分类特别有用。对于图像数据,我们通常可以期待低噪声。例如,一个明确的狗的图像实际上是一只猫的可能性相当低。此外,除了一些潜在的边界情况和种族,狗和猫很容易区分他们的外表。如前所述,这意味着相当严格的阶级界限。
对于表格数据,我们应该预料到标签噪声会更加普遍。考虑臭名昭著的信用卡欺诈检测问题,欺诈交易通常占少数类别。虽然偏远国家异常高的提款率更有可能是欺诈性的,但仍然有相当高的几率是正常的。
SMOTE 不管用怎么办?
总结以上几节— SMOTE 可以工作,但不能保证。如果 SMOTE 让您失望,并且您已经验证了您的代码本身是正确的,我会推荐以下步骤:
- 网格搜索尽可能多的 SMOTE 超参数 —如果你有足够的可用资源和数据,你应该尝试彻底搜索最佳的 SMOTE 超参数,如果你还没有这样做的话。
- 尝试另一种用于不平衡数据的重采样算法—Python 中的不平衡学习包提供了更多的重采样方法。尝试他们的又一轮超参数优化。
- 为你的分类器调整决策界限——这个有点棘手,只用几句话来解释。我可能会在以后的文章中更详细地解释这一点。现在我推荐这个和这个来解释一下。
- 深入挖掘不平衡分类的研究 —一个快速的谷歌学术研究给你大约 25,000 篇关于不平衡分类问题重采样的文章。你很有可能找到适合你手头问题的东西。
结论
希望这篇简短的文章能够说服你不要盲目地跳上数据科学的宣传列车。虽然 SMOTE 和重采样在工具包中肯定有它们的位置,但它们并不是神奇的银弹。
显然,“SMOTE”和“re-sampling”甚至是求职面试中不平衡数据的预期标准答案。我个人认为这是很有问题的做法。实际上,这种做法进一步传播了关于 SMOTE 普遍适用性的错误观念。希望这篇文章能说服你。
此外,这也是另一个例子,说明了为什么对算法的深入理解是有用的,并且有助于调试。毕竟,你确实需要(至少一些)机器学习的数学知识🙂
参考
【1】Chawla,Nitesh V .等 SMOTE:合成少数过采样技术。人工智能研究杂志 2002 年第 16 期。
【2】Elor,Yotam 哈达尔·阿韦尔布赫-埃洛尔。打,还是不打?。arXiv 预印本 arXiv:2201.08528,2022。
原载于 2022 年 6 月 13 日 https://sarem-seitz.com**的 。
为什么这么多组织在人工智能和机器学习上犯了错误
人工智能的未来:更快地自动化错误决策

照片由作者大卫·E·斯威诺提供
如果您想获得数据科学投资的投资回报,您必须了解它能(或不能)做什么。否则,你就是在浪费时间和金钱。
人工智能(AI)被认为是解决大量商业弊病的灵丹妙药,但它并不总是产生积极的、可操作的结果。这是为什么呢?越来越多的组织目前在运营中使用人工智能、机器学习(ML)或数据分析,但这些实施的商业价值并没有得到实现。
人工智能停滞不前而不是飞速发展有一系列原因。人们很难理解人工智能到底在做什么,以及它的局限性。这些误解通常是由媒体驱动的,带有耸人听闻或过于简化的报道。然而,这些数据科学的神话也被许多声称知道他们在谈论什么的人抛出。
关于人工智能的 8 个神话
1.人工智能会神奇地解决你所有的问题
AI 承诺大事;增加收入,降低成本,在欺诈发生前识别欺诈,消除您所有重复和单调的工作。但是那些怀着远大梦想进入人工智能和人工智能领域的组织往往会发现现实并不尽如人意。
人工智能应该是一个循序渐进的过程。组织应该从改善流程和提高客户满意度或自动化业务流程这样的项目开始。随着时间的推移,随着对人工智能的能力和理解的增长,它可以用来应对巨大的金融挑战。
也要记住帕累托原则;80%的成果来自 20%的投入。没有必要深究那些不会产生可衡量结果的细节和问题。它们只会让系统——和你的员工——陷入困境。
2.机器学习是关于“像人一样思考”
人类是复杂的生物,我们的大脑复杂得令人难以置信。我们一直在使用启发式方法,这是我们从多年的经验中学到的“经验法则”。我们学到的刻板印象让我们做出快速的判断,但不一定是正确的。我们不希望计算机像人类一样思考,因为人类有错误的思维。
事实是,机器学习就是从数据中做出预测。如果数据质量差,结果就不会客观。
垃圾输入=垃圾输出
机器学习只是学习数据中的偏差,以及团队做出的假设。为什么这很重要?算法、数据和团队中的偏差会给企业带来重大损失。
例如,银行使用人工智能来决定借钱给谁。谁是风险?谁更有可能按时还贷?我们知道很多数据是有偏差的,所以基于这些数据的机器学习也会有缺陷。
历史上,男性是抵押贷款的持有者。申请贷款的妇女经常被拒绝,原因与她们的经济能力或偿还银行的能力无关。如果人工智能查看这些数据,它不会说“哦,我发现银行系统在历史上是重男轻女的”,而是说“女性贷款更经常被拒绝,因此女性的抵押贷款申请应该被拒绝”。还记得苹果信用卡惨败给丈夫的信用额度比妻子高 20 倍吗?
从各方面来看,女性的信贷风险实际上都低于男性。女性按时还贷,违约率更低。因此,如果一家银行实施有缺陷的 ML,他们不仅是在借钱给高风险的男性,而且还错过了低风险女性将提供的收入。
另一方面,法律意味着你不能基于性别进行歧视;在确定信用度时考虑性别是违法的。但是无视性别的信用贷款歧视女性。机器学习是从数据中学习,这些数据往往是有缺陷的,有偏见的,而且远非客观的。大多数时候,它不是公开的,而是训练数据集中的细微之处和代理。
3.AI 是即插即用的
有了所有的 SaaS 服务和软件公司做出的重大承诺,你认为人工智能很容易是情有可原的。只要把数据放进去,机器就会飞快地浏览信息,吐出你想知道的东西。不需要编码知识!
但是,即使员工了解软件服务,也有很多工作需要先做。
数据清洗:有数据,有好的数据。如果数据错误、不完整、样本太小,或者记录的信息完全错误,那么输入大量数据是没有意义的。
理解任务的结果:当一个企业或客户说他们想要一个钻头时,一个好的数据科学家知道他们实际上想要一个洞。不仅如此,他们还知道数据是否能够提供这些信息。领域知识:数据科学的现实是行业缺乏人才;没有足够的高质量数据分析师和科学家。缺乏训练有素和有经验的员工,这阻碍了人工智能的有效性和在市场上的应用。组织没有(或找不到)具备适当技能的数据科学家作为员工,因此外包给第三方提供商。依赖外部供应商只是一种短期解决方案;领域知识对于产生准确的结果至关重要。
4.机器学习预测未来
这是真的,如果未来和过去完全一样的话。ML 训练历史数据,并根据理论预测同样的事情会再次发生。不过,ML 不仅仅是做预测。您可以使用它来创建业务洞察和简化流程,添加新产品或新功能,以及进行预测。如果你不用 ML 来改变你商业决策的行为,那还有什么意义呢?
5.随着时间的推移,预测会自动变得更好
ML 使用不同的算法,称为模型,来创建他们的预测。一旦你开始生产一个模型,它就开始退化。这是因为数据会变,环境会变,人也会变。一个模型将是一致的。这就是为什么模型需要从一开始就重新培训,或者如果新模型更合适就使用新模型。
这些退化的模型是由于数据漂移。这是模型试图预测的东西被不可预见的变量改变的时候。例如,如果你预测一家实体店的销售额,其他变量也需要考虑在内,比如天气、即将到来的假期以及你的竞争对手在做什么。
概念漂移的一个例子是当皮肤癌诊断系统由于忽略变量而错过皮肤癌时。该机器知道寻找凸起的边缘、不规则的形状和随时间的变化,这将提醒临床医生怀疑癌症。但如果机器不考虑肤色(由于日晒或人种),就会出现漏报。
泛化或协变量转移是困扰模型的另一个问题。如果用于训练模型的数据来自一个群体,可能是一个西方的富裕国家,那么它对于该组数据来说是过度拟合的。其他组和看不见的数据意味着预测不会准确,因为它们不能很好地概括。必须采取措施防止模型退化。部署后必须监控 ML 性能。如果模型降级,要么重构模型,要么尝试另一个更适合的模型。它可能需要添加新功能或更改参数。这就是所谓的持续学习,如果预测是准确的,他们需要检查和调整。
6.机器学习是为了提供更高的准确性
准确性很好,但这并不能说明性能。一个有 51%准确率的模型可以正确预测彩票号码,你就赢得了一千万美元。当预测导致巨大损失的欺诈性贷款申请时,准确率为 99%的模型可能会给出假阴性。
ML 研究概率,而不是确定性。
就像模型需要不断的重新评估一样,结果需要被检查以保证精确性。多少个假阴性到假阳性?这些错误的商业价值是什么?您损失了多少潜在收入?是系统没有足够的辨别力,你给你的销售团队提供了太多的线索,还是他们因为系统过于繁琐而拒绝了太多的线索而无所事事?
7.AI 和 ML 正在取代人
是的,天要塌下来了,小鸡。每当有重大的、有威胁性的变化时,人们都会担心会失去工作。这给人工智能的采用带来了阻力,因为人们试图抵制工作不安全感。一项研究显示,38%的人预计技术将在未来三年内淘汰他们工作场所的工作。据预测,到 2030 年,制造业将有多达 2000 万个工作岗位被机器人夺走。这是一些可怕的数字。
真实情况是 AI 和 ML 在增广人。
他们接受枯燥、重复的任务,并允许人们进行创造性的、不可预测的、更复杂的任务。人工智能应该与人类携手合作,在工作场所做出积极的改变。
我们可以回顾一下工业革命,看看人工智能革命的未来会是什么样子。这一对 18 和 19 世纪几乎所有工作的重大改革并没有造成长期的大范围失业和痛苦。人们总是会找到新工作(通常是在一段痛苦的调整期之后),对大规模失业的担忧是没有根据的。
虽然人工智能会导致失业,但预计任何损失都将被更强大、更富裕的经济创造的新工作所抵消。自动化和人工智能将改变工作和生活,这是毋庸置疑的。但在大多数情况下,这些变化将是积极的。
8.数据越多,对机器学习越有利
GarbageIn:GarbageOut。如果你给机器输入不相关的信息,没有被清理的数据,或者是错误的,结果将会反映出来。数据科学家说,他们大约 50%的职责是清理数据,这是有原因的。即使是最聪明的机器也无法从错误的数据中获得洞察力。
数据科学的好处可能是巨大的
这不是一个神话;数据科学的商业成果,如果做得好,可以是所有那些承诺的东西。更快、更好、更强,组织的超人。
但是,要在组织层面上使用人工智能,需要对它能做什么以及它在哪里没用有更广泛的理解。否则,这只是那些 80%左右的数据科学项目中的又一个,永远不会启动。为了获得数据科学投资的投资回报,要对人工智能能做什么持现实态度,并明智地将其应用于具有大量数据的明确定义的项目。虽然这听起来不像即插即用和预测未来那样诱人(也不容易),但这是一种更成功的战略,可以获得人工智能可以提供的结果。
这篇文章的一个版本首先出现在位于 https://www.alteryx.com/input/blog/8-myths-about-ai的 Alteryx #Input 博客上
为什么软件开发技能对数据科学至关重要
意见
数据科学家应该向软件工程师学习

彼得·冈博斯在 Unsplash 上的照片
您的旅程概述
数据科学家的传统角色
大约十年前,数据科学家被《哈佛商业评论》、《福布斯》和其他杂志评为本世纪最性感的工作。这并不奇怪,因为数据科学家被给予高工资和有趣的问题去解决。它很快成为大学毕业生和自学者渴望的热门工作。
2010 年代的数据科学家拥有极其广阔的范围和不明确的职责。数据科学家就是能够从数据中获得洞察力的人。
不同公司的两位数据科学家可能承担着截然不同的职责:
- 在 A 公司,数据科学家可以使用 Microsoft Excel、PowerBi 和 SQL 数据库等技术。A 公司的数据科学家发表演讲,分享他们的发现。
- 在 B 公司,数据科学家可以在 Jupyter 笔记本上开发机器学习模型。B 公司的数据科学家使用高级统计数据,并通过报告传达她的见解。
企业逐渐明白,在生产中拥有预测模型真的很有价值。然而,有些事情非常不对劲。许多公司艰难地认识到,在生产中获得可靠的模型并不容易。尤其是如果你雇佣了你的整个数据科学团队,因为他们擅长微软 Excel 和高级统计。
很明显,没有软件开发技能,数据科学团队无法将模型部署到生产中。
这是否意味着公司应该雇佣软件开发人员来做数据科学家的工作?不!数据科学家理解和理解数据的技能是多年经验的结晶。不应低估这一点🔥
注意:除了缺乏软件开发技能之外,还有其他问题会使模型难以部署到生产中。孤立的数据源或糟糕的沟通实践是其他常见的原因。
变化的世界
解决生产问题的方法是雇佣更多的人来弥补技能的不足。我们现在有许多新的角色,如机器学习工程师或 MLOps,旨在部分填补这一空白。连同基于云的部署解决方案,这无疑有所帮助😃
重点是跨职能团队。跨职能数据团队由不同背景的人组成。这使得将模型部署到生产环境中更加现实。
这个团队可能包括一个具有 DevOps 背景的人和一个具有测试背景的人。关于如何发展跨职能团队,你可以从看一下这篇博文开始。
然而,部署过程也受到数据科学家的软件开发技能的影响。通常机器学习模型是用 Jupyter 笔记本上的乱码开发的。它们留给传统的软件开发人员/测试人员/DevOps 人员来“修复”和部署。这种方法创建了一个瀑布结构,使整个过程变慢。
应考虑以下因素:
当团队成员积极努力改进彼此的领域时,跨职能团队变得真正有用。
这对跨职能团队中的数据科学家意味着什么?数据科学家应该努力掌握软件开发领域的一些基本技能。这也将使他们能够拓展 MLOps 等需求极高的领域。
这一开始看起来非常耗时!我并不是说数据科学家应该成为一名成熟的软件开发人员。我并不主张数据科学家一定要建立网站或管理 Kubernetes 集群(尽管如果你想这么做,我向你致敬!).
目标应该是掌握并整合这些领域的基础知识,以便更快地将模型部署到生产中并产生价值。更好的是,公司应该分配(支付💸)在下一节中,数据科学家应该学习和练习四种软件开发技能。
四种基本的软件工程技能
数据科学家应该在他们的软件开发之旅开始时关注四个关键技能:
你应该学习如何编写高质量的代码🐍
如果你正在使用 Python,那么你应该始终遵循一个风格指南(通常是 PEP8 )。您应该编写可重用和可测试的模块化代码(以函数和类的形式)。变量名应该仔细挑选。
目标是编写尽可能接近于自文档化的代码。掌握一些设计原则有助于提高代码的可重用性和可扩展性。在这里,我的建议是要有远大的梦想,但要从小处着手。每周,找出你代码中可以改进的新部分。你会惊讶于你提高技能的速度。
2—您应该学会如何使用命令行💻
学习使用命令行(对于数据科学来说通常是 bash)是非常有益的。许多命令行工具(如 csvkit 和 curl )对于数据科学家来说非常有用。
但是,除了特定的工具之外,习惯于命令行的心态真的很有用。掌握后,命令行将成为您最好的朋友,而不是敌人。我的建议是要么从命令行的一般入门课程开始(比如 YouTube 上有很多免费课程),要么从 csvkit 开始。对于 csvkit,我做了一个免费视频系列,你可以看看开始😸
3—您应该学习如何使用版本控制💾
一个版本控制软件(典型的是 Git )会将你的代码库随着时间的变化分组到你可以恢复的块中。这使得编码过程可靠,没有不必要的损失。如果处理得当,许多贡献者可以在同一个项目上工作,而不用担心破坏彼此的代码。
在现代世界中,在任何严肃的数据科学团队中,使用版本控制都是理所当然的。在这种情况下,您可以逐渐引入新的更改,或者在必要时恢复到以前的版本。
4 —你应该学习如何编写测试⏰
编写测试可以确保代码的可靠性。如果代码发生了修改(实际上,它可能会以某种方式被修改),那么您可以通过确保所有测试都通过来安全地完成。为你的代码编写测试将迫使你考虑边缘情况,从而更加关注你的代码。
知道如何设置一个最小的 CI/CD 环境来自动评估测试也是非常有用的。但是如果你是测试新手,那么我建议你从用 Python 的 pytest 库编写一些基本的单元测试开始。
其他话题?
上述四个主题不应被视为一个广泛的清单。其他主题,如使用 Docker 的容器化和使用 FastAPI 的 REST-API 开发也是重要的主题。然而,以上四个主题是数据科学家需要掌握的最基本的软件开发技能。
举例来说,一旦你决定使用 FastAPI 进行 API 开发,了解如何编写高质量的代码将会对你有所帮助。同样,熟悉命令行会让 Docker 之类的技术变得不那么可怕。
软件开发技能如何指导决策
软件开发技能如何帮助你在数据科学团队中做出重要决策?这里有三个取自现实世界的例子:
- 一位同事提到了一款很酷的新产品,该产品在 Steriods 上提供 Jupyter 笔记本。向 Jupyter 笔记本问好,它带有额外的小部件和 GUI 菜单,用于开发机器学习模型。惊艳!然而,你意识到新的笔记本文件是不可能测试和版本控制的。你意识到这一点的原因是因为你知道这些话题的基础。你的团队应该采用这个新工具吗?大概不会。缺乏版本控制和测试是大多数严肃任务的绊脚石。
- 有时,数据科学家会在技术和非技术面试中面试新团队成员。假设您正在为您的团队招聘一名软件开发人员、测试人员或 MLOps 从业人员。没有软件开发经验会使评估候选人更加困难。没有软件开发技能,你甚至可能最终给候选人一个不相关的技术面试。
- 许多机器学习工具开始从用户那里获取软件开发技能。看一下 MLFlow ,这是一个追踪机器学习模型(以及包装模型等其他东西)的流行工具。甚至 MLFlow 的 QuickStart 也包含 git 命令和 curl 之类的终端命令!
包扎

我希望我已经使你相信软件工程实践对于一个数据科学家来说是非常有用的!
喜欢我的写作吗?查看我的其他帖子,了解更多 Python 内容:
- 用漂亮的类型提示使你罪恶的 Python 代码现代化
- 用 Python 可视化缺失值非常简单
- 使用 PyOD 在 Python 中引入异常/离群点检测🔥
- 5 个超赞的数字功能,能在紧要关头拯救你
- 5 个专家提示,让你的 Python 字典技能突飞猛进🚀
如果你对数据科学、编程或任何介于两者之间的东西感兴趣,那么请随意在 LinkedIn 上加我,并向✋问好
为什么类似 SQL 的接口对于分布式计算来说是次优的
检查 SQL 接口的局限性
这是我们最近的 Spark Data + AI Sumit talk 的书面版本。

柴犬驾驶飞机——作者图片
分布式计算的类 SQL 框架
在我们的上一篇文章中,我们讨论了使用 Pandas 接口进行分布式计算的局限性。有些人很快就认为我们支持 SQL,但这也不完全正确。在这里,我们将了解传统 SQL 以及将其用作大数据工作流语法的难点。对于活跃的 SQL 用户来说,这些都不会太令人惊讶,但是讨论它们将展示使用 SQL 与使用 Python 之间的权衡。
数据社区经常在 SQL 和 Python 之间两极分化。喜欢 Pandas 和 Spark 提供的功能接口的人通常会很快指出 SQL 为什么不能进行更复杂的转换,或者需要更多的代码行。另一方面,SQL 用户发现 SQL 作为一种语言更具表现力。在本文的最后一节,我们将展示这些工具并不相互排斥,我们可以通过赋格无缝地利用它们。
SQL 经常被 Python 代码夹在中间
当我们在本文中谈论 SQL 时,我们指的是像 DuckDB 这样的工具,或者对于大数据,像 SparkSQL 和 dask-sql 这样的工具。最后两个接口允许 SQL 爱好者用类似 SQL 的语言表达计算逻辑,然后在各自的分布式计算引擎(Spark 或 Dask)上运行它。
但是即使这些 SQL 接口存在,它们也经常在 Python 代码之间被调用。以 Spark 的后续文档(也见下图)为例,Python 代码仍然需要执行大量数据帧的转换或加载,以及 SQL 查询后的后处理。这是因为标准 SQL 没有表达分布式计算用户执行的许多操作的语法。目前,SQL 不足以表达端到端的工作流。

来自 Spark 文档的示例
对于主要想使用 SQL 的用户来说,有很多 Python 代码需要理解。SQL 通常被归入工作流的有限部分。我们将通过一个具体的例子更仔细地研究 SQL 的缺点。
示例数据和查询
以下面由多个时间序列组成的数据帧为例,有三列。第一列指的是分组,第二列指的是排序(你可以把它想象成一个 datetime),最后一列指的是关注的值。

基线数据框架—作者提供的图像
以如下所示的查询为例。没必要真的深究和理解。如果这已经令人望而生畏,那是因为对于更复杂的操作来说,SQL 的表达能力更差,更难阅读。我们将在下一部分对其进行分解。

对基线数据帧的 SQL 查询—按作者排序的图像
该块中有 5 条SELECT语句。按顺序,他们做:
- 获得这些值的滚动平均值和滚动标准偏差
- 计算滚动 z 值(有足够预热的记录)并过滤掉空记录
- 根据异常值计数获取排名靠前的时间序列
- 用一个
INNER JOIN获得最差时间序列的完整数据到先前的 z 分数表 - 对最差表中的 z 分值求和
此操作的设计并不完全合理。更重要的是查询和中间表的结构。我们有一个由两个下游表使用的中间表z。这就引出了传统 SQL 的第一个问题。
问题 1:传统的 SQL 缺乏用于分布式计算的语法
上面查询的结构如下所示。桌子z最终被top和worst共同使用。因为分布式计算使用惰性计算,所以只在需要时才计算操作。这样做的一个副作用是,当使用 Spark 或 Dask 时,z可能会被重新计算两次,一次用于top,一次用于worst。

数据帧 z 使用了两次—图片由作者提供
通过在 Spark 数据帧上显式调用.persist()可以避免z的重新计算。但是我们在使用 SparkSQL 接口的时候如何持久化呢?没有PERSIST关键词。我们需要分解 SQL 查询,并在查询的下游部分之前使用 Python 调用 persist 调用。SQL 也没有分组映射语义。
问题是 SQL 没有分布式计算操作的关键字,比如持久化或广播。如果没有必要的语法,查询优化对我们来说就是一个黑盒,结果可能不是最佳的(在我们的例子中,差别是 21 秒对 12 秒)。这表明z花了 9 秒钟来计算,我们可以通过显式使用 persist 调用来删除重复的计算。
缺乏表示这些的语法阻止了我们充分利用分布式计算引擎,除非我们将逻辑带回 Python。
问题 2: SQL 传统上只返回一个表
接下来,一个 SQL 查询与一个返回相关联。它面向单一任务,限制了可能操作的表面积。例如,将一个数据帧分割成两个独立的数据帧通常用于机器学习(训练-测试分割)。如果不将一个查询分解成多个查询,这是不可能的,这会造成一些冗余。
对于熟悉 Python 的人来说,这相当于用一个函数调用返回多个值。

标准 SQL 不支持的语义—按作者分类的图像
问题 3: SQL 引入了大量样板代码
SQL 的另一个缺点是它引入了大量样板代码。上面的查询已经通过使用公共表表达式(cte)写得很好了,它允许从上到下读取。在其他情况下,SQL 从业者通常从内向外编写查询,其中内部查询用于外部“下游”查询。SQL 从业者经常不得不处理长达数百行的查询。
更深入地看这个查询,我们甚至不关心上面查询中的中间表,但是我们无论如何都要给它们命名,以便以后引用它们。大量的样板代码降低了阅读查询所表达的业务逻辑的能力。这增加了维护的开销,尤其是对于没有编写原始查询的人。
问题 4:修改会导致框架锁定
SparkSQL 支持使用修改后的语法读取 parquet 文件。注意,第一个SELECT语句有一个类似于下面的FROM:
FROM parquet.`/tmp/t.parquet`
这实际上是 Spark 特有的语法,有助于 Spark 用户,但它造成了框架锁定。SQL 的一个优点是它无处不在,被广泛采用,但是如果您想使用另一个 SQL 引擎,添加特定的语法会降低可移植性。这是创建框架锁定的 Spark 特定语法的最简单的例子,但是还有很多。
传统的 SQL 很难在大数据上迭代
这些问题的结果是大数据上的 SQL 查询变得难以迭代。痛点被放大了。大数据查询通常需要几个小时,这使得用户必须能够快速、廉价地进行迭代。
在处理大数据时,快速迭代有三个主要障碍。
- 在迭代查询的下游部分之前,我们如何缓存昂贵的中间步骤的结果?
- 我们如何在较小的数据上运行完整的查询来进行测试?然后在准备就绪时将其无缝引入大数据?
- 我们如何在 SQL 语法中保持加载、保存和持久化等操作,以便不需要频繁地将数据提交给 Python?
列表中的第一个包括在代码的 Python 和 SQL 部分之间处理数据帧的技巧,但这仍然是次优的用户体验。像上面这样相对较大的 SQL 查询需要拆分,并用更多的 Python 代码包围起来。我们如何避免这种情况,并将大部分代码保留在 SQL 中?
列表中的最后两个用目前的工具几乎是不可能的。即使 SQL 代码是标准的 SQL 并且跨后端兼容,我们的问题也变成了 Python 代码。同样,SQL 不足以单独表达端到端的工作流。我们求助于编写 PySpark 代码,这是框架锁定的另一个来源。
FugueSQL —用于计算工作流的增强 SQL 界面
FugueSQL 通过扩展标准 SQL 来解决这些问题,使其对计算工作流更具可读性、可移植性和表达性。FugueSQL 是 Fugue 的开源接口,使用户能够在分布式计算引擎上编写端到端的查询。我们可以使用 FugueSQL 将上面的 SQL 重写为下面的形式。

使用 FugueSQL 重写的 SQL —图片由作者提供
FugueSQL 遵循对任何后端都不可知的 SQL 原则;这段代码从任何框架锁定中删除。用户只需指定引擎,就可以在 Pandas 或 Duckdb 到 Spark 或 Dask 之间切换。上面的代码可以在任何后台的赋格支持上运行。
我们将检查上面查询中突出显示的更改:
LOAD现在是一个兼容所有后端的通用操作。FugueSQL 还附带了一个SAVE关键字,它允许用户执行完整的提取-转换-加载(ETL)工作流。FugueSQL 的附加关键字下推到指定的后端。例如,LOAD用 Spark 引擎用 parquet 会翻译成 PySpark 的spark.read.parquet。- 变量赋值减少了大量样板代码。另一个变化是缺少一个明确的
FROM条款。如果没有FROM子句,则自动消耗上一步中的数据帧。 PERSIST关键字下推到后端持久化(本例中是 Spark)。这仅仅通过添加一个关键字就明确地消除了 z 的重新计算。
上面的代码片段是在 Jupyter 笔记本单元格中编写的。与原始查询相比,该查询可以很容易地分成多个单元格(稍作修改)。我们需要做的就是使用YIELD关键字来将数据帧保存在内存中(或者为更大的数据帧归档)。这对于 SQL 用户来说更加自然,因为他们不需要处理 Python 代码来管理内存中的数据帧。

为迭代更改引擎—按作者排序的图片
这张图片中重要的一点是,在 Spark 上运行全部内容之前,我们可以使用 Pandas 或 DuckDB 引擎迭代采样数据。因为有了YIELD LOCAL DATAFRAME语句,所以df作为熊猫数据帧保存在内存中。
虽然本文没有涉及,但 FugueSQL 也能够与 Python 代码交互。从 FugueSQL 调用 Python 函数将在后面的文章中讨论,但是可以在这里找到一个例子。
结论
固守传统的 SQL 使其无法表达端到端的计算工作流,通常需要补充 Python 代码。开发人员的迭代时间很慢,因为对大数据的查询需要一段时间,而运行标准 SQL 需要重新运行所有中间步骤。 FugueSQL 将 SQL 提升为一级语法,允许用户使用 **LOAD, SAVE, PERSIST** 等关键字调用与分布式系统相关的 Python 代码。
FugueSQL 通过以下方式加快大数据迭代速度:
- 允许本地和分布式后端的无缝交换(DuckDB 或 Pandas 到 Spark 或 Dask)。
- 删除标准 SQL 引入的样板代码。
- 添加调用 Python 代码的关键字,允许 SQL 作为与 Python 相对的主要语言。
这些增强允许不太熟悉 Python 的 SQL 爱好者和数据从业者用他们喜欢的语法定义他们的代码。SQL 的优点是易于阅读,而 FugueSQL 的目标是在保持标准 SQL 的直观和表达精神的同时扩展这一点。
编辑:“厂商锁定”改为“框架锁定”,因为 Spark 是开源的。来自 Giles Middleton 的反馈。
资源
为什么创业基础是人工智能战略的关键
原文:https://towardsdatascience.com/why-startup-fundamentals-are-key-to-ai-strategy-76e5a59d9b96
利用“技术创业心态”成功整合商业环境中的数据科学工具

在他的书《从零到一》中,彼得·泰尔探讨了成功的科技创业公司所共有的核心基础。要点:初创公司应该具备七个关键特征——否则,就要挂红旗了。从一名从事数字战略咨询的湾区数据科学家的角度来看,这就是为什么伟大的人工智能创新项目也展示了成功的科技创业公司的基本要素。
这篇文章将探讨科技初创公司和数字化转型之间的相似之处(和一些不同之处),重点是应用人工智能。目标受众包括数据从业者(DS、DE 等。)、技术专家和企业高管——因为技术和非技术人员都与人工智能集成密切相关。
TL;DR:你的应用人工智能项目应该充分回答关于其商业可行性的七个问题,就像科技初创公司一样。
介绍
科技初创公司和数字化转型之间的相似之处数不胜数:首要目标是通过技术的新颖应用创造价值。BCG Gamma 的 Silvio Palumbo 表示,在当今的数字化转型中,各种形状和规模的公司都有可能通过人工智能工具的“智能集成”来找到“竞争优势”[1]。此外,尽管这一趋势在很大程度上是由开源和订阅工具领域的进步推动的,但人工智能创新的商业可行性仍然取决于价值创造基本面、高水平战略和高管的支持。
风险投资基金中的“独角兽”创业公司比基金中所有其他公司的价值总和还要高。类似地,插入独角兽用例的人工智能工具创造的价值超过所有其他选项的总和。
突出的问题变成了:在给定的商业环境下,人工智能集成最有希望的机会是什么?
在这篇博文中,我认为将“技术创业基础”应用到人工智能开发中有助于你的团队在项目的早期和整个过程中关注这个问题。虽然没有灵丹妙药,但这种方法让你可以将人工智能创新项目作为一项商业计划,目标是找到最大的价值创造机会。最终,它让你避免了开发过程中的坏运气(而且,通常有助于赢得高管的认同)。
人工智能入门
一、什么是 AI 和机器学习?虽然你可能听说过人工智能,但让我们清楚地定义它在数字化转型中的实际意义。人工智能是指自动执行预测任务的计算机程序。换句话说,它们是自动数据贴标机。一些常见的例子包括:一条推文的情绪是什么?图像中包含什么?一个短语在另一种语言中是什么意思?对于这样的任务,通常有而不是一组预定义的规则,计算机程序可以用它们来做出好的预测(if-else/and-or 规则是“传统”计算机编程的租户)。相反,今天的大多数人工智能工具依赖机器学习(ML)模型进行预测,这是与计算机科学不同的领域的产物,即统计学习领域。
有关 AI/ML 的更多背景信息,请参见附录部分。
将创业基础映射到人工智能战略
- 注意,所有斜体的引用来自彼得·泰尔的书《从零到一》的第 13 章[2]。
1 —民众提问
“你有合适的团队吗?”
虽然工程印章对于开发智能人工智能工具是必要的,但确保您的数据科学团队能够有效地向业务利益相关者传达结果也同样重要[3]。用正确的心态组建一个高效的团队。
首先,指派一个被授权的利益相关者作为项目经理,一个与高层领导关系密切的人。他们的工作是充当创新团队和高管之间的桥梁,确保开发不会偏离规定的业务路线。
"真正的技术人员穿 t 恤和牛仔裤[而不是西装]"
技术圣人保罗·格拉厄姆强调,公司必须授权他们最好的开发人员以精益的方式创新。要做到这一点,就要让人才与角色相匹配(首先要集合各种各样的人才)。通过这种方式,想法可以相互交流,开发任务可以由最优秀的人来完成。你的顶级沟通者、甲板设计师、机器学习工程师、软件开发人员和数据工程师很可能不是一个人。应用人工智能创新不是一个人的工作——合作是必须的(小组之间不应该有任何交接)。
2 —分配问题
"你有办法不仅创造产品,还能交付产品吗?"
仅仅聚集合适的人才是不够的。你的团队需要执行一个可靠的计划。以下是谷歌首席决策官 Cassie Kozyrkov 如何确保应用人工智能项目在整个生命周期中考虑交付。首先,在一个项目的开始,确保你“现实检查”和“定义你的目标”[4]。不这样做也许是创造没有真正商业价值的解决方案的最简单的方法。假设系统正在生产中,并且正在产生输出。人们如何从预测中受益?大规模集成该工具的价值主张是什么?
您还需要问自己一些现实检查问题:比如更简单的解决方案是否有效,是否有可供学习的数据,您将如何在系统级别进行部署,您是否有项目成本预算,等等。
当在开发的开始定义目标时,再次考虑可操作的产品。为了项目成功,需要达到什么目标?确保在业务术语中清楚地定义目标,高管们会仔细聆听。
"销售和交付产品至少和产品本身一样重要."
AI/ML 的交付归结于生产中模型的部署,它在整个业务的规模上进行预测。在大多数情况下,除了实际预测之外,还有许多因素影响部署(用户界面、分析包装器、可视化包装器、实时主动学习、“人在回路中”决策、监控、单元测试、延迟要求、硬件要求、系统要求等)。).Activision ML 的 Carly Taylor 建议数据从业者在野外预测时考虑以下问题:“什么时候?多久一次?在哪里?你对这些预测做了什么?…你在建造真正有用的东西。我们的[系统级]政策是什么?”[5].在开发之前、期间和之后关注这些问题有助于项目首先有效地创造价值。
3 —工程问题
“公司必须努力提高 10 倍,因为仅仅是渐进式的改进往往最终对最终用户来说没有任何改进。”
寻找突破性技术的机会取决于您和您的创新团队。确保人工智能/人工智能工具能够认真地向最终用户交付价值。用 Cassie Kozyrkov 的话说,应用人工智能工具“不是一次性的”——它们应该为需要“大量标签项目”的重要、频繁的工作流提供价值[4]。
"只有当你的产品好 10 倍时,你才能给顾客提供明显的优势."
如果你创造的技术具有 10 倍的创新潜力,高管们会对投资你的人工智能/人工智能项目更感兴趣——就像硅谷的风投投资者和科技初创公司一样。
4 —秘密问题
“你是否发现了别人看不到的独特机会?”
模型算法通常不是区分 AI/ML 集成领导者的秘密。人工智能中的秘密更经常是数据集和/或用例——这些是你专注于搜索“别人看不到的独特机会”的领域。
"伟大的公司都有秘密:别人看不到的成功的具体原因."
高质量的数据集有许多显著的特征,包括范围、信号、治理和相关性(对于重要的业务工作流)。古老的格言“更多的数据胜过更好的模型,但更好的数据胜过更多的数据”是企业人工智能的核心租户。例如,如果你想开发一个预测客户健康动态(如流失)的 AI/ML 工具,你首先需要投资于以一种全面的方式存储客户事件数据的数据堆栈(参见下面的“时间问题”)。而且,正如在“分布问题”中所讨论的,也许非人工智能的解决方案可以充分地适用于给定的情况。再拿流失预测来说。首先,问你自己(通过挖掘数据):在你的群组分析中,是否有与流失相关的趋势,例如用电量?也许这个简单的规则可以充分预测给定情况下未来的客户流失。或者,也许可以关注其他领先的客户流失指标,如客户投诉,以帮助您获得对未来的初步观点。
在你的企业中,应用人工智能的最佳用例通常已经有了自己的“秘方”。假设您在金融服务行业,并且您的产品在市场上与众不同—您定期执行这些约定。看看 AI/ML 是否能提供绿地价值来扩大你的市场领先地位,在这个过程中扩大你最重要的业务。搜索模型插入良好的用例。这样,你不一定需要专有数据集来创建有价值的人工智能工具。当模型为拥有这种秘方的用例创造价值时,你仍然可以从人工智能集成中获得巨大的回报(不管底层模型有多专有)。
5 —时机问题
"现在是开始你的特殊业务的正确时间吗?"
Monica Rogati 在 Hackernoon 上的博客“人工智能的需求层次”可能是关于人工智能计时的最知名的文章[6]。已经被阅读了近 20 万次。人工智能和深度学习位于“需求层次”金字塔的最顶端。对于人工智能创新团队来说,将你的深度学习模型视为冰山一角是有帮助的,它位于大量数据工程、数据治理、基础设施和数据分析的顶部。在一家公司开始涉足原生数据时,在人工智能/人工智能之前转向分析通常是有意义的。例如,假设你是一家对客户流失分析感兴趣的 SaaS 企业。在添加人工智能工具之前,开发分析解决方案,如指标跟踪和群组分析,以更精简的方式挖掘见解。知道你的 AI 集成项目什么时候不成熟。在文章的最后,罗加蒂谈到了如何将“最小可行产品”(MVP)的精神带到应用人工智能中,同时仍然尊重你的时间和准备:
“数据科学需求层次不是一年内构建互不关联、过度设计的基础架构的借口。就像构建一个传统的 MVP(最小可行产品)一样,你从产品的一个小的垂直部分开始,然后让它端到端地工作。你可以建造它的金字塔,然后横向增长。…我们没有构建一个无所不包的基础架构,也没有端到端地投入使用。”[6].
此外,考虑 AI/ML 解决方案,这些解决方案需要更少的资源来开始 MVP 开发,资源紧张或高管买入率低,例如,在可以使用开源数据集的情况下(想想像情感分析这样的事情)。如果可能的话,在你的后兜里放一个 v0 原型来推销项目是非常有用的。考虑创建一个基线模型,并将其部署在沙盒环境中(以便利益相关者可以看到其潜力)。
如果你是一名高管,和你的技术人员谈谈像罗加蒂的博客。考虑您对工具投资的偏好以及全球相关的时间问题(政府政策变化、隐私法规、客户反馈、市场饱和等)。).
6 —垄断问题
"垄断是每一个成功企业的条件."

对我来说,想到(真正的商业)垄断的第一个想法是:谷歌搜索。“Google”实际上是在线搜索信息的动词。如果你用谷歌搜索所有搜索引擎的全球市场份额,你会发现它超过了 90%。这是整个市场的绝对垄断。而且,如果你是谷歌,在搜索中集成人工智能/人工智能工具是非常有意义的。让我们关注今天搜索中应用人工智能集成的一个线程(在一个庞大的列表中):BERT。2017 年,谷歌研究人员发表了《注意力是你需要的全部》,介绍了神经网络模型的变压器架构。快进到 2019 年,谷歌的一篇博客文章断言,整合 BERT 模型(Transformer 模型的自然语言迭代)导致了“过去五年中最大的飞跃,也是搜索史上最大的飞跃之一”[7]。该吸取的教训?在你业务的最强部分附加伟大的人工智能/人工智能工具。
“如果一个子市场是虚构的,你就无法主导它,而巨大的市场是高度竞争的,不是很容易达到的。……夸大自己的独特性是解决垄断问题的一个简单方法。”
现在,让我们看看另一个谷歌产品:合同文件。这个数字 AI/ML 工具使用户能够更好地理解他们的合同。功能包括法律实体提取(显示重要的合同信息)和光学字符识别。事实上,Gartner 在“Forrester Wave:面向文档的文本分析平台,Q2 2022”中为谷歌加冕市场领导者。考虑一下你是否认为 Google Contract DocAI 是垄断?
让我们先问问自己,这项 SaaS 技术的整个市场是什么。是 Forrester 确定的“面向文档的文本分析平台”吗?亚马逊和微软都是解决方案非常相似的直接竞争对手。是不是所有合同文件的软件工具?事实证明,合同人工智能有大量的姐妹行业,如合同生命周期管理(例如,参见 CLM 文档)。是应用 AI 领域的任何及全部 NLP 工具吗?所有 AI/ML 工具?如你所见,很难定义这种产品的全球市场。当整个市场还不庞大时(理想情况下,还具有增长潜力),垄断最容易形成。记住,“夸大你自己的独特性是一个容易搞糟垄断问题的方法。”
7 —耐久性问题
"未来 10 年或 20 年,你的市场地位还能守住吗?"
“什么能阻止[x]毁掉我的生意?”
从你的人工智能集成中获得的巨大回报是以未来价值来衡量的。毕竟,公司的估值是基于它们未来的现金流。在应用人工智能领域,工具化进展迅速。没有办法确保任何一个解决方案能够“在未来 10 年和 20 年内都是可防御的”在这个领域,最先进技术的边界似乎每隔几个月就会扩展一次。根据你的人工智能策略本身来表述“耐久性问题”更重要。从长远来看,遵循一个关于价值创造基础和强大的业务规划的合理的高层次战略,可以确保你的 AI/ML 集成项目的持久性。正如“人的问题”所表明的,你需要合适的内部数据科学家团队,或者合适的数据科学顾问合同,以成功实现应用人工智能集成。您的数据科学团队的质量本身就是“耐用性问题”的一个因素,也是您的产品在市场上的差异化程度。
结论
在 AI/ML 项目开发之前、期间和之后,数据科学家和高管都需要考虑价值创造。在整个开发过程中保持技术创业心态对于实现独角兽价值至关重要,通过这种方式,数据科学家和企业高管可以从新技术企业中学习。关于人工智能策略的更多阅读,请参见 Cassie Kozyrkov 关于应用人工智能路线图的帖子[4],Silvio Palumbo 关于人工智能集成的博客[1],以及引文中链接的所有资源。成为 AI/ML 社区的一员是一个激动人心的时刻!感谢阅读。
引文
[1] S. Palumbo,智能集成:人工智能成熟度的四个级别,以及为什么可以达到第三级 (2022),Gamma-BCG X 的一部分
[2]彼得·布莱克。《从零到一:创业笔记,或如何打造未来》(2014 年),纽约皇冠商务(印刷版)
[3] S. Berinato,数据科学和说服的艺术:组织努力传达他们收集的所有信息中的洞察力。下面是原因,以及如何修复它。 (2019),《哈佛商业评论》
[4] C. Kozyrkov,应用人工智能的 12 个步骤:每个机器学习项目的路线图 (2019),初创公司
[5] C. Taylor,D. Liu,利用人工智能解决游戏中的破坏性行为——卡莉·泰勒——数据科学家秀 045 (2021),数据科学家秀
[6] M. Rogati,人工智能的需求层次 (2017),Hackernoon
[7] P. Nayak,理解搜索比以往任何时候都更好 (2019),关键词。
附录:AI/ML 工具的背景
今天最先进的机器学习模型基于历史数据中的模式进行预测。这些模式对应于模型特征(数据集)和结果(标签预测)之间的相关性,并且特定于所采用的训练数据集。谷歌的 Cassie Kozykrov 将训练机器学习模型比作厨师学习菜谱。这是一个很好的类比:在 Twitter 情绪模型的情况下,该模型遵循一个配方,它“学习”了推文中的积极与消极,这是基于推动手头历史推文中情绪的因素。
ML 模型实际上只能够在新数据上做出合理的预测,当事情非常类似于它被训练的数据集时。这个“泛化问题”是当今人工智能和机器学习最具影响力、最普遍、最受关注的约束之一。结果:训练数据集的重要性不能被夸大。你需要大量的数据集来开发人工智能工具。在我们工具化的世界中,大多数玩家都可以访问相同类型的模型。常见的例子包括 XGBoost(一种基于树的表格数据架构)和 Transformers(一种用于自然语言和计算机视觉的神经网络架构)。这两种模型几乎都被企业数据科学中的每个参与者使用。有趣的是,世界各地的微软开发的大多数最先进、真正庞大的模型(那些在基准排行榜上名列前茅的模型,如用于自然语言处理的强力胶)在延迟方面不够有效,无法部署在大多数商业环境中。因此,即使这些专有模型可能看起来更好,如果你看一下基准排行榜,它们也不足以在野外扩展。在深度学习工具的情况下,通常轻量级架构(如开源的 distil Transformer)可以有效地完成这项工作。
为什么统计显著性会毁了你的 AB 测试
原文:https://towardsdatascience.com/why-statistical-significance-is-ruining-your-ab-tests-8f1f33d7810a
如何加倍你的实验率和加倍你的实验产出
AB 测试中的统计显著性被广泛认为是决定何时停止实验以及最终是否在 AB 测试后推出新功能的基准统计。然而,等待收集足够的数据来满足统计显著性可能会损害您推出新功能的速度。

由于所需的数据量,对 KPI 影响较小的新功能在实际时间内可能不会达到统计显著性。因此,你很可能会浪费时间开发新的功能,结果在实验后因为它们的影响小而被拒绝。或者也许你在构思阶段就拒绝了一个想法,因为你等不及证明它的价值。
我的观点是,如果这个新特性可能只会带来性能的小幅提升,那么它也不太可能导致性能的大幅下降。
如果潜在影响较小,我们可以做出更高风险的决策,而这些较小影响的特征可能是最多的,因此累积起来会产生较大的影响。
在这篇文章中,我将讨论如何从统计显著性切换到 损失 作为你的实验停止标准,可以让你的实验率加倍,从而使你的实验产出加倍。
蒙特卡洛模拟设置
在这篇文章中,我通过蒙特卡洛模拟生成了一些合成的但真实的数据。我已经产生了 1000 个独立实验的结果。
1000 个实验中的每一个都有:
- 对照的转化率为 5%
- 从平均值为 0、标准偏差为 2.5 的正态分布中得出的变量转换率的上升
通过选择均值为 0 的正态分布,我们可以确保所有实验的净改善为零——如果我们在分析中看到改善,那是因为我们推出了更多正面改善多于负面改善的实验。我们还将生成许多影响较小的实验——影响较大的实验太容易分析,会对我的分析产生有利的偏见。2.5 的标准偏差确保 95%的所有实验都有-5%到 5%之间的提升。
对于每个实验,我已经计算出了达到 95%的 击败对照 的机会所需的样本量。这个样本量代表实验的结束。在每次实验结束时,我还会计算选择变体 的 损失或风险。
结果
下面是我们每个实验的损失的直方图,按照上升是正还是负来划分。我们可以清楚地看到,负上升(即变体比对照差)的实验具有更大的损失,而正上升(即变体比对照好)的实验具有的损失大多低于 0.5%。

图一
很明显,有非常有力的证据表明,实验的损失可以非常准确地确定一个新特性对我们的 KPI 有正面还是负面的影响。
根据损失对影响进行分类
让我们看看在实验中使用损失可以准确预测积极或消极影响的程度。为此,我们将使用阈值损失建立一个非常简单的分类器, l :
- 损失 < l :正面影响
- 损失 >损失损失:负面影响
在下图中,我画出了我们的分类器对于 l 值范围的精确度和召回率。

图 2
随着阈值损失的增加(向右移动),我们将越来越多的实验归类为具有积极影响。由于我们大多数正面影响实验的损失低于 0.5%(图 1),因此我们在这一点上达到了 100%的召回率。但是,我们也有几个负面影响实验有非常小的损失。随着分类器中的 l 增加,我们开始将一些负面影响实验误分类为正面影响,因为它们的损失很小。因此,我们开始看到精度下降。
然而,像这样的情节是数据科学家的梦想——准确率和召回率都在 95%以上,损失阈值为 l ~ 0.5%。
结论…到目前为止
让我们回顾一下到目前为止我们所学的内容。如果我们运行一个实验,直到我们达到统计显著性(击败对照的机会为 95%),那么损失将为:
- 小(<0.5%) for the experiments with a positive impact
- large (> 0.5%)用于有负面影响的实验
如果我们推出所有损失小于 0.5%的实验,那么我们将推出几乎所有具有积极影响的实验,并排除大多数具有消极影响的实验。
此时一个明显的问题是:为什么?我们已经达到了统计显著性,为什么还要计算损失和在我们的推广中犯潜在的错误呢?继续阅读寻找答案——我保证疯狂是有方法的!
样本量减半
在本节中,我们将探讨在比统计显著性所需样本量更小的样本量下损失的相关性。
让我们通过实验中点的损耗来定义 中点损耗——当观众是统计显著性所需的一半时
下面是实验结束时的损失(当样本量足够大时,具有统计学意义)与中途的损失(中点损失)的散点图。

图 3
有趣的是:
中点损失与实验结束时的损失非常相关——在实验进行到一半时,我们可以高度准确地预测实验结束时的损失会是多少
中点损失是实验结束时损失的重要预测值,实验结束时损失是上升的重要预测值。因此,我们可以通过计算中点损失来有效预测实验中途的结果。
根据中点损失对影响进行分类
现在,让我们来看看在使用中点损失的实验中,我们能准确预测正面或负面影响的程度。同样,我们将使用阈值损失构建一个非常简单的分类器

图 4
除了精确度和召回率稍低之外,精确度-召回率图在性质上类似于图 2。这里用作阈值的最佳中点损失现在是 0.6% —略高于实验结束时的损失。这并不奇怪,因为我们只有一半的样本量,因此对结果不太有信心。
总隆起
虽然精确度和召回率很有趣,但它们并不那么重要——这里的北极星指标是我们所有推出的功能的综合影响。为此,我将计算提升比:
上升率 是所有铺开实验的上升总和,占所有正上升实验总和的%
此度量具有以下属性:
- 如果我们推出所有正上升的实验,而没有一个负上升的实验,那么我们将得到 100%的上升率
- 如果我们错误地推出了一些负面实验,那么提升率将会下降到 100%以下
- 如果我们因失误而未能推出一些积极的实验,那么上升率也将下降到 100%以下
我们希望最大化提升率。
下面是覆盖在我们的中点损失精确召回图上的上升率的曲线图。

图 5
我们看到的是,当损失阈值约为 0.6%时,提升比率达到峰值(~97%),这是精确度和召回率最高的地方。在较低的 l 值下,我们的召回率非常低——我们无法识别和推出积极的实验。高于 0.6%时,精度开始下降——我们推出了太多的功能,但由于失误而产生了负面影响。
如果我们选择在中点损失小于 0.6%时推出所有实验,那么我们将实现约 97%的提升率。这里非常关键的一点是,我们使用中点损失作为我们的止损标准。
我们从实验中获得了 97%的性能,但是它们只运行了一半的时间。每个实验的持续时间减半意味着我们可以在同一时期运行两倍的实验,并提供 194%的提升率——几乎是等待实验达到统计显著性的性能提升的两倍。
结论
我们已经证明,我们可以根据中点损失近乎完美地预测一个实验会带来正面还是负面的提升。通过采用中点损失阈值标准来停止您的实验,您可以将所需的样本量减半,并将这些实验的持续时间减半——让您有时间运行两倍的实验,并实现两倍的提升。
评论
值得注意的是对所展示的模拟的一些评论:
- 5%的基线转换率非常小,这是特意选择的,因为较大的基线转换率更快达到统计显著性。
- 同样,我们的大多数实验都有-5%到 5%之间的提升,这也是较小的一面,因为当考虑统计显著性时,这也可能使这些实验不确定。
当您试图检测小转换率中的小变化时,选择以上因素来突出该方法的有效性。对于更大的影响实验或更大的基线转化率,你通常不会遇到实验持续时间达到统计显著性的问题。
另一个要考虑的因素是开展实验所需的工程时间。虽然这里介绍的方法可能允许您检测小的影响实验,但您需要相当多的影响实验才能产生有意义的结果。这将非常适合对您的产品进行小的更改,例如按钮颜色和副本的更改。
Git 回购
用于模拟的代码都可以在git repo中找到。
为什么要用 python 同步你的 Jupyter 笔记本(。py)文件?
对于 Jupyter 笔记本来说,这是自动化 git 差异的最简单的方法

尼尔和祖玛·斯科特在 Unsplash 上的照片
什么是 Jupytext?
Jupytext 已经存在几年了,有一些非常有用的指南可以分享 jupytext 能为你做什么。例如, readthedocs 页面分享了多个安装和配置技巧,并链接回 Marc Wouts 发布 jupytext 的关于数据科学的文章。
简单地说,jupytext 是一种自动复制不同格式文件的方法。Jupytext 不局限于简单的。ipynb '和'。下面分享的 py 例子。
一个常见的用例
在一个团队中开发 Jupyter 笔记本时,与 git 的区别可能很难理解,或者至少比较 Jupyter 笔记本结构中不必要的项目是一件麻烦的事情。有些资源,比如 ide 的附件,甚至是你可以授权的软件,可以为 Jupyter 笔记本提供更好的区别。
然而,一旦我学会了用 Jupytext 自动同步到 python 文件,我就知道这会导致我以最简单的方式寻找的 git 差异。
如何进行最简单的 Jupytext 设置
首先,安装 jupytext
pip install jupytext --upgrade
接下来,启动 Jupyter 笔记本服务器并打开笔记本。在 Jupyter 笔记本中,点击 编辑>编辑笔记本元数据

编辑>编辑笔记本元数据(按作者分类的图像)
在元数据的顶部,添加下面的代码(或者类似的一行程序,因为它们在保存时会呈现相同的输出)。点击右下角的编辑按钮进行保存。不要太担心位置——这是否是第一个片段,等等。-因为订单会在关闭时自动重新订购。您可以重新打开元数据设置来验证您的更新是否已保存。
"jupytext": {
"formats": "ipynb,py"
},

将 jupytext 片段拖放到元数据中(按作者排序的图像)

点击右下角的编辑按钮保存并关闭(图片由作者提供)
关闭 jupyter 笔记本时,会创建一个 .py 文件进行匹配。

瞧,现在有一个。与同步的 py 文件。“ipynb”文件(图片由作者提供)
就是这些了
无论何时编辑其中一个文件,另一个文件都会被更新。您可能需要刷新浏览器或 IDE 才能看到更新。
在中检查。py' 文件进行代码版本控制,如 git 或 svn,请放心,您离 Jupyter 笔记本天堂又近了一步。
如果你喜欢这篇文章,并且想读(写)更多这样的文章,请考虑使用我的推荐链接成为中等会员:【https://sabolch-horvat.medium.com/membership
为什么文本摘要仍然很难
原文:https://towardsdatascience.com/why-text-summarization-is-still-hard-39e5559db86
以及这如何给创业公司带来机会
在所有自然语言处理(NLP)任务中,摘要可以说是最不值得上头条的任务之一。缩减一篇文章的内容比让 GPT-3 自动产生创业想法要少得多。然而,尽管文本摘要比较低调,但它还远远没有得到解决,尤其是在工业领域。像微软这样的大公司提供的基本 API 为小公司从各种角度处理摘要留下了足够的空间,目前还没有明显的赢家。本文讨论了为什么文本摘要仍然是一个挑战的原因。

摘要不仅仅是文本到文本
很简单,摘要可以被看作是通过有损压缩进行的文本到文本的转换。实际上,摘要是一个(文本+上下文)到文本的问题。
摘要将较长的文本缩减为较短的文本,同时在这个过程中丢弃不太重要的信息。但是什么重要?做出一个普遍的判断是非常困难的,因为答案高度依赖于文本的领域、目标读者和摘要本身的目标。例如,考虑一篇关于新冠肺炎的科学论文。摘要应该包含任何生物学术语,还是应该让外行人也能理解?它应该是一个主要事实发现的干巴巴的列表,还是为了说服用户阅读整篇文章而变得爽快和有悬念?
换句话说,什么是好的总结取决于上下文。摘要不仅仅是文本到文本的转换,而是一个文本到文本的问题。像微软的 Azure 认知服务这样的通用摘要 API 遵循了天真的文本到文本的 T21 定义。除了期望的摘要长度之外,它们不允许关于期望输出的任何规范。这就是它们在现实应用中失败的原因,在现实应用中,上下文的细微差别可以成就或摧毁一个产品。
提取是幼稚的,抽象是不根植的
在摘要领域,有一个既定的二分法:摘要可以是提取的(即来自原始文本的片段),或者是抽象的(即新生成的文本)。摘录摘要定位原文中的关键句子,因此倾向于准确地反映主要思想(除非句子被恶意地精挑细选,以歪曲它的方式)。不利的一面是摘录摘要在完全抓住内容的能力上受到限制——被删除的句子将永远丢失。
另一方面,抽象总结更接近于模仿人类总结的方式:我们把旨在讲述整个故事的新句子放在一起,但是是从更高层次的角度。然而,抽象的摘要在技术上很难产生,因为它们需要生成模型,如 GPT 家族。目前,此类模型遭受幻觉— 它们的输出可能与事实不符和/或不受原文支持。这对于摘要来说是一个巨大的障碍,因为忠实于输入文本是不可避免的。虽然防止幻觉是一个活跃的研究领域,但没有保证一定质量的防弹解决方案。这就是为什么谷歌的抽象摘要 API 仍然是实验性的(即,它不像通过谷歌云平台提供的产品那样得到官方支持)。
NLP 仍然在长文档中挣扎
根据定义,摘要假设一个长的输入文本;否则,一开始就不需要它。然而,NLP 领域仍然难以处理相当大的文档。
主要的模型架构是转换器,它强制输入令牌的最大数量在几千以内。任何超过这个令牌数的文档都需要被分割成片段,分别进行汇总。最终结果仅仅是独立子摘要的拼接。对于某些文档类型,比如新闻文章,我们可能会使用这种技术,因为新闻文章可以自然地分成几个独立的部分。然而,当应用于小说时,它就变得不可用了,因为小说在设计上包含了复杂的章节间依赖关系。例如,一个人物的拱形常常横跨整本书;由于子概要是简单地连接在一起的(并且彼此都不知道),所以概要中的角色的轨迹也会被分割。没有一句话能简洁地描述他们的旅程。
公司如何应对总结
由于没有通用的摘要解决方案,各公司根据目标文档的性质,以不同的方式进行处理,从手动到全自动。
人工生成的图书摘要
一方面,像 Blinkist 和12 分钟这样的公司雇佣人类专家为非小说类书籍制作高质量的摘要,这些摘要可以在 15 分钟内阅读完。虽然这种方法确实确保了高质量的内容,但它并没有超出人类策划的畅销书名单,所以如果你的阅读品味与众不同,这种方法就行不通。
中型内容的自动化摘要
为博客帖子、新闻文章、研究论文或内部公司文档等中等大小的内容制作摘要更适合自动化处理,但仍然很费力。由输入域定义的每个用例(例如,新闻、法律、医疗等。)和输出格式(要点、亮点、单段摘要等)。)需要单独的训练数据集,并且可能需要单独训练的模型。
Google AI 最近发布的一篇博客宣布了 Google Docs 中自动生成摘要的新功能,为收集干净的训练集提供了理由,这些训练集专注于特定的输入领域,并在收集的摘要中强制执行一致的风格:
[……]早期版本的[摘要]语料库存在不一致和高度变化的问题,因为它们包括多种类型的文档,以及多种撰写摘要的方式,例如,学术摘要通常冗长而详细,而执行摘要则简短而有力。这导致了一个很容易混淆的模型,因为它已经在如此多不同类型的文档和摘要上接受了训练,以至于它很难学习它们之间的关系。[……]我们仔细清理和过滤了微调数据,以包含更加一致的训练示例,并代表了摘要的一致定义。尽管我们减少了训练数据的数量,但这导致了更高质量的模型。(摘自谷歌人工智能博客)
与上面自己的建议一致,谷歌为它的实验性抽象概括 API 提供了不同的端点,每个端点都专注于一个相当狭窄的应用:

由谷歌的实验抽象摘要 API 提供的独立摘要模型(目前在私人访问下)。
创业案例研究:总结
(本文不是由 Summari 或任何其他提到的产品赞助的。)
缺乏一个可以在任何环境下执行摘要的单一模型为创业公司提供了一个巨大的机会;他们可以专注于对大型科技公司不太有吸引力的利基市场。
例如,summary开始着手一项任务,帮助互联网消费者在进行端到端阅读之前筛选文章。在早些时候的一次采访中,这位创始人表达了对最先进的摘要技术的失望,最初选择了人工生成的摘要:
不幸的是,我们没有从人工智能技术中获得我们想要的质量类型。我们相信伟大的总结是有艺术的,它不仅仅是从文本中复制精选的短语,还需要更深层次的理解,这需要一个人,至少现在是这样。( Ed Shrager ,Summari 的创始人,在奈斯实验室的采访中)
快进大约一年,Summari 现在提供了一个 Chrome 扩展,可以为几乎所有有文本内容的网站制作高亮显示。毫无疑问,人类策划的摘要相当于一个干净的训练集,使他们能够建立一个模型,自动化和扩展他们的原始任务。这里的是到目前为止他们对这篇文章的总结。
文本之外:音频和视频摘要
与音频和视频相比,文本可以说是最简单的总结形式。毫不奇怪,音频和视频的最新模型和行业实践落后于文本。
例如,在缩短播客的过程中几乎没有自动化。对于播客来说,通常的做法是发布从他们的完整剧集中剪辑出来的 YouTube 短片(例如,见乔·罗根的频道)。这是手动的提取汇总。相比之下, Blinkist 直接与播客创作者合作,额外制作他们剧集的较短版本,他们称之为“短播”——相当于抽象摘要的人工版本。
然而,有些自动化是可以预见的。像 Snackable 这样的初创公司旨在从音频和视频文件中自动提取并拼接关键片段,目前是以纯粹的提取方式。随着视频处理和生成技术的进步,抽象概括成为可能只是时间问题。
结论
总结文本是一项艰巨的任务,因为它高度依赖于上下文。正因为如此,我们不太可能趋同于一个单一的普遍解决方案,也不太可能依赖万能的 GPT 模型为每一种情况做出正确的总结。这种分散的格局为创业公司提供了一个机会,可以投资于专注于非常具体的用例的干净的培训集,并补充大型技术的产品。
特别感谢 Gaurav Nemade 与我分享他一贯深思熟虑的观点。
种子主题模型:用 keyATM 在 R 中实现它们
基于英国议会记录的地缘政治风险建模

作者图片
在文本分析中,主题模型是从大量文档中提取主题的重要方法。也许在这个领域中最广泛使用的模型是潜在狄利克雷分配(LDA) 。
LDA 是一种概率主题模型,存在多种变体。今天我们来看看其中的一个:种子 LDA 模型。
种子主题模型允许研究者将一组关键词传递给模型,该模型在评估之前概述了所寻找的主题。这种播种提供了对评估过程的更多控制,并且也导致了结果的【更好的】可解释性。
在我最近的硕士论文中,我探讨了如何基于文本数据来度量地缘政治风险的抽象概念。为此,我使用了过去 200 年英国下议院议会辩论的记录,并应用了种子话题模型。
数据科学可以帮助提供新的测量工具,并使我们能够将新的或者更精确的变量纳入经济计量模型 。
在我的申请中,这意味着用一个种子 LDA 主题模型来衡量地缘政治风险,并将这一衡量结果插入一个向量自回归。
我想给你一个种子 LDA 模型的概述,并提供一些基本设置的代码片段。
为什么是艾达?
LDA 模型假设文档由不同主题的混合组成,并且这些主题中的每一个都是单词的混合。
您可以通过多种方式扩展 LDA 模型。例如,您可以在文档中加入元信息,如作者或发布时间。
对我来说,动态种子 LDA 模型似乎足够了。因为它是一个动态的模型,你的主题可以随着时间的推移而发展,创造历史的线索,而不是快照。
在过去的 200 年里,世界发生了巨大的变化。与 2010 年的辩论相比,冷战、核武器的发明或语言的变化等发展可能会改变 1810 年的辩论。
为什么播种 LDA?
解释常规 LDA 模型发现的主题的诱惑是巨大的。
一个包含诸如战争、军事或武器等词汇的话题,必须是关于地缘政治风险的——对吗?
不对。至少在严格的意义上。
如果你想衡量一个概念,重要的是要有一个工具来传递客观可解释的主题。
在这种情况下,目标意味着,在实际评估您的模型之前,您需要通过概念的明确定义。否则,你会发现自己事后挑剔,做出主观的解读。
把这比作向你的朋友证明你可以用篮球打半场球:为了做好这件事,你必须先宣布投篮,然后得分。而不是打一枪,事后大喊“我早告诉你了”。****
工具箱:Quanteda 和 keyATM
我推荐用 R 来处理种子 LDA,主要是因为有库[quanteda](https://quanteda.io/)(数据清理)和[keyATM](https://keyatm.github.io/keyATM/index.html)(种子 LDA 模型)。
我想向您简要介绍一下计算动态种子 LDA 模型所需的步骤。
有关更深入的解释,请分别访问 quanteda 和 keyATM 的文档。
预处理数据
首先,需要对数据进行预处理。为此,我们首先创建一个语料库。
第二步,我们可以用函数tokens预处理这个语料库,并用keyATM_read将其转换成适合 keyATM 模型的格式。
对于动态植入 LDA 模型,有必要创建一个周期变量,该变量从 1 开始,每个周期递增 1。在下面的代码中,一个周期反映了从 1805 年开始的 5 年。
关键词词典
我们用一个关键字字典作为主题模型的种子。
每个主题可以传递任意多的关键字。然而,模型的拟合度很大程度上取决于你的关键词的质量。例如,好的关键字出现在许多文档中,并且是感兴趣的概念所特有的。****
指定和评估模型
现在,我们可以为我们的模型指定一些超参数。同样,要获得更深入的解释,请访问上面提到的文档。
请注意,考虑到no_keyword_topics是很重要的,因为它使模型也能够自由探索数据中的模式。
最后,我们可以将所有这些组件作为参数组合在keyATM中来评估我们的模型。
重要的是要记住,估计可能会花费很长时间,并且可能需要大量资源。除其他因素外,计算成本还取决于文档的数量和长度。
例子:用辩论记录衡量地缘政治风险
在评估之后,我们应该以整体的方式评估模型,即以不同的方式评估拟合的质量。
例如,我们可以查看我们的结果主题。我们用函数top_words得到一个主题中最“重要”的词,正如我们的模型所估计的那样。
对于我的应用程序,像军事、国防或陆军这样的词是代表地缘政治风险(话题战争)的话题的顶级词。请注意,我们如何以相同的估计得到财政、教育和和平概念的主题。通过在关键字字典中指定更多的主题,我们可以一次获得多个种子主题:虽然计算时间略有增加,但我们可以“几乎”免费获得额外的度量。

作者图片
对于动态主题模型,您还希望绘制出主题在一段时间内的发展情况。下图显示了一段时间内辩论中地缘政治风险的估计份额。阴影区域代表英国的军事冲突时期。

作者图片
您还可以看到所有主题的份额随时间的发展。所有非关键词话题的分享都被聚集到下面的白色区域。

作者图片
虽然其中一些主题的时间趋势似乎是合理的,如第一次世界大战前“殖民地”主题的重要性,但也有一些主题不太适合,如和平主题。
此外,这些话题相对于彼此的重要性可能令人难以置信,因为很难相信“战争”这个话题在第二次世界大战期间只占话语的 15%左右。
然而,种子主题模型是基于非结构化数据(例如文本)创建度量的令人兴奋的工具。在未来,重要的是加入更多的元数据,并改进种子关键词。在我的情况下,这可能意味着在模型中包括一个讨论者的政治倾向,或者使我的关键字字典更精确地适应这个概念。
什么时候预先训练你自己的 Transformer 语言模型才有意义?
预先训练你自己的模型有什么缺陷、好处和步骤,以及现有 PLM 的局限性?

图片由 DALL-E 生成,带有作者提示。
这篇博文是写给谁的,对这篇博文有什么期待?
这篇博文的目的是讨论如何使用预训练语言模型(PLM)来创建自然语言处理(NLP)产品,以及使用它们的优缺点。将讨论从头开始训练您自己的变压器模型。介绍了从头开始的预培训的高级益处和缺陷。该内容面向新的和有经验的 NLP 工程师,也面向参与创建涉及 NLP 的产品的其他专业人员。
什么是预训练的变压器语言模型,为什么这么受欢迎?
PLM 是使用自我监督的预训练任务对大量数据进行预训练的大型模型。Transformer 模型是 PLM 的一种特殊类型,它基于[0]中介绍的自我关注机制。变形金刚模型有很多变体,很多可以在拥抱脸(HF) [1]上找到。HF 是一个开源平台,工业和学术研究人员可以上传和下载模型。从零开始训练这些庞大的模型通常是由非常大的公司如谷歌、脸书和 OpenAI 或研究机构如艾伦人工智能研究所完成的。随着拥抱脸的兴起,许多没有资源或知识来创建这样一个模型的工程师已经可以使用它们了。采用 PLM 并将其应用于新问题是迁移学习的一种形式。
BERT [2]是 NLP 从业者中最著名的 PLM 之一,于 2018 年发布。然而,对大多数人来说,GPT-3 [3]可能是最广为人知的模型。这两种模型都基于变压器架构[0]。开源变压器模型在 NLP 产品和研究中已经变得无处不在。只需几行代码,使用 HF 开发的 transformers 库[1],就可以将预先训练好的 Transformer 模型应用到您的新任务中。
在 Slimmer AI,一个人工智能创业工作室,我们(共同)在我们认为人工智能可能具有颠覆性的领域建立企业。虽然建立合资企业是我们工作的一大部分,但我们也通过应用我们的人工智能专业知识来支持科学出版过程。对于这些产品,我们一直在寻找提高性能的方法。我们用预先训练好的变压器模型进行实验,看看它们是否适合某个产品。然而,由于交付期限的原因,我们并不总是有空间在一个项目中试验或尝试全新的东西。出于这个原因,Slimmer AI 引入了 AI Fellowship,ML 工程师花时间来提高他们的技能并了解最新的发展。这使得我们能够比日常的产品构建工作更深入地研究主题。点击这里阅读更多关于我们人工智能联盟的信息。从头开始训练你自己的 Transformer 语言模型是一个大项目,而不是我们在构建一个单独的产品时会做的事情。在 AI Fellowship 中,我们探索并实验训练我们自己的语言模型。
读完这篇博文后,你会了解到:
- 什么是预训练的变压器模型以及在哪里可以找到它们,更具体地说,您将了解 BERT [2]和 SciBERT [4]。
- 一个预先训练的模型在哪些方面拥有领域知识,以及你可以做些什么来使一个模型适应你的领域。
- 为什么应用这些模型会受到这些模型局限性的约束,它们的局限性是什么。
- 如何在不预先训练您自己的模型的情况下克服这些限制。
- 从头开始预先训练你自己的模型有什么好处。
- 当(考虑)训练你自己的基于 Transformer 的语言模型时,你应该考虑什么。
为什么产品生命周期管理不是灵丹妙药:SciBERT 案例研究
当考虑变压器产品生命周期时,作为 ML 工程师,我们主要关注两点:
1)模型的架构:层数、参数、注意机制、预训练任务等。
2)用于训练模型的数据的域。
作为一名应用 ML 工程师,关键技能之一是识别你正在处理的问题和领域。如果您正确地识别了问题,您就可以有效地搜索和应用您能找到的最相关的工具。例如,如果您正在处理法律数据,应用基于英语法律文本训练的模型比基于普通英语维基百科训练的模型更有可能给你带来更好的结果。
按照这种思路,BERT 是一个通用模型,因为它不是在特定的数据领域中训练出来的。它在来自图书语料库数据集和英语维基百科的通用英语文本上进行训练。另一方面,SciBERT [4]是一个专门的模型,它是在来自语义学者语料库的科学论文上训练的。因此,将这种模式应用到你为科学出版业创造的所有产品中是一个合乎逻辑的选择。问题解决了对吗?不完全是…
2019 年发布的 SciBERT 是一个强大的模型,它是为科学领域量身定制的,具有惊人的功能,今天仍然是一个强大的模型。在科学领域也有其他模型,如 BioBERT [5]或 SPECTER [6],但在这篇博客文章中,我们将坚持讨论 SciBERT [4]。在科学出版行业构建产品时使用 SciBERT [4]的主要缺点是:
- 速度。它需要一个快速的 GPU,这可能是昂贵的。
- 表演。它的性能并不总是比一些更轻便的型号好多少。这就是为什么对于你开发的每一个产品,你都需要一个更简单的基线,比如 word 2 vec[8]embeddeds,看看你的 PLM 是否明显优于它。
- 输入限制。它可以处理的最大序列长度是 512 个令牌。
- 域。虽然 SciBERT 是一个在学术论文上训练的模型,但是这些论文的领域并不是非常多样化。大约 80%的训练数据由来自生物医学领域的论文组成,其余 20%来自计算机科学领域。有许多不同的科学学科没有被这个语料库覆盖。
🐌SciBERT 速度和输入限制
像 SciBERT 一样,使用 PLM 最简单的方法是以冻结的方式使用它。这需要加载模型并使用检查点上的权重来使用模型,而不是通过改变任务的权重来微调模型。您只需要向前传递您的语料库来创建文档嵌入(表示)。像 SciBERT 这样的大型 PLM 是具有许多参数的复杂模型。至关重要的是,任务性能随着模型的复杂性而增加。我们将在这一部分讨论速度,在下一部分讨论性能。
我们可以使用拥抱脸基准测试来获得一些具体的例子,看看 SciBERT 用了多长时间来特征化文本。我们的批量为 256,抽象长度为 128、256 和 512 个单词[7]。

图 1 — 通过 NVIDIA GeForce RTX 2080 Ti 上的拥抱脸进行 SciBERT 基准测试。图片由作者提供。
假设你有一个 400 万篇论文的语料库,每篇论文使用大约 512 个单词(例如,标题和摘要)。然后,根据上面的基准,我们需要 55 分钟的处理时间:
featurize_time_in_minutes = ((4_000_000 / 256) * 2.116) / 60
print(featurize_time_in_minutes)
55.104166666666664
这仍然是一个可管理的时间量,我们可以存储我们的嵌入向量,并在每次我们想做新的实验时加载论文。这需要一些 IO 管理,但不是实验的明确障碍。
然而,512 个令牌并不总是足够的。这个数量大致相当于一篇论文摘要的大小。对于某些用例,最好使用(部分)全文。在这个场景中,文本需要以 512 为一批进行分块,因为 SciBERT 最多只能处理 512 个单词。比方说,平均来说,这些论文包含 5120 个单词(这是保守的)。这意味着每张纸需要 10 批 512 张。这将使我们的特征化步骤长达近 10 个小时。
此外,在生产中,最好能够在 CPU 实例上运行模型,因为 GPU 实例更昂贵。当 ML 产品被转移到生产中时,模型被存储在容器中,并且根据组织的基础设施,它们或者是自托管的或者是通过云提供商托管的。当模型是自托管的时,组织本身负责管理模型运行的硬件。使用云提供商可以避免这种情况,组织可以为使用云提供商硬件的时间付费。硬件越好,价格越贵。拥有一个 GPU 实例比拥有一个 CPU 实例要昂贵得多。假设终端全天候运行,这就是谷歌云平台(GCP)的成本。

一个端点的 GPU 成本概述,图片由作者提供。
上图显示了成本的巨大差异。CPU 推理容器要便宜得多。
在上一节中,我们研究了 GPU 上的基准测试,现在让我们转到 CPU 上,使用 SciBERT 在 CPU 上进行特性测试需要多长时间?这次我们分批取 1,16,32。因为批量越大耗时越长。

图 2 — 笔记本电脑 CPU(英特尔 i7)上的 SciBERT 性能指标评测。图片作者。
在图 2 中,我们看到了一些限制。在 CPU 上运行这个不是很可行,尤其是在使用(部分)全文的时候。特征化一批 32×512 需要 12.271 秒,如果我们再次假设较长的文档大约为 5120 个单词,我们将不得不等待 2 分钟以上才能获得嵌入。这很快成为生产的瓶颈。
模型域名
如前所述,SciBERT 是一个为科学领域定制的模型,但是科学领域到底是什么?让我们再深入一点。NLP 中领域的概念定义模糊,经常以直观的方式使用[10]。在同一种语言中,在风格、词汇、话题和其他语言细微差别方面存在巨大差异。例如,在自然语言处理中,谈论来自“医疗领域”的数据是常见和典型的。然而,哪些文本适合这一领域并没有明确的定义。在[11]中,作者从不同的来源获取文本,如医学文本、法律文本和 IT 文本(软件手册)。通过对不同大型 PLM 的聚类实验,他们表明区分这三种来源并非易事。
PLM 的规模越来越大,人们可能会怀疑一个足够大的模型是否值得适应一个更适合的领域。在[12]中,作者表明,对于像 RoBERTa [13]这样的大型模型,仍然值得将模型应用于特定领域。他们在领域适应前后测试模型在生物医学和计算机科学领域的性能。对于这两个领域,模型的性能在适应后都有所提高。
我们可以说有两种方法可以使一个模型适应一个领域:
- 通过模型网络的权重。
- 通过模型的词汇。
模型的权重有什么作用?
权重对模型中的所有知识进行编码。当文本作为模型的输入给出时,基于模型的权重矩阵创建该文本的数字表示。
什么是词汇?
传统上,模型的词汇表由训练数据中出现的所有唯一单词组成。这是不可扩展的。在 BERT 和 SciBERT 的情况下,模型利用了子单词标记化,更具体地说是单词片段标记化[7]。因此,词汇表由单词的小片段或子单词组成。不同的模型使用不同的算法进行标记化,因此产生不同的词汇,但是一般来说,子词是基于它们在用于训练模型的语料库中出现的频率来创建的。
使用子词的主要好处是,该模型可以处理词汇以外的词,即在训练期间没有见过的词。模型词汇表的作用是 Transformer 模型中相对研究不足的方面。在[14]中,作者使用英语和日语两种语言的 RoBERTA 目标训练了一个语言模型,并表明使用不同的词汇构建方法会影响性能。在他们写作的时候,他们写道,模型的词汇在训练后不能改变,因此是模型架构中的一个关键决定。在[15]中,介绍了一种改变方法词汇的方法,但是这并没有被广泛实现。模型的词汇表仍然是一个重要的组成部分。
让我们看看 BERT 和 SciBERT 之间的一些词汇差异,以便更好地理解拥有不同的词汇如何影响特定领域单词的标记化。我们将看到四个科学术语:“抗脂多糖”、“血管造影术”、“近距离放射疗法”和“脑电图”。在科学领域中,拥有这些更长的特定领域的单词是很典型的。
抗脂多糖
BERT tokenizer: ['anti ',' ##lip ',' ##op ',' ##ol ',' ##ys ',' ##ac ',' ##cha ',' ##ride ',' ##s']
SciBERT 标记符:['anti ',' ##lip ',' # #多糖',' ##s']
血管造影
BERT tokenizer: ['ang ',' # # loggraphy ']
SciBERT tokenizer: ['血管造影术']
近距离放射治疗
BERT tokenizer: ['bra ',' ##chy ',' ##therapy']
SciBERT tokenizer: ['brachy ',' ##therapy']
脑电图
BERT 标记符:['电子',' # #事件',' # #相位',' # #对数',' # #图形']
SciBERT tokenizer: ['脑电图',' # #描记']
'表示它是属于前一个单词的新 WP。
在单词“抗脂多糖”的第一个例子中,我们看到了 SciBERT 和 BERT tokenizers 之间的巨大差异。SciBERT 记号赋予器将单词分解成 4 个单词片段,而 BERT 记号赋予器将单词分解成 9 个单词片段。这两种模型的区别在于,SciBERT 模型的词汇表中包含“多聚糖”。
在第二个例子中,完整的单词“angiography”是 SciBERT 模型词汇表的一部分。如果我们想得到一个单一的向量来给出单词“血管造影”的数字表示,我们必须取平均值,或任何其他加权措施,在伯特的情况下。然而,如果我们使用 SciBERT,我们不必在单词块之间求平均值。这是有益的,因为该模型可以在这种唯一的标记中编码专门的知识,而不是将知识散布在也在其他方面使用的多个标记上。这种专用词汇表的另一个好处是序列长度变得更短,因此计算速度更快,或者在处理较大的文本时,它允许考虑更多的上下文。
预训还是不预训?
在前面的部分中,我们建立了像 SciBERT 这样的模型的一些限制,以及为什么它对我们来说不是理想的。在这一节中,我们将看看替代方案的利弊。我们的问题是,没有符合我们要求的拥抱脸预训练模型可用:
- 快速高效的推断:最好不用 GPU,节省成本。低延迟和低内存。
- 在多种多样的科学文本上的艺术(SOTA)或接近 SOTA 的表现。
- 能够处理超过 512 个令牌的序列。
设计和训练你自己的模型是一项繁重的工作,即使我们有工具和框架来帮助我们,这也不是一项简单的任务。当在应用人工智能领域工作时,从头开始训练一个模型是非常激烈和昂贵的措施。
总的来说,如果我们希望有一个预先训练好的变压器模型来帮助我们解决问题,我们可以做两件事:
- 从头开始预训练模型。
- 改变现有模型。这可以通过压缩、知识提炼[18]和微调等技术来实现。
让我们来看看这些技术,它们对模型给予了什么样的控制,以及如何控制

不同模型定制技术概述,图片由作者提供。
表中信息的一些注释:
- 如果你使用云图形处理器,成本包括时间和金钱。
- 在已建立的微调管道中,词汇自适应不是惯例,在[15]中,作者展示了一种实现这一点的方法,但这不是已建立的实践。通常,仅在微调中,权重被调整。
- 在[16]中,他们展示了如何从一个具有大量词汇的模型中使用 KD 并转移到一个具有较少词汇的模型的方法。
- KD 的成本取决于您的选择。更大的学生网络,更高的成本和更长的培训时间。
(可以使用不同的压缩技术,但是讨论它们是如何工作的超出了这篇博文的范围。知识提炼可以被视为一种压缩技术,在这篇文章中,我们将它单独列出,因为它在操作上不同于其他压缩技术。在蒸馏中,基于(较大的)教师模型的知识训练新的(较小的)模型。这不同于其他压缩技术,如剪枝[17],从现有模型的连接被删除。在蒸馏中,有更多的自由,因此有更多的选择。例如,提取模型的架构可能与教师模型非常不同。
对于微调,有一些坏消息。尽管在拥抱脸网站上,我们可以找到很多免费的模型,但是符合上述需求的模型并不多。拥抱脸有三种模式,至少满足我们的一个要求:能够处理比 SciBERT 更长的序列:
- allenai/longformer-base-4096
- 谷歌/大鸟-罗伯塔-基地
- 威斯康辛大学麦迪逊分校/纽约证券交易所
但是这些并不能满足我们所有的要求,这些模型之所以能够处理比 BERT 更长的序列,是因为它们使用了不同的注意机制。关于不同 PLM 中使用的不同注意机制的详细概述,请看由托马斯·范东恩撰写的帖子。
如果我们看上面的表格,我们会发现“蒸馏”和“从头开始的预培训”在所有栏目中的得分都是“是”。然而,即使对于蒸馏,在技术上也有可能改变词汇,微调新领域的权重,实现新的注意机制,并同时降低延迟,这可能会很快变得混乱。正如 SciBERT 不是一颗神奇的子弹一样,在替代模型或技术方面也没有立竿见影的效果。
当你进行预训练时,你会期待什么?
从头开始预先训练一个模型是一个大项目,有好处也有潜在的陷阱。在本节中,我们将查看这些内容,并列出训练您自己的模型所需的步骤。
训练自己的模型有什么好处?
- 您可以更深入地理解您正在使用的技术,并获得数据和架构优化方面的经验。这是你微调模型时不用处理的事情。
- 完全根据您的喜好定制。一切都如你所愿。因此有可能为您提供您需要的定制性能。
- 它看起来很酷,你可以为你的模型想一个名字。
读完这些好处后,你可能会很兴奋,并准备开始你的预训练,但在此之前,看看这些陷阱。
预先训练你自己的模型的陷阱。
- 决策:有很多决策要做。例如,架构(深度、层的类型、注意力的类型等)、损失函数、训练算法、数据集和超参数。
- 大型项目:这不是你的常规副业,很难在全职工作之外管理。因为涉及到大量的工作,这不是你一个人可以完成的,会有相互依赖。
- 计算能力:需要大量的培训和实验。要做到这一点,你需要有足够的计算能力。这是有代价的。即使您可以访问多个 GPU,您也需要确保高效地使用它们。
- 证明你更优秀:如果你想发布你的模型和结果,你必须证明你在一系列评估任务上更优秀。这并不是一件轻而易举的事情,因为你需要确保所有的测试条件对于你想要击败的模型都是一样的。
现在你明白了好处和陷阱,如果你决定从头开始预先训练你自己的模型,下面是你需要采取的步骤。
预先训练你自己的变压器模型的步骤。
- 数据。决定你将使用哪些数据,检查它,并特别注意你的序列长度。你有大部分是长序列文本还是大部分是短序列文本?
- 记号赋予器。决定你要使用哪种标记化方法,以及你的词汇量有多大。
- 注意。决定你要用哪种注意力机制,这将决定你的序列长度可以有多长。许多机制都在 Xformers 库中实现。
- 目标。决定你将使用的培训前目标。如果你想保持你的训练有效,你需要寻找有效的训练前任务,如 ELECTRA [19]。
- 训练。开始训练。在这个阶段,您将优化您的超参数。
- 评价。评估你的模型。根据您的目标,如果您计划仅将您的模型用于您自己的产品/解决方案,您不必在广泛的学术任务上评估您的模型,但可以直接在这些任务上评估您的模型。
- 优化。优化你的模型。即使您已经做出了有效的架构选择,也值得进行优化(例如 ONNX、蒸馏、修剪)以实现更低的延迟。
- 部署。将它融入到你的产品中,并衡量是否有现实影响。
结束语
如果你的目标是快速开发一个好的产品,你不需要预先训练你自己的定制语言模型。通过使用 PLM,可以快速移动和快速创建产品。这可能不是最佳解决方案,但在某些情况下,这可能已经足够好了。验证您导入的 PLM 是否增加了足够的价值,以证明其规模和速度是合理的,这一点尤为重要。
预先训练你自己的模型在时间和计算能力上都是一项投资。如果你成功地训练了一个模型,你就有了可以用于未来产品的东西。而且,如果允许的话,根据你的隐私和数据权限,你可以发布你的模型,其他人也可以利用。我们每天都在使用开源代码和模型,能够为之做出贡献是表达“感谢”的一种好方式。
参考文献
[0] Wolf,t .,处女作,l .,Sanh,v .,Chaumond,j .,Delangue,c .,Moi,a .,Cistac,p .,Rault,t .,Louf,r .,Funtowicz,m .,& Brew,J. (2019)。拥抱脸的变形金刚:最先进的自然语言处理。ArXiv,abs/1910.03771。
【1】瓦斯瓦尼,a .、沙泽尔,N.M .、帕尔马,n .、乌兹科雷特,j .、琼斯,l .、戈麦斯,A.N .、凯泽,l .,&波洛舒欣,I. (2017)。你需要的只是关注。ArXiv,abs/1706.03762。
【2】Devlin,j .,Chang,m .,Lee,k .,& Toutanova,K. (2019)。BERT:用于语言理解的深度双向转换器的预训练。ArXiv,abs/1810.04805。[3]布朗、T.B .、曼、b .、赖德、n .、苏比亚、m .、卡普兰、j .、达里瓦尔、p .、尼拉坎坦、a .、希亚姆、p .、萨斯特里、g .、阿斯克尔、a .、阿加瓦尔、s .、赫伯特-沃斯、a .、克鲁格、g .、海尼根、T.J .、柴尔德、r .、拉梅什、a .、齐格勒、D.M .、吴、j .温特、c .、黑塞、c .、陈、m .、西格勒、e .利特温、m 语言模型是一次性学习者。ArXiv,abs/2005.14165。
【4】贝尔塔吉,I .,罗,k .&科汉,A. (2019)。SciBERT:科学文本的预训练语言模型。自然语言处理经验方法会议。【5】李,尹俊杰,魏,金,金美贞,金美贞,苏春红,&康,金(2020)。BioBERT:用于生物医学文本挖掘的预训练生物医学语言表示模型。生物信息学,36,1234–1240 页。
【6】科汉,a .,费尔德曼,s .,贝尔塔吉,I .,唐尼,d .,&韦尔德,D.S. (2020)。SPECTER:使用引用通知转换器的文档级表示学习。计算语言学协会年会。
[7]舒斯特,m .,T25 中岛,K. (2012 年)。日韩语音搜索。2012 年 IEEE 声学、语音和信号处理国际会议(ICASSP),5149–5152。
【8】米科洛夫,t .,陈,k .,科拉多,g . s .&迪安,J. (2013)。向量空间中单词表示的有效估计。学习表征国际会议。
【9】罗,k,王,L.L .,诺依曼,m .,r . m .&韦尔德,D.S. (2020)。语义学者开放研究语料库。计算语言学协会年会。
【10】Wees,M.V .,Bisazza,a .,Weerkamp,w .,& Monz,C. (2015)。域中有什么?统计机器翻译中的体裁和主题差异分析。ACL。
[11]阿哈尼,r .,T29 戈德堡,Y. (2020 年)。预训练语言模型中的无监督领域聚类。计算语言学协会年会。
[12]guru rangan,s .、Marasovi,a .、Swayamdipta,s .、Lo,k .、Beltagy,I .、Downey,d .、&Smith,N.A. (2020)。不要停止预训练:使语言模型适应领域和任务。ArXiv,abs/2004.10964。
【13】刘,y .,奥特,m .,戈亚尔,n .,杜,j .,乔希,m .,陈,d .,利维,o .,刘易斯,m .,泽特勒莫耶,l .,&斯托扬诺夫,V. (2019)。RoBERTa:稳健优化的 BERT 预训练方法。ArXiv,abs/1907.11692。
[14]博斯特罗姆,k .,T32 德雷特,G. (2020)。字节对编码不是语言模型预训练的最佳选择。调查结果。
【15】萨缅科,I .、吉洪诺夫,a .、科兹洛夫斯基,b . m .&亚姆什奇科夫,I.P. (2021)。微调变压器:词汇转移。ArXiv,abs/2112.14569。
[16]科列斯尼科夫,库拉托夫,科诺瓦洛夫,布尔采夫,M.S. (2022)。词汇缩减的俄语语言模型的知识蒸馏。ArXiv,abs/2205.02340。
[17]勒村,y .,登克,J.S .,T35 索拉,S.A. (1989 年)。最佳脑损伤。乳头。
【18】hint on,g . e . viny als,o .&Dean,J. (2015)。从神经网络中提取知识。ArXiv,abs/1503.02531。
[19]克拉克,k .,梁,m .,乐,Q.V .,T37 曼宁,C.D. (2020)。ELECTRA:预先训练文本编码器作为鉴别器而不是生成器。ArXiv,abs/2003.10555。
为什么用 Python 输入你的参数?
原文:https://towardsdatascience.com/why-type-your-arguments-in-python-5bf24d7201eb
为什么在 Python 中提供参数类型是有意义的一些令人信服的理由

键入参数
与许多其他编程语言相比,无论何时用 Python 编写函数,你都不需要考虑类型。当然,对于在函数体中进行的任何函数调用或属性调用,仍然要考虑这些类型,但是不管怎样,在不需要将它们放入代码的情况下,考虑这些事情是很容易的。例如,下面的函数只打印一个值:
def printer(x):
print(x)
我们可以在任何数据类型上调用 print,所以这个函数可以处理任何数据类型。我们真的根本不需要考虑提供什么类型。如果我们想指定 x 应该是某种类型,我们可以用一个冒号后跟一个类型来实现,例如
def printer(x : int):
print(x)
从语法上来说,这不会对我们的代码产生任何影响。从执行的各个方面来看,这两个函数都会做完全相同的事情。如果我们通过这个函数传递一个浮点数或者一个字符串,我们不会得到一个错误。老实说,我认为 Python 肯定可以从 Julian MethodError 中受益,因为虽然可能不存在多重分派的预期,但仍然限制类型确实很有意义。MethodErrors 可以提供关于所提供的参数的大量信息,使事情更容易检查或“猜测”,而无需文档。
我喜欢 Julia 的一点是,Julia 的方法错误提供了你做错了什么的背景。Python 只有 ArgumentErrors,我想这可以被认为是类似的东西,但是想象一下如果你得到了类似的东西
ArgumentError No method matching printer(x : int)
closest candidates are
printer(x : float)
或者我只是想让 Python 成为 Julia,我不知道。
在这两种情况下,ArgumentError 与 MethodError 在语义上是不同的,尽管在这种情况下,我认为名称可以互换,因为是参数和方法共同造成了错误。
为什么要输入参数?
既然我们已经介绍了如何输入参数,并确定它在语言与数据的交互方面确实没有做很多事情,我们可能应该问为什么。我的意思是,如果为我们的参数设置一个类型除了占用空白空间之外没有任何作用,那么我们为什么还要花时间输入这些内容呢?
需要考虑的一件重要的事情是,大多数项目会被不止一个人阅读。在上面 printer()函数的例子中,有一些类不能被这个函数打印。也就是说,所提供的这些类型的值很可能会导致一些令人困惑的错误输出,其中函数内部的另一个调用是用错误的类型调用的。此外,如果我们没有输入我们的参数,而其他人正在读取这个函数,除了参数的命名和函数调用之外,我们预期的类型就不明显了。也就是说,由于构造函数的原因,并不是所有的 Python 对象都包含在 Python 中,有些类型可能会导致很多问题。
键入参数的另一个原因是它将为文档提供这些信息。Python 有很多自动化的文档工具,大多数都利用了这一点。为了使用一个函数,人们需要知道的最重要的事情之一就是参数,以及参数的类型。考虑到这一点,文档指出应该为哪些参数提供什么类型可能是非常重要的。
我输入参数的最后一个原因是,通过函数装饰器,它使函数更加元可编程。一个很好的例子就是多调度模块,它实际上是一个非常棒的模块。如果您想了解更多关于该模块的信息,我实际上写了一整篇文章,以下是链接:
没有什么比多态更让我开心了。
结束语
在句法方面,我感到奇怪的是,来自其他语言的论元类型被公然忽略了。我觉得让 Python 解释器与参数类型进行更多的交互会很好,就像它与多分派模块的交互一样,但是我认为这有点主观,因为这是由 Julia 程序员提出的。也就是说,不管它实际上对您的 Python 代码做了什么,我确实认为将参数键入您的函数是一个好主意。
键入参数为函数提供了大量的上下文。我以前说过这句话,但现在我要再说一遍,函数最重要的部分是输入和输出。每当我想分解一个函数时,这总是我要去的地方——它需要什么,它给出什么,然后我们如何从它需要什么到它给出什么——这个概念表面上很简单。对这类事情有更多的背景是很好的,尤其是在我们与他人共享代码的情况下。这也包含在文档中,这更是键入你的论点的理由。感谢您的阅读,我希望现在有了这些阐述,您的功能最终会得到某种程度的改进!
为什么在神经网络中使用学习率调度器可能是浪费时间
提示:批量大小是关键,它可能不是你想的那样!

安德里克·朗菲尔德在 Unsplash 上拍摄的照片
TL;DR: 不是用一个因子来降低学习速率,而是用同一个因子来增加批量大小,以实现更快的收敛,如果不是更好的话,也能获得更好的训练结果。
近年来,神经网络的不断发展使得其在现实世界中的应用越来越多。尽管神经网络和其他类似的深度学习方法很受欢迎,但它们仍然存在严重的缺陷,影响了它们在未知数据集上的可用性或性能。
众所周知,神经网络消耗大量的计算资源和时间。随着对神经网络研究的不断深入,越来越多的复杂结构被提出并付诸实践。然而,即使一些较大的网络与较小的网络相比可能能够获得不错的性能,但其训练和推理时间的总量使它们不适用于许多现实世界的场景。减少神经网络训练时间的一个标准解决方案是增加批量大小,或者增加网络在其参数的一次更新中看到的数据量。从逻辑上讲,如果网络在每次更新时看到更多的数据,那么与每次出现新样本就更新其参数的网络相比,参数更新的总数将会减少。我们可以通过减少参数更新的次数来有效地减少网络训练时间。
批量大小和泛化差距
然而,最近的研究表明,更大的批量会导致更大的泛化差距(参见 Nitish Shirish Keskar 等人的“关于深度学习的大批量训练:泛化差距和尖锐极小值”)。术语“泛化差距”与“过度拟合”同义,描述的是训练数据和测试数据之间存在性能“差距”的现象,后者比前者更差。为了提高在看不见的数据集上的性能,模型需要能够将它们在训练中所学的知识归纳并应用到新的、看不见的样本上。
为了解释为什么增加批量大小会导致显著的泛化差距,想象一个以 2 递增的数字串,从“1,3,5,…”,一直到“…,37,39”。现在,假设你被告知在不看的情况下从列表中随机选择 15 个数字。在你睁开眼睛后,你有多大可能发现原始列表中的增量模式(假设事先没有告诉你模式)?经过一番重新整理,答案估计很有可能。这是因为您获得了列表中 20 个值中的 15 个;偶然地,一些组合可以泄露模式。如果你只能从列表中随机选择三个值会怎么样?你还能辨认出模式吗?答案并不确定,因为列表中 20 个值中的 3 个可能很少或没有给出关于每个数字之间的原始关系的信息(增量为 2)。

一个关于大批量和小批量之间差异的思想实验。图片来自作者。
批量大小对模型性能的影响与上述思维实验的方式相同:批量越大,模型越有可能在更少的时期内发现特征和目标之间的关系。这一发现可能有利于训练,因为该模型可以更快地达到全局最小值,但这是以较差的泛化能力为代价的。当模型在每个更新步骤中看到的数据较少时,它可能无法找到适合训练数据的最佳参数,但它更有可能为同一类型的所有数据集归纳出一个通用规则(就像世界上不是每个数字列表都有递增 2 的模式一样)。批量较大的模型更容易过度拟合,从而比批量较小的模型产生更大的泛化差距,而批量较小的模型可以在看不见的样本上表现良好。
批量大小和学习率与噪音训练的关系
Samuel L. Smith 和 Quoc V. Le 在他们的研究“关于一般化和随机梯度下降的贝叶斯观点”中,通过提出 SGD 优化器的“噪声标度”计算进一步证明了这一点:
其中 N 是训练样本的数量,B 是批量大小,ε是学习速率。由于 B << N,在大多数情况下,我们可以将噪声比例简化为
“噪声标度”值描述了由于批量更新而导致的 SGD 训练中随机波动的幅度。直观地说,噪声越小,训练结果越好,但请注意,在训练中保持合理的噪声量有助于模型的泛化,从而使训练规范化。将批量大小设置为相对较大的值将在整个训练过程中产生最小的噪声,从而导致较差的泛化能力,因此较大批量大小周围的直觉会导致较大的泛化差距。
增加批量大小和降低学习速率都会降低噪声水平。
衰减的学习率通常被用作训练的一部分来降低噪声。通过在训练开始时具有较大的学习率值,网络将能够在噪声情况下搜索较大的参数空间。一旦模型找到了要采取的最佳方向/步骤,额外的噪声可能会开始损害训练(即,验证损失开始增加),然后学习速率会慢慢降低。当模型接近全局最小值时,这个动作将减少噪声,减慢到更稳定的训练阶段。这样做可以确保模型的泛化和良好的训练结果,但代价是复杂网络需要长达数天甚至数周的训练时间。
增加批量大小,而不是降低学习速度
注意,不仅衰减ε/学习率的值可以降低噪声水平,而且增加 B/批量大小的值也可以达到相同的效果。利用这一事实,Samuel L. Smith、Pieter-Jan Kindermans、Chris Ying 和 Quoc V. Le 在他们的研究中表明,学习速度和批量大小成反比。作者提出,不使用学习率调度器,而是在训练期间增加批量大小,这可以根据经验获得与衰减学习率相同的结果。在作者的实验中,他们在以下条件下训练了三种类型的模型:
- 学习率下降:学习率反复下降 5 倍
- 混合:在第一步增加批量,同时保持学习速率不变,然后训练类型恢复到#1。这种训练方法模拟了较大批量不可行的潜在硬件限制
- 增加批量:批量以与#1 相同的速率增加 5 倍。
下图显示了他们的培训结果。

三种模型的实验结果。来自塞缪尔·史密斯等人。
在左边,我们观察到,随着批量的增加,模型的训练结果等价于学习率下降的训练。然而,与传统的衰减学习率模型相比,增加批量大小的模型实现了 67%的参数更新。更具体地说,作者指出,批量大小应增加到大约 N/10;然后,在剩余的训练持续时间内,该模型将回复到传统的衰减学习率训练风格。
本质上,我们可以用“批量调度器”代替学习率调度器,并大大减少训练时间。下面是作者在 Keras 中的方法的简单演示。使用森林覆盖类型数据集(可在 Scikit-learn 中找到)。
from sklearn.datasets import fetch_covtypedataset = fetch_covtype()
X = pd.DataFrame(dataset.data)
y = pd.DataFrame(dataset.target)
y = pd.get_dummies(y[0])X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
用于演示的模型是一个相当深的表格数据网络,有七个隐藏层和一个类似瓶颈的设计。
def create_testing_model():
inp = L.Input(X.shape[1])
x = L.Dense(512, activation="relu")(inp)
x = L.Dropout(0.25)(x)
x = L.Dense(256, activation="relu")(x)
x = L.Dense(256, activation="relu")(x)
x = L.Dropout(0.25)(x)
x = L.Dense(128, activation="relu")(x)
x = L.Dense(256, activation="relu")(x)
x = L.Dense(256, activation="relu")(x)
x = L.Dropout(0.25)(x)
x = L.Dense(512, activation="relu")(x)
x = L.Dropout(0.25)(x)
out = L.Dense(7, activation="softmax")(x)
normal_model = M.Model(inputs=inp, outputs=out)
normal_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=6e-4), loss=tf.keras.losses.CategoricalCrossentropy())
return normal_model
通过模仿 Keras ReduceLROnPlateau回调,当验证损失在三个时期内没有改善时,批量大小从 256 开始增加 5 倍。
# timing purposes
batch_start = time.time()
# total epoch number
epoch=60
# ensure the batch size is 256 on the first epoch
batch_size=256/reduce_fac
# for recording training history
batch_history_total = pd.DataFrame([], columns=["loss", "val_loss"])
# init model
batch_model = create_testing_model()# make sure batch size does not exceed sample size/10 and epoch doens't go below 0
while epoch > 0 and batch_size <= len(X_train)/10:
print(F"CURRENT BATCH SIZE: {batch_size*reduce_fac}")
# early stop if val loss stops improving for 3 epochs
early_stop = C.EarlyStopping(monitor="val_loss", patience=3, verbose=2)batch_history = batch_model.fit(X_train, y_train, epochs=epoch, batch_size=int(batch_size*reduce_fac),
validation_data=(X_test, y_test), callbacks=[early_stop], verbose=2)
cur_history = pd.DataFrame(batch_history.history)
# concat the current training history to the total dataframe
batch_history_total = pd.concat([batch_history_total, cur_history], axis=0).reset_index(drop=True)
# adjust batch size
batch_size = batch_size*(reduce_fac)
# decrease total epoch by the number of epochs trained
epoch_trained = len(cur_history)
epoch -= epoch_trainedif epoch > 0:
# reverting to reduce lr training
print("reverting to reduce lr training")
reduce_lr = C.ReduceLROnPlateau(monitor="val_loss", factor=(1/reduce_fac), patience=3, verbose=2)
batch_history = batch_model.fit(X_train, y_train, epochs=epoch, batch_size=int(batch_size), validation_data=(X_test, y_test), callbacks=[reduce_lr], verbose=2)
batch_history_total = pd.concat([batch_history_total, pd.DataFrame(batch_history.history)], axis=0).reset_index(drop=True)batch_end = time.time()
为了进行比较,用 256 的恒定批量训练相同的模型,同时降低验证损失没有改善的每三个时期的学习率。两个模型都训练了 60 个历元,仅用ReduceLROnPlateau训练的模型令人惊讶地慢了两倍(在 GPU 上),验证性能稍差。

减少批量方法和衰减 lr 方法的训练结果比较。图片来自作者。
下次,在训练任何神经网络时,无论是图像分类、表格数据还是音频任务,都要给“批量调度程序”一个尝试。结果可能会为您节省一些时间(和细微的改进)!
为什么我们真的需要数据科学
原文:https://towardsdatascience.com/why-we-actually-need-data-science-cc1df7adce97
数据科学具有并将继续具有影响力的 3 个原因

照片由 Dan Meyers 在un plash上拍摄
从事数据科学 6 年后,你会认为我能够自信地回答这个问题“数据科学家,嗯…那么你是做什么的?”我通常会用“我帮助人们做出更好、更快的决定”这句话来回应具有讽刺意味的是,我真的无法证明我做到了足够的一致性,以证明“数据科学”是我的职业。也许在分析 A/B 测试的上下文中,我可以说我帮助某人做了一个决定…但是如果他们只是优化使用,或者根本不运行测试,可能会更快!
因此,我很自然地向我的朋友们哀叹我的工作似乎毫无价值。他们告诉我要成为一名工程师,或者也许我想尝试一下产品管理?我开始点击顶级科技公司的前数据科学家的文章,惊呼你不应该成为像这个、这个或这个这样的数据科学家。
但是转行很难,我也可以偷懒。所以我这个纸上谈兵的经济学家开始思考市场。如果数据科学真的没有价值,为什么市场似乎赋予我们的价值接近于工程师的?
也许不是我的工作没有价值,而是我陷入了一个没有正确分配价值的陷阱。

想象一下,你是当地咖啡店的一名咖啡师,你告诉自己,你工作的目的是帮助你周围的人更有效率。你可能会看到咖啡因让顾客疯狂地打字,但是真的,你能知道你是否成功了吗?你可能会很快对你的咖啡师工作不再抱有幻想。但是,如果你把注意力放在一个更实际、更直接的目标上——用美味的咖啡和热情的交谈取悦顾客,会怎么样呢?
因此,我查阅了我职业生涯的历史数据,看看能否找出一些模式,指出我真正创造了价值的地方,希望借此重塑我对自己工作的看法。
我在职业生涯中所做的所有工作的心理目录,服务于大型公司,到小型科技初创公司,让我总结出数据科学在当今和未来的组织中真正有价值的 3 个原因。
1。创建和维护指标很难
数据科学家对其组织中的数据传播拥有所有权。我们有责任理解数据生成过程,用业务逻辑精心制作度量标准,并以易于理解的格式呈现出来。因为企业是一个不断进化的有机体,所以它产生的数据也是如此,因此它的指标也是如此。
就拿这个看似简单的问题来回答我的新订阅业务 Barkflix 吧(当然是狗的网飞)。
我们昨天从新用户那里获得了多少收入?
这是我的数据科学家可能会有的一段内部对话。
好吧,很简单,让我们从我们的支付处理器获得新用户的收入。
嗯,哦,等等,我们的支付处理器不知道谁是新客户,谁是现有客户。我需要检查我们的应用程序数据库。哦,对了,我们有三个不同的支付处理器。看起来我需要以某种方式将所有这些结合起来。等等,什么,我们加了第四个处理器?我如何获得这些数据…?
哦。内特什么时候想要这个?
我想我和其他一些数据科学家都认为这种耗时的工作有失我们的身份。这只是妨碍聚类分析的工作,而我知道聚类分析将彻底改变这个行业。
事实是,这项工作很有价值,也很有挑战性。分析工程师的崛起更加证明了这项工作的价值。这些角色的描述围绕设计、构建和维护可扩展的数据模型,以支持自助式商业智能工具并促进数据驱动的决策制定。或者,理解数据,并把它传播给人们。
2。小的优化很重要,或者更多的业务有更大的规模
随着数字经济的兴起,我们看到更多的企业能够接触到更多的人。规模公司的增加意味着小的改进更重要。
回到我的热门新独角兽 Barkflix,假设我每月收取 10 美元订阅这个绝对重要的内容生态系统。我团队中的一名 rockstar 数据科学家在我们的注册流程中发现了一个高摩擦点。我们设计了一个补丁,将我们的转化率从 10%提高到 11%。
现在想象一下,这还是 Barkflix 的早期阶段,我们是一家小夫妻店,每个月有 500 人访问我们的注册页面,这意味着这一改进导致了 5 个以上的客户,每个月我的口袋里多了 50 美元。我可以请几个朋友喝一些 19 美元的鸡尾酒,他们告诉我“非常值得”
现在让我们把它放大一点。我现在每月有 500,000 名访客,这一改进现在为我带来了 5,000 名新客户和 5 万多美元。同样的变化将超过支付我昂贵的曼哈顿公寓的租金。
寻找潜在的机会和测试增量改进是数据科学家工作的一部分。随着规模的扩大,数据科学家对收入驱动指标的每一点改进都会产生越来越多的价值。

3。我们有更多的财富,更多的时间,所以我们可以拥有更多的东西。即更多的问题,以及我们答案中更高程度的确定性
非常类似于生活方式蔓延的现象,收入的增加导致生活方式商品支出的增加(漂亮的餐馆、鞋子、汽车、书籍等等)。)这种更高层次的消费变得正常化,甚至被视为一种必需品。然而,更多的钱并不是等式的唯一部分。你需要时间来真正消费这些闪亮的新物品。
想象一下,从每天 10 小时、每小时 15 美元的通勤时间 2 小时,到每天 7 小时、每年支付 20 万美元的工作时间,这一切都可以在舒适的卧室里完成。这种更多时间和更多金钱的结合自然会将你的注意力从日常工作转移到更多的休闲活动上。
在工作场所,我们也看到了类似的现象。在过去的二十年里,我们目睹了工作场所生产力的巨大提高。作为证据,以新的和改进的 SaaS 工具的兴起为例,这些工具把我们从整天推纸中解放出来。****
Salesforce 革新了 CRM 领域。Asana,Airtable 和吉拉帮助团队组织他们的工作。Hubspot、Marketo 和 Google Analytics 为营销人员自动化了以前耗时的任务。这样做的结果是,更多的人现在能够把注意力集中在 SaaS 工具没有解决的问题上。
这些问题太过微妙,任何工具都无法设计出一个全面的解决方案。这些是容易落入分析团队手中的问题类型。我们的产品发布会成功吗?我们应该为产品构建的下一个功能是什么?在接下来的一年里,我们应该在制作什么类型的内容上投入更多?
我认为,如果我们没有优化产品开发、网站开发、内容创作和经营企业所必需的大量其他工作流程,我们甚至不会问这类问题(或者至少不会真的 真的 期待有人回答这些问题)。
我认为这最后一块是我最难吞下的药丸。通常情况下,这里提出的问题并不是由数据科学家来回答的,或者我们只是没有合理的数据集来处理。有时候,回答这些问题真的是浪费时间,会让分析师陷入兔子洞,再也回不来了。
那么我现在永远快乐吗?
我已经部分解决了我对数据科学价值的怀疑。有时候,我仍然很难感觉到自己对我的团队和整个企业产生了有意义的影响。但是,当我漂浮在存在的以太中,而我的懈怠被临时的请求弄得支离破碎时,我发现我能够通过依靠这三个数据科学价值的支柱来找到我大部分工作的意义。
你怎么想呢?我是不是少了一根柱子?这三个人中有一个应该被淘汰吗?
为什么我们需要处理不平衡的班级
原文:https://towardsdatascience.com/why-we-need-to-deal-with-imbalanced-classes-ec0dc1a7b803
类别不平衡自然会出现在某些类型的分类问题中,如信用归属(数据集通常包含的批准信用比拒绝信用多得多)或欺诈检测(欺诈通常只占总交易的一小部分)。
类别不平衡是指分类变量的模态之一相对于其他模态而言被过度表示,如下例所示:

不平衡变量的饼图示例(图片由作者提供)
建议在训练模型之前处理类别不平衡,建议的方法通常属于以下类别之一:
如果你想了解关于某个主题的更多信息,我已经为每种方法链接了一些资源。这篇文章将重点阐述为什么首先要解决职业不平衡,以及它对模型性能的影响。假设您熟悉分类模型的性能测量,如果不熟悉这里是一个很好的起点。
所选择的性能度量标准需要意识到类别不平衡
让我们考虑一个类似于饼图所示的例子,数据集中 0 类和 1 类之间的比例为 1:10。我们希望建立一个分类模型,根据一些特征来预测 0 或 1。
在这种情况下,选择准确度作为性能度量(例如,总预测中正确预测的比例)可能导致训练连续预测最频繁类别的虚拟模型。对于这种模型,混淆矩阵将由下式给出:

班级失衡情况下虚拟模型的混淆矩阵(图片由作者提供)
根据精度公式,我们可以看到我们的虚拟模型可以提供 0.9 的精度:

准确率评分(图片由作者提供)
这种数字在理论上可能听起来不错,但来自一个实际上解释不了太多的模型,并且在新数据上概括得很差。为了防止这种行为,通常在实践中根据几个度量标准来评估模型。例如,通过计算 ROC AUC 得分(在这种情况下为 0.5),可以很容易地排除这种极端情况。
但是为模型评估选择一个好的度量标准从来都不是显而易见的,尤其是在类不平衡的情况下。从源头上解决阶层失衡有助于消除这种额外的担忧。
使用默认分类阈值可能会导致性能下降
执行模型评估时,分类阈值经常被遗忘,但它提供了额外的自由度,可以帮助调整已训练的模型,以达到所需的性能。
分类模型的原始输出通常是 0 和 1 之间的连续值,表示结果的概率。基于所选择的分类阈值,它被转换成类别 0 或类别 1。阈值默认设置为 0.5,该值通常将理论模型输出除以二(大多数 ML 模型通常假设正态性,尽管在该假设不成立的情况下一些模型足够稳健):

平衡训练集的模型输出和默认分类阈值的示例(图片由作者提供)
基于不平衡数据集训练模型会导致非常扭曲的输出分布。在这种情况下,使用分类阈值的默认值可能会导致性能下降。

不平衡训练集的模型输出和默认分类阈值示例(图片由作者提供)
甚至像随机森林这样的非概率模型也仍然依赖于这样的假设,即用于执行 bootstrap 聚合的抽样是有代表性的。在不平衡的类的情况下,这种假设不一定成立,并且会导致较差的整体性能。
在偏斜的输出分布的情况下,可以从 ROC 曲线或精确召回曲线计算“最优”分类阈值,如这里的所示,或者通过定义自定义网格搜索分数,这是我已经讨论过的主题这里的
平衡班级不是免费的
我们已经看到了处理不平衡数据的重要性,但是将这种转换应用于数据并不一定没有后果。根据选择的方法,它可能会引入偏差,导致过度拟合或删除重要信息。
在平衡了类之后,我们可以检查整体模型性能是否受到所使用的平衡技术的太大影响。对于我之前考虑的例子,不平衡数据(红色)的 ROC 曲线几乎与平衡数据(绿色)的 ROC 曲线重叠,这意味着增加的健壮性在性能上几乎没有损失。

如果平衡数据集获得的结果好得多或太好,则可能出了问题。一个常见的错误是在训练和测试中分割数据集之前或交叉验证之前进行过采样(有关示例和更详细的解释,请参见此处的)。这导致数据泄露和评估指标不可信。
这篇文章的目的是说明类不平衡对训练模型和一些评估指标的影响。数据科学家已经在世界各地使用不同的方法来处理类别不平衡,但没有一种方法是免费的,它们对模型性能的影响需要正确评估。
为什么我们应该在数据可视化中使用动画
原文:https://towardsdatascience.com/why-we-should-use-animation-in-data-vizualisation-6004fb400014
3 个有证据支持的理由,让数据充满活力

来源:作者
数据可视化中的动画在某种程度上是一个肮脏的词,作为一个数据可视化纯粹主义者讨厌的主题,它位于 3D 条形图之上,饼图之下。

爱他们或恨他们,饼状图在这里停留。来源:作者
诚然,滥用动画来可视化数据会造成混乱,并给我们的工作记忆带来压力。
然而,如果处理得当,移动可以给数据可视化增加一些其他方式无法复制的东西。在这篇文章中,我提出了 3 个理由,说明为什么你应该用 motion 让你的数据栩栩如生。
1.约会
21 世纪的所有媒体都在尖叫着吸引我们的注意力。竞争激烈,内容无处不在。这导致内容提供商采取越来越复杂的策略来吸引和保持我们的注意力。
商业世界也不例外。工具使得创建和分享内容变得越来越容易。然而,有点矛盾的是,这使得个人内容很难从任何给定组织中不断增加的“数字噪音”中脱颖而出。这种噪音会导致失去参与。人们可能会对信息变得麻木,即使这些信息很有见地、信息丰富,纯粹是因为它们没有吸引力。
那么,对于数据可视化来说,为什么这很重要呢?答案很简单;参与是关键,因为无法吸引用户的数据可视化也无法传达任何信息。
在吸引终端用户方面,企业可以从社交媒体巨头那里学到很多东西。2016 年,Twitter 对其数据进行了内部分析。他们发现媒体显著增加了内容的参与度。视频被转发的可能性是照片的 600%。[2]
视频被转发的可能性是照片的 6 倍,是 gif 的 3 倍。
这并没有告诉我们为什么视频比静态内容更突出。我们可以假设这样的媒体是
- 更容易让我们消化;
- 建议更高质量的内容(基于视频需要更多时间投入的事实);和
- 从其他格式的“噪音”中脱颖而出。
微软的研究更进了一步,表明在数据可视化方面,motion 更吸引用户,因为它更“有趣”。在一项比较静态图表和动画图表的研究中,相比静态图表和图形,参与者更喜欢动画可视化。“用户反复反映动画状态‘有趣’或‘令人兴奋’,或者有一次‘感人至深’。”"[3]
2.情绪
情绪对我们的认知过程有着实质性的影响,包括感知、注意力、学习、记忆、推理和解决问题[4]。为了创造一个真正强大、持久和难忘的体验,我们必须与用户的情感互动。
动画可以用静态图表无法做到的方式做到这一点。即使是无生命的物体的动画也能让它们活起来。它可以创造更高层次的意义,包括因果关系和拟人。
为了理解这到底意味着什么,有必要看看下面这段 1944 年的海德尔-齐美尔错觉视频:
海德尔和西美尔 1944
尽管制作于近 80 年前,这个视频仍然能够对三个无生命的几何形状产生同样的情感依恋。
与最初(和随后)实验中参与者表现出的情感依恋一样令人惊讶的是解释的一致性。参与者看到相同的故事展开,并在描述场景时将人类特征归因于这些形状[5]。事实上,我们发现这样的图像很容易解释,这突出了它们向观众传达信息的能力。
3.信息
作为人类,我们天生就能感知运动。这基本上是一种生存机制,能够快速发现向我们移动的捕食者可能意味着生与死的区别。有趣的是,运动是这种认同的关键部分。
我们都经历过这种情况,就像当人群中的朋友开始向我们挥手时,我们更容易认出他们一样,一只被伪装掩盖的隐形动物一旦开始移动,就会立即失去隐形能力。
一旦开始移动,伪装成隐形的动物会立刻失去隐形能力。
最近的研究证实了我们识别移动物体的效率,即使是在“嘈杂”的背景下。通常,我们可以在 30 毫秒内识别这种情况下的对象[6]。
鉴于数据可视化的整个领域都致力于以可视化的方式呈现信息,使我们能够快速有效地处理信息,动画提供了一个强大的工具来“利用”我们对运动物体识别的敏感性。
结论
数据可视化中的运动在很大程度上被该领域的专家所回避。这意味着失去了一个机会;有明确的证据表明,动画可以增加参与度,与观众建立情感联系,并以更有效、更丰富的方式传达信息。
下次你需要可视化数据的时候,不要忘记用运动来赋予它生命!
进一步阅读
如果你对数据可视化中使用运动的例子感兴趣,我在下面附上一些我最喜欢的。如果你有更多的例子要补充,请评论。
- 汉斯·罗斯林 200 年内去了 200 个国家就是一个很好的例子。它已经被复制了很多次,但从来没有像汉斯那样以激情和表现力闻名。
- 条形图竞赛被描述为数据可视化的“坐立不安的旋转器”。他们是动画如何吸引观众的一个很好的例子。还不清楚是谁创造了第一个这样的例子,但是全球品牌的演变被认为是第一个病毒式的例子。
- 这个欧洲和美国之间航班的例子正在记忆中。用静态图表不可能以同样的方式重新创建它。
参考
[1] 视觉数据传播的科学:什么在起作用。2021 史蒂文·l·弗朗科内里等人。
[2] 推特内部数据分析 2016
[3] 动画在趋势可视化中的有效性 2016,乔治·罗伯逊等。
【4】感性因果论与兽性。2017 年,布莱恩·J·舒拉,帕特里斯·D·特雷穆特
[5]f . Heider 和 m . Simmel(1944)对表观行为的实验研究。美国心理学杂志,57,243–259 页
[6] 空间抑制促进运动物体的快速图形-背景分割,Nature Communications 2019,Duje Tadin 等
为什么我们永远不会打开深度学习的黑匣子
原文:https://towardsdatascience.com/why-we-will-never-open-deep-learnings-black-box-4c27cd335118

作者图片
但是我们仍然可以把整件事炸掉
尽管关于深度学习的黑盒有很多争论,但很少有人谈到为什么深度神经网络首先是黑盒。当我们说是黑匣子的时候,又是什么意思呢?这真的是很难的数学吗,还是有什么东西使得它们本质上无法解释?有什么方法可以训练深度神经网络,让它不是黑盒?
黑盒是什么?
在最基本的层面上,“黑盒”只是意味着,对于深度神经网络来说,我们不知道所有单个神经元如何共同工作以达到最终输出。很多时候,甚至不清楚任何特定的神经元自己在做什么。
深度神经网络中的每个神经元从其他神经元接收一组输入,将每个输入乘以它在训练期间学习的权重,然后计算输出(通过将它传递给非线性激活函数)。众所周知,这在数学上等同于在前一层神经元的空间中绘制线性决策边界(例如,2 维中的线或更高维中的超平面)。当考虑多个神经元如何一起工作时,黑箱就出现了。
让我们看看这个非常简单的问题是什么样子的。我们有一个包含蓝色和红色样本的训练集。每个样本有两个输入值, x 和 y,我们可以在下面看到。

训练样本,每个样本由两个值(x,y)组成,标记为蓝色或红色
然后,我们训练一个简单的神经网络,它可以准确预测所有这些训练样本的蓝色或红色(该网络有 2 个隐藏层,每个隐藏层有 4 个神经元)。我们可以通过根据网络的输出给背景着色来表示网络学到了什么。

经过训练的神经网络的预测在背景中是彩色的
它能合理地预测所有( x , y )对的标签,甚至在训练样本之外。现在我们来看看第一层的神经元在做什么。我们可以把他们的决策边界画成线。

单个神经元无法解释
它们没有多大意义。观察这些神经元的决策边界,我们发现了几个谜团。这些行与训练样本有什么关系?这些线路与网络输出有什么关系?而它为什么要这样学呢?
这个就是我们所说的“黑匣子”。
黑盒的存在是由于中间神经元在做出网络最终决定的过程中做出了这些奇怪的决定。不仅仅是复杂的、高维的非线性数学;黑匣子本质上是由于非直觉的中间决策。
为什么会有黑匣子?
考虑每个神经元在训练过程中必须学习什么。较浅的神经元学习如何从输入中提取特征,并将它们映射到一些有用的潜在空间。更深层的神经元学习如何将训练样本从潜在空间映射到适当的输出。
然而,所有的神经元同时被训练。一开始,他们是随机开始的。较浅的神经元开始训练,以开发一个对较深的神经元当前正在做的事情有用的潜在空间。同时,更深层的神经元必须学会如何从这个最初糟糕的潜在空间映射到适当的输出。
虽然每个神经元都在学习做自己的工作,但它会因其他神经元的不良表现而受到削弱。他们学会补偿其他神经元的缺陷,这发展成一个相互依赖的可悲的缠结网络。
为了看到这一点,让我们看看上面例子中的四个第一层神经元。在训练结束时,我们可以通过计算损失函数相对于每个样本的神经元输入权重的梯度,来测量每个训练样本对每个神经元的决策边界的位置有多大影响。

四个第一层神经元中的每一个,每个训练样本的大小与其对神经元位置的影响程度成比例
最负责设计每个神经元的训练样本分布在输入空间中,并且不反映训练样本的明显结构。由于通过梯度下降学习的纠缠性质,每个神经元都做一点点事情,以处理它从前一层接收的混乱。结果是,不是每个神经元都有明确的功能来反映它试图学习的任务的结构,而是它的功能被涂掉了。这导致了黑箱,每个神经元形成看似无意义的决策边界。
这些纠缠的潜在空间的另一个结果是无法远离原始训练样本进行外推。我们可以缩小这个红蓝问题,看看神经元决策边界的混乱如何在训练样本的领域之外产生意想不到的、不良的行为。

缩小显示无法解释的隐藏神经元是造成推断不佳的原因
很明显,在遥远的外推法中,网络的行为渐近于基于神经元决定边界的几何形状。隐藏神经元的不可解释性是深度学习无法进行外推和概括的直接原因。在更高的维度上,这产生了奇怪的决策边界,导致了对抗性攻击的现象。
炸毁黑匣子
基于梯度的深度学习如果没有可解释性、泛化和对抗鲁棒性,永远无法实现真正的人工智能。我们不应该试图打开一个无法打开的黑匣子,而是需要将整个事情炸毁,并找到一种更类似于人类推理的深度学习的替代训练方法。即使是传统的基于梯度的深度学习的创始人之一 Geoffrey Hinton,也说过同样的话。
麦卡洛克和皮茨最初提出的人工神经元实际上是为了模仿简单的人类逻辑[1]。当我们开始依赖梯度下降来训练神经网络以解决更大、更复杂的问题时,我们只是被困在了黑盒中。
可解释的深度学习会是什么样子?对于上面的红色对蓝色问题,似乎最直观的神经网络将使用具有如下所示决策边界的神经元。

一个理想的神经网络的输出和第一层神经元的蓝色与红色的任务
现在,神经元对于训练样本有意义,并且很容易看出它们如何与网络的最终输出相关。网络进行推断是因为神经元学习了可解释的和可概括的决策边界。但是梯度下降怎么学这个呢?
它不能。
如上所述,梯度下降训练迫使神经元补偿其他神经元的不足,在多个神经元之间涂抹简单的功能。它没有教导神经元在网络中具有专门的和/或模块化的子功能。
因为神经元通过决策边界形成分离,理想情况下,它们会学习基于概念的结构来分离训练样本。这是最近提出的一种替代方法的基础,该方法用于训练模仿人类推理的可解释的深度神经网络【2】。
在这种称为本质神经网络(ENNs)的深度学习新范式中,每个神经元都承担一些专门的功能(即分离训练样本的两个子群或区分不同的学习特征),同时仍然能够扩展到大型问题,如计算机视觉和复杂的逻辑推理问题。
当我们抛弃梯度下降并使用更类似人类的方法来训练深度神经网络时,黑箱立即消失。通过赋予可解释性,我们使深度神经网络能够进行推断和概括,并且对敌对攻击更加鲁棒。这种新形式的神经符号人工智能正在带来许多新的能力,这些能力以前是传统的基于梯度的深度学习所不可能的。
黑盒不是神经网络的必要特征。随着我们超越反向传播和梯度下降,深度学习将回到它的起源,并再次模仿人类推理,而没有不必要的黑盒神秘主义。
参考文献
[1] W.S .麦卡洛克& W .皮茨,“神经活动中固有观念的逻辑演算”。数学生物物理学通报 5 ,115–133(1943)。
[2]布拉塞克和林明敏。“模拟推理的可解释神经网络”。自然计算科学 1 ,607–618(2021)。
[3]布拉塞克。《亚里士多德如何修复深度学习的缺陷》。渐变 (2022)。
为什么要为机器学习问题编写解决方案描述
完成开发机器学习模型。嗯,还没有完成

杰森·古德曼在 Unsplash 上的照片
你已经解决了一个机器学习问题。你的模型的准确性是令人敬畏的。你完了。等等!没那么快!
到目前为止,你的工作可能是一个 Jupyter 笔记本,其中充满了代码,一些视觉效果,很少的文档。如果你在一个月左右后看到你的作品,你可能会很难理解你自己的创作。更糟糕的是,Jupyter 笔记本没有你在解决方案中所做的所有决定和假设。
将您所做的惊人工作制作成解决方案描述文档是一个很好的做法。这种文件有以下好处:
- 无论说了什么,做了什么,一份写得很好的文档比一本笔记本更容易理解。
- 你可以记录下为什么你采取了一种特殊的方法。它有助于把你的解决方案放在一个角度。
- 它可以帮助运营团队理解您的解决方案。通常,运营或 IT 团队会对他们更了解的解决方案感到满意。
我举个例子说明一下。就拿 Kaggle 房价预测问题(https://www . ka ggle . com/c/House-prices-advanced-regression-techniques)来说吧。这是 Jupyter 笔记本中解决方案的快照。

Jupyter 笔记本中解决方案的快照图像(图片由作者提供)
现在,让我们看看如何写一个解决方案描述所做的工作。解决方案描述是记录您如何解决给定问题的一种方式。该文档可以有三个主要部分:
业务目标:这部分解释了问题,以及为什么需要机器学习来解决问题。
解决方案总结:这部分有解决方案的总结。它可以是一个列出主要步骤的表格。查看汇总表是快速了解解决方案的好方法。
解决方案细节:这一部分简要描述了解决方案、所采用的假设以及解释该方法的视觉效果。
我举个房子预测问题的例子来说明一下。
商业目标
目标是预测爱荷华州埃姆斯的住宅价格。有 79 个变量与房子有关。人类不可能确定如此多的变量和价格之间的关系。所以我们采用机器学习的方法。
解决方案摘要
该摘要以简明的方式列出了解决方案步骤,如下所示。

解决方案摘要(图片由作者提供)
如表格有助于以非常简洁的方式清晰高效地记录解决方案。你可以更进一步,为你解决的每个问题做一个标签。这也将让你了解你通常如何处理机器学习问题,重用你的方法以及在你自己的游戏中改进。
解决方案详细信息
本节通过解释和视觉效果来描述解决方案。它包含了重要的步骤以及你所做的假设。在房价预测问题上,我们总结了数据处理、特征工程、机器学习方面的解决方案。
数据处理
剔除目标变量中的异常值:观察价格和其中一个输入变量居住面积之间的散点图,我们观察到一些价格是异常值。我们可以通过删除它们来移除它们。

房价-异常值剔除(图片由作者提供)
移除目标变量中的偏斜:目标变量价格偏斜。所以我们可以用对数变换 来定价,以使其呈正态分布

销售价格—消除偏差(图片由作者提供)
估算缺失值:房价数据集有许多缺失值。丢失值非常多的要素有:游泳池、其他要素、小巷、栅栏、壁炉、前区。

缺少值的要素(按作者排序的图像)
这是估算缺失值的方法。
池:我们假设缺少值意味着没有池。这是一个很好的假设,因为大多数房子没有游泳池。我们用 NONE 替换丢失的值。
杂项、小巷、栅栏、壁炉:我们可以使用与 Pool 类似的方法,用 NONE 替换缺失值。
基底表面积特征:我们假设缺失值意味着没有基底。所以我们用零来代替它。
地块面积:由于与该房产相连的每条街道的面积很可能与其邻近的其他房屋面积相似,我们可以通过邻近地块面积的中位数值来填充缺失值。
MSZoning(一般分区分类):缺失值百分比为 0.13%,非常少。所以我们可以用最常见的值来填充缺失的值,也就是‘RL’。
电气、厨房质量、外观、销售。Type: 我们遵循与 MSZoning 相同的方法,因为缺失值的百分比非常小。我们用最常见的值替换它。
特征工程
这里我们应用一些“常识”和“创造性”的特征工程。
转换一些真正分类的数值变量:数据具有建筑类别(MSSubClass)、整体状况(OverallCond)、销售年份(YrSold)、销售月份(MoSold)等特征。虽然它们有数值,但它们实际上是分类的。所以我们可以把它们转换成非数值。我们将能够一次性编码它,而不是将其规范化。
能够表示有序集的标签编码特征:在数据集中,存在与质量相关的特征。例如壁炉质量、地下室质量、车库质量等。低值表示低质量,高值表示高质量。这意味着这些值代表一个排序:从最低质量到最高质量。这些特征是应用标签编码并将其转换为 1、2、3 等的良好候选
组合特征:我们可以创建一个名为总表面积的新特征,它是地下室总面积+一楼总面积+二楼总面积。
将高度偏斜的特征转换为正态分布:类似于消除目标可变价格的偏斜,正如我们在上面看到的,我们也可以将高度偏斜的特征转换为正态分布。对于价格,我们应用了对数变换,因为价格中的值是高值。但是,倾斜的输入要素大多与公摊面积、地下室面积、地块面积等区域相关。由于面积值在数值上不是很高, box-cox 变换是一个很好的方法。
机器学习
在这一部分,我们将看到机器学习的解决方案。
交叉验证策略:决定交叉验证策略是机器学习模型训练首先要做的决定之一。在解决方案中,我们将随机选择 K 倍(K=5)。
精度指标:由于这是一个连续值预测问题,精度指标为 RMSE(均方根误差)。
基础模型:基础模型由应用不同的机器学习算法组成:Lasso 回归、弹性网回归、核岭回归、梯度推进回归、XGBoost 和 LightGBM。

基本模型精度(图片由作者提供)
集合模型:这种方法结合了这里描述的多个模型。
所有基础模型的平均值:一种方法是取所有基础模型的平均值来生成最终价格预测,如下图所示。

所有基本型号的平均价格预测
集合平均模型的 RMSE 为 0.1081,优于基本模型。
所有基础模型的堆叠:在这种方法中,我们对模型的一个折叠进行预测。然后,这些预测用于训练另一个模型,该模型用于进行测试数据预测。

所有基本模型的堆叠
这种方法得到的 RMSE 为 0.07,比平均方法好。
结论
制作这样的解决方案文档是一种以简洁明了的方式获取知识产权的好方法。向他人解释你的解决方案以及理解你自己的解决方案将被证明是有效的。
额外资源
网站(全球资讯网的主机站)
你可以访问我的网站进行零编码分析。T3【https://experiencedatascience.com】T5
媒体订阅和推荐链接
请 订阅 每当我发布一个新的故事时,请保持通知。
您也可以通过我的 推荐链接 加入 Medium
Youtube 频道
这是我的 YouTube 频道
https://www.youtube.com/c/DataScienceDemonstrated的链接
为什么你实际上不需要一个反向 ETL 平台
原文:https://towardsdatascience.com/why-you-dont-actually-need-a-reverse-etl-platform-bee9366b5ecb
你只需要一个强大的数据管道系统

Photo by 愚木混株 cdd20 on Unsplash
如果你熟悉数据管道产品,你很可能会遇到一些自称为反向 ETL 平台的产品。其他人可能不接受这个标签,但是承认他们支持这个功能。
那么,逆向 ETL 是怎么回事呢?它需要成为一件东西吗?如何在数据栈中实现反向 ETL 呢?
在这篇文章中,我认为这三个问题的答案是:
这是任何好的管道系统都应该具备的能力。
不,如果您有一个架构良好的集成堆栈。
而且,
专注于能够捕获和操作数据的健壮、灵活的数据管道,不要担心什么是向前的,什么是“反向的”
和往常一样,为了搭建舞台(还有,咳咳,为了安抚万能的算法),让我们从一些定义开始。
什么是反向 ETL?
在我们深入研究反向 ETL 之前,我们必须先谈谈原始(正向)版本,以及与之密切相关的 ELT 概念。
最简单的定义是,ELT、ETL 和反向 ETL 最终都是数据管道。他们的工作是将数据从系统 A 转移到系统 B,并在此过程中应用转换。“E”代表“提取”,“T”代表“转换”,“L”代表“加载”。
ETL 和 ELT 的概念都偏向于管道的接收方面。也就是说,数据生命周期的一部分,其中数据从外部位置提取到组织的统一真实来源中。通常,这是一个数据仓库或数据湖。
ETL 和 ELT 之间的区别充其量是模糊的— 参见我的帖子了解更多。但是一个快速的经验法则是,ELT 通常被视为更现代的框架。它的目标是让数据相对不变地进入数据仓库或湖。在那里,你可以随心所欲地分析和操作它。
许多分析工具直接处理仓库中的数据,比如 dbt 和 BI 工具,如 Superset 或 Looker。但是最终,数据将需要被操作化。它必须以适当的格式被输送到需要它的地方,并投入使用。
这是你经常听到的反向 ETL 的卖点。
反向 ETL 被定义为“将数据从数据仓库转移到第三方系统以使数据可操作的过程。”
数据操作化的重要性
数据操作化是向系统提供准确、及时的数据的过程,数据可用于推动行动。
这是数据生命周期的关键部分,它需要来自数据存储系统的输出。
运营数据的例子不胜枚举。以下是几个例子:
- 使用客户行动数据加快技术支持体验
- 在仍有时间防止失去风险客户或销售线索的情况下识别它们
- 基于与在线商店的互动定制超个性化营销
- 监控产品和平台的使用,以确保应用程序的健康,并为用户提供个人消息和指标
您会注意到关于这些用例的两件事。
首先是他们相对较快地需要新数据。
第二个原因是,它们主要是由从事而非严格意义上的数据相关工作的人驱动的。数据人员喜欢将所有这些人归为“业务人员”这一大类更具体地说,他们从事营销、销售、客户服务、客户宣传和管理等工作。
这些专业人员拥有广泛的技术技能水平,他们需要的不仅仅是访问可操作的数据。他们还需要对它有很大程度的控制,因为,当然,环境是不断变化的。
这意味着对于数据操作化工具来说,广泛可用和 UI 向前尤其重要。
我们稍后将回到这一点。
批处理数据范式如何创建反向 ETL
因此,我们现在已经确定,我们需要将数据从存储中取出,并对其进行操作。这是毫无争议的。
但是为什么我们需要一整套独特的工具来做这件事呢?这与任何其他类型的数据管道有何不同?
简短的回答是:不是。反向 ETL 是一个简单的模型,它起源于以批处理为中心的范例和数据仓库作为唯一真实来源的概念。
批量数据范例
批量数据处理是指在给定的时间间隔内对数据执行操作,如接收或转换数据。因为这并不反映事件在现实世界中是如何发生的,所以这种类型的处理在数据管道中引入了一个滞后——新事件直到下一次批处理运行时才会反映出来。
这与实时处理或事件流形成对比,在实时处理或事件流中,拾取的每条新数据都会触发一个事件,该事件通过数据管道连续流动。更多的背景,你可以查看我在批处理 vs 实时数据处理的帖子。
批处理一直被认为是一种更容易实现的方法。一般来说,公司使用批处理在内部建立管道。最近,开箱即用的 ELT 提供商被广泛采用(例如 Fivetran)。这些更容易使用,但仍然依赖于批处理。
数据仓库是事实的唯一来源
您有时会在当今的数据架构中看到一种模式——也是逆向 ETL 假设您遵循的模式——将数据仓库作为事实的中心来源。从其他系统获取数据到数据仓库进行分析有很高的优先级。
这在多个层面上都存在问题。
第一个问题是,当与我们刚刚讨论的批量接收方法结合使用时,数据仓库如何成为陈旧、停滞数据的温床。使用这些类型的管道,数据不会流入仓库;它在那里着陆,当它着陆时,它已经有点过时了。
现在,这对于探索性的分析和转换来说是可以接受的。毕竟,这就是数据仓库的设计目的。
当您还试图操作来自数据仓库的数据时,这个真正成为一个问题。
数据仓库与操作数据库非常不同。你可以在这里阅读更多关于差异的内容,但最终,当你想要人类或系统可以快速处理的新鲜数据时,你应该从为此目的而设计的运营数据库中获取数据。
如果您将批处理管道和数据仓库放在操作数据库和目标系统之间,那么您已经为失败做好了准备。
反向 ETL 平台在将数据从仓库中取出来方面有多好并不重要,即使它是一个实时管道也不重要,因为仓库中的数据已经过时了。
当然,记录的体系结构和系统有很多变化。无论如何,来自批处理管道终止的延迟不能在下游取消。

从仓库批量接收和反向 ETL 的栈的简单示意图。按作者。
“只是另一个数据管道”
一些构建管道平台的公司倾向于“逆向 ETL”的标签其他人对此持怀疑态度。这是有意义的,因为正如我们所看到的,围绕反向 ETL 的对话倾向于转向糟糕的架构模式领域。
数据管道平台 Rudderstack 的首席执行官 Soumyadeb Mitra 写了一篇关于逆向 ETL 技术细节的文章。
他的文章中有几个要点值得一提:
- 一个好的管道平台应该能够将数据传入和传出存储器,并传到各种外部系统,因此没有必要进行区分。
- 实时管道(或事件流)在数据生命周期的所有阶段都很重要。实时和批处理的划分是很多问题开始出现的地方。
我想再详细阐述一下第二点。
统一的实时数据管道使得反向 ETL 变得不必要
假设您是一家拥有在线商店的公司,您正在执行分析,目标是提高您的销售和营销工作。您在几个 SaaS 工具中拥有客户数据,并且您可以使用 ELT 工具轻松地将这些数据传输到您的仓库中。
至关重要的是,您还拥有客户与您的在线商店互动的事件日志。您希望这些事件实时到达仓库,所以您不辞辛苦地与 Kafka 建立了一个基于事件的管道。
通过一些操作,您可以在您的仓库中一起探索和分析所有这些数据,并为一些准备好投入使用的数据产品建模。
现在怎么办?
多亏了卡夫卡,你有了一个低延迟的数据流,这很棒,但它终止于仓库。动态数据变成了静态数据,受到批处理数据管道和数据仓库的限制。现在,你的数据仓库是你最好的事实来源。阻力最小的途径是购买一个逆向 ETL 产品,然后就此收工。
但是有一个更好的方法:用一个管道平台统一您的数据系统,这个平台首先基于事件,但是也可以处理批量数据。当然,通过管道将数据传输到仓库进行探索性分析,但是直接从数据源操作它,并即时应用任何您需要的转换。被操作化的数据和你分析的数据是一样的,但是它不需要通过到仓库。

实时数据管道如何连接堆栈各种组件的简单示意图。按作者。
这是可能的,因为正如 Mitra 指出的,您可以将表格数据(您通常批处理的数据)建模为事件流,并在以后重建该表。但反过来就不对了。
现在,我们到了困难的部分。在今天的环境中,创建这种基于事件的数据集成是一个巨大的技术障碍。
像 Materialize 这样的实时 SQL 平台可以为实时数据分析提供一种可行的方法,尽管这实际上增加了一种新的数据存储类型,但仍然需要额外的工作才能完全集成到整个堆栈中。
Kafka,作为最受欢迎的事件总线和一个无人化的框架,可能是一个解决方案——对于一些具体的例子,查看 Kai Waehner 关于反向 ETL 如何成为反模式的帖子。
但是 Kafka 很难,成功使用它往往需要你集成多个第三方连接器和服务。
而且,正如我们前面提到的,数据操作需要可访问和透明,否则您可以剥夺“业务人员”的权力,他们的工作职能需要可靠的数据基础。
理想情况下,您想要的是一个事件驱动的实时管道框架,它提供工程能力,也提供业务人员可以利用的一个直观的、UI 向前的应用程序。
反向 ETL 的更好选择
你可能读过最后一段,然后想:好吧,这很好,但是它存在吗?
“反向 ETL”的趋势是一个有趣的趋势,因为它既可以鼓励你对糟糕的架构贴上创可贴,或当公司的真正使命是改善整体数据集成时,它可以是一个营销术语。
简而言之,这些管道是绝对可能的,而且已经开始出现了。就目前而言,任何潜在客户的工作都是阅读公司使用的营销术语之外的内容,并了解他们实际上支持什么样的架构。
在我的公司 Estuary,我们的平台是围绕实现实时数据访问民主化的使命而构建的。我提出这个问题是因为我们选择这个任务的原因与此特别相关。
我们相信该行业正处于向实时数据转变的风口浪尖,我们很高兴成为其中的一员。
拥有一个实时数据中枢是解决大量基础设施问题的方法。从技术上来说,这是完全可能的,但你不会看到它开始接管,直到它变得比旧的替代品更方便。
幸运的是,作为一个行业,我们正处于风口浪尖。
本文原载于 河口博客 。
你可以在LinkedInSlackGitHub上和我以及我的整个团队联系。**
如果您是数据科学的初学者,为什么现在必须学习 GIT
它能救你的命:相信我!

穆罕默德·拉赫马尼在 Unsplash 上的照片
相信我,oding 会让你陷入真正的困境。如果你是一个初学者,现在你正在写你的第一行代码,也许你正在处理警告和代码错误;我猜对了吗?
随着你的学习和实践越来越多,你将会处理项目,不幸的是,会处理更大的问题。尤其是如果你正在使用 Jupyter 笔记本电脑,因为你想成为一名数据科学家,对吗?所以,从笔记本开始!——细胞可以彼此独立运行的事实会给你带来麻烦和各种问题。
这里有一个事实:假设你已经工作了几个小时——也许几天!—有些事情进行得很糟糕,以至于你不得不把你的项目扔进垃圾箱,从头再做一次。这将是一个真正的遗憾,不是吗?
这就是 GIT 的用武之地;但在说之前,我想告诉你我在数据科学学习之初是如何理解 GIT 的重要性的。然后,我会告诉你为什么 GitHub 对你很重要,如果你是一个初学者,为什么你应该现在就学习它(如果你不是初学者,我假设你知道如何使用 GitHub,不是吗?😃 ).
我如何理解 GIT 的重要性(以及 GIT 的基本工作原理)
我做了两年的机械设计师,设计复杂的机械零件。为了给你一个思路,我设计了可以有几十个甚至几百个子部件的组装部件。
由于市场变化非常快,我们被要求尽快设计零件;但是,你知道客户:他们经常在你工作的时候(或者工作结束后)要求改变。);你能想象把帐篷里的 4 个零件变成上百个零件的组合意味着什么吗?
嗯,是的:CAD 软件可能会爆炸(事实上,有时它会发生!).是的:如果 CAD 在工作时间后爆炸,你可能会损失几个小时的工作时间;除非你有救生圈。
幸运的是,我曾为一家公司工作,这家公司的 CAD 软件中集成了救生圈。简单来说,这就是救生圈能做的:
- 它将设计好的零件保存在本地服务器中
- 它允许自动保存到本地,所以如果 CAD 崩溃了,你可以取回你做的工作(万岁!)
- 它让你有可能“冻结”项目的一个版本(也就是装配和所有子部件),如果需要的话,你可以返回到以前的版本(这就是神奇之处!!)
所有这些东西真的拯救了我的生活和工作。但是,你知道吗?这实际上是 GIT 版本控制系统允许您做的事情!
当我体验到失去工作时间的感觉时,当我开始学习数据科学并熟悉 GIT 的概念时,我立即决定暂停我的数据科学学习来学习和实践 GIT,因为我不想在未来失去编码时间。
因此,GIT 所做的基本上非常简单:它将您的代码(也就是您的 Jupyter 笔记本中的数据科学项目、包含数据的 CSV、文件夹以及您需要的任何东西)保存在一个“特定的文件夹”中,称为“存储库”。这个特定的文件夹是“git 启动的”(当你输入命令$ git init),这允许你控制你的软件版本。
此外,除了对你的软件进行版本控制之外,如果你的代码变得一塌糊涂,你还可以回到以前的版本,你还可以发布你的代码。这意味着你可以在任何机器上克隆(用$ git clone)一个现有的存储库,例如在你的一个同事的计算机上,但是你是唯一可以修改项目的人(除非你把权限给你的一些同事)。
为什么您应该现在就开始学习 GitHub
我相信有两个主要原因可以让你今天就开始学习 GitHub 的工作原理:
- 首先是版本控制。我希望我已经说服了你:如果你的代码变得一塌糊涂,版本化你的软件将使你免于从 0 开始(或者,几乎从 0 开始),给你从先前版本重新开始的可能性。
- 第二,在 GitHub 上你可以创建任意多的库,这给了你创建自己的文件夹的可能性。这意味着,当你创建数据科学项目时,你可以将它们存储在本地(在你的 PC 上)存储库中,然后加载到 GitHub 上,这样你就可以向全世界展示它们。此外,GitHub 给了你托管一个网站的可能性;通常,我们主持我们的投资组合。举个例子,下面是我的。当你点击我制作的各种项目时,你可以看到它们都存储在我的 GitHub 上(另外,我为每个项目创建了一个解释性 PDF,这样即使你现在不是数据科学领域的专家,你也可以理解)。
学习 GitHub 有多难?嗯,功能和主要命令非常容易学习:我想我在 3-4 个小时内学会了(如果我做到了,你也能做到!).
你甚至会找到一些教你如何开始使用 GitHub 的在线课程:选择一个,它肯定会对你有帮助。
另外,根据我的经验,我想给你更多的建议:
- 从 GitHub 桌面开始。我已经使用命令行开始使用 GitHub 没什么不好的,但是我劝你用 GitHub 桌面(这里可以从下载)因为更人性化;而且,这也避免了你犯一些错误。
- 因为你在本地工作(你在你的 PC 上开发你的代码),你必须将你的本地机器连接到你的 GitHub 账户来加载 GitHub 上的库。GitHub 为您提供了以安全的方式连接两者的可能性,实现了 SSH 密钥。在本文中,我创建了一个指南来帮助你完成这些步骤(请注意:本指南的最后一部分旨在使用命令行来使用 GitHub:你可以跳过它,因为我建议你使用 GitHub Desktop)。
结论
在本文中,我们已经看到了 GitHub 的重要性;在我看来,如果你是一个初学者,你应该现在就学习它,主要有两个原因:
- 它给了你版本化你的代码和你的项目的可能性,在任何类型的问题发生时节省你的工作时间
- 如果你在找工作,它让你有可能创建一个公开的投资组合展示给人力资源和经理
剧透提醒:如果你是一个新手或者你想学习数据科学,并且你喜欢这篇文章,那么在接下来的几个月里我将开始辅导像你一样有抱负的数据科学家。我会在接下来的几周告诉你我什么时候开始辅导,如果你想保留你的座位… 订阅我的邮件列表 :我会通过它和在接下来的文章中传达我辅导旅程的开始。
考虑成为会员:你可以免费支持我和其他像我一样的作家。点击 这里的 成为会员。
我们一起连线吧!
LINKEDIN(向我发送连接请求)
为什么必须使用 Plotly Express 进行数据可视化
原文:https://towardsdatascience.com/why-you-must-use-plotly-express-for-data-visualization-9d47f9182807
这个可视化库提高了您的工作效率,并提供了更好的见解

在这篇文章中创建的仪表板。图片由作者提供。
Plotly Express
Plotly 是一家来自加拿大的公司,基于 JavaScript、D3.js、HTML 和 CSS 创建动态图形库。他们的库可用于 Python、R、Julia 和其他编码语言,我见过的用于创建仪表板的最强大的工具之一是他们的 Dash 库。
然而,Dash 的使用并不简单,因为您必须具备一些 HTML 和 CSS 的基础知识才能更好地使用它。这并不难,只是更复杂,需要更多的学习时间来适应语法。
知道这一点,也愿意获得更大的市场份额,Plotly 在几年前推出了 Plotly Express。这是一个低代码和易于使用的 Dash 图形版本。在我看来,它是 Python 可用的最棒的图形库之一,特别是因为你可以与数据交互,使你的分析更加动态,并获得比静态图形更有趣的见解。
也就是说,这个论点是我为什么在执行探索性数据分析时应该选择 Plotly express 的主要原因。与数据进行交互,能够只选择数据的一部分,放大数据的某些部分,而且视觉效果非常好,这将对您分析和呈现数据有很大帮助。
Plotly Express 101
说比做更好。所以我们来编码吧。
从安装pip install plotly-express开始。
然后用import plotly.express as px导入。
语法非常简单。
**# Basic syntax
*px.graphic_type(dataset, x, y, color, size, title...)***# Creating a basic scatterplot
px.scatter(x=[1,2,3,4,5], y=[6,7,8,9,10], title='My First Graph')

你刚刚建立了你的第一个 Plotly 图形。图片由作者提供。
小菜一碟,不是吗?这就是这个图书馆的妙处!现在让我们把我们的图形做得更漂亮一点。为此,我将使用众所周知的玩具数据集提示,您可以从 Python 中的 seaborn 库中加载该数据集。
import seaborn as sns
df = sns.load_dataset('tips')

来自 Seaborn 的 Tips 数据集。图片由作者提供。
单变量图形
单变量图形仅显示数据集的一个变量的分布。直方图、箱线图、小提琴图等都是这类图形的好例子。
# Histogram
fig = px.histogram(df, x='tip')
fig.update_layout(bargap=0.1)
fig.show()

直方图。图片由作者提供。
如果你注意到在上面的代码中,我把我的图形赋给了一个名为 fig 的变量。这种做法必须成为代码中的“标准”,因为当您想要向可视化添加自定义更新时——就像上图中条形之间的间隙——您将能够将图形作为它所分配到的变量的名称来引用,在本例中为 fig。
#Boxplot
fig = px.box(df, y='tip')
fig.show()

按性别分列的小费方框图。图片由作者提供。
创建箱线图和其他图形一样简单。你可以在上面的图片中看到,我只是使用了基本语法px.graph_type(data, variable),这就是图形。看看最大值、最小值、中值是多么容易:你只需将鼠标悬停在画面上。然后,我们可以很快看到这个分布向右倾斜,tips 值中有一些异常值。
您也可以使用下面的代码快速绘制小提琴图。
# Violin plot
px.violin(df, x='tip')
或者绘制经验累积分布图。此图显示了点数的分布及其百分比。50.4%的数据在 2.92 以下。

用 Plotly 实现 ecdf。图片由作者提供。
多变量图形
在这种情况下,我们将使用两个或更多的变量来比较或分析它们之间的关系。常见的例子有散点图、线图、条形图、多盒图等。
让我们来看看提示和合计 _ 账单是什么关系。
# Scatter plot comparing Tips vs. Total Bill
fig = px.scatter(df, x='total_bill', y='tip', size='total_bill')
fig.show()

散点图。图片由作者提供。
很容易看出 total_bill 和小费金额之间有线性关系。当我们增加一个时,另一个也会增加。请注意,我们还使用了 total_bill 作为我们的大小参数,因此随着账单金额的增加,点数也会增加。
# Including another variable
px.scatter(df, x='total_bill', y='tip', size='total_bill', color='sex')

按性别划分的小费与总额 _ 账单。作者图片
我们还可以添加颜色参数,看看男女是怎么给小费的。在这个数据集中,除了右侧的几个异常值之外,看起来差别不是很大。如果我们快速检查一下这些数字,平均值实际上并不太远(男性:3.08,女性:2.83)。
# Mean tip by size
mean_tip = df.groupby('size').tip.mean().reset_index()
px.bar(mean_tip, x='size', y='tip')

按聚会规模计算的平均小费。图片由作者提供。
要通过按团体大小的提示创建条形图,首先需要创建一个分组数据框架,然后绘制它。不像用熊猫绘图那样实用,在熊猫绘图中你可以收集所有的东西并用一行代码绘图,但是结果没有这个漂亮。
3D 图形
Plotly Express 的另一个很酷的功能是可以很容易地绘制 3D 图形。你所要做的就是使用功能scatter_3d并添加一个 z 轴。
**# 3D Plot total_bill vs. tip vs. size**
fig = px.scatter_3d(df, x='total_bill', y='tip', z='size', color='sex')
fig.show()

3D 绘图。图片由作者提供。
3D 绘图的一个有趣之处在于,你可以获得一些在 2D 绘图中可能得不到的洞察力。请注意,在这张图中,对于人数等于 2 的聚会,红色和蓝色更加平衡,这表明可能会有更多的夫妇一起吃午餐或晚餐。然后我们可以看到,在其他规模的聚会中,男性人数更多。
3D 图形非常酷,但也应该小心使用,因为它们读起来并不像看起来那么琐碎。当看这些图时,你更容易失去注意力。
创建简单的仪表板
使用 Plotly,很容易创建一个仪表板。与 plotly express 的不同之处在于,你必须使用plotly.graph_objects **as** go来代替 Plotly Express。我的 GitHub 中的要点有完整的代码,你可以用它作为模板来创建你自己的可视化。
您可以看到代码本身与 px 图形非常相似。通过一些调整,您可以创建一个数据集的漂亮的“仪表板”视图。

使用 Plotly 的仪表板。图片由作者提供。

图片由作者提供。
现在分析数据更容易了。我们可以看到,数据显示男性占大多数,聚会人数越多的桌子自然给的小费就越多。如果你是一个服务器看到这个,你可能会选择周五和周日的晚餐时间。不吸烟的人也倾向于给更多的小费。
在你走之前
数据可视化对于良好的数据探索至关重要。如果一幅图像胜过千言万语,为什么不用一幅好的图像呢?
Plotly express 与其他可视化库一样简单,并为您提供了额外的功能,如与数据交互、缩放、选择要显示或隐藏的类别。仪表板是我们在单个视图中汇编大量信息的好方法,允许对数据集进行更完整的分析。这就是为什么我总是将这个库导入到我的会话中。
我希望你今天在日常工作中学到了一些有用的东西。如果是这样,请关注我的博客。另外,如果你想加入中级会员来阅读好的内容,这里有一个推荐链接。
参考
https://plotly.com/python/plotly-express/ https://plotly.com/python/subplots/ https://gustavorsantos.medium.com/
作为数据科学家,为什么应该提问
原文:https://towardsdatascience.com/why-you-should-ask-questions-as-a-data-scientist-46fb9f2bf893
不要以为你什么都知道

在 Unsplash 上 Marten Newhall 拍摄的照片
当我开始在数据领域工作时,我为自己做的最好的事情就是提出问题。我仍然记得我与一位高级数据科学家导师的第一次一对一会谈。我们面对面地坐在会议室里,我开始了我的谈话,“我有一个愚蠢的问题,但是…”他在那里拦住了我。他给了我一些关于提问的最佳建议。无论你的问题是什么,都不要假设或以“我有一个愚蠢的问题”开头。你问问题不是为了问。你问问题是因为你需要答案。自从那次会议后,我开始向我的同事问更多的问题。
在这次谈话中,我学到的另一个教训是,我做了多长时间的工作并不重要;我可以随时提问。数据科学都是关于问题的,所以我们要多问问题。和我一起工作过的大多数人更希望你提问,尤其是在遇到问题的时候。在日常的站立通话中,人们通常会提出他们正在努力解决的问题、妨碍他们工作的障碍和疑问。但这不一定是我们唯一一次提到我们在挣扎。
清晰和理解
我提问的主要原因是为了清晰和理解。在最近的工作中,我经历了很多变化和挑战。我不再担心这个,而是将注意力转移到以下几个方面:
- 你可以问哪些问题来巩固你对这个话题的理解?
- 你可以研究哪些信息来帮助澄清你遗漏的要点?
- 您需要完成哪些受此信息影响的工作项目?
这些类型的问题有助于获得理解和清晰。数据科学和软件是广阔的领域,通常,你会面临你以前没有见过的新问题。我发现,就工作提出有针对性的问题比做出假设更好,尤其是在涉及数据的时候。相反,我会请人澄清,而不是假设我知道些什么。
例如,我经常与团队合作进行 MLOps 和 CI/CD 改进。我已经和一个新团队走过了两次端到端的过程,以了解他们如何使用他们的管道和他们的问题领域。我做了大量的笔记,并用这些信息绘制了两张当前状态的图表。我最大的错误是在这个时候提出我的改进建议。为什么?因为我仍然对他们当前运营的具体方面有疑问,如果我做出假设,这些假设可能是错误的,导致我的分析不正确,并提出错误的改进建议。因此,如果你需要清晰,并准备好做出假设,请别人帮助你更好地理解。
持续学习
不断提问的另一个原因是不断学习。问问题,不管是对你自己还是对别人,都是一种很好的方式,可以让你了解我现在知道的和我想知道的。我经常问自己这样的问题:
- 你知道如何得到这个答案吗,或者你需要学习吗?
- 您对特定工具或服务的工作原理有基本的了解。你想拓展这方面的知识吗?如果是,你想从哪里开始?
- 有没有什么课程或书籍可以帮助你拓展技能,对你目前的工作有所帮助?
正如我反复说过的,数据科学和工程是千变万化的。有了它,我们总能学到很多东西。因此,不要假设你知道一些事情或者对你目前的思维状态感到满意,而是问自己和别人一些问题。然后,决定如何推动自己前进。
对于 2022 年,我将关注的主要问题是深入研究我工作的数据平台如何利用云。我想了解我们的数据在哪里,我们如何扩展,以及任何其他有关云的信息,这些信息可以帮助我更好地履行我目前的职责。今年你会问自己什么问题来进一步提高你的数据科学技能?一些需要考虑的领域:
- 您可以在工作中利用哪些新的数据源?
- 你的数据工程师做什么来为你提供数据?您如何更好地与他们交流,以确保双方都了解这些数据的需求?
- 如果你不是数据科学家,你的数据科学家今天在做什么?他们如何运行当前的流程?有没有办法利用这些知识来改进您的平台、数据、安全性或其他方面?
和你的团队一起问这些问题,让自己有更多的学习机会,更清晰和理解。
改进和最佳实践
最后,问自己一些需要改进的问题。这与持续学习密切相关。你目前进行分析、运行代码或产生结果的方式是最好的方式吗?你能做得更好吗?我们经常认为我们在做的事情符合我们和我们团队的最大利益,但是随着我们的技能和洞察力的发展,我们知道我们应该如何做我们的工作有一个更好的方法或最佳实践。
我们不想在事业上倒退。相反,我们希望专注于如何提高我们的技能、我们正在做的工作以及我们正在遵循的实践。
例如,考虑一个团队,该团队使用数据块为分析开发创建集群。这个团队有许多成员,每个成员都可以按照他们认为合适的方式为他们的工作创建集群和工作。团队越大,创造的集群和工作就越多。对于一些团队来说,这可能是一个很好的开始模型,但是最终,需要一种方法来管理成本以及谁可以创建集群。为了解决这个问题,您引入了更细粒度的集群策略和权限。并委派谁能创建集群和作业,谁不能。从每个人都可以创建集群和工作到只有少数人有集群策略限制的转变是一个团队如何随着时间的推移而改进并开发管理其平台的最佳实践的例子。最终,最佳实践的当前状态可能不是最好的,因此团队再次迭代并做出改变。
这就是为什么我们总是问自己和我们的团队,我们如何才能改善?我们如何更好地与最佳实践保持一致?
最后的想法
- 使用问题来获得对你的工作的更好的清晰和理解。如果做一个假设,问问自己这是不是作品的准确写照,还是需要更多的洞察?
- 使用问题来帮助促进您的持续学习之旅。问问你自己你知道什么,不知道什么?你想在哪些方面有所改进?
- 使用问题来询问您自己和您的团队如何改进并符合最佳实践。在不断学习的同时,总会有新的方法来做事情,但这是你和你的团队最好的方法吗?
你为什么会问数据科学方面的问题,你问的最多的是什么类型的问题?
感谢阅读!我希望你喜欢阅读我所学到的东西。要了解更多,请订阅我的简讯。
为什么您应该在下一个机器学习项目中考虑多云策略
多云战略使公司能够选择不同云服务提供商提供的云服务

云计算服务一直由世界上最受欢迎的大型科技公司主导,如 AWS、微软 Azure、谷歌 GCP 和 IBM。但是每个云服务提供商都有一些优点和缺点,这使得一个云解决方案很难满足组织的所有需求。
实施多云战略使公司能够更加灵活地优化成本、速度和性能。
在本文中,您将了解什么是多重云策略,它的优点和缺点,以及它将如何降低运行您的基础设施和应用程序的成本。
让我们开始吧。
什么是多云战略?
多云策略是指使用来自两个或更多供应商的一个以上的云服务(多个云服务)。公司实施多云战略,为他们提供的不同服务分配计算资源,以最大限度地降低停机和数据丢失的风险。

使用 canva.com 的创建
根据来自 Tech Target 的调查,在 EMA 调查的 260 家企业中,61%的受访者报告使用两家或更多公共云提供商。
对于从事人工智能和机器学习项目的公司,建议考虑采用多云战略,以帮助利用不同云服务提供商提供的独特而经济的服务来管理您的基础设施和应用程序。
例如,一家公司可以在两家云服务提供商中存储、处理和分析他们的数据集,然后在另一家云服务提供商中构建和部署一个机器学习模型。
注意:多云环境可以是公共的、私有的或两者的组合。
按需云定价比较

表 1:云和本地选项的每小时成本
上表显示了基于 GPU 的三种不同性能级别(低、中和高)的价格,还显示了两种利用率假设:低(10%)和高(100%)。
实施多云战略的优势
如果您计划实施多云战略,以下是您将从中受益的一些优势。
1.价格承受能力
这是实施多云战略的最重要的优势之一。云服务提供商提供不同的价格和等级,因此,您可以根据价格选择不同的服务。
例如,如果运行您的机器学习应用程序需要大量的计算资源来进行预测并提供快速响应,您可以在计算资源比其他云服务提供商更便宜或更实惠的地方部署您的模型。

由canva.com创作
“如今,92%的组织已经实施或正在实施云计算战略,82%的大型企业已经采用了混合云基础架构。平均而言,组织使用的是 2.6 版本的公共云和 2.7 版本的私有云。”-https://www.factioninc.com/blog/hybrid-multi-cloud/multi-cloud-trends/#:~:text=Today%2C 92 percent of organizations,adopted a hybrid cloud infrastructure.
2.防止停机。
几个小时的停机时间会造成数百万美元的收入损失,此外,还会失去客户的信任。实施多云战略可以帮助公司全天候运行其应用程序,这提供了额外的可靠性,因为一家云提供商的停机时间不一定会影响其他云提供商的服务。

由canva.com创造;数据来源:algorithmia.com
3.更低的延迟
如果你依赖于一个云服务提供商,如果他们远离云服务器,有时会导致用户访问数据和应用程序的延迟。使用多云策略可以帮助您避免延迟(称为延迟),因为离用户最近的服务器可以在很短的时间内提供所请求的数据。
4.避免供应商锁定
毫无疑问,依赖一个云服务提供商的所有服务会带来困难和挑战。如果供应商决定改变价格模式或功能,你就有麻烦了。转移到另一家供应商也很昂贵,而且耗费大量时间。
跨多个云服务提供商运行您的应用程序可以帮助您避免依赖单一云服务提供商。
5.访问 spot 实例的灵活性。
Spot 实例是云中未使用的备用容量,其价格低于按需实例。对于需要快速存储、大量内存或快速网络的应用程序或工作负载,您可以使用 spot 实例。
例如,大多数 AI 和 ML 应用程序可能需要更多的计算能力,这使得 spot 实例成为最佳选择。
但是一些云服务提供商,如 AWS,并不总是有能力提供符合您要求的 spot 实例。如果你依赖单一的云服务提供商,你的应用程序将不会像预期的那样运行。
通过实施多云策略,您可以避免这种挑战,因为您可以选择立即从其他云服务提供商那里选择 spot 实例。
实施多云战略的劣势
尽管有它的优势,多云策略也有它自己的缺点。
1.需要更多的人才。
在实施云计算战略之前或之时,拥有合适的人员非常重要。一个人无法学习和理解来自不同供应商的云服务的基础设施、需求和规范。
供应商提供的大多数云服务在配置、迁移和维护流程等方面各不相同。拥有合适的人才对于从云计算环境中获益至关重要。
2.安全风险
当与单个云提供商合作时,根据数据和访问权限来管理应用的安全性是很容易的。当您在多个云环境中运行您的应用程序时,它变得更加复杂和具有挑战性。
如果您不跨多个云配置安全控制,这可能会增加安全违规的可能性。
3.管理复杂性。
实施多云战略会增加运营管理的复杂性。您使用的云环境越多,管理所有环境的管理任务就越多,因为每个云提供商都有自己独特的管理环境的方式。
如果云环境管理不善,公司最终可能会为未使用的服务付费,这将增加更多不必要的成本,而不是最大限度地降低成本。
适用于多云战略的管理平台和工具
采用和管理多个云服务环境并不容易,借助以下管理平台和工具,您可以轻松迁移到多云环境。
该平台允许您跨不同的数据中心和云服务提供商使用多云应用服务。该平台支持由微软 Azure、谷歌云和 AWS 提供的公共和私有云服务。
当涉及到训练和部署机器学习模型时,Aipaca 可以帮助您为您的机器学习项目管理多个云环境。Aipaca 提供了一个名为 Aibro 的无服务器 MLOps 工具,使机器学习云计算变得便宜、简单、快速。
他们目前支持 AWS 云平台,并计划支持更多云平台,如 Microsoft Azure、Google Cloud 和 IBM Cloud。
Centilytics 是一个云管理平台,允许公司管理其多云环境。Centilytics 提供了灵活性和成本节约,该公司还可以在一个位置分析、管理和优化您所有客户的云支出。它还为云优化提供了准确的建议。
最后的想法
尽管实施多云战略存在诸多挑战/缺点,其中大部分都是可管理的,但优势大于劣势。
多云战略使公司能够选择不同云服务提供商提供的云服务,因为有些服务比其他服务更适合特定的任务。最后,公司可以从云开支中节省大量资金。
在本文中,您了解了什么是多重云策略、它的优点和缺点,以及可以帮助您管理多重云环境的推荐平台。
如果你学到了新的东西或者喜欢阅读这篇文章,请分享给其他人看。
为什么您应该考虑对医疗数据使用生成式对抗网络
将 GANs 用于医疗数据的两个优势

有监督的深度学习方法需要大量可能难以获得的数据,尤其是在医学领域。一个原因是,它是私人数据,我们不能总是获得、使用和共享。第二个原因是很难获得这些数据的标签。需要专家标注者,比如医生护士,既费钱又费时。即使我们有大量带标签的数据,我们也可能会遇到其他问题,比如数据平衡。罕见的情况不会在数据中频繁出现,并且会产生影响我们模型结果的偏差。
那么我们如何克服这些问题呢?有没有一种方法有帮助的潜力?生成性对抗网络。
有这种潜力的一种方法是生成模型。在这篇文章中,我将描述如何使用生成方法,特别是生成敌对网络,我们可以减少上述问题。这篇文章将涉及数据量、平衡和隐私问题。
生成对抗网络
GAN 模型生成与训练集具有相同统计信息的新数据。它由两个相互竞争的网络组成,即生成器和鉴别器。生成器学习从潜在空间映射到感兴趣的数据分布,以便生成新的样本。鉴别器的目标是将生成器生成的候选数据与真实数据分布区分开。换句话说,对每个样本进行分类,不管它是真实样本还是生成的样本。生成器的目标是骗过鉴别器,让它相信生成的样本是真实的,最大化鉴别器的错误率。
通过使用 GANs,我们可以生成新的医学样本,并解决数据量、平衡和隐私问题。
数据量和平衡
在许多情况下,我们希望开发一种用于医疗目的的算法,但我们没有足够的数据,即使有,也是不平衡的。这两个问题都可以通过使用 GAN 方法生成高质量的合成数据来解决。
比如德摩根。DermGAN 是一个生成网络,将临床图像与皮肤状况相结合。皮肤状况、其存在区域(橙色矩形)和皮肤颜色(红色背景)的贴图编码通过生成器生成合成图像。

图片来自报纸—https://arxiv.org/pdf/1911.08716.pdf
使用 DermGAN,我们可以生成大量关于皮肤状况的图像。这解决了数据量问题。我们还可以控制图像的平衡。我们可以从稀有条件中产生更多的样本,这解决了数据平衡问题。
这只是这一领域工作的一个例子。
使用生成式方法可以生成几乎任何数量的图像,并且需要对样本进行平衡。这是克服数据量和平衡问题的一步。
数据匿名化
大多数医学研究都是使用真实患者的私人医疗数据进行的。开源的数据集有好几个,但是局限于特定的领域和特定的数据分布,对于开发真实世界的系统来说是不够的。为了解决这个问题,每个小组都为自己手头的问题创建了自己的医学数据集。这些数据集是私有的,不能与组外的同事共享。因此,知识共享和结果的可复制性变得几乎不可能。
通过使用 GANs 生成合成医疗数据,我们将能够创建新的数据集,并在医疗界共享它们,从而进一步推动研究。
挑战
虽然这听起来很棒,但在使用合成数据之前,我们需要考虑几件事。比如说-
- 真实数据和合成数据之间的比率
- 用于从 GAN 采样图像的采样技术
- 合成数据的分布。
- 合成数据的质量
- …
当我们考虑在模型中使用合成数据时,我们应该想到这一点。
总结
我希望我说服了您,医学界可以从开发和改进 GAN 方法中受益,以生成高质量的合成医学数据。我不确定我们是否已经到了只能使用合成数据的地步,尤其是不能用于所有领域。但我认为这是一个很好的动机,让我们努力开发医疗数据的 GAN 方法。我会继续在我的地盘上调查此事,并随时通知你。
https://openaccess.thecvf.com/content/CVPR2021W/ISIC/html/Bissoto_GAN-Based_Data_Augmentation_and_Anonymization_for_Skin-Lesion_Analysis_A_Critical_CVPRW_2021_paper.html https://arxiv.org/abs/1911.08716
为什么应该处理丢失的数据,下面是如何做的
使用熊猫处理丢失数据的综合指南

Pierre Bamin 在 Unsplash 上拍摄的照片
在每个真实世界的数据集中,丢失数据值几乎是不可避免的,并且在典型的数据收集过程中几乎无法避免。
这可能是由各种原因造成的,例如数据输入过程中的错误、数据收集过程中的技术问题、文件丢失/损坏以及许多其他原因。
在任何真实世界的数据集中,通常都有一些数据科学家和机器学习工程师必须处理的缺失数据,否则,它可能会在开发数据管道时导致几个问题。

表格中缺少数据(图片由作者提供)
因此,在这篇文章中,我将展示一些技术,您可以使用这些技术来处理数据驱动项目中的缺失数据,并可能消除构建数据管道时缺失数据可能导致的问题。
文章的亮点如下:
为什么要处理缺失数据处理缺失数据【1】保留缺失数据【2】丢弃缺失数据<#a536>
我们开始吧🚀!
为什么应该处理缺失数据
在进入如何解决问题之前,有必要首先了解为什么有必要处理丢失的数据。
数据确实是所有数据科学和机器学习项目的主要驱动力。它是所有项目的核心元素,机器将根据它做出所有决定。
虽然缺失数据的存在确实令人沮丧,但从数据集中彻底消除缺失数据并不总是正确的做法。例如,考虑下图。

表格中缺少数据(图片由作者提供)
如果您考虑消除至少有一个缺失值的所有行,它将:
#1 减少数据集中的数据点数量
如下图所示,直接拒绝包含任何缺失值的行会显著减少数据集中的行数。

删除至少有一个 NaN 值的行(作者图片)
#2 导致我们已经拥有的其他有价值的(正确的)信息的丢失

在至少有一个 NaN 值的行中标记的非 NaN 值(图片由作者提供)
例如,在上面的数据帧中,即使中间一行的ColB的值没有被观察到,我们仍然精确地知道colA和colB中的相应值,这对数据管道仍然非常有价值。
处理缺失数据
既然您已经理解了为什么应该处理缺失数据,那么让我们来理解处理缺失数据的技术方面。
每当您在表格数据中遇到缺失值时,基本上只有三个选项可供选择,如下图所示:

处理缺失数据的三种方法(图片由作者提供)
下面就来详细讨论一下这三种方法。
#1 保留丢失的数据
顾名思义,该方法完全忽略数据集中任何缺失数据点的存在。
在这种情况下,数据集转换方法返回原始数据集的副本,如下图所示:

不对数据帧应用任何变换(图片由作者提供)
然而,这里本质上假设丢失的数据点不会在数据管道中引起任何麻烦,并且所利用的方法擅长处理丢失的数据。
因此,如果缺失的数据保持原样,那么决定他们的算法是否可以工作是数据科学家或机器学习工程师的工作。
保持丢失数据不变的方法定义如下。我们定义了函数handle_missing_data(),它将源数据帧作为一个参数,不做任何转换就返回它。
如上面的实现所示,原始数据帧保持不变。
#2 丢弃丢失的数据
接下来,想象一下,如上所述,保留丢失的数据对于您的特定用例是不可行的。
在这种情况下,完全丢弃丢失的数据可能是一个前进的方向。
这里的主要思想是从数据帧中删除有任何缺失值的整行(或者一列,如果您的用例需要基于序列的分析的话)。
换句话说,在这种技术中,您只保留那些对应于每个列(或行)具有非空值的数据行(或列),并将数据集视为被删除的行从未存在过。
逐行下降
顾名思义,这里的目标是删除数据帧中包含缺失值的行。
下图描述了按行放置。

删除至少有一个 NaN 值的行(作者图片)
在面向行的删除中,列数保持不变。
按列丢弃
与按行删除相比,按列删除涉及删除数据帧中包含缺失值的列(或系列)。

删除至少有一个 NaN 值的列(作者图片)
在面向行的删除中,行数保持不变。
履行
类似于上面讨论的保留缺失数据的策略,接下来,我们将实现handle_missing_data()函数来删除数据帧中缺失值的行(或列)。
您可以使用如下所示的[dropna()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dropna.html)方法从数据帧中删除行:
axis 参数指定要从数据帧中删除缺失值的方向(行或列)。
-
axis=0执行面向行的拖放。下面演示了这一点: -
axis=1执行面向列的删除,如下面的代码块所示:
#3 填写缺失的数据
最后一种方法是用某个值来填充缺失的数据,该值可能是给定未观察位置的最佳估计值,如下所示。

用随机策略替换缺失值(图片由作者提供)
该策略可能涉及用列的平均值、中值或列的最频繁值(众数)来填充缺失数据,具体取决于列中值的类型。
这是因为平均值、中值和众数只能对数值进行估计。但是,在分类列的情况下,mean 和 median 没有任何意义。
此外,填充标准完全取决于您的特定数据源、您正在解决的问题,以及您对评估特定缺失数据点的感觉如何。
履行
寻找缺失值的最佳估计猜测值的最常用技术包括均值、中值和众数,如下所示:
- 填充平均值:
平均值策略用列的平均值替换缺失值。
如上所述,中庸策略并没有取代colB中任何缺失的价值观。
- 填充中间值:
接下来,中值策略用中值替换列中缺少的值。这在下面实现:
再一次,最初从colB丢失的值仍然用NaN 值填充。
- 填充模式:
最后,用模式值填充会用该列中最常见的值替换缺失的值,如下所示:
您还可以对不同的列应用不同的填充策略,如下所示:
这里,我们用colA的平均值填充colA中缺失的值,用colB中的模式填充缺失的值。
结论
总之,在这篇文章中,我演示了如何处理熊猫数据帧中的缺失数据。具体来说,我们研究了为什么处理缺失数据对您的数据管道至关重要,然后是处理缺失数据的常用策略。
在处理缺失数据时,您应该记住,在本文讨论的三种方法(保留、删除和填充)中没有正确的方法。这是因为每个案例都是不同的。
视情况需要,选择哪种具体方法总是由您来决定。
感谢阅读!
🧑💻成为数据科学专家!获取包含 450 多个熊猫、NumPy 和 SQL 问题的免费数据科学掌握工具包。
✉️ 注册我的电子邮件列表 不要错过另一篇关于数据科学指南、技巧和提示、机器学习、SQL、Python 等的文章。Medium 会将我的下一篇文章直接发送到你的收件箱。
为什么您应该了解大数据
原文:https://towardsdatascience.com/why-you-should-know-big-data-3c0c161b9e14
了解大数据的方方面面

斯蒂芬·道森在 Unsplash 上拍摄的照片
如今,当试图描述这样一种现象时,每个人都在谈论大数据这个词,即公司和公共组织拥有的数据量不断增加,这将传统数据库推向了极限。
大数据的定义
Gartner IT 词典对大数据的定义如下:
“大数据是高容量、高速度和/或高变化的信息资产,需要经济高效、创新的信息处理形式,以提高洞察力、决策和流程自动化。”
4 V 的数据
尽管很难准确描述大数据系统“大”的原因,但我们使用了四个概念来定义此类系统。从它们的名字可以看出,它们也被称为大数据的 4 V:
- 数量:来自网飞、亚马逊或脸书等公司的大数据应用程序包含海量数据,这些数据已经达到万亿甚至千兆字节的数量级。在某些情况下,需要数千台机器来存储和处理如此大量的数据。此外,出于安全原因,数据被复制,以便在发生故障时可以访问,这进一步增加了数据量。
- 多样性:数据不仅来自多种数据源(如图像数据、音频数据等。)还具有各种各样的数据结构。信息必须转换成格式,以便可以一起使用。例如,必须为数据规范达成一致的模式。
- 速度:速度是指数据处理的速度。典型的大数据系统以越来越快的速度存储和管理大量数据。新数据生成、修改和处理的速度是一个挑战。Twitter、脸书或 YouTube 等社交网络的用户不断创造新内容。这不仅包括每小时发布的数百万条推文,还包括跟踪信息,例如,浏览量或用户的 GPS 数据。
- 准确性:人为产生的数据可能不可靠。社交网络或博客上的帖子可能包含不正确的信息、矛盾之处,或者只是简单的错别字。所有这些都使得算法很难从数据中提取价值。挑战在于识别哪些数据是可信的,哪些是不可信的。算法用于测量数据质量和执行数据清理步骤。
根据不同的文献,大数据的定义只有三个 v,即数量、速度和多样性。在其他定义中,甚至更多地提到了 Vs。一个例子是“价值”,这意味着应该使用大数据从数据中提取有意义的价值,例如通过应用机器学习算法。
数据从哪里来?
在传统的信息系统(如银行或保险公司使用的系统)中,数据主要由公司的员工收集。在大数据应用中,数据来源更加多样化。在当今世界,几乎所有工业部门和公司规模都会产生各种各样的数据。除了数字化活跃的公司,制造业也从各种来源收集信息:
- 【经典】数据:这是法律要求公司无论如何都要收集的数据,或者出于普遍利益已经收集了一段时间的数据。例如,这包括发票上的订单的所有信息(订单号、销售额、客户、购买的产品等。).
- 多媒体来源:视频、音乐、录音以及演示幻灯片等多媒体文档甚至比文本输入更难分析。对这些输入格式进行适当的预处理是存储这些数据所需的最重要的步骤之一。简单的图像预处理算法可以用来提取图像的大小或主色。使用机器学习技术的更复杂的算法可以识别图像中的内容或图像中的人是谁。
- 用于监控的传感器数据和其他数据:服务器、智能手机和许多其他设备会产生所谓的日志条目,这些条目会在使用过程中出现。网络服务器记录网页的每一个请求。这样的日志条目包含了很多关于冲浪者的信息:他的 IP 地址、国家、城市、浏览器、操作系统、屏幕分辨率等等。这使得分析点击行为、在特定网页上花费的时间长度以及访问者是回头客还是新访问者成为可能。在智能手机中,有传感器收集数据,如 GPS 位置或电池状态。接近传感器和陀螺仪可以结合使用,以检测手机是否在口袋中,用户是否拿着它,或者它是否在桌子上。
大数据的风险
尽管大数据创造的新技术可能性代表了真正的进步,但它们也伴随着严重的威胁。为了保持领先,我们需要了解大数据会带来哪些潜在威胁。
安全问题
随着能够存储和处理如此大量数据的技术实现,数据安全性的合规性可能会很快退居幕后。公司存储的数据越多,就越需要努力保护数据,因为这也使其成为更大的攻击目标。因此,必须采取的预防措施造成了巨大的成本,然而,这并不意味着公司本身暂时有任何额外的收入。
在大数据时代之前,只有国家能够并被授权存储个人的大量信息。税务机关、居民登记处、驾照办公室等。收集了大量政府专有的个人信息,并受到数据保护法的保护。另一方面,许多大公司现在能够收集这些个人信息,甚至可能拥有比政府更多的个人数据。
伦理问题
在保险行业,投保人行为数据对费用产生影响已经是一种常见的做法。如果一个造成车祸,很有可能保险费会增加。另一方面,如果一个人连续几年无事故驾驶,这可能导致保险费的减少。
随着大数据和关于单个人的可用数据量的增加,越来越多的信息将被包括在这种计算中,从而使保费个性化也不是不可能的。诚然,保险公司的这种做法是可以理解的。然而,这种措施也有歧视性影响,这只是一个细微的差别。例如,我们对一个人仅仅因为他或她的家庭或周围环境中的人在统计上造成更多的事故而不得不支付更高的保险费有什么感觉?
这是你应该带走的东西
- 大数据是指高容量、快速和/或变化丰富的信息库存。为了处理这些,我们需要新形式的信息处理。
- 大数据可以用所谓的 4 V 来表征:数量、多样性、速度和准确性。
- 在许多情况下,数据来源于经典数据库、多媒体源或监控数据(传感器数据)。
- 随着组织和公司可用数据量的不断增加,数据安全问题变得越来越重要。
如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**
*https://medium.com/nerd-for-tech/what-are-deepfakes-and-how-do-you-recognize-them-f9ab1a143456 https://medium.com/nerd-for-tech/understanding-mapreduce-with-the-help-of-harry-potter-5b0ae89cc88 https://medium.com/codex/explainable-ai-integrated-gradients-for-deep-neural-network-predictions-eb4f96248afb *
为什么你应该学习机器学习工程而不是数据科学
意见
以及如何开始

马文·迈耶在 Unsplash 上的照片
介绍
根据[1],数据科学是就业市场上最受欢迎的工作之一。但是现在还是这样吗?还是已经有更合意的了?
有!在就业市场上,机器学习工程正在超越数据科学。
在这篇文章中,我想阐明为什么机器学习工程在我看来正在超越数据科学,以及你如何开始学习它。
但我们先从了解两种工作角色的区别开始。
机器学习工程师 vs .数据科学家
引用一篇雪花文章很好地总结了这种差异[2]:
在同一项目或公司内,机器学习工程师比数据科学家更靠后。很简单,数据科学家将分析数据,并从数据中收集见解。机器学习工程师将专注于编写代码和部署机器学习产品。
我们还可以了解数据科学项目的生命周期,以便更好地理解差异:

图 1: ML 项目生命周期3。
所以基本上,一个数据科学家开发一个模型,训练和评估它。然后,机器学习工程师采用该模型,将其部署到生产中,并确保模型得到维护。所以机器学习工程师把训练好的模型放到产品中,这样就可以从模型中产生收入。
但是这两种工作不是同等重要吗?是的,他们是。但是,数据科学家已经从公司大规模聘用,因为他们大多处于建模和探索阶段。现在非常需要机器学习工程师,因为公司现在需要将这些模型投入生产,以从中创造价值。
根据 Venture Beat [4]的一篇文章,“87%的数据科学项目从未投入生产”。这是因为缺乏知道如何将模型投入生产的雇佣机器学习工程师。这种不匹配清楚地表明,公司现在更加关注(至少他们应该)雇用机器学习工程师,以便能够将模型投入生产。
在 Glassdoor 上查看公开招聘信息时,我们也可以看到这种差异。对于美国的加利福尼亚州,与 3345 机器学习工程师职位发布相比,目前有 1809 数据科学家职位发布。所以机器学习工程师的空缺职位几乎是原来的两倍!
但是为什么数据科学家不能简单地学习如何将模型投入生产呢?因为数据科学家专注于 ML 代码,而 ML 代码通常只是整个 ML 基础设施的一小部分(图 2)。数据科学家也应该只关注那一小部分。专注于 ML 代码和部署、监控等基础设施实在是太复杂了
因此,在您的团队中拥有一名数据科学家和一名机器学习工程师来从您的数据中创造最佳价值是非常重要的。

图 2: ML 代码与完整的 ML 基础设施的比较。
好的,现在我们知道机器学习工程师目前在劳动力市场上更受欢迎。但是做一个机器学习工程师需要哪些技能呢?成为机器学习工程师需要学习什么?
成为机器学习工程师的途径
在这一部分,我想重点介绍成为机器学习工程师所需的技能,以及可能是最好的学习工具。最重要的是,我想给你提供我在成为机器学习工程师的旅程中参加的在线课程的链接。
声明:我只提供我自己参加过的课程的链接。我提供的链接不是附属链接,所以我没有从分享它们中得到任何钱。我只是想与你分享它们,因为它们真的在我的学习旅程中帮助了我!
最有价值的技能
因此,根据 Udacity [6]的一篇文章,这些是成为机器学习工程师最有价值的技能:
- 计算机科学基础与编程:数据结构(堆栈、队列、…)、算法(搜索、排序、…)、可计算性和复杂性以及计算机架构(内存、缓存、带宽、…)
- 概率与统计:概率、贝叶斯法则、统计测量(中位数、均值、方差、…)、分布(均匀、正态、二项式、…)和分析方法(方差分析、假设检验、…)
- 数据建模和评估:发现有用的模式(相关性、聚类等)并预测未知数据点的属性(分类、回归、异常检测等),使用正确的性能指标(准确性、f1 分数等)持续评估模型性能
- 应用机器学习算法和库:为潜在问题选择正确的模型(决策树、最近邻、神经网络、多个模型的集成等等),训练模型的学习过程(线性回归、梯度推进等等),了解超参数的影响,体验不同的 ML 库(Tensorflow、Scikit-learn、PyTorch 等等)
- 软件工程和系统设计:了解不同的系统组件(REST APIs、数据库、查询等),为 ML 组件构建接口
学习工具
现在让我们来看看我认为必须学习的工具:
- Python: 这个我觉得很清楚。Python 仍然是机器学习领域排名第一的编程语言[7],也很容易学。
- Linux: 作为一名机器学习工程师,会从事很多基础设施方面的工作,能够在 Linux 上工作真的很重要。
- 云:越来越多的应用正在向云迁移。这意味着,作为一名机器学习工程师,你可能也会将模型部署到云环境中。因此,我建议学习与至少一个流行的云提供商(GCP,Azure,AWS)合作。我目前正在报名参加 Udemy 上的 AWS 开发者证书课程,我真的很推荐!
- Docker,Kubernetes: 在我看来,这两个工具是每一个机器学习工程师必须学习的!它们非常强大,可以轻松地将模型部署到产品中,并为您的应用程序创建完整的架构。我参加了 Udemy 上的 Docker 和 Kubernetes 完全指南,在整个课程中学到了很多!
其他有用的在线课程
所以现在你知道了需要什么技能,需要学习什么工具,我还想向你展示一些其他有用的在线课程,我认为这些课程可以帮助你成为一名机器学习工程师(至少它们帮助了我):
- 深度学习特殊化作者吴恩达: 本课程重点介绍深度学习以及如何训练图像分类领域的模型等等。安德鲁很擅长解释这个理论。但你也可以在实践课程中直接应用理论,这对于应用机器学习算法和库所需的技能来说是很好的。
- 机器学习 Nanodegree by uda city:uda city 这个所谓的 nano degree 专注于训练 ML 模型并投入生产,主要使用 AWS SageMaker 等。你也可以看看我的中间文章,里面写了我为通过这门课程而做的顶点工程。注意:Udacity 用那个课程的更新版本替换了我的课程。但我认为这个新版本仍然很有意义。
- IBM 机器学习专业证书:Coursera 上的这门课程集中在机器学习的每一个方面,动手量很大。您将了解监督和非监督机器学习、深度学习、强化学习等更多内容。在每门课程结束时,你必须建立自己的顶点项目,你还必须创建一个报告,描述你的应用等等。
结论
你现在已经知道,成为一名机器学习工程师比成为一名数据科学家更令人向往。你现在也知道了成为一名机器学习工程师需要学习的技能和工具。
因此:去把你的手弄脏吧!学习这些工具,参加一些在线课程,并获得你的第一份机器学习工程工作。
还有一点我想说的是:永远要把你的手弄脏!尽可能多地动手做 ML 项目。并且不要忘记将你训练好的模型投入生产,因为你想成为一名机器学习工程师。
你也可以阅读我关于深度学习项目的文章,在那里我训练了一个 ML 模型并将其投入生产。
在这篇文章中,我解释了潜在的问题以及我是如何训练 ML 模型的。然后,我将训练好的模型打包到 Docker 容器中,并使用 Flask 创建一个简单的网页。
在本文中,我将 Flask 应用程序部署到 AWS 中,这样每个人都可以访问我的应用程序。
谢谢你把我的文章看完!我希望你喜欢这篇文章。如果你想在未来阅读更多类似的文章,请关注我,保持更新。
接触
参考
[1]托马斯·h·达文波特和 DJ·帕蒂尔,数据科学家:21 世纪最性感的工作 (2012),哈佛商业评论
[2]雪花,机器学习工程师 VS 数据科学家
[3] Sundeep Teki, ML 工程师 vs .数据科学家 (2022),Neptune AI 博客
[4] VB 工作人员,为什么 87%的数据科学项目永远无法投入生产? (2019),VentureBeat
[5]拉希德·卡兹米,生产中的机器学习(MLOps) (2022),走向数据科学
[6] Arpan Chakraborty,成为机器学习工程师需要的 5 个技能 (2016),Udacity
【7】Sakshi Gupta,机器学习最好的语言是什么? (2021),跳板
为什么你不应该睡在数据伦理上
原文:https://towardsdatascience.com/why-you-should-not-sleep-on-data-ethics-adb31f4bc08e
意见
一对一讨论如何规划今天,打造更美好的明天

照片由马体芒果
介绍
数据道德是将道德价值观应用于商业目的的数据收集、存储、处理和共享。它涉及应用道德原则来保护组织的利益相关者免受管理不善或处理不当的数据造成的伤害。
全球范围内的数据爆炸引发了关于组织如何使用和共享数据的伦理问题。
数据是一个用来描述信息的任何数量或抽象描述的术语。数据和信息之间的区别很微妙,但很重要:数据指的是实体或对象的实际数字表示,而信息只是一些东西的意思。
例如,如果你问我昨天多大(以人类的年龄计算),我会很有把握地说“24 岁”——但如果你问我眼睛是什么颜色,我更有可能回答“绿色”,因为如果不进一步调查,两个人都不可能知道他们是否错了。
这是有道理的:我们怎么知道一个人的真实年龄,除非他们给我们提供证据?如果有人告诉我们他们 24 岁,但是没有任何文件证明他们 24 岁(例如出生证明),那么也许所有的希望还没有破灭!然而在这种情况下…
数据为您的数字生活提供动力。
数据是推动数字经济的燃料。这是事情发生的原因,是我们的设备运行的原因,也是找到新的做事方式的关键——从无人驾驶汽车到虚拟现实应用。
随着时间的推移,数据也变得越来越有价值,因为它可以由机器学习算法进行处理,这些算法从过去的行为模式中学习,以便对未来事件进行预测。
数据就像石油:一旦你有了充足的数据(像石油一样),你就可以用比以前更少的努力做更多的事情,因为你的机器可以用更少的资源为你做更多的事情。
事实上,一位专家最近将数据描述为“新黄金”,他将数据的价值与铂或银等其他贵金属进行了比较,这是因为近年来技术进步如此之快
数据用于关键情况,如警务和医疗保健。
你可能会想,“数据伦理?那不就是为了学术吗?”让我告诉你,不是!数据伦理是现代生活的重要组成部分。数据可用于关键情况,如警务和医疗保健。
数据被不当使用的最常见的例子之一是当一个人的个人信息被公司或政府机构滥用时。
例如:如果有人获得了你的社会安全号码,窃取了你的身份并用它在网上购买了一些东西(如信用卡),那么你就没有办法知道是谁干的——即使之前有迹象指出了问题(如在银行对账单上看到可疑活动),它们现在可能已经被删除了,因为银行通常不会告诉任何人欺诈案件,直到他们停止进一步调查(这可能需要几个月)。
我们不能再盲目相信我们的算法。
多年来,我们一直盲目相信我们的算法,但现在是时候停止了。
机器学习算法旨在从错误中学习,这意味着它们可以更好地根据已经发生的事情做出决定。
这是好事!如果你想一想,我们都只是我们的机器用来作为模型输入的数据点,指导它们的行为和决策过程。
但这也意味着,当你的模型或算法出现问题时——无论是因为它是在不完整的数据上训练的,还是因为涉及到人类的偏见——如果没有足够快的纠正或适当的更新,它可能会灾难性地失败。
与这些系统互动的其他人也是如此:他们可能不总是理解为什么某些事情会以自己的语言或行为发生,然后想知道为什么这些相同的事情会一再发生,直到有人告诉他们在这种情况下应该如何行事(有时甚至是在被告知之后)。
数据伦理是关于理解我们正在创造的世界。
数据伦理是关于理解我们正在创造的世界。简而言之,数据伦理意味着负责任地、合乎道德地使用数据,以便为每个人改善我们的生活。
如果你不确定这意味着什么,可以这样想:如果有人要写一本关于如何在你的生活中变得更有道德的书,难道他们不会包括各种哲学思想,比如“你希望别人怎么对你,你就怎么对别人”?或者你自己经历中更具体的例子(例如,“如果有人在我在家吃早饭前问我是否可以借我的手机,我会说不行”)。
当我们谈论数据隐私和道德时,这正是我们在考虑这些问题时所做的工作!
技术现在比以往任何时候都更加强大。
你可能会想,“我不是技术专家!我怎么知道技术越来越强大?”好吧,让我们看看数字。
几十年来,技术的力量一直呈指数增长。例如,想想我们今天的计算机比 20 年前快了多少:
- 1/1000 秒执行一次操作——相比之下,1993 年当我的第一台计算机制造出来时,这一时间仅为 10 万分之一秒。
- 集成电路上的晶体管数量已经从每平方英寸 2 千晶体管(KTI)或 2 亿 KTI 增加到今天的个位数十亿,摩尔定律仍然适用,尽管自 1965 年诞生以来已经超过 50 年,到 2016 年初已经达到极限。
仅仅说你不知道你的技术在做什么是不够的。
你不能总是相信你的数据。
你可能认为使用收集和分享信息的设备是道德的,但这不足以说明你不知道你的技术在做什么。
您需要了解使用数据的后果,并确保您了解第三方是如何收集、处理、共享或出售数据的。如果你对这些东西不确定,那就不要使用它们!
什么是数据伦理?
“数据伦理”一词是由哲学家约翰·拉尔斯顿·索尔在其著作《远离家乡:全球数据史》(2006 年)中提出的。在这本书里,他说我们需要一些系统来帮助我们理解如何使用信息并根据信息做出决策,因为这些系统将以我们今天无法想象的方式影响我们的生活。
数据伦理是将伦理价值观应用于商业目的的数据收集、存储、处理和共享。它涉及应用道德原则来保护组织的利益相关者免受管理不善或处理不当的数据造成的伤害。
数据伦理可以用在两个方面:作为思考你如何收集客户信息的框架;或者作为一个工具包,帮助你在与和你有相似兴趣的人一起工作时,应用你自己的个人价值观和信仰。
数据道德是将道德价值观应用于商业目的的数据收集、存储、处理和共享。这是关于理解我们正在创造的世界以及如何使用它。
这意味着你需要比以往任何时候都更好地了解你的技术,因为即使你第一次踏上这一旅程时不知道你的技术在做什么(我不是特指大数据),现在也有比以往任何时候都多的眼睛在关注所有这些大堆数字。
但是,谁应该对数据伦理负责?
数据专业人员应该对数据道德负责。他们是创建和操作数据集的人。
数据科学家可以利用这一事实,确保他们的数据集中的每一个元素都考虑到了其道德影响。
如果您与外部供应商合作收集您的数据,无论是在线调查还是其他类型的研究,他们都需要知道他们对自己的信息有多少控制权,以及在进入他们的系统的过程中应该采取什么样的保护措施。
数据所有者还必须确保使用他们产品的任何人都完全理解这些权限以及这些责任持续多长时间(如果有的话)。
治理委员会成员也是如此:如果有人注册了访问权限,但在实际使用你的公司提供的任何产品/服务之前就离开了,他们不应该期望永远自由支配其他人的帐户!这可能会导致非常危险的道路。
数据伦理是一件大事。而且,就像生活中的许多事情一样,这不仅仅是技术上的问题:这是关于你是谁以及你为什么做你所做的事情。
每个处理数据的人都应该理解他们的角色和道德义务。
为了在当今的数字经济中保持竞争力,公司需要确保其员工接受关于如何合乎道德地管理大数据的适当培训。
培训非常重要,因为它可以确保您的所有员工都了解合乎道德地使用数据的重要性,以及当他们遇到这种情况时可以做些什么。它还帮助他们了解如何识别可疑活动或可能导致违反 GDPR 法律以及 PCI DSS(支付卡行业数据安全标准)等其他法规的活动。
我们如何做到这一点?
数据伦理是一个宽泛的术语,涵盖了很多领域。但它可以分为三个主要的关注领域: 隐私、安全 和 公平 。
隐私
指个人或团体保护其个人信息不被他人以不符合其意愿或利益的方式未经其同意使用的权利。
理想情况下,这意味着人们应该能够控制他们的哪些数据被收集以及如何使用——无论是出于营销目的还是其他原因(如执法)。
安全性
指的是保护数据,防止可能想要伤害其所有者或未经信息所有者(即黑客)许可访问其中包含的敏感信息的人未经授权访问数据。
这包括像加密技术这样的东西,当存储在服务器上时,它有助于防止未经授权的访问,但也确保没有其他人可以看到你在那里存储的东西-即使他们知道你的计算机在哪里!这就像在你的门上安装了超级坚固的锁,只有那些有权限的人才能进去。
公平
指通过防止偏见、冷漠或任何形式的算法歧视来保护数据表示。尤其是当涉及机器学习算法时,我们深入挖掘任何潜在的数据偏差,这一点至关重要,我们可能会间接反馈给我们的算法。
对数据的任何使用都有几个其他的伦理考虑。
透明度很重要,因为它让用户知道他们的信息是如何被公司或组织使用的。
问责制是有效数据道德的另一个关键组成部分,因为它确保所有利益相关者都能获得关于其个人信息如何被使用的信息,以便他们能够就是否希望其个人数据以特定方式被使用做出知情决定。
当我们谈论数据时,我们需要更多地谈论道德
数据伦理是一个新的研究领域,但它值得你花时间去理解数据使用对社会的影响。
数据伦理不仅仅关乎隐私和安全。这也是关于理解数据使用对整个社会的影响。你知道的越多,你就能更好地就收集什么样的信息以及将来如何使用这些信息做出明智的决定。
结束语
正如我们已经讨论过的,让我们的生活变得更简单的技术也让它们变得更复杂。
我们生活在一个算法在我们不知情或未经我们同意的情况下为我们做出各种决定的世界——我们应该对这些系统如何处理我们的数据负责。
如果你想跟上数据伦理的新发展,那么有很多地方可以让你更多地了解公司是如何处理其工作的伦理影响的(或者根本不处理)。
你喜欢这篇文章吗?如果是,请考虑订阅我的电子邮件列表,以便在我发布新内容时得到通知。免费的:)
https://david-farrugia.medium.com/subscribe
也许你也可以考虑成为一名会员来支持我和你其他喜欢的作家。每月 5 美元,你就可以无限制地阅读 Medium 上的每一篇文章。
https://david-farrugia.medium.com/membership
想联系吗?
我很想听听你对这个话题的想法,或者任何关于人工智能和数据的想法。
如果你想联系我,请给我发电子邮件至 davidfarrugia53@gmail.comT5T7。
为什么不应该信任 train_test_split()函数
原文:https://towardsdatascience.com/why-you-should-not-trust-the-train-test-split-function-47cb9d353ad2
机器学习
反思如何使用 train_test_split()函数会导致错误的结果,并结合实际演示

毫无疑问,几乎所有的数据科学家在他们的一生中都至少尝试过一次使用train_test_split()函数。scikit-learn Python 包提供了train_test_split()函数。通常,我们不太关心使用这个函数的效果,因为用一行代码我们就可以将数据集分成两部分,训练集和测试集。
的确,使用该功能可能会有危险。在这篇文章中,我将试着解释为什么。
文章组织如下:
train_test_split()功能概述- 潜在风险
- 可能的对策。
train _ test _ split()函数概述
train_test_split()函数由 sklearn 包下的 model_selection 子包提供。该函数接收以下参数作为输入:
- 数组— 要拆分的数据集;
- test_size — 测试集的大小。它可以是浮点数,也可以是整数。如果它是一个浮点数,它应该是一个介于 0.0 和 1.0 之间的数字,表示要包含在测试集中的数据集的比例。如果它是一个整数,它是包含在测试集中的样本总数。如果未设置 test_size,该值将自动设置为列车大小的补码;
- train_size —列车组的大小。它的行为是对 test_size 变量的补充;
- random _ state-在应用分割之前,数据集被混洗。random_state 变量是一个整数,它初始化用于洗牌的种子。它用于使实验具有可重复性;
- 洗牌 —指定是否在分割前洗牌。默认值为 True
- 分层 —如果不是无,它为类别标签指定一个频率数组。这允许拆分阶段保留指定的类别标签的频率。
通常,我们从 scikit-learn 文档中复制如何使用train_test_split()的示例,并按如下方式使用:
from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = **train_test_split**(X, y, test_size=0.33, random_state=42)
我们不太在意这个特性的效果。我们继续写代码吧。
但是也有潜在的风险,我将在下一节向您展示。
2 潜在风险
在内部,train_test_split()函数使用一个种子,允许您将数据随机分成两组:训练集和测试集。
该数字是伪随机,因为相同的数据子部分对应相同的种子值。这对于确保实验的再现性非常有用。
不幸的是,使用一个种子而不是另一个种子可能会导致完全不同的数据集,甚至会修改接收训练集作为输入的所选机器学习模型的性能。
为了理解这个问题,我们举个例子。让我们考虑由 scikit-learn 库提供的经典糖尿病数据集。该数据集与 scikit-learn 一起发布,可通过此链接获得。
我们生成 100 个不同的训练/测试集对,具有不同的random_state值(从 0 到 99),我们通过线性回归模型对每个训练集建模。然后,我们计算每个模型的均方误差(MSE)值。
首先,我们加载数据集:
from sklearn.datasets import **load_diabetes**diabetes = load_diabetes()
X = diabetes.data
y = diabetes.target
然后,我们计算每个模型的 MSE:
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_errorn = 100
mse_list = []
seed_list = np.arange(0, n)for seed in seed_list:
X_train, X_test, y_train, y_test = **train_test_split**(X, y, test_size=0.30, random_state=seed)
model = **LinearRegression()**
model.fit(X_train,y_train) y_pred = model.predict(X_test)
mse_list.append(**mean_squared_error**(y_test,y_pred))
我们将不同种子产生的不同 mse 存储在 mse_list 中。现在我们可以绘制 MSE。我们使用 matplotlib 包:
import matplotlib.pyplot as pltplt.plot(seed_list, mse_list)
plt.title("MSE VS RANDOM STATE")
plt.xlabel('random state')
plt.ylabel('mse')
plt.grid()
plt.savefig('mse vs random state.png')
plt.show()

作者图片
我们注意到 MSE 依赖于随机状态。我们还计算平均值和标准差:
import numpy as npmean = np.mean(mse_list)
std = np.std(mse_list)
它给出了以下输出:
mean = 3029.5400317608987std = 302.5362185895346
通过这个实验,我们可以得出结论,train_test_split()功能并没有看起来那么无害。
如何减轻train_test_split()的影响?让我们看看一些可能的解决方案。
3 种可能的对策
至少有两种可能的对策来减少train_test_split()的影响:
- 使用不同的随机状态值多次运行
train_test_split(),如前一节所示。然后我们可以计算我们度量的平均值; - 使用交叉验证,作为
train_test_split()的替代方案。交叉验证自动将数据集分成 K 个折叠,并在 k-1 个折叠上执行训练阶段,在剩余的折叠上进行测试。然后,它移动折叠,并在接下来的 k-1 个折叠上重新进行训练阶段,并在剩余的折叠上进行测试。诸如此类。
Eijaz Allibhai 有一篇非常有趣的文章,题为机器学习中的保留与交叉验证,解释了 train_test_split()(也称为保留)和交叉验证之间的区别。
摘要
在本文中,我描述了使用 scikit-learn 包提供的train_test_split()函数的潜在风险。为了对比它们,一个可能的对策是使用交叉验证。
如果你读到这里,对我来说,今天已经很多了。谢谢!你可以在这篇文章里读到更多关于我的内容。
相关文章
[## Python 和 scikit 中的完整数据分析工作流程-学习
towardsdatascience.com](/a-complete-data-analysis-workflow-in-python-and-scikit-learn-9a77f7c283d3)
为什么您应该选择置信区间而不是 p 值
原文:https://towardsdatascience.com/why-you-should-prefer-confidence-interval-over-p-value-e32293bd174c
传达您的统计分析结果

埃尔佩佩在 Unsplash 上拍摄的照片
从根本上说,数据科学领域的分析师之旅包括三个阶段:学习、实践和展示。这些相位不一定是线性的;当然,我们大部分人都是来来回回。你认为这三个阶段中哪一个是最困难的?
个人觉得第三个最复杂。当然,前两个阶段的挑战已经够多了;然而,过了一段时间,你意识到学习并能够使用某些方法只是时间问题。毕竟,方法学家推导出了所有的方程,软件包开发人员将这些方程转换成随时可用的函数,慷慨的指导者创建了大量的教程来使您的生活更加轻松。相反,关于最后一个阶段,似乎有很多因素是我们无法控制的。
考虑一下演示阶段。在准备陈述你的发现(作为报告和/或口头陈述的一部分)之前,问自己至少三个问题:
1.我要向谁展示我的分析结果?
2.他们的局限性是什么(例如,缺乏统计培训/对行话的误解)?
3.考虑到这些限制,我怎样才能以一种既能被我的读者/观众所理解,又在技术上正确的方式来展示我的发现呢?
事实上,在有效和准确地展示您的统计结果方面存在许多挑战。今天,我们将集中讨论一个我个人和许多其他人都在努力解决的特殊问题— 如何解释统计测试结果的重要性?我们应该依赖 p 值吗?还是应该更倾向于置信区间?
现实世界的例子
假设我们的研究问题是:与非退休人员相比,退休人员在多大程度上更容易出现频繁失忆?
为了调查这个问题,我们将使用消费者金融保护局(CFPB)开展的 2016 年金融福利调查的公开数据。
让我们将数据直接从 CFPB 网站导入到 R:
**#Import data**
data <- read.csv("[https://www.consumerfinance.gov/documents/5614/NFWBS_PUF_2016_data.csv](https://www.consumerfinance.gov/documents/5614/NFWBS_PUF_2016_data.csv)")
关于失忆的问题(变量名为 MEMLOSS )问:
在过去的 12 个月中,您是否经历过更频繁发生或越来越严重的困惑或记忆丧失?
响应选项包括:1=是,0=否
而关于退休的问题(变量名为 EMPLOY1_8 )问:
以下哪一项描述了您目前的就业或工作状况?
回答选项为:1=自雇,2=为雇主或军队全职工作,3=为雇主或军队兼职,4=家庭主妇,5 =全职学生,6。永久患病、残疾或无法工作,7=失业或暂时下岗,8=退休
好消息是他们已经创建了一个退休虚拟人( EMPLOY1_8 ),如果有人退休,这个虚拟人的值为 1,否则为 0。
让我们来估计一下退休 人和非退休人中报告在过去 12 个月内经常失忆的受访者比例的差异。我们可以选择运行多个统计测试,这将导致我们得到相同的结论。不管怎样,让我们做一个加权最小二乘法(即,使用包含调查权重的线性回归估计模型):
**#finalwt is the weight variable which accounts for the complex survey design**
summary(lm(MEMLOSS~EMPLOY1_8,data=data,weights = finalwt))

(图片由作者提供)
如果您遵循 p 值方法,这就是您对上述发现的解释:
在过去 12 个月中,退休人员中经常出现记忆力丧失的受访者比例比非退休人员高 5.24 个百分点。由于 p 值小于 0.05,这种差异在 5%的显著性水平上显著不同于 0。简单地说,退休的人比非退休的人更有可能经历频繁的记忆丧失/退休的人比非退休的人经历频繁的记忆丧失的概率有统计学上的显著差异。
如果你的读者是推断统计学的专家,上述结论应该没问题。然而,如果你将上述结果传达给从未上过推断统计学课程的人,那该怎么办呢?
在我开始学习推断统计学之前,如果有人向我展示上述发现,我会跳过前面的部分,只关注“简单地说”之后的部分。至关重要的是,我会把“统计显著”解释为“使用统计方法发现的实质性/值得注意/值得注意/显著/重要的发现”
但是一个有统计学意义的发现到底意味着什么呢?(我会在另一篇文章中详细讨论这个问题)。在我们的例子中,这意味着我们有充分的证据相信,退休人员和非退休人员之间频繁失忆的真实总体水平差异不同于 0 。
https://vivdas.medium.com/statistical-significance-are-you-interpreting-correctly-7fff26130fb7
现在,不管我是一个业余读者还是一个政策制定者,上面的发现对我来说既不有趣也没用。为什么?
对于没有接受过统计学培训的人来说,最令人困惑的部分可能是,起初,你说两者之间的差异是 5.24 个百分点。然后,你说这个差和 0 有很大的不同。数学上,5.24 和 0 不一样不是很明显吗?🤔
如果我把注意力放在这个差异不为零的事实上,这个发现似乎很明显,因为我已经知道退休的人更有可能变老,而老年人更有可能经历频繁的记忆丧失。此外,我不知道两组结果的差异有多大。我想知道差异的【程度】或【大小】,这样我就能理解该发现的现实世界意义(而不仅仅是所谓的统计意义)。
传统使用的 p 值方法在这方面失败了。不幸的是,许多学科的惯例是用 p 值法来解释统计检验的结果。在许多情况下,记者和博客曲解了这些结果(不是他们的错🤷♂️ ) ,这最终导致了对现实世界现象规模的大规模误解😕。
这正是我更喜欢置信区间方法的原因。让我们估计一下差异的置信区间:
*confint(lm(MEMLOSS~EMPLOY1_8,data=data,weights = finalwt))*

(图片由作者提供)
作为一名分析师,如果你报告说,退休人员和非退休人员之间频繁失忆的差异的 95%置信区间(CI)为【3.25 个百分点,7.22 个百分点】,作为一名非统计学家,我会直观地这样想:**“有 95%的可能性真正的差异在 3.25 个百分点和 7.22 个百分点之间”**
*** 以上解读直观但错误。有趣的是,很多研究者是这样解释自己 95%的 CI 的 (O'Brien and Yi,2016) 。此外,如果你用谷歌搜索 95% CI 的解释,你会发现大量的资源显示相同的解释。***
95%置信区间方法要有用得多。我们知道统计是减少不确定性的科学工具,理想情况下,任何统计测试都应该:
- 产生目标参数的估计值并且
- **在它的任一侧附加一系列可能的值。
最重要的是,了解某种结果的可能性范围是在不确定情况下制定政策的先决条件。除了推断两组之间结果的真实差异是非零的(因为区间中不包含 0),95% CI 方法帮助我们了解真实差异的可能性范围。
现在,成为“技术上正确的🤓**,95%置信区间的解释与我们直观感知的相反😒。下面是如何考虑这个问题的:**
如果我们从同一人群中抽取 100 个不同的随机样本(即,如果 CFPB 进行 100 次相同的调查)并为每个样本构建 95%的置信区间,我们预计这 100 个置信区间中的 95 个将包含两组结果的(未知)人群水平差异。
实际上,我们只从感兴趣的人群中抽取一个样本(即 CFPB 只进行了一次调查)。因此,这个 CI [3.25 个百分点,7.22 个百分点]包含两组结果中真实人群水平差异的概率是 95%。
上述内容的简化版本是:
正确一: 我们 95%确信区间【3.25 个百分点,7.22 个百分点】包含了两组结果的真实人群水平差异。**
错一: 我们有 95%的把握两组结果的真实人群水平差异在 3.25 个百分点到 7.22 个百分点之间。**
总的来说,这是一个难题:
- 如果你使用 p 值/统计显著性方法来报告你的统计发现,读者会知道某事物的估计值是否为零。然而,在许多情况下,知道某事物的估计值是非零的可能没有什么实际用处。同样,一个有统计学意义的发现可能实际上是无关紧要的,然而读者可能直觉地认为这个发现是实质性的。
- 如果你用 95%置信区间的方法报告你的统计发现,读者会对估计的可能性范围和发现的实际意义有所了解。然而,尽管你尽了最大努力,从严格的技术角度来看,读者可能会以一种错误的方式直观地解释 95% CI。
因此,在这两种情况下都很有可能出现误解。然而,置信区间方法中的误解可能是无害的😐,而在 p 值方法的情况下,误解可能很严重😡。**
更新: 我写了另一篇文章试图进一步解释置信区间的“正确”解释。你可以在这里找到:
***https://vivdas.medium.com/confidence-interval-are-you-interpreting-correctly-a8834ba5a99b
如果你想阅读我以前关于如何尝试了解未知的的一些帖子,这里有一些建议:
https://vivdas.medium.com/why-is-correlation-neither-necessary-nor-sufficient-for-causation-in-non-experimental-data-900c866d59d7 https://vivdas.medium.com/how-to-explore-the-effect-of-doing-something-part-2-228f857bb060
参考文献
2016 年 CFPB 金融福祉调查:https://www . consumer finance . gov/data-research/Financial-Well-Being-Survey-data/
用户指南:https://files . consumer finance . gov/f/documents/cfpb _ nfwbs-puf-User-Guide . pdf
码本:https://files . consumer finance . gov/f/documents/cfpb _ nfwbs-puf-code book . pdf
奥布莱恩,s . f .&易,Q. L. (2016) 。我如何解释置信区间?输血, 56 (7),1680–1683。***
为什么你应该把自己推出舒适区
原文:https://towardsdatascience.com/why-you-should-push-yourself-out-of-your-comfort-zone-e6b6d00fec12
面对数据领域的新挑战

照片由 Katrina Wright 在 Unsplash 拍摄
我喜欢在技术和数据领域工作的众多原因之一是有许多可能性。无论你在哪里工作,总有新的东西要学。坐在一个变化如此之快的地方意味着我们需要适应不舒服的感觉。所以我们需要把自己推出舒适区。
找出你的问题
在我大学的第一次实习中,我所在的项目团队需要在数百名观众面前进行演示。这些人从工程师、数据人员、经理到主管。回想这段经历,很恐怖。当时,我不知道如何在公众面前讲话,尤其是在许多可能并不都有技术背景的人面前。这是第一个把我推出舒适区的项目。这就是我如何找到演讲会,并进入公开演讲。
那我为什么要提这个?因为如果我没有推动自己去做一些新的事情,我就不会学到今天在我的职业生涯中帮助我的许多经验教训。我所做的大部分工作都需要与不同的人进行书面和口头交流。当审视你今天面临的挑战时,找出那些会把你推出舒适区的挑战。至少找出这些问题中的一个来解决,要知道这可能不是一条容易走的路。将你自己推出舒适之外,你会学到很多东西。
例如,回到 2019 年,成为一个项目的领导者是我意识到我在职业生涯中还没有做过的事情。如果你从未领导过数据科学或工程项目,或者担任过首席工程师或数据科学家,这可能会超出你的舒适区,令人害怕。许多陌生人都是第一次进入这种角色,但这是一个很好的学习机会。作为团队负责人,我领导的第一个大项目之一是编写一份提案,并与一个承包商团队一起执行一个项目,以便为我们的数据科学管道实施更多可扩展的指标。这一经历给了我一个机会,在紧迫的时间期限内,与一群我从未合作过的全新的人一起实施一个项目。它有助于展示开发此类项目时有效规划、优先排序和实施的价值。
如果你正在寻找一个需要解决的挑战,考虑和你的经理或技术主管谈谈,如何在你的舒适区之外的项目中承担更多的责任。这些挑战将让你了解自己的优势所在,以及在领导能力和技术技能方面需要改进的地方。
接受挑战
那么这对你意味着什么呢?当你在工作中面临新的挑战时,这是你站出来把自己推出舒适区的时候了。与其依赖团队中的其他人,不如自己接受挑战,决定如何改进。变化可能是可怕的,但不同之处在于你将在哪里对自己和自己的能力了解得最多。改变是你开拓未来发展的地方。
回顾我们第一次成为项目领导的例子,你如何迎接挑战?在我领导的第一个项目中,我根据团队的反馈起草了一份工作说明书。我组织了资源,并确保我们分配了适当的时间来完成任务。我带领我们的开发人员和数据科学家完成了这个项目。有时这意味着我自己做开发工作,重新安排我们不能在期限内完成的工作的优先级,或者解释项目期间明显的重大风险。而在其他时候,这意味着接受完成不同方面工作的其他人的输出,并确保项目进展顺利。
当你面对未来的挑战时,确定你如何完成这项工作。迎接挑战的一些方法包括:
- 研究这个主题或者参加一个课程来学习更多关于某个特定主题的知识。这对于你正在学习的新技术或方法来说会很有用。
- 利用你的同事;如果你认识一个和你有相似经历的人,安排一次一对一的讨论。我经常与不同的人会面,讨论领导技能以及我如何在这一领域继续成长和挑战自己。你不需要独自面对挑战来从中学习。
- 记录你的进步。无论是你团队的技术文档还是你自己的日志,记录下你正在经历的过程。这个文档很好地提醒了你的工作,你已经完成的事情,以及学到的教训。同样,你可以在接下来面临的挑战中利用这一点!文档是你如何解决问题的极好参考。
反思吸取的教训
在谈论承担一个项目的领导时,迎接这个挑战让我理解了一个项目是如何从开始到结束的。从这项工作中,我学到了不同的领导技能,这些技能伴随着保持项目按计划进行和克服意想不到的挑战。这个挑战是承担更复杂工作和领导其他项目的开始。
对我自己来说,我经常喜欢在项目结束时反思我认为可以做得更好的地方以及未来改进的可能行动。例如,我用电子日志记录我的笔记,每月跟踪这些项目,利用这些信息进行自我发展,并在出现评论时进行回顾。你还可以通过其他方式来反思在这次经历中学到了什么,包括:
- 和你的团队一起主持一个回顾会。确定哪些进展顺利,哪些进展不顺利,以及哪些方面可以改进。在大型项目之后,这有助于了解每个人对工作的感受。如果人们不愿意在会议上发言,一些工具允许你匿名提交。
- 调查是向许多人征求反馈的好方法。它们对于获得工作的具体方面进展如何的总体评价也非常有用。
- 与从事项目特定方面工作的关键团队成员进行一对一的交流。如果你遇到比你级别高的人,这些会议也会很棒。这些人通常可以提供关于您可以改进的领域的见解,或者他们会如何以不同的方式处理项目的特定方面。
你可以利用这段时间进行自我反思和团队反思,以了解如何改进前进。大多数项目不仅仅是一个人单独编码,因此获得广泛的反馈对于评审来说是有价值的。
最后的想法
新的挑战可能令人紧张或担忧,但它们也可能是我们最杰出的老师。第一次承担团队领导的角色并领导项目在开始时是一次具有挑战性的经历。我接受了以前从未面对过的新问题,学会了如何评估完成工作的风险,并反思了学到的教训。正是这些类型的项目对我帮助最大。因此,当你审视自己的工作时,请考虑以下几点:
- 确定你想要解决的问题。这可以是一项新技能,学习一种新算法,承担一个新项目,或者其他任何事情。
- 迎接这个问题给你带来的挑战。有时这可能会变得很困难或看起来很可怕,但当我们把自己推出舒适区时,会有很多东西要学。
- 反思吸取的教训。花点时间思考这个挑战的结果。你从这次经历中学到了什么,你如何在下一次经历中运用这些?
你经常把自己推出舒适区吗?如果不是,是什么阻止了你采取下一步行动?
感谢阅读!我希望你喜欢阅读我所学到的东西,并对关注更多,订阅我的通讯。
为什么应该用 Zarr 保存 NumPy 数组
原文:https://towardsdatascience.com/why-you-should-save-numpy-arrays-with-zarr-dabff4ae6c0c
利用 Dask 更快地读写阵列

图片由 Sebastian Kanczok 通过 Unsplash 拍摄
TL;博士;医生
这篇文章告诉你为什么以及如何使用 Zarr 格式来保存你的 NumPy 数组。它指导您使用 Zarr 和 Dask 并行地读取和写入大型 NumPy 数组。
如果你想直接进入,这是代码。如果您对代码有任何疑问,请在 Twitter 上联系我。
保存 NumPy 数组的常用方法
存储 NumPy 数组的三种常见方式是存储为 [.csv](https://crunchcrunchhuman.com/2021/12/25/numpy-save-csv-write/) 文件,存储为 [.txt](https://mungingdata.com/numpy/save-numpy-text-txt/) 文件或.npy文件。
这些方法都有重要的局限性:
- CSV 和 TXT 文件是人类可读的格式,不能包含大于二维的 NumPy 数组。
- 原生 NPY 二进制文件格式不支持并行读/写操作。
让我们在下面看看这一点。
我们将从创建一个伪 NumPy 数组开始。我们将使用np.random.rand生成两个填充了随机数的数组,一个是二维数组,一个是三维数组:
import numpy as np array_XS = np.random.rand(3,2)
array_L = np.random.rand(1000, 1000, 100)
将三维array_L存储为.txt或.csv会抛出一个值错误:
np.savetxt('array_L.txt', array_L, delimiter=" ") np.savetxt('array_L.csv', array_L, delimiter=",") ValueError: Expected 1D or 2D array, got 3D array instead
您可以将三维数组存储为. npy 文件。这是可行的,但不能扩展到大于内存的数据集或其他需要并行读取和/或写入的情况。
np.save('array_L.npy', array_L)
用 Zarr 保存 NumPy 数组
除了上面的三个选项,考虑将 NumPy 数组保存到 Zarr 中,这是一种用于存储分块、压缩的 N 维数组的格式。
Zarr 的三个最重要的优势是:
1.内置多种压缩选项和级别
2.支持多个后端数据存储 (zip,S3 等。)
3.可以并行读写数据 *在 n 维压缩块中吗
Zarr 还被 Dask、TensorStore 和 x-array 等 PyData 库广泛采用,这意味着将这种文件格式与支持的库一起使用时,性能会有显著提高。
** Zarr 支持分别并发读取和并发写入,但不支持同时并发读取和写入。*
用 Zarr 压缩 NumPy 数组
让我们看看 Zarr 的压缩选项。下面,我们将小数组和大数组保存到.zarr并检查结果文件大小。
import zarr
# save small NumPy array to zarr
zarr.save('array_XS.zarr', array_XS) # get the size (in bytes) of the stored .zarr file
! stat -f '%z' array_XS.zarr
>> 128 # save large NumPy array to zarr
zarr.save('array_L.zarr', array_L) # get the size of the stored .zarr directory
! du -h array_L.zarr >> 693M array_L.zarr
将array_L存储为 Zarr 会导致文件大小显著减少(array_L减少约 15%),即使只有默认的开箱即用压缩设置。查看随附的笔记本以获得更多压缩选项,您可以调整这些选项来提高性能。
用 Zarr 加载 NumPy 数组
您可以使用zarr.load()将存储为.zarr的数组加载回 Python 会话中。
# load in array from zarr
array_zarr = zarr.load('array_L.zarr')
它将作为一个常规的 NumPy 数组加载。
type(array_zarr) >>> numpy.ndarray
Zarr 支持多个后端数据存储。这意味着你也可以轻松地从基于云的数据商店加载.zarr文件,比如亚马逊 S3:
# load small zarr array from S3
array_S = zarr.load(
"s3://coiled-datasets/synthetic-data/array-random-390KB.zarr"
)
利用 Zarr 和 Dask 并行读写 NumPy 数组
如果您正在处理存储在云中的数据,那么您的数据很可能比本地机器内存大。在这种情况下,您可以使用 Dask 并行读写大型 Zarr 数组。
下面我们试着加载一个 370GB。zarr 文件直接插入到我们的 Python 会话中:
array_XL = zarr.load(
"s3://coiled-datasets/synthetic-data/array-random-370GB.zarr"
)
这将失败,并出现以下错误:
MemoryError: Unable to allocate 373\. GiB for an array with shape (10000, 10000, 500) and data type float64
加载同样的 370GB。zarr 文件放入 Dask 数组工作正常:
dask_array = da.from_zarr(
"s3://coiled-datasets/synthetic-data/array-random-370GB.zarr"
)
这是因为 Dask 评估懒惰。在您明确指示 Dask 对数据集执行计算之前,不会将数组读入内存。在这里阅读更多关于 Dask 的基础知识。
这意味着您可以在本地对该数据集执行一些计算。但是将整个数组加载到本地内存仍然会失败,因为您的机器没有足够的内存。
注意:即使你的机器在技术上有足够的存储资源将这个数据集溢出到磁盘,这也会大大降低性能。
https://coiled.io/blog/common-dask-mistakes/
使用线圈扩展到 Dask 集群
我们需要在云中的 Dask 集群上运行它,以访问额外的硬件资源。
为此:
- 旋转盘绕的簇
cluster = coiled.Cluster(
name="create-synth-array",
software="coiled-examples/numpy-zarr",
n_workers=50, worker_cpu=4,
worker_memory="24Gib",
backend_options={'spot':'True'},
)
2.将 Dask 连接到该群集
from distributed import Client
client = Client(cluster)
3.然后轻松地在整个集群上运行计算。
# load data into array
da_1 = da.from_zarr(
"s3://coiled-datasets/synthetic-data/array-random-370GB.zarr"
) # run computation over entire array (transpose)
da_2 = da_1.T da_2%%time
da_2.to_zarr(
"s3://coiled-datasets/synthetic-data/array-random-370GB-T.zarr"
) CPU times: user 2.26 s, sys: 233 ms, total: 2.49 s
Wall time: 1min 32s
我们的 Coiled 集群有 50 个 Dask workers,每个都有 24GB RAM,都运行一个包含必要依赖项的预编译软件环境。这意味着我们有足够的资源轻松地转置数组,并将其写回 S3。
Dask 能够并行地为我们完成所有这些工作,而无需将数组加载到本地内存中。它在不到两分钟的时间内将一个 372GB 的阵列加载、转换并保存回 S3。
保存 NumPy 数组摘要
让我们回顾一下:
- 许多存储 NumPy 数组的常用方法都有很大的局限性。
- Zarr 文件格式提供了强大的压缩选项,支持多个数据存储后端,并且可以并行读/写 NumPy 数组。
- Dask 允许您充分利用这些并行读/写能力。
- 将 Dask 连接到按需盘绕的集群允许在大于内存的数据集上进行高效计算。
在 LinkedIn 上关注我,了解定期的数据科学和机器学习更新和黑客攻击。
原载于 2022 年 1 月 5 日https://coiled . io。
为什么现在应该停止在 SQL 连接中使用“OR”
原文:https://towardsdatascience.com/why-you-should-stop-using-or-in-sql-joins-right-now-d5b6e83464cf
向查询运行时优化迈出“急需”的一步

何塞·阿拉贡内塞斯在 Unsplash 上的照片
根据我的经验,在 Python 之后,结构化查询语言(SQL)是众多数据科学家在他们的机器学习/数据科学项目中最受欢迎的工具。玩数据库并看到它们通过 SQL 中定义的各种机制相互交互是令人愉快的,其中之一当然是 SQL JOINS!
如果你对连接不熟悉,我强烈推荐这篇文章,以便在继续之前理解它们。说到这里,让我们转到为什么我把你带到这里,当然,为什么我说你应该立即停止在 SQL 连接中使用条件“OR”。
实验一:
考虑下面的查询,它采用两个表,即 Table1 和 Table2,,并在【col 3】或【col 2】中的值相等的情况下,在两个表之间执行内部连接。

涉及内部连接的 SQL 查询(作者使用 snappify.io 创建的图像)
乍一看,我相信您会想,这个查询有什么问题,对吗?但是当我实现了一个类似的查询,涉及到一个带有内部连接的条件“OR ”,每个表上有将近一万条(10⁴)记录时,查询表现异常。它花了将近 3 小时 55 分钟来执行,这促使我解决这里涉及的问题。我用一个条件“AND”代替条件“OR”进行了一个类似的实验,它在几分钟内就被执行了。
实验二:
在 StackOverflow 上快速查找将我带到了这里,这表明我们可以将条件“OR”转换为“UNION”操作,如下所示:

涉及内部连接和联合的 SQL 查询(作者使用 snappify.io 创建的图像)
本质上,我们可以将连接条件分成多个查询,并使用“SQL UNION”方法将结果连接起来。你可以在这里阅读 SQL 中的 UNION。
令我惊讶的是,这个查询仅用了 2 分钟就在实验 1 中使用的相同表上执行了。
为了消除随机性并确保资源的不可用性不会对运行时间产生影响,我将上述实验重复了三次。下面的柱状图描述了实验 1 和实验 2 在这三次试验中的平均运行时间。

实验 1 和实验 2 的运行时间是三次试验的平均值。
上面的观察确实提出了许多关于 SQL 的查询规划器如何优化 SQL 查询的问题。要知道为什么条件“或”特别有问题,让我们重复下面的问题。
问题 SQL 中的连接到底是如何执行的?
在这里,如果您首先理解要连接两个表,通过执行嵌套搜索来采用简单的方法是不可行的,这将会有所帮助,因为我们通常要处理大量的数据。例如,一个通常的嵌套搜索,每个表中只有一百万行(10⁶),相当于一个万亿循环操作(10)。
为了优化这一点,SQL 利用散列连接通过查找两个输入之间的匹配行来合并表,这通常比使用嵌套循环更有效。这里,每一行都用一个特定的散列函数映射到它的散列值,两个表的比较发生在一个特定的散列桶中。
问题 2:实验 1 中接近四个小时的查询运行时间是否表明它选择了嵌套连接?
是的。实验 1 中的连接很可能是使用嵌套循环中的表扫描来执行的,如果表很大,这显然会很慢。
问题 3:如果连接通常在 SQL 中被优化,为什么 SQL 在实验 1 中仍然采用嵌套操作?
这个问题特别与实验 1 中使用的连接条件有关,即,
condition1 **OR** condition2
我们可以从观察到的运行时推断出,上述连接条件对于散列连接是不可优化的。换句话说,SQL 不够聪明,无法优化连接条件或认识到查询本质上等同于多个查询的“联合”——除非像实验 2 中那样明确指定。这就是它花费了令人难以置信的大量时间来执行的原因。
问题 4:为什么实验 2 中的查询更快?
如上所述,SQL 无法识别实验 1 中的查询可能等同于两个查询的“联合”。这使得原始查询不可优化,我们没有其他选择,只能显式地将这些信息嵌入到查询中,并手动将其拆分,以协助 SQL 的优化模块。
这就把我们带到了这篇文章的结尾。总之,请注意,据我所知,这些限制特别存在于 SQL 连接中。你还可以在其他地方自由使用条件句“或”,如“WHERE”、、【HAVING】、、等。
非常感谢阅读,我希望你喜欢它。
非常感谢您的想法和反馈。

憨豆先生的迷因是用 clideo.com 创造的。
为什么您应该使用 Scikit-Learn 管道
原文:https://towardsdatascience.com/why-you-should-use-scikit-learn-pipelines-8754b4d1e375
这个工具把你的代码带到了一个新的高度

约翰·金南德在 Unsplash 上的照片
在使用 Scikit-learn 包足够长的时间后,机器学习工作流可能会开始出现重复。任务通常需要对数据进行一系列转换,然后将处理后的数据拟合到估计器,估计器随后将进行预测。
对于那些希望将他们的 Scikit-learn 专业知识提升到下一个级别的人,该模块提供了Pipeline类,这是一个使用户能够以更加用户友好的方式执行这些转换的工具。
在这里,我们深入研究 Scikit-learn 管道做什么,以及为什么它们应该更多地用于机器学习项目。
为什么要使用管道?
Scikit-learn pipeline 是一个工具,它将工作流程的所有步骤连接在一起,以简化流程。
构建管道的主要好处是提高了可读性。
管道能够通过一次调用执行一系列转换,允许用户用更少的代码获得结果。
它们只需要一个fit方法来将所有转换应用于数据,这比将fit和transform方法应用于每个预处理步骤的训练和测试数据要方便得多。
此外,通过使用管道来链接过程中的所有转换,用户一眼就可以轻松理解工作流。有了这种安排,提前进行修改和发现任何潜在错误将变得更加容易。
管道是强制性的吗?
此时,您可能会想:管道可能会提高可读性,但是我们不能通过使用适当的文档编写结构良好的代码来获得可读性吗?
答案当然是肯定的!
Scikit-learn 管道只是一个方便的工具,对于项目的成功并不重要。
然而,当一个模块直接提供了一个可以让生活变得如此简单的工具,为什么要拒绝它呢?
个案研究
Scikit-learn 管道的效用可以通过用 Python 创建一个管道得到最好的演示。
假设我们正在建立一个使用玩具数据集预测乳腺癌的模型。
构建模型的步骤包括:
- 使用最小最大缩放器缩放功能
- 利用主成分分析降低维数
- 将数据拟合到随机森林分类器
让我们先在没有管道的情况下进行这个练习,然后再使用管道。然后,我们可以在过程和模型性能方面比较这两种方法。
首先,我们需要导入以下包:
- 在没有管道的情况下训练模型
创建一个没有管道的评估器非常简单。对于每个预处理步骤,我们对训练数据使用fit_transform方法,对测试数据使用transform方法。然后,我们将处理后的数据与随机森林分类器相匹配,并用它进行预测。
既然已经训练了随机森林分类器,让我们使用 f-1 得分度量来评估它在测试集上的性能。

代码输出(由作者创建)
这是训练分类器的可行方法。然而,由于一些原因,从长远来看,它可能并不理想。
首先,随着编写更多的代码来促进转换,出错的几率会增加。例如,可能会意外地将fit_transform方法应用于测试数据,或者以错误的顺序执行转换(例如,在特征缩放之前执行 PCA)。
其次,这种方法产生的代码在可读性方面不是最理想的。当您执行工作流的每一步时,代码可能看起来很直观,但是当您在几个月后重新访问您的工作时,它会保持这种方式吗?对阅读你的代码的其他人来说是直观的吗?
2。用 a 管道训练模型
这一次,我们将使用管道将过程中的所有步骤连接在一起。
这意味着创建一个管道对象,并在steps参数中指定所有的转换。输入的参数是一个元组列表,每个元组代表一个转换。
有多简单?
通过最少的代码,我们能够执行所有的转换,训练模型,并使用模型生成预测。
我们只需要使用一次fit方法来执行流水线中的所有步骤,而不是应用大量的fit和transform方法。
此外,我们现在可以清楚地检查在训练模型之前将应用于数据的一系列转换。这使得代码更容易理解,更不容易出错。
管道对象现在可以像任何估计器一样用于生成预测。让我们看看它在测试集上的表现。

代码输出(由作者创建)
f-1 分数保持不变,这并不奇怪,因为管道只是方便的工具,并不能提高模型性能。然而,如果我们比较两种方法中使用的代码,很容易看出 Scikit-learn 管道的吸引力。
3。额外收获:用管道优化模型
我们已经展示了 Scikit-learn 管道如何通过使用户能够用最少的代码训练模型来增强可读性。
然而,管道还有一个值得一提的特性。
除了用更简洁的代码顺序执行转换之外,pipeline 对象还可以通过超参数调整来优化模型性能!
通常,超参数调整用于单独为估计量寻找最佳超参数集。然而,通过管道,用户也可以优化特征工程过程!
为了实现这一点,我们需要创建存储工作流中所有步骤的管道对象以及存储所有感兴趣的超参数的字典。
该字典包含超参数及其对应的值列表,作为键-值对。该字典中的关键字必须以
在创建了管道对象和超参数字典之后,我们可以使用网格搜索来执行超参数调整。
最后,我们可以用最佳超参数导出模型,并使用它来生成预测。

代码输出(由作者创建)
结论

在 Unsplash 上 Prateek Katyal 拍摄的照片
总而言之,Scikit-learn 管道作为一种手段,以更简洁的方式将机器学习任务中的所有步骤链接在一起。
它们可能不会提高模型性能,但它们简化机器学习工作流程的能力使它们变得非常有价值。
我祝你在数据科学的努力中好运!
为什么不应该用 pandas.get_dummies 进行机器学习
反对用熊猫做热门编码的理由

来自 Pexels 的 Alexander Kovalev 的照片:https://www . Pexels . com/photo/gray-photography-of-stop-signage-under-sky-1585711/
熊猫图书馆因其在机器学习项目中的实用性而闻名。
然而,Pandas 中有一些工具对于训练模型来说并不理想。这种工具的一个最好的例子是get_dummies函数,它用于一个热编码。
在这里,我们提供了熊猫的一个热门编码功能的快速纲要,并解释了为什么它不适合机器学习任务。
一个关于熊猫的热门编码
让我们先快速回顾一下如何用 Pandas 对变量进行热编码。
假设我们正在处理以下数据:

代码输出(由作者创建)
我们可以从这个数据集中创建虚拟变量,方法是识别分类特征,然后使用get_dummies函数转换它们。

代码输出(由作者创建)
然后,我们可以用虚拟变量替换数据集中的当前分类特征。

代码输出(由作者创建)
总而言之,get_dummies功能使用户能够用最少的代码来编码他们的特性,适合熊猫工具。
熊猫的缺点. get_dummies
get_dummies函数是一种快速简单的变量编码方式,可用于任何后续分析。然而,出于机器学习的目的使用这种编码方法是错误的,原因有二。
- get _ dummies 函数不考虑看不见的数据
任何机器学习模型都必须考虑看不见的数据。因此,用测试数据生成的虚拟变量必须与用训练数据生成的虚拟变量相匹配。
考虑到这一点,很容易看出使用 Pandas 进行热编码会导致什么问题。
熊猫图书馆的get_dummies方法基于当前值对特征进行编码。然而,测试数据中唯一值的数量总有可能不与训练数据中唯一值的数量相匹配。
在上例的数据集中,job特征包含 3 个唯一值:“医生”、“护士”和“外科医生”。对该列执行一次热编码会产生 3 个虚拟变量。
然而,如果测试数据的job特性比训练集的特性有更多的唯一值,会发生什么呢?这些数据会产生与用于训练模型的数据不匹配的虚拟变量。
为了说明这一点,让我们用这个数据训练一个线性回归模型,把income作为目标标签。
假设我们希望用一个测试数据集来评估这个模型。为此,我们还需要对新数据集进行热编码。但是,该数据集的job特征有 4 个唯一值:“医生”、“护士”、“外科医生”和“药剂师”。
因此,在对测试集执行一次热编码后,训练集和测试集中的输入特征数量不匹配。

代码输出(由作者创建)
一个热编码测试数据集有 8 个输入要素。
不幸的是,用包含 7 个输入特征的数据训练的线性回归模型将不能使用不同维度的数据进行预测。
为了展示这一点,让我们尝试在测试集上使用predict方法来生成预测。

代码输出(由作者创建)
正如预期的那样,该模型无法用这些测试数据进行预测。
2。get_dummies 方法与其他机器学习工具不兼容。
数据预处理通常需要执行一系列操作。
不幸的是,Pandas 库的一个热门编码方法很难与标准化和主成分分析等操作无缝结合使用。
虽然get_dummies函数当然可以集成到预处理过程中,但它需要一种在代码可读性和效率方面不太理想的方法。
更好的选择
幸运的是,有更好的编码分类变量的方法来解决上述问题。
这些方法中最受欢迎的是 Scikit Learn 的onehotencode,它更适合机器学习任务。
让我们使用当前数据集来演示 OneHotEncoder。
首先,我们创建一个OneHotEncoder对象,将‘ignore’赋给handle_unknown参数。这确保了经过训练的模型能够处理看不见的数据。
接下来,我们创建一个存储OneHotEncoder对象的Pipeline对象。
之后,我们创建一个ColumnTransformer对象,我们可以用它来指定需要编码的特性。
需要一个ColumnTransformer对象,因为没有它,每个列都将被编码,包括数字特征。使用该对象时,有必要将“通过”值分配给remainder参数。这确保了没有在转换器中指定的列不会被删除。
有了这个新的 column transformer 对象,我们现在可以用fit_transform方法对训练数据集进行编码。
最后,我们可以用transform方法对测试数据进行编码。
这一次,生成预测应该没有问题,因为训练集和测试集具有相同数量的输入特征。

代码输出(由作者创建)
OneHotEncoder 的工作原理
在机器学习环境中,Scikit Learn 的OneHotEncoder优于 Pandas library 的get_dummies方法的原因有很多。
首先,它使用户能够训练模型,而不必担心训练集和测试集之间的分类特征的唯一值的差异。
其次,由于 Scikit Learn 库提供的其他工具,用户现在可以更有效地简化其他操作。
因为像StandardScaler和PCA这样的流行类来自同一个 Scikit Learn 包,所以更容易内聚地使用它们并有效地处理数据集。尽管给定的任务需要大量的操作,但是用户会发现用可读的代码来执行它们是很容易的。
使用OneHotEncoder的唯一缺点是它的学习曲线有点陡。希望学习使用该 Scikit 学习工具的用户还必须熟悉其他 Scikit 学习工具,如Pipeline和ColumnTransformer。
结论

照片由 Prateek Katyal 在 Unsplash 上拍摄
当我开始训练模型时,使用熊猫为机器学习任务编码是我最大的失误之一,所以我认为值得强调这个问题,以免其他人犯同样的错误。
即使您一直在使用 Pandas 进行热编码,我也强烈建议您在未来的项目中切换到 Scikit Learn 库的OneHotEncoder。
我祝你在数据科学的努力中好运!
为什么您的仪表板被忽略
原文:https://towardsdatascience.com/why-your-dashboards-are-ignored-a46db2ae888c
为什么你做的即席分析是错误的
原文:https://towardsdatascience.com/why-youre-doing-ad-hoc-analytics-wrong-49d177202c7a
在你下一次翻 SQL 垃圾箱之前:问为什么并记录所有的事情

可能是红鲱鱼。图片来自 Freepik。
当我在 Airbnb 担任数据科学家时,我最常收到的问题是:
“你知道有多少人点击了这个按钮吗?”
但是这个问题的奇怪之处在于:这很少是利益相关者真正想要回答的问题。
像这样的临时问题是不可避免的(而且实质上!)数据科学/分析工作的一部分,然而这种类型的工作很少被讨论,更不用说优化或反思了。作为分析师,我们已经发展出令人印象深刻的技术能力,能够熟练地回答这类问题,但我们中很少有人学会最大化我们答案的影响——找到问题的根源并回答而不是。也就是说:我们擅长回答问题,但我们不擅长找到正确的问题来回答。
这里的一些自省可以帮助你决定你的团队是作为服务台 SQL 猴子还是作为利益相关者协作中的战略伙伴。在接下来的内容中,我将讨论两种提升特设工作以服务于后者的实用方法:询问为什么和记录一切。
第一步:总是问 W hy
开始临时工作时,第一步也是最重要的一步是简单地询问为什么需要完成这个请求。商业目标是什么?正在回答什么问题?问分析师的问题往往是转移话题。但是在每一个简短的问题背后,通常都有一个有价值的战略请求。在翻箱倒柜地研究数据之前,简单地问一下为什么可以帮助揭示隐藏在下面的真正的、战略性的问题。这一点至关重要,原因如下:
- 更好的决策 问为什么总是导致更好的决策。如果您理解了业务目标,您就能够找到直接解决它的数据,而不是依赖于利益相关者的尝试。非技术人员根本不知道他们需要什么数据(也不应该期望他们知道),更不用说什么分析是可能的了。因此,我们有责任确保我们提取的数据是他们应得的数据,而不是他们“需要”的数据。
- 更有趣的工作 问为什么会给我们带来更多有趣的问题。例如,“我们如何在没有实验的情况下测量影响”比我在文章开头提出的点击按钮的问题有趣得多。我怀疑我们中的许多人都明白,问为什么会是的首选回应方式,但我们故意忽略我们内心高贵的声音,以保护我们的时间用于我们自己的、更智力上令人满意的项目。但这是错误的心态。
- 为自己发现更好的项目 回答足够多的战略要求,你会发现其中往往有规律可循。这些可以导致分析团队应该致力于的长期项目。我们应该像运营产品团队一样运营我们的数据团队,这是通过解决我们利益相关者的问题来实现的,而不是通过建立另一个无用的垃圾板。“以用户为中心是关键”,临时工作是我们成为以用户为中心的主要方式。

每一个快速提问的背后都是一个战略要求。揭开这一层可以为未来更深入的工作提供信息。图片作者。
第二步:文档。一切。
一旦你问了为什么,下一个最有效的方法就是写下你做了什么。写下:
- 业务目标(您刚刚达成一致的目标)
- 你的方法
- 您的发现(以及您的 SQL 查询,以便能够重现这项工作)
- 任何基于你工作的决定。
把它放在一个你的队友可以发现的地方,并创建模板,这样在浏览过去的工作时,认知负荷会最小化。工具在这里可以发挥巨大的作用。在 Airbnb,我们试图使用 Github 和 Google docs 来实现这一目的,但这些努力的问题是,在高度紧急的情况下,它们总是被搁置。现代文档工作区的工作效果要好得多——它们可搜索、易于书写,并且无缝协作。

一个分析模板,在hyperquery中创建。图片作者。
一旦你觉得分析“完成”了,就很容易忽略工作流程的这一部分。但是记录有助于你和其他人利用它超越单一的决定,让你的工作具有规模。您的利益相关者将受益于能够重新审视他们的决策以及这些决策的准确理由,但是当类似的问题不可避免地出现时,您和您的分析师同事可以搜索您的工作库,或者更好地,找到可以激励未来高杠杆努力的模式。
最终意见
临时工作占据了40–50%的分析师时间,但投入其中的注意力很少相称。通过不断地问为什么并实施一些基本的文档实践,你不仅可以让这段时间更有影响力,而且也更有收获。也就是说,不管更好的操作点是什么,为您的团队建立某种策略或一套最佳实践来提升临时工作肯定是值得的。
如果你有任何自己成功的策略,请告诉我们!
有兴趣了解更多关于为您的特设工作流提供超级动力的信息吗?在 联系我 Robert @ hyperquery . ai,在hyperquery . ai查看我们专用的即席分析平台。
人工智能会理解我们的情感吗?
原文:https://towardsdatascience.com/will-ai-ever-understand-our-emotions-2f27b5e2fa9d
情感计算
使用机器学习来识别人类情感

人工智能改变了我们的生活。到处都有大量的应用。从手机上的人脸识别软件和智能数字助理,如 Siri、Alexa 或谷歌助理,到推荐你下一首最喜欢的歌曲或电视剧。人工智能正被用于另一个计算领域,即情感计算。然而,这意味着什么呢?简单来说,就是机器有能力处理一些我们认为机器永远做不到的事情,也许科幻电影里除外;情绪!
我们的 Apple Watch 或 Fitbit 能知道我们的感受吗?
好吧,在现实中,这不是一个是或否那么简单。然而,总而言之,从生理传感器中可以提取出一些线索,帮助我们推断个人的情绪状态。例如,临床上最有效的方法之一是使用电流传感器来评估个体的压力。简单来说,传感器可以检测皮肤上的微量汗液,这有时表明我们处于紧张的状态。你的手心出汗了多少次,是什么时候?也许在考试或紧张事件之前?
另一个例子可能是紧张以及不同的人如何处理它。有些人可能会比平时动得更多,有些人可能会保持异常的安静和静止。在这两种情况下,身体活动与其通常状态不同,并且潜在地心率升高。
另一项经临床验证的观察将心率与压力联系起来。更具体地说,已经观察到心率即使平均为比如说每分钟 60 次,在一分钟内也不会以相等的间隔跳动 60 次。更具体地说,一个心跳到另一个心跳(心跳到心跳的间隔)将略有不同,例如 828 毫秒、845 毫秒、754 毫秒、742 毫秒。方差通常是几十毫秒到一百毫秒左右。这是一个重要的观察结果,因为它与自主神经系统的功能相关。具体来说,当我们处于战斗或逃跑模式时,心跳间隔往往更恒定,而如果我们放松,似乎会有更大的变化。这被正式定义为心率变异性(HRV ),它从生理角度让我们了解我们的感受。专业运动员也使用 HRV 来优化他们的训练制度,并在一些正念练习和生物反馈领域普遍推荐。
这些方法的共同点是使用特殊传感器以各种方式监控人体活动。说了特殊传感器,并不意味着它们不常见,不再是了。至关重要的是,能够测量心率、温度和运动的传感器嵌入到许多人拥有的商业设备中,如智能手表和健身带等。
但是这一切和人工智能有什么关系呢?
简而言之,这些传感器产生(时序)信号,算法可以利用这些信号来识别模式。实际上,从机器学习的角度来看,我们可以基于那些捕捉信息的信号来创建特征(特性),这些信息可能被归类为特定的情绪状态。我们基本上是在那些捕捉情绪的信号中寻找那些线索。例如,从心率信号中,您可以计算并提取 HRV(如上所述,它在压力中起作用),或者从加速度计中计算活跃的时间等等。
这正是 2015 年一项用户研究从可穿戴设备收集生理数据时的想法。特别是,这项研究旨在通过在参与者身上安装身体传感器来收集上班时的数据,并在离开时停止收集数据。这项研究进行了 2 周,收集了许多小时的数据信号。为了开发有监督的机器学习模型,我们还需要标签。这就是为什么每个用户都必须下载并使用一个配套的 android 应用程序,用户必须在工作日和两周内每隔 2 小时报告一次他们的感受。Android 的应用程序界面示例如下所示。

用于情绪数据收集的 android 应用程序截图(Zenonos 等人,2016 年)
2 周后,对数据进行评估,并将参与者的手动标签(他们的情绪)与他们相应的多模态信号对齐。下面展示了从获得各种可穿戴数据信号到预测和推断潜在情绪的端到端过程的可视化。

情绪识别系统(MSR)如何工作的视觉描述(Zenonos et al. 2016)
由于不同的人可能会对情绪有不同的理解,或者对相同的情况有不同的反应,因此我们进行了进一步的研究来探索这种直觉。从下图中我们可以看到,与上面的方法相比,还有一个额外的步骤。具体来说,数据信号根据它们的相似程度被适当地分组在一起,以便不同的情绪感知落入相同的桶中(感知集群)。

感知集群架构的视觉描述(Khan 等人,2020 年)
总而言之,这项研究和有效计算在理解人类情感方面取得了巨大的进步。如今,随着商用智能手表能够对人们的压力水平提供反馈,这一点变得尤为明显。这只是开始。想象一下这项技术的可能性。不仅要了解情绪,还要了解潜在的精神状态,比如疲劳和冷静,这可能会挽救生命。想象一下一个卡车司机或外科医生在长途旅行或漫长而艰难的手术之前的情景。从精神状态的角度来评估健康状况的工具可能非常有用,如果不是至关重要的话。当然,然而,需要更多的研究来改进和验证这些模型,以及考虑任何伦理和隐私因素。
你可以在这里找到这篇文章中讨论的两篇研究论文:
我会抽一张战斗 VIP 通行证吗?探索神奇宝贝交易卡游戏的可能性
许多纸牌游戏都涉及运气。不管一个玩家或一副牌有多好,拥有或不拥有优势都可能为轻松获胜或令人难忘的失败铺平道路。这条规则适用于神奇宝贝交易卡牌游戏(PTCG)。除了是我们有些人喜欢的生物牌,PTCG 是一个复杂和充满竞争的游戏,有丰富的机制,选项和机会,纯粹的技能不会让你走那么远。
其中一张卡叫做战斗 VIP 通行证 (BVP),我不确定我是爱它还是恨它。BVP 是竞争中的一张王牌。这是一张物品训练卡,可以让你在你的牌组中搜寻最多 2 个基本神奇宝贝,并将它们放在你的长凳上。然而,它有一个很大的缺点:你只能在第一个回合使用它。

战斗 VIP 通行证。插画师:(https://www.pokemon.com/us/pokemon-tcg/pokemon-cards/上田良?specialty artist = Ryo % 20 Ueda)。我拍的照片。
上周末玩的时候,我处在一个只有拿到这张牌才能赢的位置。所以我的第一次转变来了;我抽了一张牌,你猜怎么着?不是 BVP。如果这还不够的话,在第二场比赛中,我处于一种我会喜欢拥有那张牌的情况。
一副神奇宝贝牌有 60 张牌,我有四份 BVP 的拷贝。游戏开始时,每个玩家抽七张牌,每回合开始时抽一张。所以你用八张牌开始一场比赛,然而,在两场比赛中,我没有 BVP。在我的沮丧中,我想要答案;关于我为什么没有画 BVP 的答案。这个答案的问题是,“在第一轮抽中 BVP 的概率是多少?”
我使用超几何分布函数(图 1)来解决我计算的概率。这个分布回答了以下类型的问题:“如果我在一个有 N 个东西的桶中有 K 个东西,如果我从桶中取出 n 个东西,我得到这些东西的 k 个的机会有多大?”除了有助于确定神奇宝贝的概率之外,这种分布通常有助于确定没有替换的成功概率。它类似于数据科学中最常见的分布之一——二项式分布,在这种分布中,您使用替换进行采样。在我的例子中,考虑到我的一副 60 张牌中有 4 张,我想知道我第一手牌恰好抽到一张 BVP 的概率。用数学的方法来说,并解释维基百科的定义,超几何分布“描述了在 N 次抽奖中 K 次成功的概率,从大小为 N 的有限群体中,正好包含 K 个具有该特征的对象。”

图 1:超几何分布
拿到我第一手牌的概率
让我们从最简单的情况开始:我最初的手牌中至少有一张 BVP 的概率。在比赛开始时,每个玩家抽七张牌。你想在这手初始牌中拿到一张 BVP,因为你只能在第一回合使用它。让我们使用图 1 所示的超几何分布来计算概率。但我不想只给出数字,而是想一步一步地解释这个过程。
等式的右边是我们想要解决的问题。参数K代表群体中有多少感兴趣的对象。这里的值是 4 ,因为这是我在我的牌组中运行的 BVP 数。接下来是k,我们希望获得的对象数量。在这种情况下,值是 1 ,因为我想找到至少一个 BVP。N是人口的大小,或者说我的神奇宝贝套牌的大小,也就是 60 。最后,还有n,我要抽的牌数。在这个例子中,这个值是 7 ,因为这是游戏开始时你抽的牌的数量。图 2 显示了插入了值的公式。我将跳过这个计算,因为它涉及几个步骤和巨大的数字。如果你很好奇,谷歌一下“二项式系数”,这是那些带数字的垂直括号的名字。同时,你可以在图 3 中看到我的涂鸦。

图二。求解公式的第一步。

图 3。一次尝试。
我初始手牌中至少抽到一张 BVP 的概率是 33.63 %。我认为由于这张牌对我不好,我的机会更低——但我可能会忘记所有我赢的次数,所以我不会抱怨太多。
我的牌组有四个 BVP,而不是一个,理想情况下,我更愿意抽两个 BVP 来开始这场比赛,我的牌桌上至少有四个神奇宝贝。那么,这个事件发生的概率是多少?现在,我将使用 Python 编程语言编写一个小的计算机脚本来计算抽中两张以及零张、三张和四张战斗 VIP 通行证的概率,而不是使用计算器和我的大脑来解决这个问题。我将分享下面的脚本给那些可能觉得有用的人。
from scipy.stats import hypergeom
import numpy as np
# Supress scientific notation
np.set_printoptions(suppress=True)
def calculate(M, n, N):
[M, n, N] = [M, n, N]
rv = hypergeom(M, n, N)
x = np.arange(0, n+1)
return rv.pmf(x) * 100
calculate(60, 4, 7)
这个脚本输出[60.05, 33.63, 5.93, 0.38, 0.01 ],分别是抽取零、一、二、三或四个 BVP 的概率。拥有所有值的好处是,我们可以将它们相加来计算累积概率,就是这样,获得至少或最多 N 张牌的概率。比如抽两个 BVP 的概率是 5.93% ,但是抽两个以上的概率是 6.32% (最后三个值之和)。下面的折线图(图 4)显示了概率。

图 4。可视化的概率。
画我的第一张卡片
在抽取了最初的七张牌并设置了六种奖品后,我们开始游戏。每个玩家从抽一张牌开始他们的回合,给我们另一次机会获得 BVP。不像上一个例子,我们从 60 个人中抽牌,现在我们从 47 个人中抽牌(60–7–6)。所以,假设我们在起手牌中没有得到任何 BVP,我们的新参数集是 K = 4, k = 1, N = 47, n = 1,这等于概率为 8.51 %。
如果我第二个开始
先开始的缺点是你不能出支持者,一种你只能轮流出一次的牌,因为他们很强大。在这些支持者的卡片中,有一张是我打的,它叫伊里达。这张教练卡允许玩家在其牌组中搜寻任何水中神奇宝贝和物品卡。BVP 是一个物品,所以我可以从伊里达那里抓一个来增加我在第一回合玩的机会。
我想讲的最后一个场景是从第二局开始,在我最初的手牌中没有抽到 BVP 或伊里达,然后抽到了轮到我的牌。在这种情况下,我希望这张抽到的牌是 BVP 或伊里达,这样我就可以搜索 BVP 了。所以,我的新目标是拿到这八张牌中的一张( K =8),而不是四张中的一张。这种情况发生的概率是 17.02%,给了我一个额外的机会在游戏早期看到这张广受好评的卡片。

BVP 和艾瑞达。我拍的照片。
概述
运气影响神奇宝贝交易卡游戏。你可能在玩一场完美的游戏,知道你离胜利只有一步之遥,直到你的对手抽到了一张让比赛变得对自己有利的牌。这篇文章关注的是一张可以在第一回合就决定游戏结果的牌。问题中的卡片是战斗 VIP 通行证,它让你只能在你的第一个回合用神奇宝贝填满你的长椅。
利用超几何分布,我计算了在三种不同情况下抽中 BVP 的概率:在你的第一手牌中抽中,第一次抽中,或者第一回合抽中,假设你的牌组中有 Irida。在第一种情况下,你最初拿到 BVP 的概率是 33.63%。第二种情况,我称之为后备选择,有 8.51%的可能性。最后,还有一个涉及 Irida 的复杂场景,这是一张卡,它的效果让你可以搜索 BVP,并将使用它的几率增加到 17.02%,但前提是你愿意选择第二种。
还有成百上千的场景我没有涉及到。它们包括使用让你抽额外牌的牌,其他让你搜索牌的牌,以及让你用牌组顶上的牌替换你的牌的牌。这张卡会是什么?只有数字知道。现在,我将祈求好运,期待一个令人满意的结果。
高级 ai 会倾向于寻求权力吗?
原文:https://towardsdatascience.com/will-powerful-ais-tend-to-seek-power-e3b6bb02f3a5
播客
高级 ai 会倾向于寻求权力吗?
亚历克斯·特纳在他备受关注的 2021 NeurIPS 论文中写道,为什么我们应该担心人工智能系统的寻权行为
编者按:TDS 播客由杰雷米·哈里斯主持,他是人工智能安全初创公司墨丘利的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。
今天的这一集有些特殊,因为我们将要谈论的可能是第一次对高级人工智能系统在未来可能具有的权力寻求倾向进行的定量研究。
很长一段时间以来,在人工智能安全领域一直存在这样的争论:
- 有些人担心强大的人工智能最终会取代甚至完全消灭人类,因为他们一方面找到了更聪明、更有创造性和更危险的方法来优化他们的奖励指标,另一方面
- 那些说这是终结者的好莱坞胡说八道,以一种无益的和误导的方式将机器拟人化。
不幸的是,最近在人工智能比对方面的工作——特别是 2021 年 NeurIPS 的一篇引人注目的论文——表明人工智能收购的论点可能比许多人意识到的更强。事实上,它开始看起来像我们应该期待看到高能力人工智能系统默认的权力寻求行为。这些行为包括像人工智能系统阻止我们关闭它们,以病态的方式重新利用资源来服务于他们的目标,甚至在极限情况下,产生将人类置于危险之中的灾难。
就这些可能性而言,令人兴奋的是,我们开始开发一种更强大、更量化的语言来描述人工智能故障和寻力。这就是为什么我如此兴奋地与人工智能研究员亚历克斯·特纳坐下来,在这一集的 TDS 播客中,讨论他进入人工智能安全的道路,他的研究议程和他对人工智能未来的看法。
以下是我在对话中最喜欢的一些观点:
- 人工智能对齐是一个非常复杂的问题,从远处看似乎很简单。出于这个原因,包括人工智能研究人员在内的人们可以确信这不值得担心,因为他们根本没有参与对齐风险的真正争论。人们太容易忽视对强大的人工智能系统在未来构成的内在风险的担忧,将其视为“终结者式的场景”,或“埃隆总是在炒作的东西”,而在现实中,人工智能风险论点背后有大量令人信服的工作(甚至越来越多的实验结果)和研究。
- 亚历克斯的论文表明,最优政策——在广泛的环境中实现最大回报的政策——往往会寻求权力。粗略地说,这里的“权力寻求”是指访问那些导致他们在未来有更多选择的状态。例如,对于人工智能系统来说,关机是一种低功耗状态,因为关机后它的动作空间是空的——它没有任何选择。基于 Alex 的工作,我们可以期待具有最优策略的人工智能系统避免因此而被关闭。
- 亚历克斯的论文附有一些警告。首先,它的结论只适用于最优政策,并没有明确指出不完美的政策。因此,你可能会认为次优政策(可能更现实)不会导致同样的权力追逐行为。但是亚历克斯的后续工作填补了这个漏洞:结果证明,产生权力追求根本不需要最优性。
- 第二个警告与亚历克斯对人工智能系统在环境中导航时获得的回报所做的假设有关。Alex 的工作探索了暴露于各种随机奖励分布的人工智能代理的行为,并表明在典型情况下,这些代理倾向于寻求权力。但我们可能希望人为设计的奖励分配风险更小,因为它们被明确设计为安全和有益的。但亚历克斯说,不要这么快:奖励工程是出了名的困难和违反直觉。正如许多实验所表明的那样,即使是今天的人工智能系统也没有为我们想要的东西进行优化:它们为易于测量的代理进行优化,如果这些代理被用作比人类更聪明的代理的奖励指标,那将是彻头彻尾的危险。
编者按:亚历克斯关于权力寻求的工作是重要的、及时的、有前途的。如果你对人工智能安全感兴趣,或者即使你是人工智能风险怀疑论者,我强烈推荐阅读并参与他的工作。我认为,如果不理解权力追求,尤其是亚历克斯的观点,就不可能对人工智能风险有一个知情的、怀疑的看法。你可以在 turneale@oregonstate.edu通过电子邮件联系 Alex。

章节:
- 0:00 介绍
- 2:05 对比对研究感兴趣
- 8:00 比对研究的两大阵营
- 13:10 neur IPS 论文
- 17:10 最佳策略
- 25:00 两件式辩论
- 放松某些假设
- 32:45 对论文的异议
- 39:00 更广义的优化
- 46:35 总结
合成数据会给 ML 工程师带来伦理挑战吗?

茱莉亚·科布利茨在 Unsplash 上的照片
什么是合成数据?
机器学习框架变得越来越普遍,越来越容易使用,对于大多数常见的任务,都有现成的机器学习模型。随着机器学习的模型方面变得商品化,许多机器学习计划的焦点转移到数据上。
一些业内人士估计,数据科学家 70%以上的时间都花在了收集和处理数据上。一些算法需要大量数据,如果没有标准数据集,研究人员可能需要收集和手工标注数据。这是一个缓慢、昂贵且容易出错的过程,这使得机器学习项目变得复杂,并延迟了上市时间。
合成数据集由计算机使用算法方法生成。数据点类似于真实世界的事件,但实际上并不代表真实世界的事件。至少在理论上,合成数据集可以为机器学习模型训练提供无限、高质量、低成本的数据。实际上,事情要复杂一些。
为了使合成数据有效地作为机器学习模型的输入,它需要具有三个有些矛盾的属性:
- 合成数据必须具有类似于真实数据集分布的统计分布
- 理想情况下,合成数据点应该与真实数据点无法区分
- 合成数据点应该彼此充分不同
用于生成数据的随机过程或算法并不总是能让研究者进行精细的控制。很多合成数据采样技术都是基于随机化的,有些是从随机噪声开始,逐渐形成有意义的伪影。这使得调整算法以准确提供模型所需的数据变得困难。
什么是 ML 工程?
机器学习工程师 (ML 工程师)是专注于研究、设计和构建人工智能(AI)系统以运行预测模型的 IT 专业人士。
机器学习工程师是专注于统计和模型构建的数据科学家与可有效训练模型并将其部署到生产中的可操作人工智能系统之间的桥梁。他们的主要角色是评估和安排大量数据,为 ML 算法和模型优化数据,并构建用于创建和运行这些模型的系统。
ML 工程师通常作为数据科学团队的一部分,与数据科学家、数据工程师、数据分析师、数据架构师和业务经理合作。根据组织的规模,他们还可能与 IT 运营、软件开发人员和销售团队进行互动。
合成数据生成方法
基于已知分布生成数据
您可以创建复杂的合成数据集或简单的表数据,而无需从实际数据开始。这个过程始于对实际数据集分布和所需数据特征的深入理解。您对数据的结构理解得越好,合成数据就越接近真实世界。
将真实数据拟合到分布中
如果您有真实世界的数据集可用,您可以通过识别最佳拟合分布来生成合成数据。然后可以根据分布参数创建合成数据点。有两种常用方法来估计最佳分布:
- 蒙特卡罗方法 —使用随机抽样和统计分析结果的迭代方法。这种方法可以生成原始数据集的变体,这些变体足够随机以至于是真实的。它涉及一个简单的数学结构,但需要很高的计算能力。但是,与生成合成数据的其他方法相比,它生成的数据不太准确。
- 非神经机器学习技术 —例如,可以使用决策树来估计真实数据集的分布。然而,机器学习模型可能会过度拟合模型,导致预测分布在原始数据集中的数据点之外具有有限的概括能力。
神经网络技术
神经网络是一种创建合成数据的复杂方法。它可以处理比决策树等传统算法更丰富的数据分布,并可以合成视频和图像等非结构化数据。
合成数据生成有三种常见的神经技术:
- 变分自动编码器(VAE) —一种无监督算法,它学习初始数据集的分布,并使用编码器和解码器产生合成数据。这个模型产生的重建误差可以通过迭代训练来最小化。
- 生成对抗网络(GAN) —一种使用两个神经网络来生成合成数据点的算法。第一个神经网络(生成器)生成假样本,而第二个(鉴别器)学习区分真假样本。尽管 GAN 模型训练复杂且昂贵,但它们可以生成高度真实且详细的数据点。
- 扩散模型 —一种算法,通过逐步引入高斯噪声来破坏训练数据,直到最终图像是纯噪声。然后训练神经网络逐渐去除噪声,逆转这一过程,直到产生新的图像。扩散模型在训练期间非常稳定,并且可以从图像和音频产生高质量的结果。
合成数据的伦理挑战
任何数据集都会有偏差,因为人们在选择与数据集最相关的数据时会做出无意识的决定。例如,许多图像数据集被发现具有不成比例数量的显示白人或男性个体的图像。
合成数据会让这个问题变得更糟。现实世界极其复杂和微妙。合成数据不会创建它所代表的真实数据的“公平样本”——它更有可能专注于真实世界中的特定模式和偏见,并放大它们。另一个方面是,合成数据即使完美地反映了真实的数据分布,也没有考虑到真实世界的动态性。真实数据将不断变化和发展,而合成数据集将仍然是“及时的快照”,最终会变得陈旧。
最大的担忧是,最终由合成数据支撑的人工智能模型将成为一个封闭的系统。他们将基于有偏见的、重复的数据集进行训练,并生成一组封闭的、有限的预测。这些预测将会越来越偏离现实,而且可能会对用户不利。
ML 工程师有工具和技能来缩小这种“现实差距”。但是他们必须意识到这个问题。在许多情况下,合成数据集将无法准确模拟现实,组织将需要承担收集真实数据的成本和复杂性。在许多情况下,将由 ML 工程师做出决定——对于给定的问题,我们需要真实的数据,还是可以满足于合成的数据。
这是一个复杂的挑战,涉及技术、知识和道德层面。在数据的最后,有社会责任感的 ML 工程师将需要权衡问题的重要性,偏见对客户或最终用户的影响,以及数据的成本,并为组织及其客户确定一个最优的解决方案。这是 ML 工程团队肩负的新责任,将影响数百万人的生活和福祉。
结论
在这篇文章中,我解释了合成数据的基础,并介绍了它可能给 ML 工程师带来的几个道德挑战:
- 生成合成数据的成本要低得多,但可能比真实世界的数据更有偏差、更不准确
- 合成数据会放大现实世界数据中已经存在的偏差
- 合成数据可能会引入不准确性,导致错误的决策
- 合成数据对现实世界现象的时间变化不敏感
许多机器学习工程师将面临一个困境——选择更容易的合成数据,同时在质量和偏差上做出妥协,或者投资于提供更高保真度的真实数据集。
真实数据集不是没有偏差的,合成数据集可以以最小化偏差和不准确性的方式构建。在某些情况下,由于注释和解释中的错误,合成数据集甚至可能比真实世界的数据更准确。最终,将由对社会负责的机器学习工程师来做出决定——无论是合成数据还是真实数据——这将影响我们未来生活的许多方面。
风能分析工具箱:预期功率估算器
原文:https://towardsdatascience.com/wind-energy-analytics-toolbox-expected-power-estimator-ce166932f83d
一个开源模块,用于预测运行风力涡轮机的发电量

杰西·德穆伦内尔在 Unsplash 上的照片
介绍
在本文中,我将介绍估算运行风力涡轮机的预期功率的程序,并展示一个带有探索性数据分析的案例研究。除非标题另有说明,所有图片均由作者提供。
预测运行涡轮机的功率输出对于技术和商业目的是有价值的。风能专家通过将当前产量与基于供应商提供或历史计算基准的预期值进行比较来跟踪涡轮机的性能。
功率曲线定义了风速和涡轮机输出功率之间的关系,是风力涡轮机性能分析中最重要的曲线。

涡轮机制造商通常根据流向涡轮机的不间断气流提供供应商功率曲线。然而,运行的风力涡轮机的功率输出受到诸如地形、风速表位置以及与现场其他单元的接近程度等因素的影响。
因此,被称为运行功率曲线的每个涡轮机的历史运行行为可以优选作为基准。新发布的预期功率模块使用涡轮机级运行功率曲线来估计功率输出。
评估程序

预期功率估计过程
评估程序从过滤历史(训练)数据开始,以去除异常运行数据,如停机时间、降额和性能不佳数据点。
接下来,使用 0.5m/s 的风速桶将净化的运行数据分箱,将中值风速和平均功率作为每个桶的代表性功率曲线数据点。
建议使用三种插值方法,即 Scipy interp1 中定义的线性、二次和三次插值方法,对分级后的功率曲线数据进行插值。
模块使用
托管在 PyPi 上的开源 scada 数据分析库包含预期的 power 模块,代码实现的细节可以在 GitHub 上找到。可以通过简单的 pip 命令安装该库,如下所示。
# Pip install library
pip install scada-data-analysis
此外,GitHub repo 项目可以克隆如下:
# Clone github repo
git clone [https://github.com/abbey2017/wind-energy-analytics.git](https://github.com/abbey2017/wind-energy-analytics.git)
从导入熊猫库开始。
# Import relevant libraries
import pandas as pd
接下来,加载训练和测试操作数据
# Load scada data
train_df = pd.read_csv(r'../datasets/training_data.zip')
test_df = pd.read_csv(r'../datasets/test_data.zip')
该模块由一个带有拟合和预测方法的预期功率估算器组成。估计器用唯一涡轮机标识符、风速和功率以及估计方法和插值种类的列标签来实例化。
# Instantiate estimator class
power_model = ExpectedPower(turbine_label='Wind_turbine_name', windspeed_label='Ws_avg', power_label='P_avg', method='binning', kind='linear')# Fit the estimator with the training data
power_model = power_model.fit(train_df)# Predict the power output based on wind speed from test data
pred_df = power_model.predict(test_df)
目前只实现了宁滨方法,而 autoML 方法将在未来的版本中考虑🔨。
案例研究:
预测运行风力涡轮机的预期功率
在本例中,我们将执行探索性数据分析(EDA),预测预期功率,计算生产损失并可视化结果。
涡轮机 SCADA 数据来自由 Engie 运营的法国 La Haute Borne 风力发电场,并且是公开可用的。
数据探索
首先,我们导入相关的库进行分析。

然后,我们加载数据并检查它是否被正确读取

接下来,我们检查数据中的行数和列数

现在,让我们看看涡轮机的数量和数据的时间间隔。

为了完成 EDA,我们可能想要知道哪个涡轮机的生产与现场的其他涡轮机最相关。这里,我们根据每个涡轮机的功率输出进行相关性分析。

数据处理
在此分析中,所需的列是时间戳(日期时间)、涡轮机标识符(风力涡轮机名称)、风速(Ws 平均)和功率输出(P 平均)。因此,我们可以从数据中提取这些列。

此外,我们必须确保数据中没有缺失值,因为所选特征对分析至关重要。如下所示,少于 1%的数据点有缺失值。

下面的代码片段用于生成缺失值在删除前后的可视化效果。
# Create a custom color map
from matplotlib.colors import LinearSegmentedColormapmyColors = ((0.0, 0.8, 0.0, 1.0), (0.8, 0.0, 0.0, 1.0))
cmap = LinearSegmentedColormap.from_list('Custom', myColors, len(myColors))# Plot heatmap
plt.figure(figsize=(15,6))
ax = sns.heatmap(scada_df.isna().astype(int)+1, cmap=cmap);# Post-process visualization
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
colorbar = ax.collections[0].colorbar
colorbar.set_ticks([1.2, 1.8]) # [0.95, 1.05] with no missing values
colorbar.set_ticklabels(['Available', 'Missing'], fontsize=12)
删除缺失值之前:

删除缺失值后:

接下来,我们使用清理数据的 70–30 时间分割来创建训练和测试数据。

训练和测试数据具有以下形状:

建模
让我们通过将我们的估计器拟合到训练数据来构建功率预测模型。

然后,使用测试数据预测涡轮机的预期功率。

结果
在分析结果时,我们将使用均方根误差(RMSE)指标并将结果可视化。此外,我们将比较三种插值方法,即线性,二次和三次。

如下表所示,这三种方法的性能相当,可用于估计预期功率。

此外,下图显示了使用不同插值方法并基于测试数据的每个涡轮机的预期功率曲线。

对于测试数据中的每台涡轮机,与实际生产相比,使用立方模型估算的预期功率的时间序列如下所示:

现在,我们可以计算每个涡轮机的生产损失,以确定哪一个应该优先维修。


如上图所示,汽轮机 R80790 在所考虑的时间内具有最大的生产损失,应优先进行运行检查。此外,涡轮机 R80721 的发电量超过预期,可视为同期性能最好的涡轮机。
总结这个例子,出于商业目的,报告每小时的生产损失通常是有帮助的。损失的时间序列如下:

如果你能在文章中做到这一步,你就是中等冠军了!
对未来的展望
在本文中,我们介绍了用于估算运行风力涡轮机输出功率的预期功率模块,并介绍了一个带有探索性数据分析和建模的案例研究。
我们希望发布的下一个模块将有助于根据风电场的功率曲线和风速分布计算年发电量(AEP)。这将有助于为风能分析中的关键任务提供快速且易于使用的模块。
预期的电源模块现已在 PyPi 上提供,请不要忘记启动GitHub Repo。也可以在这里 玩一下本文使用的 Jupyter lab 笔记本 。
什么更有趣?你可以通过我下面的推荐链接订阅 Medium 来获得更多我和其他作者的启发性文章,这也支持我的写作。
https://aolaoye.medium.com/membership
用 Python 进行风能物理和资源评估
原文:https://towardsdatascience.com/wind-energy-physics-and-resource-assessment-with-python-789a0273e697
了解风速和风向的分布,并利用风能潜力
众所周知,太阳能和风能等可变可再生能源(VRE)是解决全球能源转型难题的关键因素。虽然这些能源不是穷尽性的,并且在没有燃料成本的情况下产生电力,但是目前与它们相关的主要挑战是它们的间歇性和不确定性。太阳不会一直照耀,风也不会一直吹。理解这些能源的物理特性是开发其巨大潜力的第一步。在这篇文章中,我将讨论风能的物理特性以及可以从这种能源中挖掘出来的资源潜力。为此,我将使用 Python。
让我们开始吧。

来自 Unsplash 的 Sander Weeteling 的图片
数据
荷兰是我迄今为止生活过的风最大的国家。这可能是由于该国的地理位置及其靠近北海。在这篇文章中,我正在考虑一个美丽的荷兰学生城镇 Wageningen 来评估风能的潜力。所选位置 2020 年 50 米高度的每小时风速和风向数据从由 NASA 全球能源资源预测(POWER)项目维护的开源数据库中获得(NASA,2022)。
如下图所示,2020 年瓦赫宁根的每小时风速在 0 至 25 米/秒之间变化。中值风速为 6.98 米/秒,平均风速为 7.22 米/秒。

所选位置 2020 年的每小时风速。图片作者。
我想更仔细地检查一年中不同月份的风速是如何不同的。因此,我绘制了月风速曲线,如下图所示。我注意到与其他季节相比,冬季的风速更高。我还在 12 月、1 月和 2 月观察到一些峰值,类似于极端风速的情况。

按月份分类的选定位置的每小时风速分布图。图片作者。
威布尔分布
当全年测量风速时,注意到大部分地区很少出现强风,而中等和清新的风很常见 (丹麦风力工业协会,2003) 。
如果我们将一年中任何地点的每小时风速按升序排序,那么我们得到的形状在统计学语言中称为威布尔分布。这是一种曲线基本向右倾斜的分布类型。威布尔分布的公式如下所示:

威布尔分布函数的公式。图片作者。
某类风速的概率密度函数取决于两个主要参数,即比例参数和形状参数。形状参数决定了曲线的形状,在 1 和 3 之间变化,取决于给定时间范围内某个区域的风速可变性。
根据提供的 2020 年的数据,我使用以下公式获得了荷兰瓦赫宁根风速的形状参数和比例参数,分别为 2.23 和 8.15 米/秒
params = stats.weibull_min.fit(data,
floc = 0, #Fix the location at zero
scale = 2 #set 1st scale parameter)
我想检查不同形状参数值下的风速分布图。因此,我使用scipy.stats.weibull_min.pdf()函数为 0 到 25 米/秒的速度范围构建了不同的曲线,并将参数传递给它。
绘制形状参数对威布尔分布影响的代码

显示风速分布中形状参数影响的图。图片作者。
我注意到风速分布曲线更短但更宽,形状参数值更低(此处为 1.5),如蓝色曲线所示。这类似于更加多变的风。风速分布曲线较高但较窄,具有较高的形状参数值(此处为 3 ),如红色曲线所示。这表示风速更集中在 0 到 15 米/秒之间的更恒定的风
接下来,我使用 seaborn 库的histplot函数绘制了 2020 年荷兰瓦赫宁根的风速数据,如图所示。用于平滑分布的核密度估计被设置为真,并且对于 0 到 25 m/s 之间的每个速度等级,箱的数量被设置为 26
sns.histplot(data = df, x = df.Speed, kde = True, bins = 26)

荷兰瓦赫宁根 2020 年威布尔风分布曲线。图片作者。
我观察到,与其他速度等级相比,4 米/秒和 10 米/秒等级之间的风速具有更高的频率,每个速度等级一年中有超过 600 个小时。
接下来,我绘制了理论风速分布和观测风速分布。

2020 年荷兰瓦赫宁根的理论和观测风速分布。蓝线代表频率(左侧 y 轴),红线代表每个速度等级(x 轴)的累积频率(右侧 y 轴)。图片作者。
理论分布曲线和观察到的分布曲线都显示了类似的模式。然而,理论频率曲线比观测频率更平滑。
威布尔分布函数在评估风能潜力时很重要,我将在功率曲线部分进一步讨论。
风玫瑰图
风玫瑰图显示了风速和风向的分布。为了构建风玫瑰图,我使用了一个名为windrose的 Python 包。使用的代码如下:
from windrose import WindroseAxesax = WindroseAxes.from_ax()ax.bar(df.Direction,
df.Speed,
normed=True, #get % of number of hours
opening= 0.8, #width of bars
edgecolor=’white’)ax.set_legend(loc = “best”)plt.title(f”Wind rose diagram for {place}”)plt.show()

荷兰瓦赫宁根的风玫瑰图。图片作者。
上面的风玫瑰图显示,2020 年瓦赫宁根最盛行的风向是西南方向。这给人的印象是风力涡轮机主要面向最大风能潜力的方向。圆圈周围辐条的长度表示风速的频率(这里用%表示,因为normed设置为真)。轮辐中占主导地位的蓝色和青色表明,2020 年 5-10 米/秒级和 10-15 米/秒级的风速最频繁。
还可以通过在父轴内创建子插入轴并在插入轴上绘制风玫瑰图,将风玫瑰图覆盖在该区域的地图上。这给出了基于该位置的地理地形的风剖面分布的感觉。
在地图上叠加风玫瑰图的代码

在荷兰瓦赫宁根地图上叠加风玫瑰图。图片作者。
势曲线
风力涡轮机的功率曲线是描绘风力涡轮机在不同风速下产生多少电功率输出的图表。这些曲线是通过现场测量发现的,其中从称为风速计的设备(放置在距离风力涡轮机合理距离的桅杆上)读取风速读数,并相对于涡轮机的电功率输出绘制。
对于本文,我考虑 Enercon E53/800 风力涡轮机,因为其轮毂高度也是 50 米,与风速数据相同。功率曲线数据取自风机库(开放能源平台,2019)。
风力涡轮机需要最低风速来开始旋转,即所谓的启动速度。一旦风力涡轮机开始旋转,只有当风速超过阈值水平时,即所谓的接入速度时,风力涡轮机才开始发电。对于给定的涡轮机,接入速度为 3 米/秒。给定的风力涡轮机的功率输出从 3 米/秒稳定增加,直到达到 14 米/秒(额定速度)。从 14 m/s 开始,风力涡轮机开始产生其最大功率输出(额定功率),在这种情况下为 810 kW。
随着风速的增加,风力涡轮机的叶片旋转得更快。然而,风力涡轮机被设计成在超过一定速度时停止运行,以防止损坏叶片。这个速度被称为切出速度,在这种情况下是 25 米/秒。当风速回落到切断速度以下时,涡轮机可以被允许重新启动并发电。

所选风机的功率曲线。图片作者。
为了计算给定风力涡轮机的发电量,给定风速等级下的功率输出需要乘以其相应的频率(以小时数表示)。在 2020 年荷兰瓦赫宁根的给定风力条件下,给定额定功率为 810 kW 的风力涡轮机的总发电量为 2631078 kWh 或 2631 MWh。在一个假设的设置中,如果给定的风力涡轮机全年每小时以额定速度运行,它将产生 7115 千瓦时。因此,给定设置中给定风力涡轮机的容量系数为 37%(前两个数字的比值)。

给定风力涡轮机的发电量计算。图片作者。
结论
虽然风能是通过廉价和清洁的电力来满足日益增长的全球能源需求的一种重要能源,但也存在一些与之相关的挑战——其中一个主要挑战是它的间歇性。然而,理解资源评估的物理和概念是利用任何能源的巨大潜力的第一步。
在本文中,我通过威布尔分布曲线讨论了风速分布的性质,用风玫瑰图分析了风速和风向的同时分布,并评估了使用风力涡轮机的功率曲线可以利用的能量潜力。
本文的 Python 实现可以在 jupyter notebook 的这个 GitHub 库中找到。感谢您的阅读!
参考文献
丹麦风力工业协会(2003 年)。描述风的变化:威布尔分布。
美国国家航空航天局(2022)。世界能源资源(电力)项目预测数据查看器。
开放能源平台(2019)。风力发电机库。
用水文学和自动化赢得洪水预报竞赛
实践教程
赢得水文和汽车洪水预报竞赛
端到端项目演练

标题图片(作者图片)
大家好,一直在等待关于自动机器学习(AutoML)的新帖子!
今天我想写一篇关于我们 NSS 实验室团队和我如何使用 AutoML 工具赢得 2021 年黑客马拉松紧急数据黑客的帖子。比赛的任务是建立一个模型来预测未来七天河流水位的上升。我们使用来自气象站、仪表和卫星的数据,将这些数据源融合在一个复合模型中。复合管道由时间序列预测模型、多目标回归和物理模型组成。先说说我们是怎么建立起这么复杂的模型的。
这是什么黑客马拉松? Emergency data hack(俄语)是与俄罗斯紧急情况部合作举办的黑客马拉松,在为期两天的比赛中,几十个团队分析数据并建立模型以预防紧急情况。黑客马拉松于 2021 年 5 月 28 日至 30 日举行。
黑客马拉松是为了什么?首先,这是一个获得新知识和经验的好机会。其次,你可以在现实世界的任务中尝试你的发展。我们有相似的兴趣:在我们的 NSS 实验室开发的 FEDOT 开源 AutoML 框架被用于黑客马拉松。这让我们发现了它的优点和缺点。
如果你不知道 AutoML 是什么,你可以看看这张图(它几乎足以理解它):

几乎所有的 AutoML 框架(http://memegenerator.net/)
选择这个特别的黑客马拉松的一个重要因素是它的主题。除了开发学术 AutoML 框架之外,我们还准备科学论文并管理各种与某些东西建模相关的项目,这些东西会周期性地受到自然过程的损害和破坏。在绝大多数情况下,这种建模与地球科学有关:气象学、水文学、海洋学、地质学等。我们决定在这次黑客马拉松中尝试结合我们在开发算法解决地理问题和 AutoML 方面的经验。
任务描述
黑客马拉松有几条赛道,我们的是第二条。任务是:“建立一个预测模型,使我们能够提前 7 天估计春季期间勒拿河某些地区的每日最高水位增量”。
略谈现状 勒拿河偶尔会经历危险的洪水,给萨哈共和国(雅库特)的居民造成严重伤害。这种洪水在季节性洪水期间尤其危险(河流水情的一个阶段,每年在同一季节重复发生;河流含水量相对长期和显著的增加,导致其水位上升(即。维基百科)。为什么春天河水泛滥特别危险?很大程度上是因为冰塞,冰塞在河上形成,起到了一种大坝的作用,导致上游水位急剧上升。另一方面,融化的雪也起了作用,因为随着时间的推移,融化的雪进入了河道。所有这些导致了更高的水位。
为了清楚起见,您可以查看其中一个水文站的流量过程线(图 1):

图一。勒拿河水文测量站“Tabaga”的流量图(图片由作者提供)
黑线显示了从 1985 年到 2019 年不同年份给定站的排水量实现情况。蓝线显示平均值。可以看出,在放电的春天有爆发式的增长。
一个预测河流水位的系统将允许有效地分配资源,以防止洪水或在正确的时间疏散人口。
共有 10 个水文站,需要对其进行预报。为解决方案提供了异构数据:
- 计量站的水位,以时间序列的形式,具有每日离散性;
- 水文站记录的气象参数,以及关于河流事件的附加信息;
- 气象站的气象条件数据:气温和降水,到积雪高度。但这些数据指的是距离水文站有一段距离的点;
- 我们从遥感数据中获得的其他参数(如积雪特征)。
数据包含间隙,一些参数需要插值到相邻点。此外,气象参数是在不同的时间点记录的:在参数的特定每日时间、每日平均值和每月平均值有观测值。因此,在构建模型之前,需要对数据进行预处理。
基线
需要解决回归或时间序列预测任务,其中目标变量是水位在一天内上升了多少的值。
首先,让我们处理目标变量:“最高水位日增量”听起来太复杂了(至少对我们来说)。我们来简化一下!我们不会预测增量,而只是预测最高日水位值。增量计算将在提交前的最后一刻进行,这并不困难。
测量站的水位值最初可以使用单变量时间序列预测来“直接”预测。有这样一个工具是很好的: FEDOT 预测时间序列足够好。
虽然 AutoML 基于时间序列构建预测,但我们可以花时间提出新的方法。我们决定开发更先进的模型,并为这些“复杂”的模型准备数据作为下一步。

档案照片(右边的伊利亚)(【http://memegenerator.net/】T2)
我们决定使用多步方法:我们需要用几个独立的模块(模型)建立一个集合模型。第一个模型是时间序列预测模型。为什么是那个?因为我们已经建造了它,它已经给出了第一个预测。关于其实现的细节将在下面给出。我们将这个模型称为“模型 1”或“时间序列预测模型”。
“模型 2”。该模型应考虑天气条件以及河流上游或下游发生的事件。它可以建立在多元回归的基础上(当预测不是针对一个元素,而是针对几个元素(在本例中是针对七个元素)时,它是一种回归)。
“模型 3”是一个物理融雪径流模型(SRM)。它捕捉了融雪产生的水量。该模型描述了水从雪中转移到河流的一般过程。所以 SRM 被专家解读的很好。
数据预处理
第一个预处理步骤是将值插值到正确的节点。如上所述,并非所有的参数都是指我们有水文压力计的相同地理点。这个问题可以通过下图(图 2)来说明。

图二。以日平均气温为例,将气象参数值插值到正确的节点上。显示某个时间点记录值的“时间片”(图片由作者提供)
该图显示,在某个时间点,参数值对于所有点都是未知的。有必要应用插值过程。首先想到的是应用一些简单的插值算法。但同时,我们希望保持方法的灵活性:一些气象站虽然位于我们感兴趣的测量站附近(通过经度和纬度坐标),但可能与它的相关性很差。这可能是由于地形的不均匀性。例如,这些点彼此靠近,但是最近的气象站以及测量站位于山谷中,而相邻的两个气象站位于山谷中。那么最好的解决方案就是只从最近的气象站获取数值,而根本不进行插值。在另一种情况下,一个好的解决方案是对五个相邻气象站的值进行平均。双线性插值或最近邻插值不满足这些要求。
因此,决定实施一种方法,其中 K-最近邻(K-nn)机器学习模型将对这些值进行插值。该算法的原理可以在下面的动画中看到:

动画。以两个测量站为例,演示具有不同邻居数量的 K-最近邻居算法(动画由作者制作)
从动画中可以看出,我们可以为所需的气象站更改相邻气象站的数量。在这种情况下,气象参数新值的预测基于两个预测值:纬度和经度。分类指标也可以用这种方法进行“插值”,为此,有必要用分类器代替回归 K-nn 回归器。
简短的免责声明。我们知道上述方法对于气象参数的插值并不是最佳的。如果真的那么简单,那么我们就不需要气象模型、具有同化遥感数据的再分析网格以及用于计算气象特征的复杂算法。但是为了在有限的时间内解决这个问题,我们使用了这个算法。
第二步是填补空白。
观测数据往往有大量的空白。它们可以有不同的长度(在我们的数据集中,它们达到了一年!)并且可以对数据集中的单个变量和所有观察到的特征进行观察。因为即使有很大的缺口,也不希望丢失额外的参数,所以决定根据时间序列的前后部分来填补这些缺口。
填补不重要的空白(不到半年)的主要助手仍然是 FEDOT 框架。该框架在填补空白方面的主要功能依赖于时间序列预测。对于每个间隙,“间隙间隔”中的值是基于前期(间隙左侧的系列部分)和间隙后值(间隙右侧的系列部分)预测的。预测模型首先对史前史进行预测,然后对序列的反向后续部分进行预测。然后使用加权平均值将预测值组合起来。如果您想了解更多有关如何在 FEDOT 中预测时间序列的信息。以及如何填补时间序列中的空白,请查看之前的文章“ AutoML for time series:使用 FEDOT 框架的高级方法”。
采用了更复杂的方法来填补长时间的空白(超过半年)。由于大多数气象和海洋气象数据的过程是由季节决定的,因此用单一的预测来填补长时间的空白是不可行的方法,因为峰值(被遗漏的)明显丢失了。在这种情况下,我们使用下面的方法。由于数据集代表长期观察,因此可以分解时间序列并提取所需的季节性和趋势,即使考虑到差距,这也是一项没有太大损失的可解决的任务。

图 3。时间序列中的间隙填充(图为水温示例)。红框显示了原始时间序列中的最大差距(图片由作者提供)
库 statsmodels 用于时间序列的分解。因此,来自“分解”时间序列的季节性成分允许检索时间序列的峰值。然而,仅使用季节性因素来填补缺口并不能反映绝对值,而是代表波动的幅度。因此,为了正确填补这一空白,趋势部分的中值被加到季节部分,这使得有可能将获得的值与主要时间序列相关联。这种方法对于填补水流量和温度之间的差距特别有效,因为这些指标的波动非常明显,而且它们之间的差距非常显著,特别是考虑到这些参数是我们模型的主要预测因素。
附加数据
我们没有使用它…
所以,我们开始寻找开放的数据来源。几乎在任何与地理数据有关的活动中,第一个可能有用的东西是所需区域的高程矩阵。我们决定采用全球数字高程模型(DEM)——SRTM。基于此 DEM,我们建立了测量站的剖面图(图 4)。

图 4。数字高程模型及其生成的剖面图(图片由作者提供)
我们认为,如果我们知道流速和流量,这些数据可以用来计算河流水位。我们甚至在获得访问数据的权限之前就计划使用这种策略…但是我们没有获得流量。因此,我们对数据所能做的就是大致了解测量站河段的地形特征。理论上,我们也可以在将来的模型中使用这个浮雕,但是没有足够的时间去尝试。
模型 1:时间序列预测
已经有几篇关于用 AutoML 预测时间序列的帖子了:“ AutoML for time series:绝对是个好主意”和“ AutoML for time series:使用 FEDOT 框架的高级方法”。描述了为 FEDOT 使用的预测序列建立管道的基本原理。
然而,让我们简要描述一下时间序列预测模型是如何建立的。它首先将一维时间序列转换为滞后表(滞后表)。因此,表中的特征是时间序列在 t、t-1、t-2 等时刻的当前值和先前值。在这种情况下,目标变量变成时刻 t+1、t+2、t+3 等的值。经过这种转化,就可以在桌面上训练出合适的机器学习模型。使用堆叠技术,可以将几个模型组合成一个管道。这就是 FEDOT 所做的。
如果您查看三个验证块的最终预测示例,您可以看到该模型表现得相当好(图 5)。

图 5。使用时间序列预测模型进行洪水水位预测的示例。预测中使用的管道结构如下所示。(图片由作者提供)
模式二。多输出回归
该模型负责计算气象参数和河流上发生事件的数据。先说概念:计划在如下形式的表上求解多目标回归(图 6)。

图 6。用于求解多输出回归任务的生成表示例。基于在一定时间间隔内汇总的关于天气状况的信息,建立未来七天的预测模型。(图片由作者提供)
表中的目标变量是七个,在本例中是从 4 月 21 日到 4 月 27 日,这等于预测范围。同时,可以对不同时间间隔的属性进行汇总。例如,考虑日之前 10 天或 20 或 30 天的降水量。
从特征的描述中可以清楚地看出,它们是从初始气象参数中得到的。参数可以用不同的方式聚合:取和、平均值、振幅、最频繁值等等。
这将是令人兴奋的(我希望),仔细看看积雪高度的振幅的例子。让我们马上说,在雪高的情况下,这并不完全是“振幅”,其值是通过公式 max-min 计算的。对于雪的高度,我们使用所选间隔的第一个和最后一个值之间的差值:第一个-最后一个。考虑这样一个例子,我们合计 2020 年 4 月 20 日日的值,并取一个时间间隔进行合计,例如 10 天:在这种情况下,我们将从 4 月 10 日日记录的雪高值中减去 4 月 20 日日获得的值,因此我们将知道“在选定的时间间隔内融化了多少毫米的雪”,我们将对 11.04 和 21.04 等进行同样的操作。有一个选项可以只计算振幅,但我们是这样做的。这样做是为了区分降雪的时间和积雪高度增加的时间,以及积雪大量融化的时间。在振幅的情况下,这是不可能的,尽管我们在这个方法的第一个版本中计算了振幅。为了更好地理解这一点,让我们看一下图表,它同时显示了当天的最高水位和积雪的“幅度”(图 7)。

图 7。积雪高度的最大值(左边的蓝色曲线和刻度)和“振幅”(右边的橙色曲线和刻度)(图片由作者提供)
橙色曲线的峰值可以解释为“融雪最强烈的时期”。在这种情况下,橙色线的零下极端可以被称为“强烈积雪期”。我们感兴趣的正是第一个。因此,我们可以从图表中看到,当雪的“振幅”达到其峰值时,水平开始增长,并一直持续到“振幅”达到接近零的值,然后观察到最高的最大水平。
我们对其他气象参数进行了类似的变换。然而,有一个重要的预测因素值得仔细研究(甚至在图 6 中它也因此用红色突出显示)——事件重要性等级的总和。这里的事件是“河上发生的事情”。它们以短语“冰漂流”、“波浪”、“破冰”、“车站上方冰塞”等形式被记录下来。这些短语不能直接转移到模型中,这些短语的聚合过程也必须通过,以便我们可以在多输出回归模型中使用它们,但目前还不清楚如何进行。
我们所做的:我们将事件与等级相匹配。等级是我们对一个事件在洪水期间对水位上升有多大影响的专家评估。排名范围从 0 到 3,其中 0 不重要,3 非常重要。因此,生成了一个特殊的表,下面展示了其中的一部分。
表 1。一些事件和重要性为它们排序

现在我们用序数值来代替文字描述。所以现在我们可以说“站下堵塞”(3)大于“河流已经干涸”(0)。接下来,应该汇总数据,就像我们之前处理气象参数一样。对于军衔,决定计算某一时期的总数。如果等级的总和很大,我们可能会预期河水位会有明显的变化,反之亦然。
因此,特性已经生成,是时候决定将特性映射到目标的模型了…但是我们不会这样做。AutoML 将自己构建一个合适的模型,它将定义数据预处理和转换的方法,所以我们只需运行 FEDOT 并继续下一步。最终管道的结构示例如图 8 所示。

图 8。解决多目标回归问题时找到的管道示例(图片由作者提供)
模型预测的结果已经由两部分组成,如下图所示(图 9)。

图 9。从两个模型获得的预测示例(图片由作者提供)
模型 3。物理模型
决定为其中一个测量站建立一个物理模型。为此,我们使用现有的物理建模方法(即融雪径流模型——SRM ),并用纯 Python 实现了该算法。之后,我们将准备好的模型集成到我们的公共管道中。我们获得了一种混合建模:我们使用机器学习和物理模型来预测水位。由于黑客马拉松的时间有限,我们在一个晚上写了这个模型…
你可能会问为什么我们要这样做,为什么我们要在一个晚上写物理模型,为什么我们要用 Python?—别问了,我们只是玩玩。毕竟,这就是黑客马拉松的意义所在。
这就是它的工作原理。对于我们的方法,我们采用了相对简单的 SRM,因为它易于用代码实现和执行调整,并且我们有兴趣尝试使用确定性模型和机器学习方法的组合方法,而不管方法的复杂性。此外,通过调整模型,我们将意味着定义模型超参数,例如在计算中作为常数的系数和其他值。这种调整的需要是由于我们不能直接知道我们感兴趣的模型参数,我们必须在观察到的现象的基础上对它们做出一些假设。
所使用的模型旨在重现山区流域的日径流。该模型同时考虑了融雪和雨水的贡献。然而,该模型也可以适用于其他条件和其他数据。在我们的案例中,在计算河流流量时,我们将各种气象和融雪数据以及上游水位测量值插值到现场。在没有更好的替代方案的情况下,我们没有将流域上的降水层完全整合到我们模拟的河流量规中,而是被迫使用河流量规点的值。对于积雪数据,我们使用来自 MODIS 传感器的每日数据,并对整个流域的值进行平均。因此,对于时间 n 的流量,我们获得以下关系:

用于计算的主方程
这种模型应用的缺点是,其中包含的许多系数取决于各种气象条件,也与该地区的气候和盆地的地形特征有关。如果我们一次对所有可用数据进行模型调整(校准),许多参数将被“平均”估计,而它们与气象因素影响相关的可能变化将不会被考虑在内。为此,我们决定根据气象测量结果,通过对观测日进行聚类来确定特征气象条件。这种聚类的困难在于空间的高维度:八个测量值描述了不同的气象特征。虽然在高维空间中有聚类方法,但我们决定先降低所研究空间的维度,然后再进行聚类。在使用主成分分析(PCA)和输入阵列的相应变换后,通过 DBSCAN 进行分类。为每个集群分别执行后续操作以找到模型参数。
为了进一步搜索模型参数,我们对目标函数进行了优化,该目标函数通过测量站中观测到的排水量与通过之前引入的模型对选定数据进行的预测之间的差异模数来定义。

对系数进行优化,并使用差分进化方法。假设在处理新数据时,算法将首先确定所处理的一天属于哪种类型的气象条件,选择与这些条件对应的流量模型系数值,然后计算出水量。
最后一个任务(由于时间限制,在黑客马拉松中没有完全解决)是根据水流量确定河流水位。虽然大多数河流的特点是流量和水位之间的非线性关系,但冰冻的河流和其他冰现象具有更复杂的关系。例如,当一条河流打开时,可能会发生冰塞,阻止水向下游流动。在这种情况下,水位显著增加,但流速(定义为单位时间内流经横截面的水的体积)由于速度较低而变得不明显。图 10 显示了一个河流站的这种依赖性的例子。

图 10。测量河流站的水位和流量(图片由作者提供)
结论
结果是三个模型的集合,用于时间序列预测、聚合气象数据的多元回归和单个测量站的物理建模。准备好的模型胜过竞争者。所以,我们拿了第一名。
我们希望我们的经验能帮助那些阅读这篇文章的人在他们的黑客马拉松中成功地表现。或者,它可能对工程师和程序员有用,他们的任务是为他们需要的河流实现一个预测系统。
- 提供解决方案的存储库— emergency_datahack_nss
- 我们使用的 AutoML 框架是 FEDOT
- 关于我们实验室的信息页面— NSS 实验室
- 关于赢得黑客马拉松的新闻帖子— ITMO。新闻
- 我们的手稿基于提出的方法— 使用复合模型和自动机器学习进行短期河流洪水预报:勒拿河案例研究
NSS 实验室团队:
- 尤利娅·鲍里索娃
- 米哈伊尔·马斯利亚耶夫
- 格列布·马克西莫夫
- 伊利亚·雷文
- 米哈伊尔·萨拉法诺夫
愿望铲球偷看总是有效的 p 值
原文:https://towardsdatascience.com/wish-tackles-peeking-with-always-valid-p-values-8a0782ac9654
在不增加假阳性率的情况下自适应地查看和总结实验
在运行你的 A/B 测试之前,你是否估计了效应大小并计算了样本大小?当您结束 A/B 测试时,您是否只检查一次 p 值?当p-值为 5.1%(比阈值 5%高 0.1%)并且已经达到预先计算的样本量时,您是否会停止实验并得出产品特性没有改善业务的结论,而不是延长实验时间?如果你对任何一个问题的回答是否定的,那么你的实验的假阳性率(第一类错误率)很可能是夸大的。
当实验者在实验达到预先计算的样本量【1】之前,根据观察到的结果偷看并做出决定时,假阳性率被夸大。对于固定样本量测试(如 t-test),最佳实践是在启动实验之前估计所需的样本量,并在达到样本量后得出结论,但许多实践者在实践中并不遵循这一点。
要求实验者取得统计学学位并忠实地遵循程序是最好的解决方案吗?我们认为实验者有很强的理由偷看,偷看应该被允许。在本文中,我们介绍了我们对窥视问题的研究,以及我们如何在 Wish 实现始终有效 p 值来控制假阳性率,同时使实验者能够自适应地结束他们的实验。
偷看会使假阳性率增加四倍
我们通过运行 1000 个模拟的 A/A 测试来评估假阳性率,包括有无窥视。每个 A/A 测试都是使用模拟存储桶分配和真实指标数据(一个月的商品总值数据)构建的。在每个 A/A 测试中,我们使用韦尔奇的 t 检验和零假设 —两个实验桶之间没有显著差异。任何检测到的差异都是假阳性,因为在 A/A 测试中,两个实验桶来自同一个群体。
不进行窥视(即在所有 1,000 个 A/A 测试的相同样本量下得出结论),假阳性率约为 4.7%。相比之下,如果我们在第一次p-值低于阈值 5%时结束实验,假阳性率约为 21%。
甚至我们更加保守,将决策规则设置为当连续 n 天 p 值低于 5%时结束实验。假阳性率仍然很高。下表展示了对应于T5【n不同值的假阳性率。
当连续 n 天 p 值低于 5%的实验结束时的假阳性率
为什么偷看会提高假阳性率?
A/B 测试平台通常利用固定样本量测试(例如 t-test),其中所需的样本量是预先估计的。只有当实验者在达到预先计算的样本量后仅结束一次实验时,固定样本量测试才能最佳地权衡假阳性率和检测真阳性的概率(功效)。
当适应性地结束实验时,假阳性率显著增加,即,当实验变得具有统计显著性时结束实验。下图描绘了典型 A/A 测试的p-值随时间变化的轨迹。我们对为期一个月的 A/A 实验中的每一天都应用了 Welch 的 t 检验。在 A/A 实验的整个过程中,p-值多次低于红线(决策阈值p-值< 0.05)的情况并不罕见。此外,如果我们无限期地运行实验,并在p-值为 5%时立即结束实验,则假阳性率会达到 100%。也就是说,p-值总是在某个时候由于偶然的原因降到 5%以下。

为什么实验者会偷看?
如果偷看大大增加了假阳性率,为什么实验者不总是计算所需的样本量,只有在达到预先计算的样本量时才结束实验?
样本量计算困难且不准确
在进行实验之前,计算样本量是很困难的,至少是不准确的,因为这需要实验者估计效果的大小——新产品特性会产生多大的影响。在进行实验之前,实验者通常不太了解她的新产品特性的影响。即使实验者具有关于其新产品特征要移动的度量的广泛领域知识,并且可以从以前的实验中估计效果大小,如果她低估了效果大小,则计算的样本大小可能会大得多,因为期望的样本大小是效果大小的平方。例如,当估计的效应大小是真实效应大小的一半(或 10%)时,实验者会等待四(或 100)倍的时间。
实验者有充分的理由偷看
虽然偷看会增加假阳性率,但实验者有强烈的动机去偷看。因为 1)在科技公司进行的在线实验的数据源源不断,2)尽快完成实验对客户和公司都有好处。
在现代在线实验中,实验者实时观察数据,这使得实验能够自适应地调整样本容量(称为自适应样本容量测试)。商业伙伴经常问数据科学家要等多久才能让实验达到统计显著性,数据科学家可以等更长时间或更早停下来调整样本大小。这对于农业研究来说是不可能的,因为大多数实验设计理论是在 100 年前发展起来的。在农业实验中,即使实验者想偷看,她也别无选择,只能等到作物季节结束时收集数据。
实验者希望尽快结束实验,以改善或停止伤害用户体验。在固定样本量的测试中,实验者不应该在样本量达到之前向所有用户推出一个有益的特性,即使实验结果已经暗示了显著的改进。否则,固定样本量测试(如 t-检验)会导致高阳性率。反之亦然,如果实验者不想错误地扼杀一个产品特性,即使早期的实验结果显示出显著的退化,她也不能停止实验。
我们没有要求所有从业者(他们不一定受过严格的统计训练)遵循实验设计的最佳实践,这有时是违反直觉的,而是为从业者提供了总是有效的 p 值。这使得我们可以在实验结果显示出显著的正面或负面影响时,立即结束实验。
始终有效的 p 值方法的工作原理
我们在 Wish 的内部实验平台中实现了始终有效的p-值。始终有效的 p 值的一个巨大优势是,最终用户在实验 UI 中看不到任何差异,并且可以像解释固定样本量测试产生的 p 值一样解释始终有效的 p 值。此外,当数据科学家启动他们的实验时,他们不需要用头撞墙来计算所需的样本量。
如果你想知道这种方法如何工作的数学细节,请通读本节的其余部分。如果您只是对使用这种方法感兴趣,那么在不影响理解的情况下,您可以跳过本节的其余部分。
这里的一个关键信息是,即使当你偷看时,你的实验的假阳性率也得到很好的控制——一旦你看到 p 值低于阈值(例如,5%),就结束实验。
为了让 p 值无论实验者何时选择停止实验都始终有效,我们需要确保

也就是说,在零假设θ₀下,小于阈值 s 的 p 值的概率(范围从 0 到 1)在任何时间 t 都小于 s。这意味着即使在依赖于数据的时间查看 p 值,假阳性率仍低于预定义的阈值 s。例如,如果我们将阈值设置为 5%,只要始终有效的 p 值低于 5%,我们就可以结束实验,并且 fpp
始终有效的 p 值方法利用顺序测试,该方法应用于临床研究,数据随时间顺序到达。拉梅什·乔哈里等人。al 介绍了一种序贯测试——混合序贯概率比测试(mSPRT ),它适用于 A/B 测试,能够在不增加假阳性率的情况下进行序贯决策。本质上,始终有效的p-值计算为 1/λ,其中λ计算如下

这里,λ代表数据遵循零假设的可能性与数据遵循混合备选假设的可能性的比,h(θ)是θ在备选假设下的混合分布。直觉上,λ越大,数据越有可能来自零分布。
可以证明λ在零假设下是一个鞅,因此,

因此,1/λ遵循始终有效的 p 值的定义。
具体来说,估计如下

在哪里
- Y̅ₙ和 X̅ₙ分别是处理桶和对照桶的样本均值。
- 样本量为 n。
- Vₙ是对照桶的样本方差和处理桶的样本方差之和。
赵震宇等人。al 已经表明,混合分布 h(θ)中的τ可以估计如下:

其中 Z 为 Z 统计量,α为显著性水平,v_ctrl 和 v_trt 分别为对照和处理桶的样本方差。
总是有效的 p 值方法控制假阳性率
我们通过运行 A/A 测试来评估始终有效的 p 值方法的假阳性率。在每个 A/A 测试中,我们在每个数据点到达后计算始终有效的p-值,当始终有效的p-值低于 5%或实验达到预定的样本量时,我们结束实验,该样本量远大于任何典型实验所需的样本量。我们的 A/A 研究表明,始终有效的 p 值方法的假阳性率低于 5%。
总是有效的 p 值方法导致令人满意的功率
我们还研究了始终有效的 p- 值方法的真阳性率(功效),如下所示:
- 模拟两周的数据,其中处理桶的平均值比对照桶的平均值大 0.1%
- 在每天的数据到达后,使用 t 检验和永远有效的 p- 值方法计算 p 值。
- 如果 Welch 的 t 检验得出的p-值在 14 天中的任何一天小于 5%,或者最后一天的始终有效的p-值小于 5%,则我们声明检测到差异。
- 重复步骤 1 到步骤 4 10,000 次
- 将差值从 0.1%增加到更大的值,然后重复步骤 1 至步骤 4。
我们测试了不同的效应大小(范围从 0.1%到 1%),并比较了永远有效方法和 t 检验方法的功效。请注意,这不是一个公平的比较,因为 t 检验在顺序应用时有更高的假阳性率。我们绘制了两种方法在不同效应大小下的真实阳性率。下图表明,即使与假阳性率较高的 t 检验相比,始终有效的 p 值方法也能产生令人满意的功效。

永远有效的 p 值方法和 t 检验之间的功效比较
始终有效的 p 值的一个警告是,当桶高度不平衡时,它们往往不太可靠。实际上,在 Wish,当两个桶不太不平衡时(例如,处理与控制的比率不大于 5),我们应用该技术。
结论
尽管偷看大大增加了假阳性率,但从业者有充分的理由偷看。为了使他们能够在控制假阳性率的同时尽快结束实验,我们实现了始终有效的 p 值,这使得实验人员能够实时调整实验长度(样本大小)。我们的模拟研究表明,总是有效的 p 值方法控制了假阳性率,并且具有令人满意的真阳性率。
感谢
感谢 Eric Jia 和对本项目的贡献。我也非常感谢刘派、Pavel Kochetkov 和 Lance Deng 对这篇文章的反馈。
References
[1] Johari, Ramesh, et al. “Peeking at a/b tests: Why it matters, and what to do about it.” *Proceedings of the 23rd ACM SIGKDD International Conference on Knowledge Discovery and Data Mining*. 2017.[2] Zhao, Zhenyu, Mandie Liu, and Anirban Deb. “Safely and quickly deploying new features with a staged rollout framework using sequential test and adaptive experimental design.” *2018 3rd International Conference on Computational Intelligence and Applications (ICCIA)*. IEEE, 2018.
单词嵌入符合康定斯基的颜色理论
原文:https://towardsdatascience.com/word-embeddings-align-with-kandinskys-theory-of-color-26288b864834

罗斯·索科洛夫斯基在 Unsplash 上的照片
艺术与科学交汇处的快速实验
介绍
在本文中,我描述了一个快速实验,旨在通过使用单词嵌入表示和余弦相似性来测试瓦西里·康丁斯基的形式和颜色理论,量化两个看似正交的集合(基本形状和原色)之间的关联。然而,尽管使用余弦相似度来测量单词之间的关联在自然语言处理文献中被广泛接受,并且尽管该实验的结果是积极的(即,结果与基础理论一致),但本文既不旨在确认该理论,也不旨在作为强调数据科学中良好实验实践的教程,而是呼吁质疑余弦相似度在定性上下文中的价值,如讨论中更详细描述的。
形状和颜色
大约一个夏天,在研究包豪斯的时候(我想尝试用形式而不是文字来思考),我偶然发现了盖蒂研究所的康定斯基形式和色彩练习;成为各种练习、测验、谜题等的欣赏者。这可以在网上找到,因为我对这个练习特别提出的问题非常感兴趣(例如,“颜色是什么形状?”)尽管如此,我还是决定试一试。
这个练习简单地要求参与者将{圆形、三角形、正方形}与{红色、蓝色、黄色}一一对应,在对这个任务进行了一些思考之后,我决定首先三角形是红色的(它们是最尖的,红色有它的尖锐性),然后圆形是黄色的(可能太受儿童书籍中太阳的黄色圆形的影响),最后正方形必须是蓝色的(方便地说,是地平线上的海洋);兴奋地,我进入下一个屏幕,看看我是否正确(愿意假设一个人对提出的问题可能是正确的)。

提交的模型(来源:作者)
我的模型不正确。根据康定斯基的理论,三角形是黄色的(黄色是我误认为红色的鲜明颜色),圆形是蓝色的(将它们的圆形与蓝色的深度和平静对齐),正方形是红色的(“有力”和“自信”,反映了平面的方形固有的“张力”)。(读完之后,我完全同意这些解释,并认为它们是正确的——它们是很好的解释——但我确实喜欢我自己的解释,即使它们分别过度延伸,依赖于第二代表,依赖于事后推理。)

康定斯基的模型(来源:作者)
检验康定斯基的理论
为了测试他的理论,康定斯基向他的学生展示了一份调查(类似于上面的练习),要求他们将形状与颜色搭配起来;我不知道他在教学中是否让他的模型出错了,但是大多数的回答确实与康定斯基的模型一致。
与康定斯基的学生相比,他们在完成调查之前可能已经受到了他的模型的影响(毕竟他们是从他那里学到的),我看不出有什么真正的理由来假设模型本身已经影响了大量随机的英语使用者样本(例如可以对其文本 word 2 vec embeddeds[3]进行训练的样本),以至于英语语言本身中的这些形式和颜色集合由于模型而特别对齐(即,标准英语中的单词用法似乎与模型无关)。
所以我用预训练的 word2vec 嵌入做了一个实验,看看在成对的形状和颜色之间的余弦相似性中是否有任何值得注意的模式,结果如下所示;很好地,它们也符合康定斯基的模型(!?),其中“三角形”最接近“黄色”,“圆形”最接近“蓝色”,“方形”最接近“红色”。

用单词嵌入测试康定斯基的模型(来源:作者)
讨论
虽然我不一定会称康定斯基的模型从我的小实验中得到证实(圆形的形状与蓝色产生“内心共鸣”意味着什么?康定斯基的作品对我们的联想以及我们的语言产生了多大的影响?),结果——即使它们只是幸运的相关性——作为一种量化概念信息的方法,与单词嵌入一起考虑是很有趣的[1,2,4]。
作为另一个例子,当颜色集合{红色、蓝色、黄色}的等级对应于康定斯基的形状集合模型{圆形、三角形、正方形}时,通过将颜色列表扩展到{粉色、深红色、红色、栗色、棕色、玫瑰色、鲑鱼色、珊瑚色、巧克力色、橙色、象牙色、金色、黄色、橄榄色、黄绿色、石灰、绿色、海蓝宝石色、青绿色、天蓝色、水绿色、青色、茶色、褐色、米色、蓝色、藏青色、烟色、石板色、紫色、紫色、梅子色、靛蓝色、淡紫色、品红色、白色、白色 其与原始颜色集的顶部颜色具有最低的相似性,实际上更像“深红”或“珊瑚色”而不是“红色”(与“红色”的 0.140 相比,相似性分别为 0.153 和 0.171),并且它也更像“绿色”和“橙色”(0.171 和 0.155)。 (不过有趣的是,‘黄色’和‘蓝色’仍然是‘三角形’和‘圆形’的统治色。)
因此,虽然我可能从这个实验中得到了一些乐趣,虽然我对结果与理论相符感到惊讶,但我不会确切地说我已经证实了理论,因为围绕余弦相似性在这种情况下的意义仍然有一些问题值得问(例如,关联和非关联之间的界限在哪里?,一个赢者通吃的联想策略就够了吗?)以及这些相关性出现在语言中的机制(例如,三角形和黄色之间的联系是否比它们的锐度更多?圆是蓝色的,是因为它平滑得像平静,还是因为它的曲线像从上面看去的一个湖?方块是沉睡的无色思想的绿色,还是愤怒的最狂暴的红色或深红色的血液?)
结论
尽管我的实验范围很窄,而且它基于一个(也许很奇怪的)问题(例如,“颜色是什么形状?”),提出了上面列出的问题,我相信我对这里给出的结果感到的惊讶可以说是反映了我看到的对诸如 ChatGPT 等语言模型表达的惊讶;因此,推而广之,我相信,我在考虑实验结果时所持的怀疑态度,应该在更普遍地考虑语言模型的输出时得到反映。
我们的语言(~我们的概念系统)显然是结构良好的,足以让接受过训练的模型以令人印象深刻的方式理解它的模式(特别是当模型所做的很难理解的时候);然而,当评估这些模型时(当决定我们是否对它们的能力印象深刻或是否信任它们的输出时),我认为我们可能高估了我们说话的意图的重要性,相对于我们说话的模式,在我们理解我们自己是说话的生物时;换句话说,当人类带着意图(某种生物和学习的复合体)并根据语言模式说话时,模型只根据语言模式生成文本(我看不出有什么理由去假设意图);这种能力似乎足以产生连贯的文本(至少对于短文本来说),但它也提出了为什么一个文本是在另一个之上产生的问题。
回到这篇文章中介绍的实验,也许康定斯基的理论是正确的,因为它捕捉到了一些真实存在的关于形式和颜色的东西,也许真实存在的东西在我们的语言中被捕捉到(也许通过理论的陈述),但没有机制是已知的(没有梳理数据或剖析嵌入来追溯为什么结果与理论一致的一些原因),除了我们已经拥有的,由一些数字装饰的东西,我们还有什么?
参考
- Garg、Nikhil、Londa Schiebinger、Dan Jurafsky 和 James Zou。2018."单词嵌入量化了 100 年来的性别和种族刻板印象."美国国家科学院院刊 115 (16)。https://doi.org/10.1073/pnas.1720347115。
- 科兹洛斯基,奥斯汀 c,马特塔迪,詹姆斯埃文斯。2019."文化的几何学:透过词汇嵌入分析阶级的意义."美国社会学评论84(5):905–49。https://doi.org/10.1177/0003122419877135。
- 米科洛夫、托马斯、程凯、格雷戈·科拉多和杰弗里·迪恩。2013.“向量空间中单词表示的有效估计”,一月。http://arxiv.org/abs/1301.3781。
- 斯托尔茨,达斯汀 s .和马歇尔 a .泰勒。2021."单词嵌入的文化制图."诗学88(10 月):101567。https://doi.org/10.1016/j.poetic.2021.101567
印地语和 Python 的 hindiwsd 库的词义消歧

格伦·卡丽在 Unsplash 拍摄的照片
随着迄今为止在自然语言处理(NLP)和语言学领域取得的所有进步,你会认为智能系统能够找出在特定上下文中使用的单词的正确含义。然而,情况并非如此,至少在像印地语和混合代码的 Hinglish 这样的低资源语言中不是这样,在这些语言中还有许多工作要做。这显然为词义消歧的研究和其他面向应用的工作打开了许多机会。因此,我和我的合著者 Mirza Yusuf 决定构建一个 Python 库,作为那些对研究印地语和混合代码的 Hinglish 的词义消歧感兴趣的人的一个有用的起点。这篇文章简要介绍了我们的思维过程、工作和图书馆。
词义消歧
简单来说,词义消歧是指识别一个词在一定语境中使用的正确含义。这里有一个例子:
"我用球棒击球。"
一个非常简单直白的句子而且我相信我们大多数人都知道这里的“bat”指的是板球、棒球等运动中使用的木棍状物体。现在,“蝙蝠”的另一个意思是倒挂在树上的会飞的哺乳动物,但是我们知道你不能用它来击球(或者我希望如此)。
这就是我们的问题陈述的本质。我们进行了研究,以设计出最佳的方法,教我们的系统根据单词使用的上下文来消除它们的歧义。虽然像英语这样的高资源语言已经做了大量的工作,但是低资源语言还有很长的路要走。
由于能够很好地读、写和理解印地语,我和我的合著者决定解决印地语以及混合代码的 Hinglish 文本的这个问题。
我们的方法
我们最初花了很多时间寻找相关的数据集和预先存在的 Python 库,以及大量的文献调查。在这个过程中,我们遇到了 WordNet,这是一个由单词之间的语义关系组成的词汇数据库,并最终出现了 IndoWordNet,这是一个由主要的印度语言组成的 WordNet 版本。
我们必须做出的一个重要决定是,为了成功地解决这个问题,要看使用哪种学习方法/模式。现在,我们通常着眼于涉及神经网络架构的监督或非监督方法,如 LSTMs、GRUs、BERT 等。然而,我们发现现有的监督和非监督方法很难使用,数据集格式也不是非常理想的形式。这促使我们探索基于字典的方法,在实验中,我们还发现这种方法比有监督或无监督的方法更快,并且很好地利用了 WordNet 的特性。
Lesk 算法
Lesk 算法是一个相当简单但有效的算法,它利用了 WordNets 的结构和特性来执行词义消歧。
本质上,它接受要消除歧义的单词以及该单词使用的上下文,然后使用 WordNet 找出该单词的意思。
伪代码是这样的:
基于上下文(歧义词周围的词)和在 IndoWordNet 中找到的与歧义词相关的词,计算重叠。对于 IndoWordNet 中存在的单词的每个意思计算这种重叠,并且具有最高重叠分数的意思被认为是正确的意思。
这里有一个例子可以让你更好地理解:

使用 Canva 制作的图像
然而,我们注意到这个算法有一个主要问题——它只提供了一个非常一般化的解决方案。由于用于各种不同含义的支持单词(上下文)的共同性质,Lesk 算法经常不能识别给定句子中单词的正确含义。
自定义 Lesk 算法
这促使我们探索监督和非监督的方法,却发现这一领域的数据集特别缺乏。因此,我们缩小了问题陈述的范围。我们决定为 20 个最常用的歧义印地语单词增强词义消歧。这使得我们也可以用这 20 个单词的各种含义来建立一个特殊的数据集,并使用它来增强我们的 Lesk 算法。
我们继续修改 Lesk 算法,使自定义数据集中出现的单词在确定单词的含义时也发挥作用。
我们使用了一个叫做“交集的附加参数来增强算法。这是我们的算法版本的伪代码,尽管只做了很小的改变,我们观察到结果有很大的不同!
结果
通过一个小的调整和一个更集中的方法,我们能够大大提高我们基于字典的 Lesk 算法的准确性。
我们能够将所选的 20 个单词以及每个单词最常用的两个意思的准确率提高一倍。更重要的是,我们能够创建一个框架,可以在未来用于研究目的,或者简单地作为一个项目的方便工具!

词义消歧的准确度比较
虽然这可能不是最稳健的解决方案,而且对于整个语言来说肯定需要时间,但我们希望我们在这里的工作将鼓励未来对数据集进行更多的研究和管理,从而可以直接使用监督和非监督方法,并提供快速预测。
更多信息可以查看我们的 Python 包 这里或者干脆
pip 安装 hindiwsd
欢迎在我们的 Github 库上与我们交流,我们非常乐意帮助您!
以下是我们发表的论文的链接,以防有人需要:http://www . lrec-conf . org/proceedings/lrec 2022/workshop/wild re 6/pdf/2022 . wild re 6-1.4 . pdf
带时间序列的 Word2Vec:一种迁移学习方法
原文:https://towardsdatascience.com/word2vec-with-time-series-a-transfer-learning-approach-58017e7a019d
学习时间序列的有意义的嵌入表示

向量表示是机器学习生态系统中的一个关键概念。无论我们面临什么样的任务,我们总是试图赋予我们所掌握的数据以意义。我们习惯于采用技术来提供数据的数字表示,以发现隐藏的行为并产生有价值的见解。
随着深度学习的兴起,以较少的假设和较少的努力获得有意义的数据表示正在成为现实。我们来想想 TF-IDF(词频-逆文档频)。这是自然语言处理中采用的一种技术,用于将一组原始文本文档转换成一个数字矩阵。TF-IDF 在很长一段时间内占据了主导地位,代表了一种编码文本序列的好方法。深度学习的出现拯救了新技术,首先是 Word2Vec,然后是 transformer 编码。它们是现成的解决方案,并且在提供文本数据的数字数据表示方面非常有效,而不需要(在大多数情况下)理解上下文。这使得它们比老式的 TF-IDF 更受欢迎。
在 NLP 领域采用深度学习嵌入表示是革命性的。通常将术语“嵌入表示”与涉及文本数据的应用联系在一起。这是因为很容易概括文本内容中单词的位置依赖性。如果我们认为还存在其他表现出位置依赖性的数据源,为什么不在其他领域推广文本嵌入技术呢?
一个引人入胜的想法可能是将 NLP 中获得的成果转化到时间序列领域。乍一看,这可能是一个完美的拟合,因为时间序列数据也是由位置/时间关系来表征的。主要的好处应该在于提供学习技术,这些技术可以产生遵循潜在的时间依赖性的数据的有价值的向量表示。这不是我们第一次对时间序列数据的嵌入感兴趣。我们发现了 Time2Vec 作为时间的模型不可知表示的效用,可以在任何深度学习预测应用中使用。我们还提出了 Corr2Vec 通过研究多个时间序列的相互相关性来提取它们的嵌入表示。
在本帖中,我们尝试在时序域中应用 Word2Vec。我们的目标是利用无监督方法的灵活性,如 Word2Vec,来学习时间序列的有意义的嵌入。生成的嵌入应该能够捕获底层系统行为,以便在其他上下文中也可以重用。
数据
我们从 evergreen UCI 知识库中收集了一些开源数据(参见这里的UCI 许可政策的)。停车伯明翰数据集包含 2016/10/04 至 2016/12/19 期间每小时 8:00-16:30 的停车占用率。它非常适合我们的目的,因为它记录了来自不同位置的数据,使我们能够在多变量的情况下进行切换。
我们掌握了原始占用率(即当时停车场有多少辆车)和最大停车容量。

所有停车区域的占用时间序列(图片由作者提供)
数据显示了缺失观测值的存在,但也显示了一些有规律的季节性模式。我们观察每天和每周的行为。所有的停车区往往在下午达到最大占用率。其中一些人在工作日使用最多,而其他人在周末更忙。

所有停车区域的占用率小时模式(图片由作者提供)

所有停车区的占用率每日模式(图片由作者提供)
建模
如何将 Word2Vec 应用于时序数据?当将 Word2Vec 应用于文本时,我们应该首先用一个整数映射每个单词。这些数字代表单词在整个文本语料库中的唯一标识符。它们需要允许唯一的可训练嵌入的关联。对于时间序列,我们也应该这样做。整数标识符由宁滨连续时间序列生成为区间。在每个时间间隔,我们关联一个唯一的标识符,它指的是一个可学习的嵌入。

时间序列离散化示例(图片由作者提供)
在将时间序列离散化之前,我们应该考虑对它们进行缩放。在多元环境中工作时,这一点尤为重要。我们需要以统一的方式应用离散化来实现单义整数映射。考虑到我们的停车数据,我们使用占用率系列(在 0-100 范围内标准化)来避免误导学习行为。
Word2Vec 架构与 NLP 应用程序中的架构保持一致。有不同的现成解决方案。我们选择手工 Tensorflow 实现:
input_target = Input((1,))
input_context = Input((1,))
embedding = Embedding(n_bins+1, 32)
dot = Dot(axes=1, normalize=True)([
Flatten()(embedding(input_target)),
Flatten()(embedding(input_context))
])
model = Model([input_target, input_context], dot)
model.compile(
optimizer=Adam(learning_rate=1e-5),
loss=BinaryCrossentropy(from_logits=True)
)
训练数据和相关标签由跳格法生成。它根据用户定义的窗口大小生成整数对。同一窗口中的整数对的标签等于 1。随机生成的配对被标记为 0。
通过检查学习到的嵌入,我们了解到网络可以自动识别我们数据的循环性质。

每个装箱的时间序列序列的 2D 嵌入可视化(图片由作者提供)
扩展所有时间序列的嵌入表示,我们注意到每小时和每天的观察之间的明显分离。

每个时间序列中所有观测值的 2D 嵌入可视化(图片由作者提供)
这些可视化证明了我们方法的优点。假设条件低,需要设置的参数少,就可以生成有意义的时间序列嵌入。
摘要
在这篇文章中,我们提出了著名的 Word2Vec 算法的推广,用于学习有价值的向量表示。没有太多的努力,我们采用了在时序上下文中应用 Word2Vec 所需的所有考虑因素。最后,我们展示了这种技术在一个非标准的 NLP 应用程序中的有效性。整个过程可以很容易地集成在任何地方,并很容易地用于迁移学习任务。
引文
- Dua d .和 Graff c .(2019 年)。 UCI 机器学习知识库。加州欧文:加州大学信息与计算机科学学院。
- 丹尼尔·h·斯托尔菲、恩里克·阿尔巴和姚。智能城市停车场占用率预测。in:2017 年 6 月 14 日至 16 日在西班牙马拉加举行的 Smart-CT 2017 第二届国际会议。
- 伯明翰市议会。
保持联系: Linkedin
一种频率分析方法
原文:https://towardsdatascience.com/wordle-a-frequency-analysis-approach-9989c3d7be5f
单词的频率分布及其字符位置可能会揭示更多信息,以帮助优化对正确答案的搜索。

截图来自https://wordlegame.org/—作者
像你们大多数人一样,我的社交媒体最近充满了奇怪的绿色、黑色和黄色方块,上面有 Wordle 分数。起初我不知道它是什么,但它的持续泛滥让我足够好奇,想知道炒作是怎么回事。
在了解了这个游戏之后,这是我们小时候玩的游戏策划的一个简单的转变。我习惯于通过编写游戏的懒惰解决方案来失去朋友(你可能记得我已经破坏了一些数独游戏),我决定分析是否有一种基于统计测量的有效猜测方法。
在这个游戏中,搜索空间由长度为 5 个字符的英语单词组成。你可能已经知道,有些网站已经深入挖掘了 Wordle 源代码,找到了使用过的单词列表。对于我的方法,我使用 NLTK 工具包将它保存在一个通用英语单词列表中。这种方法的唯一缺点是一些高可能性的单词可能会被拒绝,您必须从推荐的单词列表中选择。
最初的天真方法似乎很简单。将单词列表分解成字符,并对出现的单词进行简单的频率分布分析。

作者图片
看上面的图表,出现频率最高的 5 个字母是“a”,“e”,“s”,“o”,“r”。人们很快就会想到“崛起”这个词。
但是这种天真的方法忽略了字符的位置,而是只汇总了整个语料库的计数。如果我们根据 5 个可能的位置来计算频率呢?从下表中可以看出,分布与预期的不同。

作者图片
例如,最常见的 5 个字符的单词以“s”开头,大约有 11.4%的分布,而“a”是最常见的第二个字符,在所有 5 个字符的单词中有 18%的分布。
因此,我们可以尝试的另一种方法是:
- 计算每个位置的分布
- 选择出现频率最高的位置和字符(例如,在上面的分布中,我们看到最后一个位置的“s”得分最高,为 19.8%)
- 在其中一个字符已被锁定的情况下,将单词列表过滤为后验单词
- 对剩余的 4 个位置和选择重复频率分布

作者图片
使用这种方法,上述分析表明,从单词 _ 字母列表开始的最佳单词是单词‘bares’。如果它是使用从源代码中收集的 Wordle 单词列表加载的,它将会是单词“spice”。
您可能已经注意到,在最初的几次猜测中,我也试图使用包含 5 个独特字符的单词。这增加了我们更快缩小搜索空间的机会,同时也避免了一些错误的实现,其中重复的字符被不一致地标记(我还没有广泛地验证这一点)。
在每个单词作为一个猜测出现后,通过基于以下内容的过滤过程运行单词列表,简单地重复上述方法:
- 已知处于正确位置的字符
- 不在单词中的字符
- 字符出现在错误的位置(必须出现,但不在尝试的位置)
然后重复频率分布过程,直到解出谜题。
结论
在我们的日常任务中,我们经常可以通过蛮力方法来解决问题,如果存在限制我们穷尽整个搜索空间的能力的约束,这并不总是最高效的,甚至是最有效的。在这个例子中,我们被限制在由近 10k 个单词组成的搜索空间中进行 6 次尝试。
这种分析表明我们可能如何使用数据和逻辑,在这种情况下,字母的频率分布比随机猜测做得更好。通过利用这样的分布,我们能够通过一些 Python 和统计数据显著缩小搜索空间。

截图自https://wordlegame.org/—作者
我希望它激发了一些使用数据和逻辑解决一些日常问题的想法(或者在这种情况下是有趣的,抱歉)。
C ode 可以在这里找到——虽然它不是生产级的,只是为了好玩而工作的东西。
使用 Python 3 构建一个 Wordle 解算器
原文:https://towardsdatascience.com/wordle-solver-using-python-3-3c3bccd3b4fb
用 Python 3 开发字母位置概率求解单词

Wordle Guess Block |作者图片
大家好,我相信现在你们都已经看到了 Wordle,Twitters 的最新困扰。
如果你和我一样,你在这个游戏中挣扎。但是不要担心,在搜索和数据分析的帮助下,我们可以变得很棒,你可以把你自己的获奖截图发布到你的 Twitter 上。
什么是沃尔多
首先,让我解释一下什么是 Wordle。Wordle 是一个简单的游戏,你有六次机会猜出一个五个字母的单词。
每次预测之后,游戏都会给你提示。
绿色瓷砖表示您预测了字母的正确位置。
黄色的瓦片意味着字母在单词中,但是你的预测有错误的位置。
最后,灰色的瓷砖意味着这个字母不在单词中。每天游戏的单词都会被重置,让每个人都有机会猜对单词。
构建控制台游戏
有了对 Wordle 的新认识,让我们用 python 3 为我们的主机构建一个可玩的游戏版本。我们将创建一个包含游戏板和规则的自定义类。我们班有四个函数。
第一个功能是检查游戏是否结束。如果玩家成功猜中了暗号,游戏就结束了。如果玩家使用了所有六种猜测,游戏也完成了。
类的第二个功能决定了游戏的结果。该函数返回一个元组。第一个元素是代表游戏结果的二进制值。如果玩家赢了,元组的第二个元素被设置为游戏获胜的猜测。如果玩家输了,元组的第二个元素被设置为 99。
该类的第三个函数用玩家最近的猜测更新游戏面板。
第四个也是最后一个函数决定玩家的猜测是否有效。此功能有助于验证用户输入,以确保游戏正常运行。
构建游戏助手
现在让我们制作实际的 Wordle 解算器。我们的单词库从 15917 个单词开始,给我们猜对单词的初始概率只有 0.0063%。这个小百分比意味着如果我们想要赢得比赛,缩小搜索空间是至关重要的。
为了帮助增加赢得游戏的概率,我们首先要做的是利用游戏规则来缩小我们的搜索空间。根据我们猜测返回的颜色,我们可以从我们的单词库中过滤出灰色/黑色字母,因为我们知道它们不在秘密单词中。
我们可以应用于单词库的另一个过滤器是选择包含黄色字母的单词,因为我们知道所有黄色字母都在秘密单词中。我们还可以过滤掉在猜测位置带有黄色字母的所有单词,因为我们知道该字母不会在那个位置。
我们将执行的最后一个利用是在预测位置过滤所有绿色字母,因为我们知道这是在单词中。
既然我们知道如何缩小搜索空间,我们需要确定如何猜测最有影响力的词。为此,我们将使用每个字母定位概率的总和对每个单词进行评分。

单词评分公式|作者图片
- S =单词得分
- w =当前单词
- W =单词库
- n =单词库中的单词量
- p =当前字母在 word 中的位置
通过使用字母定位概率,我们尽最大努力找到绿色和黄色字母,这样我们可以缩小我们的单词库,增加我们猜测正确单词的概率。
如果一个元音字母不在我们知道的字母中,我们将在单词评分等式中添加一个偏差,以鼓励找到一个元音字母。我们这样做是为了利用这样一个事实,即我们知道每个单词都有一个元音。我们添加到单词得分的偏差是单词中唯一元音的百分比。

元音偏差|作者图片
- b =元音偏向
- w =当前单词
- V =元音库
最后,我们返回单词得分的argmax,因为我们知道这是最有影响力的单词。
包裹
现在你知道了,我们已经成功地创建了我们的 Wordle solver,这给了我们赢得游戏的高概率。
你可以在我的 GitHub 这里查看完整版本的代码。
感谢阅读。
参考
WORDLE-VISION:简单分析提升你的 WORDLE 游戏
原文:https://towardsdatascience.com/wordle-vision-simple-analytics-to-up-your-wordle-game-65daf4f1aa6f

斯文·布兰德斯马在 Unsplash 上的照片
我爱文字游戏。近十年来,我有一种不太健康的痴迷,那就是与朋友一起玩单词,每天同时与玩家玩多达十几个游戏,事实上,这些玩家不是我的朋友,而是随机的对手。然而,近年来这个曾经流行的移动应用程序已经失宠,留下了我和一群挥之不去的怪人,他们会通过应用程序内的聊天功能与我进行奇怪的对话(见图 1,这是许多例子中的一个)。因此,我对这个应用程序的兴趣减弱了,最终消失了。

图 1:与一个陌生人(众多陌生人中的一个)的应用程序内对话的单词片段。我的幽默尝试总是不受重视。
然后我听说了一个风靡全国的叫做的游戏。不需要下载,也没有账户登录,没有广告,没有来自 randos 的主动信息。我很好奇。对于那些不熟悉单词的人来说,这个游戏的目的是在 6 次尝试中猜出 5 个字母的英语单词。这个游戏告诉你是否猜对了一个字母,也告诉你是否猜对了它在单词中的位置。
玩了一个星期后,我开始想:最佳的第一个猜测是什么?凭你的第一次猜测,你什么也没找到,对吗?嗯,这不完全正确。我们有一些先验知识。例如,我们知道正确的单词必须是 5 个字母长的英语单词。它可能需要一个或多个元音,或者至少一个 Y 。此外,你可能听说过 E 是英语中使用最多的字母(尽管这并不一定适用于所有由 5 个字母组成的英语单词)。我内心的科学家渴望找到一种方法,利用公开可用的单词数据来决定最佳单词猜测。
与你可能在网上读到的相反,并不总是需要用最先进的机器学习模型来解决数据问题。您可能不需要花费数天时间来担心调整超参数、交叉验证或在您的神经网络中包含多少隐藏层。有时候一杯咖啡、几十行代码和几个条形图可以让你在短时间内走得很远。
快速收集一些见解的第一步是找到一个包含英语中所有单词的词典。幸运的是,这被证明是相当容易做到的(因为互联网)。我在 dwyl 的回购中发现了 GitHub 上的一个文本文件,包含 37 万字。出于 Wordle 的目的,我们只关心 5 个字母的单词,这样我们就有将近 16,000 个单词。接下来我想知道的是在 5 个字母的单词中最常见的字母是什么。为此,我需要计算每个字母的频率,结果如图 2 所示。

图 2:5 个字母单词中字母的频率分布。10.5%的字母是 A, 9.8%的字母是 E ,以此类推(图片由作者提供)。
我有点惊讶地发现,在 5 个字母的单词中,字母是最常见的,占所有字母的 10.5%。字母 E 紧随其后,为 9.8%,其次是 S 为 8.2%。不出所料,所有元音都排在前 10 位,而字母 V 、 Z 、 J 、 X 和 Q 出现频率最低。查看数据的另一种有趣方式是进行累计求和,以获得图 2 的累计频率版本。下面的图 3 显示了这个分布的样子。

图 3:在 5 个字母的单词中发现的字母的累积频率分布。我们可以看到,前 7 个字母占了所有字母的一半以上(图片由作者提供)。
关于累积分布的巧妙之处在于,我们可以清楚地看到,前 7 个字母( A 、 E 、 S 、 O 、 R 、 I 、 L )占了 5 个字母单词中所有字母的一半以上(53%)。似乎合乎逻辑的是,最佳的第一个单词猜测应该是只包含这 7 个字母的单词。原来,这本词典中有 231 个单词只有使用字母 A 、 E 、 S 、 O 、 R 、 I 、 L 。因此,我们现在将 16,000 个 5 个字母的单词缩减为 231 个,减少了 98.5%。
但是这 231 个单词中的一些包括 SALSA ,它(虽然美味)只由 3 个不同的字母组成,因此削弱了我们在第一次尝试中消除字母的能力。因此,首先猜测的更好的单词是只使用前 7 个字母并且每个字母只使用一次。强加这个要求留给我们的只有 60 个单词,这是一个更合理的数量,我们可以快速排序。
关于这整个分析的一个重要的警告是,我使用 5 个字母的英语单词的完整列表作为输入。事实上,可能的单词列表是大约 2500 个最常见单词的精简集合。访问这个 2500 个单词的列表将极大地改善这里的结果。作为替代,我可以手动筛选我们的 60 个单词列表,删除更深奥的单词。剩下的单词组成了我精选的 21 个最佳单词列表(按字母顺序排列):
过道
沉香
崛起
崛起
伯爵
赖尔
激光
说谎者
说谎者
洛里斯
失败者
涂油者
口述
铁轨
升起
雷亚尔
滚转
滚转
角色
滑行者
太阳能
让我们在几个真实的单词游戏中尝试一下这个列表中的一些单词作为第一猜测。在图 4 中,我展示了 1 月 22 日(左)和 1 月 23 日(右)的 Wordle 结果。


图 4:左边——我的单词(2022 年 1 月 22 日的第 217 号),我使用了第一个单词过道,来自我策划的 21 个最佳单词猜测列表。右—我的单词(2022 年 1 月 23 日的第 218 号),我尝试了我列表中的另一个单词, RAISE ,作为第一猜测(作者的单词的截图)。
1 月 22 日猜过道帮我试了 3 次才解决了这个问题!在最初的猜测之后,我使用上面图 2 中的频率来帮助指导我随后的猜测。比如我在闪避之前猜到肉馅是因为 M 比 W 出现的频率更高。令人惊讶的是,我猜中的每一个正确的字母都在正确的位置上,这说明运气在这个游戏中的作用不可低估。1 月 23 日猜加注也让我试了 3 次才解出谜题。我再次参考了我所学的关于频率的知识来帮助我选择下一个要猜的字母。
现在有了一些数据驱动的见解,前进吧!
我为这个分析编写的代码以及上面的条形图可以在my word le-VISION GitHub repo中找到。
[1]Lexico.com。"字母表中哪些字母最常用?"2021.https://www.lexico.com/explore/which-letters-are-used-most(2022 年 1 月 23 日访问)
[2]维克多,丹尼尔。"《沃尔多》是一个爱情故事。"纽约时报,2022 年 1 月 3 日https://www . nytimes . com/2022/01/03/technology/wordle-word-game-creator . html
单词机器人
使用谱聚类和强化学习在 Wordle 中取胜
Wordle 是一款简单的文字游戏,由 Josh Wardle 开发。在纽约时报上读到这个消息后,我试了一下,立刻就被吸引住了。我的妻子和我已经形成了一种竞争,每天都在比较我们的猜测计数,我认为制作一个机器人加入竞争会很有趣。在这篇文章中,我将回顾 Wordle 的规则,并通过 1000 多个模拟游戏介绍我的机器人的设计和性能。使用的主要 ML/AI 技术有谱聚类和 Q 学习。
TLDR;
- 我用机器学习建立了一个机器人,在硬模式下玩 Wordle。
- 在 1000 多个模拟游戏中,机器人平均猜了 3.767 次才找到正确的单词。
- 该机器人在 1000 场模拟游戏中输掉了 8 场,总体胜率为 99.2%。
单词入门/规则复习
对于外行人来说,Wordle 是一个类似于 Hangman 的猜谜游戏。每天,你有多达 6 次的机会来猜测一个隐藏的单词。对于每个猜测的每个字母,您都会收到反馈(绿色表示字母在隐藏单词中且位置正确,黄色表示字母在单词中但位置错误,灰色表示字母不在隐藏单词中)。
如果你在硬模式下玩游戏,你需要使用来自每个猜测的反馈来通知你随后的猜测(防止你纯粹为了获得信息而提交猜测,并减少可能的单词池)。我的机器人在硬模式下玩。
相关著作
快速浏览博客圈发现,我不是唯一一个对使用数据科学解决 Wordle 挑战感兴趣的人。
- Matt Rickard 描述了一种贪婪的方法来确定 Wordle 的“最佳”开始猜测,这是基于尽可能多地排除答案选项(他选择了“SOARE”,我甚至不知道这是一个单词)。
- Mark Scherschel II 提出使用成对距离来衡量单词中心性,选择“TARES”作为最佳起始单词选择,因为它与其他单词相对接近。
我想在我的机器人设计中融入并扩展这些想法,以构建一个自主代理,更少地关注最佳首词,而更多地关注管理最佳下一个猜测的政策,以从一个游戏状态导航到下一个游戏状态。理想情况下,这个代理会从它的经验中学习,并随着它玩越来越多的游戏而提高它的性能。
源数据
虽然英语中有超过 12,000 个五个字母的单词,但其中许多都是古老的或深奥的。Wordle 的创建者将答案库限制在约 2300 个日常语言中的常用词。我从 Wordle 源代码手动复制了 2300 字的库来训练我的 bot。
单词相似度图
可行单词的集合被组织在无向图数据结构中。每个“节点”或“顶点”都是一个单词,单词之间的每个“边”或“链接”都通过一个简单的相似性得分进行加权。得分由两个单词之间的每个普通字母加 1 分组成,如果普通字母也在单词中的相同位置,则加 1 分。
为较小答案库中的所有单词计算成对相似性得分,并存储在对称邻接矩阵中。
谱聚类
谱聚类用于将图划分成 k 个“社区”(相似节点的组)。我使用的 k 值为 5。在这篇文章中,我不会深入研究这个算法,但是如果你没有遇到过这个算法,你可以把它看作是相似图的 K-Means。对于那些有兴趣进一步阅读的人来说,看看这个维基百科帖子(很好的起点)。
q 学习
q 学习是一种强化学习的形式,它寻求学习一种基于经验的政策π(以状态、行动和结果奖励之间的转换的形式)。Q 学习中的一个挑战是定义一个离散的状态空间,使得代理可以通过实验获得足够的经验来学习给定情况下的“最佳”行动,以最大化期望的回报。另一个挑战是建立一个有效平衡短期和长期回报的奖励系统(例如,训练一个机器人吃一块糖比为上大学存钱更容易)。
在本练习中,我通过离散化 k 个社区中的节点分布来定义状态。当 k=5 时,这导致了 126 种可能的状态。
动作被定义为机器人应该从哪个社区选择下一个单词猜测。
奖励被定义为以下三个部分的总和:缩减系数、正确系数和获胜奖金。
收缩因子被定义为猜测前的可行单词数与猜测后的可行单词数的比率。猜测排除的单词越多,收缩系数越大。
正确系数定义为每个绿色字母 2 分(正确位置的正确字母),每个黄色字母 1 分(正确字母,错误位置),每个灰色字母 0 分(字母不在正确单词中)。
这个机器人每猜对一次就能获得 1000 分的高额奖金。
总体战略
对于每个游戏,一个年级的班级从 2315 个常见的 5 个字母的单词库中随机选择一个单词。然后,机器人使用迭代方法进行猜测。在每次迭代中,机器人执行以下操作:
- 对剩余的可行单词集使用谱聚类来划分图
- 离散化词在聚类中的分布
- 使用 Q 学习器选择一个聚类,从中得出下一个猜测
- 选择猜测作为所选聚类的“质心”(该聚类中具有最高总相似性得分的单词)
- 将猜测传递给 Grader 类,该类评估猜测并以每个字母的分数向量的形式返回反馈(2 代表绿色,1 代表黄色,0 代表灰色)
- 基于来自分类器的反馈过滤图形以排除不可行的单词
- 计算奖励,并在下一次迭代中传递给 Q 学习者
当机器人猜出正确的单词时,它会终止迭代。
根据这个策略,我让机器人玩 1000 场游戏,并记录每场游戏的猜测次数。
表演
在 1000 场游戏中,机器人平均猜对次数为 3.767 次。它“输”了 8 次(需要猜 6 次以上来识别正确的单词),总胜率为 99.2%。下面的图表显示了该机器人在 1,000 场游戏中的表现:

WordleBot guess 统计了超过 1000 个模拟游戏。
结论
我对我的机器人的总体表现感到惊喜。我自己的单词平均水平徘徊在 3 到 4 之间,所以这个机器人绝对可以和人类抗衡!
也就是说,还有很大的改进空间。计算和存储相似性得分是我的机器人当前设计中计算效率最低的部分,因为它需要为 2315 个单词计算 5359225 个成对的相似性得分。由于该图是对称的,从技术上讲,大约 500 万次相似性计算可以减半为大约 250 万次。因为分数永远不会改变,所以它们实际上只需要计算一次(而我当前的设计出于权宜之计,不必要地重新计算分数)。整个模拟在 Google Colab 环境中运行大约需要 45 分钟,我相信通过一些重构,这个运行时间可以大大减少。
此外,还有许多可以调整的参数。例如,在这个练习中,我任意地将社区的数量设置为 5,并取得了对我来说足够好的结果,但是可能还有其他一些最佳的社区数量。
最后,我怀疑机器人的 Q 学习部分可能是矫枉过正。随着机器人获得经验,我没有观察到我预期的每场比赛猜测次数的减少,这表明没有太多的“收敛”发生。对于一个有趣的短项目来说,我真的很满意它的结果。
这篇文章的扩展版本包括代码可以在我的 GitHub 页面的 Jupyter 笔记本中找到。你怎么接近这样的机器人?请在评论中告诉我!
单词转化成向量
原文:https://towardsdatascience.com/words-into-vectors-a7ba23acaf3d
单词嵌入入门
单词嵌入的概念

照片由阿玛多·洛雷罗、穆库尔·瓦德瓦、塔曼娜·茹米在 Unsplash 上拍摄
本文是 2ⁿᵈ系列中的一篇关于单词嵌入的入门文章:** 1。word 2 vec 背后有什么 | 2。单词成向量 |
3。统计学习理论 | 4。word 2 vec 分类器 |
5。word 2 vec 超参数 | 6。单词嵌入的特征**
第一篇文章,Word 2 vec,介绍了系列文章,为 word 嵌入做好铺垫。在本文中,我们将回顾导致现代单词嵌入的基本 NLP 概念。
数据驱动的自然语言处理简介
虽然从最早的人类文字开始,人们就已经开始研究语言学,从最早的计算机开始,自然语言处理(NLP)就已经发展起来,但直到 20 世纪 90 年代,NLP 中的“统计革命”才形成(Johnson,2009)。这个时代标志着 NLP 的重点从符号或基于规则的算法(如语法或句法)转向数据驱动的技术,该技术将更强大的计算机上的机器学习与互联网上的大型语料库相结合。
根据 Nivre (2002)的说法,统计以三种主要方式参与 NLP:
- ****应用:应用于随机模型的算法是确定性的
- ****采集:模型使用来自经验数据的推断
- ****评估:包括描述性统计、估计和假设检验
约翰逊(2009)讨论的“统计革命”主要是关于采集方法。
接下来,我们将回顾导致机器学习技术应用于 NLP 的统计获取方法。
在书面文件中,单词掌握着主要意思。虽然标点、大写、符号、拼写、布局、格式、字体/手写和插图也可以传达意义,甚至可以应用于矢量或集成到单词矢量中,但大部分价值在于单词及其排序。
基于频率的矩阵
在计算机语言处理中使用向量来表示单词的想法至少可以追溯到 1975 年,当时 Salton 等人(1975)发表了引入向量空间模型概念的基础性工作。最简单的向量空间模型是 one-hot 向量,其中在 1× v 向量的一个单元中使用数字 1 来显示词汇表 v 中唯一单词的存在,所有其他单元都为 0。

****一个热词向量示例(图片由作者提供)
我们将介绍以下三个额外的例子,说明如何在计算机语言处理中使用向量来表示单词(NSS,2017 年):
- 字数
- 术语频率-逆文档频率(TF-IDF)
- 共现
使用这些向量空间模型的语言建模技术被称为基于计数的方法,因为它们使用全局单词出现值的总和(Almeida and Xexéo,2019;Levy 等人,2015 年)。
字数
当收集分布式单词表示的单词数据时,可以从对一系列文档中的单词进行简单计数开始。每个单词在每个文档中出现的次数总和是一个计数向量。例如:

字数示例
(图片由作者提供,灵感来自 Potts 和 MacCartney,2019)
计数向量是单词在文档中表示时的基本描述性统计,维度为 v × d ,其中 v 是词汇表中的单词数, d 是文档数。这个矩阵的每一行可以被认为是一个长度为 d 的字向量,它是一个稀疏矩阵。
术语频率-逆文档频率(TF-IDF)
对于文档来说,一个更重要的描述性统计数据是单词在文档中的相对频率,即一个单词在文档中出现的频率相对于它在整个语料库中出现的频率。例如,在确定某个单词在文档中对于基于文本的搜索引擎结果排名的重要性时,该值非常有用。
计算相对频率值的典型方法称为项频率-逆文档频率(TF-IDF) ,并根据各种公式定义为乘以 TF×IDF ,但以下两个公式最常用(Voita,2020):

其中, t 为术语或单词, d 为文档, n 为 t 在 d 中出现的次数, N 为 t 在所有文档中出现的次数, D 为文档总数,| {D∈D
对于 TF 和 IDF 统计,这些公式还有其他变化。 IDF 公式的一个值得注意的方面是在各种变化中一致使用 log,这在信息论中有理论依据(Robertson,2004),其细节是在 TF - IDF 在互联网上广泛用于搜索后正式开发的。
这里的一个要点是,TF-IDF 值在很大程度上非常有用,因为它是原始统计数据的重新加权。
TF-IDF 字向量的维数与计数向量的维数相同: v × d 。矩阵的每一行都可以作为长度为 d 的字向量。
共现
现在,让我们看一个同现 矩阵的例子。下图是一个表格,显示了两个单词在语料库的每个文档中出现的频率。这个表也可以被认为是一个大小为 v × v 的矩阵,它与文档的数量无关。因为矩阵量化了词的接近度,根据语言学中的分布假设(Firth,1957),值指向词的意义。

同现矩阵示例
(图片由作者提供,灵感来自 Potts 和 MacCartney,2019)
这个矩阵的每一行都可以作为长度为 v 的词向量,其中 v 为词汇量。该矩阵是单词邻近度的模型,并且是比之前示出的单词计数矩阵更密集的矩阵。
但是共现矩阵不需要仅用于每个文档的单词。人们可以查看段落、句子或一定数量的单词窗口中的成对单词。原来设置一个相关的窗口大小是相当重要的。我们将在下一节“实验设计考虑”中讨论窗口大小。
尽管如此,因为大多数值是重复的,所以同现矩阵并不像理想的那样密集,所以可以应用降维技术而不会丢失重要的信息,正如我们将在下面题为“降维”的部分中看到的。
有许多方法可以形成字数统计向量,这取决于所建模的内容。对于语境,再多几个计数向量分别是:单词×语篇语境、音系片段×特征值、单词×句法语境(Potts 和 MacCartney,2019)。
重新加权
我们之前讨论过 TF-IDF,其中的 idf 为单词计数值增加了一个权重。TF-IDF 值是重新加权的一个例子,可以用来将信息集中到一个字向量中。
向量空间模型中的重新加权通常涉及调整单词的频率,正如我们在 TF-IDF 中看到的那样。在自然语言中,原始单词计数在很大程度上受到词频倾向于遵循 Zipf 定律 的事实的影响,也就是说,当绘制 log( rank )与 log( frequency )时,它几乎是线性的。

维基百科语料库
词频中的齐夫定律(Sergio Jimenez viaWikipedia, CC BY-SA 4.0 )
重新加权的一个挑战是确保不常用词的信息被充分表示,而不引入来自离群数据放大的异常。基于计数的向量的重新加权公式的示例包括(Potts 和 MacCartney,2019 年):
- ****归一化:向量值的欧几里德或 L norming (L2 范数)
- 概率**:将向量值表示为概率P(d|t),总和为 1**
- 观察/预期 : O / E 以及相关的χ或G-测试统计**
- TF-IDF :参见上一节,注意 TF-IDF 的变体考虑了单词的经验分布
- PMI :逐点互信息
- PPMI :正逐点互信息
上述重新加权公式适用于字数统计向量,其中 t 是单词,而 d 是文档。
使用哪种重新加权公式取决于应用。在自然语言理解中,Potts 将 PMI 称为“故事的英雄”(Potts 和 MacCartney,2019),因为它倾向于揭示自然语言处理中的见解。PMI 的公式是:

其中 P ( t , d )是单词 t 和文档 d 一起出现的概率, P ( t )是单词出现的概率, P ( d )是文档出现的概率。PMI 量化了两个事件发生的概率之间的差异,如果事件是独立的,则给出它们的联合分布和它们的单独分布。由于当个体概率变得非常小时,PMI 很难定义,因此更经常使用 PPMI(Jurafsky 和 Martin,2019)。PPMI 用 0 代替负的 PMI 值。
实验设计考虑
窗口缩放是一个与数据收集方式有关的重新加权概念。在共现矩阵示例中,为每个文档测量并记录单词的共现。如果共现的范围被改变为部分或段落而不是整个文档,则得到的矩阵是不同的。事实上,上下文窗口通常被测量为一定数量的单词,例如 10。当单词出现在单独的句子中或包含标点符号时,是否被计算在内是其他考虑因素。
计算单词以创建单词向量的一个重要方面是用于预处理文档的标准。例如:
- 标点符号应该包含还是删除?
- 是否应该去掉大写以使单词被认为是相同的,即使一些单词的大写会改变它们的意思?
- 是否应该运行一个命名实体识别脚本来将名称分类到预定义的类别中,比如人名、企业名称和街道名称?
- 是否应该标记词类,以便区分单词的动词和名词形式,如“run ”?
- 是否应该修改动词,使动词的所有时态都相同(例如,词条化或词干化)?(动词的预处理通常通过去除英语中动词的‘ed’结尾来完成。)
- 拼写错误和生僻字应该怎么处理?
- 像“the”和“a”这样非常常见的词应该删除吗?
- 表情符号应该被包括在内吗?🤔
当决定是否以及如何预处理文档以准备单词嵌入训练时,需要回答这些类型的问题。
像单词的开始和结束这样简单的事情也不总是很清楚。在英语中,空格通常分隔单词,但连字符有助于创建复合词。另一方面,在德语中,复合词是不发音的,而且会变得很长,例如在拼出数字 123,456 时:
einhundertdreiundzwanzig tau sendvierhundertsechsundfünfzig
此外,甚至不考虑“单词”作为向量的基本度量单位,而是考虑字母组合(字符 n-grams )来寻找意义模式,这值得吗?
对如何预处理文档做出适当的决定是实验设计的一个重要方面,这取决于所构建的应用程序和语言模型。
距离测量
一旦从数据中设计和创建了单词向量,应该如何测量它们的相似性呢?也就是说,如果一个语言模型预测了一个具有特定向量估计的单词,那么在给定其在数据集中的向量的情况下,应该如何发现最近的单词呢?
欧几里德距离是矢量的几何标准。它的方程式是:

其中 a 和 b 是两个大小为 n 的向量。然而,在记录词的频率的向量空间模型中,更频繁的词往往具有更大的量级,这往往阻碍了词的比较(Jurafsky 和 Martin,2019)。几何上,当向量方向是唯一的度量时,可以使用余弦相似度,这是用于测量单词嵌入相似度的最常见度量(Jurafsky 和 Martin,2019;Turney 和 Pantel,2010 年)。余弦相似性的公式为:

在这里,点符号表示向量上的点积(粗体),双垂直线表示向量的 L 范数,它在分母中用于归一化幅度。值的范围从 1 表示完全相反到 1 表示完全相同。
下图显示了在确定最近邻时,欧几里德距离和余弦相似性如何相互矛盾。

****欧几里德距离和余弦相似度(图片由作者提供)
注意,如果余弦相似度通过减去平均值而被归一化,则它成为皮尔逊相关系数。像余弦相似性一样,皮尔逊相关系数的范围从 1 到 1,但它是对线性关系量的度量,不受位置和比例的影响。
要将余弦相似性转换为“距离”度量,其中更远的距离由更大的数字表示,可以考虑使用:

但是请注意,这个度量不是一个“适当的”距离度量。根据 Potts 和 MacCartney 的说法,真实距离必须是对称的,对于相同的向量必须是 0,并且必须满足三角形不等式(Potts 和 MacCartney,2019)。因此,余弦相似度应转换为角距离,范围从 0 到 1:

在文献中用于词向量的额外距离度量(无论合适与否)有(Potts 和 MacCartney,2019):
- 与交集相关的基于匹配的方法,包括:匹配、Jaccard、骰子和重叠
- 与 Fisher 信息相关的 Kullback-Leibler (KL)散度方法,用于推导概率,包括:KL 散度、对称 KL 和 Jensen-Shannon 散度,以及具有偏斜的 KL
选择哪种距离度量取决于应用需要强调什么。在统计 NLP 中,测量结果的试错多于统计纯度。
降维
可视化通过将多维数据减少到两个或三个维度来直观地显示主导关系,从而帮助我们理解向量关系。在统计学中,人们经常使用主成分分析(PCA),其中向量被线性映射到数据方差最大化的二维平面中。
t-分布式随机邻居嵌入( t -SNE)是一种用于可视化的机器学习算法,由范德马滕和辛顿于 2008 年发表,其目标是 " 很好地捕捉高维数据的大部分局部结构,同时也揭示全局结构,例如在几个尺度上存在的聚类”(范德马滕和辛顿,2008)。使用 t -SNE 来可视化一组单词关系或者甚至获得一大组单词关系的高级视图是很常见的。
PCA 和 t -SNE 是众所周知的用于在二维或三维中可视化向量的降维技术,但是它们也可以用于将单词向量的维数减少到任何更低的维数。
向量空间模型的其他降维技术包括(Potts 和 MacCartney,2019 年):
- 奇异值分解
- LSA(也称为 LSI),它使用截断 SVD
- 非负矩阵分解(NMF)
- 概率 LSA (PLSA)
- 潜在狄利克雷分配
- 使用神经网络的自动编码器
降维技术导致了基于机器学习预测的单词嵌入,最著名的早期方法是 Word2vec,我们将在统计学习理论介绍之后的未来文章中详细讨论它。
摘要
在本文中,我们学习了如何使用向量来量化文本中的单词邻近关系,如何测量这些关系,以及如何使用降维和机器学习技术来最大化数据的可用性。
在本系列的下一篇文章 统计学习理论 中,我们将回顾一下理解一个浅层神经网络分类器的数学理论,比如 Word2vec。
这篇文章是 2ⁿᵈ系列文章中关于单词嵌入的初级读本:** 1。word 2 vec 背后有什么 | 2。字成矢|
3。统计学习理论 | 4。word 2 vec 分类器 |
5。word 2 vec 超参数 | 6。单词嵌入的特征**
****关于这个主题的更多信息:我推荐学习更多关于单词嵌入基础的资源是斯坦福大学的这个在线计算机科学课程:Potts,c .和 MacCartney,B. (2019)。 CS224U 自然语言理解 。
参考
阿尔梅达,f .和 Xexéo,G. (2019)。词汇嵌入:一个综述。可从 arXiv:1901.09069v1 获得。
弗斯,J. R. (1957)。语言学理论概要,1930-1955。在弗斯(编辑),语言分析研究,语言学会特刊,第 1-32 页。英国牛津:巴兹尔·布莱克威尔出版社。
约翰逊博士(2009 年)。统计革命如何改变(计算)语言学。计算语言学协会 2009 年欧洲分会关于语言学和计算语言学之间的相互作用研讨会的会议录:良性、恶性还是空洞?,第 3–11 页。 PDF 。
jurafsky d .和 Martin j .(2019 年)。语音和语言处理:自然语言处理、计算语言学和语音识别的介绍。徒弟堂,第三版,2019 稿。
Levy、y . Goldberg 和 I . Dagan(2015 年)。利用从单词嵌入中获得的经验改进分布相似性。《计算语言学协会汇刊》,3:211–225。可在 doi 10.1162/tacl_a_00134 处获得。
Nivre,J. (2002 年)。自然语言处理中的统计方法。在布本科 j .和旺格勒。促进它。第二次促进瑞典新大学和大学学院 IT 研究的会议,第 684–694 页。斯克夫德大学。
NSS (2017 年)。[对单词嵌入的直观理解:从计数向量到 Word2Vec](https://www.analyticsvidhya.com/blog/2017/06/ word-embeddings-count-word2veec/) 。分析 Vidhya。
Potts c .和 MacCartney b .(2019)。 CS224U 自然语言理解 ,在线计算机科学课程。加州斯坦福:斯坦福大学。
罗伯逊(2004 年)。理解逆文献频率:关于 IDF 的理论争论。文献杂志,60(5):503–520。可在 doi10.1108/00220410410560582处获得。
黄和杨(1975)。自动标引的向量空间模型。ACM 的通信,18(11):613–620。可在 doi10.1145/361219.361220处获得。
特尼博士和潘特尔博士(2010 年)。从频率到意义:语义学的向量空间模型。人工智能研究杂志,37:141–188。 PDF 。
范德马滕和辛顿(2008 年)。使用 t-SNE 可视化数据。机器学习研究杂志,9:2579–2605。 PDF 。
沃伊塔湖(2020)。自然语言处理课程:单词嵌入。 Github 。
*除非另有说明,数字和图像均由作者提供。
使用 Nb_Search 加快工作速度
原文:https://towardsdatascience.com/work-faster-with-nb-search-fb381b1c88d8
概述 nb_search,一个用于查找和组织 Jupyter 笔记本的 Python 包

文件在哪里?(图片来自 iStock,授权给 Dennis Loevlie)
H 你是否曾经需要找一本旧的 Jupyter 笔记本,却忘记把它放在哪里了?你还记得你用过的包装或者你在减价时用过的一些术语吗? Nb_search 提供了一种简单高效的方法来寻找丢失的笔记本电脑以及更多!
目录
- 装置
- 搜索功能
- 帮助加速和组织工作流程的额外实用程序
- 使用示范
- 我们连线吧!
装置
nb_search 在 PyPi 上,所以你可以简单地用 pip 安装它。
pip install nb_search
您也可以从下面的 GitHub 页面手动安装它:
https://github.com/loevlie/nb_search
功能
搜索 _ 文件
nb_search.search_files 递归搜索指定目录(或默认为当前目录)中的所有 Jupyter 笔记本文件。然后,它打印出所有找到的笔记本的可点击链接,并返回路径列表。使用 search_files,的一个例子,其中路径保存到名为 files 的变量中,如下所示:

除了目录路径,你还可以使用文件路径列表,就像上面我们保存到变量 files 中的那些。这允许您使用不同的过滤标准进一步细化搜索。这可以使用下面几节中的任何 nb_search 函数来完成。
搜索 _ 笔记本
nb _ search . search _ notebook建立在 nb_search.search_files 之上,允许你根据代码或降价单元格中的特定模式过滤结果。它将搜索您在第一个参数中提供的字符串的匹配项。在下面的例子中,我使用 search_notebook 返回目录下的所有笔记本。/PATH "我用 numpy 的地方。

另一个选项是搜索降价单元格。您可以通过更改第二个参数来实现,如下所示:

搜索 _ 标题
nb _ search . search _ heading允许您在笔记本的标题(1 至 6)中进行特定搜索。例如,我正在搜索标题中带有“title”一词的笔记本,如下所示:

帮助加速和组织工作流程的额外实用程序
标题 _pprint
nb _ search . headings _ pprint是查看笔记本的快捷方式(前提是笔记本在减价单元格中使用了适当的标题)。唯一的参数是笔记本的路径。下面是我之前做的一个使用 headings_pprint 查看 nb_search 辅导笔记本标题的例子:

nb_search 中的许多函数在耦合时甚至更有帮助。例如,在找到笔记本列表后,您可以很容易地使用 headings_pprint 来进一步检查每个笔记本,如下所示:


search_todo
TODO 标记可以帮助您组织、计划甚至协作项目。通过使用以下语法,您可以在 Jupyter 笔记本的任何代码单元格中放置 TODO 标记,无论是否有到期日/描述:
不要担心错误消息。如果它困扰着你,那么你可以通过在笔记本顶部添加以下代码来摆脱它:
现在, nb_search.search_todo 为您提供了在目录中搜索任何添加了 todo 标签的笔记本的能力。然后,它会在可选描述和截止日期(转换为截止日期前的天数或延迟天数)下方显示每个笔记本的可点击链接。这方面的一个例子如下所示:

我个人发现 TODO 标签在与团队合作时特别有用。TODO 标记提供了一种有组织的方式来将工作分配给团队的不同成员。该描述可用于详细说明需要做什么以及谁应该专注于该特定任务。你也可以设定一个暂定的截止日期,让事情走上正轨。
使用示范

(图片由作者提供)
我们连线吧!
我希望这对于使用 nb_search 来组织和使用 Jupyter 笔记本是有帮助的!如果您对 nb_search 有任何问题或建议,请随时联系我。
使用 UiPath 的 Alteryx
原文:https://towardsdatascience.com/work-with-alteryx-from-uipath-82c92d4bbf42
如何从 UiPath 对 Alteryx 工作流进行 API 调用

托拜厄斯·卡尔松在 Unsplash 上的照片
UiPath 的应用之一是机器人过程自动化( RPA )。基本上,您可以构建机器人来自动执行由您的分析师执行的日常重复性任务,例如通过从不同来源获取数据并转换它们来每月发布一份报告,或者从不同的部分提取数据并检查它们,或者填写数据输入所需的数千种表格。这将使数据分析师能够专注于更高效的任务,并提高工作效率。
Alteryx 有各种应用程序,如数据提取、加载和转换,以及高级应用程序,如具有机器学习模型创建和预测的分析。相互补充,UiPath 和 Alteryx 宣布了一个合作关系,其中一个连接器在 UiPath 中宣布,另一个连接器在 Alteryx 中宣布,这样你可以使用 API 调用从 UiPath 运行 Alteryx 工作流,或者你可以从 Alteryx 调用 UiPath 工作流。
在本文中,我将解释如何从 UiPath 运行 Alteryx 工作流。
将 Alteryx 工作流保存到图库:
这种集成工作的一个要求是,您的 Alteryx 工作流必须保存到 Alteryx 服务器/私有图库中。这可能是您所在组织的图库,也可能是您托管 Alteryx 工作流的私人网站(针对个人)。为此,请单击文件→另存为→私人画廊

保存到私人画廊。图片作者。

输入您私人网站的 URL。图片作者。
第一次这样做时,系统会提示您输入网站的 URL,以便将 Alteryx 连接到您的图库。或者,您可以连接到 Alteryx 公共画廊。您可以在 Alteryx 社区图库最佳实践中找到更多相关信息。
在您的 Alteryx 图库中,当您点击您的工作流程时,您会注意到 URL 对于您的每个工作流程都有一个唯一的作业 ID。这个作业 ID 很重要,因为它是传递给 UiPath 的参数,以便从 UiPath 调用 Alteryx 工作流。它是下面显示的 URL 中“/”之后的最后一个键。
https://my _ private _ website . com/gallery/app/my _ job _ name/622 a28 ebae5a 222114428966
你需要从你的 Alteryx 私人图库中得到的另一个信息是你的 API 密匙和秘密密匙(比如密码)。如果您不是管理员,请联系您组织的 Alteryx 管理员,确认您的帐户已启用“API”访问。请注意,即使您的私有配置文件显示启用了 API 访问,您的管理员也必须在他们的终端(在订阅→用户→ API 访问下)启用它,这样 UiPath + Alteryx 集成才能工作。

您将在 Alteryx 服务器配置文件中找到您的 API 密钥和秘密。作者图片
从 UiPath 连接到 Alteryx:
在 UiPath 内部,第一步是添加 Alteryx 包,该包已经由 UiPath 作为官方包发布。

在 UiPath 中安装 Alteryx 包。作者图片
将 Alteryx 应用程序范围添加到您的序列中,然后单击“配置”。

在 UiPath 中配置 Alteryx 作用域。作者图片
单击“测试”连接以确认您的连接成功。如果这里出现错误,请检查您的 API 密钥、秘密 ID 和基本 URL。注意,你的基础 URL 必须 以“ /gallery/api ”结尾。请与 Alteryx 管理员确认您的 API 访问是从“订阅→用户→ API 访问”启用的。
确认连接后,下一步是输入您的作业 ID、优先级(0,1,2,其中 0 是默认值)和变量,如 Answers In、wait time 和 Job Out(声明一个变量来存储输出)。

在 UiPath 中配置您的作业。作者图片
就是这样!使用这种方法,当运行 UiPath 工作流时,您将能够在后台运行 Alteryx 工作流。UiPath 将自动连接到您的 Alteryx 服务器,进行所需的 API 调用来运行工作流。这样做的主要优点是,您的 UiPath 工作流现在可以从中央 orchestrator 运行,而无需在客户端计算机上安装 Alteryx,并且需要在后台运行的 UiPath 进程可以继续这样做,而无需为了运行 Alteryx 工作流而接管对键盘/鼠标的控制。
如何使用 Python GeoPy 和 Plotly 创建交互式地图
地理编码,反向地理编码,计算表面距离,行驶距离,用 Python 创建散点图、气泡图、Choropleth 图和动画图

作者图片
地理空间数据科学正在蓬勃发展。典型的地理空间数据集包括纬度、经度、地址、街道名称、邮政编码等数据。这些数据需要一套不同的工具来处理,并将其转化为见解。在本文中,我将分享对处理地理空间数据和创建地理空间可视化有用的 Python 库:GeoPy和 Plotly 。
一.处理地理空间数据
GeoPy 是一个 Python 库,提供对几种流行的地理编码 web 服务的访问,如 Google Maps、Bing Maps、nomist 和 Yahoo BOSS。它是帮助我们处理地理空间数据的有用工具。
安装库
打开 anaconda 提示符或命令提示符,并键入以下命令。
pip install geopy
导入库
import geopy
初始化地理编码服务 API
正如我上面提到的,有许多地理编码 API 可用。可以使用免费 API,比如, OpenStreetMap 或者付费 API,比如, Google Maps。这完全取决于你将如何部署你的程序。你会得到你所付出的。一个免费的 API 可能很适合你自己喜欢的项目。如果您将部署用于商业目的,您应该考虑付费 API,因为您可以扩展每日 API 配额限制。
为了使用谷歌地图 API,我们需要一个谷歌地图 API。你可以按照这个快速教程为免费生成自己的谷歌地图 API 密匙。
# **OpenStreetMap API**
from geopy.geocoders import Nominatim
geolocator = Nominatim(user_agent="[Y](mailto:aaron_clover@hotmail.com)our Email")# **Google Maps API**
from geopy.geocoders import GoogleV3
geolocator = GoogleV3(api_key='Your Google Maps API Key')
从原始地址中提取纬度和经度
为了创建地图,我们需要获得每个位置的纬度和经度。通常,数据集会包含地址信息,如街道号、街道名称、城市、州、邮政编码。要获得纬度和经度,我们可以使用 GeoPy 的“ geocode ”函数。
address = '5801 SW Regional Airport Blvd Bentonville AR'
location = geolocator.geocode(address)
print(location.latitude, location.longitude)
# 36.3236395 -94.255661
“地理编码”功能的另一个用途是将杂乱的地址数据转换成干净、标准化的格式。
从纬度和经度提取原始地址信息
有时,数据集可能只有纬度和经度,而没有任何地址信息,例如对人类有意义的城市、州、邮政编码。逆向地理编码是将位置坐标转换为带有位置信息的地址的重要步骤。要实现这一点,我们可以简单地使用“反向函数来获取位置的地址信息
location = geolocator.reverse('36.350885, -94.239816')
print(location.address)
# 2658, Southwest 20th Street, Bentonville, Benton County, Arkansas, 72713, United States
除了我们上面提到的公共和商业地理编码服务 API,还有政府地理编码 API,它们对公众开放。例如,我们可以从 FCC 的 Area 和 Census Block API中提取县信息,使用经纬度。
在下面的代码中,我们创建了一个函数,“extract_location_data”,使用纬度和经度从 FCC API 返回位置信息。
import requests
import urllib
def **extract_location_data**(lat, lon):
params = urllib.parse.urlencode({'latitude': lat, 'longitude':lon, 'format':'json'})
url = '[**https://geo.fcc.gov/api/census**/block/find?'](https://geo.fcc.gov/api/census/block/find?') + params
response = requests.get(url)
data = response.json()
return dataresult = extract_location_data(36.350885, -94.239816)
print(result['County'])
print(result['State'])
# {'FIPS': '05007', 'name': 'Benton County'}
# {'FIPS': '05', 'code': 'AR', 'name': 'Arkansas'}
测量两地之间的表面距离
两个地方之间的距离可能是许多机器学习模型中的一个基本特征。但是数据集中通常缺少这些信息。为了计算距离,我们不必自己应用数学公式。GeoPy 库中提供了测地线函数来计算两个地方之间的表面距离。
在下面的代码中,“测地线((lat_1,lon_1),(lat_2,lon_2))** ”将返回两个坐标之间的表面距离。您可以用公里、英里或英尺来返回距离。**
from **geopy.distance** import **geodesic**
point1 = (36.236984, -93.09345)
point2 = (36.179905, -94.50208)
distance = **geodesic**(point1, point2)
print(**distance.miles**, ' miles')
print(d**istance.meters**, ' meters')
# 78.80808645816887 miles
# 126829.32109293535 meters
测量两地之间的行驶距离
表面距离仅仅是一个理论概念,它适用于两个地方之间的直线距离。我们可能需要汽车、自行车或步行的驾驶距离,这在机器学习模型中更有意义。使用 API 很容易获得行驶距离。
****方法一:使用 自由 OSRM API 计算行驶距离
OSRM 是一个免费的开源 API。用 Python 中的“请求”函数提取信息很容易。在下面的代码中,它向 API 发送两个坐标,它将返回行驶距离和行驶时间。
import requests
import json
lat_1, lon_1 = 36.236984, -93.09345
lat_2, lon_2 = 36.179905, -94.50208r = requests.get(f"[**http://router.project-osrm.org/route/v1/driving/{lon_1},{lat_1};{lon_2},{lat_2}?overview=false**](http://router.project-osrm.org/route/v1/driving/{lon_1},{lat_1};{lon_2},{lat_2}?overview=false)""")
routes = json.loads(r.content)
route_1 = routes.get("routes")[0]
print(route_1["duration"], ' seconds')
print(route_1["distance"], ' meters')
# 8048.5 seconds
# 150883.3 meters
****方法二:使用 谷歌距离矩阵 API 计算行驶距离
我们也可以使用 googlemaps 库和 Google 的距离矩阵 API 来获取类似的信息。从下面的代码中可以看出,从谷歌的距离矩阵 API 得到的结果与从 OSRM API 得到的结果相当。
import **googlemaps**gmap = **googlemaps.Client**(key='Your Google Maps API Key')
gmap_output = gmap.distance_matrix((36.236984, -93.09345), (36.179905, -94.50208), mode='driving')
print(gmap_output['rows'][0]['elements'][0]['duration']['value'], 'seconds')
print(gmap_output['rows'][0]['elements'][0]['distance']['value'], 'meters')
# 6913 seconds
# 150700 meters
二。基本地图
在本节中,我将讨论如何使用 Plotly 库来创建具有交互功能的漂亮地图。这些图表很容易制作,并且是独立的,可以随时展示。
Plotly 还制作了 Dash ,这是一个用于创建可视化、构建 web 应用程序和部署机器学习模型的 Python 框架。
安装库
打开 anaconda 提示符或命令提示符,并键入以下命令。
pip install plotly
导入库
除了 Plotly 基本函数,我们还将 Plotly 使用 。Express ,它是 Plotly 的一个包装器,可以用简单的语法创建复杂而丰富的交互图形。
import plotly
import plotly.express as px
注册地图框令牌
如果你要创建一个基于 MapBox 服务的地图,那么你需要注册一个免费的 MapBox 账户并获得一个 MapxBox 令牌。这里 可以获得一个免费的地图框令牌 。典型的令牌看起来像“PK . eyj 1 ijoyiwayb 24 ty 2 xvdmvyxxxxsi 6 imnrcgvybwd 3 atcwaxuyd 242am v9 nzfvaziifq . 8 snsj 0 JCI 1 vknued 2 finib”。
如果使用 Plotly.Express,您可以在layout.mapbox.access_token或px.set_mapbox_access_token()配置功能中注册令牌。
from urllib.request import urlopen
**px.set_mapbox_access_token**(open('mapbox.mapbox_token').read())
**mapbox_access_token** = open('mapbox.mapbox_token').read()
创建模拟位置数据
对于本文,我将创建一个模拟的地理空间数据集,其中包括美国 1000 个随机生成的地点。
lon = []
lat = []
county = []
state = []
observation = 1while observation <= 1000:
lon_0 = np.random.uniform(-120,-65)
lat_0 = np.random.uniform(26,48)
data = **extract_location_data**(lat_0, lon_0)
print(data)
if data['status'] == 'OK':
if data['County']['FIPS'] != None:
lon.append(lon_0)
lat.append(lat_0)
county.append(data['County']['FIPS'])
state.append(data['State']['code'])
observation +=1
else:
pass
else:
pass
years = [randint(2000, 2010) for i in range(1000)]
df = pd.DataFrame({'long': lon, 'lat': lat, 'state': state, 'county_fips': county, 'year': years})
在地图中创建位置散点图
首先,让我们创建一个最简单的地图,一个散点图,它在地图上显示位置。我们将使用 Plotly.Express 中的" scatter_mapbox 。在程序中,我们需要指定位置的纬度和经度。
要导出地图图形,我们可以使用“ write_html 函数将绘图保存在本地文件夹中。
fig = px.**scatter_mapbox**(
df,
lat="lat",
lon="long",
zoom=4,
title = 'Simulated Locations in the US'
)fig.write_html('Locations Map.html')

作者图片
创建 Choropleth 地图
接下来,我们将创建 choropleth 地图,这是一种统计专题地图,使用颜色的强度来表示地理位置内地理特征的汇总。在下面的代码中,我们将创建两个 choropleth 地图——第一个显示美国每个州的沃尔玛商店的数量,第二个显示每个县的沃尔玛商店的数量。****
在创建地图之前,我们需要汇总数据来计算每个州或县的沃尔玛商店数量。为此,我们在 Pandas 中使用“groupby”和“count”函数。然后可以使用“ choropleth 功能创建 Choropleth 图。
- " location "参数将采用地理单位(例如,州或县 FIPS 代码)。
- " color "参数将采用与地理单元内的颜色强度相对应的数值(例如,沃尔玛商店的数量)。沃尔玛商店的数量越多,颜色越深。
- " locationmode "参数表示地图上的一组位置。它可以采用“ISO-3”、“美国-州”或“国家名称”中的一个。“ISO-3”是一个由三个字母组成的国家代码,而“美国-州”将返回带有州边界的美国地图。县边界在“locationmode”参数中不可用。相反,我们需要在“ geojson ”参数中包含一个县 GeoJSON 文件。此 GeoJSON 文件位于'https://raw . githubusercontent . com/plot ly/datasets/master/geo JSON-counties-FIPS . JSON '。
- "范围参数表示地图中的范围。它可以取“世界”、“美国”、“欧洲”、“亚洲”、“非洲”、“北美”或“南美”中的一个
- " color_continuous_scale "参数指定当由" color "表示的列包含数值时的连续色标
# by state
store_by_st = df.groupby('state')['long'].count().reset_index()
store_by_st.rename(columns = {'long':'Number of Locations'}, inplace = True)fig = px.**choropleth**(
locations=store_by_st['state'],
color = store_by_st['Number of Locations'].astype(float),
**locationmode**="**USA-states**",
**scope**="usa",
**color_continuous_scale** = 'ylorrd',
labels={'color':'Number of Locations'},
title = 'Number of Simulated Locations by State'
)
fig.write_html('Choropleth Map by state.html')##################
# by county
with urlopen('[**https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json**'](https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json')) as response:
**counties** = json.load(response)store_by_county = df.groupby('county_fips')['long'].count().reset_index()
store_by_county.rename(columns = {'long':'Number of Locations'}, inplace = True)fig = px.choropleth(
locations=store_by_county['county_fips'],
color = store_by_county['Number of Locations'].astype(float),
**geojson=counties,
scope="usa"**,
color_continuous_scale = 'ylorrd',
labels={'color':'Number of Locations'},
title = 'Number of Simulated Locations by County'
)
fig.write_html('Choropleth Map by county.html')

作者图片
基于列创建气泡图
接下来,我们将使用“ scatter_geo ”函数创建气泡图。该功能类似于 choropleth 功能。我们需要用数字列指定“大小参数,而不是“颜色参数”。
# by state
fig = px.**scatter_geo**(
store_by_st,
locations="state",
hover_name="state",
size="Number of Locations",
size_max = 30,
locationmode="USA-states",
scope="usa",
title = 'Number of Locations by State'
)
fig.write_html('Bubble Map by state.html')##################
# by county
fig = px.**scatter_geo**(
store_by_county,
locations="county_fips",
hover_name="county_fips",
size="Number of Locations",
size_max = 30,
geojson=counties,
scope="usa",
title = 'Number of Locations by County'
)
fig.write_html('Bubble Map by county.html')

作者图片
创建一个有多个图层的地图
我们可以在 Plotly 中创建多层地图。例如,在一张地图中,我们希望同时包含散点图和 choropleth 图。为此,我们需要同时使用“ plotly.graph_objects 和“ plot.express ”。
我们将使用“px.choropleth”创建地图的第一层,然后使用“ add_trace ”函数和“ go”添加第二层。散射地理”。“去吧。Scattergeo”相当于“px.scatter_mapbox”。最后,我们可以使用“ update_layout ”功能修改整个地图的布局,如标题、字体、字号、图例等。
import **plotly.graph_objects** as go# by state
fig = px.choropleth(
locations=store_by_st['state'],
color = store_by_st['Number of Locations'].astype(float),
locationmode="USA-states",
scope="usa",
color_continuous_scale = 'Blues',
labels={'color':'Number of Stores'},
)
fig.add_trace(
go.Scattergeo(
lon =df['long'],
lat =df['lat'],
mode="markers",
marker_color = 'maroon',
marker_size = 4
)
)
fig.update_layout(
title_text ='Simulated Locations by State',
title_x =0.5,
title_font_size=30)
fig.write_html('Choropleth_with_locations by state.html')##################
# by county
fig = px.choropleth(
locations=store_by_county['county_fips'],
color = store_by_county['Number of Locations'].astype(float),
geojson=counties,
scope="usa",
color_continuous_scale = 'Blues',
labels={'color':'Number of Stores'},
)fig.add_trace(
go.Scattergeo(
lon =df['long'],
lat =df['lat'],
mode="markers",
marker_color = 'maroon',
marker_size = 4
)
)
fig.update_layout(
title_text ='Simulated Locations by County',
title_x =0.5,
title_font_size=30)
fig.write_html('Choropleth_with_locations by county.html')

作者图片
三。高级地图图形
在本节中,我们将讨论在 Plotly 中创建高级地图图形。通过组合不同的功能,我们可以创建丰富的交互式地图。
使用下拉菜单将多个地图合并到一个页面中
有时,我们会创建多个地图,但希望在一个页面中包含所有地图。创建一个下拉菜单是一个包含所有信息同时保持格式整洁有序的完美解决方案。
在下面的示例中,我们希望创建一个县级的 choropleth 地图,并设计一个下拉菜单来选择一个指定的州以显示 choropleth 地图。
在程序中,我们需要两个组件,即“轨迹和“按钮”。“痕迹”是一个包括所有地图图层的列表。每一层都包括特定州的 choropleth 地图。“按钮”是下拉菜单中包含元素的列表。为了使下拉菜单工作,我们需要在我们创建的“按钮”列表的“布局”中指定“update menus”参数。当用户在下拉菜单中选择一个值时,可以通过“更新菜单”参数自动更新“可见”和“标题”参数。
不要忘记在布局参数中包含我们上面提到的“access token = map box _ access _ token”。
import numpy as np
store_by_county2 = df.groupby(['state', 'county_fips'])['long'].count().reset_index()
store_by_county2.rename(columns = {'long':'Number of Locations'}, inplace = True)# create drop-down menu
menu = store_by_county2['state'].unique().tolist()
visible = np.array(menu)**traces** = []
**buttons** = []
for value in menu:
subset = store_by_county2[store_by_county2['state']==value]
traces.append(
go.**Choroplethmapbox**(
locations=subset['county_fips'],
z = subset['Number of Locations'].astype(float),
**geojson = counties**,
colorscale = 'Blues',
colorbar_title = "Number of Stores",
**visible**= True if value==menu[0] else False
))
buttons.append(
dict(label=value,
method="**update**",
args=[{"**visible**":list(visible==value)},
{"**title**":f'Simulated Locations in <b>{value}</b>'}]))# create figure
fig = go.Figure(
data=**traces**,
layout=dict(
**updatemenus**=[{"active":0, "buttons":**buttons**,}]
))first_title = menu[0]
fig.update_layout(
title= f'Simulated Locations in <b>{first_title}</b>',
autosize=True,
hovermode='closest',
showlegend=False,
mapbox=dict(
**accesstoken=mapbox_access_token**,
bearing=0,
center=dict(
lat=38,
lon=-94
),
pitch=0,
zoom=4,
style='light'
))fig.write_html('Maps with drop-down.html')

作者图片
或者,我们可以使用破折号来创建一个使用 回调函数 的下拉菜单。
以给定的半径围绕点画一个圆
我们可以使用“分散地理”功能在地图上创建气泡,但是,当我们放大或缩小地图时,气泡的大小将保持不变。要在地图上画一个给定半径的圆,我们需要一个不同的解决方案。
在下面的例子中,我们希望包括加州大学洛杉矶分校 30 英里半径范围内的沃尔玛商店。
为了在地图中包含这个 30 英里的缓冲区,我们需要从头开始创建它。首先,我们需要中心点的纬度和经度,例如,加州大学洛杉矶分校。然后我们创建一个名为" geodesic_point_buffer 的函数,使用" pyproj "和" shapely "库返回这个 30 英里缓冲区边界的纬度和经度列表。当缓冲区的边界上有足够多的点时,我们可以通过用直线连接这些点来创建一个看起来非常接近圆形的几何对象。
最后,我们只需要将刚刚在“地图框”参数中创建的 30 英里缓冲区包含在“图层=图层”中。为了使地图看起来更好,我们希望在打开地图时基于中心点将地图居中。为此,我们可以设置“ center=dict( lat=lat,lon=lon) ”,其中 lat 和 lon 是中心点的经度纬度。
from functools import partial
import pyproj
from shapely.ops import transform
from shapely.geometry import Point# define a function to create buffer around a point
proj_wgs84 = pyproj.Proj('+proj=longlat +datum=WGS84')
def **geodesic_point_buffer**(lat, lon, miles):
# Azimuthal equidistant projection
aeqd_proj = '+proj=aeqd +lat_0={lat} +lon_0={lon} +x_0=0 +y_0=0'
project = partial(
pyproj.transform,
pyproj.Proj(aeqd_proj.format(lat=lat, lon=lon)),
proj_wgs84)
buf = Point(0, 0).buffer(miles * 1000/0.621371) # distance in miles
return transform(project, buf).exterior.coords[:]# Example
target = geolocator.geocode('University of California, Los Angeles')
**lat, lon = target.latitude, target.longitude**# create buffer layer
**features** = [{ "type": "Feature", "geometry": {"type": "LineString","coordinates": geodesic_point_buffer(lat, lon, 30)}}]
**layers** = [dict(
sourcetype = 'geojson',
source={"type": "FeatureCollection", 'features': **features**},
color= 'maroon',
type = 'fill',
opacity=0.2,
line=dict(width=1.5),
below = "state-label-sm"
)]# Create the map
fig = go.Figure()fig.add_trace(go.Scattermapbox(
lat=df['lat'],
lon=df['long'],
mode='markers',
marker=go.scattermapbox.Marker(
size=5,
color='blue'
)
))fig.update_layout(
title='Simulated Locations <br>Within 30 Miles of UCLA',
autosize=True,
hovermode='closest',
showlegend=False,
mapbox=dict(
accesstoken=mapbox_access_token,
**layers=layers**,
bearing=0,
center=dict(
lat=lat,
lon=lon
),
pitch=0,
zoom=6.5,
style='light'
),
)fig.write_html('Locations Map with 30 Miles Radius.html')

作者图片
通过修改程序中的特征和图层列表变量,我们可以围绕多个点创建圆或不同颜色的圆。
创建动画地图
接下来,我们将创建一个动画地图。例如,我们想创建一个会随时间变化的动态** choropleth 图。**
几个 Plotly Express 函数,如散点图、条形图、choropleth,通过 animation_frame 和 animation_group 参数支持动画人物的创建。
在下面的示例中,我们将创建一个动态 choropleth 地图,该地图按州显示一年中模拟位置的数量
首先,我们将聚合数据框,使用 pandas 中的“groupby”和“count”函数按州计算年度位置计数。然后“px.choropleth”被用于动画地图,带有附加参数,“animation _ frame = df[“year”]”和“animation _ group = df[“state”]”。
- animation_frame :该参数指定了一个用于创建动画帧的列。在这个例子中,我们将每年创建一个帧
- animation_group :该参数指定列中的相同值将被视为每帧中的相同对象。在这个例子中,我们将把每个帧中具有相同状态的行视为相同的对象。
- range_color :该参数指定了 choropleth 贴图中的颜色范围。以保持所有框架的配色方案一致。我们需要用最大数值指定 range_color 参数。
df =df.sort_values(['state', 'year'])
df = df.groupby(['state', 'year'])['long'].count().reset_index()
df = df.rename(columns = {'long': 'count'})
df = df.sort_values(['year'])
color_max = df['count'].max()fig = px.choropleth(
locations=df['state'],
color = df['count'].astype(float),
locationmode="USA-states",
scope="usa",
color_continuous_scale = 'ylorrd',
labels={'color':'Number of Locations'},
title = 'Number of Simulated Locations by State',
**animation_frame**=df["year"],
**animation_group**=df["state"],
range_color = [0, color_max]
)
fig.update_layout(
title_x =0.5,
title_font_size=30)fig.write_html('choropleth with sliders by state.html')

作者图片
感谢您的阅读!!!
如果你喜欢这篇文章,并且想请我喝杯咖啡,请点击这里。****
您可以注册一个 会员 来解锁我的文章的全部访问权限,并且可以无限制地访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请 订阅 。
如何使用 SQLAlchemy 和 Pandas 从 Python 连接到 SQL 数据库
原文:https://towardsdatascience.com/work-with-sql-in-python-using-sqlalchemy-and-pandas-cd7693def708
通过 SQLAlchemy 提取 SQL 表,在 SQL 数据库中插入、更新和删除行

帕斯卡尔·米勒在 Unsplash 上的照片(作者修改)
在一个数据科学项目中,我们经常需要与关系数据库进行交互,例如,在 SQL 表中提取表、插入、更新和删除行。为了完成这些任务,Python 有一个这样的库,叫做 SQLAlchemy 。它支持流行的 SQL 数据库,如 PostgreSQL 、 MySQL 、 SQLite 、 Oracle 、微软 SQL Server 等。更好的是,它有内置功能,可以与熊猫集成。SQLAlchemy 和 Pandas 是处理数据管理的完美组合。
安装库
除了 SQLAlchemy 和 pandas,我们还需要安装一个 SQL 数据库适配器来实现 Python 数据库 API 。例如,我们需要为 PostgreSQL 安装“psycopg2”或“pg8000”,为 mysql 安装“mysql-connector-python”或“oursql”,为 Oracle SQL 数据库安装“cx-Oracle”,为 Microsoft SQL Server 安装“pyodbc”或“pymssql”等。在本文中,我将讨论如何将 PostgreSQL 与 Python 集成,因此,让我们安装“ psycopg2 ”。
打开 anaconda 提示符或命令提示符,并键入以下命令。
pip install SQLAlchemy
pip install pandas
pip install psycopg2
导入库
import sqlalchemy
import pandas as pd
创建到数据库的连接
首先,让我们基于一个 URL 使用“ create_engine() ”函数创建一个与 PostgreSQL 数据库的连接。URL 通常由方言、驱动程序、用户名、密码、主机名、数据库名以及用于附加配置的可选参数组成。数据库 URL 的典型形式看起来像“方言+驱动://用户名:密码@主机:端口/数据库”。比如微软 SQL Server 的“MSSQL+py odbc://username:password @ host:port/database”,mysql 的“MySQL+MySQL connector://username:password @ host:port/database”,postgresql 的“PostgreSQL+psycopg 2://username:password @ host:port/database”。
url = '**postgresql+psycopg2://username:password@host:port/database**'
engine = sqlalchemy.**create_engine**(url)
我们还可以在“create_engine()”函数中包含可选参数。例如,我们可以添加“-csearch_path=schema_name”来覆盖 PostgreSQL 中当前会话的搜索路径。这相当于写一个查询,“将 search_path 设置为 schema_name”。
engine = sqlalchemy.create_engine(params, **connect_args**={'options': '**-csearch_path=schema_name**'})
使用 SQLAlchemy 运行 SQL 查询
一旦创建了连接,我们就可以用 Python 与 SQL 数据库进行交互。让我们从最简单的查询开始,“SELECT * FROM table”。
from sqlalchemy.sql import **text**
sql = '''
SELECT * FROM table;
'''
**with engine.connect()**.execution_options(**autocommit=True**) as conn:
query = conn.execute(**text**(sql))
df = pd.DataFrame(query.**fetchall()**)
我们将使用几个关键功能。
- text(): SQLAlchemy 允许用户通过函数“text()”使用 Python 中的原生 SQL 语法。它会将文本语句原封不动地传递给 SQL 数据库。因此,我们可以在一个 Python 框架内使用原生 SQL 语法,比如, DELETE , UPDATE , INSERT , SELECT,全文搜索等。
- engine.connect(): 该函数返回一个 SQL 连接对象。通过使用 Python 上下文管理器(例如,和语句)," Connection.close() "函数将自动包含在代码块的末尾。
- autocommit=True: 函数内部的可选参数。execution_options()"允许我们打开自动提交特性。这意味着我们不需要编写额外的代码,例如,“connection.commit()”和“connection.rollback()”。" connection.commit() "将提交对 SQL 数据库的任何更改,而" connection.rollback() "将放弃任何更改。使用自动提交的一个好处是我们有更少的代码行,并且解决了忘记提交变更的潜在问题。
- fetchall(): 这个函数将返回 row 对象,这些对象可以与 Pandas 集成在一起,创建一个数据框。
在下面的例子中,我们将更新、插入、删除 SQL 表中的行。与 SELECT 唯一不同的是我们编写了“conn.execute(text(sql))”而不是“query = conn.execute(text(sql))”,因为我们不提取表。
# **Update** rows in a SQL table
sql = '''
UPDATE table
SET col='abc'
WHERE condition;
'''
with engine.connect().execution_options(autocommit=True) as conn:
**conn.execute(text(sql))**# **Insert** new rows in a SQL table
sql = '''
INSERT INTO df
VALUES
(1, 'abc'),
(2, 'xyz'),
(1, 'abc');
'''
with engine.connect().execution_options(autocommit=True) as conn:
**conn.execute(text(sql))**# **Delete** rows in a SQL table
sql = '''
DELETE FROM df
WHERE condition;
'''
with engine.connect().execution_options(autocommit=True) as conn:
**conn.execute(text(sql))**
在 Python 中运行 SQL 查询可以非常灵活。我们可以设置一个 for 循环来基于不同的条件运行多个 SQL 查询。例如:
For i in [value_1, value_2, value_3, ...]:
if condition_1:
sql = '''**sql_query_1**'''
elif condition_2:
sql = '''**sql_query_2**'''
else:
sql = '''**sql_query_3**'''
with engine.connect().execution_options(autocommit=True) as conn:
conn.execute(text(sql))
运行多个 SQL 查询
在单个块中运行多个 SQL 查询也很简单。我们只需要用分号分隔语句。SQLAlchemy 的简单实现使得在 Python 中与 SQL 交互变得容易。
sql = '''
DROP TABLE IF EXISTS df;
CREATE TABLE df(
id SERIAL PRIMARY KEY,
salary integer
);
INSERT INTO df (salary)
VALUES
(400),
(200),
(3001);
SELECT * FROM df;
'''
with engine.connect().execution_options(autocommit=True) as conn:
query = conn.execute(text(sql))
df = pd.DataFrame(query.fetchall())
在 Pandas 数据框中存储 SQL 表
我们已经提到了在 pandas 数据框中保存 SQL 表的“fetchall()”函数。或者,我们也可以使用“ pandas.read_sql ”来实现。由于 SQLAlchemy 是和 Pandas 集成的,所以我们可以用“con = conn”直接使用它的 SQL 连接。
with engine.connect().execution_options(autocommit=True) as conn:
df = pd.**read_sql**(f"""SELECT * FROM table_name WHERE condition""", **con = conn**)
将数据帧插入现有的 SQL 数据库
要将新行插入到现有的 SQL 数据库中,我们可以使用带有原生 SQL 语法 insert 的代码,如上所述。或者,我们可以用“T2”熊猫。DataFrame.to_sql ,带有选项“if _ exists = ' append'”将行大容量插入到 sql 数据库中。这种方法的一个好处是我们可以充分利用 Pandas 的功能,比如导入外部数据文件和转换原始数据。因此,我们可以有一个兼容的 Pandas 数据帧(例如,具有与 SQL 表相同的列和数据类型),并准备好插入到现有的 SQL 数据库中。
df = pd.read_excel('sample.xlsx')
with engine.connect().execution_options(autocommit=True) as conn:
df.**to_sql**('table_name', con=conn, **if_exists=**'**append**', index= False)
创建新的 SQL 数据库
熊猫。DataFrame.to_sql ”也用于创建新的 sql 数据库。正如您在下面的示例中看到的,我们从 excel 电子表格中导入外部数据,并从 pandas 数据框架中创建新的 SQL 表。
from **sqlalchemy.types** import Integer, Text, String, DateTimedf = pd.read_excel('sample.xlsx')
df.**to_sql**(
"table_name",
con = engine,
**if_exists = "replace"**,
schema='shcema_name',
index=False,
chunksize=1000,
dtype={
"col_1_name": Integer,
"col_2_name": Text,
"col_3_name": String(50),
"col_4_name": DateTime
}
)
为了正确地创建一个新的 sql 表,我们需要用" to_sql() "函数指定几个重要的参数。
- if_exists :如果数据库中已经存在一个名为“table_name”的表,该参数将指示如何处理。传递“ replace ”将删除现有表中的所有行,并将其替换为当前的 pandas 数据框。如上所述,传递“ append ”只会将 pandas 数据框追加到现有的 SQL 表中。
- schema :该参数将获取保存新 SQL 表的模式名。如果已经在连接中指定了模式名,则不需要。
- index :该参数表示我们是否要在新的 SQL 表中为 DataFrame 的索引创建一列。
- chuncksize :该参数将指定每批中一次要插入的行数。默认情况下,所有行都将一次写入。
- dtype :该参数将指定新 SQL 表中列的数据类型。我们使用的数据类型来自 sqlalchemy.types.
感谢您的阅读!!!
如果你喜欢这篇文章,并且想请我喝杯咖啡,请点击这里。
您可以注册一个 会员 来解锁我的文章的全部访问权限,并且可以无限制访问介质上的所有内容。如果你想在我发表新文章时收到电子邮件通知,请订阅。
工作流编排与数据编排——它们有什么不同吗?
让我们消除这些术语的歧义,以便更好地理解工作流编排——用一个现实生活中的类比!

来自 Pexels 的 Artem Podrez 摄影
随着现代数据堆栈的兴起,业内许多工具开始将自己定位为“数据编排者”,而不是“工作流编排者”本文试图消除这些术语的歧义。我认为数据编排这个名字是一个容易混淆的简写术语,而工作流编排和数据流自动化更好地代表了现代数据堆栈的编排。
注:当我在府尹工作时,这篇文章反映了我的观点。把在我的博客上发表的任何东西(而不是在完美博客上发表的)视为不反映我雇主的个人观点。
**Table of contents:**· [What is workflow orchestration?](#3ef6)
· [What do people mean when they say “data orchestration”?](#a261)
· [Workflow orchestration — Explain Like I’m 5](#ff47)
∘ [Sequential, concurrent, and distributed workflow execution](#15c2)
∘ [Scale and graceful failure handling](#afe5)
∘ [Hybrid execution model](#3c18)
· [Why “workflow orchestration” is a less confusing term than “data orchestration”](#97b1)
· [Conclusion](#7aa6)
什么是工作流编排?
工作流编排意味着以尊重编排规则和业务逻辑的方式管理你的数据流。工作流编排工具允许你将任何代码转换成一个工作流,你可以安排、运行、观察。
一个好的工作流编排工具将为您提供连接到现有数据堆栈的构建模块,并允许您:
- 任务间传递数据,
- 触发特别参数化运行,
- 分配自定义(复杂)日程,
- 提醒当出现故障时,
- 重试并从故障中恢复,
- 通过缓存避免昂贵的重新计算,
- 并且将节省您编写防御代码的时间,只是为了确保您的工作流步骤以正确的顺序运行,并确保您发现故障何时发生(可见性)。
简而言之,好的工作流软件消除了负面工程。
当你考虑好的工作流工具时,你应该考虑当事情出错时它们是如何工作的。工作流只有在事情失败的时候才有意思;他们有点像保险或代码的风险管理 。假设事情会出错——它们会出错——正确的工作流工具应该使处理那些“错误的事情”变得快速而简单,并指导您如何修复它们。— 耶利米·劳因,2020 年 5 月
人们说的“数据编排”是什么意思?
它们通常指的是接触数据的工作流节点的编排。任何与数据交互的工作流节点,无论是生成数据还是使用数据,都属于这一类别。
根据这个定义,“数据编排”是编排数据(或数据仓库)工作流的简写术语,但它仍然描述工作流编排或数据流自动化。
工作流程编排—像 5 岁小孩一样解释
假设工作流编排工具是您的个人交付服务:
- 每个订单(或购物车)反映了你的工作流程,
- 每个交货是一个工作流运行,
- 把东西放进购物车非常简单方便——你只需要增加几个装饰工,然后你就可以去参加比赛了。
- 在每个订单(或购物车)中,你可能有许多产品被打包进箱子——你的任务,
- 盒子里的产品可能有各种口味、形式和形状,它们反映了你放入购物车的东西——你希望被编排成什么样的以及如何编排,
- 风格可能反映了您的数据复制工作(例如 Airbyte)、数据转换(例如 dbt)、数据清理(例如 pandas)、ML 用例(例如 scikit-learn)等等。
- 你的箱子可能如你所愿一样小或一样大——最终是你的订单(你的工作流程设计),
- 你盒子里的产品可能来自各个厂商,也就是你的数据工具,比如 dbt,Fivetran,你喜欢的 ML 框架,你定制的数据清理库,
- 交付地址可能是你的家庭地址(你的数据仓库)、你的度假地址(你的数据仓库),也可能是朋友的地址(某个外部数据库、数据处理服务、微服务或应用)。

来自 Pexels 的 Norma Mortenson 的照片
顺序、并发和分布式工作流执行
按照交付服务的类比,在编排中,您可以:
- 决定你是想让你的订单一次交付还是按顺序交付——你的任务的执行顺序,
- 选择您的交付类型 —您可以选择标准的(顺序执行)或使用特殊分布式服务的快速交付,例如 Dask 、 Ray ( 想象多辆交付卡车),或者甚至使用并发和异步 ( 一个更快更高效的单一交付程序,可以更好地切换上下文
- 确定你的订单应该如何包装——你可以选择打包到一个子流程、一个 Docker 容器、 Kubernetes 作业或者一个 ECS 任务中。
工作流程编排将负责交付,即执行。它将确保您的产品按照预期得到包装,按照正确的时间表得到运输,并且所有箱子都有正确的交付类型——一些包裹需要通过快递快速交付,而其他的可以等待并按顺序执行。
规模和优雅的故障处理
一个好的交付(编排)服务的伸缩性非常好。你可以让的多个交付安排在同一时间,潜在的数千辆卡车(甚至货船车队)数百万个包裹,它仍然会让你对每个包裹的交付状态有细粒度的可见性。
这种调度服务是异步和高度可用的,以保证即使在一些供应商生病或一些卡车抛锚的情况下,您的订单也能发货。交付过程中可能会出现许多问题:
- 有些盒可能会损坏并可能需要退回,即重试或重启,
- 整个交货可能需要重新安排,因为那时你不在家。
混合执行模型
好的送货服务也会尊重你的隐私并纯粹基于元数据运作,比如你的送货地址、送货类型、包装形式等。然后它负责传输和执行(数据流),但是它不能 也不应该打开盒子 去检查 ( 你的数据)。
为什么“工作流编排”比“数据编排”更容易混淆
一个好的工作流程编排工具(您的交付服务)允许您挑选您的产品,将它们放入盒子中,并根据您的意愿定制您的订单,但最终,它负责数据移动 ( 交付、运输、数据流、执行),而不是关于那些盒子中的实际产品(您的数据)。因此,术语“数据编排”是一个容易混淆的简写,缺少描述数据在系统中流动时的实际移动的词语。数据流编排、工作流程编排、数据运动编排、数据处理编排、数据传输编排——所有这些都比速记术语“数据编排”清晰得多。
工作流编排是关于数据流和确保你可以通过各种故障处理机制依赖它的执行。它可以让你看到交付花了多长时间。它可以为您提供所有货件更新(您的工作流程执行日志)。它可以告诉您一个箱子是否被成功地运送到最终收件人,但它不能直接打开箱子检查里面产品的品牌、质量和产地。
借用这篇博客文章中的比喻,编排是关于箭头的,这些箭头表示不同的盒子(任务)在执行时的转换,而不是关于盒子本身(你的数据)。它应该保证您的流程和任务在正确的时间以正确的顺序和正确的并行性运行。它应该防止错误和失败,帮助您从错误和失败中恢复,并正确解释执行状态。
结论
本文旨在消除这些术语的歧义,并让您对什么是工作流编排有所了解。如果你还有任何问题或者想进一步讨论这个话题,你可以加入提督社区 Slack:prefect.io/slack。
感谢阅读!
圣克拉拉县的劳动力公平
原文:https://towardsdatascience.com/workforce-equity-at-the-county-of-santa-clara-9da948d63ec5
使用逻辑回归预测圣克拉拉县的全职就业结果

普里西拉·杜·普里兹在 Unsplash 上的照片
随着许多雇主开始重新评估其劳动力的人口构成,我想通过挖掘我自己雇主公开提供的劳动力数据来确定在我工作的地方人口公平是什么样的。通过这个项目,我应用了探索性数据分析(EDA)和数据可视化方法,并拟合了一个逻辑回归模型,以预测各种人口特征的全职就业状况。出于本分析的目的,全职员工被定义为被归类为(永久或试用状态)或执行管理层的员工。所有其他人都被视为非全职员工。
内容
1.探索性数据分析和数据可视化
2.数据清理和回归建模
3.解释结果
4.优势和局限
探索性数据分析&数据可视化
首先,我从圣克拉拉县开放数据门户下载了平等机会就业数据集(n=19707)并将其导入 r。我从一些 EDA 和数据可视化开始,以扫描数据中的异常、异常值或任何其他有趣的趋势。
使用 Rstudio,我构建了条形图和点状图来可视化数据集中的关键变量。以下是我用来构建条形图的示例代码:
ggplot(df, aes(Gender))+
geom_bar(stat="count", width=0.7, fill="#1A1A75")+
geom_text(aes(label = ..count..), stat = "count", vjust = 2, colour = "white")+
labs(title = "Count of Gender of SCC Workforce", x= "Gender", y= "Count")+
theme_bw()+
theme(plot.title= element_text(size= 20), axis.text.x = element_text(hjust = 1),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.line = element_line(color = 'black'))

使用 Rstudio 创建的样本中的性别分布。点击此处查看该分析的其他可视化效果。图片作者。
为了创建一个点状图,我开发了一个单独的视图,只使用“部门”变量创建一个新的数据框。这使得“Department”变量的计数很容易传递到 ggplot (Rstudio 数据可视化库)中。下面是我用来按“部门”构建点状图的代码:
dept_count <- table(df$Department)
dept_count <- as.data.frame(dept_count)
ggplot(dept_count, aes(x=Freq, y= Var1, label= Freq)) +
geom_point(aes(col=Var1), size=7, show.legend = F)+
geom_text(color="white", size=2.5, parse = T)+
xlab("Count")+
ylab("Department")+
labs(title="Count of Employees by Department")+
theme_minimal()
以下是结果输出:

图片作者。
我还使用 Google Data Studio 创建了一个简单的仪表板来显示原始数据。
作者使用 Google Data Studio 创建的仪表板。
数据清洗和回归建模
从那里,我开始清理和格式化数据,以传递到逻辑回归模型中。这需要将数据编码成定量格式(对于参照组,从 0 开始对变量进行编号,变量中的每个类别增加 1)。这个数据字典提供了关于我在模型中使用的变量是如何编码的文档。以下是我的代码示例,用于对“种族”变量进行编码,并将该变量从字符变量转换为数字变量:
df$Ethnicity[df$Ethnicity == "American Indian/Alaska Native"] <- 1
df$Ethnicity[df$Ethnicity == "Black/African American"] <- 2
df$Ethnicity[df$Ethnicity == "Native Hawaiian/Oth Pac Island"] <- 3
df$Ethnicity[df$Ethnicity == "Asian"] <- 4
df$Ethnicity[df$Ethnicity == "Hispanic/Latino"] <- 5
df$Ethnicity[df$Ethnicity == "Two or More Races"] <- 6
df$Ethnicity[df$Ethnicity == "Not Applicable"] <- 7
df$Ethnicity[df$Ethnicity == "Not Identified"] <- 8
df$Ethnicity[df$Ethnicity == "White"] <- 0 #comparison group
df$Ethnicity <- as.numeric(df$Ethnicity) #convert from chr to numeric
然后,我将选定的人口统计变量拟合到回归模型中,并总结了模型的结果:
m <- glm(as.factor(Employment.Status) ~ as.factor(Age) + as.factor(Gender) + as.factor(Ethnicity), family = "binomial", df)summary(m)
解读结果
下面的输出显示了逻辑回归模型的结果。“*”表示显著性,您可以看到,在对模型中包含的其他变量进行调整后,并非模型中包含的所有变量都保持显著性。
Call:
glm(formula = as.factor(Employment.Status) ~ as.factor(Age) +
as.factor(Gender) + as.factor(Ethnicity), family = "binomial",
data = df)Deviance Residuals:
Min 1Q Median 3Q Max
-2.6159 -0.6046 -0.5015 -0.3303 2.4451Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.066504 0.062371 -33.133 < 2e-16 ***
as.factor(Age)1 4.871116 0.735350 6.624 3.49e-11 ***
as.factor(Age)2 1.404890 0.065853 21.334 < 2e-16 ***
as.factor(Age)3 0.375222 0.059230 6.335 2.37e-10 ***
as.factor(Age)4 0.001325 0.065042 0.020 0.9837
as.factor(Age)5 0.572097 0.073372 7.797 6.33e-15 ***
as.factor(Age)6 1.811770 0.140881 12.860 < 2e-16 ***
as.factor(Gender)1 0.055374 0.042902 1.291 0.1968
as.factor(Ethnicity)1 -0.859101 0.431944 -1.989 0.0467 *
as.factor(Ethnicity)2 -0.181556 0.101585 -1.787 0.0739 .
as.factor(Ethnicity)3 -0.577770 0.297139 -1.944 0.0518 .
as.factor(Ethnicity)4 0.084497 0.054369 1.554 0.1202
as.factor(Ethnicity)5 -0.871253 0.069348 -12.563 < 2e-16 ***
as.factor(Ethnicity)6 -0.063882 0.113879 -0.561 0.5748
as.factor(Ethnicity)7 0.528137 0.065507 8.062 7.49e-16 ***
as.factor(Ethnicity)8 -10.607403 122.517633 -0.087 0.9310
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1(Dispersion parameter for binomial family taken to be 1)Null deviance: 16956 on 19706 degrees of freedom
Residual deviance: 15713 on 19691 degrees of freedom
AIC: 15745Number of Fisher Scoring iterations: 11
为了从模型中获得优势比和 p 值,需要对输出进行指数运算。这可以通过在模型上执行以下代码来完成:
exp(coefficients(m))
exp(confint(m))
以下是上述代码的输出:
#Odds Ratios:
exp.coefficients.m..
(Intercept) 0.12662764672
as.factor(Age)1 130.46646380157
as.factor(Age)2 4.07508039371
as.factor(Age)3 1.45531434372
as.factor(Age)4 1.00132609820
as.factor(Age)5 1.77197884694
as.factor(Age)6 6.12127428713
as.factor(Gender)1 1.05693574512
as.factor(Ethnicity)1 0.42354252826
as.factor(Ethnicity)2 0.83397119030
as.factor(Ethnicity)3 0.56114822112
as.factor(Ethnicity)4 1.08816930350
as.factor(Ethnicity)5 0.41842702920
as.factor(Ethnicity)6 0.93811576448
as.factor(Ethnicity)7 1.69577024472
as.factor(Ethnicity)8 0.00002473223#Confidence Intervals:
2.5 % 97.5 %
(Intercept) 0.1119507 0.1429609
as.factor(Age)1 38.9341090 811.6890375
as.factor(Age)2 3.5822371 4.6374648
as.factor(Age)3 1.2960778 1.6348703
as.factor(Age)4 0.8813076 1.1373159
as.factor(Age)5 1.5337998 2.0450854
as.factor(Age)6 4.6385626 8.0627385
as.factor(Gender)1 0.9718973 1.1499075
as.factor(Ethnicity)1 0.1624569 0.9095877
as.factor(Ethnicity)2 0.6811999 1.0146872
as.factor(Ethnicity)3 0.2988317 0.9668308
as.factor(Ethnicity)4 0.9783697 1.2108047
as.factor(Ethnicity)5 0.3649682 0.4790100
as.factor(Ethnicity)6 0.7478204 1.1689730
as.factor(Ethnicity)7 1.4911884 1.9278357
as.factor(Ethnicity)8 NA 1.4707108
我立刻注意到一个不寻常的 1 岁组的优势比,即“20 岁或以下”。回顾 EDA,我们看到这个类别只有 35 个观察值。由于该类别中有限的样本量,(和大的置信区间),这一估计可能是不可靠的。
提示:如果置信区间超过 1(例如. 08–1.3),研究组和对照组之间没有统计学差异。
这项分析的结果包括,在圣塔克拉拉郡,西班牙裔成为全职员工的可能性比白人低 58%(CI:0.36–0.47)。在圣克拉拉县,没有发现全职职位的“男性”和“女性”之间的统计差异。与 40-49 岁的人相比,30-39 岁的人成为该县全职员工的可能性高出 45%(CI:1.30-1.63)。
优势&局限
与任何类型的建模一样,使用某些技术优于其他技术也有利弊。逻辑回归也不例外。尽管它是构建和解释起来较为简单的模型之一,但它严重依赖于具有足够样本量的格式良好的数据。
该数据集的一些局限性包括这些变量是如何测量的。例如,“性别”变量只收集了“男性”和“女性”信息,而没有 SOGI(性取向性别认同)信息。此外,“种族”变量是聚合的,因此不可能通过不同的种族亚组来确定模式或趋势。
这一分析中使用的数据已经过时,并且在一个变量的所有类别之间不平衡。为了解决这个问题,可以操纵数据来创建更大的类别(例如,更大的年龄范围)。然而,这将冲淡结果解释的粒度。更强大的数据收集将允许更准确的分析,从而更容易确定需要改进的领域,以实现更公平的员工队伍。
我的完整代码、数据字典、原始和分析数据集以及数据可视化可以在我的 Github 页面这里找到。
在分析领域工作去神秘化
原文:https://towardsdatascience.com/working-in-analytics-demystified-a78e8bd60f32
这篇文章旨在阐明现代“全栈分析师”的定义,深入探讨这个角色的实际范围和成功的要求

数据科学组件,图片由作者提供
数据科学家是本世纪最性感的工作。大约 10 年前,这是一篇著名的 HBR 文章的标题,它打开了数据科学这门尚处于萌芽阶段的学科的大门。在接下来的几年里,许多雇主和企业(从中小企业和初创企业到老牌大公司)都在寻找最优秀的人才来释放大数据海洋中的潜力和隐藏的宝藏。一个引人注目的口号——“数据是新的石油”——是为了在一个越来越数字化的世界中捕捉数据的巨大相关性和重要性而创建的,收集了前所未有的数据量。
在过去十年中,数据科学一直被视为一个巨大的黑匣子。由于这一学科还是相当新的,对于构成数据科学的不同元素的结构还没有一致的共同理解。这引发了很多关于“数据科学家”应该拥有和执行什么能力和任务的困惑。公司的求职公告板上满是数据科学家和分析师的角色,但他们缺乏共同的特质。机器学习、算法、分析和数据工程专业经常互换使用,因此不同公司(甚至同一家公司)的数据科学家会做完全不同的事情。
现在,我们终于到达了数据科学的分类和定义广为人知和共享的地步。
数据科学可以分为三个不同的宏观领域,每个领域都有特定的技能和目标:分析&洞察力、机器学习&算法和数据工程。在这篇文章中,我将分享“全栈分析师”(FSA)在“分析&洞察”领域的一系列行动和主要活动,强调要在这一具有挑战性的角色中脱颖而出需要掌握的主要能力。
注意:就每一个理论框架而言,现实总是更加微妙,可能存在细微的差异。如上图所示,由个人兴趣或业务需求驱动的分析角色经常会与机器学习和数据工程有很大的重叠,所以要随时准备好学习和适应!

全栈分析框架,图片由作者提供
现代金融服务管理局需要软硬技能的独特结合。FSA 跟踪整个数据“价值链”,从数据提取到最终的业务决策,因此多才多艺和兼收并蓄是成功完成这一角色所需要的最重要的品质。在本文的剩余部分,我将更深入地探讨 FSA 涵盖的每一项活动。
数据提取&数据操作—FSA 的主要目标之一是将大量无组织、混乱、杂乱的原始数据转化为可操作的业务洞察。因此,FSA 必须能够提取和操作数据以满足业务需求。在实践中,这转化为能够清理和理解底层数据,查询数据库,并为利基商业案例创建模型和特别管道/来源。
直到最近,实现数据提取和数据操作普遍需要的唯一语言是 SQL。虽然它仍然是每个 FSA 闭着眼睛都必须知道的不可或缺的重要工具,但现代 FSA 被要求使用其他语言,如 evergreen Python 和 R,或更近的明星,如 Scala 和 Spark。
实验 —在科技公司,实验和 A/B 测试对于实现增长和战略目标至关重要。机械地运行测试结果的工具现在很常见,实验通常可以由业务团队自主运行。需要 FSA 的支持和意见,尤其是关于实验设置及其解释。FSA 拥有关于基础数据、统计数据和商业敏锐度的独特知识组合,因此他们可以帮助团队进行实验,确保测试设置正确,并且结果不会有偏差或被误解。
数据可视化—FSA 与业务方沟通的首选方式之一是通过数据可视化。随着可用工具和所用技术的进步,如今数据可视化本身可以被视为一个独立的学科(我在本文中探讨了这个主题:https://medium . com/me/stats/post/78993900 ECA 9)。.)数据可视化的强大功能来自其多种用途:它可以用于报告记分卡上的指标和 KPI,或者用于业务团队使用的自助服务工具,或者用于数据演示和讲述故事。
FSA 通常被视为组织内的 SME,因为它们能够直接与数据提取&数据操作活动产生的数据联系。
通常,FSA 应该精通一个或多个 BI 工具(Looker、Tableau、Power BI 等)或数据可视化库(例如 seaborn)。
商业战略——FSA 与机器学习数据科学家和数据工程师的主要区别在于 FSA 与整体产品战略的接近程度。金融服务助理必须具备商业头脑,他们需要在相关的业务和行业中拥有深厚的专业知识。FSA 是产品经理(或分配给他们的职能的所有者,例如营销经理、商务经理)的得力助手。等等),它们对策略的计划、测试和跟踪有直接的影响。FSA 应该是指标和 KPI 树的所有者,他们应该建立它们的定义、可行性评估、计算和报告套件。
项目管理——FSA 很可能不仅仅分配给特定的团队。通常,FSA 服务于多个产品团队、多个营销渠道等。因此,许多不同的团队向 FSA 请求支持并不罕见。
能够对众多请求进行优先排序并拥有相关的项目管理技能对于确保 FSA 致力于对业务具有最高影响价值的活动至关重要。
因此,利益相关者管理技能至关重要。FSA 在一个组织内与大量不同的人一起工作,从初级到高级管理人员,与技术和非技术受众打交道。
业务决策 —最终,FSA 的工作由所采取的数据驱动型业务决策来衡量。通过不断的咨询和与业务团队的有影响力的联系,FSA 可以直接或间接地做出这些决定。沟通可能是金融服务专员最重要的技能。无论你的模型有多复杂,你的洞察力有多强,如果你不能恰当地传达信息,你的工作就会被认为是草率和不充分的,从而影响你作为一个能够通过数据驱动的洞察力来推动价值的领导者的可信度和资格。
结论
一个反复出现的问题是“什么真正让分析师变得伟大?”我要说的是,尽管技术技能无可争议地必不可少,而且需求也越来越大,但金融服务人员只有具备出色的业务和行业知识,并且能够通过清晰、简洁且易于理解的沟通来传达自己的见解,才能真正改变一个组织。
由于 FSA 与系统和业务密切相关的独特地位,我预计越来越多寻求通过数据价值链获得真正竞争优势的公司将高度重视这一角色。
使用调查数据——在 R 中清理和可视化李克特量表问题
如何格式化数据以进行定量分析

如果你曾经读过或写过一份调查,你可能遇到过李克特式的问题。这些问题在某种程度上是可以回答的,比如强烈不同意到强烈同意,或者总是到从不。这种尺度允许我们量化情感,否则是主观的。今天,我们将讨论如何清理、聚合和可视化这类数据。
关于数据集
我们将使用 1000 名受访者的产品满意度调查数据。我们的目标是对这些数据进行重新编码和整形,使它们适合计算汇总统计数据和创建可视化。你可以在这里下载数据集。
加载数据
该数据集为我们提供了申请人 ID,以及他们对每个问题的回答,每个问题编码在一列中。如您所见,列标题不是正确的变量名。这将使得在函数中调用它们变得困难。所以让我们用colnames()按照类别和物品号来重命名它们。请务必将原始名称保存到单独的文件中,以供将来参考。我们还需要加载 tidyverse 包来执行我们的数据争论和可视化。
记录李克特反应
我们的回复仍然是文本形式。我们需要用数字标度记录它们,以便进行定量分析。我们可以使用case_when()编写一个函数来完成这个任务。这也可以用gsub()来完成,但我更喜欢这种方法,因为它使我们的代码更紧凑,可读性更好。
有时,短语以否定的方式陈述,以确保回答调查的人不会不加选择地输入所有高分或低分答案。因此,我们必须:
- 确定哪些项目是积极或消极的。
- 在我们的例子中,除了以下项目,所有项目都按正比例缩放:

负面陈述项目(作者图片)
2.将函数原样应用于按正比例缩放的项目,并翻转按负比例缩放的项目的比例。
-
在这里,我写了两个独立的函数,分别用于正负比例项。
-
为了应用正数,我使用
select()删除了 id 列和所有负数比例的项目,所以我可以很容易地mutate_all()剩下的正数。 -
为了应用负的那个,我反其道而行之,在应用
mutate_all()之前只保留负比例的项目。 -
在应用了这两个函数之后,我们必须使用
cbind()重新组合我们的数据。
计算总数

每个回答者的总体总数和类别总数的前 6 行(图片由作者提供)
-
由于本次调查衡量的是整体产品满意度,我们想知道每位受访者在所有项目上的总分。我们可以通过对重新编码的数据使用
rowSums()函数来实现这一点。 -
现在,让我们得到每个类别的总分。我们可以使用
mutate()来创建包含类别总计的新列。rowwise()函数允许我们跨列执行操作。执行完所有操作后,记得使用ungroup()将您的数据恢复为整齐的格式。 -
作为一个练习,你可以试着自己计算类别意味着什么!
重塑数据
我们希望我们的数据以整洁的格式进行分析和可视化。在这种情况下,这意味着旋转我们的数据,这样我们的新列将只包含 ID 号、问题号、类别和响应。首先,我们将关注问题编号和回答。然后,我们可以用mutate()来分配它们的类别。我们将结合使用str_match()和正则表达式,或正则表达式,从问题列中获取类别和问题编号。这里,“^[^_]+(?=_)”表示获取下划线之前的所有字符,“[0–9]$”表示获取字符串的最后一位数字。
汇总数据

每个类别回复的平均值和标准偏差(图片由作者提供)
现在,我们可以计算数据的汇总统计数据。因为我们想按类别计算统计数据,所以我们将按这个变量分组。然后,我们将把数据传递给summarise()函数。这是我们的预期输出:
可视化数据

总分直方图(图片由作者提供)
首先,我们来了解一下总分的分布概况。我们使用了survey_recoded数据,因为该数据集包含每位受访者的总分。请注意,数据似乎近似遵循正态分布。这是意料之中的,因为我们有 1000 个大样本。

每个类别分数的直方图(图片由作者提供)
现在,让我们看看类别级的分布。我们将使用survey_long作为我们的数据,并将类别传递给facet_wrap()来做这件事。

每个问题分数的直方图(图片由作者提供)
我们也可以通过相同的方法来查看问题级别的分布。在这里,我添加了一个额外的参数nrow = 3,这样所有来自同一类别的问题都出现在一行中。

每个问题的平均分(图片由作者提供)
最后,让我们看看每个问题的平均回答水平。我们将变量 response 除以 1000,因为如果没有它,我们的图表只会给出每个问题的得分总和。我们还添加了ylim(0,5),因为否则,我们的图形在 y 轴上只有 0 到 4。这是因为问题的最高平均分从来不会高于 4 分。
接下来的步骤
本教程侧重于为探索性分析准备测量数据。我们创建的条形图很有见地,但我们可以对它们的美学进行改进,使它们更加专业和引人注目。请阅读我关于如何做到这一点的教程:
为了进行更深入的分析,我们还可以执行统计测试来检验相关性和测量调查的可靠性。您可以阅读更多关于这些概念的内容,并尝试将它们与 R 的cor.test()函数和ltm包中的cronbach.alpha()一起应用。
在 Python 中使用井轨迹
原文:https://towardsdatascience.com/working-with-well-trajectories-in-python-b8eb39773f64
使用 wellpathpy 将原始油井测量数据转换为完整的位置数据

詹姆斯·惠勒摄:https://www . pexels . com/photo/photo-of-pathway-around-by-fir-trees-1578750/
处理地下数据时,深度是一个重要的测量指标。它用于将多组数据绑定到单个引用。有许多用于识别地下位置的深度参考。这些包括测量深度(MD)、真实垂直深度(TVD)和真实水下垂直深度(TVDSS)。

钻井和测井中关键参考和深度名称的图解。图片由作者提供。
上图展示了本文中提到的关键深度参考。
- 测量深度(MD)是沿井眼长度测量的井眼长度。
- 真实垂直深度(TVD),是基准面(如转盘)和井筒中一点之间的绝对垂直距离。
- 海底真实垂直深度(TVDSS),是平均海平面和井筒中某点之间的绝对垂直距离。
当井是垂直的时,MD 等于从同一基准面测量的 TVD。在井斜的情况下,TVD 值变得小于 MD 值。
当钻井时,通常进行勘测测量,以确保井沿着预定方向前进。这将生成一个具有稀疏测量值的数据集,其中包含井眼倾角(井眼偏离垂直方向的程度)、相对于北方的方位角以及当前测量的深度。
为了将其转换为定期采样的数据,类似于测井测量中使用的采样率,我们需要应用三角计算
在本教程中,我们将看到如何使用名为 wellpathpy 的 Python 库来生成完整的位置日志。
视频教程
如果你想看这个教程的实际操作,我的 YouTube 频道上有这个教程的视频版本。
好可怜
wellpathpy 是一个 Python 库,开发该库是为了从各种行业标准方法中获取油井测量数据并计算位置日志。这允许我们生成 TVD、TVDSS、北距和东距。
你可以在下面链接的 GitHub repo 中找到更多关于这个库的信息。
https://github.com/Zabamund/wellpathpy
如果您还没有安装这个库,您可以使用 pip 安装它,如下所示:
pip install wellpathpy
本教程中使用的数据
本教程中使用的数据是 Equinor 在 2018 年发布的 Volve 数据集的子集。数据集的全部细节,包括许可证,可以在下面的链接中找到。
https://www.equinor.com/energy/volve-data-sharing
Volve 数据许可证基于 CC BY 4.0 许可证。许可协议的全部细节可以在这里找到:
导入库和数据
我们教程的第一步是导入我们将要使用的库。
在这种情况下,我们将主要使用 3 个库: wellpathpy 、 matplotlib 用于可视化我们的井轨迹,以及 pandas 用于创建我们的最终数据帧。
我们还导入了 numpy ,它用于在 wellpathpy 生成的数组中隐藏科学记数法。这是通过使用np.set_printoptions()函数并传入supress=True来实现的。
import wellpathpy as wp
import matplotlib.pyplot as plt
import pandas as pdimport numpy as np
np.set_printoptions(suppress=True)
导入库后,下一步是导入测量数据。原始 csv 文件包含三列,分别命名为 md,inc 和 azi。

一旦我们有了这种格式的 csv 文件,我们就可以给函数调用wp.read_csv()分配三个变量:md, inc和azi。
md, inc, azi = wp.read_csv('Data/Volve/15_9-F-12_Survey_Data.csv')
在后台执行许多检查,以确保数据是有效的。其中包括:
- 测量深度(md)单调增加
- 各列按照 md,inc 和 azi 的正确顺序排列
- 倾斜度(inc)包含 0 到 180 度之间的值
- 方位角(azi)包含 0 到 360 度之间的值
一旦数据被加载,我们可以调用md并查看数据。这允许我们检查数据是否已经成功加载。

载入井眼路径后的测量深度数据示例。图片由作者提供。
将调查数据加载到 wellpathpy
加载数据后,我们现在需要创建一个 wellpathpy 偏差对象。这是按如下方式启动的:
dev = wp.deviation(md, inc, azi)
dev
当我们查看 dev 对象时,我们看到 md,inc 和 azi 数据存储在数组中。

我们可以通过像这样调用它们来访问每一个:
dev.md
dev.inc
dev.azi
可视化原始调查数据
我们可以在 matplotlib 中通过创建一个包含两个子图形的图形来非常简单地可视化这些原始数据。一个用于倾角(inc ),一个用于方位角(azi)。这将使我们能够看到井眼轨迹如何随测量深度变化。
fig, ax = plt.subplots(1, 2, figsize=(8,10))ax1 = plt.subplot2grid((1,2), (0,0))
ax2 = plt.subplot2grid((1,2), (0,1))ax1.plot(dev.inc, dev.md, color = "black", marker='.', linewidth=0)
ax1.set_ylim(dev.md[-1], 0)
ax1.set_xlim(0, 90)
ax1.set_xlabel('Deviation', fontweight='bold', fontsize=14)
ax1.set_ylabel('Measured Depth', fontweight='bold', fontsize=14)
ax1.grid(color='lightgrey')
ax1.set_axisbelow(True)ax2.plot(dev.azi, dev.md, color = "black", marker='.', linewidth=0)
ax2.set_ylim(dev.md[-1], 0)
ax2.set_xlim(0, 360)
ax2.set_xlabel('Azimuth', fontweight='bold', fontsize=14)
ax2.grid(color='lightgrey')
ax2.set_axisbelow(True)plt.show()

原始勘测测量的井筒方位角和倾斜度。图片由作者提供。
在上面的图中我们可以看到:
- 钻孔开始是垂直的,直到我们到达大约 500 米,然后逐渐增加,然后减少。最终,井眼以 53.43 度的倾角落在目标层段内。
- 当钻孔开始垂直时,我们的方位角被设置为正北(0 度),然后转向东北,然后转向南方,并在东方结束
在本文的后面,我们将能够以 3D 的形式来可视化这些数据,这将会更有意义。
从测量数据创建位置日志
调查数据是随机抽样的,两次测量之间有很大的间隔。我们可以对数据进行重新采样,这样每 1 米就有一次测量。
为此,我们首先定义深度步长,然后创建一个在 0 和最后一个md值之间的等间距值列表。
depth_step = 1
depths = list(range(0, int(dev.md[-1]) + 1, depth_step))
wellpathpy 包含多种计算真实垂直深度(TVD)的方法,但是,我们将重点介绍最小曲率法。如果你想了解这种方法背后的数学原理,请点击下面的链接。
https://www.drillingformulas.com/minimum-curvature-method/
为了创建我们的 TVD 曲线和位置测量(北距和东距),我们需要调用以下代码。这也将重新采样我们的深度值,以便我们每 1 米测量一次。
pos = dev.minimum_curvature().resample(depths = depths)
当我们调用pos时,我们得到一个包含三个数组的位置对象,如下所示:

创建重新采样的测量数据
为了将我们的原始测量数据更新到 1 米的深度步长,我们可以通过调用pos.deviation()来对数据进行重新采样
resampled_dev = pos.deviation()
当我们查看resampled_dev时,我们得到下面的数组。

与我们对数据进行重采样的任何计算一样,将结果与原始结果进行比较是一种很好的做法。
下面的代码与上面创建的图相同,但是现在包含了来自变量resampled的重采样数据。
fig, ax = plt.subplots(1, 2, figsize=(8,10))ax1 = plt.subplot2grid((1,2), (0,0))
ax2 = plt.subplot2grid((1,2), (0,1))ax1.plot(dev.inc, dev.md, color = "black", marker='.', linewidth=0)
ax1.plot(resampled_dev.inc, resampled_dev.md, color='red')ax1.set_ylim(dev.md[-1], 0)
ax1.set_xlim(0, 90)
ax1.set_xlabel('Deviation', fontweight='bold', fontsize=14)
ax1.set_ylabel('Measured Depth', fontweight='bold', fontsize=14)
ax1.grid(color='lightgrey')
ax1.set_axisbelow(True)ax2.plot(dev.azi, dev.md, color = "black", marker='.', linewidth=0)
ax2.plot(resampled_dev.azi,resampled_dev.md, color='red')ax2.set_ylim(dev.md[-1], 0)
ax2.set_xlim(0, 360)
ax2.set_xlabel('Azimuth', fontweight='bold', fontsize=14)
ax2.grid(color='lightgrey')
ax2.set_axisbelow(True)plt.show()
当图出现时,我们可以看到原始数据为黑点,重采样数据为红线。总的来说,这看起来不错,并且这些值看起来彼此一致。

原始勘测测量的井眼方位角和倾斜度,以及来自 wellpathpy 的重采样数据。图片由作者提供。
创建位置地块
现在我们已经对数据进行了重新采样,并创建了位置日志,我们可以用下面的代码来可视化我们的位置数据。这允许我们生成三个图:
- 北/南位置与东/西位置(地形图)
- 东西位置随深度的变化
- 北/南位置随深度的变化
fig, ax = plt.subplots(2, 2, figsize=(15,5))ax1 = plt.subplot2grid((1,3), (0,0))
ax2 = plt.subplot2grid((1,3), (0,1))
ax3 = plt.subplot2grid((1,3), (0,2))ax1.plot(pos.easting, pos.northing, color = "black", linewidth=2)ax1.set_xlim(-500, 400)
ax1.set_ylim(-400, 100)
ax1.set_xlabel('West (-) / East (+)', fontweight='bold', fontsize=14)
ax1.set_ylabel('South (-) / North (+)', fontweight='bold', fontsize=14)
ax1.grid(color='lightgrey')
ax1.set_axisbelow(True)ax2.plot(pos.easting, pos.depth, color = "black", linewidth=2)ax2.set_xlim(-500, 400)
ax2.set_ylim(3500, 0)
ax2.set_xlabel('West (-) / East (+)', fontweight='bold', fontsize=14)
ax2.set_ylabel('Depth', fontweight='bold', fontsize=14)
ax2.grid(color='lightgrey')
ax2.set_axisbelow(True)ax3.plot(pos.northing, pos.depth, color = "black", linewidth=2)ax3.set_xlim(-500, 400)
ax3.set_ylim(3500, 0)
ax3.set_xlabel('South (-) / North (+)', fontweight='bold', fontsize=14)
ax3.set_ylabel('Depth', fontweight='bold', fontsize=14)
ax3.grid(color='lightgrey')
ax3.set_axisbelow(True)plt.tight_layout()plt.show()
这产生了下面的图。

从不同角度观察井眼位置,以便更好地了解井眼轨迹。图片由作者提供。
计算 TVDSS
这些计算的一个常见输出是 TVDSS。这为我们提供了基准面(如转盘或钻台)和井筒内某个位置之间的绝对垂直差。
当它被计算时,我们在平均海平面以上有正值,在平均海平面以下有负值。

钻井和测井中关键参考和深度名称的图解。图片由作者提供。
为了计算 TVDSS,我们简单地调用pos.to_tvdss()并传入我们的永久数据。
pos_tvdss = pos.to_tvdss(datum_elevation=30)
运行时,它返回以下带有多个数组的位置对象。第一个标记为深度的代表我们的 TVDSS 数据。

用 Plotly 构建 3D 井眼轨迹图
查看 2D 图上的油井位置可能会有很大的局限性,尤其是当您试图了解井筒的位置和形状时。在本节中,我们将了解如何使用 Python 和创建交互式 3D 绘图
但是,在我们生成图之前,我们可以选择将我们的井筒定位在正确的位置。
设置参考位置
我们的位置对象的当前北距和东距从 0 开始。我们可以通过提供钻机的表面位置来修正该位置。
wellhead_northing = 6478572
wellhead_easting = 435050pos_wellhead = pos.to_wellhead(surface_northing=wellhead_northing,
surface_easting=wellhead_easting)
运行这部分代码后,我们可以检查我们的井筒位置的北距,以查看数据是否已更新。
pos_wellhead.northing

创建 3D 井眼轨迹图
现在,井眼已经参考到正确的位置,我们现在可以制作我们的三维绘图。
为此,我们将依赖于 plotly 而不是 matplotlib。在我看来,与 matplotlib 相比,它生成了更好的交互图。
下面的代码用于生成 3D 绘图。如果你没有安装 plotly ,你可以使用!pip install plotly在 Jupyter 笔记本上安装。
import plotly
import plotly.graph_objs as go# Configure Plotly to be rendered within the notebook
plotly.offline.init_notebook_mode()# Configure the trace.
wellpath = go.Scatter3d(
x=pos_wellhead.easting,
y=pos_wellhead.northing,
z=pos_wellhead.depth,
mode='markers',
marker={
'size': 5,
'opacity': 0.8,
}
)data = [wellpath]fig = go.Figure(data=data)fig.update_layout(scene = dict(
zaxis_autorange="reversed",
xaxis_title='West (-) / East (+) (m)',
yaxis_title='South (-) / North (+) (m)',
zaxis_title='TVD (m)'),
width=800,
margin=dict(r=20, b=10, l=10, t=10))
plotly.offline.iplot(fig)
当上面的代码被执行时,我们生成了下面这个可以移动的图。注意,为了显示 z 轴随着我们的深入而增加的深度,我们需要通过用zaxis_autorange="reversed"更新布局来反转 z 轴。

用 plotly 创建的交互式 3D 井眼轨迹。图片由作者提供。
创建重采样调查数据和位置数据的熊猫数据框架
在 Python 中处理测井数据时,通常会处理数据帧。 wellpathpy 没有提供直接导出到这种格式,但是我们可以很容易地创建一个数据帧,如下所示:
#Create a dictionary of the curve names and the data
data = {'MD':resampled_dev.md,
'AZI':resampled_dev.azi,
'INC':resampled_dev.inc,
'TVD':pos.depth,
'TVDSS':pos_tvdss.depth,
'YLOC':pos.northing,
'XLOC':pos.easting,
'NORTHING': pos_wellhead.northing,
'EASTING': pos_wellhead.easting}df = pd.DataFrame(data)
这将返回以下数据帧,其中包含我们计算的所有位置数据和测量结果。

在 pandas 数据框架内结合油井测量和位置数据。图片由作者提供。
摘要
使用 wellpathpy 库,将原始油井测量数据转换为定期采样的位置测量数据是一个简单的过程。生成数据后,我们可以创建交互式图表,以便更好地可视化和理解井眼轨迹。此外,这种数据现在可以与来自同一口井的测井数据集成,以便具有完整的复合井数据集。
感谢阅读。在你走之前,你一定要订阅我的内容,把我的文章放到你的收件箱里。 你可以在这里做!*或者,您可以* 注册我的简讯 免费获取更多内容,直接发送到您的收件箱。
其次,通过注册会员,你可以获得完整的媒介体验,并支持我和成千上万的其他作家。每月只需花费你 5 美元,你就可以接触到所有精彩的媒体文章,也有机会通过写作赚钱。
如果你用 我的链接 , 报名,你直接用你的一部分费用支持我,不会多花你多少钱。如果你这样做了,非常感谢你的支持!
数据工程中的工作量
原文:https://towardsdatascience.com/workloads-in-data-engineering-4dd10c7a0462

照片由 Maximalfocus 在 Unsplash 上拍摄
数据工程
各种类型的数据工作负载简介
介绍
数据库是存储和处理产品。他们从应用程序中获取数据并存储它们。当您发出查询时,数据库引擎会处理这些查询,并将查询的输出返回给您。应用程序捕获的数据种类以及用户使用数据的方式千差万别。早期,存储和处理工作负载分为两大类:事务性和分析性。最近,数据工程中出现了第三种类型的工作负载— translytical。
本文将研究这三种类型的工作负载——何时使用它们以及它们的定义特征是什么。本文还将带您浏览一些示例和资源,以进一步理解这些工作负载。请记住,这三种工作负载是运行各种数据库、数据仓库、数据湖、数据集市等的基础。,所以对这些有很好的理解是极其重要的。就这样,让我们开始吧。
工作负载的类型
事务性的
传统上称为 OLTP(在线事务处理),这种工作负载在服务于事务性应用程序(如预订系统、购物网站等)的数据库中最为常见。OLTP 工作负载通常需要支持 ACID 属性的系统来保证事务。一些最流行的数据库主要是事务性的,如 MySQL、PostgreSQL、Microsoft SQL Server 和 Oracle。然而,它们中的许多(我们稍后会谈到)也可以支持其他类型的工作负载!
正式或附带特征的组合可以识别事务性工作负载。事务性工作负载—
- 支持 ACID 属性,即,使您能够执行事务
- 将数据写入高度规范化的数据库模式,即 3NF 及更高版本
- 让数据库模式与应用程序特性和用例保持一致
- 当应用程序获取完整的行时,在磁盘上连续存储行
- 不支持大量的分析性临时查询工作负载
谷歌对交易型数据库有如下类似的定义:
事务型数据库是 行存储 ,这意味着数据是以行而不是列的形式存储在磁盘上的。当您需要 了解用户表 中某个客户的所有信息时,行存储非常有用,因为您可以只获取您需要的数据。但是当您试图计算特定邮政编码中的客户数时, 就没那么好了,因为您不仅要加载
*ZIP*列,还要加载*name*、*address*和*user_id*列。
事务数据库通常是单节点的。大规模运营的公司可能有基于集群的事务数据库,但这并不一定意味着事务是分布式的。基于集群的系统中的事务可以很好地在单个节点上执行,因为您已经共享和分区了数据,因此不需要另一个节点上的数据。很少有数据库承诺分布式事务。我最近为 FaunaDB 写了一篇文章,讨论了一些特性。一定要去看看!
分析的
数据的理念是支持企业更好的决策。当您希望从事务性系统和其他系统捕获的数据中获取业务价值时,就需要分析工作负载。这是构成 DSS 或决策支持系统工作负载的数据库和数据仓库类别的基础;OLAP 或在线分析处理是这种情况下的工作负载类型之一。
决策支持系统或分析性工作负载可以通过事务性工作负载的独特的、有时完全相反的特征来识别。分析工作负载—
- 不支持事务或 ACID 属性
- 拥有高度非规范化的数据库模式——星型和雪花型
- 使用基于列的存储,而不是基于行的存储
- 支持临时查询业务工作负载以获取价值
- 在分布式大规模并行处理设置中性能更佳
如果您的起点是几个事务系统,那么您需要执行几个步骤来支持业务的分析工作负载。您需要集成各种数据源,并将获得的数据转换成非常适合列存储和分析工作负载的非规范化数据库模式。还需要选择支持红移、Trino、雪花等负载的数据处理系统。
另外,在我最近为前雇主 Servian 撰写的一篇文章中,我已经相当详细地讨论了存储这个话题。这可能有些用处。
https://servian.dev/ingredients-of-a-data-warehouse-cd68b48f5306
转溶的
在数据工程领域(也许其他领域也是如此)有一种将多种东西整合到一个产品中的趋势。虽然现代数据堆栈中的产品数量与日俱增,但越来越多的工具承诺了越来越多的事情。举个例子,Snowflake 支持事务性工作负载的计划,或者 Redshift 对 Spark 的推动,等等。
顾名思义,Translytical 工作负载或 HTAP(混合事务/分析处理)工作负载也旨在通过支持分析和事务工作负载来实现类似的目标。这是通过在内部支持多个存储和处理框架,并从数据工程师那里抽象出许多实现细节来实现的。混合表就是这样的例子。我最近为 SingleStore 写了一篇文章,其中我谈到了为支持不同工作负载而构建的表,即混合表。
使用 translytical 系统,您可以运行事务性查询(完全支持 ACID)和分析性查询来处理大量数据、进行聚合等等。以下是转溶系统的一些特征:
- 支持 ACID 事务以及 OLAP
- 支持各种数据建模方法
- 通常有基于行和基于列的存储
- 通常支持非 ACID 键值和文档存储
- 致力于一个真正多用途的数据库
最后,我想分享一下对这些不同工作负载的快速观察。公司中的数据团队,尤其是小公司,经常会遇到这样的情况:他们必须快速扩展以支持更多的查询,处理事务性数据库、副本或数据仓库中的大量数据。对此问题的下意识解决方案是在问题上投入更多的计算。它会工作一段时间,直到它需要更多的计算能力。我曾经写过在一个问题上投入更多的计算是不明智的。
结论
HTAP 工作负载的使用量正在上升,而 OLAP 和 OLTP 仍然表现强劲。有如此多的数据、如此多的公司和如此多的数据库产品——数据库市场竞争激烈,尤其是商业级数据库的开源替代产品。许多公司使用专门构建的数据库,因为它们具有针对特定用例的专业和高级功能。其他公司正在尝试服务于 HTAP 空间的产品。
许多人,包括 Forrester 的 2022 年 Translytic 数据平台报告,都指出,尽管有许多好的数据平台可供 HTAP 使用,但在这些平台被认为可以大规模工作之前,仍有许多工作要做,具有透明度、可观察性、可靠性和安全性。即使你不负责选择这些不同的数据库,我建议你知道它们的存在和用途。结果可能是很多公司已经开始使用 HTAP 的数据库。
如果你觉得我的文章有用,请订阅并查看我的文章🌲 Linktree 。你也可以考虑用我的推荐链接购买一个中级会员来支持我。
https://kovidrathee.medium.com/membership
你能运行这个 Docker 图像吗?
原文:https://towardsdatascience.com/would-you-run-this-docker-image-7b52d99e78ec
您如何知道您从 Docker Hub 中提取的 Docker 映像可以安全使用?

由 Timelab Pro 在 Unsplash 上拍摄的照片
Docker Hub 上只有极小一部分公开图片经过了验证和审查。一般来说,Docker Hub 上推送的图片有三种类型:
- 已验证:已验证图像是来自已验证发行商的高质量图像。Docker 团队已经分析了这些图像的安全缺陷。
- 官方:这是一组精选的码头工人图片。这些是高质量的 Docker 图像,但它们没有经过安全审查。因此,尽管它们通常被认为是安全的,但您仍然应该小心漏洞。
- 公开:任何人都可以在 Docker Hub 上发布图片。你永远不要相信未经核实的公众形象,你需要格外小心。
那么,你怎么知道你从 Docker Hub 中提取的 Docker 映像可以安全使用呢?您如何知道映像所依赖的开源软件库已经更新到了最新版本,并且安装了解决已知 CVE(常见漏洞和暴露)的所有安全补丁?
在这个故事中,您将看到如何分析 CVEs 的图像,并在运行它之前检测安全性缺陷。如果您想进入生产阶段,这是至关重要的一步,但即使您只在本地运行一个映像。
Learning Rate 是一份时事通讯,面向那些对 AI 和 MLOps 世界感到好奇的人。你会在每个月的第一个星期六收到我关于最新人工智能新闻和文章的更新和想法。订阅这里!
信任如何扫描 Docker 图像?
如果您是 Docker Hub 上图像的发布者,您可以首先使用 Docker 内容信任对您的 Docker 图像进行签名。如果你拉一个图像,只寻找签名的图像。但是,此过程不会扫描映像内部的漏洞。
那么你能做什么呢?有两个步骤可以遵循。首先,当你构建你的图像时,你应该使用可信方审查过的图像作为你的基本图像。然后,你也应该亲自检查最终的图像,将它分解成不同的图层,并逐一检查。
Docker 图像层由软件材料清单(BOM)组成。这个术语只不过是您的映像用来成功运行的库和依赖项的列表。当您将映像分解成不同的层时,您的目标是在 BOM 的库和包中找到漏洞。
分解物料清单

要分解 BOM,首先需要理解它的内容。因此,BOM 包含两种类型的组件:
- 我们的软件直接用来执行的库
- 安装在我们映像中的库所依赖的其他软件
我们需要列举这两个类别来编辑一个完整的构成我们图像的材料列表。为此,我们将使用 Syft 。
Syft 安装
要安装 Syft,请遵循官方的库文档。例如,Linux 发行版的推荐方式是运行以下命令:
curl -sSfL [https://raw.githubusercontent.com/anchore/syft/main/install.sh](https://raw.githubusercontent.com/anchore/syft/main/install.sh) | sh -s — -b /usr/local/bin
使用 Syft 检查图像
让我们现在拉 Ubuntu 仿生海狸图像,并分解它。首先,让我们下载图像:
docker pull ubuntu:18.04
要查看此映像的 BOM,请运行以下命令:
syft ubuntu:18.04
结果应该呈现出构成 Ubuntu 仿生海狸图像的 89 个包。

Syft 结果—作者图片
所以,这是很好的第一步。我们有图像的 BOM。但是我们如何知道这些包中是否包含任何关键的 CVE 呢?我们应该对照 CVE 的数据库一个一个地检查吗?幸好没有!我们将为此使用另一个工具:让我们使用“grype”。
grype 装置
要安装 grype,请遵循官方的库文档。例如,Linux 发行版的推荐方式是运行以下命令:
curl -sSfL [https://raw.githubusercontent.com/anchore/grype/main/install.sh](https://raw.githubusercontent.com/anchore/grype/main/install.sh) | sh -s — -b /usr/local/bin
检查 CVE 的图像
要检查之前为 CVEs 提取的 Ubuntu Bionic Beaver 图像,只需运行以下命令:
grype ubuntu:18.04

grype 结果—作者图片
幸运的是,今天你不会在那里发现任何高或严重的 CVEs。然而,让我们随机抽取一个公众形象,并对其进行测试:
grype ocdr/pytorchserver:1.9

grype 结果—作者图片
由于它依赖于几个库,例如Pillow和log4j,所以ocdr/pytorchserver:1.9映像包含几个关键的 CVE。既然你知道这个,你会用它吗?
但是,有些 CVE 很容易修复。我们看到 grype 通知我们 Pillow 库引入的所有关键 CVE 都在版本9.0.0中得到修复。所以,我们要做的就是把 Pillow 的版本更新到9.0.0,重新构建镜像。
结论
Docker Hub 上只有极小一部分公开图片经过了验证和审查。在这个故事中,我们看到了如何提取图像,分析其 BoM,并检查其安全性缺陷。
如果您想进入生产环境,这个过程是关键,但即使您只想在本地运行单个映像,也是如此。你怎么能确定它不会伤害你的系统?
在接下来的文章中,我们将以 Docker 和 Kubernetes 安全性来结束。我们将实时监控我们的系统,并在运行时扫描安全事件。
关于作者
我叫迪米特里斯·波罗普洛斯,我是一名为阿里克托工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据运算的帖子,请在 Twitter 上关注我的 Medium 、 LinkedIn 或 @james2pl 。
所表达的观点仅代表我个人,并不代表我的雇主的观点或意见。
通过理解读者的想法写出更好的科学论文
更好地传达你的故事,让人们理解

艾蒂安·吉拉尔代在 Unsplash 上拍摄的照片
作为一名科学家,你经常研究多年却没有发表任何东西。最终,你决定是时候分享你学到的东西了。你面临着一个看似不可能的挑战,那就是把这一大堆课程写在一张纸上。
但是,有没有办法让科技写作的过程变得更容易呢?有没有简化和提高你写作的诀窍?
在这篇文章中,我将分享一些在三个层次上组织你的写作的方法。我们从思考你的关键信息和构建故事开始。在下一个层次,我们用段落来传达你的主要论点。最后,你的句子确保你的读者在正确的时间思考正确的事情。
因此,你的读者会更简单、更快地理解你的故事。你将会有一个更好、更轻松的体验,把你的信息带到纸上,带到世界上。
我想指出的是,下面的大多数方法都是从出版作者和著名编辑马克·布坎南和贾斯汀·穆林斯的伟大科学写作研讨会中得到启发的。
你论文的故事和结构
在写任何东西之前,你都要决定你的论文应该传达哪些要点。通常这是你开发的一种特定方法或你实现的一个重要目标。找出两到三点你希望你的读者从阅读你的论文中带走的东西。你的论文应该仅仅通过在你的读者的大脑中燃烧这些点来优化。
找一个故事,引导无知的读者理解你的要点。在故事过程中,尽量不要让你的读者走神。例如,你不应该通过假定一个先进的专家知识或不断地从一个场景跳到另一个场景来吓跑读者。你的故事应该从基础开始。从那里开始,它应该有一条清晰的路径,朝着你论文的目标前进。让你的读者理解你的主要观点。
你用带有引导性陈述的主题句来概括故事。这些将被用作撰写论文的起点。此外,这些陈述将引导读者阅读你的论文。这些陈述应该向读者提供整体情况。然而,这些不一定要涵盖你的工作的技术细节。这个科学写作的故事主要由四部分组成。引言、方法、结果和结论。
段落的主题句和结构
你的论文被分成多个段落,每个段落有一个特定的观点。这有助于读者逐步理解你的文章。此外,这提供了防止读者迷路的逻辑结构。你的每一个要点都有专门的空间来展开。
每一段都以一个主题句开始,对你的故事进行引导性陈述。这个句子应该包括这个段落将要提出的论点。这有助于读者理解他或她能从段落中得到什么。大脑已经知道它现在应该理解什么陈述。它知道要寻找什么,也就是下一个问题的答案。为什么主题句是真的?
这一段的其余部分作为主题句的解释和讨论。现在应该实现主题句的期望了。这里是你把你的参考作为前提的地方,你应该讨论你的陈述的讨厌的细节。你知道读者在读段落的第二部分时处于哪种状态。读者想知道为什么他或她应该相信你的陈述。
你这样构建你的段落是因为它有助于读者理解你的故事。这有助于理解你想要表达的要点。每个人都有一个慢速和快速工作的大脑。如果你建立了一个清晰的路径,并且没有被任何干扰所混淆,读者可以使用他或她的快速工作的大脑。举例来说,这些中断可能是对读者没有的问题的回答。为了避免这种情况,我们稍后会介绍一种理解和控制读者心理状态的策略。
控制读者思想的句子
你论文的第一稿只是突出了你的主题句。通过将这些内容倒入你的手稿中,你就勾勒出了你论文的故事。在这里,你可以改变你的故事,尝试不同的角度。然而,你不应该把注意力放在句子的美或者像语法这样不相关的事情上。你的初稿的目的是对你的故事有一个感觉,最重要的是,看看它在纸上是否也行得通。偶尔,你可以为每一段的大概内容做笔记。但是不要陷进去。
下一步,你将所有重要的内容添加到你的段落中。此后,每一段都应包含所需的信息,以相信你各自的主题句。你还不用把信息联系起来。你甚至不需要写出好句子。现在把所有东西都放好。我把这称为写论文的最后研究阶段。如果你对一些论点缺乏证据,现在是做艰苦工作的时候了。找到并记录下来。
我们暂时中断节目,以便对句子有所了解。请享用。我们随时都会回到写作过程。
一个句子的目的是传达一种思想。准确地说,理想情况下是一个完整的思想单元。是什么意思?一个句子总是带有作者思想的一部分。如果这个思想不需要额外的背景就能被理解,我们称之为完整的思想。另外,如果我们不能把它进一步分成多个完整的单元,我们称它为一个单元。换句话说,一个句子应该把一个想法的最小的可理解的版本从作者传递给读者。一句话永远不要试图一次传递一个不完整的想法或多个想法。消息不会如期到达。
要理解一种思想,读者必须知道它是关于什么的。如果你在交谈中解释一个概念,你可以很容易地检查你的对手是否知道你在想什么。如果你思考两件不同的事情,你的同伴不会理解你的想法。在这种情况下,你通常会通过重申你所说的来澄清任何困惑。然而,你不能在书面的句子中消除任何混淆。因此,你必须确保读者在阅读你的句子时考虑的是正确的事情。
你可以通过使用简单的主动句来控制读者的想法。这样一个句子的基本结构包含三个部分。一个行动者(主体),行动者(动词)和接受者(客体)。读者总是会想到一个句子的行为者。所以,要让自己清楚读者应该怎么想,并以此作为行动者。通过在连续的句子中改变演员,你可以转移读者的注意力。严格地做到这一点是安全地向读者传递你思想的精确副本的唯一方法。
你的读者只能一个接一个地理解思想。那是因为我们,人类,主要使用我们运转缓慢的大脑来理解新概念。而迟钝的大脑是串联工作的。因此,读者不能同时理解多个新概念。尽管通常多种想法是相互关联的,但你必须一个接一个地解释它们。你必须把各自的想法串联起来,而不是平行的。如果你把它们平行排列,读者会丢掉其中一个想法,甚至更糟,两个都丢掉。
你的读者只能用 7 块或更少的信息来处理一个想法。举个例子,试着记住下面的随机数列表:3141592|839。在该线之后,即长度为 7 时,它会变得更硬。这里,一个数字对应一个信息块。这同样适用于传达思想。超过 7 个词块的想法是非常难以理解的。读者在到达句子末尾之前会忘记句子或思想的开头。
这取决于读者先前对一段信息的理解。一个组块并不总是恰好是一个单词或数字。例如,一个数学呆子可能知道数字 pi=3.141592。对于这个人,上面的 10 个数字的列表将只包含 4 个而不是 10 个组块。这个人可以很容易地记住这个名单。你必须时刻意识到你的读者的知识,并调整你的句子。这就保证了读者能够理解你的句子并理解你的想法。
你可以通过使用重新编码的方法来传达复杂的思想。复杂思维是指超过 7 个信息块的思维。这不是一句话可以解释的。然而,你可以让读者准备好接受这样一个复杂的思想。你只需要把它分成多个更小的想法,每个想法传达一个所需组块的子集。通过彻底解释这样一个更小的思想,读者可以在未来将其作为一个单独的块来访问。换句话说,来自较小思想的多个组块被重新编码成单个组块。现在你可以用单个词块在一个句子中表达复杂的思想。
现在回到我们关于写作过程的节目。我们结束吧。
写论文的倒数第二步是根据句子的规律编辑你的段落。注意段落的大致结构。它有一个主题句和一组句子,让读者相信主题句。你现在的任务是找到一系列好的句子,向你的读者传达所需的思想。这个过程要慢慢来。最后,你的读者应该能够理解每一段,而不必多次阅读。
一篇好的论文基本上是用重新编码的方法一步步教育读者。第一层(句子)将已知单词重新编码成思想和新概念。第二层(段落)将多个概念重新编码成单个语句,即主题句。最后一层(整篇论文)使用故事将主题句重新编码成论文的一条信息。一篇好的论文在每一个记录层面上都是成功的。
迭代编辑让你的作品熠熠生辉
最后一步是在每个层面(句子、段落、故事)上频繁地编辑你的论文,并一点一点地改进它。这一步造就了伟大的作品。正如马克·布坎南(Mark Buchanan)所说:“通过反复的编辑过程,你的写作变得比你自己更好。“也就是说,你论文的第一版不一定要光彩照人。开始就好,之后再改进。在你出现拼写错误之前,去掉纸上的白色;)
如果你想了解更多,给这些人一个机会,并考虑为你和你的团队预订他们的研讨会。他们提供更多的细节、例子、练习,甚至是对你写作的反馈。来个看看。
如果你喜欢这篇文章,并能学到一些东西,你可能也会喜欢我未来关于科学工作及其思考的文章。请随时关注并加入我的旅程。它对我来说意味着整个世界(微笑)。
使用 Scikit-Learn 用不到 50 行代码编写你的第一个机器学习程序
利用机器学习的力量来分析巧克力饼干!

由 Unsplash 上的 Arseny Togulev 拍摄
机器学习在世界上日益无处不在,这使得它看起来像是一种没有数学和计算机科学的透彻知识就不可能理解和实现的技术。然而,事实远非如此。在当今世界,顶级公司都是在车库中建立起来的,FOSS(自由和开源软件)随处可见,有几个社区建立的库可以简化开发机器学习模型的过程。
什么是 Scikit-Learn?
Scikit-learn 是 Python 编程语言的机器学习库。它建立在几个 Python 库之上,包括 NumPy(数学函数)、SciPy(更多数学!),以及 Matplotlib(数据可视化)。
如果你已经熟悉机器学习领域的一些术语,你可能会问为什么我们不使用 TensorFlow(由谷歌开发)。虽然 TensorFlow 也是一个机器学习库,但它主要专注于深度学习和神经网络,而 scikit-learn 包含更通用的机器学习概念。Scikit-learn 也被广泛认为比 TensorFlow 对初学者来说更容易。
摩根大通和 Spotify 等公司使用 scikit-learn 进行预测分析或歌曲推荐等任务。你可以在这里看到完整的证词列表。
入门指南
对于本教程,您需要:
- Python(版本 3.7 或更高)-推荐基本体验
(安装教程:https://www . tutorialspoint . com/how-to-install-python-in-windows)
然后,在您的终端中使用pip安装三个软件包:
pip install notebookpip install numpypip install scikit-learn
在你的终端中运行jupyter notebook。您的默认 web 浏览器应该会打开一个选项卡,您可以在其中看到文件资源管理器。只需转到您希望创建程序的目录,然后创建一个 Python 3 笔记本(选择右上角的new)。您现在应该会看到这个屏幕:

你可以通过点击“无标题”来重命名文件
代码时间!
完成写入后,按下屏幕顶部的Run按钮运行每个单元格。
在第一个单元格中,首先导入我们需要的库:
现在,该是我们训练模型的数据时间了。假设我们去一家饼干店,根据人们尝试的饼干对他们进行调查:
花点时间看看这些数据(注意顶部的注释)。作为人类,我们很快识别出一种模式:甜饼干是好的,苦饼干是坏的。这个简单的结论就是我们将训练我们的模型去识别的。
接下来,我们定义数据的特征和标签:
这是不言自明的——我们训练模型的特征是饼干是甜的还是苦的,标签是饼干是好还是不好。
按下alt + enter创建一个新的单元格。
现在,我们需要开发一个测试集来测试我们的模型。这将让我们了解模型的精确度。
按alt + enter创建一个新的单元格。
神经网络
我们将使用 scikit-learn 的 MLPClassifier 作为我们的模型。MLP 简单地(或者不那么简单地)代表多层感知器。不严格地说,多层感知器是一种前馈人工神经网络(其中输入和输出为 0 或 1):

鸣谢:图片由作者提供。
“神经网络”不是巧合——神经网络中的节点(或神经元)类似于人脑中的神经元。如果神经元受到足够的刺激,它们就会被触发。
隐藏层是奇迹发生的地方(之所以称为“隐藏”是因为在网络之外无法看到):
隐藏层中的每个神经元都有一个权重来反映其输入的重要性。例如,如果我们要在数据中增加更多的因素,而不仅仅是(甜或苦),模型会对这些特征中的每一个施加权重。虽然咸味和甜味都有助于制作好的饼干,但咸味的权重(即 0.2 倍)可能小于甜味的权重(即 0.5 倍),因为模型发现甜味更重要。
每个神经元也有一个偏差(一个常数),通过增加或减少该偏差来抵消神经元的结果。对于本教程来说有点太复杂了,所以我们将跳过细节。
我们将创建的神经网络不是深度学习网络。具有足够的隐层(超过三个 3 是普遍接受的数字),一个神经网络被定义为深度神经网络。深度神经网络不需要标记数据。例如,一个经典的神经网络需要人为干预来标记数据集,正如我们在标记哪些 cookies 是好的,哪些不是时所做的那样——这就是所谓的监督学习。另一方面,深度神经网络执行无监督学习。他们可以利用未标记的数据,并根据它自己确定的特征将其分组到不同的组中。
最后,代码(我知道这并不令人印象深刻)。我们简单地定义我们的隐藏层将有 5 层,我们将通过我们的数据循环 3000 次。
按alt + enter创建一个新的单元格。
现在,我们将根据我们提供的数据来拟合或训练模型。我们的模型将遍历数据 3000 次(意味着它完成了 3000 个时期),正如我们创建网络时所定义的那样。
然后,我们将使用它开发的权重和偏差,对照训练和测试集来测试我们的模型。
输出:
Training set score: 100.000%
Testing set score: 100.000%
按alt + enter创建一个新的单元格。
最后,我们可以使用我们经过试验和测试的模型来确定 cookie 是否是好的。这是我用来测试的一些代码:
还记得我们从数据中得出的结论吗?我们的模型成功地匹配了我们的想法!
Type: Sweet cookie
Good cookie!
Type: Bitter cookie
Bad cookie!
结论
当然,我们在这个模型中使用的数据不需要神经网络——但它是一个简单数据的简单实现,因此我们可以更多地关注机器学习如何工作。当然,可以根据自己的喜好随意更新 cookie 调查和测试集!添加更多特性(松脆度、咸味等)并对模型进行测试!
感谢您的阅读!我希望你非常喜欢这个教程,并且现在对机器学习的想法更加适应了。
写数据文章就像盖房子
原文:https://towardsdatascience.com/writing-a-data-article-is-like-building-a-house-755fc267760a
技术写作
我构建可靠数据故事的秘诀
当你内心深处知道你想谈论一个技术话题,但你真的不知道如何在一篇文章中正确对待它时,你知道这种感觉吗?
找到合适的角度来传达正确的信息有时会很棘手。尤其是在处理数据科学或数据分析等技术主题时。如果你再加上为你的文章找到一个逻辑结构并相应地举例说明的挑战,那么要写出一篇好文章可能是一场真正的战斗。
首先,作为一名数据从业者,您为什么会对撰写技术文章感兴趣?第一个好处是清晰地表达你的想法。作为一名数据分析师,在线写作帮助我记录日常生活中应用的最佳实践。有时,当我想向一位同事解释一个概念时,我会参考我的一篇文章:一旦你完成了写下你的专业知识的某些部分的工作,以后就更容易参考它们。还有其他原因可以让你写一些与你的数据工作不直接相关的技术文章。当你更深入地探索一个话题时,你会学到新的东西。写作也是提高你创造力的一种锻炼——也可能提高你的自信心。
每个作家都会找到自己写技术文章的理由。在撰写了与数据相关的文章之后——其中大部分发表在面向数据科学的出版物上——我想与您分享我的个人建议,以阐明一篇关于数据的文章,这样您就可以享受技术写作的好处。
除了建造房屋,你还必须具备:
- 坚实的基础
- 精心设计的结构
- 漂亮的室内装饰

照片由 Avel Chuklanov 在 Unsplash 上拍摄
#1 基金会
不管你谈论的主题是什么,一个好的介绍都是以一句流行语开始的。短短几句话,读者就必须明白这篇文章的内容。他们必须找到进一步阅读你的文章的理由。
最好的方法是找到你的文章正在解决的痛点。基于你如何处理这个主题,你提出了一个解决读者痛点的方案。作为一名作家,你并不总是很容易将文章的主题与痛点联系起来,但我发现写一个吸引人的引言非常有用——甚至在你选择最好的吸引人的角度之前写几个草稿。
以下是一些棘手问题的示例:
- 你想让解决一个技术概念并向你的读者详细解释它吗?
→ 解决的痛点可能是:“如果你对这个概念感到困惑,或者你从未听说过它,请进一步阅读我的文章,你会对它有一个清晰的了解”
- 要不要通过方法论去做一件事?
→ 解决的难点可能是:“如果你不知道如何做到这一点,进一步阅读我的文章,你将能够自己做到这一点”
- 你想分享关于主题的最佳实践吗?
→ 解决的难点可以是:“您知道您可以做得更好吗?你可能做错了,所以继续往下读我的文章来改进你的练习吧
最重要的是,你应该从一开始就牵着读者的手。特别是当你处理一个技术话题时,定义整篇文章中使用的术语是至关重要的。如果术语之间需要添加任何细微差别,那么文章的引言就是添加的地方。
例如,下面这篇关于 SQL 的文章面向技术和非技术读者。这就是为什么我在文章的开始部分对 SQL 的含义进行了适当的解释。
💔-reasons-why-you-should-learn-sql-even-if-you-are-not-part-of-a-tech-team-232be317b9d7>
#2 结构
我通常把我的内容分成三个部分——这绝对是我学习的遗产。但是没有什么能阻止你创造一个完全创新的结构。对我来说,三部分结构可以让我足够详细地阐述我的想法,这样我就可以在几个段落中阐述同一个想法,同时我希望一篇文章保持相对紧凑,不要太长。所以三个细节部分通常就能解决问题。
至于组织文章的最佳方式,在我作为技术写作者的经历中,我已经确定了一些模式。根据你想通过文章传达的信息和你想与读者建立的关系,文章结构的一种变化可能比其他变化更相关。
变式 A:你想为一个给定的陈述提供理由

作者图片
在这类文章中,你应该关注你的陈述背后的论证。要做到这一点,介绍应该用来设置场景。这会让读者理解你的论点发生的背景。
在你文章的主体中,每一个论点都应该有例子支持。这里你可以举一个例子并在你文章的每一部分中提到它,或者用一个具体的例子来支持每一段。例子会加强你的说法,让读者理解你每个论点的意思。
最后,结论应该包括对你所有论点的概述,还应该包括可能的反驳论点和你刚刚提出的案例的局限性。
这是我在本文中使用的结构类型,我首先阐述我的观点,然后用一个详细的例子详细说明它:
变体 B:你想向你的读者解释一个概念

作者图片
在这种类型的文章中,所有的教学技巧都是必需的,因为文章的目标是使一个复杂的概念易于理解。你甚至希望你的读者在所呈现的概念上变得知识渊博。为此,我使用的一个技巧是将我的内容组织成问题和答案。这加强了这种感觉:在教室里,有一位专注的老师会仔细检查学生可能有的每一个问题。
因此,一个好的引言包括你文章中要用到的术语的定义。即使您将在文章正文的后面进一步详细介绍,从定义开始会让读者有信心详细解释每个元素。
这类文章的一个关键要素是使用图形元素来解释你的概念。对于视觉记忆力强的读者来说,这可以帮助他们理解一些复杂的概念。
这就是我在本文中使用的结构类型,在本文中,我使用自己创建的图表解释了现代数据堆栈的概念:
变体 C:你想教你的读者如何做某事

作者图片
这是典型的教程文章。在这种结构中,在整篇文章中引用一个例子尤为重要。这可以在引言或文章的第一部分完成。
在你展示完例子后,你将牵着读者的手带他们经历问题解决方案的每一步。因此,我喜欢坚持对步骤和段落进行编号,以使教程更容易理解。
最后,没有什么比代码片段和截图更能让每个动作清晰明了。对你解决引言中暴露的问题的方式保持透明会让你的读者信任你:如果他们能重复你所经历的步骤,你的教程很有可能是有效的。
这是我在本文中应用的结构类型,在本文中,我解释了我在标准 SQL 中处理空值的方法:
#3 装修
现在你的房子(或者我应该说,你的文章)的主要部分已经建成,是时候给它添加装饰元素了。
在这里我想为你的文章谈谈图片和插图。人们常说“一图胜千言”……我必须承认,我觉得这句话非常贴切。要对你的读者产生影响,没有什么比一幅合适的插图更好的了。有两种方法可以做到这一点:要么你自己创作插图,要么你从互联网上下载——或者你把两种方法结合起来。如果你没有创作自己的插图,请确保你有权利分享他人的作品。
最后但同样重要的是:你文章的整体外观。我的意思是,在发表一篇文章之前,你应该检查以下几点:
- 审美:图片与文字之间,文字段落本身之间的和谐等等。
- 正确性:没有拼写错误,数字正确,代码段正确等。
- 详尽无遗:确保你解决了引言中提到的所有话题
…然后您就可以点击“发布”按钮了!
结论
有了坚实的基础(找到你的文章要解决的痛点)精心设计的结构(根据你文章的目的选择)一些内部装饰的好元素(你文章的插图和视觉一致性),你的技术文章现在就可以与读者分享了。
你写过数据相关话题的文章吗?你是怎么想出一个结构的?你还想分享什么建议?我很乐意了解其他作者是如何写出技术文章的!
你喜欢读这篇文章吗? 成为 的一员,加入一个不断成长的充满好奇心的社区吧!
https://marie-lefevre.medium.com/membership
为我的数据框包编写合并函数
原文:https://towardsdatascience.com/writing-a-merge-function-for-my-data-frames-package-f492e313d02b
OddFrames.jl
为我的数据框包编写合并函数
为我的包 OddFrames.jl 创建两种不同类型的合并。
介绍
数据管理框架通常因其方法和功能而备受推崇。这可能是因为这些方法可能很难构建,但也非常方便。关于移动和操作数据有很多东西要学,做这样一个项目的好处是我可以尝试创建自己的算法。
对于那些不熟悉我的项目 OddFrames.jl 的人来说,这是一个用于 Julia 中数据管理的选择性面向对象包。当然,这个软件包的应用主要是基于科学计算。也就是说,这肯定是一个有趣又酷的项目。如果你想查看这个项目的 Github,你可以在这里:
https://github.com/ChifiSource/OddFrames.jl
今天,我们将加入一些新的功能,使我们能够轻松地将奇数帧组合在一起。我有几个有趣的计划来解决这个问题,我计划用函数做一些很酷的调度,我知道一个合并函数将会非常困难,所以不再多说,让我们开始写这些函数吧!最后一件事,如果你想更深入地了解 OddFrames.jl,这里是我写的关于这个包的前一篇文章,我在其中写了一个 CSV 解析器:
修改构造函数
由于有如此多的构造函数,我将把本文示例的重点放在 pairs 的分派上:
function OddFrame(p::Pair ...)
不过,在我们深入研究这个函数之前,让我们快速看一下外部构造函数,了解一下上下文。
mutable struct OddFrame <: AbstractMutableOddFrame
labels::Array{Symbol}
columns::Array{Any}
types::Array
head::Function
drop::Function
dropna::Function
dtype::Function
merge!::Function function OddFrame(p::Pair ...)
任何时候我们看到这样的外部构造函数,在内部构造函数中有不同的调用,这意味着我们很可能期望从函数中返回外部构造函数内部的数据。换句话说,OddFrame(::Pair)只是一组命令,在获取数据的算法完成后,这些命令将调用 OddFrame(::Array,::Array,…)。这个函数的第一部分获取我们的三个数组、标签、列和类型。
function OddFrame(p::Pair ...)
# TODO Would be nice to combine these loops:
labels = [x[1] for x in p]
columns = [x[2] for x in p]
length_check(columns)
name_check(labels)
types = [typeof(x[1]) for x in columns]
# Head
#== TODO, maybe the best approach to solving
the TODO outlined in member_func : 35 is to check
REPL or otherwise here?==#
之后,定义了这个新构造类型 OddFrame 的子类型的函数调用。这些调用来自 member_funcs.jl 文件的函数,我们很快也会用到这个文件。
head(x::Int64) = _head(labels, columns, types, x, )
head() = _head(labels, columns, types, 5)
# Drop
drop(x) = _drop(x, columns)
drop(x::Symbol) = _drop(x, labels, columns, coldata)
drop(x::String) = _drop(Symbol(x), labels, columns, coldata)
# Dropna
dropna() = _dropna(columns)
# Dtype
dtype(x::Symbol) = typeof(coldata[findall(x->x == x,
labels)[1]][1])
dtype(x::Symbol, y::Type) = _dtype(columns[findall(x->x == x,
labels)[1]], y)
这是构造函数中我要添加代码的部分。我将简单地为一个名为 merge 的新功能添加一些调度功能!().你可能会注意到这里合并!()有一个解释点,表明这个方法将改变它所调用的类型。在这个例子中,我们需要一个 OddFrame 来合并 OddFrame。我们的方法也需要来自当前 OddFrame 的数据。这是我想到的:
# Merge
merge_def = labels[length(labels)]
merge!(od::OddFrame; at::Symbol = merge_def) = _merge!(labels,
columns, od, at)
merge!(x::Array; at::Symbol = merge_def) = _merge!(labels,
columns, od, at) merge_defi = length(labels) merge!(od::OddFrame; at::Int64 = merge_defi) = _merge!(labels,
columns, od, at)
merge!(x::Array, at::Int64 = merge_defi) = _merge!(lables,
columns, od, at)
前两个合并是针对作为符号的 at 关键字参数,后两个合并是针对作为整数的 at 关键字参数。我们实际上可以通过将默认的关键字参数赋予别名来改变它,例如 merge_def,这种方法的唯一问题是函数永远不会被重新定义。换句话说,在这里使用默认值可能更好,并使用条件,因为 OddFrame 的长度是可变的,所以如果我们删除了一些列,现在我们的长度只有 2,我们不希望设置索引为 5。让我们改变这一点:
# Merge
merge!(od::OddFrame; at::Symbol = :end) = _merge!(labels,
columns, od, at)
merge!(x::Array; at::Symbol = :end) = _merge!(labels,
columns, od, at)
merge_defi = length(labels)
merge!(od::OddFrame; at::Int64 = 0) = _merge!(labels,
columns, od, at)
merge!(x::Array, at::Int64 = 0) = _merge!(lables,
columns, od, at)
现在我们要做的就是使用 new()函数包装这个类型:
# type
new(labels, columns, types, head, drop, dropna, dtype);
合并方法
在我们开始写合并之前!()方法,我们先写 merge()。与我们刚刚写的两个函数相反,这两个函数将调用 _merge!()在 member_funcs.jl 中,我们现在在 methods.jl 中工作,我准备写一个方法,用非变异的方式合并两个 OddFrame,返回一个新的 odd frame。存储在 methods.jl 和 member_funcs.jl 中的函数之间的最大区别是成员函数是该类型的子类型,这通常意味着它们将使用我们的数组,而方法通常将使用这些 member_funcs 与索引和所有其他 frame 属性一前一后地为 API 做出贡献。也就是说,我们在这里的论点可能只是两个奇怪的框架。我还将继续下去,把标签也拿出来:
function merge(od::AbstractMutableOddFrame,
od2::AbstractOddFrame; at::Int64 = width(od))
# f = od, s = od2
flabels = od.labels
slabels = od2.labelsend
现在,我们将使用 width(od)来填充 at 调用,这将被限制为表示轴的索引。一旦进入函数,我们就可以继续进行一些初始化。
pairs = []
我们将简单地创建一组新的对,然后将它们直接发送回 OddFrame 构造函数,并返回一个新的 OddFrame。如果我们的 OddFrame 的维度有效,我还将添加一个 throw,例如,我们有一个 3 列的 OddFrame,但有人试图在 5 列进行合并。
if at > width(od) || at < 1
throw(BoundsError("Merge position is not an index on this OddFrame!"))
end
我们将需要在随后的 for 循环中使用 enumerate()方法来枚举我们的索引,
for (n, col) in enumerate(od.labels)end
现在我想到了,我们甚至真的不需要 od.labels,我们可以只使用我们的 od 的宽度()。
for n in 1:width(od)
现在我将有一个嵌套的 for 循环,如果值等于 at,它将填充第二个 Oddframe 的对。
function merge(od::AbstractMutableOddFrame,
od2::AbstractOddFrame; at::Int64 = width(od))
if at > width(od) || at < 1
throw(BoundsError("Merge position is not an index on this OddFrame!"))
end
pairs = []
for n in 1:width(od)
if n == at
for col in width(od2)
push!(pairs, )
end
end
push!(pairs, od.labels[n], od.columns[n])
end
end
现在让我快速总结一下这个函数:
function merge(od::AbstractOddFrame,
od2::AbstractOddFrame; at::Int64 = width(od))
if at > width(od) || at < 1
throw(BoundsError("Merge position is not an index on this OddFrame!"))
end
pairs = []
for n in 1:width(od)
if n == at
for n in 1:width(od2)
push!(pairs, od2.labels[n] => od2.columns[n])
end
end
push!(pairs, od.labels[n] => od.columns[n])
end
return(OddFrame(pairs))
end
我还意识到不可变和可变类型都可以通过这一点,所以我改变了一点。现在,让我们打开一个笔记本,尝试这段代码。
include("../src/OddFrames.jl")
using Main.OddFramesarr = [5, 10, 15, 20, 25]
od = OddFrame(:A => arr, :B => arr)
od2 = OddFrame(:Z => arr, :B => arr) od3 = Main.OddFrames.merge(od, od2)Column names may not be duplicated!
Stacktrace:
[1] (::Main.OddFrames.var"#name_check#75")(labels::Vector{Symbol})
@ Main.OddFrames ~/dev/OddFrames.jl/src/type/frame.jl:125
在未来,我认为这可能是有意义的改变,只是用一个数字或什么来修改标签,然而这只是一个简单的用法错误,我在我的 OddFrames 上使用了相同的符号两列。
od2 = OddFrame(:Z => arr, :W => arr)od3 = Main.OddFrames.merge(od, od2)UndefVarError: coldata not defined
我的天啊。
被调用的构造函数似乎过时了,因为现在 coldata 只在打印头时被引用,数组类型用于保存数组。很可能我们在这里调用 OddFrame(::Array{Pair})而不是调用 OddFrame(::Pair…),这并不是一件坏事,只是对于我正在开发的这个版本,我主要关注的是保持一个构造函数最新,直到 API 相对健壮。让我们继续看一看产生这个错误的构造函数:
function OddFrame(p::AbstractVector)
# Labels/Columns
labels, columns = map(x->x[1], p), map(x->x[2], p)
length_check(columns)
name_check(labels)
types = [typeof(x[1]) for x in columns]
# Head
head(x::Int64) = _head(labels, columns, types, x)
head() = _head(labels, columns, types, 5)
# Drop
drop(x) = _drop(x, columns)
drop(x::Symbol) = _drop(x, labels, columns, coldata)
drop(x::String) = _drop(Symbol(x), labels, columns, coldata)
dropna() = _dropna(columns)
dtype(x::Symbol) = typeof(coldata[findall(x->x == x,
labels)[1]][1])
dtype(x::Symbol, y::Type) = _dtype(columns[findall(x->x == x,
labels)[1]], y)
# type
new(labels, columns, coldata, head, drop, dropna, dtype);
end
坚持住。
我真的这么生气吗?所以这很奇怪。您可能已经注意到,在我展示的第一个构造函数中有一个 TODO,
# TODO Would be nice to combine these loops:
labels = [x[1] for x in p]
columns = [x[2] for x in p]
虽然在这个构造函数中可能没有做到这一点,但我认为这个 map()语法更好:
labels, columns = map(x->x[1], p), map(x->x[2], p)
然而,这种语法有一个问题——它将返回一个元组,因为 map()将保留这些类型,所以我们需要将其更改为其他构造函数所具有的类型。最后,我将更改真正导致我们错误的东西,即 new()调用:
new(labels, columns, coldata, head, drop, dropna, dtype);new(labels, columns, types, head, drop, dropna, dtype);
我可能想做的另一件事是提取更多的这些构造函数,在这一点上重复这么多行是没有意义的,函数定义当然可以从这些构造函数中提取——但是现在,让我们回来测试一下!
od3 = Main.OddFrames.merge(od, od2)OddFrame([:A, :Z, :W, :B], Any[[5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25]], DataType[Int64, Int64, Int64, Int64], Main.OddFrames.var"#head#66"{Vector{DataType}, Vector{Vector{Int64}}, Vector{Symbol}}(DataType[Int64, Int64, Int64, Int64], [[5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25]], [:A, :Z, :W, :B]), Main.OddFrames.var"#drop#67"{Vector{Vector{Int64}}, Vector{Symbol}}([[5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25]], [:A, :Z, :W, :B]), Main.OddFrames.var"#dropna#68"{Vector{Vector{Int64}}}([[5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25]]), Main.OddFrames.var"#dtype#69"{Vector{Vector{Int64}}, Vector{Symbol}}([[5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25]], [:A, :Z, :W, :B]), #undef)
列表底部的未定义是因为合并!()函数尚未在此构造函数中定义。现在让我们来看看这个奇怪的框架:
od3.head()

(图片由作者提供)
嘿,成功了!
现在让我们通过在索引 1 处合并来检查 at 关键字参数:
od3 = Main.OddFrames.merge(od, od2, at = 1)
od3.head()

(图片由作者提供)
然而,我们需要做一点小小的调整,这真的很简单。在 merge()方法中,我们需要给 at 参数加 1,使它的宽度为 OddFrame + 1,因为在第一个示例中,现在它将它们全部放在索引 2 处:
function merge(od::AbstractOddFrame,
od2::AbstractOddFrame; at::Int64 = width(od) + 1)
让我们快速复制这个函数并稍微修改一下,以便为 at 关键字参数提供调度:
function merge(od::AbstractOddFrame,
od2::AbstractOddFrame; at::Symbol = od.labels[width(od)])
pairs = []
for n in 1:width(od)
if n == at
for n in 1:width(od2)
push!(pairs, od2.labels[n] => od2.columns[n])
end
end
push!(pairs, od.labels[n] => od.columns[n])
end
return(OddFrame(pairs))
end
我们只需要将条件改为询问 labels[n]是否等于 at:
function merge(od::AbstractOddFrame,
od2::AbstractOddFrame; at::Symbol = od.labels[width(od)])
pairs = []
for n in 1:width(od)
if od.labels[n] == at
for n in 1:width(od2)
push!(pairs, od2.labels[n] => od2.columns[n])
end
end
push!(pairs, od.labels[n] => od.columns[n])
end
return(OddFrame(pairs))
end
变异函数
现在是艰难的时候了。我们需要用多个调度调用来创建一个类似的成员函数来合并我们的 OddFrames。这是备忘单:
merge!(od::OddFrame; at::Symbol = :end) = _merge!(labels,
columns, od, at)
merge!(x::Array; at::Symbol = :end) = _merge!(labels,
columns, od, at)
merge_defi = length(labels)
merge!(od::OddFrame; at::Int64 = 0) = _merge!(labels,
columns, od, at)
merge!(x::Array; at::Int64 = 0) = _merge!(labels,
columns, od, at)
我们基本上需要为每次做函数 _merge!()上面叫。因为关键字参数是由我们的成员函数 OddFrame.merge 处理的!(),我们可能应该对 at 使用位置参数,这样更快更容易。我在这里也改变了我的想法,虽然我们对符号使用了 dispatch,上次也是这样,但这次我们只是将值设置为等于 labels 数组中给定符号的索引。
merge!(od::OddFrame; at::Any = 0) = _merge!(labels,
columns, od, at)
merge!(x::Array; at::Any = 0) = _merge!(labels,
columns, x, at)
我在最初的调用中也犯了一些愚蠢的错误,我用这些替换了它们。因此,以下是匹配方法:
function _merge!(labels::Array{Symbol}, columns::Array{Any},
od::AbstractOddFrame, at::Any)end
function _merge!(labels::Array{Symbol}, columns::Array{Any},
x::Array, at::Any)end
第一个更难,因为它需要我们遍历整个 OddFrame,所以让我们先来看看数组版本。我要做的第一件事是确保我们的 at 参数是一个整数。我们将使用 findall()获取该元素的位置,如果它是:
if typeof(at) == Symbol
at = findall(x->x==at, labels)[1]
end
我还将抛出 length_check(),如果数组的维数不同,这个函数将抛出:
length_check([x, columns[1])
现在我们将使用大量的推力!().如果你想阅读关于推的完整指南,推在朱莉娅中是相当重要的!(),以及这种非常通用的方法的所有细微差别,我实际上已经写了一整篇文章,您可以在这里阅读:
如果我没有一个想法的话,接下来的部分会非常困难。我将扩展一些 Julia 的基本方法来处理一些我需要的语法。另一种方法是在将来我需要的时候再做,这可能会很多,所以我认为这肯定是一个好主意。我要创造一个新的推动!()变异方法。
这里的问题是,由于我们处于突变模式,我们必须改变这些类型。否则,该函数将需要一个适当的返回——这是我们不想要的,因为它只是为了改变类型本身。这就是我在讨论允许程序员更容易地管理不变性和可变性时所讨论的,这是该包的一个关键焦点——因此,重要的是,无论何时某个东西应该变异,它都应该变异,无论何时它不应该变异,它都不应该变异。将来,很可能大多数可变版本的方法,比如我们前面写的那个,将会使用一个又一个副本。目前,我的重点是获得一个足够好的内核来考虑这个工作接口,实际上我们已经非常接近了!
我实际上为此创建了一个新文件,该文件中所有代码的重点是扩展基本 Julia 类型的功能,这是该包的另一个关键特性 OddFrame 中没有意外和专有的构造数据类型,只有 Julian 数据结构。因为我们可以扩展它们中的任何一个来使用我们的方法,所以真的没有必要创建新的类型。我做的文件叫interface/basetools.jl。我添加的第一个方法将是一个新的推送!()绑定,它将接受一个 iterable、一个值和一个 at 关键字参数。这样,如果我想添加 at = _,它总是在那里,但不会干扰定期推送!(),因此我可以导出它。
function push!(iter::AbstractVector, val::Any; at::Int64 = length(iter))
现在我将按范围索引 iter 向量,并获取索引前后的所有值。我将对索引后面的值做同样的处理:
begin_vec = iter[1:at - 1]
n_vec = iter[at:length(iter)]
现在我们将使用 deleteat 清除数组!(),这样我们就可以推了!()这些值随后返回。
# Clears Array
deleteat!(iter, 1:length(iter))
然后,最后:
[push!(iter, v) for v in begin_vec]
push!(iter, val)
[push!(iter, v) for v in n_vec]
总的来说,
import Base: push!function push!(iter::AbstractVector, val::Any; at::Int64 = length(iter))
begin_vec = iter[1:at - 1]
n_vec = iter[at:length(iter)]
# Clears Array
deleteat!(iter, 1:length(iter))
[push!(iter, v) for v in begin_vec]
push!(iter, val)
[push!(iter, v) for v in n_vec]
iter
end
现在让我们回到我们的合并!()函数这其实是为了和简单地使用 push!()将数组的值放入各自的索引中。
function _merge!(labels::Array{Symbol}, columns::Array{Any},
x::Array, at::Any)
if typeof(at) == Symbol
at = findall(x->x==at, labels)[1]
end
length_check([x, columns[1]])
push!(labels, Symbol(at), at = at)
push!(columns, x, at = at)
end
简单。
最后,我们只需要为 OddFrame 编写相同的代码。
function _merge!(labels::Array{Symbol}, columns::Array{Any},
od::AbstractOddFrame, at::Any)
if typeof(at) == Symbol
at = findall(x->x==at, labels)[1]
end
for (n, val) in enumerate(names(od::AbstractOddFrame))
push!(labels, val, at = at)
push!(columns, od[val], at = at)
at += 1
end
end
这里很容易理解,我们只是迭代地推进,并在每个循环中添加 at。我们使用 names(od),这只是一个绑定到 od.labels 的方法,然后我们使用名称进行索引,我们通过循环来获取列的数据。然而,现在我意识到我搞砸了一件事,我忘记了类型数组!然而,一旦我们完成这两个功能,这将是一个快速而简单的修复。我想我有点搞混了,因为非变异函数不需要类型数组,因为它通过调用构造函数返回。
我想现在是时候验证一下了。
include("../src/OddFrames.jl")LoadError: syntax: invalid iteration specification
in expression starting at /home/emmac/dev/OddFrames.jl/src/type/member_func.jl:79
in expression starting at /home/emmac/dev/OddFrames.jl/src/type/frame.jl:3
in expression starting at /home/emmac/dev/OddFrames.jl/src/OddFrames.jl:1
我的天啊。
for (n, val) in enumerate(names(od::AbstractOddFrame))
为什么我把铅字放在那里?
include("../src/OddFrames.jl")
od.merge!(arr)MethodError: no method matching _merge!(::Vector{Symbol}, ::Vector{Vector{Int64}}, ::Vector{Int64}, ::Int64)
Closest candidates are:
_merge!(::Array{Symbol}, ::Array{Any}, ::Array, ::Any) at ~/dev/OddFrames.jl/src/type/member_func.jl:85
噢,向量。
LoadError: UndefVarError: Vectoor not defined
in expression starting at /home/emmac/dev/OddFrames.jl/src/type/member_func.jl:74
我讨厌这里。
od.merge!(arr)
BoundsError: attempt to access 2-element Vector{Symbol} at index [0:2]
我想我可能不小心把默认值设置为零了…
# Merge
merge!(od::OddFrame; at::Any = 0) = _merge!(labels,
columns, od, at)
merge!(x::Array; at::Any = 0) = _merge!(labels,
columns, x, at)
我做到了。
od.merge!(arr, at = 1)
3-element Vector{Vector{Int64}}:
[5, 10, 15, 20, 25]
[5, 10, 15, 20, 25]
[5, 10, 15, 20, 25]
嘿!
od.head()
[[5, 10, 15, 20, 25], [5, 10, 15, 20, 25], [5, 10, 15, 20, 25]]DataType[Int64, Int64]BoundsError: attempt to access 2-element Vector{Any} at index [3]
为什么会这样?类型数组没有新元素,因此每当我们试图访问对应于新标签的元素时,它都会得到一个错误。您可以看到 head 函数当前打印数据,有三列,但只有两种类型。
添加类型
这里我不打算讲太多的细节,但是我们将把类型作为参数添加到方法中!()函数,并以与之前相同的方式推送,并在 method()函数中添加简单的调用。让我们从方法函数开始,我也将继续,从另一个函数中复制代码,将 at 转换为数字。
function merge(od::AbstractOddFrame,
od2::AbstractOddFrame; at::Any = width(od) + 1)
if typeof(at) == Symbol
at = findall(x->x==at, labels)[1]
end
if at > width(od) || at < 1
throw(BoundsError("Merge position is not an index on this OddFrame!"))
end
pairs = []
for n in 1:width(od)
if n == at
for n in 1:width(od2)
push!(pairs, od2.labels[n] => od2.columns[n])
end
end
push!(pairs, od.labels[n] => od.columns[n])
end
return(OddFrame(pairs))
end
我也刚刚意识到,在这里我们的类型被照顾,我完全忘记了。
function _merge!(labels::Vector{Symbol}, types::AbstractVector,
columns::AbstractVector, od::AbstractOddFrame, at::Any)
if typeof(at) == Symbol
at = findall(x->x==at, labels)[1]
end
for val in names(od)
push!(labels, val, at = at)
push!(columns, od[val], at = at)
push!(types, od.dtype(val))
at += 1
end
end
哦,我意识到我是在毫无理由地列举…方便地调用你自己的函数使工作花费更少的时间感觉真好。
每次都令人满意。
现在,我们最后修改内部构造函数中的调用:
# Merge
merge!(od::OddFrame; at::Any = 0) = _merge!(labels, types,
columns, od, at)
merge!(x::Array; at::Any = 0) = _merge!(labels, types,
columns, x, at)
现在让我们最终尝试一下吧!
od.merge!(arr, at = 1)
3-element Vector{DataType}:
Int64
Int64
Int64od.head()

(图片由作者提供)
太美了。
很酷的急件
在完成这些整合之前,我还想做一些很酷的事情。第一个是添加一个新的 dispatch,它只是+操作符下的 merge 函数的别名。这段代码将进入 methods.jl。
+(od::AbstractOddFrame, od2::AbstractOddFrame) = merge(od, do2)
当然,必须将这些添加到输入中:
import Base: show, size, length, +, +=, -, -=
现在我们可以在奇数帧上使用+和-。
结论
OddFrames 项目进展非常顺利,我认为它也有一些非常酷的特性。我发现特别酷的是范式性质与奇怪框架的可变性相一致。虽然今天编程的某些部分有点疯狂,但可能大部分是在我们写那个推送的时候!()方法,我认为该基地变得非常有前途,我真的很高兴能把它的其余部分建设好!谢谢你看我的文章,它对我来说意味着整个世界!
编写更好的 R 函数—最佳实践和技巧
原文:https://towardsdatascience.com/writing-better-r-functions-best-practices-and-tips-d48ef0691c24
了解一些常用的最佳实践和技巧,以使您的代码更具可读性,更易于他人调试

照片由 Patrick @Unsplash.com 拍摄
大多数情况下,编写代码是一项协作工作。即使你是一个人在工作,写干净易读的代码只会让你的未来免于一些真正棘手的问题。
如果您没有软件工程背景(大多数数据科学家和分析师都没有),您可能需要一段时间才能理解编写干净的代码是优秀数据专业人员最重要的特征之一。当你的级别更高,需要更频繁地查看别人的代码时,这种感觉也会发展起来。
如果你用 R 或 Python 编程,你肯定会偶然发现函数的概念。函数是使我们的代码可重用和模块化的主要特性,使得从头构建更容易。如果没有函数,您可能会构建混乱的脚本,难以维护、调试和传递给其他开发人员。
然而,编写函数并不是干净代码的灵丹妙药。即使你把所有的代码都放到函数中,仍然有很多事情会让你的脚本难以理解和调整。
因此,在这篇文章中,我们将探索一些很酷的最佳实践,它们将增强你的 R 函数。这些技巧涵盖了从文档和样式到整体代码结构的各个方面,应该会在下次您必须构建一些 R 脚本时对您有所帮助。
我们开始吧!
缩进,缩进,缩进
与 Python 相反,R 不需要适当的缩进就能工作。例如,下面的函数就很好:
print_names <- function(vector_names) {
for (name in vector_names) {
print(paste(‘Hello’,name))
}
}
但是读起来超级费劲。
循环从哪里开始?循环内部是什么?我们在迭代什么?缩进函数是读者查看代码的一种优雅方式,例如:
print_names <- function(vector_names) {
for (name in vector_names) {
print(paste('Hello',name))
}
}
该函数的工作方式完全相同,但更清晰、更容易理解。你可以按照空格来检查循环插入的位置以及它在函数中是如何表达的。
缩进的另一个很酷的用法是当我们调用有很多参数的函数或者使用嵌套函数时。例如,假设下面的函数使用mtcars dataframe 根据rownames创建一个brand列:
get_car_brand <- function(car_model) {
car_brand <- sapply(strsplit(car_model, ‘ ‘), ‘[‘, 1)
return (car_brand)
}
嵌套函数有点难以理解,因为我们使用了sapply和strsplit的组合。我们可以利用缩进使函数更具可读性:
get_car_brand <- function(car_model) {
car_brand <- sapply(
strsplit(car_model, ' '),
'[',
1
)
return (car_brand)
}
适应这种向函数中输入参数的方式可能需要一段时间。但是,如果您最终没有使用 apply %>%,这是读取嵌套函数的一种更干净的方式。
长函数上的短函数
如果你有一个太长的函数,把它分解成更容易理解和调试的小的子函数。
让我们再次以mtcars数据帧为例。使用下面的函数,我们可以:
- 在
mtcars数据框架上创建汽车品牌; - 通过
brand聚合hp; - 在散点图中按品牌绘制马力;
plot_average_horsepower_brand <- function(cars_df) {
cars_df_copy <- cars_df
cars_df_copy['car_brand'] <- sapply(
strsplit(rownames(cars_df_copy), ' '),
'[',
1
)
aggregate_brand <- aggregate(
cars_df_copy$hp,
by = list(cars_df_copy$car_brand),
FUN = mean
)
sort_order <- factor(
aggregate_brand[order(aggregate_brand[,'x']),]$Group.1
)
ggplot(
data = aggregate_brand,
aes(x=factor(Group.1, levels=sort_order), y=x, color='darkred')
) + geom_point() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
}
如果我调用plot_average_horsepower_brand(mtcars),这个函数将起作用,并将产生如下的图形:

按品牌列出的平均马力—按作者列出的图片
此功能的主要问题是什么?它试图在同一块代码中做所有的事情,这使得调试更加困难。可以说,当你看到我们在里面做了这么多事情时,有点不知所措。
我们可以用几种策略来解决这个问题。一个很酷的方法是将代码分解成更小的块,使其可重用和模块化,例如:
create_brand <- function(cars_df) {
brands <- sapply(
strsplit(rownames(cars_df), ' '),
'[',
1
)
return (brands)}mean_by_variable <- function(df, agg_var, by_var) {
aggregate_brand <- aggregate(
df[,agg_var],
by = list(df[,by_var]),
FUN = mean
)
return (aggregate_brand)
}plot_sorted_scatter <- function(cars_data, agg_var, by_var) {
# Add Brand
cars_data$brand <- create_brand(cars_data)
# Create Aggregation
agg_data <- mean_by_variable(cars_data, agg_var, by_var)
# Sort
sort_order <- factor(
agg_data[order(agg_data[,'x']),]$Group.1
)
ggplot(
data = agg_data,
aes(x=factor(Group.1, levels=sort_order), y=x, color='darkred')
) + geom_point() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
}plot_sorted_scatter(mtcars, 'hp', 'brand')
您可能想知道我们的代码是否过于复杂。但是通过模块化,我们可以:
- 更容易调试。如果我在创建汽车品牌时需要改变什么,我只需查看
create_brand。如果我需要更改我在聚合中使用的函数,我只需更改mean_by_variable。 - 扩展其他变量或图的逻辑。
- 更容易地向
plot_sorted_scatter添加额外的步骤。
对于大多数编程语言来说,将函数分解成更小的函数是一种通用的最佳实践。在 R 中,没有什么不同,当你让脚本休息一会儿,然后再拿起时,你会感觉到不同。
利用文档字符串
其核心是,您可以通过在 R 函数上添加注释来记录它们。对于更复杂的函数,你想详细解释参数,这是不切实际的。另一个挫折是,如果我们不使用一个小技巧,当我们提供一个?custom_function时,R 不能访问文档(接下来解释这个!).
例如,如果我们做一个函数,返回一个数的乘法逆运算,比如:
return_inverse <- function(x) {
#' Computes the multiplicative inverse of the input
return(1/x)
}
稍后呼叫:
?return_inverse
我们将在 R 控制台上看到一个错误:

我们希望访问Computes the multiplicative inverse of the input,就像我们从其他内置(或外部库)函数中访问文档一样。
幸运的是,我们可以使用docstring为我们的函数提供更高级的文档,这将帮助其他人更好地调试您的代码。例如:
library(docstring)
?return_inverse
注意:您可能需要重启 R 来访问功能文档(加载docstring后)。上面的代码现在将返回:

尽管并不完美,但我们现在可以在环境中的任何地方访问我们的return_inverse的特定文档。很酷的想法是,我们甚至可以利用参数来使文档更好:
return_inverse <- function(x) {
#' Multiplicative Inverse of Number
#'
#' [@description](http://twitter.com/description) Computes the multiplicative inverse of the input
#'
#' [@param](http://twitter.com/param) x: Real number.
return(1/x)
}?return_inverse
这将产生非常酷的东西——我们函数的大量文档!

docstring非常酷,因为它帮助你为一个函数构建文档,这个函数模仿了大量的 base R 代码文档。你可以在这里查看更多关于docstring 的信息,包括我没有提到的额外参数。
显性回报与隐性回报
您可能会注意到,在我的整个函数中,我大多使用显式返回(使用return关键字在函数末尾包装最后一个对象)。请记住,这更多的是个人偏好,而不是适当的“最佳实践”。
但是,可以说,显式返回更有利于读者理解每个函数的结果。有时,查看函数的最后一条指令可能会令人困惑。
例如,以下两个函数将产生完全相同的结果:
create_brand_explicit <- function(cars_df) {
brands <- sapply(
strsplit(rownames(cars_df), ' '),
'[',
1
)
return (brands)
}create_brand_implicit <- function(cars_df) {
sapply(
strsplit(rownames(cars_df), ' '),
'[',
1
)
}
支持显式返回的另一个理由是变量名可以向读者暗示你的代码做了什么。在更复杂的函数中,更容易将读者的注意力吸引到显式的 return 语句上。这方面的一个例子是函数create_brand_explicit.,通过声明该函数返回一个brands变量,用户应该期望该对象将包含关于数据帧中汽车品牌的数据。
不加载库
最后,不要在函数中加载库,总是将依赖项放在脚本的顶部。
例如,在我们使用了ggplot2的 plot_sorted_scatter 中,可能会有这样的诱惑:
plot_sorted_scatter <- function(cars_data, agg_var, by_var) { library(ggplot2)
# Add Brand
mtcars$brand <- create_brand(cars_data)
# Create Aggregation
agg_data <- mean_by_variable(cars_data, agg_var, by_var)
# Sort
sort_order <- factor(
agg_data[order(agg_data[,'x']),]$Group.1
)
ggplot(
data = agg_data,
aes(x=factor(Group.1, levels=sort_order), y=x, color='darkred')
) + geom_point() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
}
虽然没有什么可以阻止你这样做,但是这被认为是一个不好的做法,因为你希望你的依赖关系在你的代码的开始被声明。这将让用户知道他们必须如何设置他们的系统,以便能够从上到下运行您的脚本——由于大多数 R 脚本依赖于外部库,这是帮助其他人获得与您在本地机器或服务器上获得的相同结果的关键。
感谢你花时间阅读这篇文章!我希望这对你将来的 R 脚本有用。
函数是编程语言中一个非常令人兴奋的概念。它们非常灵活,可以提高脚本的速度和模块化程度。学习这些最佳实践将有助于您更频繁地共享代码,并使其他人(或者更重要的是,您未来的自己)更容易使用代码!)来理解和调试你的函数。
我在 Udemy 上建立了一个R入门和一个 学习数据科学的训练营 。这两个课程都是为初学者量身定做的,我希望你能在我身边!

数据科学训练营:你成为数据科学家的第一步 —图片由作者提供
https://medium.com/membership/@ivopbernardo
以下是这篇文章中代码的要点:
你想成为一名更好的数据科学家吗?开始写文章
原文:https://towardsdatascience.com/writing-can-help-you-become-a-better-data-scientist-8b0f8adf74a5
少数人拥有但许多数据科学家需要的技能。

图片来自 Shutterstock,授权给 Frank Andrade
日复一日,互联网上产生了大量的数据,但只有极少数人知道数据是什么,有什么用,为什么有价值。
这就是为什么每个数据科学家都需要教育他们的受众,以便让其他人知道他们的职业如何为他们工作的公司创造价值。
你可能擅长编码、分析数据和建立机器学习模型,但如果你不能够有效地沟通你作为数据科学家所做的事情,没有人会知道你提供的价值以及他们为什么需要你。
信不信由你,写作可以帮助你提高沟通技巧,了解更多关于数据科学的知识。让我们深入研究一下。
数据科学家为什么要写作?
数据无处不在。每天越来越多的数据是由像你我这样的普通人创建的,但非技术人员不知道数据实际上是什么,为什么它如此有价值。
这就是为什么数据科学家在每次演讲中都需要教育他们的观众。您可能需要描述数据,解释您如何使用它来开发模型(不要使用太多的术语),并分享您的见解
没有良好的沟通技巧,你怎么能做到这一切呢?
公司拥有大量数据,他们需要数据科学家,这些科学家不仅能够理解和使用这些数据,还能与他人交流他们的见解。问题是,有时候那些有技术背景的人并不是最好的沟通者。
这就是写作能有所帮助的时候。
写作是一种有助于以可读的形式交流你的思想和观点的活动。说话是那么的自发,你根本没有时间去思考和阐述你的想法。然而,写作时你有足够的时间组织你的想法,写下来,甚至有机会在以后改进它们。
写作可以帮助你有效地传递思想。一旦你变得擅长写作,在演示或会议中解释你的想法会变得更加自然和流畅。
数据科学和写作有什么共同点?讲故事!
做好演讲的关键之一是吸引你的观众。要做到这一点,你需要有良好的沟通技巧。
如果你的听众不太了解数据科学,你的演示太专业,缺乏可视化,你的听众将不会理解你在说什么,并在精神上离开你。
写作也是如此。如果你文章的引言和正文不能让你的读者相信你的文章值得他们花时间,他们会离开的。
为了提高我们在演讲或写文章时的沟通技巧,我们需要学习讲故事的艺术!

作者图片
好的讲故事有三个要素:好的叙述、可视化和数据。
擅长讲故事的最好方法是练习。为此,你通常需要定期面对观众,像在工作中做演示一样解释事情。
然而,我们大多数人没有听众来练习,也没有时间去参加公开演讲课程。在这里,写作可以有所帮助。
你可以在一篇文章中实现讲故事的三个要素。假设您正在进行一个项目,以检测潜在的欺诈,并希望在一篇数据科学文章中解释您的分析。我们是这样做的。
首先,我们要为我们的观众使用正确的叙述。如果他们不太了解这个话题,在介绍中,我们用简单的英语解释这个项目是关于什么的。然后,在文章的主体,我们描述数据,我们使用可视化来增加参与度,并分享我们的见解。最后,在结论中,我们总结了文章最重要的一点。
一篇文章的结构和一个演示文稿很相似,不是吗?您可以将一篇数据科学文章视为您的演示文稿的脚本!
写作对你作为数据科学家的职业生涯有什么帮助?
写作可以通过不同的方式提升你作为数据科学家的职业生涯。
首先,正如我们之前提到的,写作会提高你的沟通能力。如果你学会了如何在写作时讲故事,那么在观众面前演讲时讲故事会变得容易得多。
第二,你可以在简历中包含你的数据科学文章的链接。如果文章写得好,解释得好,招聘人员会看到你有良好的沟通技巧,所以你获得工作面试的机会会增加。大多数招聘人员没有技术背景,所以他们不会在 Github 上检查你的代码,但会阅读你的文章以更好地了解你的项目。
最后,当你写一篇文章时,你会更加意识到你的工作。您可以发现弱点,并发现需要更多研究的部分,或者可以成为另一篇文章的新主题。
就是这样!现在是你开始写第一篇文章的时候了。
代码少,赚得多:获取我的免费电子书来赚钱写数据科学文章。与 10k+人一起加入我的电子邮件列表。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,让您可以无限制地访问数以千计的 Python 指南和数据科学文章。如果你用我的链接注册,我会赚一小笔佣金,不需要你额外付费。
https://frank-andrade.medium.com/membership
为数据科学家编写简洁的 Python 代码
原文:https://towardsdatascience.com/writing-clean-python-code-for-data-scientists-3448c162ac01
你是数据科学家,想用 Python 写干净的代码吗?那你来对地方了。写出好的代码需要很多东西。作为一名数据科学家,您可能会做大量 R&D / PoC 类型的工作,这需要您快速行动。因此,你在写好代码和快速前进之间走着一条微妙的路线。这种平衡是一种艺术形式,你必须为自己发展并适应你的情况。项目构思和部署之间有很大的灰色地带。但是随着你的项目变得越来越大,越来越接近部署,你就越想确保你写的是干净的、可持续的代码。这是我制作的一张图片,试图概括这种平衡。

项目生命周期中干净代码的平衡
下面是一个非常酷的图形,展示了编写好代码的各个层次。因为这篇文章的缘故,我将把重点放在这个金字塔的最上面两层,(单元)测试和代码风格。

TLDR,我将集中讨论你今天可以做的四件事来写更干净的代码。最后,我将谈谈如何实现这一过程的自动化,以及我个人如何在自己的工作中找到整洁和快速之间的平衡。
格式化程序— autopep8
- 格式化程序关心的是样式和是否符合 PEP8 定义的基本编码标准。
- 理想情况下,您应该知道编码标准并编写已经符合的代码。但是我们并不都是完美的,所以这就是格式化程序的作用。然而,如果你想在 autopep 警告你之前知道格式化你的代码的正确方法,请查看:【https://realpython.com/python-pep8/
- 怎么跑?简单。通过“pip 安装 autopep8”安装
- 然后用“autopep 8-r-a-a-in-place
运行 - r 将在整个目录中递归运行。-a 会让它变得有攻击性。—就地将就地更改文件。
- 最后一个技巧:您还可以设置 vscode 使用 autopep8 格式化程序并在保存时格式化!
棉绒——皮林
- Linter 就像一个更加固执己见的格式化程序。它也更聪明一点。linter 可以捕捉潜在的错误,比如变量没有被定义,以及许多其他问题。
- 我为 Python 使用的 linter 是 pylint。linter 有很多规则和配置选项。您可以通过在项目目录中包含一个. pylintrc 来自定义所有这些参数。而不是自己写。pylintrc,我推荐从大 G 自己做的一个开始: google pylintrc 。
- 要安装,只需“pip install pylint”并运行“pylint
” - 还可以设置 vscode 来使用 pylint。只需使用热键 ctrl+shift+p 来打开命令 pallete,并键入“Python: enable/disable 林挺”来启用它。

- 这里的最后一点是,您可以在任何一行使用“# pylint: disable=”来忽略下面一行,从而忽略 pylint 错误。只要把你想忽略的错误的名字放在等号后面。比如“missing-function-docstring”。您也可以将此注释放在文件的顶部,以忽略整个文件。
测试— pytest
好了,开始测试的一个很好的地方就是为你的函数写很多断言语句。如果您还不想创建单独的测试文件,只需在文件底部的 If 语句子句中编写一些断言,如下所示:
if name == "main ":
断言 my_function() == "预期输出"
理想情况下,试着想出任何边缘情况并测试它们。你的目标是获得 100%的覆盖率,这意味着在你所有的测试中,你写的每一行代码都会运行。我们很快会谈到更多关于覆盖率的内容。
特别要注意的是你所有的“如果”语句,尽量确保你的测试会触发这些子句。
好了,现在你已经写好了你的断言语句,只是
- 创建一个 test.py 文件(文件名无关紧要)
- 在该文件中,创建一个以“test_”开头的函数,例如“def test_my_function():”
- 将您断言语句移到该函数中
-
pip 安装 pytest
-
pytest test.py
Pytest 将在该文件中查找任何以“test_”开头的函数并运行它们。如果 assert 语句的计算结果不为 true,pytest 会让您知道。
Pytest —更多高级功能
固定装置
如果您有一段代码需要一段时间才能运行,您可以使用“fixtures”来确保您只在测试中运行这段代码一次,然后在其他测试中重用结果。你只需要给函数添加一个装饰器,这需要一段时间来运行。见这里一个简单的例子。
嘲弄的
单元测试是关于测试代码的“单元”(与用于确保整个管道运行良好的集成测试相反)。如果你有一个 API 调用,DB 调用或者其他一些你不想在你的单元测试中测试的外部依赖,但是你需要它们的输出来运行你的函数,你可以使用“嘲讽”。模仿是通过简单地包含一个
" mocker . patch(' path . to . function . to . mock ',return_value=5)"
在调用调用该函数的代码之前,在测试函数中调用。
这里见简单教程。确保您首先“pip install pytest-mock”!
新闻报道
pip 安装 pytest-cov
还记得我在上面说过要试着写一些在某个时候会用到/覆盖你所有代码的测试吗?嗯,有一种自动的方法可以知道我们覆盖了多少代码!超级简单。假设您正在构建一个名为 my_package 的 python 包,并且您有一个名为 my_tests 的测试文件夹。您可以在您的包上运行所有的测试,并使用下面的命令获得结果覆盖率(从 0 到 100%)。
pytest — cov=my_package my_tests
它会把你的结果打印到终端上。但是等等,还有呢!通过运行以下命令,您可以写出一个. xml 文件,其中包含关于哪些行被覆盖和缺失的所有数据(注意,在双破折号之前应该有空格)。
pytest-cov = my _ package-cov-report = XML my _ tests
如果你使用的是 vscode,你可以安装一个名为“Coverage Gutters”的扩展,并可视化哪些行被覆盖了!超级酷。

“Coverage Gutters 扩展”的演示。左边的红色高亮显示的是第 180 行没有经过测试。
静态类型— mypy
我知道我们已经谈了很多,但我还有一个问题要告诉你。所以你知道 python 通常不使用类型。然而,类型提示不久前作为“更正式的注释”被引入。它们对代码的可读性非常有帮助。事实证明,它们不仅仅可以提高可读性。它们实际上有助于在运行前捕捉错误!
pip 安装 mypy
mypy my_package

Splits 应该带一个 str,但是我们给它传递了一个 int。这将导致引发 TypeError 运行时异常。使用 mypy,我们能够提前发现这个错误!
额外提示—目录结构
这最后一个技巧很简短。基本上,我只是想链接到另一篇关于好的目录结构的文章。拥有一个好的结构会让你看起来像一个专家,让项目的新来者更容易知道在哪里找东西,并且让在你的计算机上设置路径变量变得更容易。
持续集成/开发(CI/CD)
我们已经讨论了很多了。为了使这个主题完整,我应该谈谈如何通过使用 CI/CD 工具(如 Github actions)来自动化我们在这里讨论的一切。然而,这可能是(也确实是)一个值得单独写一篇文章的主题。所以暂时就这么说吧。只要适合你,我就会经常运行这 4 个命令,不会拖慢你的“速度”,但足够经常到你可以为你干净的代码感到自豪的程度。
autopep8 -a -a -r —就地 my_package
pylint **/*。巴拉圭
mypy my_package
pytest—cov = my _ package—fail-on = 100 my _ tests
对我来说,这意味着我要运行这四个命令,并确保它们在我提交 PR 之前都通过。我发现,如果我在尝试开发新功能的过程中过于担心样式、静态类型或测试,我可能会花太多时间来使代码变得漂亮,而我可能会扔掉这些时间。祝你的发展之旅好运!
编写定制的 Django 数据库函数
原文:https://towardsdatascience.com/writing-custom-django-database-functions-4358e6030df7
通过定制 Django 数据库函数,可以更好地控制查询过滤

Django 的数据库函数表示将在数据库中运行的函数。它为用户提供了一种将底层数据库提供的函数用作注释、聚合或过滤器的方式。函数也是表达式,所以可以和聚合函数等其他表达式一起使用和组合。
Django 丰富的特性之一是可以定制它的各种功能。是的,你答对了!🙌我们可以根据需要定制 Django 数据库功能。
📝在本帖中,我们将通过下面的几个例子来了解根据我们的业务需求编写自定义函数的要点。
👉让我们先了解一下Django Func()类,它是我们前进的基础。
📜姜戈Func(*expressions, **extra)级
- 类 Func() 是
Django Query Expressions最通用的部分 - 它允许以某种方式将几乎任何函数或操作符实现到
Django ORM中 - Func()表达式是所有涉及数据库函数如
COALESCE和LOWER或聚合如SUM的表达式的基本类型 - 我推荐在使用
Func()之前先阅读避开 SQL 注入
以下是编写自定义数据库函数的一些方法:
🔹自定义数据库函数
我们可以使用 Django 的 Func 类创建自定义数据库函数。在我的一个项目中,我想用特定的日期格式将 Django 过滤器中的UTC时间戳转换成IST。编写两个简单的 Django 数据库函数帮助我reuse在多个实例中使用它,如下所示:
from django.db.models import Func
class TimestampToIST(Func):
""" Converts the db (UTC) timestamp value to IST equivalent timestamp
"""
function = 'timezone'
template = "%(function)s('Asia/Calcutta', %(expressions)s)"
class TimestampToStr(Func):
""" Converts the timestamp to string using the given format
"""
function = 'to_char'
template = "%(function)s(%(expressions)s, 'DD/MM/YYYY HH24:MI:SS')" # 21/06/2021 16:08:34 # Usage
Author.objects.annotate(last_updated=TimestampToStr(TimestampToIST(F('updated_at'))))
🔹数据库功能的部分实现
另一个很好的定制例子是创建一个新版本的函数,其中已经填充了一两个参数。例如,让我们创建一个专门的SubStr,它从字符串中提取第一个字符:
from functools import partial
from django.db.models.functions import Substr
ExtractFirstChar = partial(Substr, pos=1, length=1) # Usage
User.objects.annotate(name_initial=ExtractFirstChar('first_name'))
🔹执行没有聚合函数的GROUP BY
想象一种情况,我们想使用GROUP BY而不使用任何聚合函数。Django ORM不允许我们在没有集合函数的情况下使用GROUP BY🤨因此,为了实现这一点,我们可以创建一个 Django 函数,它被 Django 视为一个聚合函数,但在一个SQL query中计算为NULL,来源于 StackOverflow
from django.db.models import CharField, Func
class NullAgg(Func):
"""Annotation that causes GROUP BY without aggregating.
A fake aggregate Func class that can be used in an annotation to cause
a query to perform a GROUP BY without also performing an aggregate
operation that would require the server to enumerate all rows in every
group.
Takes no constructor arguments and produces a value of NULL.
Example:
ContentType.objects.values('app_label').annotate(na=NullAgg())
"""
template = 'NULL'
contains_aggregate = True
window_compatible = False
arity = 0
output_field = CharField()
📑资源
原载于 2022 年 5 月 19 日https://dev . to。
撰写更多成功的机器学习研究论文
原文:https://towardsdatascience.com/writing-more-successful-machine-learning-research-papers-39863ca9ea90
让你的评论者和同事印象深刻的事情。
你花了这么多时间做研究,有时失败了,重做,最后有了一些不错的结果。现在你想看到它们被发表——也许这是你博士学位的要求,也许只是为了你自己的自我,因为在一个有声望的期刊上发表一篇有你名字的论文是很酷的。所以,你继续写你的论文,并提交给期刊或会议。

图片作者。
但是等等——先别提交!有几件事你应该避免。我哪里知道?我正在领导一个小的研究小组,我已经做了一段时间的评论。有些错误是反复犯的。如果你想写一篇成功的机器学习论文,下面是我的建议列表:
1。不要假设读者知道你的主题的重要性!
你知道你在做什么,你为什么要这么做。但是你的普通读者不会。永远记住,你至少会有四种读者:

你的读者从你确切的研究领域获得大量知识的可能性很高!图片作者。
- 你的主管。不要为你的主管写作。一个好的主管已经知道你在做什么,尤其是你为什么这么做。
- 和你在同一个研究领域的人。他们大多知道所有相关的工作,也知道所有相关的术语。但是这些人越接近你确切的研究课题,存在的就越少。所以他们不太可能是你的同龄人。
- 密切相关的研究领域的人。这些人不会确切地知道你的研究是关于什么和具体的问题。但是他们对更广泛的研究领域有很好的理解。如果你没有给出一个好的动机(包括当前的挑战和为什么你的研究是必要的),你会失去这些读者。最后:
- 远程相关研究领域的人。这是最大的群体。而且很有可能(至少一些)你的评审员将来自这个小组。他们不知道所有相关的工作,也不知道你的研究为什么重要,你的方法有什么特别之处。所以你需要向他们解释这一点。
底线:
在动机上花足够的时间。清晰地表达你的想法,并用文献来支持你的主张!所有这些都进入你作品的引言部分。
2。写一些新颖的见解,而不是技术上的新奇。
几乎所有的机器学习论文都会在引言的最后陈述“新颖性”。他们为什么这样做?因为这是许多期刊和会议的要求,所展示的是新的。从论文中要求这一点是很好的,因为:如果它不是小说,为什么要费心去读它?
新奇的东西是已知的东西。然而,作者(有时还有审稿人)会将其误解为:你需要开发一种突破性的新方法,否则你的论文就不够有价值。

来自我们自己的一篇论文的评论。图片作者。
因此,大多数作者认为他们需要通过创建一种与最先进的方法不同的方法来勾选新颖性复选框,并且很容易感觉到技术新颖性是机器学习论文最重要的方面。

有时候这是我阅卷时的印象。图片作者。
但事实并非如此。洞察力是任何论文最重要的方面(不管是哪个领域)。如果读者觉得了解了一些她/他事先不知道的事情,那么这就是一种新奇!
底线:
花时间分析和解释你的结果。如果你的方法比最先进的方法显示出更好的结果,你应该首先怀疑你的结果!分析、解释——然后,作为最后一步,发布。
3。不要假设读者知道你以前的作品

有时人们会大量引用他们以前的作品。图片作者。
虽然我们都同意研究是渐进的,你很可能会发表一篇基于你以前的发现的论文:假设你的读者还没有读过那篇论文。更重要的是:不要试图强迫他们读它。
大多数评论者都是资深科学家或教授,他们有很多事情要做。让你的论文尽可能容易阅读对你最有利!因此,你的论文需要完全独立。
我的研究是机器学习,我看到人们为自己的东西(例如模型架构)介绍自己的缩写和名称,他们希望人们在后续的论文中知道他们在谈论什么。他们没有。
4。遵循科学方法
我不想在这里责备任何人,但是从我所做的一些评论来看,下面的方法并不少见:
- 方法的发展
- 该方法的测试
- 分析结果,然后
- 解释为什么这种方法有效。
但那是糟糕的科学。这叫做倾听——结果已知后的假设。机器学习尤其倾向于倾听。
相反,假设应该总是先出现:

科学方法。图片作者。
我相信你已经知道了。但是我想激励你也这样组织你的论文。你的方法永远是对你的假设的检验!
- 我们在数据中观察到了什么,让我们认为我们可以以特定的方式改进最先进的技术?(观察)
- 我们需要如何设计我们的方法?(假设)
- 我们应该用什么方式来写一个测试,来明确地找出我们的假设是对还是错?注意:下定决心如何确保你的测试没有偏差!(衍生测试)
- 在独立的测试集(实验)上运行推理
- 分析你的结果,并将其与你的假设联系起来!(分析)
- 一旦你得出了结论,并且认为这些结论很有趣,你就可以开始写论文了!
5.做你自己的魔鬼代言人
谦虚是成为科学家的一个很好的先决条件。这意味着你应该时刻意识到你研究的局限性。说出它们的名字并写下来。这些是你论文的重要部分。
你的审稿人必须找出你论文的弱点:

openreview 上的 MIDL 评论中的弱点字段。图片作者。
试着变聪明,在他们之前发现弱点。目标不是找借口。目标实际上只是描述你所做的限度,这样其他人就不会犯可避免的错误。
以下是评论者可能会问的一些邪恶的问题:
- 这些发现可能仅仅是因为数据集/超参数/随机状态的幸运选择吗
- 为什么在你的实验设置中选择 X,Y,Z?
- 这也适用于其他数据集吗?
最后:请不要忘记在分享之前校对你的文字。
6.避免“不必要的纠结”
公式是描述非常精确的事物的一个很好的工具。然而,它们有一个副作用:与你用语言或伪代码描述你所做的事情相比,读者常常需要更长的时间来理解一个公式。
那为什么人们经常使用比必要的更多的数学呢?伊恩·古德费勒在这条推特上总结得很好:
正如伊恩所说,虽然这可能有助于你通过同行评审,但从长远来看,如果你的论文很难理解,它的影响会小得多。
所以:请使用公式,如果它能帮助读者更准确地理解你做了什么。不要加数学来炫耀。
7.概念第一,写作第二。
是的,最后期限正以光速逼近。是的,你需要开始写,但是你的结果还没有准备好,因为如果你现在不开始写,你会错过最后期限。

在你的结果还不知道的时候,不要写论文。图片作者。
不要写“随到随走”。写论文和写小说有很多共同之处。在写第一段之前,你应该提前了解主要的故事情节。
如果你在得到结果和进行分析之前就开始写论文,很可能是在浪费时间。
有一个例外:在你的结果被分析之前,我唯一能推荐开始写作的部分是引言。尽量早点写引言。会帮助你识别相关的相关工作,对自己的工作有更清晰的认识。
8。最后写摘要
摘要是你论文最重要的部分。这是大多数人都会阅读的部分。我建议你在最后写摘要。只有在写完讨论之后,你才知道你论文的关键精华和要点。
永远记住摘要是对整篇论文的简短总结,包括结论。我总是试图用一到三句话来概括论文的每一部分(引言/方法/结果/讨论)。

我使用注释来构建自己的摘要。图片作者。
在撰写摘要之前,我可以推荐一个小练习:
试着用厚记号笔把你的论文包在一张纸上。试着用那张纸向别人解释你的概念。如果成功了,你就可以总结你的论文了。现在你可以开始写论文的最后一部分了!
用 Frame 子句编写窗口函数
原文:https://towardsdatascience.com/writing-window-functions-with-the-frame-clause-e91ada9da8b9
辅导的
计算 6 个月移动平均值

佩克斯·马体·米罗什尼琴科
当我看着我所做的工作时,我认为自己是一名建筑工人。像大多数建筑工人一样,我有工作时使用的工具。我越了解这些工具,工作就越容易做。其中一个工具是窗口功能。我喜欢它的多功能性;它分割、切割和控制数据的能力。
在本教程中,我们将深入研究窗口函数中的框架子句。我们使用 frame 子句来控制计算中包含的行数。我们在使用集合窗口函数或某些值窗口函数时会发现框架子句。本教程假设你对窗口函数有基本的了解。如果你是窗口函数新手,推荐阅读 SQL 窗口函数,一段爱恨情仇。
当处理数据时,窗口函数是一个强大的工具。为了帮助解释框架子句,我展示了如何使用它来计算一个 6 个月的移动平均值。
数据:如何访问它?
要跟进,您可以通过以下方式访问数据:
- 点击此处,我将向您发送用户名和密码以及如何访问数据的说明,以便您直接查询数据库。我有几个用户座位。注册无需付费,也不会收集个人信息。我正在探索新的方法来帮助读者轻松地接触这些材料。
- 直接从这里下载一个 csv 文件,这样你就可以把它上传到你自己的数据库。
现在我们已经知道了,我们可以更深入地了解窗口函数中的框架子句。
框架子句:是什么?

窗口功能,作者
框架子句确定包含在窗口函数计算中的行。我使用术语计算作为提醒,窗口函数产生一个数值结果和在框架子句中指定的行确定结果。这才是真正的重点。你可以通过使用框架子句来控制窗口函数的结果。
frame 子句具有以下语法:

框架子句语法,作者
框架条款:大局!
从概念上来说,我发现把窗口框架想象成从左到右向下执行是很有帮助的。向下走到下一排。从左到右在向下到下一行之前填充结果列。

窗口框架执行,作者
在我们的示例中,假设处理从第 7 行开始:
- 第 7 行(蓝色):窗口框架包括当前行(第 7 行)和之前的 5 行(第 2-6 行),用于确定窗口函数结果, X 。一旦该列填充了结果,处理继续向下并到达下一行第 8 行。
- 第 8 行(橙色):在第 8 行,重复该过程,窗口框架包括当前行(第 8 行)和前面的 5 行(第 3-7 行),以确定窗口函数结果, Y 。一旦该列填充了结果,处理继续向下到下一行第 9 行。
- 第 9 行(灰色):在第 9 行,重复该过程,窗口帧包括当前行(第 9 行)和前面的 5 行(第 3-7 行),以确定窗口函数结果, Y 。一旦该列填充了结果,处理继续到下一行,行 10 。
处理继续向右下方进行,直到到达最后一行。要点要记住:
- 框架将向下移动并在每行处理后将结果列填充到右侧。
- 当前行随着每行的加工而变化。
框架条款:计算 6 个月移动平均线

销售示例,作者
在我们的例子中,对于上面显示的数据,我们假设语法是“前 5 行和当前行之间的行”。处理从第 7 行开始。
- 第 7 行(蓝色):窗口框架包括当前行(第 7 行)和前面的 5 行(第 2-6 行),以确定 6 个月的移动平均值$582,497。6 个月移动平均线的计算如下。一旦该列被填充,处理继续向下进行到 r ow 8。

- 第 8 行(橙色):在第 8 行,重复该过程,窗口框架包括当前行(第 8 行)和前面的 5 行(第 3-7 行),以确定 6 个月的移动平均值$601,488。6 个月移动平均线的计算如下。一旦该列被填充,处理继续向下进行到 r ow 9。

- 第 9 行(灰色):在第 9 行,重复该过程,窗口框架包括当前行(第 9 行)和前面的 5 行(第 4-8 行),以确定 6 个月的移动平均值$625,009。6 个月移动平均线的计算如下。一旦该列被填充,处理继续从左到右向下进行到最后一行。

我们的场景
下面的例子中的数据包括按月的销售收入。目的是使用框架条款计算 6 个月移动平均线。说完这些,让我们在 SQL 编辑器中深入研究一下代码。对于 6 个月移动平均线,,我们使用平均窗口函数,并将框架子句设置为“前 5 行和当前行之间的行”。这里,我们也按升序对月份进行排序,因为我们希望从最早的日期到最后的日期进行聚合。

当查询运行时,它会在 SQL 编辑器中显示结果。突出显示的第 7–9 行与前面解释中共享的结果相匹配。我建议通过下载数据集或请求用户名和密码来练习。暂时就这样了。继续成长、学习和发展。
分享的灵感 :每一次分享的关键一击和教训,都让我想起我以前的同学,当时的经理,索菲亚。她用自己的语言、善良、专注和领导力帮助我重拾信心。当我结束为她工作时,我觉得我可以飞黄腾达了。直到今天,我还在惊叹她和她丈夫亚伯的善良。每一天都是让别人的生活变得更好的机会。拿着!
WSL 2 是更高效的数据科学工作流的最佳选择
为什么我最喜欢的工具比双引导和虚拟机更好

卡尔·海尔达尔在 Unsplash 上拍摄的照片
我很高兴能写下我最喜欢的工具之一。这对我的数据科学工作和内容创作产生了切实的影响。你可能会问,这个神奇的工具是什么?嗯,是 WSL 2。如果您对它不熟悉,那么我很高兴能够传播对这个特定工具包的认识。
如果你喜欢视频格式,在这里看。
WSL 2 是什么?
WSL 2 是 Linux 2 的 Windows 子系统的缩写。正如你们中的许多人所知,我使用 Windows 来创建我所有的内容,使用 Linux (Ubuntu)来完成我大部分的数据科学工作。WSL 2 让我可以在同一台计算机上使用两种操作系统,而不必进行双重引导。

克雷格·勒文,微软和我的项目经理
作为一名 Windows 用户,你可以使用 Linux 提供的一切,比如令人惊叹的开源工具等等。作为一个 Linux 用户,你可以访问所有你喜欢的 Windows 工具,你可以同时使用它们,并且完全集成在一起。
–微软项目经理克雷格·勒文
从本质上讲,我可以在我的 Windows 机器上打开一个 Linux 终端,使用所有的 Linux 命令,并且几乎拥有 Windows 和 Linux 的所有好处。而且,它是免费的(我知道我会在评论中经常被问到这个问题)。
对我有利…对你也有利
对大多数人来说,这似乎不是什么大事。为什么我不直接使用双引导或者有两台电脑,其中一台运行 Linux,另一台运行 Windows?或者为什么我不仅仅依赖单一的操作系统?
WSL 2 允许您从 Windows 和 Linux 访问相同的文件系统。对我来说,这是一件大事。比方说,如果我用 NVIDIA 的 RAPIDS 制作一个教程,它只在 Linux 上工作,我可以编码,录制屏幕,编辑视频,而不需要在计算机之间切换或关闭和打开它们,就像你用双引导一样(我过去必须这样做很多次)。
这也为你打开了一个巨大的工具套件。例如,如果您想使用最流行的 BI 工具之一来研究数据,您就不必考虑如何在 Linux 上配置它们;你可以在 Windows 上使用它。这使得你的工作和选择更加灵活。
这种改变不仅仅是对你的家用电脑有好处。我在这里看到的最大好处是对你的工作。大多数公司的大多数计算机都使用 Windows,大多数数据科学家仍然喜欢在本地完成大部分工作。如果您在 Windows 上工作,并且有在 Linux 上投入生产的解决方案,在此之前,您必须在某种云或虚拟环境中进行所有测试。现在,你基本上可以离线测试一切。
WSL 2 对公司来说也很棒。他们中的许多人喜欢与 Windows 相关的安全性和标准化。Linux 环境也受到类似的保护,这使得数据科学家可以使用这些工具,而不会受到 IT 部门的警告。
但是 WSL 2 表现如何呢?
与 WSL 1 不同,WSL 2 运行真正的 Linux 内核。这意味着 Linux 命令本身就可以工作。我个人认为 WSL 2 是对 WSL 1 的巨大升级。我个人觉得第一个版本非常有限,但我认为 Windows 在 WSL 2 上做得很好。您会注意到的第一个区别是更快的 I/O 性能。这意味着使用 pip 和 git 命令将轻而易举。我们说的是比 WSL 1 快两到五倍的速度,以及裸机 Linux 环境的可比速度(Sharma,2021)。
这是大约 10%的虚拟化税,因此它的构建时间和编译时间以及人工智能训练时间等都非常具有可比性。
–微软项目经理克雷格·勒文
数据科学呢?
对于 WSL 1 和 WSL 2 之间的数据科学家来说,最重要的事情是 WSL 2 支持 GPU,所以你可以进行深度学习和所有这些在 WSL 1 中受到主要限制的事情。此外,根据惠普团队一直在进行的测试,全新 Linux 安装和 WSL 2 之间的数据科学任务似乎也几乎相同。
最大的个人增值
如果你熟悉我的内容,你就知道我有多在乎深度工作和效率。我个人发现 WSL 2 最大的好处是它极大地限制了上下文切换。我爱我的运行 Linux 的工作站,我仍然用它来训练模型。然而,远程处理基本工作需要认知开销。我打乱了工作流程,不得不重新调整工作重点。
WSL 2 让我可以在相关的任务之间无缝切换,并把所有事情都放在一个罩下。老实说,这可以让我每天节省一个多小时。
【WSL 2】解锁 N10 工作流程的能力,同时保持在 Windows 机器上,这很酷。
–微软项目经理克雷格·勒文
为什么这对你有好处!
我转而使用 Linux 主要是为了数据科学,我通常觉得这样可以使用更多的工具。对于那些忠实于 Windows 或者害怕“一路”切换到 Linux 的人来说,这为你提供了一个很好的实验选择。您不必担心错过最新或最先进的工具。
结论
到目前为止,您应该更好地理解了 WSL 2 以及它如何融入您的工作流!如果你想知道如何安装的细节,你可以点击这个链接。我希望你能和我一样发现这个工具的价值。此外,请在评论中告诉我 WSL 2 如何帮助您的工作流。
如果您喜欢这篇文章,记得在 Medium 上关注我以获取更多类似的内容,注册我的简讯以获得我的内容创作和数据科学行业其他学习资源的每周更新!此外,考虑通过注册会员来支持我和成千上万的其他作家。
https://medium.com/@kenneth.b.jee/membership
下次再见,祝您的数据科学之旅好运!
参考
- 夏尔马,M. (2021)。 Windows 11 WSL 2 几乎和原生运行 Linux 一样快。技术雷达。https://www . techradar . com/news/windows-11-wsl-2-is-as-quick-as-running-Linux-native
地球科学家的食谱
原文:https://towardsdatascience.com/xarray-recipes-for-earth-scientists-c12a10c6a293
气候数据科学
帮助您分析数据的代码片段

地球科学数据通常打包在带有标注维度的 NetCDF 文件中,这使得xarray成为完美的分析工具。Xarray有一些强大而通用的内置方法,比如resample()、groupby()和concat()。这个包是地理空间分析的组成部分,这就是为什么它是 Pangeo 软件栈的主干。Xarray在开源 Apache 许可下可用。
本文是面向地球科学家的片段集。
**Table of Contents**[0\. Installation](#b377)
[0.1 Tutorial dataset](#0b57)
[1\. Climatology and Anomalies](#e7e2)
[2\. Downsampling: Monthly Average](#ab3a)
[2.1 Monthly Max, Min, Median, etc.](#b23c)
[2.2 N-month Average](#4043)
[3\. Upsampling: Daily Interpolation](#3865)
[4\. Weighted Average](#c729)
[5\. Moving Average](#544f)
[6\. Ensemble Average](#88e6)
[7\. Assign New Variables or Coordinate](#3b71)
[7.1 Assign new variable](#2360)
[7.1 Changing time coordinate](#4417)
[7.2 Changing longitude coordinate](#6c13)
[8\. Select a Specific Location](#7044)
[9\. Fill in Missing Values](#3108)
[9.1 Fill NaN with value](#a0d9)
[9.2 Replace with climatology](#d007)
[9.3 Interpolate between points](#9608)
[9.4 Forward/backward filling](#2521)
[10\. Filter Data](#7a5f)
[11\. Mask Data](#bbe5)
[12\. Final Thoughts](#7d35)
[12.1 Split-apply-combine](#a90b)
0.装置
开发者推荐使用conda包管理器和社区维护的conda-forge通道进行安装。
0.1 教程数据集
安装完成后,尝试加载这个教程数据集,它是两年来每天采样 4 次的空气温度。
这个数据集可以用来研究本文中的食谱

作者图片
1.气候学和异常
- 气候学:月气候学需要对一个时间序列中的所有一月进行平均,然后是所有二月,等等。
- 异常:这些是与气候学的偏差或者是原始时间序列与气候学的差异。例如,如果时间序列是每月的,那么 2013 年 1 月的异常值就是 2013 年 1 月的值减去 1 月的气候学值。
[groupby()](https://xarray.pydata.org/en/stable/generated/xarray.Dataset.groupby.html)将收集所有相似的坐标值,在本例中,每个组包含每个月——不考虑年份。然后,我们计算每组的平均值,并将它们重新组合在一起
提供给groupby()的参数指定了我们想要分组的内容。您可以通过[.dt](https://xarray.pydata.org/en/stable/generated/xarray.core.accessor_dt.DatetimeAccessor.html) 访问器获取月份,这就是我们在本例中所做的。
计算月距平时,我们先按月分组,然后减去气候学。例如,从一月组的每个成员中减去一月气候学,然后每隔一个月重复一次。
2.缩减采样:月平均值
- 下采样:降低样本的频率。例如,从每日时间序列变为每月时间序列
为了用xarray实现这一点,我们使用[.resample()](https://xarray.pydata.org/en/stable/generated/xarray.Dataset.resample.html)。所提供的参数指定了时间维度(如time)和重采样频率(如每月)。在上面的示例中,采样频率字符串'1MS’表示每月采样一次,新的时间向量以月初为中心( 2000 年 1 月 1 日、 2000 年 2 月 1 日等)。).
采样频率(如‘1MS’)指定如何对数据进行重新采样。其他选项见 Pandas 文档中的偏移别名。
为了计算月平均值,你必须附上.mean()。移除此方法将返回 DatasetResample 对象。请务必说明您想对样本做什么。
2.1 月最大值、最小值、中位数等。
还有一套其他方法可以和resample()一起使用,包括:max、min、median、std、var、quantile、sum
查看重采样文档了解更多细节
2.2 N 月平均值
如果你想每两个月平均一次,而不是每月平均一次,会怎么样?在这种情况下,您只需在采样频率字符串中提供采样的月数:
- 月平均:
‘1MS’(1 可选) - 两个月平均值 :
‘2MS’ - …
- 15 个月平均值 :
‘15MS’(我不知道你为什么会想这么做,但你可以)
3.上采样:每日插值
- 上采样:增加采样频率。例如,从每月时间序列变为每天时间序列
这是一个使用线性插值将数据从月分辨率提升到日分辨率的示例。换句话说,将“低”时间分辨率(例如每月)转换成“高”分辨率(例如每天)。所提供的参数指定了时间维度(例如time)和重采样频率(例如每天)。在上面的例子中,采样频率字符串'1D’表示每天采样。
然后我们附加.interpolate(“linear”)在点之间进行线性插值。
4.加权平均值
- 加权平均值:根据重要性缩放的值的平均值。计算方式为权重之和乘以值除以权重之和。
对地理空间数据进行平均时,对数据进行加权通常很重要,这样小区域就不会扭曲结果。例如,如果您的数据位于一个统一的经纬度网格上,那么两极附近的数据比低纬度地区占用的面积要小。如果你计算全球的平均温度,那么北极的权重应该小于热带,因为它占据的面积更小。
在这个例子中,我做了一个普通的方法,用纬度的余弦来加权。这个例子来自于 xarray 网页。
当用xarray计算加权平均值时,注意ds.weighted(weights)是“加权类”的一个实例除了加权平均值,我们还可以计算加权标准差、加权和等。见选项此处。
另一种方法是按网格单元面积加权,见下面的帖子
5.移动平均数
- 移动平均:通过在特定间隔内平均来平滑数据的技术。间隔由特定数量的数据点定义,称为窗口长度。
滚动平均/运行平均/移动平均是一种平滑短期波动以提高信噪比的技术。您使用[.rolling()](https://xarray.pydata.org/en/stable/generated/xarray.Dataset.rolling.html)方法,并向其提供应用平均值和窗口长度的尺寸。
在上面的例子中,我对气温教程数据集应用了 30 天的运行平均值。由于数据每天采样 4 次,因此每 30 天有 120 个样本。
你可以提供给rolling()的一个可选参数是center=True,这将把标签设置在窗口的中心而不是开始。
6.总体均值
- 总体平均:对同一数量的多个估计值进行平均。例如,CMIP 模型的集合是集合的一个例子。
如果您有一个包含多个相关变量的数据集,您可以在一个新的维度上连接它们,然后执行统计。
我之前写过这个主题,描述了用xarray实现这一点的不同技术。我现在更喜欢上面的代码。
7.分配新变量或坐标
Xarray提供了三种“赋值”方法:
[.assign()](https://xarray.pydata.org/en/stable/generated/xarray.Dataset.assign.html)→将新的数据变量分配给数据集[.assign_coords()](https://xarray.pydata.org/en/stable/generated/xarray.Dataset.assign_coords.html)→给数据集分配新坐标[.assign_attrs()](https://xarray.pydata.org/en/stable/generated/xarray.Dataset.assign_attrs.html)→给数据集分配新属性
7.1 分配新变量
要向数据集添加新变量,可以使用.assign()。请记住,xarray没有‘inplace’选项,您总是需要显式地将对象赋给一个变量。
在本例中,我将单位从摄氏度改为开尔文,并将其赋给一个名为temp_k的变量。单位很重要,还记得火星气候轨道器事件吗?
第二行是可选的,只是向新变量添加属性。
7.1 改变时间坐标
这是一个改变时间坐标的例子。当您希望时间坐标从每月的 15 号而不是 1 号开始时,这很有用。
7.2 改变经度坐标
经度有两种约定
- 【360°约定】:0°到 360°,本初子午线为 0°,数值向东递增
- 【180°约定】 : -180W 到 180E,以本初子午线的零点为中心
我编造了大会的名字。如果有合适的名字,请告诉我
从 180°变为 360°
从 360°变为 180°
或者,您可以使用列表理解来使其更加直观。
8.选择一个特定位置
使用sel()选择特定的纬度/经度位置。默认情况下, sel()在数据集中查找精确匹配,但是提供method=’nearest’告诉xarray查找与您的选择最接近的匹配。
9.填写缺失的值
通常情况下,您的数据会缺少需要填充的值。
不幸的是,这个问题没有“一刀切”的解决方案
常用方法:
- 用某个值填充缺失的值
- 用气候学填补缺失值
- 在点之间插值
- 传播价值观
9.1 用值填充 NaN
使用fillna()方法是替换缺失值的一种简单方法。在上面的例子中,我使用 0 作为填充值,但这可以是任何值,选择取决于具体情况。
9.2 替换为气候学
要替换为气候值,首先必须通过grouby()将数据分组,然后使用随气候值一起提供的fillna()。
9.3 点之间的插值
另一种方法是使用[interpolate_na()](https://xarray.pydata.org/en/stable/generated/xarray.Dataset.interpolate_na.html)对缺失值进行插值。这是时间上的线性插值。
参见
[**interpolate_na()**](https://xarray.pydata.org/en/stable/generated/xarray.Dataset.interpolate_na.html)文档 获取完整的 interp 方法列表。
9.4 向前/向后填充
ds.ffill('time')
ds.bfill('time')
这些方法用第一个非 NaN 值填充 NaN 值,沿着提供的维度,ffill()向前填充,bfill()向后填充。
10.过滤数据
基于条件表达式选择数据。只有表达式计算结果为 True 的数据才会被保留,其他数据都不会被保留。
11.分离数据
基于条件表达式屏蔽数据。如果提供的条件为真,掩码值将为真,否则为假。这可以通过简单地乘以 1 变成 1 和 0 的掩码。
12.最后的想法
我希望这篇文章对你的工作有所帮助,并展示了xarray的威力。请记住,这篇文章仅仅提供了一个人的观点。对于其中一些问题,可能有更有效或更直观的解决方案。因此,我强烈鼓励反馈和评论
我在这篇文章中没有提到的一个常见任务是重新划分数据。我鼓励你去看看令人惊叹的 xESMF 包。
最后,这篇文章中的例子可以用于其他目的。例如,计算气候学实际上是一个分割-应用-组合策略的应用。
12.1 拆分-应用-合并
- 将数据分成组,
- 对每组应用一个函数
- 将所有的组重新组合在一起。
当计算气候学时,“应用”步骤是平均的。但是,这可以换成:max、min、median、std、var、quantile或sum。使用map()甚至可以提供一个自定义方法。
感谢阅读和支持媒体作者
神经网络中的 Xavier Glorot 初始化——数学证明
使用 tanh 激活函数寻找深度学习层中权重矩阵的最佳初始分布的详细推导

Xavier Glorot 的初始化是神经网络中初始化权重矩阵的最广泛使用的方法之一。虽然在实践中,它很容易在您的深度学习设置中使用,但思考这种标准初始化技术背后的数学推理可以证明是最有益的。此外,在机器学习面试中经常会问对这种方法的理论理解,知道这种推导提供了一个展示你知识深度的好机会。
基于 Xavier Glorot 等人(2010)的论文“理解训练深度前馈神经网络的困难” ⁽ ⁾”,我们提供了详细的三步数学推导:前向传递方程、后向传递方程和权矩阵分布的推导。Xavier Glorot 初始化通常与 tanh 激活函数一起使用,该函数满足证明所需的所有假设。
注释
- 具有权重矩阵 Wⁱ 和偏置向量 bⁱ 的神经网络由一组两个连续的变换组成: sⁱ = zⁱ Wⁱ + bⁱ 和 zⁱ ⁺ = f (sⁱ)
- 一层有 nⁱ 单位,于是有 zⁱ∈ ℝⁿ⁽ⁱ⁾、∈ℝⁿ⁽ⁱ⁾ⁿ⁽ⁱ⁺⁾、bⁱ∈ℝⁿ⁽ⁱₙₙ
- zⁱ Wⁱ+bⁱ 有维度(1×nⁱ)×(nⁱ×nⁱ⁺)+1×nⁱ⁺= 1×nⁱ
- f 是一个基于元素的函数,因此它不会影响向量的形状,而zⁱ⁺= f(zⁱwⁱ+bⁱ)∈ℝⁿ⁽ⁱ⁺*⁾*
- 对于深度为 n :
的神经网络,z⁰ 为输入层, zⁿ 为输出层 - L 是神经网络的损失函数
假设
- 假设 1 :
我们假设用于特定层的激活函数为奇数,单位导数为 0:f '(0)*=1。回想一下,一个奇数函数被定义为 f(-x) = -f(x)。Glorot 初始化使用的一个常用激活函数是 tanh ,因此,我们需要验证该函数是否满足上述假设:tanh '(x)= 1-tanh(x)tanh '(0)= 1–0 = 1* - 假设 2:
我们假设初始化时的所有输入和所有层都是 iid,即独立、同分布、*,因此权重和梯度也是如此。* - 假设 3:
我们假设输入用零均值归一化,权重和偏差按照以零为中心的分布初始化,
即𝔼[z⁰] = 𝔼[Wⁱ] = 𝔼[bⁱ] = 0。由此及 f 在零点的线性度得出 zⁱ 和 sⁱ 在初始化时都有一个零期望值。
动机
该证明的目的是在给定两个约束条件的情况下,通过确定 Var[W] 来找到权重矩阵分布:
- ∀ i,var[zⁱ]=var[zⁱ⁺,
即正向信号是恒定方差流动的** - ∀ i,var[∂l/∂sⁱ]=var[∂l/∂sⁱ⁺,
即反向信号以恒定的方差流动
为了防止神经网络中梯度的爆炸和消失,上述两个等式帮助我们保证,在初始化时,层和梯度的方差在整个网络中是恒定的,即信号增益正好是 1。相反,如果增益高于1,则可能导致梯度爆炸和优化发散,而信号增益低于1,则可能导致梯度消失并停止学习。
数学证明:Xavier Glorot 初始化
一、向前传球
我们正在寻找 Wⁱ ,使得每个后续层 z 的方差相等,*即var[zⁱ]=var[zⁱ⁺。***
我们知道 zⁱ ⁺ = f (zⁱ Wⁱ + bⁱ).
为了简化即将到来的计算,我们首先在等式两边的索引 k 处应用方差运算符。请注意,每个输出神经元依赖于来自输入层的所有输入神经元。因此,当取 zⁱ ⁺ 的元素 k 时,整个向量 zⁱ 被用于计算。这就是为什么 z 仅在等式左侧的 k 处步进。
我们现在详细分析这个推导背后的步骤:

- 第一步,遵循前面说的假设 1 ,给定 f 在 0 有一个单位导数,并且是奇数,我们可以在 0 附近近似 f(x) ≃ x 。然后, zⁱ Wⁱ + bⁱ 是假设在初始化时在 0 附近,因为 Wⁱ 和 bⁱ 是从以 0 为中心的分布中采样的,并且 z⁰、神经网络的输入向量由于输入归一化而被假设为归一化的。因此,每个随后的层 zⁱ 在预期中将为 0。
- 第二步,应用变量独立性下方差的可加性质,即 Var[X+Y] = Var[X] + Var[Y]与 X ⟂ Y ,
和一个常数 c ,的方差性质即 Var[c] = 0 ,我们知道 Var[X+c] = Var[X]。此外,为了清楚起见,我们将向量和矩阵乘法写成给定 zⁱ∈ ℝⁿ⁽ⁱ⁾.的 nⁱ 元素的和

- 在第三步中,我们使用输入向量 z 和权重矩阵 W 之间的独立性 z ⟂ W 的假设,这是由于所有变量在初始化时是不相关的。在独立性下,和的方差是方差的和。
- 第四步,类似于方差和的规则,独立乘积的方差等于它们的方差加上两个涉及期望和方差的交叉项的乘积,
即 Var[XY]= Var[X]Var[Y]+E[X]Var[Y]+E[Y]Var[X]。

- 在第五步中,方程被很好地简化了,因为前两项被消除了,因为 z 和 W 都具有零均值。这是根据输入假设的标准化和从以零为中心的分布中对 W 的采样得出的。
- 在第六步中,我们注意到总和的每一项都是相同的,因为 z 和 W 是独立且同分布的,从而得出最终形式。
总之,分解了每个步骤后,下面是完整的推导过程,供再次回顾:

最后,这使我们得出前向传递证明的结论,即层的权重 Wⁱ 的方差是输入数量 nⁱ 的倒数。

有趣的事实:上面的证明也是 LeCun 初始化的演示,最初由 LeCun 等人(1998)在⁽ ⁾的“高效反向投影”中介绍。
二。偶数道次
我们正在寻找 Wⁱ 这样var[∂l/∂sⁱ]=var[∂l/∂sⁱ⁺。**
这里有、sⁱ⁺=f(sⁱ)wⁱ⁺+bⁱ⁺,有 sⁱ∈ℝⁿ⁽ⁱ̿̿。**
在应用方差算子之前,让我们先计算导数。
按链规则:

以矩阵形式:

请注意这个等式两边每个矩阵和向量的维数:1×nⁱ⁺=(1×nⁱ⁺×(nⁱ⁺*****×*nⁱ⁺
在下一步中,为了简化计算,我们使用元素表示法重写同一个等式。梯度现在变成导数,权重矩阵被截断到其第 k 列:

现在我们可以在两边应用方差。

- 首先,让我们估计导数f’。为了找到这一点,我们需要回顾一下,认识到 sⁱ 的期望值在初始化时是 0。这就简化了估算,因为我们知道 f 被假定为在 0 附近是线性的,其中 f'(0) = 1。因此, 𝔼[f'(sⁱ)] = 𝔼[f'(0)] = 1。
- 二、知道独立变量之和的方差等于方差之和,我们就可以把这个规律应用到右手边的方差上。

- 第三,应用自变量乘积方差的规则,即 Var[XY]= Var[X]Var[Y]+E[X]Var[Y]+E[Y]Var[X],方程简化为个体期望和方差的乘积之和。

- 第四,由于两项的期望值都等于零,剩下的部分就是方差之和。
- 在第五步中,我们注意到总和的每一项都是相同的,因为 L 相对于 sⁱ ⁺ 和 W 的偏导数是独立且同分布的。
总的来说,以下是所有的步骤供您再次回顾:

最后,我们得到以下结果:

三。重量分布
根据上述向前和向后传递的演示,我们得出两个结果:

论文作者建议对最终结果的方差进行平均。主要理由来自于这样一个事实,即当神经网络具有相同宽度的后续层时(这是经常发生的情况),两个等式得到满足,即 nⁱ = nⁱ ⁺* 意味着平均结果满足前面两个等式。*

实际上,既然分布的方差是已知的,就用正态分布 N(0,𝜎 ) 或均匀分布 U(-a,a) 来初始化权重。如上文假设 3* 所述,选择的分布以 0 为中心是最基本的。*
- 为正态分布 N(0,𝜎 )
如果 X ~ N(0,𝜎),那么 Var[X] = 𝜎 ,那么方差和标准差可以写成:

因此,我们可以得出结论, Wⁱ 遵循系数的正态分布:

提醒一下, nⁱ 是该层的输入数, nⁱ ⁺ 是该层的输出数。
- 为均匀分布 U(-a,a) 为均匀分布
如果 X ~ U(-a,a) ,那么使用下面的服从均匀分布的随机变量的方差公式,我们可以找到界 a :

最后,我们可以得出结论, Wⁱ 遵循一个带系数的均匀分布:

结论
在本文中,我们提供了根据 Xavier Glorot 初始化查找权重矩阵分布的详细步骤。
给定一个单位导数为 0 的奇激活函数,如 tanh ,我们可以遵循这种方法来保证初始化时信号的最佳前向和后向传播,即在前向和后向传递中保持方差恒定。
引文
(1)理解训练深度前馈神经网络的难度 、Glorot 等 (2010)
(2) 高效反向推进 、LeCun 等 (1998)**
来源:以上所有方程式和图片均为本人所有。
XGBoost 备选基础学习者
原文:https://towardsdatascience.com/xgboost-alternative-base-learners-a2dc72d97e64
dart、gblinear 和 XGBoost 随机森林简介
介绍
XGBoost 是“极限梯度提升”的缩写,是处理表格数据的最强的机器学习算法之一,由于成功赢得了众多 Kaggle 比赛,因此当之无愧的声誉。
XGBoost 是一种通常由决策树组成的集成机器学习算法。组成 XGBoost 的决策树分别被称为 gbtree ,是“梯度增强树”的缩写。XGBoost 集成中的第一个决策树是基础学习器,所有后续的树都从它的错误中学习。
尽管决策树由于其出色的集成分数而通常被首选为基础学习器,但是在某些情况下,备选的基础学习器可能会胜过它们。xd boost 包括 gblinear 、 dart 和 XGBoost Random Forests 作为备选的基础学习器,我们将在本文中探讨所有这些。
这篇文章是基于我的书第 8 章用 XGBoost 和 Scikit-learn 实践梯度增强的新例子。鼓励不熟悉 XGBoost 的读者查看我以前的文章,在 scikit-learn 中开始使用 XGBoost。下面的所有代码都可以在这个 Colab 笔记本里找到。

照片由邓肯 C,Flickrhttps://www.flickr.com/photos/duncan/99863704
什么是基础学习者?
理解基础学习者首先需要理解助推。根据 Michael Kearns 1998 年的文章《关于假设增强的思考》,增强器是一种机器学习算法,旨在将弱学习者转化为强学习者。这是什么意思?
假设一个数据集将所有输出分类为红色或蓝色,您构建一个决策树,该决策树的正确预测率为 50%。显然,这并不比随机猜测好多少。但是如果决策树 51%的时候是正确的呢?假设真正的学习已经发生,这个弱学习者可以通过建立在已经发生的学习之上而转变成强学习者。
所有的提升组合都是从基础学习者开始的。它被称为“基础”是因为它排在第一位,被称为“学习者”是因为集合中的所有后续模型都从它的结果中学习。
在 XGBoost 中,在构建第一个决策树模型(称为基础学习器)之后,第二个模型根据第一个模型的错误(也称为残差)进行训练。然后根据第二个模型的误差训练第三个模型。然后,第四个模型根据第三个模型的错误进行训练,以此类推,大约一百棵树。
与随机森林不同,随机森林采用多个决策树的平均值,XGBoost 基于先前树的错误构建新的决策树。随着训练的继续,XGBoost 变得更加精确,因为误差随着集合的增长而被校正。
Boosting 是一个通用的概念,所以有各种各样的 boosting 风格,比如在 XGBoost 之前风靡一时的 AdaBoost。类似地,基础学习器是一个通用的概念,因此除了决策树之外,还可以使用不同的基础学习器。
在继续之前,我想强调的是,强化能让弱学习者变成强学习者。如果基础学习者太强,在随后的几轮中学习可能会受到限制,并且收获最多也是微乎其微。一个基础学习者当然可以做得比 51%更好才有效,但是当重要的学习发生在第一轮训练之后时,梯度推进算法工作得最佳。
数据和代码准备
打开一个 Colab 笔记本开始在实践中使用 XGBoost 基础学习者。我们将使用佩斯·r·凯利和罗纳德·巴里从美国人口普查中提取的加州住房数据集。插入下面的代码片段,从 Colab 提供的 sample_data 文件夹中加载数据,然后按 Shift-Enter 运行单元格。
import pandas as pd
df=pd.read_csv('/content/sample_data/california_housing_train.csv')
df.head()
加州住房数据的标准是使用机器学习来预测最后一列,即 median_house_value ,如下图所示

加州住房数据框架。作者提交的照片。
现在,将数据分成机器学习输入 X 和机器学习输出 y。我们将实现 cross_val_score 来将模型分割成 5 个部分,以防止在测试模型时出现不均匀分割。
y = df.iloc[:, -1]
X = df.iloc[:, :-1]
from sklearn.model_selection import cross_val_score
最后的初步步骤是编写一个函数,该函数将机器学习模型作为输入,并将 5 次折叠的平均分数作为输出返回。
def cv_score(model):
cv = cross_val_score(model, X, y)
cv_mean_score = cv.mean()
return cv_mean_score
注意,默认情况下 cross_val_score 将返回 R2 作为回归的评分标准。R2 评分标准返回一个百分比,该百分比表示模型可以解释多少预测变化,因此越接近 1 越好。
gbtree
所有 XGBoost 模型都有基础学习者。如果你没有对默认设置做任何改变,那么基础学习者就是 gbtree ,是“梯度提升树”的缩写,它的操作就像一个标准的 sklearn 决策树。
让我们使用 XGBRegressor 默认值对 XGBoost 模型进行评分。将objective ='reg:squarederror '指定为一个参数,以防止错误消息让所有人都知道 XGBoost 已经更改objective ='reg:linear '。
from xgboost import XGBRegressor
model = XGBRegressor(objective='reg:squarederror')
cv_score(model)*0.49547514549498306*
默认的 XGBRegressor 解释了预测中几乎 50%的变化,这是一个相当不错的分数。
现在运行相同的模型,但是指定 gbtree 作为基本学习者,这是 XGBoost 的默认设置。用于指定基础学习者的参数是' booster' ,如下面的代码片段所示。
model = XGBRegressor(objective=’reg:squarederror’, booster=’gbtree’)
cv_score(model)*0.49547514549498306*
不出所料,比分完全一样。
本节的目的不是深入讨论 gbtree 的细节,而是揭示 XGBoost 如何使用 booster 参数实现基础学习器,以便在接下来的章节中对其进行更改。
飞镖
dart 背后的想法源于 Rashmi Korlakai Vinayak 和 Ran Gilad-Bachrach 的文章“DART:辍学者遇到多重加法回归树”。
基础学习器 dart 和基础学习器 gbtree 都是梯度提升树,这意味着 dart 继承了所有 gbtree 的参数。主要区别是镖移除树木,称为脱落,以帮助防止过度拟合。
要将 dart 实现为 XGBoost 的基础学习器,请按如下方式设置参数booster ='dart '。
model = XGBRegressor(objective='reg:squarederror', booster='dart')
cv_score(model)*0.49547514549498306*
如果你留心的话,你可能会惊讶于 dart 给出的分数与 gbtree 给出的分数完全相同。据推测,该算法实际上并没有丢弃树,这违背了使用 dart 的目的。为确保至少有一棵树被丢弃,设置参数 one_drop=1 如下。
model = XGBRegressor(objective='reg:squarederror', booster='dart', one_drop=1)
cv_score(model)*0.48632337772457224*
XGBoost 使用许多默认参数来防止过度拟合,因此至少删除一棵树并不能提高分数也就不足为奇了。要点是你可以掉树,设置 booster=dart 就是这么做的。
一个重要的 dart 调整:您可以通过将参数 rate_drop 设置为所需的百分比来控制树木掉落的百分比,如以下代码片段所示。
model = XGBRegressor(booster='dart', rate_drop=0.1, objective='reg:squarederror')
cv_score(model)*0.4344857301387063*
尽管通过减少 10%的树,分数并没有提高,但是很明显,在 XGBoost 中,减少树数是防止过度拟合的有效工具。
有关飞镖的更多信息,请查看飞镖助推器 XGBoost 官方文档页面。
gblinear
决策树是非线性数据的理想选择,但在某些情况下,数据本身可能是线性的,或者最好用线性算法建模。

3.0<https://creativecommons.org/licenses/by/3.0>gyassinemrabettalk✉,CC,通过维基共享。https://commons . wikimedia . org/wiki/File:Linear _ programming _ graphical _ solution . png
XGBoost 包含了 gblinear 作为处理线性数据的基础学习器。boosting 实现是相同的,这意味着每个模型都是根据以前模型的误差进行训练的,只是在这种情况下,模型本身是线性的。
检查数据是否线性的一个好方法是应用线性回归,如下面的代码片段所示。
from sklearn.linear_model import LinearRegression
model = LinearRegression()
cv_score(model)*0.54829300976933*
这是迄今为止最强的得分,表明加州住房数据可以很好地用线性模型来表示。
现在让我们看看 XGBoost 如何使用 gblinear 作为基础模型。
model=XGBRegressor(booster='gblinear',objective='reg:squarederror')
cv_score(model)*0.40932317082444925*
令人有点失望的是,对于加州住房数据集,gblinear R2 分数比线性回归和 XGBoost 树基学习器差。
在我的 XGBoost 书中,我生成了一个随机散射的线性数据集,并且 gblinear 在第五位小数上的表现优于 LinearRegression !在下面的截图中,我使用了 RMSE(均方根误差)评分标准,所以越低越好。

摘自 Jupyter 笔记本发表在用 XGBoost 和 Scikit-learn 实际操作梯度增强。作者提交的照片。
在我看来,当使用一系列线性算法时,值得尝试一下 gblinear 。
欲了解更多关于 gblinear 的信息,请查阅xgb boost 关于线性助力器参数的文档。
XGBoost 随机森林
存在 XGBoost 随机森林的事实令人敬畏,也令人惊讶。在机器学习集成方面,随机森林是 XGBoost 的主要竞争对手之一,这些机器学习集成通常在默认参数下表现良好。回想一下,随机森林也是由许多决策树构建的,但是它们取所有树的平均值,而 XGBoost 训练树的误差(残差)。
XGBoost 随机森林实际上有两种风格:1)它们可能被用作基础学习者;以及 2)它们本身可以用作算法。
虽然 XGBoost 随机森林作为基础学习者听起来或多或少势不可挡,但请记住第一节的教训:助推器是用来将弱学习者转化为强学习者的。
随机森林通常是强大的基础学习者,因此 boosting 算法在后续的训练回合中可能会发现有限的增益。然而,可能会有随机森林基础学习者闪耀的边缘案例,在机器学习中,实验胜过一切。
XGBoost 随机森林基学习器通过不同参数实现, num_parallel_trees 。换句话说,您可以指定您希望在每轮 boosting 中携带多少棵平行树,这些平行树组合起来,在每轮训练中为您提供一个随机的森林分数。
例如,具有 10 个梯度提升树的随机森林基础学习器意味着基础学习器是 10 树随机森林,并且每一轮后续训练都包括 10 树随机森林。
让我们用 10 个梯度增强树实现一个随机森林基学习器,如下所示:
model = XGBRegressor(booster='gbtree', num_parallel_tree=10, objective='reg:squarederror')
cv_score(model)*0.4954762095888313*
在这种情况下,除了在运行时,添加并行树不会产生显著的差异,因为树的数量是运行时的 10 倍。
接下来让我们试试 XGBoost 随机森林算法。这些不是备选的基础学习者,而是备选的 XGBoost 模型。这些是 XGBoost 构建的随机森林,它们作为单独的类存在,如 XGBRFRegressor 和 XGBRFClassifier 。我们需要前者,因为我们正在处理回归。
在应用下面的代码之前,请注意,在实现 XGBRFRegressor 和 XGBRFClassifier 时,不会发生任何提升。相反,他们使用 bagging (bootstrap aggregation ),这是随机森林中的标准。要了解更多信息,请查看关于 XGBoost 随机森林的官方文档。
from xgboost import XGBRFRegressor
model = XGBRFRegressor(objective='reg:squarederror')
cv_score(model)*0.3533693725331441*
这不是最好的成绩,但也不是最差的。我们来对比一下 sklearn 的 RandomForestRegressor 。
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor()
cv_score(model)*0.3827582499069725*
根据我的经验,RandomForestRegressor 在大多数时候都比 XGBRFRegressor 表现更好。然而,随着时间的推移,我已经看到 XGBoost 的随机森林有所改进,我喜欢不时地尝试一下,看看会发生什么。
结论
在本文中,您了解了基础学习者在提升算法中的重要作用。此外,您已经看到了 XGBoost 如何默认使用 gbtree 作为它的基本学习器。值得注意的是,您已经使用 booster 和 num_parallel_tree 参数实现了 XGBoost 备选基础学习器,以应用 dart 、 gblinear 和 XGBoost 随机森林。XGBoost 随机森林类是作为奖励提供的。
根据我的经验,我大部分时间使用 XGBoost 缺省值 gbtree ,因为它通常会产生最好的结果。我的建议是尝试 gblinear 作为线性回归的替代方案,如果您的 XGBoost 模型过度拟合,并且您认为删除树可能会有所帮助,请尝试 dart 。最后,享受 XGBoost 随机森林带来的乐趣,因为它们可能会改进,并且当创造性地与其他参数一起使用时,可能会带来小的收益。
有关调优 XGBoost 备选基础模型的更多信息,请查看关于 dart 、 gblinear 和 XGBoost 随机森林、的官方文档。
编码快乐!

作者提交的照片。
作者科里·韦德是伯克利编码学院 的主任和创始人,他在这里向来自世界各地的青少年教授 Python、数据科学、机器学习&人工智能。
XGBoost:基数,总是被忽视的关键超参数

更新:发现我关于渐变提升的新书,实用渐变提升。这是用 python 中的许多例子对渐变增强的深入探究。
https://www.amazon.com/dp/B0BJ82S916
在处理超参数调整时,大多数注意力都集中在过度拟合和使用正确的正则化参数上,以确保模型不会过度学习。
然而,还有一个非常重要的问题要问:预测空间的基数是多少?换句话说,XGBoost 和更一般的决策树可以预测多少不同的值?
XGBoost 和 boosted 树是离散模型
当使用 XGBoost、CatBoost 或 LightGBM 时,记住所有这些库都依赖决策树是绝对重要的,决策树的叶子只包含常量值(除非您已经为 LightGBM 启用了线性树。参见我关于主题的另一篇文章。)
简单地说,这意味着给定的模型只能预测离散数量的值。其他模型,例如高斯过程或基本线性模型,可以预测无限数量的值。
下面的代码和图表说明了:
显示梯度增强树的离散性质。作者代码。
梯度推进树的离散性质是明确的,如上面代码生成的图所示:

梯度增强树的离散性质。剧情作者。
梯度推进树只能预测一组有限的离散值。
为什么要关心基数?
基数很重要,原因有二:
- 它与你的预测集的基数直接相关。比方说,你需要预测
n种工作和m个国家的收入。如果你的模型预测小于n*m值,你的模型很可能无法正确预测收入。可能的预测集不够大,不足以捕捉现实的复杂性。 - 这是过度拟合的一个很好的指标。如果您的模型基数比您的预测集的基数高得多,那么您的模型可能会过度拟合。
计算梯度提升树的基数
计算梯度增强树模型可以生成的预测基数并不容易。这取决于每棵树的结构,以及每个决策节点做出的决策。
在只有一个估计器的简单树集合的情况下,计算是简单的。有多少树叶,就有多少种可能的预测。如果树是完整的,即如果每个分支都达到了最大深度,则叶子的数量等于2^max_depth。
当有n个估计器时,理论上讲,第一棵树的每个可能的预测可以被第二棵树带来的任何校正更新,第二棵树又可以被第三棵树带来的任何校正更新,等等。因此,从理论上讲,梯度助推器树可以产生高达

作者的公式
实际上,这个值是一个上限,因为对于一组特征来说,每棵树相对于其他树都不是独立的。
实际上,考虑第一棵树的一片叶子,特征区间的笛卡儿积允许到达这些叶子。因此,当特征在这些间隔中变化时,第一个树的响应总是相同的值。
当在这些范围内改变特征时,可以到达其他树的每片叶子的几率很低。这解释了为什么上面的公式显然是一个最大值,因为只有叶子的子集是可达的。
驱动梯度提高了树基数
为了控制模型基数而调整的超参数与导致过度拟合的参数相同。基于上一节的解释,我们看到基本的n_estimators和max_depth可以用于引导基数。
gamma和lamda,因为它们控制节点分裂也可以使用。如果您使用的是 LightGBM,那就更简单了,因为有一个参数num_leaves,它定义了给定估计器的最大叶子数,而不管深度如何。
结论
基数是基于梯度提升树的模型的基本特征,必须根据要预测的值的基数进行分析。
这是梯度增强树的离散性质的直接结果。
重要的是要确保生成的模型有足够的自由度来为预测过程中考虑的每个不同情况生成不同的值。
XGBoost for time series: lightGBM 是更大的船!
原文:https://towardsdatascience.com/xgboost-for-timeseries-lightgbm-is-a-bigger-boat-197864013e88

威廉·戴尼奥在 Unsplash 上拍摄的照片
更新:发现我关于渐变提升的新书,实用渐变提升。这是用 python 中的许多例子对渐变增强的深入探究。
https://amzn.to/3gaBn4R [## 实用的渐变增强:深入探究 Python 中的渐变增强
这本书的梯度推进方法是为学生,学者,工程师和数据科学家谁希望…](https://amzn.to/3gaBn4R)
在处理时间序列时,能够处理外推是至关重要的。没有外推法,处理趋势是不可能的。
从数学上讲,这意味着基础模型必须在其公式中至少集成一个线性部分。
在上一篇文章中,我展示了 XGBoost 的一个最重要的限制是它不能根据构造进行推断。
这是相当严格的,因为在许多 ML 情况下,你需要能够推断。在处理时间序列时尤其如此,因为你通常想要预测未来,而没有外推,你就被困在了过去。XGBoost 所做的所有预测都是历史值的集合。
在某种程度上,XGBoost 无法预测历史上没有的东西。
问题
为了具体说明这个问题,让我们以我在上一篇文章中使用的代码为例:
试着用 XGBoost 建模一个简单的线性函数。作者代码。
我们面对的是一个简单的线性时间序列,它与时间严格成比例,可以用一个基本的线性方程来建模。
正如我们在前面的脚本生成的图中看到的,XGBoost 没有抓住趋势:

XGBoost 预测与实际值。作者的情节。
解决方案
然而,XGBoost 和更一般的使用梯度推进方法训练的决策树的集成仍然是非常强大的工具,尽管有这种限制,但在时间序列上表现相对较好。
如果我们能保持这种树状的方法,并在模型中集成一些关于特征的比例关系,那将是完美的。
我最近看到了这篇不错的论文,用分段线性回归树进行梯度提升,它准确地提出了一种使用更复杂的基础学习器的方法:分段线性函数而不是分段常数函数。
在本文中,他们表明,使用这种更复杂的学习器不仅提高了精度,而且加快了收敛速度。
从数学上讲,这意味着标准梯度的恒定权重提升了决策树,用以下公式计算:

最佳叶重。作者的公式
其中,G_j 是应用于叶中剩余数据的目标函数的梯度,H_j 是目标函数的 hessian,将被更复杂的估计器代替:

使用所选要素计算的线性权重。作者的公式。
对于树的每个叶子,标准梯度增强方法必须适合于识别 a 参数的最佳值,而且选择要使用的相关特征
在他们的论文中,作者解释了如何做这件事。
非常好的消息是,它已经在 XGBoost 最重要和最著名的竞争对手之一:LightGBM 中实现了!
LigthGBM
在深入讨论这个主题之前,先简单介绍一下 LightGBM。这是一个实现梯度推进方法来训练决策树集合的库。
根据 GitHub stars 的评价,它不如 XGBoost 有名,但它仍然是 XGBoost 的一个非常受欢迎和重要的替代产品。
它主要由微软开发。允许使用线性函数作为基础预测值的选项是linear_tree.
在数学上,我们相信
让我们检查一下之前的例子,当使用这种基础学习者的 LightGBM 时,我们得到了预期的结果。
首先,为了让我们相信选项linear_tree确实能够解决我们的问题,我们将运行没有该选项的第一个脚本:
尝试在没有 linear_tree 选项的情况下捕捉数据的线性行为。作者代码。
正如这个脚本生成的图所显示的,在没有选项linear_tree的情况下,LighGBM 的性能并不比 XGBoost 好:

没有线性树,LightGBM 无法推断。图片由作者提供。
现在让我们运行完全相同的代码,除了选项linear_tree:之外,使用 LighGBM 默认参数
用 LighGBM 和线性树捕捉数据的线性本质。作者代码。
答对了。正如理论预测的那样(这是个好消息,否则我会白写这篇文章:),LightGBM 抓住了数据的线性本质:

LightGBM 外推完美!作者的情节。
更深刻的理解
看看 lightGBM 选择的参数会非常有趣。
我们知道,我们非常基本的时间序列只是与时间成比例,其系数值为 6.66。
理想情况下,lightGBM 应该将这个值确定为其线性模型的最佳值。
这很容易检查。我们将生成最简单的模型,以便于阅读模型定义。
我们将使用save_model函数来导出模型。下面是代码:
代码基本上和以前一样,除了我们调整参数得到最简单的一个:深度减少到 1,估计器的数量是 2。
查看模型定义,我们得到:
LightGBM 导出的模型定义。内容由作者提供。
两条重要的线是:
leaf _ features = 0 0
leaf _ coeff = 0 . 7019912661967
这意味着所使用的特征是第一个特征,即时间。因为深度等于 1,所以有两片叶子。一个重量为 0.701,另一个重量为 6.659。这不完全是 6.66,但已经很接近了。
结论
在处理数据科学问题时,理解要素之间以及要素与时间之间的关联方式至关重要。基于这种理解,有必要将这种关系转化为数学公式。
了解这些公式,掌握可用机器学习算法的底层数学基础的数据科学家将能够选择正确的算法并建立相关的模型。
我们在本文中已经看到,为了进行外推,我们至少需要在我们的模型中集成一些线性。知道 LightGBM 支持这种基础学习者有助于我们高效优雅地解决我们的问题。
XGBoost:它的谱系、建筑特色和创新
顺序和并行架构合二为一

照片由玛丽奥拉·格罗贝尔斯卡在 Unsplash 上拍摄
介绍
XGBoost (极限梯度提升)是一种强大的学习算法,在过去的许多比赛中胜过了许多传统的机器学习算法。
简而言之,XGBoost 集顺序和并行架构于一身:虽然它是一种顺序学习算法(加法策略),但它将并行计算融入其架构中,以提高系统效率。

制作人:杉尾道夫
这篇文章是针对初学者的 XGBoost 的介绍性概述,是一篇一站式的文章,它将向您提供关于 XGBoost 的整体情况(如果不是细节的话)——它的谱系、架构特性和创新特性。在文章的最后,我还会给出一个简短的补充资源列表,这样读者就可以更详细地了解文章中涉及的主题。
现在,我们开始吧。
为了理解 XGBoost 的特性,我们可以从快速概述它的系谱开始。
讨论
A.XGBoost 系谱
从自上而下的角度来看,XGBoost 是 监督机器学习 的子类。而且顾名思义,XGBoost 是 Boosting 机 的高级变种,是 基于树的系综 算法的子类,像随机森林。
然而,Boosting Machine 在操作学习过程的方式上与 Random Forest 有着根本的不同。
助推机
随机森林并行运行多个独立的决策树,并通过平均所有结果来组合它们的结果。这种方法使用随机引导抽样,通常被称为 bagging。从这个意义上说,随机森林是一种并行学习算法。
相反,Boosting Machine 使用一个加法策略:即“一次增加一棵新树”(xgboost developers,2022 )。Boosting 机器依次运行名为 的单个弱/简单决策树,基学习器 。简单来说,概念上的 Boosting Machine 是建立在顺序学习架构上的。
从这个意义上说,Boosting Machine 是顺序学习的,而 Random Forest 是并行学习的。
作为增压机的参考,这里有一个麻省理工学院关于增压的讲座:https://www.youtube.com/watch?v=UHBmv7qCey4
也就是说,为了避免混淆,我应该从 系统优化 的角度在这里做一个脚注。XGBoost 还设计用于运行并行计算,以提高计算资源的使用效率( xgboost developers,n.d. )。总的来说,XGBoost 虽然继承了 Boosting Machine 的顺序学习架构,但却通过并行计算来优化系统。
渐变助推机
顾名思义,XGBoost(极限梯度推进)是梯度推进机(GBM)的高级变种,是推进机家族成员。
作为其 加法策略的一部分,梯度推进机(GBM)使用梯度下降进行优化。为了减少计算量,GBM 利用泰勒展开式的一阶项逼近目标函数,并忽略高阶项进行学习优化。换句话说,它使用目标函数(损失函数)的一阶导数( 梯度 )来确定下一个弱学习器预测器。通过这种方式,梯度提升在保留现有弱预测器的同时,在它们之上添加新的预测器以减少当前误差,从而逐步提高性能。(弗里德曼,2000 年)
b .****XGBoost的算法特点
牛顿助推机
XGBoost 扩展了梯度增强的思想,除了它的一阶导数(梯度)之外,它还使用目标函数的二阶导数(Hessian:Curvature)来进一步优化它的学习过程。这个方法叫做 牛顿拉夫逊法 。而采用牛顿拉夫逊法的增压机叫做 牛顿增压 。关于梯度下降和牛顿提升之间的差异的进一步讨论,您可以阅读由 Fabio Sigrist 撰写的论文Gradient and Newton Boosting for class ification and Regression。
由于加法策略的特定结构,二阶近似产生多个有益的数学特性,以简化算法,进一步提高计算效率。(Guestrin 和陈,2016 年)
规则化:解决方差-偏差权衡
Jerome Friedman ,梯度推进机的设计者( Friedman,2000 )阐述了正则化对于解决 偏差-方差权衡 、欠拟合-过拟合权衡问题的重要性,特别推荐用户调整梯度推进机的三个元参数:迭代次数、学习速率和终端节点/叶子数。(弗里德曼,2000 年,第 1203、1214–1215 页)
在这种背景下,XGBoost 继承了梯度推进机的正则化重点,并对其进行了进一步扩展。
- 首先,XGBoost 使用户能够调整各种超参数来约束树:例如,树的数量、单个树的深度、分区的实例权重的最小和、提升轮的最大数量以及节点/叶的数量。
- 第二,它允许用户在学习过程中应用学习速率,收缩率。( Guestrin &陈 2016 第 3 页)
- 第三,它使用户能够使用随机采样技术,如列子采样。( Guestrin &陈 2016 第 3 页
- 第四,它使用户能够调整 L1 和 L2 正则项。
C.创新:
稀疏感知算法和加权分位数草图
更重要的是,XGBoost 引入了两项创新:稀疏感知算法和加权分位数草图。(陈&guest rin 2016 p10
首先,XGBoost 有一个内置的特性叫做 默认方向。 该功能捕获稀疏数据结构的模式,并根据该模式确定每个节点的分割方向。Guestrin & Chen 提出了稀疏性的三个典型原因:
“1)数据中存在缺失值;2)统计中经常出现零条目;以及 3)特征工程的人工制品,例如一键编码( Guestrin &陈 2016
原则上,这个特性使得 XGBoost 稀疏感知算法 能够处理缺失数据:用户不需要估算缺失数据。
默认方向决定了分割的方向, 加权分位数草图 提出候选分割点。下面节选自陈和 Guestrin 的论文总结了它是什么。
“一种新的分布式加权分位数草图算法……能够以可证明的理论保证处理加权数据。总体思路是提出一种支持合并和剪枝操作的数据结构,每个操作都被证明能保持一定的精度水平。”( Guestrin &陈 2016
系统优化:效率和可扩展性
到目前为止,我们从学习算法架构的角度看到了 XGBoost 的框架。现在,我们可以从系统优化的角度来看待。
原生的 XGBoost API 在追求计算效率,或者说系统优化方面也是创新的。该 API 被称为 eXtreme (X ),因为 XGBoost 旨在通过在给定的计算资源(处理器(CPU、GPU)、内存和核外(磁盘空间):缓存访问、块数据压缩和分片)之间有效地分配计算任务,使用户能够利用给定系统计算能力的极限。( databricks,2017
关于原生 XGBoost API 的更多创新方面,这里有一个由 XGBoost 的发明者(Chen & Guestrin), XGBoost:一个可扩展的树提升系统 概述的伟大作品。
结论
这个对 XGBoost 的快速概述回顾了它的系谱、架构特性和创新,但没有深入细节。
简而言之,XGBoost 具有顺序-并行混合体系结构,从某种意义上说,它继承了 Boosting 机器谱系中的顺序学习体系结构,同时将并行计算融入其体系结构中,以提高系统效率。
由于 Boosting Machine 有过度拟合的倾向,所以原生的 XGBoost API 非常注重解决偏差-方差权衡问题,并通过超参数调整方便用户应用各种正则化技术。
如果您对原生 XGBoost API 的实现示例感兴趣,可以阅读我的另一篇文章, 用原生 XGBoost API 进行成对超参数调优。
感谢你阅读这篇文章。
建议的外部资源
对于那些想探索 XGBoost 更多细节的人来说,这里有一个我最喜欢的关于该算法的参考资料的简短列表:
- 为了快速概述 XGBoost,Jason Brownlee 对机器学习的梯度推进算法 g 的简要介绍非常简洁地捕捉到了 XGBoost 的谱系和主要特性:
- 对于 XGBoost 的数学解释, TreeBoosting-03:为什么每次机器学习比赛 XGBoost 都会赢? by 哈阮给了我一个很好的补充资源。
- XGBoost 的创新方面,XGBoost:XGBoost 的发明人陈& Guestrin 的一个可扩展的树提升系统 给大家做一个简单的总结。
- 官方原生 XGBoost API 的在线文档给你一个官方的 XGBoost 教程 。它是创新算法的重要基础资源。
- 如果你有时间和灵魂去读一篇负荷很重的论文,你应该读一读杰罗姆·h·弗里德曼的论文《关于梯度推进机》, 贪婪函数逼近:一台梯度推进机 :
确认
我要感谢 TDS 的编辑团队,特别是凯瑟琳·普雷里,感谢他们在编辑阶段提供的宝贵意见。
参考
- Brownlee,J. 对机器学习的梯度推进算法的温和介绍 。 (2016)。检索自机器学习掌握:https://Machine Learning Mastery . com/gentle-introduction-gradient-boosting-algorithm-Machine-Learning/
- 数据砖。xgboost-Linux 64。 (2017)。从 github 检索:https://github . com/databricks/xgboost-Linux 64/blob/master/doc/model . MD
- j . Friedman贪婪函数逼近:一台梯度助推机 。(2000).统计年鉴,29* (5),1189–1232。doi:10.1214/aos/1013203451*
- Guestrin,c .,& Chen,T. XGBoost: 一个可扩展的树提升系统 。 (2016)。https://doi.org/10.48550/arXiv.1603.02754
- Nguyen,H. TreeBoosting-03:为什么 XGBoost 每次机器学习比赛都赢? (未注明)。检索自数据科学博客:https://datasciblog.github.io/2020/02/26/tree-boosting-03
- xgboost 开发人员。 [XGBoost 文档](https://xgboost.readthedocs.io/: https://xgboost.readthedocs.io/en/stable/) 。(未注明)。检索自https://xgboost.readthedocs.io/:https://xgboost.readthedocs.io/en/stable/
XGBoost:使用单调约束传递业务知识

Photo by 愚木混株 cdd20 on Unsplash
几天前,我和我的一个好朋友 Julia Simon 讨论在一个基于决策树的模型中考虑商业知识。
她想到了一个非常简单的问题,即预测的值随着给定的特征严格地增加。她想知道是否有可能强制模型确保这种约束。
答案是肯定的,而且在很久以前就已经添加到 XGBoost 中了(根据 XGBoost changelogs 的说法是 2017 年 12 月左右),但它并不是 XGBoost 的一个非常知名的特性:单调约束。
让我们看看这是如何实现的,底层的数学是什么,以及它是如何工作的。
在我的书实用梯度提升中有更多关于决策树的梯度提升:
https://amzn.to/3EctIej [## 实用的渐变增强:深入探究 Python 中的渐变增强
这本书的梯度推进方法是为学生,学者,工程师和数据科学家谁希望…](https://amzn.to/3EctIej)
单调约束
先来定义一下monotonic constraint。首先,在数学中,monotonic是一个适用于函数的术语,意思是当那个函数的输入增加时,函数的输出或者严格地增加或者减少。
例如,函数 x 是严格单调的:

x 是严格单调的。作者的锅。
相反,x 函数不是单调的,至少在其整个域 R:

x 在 R. Plot 上不是单调的。
限于 R+,x 是单调的,同样代表 R-。
从数学上讲,说 f 是monotonic意味着
f(x1)> f(x2)如果 x1 > x2 在单调递增的情况下。
或者
f(x_1) < f(x_2)如果 x_1 < x_2 在单调递减的情况下。
为什么需要单调约束?
在许多情况下,数据科学家预先知道要预测的值和某些特征之间的关系。例如,瓶装水的销售水平与温度成正比,因此在预测瓶装水销售的模型中实施这种约束可能会很有趣。
使用monotonic约束是向 XGBoost 模型添加这种约束的简单方法。
让我们看一个简单的例子。假设我们正在尝试对以下等式建模,其中预测y的值取决于x,如下所示:
y = 3*x。
这是一个非常简单的关系,其中y与x严格成正比。然而,在现实生活中收集数据时,会引入噪声,这会导致数据点在局部不符合该关系。在这些情况下,有必要确保模型是monotonic,理论公式也是如此。
下面的代码显示了如何使用 XGBoost 和monotonic约束:
XGBoost 中的monotonic约束是如何实现的?
我在以前的文章中展示了如何从头开始实现决策树的梯度推进:
这段代码可以很容易地修改成集成monotonic约束。处理代码中的约束通常需要开发一个求解器,而且通常是相当复杂的代码。各种方法都是可能的。在本文中,您可以看到如何使用基于几何的迭代方法来实现这样的求解器:
然而,在梯度提升应用于决策树的情况下,monotonic约束可以很容易地实现。这种实现的简单性来自于使用二进制决策树作为底层模型。
实际上,每个节点处理的决策是一个值和一个阈值之间的比较。因此,加强单调性只需要在决策节点级别考虑这种单调性。
例如,如果右节点包含列A小于阈值T的行,则右节点的增益必须小于左节点的增益。
XGBoost 如何处理单调约束?
为了了解我们如何实现这种约束,让我们看看 XGBoost 是如何在其 C++代码中实现的:
从 XGBoost 代码中提取。
代码实际上非常简单。它只是确保单调性在增益级别得到尊重。如果不是这样,代码就人为地将增益设置为negative_infinity,以确保这种分裂不会被保持。
因此,不能确保单调性的决策节点被丢弃。
单调约束的应用及效果
下面的代码片段显示了如何向 XGBoost 模型添加monotonic约束:
用单调约束训练 XGBoost 模型。作者代码
在这个教育示例中,两个 XGBoost 模型被训练来学习一个简单的理论模型,其中y = 6.66 x。添加了一些严格的负面噪声,以确保训练数据不是monotone,即有时是y_j < y_i,即使是x_i < x_j。
第一个模型在没有任何约束的情况下被训练,而第二个模型添加了一个monotonic约束。
注意,这是通过定义参数monotone_constraint来实现的。此参数是一个元组,必须包含与模型中的特征一样多的项目。
当与特征f_i相关联的项目c_i为 0 时,不应用约束。当c_i = 1时,执行增加的单调约束,而当c_i = -1时,执行减少的单调约束。
结果预测显示在该图中:

原始数据、无约束和有约束的预测。作者的情节。
放大图可以更好地显示约束的效果:

绿色表示的受约束预测值正在严格增加。作者的情节。
它清楚地表明,没有约束的模型不能确保单调性,因为预测并不总是增加的。相反,约束模型只生成增加的预测。
结论
单调约束是将业务知识转移到模型的一种简单方法。这是一个非常简单而有效的方法来引导模型走向相关的模型化。
如果你想了解更多关于梯度增强及其应用的知识,我写了一本关于这个主题的书,实用梯度增强,它详细介绍了数学基础,并提供了实用信息来充分利用 XGBoost、LightGBM 和 CatBoost:
https://amzn.to/3EctIej [## 实用的渐变增强:深入探究 Python 中的渐变增强
这本书的梯度推进方法是为学生,学者,工程师和数据科学家谁希望…](https://amzn.to/3EctIej)
酵母绿咖啡加工:水和巧克力的附加实验
咖啡数据科学
用酵母胡闹
在之前的中,我用酵母做了实验,展示了它对生咖啡的影响。在咖啡果和绿豆上使用酵母加工已经完成,但是没有太多关于绿豆发酵的公开信息。一般来说,酵母加工减少了大大影响咖啡豆甜味的酸度和苦味。
在用酵母做实验时,我做了两个额外的实验:水和可可粉。
在的水实验中,我很好奇将咖啡豆水合,然后单独脱水是否是酵母咖啡更好的部分原因。最终,事实证明并非如此,但我很高兴我完成了这个实验,因为它有助于分离一个变量。
对于可可粉,我随机想到看看酵母和可可粉如何在发酵过程中影响咖啡风味可能会很有趣。我希望创造一种巧克力咖啡,但可可粉似乎对酵母没有影响。
设备/技术
咖啡研磨机:小生零
咖啡:家庭烘焙咖啡,中杯(第一口+ 1 分钟)
预灌注:长,约 25 秒
输液:压力脉动
过滤篮 : 20g VST
其他设备: Atago TDS 计、 Acaia 比重秤、 Kruve 筛
绩效指标
我使用两个指标来评估技术之间的差异:最终得分和咖啡萃取。
最终得分 是评分卡上 7 个指标(辛辣、浓郁、糖浆、甜味、酸味、苦味和余味)的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。
使用折射仪测量总溶解固体量(TDS),该数字结合咖啡的输出重量和输入重量,用于确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**
强度半径(IR) 定义为 TDS vs EY 控制图上原点的半径,所以 IR = sqrt( TDS + EY)。这一指标有助于标准化产量或酿造比的击球性能。
水实验
我设置了同样的豆子,经历了同样的过程,但是我为其中一个豆子留下了酵母。




****
每张图片的左边和右边分别是水和酵母处理。所有图片由作者提供。
它们在罐子里呆了 24 小时,豆子吸收了所有的水分。然而,经过水处理的有液体流出。我用 TDS 仪测试过,甚至还尝过。它非常涩,不好吃。因为糖的含量,我不确定酵母是否在消耗这种提取物,但是当我冲洗和干燥咖啡豆时,我没有把它洗掉。我也担心它会使咖啡豆脱去咖啡因,但是我没有任何证据。
****
水加工过的豆子烘烤起来非常不同。我应该把它们放久一点,它们的密度要大得多,这表明它们在烘烤中没有被充分开发。这使得味道比较变得困难。
****
水加工(左)和酵母加工(右)
部分由于烘烤,酵母比水加工的豆子好得多。我很快结束了实验,因为很明显,味觉得分分布不会有重叠。大多数用水加工过的豆子很难直接饮用。
****
向酵母和绿豆中加入可可
我看到了可可包,说“也许”,所以我试了一下。这是有趣的照片。


****
酵母加工成白色,可可+酵母看起来像巧克力棕色。
冲洗后这两个看起来很相似。
****
他们烤得非常相似,这让我认为品尝结果不会显示任何重要的东西。

就口味(最终得分)而言,他们差不多。就 TDS/EY/IR 而言,可可豆稍微好一些,但总的来说,我很快就结束了这个实验,因为看不到好的味道。
****
即使这两个实验都没有发现新的东西,我仍然喜欢写它们,因为我的失败造就了今天的我。研究就是为了一个绝妙的想法把你的头往墙上撞 100 次。
如果你愿意,可以在推特、 YouTube 和 Instagram 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以关注我在中和订阅。
我的进一步阅读:
工作和学校故事集
Yelp 数据科学家面试问题演练
原文:https://towardsdatascience.com/yelp-data-scientist-interview-question-walkthrough-dee5e5ba39b2
帮你解决 Yelp SQL 面试问题

作者在 Canva 上创建的图像
如果你曾经去过一个新城镇,想找一家好餐馆,你可能知道 Yelp。作为一家企业,Yelp 专注于创建和维护总结大量信息的产品,以帮助客户做出明智的决策。它追踪世界各地成千上万不同企业的公众形象。
组织所有这些数据并不是一件容易的事情,因此 Yelp 一直在寻找有前途的数据科学家加入其行列。
Yelp 的数据科学家必须编写不同难度的查询。为了在工作中取得成功,数据科学家候选人必须具备扎实的基本和中级 SQL 概念的基础知识。我们来看看回答技术面试问题你应该知道的具体概念。
面试中测试基本到中级概念
最佳候选人用最少的代码行编写回答问题的查询。如果没有对 SQL 概念、语句和子句的深入了解,这通常是不可能的。
扎实的基础知识可以帮助你获得一份工作。更重要的是,一旦你有了工作,掌握实用和理论上的 SQL 可以帮助你保住工作。雇主希望有人写防呆代码。Yelp 也不例外。要编写这样的查询,您需要对 SQL 有深刻的理解。

作者在 Canva 上创建的图片
以下是回答这类数据科学面试问题时你应该知道的概念:
选择/从
当您开始学习 SQL 时,SELECT 和 FROM 语句通常是您学习的最初几个概念。我们使用 FROM 语句来指定我们将使用的表。我们使用 SELECT 语句来查看表中的特定列。
这两种说法很容易掌握。不管有多难,SELECT 和 FROM 对于编写任何 SQL 查询都是必不可少的。因此,如果您没有 SQL 的工作知识,这两条语句是一个很好的起点。
哪里
Yelp 的用户通常希望看到最受欢迎的商家。过滤这些数据是 Yelp 数据科学家工作的一大部分。
WHERE 语句允许您设置条件。编写 SQL 查询时,其主要目的是确保只返回满足指定条件的记录。WHERE 语句中可以使用许多运算符。具体可以用:equals(=),not equals(!=)、大于(>)、小于(<)以及比较运算符的其他变体。对数值使用比较运算符很容易。
您还应该知道如何比较非数值(使用=或!=比较运算符)。要编写条件,必须使用正确的语法规则。例如,要将列值与文本值进行比较,需要单引号“”。要编写条件,必须使用正确的语法规则。例如,要将列值与文本值进行比较,需要单引号“”。此外,您应该知道如何使用字母和不同的运算符按照字母顺序进行排序。
例如,您可能需要删除以字母 A、B 或 c 开头的值,SQL 中的 WHERE 语句非常通用,可以应用于许多不同的任务。
最小值/最大值()
这些问题以及类似的问题通常会测试您对中级 SQL 概念的了解,例如 SQL 中的 MIN()和 MAX()聚合函数。在我们的例子中,候选人必须找到拥有最多“酷”选票的企业。在这种任务中,MAX()聚合函数会很有用。它返回指定列中具有最大值的行。MIN()返回最低值。
对于数值,使用这些聚合函数是很直观的。此外,了解 MIN()和 MAX()在应用于非数值时的工作方式也很有用。例如,MIN()函数可用于查找一列中最早的日期或以 A 开头的单词或字母表中其他较早的字母。MAX()可以做相反的事情。阅读本文“ 基础 SQL 面试问题 ”了解更多关于聚合函数的知识。
排序依据
通常会遇到 SQL 面试问题,要求您找出特定列中具有最高值的行。理解这一概念对于回答今天数据科学访谈中提出的大多数 SQL 问题至关重要。ORDER BY 允许您指定行排列的标准。该子句通常与另外两个关键字一起使用:DESC 和 ASC。这些关键字帮助您指定是希望结果按升序还是降序排列。
在处理数字时,ORDER BY 子句的默认行为很容易预测:它将从最小的数字到最大的数字对值进行排序。数据科学家职位的优秀候选人还应该知道如何对字母和日期值使用 ORDER BY。
内部连接
连接是 SQL 中不可或缺的概念。任何有抱负的数据科学家都应该在进入更高级的概念之前掌握连接。由于其实用性,内部连接可用于回答各种各样的 SQL 问题。
首先,考生应该理解编写 SQL 查询的语法。例如,您可以从组合表中选择列,而不仅仅是 from 语句后面的列。此外,如有必要,他们应该能够演示如何从一个表中选择列。
具有编写 SQL 查询实际知识的数据科学家也应该理解编写别名的重要性。他们还应该熟悉用 SQL 编写别名的语法,并理解它们在提高 SQL 查询可读性方面的作用。
编写内部连接的另一个重要方面是 ON 语句。熟练的数据科学家应该了解它的用途,并能够正确地将值从一个表映射到另一个表。选择正确的连接键来获得期望的结果也很重要。
考生应该能够解释多种联接类型之间的区别。当两个表中的记录没有任何公共值时会发生什么?INNER JOIN 是做什么的?根据资历级别,候选人应该能够区分内部和外部连接,并选择正确的类型来解决手头的问题。
在本文中,我们将回顾一个问题,并使用内部连接的关键特性来得出答案。
Yelp 数据科学家面试问题演练
在本文中,我们将重点关注 Yelp 数据科学家职位候选人的问题。
顶级酷票
这个问题目前在 StrataScratch 平台上被标记为‘中等’难度。条件相当简单明了。候选人必须编写一个查询来查找具有最高“酷”票数的记录,并以特定的格式返回。

截图来自 StrataScratch
链接:https://platform . stratascratch . com/coding/10060-top-cool-votes
在这个问题中,候选人必须使用一个有 9 列的表格。他们必须编写 SQL 查询来查找在 cool 列中具有最高整数值的记录。然后,受访者必须输出两列各自的值, business_name 和 review_text 。
这项任务很简单,有很多方法可以得到想要的结果。因此,您的重点应该是编写一个优化的 SQL 查询。只要您理解了本文中概述的原则和陈述,您应该能够找到一个运行良好且不过分冗长的解决方案。
可用数据集

截图来自 StrataScratch
数据假设
解决任何 SQL 问题的第一步,也可能是最重要的一步是研究可用数据。
首先要做的是扫描列名及其对应的数据类型。你可以利用这个机会形成第一印象,进入思维空间解决问题。如果有些事情不清楚,你可以提问来检查你的假设。
只要有可能,也试着从表中查看实际记录。看到实际的数据可以帮助你更好地理解你将使用什么样的价值观。除了实际数据之外,注意列名和数据类型将为您制定计划提供足够的信息。
如果你已经分析了数据,但事情仍然不清楚,不要害怕要求澄清一些观点。你可以通过问一些具体的、指示性的问题来获得更多的信息。
根据问题的表述,您应该准备好对值进行造型、格式化或以任何其他方式操作数据。一旦完成了对表的分析,您还应该清楚哪些列是重要的,哪些列可以安全地忽略。
让我们来看看 yelp_reviews 表的列:
- 这个问题的最终解决方案应该返回 business_name 列中的值。我们将使用 SELECT 语句来查看该列。
- 我们的问题没有提到识别每个评论,所以我们可以忽略 review_id 列。
- 这个问题不要求我们识别用户,所以我们可以忽略 user_id 列
- 我们不必用‘星星’的数量作为判断标准,所以星星列可以忽略。
- 由于时间顺序对解决方案并不重要,我们可以忽略 review_date 列
- 最终输出必须包括来自 review_text 列的值,因此我们将需要它。
- 该问题要求我们确定拥有最多“酷”投票的企业,因此我们需要使用酷列。另外两个栏目——搞笑和有用可以忽略。
这个具体问题可以用许多不同的方法来解决。如果你只能选择一个解决方案,但无法在几个好的选项中做出选择,你应该问问面试官。有时候面试官可能更喜欢标准 SQL 而不是它的许多方言。
解决方案逻辑
一旦您理解了问题和包含所有数据的表,您就可以轻松地回答像这样的复杂的 SQL 问题。首先,我们必须制定回答这个问题的方法:
- 根据问题描述,我们需要输出两列— business_name 和 review_text 。首先,我们必须使用 SELECT 语句来查看它们。
- 然后,我们必须编写 SELECT 语句,但这次是为了查看具有最高票数的记录。我们可以使用 MAX()聚合函数找到这个记录。
- 最后,我们编写 ON 语句来过滤掉所有没有最高“酷”票数的企业。
要回答 Yelp 数据科学家的采访问题,您必须具备良好的基本和中级 SQL 概念的工作知识。在第一步中,我们需要使用 SELECT 和 FROM 语句的组合。这是任何 SQL 查询的最基本的构建块,因此您应该轻松地完成这一步。
在下一步中,我们使用 MAX()聚合函数来查找 cool 列中的最大数值。函数的名称是自我描述的:它返回列中的最大值。
请注意,在执行任何类型的连接时,为表提供别名总是一个好主意。这样,您不必每次都键入表名。可以在每个表名后分配别名。您留下一个空格,并为该表编写别名。
看着这个问题,有人可能会想:如果两个或更多的企业分享最高数量的“酷”票会怎么样?在我们的解决方案中,我们使用内部连接来保留拥有最高票数的企业,并过滤掉其余的企业。如果有三家企业获得了最高票数,我们的最终结果将包括这三家企业。
常见错误解决方案
正如我们之前提到的,知识渊博的数据科学家可以找到多种方法来解决问题。最常见的错误之一是使用 ORDER BY 和 LIMIT 语句。使用这种方法,我们编写一个查询,以降序对“cool”列中的值进行排序,并使用 LIMIT 语句显示第一个值。
这种方法的问题是,最有可能的是,将有多个企业拥有最高数量的“酷”票。根据问题,我们需要退回所有这些业务,而不仅仅是一个。如果我们不知道有多少企业共享最高票数,极限陈述实际上是没有用的。
SELECT business_name,
review_text
FROM yelp_reviews
ORDER BY cool DESC
LIMIT 1
正确的解决方案
编写子查询来查找最高数量的“酷”票
首先,我们必须编写一个子查询,它将返回“cool”列中的最大值。我们使用 AS 语句,所以我们可以将这个值称为' max_cool '。
SELECT max(cool) AS max_cool
FROM yelp_reviews
运行此代码将返回以下输出:

截图来自 StrataScratch
这一步帮助我们解决了一个难题——我们知道在 yelp_reviews 表格中,企业获得“酷”投票的最高可能数量。
抓取感兴趣的栏目
首先,我们必须选择最终输出中包含的两个字段。我们应该返回来自 yelp_reviews 表中两列的值,这两列是 business_name 和 review_text 列。完成后,SQL 代码将如下所示:
SELECT business_name,
review_text
FROM yelp_reviews
该查询将返回具有相应 review_text 值的所有企业。让我们来看看:

截图来自 StrataScratch
使用内部连接过滤掉业务
在最后一步,我们使用内部连接过滤掉没有达到最大“酷”票数的企业。
为了实现这一点,我们将使用之前创建的子查询内部连接 yelp_reviews 表。
我们将给出每个表的别名,以使查询更具可读性。“yr”用于 yelp_reviews 表,而“mc”用于子查询。
SELECT business_name,
review_text
FROM yelp_reviews yr
INNER JOIN (
SELECT
max(cool) as max_cool
FROM yelp_reviews) mc
ON yr.cool = mc.max_cool
最后,我们必须选择连接键并编写 on 语句的条件。我们必须参考 yelp_reviews 表中的 cool 列,并将其设置为最大值。
这是最终答案。如果我们运行代码,输出将如下所示:

截图来自 StrataScratch
这两家公司都拥有最高的“酷”票数。
另一个正确的解决方案
使用内部连接过滤掉投票数低于最大值的企业非常简单,但是还有一个更简单的解决方案。
逻辑大致相同:我们选择想要查看的列,并有条件地返回具有最高“酷”投票数的记录。就像使用 INNER JOIN 的解决方案一样,我们使用 max()聚合函数来查找最高票数。主要区别在于,使用这种方法,我们使用 WHERE 语句来过滤企业。
该解决方案回答了 Yelp 数据科学家的采访问题,并处理了边缘案例。运行该查询将返回任意数量的具有最高票数的企业。有人可能会说,由于这种解决方案只需要编写较少的代码,因此是一种更优化的解决方案。
SELECT business_name,
review_text
FROM yelp_reviews
WHERE cool =
(SELECT max(cool)
FROM yelp_reviews)
最后的话
像 Yelp 这样的公司正在寻找编写简单而有效的 SQL 查询的数据科学家。为面试做好充分准备可以显著增加你获得数据科学工作的机会。如果你想知道如何构建你的代码,特别是在性能和可读性方面,我们推荐我们的帖子“ 编写 SQL 查询的最佳实践 ”。
在这篇文章中,我们回答了一个有趣的问题,这个问题是问参加 Yelp 数据科学家职位面试的候选人的。这不是最容易解决的问题,但面试官用它来衡量候选人的 SQL 知识的深度。
最初发表于https://www.stratascratch.com。
是的,Python 有一个内置的数据库。以下是使用方法。
原文:https://towardsdatascience.com/yes-python-has-a-built-in-database-heres-how-to-use-it-b3c033f172d3
Python 中 SQLite 的简单指南。

图片来自 Shutterstock,授权给 Frank Andrade
信不信由你,在你的电脑上安装 Python 的那一刻,你也安装了其他奇妙的工具。其中之一就是 SQLite。
SQLite 是一个嵌入式的、基于文件的关系数据库管理系统(RDBMS ),可以在我们的 Python 应用程序中使用,而无需安装任何额外的软件。相反,我们只需要导入内置的 Python 库sqlite3就可以使用这个数据库。
在本指南中,我们将了解如何连接到数据库,创建表,将数据插入表中,以及如何将其与 Pandas 集成。
如果你不想看,可以看我的 YouTube 视频!
请务必点击 订阅此处 获取我在所有教程中使用的 SQL 备忘单(免费 PDF)
创建到数据库的连接
我们要做的第一件事是创建一个到数据库的连接。为此,我们只需要导入 sqlite3 并使用.connect方法。在括号内,我们写下我们想要创建的数据库的名称。在我的例子中,我将其命名为“students.db”
**import** sqlite3
# create a connection
conn = sqlite3.connect('students.db')
如果您运行上面的代码,将在您的工作目录中创建一个名为“students.db”的新文件。

作者图片
现在我们可以创建一个表并将数据放入其中。
创建表格
在创建表之前,我们需要创建一个游标。游标是一种用于建立执行 SQL 查询的连接的对象。我们将使用光标来创建表格、插入数据等等。
要创建一个光标,我们只需要使用我们已经创建的连接和.cursor方法。
c = conn.cursor()
之后,我们使用.execute方法在数据库中创建一个新表。在引号内,我们编写了用于在大多数 RDBMS 中创建表的普通 SQL 语法。在这种情况下,我们使用CREATE TABLE语句。
c.**execute**("""**CREATE TABLE** students (
name **TEXT**,
age **INTEGER**,
height **REAL**
)""")
如您所见,在创建表的列时,我们需要定义数据类型。与大多数拥有数十种数据类型的 RDBMS 不同,SQLite 只有 5 种数据类型:
- Null:缺少值
- 整数:没有小数点的数字(例如 1、2、3、4)
- 实数:带小数点的数字(如 6.2、7.6、11.2)
- 文本:任何字符数据
- Blob:作为值存储在数据库中的二进制数据的集合。它允许我们在数据库中存储文档、图像和其他多媒体文件。
最后,我们必须提交并关闭连接。以下是目前为止的代码。
太好了!我们已经创建了第一个表,但是它是空的,所以让我们将一些数据放入其中。
将数据插入表格
让我们从向“学生”表添加一行开始。为此,我们再次使用.execute,但是现在我们使用INSERT INTO语句。
下面我补充一个学生“马克”的数据,他今年 20 岁,身高 1.9 米。
c.**execute**("**INSERT INTO** students **VALUES** ('mark', 20, 1.9)")
注意,在运行上面的代码之前,您需要注释掉CREATE TABLE语句,因为该表已经存在。
我们也可以插入多行,但是在这种情况下,我们使用.executemany 方法。除此之外,我们使用?作为占位符。这有助于我们从名为all_students的列表中添加数据。
all_students = [
('john', 21, 1.8),
('david', 35, 1.7),
('michael', 19, 1.83),
]
c.**executemany**("**INSERT INTO** students **VALUES** (?, ?, ?)", all_students)
从表 a 中选择数据显示数据
到目前为止,我们已经创建了一个表并将数据放入其中,但是我们还没有看到我们的表。要查看我们的数据,我们首先需要用SELECT语句从我们的表中选择数据,然后用.fetchall显示它。
c.execute("**SELECT** * **FROM** students")
**print**(c**.fetchall()**)
打印的输出应该是:
[(‘mark’, 20, 1.9), (‘john’, 21, 1.8), (‘david’, 35, 1.7), (‘michael’, 19, 1.83)]
如果您不想在每次想要查看表格中的数据时都重复这些步骤,您可以使用 SQLiteViewer 。在那里你只需要拖动你的。db 文件来查看其内容。

作者图片
这是我们到目前为止所做的一切
这是 Python 中 SQLite 的基础。在 SQLite 中,更新行、删除行、排序数据和删除表也是可能的。您只需要使用您的 SQL 知识来执行它们。
使用 Pandas 和 SQLite
SQLite 可以与 Pandas 中的 dataframes 集成。例如,我们将使用一个名为population_total.csv的 CSV 文件,您可以在这里下载。
**import** pandas **as** pd
df = pd.read_csv("population_total.csv")
以下是数据帧的外观:
>>> df country year population0 China 2020.0 1.439324e+09
1 China 2019.0 1.433784e+09
2 China 2018.0 1.427648e+09
3 China 2017.0 1.421022e+09
4 China 2016.0 1.414049e+09
... ... ... ...
4180 United States 1965.0 1.997337e+08
4181 United States 1960.0 1.867206e+08
4182 United States 1955.0 1.716853e+08
4183 India 1960.0 4.505477e+08
4184 India 1955.0 4.098806e+08
现在让我们创建一个内存中的 SQLite 数据库。为此,首先,我们需要安装 sqlalchemy: pip install sqlalchemy
然后我们需要创造一个引擎。
**from** sqlalchemy **import** create_engine
engine = create_engine('sqlite://', echo=**False**)
现在让我们将数据帧附加到数据库中的一个表中(这个表不需要预先创建)。在本例中,我将把df附加到一个我命名为“population”的表中。
df.**to_sql**("population", con=engine)
要查看我们的表,我们运行下面的代码。
engine.**execute**("**SELECT** * **FROM** population").fetchall()
注意:如果您想要创建一个 sqlite 文件(而不是内存中的数据库),您应该创建一个带有文件数据库的引擎。
让我们创建一个mydb.db文件,然后将df数据帧附加到一个“人口”表。
**from** sqlalchemy **import** create_engine
engine = create_engine("sqlite:///mydb.db")df.to_sql("population", engine)
同样,您可以使用.fetchall来查看表格或使用 SQLite Viewer。

恭喜你!现在您知道如何在 Python 中使用 SQLite,甚至将它与 Pandas 中的 dataframes 集成。
学习 SQL —数据专业人员最需要的技能。 加入我的 20k+人电子邮件列表,获取我的免费 SQL 备忘单。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,让您可以无限制地访问数以千计的 Python 指南和数据科学文章。如果你用我的链接注册,我会赚一小笔佣金,不需要你额外付费。
https://frank-andrade.medium.com/membership
YOLOv6:下一代物体探测—回顾与比较

图片来自 Unsplash
近年来,计算机视觉领域发展迅速,并取得了几年前看起来像科幻小说一样的成果。从分析 x 光图像和诊断病人到(半)自动驾驶汽车,我们正在见证一场革命。这些突破有很多原因——构建更好、更易访问的计算资源,但事实上它们是我们最接近开源数据科学 (OSDS)的东西。向社区公开源代码可以释放“群众的智慧”,实现大规模创新和解决问题。
计算机视觉领域最受欢迎的操作系统项目之一是 YOLO(你只需看一次)。YOLO 是一种高效的实时对象检测算法,由 Joseph Redmon 等人在 2015 年的开创性论文中首次描述。YOLO 将图像划分为一个网格系统,每个网格检测自身内部的对象。它可以用于实时推理,并且需要很少的计算资源。
今天,在第一版 YOLO 发布 7 年后,美团的研究小组发布了新的 YOLOv6 型号——它是来踢一脚的!
YOLO 的历史
YOLO 之前的目标检测
在 YOLO 之前,两阶段对象检测架构主导了该领域。它使用基于区域的分类器来定位区域,然后将它们传递给更健壮的分类器。虽然这种方法给出了具有高平均精度(mAP)的精确结果,但是它是非常资源密集的,在其操作中需要多次迭代。

两阶段对象检测,来自纸张的图像
YOLO 是如何工作的?
YOLO 提出了一种不同的方法,其中两个阶段都在同一个神经网络中进行。首先,图像被分成单元,每个单元具有 SxS 的等维区域。然后,每个单元用边界框坐标(相对于其坐标)和对象标签以及该事物出现在单元中的概率来检测和定位它所包含的对象。

YOLOv1,图像来自原纸
因为每个单元“独立工作”,所以它可以同时处理网格,减少了训练和推断所需的计算能力和时间。事实上,YOLO 实现了最先进的结果,击败了其他实时对象检测算法。
YOLO 有哪些版本?
- yolov 1(2015 年 6 月):你只看一次:统一的、实时的物体检测
- yolo v2(2016 年 12 月): YOLO9000:更好、更快、更强
- yolo v3(2018 年 4 月): YOLOv3:增量改进
- yolov 4(2020 年 4 月): YOLOv4:物体检测的最佳速度和精度
- yolov 5(2020 年 5 月): Github repo (尚未发布论文)
YOLOv6 是来踢**和取名字的
MT-YOLOv6 的灵感来自最初的一级 YOLO 建筑,因此被其作者(勇敢地)命名为 YOLOv6。虽然它提供了出色的结果,但值得注意的是 MT-YOLOv6 不是官方 YOLO 系列的一部分。
YOLOv6 是一个专用于工业应用的单级对象检测框架,具有硬件友好的高效设计和高性能。它在检测准确性和推理速度方面优于 YOLOv5,是生产应用中 YOLO 架构的最佳 OS 版本。
YOLOv6 成就
- yolov 6-nano-在 COCO val2017 数据集上实现 35.0 地图,在 T4 上使用 TensorRT FP16 进行 bs32 推理,每秒 1242 帧
- yolov 6-s-在 COCO val2017 数据集上实现 43.1 地图,在 T4 上使用 TensorRT FP16 进行 bs32 推理,每秒 520 帧。
单一图像推理

来自 YOLOv6 存储库的图像
YOLOv6s (red)提供了比所有以前版本的 YOLOv5 更好的平均精度(mAP ),推理时间大约快 2 倍。我们还可以看到基于 YOLO 的架构和基于两阶段对象检测的 EfficientDet 之间的巨大性能差距。
视频推理

来自 YOLOv6 存储库的图像
与单个图像推断相同,YOLOv6 在所有 FPS 频谱上为视频提供了更好的结果。有趣的是注意到了大约 550–620 FPS 的曲线变化。我想知道这是否与硬件性能有关,以及维护人员在进行实验时是否减少了硬件的偏差。
基准

作者图片
- 在 COCO val2017 数据集上测试了不同物体探测器的地图和速度的比较。
- 其他方法的速度结果在维护者的环境中使用官方代码库和模型进行了测试,如果在相应的官方版本中没有找到的话。
免责声明:上述评论是基于作者的说法,我们还有待核实。
YOLOv5 与 YOLOv6
YOLOv5 和 YOLOv6 的性能指标评测比较
在研究这两种型号的基准时,我发现很难对苹果进行比较。YOLOv6 的型号较少(缺少 m/l/x),并且没有任何大于 640 像素的图像信息。对于两个项目报告的基准,我们可以清楚地看到 YOLOv6 在 mAP 方面的改进。然而,v6 的参数和失败次数是 v5 的两倍,这让我想亲自深入训练过程,仔细检查下面的结果。

作者图片
YOLOv5 和 YOLOv6 之间的定性比较
我使用两种型号的 s 版本来检测以下图像中的对象:

YOLOv6 性能,图像来自 YOLOv5 存储库

YOLOv5 表演,图片来自 YOLOv5 知识库

YOLOv6 性能,图像来自 YOLOv5 存储库

YOLOv5 表演,图片来自 YOLOv5 知识库
我们可以清楚地看到 YOLOv6s 在图像中检测到更多的物体,并且对它们的标签有更高的信心。
灵活性
两个项目都有相似的方法来创建不同的模型大小。最大的区别是 YOLOv5 使用 YAML ,而 YOLOv6 直接在 Python 中定义模型参数。预示性的一瞥也表明 YOLOv5 可能在一定程度上更具可定制性。
然而,YOLOv6 如此灵活的事实意味着我们可以在未来看到更大版本的 YOLOv6,甚至更高精度的预测!
如果你创造了一个更大的 YOLOv6 模型,让我们知道不和谐!我们很想看看!
使用
你可以使用 DagsHub 的应用与 YOLOv6 的最新版本进行交互。如果您想在本地计算机上使用它,请按照下列步骤操作:
安装
git clone https://dagshub.com/nirbarazida/YOLOv6 cd
YOLOv6 pip install -r requirements.txt
dvc pull
推论
使用 YOLOv6s: python tools/infer.py --weights yolov6s.pt --source <path to image/directory>
使用 YOLOv6n: python tools/infer.py --weights yolov6n.pt --source <path to image/directory>
结论
YOLOv6 是最近发布的最令人兴奋的 OSDS 项目之一。与以前的 YOLO 版本相比,它提供了最先进的结果和各方面的显著改进。维护人员目前专注于丰富模型类型、部署选项和量化工具。尽管如此,与任何开源项目一样,社区可以极大地影响其路线图和进度曲线。
尽管该项目仍处于早期阶段,但它看起来非常有前途,我很想知道它将来会打破哪些基准。
YOLOv7:深入了解当前物体检测的最新技术
在定制培训脚本中使用 YOLOv7 需要知道的一切
在其发布后不久,YOLOv7 是用于计算机视觉任务的最快和最准确的实时对象检测模型。官方论文在 MS COCO 数据集上展示了这种改进的架构如何在速度和准确性方面超越所有以前的 YOLO 版本以及所有其他对象检测模型;在不使用任何预训练砝码的情况下实现这一性能。此外,在围绕以前的 YOLO 模型的命名惯例的所有争议之后,由于 YOLOv7 是由开发 Scaled-YOLOv4 的同一作者发布的,机器学习社区似乎很乐意接受这是“官方”YOLO 家族的下一个迭代!
在 YOLOv7 发布的时候,我们——作为微软数据和人工智能服务线的一部分——正在进行一个具有挑战性的基于对象检测的客户项目,这个领域与 COCO 完全不同。不用说,我们和客户都对将 YOLOv7 应用于我们的问题的前景感到非常兴奋。不幸的是,当使用开箱即用的设置时,结果是…这么说吧,不太好。
在阅读了官方论文后,我们发现,虽然它对架构的变化进行了全面的概述,但它忽略了许多关于模型如何被训练的细节;例如,应用了哪些数据扩充技术,以及损失函数如何衡量模型做得好不好!为了理解这些技术细节,我们决定直接调试代码。然而,由于 YOLOv7 库是 YOLOR codebase 的一个派生版本,而 YOLOR code base 本身是 YOLOv5 的一个派生版本,我们发现它包含了很多复杂的功能,其中很多在训练模型时并不需要;例如,能够以 Yaml 格式指定定制架构,并将其转换成 PyTorch 模型。此外,代码库包含许多已经从头实现的自定义组件,如多 GPU 训练循环、若干数据扩充、保存数据加载器工作器的采样器和多学习率调度器,其中许多现在可以在 PyTorch 或其他库中获得。结果,有很多代码需要分析;我们花了很长时间来理解一切是如何工作的,以及训练循环的复杂性,这有助于模型的出色性能!最终,有了这种理解,我们就能够建立我们的训练食谱,在我们的任务中获得持续的好结果。
在本文中,我们打算采用一种实用的方法来演示如何在定制的训练脚本中训练 YOLOv7 模型,以及探索诸如数据扩充技术、如何选择和修改锚盒以及揭示损失函数如何工作等领域;(希望如此!)使你能够建立一种直觉,知道什么可能对你自己的问题有效。由于 YOLOv7 架构在官方文件以及许多其他来源中都有详细的描述,所以我们在这里不打算讨论它。相反,我们打算关注所有其他细节,这些虽然对 YOLOv7 的性能有所贡献,但没有在论文中涉及。这往往是通过多个版本的 YOLO 模型积累起来的知识,但对于刚进入该领域的人来说,要找到这些知识可能非常困难。
为了说明这些概念,我们将使用我们自己的 YOLOv7 实现,它利用了官方的预训练权重,但在编写时考虑了模块化和可读性。这个项目最初是为了让我们更好地了解 YOLOv7 的工作原理,以便更好地理解如何应用它,但在成功地将它用于几个不同的任务后,我们决定将其公开。虽然我们建议使用官方实现,如果你想准确地复制 COCO 上的公布结果,我们发现这种实现更灵活地应用和扩展到自定义域。希望这个实现能够为任何希望在自己的定制培训脚本中使用 YOLOv7 的人提供一个清晰的起点,同时为最初实现中培训时使用的技术提供更多的透明度。
在本文中,我们将讨论:
一路探索所有细节,例如:
Tl;博士: 如果你只想看到一些可以直接使用的工作代码,复制这篇文章所需的所有代码都可以在笔记本 这里 中找到。虽然在整篇文章中使用了代码片段,但这主要是出于美观的目的,请遵从笔记本,而 和 为工作代码。
承认
我们要感谢英国航空公司,如果没有他们持续延误的航班,这篇文章可能就不会出现。
数据加载
首先,让我们看看如何以 YOLOv7 期望的格式加载我们的数据集。
选择数据集
贯穿本文,我们将使用 Kaggle 汽车对象检测数据集;然而,由于我们的目的是演示 YOLOv7 如何应用于任何问题,这实际上是这项工作中最不重要的部分。此外,由于图像与 COCO 非常相似,这将使我们能够在进行任何训练之前,用预训练的模型进行实验。
该数据集的注释采用. csv 文件的形式,该文件将图像名称与相应的注释相关联;其中每行代表一个边界框。虽然在训练集中有大约 1000 幅图像,但是只有那些带有注释的图像才包含在这个文件中。
我们可以通过将它加载到熊猫数据帧中来查看它的格式。

由于我们的数据集中并非所有图像都包含我们试图检测的对象的实例,因此我们还希望包含一些不包含汽车的图像。为此,我们可以定义一个函数来加载注释,其中也包括 100 张“负面”图像。此外,由于指定的测试集是未标记的,让我们随机选取这些图像的 20%作为我们的验证集。
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/examples/train_cars.py
import pandas as pd
import random
def load_cars_df(annotations_file_path, images_path):
all_images = sorted(set([p.parts[-1] for p in images_path.iterdir()]))
image_id_to_image = {i: im for i, im in enumerate(all_images)}
image_to_image_id = {v: k for k, v, in image_id_to_image.items()}
annotations_df = pd.read_csv(annotations_file_path)
annotations_df.loc[:, "class_name"] = "car"
annotations_df.loc[:, "has_annotation"] = True
# add 100 empty images to the dataset
empty_images = sorted(set(all_images) - set(annotations_df.image.unique()))
non_annotated_df = pd.DataFrame(list(empty_images)[:100], columns=["image"])
non_annotated_df.loc[:, "has_annotation"] = False
non_annotated_df.loc[:, "class_name"] = "background"
df = pd.concat((annotations_df, non_annotated_df))
class_id_to_label = dict(
enumerate(df.query("has_annotation == True").class_name.unique())
)
class_label_to_id = {v: k for k, v in class_id_to_label.items()}
df["image_id"] = df.image.map(image_to_image_id)
df["class_id"] = df.class_name.map(class_label_to_id)
file_names = tuple(df.image.unique())
random.seed(42)
validation_files = set(random.sample(file_names, int(len(df) * 0.2)))
train_df = df[~df.image.isin(validation_files)]
valid_df = df[df.image.isin(validation_files)]
lookups = {
"image_id_to_image": image_id_to_image,
"image_to_image_id": image_to_image_id,
"class_id_to_label": class_id_to_label,
"class_label_to_id": class_label_to_id,
}
return train_df, valid_df, lookups
我们现在可以使用这个函数来加载我们的数据:

为了更容易地将预测与图像相关联,我们为每个图像分配了一个唯一的 id;在这种情况下,它只是一个递增的整数计数。此外,我们添加了一个整数值来表示我们想要检测的类,在本例中是一个单独的类,即“car”。
一般物体检测模型都会预留0作为背景类,所以类标签要从1开始。YOLOv7 的情况是而不是,所以我们从0开始我们的类编码。对于不包含汽车的图像,我们不需要类别 id。我们可以通过检查函数返回的查找来确认这一点。

最后,让我们看看我们的训练和验证集的每个类中的图像数量。由于一幅图像可能有多个注释,我们需要确保在计算计数时考虑到这一点:

创建数据集适配器
通常,在这一点上,我们会创建一个 PyTorch 数据集,该数据集特定于我们将要训练的模型。
然而,我们经常使用首先创建数据集‘adaptor’类的模式,单独负责包装底层数据源并适当地加载它。通过这种方式,我们可以在使用不同数据集时轻松切换适配器,而无需更改任何特定于我们正在训练的模型的预处理逻辑。
因此,现在让我们专注于创建一个CarsDatasetAdaptor类,它将特定的原始数据集格式转换成图像和相应的注释。此外,让我们加载我们分配的图像 id,以及我们的图像的高度和宽度,因为它们可能对我们以后有用。
这一点的实现如下所示:
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/examples/train_cars.py
from torch.utils.data import Dataset
class CarsDatasetAdaptor(Dataset):
def __init__(
self,
images_dir_path,
annotations_dataframe,
transforms=None,
):
self.images_dir_path = Path(images_dir_path)
self.annotations_df = annotations_dataframe
self.transforms = transforms
self.image_idx_to_image_id = {
idx: image_id
for idx, image_id in enumerate(self.annotations_df.image_id.unique())
}
self.image_id_to_image_idx = {
v: k for k, v, in self.image_idx_to_image_id.items()
}
def __len__(self) -> int:
return len(self.image_idx_to_image_id)
def __getitem__(self, index):
image_id = self.image_idx_to_image_id[index]
image_info = self.annotations_df[self.annotations_df.image_id == image_id]
file_name = image_info.image.values[0]
assert image_id == image_info.image_id.values[0]
image = Image.open(self.images_dir_path / file_name).convert("RGB")
image = np.array(image)
image_hw = image.shape[:2]
if image_info.has_annotation.any():
xyxy_bboxes = image_info[["xmin", "ymin", "xmax", "ymax"]].values
class_ids = image_info["class_id"].values
else:
xyxy_bboxes = np.array([])
class_ids = np.array([])
if self.transforms is not None:
transformed = self.transforms(
image=image, bboxes=xyxy_bboxes, labels=class_ids
)
image = transformed["image"]
xyxy_bboxes = np.array(transformed["bboxes"])
class_ids = np.array(transformed["labels"])
return image, xyxy_bboxes, class_ids, image_id, image_hw
注意,对于我们的背景图像,我们只是为边界框和类 id 返回一个空数组。
利用这一点,我们可以确认数据集的长度与我们之前计算的训练图像的总数相同。

现在,我们可以用它来可视化我们的一些图像,如下所示:


创建 YOLOv7 数据集
现在我们已经创建了数据集适配器,让我们创建一个数据集,它将我们的输入预处理成 YOLOv7 不管我们使用的适配器是什么,这些步骤都应该保持不变。
这一点的实现如下所示:
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/dataset.py
class Yolov7Dataset(Dataset):
"""
A dataset which takes an object detection dataset returning (image, boxes, classes, image_id, image_hw)
and applies the necessary preprocessing steps as required by Yolov7 models.
By default, this class expects the image, boxes (N, 4) and classes (N,) to be numpy arrays,
with the boxes in (x1,y1,x2,y2) format, but this behaviour can be modified by
overriding the `load_from_dataset` method.
"""
def __init__(self, dataset, transforms=None):
self.ds = dataset
self.transforms = transforms
def __len__(self):
return len(self.ds)
def load_from_dataset(self, index):
image, boxes, classes, image_id, shape = self.ds[index]
return image, boxes, classes, image_id, shape
def __getitem__(self, index):
image, boxes, classes, image_id, original_image_size = self.load_from_dataset(
index
)
if self.transforms is not None:
transformed = self.transforms(image=image, bboxes=boxes, labels=classes)
image = transformed["image"]
boxes = np.array(transformed["bboxes"])
classes = np.array(transformed["labels"])
image = image / 255 # 0 - 1 range
if len(boxes) != 0:
# filter boxes with 0 area in any dimension
valid_boxes = (boxes[:, 2] > boxes[:, 0]) & (boxes[:, 3] > boxes[:, 1])
boxes = boxes[valid_boxes]
classes = classes[valid_boxes]
boxes = torchvision.ops.box_convert(
torch.as_tensor(boxes, dtype=torch.float32), "xyxy", "cxcywh"
)
boxes[:, [1, 3]] /= image.shape[0] # normalized height 0-1
boxes[:, [0, 2]] /= image.shape[1] # normalized width 0-1
classes = np.expand_dims(classes, 1)
labels_out = torch.hstack(
(
torch.zeros((len(boxes), 1)),
torch.as_tensor(classes, dtype=torch.float32),
boxes,
)
)
else:
labels_out = torch.zeros((0, 6))
try:
if len(image_id) > 0:
image_id_tensor = torch.as_tensor([])
except TypeError:
image_id_tensor = torch.as_tensor(image_id)
return (
torch.as_tensor(image.transpose(2, 0, 1), dtype=torch.float32),
labels_out,
image_id_tensor,
torch.as_tensor(original_image_size),
)
让我们使用这个数据集包装我们的数据适配器,并检查一些输出:

由于我们没有定义任何转换,输出基本上是相同的,主要的例外是盒子现在是规范化的 cxcywh 格式,并且我们所有的输出都被转换成张量。注意cx,cy代表中心 x 和 y,这意味着坐标对应于盒子的中心。
需要注意的一点是,我们的标签采用了[0, class_id, ncx, ncy, nw, nh]的形式。张量开始处的零空间将被 collate 函数稍后使用。
转换
现在,让我们定义一些转换!为此,我们将使用优秀的albuminations 库,它提供了许多转换图像和边界框的选项。
虽然我们选择的转换很大程度上是领域特定的,但是在这里,我们将定义与原始实现中使用的转换相似的转换。
这些是:
- 在保持纵横比的同时,根据给定的输入(640 的倍数)调整图像大小
- 如果图像不是正方形,应用填充。为此,我们将遵循纸在使用灰色填充,这是一个任意的选择。
培训期间:
- 水平翻转。
我们可以使用下面的函数来创建这些转换,如下所示:
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/dataset.py
def create_yolov7_transforms(
image_size=(640, 640),
training=False,
training_transforms=(A.HorizontalFlip(p=0.5),),
):
transforms = [
A.LongestMaxSize(max(image_size)),
A.PadIfNeeded(
image_size[0],
image_size[1],
border_mode=0,
value=(114, 114, 114),
),
]
if training:
transforms.extend(training_transforms)
return A.Compose(
transforms,
bbox_params=A.BboxParams(format="pascal_voc", label_fields=["labels"]),
)
现在,让我们重新创建数据集,这一次传递将在评估期间使用的默认转换。对于我们的目标图像尺寸,我们将使用640,这是较小的 YOLOv7 模型的训练值。一般来说,我们可以选择 8 的任意倍数。


使用这些变换,我们可以看到我们的图像已经被调整到我们的目标大小,并且应用了填充。使用填充的原因是,我们可以保持图像中对象的长宽比,但在我们的数据集中图像有一个共同的大小;使我们能够高效地批量处理它们!
使用预训练模型
既然我们已经探索了如何加载和准备我们的数据,让我们继续看看我们如何利用预训练模型来进行一些预测!
加载模型
为了理解如何与模型交互,让我们加载一个预训练的检查点,并使用它对数据集中的一些图像进行推断。由于这个检查点是在包含汽车图像的 COCO 上训练的,我们可以假设这个模型在开箱即用的情况下应该在这个任务上表现得相当好。为了查看可用的模型,我们可以导入AVAILABLE_MODELS变量。

在这里,我们可以看到可用的模型是原始论文中定义的体系结构。让我们使用create_yolov7_model函数创建标准的yolov7模型。

现在,让我们来看看模型的预测。向前通过模型将返回 FPN 头给出的原始特征地图,为了将这些转换成有意义的预测,我们可以使用postprocess方法。

考察形状,可以看到模型已经做了 25200 次预测!每个预测都有一个关联的长度为 6 的张量-条目对应于 xyxy 格式的边界框坐标、置信度得分和类别索引。
通常,对象检测模型倾向于做出许多相似的、重叠的预测。虽然有许多方法来处理这个问题,但在最初的论文中,作者使用了非最大抑制 (NMS)来解决这个问题。我们可以使用下面的函数来应用 NMS 以及第二轮置信度阈值。此外,在后处理过程中,我们通常希望过滤置信度低于预定义阈值的任何预测,让我们在此处增加置信度阈值。
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/trainer.py
def filter_eval_predictions(
predictions: List[Tensor],
confidence_threshold: float = 0.2,
nms_threshold: float = 0.65,
) -> List[Tensor]:
nms_preds = []
for pred in predictions:
pred = pred[pred[:, 4] > confidence_threshold]
nms_idx = torchvision.ops.batched_nms(
boxes=pred[:, :4],
scores=pred[:, 4],
idxs=pred[:, 5],
iou_threshold=nms_threshold,
)
nms_preds.append(pred[nms_idx])
return nms_preds

应用 NMS 后,我们可以看到,现在我们只有一个单一的预测这个图像。让我们想象一下这是什么样子:

我们可以看到这个看起来相当不错!来自模型的预测实际上比地面事实更紧密地围绕着汽车!
现在我们有了我们的预测,唯一要注意的是边界框是相对于调整后的图像大小的。为了将我们的预测缩放回原始图像大小,我们可以使用以下函数:
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/trainer.py
def scale_bboxes_to_original_image_size(
xyxy_boxes, resized_hw, original_hw, is_padded=True
):
scaled_boxes = xyxy_boxes.clone()
scale_ratio = resized_hw[0] / original_hw[0], resized_hw[1] / original_hw[1]
if is_padded:
# remove padding
pad_scale = min(scale_ratio)
padding = (resized_hw[1] - original_hw[1] * pad_scale) / 2, (
resized_hw[0] - original_hw[0] * pad_scale
) / 2
scaled_boxes[:, [0, 2]] -= padding[0] # x padding
scaled_boxes[:, [1, 3]] -= padding[1] # y padding
scale_ratio = (pad_scale, pad_scale)
scaled_boxes[:, [0, 2]] /= scale_ratio[1]
scaled_boxes[:, [1, 3]] /= scale_ratio[0]
# Clip bounding xyxy bounding boxes to image shape (height, width)
scaled_boxes[:, 0].clamp_(0, original_hw[1]) # x1
scaled_boxes[:, 1].clamp_(0, original_hw[0]) # y1
scaled_boxes[:, 2].clamp_(0, original_hw[1]) # x2
scaled_boxes[:, 3].clamp_(0, original_hw[0]) # y2
return scaled_boxes

理解损失
在我们开始训练之前,除了模型架构之外,我们还需要一个损失函数,它将使我们能够衡量我们的模型执行得有多好;为了更新我们的参数。由于对象检测是教导模型的一个难题,所以这种模型的损失函数通常相当复杂,YOLOv7 也不例外。在这里,我们将尽最大努力说明其背后的直觉,以促进其理解。
在我们深入研究实际损失函数之前,让我们先了解一些需要理解的背景概念。
锚箱
目标检测的主要困难之一是输出检测框。也就是说,我们如何训练一个模型来创建一个边界框,并在图像中正确定位它?
有几种不同的方法,但 YOLOv7 家族是我们所说的基于主播的模式。在这些模型中,一般的哲学是首先创建许多潜在的包围盒,然后选择最有希望的选项来匹配我们的目标对象;根据需要稍微移动和调整它们的大小,以获得最佳的匹配。
基本思想是,我们在每个图像的顶部绘制一个网格,并且在每个网格交叉点(锚点),基于多个锚点大小生成候选框(锚点框)。也就是说,同一组框在每个锚点重复出现。这样,model 需要学习的任务,稍微调整这些盒子的位置和大小,比从头开始生成盒子要简单。

在锚点样本处生成的锚点框的示例。
然而,这种方法的一个问题是,我们的目标,地面真相,盒子的大小可以变化——从小到大!因此,通常不可能定义一组可以匹配所有目标的锚尺寸。出于这个原因,基于锚的模型架构通常采用特征金字塔网络(FPN)来协助这一点;YOLOv7 就是这种情况。
要素金字塔网络(FPN)
FPNs(在用于对象检测的特征金字塔网络中介绍)背后的主要思想是利用卷积层的性质——减少特征空间的大小并增加初始图像中每个特征的覆盖范围——来输出不同比例的预测。fpn 通常被实现为卷积层的堆栈,正如我们通过检查 YOLOv7 模型的检测头所看到的那样。

虽然我们可以简单地将最终层的输出作为预测,但是由于较深的卷积层隐含地利用来自先前层的信息来学习更多的高级特征,因此它们无法访问如何检测包含在先前层中的较低级特征的信息;这可能导致检测较小对象时性能不佳。
由于这个原因,自上而下的路径和横向连接被添加到常规的自下而上的路径(回旋层的正常流动)。自上而下的路径通过从更高的金字塔等级向上采样空间上更粗糙但语义上更强的特征地图来产生更高分辨率的特征。然后,通过横向连接,用自下而上路径的特征增强这些特征。自底向上的特征映射具有较低层次的语义,但是它的激活被更精确地定位,因为它被二次抽样的次数更少。
总之,FPNs 在多个尺度上提供了语义强的特征,这使得它们非常适合于对象检测。下图显示了 YOLOv7 在其 FPN 中实现的连接:

yolov 7 系列特征提议网络架构的表示。来源: YOLOv7 纸 。
在这里,我们可以看到我们有一个“正常模型”和一个“带辅助头的模型”。这是因为 YOLOv7 家族中一些体型较大的车型在训练时使用了深度监督;也就是说,为了更好地学习任务,他们利用了损失中更深层的输出。稍后我们将进一步探讨这一点。
从图像中,我们可以看到 FPN 中的每个图层(也称为每个 FPN 头)的特征比例是前一个图层的一半(每个引线头及其对应的辅助头的比例相同)。这可以理解为每一个随后的 FPN 头部“看到”的物体都是前一个的两倍大。我们可以通过给每个 FPN 头分配不同步长(网格单元边长)和比例锚尺寸的网格来利用这一点。
例如,基本yolov7模型的锚配置如下所示:

yolov 7 系列主模型中每个 fpn 头的锚网格和不同(默认)锚框尺寸的图示
正如我们所看到的,我们有锚框大小和网格,覆盖了完全不同的尺度:从微小的对象到可以占据整个图像的对象。
现在,我们从概念上理解了这些想法,让我们看看从我们的模型中得出的 FPN 输出,这将用于计算我们的损失。
这些章节直接取自 原 FPN 论文 ,因为我们觉得不需要进一步解释。
分解 FPN 产出
回想一下,当我们之前进行预测时,我们使用模型的postprocess方法将原始 FPN 输出转换成可用的边界框。既然我们理解了 FPN 试图做什么背后的直觉,让我们检查这些原始输出。

我们模型的输出总是一个List[Tensor],其中每个组件对应一个 FPN 的头。对于使用深度监控的型号,辅助头输出在导联头输出之后(每一个的数量总是相同的,线对的两侧顺序相同)。其余的,包括我们在这里使用的,只有导联头输出。

检查每个 FPN 输出的形状,我们可以看到每个输出都有以下尺寸:
[n_images, n_anchor_sizes, n_grid_rows, n_grid_cols, n_features]
其中:
n_images—批次中图像的数量(批次大小)。n_anchor_sizes-与头部相关的锚尺寸(通常为 3)。n_grid_rows——垂直方向上锚的数量,img_height / stride。n_grid_cols-水平方向的锚数量,img_width / stride。n_features-5 + num_classes-
-cx-锚箱中心水平校正。cy-锚箱中心垂直校正。w-锚箱宽度修正。h-锚箱高度修正。obj_score-与锚盒内包含对象的概率成比例的分数。cls_score-每类一个,得分与该对象所属类别的概率成比例。
当这些输出在后处理期间被映射成有用的预测时,我们应用以下操作:
cx、cy:final = 2 * sigmoid(initial) - 0.5
[(∞、∞)、(∞、∞)]→[(0.5,1.5),(-0.5,1.5)]
-模型只能将锚点中心从 0.5 个单元后向前移动 1.5 个单元。请注意,对于损失(即,当我们训练时),我们使用网格坐标。w、h:final = (2 * sigmoid(initial)**2
[(∞、∞)、(∞、∞)] → [(0,4),(0,4)]
——模型可以任意变小,但最多变大 4 倍。更大的物体,在这个范围之外,必须由下一个 FPN 头预测。obj_score:final = sigmoid(initial)
(∞,∞) → (0,1)
-确保分数映射到一个概率。cls_score:final = sigmoid(initial)
(∞,∞) → (0,1)
-确保分数映射到一个概率。
中心先验
现在,很容易看出,如果我们在每个网格的每个定位点放置 3 个定位框,我们最终会得到很多框:3*80*80 + 3*40*40 + 3*20*20=25200准确地说,是每个 640x640px 图像!问题是,这些预测中的大部分都不会包含一个我们归类为“背景”的物体。根据我们需要应用于每个预测的操作顺序,计算很容易堆积起来并减慢训练速度!
为了降低问题的计算成本,YOLOv7 loss 首先找到可能与每个目标框匹配的锚框,并对它们进行不同的处理——这些锚框被称为中心优先锚框。该过程应用于每个 FPN 头,对于每个目标框,一次批量跨越所有图像。
每个锚——我们网格中的坐标——定义一个网格单元;其中我们认为锚点位于其对应网格单元的左上方。随后,每个单元格(边界上的单元格除外)有 4 个相邻的单元格(上、下、左、右)。对于每个 FPN 头部,每个目标框位于网格单元内的某个位置。假设我们有下面的网格,目标框的中心用一个*表示:

基于模型的设计和训练方式,它能够输出的x和y修正量在[-0.5, 1.5]网格单元的范围内。因此,只有最近锚盒的子集能够匹配目标中心。我们选择这些锚框中的一些来代表目标框的之前的中心。
- 对于引线头,我们在之前使用精细中心,这是一个更有针对性的选择。这由每个头的 3 个锚组成:锚与包含目标框中心的单元相关联,旁边是离目标框中心最近的 2 个网格单元的锚。在图中,中心前锚标有
X。

铅检测头的选定中心先验
- 对于辅助头(对于使用深度监控的型号),我们在之前使用粗中心,这是一个针对性较低的选择。这由每个头的 5 个锚组成:包含目标框中心的单元的锚,在所有 4 个相邻网格单元旁边。

辅助探测头的选定中心先验
这种细与粗的区分背后的推理是,辅助头的学习能力低于领头头,因为领头头在网络中的位置更深。因此,我们尽量避免从辅助头可以学习的地方限制太多,以确保我们不会丢失有价值的信息。
类似于坐标校正,模型只能在间隔[0, 4]中对每个锚框的宽度和高度应用乘法修改器。这意味着,它最多可以使锚盒的侧面扩大 4 倍。因此,从被选为中心先验的锚框中,我们过滤那些比目标框大或小 4 倍的锚框。
总之,中心先验由锚框组成,锚框的锚足够靠近目标框中心,并且其边不太偏离目标框边尺寸。
最优运输分配
评估对象检测模型时的困难之一是能够将预测框与目标框进行匹配,以便量化模型是否做得好。
最简单的方法是定义 Union (IoU)阈值上的交集,并基于此做出决定。虽然这通常是可行的,但当存在遮挡、模糊或多个对象非常靠近时,就会出现问题。最优传输分配 (OTA)旨在通过将标签分配视为每个图像的全局优化问题来解决其中一些问题。
主要直觉在于将每个目标框视为k正标签分配的提供者,而将每个预测框视为一个正标签分配或一个背景分配的需求者。k是动态的,依赖于每个目标框。然后,将一个正标签分配从目标盒传送到预测盒具有基于分类和回归的成本。最后,目标是找到一个运输计划(标签分配),使图像的总成本最小化。
这可以使用现成的解算器来完成,但 YOLOv7 实现了 simOTA (在 YOLOX 论文中介绍),这是 OTA 问题的简化版本。以减少标签分配的计算成本为目标,它为每个目标分配具有最低运输成本的𝑘预测盒,而不是解决全局问题。中心先验框被用作该过程的候选者。
这有助于我们进一步筛选可能与真实目标相匹配的模型输出数量。
YOLOv7 损失算法
既然我们已经介绍了 YOLOv7 损耗计算中使用的最复杂的部分,我们可以将使用的算法分解为以下步骤:
- 对于每个 FPN 头(或每个 FPN 头和辅助 FPN 头对,如果使用辅助头):
- 找到中心优先锚盒。
- 通过 simOTA 算法优化候选选择。为此,请始终使用铅 FPN 头。
- 使用预测的对象概率和 Union 上的完全交集 (CIoU)之间的二元交叉熵损失获得对象损失分数,将匹配的目标作为基础事实。如果没有匹配,这是 0。
- 如果有选择的候选锚盒,也计算(否则都是 0):
-盒(或回归)损失,定义为所有候选锚盒与其匹配目标之间的mean(1 - CIoU)。
-分类损失,使用每个锚盒的预测类别概率和匹配目标的真实类别的独热编码向量之间的二进制交叉熵损失。 - 如果模型使用辅助头,将从辅助头获得的每个分量加到相应的主损耗分量上(即
x = x + aux_wt*aux_x)。贡献权重(aux_wt)由预定义的超参数定义。 - 将目标损失乘以相应的 FPN 头权重(预定义的超参数)。
2.将每个损失成分(客体、分类、回归)乘以其贡献权重(预定义的超参数)。
3.合计已经加权的损失部分。
4.将最终损失值乘以批量。
作为一个技术细节,评估期间报告的损失通过跳过 simOTA 和从不使用辅助头在计算上更便宜,即使对于时尚深度监控的模型也是如此。
虽然这个过程包含很多复杂性,但在实践中,这些都被封装在一个类中,该类可以如下所示创建:

微调模型
现在,我们已经了解了如何使用预训练模型进行预测,以及我们的损失函数如何衡量这些预测的质量,让我们看看如何根据自定义任务对模型进行微调。为了获得论文中报告的性能水平,YOLOv7 使用各种技术进行了训练。然而,出于我们的目的,在逐步引入不同的技术之前,让我们从所需的尽可能少的训练循环开始。
为了处理训练循环的样板文件,让我们使用 PyTorch 加速的。这将使我们能够只定义与我们的用例相关的训练循环的部分,而不必管理所有的样板文件。为此,我们可以覆盖默认 PyTorch 加速 [Trainer](https://pytorch-accelerated.readthedocs.io/en/latest/trainer.html#pytorch_accelerated.trainer.Trainer)的部分内容,并创建一个特定于 YOLOv7 型号的训练器,如下所示:
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/trainer.py
from pytorch_accelerated import Trainer
class Yolov7Trainer(Trainer):
YOLO7_PADDING_VALUE = -2.0
def __init__(
self,
model,
loss_func,
optimizer,
callbacks,
filter_eval_predictions_fn=None,
):
super().__init__(
model=model, loss_func=loss_func, optimizer=optimizer, callbacks=callbacks
)
self.filter_eval_predictions = filter_eval_predictions_fn
def training_run_start(self):
self.loss_func.to(self.device)
def evaluation_run_start(self):
self.loss_func.to(self.device)
def train_epoch_start(self):
super().train_epoch_start()
self.loss_func.train()
def eval_epoch_start(self):
super().eval_epoch_start()
self.loss_func.eval()
def calculate_train_batch_loss(self, batch) -> dict:
images, labels = batch[0], batch[1]
fpn_heads_outputs = self.model(images)
loss, _ = self.loss_func(
fpn_heads_outputs=fpn_heads_outputs, targets=labels, images=images
)
return {
"loss": loss,
"model_outputs": fpn_heads_outputs,
"batch_size": images.size(0),
}
def calculate_eval_batch_loss(self, batch) -> dict:
with torch.no_grad():
images, labels, image_ids, original_image_sizes = (
batch[0],
batch[1],
batch[2],
batch[3].cpu(),
)
fpn_heads_outputs = self.model(images)
val_loss, _ = self.loss_func(
fpn_heads_outputs=fpn_heads_outputs, targets=labels
)
preds = self.model.postprocess(fpn_heads_outputs, conf_thres=0.001)
if self.filter_eval_predictions is not None:
preds = self.filter_eval_predictions(preds)
resized_image_sizes = torch.as_tensor(
images.shape[2:], device=original_image_sizes.device
)[None].repeat(len(preds), 1)
formatted_predictions = self.get_formatted_preds(
image_ids, preds, original_image_sizes, resized_image_sizes
)
gathered_predictions = (
self.gather(formatted_predictions, padding_value=self.YOLO7_PADDING_VALUE)
.detach()
.cpu()
)
return {
"loss": val_loss,
"model_outputs": fpn_heads_outputs,
"predictions": gathered_predictions,
"batch_size": images.size(0),
}
def get_formatted_preds(
self, image_ids, preds, original_image_sizes, resized_image_sizes
):
"""
scale bboxes to original image dimensions, and associate image id with predictions
"""
formatted_preds = []
for i, (image_id, image_preds) in enumerate(zip(image_ids, preds)):
# image_id, x1, y1, x2, y2, score, class_id
formatted_preds.append(
torch.cat(
(
scale_bboxes_to_original_image_size(
image_preds[:, :4],
resized_hw=resized_image_sizes[i],
original_hw=original_image_sizes[i],
is_padded=True,
),
image_preds[:, 4:],
image_id.repeat(image_preds.shape[0])[None].T,
),
1,
)
)
if not formatted_preds:
# if no predictions, create placeholder so that it can be gathered across processes
stacked_preds = torch.tensor(
[self.YOLO7_PADDING_VALUE] * 7, device=self.device
)[None]
else:
stacked_preds = torch.vstack(formatted_preds)
return stacked_preds
我们的训练步骤非常简单,唯一的修改是我们需要从返回的字典中提取总损失。对于评估步骤,我们首先计算损失,然后检索检测。
评估逻辑
为了评估我们的模型在这个任务上的性能,我们可以使用平均精度(mAP);对象检测任务的标准度量。也许最广泛使用(和信任)的 mAP 实现是包含在 PyCOCOTools 包中的类,它用于评估官方 COCO 排行榜提交。
然而,由于它没有最具创意的界面,我们围绕它创建了一个简单的包装器,使它更加用户友好。此外,对于 COCO 竞赛排行榜之外的许多案例,使用固定的借据阈值评估预测可能是有利的,而不是默认使用的借据范围,我们在评估器中添加了一个选项来执行此操作。
为了封装我们的评估逻辑以便在训练中使用,让我们为这个创建一个回调;其将在每个评估步骤结束时被更新,然后在每个评估时期结束时被计算。
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/evaluation/calculate_map_callback.py
from pytorch_accelerated.callbacks import TrainerCallback
class CalculateMeanAveragePrecisionCallback(TrainerCallback):
"""
A callback which accumulates predictions made during an epoch and uses these to calculate the Mean Average Precision
from the given targets.
.. Note:: If using distributed training or evaluation, this callback assumes that predictions have been gathered
from all processes during the evaluation step of the main training loop.
"""
def __init__(
self,
targets_json,
iou_threshold=None,
save_predictions_output_dir_path=None,
verbose=False,
):
"""
:param targets_json: a COCO-formatted dictionary with the keys "images", "categories" and "annotations"
:param iou_threshold: If set, the IoU threshold at which mAP will be calculated. Otherwise, the COCO default range of IoU thresholds will be used.
:param save_predictions_output_dir_path: If provided, the path to which the accumulated predictions will be saved, in coco json format.
:param verbose: If True, display the output provided by pycocotools, containing the average precision and recall across a range of box sizes.
"""
self.evaluator = COCOMeanAveragePrecision(iou_threshold)
self.targets_json = targets_json
self.verbose = verbose
self.save_predictions_path = (
Path(save_predictions_output_dir_path)
if save_predictions_output_dir_path is not None
else None
)
self.eval_predictions = []
self.image_ids = set()
def on_eval_step_end(self, trainer, batch, batch_output, **kwargs):
predictions = batch_output["predictions"]
if len(predictions) > 0:
self._update(predictions)
def on_eval_epoch_end(self, trainer, **kwargs):
preds_df = pd.DataFrame(
self.eval_predictions,
columns=[
XMIN_COL,
YMIN_COL,
XMAX_COL,
YMAX_COL,
SCORE_COL,
CLASS_ID_COL,
IMAGE_ID_COL,
],
)
predictions_json = self.evaluator.create_predictions_coco_json_from_df(preds_df)
self._save_predictions(trainer, predictions_json)
if self.verbose and trainer.run_config.is_local_process_zero:
self.evaluator.verbose = True
map_ = self.evaluator.compute(self.targets_json, predictions_json)
trainer.run_history.update_metric(f"map", map_)
self._reset()
@classmethod
def create_from_targets_df(
cls,
targets_df,
image_ids,
iou_threshold=None,
save_predictions_output_dir_path=None,
verbose=False,
):
"""
Create an instance of :class:`CalculateMeanAveragePrecisionCallback` from a dataframe containing the ground
truth targets and a collections of all image ids in the dataset.
:param targets_df: DF w/ cols: ["image_id", "xmin", "ymin", "xmax", "ymax", "class_id"]
:param image_ids: A collection of all image ids in the dataset, including those without annotations.
:param iou_threshold: If set, the IoU threshold at which mAP will be calculated. Otherwise, the COCO default range of IoU thresholds will be used.
:param save_predictions_output_dir_path: If provided, the path to which the accumulated predictions will be saved, in coco json format.
:param verbose: If True, display the output provided by pycocotools, containing the average precision and recall across a range of box sizes.
:return: An instance of :class:`CalculateMeanAveragePrecisionCallback`
"""
targets_json = COCOMeanAveragePrecision.create_targets_coco_json_from_df(
targets_df, image_ids
)
return cls(
targets_json=targets_json,
iou_threshold=iou_threshold,
save_predictions_output_dir_path=save_predictions_output_dir_path,
verbose=verbose,
)
def _remove_seen(self, labels):
"""
Remove any image id that has already been seen during the evaluation epoch. This can arise when performing
distributed evaluation on a dataset where the batch size does not evenly divide the number of samples.
"""
image_ids = labels[:, -1].tolist()
# remove any image_idx that has already been seen
# this can arise from distributed training where batch size does not evenly divide dataset
seen_id_mask = torch.as_tensor(
[False if idx not in self.image_ids else True for idx in image_ids]
)
if seen_id_mask.all():
# no update required as all ids already seen this pass
return []
elif seen_id_mask.any(): # at least one True
# remove predictions for images already seen this pass
labels = labels[~seen_id_mask]
return labels
def _update(self, predictions):
filtered_predictions = self._remove_seen(predictions)
if len(filtered_predictions) > 0:
self.eval_predictions.extend(filtered_predictions.tolist())
updated_ids = filtered_predictions[:, -1].unique().tolist()
self.image_ids.update(updated_ids)
def _reset(self):
self.image_ids = set()
self.eval_predictions = []
def _save_predictions(self, trainer, predictions_json):
if (
self.save_predictions_path is not None
and trainer.run_config.is_world_process_zero
):
with open(self.save_predictions_path / "predictions.json", "w") as f:
json.dump(predictions_json, f)
现在,我们所要做的就是将我们的回调插入我们的训练器,我们的地图将在每个时期被记录下来!
跑步训练
现在,让我们将目前为止看到的所有内容放入一个简单的培训脚本中。在这里,我们使用了一个简单的训练方法,它适用于各种任务,并且进行了最小的超参数调整。
因为我们注意到这个数据集的基础事实框可以包含对象周围相当多的空间,所以我们决定将用于评估的 IoU 阈值设置得相当低;因为由该模型产生的盒子很可能会更紧地围绕该对象。
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/examples/minimal_finetune_cars.py
import os
import random
from functools import partial
from pathlib import Path
import numpy as np
import pandas as pd
import torch
from func_to_script import script
from PIL import Image
from pytorch_accelerated.callbacks import (
EarlyStoppingCallback,
SaveBestModelCallback,
get_default_callbacks,
)
from pytorch_accelerated.schedulers import CosineLrScheduler
from torch.utils.data import Dataset
from yolov7 import create_yolov7_model
from yolov7.dataset import Yolov7Dataset, create_yolov7_transforms, yolov7_collate_fn
from yolov7.evaluation import CalculateMeanAveragePrecisionCallback
from yolov7.loss_factory import create_yolov7_loss
from yolov7.trainer import Yolov7Trainer, filter_eval_predictions
def load_cars_df(annotations_file_path, images_path):
all_images = sorted(set([p.parts[-1] for p in images_path.iterdir()]))
image_id_to_image = {i: im for i, im in enumerate(all_images)}
image_to_image_id = {v: k for k, v, in image_id_to_image.items()}
annotations_df = pd.read_csv(annotations_file_path)
annotations_df.loc[:, "class_name"] = "car"
annotations_df.loc[:, "has_annotation"] = True
# add 100 empty images to the dataset
empty_images = sorted(set(all_images) - set(annotations_df.image.unique()))
non_annotated_df = pd.DataFrame(list(empty_images)[:100], columns=["image"])
non_annotated_df.loc[:, "has_annotation"] = False
non_annotated_df.loc[:, "class_name"] = "background"
df = pd.concat((annotations_df, non_annotated_df))
class_id_to_label = dict(
enumerate(df.query("has_annotation == True").class_name.unique())
)
class_label_to_id = {v: k for k, v in class_id_to_label.items()}
df["image_id"] = df.image.map(image_to_image_id)
df["class_id"] = df.class_name.map(class_label_to_id)
file_names = tuple(df.image.unique())
random.seed(42)
validation_files = set(random.sample(file_names, int(len(df) * 0.2)))
train_df = df[~df.image.isin(validation_files)]
valid_df = df[df.image.isin(validation_files)]
lookups = {
"image_id_to_image": image_id_to_image,
"image_to_image_id": image_to_image_id,
"class_id_to_label": class_id_to_label,
"class_label_to_id": class_label_to_id,
}
return train_df, valid_df, lookups
class CarsDatasetAdaptor(Dataset):
def __init__(
self,
images_dir_path,
annotations_dataframe,
transforms=None,
):
self.images_dir_path = Path(images_dir_path)
self.annotations_df = annotations_dataframe
self.transforms = transforms
self.image_idx_to_image_id = {
idx: image_id
for idx, image_id in enumerate(self.annotations_df.image_id.unique())
}
self.image_id_to_image_idx = {
v: k for k, v, in self.image_idx_to_image_id.items()
}
def __len__(self) -> int:
return len(self.image_idx_to_image_id)
def __getitem__(self, index):
image_id = self.image_idx_to_image_id[index]
image_info = self.annotations_df[self.annotations_df.image_id == image_id]
file_name = image_info.image.values[0]
assert image_id == image_info.image_id.values[0]
image = Image.open(self.images_dir_path / file_name).convert("RGB")
image = np.array(image)
image_hw = image.shape[:2]
if image_info.has_annotation.any():
xyxy_bboxes = image_info[["xmin", "ymin", "xmax", "ymax"]].values
class_ids = image_info["class_id"].values
else:
xyxy_bboxes = np.array([])
class_ids = np.array([])
if self.transforms is not None:
transformed = self.transforms(
image=image, bboxes=xyxy_bboxes, labels=class_ids
)
image = transformed["image"]
xyxy_bboxes = np.array(transformed["bboxes"])
class_ids = np.array(transformed["labels"])
return image, xyxy_bboxes, class_ids, image_id, image_hw
DATA_PATH = Path("/".join(Path(__file__).absolute().parts[:-2])) / "data/cars"
@script
def main(
data_path: str = DATA_PATH,
image_size: int = 640,
pretrained: bool = True,
num_epochs: int = 30,
batch_size: int = 8,
):
# Load data
data_path = Path(data_path)
images_path = data_path / "training_images"
annotations_file_path = data_path / "annotations.csv"
train_df, valid_df, lookups = load_cars_df(annotations_file_path, images_path)
num_classes = 1
# Create datasets
train_ds = CarsDatasetAdaptor(
images_path,
train_df,
)
eval_ds = CarsDatasetAdaptor(images_path, valid_df)
train_yds = Yolov7Dataset(
train_ds,
create_yolov7_transforms(training=True, image_size=(image_size, image_size)),
)
eval_yds = Yolov7Dataset(
eval_ds,
create_yolov7_transforms(training=False, image_size=(image_size, image_size)),
)
# Create model, loss function and optimizer
model = create_yolov7_model(
architecture="yolov7", num_classes=num_classes, pretrained=pretrained
)
loss_func = create_yolov7_loss(model, image_size=image_size)
optimizer = torch.optim.SGD(
model.parameters(), lr=0.01, momentum=0.9, nesterov=True
)
# Create trainer and train
trainer = Yolov7Trainer(
model=model,
optimizer=optimizer,
loss_func=loss_func,
filter_eval_predictions_fn=partial(
filter_eval_predictions, confidence_threshold=0.01, nms_threshold=0.3
),
callbacks=[
CalculateMeanAveragePrecisionCallback.create_from_targets_df(
targets_df=valid_df.query("has_annotation == True")[
["image_id", "xmin", "ymin", "xmax", "ymax", "class_id"]
],
image_ids=set(valid_df.image_id.unique()),
iou_threshold=0.2,
),
SaveBestModelCallback(watch_metric="map", greater_is_better=True),
EarlyStoppingCallback(
early_stopping_patience=3,
watch_metric="map",
greater_is_better=True,
early_stopping_threshold=0.001,
),
*get_default_callbacks(progress_bar=True),
],
)
trainer.train(
num_epochs=num_epochs,
train_dataset=train_yds,
eval_dataset=eval_yds,
per_device_batch_size=batch_size,
create_scheduler_fn=CosineLrScheduler.create_scheduler_fn(
num_warmup_epochs=5,
num_cooldown_epochs=5,
k_decay=2,
),
collate_fn=yolov7_collate_fn,
)
if __name__ == "__main__":
main()
启动训练如这里所述,使用单个 V100 GPU 并启用 fp16,经过 3 个时期后,我们获得了0.995的地图,这表明模型已经几乎完美地学习了任务!
然而,虽然这是一个伟大的结果,但它在很大程度上是意料之中的,因为 COCO 包含了汽车的图像。
从头开始训练
现在,我们已经成功地微调了一个预训练的 YOLOv7 模型,让我们探索如何从头开始训练这个模型。虽然这可以使用许多不同的训练食谱来完成,但让我们来看看作者在 COCO 上训练时使用的一些关键技术。
镶嵌增强
数据扩充是深度学习中的一项重要技术,其中我们通过在训练期间对我们的数据应用一系列扩充来综合扩展我们的数据集。虽然对象检测中常见的变换往往是增强,如翻转和旋转,但 YOLO 的作者采用了一种略有不同的方法,即应用马赛克增强;之前由 YOLOv4、YOLOv5 和 YOLOX 型号使用。
镶嵌增强的目的是克服对象检测模型倾向于集中于检测朝向图像中心的项目的观察。关键的想法是,如果我们将多幅图像拼接在一起,对象很可能位于通常在数据集中看到的图像中观察不到的位置和上下文中;这将迫使模型学习的特征更加位置不变。
虽然 mosaic 有几个不同的实现,每个都有微小的差异,但这里我们将展示一个组合了四个不同图像的实现。这种实现在过去对我们很有效,有各种各样的对象检测模型。
虽然在创建镶嵌图之前不需要调整图像的大小,但这确实会导致创建的镶嵌图大小相似。因此,我们将在这里采用这种方法。我们可以通过创建一个简单的调整大小转换并将其添加到数据集适配器中来实现这一点。
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/dataset.py
import albumentations as A
def create_base_transforms(target_image_size):
return A.Compose(
[
A.LongestMaxSize(target_image_size),
],
bbox_params=A.BboxParams(format="pascal_voc", label_fields=["labels"]),
)

为了应用我们的增强,我们再次使用白蛋白,它支持许多对象检测转换。
虽然数据扩充通常以函数的形式实现,传递给 PyTorch 数据集并在加载图像后立即应用,但由于镶嵌需要从数据集中加载多幅图像,因此这种方法在这里不起作用。我们决定将 mosaic 实现为数据集包装类,以清晰地封装这一逻辑。我们可以导入并使用它,如下所示:

让我们看一些产生的图像类型的例子。由于我们还没有向镶嵌数据集传递任何调整大小的变换,这些图像相当大。


请注意,虽然镶嵌图像看起来非常不同,但它们都被称为具有相同索引的,因此被应用于相同的图像!创建镶嵌图时,它会从数据集中随机选择另外 3 幅图像,并将它们放置在随机位置,这样每次都会生成不同外观的图像。因此,应用这种增强确实打破了我们对训练时期的概念——数据集中的每幅图像只被看到一次——因为图像可以被看到多次!
因此,在使用 mosaic 进行训练时,我们的策略是不要过多考虑历元的数量,尽可能长时间地训练模型,直到它停止收敛。毕竟,纪元的概念只有在帮助我们跟踪训练时才真正有用——该模型只能看到连续的图像流!
混合增强
镶嵌增强通常与另一种变换一起应用— 混合。为了形象化这是做什么的,让我们暂时禁用马赛克,并启用它自己的混音,我们可以这样做,如下所示:

有意思!我们可以看到,它已经结合了两个图像在一起,这导致了一些'幽灵'寻找汽车和背景!现在,让我们启用两种转换并检查我们的输出。

哇!在我们生成的图像中有相当多的汽车要检测,在许多不同的位置——这对模型来说肯定是一个挑战!请注意,当我们一起应用马赛克和 mixup 时,单个图像与马赛克混合在一起。
镶嵌后仿射变换
正如我们之前提到的,我们正在创建的镶嵌图比我们将用来训练模型的图像尺寸要大得多,所以我们需要在这里做一些大小调整。最简单的方法是在创建马赛克后简单地应用调整大小变换。
虽然这可以工作,但这可能会导致一些非常小的对象,因为我们实际上是将四个图像的大小调整为一个图像的大小——这可能会成为一个问题,因为域已经包含非常小的边界框了!此外,我们的每个马赛克在结构上非常相似,每个象限都有一个图像。回想一下,我们的目标是使模型对位置变化更加健壮,这实际上可能没有多大帮助;因为模型可能只是从每个象限的中间开始寻找。
为了克服这一点,我们可以采取的一种方法是简单地从我们的马赛克中随机选取一部分。这将仍然提供定位的可变性,同时保持目标对象的大小和纵横比。在这一点上,这也可能是一个很好的机会,加入一些其他的变换,如缩放和旋转,以增加更多的可变性。
所使用的精确变换和大小将在很大程度上取决于您所使用的图像,因此我们建议在训练模型之前,先试验这些设置,以确保所有对象仍然可见和可识别!
我们可以定义应用于镶嵌图像的变换,如下所示。这里,我们选择了一系列仿射变换——在我们目标数据的合理范围内——然后是随机裁剪。在最初的实现之后,我们也比 mosaic 更少地应用 mixup。
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/mosaic.py
def create_post_mosaic_transform(
output_height,
output_width,
pad_colour=(0, 0, 0),
rotation_range=(-10, 10),
shear_range=(-10, 10),
translation_percent_range=(-0.2, 0.2),
scale_range=(0.08, 1.0),
apply_prob=0.8,
):
return A.Compose(
[
A.Affine(
cval=pad_colour,
rotate=rotation_range,
shear=shear_range,
translate_percent=translation_percent_range,
scale=None,
keep_ratio=True,
p=apply_prob,
),
A.HorizontalFlip(),
A.RandomResizedCrop(height=output_height, width=output_width, scale=scale_range),
],
bbox_params=A.BboxParams(format="pascal_voc", label_fields=["labels"], min_visibility=0.25),
)




查看这些图像,我们可以看到大量的变化,这些图像现在是用于训练的正确大小。由于我们选择了随机比例,我们还可以看到,并非每个图像看起来都像马赛克,因此这些输出不应与模型在推理过程中看到的图像太不相似。如果使用更极端的增强,例如在训练图像和推断图像之间存在显著差异,则在训练结束前不久禁用这些增强可能是有利的。
在官方实现中,作者在训练期间使用 4 和 9 图像的马赛克。然而,当结合缩放和裁剪来检查这些增强的输出时,在许多情况下,输出看起来非常相似,所以我们选择在这里省略这一点。
将权重衰减应用于参数组
在前面的简单示例中,我们创建了优化器,以便它可以优化我们模型的所有参数。然而,如果我们想跟随作者介绍权重衰减正则化,遵循的使用卷积神经网络进行图像分类的锦囊妙计中给出的指导,这可能不是最佳的;该论文建议权重衰减应该仅应用于卷积和全连接层。
为了在 PyTorch 中实现这一点,我们需要创建两个不同的参数组来进行优化;一个包含我们的卷积权重,另一个包含剩余的参数。我们可以这样做,如下所示:

检查方法定义,我们可以看到这是一个简单的过滤操作:

现在我们可以简单地将这些传递给优化器:
optimizer = torch.optim.SGD(
param_groups["other_params"], lr=0.01, momentum=0.937, nesterov=True
)
optimizer.add_param_group(
{"params": param_groups["conv_weights"], "weight_decay": weight_decay}
)
学习率调度
当训练神经网络时,我们经常希望在训练期间调整我们的学习率的值;这是使用学习率调度器来完成的。虽然有许多流行的时间表,但作者选择了余弦学习率时间表——在训练开始时进行线性热身。它具有以下形状:

余弦学习率计划(带热身)
在实践中,我们发现一段时间的预热和冷却——学习率保持在最小值——通常是这个调度器的一个好策略。此外,调度程序 PyTorch-accelerated 支持一个 k-decay 参数,该参数可用于调整退火的积极程度。
对于这个问题,我们发现使用 k-decay 将学习速率保持在一个更高的值更长的时间效果很好。该时间表以及预热和冷却时间如下所示:

余弦学习率计划(带预热),设置为 k_decay = 2
梯度累积、缩放权重衰减
在训练一个模型的时候,我们使用的批量大小往往是由我们的硬件决定的;因为我们想尽量增加我们可以放在 GPU 上的数据量。但是,必须考虑一些因素:
- 对于非常小的批量,我们无法估计整个数据集的梯度。这可能导致训练不稳定。
- 修改批量大小会导致超参数需要不同的设置,例如学习率和权重衰减。这使得很难找到一组一致的超参数。
为了克服这一点,作者使用了一种称为 梯度累积 的技术,其中来自多个步骤的梯度被累积以模拟更大的批量。例如,假设我们在 GPU 上可以容纳的最大批量是 8。我们可以保存梯度值,继续下一批并添加这些新梯度,而不是在每批结束时更新模型的参数。在指定数量的步骤之后,我们执行更新;如果我们将步骤数设置为 4,这大致相当于使用 32 的批量大小!
在 PyTorch 中,这可以手动执行,如下所示:
num_accumulation_steps = 4
# loop through ennumerated batches
for step, (inputs, labels) in enumerate(data_loader):
model_outputs = model(inputs)
loss = loss_fn(model_outputs, labels)
# normalize loss to account for batch accumulation
loss = loss / num_accumulation_steps
# calculate gradients, these are summed automatically
loss.backward()
if ((step + 1) % num_accumulation_steps == 0) or
(step + 1 == len(data_loader)):
# perform weight update
optimizer.step()
optimizer.zero_grad()
在最初的 YOLOv7 实施中,选择梯度累积步骤的数量,使得总批量(在所有过程中)至少为 64;这缓解了前面讨论的两个问题。此外,作者以下列方式根据批次大小调整重量衰减:
nominal_batch_size = 64
num_accumulate_steps = max(round(nominal_batch_size / total_batch_size), 1)
base_weight_decay = 0.0005
scaled_weight_decay = (
base_weight_decay * total_batch_size * num_accumulate_steps / nominal_batch_size
)
我们可以将这些关系形象化如下:

首先查看累积步骤的数量,我们可以看到累积步骤的数量会减少,直到达到我们的名义批量,然后不再需要梯度累积。

现在来看看所使用的重量衰减量,我们可以看到,在达到标称批量之前,重量衰减量保持在基础值,然后随着批量的增加而线性增加;随着批量变大,应用更多的重量衰减。
模型 EMA
在训练模型时,通过对在整个训练运行中观察到的参数进行移动平均来设置模型权重值可能是有益的,这与使用在最后一次增量更新之后获得的参数相反。这通常通过维护模型参数的指数加权平均值(EMA)来实现,在实践中,这通常意味着维护模型的另一个副本来存储这些平均权重。然而,不是在每个更新步骤之后更新该模型的所有参数,而是使用现有参数值和更新值的线性组合来设置这些参数。
这是使用以下公式完成的:
updated_EMA_model_weights = decay * EMA_model_weights + (1\. - decay) * updated_model_weights
其中衰减是我们设定的参数。例如,如果我们设置 decay=0.99,我们有:
updated_EMA_model_weights = 0.99 * EMA_model_weights + 0.01 * updated_model_wei.99 * EMA_model_weights + 0.01 * updated_model_weights
我们可以看到它保留了 99%的现有状态,只保留了 1%的新状态!
为了理解为什么这可能是有益的,让我们考虑这样的情况,我们的模型在训练的早期阶段,在一批数据上表现得非常差。这可能导致对我们的参数进行大量更新,过度补偿所获得的高损失,这对即将到来的批次是不利的。通过仅并入最新参数的一小部分,大的更新将被“平滑”,并且对模型的权重具有较小的整体影响。有时,这些平均参数有时可以在评估期间产生明显更好的结果,并且这种技术已经在用于流行模型的若干训练方案中使用,例如训练 MNASNet、MobileNet-V3 和 EfficientNet 使用 TensorFlow 中包含的实现。
YOLOv7 作者采用的 EMA 方法与其他实现略有不同,因为它不使用固定的衰减,而是根据更新次数来改变衰减量。我们可以扩展 PyTorch-accelerated 中包含的 ModelEMA 类来实现如下定义的行为:
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/utils.py
from pytorch-accelerated.utils import ModelEma
class Yolov7ModelEma(ModelEma):
def __init__(self, model, decay=0.9999):
super().__init__(model, decay)
self.num_updates = 0
self.decay_fn = lambda x: decay * (
1 - math.exp(-x / 2000)
) # decay exponential ramp (to help early epochs)
self.decay = self.decay_fn(self.num_updates)
def update(self, model):
super().update(model)
self.num_updates += 1
self.decay = self.decay_fn(self.num_updates)
在这里,我们可以看到衰减是通过在每次更新后调用一个函数来设置的。让我们想象一下这是什么样子:

由此可以看出,衰减量随着更新次数的增加而增加,即每个历元一次。
回想上面的公式,这意味着,最初,我们倾向于使用更新的模型权重,而不是历史平均值。然而,随着训练的进行,我们开始加入更多以前时期的平均体重。这与这种技术的通常用法有很大的不同,这种技术的设计是为了帮助 EMA 模型在更早的时期更快地收敛。
选择合适的锚箱尺寸
回想一下之前关于锚框的讨论,以及这些如何在 YOLOv7 如何检测对象方面发挥重要作用,让我们看看如何评估我们选择的锚框是否适合我们的问题,如果不适合,为我们的数据集找到一些明智的选择。
这里的方法很大程度上是从 YOLOv5 中使用的自身抗体方法改编而来的,YOLOv7 中也使用了这种方法。
评估当前锚盒
最简单的方法是简单地使用与 COCO 相同的锚盒,这些锚盒已经与定义的架构捆绑在一起。

这里我们可以看到,我们有 3 个组,每个组对应于特征金字塔网络的每一层。这些数字对应于我们的锚大小,锚框的宽度和高度将被生成。
回想一下,特征金字塔网络(FPN)有三个输出,每个输出的作用是根据对象的比例来检测对象。
例如:
- P3 8 号用于探测较小的物体。
- P4/16 用于探测中等物体。
- P5/32 用于探测更大的物体。
考虑到这一点,我们需要为每一层设置相应的锚点大小。

为了评估我们当前的锚盒,我们可以计算最好的可能召回,如果模型能够成功地将适当的锚盒与基础事实相匹配,这将会发生。
查找和调整地面真实边界框
为了评估锚盒,我们首先需要了解数据集中对象的形状和大小。然而,在我们可以评估之前,我们需要根据我们将在其上训练的图像的大小来调整我们的地面真相框的宽度和高度——对于该架构,这被推荐为 640。
让我们从找出训练集中所有地面真值框的宽度和高度开始。我们可以如下所示计算这些值:

接下来,我们需要图像的高度和宽度。有时候,我们提前掌握了这些信息,在这种情况下,我们可以直接使用这些知识。否则,我们可以这样做:

我们现在可以将其与现有的数据框架合并:

现在,我们可以使用这些信息来获得地面实况目标相对于目标图像大小的调整后的宽度和高度。为了保持图像中对象的纵横比,调整大小的推荐方法是缩放图像,使最长的尺寸等于我们的目标尺寸。我们可以使用下面的函数做到这一点:
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/anchors.py
def calculate_resized_gt_wh(gt_wh, image_sizes, target_image_size=640):
"""
Given an array of bounding box widths and heights, and their corresponding image sizes,
resize these relative to the specified target image size.
This function assumes that resizing will be performed by scaling the image such that the longest
side is equal to the given target image size.
:param gt_wh: an array of shape [N, 2] containing the raw width and height of each box.
:param image_sizes: an array of shape [N, 2] or [1, 2] containing the width and height of the image corresponding to each box.
:param target_image_size: the size of the images that will be used during training.
"""
normalized_gt_wh = gt_wh / image_sizes
target_image_sizes = (
target_image_size * image_sizes / image_sizes.max(1, keepdims=True)
)
resized_gt_wh = target_image_sizes * normalized_gt_wh
tiny_boxes_exist = (resized_gt_wh < 3).any(1).sum()
if tiny_boxes_exist:
print(
f"""WARNING: Extremely small objects found.
{tiny_boxes_exist} of {len(resized_gt_wh)} labels are < 3 pixels in size. These will be removed
"""
)
resized_gt_wh = resized_gt_wh[(resized_gt_wh >= 2.0).any(1)]
return resized_gt_wh

或者,在这种情况下,由于我们所有的图像都是相同的大小,我们可以简单地指定一个图像大小。

请注意,我们还过滤掉了相对于新图像尺寸而言非常小(高度或宽度都小于 3 个像素)的任何框,因为这些框通常太小而没有用!
计算最佳可能回忆
既然我们在训练集中有了所有地面真实框的宽度和高度,我们可以如下评估我们当前的锚框:
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/anchors.py
def calculate_best_possible_recall(anchors, gt_wh):
"""
Given a tensor of anchors and and an array of widths and heights for each bounding box in the dataset,
calculate the best possible recall that can be obtained if every box was matched to an appropriate anchor.
:param anchors: a tensor of shape [N, 2] representing the width and height of each anchor
:param gt_wh: a tensor of shape [N, 2] representing the width and height of each ground truth bounding box
"""
best_anchor_ratio = calculate_best_anchor_ratio(anchors=anchors, wh=gt_wh)
best_possible_recall = (
(best_anchor_ratio > 1.0 / LOSS_ANCHOR_MULTIPLE_THRESHOLD).float().mean()
)
return best_possible_recall

由此,我们可以看到,当前的锚盒很适合这个数据集;这很有道理,因为这些图像与《可可》中的图像非常相似。
这是怎么回事?
在这一点上,你可能想知道,我们到底是如何计算最佳可能的回忆。要回答这个问题,让我们手动完成这个过程。
直觉上,我们希望确保至少有一个锚可以匹配到每个地面真相框。虽然我们可以通过将它框架化为一个优化问题来做到这一点——我们如何将每个地面真相框与其最佳锚匹配——但这将为我们试图做的事情带来很多复杂性。
给定一个锚定盒,我们需要一种更简单的方法来衡量它能在多大程度上适合地面真相盒。让我们研究一种可以做到这一点的方法,从单个地面真相框的宽度和高度开始。

对于每个锚盒,我们可以检查其高度和宽度与地面真实目标的高度和宽度的比率,并使用它来了解最大的差异在哪里。

因为这些比率的比例将取决于锚盒的边是大于还是小于我们的地面真值盒的边,所以我们可以通过计算倒数并取每个锚的最小比率来确保我们的幅度在范围[0,1]内。

由此,我们现在有了一个指示,即每个锚盒的宽度和高度独立地“适合”我们的地面真实目标的程度。
现在,我们的挑战是如何评估宽度和高度的匹配度!
一种方法是,对每个锚取最小比率;代表最不符合我们现实的一方。

我们之所以在这里选择最不合适的一边,是因为我们知道另一边与我们的目标至少以及所选择的一边相匹配;我们可以认为这是最坏的情况!
现在,让我们从这些选项中选择最匹配的锚框,这只是最大的值。

在最差的拟合选项中,这是我们选择的匹配!
回想一下,损失函数只匹配比地面真实目标的大小大或小 4 倍的锚框,我们现在可以验证这个锚是否在这个范围内,并且将被认为是成功的匹配。
我们可以如下所示,取损失倍数的倒数,以确保它与我们的价值在同一范围内:

由此,我们可以看到,至少我们的一个锚可以成功地匹配到我们选择的地面真实目标!
既然我们理解了步骤的顺序,我们现在可以将相同的逻辑应用于我们所有的基础事实框,以查看我们可以用当前的锚集获得多少匹配:

现在我们已经计算了,对于每个地面真值盒,它是否有一个匹配。我们可以取平均匹配数来找出最佳可能回忆;在我们的例子中,这是 1,正如我们前面看到的!

选择新的锚箱
虽然使用预定义锚可能是类似数据集的好选择,但这可能并不适用于所有数据集,例如,包含大量小对象的数据集。在这些情况下,更好的方法可能是选择全新的锚。
让我们探索一下如何做到这一点!
首先,让我们定义我们的架构需要的锚点数量。

现在,基于我们的边界框,我们需要定义一个合理的锚模板的宽度和高度。我们可以估计这一点的一种方法是通过使用 K-means 来根据我们需要的锚大小的数量来聚集我们的地面真实纵横比。然后,我们可以使用这些质心作为我们的开始估计。我们可以使用以下函数来实现这一点:
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/anchors.py
def estimate_anchors(num_anchors, gt_wh):
"""
Given a target number of anchors and an array of widths and heights for each bounding box in the dataset,
estimate a set of anchors using the centroids from Kmeans clustering.
:param num_anchors: the number of anchors to return
:param gt_wh: an array of shape [N, 2] representing the width and height of each ground truth bounding box
"""
print(f"Running kmeans for {num_anchors} anchors on {len(gt_wh)} points...")
std_dev = gt_wh.std(0)
proposed_anchors, _ = kmeans(
gt_wh / std_dev, num_anchors, iter=30
) # divide by std so they are in approx same range
proposed_anchors *= std_dev
return proposed_anchors

在这里,我们可以看到,我们现在有了一组锚模板,可以用作起点。像以前一样,让我们使用这些锚盒来计算我们的最佳可能回忆:

我们再次看到,我们的最佳可能召回率是 1,这意味着这些锚大小也很适合我们的问题!
虽然在这种情况下可能是不必要的,但我们可以使用遗传算法进一步改进这些锚。遵循这种方法,我们可以定义一个适应度(或奖励)函数来衡量我们的锚盒与我们的数据匹配的程度,并对我们的锚大小进行小的随机改变,以尝试并最大化该函数。
在这种情况下,我们可以将我们的适应度函数定义如下:
def anchor_fitness(anchors, wh):
"""
A fitness function that can be used to evolve a set of anchors. This function calculates the mean best anchor ratio
for all matches that are within the multiple range considered during the loss calculation.
"""
best_anchor_ratio = calculate_best_anchor_ratio(anchors=anchors, gt_wh=wh)
return (
best_anchor_ratio
* (best_anchor_ratio > 1 / LOSS_ANCHOR_MULTIPLE_THRESHOLD).float()
).mean()
在这里,我们为每场比赛取最佳锚定比,在损失计算时会考虑。如果一个定位框比它匹配的边界框大或小四倍以上,它不会影响我们的分数。让我们用这个来计算我们建议的锚尺寸的适合度分数:

现在,让我们在优化我们的锚时使用这个作为适应度函数,如下所示:

检查这个函数的定义,我们可以看到,对于指定的迭代次数,我们只是从正态分布中采样随机噪声,并使用它来改变我们的锚大小。如果这一变化导致分数增加,我们将这些作为我们的锚尺寸!
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/yolov7/anchors.py
def evolve_anchors(
proposed_anchors,
gt_wh,
num_iterations=1000,
mutation_probability=0.9,
mutation_noise_mean=1,
mutation_noise_std=0.1,
anchor_fitness_fn=anchor_fitness,
verbose=False,
):
"""
Use a genetic algorithm to mutate the given anchors to try and optimise them based on the given widths and heights of the
ground truth boxes based on the provided fitness function. Anchor dimensions are mutated by adding random noise sampled
from a normal distribution with the mean and standard deviation provided.
:param proposed_anchors: a tensor containing the aspect ratios of the anchor boxes to evolve
:param gt_wh: a tensor of shape [N, 2] representing the width and height of each ground truth bounding box
:param num_generations: the number of iterations for which to run the algorithm
:param mutation_probability: the probability that each anchor dimension is mutated during each iteration
:param mutation_noise_mean: the mean of the normal distribution from which the mutation noise will be sampled
:param mutation_noise_std: the standard deviation of the normal distribution from which the mutation noise will be sampled
:param anchor_fitness_fn: the reward function that will be used during the optimization process. This should accept proposed_anchors and gt_wh as arguments
:param verbose: if True, the value of the fitness function will be printed at the end of each iteration
"""
best_fitness = anchor_fitness_fn(proposed_anchors, gt_wh)
anchor_shape = proposed_anchors.shape
pbar = tqdm(range(num_iterations), desc=f"Evolving anchors with Genetic Algorithm:")
for i, _ in enumerate(pbar):
# Define mutation by sampling noise from a normal distribution
anchor_mutation = np.ones(anchor_shape)
anchor_mutation = (
(np.random.random(anchor_shape) < mutation_probability)
* np.random.randn(*anchor_shape)
* mutation_noise_std
+ mutation_noise_mean
).clip(0.3, 3.0)
mutated_anchors = (proposed_anchors.copy() * anchor_mutation).clip(min=2.0)
mutated_anchor_fitness = anchor_fitness_fn(mutated_anchors, gt_wh)
if mutated_anchor_fitness > best_fitness:
best_fitness, proposed_anchors = (
mutated_anchor_fitness,
mutated_anchors.copy(),
)
pbar.desc = (
f"Evolving anchors with Genetic Algorithm: fitness = {best_fitness:.4f}"
)
if verbose:
print(f"Iteration: {i}, Fitness: {best_fitness}")
return proposed_anchors
让我们看看这是否提高了我们的分数:

我们可以看到,正如我们所预期的那样,我们进化的锚比我们最初提出的锚具有更好的适应度分数!
现在,剩下要做的就是考虑每个锚的最小维度,按照粗略的升序对锚进行排序。

将所有这些放在一起
现在我们已经了解了这个过程,我们可以使用下面的函数在一个步骤中为我们的数据集计算锚。

在这种情况下,由于我们的最佳召回率已经大于阈值,所以我们可以保持原始的锚大小!
但是,如果我们的锚点大小发生变化,我们可以按如下所示进行更新:

跑步训练
现在,我们已经研究了在原始培训配方中使用的一些技术,让我们更新我们的培训脚本,以包括其中的一些功能。更新后的脚本如下所示:
# https://github.com/Chris-hughes10/Yolov7-training/blob/main/examples/train_cars.py
import random
from functools import partial
from pathlib import Path
import numpy as np
import pandas as pd
import torch
from func_to_script import script
from PIL import Image
from pytorch_accelerated.callbacks import (
ModelEmaCallback,
ProgressBarCallback,
SaveBestModelCallback,
get_default_callbacks,
)
from pytorch_accelerated.schedulers import CosineLrScheduler
from torch.utils.data import Dataset
from yolov7 import create_yolov7_model
from yolov7.dataset import (
Yolov7Dataset,
create_base_transforms,
create_yolov7_transforms,
yolov7_collate_fn,
)
from yolov7.evaluation import CalculateMeanAveragePrecisionCallback
from yolov7.loss_factory import create_yolov7_loss
from yolov7.mosaic import MosaicMixupDataset, create_post_mosaic_transform
from yolov7.trainer import Yolov7Trainer, filter_eval_predictions
from yolov7.utils import SaveBatchesCallback, Yolov7ModelEma
def load_cars_df(annotations_file_path, images_path):
all_images = sorted(set([p.parts[-1] for p in images_path.iterdir()]))
image_id_to_image = {i: im for i, im in enumerate(all_images)}
image_to_image_id = {v: k for k, v, in image_id_to_image.items()}
annotations_df = pd.read_csv(annotations_file_path)
annotations_df.loc[:, "class_name"] = "car"
annotations_df.loc[:, "has_annotation"] = True
# add 100 empty images to the dataset
empty_images = sorted(set(all_images) - set(annotations_df.image.unique()))
non_annotated_df = pd.DataFrame(list(empty_images)[:100], columns=["image"])
non_annotated_df.loc[:, "has_annotation"] = False
non_annotated_df.loc[:, "class_name"] = "background"
df = pd.concat((annotations_df, non_annotated_df))
class_id_to_label = dict(
enumerate(df.query("has_annotation == True").class_name.unique())
)
class_label_to_id = {v: k for k, v in class_id_to_label.items()}
df["image_id"] = df.image.map(image_to_image_id)
df["class_id"] = df.class_name.map(class_label_to_id)
file_names = tuple(df.image.unique())
random.seed(42)
validation_files = set(random.sample(file_names, int(len(df) * 0.2)))
train_df = df[~df.image.isin(validation_files)]
valid_df = df[df.image.isin(validation_files)]
lookups = {
"image_id_to_image": image_id_to_image,
"image_to_image_id": image_to_image_id,
"class_id_to_label": class_id_to_label,
"class_label_to_id": class_label_to_id,
}
return train_df, valid_df, lookups
class CarsDatasetAdaptor(Dataset):
def __init__(
self,
images_dir_path,
annotations_dataframe,
transforms=None,
):
self.images_dir_path = Path(images_dir_path)
self.annotations_df = annotations_dataframe
self.transforms = transforms
self.image_idx_to_image_id = {
idx: image_id
for idx, image_id in enumerate(self.annotations_df.image_id.unique())
}
self.image_id_to_image_idx = {
v: k for k, v, in self.image_idx_to_image_id.items()
}
def __len__(self) -> int:
return len(self.image_idx_to_image_id)
def __getitem__(self, index):
image_id = self.image_idx_to_image_id[index]
image_info = self.annotations_df[self.annotations_df.image_id == image_id]
file_name = image_info.image.values[0]
assert image_id == image_info.image_id.values[0]
image = Image.open(self.images_dir_path / file_name).convert("RGB")
image = np.array(image)
image_hw = image.shape[:2]
if image_info.has_annotation.any():
xyxy_bboxes = image_info[["xmin", "ymin", "xmax", "ymax"]].values
class_ids = image_info["class_id"].values
else:
xyxy_bboxes = np.array([])
class_ids = np.array([])
if self.transforms is not None:
transformed = self.transforms(
image=image, bboxes=xyxy_bboxes, labels=class_ids
)
image = transformed["image"]
xyxy_bboxes = np.array(transformed["bboxes"])
class_ids = np.array(transformed["labels"])
return image, xyxy_bboxes, class_ids, image_id, image_hw
DATA_PATH = Path("/".join(Path(__file__).absolute().parts[:-2])) / "data/cars"
@script
def main(
data_path: str = DATA_PATH,
image_size: int = 640,
pretrained: bool = False,
num_epochs: int = 300,
batch_size: int = 8,
):
# load data
data_path = Path(data_path)
images_path = data_path / "training_images"
annotations_file_path = data_path / "annotations.csv"
train_df, valid_df, lookups = load_cars_df(annotations_file_path, images_path)
num_classes = 1
# create datasets
train_ds = DatasetAdaptor(
images_path, train_df, transforms=create_base_transforms(image_size)
)
eval_ds = DatasetAdaptor(images_path, valid_df)
mds = MosaicMixupDataset(
train_ds,
apply_mixup_probability=0.15,
post_mosaic_transforms=create_post_mosaic_transform(
output_height=image_size, output_width=image_size
),
)
if pretrained:
# disable mosaic if finetuning
mds.disable()
train_yds = Yolov7Dataset(
mds,
create_yolov7_transforms(training=True, image_size=(image_size, image_size)),
)
eval_yds = Yolov7Dataset(
eval_ds,
create_yolov7_transforms(training=False, image_size=(image_size, image_size)),
)
# create model, loss function and optimizer
model = create_yolov7_model(
architecture="yolov7", num_classes=num_classes, pretrained=pretrained
)
param_groups = model.get_parameter_groups()
loss_func = create_yolov7_loss(model, image_size=image_size)
optimizer = torch.optim.SGD(
param_groups["other_params"], lr=0.01, momentum=0.937, nesterov=True
)
# create evaluation callback and trainer
calculate_map_callback = (
CalculateMeanAveragePrecisionCallback.create_from_targets_df(
targets_df=valid_df.query("has_annotation == True")[
["image_id", "xmin", "ymin", "xmax", "ymax", "class_id"]
],
image_ids=set(valid_df.image_id.unique()),
iou_threshold=0.2,
)
)
trainer = Yolov7Trainer(
model=model,
optimizer=optimizer,
loss_func=loss_func,
filter_eval_predictions_fn=partial(
filter_eval_predictions, confidence_threshold=0.01, nms_threshold=0.3
),
callbacks=[
calculate_map_callback,
ModelEmaCallback(
decay=0.9999,
model_ema=Yolov7ModelEma,
callbacks=[ProgressBarCallback, calculate_map_callback],
),
SaveBestModelCallback(watch_metric="map", greater_is_better=True),
SaveBatchesCallback("./batches", num_images_per_batch=3),
*get_default_callbacks(progress_bar=True),
],
)
# calculate scaled weight decay and gradient accumulation steps
total_batch_size = (
batch_size * trainer._accelerator.num_processes
) # batch size across all processes
nominal_batch_size = 64
num_accumulate_steps = max(round(nominal_batch_size / total_batch_size), 1)
base_weight_decay = 0.0005
scaled_weight_decay = (
base_weight_decay * total_batch_size * num_accumulate_steps / nominal_batch_size
)
optimizer.add_param_group(
{"params": param_groups["conv_weights"], "weight_decay": scaled_weight_decay}
)
# run training
trainer.train(
num_epochs=num_epochs,
train_dataset=train_yds,
eval_dataset=eval_yds,
per_device_batch_size=batch_size,
create_scheduler_fn=CosineLrScheduler.create_scheduler_fn(
num_warmup_epochs=5,
num_cooldown_epochs=5,
k_decay=2,
),
collate_fn=yolov7_collate_fn,
gradient_accumulation_steps=num_accumulate_steps,
)
if __name__ == "__main__":
main()
再次启动训练,如本文所述,使用单个 V100 GPU 并启用 fp16,在 300 个时期后,我们获得了模型和 EMA 模型的0.997图;比我们的迁移学习运行略有增加,并且可能是在这个数据集上可以实现的最高性能!
结论
希望这已经提供了 YOLOv7 培训过程中一些最有趣的想法的全面概述,以及如何在定制培训脚本中应用这些想法。
复制这篇文章所需的所有代码都可以作为笔记本 在这里 。虽然在整篇文章中使用了代码片段,但这主要是出于美观的目的,请遵从笔记本,而https://github.com/Chris-hughes10/Yolov7-training为工作代码。
克里斯·休斯 和T21【伯纳特】普伊格阵营 都在领英
使用的数据集
这里,我们使用了来自 Kaggle 的汽车物体检测数据集,该数据集作为竞赛六(tjmachinelearning.com)的一部分公开提供。该数据集经常用于学习目的。
虽然这个数据集没有明确的许可,但是我们从作者那里得到了明确的许可,可以将它作为本文的一部分使用。除非另有说明,本文中使用的所有图像都来自该数据集。
参考
- 【2207.02696】yolov 7:可训练的赠品袋为实时物体探测器树立了新的艺术水平(arxiv.org)
- 回应关于 yolov 5(roboflow.com)的争议
- 【2011.08036】Scaled-yolov 4:缩放跨级局部网络(arxiv.org)
- WongKinYiu/yolor:论文的实现——你只学习一种表示法:多任务统一网络(https://arxiv.org/abs/2105.04206)(github.com)
- ultralytics/yolov5: YOLOv5🚀在 py torch>ONNX>CoreML>TF lite(github.com)
- Chris-Hughes 10/yolov 7-training:yolov 7 模型系列的一个干净的模块化实现,它使用官方的预训练权重,并带有用于训练模型执行定制(非 COCO)任务的实用程序。(github.com)
- 【WongKinYiu/yolov7:纸的实现——yolov 7:可训练的免费包为实时物体探测器树立了新的艺术水平(github.com)
- 相册:快速灵活的图像增强
- 非最大抑制解释|论文代码
- 【1612.03144】用于目标检测的特征金字塔网络(arxiv.org)
- Jaccard 索引—维基百科
- 【2103.14259】OTA:目标检测的最佳运输分配(arxiv.org)
- 【2107.08430】YOLOX:2021 年超越 YOLO 系列(arxiv.org)
- Chris-Hughes 10/pytorch-accelerated:一个轻量级库,旨在通过提供一个最小但可扩展的训练循环来加速 py torch 模型的训练过程,该训练循环足够灵活,可以处理大多数用例,并且能够利用不同的硬件选项,而无需更改代码。文件:https://pytorch-accelerated.readthedocs.io/en/latest/(github.com)
- 培训师— pytorch 加速 0.1.3 文档
- 评估措施(信息检索)—维基百科
- pycocotools PyPI
- 回调— pytorch 加速的 0.1.3 文档
- 快速入门— pytorch 加速的 0.1.3 文档
- *【arxiv.org *
- 深度学习基础——体重衰减|作者:Sophia Yang | Analytics vid hya | Medium
- 【1812.01187】利用卷积神经网络进行图像分类的锦囊妙计(arxiv.org)
- 什么是深度学习中的梯度积累?|作者拉兹·罗滕博格|走向数据科学
- Utils — pytorch 加速的 0.1.3 文档
- 遗传算法——GeeksforGeeks
“你不能预测你的模型的错误”…或者你能吗?
原文:https://towardsdatascience.com/you-cant-predict-the-errors-of-your-model-or-can-you-1a2e4a1f38a0
NannyML 已经发布了 DLE,这是一种算法,能够在缺乏基本事实的情况下预测回归模型的平均误差和均方误差

[图片由作者提供]
在 a 之前的文章中,我们已经看到了如何在地面真相可用之前预测你的分类模型的性能。这在现实世界中非常有用,因为它可以为您提供模型在生产中表现如何的早期反馈。
这是由 NannyML 构想的一种算法实现的,称为“基于信心的性能估计”(CBPE),它使用模型预测的概率来获得任何分类指标的可靠估计。一个自然的问题是:
我们能对回归模型也这样做吗?
根据 NannyML 的说法,是的,我们可以。他们开发了一种称为“直接损失估计”(DLE)的方法,允许在无法获得基本事实的情况下,估计任何回归模型的性能(特别是平均绝对误差和均方误差)。
他们声称在这里作为评估性能的一站式解决方案,DLE 优于其他方法,如贝叶斯方法或整合分位数回归。但是除非我们看到,否则我们不会相信,对吗?因此,在本文中,我们将探索 DLE,在真实数据集上尝试它,看看它是否真的像 NannyML 承诺的那样表现良好。
主要思想
DLE 背后的直觉非常简单。简单到好得难以置信。基本上,的想法是直接预测模型产生的误差。
我知道你在想什么。你可能会持怀疑态度,因为你听说过:
"你不能让一个模型预测另一个模型的误差."
但这是真的吗?要回答这个问题,需要从后验分布说起。
介绍后验分布
我们习惯于为每个观察返回单个值的模型,也称为“点预测”。但是,我们必须记住,在点预测的背后,总是有一个完整的分布。如果你喜欢花哨的统计术语,你可以称之为“后验分布”。
后验分布是什么意思?
后验分布给出了预测不确定性的完整描述。
让我们借助一个例子来理解它。
假设我们训练了一个模型,根据一个人的国家、性别、年龄、婚姻状况和工作来预测她的收入。现在,假设我们有 10,000 人,他们都具有以下特征:
- 国家:美国。
- 性别:女。
- 年龄:27。
- 婚姻状况:已婚。
- 工作:销售人员。
当然,即使他们有相同的特征,他们也不会有相同的收入。我们可以把他们收入的分布想象成这种特殊特征组合的后验分布。
让我们用 Python 生成一个后验分布:
**# generate posterior distribution**import numpy as npposterior_distribution = np.random.lognormal(0, .25, 10000) * 50000
这是分布的直方图:

个人收入的后验分布。[图片由作者提供]
知道后验分布最酷的一点是,我们可以计算任何我们想要的东西:百分位数、平均值、中位数……任何东西。
然而,大多数预测模型被设计成获得点预测。的确,当给定上面的 10,000 个人时,你的模型将预测他们每个人的收入是相同的。正如您所猜测的,模型通常被设计用来预测后验分布的平均值。
np.mean(posterior_distribution)
在我们的例子中,这是 50,000 美元。这是模型的预测。

点预测对应于后验分布的平均值。[图片由作者提供]
为什么预测误差没有意义…
对于我们每一个个体,误差是由点预测和真实值之间的差给出的。例如,如果一个人的收入是 65,000 美元,模型产生的误差是-15,000 美元。
error_distribution = point_prediction - posterior_distribution
如果你取平均误差:
mean_error = np.mean(error_distribution)
根据定义,这当然是零。
平均误差为零的事实很有意义。这意味着,平均而言,我们的模型做出了正确的预测,因为正误差抵消了负误差。
在这一点上,很容易理解为什么预测误差没有意义。因为这意味着试图预测定义为空的东西。 *******
但是我们不是说过 DLE 正是建立在预测误差的基础上吗?那么,有什么条件呢?
******* 我们假设模型预测的是后验分布的均值。情况并非总是如此。例如,如果您使用不同于 MSE 的损失函数,您的误差可能不会以 0 为中心。然而,在这种情况下,预测带符号的误差仍然是没有意义的:这就像“纠正”您在一开始选择的损失函数一样。
…但是预测绝对误差很有意义。
重点是 DLE 不预测符号误差,而是预测绝对误差!
这似乎是一个很小的区别,但实际上,这是一个完全不同的故事。事实上,与带符号的错误相反:
绝对误差是我们的点预测的不确定性的度量。
有了后验分布,我们可以计算绝对误差:
absolute_error_distribution = np.abs(point_prediction - posterior_distribution)
如果我们取平均值,我们得到平均绝对误差:
mean_absolute_error = np.mean(absolute_error_distribution)
对于我们模拟的分布,平均绝对误差约为 10,000 美元。这意味着,平均而言,实际收益与我们的模型给出的点预测相差 10,000 美元。

点预测和平均绝对误差。[图片由作者提供]
总而言之:
- 试图预测有符号的错误是没有意义的,因为我们试图纠正一个我们认为是最好的模型。
- 试图预测绝对(平方)误差很有意义,因为我们试图量化与预测相关的不确定性。
请注意,我们的模型的最终性能直接与不确定性相关联。很直观。不确定性越多,表现越差。不确定性越少,性能越好。
描述 DLE 算法
在实践中,DLE 训练了一个新模型,该模型学习与原始模型的预测相关的不确定性。事实上:
- 原始模型进行点预测(一如既往)。
- DLE 引入的模型预测主模型产生的绝对(或平方)误差。NannyML 称之为“保姆模型”(原因很明显)。
我们可以将整个过程总结为 4 个步骤。
像往常一样,一切都从在训练数据集上训练模型开始。

第一步。该模型在训练数据集上学习。[图片由作者提供]
然后,该模型用于对测试数据集进行预测。一旦我们有了预测,我们也可以计算绝对误差,作为目标和预测之间的绝对差:

第二步。该模型对测试数据进行预测,并计算绝对误差。[图片由作者提供]
此时,第二个模型——称为保姆模型——使用原始特征和第一个模型做出的预测来学习关于绝对误差的模式。

第三步。该模型学习预测测试数据的绝对误差。[图片由作者提供]
最后,当在生产中使用模型进行预测、时,我们可以使用第一个模型来预测目标变量,使用保姆模型来预测绝对误差。

第四步。一旦生产数据可用,主模型就对目标进行预测,而保姆模型对绝对误差进行预测。[图片由作者提供]
总而言之,这是整个流程的视图(为了清晰起见,没有区分培训、测试和生产数据集):

直接损失估算的所有步骤。为了简单起见,这里没有区分训练、测试和生产数据集。[图片由作者提供]
在真实数据集上预测性能
让我们看看 DLE 是否在真实数据集上工作。
我们将使用超导体数据集,一个来自 UCI 的开源数据集。该数据集由 21,263 种材料组成,记录了关于原子质量、原子半径、密度等的 81 个特征。目标是预测临界温度。
我们将把数据集分为培训数据集、测试数据集和生产数据集。
**# load data and split into train, test, prod**import pandas as pddf = pd.read_csv(‘/kaggle/input/superconductor-dataset/train.csv’).sample(frac=1, random_state=123)df_train = df.iloc[:int(len(df)/5)]
df_test = df.iloc[int(len(df)/5):int(len(df)/5*2)]
df_prod = df.iloc[int(len(df)/5*2):]X_train = df_train.drop("critical_temp", axis=1)
X_test = df_test.drop("critical_temp", axis=1)
X_prod = df_prod.drop("critical_temp", axis=1)
y_train = df_train["critical_temp"]
y_test = df_test["critical_temp"]
y_prod = df_prod["critical_temp"]
此时,我们已经准备好训练我们的模型:
**# train model**from lightgbm import LGBMRegressormodel = LGBMRegressor().fit(X=X_train,y=y_train)
然后,我们使用测试数据集来计算模型产生的误差。
**# compute observed errors made by the model on test data**pred_test = pd.Series(model.predict(X_test),
index=X_test.index).clip(0)error_test = pred_test — y_test
一旦我们有了错误,我们就可以训练保姆模型。请注意,nanny 模型使用所有原始特征加上第一个模型所做的预测作为特征。
**# train model to predict the absolute error**model_abs_error = LGBMRegressor().fit(
X=pd.concat([X_test, pred_test], axis=1),
y=error_test.abs()
)
在生产数据集上,我们首先使用主模型来获得预测。然后,我们使用保姆模型来获得预测的绝对误差。
**# predict the absolute errors on production data**pred_prod = pd.Series(model.predict(X_prod), index=X_prod.index).clip(0)pred_abs_error_prod = pd.Series(model_abs_error.predict(pd.concat([X_prod, pred_prod], axis=1)), index=X_prod.index)
在这一点上,我们有了对每一次观察的绝对误差的预测。因此,我们最终可以预测生产数据集的平均绝对误差(MAE ),这是我们最初的目标。
**# predict MAE on production set**pred_mae_prod = np.mean(pred_abs_error_prod)
针对数据漂移测试 DLE
DLE 的全部目的是在我们没有基本事实时预测我们模型的平均误差(或均方误差)。这在现实场景中非常有用,因为我们想提前知道模型的性能是否有所下降。
为了模拟这个场景,我们将把生产集分成十个部分。为了重现数据漂移,我们不会随机分割褶皱,而是会根据模型做出的预测进行分割。通过这种方式,我们确保折叠彼此之间有足够的差异,并且跨文件夹的性能有合理的差异。
因此,让我们将每个文件夹上的实际 MAE 与作为保姆模型预测的绝对误差的平均值而获得的 MAE 进行比较。

在测试褶皱上观测 MAE 和预测 MAE 的比较。[图片由作者提供]
结果令人印象深刻。通过保姆模型预测的 MAE 实际上与实际的 MAE 相同。
让我们试试 MSE:

测试褶皱上观测 MSE 和预测 MSE 的比较。[图片由作者提供]
同样在这种情况下,结果是惊人的好。
如果我们试图估计误差(而不是绝对误差),会发生什么?
我们已经看到,从理论的角度来看,试图估计符号误差是没有意义的。但是既然数据科学家更喜欢实践而不是理论,我们还是试着去做吧。
换句话说,这意味着重复上面的过程,但是不是在测试绝对错误上训练保姆模型,而是在测试符号错误上训练它。
在 Python 中,这意味着以下内容:
**# train model to predict the error (which makes no sense)**model_error = LGBMRegressor().fit(
X=pd.concat([X_test, pred_test], axis=1),
y=error_test
)
现在,我们能够预测测试集中每个观察值的有符号误差。如果我们取这些预测,取它们的绝对值,然后平均它们,这是对 MAE 的新估计。
让我们看看它在上述相同的测试折叠中表现如何:

在测试褶皱上观测 MAE 和预测 MAE 的比较。这里预测的 MAE 是从一个基于符号误差而不是绝对误差训练的保姆模型中获得的。[图片由作者提供]
很明显,使用这种新策略,预测的 MAE 系统地低估了实际的 MAE。
如果我们尝试为 MSE 这样做,情况会更糟:

测试褶皱上观测 MSE 和预测 MSE 的比较。这里预测的 MSE 是从一个基于符号误差而不是平方误差训练的保姆模型中获得的。[图片由作者提供]
这只是进一步证明,预测绝对误差与预测带符号误差然后取绝对误差完全不同。
如果你不想重新发明轮子…
在本文中,我们从头实现了 DLE,以展示它是如何工作的。然而,在现实生活中,使用维护良好的库往往更好。
所以,您可能想直接使用 NannyML,它有几个本机功能,比如为您拟合超参数。
**# use DLE directly with NannyML**import nannyml as nmlestimator = nml.DLE(
feature_column_names=features,
y_pred='y_pred',
y_true='critical_temp',
metrics=['mae', 'mse'],
chunk_number=10,
tune_hyperparameters=False
)estimator.fit(df_test)
results = estimator.estimate(df_prod_drifted)
此外,您可以通过一行代码轻松获得关于模型性能的精彩图表:
results.plot(metric=’mae’)

当它不起作用时
当使用 DLE 估计性能时,有一些假设你应该注意。事实上,如果这些假设中的一些被违反,DLE 估计的性能可能不再可靠。这些是假设:
- 没有概念漂移。如果特征和目标变量之间的关系以不可预见的方式改变,你试图预测的误差也会改变,因此保姆模型可能会失败。
- 在输入空间中,没有协变移动到以前看不见的区域。主模型和保姆模型都学习原始特性。如果特征漂移到在训练/验证阶段看不到的值,模型可能做出不正确的猜测。
- 数据的样本足够大。当然,训练和验证数据集都需要足够大,以使两个模型都足够健壮。
总结
在本文中,我们已经看到了当基础事实不可用时,如何可靠地预测回归模型的预期性能(特别是 MAE 和 MSE)。
这种算法被称为“直接损失估计”(DLE),由 NannyML (一个专注于部署后数据科学的开源库)在本文中提出。
DLE 基于直接预测每个单次观测的绝对(或平方)误差的直觉。我们已经在超导体数据集上进行了测试,并获得了出色的结果。

您可以在 Github 资源库中找到本文使用的所有代码。
感谢您的阅读!我希望你喜欢这篇文章。如果你愿意, 在 Linkedin 上加我 !
没有 U,你不能拼写扩散
原文:https://towardsdatascience.com/you-cant-spell-diffusion-without-u-60635f569579
理解稳定扩散的核心构件之一

迈克·彼得鲁奇在 Unsplash 上的照片
就在最近, StabilityAI ,stability Diffusion 背后的公司,发布了他们最新版本的图像生成器。如果从纯文本生成令人难以置信的好图像还不够,该模型现在可以生成更高分辨率的图像,并可以做真正有趣的事情,如使用深度图来生成新颖的图像。
事实上,自其发布以来,没有其他软件被开发者采用得更快。
看稳定扩散发行说明。我想特别提请你们注意一句话:
新的稳定扩散模型(稳定扩散 2.0-v),分辨率为 768x768。U-Net 中的参数数量与 1.5 相同,但是…
U-Net 中相同数量的参数。这个 U-Net 是怎么回事?为什么 U-Net 周围的所有机械都变了,但它在这个模型的机舱中保持不变?今天我们的故事就从这里开始。
U-Net 是这种图像生成模型的核心构件之一。这里有一个过于简单的解释。稳定扩散中的 U-Net 将编码文本(处理成它能理解的格式的纯文本)和一组嘈杂的数字作为输入。经过多次迭代,它从接收到的噪声阵列中产生一个包含可成像信息的阵列。然后,U-Net 的输出被另一个称为解码器的网络用来创建我们都视为输出的图片。
但这根本不是 U-Net 的设计初衷。事实上,早在 2015 年,它就是为医学成像而设计的!那么,它是如何成为最先进的图像生成软件的呢?让我们解构 U 网,一探究竟!
了解 U-Net
为什么叫 U 网?看一眼它的结构就会告诉你答案。

来自[1]
U-Net 只接受一幅图像作为输入,并产生另一幅图像作为输出。更具体地说,给定下面左边的图像,U-Net 产生右边的图像。

来自[1]
这个任务叫做语义分割。给定一幅图像,将每个像素(图像的最小原子单位)分配给它所属的类。
所以,你可以看到,这个神经网络的结构是 u 形的。这种对称是有原因的。“U”的前半部分收缩输入图像,并在每个阶段从中提取有意义的信息(称为特征)。它通过使用卷积神经网络的基本构建模块来实现这一点,这些模块包括卷积层、汇集层和激活层。“U”的后半部分与此过程相反,将中间结果(称为特征图)扩展到越来越大的尺寸,直到最终输出与输入图像的尺寸相同。为此,除了汇集和激活层之外,它还使用了一个称为转置卷积的层(不要说去卷积)。
于是,卷积网络+卷积网络的镜像= U-Net。但是,它有另一个特殊的特点。注意到连接网络前半部分和后半部分的灰色箭头了吗?它们有助于扩张过程。
我们用一个小时候的游戏来进一步理解这一点。
您说什么?
作为一个孩子,我相信你一定玩过电话游戏(又名小道消息交流游戏)。一群孩子站成一排。第一个孩子在第二个孩子耳边小声说了些什么。第二个孩子小声告诉第三个孩子,以此类推。最终,最后一个孩子必须猜出最初耳语的内容。通常情况下,秘密文本在不同的孩子之间变化如此之大,以至于最终版本几乎不像原始版本。
现在,让我们把这个游戏加一个转折。第一个孩子要问一个问题,而不是低声说一句话。最后一个孩子必须回答这个问题。随着这个问题从一个孩子传到另一个孩子,它不断变化,最终,最后一个孩子对“人生的目的是什么?”这个问题的回答是“42”。

作者创建的图像
现在,如果我们改变规则来帮助孩子们呢?(我的意思是,他们毕竟是孩子)根据新的规则,一个孩子在链条的前面可以和后面的孩子交流。但是,他们只能问他们听到的问题。那不是让最后一个孩子回答原问题更容易吗?

作者创建的图像
这就是 U-Net 架构中灰色连接器背后的直觉。
它们帮助模型的后半部分猜测丢失的像素可能是什么。为什么我说缺失像素?在 U-Net 的前半部分,模型有完整的图片(双关语)。每一层都会缩小图片,并在将图片传递给下一层之前,有效地删除原始信息。
网络的后半部分具有将小图片(特征地图)扩展回与网络接收的原始图片相同大小的困难任务。但是,在不知道要在扩展的区域中填充什么的情况下,如何扩展某些内容呢?虽然这是一个值得单独贴出的话题,但可以肯定地说,如果你有背景知识,那么做起来会更容易。这种背景来自灰色的连接器(在我们的游戏链中较早的孩子)。
反向旅程
传统上,独特的神经网络首先在计算机视觉社区中被提出用于图像相关的问题,然后发展到其他领域,如医学成像等。
U-Net 是这一规则的例外之一。它旨在解决医学成像问题,但它是如此通用和有用,以至于它进入了许多计算机视觉问题——从赢得 Kaggle 比赛到从卫星图像中提取道路或建筑物。
今天,它位于稳定扩散的核心,它将高维图像映射到另一个图像的能力使我们只需键入几个词就可以创建令人难以置信的图像。
参考
- U-Net:用于生物医学图像分割的卷积网络【https://arxiv.org/abs/1505.04597v1
- 稳定的扩散采用率:【https://a16z.com/2022/11/16/creativity-as-an-app/
你值得拥有更好的 ROC 曲线
原文:https://towardsdatascience.com/you-deserve-a-better-roc-curve-970617528ce8
通过 ROC 阈值可视化提取更深入的见解

具有可视化决策阈值的 ROC。我们将学习如何阅读它!图片作者。
本文中的所有源代码都可以在 Github 上获得!在任何上下文中随意重用图形,无需注明出处(不保留任何权利)。
动机
接收器操作特性(ROC)曲线对于理解模型的行为是非常宝贵的。不过,这些曲线包含的信息比你通常看到的要多。通过可视化阈值,我们可以更多地了解我们的分类器。另外,保留这些信息可以使 ROC 曲线更加直观。
历史
ROC 曲线是由战时挑战激发的。在第二次世界大战中,人们希望量化雷达系统的行为。让我们考虑雷达解释的四种可能结果:
真阴性(TN) :射程内没有飞机,雷达技师报告没有飞机。唷。
真阳性(TP) :射程内有飞机,雷达技师报告有飞机。至少我们知道它在那里!
假阴性(TN) :射程内有一架飞机,但雷达技师没有报告。啊哦,我们毫无准备。
误报(FP) :范围内没有飞机,但雷达技师报告有飞机。我们在浪费资源准备一架不真实的飞机!

二元分类器结果。图片作者。
显然,前两种结果比后两种好,即真比假好。然而,在现实中,我们不得不进行权衡。
例如,如果我们的雷达技术人员有很多假阴性,我们可以让她的指挥官对她大喊,直到她报告更多出现在她屏幕上的东西。通过降低她报告的“决策门槛”,她会得到更少的假阴性和更多的真阳性。然而,不幸的是,她也更有可能意外报告假阳性。
为了总结雷达“接收器”的“工作特性”的权衡,军事分析家们提出了“接收器工作特性”曲线。
标准 ROC 曲线
怎样才能有意义地总结这四个量之间的关系?一种方法是使用真阳性率(TPR)和假阳性率(FPR)。
TPR 有很多名字。你可能会称之为“敏感”或“回忆”。它是所有实际阳性(TP+FN)的一部分,我们的模型将其标记为阳性。这是我们的雷达技术报告的真正飞机的一部分。相比之下,FPR 是非平面场景(FP+TN)的百分比,我们的雷达技术报告为平面。
ROC 曲线将 TPR 绘制为不同决策阈值下 FPR 的函数。基于这些定义,我们希望在最大化 TPR 的同时最小化我们的 FPR。

定义真阳性率(TPR)和假阳性率(FPR)。图片作者。
实现低 FPR 有一个简单的方法…永远不要报告任何飞机(蓝色)。也有一个简单的方法来实现高 TPR…称一切为飞机(黄色)。不过,理想情况下,我们会两者兼得。我们将拥有 100%的 TPR 和 0%的 FPR。我们的雷达技术人员将正确地报告所有真实的飞机,而不会意外地报告一架非飞机(绿色)。如果我们真的非常非常不擅长我们的工作,我们可能无法正确地报告任何真实的飞机,而把每一个非飞机称为飞机(红色)。

基本 ROC 曲线解释,参见第节。图片作者。
一个好的模型(或雷达技术)会弯向绿色角落。不好的模型(即随机的)会拥抱对角线。一个可笑的坏模型(即,比随机模型更坏)将向红色角落弯曲。
不过,这开始变得抽象了……让我们用具体的雷达例子来说明这一点。

一组样本分类决策的 ROC 曲线。图片作者。
让我们考虑一下我们想象中的雷达技术的六种分类情况,三种飞机和三种非飞机。对于每一项,她都用 0%到 100%的范围来表示自己的信心。在一个给定的阈值处,我们想象我们的雷达技术将关于该阈值的一切都报告为一架飞机。然后,我们使用上面的等式计算 FPR 和 TPR。绘制这些值的结果就是…上图所示的 ROC 曲线!
带阈值的 ROC 曲线
我的问题是:我们使用决策阈值来计算那条绿线,但随后我们丢弃了那条信息!从绿线中,我们无法判断每个点的决策阈值。在部署后选择要使用的决策阈值时,这些信息非常有用。
让我们试着用彩色地图保存这些信息。我还没有在网上为这种 ROC 曲线找到一个好名字…所以我称它为彩虹 ROC。
让我们放下雷达技术的例子,考虑一个真正的分类器。让我们使用虹膜数据集。我们将把三个类的问题视为一个一对其余的问题,结果是每个类有一个 ROC。
首先,我们需要加载数据集并拟合我们的模型。我们将使用基本的 SVM!
数据加载和模型拟合。作者来源。
现在我们有了分类器,我们可以得到分类分数并生成我们的 ROC 曲线。
CLI 处理程序和彩虹 ROC 逻辑。作者来源。
结果是具有可视化决策阈值的 ROC 曲线。基于我们对假阳性率和期望的真阳性率的容忍度,我们可以为我们的用例选择适当的决策阈值。

彩虹 ROC 曲线与彩色地图的决策阈值。图片作者。
结论
彩虹 ROC 帮助我们保持阈值信息,并且,至少对我来说,使得解释 ROC 曲线更容易。感谢阅读!我希望你学到了一些东西。
你不需要神经网络来持续学习
原文:https://towardsdatascience.com/you-dont-need-neural-networks-to-do-continual-learning-2ed3bfe3dbfc
持续学习是 ML 模型随着新数据的到来而不断学习的能力。这是如何用 XGBoost、LightGBM 或 CatBoost 在 Python 中实现的。

从静态学习到持续学习。[图片由作者提供]
持续学习是目前人工智能领域最热门的话题之一。然而,我读到的关于这件事的一切似乎都想当然地认为——为了进行持续的学习——你需要使用神经网络。
我认为这构成了广泛采用持续学习的障碍。事实上,神经网络比其他机器学习方法(例如梯度推进)需要更多的数据准备和调整。此外,它们在涉及表格数据的问题上往往表现不佳。
这就是为什么在本文中,我将向您展示如何在 Python 中实现持续学习,而不必使用神经网络,而是使用 XGBoost、LightGBM 和 Catboost 等库。
静态学习
很多时候,在实际应用中,采用的是静态方式学习。
它或多或少是这样工作的。您根据最新的可用数据(例如,过去 4 周的交易)来训练模型,并使用该模型对新数据进行预测。
过了一段时间,比如说一个星期,你发现你的模型的性能下降了。因此,你可以简单地扔掉现有的模型,然后重复上面同样的过程:获取最近 4 周的数据,训练一个新的模型,等等。
这也叫无状态训练,因为你没有跟踪之前模型的状态。换句话说,你没有记录以前的模型学到了什么。
下图描述了这一过程:

静态学习示例。每周都会构建一个全新的模型(基于前 4 周的数据)。[图片由作者提供]
现在你可能会问:“但是为什么我们每次只使用最近 4 周的数据呢?难道我们不能只取所有的数据,也就是说第一个模型 4 周的数据,第二个模型 5 周的数据…?”
在实践中采用这种方法有许多原因。
- 概念漂移:随着时间的推移,我们试图预测的现象会发生变化,因此使用较旧的数据可能会适得其反。
- 内存约束:如果您需要将数据加载到内存中来训练模型,如果您总是使用最近 4 周的数据,则数据所使用的内存或多或少是恒定的。
- 时间限制:向模型中输入过多的数据会越来越多地延长训练时间。
持续学习
但是,从概念上讲,扔掉旧模型意味着浪费旧模型迄今为止收集的所有知识。理想情况下,我们应该找到一种方法来保留以前的知识,同时逐渐添加来自新数据的信息。这正是不断学习的目的。
按照上面的例子,这是持续学习的结构:

不断学习。现有模型的新版本每周更新一次(基于上周的数据)。[图片由作者提供]
与静态学习不同,在连续学习中,每个数据点仅进入训练程序一次:训练数据集之间没有重叠。
请注意,在这种情况下,就像我们只有一个单一的模型,它每周都会更新。这些不是不同的模型。这些只是同一款的不同版本。这也被称为有状态训练,因为模型的先前状态被保留。
还要注意,这种方法在以下方面具有额外的优势:
- 内存消耗:每周,加载到内存中的数据远远少于静态学习中的数据。
- 时间消耗:训练过程是在较小部分的数据上进行的,因此速度要快得多。
好吧,但是我怎么用 Python 来做呢?
我读过的所有关于持续学习的论文和文章似乎都想当然地认为持续学习等同于深度学习。
从深度学习的角度来看,持续学习包括更新神经网络的权重。换句话说,使用第一组数据,模型学习一组权重,然后在接下来的运行中,权重被稍微调整以适应新的训练数据,等等。这样,模型从旧数据中学习到的东西会随着时间的推移“保留”在模型中。
但在许多 ML 应用中,甚至不使用神经网络,原因有很多:它们更难调整,并且往往比表格数据集上的其他模型(例如,梯度推进模型)表现更差。
因此,重要的是要知道,你也可以在不包括神经网络的项目中进行持续学习。事实上,持续学习已经在一些最流行的机器学习现成库中实现了。
让我们看看其中的三个。
1.XGBoost
假设你已经训练了一个 XGBoost 模型,名为model_old。现在,新的数据到来了,您希望只在新的数据上“更新”旧模型的知识。您只需要将旧模型作为新模型的参数进行传递。为此,您必须使用一个名为xgb_model的参数:
from xgboost import XGBClassifiermodel_new = XGBClassifier().fit(X_new, y_new, xgb_model=model_old)
2.LightGBM
LightGBM 也是如此,但是这个参数叫做init_model:
from lightgbm import LGBMClassifiermodel_new = LGBMClassifier().fit(X_new, y_new, init_model=model_old)
3.CatBoost
相同的语法适用于 CatBoost:
from catboost import CatBoostClassifiermodel_new = CatBoostClassifier().fit(X_new, y_new,
init_model=model_old)
静态学习与真实数据集上的持续学习
让我们在一些真实数据的帮助下让代码变得生动起来。
我们需要的所有进口如下:
import numpy as np
import pandas as pd
from datetime import datetime
from dateutil.relativedelta import relativedelta
from catboost import CatBoostClassifier
from sklearn.metrics import roc_auc_score
1.数据集
我使用了 Kaggle (CC0:公共领域许可)的干旱数据集。该数据集是 2000 年至 2016 年间美国所有县的天气数据和干旱信息的时间序列。
数据集由 21 列组成:
- FIPS:美国每个县的唯一 ID(总共有 3108 个不同的县)。
- 日期:从 2000 年 1 月 1 日到 2016 年 12 月 31 日的每一天(共 6210 天)。
- 一组 18 个天气变量,如降水量、地面气压、湿度等。
- 得分:介于 0 和 5 之间的分数,是干旱强度的量度。
以下是数据集前 5 行的屏幕截图:

干旱数据集的前 5 行。[图片由作者提供]
任务是预测是否会有干旱。
因为我们正在做一个小实验,所以我们将只使用所有数据的一个子集:我们将只保留从最初的 3108 个县中随机选择的 100 个县,并且我们将只保留 2014 年 1 月 1 日以后的数据。
而且我们会把数值型的目标变量变成二元变量(不管干旱的分数是否大于 0),把这个变成二元分类问题。
**# import original data** df = pd.read_csv("/kaggle/input/us-drought-meteorological-data/train_timeseries/train_timeseries.csv")**# select 100 fips at random and keep only data starting from 2014** np.random.seed = 123
some_fips = np.random.choice(df["fips"].unique(), size=100,
replace=False)df = df.loc[df["fips"].isin(some_fips), :]
df = df.loc[df["date"]>="2014", :]**# change "date" dtype from string to datetime** df.loc[:,"date"] = pd.to_datetime(df.loc[:,"date"])**# turn continuous score into binary target** df.loc[:, "score"] = (df.loc[:, "score"].fillna(0)>0).astype(int)
现在,让我们选择将在培训过程中使用的功能:
**# set name of target column**
target = "score"**# set names of features**
features = ["fips", "PRECTOT", "PS", "QV2M", "T2M", "T2MDEW",
"T2MWET", "T2M_MAX", "T2M_MIN", "T2M_RANGE", "TS", "WS10M",
"WS10M_MAX", "WS10M_MIN", "WS10M_RANGE", "WS50M", "WS50M_MAX",
"WS50M_MIN", "WS50M_RANGE"]**# set position of categorical features (in our case, just "fips")**
cat_features = [0]
2.静态学习
现在,假设我们每个月都想得到一个新的模型。按照静态学习的方法,这将意味着,在每个月的第一天,我们必须根据前一年的所有数据训练一个全新的模型。
让我们重复这个过程 24 次(即 2 年)。关于预测模型,我将使用 CatBoost。这是代码的样子。
**### static learning**train_to = datetime(2015,1,1)for i in range(24): train_from = train_to - relativedelta(years=1) train_index = (df["date"] >= train_from) & (df["date"] < train_to)
model = CatBoostClassifier().fit(
X=df.loc[train_index, features],
y=df.loc[train_index, target],
cat_features=cat_features) train_to = train_to + relativedelta(months=1)
在每个迭代的末尾,我们将培训日期提前一个月。为了清楚起见,我已经打印出了前 5 次迭代的训练周期:

静态学习前 5 次迭代的训练日期。[图片由作者提供]
3.持续学习
现在让我们应用同样的逻辑,但是使用持续学习。这意味着我们仍将每月把最大训练日期提前一个月。但是,这一次,我们将只使用上个月的数据,因为模型已经嵌入了它以前学习过的模式。
**### continual learning**train_to = datetime(2015,1,1)model = Nonefor i in range(24): if i == 0:
train_from = train_to - relativedelta(years=1)
else:
train_from = train_to - relativedelta(months=1) train_index = (df["date"] >= train_from) & (df["date"] < train_to)
model = CatBoostClassifier().fit(
X=df.loc[train_index, features],
y=df.loc[train_index, target],
cat_features=cat_features,
init_model = model)
train_to = train_to + relativedelta(months=1)
这是前 5 个训练阶段:

持续学习的前 5 次迭代的培训日期。[图片由作者提供]
在第一次迭代时(当i=0),我们没有之前训练过的模型(这等于model=None)。因此,培训期为一年:从 2014 年 1 月 1 日至 2014 年 12 月 31 日。如果你看看上面,你会注意到这在静态学习中是完全一样的。
但是,差异从第二次迭代开始(当i>=1)。事实上,model现在是一个合适的 CatBoost 模型,因此我们可以从我们离开的地方继续训练。事实上,之前的模型已经在除了上个月之外的所有之前的数据上进行了训练。所以我们只需要继续对上个月的数据进行训练。
为了更加清晰,让我们来比较两段代码:

用于静态学习和持续学习的代码的比较。[截图来自 https://text-compare.com/
4.结果
既然我们已经看到了静态学习和持续学习的区别,对这两种方法的表现感到好奇是很自然的。
为了看到这一点,我们可以计算每个模型的 ROC 曲线下的面积。在我们的例子中,测试集是最大训练日期之后的一个月。
当然,这两种方法的测试数据集是相同的。因此,将下面这段代码添加到for循环中就足够了:
test_from = train_totest_to = test_from + relativedelta(months=1)test_index = (df["date"] >= test_from) & (df["date"] < test_to)
roc_auc_score(
df.loc[test_index, target],
model.predict_proba(df.loc[test_index, features])[:,1])
这些是前 5 次迭代的测试日期:

两种方法的前 5 次迭代的测试日期。[图片由作者提供]
这些是我在每种方法的 24 次迭代中记录的roc_auc_score:

干旱数据集上的静态学习与持续学习。每种方法都有 24 个模型根据维持数据进行了训练和评估。[图片由作者提供]
一些统计数据:
- 在 24 次迭代中的 17 次(即 71%的时间),持续学习比静态学习表现得更好(即 ROC 更高)。
- 静态学习平均得分为 60.5%,而持续学习平均得分为 65.0%。
当然,这只是一个实验,但它让我们得出结论:持续学习可能比静态学习表现更好,在不涉及神经网络(或深度学习)的应用中也是如此。
综上
在本文中,我们看到了如何使用开箱即用的 ML 模型(如 XGBoost、LightGBM 和 CatBoost)在 Python 中执行持续学习。
此外,在真实数据集的帮助下,我们已经看到,除了具有其他实际优势(如更低的内存消耗和更快的训练时间)之外,持续学习可能会给出比静态学习明显更好的结果。

如果你有兴趣加深对持续学习的了解,我建议你阅读 Chip Huyen 的 惊人博客,这篇文章就是受其启发而写的。

感谢您的阅读!我希望你喜欢这篇文章。如果你愿意, 在 Linkedin 上加我 !
不需要样本数据,需要 Python Faker
原文:https://towardsdatascience.com/you-dont-need-sample-data-you-need-python-faker-fa87c2a119a9

图片来自 Pixabay 的 anncapictures
一个可扩展的 Python 库,它生成假数据来“填充”你的项目
Python 有一个内置模块“random”,允许我们随机生成多种类型的数据,比如数字和字符串。但是,它不能生成任何“有意义”的数据,如人名。有时,当我们需要生成一些虚拟数据以方便演示或实验时,将人名命名为“Christopher Tao”会比“Llisdfkjwe Asdfsdf”好得多:)
一个常见的解决方案可能是从开源数据集中下载一些样本数据。然而,如果我们对数据的分布或虚拟数据的特定模式没有任何偏好,那么最好和最简单的解决方案就是生成假数据。
在本文中,我将介绍这样一个 Python 第三方库——Faker。它可以生成各种类型的假数据,而不仅仅是姓名。现在我们应该开始了。
1.基础

图片来自 Pixabay 的sarajughernaut
首先,我们需要使用pip安装库,如下所示。
pip install Faker
然后,让我们从一些基本的使用模式开始。在生成任何假数据之前,我们需要如下实例化 Faker 的对象。
from faker import Faker
fake = Faker()
一旦我们有了“假”实例,一切都很简单。比如生成一个人的名字,我们就用它的name()方法。
fake.name()

要生成一个地址,我们可以使用它的address()方法。
fake.address()

我们也可以使用 Faker 来生成一些文本。这没有任何意义,但至少看起来像真实的句子。这个阶段不要期望太高,这里没有机器学习模型这样的魔法。一切都是基于随机性。
fake.text()

在 Faker 库术语中,上述每种类型的“生成器”都被称为“提供者”。供应商太多了,我无法一一列举。然而,当您继续阅读时,可以在本文的其余部分找到一些新的提供者。如果您想获得它们的完整列表,文档总是您的好朋友。
2.批量生成

图片由 S .赫尔曼& F .里克特从皮克斯拜拍摄
如果我们每次只能生成一个假数据的话,用处不会很大。因此,了解我们可以批量生成假数据是很重要的。
2.1 使用 For 循环
假设我们想要生成一些虚假的用户资料。每个配置文件应该包含用户的名字/姓氏,家庭住址,职位和他们工作的公司。最简单的方法是将所有内容放入一个 for 循环,如下所示。
for _ in range(3):
print('Name:', fake.name())
print('Address:', fake.street_address())
print('Job:', fake.job())
print('Company:', fake.company())
print()

可以看出,虽然我们只是重复调用这些方法,但配置文件是不同的。Faker 的内部机制是基于随机性的。因此,我们每次生成的假数据也是随机的。
2.2 使用内置提供程序
Fake 不仅能够每次生成一种类型的数据。以用户配置文件为例,我们实际上不必单独生成每个字段,因为 Faker 有能力生成假的配置文件。就调用它的fake.profile()方法。
import pprintfor _ in range(3):
pprint.pprint(fake.profile())

2.3 生成熊猫数据框架
使用像profile()这样的高级伪提供者的好处在于,它为我们生成字典。这意味着我们可以将它与熊猫等其他工具无缝集成。
例如,我们想在熊猫数据框架中生成用户配置文件。Faker 的设计让它变得非常容易。从字面上看,只有一行代码。
import pandas as pdpd.DataFrame([fake.profile() for _ in range(10)])

点击图像放大
再贴一张有部分栏目的截图,以防上面的完整截图在你的设备上不太可读

3.扩展提供程序

Faker 有许多内置的提供程序,可以在大多数时候满足我们的要求。这些可以在官方文档中获得。
然而,如果事实证明我们找不到我们需要的提供商呢?不要担心,Faker 已经开放了它的“协议”,以便社区可以贡献更多的提供商。在 Faker,他们称这些为“扩展提供商”。这是目前为止他们的名单。
我将选择这辆车作为展示的例子。Faker 无法生成车辆品牌、型号等,但已经有一个扩展提供商可以提供帮助。
首先,根据文档,我们需要首先安装扩展的提供程序。也可以用pip安装。
pip install faker_vehicle
然后,我们需要向 faker 实例注册这个扩展提供者。
from faker_vehicle import VehicleProviderfake.add_provider(VehicleProvider)
完成后,我们现在就可以开始使用它了。生成汽车制造商的简单示例。
fake.vehicle_make()

这个扩展提供程序也有一些高级方法。例如,我们可以使用方法fake.vehicle_year_make_model()生成汽车的轮廓。
for _ in range(5):
print(fake.vehicle_year_make_model())

还注意到品牌和型号可以匹配。非常感谢这个扩展提供者的作者。
4.定制的 Lorem

我们可以如下使用 Faker 生成一个句子。
fake.sentence()

然而,如果我们希望句子是从我们的“字典”中生成的呢?换句话说,我们希望使用有限的单词作为我们将要生成的所有随机句子的库。
一些造假者支持定制,sentence()方法就是其中之一。我们可以传入一个单词列表,这样所有的句子都将在预定义的单词库中生成。
my_words = [
'only', 'these', 'words', 'are',
'allowed', 'to', 'be', 'used'
]for _ in range(5):
print(fake.sentence(ext_word_list=my_words))

5.独特的价值观

图片来自 Pixabay
通过了解 Fake 的机制是从后台的现有池中随机生成假数据,您可能会担心,如果我们为足够的卷生成一种类型的假数据,它可能会生成重复数据。
答案是肯定的。如果我们使用不带任何注释的方法,就有可能生成重复的数据。让我们做一个简单的实验,生成 500 个名字。这里我们生成名而不是全名是为了更容易产生问题。
names = [fake.first_name() for _ in range(500)]
print('Unique sentences:', len(set(names)))

可以看到,在我们生成的 500 个名称中,只有 244 个名称是唯一的。
Faker 提供了这个问题的解决方案。也就是说,在调用方法之前调用unique属性。例如,我们应该使用fake.unique.first_name(),而不是使用fake.first_name()。
names = [fake.unique.first_name() for _ in range(500)]
print('Unique sentences:', len(set(names)))

这一次,我们让所有 500 个名字都变得独一无二。然而,如果你和我一样好奇,你可能会问这样一个问题:如果我生成了更多的名字呢?让我们测试 2000 个名字。
names = [fake.unique.first_name() for _ in range(2000)]
print('Unique sentences:', len(set(names)))

很明显,名字池在 1000 个名字之后就耗尽了。因此,Faker 也有其自身的局限性,但这是可以理解的,因为有意义的虚拟数据不可能是无限的。好的一面是,当它不能满足我们的要求时,它会抛出错误。
6.随机性中的确定性

图片来自 Pixabay 的 anncapictures
我们知道 Faker 会随机生成数据。这是否意味着我们可以复制一些演示?不,如果我们一开始就计划好,我们总是可以复制假数据的。那就是使用种子号。
现在,让我们实例化一个新的 faker 对象。我们可以如下指定它的种子号。种子数量可以是任何整数。我们就用 123 吧。
fake = Faker()
Faker.seed(123)
然后,让我们生成 5 个名字。
for _ in range(5):
print(fake.name())

记住这些名字。现在,让我们在不分配种子号的情况下更新 faker 实例。然后,生成另外 5 个名字。
fake = Faker()for _ in range(5):
print(fake.name())

当然,它们会有所不同,因为它们是随机的。
让我们下次用同样的 123 号种子再做一次。

如果您将这 5 个名称与我们第一次使用相同的种子号时进行比较,它们是相同的。
所以,如果要重现同样的假数据生成,用种子号就行了。
摘要

在本文中,我介绍了 Faker 库,它是 Python 社区中令人惊叹的反重力库之一。它确实非常容易使用,而且会非常有用。当你只需要一些哑数据,并且不关心数据的分布时,可以考虑先用 Faker。
https://medium.com/@qiuyujx/membership
如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)
数据质量问题比你想象的要多
原文:https://towardsdatascience.com/you-have-more-data-quality-issues-than-you-think-45d3bf6fa3b3
平均来说,公司的数据仓库中每 15 个表就会遇到一个数据问题。以下是 8 个原因以及你能做什么。

一个常见的数据质量问题是数据漂移。在 Unsplash 上由 Aral Tasher 拍摄的照片。
跟我说:你的数据永远不会完美。
任何努力获得完全准确数据的团队都会非常失望。单元测试、异常检测和编目是重要的步骤,但是仅仅依靠技术是无法解决数据质量问题的。
像任何熵系统一样,数据会中断。正如我们所了解的那样,构建解决方案来抑制数据问题的原因和下游影响,这种情况比你想象的更经常发生。
事实上,虽然大多数数据团队知道他们有数据质量问题,但他们大大低估了每月、每周甚至每天发生的数量。
我们的产品数据来自与许多跨行业、跨公司规模和跨技术堆栈的数据团队的合作,显示出一般组织每年会经历的数据事件数量大约是每 15 张表就有一次。
那么,即使有了可靠的测试和其他方法,为什么这么多的数据质量问题会被忽视,只是在几个小时、几天甚至几周后才被利益相关者发现呢?是怎么走到这一步的,能做些什么?
在这篇博文中,我将深入探讨隐藏的数据质量问题的 8 个原因(或“沉默的大多数”数据停机时间)以及改进检测和跟踪的最佳实践。
隐藏在明处的质量问题

数据新鲜度是一个隐蔽的质量问题的例子。照片由基兰伍德在 Unsplash 上拍摄
许多原因(包括本文中的大多数原因)导致组织低估了数据质量问题的普遍性,这些问题涉及到模糊其可见性或感知的因素。但是也有一些数据质量问题在众目睽睽之下大胆地出现,没有人知道。
例如,野外健康问题每天都会出现,但却是最难检测和识别的数据质量问题之一。
测试现场健康状况可能看似容易,有一些开源和转换工具可以提供帮助。如果一个字段不能为空,那就很简单了。
然而,定义阈值确实具有挑战性。原因是范围中间的阈值通常不同于边缘。例如,从 40%零到 50%零可能是好的。然而,从 0% null 到 0.5% null 可能意味着灾难性的失败。
另一个隐藏在眼前的数据质量问题是新鲜度(或及时性,如六个数据质量维度中所述)。您的数据消费者有最后期限。他们在特定时间交付报告或执行数据相关操作。
在每个管道中,当一个表延迟时,会有一系列的过程使问题复杂化。您的业务利益相关者可能不知道利用过时的数据。另一种可能性是,他们只是没有执行需要做的事情,因为很难区分什么时候真正没有生成记录,什么时候上游出现了问题。
如果你问数据晚运行一个小时是否应该被认为是数据质量问题,那么可能是时候开始考虑开发 数据 SLA了,因为知道的唯一方法是与你的利益相关者交谈。
数据漂移

数据逐渐增加是因为底层事件还是因为数据漂移和数据质量问题?由活动创作者在 Unsplash 上拍摄的照片
数据漂移是指由于数据质量问题而不是真正的潜在趋势,数据在一个方向上逐渐且一致地变化。这种有害数据的潜在蔓延给任何运行基本数据质量报告或异常测试的人带来了巨大的问题,因为这些过程旨在捕捉数据中的重大变化。
当下游数据分析师在每份报告中看到的都是略高或略低的数字时,他们又如何能发现数据质量问题呢?
数据漂移也特别具有挑战性,因为下游数据分析师可能会看到一个数字略有上升或下降,但这不足以引起他们的质疑。
期望漂移

就像你可以通过逐渐增加热量来煮青蛙一样,数据团队可以习惯于数据质量问题的逐渐增加。照片由 Ladd Greene 在 Unsplash 上拍摄
有一个古老的神话,你可以煮一只青蛙,只要你逐渐提高水温,它们就不会跳出锅外。撇开秋葵食谱不谈,数据团队也是如此。
只要有仪表板,就有坏掉的仪表板和试图修复它们的数据工程师。
长期以来,这一直是标准操作程序,以至于许多组织可能不明白,如果数据工程师不经常解决数据质量问题,他们可以为组织增加多少价值。
对于退一步评估他们的团队如何花费时间的数据领导者来说,结果令人震惊。
在推出该公司之前,我们与 150 多位数据领导者进行了交谈。我们发现,团队平均花费 30%以上的时间来解决数据质量问题。
在我们产品开发人员的前公司,6 个年度 okr 中有 4 个专注于以某种方式处理或提高数据可靠性。
所有权漂移

如果您无法了解所有数据资产,就无法了解所有数据质量问题。照片由 krakenimages 在 Unsplash 上拍摄
数据仓库或湖是现代数据栈中绝对重要的组件。然而,尽管它确实是真理的来源,但它也可能遭受我们所谓的“公地悲剧”
当您的整个数据团队都可以访问仓库,但没有标准的控制或良好的数据卫生时,仓库可能会相对较快地变成垃圾场。理论上,确保每个数据资产都有一个所有者似乎很容易,但现实并非总是如此。
你永远不会看到以软件工程人员的名字命名的微服务,但是对于每个工程师来说,在仓库中拥有他们自己的模式是非常非常普遍的做法。
挑战在于每个团队都会有自然更替。人们来来去去。当没有强大的沿袭或其他流程来了解不同的数据集如何影响仪表板或下游的其他资产时,您将无法了解潜在的数据质量问题或事件。
作为数据认证流程的一部分,标记数据 SLA 所涵盖的表是一个很好的解决方案,可以避免出现“您正在使用那个表?!"问题。
缺乏对事故分类的可见性

数据质量问题的事件分类。
说到可见性问题,缺乏对事件分类流程的可见性是我见过的数据领导者不仅低估其数据质量问题数量的最大原因之一。
当数据管道损坏、数据不准确或数据不一致时,数据工程师不想在屋顶上大喊大叫。除非有强大的数据事件分类流程和文化——这发生在持续聊天的地方,如 PagerDuty、Microsoft Teams 或 Slack——识别和缓解数据质量问题发生在电子邮件的幕后。
缺乏针对数据质量问题的 KPI 和 SLA

跟踪 Red Ventures 的数据质量问题 SLA 和 SLIs。图片由布兰登·贝德尔提供。
具有讽刺意味的是,数据团队可能无法掌握发生的全部数据质量问题的原因之一是…缺乏数据。
这就是为什么越来越多的数据团队开始在数据团队和业务部门之间制定数据 SLA 或服务水平协议,指定他们可以从数据系统获得的性能水平。毕竟,你只能提高你衡量的东西。
高级数据科学家 Brandon Beidel 为 Red Ventures 做了同样的事情。正如布兰登所说:
“下一层是衡量绩效。这些系统的表现如何?如果有大量的问题,那么也许我们没有以一种有效的方式建立我们的系统。或者,它可以告诉我们在哪里优化我们的时间和资源。也许我们的 7 个仓库中有 6 个运行顺利,所以让我们仔细看看不顺利的那个。
有了这些数据 SLA 之后,我创建了一个按业务和仓库划分的仪表板,以了解每天满足 SLA 的百分比。"
取消人工检查站

像逆向 ETL 这样的过程正在将人工数据质量抽查器从循环中剔除。
长久以来(也许太久了),数据分析师和业务利益相关者一直是组织数据可靠性的安全网。
如果坏数据被传送到仪表板,希望数据分析团队中的某个人会注意到它“看起来很滑稽”现代数据堆栈正在发展到更多的人被排除在循环之外的地步。
例如,许多组织开始实施 反向 ETL 管道,将数据从数据仓库直接拉入操作系统(如 Marketo 或 Salesforce)。或者,也许这些数据正被用来为机器学习模型提供信息。
这些流程使数据更具可操作性和价值,但也使数据团队更难发现数据质量问题。自动化流程可以受益于自动化的数据可观察性和监控。
数据质量问题的覆盖范围

数据质量问题的覆盖范围比你想象的要大。照片由 Unsplash 上的 Fernando @cferdophotography 拍摄。
对于第一次看到端到端数据可观察性解决方案的组织来说,覆盖范围始终是最令人震惊的因素。自动发现和机器学习驱动的监视器非常强大。
关于数据质量,我们最常听到的一句话是,“我不可能为所有这些编写一个测试。”这是真的。数据生态系统增长太快,有太多的 未知未知 需要手动编写测试来涵盖一切。
通常情况下,数据团队会为过去失败的东西编写测试。但是要做到这一点,您需要所有东西都至少中断过一次,并且在此期间没有添加新的数据资产。
手动流程无法扩展,结果是常见的数据质量问题被遗漏,数据完整性付出了代价。
第一步是知道你有问题
除非大家一致认为存在需要解决的问题,否则永远不会有提高数据质量的动力。
花一分钟时间将您环境中的表数除以 15,看看这个数字与您记录的年度事件数相比如何(如果您没有记录这些事件,请开始记录)。
无论如何,这都不是一个完美的估计;几乎有无限多的变量会影响您的具体结果。然而,如果这两个数字有很大差异,可能是时候建立更好的系统来捕捉和解决数据质量问题了。
藩 本文合著。
如果您想了解更多有关如何测量数据停机时间或其他数据质量指标的信息,请联系 巴尔 和其余的 蒙特卡洛团队。
即使别人对你说不,你也要保持好奇心,跟着兔子走
Robert McKeon Aloe 谈论数据、咖啡和在每个转折点质疑假设的重要性。
在 Author Spotlight 系列中,TDS 编辑与我们社区的成员谈论他们在数据科学领域的职业道路、他们的写作以及他们的灵感来源。今天,我们很高兴与 罗伯特·麦肯芦荟 分享我们的对话。

照片由罗伯特·麦肯芦荟提供
2006 年, 罗伯特 在底特律 Mercy 获得电气工程学士和硕士学位,后赴圣母大学攻读计算机科学与工程博士学位。在他关于 3D 人脸识别的论文中,他通过结合结构光和物体跟踪构建了一个新颖的 3D 人脸扫描仪。
从研究生院毕业后,罗伯特在一家初创公司工作了四年,从事 3D 人脸识别研究,然后加入了苹果公司。在苹果公司,他为第一款 Apple Watch 开发了手腕检测和背景心率,然后为 Face ID 开发了机器学习,AR Kit,并为盲人用户开发了人物检测。
除了喝咖啡,他大部分时间都和家人在一起,他的爱好包括学习意大利语、阅读、园艺、搭建乐高积木、跑步、滑雪和迪士尼。
对于那些可能只熟悉你的咖啡数据写作的 TDS 读者来说,你在数据科学和计算机视觉方面的主要专业领域是什么,你最感兴趣的主题是什么?
我在大学选修图像处理课程时开始涉足这一领域。我爱上了问题空间在算法开发和让算法实时运行方面的难度。班级项目——高级设计项目——是为一个自主机器人做计算机视觉。
当我读研究生的时候,我真的很想从事自主机器人的研究。选项很少,最后我做了 3D 人脸识别。我觉得在计算机视觉领域有足够的重叠,很快,我发现我一半的工作是数据科学。为了我的论文,我设计并建造了一个 3D 面部扫描仪,你可以走过它,然后我继续为一个大规模的测试集收集成千上万的人的扫描。
“数据为王”成了我的口头禅。
我被难题吸引住了。3D 人脸识别是一个难题,最终通过数据来解决。后来,我被一个更广泛的问题吸引住了,“计算机视觉如何改善我们的日常生活?”这一点在可访问性领域非常明显,这和我参与的其他项目一样具有挑战性。
是什么让你决定探索浓缩咖啡制作背后的数据科学?你是如何开始这段旅程的?
我的旅程是从不满足开始的。我已经非常擅长在工作中使用我的浓缩咖啡机,我在家里有一台 La Pavoni。然而,我没有得到同样好的浓缩咖啡。所以我开始做一个数据表来帮助我弄清楚如何做出更好的咖啡。当我用它来跟踪各种相关变量时,这个数据表迅速扩展。一旦我能追踪它们,我就能在实验的大设计中分析原因和结果。
在你正在进行的咖啡工作中,你从数据中学到的最重要的东西是什么?
质疑假设!
当我开始为咖啡做一些数据科学的东西时,我以为这个领域的大部分内容都已经解决了。当我开发了 staccato(筛过的和分层的)浓缩咖啡镜头时,我遇到了很多阻力,我开始意识到知识的深度不在那里。也许它来自个人经历或隐藏在付费墙或公司背后,但它不是免费提供的。
因此,我开始质疑每一个假设,并试图将数据与浓缩咖啡的每一个变量联系起来。我认为这在数据科学中是很典型的。即使别人拒绝你或反对你的实验,你也要保持好奇心,跟着兔子走。你可能是错的,但你可能是对的。
咖啡很奇怪。我最近惊讶地发现一个便宜的研磨机胜过一个更贵的研磨机。随着我越挖越深,我很难找到原因。它们有相似的粒子分布,但是当我开始用一些模式识别来比较这些粒子时,我发现其中一些粒子有不同的形状。所以有时候,性能差异的原因并不在表面层面。
你目前正在写一本收集你写的咖啡相关数据的书。你希望读者能从这本书里得到什么,而通过分散的文章是很难得到的?
我已经写了几年了,我很难找到一个好的方法来组织我所有的作品。我发现咖啡中没有太多与田地大小相关的数据。所以我把所有的东西放在一起看它会是什么样子,它太多了。我有 1200 页的材料。我问我所在的一个咖啡小组,家庭浓缩咖啡爱好者,我该怎么做。大家建议 200 页左右的东西。
我决定展示如何提高浓缩咖啡的每个变量。然后,阅读这本书的人可以逐步提高他们的浓缩咖啡,并有一个起点或计划,从好的浓缩咖啡到更好的浓缩咖啡,甚至是最好的浓缩咖啡。
每篇文章都深入探讨了一个主题;这本书擅长展示所有变量的万尺视角。从那以后,它可能看起来不那么杂乱,因为浓缩咖啡有很多难以分类的变量。
你已经设法选择了一个相当狭窄的利基市场,并且仍然有源源不断的新文章想法。对于那些想写自己专业领域的人,你有什么建议吗?
我开始写关于工作的故事。我意识到我有很多故事可以帮助人们——尤其是刚进入职场的大学毕业生。故事对我来说一直很重要。后来,当我发现我的技能可以增值的时候,我扩展到了咖啡行业。对于工作故事和生活故事,我做了一个初步的头脑风暴,列出了一个主题列表,我承诺每周发布一次,然后在固定的日子每周发布两次。
最初,我没有太多关于写咖啡的想法,但我做了很多实验。我很快就承诺出版我所有的实验——甚至是愚蠢的、疯狂的和怪异的实验。我真的患上了多动症,所以我的写作过程分为实验(图片、数据、大纲)、初稿和终稿。
有几个星期,我专注于这些领域中的一个,比如实验。我进行了一系列实验,将笔记放入一个文档中。然后我会花一周的时间做多个初稿,最后,我会在一周内完成 5 篇或者 10 篇文章。这个过程符合我天生的好奇心和对截止日期的需求。
如果你想写什么,从你的故事开始。你为什么会爱上你的研究领域,你的爱好,你的伴侣,你的音乐?从你容易写的东西开始,然后从那里开始。
开始写作的另一个方面是专注。你关注的是你自己和这篇文章能为你做什么,还是你关注的是这篇文章能为你身后的人做什么?这些是应该为之写作的人——新来者。
对于全职工作的数据科学家来说,一个持续的痛点是为他们的公开写作留出时间。你是如何平衡这两者的?
我从小做起。问题是要看到收益在哪里。对一些人来说,写作的最终目标是获得认可或赚钱。我的目标是写作的过程。我发现这个过程是放松和宣泄的。每一件作品都为我提供了一个更好的反思和评估的机会。
关于全职工作,我努力平衡工作和生活,这样我就有时间写作。我们经常谈论平衡工作和生活,而现实是我们大部分时间都在工作,有时也有一些生活。通过让我们的现实与我们的理想相匹配,我们就有时间以与工作无关的方式来编写和分析数据。
你的日常工作和公共写作在想法、过程或见解方面有没有交叉融合?
我不打算成为一名职业作家或咖啡爱好者。我的大多数文章都不在付费墙后面,我为我的书启动 kickstarter 的唯一原因是为了支付图形设计、编辑和印刷的成本。我真的很喜欢我的专业工作以及它对世界的影响。
想法不会交叉授粉,但过程会。我在工作中分析数据的方式影响了我对咖啡数据的看法。我并行处理多个项目的方式在我并行撰写多个主题的过程中得到了复制。优先化的关键概念已经在我的工作中根深蒂固,结果就是我优先化我的写作。我总是有一个按优先顺序排列的主题列表,总有一些主题我永远也不会触及,因为它们的优先级不够高。
最后一个问题:2022 年你最想探索的项目和新的学习领域是什么?
我积累了相当多的浓缩咖啡知识,我想找到一些方法来为不同的人群从中提取最好的东西。我的第一本书关注的是有过一些浓缩咖啡经验的人。我想写点东西给那些不熟悉浓缩咖啡的人,也写点东西给超级关注科学的浓缩咖啡爱好者。
为了探究罗伯特的工作,你可以浏览他的关于咖啡数据科学的 TDS 文章档案,或者查看他自己的按主题组织的媒体文章集。你也可以在 Twitter 和 LinkedIn 上关注他。你可以在该项目的 Kickstarter 页面上了解更多关于他即将出版的书。
你刚开始一份新工作,高级数据科学家要走了。现在怎么办?
不要惊慌,按照以下步骤平稳过渡

当我需要的时候,这个杠杆在哪里?照片由梁杰森在 Unsplash 上拍摄
在我开始我的新工作后不久,我注意到我的团队中更资深的数据科学家——唯一的其他 DS,他们开发了我们的许多数据产品并维护所有这些产品 — 在电话和不可用的阻塞方面有许多困难。他还带着他的个人笔记本电脑去工作,这本身并不奇怪,但结合上述情况,感觉就像一个不祥的征兆。我把我的感受归因于对新工作的焦虑,特别是因为我喜欢我的团队和资深 DS,而且我很快就建立了良好的关系。
然后吊杆。我开始新工作三周后的一天早上,当我正在装满我的水瓶时,那位高级主管走过来对我说:“嘿,你有时间吗?”这些话不会带来好结果。上帝保佑他,他至少给了我一周的额外时间,超过了通常的两周通知时间,我为他感到激动,因为他正在走向一个令人兴奋的新机会。但是热死了!在他宣布这个消息后,我们聊了一会儿,我发现自己狂笑不止——这种情况一点也不好笑,但我还能做什么呢?

戏剧性的重演我在外面笑,同时隐喻内心的死亡。 照片由 christian buehner 在 Unsplash 拍摄
我给我的朋友发消息发泄这种情况,但许多人不是数据科学家,也不太明白为什么这是一件大事。“你都快一个月了,不是已经什么都知道了吗?”哈。哈。哈。我不怪他们;不在 ds 的杂草中,很难理解适应组织中所有不同的数据集、数据产品和流程通常需要时间。
但是一旦我笑完了,现实就来了,我知道我必须为接下来的三周建立一个过渡过程,以确保在我的同事离开后,我和我的团队继续保持稳定和成功。我认为其他地方的数据科学家可能有一天会发现自己处于类似的情况,下面是我为同事的离开做准备时采取的步骤和考虑的事情。
与时间赛跑

保拉·格雷罗在 Unsplash 上拍摄的照片
如果你发现自己处于我上面描述的情况,把你同事脑子里的所有东西都转到你自己的脑子里,这是在和时间赛跑。持续的沟通至关重要。我很幸运,我的同事提出每天在日历上划两个小时,直到他离开,以确保我学到了我需要学习的一切;在你剩下的时间里,你绝对应该坚持和你即将离职的同事做一些类似的事情,最好把时间记在日历上,比预期的少用一些,而不是相反。我把重复出现的日历项目称为我们的机构知识转移聚会——尽管它实际上不是聚会,但也可以试着对一个严重的情况笑一笑——但即使在预定的会议之外,你也应该不断地回顾你在以前会议中的笔记,并向你的同事发送问题,以便为下一次会议做好最好的准备,并尽可能取得最大的进展。
证明文件

当我说“把他们大脑里的所有东西都转到你自己的大脑里”时,我当然不是指 100%的你的大脑。我只希望我有善意狩猎- 级别的记忆力,但我没有,你们中的许多人可能也没有。这就是文档是关键的原因。作为一名数据科学家,在开发新产品和编写代码的兴奋中,我发现很容易忽略文档的重要性,但它将拯救你,让你回到这里。
对于数据集和数据产品/过程,审查可用的文档,改进它,并在需要时制作新的文档。如果你不清楚存在什么,不要犹豫为已经记录的产品创建新的文档——重要的是它对你和其他留下来的人有意义。一个明确的数据字典来识别重要数据集的内容和目的是必要的,如果您需要一个模板来开始创建标准化的数据产品文档,下面是我想到的:
描述 : 产品是做什么的?
输入数据 : 什么数据进去,在哪里?业主是谁?
输出数据 : 输出什么数据,在哪里?业主是谁?
流程 : 从输入数据到输出数据,一步一步的实际发生了什么?
存储库,关键脚本 : 促进上述过程的代码在哪里?与该流程中的每个步骤相关联的关键脚本是什么?
调度 : 这个流程计划什么时候运行?
联系人/用户/利益相关方 : 谁是产品的关键联系人?谁是产品的用户/利益相关者?
故障排除 : 常见问题的故障排除步骤有哪些?
循环

Deva Darshan 在 Unsplash 上拍摄的照片
文档很重要,但是不要试图在你同事有空的最后一天完成。就像我四年级的自己认为我的第一稿论文是最终稿一样,就像大多数东西一样,第一次迭代很少是最好的,你会在第一次通过时错过一些东西。每次你回顾你正在迭代或编写的文档中的内容时,你可能会发现一些你仍然不清楚的小事情,可能每次都更加细化。这就是为什么至少在你的同事离开前几天,第一次看完你正在做的任何文件是很重要的,这样你就可以和他们一起看完所有的文件,他们可以填补剩下的知识空白。
优化

马塞尔·埃伯勒在 Unsplash 上的照片
数据科学最有趣的部分之一是能够成为一名疯狂的科学家,探索你的一些疯狂想法如何能够带来积极的商业影响。总有时间去研究数据,用新的东西来激发创造力,对吗?
不,不总是。作为一名数据科学家,确实会有创造性探索的机会,但过渡期不是时候。正如我明智的老板所提到的,我通过最近的经历充分认识到,在这样一个过渡的场景中,确保你拥有适当的知识来“保持运转”,继续团队迄今为止的工作,继续提供一贯的服务,这是最重要的目标。所以,在将你的精力转移到新的努力之前,你要全力以赴,确保你已经理解了已经存在的东西——通过如上所述的文档或其他任何东西。
保持乐观

在这样一个动荡的时代,很容易感到不知所措;如果我说我从来没有,那我是在撒谎。这是一个合理的反应——我的职责已经发生了根本的变化,并以一种完全意想不到的方式扩展开来。但是就像许多感觉一样,承认这种感觉是一回事,让它控制自己又完全是另一回事。
保持头脑清醒,尽你最大的努力,同时相信,结合你最大的努力和他人对变化环境的理解,一切都会迎刃而解。当然,你应该努力把每件事都记录下来,尽可能想出最好的办法,但是你和你的离职同事都是人,几乎不可避免地会发生一些你没有问或他们忘记提及的事情。不要因为达不到完美而失望;相反,以建立一个坚实的框架为荣,让你尽可能地接近它,在此基础上,你可以解决剩下的问题。
我选择将同事离职前后的过渡经历视为快速熟悉团队中所有不同产品并证明自己是新人的机会。此外,我渴望在未来几年里晋升到总监级别,当我一头扎进数据和产品中尽快熟悉起来时,我对自己说,这是作为总监加入团队的感觉吗?与我的行业导师的一些简短交谈表明,这确实是一个类似的过程,因此我将整个经历视为支持未来 Danny 的良好实践。
结论

在我的高级同事和团队中唯一的另一名数据科学家在工作仅几周后离开时,我不禁想起了《星际穿越》中的场景,库珀试图与失去控制的耐力号对接:
案例:不可能!
库柏:不,这是必须的。
愿你在数据科学职业生涯中永远不会经历我所经历的意外转变。但是如果你这样做了,我希望我上面概述的对你有用,并在你困难的时候给你带来一些安慰。尽管你可能觉得自己处于一种不可能的境地,但你可以——并且会尽最大努力——为你和你的团队的持续成功打下必要的基础。
你可能是一名数据分析师,如果…
原文:https://towardsdatascience.com/you-might-be-a-data-analyst-if-8eaa8042156f
看看数据分析的非技术方面

卢克·切瑟在 Unsplash 上的照片
大量资源详细介绍了数据分析师所需的各种技能。然而,作为一名数据分析师,除了技术能力之外,还有很多东西。
作为一名数据分析师,除了技术能力之外,还有很多东西。
公司想要全套服务:一个全面发展的人,既能带来技术能力,也能带来非技术能力。也就是说,本文的重点是数据分析的非技术方面。
成为数据分析师需要什么
数据分析师不仅必须具备处理数据和为组织提供见解的技能,还必须具备能够让数据分析师对业务产生变革性影响的关键素质。这是你真正可以超越职位描述的地方。我列举了一些我认为能让数据分析师成为伟大数据分析师的特征。如果以下任何一种情况听起来像你,你应该把数据分析作为一种职业。
你可能是一名数据分析师,如果…
1.你对数据有强烈的兴趣
2.你是一个优秀的沟通者
3.你是一个侦探
4.你感同身受
5.你很有创造力
6.你永远是一个学习者
在接下来的几节中,我将深入探讨每一种品质,并提供一些实际运用的例子。如果您对成为一名数据分析师需要什么感兴趣,请继续阅读以了解更多信息!
> > > 1。你对数据有浓厚的兴趣
首先,你必须对数据有浓厚的兴趣。这一点怎么强调都不为过。数据是硬的。对数据的热爱将有助于防止倦怠或对数据不那么迷人的方面感到气馁。这包括经常是费力的数据准备过程,这可能是你花费大部分时间的地方(并且提供了你作为分析师的大部分价值)。
数据是硬的。
你的分析、可视化和洞察力取决于你的数据。你可能听说过“垃圾进,垃圾出”这句话。确保你不是在和垃圾打交道是一项艰巨的工作。对你的数据了如指掌,并对你传达给利益相关者的真相充满信心,这是成为一名优秀分析师的关键。
数据沼泽 只有当你对数据充满热情时,这种对组织数据的深度挖掘才会发生。你不可能给我足够的钱让我在佛罗里达州的一个真正的沼泽中跋涉,但出于某种原因,我的大脑喜欢在“数据沼泽”中爬行并清理东西的冒险!和佛罗里达沼泽一样,不言而喻,这并不适合所有人。
我的大脑喜欢在“数据沼泽”中爬行和清理东西的冒险!

尼尔斯·莱恩哈特在 Unsplash 上的照片
建立对数据的信任 一旦你对数据有了这种程度的熟悉,并且是一个主题专家,你的利益相关者可能会“让你进来”,并且相信数据所说的话。如果你能够做到这一点,你可能会开始看到业务战略和决策产生于你提供的见解。这是一件美好的事情!许多组织声称自己是数据驱动的,但尽管拥有各种花哨的仪表盘和分析工具,许多决策者仍凭直觉行事。我们作为数据分析师一定要努力改变这一点!
这里有一些迹象表明你可能对数据有浓厚的兴趣。
- 你喜欢文章包含引人入胜的图表和图形
- 你创建了一个电子表格,记录你生活中的重要事情
- 你是数据美丽子编辑的粉丝
额外提示你可能对数据情有独钟
如果你曾经看过一个图表或图形,并对自己说“嘿,这有点误导”,你真的应该考虑数据分析!数据的准确交流和良好的道德规范在该领域至关重要,上面的例子表明您对数据的呈现有敏锐的眼光,并有良好的道德意识!
在这一领域,准确的数据交流和良好的道德规范至关重要。
如果你想听一个困扰我的数据实践,请阅读这一段。最让我困扰的一个可疑的可视化实践是当 y 轴从零以外的任何位置开始时(没有清楚地传达这种转换)。这通常是有意为之,目的是歪曲图表的解释,以支持不代表现实的对话。这看起来很糟糕,是任何数据分析师都不应该犯的错误。敬请关注未来的一篇文章关于数据分析中的道德以及为什么你应该关心被误解的数据!
> > > 2。你是一个优秀的沟通者
继续关于道德的讨论,数据的道德表现是成为优秀沟通者的支柱。当然,一个人可能很擅长传播谎言,但我相信公司希望伟大的真理传播者为他们的业务工作。
数据的道德表现是成为优秀沟通者的支柱。
不同的利益相关者可能意味着不同的复杂性 作为一名数据分析师,你还必须善于从不同的细节深度解释复杂的主题。作为一名数据沟通者,你必须能够向公司的多个层面提供见解,从数据专业人员到经理,他们可能是也可能不是技术人员。

此外,您可能需要与各种内部业务单位甚至外部客户进行沟通。每种关系都需要不同的方法来围绕数据分析展开对话。从与数据专业人员同事一起深入“数据战壕”的抽象层面来看,您可能需要将您的发现传达给高层领导或首席执行官。他们想要事情的简短和甜蜜的版本,你必须避免在这些讨论中“进入杂草中”,除非被提示更多的细节。
多种沟通形式 作为数据分析师,沟通可以被认为是通过仪表盘和图表提供分析;然而,作为一名数据分析师,沟通需要的不仅仅是创建信息丰富的图形的能力。这包括写得好的能力。事实上,包括亚马逊在内的许多公司不相信 PowerPoint 风格的演示,而是通过书面报告进行沟通,团队成员在会议开始时默默地给自己朗读。
没有数据分析师是孤岛 团队合作是数据分析师的关键。据我所知,没有任何数据分析师能够在没有任何其他业务职能部门的输入或帮助的情况下在真空中工作。

汤姆·温克尔斯在 Unsplash 上的照片
数据分析师通常有效地扮演项目经理的角色,协调最终用户、工程和交付最终产品所需的各个部门之间的工作和沟通。管理这些关系对于成为一名高效的数据分析师至关重要。
数据分析师通常有效地扮演项目经理的角色。
> > > 3。你是侦探
数据分析师必须会玩侦探游戏。解决问题是数据分析师的舒适区。
积极主动的数据分析师 不仅是侦探——咳咳,数据分析师——专注地倾听对分析的要求,而且他们还留意有关一般业务沟通的情况。细心的数据分析师总是在寻找潜在的数据点或需要改进的地方。
下面是一个实例:一名数据分析师经常听到同事谈论他们对销售渠道中可能性百分比的可信度的怀疑。就这样,一个分析项目被发现了!然后,数据分析师引入销售渠道数据来计算实际百分比可能性,并将这些数字与指定的估计值进行比较。
细心的数据分析师总是在寻找潜在的数据点或需要改进的地方。
这样,在没有提示的情况下,分析师就对他们部门中常见的不满话题提出了解决方案,并为销售部门的预测提供了潜在的改进意见。这是作为一种习惯的持续改进。
初级,我亲爱的沃森 解决问题是数据分析师的日常业务。请注意,将进一步的复杂性引入公司运营并不是数据分析师的工作。数据分析师的任务是识别和解决公司可以采取合理措施解决的问题。
数据分析师主要通过使用他们的分析方法,即归纳法来使用和推广。夏洛克·福尔摩斯使用的调查手段,归纳推理从观察开始,接着是模式发现,然后根据模式形成理论。如果对归因有足够的信心,就可以根据这些理论采取行动。数据分析师的工作是促进和帮助这一调查之旅。
归纳推理从观察开始,然后是模式发现,再根据模式形成理论。

始终了解业务案例 对于数据分析师来说,了解他们执行的任何分析的业务案例非常重要。这些知识有助于生成分析,并确保数据不会被意外曲解。如果你在成长过程中是一个“为什么孩子”,数据分析可能是一个合适的选择!
我在本文前面提到了数据伦理。数据误报可能因失误而发生,因此您必须采取预防措施,因为您的诚信岌岌可危。
通过了解商业案例,你不仅可以防止潜在的负面影响,还可以让自己实现巨大的收益。当你用正确的信息装备自己时,更深层次的分析,相邻的分析,和有效利用时间都是可能的结果。通过这种方式,业务问题通常需要重新构建,以确保它们是针对业务案例的,并且可以有定义的后续操作。你要确保每个人的时间都花得值!
深入细节 数据分析要求你注重细节。精通研究与关注细节密切相关,因为分析师必须愿意并能够弄清各种问题的真相:数据源、数据所有权、数据准确性、数据清洁度、数据组织等等。
分析师必须愿意并有能力对各种问题刨根问底。
>>> 4.你很有同情心
数据分析师处于业务的第一线。他们不断努力灭火,同时优先考虑改善业务。
我们需要尽快完成 无论在哪里,利益相关者都会带着需要尽快完成的需求来找数据分析师。这种压力让我的下一个观点更加难以实现:数据分析师在与利益相关者合作时必须换位思考。
数据分析师在与利益相关者合作时必须换位思考。
这种共鸣有多种形式。同情利益相关者在灭火时感受到的痛苦。同情那些必须平衡您的数据请求,同时满足许多其他请求的工程师。以及对涉众在描述需求时可能经历的困难的同情。
理解提问 这最后一点我相信是经常被忽视的一点。当有人向你(数据分析师)提出紧急请求时,你很容易会觉得有必要在完全理解请求之前跑开,并尝试满足请求。你必须抑制住这种冲动!确保你明白要求你做什么,更重要的是,确保要求者完全明白他们想要什么。
在完全理解要求之前,很容易感觉到需要离开并尝试满足要求。
很多时候,在一些建设性的对话之后,如果我在最初的询问之后立即开始工作,那么约定会朝着一个截然不同的方向发展。节省你的时间和精力!请关注未来的一篇文章,我将在其中讲述我处理分析请求的过程。
>>> 5.你很有创造力
作为一名数据分析师,一定要培养自己的创造力!企业不应该在企业的现状。提出创造性的想法和解决方案,你会被认为是一个有价值的贡献者。
保持简单…… 作为一名数据分析师,你会有很多复杂的工作。不要直奔复杂的解决方案或解释。给自己一点时间想出更简单的解释。这里有一个例子:一个数据分析师注意到一个数据源中有许多空字段,结果导致分析严重失真。
数据分析师决定分析空白字段在数据输入者中的分布,而不是将此问题升级,并让多个利益相关者与负责输入数据的所有个人一起解决问题。分析师发现一个人产生了绝大多数的错误数据。数据分析师没有让这个问题变得更加严重,而是能够与个人一起解决问题,并确保他们理解这些数据的数据输入过程。
…但是要多角度考虑 就像我前面说的,不要安于现状。不要默认最小阻力的路径。事实上,如果你经历了对一个看似良性和合理的想法的特别强烈的抵制,你很可能会有所发现!数据分析师并不意味着简单地粗制滥造仪表板,最终由于缺乏业务应用而积灰,而是应该开始对数据提出新的问题。
数据分析师并不意味着简单地粗制滥造仪表板,最终由于缺乏业务应用而积灰,而是应该开始对数据提出新的问题。
> > > 6。你永远是一个学习者
数据领域最让我兴奋的一个方面是这个领域变化的程度和速度。作为一个热爱教育的终身学习者,我知道这是我的职业。如果你喜欢学习新事物,数据分析可能适合你!
数据马拉松 在数据领域,你学得快,不断学习!从数据中可以学到很多东西,尤其是在企业之间转移数据。每个企业和行业都是独特的,有一个不同的行话和衡量标准的世界。

金伯利农民在 Unsplash 上拍摄的照片
即使您停留在一个行业或一个企业,数据领域也在不断变化,每天都有新工具出现。你需要能够找到对你和你的组织最有效的方法,而不被卷入炒作,同时确保你和你的公司不会落后。这是一个挑战平衡的打击。我承认我对新兴技术和公司的新闻感到兴奋。只要记得培养你的非技术技能!
总结
如果你对数据有强烈的兴趣,你可能是一名数据分析师,你是一名优秀的沟通者,你是一名侦探,你有同情心,你有创造力,或者你永远是一名学习者。这些只是数据分析师需要具备的少数非技术素质。留下你认为对数据分析师重要的任何其他品质的评论!
看看我的另一篇文章,我长大后想成为一名数据分析师…从来没有人说,在那里我谈论了我对数据分析的发现!
直到下一次…
喜欢这些内容吗?关注我在媒体上看到未来的职位!成为中等会员这样你就可以无限阅读文章了。相信我,值得!
如果你对数据专业人员的生活有任何疑问,请随时在 LinkedIn 或 Twitter 上联系我!
https://medium.com/@ChristianWritesData/membership
关于随机森林特征的重要性,他们没有告诉你的 5 个事实
每个数据科学家经常面临需要使用监督学习(回归或分类)来建模特定现象的情况。在大多数情况下,模型的预测能力并不是分析的唯一期望结果,利益相关者希望了解不同因素对特定结果的影响。这就是特性重要性的来源。

图片由在 freepik.com 发现的 wayhomestudio 提供。
这个想法很简单。像往常一样,您训练一个基于树的模型来预测作为给定特征的函数的结果。然后,您可以利用基于树的模型的内置功能来报告每个特性对模型的重要性。
简而言之,基于树的模型根据基于每个变量获得的杂质减少量来计算特征重要性。
1-特征的重要性受到模型本身精度的限制
如果一个模型在测试集上的准确度很低,那么特征重要性就不可靠。模型应该能够对看不见的数据做出合理的预测,让我们相信它对特征重要性的判断。自然,过度拟合模型也不可靠。
2-特征重要性被标准化,并且将总是加到 1
这比大多数人意识到的更重要。重要性的标准化意味着绝对值不能被解释;相反,我们必须只关注功能的排名。绝对值可以通过添加或移除独立特征来改变,但是原始特征集和目标之间的关系没有改变。
此外,如果数据集中没有任何要素确实与目标相关,则基于每个要素获得的信息将非常少。尽管如此,正常化步骤会夸大它们的重要性。
让我来演示一下。我将创建一组随机特征和一个随机目标。直观上,随机数的序列之间没有关系,所以它们的重要性应该为零吧?让我们看看:
data = {'random_feature_1':[random.random() for _ in range(1000)],
'random_feature_2':[random.random() for _ in range(1000)],
'random_feature_3':[random.random() for _ in range(1000)],
'random_feature_4':[random.random() for _ in range(1000)],
'random_target':[random.random() for _ in range(1000)]}data = pd.DataFrame(data)
我们分成训练/测试和拟合随机森林模型,并绘制重要性:

作者图片
但是等等,我们看到每个人的重要性大约为 0.25!这并不意味着随机特征解释了目标的可变性,它只是重要性标准化的一个假象。
3-模型调整时,排名可能会发生变化
调整模型会改变基于不同要素拆分数据的方式。因此,这也将改变特性的重要性。
为了证明这一点,让我们重复上一节中的实验,但这次控制杂质减少的最小量。当信息增益小于我们可以指定为超参数的某个阈值时,该参数防止模型进行分割。让我们选择一个很小的值,比如 0.003,然后再次拟合模型。这一次,模型将赋予我们以下重要性:

作者图片
我们做到了!进口都是零。请注意,树已经“尝试”正常化重要性,但是它不能将零乘以一个数使它们相加为 1。
4-特征缩放对基于树的特征重要性没有影响
基于树的模型对特征比例不敏感,所以它们的重要性也不敏感是有道理的。为了证明这一点,我使用了来自 scikit-learn 数据集的“葡萄酒数据”,我们可以根据葡萄酒的化学成分对其质量进行分类。
这是当训练和超参数调整随机森林模型时,您将在第一轮中获得的特征重要性。

葡萄酒质量数据对非比例特征的重要性,按作者分类的图像
所以让我们开始重新调整一些特征:
data['flavanoids'] /= 1000
data['alcohol'] /= 1000
data['proline'] *= 1000
现在让我们再次看到它的重要性:

线性变换后的特征重要性,作者提供的图像
注意到什么不同了吗?没有吗?我也没有。
如果您认为这些只是简单的转换,那么当您执行非线性转换(如 log)时会发生什么呢?我完全支持实验,所以让我们一起来看看:
data['flavanoids'] = np.log(data['flavanoids'])
data['alcohol'] = data['alcohol']**2
data['proline'] = data['proline']**0.5
这一次,在采取相同的步骤后,我们将获得以下结果:

非线性变换后的特征重要性,按作者排列的图像
其再次显示与之前相同的值。结案了。
基于 5-树的特征重要性是有偏差的。
是的。你没看错。基于树的模型的特征重要性意味着快速,而不是超级准确,并且它们倾向于夸大连续或高基数特征的重要性。
摘要
本文强调了基于树的特性重要性的一些基本属性,以及它们如何受到超参数调整等因素的影响。总之,在判断每个独立变量对模型预测能力的贡献时,数据科学家应该了解这些属性。
您应该使用它来可视化 SQL 连接,而不是维恩图
数据科学
维恩图是去年才有的

图片由作者提供,灵感来自数据科学领域的R
几周前,我在 Reddit 上发表了一篇关于 SQL 反连接的文章。分享没多久,就得到这样的回应:
作者图片
这激起了我的兴趣,因为到目前为止,我还没有读过或听说过任何人认为维恩图是可视化 SQL 连接的一种糟糕的方式,我已经坚持用 SQL 编码超过 3 年了。我个人认为维恩图有助于快速记住和可视化两个表之间的连接类型。这是我的回答:
作者图片
在那次评论之后,我得到了一些热情的回应,并意识到这已经被广泛讨论过了,而且背后有一些历史。当我读得更多的时候,我发现了一个很受欢迎的 Reddit 帖子,标题是“在解释连接时对维恩图说不”。阅读别人对它的看法是很有趣的。我还发现了一个相关的流行帖子,发表在 2 年前的《走向数据科学》上,标题是“我们能停止使用 SQL JOINs venn diagrams 吗?”。
这场争论让我想起了关于 SQL 如何发音的争论,或者我第一次听说制表符与空格的争论。在我考虑了双方的观点之后,我决定写这篇文章,然后发现了我认为被低估的 SQL 连接的可视化,我称之为方格旗图。🏁
快速补充说明:关于 SQL 的发音,SQL 最初拼写为“SEQUEL ”,由于商标问题,仅改为“SQL”。
尽管我对这个主题有自己的看法,但我认为值得说明的是,我相信争论双方都有一些有效的观点,并且这些可视化都是表示 SQL 连接的有效方式。这种争论存在的原因是因为双方都从不同的学习方法中看到了好处,这是可以的。当然,对于大多数人来说,可能有一个最佳的学习路径,但是学习是一种定制的体验,所以我不想低估其他人通过使用不同的可视化发现的好处。但是请记住,真正理解 SQL 连接的最佳方式是进入代码和实践!SQL Practice.com是我发现的练习 SQL 的好资源。
但是请记住,真正理解 SQL 连接的最佳方式是进入代码和实践!
也就是说,我希望解决“伟大的 SQL 维恩图辩论”中双方的关键点,并提出一个可能(仅仅是可能)安抚双方的解决方案。
争论中对立双方的要点
为了更好地理解双方,我读了 Reddit 上的一些观点和文章。以下是我发现人们不同意使用维恩图来可视化 SQL 连接的原因:
- 维恩图源于集合论,因此不应该用于可视化两个关系表之间的连接
- 一些人声称,当使用维恩图向学生介绍连接概念时,他们很难理解连接
- 维恩图从技术上来说并不能正确地表示一个连接实际上在做什么
- 维恩图有各种局限性:例如,不能很好地显示其他连接类型(如交叉连接),不能显示重复出现时会发生什么,等等
这些是我从那些反对使用文氏图的人那里找到的主要批评。那些支持 SQL 文氏图的人给出了两个要点:
- 虽然维恩图在技术上可能不正确,但它们有助于人们记住连接的类型,并且更容易理解
- 根据所选的列,联接和集合操作可能会产生完全相同的结果
不管你更赞同哪一方,你现在已经对我决定写这篇文章的原因有所了解了。
维恩图的另一种解决方案
2016 年有一篇流行文章也反对使用维恩图,作者提出了一种替代图,称为“连接图”。下面是一个内部联接的示例,它被可视化为一个联接图:

图片由作者提供,灵感来自 Jooq 博客
此图非常有用,因为它比文氏图更准确地表示了 SQL 连接中使用的表格结构。这个图表的问题是,它将主键显示为颜色,但在这些颜色中也有数字或字母。矩形内的字母和数字应该表示除主键列(用颜色表示)之外的列,但这是可视化开始崩溃的地方。用一个矩形表示多个列可能会令人困惑和不直观(至少对我来说是这样)。无论如何,这种可视化似乎对那些难以理解使用 SQL 连接的人很有帮助。每一种可视化都有一定的局限性。
方格旗图🏁
当我回顾可视化 SQL 连接的不同方法时,我发现了我个人最喜欢的方法。我希望这个图能够弥合双方之间的差距,或者至少提供另一种选择来帮助人们理解 SQL 连接。
这个图表最初是由 Hadley Wickham 和 Garrett Grolemund 在他们的书“数据科学的 R”中创建的。图表可以在关于【关系数据】的章节中找到。
我在一张备忘单上重新创建了这些图表,如下所示,但是我也创建了一个 Github 库,所以你可以在这里下载图片。

图片由作者提供,灵感来自数据科学的R

图片由作者提供,灵感来自用于数据科学的R
为什么我😍方格旗图
以下是我喜欢这张图的所有原因:
- 比联接图更准确地表示联接,因为它的主键具有相同的颜色和数字
- 为每个表显示 1 个额外的值列,以帮助直观显示除主键列之外的列中的数据发生了什么
- 连接线有助于简化视觉效果,便于看到连接的行
- 与连接图类似,连接产生的输出表显示在右侧
- 适用时显示空值,这正是在 SQL 中执行连接时发生的情况
- 可以显示交叉连接,这与文氏图相比是一个优势
- 显示 SQL 语法以供参考,类似于维恩图备忘单这里的
我仍然相信维恩图对于可视化 SQL 连接是有用的,但是它们在它们所能表示的范围和准确性方面是有限的。希望这些方格旗图可以成为你学习 SQL 的很好的参考。感谢阅读!
如果您喜欢阅读这篇文章,下面是我写的其他相关 SQL 文章:
在 LinkedIn 上关注我,了解我所有帖子的最新动态。如果你想知道我是如何进入数据科学的,我在我的简历中写了这个。
另外,如果你还没有加入 Medium,并且想无限制地阅读我的文章,请考虑通过我下面的推荐链接加入😃
https://medium.com/@andreasmartinson/membership
参考
- A.Martinson,如何在你的数据科学职业生涯中使用 SQL 反联接 (2022),走向数据科学
- A.Martinson,初级/中级 SQL 文章,www.reddit.com
- D.Cassel,Spaces vs . Tabs:Google 的 Golang (2016)重新点燃了 20 年的争论
- 米(meter 的缩写))Winand,解释连接时对维恩图说不 (2016),www.reddit.com
- R.我们能停止使用 SQL JOINs venn diagrams 吗? (2019),走向数据科学
- sql-practice.com,SQL Practice.com
- www.reddit.comSQL怎么发音
- D.张伯伦,SQL 的早期历史 (2012),IEEE 计算历史年鉴
- lukaseder,在解释连接时对维恩图说不 (2016),Jooq 博客
- H.Wickham & G. Grolemund, R for Data Science (2017),O'Reilly
- A.马丁森(2022), SQL 方格标志连接图,www.github.com
- C.L. Moffat,SQL 连接的可视化表示 (2009),代码项目
你需要的不仅仅是一个词——云
原文:https://towardsdatascience.com/you-will-need-more-than-just-a-word-cloud-232658916abe
分析单词的五大技巧

不管任何人告诉你什么,语言和想法可以改变世界。”——约翰·基廷
单词是任何文本或文档的组成部分,分析这些组成部分可以让您深入了解文本。在这个故事中,我将展示热门词汇分析技术。我们将从最基本的词云开始,然后转向一些高级技术来掌握词分析。
资料组
我将使用受欢迎的电视节目《老友记》中的数据集。这些数据涉及从 1994 年到 2004 年的剧集。有 236 行,对应 236 集。每一行都有关于这一集的摘要文本,我们将使用文字分析来分析这一集的摘要文本。

朋友数据集(图片由作者提供)
数据集引用可在故事的结尾获得。
1.词云
最基本的,但必须具备的单词分析是单词云。它让你深入了解经常出现的单词。然而,你需要排除停用词,如 the,is,that,are 等,所以这是剧集摘要中的词云。不出所料,出现频率最高的词都与演员有关。

文字云(图片由作者提供)
Wordcloud 看起来很惊艳,你可以通过改变颜色来打动你的观众。

不同颜色的文字云(图片由作者提供)
2.单词直方图
单词云的缺点是,出现频率最高的单词非常突出,以至于我们不知道哪些单词比其他单词出现得更多。这个问题可以通过使用单词直方图来解决,如下所示。

单词直方图(图片由作者提供)
最右边的条表示出现次数最多的单词。我们看到出现次数最多的单词是 Rachel,出现了 174 次,Ross 出现了 157 次。虽然在单词云中,这两个单词看起来大小相似,但我们可以在单词直方图中看到差异。
这是单词直方图的动画视图。

单词直方图动画视图(图片由作者提供)
3.NGRAM 分析
现在让我们从分析单词开始,分析单词对,也称为 n 元语法分析,如下所示。

你可以观察到 Monica-Chandler 是最常见的单词对。这意味着很多剧集都是围绕莫妮卡和钱德展开的。
4.文字网络
让我们进入一个词汇网络,让我们的分析更上一层楼,如图所示。

文字网(图片由作者提供)
可视化显示了单词是如何相互连接的。它允许你详细地探索这个角色。例如,我们看到 Joey 与试镜和食物等词联系在一起。这意味着他的工作以及他对食物的偏好。
5.Word 文档热图
现在让我们来看看剧集词热图,它可以分析一集的内容。

Word 文档热图(图片由作者提供)
在 X 轴上,你可以看到特定于一集的词。Y 轴代表单词。你会发现角色的名字不在这里,因为他们是所有剧集的共同名字,而不是某一集的特定名字。
热图的颜色显示了某个单词是否对该集特别重要。它是基于 TF-IDF 对单词的评分。
我们看到“再见”这个词对第 233 集特别重要。查看第 233 集的摘要文本,我们看到这与一个告别聚会有关。
第 233 集描述:“那伙人将瑞秋抛出一个 再见 的聚会,期间她向大家逐个说 再见”
热图这个词是快速理解一集内容的好方法,而不是阅读整个摘要。
结论
通过分析单词,我们可以非常深刻地了解文章的内容。多种技术,如单词云、单词直方图、单词网络和 word 文档热图,将帮助您深入探索文本。
数据源引用
数据来自https://www . ka ggle . com/ruchi 798/friends-TV-show-all-seasons-and-episodes-data
数据集具有许可证 CC0 公共域。该数据集可用于非商业或商业目的,无需征得许可
.
请订阅,以便在我发布新故事时随时获得通知。
https://pranay-dave9.medium.com/subscribe
你也可以通过我的推荐链接加入 Medium
https://pranay-dave9.medium.com/membership
额外资源
网站(全球资讯网的主机站)
你可以访问我的网站进行零编码分析。https://experiencedatascience.comT21
Youtube 频道
这是我的 YouTube 频道
https://www.youtube.com/c/DataScienceDemonstrated的链接
你的异常检测模型比你想象的要聪明
原文:https://towardsdatascience.com/your-anomaly-detection-model-is-smarter-than-you-think-c1cade5fcabe
多变量时间序列异常检测模型可以提供丰富的见解,如果你投资一些时间在后处理他们的结果…

马库斯·斯皮斯克在 Unsplash 上的照片
在处理工业传感器数据时,我经常处理异常检测用例。在过去的十年里,我一直在和几十个客户研究这个话题,在过去的五年里几乎每天都在研究。我接触的典型终端用户是工厂经理、工艺工程师或生产线上的操作员。大多数过程工程师都非常精通统计学,有些甚至是优秀的工业数据科学家。不过,这是一个例外,而不是常规,你最终会陷入必须回答棘手问题的讨论中:
“我需要知道为什么你的模型检测到异常。在调整制造流程之前,我需要可靠的根本原因分析。”
“异常检测是不够的:当模型检测到异常时,已经太晚了。我需要预测来证明在这种方法上投入时间和精力的合理性。”
"我需要处方:告诉我应该怎么做才能防止失败的发生."
诸如此类……前段时间我在 LinkedIn 上发布了几个简短的演示来揭开异常检测的神秘面纱(见 本帖 和 本帖 )。在这篇博文中,我将详细介绍如何为您自己的模型生成类似的输出,并使它们更加智能。这样你就能更好地应对上述问题了!简而言之,到本文结束时,您将拥有:
- 用 Amazon SageMaker 在云中建立一个 Jupyter 环境,
- 克隆了 GitHub repo,其中包含了遵循本文的所有代码。
- 发现一个好的数据集,下载并探索它
- 使用 Amazon Lookout for Equipment(AWS 专门用于异常检测的托管服务)训练异常检测模型
- 可视化异常检测模型的原始结果
- 对结果进行后处理,以获得更有意义的见解
所以,我们开始第一步吧!
让我们舒适:准备您的环境!
我鼓励你跟随这篇博文,浏览 GitHub,获取 这一系列的 Jupyter 笔记本 。你可以使用你常用的 Jupyter 环境,或者用 Amazon SageMaker 创建一个。
- 如果您想使用您通常的 Jupyter 环境,已经有了一个训练过的异常检测模型,并且已经在一些测试数据上对其进行了回测,以获得一些结果,那么您已经可以进入下一段了!
- 如果你想使用你通常的 Jupyter 环境,有一些数据,并想尝试 Amazon Lookout 的设备来训练一个异常检测模型,你将需要创建一个 AWS 帐户,并确保你的帐户凭证可以从你的环境中访问。
- 如果你想留在 AWS 环境中,你可以创建一个 AWS 账户,启动一个 Amazon SageMaker 环境,让它访问 Amazon Lookout for Equipment API。您可以使用与本文相同的数据集,也可以自带数据集。
我现在认为您的环境已经准备好了:第一步是在其中克隆这个 GitHub 库:
git clone [https://github.com/michaelhoarau/smarter-anomaly-detection.git](https://github.com/michaelhoarau/smarter-anomaly-detection.git)
该资料库中的笔记本将引导您完成从数据准备到结果后处理的整个过程。可以从第一个[1_data_preparation.ipynb](https://github.com/michaelhoarau/smarter-anomaly-detection/blob/main/notebooks/1_data_preparation.ipynb)开始。
数据概述
等一下…你把异常现象叫做什么?
你可能会觉得这很奇怪,但是这个问题经常被忽视!要从机器学习的角度构建您的业务问题,理解以下内容至关重要:
- 你感兴趣的异常的形状
- 他们如何积累加班时间
- 当它们被触发时会发生什么
此外,如果您想了解构建异常检测系统的潜在投资回报,您还需要了解:
- 谁是潜在用户
- 他们需要做出什么样的决定
- 哪些见解可以使这些决定更容易做出
- 这些见解应该如何传达
收集这些知识将确保您的系统被最终用户实际采用。
也就是说,让我们看看可以从时间序列数据中捕捉到的不同类型的异常:
- 突变:这是最容易发现的。变化(单变量或多变量,当它发生在多个时间序列时)是突然的,并且值的变化是明显的。在工业环境中,这些突然的变化经常在边缘水平被注意。特定的软件组件监控这些过程和设备。
- 水平变化:当一个给定的时间序列在基于基本条件或运行模式的数值范围之间变化时,这种情况就会发生。如果您想在检测异常时考虑所有操作模式,您需要注意将它们全部包含在您的训练数据中。否则,在推理时,一个新的操作模式可能会被认为是一个异常,您最终会有许多误报要处理。
- 趋势:一组信号可以随时间变化(不一定同向)。当您想要评估一个过程或一件设备的状况时,这些趋势异常将是搜索的很好的前兆事件。它们将帮助你在实际故障可能发生之前建立预警信号。
现在,我们已经从定义的角度对该领域进行了阐述,让我们通过查看一个实际的工业多元数据集来将其付诸实践…
数据集概述
搜索带有注释异常和足够历史数据的工业多元时间序列数据集本身就是一个挑战。我见过的唯一有相关异常的公开数据集是 Kaggle 提供的一个工业水泵数据集。你可以从 这个链接 下载这个数据集。该数据集包含 2018 年 4 月 1 日至 2018 年 8 月 31 日(5 个月)的 52 个传感器,采样率为 1 分钟。但是,由于没有与此数据集相关联的许可,因此您不能将其用于任何商业用途。
为了帮助您入门,前面提到的 repo 中的第一个笔记本([synthetic_0_data_generation.ipynb](https://github.com/michaelhoarau/smarter-anomaly-detection/blob/main/notebooks/synthetic_0_data_generation.ipynb))将生成一个合成的多变量 timeseries 数据集,并在其上添加不同类型的异常:这不像真实的东西,但它将足够接近于在这里暴露我的思想过程。我用 20 个信号、1 年的数据和 10 分钟的采样率生成了一个数据集。
让我们加载这些数据,看看一些信号:
- 确定故障时间(下面的红点)
- 突出显示该虚拟资产被破坏或从故障中恢复的时间段(在下面用黄色标出):

综合数据时间序列概览(图片由作者提供)
使用该数据集的前五个月来训练模型(从 2021 年 1 月到 2021 年 5 月),将为我们提供至少 2 个异常范围来评估我们的模型。
- 其中之一是 2021 年 11 月可见的黄色带
- 另一个将是 2021 年 9 月初绿色信号上可见的奇怪尖峰
让我们为这 20 个时间序列绘制一个带状图:

时间序列带状图(图片由作者提供)
我使用带状图来压缩时间序列信号信息。压缩后,每个时间序列都变成一条彩色带,其中:
- 信号的低值为绿色
- 中等值为橙色
- 高值为红色
这个简单的条形带非常便于显示何时出现低值或高值。当把我们数据集的 20 个信号的所有条带放在一起时,我们得到上图。你可以看到一些可能感兴趣的垂直红色区域。其中一些与资产被标记为损坏的已知时期相匹配(大约在 11 月)。
在某些情况下,我们还可以看到许多信号从左到右从红色变为绿色:这可能是某个日期后数据集发生漂移的迹象。这是我之前遇到的一个数据集示例,您可以看到 2017 年 12 月后发生的转变:

沿时间轴移动的带状图(图片由作者提供)
在进一步调查后,这种类型的信息对于为您训练的任何异常检测模型设置再训练触发器将是至关重要的。如果不知道这一点,您将逐渐看到您的模型发布越来越多的误报,使您的模型不可用,并可能失去模型最终用户的信任。
为了生成带状图,我使用了我的tsia包(pip install tsia)和下面的代码:
带状图生成(由作者编写代码)
如果你想了解更多关于带状图的知识,可以看看我之前的文章,在这篇文章中我深入探讨了带状图是如何产生的:
一旦你的合成数据集生成,你可以使用第二个配套笔记本([synthetic_1_data_preparation.ipynb](https://github.com/michaelhoarau/smarter-anomaly-detection/blob/main/notebooks/synthetic_1_data_preparation.ipynb))为亚马逊寻找设备准备数据。请随意更新它,以便将数据准备成适合您自己的异常检测模型的格式。您已经准备好训练您的模型了!
训练异常检测模型
我将使用 Amazon Lookout for Equipment 在之前的数据集上训练一个异常检测模型。为此,您需要一个 AWS 帐户。然后,在已经提到的 GitHub repo 中,您可以运行第三个配套笔记本(称为[synthetic_2_model_training.ipynb](https://github.com/michaelhoarau/smarter-anomaly-detection/blob/main/notebooks/synthetic_2_model_training.ipynb)的笔记本):这将把数据推送到亚马逊 S3(大多数托管 AWS AI/ML 服务用于输入数据集的 AWS 块存储服务),摄取数据并训练异常检测模型。
如果你想了解更多关于 Amazon Lookout for Equipment 的信息,在我的书《AWS 上的时间序列分析》中有六个章节专门介绍这项服务:
https://www.amazon.com/Time-Analysis-AWS-forecasting-anomalies-ebook/dp/B09MMLLWDY
随意看看 这篇博文 和 这篇另一篇 以获得更多关于这些章节的详细信息:
训练过程包括对历史数据的回溯测试评估,在这种情况下,如果服务当时正在运行,您可以检测到它应该检测到的事件。与其他异常检测模型一样,Amazon Lookout for Equipment 的原始结果如下所示:

亚马逊寻找设备原始结果(图片由作者提供)
在顶部,我绘制了一些传感器的时间序列。下方绿色部分是虚拟资产从故障中恢复的已知时间段。在底部,红色的,是由 Lookout for Equipment 检测到的事件。当看到这些结果时,有人可能会指出:
“有些误报,我没时间去调查每个事件!”
“你的模型只检测已经发生的异常情况,没有用!”
“我有数百个传感器:当你的模型检测到异常时,我仍然必须调查我的整个操作,我不会在这里节省任何时间!”
听起来很熟悉?让我们看看如何从您的异常检测模型中获得更多见解,并开始赢得您的最终用户的更多信任…
后处理模型的原始输出
如前所示,最基本的异常检测模型能够从您的时间序列数据集中标记出它认为异常的时间戳。现在让我们打开回购 的 第四笔记本,开始对这些结果进行更详细的后处理。

亚马逊寻找设备原始结果(图片由作者提供)
对这个异常检测模型进行字面解释,你可能会说你有太多的误报(所有红色事件都发生在 2021 年 11 月故障之前)。您可能希望模型标记失败周期,而不标记之前或之后的任何内容。但这与许多假设有关:
- 你有失败的确切日期
- 您实际上知道这是一个故障,而不是维护事件
- 您知道在故障发生之前,您的设备或过程不会触发任何前兆事件
这有很多假设,大多数时候你不会处于这种确切的情况:在你的时间序列中任何可见的异常事件要么是前兆事件,要么是可检测的异常(对未来事件的预警),要么是故障,要么是维护活动,要么是你的工业过程在问题后恢复的愈合期。
这一事实实际上给了我们一些余地来对我们的异常检测模型原始输出进行后处理,尤其是当您想要了解您的资产/流程所处的条件时(用于基于条件的维护方法的条件监控)。
测量事件率
异常检测模型触发事件,作为用户,您必须决定某个事件实际上是您关注的异常、您不想捕获的异常、预先警告失败即将到来的前兆事件还是误报。过滤掉最后一个可以通过测量你在感兴趣的一段时间内有多少事件来实现。例如,测量与我从另一个数据集获得的类似结果相对应的日常事件的数量,产生了类似于下图的图:

测量异常检测模型发布的事件率(图片由作者提供)
基于此,您可以决定仅在每日事件率达到每天至少 200 次后才通知操作员。这将允许您只对在此期间检测到的 41 个事件中的 3 个事件做出反应。我的异常检测模型输出一个数据帧,每个时间戳都有一个状态(0 表示没有检测到异常,1 表示发现了异常)。我的数据采样率为 5 分钟,因此如果我在12 x 24 = 288 periods上使用滚动窗口,我将覆盖一整天的数据:
使用这个简单的技术,您可以解决(至少部分解决)前面提到的一些问题。当事件发生率开始变大时,您可以使用它来做出实际反应(允许您从检测到预测),并过滤掉误报(当检测到稀少事件时)。
现在,让我们尝试解决最后一个问题,即在帮助进行根本原因分析时,异常检测模型缺乏精确性会浪费时间…
测量和绘制变量贡献
许多异常检测模型也产生一些可解释的细节,例如每个变量对任何检测到的给定事件的贡献。Amazon Lookout for Equipment 也不例外,每个模型结果都会在检测到的每个事件的 JSON 输出中包含以下字段:
'predicted_ranges': [
{
'start': '2019-08-08T00:42:00.000000',
'end': '2019-08-08T01:48:00.000000',
'diagnostics': [
{'name': 'synthetic\\signal_00', 'value': 0.052},
{'name': 'synthetic\\signal_01', 'value': 0.023},
{'name': 'synthetic\\signal_02', 'value': 0.038},
{'name': 'synthetic\\signal_03', 'value': 0.023}, ... {'name': 'synthetic\\signal_17', 'value': 0.049},
{'name': 'synthetic\\signal_18', 'value': 0.033},
{'name': 'synthetic\\signal_19', 'value': 0.046},
{'name': 'synthetic\\signal_20', 'value': 0.044}
]
}, ...]
如果你在看我的配套笔记本,你会看到我如何重新格式化这种类型的输出。我使用了适合进一步绘图和处理的扩展数据框架:

每个时间戳的可变贡献的扩展结果数据框架(图片由作者提供)
现在,让我们绘制变量重要性随时间的演变图,而不是事件率:

可变重要性演变(图片由作者提供)
我给每个传感器分配了不同的颜色,这个图还不可读。看起来其中一个绿色信号是左边第一个大事件的关键因素。另一个信号,红色的,对右边的事件来说有上升的趋势。让我们使用累积条形图:

使用条形图的可变重要性演变(图片由作者提供)
这有点好,我们实际上可以更好地跟踪趋势,并确定多个信号可能是给定事件中最有贡献的信号。然而,当试图理解“最重要”的信号时,我们不需要绘制所有的 50+信号。让我们试着把重点放在前 5 位,并把所有其他的放在“其他信号”类别中:

关注前 5 个信号的可变重要性演变(图片由作者提供)
它的绘图速度更快,可读性更强。对于 20 个信号,如果每个信号对给定事件的贡献相同,则每个信号的贡献为 5%,5 个信号的贡献为 25%。我们可以从上面看到,前 5 名传感器始终达到 30%到 60%的贡献,这意味着这可能是非常有趣的进一步调查…
现在,让我们添加一些信号和事件(已知的或检测到的),为这个柱状图添加一些额外的上下文:

最终变量演化图(图片由作者提供)
你会在[synthetic_3_model_evaluation.ipynb](https://github.com/michaelhoarau/smarter-anomaly-detection/blob/main/notebooks/synthetic_3_model_evaluation.ipynb) 笔记本里找到所有的详细代码。从上面显示的扩展结果数据框架(我们称之为df)中,前面的条形图是用以下代码生成的:
变量演变条形图生成(作者代码)
下一步是什么?
在本文中,您了解了如何利用事件率将您的模型从单纯的检测提升到某种程度的预测,以及如何开始更好地了解哪些信号是应该首先研究的。
在我的下一篇文章中,我将详细介绍如何更深入地理解我的异常检测模型的结果:
我希望你觉得这篇文章很有见地:如果你不想错过我即将发布的帖子,请随时在这里给我留下评论,并不要犹豫订阅我的 中型电子邮件源 !想支持我和以后的工作?通过我的推荐链接加入 Medium :
https://michoara.medium.com/membership
您对这 5 个问题的回答将突出显示作为一名有抱负的数据科学家需要投资的领域
了解在哪里投入时间是成为更好的数据科学家的第一步

Denys Nevozhai 在 Unsplash 上拍摄的照片
自从 2021 年【大辞职】开始,人们被推向他们认为更充实、更灵活、更有挑战性的职业。数据科学是吸引人们关注的职业之一。
然而,数据科学有一个特点,那就是它是一个非常具有挑战性的职业。由于学习成为数据科学家所需的技能需要大量的努力,并且转行需要大量的时间,因此建议寻求进入这一新兴领域的个人长时间照照镜子,以确定这是否是他们的道路。
您对这五个问题的回答将帮助您确定作为一名有抱负的数据科学家,您应该投资于哪些领域,还将帮助您找出如何解决这些领域的问题,使您成为一名更有效的数据科学家。
1.你是一个能够跟上不断变化的数据科学技术的终身学习者吗?
与技术领域的任何其他专业一样,数据科学需要不断学习和专业发展,才能保持领先地位,并保持与公司的相关性。
这是数据科学职业生涯中最重要的真理之一,也应该是你在考虑这条道路时问自己的第一个问题。承认自己不是那种想把整个职业生涯都花在学习新技能上的人没有错。
我记得在大学第一次见到这个新的团队成员,并被告知他喜欢在业余时间写代码。他不仅喜欢它,而且热爱它,除了包括编程在内的所有大学工作之外,他还总是参加黑客马拉松,实现自动化,并执行自己的个人项目。这位团队成员最终成为了我们当中最好的程序员,并且非常幸运地从大学毕业后就在技术领域找到了一份很好的工作。这个故事不是要告诉你,你必须把醒着的每一个小时都用来学习和实践数据科学。相反,这里是给你一个例子,当你把一些时间投入到进一步的学习和实践你希望得到的工作中时,你会取得什么样的成就。
终身学习将发生在工作中,工作之外,以及你的无薪休息时间。例如,你现在正在读这篇文章,而且可能没有报酬。虽然您可能只是基于一个有趣的标题有意识地选择了这篇文章,但您也在下意识地扩展您对数据科学领域的理解和知识。
无论您需要跟上新的编程语言、替代的数据可视化技术,还是您所在领域的商业实践的进步,持续学习都将是您数据科学职业生涯中不变的一部分。
如何成为一名终身学习者
激情是帮助你成为终身学习者的基本要素。
没有激情,你就不会愿意每天留出时间来提高自己的技能。
因此,确定您的激情所在以及如何利用这些激情提升您的数据科学技能至关重要。例如,你可能会对南美野火造成的森林砍伐率感兴趣。这立刻给你一个项目想法,你可以用数据科学来解决。例如,你可能有兴趣开发一个机器学习模型,预测到 2050 年世界上最常见的眼睛颜色。
你的热情不一定与你为工作所做的数据分析类型相关联。相反,您可以通过从事与您的兴趣相关的数据项目来进一步了解编程语言、数据可视化技术和数学建模。这些能力不仅会让你的工作变得更容易,还会通过愉快的努力获得。
2.你分析数据的能力有创造性吗?
作为一名数据科学家,经常感觉像是被困在一个荒芜的岛上,被告知要设计一个系统才能回到大陆。你没有资源,没有材料,没有食物,没有水,然而你被期望使用你想象中设计的某种系统逃离这个岛。
在办公室里也是一样,数据科学家经常被告知要使用不符合标准的数据、很少的资源和很少的预算来解决相当多的业务问题。
因此,数据科学家的一个重要属性是创造力。
虽然创造力不能神奇地让你的数据比实际情况更好,也不能改善你的工作条件,但它可以充分利用一般情况。
此外,创造力对于操纵、解释和从数据中提取意义至关重要。不仅如此,它还能帮助你理解业务问题以及你选择如何解决它们。数据洞察力和未来预测仅受您用于解决问题的创造力的限制,可以进一步帮助您理解复杂的概念,并在数据科学的不同方面之间建立联系。
旁注,当你的代码运行一个分析需要几个小时时,作为一个数据科学家,发挥创造力也是很重要的。创造力会让你不会感到无聊,当你的老板走过时,会让你看起来好像在做很多重要的工作。
如何变得更有创造力
创造力似乎是我们在小学时就被敲打出来的东西,在那里,我们因为用不同于我们所学的方法解决数学问题而受到斥责,并被迫在线内着色。
直到后来我们终于走出学校,突然被问到我们的观点是什么,或者我们将如何解决一个问题,或者我们对一张照片的想法是什么,或者我们将如何以不同的方式分析一些数据,这一点才变得明显。
让孩子般的创造力回到你生活中的关键是开始问问题。
“为什么要这样分析数据?”
“使用这种可视化技术来更好地准确表达我们的大部分价值观不是更好吗?”
“你不认为这个机器学习模型可以通过实施更好的假设检验方法来改进吗?”
一旦你开始质疑事情是如何完成的,你就开始锻炼你大脑中早已死亡的那部分。你大脑的这一部分将开始以不同的方式看待商业问题,询问你如何改进商业问题,并改进你如何将技术概念转化为一般概念。
3.你愿意解决你不知道如何解决的问题吗(当你需要帮助时,你愿意寻求帮助吗)?
问任何一个高级技术人员,他们会告诉你他们一天的大部分时间都花在搜索如何解决一个给定的问题上。
尽管我们认为我们的教育,无论是通过自学还是传统方式,都会给我们解决职业生涯中遇到的每一个问题所需的所有工具,但事实仍然是每天仍有无数的问题需要谷歌搜索。
无论是如何进行特定类型的数学建模,如何修复代码中的错误,或者如何在可视化中获得正确的颜色,总会有一些你需要解决的问题,但你不知道如何解决。
我回想起我在大学的时候,这对我来说是正常的——我在一个软件工程项目中,在那里我感觉自己不断地从消防水管喝水,并且在处理编程问题时总是在我的舒适区之外。我很快认识到,当我足够谦逊地承认我需要帮助来解决问题时,最好的学习经历就来了。我得到的帮助不仅让我避免了几个小时的碰壁,还让我不断进步,不会在小细节上陷入困境。对我来说,关键是给一个老大学尝试的问题,当我用尽所有选择时,寻求帮助。
数据科学的美妙之处在于它是一个需要解决的永无止境的问题。这意味着,在日常生活中,你需要通过自己的勇气和决心(以及良好的谷歌搜索技能)或者通过他人的帮助来解决问题。
无论情况如何,你都需要确定这种工作是否适合你。你愿意每天都知道你可能不知道你试图解决的问题的答案吗?更重要的是,当你需要帮助的时候,你愿意寻求帮助吗?
如何提高自己的解题技巧
正如我之前开玩笑说的,我们大多数人花时间在 Google 和 StackOverflow 上,试图解决我们的问题。
但是如果我告诉你这是一个实际上并不能提高你解决问题能力的拐杖呢?
在谷歌上寻找代码修复并不是真正教你如何解决问题——它实际上只是教你如何提出伟大的问题(这是数据科学中一项有价值的技能,但不是这里的重点)。因此,提高你解决问题技能的最好方法是在 StackOverflow 上安装一个网站拦截器,并专注于理解为什么你的代码出错,以及应该如何工作背后的基本原则。
这个过程将带你回到决策树、代码文档和你的同事,帮助你发展你自己对为什么出错以及如何修复它们的感觉。这里的关键是开始迫使你的大脑通过错误、错误和故障进行逻辑思考,确定根本原因,然后想出一个你自己开发的适当解决方案。
走捷径去 Reddit、Quora 或 StackOverflow 可能很有诱惑力。然而,你在欺骗自己,让自己失去了学习和拓展技能的机会。不幸的是,提高你解决问题的能力没有捷径,除了走出去,用艰难的方式解决问题。
4.你能讲一个将数据转化为可行建议的好故事吗?
数据科学家不仅仅是包裹在一个令人困惑的谜中的编码员、数学家和图形设计师。
他们也是故事讲述者和专家交流者。
任何公司的梦想数据科学家都可以编写生产就绪的代码,可以理解数学概念和关系,可以设计引人注目、易于阅读的漂亮的可视化效果,并可以使用数据讲述一个伟大的故事。
然而,在竞争一份数据科学工作时,大多数候选人只会具备前三种属性。
只有少数人有能力用塑造公司未来的数据来讲述一个故事。
数据科学家必须是专业的沟通者,能够将数据告诉他们的内容转化为易于操作的目标,然后由公司利益相关者采取行动。关键是将这些信息以故事的形式呈现出来,向利益相关者解释他们目前所处的位置,他们是如何到达那里的,以及他们未来需要去哪里。这些故事必须提供准确的见解,但不要过于技术性——它们必须以原始数据无法做到的方式与利益相关者相关联。
幸运的是,这是一项可以随着时间的推移而提高的技能。因此,即使你对这个问题的回答是否定的,也不应该阻止你进入数据科学领域。数据科学的伟大之处在于,你不允许自己置身于一群软件工程师之中,这些工程师只需要在他们的 IT 业务分析师来查看项目进展情况时与外部人员交谈。相反,你会不断被迫解释你的结果,并告诉你的数据与外界的故事。总会有机会练习讲故事,所以如果你愿意,你会很快提高你的沟通技巧。
如何成为讲故事的专家
最好的故事讲述者是那些阅读的人。很多。
没有经历过别人如何讲述自己的故事,你就无法开始讲述自己的故事。
这里的第一个策略是从消费数据科学内容开始。这可以是该领域内的任何内容,例如关于如何进行特定分析的技术文章,关于如何成为更好的 Python 程序员的提示,或者像本文这样专注于数据科学个人发展方面的文章。通过观察别人如何将单词组合起来形成解释,你可以开始根据你欣赏和喜欢阅读的写作风格来构建自己的故事。
第二个策略是简化你的故事。太多时候,数据科学家被一个项目的本质数据细节所困扰,而没有意识到接收端的人可能对面向对象编程、A/B 测试或 Tableau 一点也不感兴趣。关键是要简化你的故事,使它们不包含行话,易于理解,并为利益相关者提供开头、中间和结尾(或我们是如何来到这里的,我们目前在哪里,以及我们想要去哪里)。
正如我上面提到的,讲故事是一项技能,如果你把自己放在那里去做,你会在你的职业生涯中不断得到练习的机会。告诉你的经理他们可以展示你的项目是很容易的。然而,如果你不把自己放在这种情况下,并以清晰、易懂、简洁的方式练习表达你的观点,你的技能就不会提高。
5.你有让你对一个组织至关重要的商业头脑吗?
可以说,数据科学求职者最重要的特质是商业头脑。
在申请工作时,数百名数据科学家将竞争一个职位。每个人都可以写代码,每个人都可以做微积分,每个人都可以产生漂亮的数据可视化。那么,还有什么能让你从其他候选人中脱颖而出呢?
商业头脑。
如果没有商业头脑,你的数据分析将缺乏公司和行业的本质,而这些本质是做出有益于你的组织的结论所必需的。商业敏锐性为你提供了所需的洞察力,使你能够根据你的分析得出结论,从而塑造你的组织的发展势头。商业敏锐性也是一种工具,你可以用它来找出你的分析中的任何不准确之处,并确定如何在未来加以改进。
据估计, 85%的数据科学项目失败了,这一比率通常可以归因于数据科学家缺乏商业头脑,导致分析没有方向,没有特定商业问题的答案,并且无法预测这些结果如何应对未来的未知。
商业敏锐性让你明白你在寻找什么,应该如何寻找,以及一旦得到结果你打算怎么做。
每个行业的商业敏锐度看起来都不一样,因此,无论是从行业内不同职位的工作中获得的知识,还是在学习成为数据科学家的过程中获得的知识,你都必须具备一些商业敏锐度。
如何提高你的商业敏锐性
提高商业敏锐性的最好方法是从你的特定领域获取内容。
播客、时事通讯、博客文章、新闻文章等等,都是提高你的商业敏锐度的好方法,而不需要积极研究这个主题。
还记得你小时候学东西的轻松程度吗?比如我 4 岁的时候曾经对恐龙很痴迷。这意味着我一直在消费关于恐龙的内容,并且可以描述你问我的任何恐龙,包括它们的长相,它们的饮食,以及它们在地球上的什么地方。不仅如此,我经常阅读的一部分是专为大学水平的读者设计的关于恐龙的纯科学书籍。在这么小的年纪就吸收这本书,意味着当人们用恐龙的通用名而不是学名时,我要到处纠正他们。
为了提高你的商业敏锐度,你需要像小时候痴迷于任何事情时一样进行学习。幸运的是,内容涵盖一切,从生物医学科学的进步到股票市场的最新变化,到关于石油和天然气新商业实践的讨论,再到关于医院如何更有效运营的报告,可以用来帮助你更好地了解你的行业。有了这些知识,您可以增强您的数据分析,以确保您完全回答了您收到的业务问题,并可以就如何改进未来的数据采集和分析提出建议。
订阅将我的故事直接发送到您的收件箱:故事订阅
请成为会员,使用我的推荐链接获得无限制的媒体访问权限(我将收取少量佣金,无需额外费用):媒体会员
通过捐赠来支持我的写作,以资助更多像这样的故事的创作:捐赠
您的数据目录不应该只是多一个用户界面
原文:https://towardsdatascience.com/your-data-catalog-shouldnt-be-just-one-more-ui-e6bffb793cf1
深入了解以 API 为中心的数据目录如何通过组合不同类型的元数据来帮助您确保数据平台的成功

克劳迪奥·施瓦兹在 Unsplash 上的照片
在过去的几年中,数据领域在数据平台的设计、构建和使用方面经历了重大转变。这一变化,可以被称为数据技术的第三次浪潮,迎来了现代数据栈的时代。在这个时代,存储和计算方面的可扩展性是一个已解决的问题。相反,我们终于有机会专注于从数据中提取尽可能多的价值,民主化对数据的访问,甚至生活质量的提升——而不必太担心技术限制和复杂性。
这个时代的关键支柱之一是数据目录,这一概念无论如何都不是新的或革命性的——我们可以想到 Informatica 或 Hadoop 生态系统的 Apache Atlas——但由于当今强大的现代数据堆栈,它有望释放组合不同类型元数据的未开发潜力。
和其他部分一样,这个问题最初是由数据驱动的科技公司解决的。Airbnb 在 2017 年谈到了 Dataportal ,然后网飞在 2018 年用一篇关于 Metacat 的博客文章进行了反击。Lyft 在 2019 年开源了基于拉的 Amundsen ,然后 LinkedIn 在 2020 年初通过开源基于推的 DataHub 进行了反击。
期望很高:释放元数据的未开发潜力将优化我们与数据资产的交互方式,简化流程,解决当今的许多数据管理问题,并帮助我们在当今的数据谱系迷宫中导航。然而,我们已经到了 2022 年,十几个不同的数据目录只为我们的堆栈增加了一个东西:UI。
当然是一个有很多功能的用户界面。当今的数据目录提供了无缝的数据发现、集中的数据词汇表、广泛的集成和许多其他有用的功能。但是,这真的是 吗 ?我个人不这么认为。
在本文中,我们将介绍数据目录应该(并且能够)实际提供的一些功能。但是首先:
哪里出了问题?
直到 2019 年,大规模解决元数据管理仍然是一个复杂的挑战,有许多未解的问题。我们应该从不同的工具中提取元数据,还是应该由工具将元数据推入目录中?我们应该只对表进行分类,还是应该将所有种类的数据资产(仪表板、笔记本、管道等)都包括在图表中。)甚至人?我们应该考虑列级沿袭还是只会让事情变得更加复杂?
到 2019 年,前景变得更加清晰,许多主要的科技公司披露了他们如何解决这个问题,以及他们的方法可行/不可行。在接下来的几个月里,SaaS 工具公司能够提供类似的功能,我们很快就达到了一个状态,任何公司都可以向其堆栈中添加一个数据目录,提供数据发现和沿袭等核心功能,然后,这一进展就停滞了。
我们再也听不到创新的新功能或重大改进。相反,今天的数据目录似乎满足于成为数据平台中的一块砖。一块砖告诉我们仓库里有什么,资产是如何连接的,但仅此而已。
现代数据堆栈的当前状态依赖于一组不同的工具,每个工具都存储一些元数据。例如,我们的数据编排工具知道我们的数据资产和构建它们的管道的执行状态,我们的数据可观察性工具知道我们的资产的健康状况,我们的数据目录确保我们可以导航不同类型的数据资产。

现代数据堆栈中元数据的当前状态:连接到不同资产(黄色)并存储一部分元数据的不同工具(绿色)。(图片由作者提供)
在这种当前状态下,堆栈中的每个新工具都需要回到一个空白的绘图板,并解决所有其他工具已经解决的问题,例如直接从不同的源中检索元数据,并为不同的实体定义模式(例如,每个工具都需要回答诸如“我们希望为给定的表存储什么元数据?”以及“我们可以从 BI 资产中检索哪些元数据?”).
我个人认为,这种当前状态 就是 阻碍了数据编目的进程。如果元数据分散在定义了自己的模式和标准的十种不同工具中,我们如何释放元数据的潜力呢?许多数据目录试图通过从尽可能多的工具中提取元数据来解决这个问题,但是添加更多的连接器和集成从来都不是解决混乱的架构的正确答案。
前进的道路:更少的 ui,更多的 API
“元数据管理是一个已解决的问题”
尽管可能感觉不像,但目前没有任何技术复杂性或未解决的工程问题阻止给定的公司将其所有元数据集中在一个地方。(但行业特有的边缘案例可能依然存在。)
下一个合乎逻辑的步骤是让数据目录成为公司内部的中央元数据存储库,其他工具通过 API 连接到它,以检索任何所需的元数据,并用新的元数据丰富它。这种转变要求关于不同元数据实体定义的标准化,这是像 OpenMetadata 这样的开源项目已经实现的事情。
一旦所有元数据都在一个位置可用,数据目录就变成了现代数据堆栈中的中央水平元数据存储库,而不仅仅是存储一部分元数据的一块砖。反过来,数据目录 UI 成为构建在此元数据存储库之上的独立组件。

现代数据堆栈中的元数据管理建议:连接到不同工具(绿色)和资产(黄色)的中央元数据库。(图片由作者提供)
这种设计提供了两个核心优势,打破了数据目录的当前状态:
- 我们(慢慢地)走向元数据标准:这停止了无休止的集成/连接器竞赛。数据堆栈中的每个工具不需要能够与其他每个工具和资产集成来交换元数据。相反,我们应该致力于定义标准,并推动堆栈中的现有工具与其保持一致。栈中的工具仍然可以利用元数据并丰富它,但这将通过标准化的实体和模式来实现。
- 数据目录成为 所有元数据 的访问点:这为我们需要组合不同类型的元数据的用例打开了大门,并且还确保我们总是可以通过简单地与目录交互来构建给定资产的完整元数据图片。
激活元数据(真的)
将公司的所有元数据集中在一个位置具有巨大的潜在优势,这对数据平台的成功至关重要。
当我们谈到激活元数据时,我们不应该仅仅着眼于模式更改后的松弛通知或测试失败时的警报。相反,有更广泛的潜在使用案例:
- 自动检测业务关键型数据资产的上游瓶颈并发出警报:假设数据目录可以访问所有元数据,那么监控业务关键型数据资产的整个上游谱系(例如财务仪表板)并自动检测数据质量、性能甚至可能的优化方面的问题将会非常简单。
- 用不同类型的元数据丰富整个数据堆栈:通过 API 将不同类型的元数据从目录中提取出来,这意味着我们可以在有意义的时候将它注入堆栈中的其他工具。例如,当给定的表出现数据质量事件时,直接在依赖于该表的下游仪表板中查看警报不是很方便吗?当前的工作流希望数据用户导航到数据目录 UI,以确保信任给定的图表或数据资产是安全的,这给这种情况增加了不必要的摩擦。
- 立即检测并修复未优化的数据资产:例如,data catalog 将能够检测到使用筛选器频繁查询的表,该筛选器使用的列不是对该表进行分区的列,因此它可以更新该表,并使用最常用于筛选的列对其进行分区。
这些只是通过集中元数据并在其上构建功能所能实现的几个例子——一旦我们考虑对我们的数据平台必须提供的所有元数据类型进行基于 API 的访问,尚未开发的可能性领域就更加令人兴奋。
主动数据目录
考虑到数据目录具有完整的数据血统,它应该是所有基于元数据的工作流的基础。
举个例子,让我们看看数据契约——这个概念最近得到了很多关注,但却没有一个明确的实现方法。在理想情况下,数据契约应该由数据目录的 API 支持。通过这样做,我们可以实现以下场景:
- 产品开发团队中的某个人提出了一个 pull 请求,该请求更新了给定表中的一列或一个模式定义。
- CI 步骤对数据目录执行 API 调用。
- 数据目录返回依赖于该列的下游表和列的列表、它们的重要性(基于业务重要性)以及它们相应的所有者。
- 重要性分数高于预定义值(例如 4/5)的受影响下游表的所有所有者将作为审阅者添加到 PR 中。
- 其受影响的下游表/列具有较低重要性的其他所有者得到关于提议的变更的通知(通过 Slack、电子邮件或任何其他工具)。
这个简单的工作流已经可以简化我们管理数据契约的方式:它可以成为管理数据资产本身的自动化部分,而不是单独管理契约和增加不必要的摩擦。数据消费者在变更发生之前得到通知(而不是在数据目录检测到变更后收到警报),并可以在应用变更之前采取行动。**
上述示例只是主动数据目录简化我们管理数据资产的方式并为新的增强和自动化打开大门的一个场景。
正确的数据目录
市场上已经有大量的数据目录。因此,可能有许多选项能够处理本文中讨论的场景——但我个人知道有两个开源项目做得很好:
open 元数据
本文中讨论的大多数想法和原则与 OpenMetadata 背后的愿景完全一致,这是一个由团队构建的开源项目,该团队改造了优步的数据文化。OpenMetadata 旨在解决通用元数据管理问题,而不是简单地开源在优步完成的工作,或者只是从优步的角度来看待问题。
数据中心
最初建于 LinkedIn,并于 2020 年开源, DataHub 从第一天起就考虑到了可扩展性。它对 API 的高度关注使它成为一个理想的目录,可以在其上丰富各种组件 LinkedIn 的团队正在做的正是这个。
结论
尽管它们可以说是现代数据堆栈中最令人兴奋的组件,但出于某种原因,今天的数据目录满足于现状,只是堆栈中多了一个 UI——但这需要改变。
前进的道路是模糊的、混乱的,而且绝不容易,但是走过这条路是值得努力的。通过将公司的所有元数据存储在一个地方,并提供优化的基于 API 的访问,我们为解决当今一些最紧迫的数据问题打开了非常广泛的可能性之门。
数据通常不正常
原文:https://towardsdatascience.com/your-data-isnt-normal-54fe98b1f322
使用其他数据科学技术获得更准确的见解

詹姆斯·考辛斯在 Unsplash 上一张一张地拍照
不管你在统计学课上学到了什么,大多数数据都不是正态分布的。想想常见的正态分布现象的例子,如身高或高考分数。如果你能收集地球上所有人的身高并绘制成柱状图,左边的尾巴会比右边的重;婴儿和儿童比成人矮,所以分布是偏斜的。直接的统计修正是去除不符合的观察值,比如孩子,或者选择一小组符合正常模式的人群。经典的正常身高例子来自一个非常特殊的子集:19 世纪伦敦监狱中的成年男性囚犯。大学入学考试也不是整齐分布的。他们使用原始分数——正确答案的数量——进行评分,然后将其换算成正态分布。我们知道原始分数不是正态分布的,因为否则它们就不需要被缩放和拟合成正态曲线。现实生活毕竟不是那么正常。
本文首先回顾了正态分布的用途和局限性。然后,我们将检查两个非正常数据的案例研究。传统的统计工具将用于帮助确定分布。还将应用生成意外结果的数据拟合函数。将开发两个案例研究的最佳解决方案。然而,最重要的收获可能是数据本身的形状揭示了大量的信息,可能比任何统计测试都多。
正态分布
19 世纪初,德国数学家和物理学家卡尔·弗里德里希·高斯首次描述了正态分布。这就是为什么正态分布也被称为高斯分布。高斯记录并分析了小行星运动的观测误差。这就是“观察”一词在回归和其他分析中的来源——字面意思是通过望远镜观察。本质上,正态分布是关于人类观察现象的错误。在许多情况下,这仍然是它今天的主要目的。
正态分布有两个参数:均值()和标准差(【σ】)。对于任意值, x ,其概率分布为:[1]

美国商务部,公共领域图像
曲线围绕平均值对称,呈钟形。对于 0 的平均值和 1 的标准偏差,一百万个随机正态观测值的曲线如下所示:

作者图片
曲线的一些特征使得统计分析更加方便。均值、中值和众数都是一样的。作为一种概率分布,即使参数改变,曲线的面积也等于 1。它的对称性是测试尾部极值的理想选择。
大数定律(LLN)与正态分布没有直接关系,但它相对于中心极限定理(CLT)来说很重要。LLN 指出,我们从分布中抽取的样本越多,它们的平均值就越有可能接近该分布的平均值。这具有直观的意义,因为更多的样本意味着更接近分布。LLN 适用于任何封闭分布,甚至高度非正态分布,因此该定律的假设是关于样本本身的。每个样本必须是随机的、同分布的和独立的。这有时被称为“i.i.d”。在数学意义上,这意味着每个样本来自相同的均值和标准差。值得注意的是,LLN 只控制随机误差,而不考虑系统误差或采样偏差。[2]CLT 指出,随着我们获取更多的 i.d .样本,它们在平均值周围的分布将收敛于正态分布。奇怪的是,LLN 不需要大量的样本;根据潜在的分布,它将只采集 15 到 30 个样本。
LLN 和 CLT 是调查分布均值和抽样误差形状的工具。就是这样。这两种工具都无法描述数据的形状或分布。两者都不会将您的数据转换为正常数据。两者对采样数据的要求都出奇的严格。总的来说,它们是统计学和数据科学中最常被误用的两种工具,我们将在两个案例研究中看到这一点。
案例研究:医院质量
场景:联邦医疗保险计划通过奖励那些平衡了患者安全和整体体验并降低了保险索赔的医院来提高医疗质量。负责管理医疗保险的美国卫生与公众服务部创建了一个基于价值的采购(VBP)总绩效评分(TPS ),以比较大约 3000 家医院每年的绩效。该分数有一个复杂的计算过程,包括许多不同的输入,如患者调查、保险索赔和再入院率。作为激励措施,政府扣留国家医疗保险医院报销的 2%,并根据每个医院的 TPS 重新分配该金额。得分较高的医院获得奖金,而得分较低的医院则受到惩罚。
VBP 系统激发了一位医院主管的研究想法。他提议研究医院质量和领导风格之间的可能联系。TPS 被用作质量的代理,医院质量经理使用 Likert 类型的工具进行调查。数据被下载,一份调查问卷通过电子邮件发送给了 2777 名质量经理。为不止一家医院工作的管理者只收到一份调查问卷。因为先前的 TPS 数据是由合格的专家分析的,并被认为是“以 37 分为中心的相当正态的分布,少数例外的医院得分超过 80 分”,[3]所以假定当前的数据是相似的。总共回收了 150 份完整的问卷,这位高管进行了多元线性回归,以寻找质量和领导风格之间的任何相关性。消除了几个异常值,使误差残差看起来更正常,并尝试了对领导数据的不同转换。然后,LLN 和 CLT 认为误差是正态分布的。结果令人失望,领导风格解释了不到 2%的分数变化。独立变量的相关 P 值非常不显著。哪里出了问题?你建议如何纠正它?
解决方法:我们从从属数据开始。最近的 VBP 数据看起来与前几年相似,TPS 的直方图和密度如下所示:


作者提供的图片
然而,这些数据是否如专家们所断言的那样是“相当正常的分布”呢?一个正常的 Q-Q 图表明它不是:

作者图片
分数在两个极端都偏离了参考线。如果不是正常的,那是什么?作为 R 中 MASS 库的一部分,descdist使单变量分布适合不同种类的数据。descdist使用不同的估计方法,如最大似然法和矩匹配法,生成偏度-峰度图,也称为卡伦和弗雷图。[4]创建总绩效得分的库伦和弗雷图( tps )的代码是:
library(MASS)
library(fitdistrplus)
descdist(tps, discrete = FALSE, boot = NULL, method = "unbiased", graph = TRUE, obs.col = "darkblue", obs.pch = 16, boot.col = "orange")
得到的图形是:

作者图片
TPS 的蓝点观察在理论伽马分布线上。似乎专家们错了——而且有着重大的影响。
回想一下,研究人员使用了多元线性回归。这种方法的第一个假设是因变量和自变量是线性相关的。这是直观的,因为如果它们不是线性相关的,测试就是错误的。事实上,具有伽马分布的因变量需要使用最大似然估计的广义线性模型(GLM),而不是普通的最小二乘法(OLS)。GLM 中的链接函数将针对伽玛分布中右侧尾部的极端观测值进行调整。
几个常见的错误会导致错误的设定。最初的问题,“这个数据看起来正常吗?”与“这个数据的形状是什么?”图表可以被主观地观察,引导研究者看到他们正在寻找的模式。[5]人们也很容易相信,如果采集的样本越多,那么分布看起来就越正常。TPS 是对所有医院的普查,因此是非概率的真实分布。无法采集更多的样本。误解数据本身的含义可能是另一个因素。分数并不代表质量,而是作为一种保险赔偿再分配方案。保险支付遵循伽玛分布,这就是评分系统反映伽玛分布的原因。单单研究和主题专业知识就可以取代大部分工作来确定数据是伽马分布的以及为什么是伽马分布的。
有趣的是,使用伽玛分布因变量运行 OLS 回归可能会产生与正确指定的 GLM 相差不远的估计系数结果。[6]这可能部分取决于分布的形状(其中“形状”是指定的参数之一)。然而,这位研究人员不幸地以两种方式改变了数据。通过剔除右边尾部的“异常值”,他们无意中将表现最好的医院排除在分析之外。换句话说,调查的范围缩小到非优秀医院的领导风格。研究人员还尝试对独立数据进行平方根变换,使其表现符合预期;这加剧了错误设定。这再次显示了对数据意义的误解,因为“领导能力的平方根”是无意义的。
该研究人员的方法还有其他问题。TPS 最终受到了每家医院数百名员工的影响。这代表了要调查的真实人口。研究人员选择了一种有目的的非概率抽样技术,仅用于挑选质量经理。回想一下,要使用 LLN 和 CLT,样本必须是随机选择的,并且独立同分布。抽样框架不再与真实人口相匹配,因此存在偏差。这也意味着误差项在线性回归中不是正态分布的。随着一些统计数据的出现,有可能声称质量经理是被研究的人群,但是由此产生的固有的非概率性偏差不能被测量或者被合并到模型中。此外,质量经理的选择不是独立的。因为一些经理为不止一家医院工作,他们的调查不能与单个 TPS 联系在一起,使他们不独立。这些因素中的每一个都促成了这个错误的研究项目。
结论:让数据引导你制定研究计划。从它的构成和形状,你可以推断出它的大概意思。不要仅仅为了满足您想要运行的特定统计测试的需要而转换数据。不要相信专家,而是要研究和验证他们的断言;这样,你就成为了数据和研究的专家。记得 LLN 和 CLT 有关于概率的假设;并非所有来自 30 个或更多样本的数据都与平均值和正态分布相关。
在我们的案例研究中,假设的最初想法可能是:“领导风格和医院质量之间存在相关性。”最终的研究结果更接近于:“领导风格的平方根和医院保险支付再分配方案(不包括优秀员工)之间存在错误的线性相关性,这是由误用于非概率数据的概率技术控制的,所有这些都受到未知和不可测量的统计偏差的影响。”最重要的是,要认识到每个假设和数据操作都会对结果的意义产生影响。
案例研究:税收和社会公正
场景:一位社会科学家想要研究边际税率,以检查与各种社会公正措施的相关性。理论上,他们希望找到一种关系,证明较高的最高边际税率意味着这些州对社会公正的更强承诺。社会公正数据将在以后挖掘,以适应边际税收数据。超出了这个一般的概念,研究的思想还没有成型。研究人员创建了一个向量,税,每个州和哥伦比亚特区的最高边际所得税税率。税收数据被证明是“有问题的”,因为它的分布很奇怪,社会科学家想知道如何使用正态分布对它进行统计测试。
解决方案:数据可能“有问题”,原因有很多。它可能无法访问或不完整。数据可能测量错误。数据框可能不代表人口。然而,正确测量和充分收集的人口信息讲述了自己独特的故事。分析数据的困难可能被证明是一个“问题”,但是数据本身可能是非常完美的。就税率而言,看一下直方图和密度图:


作者提供的图片
根据这些图表,数据肯定是不正常的,特别是因为严重的左尾巴。然而,卡伦和弗雷的图表讲述了一个不同的故事:

作者图片
卡伦和弗雷图表绘制的数据是正常的。这是因为我们的数据遵循两种不同的概率分布。很难看出这一点,但线索是直方图中的 9 个零观测值。最高边际税率为 0%表示一个州没有所得税,而所有其他值都意味着有税存在。这种非此即彼的分类就是二项式分布。在有所得税的州中,它们的最高边际税率遵循不同的分布。用这一行代码只保留有税收的州,
nozerotax <- tax[ tax != 0 ]
我们可以用图表显示的零税率数据:


作者图片。
其余 42 个税务管辖区的 Cullen 和 Frey 图显示了 beta 分布。这正是我们所期望的,因为百分比通常采用贝塔分布的形式,边际税率用百分比表示。
结论:研究人员选择了一个单一变量 tax ,它遵循两种不同的分布。这使得 LLN 和 CLT 不适用,因为违反了身份识别假设。推而广之,任何依赖于正态分布的统计检验都不能使用。税变量不适合研究者的目标和期望的统计方法。
走向
数据科学家做出的每一个方法和统计决策都会影响他们的研究结果。因为大学里教授的许多统计测试都依赖于正态分布,所以扭曲我们的数据或忽略关键假设来运行我们熟知的测试可能很有诱惑力。这有点像一句老话,如果你唯一的工具是一把锤子,那么一切看起来都像钉子。与其问数据是否正常,不如先问:它是如何分布的?这告诉了我什么?你可能会发现远远超出你预期的东西。
参考资料和数据来源
[1]美国商务部,NIST/SEMATECH 统计方法电子手册(2012 年),https://www . ITL . NIST . gov/div 898/Handbook/PMC/section 5/PMC 51 . htm
[2] J. Orloff 和 J. Bloom《中心极限定理和大数定律》,《概率统计导论》,(2014),麻省理工学院开放课件,https://OCW . MIT . edu/courses/mathematics/18-05-概率统计导论-spring-2014/readings/MIT 18 _ 05s 14 _ reading 6b . pdf
[3] E. Klein 和 P. Shoemaker,“基于价值的采购:质量评分和激励支付的预览”,《医疗保健财务管理》,(2012),https://www . ahd . com/news/HFM _ FeatureStory _ ValueBasedPurchasing _ 2012 _ January . pdf
[4] M. Delignette-Muller 和 C. Dutang,“fitdistrplus:拟合分布的 R 包”,《统计软件杂志》,(2015),https://www.jstatsoft.org/article/view/v064i04
[5] N .托顿和 p .怀特,“无处不在的神话般的正态分布”,《研究与创新》,(2011 年),https://www . researchgate . net/publication/322387030 _ The _ Ubiquitous _ mythic _ Normal _ Distribution
[6] P. Johnson,“具有伽马分布因变量的 GLM”,科罗拉多大学博尔德分校,(2014 年),https://pj . free faculty . org/guides/stat/Regression-GLM/伽马/伽马-01.pdf
数据来源:总绩效得分数据在公共领域,可从医疗保险&医疗补助服务中心获得。所得税数据来自以下国家税务机关: AL 、 AZ 、 AR 、 CA 、 CO 、 CT 、 DE 、 DC 、 GA 、 HI 、 IL 、 IN 、 https://forms.in.gov/Download.aspx?id=15122 MD , MA , MI , MN , MS , MO , MT , NE , NJ , NM , NY , NC , ND
您的数据网格不能只为开发人员提供自助服务
原文:https://towardsdatascience.com/your-data-mesh-cant-be-self-serve-just-for-developers-34bdeddc257e
一个集中提供的平台必须支持编码人员和分析人员

图片来源: Unsplash
我遇到的许多数据组织的领导者都在考虑数据网格。这种方法在大型企业中越来越受欢迎,包括将数据和分析的控制权分散到各个业务团队或领域。一些功能仍然是集中管理的,包括数据网格社区所说的自助式数据平台。
我欣赏数据网格背后的动机,并认为它对一些组织来说是理想的。然而,我强烈地感觉到,如果组织过度转向去中心化,他们会冒着让领域团队得不到支持的风险,并最终形成一个功能失调的数据网格。在本文中,我将概述数据网格以及是什么导致组织考虑数据网格,并且我将论证数据网格的自助服务平台需要更加雄心勃勃。
数据网格去中心化:我们如何做到这一点?
我们应该感谢 Thoughtworks 的 Zhamak Dehghani 对数据网格的讨论。她 2019 年的演讲“湖外”阐述了这些想法,最近她将细节扩展到了长篇数据网格书。如果您正在使用数据网格,我强烈建议您阅读一下。
数据网格的第一个原则是分散的域所有权。根据 Dehgani 的说法:
数据网格的核心是将数据责任分散和分配给最接近数据的人。
为什么数据网格的分散化在数据领导者中引起了如此强烈的共鸣?尽管技术进步实现了无限的数据集中化,但大多数组织尚未将其付诸实施,以提供及时、有效的数据价值。
从历史上看,从原始数据到分析价值的路径要经过一个由 ETL 和数据仓库专家组成的中央团队。工具和专家都很昂贵,所以只有最大的组织才能进行组织范围的分析。那些采用了数据仓库的公司仍然必须明智地决定包括哪些数据,这样他们才能控制成本。
数据湖和云数据仓库极大地降低了处理和存储成本,现在可以聚合几乎无限量的数据。这与各种类型的数据量爆炸式增长相吻合,这个大数据时代让我们离向所有潜在用户提供组织的所有数据的承诺更近了一步。
然而,剩下的最大障碍是让这些数据实际可用,而不是以模糊的数据沼泽结束。中央团队可以将数据存储库扩展到 Pb 级,但是更难扩展的是将必要的上下文和专业知识与数据结合起来。这种知识共享一直是业务领域和中央数据团队之间不可或缺的伙伴关系。对于大多数组织来说,共享并没有跟上数据增长的步伐。
毫不奇怪,这些领域在去中心化的方法中看到了潜在的答案。Dehghani 描述了一个数据管理拐点。集中化对更大、更复杂的组织需求产生了越来越大的影响,但也只是在一定程度上。

作者图片
超过一定程度的复杂性、数据用例的多样性和无处不在的来源,组织继续集中数据及其控制将变得毫无意义。
去中心化让中央数据团队何去何从?
对于数据世界来说,关于集中和分散什么的决策既不是新的也不是独特的。每个集中化选择通常都有权衡,很少有一刀切的方法。
Dheghani 强调了将钟摆转向分权的两个直接风险:不兼容和低效。如果域开发独立的平台,我们将回到数据孤岛,并阻碍必要的跨域数据产品和分析。每个领域构建自己的数据环境和团队既昂贵又低效,牺牲了组织规模的优势。
Dehghani 在她剩下的原则中提到了这些风险。将数据视为产品可确保领域为更大的组织提供分析就绪的数据。联合治理平衡了域的独立性和网格的互操作性,详细说明了适用于所有域的策略。
自助式数据平台的原则也涉及到这两种风险,在中央平台中增加了一些效率,同时也确保了域之间的兼容性和集成。中央团队必须为一致性和互操作性的基线提供这个平台。
他们什么意思,自助?
中央数据平台旨在提供基本的基础设施,以便领域团队不会试图构建或购买每一个工具和功能来生成分析见解。Dehghani 鼓励那些提供自助式数据平台的人为“多面手多数”构建平台。
如今,许多组织都在努力寻找数据工程师等数据专家,同时也有大量渴望与数据打交道的通才开发人员。大数据技术的碎片化、围墙化和高度专业化的世界创造了高度专业化的数据技术专家的同样孤岛化的碎片。
Dehghani 正确地识别了数据工程师的市场短缺和在领域团队中为他们配备人员的不切实际的目标。相反,领域团队应该依靠“多面手开发人员”来消费集中式基础设施并在其上构建。
我相信一个需要领域开发者和编码的平台仍然是一个不必要的高成功壁垒。为了让数据网格取得成功,中央团队需要提高自助服务的标准,为域提供一条从源到分析的简单的端到端路径。
自助不能意味着 DIY:它必须更“服务”而不是“自己”
自助服务向不同的人传达不同的东西。对一些人来说,这是一顿现成的午餐,对另一些人来说,这是一个自己动手的宜家式组装项目。有些领域是高度技术性的,并着手构建匹配或超过中央团队可能构建的能力的数据基础设施。
但是对于一个组织来说,要想从数据网格中获得价值,它需要的不仅仅是几个雄心勃勃的领域。如果只有最了解数据的领域实现了飞跃,那么组织并没有超越集中化,他们只是移动了控制,或许还有人员。
因此,中心团队必须交付一个服务和基础设施,在这个基础设施中,不需要开发人员,领域就可以快速生产。下面我将详细介绍这种平台的基本要素,从底层计算扩展到源和目标的集成和转换,再到持续监控和管理。有了这些,这些领域可以专注于数据和分析,快速超越设置和管道管理。
托管计算基础设施
首先,中央团队应该从域中抽象出核心计算和存储基础架构的细节。应该集中配置框架和灵活调整计算资源。从数据管道的角度来看,域应该提供其延迟需求的参数,但在其他方面,希望平台能够无缝地适应不断变化的数据量。
现成的数据源接收
每个域都有自己的数据源,包括文件、数据库、SaaS 应用程序等等。中央团队必须提供一个平台,在不需要任何代码的情况下将数据从这些数据库中提取到分析基础架构中。即使对于唯一的、专有的数据源,也不应该让域自己编写这些集成的代码。
无代码转换
在将数据从操作层面转移到分析层面的过程中,提供转换功能至关重要。领域团队将拥有不同的技能,数据转换选项应该反映这一点。ETL 专家的集中团队一直在努力跟上业务领域的需求。答案不是分散地转移复杂性和专业编码。相反,域应该有一个用户友好的接口来连接数据源(提取)和分析目的地(加载)。无代码方法还应该包括转换,使用数据管理器来帮助分析师可视化地准备数据以供分析使用。工具也应该服务于用户能够用 SQL、Python、Scala 和其他语言编码的领域,但是这些技能不应该是必需的。
可靠的管道管理
尽管数据网格将许多控制推到域外,但中央团队仍必须确保数据管道的正常运行时间和性能。如果没有这样的支持,域将陷入检查管道结果、对模式更改做出反应和无休止地排除故障的困境。中央平台应尽可能包括自动问题检测、警报和解决。当需要领域输入时,中心团队应该有效地促进引导式解决方案。
简单而彻底的可配置性
虽然中央团队需要优先考虑易于使用、自动化优先的方法,但它也需要捕捉领域输入并相应地进行调整。用户必须能够为调度、依赖性管理、容错阈值等配置他们的管道。该平台应该具有集成特定领域流程(如数据质量)的灵活性。
有了这个真正的自助式数据平台,域可以轻松地选择它们的数据源,选择调度选项,可视化地定义必要的转换,并知道管道将在那里工作。在几个小时之内,他们应该超越数据移动,开始分析或发布他们的数据产品。如果中央团队能够提供这个平台,他们就可以为数据驱动的组织提供有效的数据网格。
自助式数据平台是中央团队+领域合作伙伴关系的关键
在削减工程负担的同时,为数据网格中的每个独特领域提供服务的能力是一个具有挑战性的平衡。有了真正的自助式数据平台,域可以轻松地选择它们的数据源,选择调度选项,可视化地定义必要的转换,并知道管道将在那里工作。在几个小时之内,他们应该超越数据移动,开始分析或发布他们的数据产品。如果中央团队能够提供这个平台,他们就可以为数据驱动的组织提供有效的数据网格。
数据网格应该加速数据从其来源到那些能够从中产生见解和价值的人的路径。我见过一些组织能够很好地整合数据。当中央团队和平台可以促进领域提供他们的专业知识时——而不是一个团队试图做所有的事情——这是一个令人鼓舞的组合!
你的数据集不平衡?什么都不做!
原文:https://towardsdatascience.com/your-dataset-is-imbalanced-do-nothing-abf6a0049813
阶级不平衡不是问题。揭穿了一个最普遍的误解在 ML 社区。

[图片由作者提供]
我经常从数据科学家那里听到的一句话是:
"我们有一个问题:这个数据集是不平衡的."
讲了这些之后,他们通常会开始提出一些平衡技术来“修复”数据集,比如欠采样、过采样、SMOTE 等等。
我认为这是机器学习社区中最普遍的误解之一。所以,我想明确声明:
阶层失衡不是问题!
在这篇文章中,我将向你解释为什么。
此外,借助一个简单的例子,我将让您直观地了解为什么在大多数情况下,用欠采样、过采样或 SMOTE 来平衡数据集会适得其反。
我希望这能帮助你避免做不必要的(或有害的)工作,而把注意力集中在重要的问题上。
重要的事情先来。什么是阶层失衡?
机器学习中最常见的任务之一是二进制分类。在这些任务中,目标是建立一个可以预测某个现象是否会发生的机器学习模型。现象可能是任何东西:客户是否会流失,是否会发生地震,用户是否会喜欢一部电影。
由于问题是二元的,我们的目标向量只由 1(也称为正)和 0(也称为负)组成。
当积极因素比消极因素少得多时,就会出现阶层失衡。阳性占总数的百分比也称为患病率。即使没有硬阈值,当患病率≤ 10%时,我们也会同意考虑数据集不平衡。
在实际应用中,类不平衡是最常见的情况。的确,很多值得解决的问题本来就不平衡。这是因为资源有限。由于您希望将资源投入到少数可能有积极结果的案例中,因此能够预测罕见事件(如客户呕吐、地震或用户喜欢电影)的模型是非常有价值的。
问题出在哪里?
那么,阶层失衡的问题出在哪里?
如果你谷歌一下“类不平衡机器学习”,你会发现一些类似这样的文章:
- "对抗你的机器学习数据集中不平衡类的 8 个策略"。
- 机器学习中处理不平衡类的 10 个技巧。
- 机器学习中处理不平衡数据的 5 种技术。
从这些标题中,你可以看出阶级不平衡是一个主要问题。但真的是这样吗?
我查阅了各种资源,发现了一篇有趣的文章,标题是“代价敏感学习和类不平衡问题”,是“机器学习百科”的一部分。
在文章中,作者提到了在处理不平衡数据集时可能会遇到的 3 个问题:
- 当你实际上想要以不同的方式衡量误差时,你使用准确度作为度量标准。
- 你假设训练数据集和服务数据集的类别分布是相同的,而不是。
- 你没有足够的正面实例来训练一个可靠的 ML 模型。
奇怪的是:
在所有这些情况下,问题都是阶级不平衡。
事实上,让我们分别来看它们:
- 问题是你选择了与你的目的有关的错误指标:这与不平衡的数据无关。关键是您应该选择一个适合您的数据集的指标,而不是相反。
- 问题是训练/发球偏斜:与不平衡数据无关。事实上,如果您有一个流行率为 50%的训练数据集,您的数据是完全平衡的,但是如果您的服务数据流行率为 10%,您的模型无论如何都会受到训练/服务偏斜的影响。
- 问题在于数据稀缺:这与不平衡的数据无关。事实上,如果你有 5 个阳性和 5 个阴性,你的数据集是完全平衡的,但你没有足够的数据来建立一个 ML 模型。相反,如果你有 100 万个阳性和 10 亿个阴性,你的数据集严重不平衡,但你有足够多的数据。
无论是否存在阶层失衡,这 3 个问题都可能存在。
事实上,即使您的数据集完全平衡,您也应该经常问自己是否选择了正确的指标,是否可能存在训练/服务偏差,或者是否没有足够的数据来训练模型。
秀,不要说
在上一段,我们已经看到了为什么阶级不平衡不是问题的理论解释。但是您可能仍然认为使用欠采样、过采样或其他技术来平衡数据集不会有任何害处,对吗?
不对。
让我们借助一个玩具例子来看看为什么。
我们有一个 100,000 个观察值的训练数据集。数据集只有一个连续的要素。目标变量是二元的,有 10%的正变量。因此,数据集是不平衡的。
你的同事建议你对少数民族阶层进行过度采样,以“对抗阶层失衡”。所以你随机抽取正面的例子,直到你达到完美的平衡:50%的负面和 50%的正面。
此外,假设我们可以观察由 10,000 个观察值组成的服务数据集(即,您的模型在推理时将遇到的数据),其中 12%是正面的。
我们可以通过下面的代码模拟这种情况:
**# generating toy datasets**import numpy as npnegatives_train = np.random.normal(5,1,90_000)
positives_train = np.random.normal(8,1,10_000)
x_train = np.concatenate([negatives_train, positives_train])
y_train = np.array([0] * len(negatives_train) + [1] * len(positives_train))positives_train_os = np.random.choice(positives_train, size=len(negatives_train))
x_train_os = np.concatenate([negatives_train, positives_train_os])
y_train_os = np.array([0] * len(negatives_train) + [1] * len(positives_train_os))negatives_serve = np.random.normal(5,1,8_800)
positives_serve = np.random.normal(8,1,1_200)
y_serve = np.array([0] * len(negatives_serve) + [1] * len(positives_serve))
x_serve = np.concatenate([negatives_serve, positives_serve])
这是数据的图形表示:

训练数据集、过采样训练数据集和服务数据集中的负分布和正分布。[图片由作者提供]
我们希望建立一个机器学习模型,帮助我们根据 x 的值预测 y 的值。我们将使用一个只有一个参数的模型:决策阈值。该模型的工作原理如下:
- 如果特征≥阈值,那么我们的预测是 1。
- 如果特征< threshold, then our prediction is 0.
During training, what binary classifiers do is try to reach the parameters that minimize the loss. In our case, we have just one parameter, so training the classifier will consist in trying many possible thresholds and picking the one that gives the smallest loss. The default loss used in binary classification is log-loss, so we will stick to that.
**# "training" our model on the original dataset**from sklearn.metrics import log_lossts = np.linspace(np.min(x_train), np.max(x_train), 100)log_loss_train = [log_loss(y_train, x_train >= t) for t in ts]best_thres_train = ts[np.argmin(log_loss_train)]
Let’s plot the log-loss as a function of the threshold:

Training dataset. Log-loss corresponding to any possible threshold. [Image by Author]
Using the original training data, we have that the best threshold (i.e. the one that produces the smallest log-loss) equals 7.29.
But what happens if we use the oversampled (i.e. balanced) dataset?
**# "training" our model on the original dataset**log_loss_train_os = [log_loss(y_train_os, x_train_os >= t) for t in ts]best_thres_train_os = ts[np.argmin(log_loss_train_os)]

Oversampled training dataset. Log-loss corresponding to any possible threshold. [Image by Author]
The log-loss curve is very different now, it is practically symmetrical, leading to choosing a threshold of 6.47.
The final aim of any model is to perform best on unseen data. So, let’s see the log-loss curve also on serving data:

Comparing log-loss curves on the different datasets. [Image by Author]
As it was easy to imagine, the log-loss curve of the serving data is much more similar to the one of original data than to the one of oversampled data. Indeed:
- with threshold = 7.29 (best log-loss on 原始数据,服务数据的对数损耗为 1.28 。
- 阈值= 6.47(对过采样数据的最佳对数损失),对服务数据的对数损失为 2.30 。
换句话说,如果您在过采样数据集上训练模型,那么与在原始(不平衡)数据集上训练相比,您在服务数据上获得的性能会更差。
这是一个简单的解释,说明了为什么只有在您确切知道自己在做什么的情况下,才应该对数据集进行过采样/欠采样。不存在自动平衡不平衡数据集的首选方法。
综上
综上所述,阶级失衡本身从来都不是问题。
有时,它与不同的问题有关,如错误的指标、培训/服务偏差或数据匮乏。在这种情况下,你应该找到解决问题的方法,而不是解决阶级不平衡的问题。
你的朋友(可能)比你的朋友多。
原文:https://towardsdatascience.com/your-friends-probably-have-more-friends-than-you-b44ffcb5280
这个悖论背后的数学可以帮助我们预测下一个疫情。

我们中的大多数人都认为自己对数字有很好的感觉,在日常生活中,这种感觉往往不会受到质疑。但有时,我们会遇到似乎违背逻辑的陈述。由于数千年的进化,我们的大脑采取捷径来处理我们遇到的刺激。
像蒙蒂·霍尔问题这样的脑筋急转弯利用了这个事实来表明世界并不总是像它看起来那样。我们觉得自己总是对的,直到我们错了:
你的朋友(可能)比你有更多的朋友。
这个难以下咽的药丸,是与营养背道而驰的数学真理。
外行的解释
通俗地说,这种现象背后的原因如下:
假设爱丽丝是个交际花,几乎和每个人都交朋友,而鲍勃只有几个朋友。
如果你遇到爱丽丝和鲍勃,你和爱丽丝成为朋友的可能性比和鲍勃成为朋友的可能性大得多。一般来说,你更有可能和有很多朋友的人成为朋友。由此可见,你的普通朋友会比你有更多的朋友。
和很多事情一样,这不是一般人想的那样。根据 NYU 的一项研究,大多数人认为他们的朋友比他们的朋友多。
数学直觉
尽管这在直觉上是有道理的,但背后的数学非常丰富,甚至可以用来比通常的方法更早地检测疾病的传播。不喜欢数学就跳到下一节:)
我们的目标是计算,对于一般人来说,他们的朋友有多少个。这最好被形象化为一个图,其中一个顶点代表一个人,一条边代表一段友谊。所有的友谊都是双向的——例如,如果爱丽丝是鲍勃的朋友,那么鲍勃也必须是爱丽丝的朋友。

一个样本图,其中每个顶点代表一个人,每条边代表一段友谊。
我们的目标是平均以下各项:
给定一个人的朋友,那个人有多少个朋友。
这可以建模为随机选择一个人,随机选择那个人的一个朋友,获得那个朋友拥有的朋友数量的期望值。这是一个难题。我们可以通过取一个随机的友谊(边)并在友谊的两端随机选择两个朋友(顶点)中的一个来简化它。对于给定顶点被选择的概率,参见等式 1。

d(v)是顶点 v 的度数。|E|是图中的边数。p(v)是选择随机朋友时,我们选择顶点 v 的概率。
现在,我们可以对所选顶点的朋友数量进行平均。我们通过遍历每个顶点,取其被选中的可能性 p(v)与该人拥有的朋友数量 d(v)的乘积,得出等式 2。
为了继续对这个系统建模,我们将不得不从我们的统计工具箱中做出一些假设。让我们假设一个人的朋友数量正态分布,有一个确定的方差和平均值。在统计学中,方差表示随机变量和总体均值之间的期望差的平方。等式 3 展示了这一事实的有趣特性。

设均值= mu,方差= sigma,N = |E|。前半部分显示方差是随机变量和总体均值之间的期望差的平方。后半部分仅仅是这种说法的简化。
利用等式 3,我们可以简化等式 2。我们发现,平均而言,给定朋友的朋友数量如下。

这是什么意思?嗯,这意味着朋友的朋友的平均数量几乎总是大于朋友的平均数量。简单来说:你的朋友(可能)比你的朋友多。这背后的数学原理超越了朋友的范畴,概括了许多现象,包括为什么你的伴侣可能比你拥有更多的伴侣,以及普通大学生认为班级的平均人数比实际人数多。
超越平均水平
我应该指出,这并不是全部的故事。我们不是从正态分布中选择朋友的机器人。相反,许多细微之处影响着我们与谁交朋友。
作为一名大学生,很明显,受欢迎的学生通常是受欢迎学生的朋友,而不受欢迎的学生通常是不受欢迎学生的朋友。
这改变了我们系统的动力。我们不能再假设我们朋友群体的规模是正态分布的。如果朋友很少的人是朋友很少的人的朋友(反之亦然),那么我们最初的假设,即我们的朋友和我们有同样数量的朋友,实际上可能是准确的。
超越概率:及早发现疾病传播
友谊悖论可能不仅仅是一个让你感到不自在的脑筋急转弯。在社区中,一些人比其他人联系更紧密;这些关系良好的人与更多的人有互动,因为他们与普通人的分离度更小。由此可见,这些核心个体通常是传染病的第一携带者。
一些人建议监控这些中心个体,以早期发现疾病的传播。然而,这需要分析社区中的每一个社交圈,以便找到几个核心个体。这实际上是不可能的。
相反,我们可以随机选择社区中某个人的朋友。在大量人群中,这位朋友可能比一般人更受欢迎,人脉也更广。从理论上讲,这个人可能是疾病的早期携带者。
两位教授,克里斯塔基斯(哈佛)和福勒(加州大学圣地亚哥分校政治学)就是这么做的。他们对随机抽取的哈佛学生的朋友进行流感监测。他们随机选择了一名哈佛学生。然后,他们要求他们提名一个朋友也受到监控。他们的成果非常显著。
他们发现“”疫情在朋友组的进展比随机选择的组提前了 14.7 天。“科学家们能够比传统技术提前几周检测到流感传播的开始。
虽然这项研究是在 2009 年完成的,的论文在过去的一年里被引用了 466 次,引用数十次。目前还不清楚这种看似矛盾的统计学怪癖是否在最近得到了应用——但对 COVID 变体传播的应用是显而易见的。
感谢阅读!我希望你能从中学到一些东西。你可能会喜欢我的其他一些文章。
进一步阅读
来源
是什么让你觉得自己如此受欢迎?自我评价维护和主观方面的“友谊悖论”
[2] 利用友谊悖论对社交网络进行采样
数学家们说,“友谊悖论”并不总能解释真正的友谊
你的主观概率
原文:https://towardsdatascience.com/your-subjective-probability-8ff928ed853d
至少有 50%的时间你总是对的

在我的上一篇文章中,我简要概述了概率的不同风格。有些根源于现实,有些则更为主观。在此基础上,在本文中,我将探索哲学家们所说的主观概率。
正如我在上一篇文章中解释的那样, chance 可以被认为是“世界的一部分”也就是说,这与我们的个人观点和信仰无关。不管你愿不愿意,原子都会衰变。无论你是否持有这种观点,太阳总会升起。
这似乎并不适用于所有我们称之为“概率”的事物你可以自由地说你相信明天会下雨,并且非常坚信这种说法。当你听着当地气象员预报天气将持续完全干燥时,你甚至可以相信会下雨。“你就等着瞧吧”——你心里想。
下雨的可能性(概率“在世界上”)似乎在某种程度上与你对下雨概率的信念脱钩。
虽然在下雨的情况下,这可能看起来像是一个愚蠢的例子,但在考虑“加息对美国中西部失业率的影响”时,这一点要重要得多。或者“电晕疫苗接种在多大程度上导致接种疫苗的人死亡率过高。”要分析出“真正”的机会是什么,以及你认为什么是有效的证据,要困难得多。
不同的来源可能会突出不同的方面,你选择的读物可能会偏爱某些声音。现在很清楚,这种情况发生的实际“机会”可能与你认为的发生的信念大相径庭。
参见我上一篇文章中关于概率解释的简介:
信仰程度
不是所有的哲学家都同意这种主观概率不同于偶然性。一些人认为他们事实上能够将不同类型的概率结合起来(见刘易斯的主要原理)。然而,对于那些相信主观概率的人来说,他们的逻辑类似于我上面写的。对他们来说,机会和主观概率不是一回事。
有些词汇应该简单解释一下,这样其他的就更容易阅读了。哲学家喜欢说“代理人”(一个有自主权的人)或“命题”,所以让我们快速看看他们这么说是什么意思:
命题:断言某一事实的陈述。
这个可以很多(很多!)的事情。像“人类有头”这样的琐事,或者像“明天会下雨”这样的陈述。请注意,这些命题并不表达“可能性”,它们表达的是一个可以为真或为假的陈述。或者它可以是一个…
信仰程度:你有多强烈地认为某个命题是真的或假的(或者在一定程度上)。
信仰的程度有时也被称为信仰。你可以认为这种说法完全正确(毕竟“人类有头)或者完全错误(金星上没有水)。那么,为什么我们认为信仰是以“度”来表示的呢?
怀疑
按照逻辑,信任之所以有等级之分,是因为人们能够怀疑某些命题。这并不意味着他们不相信这个命题(如果是这样的话,他们甚至不会考虑它)。
让我试着换个说法:我们可以考虑火星上的生命。你可能会发现这极不可能,但你愿意考虑它。如果有足够的证据,你甚至可以改变你的信仰,非常强烈地相信火星上有生命。有趣的事实:火星是我们目前所知的唯一一个完全由机器人居住的星球!)
"[.。。我不需要相信[一匹马,在一场名为粉红德比的赛马中]获胜的客观概率很低,甚至相对于我所知道的,获胜的证据概率也很低。我只需要,我想不出任何理由,相信他会赢。”Mellor (2005 年,第 66 页)
这种“信任度”被表达(或者应该被表达)为一种概率,这并不是一个已知的事实。这需要另一次思想飞跃。现在,让我们假设它确实如此。
所以,因为我们可以怀疑命题,我们必须接受它们是程度。有些比其他的可能性更大,我们认为有些比其他的更真实(或虚假),而不是完全真实或虚假(这就是我们所说的完全相信)。
主观概率约束
那么,现在我们已经确立了个人可以认为某些命题是真的、假的或介于两者之间的任何一个……那又怎样?事实证明,我们可以用各种方法来模拟这种行为。事实上,跳过几个步骤(即,信任度如何用概率表示,这些如何满足基本的概率公理,以及信任的更新如何从根本上是贝叶斯),我们可以制作行为的复杂模型以及代理人应该如何行为。
这种规范性的转变(告诉某人应该如何做任何事情)不仅非常有哲理性,而且还有些用处。有一个规范的框架——或基准——允许我们称某些行为是非理性的。例如,当一个人根据新的证据改变他们的主观概率时。
**本文是我在鹿特丹伊拉兹马斯大学攻读博士学位时进行的一些文献研究的推广。它建立在大量研究的基础上,很难在这里引用,完整的文献综述可在:*【www.jeroenvanzeeland.com/research
使用 Python 中的语音识别功能将 Youtube 转换为文本
原文:https://towardsdatascience.com/youtube-to-text-with-speech-recognition-in-python-cd47d6d98b16
基于 Python 的库,用于为 NLP 用例下载 Youtube 视频

作者照片
最后更新时间:2022 年 4 月 5 日
为了构建一个会话式的语音到文本的 NLP 模型,我开始研究从 Youtube 平台检索音频数据的方法。目标是将音频和文本片段之间的数据配对作为输入数据源。

作者照片
有两个库可以从 Youtube 平台检索音频和文本数据集。
- 你-得到图书馆
- youtube2text 图书馆
你-得到库
一个简单的方法是使用 you-get 库。基于 python 的库可以很容易地用 pip 安装。
pip install you-get
基于 python 的库使用终端命令进行操作。音频文件可以在各种输出(. mp4,.而文本呈现在子剪辑字幕输出文件(.srt)。
安装后使用这个库很简单。在终端中使用以下命令获取有关下载哪些源代码的信息。
you-get -i https://www.youtube.com/watch?v=2M_kCCcNDts

作者照片
使用下面的命令继续下载。将 itag 值更改为具有首选输出类型(. mp4,.webm)。
you-get --itag=137 https://www.youtube.com/watch?v=2M_kCCcNDts
或者,在不指定标记值的情况下运行命令将下载列表中的默认值。
you-get https://www.youtube.com/watch?v=2M_kCCcNDts
字幕文件的格式如下。

作者照片
注意文本都是小写的,没有用句末标点符号分开。因此,必须执行后续处理工作的连续步骤,以获得音频和文本的工作对。
Youtube2text 库
Youtube2text library 旨在为音频<>文本配对获取合适的格式。在撰写本文时,该库支持三种功能。
- 检索 Youtube URL 作为音频和文本输出
- 下载音频文件格式的 Youtube 音频。wav,。flac)
- 将音频文件提取到文本输出
使用以下命令通过 pip 安装库。
pip install youtube2text
要检索作为音频和文本输出的 youtube URL,请在 python 环境中运行以下命令。
from youtube2text import Youtube2Text
converter = Youtube2Text()
converter.url2text("https://www.youtube.com/watch?v=Ad9Q8rM0Am0&t=114s")
该库目前支持 wav 或 flac 格式的音频和 csv 格式的文本,输出存储在指定路径或默认路径的子文件夹中(

作者照片。
下面列出了目录的布局。 Audio 文件夹包含整个音频文件,而 audio-chunks 文件夹存储与文本文件中的元数据相匹配的音频文件片段。文本文件夹中的 Csv 文件是从音频到文本的翻译输出。
<Own Path> or <HOME_DIRECTORY>/youtube2text
│
├── audio/
│ └── 2022Jan02_011802.wav
|
├── audio-chunks/
│ └── 2022Jan02_011802
│ ├── chunk1.wav
│ ├── chunk2.wav
│ └── chunk3.wav
│
└── text/
└── 2022Jan02_011802.csv
我的目标是准备音频和翻译文本来训练自定义自动语音识别(ASR)模型。任何使用大型语言预训练模型的尝试都需要以期望的频率(例如:16 kHz)对新添加的音频数据进行采样。选定的采样速率应该与现有 NLP 模型的训练速率相匹配。在这种情况下,音频数据检索过程允许切换采样速率是很重要的。Youtube2text 的设计考虑到了音频采样率的切换。
converter.url2audio(urlpath = "https://www.youtube.com/watch?v=rFYcvrKkr90", audiosamplingrate = 22000)
让我们看一下文本文件。文本文件包含两列:text 和 wav。并排提供映射到文本句子的音频块的元数据。

作者照片
文本文件输出的排列方式允许在需要时纠正任何句子。
由于使用现有的语音识别模型将音频翻译成文本,因此生成的文本高度依赖于音频输出的质量。音频可能并不总是以精确的方式表达。把事情放在上下文中,想象一下,一个没有背景噪音的演讲者的 Ted 演讲比一个有背景音乐的多人主持的网络研讨会更容易理解。
通过几次尝试,很明显文本需要人工检查和校正,以产生高质量的翻译文本数据。希望在这个过程中花费时间和精力来获得高质量的翻译文本。
虽然有时可能会很乏味,但这有助于确保为后续的 NLP 模型训练准备好良好的原始数据源。沿着文本列重放音频块使得更容易找到与音频输出相对应的部分配对。
说明文件的列对于 ASR 用例之类的任务很重要。在训练模型的后一步骤中,数据准备步骤从特定音频路径读入音频文件并预处理矢量嵌入。否则,如果不需要 wav 信息的元数据,则可以通过在单个步骤中删除列来容易地消除说明文件的列。

作者照片
查看 python 笔记本获取更多使用 youtube2text 库的例子。
感谢阅读。
资源
简单解释了 z 测试
原文:https://towardsdatascience.com/z-test-simply-explained-80b346e0e239
统计假设检验的 Z 检验的直观解释

克里斯·贾维斯在 Unsplash 上的照片
介绍
在本文中,我们将讨论和解释 Z 测试。这是一个统计假设测试,其中我们测量的统计分布,例如平均值,是正态分布的部分。
有多种类型的 Z 检验,然而在本文中,我们将考虑最简单和最广为人知的一种,即单样本均值检验。这用于确定样本的平均值和总体的平均值之间的差异是否具有统计显著性。
Z 测试的名称来自正态分布的 Z 得分。这是对原始分数或样本统计数据距离总体均值有多少标准差的度量。
z-检验是医学和数据科学等领域中最常见的统计检验,因此是理解的基本要素。
要求
- 样本量大于 30。这是因为我们想确保我们的样本均值来自正态分布。如中心极限定理所述,任何分布如果包含超过30 个数据点,都可以近似为正态分布。
- 标准差和意味着总体的是已知的。
- 样本数据是随机收集/获取的。
步伐
- 陈述零假设, H_0 。这就是你从总体上认为是真实的,这可能是总体的的意思, μ_0:

作者在 LaTeX 中生成的方程。
- 陈述候补假设, H_1 。这是你从你的样本中观察到的。如果样本均值与总体均值不同,那么我们说均值不等于

作者在 LaTeX 中生成的方程。
- 选择你的临界值, α ,它决定了你是接受还是拒绝零假设。通常,对于 Z 检验,我们将使用统计显著性 5% ,即正态分布中总体均值的 z = +/- 1.96 标准差:

作者用 Python 生成的图。
这个临界值基于置信区间,你可以在我之前的文章中读到:
- 使用样本平均值、 μ_1 、总体平均值、 μ_0 、样本中的数据点数、 n 和总体的标准差、【σ计算 Z 检验统计量:

作者在 LaTeX 中生成的方程。
- 如果检验统计量大于(或小于)临界值,那么替代假设为真,因为样本的均值从总体均值来看具有足够的统计显著性。
一种粗略的思考方式是,样本均值距离总体均值如此之远,以至于它必须是真实的(或者样本是一个完全的 anamoly)。
例子
让我们通过一个例子来充分理解单样本均值 Z 检验。
一所学校说它的学生比其他学校的学生平均聪明。它以 50 名 学生为样本,这些学生的平均智商测量值为 110 。人口,其余学校,平均智商 100 标准差 20 。学校对学生的说法正确吗?
无效假设和替代假设是:

作者在 LaTeX 中生成的方程。
这里我们说的是我们的样本,学校,有着比人口平均值更高的平均智商。
现在,这就是所谓的右侧 单尾检验因为我们的样本均值大于总体均值。因此,选择一个临界值±5%,它等于一个 Z 值 1.96 ,如果我们的 Z 检验统计量大于 1.96,我们只能拒绝零假设。
如果学校声称其学生的智商为 90,那么我们将使用如上图所示的左尾测试。如果我们的 Z 检验统计量小于-1.96,我们就拒绝零假设。
计算我们的 Z 检验统计量:

作者在 LaTeX 中生成的方程。
因此,我们拒绝零假设,学校的说法是正确的!
结论
希望你喜欢这篇关于 Z 测试的文章。在这篇文章中,我们只讨论了最简单的情况,单样本均值检验。然而,还有其他类型的测试,但它们都遵循相同的过程,只是有一些细微的差别。
和我联系!
- 要想在媒体上阅读无限的故事,请务必在这里注册! 💜
- 在我发布注册邮件通知时获取更新!T13😀
- 领英 👔
- 碎碎念 🖊
- github🖥
- https://www.kaggle.com/egorphysics🏅
(所有表情符号由 OpenMoji 设计——开源表情符号和图标项目。许可证: CC BY-SA 4.0
z 测试统计公式& Python 实现
原文:https://towardsdatascience.com/z-test-statistics-formula-python-implementation-3755d67ba0e7
公式、定义、示例和 Python 实现

1.目的
对大致符合正态分布的数据(即未偏斜的数据)进行称为 z 检验的统计检验。z 检验可用于检验两个样本甚至一个样本之间的比例差异,以评估假设。对于具有已知方差的总体,它确定两个广泛样本的平均值是否不同。根据我们对假设的选择,z 检验可以分为左尾、右尾和双尾假设检验。虽然 z-检验和 t-检验相对相似,但下一节将介绍几个差异。
1.1 定义 Z 检验
如果数据呈正态分布,并且您想检查两个总体的平均值是否不同,您可以应用 z 检验。为了验证上述说法,有必要制定零假设和替代假设,并计算 z 检验统计量的值。z 临界值是选择标准的基础。
2.范围
- 一种是对单个比例进行 z 检验,以检验涉及人口比例的假设。例如,我们记得 2010 年足球世界杯上的章鱼保罗。如果我们想测试保罗的成功率是否显著高于 50%(即保罗的预测不是偶然的),该怎么办?

图 1。举例说明一个比例 Z 检验的 Z 统计量。作者使用 Jupyter 笔记本中的乳胶显影的图像。从上面的例子中,𝜃表示保罗在世界杯 n 场不同比赛中的成功预测,而𝜃o 是假设的成功率,例如,50%。
- 使用 Z 检验法对两个群体具有相同比例的观点进行了比例差异检验。比如说,假设保罗有一个哥哥叫安吉尔,全世界都想知道安吉尔是否有和保罗一样的能力。因此,我们想对安吉尔进行另一个类似的实验,并检验安吉尔和保罗有同样能力的假设。

图 2。说明两个比例 Z 检验的 Z 统计量。图像由作者在 Jupyter 笔记本中使用 Latex 开发。从上面的例子中,𝜃x 象征着保罗在世界杯不同比赛中的成功预测,而𝜃y 象征着安吉尔成功预测了多少场比赛。
- 如果要针对特定的总体平均值检验假设,请使用单个平均值的 z 检验。例如,在医疗保健行业,如果我们想要评估一段时间内产品的销售额,并检查一年的平均销售额是否与公司设定的目标有显著差异(更显著),该怎么办?

图 3。说明了单样本 Z 检验的 Z 统计量。x 代表样本均值,而μ是假设的甚至是总体均值,σ是总体标准差,n 是样本大小。作者在 Jupyter 笔记本中使用 Latex 开发的图像。
- 当数据中每个样本的样本量为 30 或更大时,Z 检验可用于检验均值或方差的相等性,即检验两个总体均值相等的假设。例如,经常地,当在测试组和对照组上设计和运行活动时,我们希望了解测试组是否在特定结果变量上优于对照组。

图四。说明了双样本 Z 检验的 Z 统计量。其中 x1 和 x2 表示两个样本的均值(平均值),或者在这种情况下,表示测试组和对照组的表现均值,δ是两个群体均值之间的假设差异(如果我们测试相等的均值,则为 0,即两个群体的表现没有差异),σ1 和σ2 是两个群体标准差的统计注释,以及测试和对照组的 n 1 和 n 2 样本量。
3.假设
z 检验通常假设从中抽取样本的总体是正态分布的;然而,在样本量较大的情况下,人们可以忽略正态假设,尽管这样做是不可取的。理想情况下,对于涉及均值差异的检验,我们倾向于仅在样本量大于 30 时进行 z 检验。
4.t 检验与 Z 检验
z-检验比 t-检验更实用,t-检验对每个样本量有不同的临界值,而 z-检验对每个显著性水平有一个临界值(例如,5%双尾的临界值为 1.96)。因此,对于大样本量和已知的总体方差,许多统计检验可以很容易地作为近似 z 检验来进行。学生 t 检验可能更适合未知总体方差(必须使用样本本身进行近似计算)和样本量较小时。当样本量很小时,通常采用 t 检验;然而,当样本量很大或相当大时,在已知标准偏差的情况下,z 检验是优选的。
5.方法学
5.1 一个比例的 Z 检验
问题陈述
让我们考虑一下,公司 A 已经推出了一个新的网页布局,以支持在一个国家推出 RAT 测试套件。这是一个设计实验,随机向 30 名经常看流感产品的顾客展开。该网页是一个登录页面,目前包含购买不同 COVID 测试套件的链接。该倡议允许人们更多地了解 COVID 协议,并在访问测试中心之前获得必要的家庭测试支持。亚马逊希望检查新的布局是否可以将点击率提高 40%的特定幅度。
零假设(h₀):)𝐻𝑜=受访者样本比例(点击新登陆页面的用户)小于 40% 𝑃 < =0.40 (40%)
替代假设(H₁): 𝐻𝑎 =样本受访者比例(来自新登陆页面的用户点击量)大于 40% 𝑃 > 0.40 (40%)

图 1: 显示了单尾与双尾测试的剔除区域。假设我们的替代假设检查点击率大于 40%,我们将执行右尾测试。代替在这个用例中考虑μ,我们将检查𝜃 >是否是一个值。作者创造的形象。
5.1.2 计算
考虑到接触到新登录页面的 30 个客户中,有 17 个使用/点击了指向 RAT 测试套件的链接。使用下面列出的公式对一个比例进行 z 检验。

图 5。概述了比例的 Z 检验的计算。作者使用 Latex 和 Markdown (Jupyter Notebook)准备的图像。
5.1.3 结论
使用上面链接中的 Z 得分表,让我们确定获得 Z 得分< =1.88 的概率,如图 1 所示。
𝑃(𝑧≤1.88)=0.96995.但是我们想要计算 Z 右侧的概率(因为我们感兴趣的是获得落在拒绝区域或临界区域的概率值),即 1–0.96995 = 0.030051–0.96995 = 0.03005。
我们拒绝零假设,支持替代假设,即公司 A 通过更新登录页面,在 95%的置信度下,从 p<0.05 开始,设法将点击率提高了至少 40%。
Python 实现
#-------------import required library from statsmodel**from** statsmodels.stats.proportion **import** proportions_ztest#--------perform a one proportion z-test using the invoked function
#----count = number of successful click through rates (17)
#-----nobs = number of observation (customer exposed to the new landing page)#-----value: Hypothesized difference (in this atleast 40% click through rate)proportions_ztest(count**=17**, nobs**=30**, value**=**0.40)(1.8421903154590296, 0.03272365827054635)
请注意,由于四舍五入的原因,Z 统计值可能不同于实际操作示例。
5.2 均值差异的 Z 检验
问题陈述
让我们考虑一下,一家制药公司对一群医生开展了一项数字活动,以推广一种针对不同适应症的新上市药物。该团队为这项实验设计(推广活动)确定了一组医生,并跟踪了这些医生在活动前后的表现。医生的表现取决于开了这种新药的病人数量。我们想核实这场运动是否促使医生让更多的病人服用这种新药。
计算和方法


图 6。概述了均值差异的 Z 检验的计算。作者使用 Latex 和 Markdown (Jupyter Notebook)准备的图像。
Python 实现
from scipy import statstest_stat, p_value = stats.ttest_rel(data['New Patient Count'], df['Old Patient Count'], alternative = 'greater') #Performing a paired t-testprint('The p-value is', p_value)
6.结论
问题陈述主要驱动假设检验技术的选择。当进行基于试验控制的分析时,我们会看到一个连续的结果(例如,推广活动前后的患者人数),均值差异的 Z 检验允许我们验证差异。我们可以在影响前和影响后进行测量,甚至在测试组和控制组之间进行测量,以精确地确定计划对测试组的影响,以及该计划是否能在不受其他变量影响的情况下做出更好的响应。后 COVID 时代,公司正专注于理解数字渠道相对于其他目标渠道的影响。对于我们的结果变量是二元的情况,我们可以使用比例检验的 Z 检验来查看结果的差异。
关于作者:高级分析专家和管理顾问,帮助公司通过对组织数据的商业、技术和数学的组合找到各种问题的解决方案。一个数据科学爱好者,在这里分享、学习、贡献;你可以和我在 上联系 和 上推特;
Zenodopy——从 Python 管理 Zeno do 项目
原文:https://towardsdatascience.com/zenodopy-manage-zenodo-projects-from-python-aa7c36634c1d
用这个包创建项目和上传/下载文件

卡尔·安德森在 Unsplash 上的照片
Zenodo 是一个由 CERN 运营的通用开放存取知识库。研究人员可以存放任何与研究相关的数字制品,包括数据集、研究软件、报告等。
Zenodo 是目前最好的数据管理服务之一。团队相信公平原则,即数据应该是可访问的、可访问的、不可操作的、 R 可用的。此外,它们符合计划 S。****
该 web 应用程序简单、直观,非常适合存放个人电脑上的小型数据集。然而,上传大型远程数据集需要使用 Zenodo API ,我之前在本文的中讨论过。
Zenodopy是该 API 的包装器,通过提供高级功能来简化使用:
.create_project().upload_file().download_file().delete_file()
在这篇文章中,我将带你经历开始使用这个包所需要的所有步骤。请注意,他的包裹不属于芝诺多,也不被芝诺多认可。项目代码可通过 GitHub 获得
https://github.com/lgloege/zenodopy
先决条件
在我们开始上传数据之前,有几件事情需要核对。
创建一个帐户
如果您还没有帐户,请前往https://zenodo.org/并点击右上角的注册来创建一个帐户

作者图片
创建个人访问令牌
登录后,单击右上角的下拉菜单,导航至“应用程序”。从那里,在“个人访问令牌”部分点击“新令牌”

作者图片
下一个屏幕是您命名令牌和设置范围的地方。我更喜欢将范围设置为deposit:write,允许上传但不允许发布。

作者图片
点击“创建”后,您的密钥将会出现。暂时不要关闭这个窗口,我们将把这个密钥存储在一个点文件中。你只能看到你的令牌一次。
将密钥存储到点文件中
将以下命令复制并粘贴到您的终端中,用上一步中的密钥替换your_key 。
{ echo 'ACCESS_TOKEN: your_key' } > ~/.zenodo_token
这将在您的主目录中创建一个名为.zenodo_token的文件,其中包含您的访问令牌。确保您的令牌存储在 dot 文件中。
tail ~/.zenodo_token
这个点文件很重要。Python 包将在这个文件中查找您的令牌。格式也很重要,必须是ACCESS_TOKEN: your-token。您的~/.zenodo_token文件应该类似如下:

的例子。zenodo_token 文件。注意,这不是真实的令牌(图片由作者提供)
安装 python 包
通过 pip 安装软件包
pip install zenodopy
请注意,该包依赖于以下内容:
requests==2.26.0
types-requests==2.27.7
wget==3.2
确保您可以导入它
现在在终端中打开 Python,确保可以导入zenodopy
import zenodopy
如果你没有任何错误,那么让我们开始使用它!
创建新项目
以下步骤将引导您创建一个新项目并上传/下载数据,所有这些都在 Python 中完成。
1.创建客户端对象
我们首先创建一个Client()对象
import zenodopy
zeno = zenodopy.Client()
这个创建的对象通过存储在~/.zenodo_token中的ACCESS_TOKEN知道你的 Zenodo 账户。但是,它还没有指向您的任何项目。
1.1 测试 zenodopy 是否正在读取您的令牌
要确保正确读取您的令牌,请运行以下命令。如果一切正常,你的钥匙会被提示。
zeno._get_key()
1.2 找不到您的令牌?
如果zenodopy不能读取你的令牌,首先检查以确保你的~/.zenodo_token文件设置正确。
使用~/.zenodo_token的另一种方法是用访问令牌简单地启动Client() 。
import zenodopy
zeno = zenodopy.Client(ACCESS_TOKEN=your_token_goes_here)
这种方法的缺点是您的令牌现在暴露了。不太好。总是尝试使用到.zenodo_token文件,这样你的令牌就被隐藏了。
2.创建项目
params = {'title': 'project_title,
'upload_type': 'dataset',
'description': 'what this repository is about'
'creators': [{'name': 'Joe Low'
'affiliation': 'nasa'}]}zeno.create_project(*params)
最起码,您需要提供一个title。但是,这样做会将upload_type和description设置为默认值。您可以随时使用change_metadata()更改这些设置
对于创建者,传递一个以name和affiliation为关键字的字典列表。这是可选的,以后可以用change_metadata()更改
创建项目后,您的对象现在指向该项目,这意味着您开始向其上传数据。
您可以使用str()方法验证您当前指向的项目
str(zeno)
3.上传数据
如果你正在上传一个目录,确保首先压缩它。芝诺多只允许您上传文件。
使用.upload_file()方法,您提供想要上传的文件的路径。
zeno.upload_file(/path/to/file.zip)
如果文件上传成功,系统会提示您。
4.删除项目
我觉得最好是从 web 界面删除一个项目。说到这里,可以从zenodopy中删除一个项目。有意隐藏此功能,以最大限度地减少意外删除。您只需将项目 ID 提供给_delete_project()
zeno._delete_project('project_id')
使用这种方法时要小心。它没有内置自动防故障装置。项目一旦被删除,就无法恢复。
从现有项目开始
现在,让我们浏览一下扩充现有项目的步骤。
1.创建一个客户端()对象
使用 Zenodoapi 总是从创建一个Client()对象开始
import zenodopy
zeno = zenodopy.Client()
2.列出当前项目
我们首先需要列出我们所有的项目及其相关的项目 ID。
zeno.list_projects
如果您有与您的帐户相关的项目,它们将显示在此处。您的输出将如下所示:

作者图片
在我的账户里有四个项目。属性显示项目名称及其关联的 ID 号。
3.设置项目
set_project()将把你的对象连接到与所提供的 ID 相关联的项目。
zeno.set_project('5867022')
这将使您的对象指向项目 ID 5867022。注意此 ID 仅适用于您的帐户。你可以用str()来验证你指向的是哪个项目。
str(zeno)
4.列出文件
现在让我们列出与这个项目相关的所有文件
zeno.list_files
如果您的项目中有文件,您的输出将如下所示:

作者图片
5.上传文件
上传文件和以前一样。简单证明文件路径到.upload_file()
zeno.upload_file('/path/to/file.zip')
6.下载数据
现在让我们下载一个文件。目前,zenodopy只能下载与您的项目相关的文件。
使用.list_files显示项目中的所有文件,然后向.download_file()提供想要下载的文件的名称
zeno.download_file(filename)
未来的版本将能够通过提供 DOI 从任何公共项目下载文件。
最后的想法
ZenodoPy 的主要目的是简化 Zenodo 项目的大文件上传/下载,我希望它最终可以用于从 Python 全面管理 Zenodo。
团队比个人更擅长解决问题。因此,任何发现这个项目有用的人都被鼓励为这个项目做出贡献。
https://github.com/lgloege/zenodopy
如果你在代码中发现任何错误,请在 GitHub 上打开一个问题。
本文将作为一个动态文档,并在发生重大变化时进行更新
相关文章
[## 让你的科学与芝诺多公平
towardsdatascience.com](/make-your-science-fair-with-zenodo-b209fc74400c) https://blog.jupyter.org/binder-with-zenodo-af68ed6648a6 https://medium.com/@manishaagarwal_1980/zenodo-integration-a-care-for-users-privacy-as-well-as-their-experiences-ba2f5b35e5de
零能源成本——我们离零还有多远?
原文:https://towardsdatascience.com/zero-energy-cost-how-close-to-zero-can-we-get-b58a6c2a3ac7

降低样本家庭每种产品/服务的能源成本。图表中显示的节约只能通过所有产品和服务的组合来实现。电动汽车+代表电动汽车的智能充电(考虑股票交易价格)。家用电池+代表一个带能源优化软件的家用电池(作者编写)。
一项分析表明,在技术的帮助下,普通家庭可以降低多少能源成本
市场上有一系列的电力包、热泵、太阳能电池板、家用电池组和电动汽车。目前,这些资产的提供商在对这些资产的成本效益进行估计时,并没有考虑您的消费习惯或现有合同。此外,没有考虑资产的相互依赖性。例如,太阳能电池板的盈利能力取决于我们自己能消耗多少能源,而电池组的成本节约取决于太阳能电池板的存在和家庭用电量。此外,所有这些解决方案的价值取决于你的电力和网络合同以及房子的特点(屋顶面积、角度和方向、供暖面积等)。).
电器的盈利能力取决于家庭的消费模式、现有合同、房子的特点以及其他电器设备的存在。
在本文中,我将展示数据科学如何通过模拟所有可能变化的行为和成本,让我们找到能源产品和服务的最佳组合。
方法学
到目前为止,还没有解决方案允许我们以高粒度分析上述所有元素的组合效果。在 E-Lab(ene fit 的 R&D 分公司)中,我们创建了能源解决方案计算器,通过以下步骤解决了这个问题:

能源解决方案计算器的高级步骤(图片由作者提供)。
首先,正在创建客户档案,其中包括电力消耗模式和一些关于家庭的基本数据,如自由屋顶面积、加热面积、当前加热系统和汽车燃料消耗。
其次,能源求解器创建场景,每个场景代表一种服务和产品的组合。想象一下,你有 3 个不同的电力包,4 个太阳能解决方案,3 个不同的储能设备,5 个电动汽车充电器,6 个不同的热泵和 5 个不同的网络包适合特定的客户。这意味着可能的场景总数为 3 * 4 * 3 * 5 * 6 * 5 = 5400。
第三,能源解决方案计算器在长达一年的时间内每小时模拟每个场景中所有资产的行为。
最后,根据利益指标(成本节约、盈利能力或二氧化碳节约)对结果进行汇总和排名。
在未来,我将更详细地介绍能源解决方案的工作原理,但本文的目的是展示一个样本家庭的结果。
样本家庭的概况
本分析中使用的样本家庭具有以下特征:
- 电力消耗:每年 5000 千瓦时,每小时消耗模式符合爱沙尼亚家庭消费者的平均模式。
- 电包:兑换( 链接电包 )。
- 网络包:网络 5 ( 链接 Elektrilevi 包 )。
- 加热面积 : 120 平方米。
- 加热源:柴炉加热。
- 安装地热集热器的免费用地面积 : 600 m2。
- 太阳能解决方案:没有,但屋顶有 60 平方米的自由空间,40 度角,朝南。
- 汽车:汽油动力汽车,平均油耗 6.5 l/100 km,年行驶里程 1.2 万 km。
在分析时(2022 年 8 月),该家庭的年能源成本为 4,218€,包括木柴、汽车燃料和电费(使用爱沙尼亚过去 12 个月的电价)。
寻找最佳能源解决方案
在这个分析中,我的目标是找到最小化样本家庭年能源成本的能源解决方案。每小时总共模拟了 5400 种不同的产品和服务组合(场景),得出的最佳解决方案如下:
- 电包:交换(保持不变)。
- 网络(DSO)包:网络 4(原网络 5)。
- 热泵 : 9 kW 地源热泵(原木材加热)。
- 太阳能解决方案 : 7.5 kW(原本无)。
- 电池:华为 luna 2000–15-S0,带优化软件(原本无)。
- 电动汽车:对,带智能充电(原本是汽油动力车)。
借助该解决方案,样本家庭每年的能源成本可降低 90%以上。

降低样本家庭每种产品/服务的能源成本。图表中显示的节约只能通过所有产品和服务的组合来实现。电动汽车+代表电动汽车的智能充电(考虑股票交易价格)。家用电池+代表一个带能源优化软件的家用电池(作者编写)。
首先,能源解算员建议将样本家庭的网络包从“网络 5”切换到“网络 4”(这些网络包特定于爱沙尼亚市场),这将每年节省€6 英镑。
能源求解器的所有结果对于给定的家庭都是独特的,因为“最佳”解决方案和获得的节约取决于特定家庭的消费模式和其他输入参数。
其次,求解者建议用热泵代替木材加热,这样每年可以为家庭节约€498。
第三,计算器显示,该家庭可以添加一个容量约为 7.5 千瓦(基于自由屋顶面积)的太阳能电池板解决方案,这将每年节省额外的 1,556€。
第四,求解者建议将目前的汽油发动机汽车更换为电动汽车,这将每年减少 1179€的能源成本,前提是用户还购买了电动汽车充电器,从而可以根据交换价格安排充电时间。
最后,计算器显示,通过购买一个带有优化软件的 15 千瓦时家用电池,可以节省€582,从而使样本家庭的总成本降低约 90%。
值得注意的是,这些节省是上述所有设备协同作用的结果。如果我们排除其中一个设备,其他设备带来的节约也会改变。
能源解决方案将家庭作为一个整体进行评估,这意味着添加或移除一个设备会影响其他设备的节省。
最佳解决方案的电力消耗示例
17.2022 年 8 月是爱沙尼亚电力市场上不同寻常的一天,因为 18:00-19:00 之间的电价为 4000€/兆瓦时或 4€/千瓦时。让我们来看看有和没有最佳能源解决方案的示例家庭的电力消耗。
下图显示了电价(€/千瓦时)和家庭每小时的用电量。使用原始的消费配置文件,我们的示例家庭在一天中会有相对恒定的消费,而不管电价如何。

2022 年 8 月 17 日爱沙尼亚市场上样本家庭的电力消耗,无最佳解决方案(由作者编制)。
使用最佳解决方案(如下图),耗电量会有显著变化。从表中可以看出,电价较低的时段(如 10:00-11:00、14:00-16:00)用电量最高,电价较高的时段(08:00-09:00、18:00-19:00)家庭向电网送电。太阳能电池板、电动汽车的智能充电和家用电池的优化使用使这一切成为可能。

模拟样本家庭 2022 年 8 月 17 日在最优情景下的用电量(作者准备)。
如果我们看看最佳情况下每台设备的耗电量(下图),我们首先会注意到太阳能电池板产生的电力(表中的负值代表发电量)。我们还看到,电动汽车的智能充电可以确保电池在最实惠的时间充电。然而,由优化软件驱动的家用电池具有最复杂的行为模式。它监控股票交易价格、家庭环境的消耗以及太阳能的产生,并使用充电和放电时间表来最小化家庭成本。

最优情景下 2022 年 8 月 17 日样本家庭每台设备的模拟用电量(作者准备)。
总体而言,分析表明,现代电气设备和智能软件可以显著降低家庭的能源成本。但是,应该注意的是,节约能源成本并不等于盈利。盈利能力被有意排除在本分析的范围之外,因为它取决于用户购买设备的确切价格。


浙公网安备 33010602011771号