TowardsDataScience-2023-博客中文翻译-一-

TowardsDataScience 2023 博客中文翻译(一)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

通过项目驱动的面试招聘你下一个数据分析炽星

towardsdatascience.com/hire-your-next-analytics-superstar-with-a-project-based-interview-36086461ccc8?source=collection_archive---------22-----------------------#2023-01-06

基于项目的面试可成为招聘全能人才的有效策略。以下是如何在您的企业中实施这一策略。

Nikita GoldovskyTowards Data Science Nikita Goldovsky

·

关注 发表在 Towards Data Science ·6 min 阅读·2023 年 1 月 6 日

--

用 DALL-E 创作

雇佣一位数据分析师或科学家可能是一种充满焦虑的体验。您如何确保最终雇佣的人既具备技术方面的能力,又能解释业务并传达结果?

一个高度成功的策略是让面试者完成一项回家作业项目。

如果你还没有遇到这种招聘形式,家庭作业(或测试/作业)是一种通过模拟候选人在工作中会遇到的项目来评估其适应性的方式。这是一种在许多技术和非技术职位中广泛使用的招聘策略。

当谈到分析行业时,家庭作业通常涉及给候选人提供一个数据集,并列出他们必须使用数据回答的封闭式和/或开放式问题。这通常还包括候选人通过演示文稿展示他们的发现,并提交任何脚本/代码/文档。

在面试过程中给予候选人家庭作业作为评估的一部分有几个优点,例如:

  1. 让候选人以更真实和有意义的方式展示他们的技能。

  2. 筛选出那些没有表现出完成作业所需的毅力的候选人。

  3. 允许候选人在自己的节奏和环境中工作。这对于那些可能在时间压力或高压环境下表现不佳的候选人尤其有用。

  4. 让面试官看到候选人如何解决问题,他们的思考过程和对细节的关注。

  5. 让面试官评估候选人提供高质量工作的能力以及是否能按时完成任务。

家庭作业可以提供比仅仅面对面面试或技能测试更全面和准确的候选人技能和能力评估。在我的招聘经验中,家庭作业——特别是演示文稿——确实帮助我们区分了能够理解我们业务特定数据细微差别的候选人与那些可能有扎实基础但难以分辨噪音和信号的候选人。这确实帮助我们招聘到能够推动业务发展的全能人才。

虽然这是招聘合格数据分析师的有效方法,但创建这样的项目可能会耗时且评估标准不确定。

以下是如何做到这一点。

你希望评估任何潜在分析职位候选人的三个主要标准是:

  1. 他们工程建模数据集的能力。

  2. 他们分析解释数据的能力。

  3. 他们沟通发现的能力。

让我们更详细地了解如何构建家庭作业项目并评估提交内容。

1. 评估建模/工程

如何实施

提供 2-3 个独立的数据表供候选人操作和合并。在作业中包含多个表格将帮助候选人展示他们理解如何处理不同粒度和对象来讲述共同故事的能力。

在现实世界中,数据通常包含缺失、错误且缺乏文档。了解你发送的数据存在什么问题,并要求候选人包括他们对数据可能存在“偏差”的观察。我们大多数人需要能够应对的常见数据挑战包括:重复记录、缺失值、次优数据类型。

包括几个有客观正确/错误答案的问题。理想的候选人应能操作数据以回答基本的描述性问题,例如“过去 6 个月发生了多少销售”或“2022 年的平均停留时间是多少?”

让候选人提交他们的代码,以评估其数据建模技术的效率和鲁棒性。

评价标准

  1. (5 分) 效率:提供的代码是否构成了一个工程良好的数据模型,该模型高效且避免了不必要的操作?

  2. (5 分) 准确性:候选人是否准确地连接和聚合了数据?他们是否充分考虑并解释了任何存在的坏数据?

  3. (5 分) 可解释性:数据模型是否已标记和文档化?用户是否能在无需进一步解释的情况下理解数据管道的功能?

2. 评估分析和批判性思维技能

实施方法

确保包括一个候选人可以利用此数据集帮助解决的业务目标。例如,可以决定哪些营销活动有效,哪些客户群体最成问题,或询问可能导致过大损失或低效的原因。

在你的评估中,包含一些问题,以帮助你评估候选人在三种主要分析技术——描述性/探索性、因果性和预测性/推断性方面的推理能力。

  1. 包括一些问题,以评估候选人探索和描述数据的能力。这些问题可以包括“在数据中,你认为最有趣的数据点是什么?”或“你在数据中发现了什么令人惊讶的东西?”

  2. 包括一些问题,以帮助候选人展示他们连接因果关系的能力——例如“哪个活动/战术表现最佳”,或“哪个 A/B 变体最成功,为什么?”

  3. 根据职位的范围,你还可以包括一些问题,帮助你评估候选人通过高级分析进行预测性思考的能力。问题可以包括“在数据上开发和训练预测模型,并使用适当的指标评估其性能。”或“使用特征工程和/或参数调整来提高预测模型的性能。”

评价标准

(5 分) — 候选人是否以准确和有代表性的方式描述了数据?

(5 分) — 候选人是否以合理的方式连接了因果关系?

(5 分) — 候选人是否应用分析技术对数据做出合理的推断或预测?

3. 评估沟通技巧和叙事能力

如何实施

分析只有在别人能理解和使用的情况下才有用,因此数据分析师能够有效沟通他们的发现是很重要的。

要求候选人总结他们的分析并在一个由混合(技术和非技术)观众组成的后续面试中进行展示。观察分析师在向非技术观众解释其思路和模型的技术细节方面的能力。他们是否使用清晰简洁的语言,并能有效回答问题和解决疑虑?

准备一系列问题以深入了解候选人包括/排除展示内容的理由。示例问题包括:

  • “你为什么选择这个特定的可视化方式?”

  • “你是如何决定观察哪些变量的?”

  • “你如何判断预测模型是否适合当前问题?”

  • “如果有更多时间,你会做什么?”

评估标准

(5 分) — 演示是否清晰,候选人是否能够向非技术观众展示技术信息?

(5 分) — 候选人是否能回答关于潜在商业影响或在提供额外商业背景时的观众提问?

(5 分) — 候选人是否能够为展示中的方法提供合理的解释?

结论

希望这篇文章能为你在进行基于项目的面试时提供更多框架。

积分系统以及在项目中强调的技能集可以根据你的具体招聘需求进行调整。你可能需要根据是否寻求倾向于技术或业务方向的人才来调整积分分配,但根据我的经验,培养分析师拥有平衡的技能集会带来更有成就感的职业生涯和更好的成果。

一个常见的问题是给予候选人完成作业的时间长短?重要的是考虑到人们可能会很忙,并根据候选人的个人情况提供灵活性。然而,候选人能够设定日期并坚持完成是很重要的。一般来说,我认为一周应该是一个合理的作业返回时间。

如果你希望获取我在面试中使用的具体模板,请随时通过 Medium 或 LinkedIn 联系我。

祝招聘愉快。

进化多目标优化与耙选择

原文链接

一种用于良好分布的帕累托前沿近似的方法

Oliver Kramer Towards Data Science Oliver Kramer

·

Towards Data Science上发布的文章,作者:Oliver Kramer,阅读时间约 5 分钟,发布日期:2023 年 3 月 14 日。

--

点击这里

图像由 AI 生成。

多目标优化是一个关键的研究领域,在工程、金融和生物学等多个领域具有广泛的应用。它涉及同时优化多个目标,其中目标可能是相互冲突的,不可能在不妥协其他目标的情况下改善一个目标。近年来,许多新方法已经被开发出来,以解决多目标优化问题。在这篇博客文章中,我们介绍了一种名为“rake selection”的新方法,旨在有效解决多目标优化问题。

遗传算法(GA)非常适合多目标优化,因为它们基于解决方案的种群而非单一解决方案运行。这一特性使它们能够探索更大的搜索空间,并捕捉到可以覆盖广泛目标的多样化解决方案。

在典型的遗传算法(GA)中,生成一个潜在解决方案的种群,并根据多个目标评估其适应度。然后,选择最适合的个体进行繁殖,产生新一代解决方案,这些解决方案经过进一步的选择和繁殖,直到找到一个令人满意的解决方案。

在多目标优化中,种群中每个个体的适应度是同时基于多个目标进行评估的。这使得遗传算法能够捕捉到一组在种群中不被任何其他解决方案支配的解决方案,这被称为帕累托最优前沿。

图 1:在一个具有两个目标的最小化问题中,一个解决方案将目标空间划分为四个象限。

如果解空间中的其他任何解决方案都不支配某个解决方案,则该解决方案被称为帕累托最优。图 1 展示了单个解决方案如何将目标空间划分为四个象限。左下象限包含支配考虑中的解决方案的解决方案;所有这些象限中的解决方案在两个目标方面表现更好。右上象限中的解决方案被考虑中的解决方案支配。左上和右下象限包含与考虑中的解决方案不可比拟的解决方案。非支配解的集合包括相对于所有其他解决方案在这些象限中的解决方案。

非支配排序

大多数多目标优化方法,如 NSGA-II、撬棒选择和基于超体积指标的选择,都从非支配排序开始。这个过程涉及根据非支配等级对 GA 的种群进行排序。如果一个解没有被其他解支配,则被认为是非支配的。第一个等级包含所有非支配解,如果这些解被移除,第二个等级由未被剩余解支配的解组成。这个过程持续进行,直到所有解都被排序。图 2 说明了解如何将解排序到不同的等级中,粉色方块代表非支配解,橙色方块代表第二等级的解,灰色方块代表第三等级的解。第一等级的非支配解作为二次选择过程的基础。

图 2:非支配排序根据支配关系为每个解分配一个等级。

撬棒选择

维持目标空间中解的均匀分布的一个有效方法称为撬棒选择。它基于在撬棒基准上正交定位的撬棒,并延伸到非支配解所在的目标空间区域,见图 3。撬棒基准将最优解与单一目标相关联。对于每个撬棒,选择排名为一的解中距离最近的点。如果非支配解过少,还可能考虑来自下一个排名的解。无论目标空间的维度如何,点和线之间的距离都可以高效地计算。

图 3:离撬棒最近的解被选择进入下一代。

形成撬棒基准的最优点可以通过单独优化每个目标来计算,然后使用其余目标的适应度值计算目标空间中的附加坐标点。或者,撬棒基准可以通过使用当前种群中每个目标的最佳解来计算。这种即时计算的撬棒基准可能导致不同的位置。

撬棒方法的有效性在 Pareto 前沿具有线性形状时最为明显。当前沿变得更加弯曲时,撬棒在并行线切割中的效果会降低。然而,可以基于当前非支配解集的形状对撬棒方法进行调整。[2] 提出了类似的机制,通过计算比例来调整每一步的撬棒距离,基于撬棒与选择解之间的连接。这种方法允许在处理形状更复杂的 Pareto 前沿时具有更大的灵活性和有效性。

实验

图 4 展示了在多目标问题 ZDT1 至 ZDT4 上进行 Rake Selection 的实验结果。耙基连接角点,垂线定义了目标空间中的耙。需要注意的是,由于坐标轴的比例不均,这些耙在图中可能不会显得垂直于耙基。

图 4:ZDT 1–4 的耙选择实验(来自[2])。

在 1,000 次迭代后,相当于 50,000 次目标函数评估,解位于耙上和帕累托前沿的曲线上。这表明 Rake Selection 方法在识别非支配解和实现多个目标之间最佳权衡方面的有效性。

结论

总结而言,多目标优化是一个关键的研究领域,具有广泛的应用。遗传算法(GAs)是多目标优化的有效工具,因为它们基于解的种群运作,并能够探索多样的解集,以捕捉多个目标之间的权衡。非支配排序和耙选择是可以有效识别帕累托最优前沿和选择最佳权衡解的技术。实验结果表明,Rake Selection 方法在识别非支配解和实现多个目标之间的最佳权衡方面是有效的。总体而言,多目标优化是一种强大的方法,能够提供对复杂系统的宝贵见解并指导决策过程。

除非另有说明,所有图像均由作者提供。

参考文献

  1. K. Deb, S. Agrawal, A. Pratap, 和 T. Meyarivan. 一种快速且精英的多目标遗传算法:NSGA-II. IEEE 进化计算汇刊,6(2):182–197,2002 年。

  2. O. Kramer 和 P. Koch. 耙选择:一种新颖的进化多目标优化算法。见于 人工智能进展(KI),页 177–184,2009 年。

1.5 年的 Spark 知识总结为 8 个技巧

原文:towardsdatascience.com/1-5-years-of-spark-knowledge-in-8-tips-f003c4743083?source=collection_archive---------0-----------------------#2023-12-24

我在 Databricks 客户合作中的学习经验

Michael BerkTowards Data Science Michael Berk

·

关注 发布于 Towards Data Science · 8 分钟阅读 · 2023 年 12 月 24 日

--

图 1:如何编写 Apache Spark 的技术图示。图片由作者提供。

在 Databricks,我帮助大型零售组织部署和扩展数据及机器学习管道。以下是我在实际工作中学到的 8 个最重要的 spark 技巧/窍门。

在本文中,我们假设读者对 Spark 及其结构有一般的工作知识,但本文应对所有层次的读者都适用。

让我们深入探讨一下吧!

0 — 快速回顾

快速回顾一下 Spark 的功能…

Spark 是一个大数据处理引擎。它接受 Python/Java/Scala/R/SQL 并将这些代码转换为一组高度优化的转换操作。

图 2:Spark 驱动程序和工作节点配置。图像作者提供。

在最低层次上,Spark 创建任务,这些任务是 对数据分区的可并行变换。这些任务从驱动节点分配到工作节点,工作节点负责利用它们的 CPU 核心来完成变换。通过将任务分配给可能许多的工作节点,Spark 允许我们进行水平扩展,从而支持在单台机器上不可能实现的复杂数据管道。

好吧,希望这些信息不是全新的。不管怎样,在接下来的部分,我们会慢下来一点。这些提示应该对 Spark 的初学者和中级用户都有帮助。

1 — Spark 就是一个杂货店

Spark 是复杂的。为了帮助你和其他人理解其结构,我们借用 排队理论 中一个非常好的类比:spark 就像是一个杂货店

当考虑 Spark 的分布式计算组件时,有三个主要组件……

  • 数据分区: 我们数据的行子集。在我们的杂货店中,它们是 杂货

  • Spark 任务: 在数据分区上执行的低级变换。在我们的杂货店中,它们是 顾客

  • 核心: 你处理器中并行工作的部分。在我们的杂货店中,它们是 收银员

就这样!

现在,让我们利用这些概念来讨论一些 Spark 的基础知识。

图 3:收银员类比的插图,特别是针对数据倾斜。图像作者提供。

如图 3 所示,我们的收银员(核心)一次只能处理一个顾客(任务)。此外,一些顾客有很多杂货(分区行数),如图中收银员 2 处的第一个顾客所示。从这些简单的观察……

  • 收银员(核心)越多,你可以并行处理的顾客(任务)就越多。这是 水平/垂直扩展

  • 如果你没有足够的顾客(任务)来充分利用你的收银员(核心),你就会支付收银员的空闲时间。这与 自动扩展、集群大小和分区大小有关。

  • 如果顾客(任务)的杂货(分区行数)差异很大,你会看到收银员的利用率不均。这是 数据倾斜

  • 你的收银员(核心)越好,他们处理单个顾客(任务)的速度就越快。这涉及到升级你的处理器。

  • 等等。

由于这个类比来源于直接与分布式计算相关的排队理论,它的解释能力非常强!

使用这个类比来调试、沟通和开发 Spark。

2— 将数据一次性收集到内存中

Spark 初学者最常见的错误是误解了延迟计算。

懒惰求值 意味着在调用内存集合之前不会执行任何数据转换。调用集合的方法示例包括但不限于……

  • .collect():将 DataFrame 载入内存作为 Python 列表。

  • .show():打印 DataFrame 的前 n 行。

  • .count():获取 DataFrame 的行数。

  • .first():获取 DataFrame 的第一行。

最常见的错误集合方法是程序中普遍使用 .count()。每次你调用集合时,所有上游的转换都会从头开始重新计算,因此如果你有 5 次 .count() 调用,你的程序将呈渐近地运行 5 倍时间。

Spark 是懒惰求值的!管道应该有一个从源头到目标的单一流动。

3— 达到服务级别协议后停止

在与大型组织合作时,一个出乎意料的常见问题是他们忽视了大局,从而以低效的方式优化管道。

这是大多数用例中管道优化的方法……

  1. 问问我们是否需要做这个项目。 简而言之,考虑一下优化管道实际上能带来什么。如果你期望运行时间提高 20%,而管道的运行成本是$100,那么你是否应该投入你极其昂贵的数据工程师的薪资以节省每次运行的$20?也许吧,也许不是。

  2. 寻找代码中的低悬果实。 在同意做这个项目后,检查代码是否有明显的缺陷。示例包括懒惰求值的误用、不必要的转换和转换顺序不正确。

  3. 利用计算来使任务在服务级别协议下运行。 在检查代码相对高效后,直接将计算资源投入问题中,以便你可以 1) 达到服务级别协议,并且 2) 从 Spark UI 收集统计数据。

  4. 停止。 如果你已经充分利用了计算资源,并且成本不算过高,做一些最后的计算改进后就停下来。你的时间是宝贵的。不要浪费时间去节省美元,而是在其他地方创造数千美元。

  5. 深入挖掘。 最后,如果你真的需要深入挖掘,因为成本不可接受,那么就卷起袖子,优化数据、代码和计算。

这个框架的美妙之处在于,1–4 只需要对 Spark 有基本了解,并且执行非常快速;有时你可以在 30 分钟的电话会议中收集 1–4 的相关信息。该框架还确保我们会在“足够好”时立即停止。最后,如果需要第 5 步,我们可以将其委托给团队中最强的 Spark 专家。

通过找出所有避免过度优化管道的方法,你可以节省宝贵的开发时间。

4—磁盘溢出

磁盘溢出是 Spark 作业运行缓慢的最常见原因。

这是一个非常简单的概念。Spark 设计用于利用内存处理。如果内存不足,Spark 将尝试将额外的数据写入磁盘以防止进程崩溃。这称为磁盘溢出。

图 4:突出显示磁盘溢出的 Spark UI 屏幕截图。图片由作者提供。

写入和读取磁盘速度较慢,因此应尽量避免。如果你想了解如何识别和减轻溢出,请参考这个教程。然而,一些非常常见且简单的方法来减轻溢出是…

  1. 每个任务处理更少的数据,这可以通过更改分区数量来实现,方法是通过spark.shuffle.partitionsrepartition

  2. 增加计算中的 RAM 与核心的比率。

如果你希望你的作业运行得更佳,请防止溢出。

5—使用 SQL 语法

无论你使用 Scala、Java、Python、SQL 还是 R,Spark 在底层总是利用相同的转换。因此,选择适合你任务的语言。

对于许多操作,SQL 是支持的 Spark 语言中最简洁的“语言”!更具体地说:

  • 如果你在添加或修改列,请使用selectExprexpr,尤其是与 Python 的f-strings配合使用。

  • 如果你需要复杂的 SQL,创建临时视图然后使用spark.sql()

这里有两个快速示例…

# Column rename and cast with SQL
df = df.selectExpr([f"{c}::int as {c}_abc" for c in df.columns])

# Column rename and cast with native spark
for c in df.columns:
    df = df.withColumn(f"{c}_abc", F.col(c).cast("int")).drop(c)
# Window functions with SQL
df.withColumn("running_total", expr(
  "sum(value) over (order by id rows between unbounded preceding and current row)"
))

# Window functions with native spark
windowSpec = Window.orderBy("id").rowsBetween(Window.unboundedPreceding, Window.currentRow)
df_with_running_total_native = df.withColumn("running_total", F.sum("value").over(windowSpec))

使用 SQL。

6—全局过滤器

你需要读取存储在复杂目录中的一堆数据文件吗?如果是这样,请使用 Spark 极其强大的读取选项

我第一次遇到这个问题时,我重写了os.walk以与数据存储在的云提供商一起使用。我非常自豪地向我的项目伙伴展示了这个方法,他只是说,“让我分享我的屏幕,”然后向我介绍了全局过滤器。

# Read all parquet files in the directory (and subdirectories)
df = spark.read.load(
  "examples/src/main/resources/dir1",
  format="parquet", 
  pathGlobFilter="*.parquet"
)

当我应用上面显示的 glob 过滤器,而不是我自定义的 os.walk 时,数据摄取操作的速度提高了超过 10 倍。

Spark 有强大的参数。在构建定制实现之前,请检查是否存在所需的功能。

7 — 使用 Reduce 与 DataFrame.Union

循环几乎总是对 spark 性能有害。原因如下…

Spark 有两个核心阶段 — 规划和执行。在规划阶段,spark 创建一个有向无环图(DAG),指示如何执行指定的转换。规划阶段相对昂贵,有时可能需要几秒钟,因此你希望尽可能少地调用它。

让我们讨论一个必须遍历许多 DataFrame、执行昂贵转换,然后将它们追加到表中的用例。

首先,几乎所有的迭代使用案例都得到了本地支持,特别是 pandas UDFs、窗口函数和连接。但如果你确实需要一个循环,下面是如何调用一个单一的规划阶段,从而在一个 DAG 中完成所有变换。

import functools
from pyspark.sql import DataFrame

paths = get_file_paths()

# BAD: For loop
for path in paths:
  df = spark.read.load(path)
  df = fancy_transformations(df)
  df.write.mode("append").saveAsTable("xyz")

# GOOD: functools.reduce
lazily_evaluated_reads = [spark.read.load(path) for path in paths]
lazily_evaluted_transforms = [fancy_transformations(df) for df in lazily_evaluated_reads]
unioned_df = functools.reduce(DataFrame.union, lazily_evaluted_transforms)
unioned_df.write.mode("append").saveAsTable("xyz")

第一个解决方案使用 for 循环遍历路径,进行复杂转换,然后追加到我们感兴趣的 delta 表中。在第二种方法中,我们存储一个惰性计算的 DataFrame 列表,对它们进行转换,然后通过合并进行简化,执行一个 spark 计划并写入。

我们实际上可以通过 Spark UI 看到后端架构的差异…

图 5:for 循环与 functools.reduce 的 spark DAG。图片由作者提供。

在图 5 中,左侧对应于 for 循环的 DAG 将有 10 个阶段。然而,右侧对应于functools.reduce的 DAG 将只有一个阶段,因此可以更容易地进行并行处理。

对于读取 400 个唯一 delta 表并追加到一个 delta 表的简单用例,这种方法比 for 循环快 6 倍。

创造性地创建一个单一的 spark DAG。

8 — 使用 ChatGPT

这不是关于炒作。

Spark 是一个成熟且有充分文档的软件。LLMs,特别是 GPT-4,擅长将复杂信息提炼成易于理解和简明的解释。自从 GPT-4 发布以来,我没有做过一个复杂的 spark 项目而没有依赖 GPT-4。

图 6:GPT-4 对影响 spark 中数据分区大小的示例输出。图片由作者提供。

然而,显而易见的是,使用 LLMs 时要小心。你发送到封闭源模型的任何内容都可能成为母公司组织的训练数据 — 确保你不发送任何敏感信息。此外,请验证 GPT 的输出是否可靠。

如果使用得当,LLMs 在 spark 学习和开发中具有颠覆性作用。每月$20 是值得的。

10 个数据分析师可能会遇到的常见问题——以及如何回答它们

原文:towardsdatascience.com/10-common-questions-data-analysts-are-likely-to-encounter-and-how-to-answer-them-5d4d967635da?source=collection_archive---------12-----------------------#2023-07-25

学习如何应对一些行业数据分析师最常遇到的问题

Matthew Gazzano Towards Data Science Matthew Gazzano

·

Towards Data Science 上发布 ·10 分钟阅读·2023 年 7 月 25 日 关注

--

照片Scott Graham 提供,来源于 Unsplash

在快速变化的数据分析领域,遇到既视感并不罕见,尤其是当你进入新的角色时。你可能会注意到一个反复出现的模式,那就是关于数据和业务的相同问题不断浮现。

但请相信,这不是偶然的。

在不同的组织、行业和部门之间,出现了显著的相似性。尽管它们提供的产品、服务和商业模式各异,但组织对从数据中获得的见解有着共同的渴望。

作为数据分析师,理解和解决有关你业务的基本问题对你的有效性至关重要。通过将你的报告和分析框架置于核心业务问题的背景下,你可以引发与管理层和决策者更深入的讨论。

通过这篇文章,我旨在为你提供应对这些重复出现的问题所需的知识和见解。通过准备好解决这 10 个基本问题,你将增强你的分析能力,并在组织中确立自己作为不可或缺的资产。

下面是你可能会被问到的问题。

💸财务与销售

产品收入与基准相比如何?

组织通常想知道他们的财务状况是否达到了年度目标。这虽然最常见于财务数据分析师的报告中,但大多数分析师都应该做好回答这类问题的准备。

当我处理财务规划与分析报告时,组织通常会为财政年度设定一系列目标/配额基准。这些基准会以月度和累计总额的形式提供。它会类似于以下内容:

我们希望 X 产品线每月带来$100,000 的收入。这意味着它在第一个月应带来 100,000,在第二个月带来 200,000……

为了解决这个问题,作为数据分析师,你的角色将涉及将各种数据源,如 CRM 系统或由高层领导设定的外部清单,与账单系统结果连接起来。通过合并这些来源,你可以识别出收入的盈余或赤字,并为任何偏离预期目标的情况提供有意义的解释。你的分析将揭示造成赤字的潜在因素,使管理层能够做出明智的决策。

这种类型的报告不仅展示了你处理财务数据的能力,还展示了你在连接和解释来自不同来源的信息方面的分析技能。通过有效地弥合财务目标与实际结果之间的差距,你使决策者能够全面了解组织的财务表现。

我们预期收入将如何增长或收缩?

预测收入增长或收缩的趋势是领导者希望解决的关键问题。作为数据分析师,你可以在提供有关未来收入趋势的知情预测方面发挥关键作用,通常是在月度和季度基础上。让我们探讨一些你作为数据分析师或财务团队成员可以实施的技术,以有效解决这个问题:

  • 时间序列预测模型 — 该模型使用统计技术根据历史数据预测日期轴上的值。可以使用多种技术来执行时间序列模型,详细内容过于繁琐,此处不作详述 — 请参阅我提供的链接以获取更多信息。

  • 利用销售管道 — 常见的财务报告是“实际 vs 预测”报告。例如,“3 X 9”表示我们展示了 3 个月的实际数据与 9 个月的预测数据。预测元素将利用 CRM 中的开放数据,并计算在财年结束前的成交可能性百分比。

  • 手动输入 — 在某些团队中,销售领导希望能够审查管道中的内容,并挑选出哪些销售机会将会成交、成交时间以及预计价值,作为最终审查。虽然这种方法仍然有效,但依赖于个人的判断,无法程序化精简。这通常涉及接收一个需要纳入时间序列汇总的数值电子表格。

通过准确预测收入的增长或收缩,组织可以主动分配资源,设定实际目标,并识别潜在的绩效差距。掌握这些信息,领导者可以做出与预算编制、投资、招聘和运营规划相关的关键决策。收入预测使领导者能够评估组织的财务健康状况,评估销售和营销策略的有效性,并做出调整以确保可持续增长。最终,收入预测使领导者能够应对不确定性,降低风险,并引导组织走向长期成功。

特定销售渠道的有效性如何?

销售和财务领导希望能够查看不同收入来源的情况。一些可能的问题包括:

  • 哪些销售渠道随着时间的推移在增长和缩减?

  • 哪些产品在特定渠道上表现最佳,这反映了我们客户的什么信息?

销售渠道本身指的是组织可以获取收入的不同方式。销售渠道的数量在不同的组织中会有所不同,因为这还取决于其商业模式。一个典型的销售渠道集可能如下所示:

  • 直接销售 — 使用销售团队与客户达成交易并将其记录在如 Salesforce 等 CRM 系统中。

  • 电子商务 — 客户直接在组织的网站上购买产品。

  • 企业合作伙伴关系 — 组织可以与其他公司合作销售其产品或服务。这可能包括形成战略联盟、合资企业或附属合作伙伴关系,以扩展其覆盖范围并利用合作伙伴的客户基础。

由于直接销售在大多数组织中占据重要部分,我发现这种类型的渠道报告最为突出。销售团队通常会实施某些销售举措,在特定时间段内推广特定产品线。这通常会引发一个仪表盘的创建,用于显示这些举措的进展情况,我们如何达到整体目标,以及每个销售代表的表现情况。预计这种类型的报告会受到最严格的审查,因为它很可能作为销售佣金的参考……

🎯竞争分析

我们拥有的市场份额百分比是多少?

对组织来说,了解他们在竞争对手中的位置是重要的。例如,领导层需要掌握以下内容:

  • 按客户支出和单位数量,总市场规模是多少?

  • 按产品类别?按地区?我们的组织在市场规模中占有多少百分比?

这可以通过多种方式在各个行业中进行跟踪。我们可以以新闻媒体行业为例。该领域的组织希望了解每天在所有新闻网站上有多少总访客,以及这些访客中有多少在每个网站上。像Comscore这样的供应商提供一系列工具,允许分析团队评估竞争格局,并查看主要网站的独特页面浏览量。

👤客户报告

客户保留:客户流失和续订率?

领导者想了解他们如何扩展客户基础。这意味着提供在给定时间点的汇总总数,同时了解典型客户的流失率。如果有客户在 x 个月后停止使用你的服务的模式,那么原因是什么?这对客户生命周期的故事有什么启示?

理解客户保留率和流失的可能性,使领导者能够调整路径或为销售团队提供挽救可能流失客户的能力。当团队掌握流失报告时,他们将对客户流失的原因有深入了解,并会制定标准化流程以保持客户的账单状态。

客户细分:我们的典型客户类型有哪些?

映射一系列客户档案是向产品和销售领导者提供的强大分析。这使团队能够根据最相关的客户群体(在 B2C 中)或最相关的组织类型(在 B2B 中)来量身定制服务。

这是一个典型的机器学习问题,使用 K-Means 聚类模型,这是一种无监督模型,根据一系列输入将记录分组到一定数量的不同组中。执行客户细分模型是一个独立的主题 —— Ceren Iyim 写了一篇关于这个主题的优秀文章,名为Customer Segmentation with Machine Learning**。

📱产品与市场营销

我们的产品/服务哪些功能使用最多,哪些功能使用最少?

拥有数字产品的组织不断监测用户最常使用的功能。理解用户的“点击路径”背后的模式使得 Instagram 和 TikTok 等产品能够如此引人入胜和互动(有人可能会说这些服务在这一领域过于有效,但那是另一篇文章…)。当产品团队能够理解产品中的哪些功能有效、哪些无效时,他们可以构建对用户更具影响力的功能,这最终会提高客户留存率。

这也适用于那些不是围绕数字产品建立的组织——包括服务、零售和酒店业。数据可能通过客户调查的不同形式进行收集。领导者仍然可以从直接的客户反馈中获得有价值的发现,甚至从客户交易历史中获得洞察。亚马逊客户评论就是一个很好的例子——分析团队可以发展出对产品的一般情感,并报告评论中最常出现的关键词。

我们的在线品牌互动(网站/社交媒体)是什么?

公司希望对其品牌认知有一个清晰的把握。因此,创建了数字营销活动来吸引对其组织的关注和参与,这最终成为销售工具。不足为奇的是,客户更可能购买他们熟悉和信任的品牌产品。

因此,数据分析师可以被指派负责报告社交媒体互动或网站分析数据。在这种情况下,你可能需要回答如下问题:

  • 我们的帖子获得了多少印象、互动和分享?这些数据随时间如何变化?

  • 哪些页面或帖子表现得比其他的更好?

当领导者能够回答这些问题时,他们可以将品牌互动策略调整为最有效的内容,从而最终增加浏览量和认知度。

我们的客户满意度是多少?

与品牌互动类似,领导者也需要了解客户对你的产品和品牌的总体情感。团队可以参考多个数据点来推测总体满意度,包括以下内容:

  • 使用开源或付费 API 提取的组织社交媒体账户上的评论、推文和帖子。

  • 客户满意度调查

从客户满意度中获得的洞察将能够描述客户认为哪些方面做得好与哪些方面存在最大痛点的更大趋势,从而对他们的产品和服务产生积极影响。以下是一些相关的示例:

  • Apple 和 iPhone:客户反馈在新 iPhone 型号的推出中发挥了重要作用。Apple 通过调查、用户测试和客户服务互动积极寻求客户意见。有关电池寿命、相机质量、软件功能和设计偏好的反馈影响了 Apple 在随后的 iPhone 发布中对这些方面的改进决策。

  • Netflix 和离线观看:响应客户反馈,Netflix 引入了离线下载内容的选项。许多用户请求这一功能,尤其是在互联网连接有限的情况下。Netflix 听取了客户的偏好,并引入了下载功能,提高了客户满意度,扩展了服务的便利性。

🚚供应链优化

我们的操作流程中存在哪些低效之处?

对于提供实物产品的组织而言,了解从订单到履行过程中存在的痛点对于提升整体服务质量和扩大订单量至关重要。运营领导者总是寻找减少交付时间的方法。一种常见的衡量方法是周期时间分析,这是一种时间序列报告,用于了解每个过程环节需要多长时间。例如,履行过程可能如下所示:

  • 客户下订单。

  • 订单在订单处理系统中接收。

  • 运营团队审核订单并向制造商请求所需材料。

  • 根据生产订单生产货物。

  • 材料被打包并装载到配送车辆上。

  • 产品正在运送到客户处。

  • 订单交付给客户。

在此过程中,我们会为每个步骤分配一个时间值,并生成一个仪表板,汇总所有订单的每个步骤的平均值/中位数长度,并根据筛选条件(按产品线、在某个日期范围内、客户位置等)进行过滤。

改善操作流程的另一种方法是通过错误率监控,这是设计用来捕捉异常、操作错误或对标准化流程的例外的报告。例如,为什么订单在某一步骤失败?哪些客户订单场景在流程中没有考虑到,从而导致了手动操作? 一旦这些问题得到解答,领导者就可以采取措施来解决它们或减少它们的发生频率。

结论

本文提供了各种组织性问题。一时间掌握所有这些问题可能会让人感到不堪重负。所以这里有一个建议:专注于我之前提到的业务领域中的一个。深入了解该领域,明白管理层对它提出了哪些问题。在我担任过的几乎所有分析角色中,那些在其领域内成为专家的数据分析师也是最受认可的。

虽然这个列表并未完全涵盖每个组织的报告需求,但它应为你提供一个关于需要在高层次上提出哪些问题的一般框架。在任何角色中,你的工作与最终收益(收入)的关联越紧密,你作为员工的不可替代性就越强。作为数据分析师时,当你对我所讨论的这些问题有深入理解时,你在角色中的有效性更高,并能提供可操作的结果。

提升检索增强生成系统性能的 10 种方法

原文:towardsdatascience.com/10-ways-to-improve-the-performance-of-retrieval-augmented-generation-systems-5fa2cee7cd5c?source=collection_archive---------0-----------------------#2023-09-18

从原型到生产的工具

Matt AmbrogiTowards Data Science Matt Ambrogi

·

关注 发表在 Towards Data Science ·9 分钟阅读·2023 年 9 月 18 日

--

快速入门指南并不够

“检索增强生成是补充用户对大型语言模型(LLM)如 ChatGPT 的输入,加入你(系统)从其他地方检索到的额外信息的过程。LLM 然后可以利用这些信息来增强它所生成的响应。” — Cory Zue

LLMs 是一个了不起的发明,但存在一个关键问题。它们会编造内容。RAG 通过为 LLMs 提供事实背景,使其在回答查询时更有用。

使用 LangChain 或 LlamaIndex 等框架的快速入门指南,任何人都可以用大约五行代码构建一个简单的 RAG 系统,比如一个文档聊天机器人。

不过,用那五行代码构建的机器人效果不会很好。RAG 易于原型设计,但很难投入生产——即达到用户满意的程度。一个基础教程可能使 RAG 达到 80% 的效果,但弥补剩下的 20% 通常需要一些认真的实验。最佳实践尚未确定,并且可能因使用场景而异。但是,弄清楚最佳实践是非常值得我们花时间的,因为 RAG 可能是使用 LLMs 的最有效方式。

本文将探讨提高 RAG 系统质量的策略。它专为那些使用 RAG 构建系统、希望弥补基础设置与生产级性能之间差距的读者量身定制。就本文而言,提高意味着增加系统处理的查询比例,其中系统:1. 查找适当的上下文 2. 生成适当的响应。我将假设读者已经理解 RAG 的工作原理。如果没有,我建议阅读 这篇文章 作为一个很好的介绍。还假设读者对用于构建这些工具的常见框架有一些基本了解:LangChainLlamaIndex。然而,本文讨论的思想与框架无关。

我不会深入探讨每种策略的具体实施细节,而是会尝试给出何时以及为何它可能有用的想法。鉴于这一领域的快速发展,提供一个详尽或完全最新的最佳实践清单是不可能的。相反,我旨在概述一些你在尝试改进检索增强生成应用程序时可以考虑和尝试的内容。

提高检索增强生成性能的 10 种方法

1. 清理你的数据。

RAG 将 LLM 的能力连接到你的数据上。如果你的数据在实质上或布局上令人困惑,那么你的系统将会受挫。如果你使用的数据包含冲突或冗余的信息,你的检索将难以找到正确的上下文。当它找到时,LLM 执行的生成步骤可能会不尽如人意。假设你正在为你的创业公司帮助文档构建一个聊天机器人,你发现它的效果不好。你首先应该查看的是你输入系统的数据。主题是否按逻辑分解?主题是否集中在一个地方还是多个分开的位置?如果你作为一个人,不能轻松判断需要查看哪个文档来回答常见问题,你的检索系统也无法做到。

这个过程可以像手动合并同一主题的文档一样简单,但你可以更进一步。我见过的一个更具创意的方法是使用 LLM 创建所有提供的文档的摘要。检索步骤可以首先在这些摘要上进行搜索,仅在必要时才深入详细信息。一些框架甚至将其作为内置抽象。

2. 探索不同的索引类型。

索引是 LlamaIndex 和 LangChain 的核心支柱。它是承载检索系统的对象。RAG 的标准方法涉及嵌入和相似性搜索。将上下文数据分块,嵌入所有内容,当有查询时,从上下文中找到相似的部分。这种方法效果很好,但并非所有用例的最佳方法。查询是否与特定项目有关,例如电子商务商店中的产品?你可能需要探索基于关键词的搜索。它不一定是非此即彼,许多应用程序使用混合方法。例如,你可以对特定产品的查询使用基于关键词的索引,但对一般客户支持则依赖嵌入。

3. 尝试不同的分块方法。

分块上下文数据是构建 RAG 系统的核心部分。框架抽象了分块过程,让你无需考虑。但你应该考虑。分块大小很重要。你应该探索哪种方式最适合你的应用程序。一般来说,更小的块通常会改善检索,但可能会导致生成缺乏周围上下文。有很多方法可以处理分块。唯一不适用的是盲目处理。这篇来自 PineCone 的文章列出了一些策略。我有一组测试问题。我通过运行实验来处理这些问题。我用小、中、大块大小循环了一遍,发现小块效果最好。

4. 玩转你的基础提示。

在 LlamaIndex 中使用的一个基础提示的例子是:

“上下文信息如下。根据上下文信息而非先前知识,回答查询。”

你可以覆盖这个设置并尝试其他选项。你甚至可以破解 RAG,使其在找不到好的答案时允许 LLM 依赖自己的知识。你还可以调整提示来帮助引导它接受的查询类型,例如,指示它对主观问题以某种方式回应。至少,覆盖提示是有帮助的,这样 LLM 可以了解自己正在做的工作。例如:

“你是一个客户支持代表。你旨在尽可能提供有用的信息,同时只提供事实信息。你应该友好,但不要过于多话。上下文信息如下。根据上下文信息而非先前知识,回答查询。”

5. 尝试元数据过滤。

提高检索效果的一个非常有效的策略是为你的块添加元数据,然后利用它来帮助处理结果。日期是一个常见的元数据标签,因为它允许你进行按时间过滤。假设你正在构建一个允许用户查询其电子邮件历史的应用程序。较新的电子邮件可能会更相关。但从嵌入角度来看,我们不能确定它们与用户查询的相似性最高。这提出了一个在构建 RAG 时需要记住的普遍概念:相似性 ≠ 相关性。你可以将每封电子邮件的日期附加到其元数据中,然后在检索时优先考虑最新的上下文。LlamaIndex 有一个内置的 Node Post-Processors 类,专门帮助处理这个问题。

6. 使用查询路由。

通常拥有多个索引是很有用的。然后在查询到达时,将它们路由到适当的索引。例如,你可能会有一个处理总结性问题的索引,一个处理指向性问题的索引,还有一个适用于与日期相关的问题的索引。如果你尝试优化一个索引以应对所有这些行为,你会发现它在所有这些行为上的表现都将有所折中。相反,你可以将查询路由到合适的索引。另一种用例是将一些查询定向到基于关键词的索引,如第二部分所述。

一旦你构建了你的索引,你只需要在文本中定义每个索引的用途。然后在查询时,LLM 将选择合适的选项。LlamaIndex 和 LangChain 都提供了这样的工具。

7. 关注重新排名。

重新排名是解决相似性与相关性之间差异问题的一种方法。通过重新排名,你的检索系统会像往常一样获取顶级节点作为上下文。然后它会根据相关性对这些节点进行重新排序。Cohere Rereanker通常用于此策略。这是我看到专家们经常推荐的策略。无论用例是什么,如果你在使用 RAG,你应该尝试重新排名,看看它是否能改善你的系统。LangChain 和 LlamaIndex 都提供了简化设置的抽象。

8. 考虑查询转换。

你已经通过将用户的查询放入基本提示中来改变用户的查询。进一步改变它可能是有意义的。这里有几个例子:

重新表述:如果你的系统没有找到查询的相关上下文,你可以让 LLM 重新表述查询并再次尝试。对人类来说看似相同的两个问题,在嵌入空间中不总是看起来那么相似。

HyDE: HyDE是一种策略,它会获取一个查询,生成一个假设响应,然后使用这两者进行嵌入查找。研究发现,这可以显著提高性能。

子查询:LLM 在处理复杂查询时通常表现更好。你可以将这种功能集成到你的 RAG 系统中,以便将查询分解为多个问题。

LLamaIndex 有关于这些类型的 查询转换 的文档。

9. 微调你的嵌入模型。

基于嵌入的相似性是 RAG 的标准检索机制。你的数据被分解并嵌入到索引中。当查询到来时,它也会被嵌入以便与索引中的嵌入进行比较。但是,是什么在进行嵌入呢?通常是一个预训练模型,如 OpenAI 的 text-embedding-ada-002

问题在于,预训练模型对嵌入空间中的相似性的理解可能与你实际上下文中的相似性不太一致。假设你在处理法律文件。你希望你的嵌入模型更多地依据你的领域特定术语,如“知识产权”或“合同违约”,而不是像“特此”和“协议”这样的通用术语。

你可以微调你的嵌入模型来解决这个问题。这样做可以提升你的检索指标 5%–10%。这需要更多的努力,但可以显著改善你的检索性能。这个过程比你想象的要简单,LlamaIndex 可以帮助你生成训练集。有关更多信息,你可以查看 Jerry Liu 关于 LlamaIndex 如何进行嵌入微调的 这篇文章,或 这篇文章,其中详细介绍了微调的过程。

10. 开始使用 LLM 开发工具。

你可能已经在使用 LlamaIndex 或 LangChain 来构建你的系统。这两个框架都有有用的调试工具,允许你定义回调,查看使用了什么上下文,检索来自哪个文档等等。

如果你发现这些框架内置的工具不够完善,那么有一个不断扩展的工具生态系统可以帮助你深入了解你的 RAG 系统的内部工作。Arize AI 提供了一个 notebook 内工具,允许你探索上下文是如何被检索的以及为什么。 Rivet 是一个提供可视化界面的工具,帮助你构建复杂的代理。它刚刚由法律科技公司 Ironclad 开源。新工具不断推出,值得尝试看看哪些工具对你的工作流程有帮助。

结论

使用 RAG 构建系统可能令人沮丧,因为它很容易上手,但要做到完美却非常困难。我希望以上策略能为你提供一些灵感,帮助你弥合这一差距。这些想法中的任何一个都不是全能的,整个过程充满了实验、尝试和错误。我在这篇文章中没有深入探讨评估,即如何衡量系统的性能。目前,评估更像是一门艺术而非科学,但设立某种可以持续检查的系统是很重要的。这是判断你所实施的变化是否有效的唯一方法。我之前写过关于如何评估 RAG系统的文章。欲了解更多信息,你可以探索LlamaIndex EvalsLangChain Evals和一个非常有前景的新框架,RAGAS

10 位女性分享在 IT 和数据领域的工作经验

原文:towardsdatascience.com/10-women-working-in-it-and-data-share-their-experience-17d8845233e6?source=collection_archive---------11-----------------------#2023-03-09

Kamila HamalcikovaTowards Data Science Kamila Hamalcikova

·

关注 发表在 数据科学前沿 ·9 分钟阅读·2023 年 3 月 9 日

--

IT 行业仍然被认为是男性主导的领域,还是时代在慢慢变化?我们采访了来自欧洲不同国家的女性 IT 专业人士,了解她们在 IT 或数据相关工作的经验。

照片由 Mimi Thian 提供,来源于 Unsplash

为什么技术行业中的从业者是更多女性还是男性很重要?如今,技术招聘人员经常试图确保 IT 和数据团队的多样性,以确保 10 名软件工程师或数据分析师组成的团队不会提出相同的 10 个解决方案。

尽管有这些努力,根据麦肯锡的研究在欧洲只有 22%的技术岗位由女性担任。考虑到女性在 STEM 学科的低入学率,这并不令人感到特别惊讶。此外,根据欧洲统计局的数据,女性在高等教育中 STEM 学科的毕业率正在下降。

因此,我邀请了 10 位具有不同 IT 领域经验的女性 IT 专业人士分享她们在技术行业的亮点和挑战,以帮助年轻女孩决定是否想尝试 IT 职业。(更多关于受访女性的细节可以在本文末尾找到。)

问题 1: 你认为女性在 IT/数据领域或任何人从事这个领域工作的最大优势是什么?

图片由Christina @ wocintechchat.com拍摄,来源于Unsplash

玛利亚: 我会从 IT/数据从业者的角度来回答这个问题。因为我认为这些优势与性别无关。IT 或数据相关的工作需求非常高。这给了你选择的机会,对我来说这也意味着力量。

你不必急于得到任何东西,你可以选择最适合你的工作。其他一些优势是能够在任何地方工作,因为你只需要一台笔记本电脑和 Wi-Fi。高薪是当前对这些角色需求旺盛的另一个结果。此外,我非常喜欢挑战和解决问题的可能性,但我认为这更多是个人感受。

雅雅: 优势包括各种工作机会、体面的薪资以及在一个动态环境中工作,在这里你总是能学到新东西。这也非常灵活,你可以很容易地转换领域。

比安卡: 最大的优势是持续学习和自我发展、环境、薪资和其他福利。

尽管来自葡萄牙的数据工程师玛利亚、在法国工作的摩洛哥软件工程师雅雅,以及来自罗马尼亚的软件测试员比安卡提到高薪是技术行业的一个优势,但这只是部分正确。

IT 和数据领域的薪资通常高于大多数其他部门,但基于经济活动的性别薪酬差距在欧盟 ICT 部门中仍然是第二高来自 Eurostat 的数据 显示,2020 年未经调整的性别薪酬差距为 13%,而金融和保险服务领域为 26.4%,信息和通信(ICT)紧随其后,为 19.4%的性别薪酬差距

Swati:一般来说;在家工作、灵活的工作时间(如果项目允许的话),而且不需要太多的体力劳动。

Nadia:有很多优势;高薪资、成长机会、灵活的时间表:我不必在 9 点开始一天或在 6 点结束。我每周的会议相对较少,我可以根据自己的喜好安排剩余的时间。

另外,跨国移动的可能性:我去年搬到了法国,我不会说法语,但这并不影响我的工作,因为一切都用英语讨论。而且,工作的性质当然也是不受国家限制的

正如 Swati Bhoi 和 Nadia Chirkova 所提到的,她们都在法国工作,尽管她们来自不同的原籍国(印度和俄罗斯),但远程工作的可能性是一个特别的好处,特别是在 IT 领域。根据来自 Coresignal 的分析IT 和服务在 2021 年在欧洲的远程职位中占据了最高比例,为 22.1%

Darya:社区和网络。尽管女性在 IT 领域仍然代表性不足,女性创建了许多支持小组,你可以在这些小组中结识志同道合的人。有专门针对女性的活动(如HerHackathon)或女性专属的专业项目(如BreakLine)。社区支持和欢迎。

Sylvie:作为自由职业者,自主安排工作时间的可能性。另外,该领域的良好薪资使我们能够获得独立。对我来说,作为女性是一个优势,因为多样化团队的需求使女性更容易找到工作,而且女性在电脑前被贴上极客标签的可能性较小。

Andreea:总体而言,能够在一个非常创新的领域工作在技术和社会发展的过程中变得越来越重要。

Catherine:对我来说,这是一个女性配额和学习成长的机会。

尽管一些公司努力聘用更多女性担任某些技术岗位,但女性在技术快速增长的角色中的代表性仍然很低

虽然 46%的欧洲 UX 设计师是女性,但在软件工程和架构职能中,女性仅占 19%。此外,云解决方案架构师仅有 10%是女性,Python 开发者中只有 13%是女性,而这些职位在就业市场上需求极高

图片来源:Humphrey MulebaUnsplash

特雷莎:女性在 IT/数据领域工作的最大优势,或者说任何人在这个领域工作的最大优势,就是工作的创造性。这些项目和挑战种类繁多,提供了持续学习和成长的机会。此外,工作的性质允许在时间和空间上有很大的灵活性。

只需一台笔记本电脑、网络连接,和可能的一杯好咖啡,我就可以在任何地方工作,无论是在家里、咖啡店,还是在我们位于布拉格市中心的美丽办公室。这种灵活性使我能够追求职业生涯,同时还能照顾我的两个儿子(陪伴大儿子去柔道课,或在周五午餐后接小儿子放学)。

我真正喜欢在 IT 行业工作的原因是女性可以获得的支持和社区。有许多旨在支持和赋能女性的倡议(比如CzechitasPyLadies),社区也非常强大和热情。

问题 2:你认为女性在 IT/数据领域工作,或者一般在这个领域工作的人面临的最大挑战是什么?

图片来源:LinkedIn Sales SolutionsUnsplash

凯瑟琳:我认为在 IT 或数据世界中,男性的声音更容易被听到。

Swati:从我的女性视角来看,当你提出解决方案时,可能面临被不够重视的风险,有时薪水较低,还需要更具说服力来证明自己的价值。

Aya:我认为最大的挑战之一是打破这是男性领域的刻板印象,证明我们同样有能力和聪明。这迫使我们在工作中加倍努力,以使我们的技能得到认可。

Maria:一个常见的挑战是需要不断学习。你必须投入大量精力来学习和提升你的编程/分析技能。但他们没告诉你的是,如果你想要成功,你必须不断进行这项工作。总是有新的东西,新的更好的东西,你必须跟上 IT 及其市场的速度

作为女性,我不得不承认,相较于我的男性同事,我觉得自己必须比男性客户证明自己多一倍或三倍才能被认真对待。不止一次,他们的问题被直接问向电话中的男性,即使我在主导讨论。这也引发了“我够好吗?”、“我只是填补了一个名额吗?”的疑问,你开始怀疑自己并感觉像个冒名顶替者。

面试的女性共同关心的问题是,相比男性同行,IT 领域的女性工作者需要证明自己,这可能也是2022 年所有科技裁员中有 69.2%是女性的原因之一。这个数据来自于WomenTech Network 的研究

Bianca:最大挑战是技术迅速变化以及客户需求模糊且经常不完整。

Sylvie:我认为在 IT 领域对女性没有什么特别的要求,除了我每月一次的生理期让我感到困扰。我在同事中感到受尊重。

Andreea:总体而言,跟上技术进步,以及向不同的参与者展示数据素养和数据民主的重要性,当数据基础设施尚未到位时。

Darya:这绝对是冒名顶替综合症。我遇到了许多背景、经验和职业水平各异的了不起的女性,她们时不时感受到“自己不够好”的感觉。IT 领域广阔且变化迅速,所以不可能了解所有的东西。

此外,这个领域竞争激烈,因此人们常常会相互比较。这一切都造成了一种不擅长编程、数据、演讲等的错觉。总有某人做得更好。所以在我看来,成功往往取决于个人,是否能够勇敢地开始只与过去的自己进行比较。

虽然白俄罗斯数据科学家 Darya Petrashka 提到的冒名顶替综合症在 IT 和数据领域由于技术快速变化而相当常见,但女性往往会更多地遭遇这一综合症。

正如网站 womenintech.co.uk/ 所指出的:“女性常常感到自己不属于这里,不如男性同行技艺高超。同样,来自少数群体的员工可能会因为相同的原因而体验到类似的感觉。”

Nadia:最大的挑战是保持知识的最新:你使用的框架可能会频繁变化,或者出现新的工具。但通常有许多以友好和全面的方式编写的教程或博客,所以这并不难。在我的领域,这一点更为强烈,因为除了工具之外,你还必须跟上科学文献。相关的文章每天都在发布。但这也是一种乐趣,因为阅读所有这些文章令人兴奋和鼓舞。

另一个挑战是较低的身体活动水平和大量的屏幕时间,对健康有非常负面的影响。你必须通过其他活动如散步或运动来弥补这一点。我个人在没有特殊的蓝光阻挡眼镜的情况下无法看屏幕,因为我的眼睛很快就会感到疲劳。

Tereza:我觉得最近变化很大,但仍然存在的最大挑战之一是克服我们内心的刻板印象。当我考虑大学选项时,我最初倾向于更多传统的女性专业,比如语言和经济学。我没有考虑 IT 行业的职业。

然而,在发现了专注于女性 IT 教育的组织之后,我意识到我的真正热情在于编程和数据分析。总结一下——保持开放的心态,接受所有可能性,并且永远不要停止学习和探索。

受访的 IT 或数据专业人士:

Catherine,是一名拥有 1-2 年经验的 BI 分析师,来自英国,远程为位于伦敦的客户工作。

Bianca,是一名拥有 2 年经验的测试工程师,来自罗马尼亚,工作地点在罗马尼亚的雅西。

Sylvie,是一名数据分析师和教师(自由职业者),拥有 7 年经验,来自法国,在法国格勒诺布尔工作。

Swati,是一名拥有 4 年以上经验的 IT 顾问,来自印度,在法国巴黎工作。

Darya,是一名拥有 3.5 年经验的数据科学家,来自白俄罗斯,远程在波兰工作。

Tereza,是一名数据分析经理,拥有 8 年经验,来自捷克,在捷克布拉格工作。

Andreea,是一名面向客户的数据科学家,拥有 6 年经验,来自法国,在法国里昂工作。

Maria,是一名拥有 2 年经验的数据工程师,来自葡萄牙,远程为位于伦敦和捷克布拉格的客户工作。

Nadia,是一名计算机科学领域的研究科学家,拥有 5 年经验,来自俄罗斯,在法国格勒诺布尔工作。

Aya,是一名拥有 1 年经验的软件工程师,来自摩洛哥,在法国格勒诺布尔工作。

声明:部分名字已根据受访女性的要求进行了更改。

TDS23-MEM

来源:{url}

学习-DBT-简易方式-7d9f773d25ea?source=collection_archive---------6-----------------------

2023 年十大令人困惑的 XGBoost 超参数及其调优技巧.md

10 个决策树比 1 个更好-719406680564.md

10 个示例学习 Python 的 JSON 模块 - 793e62309d64.md

10 个令人兴奋的项目创意,使用大型语言模型(LLMs)来丰富你的作品集-970b7ab4cf9e.md

10 个最常见却令人困惑的机器学习模型名称-e70595eef514.md

10 个最常被问的 Python 列表问题在 Stack Overflow 上的链接

2023 年提高数据科学技能的 10 个简单方法-af274dc513da.md

10 个关于 Julia 的知识点,我希望早知道的-3104ce7e3a2c.md

未来十年将塑造数据科学家角色的 10 个趋势-759cdda3a442.md

10 个提高 Pinns 的实用提示和技巧-1a5dd7b86001.md

10 种向 Pandas 数据框中添加列的方法-ccadf7306d89.md

11 个帮助我获得第一个数据科学职位的实用技巧-be5d2036d49.md

11 个你可能忽视的有用 Pandas 功能点 - 原文链接

12 个用于数据科学的思维模型-f2e2133d85ea.md

12 个 Python 装饰器,助力你的代码提升到新水平-a910a1ab3e99.md

像专业人士一样测试你的预测的 12 种方法

2023 年你需要了解的 13 个数据流行词-87d8d908e5b6.md

130 毫升技巧和资源,经过精心策划,历时 3 年,并附赠免费电子书 7832ca4a37ef.md

14 种让你的 Pandas 工作流飞快的技巧-b00ff0ac9267.md

16、8 和 4 位浮点格式 - 它是如何工作的?

python-mocking-in-production-1924953c8a14?source=collection_archive---------12-----------------------

2 个最佳 SQL 技巧,用于查找表中的重复值-1197618dcc74.md

2 种有效的方式将数据从本地迁移到云端-b3c3b03837f0.md

2023 年你需要知道的 2 个重要 SQL CASE WHEN 示例-cb5d50e59daa.md

2 个简单步骤减少 Pandas DataFrame 的内存使用 - b654565d654.md

2 个任务提升你的 Python 数据处理技能-3daf6c1c0528.md

20 个令人惊叹的 Julia 技巧和窍门,帮助高效程序员-5fedbfd00f73.md

20 个示例,掌握 Python Pandas 中数据框的合并技巧-22ffcd6059d1.md

2023 年回顾:总结后 ChatGPT 时代以及对 2024 年的展望-bb4357a4e827.md

8 个大多数数据科学课程没有教授但你应该知道的内容(第一部分)- 812e691c9430?source=collection_archive---------1-----------------------

2d-矩阵-变换-计算机-视觉-80b4a4f2120f.md

3 个常见的时间序列建模错误你应该知道的-a126df24256f.md

3 种提升大型语言模型的简单方法-68670fde9ffa.md

3 个简单技巧优化 Pandas DataFrames-b8bdbd50253.md

比较两个 Pandas DataFrames 的三种简单方法

3-easy-ways-to-include-interactive-maps-in-a-streamlit-app-b49f6a22a636.md

你应该了解的 3 个 PostgreSQL 基本函数-b2a96e301ac0.md

3 门免费课程以跟上最新的 ChatGPT 趋势

3 种有效使用 ChatGPT 和 GPT-4 进行更好编码的方法-7fb94e86be3e.md

3 个重要的 SQL 优化技术-d6da3e9c8442.md

3 种智能方式利用 ChatGPT 加速你的下一个数据科学项目-8c3e1c5aea18.md

3 个初级数据科学家应做的职业决策-728b20991120.md

3 个关于 Python 数据结构的必知概念-641e6f6207fc.md

3 个强大的 Python 库,帮助部分自动化 EDA,并让你开始你的数据项目。

3-powerful-tricks-to-work-with-date-time-data-in-python-67c2d3834338.md

3 个关于 astypeto_datetime 在 Pandas 中的实际区别 fe2c0bfc7678.md

2023 年数据科学作品集需要的 3 个项目-56623fc18c31.md

你希望翻译的是文件名吗?请确认。

3-python-用于高效解决特定数据处理任务的操作-551c8ed41c02.md

3 种快速简单的方法来使用 Pandas 可视化你的数据-4cac57fb4c82.md

3 个提升编程的快速技巧-760e06afd0c6.md

你应该注意的 3 个静默的 Pandas 错误 - 80d0112de6b5.md

3 种简单而强大的方式,这种 AI 技术将彻底改变数据管理。

3 种简单方法在 Python 中创建瀑布图-1124f7afc90f.md

3 个可能削弱你 SQL 查询性能的微妙错误-47e897688977.md

3 种季节性及其检测方法-4e03f548d167.md

3 个使用 Matplotlib 创建的独特图表,你可能之前没有见过的-421ab8cdd36f.md

3-使用 SQL 的 CASE WHEN 语句的场景-51e8e2829218.md

3 个处理日期时间数据的实用 Pandas 提示-424afbec628b.md

3 种在 Python 中使用 Altair 构建地理地图的方法-77c8e0781538.md

3 种方法将 matplotlib 图表嵌入 HTML 页面 - 8e11fa66a4b0.md

在非数据科学岗位上积累数据科学经验的三种方法 - 在非数据科学岗位上积累数据科学经验的三种方法

扩散模型:它们是如何扩散的?

原文

理解生成式人工智能背后的核心过程

Onur Yuce Gun, PhD Towards Data Science Onur Yuce Gun, PhD

·发表于 Towards Data Science ·7 分钟阅读·2023 年 11 月 4 日

--

扩散模型

机器学习中的“扩散模型”这一名称来源于统计学中扩散过程的概念。

那是什么统计概念

在自然科学中,扩散是粒子从高浓度区域扩散到低浓度区域的过程,这一过程常通过物理学和数学中的扩散方程来描述。

反应-扩散是一个很好的例子。

卡尔·西姆斯的珊瑚生长模拟

反应-扩散

反应-扩散是一个相当复杂的过程;如果你想阅读数学逻辑,你可以访问扩散模型大师卡尔·西姆斯的网站:

## 反应-扩散教程

格雷-斯科特反应-扩散模型的插图教程。

www.karlsims.com

让我们从一个简单的类比开始:

反应-扩散系统是一种描述事物如何变化和移动的方式,尤其是当你谈论化学物质时。想象你在纸上有几种不同的颜料,它们开始混合并创造出新颜色——这就像是“反应”部分。颜料的斑点不仅停留在一个地方;它们会扩散并混合——这种扩散就像是“扩散”部分。

所以,这些系统只是一组规则,它们告诉我们这些过程是如何发生的:化学物质如何相互反应形成新物质,以及它们是如何移动或扩散的。

这可以描述自然界中的许多不同事物,例如动物皮肤上的图案是如何形成的,污染是如何在环境中扩散的,以及许多其他同时发生反应和移动的情况!

反应-扩散算法在生成吸引人且实用的图案方面非常高效 — 图像由作者提供。

为什么这有用?

嗯,你可以用它们设计鞋子! 😉

New Balance Two WXY 系列具有反应-扩散图案 — 这不是我的设计,但计算设计团队在 2018 年向办公室的设计师们介绍了 RD — www.newbalance.com

嗯,这只是一个应用案例 —

反应-扩散系统可以通过数学公式进行描述和模拟,这有助于我们理解扩散事件。

这些公式主要解释了一个或多个化学物质如何随时间变化并在空间中移动。

这涉及到化学反应,其中化学物质可以转变为其他化学物质,以及扩散,即使这些化学物质在一个区域内扩散的过程。

AI 和机器学习中的扩散模型

在生成式 AI 领域,扩散模型以一种有些类似的方式工作,通过建模向数据中逐步添加噪声的过程,然后学习逆转这一过程。

然而,重要的是不要将反应-扩散生成的图案与机器学习系统中添加到图像的噪声混淆!

扩散模型接收一个信号(例如图像),并逐渐添加噪声,直到原始信号完全被遮蔽。再次强调,添加的噪声不需要有特定的形状(与反应-扩散生成的美丽图案相对!)

这在概念上类似于物质通过介质扩散的物理过程。

扩散模型的训练涉及学习逆过程:从一个有噪声的信号开始,逐渐去除噪声,以恢复原始信号。

这个过程让人联想到物理学中逆转扩散过程,其中粒子的位置从平衡状态(完全扩散)追溯到原始状态(浓缩)。

因此,"扩散模型"这一名称捕捉了从噪声到干净、有序信号的逆过程的本质,这是机器学习生成模型中的核心。

我见过很多解释扩散模型的图示,但要么我迷失在其中,要么它们被过于简化,以便阐明实际过程。

我还想为设计师开发一个自定义图示:这些人懂得伪代码和流程图,但不一定了解其中的数学(当然也有例外):

图示展示了扩散过程 — 由作者绘制

这个图示既不特别详细也不完全简化。我希望复杂度能“刚刚好”,使更多的人能够理解扩散过程。

在这个图示中,我感到有必要解释 UNET 和随后的“添加噪声”节点,因为它们构成了扩散过程中的“魔法”。

我们开始吧 —

UNET

UNET 是一种用于快速而精确图像分割的卷积网络架构。

结果发现,它最初是为了生物医学图像分割开发的。

UNET 的名称来源于其 U 形架构!

在“UNET”名称的由来中,我猜想可能有一些视觉传达元素在发挥作用!在我提供一个更民主化的 UNET 描述(我也能理解的 (!))之前,这里是我在 Medium 上找到的更逐行的技术描述(做得好!):

UNet 逐行解释

示例 UNet 实现

towardsdatascience.com

UNET 在其他应用中变得流行,如地理信息系统(GIS)。在这方面,分割技术可以帮助从航空或卫星数据中划定海岸线,实现精确的沿海制图。同样,它也可以用于识别和提取大型结构的轮廓,如摩天大楼或工业综合体,从高分辨率图像中提取。

因此,UNET 的应用逐渐扩展到其原始应用之外的各种图像到图像的翻译任务,包括图像去噪、超分辨率,最后进入生成模型如稳定扩散。

在稳定扩散模型的训练中,这包括从文本描述生成图像(文本到图像)或将一张图像转化为另一张具有某些风格变化的图像(图像到图像),UNet 发挥了重要作用。以下是其使用的简化说明:

1. 编码器-解码器结构

可以把 UNET 想象成一台有两个主要部分的机器。第一部分是“编码器”,它像一台紧凑的相机,聚焦于图像中最重要的部分,并将其拆分成更小的部分——这样更易于处理。第二部分是“解码器”,它像一位艺术家,接过紧凑的版本后,重新绘制出包含所有细节的完整图像。

2. 跳跃连接

通常,当你将某物缩小(如在编码器中),你会丢失一些细节(显而易见!)。但 UNET 有一个巧妙的技巧:它使用“跳跃连接”,这些快捷方式允许它记住并恢复在图像变小时可能丢失的细节。这有助于“艺术家”部分确保最终图像与原始图像一样详细。

3. 潜在空间操作

想象图像在编码器中的压缩版本作为一种蛋糕的食谱,我们称之为“潜在空间”。这个食谱包括了制作蛋糕所需的所有原料和步骤。在稳定扩散中,你可以改变这个食谱来改变最终的蛋糕。想把香草蛋糕变成巧克力蛋糕?你调整食谱——这就像调整“潜在空间”。你不是从头开始;你只是对原料(或图像特征)做一些小的调整,以得到不同但相关的结果(这可以帮助我们猜测自定义训练的提示)。如果基本食谱是草图,那么添加的风味和装饰就是绘画中的颜色和纹理。

4. 使用噪声进行训练

想象一下在暴风雨中开车。一开始,雨水猛烈地拍打在你的挡风玻璃上,让你难以看清前方的路。然而,雨刷会擦去水滴,给你短暂的清晰视野。训练 UNET 处理噪声就像是在提升雨刷的效果。起初,它们可能有缺陷,每次擦拭视野依然模糊。但随着你“训练”雨刷——调整其速度并确保它们与玻璃的接触正确——它们在清除雨水方面变得更有效。同样,U-Net 开始时试图穿透图像上的“雨”噪声。随着时间的推移,它在清除噪声方面变得更好,直到它能稳定地提供清晰的“视图”——就像有效的雨刷最终给你清晰的道路视野一样——可能不是最好的比喻,但你明白了……

如果你不喜欢这样的比喻,这里有一个更直接的描述:

添加和去除噪声

· 从一张干净的输入图像开始,引入噪声,将其转变为带噪声的图像。

· 在训练过程中将带噪声的图像输入 UNET,这是一种去噪自动编码器。

· 训练 UNET 预测并识别添加到图像中的特定噪声。

· 通过迭代训练,提高 U-Net 准确预测噪声的能力。

· 随着 UNET 技能的提升,它在从噪声数据中生成清晰图像方面变得更为娴熟。

· 利用 UNET 的噪声处理和操作能力来增强扩散过程,该过程系统地添加和去除噪声以制作新图像。

这种逐渐扩散的过程是产生高质量合成图像所必需的,已经在互联网上和我们的日常生活中广泛存在——但像 Midjourney、Stable Diffusion、Dall-E 等 AI 驱动的图像生成模型的令人印象深刻的结果是无可否认的。

就这样。读完这些内容后,希望你对扩散模型有更清晰的理解。

了解作者

想了解更多或联系我?这里是我的 LinkedInYouTube 账号。我还会在我的 Instagram 账号上分享生动的图像和新闻。

参考资料

[## U-net 是如何工作的? | ArcGIS Python API

ArcGIS Python API 文档。

developers.arcgis.com](https://developers.arcgis.com/python/guide/how-unet-works/?source=post_page-----5ac0fcb4426f--------------------------------) ## 卷积神经网络解析

让我们一起构建你的第一个 CNN 模型

towardsdatascience.com

探索 Linux 容器中叠加文件系统的力量

towardsdatascience.com/exploring-the-power-of-overlay-file-systems-in-linux-containers-d846724ec06d

通过独特而简单的分层思路释放容器化的潜力

Dimitris Poulopoulos Towards Data Science Dimitris Poulopoulos

·发布于 Towards Data Science ·7 min read·2023 年 2 月 1 日

--

Hoach Le Dinh 摄影 Unsplash

本系列文章探讨了 Linux 容器如何在幕后工作,以及我们需要什么工具来构建类似的环境而无需 Docker。为什么要这样做?好吧,如果你真的想知道当你执行 docker run 时发生了什么,这是你需要迈出的第一步。最后两篇文章集中在命名空间和控制组(groups)在容器化中的作用。

## 容器:它们如何在幕后工作以及为什么它们正在主导数据科学世界

初学者理解 Docker 神奇的指南

## Linux 控制组的力量:容器如何掌控其资源

使用 Linux 控制组优化容器资源分配

The Power of Linux Cgroups: How Containers Take Control of Their Resources

本文完成了对这一主题的讨论,引入了叠加文件系统,这是创建类似容器环境所需的最后一个构建块,无需依赖 Docker。

Linux 容器彻底改变了现代计算中应用程序的部署和管理方式。此技术的核心是叠加文件系统,这是一个关键组件,使容器能够共享一个共同的基础镜像,同时保持每个容器之间的隔离。

在接下来的段落中,我们将深入探讨叠加文件系统的概念及其在使 Linux 容器轻量、高效和安全方面的作用。无论你是经验丰富的系统管理员还是刚开始探索容器的新人,本文都提供了叠加文件系统的全面概述及其对容器化世界的影响。

学习速率是一个针对那些对 MLOps 世界感兴趣的人的新闻简报。MLOps 是一个广泛的领域,致力于以高效和可重复的方式将 ML 模型投入生产。容器在管道中扮演了关键角色。如果你想了解更多类似的主题,可以在 这里 订阅。你会在每个月的最后一个星期六收到我的更新和对最新 MLOps 新闻和文章的看法!

设置场景

Linux 容器已成为现代计算的一个重要组成部分,提供了一种高效和灵活的方式来打包和部署应用程序。与传统虚拟机不同,容器允许多个隔离的应用程序在单个主机上运行,共享主机的操作系统内核。这种架构提供了诸多好处,包括提高效率、可扩展性和可移植性。

容器已经存在了十多年,首个容器化技术出现于 2000 年代初。然而,直到 2013 年 Docker 的出现,容器才真正起飞,成为现代应用程序部署的基石。

然而,为了充分理解底层发生的情况,我们退一步,假设 Docker 不存在,尝试自己创建一个类似容器的环境。虽然这个努力不会给我们一个生产就绪的解决方案,我们仍然会缺少一些东西,但它将使我们更好地理解容器是如何工作的。

为此,本文探讨了叠加文件系统。叠加文件系统是一种联合文件系统,允许多个下层堆叠在一起,创建数据的统一视图。在 Linux 容器的背景下,叠加文件系统用于将容器所做的更改层叠在基础镜像之上,同时保持原始镜像的完整。这允许容器共享一个共同的基础镜像,并减少需要传输、存储和部署的镜像的大小。

如果这个解释听起来令人困惑,让我们自己创建一个 overlay 文件系统。它比你想象的要简单。

一个简单的例子

要构建 overlay 文件系统,我们需要两样东西:i)一个下层和 ii)一个上层。简单来说,我们需要两个目录。为了清楚地说明它们的角色,我们将第一个命名为lower,第二个命名为upper

mkdir lower upper

接下来,让我们进入lower目录并创建三个文件。我们将第一个文件命名为delete-me.txt,第二个命名为duplicate.txt,最后一个命名为lower.txt。可以在文件中放入任何内容,但请确保明确说明这是一个存在于lower目录中的文件。例如,在duplicate.txt中我们可以写:

I'm a duplicate file in the lower directory. 

lower目录 — 作者提供的图片

upper目录中,我们还将创建一个duplicate.txt文件和一个upper.txt文件。你可以根据自己的需要填充这些文件的内容,但请确保明确说明这些文件存在于upper目录中。

upper目录 — 作者提供的图片

现在,我们准备创建一个 overlay 文件系统,它将把两个目录叠加在一起,并呈现数据的统一视图。为此,我们需要两个更多的目录,一个名为merged,另一个名为workdir。这些目录与lowerupper目录处于同一层级。

mkdir merged workdir

我们的工作环境 — 作者提供的图片

创建 overlay 文件系统的命令如下:

sudo mount -t overlay -o lowerdir=lower/,upperdir=upper/,workdir=workdir none merged/

让我们一步一步地解读这个过程。首先,你说你想挂载一个类型为overlay的新文件系统。你指定了lowerupperworkdir目录。你将源设置为none,因为没有包含数据的底层设备。相反,数据来自lowerupper目录。最后,你说你想将其挂载到merged目录中。workdir目录是系统保存所需元数据的特殊位置。我们不会触碰这个目录。

如果你现在列出merged目录中的文件,你会看到lowerupper目录的统一视图。lower目录中的lower.txtdelete-me.txt文件都在其中,而upper目录中的upper.txtduplicate.txt文件也在其中。正如你现在可以安全地猜测的那样,lowerupper目录中具有相同名称的任何文件都将来自upper目录。

合并目录 — 作者提供的图片

此外,lower目录是只读的。你在merged目录中所做的任何更改不会影响它。相反,一切都将记录在upper目录中。

例如,如果你删除了delete-me.txt文件,系统将在upper目录中放置一个字符设备,其名称相同,次要和主要数字为零。这告诉系统该文件已被删除,你将在merged目录中看不到它:

删除文件 — 作者图像

如果你在lower目录中浏览,你会发现delete-me.txt文件仍在你留下的位置:

下层目录是只读的 — 作者图像

另一方面,如果你更改了lower.txt文件的内容,系统将执行写时复制操作。这意味着,当你保存对lower.txt文件的更改时,它将把文件复制到upper目录,并在其中保留这些更改。你可以自行检查每个目录中的lower.txt文件的内容。

这就是覆盖文件系统的精髓;你有一个保持不变的下层目录,而一个位于其上方的目录记录所有的更改。这就是在容器中如何保持基础镜像的文件系统不变,并拥有记录你对容器所做更改的层。

结论

总之,覆盖文件系统在 Linux 容器的成功中扮演着至关重要的角色,它使得应用程序的高效、安全和隔离部署成为可能。覆盖文件系统允许容器共享一个共同的基础镜像,同时保持每个容器相互隔离,从而减少镜像的大小并提高容器生态系统的整体性能。

在本文中,我们探讨了覆盖文件系统的基础知识及其对容器化生态系统的影响。现在,我们准备继续前进,创建一个不使用 Docker 的类似容器的环境。我将在下一篇文章中见到你。

关于作者

我的名字是Dimitris Poulopoulos,我是一名在Arrikto工作的机器学习工程师。我为欧洲委员会、Eurostat、IMF、欧洲中央银行、OECD 和 IKEA 等主要客户设计和实施了人工智能和软件解决方案。

如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据操作的文章,可以在MediumLinkedIn或 Twitter 上的@james2pl关注我。

表达的意见完全是我个人的观点,并不代表我雇主的观点或意见。

最佳 ChatGPT 插件

译文:towardsdatascience.com/the-best-chatgpt-plugins-96ea60ccf5f8

网页浏览、总结、编码等

Sophia Yang, Ph.D.Towards Data Science Sophia Yang, Ph.D.

·发布于 Towards Data Science ·8 分钟阅读·2023 年 5 月 25 日

--

就在几天前,OpenAI 通过向所有 ChatGPT Plus 用户推出网页浏览和插件进行了令人兴奋的更新。我认为现在是深入探讨一些我觉得有用的插件,并讨论它们在网页浏览、总结、编码等方面的应用的好时机。

⭐ 1. 网页浏览

至少有三种工具可以用于网页浏览:

  • OpenAI 的官方 Bing 搜索

  • KeyMate.AI 搜索

  • WebPilot

这三个工具在处理基本问题时表现很好。不过,我想找一个例子,展示语言模型和网页浏览工具需要几个步骤才能找到答案。这里是我想出的需要三步网页搜索的问题:

“谁接替了伍迪·约翰逊成为美国驻英国大使?她在那位总统的任期内服务过?此外,这位总统目前的年龄是多少?”

令人惊讶的是,这三个工具都答错了这个问题。最终的答案可能还可以,但中间的答案是错误的。

1.1 Bing 搜索(全新)

微软刚刚宣布将 Bing 作为默认搜索体验带入 ChatGPT。当我今晚写这篇博客文章时,我注意到我已经在 ChatGPT 上获得了 Bing 搜索的访问权限!

Bing 搜索使用了正确的搜索词,但返回了错误的答案。简·哈特利是现任美国大使,而不是伍迪·约翰逊的继任者。

对这个工具,我有一些喜欢和不喜欢的地方:

✅ Bing 搜索:我喜欢我们知道确切的搜索词,并且可以点击搜索词查看搜索结果。

✅ 思考:看起来 OpenAI 给了 LLMs 在每一步中考虑该做什么的机会。

✅ 引用:我真的很喜欢答案中的引用。

❎ 我希望能更清楚地看到内部发生了什么。也许有方法可以做到这一点,但我找不到。如果有人知道,请告知。

❎ 我们不能将 Bing 搜索与其他插件一起使用。将 Bing 的搜索能力与插件功能结合会更强大。我猜现在的替代方案是使用像 WebPilot 这样的搜索插件与其他插件互动。

1.2 OpenAI 之前的官方网页浏览(已替换为 Bing 搜索)

OpenAI 之前的网页浏览可以通过三个步骤正确回答这个问题:

1.3 KeyMate.AI 搜索

嗯,这个工具给出了各种错误答案。它甚至没有返回一个合法的大使。而且,看步骤时,它浏览了太多文档,不清楚它使用了哪个文档来得出最终答案。

1.4 WebPilot

WebPilot 是一个开源工具。你可以在这里找到它的 Github 仓库。WebPilot 没能找到谁接替了 Woody Johnson。正确的答案应该是 Yael Lempert,而不是现任大使 Jane D. Hartley。

✅ 我喜欢它提供了内部的情况:网页链接、链接内容以及语言模型应如何响应的规则。

✅ WebPilot 还可以以指定的语气回答问题、翻译、重写等。

⭐ 2. 网页总结

上述所有工具也可以用于总结网页:

2.1 OpenAI 官方网页浏览

2.2 KeyMate.AI 搜索

2.3 WebPilot

2.4 链接阅读器

链接阅读器工具旨在读取各种链接的内容,包括网页、PDF、PPT、图片、Word 文档及其他文档。

⭐ 3. 视频总结

3.1: VoxScript

VoxScript 在总结 YouTube 视频方面非常有用。我实际上发现这个工具对我最为有用。

3.2 ChatWithVideo

ChatWithVideo 在总结视频方面也表现不错。我发现当我询问有关视频的问题时,所需时间较长。

3.3 VideoInsights

我似乎对 VideoInsights 运气不佳。

⭐ 4. 编码

Noteable

我无法相信只有一个 ChatGPT 编程插件,那就是 Noteable。他们确实抓住了机会展示和推广他们的产品。虽然我之前没有机会使用 Noteable,但由于它是唯一的编程相关插件,我不得不尝试一下,结果令人印象深刻。

这是我使用的提示词(来源):

加载此数据:media.githubusercontent.com/media/MuseumofModernArt/collection/master/Artists.csv

我是美国一家主要艺术博物馆的协调员。我正在考虑在来年展示哪些藏品。请创建并执行一个笔记本,以分析上述数据,并提供图表和图形,以及趋势和异常的描述,讲述 MoMA 的艺术家在性别、国籍和历史时期上是否得到代表。请记住,我没有数据分析经验,所以请在笔记本中包含解释你所做工作的文本,并说明为什么这些工作很重要,以便易于理解。

Noteable 插件能够指导我创建一个新项目,并使用我创建的新项目:

结果是一个完整的报告,包括数据清洗、可视化和洞察!哇!如果你仔细观察,会发现有两个地方代码未运行并返回了错误信息。Noteable 能够重新运行并修复这些错误:

似乎发生了一个错误,因为‘artists’变量未定义。这可能是由于内核重启,导致所有变量被清除。我们需要重新运行数据加载和缺失值处理的单元格。

关于 Noteable 插件的更多想法:

✅ 我喜欢生成报告的简便性,用户界面也很棒。我没有期待一个完整的报告,那真是一个惊喜。我还发现了更多的示例,包括各种建模和可视化样式。

❎ 每次运行的时间比我想象的要长,而且每次得到的结果都不同。我希望它能更快,并生成更一致的结果。但这其实不是 Noteable 的问题,而是使用 OpenAI 模型的一个问题。生成的所有内容都是概率性的,没有保证每次都能得到相同的结果。

⭐ 5. 我最喜欢的 ChatGPT 插件

你最喜欢的 ChatGPT 插件是什么?你能猜到我最喜欢的是哪个吗?一定是 Comic Finder 插件。

你可以组合使用最多三个插件。例如,WebPilot 能够回答谁是英国首相,而 Comic Finder 找到了一些与政治相关的漫画。输出的信息很有价值,澄清了这些漫画虽然没有直接提到英国首相,但与政治领域相关。非常聪明!

⭐ 其他工具

  • Wolfram Alpha:毫无疑问,Wolfram Alpha 是最可靠的 ChatGPT 插件之一。它提供了强大的计算能力、精准的数学功能、精心整理的知识、实时数据和可视化工具。

  • Zapier:凭借跨越超过 5,000 个应用程序集成的自动化任务能力,Zapier 是一个非常实用的插件,可以简化你的工作流程。

  • PDF 互动:有很多插件可以与 PDF 文件互动。例如,之前提到的 Link Reader 插件与 PDF 文件配合得很好。此外,AskYourPDFChatWithPDF 等插件在这方面提供了更多功能。

  • 与金融相关的插件:值得注意的是,针对金融相关任务的插件非常丰富,特别是在获取市场数据和见解方面。

🤔 我对 ChatGPT 插件的投诉:

  • 用户界面: 用户界面还有改进的空间。虽然只有 128 个插件,但已经感到有些令人不知所措。如果能有一个搜索栏以便于查找特定插件,以及一些插件的描述缺乏有用的细节,那会更好。

  • 插件的可信度: 我对插件的可信度不太确定。我唯一信任的插件是 Wolfram 插件,因为它由著名的 Wolfram Alpha 开发。然而,对于其他插件,我不确定背后的开发者,也没有时间去调查每一个。如果插件能够像应用商店的应用程序一样托管,提供链接、演示和评分,那将有助于提高透明度。

  • 与网页浏览的兼容性: 目前,官方的网页浏览插件无法与其他插件一起使用。如果能够与其他插件无缝配合,那将会很有帮助。

  • 插件使用的限制: 只能同时使用三个插件可能会令人沮丧。如果语言模型本身可以决定使用哪些已安装的插件,而不是限制用户只能选择三个,那将更为理想。虽然我理解更多插件可能会消耗额外的令牌空间,但如果能有更多插件的选择将会很受欢迎。

  • 插件在代码中的集成: 我不确定如何将插件集成到 OpenAI API 中。如果有一个一致且简单的方法在 Python 中与插件互动,那将非常好。

🔮 未来

在微软 Build 大会上公布的最新消息确实将 ChatGPT 插件提升到了一个全新的水平。微软正在建立一个跨平台的 AI 插件生态系统:

开发者现在可以使用一个平台来构建和提交适用于消费者和商业环境的插件,包括 ChatGPT、Bing、Dynamics 365 Copilot、Microsoft 365 Copilot 和 Windows Copilot。

ChatGPT 插件无疑仍处于开发初期。我预期会有大量新插件涌现,赋能和释放 AI 系统的潜力。我期待着探索更多插件并见证未来的进展。

. . .

Sophia Yang于 2023 年 5 月 24 日发布

Sophia Yang 是一名高级数据科学家。请在LinkedInTwitterYouTube上与我联系,并加入 DS/ML Book Club ❤️

从零开始训练 BERT 的终极指南:最终篇

译文:towardsdatascience.com/the-ultimate-guide-to-training-bert-from-scratch-final-act-eab78b0657bb

最终前沿:构建和训练你的 BERT 模型

Dimitris PoulopoulosTowards Data Science Dimitris Poulopoulos

·发表于Towards Data Science·阅读时间 7 分钟·2023 年 12 月 18 日

--

图片来源:Rob LaughterUnsplash

本博客文章总结了我们关于从零开始训练 BERT 的系列内容。有关背景和完整理解,请参阅系列的第一部分、第二部分和第三部分。

当 BERT 在 2018 年登场时,它在自然语言处理(NLP)领域掀起了一场巨浪。许多人将其视为 NLP 的 ImageNet 时刻,类似于 2012 年深度神经网络对计算机视觉及更广泛的机器学习领域带来的变化。

五年过去了,预言依然准确。基于 Transformer 的大型语言模型(LLMs)不仅仅是新玩具;它们正在重塑整个格局。从改变我们的工作方式到革命性地获取信息,这些模型是无数新兴初创公司背后的核心技术,旨在挖掘它们尚未被充分利用的潜力。

这就是我决定撰写这一系列博客文章的原因,深入探讨 BERT 的世界以及如何从零开始训练自己的模型。重点不仅仅是完成任务——毕竟,你可以轻松找到在 Hugging Face Hub 上预训练的 BERT 模型。真正的魔力在于理解这个突破性模型的内部工作原理,并将这些知识应用于当前环境。

第一篇文章作为你的入门票,引入了 BERT 的核心概念、目标和潜在应用。我们甚至一起完成了微调过程,创建了一个问答系统:

BERT 训练的终极指南:介绍 [## BERT 训练的终极指南:介绍

解密 BERT:改变 NLP 领域的模型的定义和各种应用。

BERT 训练的终极指南:介绍

第二部分作为你了解分词器这一经常被忽视领域的内部指南——解释它们的作用,展示它们如何将单词转换为数值,并指导你训练自己的分词器:

BERT 训练的终极指南:分词器 [## BERT 训练的终极指南:分词器

从文本到标记:你的 BERT 分词指南

BERT 训练的终极指南:分词器

在第三章中,我们讨论了我认为整个训练流程中最困难的步骤:数据集准备。我们深入探讨了为 BERT 的专用 Masked ML 和 NSP 任务准备数据集的细节,这些任务是它用来理解语言和上下文的代理。

BERT 训练的终极指南:准备数据集 [## BERT 训练的终极指南:准备数据集

数据准备:深入探讨,优化流程,并发现如何应对最关键的步骤

BERT 训练的终极指南:准备数据集

现在,最终的序幕拉开并落下:训练。在这篇文章中,我们将挽起袖子,训练一个新的 BERT 模型——你可以稍后针对特定的 NLP 需求进行微调。如果你已经涉足深度学习模型训练,这篇文章应该能让你毫无问题地跟上。所以,事不宜迟,让我们深入探讨吧!

学习速率是一个面向对 ML 和 MLOps 世界感兴趣的人的通讯。如果你想了解更多类似的主题,请点击这里订阅。每个月的最后一个星期天,我会与你分享最新的 MLOps 新闻和文章的更新和看法!

训练循环

现代框架如 PyTorch 和 TensorFlow 简化了构建和训练深度神经网络的过程。训练循环就像编排,将数据集特征传递通过模型以获得预测,将模型的预测与标签(真实值)进行比较以计算损失,最后使用损失通过模型的层反向传播误差以更新模型的权重。

这就是在 PyTorch 中流程的样子:

for epoch in range(epochs):
    # forward pass
    predictions = model(features)
    # calculate the loss
    loss = criterion(predictions, labels)
    # propagate the error backward to calculate the gradients
    loss.backward()
    # update the model's weights
    optimizer.step()

    # xero gradients
    optimizer.zero_grad()

理论上,这看起来很简单,不是吗?实际上,它变得更简单而且更强大。我们将使用 Hugging Face transformers 库来训练我们的 BERT 编码器。Trainer 对象为我们处理循环实现,并添加了许多其他功能。因此,我们可以将注意力转向代码的其他部分。现在,让我们首先定义我们的指标。

在纸面上,这个过程看起来真的很简单,你不觉得吗?现实更好:它不仅简单,而且非常强大,这要归功于像 Hugging Face transformers 库这样的工具。

他们的 Trainer 对象承担了繁重的工作,同时增加了许多额外的功能。因此,我们可以将注意力转向代码的其他部分。

所以,接下来让我们定义我们的指标。

准确率

为了评估我们模型在训练期间的表现,我们需要关注两个关键数字:损失和对我们有意义的指标。损失是像梯度下降这样的算法在反向传播期间用来改进模型性能的,而指标则是更易于理解的东西。

本着简洁的精神,我们将选择一个直接的衡量标准:模型在掩码语言建模(MLM)和下一句预测(NSP)任务上的准确率。实质上,我们关心的是两个方面:我们的模型在填充那些掩码词时的准确度,以及它在识别两句逻辑上是否相互衔接方面的表现。

然而,我们必须说这不是验证我们模型性能的最佳方式。例如,考虑以下句子:

我在数学方面是 [MASK]。

你会如何填充掩码词?我是擅长数学,还是我是糟糕的数学?这两种预测在语境上似乎都有效,但它们完全改变了句子的意思。

因此,用只有一个正确答案的验证数据集来评估模型不是最好的主意。这可能并不能真正测试模型理解语言的能力,而是记忆能力。为了解决这个问题,我们可以计算另一个指标,比如训练后模型的困惑度。

现在,让我们通过从 transformers 库中加载相应模块来评估模型的准确性:

import evaluate

metric = evaluate.load("accuracy")

def preprocess_logits_for_metrics(logits, labels):
    mlm_logits = logits[0]
    nsp_logits = logits[1]
    return mlm_logits.argmax(-1), nsp_logits.argmax(-1)

def compute_metrics(eval_preds):
    preds, labels = eval_preds

    mlm_preds = preds[0]
    nsp_preds = preds[1]

    mlm_labels = labels[0]
    nsp_labels = labels[1]

    mask = mlm_labels != -100
    mlm_labels = mlm_labels[mask]
    mlm_preds = mlm_preds[mask]

    mlm_accuracy =  metric.compute(
        predictions=mlm_preds, references=mlm_labels)["accuracy"]
    nsp_accuracy = metric.compute(
        predictions=nsp_preds, references=nsp_labels)["accuracy"]

    return {"Masked ML Accuracy": mlm_accuracy, "NSP Accuracy": nsp_accuracy}

训练

假设你在整个教程系列中一直跟随我,现在你正处于一个关键时刻,你已经准备好了数据集。数据科学家中有一句常见的说法:一旦你的数据集为模型雕琢完成,你的大部分工作就差不多完成了。由于深度学习框架的进步,接下来的步骤往往感觉像是简单的模板代码。

所以,带着期待,让我们开始实例化模型吧。

from transformers import BertForPreTraining, BertConfig

model_config = BertConfig()
model = BertForPreTraining(model_config)

现在迎来了高潮:启动训练脚本。首先,我们将定义训练参数,然后,在一切准备就绪后,我们将启动训练循环:

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    "bert-pretrained-wikitext-2-raw-v1",
    logging_first_step=True,
    evaluation_strategy="epoch",
    learning_rate=5e-5,
    weight_decay=0.01,
    push_to_hub=True,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=32,
    fp16=True,
    save_strategy="epoch",
    num_train_epochs=20
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=nsp_train_dataset,
    eval_dataset=nsp_validation_dataset,
    data_collator=data_collator,
    compute_metrics=compute_metrics,
    preprocess_logits_for_metrics=preprocess_logits_for_metrics
)

两小时和 20 个周期后,在有限的 Colab Notebook 环境中,我在 NSP 任务上达到了 80%的准确率,在 Masked ML 任务上达到了 15%的准确率。对于一个从零开始训练的模型来说,这些数字还不错。

训练结果 — 作者提供的图片

然而,值得注意的是,在大多数实际场景中,你的起点并不是从零开始。通常,你会从一个预训练的模型开始,这往往能带来更好的结果。

同时,还有大量的空间进行实验和提升性能。调整训练过程中的超参数,特别是学习率,可以显著提升你的结果,即使你是从头开始构建模型。

结论

我们已经达到了从零开始训练 BERT 模型的最终目的地。在这段旅程中,我们了解了为什么 BERT 被认为是 NLP 领域的开创性模型,并深入探讨了分词器的复杂性,不仅理解了它们的机制,还掌握了训练我们自己分词器的技巧。

接下来,我们的旅程带我们经历了为两个并行运行的任务——NSP 和 Masked ML 任务——准备数据集的细致过程。最终,我们来到了启动训练脚本的关键步骤,使用了transformers库。

这是一段漫长但充实的旅程,我希望你也喜欢它。你应该准备好将这些知识应用到自己的活动中,并扩展到更多领域。下次见,祝编程愉快!

关于作者

我的名字是 Dimitris Poulopoulos,我是一个为 HPE工作的机器学习工程师。我为欧洲委员会、国际货币基金组织、欧洲中央银行、宜家、Roblox 等主要客户设计和实施了 AI 和软件解决方案。

如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据运维的文章,可以在 MediumLinkedIn 或在 Twitter 上的 @james2pl 关注我。

所表达的意见仅代表我个人,并不代表我的雇主的观点或意见。

从交叉职能机器学习项目中获得的 20 个经验教训

原文:towardsdatascience.com/20-learnings-from-delivering-cross-functional-machine-learning-projects-ad32371d09c1?source=collection_archive---------10-----------------------#2023-08-07

如何在复杂的跨团队项目中导航,推动及时解决方案,同时不破坏关系

Aliaksei MikhailiukTowards Data Science Aliaksei Mikhailiuk

·

关注 发表在 Towards Data Science ·6 分钟阅读·2023 年 8 月 7 日

--

图片由作者使用 SDXL 1.0 生成。

无论你是在领导还是协助跨团队项目,都会感到压力。虽然推动截止日期和管理复杂情况的压力常常有“什么没有杀死你就让你更强”这种说法,但这些情况是可以避免的,并且学习可以在一起完成大项目的有趣环境中发生。

跨团队的工程项目具有挑战性,而机器学习项目则增加了另一层复杂性,合作者通常需要帮助理解技术背后的知识、机器学习在生产中的影响和风险。

在与美国和亚洲公司及创业公司合作的过程中,这些公司具有非常不同的工作伦理、标准和期望,我很幸运地领导并参与了多个大规模的跨团队项目,交付了面向客户的机器学习解决方案。

在这里,我总结了在规划、开发过程、管理文档和沟通方面的经验,以及我从跨团队合作中提炼出的规则。

## 成为一名高效的机器学习团队负责人

管理沟通、基础设施和文档

[towardsdatascience.com

规划、截止日期和里程碑

  • 当将团队聚集在一起处理某个特性时,准备一页纸总结该特性、说明其必要性的充分理由,并通过来自 A/B 测试/使用分析的数据或上级的请求来证明其优先级。

  • 在接触团队时,要记住每个团队都有自己要实现的功能堆栈;如果他们承诺处理某项任务,就需要重新调整任务的优先级。

  • 在规划新特性和定义工作范围时,始终首先联系经理。他们了解资源状态,并可以指引合适的人选。始终将经理纳入规划和沟通中;他们可能知道其他项目的相似性。

  • 降低成本和提高货币化是团队总是愿意投入资源和时间的两个话题。如果你能将你的要求以这种格式呈现,达成一致将更容易。

  • 不同的文化和国家对权力在团队成员之间的分配有着截然不同的看法。虽然只是作为指导,霍夫斯泰德的权力距离指数在多个场合上帮助我们更快速、顺利地达成优先级对齐。

开发过程

  • 确定日期 — 制定明确且有依据的截止日期。请团队制定工作范围并提出一个日期。截止日期本身并不总是有帮助,但它们作为团队成员在规划工作时的心理锚点,使工作更加集中。

  • 确保您遵循适合当前时刻的开发模式和标准。当为了紧迫的截止日期而推动某些特性时,可能会带来短期思维的成本。确保记录下妥协的地方,并在截止日期后分配时间来完善项目。

  • 当面临重大截止日期时,团队会变得更容易管理,人员通常愿意付出额外的努力;在节奏较慢的时期不要总是指望这一点。

  • 在计划休假时,安排好交接工作,并在您离开时设定清晰的里程碑。分配职责,并指出您回来后需要处理的范围。

  • 设定明确的基准,并报告与基准的结果——这样团队更容易看到进展并提供反馈。

文档

  • 在为外部人员准备文档时,确保进行团队级审查,然后请一个不同团队的可信成员阅读文档,以确保对外部人员而言文档清晰,然后再与其他外部成员和高层管理人员分享。

  • 记录会议内容,发送跟进邮件、总结并列出负责人员,确保每个人达成一致。

  • 在与共同作者分享文档之前,总是要先询问,尤其是当文档尚未完成时。

  • 无论是外部还是内部文档,所有文档必须包含 POC、创建日期和最后一次重要更改。

  • 使用共享文档进行讨论和对齐。口头协议往往会被误解和遗忘。拥有书面文本来对齐提供了一个起点和一个建设性讨论的锚点。如果您是文档的所有者,请允许第三方留下评论。

沟通

  • 确保提供定期报告——这些不仅有助于早期标记问题,还能让您保持责任感。如果问题被足够早地标记出来,管理层将有时间提供资源以协助交付。决策过程中的可见性和透明度也使得批评变得更加容易,从而帮助形成更好的解决方案。

  • 高层管理人员没有时间深入了解;但他们会对当前的方向和优先级有更好的了解。当向高层管理人员提供信息或潜在的开发选项时,不要过多深入细节。保持报告简洁明了,多用视觉材料,高层概述和现实世界的类比,早期标记问题,这些都是让管理层满意的要素。

  • 在向高层管理人员沟通不同选项时,不要仅仅描述几个选项并要求他们选择一个。最好有一个预设的决定,例如,我们想选择 X,因为它具备 AB 和 C;然而,Y 有 DEF,但从长远来看,这一点不重要。您是否同意我们继续进行 A?

  • 沟通时,与其不抄送重要人员,不如多抄送一个不太相关的人。问问自己:“如果我不联系他们,他们会生气吗?”——项目可能因为有人生气而脱轨。

  • 与团队成员以及跨团队保持良好关系是至关重要的。保持这种关系的最佳方式是保持关注,并开放接受反馈。主动请求反馈,尤其是负面的 — 发送消息表明你愿意沟通 — 建立信任。潜意识中,人们会注意到问题并在可能爆发之前告诉你。

三种每位博士生免费获得的软技能

这是我在攻读机器学习博士学位期间学到的关于研究、沟通和团队合作的全面技巧和窍门列表…

towardsdatascience.com

摘要

客户和高层管理者不需要问题。他们需要解决方案。这通常是员工和企业主之间的区别 — 员工擅长指出问题,而企业主推动解决方案。

拥有主人心态有助于将人们聚集在一起,建立更具鼓励性的环境。毕竟,当前方有明确的道路时,工作起来要容易得多,而领导者的任务就是定义这条道路。

然而,当争论出现时,当不确定正确决策时,问自己,“对团队和项目来说,什么是最好的?” — 这总是有助于做出正确的决策。

建立项目管理技能的好书包括:

如果你喜欢这篇文章,请与朋友分享!要阅读更多关于机器学习和图像处理的主题,请按下订阅按钮!

喜欢这位作者吗?保持联系!

我是否遗漏了什么?请随时留言、评论或直接在 LinkedInTwitter 上给我发消息!

## 如何在机器学习面试中脱颖而出

机器学习面试的准备指南和资源。

towardsdatascience.com ## 我希望在攻读机器学习博士学位前掌握的九种工具

无论你是创建初创公司还是取得科学突破,这些工具都能提升你的机器学习流程。

towardsdatascience.com

本博客中的观点仅代表我个人,不代表 Snap 的观点。

寻找机器学习团队候选人时应关注的 20 种软技能

原文:towardsdatascience.com/20-soft-skills-to-look-for-in-candidates-for-your-machine-learning-team-23dfccf996ee?source=collection_archive---------12-----------------------#2023-02-16

Lydia NemecTowards Data Science Lydia Nemec

·

关注 发表在 Towards Data Science ·9 分钟阅读·2023 年 2 月 16 日

--

尽管教育、技能和经验提供了技术基础,并且对一个有能力的机器学习(ML)团队至关重要,但只有当结合了正确的软技能时,团队才会变得强大和成功。

图像阅读机器人 ML 生成自 creator.nightcafe.studio

教育、技能和经验是一个合格的机器学习专家的重要特质。拥有数学、计算机科学、自然科学和统计学的坚实教育背景,为理解驱动机器学习模型的基础理论和算法提供了强有力的基础。此外,通过动手实践各种编程语言、库和工具来获得实际技能,对于实现和部署成功的机器学习解决方案至关重要。[R1, R2]

然而,正确的软技能组合可以将一支有能力的机器学习专家团队转变为成功的团队。这些技能补充了技术技能。[R3] 软技能是个人属性和能力。在机器学习领域,软技能尤为重要,因为它们使专家能够有效地与利益相关者、客户和其他团队成员合作,以确保解决了正确的问题,开发和部署了正确的解决方案。

准备好提升你的团队吧!在接下来的 20 点中,我将分享我对这些能力的个人见解。所以,让我们深入探讨,发现如何发掘我们自己和团队的最佳表现!

阅读机器人的图像由 openAI 的 dall-e 生成

  1. 有目的地工作:明确活动的目的很重要。对目标有清晰的认识可以使工作保持一致。在机器学习项目中,很容易从一个有趣的分析跳到另一个。风险在于,你可能会在某个地方结束,却没有找到解决实际问题的方案。有目的地工作可以帮助开发有意义的解决方案。[R4]

  2. 有纪律和专注地工作:自律工作是成功的主要因素。自律使你能够纠正过去的行为而不重复错误。它使得培养良好的工作习惯并保持足够的质量标准成为可能。[R5] 在现代工作环境中,干扰无处不在。专注于特定的目标并保持全神贯注,可以在有限的时间内获得高质量的结果。[R6]

  3. 智力严谨性和灵活性:在开发机器学习解决方案时,明确基本假设、应用逻辑严谨的推理并得出结论很重要。同时,保持心理灵活性以重新审视假设和结论也同样重要,如果结果看起来可疑的话。根据我的经验,机器学习生成的结果如果看起来好得不真实,往往并非如此。

  4. 时间管理:找到专注工作的时间是困难的。团队可以引入专门的时间用于集中工作,但这只能在每个团队成员充分利用可用时间时才能实现集中工作。时间管理的另一个方面是能够持续工作,设定明确的优先级,以管理项目和团队之间的相互依赖,并按时完成任务。记住:你永远无法完成所有想做的事情,所以一定要优先排序![R7, R8]

  5. 跨文化能力:ML 专家可能来自不同的教育背景、国籍和年龄群体。每个团队成员都能够处理文化差异非常重要。例如,在我们的团队中,主要使用英语,但对我们大多数人来说,英语是外语。重要的是要仔细倾听并提出问题,直到双方都确认达成了共同的理解。

  6. 终身学习的态度:ML 领域广泛,团队的要求和期望也多种多样。对领域特定主题以及与之相关的内容有深厚的学习态度,加上对其他解决有趣问题领域的广泛兴趣,可以成为解决问题的丰富灵感来源。这种基本态度可以为团队快速熟悉新项目中的主题条件奠定基础。顺便说一下,午餐讨论也会变得更加有趣![R9]

  7. 挫折容忍度:在开发 ML 解决方案时,实验和测试未知结果的新方法是日常工作的一部分。ML 专家必须承担进入未知领域所带来的风险。事情不应该出错,但往往会出错,例如数据不足(数量、信息内容、质量)、算法可能无法收敛等等。团队需要能够在问题变得困难时坚持下去,能够应对挫折并继续前进。第一次、第二次或第三次都不会总是成功——ML 是困难的,我们必须应对。

  8. 责任感:处理数据意味着处理信任。这带来了极大的责任:对数据、对所应用算法的结果以及对因数据偏差等意外结果负责。ML 团队必须认识到这一点,并且不畏惧承担这种责任。[R10]

  9. 责任感与所有权:机器学习解决方案的开发和运作是复杂的,往往意味着团队必须应对不确定性。重要的是团队中的每个人都要对各自的工作负责。具有健康所有权思维的团队通常拥有“如果我搞坏了,我就修复”的文化。然而,所有权需要问责。每个人必须对自己负责,并且必须有空间和安全感,诚实地面对结果。[R11]

  10. 工程思维:无论其准确性如何,机器学习模型如果不能部署、应用于现实数据、扩展和维护,则价值有限。成功的团队会开发出可维护、可扩展且稳健的端到端解决方案。这包括两个方面:首先是数据——数据通常是杂乱的,需要大量工作和精心处理才能揭示其有价值的信息内容。其次是软件(& 云)工程。在这里,软件工程的最佳实践适用。[R12]

  11. 分析与批判性思维:不要信任机器!毕竟,机器学习算法解决的是数值优化问题。它们将数字作为输入并输出数字。无论结果是否合理或是否解决了给定的问题,都需要由经验丰富的机器学习专家团队进行检查。

  12. 协作:信任和安全是任何真正协作的基础。每个团队成员必须对自己的能力和局限性有信任,以便在团队内建立信任并进行持续改进。在这种情况下,安全意味着:首先,在团队内部,我们尊重所有权,但积极为机器学习产品的成功做出贡献。其次,每个人都会犯错,每个团队成员都应感到安全,能够承认错误、纠正错误并从中学习。这使得每个团队成员都能成为一个强大而可靠的合作者。

  13. 基于角色的指导:基于角色的指导是一种有效促进个人和职业成长的方式。这种方法有两个方面:首先,寻找团队内部或外部的导师,以进行积极的学习、获得指导和交流意见。其次,寻找受指导者,提供指导,例如,通过代码审查、数据探索讨论或挑战机器学习解决方案。

  14. 问题解决:我们收到的每个请求都代表着一个尚未解决的问题。我们的工作是根据现有数据探讨是否存在潜在解决方案,这本质上是一个困难的挑战。为了解决这个问题,团队中的每个人都需要拥有个人的工具箱,能够处理、理清,并最终解决所给的问题。这通常涉及将先进的分析应用于复杂的数据集,开发有效的算法,并寻找创新的解决方案。这种能力为团队、公司及其客户做出了宝贵的贡献。

  15. 有效沟通:主动发言者和主动倾听者都对良好的沟通负有责任。发言者需要能够在与合作者、利益相关者或其他专家沟通时清晰地表达复杂的技术概念和结果。[R13] 在机器学习开发中,明确的资源和时间规划可能是困难的。这也是为什么它被称为数据科学。因此,协商资源和截止日期的能力是团队工作的一个重要方面。[R14] 数据往往无法满足利益相关者的期望,这可能会导致摩擦。同样重要的是,具有文化和教育多样性、不同能力以及有时目标冲突的团队的性质可能会导致真正困难的对话。处理这些对话需要耐心、理解的开放态度以及适量的同理心。[R15] 我们在这里列出了成功的机器学习专家所需的三大主要沟通方面。

  16. 应对模糊性:模糊性来源于竞争的想法、不明确的结果愿景、相互冲突的利益和有限的信息。根据可用信息推理和调整计划的能力对于得出结论和确定最佳下一步至关重要。[R16]

  17. 战略思维:能够设想整体解决方案及其对团队、组织、客户和社会的影响是机器学习专家的重要技能。这种能力结合对机器学习产品开发中相互关联复杂性的深刻理解,使他们能够专注于大局,预见障碍,并提前考虑几步。因此,专家能够清晰地与利益相关者和客户沟通,并优先关注成功的关键领域。

  18. 组织能力:机器学习产品开发中的典型挑战包括复杂的相互依赖、不可预见的障碍和不完整的信息,例如关于现有数据是否足以解决问题的不确定性。计划可计划的事项、处理意外情况、设定优先级、分配合适的资源以及有效地交付结果是关键技能。[R17]

  19. 商业头脑:这是识别和优先考虑那些对公司经济成功产生积极影响的正确决策的能力。一个重要的前提是理解商业问题和客户需求。然后,挑战在于有效且技术上高效地实现这些需求。技术解决方案的表现与机器学习模型的质量相关,同样重要的是其成本效益实施。强大的商业头脑使机器学习专家能够为公司的利润做出贡献。

  20. 以客户为中心给客户他需要的,而不是他要求的。 这是将知识和能力用于帮助客户的关键技能。客户应该对机器学习产品和开发团队充满信心。客户中心意味着理解客户的需求并开发合适的解决方案。[R18]

我们的计划是通过新产品引领公众,而不是询问他们想要什么样的产品。公众不知道什么是可能的,但我们知道。” 赤尾守田(1921–1999)是日本商人,索尼的联合创始人、首席执行官和董事长。

推荐阅读列表

[R1] 安基塔·尼贾姆;数据科学家必备的 20 种技能 blog.insaid.co

[R2] 西德赫什·辛德;在这里发现顶级 11 种数据科学技能 emeritus.org/blog(2022 年 12 月 8 日)

[R3] 安迪·麦克唐纳;成功数据科学家必须具备的 5 种软技能 towardsdatascience.com(2022 年 10 月 28 日)

[R4] 西蒙·西内克;《从为什么开始:伟大的领导者如何激励每个人采取行动》(2011 年 12 月 27 日)

[R5] 詹姆斯·克利尔;《原子习惯:一种简单且经过验证的方法来建立好习惯并打破坏习惯》(2018 年 10 月 16 日)

[R6] 丹尼尔·戈尔曼;《专注:卓越的隐藏驱动力》(2013 年 10 月 8 日)

[R7] 奥利弗·伯克曼;《四千周:凡人的时间管理》(2021 年 8 月 10 日)

[R8] 米哈伊·契克森米哈伊;《心流:最佳体验的心理学》(2008 年 7 月 1 日)

[R9] 大卫·爱泼斯坦;《范围:全才如何在专业化世界中获胜》(2020 年 10 月 1 日)

[R10] 凯特·克劳福德;《人工智能地图:权力、政治与人工智能的全球成本》(2022 年 8 月 16 日)

[R11] 乔科·威林克,莱夫·巴宾;《极端责任:美国海豹突击队如何领导和胜利》(2017 年 11 月 21 日)

[R12] 罗伯特·C·马丁;《清洁代码:敏捷软件工艺手册》(2008 年 8 月 1 日)

[R13] 南希·杜阿特;《幻灯片学:创造伟大演示的艺术与科学》(2008 年 8 月 7 日)

[R14] 克里斯·沃斯;《从不妥协:谈判如同你的生命依赖于此》(2017 年 3 月 23 日)

[R15] 道格拉斯·斯通,布鲁斯·帕顿,希拉·欣;《困难对话:如何讨论重要的事》(2010 年 11 月 2 日)

[R16] 卡琳·艾尔斯特,塔玛拉·克里斯滕森;《在模糊中引领:如何将不确定性转变为可能性》(2022 年 10 月 12 日)

[R17] 斯科特·伯肯;《实现目标:掌握项目管理》(2008 年 4 月 1 日)

[R18] 保罗·洛普辛斯基 《为什么索尼的联合创始人赤尾守田是我心目中的产品经理英雄,以及他为什么也应该成为你的英雄》(2017 年 3 月 7 日)

2023 年的预测:AI 研究的下一步是什么?

原文:towardsdatascience.com/2023-predictions-whats-next-for-ai-research-de5b035bc448?source=collection_archive---------2-----------------------#2023-01-08

对于过去的一年感到兴奋,我们展望 2023 年,想知道它会是什么样子。

Tal RosenweinTowards Data Science Tal Rosenwein

·

跟随 发表在Towards Data Science ·12 分钟阅读·2023 年 1 月 8 日

--

这篇博客文章由Guy Eyal,Gong 的 NLP 团队负责人,共同撰写。

简而言之:

2022 年,大型模型在各类任务和领域中取得了最先进的成果。在自然语言处理(NLP)方面,当模型被训练以对齐用户意图和人类偏好时,实现了显著突破,从而提高了生成质量。展望 2023 年,我们可以期待看到改进对齐过程的新方法(例如带有 AI 反馈的强化学习)、理解对齐效果的自动化指标的开发,以及个性化对齐模型的出现,即使是在线方式。可能还会关注解决事实性问题以及开发开源工具和专门的计算资源,以支持对齐模型的工业规模发展。除了 NLP,还可能在音频处理、计算机视觉和机器人等其他模态中取得进展,并发展多模态模型。

2022 年 AI 研究进展:年度回顾

2022 年对人工智能/机器学习来说是优秀的一年,发布了大量语言模型(LLMs)并在各种基准测试中取得了最先进的结果。这些 LLMs 通过少量学习展示了其优越的表现,超越了在相同任务上进行了微调的小型模型[1–3]。这有可能减少对专业领域数据集的需求。诸如思维链[4]和自洽性[5]等技术也帮助提升了 LLMs 的推理能力,在推理基准测试中取得了显著提升。

对话系统也取得了显著进展, resulting in more helpful, safe, and faithful models that could stay up-to-date through fine-tuning on annotated data and the use of retrieval from external knowledge sources [6–7]。

在自动语音识别(ASR)中,使用编码器-解码器变换器架构实现了模型规模的更高效扩展,在多个 ASR 基准测试中减少了 50%的词错误率而无需领域适配[8]。

扩散模型[9–10]在大型图像数据集上进行训练,在计算机视觉领域取得了显著进展,并引发了 AI 艺术的新趋势。此外,我们还看到了利用预训练大语言模型(LLMs)来提升从视觉到机器人等任务表现的多模态模型的初步出现[9–12]。

最终,ChatGPT [13] 的发布让用户瞥见了在各个领域和领域中与 AI 助手合作的未来。

图片来源:Moritz KnöringerUnsplash

2023 年预测:对齐年的到来

对过去一年充满期待,我们展望 2023 年,想知道它会是什么样子。以下是我们的想法:

最近几个月,人工反馈的强化学习(RLHF),一种使模型与用户意图和人类偏好对齐的监督方法,变得越来越受欢迎 [15]。这种监督方法在生成质量方面表现出良好的结果,比较 vanilla GPT3 [16] 和 ChatGPT 的输出可以看出。RLHF 的效果如此显著,以至于经过指令调优的模型超越了一个大于 100 倍的模型。

图片来源 — InstructGPT [15]

随着今年趋势的进一步发展,我们和许多人一样,预计对齐将继续是一个重要因素。然而,我们预测将有更多的核心功能进行积极研究,这些功能目前在大多数模型中缺失,因此限制了它们在许多领域的适用性。

人工智能反馈的强化学习(RLAIF)

目前,RLHF 需要人工策划的数据。虽然与预训练数据规模相比较小,但仍需要大量且昂贵的人力。例如,OpenAI 使用了 40 名注释员编写和标注了 64K 个样本用于指令调优 [15]。我们认为,今年将被利用的一个有趣且令人兴奋的替代方案是使用不同的 LLM 作为指导者和标注者——人工智能反馈的强化学习(RLAIF)。RLAIF 将能够降低成本并快速扩展对齐过程,因为机器将完成所有的端到端工作。Anthropic [17] 最近的一项有趣工作表明,通过良好的提示,可以引导语言模型对有害输出进行分类。这些分类结果反过来用于训练 RLHF 所需的奖励模型。

对齐的度量标准

我们假设将会开发许多方法来实现模型输出与用户意图之间更好的对齐。这反过来将进一步提高生成质量。

为了理解哪种方法更优,自动度量标准应与当前的人类评估方法一起开发。这是 NLP 中的一个长期问题,因为以前的度量标准未能与人类注释对齐。

最近,引入了两种有前景的方法:MAUVE [18],它通过使用发散前沿比较人类生成和模型生成输出的分布,以及模型编写的评估 [19],它利用其他语言模型来评估生成输出的质量。在这些领域的进一步研究可能是 2023 年的一个有价值的方向。

个性化对齐模型

期望一个模型与整个社会对齐是没有意义的,因为我们彼此之间并不对齐。因此,我们预计会看到许多不同的模型与不同的用途和用户对齐。我们称之为个性化对齐模型。

我们将看到各种公司根据自己的需求调整模型,而大型公司则将多个模型与不同的用户需求对齐。这将大大改善最终用户在使用 LLMs 时的体验,包括个人助理、互联网搜索、文本编辑等。

开源和专业计算

要实现行业规模的个性化对齐模型,需要公开可用的两个组件:可对齐的模型和计算资源,这两个组件今天尚未完全存在。

待对齐的模型和 开源模型作为对齐的候选者必须被开发,因为当前的模型,如 Meta 的 OPT [20],不够充分,因为它们与付费 API 不匹配。或者,我们将看到模型对齐的付费 API:由 Google / OpenAI / Cohere / AI21 提供的非公开模型将提供全面的消费者服务选项,并将作为一种有效的商业模式。

计算资源: 尽管对齐比预训练便宜得多,但仍然需要非常专业的计算资源。因此,我们预测将出现争相生成这种公共可用基础设施的情况,可能是在云端。

处理事实问题

LLMs 生成的输出的明显流畅性可能会导致个人将模型视为事实准确且自信。然而,LLMs 的一个已知限制是它们倾向于生成虚假的内容,这仍需要通过对齐来解决。因此,我们看到两个重要的研究方向将在今年蓬勃发展:文本输出的源(引用)和模型的置信度。

当前输出的源可以通过多种方式实现。一个有趣的方向是将 LLMs 与文本检索机制连接,这将有助于将输出与已知来源进行关联[21]。这也可能帮助模型保持相关性,即使它们的训练过程在过去某个时间点停止了。另一个最近提出的想法是在后处理过程中通过搜索最接近输出的文档[22]来实现。虽然后者不能解决虚假信息,但可以使用户更容易验证结果。

在不同领域(例如 ASR [23])的近期研究中训练了具有两个输出的模型:令牌预测和每个令牌的置信度评分。使用类似的方法,同时将置信度评分扩展到整个输出,将帮助用户谨慎对待结果。

在线对齐

随着人们的兴趣、信仰、工作和家庭状况的变化,他们的个人助理也应该适应这些变化是有意义的。我们预测的一个非常有前景的研究方向是在线对齐。用户将能够在部署后继续个性化他们的模型。对齐过程将通过向模型提供反馈的在线学习算法不断更新[24]。

其他模态的情况如何?

我们期望在音频和语音识别领域看到显著的改进。我们假设 Whisper [8] 将能够利用未标注的数据(如 Wav2Vec 2.0 [25] / HuBERT [26]),这将显著提高在挑战性声学场景中的表现。

SpeechT5 [27] 是一个早期的探索者,因此我们假设类似 T0 的模型 [28] 将在规模上(包括训练数据和模型大小)进行训练,从而改进音频嵌入。这将实现一个统一的语音增强、分离和转录系统。在长期来看,我们期待听觉模型能够回答类似于自然语言处理(NLP)模型的问题。这些听觉模型的基础上下文将是一个音频片段,该片段将作为查询的上下文,而无需隐式转录。

多模态模型

明年的一个重要范式将是大型多模态模型。它们会是什么样子?我们猜测它们可能会非常类似于语言模型。我们所指的是,用户将以某种模态提示模型,模型将能够以不同的模态生成其输出(如 Unified-IO [29])。

尽管非常令人兴奋,扩散模型 [9] 目前无法对图像进行分类。这可以通过输出类似于我们今天在分类任务中使用的 LLM 的文本来轻松解决。类似地,这些模型将能够通过良好的提示来转录、生成和增强音频和视频。

那么多模态模型的对齐情况如何?这是远未来的事!或者用我们当前领域的节奏来说——几个月后。

结束语

本文展示了我们对 2023 年 AI 研究中所需进展的预测。大型模型可以执行广泛的学术任务,正如它们在标准基准测试中的出色表现所示。然而,这些模型在现实场景中仍会遇到令人尴尬的失败(不真实、有毒,或对用户没有帮助)。我们相信,通过将模型与用户需求对齐并保持其最新状态,可以解决许多这些问题。为此,我们关注了对齐过程的可扩展性和适应性。如果我们的假设正确,生成语言模型领域将很快发生重大变化。这些模型的潜在用途广泛,从编辑工具到可以在法律、会计和工程等行业中自动化手工劳动的领域特定 AI 助手。将上述声明与计算(GPT-4)的预测进展结合起来,并采用同样的方法应用于视觉和音频处理领域,预示着另一个令人兴奋的年头。

感谢阅读!!如果您对这一 2023 年的预测有任何想法,我们热烈欢迎在评论中分享。

参考文献:

[1] Chowdhery, A., Narang, S., Devlin, J., Bosma, M., Mishra, G., Roberts, A., Barham, P., Chung, H. W., Sutton, C., Gehrmann, S., Schuh, P., Shi, K., Tsvyashchenko, S., Maynez, J., Rao, A., Barnes, P., Tay, Y., Shazeer, N., Prabhakaran, V., . . . Fiedel, N. (2022). PaLM: 通过路径扩展语言建模。arXivdoi.org/10.48550/arXiv.2204.02311

[2] Scao, T. L., Fan, A., Akiki, C., Pavlick, E., Ilić, S., Hesslow, D., Castagné, R., Luccioni, A. S., Yvon, F., Gallé, M., Tow, J., Rush, A. M., Biderman, S., Webson, A., Ammanamanchi, P. S., Wang, T., Sagot, B., Muennighoff, N., . . . Wolf, T. (2022). BLOOM: 一种 176B 参数的开放访问多语言模型。arXivdoi.org/10.48550/arXiv.2211.05100

[3] Zhang, S., Roller, S., Goyal, N., Artetxe, M., Chen, M., Chen, S., Dewan, C., Diab, M., Li, X., Lin, X. V., Mihaylov, T., Ott, M., Shleifer, S., Shuster, K., Simig, D., Koura, P. S., Sridhar, A., Wang, T., & Zettlemoyer, L. (2022). OPT: 开放预训练变换器语言模型。arXivdoi.org/10.48550/arXiv.2205.01068

[4] Wei, J., Wang, X., Schuurmans, D., Bosma, M., Ichter, B., Xia, F., Chi, E., Le, Q., & Zhou, D. (2022). 思维链提示在大型语言模型中引发推理。arXivdoi.org/10.48550/arXiv.2201.11903

[5] Wang, X., Wei, J., Schuurmans, D., Le, Q., Chi, E., Narang, S., Chowdhery, A., & Zhou, D. (2022). 自我一致性改善语言模型中的思维链推理。arXivdoi.org/10.48550/arXiv.2203.11171

[6] Thoppilan, R., De Freitas, D., Hall, J., Shazeer, N., Kulshreshtha, A., Cheng, H., Jin, A., Bos, T., Baker, L., Du, Y., Li, Y., Lee, H., Zheng, H. S., Ghafouri, A., Menegali, M., Huang, Y., Krikun, M., Lepikhin, D., Qin, J., . . . Le, Q. (2022). LaMDA: 对话应用的语言模型。arXivdoi.org/10.48550/arXiv.2201.08239

[7] Shuster, K., Xu, J., Komeili, M., Ju, D., Smith, E. M., Roller, S., Ung, M., Chen, M., Arora, K., Lane, J., Behrooz, M., Ngan, W., Poff, S., Goyal, N., Szlam, A., Boureau, Y., Kambadur, M., & Weston, J. (2022). BlenderBot 3: 一个持续学习以负责任地互动的部署对话代理。arXivdoi.org/10.48550/arXiv.2208.03188

[8] Radford, A., Kim, JW, Xu, T, Brockman, G., McLeavey, C., Sutskever, I. (2022). 通过大规模弱监督实现稳健的语音识别。OpenAI。cdn.openai.com/papers/whisper.pdf

[9] Rombach, R., Blattmann, A., Lorenz, D., Esser, P., & Ommer, B. (2021). 《使用潜在扩散模型的高分辨率图像合成》。arXivdoi.org/10.48550/arXiv.2112.10752

[10] Ramesh, A., Dhariwal, P., Nichol, A., Chu, C., & Chen, M. (2022). 《基于 CLIP 潜变量的分层文本条件图像生成》。arXivdoi.org/10.48550/arXiv.2204.06125

[11] Tang, Z., Yang, Z., Wang, G., Fang, Y., Liu, Y., Zhu, C., Zeng, M., Zhang, C., & Bansal, M. (2022). 《统一视觉、文本和布局以实现通用文档处理》。arXivdoi.org/10.48550/arXiv.2212.02623

[12] Wang, P., Yang, A., Men, R., Lin, J., Bai, S., Li, Z., Ma, J., Zhou, C., Zhou, J., & Yang, H. (2022). 《OFA:通过简单的序列到序列学习框架统一架构、任务和模态》。arXivdoi.org/10.48550/arXiv.2202.03052

[13] Ahn, M., Brohan, A., Brown, N., Chebotar, Y., Cortes, O., David, B., Finn, C., Fu, C., Gopalakrishnan, K., Hausman, K., Herzog, A., Ho, D., Hsu, J., Ibarz, J., Ichter, B., Irpan, A., Jang, E., Ruano, R. J., Jeffrey, K., . . . Zeng, A. (2022). 《做我能做的,不是我说的:将语言建立在机器人能力上》。arXivdoi.org/10.48550/arXiv.2204.01691

[14] chat.openai.com/chat

[15] Ouyang, L., Wu, J., Jiang, X., Almeida, D., Wainwright, C. L., Mishkin, P., Zhang, C., Agarwal, S., Slama, K., Ray, A., Schulman, J., Hilton, J., Kelton, F., Miller, L., Simens, M., Askell, A., Welinder, P., Christiano, P., Leike, J., . . . Lowe, R. (2022). 《训练语言模型以遵循人类反馈的指令》。arXivdoi.org/10.48550/arXiv.2203.02155

[16] Brown, T. B., Mann, B., Ryder, N., Subbiah, M., Kaplan, J., Dhariwal, P., Neelakantan, A., Shyam, P., Sastry, G., Askell, A., Agarwal, S., Krueger, G., Henighan, T., Child, R., Ramesh, A., Ziegler, D. M., Wu, J., Winter, C., Hesse, C., . . . Amodei, D. (2020). 《语言模型是少样本学习者》。arXivdoi.org/10.48550/arXiv.2005.14165

[17] Bai, Y., Kadavath, S., Kundu, S., Askell, A., Kernion, J., Jones, A., Chen, A., Goldie, A., Mirhoseini, A., McKinnon, C., Chen, C., Olsson, C., Olah, C., Hernandez, D., Drain, D., Ganguli, D., Li, D., Perez, E., Kerr, J., . . . Kaplan, J. (2022). 《宪法 AI:来自 AI 反馈的无害性》。arXivdoi.org/10.48550/arXiv.2212.08073

[18] Pillutla, K., Swayamdipta, S., Zellers, R., Thickstun, J., Welleck, S., Choi, Y., & Harchaoui, Z. (2021). MAUVE: 使用发散边界测量神经文本与人类文本之间的差距。arXivdoi.org/10.48550/arXiv.2102.01454

[19] Perez, E., Ringer, S., Lukošiūtė, K., Nguyen, K., Chen, E., Heiner, S., Pettit, C., Olsson, C., Kundu, S., Kadavath, S., Jones, A., Chen, A., Mann, B., Israel, B., Seethor, B., McKinnon, C., Olah, C., Yan, D., Amodei, D., . . . Kaplan, J. (2022). 通过模型编写的评估发现语言模型行为。arXivdoi.org/10.48550/arXiv.2212.09251

[20] Iyer, S., Lin, X. V., Pasunuru, R., Mihaylov, T., Simig, D., Yu, P., Shuster, K., Wang, T., Liu, Q., Koura, P. S., Li, X., Pereyra, G., Wang, J., Dewan, C., Celikyilmaz, A., Zettlemoyer, L., & Stoyanov, V. (2022). OPT-IML: 通过泛化视角扩展语言模型指令元学习。arXivdoi.org/10.48550/arXiv.2212.12017

[21] He, H., Zhang, H., & Roth, D. (2022). 通过检索重新思考:忠实的大型语言模型推理。arXivdoi.org/10.48550/arXiv.2301.00303

[22] Bohnet, B., Tran, V. Q., Verga, P., Aharoni, R., Andor, D., Soares, L. B., Eisenstein, J., Ganchev, K., Herzig, J., Hui, K., Kwiatkowski, T., Ma, J., Ni, J., Schuster, T., Cohen, W. W., Collins, M., Das, D., Metzler, D., Petrov, S., . . . Webster, K. (2022). 属性问答:属性大型语言模型的评估与建模。arXivdoi.org/10.48550/arXiv.2212.08037

[23] Gekhman, Z., Zverinski, D., Mallinson, J., & Beryozkin, G. (2022). RED-ACE: 使用置信嵌入进行鲁棒的错误检测。arXivdoi.org/10.48550/arXiv.2203.07172

[24] Bai, Y., Jones, A., Ndousse, K., Askell, A., Chen, A., DasSarma, N., Drain, D., Fort, S., Ganguli, D., Henighan, T., Joseph, N., Kadavath, S., Kernion, J., Conerly, T., Elhage, N., Hernandez, D., Hume, T., Johnston, S., Kravec, S., . . . Kaplan, J. (2022). 通过人类反馈的强化学习训练有用且无害的助手。arXivdoi.org/10.48550/arXiv.2204.05862

[25] Baevski, A., Zhou, H., Mohamed, A., & Auli, M. (2020). wav2vec 2.0:一种自监督学习语音表示的框架。arXivdoi.org/10.48550/arXiv.2006.11477

[26] Hsu, W., Bolte, B., Tsai, Y., Lakhotia, K., Salakhutdinov, R., & Mohamed, A. (2021). HuBERT:通过掩蔽预测隐藏单元进行自监督语音表示学习。arXivdoi.org/10.48550/arXiv.2106.07447

[27] Ao, J., Wang, R., Zhou, L., Wang, C., Ren, S., Wu, Y., Liu, S., Ko, T., Li, Q., Zhang, Y., Wei, Z., Qian, Y., Li, J., & Wei, F. (2021). SpeechT5: 统一模态编码器-解码器预训练用于口语语言处理。arXivdoi.org/10.48550/arXiv.2110.07205

[28] Sanh, V., Webson, A., Raffel, C., Bach, S. H., Sutawika, L., Alyafeai, Z., Chaffin, A., Stiegler, A., Scao, T. L., Raja, A., Dey, M., Bari, M. S., Xu, C., Thakker, U., Sharma, S. S., Szczechla, E., Kim, T., Chhablani, G., Nayak, N., . . . Rush, A. M. (2021). 多任务提示训练实现零样本任务泛化。arXivdoi.org/10.48550/arXiv.2110.08207

[29] Lu, J., Clark, C., Zellers, R., Mottaghi, R., & Kembhavi, A. (2022). Unified-IO: 统一模型用于视觉、语言和多模态任务。arXivdoi.org/10.48550/arXiv.2206.08916

[30] Mildenhall, B., Srinivasan, P. P., Tancik, M., Barron, J. T., Ramamoorthi, R., & Ng, R. (2020). NeRF: 将场景表示为神经辐射场用于视图合成。arXivdoi.org/10.48550/arXiv.2003.08934

[31] Chen, C., Gao, R., Calamia, P., & Grauman, K. (2022). 视觉声学匹配。arXivdoi.org/10.48550/arXiv.2202.06875

[32] Zhu, Z., Peng, S., Larsson, V., Xu, W., Bao, H., Cui, Z., Oswald, M. R., & Pollefeys, M. (2021). NICE-SLAM: 神经隐式可扩展编码用于 SLAM。arXivdoi.org/10.48550/arXiv.2112.12130

使数据团队成功的决策

点击这里查看原文

TDS 编辑Towards Data Science TDS 编辑

·

关注 发表在 Towards Data Science · 发送为 通讯 · 3 分钟阅读·2023 年 7 月 13 日

--

现实是复杂的:人们和组织的行为往往出乎意料,外部事件可能不断干扰我们最精密的工作流程。对于数据团队来说,可能会倾向于用最新的光鲜工具或炫目的新员工来应对这些挑战时刻。这样做可能会有效——但只是到一定程度为止。

面对变化、令人失望的结果或偶尔的公司混乱,帮助团队培养韧性的方法很少是这种快速的神奇解决方案。 相反,是多个明智决策的逐渐积累——这些决策使可靠的实践和一致的表现成为可能。本周,我们精选了一些文章,专注于帮助数据团队脱颖而出并在长期内保持成功的决策。享受阅读!

  • 数据测试是“验证数据质量的最基本和实用的方法之一,” Xiaoxu Gao 说——这是许多数据团队任务的核心。 然而,创建可靠的测试以形成稳健的商业决策并非易事;Xiaoxu 的文章 提供了一条有用的路线图,以避免一些最常见的陷阱

  • 从测试的细节中退一步,shane murray 提出了一个关键问题:哪个团队应该首先对数据质量负责? 如你所猜测的,这个常见难题没有一刀切的答案,但一旦你对每种选择涉及的权衡有了更细致的理解,你将更有能力做出正确的决策。

图片来自 AJ AlaoUnsplash

  • 数据科学家天生擅长连接,无论是业务和产品团队之间,营销人员和客户之间,还是技术与非技术合作伙伴之间。 Robert Yi 最近分享了一篇发人深省的文章 呼吁数据团队对他们提供给其他利益相关者的数据负责,文章概述了他们可以采取的一些步骤,以确保在业务职能之间进行清晰有效的沟通。

  • 当你从事投资组合项目时,目标往往是找到最准确的模型并结束工作。 Hennie de Harder 提醒我们,在实际工作中,许多其他因素也会影响决策——从成本到实施复杂性。这就是为什么 数据团队必须拥有一致的方法来比较不同的机器学习解决方案

这是一个我们保证你不会后悔的小决定:阅读更多我们的每周亮点!我们在过去几天发布了一些出色的文章,不希望你错过它们。

  • Rik JongeriusWessel 带我们了解他们为荷兰铁路运营商 NS 执行的迷人项目:这涉及到向移动应用用户提供 实时列车拥挤度预测。

  • 将数据转化为驱动行动的洞察 可能会很棘手——尤其是在较大的组织中。Khouloud El Alami 分享了一些弥合这一差距的实用想法。

  • 语言模型如何编码和表示历史事件? Yennie Jun 探索了一个话题,其相关性将在 AI 工具在教育环境中变得越来越普及时迅速增长。

  • 如果你对最近出现的开源 LLMs 感到好奇,可以考虑关注并跟随 Het Trivedi 的 云端运行 Falcon-7B 模型的教程,作为微服务。

  • 利用 Spark 和 Tableau Desktop 的强大功能,Yu Huang, M.D., M.S. in CS 展示了如何 自动化创建仪表板的过程。

  • 如果你想要一个有趣(且启发性强)的项目回顾,Shaked Zychlinski 解释了 他们如何创建一个基于 ChatGPT 的法语辅导器的工作原型(包括语音转文本和文本转语音功能)。

感谢您支持我们的作者!如果您喜欢 TDS 上的文章,可以考虑 成为 Medium 会员 — 这将解锁我们整个档案(以及 Medium 上的所有其他帖子)。

直到下一个 Variable,

TDS 编辑团队

车辆路径问题:精确与启发式解决方案

原文

了解如何用 Python 解决复杂的路由问题

布鲁诺·斯卡利亚 C. F. 莱特Towards Data Science 布鲁诺·斯卡利亚 C. F. 莱特

·

关注 发表在 Towards Data Science ·13 分钟阅读·2023 年 8 月 4 日

--

照片由 Nik Shuliahin 💛💙 提供,来源于 Unsplash

车辆路径问题(VRP)的目标是确定由一组车辆执行的最佳路径集合,以服务于一组指定的客户。由于其多种应用和具有挑战性的组合特性,它是运筹学和数值优化中研究最多的问题之一。

具有负载(和持续时间)约束的容量车辆路径问题(CVRP)是最常见的变种之一,因为它引入了负载容量有限的车辆以及可能的持续时间/距离约束。其他常见的变种还引入了多个仓库、异质车队、取货和配送以及时间窗约束。

这些问题的组合方面使得考虑一个简单的 15 点集合时,有 6 × 10¹¹条可能的路线将其连接起来(Dantzig & Ramser, 1959)。因此,某些现实世界的应用在过去几十年中计算和算法研究进展之前可能仍不切实际。Branch-Cut-and-Price 算法已能够证明具有数百名客户的 CVRP 实例的最优性(Fukasawa et al., 2006; Pecin et al., 2017),而最先进的元启发式算法结合局部搜索技术可以在几秒钟内为这些实例提供高质量(有时最优)的解(Vidal et al., 2012; Vidal, 2022)。

在本文中,我们将介绍具有负载(和持续时间)约束的容量车辆路径问题,并使用混合整数规划(MIP)和专门的(元)启发式算法进行求解。在第一部分中,我们将使用 Python AML Pyomo,配合 HiGHS 求解器,而在第二部分中,我们将使用 Google OR Tools 包。

那些对问题的现实世界应用更感兴趣而不是理论方面的读者可能会快速浏览MIP部分,并对专门(元)启发式有用扩展部分更感兴趣。

那些对 MIP 公式感兴趣但尚不熟悉数值优化的人可能会发现查看我之前关于线性规划分支定界方法的故事会很有帮助,然后再继续阅读本文。

与往常一样,你可以在这个 git 仓库 中找到完整的代码。

现在,让我们深入了解吧!

混合整数规划

本节中提出的数学公式将使用 Toth & Vigo (2002)中所展示的相同方程,该模型被称为“三指数车辆流量公式”。

考虑一个节点集合V(需求和仓库)和一个车辆集合K。我们将使用小写的ij来表示节点索引,小写的k来表示车辆索引。由于该模型适用于不对称情况,假设节点是一个完整的有向图G(V, A)的一部分,其中A是弧。在这个问题中,有一个单一的仓库节点,索引为 0,所有车辆的容量都是Q。考虑两个决策变量组:

  • x_{i, j, k}: 是一个二进制变量,表示由车辆 k 执行的从节点 i 到节点 j 的活跃弧。

  • y_{i, k}: 是一个二进制变量,表示节点 i 的需求由车辆 k 满足。

我们的目标是最小化与活跃弧相关的成本值。总持续时间或距离是常见的例子。假设弧 ij 的成本是 cᵢⱼ。目标函数可以表示为如下。

CVRP 的目标函数。(作者提供的图像)。

我们还需要包括确保以下条件的约束:

  • 每个客户 i 只被访问一次,因此有一个从它出发的活跃弧和一个到达它的活跃弧。

  • 如果任何由车辆 k 索引的弧变量进入一个节点 i 或从中出去,则该节点的需求 q 分配给车辆 k

  • 分配给一辆车的总需求不能超过其容量 Q

  • 恰好 |K| 个节点从仓库出发并到达仓库。

  • 没有子回路……然而,子回路的数量可能太大,无法从一开始就枚举出来。我们将详细讨论如何进行处理。

CVRP 的约束。(作者提供的图像)。

和往常一样,在 Python 教程中,让我们通过导入本节中使用的库开始我们的 动手 部分:

import time
from itertools import cycle

import numpy as np
from scipy.spatial.distance import pdist, squareform
import matplotlib.pyplot as plt
import matplotlib as mpl
import networkx as nx
import pyomo.environ as pyo
from pyomo.contrib.appsi.solvers.highs import Highs

现在让我们实例化一个具有 N 个需求节点的随机问题。在此示例中,depot 节点被假设为第一个节点(索引 0),因此我们确保其对应的需求也为零。

np.random.seed(42)  # Results should be always the same

N = 10
demands = np.random.randint(1, 10, size=N)
demands[0] = 0

capacity = 15
n_vehicles = 4

coordinates = np.random.rand(N, 2)
distances = squareform(pdist(coordinates, metric="euclidean"))
distances = np.round(distances, decimals=4)  # avoid numerical errors

必要的车辆数量可以通过使用二进制装箱问题来计算。如何执行的示例也包含在 完整源代码 中。

pyomo 中建模问题有两种方法:AbstractConcrete 模型。在第一种方法中,问题的代数表达式在提供一些数据值之前被定义,而在第二种方法中,模型实例在定义其元素时立即创建。你可以在 库文档 或 Bynum 等人(2021)的书中找到更多关于这些方法的信息。本文中,我们将采用 Concrete 模型的表述。

model = pyo.ConcreteModel()

让我们实例化需求节点 V、弧 A 和车辆 K 的集合。注意,仓库节点包含在节点 V 集合中,如原始数学公式所示。

model.V = pyo.Set(initialize=range(len(demands)))
model.A = pyo.Set(initialize=[(i, j) for i in model.V for j in model.V if i != j])
model.K = pyo.Set(initialize=range(n_vehicles))

现在我们设置容量、需求负荷和弧成本的参数。

model.Q = pyo.Param(initialize=capacity)
model.q = pyo.Param(model.V, initialize={i: d for (i, d) in enumerate(demands)})
model.c = pyo.Param(model.A, initialize={(i, j): distances[i, j] for (i, j) in model.A})

以及指示给定车辆中的活跃弧和车辆访问的节点的决策变量。

model.x = pyo.Var(model.A, model.K, within=pyo.Binary)
model.y = pyo.Var(model.V, model.K, within=pyo.Binary)

在包含约束之前,我将创建我们的目标,计算活跃弧的总成本。

model.obj = pyo.Objective(
    expr=sum(
        model.x[i, j, k] * model.c[i, j]
        for (i, j) in model.A
        for k in model.K
    ),
    sense=pyo.minimize,
)

我们还必须包括之前列出的约束条件。首先,让我们使用常见的Pyomo签名来实现它们:*function(model, domain)

def arcs_in(model, i):
    if i == model.V.first():
        return sum(model.x[:, i, :]) == len(model.K)
    else:
        return sum(model.x[:, i, :]) == 1.0

def arcs_out(model, i):
    if i == model.V.first():
        return sum(model.x[i, :, :]) == len(model.K)
    else:
        return sum(model.x[i, :, :]) == 1.0

def vehicle_assignment(model, i, k):
    return sum(model.x[:, i, k]) == model.y[i, k]

def comp_vehicle_assignment(model, i, k):
    return sum(model.x[i, :, k]) == model.y[i, k]

def capacity_constraint(model, k):
    return sum(model.y[i, k] * model.q[i] for i in model.V) <= model.Q

然后将它们纳入我们的模型中。

model.arcs_in = pyo.Constraint(model.V, rule=arcs_in)
model.arcs_out = pyo.Constraint(model.V, rule=arcs_out)
model.vehicle_assignment = pyo.Constraint(model.V, model.K, rule=vehicle_assignment)
model.comp_vehicle_assignment = pyo.Constraint(model.V, model.K, rule=comp_vehicle_assignment)
model.capacity_constraint = pyo.Constraint(model.K, rule=capacity_constraint)

请注意,我还没有包括子旅行消除约束。我们应该考虑所有可能的N个节点的排列,每次取k个,这可能会变得非常庞大,即使对于中等规模的实例也很难枚举。或者,在我们的解决过程中,我们将递归地在每次找到新解时包括子旅行消除约束,如果我们验证这个解产生了子旅行的话。在一些商业求解器中,这些称为“懒惰约束”,可以通过回调直接纳入求解器。

首先,让我们创建一个函数,给定一个子旅行、所有剩余节点、一个子旅行中的节点和一辆车,返回一个Pyomo表达式,对应于之前陈述的数学公式。此外,让我们包括一个ConstraintList,在解决过程中我们将向其中添加新的元素。

def subtour_elimination(model, S, Sout, h, k):
    nodes_out = sum(model.x[i, j, k] for i in S for j in Sout)
    return model.y[h, k] <= nodes_out

model.subtour_elimination = pyo.ConstraintList()

我们必须创建一些函数,给定一个解,返回创建的子旅行(如果存在)。为此,我们将首先使用find_arcs函数创建一个活动弧的列表。这个列表将用于创建一个不完整的有向图,使用NetworkxDiGraph类。find_subtours函数应该返回一个listsets,表示连接组件。

def find_arcs(model):
    arcs = []
    for i, j in model.A:
        for k in model.K:
            if np.isclose(model.x[i, j, k].value, 1):
                arcs.append((i, j))
    return arcs

def find_subtours(arcs):
    G = nx.DiGraph(arcs)
    subtours = list(nx.strongly_connected_components(G))
    return subtours

我们的目标是消除不包含仓库节点的连接组件组。因此,在下一步中,我们将创建函数,遍历集合列表,并包括新的约束条件,如果组件集合不包括仓库节点的话。这将使用ConstraintList类的add方法。

def eliminate_subtours(model, subtours):
    proceed = False
    for S in subtours:
        if 0 not in S:
            proceed = True
            Sout = {i for i in model.V if i not in S}
            for h in S:
                for k in model.K:
                    model.subtour_elimination.add(
                      subtour_elimination(model, S, Sout, h, k)
                    )
    return proceed

现在我们已准备好提出一个解决程序,它迭代地求解 MIP,验证当前解是否有子旅行,如果有,则包括新的约束以消除它们。否则,找到的解就是最优的。

def solve_step(model, solver):
    sol = solver.solve(model)
    arcs = find_arcs(model)
    subtours = find_subtours(arcs)
    time.sleep(0.1)
    proceed = eliminate_subtours(model, subtours)
    return sol, proceed 

def solve(model, solver):
    proceed = True
    while proceed:
        sol, proceed = solve_step(model, solver)
    return sol

现在让我们实例化求解器并求解我们的模型。如果安装了highspy包,Highs求解器可以在Pyomo中使用(检查导入)。所以确保运行pip install highspy

solver = Highs()
solver.highs_options = {
    "log_file": "Highs.log",
    "mip_heuristic_effort": 0.2,
    "mip_detect_symmetry": True,
    "mip_rel_gap": 1e-6,
}

solution = solve(model, solver)

还需要一个函数来找到创建的路径,然后我们就准备好绘制结果了。

def find_tours(model):
    tours = []
    for k in model.K:
        node = 0
        tours.append([0])
        while True:
            for j in model.V:
                if (node, j) in model.A:
                    if np.isclose(model.x[node, j, k].value, 1):
                        node = j
                        tours[-1].append(node)
                        break
            if node == 0:
                break
    return tours

使用 MIP 产生的 CVRP 路径。(作者提供的图像)。

对于总共有 10 个节点的小规模实例,结果令人惊讶。然而,即使对于这个小规模实例,求解器也花费了将近半分钟来获得解,而且随着需求点增加,复杂性显著增加。幸运的是,有专门的算法公开可用,用于在短时间内找到更大实例的优质解。让我们在下一节中查看它们。

专门的(元)启发式算法

多年来,已经提出了几种针对 VRP 变体的专门(元)启发式算法。它们大多依赖于局部搜索算法,以便在给定的解中尝试不同的扰动,从而顺序地改进其成本,直到在给定的邻域中无法进一步改进。使用 Google Or Tools 时,我们也将使用与构造算法相关的局部搜索方法。

在这一部分中,将使用 Rochat 和 Taillard (1995) 的实例 150d。其数据来源于 CVRPLIB。该实例有 150 个客户和一个仓库节点,因此我们肯定无法使用之前展示的 MIP 策略来解决它。

让我们再次从导入所使用的库开始。

from itertools import cycle

import numpy as np
import pandas as pd
from scipy.spatial.distance import pdist, squareform
import matplotlib.pyplot as plt
import matplotlib as mpl
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp

让我们从一个包含每个节点坐标和需求的数据文件中加载问题数据。

dataset = pd.read_csv("./data/tai150d.csv", index_col=0)
coordinates = dataset.loc[:, ["x", "y"]]
demands = dataset.d.values

capacity = 1874
n_vehicles = 15
N = coordinates.shape[0]

distances = squareform(pdist(coordinates, metric="euclidean"))
distances = np.round(distances, decimals=4)

使用 OR Tools VRP 求解器的第一步是实例化一个路由管理器和一个模型。

# Create the routing index manager: number of nodes, number of vehicles, depot node
manager = pywrapcp.RoutingIndexManager(
    N, n_vehicles, 0
)

# Create Routing Model
routing = pywrapcp.RoutingModel(manager)

接下来,我们将添加回调函数以量化与弧/边和节点相关的维度。我们 routing 实例的 RegisterTransitCallback 方法可以用来量化与弧/边相关的任何维度,而 RegisterUnaryTransitCallback 方法可以量化与节点相关的值。根据参数的原始大小,可能需要重新缩放你的参数,因为 ortools 只考虑维度中的整数值。

# Same valid for any callback related to arcs/edges
def distance_callback(from_index, to_index):
    from_node = manager.IndexToNode(from_index)
    to_node = manager.IndexToNode(to_index)
    return distances[from_node, to_node]

transit_callback_index = routing.RegisterTransitCallback(distance_callback)

# Same valid for any callback related to nodes
def demand_callback(from_index):
    from_node = manager.IndexToNode(from_index)
    return demands[from_node]

demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)

现在,我们将使用之前定义的 demand_callback_index 来加入容量约束。请注意,持续时间约束也可以使用相同的语法定义,只需将 RegisterTransitCallback 的实例作为第一个参数传递。此外,需要强调的是,routing 模型处理异质车队,因此我们必须在第三个参数中传递一个值的列表。

# Any constraint associated with vehicles can take same arguments
routing.AddDimensionWithVehicleCapacity(
    demand_callback_index,
    0,  # null capacity slack
    [capacity,] * n_vehicles,  # vehicle maximum capacities (list for each vehicle)
    True,  # start cumul to zero
    'Capacity'
)

同样,目标的定义也将 callback 作为主要参数。在这个示例中,让我们最小化在 transit_callback_index 中定义的距离。

routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)

最后,我们必须定义求解器参数并解决我们的模型。

# Setting heuristic strategies
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
    routing_enums_pb2.FirstSolutionStrategy.CHRISTOFIDES
)
search_parameters.local_search_metaheuristic = (
    routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
)
search_parameters.time_limit.FromSeconds(300)

# Solve the problem
solution = routing.SolveWithParameters(search_parameters)

以下代码片段可用于提取我们解决方案中使用的路线。

tours = []
for vehicle_id in range(n_vehicles):
    index = routing.Start(vehicle_id)
    tours.append([])
    while not routing.IsEnd(index):
        node_index = manager.IndexToNode(index)
        previous_index = index
        index = solution.Value(routing.NextVar(index))
        tours[-1].append(node_index)
    else:
        node_index = manager.IndexToNode(index)
        tours[-1].append(node_index)

可以通过运行简单的代码 solution.ObjectiveValue() 来访问目标值。使用所提供的配置,我得到的目标值为 2679,这与证明的最优值 2645 非常接近(1.2% 的差距)。获得的路线如下图所示。

使用 ortools 在实例 tai150d 中获得的路线。(图片来源于作者)。

完整的代码(包括图表)可以在这个 git 仓库 中找到。

有用的扩展

OR Tools 库作为路由问题的一般求解器非常出色,因为它处理了多种变体的 VRP,例如时间窗、异构车队和多个仓库。然而,适用于经典 CVRP 的算法表现可能更佳。完全值得查看一下 HGS-CVRP 包(Vidal, 2022),它结合了最先进的遗传算法和几种局部搜索策略。该算法能在不到 20 秒的时间内找到实例 tai150d 的最优解。

关于一些实际应用,可能需要依赖于道路距离(或持续时间)而非欧几里得距离来连接位置。谷歌提供了一个很好的付费接口,你可以在 这个教程 中查看。然而,如果你在寻找开源替代方案,值得查看 OpenStreetMap API。一些有用的请求包括:

中应该是以逗号分隔的经纬度对的列表,在不同对之间用分号分隔。你还可以在 table 请求中指定起点和终点,这在完整表格太大而无法在单个请求中处理时很有用。

除了进行精确的路由计算外,数据可视化也是一个重要工具。Pythonfolium 可以非常有用。绝对值得查看一下。

进一步阅读

在本文早些时候,我们实现了一个精确的 MIP 模型用于 CVRP,这对于中等规模的实例并不适用。然而,结合 列生成分支定界 的算法在解决上百客户的实例时表现成功。值得查看 Fukasawa 等人(2006)和 Pecin 等人(2017)的研究论文。

对于感兴趣的列生成介绍,可以在我之前的 Medium 文章 中找到。

关于 元启发式算法,Vidal 等人(2012)和 Vidal(2022)的论文非常出色。两者也以技术报告的形式提供,链接可以在 HGS-CVRP 仓库中找到。

结论

在这篇文章中,介绍了两种解决容量受限车辆路径问题(CVRP)的方法:混合整数规划和(元)启发式方法。第一种方法用于解决一个小规模实例,并且成功解决了该问题,但无法处理中等规模或大型实例。第二种方法则用于解决文献中的一个具有挑战性的问题,该问题有 150 个客户,解算器在 300 秒内找到了一个与已知最优解相差 1.2%的高质量解。

参考文献

Bynum, M. L. 等, 2021. Pyomo-optimization modeling in python. Springer.

Dantzig, G. B., & Ramser, J. H., 1959. The truck dispatching problem. 管理科学, 6(1), 80–91.

Fukasawa, R., Longo, H., Lysgaard, J., Aragão, M. P. D., Reis, M., Uchoa, E., & Werneck, R. F., 2006. 针对容量受限车辆路径问题的鲁棒分支定界与定价方法. 数学规划, 106, 491–511.

Pecin, D., Pessoa, A., Poggi, M., & Uchoa, E., 2017. 改进的分支定界与定价方法用于容量受限车辆路径问题. 数学规划计算, 9, 61–100.

Rochat, Y., & Taillard, É. D., 1995. 在车辆路径问题的局部搜索中进行概率性多样化和强化. 启发式期刊, 1, 147–167.

Toth, P., & Vigo, D., 2002. 车辆路径问题概述. 车辆路径问题, 1–26.

Vidal, T., 2022. 针对 CVRP 的混合遗传搜索:开源实现和 SWAP*邻域. 计算机与运筹研究, 140, 105643.

Vidal, T., Crainic, T. G., Gendreau, M., Lahrichi, N., & Rei, W., 2012. 一种用于多车库和周期性车辆路径问题的混合遗传算法. 运筹学, 60(3), 611–624.

成功数据共享的 3 条不可变规则

原文:towardsdatascience.com/3-immutable-rules-for-successful-data-sharing-ac884766ca10?source=collection_archive---------1-----------------------#2023-01-21

解锁数据协作的力量

Louise de LeyritzTowards Data Science Louise de Leyritz

·

关注 发表在 Towards Data Science ·9 分钟阅读·2023 年 1 月 21 日

--

在我之前的文章中,我讨论了数据共享这一已经非常成熟的概念。数据共享指的是向所有部门开放数据访问,以赋予每个部门进行数据驱动决策的能力。

对于公司来说,在没有适当计划的情况下贸然开展数据共享计划,认为仅仅增加业务部门的访问权限就足够了,这种情况仍然很常见。这种做法是错误的。实际上,数据共享是一项复杂的任务,需要经过深思熟虑的规划和执行才能成功。

我们提出了三条不变的规则,以确保你的数据共享计划的成功

  1. 你不应妥协数据质量

  2. 你应为数据提供丰富的背景

  3. 你应提供正确的接口来探索数据

关于数据质量的第一条规则是数据共享的基石——这是一个不可谈判的前提。数据质量是数据生产者(软件和数据工程团队)的责任。这是将高质量数据交到数据团队手中的问题。没有高质量的数据,数据团队无法完成工作,更无法与其他部门进行数据共享。实际上,如果数据团队无法使用数据,那还分享给别人干嘛呢?

本文中的第二条和第三条规则侧重于确保高质量数据有效地与业务团队共享。这不仅涉及提供准确和可靠的数据,还包括用相关背景丰富数据,并通过用户友好的界面使其易于访问。这样,即使是技术能力较弱的团队也能轻松使用数据。下面可以找到一个可视化表示。

有效数据共享的三条规则 — 图片由Castor提供

忽视这些规则中的任何一条必然会导致失败,这也是我们理想中想要避免的。让我们深入探讨每一条规则。

数据质量

成功的数据共享的基础是保持你与业务部门共享的数据质量。

数据共享是为了让业务部门能够做出数据驱动的决策。为此,你必须提供一流的数据

当你分享有缺陷的数据时,人们显然会做出错误的决策。这可能导致重大财务损失、错失机会,并损害公司声誉。更重要的是,这会侵蚀对信任的数据信任,并导致对数据的普遍漠视。如果计划不是分享一流的数据,那么根本不要分享数据。数据共享要么全力以赴,要么彻底放弃。如果执行不当,可能对你的组织造成损害。

数据质量是一个涵盖所有影响数据能否用于预期用途的因素的总括性术语。有几个特征定义了高质量的数据,包括但不限于:

  • 准确性:数据准确描述其所代表的现实世界现象的程度。

  • 完整性:数据是完整的,包含了所有必要的信息。

  • 一致性:数据在不同来源和平台上是一致的。

  • 可靠性:数据是最新的,并且与预期的使用场景相关。

  • 可用性:数据被预期受众理解和使用以做出明智决策的难易程度。

你可以在 Kevin Hu 的 文章中找到更多数据质量指标。

数据质量属性及其相关指标 — 图片来源于 Castor

当你分享具有这些属性的数据时,你增加了改进决策和效率的可能性。但这并不是数据质量的全部。

确保你的数据符合正确的质量标准的一个好方法是实施 数据合同

数据合同是任何数据民主化倡议中的重要组成部分。数据社区与数据合同有着 爱恨交织的关系。但我们认为它们在数据共享对话中值得一提。

数据合同是数据生产者和数据消费者之间的协议,概述了共享和使用数据的具体条款和条件。它们在确保数据质量方面可以发挥重要作用,通过设定明确的期望和处理数据的指导方针。

数据合同规定,在数据共享之前,数据必须符合某些格式、约束和语义意义,或者可能包括要求定期审计数据质量的条款。

数据合同可能包括以下信息:

  • 正在收集哪些数据

  • 数据的采集频率和方式

  • 谁拥有和负责数据(个人或团队)

  • 谁可以访问数据以及访问的级别

  • 安全性和治理措施,例如匿名化

例如,让我们考虑一下驱动 Ubereats 的机器学习模型。该模型的性能取决于其训练数据的准确性,而这些数据来源于公司内部的各种表格。

为确保模型正常运行,我们期望数据的完整性始终得到保持;这意味着列不应被删除,每个字段的值应保持一致,所有关键业务逻辑应得到遵守。如果这些条件中的任何一个未得到满足,模型的性能可能会受到影响。

为确保这些期望得到满足,应在数据合同中进行概述,以使数据生产者对维护数据的完整性负责。

总体而言,数据合同可以通过设定明确的指导方针和期望来提供一个确保数据质量的框架。这可以帮助确保所有相关方对数据质量的维护负有责任。这样,数据合同可以防止有缺陷的数据流入运营团队手中。

维护高水平的数据质量很重要,但仅此不足以解决问题。下一步是确保提供背景信息。

丰富的背景

背景是有效实施数据共享的第二个关键。没有背景的数据是危险和毫无价值的,因为它可能被不同的团队以不同的方式解读。

让我告诉你,这不是一个安全的选择。不同的解释意味着不同的结论,最终导致部门间报告不一致。如果你要引导业务团队进入未知领域,就给他们一张地图。背景就是这张地图。

人们了解一个数据集时,他们需要知道这些数据将满足什么需求、其内容以及其位置。一旦人们找到相关的数据集,他们就完成了 10%的工作。接下来,他们需要通过一个包含 10 多个问题的检查清单,确保他们理解自己使用的数据。只有当人们能够回答以下问题时,他们才真正理解数据:

  • 数据来源于哪里?

  • 它的流动路径是什么?它向下游喂送了哪些表?

  • 谁拥有它/谁对它负责?

  • 我所在领域中某个字段的含义是什么?

  • 为什么这很重要?

  • 这个表最后一次更新时间是什么时候?

  • 这些数据的上游和下游依赖关系是什么?

  • 这是生产质量的数据吗?

背景始于文档。所有共享的数据资产都需要被记录,以便利益相关者理解它们。实际上,这意味着对你的数据资产进行整理,包括列定义、标签、所有者等。当你正确记录数据时,人们知道在哪里找到它以及如何使用它,而不需要向公司中的其他人寻求帮助。

提供背景的第二个方面是拥有强大的数据血统能力。数据血统是一种极其强大的透明工具。它使人们能够理解数据资产之间的关系。如果上游出现问题,数据血统允许每个人了解下游的后果,从而避免不愉快的惊讶。血统还可以帮助利益相关者在数据问题出现时识别其来源。

数据血统:追踪数据资产之间的关系——图片来自Castor

提供背景的第三个方面是促进社交发现。这可以通过共享有关数据如何被利用的信息来实现。

当人们看到他们的同事如何使用和查询数据时,他们能够以更强的基础开始,并从同事的见解和策略中学习。社交发现使团队能够在彼此的知识基础上进行合作,从而提高工作效率。

例如,一个想要对营销合格线索(MQLs)进行分析的市场营销分析师可以利用社交发现来简化过程。通过社交发现,分析师可以迅速识别出营销团队使用的最相关的表格和数据集。此外,他还可以访问团队执行的查询,这可以作为分析的起点。这不仅节省了时间,还允许分析师从同事的工作中获得见解和学习。

用户友好的接口

如果你要与他人共享数据,必须通过正确的接口进行。并非所有团队成员的技术水平相同,也不是所有团队的数据需求相同。为正确的团队提供合适的接口对于让数据对所有人都可访问至关重要。

如果你在 dbt 中记录数据,不能期望市场营销团队在那儿提取文档。上下文应该在对业务团队友好的工具中提供。有两种方法可以实现这一点:

实现这一点的一种方法是提供一个能够高效搜索和导航的工具。该工具应易于使用和理解,以确保非技术团队成员能够有效使用。数据目录是一个可以用来轻松发现、理解和访问数据的工具的例子。

另一种提供正确接口的方法是通过使数据在业务团队已经使用的工具中易于访问。这种方法涉及将数据交付给团队已经熟悉的工具。可以使用反向 ETL 工具来实现这一目的。

通过在现有工具中使数据可查找,团队可以访问所需的数据,而无需导航新系统或学习新软件。例如,一旦在数据仓库中计算了线索评分,反向 ETL 允许将该指标同步到 Salesforce。这使得销售人员可以直接在他们熟悉的工具中访问数据。

无论你采用什么方法,请记住,如果你想让数据对所有人可用,必须满足业务团队的需求。要求他们学习技术团队的工具和流程只会阻碍你的努力。

提供正确的接口对于数据民主化和使所有团队成员都能访问数据至关重要。在决定正确的接口时,重要的是考虑不同团队的技术专长和数据需求。通过提供易于使用的工具或将数据发送到现有工具,团队可以访问所需的数据,以做出明智的决策并推动结果。

结论

总之,数据共享是推动数据驱动决策和促进跨部门协作的强大工具。

但这是一个复杂的任务,需要深思熟虑的规划和执行才能成功。

我们提出了三条不可改变的规则,以确保您的数据共享计划成功:1)保持数据质量,2)提供数据的丰富背景,3)提供正确的界面以探索数据。

当然,数据共享涉及隐私和安全问题,这篇文章中没有提及。我下一篇文章将完全致力于这个话题!

关于我们

我们撰写了有关利用数据资产的所有过程:从 现代数据栈 到数据团队组成,再到数据治理。我们的 博客 涵盖了从数据中创造实际价值的技术和非技术方面。

在 Castor,我们正在为 Notion、Figma 和 Slack 一代开发一个数据文档工具。

想要了解更多?联系我们,我们将向您展示演示。

最初发表于 https://www.castordoc.com

2024 年值得期待的 3 项音乐 AI 突破

原文:towardsdatascience.com/3-music-ai-breakthroughs-to-expect-in-2024-2d945ae6b5fd?source=collection_archive---------2-----------------------#2023-12-30

2024 年可能成为音乐 AI 的转折点

Max HilsdorfTowards Data Science Max Hilsdorf

·

关注 发布于 Towards Data Science ·11 分钟阅读·2023 年 12 月 30 日

--

图像由 DALL-E 3 生成。

回顾:2023 年如何改变了音乐 AI

从我的角度来看,2023 年是音乐 AI 历史上最激动人心的一年。以下是我们在这一年中体验到的一些突破:

  • 文本到音乐生成已经跨越了“恐怖谷”阶段(例如 MusicLM

  • 开源旋律条件的音乐生成发布(例如 MusicGen

  • 首个基于提示的音乐搜索产品上线(例如 Cyanite

  • 开源的具备音频理解/生成能力的聊天机器人已发布(例如,AudioGPT

  • 开源的音乐描述 AI已发布(例如,Doh 等,2023

  • 基于提示的源分离试点(例如,Liu 等,2023

从文本到音乐生成,再到全文本音乐搜索,2023 年充满了突破。这些进展是冰山一角,展示了音乐 AI 内在的潜力。然而,即使有这些令人兴奋的发展,该领域仍明显落后于其更大的兄弟语音 AI,甚至落后于其表亲 NLP 和计算机视觉。这一差距可以在两个关键方面观察到(或听到):

1. 技术尚不成熟。无论是音乐生成、基于文本的搜索还是神经嵌入:目前音乐 AI 中的所有技术在文本和图像领域至少已有 1 到 3 年的历史。该领域需要更多的资金、时间和智力支持。

2. 缺乏令人信服和流行的商业产品。在音乐 AI 潜力变得明显之后,许多初创公司纷纷成立,开始致力于商业产品的开发。然而,随着这些产品的开发和测试,音乐家和企业急切地等待将 AI 技术融入他们的工作流程的机会。

然而,在 2023 年音乐 AI 技术成功之后,我对研究人员和公司在这两个方面取得进展感到乐观。在这篇文章中,我想强调我希望在 2024 年看到的三个具体发展,并解释它们为何重要。凭借这些预期的进展,2024 年将站在通过 AI 彻底改变我们与音乐互动的前沿

1. 灵活自然的源分离

源分离的可视化。图像摘自作者的博客文章

什么是源分离?

音乐源分离是将完整制作的音乐分解为其原始乐器源(例如,人声、节奏、键盘)的任务。如果你从未听说过源分离,我写了一篇完整的博客文章讲解了它是如何工作的,以及为什么这是一个如此具有挑战性的技术问题。

源分离领域的第一个重大突破发生在 2019 年,当时 Deezer 发布了分离器(Spleeter)作为开源工具。自这一技术飞跃以来,该领域经历了相对稳定的小步改进。然而,如果将原始的 Spleeter 与现代开源工具如 Meta 的DEMUCS或商业解决方案如LALAL.ai进行比较,差异显得非常明显。因此,在经历了多年缓慢、渐进的进展后,为什么我会期待源分离在 2024 年爆发呢?

为什么我们应该期待源分离的突破?

首先,源分离是其他音乐 AI 问题的基石技术。拥有一个快速、灵活且自然的源分离工具,可以将音乐分类、标记或数据增强提升到新水平。许多研究人员和公司正在仔细观察源分离领域的进展,准备在下一次突破发生时采取行动。

其次,不同种类的突破将推动该领域的发展。最明显的是分离质量的提升。虽然我们肯定会看到这方面的进展,但我不期望会有重大飞跃(如果错了我很乐意接受)。不过,除了输出质量,源分离算法还有两个其他问题:

1. 速度: 源分离通常运行在大型生成神经网络上。对于单个音轨,这可能还可以。然而,对于商业应用中遇到的较大工作负载,速度通常仍然太慢——尤其是在推理过程中执行源分离时。

2. 灵活性: 一般而言,源分离工具提供一套固定的源(例如“人声”、“鼓声”、“低音”、“其他”)。传统上,无法执行根据用户需求定制的源分离,因为这需要针对这一任务训练一个全新的神经网络。

一旦源分离的速度足够快以在推理过程中进行(即每次模型预测之前),许多有趣的应用将会出现。例如,我曾写过关于利用源分离使黑箱音乐 AI 可解释的潜力。我认为速度优化的商业兴趣非常大,这可能会推动明年的突破。

此外,当前代源分离 AI 的灵活性有限,使其在各种应用场景中无法使用,即便原则上具备潜力。在一篇名为Separate Anything You Describe的论文中,研究人员今年推出了基于提示的源分离系统。想象一下在文本框中输入“给我第二段的主合成器,但不带延迟效果”,然后得到你所需的源音频。这就是我们所期待的潜力。

摘要:源分离

总之,由于其在音乐 AI 中的重要性以及速度和灵活性的持续改进,音乐源分离在 2024 年可能会取得重大进展。新的发展,如基于提示的系统,使其更具用户友好性和适应性。这一切都预示着在行业中的更广泛应用,这可能激励该领域的研究突破。

2. 通用音乐嵌入

图像由 DALL-E 3 生成。

自然语言处理(NLP)中的嵌入

为了理解音乐嵌入是什么以及它们为何重要,让我们来看看自然语言处理(NLP)这一术语的起源。在 NLP 中嵌入出现之前,该领域主要依赖于更简单的基于统计的方法来理解文本。例如,在简单的词袋(BoW)方法中,你只需统计词汇表中每个单词在文本中出现的频率。这使得 BoW不比一个简单的词云更有用

一个简单的词云示例。图像作者提供。

嵌入的引入显著改变了自然语言处理(NLP)的格局。嵌入是单词(或短语)的数学表示,其中单词之间的语义相似性通过这些嵌入空间中的向量之间的距离体现出来。简单来说,单词、句子或整本书的意义可以被压缩成一堆数字。通常,每个单词/文本的1001000个数字已经足以数学上捕捉其意义。

Tensorflow Embedding Projector上使用 t-SNE 可视化的 Word2Vec(10k)嵌入。突出显示了与“violin”最相似的前 5 个单词。截图由作者提供。

在上图中,你可以看到基于其数值嵌入的 10,000 个单词在三维图表中的表示。因为这些嵌入捕捉了每个单词的意义,我们可以简单地在图表中查找最接近的嵌入,以找到类似的术语。这样,我们可以轻松识别出与“violin”最相似的 5 个术语:“cello”、“concerto”、“piano”、“sonata”和“clarinet”。

嵌入的主要优势:

  • 上下文理解: 与早期的方法不同,嵌入对上下文敏感。这意味着相同的单词可以根据在不同句子中的使用情况具有不同的嵌入,从而提供更为细致的语言理解。

  • 语义相似性: 具有相似意义的单词通常在嵌入空间中靠得很近,这使得嵌入非常适合用于音乐搜索引擎或推荐系统中的检索任务。

  • 预训练模型: 借助像 BERT 这样的模型,嵌入从大量文本中学习,并可以针对特定任务进行微调,从而显著减少对任务特定数据的需求。

音乐的嵌入

因为嵌入不过是数字,原则上任何东西都可以压缩成有意义的嵌入。下图给出了一个例子,其中不同的音乐类型根据其相似性在二维空间中进行可视化。

音乐类型嵌入在 Every Noise at Once 上的二维空间中可视化。截图由作者提供。

然而,尽管嵌入在工业和学术界已经成功使用了超过 5 年,我们仍然没有广泛采用的领域特定音乐嵌入模型。显然,利用嵌入技术在音乐领域具有巨大的经济潜力。以下是一些可以立即实施的嵌入使用案例,前提是能够获得高质量的音乐嵌入:

  1. 音乐相似性搜索:在任何音乐数据库中搜索与给定参考曲目相似的曲目。

  2. 文本到音乐搜索:通过自然语言搜索音乐数据库,而不是使用预定义标签。

  3. 高效机器学习:基于嵌入的模型通常需要比传统基于频谱图或类似音频表示的方法少 10 到 100 倍的数据进行训练。

到 2023 年,我们在开源高质量音乐嵌入模型方面已经取得了很大进展。例如,MicrosoftLAION 都分别发布了针对通用音频领域训练的 CLAP 模型(特定类型的嵌入模型)。然而,这些模型大多是在语音和环境声音上训练的,使得它们在音乐方面的效果较差。随后,Microsoft 和 LAION 发布了针对音乐数据单独训练的音乐特定版本 CLAP 模型。 M-A-P 今年也发布了多个令人印象深刻的音乐特定嵌入模型。

我在测试所有这些模型后的印象是,我们越来越接近目标,但仍未达到三年前文本嵌入所能实现的效果。在我看来,主要瓶颈仍然是数据。我们可以假设像 Google、Apple、Meta、Spotify 等所有主要参与者已经有效地使用音乐嵌入模型,因为他们可以访问巨量的音乐数据。然而,开源社区还没有能够赶上并提供一个令人信服的模型。

摘要:通用音乐嵌入

嵌入技术是一种有前景的技术,它使检索任务更准确,并在数据稀缺时支持机器学习。不幸的是,针对音乐的突破性领域特定嵌入模型尚未发布。我希望并怀疑,开源项目或甚至致力于开源发布的大型公司(如 Meta)将在 2024 年解决这个问题。我们已经很接近,一旦达到一定水平的嵌入质量,每家公司都将采用基于嵌入的音乐技术,以在更短时间内创造更多价值。

3. 弥合技术与实际应用之间的差距

使用 DALL-E 3 生成的图像。

2023 年是个奇怪的一年……一方面,AI 已成为科技界最大的热门词汇,几乎所有终端用户和企业都能找到 ChatGPT、Midjourney 等的应用场景。另一方面,只有少数实际完成的产品已被推出并广泛采用。当然,Drake 现在可以唱“我的心会继续”,但迄今为止,这项技术周围尚未构建出商业案例。而且,是的,AI 现在可以为节拍制作人生成声音样本。然而,实际上,一些作曲家正在努力微调自己的 AI 模型以应对 缺乏有吸引力的商业解决方案

从这个角度看,音乐 AI 的最大突破可能不是花哨的研究创新。相反,它可能是基于 AI 的产品和服务在满足企业或最终用户需求上的成熟度提升。在这条路上,任何想要构建音乐 AI 产品的人都还面临许多挑战:

  1. 了解音乐行业或最终用户的需求:技术本身通常对用例无特定要求。了解技术如何满足实际需求是一个关键挑战。

  2. 将花哨的演示转变为稳健的产品:今天,数据科学家可以在一天内构建一个聊天机器人原型甚至一个音乐生成工具。然而,将一个有趣的演示转变为一个有用、安全和成熟的产品是要求高且耗时的。

  3. 应对知识产权和许可问题:伦理和法律考虑使公司和用户在提供或采用基于 AI 的产品时犹豫不决。

  4. 确保资金/投资和首个收入来源:在 2023 年,已经创立了无数音乐 AI 初创公司。强有力的愿景和明确的商业案例将是确保资金和推动产品开发的必要条件。

  5. 市场营销和用户采纳:即使是最伟大的创新产品现在也容易被忽视。最终用户和企业被关于 AI 未来的报告和承诺淹没,难以触及目标受众。

例如,我们可以更详细地了解 AI 如何通过数字音频工作站(DAW)的新插件来影响音乐制作。在最近的博客文章中,Native Instruments 展示了 10 个新的 AI 驱动插件。为了展示现有的可能性,我们来看一下Audialab 的“Emergent Drums 2”。Emergent Drums 允许音乐家用生成 AI从零开始设计他们的鼓样本。该插件很好地集成到 DAW 中,并作为一个功能齐全的鼓机插件运行。请亲自查看:

演示视频:“Emergent Drums”由 Audialab 提供。

再次放眼远眺,音乐 AI 的潜在应用广泛,从音乐制作到教育、市场营销和分销。利用 AI 的巨大技术潜力为这些领域提供实际价值将是明年面临的关键挑战。

摘要:从研究到产品

2023 年是音乐 AI 的一个重要年份,为未来铺平了道路。2024 年的真正改变者是什么?不仅仅是技术——而是让它在现实场景中为真实的人群服务。预计音乐 AI 将走出实验室,融入我们的生活,影响我们创作和消费音乐的方式。

你好,2024。

2023 年为 AI 及其可能性奠定了技术基础,并提高了公众意识。这就是为什么,我估计 2024 年可能是开始开发音乐 AI 产品的最佳年份。当然,这些产品中的许多将会失败,一些 AI 的承诺最终也会落空。看一下下面图中的著名的 Gartner Hype Cycle,我们应该提醒自己这是正常现象。

Gartner Hype Cycle。图片由作者提供。

没有人可以确定我们目前在这个炒作周期的哪个位置(如果有人知道,请告诉我)。不过,考虑到今年创造的所有基础工作和公众意识,2024 年有可能成为历史性的AI 音乐技术里程碑年。我非常期待明年会带来什么。

成为音乐家真是个好时光!

关于我

我是一名音乐学家和数据科学家,分享我对 AI 与音乐当前话题的看法。以下是与本文相关的一些以往作品:

MediumLinkedin上找到我!

高斯混合模型(GMM)的 3 个应用场景

原文:towardsdatascience.com/3-use-cases-for-gaussian-mixture-model-gmm-72951fcf8363?source=collection_archive---------1-----------------------#2023-07-27

特征工程、无监督分类以及使用 GMM 算法的异常检测

Viyaleta ApgarTowards Data Science Viyaleta Apgar

·

关注 发布于 Towards Data Science ·10 分钟阅读·2023 年 7 月 27 日

--

高斯混合模型(GMM)是一种简单而强大的无监督分类算法,基于 K-means 算法来预测每个实例的分类概率。GMM 的这一特性使其在许多应用中都具有很大的灵活性。在本文中,我将讨论 GMM 如何用于特征工程、无监督分类和异常检测。

高斯混合模型(GMM)是什么?

模型描述

尽管单一或多个变量的数据集的高斯分布试图以概率方式表示整个数据集,GMM 假设数据集中存在子群体,并且每个子群体遵循其自己的正态分布。以无监督的方式,GMM 试图学习数据中的子群体及其对每个数据点的概率表示[1]。GMM 的这一特性使我们能够使用模型找到属于任何子群体的概率较低的点,从而将这些点分类为异常值。

GMM 本质上是通过利用组件来表示这些子群体,并修改多变量概率分布函数以适应组件,从而扩展了多变量高斯分布以适应子群体情况。温馨提醒,多变量高斯分布的概率密度函数如下:

多变量高斯分布的概率密度函数

在 GMM 中,每个实例的概率被修改为所有组件的概率和,组件权重被参数化为𝜙。GMM 要求所有组件权重的总和为 1,以便将每个组件视为整体的一个比率。GMM 还结合了每个组件的特征均值和方差。模型如下:

GMM 模型的公式

注意多变量分布与 GMM 之间的相似性。实质上,GMM 算法为每个组件找到正确的权重,这些组件被表示为多变量高斯分布。在他的文章中,Oscar Contreras Carrasco对 GMM 做了精彩的推导[2]。

模型的参数可以通过随机初始化或使用特定策略进行初始化,模型的组件权重𝜙通过重复的期望最大化(EM)步骤来确定[1]。

模型算法

GMM 的实施的第一部分是组件的初始化。GMM 的实施包括初始化步骤,随后是迭代的期望最大化(EM)过程,直到收敛:

第 1 步:初始化步骤中,模型参数被初始化:K值从数据集中随机分配为组件均值;组件方差根据随机分配的均值计算;所有组件权重被赋值为 1/K

步骤 2:期望步骤中,我们计算每个数据点由每个组件生成的概率。每个数据点-组件对的期望是该特定组件的权重乘以我们数据点属于该组件的概率(给定组件均值和方差),作为所有其他组件概率的一个分数,参数化为各自的组件权重。基本上,期望步骤尝试找出每个点属于每个组件的可能性,并利用这个值来逐步调整模型参数直到收敛。

期望步骤的公式

步骤 3:最大化步骤中,我们重置组件的权重和均值,并根据期望步骤中的γ值重新计算方差。新的组件权重设置为该组件所有数据点期望值的总和。每个组件的新均值是所有数据点的平均值,加权由期望值决定。

最大化步骤的公式

就像在 k-means 算法中一样,如果组件的数量事先不可用,那么猜测组件的数量K是合适的。

下面是 GMM 收敛的视觉示例。在这里,GMM 展示了在具有两个簇的二维数据集上的收敛。该算法的行为类似于 k-means,但不同之处在于它估计概率密度(而不是纯粹对样本数据点的分类)。

旧忠实数据的 EM 聚类 [3]

让我们看看这个算法如何应用于我们的三个用例。

特征工程中的 GMM

尽管一些机器学习模型(如臭名昭著的 XGBoost)可以学习各种输入特征分布,但其他模型对其要求更为严格。线性回归、逻辑回归、线性判别分析(LDA)和多变量高斯通常期望特征呈正态分布,如果数据是多模态的,可能效果不佳。我们可能还有其他分析和视觉上的原因需要处理多模态,而 GMM 可以帮助我们实现这一点。

我们来看一个虚构书店数据集中双峰特征的示例。我从 Kaggle 数据库中提取了这些数据(链接在此),其中包含从 books.toscrape.com [7] 爬取的数据。该数据集包含典型的书店信息,如书名、类别、价格和评分。它还包含书籍数量,这决定了虚构书店库存中的书籍总数。巧合的是,这些书籍具有双峰分布。我们来看看是否可以使用 GMM 作为特征工程技术,从书籍数量数据中创建两个独立的特征。

我使用以下代码在 Python 中实现了这一任务:

让我们查看结果。左侧的图表显示了书籍数量的原始分布。右侧的图表显示了每个预测组件的分布,在 GMM 转换之后。请注意,完整分布的形状完全相同,但两个组件在特定点处拆分了原始分布,创建了两个(大多)正常的直方图。如果我对分割两个组件的点不满意,我可以使用 GMM 预测的概率来调整组件 1 结束和组件 2 开始的位置。

数量在 GMM 之前和之后的分布 [9]

GMM 用于无监督分类

GMM 的另一个应用场景是无监督分类。在这方面,GMM 的工作方式类似于 K-means 算法,但允许对类别归属进行概率性判断(不同于 K-means,其中输出是一个二元度量)。这对于需要自定义分类阈值或简单要求概率输出的用例特别有益。

对于这个例子,我下载了企鹅数据集(在 Kaggle 上提供),并选择了两个特征以进行可视化演示:企鹅的喙长度和深度(喙是企鹅喙的顶部脊)[8]。我删除了空值数据点,并创建了散点图以描绘数据。

当我们查看散点图时,3 个潜在的组脱颖而出。如果我们为实际类别上色,我们会发现,事实上,三种企鹅与三组数据点对齐。这个例子非常基础,因为在现实世界中,我们通常处理的是多维数据,且没有简单的方法来确定数据集中存在多少个子群体。尽管如此,我们还是来看看 GMM 如何帮助我们对数据进行分割。

描述企鹅喙长与深度的散点图 [9]

在下面的 Python 代码片段中,我下载了数据集,删除了空值,选择了两个感兴趣的特征,并将 GMM 模型拟合到它们上面。sklearn提供了两种预测选项——预测类别和预测类别归属的概率。每个数据点的概率总和等于 1(根据 GMM 算法约束)。

让我们看看每个数据点属于每个组件的概率。下面的三个散点图显示了每个组件实例的概率。透明度较高的点具有较低的概率,而颜色较亮的点具有较高的概率。在下图中,我们可以看到,GMM 对位于不同组件之间的点预测了更大的不确定性,这在预期之中。我们可以使用gmm.predict_proba()函数来控制类别归属。

描述每个预测类别的类归属概率的散点图 [9]

用于异常检测的 GMM

在我的之前的故事中,我分享了异常检测的基础知识以及统计方法在检测异常中的应用[6]。也就是说,我使用多变量高斯分布来识别低概率符合正态分布的数据点。然而,这种方法的问题是我们的数据往往更复杂。高斯混合模型(GMM)尝试解决多模态问题,并且在特征形成正态分布特征关系的子群体的情况下非常有用。

在这个最后的例子中,我将使用来自ODDS library [10]的葡萄酒数据集。这是我在我的帖子中使用的数据集,在那里我应用了多变量高斯分布来检测离群点。让我们看看我是否可以改进之前的结果,在那个结果中,41 个实例被错误分类,其中 40 个被错误识别为异常点。相比多变量高斯分布方法,第一个优点是我们可以使用所有特征,而不限于仅正态分布的特征。第二个优点是我们可以考虑数据子群体。

GMM 最显著的优势可能是该模型可以帮助发现两种类型的异常数据:一种是人口中的离群点(例如数据录入错误),另一种是形成自己组的异常(例如信用卡欺诈行为)。在葡萄酒数据集中,正实例被构建为两种类型的葡萄酒样本,而数据集中的异常被构建为第三种葡萄酒的子样本 [10]。因此,我们很可能会发现我们的离群点在这个实例中构成了自己的组。

由于我们的数据集中有 13 个特征,让我们将数据汇总到前两个 PCA 组件中并绘制它们。在下面的图表中,我们将看到散点图包含一个密集区域和大约二十个分散的点。数据中没有明显的子群体。

前两个 PCA 组件的散点图,显示真实的异常情况 [9]

下面的代码片段使用sklearn库中的 2 个组件和标准输入字段来拟合 GMM 模型到我们的数据集。希望一个组件能够识别正实例,而另一个组件能够识别异常。

预测组件的编号是随机的,因此我做了一些背景处理来重新标记预测。你可以查看我的 Jupyter notebook 以获取更多关于如何做到这一点的指导。

结果看起来很好!我们没有假阴性,并且有 11 个错误识别的异常。如果这是现实世界,我们在运行此模型后只需人工检查大约 15%的数据。这已经是对多变量高斯分布方法的显著改进。

GMM 结果的混淆矩阵 [9]

如果我们绘制结果,可以看到被错误识别的异常情况实际上更接近最密集的区域。再次强调,我们仅使用前两个 PCA 组件进行可视化,GMM 在对数据点进行分类时做了更多工作。

前两个 PCA 组件的散点图,显示预测的异常情况 [9]

我们可能通过分析预测的异常概率来改进这个结果。下面,我已经为错误识别的异常实例和真实异常实例绘制了这些概率(没有预测的假阴性)。

条形图显示了在错误识别的异常和真实异常之间的异常类别归属概率 [9]

我们可以看到大多数错误识别的异常概率低于 0.9999。因此,我们可以使用预测的异常概率并设置新的阈值,而不是使用默认分类。如果我们将阈值设置为大于 0.9999 的异常检测,我们将只有 3 个错误识别的异常。这可以用一行代码完成:components = np.where(proba>0.9999, 1, 0)。在某些情况下,改变阈值可能会增加我们最终结果中的假阴性。幸运的是,这种情况并不存在。

GMM 结果的混淆矩阵,在降低提高概率阈值之后 [9]

在以下 PCA 散点图中,我们可以看到我们的模型仍然预测了 3 个错误识别的异常,但通过新的改进,我们只需手动检查 10% 的数据实例以检测异常。考虑到 8% 的数据实际上是异常的,这真是太棒了!

提高异常概率阈值后的前 2 个 PCA 组件的散点图 [9]

预测类别概率的能力使得 GMM 成为数据科学中一个强大而多用途的工具。尽管它有与 K-means 同类的相同限制,GMM 在特征工程、更灵活的无监督分类和异常检测中非常有用。

我在写这篇文章时非常开心,读它的时候也同样开心。我总是欢迎反馈和问题,所以一定要利用评论区。如果你希望直接反馈,可以随时在 LinkedIn 上找到我

来源:

  1. brilliant.org/wiki/gaussian-mixture-model/

  2. towardsdatascience.com/gaussian-mixture-models-explained-6986aaf5a95

  3. commons.wikimedia.org/wiki/File:EM_Clustering_of_Old_Faithful_data.gif

  4. towardsdatascience.com/understanding-anomaly-detection-in-python-using-gaussian-mixture-model-e26e5d06094b

  5. scikit-learn.org/stable/modules/generated/sklearn.mixture.GaussianMixture.html#sklearn.mixture.GaussianMixture.fit

  6. towardsdatascience.com/the-basics-of-anomaly-detection-65aff59949b7

  7. www.kaggle.com/datasets/sbonelondhlazi/bookstore-dataset

  8. medium.com/r/?url=https%3A%2F%2Fwww.kaggle.com%2Fdatasets%2Fparulpandey%2Fpalmer-archipelago-antarctica-penguin-data

  9. github.com/viyaleta/Medium-Code-Examples/blob/main/GMM/3%20Use-Cases%20for%20GMM.ipynb

  10. Saket Sathe 和 Charu C. Aggarwal. LODES: 局部密度与谱异常检测的结合。SIAM 数据挖掘会议,2016. odds.cs.stonybrook.edu/wine-dataset/

34% 更快的整数到字符串转换算法

原文

我们打印整数的速度够快吗?

Tigran Hayrapetyan Towards Data Science Tigran Hayrapetyan

·

关注 发表在 Towards Data Science ·14 min read·2023 年 12 月 4 日

--

1. 介绍

在计算机编程中,将给定的整数转换为字符串是一项常见操作,例如在将整数打印到屏幕上或任何文本文件(如 *.xml, *.json, *.csv, *.txt 等)之前需要进行此操作。

众所周知,整数(以及其他所有内容)在计算机内存中以二进制格式存储——即 0 和 1 的序列。例如:

  • 数字 12 在内存中的表示为“1100”。

  • 数字 29 被表示为“11101”。

这就是为什么每次我们想要将其转换为人类可读的十进制格式时,需要进行这样的转换。

在这个故事中,我将要:

  • 对用于这种转换的标准算法进行概述,

  • 观察其现有的优化,

  • 提出我的算法,并且

  • 展示他们的实验比较。

我们将看到,我的算法对于 32 位整数运行25–38%更快,对于 64 位整数运行40–58%更快,相比于优化的标准算法。其在 C++语言中的实现可以在 GitHub 上找到,如文末引用。

当然,如果应用在其生命周期内只打印少量整数,那么负责将它们转换为字符串的算法不会成为瓶颈。但是,对于那些将大量数据打印到文本文件中的情况,转换算法的效率就开始发挥作用。在数据科学或机器学习等领域,转换大量整数为字符串的需求很常见,例如在将数据集导出到文本文件时,如.csv 或.json。

2. 标准转换算法

将整数转换为字符串是一个常见操作,任何现代编程语言中都有实现这样的算法,无论是作为语言的一部分还是作为标准库的一部分。而且这个算法几乎在所有地方都是相同的——基于反复获取并提取整数的最后一个数字,然后继续处理剩余部分。

为了获得给定整数N的最后一位数字,它只是计算其除以 10 的余数:

“digit := N mod 10”,

并且为了提取它,执行整数除法:

“N := N / 10”。

*给定一个整数 N,它的最后一位数字

其余部分正在计算中。*

请注意,在这个故事中,当除以两个整数时,我们将假设只取结果的整数部分。

作为完整算法的示例,当打印数字“N = 2'167”时,将执行以下操作:

*打印数字“2167”的操作:

第一步:2167 % 10 = 7(存储数字“7”),2167 / 10 = 216(继续处理 216),

第二步:216 % 10 = 6(存储数字“6”),216 / 10 = 21(继续处理 21),

第三步:21 % 10 = 1(存储数字“1”),21 / 10 = 2(继续处理 2),

第四步:由于“2 < 10”,只存储最后一个数字“2”。

第五步:(未示例)反转存储的数字的顺序并打印它们。*

请注意,当我们处理 1 位整数(即范围为[0..9])时,我们可以直接发送进行打印,因为这些 10 个数字中的每一个对应的字符已经固定。且除以 10 的余数总是 1 位整数。

我们还可以注意到,这个算法报告的* N *的数字是倒序的(这里我们得到的数字序列是‘7’,‘6’,‘1’,‘2’,而不是‘2’,‘1’,‘6’,‘7’),所以在最后需要将生成的序列进行反转。

总结一下,它的伪代码如下:

var result[0 .. 25] : Array of Characters  // Assume at most 25 characters

// The procedure takes integer 'N' to be printed, and fills its
// decimal characters into 'result' array.
procedure print( N: Integer )
    i := 0  // Index over 'result' array
    while N > 0
        result[ i ] := '0' + (N mod 10)  // Take the last digit
        N := ⌊ N / 10 ⌋   // Pick out the last digit
        i := i+1
    result[ i ] := '\0'  // Append the terminating 'null' character
    reverse array result[0 .. i-1]

描述的算法很简单,我们可以用 3–4 行代码轻松实现。但它的瓶颈在于对N的每一位小数表示使用了两个相对昂贵的操作——整数除法和整数余数计算。众所周知,整数除法和余数计算平均花费的时间比两个整数的加法、减法甚至乘法要长 4–5 倍。这里我们可以观察到上述算术操作的时间基准:

*时间(以纳秒为单位)花费的实验比较,用于执行 5 种类型的

算术操作(每个操作在随机数据上运行 200 次)。

我们可以看到最后两个操作(整数除法和余数计算)

花费的时间显著更多。此外,我们看到整数乘法

执行的速度几乎与加法或减法一样快。*

实验是在以下系统下使用 Google Benchmark 进行的:

*CPU: Intel Core i7–11800H @ 2.30GHz

内存:16.0 GB

操作系统:Windows 11 Home,64 位

编译器:MSVC 2022 (/O2 /Ob2 /MD /GR /Gd)*

让我们看看是否存在更快的整数打印方法…

3. 现有优化

优化 1

对于描述的算法,一个常见的优化是消除最后一步反转生成的数字序列。这个技巧在例如 [1] 中有很好的介绍。在这个优化中,我们将数字直接按正确的顺序写入缓冲区。由于算法本身从右到左报告给定整数N的数字,所以我们也将它们从右到左写入缓冲区。

*将生成的数字从右到左填入结果数组,

直接以它们在最终位置的顺序。*

伪代码将如下所示:

var result[0 .. 25] : Array of Characters  // Assume at most 25 characters

// The function takes integer 'N' to be printed, and returns position 
// of its converted first character in the 'result' array.
function print( N: Integer ) : Integer
    result[ 25 ] := '\0'  // Place the terminating 'null' character at the end
    i := 25  // Index over 'result' array
    while N > 0
        i := i-1  // Here we go to left, for placing the next digit
        result[ i ] := '0' + (N mod 10)  // Take the last digit
        N := ⌊ N / 10 ⌋  // Pick out the last digit
    return i  // Position from where the converted integer starts

注意,在本故事的此处和所有其他伪代码中,我们没有处理打印数字“0”的情况。根据所有编写的算法,“0”将显示为没有任何位的序列,因此在几乎所有打印算法中,打印“0”都在一个单独的分支中完成。我们这里只是为了简洁跳过了这个分支。

这个优化的另一个小优点是我们不需要在每次转换后都写入终止的空字符。相反,我们只需在缓冲区的最后一个位置写入一次,因为N的最后一位的物理位置是预先固定的,它将始终是缓冲区中倒数第二个位置。

这种优化的缺点是第一个字符的位置变得可变,因为它取决于整数N的位数。

*优化 1 的缺点:不同

位数计数将在输出数组中从不同的位置开始。*

然而,实际上这不会成为问题,因为转换后的整数通常会立即发送到文本文件或屏幕上,因此不会在内存中停留太久。对于这样的目的,我们不需要转换的数字从内存中某个精确指定的位置开始写入。

优化 2

下一项优化是通过使用整数除法和余数计算操作来在单一步骤中获取N的 2 位数字。这个技巧在[1]和[2]中也有详细记录。为此,我们不再重复计算

“digit := N mod 10”,接着

“N := N / 10”,

我们将计算:

“digits := N mod 100”,接着

“N := N / 100”,

这将给我们N的最后 2 位数字,然后将它们都剪掉。

*启用第二个优化的数字“5174092”打印操作:

步骤 1:5174092 % 100 = 92(存储数字“92”),5174092 / 100 = 51740(继续处理 51740),

步骤 2:51740 % 100 = 40(存储数字“40”),51740 / 100 = 517(继续处理 517),

步骤 3:517 % 100 = 17(存储数字“17”),517 / 100 = 5(继续处理 5),

步骤 4:由于“5 < 100”,只存储最后一位数字“5”。*

请注意,为了最终高效地打印这些获得的 2 位数字,我们应该准备一个长度为 100 的数组(索引从 0 到 99——因此对应所有可能的余数“N mod 100”),其中的值将是一对字符,从“00”,“01”,“02”,……一直到“98”,“99”。

在此优化中,整数除法和余数操作的数量减少了近 2 倍。

完成这部分后,我想引起你们的注意,即使启用了上述两个优化,我们仍然会进行与给定整数N中的数字数量成正比的整数除法和余数计算操作。

4. 我的算法

我打算提出另一种算法,这将使 32 位整数的整数打印加速约25–38%,64 位整数的加速约40–58%。其思想是——如果我们从给定整数N中提取数字时不是从右到左,而是从左到右呢?所以首先我们会获得最重要的数字,然后是下一个重要的数字,依此类推,直到只剩下最不重要的数字。如果我们事先不知道N的位数,这会变得有点困难,但现在让我们暂时搁置这个问题,假设我们已经知道N中有L位数字。

具有 L=7 位的输入数字 N 的示例。

那么我们如何获得最重要的数字呢?再次使用整数除法,但这次为:

“digit := N / 10^(L-1)”

获取给定整数的最左侧数字的示例。

那么我们如何从 N 中提取它,以便能够继续处理剩余部分?在知道最重要的数字是‘d’后,我们可以进行以下减法操作:

“N := N — d*10^(L-1)”

从给定整数中提取最左边数字的示例。

后续我们将重复除法和减法操作,直到 N 变为 1 位整数(即范围 [0..9]),最终也会打印该数字。让我们看看算法在“N = 6'129”情况中的表现。请注意,它有 4 位数字,所以这里我们从“L=4”开始。

*使用我的算法打印数字“6129”的操作:

步骤 1:6129 / 1000 = 6(打印数字‘6’),6129–6*1000 = 129(继续处理 129),

步骤 2:129 / 100 = 1(打印数字‘1’),129–1*100 = 29(继续处理 29),

步骤 3:29 / 10 = 2(打印数字‘2’),29–2*10 = 9(继续处理 9),

步骤 4:由于“9 < 10”,只需打印最后一位数字‘9’。*

你可能会争辩说,计算不同的 10 的幂比进行整数除法或取余计算更耗时。这绝对正确,除了一个细节:我们可以预计算所有必要的 10 的幂,并在程序的整个执行过程中使用它们。对于 32 位整数,只有 10 个不同的 10 的幂,对于 64 位整数,有 20 个 10 的幂。因此,将它们全部预计算并保存在内存中不会成为问题。

那么总体上我们有什么?为了用我的算法打印一个 N 的数字,我们做:

1 次整数除法,

1 次乘法,以及

1 次减法,

与标准算法相比:

1 次取余计算以及

1 次整数除法。

在下一节中,我们将看到我的方法实际上更好,因为乘法和减法加起来比取余计算消耗的 CPU 时间更少。这些算术操作的时间消耗实验比较在第二章中介绍过。

我算法的主要部分的伪代码可能如下所示:

var powers_of_10[0 .. 10] : Array of Integers 
  = { 1, 10, 100, 1'000, ..., 100'000'000, 1'000'000'000 }
  // Precalculated powers of 10, which will be used during print

var result[0 .. 25] : Array of Characters  // Assume at most 25 characters

// The procedure takes integer 'N' to be printed, and fills its
// decimal characters into the 'result' array.
procedure print( N: Integer )
    L := calculate_digits_count( N )
    i := 0  // Index over 'result' array
    while L > 0
        digit := ⌊ N / powers_of_10[ L-1 ] ⌋  // Obtain left-most digit
        result[ i ] := '0' + digit   // Write it to the 'result' array
        N := N – digit * powers_of_10[ L-1 ]  // Calculate remaining part
        L := L-1  // Adjust its count of digits accordingly
        i := i+1
    result[ i ] := '\0'  // Append the terminating 'null' character

由于我的算法从左到右打印 N 的数字,我想称之为“左到右打印机”或简短为“LR 打印机”。

还有一件事需要高效找到 LN 的十进制数字计数。幸运的是,预计算的 10 的幂数组在这里也会有帮助。我们只需从小的幂次迭代到较大的幂次,直到找到比 N 大的幂 10^L。然后,指数 L 本身将表示 N 中的数字计数。

例如,获取“N = 23'504”的数字计数如下所示:

如何计算数字 N = 23'504 的数字计数 L。

我们依次将 N 与 10 的幂比较,直到 N 变小。

这发生在 100'000 的幂次上,即 10⁵,因此我们得出结论 L=5。*

该函数的伪代码可能如下所示:

// The function takes integer 'N' and returns count of its digits.
function calculate_digits_count( N: Integer ) : Integer
    // Check case of numbers with maximal count of digits
    if N >= powers_of_10[ 9 ]  // Compare with maximal power of 10
        return 10  // Count of digits for such numbers
    // Regular case
    L := 0
    while N >= powers_of_10[ L ] 
        L := L+1
    return L

通过这两个部分,我们提供了将整数转换为字符串的完整算法。

请注意,由于“LR 打印机”从左到右报告 N 的数字,因此最后不需要做任何反转。此外,与现有的优化 1 相比,这里我们保留了指定转换后的 N 的第一个数字应放置在内存中的位置的能力。

“LR 打印机”可以用于打印任何基数的数字(不仅仅是 base 10)。为此,我们只需要用新基数的预计算幂替换预计算的 10 的幂。

“LR 打印机”在 C++ 语言中的实现可以在 GitHub 上找到,链接为 [3]。

“LR 打印机”的优化 2

我的算法可以通过在“现有优化”部分中描述的第二次优化进行增强,并在 [1] 和 [2] 中进行了记录。如果进行优化,则我们将每次打印 2 位数字,而不是逐位打印。

让我们看看它如何在数字“N = 4'610'937”上运行。这里 L=7,我们这次从将 N 除以 10^(L-2)=10'000 开始:

*启用第二次优化的“LR 打印机”打印数字“4610937”的操作:

步骤 1:4610937 / 10⁵ = 46(打印数字‘46’),4610937–46*10⁵ = 10937(继续处理数字 10937),

步骤 2:10937 / 10³ = 10(打印数字‘10’),10937–10*10³ = 937(继续处理数字 937),

步骤 3:937 / 10 = 93(打印数字‘93’),937–93*10 = 7(继续处理数字 7),

步骤 4:由于“7 < 100”,只打印最后一位数字‘7’。*

启用此功能后,我们将花费:

1 次整数除法,

1 次乘法,以及

1 次减法,

每 2 位输入数字。

在这里,数字将按其自然顺序 — 从左到右获取,因此无需在最后进行反转。

启用第二次优化的“LR 打印机”的实现也可以在 GitHub 上找到,链接为 [3]。

5. 与现有算法的实验比较

进行实验比较对于这类工作至关重要,因此在本章中,我将展示以下整数打印算法的比较结果:

  • 第一个优化的标准算法(标记为“Std”),

  • 我的算法“LR 打印机”(标记为“LR”),

  • 标准算法的第二次优化(标记为“Std [2-dig]”)和

  • 含第二次优化的“LR 打印机”(标记为“LR [2-dig]”)。

这些算法都在 32 位和 64 位整数上进行了测试,输入数字的位数不同。

在 base=10 中打印数字:

基数=10(普通情况)下打印的结果是:

*打印 1 个数字(无论是 32 位还是 64 位)所花费的时间(以纳秒为单位),

具有特定位数的不同算法。

打印是在 base=10 中完成的。*

对于 32 位整数,我们可以看到“LR printer”相较于标准打印机的性能提升约为30–38%。当启用第二次优化(每步打印 2 位)时,性能提升较低,为13–28%。这是完全预期的,因为总体上我们只执行了 2 或 4 步。

在打印 64 位整数时,我的算法表现更佳。“LR printer”比标准算法快约40–50%。当两者都启用第二次优化时,“LR printer”性能提升47–58%

本故事标题中的百分比是通过考虑最常见的情况选择的:当我们在 base=10 下处理 32 位整数,并假设它们有许多位数时。在这种情况下,“LR printer”相对于标准算法的性能提升为 30–38%,所以取平均数大约为 34%。

在 base=3 中打印数字:

让我们看看在其他基数下打印整数时结果是否会显著不同。我们将观察在数字 base=3 中的打印情况:

*打印一个数字(无论是 32 位还是 64 位)所花费的时间(以纳秒为单位),

具有一定数量位数的情况下,使用不同算法。

打印是在 base=3 中进行的。

如我们所见,对于 32 位整数,“LR-printer”相对于标准算法的性能提升约为25–33%,这通常对应于所使用算术操作的性能差异。

对于 64 位整数,“LR-printer”的性能提升约为短数字(8 位)50–55%,长数字(36 位)27–30%

总体备注

通常,整数打印的基数不会对相对性能提升产生太大影响,因为打印过程中要执行的操作数量与输入数字的位数成正比,而不是这些位数可能具有的值的数量。

几乎总是这样,数字位数越多,“LR-printer”(或“LR-printer [2-dig]”变体)比标准打印算法(或其“2-dig”变体)的表现会更好。这一点也很明确,因为位数越多,循环外指令的影响(如从一个函数调用另一个函数或放置空字符)越小。

总体来说,在打印 64 位整数时,“LR-printer”和“LR-printer [2-dig]”变体的结果都更令人印象深刻。

对我个人而言,这些结果相当显著。

6. 结论

我们提出了一种将整数转换为字符串的新算法,称为“LR printer”。与优化后的标准转换算法相比,它在 32 位整数上运行25–38%更快,在 64 位整数上运行40–58%更快。我们的算法可以在任何数字基数下工作(不仅仅是在普通的 base 10 下)。

将整数转换为字符串的算法在仅打印少量数字的应用程序中从不成为瓶颈。但对于其他类型的应用程序,例如自动生成 .csv、xml 或 *.json 等文本文件的应用程序,转换算法的效率就显得尤为重要。特别是当这些文本文件将包含大量数字时,例如在导出大型数据集时。

非常感谢你读到最后!很高兴看到你在下面的评论!

我向 David Ayrapetyan 表示感谢 (www.linkedin.com/in/davidayrapetyan/),感谢他仔细审阅了本故事的草稿,并提出了多个上下文改进和语法修正。

感谢 Hayk Aslanyan (www.linkedin.com/in/haykaslanyan/),感谢他对草稿进行了技术审查,并提出了其他改进建议。

插图设计由 Asya Papyan 制作: www.behance.net/asyapapyan

如果你喜欢阅读这个故事,可以在 LinkedIn 上找到我: www.linkedin.com/in/tigran-hayrapetyan-88989b12/

参考文献

[1] : “整数到字符串转换” — tia.mat.br/posts/2014/06/23/integer_to_string_conversion.html

[2] : “C++ 的三个优化技巧” — www.facebook.com/notes/10158791579037200/

[3] : “C++ 语言中的 LR 打印机实现” — github.com/tigranh/lr_printer

数据可视化中的 3D 和动效

原文:towardsdatascience.com/3d-and-motion-in-data-visualisation-d25a386810dd

数据可视化、Python 和 3D 创作软件,完美的融合。来源:作者

许多好莱坞特效背后的开源工具如何帮助创建令人惊叹的数据可视化

Josh TaylorTowards Data Science Josh Taylor

·发表于 Towards Data Science ·3 分钟阅读·2023 年 1 月 20 日

--

数据可视化领域已经非常成熟,拥有一些出色的文献[1,2],指导如何以视觉方式传达数据。

不幸的是,随着这种成熟度的提高,带来了平淡。如今的大多数可视化都是单调的,原始性被摒弃,采用经过验证的方法。

当我们有证据表明,使用条形对比长度是最有效的数据表示方式时[3],我们如何避免一切都变成条形图呢?

有用的数据可视化备忘单。来源:作者

为数据可视化注入兴奋感

在我之前的两篇文章中,我讨论了为什么我们应该使用动画和3D在数据可视化中的应用。

两种方法的有力论点是需要为一个应该引发兴趣和刺激的领域注入兴奋感。创造新的、不同的和原创的东西是关键。真正的原创性很难[4],但对数据可视化的持续发展至关重要。

在这篇文章中,我们将覆盖一个实际示例,展示如何实现这一点。我们将从电影行业获取灵感;将特效工具和数据结合在一起,创造出不同的东西。

下面的示例可以看到。它比较了基于 GitHub 活动的编程语言的变化。创建此图表的代码在本文末尾提供。

首次(?) 3D 蝴蝶图表比赛的视频。来源:作者

但首先,让我们更详细地探讨一下我们将使用的工具。

Blender 简介

Blender 是一个免费的开源 3D 创作套件。它可以用于创建各种计算机生成的内容,包括多个大片电影的特效。

Blender 在数据科学和数据可视化方面特别有趣,因为它拥有一个完整的 Python API,可以用于程序化地构建可视化。

这开启了几乎无限的可能性。单个 Python 工作流可以用于处理数据,然后使用 Blender 根据这些数据创建强大、逼真的 3D 渲染!

你可以在其网站上找到更多信息以及下载 Blender,blender.org

一个简单的示例

虽然完整的教程超出了本文的范围,但以下内容展示了如何轻松入门。

它基于 Python 列表创建一系列立方体,并将这些立方体放置在场景中,并添加一个光源:

少于 15 行代码的条形图

经过一些小的调整,Blender 可以渲染出下面的条形图,代表上面脚本中的数据列表:

只需几行代码即可生成条形图。来源:作者

Blender 网站上提供了全面的文档,允许你轻松在此示例基础上进行扩展。如前所述,实际上没有什么限制,因为一切都可以通过 Python API 来控制。这意味着你可以:

  • 通过设置关键帧创建动画。

  • 设置一个场景,完全控制光照、颜色和材质。

  • 确定相机位置并控制其随时间的移动。

有了这些考虑,创建上面视频中展示的“蝴蝶竞赛”图表相对简单。

可以在此找到完整的脚本和数据

结论

希望这篇文章能激发一些灵感,帮助我们摆脱数据可视化的现状,通过借鉴数据科学以外的技术和工具,为内容注入兴奋感和原创性。

如果你对数据可视化的原创方法有其他想法,请在评论中添加。

参考文献:

  1. 《定量信息的视觉展示》。爱德华·R·塔夫特,1983 年

  2. 《功能艺术:信息图形和可视化导论》。阿尔贝托·卡伊罗,2011 年

  3. 科学数据的图形感知和图形方法。威廉·S·克里夫兰;罗伯特·麦吉尔,1985 年

  4. 结构性想象:类别结构在示例生成中的作用。沃德·T·B,1994 年

使用 Open3D 进行 3D 数据处理

原文:towardsdatascience.com/3d-data-processing-with-open3d-c3062aadc72e?source=collection_archive---------1-----------------------#2023-05-08

使用 Python 的 Open3D 库处理 3D 模型的简洁指南(附带互动式 Jupyter Notebook)

Prerak Agarwal数据科学之路 Prerak Agarwal

·

关注 发表在 数据科学之路 ·13 分钟阅读·2023 年 5 月 8 日

--

在这篇文章中,我提供了一个简洁的指南,讲解如何使用 Python 的Open3D库来探索、处理和可视化 3D 模型——这是一个开源的 3D 数据处理库。

使用 Open3D 可视化的 3D 模型(原始 3D 模型可在这里找到)。

如果你考虑处理 3D 数据/模型以进行特定任务,如训练 AI 模型进行 3D 模型分类和/或分割,你可能会发现这个讲解很有帮助。互联网上的 3D 模型(如 ShapeNet 数据集中)有多种格式,如 .obj.glb.gltf 等。使用像 Open3D 这样的库,这些模型可以轻松处理、可视化并转换为其他格式,例如更易于理解和解释的点云。

本文也提供了 Jupyter Notebook 版本,适合那些希望跟随并在本地运行代码的读者。包含 Jupyter Notebook 及所有其他数据和资产的 zip 文件可以从以下链接下载。

[## 3D 数据处理与 Open3D.zip

包含 Jupyter Notebook、3D 模型及所有其他必要文件的 zip 文件。

drive.google.com](https://drive.google.com/file/d/1290xG3_BEYn9WN9TwkYKMmNbNRsrWjGk/view?usp=share_link&source=post_page-----c3062aadc72e--------------------------------)

在本教程中,我将介绍以下任务:

  1. 加载和可视化一个 3D 模型作为 网格

  2. 通过采样点将 网格 转换为 点云

  3. 点云 中移除隐藏点*

  4. 点云 转换为数据框

  5. 保存 点云 和数据框

让我们先导入所有必要的库:

# Importing open3d and all other necessary libraries.

import open3d as o3d
import os
import copy
import numpy as np
import pandas as pd
from PIL import Image

np.random.seed(42)
# Checking the installed version on open3d.

o3d.__version__
# Open3D version used in this exercise: 0.16.0

加载和可视化一个 3D 模型作为 网格

可以通过运行以下代码行将 3D 模型读取为网格:

# Defining the path to the 3D model file.
mesh_path = "data/3d_model.obj"

# Reading the 3D model file as a 3D mesh using open3d.
mesh = o3d.io.read_triangle_mesh(mesh_path)

要可视化网格,请运行以下代码行:

# Visualizing the mesh.
draw_geoms_list = [mesh]
o3d.visualization.draw_geometries(draw_geoms_list)

网格应在新窗口中打开,呈现出下图所示的样子(请注意,网格以静态图像形式打开,而不是这里展示的动画图像)。可以使用鼠标指针旋转网格图像。

3D 模型作为网格可视化(估计表面法线之前)。

如上所示,汽车网格看起来不像典型的 3D 模型,且被涂成均匀的灰色。这是因为网格没有关于 3D 模型中顶点和表面的 法线 信息。

什么是 法线 — 在给定点的表面上的法线向量是垂直于该点表面的向量。法线向量通常被称为“法线”。要了解更多相关内容,可以参考这两个链接:法线向量点云中的表面法线估计

可以通过运行以下代码行来估计上述 3D 网格的 法线

# Computing the normals for the mesh.
mesh.compute_vertex_normals()

# Visualizing the mesh.
draw_geoms_list = [mesh]
o3d.visualization.draw_geometries(draw_geoms_list)

一旦可视化,网格应如下面图像所示。计算 法线 后,汽车模型将正确渲染,看起来像一个 3D 模型。

3D 模型可视化为网格(估算表面法线后)。

让我们创建一个 XYZ 坐标系,以了解这个车模在欧几里得空间中的方向。XYZ 坐标系可以覆盖在上面的 3D 网格上,通过运行以下代码行进行可视化:

# Creating a mesh of the XYZ axes Cartesian coordinates frame.
# This mesh will show the directions in which the X, Y & Z-axes point,
# and can be overlaid on the 3D mesh to visualize its orientation in
# the Euclidean space.
# X-axis : Red arrow
# Y-axis : Green arrow
# Z-axis : Blue arrow
mesh_coord_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=5, origin=[0, 0, 0])

# Visualizing the mesh with the coordinate frame to understand the orientation.
draw_geoms_list = [mesh_coord_frame, mesh]
o3d.visualization.draw_geometries(draw_geoms_list)

3D 网格可视化带有 XYZ 坐标系。X 轴:红色箭头,Y 轴:绿色箭头,Z 轴:蓝色箭头 [容易记住的方式 — XYZ::RGB]

从上述可视化中,我们可以看到这辆车的网格方向如下:

  • XYZ 轴的原点在车模的体积中心在上面的图像中未见,因为它在车模网格内部)。

  • X 轴(红色箭头):沿着汽车的长度维度,正 X 轴指向汽车的引擎盖(在上面的图像中未见,因为它在车模网格内部)。

  • Y 轴(绿色箭头):沿着汽车的高度维度,正 Y 轴指向汽车的车顶。

  • Z 轴(蓝色箭头):沿着汽车的宽度维度,正 Z 轴指向车的右侧。

现在我们来看看这个车模内部的情况。为此,我们将沿 Z 轴裁剪网格,并移除车的右半部分(正 Z 轴)。

# Cropping the car mesh using its bouding box to remove its right half (positive Z-axis).
bbox = mesh.get_axis_aligned_bounding_box()
bbox_points = np.asarray(bbox.get_box_points())
bbox_points[:, 2] = np.clip(bbox_points[:, 2], a_min=None, a_max=0)
bbox_cropped = o3d.geometry.AxisAlignedBoundingBox.create_from_points(o3d.utility.Vector3dVector(bbox_points))
mesh_cropped = mesh.crop(bbox_cropped)

# Visualizing the cropped mesh.
draw_geoms_list = [mesh_coord_frame, mesh_cropped]
o3d.visualization.draw_geometries(draw_geoms_list)

在 Z 轴上裁剪的 3D 网格,右半部分的车被移除(正 Z 轴)。裁剪后的网格展示了这个 3D 车模的详细内部结构。

从上述可视化中,我们看到这个车模有详细的内部结构。现在我们已经看到这个 3D 网格内部的内容,我们可以在移除属于车内部的“隐藏”点之前,将其转换为点云。

通过采样点将网格转换为点云

在 Open3D 中,将网格转换为点云可以很容易地完成,通过定义我们希望从网格中采样的点的数量。

# Uniformly sampling 100,000 points from the mesh to convert it to a point cloud.
n_pts = 100_000
pcd = mesh.sample_points_uniformly(n_pts)

# Visualizing the point cloud.
draw_geoms_list = [mesh_coord_frame, pcd]
o3d.visualization.draw_geometries(draw_geoms_list)

从 3D 网格中均匀采样的 100,000 个点创建的 3D 点云。

请注意,上面的点云中的颜色仅指示点在 Z 轴上的位置。

如果我们像对网格一样裁剪点云,它会是这样的:

# Cropping the car point cloud using bounding box to remove its right half (positive Z-axis).
pcd_cropped = pcd.crop(bbox_cropped)

# Visualizing the cropped point cloud.
draw_geoms_list = [mesh_coord_frame, pcd_cropped]
o3d.visualization.draw_geometries(draw_geoms_list)

在 Z 轴上裁剪的 3D 点云,右半部分的车被移除(正 Z 轴)。与上面的裁剪网格类似,裁剪后的点云也展示了这个 3D 车模的详细内部结构。

我们在裁剪后的点云的可视化中看到,它还包含了属于车模内部的点。这是预期的,因为这个点云是通过从整个网格中均匀采样点创建的。在下一部分中,我们将移除这些属于车内部并不在点云外表面的“隐藏”点。

点云中移除隐藏点

想象你将光线照射到汽车模型的右侧。所有落在 3D 模型右侧外表面的点都会被照亮,而点云中的其他点则不会。

插图展示了 Open3D 的隐藏点移除功能如何在从给定视角查看的点云上工作。所有被照亮的点被认为是“可见”的,而所有其他点被认为是“隐藏”的。

现在让我们将这些被照亮的点标记为“可见”,所有未被照亮的点标记为“隐藏”。这些“隐藏”的点还包括所有属于汽车内部的点。

该操作在 Open3D 中称为隐藏点移除。为了在点云上执行此操作,使用 Open3D,运行以下代码行:

# Defining the camera and radius parameters for the hidden point removal operation.
diameter = np.linalg.norm(np.asarray(pcd.get_min_bound()) - np.asarray(pcd.get_max_bound()))
camera = [0, 0, diameter]
radius = diameter * 100

# Performing the hidden point removal operation on the point cloud using the
# camera and radius parameters defined above.
# The output is a list of indexes of points that are visible.
_, pt_map = pcd.hidden_point_removal(camera, radius)

使用上述输出的可见点索引列表,我们可以在可视化点云之前,用不同的颜色标记可见点和隐藏点。

# Painting all the visible points in the point cloud in blue, and all the hidden points in red.

pcd_visible = pcd.select_by_index(pt_map)
pcd_visible.paint_uniform_color([0, 0, 1])    # Blue points are visible points (to be kept).
print("No. of visible points : ", pcd_visible)

pcd_hidden = pcd.select_by_index(pt_map, invert=True)
pcd_hidden.paint_uniform_color([1, 0, 0])    # Red points are hidden points (to be removed).
print("No. of hidden points : ", pcd_hidden)

# Visualizing the visible (blue) and hidden (red) points in the point cloud.
draw_geoms_list = [mesh_coord_frame, pcd_visible, pcd_hidden]
o3d.visualization.draw_geometries(draw_geoms_list)

从上图所示的相机视角下的隐藏点移除操作后的点云。蓝色为“可见”点,而红色为“隐藏”点。

从上面的可视化中,我们可以看到隐藏点移除操作如何在给定的相机视角下工作。该操作从给定的相机视角下消除所有被前景点遮挡的背景点。

为了更好地理解这一点,我们可以再次重复相同的操作,但这次在轻微旋转点云之后。实际上,我们在这里尝试改变视角。但不是通过重新定义相机参数来改变视角,而是通过旋转点云本身。

# Defining a function to convert degrees to radians.
def deg2rad(deg):
    return deg * np.pi/180

# Rotating the point cloud about the X-axis by 90 degrees.
x_theta = deg2rad(90)
y_theta = deg2rad(0)
z_theta = deg2rad(0)
tmp_pcd_r = copy.deepcopy(pcd)
R = tmp_pcd_r.get_rotation_matrix_from_axis_angle([x_theta, y_theta, z_theta])
tmp_pcd_r.rotate(R, center=(0, 0, 0))

# Visualizing the rotated point cloud.
draw_geoms_list = [mesh_coord_frame, tmp_pcd_r]
o3d.visualization.draw_geometries(draw_geoms_list)

3D 点云绕 X 轴旋转了 90 度。注意,现在,与之前不同的是,Y 轴(绿色箭头)沿着汽车的宽度方向,而 Z 轴(蓝色箭头)沿着汽车的高度方向。X 轴(红色箭头)没有变化,仍然沿着汽车的长度方向。

插图展示了隐藏点移除操作如何在从之前相同视角查看的旋转点云上工作。如前所述,所有被照亮的点被认为是“可见”的,而所有其他点被认为是“隐藏”的。

通过在旋转的汽车模型上重复相同的过程,我们将看到这次所有落在 3D 模型上表面(汽车的车顶)的点会被照亮,而点云中的其他点则不会。

我们可以通过运行以下代码行来重复对旋转点云的隐藏点移除操作:

# Performing the hidden point removal operation on the rotated point cloud
# using the same camera and radius parameters defined above.
# The output is a list of indexes of points that are visible.
_, pt_map = tmp_pcd_r.hidden_point_removal(camera, radius)

# Painting all the visible points in the rotated point cloud in blue,
# and all the hidden points in red.

pcd_visible = tmp_pcd_r.select_by_index(pt_map)
pcd_visible.paint_uniform_color([0, 0, 1])    # Blue points are visible points (to be kept).
print("No. of visible points : ", pcd_visible)

pcd_hidden = tmp_pcd_r.select_by_index(pt_map, invert=True)
pcd_hidden.paint_uniform_color([1, 0, 0])    # Red points are hidden points (to be removed).
print("No. of hidden points : ", pcd_hidden)

# Visualizing the visible (blue) and hidden (red) points in the rotated point cloud.
draw_geoms_list = [mesh_coord_frame, pcd_visible, pcd_hidden]
o3d.visualization.draw_geometries(draw_geoms_list)

从上图所示的相机视角隐藏点去除操作后的旋转点云。再次,可见的点为蓝色,而隐藏的点为红色。

上述旋转点云的可视化清楚地说明了隐藏点去除操作的工作原理。因此,为了从这个汽车点云中删除所有的“隐藏”点,我们可以通过依次绕三个轴线将点云略微旋转从 -90 度到 +90 度来顺序执行这个隐藏点去除操作。在每次隐藏点去除操作之后,我们可以聚合出点的索引输出列表。在所有隐藏点去除操作之后,点的聚合索引列表将包含所有未隐藏的点(即,位于点云外表面上的点)。以下代码执行这个顺序隐藏点去除操作:

# Defining a function to rotate a point cloud in X, Y and Z-axis.
def get_rotated_pcd(pcd, x_theta, y_theta, z_theta):
    pcd_rotated = copy.deepcopy(pcd)
    R = pcd_rotated.get_rotation_matrix_from_axis_angle([x_theta, y_theta, z_theta])
    pcd_rotated.rotate(R, center=(0, 0, 0))
    return pcd_rotated

# Defining a function to get the camera and radius parameters for the point cloud
# for the hidden point removal operation.
def get_hpr_camera_radius(pcd):
    diameter = np.linalg.norm(np.asarray(pcd.get_min_bound()) - np.asarray(pcd.get_max_bound()))
    camera = [0, 0, diameter]
    radius = diameter * 100
    return camera, radius

# Defining a function to perform the hidden point removal operation on the
# point cloud using the camera and radius parameters defined earlier.
# The output is a list of indexes of points that are not hidden.
def get_hpr_pt_map(pcd, camera, radius):
    _, pt_map = pcd.hidden_point_removal(camera, radius)    
    return pt_map
# Performing the hidden point removal operation sequentially by rotating the
# point cloud slightly in each of the three axes from -90 to +90 degrees,
# and aggregating the list of indexes of points that are not hidden after
# each operation.

# Defining a list to store the aggregated output lists from each hidden
# point removal operation.
pt_map_aggregated = []

# Defining the steps and range of angle values by which to rotate the point cloud.
theta_range = np.linspace(-90, 90, 7)

# Counting the number of sequential operations.
view_counter = 1
total_views = theta_range.shape[0] ** 3

# Obtaining the camera and radius parameters for the hidden point removal operation.
camera, radius = get_hpr_camera_radius(pcd)

# Looping through the angle values defined above for each axis.
for x_theta_deg in theta_range:
    for y_theta_deg in theta_range:
        for z_theta_deg in theta_range:

            print(f"Removing hidden points - processing view {view_counter} of {total_views}.")

            # Rotating the point cloud by the given angle values.
            x_theta = deg2rad(x_theta_deg)
            y_theta = deg2rad(y_theta_deg)
            z_theta = deg2rad(z_theta_deg)
            pcd_rotated = get_rotated_pcd(pcd, x_theta, y_theta, z_theta)

            # Performing the hidden point removal operation on the rotated
            # point cloud using the camera and radius parameters defined above.
            pt_map = get_hpr_pt_map(pcd_rotated, camera, radius)

            # Aggregating the output list of indexes of points that are not hidden.
            pt_map_aggregated += pt_map

            view_counter += 1

# Removing all the duplicated points from the aggregated list by converting it to a set.
pt_map_aggregated = list(set(pt_map_aggregated))
# Painting all the visible points in the point cloud in blue, and all the hidden points in red.

pcd_visible = pcd.select_by_index(pt_map_aggregated)
pcd_visible.paint_uniform_color([0, 0, 1])    # Blue points are visible points (to be kept).
print("No. of visible points : ", pcd_visible)

pcd_hidden = pcd.select_by_index(pt_map_aggregated, invert=True)
pcd_hidden.paint_uniform_color([1, 0, 0])    # Red points are hidden points (to be removed).
print("No. of hidden points : ", pcd_hidden)

# Visualizing the visible (blue) and hidden (red) points in the point cloud.
draw_geoms_list = [mesh_coord_frame, pcd_visible, pcd_hidden]
# draw_geoms_list = [mesh_coord_frame, pcd_visible]
# draw_geoms_list = [mesh_coord_frame, pcd_hidden]
o3d.visualization.draw_geometries(draw_geoms_list)

从相同相机视角经所有连续隐藏点去除操作后的点云。聚合的“可见”点(即,点云外表面上的点)为蓝色,而“隐藏”点(即,不在点云外表面上的点)为红色。

让我们再次裁剪点云,看看属于汽车内部的点。

# Cropping the point cloud of visible points using bounding box defined
# earlier to remove its right half (positive Z-axis).
pcd_visible_cropped = pcd_visible.crop(bbox_cropped)

# Cropping the point cloud of hidden points using bounding box defined
# earlier to remove its right half (positive Z-axis).
pcd_hidden_cropped = pcd_hidden.crop(bbox_cropped)

# Visualizing the cropped point clouds.
draw_geoms_list = [mesh_coord_frame, pcd_visible_cropped, pcd_hidden_cropped]
o3d.visualization.draw_geometries(draw_geoms_list)

经所有连续隐藏点去除操作后的裁剪点云,显示了所有属于 3D 汽车模型内部的“隐藏”点,标记为红色。

从上述经隐藏点去除操作后的点云可视化中,我们看到所有属于汽车模型内部(红色)的“隐藏”点现在与位于点云外表面(蓝色)的“可见”点分离开来。

点云转换为数据框

如预期的那样,点云中每个点的位置可以用三个数值来定义 — X、Y 和 Z 坐标。然而,请回忆上面的部分,我们还为 3D 网格中的每个点估计了表面法线。当我们从这个网格中取样点以创建点云时,点云中的每个点还包含与这些表面法线相关的三个附加属性 — X、Y 和 Z 方向的法线单位向量坐标

因此,为了将点云转换为数据框,点云中的每个点可以通过以下七个属性列在单独的行中表示

  1. X 坐标 (浮点数)

  2. Y 坐标 (浮点数)

  3. Z 坐标 (浮点数)

  4. X 方向法向量坐标 (浮点数)

  5. Y 方向法向量坐标 (浮点数)

  6. Z 方向法向量坐标 (浮点数)

  7. 点可见性 (布尔值 True 或 False)

通过运行以下代码行,可以将点云转换为数据框:

# Creating a dataframe for the point cloud with the X, Y & Z positional coordinates
# and the normal unit vector coordinates in the X, Y & Z directions of all points.
pcd_df = pd.DataFrame(np.concatenate((np.asarray(pcd.points), np.asarray(pcd.normals)), axis=1),
                      columns=["x", "y", "z", "norm-x", "norm-y", "norm-z"]
                     )

# Adding a column to indicate whether the point is visible or not using the aggregated
# list of indexes of points from the hidden point removal operation above.
pcd_df["point_visible"] = False
pcd_df.loc[pt_map_aggregated, "point_visible"] = True

这将返回如下所示的数据框,其中每个点都是由上面解释的七个属性列表示的一行。

3D 点云转换为数据框。

保存点云和数据框

点云(隐藏点移除前和后)以及数据框现在可以通过运行以下代码行来保存:

# Saving the entire point cloud as a .pcd file.
pcd_save_path = "data/3d_model.pcd"
o3d.io.write_point_cloud(pcd_save_path, pcd)

# Saving the point cloud with the hidden points removed as a .pcd file.
pcd_visible_save_path = "data/3d_model_hpr.pcd"
o3d.io.write_point_cloud(pcd_visible_save_path, pcd_visible)

# Saving the point cloud dataframe as a .csv file.
pcd_df_save_path = "data/3d_model.csv"
pcd_df.to_csv(pcd_df_save_path, index=False)

3D 模型(上图:完整,下图:裁剪)可视化为 1. 网格,2. 点云和 3. 隐藏点移除后的点云。

就这些!希望本次演示能让你对如何在 Python 中处理 3D 数据有一点清晰的了解。如果你有任何问题或对如何更好地完成这些任务有建议,请告诉我。如果你有需要使用 3D 数据的有趣应用想法,请随时联系我——我很高兴能交流并分享类似的兴趣!同时,也欢迎在 LinkedIn 上与我联系。

本次演示中使用的 3D 汽车模型已从原始文件中稍作修改,以适应本练习的需要。感谢原作者 — “Tesla Model S Plaid”https://skfb.ly/oEqT9)由 ValentunW 创作,使用了 Creative Commons Attribution 许可协议(http://creativecommons.org/licenses/by/4.0/)。

3D 深度学习 Python 教程:PointNet 数据准备

原文:towardsdatascience.com/3d-deep-learning-python-tutorial-pointnet-data-preparation-90398f880c9f

实践教程,深度探讨,3D Python

《终极 Python 指南:为 PointNet 架构训练 3D 深度学习语义分割模型而构建大型 LiDAR 点云》

Florent Poux, Ph.D.Towards Data Science Florent Poux, Ph.D.

·发表于 Towards Data Science ·30 分钟阅读·2023 年 5 月 31 日

--

这张创意插图直观地突出了 3D 深度学习如何以易于分类的方式表现自上而下的场景。如果你喜欢这些,联系Marina Tünsmeyer

近年来,3D 深度学习的应用领域迅速扩展。我们在机器人技术、自动驾驶与地图制作、医学成像和娱乐等各个领域都拥有卓越的应用。看到这些结果时,我们常常感到惊叹(但并非总是如此😁),我们可能会想:“我现在就要在我的应用中使用这个模型!”但不幸的是,噩梦开始了:3D 深度学习的实现。即使新的编码库旨在简化这一过程,实现一个端到端的 3D 深度学习模型仍是一项壮举,尤其是当你孤身一人待在某个阴暗的角落时。

这就是编码 3D 深度学习的感觉。© F. Poux

在 3D 深度学习框架中,最被忽视的痛点之一是将数据准备好以供选定的学习架构使用。我指的不是一个精美的研究数据集,而是一个实际的(混乱的)数据仓库,你想在其上开发应用程序。在大型且复杂的 3D 点云数据集的情况下,这个问题尤为严峻。

哦,你是否明白我们要在这篇文章中探讨什么?你梦到了它(不要隐藏,我知道😉),我们将深入到适当的编码深度。这篇实践教程探讨了如何高效地准备从航空 LiDAR 活动中获得的 3D 点云,以用于最流行的基于点的 3D 深度学习模型:PointNet 架构。

我们涵盖了整个数据准备流程,从 3D 数据整理到特征提取和归一化。它提供了知识和实际的 Python 技能,以解决现实世界的 3D 深度学习问题。

PointNet 数据准备工作流程用于 3D 语义分割。© F. Poux

通过跟随本教程,你将能够将这些技术应用到你自己的 3D 点云数据集上,并利用它们来训练 PointNet 语义分割模型。你准备好了吗?

Theory. 3D Deep Learning Essentials
Step 1\. Preparing the Environment
Step 2\. 3D Data Curation
Step 3\. 3D Data Analysis
Step 4\. 3D Data labelling
Step 5\. Feature Selection
Step 6\. Data Structuration
Step 7\. 3D Python I/O
Step 8\. 3D Data Normalization
Step 9\. 3D Interactive Vizualisation
Step 10\. Tensor Creation

🎵读者注意:本实践指南是与我的亲爱的同事* UTWENTE 合作的一部分 桑德·奥德·埃尔伯林克教授。我们感谢来自数字双胞胎 @ITC 项目的财务支持,该项目由特温特大学 ITC 学院资助。

3D 深度学习要点

3D 语义分割 VS 分类

3D 语义分割和 3D 点云分类的根本区别在于,分割旨在为点云中的每个点分配标签。而分类则试图为整个点云分配一个标签。

分类模型与语义分割模型之间的区别。在这两种情况下,我们都传递一个点云,但对于分类任务,整个点云是一个实体,而在语义分割的情况下,每个点是一个需要分类的实体。© F. Poux

例如,使用 PointNet 架构,3D 点云分类涉及将整个点云通过网络,并输出一个代表整个点云的标签。相比之下,语义分割的“头部”将为云中的每个点分配一个标签。方法的不同在于,分割需要对表示的 3D 空间有更详细的理解,因为它试图识别和标记点云中的个体对象或区域。相比之下,分类仅需要对点云的整体形状或组成有较高层次的理解。

总的来说,尽管 3D 语义分割和分类是分析 3D 点云数据的关键任务,但主要区别在于标记过程所需的详细程度和粒度。

正如你猜到的,我们将攻克语义分割,因为它需要对被分析的空间有更详细的理解,这非常有趣 😁。

不过在此之前,让我们稍微回顾一下,以更好地理解 PointNet 架构的工作原理,好吗?

PointNet:一种基于点的 3D 深度学习架构

将复杂的主题拆解成小块知识是我的专长。但是我必须承认,当涉及到 3D 深度学习时,通过神经网络中不同过程学到的函数的复杂性以及超参数确定的经验性特征是重要的挑战。要克服这些挑战,嗯?😁

首先,让我们回顾一下 PointNet 是什么。PointNet 是 3D 深度学习中神经网络的开创者之一。如果你理解了 PointNet,你就可以使用所有其他高级模型。但是,当然,理解只是方程的一部分。

PointNet 架构能够处理三种语义应用:分类、部件分割和语义分割。© F. Poux

另一部分是让这些复杂的东西发挥作用,并将其扩展到你的数据上!这是一项具有挑战性的任务!即使对于经验丰富的编码员也是如此。因此,我们将这个过程分为几个部分。今天,我们讨论的是准备数据,以确保我们在实际条件下有用的东西。

为了正确准备数据,理解网络的构建块是至关重要的。下面我将介绍在使用网络准备数据时需要考虑的关键方面。

论文作者描述的 PointNet 模型架构:ArXiv 论文

PointNet 的架构由几个处理点云数据的神经网络层组成。PointNet 的输入是一个简单的点集,每个点由其 3D 坐标和附加特征(如颜色或强度)表示。这些点被输入到连续的共享多层感知器(MLP)网络中,网络学习从每个点中提取局部特征。

论文作者描述的 PointNet 架构中的 MLP:ArXiv 论文

🦚 注意MLP 是一个由多个层连接的节点或神经元构成的神经网络。MLP 中的每个神经元从上一层的神经元接收输入,利用权重和偏置对该输入进行变换,然后将结果传递给下一层的神经元。MLP 中的权重和偏置在训练过程中通过反向传播学习,以最小化网络预测值与真实输出之间的差异。

这些 MLP 是全连接层,每一层后面跟着我们称之为“非线性激活函数”(如 ReLU)。每层的神经元数量(例如 64)和层数(例如 2)可以根据具体任务和输入点云数据的复杂性进行调整。正如你所猜测的,神经元和层数越多,目标问题可能越复杂,因为架构的可塑性带来了组合可能性。如果我们继续深入研究 PointNet 架构,我们会看到我们用 1024 个特征描述原始的 n 个输入点,这些特征从最初提供的(X、Y 和 Z)中延展出来。这是架构通过使用最大池化操作对局部学习特征进行全局描述的地方,从而获得一个总结整个点云的全局特征向量。然后,这个全局特征向量会通过若干全连接层来生成分类头的最终输出,即 k 类的评分。

由论文作者描述的 PoinNet 模型架构的 MaxPool 和 MLP: ArXiv Paper

如果你仔细观察,PointNet 中的语义分割头是一个全连接网络,它将全局特征向量和局部特征向量串联在一起,为输入点云数据中的每个点生成一个每点评分或标签。语义分割头由若干全连接层、ReLU 激活函数和一个最终的 softmax 层组成。最终 softmax 层的输出代表不同语义标签或类别的每点概率分布。

由论文作者描述的 PoinNet 模型架构的分割头: ArXiv Paper

PointNet 架构能够捕捉任务如 3D 数据中的对象分类和分割所需的重要几何和上下文信息,通过从输入点云中的每个点学习局部和全局特征。PointNet 的一个关键创新是在最大池化操作中使用对称函数,这确保了输出对输入点的顺序不变。这使得 PointNet 对输入点顺序的变化具有鲁棒性,这在 3D 数据分析中至关重要。

现在,我们准备全力以赴地为 PointNet 准备数据。最开始我们指的是什么点云?我们是否输入一个完整的点云?

PointNet: 数据准备的关键方面

从高层次来看,如果我们研究原始论文,我们可以看到 PointNet 的功能非常直接:

  • 我们将点云数据规范化到标准空间。

  • 我们计算一系列特征(不依赖于我们已有的知识,而是利用网络的能力来创建有用的特征)

  • 我们将这些特征汇聚成考虑中的点云的全局特征。

  • 选项 1:我们使用这个全局特征来对点云进行分类

  • 选项 2:我们将这个全局特征与局部特征结合,构建更精确的语义分割特征。

PointNet 在语义分割或分类任务中的五个步骤。© F. Poux

一切都围绕特征展开,这意味着我们提供给网络的块应该非常相关。例如,给出整个点云是行不通的,给出一个微小的样本也是行不通的,提供具有不同分布的结构化样本也行不通。那么我们怎么做呢?

让我们遵循一个线性的十步流程,以获得经过深思熟虑的 3D 点云训练/推理准备数据集。

PointNet 数据准备工作流程。© F. Poux

第一步:准备你的工作环境

在本文中,我们使用两个主要组件:CloudCompare 和 JupyterLab IDE (+ Python)。对于最佳设置的详细视图,我强烈建议你参考这篇文章,它详细介绍了所需内容:

## 3D Python 工作流程用于 LiDAR 城市模型:逐步指南

解锁 3D 城市建模应用程序的终极指南。该教程涵盖了 Python…

[towardsdatascience.com

我们将有一个特定的库栈,分为主要库、绘图库和实用库。

🦚 注意如果你在本地环境中工作,我建议本教程使用 pip 进行包管理(pip install library_name)

我们将使用的两个主要库是 NumPy 和 Pytorch:

  • Numpy:NumPy 是一个用于处理数值数据的 Python 库,它提供了操作数组和矩阵、数学运算和线性代数函数的功能。

  • Pytorch:Pytorch 是一个流行的 Python 深度学习框架。它提供了构建和训练神经网络以及优化和评估模型的工具。

然后,我们使用两个绘图库来支持这些:

  • Matplotlib:Matplotlib 是一个用于创建可视化图表、图形和图像的 Python 库。

  • Plotly:Plotly 是一个用于创建交互式可视化的 Python 库。

最后,我们还将使用三个实用模块:

  • os: Python 中的 os 模块提供了一种使用操作系统相关功能的方法。它提供了与文件系统交互的函数,例如创建、删除和重命名文件和目录。

  • glob: Python 中的 glob 模块提供了一种使用模式匹配文件和目录的方法。例如,它可以在目录中找到所有具有特定扩展名的文件。

  • random(可选):random库是一个内置模块,提供生成随机数、从列表中选择随机项和打乱序列的函数。

一旦完成,我们就可以进入第二个方面:获取新的 3D 点云数据集!

步骤 2:3D 数据整理

对于本教程,我们前往荷兰东部,靠近恩斯赫德,那里有特温特大学的光芒🌞。在这里,我们选择了 AHN4 数据集的一部分,这部分数据应该有足够的树木、地面、建筑物以及一点水 🚿。我们可以说每个类别都有足够的点!

🦚 注意: 我们将在不平衡的数据集上进行训练,其中地面点的比例远高于其他类别。这不是理想的情况,在这种情况下,MLP 和语义分割头可能会偏向于预测多数类别标签,而忽略少数类别标签。这可能导致不准确的分割和少数类别点的错误分类。不过,可以使用几种技术来减轻不平衡类别的影响,如数据增强、少数类别的过采样或欠采样,以及使用加权损失函数。这是另一个话题。 😉

为了收集数据集,我们访问开放数据门户 geotiles.nl。我们缩放到一个感兴趣的区域,等待有 _XX(以便数据量一致),然后下载.laz 数据集,如下所示:

从荷兰 AHN4 LiDAR 活动中收集点云数据集。© F. Poux

此外,我们可以准备一些引人注目的用例,以便你以后可以在感兴趣的切片上测试你的模型。例如,可以在你所在的地方,如果那里有开放数据。

🦚 注意: 如果你想对你的模型进行真正的挑战,下载另一个地区的切片是一个很好的泛化测试!例如,你可以下载一个 LiDAR HD 点云切片,以查看如果用于训练或测试,是否会有差异/改进。

现在你已经有了.laz 文件格式的点云,让我们探索 info 文件提供的特性,你也可以查看或下载:

一份关于选定 3D LiDAR 点云数据集的信息文件。© F. Poux

这有助于深入理解数据内容,这是构建高质量数据集时至关重要的第一步。

这展示了点云的附加信息内容。© F. Poux

在浏览各种信息点时,有几个字段值得注意:

number of point records:    32080350
offset x y z:               205980 464980 0
min x y z:                  205980.000 464980.000 4.101
max x y z:                  207019.999 466269.999 53.016
intensity          56       5029
classification      1         26
Color R 17 255
      G 39 255
      B 31 255
    NIR 0 255

这个小文件选择提示我们将处理大约 3200 万数据点,这些数据点具有颜色、强度,并且如果我们希望稍后提升模型,还可以具有近红外字段。

非常好!现在我们已经安装了软件堆栈并下载了 3D 点云,我们可以进行 3D 数据分析,以确保输入到模型中的数据符合预期。

步骤 3:3D 数据分析(CloudCompare)

现在是时候将 3D 航空点云文件加载到软件CloudCompare中了。首先,在计算机上打开 CloudCompare,直到出现空的 GUI,如下所示。

CloudCompare 的 GUI。来源:learngeodata.eu

从那里,我们通过拖放的方式加载下载的.laz 文件,并在导入时从弹出的菜单中选择一些属性,如下图所示。

在 CloudCompare 中导入 3D 点云。我们确保选择相关特征进行加载。© F. Poux

🦚 注意我们取消选择所有字段以预选一些带来不相关或低相关信息的特征以及每个点的标签,这些标签可能指示真实情况。因此,我们只保留强度和分类字段。确实,由于我们针对的是航空点云,我们希望选择能够高效泛化的特征。因此,我们的目标是选择在未标记的数据中可能找到的特征,以便我们后续的模型能够在这些数据上进行表现。此外,点云还具有 RGB 信息,这也是一个不错的选择。

在这一阶段,个选定的特征如下:X、Y 和 Z(空间),R、G、B(辐射计),以及强度。此外,我们保留来自.laz 文件分类字段的 AHN4 标签。成功将 3D 航空点云导入 CloudCompare 后,我们就准备好进行分析和可视化了。我们可以快速查看“对象属性面板(3)”中的两个额外字段(强度和分类)。如果我们研究强度,会发现一些离群点稍微偏移了我们的特征向量,如下所示。

强度着色的点云及其分布直方图。© F. Poux

如果我们想将其用作 PointNet 的输入特征,这是我们必须解决的第一个观察点。

关于颜色值(红色、绿色、蓝色),它们是从另一个传感器获得的,可能是在另一个时间。因此,由于它们是从该区域的现有正射影像中合成的,我们可能会遇到一些精度/重投影问题。但正如你所想,能够将绿色元素与红色元素分开应该能给我们一个清晰的指示,表明一个点属于植被类别的概率😁。

LiDAR 数据集使用正射影像着色以获取 R、G、B 特征。© F. Poux

我们有一个点云,其中包含 3200 万个点,以笛卡尔坐标系(X,Y,Z)表示,每个点都有强度特征和颜色(红色、绿色和蓝色)。

🦚 注意你可以将这个阶段留到后面,因为你可能会有许多选择的特征,例如下面所示的近红外(NIR)通道,它在数据集中是可用的。例如,这是一个方便的字段,可以突出显示健康(或不健康)的植被。 😉

近红外特征。© F. Poux

如果你滚动可用的字段,我们还有另一个最后的标量字段。分类字段,当然!这对于帮助我们创建标注数据集非常方便,以避免从零开始(感谢开放数据!👌)

提供的分类。© F. Poux

🦚 注意出于教学培训的目的,我们将考虑将分类作为教程剩余部分的真实情况。然而,请知道分类是有一定不确定性的,如果你想要最好的模型,必须修正它。确实,有一句著名的 3D 深度学习格言:垃圾进=垃圾出。因此,数据的质量应该是首要的。

让我们专注于精炼标注阶段。

第 4 步:3D 数据标注(标签连接)

好的,在进入这个步骤之前,我必须说点什么。3D 点云标注用于训练有监督的 3D 语义分割学习模型是一个(痛苦的)手动过程。目标是为 3D 点云中的单个点分配标签。这个过程的主要关键目标包括识别点云中的目标物体、选择合适的标注技术,并确保标注过程的准确性。

标注过程的一个示例:标注簇与标注单个点。© F. Poux

要识别点云中需要标注的物体或区域,我们可以手动检查云,或者使用基于特征(如大小、形状或颜色)自动检测特定物体或区域的算法。

[## 3D Academy - 点云在线课程

为教师、研究人员、开发人员和工程师提供的最佳 3D 在线课程。掌握 3D 点云处理及……

learngeodata.eu

在我们的案例中,我们有一个优势:点云已经被分类。因此,第一步是将每个类别提取为独立的点云,如下图所示。

首先,我们选择点云并将“颜色”属性从 RGB 切换到标量场。然后确保我们可视化分类标量场。从那里,我们转到 EDIT > Scalar Field > Split Cloud by Integer Value,从而在点云中为每个类别生成一个点云。

从我们得到的各种点云类别中,我们可以看到:

class 1 = vegetation + clutter
class 2 = ground
class 6 = buildings
class 9 = water
class 26 = bridge. 

从那里,我们可以重新处理class 1 = vegetation + clutter

必须根据特定任务和可用数据选择合适的标记技术。例如,我们可以使用无监督技术进行更多的探索性分析,并通过选择植被中的候选点来进行一些颜色阈值处理,如下图所示。

根据颜色信息对点云进行分割,以半自动化的方式创建更精确的标签。© F. Poux

这将给出不准确的结果,但可能会加快手动选择属于植被的任何点。

最后,确保标签过程的准确性对于产生可靠结果至关重要。这可以通过手动验证或质量控制技术,如交叉验证或标注者一致性,来实现。

🦚 注意了解术语是好的,但不要害怕。这些概念可以在后续阶段覆盖。一件事一次完成。 😉

最终,标签过程的准确性将直接影响后续任务的表现,包括 3D 语义分割。在我们的案例中,我们将数据组织如下:

Class 1 = ground
Class 2 = Vegetation
Class 3 = Buildings
Class 4 = Water
Class 0 = unannotated (All the remaining points)

我们在 CloudCompare 中执行此操作。

在 CloudCompare 中组织各种类别。© F. Poux

在为清晰度重命名我们的不同点云(初始化)后,我们将(1)将杂乱物体合并为一个单独的点云,(2)删除所有点云的分类字段,(3)用新的编号重新创建分类字段,(4)克隆所有点云,(5)合并克隆点云,如下所示。

初步准备我们的标签进入新的点云。© F. Poux

数据准备阶段在 CloudCompare 中执行。© F. Poux

现在,我们有一个带标签的数据集,并具有特定的点标签分布。

我们注意到,在 32,080,350 个点中,23,131,067 个属于地面(72%),7,440,825 个属于植被(23%),1,146,575 个属于建筑物(4%),191,039 个属于水体(不到 1%),剩余的 170,844 个未标记(类别 0)。这将非常有趣,因为我们处于这个特定的不平衡情况中,具有主导类别。

现在我们已经分析了点云的内容并细化了标签,我们可以深入进行特征选择。

第 5 步。3D 特征选择

在使用 PointNet 架构进行 3D 点云语义分割时,特征选择对于准备训练数据至关重要。

在传统机器学习方法中,通常需要特征工程来选择和提取数据中的相关特征。然而,使用 PointNet 等深度学习方法可以避免这一步骤,因为模型可以自动学习从数据中提取特征。

然而,确保输入数据包含模型学习相关和推导特征所需的信息仍然很重要。我们使用七个特征:XYZ(空间属性)、RGB(辐射属性)和强度I(激光雷达衍生)。

X                Y              Z          R  G   B  INTENSITY
205980.49800000 465875.02398682 7.10500002 90 110 98 1175.000000
205980.20100001 465875.09802246 7.13500023 87 107 95 1115.000000
205982.29800010 465875.00000000 7.10799980 90 110 98 1112.000000

这是我们的参考。这意味着我们将使用这个输入来构建模型,任何我们希望用训练好的 PointNet 模型处理的其他数据集必须包含这些相同的特征。在进入 Python 之前,最后一步是根据架构规范结构化数据。

第 6 步。数据结构化(瓦片化)

由于几个原因,在使用神经网络架构 PointNet 处理 3D 点云时,将其结构化为正方形瓦片是必要的。

在此工作流中的瓦片定义是训练 PointNet 3D 深度学习架构。© F. Poux

首先,PointNet 要求输入数据为固定大小,这意味着所有输入样本应具有相同数量的点。通过将 3D 点云分割成正方形瓦片,我们可以确保每个瓦片具有更均匀的点数量,使 PointNet 能够一致有效地处理它们,而不会在采样到最终固定点数时产生额外开销或不可逆损失。

采样策略对 3D 点云数据集的影响示例。© F. Poux

🌱 增长使用 PointNet 时,我们需要将输入瓦片固定为推荐的 4096 个点。这意味着需要一种采样策略(CloudCompare 中未实现)。如上图所示,使用不同策略对点云进行采样将产生不同的结果和物体识别能力(例如右侧的电线杆)。你认为这会影响 3D 深度学习架构的性能吗?

其次,PointNet 的架构涉及应用于每个点的共享多层感知机(MLP),这意味着网络在处理每个点时是独立的,不考虑其邻居。通过将点云结构化为瓦片,我们可以在保持每个点局部上下文的同时,让网络独立处理点,从而从数据中提取有意义的特征。

生成的 3D 点云瓦片。© F. Poux

最后,将 3D 点云结构化为瓦片也可以提高神经网络的计算效率,因为它允许对瓦片进行并行处理,从而减少分析整个点云所需的总体处理时间(在 GPU 上)。

我们使用“横截面”工具(1)来实现这一目标。我们将大小设置为 100 米(2),然后沿 X 轴和 Y 轴(负方向)移动,以尽可能接近初始瓦片的最底部角落(3),我们使用多个切片按钮(4),沿 X 轴和 Y 轴重复(5),得到最终的方形瓦片(6),如下所示。

自动化瓦片创建过程在 CloudCompare 中。© F. Poux

自动化瓦片创建过程在 CloudCompare 中。© F. Poux

这允许定义大约一百米乘一百米的瓦片,沿 X 轴和 Y 轴。我们获得了 143 个瓦片,其中抛弃了最后 13 个瓦片,因为它们可能更能代表我们希望输入的内容(即,它们不是方形,因为它们位于边缘)。在剩下的 130 个瓦片中,我们选择了大约 20%具有代表性的瓦片(按住 Shift + 选择),如下所示。

对 PointNet 进行的选择和手动分割训练集和测试集。© F. Poux

🌱 增长我们按照 80/20 的比例将数据分为训练集和测试集。在这个阶段,你怎么看这种方法?你认为什么样的策略比较好?

在这个过程中,我们在训练集和测试集中分别拥有约 100 个瓦片和 30 个瓦片,每个瓦片都保留了原始点数。然后,我们选择一个文件夹,将每个瓦片导出为 ASCII 文件,如下所示。

将点云瓦片导出以供 PointNet 使用

🦚 注意CloudCompare 允许在选择以 ASCII 文件格式导出时,将所有点云独立导出到一个目录中。它会在最后一个字符之后自动缩进,使用“*_*”字符以确保一致性。这非常方便,可以使用/滥用。

将 3D 点云结构化为方形瓦片是使用 PointNet 时的一个重要预处理步骤。它允许输入数据大小的一致性,保留局部上下文,并提高计算效率,这些都有助于更准确和高效的数据处理。这是进入 3D Python 🎉之前的最后一步。

第 7 步。3D Python 数据加载

现在是时候在 Python 中处理点云瓦片了。

为此,我们导入所需的库。如果你使用的是可以在这里访问的 Google Colab 版本:💻 Google Colab Code,那么重要的是要运行下面所示的第一行:

from google.colab import drive
drive.mount('/content/gdrive')

对于任何设置,我们必须导入下述各种库:

#Base libraries
import numpy as np
import random
import torch
#Plotting libraries
%matplotlib inline
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
import plotly
import plotly.graph_objects as go
#Utilities libraries
from glob import glob
import os

太好了!从这里开始,我们将数据文件名拆分到各自的文件夹中,pointcloud_train_filespointcloud_test_files

#specify data paths and extract filenames
project_dir="gdrive/My Drive/_UTWENTE/DATA/AHN4_33EZ2_12"
pointcloud_train_files = glob(os.path.join(project_dir, "train/*.txt"))
pointcloud_test_files = glob(os.path.join(project_dir, "test/*.txt"))

🦚 注意我们在资源管理器中有两个文件夹:训练文件夹和测试文件夹,均在 AHN4_33EZ2_12 文件夹中。我们在这里做的是首先指定根文件夹的路径,然后用 glob 收集训练和测试中的所有文件,使用 *** 表示“选择所有”。一种处理多个文件的便捷方法!

在这一步,两个变量保存了我们准备好的所有瓦片的路径。为了确保这一点,我们可以打印从 0 到 20 的随机分布中随机选取的一个元素:

print(pointcloud_train_files[random.randrange(20)])
>> gdrive/My Drive/_UTWENTE/DATA/AHN4_33EZ2_12/train/AHN4_33EZ2_12_train_000083.txt

太好了,所以我们可以进一步将数据集拆分成三个变量:

  • valid_list:这保存了验证数据路径。验证拆分通过在每个训练周期后微调模型来帮助提高模型性能。

  • train_list:这保存了训练数据路径,即用于训练的数据集。

  • test_list:这保存了测试数据路径。测试集告知我们模型在完成训练阶段后的最终准确性。

这是通过友好的 numpy 函数完成的,这些函数作用于列表中的数组索引。实际上,我们随机提取了pointcloud_train_files中的 20%,然后将保留的部分与未保留的部分进行分割,后者构成了train_list变量。

#Prepare the data in a train set, a validation set (to tune the model parameters), and a test set (to evaluate the performances)
#The validation is made of a random 20% of the train set.
valid_index = np.random.choice(len(pointcloud_train_files),int(len(pointcloud_train_files)/5), replace=False)
valid_list = [pointcloud_train_files[i] for i in valid_index]
train_list = [pointcloud_train_files[i] for i in np.setdiff1d(list(range(len(pointcloud_train_files))),valid_index)]
test_list = pointcloud_test_files
print("%d tiles in train set, %d tiles in test set, %d files in valid list" % (len(train_list), len(test_list), len(valid_list)))

然后,我们通过查看中位数、标准差和最小-最大值来随机研究一个数据文件的属性,使用以下代码片段:

tile_selected=pointcloud_train_files[random.randrange(20)]
print(tile_selected)
temp=np.loadtxt(tile_selected)
print('median\n',np.median(temp,axis=0))
print('std\n',np.std(temp,axis=0))
print('min\n',np.min(temp,axis=0))
print('max\n',np.max(temp,axis=0))

这将产生:

gdrive/My Drive/_UTWENTE/DATA/AHN4_33EZ2_12/train/AHN4_33EZ2_12_train_000083.txt 
median [2.068e+05 4.659e+05 6.628e+00 1.060e+02 1.210e+02 1.030e+02 1.298e+03 1.000e+00] 
std [ 28.892 30.155 0.679 29.986 21.4 17.041 189.388 0.266] 
min [2.068e+05 4.659e+05 5.454e+00 3.600e+01 6.200e+01 5.700e+01 7.700e+01 0.000e+00] 
max [2.068e+05 4.660e+05 1.505e+01 2.510e+02 2.470e+02 2.330e+02 1.625e+03 4.000e+00]

正如我们所注意到的,有一个核心问题需要解决:数据归一化。确实,为了避免任何不匹配,我们需要处于这种“规范空间”,这意味着我们可以在特征空间中复制相同的实验环境。使用 T-Net 就像用火箭筒🪰杀苍蝇。这是可以的,但如果我们可以避免并使用实际一致的方法,那将更聪明😁。

步骤 8. 3D Python 归一化

在将 3D 点云瓦片输入到 PointNet 架构之前进行归一化至关重要,主要有三个原因。首先,归一化确保输入数据围绕原点中心,这对于 PointNet 的架构至关重要,因为它对每个点独立应用 MLP。当输入数据围绕原点中心时,MLP 更有效,这使得特征提取更有意义,整体性能更好。

归一化对训练 3D 深度学习模型结果的影响示意图。© F. Poux

🌱 成长在盲目归一化之前,有些直觉也是有益的。例如,我们主要使用基于重力的场景,这意味着 Z 轴几乎总是与 Z 轴共线。因此,你会如何处理这种归一化?

其次,归一化将点云数据缩放到一致的范围,这有助于防止 MLP 中的激活函数饱和。这使得网络能够从整个输入值范围中学习,提高了准确分类或分割数据的能力。

在 3D 点云的强度场上说明[0,1]缩放的问题。© F. Poux

最后,归一化有助于减少点云数据中不同尺度的影响,这可能是由于传感器分辨率或扫描物体的距离差异(在航拍 LiDAR 数据中有所平坦化)。这提高了数据的一致性和网络从中提取有意义特征的能力。

好的,让我们开始吧。对于我们的实验,我们将首先捕捉特征的最小值min_f和平均值mean_f

cloud_data=temp.transpose()
min_f=np.min(cloud_data,axis=1)
mean_f=np.mean(cloud_data,axis=1)

🦚 注意我们对数据集进行了转置,以更高效、便捷地处理数据和索引。因此,要获取点云的 X-axis 元素,我们可以直接使用 cloud_data[0] 而不是 cloud_data[:,0],这样可以减少一些开销。

我们现在将对不同的特征进行归一化,以用于我们的 PointNet 网络。首先是空间坐标 X、Y 和 Z。我们将把数据中心化到平面坐标轴(X 和 Y),并确保减去 Z 的最小值,以区分屋顶和地面,例如:

n_coords = cloud_data[0:3]
n_coords[0] -= mean_f[0]
n_coords[1] -= mean_f[1]
n_coords[2] -= min_f[2]
print(n_coords)

很好,现在我们可以通过确保我们在[0,1]范围内来缩放我们的颜色。这是通过将所有颜色的最大值(255)进行除法实现的:

colors = cloud_data[3:6]/255

最后,我们将处理强度特征的归一化。在这里,我们将使用分位数来获得对异常值具有鲁棒性的归一化,就像我们在探索数据时看到的那样。这是通过三个阶段的过程完成的。首先,我们计算四分位差IQR,即第 75 个分位数和第 25 个分位数之间的差值。然后我们从所有观测值中减去中位数,并除以四分位差。最后,我们减去强度的最小值,以获得显著的归一化:

# The interquartile difference is the difference between the 75th and 25th quantile
IQR = np.quantile(cloud_data[-2],0.75)-np.quantile(cloud_data[-2],0.25)
# We subtract the median to all the observations and then divide by the interquartile difference
n_intensity = ((cloud_data[-2] - np.median(cloud_data[-2])) / IQR)
#This permits to have a scaling robust to outliers (which is often the case)
n_intensity -= np.min(n_intensity)
print(n_intensity)

太棒了!在这一阶段,我们已经有了一个归一化的点云,准备好输入 PointNet 架构。但是自动化这个过程是执行所有瓦片的下一步逻辑步骤。

创建一个点云瓦片加载和归一化函数

我们创建一个函数cloud_loader,它以一个瓦片路径tile_path和一个用于的特征字符串features_used作为输入,并输出一个cloud_data变量,其中包含归一化特征,以及一个真实标签变量gt,其中包含每个点的标签。该函数将如下操作:

定义一个云加载函数来处理点云数据集,并使其准备好用于训练。© F. Poux

这转换为一个简单的cloud_loader函数,如下所示:

# We create a function that loads and normalize a point cloud tile
def cloud_loader(tile_path, features_used):
  cloud_data = np.loadtxt(tile_path).transpose()
  min_f=np.min(cloud_data,axis=1)
  mean_f=np.mean(cloud_data,axis=1)
  features=[]
  if 'xyz' in features_used:
    n_coords = cloud_data[0:3]
    n_coords[0] -= mean_f[0]
    n_coords[1] -= mean_f[1]
    n_coords[2] -= min_f[2]
    features.append(n_coords)
  if 'rgb' in features_used:
    colors = cloud_data[3:6]/255
    features.append(colors)
  if 'i' in features_used:
    IQR = np.quantile(cloud_data[-2],0.75)-np.quantile(cloud_data[-2],0.25)
    n_intensity = ((cloud_data[-2] - np.median(cloud_data[-2])) / IQR)
    n_intensity -= np.min(n_intensity)
    features.append(n_intensity)

  gt = cloud_data[-1]
  gt = torch.from_numpy(gt).long()

  cloud_data = torch.from_numpy(np.vstack(features))
return cloud_data, gt

该函数现在用于获取点云特征和标签,如下所示:

pc, labels = cloud_loader(tile_selected, ‘xyzrgbi’)

🌱 成长如你所见,我们传递了一个特征的字符串。这对于我们不同的‘*if*’测试非常方便。然而,请注意,如果传递给函数的内容不符合预期,我们不会返回错误。这不是标准的代码实践,但这扩展了本教程的范围如果你想开始编写漂亮的代码,我建议查看 PEP-8 指南。

步骤 9. 3D Python 交互式可视化

如果我们想要平行于以前的文章,可以在这里访问:

## LiDAR 城市模型的 3D Python 工作流:逐步指南

解锁 3D 城市建模应用的终极指南。该教程涵盖 Python…

[towardsdatascience.com

我们可以使用 Open3D 可视化我们的数据集。首先,我们需要安装一个特定版本(如果在 Jupyter Notebook 环境中工作,如 Google Colab 或 CRIB 平台),并在我们的脚本中加载它:

!pip install open3d==0.16
import open3d as o3d

🦚 注意在 pip 之前的“!”是在 Google Colab 上工作时使用的,表示它应该直接使用环境控制台。如果你在本地工作,应删除此字符并直接使用 pip install open3d==0.16

然后我们执行以下连续步骤:

绘制函数以直接在 Google Colab 中绘制交互式 3D 场景。© F. Poux

这转化为以下代码行:

pc, gt = cloud_loader(tile_selected, ['xyz','rgb','i'])
pcd=o3d.geometry.PointCloud()
pcd.points=o3d.utility.Vector3dVector(np.array(pc)[0:3].transpose())
pcd.colors=o3d.utility.Vector3dVector((np.array(pc)[3:6]).transpose())
o3d.visualization.draw_plotly([pcd],point_sample_factor=0.5, width=600, height=400)

🦚注意由于我们的 pc 变量捕获了 cloud_data 来自 cloud_loader 函数的输出被转置,我们在使用 open3d 绘图时必须记得将其转置回去。

上述代码片段将输出以下可视化:

使用 plotly 绘制场景和 R、G、B 字段的结果。© F. Poux

🦚 注意使用 draw_plotly 函数时,我们无法直接控制图表的缩放,并且可以注意到 *X*, *Y,* *Z*的比例不等,这在这种情况下强调了 Z。 😁

由于你可以注意到的限制,我们创建了一个自定义可视化函数来可视化随机切片,以便运行函数:visualize_input_tile 输出一个交互式的 plotly 可视化,让我们切换渲染模式。

要测试提供的函数,我们首先需要在实验中定义类名称:class_names = [‘unclassified’, ‘ground’, ‘vegetation’, ‘buildings’, ‘water’]。然后,我们提供云特征cloud_features=’xyzi’,随机选择变量selection中捕获的点云,并可视化切片。这转化为以下代码片段:

class_names = ['unclassified', 'ground', 'vegetation', 'buildings', 'water']
cloud_features='xyzi'
selection=pointcloud_train_files[random.randrange(len(pointcloud_train_files))]
visualize_input_tile(selection, class_names, cloud_features, sample_size=20000)

这将输出如下交互式场景。

在 Google Colab 中的交互式 3D 场景。© F. Poux

🦚 注意你可以使用按钮在特征强度和从加载的感兴趣特征的标签之间切换渲染模式。

我们有一个工作解决方案用于加载、规范化和可视化 Python 中的单个切片。最后一步是创建我们称之为张量的内容,以用于 PointNet 架构。

第 10 步。张量创建

我想向你展示如何使用 PyTorch 进行初步操作。为清晰起见,让我快速定义我们使用这个库时操作的主要 Python 对象类型:张量。

PyTorch 张量是一个多维数组,用于在 PyTorch 中存储和操作数据。它类似于 NumPy 数组,但具有针对深度学习模型优化的额外好处。张量可以使用 torch.tensor() 函数创建,并用数据初始化,或者创建一个具有指定形状的空张量。例如,要创建一个 3x3 的张量并填充随机数据:

import torch

x = torch.tensor([[1.0, 2.0, 3.0],
                  [4.0, 5.0, 6.0],
                  [7.0, 8.0, 9.0]])

print(x)

这将输出:

tensor([[1., 2., 3.],
        [4., 5., 6.],
        [7., 8., 9.]])

相当简单,对吧?现在,为了简化操作,还有一个小型的 Pytorch 库,我们可以用来准备数据集列表。这个库叫做TorchNetTorchNet 旨在通过提供一组预定义的模块和助手函数来简化构建和训练复杂神经网络架构的过程,这些模块和助手函数适用于数据加载、验证和测试等日常任务。

TorchNet 的主要优势之一是其模块化设计,允许用户通过组合一系列预构建模块来轻松构建复杂的神经网络架构。这可以节省大量时间和精力,相比从头开始构建神经网络,尤其是在刚接触深度学习时。

🦚 注意除了其模块化设计外,TorchNet 还提供了多个用于常见深度学习任务的助手函数,例如数据增强、早期停止和模型检查点。这可以帮助用户获得更好的结果,并更高效地优化他们的神经网络架构

要安装torchnet版本0.0.4并将其导入到我们的脚本中,我们可以执行以下操作:

!pip install torchnet==0.0.4
import torchnet as tnt

我们还导入了另一个名为 functools 的实用模块。该模块用于处理或返回其他函数的高阶函数。为此,将 import functools 添加到导入语句中。

通常,任何可调用的对象都可以被视为此模块的函数。通过这些额外的设置,可以使用以下四行代码轻松生成训练集、验证集和测试集:

cloud_features='xyzrgbi'
test_set = tnt.dataset.ListDataset(test_list,functools.partial(cloud_loader, features_used=cloud_features))
train_set = tnt.dataset.ListDataset(train_list,functools.partial(cloud_loader, features_used=cloud_features))
valid_set = tnt.dataset.ListDataset(valid_list,functools.partial(cloud_loader, features_used=cloud_features))

现在,如果我们想要探索,可以使用像经典的 numpy 数组一样的索引来检索特定位置的张量,例如train_set[1],其输出为:

最后,我们必须将结果保存到 Python 对象中,以便在接下来的步骤中直接使用,例如 PointNet 训练。我们使用的库是 pickle,它非常适合保存 Python 对象。要保存一个对象,只需运行以下命令:

import pickle
f = open(project_dir+"/data_prepared.pckl", 'wb')
pickle.dump([test_list, test_set, train_list, train_set, valid_list, valid_set], f)
f.close()

如果你想测试你的设置,还可以运行以下代码行,确保你检索到你想要的内容:

f = open(project_dir+"/data_prepared.pckl", 'rb')
test_list_t, test_set_t, train_list_t, train_set_t, valid_list_t, valid_set_t = pickle.load(f)
f.close()
print(test_list_t)

🔮 结论

恭喜!在这个实践教程中,我们探讨了准备用于 PointNet 架构的 3D 点云数据的关键步骤。

PointNet 的 3D 深度学习数据准备工作流程。© F. Poux

通过遵循这个逐步指南,你已经学会了如何清理、处理 LiDAR 点云,提取相关特征,并为 3D 深度学习模型规范化数据。我们还讨论了一些处理 3D 点云数据的关键注意事项,如瓦片大小、规范化和数据增强。你可以将这些技术应用于你的 3D 点云数据集,并用它们来训练和测试 PointNet 模型,用于对象分类和分割。3D 深度学习领域正在快速发展,这个教程是一个基石,为你进一步探索这一激动人心的领域提供了坚实的基础。

🤿 进一步探索

但学习之旅并未止步于此。我们的终身探索才刚刚开始,未来的步骤将深入探讨 3D 体素工作、3D 数据的人工智能、语义分析和数字双胞胎。此外,我们将使用深度学习技术分析点云,解锁高级 3D LiDAR 分析工作流程。还有很多令人兴奋的内容!

参考文献

  1. Qi, C. R., Su, H., Mo, K., & Guibas, L. J. (2017). Pointnet:点集上的深度学习用于 3D 分类和分割。收录于 IEEE 计算机视觉与模式识别会议论文集 (第 652–660 页)。

  2. Poux, F., & Billen, R. (2019). 基于体素的 3D 点云语义分割:无监督几何和关系特征与深度学习方法。ISPRS 国际地理信息学杂志, 8(5), 213。

  3. Xu, S., Vosselman, G., & Elberink, S. O. (2014). 基于多实体的城市区域航空激光扫描数据分类。ISPRS 摄影测量与遥感杂志, 88, 1–15。

使用 DeepSDF 进行 3D 生成建模

原文:towardsdatascience.com/3d-generative-modeling-with-deepsdf-2cd06f1ec9b3

简单的神经网络可以捕捉复杂的 3D 几何形状

Cameron R. Wolfe, Ph.D.Towards Data Science Cameron R. Wolfe, Ph.D.

·发表于 Towards Data Science ·阅读时间 10 分钟·2023 年 1 月 30 日

--

(照片由 Milad Fakurian 提供,来源于 Unsplash

计算机图形学和 3D 计算机视觉领域的先前研究提出了多种表示 3D 形状的方法。这些方法对以下方面有用:

  1. 存储内存高效的已知形状表示

  2. 生成新形状

  3. 基于有限或噪声数据修复/重建形状

超越传统方法,深度学习——更具体地说,生成性神经网络——可以用来表示 3D 形状。为此,我们可以训练一个神经网络来输出 3D 形状的表示,从而将多种形状的表示间接存储在神经网络的权重中。然后,我们可以查询这个神经网络来生成新形状。

在这篇文章中,我们将深入研究其中一种方法,称为 DeepSDF [1],它使用一个简单的前馈神经网络来学习多种 3D 形状的符号距离函数(SDF)表示。基本思想很简单:我们不是直接编码几何体(例如,通过网格),而是训练一个生成性神经网络来输出这些几何体。然后,我们可以进行推理以(i) 获取(潜在的新)3D 形状的直接编码,或(ii) 从噪声数据中修复/重建一个 3D 形状。

(来自 [1])

背景

在深入了解 DeepSDF 的工作原理之前,我们需要理解一些背景概念。首先,我们将讨论一下 3D 形状通常是如何表示的,以及签名距离函数(SDF)如何用于表示 3D 形状。然后,我们将讨论前馈神经网络,这是一种非常简单的深度学习架构,在 3D 形状建模的研究中被广泛使用。

表示 3D 形状

在考虑如何在计算机中存储 3D 形状时,我们有三个选项:点云、网格或体素。这些表示方法各有不同的优缺点,但都是直接表示 3D 形状的有效方法。让我们简单了解它们的工作原理。

点云。 点云相对容易理解。顾名思义,它们只是存储空间中一组具有 [x, y, z] 坐标的点,这些点用于表示一个潜在的几何形状。点云非常有用,因为它们与我们从 LiDAR 或深度传感器相机等传感器中获得的数据非常接近。但点云无法提供完全封闭的表面(即具有一个封闭表面的形状)。

网格。 一种可以提供封闭表面的 3D 表示方法是网格。网格是基于顶点、边和面集合的 3D 形状表示,用于描述一个潜在的形状。简单来说,网格就是一系列多边形(例如三角形),这些多边形拼接在一起形成一个 3D 几何形状。

基于体素的表示。 体素只是具有体积的像素。在 2D 图像中的像素在 3D 空间中被称为体素(即立方体)。要使用体素表示 3D 形状,我们可以:

  1. 将 3D 空间的一个部分划分为离散体素

  2. 确定每个体素是否被填充

使用这种简单的技术,我们可以构建一个基于体素的 3D 对象。为了获得更准确的表示,我们可以简单地增加使用的体素数量,从而形成更细致的 3D 空间离散化。请参见下文了解点云、网格和体素之间的差异插图。

(来自[3])

签名距离函数

直接使用点云、网格或体素存储 3D 形状需要大量内存。相反,我们通常希望存储一种更高效的间接表示方法。一种方法是使用签名距离函数(SDF)。

给定一个空间 [x, y, z] 点作为输入,SDF 将输出该点到所表示对象的最近表面的距离。SDF 输出的符号表示该空间点在对象表面内(负值)还是外(正值)。请参见下面的方程。

(由作者创建)

我们可以通过找到 SDF 等于零的位置来识别 3D 物体的表面,这表明某个点位于物体的边界。通过使用 SDF 找到这个表面后,我们可以通过使用类似 Marching Cubes 的算法生成网格。

这有什么用? 从高层次看,SDF 允许我们存储一个函数,而不是 3D 形状的直接表示。这种函数可能更高效地存储,并且我们仍然可以用它来恢复网格表示!

前馈神经网络

许多高精度的 3D 形状建模方法基于前馈网络架构。这样的架构将一个向量作为输入,并在网络的每一层内应用相同的两个变换:

  1. 线性变换

  2. 非线性激活函数

尽管我们输入的维度是固定的,但网络架构的两个方面是我们可以选择的:隐藏维度和层数。像这样的变量,我们作为实践者需要设置,称为 超参数。这些超参数的正确设置取决于我们试图解决的问题和/或应用。

代码。 前馈网络的复杂性不大。我们可以像下面展示的那样在 PyTorch 中轻松实现它们。

DeepSDF: 学习连续有符号距离函数以进行形状表示 [1]

(来自 [1])

在计算机图形学和 3D 计算机视觉领域,先前的研究提出了许多经典的方法来表示 3D 形状和几何体。在 [1] 中,作者提出了一种基于深度学习的方法,称为 DeepSDF,该方法使用神经网络来学习广泛类别形状的连续 SDF。简单来说,这意味着我们可以通过一个单一的前馈神经网络来编码基于 SDF 的多种不同类型的 3D 形状,从而使这些形状能够被表示、插值甚至从部分数据中完成;见上文。

DeepSDF 的核心思想很简单:我们希望使用神经网络直接对 SDF 的值进行 回归。为此,我们通过 SDF 的点样本(即具有相关 SDF 值的单个 [x, y, z] 点)来训练该模型。如果我们以这种方式训练网络,我们就可以轻松预测查询位置的 SDF 值,并通过找到 SDF 等于零的点来恢复形状的表面。

我们如何表示形状? 更具体地说,考虑一个单一形状,从中我们采样固定数量的 3D 点样本及其 SDF 值。我们应该注意,采样更多的点将使形状的表示更加精确,但这会增加计算成本。

(由作者创作)

在上面的方程中,x 是一个包含 [x, y, z] 坐标的向量,而 s 是与这些坐标相关联的给定形状的 SDF 值。

训练神经网络。 从这里,我们可以直接训练一个前馈神经网络,以 x 作为输入,利用这些样本对进行训练,从而生成 SDF 值 s,使用 L1 回归损失。然后,结果模型可以输出准确的 SDF 值来表示底层形状;见下图左侧子图。

(来自 [1])

这种模型的局限性在于它仅表示单一形状。理想情况下,我们希望用一个神经网络建模多种形状。为此,我们可以将一个潜在向量(即上图中的“Code”)与每个形状关联。这是一个对每个形状唯一的低维向量,存储在我们的神经网络中。可以将这个潜在向量作为输入添加到神经网络中,以告知网络它正在为特定形状生成输出。这一简单技巧允许我们在一个模型中表示多个形状(这节省了大量内存!);见上图右侧子图。

我们可能会问的最终问题是:我们如何为每个形状获取这个潜在向量? 在 [1] 中,作者通过提出一个自解码器架构来实现这一点,该架构 (i) 将潜在向量添加到模型的输入中,并 (ii) 在训练过程中通过梯度下降学习每个形状的最佳潜在向量;见下文。

(来自 [1])

通常,潜在向量是通过 自编码器架构 学习的,但这需要额外的编码器模块,增加了额外的计算开销。作者在 [1] 中提出了自解码器方法以避免这种额外计算。这些方法之间的区别如下所示。

生成形状。 要使用 DeepSDF 进行推理,我们必须:

  1. 从稀疏/不完整的 SDF 值样本开始

  2. 从这些样本中确定最佳的潜在向量

  3. 使用我们训练好的神经网络对 3D 空间中的一系列不同点进行推理,以确定 SDF 值

从这里,我们可以使用像 Marching Cubes 这样的算法来可视化由 DeepSDF 表示的形状,这些算法对 3D 空间进行离散化,并基于这些 SDF 值提取实际的 3D 几何形状。

数据。 DeepSDF 是使用合成的 ShapeNet 数据集进行训练和评估的。特别地,它的性能在四个任务中得到了衡量。

  1. 训练集中的形状表示

  2. 重建未见(测试)形状

  3. 完成部分形状

  4. 从潜在空间采样新形状

对于前三个任务,我们发现 DeepSDF 一直优于基线方法,这表明它能够以高精度表示复杂形状,甚至能够从不完整的样本中较好地恢复形状。考虑到我们在一个单一且节省内存的神经网络中存储了大量的 3D 形状,这一点尤为显著;见下图。

(来自 [1])

我们还可以对 DeepSDF 模型的嵌入空间进行插值,以生成连贯的结果。这使我们能够做一些事情,比如找到卡车和汽车之间的平均形状;见下图。

(来自 [1])

从这些结果中,我们可以看到潜在向量之间的插值产生了形状之间的平滑过渡,这表明 DeepSDF 嵌入的连续 SDF 是有意义的!形状的常见特征 —— 如卡车车厢或椅子扶手 —— 都被 DeepSDF 利用的表示所捕捉。这对于这样一个简单的前馈网络来说,实在是非常了不起。

收获

DeepSDF 是一个前馈生成神经网络,我们可以用来表示和操作 3D 形状。使用这个模型,我们可以轻松地执行诸如生成形状的网格表示、从不完整或噪声数据中恢复基本形状,甚至生成一个新的形状,这个新形状是已知几何形状的插值。以下列出了 DeepSDF 的优点和局限性。

大量压缩。 要在计算机中存储 3D 几何形状,我们可以使用网格或体素表示。为了避免直接存储这种形状的内存开销,我们可以使用像 DeepSDF 这样的生成模型。通过这种方法,我们不再需要几何形状的直接网格编码。相反,我们可以使用 DeepSDF —— 一个易于存储的小型神经网络 —— 来准确生成各种形状的网格。

修复破损几何形状。 给定基本形状的部分或噪声表示,DeepSDF 可以用来恢复准确的网格;见下图。相比之下,大多数之前的方法无法执行这种任务 —— 它们需要访问与用于训练模型的数据类型匹配的完整 3D 形状表示。

(来自 [1])

插值潜在空间。 Deep SDF 能够表示许多不同的形状,并将它们的属性嵌入到低维潜在空间中。此外,实验表明,这个潜在空间是有意义的,并且具有良好的覆盖范围。实际上,这意味着我们可以在潜在向量(即,不同对象的向量表示)之间进行 线性插值,并生成有效的新形状。我们可以轻松地利用这一点生成具有各种有趣属性的新形状。

局限性。 DeepSDF 非常出色,但它总是需要访问(可能是噪声或不完整的)3D 几何体来进行推断。此外,寻找最佳潜在向量(即,由于自动解码器方法,这必须始终在进行推断之前完成)计算成本很高。因此,DeepSDF 的推断能力在某种程度上是有限的。总结来说,该方法较慢,无法从头生成新形状,这为未来的改进留下了空间。

结束语

非常感谢您阅读本文。我是 Cameron R. Wolfe,一名在 Alegion 工作的研究科学家,同时也是赖斯大学的博士生,研究深度学习的经验和理论基础。您还可以查看我在 medium 上的 其他文章!如果您喜欢,请在 twitter 上关注我,或者订阅我的 Deep (Learning) Focus 新闻通讯,我在其中撰写关于重要深度学习主题的易于理解的概述系列。

参考文献

[1] Park, Jeong Joon 等. “Deepsdf: 学习用于形状表示的连续符号距离函数。” IEEE/CVF 计算机视觉与模式识别会议论文集. 2019.

[2] Mildenhall, Ben 等. “Nerf: 将场景表示为神经辐射场以进行视图合成。” ACM 通讯 65.1 (2021): 99–106.

[3] Hoang, Long 等. “一种使用波形核签名和 3D 三角网中心点的深度学习方法进行 3D 对象分类。” 电子学 8.10 (2019): 1196.

室内建模的 3D 点云形状检测

原文:towardsdatascience.com/3d-point-cloud-shape-detection-for-indoor-modelling-70e36e5f2511

动手教程,3D Python

10 步 Python 指南,用于自动化 3D 形状检测、分割、聚类和体素化,以实现室内点云数据集的空间占用 3D 建模。

Florent Poux, Ph.D.Towards Data Science Florent Poux, Ph.D.

·发布于 Towards Data Science ·阅读时长 28 分钟·2023 年 9 月 7 日

--

如果你有点云或数据分析的经验,你知道识别模式有多重要。识别具有相似模式的数据点或“对象”对获取有价值的见解至关重要。我们的视觉认知系统可以轻松完成这项任务,但通过计算方法复制这种人类能力是一个重大挑战。

目标是利用人类视觉系统自然的元素分组倾向。👀

3D 点云分割阶段结果示例。© F. Poux

但这有什么用呢?

首先,它通过将数据分组到不同的段中,让你轻松访问和处理数据的特定部分。其次,通过查看区域而不是单个点,它使数据处理更快。这可以节省大量时间和精力。最后,分割可以帮助你发现通过查看原始数据无法看到的模式和关系。🔍 总的来说,分割对于从点云数据中获取有用信息至关重要。如果你不确定如何做,别担心——我们会一起搞定的!🤿

策略

在我们以有效的解决方案处理项目之前,让我们框定整体方法。本教程遵循一个由十个简单步骤组成的策略,如下方的策略图所示。

本指南展示了 3D 点云室内建模的工作流程。© F. Poux

策略已列出,下面你可以找到不同步骤的快捷链接:

Step 1\. Environment Setup
Step 2\. 3D Data Preparation
Step 3\. Data Pre-Processing
Step 4\. Parameter Setting
Step 5\. RANSAC Planar Detection
Step 6\. Multi-Order RANSAC
Step 7\. Euclidean Clustering Refinement
Step 8\. Voxelization Labelling
Step 9\. Indoor Spatial Modelling
Step 10\. 3D Workflow Export

既然我们已做好准备,那就直接开始吧。

🎵读者注意:此动手指南是* UTWENTE 联合工作的一部分,由 F. Poux V. Lehtola 共同作者。我们感谢来自数字双胞胎 @ITC 项目的资助,该项目由特温特大学 ITC 系提供。所有图像 © Florent Poux

1. 设置您的 Python 环境。

在下面的上一篇文章中,我们展示了如何快速设置 Anaconda 环境并使用 IDE JupyterLab 管理代码。如果您继续以这种方式设置自己,您将成为一名完整的 Python 应用程序开发者 😆。

## 3D Python 工作流程用于 LiDAR 城市模型:逐步指南

解锁 3D 城市建模应用程序的精简工作流程的终极指南。教程涵盖了 Python...

towardsdatascience.com

🦊 Florent我强烈推荐使用桌面设置或 IDE,避免使用 Google Colab,尤其是当您需要使用提供的库可视化 3D 点云时,因为它们在最佳情况下会不稳定,最糟糕的情况下则无法工作(不幸的是…)。

🤠 Ville我们猜您是在 Windows 上运行?这没问题,但如果您想进入计算方法领域,Linux 是首选!

好吧,我们将采取一种“快速成果”的方法。实际上,我们将通过遵循最简化的编码方法来实现卓越的分割💻。这意味着我们对底层库非常挑剔!我们将使用三个非常强大的库,分别是numpymatplotlibopen3d

好的,要在一个全新的虚拟环境中安装上述库包,我们建议您从cmd终端运行以下命令:

conda install numpy
conda install matplotlib
pip install open3d==0.16.0

🍜 免责声明我们选择了 Python,而不是 C++ 或 Julia,所以性能就是那样 😄。希望它能满足您的应用需求 😉,对于我们所谓的“离线”过程(非实时)。

在您的 Python IDE 中,请确保导入将被频繁使用的三个库:

#%% 1\. Library setup
import numpy as np
import open3d as o3d
import matplotlib.pyplot as plt

就这样!我们准备好进行室内点云建模工作流程了!

2. 点云数据准备

在之前的教程中,我们演示了如何处理点云并在使用 AHN4 LiDAR 活动获取的 3D 数据集上进行网格化。这一次,我们将使用一个通过地面激光扫描仪收集的数据集:ITC 新建筑。它被组织成三个不同的集合供您实验。紫色的是户外部分。红色是地面层,绿色是第一层,如下图所示。

使用的 ITC 数据集的 3D 点云部分。© F. Poux

你可以从这里下载数据:ITC 数据集。一旦你在本地对数据有了牢固的掌握,你可以用两行简单的代码将数据集加载到你的 Python 执行环境中:

DATANAME = "ITC_groundfloor.ply"
pcd = o3d.io.read_point_cloud("../DATA/" + DATANAME)

🦊 Florent: 这段 Python 代码片段使用 *Open3D* 库来读取位于目录“*../DATA/*”中的点云数据文件 *ITC_groundfloor.ply* ,并将其赋值给变量 *pcd*

变量现在保存着你的点云 pcd,你将用以下代码进行操作:

一旦点云数据成功加载到 Open3D 中,下一步是应用各种预处理技术来提升其质量并提取有意义的信息。

3. 点云预处理

如果你打算在 open3d 中可视化点云,建议你将点云移动以绕过大坐标的近似,这样可以避免产生晃动的可视化效果。要对你的 pcd 点云应用这种移动,首先获取点云的中心,然后通过从原始变量中减去它来进行平移:

pcd_center = pcd.get_center()
pcd.translate(-pcd_center)

现在可以通过以下代码行进行交互式可视化:

o3d.visualization.draw_geometries([pcd])

🦚 注意: o3d.visualization.draw_geometries([pcd]) 调用了 *draw_geometries()* 函数,这个函数属于 Open3D 的 *visualization* 模块。该函数接受一个几何体列表作为参数,并在可视化窗口中显示它们。在这种情况下,列表包含一个几何体,即表示点云的 *pcd* 变量。 *draw_geometries()* 函数创建一个 3D 可视化窗口并渲染点云。你可以与可视化窗口互动以旋转、缩放,并从不同角度探索点云。

太棒了👌,我们已经准备好测试一些采样策略来统一我们的下游处理。

3.1. 点云随机采样

让我们考虑那些可以有效减少点云大小同时保持整体结构完整性和代表性的方法。如果我们将点云定义为一个矩阵 (m x n),那么通过保留该矩阵每隔 n 行的一个行,我们可以得到一个简化的点云

一个点云简化策略。© F. Poux

在矩阵级别,简化操作是通过根据 n 因子保留每隔 n 行的点来进行的。当然,这取决于点在文件中的存储方式。用 open3d 切片点云是相当直接的。为了简化和参数化表达式,你可以写出以下代码:

retained_ratio = 0.2
sampled_pcd = pcd.random_down_sample(retained_ratio)

🦚 注意sampled_pcd = pcd.random_down_sample(retained_ratio) 对原始点云 *pcd* 应用随机下采样,使用的是 random_down_sample() 函数,该函数由 Open3D 提供。 retained_ratio 参数决定了下采样后保留的点的比例。例如,如果 retained_ratio 设置为 0.5,则大约 50% 的点将被随机选择并保留在采样后的点云中。

o3d.visualization.draw_geometries([sampled_pcd], window_name = "Random Sampling")

点云的下采样结果。© F. Poux

🌱 发展在研究 3D 点云时,随机采样有其局限性,可能会导致遗漏重要信息和分析不准确。它没有考虑点之间的空间组件或关系。因此,使用其他方法以确保更全面的分析是至关重要的。

尽管这种策略快速,但随机采样可能不适合“标准化”用例。下一步是通过统计异常值去除技术来处理潜在的异常值,以确保数据的质量和可靠性,以便进行后续分析和处理。

3.2. 统计异常值去除

对 3D 点云数据使用异常值过滤器可以帮助识别和去除任何显著不同于数据集其余部分的数据点。这些异常值可能是由于测量误差或其他因素造成的,这些因素可能会影响分析。通过去除这些异常值,我们可以获得更有效的数据表示,并更好地调整算法。然而,我们需要小心不要删除有价值的点。

我们将定义一个 statistical_outlier_removal 过滤器,以去除与其邻居相比远离平均值的点。它需要两个输入参数:

  • nb_neighbors,用于指定在计算给定点的平均距离时考虑多少个邻居。

  • std_ratio,允许根据点云中平均距离的标准差设置阈值水平。这个数值越低,过滤器的作用就越强。

这包括以下内容:

nn = 16
std_multiplier = 10

filtered_pcd, filtered_idx = pcd.remove_statistical_outlier(nn, std_multiplier)

🦚 注意*remove_statistical_outlier()* 函数通过指定数量的最近邻(*nn*)和一个标准差乘数(*std_multiplier*)来对点云应用统计异常值去除。该函数返回两个值:* *filtered_pcd* *filtered_idx* *filtered_pcd* 表示经过滤的点云,其中已去除统计异常值。 *filtered_idx* 是一个索引数组,对应于在异常值去除过程中保留的原始点云 *pcd* 中的点。

为了可视化这种过滤技术的结果,我们将异常值标记为红色,并将其添加到我们希望可视化的点云对象列表中:

outliers = pcd.select_by_index(filtered_idx, invert=True)
outliers.paint_uniform_color([1, 0, 0])
o3d.visualization.draw_geometries([filtered_pcd, outliers])

异常值在点云中用红色标记和可视化。© F. Poux

在从点云中移除统计异常值之后,下一步是应用基于体素的采样技术,进一步下采样数据,以便高效处理和分析,同时保留点云的基本结构特征。

3.3. 点云体素(网格)采样

网格下采样策略基于将 3D 空间划分为规则的立方体单元,称为体素。对于这个网格的每个单元,我们只保留一个代表性点,并且可以用不同的方式选择这个点。当下采样时,我们保留该单元最接近重心的点。

体素网格采样示例。© F. Poux

具体而言,我们定义一个voxel_size,然后用它来过滤我们的点云:

voxel_size = 0.05
pcd_downsampled = filtered_pcd.voxel_down_sample(voxel_size = voxel_size)

🦚 注意此行对过滤后的点云执行体素下采样, *filtered_pcd*,使用 *voxel_down_sample()* 函数。 *voxel_size* 参数指定体素下采样点云的每个体素的大小。较大的体素尺寸会导致点云密度显著减少。下采样操作的结果分配给变量 *pcd_downsampled*,代表下采样后的点云

现在是仔细可视化下采样后分布的时间:

o3d.visualization.draw_geometries([pcd_downsampled])

应用在点云上的采样策略结果。© F. Poux

在此阶段,我们有一组未在进一步处理时触及的异常点和一个下采样后的点云,构成了后续过程的新主题。

3.4. 点云法线提取

点云法线指的是 3D 点云中特定点处表面的方向。它可以用于通过将点云划分为具有相似法线的区域来进行分割。例如,在我们的案例中,法线将有助于识别点云中的物体和表面,使其更容易可视化。这也是介绍一种半自动计算法线的方法的绝佳机会。我们首先定义点云中每个点与其邻居之间的平均距离:

nn_distance = 0.05

然后,我们利用这些信息在半径为radius_normals的范围内提取有限的max_nn个点,为 3D 点云中的每个点计算法线:

radius_normals=nn_distance*4
pcd_downsampled.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=radius_normals, max_nn=16), fast_normal_computation=True)

现在,pcd_downsampled点云对象很高兴地拥有法线,准备展示其最漂亮的一面😊。此时你知道该做什么:

pcd_downsampled.paint_uniform_color([0.6, 0.6, 0.6])
o3d.visualization.draw_geometries([pcd_downsampled,outliers])

用于后续实验的点云。

完成点云的体素下采样后,下一步是配置点云形状检测和聚类的参数,这在将相似点分组以及从下采样点云数据中提取有意义的结构或对象中起着关键作用。

4. 点云参数设置

在本教程中,我们为您精选了两种最有效且可靠的 3D 形状检测和聚类方法:RANSAC 和使用 DBSCAN 的欧几里得聚类。然而,在使用这些方法之前,了解参数的同时,理解基本概念是至关重要的。

RANSAC

RANSAC 算法,RANdom SAmple Consensus 的缩写,是一个强大的工具,用于处理包含异常值的数据,这在使用现实世界传感器时常常会遇到。该算法通过将数据点分为两类:内点和外点来工作。通过识别并忽略外点,您可以专注于处理可靠的内点,从而使分析更加有效。

在 3D 点云中使用 RANSAC 进行平面检测。© F. Poux

让我用一个简单的例子来说明 RANSAC 的工作原理。假设我们想通过下面的点云拟合一个平面。我们怎么做呢?

在随机点云中进行的 RANSAC 平面检测模拟。© F. Poux

首先,我们从数据中创建一个平面,为此,我们从点云中随机选择 3 个点来建立平面。然后,我们简单地检查剩余的点中有多少点落在该平面上(达到某个阈值),这将为提案打分。

RANSAC 评分系统说明。您可以看到,每次迭代都会随机抽取 3 个点,从中创建一个平面,然后选择落在平面上的点。在这里,第 159 次迭代会是最佳候选。© F. Poux

然后,我们用 3 个新的随机点重复这个过程,看看效果如何。是更好了吗?更差了吗?然后,我们反复进行这个过程,比如说 10 次、100 次、1000 次,然后选择得分最高的平面模型(即具有最佳“支持”的剩余数据点)。这将是我们的解决方案:支持点加上我们采样的三个点构成我们的内点集,其余部分是我们的外点集。够简单吧 😁?

哈哈,对于那些怀疑者,你们难道没有一个上升的问题吗?我们如何实际确定应该重复多少次过程?我们应该多频繁地尝试?好吧,这实际上是可以计算的,但我们先把它放到一边,专注于当前的问题:点云分割😉。

RANSAC 参数设置

我们希望使用 RANSAC 来检测点云中的 3D 平面形状。使用 RANSAC 时,我们需要定义三个参数:一个距离阈值distance_threshold,用于将点标记为内点或外点;一个最小点数ransac_n,用于拟合几何模型;一个迭代次数num_iterations

距离阈值的确定:我们可能会因为需要一些领域知识来设置未来的分割和建模阈值而受到限制。因此,尝试绕过这一点以使方法对非专家开放将是令人兴奋的。我们将与你分享一个简单的想法,这可能会有用。如果我们计算数据集中点之间的平均距离,并将其作为设置阈值的基础,会怎样呢?

好吧,这是一个值得探索的想法。为了确定这样的值,我们使用 KD-Tree 来加速每个点的最近邻查询过程。从那里开始,我们可以查询点云中每个点的 k 个最近邻,然后将其打包到下面显示的 open3d 函数中:

nn_distance = np.mean(pcd.compute_nearest_neighbor_distance())

毫无意外,它接近 5 cm,因为我们将点云采样到这个值。这意味着如果我们考虑最近邻,我们会有 51 mm 的平均距离。

🌱 成长从这里开始,我们可以将 RANSAC 参数设置为从 nn_distance 变量得出的值,这样可以根据考虑的数据集进行调整,而不依赖于领域知识。你会怎么处理这个问题?

点数的确定:这里很简单。我们想要找出平面,因此我们将取定义 3D 平面所需的最小点数:3。

迭代次数的确定:迭代次数越多,你的 3D 形状检测就越稳健,但所需时间也越长。目前,我们可以将其设置为 1000,这样可以获得良好的结果。我们将在未来的教程中探索更多巧妙的方法来找出点云的噪声比例。😉

🧙‍♂️ 专家有一种自动获取每次迭代次数的方法。如果我们希望以概率 p(例如 99%)成功,数据中的离群点比例是 e(例如 60%),我们需要 s 个点来定义我们的模型(这里是 3)。以下公式给出了预期的迭代次数:

现在我们已经定义了 RANSAC 参数,我们可以研究第一次分割过程。

5. 使用 RANSAC 进行点云分割

首先将不同的阈值设置为非自动值进行测试:

distance_threshold = 0.1
ransac_n = 3
num_iterations = 1000

从那里开始,我们可以使用 RANSAC 对点云进行分割以检测平面,具体如下:

plane_model, inliers = pcd.segment_plane(distance_threshold=distance_threshold,ransac_n=3,num_iterations=1000)
[a, b, c, d] = plane_model
print(f”Plane equation: {a:.2f}x + {b:.2f}y + {c:.2f}z + {d:.2f} = 0")

我们将结果收集到两个变量中:plane_model,它包含平面的参数 a,b,c,d,以及 inliers 作为点索引。

这使得可以使用索引将点云分割为 inlier_cloud 点集(我们将其标记为红色)和 outlier_cloud 点集(我们将其标记为灰色):

inlier_cloud = pcd.select_by_index(inliers)
outlier_cloud = pcd.select_by_index(inliers, invert=True)

#Paint the clouds
inlier_cloud.paint_uniform_color([1.0, 0, 0])
outlier_cloud.paint_uniform_color([0.6, 0.6, 0.6])

#Visualize the inliers and outliers
o3d.visualization.draw_geometries([inlier_cloud, outlier_cloud]) 

🦊 Florent *invert=True* 参数允许选择第一个参数的反义,即所有不在 *inliers* 中的索引。如果你缺少阴影,记得计算法线,如上所示。*

🌱 成长尝试调整各种参数,并通过定性分析研究其影响。首先记住要去相关变化(一次一个变量);否则你的分析可能会有偏差 😊。

很棒!你知道如何将点云分割为内点集和外点集 🥳!现在,让我们研究如何找到彼此接近的一些簇。让我们想象一下,一旦我们检测到大的平面部分,我们有一些“漂浮”的物体需要 delineate。怎么做呢?(是的,这是一个假问题,我们有答案给你 😀)

6. 扩展 3D 分割:多阶 RANSAC

我们的理念将非常简单。我们将首先多次运行 RANSAC(假设 n 次)以提取构成场景的不同平面区域。然后,我们将通过欧几里得聚类(DBSCAN)处理“漂浮元素”。这意味着我们必须确保有一种方法在迭代过程中存储结果。准备好了吗?

好的,让我们实例化一个空字典,以保存迭代结果(segment_models中的平面参数和segments中的点云平面区域):

segment_models={}
segments={}

然后,我们需要确保可以影响以后迭代的次数,以检测平面。为此,让我们创建一个变量max_plane_idx来保存迭代次数:

max_plane_idx=10

🦊 Florent:在这里,我们说我们希望迭代 10 次以找到 10 个平面,但有更聪明的方法来定义这样的参数。这实际上扩展了文章的范围,将在另一节中讨论。

现在让我们进入一个工作循环 😁,我将首先快速说明。

执行以在 RANSAC 过程中进行分割的循环。© F. Poux

在第一次迭代(循环i=0)中,我们将内点与外点分开。我们将内点存储在segments中,然后我们只对存储在rest中的剩余点感兴趣,这将成为循环 n+1(循环i=1)的研究对象。这意味着我们希望将上一阶段的外点作为基础点云,直到达到上述的迭代阈值(与 RANSAC 迭代不同)。这转化为以下内容:

rest=pcd
for i in range(max_plane_idx):
    colors = plt.get_cmap("tab20")(i)
    segment_models[i], inliers = rest.segment_plane(
    distance_threshold=0.1,ransac_n=3,num_iterations=1000)
    segments[i]=rest.select_by_index(inliers)
    segments[i].paint_uniform_color(list(colors[:3]))
    rest = rest.select_by_index(inliers, invert=True)
    print("pass",i,"/",max_plane_idx,"done.")

就是这样!现在,为了可视化整体,我们通过循环中的第一行将每个检测到的分段涂上来自tab20的颜色(colors = plt.get_cmap("tab20")(i)),你只需写:

o3d.visualization.draw_geometries([segments[i] for i in range(max_plane_idx)]+[rest])

🦚 注意:我们传递给函数o3d.visualization.draw_geometries()的列表[segments[i] for i in range(max_plane_idx)]实际上是一个“列表推导式”🤔。它等同于编写一个for循环,将第一个元素segments[i]追加到列表中。方便的是,我们可以将[rest]添加到这个列表中,draw.geometries()方法会理解我们想要绘制一个点云。这不是很酷吗?

DBSCAN 对 3D 点云的第一次扫描结果

哈!我们以为完成了……但我们真的完成了吗?你注意到这里有什么奇怪的地方吗?如果你仔细看,会发现一些奇怪的伪影,比如实际切割一些平面元素的“红色线条/平面”。为什么?🧐

实际上,由于我们将所有点拟合到 RANSAC 平面候选者(在欧几里得空间中没有限制范围)而不考虑点的密度连续性,因此根据平面的检测顺序,我们会出现这些“线”伪影。所以下一步是防止这种行为!为此,我建议在迭代过程中包含一个基于欧几里得聚类的条件,以在连续的簇中细化内点集。为此,我们将依赖于 DBSCAN 算法。

欧几里得聚类(DBSCAN)

在点云数据集中,我们经常需要将空间上连续的点集合(即在 3D 空间中物理上接近或相邻)进行分组,如下所示。但我们如何有效地做到这一点呢?

在这张图中,很明显我们想要将彼此接近的点分组,找到 5 组点。© F. Poux

DBSCAN(基于密度的空间聚类应用与噪声)算法于 1996 年为此目的而提出。该算法被广泛使用,因此在 2014 年获得了经得起时间考验的科学贡献奖。

DBSCAN 算法涉及扫描数据集中的每个点,并基于密度构建一个可达点集合。这是通过分析每个点的邻域并在其包含足够点时将其包含在区域内来实现的。这个过程对每个邻近点重复,直到簇无法再扩展。没有足够邻居的点被标记为噪声,使得该算法对离群点具有鲁棒性。这不是很令人印象深刻吗?😆

DBSCAN 算法过程和两个参数ϵ和 min_points 对结果的影响的示意图。你可以看到,值越大,组成的簇就越少。© F. Poux

啊,我们差点忘了。参数选择(ϵ用于邻域,n_min 用于最小点数)也可能很棘手:设置参数时必须小心,以确保创建足够的内部点(如果 n_min 太大或ϵ太小,就不会发生)。特别是,这意味着 DBSCAN 在发现不同密度的簇时会遇到困难。但 DBSCAN 有一个很大的优势,就是计算效率高,不需要像 Kmeans 那样预定义簇的数量。最后,它允许发现任意形状的簇。现在我们准备深入探讨参数的黑暗面 💻

DBSCAN 用于 3D 点云聚类

让我详细说明逻辑过程(激活猛兽模式👹)。首先,我们需要定义运行 DBSCAN 的参数:

epsilon = 0.15
min_cluster_points = 5

🌱 成长这些参数的定义是一个需要探索的问题。你必须找到一种方法来平衡过度分割和不足分割的问题。最终,你可以基于 RANSAC 的初始距离定义使用一些启发式方法。这是一个值得探索的方面 😉。

在我们之前定义的 for 循环中,我们将在分配内点(segments[i]=rest.select_by_index(inliers))后立即运行 DBSCAN,方法是紧接着添加以下一行:

labels = np.array(segments[i].cluster_dbscan(eps=epsilon, min_points=min_cluster_points))

然后,我们将计算每个找到的簇包含多少个点,使用一种奇怪的符号表示法,该表示法利用了列表推导。结果存储在变量candidates中:

candidates=[len(np.where(labels==j)[0]) for j in np.unique(labels)]

那现在呢?我们需要找到“最佳候选者”,通常是包含最多点的簇!为此,这里是这行代码:

best_candidate=int(np.unique(labels)[np.where(candidates== np.max(candidates))[0]])

好吧,很多技巧在这里发生,但本质上,我们利用 Numpy 的熟练度来搜索并返回属于最大簇的点的索引。从这里开始,接下来的过程就简单了,我们只需确保在每次迭代中将任何剩余簇纳入后续 RANSAC 迭代中(🔥 推荐阅读 5 遍以消化!):

rest = rest.select_by_index(inliers, invert=True) + segments[i].select_by_index(list(np.where(labels!=best_candidate)[0]))
segments[i]=segments[i].select_by_index(list(np.where(labels== best_candidate)[0]))

🦚 注意rest 变量现在确保包含 RANSAC 和 DBSCAN 剩余的点。当然,内点现在被筛选为原始 RANSAC 内点集中最大的簇*。

当循环结束时,你会得到一组干净的段,这些段包含空间上连续的点集,符合平面形状,你可以使用以下代码行以不同颜色可视化:

colors = plt.get_cmap("tab20")(i)
segments[i].paint_uniform_color(list(colors[:3]))

结果应该类似于这个:

但这就结束了吗?不,不可能的 😄!一旦对点云应用了多阶 RANSAC 分割,下一阶段就是通过利用 DBSCAN 来细化剩余的未分割点,这有助于进一步提升点云分析的粒度,增加更多的簇。

🦚 注意粒度是一个在数据科学中经常使用的学术词汇。数据的粒度意味着数据组织或建模的详细程度。在这里,我们可以从点云中建模的对象越小,我们的表示的粒度就越细。(很高大上,对吧?!)

7. 欧几里得聚类精炼

好了,是时候摆脱循环,处理分配给 rest 变量的剩余点,这些点尚未分配给任何段。让我们首先对我们讨论的内容有一个视觉上的了解:

o3d.visualization.draw_geometries([rest])

我们可以使用 DBSCAN 进行简单的欧几里得聚类,并将结果捕获到一个 labels 变量中。你知道怎么做的:

labels = np.array(pcd.cluster_dbscan(eps=0.1, min_points=10))

🌱 生长: 我们使用 10 cm 的半径来“生长”簇,只有在此步骤之后至少有 10 个点时才考虑一个簇。但这是正确的选择吗? 🤔可以随意实验以找到一个好的平衡点,理想情况下,找到一种自动化的方法😀。

标签的范围在 -1n 之间,其中 -1 表示“噪声”点,0n 的值则是分配给相应点的簇标签。请注意,我们希望将标签作为 NumPy 数组获取。

左侧:参数定义不佳。右侧,我们对物体的轮廓有了更好的划分,以便进行后续处理。

很好。现在我们已经定义了每个点的标签组,让我们为结果着色。这是可选的,但对于迭代过程以搜索合适的参数值非常有用。为此,我们建议使用 Matplotlib 库获取特定的颜色范围,例如 tab20:

max_label = labels.max()
colors = plt.get_cmap("tab20")(labels / (max_label 
if max_label > 0 else 1))
colors[labels < 0] = 0
rest.colors = o3d.utility.Vector3dVector(colors[:, :3])
o3d.visualization.draw_geometries([rest])

🦚 注意max_label 应该是直观的:它存储标签列表中的最大值。这允许将其用作着色方案的分母,同时处理一个特殊情况,其中聚类被扭曲,仅产生噪声+一个簇。之后,我们确保将这些噪声点的标签设置为 -1 (黑色0)。然后,我们将点云* pcd colors 属性设置为表示 R、G、B 的 3 列的 2D NumPy 数组。

就这样!我采用了与之前相同的方法,没有任何魔法!我只是确保使用一致的参数来进行精细的聚类,以获得你一直梦想的美丽彩虹场景 🥳!

在使用 DBSCAN 精炼点云聚类结果之后,焦点转向体素化技术,这涉及将数据组织成有意义的空间结构,从而实现对点云信息的高效建模。

8. 体素化和标记

现在我们有了一个带有分段标签的点云,查看我们是否能适应室内建模工作流将会非常有趣。一种方法是使用体素来适应 O-Space 和 R-Space。通过将点云划分为小立方体,可以更容易地理解模型的占用和空白空间。让我们深入了解一下。

9.1. 体素网格生成

创建准确且详细的空间 3D 模型意味着生成一个紧凑的体素网格。这种技术将点云划分为具有自己坐标系统的小立方体或体素。

体素网格生成以结构化带有分段信息的 3D 点云。© F. Poux

要创建这样的结构,我们首先定义我们新实体的大小:体素:

voxel_size=0.5

现在,我们想知道我们需要堆叠多少个这样的立方体才能填充由点云定义的边界框。这意味着我们首先需要计算点云的范围:

min_bound = pcd.get_min_bound()
max_bound = pcd.get_max_bound()

很好,现在我们使用o3d.geometry.VoxelGrid.create_from_point_cloud()函数对任何选择的点云进行操作,以便在其上拟合体素网格。但是等等,我们想区分哪个点云以便进一步处理?

好的,让我们举例说明你想拥有“结构性”元素的体素与不属于结构性元素的杂物体素的情况。没有标签的情况下,我们可以根据它们是否属于 RANSAC 分段或其他分段来指导我们的选择;这意味着首先将 RANSAC 处理的分段进行拼接:

pcd_ransac=o3d.geometry.PointCloud()
for i in segments:
 pcd_ransac += segments[i]

🦚 注意: 在这个阶段,你有能力用体素稍后拾取的均匀颜色为点云着色。如果这是你想要的,你可以使用: pcd_ransac.paint_uniform_color([1, 0, 0])

然后,我们可以简单地提取我们的体素网格:

voxel_grid_structural = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd_ransac, voxel_size=voxel_size)

并对剩余元素做同样的处理:

rest.paint_uniform_color([0.1, 0.1, 0.8])
voxel_grid_clutter = o3d.geometry.VoxelGrid.create_from_point_cloud(rest, voxel_size=voxel_size)

最后一步是可视化我们的结果:

o3d.visualization.draw_geometries([voxel_grid_clutter,voxel_grid_structural])

使用体素表示的空间的语义表示。© F. Poux

这太棒了!它看起来像那些整个世界由立方体构成的计算机游戏!而且我们实际上可以使用分段标签来指导我们的体素建模!这开启了许多视角!但问题依然存在。使用 Open3D,提取未填充的体素是困难的;因此,完成体素化点云的结构化后,下一步涉及探索体素空间建模技术,以提供分析体素化数据空间关系和属性的替代视角,为高级点云建模开辟新途径。

🤠 Ville: 体素化是对相同点云数据的不同空间表示。如果机器人需要避开碰撞,这种表示非常有用!比如在模拟…火灾演习时也非常有用!

9. 空间建模

在室内建模应用中,基于体素的点云表示在捕捉和分析复杂环境的几何属性方面起着关键作用。随着点云数据集的规模和复杂性的增加,深入探讨体素分割技术变得至关重要,以提取有意义的结构并促进更高层次的分析。因此,让我们定义一个函数来拟合体素网格,并返回填充和空白区域:

def fit_voxel_grid(point_cloud, voxel_size, min_b=False, max_b=False):
# This is where we write what we want our function to do.
return voxel_grid, indices

在函数内部,我们将 (1) 确定点云的最小和最大坐标,(2) 计算体素网格的尺寸,(3) 创建一个空的体素网格,(4) 计算已占据体素的索引以及 (5) 将占据体素标记为 True。

从点云创建占据网格的算法工作流程。© F. Poux

这转化为我们希望在此函数中包含的以下代码:

# Determine the minimum and maximum coordinates of the point cloud
 if type(min_b) == bool or type(max_b) == bool:
   min_coords = np.min(point_cloud, axis=0)
   max_coords = np.max(point_cloud, axis=0)
 else:
   min_coords = min_b
   max_coords = max_b
 # Calculate the dimensions of the voxel grid
 grid_dims = np.ceil((max_coords - min_coords) / voxel_size).astype(int)
 # Create an empty voxel grid
 voxel_grid = np.zeros(grid_dims, dtype=bool)
 # Calculate the indices of the occupied voxels
 indices = ((point_cloud - min_coords) / voxel_size).astype(int)
 # Mark occupied voxels as True
 voxel_grid[indices[:, 0], indices[:, 1], indices[:, 2]] = True

我们已经定义了函数,现在可以使用它提取体素,按结构、杂乱和空体素进行分段:

voxel_size = 0.3

#get the bounds of the original point cloud
min_bound = pcd.get_min_bound()
max_bound = pcd.get_max_bound()

ransac_voxels, idx_ransac = fit_voxel_grid(pcd_ransac.points, voxel_size, min_bound, max_bound)
rest_voxels, idx_rest = fit_voxel_grid(rest.points, voxel_size, min_bound, max_bound)

#Gather the filled voxels from RANSAC Segmentation
filled_ransac = np.transpose(np.nonzero(ransac_voxels))

#Gather the filled remaining voxels (not belonging to any segments)
filled_rest = np.transpose(np.nonzero(rest_voxels))

#Compute and gather the remaining empty voxels
total = pcd_ransac + rest
total_voxels, idx_total = fit_voxel_grid(total.points, voxel_size, min_bound, max_bound)
empty_indices = np.transpose(np.nonzero(~total_voxels))

🦚 注意NumPy 中的 *nonzero()* 函数查找 *ransac_voxels* 变量中非零元素的索引。*nonzero()** 函数返回一个数组的元组,每个数组对应于特定轴上非零元素的索引。然后,我们将 *np.transpose()** NumPy 函数应用于从 *np.nonzero(ransac_voxels)** 获得的结果。*transpose()** 函数最终会排列数组的轴(有效地交换行和列)。通过结合这些操作,代码行对 *ransac_voxels** 的非零元素索引进行转置,生成一个转置数组,其中每行表示原始 *ransac_voxels** 数组中非零元素的坐标或索引。😊

这种体素建模方法提供了对体素化数据空间关系和属性的宝贵见解。为了在 Python 之外以透明度可视化结果,我们需要导出数据。

占据网格匹配的结果。左侧是填充体素和空体素,中间是填充体素,右侧是空体素。© F. Poux

在实现体素化点云的结构化后,下一步涉及将点云和体素数据导出为外部格式,以便实现互操作性,并与其他软件工具和工作流程无缝集成。此导出过程确保结构化的体素数据和原始点云可以轻松共享、可视化或用于进一步分析,从而促进对点云数据利用的协作和多功能方法。

10. 导出 3D 数据集

让我们首先关注导出点云分割数据集。

10.1. 分割点云导出

要导出分割后的点云,我们必须确保可以在可读的 ASCII 文件中为每个点写入标签。为此,我们将创建一个 XYZ 片段列表,并将标签特征附加到该列表。这可以通过以下for循环完成:

xyz_segments=[]
for idx in segments:
 print(idx,segments[idx])
 a = np.asarray(segments[idx].points)
 N = len(a)
 b = idx*np.ones((N,3+1))
 b[:,:-1] = a
 xyz_segments.append(b)

从那里,我们不想忘记 DBSCAN 聚类中的剩余元素,对其应用相同的原则:

rest_w_segments=np.hstack((np.asarray(rest.points),(labels+max_plane_idx).reshape(-1, 1)))

最后,我们将其附加到片段列表中:

 xyz_segments.append(rest_w_segments)

然后我们所需要做的就是使用 numpy 导出数据集并进行外部可视化:

np.savetxt("../RESULTS/" + DATANAME.split(".")[0] + ".xyz", np.concatenate(xyz_segments), delimiter=";", fmt="%1.9f")

在成功导出分割后的点云数据集后,现在的重点是将体素数据集导出为.obj文件。

10.2. 体素模型导出

要导出体素,我们首先必须为每个体素生成一个立方体;这些立方体然后在voxel_assembly中组合在一起,堆叠生成最终文件。我们创建了voxel_modelling(filename, indices, voxel_size)函数来完成这个任务:

def voxel_modelling(filename, indices, voxel_size):
    voxel_assembly=[]
    with open(filename, "a") as f:
        cpt = 0
        for idx in indices:
            voxel = cube(idx,voxel_size,cpt)
            f.write(f"o {idx}  \n")
            np.savetxt(f, voxel,fmt='%s')
            cpt += 1
            voxel_assembly.append(voxel)
    return voxel_assembly

🦚 注意函数 [cube()](https://drive.google.com/file/d/1kPu85YHl66gQH8Qumxlyd-Sp4PjgvBVm/view?usp=sharing) 读取提供的索引,根据索引和体素大小生成体素立方体,将体素立方体写入文件,并跟踪在 *voxel_assembly* 列表中的组装体素立方体,最终由函数返回。

然后用来导出三种不同的体素组合:

vrsac = voxel_modelling("../RESULTS/ransac_vox.obj", filled_ransac, 1)
vrest = voxel_modelling("../RESULTS/rest_vox.obj", filled_rest, 1)
vempty = voxel_modelling("../RESULTS/empty_vox.obj", empty_indices, 1)

分割和体素建模的结果。© F. Poux

结论

🦊 Florent:大规模祝贺🎉!你刚刚学会了如何开发一个自动形状检测、聚类、体素化和室内建模程序,用于处理由数百万个点组成的 3D 点云,并采用不同的策略!真心地,做得很好!但道路显然不止于此,因为你刚刚解锁了一个巨大的智能过程潜力,能够在片段级别进行推理!

🤠 Ville:那么,你是否在想我们是否可以让 3D 建模成为完全无需人工干预的过程?凭借我们目前的技术,我们已经完成了一半。为什么?我们的技术可以找到,例如,平面模型的参数。这就是为什么学术界的人称之为参数建模。然而,我们仍然需要仔细选择其他一些参数,比如 RANSAC 的参数。我鼓励你尝试在不同的点云上应用你的代码!

基于地面语义的 3D 网格示例。© F. Poux

更进一步

学习之旅并未结束。我们的终身探索才刚刚开始,未来的步骤将深入到 3D Voxel 工作,探索语义和点云分析与深度学习技术。我们将解锁高级的 3D LiDAR 分析工作流程。令人兴奋的事情还有很多!

  1. Lehtola, V.,Nikoohemat, S.,& Nüchter, A.(2020)。室内 3D:扫描和重建方法概述。大地空间数据手册,55–97. doi.org/10.1007/978-3-030-55462-0_3

  2. Poux, F.,& Billen, R.(2019)。基于体素的 3D 点云语义分割:无监督几何和关系特征与深度学习方法。ISPRS 国际地理信息杂志。8(5),213;doi.org/10.3390/ijgi8050213 — Jack Dangermond 奖(新闻报道链接

  3. Bassier, M., Vergauwen, M., Poux, F.,(2020)。建筑内部分类中的点云与网格特征。遥感。12,2224. https://doi:10.3390/rs12142224

《LiDAR 城市模型的 3D Python 工作流程:一步步指南》

原文:towardsdatascience.com/3d-python-workflows-for-lidar-point-clouds-100ff40e4ff0

3D Python

Florent Poux, Ph.D.Towards Data Science Florent Poux, Ph.D.

·发表于 Towards Data Science ·38 分钟阅读·2023 年 4 月 4 日

--

解锁 3D 城市建模应用的精简工作流程的终极指南。教程涵盖了结合 3D 点云、网格和体素的 Python 自动化,以进行高级分析。

《LiDAR 城市模型的 3D Python 工作流程:一步步指南》。封面来自我的另一半Marina,展示了 3D 城市建模的艺术过程。© Mimatelier

你之前遇到过“智能城市”这个词吗?或者“智能某物”?好吧,我们会涉及这个话题!将智能城市想象成一个超能的面包师 🥐:它知道你需要什么,甚至在你提出之前就会提供最直接的建议,帮助你做出美味的选择。不,这个智能城市的比喻并不是我今天唯一要分享的内容。确实,要达到这种“智能”的水平,我们首先得从基础层面入手:3D 城市模型。

如果你曾经想要创建令人惊叹的 3D 城市模型,但发现工作流程令人生畏且复杂,那么我可以帮忙!本文探讨了如何利用 Python 和开源软件定义一个强大的 3D 工作流程,以启动你的 3D 城市建模之旅。向繁琐的手动流程说(几乎)再见,迎接高效、动态且引人注目的创作吧!

我们深入探讨了一个四步策略,描述了环境设置、3D 数据策划与准备以及 3D 几何处理,以提取关键洞察,例如使用点云数据、网格、体素以及一些灰质 🧠,了解你所在社区的建筑覆盖情况。

《LiDAR 城市模型的 3D Python 工作流程:一步步指南》。© 作者

如果你已经准备好并充满热情,现在是时候开始 3D 编程了!

🎵致读者的说明:这本实践指南是* UTWENTE 与我亲爱的同事们 Dr. Sander Oude Elberink, Dr. Mila Koeva, Dr. Ville Lehtola, Dr. Pirouz Nourian, Dr. Paulo Raposo. 和 Prof G. Vosselman* 的共同工作之一。我们感谢来自数字双胞胎* @ITC 项目的资金支持,该项目由特温特大学 ITC 学院授予。

我们将在本指南中处理的 3D Python 数据集的摘录。© 作者

引言

在进入有趣的部分之前,让我讲一个小故事,为我们将要实现的目标提供一些背景。这始于一个基本的问题:什么是 3D 城市建模,它为何有用?

在一个城市化的世界里,3D 城市建模对于高效管理我们的日常生活至关重要。通过准确地以三维方式呈现我们的城市,我们可以分析和可视化复杂的城市环境,理解提议更改的影响,并做出明智的决策,以改善居民的生活质量。这是一个基本的概念,由那句优美的话很好地表达:城市塑造生活¹。

朝向智能城市以改善我们的生活。© 作者

的确,城市是人们生活、形成社区并建立自己身份的地方。它们是如市中心和郊区这样的空间,提供了一种配置和塑造物质世界和自然环境的方式。

想象一下,如果你有一种超级能力,可以对你的城市交通网络进行建模,预测交通模式并识别拥堵区域。这将如何改变你在城市中的生活方式?这只是作为城市“用户”的一个微小例子。但从根本上讲,3D 城市建模为城市规划师、建筑师和政策制定者提供了宝贵的见解,以优化城市基础设施、减少交通拥堵并提高公共安全。

通过创建我们城市的数字化复制品,我们可以更好地规划未来,为子孙后代创造可持续、宜居的社区。²⁻³

¹ Chen, X., Orum, A. M., & Paulsen, K. E. (2018). 《城市导论:地方和空间如何塑造人类体验》。约翰·威利父子公司。

² Lehtola, V. V., Koeva, M., Elberink, S. O., Raposo, P., Virtanen, J. P., Vahdatikhaki, F., & Borsci, S. (2022). 《城市数字双胞胎:服务城市需求的技术综述》。《国际应用地球观察与地理信息杂志》,102915。

³ Nourian, P., Ohori, K. A., & Martinez-Ortiz, C. (2018). 城市计算的基本手段:基于网络的城市规划计算平台的规范,旅行者指南。城市规划,3(1),47–57

3D 城市建模:工作流程

是时候认真起来,定义一个一致的 3D 工作流程,我们可以将其作为不同 3D 城市建模操作的灵感。我们的目标是(1)易于设置和运行,(2)为各种场景提供极大的灵活性,以及(3)足够强大以涵盖多模态应用的复杂性。

🦚 注意不,多模态不是脏话:它只是触及到当处理 3D 城市模型时,我们遇到各种需要考虑的地理空间数据模态。在本教程中,我们将专注于一个美丽的细分领域: 点云 体素,以及 3D 网格*。

如果我们从高层次定义工作流程,我们会遵循四个主要步骤,如下所示。

在城市模型背景下的 3D Python LiDAR 工作流程。我们从环境设置(步骤 1)和 3D 数据准备(步骤 2)开始。一旦完成这些步骤,我们进入 Python 自动化(步骤 3),其中一个特定部分处理 3D Python 挑战(步骤 4),例如地块表面或兴趣点查询。

激动了吗?仔细查看流程,你会发现我们从零开始。这允许将提出的结构适应各种场景,这些场景需要不同的环境、数据集或自动化工具。

现在让我们更深入一点。假设我们在荷兰拥有一所房子(可以靠近特文特大学),我们想更好地了解周围的区域。这是我们的起点。现在,为了更好地理解我们房子与周围环境的关系,出现了一些问题:邻里建筑区的密度有多高?房子是否可能面临洪水?我是否遵守了我所拥有地块的建筑比例?

我们应该如何解决这些问题?我们是否应该上网查找?是否应该打开地图?是否应该联系测绘服务?让我们一起探索这个过程,同时在这个背景下解锁一套新的强大技能。

在接下来的内容中,我们详细介绍了回答这些问题的四个步骤:第一步是理想的环境设置。其次,我们获取 3D 点云和作为感兴趣区域网格的城市模型。然后,我们创建一个高度关注自动化的 3D Python 笔记本。最后,我们创建一套 Python 函数以应对具有挑战性的场景。好了,让我们拿起咖啡或茶🍵,深入寻找这些问题的答案吧!

🦚 注意我设计了下一系列操作,使其易于线性跟随,无需编码技能或可用数据。然而,如果你是经验丰富的编码人员,我提供了有用的优化技巧,以确保代码性能达到巅峰!

第一步:3D 环境设置

在开始动手编写代码和进行 3D 思考之前,一个好的做法是确保我们在适当的环境中工作。我们不会在肮脏的台面上用钝刀和过期的食材做饭(或者至少我们会避免 😁)!让我们遵循下面所示的三个子步骤。

第一步:3D 环境设置。

1. 1. 软件堆栈

为了开始,让我们首先安装一个 3D 点云和网格处理软件:CloudCompare。它是一个出色的工具,允许有效地处理点云数据的科学分析(但不限于此)。它是任何迭代实验中的一个重要部分,例如,当你想快速获得关于理论可行性的数据驱动想法时。

要获取 CloudCompare 软件,你可以前往 CloudCompare.org 的下载部分,获取适合你操作系统的最新稳定版,如下所示。

cloudcompare.org/ 下载 CloudCompare

下载软件后,按照线性安装步骤操作,直到获得可以打开的工作软件,启动时界面应类似于以下 GUI:

CloudCompare GUI。

然后,我们希望获得一个允许我们专注于实际代码的 Python 发行版。它被称为 Anaconda,可以在各种平台上从 Anaconda.com 获得。下载与您的操作系统匹配的软件后,您可以进行安装。提供了一个 GUI(Anaconda Navigator),您可以启动它以快速开始,如下所示。

这是 Anaconda Navigator GUI,允许你管理未来实验的独立 Python 环境。

使用 GUI Anaconda Navigator,转到左侧的“环境”标签。然后,我们通过点击“创建”来创建一个全新的隔离 Python 环境,如下所示。

在 Anaconda Navigator 中,你的左侧有四个标签。在“首页”标签中,你会发现 IDE 处于特定环境中;在“环境”标签中,你可以创建、管理和选择任何环境。

这将使我们能够更好地管理一些我们想要安装的 Python “库”(第 1.3 步),同时避免版本冲突。

🦚 注意对于本教程,我们选择了 Python 版本 3.9.16,如上所示。创建环境后,点击它,你会看到其名称旁边有一个“播放”图标。这将开启直接在所选环境中启动 Anaconda Terminal 的可能性。这是在此环境中安装库或 IDE 的首选方式*

如果你已经有一个工作中的 Anaconda Navigator 和一个新创建的环境,我们就可以选择一种编码方式,旨在比文本编辑器更高效。😁

1.2. Python IDE

Python IDE(集成开发环境)是一种软件应用程序,提供了一整套用于开发、测试和调试 Python 代码的工具。它们提供了一个一体化的环境,我们可以在其中编写、编辑和执行代码,管理项目文件,跟踪更改,并与其他开发人员协作。一些流行的桌面 Python IDE 包括 PyCharm、Visual Studio Code 和 Spyder,每个 IDE 都提供了适应不同编程需求的独特功能和能力。

今天,我想重点介绍一个出色的“基于网络”的 IDE:JupyterLab。JupyterLab 的主要优点之一是其笔记本界面,它提供了一个可视化的、互动的编程环境。它使得实时创建、编辑和运行代码单元变得简单,而无需在不同窗口或应用程序之间切换。此外,JupyterLab 支持广泛的数据可视化工具,是处理任何地理数据科学或 3D 机器学习项目的绝佳选择。

JupyterLab IDE 用于 3D Python。

JupyterLab 直观的界面、强大的功能集以及对多种编程语言的支持,使其成为各个技能水平的 Python 开发人员的热门选择。

🦚 注意JupyterLab IDE 还支持多种编程语言,包括 Python、R 和 Julia,允许在一个环境中使用各种工具和库。这非常酷,因为 R 和 Julia 是很棒的语言。

在能够使用 JupyterLab 之前,我们需要在当前 Anaconda 环境中安装它。如前所述,我们必须在所选环境中打开 Anaconda Terminal(在我们的例子中是 ITC)。要通过 Anaconda Navigator 实现这一点,从你选择的环境中,(1)点击绿色箭头,(2)选择 Open Terminal,如下所示。*

如何在当前环境中安装依赖。

在打开的控制台(Anaconda Terminal)中,输入以下命令:conda install -c conda-forge jupyterlab,然后按“Enter”。这将直接安装 JupyterLab。

命令行在 Anaconda Terminal 中执行(我们看到我们在(ITC)环境中)。

请注意,它可能会要求你确认安装一些需要的库,你需要通过输入y并按下“Enter”键来接受。

经过 30 多秒后,安装需要我们的确认。输入‘y’并按Enter键。这将下载、解压并安装上述提到的库。

一旦过程完成,你就有一个安装在 ITC Conda 环境中的 JupyterLab IDE。不要关闭控制台;从那里,我们将通过四个简单的步骤测试一切是否顺利。

  1. 启动 JupyterLab:在相同的控制台中,你可以通过输入命令:“jupyter lab”来启动 JupyterLab。这将自动在默认网页浏览器中打开 JupyterLab 界面。

  2. 创建一个新的笔记本:要创建一个新的笔记本(我们在其中编写代码),请点击 JupyterLab 界面左上角的“文件”菜单,并选择“新建笔记本”。这将会在新标签页中打开一个新的笔记本。

  3. 编写代码:现在你可以在笔记本中开始编写代码。要创建一个新的代码单元格,请点击工具栏中的“+”按钮或按“Ctrl + Shift + Enter”。然后你可以在单元格中编写 Python 代码(例如:‘This is working’),并通过按“Shift + Enter”运行它。

  4. 保存你的工作:记得定期保存你的工作,通过点击工具栏中的“保存”按钮或使用“Ctrl + S”键盘快捷键来保存。

这些是开始使用 JupyterLab 的基本步骤。随着你对界面的熟悉,你可以探索更多高级功能,如添加 Markdown 文本、导入和导出数据以及使用 JupyterLab 扩展。

🦚 注意对于 ITC—特温特大学的学生,我们很幸运能使用 CRIB: A Geospatial Computing Platform 进行 Jupyter Lab。 如果你的计算机在跟随本课程时显示出某些处理限制,我强烈推荐使用这个云计算服务。

1.3. 3D Python 库

在本教程中,我将介绍五个对 3D 地理空间分析至关重要的库。这些库是NumPyPandasOpen3DMatplotlibShapely

🦚 注意如果你想使用上述库,我们必须确保它们在你的环境中已安装并可用。因此,在相同的环境终端中,我们使用公式pip install package-name==version(其中==version是可选的,用于固定某些版本),如下面所示:

#We install the 5 libraries with the package manager 'pip', one line at a time
pip install numpy
pip install pandas
pip install open3d==0.16.0
pip install matplotlib
pip install shapely

NumPy:这个库用于处理数组和矩阵。它提供了对大型多维数组的快速高效操作,使其成为科学计算和数据分析的强大工具。一个使用NumPy的实际示例是创建一个点云,将其作为 3D 欧几里得空间中的数据点集合。为此,你可以创建一个具有三列的 NumPy 数组,每一行表示点云中的一个点:

import numpy as np
point_cloud = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

#To print the "point_cloud" variable as [Out] from the cell
print(point_cloud) 

在这个示例中,点云包含三个坐标为(1, 2, 3)(4, 5, 6)(7, 8, 9)的点。简单吧,你说?😁

Pandas:这个库更侧重于数据操作和分析。它提供了强大的数据结构和工具,用于处理结构化数据,例如 CSV 文件、电子表格和数据库。虽然它并不是专门为 3D 数据处理设计的,但仍可以用来以表格格式读写点云。为此,你可以创建一个 DataFrame 对象,其中列表示每个点的 X、Y 和 Z 坐标:

import pandas as pd

# create a DataFrame with X, Y, and Z columns
points_df = pd.DataFrame({ 'X': [1, 4, 7], 'Y': [2, 5, 8], 'Z': [3, 6, 9]})

# save the DataFrame as a CSV file at the same place as your script
points_df.to_csv('point_cloud.csv', index=False)

在这个示例中,点云包含三个坐标为(1, 2, 3)(4, 5, 6)(7, 8, 9)的点,如使用 NumPy 获得的。然后,DataFrame 使用 to_csv 函数保存到 CSV 文件中。

🦚 注意Pandas 可以通过另一个 Python 模块进行扩展: Geopandas。这个库使得可以直接处理存储在例如 Shapefiles 或 PostGIS 数据库中的空间数据。这扩展了当前教程的范围,但了解这些内容是有益的,因为我们在其他情况下肯定会用到它。 😉

Open3D:这个库专注于 3D 数据处理和可视化。它提供了各种工具和函数,用于处理点云、网格和其他 3D 数据格式,如体素。

🦚 注意快速安装该库的方法是运行 Anaconda 环境终端并输入以下命令: pip install open3d==0.16.0 ,这将安装版本 0.16.0 的 Open3D,如本教程中使用的版本。

现在,在 Jupyter Lab IDE 中,让我们使用内置的 Open3D 函数创建一个点云:

import open3d as o3d

# create an empty point cloud object
point_cloud = o3d.geometry.PointCloud()

# add points to the point cloud
points = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
point_cloud.points = o3d.utility.Vector3dVector(points)

在这个示例中,点云包含三个具有坐标(1, 2, 3)(4, 5, 6)(7, 8, 9)的点。

🦚 注意在上面的代码块中,我们创建了一个 Open3D PointCloud 对象,然后通过 o3d.utility.Vector3dVector 函数将点列表传递给 points 属性,该函数将点列表转换为可以添加到点云对象中的格式。

Matplotlib:这个库用于数据可视化。它提供了许多工具,用于创建高质量的图表、图形和其他可视化内容。虽然它并不是专门为 3D 数据可视化设计的,但它仍然可以创建点云的 3D 散点图。为此,你可以使用 Axes3D 类创建 3D 图,并使用 scatter 函数绘制点:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

# create a point cloud with 1000 random points
points = np.random.rand(1000, 3)

# create a 3D plot and add the points as a scatter plot
fig = plt.figure()
ax = fig.add_subplot(111, projection=’3d’)
ax.scatter(points[:,0], points[:,1], points[:,2])

# show the plot
plt.show()

在这个示例中,使用 NumPy 生成了一个包含 1000 个随机点的点云。然后,使用 scatter 函数在 3D 图中绘制这些点。最后,使用 show 函数显示图形。

Shapely:这个库主要用于 2D 几何操作,用于创建、操作和分析几何对象。它提供了广泛的工具来处理点、线、多边形和其他几何形状。Shapely的一个常见用例是创建一个多边形,并检查一个点是否在多边形内。为此,你可以使用定义多边形的坐标列表创建一个 Polygon 对象,然后使用contains函数检查点是否在多边形内:

from shapely.geometry import Point, Polygon

# create a polygon with four vertices
polygon = Polygon([(0, 0), (0, 1), (1, 1), (1, 0)])

# create a point
point = Point(0.5, 0.5)

# check if the point is within the polygon
if polygon.contains(point):
 print(‘The point is within the polygon.’)
else:
 print(‘The point is outside the polygon.’)

这只是一个简单的例子,但 Shapely 提供了许多其他用于处理几何对象的函数,这些函数对于更复杂的应用可能会很有用。

🦚 注意在这个例子中,使用一个坐标列表创建了一个具有四个顶点的多边形。然后,使用 Point 函数创建一个点。最后,使用 contains 函数检查点是否在多边形内,并根据检查结果将消息打印到控制台。

在这个 3D 城市建模的工作流程中,我们的第一步是巧妙地结合简单而有效且经过验证的工具和库,下面做了视觉总结。

环境设置的总结。© 作者

现在我们有了明确的软件栈、功能齐全的 Python IDE 和对 Python 库的基本理解,我们可以开始准备数据。

第 2 步:3D 数据准备

我们现在进入第二步:3D 数据准备。

3D 数据准备。我们将下载 3D 数据集,然后开发一些简单的 3D 可视化,通过下采样和导出数据为适合 Python 使用的格式来进行处理。

我们的目标是收集和准备数据集,以便我们用 Python 进行分析。因此,这一步也充当了“3D 数据可视化”阶段,我们将定性评估我们所处理的数据。如果你准备好了,就开始吧。

2.1. 下载 3D 数据集

对于 3D 城市模型分析,我们从开放数据源中收集了一些优秀的数据集。我展示了荷兰一个特定的兴趣区域,但我鼓励你研究你自己的房子或任何对你感兴趣的地点(如果你住在荷兰的话,当然,😉)。

点云数据集。首先,我们使用geotiles.nl门户网站收集点云数据集,该网站提供了一些在 CC-BY 4.0 许可下的良好数据集。为此,你可以访问该网站,缩放到感兴趣的瓦片,并获取最新版本的 AHN LiDAR 点云(在我们的案例中是 AHN4),如下图所示。文件将被下载为.laz(LasZip)文件。

从 AHN-X 项目下载 3D LiDAR 数据集,通过门户网站 geotiles.nl。

🦚 注意AHN(Actueel Hoogtebestand Nederland)源自航拍 LiDAR 覆盖,为荷兰所有地区提供数字高程地图。它包含详细且精确的高度数据,每平方米平均有八次测量。水务局、省份和公共工程部门等组织使用 AHN 进行水务和堤坝管理。根据地面高度和高程,决定水是否能从土地上顺利排出,沟渠中的水位可以多高,河流、洪水平原和沟渠中的水是否能得到充分排放,以及堤坝是否仍然足够高且强大。AHN 还用于许多其他类型的管理,如日常管理和堤坝维护、重大维护的规格准备、3D 绘图、许可和执法。市政府、企业和研究人员也使用详细的高程数据。

如果你喜欢图形,我为你准备了 AHN LiDAR 数据的快速概述:

  • AHN1: 1997–2004, 1 pt/16 m2 至 1 pt/m2

  • AHN2: 2007–2012, 8 pts/m2

  • AHN3: 2014–2019, 8 pts/m2

  • AHN4: 2020–2022, 10–15 pts/m2,

  • AHN5: 2023–2025, 10–15 pts/m2

网格数据集

对于这一部分,我们想在与我们下载的 LiDAR 区域相同的位置找到一个 3D 网格。因此,我们使用由 TUDelft 创建的平台 3DBAG,它允许我们从 CityGML Specification 中检索荷兰的 10M 建筑物,具有 LoD1.2、LoD1.3 和 LoD2.2。在这一步,我们主要关注几何形状。虽然我们知道语义和拓扑是 CityGML / City JSON 数据模型的重要方面,但将在后续文章中探讨。😉

从门户网站 3Dbag.nl 下载数据集。

🦚 注意3DBAG 包含多个细节层级的 3D 模型,这些模型是通过结合两个可用数据集生成的:来自 BAG 的建筑数据和来自 AHN的高度数据。3D BAG 会定期更新,保持最新的开放建筑库存和高程信息

下载数据集后,你应该会有一个.laz 文件格式的点云和一个或多个.obj 数据集(以及其附带的.mtl 文件),这些数据集大致描述了相同的范围,但具有不同的细节层级(在我们的例子中是 LoD 1.2、1.3 和 2.2),如下面所示。

🧙‍♂️ 向导: [可选] 如果你想深入了解 3D 数据文件格式,尤其是点云网格的部分,我建议你跟随下面的教程,详细了解如何将点云网格化及其结构。

## 5 步指南:使用 Python 从点云生成 3D 网格

教程:使用 python 自动从 3D 点云生成 3D 网格(.obj, .ply, .stl, .gltf)。(附赠)…

towardsdatascience.com

为了方便起见,你可以直接从这个 Drive 文件夹 下载选择项。

2.2. 3D 数据可视化

现在我们将进入CloudCompare以确保下载的数据分析没有特殊的惊喜。😁 启动CloudCompare后,你可以从本地文件夹加载.laz点云。当导入窗口显示时,确保仅导入此应用程序的“Classification”额外字段,并接受如下面所示的全局偏移。

在 CloudCompare 中进行 3D 数据可视化。

🦚 注意全局偏移是一个临时的偏移,以允许*CloudCompare*处理地理参考数据,该数据超出了它可以处理的可视化范围。因此,这一步是透明的,并将在保存数据时应用

现在我们可以继续导入网格数据。为此,我们执行相同的导入操作,并接受建议的平移偏移,同时确保它与应用于点云的偏移相同。

从下载的资产中加载 3D 网格。

现在你可以从数据库树中看到当前项目中导入的不同对象(如果找不到 DBTree,可以查看下图)。DBTree的功能有点类似于你的操作系统资源管理器,其中包含不同的点云或网格。每个对象(例如点云或 3D 网格)可以像图层一样可视化地激活 ☑️(或停用)并选择以识别对象属性。

CloudCompare 3D 数据处理和分析界面。© 作者

🦚 注意CloudCompare 默认不保存。为了避免崩溃,如果你担心,可以将所有数据和分析放在 DBTree 中的一个文件夹中(右键点击 > 创建新的空组),并将此文件夹保存为.bin CloudCompare 项目。

2.3. 3D 数据子采样

现在是深入 3D 数据过滤的时候了。首先,我们从DBTree中选择点云,并应用空间子采样函数以每 50 厘米保留一个点,这样可以保留足够的信息,同时不会影响计算速度。为此,我们使用下面所示的subsample函数。

3D 点云子采样

🧙‍♂️ 向导: [可选] 在我们的案例中,我们使用了空间子采样函数,每隔 50 cm 平均保留一个点。如果你想探索和深入 3D 点云采样策略,我推荐深入阅读以下文章。

## 如何使用 Python 自动化 LiDAR 点云处理

关于点云子采样的终极指南,从零开始,用 Python 编写。涵盖 LiDAR I/O、3D 体素网格处理等……

towardsdatascience.com

一旦完成子采样步骤,我们将在 DBTree 中得到一个结果子采样点云,可以用于后续处理。

在 Mesh 方面,导入后,我们可以通过点击每个网格对象旁边的小箭头图标从 DBTree 中打开它,这会显示许多不同的子网格元素。这是因为在原始的 .obj 文件中,我们有一个“对象”细化,这允许我们保留一些与几何体相关的“语义”分解。每个子网格是一个构建实体的分解。

如果你想查看这些元素,可以打开网格,通过按住 Ctrl+Shift 并在第二次鼠标点击时选择所有子元素,然后 右键点击 > 切换 以显示它们。你还可以取消选中父网格元素的 可见 属性,以确保你看到的仅是子元素,如下所示。

CloudCompare 中的 3D 数据准备。

🦚 注意: 我们不能使用子网格选择,因为它们也包含网格父项的所有顶点,这意味着,如果我们使用它,我们将不得不将网格分割为仅选中的子网格的顶点。

很棒,干得好!从那里,你可以取消选择所有元素,只保留你想要考虑的网格(在我们的例子中是 LoD 1.2),并使用例如“横截面”功能,选择你感兴趣的房屋,如下所示。

CloudCompare 中的 3D 数据选择

一旦完成,我们就准备好导出我们选择的研究站点的两种 3D 模态。

2.4. 3D 数据导出

关于点云数据,我们可以以各种文件格式导出它。由于我们希望保留分类字段以进行一些 Python 分析(例如,了解一个点是否属于地面或建筑物),我们将 3D 点云导出为 ASCII 文件,以便在 Python 中轻松处理,如下所示。

CloudCompare 中的 3D 点云数据导出。

🦚 注意在保存文件时,我们可以选择修改文件扩展名为 *.xyz* ,并在导出对话框打开时勾选“保留列名”选项。这将允许在文件中写入列名。然而,如果你用文本编辑器打开文件,确保删除两个不必要的反斜杠,如上所示。

我们的第一个 3D 数据集已经准备好,可以作为.xyz文件用于 Python。现在,让我们继续进行 3D 网格处理。

选择你感兴趣的房屋/建筑块后,我建议你获取相关的子网格元素名称,并将其用作导出文件的名称。关于导出对话框的选项,你可以选择.obj文件扩展名,这是一个安全的选择😉。

CloudCompare 中的 3D 网格导出。

3D 网格现在已经准备好,并附带一个材料.mtl文件(在我们的案例中不太有用)。

步骤 2 已完成!干得好。我们首先收集了荷兰一个区域的 3D 点云和 3D 网格。然后我们进行了可视化,以检查它们是否符合我们的意图。接着我们过滤了点云,保留每 50 厘米左右一个点,3D 网格只保留一个代表建筑房屋的对象,并将两种数据分别导出为.xyz.obj + .mtl文件。¹

步骤 2 的可视化总结:3D 数据准备。© F. Poux

现在让我们将这些数据集放入一个出色的 Python 设置中,以最大化自动化和 3D 分析!

¹为了方便,你可以在这个Drive 文件夹中找到这些数据集。

第 3 步. Python 自动化

现在真正有趣的部分开始了,是时候用 Python 编程了!🤓

3D Python 自动化。

如上所示,我们将遵循五个阶段的方法,包括导入库、加载数据集、设置我们的 3D Python 可视化工具、定义 3D 挑战的解决方案,然后将结果导出以供 Python 之外使用。让我们先在 Python 中建立我们自动化管道的主体,然后再处理不同的挑战。

3.1. 导入库

如步骤 1 中定义的,我们将坚持使用最少的库,以加深我们对其使用的了解。这些库包括NumPyPandasOpen3DMatplotlibShapely

我们将从我们的 IDE 中在 Python 笔记本(.ipynb)中编写以下代码行。

用于编写我们脚本的 IDE 视图。

我们用以下代码块导入上述提到的库:

import numpy as np
import pandas as pd
import open3d as o3d
import matplotlib.pyplot as plt
from shapely.geometry import Polygon

print(f"Open 3D Version: {o3d.__version__}")

这将返回当前的 Open 3D 版本作为字符串:Open 3D Version: 0.16.0。我们已经设置好了,可以继续加载数据集。

3.2. 加载数据集

现在我们将定义存储数据集的具体路径。我喜欢将其明确且相对于我的代码文件。这样,一切都相对表达(../ 表示转到父文件夹),使得在不同机器上编码时,浏览文件夹变得容易。

data_folder="../DATA/"
pc_dataset="30HZ1_18_sampled.xyz"
mesh_dataset="NL.IMBAG.Pand.0637100000139735.obj"
result_folder="../DATA/RESULTS/"

点云数据集

我们可以通过首先创建一个名为 pcd_df 的 Pandas DataFrame 对象来准备点云,该对象将包含点云数据:

pcd_df= pd.read_csv(data_folder+pc_dataset, delimiter=";")
print(pcd_df.columns)

这将返回 pcd_df 数据框中的列名,它们是:[‘X’, ‘Y’, ‘Z’, ‘R’, ‘G’, ‘B’, ‘Classification’]。这对于仅选择明确的“列”而不是模糊的索引非常有用 😁。这正是我们要做的:仅选择 [‘X’, ‘Y’, ‘Z’] 坐标以创建 Open3D PointCloud 对象。这是一个很好的方法来理解当我们使用不同的库时,我们必须适应不同的机制以将数据集转换为不同的 Python 对象。在这里,我们从 Pandas DataFrame 转到 Open3D PointCloud,这是使用 Open3D 库中实现的 Open3D 函数所必需的。让我们按以下四个步骤进行:

Open3D 点云创建的概念工作流程。© 作者。

这个分解机制转化为以下内容:

创建 Open3D 点云的代码工作流程。© 作者。

不过,如果我们想要更简洁一点,这个转换可以通过一行代码完成,即:

pcd_o3d=o3d.geometry.PointCloud(o3d.utility.Vector3dVector(np.array(pcd_df[['X','Y','Z']])))

我们现在有两个重要的变量:pcd_o3dpcd_df。我们还可以为 Open3D PointCloud 对象添加一些颜色:

pcd_o3d.colors = o3d.utility.Vector3dVector( np.array( pcd_df[[‘R’,’G’,’B’]]) / 255 )

我们必须注意将 R,G,B 值转换为 [0,1] 范围内的浮点值,并确保将 Vector3dVector 对象传递给 colors 属性。

网格数据集

现在,我们可以使用 open3dread_triangle_mesh() 方法将 3D 网格加载到 mesh 变量中。我们还可以使用 paint_uniform_color() 方法为网格上色:

mesh=o3d.io.read_triangle_mesh(data_folder+mesh_dataset)
mesh.paint_uniform_color([0.9,0.9,0.9])

我们现在有两个 Open3D 对象:一个包含 7 103 848 个点的 APointCloud 和一个包含 674 个点和 488 个三角形的三角网格。让我们看看这意味着什么,好吗?

3.3. Python 3D 可视化

要在 Open3D 中可视化不同的 3D 对象,我们必须传递一个包含这些 Open3D 对象的 python 列表。因此,我们的列表由一个 Open3D PointCloud 和一个 Open3D TriangleMesh 组成,即 [pcd_o3d, mesh]。让我们使用以下代码在独立窗口中可视化这个组合:

o3d.visualization.draw_geometries([pcd_o3d,mesh])

🦚 注意上面的代码行将创建一个交互式的 Open3D 窗口,结合了 3D 点云和 3D 网格。

要调整显示的颜色,有一个实用的技巧:使用一个颜色变量,该变量将传递给 PointCloud Open3D 对象的颜色属性。此变量应包含从 01 的 R、G、B 浮点值。

🦚 注意你是否注意到两个截图之间的细微差别?右侧我们可以更好地勾勒出边界,并且有更强的深度感(这在交互式操作中更为明显)。这是因为在第二种情况下,我们还使用了法线进行可视化。要做到这一点,你可以在运行可视化部分之前运行 pcd_o3d.estimate_normals() mesh.compute_vertex_normals() 。享受吧 😉

假设我们想要根据分类属性可视化点云。我们需要遵循以下四阶段过程:

使用分类作为 Open3D 颜色的概念工作流程。

如果你仔细观察,这将转换为如下代码:

使用分类作为 Open3D 颜色的代码工作流程。

# 1=unclassified, 2=Ground, 6=building, 9=water, 26=rest
pcd_df['Classification'].unique()
colors=np.zeros((len(pcd_df), 3))
colors[pcd_df['Classification'] == 2] = [0,0,0]
colors[pcd_df['Classification'] == 6] = [1,0,0]
colors[pcd_df['Classification'] == 9] = [0,0,0]
colors[pcd_df['Classification'] == 26] = [0,0,0]
pcd_o3d.colors = o3d.utility.Vector3dVector(colors)

🦚 注意因为我们希望对每个类别的颜色进行控制,我们可以根据唯一类别的数量调整上述代码。然后,映射是基于 LAS (1.4) 规范进行的。

LAS LiDAR 数据规格文件格式的 ASPRS 标准点类别。

我们可以可视化结果,类似于这样(根据输入的R,G,B值显示不同的颜色):

Open3D 交互式多模式数据可视化。© 作者。

我们已经加载了变量,可以看到点云和 3D 网格,一切看起来运行顺畅!现在是定义我们想要进行的各种 3D 城市分析任务的时刻!

3.4. 3D 城市分析

3D 城市分析指的是使用城市环境的三维(3D)模型来分析和理解建筑环境的各种方面,如建筑能源性能、城市形态和行人流动。这种分析通常涉及使用专业范式和进行分析,以获得有关城市规划和设计的见解。3D 城市分析可供城市规划师、建筑师和工程师使用,以提供有关基础设施布置、公共空间设计和各种环境影响缓解的决策信息。在本教程中,我们将涉及 3D 城市分析的三个主要方面:

  1. 城市形态分析:我们可以使用 Python 分析 3D 城市数据集中建筑物的形状和形式,这可以帮助提供有关城市设计和规划的决策。

2. 地理空间分析:我们可以对城市模型进行地理空间分析,例如根据可达性和环境影响等因素确定新基础设施项目的最佳位置。

3. 3D 可视化:我们可以创建交互式 3D 可视化,这可以帮助利益相关者更好地理解和参与城市规划和设计项目。

因此,这一步专门用于一个应用程序,我们在这里进行大部分分析。我们在 3D 城市分析中进行演示,并在第四部分通过各种 Python 挑战进行扩展,如下所示。

城市背景下的 3D 分析及其在第 4 步中的分解:3D Python 挑战(第四部分)。

不过,我们可以遵循一个通用的工作流程,适应于每个应用程序,通常由输入、处理管道和每个特定分析部分的输出组成。

从输入到输出的概念性工作流程,适用于点云数据。

输入可以有所不同,但在大多数情况下,它是一个包含空间信息的 NumPy 数组。输出可以是一个整数、一个列表、另一个 NumPy 数组……你需要的任何东西。😁

🦚 注意这个特定阶段比较复杂,我们将在笔记本中留出一些空间,用于记录与第 4 步挑战相关的不同分析。现在,让我们定义一些方法来导出我们的数据。

3.5. 3D 多模态导出

在我们的 3D 管道完全功能后,我们可以将结果保存到一个或多个文件中,以便在 Python 之外处理。为此,我们将使用两种有价值的可能性。

  1. Numpy(推荐用于复杂输出): 我们可以使用以下代码行通过 Numpy 导出:np.savetxt(result_folder+pc_dataset.split(“.”)[0]+”_selection.xyz”, np.asarray(o3d_parcel_corners),delimiter=’;’, fmt=’%1.9f’)

  2. Open3D(推荐仅需空间属性时使用): 我们可以使用以下代码行通过 Open3D 导出:o3d.io.write_point_cloud(result_folder+pc_dataset.split(“.”)[0]+”_result_filtered_o3d.ply”, pcd_selection, write_ascii=False, compressed=False, print_progress=False)

🦚 注意.split(.)允许将pc_dataset字符串对象拆分为两个字符串的列表,分别在.之前和之后,然后我们仅保留第一个元素,使用[0]。NumPy 将一个变量o3d_parcel_corners的阶段以分隔符 ; 导出到一个.xyz ASCII 文件中。Open3D 将从 open3d 对象pcd_selection中写入一个.ply 文件。*

哇,干得好!Python 自动化的批量结构已经运行起来了!恭喜!我们已经导入了库,数据集被存储在不同的显式变量中,我们确保可以处理各种可视化而无需离开 Python 的舒适环境。导出步骤已设置完成;剩下的就是解决我们在第 4 步中提出的初始问题:

我们房子周围的建筑区域有多密集?房子是否可能会受到洪水影响?我是否遵守了我拥有地块的建筑比例?

步骤 4. 3D Python 挑战

为了回答上述问题,我们将找到解决四个挑战和体素化步骤的方案,如下所示。

步骤 4:3D Python 挑战。我们研究兴趣点查询、手动边界选择、高点提取、体素化和建筑覆盖提取。

挑战 1 将允许裁剪出所需的研究区域。挑战 2 将允许提取所拥有地块的建筑比例。挑战 3 指导洪水分析。而挑战 4,经过体素化处理后,将允许获取感兴趣区域的建筑覆盖情况。让我们开始吧。

4.1. 兴趣点查询

对于这个挑战,我们从一个包含 3D PointCloud Open3D 对象和 TriangleMesh Open3D 对象的输入开始,如下所示:

一个带有 3D 网格对象的 3D 点云。© F. Poux

这个挑战的目标是只保留落在兴趣点(建筑房屋)一定距离内的数据点。我们的输入是点云和网格,输出是经过过滤的点云,符合到 POI 距离的标准,如下所示:

为此,我设置了一个六阶段的过程,如下所示:

3D 点云半径搜索的概念工作流。© 作者。

🎓 学习笔记目标是尽量自己解决问题,如果遇到困难可以查看下面的解决方案。准备好后,可以阅读下面的解决方案。 👇

(1) 设置距离阈值时,我们将所需的半径值(例如 50)传递给新变量 dist_POI。 (2) 然后,我们使用 Mesh 对象的 get_center() Open3D 方法从 BAG 数据集中获取 POI。这允许获取网格的中心作为 POI:POI=mesh.get_center()。之后,我们可以创建 KD 树 (3),一种用于组织空间中点的数据结构。KD 树对点云处理非常有用,因为它们允许快速进行最近邻搜索和范围查询。这是好消息,因为这正是我们要做的 😁。实际上,使用 KD 树可以快速找到离给定点最近的点,或者所有在一定距离内的点(我们的 POI),而无需遍历点云中的所有点。

# Creating a KD-Tree Data Structure
pcd_tree = o3d.geometry.KDTreeFlann(pcd_o3d)

🦚 注意KD-Tree 通过递归地将空间划分为较小的区域,每个级别沿一个维度的中位数进行划分(X 是一个维度, Y 是另一个, Z 是另一个 😉)。这会生成一个“树”结构,其中每个节点表示一个空间分区,每个叶子节点表示一个单独的点。

从那里,我们可以使用 Open3D 的 search_radius_vector_3d() 方法,并从输出索引中选择点:最后可视化我们的结果(4+5+6):

# Selecting points by distance from POI (your house) using the KD-Tree 
[k, idx, _] = pcd_tree.search_radius_vector_3d(POI, dist_POI)
pcd_selection=pcd_o3d.select_by_index(idx)

我们最终可以可视化我们的结果(6):

o3d.visualization.draw_geometries([pcd_selection,mesh])

半径搜索下的 3D 点云和网格叠加。

总共涉及以下代码管道:

半径搜索的代码工作流。

非常好!现在你有了一个有效的解决方案,让我们从那个选择中提取地块面积。

4.2. 手动边界选择

要提取非官方边界,我们需要采用一些半自动和互动的方法。好消息是,我们可以直接在 Python 中使用 Open3D 来完成这项工作。

首先需要创建一个带有以下 draw_geometries_with_vertex_selection() 方法的互动 Open3D 窗口:

o3d.visualization.draw_geometries_with_vertex_selection([pcd_selection])

然后可以跟随下面的动画部分,这允许选择定义地块角落的点。

手动边界选择的互动过程,使用 Open3D GUI 并按住 MAJ+鼠标选择感兴趣的点。

结果将出现在你的笔记本(或 REPL)中的单元格下方,关闭窗口后即可查看。

🦚 注意在此步骤中,使用 R G B 着色可能更容易。因此,如果您想使用正确的索引,需要在选择前返回进行更改。 😉 如果您想提取地籍边界可以通过导入官方的 2D 矢量图形并根据此数据约束进行裁剪来实现。

从你的 REPL 中,你可以将选定点的不同索引(例如 34335979215441966659242181638008)复制并粘贴到 select_by_index() 选择方法中,以定义 o3d_parcel_corners 变量:

o3d_parcel_corners=pcd_selection.select_by_index([34335 ,979 ,21544 ,19666 ,5924 ,21816 ,38008 ])

我们仍然需要进一步准备角落,因为我们想避免考虑 Z 值。因此,我们将过滤坐标以去掉 Z 值,但要注意:这样做意味着我们认为我们在一个平坦区域(这在荷兰是事实 😉)。

o3d_parcel_corners=np.array(o3d_parcel_corners.points)[:,:2]

从那里,接下来使用 Shapely 库计算地块的面积!为此,我们可以直接使用 Polygon 函数首先从提供的角落集合中创建一个多边形:

Polygon(o3d_parcel_corners)

输出如下:

错误的几何形状

这看起来有点不对,是吧?如果你不相信我,我们可以计算面积来检查:

pgon = Polygon(o3d_parcel_corners)
print(f"This is the obtained parcel area: {pgon.area} m²")

这会输出43.583 m²。对于一个大建筑来说,这听起来很奇怪,不是吗?哈哈,一个简单的问题变得有点复杂了!实际上,这里的问题在于计算面积需要一个由多边形组成的闭合形状。这并不总是能实现,因为添加角点的顺序可能会影响数据结构。因此,问题变成了排序坐标以避免边缘交叉。

处理这个问题的一种方法是从中心点的角度来看所有坐标。然后,我们可以计算列表中每个角点与中心点之间的角度。我们这样做是为了了解每个角度的宽度,从而提供排序坐标的手段。将其翻译成代码允许定义一个排序函数,如下所示:

def sort_coordinates(XY):
    cx, cy = XY.mean(0)
    x, y = XY.T
    angles = np.arctan2(x-cx, y-cy)
    indices = np.argsort(-angles)
    return XY[indices]

🦚 注意我使用 NumPy arctan2() 方法对每个坐标进行角度估算。这将返回以弧度表示的角度数组。剩下的工作是将角度按升序排序,以获取正确顺序的索引列表。然后,列表可以使用 argsort() 方法修正原始列表的索引。在此步骤中,它不适用于 U 形建筑。

从那里,我们可以将排序函数应用于角点,创建一个新的排序变量,计算多边形及其相关面积:

np_sorted_2D_corners=sort_coordinates(o3d_parcel_corners)
pgon = Polygon(np_sorted_2D_corners)
Polygon(np_sorted_2D_corners)
print(f"This is the parcel area: {pgon.area} m²")

这返回了一个面积为2 247.14 m²的多边形,这更为可信。😉

正确的几何形状

再次做得很好。我们现在对我们地块的面积有了一个不错的了解。现在,让我们在区域内找到高点和低点。

4.3. 查找高点和低点

要获取区域的低点和高点,一种特定的方法是使用 Open3D 的get_max_bound()get_min_bound()方法:

pcd_selection.get_max_bound()
pcd_selection.get_min_bound()

但是,这样做不会提示哪个点是最高的,哪个点是最低的。我们需要的是点的索引,以便在之后检索坐标。为此,我建议我们这样编码:

  1. 我们从 Open3D PointCloud 对象中创建一个 NumPy 数组对象np_pcd_selection,该对象仅包含XYZ坐标。

  2. 我们使用argmax()方法收集Z维度上的最小值和最大值的索引,并将结果存储在变量lowest_point_indexhighest_point_index中。

np_pcd_selection=np.array(pcd_selection.points)
lowest_point_index=np.argmin(np_pcd_selection[:,2])
highest_point_index=np.argmax(np_pcd_selection[:,2])

让我们通过选择索引,创建TriangleMesh球体,将它们转换到点的位置,然后使用 Open3D 进行可视化来检查结果:

# We create 3D Spheres to add them to our visual scene
lp=o3d.geometry.TriangleMesh.create_sphere()
hp=o3d.geometry.TriangleMesh.create_sphere()

# We translate the 3D Spheres to the correct position
lp.translate(np_pcd_selection[lowest_point_index])
hp.translate(np_pcd_selection[highest_point_index]))

# We compute some normals and give color to each 3D Sphere
lp.compute_vertex_normals()
lp.paint_uniform_color([0.8,0.1,0.1])
hp.compute_vertex_normals()
hp.paint_uniform_color([0.1,0.1,0.8])

# We generate the scene
o3d.visualization.draw_geometries([pcd_selection,lp,hp])

如下所示,我们现在有了包含高点和低点的 3D 场景。

让我们在 350 米的扩展区域内研究建筑覆盖情况。为此,我们将重新执行代码中的某些部分,如下所述。

4.4. 点云体素化

我们想要提取已建立的覆盖范围。为此,我们将采取一种直观的方法,首先将点云的模式转换为 2D 像素的填充模拟:3D 体素。这将允许我们使用以下方法:

通过构建 3D 体素数据结构来提取高点。我们首先对点云进行体素化,然后从二进制角度对每个体素上色,最后按自上而下的索引进行筛选。© F. Poux

基本上,我们将(1)生成存在点的体素,然后(2)根据体素所持有的点的分类对体素上色,最后,我们将筛选体素,只保留和计数每个 X,Y 坐标的最高体素。这样,我们可以避免计数那些会偏向已建立覆盖范围的元素。

第一步是创建一个体素网格。这是 Open3D 的强项:通过这些简单的代码行,我们可以将一个体素网格适配到点云中,每个体素是一个 20 cm 的立方体,然后可视化结果:

voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd_selection, voxel_size=2)
o3d.visualization.draw_geometries([voxel_grid])

3D 体素数据结构的视图。© F. Poux

🦚 注意您看到的结果已经经过体素上色的变化。这个变化意味着,使用 Open3D 时,我们必须更改体素点的颜色。然后,VoxelGrid 方法将平均这些颜色,并将结果保留为体素的颜色。

现在我们知道如何从点云生成 3D 体素,让我们通过改变它们的颜色方案来绕过颜色平均的问题。为此,一个简单的方法是以二进制方式处理颜色。即黑色([0,0,0])或其他颜色(所有其他颜色)。这意味着我们可以将颜色变量初始化为 0,然后选择所有被分类为建筑的点,并赋予它们另一种颜色,例如红色([1,0,0]):

colors=np.zeros((len(pcd_df), 3))
colors[pcd_df['Classification'] == 6] = [1, 0, 0]
pcd_o3d.colors = o3d.utility.Vector3dVector(colors)

非常好!现在,由于我们对原始点云进行了操作,我们需要根据自己的选择重新定义 POI 和选择。为了提高效率,您会找到可以运行的代码块,以更新体素渲染到新的颜色方案:

# Defining the POI and the center of study
dist_POI=50
POI=mesh.get_center()

# Querying all the points that fall within based on a KD-Tree
pcd_tree = o3d.geometry.KDTreeFlann(pcd_o3d)
[k, idx, _] = pcd_tree.search_radius_vector_3d(POI, dist_POI)
pcd_selection=pcd_o3d.select_by_index(idx)

#Computing the voxel grid and visualizing the results
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd_selection, voxel_size=2)
o3d.visualization.draw_geometries([voxel_grid])

二进制着色的点云。© F. Poux

太棒了!现在,我们需要实际获取每个体素的离散整数索引,并将它们列出,还有体素的颜色和边界。这将允许我们之后遍历每个体素及其颜色,检查一个体素是否在“体素列”中是最高的。为此,一个有效的方法是使用列表推导式来向量化我们的计算,避免不必要的循环:

idx_voxels=[v.grid_index for v in voxel_grid.get_voxels()]
color_voxels=[v.color for v in voxel_grid.get_voxels()]
bounds_voxels=[np.min(idx_voxels, axis=0),np.max(idx_voxels, axis=0)]

太棒了!在 50 米的选择区域中,我们有一个 49 x 49 x 16 的体素网格,总共有 38,416 个填充的体素。现在是提取已建立的覆盖范围的时候了!

4.5. 提取已建立的覆盖范围

现在我们有了一个具有二进制颜色的体素化点云,我们专注于下图的第三阶段:

正如我们所见,选择仅顶层体素比看起来复杂一些!😁 但幸运的是,我设计了一个简单而强大的管道,可以做到这一点,见下文。

体素选择工作流以七个子步骤提取已建成的覆盖范围。© F. Poux。

让我们一步一步来。

(1) 首先,我们初始化两个字典来保存最大索引:

max_voxel={}
max_color={}

(2 到 5) 我们遍历所有填充的体素,以检查它们是否在体素列中最高或应被丢弃,这给出了以下 for 循环:

for idx, v in enumerate(idx_voxels):
    if (v[0],v[1]) in max_voxel.keys():
        if v[2]>max_voxel[(v[0],v[1])]:
            max_voxel[(v[0],v[1])]=v[2]
            max_color[(v[0],v[1])]=color_voxels[idx]
    else:
        max_voxel[(v[0],v[1])]=v[2]
        max_color[(v[0],v[1])]=color_voxels[idx] 

🦚 注意: 首先要注意的是循环定义中的 enumerate() 方法。这允许在循环 idx_voxels 变量的每个值时跟踪列表的索引。很方便!第二点是我们使用“元组”数据类型,如 (X, Y),这给出了体素的整数位置。这使我们能够确保我们持续检查相同的 X Y 网格位置。最后,“if”语句允许测试表达的条件,并在条件返回 True时执行。如果条件不为 True,则执行 else 语句(如果存在)或跳过并退出条件检查。

(6) 我们初始化标记为已建成或未建成的体素的计数,并检查保留的顶部体素的颜色是否为黑色,如果是,我们更新相应类别的体素计数:

count_building_coverage,count_non_building=0,0
for col in list(max_color.values()):
    if np.all(col==0):
        count_non_building+=1
    else:
        count_building_coverage+=1

🦚 注意: np.all() 也是一个布尔检查,只会在所有值都是 True 时返回 True。在我们的例子中,黑色是* [0,0,0],因此返回 True ,因为所有 R G* 和* B 都设置为 0。如果其中一个不是零,则意味着并非所有值都是* 0np.all() 将返回 False,这会触发* else 语句。简单吧? 😉

(7) 我们可以提取每种类型(已建成和未建成)的覆盖区域,记得将计数变量乘以实际的 2D 体素面积(在我们的例子中为 4 平方米),并计算比率:

print(f"Coverage of Buildings: {count_building_coverage*4} m²")   
print(f"Coverage of the Rest: {count_non_building*4} m²")
print(f"Built Ratio: {(count_building_coverage*4)/(count_building_coverage*2+count_non_building*2)} m²") 

这样,对于 50 米的范围,我们的覆盖率为 17.3%,相当于 1352 平方米的已建成区域和 6456 平方米的其余区域。(350 米查询的覆盖率为 19.2%,73416 平方米和 308280 平方米)。

因此,与其余部分相比,我们在选定的兴趣点上有一个很好的建筑占用平衡!

希望测试我们的 3D Python 工作流并没有让你感到太紧张!随时返回查看代码和工作流片段,掌握隐藏的代码技巧,以便(1)出色地应对挑战和(2)优化我们实现的效率。

然而,仍然有一些调整空间,例如使用分类信息结合高低 POI 来隔离水流的地面点,或使用地块表面选择作为过滤技术来提取已建成的覆盖范围。

🔮 结论

恭喜!这在使用 LiDAR 进行城市建模的 3D Python 工作流程领域是一个真正的巨大第一步!我们下面构建的流程非常通用,可以作为你未来分析工作的参考点,这些工作可能会遵循类似的模式。

在 LiDAR 城市模型背景下的 3D Python 工作流程。我们从环境设置(步骤 1)和 3D 数据准备(步骤 2)开始。一旦完成这些步骤,我们将进入 Python 自动化(步骤 3),其中一个特定部分涉及 3D Python 挑战(步骤 4),如地块表面或兴趣点查询。© 作者

总结一下你解锁的新技能,你现在可以高效地应对以下挑战:

  1. 将开源软件与 Python 结合成一个连贯的工作流程;

  2. 收集开放数据集并在处理之前准备好它们;

  3. 使用 3D Python 处理 3D 数据模式,特别是 3D 点云、3D 网格和 3D 体素;

  4. 使用每种 3D 模式的关键方面进行高级地理空间分析,同时优化代码;

  5. 作为城市规划师从分类点云中提取关键洞察,以更好地理解本地区域。

如果你感到自己能够掌握并处理这些不同的方面,那么你正走在成为伟大的 3D 地理空间专业人士的道路上!唯一需要做的就是保持努力,推动现有的边界。

🤿 更进一步

但学习旅程并未就此结束。我们的终身探索才刚刚开始,未来的步骤将深入研究 3D 体素工作,探索语义、CityGML、CityJSON,特别是如何从聪明的城市走向智慧城市。此外,我们还将利用深度学习技术分析点云,并解锁高级 3D LiDAR 分析工作流程。有很多令人兴奋的内容!

使用 Python 进行 3D 地理空间数据集成:终极指南

原文:towardsdatascience.com/3d-spatial-data-integration-with-python-7ef8ef14589a

3D Python

用多模式 Python 工作流整合地理空间数据的教程:结合 3D 点云、CityGML、体素、矢量 + 栅格数据

Florent Poux, Ph.D.Towards Data Science Florent Poux, Ph.D.

·发表于 Towards Data Science ·阅读时间 39 分钟·2023 年 11 月 7 日

--

现在的科技进步速度简直疯狂。尤其是当我们看到 3D 数据对地理空间分析和数字双胞胎的重要性时更是如此。能够以三维捕捉和分析数据意味着我们可以创建对现实世界对象和环境的精确表示。

3D 空间数据集成通过理解 3D 数据捕捉的范围来实现。© F. Poux

🦄米拉一图胜千言。那么数字双胞胎呢?

这对城市规划、基础设施管理和灾难响应等领域尤为重要。

通过整合 3D 数据,我们可以通过依赖于精确和可靠的数据表示来提高做出明智决策的能力。此外,将这些数据整合到数字双胞胎中可以生成非常逼真的现实资产和系统的复制品,从而提高模拟和分析的效率。

但是(总是有“但是”),有效的地理空间分析和数字双胞胎创建依赖于高效整合和可视化不同的数据格式。为实现这一点,必须全面了解各种数据模式及其如何无缝集成和可视化。在数据方面,我们希望创建一个统一且全面的区域表示,以便数据能够重叠。我们真是太幸运了,因为这正是我们今天要解锁的内容!

要构建一个空间数字世界,我们必须研究 3D 数据集成。许多信息来源,如矢量数据、栅格数据、3D 点云或 3D 城市模型,可以组合形成我们星球上发生事件的统一视图。© F. Poux

在这本动手指南中,我提供了一个面向系统的 3D 数据集成工作流参考,使用 Python。因此,你不需要昂贵的软件或大规模的砖块式流水线!只需我们的 Python 朋友和精心挑选的一小组强大模块与函数。

这个倡议的终极目标是,你将拥有一个全面的指南和伴侣,陪伴你完成 3D 数据之旅!工作流被结构化为七个不同的阶段,如下所示。

使用 Python 的 3D 数据集成工作流。这是一个七步过程,用于生成统一的数据中心视图。© F. Poux

每个阶段逐步构建,以确保你可以从头开始或模块化地插入到现有系统中。由于构建得非常全面,目录将使你更容易浏览!

Chapter 1\. 3D Python Setup
1.1\. Environment Setup
1.2\. Base Libraries
1.3\. 3D Data Libraries
1.4\. Geospatial Libraries
1.5\. IDE Setup

Chapter 2\. Multi-Modal Data Curation
2.1\. 3D Data Sourcing
2.2\. Spatial Raster (GIS)
2.3\. Vector Data (GIS)
2.4\. Other Sources

Chapter 3\. Data Analysis and Profiling
3.1\. 3D Point Clouds and voxels
3.2\. 3D mesh and city models
3.3\. Spatial / Raster Imagery
3.4\. DSM, DTM, CHM

Chapter 4\. Registration / Reprojection
4.1\. Selecting a Reference System
4.2\. Data Georeferencing
4.3\. Data Reprojection
4.4\. Rigid Registration

Chapter 5\. Data Pre-Processing
5.1\. Data Cleaning
5.2\. Data Transformation
5.3\. Data Reduction
5.4\. Data Enrichment (Fusion)

Chapter 6\. Data Visualization and Validation
6.1\. 3D Data Inspection
6.2\. Point Cloud Canonical Link
6.3\. Hybrid Multi-Modal Visualization
6.4\. Projection-based Inspection

Chapter 7\. Data Sharing
7.1\. Selection Method Definition
7.2\. Data Organization
7.3\. File Format Definition
7.4 Export and External Use 

只要你准备好了,就让我们一起跳入这次精彩的 3D 数据集成探险吧,身边有杯咖啡,但不要离电脑太近 ☕(这是我亲身经历的惨痛教训)

🎵读者注意:这本动手指南是* UTWENTE 与合著者 🦊 F. Poux, 🦄* M. Koeva,* 和* 🦝 P. Nourian 的联合工作的一部分。我们感谢来自数字双胞胎 @ITC 项目的资助,由特温特大学 ITC 学院提供。所有图片均 © F. Poux

第一步:3D 数据的实施设置

第一步:3D 数据的实施设置。© F. Poux

第一个任务是快速建立一个轻量级环境,以开发我们的 3D 数据集成工作流。这是一个简单的阶段,但确保设置正确是可扩展性和复制的关键。让我们把事情做好吧!

环境设置包括基础库、3D 数据库、地理空间库和 IDE。所有这些都建立在虚拟环境管理和 Python 之上。© F. Poux

轻量级环境设置

使用 Anaconda 进行 Python 环境设置,强大的库和集成开发环境(IDE)不必痛苦。Anaconda 提供了管理 Python 包和环境的便捷方法,然后你可以使用强大的 IDE,如 Jupyter Lab 或 Spyder,让编程变得轻松。关于设置 3D Python 开发环境的详细信息,我建议你查看以下文章:

[## LiDAR 城市模型的 3D Python 工作流:逐步指南]

解锁 3D 城市建模应用程序的流畅工作流的终极指南。教程涵盖了 Python……

towardsdatascience.com

🦊 Florent如果你不想再参加另一个会话,不用担心,我不会抛下你!作为这个终极指南的一部分,这里有一个绝佳的轻量级设置,启动时间少于 5 分钟,计时 ⌚。

首先,你可以访问 Anaconda 网站 并下载适合你操作系统(Windows、macOS 或 Linux)的 Miniconda 安装程序(一个免费的 conda 最小安装程序),并选择 Python 10 版本。然后,你可以按照 Anaconda 网站上的安装说明在你的计算机上安装 Miniconda。

就这样!你现在已经用轻量级的 miniconda 安装了最简单的 Python,这将使你非常容易隔离一个受控的虚拟环境。在继续下一步之前,我们启动 miniconda 并访问其命令行:

在 Windows 中,搜索“miniconda”应该会得到这个结果。

一旦进入 Anaconda Prompt,我们按照下面所示的简单四步骤过程进行操作。

环境创建工作流程。© F. Poux

  1. 要创建一个新环境,我们写:conda create -n GEOTUTO python=3.10

  2. 要切换到新创建的环境,我们写:conda activate GEOTUTO

  3. 要检查 Python 版本,使用 python --version,以及已安装的包:conda list。这应该分别显示 Python 3.10 和基本库的列表。

  4. 要在新的环境中安装 pip,我们写:conda install pip

就这样!我们现在准备好使用 pip 管理器安装 3D 数据集成所需的库:pip install package-name,其中你需要逐一更换包名(例如:numpymatplotliblaspy[lazrs,laszip]open3drasteriogeopandas)。

Python 基础库

Python 基础库:Numpy 和 Matplotlib。

第一个包的安装通过提示完成:pip install numpy。不需要介绍NumPy,这是 Python 的基础数值和科学计算库。它支持大型多维矩阵,并提供了一系列数学函数,方便使用。NumPy 是许多其他科学库的基础,在数据分析、机器学习和科学研究中被广泛使用。

🦝 NourianNumPy 全关于线性代数。如果你需要重新学习线性代数并熟悉它,你可以从这里开始: 计算机图形学的线性代数基础 (ResearchGate)

Matplotlib 是一个流行的 Python 绘图库,支持 2D 绘图和基本的 3D 绘图功能。安装方法为:pip install matplotlib。它提供了广泛的可定制可视化选项,允许用户创建各种类型的图表,如折线图、散点图、条形图、直方图、3D 图表等。Matplotlib 在科学研究和数据科学工作流中被广泛使用。

3D Python 库

Python 3D 库:Open3D 和 Laspy。

Open3D 是一个现代的 3D 数据处理和可视化库,主要关注 3D 点云和网格。它提供了处理 3D 数据的功能,如点云配准、几何处理、网格创建和可视化。Open3D 特别适用于与 3D 计算机视觉、机器人技术和增强现实应用相关的任务。安装 Open3D 的方法是:pip install open3d

然后,我们需要安装 Laspypip install laspy[lazrs,laszip]。这个 Python 库用于读取、写入和修改存储在 LAS(激光雷达数据交换格式)和 LAZ(压缩 LAS)文件格式中的 LiDAR 数据。它提供了处理来自 LiDAR 扫描仪的点云数据的工具,并在地理空间应用中(如地形建模、林业、城市规划等)被广泛使用。

地理空间库

Geopandas 是一个建立在 pandas 和 shapely 之上的库,旨在高效处理地理空间数据。安装方法为:pip install geopandas。它扩展了 pandas 的功能,支持地理空间数据类型和操作,使用户能够处理矢量数据(点、线、面)并高效地进行地理空间分析。Geopandas 在 GIS、制图和空间数据分析中被广泛使用。

Rasterio 是我们将要安装的最后一个库,安装方法为:pip install rasterio。它用于读取和写入地理空间栅格数据。支持各种标准的栅格格式,如 GeoTIFF 或 JPEG,并提供地理空间元数据、空间参考和坐标转换功能。rasterio 对于卫星影像分析、遥感和 GIS(地理信息系统)应用非常有价值。

这些库各自具有协同作用,可以处理科学计算、数据科学和地理空间应用中的各种数据类型。

设置 IDE

我们设置的最后一步是安装 IDE。我们仍在环境中的命令行界面中,输入:pip install jupyterlab,这将会在我们的环境中安装 jupyterlab。为了清晰使用,我们可以将目录更改为项目的父目录(我们称之为 INTEGRATION),其中包含 CODE 文件夹和 DATA 文件夹:cd C://COURSES/POUX/INTEGRATION。然后,我们将从这个位置启动 jupyterlab,通过在控制台输入:jupyter lab,这将会在您的网页浏览器中打开一个新的本地页面(Chrome 是首选,但 Firefox 或 Safari 也可以)。

做得好!第一阶段成功完成!🎯我们现在准备进入第二阶段:寻找可以组合以供未来使用的数据集,用于我们的 NASA 评分工作流程。🙃

步骤 2. 多模态数据策展

步骤 2. 多模态创建。© F. Poux

我们希望整合多模态数据集。但“多模态”这个词是什么意思呢?它指的是跨不同类型和背景的数据,例如图像、点云、文本和声音……

我们使用各种 2D/2.5D/3D 模态。3D 点云、3D 网格、城市模型、体素、空间栅格、360° 图像、表格数据、矢量数据。© F. Poux

🦝 Nourian此外,这里有一个关于城市规划的基于网络计算平台的极佳指南: 必备指南 (ResearchGate)

因此,我们在此阶段的目标是识别一个感兴趣的区域,并收集尽可能多的数据,以帮助我们未来的分析。今天选择的感兴趣区域是荷兰东部 Overijssel 省(Twente 地区)Enschede 市的一部分,这里,大学的知识光芒照耀四方。🌞

确定感兴趣的区域。

🦄 Mila通过融合 3D 地理空间和遥感数据来解锁我们城市的秘密,我们诞生了城市数字双胞胎,这些双胞胎照亮了过去,导航当前,并塑造未来。如果你想更深入地了解如何使用开源工具在网络上进行数据整合,这里有一篇研究论文: 3D 数据集成用于基于 Web 的开源 WebGL 互动可视化 (ISPRS Archives)

现在我们准备获取不同的数据集。为了清晰起见,我将这些数据源分为四类:3D 数据集、空间栅格、矢量数据集,以及其他来源,如下所示。

多模态数据策展工作流程。© F. Poux

3D 数据源

第一步是从一些开放数据仓库获取数据集,这些仓库的数据许可允许我们进行一些实验。在这方面,对于荷兰,可以从一个地方获取 LiDAR 数据、地形模型和栅格图像:geotiles.nl。你可以放大原始图块,并访问各种数据集的下载链接,如下所示。

通过 GeoTiles.nl 提取 AHN4 数据集的 34FN2 图块。© Florent Poux

🦊 Florent: 为了更好地服务于你,所有使用的数据都可以在本节末尾共享的驱动器文件夹中找到。AHN 版本是 AHN4。

你可以探索的第二个获取 CityModels 的地方是 3D BAG,即建筑和地址的 3D 注册(BAG),这是荷兰最详细、开放可用的建筑和地址数据集。它包含多个详细级别的 3D 模型,生成方式是将两个开放数据集结合起来:来自 BAG 的建筑数据和来自 AHN 的高度数据。使用 3D Bag Viewer 通过图块查询,你可以探索和访问建筑物。

BAG Viewer。开放数据的许可为 CC BY 4.0,3DBAG by tudelft3d 和 3DGI

图块范围与我们从 geotiles.nl 获得的不同,这在我们进行集成阶段时显得有趣。在这个阶段,我们已经手头有了令人兴奋的数据集。现在我们可以继续探索空间栅格数据集。

空间栅格数据源

对于卫星和航空图像,USGS Earth Explorer 是最大的免费数据源之一。它是全球性的,具有友好的用户界面,使访问遥感数据变得简单。如果你需要下载多个数据集,它甚至有批量下载应用。如果这是你第一次使用,你需要执行一个额外的步骤:创建一个账户,但这是免费的且迅速的(我用不到 2 分钟 ⌚)。

USGS Earth Explorer。开放数据属于美国公共领域,Credits USGS

我从 WebUI 绘制了一个多边形,然后请求获取 Landsat > Landsat Collection 2 — Level 1 组(最新的 Landsat 图像是 L8–9 OLI/TIRS 和 L7 ETM+)。这些集合之间的差异基于数据质量和处理水平。USGS 已根据质量和处理水平 将图像分类为不同级别 (来源)。一旦进入结果部分,你可以查看足迹,然后决定哪个最适合你的需求,如下所示。

提取 LandSAT 图像的多边形。© F. Poux

对于数字高程模型,我建议使用 geotiles.nl 数据服务,因为它提供了 AHN 的最新和最精确的高程模型。我们正在以令人难以置信的速度前进,下一阶段是获取一些优秀的矢量数据集!

矢量数据策展

如果你在 GIS 社区中,我希望展示 OpenStreetMap (OSM) 的力量不会冒犯你的知识基础。

OpenStreetMap 数据策展门户。数据是开放的,遵循开放数据库许可证(ODbL),OpenStreetMap 版权

OSM 提供了不同的地图和图层,具有众包倡议,这使得它非常详尽,但精确度仍然是一个问题。实际上,OSM 对公众开放,由普通大众创建。这意味着准确性可能会根据创作者及其“制图”技能水平而有所不同。然而,OSM 是一个开放许可证街道级 GIS 数据的宝藏。

我们有几种方法可以下载 OpenStreetMap 数据,以获取一些宝贵的数据。方便的是,甚至有一个 OSM 数据维基页面 列出了所有可用的 OSM 提取数据。我的推荐是使用 Geofabrik 工具。实际上,你可以利用按语义空间范围(例如:国家、州、洲等)组织的数据。你可以快速选择一个地理位置,然后下载 OSM 数据,如下所示。

GeoFabrik 门户以收集矢量数据集。© F. Poux

🦊 Florent我也喜欢将 OSM 数据下载为形状文件,但稍后会详细说明 😉。 Mila 让我觉得这其中浓缩了大量的知识 Geospatial Data with Python

现在我们有一些 3D 数据集、一些栅格数据集和矢量形状文件。是时候在互联网世界中挖掘其他宝贵的资源了 💎。

其他来源

好吧,在这里,网络是您的盟友。您可以找到任何与分析相关的信息,从网页到新闻,再到声音和实时数据流。我不会夸大地说天空(或您的带宽 😁)是极限!不过,让我再实际一点,向您推荐一个平台:Mappillary

Mappillary 是一个提供街景图像和地图数据的平台。您可以使用地图浏览器探索并下载一些 360° 图像和兴趣点。

Mapillary 数据库通过提供的门户。如果您下载图像,它们由 Mappillary 按 CC-BY-SA 许可。

如果您想仅选择一些感兴趣的元素或与 OSM 数据进行交叉验证,您还可以按类别过滤元素。

现在,好消息来了!为了跟随即将到来的代码行,我为您简化了获取这些数据的过程,您可以直接在这个 数据驱动仓库 中找到它们。一旦您在您的 DATA 文件夹中拥有所需的数据,我们可以开始一次愉快的探索性数据分析。

第 3 步:探索性数据分析

第 3 步:探索性数据分析。

在这一阶段,我们已经设置好了 Python 代码和数据,因此我们准备好进入深度集中模式,定时器设置为 15 分钟 ⌚。实际上,我们将使用 Python 探索我们收集的各种数据集。

加载和读取 3D 数据。我们使用 Open3D(点云、网格、体素)、RasterIO 和 Geopandas。© F. Poux

这意味着,对于每种模式,我们将进行读取、分析、协调和分类其内容。通常,这意味着处理库的双向通信。为了保持简洁,我擅自将一些模式归为一组,如下所示。

涵盖的多种模式。© F. Poux

在加载任何内容之前,让我们通过在您的笔记本/脚本中输入以下九行来导入所有已安装的库:

#base libraries
import numpy as np
import matplotlib.pyplot as plt

#3D libraries
import open3d as o3d
import laspy as lp

#geospatial libraries
import rasterio as ra
import geopandas as gpd

3D 点云

让我们从我的门生开始:3D 点云。它们本质上是一组在 3D 空间中表示物理对象或环境的点。这些点来自各种来源,包括 LiDAR、摄影测量、人工智能(是的,你没看错)、扫描设备等。我们手中的第一个数据集来自 Aerial LiDAR AHN 活动,格式为 LAZ 文件。它从 LAS LiDAR 数据交换格式(LAS)扩展而来,是存储 LiDAR 点云数据的压缩对等格式。它支持 2D 和 3D 点数据及其属性,如强度和分类。为了用 Python 读取该文件,我们使用laspy库及其扩展,如下所示:

#Load a las file
las = lp.read("../DATA/34FN2_13.laz")

太好了!我们现在已经将文件加载到las变量中,可以使用laspy函数快速探索,获取可能的属性、red通道的max value或投影信息:

print([dimension.name for dimension in las.point_format.dimensions])
print(np.max(las.red))
print(las.header.vlrs[2].string)

这将产生以下结果:

['X', 'Y', 'Z', 'intensity', ‘return_number’, ‘number_of_returns’, ‘synthetic’, ‘key_point’, ‘withheld’, ‘overlap’, ‘scanner_channel’, ‘scan_direction_flag’, ‘edge_of_flight_line’, ‘classification’, ‘user_data’, ‘scan_angle’, ‘point_source_id’, ‘gps_time’, ‘red’, ‘green’, ‘blue’, ‘nir’, ‘Amplitude’, ‘Reflectance’, ‘Deviation’]
255
COMPD_CS["Amersfoort / RD New + NAP height",PROJCS[“Amersfoort / RD New”,GEOGCS[“Amersfoort”,DATUM[“Amersfoort”,SPHEROID[“Bessel 1841”,6377397.155,299.1528128,AUTHORITY[“EPSG”,”7004"]],AUTHORITY[“EPSG”,”6289"]],PRIMEM[“Greenwich”,0,AUTHORITY[“EPSG”,”8901"]],UNIT[“degree”,0.0174532925199433,AUTHORITY[“EPSG”,”9122"]],AUTHORITY[“EPSG”,”4289"]],PROJECTION[“Oblique_Stereographic”],PARAMETER[“latitude_of_origin”,52.1561605555556],PARAMETER[“central_meridian”,5.38763888888889],PARAMETER[“scale_factor”,0.9999079],PARAMETER[“false_easting”,155000],PARAMETER[“false_northing”,463000],UNIT[“metre”,1,AUTHORITY[“EPSG”,”9001"]],AXIS[“Easting”,EAST],AXIS[“Northing”,NORTH],AUTHORITY[“EPSG”,”28992"]],VERT_CS[“NAP height”,VERT_DATUM[“Normaal Amsterdams Peil”,2005,AUTHORITY[“EPSG”,”5109"]],UNIT[“metre”,1,AUTHORITY[“EPSG”,”9001"]],AXIS[“Gravity-related height”,UP],AUTHORITY[“EPSG”,”5709"]],AUTHORITY[“EPSG”,”7415"]]

第三行给我们一个有趣的概况:数据以Amersfoort / RD New + NAP height参考系统表示。我们记下来以备后用。

🦊 Florent:如你所见,第一行给了我们几个后续使用的属性。很高兴默认情况下我们可以以半结构化的方式存储这么多信息。然而,我们需要整理这些信息的相关性;通常我们默认使用*X**Y**Z**intensity**red**green**blue*。我会把其他的称为额外的奖励😁。

在这个阶段,我们没有自然的方法来可视化数据是否正确;我们必须将其转换为我们喜爱的numpy对象,然后再转换为open3d点云对象,以完成库之间的切换。首先,我们将一些laspy对象属性转换为coordscolors numpy 对象,以保存我们的点云坐标XYZ以及点云颜色:

coords = np.vstack((las.x, las.y, las.z))
colors = np.vstack((las.red, las.green, las.blue))

然后我们将 numpy 对象转换为open3d对象以可视化点云。不幸的是,从laspyopen3d没有直接的方法,尽管有😁。

pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(coords.transpose())
pcd.colors = o3d.utility.Vector3dVector(colors.transpose()/255)
o3d.visualization.draw_geometries([pcd])

🦊 Florent:这是一个很好的练习,可以看到切换库几乎总是会导致在转换之间丢失一些处理时间,并且也暴露出可能的内存错误。因此,当处理复杂的数据集和工作流程时,我建议总是使用有限数量的库。*

在 Open3D 中可视化的 3D 点云数据集。© F. Poux

现在你可以在 Python 中很漂亮地可视化完整的 LiDAR 点云,媲美专业软件!让我们加载另一个点云,以探索另一种广泛使用的文件格式:.ply。PLY 格式用于存储 3D 网格数据及其属性,如颜色和法线。它也适用于带有附加信息的点云。

使用open3d,这非常简单;你可以执行以下代码,将点云填充到pcd_itc变量中,并使用o3d.visualization函数绘制点云:

#Load a point cloud and visualize
pcd_itc = o3d.io.read_point_cloud("../DATA/ITC_outdoor.ply")
o3d.visualization.draw_geometries([pcd_itc])

在 Open3D 中可视化的 3D 室内点云。© F. Poux

这个点云没有显示任何元数据,这意味着我们将认为它以本地参考系统表示,需要以某种方式与参考数据集进行配准。

🦊Florent: 剧透警告!点云数据集就像一个特殊的中心岩石,尤其在处理多模态数据时。实际上,正如我稍后将展示的,它可以作为标准参考,将所有其他数据集与之关联。我已经提醒过你有剧透了!

是时候转向 3D 体素🧊

3D 体素

体素是另一种 3D 数据格式。体素本质上是表示空间体积的 3D 像素。它们非常有趣,你可以在以下案例研究中查看它们的实际用途:

## 3D Python 工作流用于 LiDAR 城市模型:一步一步的指南

解锁 3D 城市建模应用的最终指南。该教程涵盖了 Python……

towardsdatascience.com ## 如何使用 Python 自动化 3D 点云的体素建模

实用教程,将大型点云转换为 3D 体素🧊,使用 Python 和 open3d。解锁自动化工作流……

towardsdatascience.com

🦝 Pirouz: 体素非常像乐高模型。查看这篇关于空间数据体素化并深入研究数字乐高的论文: 地理空间应用的体素化算法 (ResearchGate)。如果你对体素情有独钟?那么在 Google 上搜索“体素艺术”,并查看库 topoGenesis (GitHub)。

现在,我们将使用以下代码行读取和可视化体素数据集:

vox_read = o3d.io.read_voxel_grid("../DATA/34FN2_13_vox.ply", format='auto')
o3d.visualization.draw_geometries([vox_read])

在 Open3D 中可视化的 3D 体素数据集。© F. Poux

在这个阶段,我们阅读、分析,并确保有一个单一的库来处理所有这些数据(numpy)和一个用于 3D 可视化的库(open3d)。

是时候在我们的 Python 实验中加入城市模型了😁

城市模型

城市模型通常遵循一种表达标准:CityGML 模型。CityGML 是一种基于 XML 的数据格式,用于表示城市环境的三维几何。CityGML 模型可以包含有关建筑物、道路、桥梁和其他基础设施的信息。城市规划师、建筑师和工程师常常使用这些模型来模拟和分析城市环境。一个优秀的获取地点,我之前故意没有提到的,是由我的同事 Olaf 精心策划的 GitHub 仓库(任何冰雪奇缘的引用禁止 ⛄):CityGML Models

🦊 Florent: 基本上,在 Python 中,我们必须确保将这些模型转换为三维网格,同时保留所需的语义/拓扑或其他信息。一个友好的 Python 工具,同样由 TUM* 提供,可以在这里找到:* citygml2obj。但如果你使用数据,我已经为你完成了繁重的工作,因此继续阅读下一个阅读方式:三维网格。

3D 网格

接下来,让我们谈谈网格。三角网格由顶点、连接顶点的边缘和完成对象“包络”的三角面组成。它们使我们能够描绘三维物体的形状。网格可以使用三维建模软件创建,也可以通过点云、城市模型和专用算法生成,甚至是人工智能!数据集采用 OBJ 格式,通常用于导出表示几何体和纹理坐标的三维网格模型。

🦝 Pirouz: 如果你想知道为什么这些花哨的词汇被用来代替点、线和多边形,你需要多了解一点另一个花哨的词汇:拓扑*

🦊 Florent: 你可以阅读 Pirouz 写的关于拓扑的故事,这可能是最简单的拓扑故事之一: 几何和拓扑基础 (ResearchGate)

要加载网格并获取其范围,我们将编写以下几行:

mesh = o3d.io.read_triangle_mesh("../DATA/10–976–660-LoD22–3D.obj")
mesh.get_axis_aligned_bounding_box()

从输出中我们看到坐标接近于 Amersfoort / RD New 所提供的。这令人欣慰;我们现在在这个参考系统中有了另一个数据集。

从那里开始,为了可视化网格,我们首先计算一些顶点法线,并使用 open3d 的相同可视化函数来进行可视化:

mesh.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh])

在三维方面,我们可以开始了!让我们用 Python 和 rasterio 探索我们的空间栅格。

空间影像

我们的影像采用 GeoTIFF 格式,这是 TIFF 格式的扩展,包含地理空间元数据,适用于栅格高程数据(DSM/DTM)和影像。要打开和分析我们拥有的空间影像,我们只需使用 rasterio 的以下代码行:

sat_image = ra.open("../DATA/RGB_34FN2.tiff")
sat_image_array = sat_image.read(1)
print(sat_image.meta)

这将产生以下输出:

{‘driver’: ‘GTiff’, ‘dtype’: ‘uint8’, ‘nodata’: None, ‘width’: 20002, ‘height’: 25002, ‘count’: 3, ‘crs’: CRS.from_epsg(28992), ‘transform’: Affine(0.25, 0.0, 254999.75,
 0.0, -0.25, 475000.25)}

有趣的是,我们可以清楚地看到这是一个20002x25002的图像,具有三个通道(RGB),其表达在CRS 28992中,这再次对应于 Amersfoort!这太棒了!

现在,为了使用 numpy 和 matplotlib 绘图,下面是获取图像的最小代码行,以避免内存错误,结果如图所示:

plt.imshow(sat_image_array)
plt.axis("off")
plt.show()

与此同时,rasterio 还提供了[show()](https://rasterio.readthedocs.io/en/latest/api/rasterio.plot.html#rasterio.plot.show)函数,以执行日常任务,例如将多波段图像显示为 RGB,并用适当的地理参考范围标记坐标轴。这使得绘图变得更加简单,如下所示:

from rasterio.plot import show
cir_image = ra.open("../DATA/CIR_34FN2.tiff")
show(cir_image)
show(sat_image)

使用 matplotlib 和 rasterio 的绘图函数可视化的各种光栅。© F. Poux

这真是太棒了!我们有多个 3D 数据集(3D 点云、体素、3D 网格)和卫星图像(包括红外和 R、G、B),我们可以用 numpy 处理这些数据,并且大多数可以用一个通用 CRS 表示。让我们转向其他光栅,重点关注高程数据(2.5D)。

高程光栅(DSM,DTM)

高程模型通常以光栅文件的形式出现,其中每个像素都有一个可以转换为其高程的值。这就是为什么我们称之为 2.5D 数据,因为它是一个纯粹的俯视图或单点视角,当它不在任何 GIS 投影系统中时也称为深度图像。我们可以用相同的代码行导入高程模型:

dtm = ra.open("../DATA/DTM5_34FN2.TIF")

现在我们有了一个保存了高程数据的dtm变量,我们可以通过dtm.meta提取其整体元数据,或者更专业地使用dtm.shapedtm.crsdtm.boundsdtm.overviews(1)

print(dtm.meta)
print(dtm.shape, dtm.crs, dtm.bounds, dtm.overviews(1))

这给我们带来了以下结果:

{‘driver’: ‘GTiff’, ‘dtype’: ‘float32’, ‘nodata’: 3.4028234663852886e+38, ‘width’: 1000, ‘height’: 1250, ‘count’: 1, ‘crs’: CRS.from_epsg(28992), ‘transform’: Affine(5.0, 0.0, 255000.0,
 0.0, -5.0, 475000.0)}
(1250, 1000) EPSG:28992 BoundingBox(left=255000.0, bottom=468750.0, right=260000.0, top=475000.0)

再次令人感兴趣的是,我们看到这个数据集使用的是相同的 CRS Amersfoort / RD New。

为了绘制图形并了解我们正在处理的内容,我们可以将数据集转换为 numpy 数组,并使用索引选择其中的一部分到选择变量中:

dtm_array = dtm.read(1).astype(‘float64’)
selection = dtm_array[300:800,300:800]

最后,我们使用两种方式绘图:rasteriomatplotlib,分别绘制全尺度数据集和放大的选择区域:

#For the dtm plot with rasterio
show(dtm)

#For the dtm zoomed-in plot with matplotlib
fig, ax = plt.subplots(1, figsize=(15, 15))
ra.plot.show(selection, cmap=’Greys_r’, ax=ax)
plt.axis(‘off’)
plt.show()

使用 RasterIO 和 matplotlib 绘制的 DTM 图。© F. Poux

最后,我们不能在不深入探讨矢量空间数据的情况下讨论光栅数据集。

空间矢量数据

在这个阶段,必须一次性明确,如果你看到的是像素,那么你并不是在谈论矢量数据。相反,矢量数据集由顶点和路径组成,这些顶点和路径以点(X,Y 坐标)、线(连接这些标记的顶点)和多边形(连接顶点以闭合由线组成的路径)的形式存在。

GIS 系统中的矢量数据。© F. Poux

🌱 成长考虑到这一点,你的直觉把 3D 点云放在哪里?体素模型?3D 网格?这些都是有深远意义的有趣问题,但有助于更好地定位 2D 与 3D 的协同作用

有趣的是,制图师(不仅仅是制图师)将这些用作“符号”来在地图上描绘现实世界的组件。这意味着他们必须不断确定实体所代表的“详细程度” (LoD),这赋予这些实体如此多的符号意义(一个点可以是一个国家、一个城市、一个公民或一个特定地点,……)。

为了处理这些矢量数据集,我们主要使用一种开放规范——shapefile 格式。这允许我们空间描述几何形状并附加一些额外的信息。在我们的计算机上,shapefile 文件格式实际上是多个文件的集合,用于表示地理数据的不同方面(参考):

  • .shp — 形状文件,包含特征几何本身。

  • .shx — 形状索引格式,包含特征几何的定位索引。这对于大型文件非常有用,因为它利用巧妙的“索引”实现快速搜索。

  • .dbf — 属性格式,以表格方式(dBase IV 格式)存储每个形状的各种属性。

在顶部,我们可能会有与 shapefile 格式配套的可选文件。最值得注意的是“.prj”文件。这个额外的文件实际上定义了坐标系统和任何必要的投影信息。因此,拥有这些文件使我们能够使用 geopandas 加载 Overijssel 区域的街道数据:

vector_data = gpd.read_file("../DATA/gis_osm_roads_free_1.shp")

矢量数据输出。© F. Poux

现在让我们通过探索其坐标参考系统(CRS)来进一步了解其投影系统:

print(vector_data.crs)

这导致了以下结果:

<Geographic 2D CRS: EPSG:4326>
Name: WGS 84
Axis Info [ellipsoidal]:
- Lat[north]: Geodetic latitude (degree)
- Lon[east]: Geodetic longitude (degree)
Area of Use:
- name: World.
- bounds: (-180.0, -90.0, 180.0, 90.0)
Datum: World Geodetic System 1984 ensemble
- Ellipsoid: WGS 84
- Prime Meridian: Greenwich

如我们所见,该数据集以 ESPG 4326 表达,这代表 WGS84. 因此,与我们之前的数据集有所不同。

Florentgeopandas 是 pandas 和 shapely 的完美结合。因此,如果你对这两者都很熟悉,使用 geopandas 将会非常轻松!一些有用的命令是 vector_data.columns vector_data.describe() ,可以让你快速了解其内容

但现在让我们通过可视化我们的数据集来查看其内容:

vector_data.plot()

这导致了以下图示:

来自 OpenStreetMap 的矢量数据。© F. Poux

这非常有趣!我们还可以使用 Matplotlib 来显示矢量数据。该区域人口密集,道路网络广泛且非常密集!现在让我们从另一个数据源中提取一些兴趣点。

矢量:兴趣点

数据集包中包含一个 GeoJSON 文件,这是一个轻量级且流行的矢量数据格式,具有几何体和属性,适用于基于 Web 的应用程序和数据交换。要加载该文件,我们也使用 geopandas,代码如下:

trashcan_data = gpd.read_file("../DATA/mapillary-trashcan_points.json")

我们分析其 CRS:

trashcan_data.crs

这给我们的答案是它仍然在 WGS84 CRS 中。最后,我们绘制以检查任何异常:

trashcan_data.plot()

关注点。© F. Poux

没有更多上下文,这可能有点平淡;因此,首先选择叠加栅格影像。但在此之前,让我们探索另一个可以尝试的数据集:360° 影像!

360° 影像

从 mappillary 平台,我们可以提取一些附有特定许可的 360° 图像。你仍然可以使用 geopandas 来读取这些影像:

image = ra.open("../DATA/An8XJdYJkSXycbyDWiVyw8dfVi2IzZ-jr9z7IxneeEXnOJPt0K1C89cMdXNkl5FZT0x6aVuPFRpda6eWV9fcnpgJkPfWjrd7k9harUP48csXGFf2azE1qF7FhkYK4h__j9t6Vd3aUfKcEdlqF_rsVO4.jpg")
print(image.meta)

这将输出:

Dataset has no geotransform, gcps, or rpcs. The identity matrix will be returned.
  dataset = DatasetReader(path, driver=driver, sharing=sharing, **kwargs)
{'driver': 'JPEG', 'dtype': 'uint8', 'nodata': None, 'width': 2048, 'height': 1024, 'count': 3, 'crs': None, 'transform': Affine(1.0, 0.0, 0.0,
       0.0, 1.0, 0.0)}

在这里,我们可以看到(当然)这张图像没有地理变换数据。这是正常的:这不是空间栅格!然而,如果我们愿意,我们可以添加一个位置,以便数据点表示拍摄照片的位置,这暗示了通过数据整合来叠加数据模态。但在去到那里之前,让我们绘制图像:

ra.plot.show(image)

结果如下:

来自 Mapillary 的 360° 影像。© F. Poux

我们现在已经成功加载了 3D 点云、作为 3D 网格的 3D 城市模型、3D 体素数据集、空间栅格(卫星影像、航拍影像和高程栅格)、矢量数据集(线条和点)以及 360° 影像。第一个目标现在显然已经成功!但我们需要检查如何将这些数据整合在一起,首先需要检查的是它们是否都表示在相同的坐标参考系统(CRS)中。为此,我将我们的分析步骤的结果汇总在下面的表格中。

| Data | Lidar Point Cloud                | TLS Point Cloud | 3D City Mesh                     | Satellite Imagery   | Digital Terrain Model | Streets (Vector)         | Trashcans                      |
|------|----------------------------------|-----------------|----------------------------------|---------------------|-----------------------|--------------------------|--------------------------------|
| Name | 34FN2_13.laz                     | ITC_outdoor.ply | 10-976-660-LoD22-3D.obj          | RGB_34FN2.tiff      | DTM5_34FN2.TIF        | gis_osm_roads_free_1.shp | mapillary-trashcan_points.json |
| CRS  | Amersfoort / RD New + NAP height | None            | Amersfoort / RD New + NAP height | Amersfoort / RD New | Amersfoort / RD New   | WGS 84                   | WGS 84                         |
| CRSC | 7415                             |                 | 7415                             | 28992               | 28992                 | 4326                     | 4326                           |

我们看到混合了 EPSG:7415、28992、4326 或缺失 EPSG。下一阶段是澄清并为所有数据集使用统一的系统。

数据注册和重投影

步骤 4:注册和重投影

在整合之前,我们必须确保所有数据集都处于兼容的格式和坐标参考系统(CRS)中。如果不是这样,我们会执行数据重投影,将它们带入一个共同的 CRS。这通过四个主要阶段实现:(1)选择参考系统,(2)地理配准主要数据集,(3)重投影其他数据集,以及(4)严格对齐本地数据集,如下所示。

已覆盖 4 个步骤工作流。© F. Poux

让我们逐步了解这些阶段。

选择参考系统

好的,让我们深入探索参考系统的激动人心的世界吧!想象一下参考系统就像是引导你的数据穿越地球广阔景观的 GPS 坐标。参考系统定义了一组规则和参数,以一种对我们这些凡人有意义的方式来表示地球表面。这就像选择一种数据流利使用的秘密语言,让它找到自己在世界上的位置。那么,我们如何选择正确的参考系统呢?让我们务实一些,首先尝试勾画项目的范围和关注区域。

投影经典案例:一个标准线,一个标准圆锥和一个标准点。© F. Poux

不同的参考系统在全球不同区域表现更佳。例如,Amersfoort / RD New 对于荷兰的本地项目可能是一个绝佳的选择。值得信赖的 WGS84 或全球横轴墨卡托(UTM)可能是你全球项目的最佳伙伴。因此,我们使用 Amersfoort / RD New + NAP 高程(用于高程信息)作为我们的 CRS。

记住,在选择参考系统时,让你的项目性质和地区来指导你的决策。选择一个能与数据语言相通的系统,并为你的地理空间工作带来和谐。🗺️

数据地理参考

好的,我们定义了 CRS;这一步确保数据标记为 EPSG:7415 的 3D 数据和 EPSG:28992 的 2D 数据。在分析时,我们可以检查这些数据集是否已经一致:

  • LiDAR 3D 点云

  • 栅格影像(包括空间栅格和高程栅格)

然而,体素数据集和城市模型数据集中的网格看起来似乎在相同的 CRS 中,但没有元数据。因此,我们迅速叠加这些数据集以检查任何可能的不一致:

o3d.visualization.draw_geometries([pcd,vox_read,mesh])

结果如下:

点云、建筑物和体素数据集被合并在一起并可视化。© F. Poux

🦊弗洛朗一切都对齐得很好,如图所示。各种数据集被故意裁剪以显示它们之间的重叠。你还可以看到点云在表示建筑物的 3D 模型上有些许粗糙和“分辨率”差异。

剩余的数据集需要处理如下:

  • 表示为 WGS84 的矢量数据集(包括 OSM 和 Mappillary)

  • ITC 点云数据集没有地理参考。

让我们首先处理数据投影。

数据重投影

空间数据重投影是一个基本过程,涉及将空间数据从一个坐标参考系统转换到另一个系统,通常是为了匹配特定空间分析的投影和坐标单位。重投影确保具有不同投影的数据集可以准确地叠加、集成或分析。

该过程涉及将地理坐标(纬度和经度)从一个基准转换到另一个基准的数学计算,考虑参数如比例、旋转和畸变。空间数据重新投影对于实现数据一致性、促进不同数据集之间的互操作性,以及在各种制图系统和应用中进行准确的地理空间分析至关重要。

在参考系统之间重新投影空间数据时,必须仔细考虑几个重要因素,以确保结果的准确性和意义。以下是一些关键考虑因素:

  1. 坐标系统和投影:了解源系统和目标系统的坐标系统和投影。确保它们兼容;如果不兼容,选择合适的转换方法。

  2. 基准和椭球体:检查源系统和目标系统中使用的基准和椭球体。基准的差异可能会导致坐标的显著偏移。如有需要,应用基准转换以正确对齐数据。如果你感到有些困惑,这里有一个不错的基准和椭球体讲座 (tamia.edu)

  3. 准确性/精度:虽然这些指向两个不同的特征(这扩展了我们文章的范围),了解你特定分析所需的“水平”很重要。重新投影可能会引入一些错误,特别是在大规模转换中。确实,任何数据“错误”都有可能影响我们分析的结果。

  4. 畸变:不同的地图投影可能会引入形状、面积、距离或角度的畸变。注意这些畸变及其对数据解释的影响。

  5. 覆盖区域:某些投影适合特定区域,但可能不适合全球数据集。选择一种在整个覆盖区域内保持数据属性的投影。

  6. 元数据和文档:记录重新投影过程,并记录应用于数据的转换。妥善记录源坐标系统和目标投影以备将来参考。

通过仔细考虑这些因素,我们确保重新投影准确执行,得到的数据适用于我们的预期应用。必须注意在重新投影过程中可能出现的错误和伪影,并验证结果以保持数据的质量和可靠性。在我们的案例中,我们需要重新投影两个矢量数据集。为此,我们使用以下步骤:

trashcan_data_georeferenced = trashcan_data.to_crs(‘epsg:28992’)
trashcan_data_georeferenced.plot(color=’red’)
vector_data_georeferenced = vector_data.to_crs(‘epsg:28992’)
vector_data_georeferenced.plot(edgecolor=’green’)

这导致了以下结果:

生成的图表。© F. Poux

🦊 Florent: 3D 重投影也是可能的,但目前超出了本指南的范围。然而,您可以浏览课程库,其中会找到一些很好的代码和示例,用于在“Segment Anything”语境下进行 3D 重投影。

注意我们图表坐标轴上的变化!在这一阶段,我们似乎只有一个数据需要注册:ITC 的 3D 点云。

数据刚性注册

3D 数据注册经典工作流程。© F. Poux

在 3D 集成中,数据注册变得至关重要。点云注册方法通常由两个阶段组成:粗略对齐,将两个点云相对快速地定位得较近。然后是精细注册,如迭代最近点(ICP)或基于特征的注册,以更高的精度对齐多个点云。为了给您一个关于全局注册的提示,我们可以通过选择三对公共点的列表来对齐 ITC 点云,以获得初步估计,然后用 ICP 进行精细化。这允许我们获得叠加的点云。这些内容大大扩展了当前指南的范围,将在另一个会议中讨论。😉

我们现在拥有一个在一致的坐标参考系统(CRS)中对齐的数据集。下一阶段是看看我们是否能“提炼数据”,以便从中建立一个智能的分析工作流程。

第 5 步。数据处理、转换和融合

第 5 步。数据预处理

这一阶段也可以在注册和重投影部分之前完成。实际上,由于我们主要按数据集工作,这是一种解决方案,并可能影响(无论是好是坏)前面步骤的结果。

空间数据集成将不同类型的空间数据(2D、3D 或 2.5D)结合起来,以创建一个统一且全面的区域数据重叠表示。这一过程使我们能够利用每种数据类型的优点,从集成的数据集中获得更有价值的见解。以下是空间数据集成的一些处理策略的快速视图:

3D 数据预处理的主要领域:数据清理、数据转换、数据减少和数据丰富。© F. Poux

现在,让我们探讨一下这四个主要阶段的实际代码实现。

数据清理(栅格数据集)

第一阶段是仔细处理缺失或错误的数据点。可以使用插值或外推等数据清理技术来填补空白或替换错误值。在我们的案例中,我们可以从数字地形模型(DTM)中填补空白的栅格值。

我通过对每个空白区域的相邻像素进行插值,试图用以下代码片段为我们的数据集提供更好的结构:

#We import a specific function
from rasterio.fill import fillnodata

#We transform to a numpy array
dtm_array = dtm.read(1)

#We interpolate
interpolated_dtm = fillnodata(dtm_array, mask=dtm.read_masks(1), max_search_distance=100, smoothing_iterations=0)

#We plot the results
fig, ax = plt.subplots(1, figsize=(15, 15))
ra.plot.show(interpolated_dtm, cmap='Greys_r', ax=ax)
plt.axis('off')
plt.show()

🌱 Growing: 你会如何使用这些来提取你区域的等高线?

这会导致以下结果:

DTM 可视化结果。© F. Poux

数据转换(3D 点云)

除了文件格式之间的转换,数据转换还指的是确保结构和内容完整性的各种过程,以便于一个或多个处理步骤。例如,在许多特征工程任务中,这对于提升 3D 机器学习模型的性能至关重要。

一种关键的数据转换方法是缩放,确保数据集中的所有值都在特定范围内,例如 0 到 1。要对点云执行此操作,我们按以下步骤进行:

#We compute a MinMaxScaler bounds
coords_itc = np.array(pcd_itc.points)
min_itc = np.min(coords_itc, axis=0)
max_itc = np.max(coords_itc, axis=0)

#MinMaxScaling
coords_itc_mmscaling = (coords_itc - min_itc) / (max_itc - min_itc)

这将我们原始的点云转换为所有点的坐标在 0 和 1 之间。因此,如果我们要绘制它们沿三个轴的分布,我们会使用以下内容:

n_bins = 20
fig, ax = plt.subplots(1, 3, sharey=True, tight_layout=True)
ax[0].hist(coords_itc_mmscaling[:,0], bins=n_bins, color = "skyblue")
ax[1].hist(coords_itc_mmscaling[:,1], bins=n_bins, color = "salmon")
ax[2].hist(coords_itc_mmscaling[:,2], bins=n_bins,color = "purple")

获得:

3D 点云分布分析。© F. Poux

如你所见,我们的点云现在拥有 X、Y 和 Z 坐标,这些坐标的范围在 0 和 1 之间。

🍇 注意这在另一种口味上很有趣。实际上,我们会根据 Z 分布的模式来寻找线索,这样的 Z 驱动算法是有意义的,例如,用于区分屋顶和地面点

数据减少:裁剪掩码,点云采样

数据减少包括各种技术,通过这些技术数据被简化到最简单的形式,以便进行最佳的分析任务。

其中之一是数据裁剪:减少数据集的空间范围。一个明确的例子是在栅格数据集上执行,以限制内存占用,当我们想要关注更小的区域时加载一个巨大的区域。为此,使用 rasterio,我们首先需要创建一个 geopandas 边界框,作为过滤掩码:

from shapely.geometry import Polygon

BBlidar=[np.min(coords, axis=1),np.max(coords, axis=1)]

minx=BBlidar[0][0]
miny=BBlidar[0][1]
maxx=BBlidar[1][0]
maxy=BBlidar[1][1]
areabbox = gpd.GeoDataFrame({'geometry':Polygon([(minx,maxy),(maxx,maxy),(maxx,miny),(minx,miny),(minx,maxy)])},index=[0],crs="EPSG:28992")

然后,我们得到一个漂亮的区域框,将其用作原始文件(或任何栅格文件)的掩码,同时确保将原始文件的元数据复制到裁剪后的版本中:

from rasterio.mask import mask

in_raster = ra.open("../DATA/CIR_34FN2.tiff")

# Do the clip operation
out_raster, out_transform = mask(in_raster, areabbox.geometry, filled=False, crop=True)

# Copy the metadata from the source and update the new clipped layer 
out_meta=in_raster.meta.copy() 
out_meta.update({
    "driver":"GTiff",
    "height":out_raster.shape[1], # height starts with shape[1]
    "width":out_raster.shape[2], # width starts with shape[2]
    "transform":out_transform})

# Plot the CIR raster
ra.plot.show(out_raster)

这会导致以下结果:

裁剪 CIR 图像。© F. Poux

另一种明确的数据减少技术是数据采样。在我们的案例中,就是试图减少点云中的点数。这可以通过几种方法来实现(这些方法也超出了本文的范围),其中一种方法是使用预定义的体素数据结构:我们希望每个体素保留一个最佳候选点:

voxel_size = 1

pcd_downsampled = pcd.voxel_down_sample(voxel_size = voxel_size)
o3d.visualization.draw_geometries([pcd_downsampled])

这会导致以下下采样点云:

3D 点云下采样结果。© F. Poux

最后,我们可以通过各种方式丰富我们的数据集。

数据丰富:将 POI 近似值注入点云

根据应用需求,可以采用各种融合技术来合并数据集。对于 2D 和 2.5D 集成,可以使用基于栅格的方法,如加权平均或多数投票,来结合多个数据层。在 3D 集成中,使用点云融合技术,如合并、平均或体素化,来创建一个代表整个区域的单一合成 3D 点云。

🦊 Florent: 使用数据增强(融合)技术,我们将来自多个来源的信息结合起来(这些来源可以是基于网络的、来自数据采集任务的,或任何相关的数据收集方式)。这使得我们能够创建更全面、更准确的基础现象表示。这些技术包括栅格加权平均(涉及对来自多个栅格层的像素值进行加权平均,其中权重代表每个栅格层的重要性或可靠性)、多数投票(通过将多数类别分配给每个像素位置来结合分类栅格数据,对土地覆盖分类非常有帮助)。但这些内容留到以后再讲。 😉

扩展数据集的快速方法是计算额外的特征。例如,这可以通过从点云中提取从邻域计算出的法线来完成,这样可以提取最佳拟合平面,然后计算法线。我们使用以下代码自动完成此操作:

nn_distance = np.mean(pcd.compute_nearest_neighbor_distance())
print(nn_distance)

#setting the radius search to compute normals
radius_normals=nn_distance*4
pcd_downsampled.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=radius_normals, max_nn=16), fast_normal_computation=True)

# Visualizing the point cloud
pcd_downsampled.paint_uniform_color([0.6, 0.6, 0.6])
o3d.visualization.draw_geometries([pcd_downsampled])

如下所示,我们可以在互动窗口查看器中按下“n”键来可视化我们新的点云,无论是否显示法线:

从 3D 点云中计算和可视化法线。© F. Poux

步骤 6. 多模态数据可视化

步骤 6. 多模态数据可视化。

多模态数据可视化是一种强大的方法,可以无缝集成我们多样的地理空间数据,包括矢量数据、空间栅格影像、数字表面模型(DSM)、数字地形模型(DTM)、点云和城市模型。

用于 3D 数据可视化的库:Matplotlib、Open3D 和 RasterIO(2D/2.5D)。© F. Poux

数据类型的全面融合使我们能够对环境有更全面的理解,为更明智的决策和复杂的分析奠定基础。现在,让我们深入探讨一个 Python 解决方案,展示如何实现这一卓越的可视化效果!

诀窍在于了解我们每个库的优点和限制!事实上,我们将专注于 Open3D,以检查数据模态(网格、3D 点云和体素)之间的一致性,并使用点云数据集作为标准参考,然后将所有其他数据集链接到它

点云作为标准参考框架。© F. Poux

因此,第一步是分析 3D 模态:

o3d.visualization.draw_geometries([pcd,vox_read,mesh])

组合的 3D 数据集。© F. Poux

🦊 Florent虽然图像可能令人困惑,但请注意,当你在一张图中可视化这三者时,什么也没有出错。事实上,数据集的范围不同,使我们能够更好地在视觉上区分模态。

太好了!接下来,我们使用 Numpy 对点云数据集进行自上而下的视图,以检查 X-Y 一致性,例如,与矢量数据一起:

# Plot the Georeferenced Shapefile and Point Cloud together using matplotlib
vector_data_clipped_pc = vector_data_georeferenced.cx[257000:258000, 471200:472400]
fig, ax = plt.subplots(figsize=(15, 15))
vector_data_clipped_pc.plot(ax=ax, color='yellow', edgecolor='yellow')
# Plot the point cloud
ax.scatter(coords[0][::100], coords[1][::100], c=coords[2][::100])
# Customize the plot (add labels, titles, legends, etc. if needed)
ax.set_xlabel('X (meters)')
ax.set_ylabel('Y (meters)')
ax.set_title('Shapefile and Point Cloud Visualization')
# Show the plot
plt.show()

这明显地将矢量数据集叠加到我们的点云上,为使这两个数据集相互交流开辟了可能性:

集成矢量和栅格数据集。© F. Poux

然后,我们通过将矢量数据集(重投影)与空间影像连接,继续我们的链接:

#Clipping vector data
vector_data_clipped = vector_data_georeferenced.cx[255000:260000, 469000:475000]

#Plot the Raster Imagery with Vector Dataset
fig, ax = plt.subplots(figsize=(15, 15))
ra.plot.show(sat_image, ax=ax)
vector_data_clipped.plot(ax=ax, facecolor='none', edgecolor='green')

#Zooming-in
fig, ax = plt.subplots(figsize=(15, 15))
plt.axis([258000, 258500, 471000, 471500])
#The line above actually define the extent of the plot using the coordinates in the CRS.
ra.plot.show(sat_image, ax=ax)
vector_data_clipped.plot(ax=ax, facecolor='none', edgecolor='cyan')

这导致了可视化结果,需要稍微放大,如代码中所示,以更好地定位两个数据集的适配情况。

栅格和矢量数据集的集成结果,带有 LiDAR 重投影。© F. Poux

对 Mappillary 的位置重复相同的过程,使用如下代码。

#Overlaying Raster with Vector data
fig, ax = plt.subplots(figsize=(15, 15))
plt.axis([257600, 260000, 471000, 471500])
ra.plot.show(sat_image, ax=ax)
trashcan_data_georeferenced.plot(ax=ax, facecolor='none', edgecolor='green')

Mappillary 的兴趣点视图。© F. Poux

最终,我们希望使用 Numpy 将栅格、矢量和点云模态叠加在一个图中:

# Plot the Shapefile and Point Cloud together using matplotlib
vector_data_clipped_pc = vector_data_georeferenced.cx[257000:258000, 471200:472400]
fig, ax = plt.subplots(figsize=(15, 15))

# plot the sattelite imagery
ra.plot.show(sat_image, ax=ax)

# Plot the shapefile
vector_data_clipped_pc.plot(ax=ax, color='blue', edgecolor='blue')

# Plot the point cloud
ax.scatter(coords[0][::100], coords[1][::100], c=coords[2][::100])

# Customize the plot (add labels, titles, legends, etc. if needed)
ax.set_xlabel('X (meters)')
ax.set_ylabel('Y (meters)')
ax.set_title('Shapefile and Point Cloud Visualization')

# Show the plot
plt.show()

这段小代码(注意其中使用的快捷方式和聪明的技巧以缩短代码)使我们能够对我们各种模态的数据集成做出最终的说明。

使用 DTM 的 Shapefile 和点云可视化。© F. Poux

我们接近我们系统方法的结束!

通过仔细应用这些处理技术,空间数据集成使我们能够创建环境的整体表示,促进更好的决策和洞察力,适用于广泛的应用。不过,我们还必须考虑最后一步:分享新处理的数据。

第 7 步:数据共享

第 7 步:数据共享。© F. Poux

空间数据导出在地理空间数据工作流中至关重要,使我们能够无缝地共享、可视化和分析 2D 和 3D 数据模态。在这方面,我们可以从更高的角度,几乎是空中视角,以更好地定位数据共享的影响范围。我选择与学术界和发布新科学发现的证据相关联。确实,我下面所示的这个过程是一个道德且强健的研发周期的关键。正如你所看到的,标准格式的数据导出,我们在这里介绍,是在推动自动化解决方案之前数据浏览的核心环节。

3D 数据共享工作流。我们从实验开始,以填充一个数据库。然后使用该数据库来探索或导出结果,以进行筛选和发布。结果随后用于重新填充和更新 3D 数据库。© F. Poux

因此,导出过程必须创建各种适用于应用程序和软件环境的文件格式。每种文件格式都有其特定性,满足不同的使用案例和先前表达的需求。对于深入的 3D 数据导出,我建议参考以下文章:

## LiDAR 城市模型的 3D Python 工作流:逐步指南

解锁 3D 城市建模应用程序的精简工作流的终极指南。教程涵盖了 Python…

towardsdatascience.com

了解如何在矢量和栅格数据集上进行相同的操作是至关重要的。像往常一样,越简单越好:让我们专注于最后的导出步骤。要导出栅格图像,你可以使用 rasterio 执行以下操作:

with ra.open("../RESULTS/CIR_34FN2_cropped.tiff",'w',**out_meta) as wf:
    wf.write(out_raster)

关于矢量数据集,geopandas 使得从我们的处理阶段导出地理参考和裁剪结果变得轻而易举:

vector_data_clipped_pc.to_file("../RESULTS/roads.shp")

最后,你可以将这些数据导入到像 QGIS 或 CloudCompare 这样的软件中,结果如下:

3D 点云、3D 体素、3D 城市模型、3D DTM、栅格和矢量数据集的叠加。它们与提出的方法论进行了整合。© F. Poux

这是 3D、栅格和矢量数据集的完美层叠!

结论

我们开始了一段激动人心的旅程,探索使用 Python 进行 3D 数据集成的世界。在这份全面的指南中,我们揭示了将各种 3D 数据模态(如点云、网格、城市模型、DSM、DTM 和体素)结合起来,以创建空间环境的统一和全面表示的巨大潜力。

本 3D 数据集成指南中介绍的工作流。© F. Poux

总结一下,我在下面列出了我们所涵盖的七个步骤的关键要点:

  • 在 Python 中设置多模态编码环境需要配备尽可能少的强大库。

  • 在获取数据集时,最好仔细考虑通过网络接口提供的具有明确许可选项的开放数据集,这样您可以轻松收集相关样本。

  • 独立地对每种模态进行分析并评估其主要特性(坐标参考系统、精度、分辨率等)是一项优秀的实践,建议通过快速分析方案来实现。

  • 掌握坐标参考系统,并了解如何从一个系统转换到另一个系统,以及特定的转换意味着什么,对于具有空间数据集的可扩展工作流是强制性的。

  • 预处理算法和技术通常是优化和组织的数据集与具有混乱管理系统的数据仓库之间的关键所在。

  • nD 数据可视化对成功的数据集成工作流至关重要,并要求您仔细考虑哪个数据集/数据模态充当您的标准锚点。

  • 数据共享作为最终阶段,允许确保一个从 A 到 Z 的系统,考虑实际和操作性的因素,即使在学术界或研发活动中,生产阶段也可能被忽视。

在结束本指南时,我无法不对 3D 数据处理的未来感到兴奋。目前,凭借 Python 的多功能性和不断增长的地理空间技术,我们有了更多的机会来更好地理解复杂的空间现象(并对此采取行动)。

因此,您可以期待更深入的高效算法、创新可视化技术和与前沿技术无缝集成的指南。通过探索我们能够利用集成的 3D 数据工作流完成的任务,我们为新应用铺平了道路,其中一些应用很快就会到来。这使我说,您正走在塑造空间分析和可视化未来的正确轨道上,改变我们感知和互动的方式。

参考文献

  1. Cárdenas, Ivan L., Morales, Luis Rodrigoandrés, Koeva, Mila, Atun, Funda, & Pfeffer, Karin. (2023 年 8 月 31 日). 《用于生理等效温度计算的数字双胞胎指南》。Zenodo. doi.org/10.5281/zenodo.83064562.

  2. Kumalasari, D.; Koeva, M.; Vahdatikhaki, F.; Petrova Antonova, D.; Kuffer, M. 《规划可步行城市:面向数字双胞胎实施的生成设计方法》。遥感. 2023, 15, 1088. doi.org/10.3390/rs150410883.

  3. Rajan, V.; Koeva, M.; Kuffer, M.; Da Silva Mano, A.; Mishra, S. 《通过多时相遥感数据对过去和现在的沙贾汉纳巴德进行三维建模》。遥感. 2023, 15, 2924. doi.org/10.3390/rs151129244.

  4. Ying, Y.; 科娃,M.; Kuffer, M.; Zevenbergen, J. 关于三维属性估值——城市三维建模方法在数字孪生创建中的综述。《国际地理信息科学期刊》,2023,12,2. doi.org/10.3390/ijgi120100025.

  5. La Guardia, M.; 科娃,M. 朝向网络上的数字孪生:基于开源结构的异构三维数据融合。《遥感》,2023,15,721. doi.org/10.3390/rs150307216.

  6. Khawte, S. S., 科娃,M. N., Gevaert, C. M., Oude Elberink, S., 和 Pedro, A. A.:基于无人机数据的巴西贫民窟数字孪生创建,《国际摄影测量与遥感档案》,XLVIII-4/W4–2022,75–81,doi.org/10.5194/isprs-archives-XLVIII-4-W4-2022-75-2022, 2022

4 个历来最有用的 pandas groupby 技巧

原文:towardsdatascience.com/4-all-time-useful-use-cases-of-pandas-group-by-77aae706322b

数据科学

一站式解决方案:解答你关于使用 pandas groupby 进行数据汇总的所有问题

Suraj GuravTowards Data Science Suraj Gurav

·发表在 Towards Data Science ·阅读时间 8 分钟·2023 年 3 月 24 日

--

图片由 Hands off my tags! Michael Gaida 提供,来自 Pixabay

在 Python 中,pandas 是一个常用的数据分析库。凭借其众多内置函数和方法,它使数据分析变得更快、更简单。

数据分析中最重要的方面之一是数据汇总,它帮助你按一个变量对数据进行分组,并将其余的数值数据汇总以获取摘要统计信息。最终,你可以使用这些摘要统计信息来回答业务问题。

这就是 pandas 函数 **groupby()** 的用途,它根据分类或非数值列的值对数据进行分组,并帮助你按这些新形成的组分析数据。

在我之前的一篇文章中 — 你应该知道的 5 个 pandas groupby 技巧— 你可以了解什么是 groupby 以及如何使用它。

在本文中,我将解释 4 个非常有用且常被搜索的 pandas groupby 技巧,并附有示例,这是你进行数据分析时必须了解的。

本文末尾我还附上了一个包含所有示例的完整 Jupyter Notebook。

让我们开始吧!

在这篇文章中,我使用了 UC Irvine Machine Learning Repository 上最受欢迎的数据集 — Iris — 该数据集由 R.A. Fisher 创建,并在 CC BY 4.0 许可证下提供。

让我们将数据集导入 pandas DataFrame — **df**

import pandas as pd
df = pd.read_csv("iris.data",
                 header=None,
                 names=["sepal_length", "sepal_width",
                        "petal_length","petal_width","class"])
df.head()

Iris 数据集 | 作者图片

这是一个简单的 150 x 5 数据集,包含 3 种 Iris 植物的相关信息。

我们先从第一个用例开始,假设你已经探索了如何使用 ***groupby***,如上文所述。

对不同列应用不同函数

在 Pandas groupby 中,你可以通过分类/非数值列对数据集中的所有行进行分组,并对其他所需的数值列应用函数以进行聚合。

例如,假设你想获取每个 Iris 植物类别的花萼长度。你可以简单地在groupby中传递列class,并对列sepal_length使用sum()函数,如下所示。

df.groupby('class')['sepal_length'].sum()

简单的 pandas groupby | 作者图片

这非常简单。

但是如果你想获取每个类别的总花萼长度和平均花萼宽度呢?

这时,**agg()**函数就派上用场了,它用于对通过使用pandas.DataFrame.groupby获得的 pandas DataFrame groupby 对象应用聚合函数。

当然,你不需要多次使用groupby(),而是可以创建一个单独的 groupby 对象,将列及其聚合函数作为键值对传递给agg()函数。

让我们看看如何实现。

在这里,你想对其应用聚合函数的两列是 — sepal_lengthsepal_width — 实际上你要对这些列应用的聚合函数是 summean

因此,键值对,即字典,将如下所示

dict1 = {'sepal_length':'sum', 'sepal_width':'mean'}

groupby 对象将被创建为:

groupby_object = df.groupby(["class"])

然后你可以对这个 groupby 对象应用agg()函数,如下所示,

groupby_object.agg(dict1)

以以下输出为例 —

对不同列应用多个函数 | 作者图片

好吧,你不需要将代码拆分成 3 行,我这样做只是为了简化问题。

通过将所有内容合并到以下单行代码中,你将得到完全相同的输出。

df.groupby(["class"]).agg({'sepal_length':'sum', 'sepal_width':'mean'})

扩展相同的逻辑,你可以在同一列上应用多个函数,但方式略有不同。

对同一列应用多个函数

通常,你需要为同一列在类别间找到多个统计数据,例如计算最小值和最大值。为此,你可以将函数列表传递给agg()方法。

举个例子,假设你想获取每个 Iris 植物类别的花萼长度的最小值、最大值和中位数。

你要对这列应用的实际聚合函数是 minmaxmedian,这些必须作为列表传递,如下所示。

df.groupby('class')['sepal_length'].agg(['min', 'max', 'median'])

在同一列上应用多个函数 | 图片来源:作者

按照相同的方法,你可以同时在多个列上应用多个函数。

例如,除了上述计算之外,你还可以获得 sepal width 的相同度量。

由于这是一个多列场景,你应该像在前一个方法中那样切换回列名和函数名的键值对。然而,这次你将传递函数名的列表,如下所示。

df.groupby(["class"]).agg({'sepal_length':['min', 'max', 'median'],
                           'sepal_width':['min', 'max', 'median']})

在多列上应用多个函数 | 图片来源:作者

因此,你将得到一个易于理解的 DataFrame。

然而,它仍然有两行作为列标题,你可以通过简单地重命名聚合列来去掉它们。

自定义聚合列名称

你可以为列聚合使用你选择的名称,以便轻松识别返回的列聚合,并去掉列标题中的第二行。

你需要做的就是创建列名和聚合函数的元组,并将这个元组分配给列名。

继续上述示例,你希望在列 sepal_length 上应用函数 ‘min’,因此元组将是 (‘sepal_length’, ‘min’),你将把这个元组分配给名为 min_sepal_length 的列,如下所示。

min_sepal_length = ('sepal_length','min')

同样,你可以为所有列聚合创建元组,并将它们分配给自定义名称。

min_sepal_length = ('sepal_length','min')
max_sepal_length = ('sepal_length','max')
median_sepal_length = ('sepal_length','median')

min_sepal_width = ('sepal_width','min')
max_sepal_width = ('sepal_width','max')
median_sepal_width = ('sepal_width','median')

最后,将它们用逗号分隔传递给 agg() 函数,如下所示。

df.groupby(["class"]).agg(min_sepal_length = ('sepal_length','min'),
                          max_sepal_length = ('sepal_length','max'),
                          median_sepal_length = ('sepal_length','median'),
                          min_sepal_width = ('sepal_width','min'),
                          max_sepal_width = ('sepal_width','max'),
                          median_sepal_width = ('sepal_width','median'))

在 Pandas 聚合函数中命名返回的列 | 图片来源:作者

快速解决!

现在,这看起来简化了很多,易于阅读。

到目前为止,你仅在一个或多个列上应用了标准或内置函数。然而,有时你需要应用一个用户定义的函数,使用聚合函数你可以轻松做到这一点。

应用自定义或用户定义的函数

通常,pandas 中内置的聚合函数不能满足你的需求,你需要为分析定义自己的自定义函数。

现在,你可以使用 agg 方法将这些用户定义或自定义函数应用于分组数据。

例如,假设你定义了一个自定义函数来计算列的范围,如下所示。

def my_range(x):
    return x.max() - x.min()

现在你希望计算鸢尾花的 sepal length、sepal width、petal length 和 petal width 的范围,并按其类别进行区分。

再次这是多列场景,因此你必须使用键值对,其中键是列名值是用户定义函数的名称,如下所示。

df.groupby(["class"]).agg({'sepal_length':my_range,
                           'sepal_width':my_range,
                           'petal_length':my_range,
                           'petal_width':my_range})

这将快速给出如下输出 —

在多列上应用用户定义的函数 | 图片来源:作者

然而,对于上述 DataFrame 的查看者来说,理解这些数字使用了哪种聚合函数将会比较困难。

因此,添加更多上下文是有意义的,可以通过添加每列的最大值和最小值来实现。

有趣的是,你还可以将自定义函数与内置函数结合使用,并将它们应用于agg()函数中的多个列。

让我们再看一个例子。

假设你想计算数据集中所有数值列的最小值、最大值和自定义函数。

现在这是多列——多函数的情况,这意味着你仍然会使用键值对,但将函数列表作为值,如下所示。

df.groupby(["class"]).agg({'sepal_length':['min', 'max', my_range],
                           'sepal_width':['min', 'max', my_range],
                           'petal_length':['min', 'max', my_range],
                           'petal_width':['min', 'max', my_range]})

以快速输出为例 —

在多个列上应用多个函数 | 作者提供的图片

有趣,对吧?

但仍然很难说my_range函数的内容以及在该函数中发生了什么计算。

给这个函数一个有意义的名称,然后再次使用该函数进行聚合更为合理。

你可以使用下面的代码轻松为函数指定名称。

my_range.__name__ = 'Max - Min'

然后在所有需要的列上重新应用my_range函数,如下所示,

df.groupby(["class"]).agg({'sepal_length':['min', 'max', my_range],
                           'sepal_width':['min', 'max', my_range],
                           'petal_length':['min', 'max', my_range],
                           'petal_width':['min', 'max', my_range]})

pandas groupby agg 自定义函数 多列 | 作者提供的图片

现在,这个输出看起来更有用,因为它准确地告诉你对每列进行了什么类型的计算。

这就是关于使用 pandas groupby的数据聚合! 🏆

我希望你觉得这篇文章有用,并将其保存为 pandas .groupby()用例的一站式解决方案

了解关于使用 pandas .groupby()的数据聚合的这些灵活特性无疑可以提升你的生产力。它还帮助你以更好和易于理解的方式汇总和表示数据。

对阅读更多 Medium 上的故事感兴趣吗?

💡 考虑 成为 Medium 会员无限访问Medium 上的故事和每日有趣的 Medium 摘要。我将从你的费用中获得一小部分,并且你无需额外付费。

💡 确保 注册我的邮件列表 以免错过有关数据科学指南、技巧和窍门、SQL 和 Python 的其他文章。

💡 这是一个完整的 Notebook 包含所有示例。

感谢阅读!

4 个你需要了解的自主 AI 代理

原文:towardsdatascience.com/4-autonomous-ai-agents-you-need-to-know-d612a643fa92?source=collection_archive---------0-----------------------#2023-04-16

“Westworld” 模拟器、Camel、BabyAGI、AutoGPT ⭐ 具备 LangChain ⭐ 的力量

Sophia Yang, Ph.D.Towards Data Science Sophia Yang, Ph.D.

·

关注 发表在 Towards Data Science ·9 min read·Apr 16, 2023

--

自主 AI 代理已经成为最热门的话题。令人印象深刻的是这一领域的进展如此迅速。自主 AI 代理是否是未来,特别是在提示工程领域?包括 Andrej Karpathy 在内的 AI 专家称 AutoGPTs 为提示工程的下一前沿。我也是这样认为的。你怎么看?

在最简单的形式中,自主 AI 代理以循环的方式生成自我指导的指令和行动。因此,它们不依赖于人类来指导对话,并且具有很高的可扩展性。过去两周内至少有 4 个显著的自主 AI 代理项目出现,在这篇文章中,我们将深入探讨每一个:

  • “西部世界”模拟 — 发布于 4 月 7 日

  • Camel — 发布于 3 月 21 日

  • BabyAGI — 发布于 4 月 3 日

  • AutoGPT — 发布于 3 月 30 日

项目 1:“西部世界”模拟

图 1. 生成代理创建可信的人类行为模拟。来源:arxiv.org/pdf/2304.03442.pdf

来自斯坦福大学和谷歌的研究人员创建了一个互动沙盒环境,其中包含 25 个生成 AI 代理,这些代理可以模拟人类行为。他们在公园散步,在咖啡馆喝咖啡,和同事分享新闻。他们展示了令人惊讶的良好社交行为:

“例如,从一个代理想要举办情人节派对的单一用户指定概念开始,代理们在接下来的两天里自主地向派对发送邀请,结识新朋友,互相邀请参加派对,并协调在合适的时间一起到场。”

这些可信的人类行为模拟之所以可能,是因为有一个代理架构(见图 2),它扩展了一个大型语言模型,并包括三个重要的架构基础:记忆、反思和规划。

图 2. 生成代理架构。来源:arxiv.org/pdf/2304.03442.pdf

1) 记忆与检索

记忆流包含每个代理的观察列表及时间戳。观察可以是代理执行的行为或代理从其他人那里感知到的行为。记忆流很长,但记忆流中的所有观察都不一定重要。

要检索最重要的记忆以传递给语言模型,需要考虑三个因素:

  • 近期性:近期记忆更为重要

  • 重要性:代理认为重要的记忆。例如,与某人分手的记忆比吃早餐的记忆更重要。

  • 相关性:与情况相关的记忆,即查询记忆。例如,在讨论化学考试的学习内容时,学业记忆更为重要。

图 3. 记忆流包含大量观察。检索识别应传递给语言模型的这些观察子集。来源:arxiv.org/pdf/2304.03442.pdf

2) 反思

反思是高层次的抽象思考,帮助代理进行概括和推断。反思会定期生成,回答以下两个问题:“我们能回答关于陈述中主题的 3 个最突出的高层次问题是什么?”、“从以上陈述中你能推断出 5 个高层次的见解是什么?”

图 4. 反思树。来源:arxiv.org/pdf/2304.03442.pdf

3) 规划

规划很重要,因为行动不仅要关注当前时刻,还要在更长时间范围内,以确保它们具有连贯性和可信度。计划也会被存储在记忆流中。代理可以基于计划创建行动,并根据记忆流中的其他观察结果对计划进行反应和更新。

图 5. 情人节派对。来源:arxiv.org/pdf/2304.03442.pdf

这些应用的可能性是巨大的,甚至可能有点吓人。想象一下一个助手,它观察并监控你的每一个动作,为你制定计划,甚至可能为你执行计划。它会在你甚至还没告诉它做什么之前,自动调整灯光、煮咖啡,并为你预定晚餐。

⭐LangChain 实现⭐

…敬请期待…

我听说 LangChain 正在开发这个 😉 实现后会添加上。

项目 2:骆驼

CAMEL(沟通型代理用于“大脑”探索大规模语言模型社会)提出了一个角色扮演代理框架,其中两个 AI 代理彼此交流:

  1. AI 用户代理:给 AI 助手指示,以完成任务为目标。

  2. AI 助手代理:根据 AI 用户的指示执行任务并提供解决方案。

  3. 任务指定代理:实际上还有另一个代理称为任务指定代理,用于为 AI 用户和 AI 助手制定特定任务。这有助于写出具体的任务提示,而用户无需花时间定义它。

在这个例子中(图 6),一个人有一个开发交易机器人的想法。AI 用户是一个股票交易员,而 AI 助手是一个 Python 程序员。任务特定代理首先提出一个具体任务及任务细节(监控社交媒体情绪并根据情绪分析结果进行股票交易)。然后,AI 用户代理成为任务规划者,AI 助手代理成为任务执行者,它们循环互相提示,直到满足某些终止条件。

图 6. 角色扮演框架。来源:arxiv.org/abs/2303.17760

Camel 的精髓在于其提示工程,即初始提示。提示实际上是经过精心定义的,以分配角色、防止角色翻转、禁止伤害和虚假信息,并鼓励一致的对话。请参见 Camel 论文 中的详细提示。

⭐LangChain 实现⭐

LangChain 实现 使用了在 Camel 论文 中提到的提示,并定义了三个代理:task_specify_agent、assistant_agent 和 user_agent。然后,它使用一个 while 循环来遍历助手代理和用户代理之间的对话:

chat_turn_limit, n = 30, 0
while n < chat_turn_limit:
    n += 1
    user_ai_msg = user_agent.step(assistant_msg)
    user_msg = HumanMessage(content=user_ai_msg.content)
    print(f"AI User ({user_role_name}):\n\n{user_msg.content}\n\n")

    assistant_ai_msg = assistant_agent.step(user_msg)
    assistant_msg = HumanMessage(content=assistant_ai_msg.content)
    print(f"AI Assistant ({assistant_role_name}):\n\n{assistant_msg.content}\n\n")
    if "<CAMEL_TASK_DONE>" in user_msg.content:
        break

结果看起来相当合理!

在 Camel 中,AI 助手的执行只是来自语言模型的答案,而不实际使用任何工具来运行 Python 代码。我想知道 LangChain 是否计划将 Camel 与所有令人惊叹的 LangChain 工具集成 🤔

🐋 真实世界的应用案例 🐋

  • 制作一个游戏

  • 渗透通信网络

项目 3:BabyAGI

Yohei Nakajima 在 3 月 28 日宣布了“任务驱动的自主代理”,并在 4 月 3 日开源了 BabyAGI 项目。BabyAGI 的关键特性只有三个代理:任务执行代理、任务创建代理和任务优先级代理。

    1. 任务执行代理 完成任务列表中的第一个任务
    1. 任务创建代理 根据前一个任务的目标和结果创建新任务。
    1. 任务优先级代理 然后重新排序任务。

然后,这个简单的过程被重复了一遍又一遍。

在 LangChain 的网络研讨会上,Yohei 提到他设计 BabyAGI 的方式是模拟他自己的工作方式。具体来说,他每天早晨开始时处理待办事项列表中的第一个项目,然后继续处理他的任务。如果出现新任务,他会简单地将其添加到列表中。在一天结束时,他会重新评估并重新排序他的列表。这种相同的方法随后被映射到代理中。

图 7. BabyAGI 流程图。来源:yoheinakajima.com/task-driven-autonomous-agent-utilizing-gpt-4-pinecone-and-langchain-for-diverse-applications/ (有趣的是 GPT-4 写了这篇研究论文)

⭐BabyAGI + LangChain⭐

BabyAGI 在 LangChain 框架内运行很简单。查看 代码。它基本上创建了一个 BabyAGI 控制器,该控制器由三个链 TaskCreationChain、TaskPrioritizationChain 和 ExecutionChain 组成,并以(潜在的)无限循环运行。通过 LangChain,你可以定义最大迭代次数,这样它就不会永远运行并花费所有的钱在 OpenAI API 上。

OBJECTIVE = "Write a weather report for SF today"
llm = OpenAI(temperature=0)
# Logging of LLMChains
verbose=False
# If None, will keep on going forever
max_iterations: Optional[int] = 3
baby_agi = BabyAGI.from_llm(
    llm=llm,
    vectorstore=vectorstore,
    verbose=verbose,
    max_iterations=max_iterations
)
baby_agi({"objective": OBJECTIVE})

这是 2 次迭代运行的结果:

⭐BabyAGI + LangChain 工具⭐ = 超能力

正如上面的示例所示,BabyAGI 仅“执行”来自 LLM 的响应。借助 LangChain 工具的强大功能,执行步骤可以使用各种工具,例如 Google 搜索,实际在线搜索信息。这里是一个例子,其中“执行”使用 Google 搜索来查找旧金山当前的天气状况。

BabyAGI 的应用潜力也非常巨大!我们只需告诉它一个目标,它就会为你执行。我认为它唯一缺少的就是接受用户反馈的接口。例如,在 BabyAGI 为我预约之前,我希望它能先与我确认。我认为 Yohei 实际上正在致力于此,以便系统能够动态调整任务优先级。

🐋 现实世界的应用案例 🐋

项目 4:AutoGPT

AutoGPT 很像 BabyAGI 和 LangChain 工具的结合体。它遵循与 BabyAGI 类似的逻辑:生成思想、推理、制定计划、批评、规划下一步行动并执行,是一个无限循环。

在执行步骤中,AutoGPT 可以执行许多 命令,例如 Google 搜索、浏览网站、写入文件和执行 Python 文件。它甚至可以启动和删除 GPT 代理?!这真是太酷了!

运行 AutoGPT 时,有两个初始输入将提示你输入:1)AI 的角色和 2)AI 的目标。在这里,我只是使用了给定的示例——建立一个业务。

它能够生成思想、推理、计划、批评、规划下一步行动并执行(在此案例中为 Google 搜索):

我非常喜欢 AutoGPT 的一点是,它允许人类互动(某种程度上)。当它需要运行 Google 命令时,它会请求授权,这样你可以在花费过多 OpenAI API 代币之前停止循环。不过,如果它还能与人类对话,以便我们可以实时提供更好的指示和反馈,那就更好了。

⭐LangChain 实现⭐

…敬请期待…

我听说 LangChain 正在开发这一功能 😉 一旦实现,将会添加进来。

🐋 现实世界的应用案例 🐋

  • 编写和执行 Python 代码:

  • 更多:

结论

在这篇文章中,我们探讨了四个显著的自主 AI 代理项目。尽管这些项目还处于早期开发阶段,但它们已经展示了令人印象深刻的成果和潜在应用。然而,需要注意的是,这些项目都存在显著的局限性和风险,例如代理可能陷入循环、幻觉和安全问题以及伦理问题。尽管如此,自主代理无疑代表了未来的一个有前途的领域,我期待看到该领域的进一步进展和发展。

参考资料:

“西部世界”模拟

骆驼

BabyAGI

AutoGPT

. . .

Sophia Yang 于 2023 年 4 月 16 日发布

Sophia Yang 是一位高级数据科学家。可以通过 LinkedInTwitterYouTube 与我联系,并加入 DS/ML 读书俱乐部 ❤️

4 位量化与 GPTQ

原文:towardsdatascience.com/4-bit-quantization-with-gptq-36b0f4f02c34?source=collection_archive---------0-----------------------#2023-07-31

使用 AutoGPTQ 量化你自己的 LLM

Maxime LabonneTowards Data Science Maxime Labonne

·

关注 发表在Towards Data Science · 10 分钟阅读 · 2023 年 7 月 31 日

--

图片由作者提供

最近的权重量化进展使我们能够在消费者硬件上运行大规模语言模型,例如在 RTX 3090 GPU 上运行 LLaMA-30B 模型。这得益于新颖的 4 位量化技术,性能降级最小,如GPTQGGMLNF4

上一篇文章中,我们介绍了朴素的 8 位量化技术和优秀的 LLM.int8()。在本文中,我们将深入探讨流行的GPTQ 算法,了解其工作原理,并使用AutoGPTQ库进行实现。

你可以在 Google ColabGitHub 上找到代码。

🧠 最优脑量化

首先介绍我们要解决的问题。对于网络中的每一层 ℓ,我们希望找到原始权重 Wₗ 的量化版本 Ŵₗ。这称为 逐层压缩问题。更具体地,为了最小化性能降级,我们希望这些新权重的输出 (ŴXᵨ) 尽可能接近原始权重的输出 (WXᵨ)。换句话说,我们希望找到:

已提出了不同的方法来解决这个问题,但我们在这里关注的是 最优脑量化器(OBQ)框架。

该方法的灵感来源于一种 剪枝技术,用于从完全训练的密集神经网络中小心地去除权重(最优脑外科医生)。它使用了一种近似技术,并提供了最佳单个权重 w𐞥 的显式公式以去除,并通过最佳更新 δꟳ 来调整剩余未量化权重 F,以弥补去除的影响:

其中 quant(w) 是由量化给出的权重舍入,Hꟳ 是 Hessian。

使用 OBQ,我们可以先量化最简单的权重,然后调整所有剩余的未量化权重以 补偿这种精度损失。然后我们选择下一个要量化的权重,依此类推。

这种方法的一个潜在问题是,当存在异常权重时,可能导致高 量化误差。通常,这些异常值会在量化最后处理,当剩下的未量化权重较少时,可以调整以弥补大误差。当一些权重通过中间更新被推到网格之外时,这种效应可能会加剧。一个简单的启发式方法是尽快量化出现的异常值。

这个过程可能计算量很大,特别是对于 LLM。为了解决这个问题,OBQ 方法使用了一种技巧,避免了每次简化权重时重新进行整个计算。量化一个权重后,它通过 去除与该权重相关的行和列(使用高斯消元法)来调整计算中使用的矩阵(Hessian):

该方法还采用了向量化处理一次处理多个权重矩阵的行。尽管效率很高,但随着权重矩阵大小的增加,OBQ 的计算时间显著增加。这种立方增长使得在具有数十亿参数的大型模型上使用 OBQ 变得困难。

🧮 GPTQ 算法

由 Frantar 等人(2023 年)提出的GPTQ 算法从 OBQ 方法中获得灵感,但对其进行了重大改进,以使其适用于(非常)大的语言模型。

第一步:任意顺序洞察

OBQ 方法按一定顺序选择要量化的权重(模型中的参数),该顺序由增加最少额外误差的权重决定。然而,GPTQ 观察到,对于大型模型,按任何固定顺序量化权重的效果都一样。这是因为即使某些权重可能单独引入更多误差,它们会在处理过程中较晚的阶段进行量化,此时可能没有其他权重可以增加误差。因此,顺序并不像我们想象的那样重要。

基于这一洞察,GPTQ 旨在对矩阵的所有行使用相同的顺序量化所有权重。这使得过程更快,因为某些计算只需要对每一列进行一次,而不是对每个权重进行一次。

图片由作者提供

第二步:懒惰批量更新

这个方案不会很快,因为它需要更新一个巨大的矩阵,每个条目的计算量非常少。这种操作无法利用 GPU 的全部计算能力,并且会受到内存限制(内存吞吐瓶颈)的影响。

为了解决这一问题,GPTQ 引入了“懒惰批量”更新。结果发现,给定列的最终舍入决策只受该列上的更新影响,而不受后续列的影响。因此,GPTQ 可以将算法应用于一次处理一批列(如 128 列),仅更新这些列及矩阵中的相应块。一个块完全处理后,算法对整个矩阵进行全局更新。

第三步:Cholesky 重构

不过,还有一个问题需要解决。当算法扩展到非常大的模型时,数值不准确可能成为问题。具体来说,某些操作的重复应用可能会积累数值误差

为了解决这个问题,GPTQ 使用了Cholesky 分解,这是一种数值稳定的数学问题解决方法。它涉及使用 Cholesky 方法从矩阵中预计算一些所需的信息。这种方法,加上轻微的“减震”(向矩阵对角线元素中添加一个小常数),有助于算法避免数值问题。

完整算法可以总结为几个步骤:

  1. GPTQ 算法以 Hessian 逆矩阵的 Cholesky 分解开始(该矩阵有助于决定如何调整权重)。

  2. 然后它以循环的方式运行,每次处理一批列。

  3. 对于每个批次中的每一列,它对权重进行量化,计算误差,并相应地更新块中的权重。

  4. 处理批次后,它会根据块的错误更新所有剩余的权重。

GPTQ 算法在各种语言生成任务中进行了测试。它与其他量化方法进行了比较,比如将所有权重舍入到最近的量化值(RTN)。GPTQ 与 BLOOM(176B 参数)和 OPT(175B 参数)模型系列一起使用,并且模型是使用单个 NVIDIA A100 GPU进行量化的。

💻 使用 AutoGPTQ 对 LLM 进行量化

GPTQ 在创建可以高效运行于 GPU 上的 4 位精度模型方面非常受欢迎。你可以在 Hugging Face Hub 上找到许多示例,特别是来自TheBloke。如果你在寻找更适合 CPU 的方案,GGML目前是你的最佳选择。最后,transformers库与bitsandbytes允许你在加载模型时通过load_in_4bit=true参数进行量化,这需要下载完整的模型并将其存储在你的 RAM 中。

让我们使用 AutoGPTQ 库实现 GPTQ 算法,并对 GPT-2 模型进行量化。这需要一个 GPU,但 Google Colab 上的免费 T4 就足够了。我们从加载库和定义要量化的模型(在这种情况下是 GPT-2)开始。

!BUILD_CUDA_EXT=0 pip install -q auto-gptq transformers
import random

from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
from datasets import load_dataset
import torch
from transformers import AutoTokenizer

# Define base model and output directory
model_id = "gpt2"
out_dir = model_id + "-GPTQ"

我们现在要加载模型和分词器。分词器使用transformers库中的经典AutoTokenizer类加载。另一方面,我们需要传递一个特定的配置(BaseQuantizeConfig)来加载模型。

在此配置中,我们可以指定要量化的位数(这里是bits=4)和组大小(懒批次的大小)。请注意,这个组大小是可选的:我们也可以为整个权重矩阵使用一组参数。实际上,这些组通常能以非常低的成本提高量化的质量(特别是使用group_size=1024时)。damp_percent值用于帮助 Cholesky 重整化,不应更改。

最后,desc_act(也称为 act order)是一个棘手的参数。它允许你根据递减的激活处理行,意味着最重要或影响最大的行(由采样的输入和输出决定)首先处理。这种方法旨在将大部分量化误差(不可避免地在量化过程中引入)放在不太重要的权重上。通过确保最重要的权重以更高的精度处理,这种方法可以提高量化过程的整体准确性。然而,当与组大小一起使用时,desc_act可能会导致性能下降,因为需要频繁重新加载量化参数。因此,我们在这里不会使用它(不过它可能会在未来得到修复)。

# Load quantize config, model and tokenizer
quantize_config = BaseQuantizeConfig(
    bits=4,
    group_size=128,
    damp_percent=0.01,
    desc_act=False,
)
model = AutoGPTQForCausalLM.from_pretrained(model_id, quantize_config)
tokenizer = AutoTokenizer.from_pretrained(model_id)

量化过程严重依赖样本来评估和提升量化质量。样本提供了一个比较原始模型和新量化模型输出的方法。提供的样本数量越多,比较的潜力就越大,从而提高量化质量。

在本文的背景下,我们使用了C4 (Colossal Clean Crawled Corpus) 数据集来生成样本。C4 数据集是一个大规模、多语言的网页文本集合,来自 Common Crawl 项目。这个庞大的数据集已经被清理和准备好,专门用于训练大规模语言模型,使其成为类似任务的极佳资源。WikiText 数据集是另一个受欢迎的选择。

在以下代码块中,我们从 C4 数据集中加载 1024 个样本,对其进行分词和格式化。

# Load data and tokenize examples
n_samples = 1024
data = load_dataset("allenai/c4", data_files="en/c4-train.00001-of-01024.json.gz", split=f"train[:{n_samples*5}]")
tokenized_data = tokenizer("\n\n".join(data['text']), return_tensors='pt')

# Format tokenized examples
examples_ids = []
for _ in range(n_samples):
    i = random.randint(0, tokenized_data.input_ids.shape[1] - tokenizer.model_max_length - 1)
    j = i + tokenizer.model_max_length
    input_ids = tokenized_data.input_ids[:, i:j]
    attention_mask = torch.ones_like(input_ids)
    examples_ids.append({'input_ids': input_ids, 'attention_mask': attention_mask})

现在数据集已经准备好,我们可以开始量化过程,批量大小为 1。可选地,我们还使用了OpenAI Triton,这是 CUDA 的替代方案,用于与 GPU 进行通信。一旦完成,我们将分词器和模型保存为 safetensors 格式。

# Quantize with GPTQ
model.quantize(
    examples_ids,
    batch_size=1,
    use_triton=True,
)

# Save model and tokenizer
model.save_quantized(out_dir, use_safetensors=True)
tokenizer.save_pretrained(out_dir)

像往常一样,模型和分词器可以从输出目录中加载,使用AutoGPTQForCausalLMAutoTokenizer类。

device = "cuda:0" if torch.cuda.is_available() else "cpu"

# Reload model and tokenizer
model = AutoGPTQForCausalLM.from_quantized(
    out_dir,
    device=device,
    use_triton=True,
    use_safetensors=True,
)
tokenizer = AutoTokenizer.from_pretrained(out_dir)

让我们检查模型是否正常工作。AutoGPTQ 模型(大多数情况下)作为一个普通的transformers模型工作,这使得它与推理管道兼容,如以下示例所示:

from transformers import pipeline

generator = pipeline('text-generation', model=model, tokenizer=tokenizer)
result = generator("I have a dream", do_sample=True, max_length=50)[0]['generated_text']
print(result)
I have a dream," she told CNN last week. "I have this dream of helping my mother find her own. But, to tell that for the first time, now that I'm seeing my mother now, just knowing how wonderful it is that

我们成功地从量化后的 GPT-2 模型中获得了令人信服的结果。更深入的评估将需要测量量化模型与原始模型的困惑度。不过,这超出了本文的范围。

结论

在本文中,我们介绍了 GPTQ 算法,这是一种最先进的量化技术,用于在消费者级硬件上运行 LLMs。我们展示了它如何解决基于改进的 OBS 技术的层级压缩问题,结合了任意顺序洞察、惰性批量更新和 Cholesky 重构。这种新颖的方法显著减少了内存和计算需求,使 LLMs 可以被更广泛的受众使用。

此外,我们在免费的 T4 GPU 上量化了我们自己的 LLM 模型并运行它以生成文本。你可以在 Hugging Face Hub 上发布你自己的 GPTQ 4-bit 量化模型。正如引言中提到的,GPTQ 并不是唯一的 4-bit 量化算法:GGMLNF4是具有稍微不同范围的优秀替代方案。我鼓励你了解更多,并尝试一下!

如果你对更多关于 LLMs 的技术内容感兴趣,可以在 Twitter 上关注我 @maximelabonne

参考文献

相关文章

## 权重量化简介

使用 8 位量化减少大型语言模型的大小

towardsdatascience.com ## 在 Colab Notebook 中微调你的 Llama 2 模型

关于 LLM 微调的实用介绍

towardsdatascience.com

了解更多关于机器学习的内容,并通过一键支持我的工作 — 现在成为 Medium 会员:

## 通过我的推荐链接加入 Medium — Maxime Labonne

作为 Medium 会员,你的会员费用的一部分将会分配给你阅读的作者,并且你可以全面访问每个故事……

medium.com

如果你已经是会员,你可以 在 Medium 上关注我

4 个初学者应避免的常见 Python 错误

原文:towardsdatascience.com/4-common-python-mistakes-you-should-avoid-as-a-beginner-bd28feb6162b

以及如何在不小心破坏面试机会之前纠正自己。

Murtaza AliTowards Data Science Murtaza Ali

·发表在Towards Data Science ·阅读时长 7 分钟·2023 年 1 月 13 日

--

照片由David Pupaza提供,来自Unsplash

Python 是一个很适合初学者的语言,但这并不意味着不会犯错误。特别是在学习编程的早期阶段,容易编写出技术上正确但风格上欠佳的代码。

如果你打算学习编程,那么学会如何编写高质量的代码至关重要。无论是在学术界还是工业界,代码的质量都很重要。它不仅影响到你自己,还影响到每一个将要阅读和使用你代码的人。更自私一点说,它还影响到你的招聘前景。

在本文中,我将讨论初学 Python 程序员常犯的四个错误。学习这些陷阱对我早期学习 Python 非常有帮助,希望对你也同样有用。

我们开始吧。

传统布尔条件

这是初学者程序员常犯的一个错误。这也是那些没有正式编程背景的程序员所犯的错误,因为他们只是将代码作为工具使用。我在说你们,数据科学家们。

Python 中的条件语句很有用,但并非总是必要的。特别是当你检查的条件已经包含布尔值(True 或 False)时,更是如此。

让我用一个简单的例子来说明。假设我们想编写代码来确定数据集是否已经被清理。幸运的是,代码库中包含一个名为is_data_clean的方便变量来跟踪这一点。我们只需检查它并返回正确的值即可。

作为第一次尝试,我们可能会写如下内容:

def a_function():
    if is_data_clean == True:
        return True
    else:
        return False

这方法够用了,但不必要地复杂。你看到问题了吗?仔细看看。

变量is_data_clean已经是布尔值;因此,它已经包含了你需要返回的值!代码检查它是否为True,如果是,则返回True,如果不是True(即False),则代码返回False。这只是一些不必要的检查。

我们可以将函数中的代码简化为一行:

def a_function():
    return is_data_clean

好得多。

手动计算总和、均值或其他内置操作

Python 具有比大多数人意识到的更多的内置功能。仍然使用循环手动计算总和的人数太多了。

如果我们在 Python 中有一组数字,我们绝对应该这样计算总和:

total = 0
for num in numbers_list:
    total += num

改用内置的sum函数:

total = sum(numbers_list)

需要最小值还是最大值?宇宙不允许你这样写:

import math
minimum = math.inf # start at highest possible value
for number in numbers_list:
    if number < minimum:
        minimum = number

这不是一门计算机科学原理入门课;这是现实世界。停止重新发明轮子,使用内置的minmax函数:

minimum = min(numbers_list)
maximum = max(numbers_list)

要查看完整的内置函数列表,请参见Python 文档 [1]。

额外奖励:内置的函数虽然从技术上讲 不是内置的。

有些函数较难找到,但这并不意味着你不应该找到它们。

例如,如果我们需要一组数字的均值(你可能会感受到这里的重复主题),我们可以使用下面的第一个代码片段,但我们应该使用第二个:

# Snippet 1: Don't do this!
total = 0
for num in numbers_list:
    total += num
avg = total / len(numbers_list)

# Snippet 2: Do this!
import numpy as np
avg = np.mean(numbers_list)

通常,Python 提供了有用的函数,这些函数在模块中。找到我们需要的模块并导入函数可能需要一点额外的工作,但结果代码非常值得。

记住——Python 的核心是简单性和可读性。内置函数是你的朋友。与人类朋友不同,它们不会让你失望。

做点什么以便什么都不做

在我教授的某一门 Python 入门课程中,学生的第一个项目是编写一个简单的决策算法。这主要是一个条件判断的练习,要求学生定义一个问题和相关的评分系统,以确定某人是否符合该问题的条件。

例如,有人可能会问,“我应该成为数据科学家吗?”那么,算法可能会包含以下问题,所有这些问题都会根据答案对最终的输出分数进行加减:

  • 我是否有兴趣使用数据来获取对世界的洞察?

  • 我是否愿意学习 Python?

  • 我是否喜欢与跨学科团队合作?

诸如此类。

在编写算法的过程中,许多学生意识到在某些情况下,他们只是希望对整体分数不做任何修改。例如,他们可能决定,如果有人愿意学习 Python,那会给整体分数加 10 分,但如果他们不愿意,则分数保持不变。

大多数学生用以下代码实现这一点:

# willing_to_learn is some predefined variable based on user input
if willing_to_learn:
    score += 10
else:
    score += 0

这是一个经典的做某事却什么也没做的案例。让我们拆解 Python 在看到代码行score += 0时需要做的所有事情:

  • 它需要查找变量score的值。

  • 它需要在这个值上加上 0。这需要调用加法函数,传入两个参数(当前值和 0),并计算输出。

  • 重新将score变量赋值为新值(显然,这个值是相同的)。

所有这些工作却做了……什么都没有。

当然,这对计算机来说并不是大量工作,也不会对代码的效率产生任何实际影响。不过,它是无用的且有些不干净,这在优秀的 Python 代码中是不常见的。

一个更好的解决方案是使用 Python 的pass关键字,它字面上告诉 Python 什么也不做,只是继续。它填补了一行代码,这一行代码本不需要存在,但如果完全空着会导致错误。我们甚至可以添加一个小注释以提供进一步的清晰度:

if willing_to_learn:
    score += 10
else:
    pass # Leave score unchanged

更简洁、更清晰、更符合 Python 风格。

单个条件失控

条件语句可以说是标准编程中更强大和一致的构造之一。当第一次学习它时,很容易忽略一个重要的细微之处。

当我们需要检查两个或更多条件时,这个细微之处就会出现。例如,假设我们正在审查一个调查,调查结果有三种形式:“是”,“否”或“也许”。

早期的 Python 程序员通常会以两种方式之一来编写这个代码:

# Possibility 1
if response == "Yes":
    # do something
if response == "No":
    # do something
if response == "Maybe":
    # do something

# Possibility 2
if response == "Yes":
    # do something
elif response == "No":
    # do something
else:
    # do something

在这种情况下,这两个代码片段实际上是一样的。它们的行为相同,理解起来并不特别困难,并且实现了预期的目标。问题在于,当人们错误地认为上述两个结构总是等同的时候。

这是错误的。上面的第二个代码片段是由多个部分组成的单一条件表达式,而第一个代码片段则包含了三个独立的条件表达式,尽管它们看起来是相互连接的。

为什么这很重要?因为每当 Python 遇到一个全新的if关键字(即,新的条件表达式开始时),它都会检查相关的条件。另一方面,Python 仅会进入elifelse条件,如果当前条件表达式中的所有先前条件都未被满足。

让我们看一个例子,看看这为什么重要。假设我们需要编写代码,根据学生在某个作业中的分数来分配字母等级。我们在 Python 文件中编写以下代码:

score = 76

print("SNIPPET 1")
print()

if score < 100:
    print('A')
elif score < 90:
    print('B')
elif score < 80:
    print('C')
elif score < 70:
    print('D')
else:
    print('F')

print()
print("SNIPPET 2")
print()

if score < 100:
    print('A')
if score < 90:
    print('B')
if score < 80:
    print('C')
if score < 70:
    print('D')
if score < 60:
    print('F')

运行这段代码会输出以下内容:

SNIPPET 1

A

SNIPPET 2

A
B
C

你看到区别了吗?在第二种情况下,我们得到了意外的输出。为什么?因为 Python 将每个if语句视为全新的条件,因此如果一个分数恰好小于多个数字检查,那么对应的字母等级会被输出所有这些检查的结果。

现在,有方法可以让多个if语句工作;例如,我们可以让条件检查一个范围,而不仅仅是一个上限。这个例子的重点不是争论一个示例优于另一个示例(虽然我个人倾向于使用elifelse以提高清晰度),而只是为了说明它们并不是相同的

确保你理解这一点。

最后的思考与总结

这是你的 Python 初学者备忘单:

  1. 当你可以直接返回布尔值时,不要为布尔值创建不必要的条件语句。

  2. 内置函数是你最好的朋友。

  3. 如果你需要告诉 Python 什么都不做,使用pass关键字。

  4. 确保你正确构建条件表达式,理解ifelifelse关键字的含义。

你决定学习 Python 是很棒的——我向你保证,这门语言会对你很好。

只要记得回报它们。

想在 Python 中表现出色? 点击这里获取独家、免费的简易指南。想在 Medium 上阅读无限故事?使用下面的推荐链接注册!

阅读 Murtaza Ali 的文章 [## Murtaza Ali - Medium

阅读 Murtaza Ali 在 Medium 上的文章。华盛顿大学的博士生。对人机交互感兴趣…

murtaza5152-ali.medium.com

参考资料

[1] docs.python.org/3/library/functions.html

评估行业应用中的大型语言模型的 4 个关键因素

原文:towardsdatascience.com/4-crucial-factors-for-evaluating-large-language-models-in-industry-applications-f0ec8f6d4e9e

每个用例都是不同的——根据客户需求和行业特定的指南。学习如何通过 4 个关键标准做出正确的 LLM 选择

Skanda VivekTowards Data Science Skanda Vivek

·发表于 Towards Data Science ·8 分钟阅读·2023 年 8 月 1 日

--

LLM 决策指标 | Skanda Vivek

在过去几个月里,我有机会与来自法律、医疗、金融、科技、保险等行业的人士交流 LLM 的应用。每个行业都有独特的需求和挑战。例如,在医疗领域——隐私至关重要。在金融领域,准确的数据至关重要。律师需要为起草法律文件等任务提供专业的、精细调整的模型。

在本文中,我将探讨帮助你为特定案例选择合适模型的关键决策因素。

响应质量

正如 Satya Nadella 在他 2023 年 Microsoft Inspire 的主题演讲 中所述,生成式 AI 引入了两个主要的范式转变:

  1. 更自然的语言计算机界面

  2. 一个位于所有自定义文档之上的推理引擎

在这两类使用场景中,响应质量极其重要。我们与计算机的界面正变得越来越接近自然语言(想想 Python 相对于 C++的友好程度,或者 C++相对于机器语言的友好程度)。然而,这些编程语言的可靠性从未真正成为问题——如果有问题,我们称之为编程错误,并归因于人为错误。然而,更自然的 LLM 界面带来了新问题,因为 LLM 会出现幻觉或给出错误答案,从而引入了新类型的“AI 错误”。因此,响应质量变得极其重要。

第二种用例也是如此。虽然我们都习惯使用 Google 搜索,但在幕后,Google 使用向量嵌入和其他匹配技术来确定哪个页面最有可能包含你所问问题的答案。如果页面列出了错误的结果——这也是人为错误,因为人们列出了不正确的信息。然而,LLM 再次引入了答案可能更加定制化的可能性,但也可能是错误的。

最近,出现了许多新的开源 LLM 进展,如 Llama2、Falcon 等。最近有一个具有挑战性的多基准测试引入了一种方法,使用强大的 LLM 作为评审来评估其他 LLM在更开放性的问题上的表现。如你所见,它们根据任务有不同类型的表现。值得查看各种模型——无论你的组织是在寻找定制聊天机器人,还是从文本中提取信息,生成自定义代码,创意写作等。

模型按类别的胜率 | Skanda Vivek(数据来自 MT-Bench LLM-as-a-judge 论文)

LLM 经济学

各种 LLM 解决方案的费用取决于你拥有的用例类型。我在这里详细讨论了这个问题:

[## LLM 经济学:ChatGPT 与开源]

部署像 ChatGPT 这样的 LLM 需要多少钱?开源 LLM 的部署是否更便宜?有什么权衡?

towardsdatascience.com](/llm-economics-chatgpt-vs-open-source-dfc29f69fec1?source=post_page-----f0ec8f6d4e9e--------------------------------)

TL;DR:对于每天请求量在 1000 次范围内的低使用情况,ChatGPT 比在 AWS 上部署的开源 LLM 更便宜。对于每天数百万次请求,部署在 AWS 上的开源模型更便宜。

比较 LLM 成本的卡通示意图 | Skanda Vivek

延迟

如果你正在处理实时请求,例如面向客户的聊天机器人,那么延迟是很重要的。可能和响应质量一样重要(甚至更多)。让我们来看一下不同 OpenAI 模型的延迟情况:GPT-3/3.5/4。多个基准测试表明,与 GPT-3.5(ChatGPT)相比,GPT-4 具有更优越的性能,而 GPT-3.5 则在 GPT-3 之上带来了革命性的变化。

假设作为一个创意机构,你希望使用 LLM 模型为企业家提供创意建议——比如为冰淇淋店新项目写一个标语。我在下面写了一些示例代码,从三个 GPT 模型生成这个标语:

#GPT-3

import time
t0=time.time()

response_gpt3 = openai.Completion.create(
  model="text-davinci-003",
  prompt="Write a tagline for an ice cream shop."
)

t1=time.time()

gpt_3_time=t1-t0

#GPT-3.5
import time
t0=time.time()
response_gpt3p5=openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
        {"role": "system", "content": "Write a tagline for an ice cream shop."}

    ]
)

t1=time.time()

gpt_3p5_time=t1-t0

#GPT-4

import time
t0=time.time()
response_gpt4=openai.ChatCompletion.create(
  model="gpt-4",
  messages=[
        {"role": "system", "content": "Write a tagline for an ice cream shop."}
    ]
)

t1=time.time()

gpt_4_time=t1-t0

响应如下。就个人而言,我更喜欢 GPT-4 的输出,但其他的也还不错。

"Cool off with all the flavor you can imagine!" ---- GPT 3 
"Scoop up happiness, one flavor at a time!" ---- GPT 3.5 
"Scooping Happiness One Cone at a Time" ---- GPT 4

现在,让我们来看一下延迟。令人惊讶的是,GPT-3.5 返回的结果最快(比 GPT-3 快),而 GPT-4 最慢,比 GPT-3.5 慢 1.4 倍。

print(gpt_3_time,gpt_3p5_time,gpt_4_time)

0.8402369022369385 0.7697513103485107 1.0829169750213623

如果基于行业特定的指标来看结果相似,而 1.4 倍的延迟不可接受,那么 GPT-3.5 是优于 GPT-4 的选择。但也许延迟不是一个大问题——比如用户收到定制报告的电子邮件,而不是实时看到响应,响应的质量更为重要。在这种情况下,GPT-4 可能是更优选项。

隐私与安全

许多来自医疗行业的人对 LLM 和从大量数据中精确定位详细医疗信息的创新非常感兴趣。然而,这些人对隐私也极为担忧。他们不喜欢使用与其他人相同的 API 处理私人数据的想法。此外,将个人健康数据共享给像 ChatGPT 这样的 API 可能会带来严重后果。

还有另外两个选择——一个是在 AWS、Azure 或 Google Cloud 等云提供商上托管开源 LLM。云提供商在遵守医疗指南和法律方面有着悠久的历史,例如《健康保险流通与问责法案》(HIPAA)。例如,AWS 有一份专门的白皮书,介绍了如何在 AWS 上架构 HIPAA 合规的应用程序。我也写了一些关于如何在私有云实例上托管 LLM 作为 API 的博客:

[## 部署开源 LLM 作为 API

开源的 LLM(大语言模型)现在非常流行,但对于封闭源 LLM API 的数据隐私问题也引起了关注。这个教程…

skanda-vivek.medium.com [## 使用 Azure ML 部署 LLM

这是一个关于如何使用微软 Azure ML 目录部署 LLM 端点为 API 的教程,并与 AWS 进行比较

skanda-vivek.medium.com

第二种更为私密的选择是将 LLM 部署在本地的私人服务器上。与在云端托管或使用像 ChatGPT 这样的封闭源 API 不同,部署在本地需要公司投资建设自己的私人数据中心,并组建团队来管理这些数据中心。

注意到在上段中我提到本地 LLM 托管是更私密的选择,但没有谈到安全性。这是因为两者都有各自的安全优缺点。使用云服务的好处是云服务提供商有严格的安全标准。此外,云基础应用程序通常更具弹性,因为提供商在多个地点拥有冗余的数据中心。但也存在恶意攻击者利用云系统访问客户数据的风险。

另一方面,本地数据中心在物理安全性方面更有保障——如果离工作地点较近。然而,这些数据中心需要良好管理,如果没有合适的团队,安全漏洞可能会随处可见。此外,单一的本地数据中心代表了一个单点故障,缺乏备份。

收获

选择“正确”的 LLM 在很大程度上取决于应用和行业。我将这个选择分解为 4 个关键因素——质量、价格、延迟和隐私/安全。在你开始将 LLM 纳入工作流程时,这可以作为决定从哪个 LLM 开始的良好准则。不过,请注意,这些因素未来可能会有所变化。

新的语言模型(LLMs)可能会出现,这些模型在性能上远优于现有模型,并且成本更低。此外,数据需求也可能发生变化。最初你可能估计客户每天需要~1k 次请求,但未来可能会增加到~10k 次,在这种情况下,使用按使用量收费的闭源 API 可能不如使用低成本的私有托管开源 API 经济。

但如果你面临一种“鸡与蛋”的情况,因为不知道使用哪个模型而不愿开始,我建议不要在分析瘫痪中花费太多时间。Andrej Karpathy(前特斯拉 AI 总监,OpenAI 联合创始人)在他的GPT 状态演讲中提供了一些好的建议。他说,开始的最佳方式是使用像 ChatGPT 这样的现成 API,并通过提示工程/检索增强生成使其表现更好(我写了一篇相关文章):

## 使用检索增强生成构建特定行业的 LLMs

各组织正在竞相采用大型语言模型。让我们深入探讨如何构建特定行业的 LLMs…

towardsdatascience.com

如果这不起作用 — 尝试对你的数据进行开源模型微调(或者如果你有资源和勇气的话,从头开始训练!)。我会对展示或探索 LLM 在你所在行业中的潜在应用持这种观点。即使你知道 ChatGPT 可能不适合你的应用,你也可以在代表性样本数据上进行测试。一旦你对探索的可能性感到满意,你可以更换 LLM。这样,你可以开始进行概念验证,给利益相关者留下深刻印象,并产生影响!

我希望这对你在应用 LLM 创建出色产品的过程中有所帮助,并期待在评论中听到你的反馈!

如果你喜欢这篇文章,关注我 — 我会写关于生成式 AI 在现实世界中的应用,更多地是数据与社会交叉的内容。

随时在 LinkedIn 上与我联系!

如果你还不是 Medium 会员,并且想支持像我这样的作者,可以通过我的推荐链接注册: https://skanda-vivek.medium.com/membership

以下是一些相关文章:

何时应对 LLM 进行微调?

LLM 经济学:ChatGPT 与开源的对比

将开源 LLM 部署为 API

你如何构建一个 ChatGPT 驱动的应用?

提取式与生成式问答 — 哪种更适合你的业务?

对定制数据进行问答的 Transformer 模型微调

释放生成式 AI 对你客户的潜力

使用 Azure ML 部署 LLM

4 种易于实现的高影响力调整方法,用于超级提升你的 Python 代码性能

原文:towardsdatascience.com/4-easy-to-implement-high-impact-tweaks-for-supercharging-your-python-codes-performance-eb0652d942b7

如何检测、理解和消除 Python 中的瓶颈,以实现 1500 倍的速度提升

Mike HulsTowards Data Science Mike Huls

·发表于 Towards Data Science ·12 分钟阅读·2023 年 7 月 12 日

--

你在本文后的 Python 代码(图片来自 SpaceXUnsplash

我的理念是尝试简单的解决方案,然后再考虑复杂的方案。通过探索本文中的简单方法,你可能会找到所需的性能提升,从而避免实现和调试多进程、线程或其他语言编写的包所需的复杂性和无数小时。

在这篇文章中,我们将深入探讨使用最小化易于实现的技巧来加速任何 Python 代码的工具和 4 种方法。我们将分析代码,检测瓶颈并以结构化的方式解决这些问题。我们将通过减少 Python 的工作量来完成这一过程。

如果你必须尽可能快地跨越一定距离,你可以选择加速行驶或缩短路径。同样,与其让 Python 执行更多的操作,不如减少操作的数量。

最终,你将获得对代码性能的更深入理解,掌握代码分析的宝贵技能,以避免在开发过程中遇到瓶颈,并成为一个更好的开发者。让我们开始编码吧!

目录

我们将从三个方面分析我们的问题:

A 部分我们定义性能的含义并讨论分析器,接下来我们将使用它来测量我们的代码。

B 部分围绕使用工具定位瓶颈展开。我们测量我们的代码并发现性能瓶颈。我们使用实际例子来理解为何我们的函数性能不佳。

C 部分中,我们讨论了消除瓶颈的方法。在前面的部分中,我们学会了如何检测问题代码并分析它,以便了解减速的原因。在这一部分,我们讨论性能提升策略

  • 选择合适的数据结构

  • 消除运行缓慢的代码(例如嵌套循环)

  • 使用内置函数

最终你将能够将这些通用策略应用于任何代码。

A. 关于性能

让我们首先处理一些定义和准备工作。

1. 什么是性能?

由于我们正在尝试优化代码的性能,我们首先需要对性能的定义有一个清晰的认识:

性能:以所需时间、使用的资源等来估计完成的有用工作量

为了简洁起见,我选择了监控函数的执行速度。内存使用和大小也很重要,但我选择省略它以使文章稍短。

## 使用多阶段构建使你的 Docker 镜像缩小 10 倍

通过留下不必要的工具来清理你的 Docker 镜像

[towardsdatascience.com

2. 如何测量性能?

Python 有一个非常方便的内置工具叫做 cProfile。这个包提供了确定性分析Python 程序的功能。这意味着对所有函数调用、函数返回和异常事件之间的时间间隔进行了精确计时。这与统计分析不同,后者使用随机样本来推断时间花费的相对情况。

使用 cProfile 非常简单:

import cProfile
from typing import List

def uppercase_list(a_list:List) -> List:
  return [str(item).upper() for item in a_list]

cProfile.run(statement="uppercase_list(['hello', 'world', '!'])")

这会产生以下结果:

8 function calls in 0.000 seconds
Ordered by: standard name

ncalls  tottime  percall  cumtime  percall  filename:lineno(function)
    1    0.000    0.000    0.000    0.000   <string>:1(<module>)
    1    0.000    0.000    0.000    0.000   cprofile_example.py:6(uppercase_list)
    1    0.000    0.000    0.000    0.000   cprofile_example.py:8(<listcomp>)
    1    0.000    0.000    0.000    0.000   {built-in method builtins.exec}
    1    0.000    0.000    0.000    0.000   {method 'disable' of '_lsprof.Profiler' objects}
    3    0.000    0.000    0.000    0.000   {method 'upper' of 'str' objects}

对列的快速说明:

  • ncalls: 调用次数

  • tottime: 在给定函数中花费的总时间(不包括子函数)

  • percall: tottime / ncalls

  • cumtime: 在该函数及所有子函数中花费的累计时间

  • percall: cumtime / primitive calls

  • 最后一列: 位置 -> 文件名中的函数名和行号

如你所见,cProfile 为我们提供了一个易于使用的工具来检查我们代码的性能。现在让我们专注于如何实际提升我们的 Python 代码。

## 使用超级快的 Rust 代码在 3 个步骤中创建 Python 包

使用包含 Rust 代码的包来扩展你的 Python 代码,以实现 >150 倍的性能提升!

[towardsdatascience.com

B. 性能分析:发现和理解瓶颈

让我们从需要优化的代码开始。作为示例,我们将使用一个非常简单的函数:它接受两个列表,并返回两个列表中存在多少个不同的项(不区分大小写)

让我们看一下这个(故意设计得超不优化的)函数:

def duplicate_count_dumb(list_1:List, list_2:List) -> int:
  """ Returns the distinct number of items that are present in both provided lists (case-insensitive) """
  duplicates: List[str] = []
  for item1 in list_1:
    for item2 in list_2:
      if str(item1).upper() == str(item2).upper():
        if (str(item1).upper() not in duplicates):
          duplicates.append(str(item1).upper())
  return len(duplicates)

这个函数显然不是解决我们问题的智能方法,但它确实很好地演示了我们如何使用cProfile来发现瓶颈。

## Python 参数、关键字参数及所有其他传递参数的方式

熟练设计你的函数参数,提供 6 个示例

[towardsdatascience.com

1. 性能分析我们的函数

当我们在我们的函数上使用cProfile时,这里是当我们传递两个都包含 1000 个五字单词的列表时的结果:

2000008 function calls in 0.523 seconds
Ordered by: standard name

 ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      1    0.000    0.000    0.520    0.520 <string>:1(<module>)
      1    0.381    0.381    0.520    0.520 list_matching.py:43(duplicate_count_dumb)
      1    0.002    0.002    0.523    0.523 {built-in method builtins.exec}
      1    0.000    0.000    0.000    0.000 {built-in method builtins.len}
      1    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
      1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
2000002    0.139    0.000    0.139    0.000 {method 'upper' of 'str' objects}

注意我们在半秒多一点的时间内执行了略多于 200 万次的函数调用。让我们先将这段时间添加到我们的基准中,以便后续进行比较:

┌─Benchmark─────────┬───────────────┬───────────────┬───────────────┐
│ dupcount 1k items │      Min (ms) │      Max (ms) │      Avg (ms) │
├───────────────────┼───────────────┼───────────────┼───────────────┤
│ default           │ 520.405100000 │ 529.250500000 │ 523.529233333 │
└───────────────────┴───────────────┴───────────────┴───────────────┘

2. 发现瓶颈

最重要的一行是底部那一行;在这里我们看到我们调用了upper方法大约200 万次。这是因为我们将 list1 中的每个项与 list2 中的每个项进行比较。

两个项都需要字符串化转大写以便进行比较。由于两个列表各有 1000 项,这意味着我们必须进行 100 万次比较(1000x1000)。问题在于我们upper函数对 item1 和 item2 进行了 100 万次。

这意味着在最佳情况下,至少upper会被调用 200 万次。如果项匹配,还会有额外的项检查它们在duplicates列表中的存在性并添加它们(这就是为什么会有 2,000,002 次而不是 2,000,000 次)。

3. 为什么这么慢?

Python 因其易用性而受到喜爱,但这也带来了缺点。在底层,Python 的内存分配比其他编程语言如 C(Python 是用 C 编写的)要慢得多。

## 为什么 Python 这么慢以及如何加快速度

查看 Python 的瓶颈在哪里

[towardsdatascience.com

由于 Python 中的字符串是不可变的,在底层每次你uppercase 时都会创建一个新变量。所有这些变量都需要分配内存,而 Python 的内存分配相对较慢。当你这样做 200 万次时,你会开始注意到这种性能下降。了解更多关于 Python 设计的信息,可以阅读上面的文章或观看下面的演示:

C. 消除瓶颈

既然我们已经找到了瓶颈,现在是时候加速我们的代码了。记住我们的目标让 Python 做更少的工作

## Python 奇特之处:理解一个函数如何修改一个不返回任何东西的变量

深入探讨 Python 如何传递参数和可变性,以防止意外错误。

towardsdatascience.com

1. 让 Python 做更少的工作 — 理解你的代码

一个简单的减少操作的方法是在循环之前将每个项目大写,因为我们不在乎大小写:

def duplicate_count_smarter(list_1:List, list_2:List) -> int:
  """ Returns the distinct number of items that are present in both provided lists (case-insensitive) """
  duplicates: {str} = set()
  list_1 = [str(w).upper() for w in list_1]
  list_2 = [str(w).upper() for w in list_2]
  for item1 in list_1:
    for item2 in list_2:
      if item1 == item2:
        duplicates.add(item1)
  return len(duplicates)

正如你所见,我们首先对每个列表中的每个项目进行大写处理。当我们再次对代码进行性能分析时,我们发现这个简单的更改将函数调用的次数从大约 200 万减少到大约 2000。这是因为现在我们需要对 list1 list2 中的每个项目进行大写处理(1k + 1k = 2k)。

2007 function calls in 0.022 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    0.019    0.019 <string>:1(<module>)
    1    0.019    0.019    0.019    0.019 list_matching.py:74(duplicate_count_smarter)
    1    0.000    0.000    0.000    0.000 list_matching.py:77(<listcomp>)
    1    0.000    0.000    0.000    0.000 list_matching.py:78(<listcomp>)
    1    0.003    0.003    0.022    0.022 {built-in method builtins.exec}
    1    0.000    0.000    0.000    0.000 {built-in method builtins.len}
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
 2000    0.000    0.000    0.000    0.000 {method 'upper' of 'str' objects}

我们调用 upperstr 函数的次数减少了 1,000 倍,从而显著提升了性能;执行时间减少了 28 倍,从 523ms 降至 18ms:

┌─Benchmark─────────┬───────────────┬───────────────┬───────────────┐
│ dupcount 1k items │      Min (ms) │      Max (ms) │      Avg (ms) │
├───────────────────┼───────────────┼───────────────┼───────────────┤
│ default           │ 520.405100000 │ 529.250500000 │ 523.529233333 │
│ first_upper       │  18.440300000 │  18.665600000 │  18.548466667 │ (x28)
└───────────────────┴───────────────┴───────────────┴───────────────┘

另请注意,我将重复项列表替换为集合,这样我们就不需要检查项目是否已包含。使用集合将进一步减轻 Python 的负担,因为集合默认只包含唯一值。

## 参数与关键字参数:在 Python 中调用函数的最快方式是什么?

timeit 模块的清晰演示

towardsdatascience.com

2. 避免嵌套循环

另一个容易发现的瓶颈是我们对列表 1 中的每一项都遍历列表 2。如果两个列表都包含 10 项,这就需要进行 (10 x 10=) 100 次比较。如果两个列表都包含 1000 项,则需要进行 100 万次比较。你可以看到,比较次数相对于输入数量呈指数级增长,这会迅速加剧我们的性能问题。了解更多关于 大 O 符号的信息。

我们可以通过实现下面的代码来避免进行两次循环和比较项:

def duplicate_count_with_in(list_1:List, list_2:List) -> int:
  """ Returns the number of times a word exists in a list (case-insensitive) xxxx"""
  duplicates: {str} = set()
  list_1 = [str(w).upper() for w in list_1]
  list_2 = [str(w).upper() for w in list_2]
  for item1 in list_1:
    if item1 in list_2:
      duplicates.add(item1)
  return len(duplicates)

在这个示例中,我们只遍历 list_1 并检查 list_2 是否包含该值。让我们查看基准测试:

┌─Benchmark─────────┬───────────────┬───────────────┬───────────────┐
│ dupcount 1k items │      Min (ms) │      Max (ms) │      Avg (ms) │
├───────────────────┼───────────────┼───────────────┼───────────────┤
│ default           │ 520.405100000 │ 529.250500000 │ 523.529233333 │
│ first_upper       │  18.440300000 │  18.665600000 │  18.548466667 │ (x28)
│ with_in           │  11.774500000 │  12.401600000 │  12.183367000 │ (x43)     │
└───────────────────┴───────────────┴───────────────┴───────────────┘

通过两个简单的改动,我们显著提高了性能,但还有一种方法可以进一步提升性能。

3. 使用合适的数据结构——去重列表

为了节省更多工作,我们可以对两个列表进行去重,因为这可以减少需要执行的比较次数。我们将暂时绕道思考一个去重列表的函数。然后,我们将把它集成到我们的列表比较函数中。你可能想到的第一个函数可能如下所示:

def dedupper(the_list: List) -> List:
  """ Removes duplicates from list (case-insensitive) """
  return_list = []
  for entry in the_list:
    _entry = str(entry).upper()
    if (_entry not in return_list):
      return_list.append(_entry)
  return return_list

没有什么特别的;它只会遍历列表中的每一项,并将其放入另一个列表中,如果该项还不在其中。这个函数的大部分工作是在检查一个值是否存在于列表中以进行去重。如果我们使用一个100,000 个单词的列表,这个函数需要稍超过一分钟才能完成:

┌─Benchmark────────────────────┬──────────────┐
│ duplicate count (100,000x)   │     Avg (ms) │
├──────────────────────────────┼──────────────┤
│ dedup_dumb                   │ 61386.198600 │
└──────────────────────────────┴──────────────┘

使用 Python 多线程的 2 行代码

何时以及如何使用多个核心以更快的速度执行

towardsdatascience.com

让我们优化这个函数以使用集合:数据集只能包含唯一值。这样我们就不需要让 Python 检查一个值是否已经存在于集合中;现在由集合来处理这个问题。重写后,我们得到了一个更易读且性能更好的函数:

def dedupper_smart(the_list: List) -> List:
    """ Removes duplicates from list (case-insensitive) """
    return list({str(entry).upper() for entry in the_list})

如你所见,我们使用集合推导式遍历列表中的每一项。在完成后,我们得到一个仅包含列表中唯一大写值的集合。在返回之前,我们将其转换为列表。当我们将这个新函数与旧函数进行比较时,我们发现它快了 2,800 多倍!

┌─Benchmark────────────────────┬──────────────┐
│ duplicate count (100,000x)   │     Avg (ms) │
├──────────────────────────────┼──────────────┤
│ dedup_dumb                   │ 61386.19860  │
│ dedup_smart                  │    21.85930  │(x2,808)
└──────────────────────────────┴──────────────┘

集合之所以速度更快,是因为它们是用 C 语言实现的(Python 自身也是用 C 语言编写的)。由于内存也由 C 管理,这样更高效。此外,集合是无序的,并且只能包含唯一值,从而节省了大量的检查和排序操作。这再次将更多工作从 Python 手中移开提高了性能

让我们在列表匹配函数中实现集合并进行基准测试:

def duplicate_count_dedup_lists(list_1:List, list_2:List) -> int:
  """ Returns the number of times a word exists in a list (case-insensitive) xxxx"""
  duplicates: {str} = set()
  set_1= {str(entry).upper() for entry in list_1}
  set_2 = {str(entry).upper() for entry in list_2}
  for item1 in list_1:
    if item1 in list_2:
      duplicates.add(item1)
  return len(duplicates)

如你所见,现在列表首先会被去重。当我们进行基准测试时,看到 巨大的性能提升

┌─Benchmark─────────┬───────────────┬───────────────┬───────────────┐
│ dupcount 1k items │      Min (ms) │      Max (ms) │      Avg (ms) │
├───────────────────┼───────────────┼───────────────┼───────────────┤
│ default           │ 520.405100000 │ 529.250500000 │ 523.529233333 │
│ first_upper       │  18.440300000 │  18.665600000 │  18.548466667 │ (x28)
│ with_in           │  11.774500000 │  12.401600000 │  12.183367000 │ (x43)     │
│ dedup_lists_set   │   0.351700000 │   0.593000000 │   0.496767000 │ (x1,053)
└───────────────────┴───────────────┴───────────────┴───────────────┘

我们的函数在 半毫秒 内完成,而不是半秒钟。我们可能还有一个小窍门可以在下一部分使用。

## 5 个实际有用的 Python 装饰器用于分析/调试代码

将这些实用的通用装饰器直接应用到你的代码中

[towardsdatascience.com

4. 使用数据结构函数 — 集合交集

我们可以添加的最后一个优化是跳过循环,使用集合的 intersection 方法:

def duplicate_count_smartest(list_1:List, list_2:List) -> int:
  """ Returns the number of times a word exists in a list (case-insensitive) """
  set_1 = {str(entry).upper() for entry in list_1}
  set_2 = {str(entry).upper() for entry in list_2}
  return len(set_1.intersection(set_2))

如你所见,我们最终得到一个非常易读、简洁的函数。intersection 方法返回一个集合,该集合包含在 set_1set_2 中都存在的项。最后,我们返回这个集合的长度。让我们将其添加到基准测试中,看看最终结果如何:

┌─Benchmark─────────┬───────────────┬───────────────┬───────────────┐
│ dupcount 1k items │      Min (ms) │      Max (ms) │      Avg (ms) │
├───────────────────┼───────────────┼───────────────┼───────────────┤
│ default           │ 520.405100000 │ 529.250500000 │ 523.529233333 │
│ first_upper       │  18.440300000 │  18.665600000 │  18.548466667 │ (x28)
│ with_in           │  11.774500000 │  12.401600000 │  12.183367000 │ (x43)     │
│ dedup_lists_set   │   0.351700000 │   0.593000000 │   0.496767000 │ (x1,053)
│ sets              │   0.340600000 │   0.369600000 │   0.351167000 │ (x1,490)
└───────────────────┴───────────────┴───────────────┴───────────────┘

我们的最终函数不仅可读性大大提高,而且比起最初的版本快了几乎 1500 倍。

## 6 步骤使 Pandas Dataframe 操作速度提升 100 倍

Cython 数据科学:将 Pandas 与 Cython 结合,实现惊人的速度提升

[towardsdatascience.com

进一步优化?

也许你已经经历了这些步骤,但代码性能仍然不够理想。那么,你可以选择应用多进程线程或甚至用像CRustCython这样的更优化语言重新编写(部分)函数。本文的目的是不立即使用大招,而是首先挤出 Python 能提供的每一滴性能。如果需要进一步优化,本文中的方法将使其更快。

链接到文章 [## Python 中的高级多任务处理:在 6 种情况下应用和基准测试线程池和进程池

安全且轻松地将多任务处理应用到你的代码中

链接到 Data Science

结论

在这篇文章中,我们通过了一个非常实用的例子来识别理解消除性能瓶颈。我们已经看到,你不必立即对代码进行复杂的结构性更改;有时简单的小的、易于实施的更改可以大大优化你的代码。当这些还不够时,总是可以选择使用线程、多进程或在编译语言中编写 Python 包。

我认为最重要的部分是真正理解你的代码,理解 Python 如何工作以及在哪里性能较差,并应用正确的数据结构。为此,请查看我其他文章或这个演示文稿

我希望这篇文章像我希望的那样清晰,但如果不是这样,请告诉我我可以做些什么以进一步澄清。同时,请查看我关于各种编程相关主题的其他文章

快乐编程!

— Mike

附注:喜欢我做的事情吗? 关注我!

链接到 Medium 会员页面 [## 使用我的推荐链接加入 Medium — Mike Huls

阅读 Mike Huls(以及 Medium 上的其他成千上万名作者)的每一个故事。你的会员费直接支持 Mike…

链接到 Medium 会员页面

4 种简单方法立即提升你的数据可视化

原文:towardsdatascience.com/4-easy-ways-to-instantly-improve-your-data-visualisations-2a5fc3a22182

改善使用 Matplotlib 创建的图表的示例

Andy McDonaldTowards Data Science Andy McDonald

·发表于 Towards Data Science ·阅读时间 9 分钟·2023 年 7 月 21 日

--

Matplotlib 条形图在改变了单个条形的颜色以引起注意之后的效果。数据是随机生成的。图像由作者提供。

创建有效的数据可视化是许多学科中的关键技能,无论是商业分析、数据科学还是地球科学。美观且易于理解的数据可视化可以帮助激发目标受众的想法,或促使他们根据我们展示的信息采取行动。

在 Python 世界中,有几个数据可视化库可供使用。然而,许多 Python 和数据科学的学习者都是从 matplotlib 开始的。

Matplotlib 提供了一种多功能的方法来呈现你的数据。 在我之前的文章中,我展示了各种数据可视化,这些可视化在默认图表的基础上更进一步。

使用 matplotlib 创建的径向条形图示例。 图像由作者提供。

然而,创建这些图形确实需要耐心和额外的 Python 代码。这通常会导致在 StackOverflow 或通过库文档进行大量搜索,以寻找修改图形中最小部分的可能解决方案。

通过遵循一些简单的指南,我们可以立即改善任何使用 matplotlib 创建的图形。

在这篇文章中,我分享了四条我在创建用于分享的图形(无论是在 Medium 还是在学术出版物中)时常用的指南。

这些指南不仅限于 matplotlib;它们同样适用于任何允许你创建图表的软件,如 Excel 或 Tableau。

去除图表垃圾并保持简单

改善 matplotlib 图表的最快和最简单的方法之一是减少显示的“图表垃圾”量。

图表垃圾指的是那些在图表上不必要和令人困惑的元素,它们实际上并没有增加读者或展示数据的价值。

在构建图表时,你应该确保只包含那些有助于读者更好理解数据的元素。

这里有一些可以让你的图表更清晰的建议:

  • 适当但有效地使用标题和标签

  • 避免复杂的词汇和术语

  • 移除不必要的网格线和边框

  • 移除背景图片

  • 避免过于华丽的字体

  • 避免使用不必要的特殊效果,如 3D 效果和阴影

作为一个例子,我们有下图,用于说明收入与年龄的关系。这个图表有几个元素使得阅读和解释变得困难,比如网格线、点标签以及背景和点之间的颜色冲突。

一个杂乱的散点图,包含大量图表垃圾——数据随机生成。图片由作者提供。

如果我们花一点时间去除这些不必要的图表垃圾,我们可能会得到如下图表。

这使得图表看起来更加整洁且易于解读。我们还添加了线性回归线,这可以帮助显示数据的整体趋势。

去除图表垃圾后的散点图——数据随机生成。图片由作者提供。

这是另一个例子,展示了去除多余图表垃圾的过程。

创建有效图表的关键之一是让数据本身说话。这意味着优先考虑数据的展示方式,超过图表上的其他元素,以清晰地传达数据分析的预期见解。

选择适当的颜色

选择图表的适当颜色可能看起来是一个简单的任务。然而,它很快可能成为一个巨大的时间消耗点,你可能会花费几个小时来决定合适的蓝色阴影或是否存在足够的颜色对比。

选择正确且最合适的颜色可以极大地影响图表的可读性,从而影响读者对数据的解读。

例如,在下图中,我们有五个不同的类别,都用单一颜色表示。虽然图表可读,但由于颜色使用不够有效,我们没有吸引读者关注数据的任何特定方面。

基本的 matplotlib 柱状图,颜色一致。数据随机生成。图片由作者提供。

如果我们将颜色更改为让类别 C 突出显示为橙色,我们立即抓住读者的注意力,并暗示这个特定类别很重要。

Matplotlib 条形图在更改单个条形颜色以吸引注意力后。数据随机生成。图像由作者提供。

另一方面,如果我们采取相反的方法,为每个类别使用随机颜色,那么最终可能会得到一个繁忙且令人困惑的图表。然而,也有可能你希望为每个条形使用不同的颜色,例如,当区分不同公司品牌时。

Matplotlib 条形图在使用过多颜色时,使整体图表难以阅读。数据随机生成。图像由作者提供。

在选择颜色时,有许多细微差别,这些差别将取决于许多因素,包括图表类型、数据和你想传达的信息。

然而,有一些通用规则可以帮助使你的图表看起来更加精致和专业:

  • 使用颜色突出信息,而不是分散注意力: 颜色应当用于吸引对数据中最关键部分的关注。

  • 保持一致性: 在创建多个可视化时,保持一致性有助于观众快速理解基于以往经验的新可视化。例如,如果在一个图表中使用蓝色表示某个类别,那么在其他图表中也尽量使用相同的颜色表示该类别。

  • 考虑颜色视觉问题/色盲: 在创建图表时,考虑色盲人士是非常重要的。例如,避免使用已知存在问题的颜色,如红色和绿色或蓝色和黄色。

  • 理解颜色心理学: 颜色背后的含义可能具有重要的影响,并且在不同文化中可能会有所不同。例如,红色通常被视为负面颜色或危险的警告,而绿色被视为积极的或增长的指示符。

我强烈推荐查看以下文章,以便更深入地了解颜色选择:

还有许多颜色调色板生成器可以帮助选择最合适的调色板。通过使用这些工具,你可以节省大量时间,并确保最大限度地提高可读性,尤其是对于有颜色视觉问题的人群。

这里有一些你应该查看的资源:

通过应用 Matplotlib 主题节省时间和代码

如果你是我文章的常客,你会发现我在最近几个月中介绍了几个 matplotlib 主题库。这些主题库使你能够立即将图形从 matplotlib 的无聊标准色彩方案转变为更加美观的形式。

这些不仅有助于改善图形的外观,还有助于提高解释性。

有许多 matplotlib 主题库可供选择,包括 mplcyberpunk,它可以将你的 matplotlib 图形转换为带有发光霓虹色的未来感图表。

例如,为了创建一个 Cyberpunk 主题的图像,我们可以使用以下代码:mplcyberpunk

import mplcyberpunk
import numpy as np

# Generate x values
x = np.linspace(0, 10, 20)

# Generate y values
y = np.sin(x)
y2 = np.cos(x)

plt.style.use('cyberpunk')
plt.figure(figsize = (8,8))

plt.plot(x, y, marker = 'o')
plt.plot(x, y2, marker = 'o', c='lime')

mplcyberpunk.add_gradient_fill(alpha_gradientglow=0.5, gradient_start='zero')

plt.xlabel('X-Axis')
plt.ylabel('Y-Axis')

plt.show()

要生成以下图像:

示例线图,带有渐变填充,由 CyberPunk matplotlib 主题生成。图片由作者提供。

尽管 mplcyberpunk 主题可以创建引人注目的图形,但应谨慎使用。它可能会被一些人视为不专业,并且可能会遮掩数据和你想传达的信息。

如果你在寻找适合纳入学术出版物的内容,那么 SciencePlots 库可能会引起你的兴趣。

SciencePlots 库包含了众多样式,方便你在撰写科学或期刊文章时设置图表。它还支持多种语言,包括中文和日语。

例如,下面的图显示了我们在 Cyberpunk 主题图表中使用的相同数据在适合纳入学术期刊文章的形式下的表现。

应用科学图表 IEEE 风格后的 Matplotlib 线图。图片由作者提供。

你可以在我下面的文章中了解更多关于 SciencePlots 库的信息:

## 使用 scienceplots 和 matplotlib 轻松创建科学图表

使用几行 Python 代码立即转换你的 Matplotlib 图形

[towardsdatascience.com

如果你对我上面提到的一些常见主题库的概述感兴趣,你可以查看我下面的其他文章。

## 升级您的数据可视化:4 个 Python 库来增强您的 Matplotlib 图表

使用这些易于使用的 Matplotlib 样式库来改进您的数据可视化

towardsdatascience.com

考虑您的观众和您讲述的故事

在创建数据可视化时,最重要的一点是考虑你的观众是谁以及你想讲述的故事是什么。

与其向用户展示大量复杂且混乱的图表,不如将数据和信息提炼成最相关的部分。这将取决于数据分析的目标,这可能由客户、研究项目或事件组织者定义。

此外,我们还需要考虑观众的背景。

他们是技术导向的高数据素养人员,还是我们的可视化需要简化以适应更广泛的非技术观众?

例如,如果我们被要求展示来自挪威大陆架一系列井的 Hugin 组的平均孔隙率值信息。

我们的第一次尝试可能是这样的:

fig, ax = plt.subplots(figsize=(8,8))

bars = plt.barh(df['well'], df['porosity'])

plt.show()

这将创建以下条形图。

基本的 matplotlib 水平条形图。数据来自 Xeek / Force 2020. 图片由作者提供。

然而,当读者查看此图时,他们必须做很多工作才能弄清楚发生了什么。

他们会问:

  • 条形图代表什么?

  • 哪口井的孔隙率最高?

  • 井 16/10–1 和井 25/8–7 之间有差异吗?

  • 哪些井被认为具有“良好”的孔隙率?

尝试回答这些问题需要相当大的努力,大多数人会跳过图形直接继续。

如果我们尝试改进图形并在不让读者费力的情况下回答这些问题,我们可以得到如下结果。

对图形进行几次调整后的水平条形图,以向读者讲述特定的故事。数据来自 Xeek / Force 2020. 图片由作者提供。

立即,我们可以回答这些问题:

  • 条形图代表什么?

    Hugin 组的平均孔隙率值

  • 哪口井的孔隙率最高?

    25/8–7,孔隙率为 26.1%

  • 井 16/10–1 和井 25/8–7 之间有差异吗?

    是的,差异为 0.1%

  • 哪些井被认为具有“良好”的孔隙率?

    橙色突出显示的井在 20% 截断值以上

我们通过简化图形并改善美学,使读者的工作变得更轻松。

我们还可以通过突出显示一根柱子来改变图形的叙事。例如,下面的图表可能是关于 well 16/2–16 的更大信息图的一部分,通过突出显示该柱子,我们立即引起注意。

应用颜色突出显示单根柱子的 Matplotlib 水平条形图,以吸引读者的注意。数据来源于 Xeek / Force 2020. 图片由作者提供。

摘要

创建有效的数据可视化是一项高质量的技能,特别是如果你涉及数据科学或数据分析。

在本文中,我分享了四个我最喜欢的创建有效可视化的指导原则。还有许多其他方法可以用来改进图形。

我很想在本文的评论区了解你创建有效数据可视化的最爱规则。

本文使用的数据集

作为 Xeek 和 FORCE 2020 机器学习竞赛的一部分使用的训练数据集 (Bormann et al., 2020)。该数据集依据知识共享署名 4.0 国际许可协议授权。

完整的数据集可以通过以下链接访问: doi.org/10.5281/zenodo.4351155

感谢阅读。在离开之前,你一定要订阅我的内容,将我的文章直接送到你的邮箱。 你可以在这里做到这一点!

其次,你可以通过注册会员获得完整的 Medium 体验,并支持我和其他数千名作家。只需每月$5,你便可以完全访问所有精彩的 Medium 文章,并有机会通过写作赚钱。

如果你通过 我的链接注册你将通过你的一部分费用直接支持我,这不会额外增加你的开支。如果你这样做,非常感谢你的支持。

4 个提高数据可视化技能的必备资源

原文:towardsdatascience.com/4-essential-resources-to-help-improve-your-data-visualisations-8151e63ce8f0

帮助你创建有效且引人注目的数据可视化的工具

安迪·麦当劳Towards Data Science 安迪·麦当劳

·发表于 Towards Data Science ·阅读时间 7 分钟·2023 年 8 月 12 日

--

图片来源:Choong Deng XiangUnsplash

创建有效的数据可视化对任何数据科学家、地球科学家或岩石物理学家来说都至关重要。通过学习这些技能,我们可以确保能够向目标受众传达我们的研究和分析结果。通过了解我们的受众,我们可以相应地调整图表和信息,例如,我们可以为公众提供更简化和艺术化的图表,而为在会议上与同行展示时提供更复杂的图表。

发展数据可视化技能可能需要时间和练习;然而,互联网上有许多免费的资源可以帮助你完成这一过程。

这些资源涵盖了从解释数据可视化最佳实践到选择适当的配色方案,并为你的下一个可视化提供灵感。

在本文中,我分享了四个我最喜欢的数据可视化资源,你可能会发现它们有用且有趣。

我与以下列出的任何资源没有关联——我只是想分享各种工具,以帮助同行数据科学家提高他们的数据可视化技能。

学习有效数据可视化的注意事项与禁忌 — 99 条数据可视化规则

制作有效的图表涉及将数据和信息尽可能简化。这包括避免使用 3D 饼图、不在柱状图上展示过多的条形图,以及避免使用过多的颜色来表示数据。

然而,这些规则有时应作为指导,因为可能会有需要偏离这些规则以实现所需效果的情况。

这就是 99 Data Viz Rules Project 的作用。它是由总部位于英国的数字机构 addtwo digital 开发的项目,专注于通过数据可视化帮助人们更有效地沟通,提供讲座和研讨会。

因为一个学生提出了关于创建数据可视化时跟随的检查清单的问题,这家公司开发了 “99 Data Viz Rules and Why It’s OK To Break Them” 系列。

这包括创建数据可视化时常用的一些规则,并展示了如何以及何时打破这些规则以创建更具吸引力的可视化效果。此外,作者还提供了展示数据的合适替代方案。

下面是如何安排饼图中的切片以影响其直观性的示例。图片来源于 addtwodigitial.com

如果你遇到数据可视化规则,问问自己你希望数据可视化达到什么效果,你的受众是谁,以及你想传达什么信息。

同时,查看下面 AddTwo 的系列,这些系列文档编写得非常好,且易于探索。

[## 99 条数据可视化规则及为什么可以打破它们 - AddTwo

迄今为止的所有规则 - 由 Adam Frost 整理 我们是一家总部位于英国的数字机构,在过去的十年里……

www.addtwodigital.com

使用 Adobe Leonardo 选择颜色调色板,让你的数据更突出

选择数据可视化的颜色有时可能会非常耗时。然而,确保选择正确且最合适的配色方案对创建引人注目且易读的数据可视化至关重要。

在为我们的数据可视化选择合适的颜色调色板时,我们必须考虑各种因素。这包括确保所选颜色之间有适当的对比度,并确保我们的调色板适合色盲和相关问题的读者。

了解这些各种因素将确保我们尽可能有效地将信息传达给受众。

我在之前的文章中介绍了许多颜色调色板工具。

## 选择数据可视化颜色调色板的 4 个必要工具

使选择数据可视化颜色调色板变得简单的工具

[towardsdatascience.com

然而,在我讨论过的选项中,当我需要选择颜色调色板时,我总是发现自己回到Adobe 的 Leonardo

这是一个全面的工具,允许你分析颜色空间中的颜色、对比度和可及性。

当你访问该网站时,你可以选择专注于用户界面设计、数据可视化或访问工具来比较和评估颜色。

Adobe Leonardo 中的可用工具。图片由作者提供。

由于本文重点是数据可视化,最佳选择是访问颜色比例选项。

当颜色比例页面出现时,我们可以使用三种数据可视化工具:热力图、散点图和地图,来可视化和自定义我们的颜色调色板。这使你可以感受所选颜色调色板在这些可视化工具中的显示效果。

选择数据可视化颜色调色板时的 Leonardo 实践。图片由作者提供。

要了解如何有效使用 Leonardo,我强烈推荐查看Nate Baldwin的这篇文章。

## 在 Leonardo 中的数据可视化颜色比例

为数据可视化创建自定义颜色比例有时可能是一项负担。借助 Leonardocolor.io,这变得…

medium.com

寻找将数据可视化提升到新水平的灵感——信息之美

有时,为科学研究和分析创建数据可视化涉及创建详细的图表,以帮助你理解数据并从中得出关键发现。

然而,当向不同的观众展示这些数据时,比如公众,他们对你的主题了解有限时,你必须完全不同地思考如何呈现数据。

这可能是一项令人生畏的任务,特别是如果你习惯于为同事或学业创建科学图表时。

当我需要灵感时,转向专注于制作强大、美丽和有影响力的数据可视化的网站对我很有帮助。即使你不需要灵感,查看数据如何变成艺术也是很棒的。

信息之美网站由大卫·麦坎德利斯运营,展示了各种超越基本散点图和条形图的数据可视化。

例如,大卫将简单的国家人口数据转换成了这种互动且独特的数据可视化。

显示世界不同地区国家之间人口差异的图表。图像由大卫·麦坎德利斯创建,数据来源于联合国。

查看下面的信息之美网站,获取更多类似的图表。

[## 信息之美

将世界的数据、信息和知识提炼成美丽的信息图和可视化

informationisbeautiful.net](https://informationisbeautiful.net/?source=post_page-----8151e63ce8f0--------------------------------)

解锁 matplotlib 的全部潜力以获得惊艳的可视化效果——matplotlib 文档

Matplotlib 是我最喜欢和使用最频繁的 Python 数据可视化库之一。然而,它确实有创建无聊图形和难以使用的声誉。

但通过一点额外的工作和一些额外的 Python 代码,你可以轻松地将一些基本的 matplotlib 生成的图形转化为显著更好的效果。

用 matplotlib 生成的径向条形图。图像由作者提供。

在 matplotlib 中,了解哪些图表或样式是可能的可能会很困难;不过,我发现matplotlib 的文档非常值得一看且易于理解。

当你访问文档中的图表类型页面时,你会看到不同图表类型的视觉效果,如果你忘记了特定图表的名称,这非常方便。

关于不同可用图表类型的 matplotlib 文档。图像来自matplotlib 文档

此外,matplotlib团队整理了一个画廊页面,你可以在其中浏览不同的图表类型和样式。画廊中的每个项目都附带了完整的代码示例,你可以使用这些示例进行重现和修改以适应你的需求。

使用 matplotlib 生成的不同图表和效果的示例。图片来自matplotlib 图片库。

把这个页面看作一个巨大的备忘单,可以帮助你在选择图表、样式、标记等时使用。请查看下面的链接。

## Matplotlib 文档 - Matplotlib 3.7.2 文档

Matplotlib 是一个全面的库,用于在 Python 中创建静态、动画和交互式可视化。学习…

matplotlib.org

总结

在这篇文章中,我们看到了四个不同但非常有用的资源,可以帮助你改变创建数据可视化的方式。这些资源包括改进配色方案和从他人那里获得灵感。

如果你有遇到任何你经常使用并想要分享的资源,欢迎在评论区告诉我们。

感谢阅读。在离开之前,你一定要订阅我的内容,获取我发送到邮箱的文章。 你可以在这里完成订阅!

其次,你可以通过注册会员获得完整的 Medium 体验,并支持成千上万的其他作家和我。只需每月$5,你就可以完全访问所有精彩的 Medium 文章,并有机会通过写作赚取收入。

如果你通过 我的链接注册, 你将直接用部分费用支持我,这不会增加你的开支。如果你这样做,非常感谢你的支持。

作为 Python 初学者,你必须掌握的 4 个关键技巧

原文:towardsdatascience.com/4-essential-techniques-you-must-learn-as-a-python-beginner-84a64ece3461

以及如何使用它们,以便你能轻松应对下一个面试

Murtaza AliTowards Data Science Murtaza Ali

·发表于Towards Data Science ·阅读时间 8 分钟·2023 年 2 月 9 日

--

照片由Carl Heyerdahl提供,来自Unsplash

Lambda 函数

假设你在 Jupyter notebook 中处理一些数据,只是进行一些快速探索和分析。你仍处于数据清理和处理的早期阶段,离任何生产就绪的模型、可视化或应用程序还有很远。但你有一个截止日期要赶,所以你快速高效地进行探索,充分发挥你出色的 Python 技能。

在你冒险的过程中,你遇到了需要转换的数据列。你只需要对该列中的数字进行平方。这并不复杂,但不幸的是,它也是一种奇怪的需求,简单到可以快速完成,但复杂到没有内置函数。

所以,你决定使用Pandas’s [apply](https://pandas.pydata.org/docs/reference/api/pandas.Series.apply.html) 函数来转换数据 列,使用你自己的自定义函数[1]。为此,你需要编写一个平方数字的函数,你以唯一的方式知道如何做到这一点:

def square(num):
    return num * num

这样可以完成任务,但有点烦人且杂乱,尤其是在 Jupyter notebook 中。它与大多数 Pandas 操作的单行结构不太匹配,因此在同事审阅你的 notebook 时,可能看起来不是很好。

但不要灰心,我的朋友,因为 lambda 函数来拯救你了。Lambda 函数——或更一般地说,匿名函数——提供了一种在 Python 中定义函数的替代方法。而最棒的是,你可以用一行代码来编写它们!这一点通过示例最为明显:

square = lambda num: num * num

上面的代码与我们之前定义的这个函数是相同的。这里有几点你应该知道的:

  • lambda 关键字类似于 def 关键字,让 Python 知道我们想要定义一个函数。

  • lambda 函数的参数在冒号的左边,返回值在冒号的右边(实际上不使用 return 关键字)。

  • 我们不需要给函数起名字,但如果我们愿意,可以通过变量赋值来做到这一点。

最后一点是关键。使用 lambda 函数让我们可以定义一个函数,并调用它或将其作为参数传递给另一个函数,而无需给它一个名称。让我们通过回到之前的例子并使其具体化来说明这一点。

让我们想象一下,我们有如下的 DataFrame my_df,其中包含三个人的薪资:

 Name  Salary
0   John   45000
1   Mary   60000
2  Julie  100000

在这个极度理想化的世界里,雇主刚刚宣布大家的薪水将被平方。我们可以通过使用 lambda 函数在一行中更新我们的 DataFrame:

>>> my_df['Salary'] = my_df['Salary'].apply(lambda num: num * num)
>>> my_df
    Name       Salary
0   John   2025000000
1   Mary   3600000000
2  Julie  10000000000

于是,财富无尽!也许有点戏剧化,但希望你现在能记住什么是 lambda 函数以及如何使用它们。如果你想更详细地讨论它们的细微差别,我建议查看我详细讨论 lambda 函数的两部分文章系列。

在这篇文章中,我们要进入下一个话题。

列表推导式

如果你在学习 Python 的早期阶段,可能还没听说过列表推导式,但我敢说你可能听说过 for 循环。你知道的,那种让你遍历序列并按自己的意愿操作的东西:

my_lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
my_new_lst = list() # Make an empty list
for item in my_lst:
    if item % 2 == 0: # checking if even
        my_new_lst.append(item)

# This will give us a new list with only the even numbers

在上面的例子中,我们取了一组数字,只保留了偶数。代码足够简单——但因为这是 Python,我们喜欢简单——我们可以让它变得更加优雅。

如何做?进入列表推导式。列表推导式是一种 Python 语法,它有效地让你在一行中运行一个 for 循环,并将所有生成的元素放入一个新构建的列表中。这在抽象的术语中有点混乱,但通过示例会更容易理解。以下是使用列表推导式实现与上面相同的效果的方法:

my_lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
my_new_lst = [item for item in my_lst if item % 2 == 0]

这段代码基本上只是将一个 for 循环压缩到了一行。你仍然有 for 关键字告诉 Python 遍历原始列表,开头的 item 只是告诉 Python 我们想要包含在最终列表中的内容。在这种情况下,我们只想包含原始项,但只有在它通过偶数筛选器时。

更重要的是,你还可以在过滤后应用额外的转换。下面的代码还会在将结果放入最终列表之前对其进行平方操作:

my_lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
my_new_lst = [(item * item) for item in my_lst if item % 2 == 0]

# Final list will be [4, 16, 36, 64, 200]

你可以看到列表推导式如何派上用场。如果你选择使用它们,这里有一些重要的点需要记住:

  • 过滤器总是会在映射转换之前应用。在上面的代码中,过滤器检查偶数,而映射转换会对原始项进行平方操作。

  • 像任何 for 循环一样,列表推导式可以用于迭代任何可迭代序列,而不仅仅是列表。那它为什么叫做列表推导式呢?因为它总是生成一个列表作为最终结果。

  • 最后,请记住,虽然列表推导式通常可以使你的代码更简洁,但并不是总是必要的。一个好的经验法则是,只有在它们确实使你的代码更具可读性时才使用它们。记住,可读性不一定是长度的函数——在传统的 for 循环中容易理解的代码在列表推导式中可能会变得杂乱和混乱。

要了解更多关于列表推导式的信息,你可以查看我关于它们的两部分系列文章。

继续和中断

Python 编程语言(以及其他编程语言)中有两个有用但偶尔被忽视的特性就是continuebreak关键字。

让我们先定义每个关键字的作用,然后看几个具体的例子,并讨论你可能想使用它们的原因。

continuebreak都是可以在循环内部执行的操作。它们的效果类似,但本质上不同。Continue 可以立即停止当前的循环运行并跳到下一次运行,而break 用于完全退出循环

和大多数编程相关概念一样,这个概念最好通过例子来理解。仔细研究下面的代码片段。

# Continue example
>>> for i in range(10):
...     if i == 5:
...         continue
...     print(i)
...
0
1
2
3
4
6
7
8
9

-------------------------------------------------------------------------------
# Break example 

>>> for i in range(10):
...     if i == 5:
...         break
...     print(i)
...
0
1
2
3
4

你看出区别了吗?当我们使用continue时,我们只是跳过了5,因为 Python 立即进入循环的下一次迭代,因此永远不会看到5print语句。而当我们使用break时,Python 完全退出循环,所以在我们遇到4之后不会再打印任何东西。

此外,值得注意的是,虽然我们在上面的例子中使用了 for 循环,但continuebreak在 while 循环中同样有效。

下面是几个你可能需要使用这些迷人小关键字的情况:

  • 如果你需要无限循环直到满足某些条件,通常做法是将breakwhile True:结合使用。通常,while True:会导致无限循环,但将其与break配合使用可以绕过这一点。

  • 假设你正在循环处理实时传输的数据(如果要技术一点,可以称之为)。如果你想忽略一些数据但继续循环处理其余的数据,那么continue将是一个非常有用的关键字。

那么,让我们继续吧。

获取用户输入

有人可能会争辩说这不是 Python 中的一个必备技术,但在编程中确实是。但由于 Python 是编程的一部分,而它也是我假设你学习的语言,因为你已经读到了我的文章的这一部分,我认为这个话题是合理的。

如果你对这一点不熟悉,收集用户输入只是一个花哨的编程术语,用于让使用你的程序或应用程序的人通过(通常是)输入他们自己的字符来与之互动。在 Python 中,这一功能通过input函数访问。你可以在实时解释器会话中尝试一下:

>>> input()
Hi I am a person
'Hi I am a person'

这里发生了三件事:

  • 首先,我只是调用了input函数

  • 这导致光标移动到下一行。此时,我能够输入Hi I am a person。我们知道 Python 期待在这一行有用户输入,因为即使这不是有效的 Python 代码,也没有出错。我们还可以看到解释器提示符(>>>)在这一行没有出现。

  • 最后一行只是将用户输入的内容打印回终端。通常,这不会发生,因为你可能会希望将用户输入保存到一个变量中,以便以后使用。

我知道上面的例子有点模糊,所以这里有一个实际程序中如何使用这个功能的代码示例。我们通常也可以将字符串传递给input函数,这将用于提示用户输入一些内容。

# Writing code that delivers a survey

user_response = input("Did you find this product useful? Please answer yes or no")
if user_response == "yes":
    print("Excellent! We are happy we could be of service.")
elif user_response == "no":
    print("Ah, we're sorry to hear that. We'll do better.")

就这样!现在你知道如何直接与使用你精彩程序的人进行互动了。还有一件事我应该提到:所有通过用户输入读取的内容都被视为字符串。因此,请记得显式地使用intfloat转换数字,以免遇到一些非常恼人的错误!

最后的想法和总结

这是你的 Python 初学者备忘单(第二部分):

  1. 通过使用一行的lambda 函数来编写更好的 Pandas 代码(或者说代码的一般,但尤其是 Pandas 代码)。

  2. 认识一下 for 循环的“表亲”——列表推导式。这是一个有用的、常被忽视的特性,可以优化你的代码质量。

  3. 利用**break****continue**最大化你的循环使用。

  4. 大量程序将要求你使用用户输入,所以你最好对它感到熟悉。

Python 是一种丰富且充满特性的语言,所有这些特性和宝藏共同旨在使成为更好的程序员。

所以学好它,掌握它。

想在 Python 中出类拔萃? 在这里获取我简单易读的指南的独家免费访问权限。想在 Medium 上阅读无限制的故事?使用下面我的推荐链接注册吧!

[## Murtaza Ali - Medium

阅读来自 Murtaza Ali 在 Medium 上的文章。他是华盛顿大学的博士生,关注人机交互…

murtaza5152-ali.medium.com

参考资料

[1] pandas.pydata.org/docs/reference/api/pandas.Series.apply.html

选择数据可视化颜色调色板的 4 个必备工具

原文:towardsdatascience.com/4-essential-tools-to-help-you-select-a-colour-palette-for-your-data-visualisation-ddb2ec92a08

让颜色调色板选择变得简单的工具

Andy McDonaldTowards Data Science Andy McDonald

·发表于 Towards Data Science ·阅读时间 5 分钟·2023 年 3 月 15 日

--

照片由Daniele Levis Pelusi拍摄,来源于Unsplash

有时,最小的事情可能比我们想象的花费更多时间。在构建数据可视化时,我们会遇到需要决定颜色方案的阶段——这看起来简单但非常重要,可能会让你的数据可视化效果大相径庭。在这一阶段,我们经常会花费几个小时来选择合适的蓝色或检查颜色之间是否有足够的对比度。

在选择数据可视化的合适颜色调色板时,我们需要考虑许多因素,包括可视化的类型、受众、颜色对比、色盲等。了解这些不同的因素可以确保我们通过数据可视化讲述的故事能够以最佳方式传达给读者。

网上有许多工具可以帮助简化颜色选择过程。在这篇文章中,我们将介绍四个强大的工具,你可以将它们添加到数据科学家/分析师的工具箱中。

Coolors.co

Coolors.co 提供了一个非常易于使用的工具,允许你为项目生成颜色方案。尽管它主要针对品牌设计,但也可以轻松用于数据可视化。

在应用程序中,你只需按空格键即可生成新的调色板。你可以重复此操作直到找到你喜欢的调色板。如果你发现喜欢某种颜色但不喜欢其他颜色,你可以锁定那种颜色,按空格键重新生成其余颜色。

在免费版本中,你只能生成包含五种颜色的调色板,并且可用的工具也有限,例如不能使用颜色理论规则。

Coolors.co 用于生成数据可视化和其他项目的颜色方案。图片由作者提供。

除了生成调色板,你还可以浏览其他用户生成并与颜色社区分享的调色板。这允许你看到当前的趋势和热门颜色。

ColorBrewer 2.0

如果你计划在数据可视化中使用地图,你可能需要查看 ColorBrewer 2.0。这个免费的在线工具由宾夕法尼亚州立大学的辛西娅·布鲁尔教授设计和开发,旨在为参与制图的任何人创建引人注目且易于访问的颜色地图。

你可以选择从顺序、发散和定性颜色方案中挑选调色板。选择哪一种将取决于你计划在数据可视化中展示什么内容。

由于它是考虑到制图师设计的,你可以在地图的背景下预览每个可用的颜色调色板。

当你将颜色调色板并排查看时,可能很容易区分每种颜色。然而,在处理地图时,调色板上不相邻的颜色可能在地图上相邻。因此,了解你选择的调色板是否适合你的地图数据是很重要的。

ColorBrewer 用于为地图数据选择颜色调色板。图片由作者提供。

Paletton

Paletton 是一个免费的工具,帮助你为设计、可视化和演示选择颜色调色板。当你访问该网站时,你会看到一个互动的颜色轮,提供了各种选项。

刚开始时,最好有一个你希望使用的基础颜色。例如,这可能基于你或你工作的公司的现有品牌。然后你可以探索不同的颜色关系,包括单色、邻近、三色和四色。这些都会改变你选择的颜色调色板的感觉。

Paletton 的一个优点是可以预览你选择的颜色方案应用到一个简单的网站或图形上。这帮助你了解所选颜色调色板的效果。此外,它还提供了模拟色盲的选项,这对于确保你的可视化对所有人都可及是至关重要的。

Paletton 用于选择颜色调色板。图片由作者提供。

Leonardo

Adobe 的 Leonardo 是一个很棒的开源工具,允许设计师、数据科学家和工程师选择、可视化和分析选定的颜色调色板。

Leonardo 似乎是我查看过的用于生成颜色调色板的工具中最全面和技术性的。它允许您分析颜色空间中的颜色、对比度和可访问性。

当您访问网站时,您可以选择专注于用户界面的颜色设计、数据可视化,或使用工具来比较和评估颜色。

Adobe Leonardo 中的可用工具。图片由作者提供。

在“颜色尺度”部分,我们可以在三个关键数据图表上可视化顺序、发散和定性方案的颜色调色板:热图、散点图和地图。这提供了一个清晰的指示,说明您的可视化与所选方案的效果如何。

Leonardo 在为数据可视化选择颜色调色板时的实际应用。图片由作者提供。

若想深入了解 Leonardo,请查看以下文章:

## 使用 Leonardo 创建基于对比的主题

对 Leonardo 的功能增强,Adobe 的开源工具,用于创建美观、可访问和自适应的颜色…

uxdesign.cc [## 使用 Leonardo 创建基于对比的主题

创建用于数据可视化的自定义颜色尺度有时可能会很繁琐。使用 Leonardocolor.io,它变得更加轻松…

medium.com [## 数据可视化中的颜色尺度

摘要

在这篇文章中,我们介绍了四种强大的工具,可以帮助您在最终确定数据可视化之前选择和分析您所选择的颜色调色板。这些工具将为您的工具集提供很好的补充,并可以加快选择适当颜色的过程,使其对所有人都易于访问。

感谢阅读。在您离开之前,您一定要订阅我的内容,将我的文章发送到您的收件箱中。 您可以在这里做到这一点!或者,您也可以 注册我的通讯 以便免费将更多内容直接发送到您的收件箱。

其次,您可以通过注册会员来获得完整的 Medium 体验,并支持我和其他成千上万的作者。只需每月 $5,您即可完全访问所有精彩的 Medium 文章,并有机会通过写作赚取收入。如果您使用 我的链接, 您将直接通过您的费用的一部分支持我,而且不会增加额外费用。如果您这样做,非常感谢您的支持!

4 种更快的 Pandas 数据分析替代方案

原文:towardsdatascience.com/4-faster-pandas-alternatives-for-data-analysis-1ff787d795

流行数据分析工具的性能基准测试

Chengzhi ZhaoTowards Data Science Chengzhi Zhao

·发布于 Towards Data Science ·阅读时长 9 分钟·2023 年 2 月 9 日

--

照片由 Mateusz ButkiewiczUnsplash 上拍摄

Pandas 毋庸置疑是 Python 中最受欢迎的库之一。它的 DataFrame 直观且拥有丰富的数据处理 API。许多 Python 库与 Pandas DataFrame 集成,以提高它们的采用率。

然而,Pandas 在处理大数据集的领域并不出色。它主要用于单台机器上的数据分析,而不是机器集群。在这篇文章中,我将尝试测量 Polars、DuckDB、Vaex 和 Modin 作为 Pandas 的替代方案的性能

类似数据库操作的基准测试h2oai 发布,这启发了本文的构思。基准测试实验于 2021 年 5 月进行。本文旨在回顾两年来这个领域的许多新特性和改进。

为什么 Pandas 在处理大数据集时速度较慢?

主要原因在于 Pandas 并未设计为在多个核心上运行。Pandas 一次只使用一个 CPU 核心来执行数据处理任务,在现代拥有多个核心的 PC 上无法利用并行计算的优势。

如何在数据规模较大(仍然可以放在一台机器上)但 Pandas 执行时间较长的情况下解决这个问题?一种解决方案是利用像 Apache Spark 这样的框架,通过集群来执行数据处理任务。但有时,通过对数据进行采样并在单台机器上分析,数据分析的效率可能会更高。

如果你希望在单台机器上进行操作,让我们在本文中比较Polars、DuckDB、Vaex 和 Modin作为与 Pandas 的替代方案。为了衡量处理大量数据所需的时间,我将分享在单台机器上的性能基准。

性能评估准备

测试机器的规格

MacBook Pro(13 英寸,2020 年,四个 Thunderbolt 3 接口)

  • CPU:2 GHz 四核 Intel Core i5(4 核)

  • 内存:16 GB 3733 MHz LPDDR4X

  • 操作系统:MacOS Monterey 12.2

测试数据集

在这种情况下,适合处理的中大型数据集足以展示差异。NYC 停车罚单[1]是进行评估的良好数据集。该数据集从 2013 年 8 月到 2017 年 6 月有 42.3M 行数据,共 51 列,包括注册州、车辆品牌和车辆颜色等信息,非常有助于洞察分析。我们将使用 2017 财政年度的数据集,包含 10.8M 行,文件大小约为 2.09G。

评估过程

  • 由于整个运行时间包括将数据读入内存,因此有必要将数据加载单独考虑。

  • 我们将重复调用 5x 次以避免边缘情况,并使用中位数值作为最终性能结果报告。

辅助函数用于重复和计算中位数

from itertools import repeat
from statistics import median
import functools
import time

durations = []

## repeat a given function multiple times, append the execution duration to a list
def record_timer(func, times = 5):
    for _ in repeat(None, times):
        start_time = time.perf_counter()
        value = func()
        end_time = time.perf_counter()
        run_time = end_time - start_time
        print(f"Finished {func.__name__!r} in {run_time:.10f} secs")
        durations.append(run_time)
    return value

## Decorator and compute the median of the function
def repeat_executor(times=5):
    def timer(func):
        """Print the runtime of the decorated function"""
        @functools.wraps(func)
        def wrapper_timer(*args, **kwargs):
            value = record_timer(func, times=times)
            print(f'{median(list(durations))}')
            return value
        return wrapper_timer
    return timer

警告:我们将展示大量代码,以便读者了解我所做的,而不是不展示过程或指向 GitHub。如果你不关心过程,请跳过,直接查看底部的结果。

Pandas:基准

为了建立比较的基准,我们将检视日常分析工作的著名用例:筛选、聚合、连接和窗口函数

  • 筛选:查找车辆品牌为 BMW

  • 聚合:按车辆品牌分组并执行计数

  • 连接:在召唤号码上进行自连接

  • 窗口函数:根据计数对车辆品牌进行排名

我仅选择了用于测试的字段,分别是‘Summons Number’,‘Vehicle Make’,‘Issue Date’。请注意,如果选择所有字段,最后两个查询会显著变慢。

import pandas as pd
from repeat_helper import repeat_executor

df = pd.read_csv("./Parking_Violations_Issued_-_Fiscal_Year_2017.csv")
df = df[['Summons Number', 'Vehicle Make', 'Issue Date']]

# ## Filter on the Vehicle Make for BMW
@repeat_executor(times=5)
def test_filter():
    return df[df['Vehicle Make'] == 'BMW']['Summons Number']

# # ## Group By on the Vehicle Make and Count 
@repeat_executor(times=5)
def test_groupby():
    return df.groupby("Vehicle Make").agg({"Summons Number":'count'})

# # ## SELF join
@repeat_executor(times=5)
def test_self_join():
    return df.set_index("Summons Number").join(df.set_index("Summons Number"), how="inner", rsuffix='_other').reset_index()['Summons Number']

## window function
@repeat_executor(times=5)
def test_window_function():
    df['summon_rank'] = df.sort_values("Issue Date",ascending=False) \
        .groupby("Vehicle Make") \
        .cumcount() + 1
    return df

test_filter()
# # The median time is 0.416s
test_groupby()
# # The median time is 0.600s
test_self_join()
# # The median time is 4.159s
test_window_function()
# # The median time is 17.465s

DuckDb:高效的 OLAP 内存数据库

DuckDB因其列式向量化引擎支持分析类型的查询而越来越受欢迎。它是SQLite的一个分析或 OLAP 版本,SQLite 是一种广泛采用的简单嵌入式内存数据库管理系统。

尽管它是一个数据库管理系统,但与 Microsoft SQL Server 或 Postgres 相比,安装并不复杂;此外,运行查询不需要外部依赖。我惊讶于使用DuckDb CLI执行 SQL 查询是如此简单。

如果你偏好 SQL 接口,DuckDb 可能是你直接在 CSV 或 Parquet 文件上进行数据分析的最佳替代方案。让我们继续一些代码示例,并同时展示使用 DuckDb 进行 SQL 操作是多么简单。

DuckDb 具有一个神奇的 read_csv_auto 函数来推断 CSV 文件并将数据加载到内存中。在运行时,我发现我必须将 SAMPLE_SIZE=-1 更改为跳过采样,因为我的数据集中有些字段未被正确推断,默认的采样大小为 1,000 行。

import duckdb
from repeat_helper import repeat_executor

con = duckdb.connect(database=':memory:')
con.execute("""CREATE TABLE parking_violations AS SELECT "Summons Number", "Vehicle Make", "Issue Date" FROM read_csv_auto('/Users/chengzhizhao/projects/pandas_alternatives/Parking_Violations_Issued_-_Fiscal_Year_2017.csv', delim=',', SAMPLE_SIZE=-1);""")
con.execute("""SELECT COUNT(1) FROM parking_violations""")
print(con.fetchall())
# ## Filter on the Vehicle Make for BMW
@repeat_executor(times=5)
def test_filter():
    con.execute("""
        SELECT * FROM parking_violations WHERE "Vehicle Make" = 'BMW'
        """)
    return con.fetchall()

# # ## Group By on the Vehicle Make and Count 
@repeat_executor(times=5)
def test_groupby():
    con.execute("""
        SELECT COUNT("Summons Number") FROM parking_violations GROUP BY "Vehicle Make"
        """)
    return con.fetchall()

# # # ## SELF join
@repeat_executor(times=5)
def test_self_join():
    con.execute("""
        SELECT a."Summons Number"
        FROM parking_violations a
        INNER JOIN parking_violations b on a."Summons Number" = b."Summons Number"
        """)
    return con.fetchall()

# ## window function
@repeat_executor(times=5)
def test_window_function():
    con.execute("""
        SELECT *, ROW_NUMBER() OVER (PARTITION BY "Vehicle Make" ORDER BY "Issue Date")
        FROM parking_violations 
        """)
    return con.fetchall()

test_filter()
# The median time is 0.410s
test_groupby()
# # The median time is 0.122s
test_self_join()
# # The median time is 3.364s
test_window_function()
# # The median time is 6.466s

DuckDb 的结果令人印象深刻。我们在过滤测试上表现相当,但在其余三项测试中相较于 pandas 性能更佳。

如果你不习惯编写 Python,你可以使用 DuckDb CLI 的 SQL 接口在命令行中进行操作,或者轻松使用 TAD

作者展示了如何通过 CLI 使用 SQL 查询 DuckDB | 图片由作者提供

Polars:惊人的快速构建基于 Rust + Arrow

Ritchie Vink 创建了 Polars。Ritchie 还发布了一篇博客文章,“我编写了一个最快的 DataFrame 库”,受到了广泛好评。Polars 的令人印象深刻之处在于,在 h2oai 的 数据库类操作基准测试 中,它在分组和连接操作中排名第一。

这里有几个 Polars 可以替代 Pandas 的理由:

  • Polars 从一开始就支持 DataFrame 的并行化。它不局限于单核操作。

  • PyPolars 基于 Rust 并具有 Python 绑定,性能出色,可与 C 进行比较,而“Arrow 列式格式”是分析 OLAP 类型查询的绝佳选择。

  • 延迟评估:计划(而非执行)查询直到触发。这可以用来进一步优化查询,例如额外的下推。

import polars as pl
from repeat_helper import repeat_executor

df = pl.read_csv("./Parking_Violations_Issued_-_Fiscal_Year_2017.csv")
df = df.select(['Summons Number', 'Vehicle Make', 'Issue Date'])

# ## Filter on the Vehicle Make for BMW
@repeat_executor(times=5)
def test_filter():
    return df.filter(pl.col('Vehicle Make') == 'BMW').select('Summons Number')

# # ## Group By on the Vehicle Make and Count 
@repeat_executor(times=5)
def test_groupby():
    return df.groupby("Vehicle Make").agg(pl.col("Summons Number").count())

# # # ## SELF join
@repeat_executor(times=5)
def test_self_join():
    return df.join(df, on="Summons Number", how="inner").select('Summons Number')

# ## window function
@repeat_executor(times=5)
def test_window_function():
    return df.select(
        [
            'Summons Number',
            'Vehicle Make',
            'Issue Date',
            pl.col(['Issue Date']).sort(reverse=True).cumcount().over("Vehicle Make").alias("summon_rank")
        ]
    )   

test_filter()
# # The median time is 0.0523s

test_groupby()
# # # The median time is 0.0808s

test_self_join()
# # # The median time is 1.343s

test_window_function()
# # The median time is 2.705s

哇,Polars 的速度真是惊人!在 Polars 中编程让你感觉像是 pySpark 和 Pandas 的混合体,但接口非常熟悉,我用不到 15 分钟就写出了上面的查询,尽管我对 Polars API 没有经验。你可以参考Polars 文档(Python 版)来快速理解。

Vaex:离线数据框

Vaex 是另一种采用延迟评估的替代方案,避免了额外的内存浪费和性能损失。它使用内存映射,并且只有在明确要求时才会执行。Vaex 具有一系列方便的数据可视化功能,使得探索数据集变得更加容易。

Vaex 实现了并行化分组操作,并且在连接操作中表现高效。

import vaex
from repeat_helper import repeat_executor

vaex.settings.main.thread_count = 4 # cores fit my macbook

df = vaex.open('./Parking_Violations_Issued_-_Fiscal_Year_2017.csv')
df = df[['Summons Number', 'Vehicle Make', 'Issue Date']]

# ## Filter on the Vehicle Make for BMW
@repeat_executor(times=5)
def test_filter():
    return df[df['Vehicle Make'] == 'BMW']['Summons Number']

# # ## Group By on the Vehicle Make and Count 
@repeat_executor(times=5)
def test_groupby():
    return df.groupby("Vehicle Make").agg({"Summons Number":'count'})

# # ## SELF join
@repeat_executor(times=5)
def test_self_join():
    return df.join(df, how="inner", rsuffix='_other', left_on='Summons Number', right_on='Summons Number')['Summons Number']

test_filter()
# # The median time is 0.006s

test_groupby()
# # The median time is 2.987s

test_self_join()
# # The median time is 4.224s

# window function https://github.com/vaexio/vaex/issues/804

然而,我发现窗口函数尚未实现,在这里跟踪的开放问题。我们可以通过每个组进行迭代,并使用在此问题中提到的建议为每行分配一个值。然而,我没有发现 Vaex 自带实现的窗口函数。

vf['rownr`] = vaex.vrange(0, len(vf))

Modin:通过更改一行代码来扩展 pandas

只需更改一行代码,Modin 是否能为用户提供比 Pandas 更好的性能?在 Modin 中,需要做以下更改,将 Pandas 库替换为 Modin。

## import pandas as pd
import modin.pandas as pd

然而,仍然有一个实现列表需要在 Modin 中完成。除了代码更改,我们还需要设置其调度的后台。我在这个例子中尝试使用 Ray

import os
os.environ["MODIN_ENGINE"] = "ray"  # Modin will use Ray

#########################
#######Same AS Pandas#######
#########################

test_filter()
# # The median time is 0.828s

test_groupby()
# # The median time is 1.211s

test_self_join()
# # The median time is 1.389s

test_window_function()
# # The median time is 15.635s, 
# `DataFrame.groupby_on_multiple_columns` is not currently supported by PandasOnRay, defaulting to pandas implementation.

Modin 上的窗口函数尚未在 Ray 上得到支持,因此它仍然使用 Pandas 的实现。时间花费与 Pandas 的窗口函数更为接近。

(py)datatable

如果你来自 R 社区,data.table 应该不是一个陌生的包。随着任何包的流行,它的核心思想会被引入到其他语言中。(py)datatable 是试图模仿 R 的 data.table 核心算法和 API 的一个尝试。

然而,在测试过程中,这并未比 pandas 更快,鉴于语法类似于 R 的 data.table,我认为在此作为 Pandas 替代方案提及是不错的。

结果

最终比较 | 作者提供的图片

最终想法

这些是我测试过的 Pandas 替代品,它们在性能上优于 Pandas。同时,API 的变化对 Pandas 并不显著。如果你考虑使用这些库之一,过渡应该是平滑的。另一方面,Pandas 仍然在 API 功能覆盖上最为全面。替代方案在高级 API 支持如窗口函数方面有所不足。

在单台机器上运行 Pandas 仍然是数据分析或临时查询的最佳选择。虽然替代库在某些情况下可能提升性能,但在单台机器上并不总是如此。

请告诉我你认为哪个是你会选择的 Pandas 最佳替代方案,留下评论。

[1] 纽约市停车罚单数据集,纽约市,CC0: 公共领域www.kaggle.com/datasets/new-york-city/nyc-parking-tickets

我希望这个故事对你有所帮助。本文是我工程与数据科学故事的系列文章之一,目前包括以下内容:

Chengzhi Zhao

Chengzhi Zhao

数据工程与数据科学故事

查看列表53 个故事

你还可以订阅我的新文章或成为推荐的 Medium 会员,享受对 Medium 上所有故事的无限访问权限。

如果有任何问题/评论,请随时在故事的评论区留言,或通过LinkedinTwitter直接联系我

4 个失败的物理信息神经网络的想法

原文:towardsdatascience.com/4-ideas-for-physics-informed-neural-networks-that-failed-ce054270e62a

这是一个关于 PINNs 的扩展列表,这些扩展要么没有提高性能,要么完全破坏了它们——所以你不必自己尝试。

拉斐尔·比绍夫Towards Data Science 拉斐尔·比绍夫

·发布于 Towards Data Science ·9 分钟阅读·2023 年 2 月 11 日

--

图片由 DeepMind 提供,来自 Unsplash

物理信息神经网络(PINNs) 的世界里,就像在任何其他新兴的机器学习领域一样,似乎每个人都急于分享他们发现的改进这些模型的惊人技术。我自己也不例外,已经发布了三篇关于改进 PINNs 性能的有用扩展的文章:

然而,常常被忽视的是那些未能奏效的无数想法。现实是,提升 PINNs 的旅程并不总是一帆风顺,许多有前途的想法最终都未能实现。

我没有失败。我只是找到了一万种行不通的方法。— 托马斯·A. 爱迪生

在本文中,我想探讨一些有前途但遗憾未能取得预期结果的 PINNs 增强想法。因此,秉持托马斯·爱迪生的精神,加入我一起深入探讨那些未成功的 PINNs 扩展。

一如既往,文章附带一个包含本文介绍概念的笔记本:

[## Burgers_PINN_fails.ipynb

Colaboratory 笔记本

drive.google.com

引言

PINN 与经典神经网络在处理和表示数据的方式上有所不同。当查看 PINN 中隐含表示的 TSNE 图时,这一点立即变得明显:

在训练有三层的 PINN 模型上进行的隐含向量的 3D TSNE 表示。左侧:基尔霍夫 PDE 的参考解。中间:第一层(蓝色)、第二层(绿色)和第三层(橙色)的表示。右侧:数据点根据位置点的 x + y 值着色,其中 x 控制像素颜色中的红色量,y 控制蓝色量。图由作者提供。

在经典神经网络中,评估出相似结果的协同位置点会在隐含向量中表示得很接近。在 PINN 中情况并非如此。每一层的表示不仅位于流形的完全不同区域,即使是协同位置点的坐标也不会混合。

这些特殊性质使得处理 PINN 变得困难且违反直觉,即使对于经验丰富的数据科学家也是如此。尽管该领域的进展主要来自理论和数学界,但以经验为驱动的研究人员在获得立足点方面遇到了困难。

本文尝试通过展示未能成功的扩展和想法来增加对 PINN 内部工作原理的理解,这些扩展和想法要么未能提高准确性,要么完全破坏了 PINN 的训练流程。

1. 批量归一化

批量归一化是深度学习中一种流行的技术,用于归一化每一层的激活,这可以提高训练稳定性并减少过拟合。然而,由于其训练过程的特殊性,PINN 中不能使用批量归一化。

在 PINN 中,训练过程涉及最小化一个损失函数,该函数衡量模型预测与系统底层物理之间的差异。这些物理定律通常不包含有关如何处理基于点的均值和标准差进行动态偏移和缩放的输入的信息。通过基于随机小批量样本的统计数据来扰动数据,批量归一化“破坏”了指导 PINN 训练的物理规律,因此不应使用。

批量归一化的替代方法可以是层归一化,它根据同一层中的节点值对节点上的值进行归一化,或是可学习的归一化,其中一个密集层负责产生偏移和缩放,并且进行正则化,以使层中的值具有零均值和单位方差。

然而,在进行各种实验后,我发现,在 PINN 中,逐层归一化可能并非必要。实际上,加快模型收敛的一个有效且简单的方法是,在输入网络之后添加一层。这一层通过使用样本的物理域范围,将配点缩放到 [-1, 1] 的范围。

import tensorflow as tf
from tensorflow.keras.layers import Input, Concatenate, Dense

def pinn_model(n_layers:int, n_nodes:int, activation:tf.keras.activations, x_range:tuple, y_range:tuple):
    x = Input((1,), name=name+'_input_x')
    y = Input((1,), name=name+'_input_y')

    # normalize data between -1 and 1
    x_norm = (x - x_range[0]) / (x_range[1] - x_range[0])
    y_norm = (y - y_range[0]) / (y_range[1] - y_range[1])
    xy = Concatenate()([x_norm, y_norm]) * 2 - 1

    u = Dense(n_nodes, activation=activation, kernel_initializer='glorot_normal')(xy)
    for i in range(1, n_layers):
        u = Dense(n_nodes, activation=activation, kernel_initializer='glorot_normal')(u) + u
    u = Dense(1, use_bias=False, kernel_initializer='glorot_normal')(u)

    return tf.keras.Model([x, y], u)

2. ReLU**(n+1) 激活

确保网络中使用的激活函数可以足够多次可微是至关重要的。这是因为 PINN 的输出需要多次相对于输入进行导数计算(根据 PDE 的微分阶数),并且还需要一次相对于模型权重进行导数计算。

具体而言,激活函数应具有 n+1 个非零导数,其中 n 是所解 PDE 的微分阶数。这个要求排除了流行的激活函数 ReLU,因为它的一阶导数是常数(0 或 1),而其二阶导数在所有地方都是零(不包括未定义的零点)。

然而,将 ReLU 激活函数提升到功率 n,可以使其可微 n 次,这可能是克服这一限制的一个有希望的思路。更好的是,通过将 PINN 初始化为多个并行层,每个层使用不同值 k ≤ n+1 的 ReLU,我们可以确保不同微分阶数的模式由网络的不同部分捕捉。

示例架构展示了一个具有多个平行层的 PINN,使用了不同功率 k ≤ n + 1 的 ReLU 激活函数。图由作者提供。

然而,让我非常失望的是,这种架构未能收敛。我只能猜测这是由于功率 k 的层没有从作用于更高阶微分的损失项中获得更新。但在推断时,当预测 u 时,即使所有 PDE 的组件没有完全通知,每一层都会参与计算。这可能导致噪声的注入,从而限制了架构的建模能力。

3. 卷积神经网络

在二维域上求解 PDE 时,人们可能会倾向于在网格中采样配点,然后将其视为图像中的像素。这将允许使用 CNN,由于其空间不变性的归纳偏差,CNN 是图像处理中的广泛使用架构。

然而,请记住,PINNs 是通过物理定律进行训练的。这些定律通常不包含有关如何从多个配点处聚合值的信息。因此,考虑多个配点的内核并没有太大意义。此外,CNN 的一个重要特性是它们可以在多个抽象层次上找到局部模式,前几层本质上作为边缘检测器。但在 PINNs 中,第一层内核的输入只是等间距配点的坐标。从中没有模式可以学习。

为了使 CNN 有效,你需要修改 PINNs 的设置和目标 [2]。

4. 向量量化

向量量化 [3]是一种机器学习技术,它将高维连续数据映射到一组离散符号中。在 PINNs 中使用向量量化层的想法是引入一个离散组件,以帮助捕捉 PDE 中的间断性。

神经网络在建模尖锐的间断性时表现不佳,因为其逼近需要使用一个步进函数,而步进函数本身不可微。通过将向量量化融入架构中,可能会认为 PINNs 能够更好地处理这些尖锐的过渡。这是因为向量量化层本质上充当了一个“离散化”步骤,将连续输入空间划分为较小、更易管理的段,并在它们之间形成明显的边界。

向量量化 PINNs(VQPINNs)可能的外观示意图。作者绘制的图。

首先,坐标 x 和 y 通过一个密集层映射到更高维空间。然后计算该向量与训练字典中一组向量之间的余弦相似度,并选择最相似的向量作为 PINN 的附加输入。因此,PINN 接收的输入包括配点和源自离散操作的向量。该操作通过直通估计器 [4]被近似为可微。

然而,回到本文主题,这一努力充满了挫折和失望,因为一个有前途的想法再次没有结出成果。当我在巴克利-利弗雷特方程 [5]上训练一个 PINN,该方程在整个领域内存在间断时,我的模型确实将其划分为两个段,并在之间形成了一个尖锐的边界。但边界从未在正确的位置,并且子区域内主方程的建模也不令人满意。

Buckley-Leverett PDE 在整个领域上有一个明显的不连续性。图由Diab et al. [5]

我最好的猜测是,不连续性使物理领域出现了明显的分割,网络的不同部分专注于不同的子领域。这意味着,如果网络的部分仅应用于自己子领域之外的区域,它们可能会与边界或初始条件断开连接。这也是为什么 PINNs 的集合,如MoE-PINNs [6],必须确保门控网络保持连续函数,即不要使 softmax 操作过于尖锐。否则,边界条件和初始条件不会在整个领域内强加,从而导致不正确的解决方案。

结论

成功实现和扩展 PINNs 是一项困难且常常违背直觉的工作。已建立的(批量归一化,CNNs)或来自其他机器学习领域的有前景的特性(向量量化)无法应用于 PINNs,这表明从业者需要深入了解内部机制,以提出有效的扩展和想法。

这些想法对于使 PINNs 更强大,加速收敛,以及最终使其成为像有限元方法这样的已建立数值方法的有前景的替代方案是非常需要的。

感谢你抽时间阅读这篇文章。我鼓励你亲自尝试这些想法,并且非常期待听到有人对这些想法进行小的调整,从而突然使它们有效的消息!为了铺平这样的发现之路,我创建了一个笔记本,你可以在其中玩转这些想法的实现:

[## Burgers_PINN_fails.ipynb

Colaboratory 笔记本

drive.google.com](https://drive.google.com/file/d/1dEJeWjmHjmKBzpDnQQqw0IIEXlnr-Rj1/view?usp=sharing&source=post_page-----ce054270e62a--------------------------------)

如果你有更多的建议或推荐,我非常愿意在评论中听到它们!你可以在mkrausai.com上找到关于我的同事 Michael Kraus 的更多信息,在rabischof.ch上找到关于我自己的更多信息。

[1] M. Raissi, P. Perdikaris, 和 G. E. Karniadakis, 物理信息神经网络:解决涉及非线性偏微分方程的正向和逆向问题的深度学习框架,计算物理学杂志 378 (2019), 686–707。

[2] Gao, Han, Luning Sun, 和 Jian-Xun Wang. “PhyGeoNet: 物理信息几何自适应卷积神经网络用于解决不规则领域的参数化稳态 PDEs。” 计算物理学杂志 428 (2021): 110079。

[3] Van Den Oord, Aaron, 和 Oriol Vinyals. “神经离散表示学习。” 神经信息处理系统进展 30 (2017)。

[4] Bengio, Yoshua, Nicholas Léonard 和 Aaron Courville。“通过随机神经元估计或传播梯度以进行条件计算。” arXiv 预印本 arXiv:1308.3432 (2013)。

[5] Diab, Waleed 和 Mohammed Al Kobaisi。“用于非凸流量函数的双曲 Buckley-Leverett 问题的 PINNs 解决方案。” arXiv 预印本 arXiv:2112.14826 (2021)。

[6] R. Bischof 和 M. A. Kraus,“物理信息神经网络的专家混合集成元学习”,第 33 届建筑信息学论坛论文集,2022。

数据驱动世界中你应该理解的 4 个重要统计理念

原文:towardsdatascience.com/4-important-statistical-ideas-you-should-understand-in-a-data-driven-world-3a9d59ee4a85

你不必成为统计学专家才能在现代世界中立足,但有一些基本的理念你应该理解。

Murtaza AliTowards Data Science Murtaza Ali

·发布于 Towards Data Science ·阅读时间 8 分钟·2023 年 7 月 2 日

--

Anne Nygård 提供的照片,来源于 Unsplash

避免现实是没有意义的。数据科学,更广泛地说,数据驱动的结构,是我们当前所构建的社会的核心。

当计算机科学热潮在 2000 年代初首次出现时,许多人注意到计算机科学将成为各个领域的核心部分。这一预言得到了验证。各行各业的公司——医疗、工程、金融等——开始聘请软件工程师进行各种形式的工作。这些领域的学生也开始学习编码。

我认为数据科学的兴起更进一步。借助计算机科学,人们可以仅仅通过雇佣软件工程师来应对挑战。业务经理或销售专家不一定需要了解这些工程师的工作内容。

但数据科学更广泛且包罗万象。由于它是一个领域的混合体 [1],它的理念即使对于那些可能不是日常数据科学家的人员也很相关。

在这篇文章中,我将对每个人都应该理解的四个重要统计理念进行高层次的概述,无论你的官方职位是什么。无论你是项目经理、招聘人员,还是 CEO,对这些概念的某种程度的熟悉肯定会对你的工作有所帮助。此外,在工作之外,对这些概念的熟悉将使你具备在现代社会中导航所必需的数据素养。

让我们开始吧。

只是一个大而糟糕的样本

在本科时期,我上过的一门数据科学课程有大量学生——近 2000 人。这门课程《数据科学基础》是校园中最受欢迎的课程之一,因为它旨在使各个部门的学生都能接触到。课程并没有立即进入高级数学和编程,而是专注于可能影响各个领域学生的高层次思想。

在我们早期的一次讲座中,教授说了一句话,至今仍让我记忆犹新,每当我处理任何即使仅仅是相关的数据时,它都会回到我的脑海中。她在讨论随机抽样,这是一个宽泛的术语,涉及以能够代表整个总体的方式选择研究总体的一个子集。这个理念是,研究这个子集应该使人能够对整个总体得出结论。

她指出,拥有一个好的样本至关重要,因为再多的数学手段和复杂技术也无法弥补一个实际上不具代表性的子集。她提到,许多人认为,如果初始样本不好,那么一个合理的解决方案是坚持相同的方法,但收集更大的样本。

“那你就会得到一个非常大、非常糟糕的样本,” 她对着充满大学生的巨大讲堂说道。

理解这一基础点——及其更广泛的影响——将使你能够理解许多人视为理所当然的许多社会政治现象。为什么总统民调经常不准确?是什么使得看似强大的机器学习模型在现实世界中失败?为什么一些公司生产的产品从未问世?

经常,答案隐藏在样本中。

“误差”并不意味着“错误”

这个话题在大多数涉及数据或统计的课程中都是隐含的,但我这里的讨论受到了阿尔贝托·开罗在他那本出色的《图表如何说谎》一书中强调这一点的启发。

开罗的书的前提是概述数据可视化如何被用来误导人们,无论是无意还是恶意。在其中一章中,开罗阐述了在数据中可视化不确定性所面临的挑战,以及这本身如何导致误导性的数据可视化。

他从统计中的误差概念开始讨论。他提到一个关键点:虽然在标准英语中,“误差”与“错误”是同义的,但在统计领域中情况完全不同。

统计误差的概念与不确定性有关。测量和模型中几乎总是会存在某种形式的误差。这与前面提到的样本有关。由于你没有描述的总体的每一个数据点,你将不可避免地面临不确定性。如果你对未来的数据点做出预测,这种情况会更加明显,因为它们尚不存在。

减少和解决不确定性是统计学和数据科学的关键部分,但超出了本文的范围。在这里,你需要理解的主要一点是,仅仅因为一个统计结果带有不确定性,并不意味着它是错误的。实际上,这很可能是一个指示,说明产生结果的人知道他们在做什么(你应该对没有任何不确定性参考的统计声明持怀疑态度)。

学习如何正确解读不确定性的统计声明[2],而不是将其视为错误。这是一个至关重要的区别。

你不能总是“为此建立一个模型”

在普通大众中,似乎存在一种观念,认为人工智能是一种神奇的工具,能够完成任何事情。随着自动驾驶汽车和逼真的虚拟助手的出现,但数据素养没有相应提升,这种思维方式的发展并不令人意外。

不幸的是,这完全是错误的。人工智能不是魔法。它严重依赖于良好的数据,如果基础数据质量较差,它的结果实际上可能会非常误导。

我曾有一位同事被分配到一个项目,她的任务是为特定目标构建一个机器学习模型。这个模型旨在根据历史数据将未来事件分类到特定类别中。

只有一个问题:她没有任何数据。项目中的其他人(显然不熟悉数据科学)不断坚持她应该继续建立模型,即使她没有数据,因为机器学习非常强大,这应该是可以实现的。他们没有意识到他们的要求根本不可行。

是的,机器学习确实很强大,是的,我们在做更酷、更好的任务方面也在不断进步。然而,就目前的情况而言,它并不是一切问题的魔法解决方案。你最好记住这一点。

数字确实会撒谎

人们像撒花一样随意使用“数字不会撒谎”这个短语。

哦,要是他们知道就好了。数字实际上是会撒谎的。很多。在某些情况下,撒谎的频率甚至超过了讲真话。但它们不是因为原始形式下的错误而撒谎;它们撒谎是因为普通人不知道如何解读它们。

有无数的例子说明数字如何被扭曲、操控、改变和转换,以支持某个论点。为了说明这一点,我将介绍一个例子:在做出笼统声明时未考虑基础人口分布。

这本身有点模糊,所以让我们看一个例子。考虑以下情况,通常会问医学学生:

假设某种疾病在一个人群中每 1000 人中就有 1 人受到影响。有一个测试可以检查一个人是否有这种疾病。该测试不会产生假阴性(即,任何患有该疾病的人都会测试为阳性),但假阳性率为 5%(即使一个人没有这种疾病,也有 5%的机会测试为阳性)。假设从人群中随机选择的一个人进行了测试并测试为阳性。他们实际上有这种疾病的可能性是多少?

乍看之下,许多人给出的合理答案是 95%。有些人甚至可能会怀疑仅使用假阳性率来做出这个判断是否在数学上准确,但他们可能仍会猜测答案接近这个数字。

不幸的是,正确答案不是 95%或接近它。这个随机选择的人实际上患有这种疾病的概率大约是 2%。

大多数人离正确答案如此之远的原因是,尽管他们关注了较低的假阳性率,但他们没有考虑到人群中该疾病的实际流行率:人群中只有 1/1000(即 0.1%)的人实际上患有这种疾病。因此,这 5%的假阳性率实际上会影响许多人,因为一开始就有这么少的人患有这种疾病。换句话说,有很多很多的机会成为假阳性。

这个问题的正式数学原理超出了这篇文章的范围,但如果你有兴趣,可以在这里查看详细解释 [3]。也就是说,你不需要深入数学就能理解要点:可以想象,利用上述情景吓唬一个人,使他们相信自己比实际情况更容易感染这种疾病。仅凭数字往往会被误用和/或误解,以促进错误的信念。

保持警惕。

最终想法和总结

这是这篇文章的一些重要要点小抄:

  1. 大样本≠好样本。确保准确代表人群需要的不仅仅是数量。

  2. 在统计学中,“错误”并不意味着“错误”。 它与不确定性有关,而不确定性是统计工作中不可避免的元素。

  3. 机器学习和人工智能不是魔法。它们严重依赖于基础数据的质量。

  4. 数字可能会产生误导。当有人提出统计声明时,尤其是在非学术(即新闻)背景下,仔细审查它,然后再接受结论。

你不需要成为统计学专家来应对这个数据驱动的世界,但理解一些基础概念和知道要避免的陷阱对你是有益的。我希望这篇文章能帮助你迈出第一步。

下次见。

想在 Python 编程中脱颖而出? 点击这里获取独家、免费的简单易读指南。想在 Medium 上阅读无限故事?通过下面的推荐链接注册!

[## Murtaza Ali - Medium

阅读 Murtaza Ali 在 Medium 上的文章。他是华盛顿大学的博士生。对人机交互感兴趣…

murtaza5152-ali.medium.com](https://murtaza5152-ali.medium.com/?source=post_page-----3a9d59ee4a85--------------------------------)

参考文献

[1] towardsdatascience.com/the-three-building-blocks-of-data-science-2923dc8c2d78

[2] bookdown.org/jgscott/DSGI/statistical-uncertainty.html

[3] courses.lumenlearning.com/waymakermath4libarts/chapter/bayes-theorem/

4 个 Pandas 函数用于 DataFrame 的逐元素比较

原文:towardsdatascience.com/4-pandas-functions-for-element-wise-comparison-of-dataframes-c7e06e51d399

通过示例进行解释。

Soner YıldırımTowards Data Science Soner Yıldırım

·发表于 Towards Data Science ·4 分钟阅读·2023 年 6 月 10 日

--

图片由 NordWood ThemesUnsplash 提供

Pandas DataFrame 是具有标签的行和列的二维数据结构。

具有 3 行 3 列的 DataFrame(图片由作者提供)

我们有时需要对两个 DataFrame 进行逐元素比较。例如:

  • 使用另一个 DataFrame 中的值来更新 DataFrame 中的值。

  • 比较值并选择较大或较小的值。

在本文中,我们将学习四种不同的 Pandas 函数,这些函数可以用于此类任务。我们还将通过示例更好地理解它们之间的差异和相似之处。

如果你想了解更多关于 Pandas 的内容,请访问我的课程 500 道练习掌握 Python Pandas

让我们首先创建两个 DataFrame 以用于示例。

import numpy as np
import pandas as pd

# create DataFrames with random integers
df1 = pd.DataFrame(np.random.randint(0, 10, size=(4, 4)), columns=list("ABCD"))
df2 = pd.DataFrame(np.random.randint(0, 10, size=(4, 4)), columns=list("ABCD"))

# add a couple of missing values
df1.iloc[2, 3] = np.nan
df1.iloc[1, 2] = np.nan

(图片由作者提供)

1. 结合

combine 函数根据给定的函数进行逐元素比较。

例如,我们可以在每个位置选择两个值中的最大值。当我们做例子时会更清楚。

combined_df = df1.combine(df2, np.maximum)

(图片由作者提供)

查看第一行第一列的值。合并的 DataFrame 取 5 和 2 中较大的值。

如果其中一个值是 NaN(即缺失值),那么合并后的 DataFrame 在这个位置也会是 NaN,因为 Pandas 无法比较带有缺失值的值。

我们可以通过使用fill_value参数来选择一个常量值以处理缺失值。在比较之前,缺失值会被这个值填充。

combined_df = df1.combine(df2, np.maximum, fill_value=0)

(图片由作者提供)

df1中有两个NaN值,它们被填充为 0,然后与df2中相同位置的值进行比较。

2. combine_first

combine_first函数用另一个数据框相同位置的值更新NaN值。

combined_df = df1.combine_first(df2)

(图片由作者提供)

如上图所示,combined_df的值与df1相同,除了NaN值,这些值被df2中的值填充。

需要注意的是,combine_first函数不会更新df1df2中的值。它仅返回第一个数据框的更新版本。

3. update

update函数使用另一个数据框中相同位置的值来更新数据框中的缺失值。

这听起来和combine_first函数的作用一样。然而,有一个重要的区别。

update函数不返回任何内容,而是就地更新。因此,原始数据框会被修改(或更新)。用一个例子来看会更清楚。

我们有两个数据框,如下所示:

(图片由作者提供)

我们来对df1使用update函数。

df1.update(df2)

这行代码不返回任何内容,但它更新了df1。更新后的版本是:

(图片由作者提供)

df1不再包含缺失值,这些值已经使用df2中的值进行更新。

4. compare

compare函数比较相同位置的值,并返回一个数据框,将它们并排显示。

comparison = df1.compare(df2)

(图片由作者提供)

如果某个特定位置的值相同,比较会显示为NaN(例如第二行,第一列)。我们可以通过使用keep_equal参数来更改这一行为。

comparison = df1.compare(df2, keep_equal=True)

(图片由作者提供)

结论

我们学习了四种不同的 Pandas 函数,用于对两个数据框中的值进行逐元素比较。它们各有不同的目的。有些用于更新值,而有些只是进行比较。

在某些情况下,这些函数中的某一个可能是最合适的使用方式。因此,最好了解所有这些函数。

你可以成为 Medium 会员 以解锁我所有的写作内容,以及 Medium 上的其他内容。如果你已经是会员,请不要忘记 订阅 ,这样我发布新文章时你就会收到邮件。

感谢阅读。如果你有任何反馈,请告诉我。

4 个 Pandas 一行代码解决特定任务的高效方法

原文:towardsdatascience.com/4-pandas-one-liners-that-surprised-me-in-a-good-way-b67955211f81

以快速简单的方式完成复杂任务

Soner YıldırımTowards Data Science Soner Yıldırım

·发布在 Towards Data Science ·阅读时间 5 分钟·2023 年 11 月 23 日

--

图片由 Tom Bradley 提供,来源于 Unsplash

第三方库是为了回应需求而创建和开发的。没有人会坐下来说:“我要创建一个工具,等待情况出现,让其他人需要它。”相反,他们会意识到问题并思考解决方案来解决它。这就是工具的创造方式。

添加新功能到现有工具也一样。新功能的添加速度和效果取决于工具的受欢迎程度和背后的团队。

确实,Pandas 拥有一个高度活跃的社区,这使得 Pandas 成为数据科学生态系统中最受欢迎的数据分析和清洗库之一。

Pandas 拥有解决非常具体问题和用例的函数。这些一定是社区中积极使用它的人提出的需求。

在本文中,我将分享 4 个可以用 Pandas 一行代码完成的操作。这些操作帮助我高效解决了特定任务,并让我感到惊喜。

1. 从列表创建字典

我有一个项目列表,想查看它们的分布。更具体地说,我想查看列表中每个唯一值及其出现次数。

Python 字典是以这种格式存储数据的好方法。项目将是字典的键,而出现次数将是值。

通过 value_countsto_dict 函数,这个任务可以用一行代码完成。

这是一个简单的示例来演示这个情况:

import pandas as pd

grades = ["A", "A", "B", "B", "A", "C", "A", "B", "C", "A"]

pd.Series(grades).value_counts().to_dict()

# output
{'A': 5, 'B': 3, 'C': 2}

我们首先将列表转换为 Pandas Series,这是一维的数据结构。然后,我们应用 value_counts 函数以获取 Series 中唯一值及其频率。最后,我们将输出转换为字典。

2. 从 JSON 文件创建 DataFrame

JSON 是一种常用的文件格式,用于存储和传递数据。例如,当你从 API 请求数据时,它很可能会以 JSON 格式交付。

当我们清理、处理或分析数据时,通常更喜欢将其以表格格式(即表格状的数据结构)表示。我们可以利用 json_normalize 函数通过一次操作从 JSON 格式的对象创建 Pandas DataFrame。

假设数据存储在名为 data.json 的 JSON 文件中。我们首先按如下方式读取它:

import json

with open("data.json") as f:
    data = json.load(f)

data
# output
{'data': [{'id': 101,
   'category': {'level_1': 'code design', 'level_2': 'method design'},
   'priority': 9},
  {'id': 102,
   'category': {'level_1': 'error handling', 'level_2': 'exception logging'},
   'priority': 8}]}

如果我们将这个变量传递给 DataFrame 构造函数,它将创建如下的 DataFrame,这绝对不是一个可用的格式:

df = pd.DataFrame(data)

df(图片由作者提供)

但如果我们使用 json_normalize 函数并提供记录路径,我们将获得一个格式良好且干净的 DataFrame:

df = pd.json_normalize(data, "data")

df(图片由作者提供)

3. Explode 函数

考虑一个你有符合特定记录的项列表的情况。你需要以每个项单独占一行的方式重新格式化它。

以下图示说明了我试图解释的内容:

(图片由作者提供)

你可以考虑多种不同的方法来解决这个任务。最简单(可能是最简单)的一个方法是 explode 函数。让我们看看它是如何工作的。

我们有以下 DataFrame:

df(图片由作者提供)

我们将使用 explode 函数,并指定要爆炸的列名:

df_new = df.explode(column="data").reset_index(drop=True)

df_new(图片由作者提供)

reset_index 将新的整数索引分配给结果 DataFrame。否则,爆炸之前的索引将被保留(即所有键值为 A 的行将具有索引 0)。

4. Combine first

combine_first 函数有一个特定的用途,但极大地简化了这个特定的任务。

使用 combine_first 函数的具体情况:

你想从一个 DataFrame 中提取一列。如果该列中存在缺失值,你希望用另一列中的值来替换这些缺失值。

在这方面,它的功能与 SQL 中的COALESCE函数相同。

让我们创建一个包含缺失值的示例 DataFrame:

df = pd.DataFrame(
    {
        "A": [None, 0, 12, 5, None], 
        "B": [3, 4, 1, None, 11]
    }
)

df(图片由作者提供)

我们需要列 A 中的数据。如果有一行缺失值(即 NaN),我们希望用同一行中列 B 的值填充它。

df["A"].combine_first(df["B"])

# output
0     3.0
1     0.0
2    12.0
3     5.0
4    11.0
Name: A, dtype: float64

正如我们在输出中看到的,列 A 的第一行和最后一行是从列 B 中取出的。

如果我们想使用 3 列,我们可以链式调用 combine_first 函数。以下代码首先检查 A 列。如果有缺失值,则从 B 列获取。如果 B 列中对应的行也为 NaN,则从 C 列获取值。

df["A"].combine_first(df["B"]).combine_first(df["C"])

我们还可以在 DataFrame 层级上使用 combine_first 函数。在这种情况下,所有缺失值都将从第二个 DataFrame 中相应的值(即同一行、同一列)填充。

最后的话

Pandas 是我用过的最万能的工具之一。从计算简单的统计数据到非常复杂的数据清理过程,Pandas 总是帮助我快速解决任务。我唯一遇到的问题是处理非常大的数据集时,这似乎是 Pandas 唯一的短板。然而,最近有一些进展使 Pandas 在处理大数据集时更高效。我相信这是对所有热爱使用这个伟大工具的人们的好消息。

感谢阅读。如果您有任何反馈,请告知我。

如何批判性地评估你遇到的下一个数据科学项目

原文:towardsdatascience.com/4-powerful-unique-ways-to-help-you-critically-evaluate-the-next-data-science-project-you-come-112cc246c5cf

意见

定性方法、数据处理和媒体来源——以及对数字可能欺骗的详细探讨

Murtaza AliTowards Data Science Murtaza Ali

·发表于 Towards Data Science ·9 分钟阅读·2023 年 3 月 9 日

--

照片由 Laurenz Kleinheider 提供,来源于 Unsplash

数据科学的热潮大多集中在炫目和奢华的方面:跟踪你每一个动作的大数据、预测地球状态的强大模型、能够比我们曾经想象的更好地模拟人类思维的智能系统。

尽管这些杰出的成就确实是数据科学的一部分,但不应盲目接受。无论你是积极从事自己的数据科学项目,还是浏览其他数据科学家快速开发的产品,了解如何批判性地评估其中的数据至关重要。

之所以称之为数据科学,是有原因的。在任何项目中,基础的数据都是至关重要的。在这篇文章中,我们将探讨四种强有力的方法,以确保我们尽可能有效地分析数据。

以人为本的数据科学

随着人工智能(AI)的日益普及(例如 ChatGPT),人们终于开始更加关注数据科学的伦理问题。

当你与大多数 AI 系统互动时,它们会声称因为它们是机器,所以没有意见。然而,这并不是真的。所有人工智能系统都受到用于训练它们的数据中的固有偏见的强烈驱动。因此,数据中的任何偏见都会转移到模型中。这就是为什么许多 AI 系统可能具有歧视性的原因——一个著名的例子是面部识别技术中的偏见 [1]。此外,在许多情况下,原始的定量数据不足以捕捉数据的重要方面。

Sourojit Ghosh(G),我长期合作的专家,专注于社会推荐算法(SRA),在他最近的出版物 [4] 中提出了以下观点:

“SRA 的运作和发展中一个共同的主题是将用户非人性化,通过将表达丰富的个体简化为数据和存档到数据库中的元数据。这些算法学习并被教导将人类表达仅视为‘内容’和‘变量’。然而,人类不仅仅是他们的数据,还有许多背景和情况涉及内容的创建。”

我们设想将社会和个人背景整合到 SRA 对用户生成内容的理解中,可以在多个层面上改善最终结果。在用户创建内容和与推荐内容互动的不同时间点,系统提示可以询问有关创建内容的可选定性问题,例如社会和本地背景,以及用户创建内容背后的动机等几种可能性。如果用户选择回答这些问题,额外的定性内容可以被纳入 SRA 的推荐选择中,因为它致力于为用户提供更有意义的内容。这种功能可以让用户确定他们提供给 SRA 的额外信息和背景的层级,以便推断和融入建议中。

上述内容从高层次上展示了定性分析技术如何与传统的定量方法相结合,以获得对数据的更深入见解。数据科学中有许多定性技术的例子——这里有两个常见的:

  • 民族志:民族志涉及通过仔细观察和积极参与某个特定群体的社会和文化经历,寻找可预测的模式 [2]。

  • 用户访谈:这是构建某种产品或模型过程中的一个中间步骤,涉及到对最终用户的实际子集进行测试 [3]。

未来已来,未来是以人为本的。不要被落在后面。

理解如何正确操作数据

如果你是经验丰富的数据科学家,这可能看起来很明显,但对于刚刚开始的人来说并不一定清楚。

说明这一点的最佳方式是通过一个特定的数据科学工作流程的示例。在这里,我们将使用分析数据并通过数据可视化将其呈现给某些观众的过程。

新手可能会倾向于查看数据集,然后仅生成已可用的度量的图表。例如,假设我们有以下数据集:

作者提供的图片

我们为了简化数据集保持较小,但你可以想象,类似的数据集可以为更大范围的人群收集——比如大学或城市。

看着上面的内容,我们可以制作一个所有人身高的条形图。也许我们可以绘制年龄分布的直方图。更好的是,我们可以使用两个变量,制作身高与体重的散点图。

这些想法本身并没有错;事实上,我们可能希望在此数据集的可视化仪表板中构建这些图表。不过,这些问题的范围有限。如果我们想要深入探讨呢?

  • 当按性别分组时,平均身高和体重是多少?如果按年龄分组呢?是否存在某种关系?

  • 如果我们的主要观众在美国以外,是否可以将身高图表以厘米为单位?

  • 我们能否制作筛选出不在特定年龄范围内的人的图表,从而更好地将我们的图表概括到我们的目标人群中?

这些问题的答案确实存在于数据集中,但在数据的原始形式中无法获取。为了获得这些问题的答案,我们需要通过各种转换、聚合和过滤器来操作数据,然后再开始生成可视化。掌握这种操作技巧是你成为数据科学家所必需的技能。

你可以使用许多工具来操作数据:

  • 如果你熟悉编程,那么你最好的选择是使用 Pandas,这是 Python 强大的数据科学模块。

  • 如果你更偏向于定性 UX,那么你可以使用任务特定的工具。例如,如果你使用Tableau进行数据可视化,你应该知道它也有许多功能来促进数据操作。

  • 如果你只是想在将数据导出到任何外部工具之前修复其初始格式,那么 Excel 或 Google Sheets 提供了许多功能,可能符合你的需求。

要点是,学习至少一种可以操作数据的工具。

越多越好,但至少需要一个。

如果可能的话,回到数据源

在他优秀的书籍《图表如何欺骗》[5]中,信息设计师阿尔贝托·卡伊罗提出了以下媒体素养的一般规则:

“不信任任何没有明确提及或链接到其发布故事来源的出版物。”

从数据科学的角度来看,我们应该采纳一个重要的心态:如果任何模型、可视化或其他数据相关的交付物在没有提供数据来源的情况下做出主张,你应该立即持怀疑态度。

用简单的语言来说,这似乎是常识。当然,我们应该不信任那些没有明确提供数据的主张。谁会做出这样的事情呢?

很多人,碰巧其中一些人相当有影响力。

卡伊罗在他的书中提供了以下示例,这是一张 2017 年 12 月由白宫推特发布的图形[6]:

公开推文,特朗普白宫档案中的图形

上述图像实际上很需要视觉上的批评,但由于这篇文章不是关于可视化的,我们暂时先不讨论它们。有一点你应该特别关注。

这张图形,简而言之,是完全虚构的。

没有数据来源链接,这也符合实际,因为构建该可视化并没有使用任何数据。所示的三进制指数增长充其量是随机的,最坏的情况则是严重夸张。

尽管如此,仍有数百万人看到了这张图形,其中许多人可能从中得出了强烈的结论,尽管实际情况是上述图像中没有呈现任何事实。应该不难理解这为什么不好,甚至可能是危险的。

下次你检查一个数据科学项目时,检查其来源。这很重要。

数学正确 ≠ 准确

这怎么可能是正确的?如果某事在数学上是正确的,那它必然是准确的。

在这里定义我们所说的准确是重要的。对于数据科学——它本质上嵌入在社会结构中——准确性包括对数据的解释。也就是说,背景很重要

因此,我们所说的准确性是指我们从数据中得出的主张不能对更广泛的观众产生误导。这用一个例子会更有意义,所以让我们看一个来自优秀在线教材的例子,计算与推理思维[7]。

考虑一个大规模的群体,其中只有一小部分人受到疾病的影响。具体来说,每 1000 人中有 4 人感染了这种疾病。我们还有一个医疗测试可以高准确度地预测一个人是否患有该疾病:假阳性率为 5/1000,假阴性率为 1/100。

考虑到这一点,请思考以下问题:如果从人群中随机选取一个人,并且他们对疾病测试呈阳性,那么他们实际上患有这种疾病的概率是多少?

在继续滚动之前,请花点时间思考一下。

那么,你怎么看?考虑到我们的测试如此准确,也许大约 80%?85%?

实际的答案可能会让你吃惊:大约来说,这个人实际上有 44%的机会真正患有这种疾病。我们在这里略过数学细节,但如果你对此感兴趣,建议点击上面的链接。

在这里,我们专注于理解为什么会出现这种情况。数学中是否存在错误?正如你可能从本小节的标题中猜到的那样,实际上是没有错误的。

这种令人困惑的结果的原因在于,这种假设性疾病在基础人群中的发病率非常低,尽管我们的测试表面上很准确,但假阳性比真阳性更多。换句话说,患病人数很少,即使是看似小的假阳性率也对应了一个相当高的原始数字。

当我们说某些东西在数学上可能是正确的,但仍然不够准确时,这就是我们的意思。如果这样的测试被盲目地进行,而没有讨论基本的背景因素,可能会导致大量误导或甚至惊慌的人。背景——在这种情况下是基础人群的分布——在处理数据时极为重要。

最终思考 + 回顾

在处理数据时,至关重要的是要对每一个方面进行批判性评估。以下是你的备忘单:

  1. 数据科学并不总是只关注数字。以人为本的数据科学是未来的发展方向。

  2. 原始数据几乎总是不够的。学会如何处理数据

  3. 可交付成果的质量仅与数据有关。严格检查数据源。

  4. 数字确实会说谎。当数学看起来正确但结论值得怀疑时,寻找背景解释

如果你没有足够仔细地考虑你的数据,人们很容易误导你。

不要让那个成为你。保持信息灵通。

直到下一次,我的朋友们。

想在 Python 方面脱颖而出? 在这里获得独家免费的简单易读的指南。想在 Medium 上阅读无限制的故事?请使用下面的推荐链接注册!

[## Murtaza Ali - Medium

在 Medium 上阅读 Murtaza Ali 的文章。他是华盛顿大学的博士生,感兴趣于人机交互…

murtaza5152-ali.medium.com

参考文献

[1] 面向面部识别技术中的偏见

[2] 质性研究的现状

[3] 如何进行用户访谈

[4] 不仅仅是数据集中的单元:想象一种以人为本的数据科学方法来进行社交推荐算法

图表如何误导,阿尔贝托·卡伊罗

[6] 终结链式移民的时机?

[7] 决策制定

在处理机器学习模型之前,你应该问自己 4 个问题

原文:towardsdatascience.com/4-questions-to-ask-yourself-before-working-on-a-machine-learning-model-d0f9fdc2fbb2

你真的需要使用它吗?

Soner YıldırımTowards Data Science Soner Yıldırım

·发表于数据科学前沿 ·阅读时间 4 分钟·2023 年 4 月 11 日

--

图片由Ana Municio提供,来源于Unsplash

机器学习(ML)并不是一种魔法,你不能期望它能解决所有问题。

机器学习在准确性和速度上的进步使我们在面对任何问题时都明确地想到机器学习解决方案。

这是一个危险的心态,可能会产生前所未有的结果。我可以向你保证,你不希望在生产环境中出现前所未有的结果。

在这篇文章中,我们将探讨在考虑将机器学习(ML)作为解决方案之前需要解决的 4 个问题。

1. 你是否拥有与需要预测的数据具有相似特征或模式的训练数据?

机器学习并不是魔法。它在没有经过训练之前无法产生结果。因此,任何机器学习系统的首要要求是数据。在考虑是否可以将机器学习应用于某个问题之前,我们需要确保我们能获取到数据。

但是,我们用来训练模型的数据需要具有与我们想要预测的数据类似的模式。

假设我们想要训练一个模型,在一个平台上进行电影推荐。如果我们用 2000 年之前的数据进行训练,它很可能无法做出好的推荐,因为人们的口味会随时间变化。

在某些情况下,数据没有任何模式。它遵循纯随机过程。

2. 是否有更简单的解决方案?

一些问题非常简单,你不需要一个机器学习系统来解决。在这种情况下,应该优先选择更简单的解决方案,因为实施基于机器学习的解决方案通常需要更多的时间和金钱。

考虑一个销售预测任务,你有两个解决方案候选。一个是基于前几天和几周的移动平均。另一个是一个机器学习模型,具有通过大量数据计算得出的几十个特征。

如果前者提供了足够好的解决方案,或者被机器学习模型以非常小的差距超越,那么你可能应该选择更简单的移动平均模型。花费额外的时间和金钱在机器学习模型上可能不值得那一点微小的改进。此外,一旦决定扩展,部署机器学习模型的成本可能会显著增加。

3. 是否具备成本效益?

这与前面提到的在简单解决方案和复杂机器学习模型之间的选择有关。然而,在这种情况下,你只有一个机器学习模型作为解决方案。

即使机器学习是唯一的解决方案,它可能也不是你想要接受的东西。你需要密切关注经济回报。

创建一个机器学习系统并将其部署到生产中是需要花费金钱的。如果你处理大量数据,通常情况下,成本会大幅增加。

收集、存储和处理数据以及在云上训练模型可能是一个巨大的开支。

这归结于比较机器学习为你的业务提供的价值和你收到的云账单。如果你在云上操作一个机器学习系统花费了数千美元,而你的业务从中获益很少,那么你可能应该寻找更好的解决方案。

4. 你能承受错误吗?

即使你创建了一个最先进的模型,表现非常好,也会有错误。没有任何机器学习模型可以达到 100%的准确率。

所以问题是你是否能够承担犯错的风险。考虑一下 X 光片上的癌症检测。这实际上是一个至关重要的案例。你会仅仅依赖机器学习模型来完成这个任务吗?

机器学习模型可以作为辅助文档,但不能被信任来做出最终决策。

最终思考

机器学习是一个非常有能力的工具,帮助解决各种企业中的众多问题。然而,它不是每个任务的首选解决方案。

在投资时间和金钱于基于机器学习的解决方案之前,需要解决本文提到的问题。

机器学习很棒,但并不总是你的最佳选择。

你可以成为 Medium 会员 以解锁我写作的完整访问权限,以及 Medium 的其他内容。如果你已经是会员,请不要忘记 订阅 ,以便每次我发布新文章时收到邮件。

感谢阅读。如果你有任何反馈,请告诉我。

4 个快速且简单的步骤来美化 R Markdown

原文:towardsdatascience.com/4-quick-and-easy-steps-to-beautify-r-markdown-f2bdccb358b2

只需不到两分钟,通过一些简单的调整就能让你的报告看起来更精致

Jenna EaglesonTowards Data Science Jenna Eagleson

·发布在 Towards Data Science ·5 分钟阅读·2023 年 1 月 5 日

--

图片由 Greyson Joralemon 提供,来源于 Unsplash

首先,我喜欢 R Markdown。它是一个奇妙、强大、令人难以置信的工具。它也可能看起来很糟糕,糟糕到你的美丽辛勤工作可能会因为丑陋的格式而丢失,尤其是与技术水平较低的观众分享时。这不是 R Markdown 本身的介绍(一些好的入门资源可以在 这里这里 找到),而是为那些已经在 R Markdown 中创建了作品并希望花额外几分钟来使其精致和可展示的人。你已经完成了艰苦的工作,是时候让它光彩夺目了。

这里是我们要去的方向的预览,你可以在本文底部找到完整的代码:

作者提供的图片

在几乎所有其他 R 教程的风格中,我将使用内置的 iris 数据集。

1. 格式化你的代码块。

我不知道为什么包消息之类的默认情况下会显示,但我们今天已经停止了这一点。

作者提供的图片。

你可以选择将块选项应用于整个文档,也可以为特定块指定选项。

要设置全局选项,请创建一个这样的代码块:

{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, 
                      message = FALSE) 

如果你想为特定的代码块设置不同的选项,可以在任何块的第一行像这样做:

```{r message = TRUE}

# 你想运行的代码以及要显示的消息

```py

有很多选项和规范可以进行尝试,但仅这两个就可以产生很大的不同。

2. 为表格添加样式

如果你将表格保留在默认的 tibble 格式中,你的读者可能会感到困惑。有许多包可以立即提升表格的可读性

图片作者提供。

我个人最喜欢的是 kableExtra 包。完整的 kableExtra 指南可以在这里找到

iris %>%
  kableExtra::kable() %>%
  kableExtra::kable_styling("striped")

只需多加两行,就能从 ICK 变成 ahhh。

再次强调,kableExtra 中的格式化可能性几乎是无限的,所以我鼓励你查看这份指南并尝试一些适合你的样式。

3. 为长表格添加滚动条

是的,我知道这与#2 相关,尽管如此,我仍然认为值得单独提及。

令我烦恼的是,表格在.Rmd 文件中的显示方式通常与我希望它们在 html 文件中显示的方式完全一致。例如,我非常喜欢长表格在显示 10 行后被裁剪,然后可以用箭头查看额外的行。但是,当你编织你的文件时,整个表格会展开,你整洁的报告突然变成了一千页的无尽行。

我希望读者能够选择查看额外的数据行,但我不想强迫他们。

图片作者提供。

你猜是谁又来救场了?kableExtra(我保证我没有赞助)。进入scroll_box

iris %>%
  kableExtra::kable() %>%
  kableExtra::kable_styling("striped") %>% 
  kableExtra::scroll_box(width = "100%", height = "400px")

如你所见,你可以以像素或百分比指定宽度和高度。我喜欢宽度占满整个屏幕,所以我通常将宽度设置为 100%,400 像素可以显示大约 9 行以及列标题,这对我来说感觉刚好。

4. 添加主题

最后,终极技巧。如果你忽略我说的其他一切,这一调整可以让你从零到英雄,一秒钟就实现。优雅的天才们创造了 CSS 主题奇迹,使报告立刻显得更加专业。你可以在这里找到现成的主题示例

图片作者提供。

我个人最喜欢的是天蓝色。对你的 YAML 进行轻微调整,可以为报告添加主题:

title: Beautified RMarkdown
author: Jenna Eagleson
output: 
  html_document: 
    theme: cerulean

最后一行代码才是关键所在。

我们只是触及了表面,大家。正如所有 R 相关的事物一样,可能性确实是无限的。有没有你特别喜欢的 R Markdown 格式化技巧?有没有什么例子令你所有的审美感官都得到满足?

Jenna Eagleson 我的背景是工业组织心理学,我在人员分析领域找到了归属。数据可视化使我的工作充满生机。我喜欢使用 Power BI、R、Tableau 以及其他遇到的工具进行学习和开发。我很想了解更多关于你的经历!可以通过 Linkedin Twitter与我联系。

如果你想要更多这样的内容,可以使用我的链接以每月 5 美元的价格注册 Medium(我将获得少量佣金,对你没有额外费用)。

[## 通过我的推荐链接加入 Medium - Jenna Eagleson

阅读 Jenna Eagleson 的每一篇故事(以及 Medium 上的其他数千位作家的故事)。你的会员费直接支持……

jeagleson.medium.com](https://jeagleson.medium.com/membership?source=post_page-----f2bdccb358b2--------------------------------)

完整代码:

---
title: "Beautified RMarkdown"
author: Jenna Eagleson
output: 
  html_document:
    theme: cerulean
---

```{r setup, include=FALSE}

knitr::opts_chunk$set(echo = FALSE,

                    message = FALSE)

```py

# Data from Iris

Look how much nicer I look!

```{r}

library(tidyverse)

iris <- iris

iris %>%

kableExtra::kable() %>%

kableExtra::kable_styling("striped") %>%

kableExtra::scroll_box(width = "100%", height = "400px")

```py

一步步解决 4 个实际问题的指南:使用变压器和 Hugging Face

原文:towardsdatascience.com/4-real-life-problems-solved-using-transformers-and-hugging-face-a-complete-guide-e45fe698cc4d

了解变压器并利用它们的力量在 Python 中解决实际问题

Zoumana KeitaTowards Data Science Zoumana Keita

·发表于Towards Data Science ·阅读时间 11 分钟·2023 年 1 月 31 日

--

图片来源:Aditya VyasUnsplash

介绍

在自然语言处理(NLP)领域,研究人员在过去几十年里做出了显著贡献,带来了各种领域的创新进展。以下是 NLP 在实践中的一些例子:

  • Siri,由苹果公司开发的个人助理,可以帮助用户完成设置闹钟、发送短信和回答问题等任务。

  • 医疗领域,NLP 正被用来加速药物发现过程。

  • 此外,NLP 还被用于通过翻译来弥合语言障碍。

本文的目的是讨论变压器,这是一种在自然语言处理领域中极为强大的模型。它将首先强调变压器相对于循环神经网络的优势,以加深你对该模型的理解。接着,将提供使用 Huggingface 变压器在实际场景中的应用实例。

循环网络——变压器之前的辉煌时代

在深入了解变压器的基本概念之前,了解循环模型及其局限性是非常重要的。

循环网络使用编码器-解码器结构,通常用于处理按特定顺序输入和输出序列的任务。循环网络的一些最常见的应用包括机器翻译和时间序列数据建模。

循环网络的挑战

例如,让我们以一句法语句子为例,将其翻译成英语。编码器接收原始法语句子作为输入,解码器生成翻译输出。

递归网络的简单示意图(作者提供)

  • 编码器逐词处理输入的法语句子,而解码器以相同的顺序生成单词嵌入,这使得训练非常耗时。

  • 当前词的隐藏状态依赖于前面词的隐藏状态,这使得无法进行并行计算,无论计算能力多么强大。

  • 序列到序列的神经网络在网络过大时容易遇到梯度爆炸的问题,导致性能不佳。

  • 另一种递归网络——长短期记忆(LSTM)网络,被开发出来解决梯度消失的问题,但它们的速度甚至比传统序列模型还慢。

结合递归网络的优点并实现并行计算的模型难道不是很有用吗?

这就是变换器派上用场的地方。

在自然语言处理(NLP)中,变换器是什么?

2017 年,Google Brain 在其著名的研究论文《Attention is all you need》中介绍了变换器,这是一种新型强大的神经网络架构。它基于注意力机制,而不是递归网络中的序列计算。

变换器的主要组件是什么?

像递归网络一样,变换器也由两个主要组件组成:编码器和解码器,每个组件都包含一个自注意力机制。以下部分提供了对变换器每个组件主要元素的总体理解。

一般 变换器架构(作者改编)

输入句子预处理阶段

这一部分包括两个主要步骤:(1)创建输入句子的嵌入,和(2)计算输入句子中每个单词的位置信息。这些计算在输入句子(编码器块之前)和输出句子(解码器块之前)中以相同的方式进行。

输入数据的嵌入

在创建输入数据的嵌入之前,我们首先对其进行分词,然后为每个单词创建嵌入,而不考虑它们在句子中的关系。

位置编码

分词过程消除了输入句子中存在的任何连接感。位置编码旨在通过为每个单词创建上下文向量来恢复原始的循环特性。

编码器块

通过前面的步骤,我们为每个单词得到两个向量的组合:(1)嵌入向量和(2)上下文向量。这些向量被结合起来,为每个单词创建一个单一向量,然后送到编码器中。

多头注意力

如前所述,所有的关系感都丧失了。注意力层的目的是识别输入句子中不同单词之间的上下文连接。这个步骤最终会为每个单词创建一个注意力向量。

位置相关前馈网络(FFN)

在这一步中,应用前馈神经网络于每个注意力向量,将其转换为可以由解码器中的下一个多头注意力层使用的格式。

解码器块

解码器块由三个主要层组成:一个被遮蔽的多头注意力层、一个多头注意力层和一个位置相关的前馈网络。后两个层与编码器中的层类似。

解码器在训练过程中使用,它接受两个输入:正在翻译的输入句子的注意力向量和对应的英文目标句子。

那么,被遮蔽的多头注意力层负责什么呢?

在生成下一个英文单词时,网络可以使用来自法语单词的所有单词。然而,当处理目标序列中的某个单词(英文翻译)时,网络只需访问之前的单词,因为让下一个单词可用会导致网络“作弊”,而不去真正努力学习。这就是被遮蔽的多头注意力层发挥作用的地方。它通过将这些下一个单词转换为零来遮蔽它们,以便注意力网络不能使用这些单词。

被遮蔽的多头注意力层的结果通过其余的层,以生成概率分数来预测下一个单词。

自然语言处理中的迁移学习

训练从头开始的深度神经网络(如变换器)不是一件容易的事,并且可能会面临以下挑战:(1)寻找目标问题所需的数据量可能非常耗时,以及(2)获得必要的计算资源,如 GPU,以训练如此深的网络可能会非常昂贵。

想象一下从头开始构建一个模型,以将曼丁哥语翻译成沃洛夫语,这两种语言都是低资源语言 收集与这些语言相关的数据是昂贵的。与其经历所有这些挑战,不如重新使用经过预训练的深度神经网络作为训练新模型的起点。

这些模型已经在一个巨大的数据语料库上进行了训练,由他人(道德人士、组织等)提供,并在语言翻译任务上(如法语到英语)评估效果非常好。

但是,你所说的重新使用深度神经网络是什么意思?

重新使用模型涉及选择一个与您的用例相似的预训练模型,细化目标任务的输入输出对数据,并使用您的数据对预训练模型的高层进行再训练。

变换器的引入导致了最先进的迁移学习模型的发展,如:

  • BERT,全称是 Bidirectional Encoder Representations from Transformers,由 Google 研究人员于 2018 年开发。它有助于解决最常见的语言任务,如命名实体识别、情感分析、问答、文本总结等。

  • GPT3(生成预训练变换器 3),由 OpenAI 研究人员提出。它是一个多层 transformer,主要用于生成各种类型的文本。GPT 模型能够生成类似人类的文本响应来回答给定的问题。

Hugging Face Transformers 介绍

Hugging Face 是一个人工智能社区和机器学习平台,由 Julien Chaumond、Clément Delangue 和 Thomas Wolf 于 2016 年创建。它的目标是通过为数据科学家、人工智能从业者和工程师提供对 20000 多个基于最先进的 transformer 架构的预训练模型的即时访问,来实现自然语言处理的民主化。这些模型可以应用于:

  • 100 多种语言的文本,用于执行分类、信息提取、问答、生成、生成和翻译等任务。

  • 语音,用于对象音频分类和语音识别等任务。

  • 用于对象检测、图像分类和分割的视觉。

Hugging Face transformers 还提供了近 2000 个数据集和分层 API,使程序员可以使用三种最流行的深度学习库:Pytorch、Tensorflow 和 Jax,轻松与这些模型进行交互。

Hugging Face transformers 的其他组件是 Pipelines。这些对象抽象了库中的代码复杂性,使得使用所有这些模型进行推理变得简单。

Hugging Face 教程

现在你对 transformers 和 Hugging Face 平台有了更好的理解,我们将带你了解以下实际场景:序列分类、语言翻译、文本生成、问答、命名实体识别和文本总结。

前提条件

第一步是按照如下步骤安装 transformers 库:

pip install transformers

我们将使用来自 Kaggle 的互联网新闻和消费者互动数据集。该数据集由 CC0: 公共领域 免费提供,旨在预测文章在发布前的受欢迎程度。

为了简化教程,本教程将仅使用数据中的三个示例,分析基于描述列。

import pandas as pd

# Create wrapper to properly format the text
from textwrap import TextWrapper

# Wrap text to 80 characters.
wrapper = TextWrapper(width=80)

# Load the data
news_data = pd.read_csv("consumer_engagement_data.csv")

# Choose candidate descriptions
description_76 = news_data.iloc[76]["description"]
description_118 = news_data.iloc[118]["description"]
description_178 = news_data.iloc[178]["description"]

english_texts = [description_76, description_118, description_178]

for english_text in english_texts:
    print(wrapper.fill(english_text))
    print("\n")

英文原新闻(图片由作者提供)

语言翻译

MarianMT 是一个高效的机器翻译框架。它使用了MarianNMT引擎,该引擎完全由微软和许多学术机构(如爱丁堡大学和波兹南的亚当·密茨凯维奇大学)用 C++开发。目前,同一引擎也用于微软翻译器服务。

赫尔辛基大学的 NLP 小组在 Hugging Face Transformers 上开源了多个翻译模型,它们的格式都是Helsinki-NLP/opus-mt-{src}-{tgt},其中{src}{tgt}分别对应源语言和目标语言。

所以,在我们的案例中,源语言是英语(en),目标语言是法语(fr)。

MarianMT 是那些使用 Marian 在Opus上收集的平行数据上进行过训练的模型之一。

  • MarianMT 除了 Transformers 外还需要sentencepiece
pip install sentencepiece
from transformers import MarianTokenizer, MarianMTModel
  • 选择预训练模型,获取分词器并加载预训练模型。
# Get the name of the model
trans_model_name = 'Helsinki-NLP/opus-mt-en-fr'
# Get the tokenizer
trans_model_tkn = MarianTokenizer.from_pretrained(trans_model_name)
# Instanciate the model
trans_model = MarianMTModel.from_pretrained(trans_model_name)
  • 使用以下函数在每个源(英语)文本前添加特殊标记>>{tgt}<<

  • 使用以下函数实现批量翻译逻辑,批量是要翻译的文本列表。

翻译文本

零样本分类

大多数情况下,训练机器学习模型需要事先知道所有候选标签/目标,这意味着如果你的训练标签是科学政治教育,你将无法预测医疗标签,除非重新训练你的模型,考虑到该标签和相应的输入数据。

这种强大的方法使得我们可以在大约 15 种语言中预测文本的目标,而无需看到任何候选标签。我们只需从中心加载模型即可使用它。

这里的目标是尝试对每个先前描述的类别进行分类,无论是技术政治安全还是金融

  • 导入管道模块
from transformers import pipeline
  • 定义候选标签。这些标签对应于我们想要预测的内容:技术政治商业金融
candidate_labels = ["tech", "politics", "business", "finance"]
  • 使用多语言选项定义分类器
my_classifier = pipeline("zero-shot-classification",       
                         model='joeddav/xlm-roberta-large-xnli')
  • 使用这个辅助函数实现预测逻辑。

  • 对第一个和最后一个描述进行预测

#For the first description
prediction_desc_76 = run_predictions(english_texts[0])

print(wrapper.fill(prediction_desc_76["Text"]))

原始文本描述

print(prediction_desc_76["Result"])

预测为主要涉及金融的文本(作者提供的图像)

这个结果显示文本总体上是关于金融的,比例为 81%。

对最后一个描述,我们得到以下结果:

#For the last description
prediction_desc_178 = run_predictions(english_texts[-1])

print(wrapper.fill(prediction_desc_178["Text"]))

print(prediction_desc_178["Result"])

预测为主要涉及技术的文本(作者提供的图像)

之前的结果显示文本总体上关于技术,可信度为 95%。

情感分类

大多数进行情感分类的模型需要适当的训练。Hugging Face 的 pipeline 模块通过指定在中心可用的特定模型的名称,使情感分析预测变得简单。

  • 加载 pipeline 模块
from transformers import pipeline
  • 选择要执行的任务并加载相应的模型。在这里,我们想要执行情感分类,使用 distill BERT base 模型。
distil_bert_model = pipeline(task = "sentiment-analysis", 
                          model="distilbert-base-uncased-finetuned-sst-2-english")
  • 模型已经准备好了!让我们分析最后两句话背后的潜在情感。
# Run the predictions
distil_bert_model(english_texts[1:])

情感评分(作者提供的图片)

该模型预测第一段文本的情感为负面,置信度为 96%,第二段则预测为正面,置信度为 52%。

问答

想象一下处理一份比苹果公司报告长得多的报告。你唯一感兴趣的只是提到事件的日期。与其阅读整个报告来找到关键信息,我们可以使用 Hugging Face 的问答模型,它将提供我们感兴趣的答案。

这可以通过向模型提供适当的上下文(苹果公司的报告)以及我们感兴趣的问题来实现。

  • 从 transformers 导入问答类和 tokenizer
from transformers import AutoModelForQuestionAnswering, AutoTokenizer
  • 使用模型名称及其 tokenizer 实例化模型。
model_name = "deepset/roberta-base-squad2"
QA_model = pipeline('question-answering', model=model_name, 
                     tokenizer=model_name)
  • 通过提出问题并指定上下文来请求模型。
QA_input = {
           'question': 'when is Apple hosting an event?',
           'context': english_texts[-1]
           }
  • 获取模型结果
model_response = QA_model(QA_input)
pd.DataFrame([model_response])

问答结果(作者提供的图片)

模型回答说苹果公司的事件是在 9 月 10 日,置信度为 97%。它甚至通过提供开始和结束位置来指定答案在文本中的位置。

结论

在本文中,我们探讨了自然语言技术从递归网络到 transformers 的演变,以及 Hugging Face 如何通过其平台使 NLP 使用变得民主化。

如果你仍然犹豫是否使用 transformers,我们认为是时候尝试一下,给你的业务案例增添价值。

如果你喜欢阅读我的故事并希望支持我的写作,可以考虑成为 Medium 会员。通过每月 $5 的承诺,你可以无限制地访问 Medium 上的故事。

欢迎在MediumTwitterYouTube上关注我,或者在LinkedIn上打个招呼。讨论 AI、ML、数据科学、NLP 和 MLOps 话题总是很愉快!

文章你应该考虑使用 Datatable 而不是 Pandas 来处理大数据吗?可能是一个好的下一步。

我不会签署“生存风险”新声明的四个理由

原文:towardsdatascience.com/4-reasons-why-i-wont-sign-the-existential-risk-new-statement-ef658ec699ca

意见

煽动恐惧是一场危险的游戏

Rafe Brena, Ph.D.Towards Data Science Rafe Brena, Ph.D.

·发表于Towards Data Science ·阅读时间 5 分钟·2023 年 5 月 31 日

--

图片由Cash Macanaya提供,来源于Unsplash

新声明是什么?

几周前,我发表了关于签署那份广为人知的未来生命研究所公开信的赞成和反对论点——最终,我签署了它,尽管有一些保留意见。一些广播和电视主持人采访了我,解释了所有这些争论的根源。

最近,我收到了来自未来生命研究所(以下称 FLI)的一封邮件,请我签署一份声明:这次是由人工智能安全中心(CAIS)发布的简短声明,集中在最近人工智能发展带来的生存威胁上。

声明内容如下:

缓解人工智能带来的灭绝风险应成为全球优先事项,与大流行病和核战争等其他社会规模风险并列。

确实非常简洁;这有什么问题呢?

为什么这不可信?

如果之前的 FLI 声明存在弱点,这份声明不仅没有纠正这些弱点,反而进一步加重了这些问题,使我无法支持它。

特别是,我有以下四个异议,这些异议肯定会比声明本身长一些:

1. 它误置了人工智能的真正风险,并转移了注意力

新声明本质上是对 AI 的恐慌呼吁,不仅仅是对我们现在能看到的自然后果感到恐慌,而是对那些由随机人物提出的非常模糊的风险估计,如“10%的人类灭绝风险”感到恐慌。

真的吗?10%的人类灭绝风险?基于什么?调查对象没有被要求解释或说明他们的理由,但我怀疑许多人在思考类似“终结者”的场景。你知道,恐怖片的目的是吓唬你,所以你去看电影。但将这些信息转化为现实是不合理的。

对人类的所谓威胁假设了一种尚未解释的毁灭能力和一种代理——消灭人类的意愿。当设备没有任何情感时,它们怎么会想要杀死我们呢?机器并不“想要”这个或那个。

我们现在看到的 AI 的真正危险是非常不同的。其中之一是生成 AI 制造虚假声音、图片和视频的能力。你能想象如果你收到一个假冒的你女儿声音的电话,她要求你去救她,你会怎么做吗?

另一个是带有伪证的公共错误信息,比如伪造的视频。那个伪造的教皇相对无害,但很快,Twitter 将被充斥着虚假的声明、关于从未发生过事件的图像等等。顺便问一下,你考虑过美国大选正在临近吗?

还有人工内容的利用,AI 算法在互联网上挖掘这些内容以生成其“原创”图像和文本:人类的工作被拿走而没有任何经济补偿。在某些情况下,对人类工作的引用是明确的,比如“将此图像制作成 X 的风格”。

人类与机器不是框架 AI 风险的正确方式。

如果一个月前的 FLI 信中有“人类与机器”心态的建议,这次被明确指出了。“来自 AI 的灭绝,”他们称之为,毫不含糊。

在我们生活的现实世界里——而非末日题材的好莱坞电影——并不是机器对我们造成了伤害或威胁我们的存在:更像是一些人(偶然地,强大和富有的那些,大公司的老板)利用新兴的强大技术来增加他们的财富,往往是以无权者的代价:我们看到计算机生成图像的可用性如何缩小了像 Fiverr 这样的地方图形艺术家的小生意。

此外,高级机器智能试图推翻人类的假设也需要质疑;正如斯蒂芬·平克所写:

“AI 反乌托邦将狭隘的 Alpha 雄性心理投射到智能的概念上。他们假设超人类智能的机器人会发展出像推翻主人或统治世界这样的目标。”

著名的 Meta AI 研究负责人扬·勒昆宣称:

“人类有各种各样的驱动因素使他们对彼此做坏事,比如自我保护的本能……这些驱动因素被编程到我们的大脑中,但完全没有理由去制造具有相同驱动因素的机器人。”

不,机器失控不会成为我们的统治者或灭绝我们:其他人类,即目前的统治者,将通过利用他们所拥有的经济和技术手段——包括适合的 AI——来增强他们的统治。

3. 将 AI 与疫情和核战争进行比较是不成立的

我明白 FLI 提到疫情是为了将声明与我们刚刚经历的事情联系起来——这在许多人中留下了情感伤痕——但这并不是一个有效的比较。撇开一些阴谋论不谈,我们走出的疫情并不是技术——疫苗才是。FLI 如何假设灾难性 AI 会传播?通过传染吗?

当然,核弹是一项技术进步,但在核战争的情况下,我们清楚地知道核弹会如何以及为什么摧毁我们:这不是猜测,就像“流氓 AI”的情况一样。

4. 来自带来风险的公司领导正在签署这一声明

另一个引起我注意的事项是看到签署声明的人员名单,从 Sam Altman 开始。他是 OpenAI 的领导者,自 2022 年 11 月以来,ChatGPT 使我们所经历的狂热 AI 竞赛启动。即使是强大的 Google 也难以跟上这场竞赛——微软的 Satya Nadella 难道没有说过他想要“让 Google 跳舞”吗?他达成了愿望,却以加速 AI 竞赛为代价。

我不理解那些在推动 AI 竞赛的公司领导者同时签署这个声明的逻辑。Altman 可以说他对 AI 的发展非常担忧,但如果我们看到他的公司仍然全速前进,那么他的担忧看起来就显得毫无意义且不协调。我不打算对 Altman 的声明进行道德评判,但接受他的支持而不加质疑会削弱声明的有效性——尤其是考虑到对 Altman 的公司来说,领导竞赛对其财务底线至关重要。

最后的思考

并不是机器失控,而是资本主义垄断和专制政府对 AI 工具的使用可能对我们造成伤害。这不仅仅是在好莱坞的反乌托邦未来,而是在我们今天所处的现实世界中。

我不会支持这种由恐惧驱动的机器愿景,因为这终究是虚伪的,因为它是由那些试图转移其追求利润的公司提出的。这就是为什么我没有签署由 FLI 支持的新声明。

此外,我怀疑富有和有影响力的领导者可以允许自己审视虚构的威胁,因为他们不会担心像自由职业平面设计师的收入减少这样更“平凡”的现实威胁:他们非常清楚自己不会在月底面临经济困难,他们的孩子或孙子也不会。

改善数据治理团队的 4 种革命性方法

原文:towardsdatascience.com/4-revolutionary-ways-to-improve-your-data-governance-team-56acdb6f1ab5

使用这 4 种变革性方法提升您的数据治理实践。

Hanzala QureshiTowards Data Science Hanzala Qureshi

·发布在数据科学的前沿 ·阅读时间 5 分钟·2023 年 7 月 24 日

--

照片由 Shubham Dhage提供,发布在Unsplash上。

如果没有有效的治理,组织将发现自己在数字化的荒野中徘徊。

最近,欧盟人工智能法案的进展也指出,对于处理高风险 AI 系统的组织来说,数据治理是必不可少的,以保持合规。尽管数据规模不断扩大,组织却在治理努力上退步,导致数据治理实践变得更加薄弱。

让我们探讨使用现代技术的四种方法,以改进您组织的数据治理。

1. 用副驾驶取代数据管家

在我多年的客户数据治理实施过程中,我还没遇到一个满意的数据管家。

他们面临着将数据转化为业务及反向转换的艰巨任务,却几乎没有权力来强制执行合规性。失败的原因通常包括治理模型设计不良、高层管理支持不足或将治理视为一次性项目,投资最小。

利用 LLM 的副驾驶可以创建政策文档,捕捉业务和技术元数据,验证数据创建是否符合已商定的标准等。即使您不能投资新工具,副驾驶也可以是一个 Slack/Teams Bot,连接到您组织的元数据工具,简单地回答"常见问题",如数据的所有者是谁,哪个数据是主数据,某列的定义是什么等,以供最终用户查询。

这对数据团队有何影响?

数据负责人可以通过减少全职资源来找到节省成本的方法。数据工程师可以确保足够的元数据被记录并传递给副驾驶工具,从而防止最终用户的查询流向他们。数据分析师/科学家可以依赖副驾驶工具来回答足够的问题,以帮助他们进行分析查询。

2. 减少论坛和委员会,并在决策中加入智能

论坛从未能做到 100%的高效;总有一些人参加只是为了在电话结束时说“谢谢,再见!”。

一个每周的数据工作组和元数据论坛,一个每两周的数据质量论坛,以及一个每月的数据委员会会议。虽然是出于良好的意图开始的,但大多数这些论坛往往有类似的听众,重复相同或类似的信息。

决策矩阵和智能工作流自动化应该能够处理 90%以上的来袭数据治理请求。例如,如果请求是关于个人位置数据的访问,最终的业务用例是人口统计分析。治理工具可以使用如下的矩阵来生成答案。

作者提供的图片 — 示例决策矩阵

这对数据团队有什么影响?

数据团队可以减少在论坛上的时间,把精力集中在战略决策上。数据分析师/科学家可以依赖治理工具(这也可以是一个 Slack/Teams 机器人)提供的智能决策输出,以了解数据是否可访问。

3. 使用语义嵌入智能分类和标记数据

对数据进行分类和标记为数据治理决策添加了急需的上下文。

从历史上看,对数据进行分类一直是最具挑战性的工作之一。这要么是资源密集型的,因为有人必须手动查看数据资产并确定其分类,要么是技术密集型的,需要不断扫描数据以跟踪变化和更新。

语义嵌入正在革新上下文。对于每个数据资产,可以以向量的形式创建一个上下文嵌入。通过使用相似度评分,这些向量可以按分类排名。例如,涉及客户的数据,如地址、出生日期等,可以通过向量相似度搜索归类为“个人数据”。这也适用于非结构化的数据如文档

这对数据团队有什么影响?

数据治理团队可以减少手动分类和标记数据的时间。可以围绕这些数据分类构建智能工作流,以提供决策支持,例如“数据是否可以访问?”、“数据是否个人或敏感?”。这些信息可以通过 Co-Pilots 提供给数据分析师/科学家,并用于决策,如 1 和 2 所讨论的。

4. 数据产品市场用于编目、发现和访问数据

数据产品市场可以成为您所有数据需求的一站式商店。

我们在数据管理上各自为政,一个工具用于工程,另一个用于管理,还有一个用于治理,还有一个用于隐私。组织通常需要提高在整个系统中整合数据治理的能力,特别是当系统敏捷且不断变化时。

市场是您用于编目和发现数据的区域,它将与您所有不同的数据资产连接。上述所有技术的汇总可以集中到数据产品市场中。例如,包含客户信息的表格将被编目到市场中,使用语义嵌入(3)进行分类和标记,通过智能治理工作流(2)进行访问,并由 Co-Pilot(1)进行上下文化。

这对数据团队有什么影响?

数据科学家可以轻松地在市场中找到数据,并通过 Co-Pilot 确定数据是否可用。数据工程师可以重用市场中的数据,而不是创建更多的重复数据。数据分析师可以使用市场中数据的概要来确定其是否适合他们的用例。数据领导者可以依赖一个平台来满足他们的治理和报告需求。

结论

现代数据治理功能的目标是减少对人力的依赖,更贴近最终用户,作为帮助者而非障碍和繁文缛节。

尽管我提出了许多新的 AI 技术来利用这些,但有一件事将永远重要:打好基础。如果您的数据质量差,您不需要 Co-Pilot 来重复这一消息。

实现数据质量的正确性是最难完成的任务之一,但因此,也是最显著的投资回报。查看我的免费《终极数据质量手册》,帮助您打好基础,并加入我的邮件列表。

## 终极数据质量手册 - 免费!

介绍《终极数据质量手册:完善数据的最佳实践》。在数据驱动的世界中……

hanzalaqureshi.gumroad.com

4 个你可能不知道的有用 BigQuery SQL 函数

原文:towardsdatascience.com/4-useful-bigquery-sql-functions-you-may-not-know-82e830d994ca

以及如何使用它们

Vicky YuTowards Data Science Vicky Yu

·发表于 Towards Data Science ·4 分钟阅读·2023 年 1 月 26 日

--

图片来源:Priscilla Du PreezUnsplash

作为长期的 SQL 用户,我总是在寻找简化 SQL 数据分析的方法。在上一篇文章中,我回顾了 6 个 BigQuery SQL 函数我希望自己早些知道的函数,今天我想分享另外 4 个希望你会觉得有用的函数。

1. PERCENTILE_CONT

PERCENTILE_CONT 计算列中值的百分位数。BigQuery 没有 MEDIAN 函数,但你可以使用 PERCENTILE_CONT 来计算中位数,因为它等同于第 50 百分位数。计算中位数和百分位数对于了解数据分布和确定可能影响分析的异常值非常有用。

在下面的示例中,我有一个包含 6 个数字(1, 3, 5, 8, 10 和 1000)的数组,这些数字通过 UNNEST 函数展开成行。第 4 行使用 0.5 作为参数来计算中位数,表示第 50 百分位数,第 5 行使用 0.95 来计算第 95 百分位数。注意结果显示 95 百分位数752,而 25 百分位数3.5中位数6.5。这表明异常值可能需要在分析中被去除,因为差异非常大。

使用 PERCENTILE_CONT 查询示例的截图由作者创建

2. COUNTIF

COUNTIF 函数在满足条件时计数。这在从表中获取不同条件的计数时很有用,而不必运行多个 SQL 查询。

在下面的示例中,为了获取负数和正数的计数,我可以使用两种不同的 where 条件在第 3 行和第 7 行运行查询两次。

作者创建的包含 COUNT 查询示例的截图

然而,使用COUNTIF我可以一次运行这个查询,以在第 1 行和第 2 行获取正负数的计数。

作者创建的包含 COUNTIF 查询示例的截图

3. IF

IF 是一种替代 CASE 的方法,当你只需要评估一个条件是否为真或假时。在第 10 行,如果 A 小于 B 且 A 大于 0,则result_from_if列设置为true,否则result_from_if设置为false。在第 11 行,使用 CASE 可以得到相同的结果,但与使用 IF 函数相比,表达式更长。

作者创建的包含 IF 和 CASE 查询示例的截图

注意:你可以将 COUNT 和 IF 与 DISTINCT 结合使用,以获取满足条件的唯一值计数。在下面的示例中,仅计数正数(1、3 和 4),DISTINCT 返回计数为 3。

作者创建的包含 COUNT、IF 和 DISTINCT 查询示例的截图

4. ERROR

ERROR 对于警告数据中的意外值非常有用。在下面的示例中,当 CASE 遇到除catdog之外的值时,在第 4 行使用了ERROR函数。这在故障排除时很有用,尤其是在 ETL 管道中,你可能希望 SQL 失败,如果列中出现需要调查的意外值。

作者创建的包含 ERROR 查询示例的截图

最终思考

虽然我提到的函数在 BigQuery 中可用,但它们可能在其他数据库中也可用,但名称不同。例如,COUNTIF 在 Snowflake 中是 COUNT_IF。如果你有几分钟时间,我强烈建议阅读你的数据库文档,因为你永远不知道会发现哪些有用的函数。

注意:上述所有查询均在 BigQuery sandbox 上运行,任何拥有 Google 账户的人均可免费使用。

## 4 个 BigQuery SQL 快捷键可以简化你的查询

检查你的数据库是否也有这些函数

towardsdatascience.com ## 6 个 BigQuery SQL 函数每个用户都应该知道

检查你的数据库是否也有这些函数

towardsdatascience.com ## BigQuery SQL 程序语言以简化数据工程

介绍

towardsdatascience.com

LangChain 中的 4 种问题回答方式

原文:towardsdatascience.com/4-ways-of-question-answering-in-langchain-188c6707cc5a?source=collection_archive---------0-----------------------#2023-04-08

与长 PDF 文档对话:load_qa_chain、RetrievalQA、VectorstoreIndexCreator、ConversationalRetrievalChain

Sophia Yang, Ph.D.Towards Data Science Sophia Yang, Ph.D.

·

关注 发表在 Towards Data Science ·6 分钟阅读·2023 年 4 月 8 日

--

你是否对与自己的文档对话感兴趣,无论是文本文件、PDF 还是网站?LangChain 使得在文档中进行问题回答变得简单。但你知道在 LangChain 中至少有 4 种方式可以进行问题回答吗?在这篇博客文章中,我们将深入探讨四种不同的提问方式以及你可以考虑的各种选项。

在我们深入探讨问答之前,你可能会问:什么是 LangChain?好问题!在我看来,LangChain 是与语言模型互动并构建应用程序的最简单方法。它是一个开源工具,封装了许多 LLM 和工具。查看我之前的 博客文章 和 视频 以了解 LangChain 的概述。

好的,现在让我们开始对外部文档进行问答。

代码:查看本博客文章的代码 这里

设置 OpenAI API

在 OpenAI 上创建一个帐户并生成 API 密钥:platform.openai.com/account。请注意,OpenAI API 并非免费。你需要在此处设置账单信息才能使用 OpenAI API。或者,你可以使用 HuggingFace Hub 或其他地方的模型。查看我之前的 博客文章 和 视频 来了解如何使用其他模型。

import os 
os.environ["OPENAI_API_KEY"] = "COPY AND PASTE YOUR API KEY HERE"

加载文档

LangChain 支持许多 文档加载器,例如 Notion、YouTube 和 Figma。在这个例子中,我想与我的 PDF 文件进行对话。因此,我使用了 PyPDFLoader 来加载我的文件。我实际使用的是 AI index report 的第一章,其中包括 55 页,并将其保存在我 GitHub 的材料目录中。

# load document
from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("materials/example.pdf")
documents = loader.load()

方法 1:load_qa_chain

load_qa_chain 提供了最通用的问答接口。它加载一个链,你可以对输入文档进行问答,并使用文档中的所有文本。

它还允许你对一组文档进行问答:

### For multiple documents 
loaders = [....]
documents = []
for loader in loaders:
    documents.extend(loader.load())

🤔但如果我的文档非常长,超出了令牌限制怎么办?

有两种解决方法:

解决方案 1:链式类型

默认的 chain_type="stuff" 使用了文档中所有的文本。实际上,它不适用于我们的例子,因为它超出了令牌限制并导致速率限制错误。这就是为什么在这个例子中,我们不得不使用其他链式类型,比如 "map_reduce"。还有哪些其他链式类型?

  • map_reduce:它将文本分成批次(例如,你可以在 llm=OpenAI(batch_size=5) 中定义批次大小),分别将每个批次的问题传递给 LLM,并根据每个批次的答案得出最终答案。

  • refine:它将文本分成批次,将第一个批次传递给 LLM,然后将答案和第二个批次传递给 LLM。它通过处理所有批次来完善答案。

  • map-rerank:它将文本分成批次,逐个批次地输入到 LLM 中,返回每个批次对问题的回答完整程度的评分,并根据每个批次中高评分的回答得出最终答案。

解决方案 2:RetrievalQA

使用所有文本的一个问题是,成本可能非常高,因为你将所有文本都传送给 OpenAI API,而 API 是按令牌数量收费的。更好的解决方案是先检索相关文本块,然后仅在语言模型中使用相关文本块。接下来,我将详细介绍 RetrievalQA。

方法 2:RetrievalQA

RetrievalQA 链实际上在底层使用 load_qa_chain。我们检索最相关的文本块,并将这些文本块输入到语言模型中。

其工作原理如下:

from langchain.chains import RetrievalQA
from langchain.indexes import VectorstoreIndexCreator
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma

# split the documents into chunks
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
# select which embeddings we want to use
embeddings = OpenAIEmbeddings()
# create the vectorestore to use as the index
db = Chroma.from_documents(texts, embeddings)
# expose this index in a retriever interface
retriever = db.as_retriever(search_type="similarity", search_kwargs={"k":2})
# create a chain to answer questions 
qa = RetrievalQA.from_chain_type(
    llm=OpenAI(), chain_type="stuff", retriever=retriever, return_source_documents=True)
query = "How many AI publications in 2021?"
result = qa({"query": query})

在结果中,我们可以看到答案和两个源文档,因为我们将 k 定义为 2,这意味着我们只关心获取两个相关的文本块。

选项:

在这个过程中你可以选择各种选项:

  • 嵌入:在示例中,我们使用了 OpenAI 嵌入。但还有许多其他嵌入选项,如 Cohere Embeddings 和来自特定模型的 HuggingFaceEmbeddings。

  • 文本分割器:在示例中,我们使用了按单个字符分割的 Character Text Splitter。你也可以使用不同的文本分割器和不同的令牌,具体请参见此 文档

  • 向量存储:我们使用了 Chroma 作为我们的向量数据库,用于存储嵌入的文本向量。其他流行的选项包括 FAISS、Milvus 和 Pinecone。

  • 检索器:我们使用了一个 VectoreStoreRetriver,它由 VectorStore 支持。为了检索文本,你可以选择两种搜索类型:search_type:“similarity” 或 “mmr”。search_type="similarity" 使用检索器对象中的相似性搜索,其中选择最与问题向量相似的文本块向量。search_type="mmr" 使用最大边际相关性搜索,它在优化查询相似性和选择文档的多样性之间取得平衡。

  • 链类型:与方法 1 相同。你也可以将链类型定义为四种选项之一:“stuff”、“map reduce”、“refine”、“map_rerank”。

方法 3:VectorstoreIndexCreator

VectorstoreIndexCreator 是上述功能的封装器。它在底层功能上完全相同,但提供了更高级的接口,让你可以用三行代码开始使用:

当然,你也可以在这个包装器中指定不同的选项。

方法 4:ConversationalRetrievalChain

ConversationalRetrievalChain 与方法 2 RetrievalQA 非常相似。它增加了一个额外的参数 chat_history 用于传递聊天记录,这可以用于后续的问题。

ConversationalRetrievalChain = 对话记忆 + RetrievalQAChain

如果你希望你的语言模型记住之前的对话,可以使用这种方法。在我下面的例子中,我询问了 AI 发表的数量,并得到了 500,000 的结果。然后我让 LLM 将这个数字除以 2。由于它拥有所有聊天记录,模型知道我指的是 500,000,因此返回的结果是 250,000。

结论

现在你知道了四种使用 LangChain 中 LLM 进行问答的方法。总结起来,load_qa_chain 使用所有文本并接受多个文档;RetrievalQA 在底层使用 load_qa_chain,但首先检索相关的文本块;VectorstoreIndexCreator 与 RetrievalQA 相同,但提供了更高层次的接口;ConversationalRetrievalChain 在你想将聊天记录传递给模型时非常有用。

致谢

感谢 Harrison Chase 的指导!

照片由 FLY:D 提供,来源于 Unsplash

. . .

Sophia Yang 提供,日期为 2023 年 4 月 8 日

Sophia Yang 是高级数据科学家。可以在 LinkedInTwitterYouTube 上与我联系,并加入 DS/ML 书友会 ❤️

4 种编码具有高基数的分类特征的方法——带 Python 实现

原文:towardsdatascience.com/4-ways-to-encode-categorical-features-with-high-cardinality-1bc6d8fd7b13?source=collection_archive---------0-----------------------#2023-06-26

学习如何使用 scikit-learn 和 TensorFlow 应用目标编码、计数编码、特征哈希和嵌入

Aicha BokbotTowards Data Science Aicha Bokbot

·

关注 发表在Towards Data Science · 9 分钟阅读·2023 年 6 月 26 日

--

“点击” — 照片由Cleo Vermij提供,发布在Unsplash

在本文中,我们将深入探讨 4 种流行的方法来编码具有高基数的分类变量:(1)目标编码,(2)计数编码,(3)特征哈希(4)嵌入

我们将解释每种方法的工作原理,讨论其优缺点,并观察其对分类任务性能的影响。

目录

— 引入类别特征

(1) 为什么我们需要编码类别特征? (2) 为什么一热编码不适用于高基数?

— 在 AdTech 数据集上的应用

— 每种编码方法概述

(1) 目标编码 (2) 计数编码 (3) 特征哈希 (4) 嵌入

— 预测 CTR 的性能基准测试

— 结论

— 进一步阅读

引入类别特征

类别特征是一种描述类别或组的变量(如性别、颜色、国家),而数值特征则测量数量(如年龄、身高、温度)。

类别数据有两种类型:有序特征,其类别可以排序(如 T 恤尺码或餐厅评分从 1 星到 5 星),和名义特征,其类别不具有任何有意义的顺序(如人的名字、城市名)。

我们为什么需要编码类别特征?

编码类别变量意味着找到一种映射,将类别转换为数值。

虽然一些算法可以直接处理类别数据(如决策树),但大多数机器学习模型无法处理类别特征,它们设计时只处理数值数据。对类别变量进行编码是一个必要的步骤。

此外,一些机器学习库要求所有数据必须是数值型的。例如,scikit-learn 就是这种情况。

为什么一热编码不适用于高基数?

编码类别特征的常见方法是应用一热编码。这种方法通过为每个唯一类别添加一个二元变量来对类别变量进行编码。

如果一个描述颜色的特征有三个类别[红色、蓝色、绿色],一热编码器将其转换为三个二元变量,每个类别一个。

如果一个类别特征有数百或数千个类别,应用一热编码将会向特征向量中添加数百或数千个二元变量。模型在面对大规模稀疏数据时会遇到维度灾难的问题:在更多维度的解空间中搜索更困难,容易过拟合,计算时间增加,以及空间复杂度上升。

那么如何在不增加特征向量维度的情况下编码高基数的类别特征呢?

在 AdTech 数据集上的应用

我们将通过在Criteo 展示广告挑战赛的数据集上应用四种编码技术来预测展示广告的点击率,来回答这个问题。

这是一个著名的 Kaggle 挑战赛,由Criteo(一家专注于程序化广告和实时竞价的法国在线广告公司)于 2014 年发起。广告的点击率(CTR)是广告被点击的次数与广告在页面上展示的次数之比。

广告技术中的数据集通常包含具有高基数的 ID 变量,例如 site_id(广告展示的网站 ID)、advertiser_id(广告背后的品牌 ID)、os_id(广告展示给用户的操作系统 ID)。

Criteo 数据集包含 100 万行,39 个匿名列:13 个数值变量和 26 个类别变量。它们的基数见下表。我们可以看到许多特征具有非常高的基数(超过 10k)

Criteo 数据集中类别特征的基数

数据集总共包含 241,338 个类别。应用独热编码意味着将特征空间从 39 维度转换为 241,351 维度。 显然,对一个超过 241k 列的稀疏矩阵进行计算是非常昂贵且低效的。

我们将数据集拆分为训练集和测试集,并探索编码方法。

from sklearn.model_selection import train_test_split

features = df.columns[1:]
categorical_features = [feature for feature in features if feature[0] == "C" ]
x_train, x_test, y_train, y_test = train_test_split(df[features], df["label"])

每种编码方法的概述

(1) 目标编码

我们使用了库 category_encoder 的目标编码器,其定义如下:

特征被替换为给定特定类别值的目标的预期值与所有训练数据上目标的预期值的混合。

from category_encoders.target_encoder import TargetEncoder

enc = TargetEncoder(cols = categorical_features).fit(x_train, y_train)
X_train_encoded = enc.transform(x_train)
X_test_encoded = enc.transform(x_test)

请注意,我们只在训练数据集上拟合编码器,然后使用拟合后的编码器转换训练集和测试集。由于在实际生活中我们无法访问 y_test,使用它来拟合编码器将是不诚实的。

  • 编码特征空间的维度: 39 列,X_train_encoded 和 X_test_encoded 的形状与 x_train 和 y_train 相同。

  • 优点:

    • 无参数

    • 特征空间没有增加

  • 缺点:

    • 目标泄漏的风险(目标泄漏指使用目标的一些信息来预测目标本身)

    • 当类别样本较少时,目标编码器会将它们替换为非常接近目标的值,这使得模型容易过拟合训练集。

    • 不接受测试集中的新值

(2) 计数编码

使用计数编码,也称为频率编码,类别被替换为它们在数据集中的频率。如果 ID 3f4ec687 在列 C7 中出现了 957 次,则我们将 3f4ec687 替换为 957。

如果两个类别在数据集中出现的次数相同,这种方法会用相同的值对它们进行编码,尽管它们不包含相同的信息。这会造成所谓的冲突:两个不同的类别被编码为相同的值。

from category_encoders.count import CountEncoder

enc = CountEncoder(cols = categorical_features).fit(x_train, y_train)
X_train_encoded = enc.transform(x_train)
X_test_encoded = enc.transform(x_test)
  • 编码特征空间的维度: 39 列,X_train_encoded 和 X_test_encoded 的形状与 x_train 和 y_train 相同。

  • 优点:

    • 易于理解和实现

    • 无参数

    • 特征空间没有增加

  • 缺点:

    • 当发生碰撞时存在信息丢失的风险

    • 可能过于简化(我们从分类特征中保留的唯一信息是它们的频率)

    • 在测试集中不接受新值

(3) 特征哈希

特征哈希将分类特征投影到一个固定维度的特征向量中,该维度比原始特征空间要小。这个特征向量的维度需要事先定义。这是通过使用哈希技巧,将哈希函数应用于特征,并将其哈希值作为特征向量中的索引来完成的。

两种实现特征哈希的方法

  • 或者我们逐个特征应用哈希(每个特征一个特征空间,因此我们需要为每个特征选择一个维度参数)

  • 我们将所有特征一起哈希(所有特征共享一个特征空间,选择一个参数,但特征之间可能会发生碰撞)。

这种方法不是无参数的,我们需要选择哈希空间的大小。我们遵循伟大文章 不要被哈希技巧欺骗 的建议,选择哈希大小 = 20**k*,其中 k 是分类特征的数量(在我们的例子中 k=26)。

from category_encoders.hashing import HashingEncoder

enc = HashingEncoder(
    cols = categorical_features, 
    n_components=20*len(categorical_features)
).fit(x_train, y_train)
X_train_encoded = enc.transform(x_train)
X_test_encoded = enc.transform(x_test)
  • 编码特征空间的维度: 533 列

  • 优点:

    • 特征空间的增加有限(与独热编码相比)

    • 由于不维护观察到的类别的字典,因此在推断过程中不会增长大小并接受新值

    • 当特征哈希应用于所有分类特征以创建单个哈希时,捕捉特征之间的交互

  • 缺点:

    • 需要调整哈希空间维度的参数

    • 当哈希空间的维度不足时存在碰撞风险

(4) 嵌入

嵌入是一种流行的编码技术,来自深度学习和自然语言处理(NLP)。它的核心是构建一个可训练的查找表,将类别映射到固定长度的向量表示。在训练过程中,查找表中的权重会被更新,以更好地描述类别之间的相似性。

我们将遵循这个 Keras 教程 来“构建一个编码器模型,将分类特征编码为嵌入向量,其中给定分类特征的嵌入大小是其词汇表大小的平方根。我们通过反向传播在一个简单的神经网络模型中训练这些嵌入向量。”

import tensorflow as tf

def build_input_layers(features):
    input_layers = {}
    for feature in features:
        if feature in categorical_features:
            input_layers[feature] = tf.keras.layers.Input(
                shape=(1,),
                name=feature,
                dtype=tf.string
            )
        else:
            input_layers[feature] = tf.keras.layers.Input(
                shape=(1,),
                name=feature,
                dtype=tf.float32
            )
    return input_layers

def build_embeddings(size=None):
    input_layers = build_input_layers(features)
    embedded_layers = []

    for feature in input_layers.keys():
        if feature in categorical_features:
            # Get the vocabulary of the categorical feature
            vocabulary = sorted(
                    [str(value) for value in list(x_train[feature].unique())]
                )
            # convert the string input values into integer indices
            cardinality = x_train[feature].nunique()
            pre_processing_layer = tf.keras.layers.StringLookup(
              vocabulary=vocabulary, 
              num_oov_indices=cardinality,
              name=feature+"_preprocessed"
            )
            pre_processed_input = pre_processing_layer(input_layers[feature])
            # Create an embedding layer with the specified dimensions
            embedding_size = int(math.sqrt(cardinality))
            embedding_layer = tf.keras.layers.Embedding(
                input_dim=2*cardinality+1,
                output_dim=embedding_size,
                name=feature+"_embedded",

            )
            embedded_layers.append(embedding_layer(pre_processed_input))   
        else:
            # return numerical feature as it is
            embedded_layers.append(input_layers[feature])

    # Concatenate all the encoded features.
    encoded_features = tf.keras.layers.Concatenate()([
                tf.keras.layers.Flatten()(layer) for layer in embedded_layers
            ])

    # Apply dropout.
    encoded_features = tf.keras.layers.Dropout(rate=0.25)(encoded_features)

    # Perform non-linearity projection.
    encoded_features = tf.keras.layers.Dense(
        units=size if size else encoded_features.shape[-1], activation="gelu"
    )(encoded_features)
    return tf.keras.Model(inputs=input_layers, outputs=encoded_features)

def build_neural_network_model(embedding_encoder):
    input_layers = build_input_layers(features)
    embeddings = embedding_encoder(input_layers)
    output = tf.keras.layers.Dense(units=1, activation="sigmoid")(embeddings)

    model = keras.Model(inputs=input_layers, 
                        outputs=output)

    model.compile(
        optimizer=tf.keras.optimizers.Adam(),
        loss=tf.keras.losses.BinaryCrossentropy(),
        metrics=[tf.keras.metrics.AUC()]
    )

    return model

embedding_encoder = build_embeddings(64)
neural_network_model = build_neural_network_model(embedding_encoder)

# Training
def build_dataset(x, y):
    dataset = {}
    for feat in features:
        if feat in categorical_features:
            dataset[feat] = np.array(x[feat]).reshape(-1,1).astype(str)
        else:
            dataset[feat] = np.array(x[feat]).reshape(-1,1).astype(float)

    return dataset, np.array(y).reshape(-1,1)

x_train, y_train = build_dataset(x_train, y_train)
x_test, y_test = build_dataset(x_test, y_test)
history = neural_network_model.fit(x_train, y_train, batch_size=1024, epochs=5)

X_train_encoded = embedding_encoder.predict(x_train, batch_size=1024)
X_test_encoded = embedding_encoder.predict(x_test, batch_size=1024)
  • 编码特征空间的维度: 64 列

  • 优点:

    • 特征空间的增加有限(与独热编码相比)

    • 在推断过程中接受新值

    • 捕捉特征之间的交互并学习类别之间的相似性

  • 缺点:

    • 需要调整嵌入大小的参数

    • 嵌入和逻辑回归模型不能在一个阶段中协同训练,因为逻辑回归不使用反向传播进行训练。相反,嵌入需要在初始阶段进行训练,然后作为静态输入用于决策森林模型。

预测 CTR 的性能基准

我们拟合了一个简单的逻辑回归模型来预测 CTR,并使用每种编码方法生成预测。

from sklearn.linear_model import LogisticRegression
model = LogisticRegression(max_iter=10000)
model.fit(X_train_encoded, y_train)
y_pred = model.predict(X_test_encoded)

我们计算了对数损失、AUC、召回率和预测 CTR 的平均值。结果见下表。数据集中的平均 CTR 为 22.6%。

我们首先注意到所有编码方法的 AUC 都相当低,这主要是因为我们使用了一个非常简单的模型,比逻辑回归更复杂的模型更适合这个任务。

前三种方法(目标编码、计数编码和哈希编码)未能让模型捕捉到足够的信号以预测 CTR:与真实平均值相比,平均预测 CTR 非常低,召回率也接近于零,AUC 接近 0.5 则表明模型几乎是随机的。使用嵌入的模型显示出最高的 AUC 和召回率,以及接近目标的平均预测 CTR。

结论

我们探讨了四种编码高基数分类数据的技术:目标编码、计数编码、特征哈希和嵌入。具体来说,我们学到了:

  • 高基数分类数据处理的挑战

  • 四种技术的优缺点

  • 如何将每种技术应用于分类任务中以预测点击率

进一步阅读

最大化利用你的数据科学学位的 4 种方法

原文:towardsdatascience.com/4-ways-to-get-the-most-out-of-your-data-science-degree-40815f6a311d

我如何平衡(1)享受我的学位和(2)为大学后的“现实世界”生活做好准备

Matt ChapmanTowards Data Science Matt Chapman

·发表于Towards Data Science ·阅读时间 9 分钟·2023 年 5 月 23 日

--

图片来源于Vadim SherbakovUnsplash

那是 2021 年 3 月,我刚刚收到了一封将改变我人生的电子邮件。

“我写信是为了通知你,我们希望向你提供 2021 年牛津大学的入学机会。”

我想我稍微尖叫了一下。或者做了个傻舞。也许两者都有。

进入牛津大学对我来说是个大事。我早已知道我想转行到数据科学领域,与我的一些课程同学不同,我并不是直接从本科毕业后进入这一领域。我已经毕业工作了几年,我进入这个领域有两个非常具体的目标:

  1. 享受它

  2. 为在行业中作为数据科学家做好准备

然而,我很快发现,平衡这两个目标在学位中是一项相当困难的平衡任务。在这篇文章中,我将分享 4 件对我有效的事情,希望它们也能帮助到你。

与现实组织合作撰写你的论文

学术界充满了做着非常有趣研究的人员。

问题是,很多研究都是非常理论性的,如果你打算在完成学位后进入行业,许多学术研究项目对你希望工作的雇主并不会特别相关。

举个例子,在我本科阶段,我倾注了心血和精力在一篇关于阿拉伯国家外国援助政策的 10,000 字论文上。我非常喜欢这个项目,但除了通过这个项目磨练出的软技能(如沟通、研究等),它与我之后能获得的任何工作几乎没有相关性。这个项目给了我很多生活经验,但在就业能力方面,我认为它贡献甚微。

结果是,当我开始攻读硕士学位时,我知道我想采取不同的方法。每年,牛津大学的学术部门都会发送一本 30 多页的手册,列出我们可以从事的潜在论文项目,但我立刻知道这些想法都不适合我。我攻读学位的主要目标不是为一些深奥的学术项目做贡献。我的目标是为在行业中作为数据科学家工作做准备,我知道做一个学术有趣但商业无关的项目不会帮助我实现这个目标。

不要误解我——我对小众学术项目没有任何反感。生活不仅仅关乎经济产出和职业,社会的丰富也在于即使不直接“与行业相关”的研究。但因为我知道自己不会在学术界待很久,我决定将精力投入到寻找一个直接与行业用例相关的项目上。如果我能找到这样的项目,我想,论文可以作为一种商业(或多或少)经验,这样我就可以在简历和面试中谈论这方面的内容。这对我来说尤其重要,因为我在机器学习/数据科学方面没有任何商业经验。

所以,当开始规划我的论文时,我开始寻找一个与行业相关的项目。我是怎么找到这样一个项目的呢?我在 LinkedIn 上给 5 位在酷公司中数据科学/机器学习团队工作的人员发了冷邮件,提到了一些关于我如何为他们可能正在解决的一些问题提供贡献的想法,并询问他们是否愿意与我合作一个数据科学项目。为什么只有 5 封邮件?因为这就是非付费、非高级 LinkedIn 账户的限制!

从我过去在外部销售工作的经验来看,我知道不应该期望高回应率。我联系的人都是他们组织中的高级成员,我知道他们可能没有时间帮助我处理这种请求。但这并没有阻止我:我只需要一个愿意给我一个机会的人。

最终,我得到了回应。一位来自大型(或多或少)美国科技公司的非常友善的人提供了帮助,他和他的同事们提供了大量支持,帮助我完成了一篇使用真实世界数据解决商业相关问题的论文。这段经历让我了解了数据科学在大型组织中的实际运作方式,并且我能够在随后的面试中谈论我所学到的(和贡献的)内容。

我对你的建议是一样的:不要把精力投入到与你未来目标无关的狭隘学术项目中。寻找一个实际公司面临的问题,并用数据科学向他们提出解决方案。如果你能说服他们你可能提供帮助,他们可能愿意提供数据和导师,帮助你写出一篇出色的论文,从而一箭双雕。

不要把精力投入到与你未来目标无关的狭隘学术项目中

如果你不能与真实的组织合作写论文,至少找一个真实的数据集,解决一个与你希望工作的公司相关的问题。如果你对创意感到困惑,可以查看这篇文章,在其中我讲解了我自己提出数据科学项目创意的框架:

如何找到独特的数据科学项目创意,使你的作品集脱颖而出

忘掉泰坦尼克号和 MNIST:选择一个独特的项目,提升你的技能,让你从人群中脱颖而出

towardsdatascience.com

在最初的 6 个月内不要申请任何工作

如果你的硕士学位课程只有一年,我认为在最初的 6 个月内申请工作是没有意义的。

为什么?首先,因为申请大量工作是彻底消耗你目前工作乐趣的可靠方式。如果你只有一年的时间来学习,不要浪费这段时间:尽量投入到课程中,结识新朋友。享受这段时间,不要总是想着“接下来会发生什么”。明天自会有它的担忧。

你应该推迟申请工作的第二个原因是,在你开始学位的初期成功的可能性不大。没错,你可能会通过第一阶段并因你的学术资历被邀请参加面试。但如果你还没有学到很多数据科学的知识,你将没有足够的技能通过技术面试阶段。等到你建立了知识和技能,能提供更具体的东西给潜在雇主会更好。

如果你在想:“我等不及 6 个月才能申请——我现在需要整理好一切!”那么我真的理解你的感受。当你处于大学的最后一年时,可能会感觉到很大的压力,尤其是考虑到许多大型公司通常在学年初招募员工。如果你是这样的人,我的建议是:不要让压力影响你。等到你准备好再申请。你一定会找到某些东西,如果你真的想申请一个校园招聘的职位,那就明年再申请,并在等待期间休息一年。

不要根据你获得好成绩的可能性来选择模块

A+、B-、C……它们不过是一些字母罢了!不要让它们决定你的选择。图片由Towfiqu barbhuiya提供,来源于Unsplash

在我攻读硕士学位的第一学期,所有学生都参加相同的课程,我们没有任何选择模块的机会。零。什么都没有。صفر。

就我个人而言,我喜欢这样。这意味着我们作为一个团队花更多的时间,建立了在数据科学多个方面的广泛基础,这对我来说很有帮助,因为我还不知道自己想专注于哪个领域。

然而,在 2021 年圣诞节期间,我面临一个重要的选择:我在第二学期应该选哪些选修模块?

我最初的想法是根据课程的难度来选择。“毕竟,”我对自己说道,“我在这个学位中的目标是取得好成绩,以便我更有可能找到一份好工作。”

然而,这种推理真的很危险。如果你只选择那些你感到 100%舒适的课程/工作,你将永远不会真正成长或提高你的技能。你会陷入同样的困境,一遍又一遍地运用相同的技能。当然,你的成绩可能会相当不错。但你不会成长。

根据我的经验,我的建议是颠覆这种思维方式,选择那些让你感到害怕的模块。为什么?因为大学是一个非常独特的个人成长机会。相比全职工作,你在时间利用上有更多的自由。根据我的经验,如果你用这段时间待在舒适区,你不会有太大的成长。相反,尝试一些新的、看起来吓人的课程,看看会发生什么。你可能会发现很多你喜欢(以及不喜欢)的东西,这将帮助你对未来做出更有信息的决策。

对我来说,这非常有效。我参加了一门看起来很吓人的自然语言处理(NLP)课程——我知道自己会有困难——但结果从中获得的收益比我在牛津上过的其他任何课程都要多。这门课程使我在完成后立即获得了 NLP 数据科学家的职位,并激发了我对这一领域的持续兴趣。如果我没有冒险选择这门看起来很吓人的课程,我永远不会知道这一点。

在你进行工作的过程中展示你的成果

虽然我认为在申请工作之前等一段时间是个好主意,但绝对不是一个好主意在开始展示你的工作和整理投资组合之前等太久。

当申请工作时,你会想花时间调整申请并准备技术面试。你不会想在匆忙中拼凑一个草率的投资组合。

为了避免陷入那种情况,尽量在工作过程中展示你的成果。在你学位的每一个作业结束时,在提交工作后,创建一个 GitHub 仓库来存储你的代码,并在 README.md 文件中写一个简要的项目描述。然后,当申请工作时,你将已经拥有一个可以展示给雇主的项目库,并用来证明你是一个出色的数据科学家。如果你想进一步提升,可以使用像 datascienceportfol.io 这样的无代码平台,为你的 GitHub 仓库构建一个漂亮的视觉前端。如果你感兴趣的话,这里是我在完成硕士学位后建立的投资组合:

## 使我获得数据科学家工作的投资组合

剧透警告:制作它出乎意料的简单(而且免费)

towardsdatascience.com

如果你读过我之前的文章,你可能已经对我不停地强调“做一个投资组合!”感到有些厌烦。但根据我的经验,如果你花几分钟时间整理和展示你的工作,从长远来看你是在给自己一个很大的帮助。

不确定学位是否适合你?

我最后想说的是,虽然学位是个人和职业成长的一个绝妙方式,但它并不适合每个人。在你现在阅读的文章中,我试图提供一些建议,帮助你在决定报名后充分利用学位。但是,如果你还在犹豫,可能需要查看一下我写的关于在决定攻读硕士学位之前需要考虑的事项的文章:

## 在承诺攻读数据科学硕士学位之前必须考虑的 8 件事

停止在 Pinterest 板上的火花:回到大学对你来说是正确的选择吗?

[towardsdatascience.com

如果你想无限制地访问我所有的故事(以及 Medium.com 的其他内容),你可以通过我的 推荐链接以每月 $5 进行注册。相比于通过普通注册页面,这不会给你增加额外费用,并且会帮助支持我的写作,因为我会获得少量佣金。如果你负担不起这笔费用(我完全理解!),如果你能关注我,将会对我意义重大。感谢阅读!

使用 Python 量化肥尾的 4 种方法

原文:towardsdatascience.com/4-ways-to-quantify-fat-tails-with-python-10ce62c0ada1

直观和示例代码

Shaw TalebiTowards Data Science Shaw Talebi

·发表于Towards Data Science ·阅读时间 11 分钟·2023 年 12 月 7 日

--

一条肥(猫的)尾巴。图像来自 Canva。

这是关于幂律和肥尾的系列文章中的第三篇。在上一篇文章中,我们探讨了如何从经验数据中检测幂律。虽然这种技术很有用,但肥尾超出了仅仅将数据拟合到幂律分布的范围。在这篇文章中,我将解析 4 种量化肥尾的方法,并分享分析真实数据的 Python 示例代码。

注意:如果你不熟悉幂律分布或肥尾等术语,请查看 这篇文章 作为入门。

在本系列的第一篇文章中,我们介绍了肥尾的概念,描述了稀有事件如何推动分布的整体统计数据。我们通过帕累托分布看到了肥尾的极端例子,例如,80%的销售额来自 20%的客户(而 50%的销售额来自仅 1%的客户)。

[## 帕累托、幂律和肥尾]

统计学中未教授的内容

towardsdatascience.com

尽管帕累托(更一般地说,幂律)分布为我们提供了肥尾的显著例子,但这是一种更普遍的概念,它在从薄尾(即高斯分布)到非常肥尾(即帕累托 80–20)的光谱上存在。

肥尾的光谱。图像由作者提供。

这种粗尾性的观点为我们提供了一种比仅仅标记为幂律(或不是)更灵活、更精确的数据分类方法。然而,这就提出了一个问题:我们如何定义粗尾性?

量化粗尾的 4 种方法

虽然没有“真实”的粗尾量度,但在实践中我们可以使用一些启发式方法(即经验法则)来量化数据的粗尾程度。在这里,我们回顾这 4 种启发式方法。我们首先从概念上介绍每种技术,然后深入示例 Python 代码。

启发式方法 1:幂律尾部指数

在幂律分布中,最粗的粗尾出现,其中幂律的尾部指数越小(即α),尾部越粗,如下面的图像所示。

具有不同α值的幂律分布示例。图像由作者提供。

这种观察表明较小的尾部指数意味着尾部更粗,自然促使我们使用α来量化粗尾。在实践中,这归结为拟合幂律分布到给定数据集,并提取估计的α值。

尽管这是一种直接的方法,但它有一个明显的限制。即当处理数据与幂律拟合不良时,该方法将失效。

启发式方法 2:峰度(即非高斯性)

粗尾的对立面是细尾(即稀有事件非常稀少到可以忽略不计)。细尾的典型例子是受欢迎的高斯分布,其中事件偏离均值 6 个标准差的概率大约为十亿分之一。

这启发了另一种通过量化数据“非高斯”程度来测量粗尾的方法。我们可以通过所谓的非高斯性测量来实现这一点。虽然我们可以设计许多这种测量方法,但最受欢迎的是峰度,由下面的公式定义。

根据参考文献[1]和[2]的峰度定义。图像由作者提供。

峰度由远离中心的值(即尾部)驱动。因此,峰度越大,尾部越粗

当所有矩都是有限的[3]时,这个测量方法往往效果很好。然而,一个主要的限制是峰度对某些分布没有定义,例如α <= 4 的帕累托分布,这使得它对许多粗尾数据无用。

启发式方法 3:对数正态的σ

在本系列的过去文章中,我们讨论了对数正态分布,该分布由下面的概率密度函数定义。

对数正态分布的概率密度函数 [4]。图像由作者提供。

正如我们之前看到的,这种分布有点狡黠,因为它在低σ时可能看起来像高斯分布,而在高σ时则类似于帕累托分布。这自然提供了另一种量化粗尾的方法,其中σ越大,尾部越粗

我们可以通过类似于启发式 1 的方法获得这个度量值。即,我们将对数据拟合一个对数正态分布并提取拟合的 σ 值。虽然这是一个简单的过程,但当对数正态拟合未能很好地解释数据时,它(如启发式 1)就会失效。

启发式 4:Taleb 的 κ

之前的启发式方法(H)以特定分布为起点(即 H1 — 幂律和 H3 — 对数正态)。然而,在实践中,我们的数据很少精确地遵循任何特定的分布。

此外,当评估遵循质的不同分布的两个变量时,使用这些度量进行比较可能存在问题。例如,使用幂律的尾部指数来比较类似 Pareto 和类似高斯的数据可能意义不大,因为幂律对类似高斯的数据拟合较差。

这促使我们使用不依赖于特定分布的肥尾度量。Taleb 在参考文献 [3] 中提出了一个这样的度量。所提议的度量 (κ) 定义为具有有限均值的单峰数据其值在 0 和 1 之间,其中0 表示数据最大程度地薄尾,而1 表示数据最大程度地肥尾。其定义如下。

Taleb 的 κ 度量定义 [3]。图像来源于作者。

该度量比较两个样本(例如,Sₙ₀ 和 Sₙ),其中 Sₙ 是从特定分布中抽取的 n 个样本的总和。例如,如果我们评估一个高斯分布并选择 n=100,我们将从高斯分布中抽取 100 个样本并将它们全部加在一起以创建 S₁₀₀。

M(n) 在上述表达式中表示平均绝对偏差,根据下面的方程定义。这个围绕均值的离散度度量比标准差 [3][5] 更加稳健。

κ 方程中平均绝对偏差的定义 [3]。图像来源于作者。

为了简化问题,我们可以选择 n₀=1,从而得到下面的表达式。

κ with n₀=1。图像来源于作者。

这里的关键术语是 M(n)/M(1),其中M(n) 量化了 n 个样本总和的均值周围的离散度(某种分布)。

对于薄尾分布,M(30) 将相对接近 M(1),因为数据通常接近均值。因此,M(30)/M(1) ~ 1

然而,对于肥尾数据,M(30) 将比 M(1) 大得多。因此,M(30)/M(1) >> 1。下图中,左侧图示展示了高斯分布的离散度如何随和数的总和而变化,右侧图示则展示了 Pareto 分布的变化情况。

高斯(左)和 Pareto 80–20(右)分布的 M(n) 和 M(1) 的缩放。注意 y 轴标签。 注意:高斯离散度的尺度增加是由于和数的增加。图像来源于作者。

因此,对于肥尾数据,κ 方程中的分母将大于分子,使得右侧第二项变小,最终,κ 较大。

如果这些数学内容超出了你的预期,那么总结如下:

大 κ = 肥尾,小 κ = 瘦尾

示例代码:量化(现实世界)社交媒体数据的肥尾特征

了解了概念后,让我们看看这些启发式方法在实际中的应用。这里,我们将使用上述每种方法分析本系列 上一篇文章 中的相同数据。

数据来自我的社交媒体账户,包括 Medium 上的月度关注者增加、YouTube 视频的收入以及 LinkedIn 的每日印象数。数据和代码可以在 GitHub 仓库 上免费获取。

[## YouTube-Blog/power-laws/3-quantifying-fat-tails 在主分支 · ShawhinT/YouTube-Blog

补充 YouTube 视频和 Medium 博客文章的代码。 - YouTube-Blog/power-laws/3-quantifying-fat-tails 在主分支…

github.com

我们首先导入一些有用的库。

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import powerlaw
from scipy.stats import kurtosis

接下来,我们将加载每个数据集并将其存储在字典中。

filename_list = ['medium-followers', 'YT-earnings', 'LI-impressions']

df_dict = {}

for filename in filename_list:
    df = pd.read_csv('data/'+filename+'.csv')
    df = df.set_index(df.columns[0]) # set index
    df_dict[filename] = df

在这一点上,查看数据总是一个好主意。我们可以通过绘制直方图和打印每个数据集的前 5 条记录来做到这一点。

for filename in filename_list:
    df = df_dict[filename]

    # plot histograms (function bleow is defined in notebook on GitHub)
    plot_histograms(df.iloc[:,0][df.iloc[:,0]>0], filename, filename.split('-')[1])
    plt.savefig("images/"+filename+"_histograms.png")

    # print top 5 records
    print("Top 5 Records by Percentage")
    print((df.iloc[:,0]/df.iloc[:,0].sum()).sort_values(ascending=False)[:5])
    print("")

Medium 月度关注者的直方图。图片由作者提供。

YouTube 视频收入的直方图。注意:如果你发现与之前文章的差异,那是因为我在数据中发现了一个异常记录(这就是查看数据的好处 😅)。图片由作者提供。

LinkedIn 日常印象的直方图。图片由作者提供。

根据上述直方图,每个数据集在某种程度上都表现出肥尾特征。让我们查看按百分比排名前五的记录,以更深入地了解这一点。

每个数据集按百分比排名前五的记录。图片由作者提供。

从这个角度看,Medium 的关注者似乎最具肥尾特征,60% 的关注者来自仅 2 个月。YouTube 收入也强烈表现为肥尾特征,大约 60% 的收入来自仅 4 个视频。LinkedIn 的印象数似乎是最不具肥尾特征的。

尽管我们可以通过 查看数据 得到肥尾特征的定性感觉,但让我们通过我们的 4 个启发式方法使其更具定量性。

启发式 1:幂律尾部指数

为每个数据集获得一个α,我们可以使用powerlaw库,正如我们在上一篇文章中所做的那样。这在下面的代码块中完成,我们在循环中进行拟合并打印每个数据集的参数估计值。

for filename in filename_list:
    df = df_dict[filename]

    # perform Power Law fit
    results = powerlaw.Fit(df.iloc[:,0])

    # print results
    print("")
    print(filename)
    print("-"*len(filename))
    print("Power Law Fit")
    print("a = " + str(results.power_law.alpha-1))
    print("xmin = " + str(results.power_law.xmin))
    print("")

幂律拟合结果。图片作者提供。

上述结果与我们的定性评估一致,即 Medium 粉丝是最胖尾的,其次是 YouTube 收入和 LinkedIn 印象(请记住,较小的α意味着较胖的尾部)。

启发式方法 2:峭度

计算峭度的一种简单方法是使用现成的实现。在这里,我使用Scipy并以类似之前的方式打印结果。

for filename in filename_list:
    df = df_dict[filename]

    # print results
    print(filename)
    print("-"*len(filename))
    print("kurtosis = " + str(kurtosis(df.iloc[:,0], fisher=True)))
    print("")

每个数据集的峭度值。图片作者提供。

峭度告诉我们与启发式方法 1不同的故事。根据这个测量,胖尾的排名如下:LinkedIn > Medium > YouTube。

然而,这些结果应持保留态度。正如我们在上面的幂律拟合中看到的,所有 3 个数据集都符合α < 4 的幂律,这意味着峭度是无限的。因此,尽管计算返回了一个值,但最好对这些数字保持怀疑态度。

启发式方法 3:对数正态的σ

我们可以再次使用powerlaw库来获得类似于启发式方法 1的σ估计值。这是它的样子。

for filename in filename_list:
    df = df_dict[filename]

    # perform Power Law fit
    results = powerlaw.Fit(df.iloc[:,0])

    # print results
    print("")
    print(filename)
    print("-"*len(filename))
    print("Log Normal Fit")
    print("mu = " + str(results.lognormal.mu))
    print("sigma = " + str(results.lognormal.sigma))
    print("")

对数正态拟合结果。图片作者提供。

查看上面的σ值,我们看到所有拟合都暗示数据是胖尾的,其中 Medium 粉丝和 LinkedIn 印象的σ估计值相似。另一方面,YouTube 收入的σ值显著较大,暗示了一个(更)胖的尾部。

令人怀疑的一点是,拟合估计了一个负的μ,这可能表明对数正态拟合可能无法很好地解释数据。

启发式方法 4:Taleb 的κ

由于我找不到现成的 Python 实现来计算κ(我没有很认真地找),这个计算需要一些额外的步骤。也就是说,我们需要定义 3 个辅助函数,如下所示。

def mean_abs_deviation(S):
    """
        Computation of mean absolute deviation of an input sample S
    """
    M = np.mean(np.abs(S - np.mean(S)))

    return M

def generate_n_sample(X,n):
    """
        Function to generate n random samples of size len(X) from an array X
    """
    # initialize sample
    S_n=0

    for i in range(n):
        # ramdomly sample len(X) observations from X and add it to the sample
        S_n = S_n + X[np.random.randint(len(X), size=int(np.round(len(X))))]

    return S_n

def kappa(X,n):
    """
        Taleb's kappa metric from n0=1 as described here: https://arxiv.org/abs/1802.05495

        Note: K_1n = kappa(1,n) = 2 - ((log(n)-log(1))/log(M_n/M_1)), where M_n denotes the mean absolute deviation of the sum of n random samples
    """
    S_1 = X
    S_n = generate_n_sample(X,n)

    M_1 = mean_abs_deviation(S_1)
    M_n = mean_abs_deviation(S_n)

    K_1n = 2 - (np.log(n)/np.log(M_n/M_1))

    return K_1n

第一个函数mean_abs_deviation()计算之前定义的平均绝对偏差。

接下来,我们需要一种方法来生成并求和n个样本。这里,我采用了一个简单的方法,随机从输入数组(X)中采样n次并将样本相加。

最后,我将mean_abs_deviation(S)generate_n_sample(X,n)结合起来,实现之前定义的κ计算,并为每个数据集进行计算。

n = 100 # number of samples to include in kappa calculation

for filename in filename_list:
    df = df_dict[filename]

    # print results
    print(filename)
    print("-"*len(filename))
    print("kappa_1n = " + str(kappa(df.iloc[:,0].to_numpy(), n)))
    print("")

κ(1,100)的每个数据集值。图片作者提供。

上述结果给了我们另一个故事。然而,考虑到这一计算的隐含随机性(回忆一下generate_n_sample()的定义)以及我们正在处理胖尾数据的事实,点估计(即只运行一次计算)是不可靠的。

因此,我进行了 1000 次相同的计算,并打印出每个数据集的均值κ(1,100)

num_runs = 1_000
kappa_dict = {}

for filename in filename_list:
    df = df_dict[filename]

    kappa_list = []
    for i in range(num_runs):
        kappa_list.append(kappa(df.iloc[:,0].to_numpy(), n))

    kappa_dict[filename] = np.array(kappa_list)

    print(filename)
    print("-"*len(filename))
    print("mean kappa_1n = " + str(np.mean(kappa_dict[filename])))
    print("")

从每个数据集的 1000 次运行中得到的均值κ(1,100)。图片由作者提供。

这些更稳定的结果表明,Medium 的关注者是最胖尾的,其次是 LinkedIn 的印象和 YouTube 的收入。

注意:可以将这些值与参考文献[3]中的表 III 进行比较,以更好地理解每个κ值。即,这些值与α介于 2 到 3 之间的帕累托分布相当。

尽管每种启发式方法讲述了略有不同的故事,但所有迹象都表明 Medium 的关注者增长是这三个数据集中最胖尾的。

结论

虽然将数据二分类为胖尾(或非胖尾)可能很诱人,但胖尾特征存在于一个范围上。在这里,我们分解了 4 种启发式方法,用于量化数据的胖尾程度

尽管每种方法都有其局限性,但它们为从业者提供了定量比较经验数据的胖尾特征的方式。

👉 更多关于幂律和胖尾: 介绍 | 幂律拟合

## 免费获取我撰写的每个新故事

免费获取我撰写的每个新故事 另:我不会将您的电子邮件分享给任何人 注册后,您将创建一个...

shawhin.medium.com

资源

连接: 我的网站 | 预约电话 | 问我任何问题

社交媒体: YouTube 🎥 | LinkedIn | Twitter

支持: 请我喝杯咖啡 ☕️

## 数据创业者

一个为数据领域创业者提供的社区。 👉 加入 Discord!

medium.com

[1] Scipy 峰度

[2] Scipy Moment

[3] arXiv:1802.05495 [stat.ME]

[4] en.wikipedia.org/wiki/Log-normal_distribution

[5] Pham-Gia, T., & Hung, T. (2001). 平均绝对偏差和中位绝对偏差。数学与计算建模34(7–8),921–936. doi.org/10.1016/S0895-7177(01)00109-1

使用 Python 将数据写入 Parquet 的 4 种方法:比较

原文:towardsdatascience.com/4-ways-to-write-data-to-parquet-with-python-a-comparison-3c4f54ee5fec

学习如何高效地使用 Pandas、FastParquet、PyArrow 或 PySpark 将数据写入 Parquet 格式。

Antonello BenedettoTowards Data Science Antonello Benedetto

·发表于 Towards Data Science ·7 分钟阅读·2023 年 3 月 13 日

--

照片来源:Dominika Roseclay

按需课程 | 推荐

一些读者联系我,询问关于 使用 Python 和 PySpark 进行数据工程 的按需课程。这些是我推荐的 3 个很好的资源:

还不是 Medium 会员?考虑通过我的 推荐链接 注册,以仅需每月 $5 即可访问 Medium 提供的所有内容!

介绍

在今天的数据驱动世界中,高效存储和处理大规模数据集是许多企业和组织的关键需求。这就是 Parquet 文件格式发挥作用的地方。

Parquet 是一种列式存储格式,旨在优化数据处理和查询性能,同时最小化存储空间。

它特别适合需要快速高效分析数据的用例,如数据仓库、大数据分析和机器学习应用。

在本文中,我将演示如何使用四个不同的库将数据写入 Parquet 文件:PandasFastParquetPyArrowPySpark

特别是,你将学习如何:

  • 从数据库中检索数据,将其转换为 DataFrame,并使用这些库中的每一个将记录写入 Parquet 文件。

  • 以批量形式将数据写入 Parquet 文件,以优化性能和内存使用。

到本文结束时,你将对如何使用 Python 写入 Parquet 文件有一个全面的理解,并解锁这种高效存储格式的全部潜力。

数据集

作为本教程的一部分使用的数据集包括关于不同货币和不同公司每日账户余额的模拟数据。

数据是通过 Python 递归函数生成的,然后插入到 SnowFlake 数据库表中。

然后,使用 Python 的 snowflake.connector 或本地 PySpark 连接工具(配合 jars)建立与数据库的连接,以检索数据集并将其转换为 DF 格式。

实现上述步骤的代码可在 这里 获得,而 DF 的前几行如下所示:

作者生成的模拟数据,已获取并转换为 DF。

现在,让我们描述四种使用 Python 将数据集写入 Parquet 格式的不同策略。

探索 4 种写入 Parquet 文件的方法

如你所见,所有方法的共同起点是将数据已转换为 Pandas 或 PySpark DF。

这将使代码更加简洁、可读,并让你的生活更轻松。

方法 # 1:使用普通 Pandas

将数据集写入 Parquet 文件的最简单方法可能是使用 pandas 模块中的 to_parquet() 方法:

# METHOD 1 - USING PLAIN PANDAS
import pandas as pd

parquet_file = 'example_pd.parquet'

df.to_parquet(parquet_file, engine = 'pyarrow', compression = 'gzip')

logging.info('Parquet file named "%s" has been written to disk', parquet_file)

在这种情况下,使用了 engine = 'pyarrow',因为 PyArrow 是一个高性能的 Python 库,用于处理 Apache Arrow 数据。它提供了 Parquet 文件格式的快速和内存高效的实现,可以提高 Parquet 文件的写入性能。

此外,PyArrow 支持多种压缩算法,包括 gzipsnappyLZ4

在本教程中,让我们假装目标是实现高压缩比(即生成更小的 Parquet 文件),即使这意味着压缩和解压速度较慢。这就是为什么使用了 compression = 'gzip'

方法 # 2:使用 Pandas 和 FastParquet

另一种流行的将数据集写入parquet文件的方法是使用fastparquet包。在最简单的形式下,它的write()方法接受一个pandas数据框作为输入数据集,并可以使用多种算法进行压缩:

# METHOD 2.1 - USING PANDAS & FASTPARQUET (WRITE IN FULL)
import pandas as pd
import fastparquet as fp

parquet_file = 'example_pd_fp_1.parquet'

fp.write(parquet_file, df, compression = 'GZIP')

logging.info('Parquet file named "%s" has been written to disk', parquet_file)

但如果fastparquet可以处理pandas数据框,那么为什么不使用方法 #1呢?

那么,使用fastparquet包至少有几个理由:

  • 它被设计成一个轻量级包,以其快速的写入性能和高效的内存使用而闻名。

  • 它提供了parquet格式的纯 Python 实现,并为开发人员提供了更多的灵活性和选项。

例如,通过使用fp.write()方法,你可以指定选项append = True,这是to_parquet()方法尚不支持的。

当源数据集太大,无法一次写入内存时,这个选项特别方便,因此为了避免OOM错误,你决定将其分批写入parquet文件中。

下面的代码是一个有效的示例,展示了如何利用pandasfastparquet的强大功能,将大型数据集分批写入parquet文件:

# METHOD 2.2 - USING PANDAS & FASTPARQUET (WRITE IN BATCHES)
import pandas as pd
import fastparquet as fp

# SETTING BATCH SIZE
batch_size = 250

data = db_cursor_sf.fetchmany(batch_size)
columns = [desc[0] for desc in db_cursor_sf.description]

# CREATES INITIAL DF INCLUDING 1ST BATCH
df = pd.DataFrame(data, columns = columns)
df = df.astype(schema)

# WRITES TO PARQUET FILE
parquet_file = 'example_pd_fp_2.parquet'
fp.write(parquet_file, df, compression = 'GZIP')

total_rows = df.shape[0]
total_cols = df.shape[1]

# SEQUENTIALLY APPEND TO PARQUET FILE
while True:
    data = db_cursor_sf.fetchmany(batch_size)
    if not data:
        break
    df = pd.DataFrame(data, columns = columns)
    df = df.astype(schema)

    total_rows += df.shape[0]

    # Write the rows to the Parquet file
    fp.write(parquet_file, df, append=True, compression = 'GZIP')

logging.info('Full parquet file named "%s" has been written to disk \
              with %s total rows', parquet_file, total_rows)

上述代码使用的策略是:

  • 定义一个batch_size:在本教程中,它设置为仅250行,但在生产中可以根据执行作业的工作机器的内存,轻松增加到数百万行

  • 从数据库中提取第一批行,使用fetchmany()而不是fetchall(),并使用此数据集创建一个pandas数据框。将此数据框写入parquet文件。

  • 实例化一个WHILE loop,它将不断从数据库中分批提取数据,将其转换为pandas数据框,最终附加到初始的parquet文件中。

WHILE loop将持续运行,直到最后一行被写入parquet文件。由于batch_size可以根据使用情况进行更新,因此这应该被视为一种更加“可控”和内存高效的方法来用 Python 写入文件。

方法 # 3:使用 Pandas & PyArrow

在教程早期提到过,pyarrow是一个高性能的 Python 库,也提供了parquet格式的快速和内存高效实现。

它的强大功能可以通过间接使用(设置engine = 'pyarrow',如方法 #1所示)或直接使用其一些本地方法。

例如,你可以很容易地复制方法 # 2.2中写入parquet文件的代码,使用ParquetWriter()方法:

# EXAMPLE 3 - USING PANDAS & PYARROW
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq

# SETTING BATCH SIZE
batch_size = 250

parquet_schema = pa.schema([('as_of_date', pa.timestamp('ns')),
                            ('company_code', pa.string()),
                            ('fc_balance', pa.float32()),
                            ('fc_currency_code', pa.string()),
                            ('gl_account_number', pa.string()),
                            ('gl_account_name', pa.string())
                           ])

parquet_file = 'example_pa.parquet'

total_rows = 0

logging.info('Writing to file %s in batches...', parquet_file)

with pq.ParquetWriter(parquet_file, parquet_schema, compression='gzip') as writer:
    while True:
        data = db_cursor_pg.fetchmany(batch_size)
        if not data:
            break
        df = pd.DataFrame(data, columns=list(parquet_schema.names))
        df = df.astype(schema)

        table = pa.Table.from_pandas(df)
        total_rows += table.num_rows

        writer.write_table(table)

logging.info('Full parquet file named "%s" has been written to disk \
              with %s total rows', parquet_file, total_rows)

请注意,由于pyarrow在幕后利用了 Apache Arrow 格式,因此ParquetWriter需要一个pyarrow模式作为参数(这些数据类型相当直观,并且与其 pandas 对应类型有些类似)。

此外,在使用此包时,你不能直接写入 pandas 数据框,但需要先将其转换为 pyarrow.Table(使用 from_pandas() 方法),然后可以使用 write_table() 方法将所需数据集写入文件。

尽管方法 #3相比其他方法更为详细,但如果在声明模式和列统计信息的可用性对你的用例至关重要,Apache Arrow 格式尤其推荐。

方法 #4:使用 PySpark

写入 parquet 文件的最后一种且可能是最灵活的方法是使用 pyspark 原生的 df.write.parquet() 方法。

当然,以下脚本假设你已连接到数据库并成功加载数据到数据框中,如 这里 所示。

注意,在这种情况下使用了 mode('overwrite'),但你可以轻松切换到 mode('append'),如果你希望批量写入数据。此外,pyspark 允许你指定大量选项,其中包括首选的 'compression' 算法:

# EXAMPLE 4 - USING PYSPARK
from pyspark.sql.types import *
from pyspark.sql import SparkSession
from pyspark import SparkConf

# CONNECT TO DB + LOAD DF

# WRITING TO PARQUET
df.write.mode('overwrite')\ # or append
        .option('compression', 'gzip')\
        .parquet("example_pyspark_final.parquet")

df.show()

结论

在这篇文章中,我们探索了四种不同的 Python 库,允许你将数据写入 Parquet 文件,包括 PandasFastParquetPyArrowPySpark

实际上,Parquet 文件格式是需要快速高效处理和分析大型数据集的企业和组织的关键工具。

你还学习了如何批量写入数据,这可以进一步优化性能和内存使用。通过掌握这些技能,你将能够充分利用 Parquet 的强大功能,将数据处理和分析提升到一个新的水平。

来源

这 5 种 SQL 技术涵盖了 ~80% 的实际项目

原文:towardsdatascience.com/5-advanced-sql-techniques-for-real-life-projects-f2db9b6680e2

加快你的 SQL 学习曲线

Thuwarakesh MurallieTowards Data Science Thuwarakesh Murallie

·发表于 Towards Data Science ·8 分钟阅读·2023 年 3 月 8 日

--

Possessed Photography 提供的照片,来源于 Unsplash

你是否对 SQL 感到好奇但又犹豫不决?或者你已经熟悉 SQL 的基础知识,但在将其应用于实际项目时遇到困难。

我了解这种感觉。

当我第一次开始学习 SQL 时,我被大量的信息所吓倒。即使今天,我仍然不断学习和探索新技术。

当然,SQL 的基础知识,如连接、子查询、过滤和排序,很容易掌握。但是,对于复杂的实际问题,你需要高级技术。

在这篇文章中,我想分享我在日常工作中最常用的五种高级 SQL 技术。通过掌握这些技术,你将能够完成几乎 80% 的生产级 SQL 查询,使你成为任何数据驱动项目的宝贵资产。

## Python To SQL — 我现在可以将数据加载速度提高 20 倍

上传大量数据的好、坏和丑陋的方法

towardsdatascience.com

我故意没有包括一些其他常用的技术,例如事务。如果你在分析角色中,这份方法列表将非常有用,而不是在软件工程师角色中。

在整篇文章中,我假设我们使用的是 Postgres 数据库。但如今每个主要的关系数据库都提供类似的功能。

1. 窗口函数

窗口函数是一种分析函数,它在与当前行相关的一组行中执行计算。窗口函数的结果与原始行一起返回,而不改变底层数据。

## 初级开发者编写多页 SQL 查询;高级开发者使用窗口函数

在记录的上下文中执行计算的一种优雅方法

[towardsdatascience.com

窗口函数的实际例子可能是计算特定产品随时间的销售收入累计总额。这对于识别销售趋势可能很有用,比如某些产品在一年中的某些时间最受欢迎。

这是一个如何使用 SUM 窗口函数计算特定产品随时间的销售收入累计总额的例子:

SELECT
  product_id,
  date,
  SUM(revenue) OVER (PARTITION BY product_id ORDER BY date) AS running_total
FROM
  sales
WHERE
  product_id = 123;

在这个例子中,SUM 窗口函数用于计算特定 product_id 随时间的 revenue 累计总额。PARTITION BY 子句按 product_id 对数据进行分组,ORDER BY 子句按 date 对数据进行排序。running_total 列包含 SUM 窗口函数的结果。

为什么不使用 **group by**

当我第一次开始使用窗口函数时,这让我感到困惑。

是的,你可以在 PostgreSQL 中使用 GROUP BY 来聚合数据。然而,使用 GROUP BY 会提供与窗口函数不同的结果。

在计算特定产品随时间的销售收入累计总额的例子中,使用 GROUP BY 会按 product_iddate 对销售数据进行分组,然后计算每个组的收入总和。这将给你每一天的总收入,而不是随时间的累计收入。

这是一个使用 GROUP BYproduct_iddate 聚合销售数据的例子:

SELECT
  product_id,
  date,
  SUM(revenue) AS total_revenue
FROM
  sales
GROUP BY
  product_id,
  date
WHERE
  product_id = 123;

了解更多关于 SQL 窗口函数的信息

2. CTE: 公共表表达式

CTE 是一个可以在单个 SQL 语句中引用的临时命名结果集。它定义了一个可以在庞大的查询中多次引用的子查询,从而简化了复杂查询。

让我们通过一个例子来更好地理解这一点。假设你有一个包含所有客户订单的表。你想找到各个产品在表现最佳的地区的销售情况。

没有 CTE,你必须编写复杂的查询,涉及子查询、连接和聚合函数。这可能使查询难以阅读和理解。然而,使用 CTE 可以简化查询,使其更易读。

这是一个经典例子,用于理解 CTE 的有用性。

WITH regional_sales AS (
    SELECT region, SUM(amount) AS total_sales
    FROM orders
    GROUP BY region
), top_regions AS (
    SELECT region
    FROM regional_sales
    WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT region,
       product,
       SUM(quantity) AS product_units,
       SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;

在上面的查询中,我们使用了两个 CTE 来计算顶级销售区域中的畅销产品。

第一个 CTE,regional_sales,通过对orders表的amount列求和并按region分组,计算每个区域的总销售额。

第二个 CTE,top_regions,仅选择那些总销售额超过所有区域总销售额 10%的区域。这是通过一个子查询来计算所有区域的总销售额并将其除以 10 实现的。

主查询然后使用IN子句将orders表与top_regions CTE 连接,以仅包含来自顶级销售区域的订单。

这是没有使用 CTE 的重写查询:

SELECT region,
       product,
       SUM(quantity) AS product_units,
       SUM(amount) AS product_sales
FROM orders
WHERE region IN (
    SELECT region
    FROM (
        SELECT region, SUM(amount) AS total_sales
        FROM orders
        GROUP BY region
    ) regional_sales
    WHERE total_sales > (
        SELECT SUM(total_sales)/10
        FROM (
            SELECT region, SUM(amount) AS total_sales
            FROM orders
            GROUP BY region
        ) regional_sales_sum
    )
)
GROUP BY region, product;

CTE 可以简化复杂的查询,使其更具可读性。它使在更大查询中多次重用相同的子查询变得更容易。

了解更多关于 CTE 的信息

3. 递归查询

你是否曾经想从一个数据以层次结构或树状结构存储的数据库中检索数据?

例如,你可能有一个产品类别树,其中每个类别都有子类别,每个子类别还可以有进一步的子类别。在这种情况下,递归查询非常有用。

递归查询是指在定义中引用自身的查询。当遍历数据库中的树状或层次结构并检索所有相关数据时,它很有用。换句话说,它使你能够从一个依赖于同一表中数据的表中选择数据。

这是一个用于遍历类别树的递归查询示例:

WITH RECURSIVE category_tree(id, name, parent_id, depth, path) AS (
  SELECT id, name, parent_id, 1, ARRAY[id]
  FROM categories
  WHERE parent_id IS NULL
  UNION ALL
  SELECT categories.id, categories.name, categories.parent_id, category_tree.depth + 1, path || categories.id
  FROM categories
  JOIN category_tree ON categories.parent_id = category_tree.id
)
SELECT id, name, parent_id, depth, path
FROM category_tree;

我们在这个例子中使用了带有WITH子句的 CTE 来定义递归查询。RECURSIVE关键字告诉 Postgres 这是一个递归查询。

category_tree CTE 由两个 SELECT 语句定义。第一个 SELECT 语句选择类别树的根节点(没有父节点的节点),第二个 SELECT 语句递归选择子节点。UNION ALL操作符结合了两个 SELECT 语句的结果。

depth列用于跟踪树中每个类别节点的深度。path列是一个数组,用于存储从根节点到当前节点的路径。

使用这个查询,我们可以检索树中所有类别及其各自的深度和路径。

了解更多关于递归查询的信息

4. 动态 SQL

如果你曾经使用过 SQL 查询,你可能遇到过一些非常复杂的查询,需要在运行时生成。编写这些查询可能令人望而却步,而执行它们则可能更加具有挑战性。

过去,我曾依靠 Python 生成复杂的 SQL 查询,并使用像 psycopg2 这样的数据库连接器执行它们。这种方法有效但不太优雅。

然而,我最近发现了 Postgres 中的动态 SQL,这使得生成和执行复杂查询变得更加可控。使用动态 SQL,你可以根据运行时条件动态创建查询,这在处理复杂的数据结构或业务逻辑时非常有用。

假设你想检索在特定日期下的所有订单。在静态查询中,你可能会写成这样:

SELECT * FROM orders WHERE order_date = '2022-03-01';

但如果你允许用户选择一个日期范围呢?使用动态 SQL,你可以根据用户输入生成查询,如下所示:

EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "SELECT * FROM orders WHERE order_date BETWEEN ? AND ?";
DATE start_date, end_date;
EXEC SQL END DECLARE SECTION;

EXEC SQL PREPARE mystmt FROM :stmt;

EXEC SQL EXECUTE mystmt USING :start_date, :end_date;

在这个例子中,我们创建了一个函数,该函数接受两个参数,start_dateend_date,并返回一个在该日期范围内的订单表。EXECUTE 语句允许我们根据输入参数动态生成查询,而 USING 子句则指定了查询的值。

这个例子很简单。但是在大规模项目中,你会需要大量动态生成的 SQL。

了解更多关于动态 SQL 的信息

5. 游标

我们的查询可能会在受限环境中运行。一次性对一个庞大的表进行密集操作可能并不总是最佳选择。或者我们可能需要对操作有更多的控制,而不是将其应用于整个表。

这就是游标派上用场的地方。游标允许你逐行检索和操作结果集中的数据。你可以使用游标遍历数据集,并对每一行执行复杂的操作。

假设你有一个名为“products”的表,其中包含所有产品的信息,包括产品 ID、产品名称和当前库存。你可以使用游标遍历所有包含特定产品的订单,并更新其库存。

DECLARE
    cur_orders CURSOR FOR 
        SELECT order_id, product_id, quantity
        FROM order_details
        WHERE product_id = 456;

    product_inventory INTEGER;
BEGIN
    OPEN cur_orders;
    LOOP
        FETCH cur_orders INTO order_id, product_id, quantity;
        EXIT WHEN NOT FOUND;
        SELECT inventory INTO product_inventory FROM products WHERE product_id = 456;
        product_inventory := product_inventory - quantity;
        UPDATE products SET inventory = product_inventory WHERE product_id = 456;
    END LOOP;
    CLOSE cur_orders;

    -- do something after updating the inventory, such as logging the changes
END;

在这个例子中,我们首先声明了一个名为“cur_orders”的游标,选择所有包含特定产品 ID 的订单详细信息。然后我们定义了一个名为“product_inventory”的变量,用于存储该产品的当前库存。

在循环中,我们从游标中获取每个订单 ID、产品 ID 和数量,从当前库存中减去数量,并使用新的库存值更新产品表。

最后,我们关闭游标,并在更新库存后进行其他操作,例如记录更改。

结论

总之,SQL 是一种强大的语言,提供了许多处理复杂数据的技术。但是,初学时掌握所有这些技术可能会让你感到不知所措。

这篇博客文章探讨了五种最常用的高级 SQL 技术,包括 CTE、窗口函数、递归查询、动态查询和游标。虽然像联接和子查询这样的基本 SQL 概念对于处理数据是基础,但这些技术将帮助你处理几乎任何 SQL 项目。

虽然这篇文章提供了高级 SQL 技术的概述,但并不打算对每种技术进行详尽讨论。有关的链接已经提供,供那些希望深入探讨这些概念的人使用。未来,我计划对每种技术进行更深入的探讨,提供更全面的理解其能力和潜在应用。

感谢阅读,朋友!如果您喜欢我的文章,让我们在 LinkedInTwitterMedium 保持联系。

还不是 Medium 会员?请使用这个链接成为会员,因为在没有额外费用的情况下,我会因推荐您而获得一小部分佣金。

5 个令人惊叹的 Python 隐藏功能 — 第一部分

原文:towardsdatascience.com/5-awesome-python-hidden-features-a0172e0bd98e

PYTHON | 编程 | 特性

使用这些酷炫的隐藏 Python 功能,将你的编程技能提升到一个新水平

David FarrugiaTowards Data Science David Farrugia

·发布于 Towards Data Science ·6 分钟阅读·2023 年 3 月 16 日

--

图片由 Emile Perron 提供,来源于 Unsplash

Python 是一种奇妙的编程语言。

Stack Overflow 的 2022 年开发者调查将 Python 排在 2022 年最受欢迎编程语言的第一位。

Python 非常适合初学者。它的语法简单易懂,大大减少了学习曲线的难度。

Python 是多才多艺的。得益于庞大而活跃的 Python 社区,Python 拥有强大的包和框架,能够解决几乎所有的开发需求。

想要编写 API 吗? 你可以使用 Python。

想要制作游戏吗? Python 能够满足你的需求。

想要处理数据并训练机器学习模型吗? 当然可以。Python 拥有适合你的工具!

Python 还隐藏着许多绝妙的技巧。我总是对所有能优雅解决复杂任务的 Python 一行代码感到惊讶!

在这篇文章中,我们将介绍 5 个酷炫的 Python 技巧,让你可以在同事面前炫耀 😜

隐藏功能 1:在 FOR 和 WHILE 循环中使用 ELSE

我们在开始编程时学习的第一件事之一就是条件语句(即if-else块)。这些条件语句允许我们根据某些变量的值来改变代码的执行流程。在if块中,我们检查某些逻辑。如果这个逻辑条件没有满足,我们就执行else块中定义的代码。这些都是常识。

不过,我们也可以在任何forwhile循环中使用else关键字。在这种情况下,else的功能是仅在循环成功完成且没有遇到任何break语句时执行代码。

你可能会问,这有什么用处?

假设我们有一个数字列表。我们想要编写逻辑来确定元组中的任何单一数字是否为偶数。

通常,我们可能会写类似这样的代码:

# we define our numbers list
numbers: list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# we also define a flag variable to indicate if an even number was found
found_even: bool = False

for num in numbers:
    # if number modulus 2 is 0, then it is even
    if num % 2 == 0:
        print(f"{num} is even")
        # we set our flag to True because we found an even number
        found_even = True
        # we can stop execution because we found an even number
        break

# if the flag is False, no even numbers where found
if not found_even:
    print("No even numbers found")

这个逻辑相对简单。我们使用一个标志(在这个例子中是found_even变量)来表示是否找到了偶数。如果在迭代过程中找到了偶数,我们使用break关键字来停止循环执行。

上述代码也可以写成如下形式:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for num in numbers:
    if num % 2 == 0:
        print(f"{num} is even")
        break
else:
    print("No even numbers found")

我们现在不再需要标志变量found_even。我们可以使用else关键字来仅在循环迭代过程中从未达到break关键字时打印“未找到偶数”。

隐藏功能 2:海象运算符

海象运算符(:=)是在 Python 3.8 中引入的。我们使用海象运算符将变量与值分配为一个表达式。

参考以下示例。我们想要实现生成一系列随机数的逻辑,直到生成一个特定的数字。假设我们要得到我最喜欢的数字:10。我们通常会写类似这样的代码:

import random

rand = None
while True:
    # generate a random number between 1 and 100
    rand = random.randint(1, 100)
    # if the random number is 10, break the execution
    if rand != 10:
        print(rand)
    else:
        break

# this will only be executed if we get a 10 and break the loop
print("We got a 10!")

在我们的循环中,我们生成一个随机数并将其存储在变量rand中。迭代的次数是基于rand变量的值。rand变为 10 的时间越早,我们就越早打破循环。

现在,使用海象运算符,我们可以通过以下代码获得相同的功能:

import random

while (rand := random.randint(1, 100)) != 10:
    print(rand)

print("We got a 10!")

在这里,我们告诉 Python 我们希望 while 循环在 rand 的值不等于 10 时运行。我们还告诉它,每次新迭代时,rand 将从 random.randint(1, 100) 获取其值。

隐藏功能 3:省略号

省略号(即...)是一个有趣的关键字,在早期开发阶段非常有用。当处理复杂逻辑时,最佳策略是分而治之——将复杂的逻辑拆分成较小且更易于实现的部分。通常,这需要我们首先实现这些较小的函数,然后将所有内容结合在一起。

然而,我们有时(出于各种原因)想要定义函数但稍后再编写其代码。省略号允许我们做到这一点!

让我们看看如何实现。

def some_function(x, y, z):
    # do something with x, y, z
    ...

# we can use it anywhere we like
some_list = [1, 2, 3, ...]

即使函数some_function没有定义代码,上述代码也不会失败。我们甚至可以调用这个函数,它仍然不会失败(当然,它也不会返回任何东西)。

隐藏功能 4:函数属性

在 Python 中,任何函数都被存储为一个对象。任何对象都可以拥有属性。因此,在 Python 中,函数也可以有属性。

我们可以使用函数属性来定义有关函数的附加信息和其他元数据。例如,假设我们想跟踪某个特定函数被调用的次数。我们可以设置一个计数器属性,在每次调用后递增它。

def my_function(x):
    return x * 2

my_function.counter = 0
my_function.counter += 1
print(my_function.counter)

函数属性的另一个有趣用例是设置 is_verbose 属性,以便在打印额外信息时进行切换。这通常通过向函数传递一个额外的参数来实现。使用函数属性,我们将不再需要额外的参数。

另一个好的例子是展示函数的文档字符串。

def my_function(x):
    """This is a docstring for my_function."""
    return x * 2

print(my_function.__name__)
print(my_function.__doc__)

通过调用属性 __name__,我们指示 Python 打印函数的名称。__doc__ 则打印函数的文档字符串。

函数属性有许多用途。你可以在这里阅读更多关于它们的内容:

[## PEP 232 - 函数属性

这个 PEP 描述了对 Python 的扩展,为函数和方法添加属性字典。这个 PEP 跟踪了……

peps.python.org

隐藏特性 5:三元运算符

Python 中的三元运算符是一种将 if-else 语句定义为单行代码的方法。

请考虑下面的示例:

x = 5
y = 10

if x > y:
    result: str = "x is greater than y"
else:
    result: str = "y is greater than or equal to x"

print(result)

我们可以使用三元运算符语法来获得相同的功能,如下所示:

x = 5
y = 10

result = "x is greater than y" if x > y else "y is greater than or equal to x"

print(result)

你可以在这里找到本系列的第二部分:

5 个更多令人惊叹的 Python 隐藏特性 — 第二部分

探索一些强大的功能以释放 Python 的全部潜力

towardsdatascience.com](/5-more-awesome-python-hidden-features-part-2-160a533c212b?source=post_page-----a0172e0bd98e--------------------------------)

结论

在这篇文章中,我们讨论了 5 个不被认为是常识的 Python 特性。这些隐藏特性的目的不仅仅是让我们炫耀 Python 技能。它们确实可以节省宝贵的开发时间,提高代码的可读性,并帮助我们编写更高效、更美观的代码。

你喜欢这篇文章吗?每月 5 美元,你可以成为会员,解锁对 Medium 的无限访问权限。你将直接支持我和 Medium 上所有你喜欢的作家。非常感谢!

## 使用我的推荐链接加入 Medium - David Farrugia

阅读 David Farrugia 的每一篇故事(以及 Medium 上成千上万的其他作家)。你的会员费用直接支持……

david-farrugia.medium.com

也许你还可以考虑订阅我的邮件列表,以便在我发布新内容时收到通知。这个服务是免费的 😃

## 获取 David Farrugia 发布内容的邮件通知

每当 David Farrugia 发布新内容时,你会收到一封邮件。通过注册,你将创建一个 Medium 帐户(如果你还没有的话)……

david-farrugia.medium.com

想要联系我吗?

我很想听听你对这个话题的看法,或者关于人工智能和数据的任何意见。

如果你希望联系我,可以发邮件到davidfarrugia53@gmail.com

Linkedin

数据共享的 5 个好处

原文:towardsdatascience.com/5-benefits-of-data-sharing-5ad8efde3385?source=collection_archive---------16-----------------------#2023-01-13

使数据民主化以释放其全部潜力

Louise de LeyritzTowards Data Science Louise de Leyritz

·

关注 发表在 Towards Data Science · 6 分钟阅读 · 2023 年 1 月 13 日

--

数据共享在许多组织中正成为常态,因为对从数据中提供价值的压力越来越大。事实上,2023 年是工作场所投资回报率之年, 这也包括数据。

无论你称之为数据网格、数据操作化、数据激活还是数据民主化,其核心理念是相同的:即向业务团队提供数据,帮助他们自主做出基于数据的决策。

数据共享的原则很简单:

  1. 每个人都应该能够访问他们所需的数据,而不仅仅是某些角色或职位。

  2. 应该没有障碍阻止人们获取他们所需的数据。

  3. 数据应以一种便于任何人访问、理解和使用的方式组织和结构化。

数据共享与自助服务的概念相一致,因为它消除了手动数据传递的需求。

有些人认为数据共享令人望而生畏。如果这导致混乱和混沌怎么办?好消息是,适当实施数据共享的好处通常会超过任何潜在的负面结果。

在这篇文章中,我将分享 5 个理由,说明为什么你应该开始与业务团队共享数据,以及你可以预期的积极影响。在即将到来的文章中,我将进一步探讨数据共享的正确实施方法。

数据共享可以为您的组织带来许多显著的好处,包括统一公司愿景、改善决策制定和提高生产力。此外,数据共享还可以帮助促进创新,并推动公司内部数据质量的提升。让我们深入了解一下!

数据共享的好处 — 图片由Castor提供

1 — 统一公司的愿景

数据共享有潜力通过创建一个员工可以自主访问和使用所需数据的市场,来使不同团队围绕公司战略达成一致。

在实践中,这意味着所有部门都可以访问相同的数据及其相关背景。也就是说,它让每个人在定义、指标计算方式和最佳数据集使用方面达成共识。这确保了每个人使用相同的语言,基于共享的定义和词汇。这会导致不同部门产生的一致报告。

在缺乏数据共享的情况下,各部门可能会孤立运作,它们的报告经常出现不一致的情况。其风险在于,各单位可能对相同概念使用不同的定义和计算方法。当人们有不同的定义时,他们的报告内容也会不同。相互矛盾的报告会减慢决策速度,并导致信任的侵蚀。数据共享使公司能够消除这种不一致。

此外,数据共享提供了对组织运作方式的更大透明度。每个部门可以跟踪进展,看到全局。这可以促进合作,帮助大家朝着相同的目标努力,而不是专注于各自孤立的观点。

2 - 提升决策能力

一旦你开放数据访问,通常会看到决策能力的提升。

当业务部门能够访问相关数据时,他们能够做出有根据和准确的决策。不再需要猜测或依赖直觉。

数据共享确保业务团队在日常操作中整合数据而不妨碍速度。这样,数据可以支持所有部门的实时决策,并通过报告促进长期战略决策。

此外,数据民主化消除了决策过程中的传统瓶颈。当只有技术团队能够访问数据时,其他团队必须寻求他们的帮助来获取信息。

在许多组织中,向各种用户分发数据仍然是手动进行的。首先,业务用户请求一个数据集。然后,分析团队接收请求,解读请求,并尝试与业务团队确认请求。当解释错误时,这种小范围的来回交流可能会持续一段时间。

这延迟了决策过程并加重了技术团队的负担。此外,需要跟上业务人员请求的数据团队扩展性差。

数据共享解放了分析团队,使其能够进行更深入、更有意义的数据分析。当分析可以脱离基本报告 — 向其他团队提供数据事实,和回答票务问题 — 时,他们可以专注于我们真正需要分析师技能的地方。

总的来说,数据共享改善了决策制定,同时释放了技术团队宝贵的时间。简而言之,这意味着每个人都能做出更好、更快、更聪明的决定。还有什么不喜欢的呢?

3 — 提高生产力

这还不止于此。数据共享还影响到所有团队的生产力

当利益相关者能够访问单一真实来源时,他们可以更快地完成任务。

依赖手动过程来请求和交付数据时,速度是一个主要问题。通过数据共享,利益相关者不需要花时间寻找数据或等待他人提供数据。这也减少了错误的风险,因为员工可以访问最新和最准确的数据。

数据共享还消除了重复的工作和仪表板的重建。如果某些东西已经存在,利益相关者会知道。他们会在现有的基础上进行改进,而不是从头开始一个新项目。而且,重用比重建更高效。

当部门孤立运行时,不同团队会花时间重建相同的东西。这是因为他们没有检查某个材料是否已经存在的方式。

数据共享因此使组织能够利用其集体知识。这节省了时间和资源,使组织更高效、更具生产力。

4- 促进创新

数据共享通过增加跨部门协作和消除团队之间的壁垒来促进创新。

正如之前所述,数据共享使员工能够访问和使用他们可能无法自己获得的数据。这可以激发他们之前未曾考虑过的新想法和方法。

例如,如果市场部门的员工可以访问销售部门的数据,他们可能会发现新的方法来定位潜在客户或优化他们的营销策略。

其次,数据共享促进了团队之间的合作。它使个人可以看到其他人正在做什么,并分享他们自己的想法和见解。这可以带来新的视角和方法。这些通常不会在团队各自独立时出现。

因此,数据共享可以创造一个有利于创新的环境。它为员工提供了与同事合作和分享见解所需的信息。这将带来更好的产品、服务和流程。

5 — 改善数据质量

分享就是关心,特别是当涉及到数据时。事实上,数据共享可以帮助提高数据质量。数据共享增加了对数据准确性和完整性的审查和验证。

随着更多的人查看数据,更容易发现错误或数据质量差的地方。这带来了更大的数据质量责任,并能够修复可能出现的任何问题。

数据共享还增强了对数据可靠性的信任。当小组控制数据时,其他人可能很难验证其准确性或理解其收集的背景。

这可能导致对数据的怀疑和不信任,从而成为决策的障碍。因此,数据共享使每个人更容易理解和信任他们使用的数据。

因此,数据共享意味着更好的数据质量、增强的决策能力和改进的背景。这一切可以导致更智能的决策和不同部门之间更大的协调。这对我来说毫无疑问。

结论

数据共享将因两个原因继续获得关注:

  1. 公司希望充分利用其数据,因为从数据中获得投资回报的压力在增加。

  2. 越来越多的工具正在开发,以使数据对业务用户更容易访问。

数据共享有潜力改善战略对齐和增强决策过程。它还可以提高生产力、促进创新和改善数据质量。

然而,需要注意的是,如果实施不当,数据共享可能会导致混乱和混沌。我的下一篇文章将重点探讨如何以避免这些陷阱的方式建立数据共享,并帮助组织实现数据民主化的最终好处。敬请关注!

最初发布于 https://www.castordoc.com

5 种最佳 Python 合成数据生成器及如何在数据不足时使用它们

原文:towardsdatascience.com/5-best-python-synthetic-data-generators-and-how-to-use-them-when-you-lack-data-f62bcf62d43c

获取更多数据

Bex T.Towards Data Science Bex T.

·发表于Towards Data Science ·阅读时间 8 分钟·2023 年 1 月 23 日

--

图片来源 Maxim Berg

2021 年,每天产生了 2.5 万亿字节(2.5 百万 TB)的数据。今天的量更大。但显然,这还不够,因为 Python 生态系统中有许多库用于生成合成数据。有些可能是为了生成合成数据而创建的,但大多数都有有益的应用,例如:

  • 机器学习:当真实数据不可用或难以获得用于模型训练时

  • 数据隐私和安全:用真实但非实际的数据替换数据集中的敏感信息

  • 测试和调试:在受控环境中使用合成数据测试和调试软件

  • 数据增强:使用机器学习或统计方法从现有数据中人工生成更多数据点

本文将展示六个用于上述目的的 Python 库及其使用方法。

使用 Faker 生成随机用户信息

Faker 是最早的 Python 库之一,用于生成各种随机信息。一些常用的 Faker 生成的属性包括:

  • 个人信息:姓名、生日、电子邮件、密码、地址

  • 各种日期和时区信息

  • 财务细节:信用卡、社会安全号码、银行信息

  • 杂项:URLs、句子、语言代码

诸如此类。

它也有一个直观的 API。在初始化一个Faker类后,你可以通过调用它的方法来生成一个新的虚假项目:

from faker import Faker

fake = Faker()

>>> fake.name()
'Nicole Perkins'

>>> fake.address()
'11669 Foster Cliffs Suite 161\nPort Elizabethfurt, OK 47591'

>>> fake.url()
'http://www.wade.com/'

所有这些方法在每次调用时都会返回新的项目,因此使用如下的代码片段构建一个人工 CSV 数据集非常简单:

import pandas as pd

df = pd.DataFrame(
    [
        {
            "name": fake.name(),
            "address": fake.address(),
            "birthday": fake.date_of_birth(),
            "email": fake.email(),
            "password": fake.password(),
        }
        for _ in range(1000)
    ]
)

df.to_csv("data/fake.csv", index=False)
df.sample(5)

作者提供的图片

如果你注意到,姓名和电子邮件地址不匹配。这是使用 Faker 的一个缺点——Faker 生成的数据集在公开使用时很容易被识别。

了解更多内容,请访问 文档

Sklearn 用于机器学习任务的合成数据集

Sklearn 是一个如此广泛且出色的库,它专门支持合成数据生成。

datasets 模块包含了许多生成各种机器学习任务的人工数据集的函数。最受欢迎的函数是 make_classificationmake_regression

两者都有 n_samplesn_features 参数来控制生成的合成数据集的行数和特征数。

from sklearn.datasets import make_classification, make_regression

X, y = make_classification(
    n_samples=5000, n_features=20, n_informative=15, n_classes=3, n_clusters_per_class=3
)

X, y = make_regression(n_samples=5000, n_features=20, n_informative=10)

为了控制任务的难度,你可以指定多少特征是有用的或冗余的,使用 n_informative(相关)或 n_redundant(信息特征的线性组合)参数。

make_classification 还提供了对分类目标的多种控制,即类别数、每个类别的簇数以及类别权重。

下面还有 make_blobs 函数用于生成聚类任务:

import seaborn as sns
from sklearn.datasets import make_blobs

X, y = make_blobs(n_samples=500, n_features=2)

sns.scatterplot(X[:, 0], X[:, 1], hue=y);

作者提供的图像

如果你在寻找一些花哨的功能,还有其他函数,如 make_checkerboardmake_circlesmake_moonsmake_s_curve

PyOD 中的离群值数据集

异常检测是数据科学中的一个普遍问题。但如果你想进行练习,质量高的带有离群值的数据集很难获得。幸运的是,Python Outlier Detection (PyOD) 库提供了一个生成带离群值的合成数据的实用函数:

from pyod.utils.data import generate_data
import seaborn as sns
import matplotlib.pyplot as plt

X, y = generate_data(
    n_train=500, contamination=0.13, n_features=2, train_only=True, random_state=1
)

# Plot
sns.scatterplot(X[:, 0], X[:, 1], hue=y)

# Modify the damn legend
legend = plt.legend(labels=['Inlier', 'Outlier'])
legend.legendHandles[1].set_color("orange")

作者提供的图像

generate_data 提供了对训练集和测试集行数的控制,并且可以调整结果集中离群值的百分比(contamination)。

PyOD 还拥有 Python 生态系统中最大的异常检测算法套件。要了解更多信息,你可以查看 我的异常检测课程

使用 CTGAN 在另一个数据集之上生成合成数据

现在,进入精彩内容。

当数据有限时,机器学习模型很难很好地泛化而不会过拟合。在这种情况下,你可以使用条件生成对抗网络——CTGAN。

在你将其拟合到任何数据集后,CTGAN 可以从数据集的信息空间中生成高度匿名的合成样本。这是一种有效增加数据安全性和数据集规模的方法。

CTGAN合成数据库 (SDV) 项目提供。它的 Python API 公开了一个CTGAN类,该类需要学习的数据集和其分类列的列表。

然后,你可以使用sample函数从中抽取任意数量的样本。下面,我们从陈词滥调的鸢尾花数据集中抽取了 20k 个合成样本:

import seaborn as sns
import pandas as pd
from ctgan import CTGAN

# Extract categorical data types
iris = sns.load_dataset("iris")
categoricals = iris.select_dtypes(exclude="number").columns.tolist()

# Fit CTGAN
ctgan = CTGAN(epochs=10)
ctgan.fit(iris, categoricals)

# Generate the data
synthetic_iris = ctgan.sample(20000)
synthetic_iris.head()

作者提供的图像。 鸢尾花数据集 (CC By 4.0).

Mimesis — 高级伪造工具

Mimesis 是一个建立在 Faker 基础上的完整随机信息生成器。它可以生成比 Faker 更多的随机属性:

from mimesis import Generic
from mimesis.locales import Locale

# Spanish locale
fake = Generic(Locale.ES)

print(dir(fake))
address     code           development  food      locale   payment  text     
binaryfile  cryptographic  file         hardware  numeric  person   transport
choice      datetime       finance      internet  path     science

它的随机生成器被分为 20 个类别,这使得 Mimesis 更加有条理。

它还大力支持 32 个地区(语言)的国家特定信息。下面,我们生成了一千行虚假的西班牙数据:

from mimesis import Generic
from mimesis.locales import Locale
import pandas as pd

# Spanish locale
fake = Generic(Locale.ES)

df = pd.DataFrame(
    [
        {
            "name": fake.person.full_name(),
            "country": fake.address.country(),
            "birthday": fake.datetime.date(),
            "email": fake.person.email(),
            "password": fake.person.password(),
        }
        for _ in range(1000)
    ]
)

df.head()

作者提供的图像

你还可以创建自定义地区,其中你可以结合多种语言以获取区域特定的信息,即特定于西欧的数据。

从其大量文档中了解更多信息。

使用 TensorFlow 进行图像增强

在计算机视觉问题中,人工增加图像数据集规模的最有效方法之一是数据增强。

这个方法很简单:当你拥有一个小的图像数据集,数据量太小以至于神经网络无法有效训练时,你可以通过使用各种随机图像转换来增加图像数量。这样,网络将有更多多样的例子进行训练。常见的图像转换有:

  • 几何变换:旋转、平移、缩放、翻转——改变图像中物体的大小、方向和位置

图像来自TensorFlow 文档 (Apache License)

  • 颜色和亮度:随机改变亮度和对比度,以引入更多的光照和颜色条件变化

图像来自TensorFlow 文档 (Apache License)

  • 噪声和模糊:添加随机噪声和模糊效果以模拟不同水平的图像质量

这种转换可以通过引入相似但不完全相同的图像变体显著增加数据集的大小。这反过来会提升神经网络的性能。

在 TensorFlow 中,可以通过多种方式执行图像增强。对于图像分类任务,有一个ImageDataGenerator类:

import tensorflow as tf

train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
    rescale=1.0 / 255,
    rotation_range=10,
    width_shift_range=0.1,
    height_shift_range=0.1,
    zoom_range=0.15,
    horizontal_flip=True,
    fill_mode="nearest",
)

你初始化它并设置你想要的转换。然后,你可以使用其flow_from_directory方法从指定的数据目录中批量读取图像:

train_generator = train_datagen.flow_from_directory(
    "data/raw/train",
    target_size=(50, 50),
    batch_size=32,
    class_mode="categorical",
)

之后,你可以将train_generator传递给 Keras 模型的fit。生成器异步工作——当模型在处理一批数据时,生成器在后台应用变换并调整下一批图像的大小。

为了使flow_from_directory正常工作,数据集文件夹的结构应具有如下层次:

$ tree -L 3 data/raw/train

data/raw/
├── train
│   ├── 0
│   ├── 1
│   ├── 2
|   ...
├── validation
│   ├── 0
│   ├── 1
│   ├── 2
|   ...

数据集必须包含训练和验证(以及测试)目录,且图像应按类别名称分组到不同的文件夹中。

如果你不能将数据集强制成这样的结构,还有其他替代方案。例如,当你使用 Keras Sequential API 构建模型时,你可以使用转换层:

from tensorflow.keras import layers

resize_and_rescale = tf.keras.Sequential([
  layers.Resizing(IMG_SIZE, IMG_SIZE),
  layers.Rescaling(1./255)
])

image_augmentation = tf.keras.Sequential([
  layers.RandomFlip("horizontal_and_vertical"),
  layers.RandomRotation(0.2),
])

model = tf.keras.Sequential([
  # Add the preprocessing layers you created earlier.
  resize_and_rescale,
  data_augmentation,
  layers.Conv2D(16, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  # Rest of your model.
])

结论

尽管全球已有如此多的数据,合成数据正变得越来越受欢迎。这一点在新兴的合成数据初创公司数量中有所体现。根据这份市场分析,2021 年全球合成数据生成行业的价值超过了 1 亿,并预计以 34.8%的年增长率增长。

在这篇文章中,我们只是浅尝辄止地了解了一些最受欢迎的开源替代方案。除非你在寻找企业解决方案,否则这些库足以满足你的基本需求。

感谢阅读!

喜欢这篇文章以及它那奇特的写作风格吗?想象一下能够访问到更多类似的文章,都是由一个才华横溢、迷人风趣的作者(顺便说一下,就是我 😃)所写。

只需 4.99 美元的会员费,你将不仅能访问我的故事,还能获取来自 Medium 上最优秀和最聪明的思想者的知识宝库。如果你使用我的推荐链接,你将赢得我的超级感激和一个虚拟的击掌来支持我的工作。

[## 使用我的推荐链接加入 Medium — Bex T。

获取对我所有⚡高级⚡内容的独享访问权限,在 Medium 上畅游无阻。通过请我喝一杯来支持我的工作…

ibexorigin.medium.com](https://ibexorigin.medium.com/membership?source=post_page-----f62bcf62d43c--------------------------------)

5 个阻碍机器学习应用的挑战

原文:towardsdatascience.com/5-challenges-hampering-machine-learning-adoption-1aa512b1808c?source=collection_archive---------19-----------------------#2023-03-27

克服障碍,释放机器学习在商业中的全部潜力

Irfan AkTowards Data Science Irfan Ak

·

关注 发表在 Towards Data Science ·6 分钟阅读·2023 年 3 月 27 日

--

图片由 Lukas Tennie 提供,来源于 Unsplash

根据 CB Insight 发布的人工智能报告,基于人工智能的初创企业在 2022 年第一季度获得了151 亿美元的融资。尽管这个数字对许多人来说可能看起来很有希望,但融资额已经回落到疫情前的水平。尽管投入了数十亿美元在人工智能领域,许多企业仍然在机器学习的采用上挣扎。原因有很多,这正是我们在本文中要探讨的内容。

如果你对了解一些阻碍企业实施机器学习系统的常见挑战感兴趣,那么这篇文章就是为你准备的。在这篇文章中,你将了解到五个阻碍企业采用机器学习的挑战。

以下是阻碍企业采用机器学习的五大挑战以及如何克服这些挑战的方法。

1. 改变对立的文化

如果你的企业文化发生对立的变化,实施机器学习将变得困难。对立可能来自各种不同的来源。可能来自不重视的高层管理人员,也可能来自不愿改变的员工,因为他们习惯于现有的系统。你如何处理这种对立将决定你的机器学习实施是失败还是成功。

如果对立来自高层管理人员,可能是因为他们不愿意冒险和投资于机器学习项目。另一方面,如果对立来自员工,可能是因为他们害怕失业。员工通常将自动化以及机器学习和人工智能视为敌人,而不是朋友。

一旦你识别出来源,就该采取纠正措施以改变他们的思维方式。例如,如果对立来自员工方面,你应该专注于说服他们,说明机器学习将节省他们的时间并使他们的工作更轻松。它可以自动化他们厌恶的繁琐和重复的任务。

另一方面,如果对立来自高层管理人员,你应该让他们看到投资的回报。一旦他们看到你的机器学习项目能够快速带来指数级的回报,他们将开始支持你的机器学习采用计划,而不是反对它。

不要做出空洞的承诺和保证,而是从小处开始,逐步推进。这不仅会给你前进的信心,还会向利益相关者展示机器学习的采用确实可以给他们的业务带来好处。首先关注快速获胜的案例,瞄准容易实现的目标,然后再将机器学习项目扩展到组织的其他部分。

2. 识别你的机器学习用例

假设你已经跨过了第一个障碍,并且没有变革阻力。下一个障碍是为你的业务找到合适的机器学习用例。毕竟,你不能仅仅因为其他人都在做机器学习就实施它。

在实施机器学习之前,你需要确定具体的机器学习用例。机器学习是一个广泛的领域,你应该对需要专注的子领域有清晰的认识。问问自己,你的机器学习计划是否利用了计算机视觉、自然语言处理或机器学习的机器人流程自动化能力。

回答这个问题将使你的业务能够制定清晰的策略。从最关键的业务功能开始,因为它可以帮助你证明你的观点。它可以是计算机视觉驱动的组装线或数据分析驱动的零售营销活动。

这可能因业务而异。你还可以将所有注意力转向解决根本性问题。这可能是流程导向的问题或其他任何问题。机器学习可以帮助你填补这些漏洞,从而提高业务效率和生产力。

3. 选择合适的训练数据

如果你的业务已经克服了变革阻力和用例问题?下一个障碍可能是找到合适的数据来训练你的机器学习算法。机器学习算法的效果取决于你用于训练它们的数据。强烈建议你用多个数据样本训练你的机器学习模型,这样它可以涵盖所有方面。

这意味着机器学习算法的结果受你用于训练的数据的影响很大。这就是为什么使用高质量、经过标准化且无任何偏见的数据对于获得机器学习模型的最佳结果至关重要。

即使你提供给这些机器学习模型的数据质量很高,它仍然需要进行标准化。跨多个数据源的数据一致性至关重要,因为任何数据的不一致性都可能对最终输出产生负面影响。

假设你训练机器学习模型的数据质量很高但数量较少,你仍然不会获得理想的结果。机器学习模型需要大量的数据来训练。你能提供给机器学习模型的高质量数据越多,最终输出效果就会越好。数据量大的另一个好处是它可以防止机器学习模型出现过拟合现象。过拟合是指机器学习模型对训练数据掌握得过于深入,以至于无法对新数据进行泛化。

总而言之,你输入机器学习模型的数据必须是多样化、全面、规范化且高质量的。数据的量也应更高,以便能够为现实世界问题提供正确的解决方案。

4. 将机器学习与人力资源融合

即使你已经正确训练了你的机器学习算法,仍然存在产生奇怪结果的风险。机器学习模型复杂,有时可能产生意外或甚至违背直觉的结果。例如,一个预测模型可能表明某个候选人最适合某个职位,但人力资源招聘人员可能会基于数据中未捕捉的其他因素不同意这一点。

当你完全依赖机器学习模型做出决策时,决策可能会存在某种偏见。由于机器学习利用历史数据来做决策,而这些数据可能包含偏见,因此也可能使你的决策产生偏见。最糟糕的是,这些机器学习模型可能会放大这些偏见,并将其融入最终输出中。

我们可以很容易地在自动简历筛选软件中看到这些例子,这些软件用于识别适合工作的候选人。哈佛商学院发现,这种招聘软件由于严格的选择标准而拒绝了许多候选人。由于机器学习模型有时会产生意外结果,持续的人类监督是必要的。

为了减少意外结果的可能性,必须在人类决策过程中保持参与,而不是完全交给机器。你可以通过包括人类专家审查或允许人类解读机器的最终结果来实现这一点。

5. 基础设施不足

机器学习的实施是一个资源密集型的过程,无论是从财务还是人力资源角度来看。你不仅需要具有实际操作经验的专业人员,还需要开发一个可能非常资源密集的机器学习基础设施。

不仅如此,企业还必须投资于能够运行机器学习算法和模型的硬件和软件。不论是强大的计算机系统、专业的软件工具还是高水平的存储基础设施,它们都是确保机器学习模型顺利运行的关键。

不仅如此,企业还必须投入时间、金钱和精力,开发一个由适当技能和专业知识支持的机器学习管道。没有这些,将无法有效运行机器学习算法,并从中获得最佳结果。

要开发一个机器学习基础设施,你需要具备模型选择、数据获取、数据可视化、模型测试等能力。此外,你还需要一个自动化的机器学习管道。这一切都需要大量投资,并不是每个企业都有足够的预算来资助这些活动。

结论

机器学习有可能彻底改变企业运营的方式,但目前有几个挑战阻碍了其普及。这些挑战包括数据质量不足、人才短缺、监管问题、可解释性和偏见。克服这些挑战需要多方面的方法,包括投资数据质量、提升员工技能、确保合规、使用透明且可解释的模型,并通过细致的算法设计来减轻偏见。通过解决这些挑战,企业可以释放机器学习的全部潜力,并在各自的行业中获得竞争优势。

在你看来,机器学习普及中的最大障碍是什么?在下面的评论区与我们分享。

每个数据科学家都应该知道的 5 种变点检测算法

原文:towardsdatascience.com/5-changepoint-detection-algorithms-every-data-scientist-should-know-e2ebb83d215f

时间序列分析中变点检测算法的基本指南

Satyam KumarTowards Data Science Satyam Kumar

·发表于 Towards Data Science ·3 分钟阅读·2023 年 3 月 7 日

--

图片由 Gerd Altmann 提供,来自 Pixabay

时间序列分析是数据科学家必须了解的主题之一。时间序列分析包括用于查看时间序列数据的过程和数学工具集,以了解发生了什么、何时发生以及为什么发生,并预测未来最有可能发生的情况。

变点是时间序列数据中的突然变化,可能表示状态之间的过渡。在处理时间序列预测用例时,检测变点对于识别随机过程或时间序列的概率分布何时发生变化至关重要。

在样本时间序列图中可能的变点(已突出显示)

本文将讨论和实现 4 种变点检测技术,并对它们的性能进行基准测试。

1. 分段线性回归:

当变点发生时,时间序列数据的模式或趋势会发生变化。分段线性回归模型的基本思想是识别不同数据区域内的模式或趋势变化。在存在变点的情况下,系数的值通常会比邻近区域的值高或低。

**Pseudo-code of the Implementation:** 1\. Divide the time-series data into sub-sections of x (say 100) days
2\. Iterate through each sub-section of the data:
    - Train data: enumerate of the data
    - Target data: raw time-series value
    - Train a linear regression model on train and target data
    - compute coeffcient of the trained LR model
3\. Plot the coefficients

(作者提供的图片),线性分段变点检测算法的结果

上述图像中的红线代表每个线性回归模型在该时间序列数据子集或部分上的系数值。系数是乘以预测值的值,因此预测值越高,系数也越高,反之亦然。

(作者提供的代码),分段线性回归换点检测算法的实现

2. Change Finder:

Change finder 是一个开源的 Python 包,提供实时或在线换点检测算法。它使用 SDAR(Sequentially Discounting AutoRegressive)学习算法,期望换点前后的 AR 过程会有所不同。

SDAR 方法有两个学习阶段:

  • 第一学习阶段:生成一个称为异常分数的中间分数

  • 第二学习阶段:生成能够检测换点的换点分数

(作者提供的图像),change finder 换点检测算法的结果

(作者提供的代码),change finder 换点检测算法的实现

3. Ruptures:

Ruptures 是一个开源的 Python 库,提供离线换点检测算法。该包通过分析整个序列并分割非平稳信号来检测换点。

Ruptures 提供了 6 种算法或技术来检测时间序列数据中的换点:

  • 动态规划

  • PELT (修剪精确线性时间)

  • 核心换点检测

  • 二分法分割

  • 自下而上的分割

  • 窗口滑动分割

(作者提供的图像),ruptures 换点检测算法的结果

(作者提供的代码),ruptures 换点检测算法的实现

结论:

在本文中,我们讨论了 3 种流行的实践技术来识别时间序列数据中的换点。换点检测算法有广泛的应用,包括医疗状况监测、人类活动分析、网站跟踪等。

除了上述讨论的换点检测算法,还有其他监督式和无监督式的 CPD 算法。

参考文献:

  1. Change finder 文档:pypi.org/project/changefinder/

  2. Ruptures 文档:centre-borelli.github.io/ruptures-docs/

感谢阅读

5 种代码优化技术,提高程序运行速度

原文:towardsdatascience.com/5-code-optimization-techniques-to-speed-up-your-programs-cc7740381bcb

使用这些与语言无关的方法使你的代码更高效、更专业

尼古拉斯·奥伯特Towards Data Science 尼古拉斯·奥伯特

·发布于 Towards Data Science ·7 分钟阅读·2023 年 11 月 29 日

--

图片来源于 Shubham DhageUnsplash

先使其工作,然后使其更快。这是许多专业程序员遵循的一个常见原则。起初,你可能会使用看起来最直观的方法编写代码,以节省草稿开发时间。在你获得一个可运行的实现后,你可能会希望通过仔细选择哪些技术数据结构在你的具体情况下效果最佳来优化代码。

在本文中,我们将探讨五种与语言无关的方法,你可以用来改善代码的运行时间。以下概念是通用的,可以应用于任何编程语言。

循环不变项提取

请考虑以下 Python 代码,它检查一个字符串列表与正则表达式的匹配情况:

import regex as re

# Get some strings as input
strings = get_strings()
# List to store all the matches
matches = []

# Iterate over the input strings
for string in strings:

  # Compile the regex
  rex = re.compile(r'[a-z]+')

  # Check the string against the regex
  matches = rex.findall(string)

  # Finally, append the matches to the list
  matches.extend(matches)

循环会将一组指令反复应用于变化的输入。考虑到这一点,你能在上面的代码中找到任何不变的操作吗?

语句 rex = re.compile(r’[a-z]+’) 在一个常量输入上操作:正则表达式字符串。在每次循环迭代中,这个语句都会做完全相同的事情,与循环的输入无关。如果我们将这个不变的语句提取出来,并在循环之前执行一次,代码仍然会保持相同的整体行为,同时节省一些 CPU 周期。

import regex as re

# Get some strings as input
strings = get_strings()
# List to store all the matches
matches = []

# Compile the regex only once before the loop
rex = re.compile(r'[a-z]+')

# Iterate over the input strings
for string in strings:

  # Check the string against the regex
  matches = rex.findall(string)

  # Finally, append the matches to the list
  matches.extend(matches)

一般来说,每个循环不变的变量或操作(不依赖于循环的输入或状态)都应该从循环中提取出来,只要代码逻辑保持不变。

有时,编译器会自动对你的代码应用这种优化。然而,它们并不总是能够检测到冗余语句,而且解释型语言没有提前优化的特权,因此你应该关注循环不变代码。

枚举状态和类型

在表示变量对象状态时,初学者程序员可能会想到使用字符串。考虑以下 Rust 代码:

struct Door {
    pub state: &'static str,
}

impl Door {
    fn new() -> Door {
        Door { state: "closed" }
    }
}

fn main() {
    // Create a new door objetc
    let mut door = Door::new();

    // Set the door state to open
    door.state = "open";

    // Check if the door is open
    if door.state == "open" {
        println!("The door is open!");
    }

    // Set the door to another state
    door.state = "semi-closed";

    // Commit a typing mistake
    if door.state == "semi-clsed" {
        println!("This won't get printed!");
    }
}

虽然字符串是一个直观的解决方案,但存在一些问题。首先,字符串状态容易出现输入错误,如最后一个 if 语句所示。此外,可能的状态有哪些?

不管怎样,我们来谈谈优化。字符串比较非常慢,因为你必须检查每一个字符以确定它们是否相等。此外,字符串需要比其他替代方案更多的字节来存储。例如,你可以使用枚举来表示对象状态,而无需担心输入错误,同时利用整数比较的速度。

struct Door {
    pub state: DoorState
}

impl Door {
    fn new() -> Door {
        Door {
            state: DoorState::Closed
        }
    }
}

enum DoorState {
    Open,
    Closed,
    SemiClosed,
    Locked,
}

fn main() {
    // Create a new door object
    let mut door = Door::new();

    // Set the door state to open
    door.state = DoorState::Open;

    // Check the door state
    if matches!(door.state, DoorState::Open) {
        println!("The door is open!");
    }

    // Match all possible states
    match door.state {
        DoorState::Open => println!("The door is open!"),
        DoorState::Closed => println!("The door is closed!"),
        DoorState::SemiClosed => println!("The door is semi-closed!"),
        DoorState::Locked => println!("The door is locked!"),
    }
}

枚举是一种基于整数的抽象,所需存储的内存非常少。除此之外,枚举通常是按值传递的,从而避免了在比较等操作时的解引用开销。许多语言原生支持枚举,大多数语言允许这种模式。

代数和布尔操作

考虑以下包含条件语句的代码片段:

def is_zero(number):
    if number == 0:
        return True
    else:
        return False

def count():
    counter = 0
    MAX = 10

    for i in range(100):
        counter += 1
        # Reset the counter when it reaches MAX
        if counter == MAX:
            counter = 0

如果语句被编译为条件跳转指令,与线性分支执行相比,这可能会显著降低你的代码速度。有时,可以用等效的表达式替代条件语句,通常更快,具体取决于其长度和操作。

以下是使用布尔和算术表达式优化过的前面的函数,而不是条件语句:

def is_zero_algebra(number):
    # The result of comparison is already a boolean
    return number == 0

def count_algebra():
    counter = 0
    MAX = 10

    for i in range(100):
        # Use the remainder operator to reset the counter
        # when it reaches MAX
        counter = (counter + 1) % MAX

然而,你应该始终对任何假定的优化替代方案进行基准测试,以检查它是否确实更快。

备忘录化

不,这不是拼写错误。备忘录化是一种算法优化技术,它包括记住函数的输出及其输入。当处理那些需要多次调用的资源密集型函数时,你可以将输入和结果存储在映射数据结构中,这样如果输入相同,就不需要重新计算函数。

一个可以通过备忘录化来改进的经典例子是计算斐波那契数列。考虑下面的代码,它计算斐波那契数列中的第 n 个数字:

def fibonacci(n):
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

R = 30
for i in range(R):
    fibonacci(i)

对于较小的输入n,函数的执行时间不会很长。然而,由于其时间复杂度为 O(2ⁿ),较大的输入值将导致显著更长的运行时间。

现在考虑另一种利用备忘录化的方式:

# Use a class to implement the memoization technique
class MemoizedFibonacci:
    def __init__(self):
        # When the object is created, initialize a map
        self.memo = {}

    def fibonacci(self, n):
        if n <= 1:
            return n
        elif n not in self.memo:
            # Store the input-output pair in a map data structure
            self.memo[n] = self.fibonacci(n-1) + self.fibonacci(n-2)
        # Return the stored output value that corresponds to the given input
        return self.memo[n]

memoized_fibonacci = MemoizedFibonacci()
for i in range(R):
    memoized_fibonacci.fibonacci(i)

从下图中可以看出,备忘录化极大地改善了函数的运行时间:

常规斐波那契函数和经过记忆化优化的替代函数的基准测试(数值越低越快)

这是因为这个记忆化函数的时间复杂度大致是线性的。

记忆化斐波那契函数的时间复杂度

使用特定案例的数据结构

数据结构选择的一个常见例子是普遍存在的链表与数组困境。你需要链表的 O(1)插入时间,还是需要数组的快速随机索引?在选择数据结构时,你必须比较每个选项的优缺点,以找到最适合你的情况的结构。有时,你甚至可能想要实现一个定制的数据结构,以完全符合你的要求。

下面是一些优化中常见的数据结构选择的其他示例:

  • 写时复制实现(COW)。实现 COW 的结构允许你通过共享不可变引用高效且安全地传递数据。只有在你实际尝试修改数据时,它才会被复制,从而节省了不必要的操作时间。

  • 循环缓冲区是实现队列行为或缓存数据时,常见的传统数组替代方案。

  • 查找表哈希表用于快速索引数据或高效处理不同情况而无需分支。

  • 解析表是一种将查找表和哈希表组合在树状结构中的方式,这使你能够高效且简洁地解析结构化数据,而无需编写复杂且易出错的代码。它们在编译器和分类算法中被广泛使用。

主要要点

优化代码不是一项简单的工作。你需要首先找到相关的瓶颈。然后,仔细检查是否存在冗余操作或是否有更直接的方法来解决问题。你可能需要将你的想法草绘到纸上,以更好地可视化算法、内存布局和数据结构。最后,进行基准测试和测试,看看你是否真正改进了代码,或者破坏了某些功能。

我希望你喜欢这篇文章。如果你有任何想法,请在评论中分享。感谢阅读!

如果你有兴趣了解更多关于代码优化的信息,请查看以下关于查找表和哈希表的文章:

[## 使用查找表和哈希表摆脱过多的 if-else 语句

一种在专业代码库中普遍使用的代码性能实践。通过这些简单的示例学习如何使用它们。

betterprogramming.pub

分析师和数据科学家的 5 个常见数据治理痛点

原文:towardsdatascience.com/5-common-data-governance-pain-points-for-analysts-data-scientists-8efe8a007ac2

理解支持创新的保护措施

Col JungTowards Data Science Col Jung

·发表于Towards Data Science ·阅读时间 14 分钟·2023 年 8 月 24 日

--

图片:Headway(Unsplash)

你是大型组织的分析师还是数据科学家?

如果你曾遇到过这些让人挠头的问题,请举手:

  • 寻找数据就像是进行一次福尔摩斯探险。

  • 理解数据血统非常令人沮丧。

  • 访问数据成为了与官僚主义怪兽的对决。

这是我常听到的一个普遍的调侃:

“那些数据治理的家伙真知道怎么让生活有趣……”

是时候对他们宽容一些了。

从我在澳大利亚一家大型银行担任工程师和数据科学家的经验来看,已经有半个十年,我有幸在这场激烈的争论中跨越两方:既是数据的饥渴消费者,又同时作为他人的守门员。

更新:我现在在YouTube上聊数据分析。

在这篇文章中,我将进行三部分的深入探讨……

  1. 基础知识:数据如何在组织中流动这很混乱!

  2. 理解 数据用户常遇到的痛点

  3. 启示:欣赏保护措施如何支持创新

第三点非常重要。

全球组织都在争相成为数据驱动型公司。创新与适当控制之间的张力不断存在,以保障公司客户、员工和声誉的安全。

随着新的数据使用案例不断出现——这几乎是随时都在发生——数据治理结构尝试同步演进。而这通常是一个挑战,因为无拘无束的创新没有自然的速度限制。

无限量的数据自助餐听起来不错,直到你的公司因客户数据泄漏到暗网而被监管机构罚款数百万美元。

哎呀,应该有这些控制措施。

数据的生命之圈

数据像下游的河流一样流经一个组织。

从捕获到使用,演变中的数据需要进行管理。

数据生命周期。图像由作者提供

这个数据生命周期的每个阶段都有其自身的...

  • 利益相关者;

  • 独特的风险;

  • 商业和技术考虑;

  • 道德困境;

  • 监管要求...

…所有这些都需要被仔细管理。

让我们简要回顾一下每个阶段。我将从我的银行和金融行业中举一些例子。

1. 捕获

第一个阶段描述了数据在公司内部的诞生

数据可以在操作源系统中创建,例如新的客户姓名或地址。它可以是前线银行人员在分行计算机中保存的客户 ID员工 ID。或者可能是一个计算的指标,例如总利润或风险加权资产,这些数据汇集了其他维度表中的数据。它甚至可能是外部数据,例如中央银行的新现金利率,这将对我们的储蓄和抵押贷款产品产生连锁反应。

在捕获过程中,关键是定义我们正在捕获的内容,并定义数据质量的期望。

如果一个数字代表某种货币,我们需要知道它是美元、英镑、人民币等。是否可以有空字段?是否总是需要有一定数量的数字,例如邮政编码(ZIP 码)?

这决定了我们需要具备的控制措施,以确保未来的数据符合期望。数据控制的目的是减少数据风险

最后是隐私合规性。如果数据涉及我们的客户和个人——这些数据可能是敏感的,可能会被用来识别他们——我们是否拥有捕获这些数据的同意

如果处理不当,当发生重大黑客事件时,可能会摧毁业务。

2. 过程

现在我们正在处理这些数据。

它可能是在系统之间移动数据而不进行更改,例如将副本导入企业数据湖中供数据科学家稍后使用。(但领导者需要确保数据不会过时!)

或者应用业务规则来过滤、聚合或以其他方式转换数据——通常将其与其他数据源集成——以生成一个精细化的输出数据集,存储在企业数据仓库中,准备供业务使用。

企业数据格局的 30,000 英尺视图。来源:Z. Dehghani 于MartinFowler.com,作者进行了一些修订

在这个阶段,重要的是要考虑以下几点:

  • 数据质量:在处理过程中没有引入错误;

  • 可追溯性:跟踪数据来源,以便清楚地了解随时间的变化;

  • 效率:战略性地设计转换以减少冗余的 ETL 管道,最小化技术债务;

  • 问责制:为公司的不断变化的数据指定一个数据所有者。这至关重要,因为数据来源中的任何问责缺口都可能影响数据质量并导致可预防的风险。

猜猜看?大公司在所有这些方面都遇到困难。

错误会发生,找出错误的根源可能很麻烦——特别是当没有正确的流程和技术来有效跟踪数据来源时。

此外,大多数组织有一个不健康的习惯,让一堆数据管道累积,因为各个团队在孤岛中为每个新项目制作一个新的 ETL 管道。(这就是企业数据产品的作用所在。)

最终,分配数据所有权和让数据用户查找它们可能是一个长期存在的挑战——稍后会详细讨论。

近年来,像微软这样的公司推出了一体化分析平台。这些云解决方案提供了数据摄取、处理、分析和管理的无缝统一体验,从而使处理企业数据变得更简单。

3. 保留

在这里,我们关注的是数据存储。(但也包括备份和恢复!)

这些可能听起来无聊,但搞错的后果是灾难性的。一些关键考虑因素:

  • 可用性。保存数据很容易。检索数据,尤其是批量或实时——大规模——则很困难。此外,数据是否对所有知识工作者无缝可发现和可访问?数据本身的价值很小;通过人力输入和分析从数据中提取的信息和见解才真正解锁价值。

  • 架构。我们是将数据存储为适合 SQL 爱好者的写时模式数据仓库中的表格?数据应该如何建模?或者我们将数据存储为读时模式数据湖中的非结构化平面文件,这提供了更多的灵活性,但在处理较小数据集时性能较差?我们应该如何组织分区?实时处理和分析是否可能?如果平台出现故障,我们是否有足够的备份和恢复措施?

  • 隐私与安全。我们的数据是否经过加密,并且在防止未经授权的访问和黑客攻击方面安全存储?尤其是我们那些受保护和敏感的数据,这些数据可能被用来识别我们的客户和人员。我们是否拥有正确的基于角色的访问权限(RBAC),以确保只有正确的人拥有正确的访问权限?在受监管的行业中,这些领域的泄露通常会导致巨额的数百万美元罚款、股价暴跌以及对公司声誉的重大打击。这不是开玩笑——我有过这样的经验。

4. 发布

啊,激动人心的部分。

这就是我们将数据转化为见解,并将其发布为信息或报告,以供内部和外部利益相关者消费的地方。

数据质量在这里至关重要,因为垃圾进垃圾出(GIGO)

你是什么你吃的,报告和机器学习模型也是如此。

不良数据会导致不可靠的见解,这代表了任何组织中的主要数据风险来源。

由于确保公司所承担的庞大数据量的良好数据质量几乎是不可能的,大型公司通常会采用有针对性的策略,专注于其数据质量检查中的关键数据元素(CDEs)

公司的关键数据元素(CDEs)构成了它们满足客户、投资者和监管要求所需的关键数据,并且这些数据需要最高级别的治理和审查。

目前,我的银行正在跟踪 1500 个 CDE,这些 CDE 在 1700 多个系统中流动。在之前的澳大利亚政府对整个银行业进行打击后,我们建立了一个由多个平台组成的‘操作系统’,该系统 24/7 监控这些 CDE 的数据质量,并在情况开始滑坡时自动创建事件。这是非常严肃的事务。

我所说的数据质量是什么意思?

以下是一些关键数据质量维度,按严重程度递减:

  • 完整性:不完整的数据意味着我们可能缺少维持业务运转和满足合规要求的关键性信息。这就是你如何破产的。

  • 有效性:虽然我们有记录,但它们可能并不有效——这意味着它们与我们在第 1 步中建立的定义模式不一致。这包括现实世界的限制(如负高度)和表特定规则(如唯一键)。

  • 准确性:数据可以存在且有效,但完全错误。想象一下,你的银行不小心将 1000 万美元存入你的账户。(那会是一个令人愉快的惊喜吗?)

  • 一致性:如果你处理的数据准确有效,但以多种方式表示,那么你会面临一致性问题。这是技术债务的一个来源,阻碍组织拥有单一的真实来源。一个经典的例子是地址在不同的源系统中被不同地书写。这会在下游产生多米诺效应,因为同一客户可能会在相同(但不同表示形式)的地址下出现多次。真糟糕。

另一个关键考虑因素是合规性数据的伦理使用

某个特定的数据分析项目是否通过了酒吧测试?它是否合法?我们是否需要获得同意才能将客户数据用于特定目的?

一个好的例子是一个市场分析项目的结果,可能涉及向客户发送有针对性的优惠。然而,他们可能没有给我们必要的同意来将我们的洞察用于营销目的。

大数据被用来理解客户偏好,比他们自己更了解。图片由作者提供

这些都是可能且常常使公司陷入困境的情况,导致重大财务和声誉损失。

5. 归档

最后一阶段是经常被忽视的数据归档或处理,即数据到达其生命周期的潜在终点时。

一般规则是应该清除不必要的数据。然而,某些数据出于监管目的必须保留,通常在澳大利亚的金融服务行业中需保留 7 年。

问题在于公司通常难以确保及时清除数据,而延长保留期限只会加大数据风险。

想象一下:发生数据泄露,20 年的客户数据被盗,尽管其中 13 年数据在使用期限到期后应已删除。这是一个常见的场景,完全可以通过良好的数据治理加以预防。

治理看门人与分析师的愿望

我会听你倾诉。

在讨论了数据生命周期以了解各个阶段面临的各种治理挑战后,让我们深入探讨分析师和数据科学家在追寻数据过程中提出的一些常见抱怨

“为什么我找不到我想要的数据?我瞎了吗?!”

不!完全可以理解。大多数组织在建立必要的基础设施以应对不断扩展的数据海洋和在分析洞察方面的竞争压力时,都经历了艰难的过程。

随着数十年的技术债务以庞大的 ETL 管道和数据仓库的混乱形式出现,以及现代数据湖中倾倒的海量数据,找到数据已演变成一项艰巨的任务——即使是治理人员有时也不例外。

与全球公司一致,我的银行依赖于数据产品和数据民主化策略来应对这些生产力杀手。

“这些表格的数据拥有者是谁?这应该很简单吗?”

答案分为三个部分。这是具有挑战性的……

  1. 分配谁应该负责数据。数据在组织内流动并与其他来源整合时,谁应该拥有这些数据?我们公司的数据管理专家最近引入了一个“生产者、处理者和消费者”框架。这个方法将整个数据旅程——从捕获到消费——划分为不同的区域,确保在每个阶段都有一个人对数据负责。数据驱动型组织的基础始于端到端的数据责任。

  2. 说服人们他们应该负责数据。这是因为拥有数据意味着承担数据被不当使用的风险,加上协助管理数据的额外劳动。这要求很高,却没有多少回报。(数据拥有者通常不会因为这个角色得到额外的津贴。)因此,一些自然的数据拥有者不愿意承担这个角色。

  3. 维护数据拥有者的名单。同事们来来去去。人员换岗。而且他们没有太多的动力来卸任他们的数据拥有者角色。这导致已经负担过重的治理团队在试图跟踪谁是组织内成千上万数据集的当前拥有者。

这是一场挣扎。

“为什么治理相关方总是要求我做这么多文书工作才能访问数据?团队资源稀缺,我们必须花费大量精力在繁琐的程序上!”

因为急于从数据中挤压见解的分析师的目标与保持数据使用合规的需求并不自然对齐。

数据创新者在加速推进,而注重风险的人则踩着刹车。

以我的银行为例。任何尝试处理敏感客户数据的举动都触发了需要进行新的隐私影响评估。是的,这需要时间,也确实很烦人,但这是法律要求,追溯到欧洲发起并随后全球采用的通用数据保护条例 (GDPR)法律。

而且这里有个关键点:这份 PIA 文档作为防线,抵御着主要监管机构对我们开出的高达$50 百万的罚款,因为它展示了我们在推进之前已经考虑了数据使用案例的影响。因为每一次违规行为都是需要承担巨额罚款的。

保险听起来很贵……直到你需要它。

“为什么我的数据发现环境上有这么多条件?感觉像是被限制在狭小的空间里。我只想要一个良好的环境和我想要的所有数据。”

因为你在发现环境中做的所有事情都涉及风险

数据可能会被泄露、不道德使用,甚至导致误导性的见解,损害公司。

这就是为什么数据治理团队会要求你在一开始就非常具体地说明你的数据和环境需求,以便他们能了解你的数据发现沙盒的风险概况。

这些游乐场应该根据用例(用例隔离原则)进行设置和组织,并在项目完成后立即退役。

听起来可能很苛刻,但数据发现环境因其…

  1. 范围蠕变,即项目团队试图在相同环境中挤入更多的用例——因此更多的分析师和数据。这意味着更多的人看到他们不该看到的数据,从而增加了数据风险。

  2. 寿命蠕变,即团队不断延长其环境的使用寿命,这增加了其工作支持业务正常运行(BAU)过程而缺乏适当控制和治理的可能性。这意味着,是的,你猜对了,更多的数据风险。

一切都是为了理解和最小化数据风险。

“为什么从数据发现环境中提取数据这么难?”

因为你可以的事情,坦率地说,对于那些注重风险的人来说,是一场噩梦。

想象一下泄露、滥用,或数据影响决策,从而对数百万客户产生不利影响,或使我们陷入监管机构的困境。

这是一片雷区。

流行的一体化分析平台,在大型公司中受到严格管理。图片由作者提供

这就是为什么发现环境就像是受保护的沙箱,仅供实验使用。如果你希望你的见解达到最佳状态——即生产类似的过程,运行正常的业务——准备好接受一些严格的审查。

锁定数据是一种减少大量数据风险的全面控制措施。

最后的话

似乎我对上述痛点的回答只突显了数据治理控制与分析师和数据科学家所要求的迭代工作风格之间的脱节。

当你面临最后期限时,很容易把治理团队视为敌人。

但没有这些人员,炉子上的火最终会吞噬整个厨房。你的数据治理团队最终确保…

  1. 你的公司的数据资产是管理和组织的,因此在公司数据资产扩展时,你可以找到并访问你所需要的东西。(至少,这是目标。)

  2. 保护措施到位以确保数据的适当使用,并防止可能毁掉客户生活和年终奖金的悲剧性数据泄露。(是的,这是真的。)

更深入地反思,这种创新与监管之间的紧张关系存在于每一个努力中,无论大小。没有束缚的飞跃通常会跟随着一个恢复和反思的时期,为下一次跳跃奠定基础。

请耐心等待。

在全球宏观层面,我们看到像中国和美国这样的强国让他们的大型科技部门在几乎没有监督的情况下飞速发展,直到一代人后才收紧对企业家的监管。在个人层面,我们知道在剧烈身体运动后适当恢复的重要性,这是一个逻辑上的停顿点。

在企业数据环境中存在这种阴阳的动态并不令人惊讶。

随着全球数据量的指数级增长、数据使用案例的日益多样化以及计算能力的不断提升,企业必须定期休整、进行一些清理,确保数据堆栈得到适当的管理和控制。

在大型公司中,通常存在两个相互竞争的目标:

首先,利用数据进行进攻性策略,使公司能更有效地竞争。这种攻击角度通常由一位激进的首席数据官(CDO)、首席数字官(另一个 CDO)或更广泛的分析社区采取。

其次,以防御性的方式管理数据,重点关注合规性。目标是通过对齐流程和系统,解决业务绩效和监管问题,从前台和源系统到后台数据平台和报告工具,简化数据流动。

最顶尖的专家在这一精细平衡行为中表现得相当出色:给予他们的分析师和数据科学家探索和创新的自由,同时确保组织在数据使用方面保持合规和伦理,尊重客户数据。

你在数据治理方面有什么经验?

Twitter 和 YouTube 这里这里这里 找到我。

我的热门 AI、ML 和数据科学文章

  • 人工智能与机器学习:快速入门 — 这里

  • 机器学习与机械建模 — 这里

  • 数据科学:现代数据科学家的新兴技能 — 这里

  • 生成性 AI:大公司如何争先恐后地采纳 — 这里

  • ChatGPT & GPT-4: OpenAI 如何赢得自然语言理解之战 — 这里

  • GenAI 艺术:DALL-E、Midjourney 和 Stable Diffusion 解析 — 这里

  • 超越 ChatGPT:寻找真正智能的机器 — 查看这里

  • 现代企业数据战略解析 — 查看这里

  • 从数据仓库与数据湖到数据网格 — 查看这里

  • 从数据湖到数据网格:最新架构指南 — 查看这里

  • Azure Synapse Analytics 实战:7 个用例解析 — 查看这里

  • 云计算入门:为您的业务利用云 — 查看这里

  • 数据仓库与数据建模 — 快速入门课程 — 查看这里

  • 数据产品:为分析构建坚实的基础 — 查看这里

  • 数据民主化:5 种“数据普及”策略 — 查看这里

  • 数据治理:分析师常见的 5 个痛点 — 查看这里

  • 数据讲故事的力量 — 销售故事,而不是数据 — 查看这里

  • 数据分析入门:谷歌方法 — 查看这里

  • Power BI — 从数据建模到惊艳报告 — 查看这里

  • 回归分析:使用 Python 预测房价 — 查看这里

  • 分类:使用 Python 预测员工离职 — 查看这里

  • Python Jupyter 笔记本与 Dataiku DSS — 查看这里

  • 常见机器学习性能指标解析 — 查看这里

  • 在 AWS 上构建 GenAI — 我的首次体验 — 查看这里

  • 数学建模与 COVID-19 机器学习 — 查看这里

  • 未来工作:在人工智能时代您的职业安全吗 — 查看这里

数据科学作品集的 5 个错误

原文:towardsdatascience.com/5-data-science-portfolio-mistakes-52f6e0ebbe4a

如何制作一个能让你被聘用的作品集

Shaw TalebiTowards Data Science Shaw Talebi

·发布于Towards Data Science ·阅读时间 5 分钟·2023 年 5 月 19 日

--

图片由Kenny EliasonUnsplash上拍摄

当尝试成为数据科学家时,不仅仅是你知道什么,还要展示什么。换句话说,有效地传达你的专业知识和过去的工作是整个过程中的一个关键部分。

最好的方法之一就是通过作品集网站来实现。作品集赋予你信誉,并使你的工作对客户和招聘经理随时可用。

然而,并非所有的作品集都是相同的。有一些关键的错误区分了差的作品集和好的作品集。在这篇文章中,我列出了 5 个错误,这些错误会确保你的作品集永远无法让你找到工作。

错误 1:不要制作作品集网站

最糟糕的作品集就是没有作品集。

404 消息用于作品集。图片由作者提供。

由于大多数人没有作品集,拥有任何东西都会带来大部分好处,并且让你在其他候选人中脱颖而出。

然而,如果你从未制作过网站,你可能会犹豫。你可能会担心这会花费金钱,或者需要花费数小时学习如何编写 HTML 和 CSS 代码。

我也经历过这样的情况。有趣的是,我花了 8 个月才制作好我的第一个作品集,这并不是因为有巨大的成本或时间投入,而是因为我拖延了自己。实际上,我的第一个作品集 没有花费我任何钱,而且大约花了一个周末时间来创建

现在有这么多免费的工具,制作网站从未如此简单。例如,在过去的一篇文章中,我描述了使用 GitHub Pages 创建一个免费且简单的作品集(无需编写代码)的方法。

## 如何用 GitHub Pages 创建一个(免费的)数据科学作品集网站

朝着下一个数据科学角色迈进的 5 个简单步骤

medium.com

错误 2:包括不相关的项目

当你试图成为所有人的一切时,你实际上适合任何人。

如果你避免了错误 1,下一步是挑选合适的项目来展示。但是什么样的项目算是“合适”的呢?

策划你的作品集时,一个至关重要的部分是了解你的受众这个作品集是为谁准备的?他们在寻找什么?我怎样才能直接满足他们的需求?

例如,如果寻找入门级的数据科学角色,请挑选那些展现技术数据技能的项目(如 Python 和 SQL),而不要挑选更适合软件工程或网页开发的项目(如 javascript、HTML 等)。虽然后者在数据科学角色中可能有帮助,但它们并不是关键,最终会分散受众的注意力。

此外,选择一个细分领域可能会有所帮助。细分有助于让你在小池塘中成为大鱼,并帮助避免“我专注于一切”的推销。

即便如此,这可能使你的受众难以看到你如何融入他们的世界。如果受众不能迅速将你作品集中的内容与他们的问题联系起来,他们将会转向下一个候选人。

错误 3:尽可能地塞满 Kaggle 项目

1 个现实世界项目 > 10 个玩具项目。

选择项目时的一个常见错误是数量重于质量。这通常表现为作品集中充满了使用来自 Kaggle 等网站的玩具数据集的项目。

虽然这些项目可以是很好的教育工具,但它们缺乏现实世界数据科学项目的基本要素,例如理解商业问题、在现有流程中工作以及从多个来源综合数据。

在评估项目的质量时,关注影响力。使用影响力作为挑选项目的过滤器,不仅使你的工作更具印象力,还传达了你的工作对某人是相关的。影响力的例子包括:你赢得了黑客马拉松,工作被发表,节省了$X,X 人下载了该项目,你在 X 人面前展示了它,等等。

对于那些刚刚起步且缺乏好的项目选择的人来说,最明显的路径是做一个独立项目。虽然这说起来容易做起来难,但这里有一些快速提示来帮助指导你的努力

  • 根据你的兴趣或职位描述(理想情况下)量身定制项目

  • 从现有的商业或组织中获取(现实世界的)数据

  • 构建一个网络爬虫或探索公共 API 以聚合数据

  • 贡献开源项目

  • 寻找一个导师

错误 4:不要包含任何视觉内容

展示而不是讲述。

最糟糕的作品集是没有作品集。第二糟糕的是没有图片的作品集。

网站的关键优势在于你可以嵌入图片、gif 和视频,不仅可以描述你的工作,还可以展示它。与其通过文字来表达这一点,不如考虑下面的 gif。

左侧是项目的文字描述,而右侧则直接展示了演示。你觉得哪一个更具吸引力?

展示与讲述。(左)项目的文字描述。(右)视频演示。图片由作者提供。

如果你没有现成的漂亮图片,可以花时间制作它们。这里不需要任何花哨的东西。例如,在我的作品集中,我大多数图像都是使用 Keynote(即苹果的 PowerPoint 版本)制作的。

错误 5:忽视所有非技术方面

数据科学版的“全身都是肌肉,却没有腿”。

很容易被数据科学中所有酷炫且令人兴奋的技术方面所吸引,同时几乎忽略所有其他非技术方面。

过于技术化的数据科学作品集。图片由作者提供。

尽管技术技能至关重要,但非技术方面,如领域知识和沟通技能同样重要。如果招聘是由非技术人员进行的(这在没有专门数据科学团队的小型组织中很常见),这一点尤为重要。

一个简单的帮助方法是列出你曾经工作的所有领域。此外,拥有展示项目或技术主题的视频或文字内容可以帮助传达那些关键的沟通技能。

[## 成为数据科学家而无需工作经验的 5 种方法

解决“需要经验才能找到工作,但需要工作才能获得经验”的循环

medium.com

资源

联系我的网站 | 预约通话

社交YouTube 🎥 | LinkedIn | Twitter

支持请我喝咖啡 ☕️

[## 免费获取我撰写的每一篇新故事

免费获取我所写的每个新故事的访问权限。附言:我不会与任何人分享你的电子邮件。通过注册,你将创建一个…

shawhin.medium.com

5 种简单有效的 Python 日志使用方法

原文:towardsdatascience.com/5-easy-and-effective-ways-to-use-python-logging-a9564bd17ccd

像专业人士一样使用 Python 日志

Dmitrii EliuseevTowards Data Science Dmitrii Eliuseev

·发表于 Towards Data Science ·5 分钟阅读·2023 年 6 月 16 日

--

图像由作者生成

我敢打赌,几乎每个 Python 开发人员有时都会使用“print”进行调试。对于原型设计来说没有什么问题,但对于生产环境来说,有更有效的方法来处理日志。在这篇文章中,我将展示 Python “logging” 比“print”更灵活和强大的五个实际原因,以及为什么如果你之前没有开始使用它,你绝对应该使用它。

让我们开始吧。

代码

为了使事情更实际,我们考虑一个玩具示例。我创建了一个小应用程序,用于计算两个 Python 列表的线性回归:

import numpy as np
from sklearn.linear_model import LinearRegression
from typing import List, Optional

def do_regression(arr_x: List, arr_y: List) -> Optional[List]:
    """ LinearRegression for X and Y lists """
    try:
        x_in = np.array(arr_x).reshape(-1, 1)
        y_in = np.array(arr_y).reshape(-1, 1)
        print(f"X: {x_in}")
        print(f"y: {y_in}")

        reg = LinearRegression().fit(x_in, y_in)
        out = reg.predict(x_in)
        print(f"Out: {out}")
        print(f"Score: {reg.score(x_in, arr_y)}")
        print(f"Coef: {reg.coef_}")
        return out.reshape(-1).tolist()
    except ValueError as err:
        print(f"ValueError: {err}")
    return None

if __name__ == "__main__":
    print("App started")
    ret = do_regression([1,2,3,4], [5,6,7,8])
    print(f"LinearRegression result: {ret}")

这段代码可以运行,但我们能做得更好吗?显然可以。让我们看看在这段代码中使用“logging”而不是“print”的五个优势。

1. 日志级别

让我们稍微修改一下我们的代码:

import logging

def do_regression(arr_x: List, arr_y: List) -> Optional[List]:
    """LinearRegression for X and Y Python lists"""
    try:
        x_in = np.array(arr_x).reshape(-1, 1)
        y_in = np.array(arr_y).reshape(-1, 1)
        logging.debug(f"X: {x_in}")
        ...

    except ValueError as err:
        logging.error(f"ValueError: {err}")
    return None

if __name__ == "__main__":     
    logging.basicConfig(level=logging.DEBUG, format='%(message)s')

    logging.info("App started")
    ret = do_regression([1,2,3,4], [5,6,7,8])
    logging.info(f"LinearRegression result: {ret}")

在这里,我将“print”调用替换为“logging”调用。我们做了一个小的更改,但它使输出变得更加灵活。通过使用“level”参数,我们现在可以设置不同的 日志级别。例如,如果我们使用“level=logging.DEBUG”,则所有输出都将可见。当我们确定我们的代码已准备好投入生产时,我们可以将级别更改为“logging.INFO”,调试消息将不再显示:

“INFO” 调试级别在左侧,“DEBUG” 在右侧,作者提供的图像

重要的是,除了日志的初始化之外,不需要任何代码更改!

顺便提一下,所有可用的常量可以在 logging/init.py 文件中找到:

ERROR = 40
WARNING = 30
INFO = 20
DEBUG = 10
NOTSET = 0

正如我们所看到的,“ERROR”级别是最高的;通过启用“ERROR”日志级别,我们可以抑制所有其他消息,只显示错误。

2. 格式化

从最后的截图可以看出,控制日志输出是很容易的。但我们可以做得更多来改进它。我们还可以通过提供“format”字符串来调整输出。例如,我可以指定如下格式:

logging.basicConfig(level=logging.DEBUG,
                    format='[%(asctime)s] %(filename)s:%(lineno)d: %(message)s')

在没有其他代码更改的情况下,我将能够在输出中看到时间戳、文件名,甚至行号:

日志输出,作者提供的图片

大约有 20 个不同的参数,可以在手册的“LogRecord attributes”一节中找到。

3. 将日志保存到文件中

Python 的 logging 是一个非常灵活的模块,其功能可以很容易地扩展。假设我们想将所有日志保存到一个文件中以备将来分析。为此,我们只需要添加两行代码:

logging.basicConfig(level=logging.DEBUG, 
                    format='[%(asctime)s] %(message)s',
                    handlers=[logging.FileHandler("debug.log"), 
                              logging.StreamHandler()])

如我们所见,我添加了一个新的参数“handlers”。一个 StreamHandler 正在控制台上显示日志,而 FileHandler,顾名思义,将相同的输出保存到文件中。

这个系统非常灵活。Python 中有许多不同的“处理器”对象,我鼓励读者自己去 查阅手册。正如我们已经知道的,日志几乎可以自动工作;无需进一步的代码更改。

4. 循环日志文件

将日志保存到文件中是一个不错的选择,但遗憾的是,磁盘空间不是无限的。我们可以通过使用循环日志文件轻松解决这个问题:

from logging.handlers import TimedRotatingFileHandler

...

if __name__ == "__main__":
    file_handler = TimedRotatingFileHandler(
            filename="debug.log",
            when="midnight",
            interval=1,
            backupCount=3,
        )
    logging.basicConfig(level=logging.DEBUG, 
                        format='[%(asctime)s] %(message)s',
                        handlers=[file_handler, logging.StreamHandler()])

所有参数都是显而易见的。一个 TimedRotatingFileHandler 对象将创建一个日志文件,该文件会在每个午夜更换,并且只保存最近的三个日志文件。之前的文件将自动重命名为类似“debug.log.2023.03.03”的格式,经过 3 天的间隔后将被删除。

5. 通过套接字发送日志

Python 的 logging 出奇地灵活。如果我们不想将日志保存到本地文件中,我们可以只需添加一个套接字处理程序,它将使用特定的 IP 和端口将日志发送到另一个服务:

from logging.handlers import SocketHandler

logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s] %(message)s',
                    handlers=[SocketHandler(host="127.0.0.1", port=15001), 
                              logging.StreamHandler()])

就是这样;无需更多的代码更改!

我们还可以创建另一个应用程序来监听相同的端口:

import socket
import logging
import pickle
import struct
from logging import LogRecord

port = 15001
stream_handler = logging.StreamHandler()

def create_socket() -> socket.socket:
    """Create the socket"""
    sock = socket.socket(socket.AF_INET)
    sock.settimeout(30.0)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    return sock

def read_socket_data(conn_in: socket.socket):
    """Read data from socket"""
    while True:
        data = conn_in.recv(4)  # Data: 4 bytes length + body
        if len(data) > 0:
            body_len = struct.unpack(">L", data)[0]
            data = conn_in.recv(body_len)
            record: LogRecord = logging.makeLogRecord(pickle.loads(data))
            stream_handler.emit(record)
        else:
            logging.debug("Socket connection lost")
            return

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s] %(message)s',
                        handlers=[stream_handler])

    sock = create_socket()
    sock.bind(("127.0.0.1", port))  # Local connections only
    sock.listen(1)  # One client can be connected
    logging.debug("Logs listening thread started")
    while True:
        try:
            conn, _ = sock.accept()
            logging.debug("Socket connection established")
            read_socket_data(conn)
        except socket.timeout:
            logging.debug("Socket listening: no data")

这里的难点是使用 emit 方法,它将所有通过套接字接收到的远程数据添加到一个活动的 StreamHandler 中。

6. 奖励:日志过滤器

最后,对那些足够细心到这一部分的读者来说,还有一个小小的奖励。添加自定义日志过滤器也很简单。假设我们只想将 X 和 Y 的值记录到文件中以备将来分析。创建一个新的 Filter 类也很简单,它将仅保存包含“x:”或“y:”记录的日志:

from logging import LogRecord, Filter

class DataFilter(Filter):
    """Filter for logging messages"""

    def filter(self, record: LogRecord) -> bool:
        """Save only filtered data"""
        return "x:" in record.msg.lower() or "y:" in record.msg.lower()

然后我们可以轻松地将这个过滤器添加到文件日志中。我们的控制台输出将保持不变,但文件中只会有“x:”和“y:”的值。

file_handler = logging.FileHandler("debug.log")
file_handler.addFilter(DataFilter())

logging.basicConfig(level=logging.DEBUG, 
                    format='[%(asctime)s] %(message)s',
                    handlers=[file_handler, logging.StreamHandler()])

结论

在这篇简短的文章中,我们学习了几种将日志集成到 Python 应用程序中的简单方法。Python 中的日志系统是一个非常灵活的框架,绝对值得花时间深入了解它的工作原理。

感谢阅读,祝你未来的实验好运。

如果你喜欢这个故事,可以随时订阅Medium,这样你会在我发表新文章时收到通知,并且可以全面访问其他作者的数千篇故事。

5 个简单的 Python 特性,你可以立即开始使用以编写更好的代码

原文:towardsdatascience.com/5-easy-python-features-you-can-start-using-today-to-write-better-code-b62e21190633

我使用 Python 已经超过 8 年了。以下是我喜欢的一些 Python 特性,它们能使你的代码焕然一新且高效。

Thushan GanegedaraTowards Data Science Thushan Ganegedara

·发布于 Towards Data Science ·阅读时间 7 分钟·2023 年 7 月 19 日

--

图片由 Chris Ried 提供,发布在 Unsplash

你必须承认,看到代码或拉取请求上出现类似“这真是超级干净 😎”或“没想到可以这样做”的评论,会让你感到非常愉悦。个人经验告诉我,拥抱良好的软件工程原则并充分利用现有的语言功能,是编写出别人会感激的好代码的秘诀。

作为一名 MLE,我每天都在使用 Python。由于其低门槛和庞大的科学工具生态系统,Python 是机器学习从业者的一个极佳选择。

这意味着,几乎没有软件工程知识的个人也可以快速开始使用 Python。

这最后一句话可以用两种不同的语气来表达:积极的或消极的(试试看!)。

起初这可能看起来是个福音,但从整体来看,缺乏软件工程原则(例如类型、对象)的约束使工程师(MLE)或科学家(DS/AS)不愿编写良好的代码(相信我,我们在软件工程师中已经有了不太好的声誉)。这不可避免地会导致大多数情况下的代码不可读、不可维护和无法测试。而更糟糕的是,有一天它会成为某个毫无防备的受害者最糟糕的噩梦,因为要重复使用这段恶劣的代码。你可能还会看到一种多米诺效应,即在糟糕代码之上构建的代码会导致……更多糟糕的代码。最终,这甚至可能会导致组织上的头痛。

总而言之,在 Python 中做事情很简单,但以正确的方式做事情却很困难。在与 Python 搅斗了 8 年之后,我仍在学习不同(和更好)的方式来改善我的代码。我很幸运有好的软件工程师会建设性地批评我的代码,当我以低效的方式做事时。如果你有同样的支持,那是你的幸运。在这里,我将分享一些可以提升你 Python 技能的方法。

1. 数据类有助于清除杂乱

比如你想管理一个学生及其身高的列表。你可以使用元组的列表来做到这一点。

students = [("Jack", 168), ("Zhou", 172), ("Emma", 165), ("Shan", 170)]

但如果你后来想添加其他属性,如体重、成绩和性别怎么办?如果不头痛并犯很多错误,你无法使用上述数据结构。你可以使用字典,但仍然显得笨重。更好的解决方案是使用 dataclasses

import dataclasses

@dataclasses.dataclass
class Student:
    name: str
    height: int

这样清爽多了!然后你只需实例化一堆 Student 对象。

students = [
    Student(name=“Jack”, height=168), 
    Student(name=“Zhou”, height=172), 
    Student(name=“Emma”, height=165), 
    Student(name=“Shan”, height=170)
]

然后,你可以使用类似 students[0].name 的语法来访问属性。无需再依赖模糊的知识,例如名称在 0th 位置,或使用容易出错的字符串键(如果你使用字典的话)。你可以使用 dataclasses 做很多其他很酷的事情。例如,

  • 使对象不可变(通过使用 @dataclasses.dataclass(frozen=True)

  • 定义 getter 和 setter

  • 使用 dataclasses.field 为属性提供额外支持

  • 将对象转换为字典(.asdict())或元组(.astuple())以进行序列化和兼容性处理。

你可以在 这里 阅读更多关于 dataclasses 的内容。

2. 使用风格在 Python 中进行比较

减少和排序是任何机器学习项目中一个重要的部分。你可能会在简单数据类型的列表(例如 strfloat 等)上使用 minmaxsorted 函数。但你是否知道,有一个巧妙的技巧可以扩展这些基本函数所能解决的问题的范围?

你可以使用 minmaxsorted 创造性地解决问题,通过一个名为 key 的特殊参数。这个键允许你定义逻辑,从可迭代对象中的每个项目中提取一个“比较键”。

比如你想要排序以下的学生身高列表,

students = [("Jack", 168), ("Zhou", 172), ("Emma", 165), ("Shan", 170)]
sorted_heights = sorted(students, key=lambda x: x[1])

更酷的是,假设你有一个 dataclasses.dataclass 而不是这个。

sorted_heights = sorted(students, key=lambda x: x.height)

看起来很酷。也许你想找出身高最高的学生。

tallest_student = max(students, key=lambda x: x.height).name

你知道吗,你甚至可以用纯 Python 模拟 argmax 操作?对于那些不知道的人,argmax 给出列表/数组中最大值的索引。这在很多算法中都是一种强制计算。

a = [3, 4, 5, 2, 1]
max_idx = max(enumerate(a), key=lambda x: x[1])[0]

我曾多次遇到过这样的问题:写了很多行代码,而这些代码本来可以通过更关注关键点来完成。

3. 让 defaultdict 成为你的默认选择

使用字典时,有一个方便的变体可能会让你的生活更轻松。假设你要管理 3 年内股票价格的变化。假设原始格式如下。

stock_prices = [
    ("abc", 95), ("foo", 20), ("abc", 100), 
    ("abc", 110), ("foo", 18), ("foo", 25)
]

你还想把这个转换为字典。你可以这样做:

stock_price_dict = {}
for code, price in stock_prices:
  if code not in stock_price_dict:
    stock_price_dict[code] = [price]
  else:
    stock_price_dict[code].append(price)

这段代码确实完成了任务。但这里有一个使用 defaultdict 的更优雅版本。

from collections import defaultdict

stock_price_dict = defaultdict(list)
for code, price in stock_prices:
  stock_price_dict[code].append(price)

哇,代码这样会更简洁。无需再担心值是否已被实例化。这真的显示了你对 Python 和数据结构的了解。

4. 对 itertools 说“我愿意”

itertools 是一个内置的 Python 库,用于轻松地对数据结构进行高级迭代。

你可能在生活中遇到过需要迭代多个列表以创建一个单一列表的情况。在 Python 中你可能会这样做:

student_list = [["Jack", "Mary"], ["Zhou", "Shan"], ["Emma", "Deepti"]]
all_students = []
for students in student_list:
  all_students.extend(students)

你相信吗,用 itertools 这只需一行代码?

import itertools
all_students = list(itertools.chain.from_iterables(student_list))

比如你想要移除身高低于 170cm 的学生。使用 itertools 这也是一行代码。

students = [
    Student(name="Jack", height=168), 
    Student(name="Zhou", height=172), 
    Student(name="Emma", height=165), 
    Student(name="Shan", height=170)
]

above_170_students = list(itertools.dropwhile(lambda s: s.height<170, students))

还有许多其他有用的函数,比如 accumulateislicestarmap 等。你可以在这里查看更多。使用 itertools,而不是重新发明轮子,这样可以避免冗长和低效的代码。通过使用 itertools,你还能享受到速度上的优势,因为它在底层有高效的 CPython 实现。

5. 打包/解包参数

打包和解包是通过星号 (*) 和双星号 (**) 操作符实现的。理解这一概念最简单的方法是使用函数。你可以定义一个带有打包参数或解包参数的函数。假设我们定义以下两个函数:

# f1 unpacks arguments
def f1(a: str, b: str, c: str):
    return " ".join([a,b,c])

# f1 packs all arguments to args
def f2(*args):
    return " ".join(args)

在调用 f1 时你只能传递 3 个参数,而 f2 可以接受任意数量的参数,这些参数被打包成一个元组 args。所以你会这样调用:

f1("I", "love", "Python")
f2("I", "love", "Python")
f2("I", "love", "Python", "and", "argument", "packing")

这些都可以工作。如果你想要一个字典,其中键是参数,值是传递的参数,你可以使用双星号操作符。

def f3(**kwargs):
    return " ".join([f"{k}={v}" for k,v in kwargs.items()])

要看看这有多好,这里是写 f2 的替代方案:

def f2(text_list: list[str]):
    return " ".join(text_list)

f2(("I", "love", "Python", "..."))

那双重括号已经让我不寒而栗了!使用 *args 甜美得多。

zip() 是一个实际的函数,它接受任意数量的可迭代对象。它通过取出每个可迭代对象的第一个项、第二个项,以此类推,创建几个新的列表。对于以下示例,zip() 让你可以在两种格式之间互换;

[("a1", "b1"), ("a2", "b2"), ("a3", "b3")] # Format 1
<->
[("a1", "a2", "a3"), ("b1", "b2", "b3")] # Format 2

当你处理数据时,这是一种意外常见的需求。记得我们上面的学生示例吗?假设我们只需要排序后的学生姓名列表。我们可以简单地将元组 zip 成两个列表。

students = [("Jack", 168), ("Zhou", 172), ("Emma": 165), ("Shan", 170)]
sorted_students, _ = zip(*sorted(students, key=lambda x: x[1]))

这就是将数据从格式 1 布局到格式 2,并丢弃第二个列表(因为它将包含所有身高数据)。

如果你正在开发一个需要处理任意数量参数的函数,可以使用参数打包。这比传递一个元组或字典要优雅得多。

结论

这些是你下次在 Python 中编写 ML 模型时可以做的 5 件不同的事。坚持良好的软件工程原则和语言标准为一组工程师和科学家提供了共同的基础,从而使他们能够协同工作并快速迭代。通过选择成为更好的软件工程师,你也将为你的同事照亮前进的道路,这将实现个人和团队之间无摩擦的合作。

如果你喜欢这个故事,欢迎订阅 Medium,你将获得来自我的新内容通知,并且解锁对其他作者成千上万精彩故事的完全访问。

## 使用我的推荐链接加入 Medium - Thushan Ganegedara

作为 Medium 的会员,你的部分会员费将直接支持你阅读的作者,同时你也可以完全访问每一个故事……

thushv89.medium.com

我在 Spotify 学到的初级数据科学家五大重要课程(第一部分)

原文:towardsdatascience.com/5-essential-lessons-for-aspiring-data-scientists-i-learned-at-spotify-part-1-7a040a731ecf

科技领域数据科学家的前几年编年史

内幕指南,助你在科技领域的前几年迅速成长并提升你的技能。

Khouloud El AlamiTowards Data Science Khouloud El Alami

·发表于 Towards Data Science ·阅读时间 8 分钟·2023 年 6 月 29 日

--

恭喜你,欢迎加入这段冒险!你正在成为一名数据科学家,你的旅程才刚刚开始!

你已经大学毕业,现在你进入了一个由改变游戏规则的成功人士组成的世界。虽然你可能还不是其中之一(暂时!),但你刚刚踏上了成为其中一员的旅程。你每天都在学习和成长,发现未来的角色以及如何在其中表现出色。

但是等等,几周或几个月过去了,现实是;你依然在心里是一个学生。你可能还没有意识到,因为这个开关还没有切换。

如果有一件事是我在开始科技职业生涯时学到的,那就是:

大学为我提供了良好的技术基础,但有些东西只能在不同的学校环境中学习。

如果你想节省寻找作为初学数据科学家所需核心技能的麻烦,那么你绝对来对地方了。事实上,我希望任何行业的应届毕业生都能在这里找到建议!

在这里,我不仅与你分享我的学习经验,还为你提供有用的技巧,帮助你避免在一开始就犯错。所以我真的鼓励你继续阅读!

确保订阅我的新闻通讯!

点击下面的链接,我将为你提供更多个性化内容和内幕技巧,帮助你在成为数据科学家的旅程中!

[## 加入+2k 读者 💌 关注我在科技和 Spotify 的作为数据科学家的旅程,不要错过!

加入+2k 读者 💌 关注我作为数据科学家在科技+Spotify 的旅程,别错过!通过注册,你…

medium.com](https://medium.com/@elalamik/subscribe?source=post_page-----7a040a731ecf--------------------------------)

但首先,让我给你讲个小故事!

多年来,Spotify 一直是我终极的工作场所。我知道我会在那里茁壮成长,因为它完美地结合了我在职业生涯中想要的一切:

  1. 拥有相似兴趣的人

  2. 平衡的工作与生活

  3. 一个创新的环境来学习和成长

  4. 最重要的是,作为一个小提琴演奏者,我非常重视在一个音乐是核心元素的地方工作

经过大量的努力、坚持和一点点运气,我终于进入了令人惊叹的 Spotify。

你能感受到我有多开心吗?

快进到两次实习,一篇硕士论文,和一个全职工作机会后,我终于找到了我梦想中的工作,天啊,我真是太兴奋了。

到这个阶段,我花了大量时间在项目中不断犯错,最终学到了一些非常宝贵的教训。我真希望我能更早地学到这些教训,因为这会节省我大量时间,并让我免去不断撞墙的挫败感。

错误的做事方式

所以,你做了有价值的工作,找出了一些有趣的见解,甚至可能还提出了一些好的建议!为自己感到自豪,你可以给自己一个击掌!

但如果你像我一样,你可能花了几周或几个月的时间才将你劳动的成果与关键利益相关者分享。人们第一次看到你的工作。为什么?因为你在大部分时间里消失了。

我在过程中犯的一个关键错误 是带着 学生心态 前行。

照片由 Siora Photography 提供,来源于 Unsplash

每次我接受一个大项目时,我都会陷入困境。你可能会告诉我;“K(就是我👋🏼),好吧,你犯了一次错误,但三次?拜托,你难道在课程给你当头一棒时还在打盹吗?” …… 事实上,我是清醒的,但确实花了一些时间才真正领悟到(或者说我可能半梦半醒)。

数据科学家经常独自工作

当然,他们与跨职能团队合作。但数据部分主要由一个人完成——你。所以,当你在大学期间独自工作于项目和作业时,很容易陷入这个陷阱。在现实世界中,你不能再孤立自己。

这种方法的问题在于,即使你的工作可能具有很大的价值,但如果未能及时传达给正确的人,其价值可能会减少影响力。人们可能已经开始做出决策。

所以,如果你想确保你所有的辛勤工作获得应有的重视,你需要尽快抛弃学校式的做法。怎么做? 给你两个字:

沟通✨ & ✨反馈

课程 1 — 持续沟通是关键

我的朋友,如果在你旅程的开始阶段有一项重要技能你需要迅速掌握,那就是:

沟通,沟通,沟通啊啊啊啊!

我会重复到你能理解为止。尽可能频繁地沟通你的工作!在技术领域,节奏可能很快,所以如果你想确保你未来的所有影响不会白费,那么沟通就是你的朋友。

你可以怎么做?

1. 定期向利益相关者更新你的进展,即使你还没有完成。

你可以通过与相关人员分享一些数据精华(最有趣的见解片段)来做到这一点。

你可以通过以下方式做到:

a) 消息(Slack、Teams、Hangout、电子邮件等……)

b) 每周分享

c) 1:1 会议(只要事先做好准备)

每当你发现一个值得注意的见解时,按下发送按钮。这样做可以:

  • 帮助你找出故事线中的漏洞

  • 识别数据不一致性

  • 解决你可能对某些关键概念的误解

    列表还在继续。

2. 确保找到正确的共享节奏

当然,目标不是每天分享你刚刚发现的内容,因为共享不可靠的见解可能会对决策产生负面影响。

相反,你更应该:

  • 不同的共享频率之间进行迭代,直到找到合适的频率

  • 确保高举一个大横幅,上面写着“正在进行中”🚧

在学校里,我们可能会在自己的小圈子里辛苦工作几周。项目可能要经过很长时间才会共享,只有在完成时才会分享。在技术领域,没有人有时间等待。所以尽快加快节奏💨。

课程 2 — 向相关利益相关者请求反馈

作者提供的 GIF

大力推动反馈循环。实际上,你要紧紧拥抱这个反馈循环。你刚刚交了一个新朋友。恭喜!

定期向你的经理或与你密切合作的更有经验的人寻求反馈是至关重要的。关键是要学会何时请求反馈。记住,找到正确的平衡适用于一切。

这可以防止你花费大量时间构建错误的叙事,然后不得不再次修改它。因此,最好尽早理顺你的故事线。

请求反馈和沟通你的见解是同一枚硬币的两面。

  • 沟通使你能够让你的利益相关者了解你工作的内容

  • 反馈使你能够重新检查你是否提供了正确的工作内容

你怎么做?

1. 分享 你的 疑问、想法和发现,通过* 标记 相关人员在你的工作文档或演示文稿中。

我在 Spotify 学到的一个经验是,通过在文档或幻灯片上标记他们的名字来向正确的人寻求反馈,并指出我需要他们意见的部分。当然,如果他们错过了你的求助信息,别忘了通过消息跟进。

2. 定期安排会议 讨论你的工作以及你遇到的任何障碍

这里有一个小贴士:

  • 安排 1:1 会议与最相关的利益相关者直接讨论你的疑问、想法和发现。

  • 确保在文档中概述你的工作背景 + 仅列出你需要反馈的内容。这样,你只会处理最重要的内容,避免浪费每个人的时间。

高级职位的人都是大师,你也想被那种魔法般的光环所笼罩🪄。如果你像我一样有一位出色的经理在帮助你成长,那么一定要好好利用这个机会。你的同事们拥有你还在努力获取的领域专业知识,所以他们看待事物的角度是不同的。

他们肯定会帮助你:

  • 发现情节漏洞

  • 完善你的故事情节

  • 给你更多的背景信息

  • 教你新技能,当他们看到你在原地打转时

全局思维 在你作为数据科学家时至关重要

但你还只是个新手,你的眼界还没完全打开。所以你确实需要另一双眼镜来照亮那些黑暗的地方。

我在科技行业的第一年的快照 (Midjourney)

交流你的见解并定期寻求反馈不仅有助于你保持项目进展正常,还可以帮助你与团队和利益相关者建立信任。当他们看到你的工作并了解你在做什么时,他们会更可能支持你和你的想法。

目前就这些。

本文是两部分系列的第一篇。接下来: 第二部分

## 我在 Spotify 学到的 5 个必备课程(第二部分)

内部指南,帮助你在科技行业中顺利度过第一年并提升你的技能

[towardsdatascience.com

在这篇文章的后续部分,我将详细讲述我在 Spotify 的头几年中学到的其他经验,这些经验可以帮助你快速成功于数据科学的旅程!

我有礼物送给你🎁!

现在就注册我的通讯订阅K’s DataLadder,你将自动获得我的终极 SQL 备忘单,其中包含我在大科技公司工作中每天使用的所有查询语句,还有另一个神秘礼物!

我每周分享作为科技公司数据科学家的经历,以及实用的技巧、技能和故事,旨在帮助你提升水平——因为没有人真正了解,直到他们亲身经历!

如果你还没做过的话

期待与你很快见面!

我在 Spotify 学到的初级数据科学家 5 个重要课程(第二部分)

原文:towardsdatascience.com/5-essential-lessons-for-aspiring-data-scientists-i-learned-at-spotify-part-2-53b84ee5e8ee

数据科学家的第一年纪事

内部指南,帮助你在数据科学家的前几年中取得成功,并提升你的技能

Khouloud El AlamiTowards Data Science Khouloud El Alami

·发表于 Towards Data Science ·阅读时长 9 分钟·2023 年 6 月 29 日

--

这篇文章是“我在 Spotify 学到的初级数据科学家 5 个重要课程”系列的第二部分。确保先查看 第一部分!

## 我在 Spotify 学到的初级数据科学家 5 个重要课程(第一部分)

内部指南,帮助你在数据科学家的前几年中取得成功,并提升你的技能。

towardsdatascience.com

所以之前我们讨论了:

  • 定期与利益相关者分享你的工作,即使它还未完成,这一点也很重要

  • 定期寻求反馈,确保你在正确的轨道上

这样做将帮助你与团队和利益相关者建立信任,以确保你的工作获得应有的影响。说到信任,让我们直接进入第 3 课

第 3 课 — 开始建立信任

产生影响全在于将你的想法推向那些会 将其付诸行动的人

这些人通常是产品经理负责制定产品的愿景和战略)、设计师负责设计产品)和工程师负责评估想法的技术可行性并实现它)。你将分享的见解和提出的建议将推动整个团队(产品经理、设计师和工程师)的工作*。

因此,你需要学习如何说服别人为什么他们应该听取你的想法以及这些想法的重要性,这也就不足为奇了。

(你也可以查看我详细的帖子 ⬇️ 关于如何将你的见解转化为有影响力的行动)

## 步骤指南:数据科学家如何赢得利益相关者以推动影响力

来自 Spotify 的数据科学家——将你的工作转化为行动的最佳组合

towardsdatascience.com

欢迎来到第 3 课!

这可能看起来很明显,但值得信赖是数据科学家的核心角色,这是一种随着时间推移而不断磨练的技能。不过你可能会问我‘K,我该怎么做?我还是个小菜鸟,谁会认真对待我?’

嘘,除非公司相信你能够可靠,否则他们不会聘用你。所以人们很可能已经在信任你,现在的关键是兑现这种信任

那么,当你刚刚起步时,如何建立信任的第一层?

我们已经讨论了如何通过沟通你的工作寻求反馈来提升你的可信度,但让我们看看你还能做些什么!

1. 积极主动,并尽可能地提问

我天生确实有一种自然才能,那就是擅长对人们提出他们没有要求的问题。这在高中可能对我不利,但在职业世界中,尤其是我所知道的技术领域,规则则有所不同。

我发现自己屡次因能够从各个角度提出问题而受到称赞(我本身是 ENTP,反正这就是我唯一能做的事)

展示你对事物如何运作以及为什么感兴趣,肯定会使你顺利成为一个可信赖的真实信息来源和决策者。

2. 保持谦虚,不要害怕承认你不知道的事情

是的,你可以再读一遍。不要像乔恩·雪诺一样。如果你不知道怎么做,没必要假装你知道。这样做可能会对你造成长远的伤害。

谦虚能产生信任,这就是为什么在你的见解中保持谦虚,并在需要时提出免责声明至关重要。

作为新手,我们常常会被诱惑去掩饰自己的无知。然而,处理数据和统计意味着结果并不是绝对的。实际上,关于你对结果的信心水平表现出脆弱可能很困难,但这是重要的。要对你的工作成果的稳健性保持透明!

你怎么做呢?

  • 确保你沟通积极尝试寻找解决方案是成长的第一步。没人会责怪你没搞对,但至少我会责怪你没有尝试

  • 显示你积极参与自己的成长,肯定会提升你在所有观察者眼中的可信度可靠性

解决方案喜欢高深莫测。所以我们是数据科学家也没什么坏处,因为我们确实喜欢挖掘那些隐藏的见解,对吧?

只有这样,你才能从他人那里学习并提升自己。

课程 4 — 向专家寻求帮助

图片由 Nathan Dumlao 提供,来源于 Unsplash

如果我早些时候学会了这一点,那节省的时间真是不可估量。

想象一下:

我正在做一个因果推断项目(这是一个统计学领域,旨在基于观察数据识别变量之间的因果关系)。我在大学时修过这个课程,但似乎记不太清楚(也许这次我又在睡觉了)。无论如何,我正在做这个项目,研究新颖的概念,像我喜欢的那样激发我的大脑。

我向我最亲近的同事寻求建议,回顾过去的项目,希望找到一些灵感、学习经验、技巧、上帝的话语…真的任何可以帮助我的东西。所以,是的,这也是一个你必须掌握的重要技能:

追溯 过去的资源 应该始终是开始新项目时的第一步

但我会在另一个故事中详细讨论这个问题。

所以我在做研究,调查内部和外部资源,却发现自己在因果推断上碰壁(正常,这是个棘手的问题)。我做了所有正确的事情(或者我认为如此)。我继续进行这个项目,经过一段时间…我有了一个很棒的主意,就是向其他数据科学家请教一个概念。

这样做,我显然会提供更多关于我的项目的细节。当…突然间…一个天降因果推断专家的声音照亮了我…友善地告诉我我…走错了路。我的整个方法论都偏离了轨道,因为我在比较两个不能比较的用户群体,这使整个分析变得不准确。

当我意识到自己搞砸了时——照片由 Jelleke Vanooteghem 提供,来源于 Unsplash

然而,我的朋友们,这就是几周的工作如何转瞬即逝成了垃圾桶中的一张单程票!(虽然并非完全如此,因为这将成为你脑中的核心记忆,成为生活的宝贵教训)

这让我们进入了第 4 课—— 学会如何独自克服挑战对发展你的批判性思维和解决问题的技能非常重要。但学会在需要时请求帮助也同样重要。 现在,人们直接去找 ChatGPT 寻求帮助,而不是自己尝试,这最终阻碍了他们学习正确的技能。

向合适的人寻求帮助有很大的好处

1. 从你所从事的工作领域的专家那里获得第一手指导

很明显,这只能给你带来提升:a) 对你的项目b) 对你的技能。记住,你可能还是个新手,但你也在和领域中的专家同一个沙箱中玩耍,所以别忘了在需要时寻求指导(除非你打算再当一会儿新手,那又是另一回事)

你怎么做呢?

  1. 寻找过去的项目,看看你正在从事的工作是否已被实施或研究。然后联系那些曾参与这些项目的人他们很可能会提供有价值的信息,并帮助你识别潜在的不一致性。

  2. 发送消息 到专门讨论问题/技术/功能/产品领域等的 Slack/Teams 频道,例如 #causal-inference#data-science(稍微宽泛一点,肯定会有人回应)

每个人都很乐意提供帮助。毕竟,他们自己也经历过这些。

2. 免去你意识到自己做错事的挫败感

这最终也将帮助你免受由于弥补过去错误而额外工作带来的压力……因为现在,你的计划也已经落后了。

只要记得至少先尝试一下。 如果你发现自己陷入困境的时间比预期的要长,那么你知道该寻求帮助的时候到了。

最后一个建议——对自己要有耐心

照片由 sydney Rae 提供,来源于 Unsplash

如果你已经坚持到这里,那么你绝对值得再获得一块额外的饼干。谢谢你读我的文章。

做好准备,现在我将把我最终的饼干赐给你这段旅程。

没有人期望你从第 1 天或第 100 天起就成为专家。

即使在科技行业也是如此!与经验丰富的人一起工作是一个在良好的环境中成长的独特机会。然而,我确实花了一些时间才能完全接受自己在房间里最没有经验的这一点。

我仰慕我合作的那些人,但我也潜意识地与他们比较自己:

  • 否则我可能需要更长时间才能交付我的工作

  • 我并不总是能提出正确的问题来进行探索,因此感觉相比于我的同龄人,我的探索范围有限

  • 我会在同时处理多个项目时感到挣扎,而其他人似乎能轻松地同时处理 5 个任务

是的,这可能看起来很明显,但至少对我来说并不那么明显。所以,如果你像我一样,有时对自己很苛刻,那是很正常的。追求最优秀很重要,但要知道在刚开始时不必追求最优秀

我的意思是,来吧,你还是个小狗,没人会期望你与完全成熟的狼在同一水平线上。但你在这个群体里,群体不会抛弃自己的成员。所以别担心,总有一天你也会嚎叫,这只是时间和承诺的问题。

从一开始就做到完美实际上并不是常态。而且,冲浪者不会在静水中滑行,这有什么乐趣呢?即使是哈利·波特一开始也没有准确地施展 Wingardium LeviOsa。

那么你能做些什么呢?

  1. 避免通过模仿前辈来过度努力。 很可能,这样做会让你感到不自然,人们也会察觉到。相反,不要强求,问问题时保持自然,做你自己

  2. 联系其他初级人员。 与可以产生共鸣的其他人交流,确实帮助我在自己挣扎时获得了更多的视角。并不一定非得是其他数据科学家,任何你关系亲近的初级人员都会有帮助。知道自己并不孤单并获得支持,改变一切。

所以最后一课: 给自己一些宽容,停止对自己施加额外的压力。如果你没有这样做,那么这个教训可能不适合你,但还是值得记住,记得对自己要善良。

我为你准备了🎁!

订阅我的新闻通讯K’s DataLadder,你将自动获得我的终极 SQL 备忘单,包括我在大科技公司每天使用的所有查询 + 另一个神秘礼物!

我每周分享作为数据科学家在科技行业的经历,包括实用技巧、技能和故事,旨在帮助你提升水平——因为没人真正了解,直到他们身处其中!

如果你还没有做过这个

很快见!

5 种适用于 R 的极佳数据管道编排工具

原文:towardsdatascience.com/5-fantastic-data-pipeline-orchestration-tools-for-r-f34ab71b1730

探索适用于 R 用户的数据管道编排的优秀选项

Chengzhi ZhaoTowards Data Science Chengzhi Zhao

·发表于 Towards Data Science ·阅读时间 11 分钟·2023 年 1 月 30 日

--

图片由 Daria Nepriakhina 🇺🇦 提供,Unsplash

数据管道编排工具对于生成健康且可靠的数据驱动决策至关重要。R 是数据科学家常用的语言之一。凭借 R 的优秀包,R 编程语言非常适合数据处理、统计分析和可视化。

一个常见的模式是将数据科学家在 R 中编写的本地脚本重写为 Python 或 Scala(Spark),然后通过现代数据管道编排工具如 Apache Airflow 调度数据管道和模型构建。

然而,许多现代数据编排项目如 Apache Airflow、Prefect 和 Luigi 都是基于 Python 的。它们能与 R 无缝配合吗?你能用 R 编写来定义 DAG 吗?在本文中,让我们探索适用于 R 脚本的流行数据管道编排工具,并评估哪个适合你的使用场景。

成功的数据管道编排的关键组成部分

根据我的经验,数据管道编排可以分为三个主要组成部分:DAG(依赖关系)、调度程序和插件

DAG(有向无环图)

DAG 定义了数据管道的蓝图。它为执行路径提供了方向。同时,你可以通过查看 DAG 来追踪依赖关系。

为什么 DAG 对任何数据管道的成功至关重要? 因为处理数据需要有一个顺序,以从数据中提取见解。这一顺序从业务规则的角度来看不能更改。否则,数据的输出将是无用的或出错的。

通过将有向无环图中的每个节点视为一个独立的功能,DAG 提供了一个对齐方式,使当前节点必须遵循上游节点定义的规则。例如,当前节点仅在所有上游节点成功时触发;或者当前节点在一个上游节点失败时可执行。

DAG 方便地提供了数据来源的视图。它提升了可见性,同时显著简化了在数据管道中追踪错误的能力。当数据管道在早晨值班时遇到不愉快的错误时,DAG 执行的实例可以快速指出错误发生的位置。

现如今,能够将 DAG 视为蓝图并在运行时查看其实例以检查作业运行状态的功能对于任何数据编排工具都至关重要。

调度器

调度器是执行数据管道的驱动程序。 一个调度器可以像 cron 作业一样简单。更复杂的调度器涉及构建自己的调度器,比如 Airflow 中的调度器,它管理所有任务状态并积极快照。

调度器的作用是什么? 调度器是一个守护进程,可以被视为后台进程。它应该全天候运行,并在达到某个时间或事件时进行监控。如果调用了调度的时间或事件,执行任务并等待下一个任务。

调度周期 | 图片由作者提供

插件

插件用于扩展性,被视为数据管道的潜力。 利用现有的软件包而不是重新发明轮子是很常见的。编排工具插件的丰富性可以节省你集中于业务逻辑的时间,而不是花费数天的时间搜索和编写“如何编写脚本将 Spark 作业提交到 EMR?

如果数据编排工具不断发展,它会吸引更多的供应商和额外的社区开发者来添加更多插件,以吸引更多用户。迁移到其他数据管道编排工具也很昂贵。

不涵盖的选项

  • taskscheduleR:一个 Windows 专用的调度器,与 Windows 任务调度程序一起使用。如果你使用的是 Windows,这绝对是一个值得探索的选项。

  • github.com/kirillseva/ruigi — 这是一个令人钦佩的尝试。然而,该项目似乎处于闲置状态,自 2019 年 5 月 26 日以来没有更多活动。

探索 R 的数据管道编排工具

我们将讨论以下 5 种不同的工具,这些工具适用于不同的使用案例。

  1. cronR

  2. targets

  3. Kestra

  4. Apache Airflow

  5. Mage

1. cronR

成功的数据管道协调的关键组成部分之一是调度。调度程序让你在无需人工干预的情况下运行数据管道更加安心。要调度 R 脚本,cronR 是你首先要探索的解决方案。

[## GitHub - bnosac/cronR: 一个简单的 R 包,用于管理你的 cron 作业。]

使用 cron 调度程序调度 R 脚本/进程。这允许在 Unix/Linux 上工作的 R 用户自动化 R 进程…

github.com](https://github.com/bnosac/cronR?source=post_page-----f34ab71b1730--------------------------------)

该包增强了一组对 crontab 的包装,使得仅使用 R 进行采纳变得更加直接。因此,你不需要担心设置 crontab,cronR 提供了一个减少复杂性的接口。

library(cronR)

f   <- system.file(package = "cronR", "extdata", "helloworld.R")
cmd <- cron_rscript(f)

## schedule R script daily at 7 am
cron_add(
  command = cmd,
  frequency = 'daily',
  at = '7AM',
  id = 'my_first_cronR',
  description = 'schedule R script daily at 7 am'
)

## schedule same R script every 15 mins
cron_add(cmd,
         frequency = '*/15 * * * *',
         id = 'my_second_cronR',
         description = 'schedule same R script every 15 mins')

使用建议

如果你只想调度 R 脚本,这个选项是一个快速且轻量的解决方案。它适用于临时调度、简单依赖关系或涉及较少状态管理的用例。

限制

由于 cronR 仅提供调度程序。你需要自己构建工作流依赖关系。如果一个 R 脚本可管理,那是可行的。然而,如果脚本的规模变得巨大并且需要中间阶段,你可能希望将其拆分。这些是 cronR 不足以处理的情况,因为它仅处理调度部分而不包括 DAG 定义和插件。

2. targets

[targets](https://github.com/ropensci/targets) 包是一个 Make-类似的 R 统计和数据科学管道工具包。

targets 是一个用于数据管道的 R 编程语言工具。你可以轻松定义一个 DAG 来创建依赖图。targets 的主要目标是提供可重现的工作流。它没有自带调度程序,但与我在这里提到的其他工具连接,不应该难以调度从 targets 生成的 DAG。

# functions
get_data <- function() {
  print("getting data")
}

transform_data1 <- function() {
  print("transforming data 1")
}

transform_data2 <- function() {
  print("transforming data 2")
}

loading_data <- function() {
  print("loading data")
}

# _targets.R file
library(targets)
tar_option_set(packages = c("readr", "dplyr", "ggplot2"))
list(
  tar_target(extraction, get_data()),
  tar_target(transform_data, transform_data1_1(extraction)),
  tar_target(transform_data2, transform_data2(extraction)),
  tar_target(loading_data, loading_data(transform_data, transform_data2))
)

管道和函数定义被分为两个 R 文件。你可以在 _targets.R 文件中构建依赖树,并通过 tar_visnetwork() 进行可视化,从而自动生成 DAG。

targets 可视化 DAG | 作者提供的图片

使用建议

你需要本地支持 R 来处理数据管道。targets 包可以帮助 R 用户提高日常数据分析工作的效率。它不需要后台运行额外的守护进程来获取 DAG 进行可视化和按需运行。如果你在寻找一个可以手动运行并寻找 DAG 管理解决方案的选项,targets 对于 R 用户来说是一个很好的选择。

限制

如果你决定使用 targets,你将拥有一个强大的 DAG 管理工具。然而,你需要一个调度器和额外的插件来使其成为生产中的数据管道编排工具。结合像 cronR 这样的调度器可以为你提供一个纯 R 解决方案。

3. Kestra

Kestra 是一个通用的数据管道编排工具。它目前仅支持三种类型的脚本:Bash, Node, 和 Python

尽管 R 语言未被包含在支持的脚本中,但你仍然可以通过使用带有Rscript命令的 bash 脚本来实现 R 脚本的编排。

id: "r_script"
type: "io.kestra.core.tasks.scripts.Bash"
commands:
- 'Rscript my_awesome_R.R'

可以通过在 YAML 文件中使用 Flowable Task 来设置更复杂的 DAG。调度也在 YAML 文件中通过 Schedule 完成。

使用建议

作为 R 用户,Kestra 允许你通过 bash 命令编排 R 代码。此外,Kestra 具有在多种语言中运行数据管道的灵活性。它给你一种现代版的 Oozie 的感觉。如果你对 Oozie 比较熟悉,Kestra 应该更容易上手。

限制

运行 R 不是原生支持的。运行时检索元数据并非简单,单纯使用Rscript命令可能需要额外的学习时间来寻找合适的核心或插件以更有效地开发。

4. Apache Airflow

Airflow 迄今为止是最受欢迎的数据管道编排工具。然而,要编写 DAG,你需要使用 Python。你可以使用 Airflow 操作符来执行你的 R 脚本。然而,定义 DAG 时,R 不在实现范围内。

Airflow 社区曾提出创建ROperator的提案。该提案是利用 rpy2,它为R在 Python 进程中嵌入运行创建接口。核心思想是传递r_command,然后将 R 脚本复制到临时文件中并进行源代码处理。

有很多 R 用户对这个拉取请求感兴趣。

[## [AIRFLOW-2193] 为使用 R 添加 ROperator · briandconnelly · Pull Request #3115 · apache/airflow

确保你检查了以下所有步骤。JIRA 我的 PR 涉及以下 Airflow JIRA 问题并引用了它们……

github.com](https://github.com/apache/airflow/pull/3115?source=post_page-----f34ab71b1730--------------------------------)

然而,这项功能尚未实现。浏览了这个拉取请求后,我们注意到 R 语言的支持在当时不是 Airflow CI 的一部分,其他选项可以执行 R 脚本,因此优先级较低。如果你希望坚持使用 Airflow 生态系统,还有一些选择。

  • 使用 BashOperator 来运行 R 代码。如果你可以从终端运行你的 R 脚本,BashOperator 满足这个要求。使用 BashOperator 也使得构建 DAG 关系、将参数传递到 bash_command 以及添加更复杂的逻辑,如重试和邮件警报,变得简单。
run_this = BashOperator(
    task_id="my_first_r_task",
    bash_command="Rscript my_awesome_R.R",
)
  • 在 Docker 容器中运行 R(参见 rocker)并使用 DockerOperator。这个选项类似于 BashOperator。使用 Docker 容器的好处是,你可以减少在与 Airflow 相同环境中执行 R 脚本的配置时间。Docker 容器每次提供一个新的 R 环境。这是一个不错且干净的解决方案。

  • [如果你仍然想要一个专用的 *ROperator*],你可以复制并粘贴上述拉取请求中的 r_operator.py,并让 Airflow 基础设施团队将其添加进去。

类似的情况也出现在 Prefect。它有一个未解决的开源拉取请求,而且该 PR 被标记为低优先级。

[## 添加 RTask 到任务库 · 问题 #5449 · PrefectHQ/prefect

你目前不能执行该操作。你在另一个标签页或窗口中登录了。你在另一个标签页或窗口中注销了…

github.com

许多现代数据管道编排是使用 Python 构建的。然而,当涉及到利用另一个流行的数据相关语言如 R 时,由于其在设计时没有与其他语言一起开始,所以优先级较低。后来,项目变得过于庞大,难以进行根本性的更改,并且需要从头开始付出巨大努力。

使用建议

如果你已经有了作为数据管道编排器的 Airflow 基础设施,这个选项是好的。Airflow 提供了成功的数据管道编排平台的三个关键元素。你已经建立了基础和资源,使用上述选项运行 R 是可能的。

限制

R 并不是 Airflow 中的第一个公民。在 Airflow 中,原生不支持运行 R。虽然有多种解决方法,但 R 仍然被视为外语。无论你选择使用BashOperator还是DockerOperator,甚至是分叉那个 PR,你仍然需要额外的支持,与数据基础设施团队沟通,以帮助你使 R 脚本在 Airflow 中可运行。

另一个限制是,使用 R 时,实时拉取 Airflow 宏(Airflow 的元数据)并不简单。你仍然可以通过查询 Airflow 的后端来使用复杂的解决方案。然而,对于没有深入了解 Airflow 的 R 用户来说,这并不友好。

5. 法师

Mage 是数据管道编排领域的新玩家。我们讨论的最大收获是 Mage 默认将 R 识别为支持语言的一部分,并允许用户定义 DAG,而不论选择何种语言(目前为 python/SQL/R)。

这是 R 用户的一个里程碑。喜欢 R 的用户在定义一个将 R 脚本封装在受限支持工具中的 DAG 时,不必切换到 Python 语法。

Mage 允许用户使用 R 编写主要的 ETL(提取、转换和加载)块。Mage 通过在 YAML 文件中维护 DAG 依赖关系来构建 DAG。这成为了一种灵活的选择,绕过了编程语言的选择。你还可以在开发 DAG 时可视化 DAG 及其 R 代码块。

Mage 管道编辑模式使用 R | 作者提供的图像

以下是我用来演示如何使用 R 编写管道并在 Mage 中构建 DAG 的三个主要块。此外,你可以轻松地在 R 中访问调度程序元数据,如 execution_date

## Data Loader (Extraction)
## You can download dataset here https://www.kaggle.com/datasets/yanmaksi/big-startup-secsees-fail-dataset-from-crunchbase
load_data <- function() {
    df <- read.csv(file='~/Downloads/big_startup_secsees_dataset.csv')
    ## Access scheduler metadata or user defined variables
    ## This part is powerful that you can access data orchestration metadata at runtime
    df['date'] <- global_vars['execution_date']
    df
}

## --------------------------------------------------------------##

## Trasformation (Transformation)
library("pacman") ## install pacman before
p_load(dplyr) ## dplyr makes it easier to recognize the dataframe column

transform <- function(df_1, ...) {
    ## filter on USA startup
    df_1 <- filter(df_1, country_code == 'USA')
    df_1
}

## --------------------------------------------------------------##

## Data Exporter (Loading)
export_data <- function(df_1, ...) {
    # You can write to file to locally
    write.csv(df_1, "~/Downloads/usa_startup_dataset.csv")
}

一旦在 Mage 中开发了管道,你可以通过使用 crontab 调度管道来附加触发器。

Mage 管道调度 | 作者提供的图像

在内部,Mage 仍然使用 Python 作为核心,并将 R 脚本解析成 tmp 文件,然后使用该文件运行 Rscript 命令。

subprocess.run(
    [
        'Rscript',
        '--vanilla',
        file_path
    ],
    check=True,
)

如果你想了解更多关于 Mage 作为 Apache Airflow 替代方案的内容,我写了一篇文章。

[## Apache Airflow 是否该被替代?mage-ai 的初步印象]

作为数据工程师对 Apache Airflow 的替代方案介绍 mage-ai

chengzhizhao.medium.com](https://chengzhizhao.medium.com/is-apache-airflow-due-for-replacement-the-first-impression-of-mage-ai-ade8208fb2a0?source=post_page-----f34ab71b1730--------------------------------)

使用建议

R 成为 Mage 的主要语言。你可以编写 R 块并顺利访问调度程序元数据,而无需担心如何注入或查询后台。在 Mage 中开发也具有互动性。工程师可以在开发过程中快速迭代测试;他们可以可视化结果,而不是调试一个庞大的 DAG。

限制

Mage 是一个成立于 2021 年的新项目,目前仍处于早期阶段。大量文档需要改进。此外,Mage 的插件数量无法与 Airflow 相比。

最终想法

这里没有涵盖许多数据管道编排选项。对于 R 用户来说,更好地与 R 语言集成的数据管道编排可以减少将初始数据分析转移到生产数据管道中的压力。我希望这里的选项能为 R 用户提供对各种数据管道编排工具的更好见解。

我希望这篇文章对你有所帮助。这篇文章是我关于工程与数据科学故事的系列之一,目前包括以下内容:

赵成志

赵成志

数据工程与数据科学故事

查看列表53 个故事!

你也可以订阅我的新文章或成为推荐的 Medium 会员,获取对 Medium 上所有故事的无限访问权限。

如有问题/意见,请随时在本文下方评论或通过LinkedinTwitter与我直接联系。

5 个函数是管理数据所需的全部工具

原文:towardsdatascience.com/5-functions-is-all-you-need-to-manage-your-data-with-dplyr-1630825c47b0

如何高效地使你的数据准备就绪

Soner YıldırımTowards Data Science Soner Yıldırım

·发布于 Towards Data Science ·8 分钟阅读·2023 年 3 月 10 日

--

照片由 Kelly Sikkema 提供,来源于 Unsplash

那些处理过真实数据的任务的人知道,大部分的工作都集中在数据整理上。

我所说的数据整理包括将数据准备好以供其他利益相关者或下游过程使用的操作。

无论你是数据分析师、数据科学家,还是数据工程师,你都需要在日常工作中执行以下一种或多种操作:

  • 过滤(例如,给我德克萨斯州的销售数据)

  • 排序(例如,我想查看上周的前 10 名畅销产品)

  • 更新(例如,更改这些产品的类别)

  • 总结(例如,我想查看每个类别的平均收入)

数据分析和操作工具存在于数据科学生态系统中,以提供高效的操作方式,从而在生态系统中保持其存在。

在这篇文章中,我们将学习如何使用数据科学中主要工具之一:dplyr 来处理这些任务。

这是一个针对 R 编程语言的包,被描述为“数据操作的语法”。

我们可以单独安装 dplyr,也可以使用 tidyverse,这是一个用于数据科学的 R 包集合。我更喜欢后者,因为它允许我使用 tidyverse 中其他包的一些函数(例如 read_csv 来自 readr)。

让我们从导入库和读取数据集开始。我们将使用我准备的模拟数据的样本数据集。你可以从我的 datasets 仓库下载它。

library(tidyverse)

sales <- read_csv("sales_data_with_stores.csv")

# display the first 6 rows
head(sales)

# A tibble: 6 x 8
  store  product_group product_code stock_qty   cost   price last_week_sales last_month_sales
  <chr>  <chr>                <dbl>     <dbl>  <dbl>   <dbl>           <dbl>            <dbl>
1 Violet PG2                   4187       498 421\.    570\.                13               58
2 Rose   PG2                   4195       473 546\.    712\.                16               58
3 Violet PG2                   4204       968 640\.    855\.                22               88
4 Daisy  PG2                   4219       241 870\.   1035\.                14               45
5 Daisy  PG2                   4718      1401  12.5    26.6               50              285
6 Violet PG4                   5630       287   5.85    7.59              24              116

结果对象是一个 tibble,类似于 DataFrame 或表格。

所有数据处理挑战都可以归结为使用以下 5 个函数中的一个或多个:

  • select

  • mutate

  • filter

  • arrange

  • summarise

1. 选择

select 函数可用于选择列。它允许我们通过保留或删除列来进行选择,依据列的名称和类型。

让我们通过几个示例来了解 select 的工作方式。以下示例展示了如何选择特定的列。

# select product_code and price columns
select(sales, product_code, price)

# A tibble: 1,000 x 2
   product_code   price
          <dbl>   <dbl>
 1         4187  570\.  
 2         4195  712\.  
 3         4204  855\.  
 4         4219 1035\.  
 5         4718   26.6 

# the following does the same
select(sales, c(product_code, price))

我们也可以通过排除(删除)一个或多个列来进行选择,方法是使用“!”:

# select all but product_group column
select(sales, !product_group)

# A tibble: 1,000 x 7
   store  product_code stock_qty   cost   price last_week_sales last_month_sales
   <chr>         <dbl>     <dbl>  <dbl>   <dbl>           <dbl>            <dbl>
 1 Violet         4187       498 421\.    570\.                13               58
 2 Rose           4195       473 546\.    712\.                16               58
 3 Violet         4204       968 640\.    855\.                22               88
 4 Daisy          4219       241 870\.   1035\.                14               45
 5 Daisy          4718      1401  12.5    26.6               50              285

如果我们选择多个连续的列,使用“:”定义范围会更方便。

# select all columns from store to cost
select(sales, store:cost)

# A tibble: 1,000 x 5
   store  product_group product_code stock_qty   cost
   <chr>  <chr>                <dbl>     <dbl>  <dbl>
 1 Violet PG2                   4187       498 421\.  
 2 Rose   PG2                   4195       473 546\.  
 3 Violet PG2                   4204       968 640\.  
 4 Daisy  PG2                   4219       241 870\.  
 5 Daisy  PG2                   4718      1401  12.5 

我们还可以使用列索引,因此以下操作与上述操作相同:

# select all columns from store to cost
select(sales, 1:5)

我们还可以根据列的数据类型选择列。例如,以下示例选择了数值列。

# select all numeric columns
select(sales, where(is.numeric))

# A tibble: 1,000 x 6
   product_code stock_qty   cost   price last_week_sales last_month_sales
          <dbl>     <dbl>  <dbl>   <dbl>           <dbl>            <dbl>
 1         4187       498 421\.    570\.                13               58
 2         4195       473 546\.    712\.                16               58
 3         4204       968 640\.    855\.                22               88
 4         4219       241 870\.   1035\.                14               45
 5         4718      1401  12.5    26.6               50              285

通过使用相同的谓词函数(即 where),我们可以选择非数字列,如下所示:

# select all non-numeric columns
select(sales, !where(is.numeric))

# A tibble: 1,000 x 2
   store  product_group
   <chr>  <chr>        
 1 Violet PG2          
 2 Rose   PG2          
 3 Violet PG2          
 4 Daisy  PG2          
 5 Daisy  PG2 

2. mutate

mutate 函数顾名思义,通过更新现有列或创建新列来修改 tibble。

例如,我们可以将价格值增加 10%:

# increase price by 10 percent
mutate(sales, price = price * 1.1)

# A tibble: 1,000 x 8
   store  product_group product_code stock_qty   cost   price last_week_sales last_month_sales
   <chr>  <chr>                <dbl>     <dbl>  <dbl>   <dbl>           <dbl>            <dbl>
 1 Violet PG2                   4187       498 421\.    627\.                13               58
 2 Rose   PG2                   4195       473 546\.    784\.                16               58
 3 Violet PG2                   4204       968 640\.    940\.                22               88
 4 Daisy  PG2                   4219       241 870\.   1138\.                14               45
 5 Daisy  PG2                   4718      1401  12.5    29.2               50              285

我们还可以创建新列:

# create price_updated column by increasing price by 10 percent
mutate(sales, price_updated = price * 1.1)

# A tibble: 1,000 x 9
   store  product_group product_code stock_qty   cost   price last_week_sales last_month_sales price_updated
   <chr>  <chr>                <dbl>     <dbl>  <dbl>   <dbl>           <dbl>            <dbl>         <dbl>
 1 Violet PG2                   4187       498 421\.    570\.                13               58        627\.  
 2 Rose   PG2                   4195       473 546\.    712\.                16               58        784\.  
 3 Violet PG2                   4204       968 640\.    855\.                22               88        940\.  
 4 Daisy  PG2                   4219       241 870\.   1035\.                14               45       1138\.  
 5 Daisy  PG2                   4718      1401  12.5    26.6               50              285         29.2

多次更新可以在一次操作中完成。在做一个示例演示这种情况之前,我们先提到两个非常有用的功能:

  • 我们可以使用“%>%”运算符将不同类型的操作组合在一起,以创建管道并解决涉及多个步骤的复杂任务。

  • 新列会立即可用,因此我们可以在同一个 mutate 函数中使用它们。

这里是一个示例,展示了上述两个功能:

> sales %>%
+     select(cost, price) %>%
+     mutate(
+         price_updated = price * 1.1,
+         profit_updated = price_updated - cost)

# A tibble: 1,000 x 4
     cost   price price_updated profit_updated
    <dbl>   <dbl>         <dbl>          <dbl>
 1 421\.    570\.          627\.           206\.  
 2 546\.    712\.          784\.           238\.  
 3 640\.    855\.          940\.           300\.  
 4 870\.   1035\.         1138\.           268\.  
 5  12.5    26.6          29.2           16.7 

在上述示例中,我们首先从 sales 中选择价格和成本列,并从价格列创建 price_updated 列,然后使用这个新列创建 profit_updated 列。

3. 筛选

filter 函数允许我们根据条件或条件集筛选观察值(即行)。

以下示例筛选出价格超过 1000 的行。

filter(sales, price > 1000)

# A tibble: 5 x 8
  store  product_group product_code stock_qty  cost price last_week_sales last_month_sales
  <chr>  <chr>                <dbl>     <dbl> <dbl> <dbl>           <dbl>            <dbl>
1 Daisy  PG2                   4219       241  870\. 1035\.              14               45
2 Violet PG1                   9692        68 1243  1500\.              26               94
3 Violet PG1                   7773       602  976\. 1325\.              19               60
4 Daisy  PG1                   1941       213  847  1177\.              18               72
5 Daisy  PG1                   4140        92  803  1202\.              12               24

让我们使用一个更复杂的筛选条件:

# rows with a price of more than 1000 and store is Daisy
filter(sales, price > 1000 & store == "Daisy")

# A tibble: 3 x 8
  store product_group product_code stock_qty  cost price last_week_sales last_month_sales
  <chr> <chr>                <dbl>     <dbl> <dbl> <dbl>           <dbl>            <dbl>
1 Daisy PG2                   4219       241  870\. 1035\.              14               45
2 Daisy PG1                   1941       213  847  1177\.              18               72
3 Daisy PG1                   4140        92  803  1202\.              12               24

如果我们有一个包含多个值的条件,可以使用“%in%”运算符。

# rows with product group of PG3, PG4, or PG5 and store is Daisy
filter(sales, product_group %in% c("PG3", "PG4", "PG5") & store == "Daisy")

# A tibble: 302 x 8
   store product_group product_code stock_qty  cost price last_week_sales last_month_sales
   <chr> <chr>                <dbl>     <dbl> <dbl> <dbl>           <dbl>            <dbl>
 1 Daisy PG4                   5634       205 14.2   18.0              14               53
 2 Daisy PG4                   2650       239 59.4  111\.               15               38
 3 Daisy PG4                   5647       352  5.85  13.3              37              108
 4 Daisy PG4                   5693       260  7.62  13.3              19               74
 5 Daisy PG4                   5696       260  7.62  13.3              29               98

4. 排序

arrange 函数根据列中的值对行进行排序。它类似于 SQL 的 order by 和 Pandas 的 sort_values 函数。

# order rows by price
arrange(sales, price)

# A tibble: 1,000 x 8
   store  product_group product_code stock_qty  cost price last_week_sales last_month_sales
   <chr>  <chr>                <dbl>     <dbl> <dbl> <dbl>           <dbl>            <dbl>
 1 Violet PG4                   5454       -22 0.570  0.66              31               28
 2 Violet PG5                   5621      -123 1.32   0.76              49              100
 3 Daisy  PG4                   2279       525 1.34   1.23              14               18
 4 Rose   PG2                   9372       350 1.41   1.42              16               40
 5 Rose   PG4                   2138       482 1.63   1.61              13               23

默认情况下,排序是按升序进行的。我们可以通过在列名之前添加“ -”来将其更改为降序。

# order rows by price descending
arrange(sales, -price)

# A tibble: 1,000 x 8
   store  product_group product_code stock_qty  cost price last_week_sales last_month_sales
   <chr>  <chr>                <dbl>     <dbl> <dbl> <dbl>           <dbl>            <dbl>
 1 Violet PG1                   9692        68 1243  1500\.              26               94
 2 Violet PG1                   7773       602  976\. 1325\.              19               60
 3 Daisy  PG1                   4140        92  803  1202\.              12               24
 4 Daisy  PG1                   1941       213  847  1177\.              18               72
 5 Daisy  PG2                   4219       241  870\. 1035\.              14               45

要按多个列排序,我们可以用逗号分隔列名。以下示例按商店名称排序,然后按 last_week_sales(降序)排序。

arrange(sales, store, -last_week_sales)

# A tibble: 1,000 x 8
   store product_group product_code stock_qty  cost price last_week_sales last_month_sales
   <chr> <chr>                <dbl>     <dbl> <dbl> <dbl>           <dbl>            <dbl>
 1 Daisy PG6                    856     52748 31.4  38.0             1883             6880
 2 Daisy PG3                   2481     10543  8.25 14.2              947             4100
 3 Daisy PG6                   3957     10090 26.9  31.3              867             2355
 4 Daisy PG6                    889     21569 13.0  16.1              808             2990
 5 Daisy PG6                   9635     26576  8.53 11.4              673             2484

5. 总结

它将组汇总为一行。结合 group_by 函数,我们可以使用它来计算组的聚合值。这一操作类似于 SQL 的 group by 和 Pandas 的 groupby 函数。

# group by rows in sales by store
by_store <- group_by(sales, store)

# calculate the avg price for each store
summarise(by_store, avg_price = mean(price))

# A tibble: 3 x 2
  store  avg_price
* <chr>      <dbl>
1 Daisy       69.3
2 Rose        60.5
3 Violet      67.8

有几个聚合函数可以从数据中提取见解并进行深入分析。

这是来自官方文档的列表:

  • 中心: [mean()](https://rdrr.io/r/base/mean.html)[median()](https://rdrr.io/r/stats/median.html)

  • 分布: [sd()](https://rdrr.io/r/stats/sd.html)[IQR()](https://rdrr.io/r/stats/IQR.html)[mad()](https://rdrr.io/r/stats/mad.html)

  • 范围: [min()](https://rdrr.io/r/base/Extremes.html)[max()](https://rdrr.io/r/base/Extremes.html)

  • 位置: [first()](https://dplyr.tidyverse.org/reference/nth.html)[last()](https://dplyr.tidyverse.org/reference/nth.html)[nth()](https://dplyr.tidyverse.org/reference/nth.html)

  • 计数: [n()](https://dplyr.tidyverse.org/reference/context.html)[n_distinct()](https://dplyr.tidyverse.org/reference/n_distinct.html)

  • 逻辑: [any()](https://rdrr.io/r/base/any.html)[all()](https://rdrr.io/r/base/all.html)

本文中涵盖的函数可以帮助你完成几乎所有的数据处理和分析工作。将它们一起使用在管道中提供了更大的灵活性。

你可以成为 Medium 会员 来解锁我写作的全部内容,以及 Medium 的其他内容。如果你已经是会员,请不要忘记 订阅 ,这样你会在我发布新文章时收到电子邮件。

感谢你的阅读。请告诉我你的反馈意见。

5 种公司可以立即实施的生成 AI 应用场景

原文:towardsdatascience.com/5-generative-ai-use-cases-companies-can-implement-today-f458707bfbbe?source=collection_archive---------0-----------------------#2023-10-07

刚开始接触大语言模型(LLM)?以下是 OpenAI、Vimeo 和其他公司数据团队目前正在实践的 5 种热门应用。

Barr MosesTowards Data Science Barr Moses

·

关注 发表在 Towards Data Science ·10 分钟阅读·2023 年 10 月 7 日

--

图片由作者提供。

生成 AI 的炒作是真实的,数据和机器学习团队正在感受到压力。

各行各业的高管们正推动他们的数据负责人 开发能够节省时间、推动收入或带来竞争优势的 AI 驱动产品。

科技巨头如 OpenAI、Google、Amazon 和 Microsoft 一直在充斥市场 推出由大型语言模型(LLM)和图像生成扩散模型驱动的功能。他们承诺帮助公司大规模分析数据,总结和综合信息,生成内容,并以其他方式转变业务。

但大多数公司在开始融入生成性 AI 时实际上从哪里开始?哪些生成性 AI 应用案例是现实的、可实现的,且真正值得投资回报的?

我们深入探讨了早期采用者的策略,以了解公司如何今天将这项技术付诸实践——以及数据团队需要什么才能大规模实施生成性 AI。

为知识工作者构建更高效的工作流程

各行业的公司正在通过自动化和简化知识工作者的耗时过程来推动早期的生成性 AI 应用案例。

鉴于 LLM 理解和提取非结构化数据洞察的能力,企业发现总结、分析、搜索和挖掘大量内部信息的价值。让我们探索一下几个关键行业如何利用生成 AI。

法律事务所

在法律行业中,AI 驱动的系统通过以下方式帮助公司:

  • 自动化监管监测以确保客户遵守合规要求

  • 起草和审查标准文档,如遗嘱和合同

  • 通过审查大量文档以识别潜在风险和问题来协助尽职调查

  • 分析合同以标记可能的问题或建议修订

  • 通过识别、分析和总结案例法、法规、期刊、规定和其他相关出版物中的相关信息来协助法律研究

技术解决方案: 法律团队正在采用具有定制模型或针对法律系统进行微调的 LLM 的专门解决方案,包括CoCounsel(由 GPT-4 驱动)、HarveyThomson Reuters的全套软件。

实际案例: 伦敦律师事务所Macfarlanes 使用 Harvey 来支持研究、分析和总结文件,并创建电子邮件和备忘录的初稿,包括客户工作——由人工律师审查其工作成果。

金融服务

早在 2023 年初,像高盛和花旗这样的华尔街机构由于数据隐私问题,著名地禁止使用 ChatGPT。尽管有这些“反 AI”头条新闻,金融行业已经使用机器学习算法多年——用于驱动欺诈检测算法和即时信用决策。而且金融产品和公司充满了生成式 AI 的潜在应用案例。

目前,Databricks 估计在金融服务中,80% 的生成式 AI 应用案例专注于简化流程以节省时间和资源。这包括:

  • 使用内部文档作为知识库的对话金融聊天机器人

  • 自动化基本会计功能,如发票捕获和处理

  • 分析、总结和提取年度报告、保险合同和财报电话记录等文档的洞见

此外,业内领导者认为,AI 检测和阻止金融犯罪和欺诈的能力 是一个极具吸引力的应用。

技术解决方案: 定制解决方案开始出现,包括 BloombergGPT,这是一个专门为金融服务开发的 50 亿参数的 LLM。

现实生活中的案例: 在 2023 年 9 月,摩根士丹利推出了一个 AI 驱动的助手,通过提供对其内部研究报告和文档数据库的便捷访问来支持金融顾问。员工可以使用该工具询问市场、内部流程和建议。

销售团队

销售和营销团队正在大量采用生成式 AI,应用案例包括:

  • 编写电子邮件、着陆页、博客文章和其他内容的初稿

  • 根据 CRM 数据为个人推广内容进行个性化

  • 分析销售互动以指导代表

  • 基于人口统计、公司数据和数字行为自动化潜在客户评分

  • 总结通话和视频会议的互动

技术解决方案: 销售平台如 Gong 使用专有模型 来生成通话摘要并推荐下一步,以帮助推动潜在客户的购买旅程,而 Salesforce 的 Einstein Copilot 基于客户的具体背景自动生成电子邮件回复和账户更新。

现实生活中的应用案例: 帐户互动平台 6sense 使用支持 AI 的对话式电子邮件解决方案 来进行潜在客户沟通——这为营销互动账户带来了 10% 的新业务管道生成。

自动化工程和数据流程

通过自动化编码和数据工程中的重复或单调工作,生成 AI 正在简化工作流程并提高软件和数据工程师的生产力。

例如,团队可以使用生成 AI 来:

  • 自动生成代码块并检查代码中的错误

  • 自动调试和修正小错误,或预测可能出现的错误

  • 生成大量与现实世界信息相符的合成数据,以便工程师在不担心隐私问题的情况下测试模型

  • 自动生成关于代码和项目的详细文档

  • 更方便地将遗留软件从如 COBOL 这样的语言(在金融领域中常见,并且 成本显著)更新为现代语言

LLMs 也直接集成到开发者解决方案中。例如,在 Monte Carlo 平台内,我们利用 OpenAI API 支持两个功能——Fix with AI 和 Generate with AI——这些功能帮助团队更好地操作数据可观察性。Fix with AI 使用 LLMs 来识别数据质量检查中的错误,而 Generate with AI 使用 LLMs 来生成新的数据质量检查建议。

即使在 OpenAI 自身,LLM 也被用于支持 DevOps 和内部功能。正如Yaniv Markovsi,AI 专家负责人,所说,他们的团队使用 GPT 模型来聚合和翻译操作信号,如服务器日志或社交媒体事件,以了解客户在使用他们的产品时的体验。这比传统的 Site Reliability Engineering 团队手动调查和分类事件的方法更为简化。

技术解决方案: 工程团队正在采用像GitHub CopilotAmazon 的 CodeWhisperer这样的工具来支持他们的日常工作流。开发人员可以提供自然语言提示,并获得像 Python、JavaScript、Ruby 等语言的代码片段和建议。

现实生活中的使用案例: 一家全球媒体公司的数据工程团队正在使用 LLMs 将拉取请求分类为其 dbt 工作流中不同级别的需要处理的请求。根据更改的分类,模型触发不同的构建命令。这有助于显著简化开发工作流——因为团队的另一种选择是硬编码一些复杂的解析来确定哪个命令适合测试更改。

与公司其他部门一起民主化数据

在数据领域,公司利用生成 AI 的最佳机会可能是增加非技术用户对数据的访问。LLMs 提供了一种途径,使组织内的团队成员可以输入自然语言提示,从而生成 SQL 查询以检索特定数据点或回答复杂问题。

这正是 Adam Conway,Databricks 产品高级副总裁,最近强调的公司最明确的第一步。

“我见过一些行业拥有大量文档,他们希望使内部团队能够从成千上万页记录中检索答案,”Adam 说道。“这才是正确的方法,因为风险很低——它让你亲自参与,提供了很多价值,并且没有太多风险。在 Databricks,我们有一个内部聊天机器人,帮助员工解决问题并查看数据。我们在这里看到了很多价值。”

技术解决方案: 像 Databricks 这样的平台正在研发嵌入式功能——他们最近宣布了 LakehouseIQ,该功能承诺使团队能够用普通语言查询数据。

尽管这些技术仍在发展中,数据团队可以根据内部文档或知识库微调模型,以为其组织构建定制能力——或者利用生成式 AI 帮助员工更快捷地进行自助查询,正如我们的实际例子所描述的那样。

实际案例: 直播购物平台Whatnot 强烈鼓励每位员工掌握 SQL,以便他们能够查询自己的数据、创建自己的仪表盘以及编写自己的 dbt 模型——即使在市场营销、财务和运营等非技术部门也是如此。生成式 AI 在员工培训中发挥了作用。

正如工程总监 Emmanuel Fuentes 最近告诉我们的,“这正在帮助人们启动。如果他们没有 SQL 背景,它能帮助他们迅速上手,这真的很棒。如果有人不知道怎么做窗口函数,比如,他们可以描述自己想做什么,得到一段 SQL 代码,然后将我们的数据表替换进去。这就像是给那些完全不了解高级分析的人一个导师。”

扩展客户支持

客户支持团队应当获得特别的赞誉,因为他们是 LLM 赋能工作流的特别理想受众。通过将语义搜索融入基本的聊天机器人和工作流中,数据团队可以使客户服务团队更快地访问信息、创建响应并解决请求。

技术解决方案: 一些 CX 解决方案已经在其平台中包含了生成式 AI 功能。例如,Oracle 的 Fusion Cloud CX 使用一个参考内部数据的 LLM,帮助代理根据客户的互动历史生成即时响应,并建议新的知识库内容以应对新出现的服务问题。

实际案例: Vimeo 工程师 使用生成式 AI 构建了一个帮助台聊天原型。该工具在一个向量存储中索引公司的 Zendesk 托管帮助文章(关于向量数据库的更多内容见下文),并将该存储与 LLM 提供商连接。当客户与现有前端聊天机器人进行的对话不成功时,记录会发送到 LLM 寻求进一步帮助。LLM 会将问题重新表述成一个单一的问题,查询向量存储中相关内容的文章,并接收相关文档。然后,LLM 会为客户生成一个最终的总结回答。

支持翻译和语言服务

最后,生成性人工智能使得在组织内部实现近乎即时的翻译和语言支持成为可能,这些组织每年在语言服务上的支出接近$60 billion——但仅翻译了它们生产内容的一小部分。像 GPT-4 这样的 LLM 有可能帮助团队提供多语言客户服务互动、进行全球情感分析,并大规模本地化内容。

技术解决方案: 目前,大多数模型可能缺乏足够的训练数据来熟练掌握不常用的语言——或理解俚语或行业特定术语——因此团队可能需要微调模型以获得良好结果。尽管如此,Google正在开发一种训练了 400 多种语言的通用语音模型,目标是构建一个通用翻译器。

真实使用案例: 在对传统翻译模型进行独特的改进时,健康科技公司Vital推出了一款由人工智能驱动的医生到患者翻译器,能够即时将高度专业的医学术语转换为通俗语言。

开始使用生成性人工智能时的三个关键考虑因素

当你的团队进入不断变化的生成性人工智能领域时,有几个关键因素需要记住。

补充你的技术栈

拥有适当的技术栈来支持生成性人工智能将帮助你的团队更快地扩展和创造价值。除了现代数据栈的常见组件外,你还需要考虑添加:

向量数据库

向量数据库目前是团队利用 OpenAI 的 LLM 构建可扩展应用程序的最有效方式之一。这些数据库支持向量嵌入,携带语义信息,有助于 AI 理解数据中的关系和模式。

团队可以使用像PineconeZilliz这样的独立向量数据库,或在现有的数据存储解决方案中使用向量嵌入功能,例如DatabricksSnowflake

微调模型

对于有更多定制需求的团队,微调模型——在针对你需求的数据集上训练预训练模型——将可能是超越向量嵌入的下一步。像TensorflowHuggingFace这样的工具是微调模型的好选择。

非结构化或流数据处理

生成式 AI 倾向于通过从大量非结构化数据中提取洞见来提供最大价值。如果你尚未将非结构化数据处理纳入你的技术栈,你可能需要实现一个类似于Spark的工具——或者Kafka,如果你正在涉足流数据的话。

确保拥有合适的团队和资源

创建 AI 试点项目需要时间和资源。虽然你可能有一个不惜成本将生成 AI 引入你的产品或业务的热情 CEO,但仍然需要对所需时间和成本有一个现实的了解。

组建你的团队

你很可能会将现有员工重新分配到原型开发或概念验证上,而不是一开始就聘请经验丰富的生成 AI 开发人员(部分原因是这是一个全新的领域,经验丰富的生成 AI 开发人员还不太存在)。这些先锋团队通常由具有一定 ML 背景的数据工程师组成。

换句话说,你的一些宝贵成员将需要从即时的创收工作中抽离出来,参与你的 AI 试点项目。考虑固有的机会成本,并将其纳入你的整体规划——同时为你的团队配备一位业务赞助人,能够为这种资源转变提供支持,同时保持团队与业务价值的紧密联系。

考虑你的硬件成本

如果你计划对你的模型进行微调,并且你对 ML Ops 不熟悉,预测并关注你将会产生的计算成本。那些 GPU 时数可能会累积起来。

优先考虑数据质量

无论你的技术栈、选择的模型或用例是什么,有一点真理始终不变:你需要确保数据输入和数据输出的质量。否则,你将冒着将糟糕数据暴露给更多内部团队的风险,无论是通过自然语言提示直接暴露,还是通过生成 AI 驱动的产品间接暴露。

生成性人工智能有可能彻底改变每个企业,但它也并非没有风险和潜在陷阱。数据测试、数据监控、AI 治理数据可观测性 有助于确保生成性人工智能为您的组织创造巨大的价值——而不是尴尬的数据灾难。

特别感谢 Naren Venkatraman、Yaniv Markovski 和 Emmanuel Fuentes 花时间与我们聊这篇文章。

5 个有用的提取与加载实践,帮助获得高质量原始数据

原文:towardsdatascience.com/5-helpful-extract-load-practices-for-high-quality-raw-data-65b9a59a8721?source=collection_archive---------8-----------------------#2023-04-04

不可变的原始区域,未经过变换、平展或去重,直到完成你的挖掘工作

Sven BalnojanTowards Data Science Sven Balnojan

·

关注 发表在 Towards Data Science ·8 分钟阅读·2023 年 4 月 4 日

--

挖掘机 - 图片由 Dmitriy Zub 提供,来源于 Unsplash

这篇文章是对 Meltano 博客的更新版。

ELT 正成为数据架构的默认选择,但许多最佳实践主要关注“变换(T)”。

但数据质量在提取和加载阶段之后的转换中决定。正如谚语所说,“垃圾进,垃圾出。”

强大的 EL 管道为提供准确、及时和无误的数据奠定了基础。

幸运的是,我们有一个充满数据专家的社区,他们使用过 Meltano、Stitch、Airbyte、Fivetran 和市场上所有的大型提取和加载工具。因此,我们请他们提供他们最关键的提取和加载实践!

我们提炼了 5 个数据实践 被社区使用和喜爱,这些实践将提升所有数据集的质量,无论你使用什么工具。

但等等,这些不是“最佳实践”吗?因为我们认为它们是可以选择的。如果你正在处理一个新项目或在提取和加载过程中还没有很多实践,你可以全部实施。如果你已经有了一些,补充那些有意义的实践。

设置舞台:我们需要提取和加载实践,因为“复制原始数据”比听起来复杂得多。

ELT 作为一种模式的概念听起来很简单,“只需首先复制源数据,然后在自己的空间中对原始数据进行转换。”然而,“复制”和“原始数据”这两个词有隐藏的障碍。

“复制”听起来很简单。但源数据会变化,除非你知道发生了什么变化,否则“复制”比你想象的要复杂。想象一下一个包含 1.5 亿个“订单”的生产表,它有一个“时间戳”,但没有“修改”数据。是的,这样的表随处可见。那么,你怎么知道订单是否被修改了?如果是,哪些订单被修改了?例如,你怎么知道哪些订单被“取消”,这种操作通常发生在同一数据记录中并在原地“修改”它?

“原始数据”听起来很清晰。然而,提取和加载的概念隐含着通常你在两个不同的技术系统 A 和 B 之间复制数据,其中你需要调整数据以匹配系统 B。你从 REST API 中获取数据并将其放入 Snowflake,或从 Oracle 数据库中提取数据并放入 Redshift。每次更换系统时,你都需要修改“原始数据”以遵循新系统的规则。你需要进行类型转换;你需要考虑是否要“展平 JSON”或是否要向数据中添加额外的元数据。

单纯“复制原始数据”每次添加新数据源或目标时都会提出新问题。即使只是来自同一生产数据库的新表,你一直在复制的数据。

这些实践将在你使用新数据源将数据导入数据系统时为你提供指导。

1. 使每次 EL 运行具有唯一标识——时间戳所有内容

我们从可能最重要的最佳实践开始:使你加载到数据系统中的每一位数据都能被识别并追溯到获取它的过程。

典型的方法是包含捕获的元数据值:

  • 摄取时间:表示加载过程开始的时间戳。

  • 摄取过程:表示加载过程及其实例的唯一标识符。

  • 源系统:关于数据来源的元数据。

图片由作者提供。

将这些元数据中的任何一个或全部添加到你摄取的数据的每一行/条目中。我们建议使用摄取器的开始时间作为“摄取时间”,因为这简化了过程。“摄取实例的标识符”应该明确。不要仅仅提供“Airflow-OracleDB-Ingester”作为过程,而是提供“Airflow-OracleDB-Ingester-ID1244”,其中 ID1244 标识特定的摄取运行。

将源系统作为元数据的一个好处是,你可以快速调试下游仪表板中的问题并识别其来源。这也是其他用例中有用的元数据。

假设你有一个遗留系统和一个新的客户注册组件。在这种情况下,你可以在仪表板中将源系统作为过滤选项,让用户仅过滤来自一个系统的客户。

图片由作者提供。

2. 在原始层级之外的层级去重数据

通常,你会遇到三种数据重复情况需要“去重”。但无论是哪种情况,都不要在原始/落地层进行去重!

第一种情况是有意的重复数据,其中源系统包含你和终端用户认为重复的内容。例如,你的 CRM 系统可能有两个相同客户的条目,该客户取消后重新注册。如果你在原始层级去重,这意味着要么合并两个条目,要么删除一个。这两者都会删除源系统中存在的数据。

第二种情况是无意间产生的重复数据,其中源系统可能会删除你在数据仓库中仍然存在的记录,或者源系统无意中生成了未来可能会删除的重复数据。尽管这是一个“错误”,我不建议在原始摄取区域删除这些数据,而是应该在后续阶段进一步过滤,例如在建模的下一阶段。否则,你会在摄取中添加难以跟踪的逻辑。

第三种情况是由于技术限制导致的重复。可能是你的摄取工具倾向于“至少一次交付”策略,或者可能是摄取过程中的一个 bug。使用“至少一次交付”增量加载策略,你确保获取所有数据行,但可能会产生一些重复数据。再次建议在原始层级保留重复数据,并在后续阶段进行过滤。

图片由作者提供。

无论情况如何,都不要在加载时去重。加载所有数据,并保持原样。稍后在下一阶段进行去重。

3. 在 EL 期间不要展平,推迟到一个阶段后进行

许多源系统在摄取时会返回数组、JSON 或其他具有某些层级的嵌套对象,你希望将其拆解以进行进一步处理。但不是在摄取层面。将原始数据按原样导入你的数据系统,然后让一个过程进行“扁平化”。

一个很典型的例子是 JSON 对象。你的源数据可能包含一个大的 JSON 对象,而你希望将其处理成 Snowflake 数据库中的单独列。这种做法建议首先创建一个仅包含元数据列和一个“JSON_blob”列的原始表,其中包含 JSON 对象。在第二步,你可以将这些数据处理成列。

图片由作者提供。

这样做的原因是,扁平化涉及业务逻辑。它涉及到你知道哪些属性是“始终存在的”。如果你在摄取时进行扁平化,你的摄取过程可能会因为一个 JSON 对象为空,或者一个 JSON 对象没有一个预期的值而中断。处理已经摄取的数据并重新运行扁平化工具总是比运行摄取和扁平化过程更容易。

附加提示:相同的实践可以避免在摄取时进行类型转换(如果可能的话)。我们建议在摄取之后再进行类型转换。

4. 拥有一个不可变的原始层

在某个时候,你将切换到数据的增量更新。此时要记住创建一个“不可变原始层”,这是一个你绝对不要修改或删除数据的区域。

“不可变原始层”:一个你绝对不要修改或删除数据的区域。

不去重复数据是其中的一部分(见规则 2),但还有更多:在你的不可变原始层中,你不会删除上游移除的记录或修改上游修改的数据。你只是加载新数据,仅此而已。

一个我仍然痛苦记忆的例子是北极星指标仪表板。它展示了我基于过去几个月客户行为所工作的北极星指标的当前发展情况。仪表板和数字看起来都很好,呈上升趋势。产品和管理决策基于此做出。北极星指标的新记录每周广播一次。

然后突然有一天,仪表板看起来不同了。我们的北极星指标减少了 10%的值,并在某个特定细分市场中减少了 30%。

一位大客户离开了,记录被完全删除。

由于我们在修改原始数据,我们彻底搞砸了北极星指标,无法恢复。

从那天起,我们使用了可能会变动的所有原始数据的快照

图片由作者提供。

为自己做个好事,让你的原始层不可变。

注意:不可变的暂存区域实际上就是你在进行全表同步时创建的,如果你不删除数据的话。此外,出于 GDPR 和隐私问题,你应该在这里做一个例外。

5. 在数据摄取时不要进行任何形式的转换,即使是轻微的,除非确实必要

在摄取时即时转换数据有充分的理由,但几乎所有你能想到的案例也可以在不转换的情况下运作。法律和安全是即时转换数据的两个好理由。对于其他所有理由,你应该首先摄取数据,然后对摄取的数据进行小规模的转换。

如果你选择在摄取数据时进行“即时”转换,确保你使其防故障。尽量只添加数据或删除数据,而不是修改数据。

如果你默认要进行转换,最好先进行数据摄取。然后,你可以,例如,创建一个映射表并在那里进行连接。

图片由作者提供。

你可以通过使用像“dbt seeds”这样的机制或摄取由外部贡献者维护的 Google Sheets 来实现。

总结

我们都知道“垃圾进,垃圾出”,但我们往往未能在提取和加载的世界中认识到这一点。

这些做法侧重于在我们流程的最开始阶段减少垃圾数据。它们将帮助你更快地解决问题,并从长远来看提高数据质量。

如果你想了解所有实践的简短版本,请查看下面的图示。

5 个有用的提取和加载实践,图片由作者提供。

5 个促进数据科学家/分析师参与的想法,而不至于在会议中窒息

原文:towardsdatascience.com/5-ideas-to-foster-data-scientists-analysts-engagement-without-suffocating-in-meetings-a57db1e2aa34

作者分享了他们成功实现这一平衡的策略

Guillaume ColleyTowards Data Science Guillaume Colley

·发表于 Towards Data Science ·阅读时间 8 分钟·2023 年 10 月 21 日

--

图片由 Aziz Acharki 拍摄,来源于 Unsplash

在管理数据科学或数据分析团队时,找到为团队成员提供不受干扰的专注时间与促进参与、合作和团队精神之间的良好平衡可能是一项挑战。在我的团队领导经历中,我尝试了许多迭代和方法来实现这一平衡。在这篇文章中,我将概述我当前拥有的五个有效的关键点。

无论你是寻求灵感的分析团队领导,还是渴望摆脱“过多会议”的数据科学家,我希望这些见解能够激励你自己的职业旅程。

1 — 早晨站会

  • 节奏:每日

  • 时长:15 分钟

早晨站会直接来源于 敏捷方法论,这是一个简短但至关重要的每日聚会,每天早晨的第一个环节(在固定时间)。尽管有些人可能会更早开始他们的一天,但站会作为整个团队的统一起点。在这个会议中,每个人,包括经理,轮流提供简洁的更新,内容包括:

  • 他们昨天完成的工作,

  • 他们今天计划达成的目标,

  • 需要与相关方进行线下讨论的任何障碍或问题

为什么要进行早晨站会?

→ 促进专注与承诺:它培养了对团队日常目标的目的感和承诺感。

→ 提升意识和协作:它让团队成员了解彼此的项目和进展,促进联系和合作。

→ 早期识别阻碍因素:在早期识别障碍可以防止浪费精力,确保顺利进展。

→ 让经理及时了解:会议使经理充分了解团队的进展,从而有效监督。

除非涉及整个团队,否则更深入的讨论应与相关方离线进行。

这个简短的会议也是经理进行小型公告或提醒以完成行政任务的机会。(有关早晨站会的更多细节 请点击这里)。

关于替代方法的反思:

在完全敏捷模式下,使用 冲刺 节奏:

这种方法已被 证明 对于专注于单一产品或项目的开发团队非常有效。然而,它在分析/数据科学实践中的应用却面临挑战。根本问题在于团队成员通常参与各种项目或分析,许多项目无法与标准的冲刺周期对接——数据科学项目往往超出典型的 2 到 3 周的冲刺周期。

项目时间表和范围的这种差异也意味着,只有一小部分敏捷仪式(规划和待办事项整理会议)与个别数据科学家的具体需求对接。显而易见,将各种复杂的数据科学任务强行融入传统敏捷冲刺的严格结构中存在显著低效。

Mimi Thian 的照片,来源于 Unsplash

2 — 周五“进行中的工作”展示

  • 节奏:每周

  • 持续时间:15 分钟+

我们最近实施了这种方法,以促进合作,而不需要在日历上预订额外的时间槽。这个举措取代了标准的每周五每日站会,具有迷你 braintrust 的本质。会议定为 15 分钟,但由于周五早晨通常较少会议,如果需要,讨论可以延长。

这是一个志愿者可以非正式地展示一个正在进行的工作,并寻求团队集体智慧的空间,例如:

  • “我正在做这个模型,想听听你的想法。”

  • “我在考虑两种解决方案,哪个选项最好?”

  • “这是我即将向利益相关者展示的内容,我如何才能使其更好?”

为什么要有“进行中的工作”展示?

→ 沟通技巧:这个环节提供了一个定期的平台来磨练总结和沟通技巧。

→ 更深入的洞察:它为团队提供了一个了解正在进行的项目和任务技术复杂性的窗口

→ 协作解决问题:它鼓励团队成员互相交流和碰撞想法

这种格式取得了良好的平衡,促进了积极参与和协作,同时为那些对主题有更深兴趣的人提供了进一步探索的灵活性。

替代方法的反思:

起初,我将这些智囊会议作为单独的 1 小时会议进行,这种格式本可以允许更深入的技术讨论。然而,事实证明,参与者往往选择跳过这些会议,以完成任务或项目,从而导致出席率不尽如人意。此外,1 小时的时间有时也成为志愿者主动分享他们正在进行的工作的障碍。

图片由Estée Janssens拍摄,来源于Unsplash

3 — 数据科学团队会议

  • 节奏:每月一次

  • 时间:1 小时

这种更正式的聚会每月举行一次(请参见下文关于节奏选择的说明),是从日常任务/项目中退后一步,反思团队的工作方式和未来机会的机会。我们还尝试邀请来自不同职能领域的人介绍他们的工作,并讨论潜在的协同效应。我们的会议结构遵循以下框架:

  • 5 分钟:快速更新行政事务和必要的提醒。

  • 25 分钟:A) 回顾,提供了反思我们过去经验的机会

    B) 战略/路线图更新和项目规划,使我们能够展望未来

  • 30 分钟:来自组织的嘉宾

为什么要召开数据科学团队会议?

→ 反思与规划:这次会议提供了一个机会,让我们退后一步,反思过去,展望未来

→ 工作方式的责任:它赋予我们的数据科学家掌控权,分析哪些做法有效,哪些无效,并致力于改进

→ 项目可见性:它使团队了解即将开展的项目,并有机会与那些符合他们兴趣和专长的项目对接。

→ 拓宽视野:它拓宽了我们团队对组织的理解,培养了与不同业务职能的联系和合作关系

替代方法的反思:

这些会议最初是每两个月一次,专注于回顾,因为我们通过其他接触点处理其他元素。然而,随着我们团队表达了对项目分配和战略的更多见解的需求,我们合并了一些其他接触点,从而允许独立的每月团队会议。

在没有早晨站会和不太定期的一对一会议的不同环境下,我发现每周的团队会议节奏是有效的。这种格式包括了行政更新、看板审查以及团队成员每周项目或技术见解的展示。

图片由 airfocus 提供,来源于 Unsplash

4 — 个人一对一

  • 节奏:每周

  • 时长:30 分钟

这是每周日程中专门为数据科学家或数据分析师留出的时间。这里 是一个关于如何设立和进行成功的一对一会议的极好资源。我已将其调整为以下结构:

  • JIRA/看板审查和更新(5 分钟或更少)

  • 讨论的重点不是谈论具体的项目或任务,而是提供机会让个人提出他们想要讨论的内容。

  • 反馈、辅导、职业发展

  • 季度目标检查和规划

  • 如果时间允许,可以进行项目或任务的讨论

为何进行个人一对一会议?

关系建设:它建立并培养了数据科学家/分析师与其经理之间的关系,建立了信任

→ 成长与发展:它通过个性化反馈和辅导促进数据科学家/分析师的成长与发展,同时通过互惠反馈来培养管理能力。有关不同级别的数据科学家/分析师期望的详细信息,请参见 这篇文章

每季度末,我们的关注点转向评估我们的年度目标,包括组织/项目目标和个人职业发展目标,并检查我们实现这些目标的进展。

替代方法的反思:

我们探讨了每两周一次 45 分钟的对话选项,但对于我们来说效果不佳。我们的对话通常在 30 分钟内结束,两周一次的间隔似乎过长,难以保持强烈的联系。

看板板块回顾:我们最初为看板回顾安排了单独的会议。后来我们将其并入了稍长的早间站立会议。由于数据科学家/分析师任务和项目的个体性质,两个选项都未能达到与完全敏捷运营相同的效果:会议的有限部分对每个人而言相关。因此,将这种个性化的接触点与单对单会议结合,作为进入核心讨论之前的快速管理事项,显得更为合理。

Chris Montgomery 的照片,来自 Unsplash

5 — 部门团队会议

  • 频率:每月

  • 持续时间:1 小时-1.5 小时

促进数据科学家/分析师参与和团队精神的最后一件事,我自己不做——就是更广泛的团队每月会议。它通常包括以下几个元素:

  • 介绍新团队成员

  • 认可成功的项目或团队成员

  • 来自部门主任或副总裁的更新

  • 商业更新

  • 趣味活动

  • 项目分享

  • 组织文化活动

为什么要进行部门团队会议?

它让数据科学家/分析师接触到更广泛的部门,并建立归属感

→ 它让个人理解他们的工作如何融入部门中

→ 它帮助识别部门中的专业知识和用于当前或未来障碍的资源

结论:

这就是我认为的必要接触点——它们不会过度增加工作负担,但能促进参与感和团队精神。当然,它仍然需要不断的完善和调整!

你认为这 5 项中哪些是多余或不必要的?你和你的团队有哪些不同的方法效果良好?请在下面的评论中告诉我!

参考文献

[1],[3]K Schwaber & J Sutherland,《Scrum 指南》(2020)。 链接

[2]M. Oster,《什么是每日站立会议以及如何有效地进行》(2023)。 链接

[4]M. Mah,《哥伦布发现敏捷》(2012)。 链接

[5]E. Catmull,《皮克斯智囊团内部揭秘》(2014)。 链接

[6] Scrum.org,《什么是冲刺回顾》。 链接

[7] S. Rogelberg,《充分利用你的单对单会议》(2022)。 链接

5 个激励人心的学习资源,帮助我保持在数据分析的前沿

原文:towardsdatascience.com/5-inspiring-learning-resources-that-help-me-stay-on-top-of-data-analytics-ec817f45462

5 个激励人心的学习资源,推动你的技能和专业知识

Hanzala QureshiTowards Data Science Hanzala Qureshi

·发表于 Towards Data Science ·阅读时间 5 分钟·2023 年 7 月 18 日

--

图片由 Cindy Liu 提供,来自 Unsplash

最近,我和一组同事举行了一次早餐会议。

我们详细讨论了职业历程、品牌价值、所犯的错误和所学到的经验。有人问我推荐哪些学习资源,以跟上行业变化,同时也提供一般的职业和管理建议。

这里是我经常参考的 5 个信息资源。

让我们开始吧!

关注这些人以获取数据管理、工程、分析以及对立观点的最新信息

1. Prukalpa

Prukalpa 是元数据方面的权威。

我通过她的一篇文章发现了 Prukalpa,数据治理品牌问题。当时我正处于最复杂的治理实施过程中,这篇文章的内容安抚了我,令我感到并不孤单。

由于 Prukalpa 是 Atlan 的联合创始人,她可以写关于她的公司的文章;然而,她选择将文章的重点放在数据管理行业上,这有助于提供更多的背景信息。她还有一个名为 Metadata Weekly 的 Substack 新闻通讯。

2. Barr Moses

Barr 是另一位行业思想领袖和联合创始人(Monte Carlo),我跟随了他一段时间。

在实施治理解决方案时,我总是很难让客户理解数据质量(DQ)及其高昂的成本。组织喜欢战术性地修复问题,积累技术债务,并在为时已晚时抱怨数据质量。

Barr 广泛撰写了数据质量、可靠性和停机问题,而这些都是数据团队每天面临的挑战。

我还推荐其他思想领袖,如 Chad Sanderson (数据合同)Ben Rogojan (咨询和工程)Teresa Tung (GenAI)

数据团队的关键收获与经验教训(第一部分)

越来越多的技术创始人曾经是数据团队的一部分,并继续撰写他们面临的挑战,这也与普通的数据工程师/分析师产生共鸣。这建立了个人和商业品牌的飞轮效应。

  1. 在 Medium/Twitter 上撰写你每日的技术挑战;这有助于与志同道合的人建立联系并澄清你的想法。

  2. 新趋势不是由 Gartner 创造的,而是由数据背后的人创造的,因此要在 Medium/Twitter/Reddit 上阅读和消费与你的领域相关的内容。

  3. 没有人能掌握所有答案,所以要阅读对立的观点和看法。Chad Sanderson写了关于数据合同的文章。Ben Rogojan则讨论了辞职 FAANG 后独立工作。这些概念可能不太常见,但它们存在,并有助于塑造你的观点。

  4. 个人品牌至关重要——爬升企业阶梯可能是无意的,但选择并遵循特定的路径更具回报。围绕你的领域建立个人品牌可以帮助你在同行中脱颖而出。

关于职业管理、激励、生产力与哲学的书籍

3. 经理的养成

我的第一年作为经理是我职业生涯中最令人不知所措的一年。

期望突然从个人贡献转变为激励团队共同贡献。一旦华丽的头衔消失,现实就会显现:这是一项艰巨的工作。我转向了 Julie Zhou 的这本书,它帮助我理解了我并不孤单。

伟大的经理是培养出来的,而非天生的!

我学会了如何举办高效的会议,提供反馈,委派任务,并成为一个领导者而不是老板。虽然仍有很多改进的地方,但这让我获得了优势!

4. 如此出色他们无法忽视你

跟随你的热情是个糟糕的建议!

我最初对此持怀疑态度;我们都学过“享受你做的工作,你的一生都不会工作一天。”然而,这本书改变了我的观点,我能够与之产生共鸣。它提倡,当你磨练你的技艺时,你将积累足够的职业资本,以便你能够掌控你的工作和生活。

我小时候并没有想过要成为数据与分析领域的领导者;事实上,我甚至不知道这样的职位存在。多年来在这个领域的不断进步,我获得了更多的自主权、能力和关联感,这三样东西是幸福的必要条件。

5. Naval Ravikant 年鉴

与长期合作的人一起玩长期游戏!

我不知道怎么评价 Naval,但他的哲学推文和这本书都是那些推文的总结,非常了不起。Naval 提倡“特定知识”,这是无法通过培训获得的。如果可以培训,那么其他人也可以。

在早餐会上,我向一些技术团队成员提到,你可以通过课程和视频学习许多技术技能,但软技能只有通过应用才能获得。如果你能将这两者结合起来,你将变得不可阻挡。

数据团队的关键要点和教训(第二部分)

  1. 当 Naval 在数据领域提到“特定知识”时,它意味着将软技能和硬技能结合起来。与不同的受众进行量身定制的沟通,无论是技术还是商业,都可以构成“特定知识”。你无法通过培训来获得这种知识;你必须应用、迭代并学习它。

  2. 管理就是理解每个人擅长某些事情,并利用他们来完成这些任务。例如,使用工程师来修复数据管道,而不是创建高层次的状态更新。

  3. 在你的专业领域建立职业资本;即花时间提高你的技能,建立你独有的特定知识,围绕这一特定知识建立你的品牌,然后请求更多的自主权。你所做的项目、你合作的团队、你服务的客户等方面的自由。

  4. 当 Naval 提到“长期”合作伙伴时,他指的是数据分析师的工程、业务、基础设施和治理团队。你今天合作的人将在 10 年内晋升职业阶梯。现在利用你的特定知识来为他们提供服务,将使你能与他们建立长期关系。

  5. 你的网络应该充满你曾工作过的所有公司和客户的人;无论项目好坏,每个人在过程中都教会了你一两课。

结论

所以——这篇文章比我预期的要长,但希望从这些资源中你能获得很多收获。如果你认为还有其他很棒的人我没有提到,请在下方评论中留下他们的名字。

如果你渴望提高你的数据技能,请查看这篇文章:

## 今年提升你数据技能的 4 种小而强大的方法

通过掌握这 4 项技能来提升你的数据游戏

towardsdatascience.com

如果你还没有订阅 Medium,可以考虑使用我的推荐链接订阅。比 Netflix 便宜,且客观上更值得你的时间。 如果你使用我的链接,我会赚取少量佣金,而你可以访问 Medium 上的无限故事。

所有观点均为我个人意见

你从未知道的 5 个 Jupyter 小技巧

原文:towardsdatascience.com/5-jupyter-hacks-that-you-never-knew-even-existed-9dc0a08fd90a

提供一个额外的小贴士

Avi ChawlaTowards Data Science Avi Chawla

·发表于 Towards Data Science ·阅读时长 6 分钟·2023 年 3 月 1 日

--

图片来源于 SigmundUnsplash

Jupyter Notebook 是几乎所有与 Python 相关的编程任务(如数据科学、机器学习、科学计算等)中最受欢迎的 IDE 之一。

它的交互式编码功能使其成为初学者和专家的首选工具。

尽管其广泛使用,但许多用户并没有充分利用它的全部潜力。

结果是,他们往往使用 Jupyter 的默认界面/功能,而在我看来,这些功能可以显著改进,以提供更丰富的体验。

因此,在本文中,我将介绍 5 个你可能从未知道的酷炫 Jupyter 小技巧。

这些将允许你在这个强大的工具上解锁新的生产力和创造力水平。

让我们开始吧 🚀!

#1 停止预览原始 DataFrame

当我们在 Jupyter 中加载 DataFrame 时,通常通过打印来预览。这如下所示:

然而,这几乎没有告诉我们数据内部的情况。

因此,你必须通过分析来深入挖掘,这涉及简单但重复的代码。

相反,使用 Jupyter-DataTables。你可以按如下方式安装:

要使用它,请在 Jupyter 中运行以下代码:

它通过许多有用的功能极大增强了 DataFrame 的默认预览。

结果是,每当你打印 DataFrame 时,它将显得更加优雅,如下所示。

这种更丰富的预览提供了排序、过滤、导出和分页操作,同时显示列分布和数据类型。

#2 一键标记你的数据

不是所有的数据都是预先标记的。

因此,通常对于未标记的数据,可能需要花费一些时间进行注释/标记。

不必在外部预览文件并标记它们或构建复杂的注释管道,你可以使用ipyannotate仅用几行代码进行注释。

它提供了一个专门用于数据注释的 Jupyter 小部件。

运行以下命令来安装它:

通过点击按钮,数据注释变得更容易。因此,ipyannotate 允许你将数据标签附加到按钮上。

假设我们有一些猫和狗的图片(未标记)。我们可以创建如下的注释管道:

如上所示,你可以通过简单地点击相应的按钮来注释数据。

更重要的是,你还可以检索标签并根据需要将它们用于你的数据管道。

#3 在 Jupyter 中查看文档

在 Jupyter 中工作时,忘记函数的参数并访问官方文档(或 StackOverflow)是很常见的。

然而,你可以在笔记本中查看文档。

按下Shift-Tab会打开文档面板。这非常有用,可以节省时间,因为你不必每次都打开官方文档。

下面展示了一个演示:

此功能也适用于你的自定义函数。

#4 Jupyter 单元格执行完成时接收通知

在 Jupyter 单元格中运行一些代码后,我们经常会离开去做其他工作。

在这里,你需要反复返回到 Jupyter 标签页来检查单元格是否已执行。

为了避免这种情况,你可以使用来自jupyternotify扩展的%%notify魔法命令。

正如名称所示,它会在 Jupyter 单元格完成(无论成功还是失败)时通过浏览器通知用户。

要安装它,运行以下命令:

接下来,加载扩展:

完成了!

现在,每当你想要接收通知时,在单元格顶部输入以下魔法命令:

每当单元格完成执行时,你将收到以下通知:

点击通知将带你回到 Jupyter 标签页。

#5 在 Jupyter Notebook 运行时清除单元格输出

在使用 Jupyter 时,我们通常会打印很多细节来跟踪代码的进展。

然而,当输出面板积累了一堆细节,而我们只对最新的输出感兴趣时,可能会感到沮丧。

此外,每次滚动到输出的底部也可能很烦人。

要清除单元格的输出,你可以使用来自IPython包的clear_output方法。

IPython 已预装在 Python 中,因此无需安装。

你可以按如下方式导入该方法:

当调用时,它会移除单元格当前的输出,然后你可以打印最新的细节。

下面展示了一个演示:

如上所示,我们只能看到单元格中的最新输出。之前的输出被删除了。

额外提示

尽管上述提示会显著丰富你的 Jupyter 体验,但我仍然面临许多在 Jupyter 上难以解决的问题。

比如说,Jupyter 在协作方面表现糟糕。由于它在本地运行,因此无法嵌入实时协作功能,让团队可以一起工作、添加评论、跟踪进展等。

更有甚者,分享同样令人痛苦。如果我需要与他人分享我的 notebook,唯一的方式就是通过电子邮件发送或在 GitHub 等网站上托管并分享链接。

最后,许多数据科学任务不仅仅局限于 Python。它们同样涉及 SQL,SQL 在与组织数据库交互时被广泛使用。

然而,在 Jupyter 中集成 SQL 是可行的,但过程繁琐。

解决方案

对这些限制感到沮丧,我开始寻找替代方案,并且很高兴我发现了 Deepnote。

在无需学习任何新知识的情况下,它迅速解决了 Jupyter 的所有限制,并始终为我提供了丰富的类似 Jupyter 的体验。

分享、协作、使用 SQL、无需代码创建图表、连接数据库等,所有这些都在 Deepnote 中无缝集成。

虽然我理解 Jupyter 旨在为所有 Python 用户提供一个通用的体验,但它在解决数据科学家的所有痛点方面做得非常糟糕,尤其是对团队工作的那些用户。

在我看来,Deepnote 是 Jupyter 的增强版,适用于所有数据驱动的项目,你一定要去试试。

结论

这篇博客到此结束。

恭喜你学会了一些 Jupyter notebook 的绝妙技巧。我相信这些提示会提升你的 Python 编程生产力。

此外,我很想知道你在使用 Jupyter notebook 时喜欢哪些技巧。

一如既往,感谢阅读!

5 个我在数据科学生涯中仅发现了 2 年的 Jupyter Notebook 技巧

原文:towardsdatascience.com/5-jupyter-notebook-tricks-i-only-discovered-2-years-into-my-data-science-career-99bbe482a45f

自定义键盘快捷键、高亮文本等

Matt ChapmanTowards Data Science Matt Chapman

·发表于 Towards Data Science ·阅读时间 7 分钟·2023 年 7 月 17 日

--

图片来源于 Lukas BatoUnsplash

尽管 R、Python 和 Julia 的用户都很喜欢使用它们,Jupyter Notebook 的潜力却很少被充分利用。

大多数用户知道基本命令(执行代码、注释、保存等),但很少有人利用 Jupyter 的隐藏技巧 — 尽管这些技巧可以节省大量时间和精力

自 2019 年(我开始使用 Jupyter)以来,我发现了很多节省时间的技巧和窍门,这些是大多数初学者不知晓的。在本文中,我将展示我最喜欢的 5 个技巧:

  1. 键盘快捷键 — “现成的”命令,例如插入/删除/移动单元格和文本

  2. 自定义 键盘快捷键 — 可以从键盘直接添加自己的高级命令,例如上下移动单元格、重启内核以及运行到当前单元格

  3. Markdown 格式 — 创建表格、格式化文本和创建复选框

  4. HTML 格式 — 突出显示文本并使评论更引人注目

  5. “启用输出滚动” — 抑制冗长的单元格结果(在调整超参数时,这是一笔巨大的财富)

提示 1:键盘快捷键

键盘快捷键提供了一种方便的方法来导航 Jupyter Notebook 并执行命令。以下是我常用的主要快捷键:

运行单元格

  • Shift + Enter — 运行当前单元格并选择下一个单元格

  • Ctrl/Cmd + Enter — 运行当前单元格

  • Option/Alt + Enter — 运行当前单元格并在下方插入另一个单元格

保存进度

  • Ctrl/Cmd + s — 保存笔记本

插入/删除单元格

首先,点击单元格内,然后确保你在command模式下,按Esc键。如果不按Esc,你将处于edit模式,只能对单元格内容进行操作(而不是单元格本身)。一旦你进入command模式,单元格中的光标将停止闪烁。然后,按以下任意一个键:

  • a — 在当前单元格上方插入一个新单元格

  • dd(按d键两次) — 删除当前单元格

  • b — 在当前单元格下方插入一个新单元格

更改单元格类型

Jupyter Notebooks 的一个乐趣在于它们允许你将评论和代码并排放置。要设置单元格的类型并确定其应将文本视为“评论”还是“代码”,首先通过选择一个单元格并按Esc键进入command模式。然后,按以下任意一个键:

  • m — markdown 模式(用于编写评论和标题)

  • y — 代码模式(用于编写代码)

选择多个单元格

再次确保你在command模式下,然后按住Shift键,使用UpDown箭头扩展选择范围,选择任意数量的单元格。

作者提供的图片

选定所有想要操作的单元格后,你可以一次性删除或移动它们,而不是逐个进行。

提示 2:自定义键盘快捷键

这对我来说是一个改变游戏规则的功能。

除了安装 Jupyter 时为你预定义的标准键盘快捷键外,你还可以创建你自己的自定义快捷键来执行高级命令。

例如,假设我想定义两个新的键盘快捷键:一个用于将单元格向上移动,另一个用于将单元格向下移动。默认情况下,可以通过使用鼠标“拖放”单元格来完成,但也可以创建一个自定义键盘快捷键,一键即可完成这个操作。

首先,点击 JupyterLab 窗口中的‘设置’,然后点击‘高级设置编辑器’,你将能够看到所有现有的键盘快捷键。

作者提供的图片

要添加自定义键盘快捷键,你需要点击高级设置编辑器窗口右上角的‘JSON 设置编辑器’(如上图所示)。

然后,在右侧的‘用户首选项’标签页中,向下滚动到键盘快捷键列表,并添加所需的快捷键。我添加了这两个,是 David 在 StackOverflow 上建议的。

{
    "command": "notebook:move-cell-up",
    "keys": [
        "Ctrl Shift ArrowUp"
    ],
    "selector": ".jp-Notebook:focus"
},
{
    "command": "notebook:move-cell-down",
    "keys": [
        "Ctrl Shift ArrowDown"
    ],
    "selector": ".jp-Notebook:focus"
},

作者提供的图片

完成快捷键添加后,点击‘保存’(在右上角),然后你就可以开始使用了!

很酷吧?

如果你喜欢这个故事,点击我的‘关注’按钮对我来说意义重大——只有 1% 的读者会这样做!感谢阅读。

提示 3:Markdown 格式

当你在所谓的“注释”单元格中写文本时,你实际上是在使用 markdown,所以,惊喜惊喜,你可以使用 markdown 格式进行书写。

这可能看起来显而易见或微不足道,但根据我的经验,很少有 Jupyter 用户充分利用他们笔记本中的 markdown 功能。

使用 markdown,你可以书写文本、标题、列表、表格,甚至代码片段。这是为你的笔记本带来更多动态和结构化的好方法。

例如,这里是我最常使用的一些格式。我特别喜欢‘表格’格式(因为这是展示某些预处理代码结果的好方法),并且我推荐使用这个Markdown Tables Generator来快速生成正确格式的表格。

使用 markdown 格式你可以做的一些事情。作者提供的图片

提示 4: HTML 格式化

这个提示有点像上面的扩展,但使用频率更低。

当你在 markdown 单元格中写文本时,你可以使用 HTML 标签来格式化文本,实现非常自定义的格式,比如高亮文本和在切换中隐藏文本。

作者提供的图片

就个人而言,我发现高亮功能在我审查他人笔记本时非常有用,尤其是当我想做评论或标记一些稍后需要回顾的内容时。这是一种很好的方式来突出我留下的注释,而不至于让笔记本显得杂乱。

提示 5: “启用输出的滚动”

默认情况下,Jupyter Notebooks 中的‘output’单元格会根据输出的高度进行调整——即当你运行一些代码时,Jupyter 会打印你代码指示的所有内容。我是说所有内容。

通常这非常有用,但在你运行打印大量输出的代码时,笔记本单元格可能会迅速增长到荒谬的长度,使得在笔记本中导航变得困难。

例如,下图展示了一个简单的代码片段,它将打印从 1 到 1001 的每一个整数。正如你所见,输出是很长的。

为了在不丢失有价值的打印信息的情况下压缩输出,右键点击输出单元格并选择‘启用输出的滚动’。这将输出保留在一个可滚动的输出单元格中,但避免了笔记本的杂乱。

作者提供的图片

在实际应用中,我发现这非常有用,比如(a)打印出表中所有列的名称,或者(b)使用optuna这样的库来调整模型的超参数。在这两种情况下,看到所有输出(所有列名或每轮训练的结果)都很有价值,我很少想要完全抑制这些输出,因为它们对调试非常有帮助。通过选择‘启用输出滚动’,我可以保留输出但将其最小化,使其不会占据整个笔记本。

还有一件事 —

我开设了一个名为AI in Five的免费新闻通讯,每周分享 5 个要点,涵盖最新的 AI 新闻、编码技巧以及数据科学家/分析师的职业故事。没有炒作,没有“数据是新的石油”的废话,也没有埃隆的推文——只有实用的技巧和见解,帮助你在职业上有所发展。

在这里订阅 如果你对此感兴趣!

[## AI in Five | Matt Chapman | Substack

最新的新闻、职业故事和来自数据科学与 AI 领域的编码技巧,概括为 5 个要点…

aiinfive.substack.com](https://aiinfive.substack.com/?source=post_page-----99bbe482a45f--------------------------------)

5 个从测试 Databricks SQL Serverless + DBT 中获得的经验教训

原文:towardsdatascience.com/5-lessons-learned-from-testing-databricks-sql-serverless-dbt-1d85bc861358?source=collection_archive---------3-----------------------#2023-10-17

我们进行了一项$12K 的实验,以测试 Serverless 仓库和 dbt 并发线程的成本和性能,并获得了意外的结果。

Jeff ChouTowards Data Science Jeff Chou

·

关注 发表在Towards Data Science · 9 分钟阅读 · 2023 年 10 月 17 日

--

作者:Jeff Chou, Stewart Bryson

图片来自Los Muertos Crew

Databricks 的 SQL 仓库产品对于希望简化生产 SQL 查询和仓库的公司具有很大的吸引力。然而,随着使用的扩展,这些系统的成本和性能变得至关重要。

在本文中,我们通过利用行业标准的 TPC-DI 基准测试,对他们的 Serverless SQL 数据仓库产品的成本和性能进行了技术深入分析。我们希望数据工程师和数据平台管理者能够利用这里呈现的结果,在选择数据基础设施时做出更好的决策。

Databricks 的 SQL 数据仓库提供了什么?

在我们深入探讨特定产品之前,让我们退后一步,看看今天提供的不同选项。Databricks 目前提供3 种不同的仓库选项

  • SQL Classic —— 最基本的仓库,运行在客户的云环境内。

  • SQL Pro —— 提升的性能,适合探索性数据科学,运行在客户的云环境内。

  • SQL Serverless —— 最佳性能,计算完全由 Databricks 管理。

从成本的角度来看,经典版和专业版都在用户的云环境内运行。这意味着您将为您的 Databricks 使用获得两个账单 —— 一个是纯粹的 Databricks 成本(DBU),另一个来自您的云提供商(例如 AWS EC2 账单)。

为了真正了解成本比较,让我们看一个在 Small 数据仓库上运行的示例成本细分的示例费用分解。

作业计算成本比较,以及各种 SQL Serverless 选项。所显示的价格基于按需列表价格。Spot prices will vary,并且是根据本出版物时的价格选择的。作者提供的图像。

在上表中,我们也看到了按需与 spot 成本的成本比较。您可以从表中看出,Serverless 选项没有云组件,因为所有管理工作都由 Databricks 处理。

Serverless 相比于专业版来说可能更具成本效益,如果您使用所有按需实例的话。但如果有廉价的 spot 节点可用,那么专业版可能会更便宜。总体来说,我认为 Serverless 的定价相当合理,因为它还包括云成本,尽管它仍然是一个“高端”价格。

我们还包括了等效的作业计算集群,这是全面管理的选择中最便宜的选项。如果成本是您关注的问题,您也可以在作业计算中运行 SQL 查询!

Serverless 的优缺点

Databricks 的 Serverless 选项是一个完全管理的计算平台。这基本上与 Snowflake 的运行方式相同,其中所有计算细节对用户隐藏。在高层次上,这种方法有其利弊:

优点:

  • 您不必考虑实例或配置。

  • 启动时间比从头开始启动集群要少得多(根据我们的观察为 5-10 秒)。

缺点:

  • 企业可能会对所有计算都在 Databricks 内部运行存在安全问题。

  • 企业可能无法利用其云合同中对特定实例的特殊折扣

  • 无法优化集群,因此你不知道 Databricks 选择的实例和配置是否实际上适合你的作业

  • 计算是一个黑箱——用户不知道发生了什么或 Databricks 在幕后实施了哪些更改,这可能导致稳定性问题。

由于无服务器的固有黑箱特性,我们对人们仍然可以调整的各种参数及其对性能的影响感到好奇。因此,让我们深入探讨一下我们探索的内容:

实验设置

我们尝试采用“实用”方法来进行这项研究,并模拟当一家真实公司希望运行 SQL 仓库时可能采取的措施。由于DBT在现代数据堆栈中非常流行,我们决定检查两个参数进行扫描和评估:

  • 仓库大小 — [‘2X-Small’, ‘X-Small’, ‘Small’, ‘Medium’, ‘Large’, ‘X-Large’, ‘2X-Large’, ‘3X-Large’, ‘4X-Large’]

  • DBT 线程 — [‘4’, ‘8’, ‘16’, ‘24’, ‘32’, ‘40’, ‘48’]

我们选择这两个参数的原因是它们都是任何工作负载的“通用”调优参数,并且它们都影响作业的计算方面。DBT 线程特别有效地调整作业在 DAG 中运行时的并行性。

我们选择的工作负载是流行的TPC-DI基准测试,规模因子为 1000。这个工作负载特别有趣,因为它实际上是一个完整的管道,模拟了更真实的数据工作负载。例如,下面是我们的 DBT DAG 的屏幕截图,你可以看到它相当复杂,改变 DBT 线程的数量可能会对其产生影响。

来自我们 TPC-DI 基准测试的 DBT DAG,作者图片

顺便提一下,Databricks 有一个出色的开源库,可以帮助快速在 Databricks 中设置 TPC-DI 基准测试。(我们没有使用这个库,因为我们是使用 DBT 进行的)

要深入了解我们如何运行实验,我们使用了Databricks Workflows并将 dbt 作为 dbt CLI 的“运行器”,所有作业都并行执行;由于 Databricks 方面的环境条件未知,不应存在变异。

每个作业启动一个新的 SQL 仓库,然后在同一 Unity Catalog 中的独特模式中运行并随后拆除。我们使用了Elementary dbt 包来收集执行结果,并在每次运行结束时运行 Python 笔记本,将这些指标收集到集中式模式中。

成本通过 Databricks 系统表提取,特别是那些用于计费使用的表。

自己尝试这个实验并克隆Github 仓库

结果

下面是成本和运行时间与仓库大小的图表。我们可以看到,运行时间在达到中等规模的仓库时停止扩展。比中等大一点的仓库对运行时间几乎没有影响(或许更糟)。这是一个典型的扩展趋势,显示了扩展集群规模不是无限的,它们总有一个点,增加更多计算资源的回报会递减。

对于计算机科学爱好者来说,这只是基本的计算机科学原理——阿姆达尔定律

一个不寻常的观察是,中等仓库的表现优于接下来的三个尺寸(大到 2xlarge)。我们重复了这个特定的数据点几次,并得到了一致的结果,所以这不是一个奇怪的偶然现象。由于无服务器的黑箱特性,我们不幸地不知道背后的情况,也无法给出解释。

各仓库大小的运行时间(分钟)。图片由作者提供

由于扩展在中等规模处停止,我们可以在下面的成本图中看到,成本在中等仓库大小后开始急剧上升,因为基本上你投入了更昂贵的机器,而运行时间保持不变。因此,你是在为额外的计算能力支付费用,但没有获得任何好处。

各仓库大小的成本(美元)。图片由作者提供

下面的图表显示了我们改变线程数和仓库大小时运行时间的相对变化。对于横轴零线以上的值,运行时间增加(这是不好的)。

线程增加时运行时间的百分比变化。图片由作者提供

这里的数据有些噪声,但根据仓库的大小有一些有趣的见解:

  • 2x-small — 增加线程数通常会使任务运行时间更长。

  • X-small to large — 增加线程数通常能使任务运行速度提高约 10%,尽管增益相对平稳,所以继续增加线程数没有价值。

  • 2x-large — 实际上存在一个最佳线程数,即 24,从清晰的抛物线可以看出。

  • 3x-large — 在线程数为 8 时运行时间出现了非常不寻常的峰值,为什么?不清楚。

为了将所有内容整合到一个综合图中,我们可以看到下面的图表,绘制了成本与总任务时长的关系。不同的颜色代表不同的仓库大小,气泡的大小表示 DBT 线程的数量。

成本与任务时长的关系。气泡的大小表示线程数量。图片由作者提供

在上图中,我们看到较大的仓库通常会导致较短的持续时间,但成本更高。然而,我们确实发现了一些不寻常的点:

  • 中型是最佳的——从纯成本和运行时间的角度来看,中型是最好的仓库选择

  • DBT 线程的影响——对于较小的仓库,线程数量的变化似乎使持续时间变化了大约+/- 10%,但成本变化不大。对于较大的仓库,线程数量显著影响了成本和运行时间。

结论

总结一下,我们关于 Databricks SQL 无服务器 + DBT 产品的前五个教训是:

  1. 经验法则是错误的——我们不能仅仅依赖关于仓库大小或 DBT 线程数量的“经验法则”。虽然确实存在一些预期的趋势,但它们并不一致或可预测,完全依赖于你的工作负载和数据。

  2. 巨大差异——对于完全相同的工作负载,成本范围从$5 到$45,运行时间从 2 分钟到 90 分钟,所有这些都由于线程数量和仓库大小的不同组合。

  3. 无服务器扩展有极限——无服务器仓库不会无限扩展,最终较大的仓库将无法提供任何加速,只会导致成本增加而没有任何好处。

  4. 中型很棒?——我们发现中型无服务器 SQL 仓库在 TPC-DI 基准测试中在成本和作业持续时间方面超越了许多较大的仓库。我们不知道原因。

  5. 作业集群可能是最便宜的——如果成本是一个问题,切换到仅使用标准作业计算和笔记本可能会便宜很多

这里报告的结果揭示了“无服务器”黑箱系统的性能可能会出现一些不寻常的异常。由于这一切都在 Databrick 的墙后,我们不知道发生了什么。也许所有这些都运行在巨大的 Spark on Kubernetes 集群上,也许他们在某些实例上与亚马逊有特殊的交易?无论如何,不可预测的性质使得控制成本和性能变得棘手。

因为每个工作负载在很多维度上都是独特的,我们不能依赖“经验法则”,或仅适用于当前状态的高成本实验。无服务器系统的混乱性质确实引发了这样一个问题:这些系统是否需要一个闭环控制系统来加以控制?

作为一种反思——无服务器的商业模式确实引人注目。假设 Databricks 是一个理性的企业,并且不希望降低他们的收入,同时他们也希望降低成本,那么必须提出一个问题:“Databricks 是否有动力去改善底层的计算?”

问题是这样的——如果他们让无服务器速度提高 2 倍,那么他们的无服务器收入会突然下降 50%——这对 Databricks 来说是非常糟糕的一天。如果他们能使其速度提高 2 倍,然后将 DBU 成本提高 2 倍以抵消速度提升,那么他们的收入将保持不变(实际上,这就是他们为 Photon 所做的)。

因此,Databricks 确实有动力在保持客户运行时间大致不变的情况下降低内部成本。虽然这对 Databricks 来说是好事,但很难将任何无服务器加速技术传递给用户,从而减少成本。

想了解如何改善你的 Databricks 管道?请联系 Jeff ChouSync 团队 的其他成员。

资源

相关内容

5 个 MLOps 成熟度级别

原文:towardsdatascience.com/5-levels-of-mlops-maturity-9c85adf09fe2

Maciej BalawejderTowards Data Science Maciej Balawejder

·发表于 Towards Data Science ·10 分钟阅读·2023 年 6 月 15 日

--

ML 基础设施从 1 级成熟度到 5 级的进展。图片由作者提供。

介绍

构建稳固的 ML 系统基础设施至关重要。它需要确保 ML 应用程序的开发和部署是有序且可靠的。但问题在于——每个公司的基础设施需求不同。这取决于他们有多少 ML 应用程序、需要多快部署,或者需要处理多少请求。

例如,如果一家公司只有一个生产模型,那么部署过程可以手动处理。在光谱的另一端,如 Netflix 或 Uber 这样有数百个生产模型的公司,需要高度专业化的基础设施来支持它们。

现在你可能会问自己一个问题:你的公司在这个光谱中处于什么位置?

GoogleMicrosoft 分享的 MLOps 成熟度级别旨在提供帮助。它们描述了基于行业最佳实践的 ML 基础设施的发展和复杂性。

本博客文章旨在综合并提取两个框架中的最佳实践。首先,我们将分析五个成熟度级别,并展示从手动流程到高级自动化基础设施的进展。然后,在最后一部分,我们将论证 Microsoft 和 Google 提出的某些观点不应盲目遵循,而应根据你的需求进行调整。这将帮助你在了解自己的基础设施现状和发现潜在改进领域时更加自觉。

好的,让我们深入探讨吧!

什么是 MLOps?

MLOps 是一组实践,用于建立一个标准化和可重复的流程,管理整个机器学习生命周期,从数据准备、模型训练、部署到监控。它借鉴了在软件工程中广泛采用的 DevOps 实践,后者关注于为团队提供快速和持续迭代的软件应用交付方法。

然而,DevOps 工具对机器学习世界来说是不够的,并且在几个方面有所不同:

  • MLOps 需要一个具有多学科技能的团队。这个团队包括负责数据收集和存储的数据工程师,开发模型的数据科学家,部署模型的机器学习工程师(MLE),以及将其与产品集成的软件工程师。

  • 数据科学本质上是实验性的,通过探索不同的模型、数据分析、训练技术和超参数配置来持续改进。支持 MLOps 的基础设施应包括跟踪和评估成功和失败的方法。

  • 即使模型在生产中正常运行,它仍可能由于输入数据的变化而失败。这被称为静默模型失败,由数据和概念漂移引起。因此,机器学习基础设施需要一个监控系统来不断检查模型的性能和数据,以防止这个问题。

现在让我们探索 MLOps 基础设施的不同成熟度级别。

级别 1 — 手动

手动机器学习基础设施。设计灵感来自谷歌的博客文章。图片由作者提供。

在这个级别,数据处理、实验和模型部署过程完全是手动的。微软将这一级别称为‘没有 MLOps’,因为机器学习生命周期难以重复和自动化。

整个工作流程高度依赖于熟练的数据科学家,并且在需要时,数据工程师协助准备数据,软件工程师协助将模型与产品/业务流程集成。

这种方法在以下情况中效果很好:

  • 早期阶段的初创公司和概念验证项目——在这些情况下,重点是实验,资源有限。在扩大运营规模之前,开发和部署机器学习模型是主要关注点。

  • 小规模的机器学习应用——对于范围有限或用户基础较小的机器学习应用(如小型在线时尚商店),手动方法可能是足够的。数据科学家可以手动处理数据处理、实验和部署过程,因数据依赖性较小且实时要求不高。

  • 临时机器学习任务——在特定场景如营销活动中,一次性的机器学习任务或分析可能不需要全面的 MLOps 实施。

根据谷歌和微软的说法,这种方法也面临一些局限性,包括:

  • 缺乏监控系统——没有关于模型性能的可视性。如果性能下降,将会对业务产生负面影响。此外,还需要在部署后进行数据科学分析,以了解模型在生产中的表现。

  • 生产模型没有频繁的重训练——模型没有适应最新的趋势或模式。

  • 发布过程痛苦且不频繁——由于是手动完成,模型发布每年只有几次。

  • 缺乏对模型性能的集中跟踪,使得比较不同模型的性能、重复结果或更新变得困难。

  • 有限的文档和缺乏版本控制——带来了一些挑战,比如引入意外代码更改的风险、有限的回滚到工作版本的能力和缺乏可重复性。

级别 2——可重复

可重复的机器学习基础设施,配有额外的源代码仓库和监控。图片来源于作者。

接下来,我们通过将实验转换为源代码并使用像 Git 这样的版本控制系统将其存储在源代码仓库中,引入 DevOps 方面的内容。

微软建议通过添加以下内容来修改数据收集过程:

  • 数据管道——允许从不同来源提取数据并将其合并。然后,通过清理、汇总或过滤操作对数据进行转换。它使基础设施比手动处理更具可扩展性、效率和准确性。

  • 数据目录——一个集中存储的数据仓库,包含数据源、数据类型、数据格式、所有者、使用情况和数据源流等信息。它有助于以可扩展和高效的方式组织、管理和维护大量数据。

为了提升基础设施,我们必须引入一些自动化测试以及版本控制。这意味着使用单元测试、集成测试或回归测试等实践。这些将帮助我们更快地部署,并通过确保代码更改不会引起错误或漏洞来提高可靠性。

在这些变化到位后,我们可以重复数据收集和部署过程。然而,我们仍然需要一个适当的监控系统。微软简单提到这一点,称“关于模型在生产中表现如何的反馈有限”,但他们没有详细说明。

级别 3——可重复

可重复的机器学习基础设施,配有自动化训练和协调实验。图片来源于作者。

可重复性至关重要的两个关键原因是:故障排除和协作。设想一种情况,你最近部署的模型性能下降,导致预测不准确。在这种情况下,你需要保留数据和模型的先前版本,以便在找到根本问题之前回滚到其他版本的模型。

此外,可重复性使不同团队成员更容易理解他人的工作并在彼此的工作基础上进行改进。这种协作方式和知识共享可以带来更快的创新和更好的模型。

为了实现可重复性,我们可能需要通过四种方式提升架构:

  • 自动化训练管道 — 处理从数据准备到模型评估的端到端训练过程。

  • 元数据存储库 — 一个数据库,用于跟踪和管理元数据,包括数据源、模型配置、超参数、训练运行、评估指标及所有实验数据。

  • 模型注册表 — 是一个存储机器学习模型、其版本及其部署所需工件的仓库,这有助于在需要时检索到确切的版本。

  • 特征存储 — 旨在帮助数据科学家和机器学习工程师通过提供集中存储、管理和服务特征的地点,更高效地开发、测试和部署机器学习模型。它还可以用于跟踪特征随时间的演变,并根据需要对特征进行预处理和转换。

在这一阶段,提供了监控服务,实时反馈模型的性能。然而,除了确认存在外,微软和谷歌均未提供更多信息。

Level 4 — 自动化

自动化机器学习基础设施与 CI/CD。图片来自作者。

这一自动化水平帮助数据科学家高效探索特征工程、模型架构和超参数的新想法,通过自动化机器学习管道(包括构建、测试和部署)。为实现这一点,微软建议加入两个额外的组件:

  • CI/CD — 在持续集成(CI)中,确保来自不同团队成员的代码变更集成到共享的代码库中,而持续部署(CD)则自动化将经过验证的代码部署到生产环境中。这允许快速部署模型更新、改进和修复漏洞。

  • 模型的 A/B 测试 — 这种模型验证方法涉及比较现有模型和候选模型之间的预测和用户反馈,以确定更好的模型。

Level 5 — 持续改进

通过自动重新训练持续改进的机器学习基础设施。图片来自作者。

在这一阶段,模型会根据监控系统的触发器自动重新训练。这个重新训练的过程也称为持续学习。持续学习的目标包括:

  • 应对可能出现的突发数据漂移,确保模型在面对数据的意外变化时仍然有效。

  • 适应诸如黑色星期五等稀有事件,在这些事件中,数据中的模式和趋势可能会显著偏离常规。

  • 克服冷启动问题,即模型需要为缺乏历史数据的新用户做出预测

推动自动化

微软和谷歌是云计算市场的主要参与者,Azure 占据了 22% 的市场份额,而谷歌为 10%。 他们提供了广泛的服务,包括计算、存储和开发工具,这些都是构建先进的机器学习基础设施的重要组成部分。

像任何业务一样,他们的主要目标是通过销售这些服务来创造收入。这也是他们的博客强调进步和自动化的部分原因。然而,更高的成熟度水平并不保证你的业务会有更好的结果。最优的解决方案是与公司特定需求和适当的技术栈相一致的方案。

尽管成熟度级别可以帮助你确定当前的进展,但不应盲目遵循,因为微软和谷歌的主要动机是销售他们的服务。一个具体的例子是他们推动自动再培训的过程。这个过程需要大量的计算,但通常是不必要的或有害的。再培训应在需要时进行。对于你的基础设施来说,更重要的是拥有一个可靠的监控系统和一个有效的根本原因分析过程。

监控应该从手动级别开始

在描述的成熟度级别中,第 2 级出现了一个有限的监控系统。实际上,一旦基于模型的输出做出业务决策,你就应该对模型进行监控,无论成熟度级别如何。这可以帮助你降低失败的风险,并查看模型在实现业务目标方面的表现。

监控的初步步骤可以简单到仅仅比较模型的预测值与实际值。这种基本的比较是对模型性能的基线评估,也是进一步分析模型失败时的良好起点。此外,还需要考虑数据科学工作的评估,包括投资回报率(ROI)的衡量。这意味着评估数据科学技术和算法带来的价值。理解这些努力在生成业务价值方面的有效性至关重要。

评估投资回报率可以为你提供洞察和信息,帮助你更好地做出资源分配和未来投资的决策。随着基础设施的发展,监控系统可能会变得更加复杂,具有更多的功能和能力。然而,你仍然应该关注在成熟度的第一个级别上应用基本监控设置的重要性。

再培训的风险

在第 5 级的描述中,我们列出了生产中自动再培训的好处。然而,在将其添加到基础设施之前,你应该考虑与之相关的风险:

  1. 延迟数据上的再培训

在一些现实场景中,例如贷款违约预测,标签可能会延迟数月甚至数年。实际情况仍在变化,但你却在用旧数据重新训练模型,这可能无法很好地代表当前的现实。

2. 无法确定问题的根本原因

如果模型的性能下降,并不总是意味着需要更多的数据。模型失败的原因可能有多种,例如下游业务流程的变化、训练-服务偏差或数据泄露。你应该首先调查以找到潜在的问题,然后在必要时重新训练模型。

3. 更高的失败风险

重新训练增加了模型失败的风险。除了它增加了基础设施的复杂性之外,更新的频率越高,模型失败的机会就越多。数据收集或预处理中的任何未检测到的问题都会传递到模型中,导致在缺陷数据上重新训练的模型。

4. 更高的成本

重新训练不是一个无需成本的过程。它涉及到与以下方面相关的费用:

  • 存储和验证重新训练的数据

  • 重新训练模型所需的计算资源

  • 测试一个新模型,以确定它是否比当前模型表现更好

总结

机器学习系统非常复杂。以可重复和可持续的方式构建和部署模型是很有挑战性的。在这篇博客文章中,我们探讨了基于谷歌和微软行业最佳实践的五个 MLOps 成熟度水平。我们讨论了从手动部署到自动化基础设施的演变,强调了每个水平带来的好处。然而,理解这些实践不应盲目跟随至关重要。相反,它们的适应应基于公司特定的需求和要求。

庆祝地球月的 5 个机器学习项目作为开发者

原文:towardsdatascience.com/5-machine-learning-projects-to-celebrate-earth-month-as-a-developer-539b47ff9b1f

环保项目的深入概述

Behic GuvenTowards Data Science Behic Guven

·发表于 Towards Data Science ·阅读时长 10 分钟·2023 年 4 月 19 日

--

Luca BravoUnsplash 上传的照片

现在世界上数据的数量难以夸大。我们淹没在从数十亿互联网用户生成的点击流数据到工业设备和科学实验产生的千兆字节数据中。数据正变得越来越像互联网时代的流通货币。

数据也代表了巨大的机会;通过分析和理解数据,我们可以获得以前不可能得到的洞察,并利用这些洞察来解决一些人类最紧迫的问题。

无论是开发机器学习模型预测自然灾害,还是构建数据驱动的工具优化能源使用,我们都有无数机会可以利用我们的数据科学知识来促进可持续发展和保护环境。

数据科学的潜力确实令人惊叹,很难不对其可能性和成果感到兴奋。通过使用先进的分析技术和机器学习从海量数据集中提取洞察,我们可以在为自己和下一代创造一个更加可持续和公平的未来方面产生真正的影响。我们正生活在一个激动人心的时代,我迫不及待地想看看未来会带来什么。

在本文中,我将分享 5 个动手数据科学/机器学习项目,这些项目将为我们提供如何利用技术解决现实世界问题的一些见解。我认为这将是庆祝地球月的一个很好的方式,并挑战自己并开展这些项目。我尽量使列表在项目主题和编程技能方面都保持多样化。这些项目也是很好的作品集项目。

让我们开始吧!

下面是本文将讨论的项目列表:

  1. 熊类保护项目

  2. LANL — 地震预测

  3. EDSA — 气候变化信念分析

  4. 基于机器学习技术的太阳能预测

  5. 开放足迹

  6. 结论

1. 熊类保护项目

机器学习、无服务器和公民科学中的熊类保护

照片由Hans-Jurgen MagerUnsplash上提供

让我们从可爱而巨大的生物——熊开始吧。

熊类保护项目解决了在偏远地区监控和跟踪熊类种群的挑战。这对保护工作至关重要,因为它可以帮助研究人员了解熊类的行为和分布,并识别可能威胁其生存的因素。

该项目结合了多种机器学习技术与计算机视觉技术,以分析由志愿者收集的公民科学数据。具体而言,它使用目标检测和人脸识别算法来识别图像和视频中的熊。这些算法使用标记数据进行训练,如ImageNet,以识别熊的特定特征,如其形状、大小和颜色。

一旦熊的图像和视频被处理,这些数据会存储在 Amazon S3(简单存储服务)桶中,并使用 Amazon SageMaker 进行分析。这使得研究人员和保护工作者能够对数据进行更深入的分析,包括识别熊类行为和种群分布的模式和趋势。

该项目还采用了无服务器架构,通过使用 AWS Lambda 和 Amazon DynamoDB 服务来处理和存储数据。无服务器计算方法允许系统根据需求的变化自动扩展或缩减,从而降低成本并提高效率。

总体而言,该项目展示了机器学习和无服务器计算技术在解决复杂环境挑战和促进保护工作中的强大能力。是AWS(亚马逊网络服务)在改善世界方面的一个极佳应用案例。

了解更多关于该项目的信息:BearResearch.org

视频演示由 Ed Miller 提供。

2. LANL — 地震预测

我们可以通过机器学习预测即将发生的地震吗?

照片由 Mohammed Ibrahim 提供,来源于 Unsplash

今年早些时候,2 月 6 日,土耳其南部发生了 7.8 级地震,靠近叙利亚北部边界。约九小时后,这次地震后又发生了另一场7.5 级地震。根据联合国新闻简报的报道,截至 3 月 23 日,死亡人数已经超过 56000 人。我们的心与受影响者同在,祝他们耐心和恢复。

也许确切预测地震灾害的时间很困难,但借助我们今天已有的先进技术,我相信我们可以减少破坏的影响,从而让更多生命免受影响。

最近发生的事件是我希望将地震预测项目纳入此列表的主要原因。

洛斯阿拉莫斯国家实验室 (LANL) 地震预测项目仅仅是一个开发机器学习模型来预测地震的研究项目。该项目基于这样一个前提,即地震通常会伴随着地壳中可检测的变化,如地震活动或其他地球物理变量的变化。

LANL 地震预测项目已经进行多年,涉及了来自地震学、地质学和计算机科学等多个学科的研究人员合作。

这个地震预测项目的动机在于地震可能带来的毁灭性后果,会对基础设施造成广泛的损坏和生命损失。如果地震能够更准确地预测,将使社区能够采取主动措施来减轻其影响。

该项目专注于开发和测试各种机器学习模型,包括神经网络、决策树和支持向量机。这些模型使用大量的地震数据进行训练,包括地震仪、GPS 传感器和卫星影像,以及其他地球物理变量如地面运动、应变和倾斜。

该项目还涉及开发和创建用于可视化和解释结果的软件工具。最终目标是识别可以用于更准确地预测地震的模式和相关性。

尽管取得了显著进展,但在实现准确可靠的地震预测之前,还必须克服许多挑战。以下是该项目面临的一些挑战:

最大的挑战之一是地壳的复杂性,它可能受到包括构造活动、火山喷发以及矿业和压裂等人类活动在内的多种因素的影响。此外,地震本质上是不可预测的,任何预测都存在一定程度的不确定性。

尽管面临这些挑战,LANL Earthquake Prediction 项目代表了在开发用于预测自然灾害的机器学习模型方面的重要进展。这些模型最终可以挽救无数生命,减少地震对全球社区的影响。

了解更多关于该项目的信息:LANL Earthquake Prediction

数据集可在Kaggle上公开获取。

3. EDSA — 气候变化信念分析 2022

我们能否基于个人的历史推文数据预测其对气候变化的信念?

图片由Matt Palmer拍摄,来源于Unsplash

该项目是一个数据科学项目,旨在使用机器学习技术分析南非公众对气候变化的态度。

该项目由 Explore Data Science Academy(EDSA)进行,该组织位于南非,提供数据科学及相关领域的培训。该项目是他们社会影响数据科学项目的一部分,该项目旨在应用数据科学技术解决社会和环境问题。他们的项目也得到了 AWS 的支持。

该项目涉及通过在线调查收集数据,调查分发给了南非代表性样本的居民。调查包含了开放式和封闭式问题,以捕捉受访者对气候变化的信念、人口统计信息以及其他相关因素。

数据收集完成后,项目团队使用各种数据清洗技术对其进行了处理,以确保数据适合分析。然后,他们采用了一系列算法来探索数据,并对公众对气候变化的态度进行洞察。

团队使用的一个重要技术是自然语言处理(NLP),它涉及使用计算方法来分析人类语言。团队对调查中的开放式回答应用 NLP,以识别受访者的常见主题和情感。这使他们能够更好地了解影响南非公众对气候变化态度的因素。

团队还应用了分类算法,根据受访者的 demographics 和其他因素预测其对气候变化的信仰。这涉及到训练机器学习模型,将受访者分类为“相信者”或“非相信者”。他们还创建了可视化工具,以帮助向更广泛的受众传达他们的发现,包括创建互动仪表板和数据可视化。

总体而言,这个项目是一个重要的举措,利用数据科学技术洞察南非公众对气候变化的态度。数据是公开的,欢迎挑战自己并运用你的数据科学技能。

了解更多关于项目的信息:EDSA — 气候变化

了解他们如何在 Explore Data Science Academy 使用 AWS:视频

数据集可以在 Kaggle 上公开获取。

4. 太阳能发电预测与机器学习技术

EMIL ISAKSSON 和 MIKAEL KARPE CONDE 的研究论文。

照片由 美国公共电力协会Unsplash 提供

这篇论文聚焦于太阳能发电预测问题,这对在电网中整合和管理太阳能至关重要。准确的太阳能发电预测可以帮助电网操作员做出与电网管理相关的明智决策,并减少在电力市场中平衡供应和需求的成本。

作者使用了一个历史天气和太阳能发电数据集,该数据集包括八年来测量的太阳辐射的每日能源输出。该数据集通过应用特征提取和归一化技术进行了预处理,以便与机器学习算法配合使用。

使用该数据集训练和评估了几个机器学习模型,包括支持向量回归(SVR)、随机森林回归(RFR)和人工神经网络(ANN)。这些模型使用平均绝对误差(MAE)和均方根误差(RMSE)等指标进行评估。

快速洞察:研究发现 ANN 模型在准确性和鲁棒性方面优于其他模型。ANN 模型可以捕捉输入特征与太阳能发电输出之间复杂的非线性关系,使其成为更有效的预测工具。

论文还讨论了所开发的预测模型的潜在实际应用,例如帮助电网运营商做出与电网管理相关的决策,并减少电力市场上平衡供需的成本。

研究得出结论,机器学习技术,特别是 ANN 模型,可以作为准确太阳能预测的实用工具,最终有助于在电网中集成和管理太阳能。

了解更多关于该项目的信息:太阳能预测

5. Open Footprint

个人、组织、产品和社区的环境档案

图片由JuniperPhoton拍摄,发布在Unsplash

气候变化是我们今天面临的最紧迫的问题之一。大气中的二氧化碳及其他温室气体增加导致气温上升、冰川融化和极端天气事件频发。作为个人、组织和社区,我们每个人都在减少碳足迹和缓解气候变化影响方面发挥作用。

为了解决这一问题,一组开发人员和环保主义者创建了 Open Footprint——一个开源碳足迹项目,旨在为个人、组织、产品和社区提供环境档案。

该项目设计为模块化和可定制的,允许用户选择与其需求最相关的指标和计算。项目托管在 GitHub 上,代码对任何人开放下载、使用和贡献。

项目包含了各种功能,如数据可视化和报告工具,使用户更容易理解和传达他们的碳足迹数据。该项目根据GNU 通用公共许可证授权,确保软件保持开源并对所有人免费提供。

总体而言,Open Footprint 项目是一个重要的倡议,促进了碳足迹和其他环境影响指标的透明度和问责制。通过提供一个开放且易于访问的平台来测量和跟踪生态数据,该项目旨在赋予个人、组织和社区更多的信息,以便做出更明智的环境影响决策,并采取行动减少碳足迹。

该项目的目标是创建一个免费的、开放的、可访问的平台,用于测量和跟踪碳足迹以及其他环境影响指标。该项目的模块化和可定制设计也确保它具有足够的灵活性,以满足各种用户的需求,使其成为任何有兴趣减少环境影响的人的有价值工具。

了解更多关于该项目的信息:Open Footprint

示例用例:EPA 的碳足迹计算器

结论

照片由Li-An Lim拍摄,来源于Unsplash

总结来说,作为当今的开发者和数据科学家,我们有能力真正改变世界。无论是预测自然灾害还是优化能源使用,数据科学和机器学习为我们提供了强大的工具,帮助我们为自己和下一代创造一个更好、更可持续的未来。

所以,让我们撸起袖子,开始工作吧!

进行实际的编程项目是提升我们技能的最佳方式。如果你对这些项目有任何问题或反馈,请随时联系我或回应这篇文章。

我是Behic Guven,我喜欢分享有关编程、技术和教育的故事。如果你喜欢阅读这些故事并想支持我的旅程,可以考虑成为Medium 会员。谢谢,

如果你想知道我写了什么样的文章,这里有一些:

  • 用 Python 构建面部识别器。

  • 逐步指南——用 Python 构建预测模型。

  • 用 Python 构建语音情感识别器。

每个数据科学家都应该避免的 5 个错误

原文:towardsdatascience.com/5-mistakes-every-data-scientist-should-avoid-7e3523f6a9ec

照片由 Lucas George Wendt 提供,来源于 Unsplash

通过避免这些陷阱来提升你的数据科学水平

Louis ChanTowards Data Science Louis Chan

·发表在 Towards Data Science ·阅读时间 7 分钟·2023 年 1 月 12 日

--

数据科学是一个广阔的领域,需要大量的经验和知识才能掌握。在我们成为更好的数据科学家的过程中,定期自我审视、从错误中学习并避免无意中犯的错误是至关重要的。

在这篇博客中,我们将讨论我作为数据科学家期间遇到的 5 个陷阱。

1. 辛普森悖论

这是数据科学中一个相对被低估的领域。辛普森悖论发生在我们合并组数据时,局部数据模式变得不可观察。这可以通过一个例子来更好地解释:

图片来自作者

我们有一个关于某打车应用流失客户的数据集。我们注意到,对于区域 1,客户的里程越多,流失的可能性越大。对于区域 2,客户的里程越少,流失的可能性越大。最后,对于区域 3,无论客户的里程是多少,流失的可能性几乎相等。在上图中,当我们将这三个区域合并时,我们会看到什么?它就变成了一大堆数据,没有明显的模式。

这可能听起来像是一个棘手的问题。但我们可能需要从最简单的角度来解决它。检测和可能解决辛普森悖论的一些方法包括:

  • 我们不应仅仅在整个数据集上应用统计分析(预处理或模型评估)。相反,我们应该将数据集拆分,分别在不同的切片——数值变量的区间、分类变量、类别和标签等——上运行相同的分析。这可以帮助我们识别在整个数据集上可能不显著的局部数据模式。

  • 当我们注意到辛普森悖论时,解决这一问题的一种方法是为这一样本组训练一个不同的模型,假设它们足够重要。可以添加启发式规则来确定应该使用哪个模型。

  • 解决辛普森悖论的另一种可能方法是在特征工程过程中引入交互项。在上面的例子中,我们可以在面积和里程之间创建一个特征交叉,以放大这两个特征与流失概率之间的不同局部关系。

2. 选择偏差

垃圾进,垃圾出。

这对于任何分析功能都是正确的。我们必须更加重视数据质量的重要性。衡量数据质量的一个标准是数据集是否准确代表了总体。

选择偏差的一个例子可能是一个机器学习模型评估新治疗焦虑的有效性,该模型训练的数据来自于大多数患者不患有该病的研究组。虽然建立治疗的基线是重要的,但如果没有包含足够的焦虑患者数据点,就会显著削弱模型的鲁棒性和泛化能力。

为了避免选择偏差,我们应始终考虑以下几点:

  • 目标人群是什么?

  • 从表现角度来看,我们的训练集与目标人群有多大的不同?

  • 我们的数据是如何收集的?我们是否可能低估或高估了某些群体?

  • 在考虑人群由哪些群体组成时,我们是否遗漏了其他身份或参数?

当有疑问时,务必花时间审查数据及其收集策略。

3. 混杂变量

我们的模型可能没有达到预期效果的一个原因可能是我们遗漏了一些与我们的自变量(X)和因变量(y)相关的额外变量(混杂变量)。这意味着,我们不是让模型更容易地找到混杂变量与因变量之间的关联,而是让模型通过特征集与因变量之间的传递关系进行学习。

一个例子可能是分析某人患肺癌的概率。可能发现患这种致命疾病的男性比例很高。但除非我们包含吸烟习惯、家庭空气质量、职业接触毒素和癌症家族史等数据点,否则这可能不是一个非常准确的结论。

作者提供的图片

一旦我们开始包含混杂变量,我们的模型应该会有所改善,因为我们提供了与因变量有更直接关系的特征。我们也应该对我们的特征对因变量的重要性得出更准确的结论。

一般来说,我们应该不断提醒自己,相关性并不等于因果关系。当我们怀疑是否遗漏了任何混杂变量时,以下问题可能会有所帮助:

  • 我们是否期望排名最重要的特征对因变量有如此大的影响?

  • 专家如何解释重要特征与因变量之间的互动?

  • 假设我们对自变量和因变量之间关系的逻辑解释涉及推理步骤(例如,更多男性吸烟,因此肺癌风险更高)。我们可以将中间变量纳入我们的特征集吗?

4. 异方差性

异方差性。除了这个词发音困难外,它还是一个数据科学家在未察觉时可能面临的致命陷阱。大多数时间序列分析方法在数据方差随时间保持稳定时效果最佳。问题在于,当数据的波动性随时间剧烈变化时。

作者提供的图片

即使手头的数据不是时间序列,也并不意味着我们可以摆脱异方差性。想象一下,你在拟合体重和身高之间的回归模型。较低年龄组的收入水平应该集中在较低的范围内。随着年龄的增加,收入应该以一个依赖于多个因素(如公司、职位变动、职业变化、经济情况等)的速率增加,从而导致收入分布更加稀疏。这意味着即使我们可能拥有较高的 R 平方值,随着年龄的增加,我们的模型也会变得不那么准确。

作者提供的图片

好消息是:我们可以通过提出这些问题来提醒自己:

  • 错误项是否与所用特征中的任何一个相关?

  • 错误项的分布是否会随着所用特征的变化而变化?

  • 我们是否可以进行任何变换(如对数变换)或重采样(如自助法)以调整方差的规模?

5. 多重共线性

另一个常见的陷阱是多重共线性。这是一个自变量彼此高度相关的情况。乍一看,这可能是一个相对次要的问题。但它会使我们的解决方案不必要地复杂甚至不稳定。

比如我们想建立一个模型来估计市场营销活动的成功。使用的特征包括 Facebook 广告、Instagram 广告、TikTok 广告和 Google 广告的点击率,以及总体转化率。无论使用哪个平台,精心策划的广告都应该期望有不错的点击率,因此点击率相关的特征应具有合理的相关性。假设你加入了这家公司,你对老板的第一印象是将模型复杂度减少至少 100% 同时保持更稳定的结果。你刚刚为你的晋升做了一个有力的证明。

我们如何准确发现和解决类似问题?以下是我过去亲自使用的一些建议:

  • 相关性矩阵:不言自明,用于发现线性相关的特征

  • 降维:PCA、t-SNE 和 UMAP 等技术可以作为减少列数的有前途的方法,同时保留数据集中大部分的方差

  • 移除/聚合变量:在上述示例中,可以使用加权平均列来替代所有彼此高度相关的列。

  • 正则化:可以使用 Lasso(L1)或 Ridge(L2)等技术来克服多重共线性

结论

这里有 5 个我个人之前曾经陷入的陷阱。

不要止步于此

像任何领域一样,数据科学是一个需要不断打磨思维和获取新知识的深坑,以便从人群中脱颖而出。

如果你喜欢这篇文章,你还可以通过下面的我的推荐链接订阅 Medium 来支持我。这是一个我找到许多有趣阅读的平台。即使你完全不打算订阅,你也可以通过点赞支持我和我的创作。

[## 使用我的推荐链接加入 Medium — Louis Chan

阅读 Louis Chan 的每一个故事(以及 Medium 上其他成千上万的作者)。你的会员费用直接支持……

louis-chan.medium.com

最后但绝对重要的是,如果我遗漏了或误解了任何关键点,请随时在 LinkedIn 上发表评论或发私信给我。让我们共同保持知识流动,并在这个领域中不断进步!

[## Louis Chan — 领先 GCP 数据与 ML 工程师 — 副总监 — KPMG 英国 | LinkedIn

一个雄心勃勃、好奇且富有创造力的个体,坚信知识领域之间的相互联系以及……

www.linkedin.com

5 个更多超棒的 Python 隐藏功能 — 第二部分

原文:towardsdatascience.com/5-more-awesome-python-hidden-features-part-2-160a533c212b

PYTHON | PROGRAMMING | FEATURES

探索一些强大的功能,以释放 Python 的全部潜力

David FarrugiaTowards Data Science David Farrugia

·发布在 Towards Data Science ·5 分钟阅读·2023 年 3 月 22 日

--

图片由 Joshua Reddekopp 提供,来源于 Unsplash

Python 是一种强大且稳健的语言 —— 使 Python 脱颖而出的一个原因是它的多功能性和动态性。Python 以其酷炫的‘一行代码’而闻名。毫无疑问,每当我们看到一行优美的 Pythonic 代码时,都会感到兴奋或好奇。

在我们之前的帖子中,我们讨论了 Python 编程语言中的 5 个超棒的隐藏功能。你可以在这里找到那篇文章:

## 5 个超棒的 Python 隐藏功能 — 第一部分

利用这些酷炫的隐藏 Python 功能,将你的编码技能提升到一个新的水平

towardsdatascience.com

我收到了很多积极的反馈,有些人甚至联系我请求更多这样的强大 Python 技巧。

那么,来看看这 5 个酷炫的隐藏功能,它们可以让你在用 Python 编程时变得更加厉害!

隐藏功能 1: 列表步进

列表步进用于选择列表的不同部分,甚至可以选择间隔不同的项。语法如下:

list[start:end:step]
  • start: 包括的第一个元素的索引

  • end: 排除的第一个元素的索引

  • step: 每次迭代之间我们跳过的元素数量

假设我们有一个包含数字 0–9 的列表,我们想要返回只有偶数的部分。我们可以这样做:

my_list: list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
even_numbers: list = my_list[::2]  # [0, 2, 4, 6, 8]

在这里,我们不指定起始和结束索引,因此,Python 将起始索引视为第一个元素,结束索引视为最后一个元素(即整个列表)。然后我们指定步长为 2。因此,Python 将从第一个元素开始并返回它(即 0)。Python 然后移动 2 步(移动到 1,然后移动到 2),并返回结果(即 2)。这个过程会重复,直到遍历完整个列表。

使用列表步进的另一个强大技巧是能够使用负索引反转列表。

my_list: list = [1, 2, 3, 4, 5]
reversed_list: list = my_list[::-1]  # [5, 4, 3, 2, 1]

隐藏功能 2:链式比较操作符

在编程中,我们经常需要作为逻辑流程的一部分评估多个比较。

假设我们有一个变量 x,我们想确保 x 大于 1 但小于 10。我们通常会这样做:

x: int = 5

condition1: bool = x > 1  # check if x is greater than 1
condition2: bool = x < 10 # check if x is smaller than 10 

print(condition1 and condition2) # True

在 Python 中,我们可以按如下方式链式比较:

x: int = 5

print(1 < x < 10)   # True
print(10 < x < 20)  # False

我们也可以这样做:

x: int = 5

print(5 == x > 4)  # True
print(x < 10 < x*10 < 100)  # True

隐藏功能 3:复数/虚数

如果你学习过数学,你对复数的概念应该很熟悉。对于那些没有学习过且感兴趣的人,你可以阅读以下文章作为这个话题的良好介绍。

[## 复数

复数 复数是实数和虚数的组合 实数是数字…

www.mathsisfun.com

Python 中一个有趣的功能是大多数人不知道的,它完全支持复数。在数学中,我们通常使用符号 i 来表示复数。在 Python 中,我们使用 j 或调用 complex() 函数。

# Creating complex numbers
z1 = 2 + 3j
z2 = complex(4, -2)  # (4 -2j)

# Accessing real and imaginary parts
print(z1.real)  # 2.0
print(z1.imag)  # 3.0

# Arithmetic with complex numbers
z3 = z1 + z2  # (6+1j)
z4 = z1 * z2  # (14+8j)
z5 = z1 / z2  # (0.1+0.8j)

# Conjugate of a complex number
z6 = z1.conjugate()  # (2-3j)

隐藏功能 4:使用 _ 访问最后一个结果

你可能已经看到大多数程序员将 _ 作为一个占位符,用于在执行过程中未使用或不需要的变量。

然而,大多数人不知道的是,默认情况下,Python 将你最后一次执行的结果赋值给变量 _

x: int = 5
y: int = 99

x + y  # 104
print(_)  # 104

隐藏功能 5:参数解包

假设我们有一个任意的函数:

def my_sum(a, b, c):
    return a + b + c

我们有一个包含 3 个数字的列表,我们希望将其传递给我们的函数。我们通常写:

my_list = [1, 2, 3]

result = my_sum(my_list[0], my_list[1], my_list[2])
print(result)  # 6

在 Python 中,我们可以这样做:

result = my_sum(*my_list)
print(result)  # 6

* 将解包整个列表,并将每个项作为参数传递给函数。随后,我们也可以使用 ** 来解包字典。

# Example of dictionary argument unpacking
def my_func(a, b, c):
    print(f"a={a}, b={b}, c={c}")

my_dict = {'a': 1, 'b': 2, 'c': 3}

my_func(**my_dict)

额外功能:import antigravity

Python 中散布着几个隐藏的彩蛋。尝试 import antigravity 😁

你喜欢这篇文章吗?每月 $5,你可以成为会员,解锁对 Medium 的无限访问权限。你将直接支持我和你在 Medium 上的所有其他喜爱作者。非常感谢!

[## 使用我的推荐链接加入 Medium - David Farrugia

获得对我所有⚡优质⚡内容的独家访问权限,并在 Medium 上无限制浏览。通过请我喝杯咖啡来支持我的工作…

david-farrugia.medium.com

也许你还可以考虑订阅我的邮件列表,以便在我发布新内容时收到通知。这是免费的 😃

[## 当 David Farrugia 发布新内容时,获取电子邮件。

当 David Farrugia 发布新内容时,获取电子邮件。通过注册,如果你还没有 Medium 账户,你将创建一个…

david-farrugia.medium.com

想要联系我吗?

我很想听听你对这个话题或任何有关 AI 和数据的想法。

如果你希望联系我,可以通过 davidfarrugia53@gmail.com 给我发邮件。

Linkedin

5 个你需要了解的强大 Python 库,用于增强你的 EDA 过程

原文:towardsdatascience.com/5-powerful-python-libraries-you-need-to-know-to-enhance-your-eda-process-f0100d563c16

利用 Python 的强大功能探索和理解你的数据

安迪·麦克唐纳走向数据科学 安迪·麦克唐纳

·发布于 走向数据科学 ·阅读时间 10 分钟·2023 年 2 月 15 日

--

图片由 Gerd Altmann 提供,来源于 Pixabay

在运行机器学习模型之前,确保数据质量良好是至关重要的。如果我们将质量差的数据输入这些模型,可能会得到意想不到或不愿意看到的结果。然而,对数据进行准备工作并尝试理解你拥有的或缺失的内容是非常耗时的。通常,这个过程可能会消耗项目可用时间的 90%。

如果你在 Python 中进行探索性数据分析(EDA),你会知道一些常见的库,比如 pandas、matplotlib 和 seaborn。它们都是很棒的库,但每个库都有自己的细微差别,这可能需要时间去学习或记住。

近年来,出现了一些强大的低代码 Python 库,使数据探索和分析阶段的项目变得更快、更容易。

在这篇文章中,我将向你介绍这五个 Python 库,它们将提升你的数据分析工作流程。所有这些库都可以在 Jupyter notebook 环境中运行。

1. YData Profiling(之前称为 Pandas Profiling)

YData Profiling 库,前身为 Pandas Profiling,允许你基于 pandas dataframe 创建详细的报告。它非常易于导航,并提供有关各个变量、缺失数据分析、数据相关性和交互的信息。

YData Profiling 的一个小问题是处理较大数据集的能力,这可能会导致报告生成变慢。

如何使用 YData Profiling 库

可以通过终端使用 pip 安装 YData Profiling:

pip install ydata-profiling

在库安装到你的 Python 环境后,我们可以简单地从库中导入 ProfileReport 模块,并与 pandas 一起使用。Pandas 用于从 CSV 文件或其他格式加载数据。

import pandas as pd
from ydata_profiling import ProfileReport

df = pd.read_csv('Data/Xeek_Well_15-9-15.csv')
ProfileReport(df)

一旦数据被读取,我们可以将 dataframe 传递给 ProfileReport,报告将开始生成。

生成报告所需的时间将取决于数据集的大小。数据集越大,生成时间就越长。

报告生成后,你可以开始滚动查看报告,如下所示。

选择的数据集的 Ydata Profile 报告。图片来源于作者。

我们可以深入探讨数据集中的每个变量,查看数据完整性、统计数据和数据类型的信息。

查看数据集内数字变量的关键统计数据。图片来源于作者。

我们还可以创建数据完整性的可视化。这让我们理解缺失的数据以及缺失情况如何在不同变量之间相关联。

通过 YData Profiling 报告的各种视图识别缺失值。图片来源于作者。

你可以在下面的文章中探索更多 Pandas Profiling(改名之前)的功能。

[## Pandas Profiling — Python 中的简易探索性数据分析]

使用 Pandas Profiling 库进行快速有效的 EDA

towardsdatascience.com](/pandas-profiling-easy-exploratory-data-analysis-in-python-65d6d0e23650?source=post_page-----f0100d563c16--------------------------------)

2. D-Tale

D-Tale 将你的 Pandas dataframe 提升到一个全新的水平。这个强大且快速的库使得与数据交互、进行基本分析甚至编辑变得非常简单。

我最近才发现这个库,但它已成为我探索数据时的首选库之一。

如果你想在下载之前尝试这个库,库作者提供了一个 实时示例

如何使用 D-Tale

可以通过终端使用 pip 安装 D-Tale:

pip install dtale

然后可以与 pandas 一起导入,如下所示。一旦 pandas 读取了数据,结果数据框可以传递给 dtale.show()

import pandas as pd
import dtale

df = pd.read_csv('Data/Xeek_Well_15-9-15.csv')

dtale.show(df)

稍等片刻,D-Tale 交互式表格将出现,显示数据框中的所有数据。

D-Tale 配备了大量功能,允许你审查数据、可视化其完整性、编辑数据等。

当我们查看个别变量时,比如数据集中的 DTC 列,我们可以使用直方图可视化其分布:

D-Tale 的 Describe 模块中的交互式直方图。图片由作者提供。

还可以查看数据在分类变量中的分布情况:

通过岩性或地质形成等类别轻松可视化数据。图片由作者提供。

如果你想探索更多 D-Tale 的功能,可以在我下面的文章中找到更多信息:

## D-Tale 用于快速和轻松的井日志数据探索性数据分析

使用 D-Tale Python 库加速探索性数据分析工作流

towardsdatascience.com

3. SweetViz

SweetViz 是另一个低代码的交互式数据可视化和探索库。通过几行代码,我们可以创建一个交互式 HTML 文件来探索数据。

如何使用 SweetViz

Sweetviz 可以通过终端使用 pip 安装:

pip install sweetviz

安装完成后,我们可以将其导入到笔记本中,并使用 pandas 加载数据。

import sweetviz as sv
import pandas as pd

df = pd.read_csv('Data/Xeek_Well_15-9-15.csv')

然后我们需要调用两行代码来生成报告:

report = sv.analyze(df)
report.show_html()

这将打开一个新的浏览器标签页,显示以下设置。

SweetViz — 一个快速而强大的 EDA Python 库。图片由作者提供。

浏览器标签页打开后,你可以浏览数据框中的每个变量,查看每个变量的关键统计数据和完整性。当你点击任何变量时,如果数据是数值型数据,将打开数据分布的直方图;如果是分类数据,将显示值的计数。

此外,它还会显示该变量与数据集中其他变量之间的关系,具体以数字表示。

如果你想直观地查看,可以点击仪表板顶部的 Associations 按钮,打开一个图形相关性图。在下图中,我们可以看到一个混合的方形和圆圈,分别代表分类变量和数值变量。

方块/圆圈的大小代表关系的强度,颜色代表 Pearson 相关系数值。这应该是我迄今为止在 Python 中见过的最好的变量关系可视化之一。

使用 SweetViz 生成的变量关联。图片由作者提供。

我发现这个库的一个小问题是你需要一个宽屏幕才能查看所有水平内容而不需要滚动。然而,不要让这阻碍你利用这个库带给你 EDA 的强大功能。

4. Missingno

如果你对使用轻量级库来探索数据的完整性感兴趣,那么 missingno 是你应该在 EDA 工具箱中考虑的一个。

这是一个 Python 库,它提供了一系列可视化工具,用于理解 pandas dataframe 中缺失数据的存在及其分布。该库提供了一小部分图表(条形图、矩阵图、热图或树状图)来可视化你的 dataframe 中哪些列包含缺失值,以及缺失程度在变量之间的关系。

如何使用 MissingNo

Missingno 可以通过终端使用 pip 安装:

pip install missingno

一旦安装了库,我们可以将其与 pandas 一起导入,并将数据加载到 dataframe 中。

import pandas as pd
import missingno as msno
df = pd.read_csv('xeek_train_subset.csv')

然后,我们可以从可用的图表中调用我们想要的图表:

msno.bar(df)
msno.matrix(df)
msno.denrogram(df)
msno.heatmap(df)

missingno 库中的四个主要图表。图片由作者提供。

上述四种图表为我们提供了以下洞见:

  • 数据框中每一列的完整性 — msno.bar()

  • 缺失数据出现的位置 — msno.matrix()

  • 缺失值之间的相关性 — msno.heatmap()msno.dendrogram()

这个库的优点在于图表简洁易懂,并且可以快速直接地融入到报告中。

要了解更多关于这些图表的信息,我推荐深入阅读下面的文章。

使用 missingno Python 库识别和可视化机器学习前的缺失数据

使用岩石物理测井数据的示例

towardsdatascience.com

5. 草图

Sketch 是一个非常新的(截至 2023 年 2 月)库,利用 AI 的力量帮助你通过自然语言问题直接在 Jupyter 中理解你的 Pandas 数据框。你还可以使用它生成示例代码,例如如何绘制数据框中的 x 和 y,然后使用该代码生成所需的图表。

该库大多是自包含的,它使用机器学习算法来理解你的问题与数据集之间的关系。有一个函数依赖于 OpenAI 的 API,但这并不影响库的使用。

Sketch 具有很大的潜力,特别是如果你希望为客户提供一个对 Python 编码知识要求非常有限的界面的话。

如何使用 Sketch

可以通过终端使用 pip 安装 Sketch:

pip install sketch

然后我们将 pandas 和 sketch 导入到我们的笔记本中,接着从 CSV 文件中加载数据。

import sketch
import pandas as pd

df = pd.read_csv('Data/Xeek_Well_15-9-15.csv')

一旦导入了 Sketch,我们的数据框将有三个新方法可用。

第一个方法是 .ask,它允许你用自然语言提问关于数据框的内容。

df.sketch.ask('What are the max values of each numerical column?')

这将返回数据框中每个数值列的最大值的以下行。

当询问 Sketch 返回每列最大值时的响应。图像由作者提供。

我们还可以询问数据框的完整性:

df.sketch.ask('How complete is the data?')

它将返回以下响应,以人类编写的形式而不是表格或图形。

当询问 Sketch 关于数据框的完整性时的响应。图像由作者提供。

非常令人印象深刻。但这还不是全部。

我们甚至可以查询库关于如何使用 .sketch.howto() 绘制数据框中的数据

df.sketch.howto("""How do I plot RHOB against DEPTH_MD 
                using a line plot and have the line coloured in red?""")

它将返回一个代码片段,说明如何执行:

从 sketch.howto 函数返回的代码片段。图像由作者提供。

运行时,将返回以下图表:

从 sketch python 库返回的代码生成的图表。图像由作者提供。

Sketch 的第三个选项是 .apply 方法,它需要一个 OpenAI API 才能运行。当我们想要从现有特征创建新特征或生成新特征时,这个功能非常有用。到目前为止,我还没有探索这个选项,但希望在不久的将来能够进行。

有关 Sketch 的更多细节,请查看以下文章:

[## Sketch:一个有前景的 AI 库,用于直接在 Jupyter 中帮助处理 Pandas 数据框

在 Jupyter Notebook 中利用 AI 的力量

andymcdonaldgeo.medium.com

摘要

在本文中,我们已经介绍了五个强大的 Python 库,它们可以用来加快和增强项目的探索性数据分析阶段。这些库从简单的图形到利用自然语言处理技术与数据进行交互都有涉及。

我强烈建议你查看这些库并探索它们的功能。你永远不知道,也许你会发现你新的最爱 Python 库。

感谢阅读。在你离开之前,你应该订阅我的内容,将我的文章直接发送到你的收件箱。 你可以在这里做到这一点!或者,你也可以 注册我的通讯 以便将额外的内容直接免费发送到你的收件箱。

其次,你可以通过注册会员来获得完整的 Medium 体验,同时支持成千上万的其他作者和我。它只需每月 $5,你将能够完全访问所有精彩的 Medium 文章,并有机会通过你的写作赚钱。

如果你通过 我的链接 你将直接支持我,你的费用也不会增加。如果你这样做了,非常感谢你的支持。

本文使用的数据集是 Xeek 和 FORCE 2020 机器学习竞赛的一部分训练数据集的子集(Bormann et al., 2020)。它在挪威政府的 NOLD 2.0 许可证下发布,详细信息可以在这里找到:挪威开放政府数据许可证(NLOD)2.0。完整数据集可以通过这里访问。

数据集的完整参考是:

Bormann, Peter, Aursand, Peder, Dilib, Fahad, Manral, Surrender, & Dischington, Peter. (2020). FORCE 2020 油井记录和岩性数据集用于机器学习竞赛 [数据集]。Zenodo. doi.org/10.5281/zenodo.4351156

5 个区分资深开发者和初级开发者的 Python 技巧

原文:towardsdatascience.com/5-python-tricks-that-distinguish-senior-developers-from-juniors-826d57ab3940

通过对 Advent of Code 难题解决方法的差异进行说明

Tomer GabayTowards Data Science Tomer Gabay

·发表于Towards Data Science ·阅读时间 6 分钟·2023 年 1 月 16 日

--

Afif Ramdhasuma拍摄的照片,来源于Unsplash

自 2015 年起,每年 12 月 1 日,Advent of Code开始。正如他们网站上所描述的,Advent of Code(以下简称 AoC)是

一个降临节日历,包含各种技能水平的小编程难题,可以用任何编程语言解决。人们将其用作面试 准备公司培训大学 课程作业练习 问题速度竞赛相互挑战

在这篇文章中,我们将探讨五种高级解决常见编码问题的方法,而不是初级方法。每个编码问题都来源于 AoC 难题,许多问题在 AoC 和其他编码挑战及评估中反复出现,比如在求职面试中。

为了阐明这些概念,我不会详细讲解完整的 AoC 难题解决方案,而是只专注于某个具体难题的一小部分,在这个难题中,资深开发者和初级开发者的差异非常明显。

1. 有效地使用推导式和分割读取文件

Day1 中,需要读取几个数字块。每个块之间由一个空行分隔(因此实际上是 '\n’)。

输入和期望输出

# INPUT
10
20
30

50
60
70

# DESIRED OUTPUT
[[10, 20, 30], [50, 60 70]]

初级开发者方法: 使用 if-else 语句的循环

numbers = []
with open("file.txt") as f:
  group = []
  for line in f:
    if line == "\n":
      numbers.append(group)
      group = []
    else:
      group.append(int(line.rstrip()))
  # append the last group because if line == "\n" will not be True for
  # the last group
  numbers.append(group)

高级开发者方法: 利用列表推导式和 .split()

with open("file.txt") as f:
  # split input into groups based on empty lines
  groups = f.read().rstrip().split("\n\n")
  # convert all the values in the groups into integers
  nums = [list(map(int, (group.split()))) for group in groups]

使用列表推导式,我们可以将之前的 10 行代码压缩成两行,而不会显著损失(如果有的话)可理解性或可读性,并且性能有所提升(列表推导式比常规循环更快)。对于那些未曾见过 map 的人,map 将一个函数(第一个参数)应用于第二个参数中的可迭代对象。在这种特定情况下,它将 int() 应用到列表中的每个值,使每个项目变成整数。有关 map 的更多信息,请点击 这里

2. 使用 Enum 代替 if-elif-else

Day2 中,挑战围绕一个 石头-剪刀-布 游戏展开。不同选择的形状(石头、纸张或剪刀)会得到不同的点数:1 (X),2 (Y) 和 3 (Z)。以下是解决此问题的两种方法。

输入和期望输出

# INPUT
X
Y
Z

# DESIRED OUTPUT
1
2
3

初级开发者方法: if-elif-else

def points_per_shape(shape: str) -> int:
  if shape == 'X':
    return 1
  elif shape == 'Y':
    return 2
  elif shape == 'Z':
    return 3
  else:
    raise ValueError('Invalid shape')

高级开发者方法: Enum

from enum import Enum

class ShapePoints(Enum):
  X = 1
  Y = 2
  Z = 3

def points_per_shape(shape: str) -> int:
  return ShapePoints[shape].value

当然,在这个例子中,简单的方法并不是 糟糕,但使用 [Enum](https://docs.python.org/3/library/enum.html) 会导致更简洁和可读性更高的代码。特别是当选项更多时,简单的 if-elif-else 方法会变得越来越差,而使用 Enum 则相对容易保持概览。有关 Enum 的更多信息,请点击 这里

3. 使用查找表代替字典

Day3 中,字母具有不同的值。小写字母 a-z 的值为 1 到 26,大写字母 A-Z 的值为 27 到 52。由于可能的值很多,像上述那样使用 Enum 会导致很多行代码。这里更实用的方法是使用 查找表:

# INPUT
c
Z
a
...

# DESIRED OUPUT
3
52
1
...

初级开发者方法: 创建一个全局字典

letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
letter_dict = dict()
for value, letter in enumerate(letters, start=1):
  letter_dict[letter] = value

def letter_value(ltr: str) -> int:
  return letter_dict[ltr]

高级开发者方法: 使用字符串作为查找表

def letter_value(ltr: str) -> int
  return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.index(ltr) + 1

使用字符串的 .index() 方法,我们可以获得索引,因此 letters.index('c')+1 将得到期望的值 3。没有必要将值存储在字典中,因为索引 就是 值。为了避免 +1,你可以在字符串的开头添加一个空格字符,使得 a 的索引从 1 开始。然而,这取决于你是否希望返回空格的值为 0 还是错误。

正如你现在可能想到的,是的,我们也可以使用查找表来解决 石头、剪刀、布 任务:

def points_per_shape(shape: str) -> int:
  return 'XYZ'.index(shape) + 1

4. 高级切片

Day5 中,需要从行中读取字母(见下方输入)。每个字母在第四个索引上,从索引 1 开始。现在,几乎所有 Python 程序员都熟悉使用例如 list_[10:20] 的字符串和列表切片。但很多人不知道,你可以使用例如 list_[10:20:2] 来定义步长为 2。在 Day5(以及许多其他编码场景中),这可以节省你大量不必要的复杂代码:

# INPUT
    [D]    
[N] [C]    
[Z] [M] [P]

# DESIRED OUTPUT
[' D ', 'NC', 'ZMP']

初级开发者方法: 使用 range 和索引的双重循环

letters = []
with open('input.txt') as f:
  for line in f:
    row = ''
    for index in range(1, len(line), 4):
      row += line[index]
    letters.append(row)

高级开发者方法: 使用高级切片方法

with open('input.txt') as f:
  letters = [line[1::4] for line in f]

5. 使用类属性存储类实例

Day11 中描述了一种猴子互相传递物品的情况。为了简化,我们假设它们只是互相传递香蕉。每只猴子可以被表示为一个 Python class 的实例,其 id 和香蕉数量作为实例属性。然而,有很多猴子,它们需要能够互相交互。存储所有猴子并让它们能够互相交互的一个技巧是定义一个包含所有 Monkey 实例的字典,将其作为 Monkey 类的类属性。使用 Monkey.monkeys[id],你可以访问所有现有的猴子,而不需要 Monkies 类或外部字典:

class Monkey:
  monkeys: dict = dict()

  def __init__(self, id: int):
      self.id = id
      self.bananas = 3
      Monkey.monkeys[id] = self

  def pass_banana(self, to_id: int):
      Monkey.monkeys[to_id].bananas += 1
      self.bananas -= 1

Monkey(1)
Monkey(2)
Monkey.monkeys[1].pass_banana(to_id=2)

print(Monkey.monkeys[1].bananas)
2

print(Monkey.monkeys[2].bananas)
4

6. 自我文档化表达式(额外奖励)

这个技巧几乎在每次编写 Python 程序时都适用。与其在 f-string 中定义你正在打印的内容(例如

print(f"x = {x}") 你可以使用 print(f"{x = }”) 来打印值,并指定你正在打印的内容。

# INPUT
x = 10 * 2
y = 3 * 7

max(x,y)

# DESIRED OUTPUT
x = 20
y = 21

max(x,y) = 21

初级开发者方法:

print(f"x = {x}")
print(f"y = {y}")

print(f"max(x,y) = {max(x,y)}")

高级开发者方法:

print(f"{x = }")
print(f"{y = }")

print(f"{max(x,y) = }")

总结

我们已经探讨了 5 个 Python 技巧,这些技巧区分了高级开发者和初级开发者。当然,单单应用这些技巧并不能让人瞬间晋升为高级开发者。然而,通过分析这两者之间风格和模式的差异,你可以学习高级开发者与初级开发者在处理编码问题时的方法差异,并开始内化这些方法,以便最终自己成为高级开发者!

如果你喜欢这篇文章并希望了解更多关于高级 Python 方法的信息,请务必阅读我关于如何找到内容以提升自己成为更高级开发者的另一篇文章!

## 如何通过向这些专业人士学习提升你的 Python 技能

避免停留在 Python 编程的初级水平

towardsdatascience.com

资源

posted @ 2024-10-13 15:12  绝不原创的飞龙  阅读(361)  评论(0)    收藏  举报