TowardsDataScience-2023-博客中文翻译-十三-

TowardsDataScience 2023 博客中文翻译(十三)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

伦理和可解释性 AI 的关键工具

原文:towardsdatascience.com/critical-tools-for-ethical-and-explainable-ai-ed0e336d82a

照片由 Wesley Tingey 提供,来源于 Unsplash

一个帮助你创建值得信赖且强大的模型的必备库和工具包指南

Nakul UpadhyaTowards Data Science Nakul Upadhya

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

--

机器学习模型通过提供显著的预测能力,彻底改变了多个领域。然而,随着这些模型变得越来越普遍,确保公平性和可解释性的需求已成为一个关键问题。构建公平和透明的模型是建立信任、避免偏见和减轻意外后果的伦理要求。幸运的是,Python 提供了大量强大的工具和库,使数据科学家和机器学习从业者能够直接应对这些挑战。事实上,众多工具和资源可能会让数据科学家和利益相关者感到不知所措,不知道该使用哪些工具。

本文将深入探讨公平性和可解释性,介绍了一系列精心挑选的 Python 包,涵盖了广泛的可解释性工具。这些工具使研究人员、开发人员和利益相关者能够深入了解模型行为,理解特征的影响,并确保他们的机器学习工作中的公平性。

免责声明:我将只关注三个不同的包,因为这三个包包含了大多数可解释性和公平性工具。然而,值得一提的工具列表可以在文章的最后找到。

InterpretML

GitHub: github.com/interpretml/interpret

文档: interpret.ml/docs/getting-started.html

可解释模型在机器学习中扮演着关键角色,通过揭示决策机制来促进信任。这种透明性对于监管合规、伦理考虑和用户接受度至关重要。InterpretML [1] 是微软研究团队开发的一个开源包,集成了许多关键的机器学习可解释性技术于一个库中。

Post-Hoc Explanations

首先,InterpretML 包含了许多后期解释算法,以揭示黑箱模型的内部。这些算法包括:

  • Shapley Additive Explanations (SHAP):一种基于博弈论的特征重要性解释方法。

  • 局部可解释模型无关解释(LIME):一种局部解释方法,它通过拟合一个可解释的替代模型来预测黑箱模型的结果。

  • 部分依赖图(PDP):一种基于扰动的可解释性方法,帮助展示特征之间的交互。

  • 莫里斯敏感度分析:一种通过系统性地扰动输入变量并观察输出结果的变化来量化输入变量对模型输出影响的方法(类似于 PDP)。

几乎所有上述方法都可以在其他库中找到,但 InterpretML 通过将所有这些方法组合到一个包中,使我们更容易使用。

Glassbox Models

除了提供后期解释外,InterpretML 还包含一些玻璃盒(或固有可解释)模型,如线性模型、决策树和决策规则(或忽略决策树)。

InterpretML 还是唯一一个包含解释性增强机器(EBM)的包,EBM 是一种基于树的梯度提升广义加性模型。内部,EBM 根据单个变量或变量交互的值生成贡献函数。这些函数随后被组合以进行最终预测,并可以通过可视化贡献值来生成全局解释。

COMPAS 数据集上的 priors_count 解释。随着 priors 的增加,模型预测的再犯率更高(作者图示)

EBM 的准确性通常与 LightGBM 和 XGBoost 等其他提升模型相当,使其成为任何数据科学家工具箱中的重要工具。请参阅 Dr. Kubler 的文章 关于 EBM 的详细解释。

Captum

GitHub: github.com/pytorch/captum

文档: captum.ai/docs/introduction

虽然 InterpretML 主要集中于“浅层”模型,Captum [2] 是 PyTorch 的深度学习可解释性首选包。该库包含许多后期可解释性算法,帮助提供特征重要性和神经元/层的归因(完整表格见下)。

Captum 归因算法按解释焦点组织(图片由作者提供)

这些算法帮助处理表格数据的可解释性,但它们的使用案例不限于此。是否曾想过 BERT 可能在其预测中关注什么?好吧,Captum 提供的教程之一展示了如何使用层集成梯度来解释 BERT 生成的问答对

问答系统可解释性(图片由作者提供)

Captum 还可以用于解释图像预测,使用如 Input x Gradient 或层相关传播等算法:

MNIST 预测解释使用层相关传播(图片由作者提供)

总的来说,这个库非常易于使用且极其多才多艺,使其成为任何深度学习开发者必知的工具。

AIF360

GitHub: github.com/Trusted-AI/AIF360

文档: aif360.readthedocs.io/en/stable/

虽然可解释性可以在识别模型中的潜在偏差方面发挥很大作用,但一些专门的工具和指标可以衡量并更重要的是,缓解数据集和预测工具中的不公平现象。其中之一是 AI 公平性 360 工具包(AIF360)[3],这是 IBM 为 Python 和 R 开发的开源库。该工具包涵盖了几乎所有可能需要的公平性和缓解方法。

此外,AIF360(如 Captum 一样)提供了大量易于入门的教程来指导如何使用这个库。

数据集

AIF360 提供的第一个极其有用的功能是提供大量在学习公平性和可解释性时非常有用的沙箱数据集。这些数据集包括成人普查收入、银行营销、COMPAS(犯罪复发数据集)、MEPS(2019-21 年医疗支出调查)数据、法学院 GPA 和德国信用数据集。所有这些都是检查公平性和系统性偏见的绝佳起点。

公平性指标

AIF360 还提供了一整套工具,用于计算在特权和非特权群体上的表示和模型性能指标。这使得用户能够轻松计算公平性评分,如平等化机会(各组之间的假阳性和假阴性率相等)和人口统计均衡(忽略敏感特征时的预测相同)。例如,使用compute_num_TF_PN可以比较一个被低估群体和特权群体之间的混淆矩阵。

缓解方法

AIF360 的顶级特色是库中包含的大量缓解算法。这些算法可以轻松集成到标准机器学习管道中,几乎不需要做任何改动,并且所有算法都与 sklearn 接口兼容。

第一组缓解方法是预处理算法。这些算法转换输入数据,以帮助平衡数据的公平性和表示。AIF360 包含四种此类算法:

  • 不平等影响去除:这项技术编辑各类特征值,以提高整体公平性,并减少系统性偏见对数据集的影响。

  • 学习公平表示(LFR):该算法找到数据的潜在表示,编码重要信息,同时隐藏关于受保护属性的信息。

  • 优化预处理:这项技术学习一种概率变换,编辑特征和标签以确保群体公平性和数据的真实性。

  • 重新加权:该算法简单地重新加权样本,以确保分类任务前的公平性。

AIF360 还提供了许多“处理中”方法,这些方法围绕训练和超参数搜索过程进行。这些方法包括网格搜索减少(寻找优化性能和公平性的超参数)、对抗性去偏(学习第二个模型,旨在使用第一个模型的结果检测受保护属性)等。

最后,AIF360 提供了多个后处理算法,这些算法接受模型的预测结果并解决其优化问题,以使预测结果更公平。这些算法包括校准均衡赔率(修改预测以确保正负率相等)和拒绝选项分类器(改变预测以给予弱势群体更有利的结果)。

荣誉提及

上述三大库非常出色,将覆盖初学数据科学家 80%的可解释性和公平性需求。然而,还有一些其他包和工具值得荣誉提及:

可解释性

  • SHAP [4] / LIME [5]:分别是 SHAP 和 LIME 算法的专用实现,以及相关可视化。

  • ELI5 [6]:该包类似于 InterpretML,提供了许多白盒模型和黑盒解释器。不幸的是,这个项目已不再更新。

  • Yellowbrick [7]:这个包扩展了 sklearn API,提供了许多用于模型内部可视化的工具。

  • Alibi [8]:该包类似于 InterpretML 和 ELI5,提供了许多解释器和白盒模型。

公平性

  • Fairlearn [9]: Fairlearn 是一个类似于 AIF360 的库,提供促进公平的工具。这个包包含了许多 AIF360 中的算法。

  • Aequitas [10]: Aequitas 是一个偏见审计工具包,它既是一个库也是一个 web 应用程序。使用这个工具,你可以生成关于数据中可能存在的系统性偏见的报告。

  • FairML [11]: FairML 是一个量化模型输入相对重要性和预测依赖性的库。这个工具可以帮助审计预测模型。

结论

最终,拥抱可解释性和公平性的集体努力将引领我们迈向一个未来,在这个未来中,AI 系统不仅准确强大,而且透明、公正和可信,从而最终惠及开发者和最终用户。通过利用这些 Python 包的能力并承诺于伦理 AI,我们可以为一个更加包容和负责任的 AI 驱动世界铺平道路。

资源与参考文献

  • 如果你对可解释的机器学习和预测感兴趣,可以关注我:medium.com/@upadhyan

  • 欲了解更多有关伦理和可解释 AI 的文章,请查看以下阅读列表:

Nakul Upadhya

Nakul Upadhya

可解释和伦理的 AI

查看列表5 个故事

参考文献

[1] Nori, H., Jenkins, S., Koch, P., & Caruana, R. (2019). InterpretML: 一个统一的机器学习可解释性框架。arXiv 预印本 arXiv:1909.09223

[2] Narine Kokhlikyan, Vivek Miglani, Miguel Martin, Edward Wang, Bilal Alsallakh, Jonathan Reynolds, Alexander Melnikov, Natalia Kliushkina, Carlos Araya, Siqi Yan, & Orion Reblitz-Richardson. (2020). Captum: 一个统一且通用的 PyTorch 模型可解释性库。

[3] Rachel K. E. Bellamy, Kuntal Dey, Michael Hind 和 Samuel C. Hoffman, Stephanie Houde, Kalapriya Kannan 和 Pranay Lohia, Jacquelyn Martino, Sameep Mehta 和 Aleksandra Mojsilovic, Seema Nagar, Karthikeyan Natesan Ramamurthy 和 John Richards, Diptikalyan Saha, Prasanna Sattigeri 和 Moninder Singh, Kush R. Varshney, & Yunfeng Zhang. (2018). AI Fairness 360: 一个可扩展的工具包,用于检测、理解和缓解不希望出现的算法偏见。

[4] Lundberg, S., & Lee, S.I. (2017). 解释模型预测的统一方法。神经信息处理系统进展 30, 4765–4774。

[5] Marco Tulio Ribeiro, Sameer Singh, & Carlos Guestrin (2016). “我为什么应该相信你?”:解释任何分类器的预测。在 第 22 届 ACM SIGKDD 国际知识发现与数据挖掘大会论文集,旧金山,加州,美国,2016 年 8 月 13–17 日(第 1135–1144 页)。

[6] TeamHG-Memex (2019) ELI5\。Github

[7] Bengfort, B., & Bilbro, R. (2019). Yellowbrick: 可视化 Scikit-Learn 模型选择过程。开放源软件期刊,4(35)。

[8] Janis Klaise, Arnaud Van Looveren, Giovanni Vacanti, & Alexandru Coca (2021). Alibi Explain: 解释机器学习模型的算法。机器学习研究期刊,22(181),1–7。

[9] Bird, S., Dudik, M., Edgar, R., Horn, B., Lutz, R., Milan, V., Sameki, M., Wallach, H., & Walker, K. (2020). Fairlearn: 评估和改进 AI 公平性的工具包 [白皮书]。微软。

[10] Saleiro, P., Kuester, B., Stevens, A., Anisfeld, A., Hinkson, L., London, J., & Ghani, R. (2018). Aequitas: 偏见和公平性审计工具包。arXiv 预印本 arXiv:1811。

[11] Adebayo, J. A. (2016). FairML: 诊断预测建模偏差的工具箱(博士论文,麻省理工学院)。

跨越人工智能鸿沟:OpenAI 如何将 LLMs 转变为主流成功

原文:towardsdatascience.com/crossing-the-ai-chasm-how-openai-turned-llms-into-a-mainstream-success-fc70315cf247?source=collection_archive---------2-----------------------#2023-10-19

以及为什么 LLMOps 将会和 MLOps 遭遇相同的命运

克莱门斯·梅瓦尔德数据科学走向 克莱门斯·梅瓦尔德

·

关注 发表在 数据科学走向 ·15 分钟阅读·2023 年 10 月 19 日

--

我一直对 ML 开发工具(广义上称为 MLOps)作为独立业务的可行性持公开怀疑态度,并且在非常少的例外情况下,我的观点得到了验证。缺乏主导设计导致了碎片化的“微市场”,由于开源替代方案和云供应商免费提供其 ML 工具(以便在基础设施层收取收入),这些市场的价值捕获非常有限。那么,是什么让 LLM 超越这些问题,获得媒体的广泛关注,并实现真正的广泛采纳?而那些将 MLOps 手册用于 LLM 的初创公司,重新品牌为 LLMOps,最终会怎样呢?

在这篇文章中,我将使用“创新扩散”理论以及“跨越裂谷”的概念,来解释我对 LLM 提供商如 OpenAIAnthropic 的乐观预期,以及我对将 MLOps 复兴为 LLMOps 尝试的悲观看法。

创新采纳与裂谷

根据埃弗雷特·罗杰斯的“创新扩散”,创新产品由具有不同特征的不同采纳者逐步采纳。创新者是最早尝试新产品的,他们愿意冒风险且对失败有较高的容忍度。最后采纳的则是那些对变化持反感态度的滞后者。著名的钟形曲线图展示了每个类别中采纳者的百分比,而累积采纳图类似于创新市场份额随时间变化的熟悉的“s 曲线”模式。

作者提供的图像(修改自 来源

基本思想是,每个群体都受到前面群体的信号和行为的影响,依赖于社会证明来决定是否采纳新产品。这是一个被广泛理解和实证记录的现象,从窗式空调到 iPhone 都有所观察。

“裂谷”是杰弗里·A·摩尔在其著作“跨越裂谷”中推广的一个概念,该理论基于罗杰斯的理论。摩尔认为早期市场和主流市场之间的差异过大,大多数产品在尝试跨越这一“裂谷”时失败,这在科技初创公司中是相当常见的失败模式。

作者提供的图像(修改自 来源

尽管罗杰斯批评了鸿沟的概念,称创新的扩散是一个“社会过程”,并且“没有相邻采纳类别之间的明显中断或断层”,但显而易见的是,许多产品未能达到主流市场,因为它们从未超越创新者群体。

摩尔提供了几种弥合鸿沟的建议,我只部分同意。根据他自己的说法,他的书主要将鸿沟视为“市场发展问题”,并专注于“穿越鸿沟的市场策略和战术”。他确实涉及了“整体产品管理”的概念,但根据他对西奥多·列维特的《市场营销想象力》的解读,这一概念仅限于通过“服务和附加产品”弥合市场信息和产品真实性之间的差距。他并未讨论产品的实际演变。实际上,创新(即核心产品)被视为一个常量。

考虑到软件(特别是开发者工具)的具体属性,我提出了两种策略(“进化”和“跳过”)来避免鸿沟,并假设它们的应用如何助推了 LLMs 的快速崛起。

避免鸿沟的两种以产品为中心的方法

随着时间的推移,进化(简化)你的开发者工具

产品是常量的限制,而“整体产品”的所有其他方面(如信息传递、分销、定价)随着不同采纳群体的变化而改变,这主要受到物理产品的影响。如果你从事生产和销售小部件的业务,更改供应链或重新配置工厂并非易事。然而,这对于纯软件产品而言完全是另一回事。不进化你的软件产品几乎总是失败的秘诀。

从大多数软件初创公司的起步方式来看,进化的必要性应当显而易见。开发者工具(特别是在 AI 领域)通常是在特定领域的专家用户基础中诞生和成长的。令人不惊讶的是,这些早期用户通常是创新者,因此并不代表更广泛的市场。创始人很容易将所有时间和精力投入到这一细分市场,根据他们的反馈调整产品。不幸的是,商业成功很少出现在这些早期群体中。创新者非常成熟,通常更倾向于自建而非购买。即便他们决定购买,他们也不会代表足够大的市场。

解决这个问题的一种方法是随着时间的推移逐步演变产品以适应不同的目标受众。对于设计良好的开发工具,这意味着引入新的抽象层和/或支持更广泛使用的语言。以我之前的雇主为例,Spark 的持续成功(至少在我看来)部分由于产品界面不断被简化,以吸引更广泛的用户(敢说是早期多数人?)。Spark 最初以 RDD(弹性分布式数据集)和 Scala 作为主要编程语言开始。然后它将语言支持扩展到 Python,通过 PySpark(吸引了更多的软件工程师)并引入了更简单的 API,如 DataFrame,以及 SparkSQL(吸引了 SQL 分析师)。最近,Spark 添加了与 Pandas 兼容的 API(吸引了数据科学家),甚至引入了一个“英语 SDK”使用 LLMs(吸引了,嗯,任何懂英语的人)。如果 Spark 没有以这种方式演变,它将停留在那些懂得用 Scala 编写复杂 MapReduce 程序的创新者领域。

作者提供的图片

这种策略看似有些显而易见,但并不是许多技术产品(尤其是在开发工具中)都能做到这一点。它们有时通过去除一些控制项来“简化”产品,但却未能引入新的、不会泄漏的抽象层。

完全跳过鸿沟

另一种方法,在开发工具中不太常见,是完全跳过鸿沟。这个想法 deceptively 简单:如果在早期市场上的成功不会自动转化为主流市场上的成功,为什么不直接瞄准早期多数人呢?

如前所述,这在硬件中尤为重要,因为产品迭代较慢、成本较高,因此核心产品无法轻易演变。iPhone 是一个很好的例子,它经常被创新者批评(即使是最近的 iPhone 15 和其“令人失望”的 USB-C 端口),但却在对技术细节不太关心的早期多数人中取得了迅速的成功。事实上,苹果公司通过他们的宣传不断向行业展示这种策略的典范。最著名的例子可能就是“你口袋里的 1,000 首歌”广告,这个广告针对的是早期多数人,而不是那些关心技术规格的创新者。

作者提供的图片

对许多科技初创公司(尤其是那些专注于开发者工具的公司)来说,这似乎不太自然,因为与创新者和早期采用者取得早期成功实在是太容易了。AI 开发者工具几乎在定义上就进入了早期市场,因为它们通常是由先进的 AI 研究人员或 ML 工程师开发的。“通过开源项目证明产品与市场契合度”的做法只会进一步强化这一点。

开源项目商业化的常见失败策略

我已经见过足够多的“开源项目转型初创公司”,对此有了一定程度的“模式识别”能力,以识别常见的失败模式。这些初创公司在通过 GitHub stars 或 PyPI 下载量来衡量的增长采纳中获得早期成功(和资金)。然后,他们悲剧性地遵循类似的路径,有时即使有一位“有过成功经验”的创始人(因为他们实际上并不理解他们以前的公司为何成功)。

作者提供的图片

向创新者推销:直观地(或幼稚地?),大多数初创公司首先尝试向创新者推销开源产品的“托管”版本。这种策略通常会失败,因为早期的创新者定义上是非常复杂的,并且倾向于自己构建而不是购买。通用的 3S 策略(托管 OSS + 稳定性、可扩展性、安全性)不足以让这一人群愿意掏钱,因为他们已经知道如何自己构建和运行服务。创新者也害怕“供应商锁定”以及失去独立创新的能力。

产品市场不匹配:下一个尝试是将相同的“托管 OSS”产品销售给早期大多数用户。这通常失败,因为核心产品仍然是为创新者优化的难以使用的产品。仅仅添加 3S 不足以激励早期大多数用户提升技能(就像计划培训数百万 ML 工程师以强迫 MLOps 市场存在一样)。如果这还不够,最后一根稻草是没有人能在这个游戏中击败 AWS(这也是越来越多基础设施开源项目转向非商业许可证的原因)。

“完整产品”:我讽刺地称这种策略为“完整产品”,因为这个术语被误用来通过次优手段填补基本的产品缺口。这种尝试通常是在意识到核心产品对更大市场来说使用起来太困难之后发生的,解决方案通常涉及“用人力解决问题”。这导致初创公司的收入结构中包含了较高的服务成分(这是任何投资者都不喜欢看到的)以及臃肿的交付组织。公平地说,某种程度上这是必要的,尤其是在企业领域或政府部门。但更常见的是,初创公司开始看起来像一家技术咨询公司。

开发工具的混合方法

我所提出的策略是一种混合方法,它仍然允许在创新者的专注用户基础上快速迭代,但通过明确在产品定义中关注早期大众,承认早期市场和主流市场的根本差异。

通过开源证明与创新者的早期成功,并不一定与找到早期大众的商业可行性相悖,只要你认识到他们需要不同的产品。具体来说,我建议:

  1. 使用你的开源项目来获得创新者的关注

  2. 利用这种受欢迎度来筹集资金

  3. 利用创新者组来了解他们如何创造下游价值以及为谁创造价值

  4. 针对那个受众群体设计你的主流产品

这就是软件的创新扩散与像 iPhone 这样的消费硬件不同之处:关键的见解是,在软件价值链中,创新者通常是早期大众的中介。换句话说,创新者本身不是价值链的终点。他们消费技术以帮助产品/业务团队创造价值。有时这会采取“卓越中心”或集中“创新团队”的形式。科技初创公司应该学习在那些创新者之后谁在价值链上,这就是他们找到早期大众的关键。关键的是,我并不是说你应该尝试在存在的组织中排除那些创新者,因为这通常会导致政治上的反弹。在这些情况下,你需要让他们成为你的“倡导者”。

科技初创公司应该学习在那些创新者之后谁在价值链上,这就是他们找到早期大众的关键。

“跳跃”策略的主要含义是在产品定义过程中明确决定针对早期大众。请注意,这与“演变”策略不同,因为“主流产品”可能不仅仅是原始产品的简化版本,而可能会采取完全不同的形态。这种不同形态的两个极端是:

  1. 比原始开源项目更高的抽象层次,以不同的形式存在。 尽管不完美,Databricks 提供了另一个例子。引起创新者组之外初步兴趣的突破性产品不仅仅是“托管的 Spark”,而是一个面向数据科学家和工程师的托管 Notebook 产品(在那个时候相当新颖)。Databricks 今天仍然遵循相同的策略,推出了像 Databricks SQL 这样的产品。

  2. 在价值链中更高的、垂直化的、更专注的产品。 Stripe 就是一个很好的例子,他们最初从一个开源支付处理库开始,然后通过像 Checkout(网站的完整支付表单)或 Terminal(销售点结账终端)这样的产品取得成功。

MLOps 如何未能演变而 LLM 跳过了鸿沟

MLOps 困在早期市场

关于 Spark 演变的类似故事,不能套用到 ML 上。MLOps 的堆栈与几年前基本相同,市场的希望在于越来越多的工程师将学会使用它。

不再回忆我们如何到达这里,我只是简要总结一下我对 MLOps 市场现状的看法:

  • MLOps 市场尚未形成“主导设计”,因此,你会发现每一个“MLOps 平台”在明显和微妙的方面都各不相同。

  • 从系统层面来看,MLOps 市场没有产生更简单的“形式因素”或抽象层级,因此仍然过于复杂,需要几个专业角色(数据工程师、数据科学家、ML 工程师等),这些角色仅在最先进的科技公司中普遍存在。

  • 能够使用这项技术的观众,即创新者和早期采纳者,更愿意保持前沿,使用开源工具而非支付供应商费用。

  • 通常,愿意支付供应商费用的观众只是默认选择主要云服务提供商的产品。云提供商免费提供“ML 平台”层,并满足于通过存储和计算来收取收入。

  • 由于云服务供应商尚未明确货币化 MLOps,这一市场的价值捕捉极其有限。

总之,MLOps 已经陷入了鸿沟,且没有迹象显示它会在另一侧重新出现。

LLM 的早期多数吸引

进入 LLM。OpenAI 据说已经超过了 13 亿美元的年度经常性收入,并预计将继续以快速的速度增长,这一点 MLOps 初创公司无法做到。实际上,你可能将前 10 名 MLOps 初创公司的收入加起来也未必接近。请记住,大多数云提供商实际上并不货币化这一层,除非你想从事以商品价格提供云基础设施的业务,否则它们的“ML”收入并不算数。

这引出了一个问题:为什么 LLM 能如此迅速地获得主流成功?我认为它们成功地跳过了两个截然不同的领域的鸿沟。

跳过开发者领域的鸿沟

传统的“判别型”机器学习模型则针对非常具体的任务进行训练,比如预测销售线索的质量或对产品列表进行排序。对于每个任务,几个专家必须合作编写数据管道、收集标签、优化所谓的“特征”、训练和微调模型、评估它们、部署它们、监控其性能,然后定期重新训练它们。无论是对每个任务重复这一过程的需求,还是完成它所需的专业知识,意味着这种奇迹仅为少数人所能实现。

另一方面,“生成型”语言模型对于各种用例“直接有效”,使得任何能够进行 API 调用的人都可以将 AI 应用于他们的产品或问题。几乎一夜之间,LLMs 通过赋予每个软件工程师 AI 超能力解决了“应用 AI”领域的人才短缺问题。关键是,同一个 LLM 可以生成诗歌、编写代码、将自然语言问题转换为 SQL 查询,或通过各种标准化测试。这要么是“开箱即用”(零样本),要么是通过给模型提供一些你想解决的问题的示例(少样本),并扩展到各种模态,而不仅仅是文本。

图片由作者提供

这正是跳过鸿沟直达早期多数群体的定义。

这也是为什么 LLMOps 注定要重蹈历史的原因。我想如果你有一把锤子,一切看起来都像钉子。不可避免地,围绕每个人都需要训练和微调自己的 LLM 的想法出现了一个小行业,这完全忽视了 LLMs 为什么一开始就如此成功的根本原因。重新增加编写数据管道、训练和微调自己的模型、部署等复杂性,会让你回到那些喜欢建造而不是购买的创新者微市场中。

请注意,我并不是说没有人应该微调和部署自己的 LLM。在某些非常具体的情况下(这些情况很少),这样做是有意义的。但在几乎所有这些情况下,你会发现自己处于创新者和早期采纳者群体中,这些群体只会使用开源工具,而不会向供应商支付费用。

跳过消费者领域的鸿沟

OpenAI 涉足于两个截然不同的领域:如上所述的开发者领域通过 API 和专用计算能力服务。而 ChatGPT 及其移动应用则非常属于“消费者”产品。ChatGPT 以在5 days内达到 1M 用户的速度而闻名,虽然没有 OpenAI 收入数据的官方细分,但有估计认为来自移动应用的收入为每月 $3M。这听起来不像是一个缓慢增长的早期市场产品,对吧?

尽管名字听起来有些术语化(GPT 代表生成预训练变换器),但 ChatGPT 直接跳过了早期大多数用户,主要是由于其友好且易于使用的形式。无论是记者、教师还是学生,都可以免费访问并立即体验其价值。如果 OpenAI 仅仅发布了一个工程师可以通过 REST API 调用的模型,可能不会导致主流的广泛采用。

大多数高管会告诉你,将焦点分散在两个截然不同的领域通常不是个好主意。然而,我认为 ChatGPT 在消费者中的广泛成功对开发者领域的需求推动起到了重要作用。事实证明,开发者和企业买家也是人。他们阅读新闻,关注趋势,并尝试消费产品。OpenAI,无论是有意还是无意,都在这方面受益匪浅。

  • 最明显的是,意识和品牌认知对任何业务都至关重要。虽然 OpenAI 和 LLMs 在 AI 圈子里已经相当有名,但 ChatGPT 使其成为了更广泛开发者群体中的一个品牌名称。

  • “鸿沟”存在的原因之一是早期大多数用户通常是风险规避型的,不信任来自创新者的信号。克服这一点的一种方法是为他们提供一种简单的方式来体验产品。ChatGPT 为早期大多数用户中的非技术决策者提供了完美的“免费试用”体验。

  • 像 OpenAI 这样实现收入增长的“典型”方式是聘请企业销售团队。事实证明,传统的销售主导增长(SLG)模式从经过验证的产品主导增长(PLG)方法中受益匪浅,例如无缝访问产品体验。企业买家越来越期望“在承诺大合同之前先看到并体验产品的价值”。

结论

我开始撰写这篇文章,作为我关于开发者工具前文的自然续集,因为我看到 MLOps 的故事在 LLMOps 中重演。但正如我写到 LLMs 如何跳过鸿沟,我意识到这些经验可能更广泛适用。

对于像 OpenAI、Anthropic 等 LLM 提供商:我不确定这些公司是否偶然发现了这种策略,但如果有意应用,确实有很多关于如何改进产品开发和市场推广的经验。然而,如果你处于快速增长阶段,优化的时间和需求非常有限。

对于 LLMOps 生态系统中的任何人:我邀请你阅读我之前关于 ML 基础设施的文章,你会看到为什么我认为在这一层面上提取的价值不多。此外,我认为很少有情况真正需要微调 LLM,但其他人已经 写过 很多

对于科技初创公司:我见过在基于开源的初创公司中进行的大规模融资轮次,其中假设要么是“管理型 OSS + 扩展、稳定性和安全性”,要么是“开放核心,稍后再考虑货币化”。我认为“跨越鸿沟”的想法在这里是有价值的,期待来自创始人和投资者的反馈!

本文表达的观点仅代表我个人,不代表我雇主的观点。

克莱门斯 是一位创业型产品领导者,过去 8 年多来致力于将 AI 带给开发者和企业。

跨越桥梁:学术界与工业界数据科学的比较

原文:towardsdatascience.com/crossing-the-bridge-a-comparison-of-data-science-in-academia-and-industry-f9c4cb3fda92

博士生对学术数据科学与工业数据科学之间惊人相似性的探索

Nazlı AlagözTowards Data Science Nazlı Alagöz

·发表于Towards Data Science ·阅读时长 8 分钟·2023 年 5 月 29 日

--

照片由Campaign Creators拍摄,刊登在Unsplash上。

作为一名决定离开学术界进入工业界的在读博士生,我花了大量时间探索我的学术经验如何转化为工业环境中的应用。最初,这两个领域似乎处于光谱的两端。然而,在经过大量研究和与许多从学术界转向工业界的人士的讨论后,我发现两者之间的相似之处比我预期的要多。在这篇博客文章中,我将分享关于学术数据科学与工业数据科学之间的差异和共性。

我应当指出,由于缺乏第一手的行业经验,我对行业和学术界的比较依赖于那些已经经历过这一过程的人的见解。此外,我是一名定量营销领域的博士候选人,因此对经济学和商业领域的学术研究更为熟悉。因此,当我进行比较时,我将行业实践与这些领域的学术研究过程进行对比。

对于那些不熟悉学术研究过程以及数据科学在这一背景下应用的人,我将数据科学定义为通过科学方法和算法从数据中提取洞察的过程。例如,我主要使用因果推断和机器学习方法来回答我论文项目中的研究问题。接下来,我将概述定量营销领域的学术研究。

在学术界,我们需要找到值得回答的相关问题(例如,付费墙如何影响收入,某些活动如何影响销售)。然后,我们收集回答这些问题所需的数据(例如,通过与在线报纸合作、网页抓取、使用 API 或采购数据)。一旦获得数据,我们可以开始准备数据以进行分析,并使用数据来检验我们的假设。当我们有了一些初步结果时,我们通过演示和撰写论文来沟通这些结果以获取反馈。我们更新分析、演示和论文,以解决收到的反馈。这一过程(即,获取反馈,解决反馈)会重复进行,直到我们达到一个通过科学标准的高质量项目/论文。

现在我们理解了数据科学如何支撑学术研究过程,让我们探讨一些在比较学术界与工业界的数据科学时值得讨论的关键方面。

目标与影响定义

数据科学在学术界和工业界的目标虽然都很重要,但焦点不同,因此影响的定义也有所不同。在学术界,主要目标往往是知识的进步。学者们旨在填补当前理解中的空白,挑战现有理论,或开发新的框架、方法或工具。这里的影响主要通过对学术界的影响来衡量,如引用、领域的进展和对教学与学习的贡献。

在工业界,数据科学的目标通常更直接地与业务目标相关,如改善运营、优化产品或服务,以及辅助战略决策。影响通常通过可衡量的结果来评估,如提升用户体验、增加收入、节省成本或其他与业务相关的关键绩效指标。这并不意味着工业界的工作纯粹是交易性的。许多工业项目也有助于更广泛的社会目标,如改善医疗保健、推进可持续实践或增强数据隐私和安全。

在这两个领域中,终极目标是创造价值,但价值的定义和衡量方式可能会有所不同。认识到这些差异可以帮助我们欣赏数据科学在学术界和工业界的独特贡献。

Dan Dimmock 拍摄,来源于 Unsplash

研究问题的制定与数据获取

在学术界,研究问题的创建和数据收集的任务通常是学者角色的内在组成部分。发展和回答问题的动力来自于知识的空白、现有知识的不足以及为我们的集体理解提供新见解的潜力。这个过程可能非常繁琐和耗时,因为它可能涉及广泛的文献综述、实验、调查或实地工作。

相比之下,在行业环境中,研究问题和数据的来源通常呈现不同的形式。问题通常来源于特定的业务需求,以提供增强操作、产品或服务的见解。行业中的数据科学家通常使用现成的数据——这些数据是业务活动的副产品或从第三方提供商处获得的。

然而,这些并不是严格的规则,两者之间可能存在相当大的差异。在学术界和行业中,提出有意义的问题并理解数据的质量和适用性是数据科学家至关重要的技能。

项目的时间表和范围

学术和行业项目都有其独特的时间表和范围,这些都与它们的具体目标相符。学术研究通常遵循较长的时间表,原因在于数据收集的细致性、严格的分析(例如,稳健性检查)、同行评审和学术出版过程。作为学者,我们的目的是通过经过严格验证的结果来丰富科学知识库,这一过程自然需要时间。

然而,在行业环境中,时间表往往较短,以响应市场和业务需求的动态变化。这并不意味着质量会受到妥协。相反,行业也遵循严格的质量控制流程,以确保结果的可靠性。节奏由迅速提供可操作的见解以保持或获得竞争优势的目标驱动。这种敏捷性使公司能够迅速适应和创新,这在快速变化的商业环境中是一种优势。

批判性和科学思维

批判性和科学思维在学术界和数据科学中都非常重要。例如,假设检验是学术界科学过程的核心,也是行业数据科学因果推断轨道(例如实验)的核心。提出良好问题、验证答案和批判性地评估论点的能力在两个领域都受到重视。

沟通与合作

在两个领域中,沟通的核心技能取决于听众。在学术界,我们与同行研究人员、教授、学生或来自工业界的利益相关者沟通,而在工业界,数据科学家与包括其他数据科学家、经理、高管或客户在内的更广泛利益相关者沟通。能够将复杂的想法简化为非专业群体易于理解的内容是一个共同的要求。

有一种误解认为博士生由于其深度专业化而缺乏沟通能力,可能难以将复杂的想法用简单易懂的语言向非专业人士或商业利益相关者解释。然而,这种说法是根本不真实的。学术界的成功依赖于有效地与不同听众沟通的能力。例如,我经常需要与来自不同背景的人进行沟通,如我领域的研究人员、其他领域的研究人员、学生以及来自工业界的人。对于这些群体中的每一个或这些群体的某些组合,我的沟通方式有所不同。

照片由 Irvan Smith 拍摄,出处 Unsplash

数据处理和良好的编码实践

随着大数据的兴起,无论是学术界还是工业界的研究人员都在处理大量数据集和复杂的数据管道。尽管使用的具体工具可能有所不同,但良好的编码实践、严格的文档编写和高效的数据管理原则在工业界和学术界都是普遍适用的。在学术界,这些原则构成了可重复和开放科学的基础。因此,许多研究人员实施这些原则,以使科学更加透明、可及、协作和可重复。

项目管理

无论在学术界还是工业界,有效的项目管理和优先级排序技能都是至关重要的,尽管两者的看法有所不同。学术界认为较长的时间线会减少项目管理的重要性,实际上这种观点是一个误解。在学术界,学者们通常需要平衡多种责任,包括教学、指导和管理,同时还要处理复杂的研究项目。研究的每个阶段都需要细致的规划和高效的任务管理,强调了强大项目管理技能的必要性。

同样地,在工业界,仔细的规划、协调和执行在数据科学项目的所有阶段都至关重要。行业的快速变化、多个同时进行的项目以及对快速、可操作的见解的需求,进一步强调了有效项目管理的重要性。尽管时间线和背景有所不同,但两个领域对成功项目管理的需求都是至关重要的,这不仅确保了项目按时完成,还保证了工作质量和资源效率。

软技能

无论是你打算发表论文还是交付一个成功的商业项目,独立性、自律、有效的协作和强大的组织能力都是至关重要的。这些技能可能是我在博士阶段学到的最重要的东西之一。能够独立工作,同时又能进行协作,这一点非常重要。

对于我的项目,我需要能够独立提出想法、解决问题和完成其他任务,同时,我们需要与我的合作者(共同作者)保持一致。我认识到有效沟通的重要性,特别是当你的合作者来自不同背景和文化时。另一个有效协作的重要技能是接受、欣赏并实施反馈。这些技能在工业角色中也同样至关重要。

学习和适应能力

学术界和工业界都要求持续的学习和适应能力,以跟上不断发展的方法和技术。在学术界,就像在工业界一样,你不能忽视任何新的有用工具,否则会失去你的竞争优势。

比如,当我在做我的第一个博士项目时,我使用了一种在学术界和工业界都常用的严格统计方法。然而,许多新的研究表明,在某些情况下这种方法的表现并不好。我最终自学了这些问题出现的原因和潜在的新解决方案。

技术技能

数据科学家在学术界和工业界都必须掌握各种技术工具。像 Python 和 R 这样的语言在这两个领域的数据科学任务中都很普遍。然而,像 SQL、Spark 和 Tableau 这样的工具在工业界可能会比在学术界更频繁地使用。

结论

我对数据科学在学术界和工业界交集的探索令人开眼界,揭示了比我最初预期的更多相似之处。许多学术研究人员发现,他们的背景为进入工业界的角色提供了坚实的基础。他们的教育和技术培训使他们能够轻松掌握新方法,同时,他们在沟通、解决问题、独立研究和协作方面的经验为他们在工业界应用这些技能提供了强大的能力。

感谢你的阅读!

如果你喜欢这篇文章并希望看到更多我的文章,请考虑 关注我

免责声明:我写作是为了学习,所以你可能会发现文章或代码中有错误。如果你发现了,请告诉我。

CRPS — 贝叶斯机器学习模型的评分函数

原文:towardsdatascience.com/crps-a-scoring-function-for-bayesian-machine-learning-models-dd55a7a337a8?source=collection_archive---------1-----------------------#2023-01-28

连续排名概率分数是一种统计指标,用于比较分布预测与真实值的差异

Itamar FaranTowards Data Science Itamar Faran

·

关注 发表在 Towards Data Science ·5 分钟阅读·2023 年 1 月 28 日

--

机器学习工作流中的一个重要部分是模型评估。这个过程本身可以被认为是常识:将数据分为训练集和测试集,在训练集上训练模型,并使用评分函数在测试集上评估其性能。

评分函数(或度量)是将真实值及其预测映射到一个单一且可比较的值[1]。例如,对于连续预测,可以使用如 RMSE、MAE、MAPE 或 R 平方等评分函数。但如果预测不是逐点估计,而是一个分布呢?

在贝叶斯机器学习中,预测通常不是逐点估计,而是值的分布。例如,预测可能是分布的估计参数,或者在非参数情况下——来自 MCMC 方法的样本数组。

在这些情况下,传统的评分函数不适合统计设计;可以将预测分布聚合成它们的均值或中位数,但这会导致对预测分布的离散性和形状的信息损失。

连续排名概率评分

CRPS——连续排名概率评分——是一个评分函数,用于将单一真实值与累积分布函数(CDF)进行比较:

CRPS 的定义[1]。图片由作者提供。

首次引入于 70 年代[4],主要用于天气预报,如今在文献和工业界重新获得关注[1][6]。当目标变量是连续的且模型预测目标的分布时,它可以作为评估模型性能的度量;例如,贝叶斯回归或贝叶斯时间序列模型[5]。

理论定义中包含 CDF 使得 CRPS 对参数化和非参数化预测都很有用:对于许多分布,CRPS 有一个解析表达式[3],而对于非参数预测,可以使用带经验累积分布函数(eCDF)的 CRPS。

在计算完测试集中每个观测值的 CRPS 后,我们需要将结果聚合成一个单一值。类似于 RMSE 和 MAE,我们将使用(可能加权的)平均值来聚合它们。

在测试集上对 CRPS 的聚合,以及经验 CDFs。图片由作者提供。

直观理解

比较单一值与分布的主要挑战是如何将单一值转换到分布的领域。CRPS 通过将真实值转换为带有指示函数的退化分布来解决这个问题。例如,如果我们的真实值是 7,我们可以这样转换:

带有指示函数的退化分布示例。图片由作者提供。

指示函数是一个有效的 CDF,符合所有 CDF 的要求。现在我们需要将预测分布与真实值的退化分布进行比较。显然,我们希望预测分布尽可能接近真实值;这通过测量这两个 CDF 之间的(平方)面积来在数学上表示:

CRPS 的可视化。预测分布用红色标记,真实值的退化分布用蓝色标记。CRPS 是两个 CDF 之间的(平方)面积。图片来源于作者。

与 MAE 的关系

CRPS 与著名的 MAE(平均绝对误差)密切相关。如果我们将点预测视为退化的 CDF 并将其代入 CRPS 方程,我们得到:

CRPS 和 MAE 之间的关系。图片来源于作者。

因此,如果预测分布是退化分布(例如,点预测),CRPS 就会简化为 MAE。这有助于获得对 CRPS 的另一种直观理解:它可以被视为 MAE 向分布预测的推广当预测分布是退化的,MAE 是 CRPS 的特例

实证评估

当模型的预测是一个参数分布(例如,模型预测分布的参数)时,对于一些常见分布,CRPS 有一个解析表达式 [3]。例如,如果模型预测正态分布的参数μσ,CRPS 可以通过以下公式计算:

正态分布 CRPS 的解析解 [3]。图片来源于作者。

已知 Beta、Gamma、Logistic、Log-Normal 等分布的解析解 [3]。

当预测是非参数化的,或者更具体地说——预测是模拟数组时,计算 eCDF 的积分是一个繁重的任务。然而,CRPS 也可以通过以下公式进行解析表达:

CRPS 的不同形式及其名称 [2]。图片来源于作者。

其中 X, X’ 是根据 F 独立同分布的。这些表达式虽然仍然计算量较大,但估计起来更简单:

根据 NRG 形式 [2] 实现的 CRPS 函数。改编自 pytorch 到 numpy,来源于 pyro-ppl,Uber Technologies © [6]

根据 PWM 形式 [2] 实现的 CRPS 函数。

你可以在 Jupyter notebook 这里 查看一个贝叶斯岭回归的示例,我在其中演示了参数和非参数 CRPS 的使用。

摘要

连续排名概率分数(CRPS)是一种评分函数,用于将单一的真实值与其预测分布进行比较。这一特性使其与贝叶斯机器学习相关,因为模型通常输出分布预测而不是点估计。它可以被视为对分布预测的著名 MAE 的一种推广。

它具有参数预测的解析表达式,并且对于非参数预测可以简单计算。总的来说,CRPS 成为评估具有连续目标的贝叶斯机器学习模型性能的新标准方法。

参考文献

  1. 严格正当的评分规则、预测与估计,Gneiting & Raftery(2007)

  2. 有限信息下的连续排名概率分数估计及其在集合天气预报中的应用,Zamo & Naveau(2017)

  3. 使用分位回归森林和集合模型输出统计量进行校准的集合预报,Taillardat、Zamo & Naveau(2016)

  4. 连续概率分布的评分规则,Matheson & Winklers(1976)

  5. 分布回归及其与 CRPS 的评估:最小最大风险的界限和收敛,Pic、Dombry、Naveau & Taillardat(2022)

  6. Pyro-PPL 中的 CRPS 实现,优步科技公司

  7. properscoring 中的 CRPS 实现,气候公司

与 Pinecone 的 CRUD 操作

原文:towardsdatascience.com/crud-with-pinecone-ee6b6f8b54e8

向量数据库

从传统数据库的角度入门向量数据库的简易指南

Manfye GohTowards Data Science Manfye Goh

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

--

图片来源于 Pexels 的 Brett Sayles

机器学习应用的快速增长和人工智能的进步推动了对专门数据存储解决方案的需求。

向量数据库由于能够执行高效的相似性搜索并支持复杂的数据结构,已经成为处理大规模高维数据的热门选择。Pinecone 最近在开发者和数据科学家中越来越受欢迎,作为一种可扩展且高效的向量数据库解决方案。

我发现有很多人教授如何使用 Pinecone,但没有人从传统数据库的角度描述它,比如如何与传统 SQL 数据库进行比较。

在本文中,我们将从传统数据库的角度清晰地理解 Pinecone 中的 CRUD(创建、读取、更新和删除)操作。我们将深入探讨向量数据库与传统数据库之间的差异,探讨如何利用向量数据库来优化现代应用中的数据管理。

本文的代码可在这里获取

什么是向量数据库?

向量数据库是一种专门设计用于存储、管理和查询以向量表示的高维数据的数据库。这些数据库在需要高效相似性搜索的应用中非常有用,例如推荐系统、图像和文本搜索引擎,以及自然语言处理任务。

在向量数据库中,数据点表示为高维空间中的向量,它们之间的关系通过距离度量(如欧氏距离、余弦相似度或曼哈顿距离)来衡量。通过索引这些向量和优化搜索算法,向量数据库可以迅速执行相似性搜索,即使在庞大的数据集中。

与关注关系型或基于文档的存储的传统数据库不同,向量数据库强调数据点之间空间关系的重要性。这种独特的关注使得向量数据库能够在需要快速识别数据集内相似项的应用中提供高性能、准确的搜索结果。

要开始使用,这里有一个我们如何与向量数据库互动的示意图:

传统数据库与向量数据库的操作模式。图片由作者提供

开始使用 Pinecone

在你获得对 Pinecone 的访问权限后,使用以下设置创建新的索引:

创建新的索引。图片由作者提供

指定你的索引名称和所需的维度。在我的例子中,我将使用“manfye-test”以及维度为 300 的索引。点击“创建索引”,索引将如下所示:

创建的索引。图片由作者提供

索引就像 SQL 中的表格,你可以像在 SQL 中一样在索引中进行 CRUD 操作。

在开始我们的 CRUD 操作之前,让我们收集所有必需的资源:

安装所需的软件包,其中包括 pinecone-client(允许你与 pinecone 互动)和 sentence_transformers(帮助你对数据进行向量化):

!pip install pinecone-client
!pip install sentence_transformers

你可以通过 Pinecone 仪表盘中的“API KEY”标签获取你的 Pinecone API 密钥环境名称

获取 API 密钥和环境名称。图片由作者提供

使用 Pinecone 索引

在继续之前,你需要了解几个处理功能,就像设置 SQL 连接一样:

a) 连接到 Pinecone 服务器和索引

import itertools
import pinecone

#Connecting to Pinecone Server
api_key = "YOUR_API_KEY"

pinecone.init(api_key=api_key, environment='YOUR_ENV_IN_DASHBOARD')

#Connect to your indexes
index_name = "manfye-test"

index = pinecone.Index(index_name=index_name)

b) 探索你的索引

# Getting Index Details
pinecone.describe_index(index_name)

# Return:
# IndexDescription(name='manfye-test', metric='cosine', replicas=1, dimension=300.0, shards=1, pods=1, pod_type='s1.x1', status={'ready': True, 'state': 'Ready'}, metadata_config=None, source_collection='')

index.describe_index_stats()

# Return:
# {'dimension': 300,
# 'index_fullness': 0.0,
# 'namespaces': {},
# 'total_vector_count': 0}

describe_index_stats 在你想了解索引中有多少数据时非常有用。

数据集准备

首先,我们将生成如下的投诉数据集,这将是我们操作的主要数据:

import pandas as pd

data = {
    'ticketno': [1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010],
    'complains': [
        'Broken navigation button on the website',
        'Incorrect pricing displayed for a product',
        'Unable to reset password',
        'App crashes on the latest iOS update',
        'Payment processing error during checkout',
        'Wrong product delivered',
        'Delayed response from customer support',
        'Excessive delivery time for an order',
        'Difficulty in finding a specific product',
        'Error in applying a discount coupon'
    ]
}

df = pd.DataFrame(data)

C — 在索引中创建数据

为了在向量数据库中创建数据,我们首先需要通过一种称为向量嵌入的技术将数据转换为向量。向量嵌入有多种方法,其中一种著名的方法是使用 OpenAI 嵌入 API。

然而,为了不使本文变得复杂,我们将使用 SentenceTransformer 包来进行嵌入。该包会自动下载所需的模型“average_word_embeddings_glove.6B.300d

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("average_word_embeddings_glove.6B.300d")

df["question_vector"] = df.complains.apply(lambda x: model.encode(str(x)).tolist())

代码将创建一个“question_vector”列,其中包含嵌入向量。请注意,所有的单词都被转换成了数字。

结果向量。图片由作者提供

最后,通过块上传数据(upsert)到索引中:

def chunks(iterable, batch_size=100):
    it = iter(iterable)
    chunk = tuple(itertools.islice(it, batch_size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it, batch_size))

for batch in chunks([(str(t), v) for t, v in zip(df.ticketno, df.question_vector)]):
    index.upsert(vectors=batch) 

现在用index.describe_index_stats()检查你的索引:

index.describe_index_stats()

# Return:
# {'dimension': 300,
# 'index_fullness': 0.0,
# 'namespaces': {'': {'vector_count': 10}},
# 'total_vector_count': 10} 

请注意,向量数量已增加到 10,恭喜你将数据集上传到向量数据库中。

R — 检索向量

读取向量上下文参考两个函数,第一个是读取函数,你传入数据的 ID,Pinecone 会返回存储的向量:

index.fetch(["1010","1009"])

数据检索很简单,只需使用index.fetch([]),只需输入你要检索的 ID 列表,Pinecone 将返回相应的向量。

第二个操作包括返回与你的查询匹配的相似数据:

query_questions = [
    "navigation button",
]

query_vectors = [model.encode(str(question)).tolist() for question in query_questions]

query_results = index.query(queries=query_vectors, top_k=5, include_values=False)

在上面的代码中,我让 Pinecone 查找“导航按钮”的相似结果,并返回前 5 个最相似的结果(top_k = 5),如下所示:

Pinecone 的查询结果。图片由作者提供

请注意,默认情况下,Pinecone 不会返回值,除非在index.query()参数中指定include_values=True。从上面的结果可以看出,相似度得分和前 5 个相似结果的 ID。

接下来的步骤是将结果转换成表格,并将其合并回主数据库。代码如下:

# Extract matches and scores from the results
matches = []
scores = []
for match in query_results['results'][0]['matches']:
    matches.append(match['id'])
    scores.append(match['score'])

# Create DataFrame with only matches and scores
matches_df = pd.DataFrame({'id': matches, 'score': scores})

# Match the result dataframe to main dataframe
df["ticketno"] = df["ticketno"].astype(str)
matches_df.merge(df,left_on="id",right_on="ticketno")

结果匹配表如下所示,显然,投诉 1001 是关于导航按钮的,相似度得分为 0.81,其余的相似度得分较低,这可能是由于我们数据集的规模:

检索到的相似查询数据框。图片由作者提供

U — 更新向量

要更新现有向量,只需重复创建步骤,并使用更新后的向量。用 upsert 函数覆盖相同 ID 的数据:

 index.upsert(vectors=batch)

D — 删除向量

按 ID 删除:

index.delete(ids=["id-1", "id-2"], namespace='')

要删除所有内容并重新开始:

index.delete(deleteAll='true', namespace="")

Pinecone 的限制和替代方案

虽然 Pinecone 提供了一个易于使用的向量数据库,适合初学者,但重要的是要了解其局限性。免费层使用 p1 Pod,仅允许在 768 维向量中存储约 1,000,000 行数据。对于更大规模的应用程序或更具挑战性的用例,这可能不足够。

此外,Pinecone 的付费计划可能非常昂贵,这可能不适合所有用户。因此,你可能需要在决定付费计划或扩展应用程序之前,考虑其他替代方案,例如本地托管的chromaWeaviate

作者的话

总之,本文提供了从传统数据库的角度理解和执行 Pinecone CRUD 操作的全面指南。

作为作者,我的目标是揭示与矢量数据库交互的过程,并强调矢量数据库独特的功能如何使其成为管理机器学习和 AI 应用中高维数据的强大而高效的解决方案。

通过带你了解创建、读取、更新和删除 Pinecone 索引中的数据的过程,我希望提供了有关如何有效管理和查询矢量数据库数据的宝贵见解。掌握这些知识后,希望你现在能够在自己的项目和应用中充分利用矢量数据库的强大功能。

最后,感谢阅读我的文章。如果你喜欢订阅 Medium 会员,请考虑使用下面的链接。这将为我提供极大的支持,使我能写更多的文章。

## 使用我的推荐链接加入 Medium - Manfye Goh

阅读 Manfye Goh 的每一个故事(以及 Medium 上成千上万其他作家的故事)。你的会员费直接支持…

manfyegoh.medium.com

如果你喜欢我的文章,这里有更多我的文章:

## 使用 ChatGPT 创建美丽的图表

使用 ChatGPT 和 Mermaid.js 创建和设计美丽的图表

medium.com

参考文献

  1. Pinecone 文档

通过 Pandera 培养数据科学中的数据完整性

原文:towardsdatascience.com/cultivating-data-integrity-in-data-science-with-pandera-2289608626cc?source=collection_archive---------9-----------------------#2023-12-22

使用 Pandera 的高级验证技术以促进数据质量和可靠性

Alessandro TomassiniTowards Data Science Alessandro Tomassini

·

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

--

由 DALL-E 生成的图像

欢迎进入数据验证的探索之旅,通过 Pandera 这个在数据科学家工具箱中较少为人知却强大的工具。本教程旨在为那些希望通过强大的验证技术来强化其数据处理管道的人们照亮道路。

Pandera 是一个 Python 库,为 pandas 数据结构提供灵活而表达力强的数据验证。它旨在为数据处理步骤带来更多的严谨性和可靠性,确保你的数据在进行分析或建模之前符合指定的格式、类型和其他约束。

为什么选择 Pandera?

在数据科学的复杂织锦中,数据是基本的线索,确保其质量和一致性至关重要。Pandera 通过严格的验证促进数据的完整性和质量。它不仅仅是检查数据类型或格式;Pandera 扩展其警觉性到更复杂的统计验证,使其成为你数据科学工作中的不可或缺的盟友。具体来说,Pandera 的亮点包括:

  1. 模式强制:确保你的 DataFrame 遵守预定义的模式。

  2. 可定制验证:支持创建复杂的自定义验证规则。

  3. 与 Pandas 的集成:与现有的 pandas 工作流程无缝对接。

创建你的第一个模式

charlesdeluvio 提供的照片,来自 Unsplash

让我们开始安装 Pandera。可以使用 pip 完成:

pip install pandera 

Pandera 中的模式定义了你的 DataFrame 的预期结构、数据类型和约束。我们将从导入必要的库和定义一个简单的模式开始。

import pandas as pd
from pandas import Timestamp
import pandera as pa
from pandera import Column, DataFrameSchema, Check, Index

schema = DataFrameSchema({
    "name": Column(str),
    "age": Column(int, checks=pa.Check.ge(0)),  # age should be non-negative
    "email": Column(str, checks=pa.Check.str_matches(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'))  # email format
})

这个模式指定了我们的 DataFrame 应该有三列:name(字符串)、age(整数,非负)和 email(字符串,匹配电子邮件的正则表达式)。现在,既然我们已经有了模式,让我们验证一下 DataFrame。

# Sample DataFrame
df = pd.DataFrame({
    "name": ["Alice", "Bob", "Charlie"],
    "age": [25, -5, 30],
    "email": ["alice@example.com", "bob@example", "charlie@example.com"]
})

# Validate
validated_df = schema(df)

在这个示例中,Pandera 将引发 SchemaError,因为 Bob 的年龄是负数,这违反了我们的模式。

SchemaError: <Schema Column(name=age, type=DataType(int64))> failed element-wise validator 0:
<Check greater_than_or_equal_to: greater_than_or_equal_to(0)>
failure cases:
   index  failure_case
0      1            -5

Pandera 的一个优势是能够定义自定义验证函数。

@pa.check_input(schema)
def process_data(df: pd.DataFrame) -> pd.DataFrame:
    # Some code to process the DataFrame
    return df

processed_df = process_data(df)

@pa.check_input 装饰器确保输入的 DataFrame 在函数处理之前符合模式。

使用自定义检查进行高级数据验证

Sigmund 提供的照片,来自 Unsplash

现在,让我们深入了解 Pandera 提供的更复杂的验证。基于现有模式,我们可以添加具有各种数据类型和更复杂检查的附加列。我们将引入分类数据、日期时间数据的列,并实施更高级的检查,比如确保唯一值或引用其他列。

# Define the enhanced schema
enhanced_schema = DataFrameSchema(
    columns={
        "name": Column(str),
        "age": Column(int, checks=[Check.ge(0), Check.lt(100)]),
        "email": Column(str, checks=[Check.str_matches(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')]),
        "salary": Column(float, checks=Check.in_range(30000, 150000)),
        "department": Column(str, checks=Check.isin(["HR", "Tech", "Marketing", "Sales"])),
        "start_date": Column(pd.Timestamp, checks=Check(lambda x: x < pd.Timestamp("today"))),
        "performance_score": Column(float, nullable=True)
    },
    index=Index(int, name="employee_id")
)

# Custom check function
def salary_age_relation_check(df: pd.DataFrame) -> pd.DataFrame:
    if not all(df["salary"] / df["age"] < 3000):
        raise ValueError("Salary to age ratio check failed")
    return df

# Function to process and validate data
def process_data(df: pd.DataFrame) -> pd.DataFrame:
    # Apply custom check
    df = salary_age_relation_check(df)

    # Validate DataFrame with Pandera schema
    return enhanced_schema.validate(df)

在这个增强的模式中,我们添加了:

  1. 分类数据:department 列验证特定类别。

  2. 日期时间数据:start_date 列确保日期在过去。

  3. 可空列:performance_score 列可以有缺失值。

  4. 索引验证:定义了一个类型为整数的索引employee_id

  5. 复杂检查:一个自定义函数salary_age_relation_check确保每个部门内薪资与年龄之间的逻辑关系。

  6. 在数据处理函数中实现自定义检查:我们将salary_age_relation_check逻辑直接集成到我们的数据处理函数中。

  7. 使用 Pandera 的validate方法:我们没有使用@pa.check_types装饰器,而是手动使用 Pandera 提供的validate方法来验证 DataFrame。

现在,让我们创建一个示例 DataFrame df_example,它符合我们增强模式的结构和约束,并对其进行验证。

df_example = pd.DataFrame({
    "employee_id": [1, 2, 3],  
    "name": ["Alice", "Bob", "Charlie"],  
    "age": [25, 35, 45],  
    "email": ["alice@example.com", "bob@example.com", "charlie@example.com"],  
    "salary": [50000, 80000, 120000], 
    "department": ["HR", "Tech", "Sales"], 
    "start_date": [Timestamp("2022-01-01"), Timestamp("2021-06-15"), Timestamp("2020-12-20")], 
    "performance_score": [4.5, 3.8, 4.2]  
})

# Make sure the employee_id column is the index
df_example.set_index("employee_id", inplace=True)

# Process and validate data
processed_df = process_data(df_example)

在这里,Pandera 会因为enhanced_schema中的salary列预期的数据类型(float,对应于 pandas/Numpy 类型中的float64)与df_example中实际的数据类型(intint64,在 pandas/Numpy 类型中)不匹配而引发SchemaError

SchemaError: expected series 'salary' to have type float64, got int64

具有统计假设检验的高级数据验证

Daniela Paola Alchapar拍摄,来源于Unsplash

Pandera 可以在验证过程中执行统计假设检验。这个功能对于验证数据分布或变量之间关系的假设特别有用。

假设你想确保数据集中的平均薪资接近某个值,例如 £75,000。可以定义一个自定义检查函数来执行单样本 t 检验,以评估样本的均值(例如数据集中薪资的均值)是否与已知均值(在我们的例子中为 £75,000)显著不同。

from scipy.stats import ttest_1samp

# Define the custom check for the salary column
def mean_salary_check(series: pd.Series, expected_mean: float = 75000, alpha: float = 0.05) -> bool:
    stat, p_value = ttest_1samp(series.dropna(), expected_mean)
    return p_value > alpha

salary_check = Check(mean_salary_check, element_wise=False, error="Mean salary check failed")

# Correctly update the checks for the salary column by specifying the column name
enhanced_schema.columns["salary"] = Column(float, checks=[Check.in_range(30000, 150000), salary_check], name="salary")

在上述代码中,我们有:

  1. 定义了自定义检查函数mean_salary_check,它接受一个 pandas Series(我们 DataFrame 中的salary列),并对预期均值进行 t 检验。如果 t 检验的 p 值大于显著性水平(alpha = 0.05),则函数返回True,表示平均薪资与 £75,000 并无显著差异。

  2. 然后,我们将这个函数包装在 Pandera 的Check中,指定element_wise=False以表示检查应用于整个系列,而不是逐个元素地应用。

  3. 最后,我们更新了 Pandera 模式中的salary列,以包括这个新的检查以及任何现有的检查。

通过这些步骤,我们的 Pandera 模式现在包括对salary列的统计测试。我们故意提高df_example中的平均薪资以违反模式的预期,以便 Pandera 引发SchemaError

# Change the salaries to exceede the expected mean of £75,000
df_example["salary"] = df_example["salary"] = [100000.0, 105000.0, 110000.0]
validated_df = enhanced_schema(df_example)
SchemaError: <Schema Column(name=salary, type=DataType(float64))> failed series or dataframe validator 1:
<Check mean_salary_check: Mean salary check failed>

结论

Pandera 将数据验证从一个普通的检查点提升为一个动态过程,涵盖了甚至复杂的统计验证。通过将 Pandera 集成到你的数据处理流程中,你可以早期发现不一致性和错误,节省时间,避免未来的麻烦,并为更可靠和深入的数据分析铺平道路。

参考文献和进一步阅读

对于那些希望深入了解 Pandera 及其功能的人来说,以下资源是很好的起点:

  1. Pandera 文档:全面介绍 Pandera 的所有功能和特性 (Pandera 文档)。

  2. Pandas 文档:由于 Pandera 扩展了 pandas,因此熟悉 pandas 非常重要 (Pandas 文档)。

免责声明

我与 Pandera 没有任何关联,只是对它非常热情 😃

机器学习风险管理的文化能力

原文:towardsdatascience.com/cultural-competencies-for-machine-learning-risk-management-c38616c2ccdf

组织文化是负责任的人工智能的一个重要方面。

Parul PandeyTowards Data Science Parul Pandey

·发表于 Towards Data Science ·阅读时间 8 分钟 ·2023 年 9 月 15 日

--

由 Google DeepMind 提供的照片 在 Pexels 上免费提供

在进步的竞赛中,我们必须小心翼翼,因为工程和数据科学上的匆忙可能会摧毁的不仅仅是代码。

想象一个人工智能(AI)系统可以万无一失的世界,它们能够无故障地完美执行任务。这听起来像是科幻梦,对吗?欢迎来到人工智能的现实世界,事情并不总是如计划般顺利。负责任的人工智能实践的一个核心部分涉及预防和处理我们称之为‘人工智能事件’的情况。本文讨论了可以防止和缓解人工智能事件的文化能力,重点在于推动负责任的人工智能实践。随后,我们将在未来的文章中探讨相关的业务流程,以提供对这一重要话题的全面视角。

关于这一系列的说明

在我们开始这一系列文章时,提供背景信息是重要的。我是‘高风险应用中的机器学习’的共同作者之一,另外两位作者是 Patrick HallJames Curtis。这一系列旨在提供一本简洁、读者友好的书籍内容补充。每篇文章中,我们旨在提炼书中的关键见解、概念和实际策略,将其浓缩成易于消化的部分,使这些知识能够被更广泛的读者所接受。

定义人工智能事件

在深入探讨机器学习安全之前,处理人工智能事件至关重要,因为我们无法有效缓解我们不了解的情况。人工智能事件涵盖了任何可能造成伤害的人工智能系统的结果。这些事件的严重性自然会因造成的损害程度而异。这些事件可能从相对较小的不便,比如购物中心安保机器人跌下楼梯,到更为灾难性的事件,例如无人驾驶汽车导致行人遇难以及大量医疗资源从急需者那里转移

人工智能事件涵盖了任何可能造成伤害的人工智能系统的结果。

我们可以将人工智能事件分为三个主要类别:

人工智能事件分类(作者根据"AI 失败时该怎么办"进行调整,已获许可)

  • 攻击 — 机器学习系统的许多部分,如软件和预测工具,都容易受到网络攻击和内部攻击。一旦发生攻击,我们就会失去控制,攻击者可能有自己的目标,涉及准确性、偏见、隐私、可靠性等方面。研究人员已经广泛记录了这些攻击类别,即保密性、完整性和可用性攻击。

机器学习攻击类型 | 图片由作者提供

  • 失败 — 失败指的是人工智能系统中的问题,通常包括算法偏见、安全和性能缺陷、数据隐私泄露、透明度不足或第三方系统组件的不足。

  • 滥用 — 人工智能工具可能被具有恶意意图的人滥用。黑客经常利用人工智能来增强他们的攻击,例如在自主无人机袭击中。此外,一些政府将人工智能用于种族 Profiling,突显了人工智能技术的广泛滥用。

人工智能事件目录:人工智能事件数据库

人工智能事件可以作为推动公司负责任技术进步的催化剂。在开发机器学习系统时,至关重要的是将当前计划与过去的事件进行交叉检查,以主动防止潜在的未来事件。这与正在进行的努力的主要目标一致,即建立人工智能事件数据库及其相关出版物。一个很好的例子是人工智能事件数据库

incidentdatabase.ai/

根据人工智能事件数据库网站的描述,他们的使命是明确的:

“AI 事件数据库致力于索引人工智能系统部署在现实世界中造成的伤害或近乎伤害的集体历史。像航空和计算机安全领域的类似数据库一样,AI 事件数据库旨在从经验中学习,以便我们可以防止或减轻不良结果。”

这里的基本思想是,像其他领域一样,AI 也可以通过从过去的错误中学习,以避免未来的重复。为了有效实现这一点,保持对这些失败的准确记录是至关重要的。

通过文化能力减轻机器学习风险

组织内部的文化在确保负责任的 AI 实践中发挥着关键作用。本文将主要探讨一些文化策略以实现这一目标。在接下来的文章中,我们还将介绍减轻 AI 相关风险的其他方法,包括业务流程和模型风险管理方面。验证、审计和事件响应团队在开发者之外也是至关重要的。

1. 组织责任

图片由 vectorjuice 提供于 Freepik

在组织中实现负责任的 AI 实践依赖于问责制、文化以及对模型风险管理 (MRM)的遵守(我们将在即将到来的文章中讨论)。如果对机器学习系统的失败、攻击或滥用没有后果,安全性和性能可能会被忽视。MRM 的关键文化原则包括书面政策和程序、独立专家的有效挑战、负责任的领导(如首席模型风险官),以及为负责任的机器学习实施而不是仅仅为了快速开发对齐激励措施。

小型组织可以指定个人或团队负责问责,以防止事件发生并奖励成功的系统。集体责任可能导致无人对机器学习风险和事件负责。

如果机器学习系统导致事件,重要的是有个人或团队承担责任,并在系统运行良好时给予奖励。如果组织假设每个人都对机器学习风险和人工智能事件负责,那么实际上没有人负责——《高风险应用的机器学习》,第一章。

2. 有效变革的文化

图片由 jcomp 提供于 Freepik

强有力的有效变革文化涉及主动质疑和审查机器学习(ML)系统开发中的各个步骤。在更广泛的组织背景下,促进对机器学习系统设计的严肃探究文化至关重要。这种文化增加了开发成功的机器学习系统和产品的可能性,同时也防止问题升级为有害事件。值得注意的是,有效的挑战应始终具有建设性和尊重,适用于所有参与机器学习系统开发的人员。

有效的挑战推动创新并保障机器学习开发的进展

为了有效地实施挑战,需要有结构化的安排。这可能包括定期会议,可能是每周一次,在这些会议上对当前设计决策进行严肃审查,并认真考虑替代设计选择。这种结构化的方法有助于确保有效挑战的文化成为组织机器学习开发过程的核心部分。

3. 多样化且经验丰富的团队

照片由Alexander Grey提供,来源于Unsplash

多样化的团队对于在设计、开发和测试机器学习系统时获得新视角至关重要。许多例子表明,数据科学家忽视机器学习系统的人口多样性会导致不良结果。增加机器学习团队中的人口多样性是解决这些疏忽的一个潜在补救措施。

在多样性中我们发现创新,在专业知识中我们确保安全,两者共同指引负责任的人工智能进步。

忽视领域专家是一个危险的赌博,因为他们带来宝贵的见解,并充当安全网,避免因对领域特定数据或结果的误解而导致潜在灾难。社会科学专家也适用同样的原则。无数实例表明,忽视这些专家的危险,不论是试图自动化需要专业知识的决策还是完全忽视他们的集体智慧

4. 自饮自酿

图像来源于 macrovector,Freepik

"喝自己的香槟" 是在组织内测试我们自己软件或产品的做法,类似于 "吃自己的狗粮"。这是一种预 Alpha 或预 Beta 测试方法,有助于在影响客户或公众之前发现部署复杂性。这种方法对识别像概念漂移、算法歧视、捷径学习和不足规范这些通常逃避标准机器学习开发过程的难题至关重要。

如果它不适合我们的组织,那么它可能还未准备好部署。这是上菜前的试饮。

5. 快速行动与打破常规

图片来源:rawpixel.com,来自 Freepik

在工程和数据科学领域,"快速行动,打破常规"的口号通常占据中心位置。然而,这种方法可能很危险,特别是当它应用于关键的机器学习系统,如自动驾驶汽车、金融、医疗等领域时。即使是一个小故障也可能导致大规模的严重危害。

在进步的竞赛中,我们必须谨慎行事,因为在工程和数据科学中的急功近利可能会摧毁的不仅仅是代码。

我们必须转变思维方式,以避免这些风险。我们应该不仅关注模型的准确性,还要优先了解我们工作的影响和潜在风险。

结论

总结来说,我们对 AI 事件、文化能力和风险缓解策略的探索突显了负责任的 AI 开发的重要性。培养组织内有效挑战和问责制的文化,确保了在识别和纠正潜在陷阱时的积极姿态。此外,整合多样化且经验丰富的团队和严格的内部测试实践,强化了负责任 AI 实施的基础。最后,必须优先考虑全面的风险评估而非急功近利。这些措施共同指引我们走向一个 AI 系统不仅在技术上先进,而且在伦理上可靠的未来,为社会的进步提供有价值的工具。

> 阅读本系列的下一篇文章 >

[## 机器学习风险管理的组织流程

组织流程是机器学习系统可靠性的关键非技术决定因素。

## 跨域桥接:将金融、隐私和软件最佳实践融入机器学习风险管理

理解超越传统模型风险管理的策略

## 跨域桥接:将金融、隐私和软件最佳实践融入机器学习风险管理

参考资料与进一步阅读

维度诅咒:直观探索

原文:towardsdatascience.com/curse-of-dimensionality-an-intuitive-exploration-1fbf155e1411?source=collection_archive---------3-----------------------#2023-12-30

Salih SalihTowards Data Science Salih Salih

·

关注 发表在 Towards Data Science ·11 分钟阅读·2023 年 12 月 30 日

--

照片由 Mathew Schwartz 提供,来源于 Unsplash

介绍

在上一篇文章中,我们讨论了数据在高维空间中的惊人行为。我们发现体积在空间的角落里以一种奇怪的方式累积,我们模拟了一个内切在超立方体中的超球体来研究这一点,观察到随着维度的增加,它们的体积比有趣地减少了。展示多维思维优势的例子有 DVD 纸实验和支持向量机(SVM)中的核技巧。

今天,我们将探讨一些高维数据的难点,这些难点被称为维度诅咒。我们的目标是对这一概念及其实际影响有一个直观的理解。下图概述了我们文章的结构。

作者插图

理解维度诅咒

“维度诅咒”是由理查德·E·贝尔曼在 1960 年代首次使用的术语。它起初源于贝尔曼对动态优化的观点,后来成为理解高维空间复杂性的一个基本概念。

很好,但“维度诅咒”是什么?

这本质上是指在处理高维数据时遇到的困难和独特特性(在我们的案例中,这指的是数据集中具有许多特征、列或属性)。这些空间远远超出了我们在三维空间中的日常经验。

当我们增加数据集的维度时,它所占据的体积会以指数级扩展。这最初可能看起来是一个优势——更多的空间可能意味着更多的数据和可能更多的见解?然而,事实并非如此,因为维度的增加带来了许多挑战,这些挑战改变了我们处理和理解这些高维数据的方式。

主要挑战

从低维数据到高维数据的转变面临着几个严峻的挑战。其中两个尤为突出,因为它们的影响最大:1) 数据稀疏性;2) 距离度量问题。它们每一个都使高维数据的分析变得更加复杂。

数据稀疏性:空旷海洋中的小岛

高维空间中的数据稀疏性就像是辽阔海洋中的几个小岛。当维度增加时,在低维空间中彼此接近的数据点会变得越来越分离。这是因为每新增一个维度,空间的扩展量是指数级增长的。想象一下一个立方体变成一个超立方体;它的角落远离中心,使得内部变得更加空旷。这种不断增长的空旷感就是我们所称的数据稀疏性。

许多数据分析技术在面对稀疏性时都会遇到困难。例如,许多聚类算法依赖于相互接近的数据点来形成有意义的簇。然而,当数据点变得过于分散时,这些算法就会面临困难。

距离度量问题:当接近失去意义时

在高维空间中,距离度量遇到了显著的挑战。像欧几里得或曼哈顿距离这样的度量,在低维空间中用于测量数据点之间的接近度,但在这些扩展空间中,它们的效果减弱。距离开始趋于收敛。这意味着大多数点对之间的距离变得几乎相等,也与参考点的距离接近。这种收敛使得区分近邻和远点变得更加困难。

在分类等任务中,距离测量对新数据点的分类至关重要,这些度量在这种情况下效果会降低。因此,算法的性能下降,导致预测和分析的准确性降低。

为了更好地理解在高维空间中距离行为的变化,让我们进行一个简单的模拟。我们将生成低维和高维空间中的随机点。这将使我们能够观察和比较距离的分布,展示这些距离如何随着维度的增加而演变。

import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import pdist

def generate_points(dimensions, num_points, range_min, range_max):
    return np.random.uniform(range_min, range_max, (num_points, dimensions))

def calculate_pairwise_distances(points):
    distances = np.sqrt(((points[:, np.newaxis, :] - points[np.newaxis, :, :]) ** 2).sum(axis=-1))
    np.fill_diagonal(distances, np.nan)  # Ignore self-distances by setting them to NaN
    return distances

def calculate_distances_from_reference(points, reference_point):
    distances = np.sqrt(((points - reference_point) ** 2).sum(axis=1))
    return distances

def calculate_stats_for_dimensions(num_points, dimensions_range, range_min, range_max):
    means_pairwise = []
    stds_pairwise = []
    means_ref = []
    stds_ref = []

    for dim in dimensions_range:
        points = generate_points(dim, num_points, range_min, range_max)
        pairwise_distances = calculate_pairwise_distances(points)
        reference_point = generate_points(dim, 1, range_min, range_max)
        distances_from_ref = calculate_distances_from_reference(points, reference_point)

        means_pairwise.append(np.nanmean(pairwise_distances))
        stds_pairwise.append(np.nanstd(pairwise_distances))
        means_ref.append(np.mean(distances_from_ref))
        stds_ref.append(np.std(distances_from_ref))

    return means_pairwise, stds_pairwise, means_ref, stds_ref

def plot_histograms_and_stats(num_points, dimensions_range, range_min, range_max):
    fig, axs = plt.subplots(2, 3, figsize=(12, 7), tight_layout=True)

    # Plotting histograms for 3D and 100D
    for i, dim in enumerate([3, 100]):
        points = generate_points(dim, num_points, range_min, range_max)
        pairwise_distances = calculate_pairwise_distances(points)
        reference_point = generate_points(dim, 1, range_min, range_max)
        distances_from_ref = calculate_distances_from_reference(points, reference_point)

        axs[i, 0].hist(pairwise_distances[~np.isnan(pairwise_distances)], bins=50, alpha=0.7, color='blue', edgecolor='black')
        axs[i, 0].set_title(f'Pairwise Distances in {dim}D')
        axs[i, 1].hist(distances_from_ref, bins=30, alpha=0.7, color='green', edgecolor='black', range=(0, max(distances_from_ref)))
        axs[i, 1].set_title(f'Distances to Reference in {dim}D')

    # Calculating and plotting mean and std deviation trends across dimensions
    means_pairwise, stds_pairwise, means_ref, stds_ref = calculate_stats_for_dimensions(num_points, dimensions_range, range_min, range_max)

    # Plotting mean and std deviation graphs for pairwise distances
    axs[0, 2].plot(dimensions_range, means_pairwise, label='Mean Pairwise', marker='o', color='blue')
    axs[0, 2].plot(dimensions_range, stds_pairwise, label='Std Dev Pairwise', marker='x', color='cyan')
    axs[0, 2].set_title('Pairwise Distances Stats')

    # Plotting mean and std deviation graphs for distances to reference point
    axs[1, 2].plot(dimensions_range, means_ref, label='Mean Reference', marker='o', color='green')
    axs[1, 2].plot(dimensions_range, stds_ref, label='Std Dev Reference', marker='x', color='lime')
    axs[1, 2].set_title('Reference Point Distances Stats')

    axs[0, 2].legend()
    axs[1, 2].legend()

    plt.show()

plot_histograms_and_stats(1000, range(1, 101), 1, 100)

图片由作者提供

代码输出显示了维度间距离的变化。在 3D 中,点之间有不同的距离。在 100D 中,点之间的距离趋于相似。右侧的图表也显示,随着维度的增加,点之间的平均距离增大,但标准差与 2D 或 3D 空间中的大致相同。

另一个需要注意的是,随着维度的增加,点之间的平均距离变得更大,并接近最大距离。这是因为大部分空间集中在角落里。

为了更好地理解这一点,我们可以模拟高达 100 维的随机点。这将使我们能够比较平均距离与最大距离。

import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import pdist

def generate_points(dimensions, num_points, range_min, range_max):
    return np.random.uniform(range_min, range_max, (num_points, dimensions))

def calculate_distances_stats(points):
    # Compute pairwise distances
    distances = pdist(points)

    # Calculate average and maximum distance
    average_distance = np.mean(distances)
    max_distance = np.max(distances)

    return average_distance, max_distance
def plot_normalized_difference(num_points, dimensions_range, range_min, range_max):
    normalized_differences = []

    for dim in dimensions_range:
        points = generate_points(dim, num_points, range_min, range_max)
        average_distance, max_distance = calculate_distances_stats(points)
        normalized_difference = (max_distance - average_distance) / max_distance
        normalized_differences.append(normalized_difference)

    plt.figure(figsize=(8, 6))
    plt.plot(dimensions_range, normalized_differences, label='Normalized Difference', marker='o', color='blue')
    plt.xlabel('Number of Dimensions')
    plt.ylabel('Normalized Difference')
    plt.title('Normalized Difference Between Max and Average Distances Across Dimensions')
    plt.legend()
    plt.show()
plot_normalized_difference(500, range(1, 101), 0, 1)

图片由作者提供

图表显示,随着维度的增加,平均距离越来越接近最大距离。我们在这里使用了标准化以确保尺度的准确性。

理解绝对距离和相对距离之间的区别非常重要。虽然绝对距离通常随着维度的增加而增加,但相对差异更为重要。像 K-means 或 DBSCAN 这样的聚类算法通过查看点相对于彼此的位置来工作,而不是它们的确切距离。这使我们能够发现如果仅仅看距离可能会遗漏的模式和关系。

但这引出了一个有趣的问题:为什么在高维空间中,点对之间的距离在增加维度时趋于相同?是什么导致了这种情况的发生?

图片由 Aakash Dhage 提供,来源于 Unsplash

为了理解为什么高维空间中的点对变得等距,我们可以查看大数法则(LLN)。这一统计原理表明,随着样本大小或维度的增加,我们的观察平均值会越来越接近期望值。

让我们考虑一个公平的六面骰子的掷骰子例子。掷骰子的期望均值是 3.5,这是所有可能结果的平均值。最初,仅有几次掷骰子,比如 5 次或 10 次,平均值可能会因为随机性而与 3.5 有显著差异。但当我们将掷骰子的次数增加到几百次或几千次时,平均掷骰子值会逐渐接近 3.5。这一现象,即多次试验的平均值与期望值对齐,展示了大数法则(LLN)的本质。它表明,尽管单个结果是不可预测的,但在多次试验中,平均值变得非常可预测。

那么,这与高维空间中的距离有什么关系呢?

在 n 维空间中,两点之间的欧几里得距离是通过对每个维度的平方差进行求和来计算的。我们可以将每个平方差看作一个随机变量,类似于掷骰子。随着维度(或掷骰子次数)的增加,这些‘掷骰子’的总和会越来越接近一个期望值。

大数法则的一个关键要求是随机变量的独立性。在高维向量中,这种独立性可以通过一个有趣的几何属性来显示:这些向量往往几乎是正交的。

import numpy as np

def test_orthogonality(dimensions, n_trials):
    for i in range(n_trials):
        # Generate two random vectors
        v1 = np.random.randn(dimensions)
        v2 = np.random.randn(dimensions)

        # Calculate dot product
        dot_product = np.dot(v1, v2)

        # Calculate magnitudes
        magnitude_v1 = np.linalg.norm(v1)
        magnitude_v2 = np.linalg.norm(v2)

        # Calculate the cosine of the angle
        cos_theta = dot_product / (magnitude_v1 * magnitude_v2)

        # Check if vectors are almost orthogonal
        if np.abs(cos_theta) < 0.1:  # Adjust this threshold as needed
            orthogonality = "Almost Orthogonal"
        else:
            orthogonality = "Not Orthogonal"

        # Calculate angle in degrees
        theta = np.arccos(cos_theta) * (180 / np.pi)  # Convert to degrees

        print(f"Trial {i+1}:")
        print(f"  Dot Product: {dot_product}")
        print(f"  Cosine of Angle: {cos_theta}")
        print(f"  Angle: {theta} degrees")
        print(f"  Status: {orthogonality}")
        print("--------------------------------")

# Try to edit this and notice the near-orthogonality of vectors in higher dimensions
dimensions = 100  # Number of dimensions
n_trials = 10     # Number of trials

test_orthogonality(dimensions, n_trials)

尝试运行上面的代码并调整维度/试验次数,你会发现高维空间中的向量几乎是正交的。

两个向量 A 和 B 之间的角度由角度的余弦值决定,这个余弦值来源于它们的点积和大小。公式表示为:

这里,AB表示向量 A 和 B 的点积,∥A∥和∥B∥是它们各自的大小。为了使两个向量正交,它们之间的角度必须是 90 度,这使得 cos(θ)等于零。通常,这在低维空间中通过点积AB等于零来实现。

然而,在高维空间中,出现了另一种现象。点积与向量大小的比率变得如此之小,以至于我们可以认为这些向量是‘几乎正交’的。

那么,在这个背景下,两个向量‘独立’是什么意思呢?

在网格城市中导航:高维独立性的类比

想象你在一个像曼哈顿街道那样的网格布局的城市中。设想你站在一个交叉口,试图到达城市中的另一个点。在这个类比中,每条街道代表高维空间中的一个维度。沿着街道移动就像在改变高维向量中的一个维度。沿着一条街道移动不会影响你在另一条街道上的位置,就像改变一个维度不会影响其他维度一样。

要到达一个特定的交叉口,你需要做出一系列独立的决策,就像在高维空间中计算距离一样。每个决策独立地做出,但最终将你引导到目的地。

这个类比也适用于高维向量中的正交性概念。当向量几乎正交时,它们沿着各自的路径移动,互相之间没有显著影响。这种条件补充了 LLN 对统计独立性的需求。

一个重要的说明:虽然这个 LLN 类比提供了一个有用的视角,但它可能无法捕捉到这种行为背后的所有思想或原因。然而,它作为一个有用的代理,提供了对点对几乎等距原因的理解可能

实际意义

维度诅咒问题的一种表现是过拟合。过拟合发生在一个复杂的模型学习到的是噪声而不是数据中的模式。这在高维空间中特别明显,因为那里有很多特征。模型可能会做出虚假的连接或相关性,并在看到新数据时表现不佳(无法进行泛化)。

维度诅咒还使得在大数据集中寻找模式变得困难。高维数据分散且稀疏,因此传统分析方法难以找到有意义的见解。需要一些改变或专门的方法来导航和理解这种类型的数据。

另一个含义是处理高维数据需要大量的计算能力和内存。在低维度下表现良好的算法在维度增加时变得更加复杂和资源密集。这意味着要么需要更强大的硬件,要么需要优化算法以高效处理增加的计算负载。

如何应对维度诅咒?

有几种策略可以应对维度诅咒。一种方法是降低维度,同时保留重要的信息(例如,PCA 算法)。另一种方法是流形学习(可以看作是一种降维方法),它揭示了高维数据中的结构。流形学习的关键思想是,许多高维数据集实际上位于高维空间中的低维流形上(例如,Isomaps)。

在这里需要注意的是,-一般来说- 传统的降维技术,如主成分分析(PCA),侧重于以线性方式保留全局数据结构和方差。相比之下,流形学习技术,如等距映射(Isomap),强调揭示数据的潜在非线性结构(流形),旨在保留局部关系和几何特征。

特征选择也是一个选项,通过选择相关特征来提高模型性能。正则化技术通过缩减不重要的特征来防止过拟合。增加样本量也可以有所帮助,尽管这可能并不总是可能的。这些方法可以帮助我们更准确和高效地分析高维数据。

结论

维度诅咒是数据科学和机器学习中最重要的问题之一。它发生在处理高维空间时。两个显著的挑战是数据稀疏性和距离度量的问题。这些挑战可能导致机器学习模型过拟合,并使计算变得更加复杂。为了解决这些挑战,可以使用降维、特征选择和正则化技术等策略。

如果你已经读到这里,我想感谢你花时间阅读这篇文章!我希望你觉得这个话题有趣,并且至少激发了你更深入了解高维数据的兴趣。请随时建议任何修改或指出任何错误或不准确之处。

使用 Apache Spark PrometheusServlet 自定义 Kafka 指标

原文:towardsdatascience.com/custom-kafka-streaming-metrics-using-apache-spark-prometheus-sink-9c04cf2ddaf1

使用 PrometheusServlet 在 Apache Spark 中创建和暴露自定义 Kafka 消费者流指标

Vitor Teixeira数据科学前沿 Vitor Teixeira

·发表于 数据科学前沿 ·阅读时间 6 分钟·2023 年 2 月 2 日

--

图片由 Christin Hume 提供,来源于 Unsplash

在这篇博客文章中,我将描述如何创建和增强当前 Spark Structured Streaming 指标,结合 Kafka 消费者指标,并通过 Spark 3 的 PrometheusServlet 暴露这些指标,使 Prometheus 可以直接访问。在之前的 Spark 版本中,必须设置 JmxSink/JmxExporter、GraphiteSink/GraphiteExporter 或自定义的 sink,将指标部署到 PushGateway 服务器。也就是说,我们无法避免解决方案复杂性的增加,因为我们必须设置与应用程序交互的外部组件,以便 Prometheus 可以抓取这些数据。

动机

比以往任何时候都更加重要的是,软件的可观察性变得必不可少。它使我们能够在不直接与系统交互的情况下,洞察软件内部发生了什么。建立在这一可观察性支柱上的一种方法是暴露应用程序指标。当这些指标建立在可观察性堆栈上时,它们可以帮助我们通过警报或查看仪表板来检测问题,并通过分析指标找到其根本原因。

Apache Spark 应用程序也不例外。确实可以访问Spark Web UI并了解我们的应用程序如何运行,但当应用程序数量增加到十倍或一百倍时,排查问题变得困难。这时像Grafana这样的可观察性工具就派上用场了。Grafana 能够连接到 Prometheus 数据库,Prometheus 通过目标 PrometheusServlet 与我们的应用程序无缝集成。

配置后,Apache Spark 原生暴露了几个度量,详细信息见这里。在 Structured Streaming 中,默认情况下不暴露任何度量,除非我们设置 "spark.sql.streaming.metricsEnabled" -> "true"。以下是 Kafka 流作业暴露的度量示例:

默认的 Spark Structured Streaming 度量

如我们所见,这些度量非常通用,并未提供关于我们源的详细信息。

目标是能够暴露 Kafka 消费者度量,帮助我们监控事件消费的情况。

定义自定义度量

度量应该是可以量化的值,提供关于应用程序状态或性能的实时洞察。在本文的范围内,我们将涵盖以下度量:

  • 起始偏移量: 流查询首次启动的偏移量。

  • 结束偏移量: 流查询处理的最后偏移量。跟踪查询的消费者进度。

  • 领先偏移量: 流查询正在消费的主题的最新偏移量。跟踪被消费主题的演变。

  • 滞后偏移量: 最后处理的偏移量与主题的领先偏移量之间的差异。跟踪流查询与实时之间的差距。

  • 消耗速率: 流查询主题的消耗速率。它是所有在流查询中订阅的主题的总和。

  • 最后记录时间戳: 从每个 TopicPartition 消费的最后消息时间戳。跟踪生产者和消费者之间的延迟。

创建度量源

下一步,在定义度量之后,是创建度量源,负责将度量暴露给 Spark 的 MetricsSystem。

为了暴露度量,我们需要创建一个扩展了 Source 的类。这将创建一个执行器连接到驱动程序,将度量作为心跳过程的一部分传递。

KafkaMetricsSource 实现

确保将仪表定义为 SettableGauge,以便在执行过程中能够更新它们。

在定义源之后,我们只需实例化源并在 Spark 的 MetricsSystem 中注册它。

源注册的简化版本

完整的源实现代码可以查看:

## spark-prometheus-kafka-metrics/KafkaMetricsSource.scala at master ·…

使用 Spark 3 PrometheusServlet 增强的 Kafka Streaming 消费者指标的 Spark Streaming 应用程序…

github.com

将 Streaming Query Progress 作为信息来源

现在我们已经有了我们的源,接下来需要做的是使用它。为此,我们需要这些指标来填充我们最近创建的仪表。

如果你之前运行过结构化 Streaming 作业,你可能会注意到,当 Streaming Query 进展时,会有类似下面的输出:

通过分析输出,我们可以看到,通过解析 JSON 已经可以获得大部分指标。开始偏移量指标使用startOffsets,结束偏移量使用endOffset,最新偏移量使用latestOffsetsource.inputRowsPerSecond作为源的消费速率,延迟偏移量则不会使用metrics值,因为我们可以通过使用endOffsetlatestOffset值来计算每个 TopicPartition 的延迟,得到更细粒度的指标。

为了利用这些信息,我们可以使用 StreamingQueryListeneronProgress 事件。

第一步是创建一个扩展StreamingQueryListener的类,以便我们能够接收并处理进度事件。它接收我们之前创建的KafkaMetricsSource,负责发出这些指标。

KafkaOffsetsQueryListener 代码片段

最后一步只是将监听器注册到我们希望接收更新的流中。

如果你希望查看完整的监听器代码,可以在这里查看:

## spark-prometheus-kafka-metrics/KafkaOffsetsQueryListener.scala at master ·…

使用 Spark 3 PrometheusServlet 增强的 Kafka Streaming 消费者指标的 Spark Streaming 应用程序…

github.com

执行器指标 — 记录时间戳

最后一个指标是每个 TopicPartition 的最后处理记录时间戳。这个指标比之前的更复杂,因为信息存在于 Spark 执行器的计算时间中。

我们唯一能访问这些信息的方法是创建一个作为副作用的虚拟 Spark 表达式。我们将使用来自Spark 的 Kafka 模式value列来欺骗 Spark 执行此转换。

KafkaTimestampMetrics 表达式

这个表达式接收整个 Kafka 行,提取必要的值,并发出指标。它将返回value列,正如我们上面所说的,它将用于欺骗 Spark 执行此表达式。

表达式使用

如果我们例如使用了timestamp列,并且在查询的后续部分选择了value列,Spark 会将此表达式从最终计划中截断,我们的指标将被忽略。如果你不打算使用value列(这比较少见),请确保使用适当的列。

配置 PrometheusServlet

设置好一切后,我们只需通过将spark.ui.prometheus.enabled=true设置为启用 UI 上的 Prometheus(它创建了一个包含所有驱动程序和执行程序指标的单一端点)并配置spark.metrics.conf以满足所需的配置。

运行应用程序并访问localhost:4040/metrics/prometheus将显示我们之前创建的所有指标以及原生指标。

/metrics/prometheus/ 端点上暴露的指标

原生 Prometheus 集成的局限性

这个新功能有一些局限性。其中之一是这个端点只暴露以metrics_spark_info开头的指标。此外,Spark 不遵循 Prometheus 命名约定,并且当前不支持标签(如果你知道某种方法,请告诉我!)。这意味着我们将在 Prometheus 中拥有大量不同的指标,这可能会有些令人不知所措。这个问题可以通过重新标记指标来解决,但可能会比较麻烦。

结论

就这些!你现在可以使用 Spark 的 Prometheus 原生集成创建和暴露 Kafka 自定义指标。现在,只剩下让 Prometheus 抓取所有端点,并使用这些指标创建漂亮的仪表板和报警,以帮助你更好地了解应用程序内部发生的情况。

尽管 Prometheus 指标接收器早期的局限性是可以理解的,因为它是一个实验性功能,但我相信它将很快通过更多自定义进行增强。

如果你想查看本文中使用的完整项目,请检查:

[## GitHub - Orpheuz/spark-prometheus-kafka-metrics: 提升了 Kafka 的 Spark Streaming 应用程序…

目前无法执行该操作。您在另一个标签页或窗口中登录了。您在另一个标签页或窗口中注销了…

github.com

自定义 ChatGPT API 的记忆功能

原文:towardsdatascience.com/custom-memory-for-chatgpt-api-artificial-intelligence-python-722d627d4d6d

LangChain 记忆类型的温和介绍

安德烈亚·巴伦苏埃拉数据科学前沿 安德烈亚·巴伦苏埃拉

·发布于 数据科学前沿 ·8 分钟阅读·2023 年 8 月 20 日

--

自制 gif。

如果你曾经使用过 OpenAI API,我相信你已经注意到这一点。

明白了吗?

正确! 每次调用 ChatGPT API 时,模型不会记住你之前的请求。换句话说:每次 API 调用都是独立的交互

当你需要与模型进行后续交互时,这确实很烦人。聊天机器人就是一个需要后续交互的经典例子。

在本文中,我们将探讨如何在使用 OpenAI API 时为 ChatGPT 提供记忆,以便它记住我们之前的交互。

热身!

让我们进行一些与模型的交互,以体验这种默认的无记忆现象:

prompt = "My name is Andrea"
response = chatgpt_call(prompt)
print(response)

# Output: Nice to meet you, Andrea! How can I assist you today?

但是当被问到后续问题时:

prompt = "Do you remember my name?"
response = chatgpt_call(prompt)
print(response)

# Output: I'm sorry, as an AI language model, I don't have the ability 
# to remember specific information about individual users.

对, 实际上模型不会记住我的名字,即使它在第一次交互时被提到。

注意: 方法 chatgpt_call() 只是 OpenAI API 的一个封装。如果你想查看如何轻松调用 GPT 模型,我们在 ChatGPT API 调用:温和介绍 中已经给出了示例!

有些人通常通过在每次进行新的 API 调用时,提前将之前的对话历史传递给模型来绕过这种无记忆的情况。然而,这种做法并未优化成本,并且对于长对话有一定的限制。

为了为 ChatGPT 创建记忆,使其能够意识到之前的交互,我们将使用流行的 langchain 框架。该框架允许你轻松管理 ChatGPT 对话历史,并通过选择适合应用程序的记忆类型来优化它。

LangChain 框架

langchain框架的目的是帮助开发人员构建由大语言模型(LLMs)驱动的应用程序。

自制截图,来自官方 LangChain GitHub 仓库

根据他们的GitHub 描述

大语言模型(LLMs)作为一种变革性技术正在出现,使开发人员能够构建以前无法构建的应用程序。然而,仅孤立使用这些 LLMs 通常不足以创建真正强大的应用程序——真正的力量在于将它们与其他计算或知识来源结合起来。

该库旨在协助开发这些类型的应用程序。

他们声称,仅使用 LLMs(大语言模型)构建应用程序可能是不够的。我们在仅使用 OpenAI API 与模型进行后续交互时也发现了这一点。

框架设置

在 Python 中启动并运行langchain库很简单。像任何其他 Python 库一样,我们可以使用pip安装它:

pip install langchain

LangChain 在幕后调用 OpenAI API。因此,需要将你的 OpenAI API 密钥设置为名为OPENAI_API_KEY的环境变量。如果需要获取 OpenAI 密钥的指导,请查看获取 API 密钥的逐步指南

LangChain:基础调用

让我们首先设置一个使用 LangChain 调用 ChatGPT 的基本 API。

这个任务相当简单,因为模块langchain.llms已经提供了一个OpenAI()方法用于此目的:

# Loads OpenAI key from the environment
from langchain.llms import OpenAI
chatgpt = OpenAI()

一旦加载了所需的模型,我们需要启动所谓的对话链。LangChain 也提供了一个用于此目的的模块:

from langchain.chains import ConversationChain
conversation = ConversationChain(llm=chatgpt)

让我们将对话定义为verbose=True,以观察模型的推理过程。

最后,langchain提供了一个.predict()方法来将你想要的提示发送给 ChatGPT,并获取其完成结果。让我们试试吧!

conversation.predict(input="Hello, we are ForCode'Sake! A Medium publication with the objective of democratizing the knowledge of data!")

让我们进行一次后续交互!

conversation.predict(input="Do you remember our name?")

# Output: " Hi there! It's great to meet you. 
# I'm an AI that specializes in data analysis. 
# I'm excited to hear more about your mission in democratizing data knowledge.
# What inspired you to do this?"

我们可以看到,使用**langchain**时,模型能够顺利处理后续交互

LangChain 内存类型

正如我们观察到的,LangChain 对话链已经跟踪了声明的conversation.predict调用。然而,默认的对话链会存储我们与模型的每一个交互

正如我们在文章开头简要讨论的那样,存储与模型的所有交互可能会迅速积累大量的令牌,以便每次提示模型时都需要处理。需要记住的是,ChatGPT 每次交互都有令牌限制。

此外,ChatGPT 的使用费用也取决于令牌的数量。处理每次新交互中的所有对话历史记录可能会随着时间的推移变得非常昂贵。

为了克服这些限制,langchain实现了不同类型的内存以供您在应用程序中使用。

让我们来探索它们吧!

#1. 完整的交互

尽管 LangChain 的默认行为是存储所有过去的交互,但这种内存类型可以被显式声明。这就是所谓的ConversationBufferMemory,它简单地用我们所有的先前交互填充一个缓冲区:

from langchain.memory import ConversationBufferMemory
memory=ConversationBufferMemory()

声明内存类型使我们能够对 ChatGPT 内存进行一些额外的控制。例如,我们可以随时使用memory.buffermemory.load_memory_variables({})检查缓冲区内容。

此外,我们可以在缓冲区中添加额外的信息,而无需与模型进行实际交互:

memory.save_context({"input": "Hi"},
                    {"output": "What's up"})
memory.save_context({"input": "Not much, just hanging"},
                    {"output": "Cool"})

如果您不需要在应用程序中操作缓冲区,您可以使用默认内存而不需要显式声明。尽管我确实推荐用于调试目的!

#2. 窗口中的交互

一种成本更低的替代方案是只存储与模型的某个数量的以前交互(k)。这就是所谓的交互窗口

当对话变得足够大时,对于您的应用程序来说,模型只记住最近的交互可能已经足够。在这些情况下,ConversationBufferWindowMemory模块是可用的。

让我们来探索它的行为吧!

首先,我们需要加载llm模型和新的memory类型。在这种情况下,我们设置了k=1,这意味着只有前一次迭代会保存在内存中:

# OpenAI key from environment
from langchain.llms import OpenAI
llm = OpenAI()
from langchain.memory import ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(k=1)

其次,让我们像前面部分所示的那样为我们的对话添加一些上下文:

memory.save_context({"input": "Hi"},
                    {"output": "What's up"})
memory.save_context({"input": "Not much, just hanging"},
                    {"output": "Cool"})

尽管我们在对话历史中存储了两个交互,由于我们设置了k=1,模型只会记住最后一次交互{“input”: “Not much, just hanging”}, {“output”: “Cool”}

为了证明这一点,让我们检查一下模型内存中有什么:

memory.load_memory_variables({})

# Output: {'history': 'Human: Not much, just hanging\nAI: Cool'}

我们可以通过设置verbose=True来提出后续问题,从而进一步证明这一点,这样我们可以观察存储的交互记录:

conversation.predict(input="Can you tell me a joke?")

详细输出如下:

正如我们所观察到的,模型只记住前一次交互

#3. 交互的总结

我相信你现在正在考虑,完全删除与模型的旧交互对于某些应用程序可能有点风险

让我们想象一个客户服务聊天机器人,它首先询问用户的合同编号。无论交互的编号是多少,模型都不能忘记这个信息。

为此,有一种内存类型使用模型本身生成之前交互的总结。因此,模型仅将对话的总结存储在内存中。

这种优化的内存类型被称为ConversationSummaryBufferMemory。它还允许存储完整的最近交互,最多到达一个最大令牌数(由max_token_limit给定),以及之前交互的总结。

让我们在实际操作中观察这种内存行为!

from langchain.memory import ConversationSummaryBufferMemory

让我们创建一个内容丰富的对话,以便我们可以探索总结能力:

schedule = "There is a meeting at 8am with your product team. \
You will need your powerpoint presentation prepared. \
9am-12pm have time to work on your LangChain \
project which will go quickly because Langchain is such a powerful tool. \
At Noon, lunch at the Italian restaurant with a customer who is driving \
from over an hour away to meet you to understand the latest in AI. \
Be sure to bring your laptop to show the latest LLM demo."

memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=100)
memory.save_context({"input": "Hello"}, {"output": "What's up"})
memory.save_context({"input": "Not much, just hanging"},
                    {"output": "Cool"})
memory.save_context({"input": "What is on the schedule today?"}, 
                    {"output": f"{schedule}"})

现在,当检查内存内容时使用memory.load_memory_variables({}),我们将看到我们互动的实际总结:

{
  'history': 'System: 
  \nThe human greets the AI and asks what is on the schedule for the day. 
  The AI responds with "Cool".\n
  AI: There is a meeting at 8am with your product team. 
  You will need your powerpoint presentation prepared. 
  9am-12pm have time to work on your LangChain project which will go quickly because Langchain is such a powerful tool. 
  At Noon, lunch at the italian resturant with a customer who is driving from over an hour away to meet you to understand the latest in AI. 
  Be sure to bring your laptop to show the latest LLM demo.'
}

总结听起来很不错,不是吗?

让我们进行一次新的互动!

llm = OpenAI()

conversation = ConversationChain(
    llm=llm, 
    memory = memory,
    verbose=True
)

详细输出如下:

从示例中我们可以观察到,这种内存类型允许模型保留重要信息,同时减少不相关的信息,从而减少每次新互动中使用的 Token 数量。

总结

在本文中,我们已经看到根据我们的需求创建 GPT 支持的应用程序内存的不同方式

通过使用 LangChain 框架而不是直接调用 OpenAI API,我们摆脱了简单的问题,比如让模型了解之前的互动。

尽管 LangChain 的默认内存类型可能已经足够满足你的应用需求,但我还是鼓励你估算一下对话的平均长度。这是一个很好的练习,可以将使用 summary 内存的平均 Token 数量——因此也是成本——进行比较。你可以以最低成本获得模型的最佳性能!

在我看来,LangChain 框架对 GPT 模型有很多贡献。你是否已经发现了其他有用的功能?

就这些!非常感谢阅读!

希望这篇文章能帮助你在构建 ChatGPT 应用程序时!

你也可以订阅我的 通讯 以获取新内容更新。特别是如果你对关于 ChatGPT 的文章感兴趣

链接 3 [## ChatGPT 审核 API:输入/输出控制

使用 OpenAI 的审核端点实现负责任的 AI

链接 4 [## 解锁 ChatGPT 分词器

实操!ChatGPT 如何管理 Token?

链接 1 链接 2 [## 精通 ChatGPT:利用 LLMs 进行有效总结

如何提示 ChatGPT 以获取高质量的总结

链接 5

同样,朝着负责任的 AI迈进:

## ChatGPT 对你的了解:OpenAI 数据隐私之旅

管理 ChatGPT 中个人数据的新方法

towardsdatascience.com

在 scikit-learn 中的自定义评分函数

原文:towardsdatascience.com/custom-scoring-functions-in-scikit-learn-d33a1ebc8f90

深入研究 RandomizedSearchCV、GridSearchCV 和 cross_val_score 中的评分函数

扎卡里·赖西克数据科学导向 扎卡里·赖西克

·发表于 数据科学导向 ·7 分钟阅读·2023 年 11 月 16 日

--

照片由 engin akyurt 提供,来源于 Unsplash

RandomizedSearchCVGridSearchCVcross_val_score 都是优化和评估 scikit-learn 中机器学习模型的工具。这些工具提供了一种系统化的方法来调整超参数和评估模型性能。

很长一段时间,我使用这些工具时没有考虑评分函数。然而,我最终了解到,在使用这些工具时,scikit-learn 默认使用模型的固有评分函数来评估性能。默认的评分指标并不总是合适的,这可能导致对模型的决策失误。

本文的其余部分将深入探讨如何以及何时在 scikit-learn 中使用自定义评分函数。

示例:保险索赔中的 Tweedie 回归

在这个示例中,我们开发了一种回归器来预测未来的保险索赔成本,这一任务因保险数据中的固有不确定性而复杂。不确定性来自几个方面。

  • 一旦有人购买了保险单,就没有保证他们会提出索赔。这导致目标变量中有很高的零值浓度。

  • 如果有人提出索赔,该索赔的金额可能大也可能小。这导致我们的目标变量具有较大的方差。

默认情况下,RandomizedSearchCVGridSearchCVcross_val_score使用与传递给它的分类器或回归器相关联的默认评分指标。对于许多广泛使用的回归器,传统的指标如 R²和 RMSE 是默认分数。然而,使用这些指标来决定参数调优或评估保险数据模型的性能通常会导致错误的决定和结果。

因此,在处理保险数据时,我们需要确保将适当的评分函数传递给这些工具,以确保我们正确设置模型参数并评估模型性能。更一般地说,每次使用这些工具时,您应该确保使用的评分函数适合您的数据。在某些情况下,您可能需要使用不同于默认的评分函数。

在接下来的几个小节中,我们将使用 light GBM、RandomizedSearchCV和自定义评分函数构建一个 tweedie 回归器。此示例旨在演示如何在像RandomizedSearchCVGridSearchCVcross_val_score这样的工具中使用评分函数。此示例并不旨在提供机器学习模型开发、超参数调优的详细概述或生成一个优秀的模型。

模拟我们的数据集

由于保险数据的敏感性,通常很难找到公开可用的索赔历史数据集。因此,我们使用下面提供的代码模拟了自己的数据集。该数据集旨在模拟保险索赔。每一行代表一个投保人,我们模拟了每个投保人的索赔次数及其索赔金额。模拟数据集确保数据具有一些定义特征。

  • policy_exposure,即个人持有保单的时间,均匀分布在 1 到 12 个月之间。

  • num_claims,即某人提交的索赔次数,服从泊松分布。这确保了右偏分布,其中大多数人没有提交任何索赔。

  • claim_cost,即个人索赔的总金额,服从 gamma 分布。如果索赔次数为 0,则索赔成本为零。

import pandas as pd
import numpy as np

np.random.seed(42)

n_samples = 50000 

policy_exposure = np.random.uniform(1, 12, n_samples)

n_features = 10
X = np.random.randn(n_samples, n_features)

annual_claim_rate = 0.05  
monthly_claim_rate = annual_claim_rate / 12  
num_claims = np.random.poisson(lam=monthly_claim_rate * policy_exposure)

claim_cost = np.zeros(n_samples)
positive_cost_indices = num_claims > 0
high_variance_gamma = np.random.gamma(2, 3000, size=positive_cost_indices.sum())
claim_cost[positive_cost_indices] = high_variance_gamma

claims_df = pd.DataFrame(
    {
        "policy_exposure": policy_exposure,
        "num_claims": num_claims,
        "claim_cost": claim_cost,
    }
)

features_df = pd.DataFrame(X, columns=[f"feature_{i+1}" for i in range(n_features)])
df = pd.concat([claims_df, features_df], axis=1)

num_claims_std_dev = 2
for i in range(1, 6):
    df[f"feature_{i}"] += np.random.normal(0, num_claims_std_dev * i, n_samples)

claim_cost_std_dev = 2
for i in range(6, 11):
    df[f"feature_{i}"] *= np.random.normal(1, claim_cost_std_dev * (i - 5), n_samples)

构建我们的回归器

我们首先要导入我们的light GBM(“LGBM”)回归器。我们将回归目标设置为 tweedie。评估指标与目标无关。

from lightgbm import LGBMRegressor

tweedie_regressor = LGBMRegressor(objective="tweedie", verbose=0)

features = [x for x in df.columns if "feature" in x]

target = "claim_cost"

weight = "policy_exposure"

Tweedie 分布是一种统计分布类型,涵盖了包括正态分布、泊松分布和 gamma 分布在内的一系列其他知名分布。Tweedie 分布的幂参数决定了 Tweedie 家族内分布的具体形式。幂参数在 1 和 2 之间会导致复合泊松-伽玛分布。

在我们的案例中,由于我们模拟了数据,我们已经知道索赔数量遵循泊松分布,这些索赔的大小遵循伽玛分布。因此,我们将设置一个随机网格搜索来找到 1 到 2 之间的最佳参数值。

使用默认评分的RandomizedSearchCV

在保险数据上构建 Tweedie 模型时,一个常见的评估指标是mean_tweedie_deviance。然而,我们的初始网格搜索将故意使用估计器的默认评分函数(在这种情况下为 R²)来选择最佳参数。一旦搜索完成,我们将计算mean_tweedie_deviance,以便将其与未来旨在优化mean_tweedie_deviance的网格搜索进行比较。

from sklearn.model_selection import RandomizedSearchCV

param_distributions = {
    "tweedie_variance_power": [x / 100 for x in range(100, 200)],
}

fit_params = {"sample_weight": df[weight]}

random_search = RandomizedSearchCV(
    estimator=tweedie_regressor,
    n_iter=50,
    param_distributions=param_distributions,
    cv=5,
    verbose=2,
    random_state=21,
    n_jobs=-1,
)

random_search.fit(df[features], df[target], **fit_params)

我们确认模型的评分等于 R²。

from sklearn.metrics import r2_score

best_power = random_search.best_params_["tweedie_variance_power"]

best_estimator = random_search.best_estimator_

best_estimator.fit(df[features], df[target], sample_weight=df[weight])

model_score = best_estimator.score(df[features], df[target])

r2 = r2_score(df[target],best_estimator.predict(df[features]))

print(model_score == r2)

# True

我们还可以查看最优的幂参数。单独解释mean_tweedie_deviance可能会有些棘手。然而,通常情况下,值越低越好。我们将使用这个值作为基准来与未来的搜索进行比较。

deviance = mean_tweedie_deviance(
    df[target],
    best_estimator.predict(df[features]),
    power=best_power,
    sample_weight=df[weight],
)

print(f"Best Power: {best_power}")
print(f"mean_tweedie_deviance: {deviance}")

# Best Power: 1.29
# mean_tweedie_deviance: 159.49

使用make_scorerRandomizedSearchCV

在没有额外评估的情况下,使用默认评分器的结果看起来还不错。然而,假设我们是经验丰富的数据科学家,并且我们意识到使用 R²来调整模型是不合适的。我们决定将评分函数切换到mean_tweedie_deviance

为了让RandomizedSearchCV能够使用这个功能,我们使用 scikit-learn 的make_scorer。根据 Scikit-learn 的文档make_scorer是一个“从性能指标或损失函数中创建评分器”的函数。换句话说,这个函数允许我们使用sklearn.metrics中任何可用的函数作为RandomizedSearchCVGridSearchCVcross_val_score中的评分函数。文档中还有一些其他可用的参数,但我们唯一需要的是greater_is_better,以告知我们的搜索较低的mean_tweedie_deviance更好。

使用make_scorer设置评分指标相对容易。

tweedie_loss = make_scorer(mean_tweedie_deviance, greater_is_better=False)

使用我们新的评分函数,我们以相同的方式运行搜索。

param_distributions = {
    "tweedie_variance_power": [x / 100 for x in range(100, 200)],
}

fit_params = {"sample_weight": df["policy_exposure"]}

random_search = RandomizedSearchCV(
    estimator=tweedie_regressor,
    n_iter=10,
    param_distributions=param_distributions,
    scoring=tweedie_loss,
    cv=5,
    verbose=2,
    random_state=21,
    n_jobs=-1,
)

random_search.fit(df[features], df["claim_cost"], **fit_params)

再次,我们可以查看网格搜索的结果。

best_power = random_search.best_params_["tweedie_variance_power"]

best_estimator = random_search.best_estimator_

best_estimator.fit(df[features], df[target], sample_weight=df[weight])

deviance = mean_tweedie_deviance(
    df[target],
    best_estimator.predict(df[features]),
    power=best_power,
    sample_weight=df[weight],
)

print(f"Best Power: {best_power}")
print(f"mean_tweedie_deviance: {deviance}")

# Best Power: 1.29
# mean_tweedie_deviance: 159.49

结果与默认评分器的结果相同,但为什么呢?当我们使用make_scorer创建自定义评分函数时,搜索不会在mean_tweedie_deviance的计算中使用测试的幂值。相反,它使用的是默认的幂值。根据 scikit-learn 的文档mean_tweedie_deviance的默认幂值为 0。Mean_tweedie_deviance的幂值为零时,相当于均方根误差,这导致与使用默认评分指标时相同的幂选择。

def weighted_mse(y_true, y_pred, weights):
    weighted_squared_diff = weights * (y_true - y_pred) ** 2

    weighted_mean_squared_error = np.sum(weighted_squared_diff) / np.sum(weights)

    return weighted_mean_squared_error

mse = weighted_mse(df[target],
    best_estimator.predict(df[features]),df[weight])

deviance = mean_tweedie_deviance(
    df[target],
    best_estimator.predict(df[features]),
    power=0,
    sample_weight=df[weight],
)

print(f"Weighte MSE: {mse}")
print(f"Deviance (Power = 0): {deviance}")

# Weighte MSE: 1566555.33
# Deviance (Power = 0): 1566555.33

如果我们专注于调整其他 LGBM 参数并且已经知道我们想要的功率,我们可以使用下面的评分函数。然而,由于我们正在调整功率参数,这对我们来说是不可能的。

tweedie_loss = make_scorer(mean_tweedie_deviance, greater_is_better=False, power=1.5)

RandomizedSearchCV使用自定义评分对象

我们可以编写自己的函数并将其用作评分标准,而不是使用make_scorer。只要函数签名是(y_true, y_pred)(estimator, X, y),scikit-learn 工具,如 RandomizedSearchCV、GridSearchCV 或 cross_val_score,都可以接受该函数作为评分函数。

对于我们的示例,我们选择设计签名为(estimator, X, y)的评分函数。函数中包含估算器使我们能够访问估算器的功率,从而在搜索过程中正确计算mean_tweedie_deviance

def tweedie_loss(estimator, X, y):
    power = estimator.get_params()["tweedie_variance_power"]
    y_pred = estimator.predict(X)
    return -mean_tweedie_deviance(y, y_pred, power=power)

现在,我们直接将这个函数传递给我们的搜索!

param_distributions = {
    "tweedie_variance_power": [x / 100 for x in range(100, 200)],
}

fit_params = {"sample_weight": df["policy_exposure"]}

random_search = RandomizedSearchCV(
    estimator=tweedie_regressor,
    n_iter=10,
    param_distributions=param_distributions,
    scoring=tweedie_loss,
    cv=5,
    verbose=2,
    random_state=21,
    n_jobs=-1,
)

random_search.fit(df[features], df["claim_cost"], **fit_params) 

和之前一样,让我们查看一下最佳功率。

best_power = random_search.best_params_["tweedie_variance_power"]

best_estimator = random_search.best_estimator_

best_estimator.fit(df[features], df[target], sample_weight=df[weight])

deviance = mean_tweedie_deviance(
    df[target],
    best_estimator.predict(df[features]),
    power=best_power,
    sample_weight=df[weight],
)

print(f"Best Power: {best_power}")
print(f"mean_tweedie_deviance: {deviance}")

# Best Power: 1.62
# mean_tweedie_deviance: 35.30

我们注意到power稍微高一些,而mean_tweedie_deviance则低得多!由于mean_tweedie_deviance是这个模型更合适的度量指标,所以我们对这个结果感到满意。

你可能已经注意到,当我们在搜索完成后计算我们的mean_tweedie_deviance时,我们会将样本权重作为输入。然而,在我们自定义的评分对象中,我们没有使用任何样本权重。如果所需信息在所需签名的任何变量中不可用,则需要将额外的参数传递给自定义评分对象。虽然可以提供额外的输入,但这需要构建自己的 k 折验证方法,这超出了本文的范围。

结论

在这篇文章中,我们探讨了在RandomizedSearchCV中自定义评分标准的复杂性。本文所涵盖的概念也适用于其他工具,如GridSearchCVcross_val_score。此外,我们还讨论了这种方法的一些限制。请记住,默认评分标准可能并不总是适合当前任务!

客户满意度测量与 N-gram 和情感分析

原文:towardsdatascience.com/customer-satisfaction-measurement-with-n-gram-and-sentiment-analysis-547e291c13a6

产品评论是进行高质量管理决策的极佳信息来源。了解更多关于正确的文本挖掘技术。

Petr KorabTowards Data Science Petr Korab

·发布于Towards Data Science ·阅读时间 7 分钟·2023 年 4 月 10 日

--

图片来源:Freepik,链接:Freepik

引言

满意的客户推动公司增长。这句五个字的句子解释了我们为何竭尽全力以最大化客户满意度。产品评论是大型公司(如AmazonApple)、中型出口商(包括Lentiamo)以及运营其 Facebook 页面的本地公司收集的主要数据来源之一。评论通常会随着时间的推移而重复收集,质量变化、市场营销沟通和客户服务友好度等因素都会影响客户表达的情感。

注:图像由作者提供,基于 Karim(2011 年)、Baker 和 Wurgler(2006 年)、Merrin 等(2013 年)和 Eachempat 等(2022 年)的审阅

商业智能(BI)角色应该是分析产品评论,识别潜在问题,并提出假设以解决这些问题。在下一阶段,这些建议会根据公司结构被其他部门审查。本文将更详细地解释基于产品评论数据的客户满意度分析。

端到端的过程包括:

  • 如何对时间序列产品评论进行探索性分析

  • 如何快速评估随时间变化的产品评论情感

  • 如何显示随时间变化的最常见满意度因素

我们将使用 Python,这是大多数 BI 和数据分析师常用的工具。

2. 数据

获取具有灵活数据许可证的未抓取产品评论数据通常很困难。此情况下,虚假评论数据集的合成数据,按照Attribution 4.0 International license分发,是一个极好的选择。

数据如下所示:

图像 1. 虚假评论数据集

文本列包含产品评论,期间标记评论日期。我们将使用的子集包含 3848 条关于服装、鞋子和珠宝产品的评论。

3. 探索性数据分析(EDA)

这种类型的数据集的探索性数据分析(EDA)应该发现每个关注期的数据集的完整性以及噪声的存在,即不带来有价值信息的噪声——数字和特殊字符。我们尽量避免数据集极度不平衡的情况,即某些期间只有很少的评论和噪声数据集包含过多的数字和特殊字符(/,@,&,;,?,等等)。

3.1. 检查数据完整性

首先,让我们检查每个期间的数据完整性。我们进行年度比较,因此汇总每年的评论:

import pandas as pd

# Calculate review frequencies by year
data['year'] = pd.DatetimeIndex(data['period']).year
rows_count = data.groupby('year', as_index=False).year.value_counts()
rows_count.columns=['year','reviews']

import matplotlib.pyplot as plt

# Generate a line plot
rows_count.plot.line(x='year',y = 'reviews')
plt.show()

一个简单的折线图展示了年度回顾频率。这里不需要进行任何图形格式化:

图像 2. 年度产品评论频率。图像来源于作者。

数据集没有严重不平衡。我们每年有大约 200 条以上的评论,这使得数据集适合情感和 n-gram 分析。

3.2. 计算字母与其他字符的比率

接下来,让我们检查数据是否主要由数字和特殊字符组成,这可能会偏向后续的文本挖掘过程。

import re

# Convert product reviews to a string
text = pd.Series.to_string(data['text'], index = False)

# Remove newline characters
text = re.sub('\n', '', text)

# Calculate sum of numbers, letters, and spaces
numbers = sum(c.isdigit() for c in text)
letters = sum(c.isalpha() for c in text)
spaces  = sum(c.isspace() for c in text)
others  = len(text) - numbers - letters - spaces

我们计算清洁度脏污度(相反的度量),它们显示了字母与其他字符(数字、空格和特殊字符)的比率:

# Calculate metrics
dirtiness = ((numbers + others) / len(text)) * 100
cleanness = ((letters + spaces) / len(text)) * 100

print(dirtiness)
print(cleanness)

打印输出为:

  • 脏污度为 8.77%

  • 清洁度为 91.22%

数据中大约有 9%的数字和特殊字符。这种噪声量不应在后续阶段偏向情感分析。

4. 客户满意度测量

文本挖掘尝试找出:(1)客户情绪随时间的变化情况,以及(2)哪些因素导致了客户满意度的变化。我们将使用Arabica,这是一个用于时间序列文本挖掘的 Python 库,来探索这两点。

EDIT Jul 2023: Arabica 已更新。请查看 文档 以获取完整参数列表。

4.1. 情感分析

Arabica 提供 coffee_break 模块用于情感和断点分析。其文档提供了有关情感评估模型和方法的更多细节。

这段代码从标点符号和数字中清理数据,移除其他冗余字符串(
”、 “/n”
“Another Long String” 作为示例),计算每条评论的情感,按年汇总情感,并识别情感时间序列中的两个主要断点:

from arabica import coffee_break

coffee_break(text = data['text'],
             time = data['period'],
             date_format = 'us',     # Use US-style date format to read dates
             preprocess = True,      # Clean data - digits and punctuation
             skip = ['<br />',       # Remove additional stop words
                     '/n',
             'Another Long String'],
             n_breaks = 2,           # 2 breakpoints identified
             time_freq = 'Y')        # Yearly aggregation

带有情感和断点的折线图:

图片 3. 带有断点的情感分析。图片来源:作者。

我们可以看到情感随时间的波动。这些变化并不迅速,因为情感波动在 [0.6: 0.7] 之间。情感值越高,客户对产品的评价越好(反之亦然)。

在 1991 年和 2000 年,情感中出现了正向和负向断点。在下一阶段,让我们检查造成这些变化的原因。

4.2. 驱动客户满意度的因素

我们将使用 cappuccino 模块的 n-gram 频率热图来直观地推导出影响客户情感的因素。请阅读文档以获取更多技术细节。

这段代码绘制了每年最频繁的八个双词组(即两个连续的词)的热图。数据预处理包括清理英文停用词、数字和三个不需要的字符串(
”、 “/n”
“Another Long String”)。标点符号和空行会被自动移除:

from arabica import cappuccino

cappuccino(text = data['text'],
           time = data['period'],
           date_format = 'us',       # Use US-style date format to parse dates
           plot = 'heatmap',
           ngram = 2,                # N-gram size, 1 = unigram, 2 = bigram
           time_freq = 'Y',          # Aggregation period, 'M' = monthly, 'Y' = yearly
           max_words = 8,            # Display 8 most frequent bigrams for each period
           stopwords = ['english'],  # Remove English stopwords
           skip = ['<br />',         # Remove additional stop words
                   '/n',
           'Another Long String'],
           numbers = True,           # Remove numbers
           lower_case = True)        # Lowercase text

双词组热图:

图片 4. 双词组热图。图片来源:作者。

双词组频率表明:

  • 2000 年代下降(缺少 “would recommend”、 “fits perfectly”“fit perfect” 在前 8 名中),以及 “wide foot”“size fit” 的高频率表明,我们可能在向不适合他们脚尺寸的客户销售产品。请注意,我们已移除像 “don’t” 这样的停用词。

结论

这种类型的文本挖掘分析应有助于做出合格的数据驱动决策,并提高我们产品和服务的质量。根据分析结果,我们确定一套关于可能存在问题的假设以及改进空间。

从本文的文本挖掘分析中,我们可以看到产品质量潜在的问题。产品与我们客户当前的需求不太匹配。收到 2001 年的评论后,分析师应制定一套问题假设,例如:

  • 新鞋穿着不舒适。

  • 我们不销售宽脚鞋,但客户需要这些鞋。

  • 新产品生产存在质量问题。

由于你比任何人都更了解你的产品,许多其他潜在问题假设会出现。其他部门(市场营销、客户关怀、物流等)应找出问题所在,然后采取具体改进措施并解决问题。响应,当然,取决于公司的规模和结构以及其他特定公司的因素。

带有代码和数据的 Jupyter Notebook 在我的 GitHub 上。

你喜欢这篇文章吗?你可以邀请我 喝咖啡 并支持我的写作。你也可以订阅我的 电子邮件列表 以获取新文章的通知。谢谢!

参考文献

Baker, M., Wurgler, J., 2006. 投资者情绪与股票收益的横截面。金融杂志 61 (4)。

Eachempati, P., Srivastava, P. R., Kumar A., Munoz, J., Dursun D., 2022. 客户情感能否影响公司价值? 一种综合文本挖掘方法。技术预测与社会变革 174 (1)。

Karim, B., 2011. 企业名称变更与股东财富效应:法国股票市场的实证证据。资产管理杂志 12 (3)。

Merrin, R. P., Hoffmann, A. O., Pennings, J. M., 2013. 顾客满意度作为对抗情感股票价格修正的缓冲器。营销信函 24 (1)。

客户细分:不仅仅是聚类

原文:towardsdatascience.com/customer-segmentation-more-than-clustering-a7226a9ff138

一个帮助你的客户细分被业务采纳的框架

Guillaume ColleyTowards Data Science Guillaume Colley

·发表于 Towards Data Science ·14 分钟阅读·2023 年 12 月 15 日

--

作者提供的图片

当数据科学团队需要构建客户细分模型时,通常是业务的要求,或者更少见的是数据科学家主动提出的决策。在这两种情况下,需求是相同的:对客户基础有更深入的理解,以便对每个细分进行更精细、差异化的决策。

然而,我看到许多聚类工作未被业务采用,因为产生的细分结果没有引起共鸣或无法被业务利益相关者采取。在这篇文章中,我将概述关键步骤和策略,以最大化业务对你细分结果的采纳。

  • 工作声明

  • 整理你的数据

  • 确定你的算法

  • 理解你的聚类

  • 多少个聚类?

  • 检查稳定性

  • 转化为业务规则(或不转化)

由于本文关注的是“应用数据科学”而非“技术机器学习”,因此不会深入探讨各种聚类算法的复杂细节,因为有丰富的资源可以让那些寻求更深理解的人了解(herehere)。

言归正传,让我们开始吧!

图片由 Ralph Katieb 提供,来源于 Unsplash

1 — 工作声明:目标、维度、时机

与每一项工作一样,你需要在深入技术工作之前回答一些重要问题。

细分的业务目标是什么?

→ 确定我们试图了解的客户信息。

有许多不同的方法来观察客户,例如,我们可以研究主要的交易行为(例如在零售中:是否具有季节性,他们的产品种类、交易的价值和频率……),或者我们可以研究他们购买的产品类型(例如在食品中:他们是购买便利产品还是新鲜的大包装家庭装、优质和有机产品……),或者我们可以研究客户的参与度(例如在银行中:拥有多少基本服务,他们拥有多少资产,以及与银行的活跃度)。

对于细分而言,不要试图一次性回答所有问题。与其有一个包罗万象的细分,不如构建(并向业务传达)几个以不同方式观察客户的细分,然后在需要时将它们结合起来。

你感兴趣的维度是什么?

→ 确定你希望将细分集中于哪些概念

一旦你知道你想通过细分回答什么样的问题,你需要确定你的细分将基于哪些维度。作为一个具备商业敏锐性的数据科学家,你应该已经有了一个大致的想法——但在这个步骤中,重要的是要与相关利益相关者沟通,并利用他们的专业知识。这可以确保细分将反映不同团队的需求和视角,同时在整个开发过程中获得利益相关者的支持。

在这个上下文中,我所指的“维度”并非具体的变量或数据元素。我提到的维度体现了诸如“活动”、“忠诚度”、“风险”和“价值”等抽象概念。正式化这些维度至关重要——它们将是你的业务利益相关者能够理解的语言。

然后你需要找到能代表这些维度的数据元素,例如:

  • 活动 → 过去一个月的交易数量

  • 忠诚度 → 在银行的年数

  • 风险 → 最新信用评分

  • 价值 → 过去 12 个月的总支出

你想使用什么时间范围?

→ 数据将基于什么时间段来代表你的客户?

是一个月?一周?一年?这取决于数据的性质、频率以及是否存在季节性。例如,超市可能通过 2 到 3 个月的购买数据就能收集到足够有意义的客户信息。另一方面,在线服装零售商面临强烈的年度季节性(黑色星期五+假期赠品),需要考虑 12 个月的时间段。

→ 你会多久重新评分客户(分配细分)?

为了让细分保持可操作性,分配给特定客户的细分必须准确反映其行为或资料的最新变化。重新计算这些细分的频率取决于相关维度变化的速度。对于每周多次购物的超市来说,每周重新计算细分可能更有意义——尤其是当这与每周的营销活动和决策相一致时。对于每年仅购物几次的在线零售商来说,每月更新一次细分可能更合适。

→ 你会多频繁地重新训练你的模型?

你的细分模型的目标是捕捉客户的档案,这些档案在时间上是建立和稳定的,因此通常不需要频繁更新模型。

除非有明显和近期的行为变化或关键数据点的可用性可以提升模型,否则你的细分模型可以有效使用较长时间,可能长达几年。

照片由Alex Block提供,来源于Unsplash

2 — 整理你的数据

你可能已经意识到数据准备对聚类项目的重要性,许多资源可供参考,包括这里这里。以下是进行归一化之前的两个有益的变换:

抖动

图片由作者提供

这种变换对小范围的整数数据点最为有用。例如,考虑变量“银行服务数量”,通常范围从 1 到十几。实施这种变换将增强你聚类的可视化效果。

通过在原始值上添加]-0.5,0.5[的随机噪声来进行抖动:

df=df.assign(Jittered_Services=df['Services']
        +np.random.normal(scale=0.49, size=len(df)))

对数变换

表示财务值的数据通常呈现偏斜分布,其中少数个体拥有大量资产或债务。偏斜数据可能对机器学习模型的性能产生不利影响。因此,应用对数变换有助于减轻偏斜的影响并提高模型的稳健性:

图片由作者提供

import numpy as np

df= df.assign(Log_Mortgage_Balance=np.log(df['Mortgage_Balance']))

这种变换——如同抖动——显著提高了图表的可读性,尤其是在你熟悉对数刻度的解释之后。

3 — 确定你的算法

这篇文章并不是深入探讨各种聚类算法,因为外面有很多很好的资源,特别是Scikit Learn

来源:Scikit-Learn(BSD 许可证)

进入下一步骤前,我们假设你已经使用最佳聚类算法和优化参数运行完毕,现在所有客户都已标记了特定的集群编号。

图片由作者提供

4 — 了解你的集群

不幸的是,聚类算法不懂商业语言(至少现在还不懂——我在看你们,LLMs)。

算法本质上使数据能够传达这样的信息:“集群 X 中的客户在彼此之间的共性比与其他集群的客户更多。” 你需要深入探讨细节并解读定义集群 X 的特定行为或特征。

集群画像可以通过描述性方法或预测性方法实现。

集群画像——描述性方法

我发现同时使用下面这两个简单工具对于获得集群的商业理解效果很好。

汇总统计与条件格式

利用带有条件格式的汇总统计涉及将集群的汇总统计并排展示,并通过格式化突出高低点。这种方法使新出现的特征变得明显。在下面的示例中,我使用了中位数,但你可以选择你喜欢的汇总统计:

图片由作者提供

在上述示例中,很明显集群“青色”和“红色”具有相似的特征,显示出最低的 Var2、Var5、Var6 和 Var7。它们之间的区别在于 Var4 和 Var8。

此外,集群“深蓝色”在 Var2 上具有最高值,而将集群“黑色”与其他集群区分开来的关键变量是 Var3,其中它表现出最低值。

所有输入变量组合的散点图

生成所有输入变量组合的散点图提供了一个视觉表示,帮助突出区分不同集群的维度。这种方法允许清晰地识别出哪些维度分隔了特定集群。

图片由作者提供

散点图显示,Var8 是区分集群“红色”和“绿色”与其他集群的因素(右下角图)。同时,底部左侧图中确认了 Var3 作为区分集群“黑色”的标志。

这个过程是迭代的:在汇总统计和散点图之间切换,你可以完善对集群的理解,并提取出获得深入了解其特征所需的见解。

早期命名你的集群

随着你在这个分析过程中的进展——考虑为你的聚类分配描述性名称。这将迫使你在此阶段找出每个聚类的差异特征,并有助于后续的利益相关者认可。在后续阶段(第 5 步和第 6 步之后),你甚至可以邀请利益相关者参与一个有趣的活动来命名聚类。这不仅会产生与业务相符的名称,还能帮助利益相关者接受这些划分。

成功找到有效的名称是将你的聚类从单纯的分组提升到有意义的业务细分的关键。

聚类分析——预测方式

另一种对聚类进行分析的方法是利用多分类预测模型。如果将聚类数量视为分类变量进行预测,可以使用这些变量作为特征来拟合模型。

查看特征重要性将告知你哪些变量影响了每个聚类。

如果你拟合一个相对简单的决策树,检查分支和树结构将准确告诉你使用了哪些变量和分割值。这是一个很好的示例。

在实践中,我尚未采用预测方法,因为我已经通过描述性方法获得了对聚类概况的良好理解。此外,利用汇总统计数据对于向利益相关者传达聚类差异至关重要,因此它们是该方法的重要组成部分。

5 — 多少个聚类?

我们都熟悉肘部法则和轮廓法

但是如果这些方法建议 2 个聚类?或 18 个?这些配置对业务是否有用或可操作?

聚类数量过少(3 个或更少)可能无法提供我们最初所期望的客户深度理解。另一方面,聚类数量过多(9 个或更多)可能使得推广、解释和实施变得困难。

我发现有用的是从上述方法开始,实际上执行算法并尝试不同数量的聚类。如果肘部法则给出了 6 个聚类(并且你已经对它们进行了分析以理解它们),那么 5 个聚类会是什么样的?7 个聚类?8 个聚类?哪些聚类被合并或分裂(表明行为更接近),哪些聚类保持不变(表明行为更独特)?

这种迭代探索有助于微调聚类解决方案,并提供对数据中潜在模式的细致理解。

在下面的示例中,从 4 个集群过渡到 5 个集群并不会影响黑色和蓝色集群,但它揭示了一个新集群,黄色集群,位于红色和绿色之间。问题是:从业务角度来看,将这些客户分为三个组而不是两个是否有意义?

作者提供的图片

这个决定取决于具体的业务目标和集群的可解释性。评估黄色集群中行为差异的显著性以及细化是否带来了可操作的洞察是关键考虑因素。

使用更多集群数量的算法,比如 12 个或 15 个,可以探索是否会出现与业务相关的独特行为。

识别这些只有在使用更多集群时才会显现的小但潜在重要的集群可能是有价值的。在这种情况下,你可以根据从细分分析中得出的业务规则将这些客户分配到特定的细分市场(见下文第七部分)。

在下面的示例中,一个对业务至关重要的客户档案只有在数据被拆分为 11 个细分(左下角的浅绿色集群)时才会被聚类揭示。由于其余的 11 个细分过于细化,最终的细分可能是一个 6 集群细分加上这个非常重要的利基客户细分:

作者提供的图片

对你的集群保持灵活性

聚类是一个重要的工具——但它仍然只是一个工具,不一定要当作唯一依据。

将业务知识注入到你的集群中是确保它们与行业细节或特定用例紧密对齐的宝贵步骤。

如果你对整体集群感到满意,但发现有两个集群实际上传达了相同的业务含义,可以考虑将它们合并为一个。例如,你可能会发现两个具有非活跃档案的不同集群——一个没有交易,另一个交易非常少。从业务角度来看,这两个集群可能需要采取相同的措施,从而 justify 它们的合并。这种人工干预可以精炼聚类结果,提高其在实际业务场景中的相关性和适用性。

在某些情况下,定义集群的属性可能在概念上是适当的,但具体定义可能与既定标准不一致。例如,在银行细分中以信用评分来衡量“风险”维度。虽然这可能导致一个“高风险”集群,但客户成为该组的一部分的标准可能不完全符合风险客户的严格商业定义,比如“信用评分<640”。在这种情况下,考虑合并或拆分集群以更好地符合定义的业务规则可能是有益的。

图片由作者提供

6 — 检查稳定性

想象一下你得到了一种极好的细分,深化了你组织对客户的理解,并用于定向和个性化营销。然而,仅仅几个月后,某些细分的规模大幅增加,而其他细分几乎减少到可以忽略的程度,使得细分失效。细分在时间上不稳定!

如果你的数据存在季节性,请确保你在涵盖季节性事件前、中、后的数据集上实施了细分。为了全面评估,考虑将细分应用于整年甚至两年的数据。

下面是评估细分稳定性的两种方法:

细分稳定性

从高层次来看,检查每个细分的规模及其随时间的变化是有价值的

  • 细分规模在不同时间段是否发生了变化?

  • 你对观察到的尺寸变化感到满意吗?

虽然某些波动可能是合理的并且具有特定背景(例如在超市环境中,可能“传统烹饪”细分在感恩节和假日期间比平时更大),但细分规模的剧烈或长期变化可能需要重新评估你的方法。在这种情况下,重新审视你的输入数据的时间范围并扩展它可能是必要的,以确保细分的稳定性和可靠性。

客户迁移

研究客户迁移到个体层面提供了更详细的视角:

  • 每个个体客户在特定时期内(例如一年)变化细分的次数是多少?

  • 哪些具体细分经历了最多的迁移?

虽然反映出更接近行为的细分之间有限的迁移可能是可以接受的,但大量迁移可能表明不稳定。在这种情况下,可能需要扩展客户数据收集的时间窗口,以捕捉更稳定的概况。

7 — 转化为业务规则(或不转化)

在一个商业环境中,如果利益相关者可能更倾向于透明度而非黑箱模型,一种可行的方法是将集群或细分转化为明确的业务规则。

一旦你了解了细分的概况,你可以在你的维度空间中描绘出最接近这些细分的界线。这些界线实际上成为定义和描述每个细分的业务规则。将这些规则转化为业务规则不仅提升了解释性,还为利益相关者提供了一个清晰易懂的细分识别和分析框架。

图片由作者提供

在上述提供的示例中,业务规则可以表述如下:

绿色集群:Var8 < 0.032 且 Var5 < 2.4

红色集群:Var8 ≥ 0.032 且 Var5 < 3

图片由作者提供

最终步骤 - 宣传你的细分

如果你已经达到这个阶段,说明你成功识别了具有不同特征和行为的细分群体,并且这与你的业务参数一致。细分群体的特征一致性及其对业务的相关性很明显,你已经确认了这些细分群体在时间上的稳定性。

你也很可能帮助了你的直接业务利益相关者,根据每个细分群体量身定制他们需要采取的行动。

现在,是否还有其他细分的应用场景可以带来业务价值?

  • 仪表盘 — 是否有一些指标和关键绩效指标可以从细分的角度进行查看?

  • 报告 — 是否有每月或每季度的业务报告和分析,细分可以为客户基础提供更深入的见解?

  • 临时分析 — 是否有任何分析使用了细分,能够揭示更细化的结果或建议?

是否有不同的渠道可以利用你的细分来个性化某些行动?

  • 电子邮件或推送通知活动

  • 电话营销活动

  • 首页着陆体验

  • 付费搜索和付费媒体渠道

结论

我希望现在已经明确,有效的客户细分不仅仅是聚类算法的技术层面。

细分模型的成功可以从以下几个方面总结:

  • 与业务目标的一致性,

  • 定义维度的清晰性

  • 对时机的深思熟虑

  • 适应业务目标的集群灵活性

  • 时间上的稳定性

最终,成功的细分创建了一个全面且有影响力的框架,用于理解和与不同的客户群体互动。

我很想知道你是否觉得这个框架有用。你在开始客户细分项目时认为还需要注意哪些关键点?我遗漏了什么?你会有什么不同的做法?

参考文献:

[1] R. Yehoshua, 《聚类介绍》(2023),medium.com/ai-made-simple/introduction-to-clustering-2ffc22673b5a

[2] T.K. Aslanyan, 《Python 中聚类的全面指南》(2023),medium.com/lunartechai/a-comprehensive-guide-to-clustering-in-python-f9fb36a94a05

[3] DisplayR, 《基于集群的细分的数据准备》(2017),docs.displayr.com/wiki/Data_Preparation_for_Cluster-Based_Segmentation

[4] P. Huilgol, 《特征变换和缩放技术以提升模型性能》(2020),www.analyticsvidhya.com/blog/2020/07/types-of-feature-transformation-and-scaling/

[5] Scikit Learn, 聚类方法概述(2023),scikit-learn.org/stable/modules/clustering.html

[6] I. Cohen, 可解释人工智能(XAI)与 SHAP - 多类分类问题(2021),towardsdatascience.com/explainable-ai-xai-with-shap-multi-class-classification-problem-64dd30f97cea

[7] E. Zuccarelli, 可解释的聚类(2020),towardsdatascience.com/interpretable-clustering-39b120f95a45

[8] R. Khandelwal, K-Means 聚类:寻找最佳簇的技术(2021),pub.towardsai.net/k-means-clustering-techniques-to-find-the-optimal-clusters-7eea5431a4fb

我其他的应用数据科学文章:

## 解码数据科学家层级:从初级到高级——什么让他们与众不同?

揭示了初级、中级和高级数据科学家的工作期望范围

towardsdatascience.com ## 提升你的数据科学职业生涯:如何成为高级数据科学家

作者概述了五种策略,这些策略将把你的数据科学实践提升到高级角色。

towardsdatascience.com ## 促进数据科学家/分析师参与的五个想法,而不会在会议中窒息

作者分享了他们成功实现这一平衡的策略

towardsdatascience.com

使用 Matplotlib 自定义颜色图

原文:towardsdatascience.com/customize-colormaps-with-matplotlib-df5b37d14662

快速成功的数据科学

使你的颜色与主题相匹配

Lee VaughanTowards Data Science Lee Vaughan

·发表于 Towards Data Science ·10 分钟阅读·2023 年 12 月 6 日

--

一颗橡子及其颜色图(作者提供)

专业图形的一个特征是贯穿其中的主题,它将一切联系在一起。这包括字体、符号,最重要的是颜色调色板。

下面是一个来自美国国家公园管理局的例子。注意这些和谐的土壤色调如何协作以唤起户外的感觉。这个图形不仅仅是传达信息,它是以风格传达信息的!

美国国家公园管理局信息图(感谢 nps.gov

Python 的 Matplotlib 绘图库使用颜色图来定义可视化的颜色方案。颜色图是用于将像素数据映射到实际颜色值的颜色数组。

虽然 Matplotlib 提供了许多内置的颜色颜色方案,但它们可能无法覆盖所有可能的场景。有时你可能希望将颜色特别定制以符合某个特定的主题或概念。

在这个快速成功的数据科学项目中,我们将探讨如何选择自定义颜色并将其转化为可以在 Matplotlib、seaborn、pandas、geopandas 和其他兼容 Python 的绘图库中使用的颜色图。然后,我们将使用这些颜色图绘制纽约市橡树的位置。

橡子,橡子,橡子!

尽管今年德州发生了严重的干旱,但我们仍然到处都是橡子。除了数量丰富外,它们还特别有色彩,拥有只有大自然知道如何制造的拿铁色和紫棕色。这个星期在欣赏一个时,我不禁想知道它会成为一个优秀的色彩图。作为一个 Python 爱好者,我立即付诸实践。

橡子(照片由Ksenia MakagonovaUnsplash拍摄)

如果你来自一个没有橡子的地方,它们是橡树生产的坚果。

捕捉橡子的颜色

捕捉物体颜色所需的只是数字照片。虽然有很多关于如何真实捕捉颜色的科学(你可以在这里找到一些技巧),我只是拍了一张在阴天的橡子的手机照片。

我随后使用Image Color Picker从图片中提取颜色。这个免费的应用程序允许你上传图片文件并使用光标选择和采样像素颜色。对于这个项目,我们将使用 RGB(红-绿-蓝)值,范围从 0 到 255。

Image Color Picker屏幕(作者提供)

为了构建橡子的色彩图,我进行了两组测量。第一组包括四个测量点,从深棕色的尖端到蛋白色的底部均匀间隔。第二组是以相同方式进行的五个测量点。每个测量的值直接从应用程序的 RGB 输出框中使用“复制”图标复制出来。

颜色样本位置(作者提供)

我进行了组测量,以查看是否有差别。根据采样的结果,没有差别。

色彩图代码

以下代码,使用 JupyterLab 编写,利用 Matplotlib 创建了离散(分类)和连续色彩图。然后使用热图显示测试这些色彩图。

创建色彩图

基于插值方法,Matplotlib 中有两种类型的色彩图:

一个列举色彩图是一个颜色列表。它是一个离散色彩图,具有预定义的颜色集,并且不在颜色之间进行插值。

一个线性分段色彩图使用插值在存储在字典中的颜色锚点之间。这创建了一个连续色彩图*。

离散色彩图适合用于由名称或符号表示的分类数据。连续色彩图在颜色之间平滑过渡。它们通常用于表示范围的值,比如绘制温度或降水量数据时。

创建列出颜色图

我们将使用 个橡实测量值来生成列出颜色图。代码如下:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

# Normalize the RGB colors (RGB color tuples in comments):
colors = [(42/255, 34/255, 31/255),    # (42, 34, 31)
          (82/255, 59/255, 53/255),    # (82, 59, 53)
          (112/255, 69/255, 37/255),   # (112, 69, 37)
          (187/255, 164/255, 132/255)  # (187, 164, 132)
         ]

# Create a ListedColormap (discrete colors):
custom_cmap_discrete = ListedColormap(colors)

# Display a colorbar with the custom colormap:
fig, ax = plt.subplots(figsize=(6, 1))
plt.imshow([[i for i in range(len(colors))]], 
           cmap=custom_cmap_discrete, 
           aspect='auto')
plt.xticks([]), plt.yticks([]);  # Turn off tickmarks
# plt.show()

我们只需要 Matplotlib 库即可,但我们添加了 NumPy 以生成一些虚拟测试数据。

Matplotlib 的 [ListedColormap()](https://matplotlib.org/stable/users/explain/colors/colormap-manipulation.html#listedcolormap) 类将值在 0 和 1 之间映射到颜色。这些颜色存储在 .colors 属性中。

因为 ListedColormap() 类使用 0 到 1 之间的值,我们必须将橡实的 RGB 值除以 255 以规范化输出(Python 从 0 开始计数,所以 256 种颜色的最大值是 255)。之后,我们只需将这个类的 colors 列表传入以创建自定义颜色图。

为了将颜色图作为颜色 查看,我们可以使用 plt.imshow() 将其绘制为图像。结果如下:

离散的颜色图(作者)

请注意,你也可以通过提供 Matplotlib 官方的 颜色名称 列表来创建颜色图。例如:

cmap = ListedColormap(["darkorange", "gold", "lawngreen", "lightseagreen"])

欲了解更多信息,请参见官方 文档

测试列出颜色图

为了在 Matplotlib 图中测试列出颜色图,我们将使用从随机数据集构建的热图。代码如下:

# Create randomized data with NumPy:
data = np.random.rand(10, 10)

# Plot using the custom colormap:
plt.imshow(data, cmap=custom_cmap_discrete)
plt.colorbar();
# plt.show()

结果如下:

使用离散颜色图构建的热图(作者)

我说得对;橡实确实是很棒的颜色图!这个图看起来像是一个美丽的“屠夫块”切菜板。

创建线性分段颜色图

我们现在将使用 个橡实测量值来生成线性分段颜色图。代码如下:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap

# Normalize RGB colors (RGB color tuples in comments):
colors = [(42/255, 34/255, 31/255),     # (42, 34, 31)
          (82/255, 59/255, 53/255),     # (82, 59, 53)
          (112/255, 69/255, 37/255),    # (112, 69, 37)
          (167/255, 143/255, 105/255),  # (187, 164, 132)
          (177/255, 166/255, 150/255)   # (177, 166, 150)
         ]

# Create a list of positions for each color in the colormap:
positions = [0.0, 0.25, 0.5, 0.75, 1.0]

# Create a LinearSegmentedColormap (continuous colors):
custom_cmap = LinearSegmentedColormap.from_list('custom_colormap', 
                                                list(zip(positions, colors)))

# Display a colorbar with the custom colormap:
fig, ax = plt.subplots(figsize=(6, 1))
plt.imshow([[i for i in range(256)]], 
           cmap=custom_cmap, 
           aspect='auto', 
           vmin=0, 
           vmax=255)
plt.xticks([]), plt.yticks([]);
# plt.show()

在这种情况下,我们导入了 Matplotlib 的 [LinearSegmentedColormap()](https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.LinearSegmentedColormap.html#matplotlib.colors.LinearSegmentedColormap) 类,而不是 [ListedColormap()](https://matplotlib.org/stable/users/explain/colors/colormap-manipulation.html#listedcolormap) 类。这个类通过在 RGB(A) 值之间的 锚点 进行 插值 来指定颜色图。也就是说,它基于查找表生成颜色图 对象,使用 线性 分段。它为每个主色生成查找表,0–1 域被划分为任意数量的分段。

结果如下:

连续颜色图(作者)

这段代码的一个关键部分是 positions 变量。请注意,我使用了 均匀间隔 的分段(例如 0.25 到 0.75 到 1.0),但你可以“拉伸”或“压缩”一个区间。例如,要制作这篇文章标题图中的颜色图,我使用了 [0.0, 0.25, 0.65, 0.75, 1.0] 定义的非对称分段。

测试线性分段颜色图

为了在 Matplotlib 图中测试线性分段色彩图,我们将再次使用从随机数据集中构建的热图。代码如下:

# Create randomized data:
data = np.random.rand(10, 10)

# Plot using the custom colormap:
plt.imshow(data, cmap=custom_cmap)
plt.colorbar();
# plt.show()

这是结果:

使用连续色彩图构建的热图(作者提供)

如果你将此热图与使用列出的色彩图生成的热图进行比较,你会发现颜色变化更多,因为线性分段色彩图是连续的,并允许使用多于四种颜色。

好了,这就是你需要了解的关于使用 Matplotlib 构建自定义色彩图的大部分内容。有关更多细节,请查看 文档

纽约市橡树的绘图

现在,让我们使用带有实际地图的连续色彩图。为了体现橡子主题,我们将绘制纽约市橡树的位置。

尽管在城市中识别出了至少十三种橡树,但我们将使用四种类型的子集:英国橡树舒马德橡树针叶橡树白橡树。我们需要限制类型,因为我们的色彩图虽然吸引人,但对于解决大量类别并不实用。

数据集

树木位置来自 NYC OpenData 门户。该门户提供了 纽约市机构及其他合作伙伴发布的免费公共数据。我已将数据过滤到四种橡树类型的名称和经纬度位置,并将其存储为 CSV 文件,保存在此 Gist 中。

我们将使用 pandas 加载此文件,然后使用 geopandas 在地图上投影位置。Geopandas 生成一个 GeoDataFrame,它类似于 pandas 的 DataFrame,但具有一个特殊的“geometry”列,将几何类型(如“POINT”)与可绘制的坐标绑定在一起。

import matplotlib.pyplot as plt
import pandas as pd
import geopandas as gpd

# Load the CSV file into a pandas DataFrame:
df = pd.read_csv('https://bit.ly/3t3Vbx7')

# Create a GeoDataFrame with Point geometries:
gdf = gpd.GeoDataFrame(df, 
                       geometry=gpd.points_from_xy(df['longitude'], 
                                                   df['latitude']), 
                       crs='EPSG:4326')

gdf.head(3)

GeoDataFrame 的前三行(作者提供)

绘制位置地图

接下来,我们将使用 geopandas 的内置绘图功能绘制点,该功能基于 Matplotlib。Geopandas 还配备了实用的内置数据集,如“nybb”用于“纽约市区边界”。我们将以 olivedrab 绘制这些市政边界,以匹配整体颜色主题。

我们还将设置树木位置 GeoDataFrame (gdf) 的地理坐标参考系统crs)为用于区界 GeoDataFrame (gdf_nyc) 的系统。这确保了两个数据集的一致投影。

# Plot tree locations along with NYC borough boundaries:
path_to_data = gpd.datasets.get_path("nybb")
gdf_nyc = gpd.read_file(path_to_data)

# Extract the boundaries GeoSeries:
borough_boundaries = gdf_nyc.boundary

# Plot the boundaries with no fill:
ax = borough_boundaries.plot(figsize=(9, 9), 
                             linewidth=1, 
                             edgecolor='olivedrab')

# Convert the tree gdf crs to the boroughs crs:
gdf = gdf.to_crs(gdf_nyc.crs)

# Plot the tree locations in the same figure:
gdf.plot(column='common', 
         ax=ax, 
         legend=True, 
         markersize=1, 
         cmap=custom_cmap)

# Customize the plot:
plt.title('NYC Selected Oak Tree Species Distribution')
plt.xticks([]), plt.yticks([]);

# Show the plot
# plt.show()

这是结果:

纽约市橡树位置的地图(作者和 NYC OpenData 提供)

需要注意的一点是,即使我们绘制了离散数据(树的名称),我们也能够使用连续色图。如果你使用离散色图,你会得到稍微不同的结果,因为两个色图的锚点不同。

无论你使用哪种色图,你都会看到针叶栎是纽约市主要的橡树类型,至少在已绘制的物种中是如此。

绘制 KDE 地图

正如你在之前的图表中可能注意到的,我们的色图范围狭窄,使得很难在视觉上解析多个离散类别。它更适合用于连续分布,如 KDE 地图所产生的。

KDE核密度估计地图是一种可视化点分布(如橡树)在地理区域中分布的方法。它基于一种统计技术,用于估计一组样本的潜在连续概率分布。

由于 KDE 地图提供了平滑出现密度表示,它非常适合突出显示高浓度或低浓度区域。通常,较高的浓度由较暗较暖的颜色表示。

要制作 KDE 地图,我们需要反转我们的色图,使得较暗的颜色表示较大的值,并使用Geoplot来制作地图 Geoplot 是一个高级开源地理空间绘图库,声称是“地理空间中的 seaborn”。这意味着它建立在底层库上,如 GeoPandas,使得映射变得简单。

你可以用以下命令安装 Geoplot(conda 或 pip):

conda install -c conda-forge geoplot

pip install geoplot

这是代码。请注意,我们在以前的工作基础上构建,而不是重新加载数据库。

from matplotlib.colors import LinearSegmentedColormap
import geoplot as gplt
import geoplot.crs as gcrs

# Reverse colormap so darkest = most dense for KDE plot:
colors = [(177/255, 166/255, 150/255),  # (177, 166, 150)
          (167/255, 143/255, 105/255),  # (187, 164, 132)
          (112/255, 69/255, 37/255),  # (112, 69, 37)
          (82/255, 59/255, 53/255),   # (82, 59, 53)
          (42/255, 34/255, 31/255)  # (42, 34, 31)
         ]

# Create a list of positions for each color in the colormap
positions = [0.0, 0.25, 0.50, 0.75, 1.0]

# Create a LinearSegmentedColormap
custom_cmap_r = LinearSegmentedColormap.from_list('custom_colormap', 
                                                  list(zip(positions, colors)))

# Get the borough boundaries:
boroughs = gpd.read_file(gplt.datasets.get_path('nyc_boroughs'))
boroughs = boroughs.to_crs('EPSG:4326')

# Reset the gdf's crs:
gdf = gdf.to_crs('EPSG:4326')

# Plot the KDE map:
ax = gplt.kdeplot(gdf, cmap=custom_cmap_r, fill=True, clip=boroughs)
gplt.polyplot(boroughs, zorder=1, ax=ax);

这是结果:

由作者和NYC OpenData提供的 4 种橡树类型的 KDE 图

现在有一张地图,让你想喝杯咖啡!

色盲的自定义色图

请注意,自定义色图可能不适合 5%到 10%患有色盲的人群。不幸的是,我们的橡子色图可能不合适,特别是在将橡树位置绘制为单个点时。

适应色盲的策略包括:

  • 使用高对比度颜色,

  • 包括图案、纹理或符号,

  • 使用单色渐变(如我们的 KDE 地图),

  • 使用颜色选择工具,如Vischeck和 Colorbrewer

要查看各种图表类型中“色盲安全”颜色的示例,请查看Matplotlib gallery中的seaborn-colorblind 样式表

总结

将你可视化的颜色映射与数据的主题匹配,可以让你的读者更感兴趣。如果 Matplotlib 提供的颜色映射不够,你可以随时生成自己的。

图像颜色提取器这样的应用程序可以帮助你从图像中提取颜色代码。Matplotlib 提供了两个类,ListedColormap()LinearSegmentColormap(),让你轻松将这些颜色代码转化为颜色映射,适用于统计图和地理空间图。

谢谢!

感谢阅读,请关注我,获取未来更多的快速成功数据科学项目。

自定义您的基于云的机器学习训练环境 — 第一部分

原文:towardsdatascience.com/customizing-your-cloud-based-machine-learning-training-environment-part-1-2622e10ed65a?source=collection_archive---------7-----------------------#2023-05-21

如何利用云的强大功能而不妨碍开发灵活性

Chaim RandTowards Data Science Chaim Rand

·

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

--

图片由 Jeremy Thomas 提供,来源于 Unsplash

基于云的机器学习(ML)服务为 AI 开发者提供了许多便利,其中可能没有比访问更多样化、完全配置、完全功能和完全维护的 ML 训练环境更重要的。例如,托管训练服务,如 Amazon SageMakerGoogle Vertex AI,使用户能够指定(1)所需的实例类型(例如,配备最新可用 GPU 的实例),(2)ML 框架及版本,(3)代码源目录,以及(4)训练脚本,并会自动启动所选实例,运行脚本以训练 AI 模型,并在完成后拆除所有内容。这些服务的优点之一是可以显著节省构建和维护自己的训练集群的时间和成本。有关云端训练的更多好处和注意事项,请参见 这里。

然而,使用预定义、完全配置、完全验证的 ML 环境的便利性也伴随着开发灵活性的潜在限制。这与可以随心所欲自由定义的本地“本地”环境相对。以下是几个示例,展示了可能的限制:

  1. 训练依赖:很容易想到那些对特定软件包(例如,Linux 包或 Python 包)有依赖的训练流程,而这些软件包可能不包括在您选择的云服务提供的预定义环境中。

  2. 开发平台独立性:您可能希望(或需要)一个独立于底层运行平台的开发环境。例如,您可能希望无论是在自己的 PC 上、在本地(“本地”)集群中,还是在云中运行,都能够使用相同的训练环境,并且无论选择哪个云服务提供商和云服务。这可以减少适应多个环境的开销,并可能有助于调试问题。

  3. 个人发展偏好:工程师(特别是有经验的工程师)对他们的开发习惯和开发环境可能非常挑剔。即使是由于开发流程的变更而可能带来的限制,无论其对团队的价值和重要性如何(例如,迁移到云端机器学习),也可能引发显著的抵触情绪。

在这篇分为两部分的博客文章中,我们将介绍一些克服基于云的训练潜在开发限制的选项。具体来说,我们将假设训练环境由一个包含 Python 环境的 Docker 镜像定义,并演示几种方法来定制环境以满足我们的特定需求。

为了演示的目的,我们将假设所选的训练服务是 Amazon SageMaker。然而,请注意,一般方法同样适用于其他训练服务,并且这种选择不应被视为对某一云服务的偏爱。最适合你的选项可能取决于许多因素,包括项目的细节、预算考虑等。

在博客中,我们将引用和演示某些库的 API 和行为。请注意,这些信息在撰写时是准确的,并可能会在未来版本的库中有所更改。在信任我们所写的内容之前,请务必参考最新的官方文档。

我想感谢 Yitzhak Levi,他的实验为这篇博客文章奠定了基础。

托管训练示例

让我们开始使用托管服务在云端进行训练的简单演示。在下面的代码块中,我们使用 Amazon SageMaker 来运行 train.py PyTorch (1.13.1) 脚本,该脚本位于 source_dir 文件夹中,并在单个 ml.g5.xlarge GPU 实例类型上运行。请注意,这是使用 Amazon SageMaker 进行训练的一个相对简单的示例;有关更多详细信息,请参阅官方 AWS 文档

from sagemaker.pytorch import PyTorch

# define the training job
estimator = PyTorch(
    entry_point='train.py',
    source_dir='./source_dir',
    framework_version='1.13.1',
    role='<arn role>',
    py_version='py39',
    job_name='demo',
    instance_type='ml.g5.xlarge',
    instance_count=1
)

# deploy the job to the cloud
estimator.fit()

为了更好地了解在云中训练时开发灵活性的潜在限制以及如何克服这些限制,让我们回顾一下使用诸如 Amazon SageMaker 的服务部署训练作业时发生的步骤。

背后的秘密

我们将在这里重点关注主要步骤;有关详细信息,请参见 官方文档。尽管我们将回顾 SageMaker API 执行的操作,但其他托管训练 API 也表现出类似的行为。

第 1 步:将代码源目录打包并上传到云存储。

第 2 步:配置所请求的实例类型。

第 3 步:将适当的 预构建 Docker 镜像 拉取到实例上。适当的镜像由训练作业的属性决定。在上述示例中,我们请求了 Python 3.9、PyTorch 1.13.1 和一个 GPU 实例类型在 us-east-1 AWS 区域,因此最终会得到 763104351884.dkr.ecr.us-east-1.amazonaws.com/pytorch-training:1.13.1-gpu-py39-cu117-ubuntu20.04-sagemaker 镜像。

第 4 步:运行 Docker 镜像。Docker ENTRYPOINT 是一个脚本(由服务定义),该脚本从云存储中下载并解压训练代码,并运行用户定义的训练脚本(下面有更多细节)。

第 5 步:当训练脚本完成时,停止并释放实例。

请注意,我们大大简化了服务执行的操作,并关注了与我们的讨论相关的那些操作。在实际操作中,还有许多其他“管理”活动在后台进行,包括 访问训练数据协调分布式训练从点中断中恢复监控和分析训练 等等。

关于 Docker 镜像

AWS 深度学习容器(DLCs)GitHub 仓库包括了由 SageMaker 服务使用的预构建 AWS Docker 镜像。这些镜像可以用来更好地理解训练脚本运行的环境。特别是,定义上述示例中使用的镜像的 Dockerfile 位于 这里。从对该文件的粗略审查中,我们可以看到,除了标准的 Linux 和 Python 包(例如,OpenSSH、pandas、scikit-learn 等),该镜像还包含几个 AWS 特定的包,例如 PyTorch for AWS 的增强版本和用于利用 Amazon EFA 的库。AWS 特定的增强功能包括用于管理和监控训练任务的特性,更重要的是,当在 AWS 的训练基础设施上运行时,用于 最大化资源利用率和运行时性能 的优化。此外,通过对 Dockerfile 的更详细分析可以明确,其创建需要细致的工作、偶尔的变通和广泛的测试。除其他考虑因素外(见下文),我们在 AWS 上训练时的首选始终是使用官方 AWS DLCs。

Dockerfile中的ENTRYPOINT 指向一个名为start_with_right_hostname.sh 的脚本。这个脚本调用了来自sagemaker-training Python 包的train.py 脚本,该脚本又调用一个解析SAGEMAKER_TRAINING_MODULE 环境变量的函数,最终运行来自sagemaker-pytorch-training Python 包的 PyTorch 特定的入口点。正是这个入口点下载源代码并启动训练,如上面第 4 步所述。请记住,我们刚刚描述的流程和我们链接的代码在撰写时是有效的,随着 SageMaker API 的演变,可能会发生变化。虽然我们已经分析了上述示例中配置的 SageMaker PyTorch 1.13 训练作业的启动流程,但同样的分析也可以应用于其他类型的基于云的训练作业。

公开访问的 AWS DLC Dockerfile 和 SageMaker Python 包源代码使我们能够了解基于云的训练运行时环境。这使我们能够更好地理解在云环境中训练的能力和局限性,并且正如我们在接下来的部分中将看到的,了解我们可以用来引入更改和自定义的工具。

自定义 Python 环境

自定义你的训练环境的第一种也是最简单的方法是添加 Python 包。训练脚本通常依赖于 Python 包(或特定版本的包),这些包不包含在云服务默认 Docker 镜像的 Python 环境中。SageMaker API 通过允许你在传递给 SageMaker 估算器的 source_dir 文件夹的根目录中包含一个 requirements.txt 文件来解决这个问题(请参见上面的 API 调用示例)。在下载和解压源代码(上面的步骤 4)之后,SageMaker 脚本将搜索 requirements.txt 文件,并使用 pip 包安装程序安装其所有内容。有关此功能的更多详细信息,请参见 这里。其他云服务也包括类似的机制来自动安装包依赖。通过在你自己的训练脚本开始时简单地包含一个指定的包安装程序,也可以轻松实现这种解决方案(尽管在运行多个进程(例如,使用 MPI)在单个实例上时,需要仔细协调)。

虽然这个解决方案简单易用,但确实存在一些限制:

安装时间

需要考虑的一点是安装包依赖所需的开销(时间)。如果我们有一个很长的依赖列表,或者其中任何依赖项需要很长时间才能安装,以这种方式自定义环境可能会在不合理的程度上增加总体时间和成本,这时我们可能会发现我们将讨论的其他方法更适合我们的需求。

访问存储库

另一个需要注意的是,这种方法依赖于访问 Python 包存储库。如果你的训练环境的网络配置允许自由访问互联网,那么这不是问题。然而,如果你在私有网络环境中进行训练,例如 Amazon Virtual Private Cloud(Amazon VPC),那么这种访问可能会受到限制。在这种情况下,你需要创建一个私有包存储库。在 AWS 中,可以使用 AWS CodeArtifact 或创建一个私有 PyPI 镜像来完成此操作。(虽然用例有所不同,但你可能会发现 这里这里 描述的方案很有帮助。)如果没有可访问的包存储库,你需要考虑我们将介绍的其他自定义选项。

Conda 包依赖

我们提出的定制解决方案对于所有依赖项都是 pip 包的情况非常好。但是,如果我们的依赖项仅作为conda包存在呢?例如,假设我们希望使用s5cmd来加速从云存储中传输数据。当我们完全控制训练环境时,可以选择使用 conda 构建 Python 环境并自由安装 conda 包依赖项。如果我们使用由云服务提供的 Docker 容器,我们无法控制 Python 环境的创建,可能无法享受相同的自由。事实上,尝试在 SageMaker 上通过subprocess安装s5cmdconda install -y s5cmd)会失败。即使我们找到一种方法使其工作,以这种方式安装包可能会导致不希望的副作用,例如覆盖其他 AWS 优化包或普遍破坏 conda 环境。

Linux 软件包依赖关系

理论上,这种方法也可以扩展以支持 Linux 软件包。例如,如果我们的训练脚本依赖于特定的 Linux 软件包或软件包版本,我们可以在脚本开始时使用subprocess调用来安装它。实际上,包括 Amazon SageMaker 在内的许多服务限制了 Linux 用户权限,从而阻止了这种选项。

总结

我们提出的第一个用于定制训练环境的解决方案易于使用,并允许我们充分利用由云服务提供商特别设计和优化的 Docker 镜像。然而,正如我们所看到的,它也有一些限制。在我们文章的第二部分中,我们将讨论两种额外的方法来解决这些限制。

定制你的云端机器学习训练环境 — 第二部分

原文:towardsdatascience.com/customizing-your-cloud-based-machine-learning-training-environment-part-2-b65a6cf91812?source=collection_archive---------8-----------------------#2023-05-21

增加开发灵活性的额外解决方案

Chaim RandTowards Data Science Chaim Rand

·

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

--

照片由 Murilo Gomes 提供,发布在 Unsplash 上。

这是关于定制你的云端 AI 模型训练环境的两篇文章的第二部分。在第一部分中,我们介绍了使用预构建的专门设计的训练环境的愿望与我们能够根据项目需求定制环境的要求之间可能出现的冲突。发现潜在定制机会的关键在于对云端训练作业的端到端流程有深入了解。我们描述了托管的 Amazon SageMaker 训练服务的这一流程,同时强调了分析公开的底层源代码的价值。接着,我们展示了定制的第一种方法——在训练会话开始时安装 pip 包依赖,并展示了它的局限性。

在这篇文章中,我们将介绍另外两种方法。这两种方法都涉及创建我们自己的自定义 Docker 镜像,但它们在方法上存在根本差异。第一种方法使用官方云服务提供的镜像,并根据项目需求进行扩展。第二种方法则采用用户定义的(与云无关的)Docker 镜像,并扩展以支持云端训练。正如我们所见,每种方法都有其优缺点,最佳选择将高度依赖于项目的具体细节。

扩展官方云服务 Docker 镜像

创建一个功能齐全、性能优化的 Docker 镜像以在基于云的 GPU 上进行训练可能会非常费劲,这需要处理大量复杂的硬件和软件依赖关系。对于各种训练用例和硬件平台,这种工作更加困难。与其尝试自己完成,不如优先利用云服务提供商为我们创建的预定义镜像。如果我们需要定制这个镜像,我们可以简单地创建一个新的 Dockerfile,扩展官方镜像并添加所需的依赖项。

AWS 深度学习容器 (DLC) github 仓库包含了扩展官方 AWS DLC 的说明。这需要登录以访问深度学习容器镜像库,从而拉取镜像、构建扩展镜像,并将其上传到你账户中的Amazon Elastic Container Registry (ECR)

以下代码块展示了如何从我们的 SageMaker 示例(第一部分)扩展官方 AWS DLC。我们展示了三种类型的扩展:

  1. Linux 包:我们安装了Nvidia Nsight Systems,用于对我们的训练作业进行高级 GPU 性能分析。

  2. Conda 包:我们安装了S5cmd conda 包,用于从云存储中拉取数据文件。

  3. Pip 包:我们安装了特定版本的opencv-python pip 包。

From 763104351884.dkr.ecr.us-east-1.amazonaws.com/pytorch-training:1.13.1-gpu-py39-cu117-ubuntu20.04-sagemaker

# install nsys
ADD  https://developer.download.nvidia.com/devtools/repos/ubuntu2004/amd64/NsightSystems-linux-cli-public-2023.1.1.127-3236574.deb ./
RUN apt install -y ./NsightSystems-linux-cli-public-2023.1.1.127-3236574.deb

# install s5cm
RUN conda install -y s5cmd

# install opencv
RUN pip install opencv-python==4.7.0.72

要了解如何扩展官方 AWS DLC 的更多细节,包括如何将生成的镜像上传到 ECR,请参阅这里。下面的代码块显示了如何修改训练作业部署脚本以使用扩展的镜像:

from sagemaker.pytorch import PyTorch

# define the training job
estimator = PyTorch(
    entry_point='train.py',
    source_dir='./source_dir',
    role='<arn role>',
    image_uri = '<account-number>.dkr.ecr.us-east-1.amazonaws.com/<tag>'
    job_name='demo',
    instance_type='ml.g5.xlarge',
    instance_count=1
)

你还可以选择自定义官方镜像,前提是你有相应 Dockerfile 的访问权限,即直接对 Dockerfile 进行所需编辑并从头构建。对于 AWS DLC,这个选项的文档记录在这里。不过,请注意,尽管基于相同的 Dockerfile,结果镜像可能由于构建环境和更新的包版本的差异而有所不同。

通过扩展官方 Docker 镜像进行环境自定义是最大限度地利用云服务预定义的功能齐全、经过验证的云优化训练环境的好方法,同时仍然允许你自由灵活地进行所需的添加和调整。然而,正如我们通过示例展示的,这种选项也有其局限性。

在用户定义的 Python 环境中进行训练

由于各种原因,你可能需要在用户定义的 Python 环境中进行训练。这可能是出于可重复性、平台独立性、安全/合规性考虑或其他目的。你可以考虑的一个选项是用自定义的 Python 环境扩展官方 Docker 镜像。这样,你至少可以受益于镜像中的平台相关安装和优化。然而,如果你的用途依赖于某种形式的 Python 自动化,这可能会变得有些棘手。例如,在一个管理的训练环境中,Dockerfile 的 ENTRYPOINT 运行一个 Python 脚本,该脚本执行各种操作,包括从云存储中下载代码源目录、安装 Python 依赖项、运行用户定义的训练脚本等。这个 Python 脚本位于官方 Docker 镜像的预定义 Python 环境中。编程自动化脚本以在单独的 Python 环境中启动训练脚本是可行的,但可能需要在预定义环境中进行一些手动代码更改,并且可能会变得非常混乱。在下一节中,我们将展示一种更简洁的方法来做到这一点。

自带镜像(BYO)

我们考虑的最终场景是你需要在由你自己定义的 Docker 镜像创建的特定环境中进行训练。和之前一样,驱动这一需求的可能是法规要求,或者是在云中运行与你本地(“本地部署”)相同镜像的愿望。一些云服务提供了将用户定义的镜像带入并适配到云中的能力。在这一部分,我们演示了 Amazon SageMaker 支持这一功能的两种方式。

自带选项 1:SageMaker 训练工具包

第一种选项,在这里有文档记录,允许你将我们在第一部分中描述的专用(托管)训练启动流程添加到你自定义的 Python 环境中。这本质上使你可以在 SageMaker 中使用自定义镜像进行训练,就像使用官方镜像一样。特别是,你可以将相同的镜像重用于多个项目/实验,并依赖 SageMaker API 在启动时将特定于实验的代码下载到训练环境中(如第一部分中所述)。每次更改训练代码时,你不需要创建和上传新的镜像。

下面的代码块演示了如何获取自定义镜像,并按照这里中详细的说明,使用SageMaker 训练工具包对其进行增强。

FROM user_defined_docker_image

RUN echo "conda activate user_defined_conda_env" >> ~/.bashrc
SHELL ["/bin/bash", "--login", "-c"]

ENV SAGEMAKER_TRAINING_MODULE=sagemaker_pytorch_container.training:main
RUN conda activate user_defined_conda_env \
  && pip install --no-cache-dir -U sagemaker-pytorch-training sagemaker-training

# sagemaker uses jq to compile executable
RUN apt-get update \
 && apt-get -y upgrade --only-upgrade systemd \
 && apt-get install -y --allow-change-held-packages --no-install-recommends \
 jq

# SageMaker assumes conda environment is in Path
ENV PATH /opt/conda/envs/user_defined_conda_env/bin:$PATH

# delete entry point and args if provided by parent Dockerfile
ENTRYPOINT []
CMD []

自带选项 2:配置入口点

第二种选项,在这里有文档记录,允许你在 SageMaker 中以用户定义的 Docker 环境进行训练,且对 Docker 镜像没有任何更改。唯一需要做的就是明确设置 Docker 容器的ENTRYPOINT 指令。实现这一点的方式之一(如这里所述)是将ContainerEntrypoint 和/或 ContainerArguments 参数传递给 API 请求的 AlgorithmSpecification。不幸的是,截至撰写本文时,SageMaker Python API(版本 2.146.1)不支持这一选项。然而,我们可以通过扩展SageMaker Session 类来轻松实现,如下面的代码块所示:

from sagemaker.session import Session

# customized session class that supports adding container entrypoint settings
class SessionEx(Session):
  def __init__(self, **kwargs):
    self.user_entrypoint = kwargs.pop('entrypoint', None)
    self.user_arguments = kwargs.pop('arguments', None)
    super(SessionEx, self).__init__(**kwargs)

  def _get_train_request(self, **kwargs):
    train_request = super(SessionEx, self)._get_train_request(**kwargs)
    if self.user_entrypoint:
      train_request["AlgorithmSpecification"]["ContainerEntrypoint"] =\
           [self.user_entrypoint]
    if self.user_arguments:
      train_request["AlgorithmSpecification"]["ContainerArguments"] =\
           self.user_arguments
    return train_request

from sagemaker.pytorch import PyTorch

# create session with user defined entrypoint and arguments
# SageMaker will run 'docker run --entrypoint python <user image> path2file.py
sm_session = SessionEx(user_entrypoint='python', 
                       user_arguments=['path2file.py'])

# define the training job
estimator = PyTorch(
    entry_point='train.py',
    source_dir='./source_dir',
    role='<arn role>',
    image_uri='<account-number>.dkr.ecr.us-east-1.amazonaws.com/<tag>'
    job_name='demo',
    instance_type='ml.g5.xlarge',
    instance_count=1,
    sagemaker_session=sm_session
) 

优化你的 Docker 镜像

BYO 选项的一个缺点是你失去了利用官方预定义镜像专业化的机会。你可以手动和有选择性地将其中一些重新引入到你的自定义镜像中。例如,SageMaker 文档包括了详细说明如何集成对 Amazon EFA 的支持。此外,你总是可以查看公开可用的 Dockerfile 来挑选你需要的内容。

摘要

在这篇两部分的文章中,我们讨论了不同的方法来定制你的云端培训环境。我们选择的方法旨在展示应对不同用例的方式。在实际操作中,最佳解决方案将直接依赖于你的项目需求。你可能会决定为所有培训实验创建一个单一的自定义 Docker 镜像,并结合一个选项来安装实验特定的依赖项(使用第一种方法)。你也可能会发现,另一种方法可能更适合你的需求,比如调整某部分的 sagemaker-training Python 包。总的来说,当你面临需要定制培训环境时,你有多种选择;如果我们所涵盖的标准选项不够充分,不要气馁,要发挥创造力!

Cutout、Mixup 和 Cutmix:在 PyTorch 中实现现代图像增强

原文:towardsdatascience.com/cutout-mixup-and-cutmix-implementing-modern-image-augmentations-in-pytorch-a9d7db3074ad

用 Python 实现的计算机视觉数据增强技术

Leonie MonigattiTowards Data Science Leonie Monigatti

·发表于Towards Data Science ·阅读时间 9 分钟·2023 年 4 月 14 日

--

Cutmix 图像增强(背景图像由作者绘制,人工生成的雕像照片使用 DALLE)

几乎可以保证,应用数据增强会提升你的神经网络性能。增强是一种正则化技术,可以人工扩展你的训练数据,帮助你的深度学习模型更好地泛化。因此,图像增强可以提升模型性能。

图像增强可以提升模型性能

经典的图像增强技术用于计算机视觉中的卷积神经网络,包括缩放、裁剪、翻转或旋转图像。

在最近的一篇关于中级深度学习技术的文章中,我们了解到除了经典技术之外,最有效的图像增强技术是:

  • Cutout [2]

  • Mixup [4]

  • Cutmix [3]

数据增强技术:Mixup、Cutout、Cutmix

本文将简要描述上述图像增强及其在 PyTorch 深度学习框架中的 Python 实现。

设置

本教程将使用一个“基础”的图像分类问题作为示例。任务是分类郁金香和玫瑰的图像:

用于图像分类的玩具数据集[1]:玫瑰还是郁金香。

在此插入你的数据! — 为了跟随本文,你的数据集应该类似于这样:

用于图像分类的玩具数据集[1]。在此插入你的数据。

PyTorch(版本 1.11.0)、OpenCV(版本 4.5.4)和 albumentations(版本 1.3.0)。

import torch
from torch.utils.data import DataLoader, Dataset
import torch.utils.data as data_utils

import cv2
import numpy as np

import albumentations as A
from albumentations.pytorch import ToTensorV2

PyTorch Dataset使用 OpenCV 加载图像。

class ExampleDataset(Dataset):
    def __init__(self, 
                data, 
                transform = None):
        self.file_paths = data['file_paths'].values
        self.labels = data['labels'].values
        self.transform = transform

    def __len__(self):
        return len(self.file_paths)

    def __getitem__(self, idx):
        # Get file_path and label for index
        label = self.labels[idx]
        file_path = self.file_paths[idx]

        # Read an image with OpenCV
        image = cv2.imread(file_path)

        # Convert the image to RGB color space.
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

        # Apply augmentations
        if self.transform:
            augmented = self.transform(image=image)
            image = augmented['image']

        return image, label 

PyTorch DataLoader随后将数据集划分为每批 8 张图像。这些基本图像变换将图像调整为 256x256 像素。

transforms = A.Compose([
    A.Resize(256, 256), # Resize images
    ToTensorV2()])

example_dataset = ExampleDataset(train_df,
                                 transform = transforms)

train_dataloader = DataLoader(example_dataset, 
                              batch_size = 8, 
                              shuffle = True, 
                              num_workers = 0)

下面你可以看到一个普通图像分类问题的训练代码。所有分散注意力的代码部分已被省略,以简化代码并只关注相关部分。

由于我们有一个二分类问题,我们将使用nn.CrossEntropyLoss()。这是值得注意的,因为我们稍后将实现一个自定义损失函数。

# Define device, model, optimizer, learning rate scheduler
device = ...
model = ...
criterion = nn.CrossEntropyLoss()
optimizer = ... 
scheduler = ...

for epoch in range(NUM_EPOCHS):        
    # Train
    model.train()

    # Define any variables for metrics
    ...

    # Iterate over data
    for samples, labels in (train_dataloader):

        samples, labels = samples.to(device), labels.to(device)

        # Normalize
        samples = samples/255

        # Zero the parameter gradients
        ...

        with torch.set_grad_enabled(True):
            # Forward: Get model outputs and calculate loss
            output = model(samples)

            loss = criterion(output, labels)

            # Backward: Optimize, step optimizer and calculate predictions
            ...

        # Step scheduler
        ...

        # Calculate any statistics
        ...

    # Validate 
    ...

Cutout

Cutout [2] 在 2017 年由 DeVries & Taylor 在论文“Improved regularization of convolutional neural networks with cutout.”中提出。

简要描述

Cutout 图像增强的核心思想是在训练期间随机去除输入图像中的一个方形像素区域。

Cutout 图像增强

Cutout 可以通过迫使模型学习更强健的特征来防止过拟合。

优势:

  • 实现简单(见 Cutout 的实现)

  • 可以去除噪声,例如背景

缺点:

  • 可以移除重要特征,特别是在稀疏图像中

使用 PyTorch 的 Python 实现

幸运的是,Cutout 在Albumentations图像增强库中可用。你可以使用CoarseDropout类(早期库版本中有Cutout类,但已被弃用)。

transforms_cutout = A.Compose([
    A.Resize(256, 256), 
    A.CoarseDropout(max_holes = 1, # Maximum number of regions to zero out. (default: 8)
                    max_height = 128, # Maximum height of the hole. (default: 8) 
                    max_width = 128, # Maximum width of the hole. (default: 8) 
                    min_holes=None, # Maximum number of regions to zero out. (default: None, which equals max_holes)
                    min_height=None, # Maximum height of the hole. (default: None, which equals max_height)
                    min_width=None, # Maximum width of the hole. (default: None, which equals max_width)
                    fill_value=0, # value for dropped pixels.
                    mask_fill_value=None, # fill value for dropped pixels in mask. 
                    always_apply=False, 
                    p=0.5
                   ),
    ToTensorV2(),
])

返回的样本批次如下所示:

对样本批次应用了 Cutout 图像增强。

Mixup

Mixup [4] 在 2017 年由 Zhang, Cisse, Dauphin & Lopez-Paz 在论文“mixup: Beyond empirical risk minimization”中提出。

简要描述

Mixup 图像增强的核心思想是在训练期间混合一对随机的输入图像及其标签。

Mixup 图像增强

Mixup 可以通过创建更多多样化的训练样本来防止过拟合,从而迫使模型学习对图像中的小变化不变的更具通用性的特征。

优势:

  • 实现相对简单(见 Mixup 的实现)

  • 增加训练数据的多样性

缺点:

  • 可以创建模糊图像,特别是对于复杂纹理的图像

使用 PyTorch 的 Python 实现

你必须实现一个mixup()函数,以将 Mixup 图像增强应用到你的深度学习训练管道中。以下代码最初来自于Riad 的这个 Kaggle Notebook,并为本文进行了修改。

mixup()函数将 Mixup 应用于整个批次。对会生成配对的操作通过打乱批次并从原始批次中选择一张图像和从打乱的批次中选择一张图像来实现。

# Copied and edited from https://www.kaggle.com/code/riadalmadani/fastai-effb0-base-model-birdclef2023
def mixup(data, targets, alpha):
    indices = torch.randperm(data.size(0))
    shuffled_data = data[indices]
    shuffled_targets = targets[indices]

    lam = np.random.beta(alpha, alpha)
    new_data = data * lam + shuffled_data * (1 - lam)
    new_targets = [targets, shuffled_targets, lam]
    return new_data, new_targets

除了增强图像和标签的函数,我们还必须使用自定义 mixup_criterion() 函数修改损失函数。该函数根据 lam 返回两个标签的损失。

# Copied and edited from https://www.kaggle.com/code/riadalmadani/fastai-effb0-base-model-birdclef2023
def mixup_criterion(preds, targets):
    targets1, targets2, lam = targets[0], targets[1], targets[2]
    criterion = nn.CrossEntropyLoss()
    return lam * criterion(preds, targets1) + (1 - lam) * criterion(preds, targets2)

mixup()mixup_criterion() 函数不会应用于 PyTorch Dataset,而是在训练代码中应用,如下所示。

由于增强应用于整个批次,我们还将添加一个变量 p_mixup 来控制将被增强的批次比例。例如,p_mixup = 0.5 将对一个 epoch 中的 50% 批次应用 Mixup 增强。

for epoch in range(NUM_EPOCHS):        
    # Train
    model.train()

    # Define any variables for metrics
    ...

    for samples, labels in (train_dataloader):

        samples, labels = samples.to(device), labels.to(device)

        # Normalize
        samples = samples/255

        ############################
        # Apply Mixup augmentation #
        ############################
        p = np.random.rand()
        if p < p_mixup:
            samples, labels = mixup(samples, labels, 0.8)

        # Zero the parameter gradients
        ...

        with torch.set_grad_enabled(True):
            # Forward: Get model outputs and calculate loss
            output = model(samples)

            ############################
            # Apply Mixup criterion    #
            ############################      
            if p < p_mixup:
                loss = mixup_criterion(output, labels)
            else:
                loss = criterion(output, labels) 

            # Backward: Optimize, step optimizer and calculate predictions
            ...

        # Step scheduler, Calculate any statistics, validate
        ...

返回的样本批次如下所示:

应用于样本批次的 Mixup 图像增强。

Cutmix

Cutmix [3] 在 2019 年的一篇名为“Cutmix: Regularization strategy to train strong classifiers with localizable features.”的论文中由 Yun、Han、Oh、Chun、Choe 和 Yoo 提出。

简要描述

Cutmix 图像增强的核心思想是在训练过程中随机选择一对输入图像,从第一张图像中剪切一个随机的像素块并将其粘贴到第二张图像上,然后按像素块的面积成比例混合它们的标签。

Cutmix 图像增强

Cutmix 可以通过迫使模型学习更强健和具有区分性的特征来防止过拟合。

Cutmix 结合了 Cutout 和 Mixup 的优缺点:

优点:

  • 相对容易实现(参见 Cutmix 的实现)

  • 增加训练数据的多样性

缺点:

  • 由于不自然的组合,可能会生成不真实的图像

  • 可能会移除重要特征,尤其是在稀疏图像中

使用 PyTorch 的 Python 实现

Cutmix 的实现类似于 Mixup 的实现。

首先,你还需要一个自定义函数 cutmix() 来应用图像增强。以下代码最初取自 Riad 的 Kaggle Notebook 并为本文做了修改。

# Copied and edited from https://www.kaggle.com/code/riadalmadani/fastai-effb0-base-model-birdclef2023
def cutmix(data, targets, alpha):
    indices = torch.randperm(data.size(0))
    shuffled_data = data[indices]
    shuffled_targets = targets[indices]

    lam = np.random.beta(alpha, alpha)
    bbx1, bby1, bbx2, bby2 = rand_bbox(data.size(), lam)
    data[:, :, bbx1:bbx2, bby1:bby2] = data[indices, :, bbx1:bbx2, bby1:bby2]
    # adjust lambda to exactly match pixel ratio
    lam = 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (data.size()[-1] * data.size()[-2]))

    new_targets = [targets, shuffled_targets, lam]
    return data, new_targets

def rand_bbox(size, lam):
    W = size[2]
    H = size[3]
    cut_rat = np.sqrt(1\. - lam)
    cut_w = int(W * cut_rat)
    cut_h = int(H * cut_rat)

    # uniform
    cx = np.random.randint(W)
    cy = np.random.randint(H)

    bbx1 = np.clip(cx - cut_w // 2, 0, W)
    bby1 = np.clip(cy - cut_h // 2, 0, H)
    bbx2 = np.clip(cx + cut_w // 2, 0, W)
    bby2 = np.clip(cy + cut_h // 2, 0, H)

    return bbx1, bby1, bbx2, bby2

剩下的与 Mixup 相同:

  1. 定义 cutmix_criterion() 函数来处理自定义损失(参见 mixup_criterion() 的实现)

  2. 定义变量 p_cutmix 来控制将被增强的批次比例(参见 p_mixup

  3. 在训练代码中根据 p_cutmix 应用 cutmix()cutmix_criterion()(参见 Mixup 的实现)

返回的样本批次如下所示:

应用于样本批次的 Cutmix 图像增强。

总结

本文总结了三种现代有效的数据增强技术,用于计算机视觉:

  • Cutout [2]:随机去除输入图像中的一个方形区域的像素

  • Mixup [4]:混合一对随机的输入图像及其标签

  • Cutmix [3]: 随机选择一对输入图像,从第一张图像中剪切出一个随机像素块,将其粘贴到第二张图像上,然后按像素块的面积比例混合它们的标签。

数据增强技术:Mixup, Cutout, Cutmix(图片由作者提供)

虽然 Cutout 将增强应用于单张图像,但 Mixup 和 Cutmix 则从一对输入图像中创建新图像。

所有讨论的图像增强技术都相对容易实现:对于 Cutout,Albumentations库已经提供了现成的实现。对于 Mixup 和 Cutmix,实施相对简单,需要实现一个增强函数和一个自定义损失函数。

享受这个故事吗?

免费订阅 以便在我发布新故事时收到通知。

[## 每当 Leonie Monigatti 发布新内容时,通过电子邮件收到通知。

每当 Leonie Monigatti 发布新内容时,您将通过电子邮件收到通知。通过注册,您将创建一个 Medium 帐户(如果您尚未拥有的话)……

medium.com](https://medium.com/@iamleonie/subscribe?source=post_page-----a9d7db3074ad--------------------------------)

LinkedInTwitter Kaggle上找到我!

参考文献

数据集

[1] 使用的图像数据集由作者从Unsplash.com收集的八张图片创建。该数据集在Kaggle上公开提供,数据集描述中注明了图像来源。由于 Unsplash 上的图片可以用于商业用途,并且这八张照片的收集并不重复类似或竞争的 Unsplash 服务,因此该数据集在 CC BY-SA 4.0 许可证下使用。

[## 玫瑰还是郁金香

玩具数据集仅包含 8 张图片(4 张郁金香和 4 张玫瑰),均来自 Unsplash。

www.kaggle.com

图像参考

除非另有说明,所有图像均由作者创建。

文献

[2] DeVries, T., & Taylor, G. W. (2017). Improved regularization of convolutional neural networks with cutout. arXiv 预印本 arXiv:1708.04552

[3] Yun, S., Han, D., Oh, S. J., Chun, S., Choe, J., & Yoo, Y. (2019). Cutmix: Regularization strategy to train strong classifiers with localizable features. In IEEE/CVF 国际计算机视觉会议论文集 (第 6023–6032 页)。

[4] 张华、Cisse M.、Dauphin Y. N. 和 Lopez-Paz D. (2017) mixup: 超越经验风险最小化。arXiv 预印本 arXiv:1710.09412。

赛博朋克风格的 Matplotlib 图表

原文:towardsdatascience.com/cyberpunking-your-matplotlib-figures-96f4d473185d

用几行代码让你的 Matplotlib 图表从乏味变得有趣

Andy McDonald Towards Data Science Andy McDonald

·发表于 Towards Data Science ·阅读时间 8 分钟·2023 年 5 月 21 日

--

应用 mplcyberpunk 主题的包含多个子图的 matplotlib 图表。图片由作者提供。

当我们创建包含数据的信息图或海报时,我们希望吸引读者的注意,并使其在视觉上令人愉悦,同时讲述一个令人信服的故事。

在 Python 中,我们有许多绘图库可以用来创建图表——其中一个是著名的 matplotlib 库。然而,默认情况下,matplotlib 生成的图表往往被视为乏味的,要将其变得视觉上吸引人可能需要耗费时间。

这就是 matplotlib 主题库发挥作用的地方。我最喜欢的库之一是 CyberPunk 主题

[## GitHub - dhaitz/mplcyberpunk: "赛博朋克风格" 的 matplotlib 图表

一个基于 matplotlib 的 Python 包,通过 3 行额外代码创建“赛博朋克”风格的图表。之后…

github.com

赛博朋克已成为一种广受欢迎的科幻子类型,其特点是描绘反乌托邦社会、高度先进的技术和反文化主题。这些设置通常以未来主义的外观呈现,突出霓虹灯和鲜艳大胆的色彩。

图片由作者使用 Midjourney 基本计划生成。(一个繁忙的反乌托邦赛博朋克主题的未来城市街道,特色是有许多街头小贩的地方。商店和区域被明亮的霓虹灯包围。雨天和阴暗的氛围。摄影真实。 — ar 3:2)

CyberPunk 主题将那些霓虹和鲜艳的颜色带到了 matplotlib 中,正如你将在这篇文章中看到的,我们可以仅用两行 Python 代码应用这种样式。

CyberPunk 主题 Matplotlib 图形示例。图片由 dhaitz 提供,来自 CyberPunk Readme。

然而,你需要意识到,使用这种风格生成的图表在创建出版物图形时可能不会被视为专业,并且可能不适合有色觉问题的读者。

因此,在实施这种样式之前必须考虑你的受众。

导入库和创建数据

在我们开始创建可视化之前,我们需要导入一些库。这些库是matplotlibpandasnumpy,它们将用于创建、存储数据和创建可视化。

import matplotlib.pyplot as plt
import pandas as pd
import mplcyberpunk
import numpy as np

一旦导入了库,我们可以使用以下代码创建虚拟数据。

# Create a list of categories
categories = ['A', 'B', 'C', 'D']

# Create a DataFrame with 4 columns and 4 rows
data = {
    'Category': categories,
    'Value1': np.random.randint(0, 20, 4),
    'Value2': np.random.randint(0, 20, 4),
    'Value3': np.random.randint(0, 20, 4),
}

df = pd.DataFrame(data)

这段代码生成了一个包含 4 列的 pandas 数据框:其中一列是分类列,其他列是数字列,由随机整数构成。

Cyberpunk 配色的条形图

我们将创建的第一个图表和主题是流行的条形图。

要创建这个图表,我们可以使用 pandas 的.plot()方法,并传入我们的x值,即我们的分类变量。我们还需要将stacked设置为False,将kind参数设置为bar

df.plot(x='Category', kind='bar', stacked=False, alpha=0.8, figsize=(10,10))

当我们运行上述代码时,我们将返回以下基本 matplotlib 图形。

使用 df.plot 生成的基本 matplotlib 条形图。图片由作者提供。

这很好;然而,它缺乏视觉吸引力,可以做得更好。

我们可以通过在df.plot()调用之前添加一行代码来轻松更改,这将使我们能够将 CyberPunk 主题应用于图表。

with plt.style.context('cyberpunk'):
    df.plot(x='Category', kind='bar', stacked=False, alpha=0.8, 
            figsize=(10,10))

我们得到的是如下条形图。

应用 mplcyberpunk 主题后的 matplotlib 条形图。图片由作者提供。

我们可以看到,图表背景颜色已切换到暗色主题,条形图呈现出“霓虹”般的外观。

在使用此主题创建条形图时,我们可能会发现条形图的颜色过于鲜艳,这就是我们必须应用 alpha 值的原因。

折线图

我们也可以对折线图进行同样的操作。

首先,我们调用df.plot(),但将kindbar更改为line。使用折线图,我们还可以添加标记,这有助于识别数据的位置。如果我们在类别之间的值变化很小,这很有用。

df.plot(x='Category', kind='line',
        lw=3, marker='.', ms=20,
        figsize=(10,10))

使用 df.plot 生成的 matplotlib 基本折线图。图片由作者提供。

要应用 CyberPunk 主题,我们需要在代码中添加with plt.style.context(‘cyberpunk’):

with plt.style.context('cyberpunk'):
    df.plot(x='Category', kind='line',
            lw=3, marker='.', ms=20,
            figsize=(10,10))

运行时将生成以下折线图。

应用 mplcyberpunk 主题后的 matplotlib 折线图。图片由作者提供。

CyberPunk 主题库带有一些额外的功能,例如使折线发光:

with plt.style.context('cyberpunk'):
    df.plot(x='Category', kind='line',
            lw=3, marker='.', ms=20,
            figsize=(10,10))
    mplcyberpunk.make_lines_glow()

应用 mplcyberpunk 主题并为折线添加光晕效果后的 matplotlib 折线图。图片由作者提供。

并在折线下方添加了透明渐变填充:

with plt.style.context('cyberpunk'):
    df.plot(x='Category', kind='line',
            lw=3, marker='.', ms=20,
            figsize=(10,10))
    mplcyberpunk.add_gradient_fill(alpha_gradientglow=0.4)

应用 mplcyberpunk 主题并对折线进行渐变填充后的 matplotlib 折线图。图片由作者提供。

如果你处理的是 1 或 2 条折线,添加渐变填充效果是不错的,但一旦开始将其应用于 3 条或更多的折线时,渐变可能会开始混合在一起,变得非常混乱。

带有 CyberPunk 样式的雷达图

雷达图(蜘蛛图或雷达图)是一种流行且广泛使用的数据可视化工具。它们允许我们通过在二维图表上同时显示多个变量来比较数据集。

生成雷达图需要更多的 matplotlib 代码,以使它们显示得更好。

from matplotlib.patches import Patch

# Set up the data so that we can close the 'loop' of the area
categories = [*categories, categories[0]]
values_1 = [*data['Value1'], data['Value1'][0]]
values_2 = [*data['Value2'], data['Value2'][0]]
values_3 = [*data['Value3'], data['Value3'][0]]

# Set up the label potisions around the circle circumference
label_loc = np.linspace(start=0, stop=2 * np.pi, num=len(categories))

# Set up the figure
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))

# Add our data as separate axes
ax.plot(label_loc, values_1, lw=2)
ax.plot(label_loc, values_2, lw=2)
ax.plot(label_loc, values_3, lw=2)

# Apply a fill to our lines
ax.fill(label_loc, values_1, alpha=0.3)
ax.fill(label_loc, values_2, alpha=0.3)
ax.fill(label_loc, values_3, alpha=0.3)

# Convert the lines and labels to a polar grid
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=categories)

# Setup the radial lines
ax.set_ylim(0, 20)
ax.set_yticks(np.arange(0, 20, 2))

# Create custom legend handles
values_1_legend = Patch(facecolor='C0', alpha=0.5, label='Values1')
values_2_legend = Patch(facecolor='C1', alpha=0.5, label='Values2')
values_3_legend = Patch(facecolor='C2', alpha=0.5, label='Values3')

# Add a legend with custom position and handles
ax.legend(handles=[values_1_legend, values_2_legend, values_3_legend],
          bbox_to_anchor=(1.3, 0.2), fontsize=20, 
          frameon=True)

plt.show()

当我们运行上述代码时,我们将得到以下的雷达图,该图使用了标准的 matplotlib 主题。

基本的 matplotlib 雷达图(蜘蛛图)。图片由作者提供。

我们应用之前相同的代码,将基本的雷达图转换为 CyberPunk 主题。

但我们还需要更改坐标轴和网格,以便能看到它们。

with plt.style.context('cyberpunk'):
    fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))

    # Add our data as separate axes
    ax.plot(label_loc, values_1, lw=2)
    ax.plot(label_loc, values_2, lw=2)
    ax.plot(label_loc, values_3, lw=2)

    # Apply a fill to our lines
    ax.fill(label_loc, values_1, alpha=0.3)
    ax.fill(label_loc, values_2, alpha=0.3)
    ax.fill(label_loc, values_3, alpha=0.3)

    # Convert the lines and labels to a polar grid
    lines, labels = plt.thetagrids(np.degrees(label_loc), labels=categories)

    # Set up the grid and line properties
    ax.tick_params(axis='both', which='major', pad=30, labelsize=15)

    ax.spines['polar'].set_linewidth(3)

    edge_color = (1, 1, 1, 0.2) 
    ax.spines['polar'].set_color(edge_color) 

    ax.grid(color='white', alpha=0.3)

    # Setup the radial lines
    ax.set_ylim(0, 20)
    ax.set_yticks(np.arange(0, 20, 2))

    # Create custom legend handles
    values_1_legend = Patch(facecolor='C0', alpha=0.5, label='Values1')
    values_2_legend = Patch(facecolor='C1', alpha=0.5, label='Values2')
    values_3_legend = Patch(facecolor='C2', alpha=0.5, label='Values3')

    # Add a legend with custom position and handles
    ax.legend(handles=[values_1_legend, values_2_legend, values_3_legend],
              bbox_to_anchor=(1.3, 0.2), fontsize=20, 
              frameon=True)

    mplcyberpunk.add_glow_effects()

    plt.show()

当代码重新运行时,我们现在得到了一个 CyberPunk 风格的雷达图。

应用 CyberPunk 主题后的 matplotlib 雷达图。

如果你想了解更多关于创建雷达图的内容,可以查看我之前的文章:

## 使用 Matplotlib 创建惊艳的雷达图

使用 Python 最流行的数据可视化库来可视化地质数据

towardsdatascience.com

使用子图和 CyberPunk 样式

在使用 matplotlib 创建图形时,我们通常希望显示多个图表。图表的类型可以根据你希望展示的内容有所不同。

这个例子展示了如何创建一个包含 4 个子图的 matplotlib 图形:2 个折线图、1 个散点图和 1 个条形图。

为了确保应用了我们的 CyberPunk 样式,我们可以添加以下代码: with plt.style.context(‘cyberpunk’):

如果我们想要应用 mplcyberpunk 库中的任何特殊样式,例如光晕,我们需要通过将 ax 对象传递给 ax 参数来指定要应用该样式的轴。

例如: mplcyberpunk.make_lines_glow(ax=ax4)

with plt.style.context('cyberpunk'):
    fig = plt.figure(figsize=(15, 10))

    ax1 = plt.subplot2grid((2,3), (0,0))
    ax2 = plt.subplot2grid((2,3), (0,1))
    ax3 = plt.subplot2grid((2,3), (0,2))
    ax4 = plt.subplot2grid((2,3), (1,0), colspan=3)

    # Line Plot 1
    x_data = np.linspace(0, 20, 50)
    y_data_clean = np.sin(x_data)
    noise = np.random.normal(0, 2, y_data_clean.shape)
    y_data_noise = y_data_clean + noise

    ax1.plot(x_data, y_data_noise)
    ax1.plot(x_data, y_data_clean, lw=2)
    ax1.set_title('Plot 1', fontsize=14)

    # Scatter Plots
    ax2.scatter(x=(np.random.randint(0, 20, 100)), y=np.random.randint(0,20, 100))
    ax2.scatter(x=(np.random.randint(0, 20, 100)), y=np.random.randint(0,20, 100))
    ax2.set_title('Plot 2', fontsize=14)

    # Bar Chart
    bar_values = np.array(["A", "B", "C", "D"])
    bar_heights = ([20, 10, 5, 7])
    bar_heights_2 = ([10, 15, 2, 18])
    bar_width = 0.35

    bar_pos_1 = np.arange(len(bar_values))
    bar_pos_2 = [x + bar_width for x in bar_pos_1]

    # Make sure the bars are offset from each other
    ax3.bar(x=bar_pos_1, height=bar_heights, width=bar_width, label='Group 1')
    ax3.bar(x=bar_pos_2, height=bar_heights_2, width=bar_width, label='Group 2')
    ax3.set_xticks([r + bar_width / 2 for r in range(len(bar_heights))])
    ax3.set_xticklabels(bar_values)
    ax3.set_title('Plot 3', fontsize=14)

    # Bottom Axis
    values = np.arange(0, 10, 0.1)
    sine_wave1 = np.sin(values)
    sine_wave2 = sine_wave1 * 2
    sine_wave3 = sine_wave1 + 0.5

    ax4.plot(sine_wave1)
    ax4.plot(sine_wave2)
    ax4.plot(sine_wave3)
    ax4.set_title('Plot 4', fontsize=14)

    mplcyberpunk.make_lines_glow(ax=ax1)
    mplcyberpunk.make_lines_glow(ax=ax4)

    plt.suptitle('Interesting Inforgraphic Using CyberPunk', fontsize=25)
    plt.show()

当代码运行时,我们得到以下图表:

应用 cyberpunk 主题后的 matplotlib 图形,具有多个子图。图片由作者提供。

概要

使用 CyberPunk 风格库,我们可以轻松而迅速地将无聊的 matplotlib 图形转变为具有未来感的外观,全部不需要添加大量新的代码行。

然而,正如文章开头提到的,在应用主题之前,始终重要的是考虑你的目标受众以及你希望通过数据可视化实现的目标。

祝你 CyberPunk 玩得愉快!

感谢阅读。在你离开之前,你应该订阅我的内容,并将我的文章发送到你的邮箱。 你可以在这里做到这一点!另外,你也可以 注册我的新闻通讯 以免费获得额外的内容直接发送到你的邮箱。

其次,你可以通过注册会员获得完整的 Medium 体验,并支持其他成千上万的作者和我。这只需每月 $5,你就可以全面访问所有精彩的 Medium 文章,并有机会通过写作赚取收入。

如果你通过 我的链接注册你将通过你费用的一部分直接支持我,而这不会增加你的费用。如果你这样做,非常感谢你的支持。

使用 D-Tale 快速轻松地进行井日志数据的探索性数据分析

原文:towardsdatascience.com/d-tale-for-fast-and-easy-exploratory-data-analysis-of-well-log-data-a2ffca5295b6

使用 D-Tale Python 库加速探索性数据分析工作流

Andy McDonaldTowards Data Science Andy McDonald

·发表于 Towards Data Science ·阅读时间 9 分钟·2023 年 2 月 13 日

--

图片由 Photo Mix 提供,来源于 Pixabay

探索性数据分析(EDA)可能是数据科学和机器学习工作流中耗时但至关重要的一部分。通过这个过程,我们可以熟悉我们的数据集,了解其内容,概览数据的统计信息等等。我们在许多项目中大部分时间都花在这个阶段。在某些情况下,这可能占到可用项目时间的 90%

在 Python 中进行 EDA 时,我们通常依赖像 pandas 和 matplotlib 这样的库来探索数据。通常,这会导致编写大量代码以将绘图显示为我们希望的方式。例如,使用 matplotlib 创建井日志图需要花费时间来处理和正确显示数据。

有几个 Python 库可以显著加快项目的 EDA 阶段。其中之一是 D-Tale

D-Tale是一个强大的探索性数据分析 Python 库,它使您能够方便地以交互方式查看、分析和编辑 pandas 数据框中的数据。如果您想在不下载的情况下探索 D-Tale 的功能,可以查看这个实时示例

要在您自己的系统上开始使用 D-Tale,您需要通过终端安装它,并运行以下命令。

pip install dtale

如果您使用的是 Anaconda,您需要使用以下命令:

conda install dtale

导入库和加载数据

任何 Python 项目的第一步是导入我们将要使用的库。在这种情况下,我们只需要pandas来从 CSV 文件中加载数据,以及D-Tale来进行分析。

import pandas as pd
import dtale

一旦导入了库,我们就可以导入数据。以这个例子为例,我们将使用从挪威海岸的多个石油和天然气井中获取的测井数据。我们为本教程使用的数据集是 Xeek 和 FORCE 2020 机器学习竞赛使用的训练数据集的一个子集(Bormann et al., 2020)。该数据集在挪威政府的 NOLD 2.0 许可证下发布,详细信息可以在这里找到:挪威开放政府数据许可证(NLOD)2.0。完整的数据集可以在这里访问。

要读取数据,我们调用以下内容:

df = pd.read_csv('Data/Xeek_Well_15-9-15.csv')

将数据加载到 D-Tale 中

一旦数据加载完成,我们就可以开始探索数据集。通常在处理数据时,我们会使用几个 pandas 函数。然而,使用 D-Tale 时,我们只需调用以下内容:

dtale.show(df)

一旦我们调用dtale.show(df),我们将看到一个类似下面的交互式数据表。这提供了比我们习惯使用的基本 pandas 函数更好的、更友好的体验。

D-Tale 创建的测井数据交互数据框。图片来源:作者。

在显示的左上角,我们可以看到两个数字。底部的数字(17717)表示数据框中存在的行数,右侧的数字(12)表示列数。

D-Tale 的行和列计数信息。图片来源:作者。

如果我们点击指向右侧的三角形,我们将看到以下菜单,该菜单提供了许多功能来简化数据分析。

D-Tale 中的菜单选项。图片来源:作者。

由于 D-Tale 中有许多选项,我们将只关注其中的一些。

使用 D-Tale 的数据摘要

使用 pandas 时,我们经常调用 [.describe()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.describe.html) 方法 来获取数据集的一些总结统计信息。通过 D-Tale,我们也可以做到这一点,但同时会获得更多的信息。

从上面的菜单中,我们只需选择“描述”。这将打开一个新页面,显示摘要信息。

在下面的示例中,我们可以看到,当选择 WELL 列时,我们得到关于该列内容的信息。由于该列的数据类型为字符串,我们可以获得关于字符、字符串长度以及该列中唯一值的详细信息。

如果你处理的是包含多个井信息的 CSV 文件,并且需要知道这些井是什么,那么这个功能尤其方便。

使用 D-Tale 创建的列摘要。图片由作者提供。

如果我们查看 LITH 列,我们将看到相同类型的摘要,但现在我们会看到该列中所有存在的岩性。

再次说明,当我们探索井日志数据时,这个功能很有用,因为我们通常对某些岩性感兴趣,用于岩石物理和井日志分析。

使用 D-Tale 创建的岩性列摘要。图片由作者提供。

我们可以通过点击“值计数”按钮,进一步探索 LITH 数据。这将展示一个漂亮的条形图,说明数据集中每种岩性的出现次数。

D-Tale 中的条形图,显示井中不同岩性的出现情况。图片由作者提供。

如果我们对数值数据做相同的处理,我们可以获得一些额外的选项来可视化我们的数据集。

例如,如果我们查看数据集中的 DTC(声波压缩慢度)列,我们可以获得关于数据的关键统计信息,包括均值、百分位数、标准差等。

我们还可以得到一个漂亮的箱线图,显示数据的分布情况。

在摘要底部,我们有该列中唯一值的信息,以及查看任何异常值的选项。

使用 D-Tale 创建的数值曲线数据摘要。图片由作者提供。

如果我们点击直方图按钮,我们可以在一个组合的直方图和 KDE 图上查看 DTC 列的值分布。这是一种非常常见的数据可视化方式,可以让我们查看数据的分布情况。在下面的示例中,我们有一个双峰分布,第一个峰值大约为 85 us/ft,第二个峰值大约为 157 us/ft。

在这个页面上,我们还可以选择更改直方图的箱数。如果我们想增加或减少查看的细节层次,这个功能非常有用。

D-Tale Describe 模块中的交互式直方图。图像由作者提供。

D-Tale 的 Describe 部分的一个很棒的功能是按类别查看值的能力。

在进行岩石物理或井眼日志分析时,我们通常希望查看不同地质层或岩性的值如何不同。这个图表提供了部分功能,使我们能够快速理解每组中的值。

通过岩性或地质层等类别轻松可视化数据。图像由作者提供。

使用 D-Tale 可视化数据完整性

在处理数据集时,在开始应用高级分析或机器学习之前考虑数据集的完整性是至关重要的。有一些可用的库;我最喜欢的之一是missingno — 这是一个简单易用的 Python 库,已经集成到 D-Tale 中。

如果我们进入菜单并选择 Visualise,然后选择 Missing Analysis,我们将能够查看missingno图表。

使用 D-Tale 分析和理解井眼日志数据集的数据完整性。图像由作者提供。

有关这些图表的更多信息,请查看下方我的文章,其中我探讨了这个库的功能。

使用 missingno Python 库在机器学习之前识别和可视化缺失数据

使用岩石物理井眼日志测量的示例

[towardsdatascience.com

使用 D-Tale 的交互式图表可视化数据

数据科学和岩石物理学的一个关键方面是可视化数据。这使你可以感觉到数据,而不是仅仅查看表中的原始数字。

要访问这些图表,我们需要导航到 D-Tale 菜单中的 Visualise → Charts。选择后,将打开一个新的浏览器标签页。

从这里开始,我们有大量选项可以选择。

创建折线图

在岩石物理学中,一个关键的图表是日志图。这本质上是一个折线图,其中一个轴上绘制了深度,另一个轴上绘制了记录的测量值。这使我们能够可视化测量值在井眼中的变化,从而可以解读所钻探的地质。

使用 D-Tale 中的折线图生成的简单日志图。图片由作者提供。

我们甚至可以在每个折线图中绘制多个曲线并控制它们的比例。这对于创建密度-中子折线图非常有用。不幸的是,我们无法对该图表应用岩性阴影,但这只是一个小问题。

使用 D-Tale 创建的密度-中子日志绘图轨迹。图片由作者提供。

然而,一个非常有用的功能是能够按类别为线着色。这在尝试理解不同地层内的日志响应时尤其有用。

使用 D-Tale 创建的日志绘图轨迹/折线图显示了按不同地层着色的体积密度日志。图片由作者提供。

使用 D-Tale 创建散点图(交叉图)

散点图(在岩石物理学中也称为交叉图)允许我们将两个变量相互对比。这使我们能够识别趋势、岩石物理方程的关键解释参数,以及数据之间的关系。

在 D-Tale 中,我们可以轻松创建非常常见的岩石物理图。密度-中子交叉图。

D-Tale 提供了大量选项来自定义绘制的数据。一旦选择了 x 轴和 y 轴变量,我们还可以选择第三个变量来为图表着色。

散点图显示了在 D-Tale 的 Charts 模块中密度和中子孔隙度数据。图片由作者提供。

这个界面的一大优点是我们可以去除不希望绘制的组,以便集中关注重要的组。

散点图显示了在 D-Tale 的 Charts 模块中,去除某些组后的密度和中子孔隙度数据。图片由作者提供。

这个设置的一个小问题是我们无法控制标记的大小或形状。能够使标记更小,以便看到更多的数据点将是很好的。

使用 D-Tale 的 Pearson 相关矩阵

在构建用于岩石物理属性预测的机器学习模型时,我们希望识别任何我们将使用的预测变量是否彼此之间高度相关——这是一个称为多重共线性的问题。我们还希望能够识别出在模型中预测目标变量最合适的变量。

要显示相关性矩阵,我们可以进入菜单并选择相关性。这将返回一个热图,显示每个数值变量的 Pearson 相关系数。

使用 D-Tale 的井日志测量之间的相关性。图片由作者提供。

从返回的表格中,我们可以看到一些测井测量值之间具有较高的相关性。例如,RHOB 和 DTC 之间有很强的负相关,这也是预期的。岩石孔隙度越高,密度(RHOB)值越低,声波压缩慢度(DTC)越高。

总结

D-Tale Python 库在探索性数据分析方面非常强大,对于测井数据的质量控制和早期分析也非常有用。本文仅覆盖了 D-Tale 内部功能的一小部分,但它们非常强大,能够显著加快测井数据集中的数据 QC 和探索阶段。这是你数据科学工具包中应该拥有的一个库。

感谢阅读。在你离开之前,你应该订阅我的内容,以便在你的收件箱中获取我的文章。 你可以在这里做到这一点!或者,你可以 注册我的新闻通讯 ,以免费获取额外内容。

其次,你可以通过注册会员来获得完整的 Medium 体验,支持数千位其他作者和我。只需每月 $5,你就可以访问所有精彩的 Medium 文章,还可以通过你的写作赚取收入。

如果你通过 我的链接注册你将直接支持我,并且这不会增加你的费用。如果你这样做,非常感谢你的支持。

日常站会可能不会提升团队的敏捷性

原文:towardsdatascience.com/daily-standups-may-not-improve-your-teams-agility-87da475ff57c

如果你觉得日常站会对你的数据科学团队没有帮助,你可能是对的

Benjamin Thürer Benjamin Thürer

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

--

敏捷团队最常见的会议或仪式之一是日常站会。它通常很简短(最多 15 分钟),大多数时候在一天中的相同时间举行,并且每个团队成员都必须参加。从理论上讲,这个会议可以是对齐团队的一个很好的方式,确保每个团队成员都知道其他人当前在做什么,并暴露他们面临的挑战。这使得日常站会也成为数据团队的畅销书。到目前为止,一切都很好,如果你觉得站会对你的数据团队有很大的帮助并使你高效,那么没有必要继续阅读,因为“永远不要改变一个正在运行的系统”。

“…许多团队进行日常站会只是因为有人告诉他们这样做,或者因为它们一直被这样做。”

日常站会的常见问题

然而,在实践中,日常站会通常并不像上面描述的那样有益。我认识的大多数数据团队都难以遵守时间表,其他团队则在让每个人始终参与或做好准备方面遇到困难。所有这些都是团队需要更深入讨论的明显迹象,或者他们没有看到站会的巨大价值。即使对于那些没有这些问题且按书本进行专业站会的团队,由于时间短,站会通常处于较高层次,更像是一个报告工具,而不是帮助团队完成工作的工具。我确实相信许多团队进行日常站会只是因为有人告诉他们这样做,或者因为它们一直被这样做。

这导致数据团队每天都开不必要的会议,每周每个团队成员浪费约 1.15 小时的宝贵时间。当然,一个解决方案是更严格地遵循常见的敏捷和 Scrum 建议。但还有另一个选择:重新考虑你实际上想通过这些会议解决什么。接下来,我将向你展示如何轻松地用替代方案取代每日站会,并利用节省下来的时间进行每周的团队时间,以深入支持你的数据科学家和数据分析师的需求。

5 人团队每天进行 15 分钟站会的预计年度“成本”(估算每人每小时 300 美元,年工作 45 周)。

你应该什么时候重新考虑每日站会?

既然每日站会已被证明是有用的,并不意味着所有数据团队现在都应该停止这一做法。然而,对于某些团队来说,尝试其他仪式可能会有很大的好处。一种方法是直接与团队开诚相见。某些团队成员可能已经对这个话题有很强的看法。否则,我会给你提供一个列表,如果以下至少一个点成立,我会考虑采取新的方法:

  • 你之所以进行站会,仅仅是因为其他人也这样做

  • 你之所以进行站会,仅仅是因为在你加入团队之前就已经这样做了

  • 你的站会经常超时

  • 在你的站会上,团队通常讨论私人话题

  • 团队成员经常缺席站会

  • 大多数团队成员没有准备好或没有要提出的事项

  • 站会更像是向团队领导汇报,而不是帮助团队成员

替代方案有哪些?

如果上述一个或多个点成立,你的团队可能会从不同的方法中受益。然而,关于替代方案的问题很大程度上取决于基本问题的答案:“站会应该改善你团队的什么?”当你找到这个问题的答案时,提出替代方案可以变得很直接。对我来说,每日站会的目的有三重,我会分别解决这三个目的。这三个目的分别是:

  1. 团队更新:向团队通报每个人目前在做什么以及面临哪些挑战。

  2. 支持:从团队成员那里获得帮助,以解决复杂的问题。

  3. 社交:作为一个团队聚在一起,培养团队精神。

使用 Geekbot 在 Slack 中的自动站会示例。此处未显示的额外问题是“今天有任何障碍吗?”。

(1) 为了向团队更新现有工作和挑战,可能完全可以选择不召开会议。这种更新是在一个非常高层次上,可以很容易通过虚拟站会离线完成。例如,我们使用一个geekbot来配合slack,它会要求所有团队成员提供简短的更新。其他替代方案可以是使用技术图纸或敏捷工作管理工具,并确保每个团队成员每天更新他们的任务/卡片。

使用技术图纸来更新数据科学团队的工作流程。

(2) 为了确保每个团队成员得到他们需要的支持,我强烈推荐每周至少召开一次较长时间的团队会议,我们称之为“团队时间”。这次会议应该持续 30-45 分钟,并确保有足够的时间真正搞清楚问题并找到解决方案。每个团队成员都可以提出一个主题,团队一起讨论。如果没有需要讨论的挑战,这也是其他知识分享方式的绝佳论坛。当你总结这些成本时,你会发现它们和每日站会的成本相似甚至更高,但这些会议实际上是有帮助的,因为它们允许团队解决问题并分享知识,从而取代其他会议,使工作更加高效。

(3) 社交方面是一个很少被提及的每日站会的需求。但对我而言,这是一个误解。一个健康且具有社交性的团队将永远是一个高效的团队。发展良好的团队氛围和精神应是每个人的关键利益。上述提到的团队时间可以服务于这一目的,但除此之外,还应该定期举行社交活动或其他形式的社交互动。

回顾过去,进行专门的会议并利用这段时间进行更长时间的深入团队时间会议,是我团队过去做出的最佳决策之一。实际上,我们每周有 2 次团队时间

总结

如果你的数据团队没有看到定期站会的必要,这可能表明其他替代方案更为有效。高层次的团队更新可以通过自动聊天工具完成,深入讨论的主题可以在专门的团队会议中讨论,而社交活动可以确保团队精神。根据团队的需求,这些替代方案可能需要相同的时间,但从长远来看,会使团队更高效。

除非另有说明,否则所有图片均为作者提供。

DASC-PM:数据科学项目的新型流程模型

原文:towardsdatascience.com/dasc-pm-a-novel-process-model-for-data-science-projects-9f872f2534b1?source=collection_archive---------8-----------------------#2023-02-07

引入 CRISP-DM 流行方法的一种替代方法

Jonas DieckmannTowards Data Science Jonas Dieckmann

·

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

--

有了对数据的轻松获取,计算能力的增加和用户友好的分析软件,各行各业的数据科学项目数量急剧增加。尽管早期的感觉就像是西部荒野一样,但现在更常见(也更推荐)为与数据相关的项目遵循特定的框架。

过程模型为定义和组织项目生命周期中的任务、活动和可交付成果提供了清晰和结构化的方法。通过实施一致的过程,团队可以确保实现所有项目目标,并且最终的可交付成果具有较高的质量。此外,过程模型有助于减少延迟、错误和预算超支的风险,使其成为数据科学项目管理的必要组成部分。

请注意,整篇文章只是对框架的快速介绍和简要总结,该框架是由一个较大的作者团队制作的,而我是 DASC-PM v1.1 的合著者之一。

Schulz 等人(2022 年): DASC-PM v1.1 — 用于数据科学项目的过程模型”**(2022), 出版商:NORDAKADEMIE gAG Hochschule der Wirtschaft, ISBN:978–3–00–064898–4, DOI:10.25673/32872.2

数据科学过程模型 DASC-PM(Schulz 等,2022 年)

DAta SCience - Process Model(DASC-PM)是一个描述与项目相关的关键领域和待完成阶段的新颖的数据科学项目过程模型。它解释了各个阶段内的典型任务,并描绘了涉及的项目角色和所需的能力。以下文章旨在介绍主要概念,并阐明与已知概念(如CRISP-DM)、TDSPKDDSEMMA)的优势。

一种新方法的兴起

基于跨行业标准数据挖掘过程(CRISP-DM),已经存在一个众所周知的“独立于行业部门和所使用的技术的数据挖掘项目执行框架。” [4] 除此之外,其他相关概念如 TDSP、SEMMA 或 KDD 旨在提供在几个细节上相互超越的可比较模型。然而,有兴趣退后一步来识别对数据科学过程模型有关的(元)需求。这些需求是通过 2019 年 4 月至 2020 年 2 月的调查收集而来,涵盖了科学和实践两个方面,因此回答了第一个研究问题 [2]:

对数据科学过程模型施加了哪些理论和实际要求?

数据收集在一个由 22 名专家组成的工作组中进行,包括 9 名教授以及 13 名具有相关理论和实践经验的数据科学从业者和科学家。在此基础上,研究了相关流程模型在多大程度上满足先前收集的要求。下面的表格提供了根据具体要求和流程模型对调查结果的概述。填充的 Harvey 球表示一个要求被相应的流程模型满足,半填充的表示一个要求至少被提及,空白的表示一个要求既未提及也未满足。[2]

(元)数据科学流程模型的要求(Schulz 等,2020)

认识到相关的知名流程模型没有一个能够满足对数据科学项目流程模型提出的 17 项要求,Schulz 等 开发了一种新的数据科学流程模型,称为 DASC-PM,以解决第二个研究问题 [2]:

如何构思一个与相关理论和实践要求对齐的数据科学流程模型?

五个 DASC-PM 阶段的简要介绍

在接下来的部分,我们将简要介绍新创建的流程模型中的五个核心阶段的主要思想:项目订单、数据提供、分析、部署和应用。[3]

数据科学流程模型 DASC-PM(Schulz 等,2022)

相应的领域和任务将以以下方式可视化:

阶段表示图例(Schulz 等,2022)

阶段 1:项目订单

领域内存在的问题触发了用例开发。随后,有前景的用例被配置为数据科学项目概要。所有相关任务都在项目订单阶段反映。通过对项目的早期、相对全面的考虑,这里也常常需要几乎所有技能领域的全面能力。[3]

阶段 1:项目订单(Schulz 等,2022)

阶段 2:数据提供

在数据提供阶段,所有可以分配到数据关键领域的活动都被总结,因此使用的术语被广泛地表述。该阶段包括数据准备(从记录到存储)、数据管理和探索性数据分析。该阶段产生一个适合进一步分析的数据源。[3]

阶段 2:数据提供(Schulz 等,2022)

阶段 3:分析

在数据科学项目中,可以使用现有的程序或开发新的程序,相关决策本身是一个独立的挑战。因此,这个短语不仅包括执行分析,还包括相关活动。该阶段的工件是经过方法论和技术评估的分析结果。[3]

第三阶段:分析(Schulz 等,2022)

第四阶段:部署

在部署阶段,创建分析结果的适用形式。根据项目的不同,这可能需要全面考虑技术、方法论和专业任务——或者可以以务实的方式处理。分析工件可以包括结果、模型或过程,并以各种形式提供给目标受众。[3]

第四阶段:部署(Schulz 等,2022)

第五阶段:应用

项目执行后使用工件并不是数据科学项目的主要部分。然而,监控是必要的,以检查模型在应用中的持续适用性,并从应用中获得用于持续和新发展的发现(包括用于迭代方法的开发)。[3]

第五阶段:应用(Schulz 等,2022)

覆盖关键领域

除了这五个过程步骤之外,该模型还包含三个覆盖所有项目阶段的关键领域:

领域

在数据科学过程中的许多环节,都需要广泛的领域背景知识。例如,分析目标的识别或对数据及其来源、质量和联系的正确理解。其他例子包括在应用中的分析结果的评估和分类,以及随后的实际使用。领域“Domain”还包括对现有解决方案的优缺点评估、需求分析、模型参数化的支持,以及最终的项目成功评估。数据科学项目的法律、社会和伦理方面也将在这里讨论。[1]

科学性

数据科学项目虽然本质上是科学的,但并不意味着它们声称是完整的、正式的、学术的或一致以研究为导向的。虽然这可能确实适用于研究项目,但在商业环境中的科学性主要指的是一个稳固的方法论:这是科学工作的通常预期特征或最低要求。[1]

所定义的项目顺序必须在每个项目阶段中系统化处理。在这里,特别需要提到的是项目管理和通过使用过程模式突出的结构化处理。必须在考虑项目情况和领域特定内容的同时,明确所需的科学性程度。[1]

IT 基础设施

数据科学项目的所有步骤都依赖于基础 IT 基础设施;然而,实际的 IT 支持范围应针对每个项目进行单独评估。即使特定的硬件和软件的使用在组织内经常被确定,也必须在所有项目阶段中考虑 IT 基础设施的限制性和赋能特征(以及在适用情况下扩展基础设施的可能性)。[1]

摘要

DASC-PM 是一种科学方法的结果,用于收集、结构化和处理数据科学领域中的(元)需求。由于所有现有的、知名的模型都未能满足所有定义的要求,因此有必要引入一种新颖的概念,使研究人员和商业及工业实践者能够使用 DASC-PM 以阶段导向的方式结构化数据科学项目。然而,作者声称 DASC-PM 不应被视为一个完成的交付物,而应被视为一个可以通过科学和实践讨论不断改进的框架。

[## Jonas Dieckmann - Medium

阅读 Jonas Dieckmann 在 Medium 上的文章。分析经理 & 产品负责人 @ Philips | 热情地写作和谈论……

medium.com](https://medium.com/@jonas_dieckmann?source=post_page-----9f872f2534b1--------------------------------)

希望你觉得这很有趣。告诉我你的想法,并随时在 LinkedIn www.linkedin.com/in/jonas-dieckmann 上与我联系,或在 Medium 上关注我。

另见我其他的一些文章:

## 案例研究:将数据科学过程模型应用于实际场景

为供应链中的材料规划开发机器学习模型

towardsdatascience.com ## 人工智能中的伦理:偏见算法的潜在根本原因

理解数据偏差的替代方法

towardsdatascience.com

参考文献

整个文章是对以下框架的简要总结:

[1] Schulz et al. (2022): DASC-PM v1.1 — A Process Model for Data Science Projects”* (2022),出版商:NORDAKADEMIE gAG Hochschule der Wirtschaft,ISBN:978–3–00–064898–4,DOI:10.25673/32872.2

以及介绍部分:

[2] Schulz et al. (2020): “Introducing DASC-PM: A Data Science Process Model” (2020)。ACIS 2020 会议论文集,45. aisel.aisnet.org/acis2020/45

另一个可引用的来源在以下书籍中提供:

[3] Kuehnel, S., Neuhaus, U., Kaufmann, J., Schulz, M., Alekozai, E.M. (2023)。“Using the Data Science Process Model Version 1.1 (DASC-PM v1.1) for Executing Data Science Projects: Procedures, Competencies, and Roles。”在:Barton, T., Müller, C. (编) 应用数据科学。Springer Vieweg,维斯巴登。 doi.org/10.1007/978-3-658-38798-3_8

其他参考文献:

[4] Wirth, R., Hipp, J. (2000) “CRISP-DM: Towards a Standard Process Model for Data Mining”,第 4 届国际知识发现与数据挖掘应用会议论文集,第 29–39 页。

仪表盘已经死了:三年后的回顾

原文:towardsdatascience.com/dashboards-are-dead-3-years-later-72347757bfa6?source=collection_archive---------0-----------------------#2023-04-12

2023 年仪表盘的目的是什么?

泰勒·布朗洛面向数据科学 泰勒·布朗洛

·

关注 发布于 面向数据科学 ·6 分钟阅读·2023 年 4 月 12 日

--

2020 年 4 月 9 日,我发布了一篇名为“仪表盘已经死了”的文章。我写这篇文章主要是为了发泄作为数据分析师的积压情绪,当然也为了挑战当时最流行的数据工具:仪表盘。

令我惊讶的是,很多人读了这篇文章。仅在第一个周末,就有超过 60,000 人阅读,到目前为止已超过 250,000 人。这篇现在臭名昭著的文章显然引起了其他人的共鸣(或者只是让他们非常生气)。在过去的三年里,我尝试写了各种后续文章,但总是在最后一刻却退缩了,不确定自己到底想说什么。

这个周末标志着那篇文章发布三周年,到现在我可能已经有足够的距离来清楚地看到事情——无论是当时行业的状况,还是我们取得的进展。

图片由Sonja LangfordUnsplash提供

仪表盘并没有死去

不用担心,我看到角落里的大象了。

当然,仪表盘并没有真正消亡。为了澄清一下,我并不是在暗示这一点。

我试图表达的是,仪表盘一直以来有一个明确的用途:快速展示数字。但我们却误用了它们,期待它们为我们做一切。我们要求它们讲故事、视觉上吸引人、快速传达信息、作为数据门户,还要做任何我们能想到的事情。当然,它们不能做到这些。

我确实相信,有时仪表盘最好的选择。但我也坚信我们应该得到更好的。

但是它们正在逐渐消亡

说有些人对这篇文章反应强烈可能有些轻描淡写(只要看看评论就知道了)。即使是那些同意仪表盘基本缺陷的人,也很难想象它们会消失。

但如今,情况显然不同了。仪表盘不再是数据社区的宠儿。Tableau 似乎已青睐,而语义层承诺从根本上动摇这个领域。

此外,我们从未有过如此多的仪表盘替代方案。现在,数据团队可以选择反向 ETL、BI 笔记本、数据画布、自助服务解决方案以及嵌入式分析。看到这些新选项的出现令人兴奋,因为每一个选项都略微改变了我们解决问题的方式——这正是关键所在……

这从来不仅仅是关于仪表盘

我在过去几年中逐渐清楚地看到,“仪表盘已死”与仪表盘本身的关系远没有与其周围的事物相关。最终,我的不满根源在于关系、沟通、流程和人员。但责怪那个被塞进来解决所有这些问题的工具要简单得多。

这大大复杂了问题。改变我们的工作方式比采用新工具要困难得多。这要求我们从根本上重新思考问题、我们的角色以及我们的关系。

在这里,我们在过去三年中取得了一些令人兴奋的进展:

分析师不仅仅是图表构建者

当 DaD 发布时,许多人问了一个问题:“如果分析师不再构建仪表板,他们会做什么?”

现在想到我们曾经认为分析师与仪表板密不可分,真的很疯狂,考虑到他们今天的角色更多。分析师现在是探索者、数据翻译者和商业的可信顾问。他们终于找回了他们的灵感。

考虑到AI 将在我们的图表构建中扮演的角色,分析师的这种观念变化尤为及时。

这个转变不仅仅是防御性的举措,它对我们的福祉是必要的。在过去五年里,我遇到了太多对数据分析师感到愤世嫉俗的人,他们对作为分析师的时光感到失望,完全放弃了他们的数据职业。至少这里有一个人际问题正在得到急需的关注。如果我真的很乐观(这是我出名的),那么我认为我们可能很快会看到一个分析师文艺复兴的到来。

我们在提出更大的问题

作为分析师,当没有人查看我们的仪表板时,我们的经理把责任推给了我们。“你不知道设计原则吗?你使用了正确的颜色吗?信息排列顺序对吗?”

现在回过头来看,很容易意识到再多的设计专业知识也无法让那些仪表板被多看一次。它们根本上是错误的解决方案,而不是错误的设计。

作为一个行业,我们似乎达成了同样的结论,从“如何使这个仪表板更好”转向“交付这些信息的最佳方式是什么?”

区别很微妙,但后者提供了更多可能的解决方案。我们终于准备好抛出一些想法了。

数据是产品吗?实施数据网格的优缺点是什么?我们应该在自助服务的光谱中走多远?

我喜欢这些问题。它们如此有效地要求我们审视我们是谁以及我们不是谁。这些讨论和实验是必要的,确实是不可或缺的,如果我们真的要取得突破。

我们还有很长的路要走

尽管这些初步的变革迹象很有希望,但事实仍然是,驱使我编写 DaD 的大多数问题仍然存在。依我看,要解决这些问题,我们有三个关键问题需要解决:

1 — 平衡堆栈

到目前为止,我甚至还没有提到在过去三年中变化最大的部分:堆栈的背面。以前我们数据库中的几个预定视图现在已经变成了一个复杂的生态系统,包含互联的管道、编排计划和复杂的数据库环境。如果你退一步来看,我们在这方面取得了如此大的进步,真的很令人惊叹。

不过,我们还没有将这些堆栈背面的创新转化为堆栈前端的价值。我们仍在摸索如何利用这些。

为了做到这一点,我们需要走出我们的泡沫在堆栈中平衡我们的精力

2 — 信任不仅仅是对的

也许由于过度关注我们的管道,今天的数据团队在文化上与三年前有所不同。今天的主要目标似乎是通过可靠性和准确性建立信任。

结果是,许多团队现在比以前更加严格和过程驱动。这些控制措施通过减少有人部署错误代码并破坏大家报告的机会,有助于优化准确性和可靠性。

一个建立在信任基础上的文化是一个伟大的目标。但准确性并不是建立信任的唯一途径。

信任还涉及到关系、沟通,甚至是自由犯错的感觉。

一个既注重这些价值观可靠性的文化有潜力显著改善数据团队和我们支持的利益相关者的生活。

3 — 我们仍然需要合适的工具

伟大的软件能激发我们最好的一面。为了实现我们在这里讨论的变革,我们需要一些来自优秀软件的支持。特别是那些:

  • 优先考虑协作:如果我们要改善数据团队与业务团队之间的关系,我们需要我们的工具积极创建一个合作空间。

  • 通过透明度来强化信任:不再有“隐藏查询”。如果我们关心信任,那透明地展示一个数字的来源不是更好吗?

  • 足够灵活以便我们进行实验:我不相信会有一种工具完全取代仪表盘。我们将需要不同的解决方案来应对不同的需求,有些需求我们甚至尚未预见到。

从仪表盘到笔记本再到白板。作者图片。

附注:我知道当我提到 count.co 在这方面的出色表现时,大家真的很喜欢 DaD 的这一部分,但这次我会避免提及,只说一句:是的,count.co 希望在这里提供帮助。我们已经抛弃了 BI 笔记本游戏,转而采用一个更具协作性和灵活性的画布。是的,我对它很看好,但这也是我在那里的原因😀。

现在怎么办?

三年前,我们很多人都认同仪表板存在的问题。直到现在,三年过去了,我们才开始将那种初生的挫败感转化为实际而稳健的改变。

所以我真正想说的是,我们终于迎来了好时光。系好安全带。

数据访问 API 无需复杂性即可访问数据湖表

原文:towardsdatascience.com/data-access-api-over-data-lake-tables-without-the-complexity-4deb68ee88b3?source=collection_archive---------3-----------------------#2023-09-28

使用 DuckDB 和 Go 在 S3 数据湖文件上构建一个稳健的 GraphQL API 服务

Alon Agmon数据科学前沿 Alon Agmon

·

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

--

图片来源:Joshua Sortino 选自 Unsplash

1. 简介

数据湖表主要由数据工程团队使用大数据计算引擎(如 Spark 或 Flink)利用,也被数据分析师和科学家使用,后者使用重型 SQL 查询引擎(如 Trino 或 Redshift)创建模型和报告。这些计算引擎已成为访问数据湖数据的标准,因为它们被设计来有效处理大数据处理通常涉及的挑战:扫描大量数据,处理基于云的对象存储,读取和写入查询优化的格式化文件,如 Parquet 或 ORC 等。

然而,将大数据产品(或其某些汇总视图)通过某种 API 提供给轻量客户端,如内部微服务,也是一个常见的需求。假设我们有一个数据湖表,存储了由某些 Spark 应用生成的实时客户统计数据。这些数据可能主要用于内部报告,但对我们组织的其他服务也可能很有价值。尽管这是一个常见的需求,但它远非简单,主要因为它需要完全不同的工具集。将 S3 桶中的 parquet 文件提供给低延迟的 HTTP API 并不简单(尤其是当文件持续更新并且在提供之前需要进行某些转换时)。

为了使这种用例有效,我们通常需要一个能够以快速客户面向延迟处理查询的数据库。同样,我们需要一些 ETL 作业来处理和转换 S3 中的数据文件并将其加载到数据库中。最后,我们必须创建一个适当的 API 端点来服务客户的查询。

确实,如插图所示,提供给轻量客户端快速查询数据湖文件的能力通常会带来更多的移动部件和流程,以便要么将数据复制并摄取到更昂贵的客户面仓库中,要么将其汇总和转换以适应低延迟数据库。

本文的目的是探讨和演示一种不同且更简单的方法,利用轻量级的内进程查询引擎来解决这一需求。具体来说,我将展示如何使用内进程引擎,如DuckDBArrow Data Fusion,以创建能够处理数据湖文件和数据量并且作为快速内存存储来服务低延迟 API 调用的服务。采用这种方法,我们可以将所需的功能高效整合到一个可以水平扩展的单一查询服务中,该服务将加载数据、汇总并存储在内存中,并高效、快速地处理 API 调用。

接下来,第二部分将概述该服务的主要要求和构建模块,并解释它们如何帮助我们解决主要挑战。第三部分将深入服务的核心——数据加载和查询功能,并演示如何使用 DuckDB 和 Go 实现它。(本文将专注于 DuckDB 和 Go,但你可以在下面链接的仓库中找到使用 Arrow Data Fusion 实现这一概念的 Rust 实现)。第四部分将在其上添加 GraphQL API 服务层。第五部分将作结。

2. 主要构建模块

应注意,这种方法的基本假设是我们希望提供给 API 的数据可以适配到我们服务的内存中,或运行服务的机器中。对于某些用例来说,这可能是一个问题,但我认为这比看起来的限制要小。例如,我曾经使用这种方法处理一个包含 2M 记录和 10 列的内存关系表,内存使用量约为 350MB。我们往往忘记,我们实际提供的数据通常比我们存储或处理的数据要小得多。无论如何,这都是一个需要牢记的重要因素。

一个独立的服务,它将作为一个更简单、更有吸引力的替代方案来取代上述常见架构,应该至少满足以下要求:

  • 它应该能够直接从我们的数据湖或对象存储中舒适地读取和转换数据文件。

  • 它应该能够在内存中存储关系数据,并以低延迟响应查询。

  • 它应该具备水平扩展性。

  • 查询、转换和加载数据应该简单且声明性强——SQL 将是最方便的方法。

简而言之,我们希望创建一个可以直接从源加载和转换数据、有效存储数据并快速查询的服务,而不是扩展应用程序的基础设施,增加数据库和额外的 ETL 过程。

在我看来,这 3 个特性组合是DuckDB(本文的重点)和Arrow Data Fusion带来的最大优势之一。虽然内存数据库并不是新事物,但 DuckDB 和 Arrow Data Fusion 的颠覆性在于它们的可扩展性,这使得我们可以使用扩展轻松地添加能力,直接读取和写入不同格式和位置的数据,并且以规模化和快速的方式进行。

因此,我们的服务将由 3 个主要组件或层组成,这些组件或层将互相包装或封装:一个低级数据组件,它将封装一个 DuckDB 连接(我将其称为DataDriver),一个DAO组件,它将使用驱动程序来执行查询和处理 API 请求,以及一个提供服务的 API 解析器。

换句话说,依赖关系及其关系方面,我们有以下结构:

**API-Resolver** *封装* 一个 **DAO 结构体** *封装* 一个 **DataDriver** **结构体** *封装* 一个 **DuckDB 连接**

下一节将重点讨论较低层(DAO 结构体和 DataDriver),而接下来的部分将讨论顶部 API 层以及它如何将所有内容整合在一起。

3. 数据加载和 DuckDB 查询

在这一部分,我们将创建一个封装 DuckDB 连接的驱动程序组件。它将负责初始化 DuckDB,并暴露一个接口来执行 SQL 语句和查询。我们将使用优秀的go-duckdb库,它通过静态链接到其 C 库,为 DuckDB 提供了一个database/sql接口。

初始化 sql.DB 连接到 DuckDB

如前所述,我们将用一个名为DuckDBDriver的结构体封装sql.DB接口,该结构体将负责正确初始化它。我们通过使用 Connector 对象执行多个初始化语句(bootQueries)来初始化 DuckDB。该连接器执行设置 AWS 凭证的语句(因为我们要从 S3 加载数据),以及加载和安装我们的服务所需的扩展:parquet扩展(用于读取 parquet 文件)和httpfs(用于直接从基于 HTTP 的对象存储(如 S3)中读取数据)。

如上面的代码块所示,函数getBootQueries()简单地返回一组初始化语句作为字符串(你可以在这里查看这些语句)。初始化语句由连接器执行,因此当我们调用OpenDB()时,我们会得到一个已加载所需扩展和秘密的 DuckDB 作为 sql.DB 连接。由于 go-duckdb 为 DuckDB 连接提供了database/sql接口,因此其主要查询功能可以相当简单地实现和暴露:

如前所述,DuckDB 数据驱动程序结构体将简单地充当一个实用类,用于封装到 DuckDB 数据库的连接,而所有查询执行将由一个 DAO 结构体有效管理,该结构体将拥有一组使用驱动程序方法的业务逻辑函数。DAO 结构体将进一步封装 DuckDBDriver 结构体。

加载缓存数据

在服务数据后端初始化的最后阶段,我们将把我们想要提供的数据从 S3 中的 parquet 文件加载到内存表中。为此,我们将使用驱动程序的execute()函数和 CTAS 查询,该查询将使用我们可以在 SQL 中表达的任何变换创建一个命名表,从read_parquet()函数获取数据。一个示例将使这点更加清楚。

假设我们有一个包含描述我们 users 的数据的 parquet 表。我们想创建一个服务,该服务将仅从此 parquet 表中暴露 3 个字段以便于快速 API 访问:namelast_nameage。我们还希望确保 age 字段能够作为整数访问,尽管它在 parquet 文件中以字符串形式保存。

为此,在我们的 DuckDB 驱动程序初始化了所需的扩展,并设置了所需的 AWS 凭证后,我们只需执行一个 SQL 语句,将我们想要的数据直接从 S3 中的 parquet 文件加载到内存中,使用 read_parquet() 函数。

CREATE TABLE Users AS 
SELECT NAME, LAST_NAME, CAST(AGE as integer)
FROM read_parquet('s3://somewhere/data/*')

在这个语句中,我们基本上创建了一个名为 Users 的内存表,它由我们从 parquet 文件中选择的字段组成,这些文件的位置在 read_parquet() 函数中给出。我们可以使用 DuckDB 支持的任何 SQL 函数和语法,包括复杂的查询语句和聚合。以下是如何使用这种方法初始化我们的服务的更完整示例。

这实际上是该服务的核心——当服务创建并启动时,它执行的语句基本上是从 S3 加载数据到其内存中。

在服务初始化并加载数据后,我们可以直接在内存表上执行所需的任何 SQL 查询,由我们的数据驱动程序表示,以便以子秒级延迟服务我们的 API。

4. 提供 GraphQL 服务

现在我们已经连接到一个加载了缓存 parquet 数据的内存表,最后一步是创建一个 GraphQL 端点,以便高效地响应数据查询。为此,我们将使用库 gqlgen by 99designs,它使这项任务变得相当简单。

gqlgen 的深入介绍,不幸的是超出了我的范围。对于那些对 GraphQL 不太熟悉的读者,建议浏览其 documentation,文档非常清晰明了。然而,我相信对 GraphQL 概念的一些熟悉即可跟随本节内容并理解要点。

使用 gqlgen 暴露一个 GraphQL 端点通常涉及 3 个主要步骤:(1)创建架构,(2)生成解析器代码和存根,(3)添加实现 API 函数的解析器代码。

我们将从描述我们表中一个用户的架构开始,并提供两个主要函数来获取用户数据:一个通用的获取所有功能和一个按邮箱获取功能。

scalar Time

type User {
  name: String!
  last_name: String!
  email: String!
  age: Int!
}

type Query {
  users: [User!]!
  getUsersByEmail(email: String!): [User!]!
}

创建架构后,我们在项目目录中调用 gqlgen 代码生成过程:

go run github.com/99designs/gqlgen generate

运行 generate 过程将生成大量代码,包括实际的 User 结构(我们的数据模型结构)、相应的 resolver 声明模板和 resolver implementation

让我们按顺序讨论这些内容。

解析器结构体是在一个名为resolver.go的文件中生成的,只有 2 个语句——一个没有属性或成员的结构体类型声明和一个将初始化它的构造函数(一个 new()方法)。正如我们将很快看到的那样,解析器是我们的 API 服务层,它为 API 的每个方法实现一个函数。resolver.go文件的目的是为了让我们向解析器注入任何所需的依赖项,或向其中添加任何我们需要的内容来处理 API 的查询。请记住,这正是我们 DAO 结构体的目的。我们的 DAO 结构体包装了 DuckDB 数据驱动程序,它持有对内存表的连接,并负责将 API 数据请求“翻译”为 SQL 查询。因此,我们只需注入一个初始化的 DAO 对象到解析器中,以便解析器可以使用它来执行查询。

// resolver.go

type Resolver struct {
 dao *data.DAO // add a ref to our DAO
}

func NewResolver(dao *data.DAO) *Resolver {
 return &Resolver{dao: dao} //initialize our DAO
}

生成的下一个文件(每次我们运行gqlgen generate过程时都会重新生成)是schema.resolvers.go,它是解析器方法的实现。生成的schema.resolvers文件本质上包含了在模式中声明的 API 函数的方法签名。在我们的例子中,它将包括这 2 个方法。

// schema.resolvers.go

func (r *queryResolver) GetUsersByEmail(ctx context.Context, email string)
 ([]*model.User, error) {

}
func (s *DAO) GetUsers() ([]*model.User, error) {
}

为了实现这些函数,我们首先需要在 DAO 结构体中拥有相应的方法,但为了举例说明,让我们先实现一个,然后完成所需的 DAO 代码。

// schema.resolvers.go

func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
 res, err := r.dao.GetUsers()
 if err != nil {
  log.Printf("error getting users: %v", err)
  return nil, err
 }
 return res, nil
}

正如你所见,由于我们的 DAO 被注入到解析器结构体中,我们可以简单地通过我们的解析器调用其函数。这种结构使得 API 层的代码非常干净和简单。

现在让我们编写 DAO 结构体中所需函数的实际实现。正如你所见,所需的代码非常简单。虽然我使用了一些辅助函数(你可以在配套的 github repo 中看到),但GetUsers()函数只是对我们内存中的 DuckDB 表执行一个 SQL 查询,并构建一个用户列表(请记住,model.User 结构体是由 gqlgen 使用我们的模式生成的)。

//dao.go

func (s *DAO) GetUsers() ([]*model.User, error) {
//QryAllUsers := "select * from users"
 rows, err := s.driver.Query(QryAllUsers)
 if err != nil {
  return nil, err
 }
 defer rows.Close()
 resultset, err := sqlhelper.ResultSetFromRows(rows)
 if err != nil {
  return nil, err
 }
 users := make([]*model.User, 0)
 for _, row := range resultset {
  user := newUserFromRow(row) // populate the user struct
  users = append(users, user)
 }
 return users, nil
}

现在我们基本上拥有了我们需要链在一起的所有层。也就是说,一个数据驱动程序结构体(封装了数据库连接),它被注入到一个DAO 结构体中,该结构体实现并作为所有所需 API 函数的接口,由解析器——我们的 API 处理程序调用。

组件和角色之间的关系通过它们在主server.go文件中的链式连接表达得非常清楚,该文件引导我们的服务及其依赖项。

// server.go
dataDriver := data.NewDuckDBDriver(awsCred)
dataStore := data.NewStore(dataDriver) 
resolver := graph.NewResolver(dataStore)
srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: resolver}))

http.Handle("/query", srv)

当我们的服务被初始化时,我们首先初始化我们的驱动程序,它获取与 DuckDB 内存存储的连接。接下来,我们将驱动程序注入到NewStore方法中,该方法创建一个 DAO 并使用驱动程序将数据从 parquet 文件加载到内存中。最后,我们将 DAO 结构体注入到 API 处理程序中,当服务 API 请求时它会调用其函数。

5. 结论

这篇文章的目的是提供一种替代方法,使薄客户端能够访问数据湖表的 HTTP API。这个用例越来越普遍,通常需要在我们的管道中添加许多动态组件、监控和资源。在这篇文章中,我提出了一种更简单的替代方案,我相信它适用于许多用例。我演示了如何利用 DuckDB 的查询性能和扩展来使我们的服务能够从远程对象存储中加载数据,将其保存在内存中的关系表中,并使我们能够以亚秒级延迟进行查询。更一般地说,我试图举例说明 DuckDB 扩展为我们的服务带来的强大能力以及它如何容易嵌入。

希望这会对你有用!

  • 附带的 GitHub 仓库及示例代码可以在这里找到。

  • 使用 Rust 和 Arrow Data Fusion 实现相同概念的 GitHub 仓库可以在这里找到。

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

使用 Pandas 进行 Python 中的数据汇总:分析地质岩性数据

原文:towardsdatascience.com/data-aggregation-in-python-with-pandas-analysing-geological-lithology-data-94192f5631c0

探索挪威大陆架上 Zechstein 组的岩性变化

Andy McDonaldTowards Data Science Andy McDonald

·发布于 Towards Data Science ·6 分钟阅读·2023 年 6 月 29 日

--

图片由作者使用 Midjourney(付费订阅计划)生成。

使用数据汇总技术可以帮助我们将一个庞大且几乎无法理解的数字数据集转换成易于消化且更具可读性的内容。数据汇总过程涉及将多个数据点总结为单一的度量指标,以提供数据的高层次概述。

在岩石物理学和地球科学中,我们可以应用这个过程来总结从井日志测量中解释出的地质构造的岩性组成。

在这个简短的教程中,我们将看到如何处理来自挪威大陆架的 90 多个油井的大型数据集,并提取 Zechstein 组的岩性组成。

导入库和加载数据

首先,我们需要导入 pandas 库,该库将用于从 CSV 加载数据文件并进行汇总。

import pandas as pd

一旦导入了 pandas 库,我们就可以使用 pd.read_csv() 读取 CSV 文件。

我们将使用的数据来自XEEK 和 Force 2020 机器学习竞赛,该竞赛旨在从井日志测量中预测岩性。我们使用的数据集代表了所有可用的训练数据。有关此数据集的更多详细信息,请参见文章末尾。

由于此 CSV 文件中的数据是用分号而不是逗号分隔的,我们需要在sep参数中传递一个冒号。

df = pd.read_csv('data/train.csv', sep=';')

然后我们可以运行这段代码开始加载过程。由于我们有一个大数据集(1100 万+行),这可能需要几秒钟。但一旦加载完成,我们可以通过调用df对象来查看数据框。这将返回数据框,并显示前五行和最后五行。

包含挪威大陆架井日志数据的数据框。图片由作者提供。

使用 pandas 的.map()将数值代码转换为岩性字符串

在这个数据集中,岩性数据存储在FORCE_2020_LITHOFACIES_LITHOLOGY列中。然而,当我们仔细查看数据时,会发现岩性值是以数字编码的。如果你不知道键,就很难解读哪个数字代表哪个岩性。

幸运的是,对于这个数据集,我们有键,可以创建一个包含键和岩性对的字典。

lithology_numbers = {30000: 'Sandstone',
                 65030: 'Sandstone/Shale',
                 65000: 'Shale',
                 80000: 'Marl',
                 74000: 'Dolomite',
                 70000: 'Limestone',
                 70032: 'Chalk',
                 88000: 'Halite',
                 86000: 'Anhydrite',
                 99000: 'Tuff',
                 90000: 'Coal',
                 93000: 'Basement'}

要将这应用于我们的数据集,我们可以使用 pandas 的map()函数,它将使用我们的字典进行查找,然后将正确的岩性标签分配给数值。

df['LITH'] = df['FORCE_2020_LITHOFACIES_LITHOLOGY'].map(lithology_numbers)

一旦运行完成,我们可以再次查看数据框,以确保映射成功并且数据框末尾添加了新的 LITH 列。

包含挪威大陆架井日志数据的数据框。图片由作者提供。

为特定地质组过滤数据框

由于我们有一个相当大的数据集,包含 11,705,511 行,因此对我们的岩性组成分析来说,关注特定的地质组会更好。

在这种情况下,我们将对子集数据进行处理,关注 Zechstein 组。

我们可以通过使用query()方法并传入一个简单的字符串来实现:GROUP == "ZECHSTEIN GP."

df_zechstein = df.query('GROUP == "ZECHSTEIN GP."')
df_zechstein.WELL.unique()

我们可以通过调用df_zechstein.WELL.unique()来检查子集中有多少个井,它返回包含 8 个井的数组。

array(['15/9-13', '16/1-2', '16/10-1', '16/11-1 ST3', '16/2-16', '16/2-6',
       '16/4-1', '17/11-1'], dtype=object)

由于我们只对岩性感兴趣,我们可以简单地提取井名和岩性列。这也将使聚合过程更加便捷。

df_zechstein_liths = df_zechstein[['WELL', 'LITH']]

井名及相关岩性。图片由作者提供。

使用链式 Pandas 函数进行数据聚合

现在我们已经将数据整理成可以处理的格式,我们可以开始聚合过程。为此,我们将把多个 pandas 方法链在一起使用。

首先,我们将使用groupby函数按 WELL 列对数据进行分组。这实际上为 WELL 列中的每个唯一井名创建数据框的子集。

接下来,我们将计算每个组中每种岩性类型的出现次数。normalize=True部分意味着它将给出比例(介于 0 和 1 之间),而不是绝对计数。例如,如果在一个井(组)中,‘砂岩’出现了 5 次,而‘页岩’出现了 15 次,该函数将返回 0.25 给‘砂岩’和 0.75 给‘页岩’,而不是 5 和 15。

最后,我们需要重新排列结果数据框,使得行索引包含井名称,列包含岩性名称。如果某口井中没有某种岩性的实例,则用零填充,因为fill_value=0

summary_df = df_zechstein_liths.groupby('WELL').value_counts(normalize=True).unstack(fill_value=0)
summary_df

我们得到的是以下数据框,展示了每口井中每种岩性的十进制比例。

以十进制形式表示的 Zechstein 组岩性组成。图片由作者提供。

如果我们想以百分比的形式查看这些数据,我们可以使用以下代码改变它们的显示方式:

summary_df.style.format('{:.2%}')

当我们运行代码时,我们得到以下数据框,它提供了一个更易读的表格,可以纳入报告中。

聚合后的 Zechstein 组的岩性组成,并将数字转换为百分比。图片由作者提供。

应用这种样式不会改变实际值。它们仍将以其十进制等效值存储。

如果我们确实想将值永久更改为百分比,可以通过将数据框乘以 100 来实现。

summary_df = summary_df * 100
summary_df

聚合后并转换为百分比的 Zechstein 组岩性组成。图片由作者提供。

一旦数据呈现此格式,我们可以用它创建类似于下方信息图的内容,该图展示了每口井的岩性百分比。

展示每口井中每种岩性的百分比变化的信息图。由作者使用 matplotlib 创建。

摘要

在这个简短的教程中,我们已经了解了如何从 90 多口井的大量测井数据中提取和总结特定的地质组。这使我们能够以易于阅读和理解的格式来了解地质组的岩性组成,并可纳入报告或演示文稿中。

本教程中使用的数据集

训练数据集用作 Xeek 和 FORCE 2020(Bormann et al., 2020)举办的机器学习竞赛的一部分。该数据集采用创作共用署名 4.0 国际许可证。

完整的数据集可以通过以下链接访问: doi.org/10.5281/zenodo.4351155

感谢阅读。在你离开之前,你绝对应该订阅我的内容,将我的文章送到你的收件箱中。 你可以在这里订阅!

其次,你可以通过注册会员获得完整的 Medium 体验,并支持成千上万的其他作者和我。它每月只需 $5,你可以完全访问所有精彩的 Medium 文章,还可以通过写作赚取收入。

如果你通过 我的链接注册你将直接用你的费用的一部分支持我,这不会让你多花钱。如果你这样做,非常感谢你的支持。

数据利他主义:企业引擎的数字燃料

原文:towardsdatascience.com/data-altruism-the-digital-fuel-for-corporate-engines-7896aee0a84c?source=collection_archive---------3-----------------------#2023-12-10

在人工智能时代处理数据的注意事项

Tea MustaćTowards Data Science Tea Mustać

·

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

--

图片由 Gilles Lambert 提供,来源于 Unsplash

超越利润:数字时代的给予与获得

数字经济建立在平等、快速和免费的知识与信息获取的美好承诺上。这已经过去很久了。取而代之的是承诺中的平等,我们得到了由网络效应放大的权力不平衡,使用户被锁定在最受欢迎服务的提供者那里。然而,乍一看,用户似乎仍然没有付出任何费用。但这时,值得仔细观察一下,因为他们确实在付出代价。我们都在付出。我们为了访问某些服务,正在提供我们的数据(而且是大量数据)。与此同时,提供者在这不平衡的方程式的背后获得了天文数字的利润。这不仅适用于当前和成熟的社交媒体网络,也适用于不断增长的 AI 工具和服务。

在这篇文章中,我们将全面探讨这一“疯狂滑梯”,并从用户和提供者的角度进行考虑。当前现实中,大多数服务提供者依赖于黑暗模式的做法来尽可能获取数据,这只是其中一个选择。不幸的是,这正是我们所生活的现实。为了了解其他可能的情况,我们将从所谓的技术接受模型入手。这将帮助我们判断用户是否真正接受了游戏规则,或者只是无论后果如何都在追逐人工智能的炒作。一旦我们弄清楚这一点,我们将转向数据(如此慷慨地被赠予)的后续处理。最后,我们将考虑一些实用步骤和最佳实践解决方案,以便那些希望做得更好的 AI 开发者参考。

a. 技术接受还是通过滑头方式获得同意?

技术接受模型绝不是一个新概念。相反,这一理论自 1989 年 Fred D. Davis 在他的《感知有用性、感知易用性和信息技术用户接受度》中提出以来,就一直是公众讨论的话题[1]。正如标题所暗示的,这个观点的要旨在于用户对技术的有用性认知以及与技术互动时的用户体验,是决定用户是否愿意接受技术的两个关键因素。

对于许多 AI 技术而言,我们不需要考虑太久就能发现这一点。我们称许多 AI 系统为“工具”,这本身就足以表明我们确实将它们视为有用的。如果说有的话,那么至少是为了打发时间。此外,市场法则基本上要求只有最用户友好和美观的应用才能进入大规模受众。

如今,我们可以在戴维斯的方程中添加两个新因素,即网络效应和‘AI 炒作’。所以现在,不仅仅是如果你从未让 ChatGPT 纠正拼写或起草礼貌邮件你就像是一个穴居人,而且你还无法参与到四处发生的许多对话中,无法理解一半的头条新闻,而且你还显得在浪费时间,因为其他人都在利用这些工具。这对接受几乎任何呈现给你的东西的动机如何呢,尤其是当它被美丽的图形用户界面精心包装时?

图片由 Possessed PhotographUnsplash 提供

b. 默认设置 — 强制性的利他主义

正如已经暗示的,我们似乎对将所有数据提供给许多 AI 系统的开发者持相当开放的态度。我们在互联网上留下了痕迹,对这些痕迹没有概览或控制权,显然还必须容忍商业行为者收集这些痕迹并用它们来制作炸鸡。这个比喻可能有些牵强,但它的含义仍然适用。看来我们必须容忍某些系统可能已经用我们的数据进行训练的事实,因为如果我们甚至无法确定所有数据的去向,如何能期望提供者弄清楚所有数据的来源,并相应地通知所有数据主体。

然而,有一件事我们目前默认是利他主义的,但在隐私和 GDPR 仍有一线生机的是在与给定系统互动时收集的数据,用于改进该系统或由同一提供者开发新模型。目前我们似乎无私地提供这些数据的原因,与前一段描述的原因有所不同。这里的利他主义更多地源于我们所处的法律状况不明以及对其许多漏洞和模糊性的滥用。(此外,用户在大多数情况下也更重视他们的钱而非隐私,但这现在无关紧要。)

例如,与 OpenAI 主动寻找每一个包含在训练模型的数据集中的个人不同,它完全可以通知其活跃用户他们的聊天记录将用于改进现有模型和训练新模型。这里是免责声明

“如上所述,我们可能会使用您提供的内容来改进我们的服务,例如训练支持 ChatGPT 的模型。有关如何选择退出我们使用您的内容来训练模型的说明,请参见这里。”

由于几个原因,这种做法无法被接受。[3] 首先,用户应该能够主动决定是否希望其数据用于改进服务提供者的服务,而不仅仅是能够在事后选择退出。其次,使用如‘可能’这样的词汇可能给普通用户一种非常错误的印象。这可能暗示这是仅在偶尔和特殊情况下才会发生的事情,而实际上这是一种常见的做法和行业黄金规则。第三,“驱动 ChatGPT 的模型”对于即使是对其实践非常了解的人来说也含糊不清。他们既没有提供关于他们使用的模型和如何训练这些模型的充分信息,也没有解释这些模型如何“驱动 ChatGPT”。

最后,在阅读他们的政策时,人们会得出这样的信念:他们只使用内容(大写的 C)来训练这些未知的模型。意思是他们仅仅使用

个人信息包括在输入、文件上传或[用户]对[OpenAI]服务的反馈中。”

然而,当我们考虑到 2023 年 3 月的丑闻时,这显然是不正确的,其中涉及一些用户的支付详情被共享给其他用户。 [4] 如果这些支付详情已经进入模型,我们可以安全地假设,随附的姓名、电子邮件地址和其他账户信息也没有被排除在外。

当然,在这种描述的背景下,数据利他主义这一术语只能带有相当大的讽刺和反讽。然而,即使是对于那些并非明显撒谎关于他们使用哪些数据,也没有故意隐瞒他们使用数据目的的提供者,我们依然会遇到问题。例如,处理操作的复杂性可能导致隐私政策的过度简化,类似于 OpenAI 的情况,或者是无人愿意查看的难以理解的政策,更不用说阅读了。这两者最终都导致相同的结果,即用户同意任何必要的条款,只为能够访问服务。

现在,对这种观察的一个非常流行的回应是,我们所泄露的大部分数据对我们来说并不重要,那么为什么对其他人来说会重要?此外,我们凭什么对那些掌控世界的大型企业如此有趣?然而,当这些数据用于建立一个完全依赖于从全球数百万个地方收集的那些微不足道的数据点的商业模型时,这个问题就会有完全不同的视角。

c. 作为商业模式的盗取数据?

要审视基于这些每天被随意抛弃的数百万个无关同意的商业模式,我们需要检查用户在放弃他们的数据时有多么利他。当然,当用户访问服务并在过程中放弃他们的数据时,他们也会以此获得服务。但这并不是他们唯一获得的东西。他们还会收到广告,或者可能得到一个二级服务,因为一级服务留给了订阅用户。更不用说这些订阅用户仍然在放弃他们的内容(以大写字母 C 表示),以及(至少在 OpenAI 的情况下)他们的账户信息。

因此,尽管用户为了使用工具或服务同意他们的数据可以被用于几乎任何用途,但他们所放弃的数据却被多次变现,以便为他们提供个性化广告和开发新的模型,这些模型可能再次采用免费增值(freemium)模式。撇开更为哲学性的问题,比如为什么银行账户上的数字比我们的生活选择和个人偏好更有价值,这似乎完全不符合逻辑,用户为了得到如此少的东西却要付出这么多。特别是因为我们讨论的数据对服务提供商至关重要,至少如果他们想保持竞争力的话。

然而,情况不必如此。我们不必等待新的具体 AI 法规来告诉我们该做什么和如何行为。至少在涉及个人数据时,GDPR 对其如何使用及其用途已经非常明确,无论上下文如何。

法律对此有何规定?

与版权问题不同,版权问题可能需要根据新技术重新解释法规,但数据保护则不能如此。数据保护在数字时代发展起来,并试图规范在线服务提供商的行为。因此,不能避免应用现有法规和遵守现有标准。是否以及如何做到这一点是另一个问题。

在这里,有几个事项需要考虑:

1. 同意是一种义务,而不是选择。

如果不在用户实际开始使用工具之前告知他们个人数据和模型输入将用于开发新模型和改进现有模型,这是一大红旗。基本上是最红的那种。类似于收集 cookie 同意的同意弹窗是一个必须,而且是一个容易编程的。

另一方面,“支付或跟踪”(或在 AI 模型背景下的“支付或收集”)的概念,即用户决定是否愿意让 AI 开发者使用他们的数据,这一想法受到激烈争议,几乎无法合法实施。主要原因是,用户仍需自由选择接受或拒绝跟踪,这意味着价格必须相对较低(服务必须非常便宜),才能证明选择是自由的。更不用说,你必须履行承诺,不收集任何订阅用户的数据。由于 Meta 最近已经转向这一模式,数据保护机构也因此收到了一些投诉[5],因此有趣的是,欧盟法院将如何裁定此事。然而,当前,依赖合法同意是最安全的方式。

2. 隐私政策需要更新

提供给数据主体的信息需要更新,以包括整个 AI 系统生命周期中的数据处理。从开发、测试到部署。为此,所有复杂的处理操作需要转化为通俗易懂的语言。这绝非易事,但这是不可避免的。虽然同意弹窗不是合适的地方来做这件事,但隐私政策可能是。只要隐私政策直接链接到同意弹窗,就可以了。

3. 发挥创造力

翻译复杂的处理操作本身就是一项复杂的任务,但对于实现 GDPR 透明度标准来说绝对必要。无论你是想使用图形、图片、测验还是视频,你都需要找到一种方式来向普通用户解释他们的数据到底发生了什么。否则,他们的同意永远无法被视为知情和合法。因此,现在是时候动动脑筋,挽起袖子,投入到规划中去了。[6]

图片由Amélie Mourichon拍摄,来源于Unsplash

[1] Fred D. Davis,《感知有用性、感知易用性与信息技术用户接受度》,MIS 季度刊,第 13 卷,第 3 期(1989 年),第 319–340 页,www.jstor.org/stable/249008?typeAccessWorkflow=login

[2] Christophe Carugati,《平台监管者面临的“支付或同意”挑战》,2023 年 11 月 6 日,www.bruegel.org/analysis/pay-or-consent-challenge-platform-regulators

[3] OpenAI,《隐私政策》,openai.com/policies/privacy-policy

[4] OpenAI,3 月 20 日 ChatGPT 停机:发生了什么,openai.com/blog/march-20-chatgpt-outage

[5] noyb,noyb 对 Meta 提起了关于“支付或同意”的 GDPR 投诉,noyb.eu/en/noyb-files-gdpr-complaint-against-meta-over-pay-or-okay

[6] untools,六顶思考帽,untools.co/six-thinking-hats

数据分析变得简单:使用 LLMs 自动化繁琐任务

原文:towardsdatascience.com/data-analysis-made-easy-using-llms-to-automate-tedious-tasks-bdc1fee552d5

Jye Sawtell-Rickson数据科学前沿 Jye Sawtell-Rickson

·发表于数据科学前沿 ·阅读时长 11 分钟·2023 年 4 月 30 日

--

一幅高质量的数字艺术作品,展示了一个能够进行技术编码、写出精彩文稿和进行战略思考的机器人(作者创作,使用 DALL-E)。

数据分析既具挑战性又有回报。从清理杂乱的数据集到构建复杂的模型,总是有做不完的工作,时间却总是不够。但如果有一种方法可以简化和自动化一些常规任务,从而腾出更多时间进行战略思考和决策呢?这就是 LLMs 的作用所在。

大型语言模型(LLMs)是可以协助处理各种自然语言处理任务的 AI 语言模型,从生成文本到回答问题。而事实证明,它们也可以成为数据分析师的宝贵工具。在本文中,我们将探讨如何在日常工作中使用 LLMs,并展示 AI 如何帮助你更聪明地工作,而不是更辛苦。

让我们直接进入正题。

注意:这些系统还不是一个完整的分析解决方案,能够取代你。但请继续关注这个领域的进展。

LLMs 如何提供帮助

LLMs 可以作为 AI 驱动的聊天机器人,帮助简化和自动化与数据分析相关的任务。凭借其先进的能力,LLMs 可以协助处理各种任务。我将这些任务分为三个大类:

  • 技术:此类别包括一些最常见的应用,通常涉及编码,包括编写代码和文档、清理数据、回答编码问题、运行数据分析和数据可视化

  • 软技能:此类别涵盖了通常成为成功数据分析师所需的软技能。AI 可以帮助起草文件以传达发现结果、从合作伙伴处收集数据需求和总结会议记录

  • 战略性: 数据分析师可以提供的最有价值的部分可能是他们的战略思维,这也可以通过 AI 得到增强。这包括头脑风暴分析内容、创建广泛的理解框架、改进和迭代分析方法以及作为一般的思维伙伴。

将这些付诸实践可以在你作为数据分析师的工作中节省大量时间和精力。

(作者创作)

让我们探索这些示例,看看今天的工具有多么强大和灵活。

使用 LLMs 的示例

本节将包含 LLMs 应用的示例。这些示例大多用不同的格式标明,以突出响应,否则你可能会将其与我的写作混淆!

在本文中,当我们提示 LLM 时,我们将使用Spotify 和 YouTube 歌曲数据集中的一个摘录,该数据集包括列信息和数据集的前 20 行。将来,LLM 可能直接访问数据集,以及执行数据集上的代码的响应,以消除如此小样本的限制。

技术高手

这篇文章中可以看到,经过代码库训练的 LLMs 是熟练的编码人员。这意味着它们可以轻松解决常见的技术数据分析任务。它们在需要少量上下文的任务上表现出色,技术任务通常属于这一范畴。

数据分析师最基本的任务之一是执行探索性数据分析(EDA)。LLMs 能够编写 Python 代码来探索数据集,并输出相应的图像。以下引用文本包含 LLM 在提示进行 EDA 时返回的响应:

  • 读取 csv 文件并显示示例: “df = pd.read_csv("filename.csv") df.head()

  • 识别感兴趣的列并进行探索: “按艺术家分组数据,检查每位艺术家的歌曲数量。 df.groupby('Artist')['song name'].count()

  • 创建图表: “创建舞动值列的直方图以查看分布。 plt.hist(df['danceability'], bins=20)

这些示例相当基础,但 LLMs 可以进一步扩展,提供其他 EDA 方法。在这种情况下,它们能够提供异常值检测和聚类等进一步探索数据的方法。

下面是给出的响应示例。通读这些内容,它们绝对可以作为初级分析师的工作,识别出一些见解,例如:

“Gorillaz 的歌曲的舞动值通常高于 Red Hot Chili Peppers 的歌曲。”

“这些数据涉及两位艺术家的歌曲,Gorillaz 和 Red Hot Chili Peppers。Gorillaz 在给定的数据中有 9 首歌曲,而 Red Hot Chili Peppers 有 4 首歌曲。”

歌曲的舞蹈性和能量值在 0.559 到 0.938 和 0.345 到 0.938 之间变化。Gorillaz 歌曲的舞蹈性值通常高于 Red Hot Chili Peppers 的歌曲。Gorillaz 歌曲的能量值也往往高于 Red Hot Chili Peppers。

歌曲的调性范围从 1.0 到 11.0,没有明显的模式或趋势。

(完整输出)

创建表格时,为列和整体表格编写良好的描述可能会有些麻烦。虽然这是好的实践,但经常被忽视。LLMs 可以通过猜测表格的上下文来完成这项工作,或者更好的是,当提供一些上下文时,LLMs 可以编写这些描述。

这张表格似乎展示了两位不同艺术家——Gorillaz 和 Red Hot Chili Peppers——的各种歌曲的信息。表格中的每一行代表一首不同的歌曲,并为每首歌曲提供了 10 列信息。以下是各列的描述:

索引:这只是一个整数索引,似乎是表中每首歌曲的唯一标识符。

艺术家:此列列出了创作歌曲的艺术家。

链接:此列提供指向艺术家在 Spotify 上页面的链接。

歌曲名称:此列列出了歌曲的名称。

专辑名称:此列列出了歌曲所属专辑的名称。

专辑:此列指示该行是否表示专辑或单曲。

来源标识符:此列似乎是每首歌曲的唯一标识符,可能是 Spotify 特有的。

舞蹈性:此列提供一个从 0 到 1 的评分,表示歌曲适合跳舞的程度。

能量:此列提供一个从 0 到 1 的评分,表示歌曲的能量水平。

调性:此列提供一个从 0 到 11 的数字,表示歌曲的音乐调性。

总体而言,这张表格似乎是一个歌曲数据的集合,包括有关艺术家、专辑和每首歌曲的各种音乐特征的信息。

这些结果的一些令人印象深刻的部分是,大型语言模型(LLM)查看了数据样本,发现“Key”列并不是行的某个关键,而是歌曲的音乐调性,“Album”列描述了它是否为专辑或单曲。典型的分析师仅凭列名无法猜测这些信息,需要花费时间进行基本的探索性数据分析(EDA)来确定数据源可以提供的值——LLMs 节省了这段时间。

今天,增强智能在数据分析中的主要应用场景是技术问题回答,例如在 Stack Overflow 上。然而,大型语言模型(LLMs)通常更具优势,它们提供特定用例和变量的正确代码,并能够给出更深入的回答或后续教程。

例如,绘制词云是一个时不时会需要的任务,但很难记住绘制词云所需的确切库和代码。你可以简单地询问 LLM,它会愉快地返回给你可用的代码。下面是关键摘录:

# 生成词云

wordcloud = WordCloud(width = 800, height = 800, background_color ='white', stopwords = set(), min_font_size = 10).generate(wordcloud_text)

我意识到这只是使用了一个单词列表,但我有单词计数,所以我很快跟进了“我可以输入加权单词吗?” LLM 重新编写了代码:

# 生成词云

wordcloud = WordCloud(width = 800, height = 800, background_color ='white', stopwords = set(), min_font_size = 10).generate_from_frequencies(weighted_words)

(完整输出)

这在没有 Stack Overflow 的帮助下是不可能完成的,也节省了大量的谷歌搜索或查找旧代码的时间。

正如可以看到的那样,LLM 在技术问题上非常出色,可以节省数据分析工作流程中的时间,但它们还具备什么能力呢?

AI 的软性方法

软技能可能被忽视为数据分析的一个要求,但它们对于确保任何分析对业务有用至关重要。

任何数据分析师都知道,理解你合作伙伴实际想要的数据是最困难的部分之一。这可能会以多种方式出现问题,从合作伙伴对某事的称呼错误,到在你返回第一部分时扩展他们的需求,再到要求错误的数据来支持他们想要得出的结论。由于其对话性质,LLM 在从合作伙伴那里收集需求方面非常出色。下面是一个对话示例。

“我需要你提取最新的动画贴纸数字。”

很好,谢谢你的请求!只是为了澄清,当你说“动画贴纸数字”时,你能否提供更多关于你具体是什么意思的信息?

你是在寻找已创建的动画贴纸的数量数据吗?还是对动画贴纸的使用指标感兴趣,比如它们被查看或下载的次数?

另外,你能否指定一下你感兴趣的时间范围?你是在寻找特定日期、周或月的最新动画贴纸数字数据吗?最后,你希望我以任何方式对数据进行分段吗,比如按贴纸类别或按用户人口统计?

实际上,可能很难让你的合作伙伴先去与 LLM 聊天,但 LLM 对于确定在开始项目工作之前是否有需要进一步澄清的事项非常有用,未来也将直接集成到分析解决方案中。

一旦需求被收集并完成分析,发现必须反馈回来。LLMs 已经有帮助作者更快地生成内容并提高质量的记录。同样,LLM 可以帮助准备总结分析工作的文档或准备演示文稿。一些 LLMs 特别擅长的任务是基本的介绍和结论,以及文本摘要。

“基于这次 EDA,歌曲推荐服务可能会考虑将更多的 Gorillaz 歌曲添加到推荐算法中,并优先考虑舞曲性和能量值较高的歌曲,因为这些歌曲在用户中似乎很受欢迎。”

(完整输出)

关于使用 LLMs 进行写作的一些技巧可以在这篇文章中找到,但还有很多其他技巧,并且随着我们不断探索如何最好地使用 LLMs,还会有更多的技巧被添加。

我们已经看到 LLMs 如何解决技术任务并协助处理许多“软”任务。这对于提高分析师的工作流程已经很棒,但我们能否更进一步呢?

大命令

战略思维是分析师能够为公司提供的最高价值之一。这可以通过 LLMs 得到提升。一些实现这一目标的方法包括头脑风暴要进行哪些分析,创建广泛的理解框架,改进和迭代你的分析方法,并作为一个一般的思维伙伴。

让我们从头脑风暴开始。你可能被分配了一个通用的任务——“更多地了解你的客户”。你可以进行哪些分析?这些分析会导致什么结果?理想情况下,你会与同行进行头脑风暴,但这并不总是可能,因此你可以与 AI 进行头脑风暴。

当被问及我们应该进行什么分析以更好地了解客户时,输出的分析包括:“客户细分,RFM 分析,购物篮分析,流失分析,客户生命周期价值。” 以及一些预测结果:

“这些分析的结果将取决于你发现的具体见解。例如,你可能会发现你的最有价值的客户是年龄较大、富有且对特殊烹饪产品感兴趣的女性。这可能会导致你开发更多针对这一人群的产品,并投资于能够引起她们共鸣的营销活动。”

(完整输出)

这是一个很好的起点,可以确保你覆盖所有的空白,这在独自工作时可能很困难。LLMs 还可以提供有关框架的建议。你可能会想知道如何在接下来的几年里发展你的业务,以及如何衡量这一点,那么:

“有几种增长框架可以用来理解如何在未来三年内增加销售。以下是三种流行的框架:

AARRR:该框架代表 Acquisition、Activation、Retention、Referral 和 Revenue。它是由 Dave McClure 开发的五步框架,通常用于初创公司圈子。AARRR 框架帮助公司了解客户生命周期的不同阶段,并如何优化每个阶段以推动增长。

RACE:该框架代表 Reach、Act、Convert、Engage。它是由 Smart Insights 开发的四阶段框架,专注于数字营销。RACE 框架帮助公司了解如何优化数字营销工作以推动增长。

3HAG:该框架代表三年内高度可实现目标。它是由 Shannon Susko 开发的战略规划框架,帮助公司设定并实现雄心勃勃的增长目标。3HAG 框架包括定义一个 BHAG(宏伟目标),然后在三年的时间里将其分解为更小的、可实现的目标。

在决定使用哪个增长框架时,考虑公司独特的情况和目标非常重要。AARRR 和 RACE 是更具战术性的框架,专注于优化客户生命周期或数字营销工作的特定阶段,而 3HAG 是一个更具战略性的框架,专注于设定雄心勃勃的增长目标。

正如你所见,回应是有帮助的,完全可以代替你期待从团队分析师那里得到的回应。

使用哪个 LLM?

撰写本文时,实际上只有一个 LLM 能够很好地完成上述所有任务,那就是 OpenAI 的 ChatGPT。它是第一个普及 LLMs 的产品,仍然是市场上最强大的产品之一,并且是免费的(提供订阅选项)。

随着 AI 领域变化的步伐加快,几个月后这些信息可能就不再准确,因此值得注意的是,市场上还有许多其他竞争者。例如,Google 正在开发他们的产品 Bard,预计其表现与 ChatGPT 相似。此外,还有许多开源替代方案可以考虑。虽然这些通常不具备相同的质量,但预计会持续改进,缩小与商业运营模型之间的差距。

使用 LLMs 的有效技巧

作为数据分析师,充分利用 LLMs 有一些技巧可以遵循。首先,给 LLMs 提供清晰且具体的输入非常重要。这意味着使用恰当的语言,避免模糊,并在必要时提供上下文。此外,LLMs 可以处理结构化和非结构化数据,因此值得尝试不同的输入格式,以找出适合特定任务的最佳格式。最后,重要的是要记住 LLMs 只是工具,而不是替代人工分析的手段。虽然它可以帮助自动化一些例行任务,但仍需由数据分析师来解释结果并根据数据做出明智的决策。

现在有很多文章讨论如何使用大型语言模型(LLMs),例如这篇,这是一个不断发展的研究领域,因此要继续学习!

结论

总之,大型语言模型是提高分析工作效率的绝佳工具,甚至能帮助你成长和学习新事物。大型语言模型可以帮助解决技术问题,发展软技能,并提升你的战略思维。与人工智能合作是未来的趋势,因此现在是学习如何将其整合到工作流程中的最佳时机,以免被落在后头。

数据分析师数据清洗指南

原文:towardsdatascience.com/data-analyst-guide-to-data-cleaning-6409159ebf3

如何处理不同类型的数据清洗

Vicky YuTowards Data Science Vicky Yu

·发表于 Towards Data Science ·阅读时间 6 分钟·2023 年 8 月 4 日

--

图片来源于 Janeke88 来自 Pixabay

虽然有很多资源可以学习技术技能,但很少有深入讲解如何清洗数据的资源——这是数据分析师必备的技能。你可能会认为可以应用相同的规则来清洗数据,但情况并非总是如此。今天,我想分享我作为数据分析师多年来学习到的,关于如何处理不同类型的数据以进行数据分析和报告的经验。

数字值

我所说的数字值是指那些对数据分析和报告有用的值。一个好的经验法则是看平均值是否有用。例如,数字订单号字段的平均值是没有意义的。然而,平均收入金额是有用的。

作为数字字段存储的数字

对于保存在数字字段中的数字,请应用以下清洗规则:

  1. 计算最小值最大值中位数99 百分位平均值。如果最小值是负数,但值应该是零或更高,则在适用的情况下将其替换为零。在下面的示例销售数据中,注意第 13 行中位数$800第 12 行平均值$20,560之间的巨大差异。如果中位数和平均值或最大值和 99 百分位之间差异很大,我通常会检查异常值,特别是当我不熟悉数据时。如果你报告包括第 4 行$100,000 的平均销售额,它将是$20,560,而不是$560,如果你排除了$100,000。这就是为什么检查异常值并排除它们是好的,尤其是当你计划报告平均值或使用数据来构建机器学习模型时,因为异常值可能会影响模型结果。根据最重要的字段优先安排你的时间,因为你可能有数十个字段需要检查,检查所有字段会非常耗时。

作者创建的示例数据

2. 计算缺失值和非缺失值。如果缺失值的数量超过非缺失值,那么这个数值字段在你的分析中可能不可用。例如,如果你有 1,000 条记录,但其中 900 条缺失,那么 100 条非缺失值在你的分析中可能不会有用。如果缺失值的数量少于非缺失值,则在适用的情况下将缺失值替换为零。如果缺失值意味着除零以外的值,则用你认为更合适的值替换,如中位数或平均数,前提是你已经去除了异常值。例如,我曾经遇到过一天的缺失DAU(每日活跃用户)数据。由于 DAU 根据一年中的时间有所不同,我用前 7 天的平均值来替换缺失那一天的 DAU。

作为字符串字段存储的数字

数值可以作为字符串值加载到数据库中,因为它们包含需要移除的字符,才能转换为数字。识别这些字符串字段的经验法则是问一下平均值是否有用。

这些字符串值的例子包括带有货币的销售金额,如$100.55 或包含逗号的数字,如 1,000。在这些情况下,你需要移除字符值,即删除货币符号以转换为 100.55,删除逗号以转换为 1000,然后将字符串值转换为数字。字符串值转换为数字后,应用上述数据清理规则,处理作为数值字段保存的数字。

字符串值

对于字符串值字段,应用以下清理规则:

  1. 按表中的 主键 检查重复项。主键通常是唯一标识符,如客户 ID 或使行在表中唯一的字段组合。在进行数据分析时,您需要确保数据源中没有重复项,因为这可能导致报告错误的数字。

  2. 计算 空值 和非空值的数量。确定是否可以用字符串值替换空值。有时我会将空值替换为‘N/A’或‘None’以用于报告,但这取决于您的要求。

  3. 计算每个唯一字符串值的行数,并按降序排列。查找具有相同含义的字符串值,并将其更改为相同的值。

在下面的示例 SQL 查询中,第 1 到第 7 行,我计算了每个item_name 值的行数,并按降序排列。由于可能有数百个唯一的字符串值,因此要集中处理行数最多的唯一值,优先清理这些,因为更改行数较少的值会花费过多时间,并且可能不会影响分析结果。

SQL 结果第 1 到第 3 行显示,item_count 为 33 的 apples,19 的 apple 和 17 的 Apples。可以将 Applesappleapples 更改为 apple。第 4 行和第 5 行中的 strawberriesstrawberry 可以更改为 strawberry。数据清理后,第 1 行和第 2 行显示的 item_countapple 69 和 strawberry 26。

作者创建的清理前唯一 item_name 值计数的截图

作者创建的清理后唯一 item_name 值计数的截图

搜索并根据需要移除特殊字符。这在非英语文本或自由格式文本中很常见,其中制表符和回车符可能是加载到数据库中的文本的一部分。大多数字符串字段不包含特殊字符,但如果在查询特定字符串值时没有返回任何行,则特殊字符可能是原因。

日期或日期时间值可以作为字符串值加载。确认字段中的日期值格式相同,并根据需要将其转换为日期或日期时间字段,以便可以使用日期函数。

日期和日期时间值

对具有日期或日期时间字段的表应用以下数据清理规则:

  1. 检查那些每个日期或日期与其他字段组合应仅有一行的表中的重复项。例如,日活跃用户(DAU)表是每日活跃用户的计数,每天应仅有一行。另一个例子是按国家的收入,其中日期和国家组合使行在收入表中唯一。我曾经继承了一个 ETL 作业,该作业没有删除更新日期相同的数据。我不小心重新运行了同一天的作业,并在收入表中引入了重复项。我只有在运行了重复检查后才意识到这一点,否则我会向利益相关者报告过多的收入。

  2. 在连接多个含有日期时间值的表时,请确认日期时间值处于相同的时区。我曾经需要创建一个报告,展示发送电子邮件后 24 小时内用户升级的数量。电子邮件表的日期时间是 CST(中央时间),但升级表的日期时间是 UTC(协调世界时间)。UTC 比 CST 快 5 小时。如果我仅仅检查 CST 的电子邮件发送日期时间后的 24 小时,我会错过在电子邮件发送后升级的 5 小时用户。幸运的是,大多数数据库都有一个 SQL 函数叫做 CONVERT_TIMEZONE,用于将日期时间转换到不同的时区。请查看你的数据库文档以确认。

最后思考

虽然数据清洗不是数据分析师,包括我自己,在工作中最喜欢的任务,但我希望我所介绍的方法能在你下一次需要为项目清洗数据时帮到你。祝数据清洗愉快!

注意:以上所有 SQL 查询都在 BigQuery 沙盒 上运行,该沙盒对任何拥有 Google 账户的人都是免费的。

你可能还会喜欢……

## 数据清洗:数据科学中不性感的一面

对实际案例的深入分析

towardsdatascience.com ## BigQuery SQL 函数用于数据清洗

适用的用例和函数

towardsdatascience.com ## 你在学校里学不到的数据知识

如何使用不完美的数据获得可用的结果

towardsdatascience.com

数据即产品:从概念到现实

原文:towardsdatascience.com/data-as-a-product-from-concept-to-reality-b2a853712250

创建有效数据产品的蓝图

Louise de LeyritzTowards Data Science Louise de Leyritz

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

--

数据即产品 — 图片由Castor提供

数据即产品的方法最近获得了广泛关注,因为公司希望最大化数据价值。

我坚信数据即产品的方法是我们为创造更好的数据体验所需的革命性变革,这是我心中珍视的概念。

关于数据体验的简要说明,以防你需要赶上进度:

数据曾经是技术团队的领域。但现在不再是。如今,数据驱动着公司内部的所有运营功能:销售、财务、营销、运营等。

商业用户已经成为数据用户,他们的体验即是数据体验数据体验是这些领域专家在日常使用数据来增强工作能力时的感受。

截至今天,数据体验仍然不尽如人意。即使我们能产生高质量的数据,仍然难以找到、访问和理解。糟糕的数据体验导致业务团队对数据失去信任并失去兴趣。如果数据不能促进更好的决策,那收集数据又有什么意义呢?

数据即产品的方法简单却有效。它涉及思维方式的转变,将数据用户视为客户,突显出优先考虑其满意度的必要性。通过更好的概念化和呈现,这一方法力求提供卓越的数据体验。虽然我们会在后续详细探讨这一方法,但关键要点是将数据视为产品意味着将数据消费者放在首位。

我们如何在实践中实现这一转变?要实施数据即产品的方法,你需要改变你对生成提供数据的方式。也就是说,你需要在生成数据在数据生命周期内应用产品管理思维。希望将数据视为产品的公司往往会关注一个方面,而忽视另一个方面。

本文将带你了解这两个步骤:

  1. 在数据创建中利用产品管理思维

  2. 将产品管理应用于数据呈现和交付

在本文中,术语“数据产品”和“数据即产品”将交替使用,指的是数据被视为产品

I. 在数据创建中利用产品管理思维

数据即产品的概念始于最基础的层面——你的思维方式。不仅要理解这一点,还要在整个组织中嵌入数据即产品的思维。

将数据视为产品意味着将传统产品开发中使用的相同原则和实践应用于你的数据项目。

那么,在实际生活中将数据视为产品到底意味着什么?埃里克·布罗达解释了数据产品是通过将产品思维应用于数据领域而产生的,正如下图所示。

图片由埃里克·布罗达提供

现实生活中的产品:

  • 分配给一个明确的负责人进行产品开发和管理。

  • 遵循具体的业务和客户需求,为特定目的和目标受众量身定制。

  • 长期视角进行开发,由产品负责人制定持续改进和成功的计划。

实际操作中这意味着什么?

威廉·安吉尔Caribou的数据工程高级技术产品经理,建议在以产品管理思维处理分析时,在数据创建阶段分配额外时间是很重要的。与其直接跳入构建仪表盘或预测模型,不如首先考虑谁将使用它,以及它将如何对他们产生价值。‍

“很多人认为在构建项目时这种方法是自然而然的。但很多时候,情况却是车子在马前面,我们使用模型是因为它们的准确性,而不是它们的业务影响。”威廉·安吉尔,数据工程高级技术产品经理

通过采用产品管理思维,可以确保所创建的分析对业务及其用户具有实际价值。

在数据创建之前采用产品管理思维是提供可靠数据产品的初始阶段。然而,仅此是不够的。在现实中,一旦产品创建完成,它还需要被包装和呈现给客户。这是下一节的主题。

II. 将产品管理应用于数据展示和交付

许多组织在数据生产阶段引入了产品管理思维,然后就不再关注数据。但如果你把数据当作产品来看待,你必须考虑数据的整个生命周期

让我们考虑一下实体产品的生命周期。一个实体产品会被创建、使用,最终被丢弃。如果你要以可持续的方式创建这个产品,你需要考虑整个生命周期,并计划安全使用、存储以及产品生命周期结束时的最终处置。数据的背景下又意味着什么呢?

“当涉及到数据时,必须考虑如何存储和记录数据,以及数据将如何被利用。” 数据工程高级技术产品经理 William Angel

为确保数据产品得到有效管理和利用,应考虑一些特定的特征。这些特征来自 Zhamak Dehghani 关于数据网格的工作。我们在这里简要介绍它们,并解释如何在实践中实现这些特征。

可靠数据产品的特征——图片来源于Castor

可发现性

可靠的数据产品的第一个特征是可发现性。一个很好的指标是,当数据团队不再被视作数据目录时,即数据消费者能够自主找到他们数据问题的答案。

这意味着公司里的每个人都知道在哪里以及如何找到适合他们特定项目的最佳数据。即使数据是混乱的,即使有 20 个表格具有相同的名称。

那么,“好”的标准是什么呢?当团队能够轻松浏览混乱的数据,并且毫不费力地识别出哪些是最受欢迎且最新的表格,而无需询问同事或上司时,数据就具备了可发现性。

你有两种利益相关者在寻找数据:一种是寻找特定数据集的人,另一种是浏览文档以了解数据全貌的人。数据应该对这两种人都具有可发现性。

确保这一点的最简单方法是将数据目录插件集成到你的数据堆栈上,以便于搜索和探索。如果你没有数据目录,你的数据文档可能会散布在不同的工具中:

  • 用于一般知识和入职培训的 Wiki 页面

  • 用于描述和标签的代码库(如果你使用代码注释)

  • 用于管理问题和请求的 JIRA 或 Gitlab

  • 用于部落知识的 Slack 频道

即使你还不能将这些与数据目录合并,你至少可以制作一个主页,汇集所有有用的链接。

使用 Notion 构建数据文档入口 — 图片来自于Castor

可理解的

人们理解数据集的前提是意识到这些数据将满足的需求、内容和位置。没有上下文的数据是毫无价值且危险的。一旦找到相关的数据集,你完成了 10%的工作。现在,你需要通过一系列问题的检查清单,以确保你理解你正在使用的数据。如果你不能回答以下问题,你就不理解你的数据。

  • 数据来源于哪里?

  • 数据流向哪里,并且流入了哪些下游表格?

  • 谁拥有它/谁对它负责?

  • 在我的领域中,某个字段的含义是什么?

  • 为什么这很重要?

  • 这个表格最后一次更新时间是什么时候?

  • 这些数据的上下游依赖关系是什么?

  • 这些数据是生产质量的数据吗?

所有相关知识都与数据关联时,人们才能真正理解数据。理解是主观的。机器学习工程师会以不同于数据分析师的方式来处理数据集。这就是为什么所有的理解都需要将所有相关知识层与数据集关联起来。

应用良好的文档原则本质上意味着为数据提供正确的上下文。如上所述,传统文档帮助数据用户理解数据集的目的和字段的含义。在此基础上,你可以以显著的方式指定数据集所有者,让数据用户知道谁负责某个数据集。

最后,数据血缘是跟踪数据历史的过程,当数据经过一系列转换,如数据提取、清洗、丰富和存储时,它有助于理解数据元素的来源、转化和依赖关系。血缘功能帮助数据用户回答诸如“这些数据的上下游依赖关系是什么?”这样的问题。

可靠的

数据产品应当可靠和值得信赖。利益相关者应对其可用性感到有信心。

数据信任可以通过提供关于数据使用和数据来源的完整透明度来实现,这需要强大的血缘功能。它有助于理解数据元素的来源、转化和依赖关系。血缘功能帮助数据用户回答诸如“这些数据的上下游依赖关系是什么?”这样的问题。

数据血缘可视化 — 图片来自于Castor

另一种建立信任的方法是围绕透明度构建你公司内人员使用数据的透明度。这使得员工可以学习专家的最佳实践

当人们知道可信赖的同事已经查询和使用了数据时,他们可以更好地推断该数据集是可信的。获得社会验证,证明数据有价值,有助于建立数据信任。

另一种弥合信任差距的方法是遵守已批准的数据产品服务水平协议(SLA):

  1. 变化间隔和及时性

  2. 完整性

  3. 新鲜度、通用性和性能

这基本上指的是数据质量检查,数据必须通过这些检查。这些检查可以手动进行,也可以通过自动化工具进行,并涉及将数据与一套定义好的规则或标准进行验证,以确保其符合所需的质量标准。使这些质量检查对每个数据集都可访问,对于帮助用户理解数据非常有帮助。‍

可寻址性

数据产品应该有一个唯一的标识符或地址,使其可以被轻松访问和使用。

数据产品的可寻址性意味着为其提供一个独特、易于找到的位置,以便其他人可以访问它。可以把它想象成房子的街道地址。就像你需要一个具体的地址来找到房子一样,数据消费者需要一个独特的地址来访问你的数据产品。

地址应该遵循一套规则和标准,以便每个人都可以轻松找到它,并以相同的方式使用,无论他们是谁或使用什么系统。这使得人们能够轻松找到、使用和理解你的数据产品。‍

可访问性

当数据产品被称为“可访问”时,这意味着它们可以被用户轻松获取或到达。这可能意味着数据可以随时使用,或者获取数据的过程对于用户来说简单方便。

如果你要提供数据背景,应该通过正确的接口进行。并非所有团队成员都具有相同的技术专长,也并非所有团队都有相同的数据需求。

例如,如果你在 dbt 中记录数据,你不能指望市场团队去那里获取文档。背景信息应该在对业务团队友好的工具中提供。

安全性

数据产品必须安全以保护敏感信息这并不令人惊讶。不论数据如何处理,安全始终是一个问题。然而,对于数据产品来说,安全变得特别具有挑战性,因为它们必须既是可发现的又是可访问的。你如何在实施强安全措施的同时使其可访问呢?

管理隐私和安全风险可以通过实施一些关键策略来实现。为了保护个人信息,投资于适当的文档并建立明确的数据共享协议是很重要的。此外,实施访问控制和遵循数据最小化实践可以帮助降低安全风险,并确保敏感信息的安全。这些策略需要一整篇文章来解释。幸运的是,我们写了一篇

确保数据共享、隐私和安全之间的兼容性 — 图片来源于Castor

可互操作

数据产品应该与其他数据产品和系统兼容,实现无缝集成。可互操作性意味着不同的数据产品可以协同工作并相互通信,而不会出现问题。

即使它们是由不同的人或组织创建的,也可以共享信息并一起使用。这使得人们更容易使用并从这些数据产品中受益。

为确保你的数据产品能够与其他产品良好配合,你需要遵循以下步骤:

  1. 使用常见且公认的数据格式和结构标准。

  2. 使用其他人可以轻松使用的开放数据格式。

  3. 编写清晰的说明和信息,告知他人如何正确使用你的数据产品。

  4. 为你的数据使用清晰的名称,以便他人可以轻松理解数据的含义。

  5. 测试你的数据产品与其他系统的兼容性,以确保其正常工作。

自身具有价值

数据产品应该为用户提供价值,即使在不与其他数据产品结合的情况下。

数据产品必须自身具有价值,因为如果没有价值,人们就不会使用它们。仅仅拥有数据是不够的 — 数据需要为用户提供实际价值。这种价值可以表现为洞察、知识或问题的解决方案。

为了使数据产品具有价值,数据产品经理(DPM)需要关注用户的需求和期望。在设计数据产品时,DPM 应该尝试弄清楚用户要解决什么问题,然后围绕这些问题设计解决方案。将数据生产者与业务战略对齐是确保数据能够为消费者提供洞察的最佳方式。

结论

为了创建数据即产品的方法,你需要转变思维方式,将数据视为需要生产、打包并提供给客户的产品。

这需要公司内部对数据生产方式进行新的思考。实际上,数据产品是将产品思维引入数据领域的结果。现实生活中的产品以用户为中心并具有明确的所有者。这两个来自产品管理的组件应该被引入数据领域,以构建可靠的数据产品。

但应用产品管理思维不应仅限于生产阶段。交付高质量数据产品的第二步是打包和展示数据,确保数据提供了合适的背景,使人们能够找到、理解和信任它。通过遵循这些步骤,你可以将数据转化为推动业务决策并提升整体数据体验的宝贵资产。

关于我们

我们撰写了有关利用数据资产的所有过程:包括现代数据堆栈、数据团队组成和数据治理。我们的博客涵盖了从数据中创造实际价值的技术和非技术方面。

在 Castor,我们正在为 Notion、Figma、Slack 一代构建一个数据文档工具。

想看看吗?联系我们,我们将向您展示一个演示。

最初发表于 https://www.castordoc.com.

数据一览:为数据分析创建动态仪表板

原文:towardsdatascience.com/data-at-a-glance-creating-dynamic-dashboards-for-data-analytics-1dce62fc6638

使用 Tableau 构建互动数据可视化

John LenehanTowards Data Science John Lenehan

·发布于 Towards Data Science ·阅读时间 8 分钟·2023 年 11 月 20 日

--

图片由 Carlos Muza 在 Unsplash 提供

数据可视化是任何数据科学家工具箱中的核心技能。任何企业生成的数据量都是巨大的,及时的决策依赖于随时掌握所有相关数据和最新分析。许多组织通过使用自动化仪表板来完成这一关键的数据显示任务,例如使用 Power BI 或 Tableau。只要源数据定期更新,这些可视化工具就会自动生成所需的图表和图形,以跟踪所需的指标。

仪表板是综合叙事数据可视化方法的一个例子——用户可以自行探索数据,并为自己的目的获取洞见。这些类型的可视化允许观众自行研究数据并发现关键见解。像 Power BI 和 Tableau 这样的商业智能应用是这种叙事形式的典型例子,互动仪表板允许观众在最小的指导下深入数据。

有关叙事可视化和数据分析中不同叙事方法的更多信息,请参阅我下面关于使用数据为演示文稿创建叙事的文章:

## 引导你的观众:在演示中创建引人入胜的叙事

如何使用叙事技巧来结构化你的数据展示

towardsdatascience.com

在这篇文章中,我想展示我使用 Tableau Public 创建的三个数据可视化示例。Tableau Public 是一个免费的服务,任何人都可以使用它来创建自己的交互式仪表板——但需要注意的是,所有生成的可视化都是公开可访问的(也可以购买许可证版,该版本更适合处理保密数据)。如果你想在自己的时间里探索数据,我在这里包含了我生成的所有仪表板的链接。

图片由 Luke Chesser 提供,来自 Unsplash

仪表板 1:专业薪资分析

我准备的第一个仪表板展示了不同经验水平、教育水平和工作领域的薪资指标。这些薪资数据来自 Glassdoor 的一小部分用户——原始数据集可以在 Kaggle 上找到 这里。这种类型的仪表板对于求职者和人力资源工作者特别有用,因为它快速展示了工作市场中的薪资范围和人口统计信息。要查看此仪表板中的数据,你可以访问 这里

薪资分析仪表板(作者提供的图像)

我包含了教育程度和职位与薪资的箱形图,以及按经验水平(按性别分段)统计的个体直方图,还有按薪资区间和教育水平分段的个体直方图。

该仪表板可以根据所有数据特征进行过滤——例如,如果我只想查看博士学位的个体,我可以在箱形图中选择 PhD 桶,其他可视化会根据过滤后的数据进行调整。

筛选博士学位的仪表板(作者提供的图像)

通过查看以上这些可视化,我们可以获得一些有趣的见解:

  1. 中位薪资随着教育水平的提高而增加,在所有相关领域中,持有博士学位的人在薪资直方图中位于最高薪资区间。

  2. 整体调查在经验水平上存在明显偏斜,大多数受访者具有 2-3 年的经验。这可能会在任何基于此仪表板的决策中引入偏差——理想情况下,应进行一个更具代表性的调查以获得更好的分析(值得庆幸的是,一旦在源文件中收集了额外的回应,所有可视化都会立即更新)。

  3. 不出所料,研究职位由博士学位持有者主导——尽管我们也看到大量博士在网络/软件开发以及销售/市场营销领域工作。博士受访者中性别比例严重失衡,女性远少于男性。

这些只是对薪资数据集的一些分析示例——额外的分析可能会揭示薪资数据中更多的趋势。

仪表板 2: 纽约市驱逐趋势,2017 年至今

我创建的下一个可视化是一个显示纽约市范围内驱逐趋势的仪表板,涵盖了城市的五个区。原始数据集来自纽约市开放数据门户,可以在这里找到。这个仪表板对于那些在市政政府或非营利组织中工作的人尤其有用,例如想要减少全市驱逐趋势的人——你可以通过链接这里探索该仪表板中的可视化内容。

纽约市驱逐趋势,2017 年至今(图片由作者提供)

对于这个仪表板,我包含了过去 6 年内纽约市记录的所有驱逐地点的地图、每个区的驱逐条形图(还可以扩展到显示区或街区级别的驱逐情况)、按区显示历史驱逐趋势的折线图,以及按受影响程度排序的街区列表。仪表板顶部的滑块使得选择分析日期范围成为可能。

数据和可视化可以按时间和地点进行筛选——在下方的截图中,我已筛选出 2019 年布朗克斯的驱逐情况。与我之前的示例类似,所有其他可视化都会调整以适应筛选的数据——例如,请注意地图现在仅包括布朗克斯区。

筛选 2019 年布朗克斯区驱逐情况,显示在驱逐实例上的提示框(图片由作者提供)

列举一下从上述可视化中获得的一些观察结果:

  1. 布朗克斯区在本报告所涵盖的整个时间段内的驱逐趋势最高,其次是布鲁克林和皇后区。

  2. 2021 年至 2022 年间,驱逐情况明显下降,这显然是由于 Covid-19 大流行期间对驱逐的禁令。

  3. 2019 年布朗克斯的驱逐率似乎随着时间的推移稳定下降,这与该时期其他区的趋势一致。

再次强调,这些示例仅代表了从驱逐情况仪表板中提取的可能观察结果的一小部分。深入分析仪表板可能会揭示更多见解,这将最终支持围绕驱逐资源做出更明智的决策。

仪表板 3: 西班牙旅游趋势,2018–2021

我想讨论的最后一个仪表板是 2018 年至 2021 年西班牙旅游趋势的仪表板。这些可视化的数据来源于西班牙政府的开放数据门户,网址是这里(不幸的是,西班牙政府尚未公布 2022 年或 2023 年的数据)。这有明显的商业和政府应用——例如,西班牙旅游部门的人可能会使用这些数据决定在哪里花钱发展区域旅游。同样,企业也可以利用这些数据预测某一年哪个地区的旅游量最大。任何想探索数据的人可以在链接这里找到仪表板。

西班牙旅游趋势,2018–2021(作者提供的图片)

在这个可视化中,我包含了一个堆积柱状图,显示了按年分的旅游情况(按游客的国籍状态细分),一个按区域的旅游条形图(可以扩展以显示按省份的旅游情况),一个显示按省份和年份的旅游矩阵,以及西班牙所有省份的地图。

再次,这个仪表板可以按位置进行调整——下面我已经筛选了加泰罗尼亚地区,并突出显示了巴塞罗那省。地图工具提示显示了该省的旅游详情,条形图显示了这一期间西班牙国民与国际游客的比例。

为加泰罗尼亚筛选并显示巴塞罗那的工具提示(作者提供的图片)

从查看这个仪表板的图表中,有几个要点值得注意:

  1. 安达卢西亚、加泰罗尼亚、加纳利群岛和瓦伦西亚是西班牙最受欢迎的自治区,差距非常大。

  2. 国际旅游在 2018 年和 2019 年远远超过了国内旅游——2020 年和 2021 年的比例大致相等,这很可能是由于 Covid-19 疫情导致的旅行限制。

  3. 巴塞罗那省的旅游人数占加泰罗尼亚省整体旅游人数的一半以上,几乎是该地区第二大受欢迎省份(赫罗纳)的 2.5 倍。这很可能主要归因于巴塞罗那市。

对西班牙其他地区和省份进行类似的分析和观察,可以更好地了解这些地区的旅游预期。

总结

总结来说,Tableau 是一个强大的数据可视化工具,使用户能够在各种数据集中创建简洁而有影响力的可视化。我在本文中介绍了最近创建的三个相对简单的可视化示例,但在 Tableau Public 上可以找到许多其他示例。在 Tableau 上可以找到的许多视觉效果都是对极其复杂主题的高度直观的表现,这显示了这些仪表盘在数据可视化中的强大能力。这些分析工具的价值不仅在于它们能够快速生成及时的图表和视觉效果,还在于它们可以以清晰且智能的格式呈现数据,从而加快分析和决策过程。

参考文献

[1] Mohith Sai Ram Reddy 等人. (2023). Salary_Data. Kaggle。可用网址: www.kaggle.com/datasets/mohithsairamreddy/salary-data (访问日期:2023 年 11 月 6 日)。

[2] (DOI), D.of I. (2023) 驱逐:纽约市开放数据,驱逐 | 纽约市开放数据。可用网址: data.cityofnewyork.us/City-Government/Evictions/6z8x-wfk4 (访问日期:2023 年 11 月 7 日)。

[3] Diaz, J. (2020 年 12 月 29 日). 纽约批准驱逐禁令至 5 月。NPR。 [在线] 可用网址: www.npr.org/sections/coronavirus-live-updates/2020/12/29/951042050/new-york-approves-eviction-moratorium-until-may (访问日期:2023 年 11 月 7 日)。

[3] Instituto Nacional de Estadística (经济事务与数字化转型部). (2022). 根据旅行者居住地的过夜住宿情况。各省。OAT(API 标识符:48427)。可用网址: datos.gob.es/en/catalogo/ea0010587-pernoctaciones-por-residencia-del-viajero-provincias-oat-identificador-api-48427 (访问日期:2023 年 11 月 10 日)。

Python 中的音频数据增强技术

原文:towardsdatascience.com/data-augmentation-techniques-for-audio-data-in-python-15505483c63c

如何使用 librosa、numpy 和 PyTorch 在波形(时域)和频谱图(频域)中增强音频

Leonie MonigattiTowards Data Science Leonie Monigatti

·发布于 Towards Data Science ·7 分钟阅读·2023 年 3 月 28 日

--

(图片由作者绘制)

深度学习模型需要大量数据。如果你没有足够的数据,生成合成数据可以帮助提高深度学习模型的泛化能力。尽管你可能已经熟悉图像的数据增强技术(例如,水平翻转图像),但音频数据的数据增强技术往往鲜为人知。

## 用深度学习进行音频分类

微调图像模型以应对领域转移和类别不平衡,使用 PyTorch 和 torchaudio 处理音频数据

towardsdatascience.com

本文将回顾流行的音频数据增强技术。你可以在波形和频谱图中应用音频数据增强:

  • 音频数据增强(波形,时域)

    ∘ 噪声注入

    ∘ 时间偏移

    ∘ 改变速度

    ∘ 改变音调

    ∘ 改变音量(不推荐)

  • 频谱图的音频数据增强(频域)

    ∘ 混合数据

    ∘ SpecAugment

对于数据增强,我们将使用[librosa](https://librosa.org/doc/main/index.html),这是一个流行的音频处理库,以及numpy

import numpy as np
import librosa

如果你已经在使用 PyTorch,你还可以使用torchaudio作为替代方案。

音频数据增强(波形,时域)

本节将讨论可以应用于波形音频数据的流行数据增强技术。你可以使用[librosa](https://librosa.org/doc/main/index.html)库中的load()方法将音频文件加载为波形。

PATH = "audio_example.wav" # Replace with your file here
original_audio, sample_rate = librosa.load(PATH)

“Speech Commands”数据集中单词“stop”的原始音频数据(图片来源:作者)

以下代码实现参考自 Kaggle 笔记本,作者为kaerururu [7]和CVxTz [5]。

噪音注入

一种流行的数据增强技术是将某种噪音注入到原始音频数据中。

你可以选择多种不同类型的噪音:

  • 白噪音
# Code copied and edited from https://www.kaggle.com/code/kaerunantoka/birdclef2022-use-2nd-label-f0
noise_factor = 0.005
white_noise = np.random.randn(len(original_audio)) * noise_factor
  • 彩色噪音(例如粉色噪音、棕色噪音等)
# Code copied and edited from https://www.kaggle.com/code/kaerunantoka/birdclef2022-use-2nd-label-f0
import colorednoise as cn

pink_noise = cn.powerlaw_psd_gaussian(1, len(original_audio))
  • 背景噪音
# Load background noise from another audio file
background_noise, sample_rate = librosa.load("background_noise.wav")

一旦定义了你想注入的噪音类型,你可以将噪音添加到原始波形音频中。当然,你可以使用各种不同类型的噪音进行数据增强。下方可以看到白噪音注入的示例。

augmented_audio = original_audio + noise

音频数据增强:白噪音(图片来源:作者)

时间平移

使用numpy库中的roll()函数,你可以在时间上平移音频。

# Code copied and edited from https://www.kaggle.com/code/CVxTz/audio-data-augmentation/notebook
augmented_audio = np.roll(original_audio, 3000)

音频数据增强:时间平移(图片来源:作者)

请注意,如果没有足够的尾部静音,音频将会环绕。根据你的声音类型,这种数据增强在某些情况下(例如人声)可能不推荐使用。

改变速度

你还可以使用[librosa](https://librosa.org/doc/main/index.html)库中的time_stretch()方法来增加(rate>1)或减少(rate<1)音频的速度。

# Code copied and edited from https://www.kaggle.com/code/CVxTz/audio-data-augmentation/notebook
rate = 0.9
augmented_audio = librosa.effects.time_stretch(original_audio, rate = rate)

音频数据增强:时间拉伸/改变速度(图片来源:作者)

改变音高

或者你可以使用[librosa](https://librosa.org/doc/main/index.html)库中的pitch_shift()方法来修改音频的音高。

# Code copied and edited from https://www.kaggle.com/code/CVxTz/audio-data-augmentation/notebook
augmented_audio = librosa.effects.pitch_shift(original_audio, sampling_rate, pitch_factor)

音频数据增强:音高变换(图片来源:作者)

改变音量(不推荐)

你也可以在音量方面增强波形。然而,如果你打算将波形转换为谱图,音量增强将无效,因为幅度在频域中不被考虑。

# Increase volume by 5 dB
augmented_audio = original_audio + 5

# Decrease volume by 5 dB
augmented_audio = original_audio - 5

频域的音频数据增强(谱图)

在用深度学习模型建模音频数据时,将音频分类问题转化为图像分类问题是一种流行的方法。为此,波形音频数据被转换为 Mel 谱图。如果你需要对 Mel 谱图有进一步了解,推荐阅读以下文章:

[## 音频深度学习简明教程(第二部分):为何 Mel 频谱图表现更佳]

《Python 音频处理指南》:用简单的英语解释什么是 Mel 频谱图以及如何生成它们。

towardsdatascience.com

你可以使用melspectrogram()power_to_db()方法从[librosa](https://librosa.org/doc/main/index.html)库将波形音频转换为 Mel 频谱图。

original_melspec = librosa.feature.melspectrogram(y = original_audio,
                                                  sr = sample_rate, 
                                                  n_fft = 512, 
                                                  hop_length = 256, 
                                                  n_mels = 40).T

original_melspec = librosa.power_to_db(original_melspec)

“Speech Commands” 数据集中“stop”一词的原始音频数据(波形) [1](图像作者提供)

“Speech Commands” 数据集中“stop”一词的原始音频数据作为 Mel 频谱图 [1](图像作者提供)

尽管你现在面临的是图像分类问题,但在选择频谱图的图像增强技术时,你必须小心。例如,水平翻转频谱图会实质性改变频谱图中包含的信息,因此不推荐这样做。

本节将讨论你可以应用于 Mel 频谱图的流行数据增强技术。

以下代码实现参考自 Kaggle Notebooks,由 kaerururu [7] 和 DavidS [6] 提供。

Mixup

简单来说,Mixup [4] 通过叠加两个样本并给新样本两个标签来实现样本的合成。

# Code copied and edited from https://www.kaggle.com/code/kaerunantoka/birdclef2022-use-2nd-label-f0
def mixup(original_melspecs, original_labels, alpha=1.0):
    indices = torch.randperm(original_melspecs.size(0))

    lam = np.random.beta(alpha, alpha)

    augmented_melspecs = original_melspecs * lam + original_melspecs[indices] * (1 - lam)
    augmented_labels = [(original_labels * lam), (original_labels[indices] * (1 - lam))]

    return augmented_melspec, augmented_labels

频谱图的数据增强:Mixup [4](图像作者提供)

另外,你也可以尝试计算机视觉中使用的其他数据增强技术,比如 cutmix [3]。

SpecAugment

SpecAugment [2] 对频谱图的作用类似于 cutout 对常规图像的作用。虽然 cutout 会遮挡图像中的随机区域,SpecAugment [2] 会遮罩随机频率和时间段。

# Code copied and edited from https://www.kaggle.com/code/davids1992/specaugment-quick-implementation
def spec_augment(original_melspec,
                 freq_masking_max_percentage = 0.15, 
                 time_masking_max_percentage = 0.3):

    augmented_melspec = original_melspec.copy()
    all_frames_num, all_freqs_num = augmented_melspec.shape

    # Frequency masking
    freq_percentage = random.uniform(0.0, freq_masking_max_percentage)
    num_freqs_to_mask = int(freq_percentage * all_freqs_num)
    f0 = int(np.random.uniform(low = 0.0, high = (all_freqs_num - num_freqs_to_mask)))

    augmented_melspec[:, f0:(f0 + num_freqs_to_mask)] = 0

    # Time masking
    time_percentage = random.uniform(0.0, time_masking_max_percentage)
    num_frames_to_mask = int(time_percentage * all_frames_num)
    t0 = int(np.random.uniform(low = 0.0, high = (all_frames_num - num_frames_to_mask)))

    augmented_melspec[t0:(t0 + num_frames_to_mask), :] = 0

    return augmented_melspec

频谱图的数据增强:SpecAugment [2](图像作者提供)

另外,如果你使用的是 Pytorch,你还可以使用 torchaudio 中的 TimeMaskingFrequencyMasking 增强方法:

import torch
import torchaudio
import torchaudio.transforms as T

time_masking = T.TimeMasking(time_mask_param = 80)
freq_masking = T.FrequencyMasking(freq_mask_param=80)

augmented_melspec = time_masking(original_melspec)
augmented_melspec = freq_masking(augmented_melspec)

总结

音频数据的增强可以应用于时间域(波形)以及频率域(Mel 频谱图)。为了在深度学习环境中成功应用数据增强,你需要考虑以下处理步骤:

  1. 将音频文件加载为波形(时间域)

  2. 将数据增强应用于波形

  3. 将音频从波形转换为频谱图(频率域)

  4. 将数据增强应用于频谱图

本文介绍了波形数据的不同数据增强技术。需要注意的是,某些数据增强技术,如增强音量,在将波形转换为谱图时效果不佳,因为频域中不考虑振幅。

虽然理论上你可以将所有图像增强技术应用于谱图,但并非所有技术都是合理的。例如,垂直或水平翻转会改变谱图的意义。此外,一种专为谱图量身定制的流行 cutout 图像增强变体会遮蔽整个时间戳和频率。

喜欢这个故事吗?

免费订阅 以便在我发布新故事时收到通知。

[## 每当 Leonie Monigatti 发布新内容时获取电子邮件。

每当 Leonie Monigatti 发布新内容时获取电子邮件。通过注册,如果你还没有,你将创建一个 Medium 账户……

medium.com](https://medium.com/@iamleonie/subscribe?source=post_page-----15505483c63c--------------------------------)

LinkedInTwitter Kaggle上找到我!

参考文献

数据集

[1] Warden P. Speech Commands: 用于单词语音识别的公开数据集,2017 年。可从 download.tensorflow.org/data/speech_commands_v0.01.tar.gz 获得

许可:CC-BY-4.0

文献

[2] Park, D. S., Chan, W., Zhang, Y., Chiu, C. C., Zoph, B., Cubuk, E. D., & Le, Q. V. (2019). Specaugment: 一种用于自动语音识别的简单数据增强方法。 arXiv 预印本 arXiv:1904.08779

[3] Yun, S., Han, D., Oh, S. J., Chun, S., Choe, J., & Yoo, Y. (2019). Cutmix: 正则化策略以训练具有可定位特征的强分类器。 IEEE/CVF 国际计算机视觉会议论文集(第 6023–6032 页)。

[4] Zhang, H., Cisse, M., Dauphin, Y. N., & Lopez-Paz, D. (2017) mixup: 超越经验风险最小化。arXiv 预印本 arXiv:1710.09412。

代码

[5] CVxTz (2018). 音频数据增强 在 Kaggle 笔记本中(访问日期:2023 年 3 月 24 日)。

[6] DavidS (2019). SpecAugment 快速实现 在 Kaggle 笔记本中(访问日期:2023 年 3 月 24 日)。

[7] kaerururu (2022). BirdCLEF2022:使用第二标签 f0 在 Kaggle 笔记本中(访问日期:2023 年 3 月 24 日)。

2023 年你需要知道的数据热词 — 第二部分

原文:towardsdatascience.com/data-buzzwords-you-need-to-know-in-2023-part-ii-42057a87814a

你今年可能会遇到的数据术语及其含义

Rashi DesaiTowards Data Science Rashi Desai

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

--

照片由Sienna Wall提供,来自Unsplash

早在 1 月初,我写过一篇关于 2023 年你需要知道的 13 个数据热词的文章,与TDS 编辑一起。我当时不愿意写一篇 15 分钟的长文来包括 23 个 2023 年的热词,但我知道我必须很快写关于那 10 个热词的内容!通过对那篇博客的评论、Twitter 私信和 LinkedIn 消息,我听到了一些我应该涵盖的 10 个词汇和一些新的词汇。因此,这里是关于 2023 年你应该知道的数据热词的第二部分,来赶上数据词汇吧!

1. 数据可扩展性

在 2023 年,公司的生存或死亡将取决于他们的数据策略。

随着数据源、获取渠道和企业消费的洞察数量的增加,组织必须具备扩展和应对变化需求的能力,方法是添加/移除数据资源。

数据可扩展性是指数据、数据库或任何数据系统在响应处理需求变化时,能够增加或减少处理能力,而不会对性能和成本产生重大影响。今天,可扩展性至关重要,因为它允许企业增长和盈利,而不会因数据结构或容量和资源的不足而受到限制。

2. 数据网格

组织在消费数据时面临的一个挑战是数据管理和治理的去中心化,这导致人们在 2023 年讨论数据网格。

数据网格采取了为去中心化的数据构建去中心化数据架构的方法。它本质上将分散在多个数据孤岛(云、本地数据、应用程序数据库、BI 仪表板、分析应用程序等)中的数据连接起来。

数据网格和数据织物之间经常进行比较——哪种选择更适合组织,让我告诉你,它们之间的差异很大。数据织物通过一个单一的、集中的系统来管理和治理多个数据源,而数据网格则完全相反。最终目标保持一致——连接分布在不同地点和组织中的数据。

3. 数据湖屋

数据湖屋在过去几年中成为一个新兴概念,我认为 2023 年是湖屋展示其功能和能力的年份(它们存在的巅峰)。

我相信每个阅读这篇博客的人都至少听说过或读过Databricks。Databricks 是一个数据湖屋,它的功能是结合运营卓越性、可靠性、安全性、合规性、性能效率、成本优化和组织数据规模,以实现商业智能(BI)和机器学习(ML)。

我不知道你怎样,但我有一个问题——数据湖与数据仓库有什么不同?

两者都被广泛接受并用于存储大数据,但我理解的区别是——数据湖是一个巨大的原始数据池,在数据进入时没有定义明确的目的,而数据仓库则是一个结构化的存储库,用于托管经过筛选、处理的数据,以达到特定的目的。

4. 黑暗数据

数据是今天科技世界中任何对话的核心,如果我们讨论每天产生的数据量,2023 年的最新估计是每天 1.145 万亿 MB。

我们为“常规业务活动”收集、处理和存储数据,一旦项目完成,通常会失败地将这些数据用于其他目的。我们将黑暗数据定义为通过各种来源获取但未用于分析或提取决策见解的数据。

存储和保护数据对企业来说不仅花费更高,有时甚至带来更大的风险。一些组织的黑暗数据示例可能包括日志文件、账户信息、财务档案、前员工的数据等,这些数据不再有用,仍在占用空间。

5. Web3

我在 2022 年中期的一个播客中听到过 Web3 这个词,那时一位单口喜剧演员谈到了他除了喜剧之外的兴趣,并谈到了 Web3、Web3 带来的机会以及其兴起所涉及的财务。

我对这意味着什么很感兴趣,谷歌了一下了解到 Web3 是万维网的第三代,它引入了由区块链技术和基于代币的经济学支持的去中心化互联网。我们在 2022 年开始看到 Web3 友好的方法,2023 年,越来越多的国家正在制定和通过立法,将自己定位为“Web3 友好”区域。

Web3 将彻底改变数据的存储和使用方式。数据将不再由一个中央实体在一个庞大的服务器中存储和控制,而是由通过一个网络(去中心化)链接在一起的数百万用户设备拥有——提升个人数据所有权。Web3 采用基于机器的数据理解来为用户开发更智能、更互联的网络体验。

6. 数据同步

2023 年是数据分析师的年头。任何数据分析师面临的一个痛点是保持所有报告工作的一致性。因此,如果我们希望 2023 年对分析师来说是一个好年头,我们就要转向同步。

数据同步正如其字面意思——确保你的数据在所有应用程序中保持一致、相同和准确,无论它们的位置如何(基本上,每个数据分析师的噩梦)。

我在一个博客中读到一个示例,以更好地理解数据同步,这里是我的见解——假设我们在音乐流媒体平台的一个服务器上添加了一个包含五首歌曲的新专辑,那么,通过数据同步,所有其他音乐流媒体平台的服务器(在协议下)将获得相同的专辑数据。数据同步在数据库管理中扮演着重要角色。数据同步还带来数据治理和安全访问控制。

7. 分布式云

全球云计算市场预计在 2023 年将 增长 42%,我预期今年组织和行业将大规模转向优化云成本和支出。

组织正在转向云计算,讨论是继续使用本地部署还是采用混合模式,在所有这些有益的讨论中,分布式云作为一种选项出现,其中一个组织使用多个云来满足性能期望、合规需求和支持计算需求,同时由公共云提供商集中管理。

当我第一次了解分布式云时,我将其与混合云混淆了,并想为我的工作场所制定建议,这里是它们的区别——混合云由多个公共云服务和本地基础设施组成,而分布式云由一个中央公共云提供商管理。

8. 超自动化

日常工作的自动化在 2023 年成为许多组织的优先事项(我的团队也是如此),而超自动化旨在自动化组织中可以自动化的一切。

超自动化旨在通过人工智能(AI)工作、机器人过程自动化(RPA)和其他技术来简化数据过程,使其能够在没有任何人工干预的情况下自主运行。超自动化从确定要自动化的项目开始,选择合适的自动化工具,通过多个自动化过程推动快速性,并使用各种数据形式扩展其能力。

9. 以人为本的数据科学

“以人为本的人工智能”这一术语已经流行了一段时间。我们今天所做的一切和创造的东西并不是在真空中运作的,它们会在某个交互点涉及到人类。我一直好奇实际应用的以人为本的数据科学会是什么样的,2023 年出现了一些项目,其中人类与产品的互动主导了业务决策。

我喜欢将以人为本的数据科学定义为一种概念,旨在理解人们如何互动以及如何理解社会情境,从而使人类能够探索和获取洞察,并以最终用户为中心(不仅仅是业务)设计数据模型。

我们可以这样看待——数据不过是人类互动的数字痕迹。以人为本的方法可以通过使过程更透明、提出问题和考虑数据的社会背景,来增强数据科学家每天做出的选择。

10. 决策智能

决策智能是新时代的智能决策处理方法。

观察 > 调查 > 建模 > 上下文化 > 执行

我第一次了解决策智能(DI)是在 2021 年,期间我在百事公司的实习项目中接触到了这一概念,其中一个项目是使用决策智能预测百事食品产品在超级碗前后的各个人群中的销售情况。决策智能提供了一个干净而简洁的数据驱动决策结构。

你使用描述性、诊断性和预测性分析,每种分析都有其自身的特点作为核心力量。借助决策智能,你可以创建三种不同类型的模型:基于人类的决策、基于机器的决策以及混合决策。这个过程涉及一个重度减少算法,它将数据科学与社会科学、决策理论和管理科学相结合。

我注意到越来越多的职位描述面向对分析感兴趣的心理学专业人士,日益关注决策智能。

这就是我在这篇长博客中的全部内容。感谢阅读!希望你觉得这篇文章有趣。请在评论中告诉我你在讲故事方面的经验、你在数据领域的旅程,以及你在 2023 年期望获得什么!

如果你喜欢阅读这样的故事,考虑通过这个链接成为 Medium 会员

祝数据分析愉快!

拉希是来自芝加哥的数据专家,她热衷于可视化数据并创造有洞察力的故事以传达见解。她是全职的医疗数据分析师,周末时会边喝一杯热巧克力边写数据博客…

HuggingFace 中的数据整理器

原文:towardsdatascience.com/data-collators-in-huggingface-a0c76db798d2

它们是什么以及它们的功能

Mina GhashamiTowards Data Science Mina Ghashami

·发表于 数据科学前沿 ·9 分钟阅读·2023 年 11 月 8 日

--

图片来自 unsplash.com

当我开始学习 HuggingFace 时,数据整理器是我最不直观的组件之一。我很难理解它们,而且没有找到足够好的资源来直观地解释它们。

在这篇文章中,我们将探讨数据整理器是什么,它们有何不同,以及如何编写自定义的数据整理器。

数据整理器:高级

数据整理器是 HuggingFace 中数据处理的关键部分。我们在对数据进行标记化后,都会使用它们,然后将数据传递给 Trainer 对象来训练模型。

简而言之,它们将样本列表整理成一个小型训练批次。它们的功能取决于它们所定义的任务,但至少会对输入样本进行填充或截断,以确保小批次中的所有样本长度相同。典型的小批量大小范围从 16 到 256 个样本,具体取决于模型大小、数据和硬件限制。

数据整理器是任务特定的。每个以下任务都有一个数据整理器:

  • 因果语言建模(CLM)

  • 掩码语言建模(MLM)

  • 序列分类

  • Seq2Seq

  • 标记分类

一些数据整理器很简单。例如,对于“序列分类”任务,数据整理器只需填充小批量中的所有序列以确保它们长度相同。然后将它们连接成一个张量。

一些数据整理器非常复杂,因为它们需要处理该任务的数据处理。

基本数据整理器

最基本的两个数据整理器如下:

1)DefaultDataCollator: 这不进行任何填充或截断。它假设所有输入样本的长度相同。如果你的输入样本长度不一致,这会引发错误。

import torch
from transformers import DefaultDataCollator

texts = ["Hello world", "How are you?"]

# Tokenize
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
tokens = [tokenizer(t) for t in texts]

# Default collate function 
collate_fn = DefaultDataCollator()

# Pass it to dataloader
dataloader = torch.utils.data.DataLoader(dataset=tokens, collate_fn=collate_fn, batch_size=2) 

# this will end in error
for batch in dataloader:
    print(batch)
    break

令牌化后的输出是:

[{'input_ids': [101, 7592, 2088, 102], 'token_type_ids': [0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1]},
 {'input_ids': [101, 2129, 2024, 2017, 1029, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1]}]

如你所见,两个序列的长度不同。第一个序列有 4 个标记,第二个序列有 6 个标记。这是在调用数据加载器时出现错误的原因!

现在如果我们将输入更改为两个相同长度的序列,例如:

texts = ["Hello world", "How are"]

然后代码运行正常,输出将是:

{'input_ids': tensor([[ 101, 7592, 2088,  102],
                      [ 101, 2129, 2024,  102]]), 
'token_type_ids': tensor([[0, 0, 0, 0],
                          [0, 0, 0, 0]]), 
'attention_mask': tensor([[1, 1, 1, 1],
                          [1, 1, 1, 1]])}

请注意,它不返回labels

2) DataCollatorWithPadding: 这个整理器填充输入样本,使它们都具有相同长度。对于填充,

  • 它要么填充到提供的max_length参数。

  • 或者它填充到批次中最长的序列。

查看 文档 获取更多详细信息。

此外,这个整理器接受tokenizer,因为许多标记器有不同的填充标记,因此DataCollatorWithPadding接受标记器来确定填充序列时的填充标记。

import torch
from transformers import DataCollatorWithPadding

texts = ["Hello world", "How are you?"]

# Tokenize
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
tokens = [tokenizer(t) for t in texts]

# Default collate function 
collate_fn = DataCollatorWithPadding(tokenizer, padding=True) #padding=True, 'max_length'

dataloader = torch.utils.data.DataLoader(dataset=tokens, collate_fn=collate_fn, batch_size=2) 

for batch in dataloader:
    print(batch)
    break

上述代码的输出是:

{'input_ids': tensor([[ 101, 7592, 2088,  102,    0,    0],
                      [ 101, 2129, 2024, 2017, 1029,  102]]), 
'token_type_ids': tensor([[0, 0, 0, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0]]), 
'attention_mask': tensor([[1, 1, 1, 1, 0, 0],
                          [1, 1, 1, 1, 1, 1]])}

注意第一个序列的 4 个标记现在填充为 6 个标记。在bert-base-uncase标记器中,填充标记的 ID 是 0。让我们在代码中查看:

print(tokenizer.special_tokens_map)
print()

这段代码输出:

{'unk_token': '[UNK]', 'sep_token': '[SEP]', 'pad_token': '[PAD]', 'cls_token': '[CLS]', 'mask_token': '[MASK]'}

pad_token = [PAD] 是用于填充的标记。让我们检查它的 token_id。

print(tokenizer.convert_tokens_to_ids('[PAD]'))

这输出为0

请注意,与 DefaultDataCollator 类似,DataCollatorWithPadding 不创建labels。如果你的数据中已经有标签,它们会返回,但否则不会创建!!

简而言之,如果你的标签简单明了,且数据在喂给模型进行训练之前不需要任何特殊处理,则使用这两个整理器。

语言建模数据整理器

语言建模数据整理器有两种模式:

  • MLM 数据整理器:这是用于掩码语言建模的,我们掩盖 15%的标记,模型进行预测。

  • CLM 数据整理器:这是用于因果语言建模的,我们掩盖当前标记右侧的所有标记,并期望模型在每一步预测下一个标记。

在代码中,MLM 整理器是:

collate_fn = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=True, mlm_probability=0.15)

CLM 整理器是:

collate_fn = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)

让我们看看 MLM 整理器的例子:

from transformers import DataCollatorForLanguageModeling
from transformers import AutoTokenizer
import torch

## input text
texts = [
  "The quick brown fox jumps over the lazy dog.",
  "I am learning about NLP and AI today"  
]

## tokenize
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')

data = [tokenizer(t) for t in texts] 

## MLM collator
collate_fn = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=True, mlm_probability=0.15)

## pass collator to dataloader
dataloader = torch.utils.data.DataLoader(data, collate_fn=collate_fn, batch_size=2)

## let's look at one sample
for batch in dataloader:
    print(batch)
    break

它输出如下内容:

{'input_ids': tensor([[  101,  1996,  4248,  2829,  4419, 14523,  2058,  1996, 13971,  3899,
          1012,   102],
                      [  101,  1045,  2572,   103,  2055, 17953,  2361,  1998,  9932,  2651,
           102,     0]]), 
'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 
'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
                          [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]]), 
'labels': tensor([[-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100],
                  [-100, -100, -100, 4083, -100, -100, -100, -100, -100, -100, -100, -100]])}

注意几点:

1) 两个示例的input_ids都以101开头,以102结尾。在第二个示例中,102之后有一个0,我们已经知道这是填充标记。让我们看看101102是什么?

print( tokenizer.decode(101), tokenizer.decode(102))
## prints [CLS] and [SEP]

它分别打印[CLS][SEP]标记。

2) 它返回labels,不同于基本的数据整理器。我们看到标签中有很多-100。请注意,每个样本的labels长度与input_ids的长度相同!标签为-100的地方意味着对应的标记没有被屏蔽,因此在计算损失时我们需要忽略这些位置。MLM 使用交叉熵损失函数,如果你查看文档,默认的ignore_index=-100;这意味着如果标签设置为-100则忽略。

第三点需要注意的是,对于第一个示例,标签是[-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100],这意味着没有任何标记被屏蔽。然而,对于第二个输入,标签是[-100, -100, -100, 4083, -100, -100, -100, -100, -100, -100, -100, -100],我们看到有一个标记被屏蔽,其对应的标签是4083input_ids中的对应标记是103,这是[MASK]标记的 token_id。

CLM 数据整理器要简单得多,因为它用于因果语言建模,即预测下一个标记。我们来看一个示例:

from transformers import DataCollatorForLanguageModeling
from transformers import AutoTokenizer

texts = [
  "The quick brown fox jumps over the lazy dog.",
  "I am learning about NLP and AI today"  
]

# Tokenize
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
tokens = [tokenizer(t) for t in texts]

collate_fn = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
dataloader = torch.utils.data.DataLoader(data, collate_fn=collate_fn, batch_size=2)

for batch in dataloader:
    print(batch)

输出如下:

{'input_ids': tensor([[  101,  1996,  4248,  2829,  4419, 14523,  2058,  1996, 13971,  3899,
          1012,   102],
                      [  101,  1045,  2572,  4083,  2055, 17953,  2361,  1998,  9932,  2651,
           102,     0]]), 
'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 
'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
                           [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]]), 
'labels': tensor([[  101,  1996,  4248,  2829,  4419, 14523,  2058,  1996, 13971,  3899,
          1012,   102],
                  [  101,  1045,  2572,  4083,  2055, 17953,  2361,  1998,  9932,  2651,
           102,  -100]])}

你会看到在这两个示例中,标签是input_ids的副本。这是因为在因果语言建模中,任务是预测给定所有先前标记的下一个标记,而某个位置的标签就是该标记本身。

自定义数据整理器

假设你有一个数据集,包含两列:指令响应

作者提供的图像

你想对一个预训练模型进行指令调优。不深入细节,我们注意到我们需要一个自定义数据整理器来仅屏蔽响应而不是指令。

假设我们写一个函数,将两列合并成以下格式:

下面是一个描述任务的指令。写一个适当完成请求的响应。 ### 指令: {instruction} ### 响应: {response} ### 结束

数据现在看起来是这样的:

作者提供的图像

我们看到有特殊标记:

  • 指令

  • 响应

  • 结束

那么让我们开始编写自定义数据整理器。我们希望有一个仅屏蔽响应而不屏蔽指令的数据整理器。为什么?因为我们希望模型生成响应而不是指令。

from typing import Any, Dict, List, Tuple, Union
import numpy as np

from transformers import AutoModelForCausalLM
from transformers import AutoTokenizer
from transformers import TextDataset, DataCollatorForLanguageModeling
from transformers import Pipeline, PreTrainedTokenizer

RESPONSE_KEY = f"### Response:\n"

class DataCollatorForCompletionLM(DataCollatorForLanguageModeling):    
    def torch_call(self, examples: List[Union[List[int], Any, Dict[str, Any]]]) -> Dict[str, Any]:

        # The torch_call method overrides the same method in the base class and 
        # takes a list of examples as input.  
        batch = super().torch_call(examples)

        labels = batch["labels"].clone()

        # The code then encodes a special token, RESPONSE_KEY_NL, 
        # representing the end of the prompt followed by a newline. 
        # It searches for this token in the sequence of tokens (labels) 
        # and finds its index.
        response_token_ids = self.tokenizer.encode(RESPONSE_KEY)

        for i in range(len(examples)):

            response_token_ids_start_idx = None
            for idx in np.where(batch["labels"][i] == response_token_ids[0])[0]:
                response_token_ids_start_idx = idx
                break

            if response_token_ids_start_idx is None:
                # If the response token is not found in the sequence, it raises a RuntimeError. 
                # Otherwise, it determines the end index of the response token.
                raise RuntimeError(
                    f'Could not find response key {response_token_ids} in token IDs \
                    {batch["labels"][i]}'
                )

            response_token_ids_end_idx = response_token_ids_start_idx + 1

            # To train the model to predict only the response and ignore the prompt tokens, 
            # it sets the label values before the response token to -100\. 
            # This ensures that those tokens are ignored by the PyTorch loss function during training.
            labels[i, :response_token_ids_end_idx] = -100

        batch["labels"] = labels

        return batch

这个数据整理器找到###Response: \n的位置,并将该位置之前的任何标记的标签更改为-100。这样,损失函数将忽略这些标记。

当我们调用数据整理器时,我们使用它如下:

data_collator = DataCollatorForCompletionLM(
        tokenizer=tokenizer, mlm=False, return_tensors="pt"
)

像往常一样,我们在进行模型训练之前,在训练对象中使用这个数据整理器。

结论

在这篇文章中,我们深入探讨了 HuggingFace 中的数据收集器。我们了解到数据收集器负责填充序列,使得批次中的所有样本长度相同。我们还看到了四种不同的数据收集器示例。其中一个重要的数据收集器是 DataCollatorForLanguageModeling,它用于 MLM 和 CLM 训练。我们还看到了如何修改数据收集器以进行指令调优的示例。

如果你有任何问题或建议,请随时联系我:

电子邮件: mina.ghashami@gmail.com

LinkedIn: www.linkedin.com/in/minaghashami/

计算机视觉数据整理指南

原文:towardsdatascience.com/data-curation-guide-for-computer-vision-acc525f4cd7

计算机视觉中的数据整理缺乏标准化,导致许多从业者不确定如何正确进行。我们总结了一些最常见的方法。

Igor SusmeljTowards Data Science 伊戈尔·苏斯梅尔

·发表于Towards Data Science ·阅读时间 10 分钟·2023 年 3 月 9 日

--

从哪里开始数据整理?瓦伦丁·安东努奇的图片:www.pexels.com/de-de/foto/person-die-kompass-halt-691637/

本文是以计算机视觉中的数据整理为背景编写的。然而,一些概念可以应用于其他数据领域,如自然语言处理、音频或表格数据。

什么是数据整理?

数据整理是一个广泛使用的术语,特别是在数据驱动的人工智能领域。理解数据整理在机器学习中的组成部分非常重要。我们理解数据整理包括以下几个组成部分:

  • 数据清理和归一化 — 删除“损坏”样本或尝试纠正它们的过程

  • 数据选择 — 根据特定任务的重要性对数据进行排序的过程

机器学习中的数据清理

理解结构化数据的数据清理最简单的方法是考虑表格数据。假设你在一家银行从事一个项目,想要根据客户的来源分析他们的支出。你的数据在一个 CSV 文件中,你发现位置信息缺失。一些条目存在拼写错误,例如城市拼写错误或完全缺失的条目。你现在可以选择通过删除所有“损坏”的条目来清理数据,或者根据其他可用数据尝试纠正缺失的条目。像fancyimputeautoimpute这样的开源库可以对表格数据中的缺失示例进行插补。

当处理非结构化数据如图像时,可以检查相机故障的图像。硬件问题可能导致视频帧损坏,或者记录的数据可能质量更好(光线不足,图像模糊)。

对于非结构化数据,你通常依赖于监督学习。你也应该考虑清理“损坏”的标签,或至少尝试纠正它们。

数据选择(与主动学习相关)

正如你可能之前听说过的,并非所有数据对你的机器学习模型都同等重要。你不想在不需要的数据上浪费资源

在训练机器学习模型时,你必须确保用于训练的数据与系统运行时预期的数据匹配。这听起来简单,但实际上是一个巨大的问题。考虑开发一个自动送货机器人感知系统。你有一个原型机器人从你有研发的城市中选择数据。你的模型将基于单一城市的数据进行训练,但最终需要在全球各种城市中工作。不同的城市可能有不同的建筑结构、环境条件、交通标志等。

可以进行逐步推出,按城市部署机器人,并持续收集更多数据以改进感知系统。但是,这样一来,初始城市的数据贡献显著高于最后的城市。如何跟踪哪些是新的、有效的,或者哪些是“冗余”的?

什么是良好整理的数据集?

首先,实际上没有通用的完美数据集。数据集的价值取决于你想要解决的任务以及模型架构、训练过程和可用计算能力等其他变量。尽管如此,一个良好整理的数据集可以帮助防止遇到本指南中概述的任何问题。

理想情况下,你的数据集是良好平衡的。你的测试集代表了模型的部署领域,并且独立于训练数据。因此,你了解模型的泛化能力。你的数据集涵盖了你关心的边缘情况,标签也是正确的。

数据整理入门指南

我创建了一个入门指南来帮助你识别机器学习管道中的问题。将其作为参考以了解最常见的数据整理工作流程。

请注意,这份备忘单并没有涵盖所有可能的问题,应作为你不知从何开始时的帮助工具。

如何使用数据整理入门指南:

  1. 识别你面临的模型问题,位于左侧

  2. 跟随箭头查找潜在的数据问题

  3. 选择解决问题的数据整理工作流程

数据整理入门指南。[图片由作者提供]

常见模型问题及其解决方案

最常见的数据问题概述及解决建议。

1: 我的模型在训练集上准确率很高,但在测试集上准确率很低

发生这种情况有不同的原因。首先,你应该排除可能导致模型过拟合训练数据的常见训练过程错误。添加更多的数据增强或正则化方法,如 L2 或权重范数,可以帮助减少过拟合的风险,正如 Andre Ng 在这个视频中所述

也可能存在其他过拟合的原因。

最常见的情况之一是你的模型在训练数据上表现良好,但在测试集上表现较差。这很可能是你的模型过拟合了。简单的训练方法,如添加正则化方法,可以减少这个问题。但在某个时候,你需要改进数据本身。[image by author]

一旦你知道这不再是模型训练问题而是数据问题,你应该查看你用于训练和测试集的数据。另一个可能的原因是你的训练数据代表性不够。可能是一些示例出现频率很低,模型无法从中学习。在这种情况下,一个潜在的解决方案是收集更多关于模型难以处理的稀有事件的数据。像主动学习这样的方案已经被提出,以自动化和可扩展的方式解决这个问题。BAAL[1] 是 2020 年的一个主动学习算法。

2: 模型在不常见/稀有情况下失败

通常情况下,你的模型只在某些类别和情况中表现良好。例如,稀有类别通常被常见的学习过程忽略。在特定的应用场景中,例如医学影像中,稀有类别可能比其他类别更重要。在这种情况下,存在几种解决方案。

首先,如果你的模型在某个特定类别上的表现不佳,也要检查一下该类别是否在你的数据集中代表性不足。如果这是一个少数类别,你应该尝试使用加权损失函数来解决这种不平衡。这是一个简单的技巧,通常会产生很有希望的结果。

如果问题仍然存在,你可以开始从数据的角度思考如何解决这个问题。有两个选项。我们可以改善类别平衡或尝试找到更多边缘情况。

改进类别平衡 你需要改变训练数据集中类别的比例,使其更加均衡。有几种方法可以处理不同的情况。如果你处理大量数据,可以开始丢弃多数类的样本(欠采样)以平衡类别。

如果你处理小型数据集或无法负担删除训练数据,你可以尝试收集更多数据,并优先考虑之前代表性不足的类别。但是如何实现后者呢?你可以使用模型在未标记数据上的预测来了解数据分布。每当我们发现主要类别时,我们就会降低对它们的优先级。我们会增加对稀有类别潜在预测的优先级。有一个处理不平衡数据的开源库叫做 imbalanced-learn。

通过主动学习发现更多边界情况 如果你确切知道你在寻找什么(例如,模型在检测警车方面表现不佳),你可以依靠相似性搜索方法,如SEALS[2]。你可以使用这些稀有物体的参考图像,并利用它们的嵌入作为搜索向量,在未标记数据中寻找相似的图像或物体。使用相似性搜索时要小心。如果你只使用某种特定类型的警车或某个角度的图像,那么你可能会发现更多相似的警车图像,这些图像不会增强你的数据集,反而增加了附近的重复项。这会产生类似于仅仅扩充初始数据集的效果。相反,你希望找到与现有警车略有不同的相似警车!

另一种更通用的方法是使用主动学习的模型预测和嵌入组合[3]。你可以通过找到难以分类或在语义上与现有训练数据非常不同的未标记数据来将新图像添加到训练集中。在 Lightly,我们在这种方法中取得了很大的成功,因为它可以自动化并扩展到大规模数据集。

3: 模型随着时间变差

你可能会发现失败率增加,并且有一种直觉认为你在几周前部署的模型没有按预期工作。这可能是“数据漂移”问题。模型需要用新的训练数据进行更新。首先,你应该分析模型在生产中看到的数据,并将其与用于训练模型的数据进行比较。可能会有明显的分布差异。

如果模型性能随着时间的推移而下降,你可能面临数据漂移问题。简单来说,模型在部署中看到的数据开始与训练中看到的数据逐渐偏离。如果不使用新数据重新训练模型并重新部署,准确率的损失可能会增加,直到系统不再可靠。[图片由作者提供]

作为一个简单的实验,你也可以训练一个简单的分类器模型,来分类给定的图像是否属于现有的训练数据或新的生产数据。如果数据分布完全匹配,你的分类器不会比随机猜测表现更好。然而,如果分类器表现良好,说明两个领域有所不同。分析领域差距后,你应该更新训练数据。例如,你可以选择一部分生产数据,使用基于多样性的采样,然后进行新的训练/测试拆分。然后,将相应的训练/测试拆分添加到你用来训练已部署模型的现有拆分中。

现在模型将使用以下内容进行训练和评估:

  • 训练(初始)+ 训练(新生产数据)

  • 测试(初始),测试(新生产数据)

注意,我们建议分别评估两个测试集,以确保你可以衡量不同分布的指标。这也将帮助你发现测试(初始)集的准确率是否发生了变化。

4: 我的模型在训练集和测试集上的准确率很高,但在部署后表现很差

这是每个机器学习模型都会面临的普遍问题。当你开始训练和评估模型时,你假设可用的训练集和测试集的分布与模型在部署时面对的情况相似。如果情况不是这样,你就不知道模型的表现如何。例如,一个专门在加州数据上训练和评估的交通标志检测模型,在部署到欧洲时可能会因为交通标志样式不同而表现很差。

确保模型在与其部署环境尽可能匹配的数据上进行训练和评估是至关重要的。避免这个问题的一种方法是持续收集数据,并仔细考虑数据收集策略,以减少任何领域差距。

为了更早发现模型泛化的潜在问题,你可以使用不同的训练测试拆分,例如基于城市而不是随机地划分训练和测试数据。

5: 训练准确率低,测试准确率低

如果训练和测试的准确率都很低,说明你的模型没有学到很多东西。模型没有改善的原因可能有多种:

  • 任务过于困难 — 根据现有的训练数据和模型的能力,解决这个问题太困难了。

  • 标签错误 — 即使数据本身非常有价值。没有正确的标签,模型无法学到任何东西。

我的模型任务是否过于困难?

在计算机视觉中,一个简单而有效的方法来确定任务是否最终太难解决,就是做“我能自己做吗”测试。给定几张带标签的训练图像和任务,你能否正确分类测试集中的图像?

如果是,你知道你可以解决它,并且数据可能足够好。检查模型是否有足够的训练数据来捕捉正确的信号。

注意:这在计算机视觉中效果特别好,因为人类在模式匹配方面非常出色。如果你处理其他数据类型,这个简单的测试可能不适用。

处理坏标签

在监督学习中,训练数据由样本(例如图像)及其对应的标签组成。即使数据集中的样本具有代表性和均衡性,不良标签仍然可能导致模型学习不到有用的东西。这就像在学校教一个孩子做数学,但使用的公式和例子是随机排列的。今天你学到 1+1=3,明天你学到 1+1=5。如果没有一致的教学模式,因为数据标注错误,我们就会遇到问题。

如果你有标签问题,可以做以下操作。从训练集中随机挑选一小部分(例如 100)样本,并手动检查潜在的标签错误。你可以对测试集做相同的操作。

拥有一些错误标签是不幸的非常常见。一些学术数据集,如 ImageNet 和 CIFAR10 的标签错误率约为 5%[4]。如果错误不是系统性的且错误率不是很高,它不应该有显著的影响。但如果你遇到系统性错误(例如,所有猫都标记为狗),你必须纠正它们。

如果你在处理大型数据集时,可能不想重新标注整个训练集/验证集和测试集。一种更直接的方法可能是纠正验证集和测试集的标签。拥有好的评估数据集是非常关键的。对于训练集,你可以使用诸如共教学[5]之类的方法来训练你的模型,即便是有噪声的标签。如今,许多模型还通过不需要标签的自监督学习方法进行预训练。因此,这些模型不会拾取训练数据中的系统性错误。但你仍然需要对这些模型进行适当评估,因此需要好的验证集和测试集。

希望你喜欢这篇文章。如果你有任何关于如何进一步改进本指南的建议,请随时联系我们或留下评论!

Igor Susmelj,

联合创始人 Lightly

[1] Atighehchian 等(2020),“用于生产的贝叶斯主动学习:系统研究和可重用库”

[2] Coleman 等(2020),“高效主动学习和稀有概念搜索的相似性搜索”

[3] Haussmann 等(2020),“面向目标检测的可扩展主动学习”

[4] Northcutt 等人 (2021), “测试集中的普遍标签错误使机器学习基准不稳定”

[5] Han 等人 (2018), “共同教学:在极度噪声标签下对深度神经网络的鲁棒训练”

数据民主化:大型公司采纳的 5 种“人人数据”策略

原文:towardsdatascience.com/data-democratisation-5-data-for-all-strategies-embraced-by-large-companies-e62fad52c250

这关乎数据、技能、工具……特别是文化!

Col JungTowards Data Science Col Jung

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

--

图片由Windows(Unsplash)提供

2006 年,《哈佛商业评论》发表了一篇题为“竞争于分析”的文章。

由学者托马斯·达文波特和珍妮·哈里斯撰写的这篇具有影响力的文章引发了关于如何利用分析作为商业竞争优势的广泛讨论。

公司开始投资于 BI 软件、大数据平台、数据科学团队和尖端的AI 和机器学习工具,希望成为数据驱动型公司。

结果令人失望。

一项德勤调查显示,14 年后只有 10%的公司在分析洞察方面具有竞争力。大多数公司只能声称拥有孤立的分析卓越领域。而最受欢迎的分析工具是,鼓声……

……微软 Excel

事实是,转型为数据驱动型组织比看起来要困难得多。

请关注我的分析和数据 YouTube 频道 这里

你们公司处于什么位置?图片由作者提供

能够大规模地利用数据驱动的洞察,并将其融入到日常决策中,需要在多个领域达到较高水平的企业数据成熟度

  • 数据: 如果你没有优质数据,AI 就无从谈起。

  • 技能: 你的员工整体上是否具备数据素养?

  • 工具: 你的基础设施是否为大规模分析做好了准备?

  • 文化: 这是最大的障碍。你的公司是否存在对数据驱动洞察抗拒的传统文化?这是一个严重的问题。

我所在的公司,一家‘四大’银行我在这里工作的过去五年作为工程师和数据科学家,是数据成熟度量表上的 2.5 分。我们正在努力达到数据驱动的 4 分,将我们置于行业领先的‘数字原生’公司边缘。(加油团队!)

全球范围内,平均公司数据成熟度大约为 2.2,参考国际高级分析研究所

这意味着只有少数员工具备超越电子表格的分析技能。

数据民主化弥合了数据素养的差距。图片由作者提供

解决方案似乎很明确。

为了铺垫迈向数据驱动的步伐,企业需要推动数据成熟度。

那么,如何推动数据成熟度呢?

数据民主化问好,这是一种被全球公司采纳的方式。

数据民主化是一种数据为所有人的全员参与精神,旨在提升公司各个角落的数据成熟度

例如,我的银行并没有扶持只能由少数特权且高度专业化的数据科学团队使用的专门工具,而是投资于提升数据、技能、工具和文化,以赋能所有 40,000 名同事……

  • 自助获取可靠的数据;

  • 自动化掉他们日常工作中的繁琐事务;

  • 拥抱数据驱动的洞察和决策,而不是‘直觉’。

如果每个人仅通过自动化一个简单的流程每周节省一个小时,我的银行每年将节省 200 万小时,相当于大约 1.5 亿美元,这些钱可以用于其他地方。

这其中蕴含着一个重要的教训。

数据民主化认识到繁琐事务大胆创新分析项目同样重要。

我们最优秀的数据科学家(公司 1%)的尖端 AI 和机器学习项目值得庆祝。

同样值得庆祝的是我们日常知识工作者和公民分析师的快速胜利。

通过为企业分析和数据科学建立一个坚实的大规模基础,数据民主化有望解决大多数公司迄今为止失败成为数据驱动的根本原因。

那么,数据民主化在实践中究竟是什么样的呢?

我会讲到五种策略,并结合我自己的经验。

前三种代表了工具的进步,而最后两种则关注数据、技能和文化领域的进展。

1. 全能分析与自动机器学习平台

2020 年代见证了低代码一站式分析与机器学习平台的兴起,它们将三种主要的企业问题解决方法整合在一个平台下:

  1. 可视化;

  2. 分析;

  3. 自动化。

最受欢迎的平台包括:

  • Alteryx,被全球最大 2000 家公司中的一半使用;

  • Dataiku,一家由谷歌投资部门支持的法国独角兽;

  • DataBricks,它将大数据计算和机器学习统一在一个平台下;

  • Snowflake,一个快速的云数据仓库颠覆者。

这是他们的用户界面:

流行的全能分析和机器学习平台。图片由作者提供

我的银行目前使用AlteryxDataiku

这些平台使我们的财务团队能够在几分钟内建立审计管道。只需将一些数据源拖放到屏幕上,使用几次点击按钮将它们连接起来,然后设置自动化,当出现红旗时通过电子邮件通知你。

不需要电子表格、SQL 或 Python。

不需要在不同程序之间跳转。

不需要对自动化管道进行繁琐的测试以查看它们是否按预期工作。

一切正常运作。

在一个全能平台上拥有所有这些功能可以产生额外的协同效应。

假设我的经理让我深入探讨客户或员工流失——这是一个经典的商业问题。在这些现代平台中,我可以……

  • 从一些可视化开始,尝试发现有趣的趋势。很好。如果我没有太大运气怎么办?

  • 不用担心,该进行一些分析。我可以原型化预测模型,以揭示数据中流失的潜在驱动因素。结果显示每周工作超过 48 小时的人辞职的几率显著增加。很好的洞察,现在我们有一个商业决策——招聘更多的人分担工作,或者识别出有辞职风险的人并及时阻止。

  • 我可以非常迅速地设置一个自动化,当数据显示有人工作过度时,通过电子邮件通知我(或人力资源部)。

在企业问题解决中,你永远不知道哪种方法——可视化、分析或自动化——效果最好。

在这里,自动化实际上是最有影响力的,因为它标记出风险员工,使得立即采取行动成为可能,从而节省了大量的后续开支。人力资源部会告诉你,替换失去的员工花费很多钱

但确实,达到目标之前需要一些探索。分析通常不是线性的。能够在一个地方完成所有工作,并推动数据和结果——通常无需编码——让生活变得轻松许多。

对于公民分析师和数据科学家来说,这无疑是一个极具赋能性的提议。

2. 综合生产力与分析生态系统

该策略审视了一组联合的应用程序和平台,它们在一个生态系统下紧密而协同地集成在一起。

最好的例子是微软。

对于企业生产力,业界标准已经是WindowsOffice 365一代:

  • Outlook 用于日历和邮件;

  • TeamsYammer 用于聊天和社区;

  • OneDriveSharePoint 用于文件存储;

  • WordExcel用于日常工作。

这些基础工具现在是更广泛的Microsoft 365生态系统的一部分,反映了微软提供统一生产力 分析平台的战略。

公式中的分析部分是微软Power Platform,这是一个低代码应用程序家族,包括……

  • Power BI — 创建数据模型和惊艳的可视化,无需编码

  • Power Apps — 创建桌面和移动应用,几乎无需编码

  • Power Automate — 自动化工作流和任务,几乎无需编码

  • Power Virtual Agent — 构建自己的聊天机器人,几乎无需编码

他们用户界面的预览:

制作可视化,构建应用程序和自动化工作流。图片由作者提供

‘民主化’来自两个方面。

首先,这些工具易于使用,并且每个月变得越来越容易。易用意味着采用,正如任何商界人士、企业家和 UX 设计师会告诉你的那样。

其次,这些 Power Platform 应用程序的集成彼此 以及微软更广泛的核心生产力应用程序,如 Outlook 和 Teams,至关重要,以使每位员工能够为自己和组织创造价值。

比如说,审查和批准请求的繁琐过程,通常通过无尽的来回邮件和手动流程处理。这是大多数组织中的主要问题源。

够了!厌倦了每次都花几个小时审查,比如数据请求?

拥有数据的公民分析师可以构建一个Power BI报告,提供有关他们拥有的数据资产的信息,并确定这些数据是否可以与同事共享。

他们可以将这些数据驱动的洞察直接嵌入到Power Apps中构建的应用程序中,该应用程序作为同事申请访问的前端。(此外,由于微软与大型企业基础设施的成熟集成,这个应用程序会立即投入生产——数据治理人员的喘息!)

现在,当同事通过应用提交他们的数据请求时,数据所有者将通过Outlook 收到自动请求的电子邮件,甚至通过 Microsoft 的Approvals 应用在Teams上直接收到通知,因为这些自动化设置在Power Automate中完成,几乎无需编码。

我们谦虚的公民分析师将一个每周占用 5 小时的繁琐任务转变为一个现在只需几分钟的自动化管道,从而释放出宝贵的时间用于更具生产力的工作。

(或者也许早下班……!)

3. 成熟的大数据工具

过去五年中,全球企业对大数据和数据湖的接受在很大程度上未能兑现其炒作的承诺。

由于粗糙的工具、数据质量问题、夸大的承诺和可扩展性挑战,解锁数据湖中所有数据潜在价值的必要手段和员工面临的现实之间的差距实在太大。

在我的银行,我们最初的 Apache Zeppelin 笔记本难以使用。Apache Atlas 上的元数据对于非数据工程师来说难以导航。数据通常是按项目基础摄取的,并且经常不完整,需要与我们的数据仓库资产集成——这对喜欢使用未处理数据进行处理和建模的数据科学家来说是一个不理想的情况。

由于许多数据资产未被使用,数据质量问题堆积如山。

数据湖中的水变得陈旧。

我们后来将更好的工具,如Power BI和管理机器学习的平台如Dataiku,接入到我们的数据湖基础设施中,但奇怪和低效的问题仍然存在,就像 PC 制造商必须将来自不同供应商的硬件和软件拼凑在一起所遇到的挑战一样。

如果有一种提供“苹果般”体验的大数据解决方案就好了:硬件和软件完美同步,数据天作之合。

那个时候就是现在。

我们的数据湖现在托管在Microsoft Azure云端,为我们提供了弹性计算和超大规模能力。此外,鉴于许多组织在 2015 年至 2020 年间开始了大数据之旅,我们正在逐步放弃 Apache Hadoop 技术栈,转而使用完全原生的 Azure 解决方案。

全程 Azure。

这包括Azure Synapse,用于无缝的数据集成和分析,将数据仓库和大数据整合在一个平台下。此外,我们还使用Azure Purview以确保全面和简化的数据治理体验。

Azure 原生的数据分析、集成和治理工具。图片由作者提供

当然,微软只是主要的云供应商之一。其他包括:

  • 亚马逊网络服务(AWS);

  • 谷歌云平台(GCP);

  • 阿里巴巴云。

所有这些巨头每年投入数十亿美元来改善他们的云基础设施、产品和服务。

简而言之——将数据、分析工具来处理数据、集成工具来结合数据和治理工具来管理数据——都集中在一个平台上,创造了 2023 年尽可能无缝的大数据体验。

4. 文化与教育……文化与教育……

我们刚刚看到三种专注于工具技术进步的策略。

我有一些可能让人有点惊讶的消息:

成为数据驱动的最大挑战不是技术。

关键在于人、流程和 文化

你可以拥有世界上最好的技术栈,但一个抵触数据驱动洞察的传统文化将阻碍你的数据成熟之路。这是致命的。

生活的现实是人们抗拒变化

这就是为什么通过提供充足的机会来提升技能和建立数据驱动的社区至关重要。

我的银行刚刚推出了一项企业范围的数据与数字赋能计划,提供给所有 40,000 名员工学习各种主题的机会,从数据流畅性到数据领导力,通过为期 8 周的课程,包括讲座、大师班、播客和由内部专家和行业人物主讲的视频。

我们还举办了一年一度的TechX大会,展示新兴技术,以及一个数据周活动,包含一整周令人兴奋的数据相关演讲。

而且猜猜看?今年的数据周主题是数据为所有人

每一个演讲都融入了数据民主化的元素。我们不惜一切代价邀请了来自数据领导者如亚马逊、AlteryxDataiku微软谷歌等的高管与我们的员工交流。

听着,我们自己的数据架构主管装扮成不可思议的“数据英雄”,强调数据是每个人的

我们公司的年度数据周大会

一个数据驱动的组织需要持续地提升和赋能其员工,这需要强大的文化、强大的社区和高层领导的强力倡导。

没有绕过的办法。

5. 数据市场

最后,让我们谈谈支持分析的数据本身。

寻求数据驱动洞察的讽刺在于,数据在大多数公司中处于糟糕的状态,主要表现为三个问题:

  • 缺乏可见性。这些数据到底在哪里?

  • 缺乏信任。不可靠的数据意味着不可靠的洞察。

  • 缺乏时效性。需要一些数据迅速得到?太遗憾了。

数据在不断流动,但就像一个谜团——没有人能可靠地确定具体内容:它包含了什么,去向何处,源头在哪里,或者谁拥有这些数据。更别提数据质量以及它如何与其他数据连接了。在尝试执行数据战略以成为数据驱动型公司时,这些痛点从一开始就成为严重的障碍。

正如我在这篇文章中所写的企业数据架构:

“几十年的数据仓库让组织淹没在一片由混乱的数据管道连接的数据系统的海洋中。魔法般的解决方案本意是将数据集中到一个中央库中。不幸的是,数据湖的梦想在许多组织中变成了数据湖的沼泽。”

企业长期以来以项目为导向来处理数据,这导致业务团队在需要解决某些问题时创建孤立的数据管道。将数据管道整合到一个集中化的数据团队中,在一个大型数据湖中迅速遇到了瓶颈,导致数据海洋泛滥,难以访问、理解,并且充满了持久的数据问题。

这对全球首席数据和分析官(CDAO)来说是令人沮丧的事情。

在 2020 年代,已经取得了显著的进展来克服这些挑战。

公司正在投资于一种去中心化的数据网格架构,这种架构使各个业务单元能够自豪地打造可重用且可信赖的 数据产品,这些数据产品可以在整个数据网格中无缝共享。

这些数据产品——就像任何从货架上取下的骄傲产品一样——可以由知识工作者进行选购和自助服务,促进了所有战略企业数据资产的最高可见性。

在我的银行中,正在构建一个NetflixYouTube 风格的数据市场,使员工能够自助服务精选的数据产品和其他数据资产……并且发布他们自己的资产。

将数据视为一种产品,使数据成为一级公民。图片由作者提供

凭借一个经过精心打磨的用户界面,该界面整合了社交元素,并快速提供元数据、数据来源和所有权信息等关键信息,我们正在见证一个集中化且精致的一站式数据商店的兴起,使组织中的每个人都能够轻松地获取和发布数据。

就像伊斯坦布尔的 16 世纪大巴扎,被视为当时的亚马逊市场,吸引了来自世界各地的商人,企业数据市场使数据民主化,面向所有人开放。

突然间,数据资产变得可发现、快速且易于访问,且值得信赖。这大大降低了推动数据洞察、智能决策和先进分析能力(如机器学习和人工智能)的成本。

很棒的东西。

未来会怎样?一些结束语

最具影响力的分析历史上通常来自具备一点分析技能的领域专家

在 1850 年代,约翰·斯诺 使用地理空间地图来分析数据,并说服科学家和政策制定者霍乱的传播与污水源有关。

Snow 不是‘分析师’或‘数据科学家’。他是医生。

在同一时期,弗洛伦斯·南丁格尔 运用了类似现代 A/B 测试的技术,并利用饼图等可视化手段揭示影响战地死亡率的潜在因素。

今天被公认为统计学的先驱,她开创性的工作奠定了我们今天遵循的众多卫生实践的基础,例如洗手和戴口罩。

南丁格尔不是统计学家。她是护士。

170 多年后,现代企业正在被提醒,数据和分析的民主化对于实现数据驱动的成功是一个前提条件。

这种胜利的心态间接解决了许多问题。

例如,识别和解决大规模数据质量问题的最佳方法是对这些数据进行运用。通过将数据访问的民主化超越少数特权者,企业正在创造出一个提升自身数据质量的绝佳配方。

换句话说,赋予公民数据分析师在公司数据湖中畅游的能力,他们清理的垃圾越多,就会激励更多同事参与其中。

数据民主化启动了一个成功的良性循环。

随着时间的推移,多方面的数据成熟度——数据、技能、工具和文化的同步提升,将最终使企业更接近于成为 4 级或 5 级分析驱动的强大企业的梦想。

未来会怎样?

技术领域正被生成性人工智能剧烈震动。

我预计大型语言模型将在当前工具之上添加一个抽象层,进一步简化各行各业人士利用分析和数据驱动洞察的用户体验。

历史上,技术进步一直关注于抽象层。

在过去,计算机先驱们深陷于汇编代码中。你必须编写这些代码来计算 1 + 1:

section .data
    result db 0  ; Variable to store the result

section .text
    global _start

_start:
    mov al, 1    ; Move 1 into AL register
    add al, 1    ; Add 1 to AL

    mov [result], al  ; Store the result in the 'result' variable

    ; Exit the program
    mov eax, 1   ; System call number for exit
    xor ebx, ebx ; Exit status 0
    int 0x80     ; Invoke the kernel

C++、Java 和 Python 等高级编程语言在 1980 年代之后出现,使我们的生活轻松了很多。到 2012 年,统计学家现在可以在‘笔记本’中原型制作常用的机器学习模型,而无需太多编程经验:

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

iris = load_iris()
X = iris.data
y = iris.target

# Train a logistic regression model
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = LogisticRegression()
model.fit(X_train, y_train)

# Make some predictions and evaluate
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy)

在 2023 年,像Power BITableau这样的企业工具,以及像AlteryxDatabricksDataiku这样的托管机器学习平台,可以通过点击几下按钮生成强大的分析。

微软推出了Fabric,一个统一了数据仓储、大数据、数据工程和管理的SaaS 云分析平台

生成式 AI 会革新分析吗?图像作者提供

几十年后,专用分析平台可能会成为历史遗迹。

只需简单地提示你想要什么,然后看奇迹发生:

“在我的数据集中寻找相关性。”

“运行压力测试以模拟 5 个基点的利率上升。”

“预测未来 5 年的当前销售预测。”

“分析数据质量并给我展示报告。”

我能说什么——现在正是分析和 AI 领域的激动人心的时刻。

Twitter 和 YouTube 这里这里这里 找到我。

我的热门 AI、ML 和数据科学文章

  • AI 与机器学习:快速入门 — 这里

  • 机器学习与机制建模 — 这里

  • 数据科学:现代数据科学家的新时代技能 — 这里

  • 生成式 AI:大型公司如何争先恐后地采用 — 这里

  • ChatGPT & GPT-4: OpenAI 如何赢得 NLU 战争 — 这里

  • GenAI 艺术:DALL-E、Midjourney 和 Stable Diffusion 解析 — 这里

  • 超越 ChatGPT: 寻找真正智能的机器 — 这里

  • 现代企业数据策略解析 — 这里

  • 从数据仓库与数据湖到数据网格 — 这里

  • 从数据湖到数据网格:最新架构指南 — 这里

  • Azure Synapse Analytics 实战:7 种用例解析 — 这里

  • 云计算基础:利用云计算为您的业务服务 — 这里

  • 数据仓库与数据建模 — 快速入门课程 — 这里

  • 数据产品:为分析建立坚实的基础 — 这里

  • 数据民主化:5 种“数据普及”策略 — 这里

  • 数据治理:分析师的 5 个常见痛点 — 这里

  • 数据讲故事的力量 — 销售故事,而非数据 — 这里

  • 数据分析入门:谷歌方法 — 这里

  • Power BI — 从数据建模到惊艳报告 — 这里

  • 回归分析:使用 Python 预测房价 — 这里

  • 分类:使用 Python 预测员工流失 — 这里

  • Python Jupyter Notebooks 与 Dataiku DSS — 这里

  • 常见机器学习性能指标解析 — 这里

  • 在 AWS 上构建 GenAI — 我的初次体验 — 这里

  • 数学建模与机器学习在 COVID-19 中的应用 — 这里

  • 工作的未来:在 AI 时代您的职业安全吗 — 这里

数据文档 101:为何?如何?为谁?

原文:towardsdatascience.com/data-documentation-101-why-how-for-whom-927311354a92

数据管理

在你的组织内部建立完整且可靠的数据文档的最佳实践

Marie LefevreTowards Data Science Marie Lefevre

·发表于 Towards Data Science ·6 分钟阅读·2023 年 6 月 13 日

--

摄影师 Maksym Kaharlytskyi 的照片,来源于 Unsplash

我记得每次作为数据团队成员进入新公司时,我的主要关注点之一是:

我会找到完整且可靠的数据文档吗?如果找到的话,在哪里呢?

在与其他公司的同行以及不属于数据团队的同事交谈时,我发现许多利益相关者依赖某种数据文档,并希望找到一个可以提供公司所有数据相关知识的地方

在这个如梦似幻的愿景中,数据文档将是完整且可靠的,并且可以在适当的地点提供给合适的人。想象一下那种感觉会是怎样的!

作为我当前雇主的数据策略师,我的优先事项之一是使数据对所有部门可用。这意味着不仅要沟通数据团队在公司所取得的工作,还要让我的每一位同事(以及可能的外部利益相关者)了解我们作为一个组织拥有的数据资产。

为此,我建立了一个中央数据文档页面,同时为每类利益相关者提供了相应的衍生文档。为了让你了解它的样子,以下是我向所有人开放的中央数据文档页面的前几行:

中央数据文档页面(作者提供的图像)

你在以前或当前的公司是否遇到过类似的问题?你想开始或继续构建你公司的数据文档吗? 在本文中,我想与大家分享我从创建数据文档的想法到其具体实施所遵循的主要步骤。我希望你能觉得这些内容有用且适用。

现在让我们深入探讨一下你应该采取的必要步骤,以为自己和你的利益相关者创建完整可靠的数据文档

什么是数据文档?

这是一组关于公司数据资产的相关信息。 它包含了一系列元素,例如指标定义、数据源描述、数据模式、数据模型解释、数据工具的访问权限映射等。

没有通用的模板来创建数据文档。最重要的是数据文档服务于你的组织。把它想象成一本全面的手册,能够帮助利益相关者自信且清晰地浏览数据环境

为什么数据文档至关重要?

就像任何类型的文档一样,数据文档为你的公司提供了一种关于过去事件的记忆。书面文档不仅仅告诉你当前的情况。它还允许任何成员掌握数据管理的历史,无论他们在组织中待了多久。通过记录逐步建立的定义、规则和流程,你为任何有意使用数据的人员带来了清晰和背景

目前,建立和维护最新的数据文档可以将概念层面上决定的内容和技术实施的内容确立下来。将所有这些元素集中在一个文档中,无疑将利益相关者聚集在一起:在他们就数据管理达成一致后,记录下来能增加其可靠性

那么,你应该从哪里开始?

正如我们稍后将看到的,你的数据文档的格式可能会根据你的目标受众而有所不同。最初不要专注于文档的形式,而是关注其意图。然后,你将根据构建数据文档的意图来衍生文档的内容和形式

你可以从问自己以下问题开始:

  • 我的受众是谁?他们的技术背景是什么?

  • 他们对数据文档有什么期望?

  • 我希望我的受众了解关于数据的哪些信息?

  • 我的受众在深入细节之前应该知道哪些基本知识?

  • 在深入细节时,我应该深入到什么程度,以便让我的受众能够很好地理解而不会迷失?

谁是你的目标受众?

一般来说,我识别出三种类型的受众,你应该针对这些受众来编写数据文档。

首要受众是最接近你作为数据领导者的位置的人:即你的数据团队。在这个可能具有异质性的团队中,我们可以区分新成员和初级员工,以及更多的高级成员。因此,你的文档应包括基本概念和更高级的元素,如指标的详细计算方法。

排在第二的位置是业务用户。首先你要服务于那些日常使用数据的用户。他们的需求可能与另一个群体的需求略有不同:业务用户可能偶尔会有数据相关的问题,而且他们与你的工作关系不紧密。你的文档应对这两种类型的业务用户都要提供便利。

最后,其他利益相关者可能需要与公司数据资产相关的特定文档。在这一类别中,我包括了管理委员会、与其他部门的同事(你不一定与他们有互动),以及外部利益相关者。他们对数据文档的需求可能较小,但需要简洁和准确。

不同受众类型的地图(图像由作者提供)

数据文档应包括(或不包括)什么?

数据文档的内容将根据目标受众的不同而有所变化。

向所有类型的受众提供的共同元素包括关于数据团队的一般知识,如:

  • 数据团队的联系方式

  • 数据团队概述以及它如何与组织其他部分互动

  • 数据团队负责的数据资产

除了这些元素,业务用户需要与他们自己职责范围相关的文档,以及数据如何支持他们的日常活动。这些元素包括:

  • 常用术语的词汇表

  • 包含指标定义和计算方法的数据字典(可能简化)

  • 数据来源的列表以及它们在数据中心的利用方式

  • 可用的数据工具列表以及谁应该有权限使用它们

  • 最常用的仪表板列表、其内容、拥有者

  • 如何使用数据工具的教程(如相关)

最后,数据团队成员需要更加具体的文档,包括技术元素,例如:

  • 数据架构和数据模型的架构图

  • 包含指标定义和计算方法的数据字典

  • 所有数据工具中使用的命名规范

  • 数据工具的风格指南

  • 你的仪表板设计系统(颜色、字体等)

  • 任何成员都应使用的模板(例如用于分享数据分析结果、向听众展示关键数据等)

  • 编码语言的最佳实践(SQL、Python、R 等)

简而言之,最好是只包含与每种受众相关的元素,以便他们能全面了解可用文档,而不会被不必要的文档元素淹没。

根据目标受众回顾数据文档的内容(图像由作者提供)

你应该如何以及在哪里提供数据文档?

至于形式,你应该适应目标受众使用的工具。他们是否将知识库集中在像 Notion 或 Confluence 这样的工具中?那么,你的数据文档就应该放在这里。他们是否更倾向于使用共享在 Google Drive 或 Sharepoint 服务器上的幻灯片?那么,你应该在这里展示数据文档的元素。

除了根据受众的习惯调整文档的形式和位置外,你还可以将一些关于数据的信息直接包含在你的数据工具中(例如,在数据仓库或数据转换工具中)。像数据目录和支持(元)数据管理的软件等新兴工具可以帮助你实现这一点。

总结

要建立完整且可靠的数据文档,请记住遵循以下步骤:

  1. 确保你了解数据文档为何至关重要

  2. 问问自己你的目标受众是谁——可能有多个受众群体,需求各不相同

  3. 决定你想在数据文档中包含哪些元素

  4. 选择合适的工具和合适的地方来创建和维护你的数据文档

这样你就准备好了!

数据驱动的调度

原文:towardsdatascience.com/data-driven-dispatch-76b7e998a7a7?source=collection_archive---------4-----------------------#2023-08-04

使用监督学习预测芝加哥车辆碰撞的服务呼叫

John LenehanTowards Data Science John Lenehan

·

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

--

Sawyer BengtsonUnsplash 的照片

介绍

在当今快节奏的世界中,数据驱动的调度响应系统变得越来越重要。调度员在接听电话时会进行一种分诊,基于严重性和时间敏感性等因素来优先处理案件。通过利用监督学习模型的力量,优化这一过程有很大的潜力,可以在与人工调度员的评估相结合的情况下,更准确地预测案件的严重性。

在这篇文章中,我将介绍我开发的一个解决方案,以改善对芝加哥汽车碰撞中伤亡和/或严重车辆损坏的预测。考虑了碰撞地点、道路条件、限速和发生时间等因素,以回答一个简单的是或否的问题——这次车祸是否需要救护车或拖车?

Chris Dickens提供的照片,来源于Unsplash

简而言之,这个机器学习工具的主要目标是基于其他已知因素,分类最可能需要呼叫(医疗、拖车或两者)的碰撞。通过利用这个工具,响应人员可以根据天气和时间等各种条件高效分配资源到城市的不同区域。

为了使这个工具准确有效,需要一个大型数据源来从历史数据中进行预测——幸运的是,芝加哥市已经有这样的资源(芝加哥数据门户),因此将使用这些数据作为测试案例。

实施这些预测模型无疑将提高处理城市街道碰撞时的准备性和响应时间效率。通过深入了解碰撞数据中的潜在模式和趋势,我们可以致力于创造更安全的道路环境,并优化应急服务。

我将在下面详细讨论数据清理、模型构建、微调和评估,然后分析模型的结果并得出结论。该项目的 github 文件夹链接,包括一个 jupyter notebook 和一个更全面的项目报告,可以在这里找到。

数据收集与准备

初始设置

我列出了项目中使用的基本数据分析库;标准库如 pandas 和 numpy 在整个项目中都有使用,以及用于可视化的 matplotlib 的 pyplot 和 seaborn。此外,我还使用了 missingno 库来识别数据中的缺失情况——我发现这个库在可视化数据集中缺失数据方面非常有用,并推荐在涉及数据框的数据科学项目中使用:

#generic data analysis 
import os
import pandas as pd
from datetime import date
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import missingno as msno

从机器学习模块 SciKit learn (sklearn)中导入了函数来构建机器学习引擎。这些函数在此处展示——我将在分类模型部分详细描述这些函数的用途:

#Preprocessing
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler

# Models
from sklearn.neighbors import KNeighborsClassifier

# Reporting
from sklearn.model_selection import train_test_split
from sklearn.model_selection import RandomizedSearchCV

#metrics
from sklearn.metrics import accuracy_score
from sklearn.metrics import f1_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score

这个项目的数据全部从芝加哥数据门户中导入,来源于两个渠道:

  1. 交通事故: 芝加哥地区车辆碰撞的实时数据集。该数据集的特征是碰撞发生时记录的条件,如天气条件、道路对齐、纬度和经度数据等细节。

  2. 警察巡逻区边界: 一个静态数据集,指示 CPD 巡逻区的边界;该数据集用于补充交通事故数据集的区信息。可以将其与原始数据集连接,以对事故最频繁的地区进行分析。

数据清理

导入两个数据集后,现在可以合并它们以将区数据添加到最终分析中。这是通过 pandas 中的 .merge() 函数完成的——我在两个数据框上使用了内连接,以捕捉两个数据框中的所有信息,使用两个数据框中的 beat 数据作为连接键(在交通事故数据集中列为 beat_of_occurrence,在警察巡逻区数据集中为 BEAT_NUM):

#joining collision data to beat data - inner join
collisions = collision_raw.merge(beat_data, how='inner',
                                 left_on='beat_of_occurrence',
                                 right_on='BEAT_NUM'
                                 )

从 .info() 函数提供的信息快速查看显示了几个数据稀疏的列。这可以通过 missingno 矩阵函数可视化:

#visualising missing data
#sorting values by report received date

collisions = collisions.sort_values(by='crash_date', ascending=True)

#plotting matrix of missing data
msno.matrix(collisions)
plt.show()

#info of sorted data
print(collisions.info())

这显示了所有列中的缺失数据矩阵,如下图所示:

未经精炼的数据集,多个列包含大量的空值

通过删除包含稀疏数据的列,可以提取一个更干净的数据集;要删除的列在列表中定义,然后使用 .drop() 函数从数据集中移除:

#defining unnecessary columns
drop_cols = ['location', 'crash_date_est_i','report_type', 'intersection_related_i',
       'hit_and_run_i', 'photos_taken_i', 'crash_date_est_i', 'injuries_unknown',
       'private_property_i', 'statements_taken_i', 'dooring_i', 'work_zone_i',
       'work_zone_type', 'workers_present_i','lane_cnt','the_geom','rd_no',
            'SECTOR','BEAT','BEAT_NUM']

#dropping columns
collisions=collisions.drop(columns=drop_cols)

#plotting matrix of missing data
msno.matrix(collisions)
plt.show()

#info of sorted data
print(collisions.info())

这导致了一个更干净的 msno 矩阵:

修剪后的数据集的 msno 矩阵

查看纬度和经度的数据,少量行有空值,而其他行错误地有零值(很可能是报告错误):

纬度和经度列都包含零值(查看每列的最小值和最大值)

这些会导致训练模型时出错,所以我将它们移除了:

#Some incorrect lat/long data - need to remove these rows
collisions = collisions[collisions['longitude']<-80]
collisions = collisions[collisions['latitude']>40]

数据充分清理后,我能够继续开发分类模型。

分类模型

探索性数据分析

在进行机器学习模型之前,需要进行一些探索性数据分析(EDA)——数据框的每一列都绘制在直方图上,使用 50 个箱子来显示数据的分布。直方图在 EDA 步骤中很有用,主要是因为它们提供了数据分布的概述,有助于识别异常值,并最终帮助做出特征工程的决策:

#plotting histograms of numerical values
collisions.hist(bins=50,figsize=(16,12))
plt.show()

最终数据集中列的直方图

粗略查看列的直方图显示,纬度数据是双峰的,而经度数据则是偏斜的。这需要标准化,以便更好地应用于机器学习目的。

未缩放的纬度-经度数据

此外,崩溃小时列似乎具有周期性特征——这可以通过三角函数(例如正弦)进行变换。

未缩放的崩溃小时数据

缩放与变换

缩放是一种在数据预处理中用于标准化特征的技术,使其具有相似的大小。这对于机器学习模型尤为重要,因为模型通常对输入特征的尺度很敏感。我定义了 StandardScaler() 函数作为此模型中的缩放器——这个缩放函数将数据转换为均值为 0 和标准差为 1 的形式。

对于具有偏斜或双峰分布的数据,可以使用对数函数进行缩放。对数函数使偏斜的数据更为对称,并减少数据中的尾部——这在处理异常值时非常有用。我以这种方式缩放了纬度和经度数据;由于经度数据都是负值,因此计算了负对数,然后进行了缩放。

#scaling latitude and longitude data
scaler = StandardScaler()

# Logarithmic transformation on longitude
collisions_ml['neg_log_longitude'] = scaler.fit_transform(np.log1p(-collisions_ml['longitude']).
                                                          values.reshape(-1,1))

# Normalisation on latitude
collisions_ml['norm_latitude'] = scaler.fit_transform(np.log1p(collisions['latitude']).
                                                      values.reshape(-1, 1))

这产生了期望的效果,如下所示:

缩放后的纬度-经度数据

相比之下,周期性数据通常使用三角函数(如正弦和余弦)进行缩放。根据早期观察,崩溃小时数据看起来大致是周期性的,所以我对数据应用了正弦函数——由于 numpy 的 sin() 函数以弧度为单位,我在计算输入的正弦值之前,首先将输入转换为弧度:

#transforming crash_hour 
#data is cyclic, can be encoded using trig transforms

#trig transformation - sin(crash_hr)
collisions_ml['sin_hr'] = np.sin(2*np.pi*collisions_ml['crash_hour']/24)

变换后的数据的直方图如下所示:

缩放后的崩溃小时数据

最后,我从模型中移除了未缩放的数据,以避免干扰模型预测:

#drop previous latitude/longitude columns
lat_long_drop_cols = ['longitude','latitude']
collisions_ml.drop(lat_long_drop_cols,axis=1,inplace=True)

#drop crash_hour column
collisions_ml.drop('crash_hour',axis=1,inplace=True)

数据编码

数据预处理的另一个重要步骤是数据编码——这是将非数值数据(例如类别)表示为数值格式的过程,以使其与机器学习算法兼容。在这个模型中,对于类别数据,我使用了一种叫做标签编码的方法——在将每个类别输入到模型之前,为列中的每个类别分配一个数值。此过程的示意图如下:

标签编码的一个示例(感谢 Zach M—来源

我对数据集中的列进行了编码,首先将要保留的列从原始数据集中分离出来,并制作了数据框(collisions_ml)的副本。然后,我在列表中定义了类别列,并使用 sklearn 的 LabelEncoder() 函数来拟合和变换这些类别列:

#segmenting columns into lists
ml_cols = ['posted_speed_limit','traffic_control_device', 'device_condition', 'weather_condition',
          'lighting_condition', 'first_crash_type', 'trafficway_type','alignment', 
           'roadway_surface_cond', 'road_defect', 'crash_type','damage','prim_contributory_cause',
          'sec_contributory_cause','street_direction','num_units', 'DISTRICT',
          'crash_hour','crash_day_of_week','latitude', 'longitude']
cat_cols = ['traffic_control_device', 'device_condition', 'weather_condition', 'DISTRICT',
           'lighting_condition', 'first_crash_type', 'trafficway_type','alignment',
           'roadway_surface_cond', 'road_defect', 'crash_type','damage','prim_contributory_cause',
           'sec_contributory_cause','street_direction','num_units']

#making a copy of the dataset
collisions_ml = collisions[ml_cols].copy()

#encoding categorical values
label_encoder = LabelEncoder()
for col in collisions_ml[cat_cols].columns:
    collisions_ml[col] = label_encoder.fit_transform(collisions_ml[col])

由于数据已经足够预处理,现在可以将数据拆分为训练数据和测试数据,并对数据进行分类模型拟合。

拆分训练和测试数据

在构建机器学习模型时,将数据分为训练集和测试集非常重要;训练集是初始数据的一部分,用于对模型进行正确响应的训练,而测试集用于评估模型性能。保持这两者分开是减少过拟合和模型偏差风险的必要措施。

我使用 drop() 函数分离出 crash_type 列(其余特征将用作预测 crash_type 的变量),并将 crash_type 定义为使用模型进行预测的 y 结果。使用 sklearn 的 train_test_split 函数将初始数据集的 20% 作为训练数据,其余部分用于模型测试。

#Create test set
#setting X and y values

X = collisions_ml.drop('crash_type', axis=1)
y = collisions_ml['crash_type']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

K-近邻分类

在这个项目中,使用 K-近邻(KNN)分类模型根据特征预测结果。KNN 模型通过检查未知数据点周围 K 个最近已知值的值来工作,然后根据这些“邻居”点的值对数据点进行分类。它是一种非参数分类器,这意味着它对底层数据分布没有任何假设;然而,它计算开销大,并且对数据中的异常值可能敏感。

我初始化了 KNN 分类器,初始 n_neighbors 设置为 3,使用欧几里得度量,然后将模型拟合到训练数据上:

#Classifier - K Nearest Neighbours
#instantiate KNN Classifier
KNNClassifier = KNeighborsClassifier(n_neighbors=3, metric = 'euclidean')

KNNClassifier.fit(X_train,y_train)

一旦模型拟合到训练数据上,我对测试数据进行了如下预测:

#Predictions
#predict on training set
y_train_pred = KNNClassifier.predict(X_train)

#predict on test data
y_test_pred = KNNClassifier.predict(X_test)

评估

机器学习模型的评估通常使用四个指标:准确率、精确率、召回率和 F1 分数。这些指标之间的差异非常细微,但用通俗的语言可以定义如下:

  1. 准确率: 真正例预测占所有模型预测的百分比。通常,应测量训练数据和测试数据的准确率以评估模型拟合情况。

  2. 精确率: 真正例预测占所有模型预测的百分比。

  3. 召回率: 真正例预测占所有数据集中正例的百分比

  4. F1 分数: 模型识别数据中正例能力的总体指标,结合了精确率和召回率分数。

我使用以下代码片段计算了 KNN 模型的指标——我还计算了模型在训练集和测试集上的准确性差异,以评估拟合情况:

#Evaluate model
# Calculate the accuracy of the model

#calculating accuracy of model on training data
train_accuracy = accuracy_score(y_train, y_train_pred)

#calculating accuracy of model on test data
test_accuracy = accuracy_score(y_test, y_test_pred)

#computing f1 score,precision,recall
f1 = f1_score(y_test, y_test_pred)
precision = precision_score(y_test,y_test_pred)
recall = recall_score(y_test,y_test_pred)

#comparing performances
print("Training Accuracy:", train_accuracy)
print("Test Accuracy:", test_accuracy)
print("Train-Test Accuracy Difference:", train_accuracy-test_accuracy)

#print precision score
print("Precision Score:", precision)

#print recall score
print("Recall Score:", recall)

#print f1 score
print("F1 Score:", f1)

KNN 模型的初始指标如下:

KNN 模型在第一次迭代中的指标

模型在测试准确率(79.6%)、精确度(82.1%)、召回率(91.1%)和 F1 分数(86.3%)上表现良好——然而,测试准确率远高于训练准确率,达到了 93.1%,差异为 13.5%。这表明模型存在过拟合现象,这意味着它在对未见数据进行准确预测时会遇到困难。因此,需要调整模型以获得更好的拟合——这可以通过一种称为超参数调整的过程来完成。

超参数调整

超参数调整是选择机器学习模型最佳超参数集的过程。我使用了k 折交叉验证对模型进行了微调——这是一种重采样技术,将数据拆分为k个子集(或),然后每个折轮流用作验证集,而其余数据用作训练集。这种方法有效地降低了由于特定训练/测试集选择引入模型偏差的风险。

KNN 模型的超参数包括邻居数量(n_neighbors)和距离度量。有多种方法可以在 KNN 分类器中测量距离,但在这里我专注于两个选项:

  1. 欧几里得: 这可以被认为是两点之间的直线距离——它是最常用的距离度量。

  2. 曼哈顿: 也称为“城市街区”距离,这是两个点之间坐标绝对差异的总和。如果你想象站在城市建筑的一个角落并试图到达对角线的另一角——你不会穿过建筑物,而是先上一个街区,然后横穿一个街区。

请注意,我也可以对权重参数进行微调(该参数决定了所有邻居是否平等投票,或者较近的邻居是否被赋予更高的重要性),但我决定保持投票权重均匀。

我定义了一个参数网格,包含n_neighbors值为 3、7 和 10,以及欧几里得或曼哈顿度量。我然后实例化了一个RandomizedSearchCV算法,将 KNN 分类器作为估计器,并传入参数网格。我将算法设置为将数据拆分为 5 折,通过将cv参数设置为 5;然后将其拟合到训练集上。以下是相关代码片段:

#Fine tuning (RandomisedSearchCV)
# Define parameter grid
param_grid = {
    'n_neighbors': [3, 7, 10],
    'metric': ['euclidean','manhattan']
}

# instantiate RandomizedSearchCV
random_search = RandomizedSearchCV(estimator=KNeighborsClassifier(), param_distributions=param_grid, cv=5)

# fit to training data
random_search.fit(X_train, y_train)

# Retrieve best model and performance
best_classifier = random_search.best_estimator_
best_accuracy = random_search.best_score_

print("Best Accuracy:", best_accuracy)
print("Best Model:", best_classifier)

从算法中检索到最佳准确率和分类器,表明分类器在使用曼哈顿距离度量时,n_neighbors设置为 10 的效果最佳,这将导致准确率为 74.0%:

交叉验证的结果——随机搜索分类器推荐使用n_neighbors=10,采用曼哈顿距离度量

因此,这些参数被输入到分类器中,模型被重新训练:

#Classifier - K Nearest Neighbours
#instantiate KNN Classifier
KNNClassifier = KNeighborsClassifier(n_neighbors=10, metric = 'manhattan')

KNNClassifier.fit(X_train,y_train)

性能指标再次从分类器中提取,方式与之前相同——以下是此迭代的指标截屏:

调整后的 KNN 模型指标

交叉验证导致所有指标的结果略有下降;测试准确率下降了 2.6%,精确度下降了 1.5%,召回率下降了 0.5%,F1 分数下降了 1%。然而,训练与测试准确率的差异降至 3.8%,而最初为 13.5%。这表明模型不再过拟合数据,因此更适合预测未见过的数据。

结论

总结来说,KNN 分类器在预测碰撞是否需要拖车或救护车方面表现良好。模型首次迭代的初步指标令人印象深刻,但测试和训练准确率之间的差距表明存在过拟合。超参数调整使得模型得以优化,从而显著减少了两个数据集之间的准确率差距。虽然在此过程中性能指标有所下降,但模型更好的拟合度的好处超越了这些问题。

参考文献

  1. Levy, J. (无日期)。交通事故——事故[数据集]。取自芝加哥数据门户网站。网址:data.cityofchicago.org/Transportation/Traffic-Crashes-Crashes/85ca-t3if(访问日期:2023 年 5 月 14 日)。

  2. 芝加哥警察局(无日期)。边界——警察巡逻区(当前)[数据集]。取自芝加哥数据门户网站。网址:data.cityofchicago.org/Public-Safety/Boundaries-Police-Beats-current-/aerh-rz74(访问日期:2023 年 5 月 14 日)。

  3. Zach M. (2022)。“如何在 Python 中进行标签编码(附示例)。”[在线]。网址:www.statology.org/label-encoding-in-python/(访问日期:2023 年 7 月 19 日)。

数据工程:一级方程式启发的初学者指南

原文:towardsdatascience.com/data-engineering-a-formula-1-inspired-guide-for-beginners-5511488803ee

初学者数据工程用例词汇表

Matteo ConsoliTowards Data Science Matteo Consoli

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

--

一个开心的数据工程师在工作

你是一个数据工程新手,想了解更多关于现代数据基础设施的知识吗? 我敢打赌你是,这篇文章就是为你准备的!

在这本指南中,数据工程与一级方程式相结合。不过,我们会保持简单。

介绍

我坚信描述一个概念的最佳方法是通过示例,即使我的一些大学教授常说:“如果你需要一个例子来解释,那就说明你没理解。”

无论如何,我在大学课堂上没怎么用心听讲,今天我会用—猜猜是什么一个例子来引导你了解数据层。

业务场景与数据架构

想象一下:明年,网格上会有一个新团队,红色雷霆赛车,他们会联系我们(是的,我和你),以建立他们的新数据基础设施。

在今天的一级方程式中,数据是核心,远比 20 或 30 年前重要。赛车团队通过惊人的数据驱动方法不断提高性能,逐毫秒进行改进。

高级设计

这不仅仅关乎圈速;一级方程式是一个数十亿美元的行业。 提高粉丝参与度不仅仅是为了娱乐;让这项运动更具吸引力不仅仅是为了驾驶员的乐趣。这些活动创造了收入。

强大的数据基础设施是竞争 F1 行业的必备条件

我们将从三个经典层次开始,构建一个支持我们赛车团队的数据架构:数据湖、数据仓库数据集市

数据湖

数据湖将作为一个用于存储来自 Formula 1 生态系统的各种来源生成的原始和非结构化数据的库:来自汽车的遥测数据(例如每秒轮胎压力、速度、燃油消耗)、驾驶员配置、圈速、天气条件、社交媒体反馈、票务、注册参加营销活动的粉丝、商品购买等。

我们的整合数据湖可以存储各种数据:非结构化(音频、视频、图像)、半结构化(JSON、XML)和结构化(CSV、Parquet、AVRO)。

数据湖与数据集成

我们将面临的第一个挑战是将所有内容整合到一个地方。 我们将创建从营销工具中提取记录的批处理作业,并且还将处理实时流式遥测数据(可以确定,这将有非常低的延迟要求)。

我们将有一长串需要集成的系统,每个系统将支持不同的协议或接口:Kafka 流、SFTP、MQTT、REST API 等。

在数据收集过程中,我们不会独自一人;幸运的是,市场上有数据集成工具可供采用,以配置和维护一个地方的摄取管道(例如按字母顺序排列:Fivetran、Hevo、Informatica、Segment、Stitch、Talend 等)。

与其依赖于数百个定时在crontab上运行的 python 脚本或定制的进程处理来自 Kafka 主题的数据流,这些工具将帮助我们简化、自动化和协调所有这些过程。

数据仓库

在定义了所有需要整合的数据流几周后,我们现在正在我们的数据湖中摄取各种显著的数据。是时候转向下一层。

数据仓库用于清理、结构化和存储来自数据湖的处理数据,为分析和报告提供结构化的高性能环境。

在这个阶段,我们不再关注数据摄取,而是将越来越多地关注业务用例。我们应考虑数据如何被同事们利用,提供定期更新的结构化数据集,涉及:

  • 汽车性能:遥测数据经过清理、标准化和整合,以提供统一的视图。

  • 策略和趋势审查:使用过去的比赛数据来识别趋势、驾驶员表现并理解特定策略的影响。

  • 团队 KPI:维修站时间、更换轮胎前的温度、汽车开发预算控制。

数据仓库与数据转换

我们将有大量管道专注于数据转换和规范化。 就像数据集成一样,市场上有许多产品可以简化和高效管理数据管道。这些工具可以简化我们的数据流程,降低运营成本,提高开发的效率(例如按字母顺序排列:Apache Airflow、Azure Data Factory、DBT、Google DataForm 等)

数据市场

数据仓库和数据市场之间有一条细微的界限。

别忘了我们为红雷赛车工作,这是一家大型公司,有数千名员工参与不同领域的工作。

数据必须可访问并根据特定业务部门的需求进行定制。数据模型围绕业务需求构建。

数据市场是数据仓库的专业子集,专注于特定的业务功能。

  • 汽车性能市场:研发团队分析与发动机效率、空气动力学和可靠性相关的数据。工程师将使用这个数据市场来优化汽车在不同赛道上的设置,或运行模拟以了解根据天气条件的最佳汽车配置。

  • 粉丝参与市场:营销团队分析社交媒体数据、粉丝调查和观众评分,以了解粉丝偏好。营销团队利用这些数据进行量身定制的营销策略、商品开发,并改善他们的 Fan360 知识。

  • 簿记分析市场:财务团队也需要数据(我相信有很多数字!)。现在,赛车团队比以往任何时候都更需要应对预算限制和规定。跟踪预算分配、收入和成本概况非常重要。

此外,通常需要确保敏感数据仅对授权团队可访问。 例如,研发团队可能需要独占访问遥测信息,并且需要使用特定的数据模型分析这些数据。然而,他们可能不被允许(或不感兴趣)访问财务报告。

数据市场与数据建模

我们的分层数据架构将使红雷赛车能够利用数据的力量进行汽车性能优化、战略决策、增强营销活动……以及更多!

就这样?

绝对不行!我们仅仅触及了数据架构的表面。还有数百个集成点我们需要考虑,而且我们只提到了数据 转换数据建模

我们完全没有涵盖数据科学领域,这可能值得单独写一篇文章,数据治理数据可观察性数据安全等也是如此。

不过,正如他们所说,“罗马不是一天建成的”。我们今天已经有很多工作要做,包括我们数据架构的初稿(见下文)。

数据架构 — 概述

结论

数据工程是一个神奇的领域,有大量的书籍专门讲述它。

在整个过程中,数据工程师将接触到无数的集成工具、多样的数据平台,旨在涵盖上述一个或多个层次(例如按字母顺序:AWS Redshift, Azure Synapse, Databricks, Google BigQuery, Snowflake, …),商业智能工具(例如 Looker, PowerBI, Tableau, ThoughtSpot, …)以及数据管道工具。

我们在Red Thunder Racing的数据工程之旅才刚刚开始,我们的工具箱中应该留有足够的灵活性空间!

数据层通常可以结合在一起,有时在一个平台上。数据平台和工具不断提高标准,逐步缩小差距,每天发布新功能。市场竞争非常激烈。

  • 你是否总是需要一个数据湖?这要看情况。

  • 你是否总是需要尽快存储数据(即流处理和实时处理)?这要看情况,业务用户对数据的新鲜度要求是什么?

  • 你是否总是需要依赖第三方工具来管理数据管道?这要看情况!

  • <Placeholder for any other question you might have>这要看情况!

如果你有任何问题或建议,请随时在LinkedIn上联系我。我保证会回答一些不同于:这要看情况!的内容。

本文所表达的观点仅代表我个人,未必反映我雇主的观点。除非另有说明,所有图片均为作者所拍摄。

本文中的故事及所涉及的所有名称均为虚构。与实际地点、建筑物和产品的任何联系纯属巧合,不应被推断。

数据工程书籍

原文:towardsdatascience.com/data-engineering-books-f373005d53fc

逐步学习数据工程的读者文摘

💡Mike ShakhomirovTowards Data Science 💡Mike Shakhomirov

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

--

照片由 Tamas Pap 拍摄,来源于 Unsplash

在这个故事中,我想谈谈那些可能对学习数据工程(DE)的人感兴趣的数据工程书籍和资源。我发现市场上很少有书籍能全面系统地解释数据工程这一概念。其中一些书籍在如何使用特定工具和数据平台架构方面很出色,而一些书籍则是我最喜欢的睡前读物:读着非常容易入睡,且极为无聊。还有些书籍适合战略决策,还有些虽然看起来有些过时但仍然有用。希望你会觉得有趣。

免责声明:此帖可能包含附属链接,这意味着如果你决定通过我的链接购买商品,我会获得佣金,但对你没有任何费用。

1. 使用 Python 进行数据工程

使用 Python 处理大量数据集以设计数据模型并自动化数据管道

Paul Crickard, 2020

这是一本适合那些希望学习开源 Apache 工具用于数据工程的书。它涵盖了数据工程的所有关键主题,如数据建模,并提供了大量最常见数据转换的示例。正如书中描述的,它涉及 Python 和数据建模,因此读者将重点关注 ETL 技术,以使用 Python 工具提取、清洗和丰富数据集。它详细解释了 Apache Kafka 和 Apache Spark,但也涵盖了处理文件格式、数据转换和清洗的基础知识。这本书提供了对数据管道部署以及数据环境工作的非常有用的观点。

我与先进 ETL 技术的故事,以补充这本书:

## 数据工程师的 Python

面向初学者的高级 ETL 技术

towardsdatascience.com

2. 数据工程基础

作者:乔·瑞斯,马特·豪斯利

发布日期:2022 年 6 月

出版社:O’Reilly Media, Inc.

总体而言,这是一本非常好的书,我相信它是我目前正在编写的书籍的最接近匹配。它涵盖了基础知识,确实非常棒。然而,它没有解释如何成为数据工程师。根据这本书,没有捷径,也没有简单的方式进入这个角色。读者需要投入 2 到 3 年的时间来学习这个特定领域。

我喜欢这本书的是它提供了对技术和架构的独立视角。

在这里我们不会看到任何市场营销内容。它非常专注于第二章中的数据工程生命周期,并解释了从项目需求收集、管道设计到上线的最佳实践。

这本书完全关于 SQL 和 Python,以及如何使用它们解决实际的数据工程任务。第四章介绍了选择合适数据工程技术的框架,非常类似于我在之前的故事中提到的:

## 数据平台架构类型

它在多大程度上满足了你的业务需求?选择的困境。

towardsdatascience.com

总的来说,这是我最喜欢的书籍之一。它不仅涵盖了数据生成、ETL、汇总和清洗的复杂性,还有战略方面的重点,这可能对数据工程经理有用。

3. 数据仓库工具包:维度建模的权威指南

建模,第 3 版

作者:拉尔夫·金博尔,玛吉·罗斯

发布日期:2013 年

出版社:Wiley

我记得我在开始使用 Snowflake 时几年前就买了这本书。

这本书在 2013 年发布,至今仍适用于许多数据建模场景。

我喜欢这本书的一个特别之处是案例研究。它提供了来自不同行业(如零售、市场营销等)的 20 多个非常有用的场景。它帮助我在高级水平上理解了维度建模和数据仓库设计。基本上,它解释了你需要了解的有关事实表和维度表的一切,以及如何在数据仓库解决方案中运行 ETL。

即使现在读起来也非常有趣,可以见证数据仓库平台的发展。

4. 数据网格

作者:扎马克·德赫加尼

发布日期:2022 年

出版社:Wiley

关于数据网格原则的简介。数据网格和去中心化数据管理无疑是数据工程领域的主要趋势之一。

数据网格定义了当我们有不同的数据领域(公司部门)及其团队和共享数据资源的状态。

我之前在我的一篇关于现代数据工程的文章中写过这个话题。

## 现代数据工程

平台特定工具和高级技术

towardsdatascience.com

这本书适合那些希望了解数据网格设计、策略和架构的读者。书中以逻辑一致的方式解释了数据所有权模型,以超越传统的数据仓库方法,向去中心化和分布式数据平台迈进。

5. 数据管道速查手册:用于分析的数据移动和处理 第 1 版

作者:詹姆斯·登斯莫尔

格式:Kindle 版

发布于 2021 年 2 月

出版社:O’Reilly Media, Inc.

这是我最喜欢的数据管道书籍之一。某些Python 和 SQL 代码片段在我的职业生涯中曾非常有用。书中的 Github 代码库演示了如何从外部数据源提取数据并将其转换为数据集。

这本书介绍了一种“构建与购买”方法,这正是数据工程师需要做的。实际上,目前市场上有许多托管的 ETL 解决方案,例如 Stitch、Fivetran 等。书中涵盖了数据管道设计原则,并解释了如何创建强健的数据处理以实现成功的分析。书中从架构角度解释了数据管道设计的许多关键点,同时还涉及现代数据基础设施、数据管道监控和警报等方面。我记得我写过一篇关于数据管道设计模式的文章,提供了类似的观点,并专注于选择合适工具的策略。

## 数据管道设计模式

选择合适的架构和示例

towardsdatascience.com

6. 现代数据平台架构:大规模企业 Hadoop 指南

作者:扬·库尼克、伊恩·巴斯、保罗·威尔金森、拉斯·乔治

发布于 2019 年

出版社:O’Reilly Media, Inc.

这本书在解释 Hadoop 技术方面非常出色。尽管这种技术在中小企业层面并不太流行,但它认为企业级应用仍然可行。读起来很有趣,重点关注实际用例,创建云端和本地的大数据基础设施。我相信它将对负责创建企业级云管道并确保高水平安全性和可用性经验丰富的数据工程师有帮助。

这不是我经常阅读的书籍,但仍然有用,因为它提供了一个被认为早已过时的概述。知道 Hadoop 仍然存在真是太好了。

7. Spark: 权威指南:简化的大数据处理 第一版

作者:Bill Chambers, Matei Zaharia

发布于 2018 年

出版社:O’Reilly Media, Inc.

这是我在大数据管道中的 ETL 方面的最爱之一。我们都喜欢 Spark,因为它的前所未有的可扩展性和成本效益。这是一本很好的书,适合想要学习数据湖中可扩展数据处理的初学者和中级用户。它涵盖了一些基本的数据工程概念和使用 Apache Spark 的数据湖数据处理。Apache Spark 被用于许多云产品中,例如 AWS Glue。这使得这本书成为有志数据工程师的绝佳选择。

8. 流处理系统: 大规模数据处理的“什么、哪里、何时以及如何” 第一版

作者:Tyler Akidau, Slava Chernyak, Reuven Lax

发布于 2018 年

出版社:O’Reilly Media, Inc.

一本关于最流行的数据管道设计模式之一——流处理的好书。它解释了流数据处理管道及其核心原理。对于数据工程师来说,理解数据管道设计模式的本质并正确应用它们,如批处理数据处理、流处理 ETL 等,非常重要。由于流处理,应用程序可以对新的数据事件作出即时响应。

流处理是企业数据的“必备”解决方案。

这本书在选择正确的数据处理方法和创建接近实时分析管道方面帮助了我很多。通常情况下,流处理并非必需,最终可能会成为一个昂贵的解决方案。

9. 数据讲故事: 商业专业人士的数据可视化指南 第一版

作者:Cole Nussbaumer Knaflic

发布于 2015 年

出版社:Wiley

一本关于数据可视化技术和商业智能(BI)的好书。尽管 BI 是数据工程的重要部分(反之亦然),但这本书不是职业指南。它解释了数据工程如何补充商业智能。它展示了如何以信息丰富、引人入胜的方式传达数据洞察。这对我的仪表板设计帮助很大。我要把它添加到我的书架上。

10. 流畅的 Python: 清晰、简洁且高效的编程 第二版

作者:Luciano Ramalho

发布于 2022 年

出版社:O’Reilly Media, Inc.

另一本我非常珍视的实用 Python 书。Python 是数据工程的重要组成部分,这使得这本书极具实用性。这本书分为五个部分,涵盖了数据工程师在数据管道中可能需要使用的几乎所有内容,即上下文管理器、装饰器、生成器和异步编程。

## 数据工程师的 Python

初学者的高级 ETL 技术

towardsdatascience.com

11. 每个数据工程师都应该知道的 97 件事:专家的集体智慧

作者:托比亚斯·梅西

发布于 2021 年

出版社:O’Reilly Media, Inc.

这是一本很棒的书,它确认了现在数据工程师的需求很高。这本书汇集了数据工程师的经验。许多作者为在大数据和人工智能领域取得显著成功的公司设计了数据管道和 ETL 过程。很高兴看到人们仍然愿意分享他们的知识,并解释他们是如何解决具有挑战性的 ETL 问题的。这本书包含 97 个用例,几乎每个数据工程师都可以用于数据处理和数据管道设计。我喜欢每天读一本。

结论

如果你是一个学习者或有志于获取新数据技能的数据爱好者,那么在云端有很多免费的机会。 我强烈建议你在一个云平台供应商那里注册账户,开始学习市场上可用的数据工程工具。许多平台提供免费层服务,探索最新的数据工程进展不应该花费任何费用。只要在使用免费层工具时注意账单就可以了。本文中提供的书籍概述将支持你的学习曲线。大多数书籍假设读者能够熟练使用 JSON、SQL、REST API,并了解 Python 编程基础。这与我之前在一篇文章中写到的数据工程技能集相符。我希望你会觉得这很有用。

## 如何成为数据工程师

2024 年的初学者捷径

towardsdatascience.com

推荐阅读:

  1. medium.com/towards-data-science/python-for-data-engineers-f3d5db59b6dd

  2. towardsdatascience.com/data-platform-architecture-types-f255ac6e0b7

  3. towardsdatascience.com/modern-data-engineering-e202776fb9a9

  4. towardsdatascience.com/data-pipeline-design-patterns-100afa4b93e3

  5. towardsdatascience.com/how-to-become-a-data-engineer-c0319cb226c2

数据工程:在单个旧 GPU 上进行大约 2 亿行的快速空间连接

原文:towardsdatascience.com/data-engineering-fast-spatial-joins-across-2-billion-rows-on-a-single-old-gpu-c6cb531949ed?source=collection_archive---------11-----------------------#2023-05-30

比较在本地机器上的旧款 Nvidia GeForce GTX 1060 GPU 上进行空间连接时,ORC 和 Parquet 的性能表现

Daniel VoyceTowards Data Science Daniel Voyce

·

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

--

图片由 Clay Banks 提供,来源于 Unsplash

在过去几周里,我对自 2019 年以来 GPU 数据处理库的进展进行了更深入的探讨。

在 4 年里,我发现许多在 2019 年处于早期 alpha 阶段的库已经成熟为实际使用的可靠项目。

我在大数据解决方案的 Data Engineering 领域工作了多年,我们定期进行的任务之一是通过多个多边形执行人类运动数据的空间连接。这是一个具有多个优化层次的很好的用例,但“点在多边形内”测试是所有工作的核心。

en.wikipedia.org/wiki/Point_in_polygon#/media/File:RecursiveEvenPolygon.svg

之前,我们探索了包括 PostGIS、Redshift 和 BigQuery 在内的多种方法来实现这一目标。最终,我们建立了管道,通过大约 1.4 亿个多边形在 BigQuery 上处理每日 2000 亿行数据。

随着 GPU 处理技术的最新进展,我对重新评估这项任务以及使用我的实验室机器评估 GPU 上的处理时间产生了兴趣。本文详细介绍了在消费者 GPU(GeForce GTX 1060)上执行这项任务的过程和所需时间。

设置

你可以在这里阅读关于设置、数据预处理和其他内容:

## GPU 数据工程,使用 Dask 和 RAPIDS.ai 将 CSV 转换为 Parquet 和 ORC

直到最近,我的大部分数据工程工作都集中在设计和部署数据管道上,通常是在…

medium.com

该实验使用的数据集包含 CSV 格式的经纬度点和一个表示拉斯维加斯的多边形。数据集被转换为 Parquet 和 ORC 格式,以评估这些格式之间的性能差异。

CuSpatial 是一个基于 rapids.ai 的库,专注于地理空间和 GIS 应用程序,用于实际性能测试。

## GitHub - rapidsai/cuspatial: CUDA 加速的 GIS 和时空算法

cuSpatial 通过 GPU 并行化加速矢量地理空间操作。作为 RAPIDS 库的一部分…

github.com

我故意避免使用任何空间索引或其他方法来提升性能。这项实验应被视为基线性能,经过各种技术手段可以显著提升。

数据集约 400GB 未压缩,包含 28 亿行点数据。

代码

下面提供的代码依赖于 Rapids.ai 和 Dask(特别是dask_cudf),以在 GPU 上处理数据并将数据划分以适应有限的 GPU 内存。这个过程涉及大量的调整和格式特定的错误信息(例如,在 Parquet 上工作正常但在 ORC 上却出现问题,反之亦然)。

ChatGPT 和 NVIDIA 团队在完成这项任务中发挥了重要作用,我喜欢大家在数据社区中的乐于助人!ChatGPT 对理解一些 Dask 相关功能和通过提供一些解决方案来处理各种错误很有帮助。

每个实验的基本设置如下,唯一的区别在于如何创建 ddfread_orcread_parquet):

rmm.reinitialize(managed_memory=True)
cluster = LocalCUDACluster()
client = Client(cluster)

polygon_wkt = "POLYGON((-115.074444 36.289153, -115.208314 36.325569, -115.208688 36.325646, -115.259397 36.335589, -115.260628 36.335652, -115.260845 36.335658, -115.276407 36.335967, -115.320842 36.33641, -115.333587 36.30627, -115.368573 36.170149, -115.3682 36.168344, -115.36794 36.16714, -115.353159 36.109493, -115.315922 36.023474, -115.298126 35.998029, -115.102856 35.918199, -115.100108 35.918038, -115.095546 35.917781, -115.084946 35.918409, -115.072079 35.923316, -114.918841 35.983522, -114.919047 36.0226, -114.919067 36.022821, -114.919108 36.022997, -114.93976 36.080677, -115.006009 36.219129, -115.007607 36.222047, -115.008162 36.222806, -115.010791 36.225883, -115.06011 36.27717, -115.068572 36.285772, -115.069297 36.286474, -115.069637 36.286803, -115.070046 36.287197, -115.071477 36.288191, -115.071736 36.288332, -115.074214 36.289087, -115.074444 36.289153))"

#build polygon geoseries
polygon_shapely = wkt.loads(polygon_wkt)
polygon = geopandas.GeoSeries.from_wkt([polygon_wkt])
polygon[0]

def spatial_join(df_partition, polygon):

    #build points geoseries
    i_ddf = df_partition[['lon', 'lat']].interleave_columns(
    points_gseries = cuspatial.GeoSeries.from_points_xy(i_ddf)

    #do spatial join
    result = cuspatial.point_in_polygon(
        points_gseries, polygon
    )

    #needed for large datasets as wont fit into size_type (https://github.com/rapidsai/cudf/issues/3958)
    true_count = np.sum(result)
    false_count = len(result) - true_count

    return (true_count, false_count)

def wrapped_spatial_join(df_partition):
    gpolygon = cuspatial.from_geopandas(polygon)
    return spatial_join(df_partition, gpolygon)

sums = ddf.map_partitions(wrapped_spatial_join).compute()

total_true_count = sum(t[0] for t in sums)
total_false_count = sum(t[1] for t in sums)

result = {'True': total_true_count, 'False': total_false_count}
result

结果与文件类型

关于结果和文件类型,在上一篇文章中我选择了 ORC 和 Parquet 作为导出文件类型,因为它们是大数据生态系统中最广泛使用的两种文件格式。ORC 经常被忽视而青睐 Parquet,但它提供的功能在某些系统上可以超越 Parquet。

我在 ORC 和 Parquet 文件上运行了相同的代码,并获得了以下结果:

Parquet 结果

3 分钟 9 秒 — 令人印象深刻!

sums = ddf.map_partitions(wrapped_spatial_join).compute()

CPU times: user 8.81 s, sys: 2.01 s, total: 10.8 s
Wall time: 3min 9s

total_true_count = sum(t[0] for t in sums)
total_false_count = sum(t[1] for t in sums)
​
result = {'True': total_true_count, 'False': total_false_count}
result

CPU times: user 1.3 s, sys: 79.2 ms, total: 1.38 s
Wall time: 1.34 s
{'True': 0    2179985812
 dtype: int64,
 'False': 0    66374320
 dtype: int64}

Total: 2,246,360,132 Rows

从 CSV 到结果的总 Parquet 时间 = 23 分 04 秒

Orc 结果

6 分钟 18 秒 — Parquet 时间的两倍但仍然 值得称道

sums = ddf.map_partitions(wrapped_spatial_join).compute()

CPU times: user 23.8 s, sys: 4.37 s, total: 28.1 s
Wall time: 6min 18s

total_true_count = sum(t[0] for t in sums)
total_false_count = sum(t[1] for t in sums)

result = {'True': total_true_count, 'False': total_false_count}
result

CPU times: user 4.29 s, sys: 301 ms, total: 4.59 s
Wall time: 4.45 s
{'True': 0    2179985812
 dtype: int64,
 'False': 0    66374320
 dtype: int64}

Total: 2,246,360,132 Rows

从 CSV 到结果的总 ORC 时间 = 17 分 55 秒

总体而言,Parquet 总时间为 23 分钟,而 ORC 为 18 分钟。然而,最佳文件格式将取决于你的使用案例和所使用的系统。

痛点

在这个过程中,我遇到了一些挑战,例如处理数据帧大小、有限的 GPU 内存以及数据集的大小和类型问题。

尽管存在这些障碍,从这个实验中得出的结论是,虽然 Parquet 转换时间较长,但转换后性能更佳。而 ORC 在计算上较慢,但转换速度较快。

两种格式在有限硬件上的表现都令人印象深刻,并且通过基本优化,性能都可以显著提升。

Dask 分布式实现中的挑战

为了利用 CuSpatial 的空间连接函数 “point_in_polygon”,纬度和经度点必须存储在交错数组格式中:

#interleaved

[
lon,
lat,
lon,
lat,
lon,
lat,
lon,
...
]

#instead of

[[lon,lat],[lon,lat],[lon,lat],[lon,lat],[lon,lat],...] 

这种特定安排可能是由于 GPU 流处理对这种数据结构的高效性。主要挑战是处理比 GPU 内存更大的数据帧,并将其转换为所需的交错数组格式。

起初,我尝试使用 dask_cudf 对我读取的点数据进行分区,希望这足以执行 point_in_polygon 函数。然而,我很快意识到,由于需要将点交错,除非这些点(10GB)能够适应 GPU 内存(6GB),否则这将是不可能的。

回顾起来,解决方案是使用map_partitions来处理* dask_cudf* 框架中的数据。交错需要在 map_partitions 函数内进行,而不是在传递之前,这样只对每个分区进行交错,而不是整个数据框。

这并不像看起来那么简单,因为函数的 Pickle 序列化存在问题,最终需要创建“wrapped_spatial_join”函数。

事后看来,这是一个显而易见的解决方案,我本应该使用map_partitions来处理dask_cudf 框架中的数据,交错需要在map_partitions函数内完成,而不是在传递给map_partitions之前,这样只对每个块进行交错,而不是整个数据框。

这也并不像描述的那样简单,因为dask 不喜欢在map_partitions函数内包含其他函数(这会导致 Pickle 序列化函数的问题——因此最终有了“wrapped_spatial_join”函数)。

数据大小和类型挑战

另一个遇到的问题是cuDF的数据集大小限制,由INT_32(2,147,483,647)大小限制。我的数据集大约包含 23 亿条记录,超出了这个限制。

这意味着不可能一次性处理整个数据集,因此必须对数据分区完成所有操作。其中一个例子是多边形内外点的最终计数。不能使用简单的result.value_counts(),需要对每个分区进行单独计算,然后汇总结果。

对于需要在数据集上进行复杂计算的大型或宽数据集,这一点尤为重要,因为处理整个数据集可能不可行。这实际上是这次实验的主要焦点,因为现在这在小型 GPU 上有效,你可以确信相同的过程可以应用于更大的 GPU 和更大的数据集!

结论

就性能而言,Parquet 转换所需时间较长,但一旦转换完成,其效率更高。根据工作流程,这可能是一个关键因素。通常,数据只需转换一次,但可能会经历多次分析,使得 Parquet 成为更合适的选择。

另一方面,ORC 转换更快,但在计算时效率较低。对于更多的临时计算,ORC 可能更可取,特别是与具有高效 ORC 引擎的工具如 TrinoDB 一起使用时。

两种格式在有限的硬件上表现出强大的性能。通过一些基本优化,它们的性能可能会进一步提高。观察每种格式的基线性能是很有价值的。

未来实验

在未来,我计划对多个多边形进行相同的实验,并探索处理这些多边形的方法。例如,我将对拉斯维加斯地区的邮政编码数据集进行空间连接,以检查处理多个多边形的可行性。此外,我还计划利用空间索引解决方案,如 Uber 的 H3,来对数据进行索引,并评估其对最终结果的影响。

关于作者

Dan Voyce

数据工程面试问题

原文:towardsdatascience.com/data-engineering-interview-questions-fdef62e46505

准备面试的技巧

💡Mike ShakhomirovTowards Data Science 💡Mike Shakhomirov

·发布在 Towards Data Science ·20 min 阅读·2023 年 11 月 30 日

--

照片由 Ignacio Amenábar 提供,来自 Unsplash

这个故事旨在揭示各种数据工程面试场景和典型讨论。它涵盖了你可能会被问到的几乎所有问题,希望对初学者和中级数据从业者在求职面试准备期间有所帮助。在我将近十五年的分析和数据工程职业生涯中,我面试了很多人,现在我想与大家分享我的观察。

数据工程面试难吗?不,如果你了解你在处理什么的话,并不难。许多公司都有技术博客,描述他们的技术栈和使用的技术。我建议提前做一些研究。

数据工程面试本身其实很简单,这项工作也非常有回报。

面试确实很简单,因为问题通常遵循相同的模式。数据平台类型 [1] 的数量仅限于四种,这将定义答案,帮助你通过面试。所以,如果我们知道我们在工程什么,那么正确回答面试问题就不是一项很大的任务。

## 数据平台架构类型

它如何满足你的业务需求?选择的两难困境。

towardsdatascience.com

数据工程(DE)面试很容易通过,除非你被要求编写代码。这通常是面试过程的第二部分。以下是我收集的数据工程面试问题及答案。希望你喜欢!

你日常的 DE 工作是怎样的?

通常招聘经理会用这个简单的问题开始谈话。在这里,我们希望展示对各种 DE 工具和框架的丰富热情和经验。提供一些数据管道的示例来装饰你的回答。可以是你构建的几个数据管道,或者以数据仓库为中心的全生命周期项目。不要称其为教程。最好说点像这样的话…

“… 一个从需求收集到数据管道设计和上线的全生命周期项目。”

这看起来更专业,这也是你希望传达的印象。尽量简洁,但也要流利地描述你的日常工作。例如,你可以说你是一个学生,目前主要关注数据质量,并且你设计并构建了数据管道,以便在将数据加载到数据平台之前,首先使用行条件检查数据。或者,你可以提到你知道如何使用 SDK 将数据加载到数据仓库等。你可以在这篇文章中找到一些好的示例[2]:

## 数据工程师的 Python

面向初学者的高级 ETL 技术

towardsdatascience.com

这并不太困难。你可以说,你在左侧有各种数据源,可以按照下面的模式创建数据管道,将它们集成到你的数据仓库(DWH)解决方案中。

数据仓库示例。图片由作者提供

请考虑这个将数据加载到 BigQuery 数据仓库的示例,使用了 Pandas 和 google.cloud 库[3]:

from google.cloud import bigquery
from google.oauth2 import service_account
...
# Authenticate BigQuery client:
service_acount_str = config.get('BigQuery') # Use config
credentials = service_account.Credentials.from_service_account_info(service_acount_str)
client = bigquery.Client(credentials=credentials, project=credentials.project_id)

...
def load_table_from_dataframe(table_schema, table_name, dataset_id):
    #! source data file format must be outer array JSON:
    """
    [
    {"id":"1"},
    {"id":"2"}
    ]
    """
    blob = """
            [
    {"id":"1","first_name":"John","last_name":"Doe","dob":"1968-01-22","addresses":[{"status":"current","address":"123 First Avenue","city":"Seattle","state":"WA","zip":"11111","numberOfYears":"1"},{"status":"previous","address":"456 Main Street","city":"Portland","state":"OR","zip":"22222","numberOfYears":"5"}]},
    {"id":"2","first_name":"John","last_name":"Doe","dob":"1968-01-22","addresses":[{"status":"current","address":"123 First Avenue","city":"Seattle","state":"WA","zip":"11111","numberOfYears":"1"},{"status":"previous","address":"456 Main Street","city":"Portland","state":"OR","zip":"22222","numberOfYears":"5"}]}
    ]
    """
    body = json.loads(blob)
    print(pandas.__version__)

    table_id = client.dataset(dataset_id).table(table_name)
    job_config = bigquery.LoadJobConfig()
    schema = create_schema_from_yaml(table_schema) 
    job_config.schema = schema

    df = pandas.DataFrame(
    body,
    # In the loaded table, the column order reflects the order of the
    # columns in the DataFrame.
    columns=["id", "first_name","last_name","dob","addresses"],

    )
    df['addresses'] = df.addresses.astype(str)
    df = df[['id','first_name','last_name','dob','addresses']]

    print(df)

    load_job = client.load_table_from_dataframe(
        df,
        table_id,
        job_config=job_config,
    )

    load_job.result()
    print("Job finished.")

你是如何创建数据管道的?

你需要明确表示你对使用第三方 ETL 工具(如 Fivetran、Stitch 等)和自己编写的定制数据连接器都很有信心。数据管道是指将数据从 A 点提取、转换和/或加载到 B 点的过程[4]。

## 数据管道设计模式

选择合适的架构及示例

towardsdatascience.com

所以你需要做的就是展示你知道如何按照三种主要数据管道设计模式来操作——批处理(分块汇总和处理)、流处理(逐条处理和加载记录)、变更数据捕获(CDC,在 A 点识别和捕获变化,以处理和加载到 B 点)。

CDC 和流处理是紧密相关的。

例如,我们可以使用 MySQL 二进制日志文件实时将数据移动到我们的 DWH 解决方案中。它必须谨慎使用,并不总是数据管道中最具成本效益的工具,但值得一提。按照概念设计图保持一切有序。这有助于解释许多 ETL 相关的事项。

概念数据管道设计。图像由作者提供

你对数据平台设计了解多少?

简而言之,有四种数据平台架构类型,这将定义你在构建管道时可能想要使用的工具。这是问题的关键——它帮助选择合适的数据工程工具和技术。数据湖、数据仓库和数据湖屋各有其优点并服务于不同的目的。第四种架构类型是数据网格,其中数据管理是分散的。数据网格定义了当我们拥有不同的数据领域(公司部门)及其自身团队和共享数据资源时的状态。它可能看起来有些混乱,但许多公司选择这种模型来减少数据官僚主义。

通常情况下,与数据湖相比,数据仓库提供更好的数据治理。由于内置 ANSI-SQL 能力,它使数据栈看起来现代且灵活。迁移到数据湖或数据仓库主要取决于用户的技能水平。数据仓库解决方案将提供更多的互动性,并将我们的选择缩小到 SQL 优先的产品(如 Snowflake、BigQuery 等)。

数据湖适用于具备编程技能的用户,我们倾向于选择以 Python 为优先的产品,如 Databricks、Galaxy、Dataproc、EMR。

什么是数据建模?

数据建模是数据工程的一个重要部分,因为数据是通过实体(表、视图、孤岛、数据湖)之间的关系进行转化的。你需要展示你对概念物理设计过程的理解。我们总是从为我们的业务流程或数据转化任务创建一个模型的概念开始。然后是功能模型,即原型,旨在证明我们的概念模型适用于该任务。最后,我们将创建一个物理模型,其中包含最终的基础设施,包括所有所需的物理实体和对象。值得一提的是,这并不总是 SQL 实体。概念数据建模可能包括所有类型的数据平台以及云存储中的半结构化数据文件。一个好的例子是,当我们需要先在数据湖中准备数据,然后用它来训练机器学习(ML)模型时。我之前在这篇文章中写过:

[## 使用 AWS Step Functions 编排机器学习管道]

高级数据工程和基础设施即代码的机器学习操作

pub.towardsai.net

提到你熟悉模板引擎DBTDataform是很好的,这些工具可以用于这项任务。为什么?它们对于数据转换单元测试[4]和数据环境[5]非常有帮助,防止人为错误,并提供更好的部署工作流。我之前在这里写过:

## 数据平台的持续集成和部署

数据工程师和机器学习操作的 CI/CD

[towardsdatascience.com

星型模式和雪花模式有什么区别?

求职面试官很常测试你的数据工程设计模式知识。尽量简明扼要地说明星型模式是我们可以利用超级大型非规范化数据集与一个事实表连接的地方。这就是为什么它被称为星型数据库设计模式,因为它看起来像一颗星。这更适用于数据仓库的 OLAP 风格分析管道。这些数据集中的数据并不总是最新的,但没关系,因为我们需要以这种方式物化数据,如果需要,我们可以更新所需的字段。

与星型模式相对,雪花模式设计的中心是相同的事实表,但它与许多其他事实表和维度表连接,这些表通常是非规范化的。这种模式设计更适合于 OLTP 数据处理,当数据需要始终保持最新状态时,能够快速提取单独的行以供应用程序使用。

在 1 到 10 的范围内,你的 SQL 技能有多好?

确保你能解释你的答案。SQL 是一种自然的方言,用于建模数据转换和创建分析数据集。自信地处理增量表更新可以直接给你 6 分。请考虑下面的例子。它使用 MERGE 创建一个增量表:

create temp table last_online as (
    select 1 as user_id
    , timestamp('2000-10-01 00:00:01') as last_online
)
;
create temp table connection_data  (
  user_id int64
  ,timestamp timestamp
)
PARTITION BY DATE(_PARTITIONTIME)
;
insert connection_data (user_id, timestamp)
    select 2 as user_id
    , timestamp_sub(current_timestamp(),interval 28 hour) as timestamp
union all
    select 1 as user_id
        , timestamp_sub(current_timestamp(),interval 28 hour) as timestamp
union all
    select 1 as user_id
        , timestamp_sub(current_timestamp(),interval 20 hour) as timestamp
union all
    select 1 as user_id
    , timestamp_sub(current_timestamp(),interval 1 hour) as timestamp
;

merge last_online t
using (
  select
      user_id
    , last_online
  from
    (
        select
            user_id
        ,   max(timestamp) as last_online

        from 
            connection_data
        where
            date(_partitiontime) >= date_sub(current_date(), interval 1 day)
        group by
            user_id

    ) y

) s
on t.user_id = s.user_id
when matched then
  update set last_online = s.last_online, user_id = s.user_id
when not matched then
  insert (last_online, user_id) values (last_online, user_id)
;
select * from last_online
;

我之前写过高级技巧。我认为这是开始准备的好地方[6]:

## 高级 SQL 技巧入门

在 1 到 10 的范围内,你的数据仓库技能有多好?

[towardsdatascience.com

对于数据转换脚本运行 SQL 单元测试和处理自定义用户定义函数(UDF)[7]将使你获得 9 分。

如何在 SQL 中获得 10 分?

这将是非常棘手的,显然与对特定工具的专业知识相关,即将表转换为结构体数组并将其传递给 UDF。

当你需要对每一行或表应用具有复杂逻辑的用户定义函数(UDF)时,这很有用。

你可以将你的表视为一组 TYPE STRUCT 对象,然后将每个对象传递给 UDF。这取决于你的逻辑。例如,我在购买堆叠中使用它来计算过期时间:

select 
     target_id
    ,product_id
    ,product_type_id
    ,production.purchase_summary_udf()(
        ARRAY_AGG(
            STRUCT(
                target_id
                , user_id
                , product_type_id
                , product_id
                , item_count
                , days
                , expire_time_after_purchase
                , transaction_id 
                , purchase_created_at 
                , updated_at
            ) 
            order by purchase_created_at
        )
    ) AS processed

from new_batch
;

OLAP 和 OLTP 的区别是什么?

在线分析处理(OLAP)和在线事务处理(OLTP)是为完全不同目的设计的数据处理系统。OLAP 旨在聚合和存储数据,以便用于分析,如报告和大规模数据处理,这也是为什么在这里经常可以看到非规范化的超大表。OLTP 处理则不同,它专注于单个事务,并要求数据处理速度非常快。好的例子包括应用内购买、管理用户账户和更新商店内容。OLTP 的数据存储在使用雪花模式连接的索引表中,其中维度表大多是规范化的。

你知道哪些数据工程框架?

我们不能知道所有事情。我面试了很多人,拥有所有数据工程工具和框架的经验并不是必要的。你可以列举一些:Python ETL (PETL)、Bonobo、Apache Airflow、Bubbles、Kestra、Luigi,我之前写过关于过去几年我们见证的 ETL 框架爆炸的文章。

## 现代数据工程

平台特定工具和高级技术

towardsdatascience.com

我们不需要对所有框架都非常熟悉,但展示自信是必须的。

为了展示对各种数据工具的自信,我们应该至少学习一两个,然后使用基本原则(数据工程原则)。采用这种方法,我们可以回答几乎所有数据工程问题:

你为什么这样做?——我从基本原则中得到了这个。

说到这,学习 Apache Airflow 的一些内容并通过简单的管道示例来展示它是完全可以的。例如,我们可以在将数据导出到云存储(bq_export_op)后运行 ml_engine_training_op,并使这个工作流每天或每周运行一次。

使用 Airflow 进行 ML 模型训练。图片由作者提供。

请看下面的这个例子。

它创建一个简单的数据管道图,将数据导出到云存储桶中,然后使用 MLEngineTrainingOperator 训练 ML 模型。

"""DAG definition for recommendation_bespoke model training."""

import airflow
from airflow import DAG
from airflow.contrib.operators.bigquery_operator import BigQueryOperator
from airflow.contrib.operators.bigquery_to_gcs import BigQueryToCloudStorageOperator
from airflow.hooks.base_hook import BaseHook
from airflow.operators.app_engine_admin_plugin import AppEngineVersionOperator
from airflow.operators.ml_engine_plugin import MLEngineTrainingOperator

import datetime

def _get_project_id():
  """Get project ID from default GCP connection."""

  extras = BaseHook.get_connection('google_cloud_default').extra_dejson
  key = 'extra__google_cloud_platform__project'
  if key in extras:
    project_id = extras[key]
  else:
    raise ('Must configure project_id in google_cloud_default '
           'connection from Airflow Console')
  return project_id

PROJECT_ID = _get_project_id()

# Data set constants, used in BigQuery tasks.  You can change these
# to conform to your data.
DATASET = 'staging' #'analytics'
TABLE_NAME = 'recommendation_bespoke'

# GCS bucket names and region, can also be changed.
BUCKET = 'gs://rec_wals_eu'
REGION = 'us-central1' #'europe-west2' #'us-east1'
JOB_DIR = BUCKET + '/jobs'

default_args = {
    'owner': 'airflow',
    'depends_on_past': False,
    'start_date': airflow.utils.dates.days_ago(2),
    'email': ['mike.shakhomirov@gmail.com'],
    'email_on_failure': True,
    'email_on_retry': False,
    'retries': 5,
    'retry_delay': datetime.timedelta(minutes=5)
}

# Default schedule interval using cronjob syntax - can be customized here
# or in the Airflow console.
schedule_interval = '00 21 * * *'

dag = DAG('recommendations_training_v6', default_args=default_args,
          schedule_interval=schedule_interval)

dag.doc_md = __doc__

#
#
# Task Definition
#
#

# BigQuery training data export to GCS

training_file = BUCKET + '/data/recommendations_small.csv' # just a few records for staging

t1 = BigQueryToCloudStorageOperator(
    task_id='bq_export_op',
    source_project_dataset_table='%s.recommendation_bespoke' % DATASET,
    destination_cloud_storage_uris=[training_file],
    export_format='CSV',
    dag=dag
)

# ML Engine training job
training_file = BUCKET + '/data/recommendations_small.csv'
job_id = 'recserve_{0}'.format(datetime.datetime.now().strftime('%Y%m%d%H%M'))
job_dir = BUCKET + '/jobs/' + job_id
output_dir = BUCKET
delimiter=','
data_type='user_groups'
master_image_uri='gcr.io/my-project/recommendation_bespoke_container:tf_rec_latest'

training_args = ['--job-dir', job_dir,
                 '--train-file', training_file,
                 '--output-dir', output_dir,
                 '--data-type', data_type]

master_config = {"imageUri": master_image_uri,}

t3 = MLEngineTrainingOperator(
    task_id='ml_engine_training_op',
    project_id=PROJECT_ID,
    job_id=job_id,
    training_args=training_args,
    region=REGION,
    scale_tier='CUSTOM',
    master_type='complex_model_m_gpu',
    master_config=master_config,
    dag=dag
)

t3.set_upstream(t1)

你会使用什么来协调你的数据管道?

区分用于数据转换的 ETL 框架和用于编排数据管道的框架非常重要。你可以提到一些:Airflow、Prefect、Dagster、Kestra、Argo、Luigi。这些是目前最受欢迎的开源项目,可以免费使用。然而,一个好的回答应该表明你能够使用自己的定制工具进行数据管道编排。如果你喜欢 AWS,你可以使用 CloudFormation(基础设施即代码)和 Step Functions 部署和编排数据管道。我之前在这里写过关于它的内容[9]:

## 数据管道编排

正确进行数据管道管理可以简化部署,并提高数据的可用性和可访问性...

[towardsdatascience.com

实际上,我们在这里甚至不需要 Step Functions,因为这将是一个非常平台特定的选择。我们可以使用平台无关的 Terraform(基础设施即代码)和 Serverless 部署带有所需数据管道编排逻辑的微服务。

你的编程语言是什么?

对这个问题的回答取决于公司技术栈。长话短说,如果你回答 Python,你不会错。这在 DE 和数据科学中是绝对必要的,因为它的简单性以及市场上众多的库和开源数据工具。

不过,不要仅仅局限于 Python。

熟悉其他语言总是有益的,例如 JAVA、JavaScript、Scala、Spark 和 R。例如,R 在数据科学中表现优异,并且在学术界和大学中非常受欢迎。提到 Spark 也是有益的。它不是一种语言(框架),但由于其卓越的可扩展性和处理大规模数据的能力,它变得非常流行[8]。

你可能不知道 Spark,但如果你知道 Python,那么你可以使用 Spark API 连接器(PySpark)。

什么是 *args 和 **kwargs?

通常,如果你在前一个问题中提到 Python,下一步就是这个。回答关于函数参数的问题是我在面试中最常问的问题之一。你需要准备好回答它,也许还会用几行代码给面试官留下深刻印象:

def sum_example(*args):
    result = 0
    for x in args:
        result += x
    return result

print(sum_example(1, 2, 3))

def concat(**kwargs):
    result = ""
    for arg in kwargs.values():
        result += arg
    return result

print(concat(a="Data", b="Engineering", c="is", d="Great", e="!"))

你对 CLI 工具和 shell 脚本的掌握如何?

云供应商的命令行工具基于 REST API,使数据工程师能够通过强大的命令行界面与云服务端点进行通信,以描述和修改资源。数据工程师使用 CLI 工具与 bash 脚本链式命令。这有助于创建强大的脚本,并轻松与云服务进行交互。考虑下面的示例。它将调用名为 pipeline-manager 的 AWS Lambda 函数:

aws lambda invoke \
    --function-name pipeline-manager \
    --payload '{ "key": "something" }' \
    response.json 

我们可以创建一些更强大的工具来部署我们的无服务器微服务。考虑下面这个例子。它将检查 lambda 包的存储桶是否存在,上传并部署我们的 ETL 服务作为一个 Lambda 函数[10]:

# ./deploy.sh
# Run ./deploy.sh
LAMBDA_BUCKET=$1 # your-lambda-packages.aws
STACK_NAME=SimpleETLService
APP_FOLDER=pipeline_manager
# Get date and time to create unique s3-key for deployment package:
date
TIME=`date +"%Y%m%d%H%M%S"`
# Get the name of the base application folder, i.e. pipeline_manager.
base=${PWD##*/}
# Use this name to name zip:
zp=$base".zip"
echo $zp
# Remove old package if exists:
rm -f $zp
# Package Lambda
zip -r $zp "./${APP_FOLDER}" -x deploy.sh

# Check if Lambda bucket exists:
LAMBDA_BUCKET_EXISTS=$(aws  s3 ls ${LAMBDA_BUCKET} --output text)
#  If NOT:
if [[ $? -eq 254 ]]; then
    # create a bucket to keep Lambdas packaged files:
    echo  "Creating Lambda code bucket ${LAMBDA_BUCKET} "
    CREATE_BUCKET=$(aws  s3 mb s3://${LAMBDA_BUCKET} --output text)
    echo ${CREATE_BUCKET}
fi

# Upload the package to S3:
aws s3 cp ./${base}.zip s3://${LAMBDA_BUCKET}/${APP_FOLDER}/${base}${TIME}.zip

# Deploy / Update:
aws --profile $PROFILE \
cloudformation deploy \
--template-file stack.yaml \
--stack-name $STACK_NAME \
--capabilities CAPABILITY_IAM \
--parameter-overrides \
"StackPackageS3Key"="${APP_FOLDER}/${base}${TIME}.zip" \
"AppFolder"=$APP_FOLDER \
"LambdaCodeLocation"=$LAMBDA_BUCKET \
"Environment"="staging" \
"Testing"="false"

你如何部署你的数据管道?

没有对错之分,但如果你说“我手动创建管道步骤,然后在云中使用供应商的控制台进行部署……”那就不是最佳答案。

现在,好的答案是提到脚本。这告诉面试官你是一个中级用户,至少熟悉 Shell 脚本。你会想说,无论你部署什么,都可以使用 bash 脚本和 CLI 工具进行部署。所有主要的云供应商都有他们的命令行工具,你至少要对其中一种工具有所了解。

最优的方法,通常被认为是最佳实践,是使用基础设施即代码和 CI/CD 工具来部署你的管道[11]。

## 数据平台的持续集成和部署

数据工程师和机器学习运维的 CI/CD

[towardsdatascience.com

你在数据科学方面的水平如何?

作为数据工程师,你不需要了解数据科学模型训练和超参数调优的所有细节,但记住一个好的数据科学家必须是一个好的数据工程师。不必完全相反,但展示至少一些基本的数据科学算法知识总是有益的。例如,你可以提到你知道如何创建线性回归和逻辑回归模型。线性回归生成定量输出(预测的数字),而逻辑回归则返回简单的答案——“是”或“否”(1/0)。事实上,所有主要的数据科学模型都可以在你的数据仓库解决方案中使用 SQL 轻松训练。

让我们假设我们的用例是客户流失预测。

BigQuery ML为例,我们可以像这样创建一个逻辑回归模型:

CREATE OR REPLACE MODEL sample_churn_model.churn_model

OPTIONS(
  MODEL_TYPE="LOGISTIC_REG",
  INPUT_LABEL_COLS=["churned"]
) AS

SELECT
  * except (
     user_pseudo_id
    ,first_seen_ts    
    ,last_seen_ts     
  )
FROM
  sample_churn_model.churn

你对数据质量和数据可靠性了解多少?

这是一个很好的问题,因为你可能会被问及在数据平台中确保数据质量的可能方法。这是数据工程师日常工作的一部分,涉及提高数据管道的数据准确性。数据工程师连接数据源并部署管道,其中数据必须被提取,然后根据业务需求经常需要进行转换。

我们需要确保所有必需的字段存在(数据质量)且没有数据丢失(可靠性)。

我们如何做到这一点?总是提到自修复管道是好的,并且你知道如何部署它们。数据工程师可以以类似于部署 ETL 管道的方式部署数据质量管道。简单来说,你需要为一个数据集使用行条件,并根据结果部署修复步骤,即提取缺失的数据并加载它。

使用行条件来确保数据集的数据质量。

所有的数据质量检查都可以作为脚本进行调度,如果任何一个检查未能满足特定条件,我们可以发送电子邮件通知。值得一提的是,现代数据仓库解决方案允许使用 SQL 脚本进行这些检查,但这不一定要局限于 SQL。任何数据检查脚本都可以在数据湖或其他地方的数据上运行。这取决于我们数据平台的类型。在这种情况下,良好的编码技能是必需的,因此我们需要展示我们知道如何创建一个简单的巡检应用程序,可以根据数据的实际位置扫描数据。

基于 SQL 的答案也很好,但它更适合数据开发人员角色,因为 SQL 通常被认为是分析中的主要数据查询方言。考虑下面的例子。它将使用 SQL 和行条件来检查是否有 NULL payment_date的记录。它还会检查重复记录。

with checks as (
    select
      count( transaction_id )                                                           as t_cnt
    , count(distinct transaction_id)                                                    as t_cntd
    , count(distinct (case when payment_date is null then transaction_id end))          as pmnt_date_null
    from
        production.user_transaction
)
, row_conditions as (

    select if(t_cnt = 0,'Data for yesterday missing; ', NULL)  as alert from checks
union all
    select if(t_cnt != t_cntd,'Duplicate transactions found; ', NULL) from checks
union all
    select if(pmnt_date_null != 0, cast(pmnt_date_null as string )||' NULL payment_date found', NULL) from checks
)

, alerts as (
select
        array_to_string(
            array_agg(alert IGNORE NULLS) 
        ,'.; ')                                         as stringify_alert_list

    ,   array_length(array_agg(alert IGNORE NULLS))     as issues_found
from
    row_conditions
)

select
    alerts.issues_found,
    if(alerts.issues_found is null, 'all good'
        , ERROR(FORMAT('ATTENTION: production.user_transaction has potential data quality issues for yesterday: %t. Check dataChecks.check_user_transaction_failed_v for more info.'
        , stringify_alert_list)))
from
    alerts
;

结果是 BigQuery 将发送包含警报的自动电子邮件:

邮件警报。图像由作者提供。

你会使用什么算法来提取或处理非常大的数据集?

如果你之前有关于使用 Python 进行数据转换的问题,这个问题可能是个陷阱。如果你喜欢 Python,那么你可能是 Pandas 库的忠实粉丝,并且你可能已经在面试中提到过。实际上,这种问题中你不希望使用 Pandas。问题在于 Pandas 处理大数据集时效果不好,尤其是在数据转换方面。你在 Pandas 数据框中运行数据转换时,总是会受到机器内存的限制。

正确的答案应该提到,如果内存有限,你会找到一个可扩展的解决方案。这可以是一个简单的 Python 生成器,是的,它可能需要很长时间,但至少不会失败。

# Create a file first: ./very_big_file.csv as:
# transaction_id,user_id,total_cost,dt
# 1,John,10.99,2023-04-15
# 2,Mary, 4.99,2023-04-12

# Example.py
def etl(item):
    # Do some etl here
    return item.replace("John", '****') 

# Create a generator 
def batch_read_file(file_object, batch_size=19):
    """Lazy function (generator) can read a file in chunks.
    Default chunk: 1024 bytes."""
    while True:
        data = file_object.read(batch_size)
        if not data:
            break
        yield data
# and read in chunks
with open('very_big_file.csv') as f:
    for batch in batch_read_file(f):
        print(etl(batch))

# In command line run
# Python example.py

最优的答案应该包括使用分布式计算来转换数据,并且理想情况下,使用一些快速且扩展性好的工具。Spark 或基于 HIVE 的工具可能是一个不错的选择。

你会在管道中何时使用 Hadoop?

你应该提到 Hadoop 是一个由 Apache 基金会开发的开源大数据处理框架,它带来了分布式数据处理的所有好处。这就是为什么它在处理大量数据的数据管道中变得如此受欢迎。它有自己的内在组件,旨在确保数据质量(HDFS——Hadoop 分布式数据系统)和可扩展性(MapReduce)。即使你没有 Hadoop 的经验,仅仅提到这些内容也足够,因为有许多工具是建立在 Apache Hadoop 之上的,例如Apache Pig(一个在 MapReduce 中执行 Hadoop 作业的编程平台)或Apache Hive——一个数据仓库项目,我们可以使用标准 SQL 方言处理存储在数据库和与 Hadoop 集成的文件系统中的数据。

你会如何处理一个大数据迁移项目?

在面试中,你可能会被问到这个问题,因为面试官希望了解你在数据迁移方面的经验以及迁移完成后的数据验证方法。在这里,我建议从业务需求开始。这可能是成本效益、数据治理或整体数据库性能。根据这些需求,我们可以选择最优的解决方案作为我们迁移项目的目标。例如,如果你当前的数据平台建立在数据湖上,并且有许多业务利益相关者希望访问数据,那么你的选择应该是 ANSI-SQL 数据仓库解决方案,在这些解决方案中我们可以提供更好的数据治理和细粒度访问控制。相反,如果我们的数据仓库解决方案在数据存储方面存在成本效益问题,那么迁移或归档到数据湖可能是一个好的选择。我之前在这里写过关于它的内容:

## 数据仓库 DBA 的日常任务

监控活动并像专家一样管理资源,或者“我的表……它去哪儿了?”

medium.com

一旦迁移完成,我们需要验证数据。数据一致性是数据工程师的首要任务,你需要展示你知道如何验证在迁移完成时没有数据丢失。例如,我们可以计算数据仓库中每个分区的总记录数,然后将其与数据湖分区中的记录数进行比较。count(*)是最便宜的操作,但它对数据验证非常有效且运行速度很快。实际上,在许多数据仓库解决方案中,count(*)是免费的。

你知道哪些 ETL 工具,它们与 ELT 有何不同?

回答这个问题时,我们想要展示的是我们不仅知道如何使用第三方工具提取、转换和加载数据,还能通过编写自己的定制数据连接器和加载器来完成。

你可以先简单提到一些托管解决方案,比如 Fivetran、Stitch 等,它们有助于 ETL。别忘了提到它们的定价模型,通常是基于处理的记录数量。

当你知道如何编码时,你不需要第三方 ETL 工具。

不要害怕说出这句话。创建自己的 ETL 工具然后将数据加载到你选择的 DWH 解决方案中是相当简单的。考虑我之前的一篇文章,其中我从 MySQL 或 Postgres 数据库中提取了数百万行数据作为例子。它解释了如何创建一个强大的数据连接器并以内存高效的方式分块提取数据 [12]。这类东西设计成无服务器的,可以轻松地在云中部署和调度。

## 使用 Athena 和 MySQL 构建批量数据管道

初学者的端到端教程

towardsdatascience.com

如果我们需要在将数据加载到 DWH 目标之前进行准备和转换,我们甚至可以创建自己的定制数据加载管理器。这是一个相当复杂的应用,但值得学习。这里是教程。

结论

“你对……的处理方法是什么?”这类问题在数据工程面试中非常常见。准备好回答这些场景问题。在面试中,你可能会被要求设计一个管道或数据平台。如果你从更广泛的角度看待问题,就是这样。每个数据平台都有其业务和功能需求——总是好在回答中提到这一点,然后提到你会根据这些需求选择数据工具。用数据管道的例子来装饰你的答案,你一定会通过面试。

推荐阅读

[1] medium.com/towards-data-science/data-platform-architecture-types-f255ac6e0b7

[2] medium.com/towards-data-science/python-for-data-engineers-f3d5db59b6dd

[3] towardsdatascience.com/modern-data-engineering-e202776fb9a9

[4] medium.com/towards-data-science/unit-tests-for-sql-scripts-with-dependencies-in-dataform-847133b803b7

[5] medium.com/towards-data-science/continuous-integration-and-deployment-for-data-platforms-817bf1b6bed1

[6] towardsdatascience.com/advanced-sql-techniques-for-beginners-211851a28488

[7] cloud.google.com/bigquery/docs/user-defined-functions

[8] spark.apache.org/

[9] medium.com/towards-data-science/data-pipeline-orchestration-9887e1b5eb7a

[10] towardsdatascience.com/how-to-become-a-data-engineer-c0319cb226c2

[11] medium.com/towards-data-science/continuous-integration-and-deployment-for-data-platforms-817bf1b6bed1

[12] medium.com/towards-data-science/mysql-data-connector-for-your-data-warehouse-solution-db0d338b782d

数据工程:为什么这不仅仅是关于你使用的工具

原文:towardsdatascience.com/data-engineering-why-its-about-much-more-than-just-the-tools-you-use-f9b2706f9c9e

重新思考数据工程,而不仅仅关注工具

Chengzhi ZhaoTowards Data Science Chengzhi Zhao

·发布于 Towards Data Science ·6 min read·2023 年 5 月 17 日

--

Dan Cristian PădurețUnsplash 上的照片

数据工程有很多工具。数据工程不仅仅依赖于 Python 或 SQL。随着大数据和云计算时代的到来,新数据相关的开源项目比以往任何时候都多。我们不仅有多个选项供你选择,而且有太多工具要求数据工程师了解每种工具的优缺点以做出选择。

数据工程有很多类似的工具。数据工程市场竞争激烈。许多开源供应商提供几乎相同的服务,只有细微的调整——所有这些都声称是顶级的,具有闪电般的性能,并拥有一个充满活力的社区。

工具使我像一个多面手一样。通过我的选择,我可以根据适合的情况拉取一个数据框架。几年前,我喜欢追逐最新和未被发现的数据工程工具。许多这些项目没有变得流行,很多需要更新。如果你发现一个很棒的 GitHub 项目,第二年它就被遗弃了,这让人感到恐怖。这就是数据工程框架如果没有足够使用而迅速消亡的速度。

我在追逐最新工具时得到的一个关键经验是:工具很重要,但许多数据工程问题不能仅通过使用最新工具来解决,而是需要人——数据工程师。

我想分享我对数据工程为何不仅仅是关于你使用的工具的想法。

用例才是数据工程的王者

图片由 JESHOOTS.COM 提供,来源于 Unsplash

数据工程师花费数小时编写每天/每小时运行的数据管道工作的目的是什么?业务需要它。

无论你的行业是什么,都需要数据收集、处理和总结数据以获得洞察,以帮助你的业务。即使你用纸张或 Excel 运营业务,数据对业务成功也是不可或缺的。

为什么数据对业务至关重要?如果我们去掉那些华丽而闪耀的术语——预测、机器学习和人工智能,企业需要数据的根本原因是确保公司能够持续运营。没有业务用例,再新的、最优秀的数据工程工具也会失去其意义。

我曾经部署了最新的数据工程框架以流式方式处理数据。最初作为概念验证项目,它运行良好。

然而,在展示了一种新的流式处理方式后,我开始遇到将其投入生产的困难。流式处理的用例还不够强大,人们仍然对批处理方法感到满意。我尝试寻找并说服他人将批处理转换为流式处理,但效果不好。我还发现迁移成本高,24/7 流式支持非常麻烦,许多 bugs 使我在如此新的框架上浪费了大量时间。

数据工程只有在有数据用例时才存在。如果我们查看开源数据工程项目,许多项目在找到倾向于这些项目的用例用户方面遇到困难。

如果你的业务有用例,进一步探索工具方面。但要确保工具不会主导任何业务用例。

数据工程理念至关重要

许多数据工程师自嘲为水管工。这一比喻可能涵盖了数据工程师职责的 70%——构建数据管道。

图片由 Florian Wächter 提供,来源于 Unsplash

无论你的数据管道是在像 Spark/Flink/Beam 这样的分布式框架中,还是纯 SQL,甚至是 Python/R 函数中,你都需要考虑三个关键因素:

  • 数据来源于哪里(提取)

  • 我们应该如何改变数据(转换)

  • 我应该将数据发送到哪里(加载)

我们也可以将这个过程总结为 ETL(提取、转换、加载)。提取和加载涉及数据的位置和存储读写效率。转换是应用业务逻辑的核心部分。

数据工程作为数据的中介或中心是必需的。虽然它通常只在事件或日志生成时才开始,但及早参与数据生成至关重要,这使得数据更易于读取且不那么混乱。

一旦数据存储在数据存储中——如 Kafka 队列、数据库或 S3 中的文件,这通常标志着许多数据工程项目的开始时间。

数据工程在这个 ETL 过程中的目标是确保数据能够及时到达最终用户,以便为帮助业务决策提供价值。

通常,数据工程团队不会站在舞台上展示销售预测或由于部署新 LLM 模型而带来的流量增加。许多数据工程师在幕后工作,是整个节目的支柱。

推导商业价值比工具更为关键。替换现有工具使管道运行速度提高 10% 不如构建能够推动 1% 收入增长的关键指标重要。

剩下的 30% 责任是获取领域知识,了解数据是如何生成的(而不是由某个团队转化并提供给你),学习最终用户在如何构建指标和维度上的偏好,并进行协作(数据工程不是单一角色)。

工具可以大大节省你构建 ETL 指标的时间,并更高效地提高你的速度。挖掘大量数据中的商业核心部分是独特的,通常难以通过添加其他工具来实现。

数据仓库是接口

最终,数据将会服务于用户。所有的辛勤工作在这个阶段得到回报。

你可以将数据按层次分级或在如 OLAP 查询引擎等分析友好的存储中进行服务。业务最关键的指标和维度应作为黄金数据集分配给最关键的业务用例。

我们常常讨论利用 OLAP、列式存储如 parquet 和并行处理。然而,模式、数据模型设计、用户友好的命名和数据目录必须得到更多评估。

认识到许多问题并不是仅仅通过增加计算资源就能解决的,这是一堂艰难的课。第一次增加更多资源可能有效,但这种情况并不总是如此。到那时,是否存在数据查询方式或设计缺陷的更根本问题已经显现。

在添加浪费资源和冗余工作的之前,重新思考数据建模设计,与用户互动,开发实际用例以评估是否涵盖这些用例。这一过程必须处理,因为数据工程师忙于评估和尝试现有工具。

我的经验是,任何流行的工具都可以实现 90% 的数据工程任务要求。我们不应花费 90% 的时间追逐最佳和最快的工具,而应将 10% 的时间专注于最关键的事情——为帮助业务提供洞察服务数据。

如果你想了解更多关于数据仓库和维度建模的内容,请参考我的数据仓库 101 文章。

通过维度建模构建更好的数据仓库:数据工程师指南

数据仓库维度建模设计 101

towardsdatascience.com

最后的思考

采用新工具可能会抛弃旧的逻辑。你是否曾经遇到过一段没人敢动的代码或理由?在丢弃旧业务逻辑之前多问几个“为什么”,可以减少麻烦。

旧逻辑存在是有其原因的;尝试去理解它是如何发展到这个阶段的,而不是单纯推崇新工具的华丽,而对旧世界缺乏尊重。

所以不要认为数据工程只是掌握各种工具。深入理解数据工程在业务用例中的数据生命周期中的位置,并更好地服务于该用例,是工具使用中最关键的部分。

希望这篇文章对你重新思考数据工程的基础有所帮助,我强烈推荐Joe Reis(作者)和Matt Housley(作者)的书——数据工程基础:规划与构建稳健的数据系统

数据熵:数据越多,问题越多?

原文:towardsdatascience.com/data-entropy-more-data-more-problems-fa889a9dd0ec?source=collection_archive---------0-----------------------#2023-05-18

如何在现代数据组织中应对和拥抱复杂性。

Salma BakoukTowards Data Science Salma Bakouk

·

Follow 发表在 Towards Data Science ·10 分钟阅读·2023 年 5 月 18 日

--

来源:unsplash.com/@brett_jordan

“就像我们遇到的钱越多,我们看到的问题也越多。” —— Notorious B.I.G

Webster’s 词典将热力学中的熵定义为一个封闭热力学系统中不可用能量的度量,同时通常也被认为是系统混乱程度的度量。

在信息论中,信息熵的概念由克劳德·香农于 1948 年提出,表示一个随机变量的“惊讶”、“信息”以及与各种可能结果相关的“不确定性”水平。对我的数学迷们来说,推荐一些有趣的阅读材料(这里和这里)。

在更广泛的背景下,熵是指事物随着时间推移而趋向无序的倾向,以及系统或环境中一般的混乱和不确定性增加。

如果你是今天繁荣的数据生态系统中的数据从业者,并被同行或业务利益相关者要求描述你的数据平台,我想你会使用以下词汇的组合:现代的/基于云的、模块化的、不断演变的、灵活的、可扩展的、安全的等。但你和我之间,你知道你也想把:混乱的、不可预测的、混沌的、昂贵的和无序的词汇加进去。

以下场景是否让你感到熟悉?

  • 商业用户无法找到并访问对他们的工作流程至关重要的数据资产。

  • 利益相关者不断质疑他们在业务仪表板中看到的数字的准确性。

  • 数据工程师花费无数时间来解决损坏的管道问题。

  • 每一个“微小”的上游变化都会导致混乱。

  • 数据团队不断面临精疲力竭,并且员工流失率高。

  • 利益相关者无法看到昂贵数据举措背后的投资回报。

列表还在继续。

每个组织都希望成为数据驱动型,但现实是许多组织在囤积数据,每年在技术和人力资源上花费数百万美元,并希望一切顺利。你是否看到熵不仅仅存在于热力学中?

那么,熵在数据平台的背景下是什么样的?

基础设施与技术

根据IDC 的研究,全球公司每年在数据和分析解决方案上花费超过 2000 亿美元,以利用数据推动创新和业务繁荣。然而,由于缺乏方向和糟糕的战略,大多数公司最终会陷入技术和数据堆栈膨胀的困境,技术债务和维护成本不断上升。

基础设施和技术熵可以以一种或几种以下方式表现出来

  • 过多的重叠工具,内部和外部解决方案的异质混合。在数据与基础设施工具方面,少即是多。如果你刚刚起步,你的分析需求相对简单,你的数据堆栈应该反映这一点。也就是说,你还应该考虑未来,倾向于选择能够随着需求扩展并提供所需灵活性的解决方案,以应对超高速增长阶段。要了解如何根据公司数据成熟度阶段为成功的数据实践奠定适当基础,请阅读这篇文章。如果你正在升级现有平台,关键是要有战略眼光,考虑业务关键性,并据此优先排序。作为一个经验法则,在投资新解决方案之前,你必须确保可以完全迁移现有解决方案和流程,并战略性地规划废弃过程,以最小化技术债务。

  • 孤立的数据。Tech Target将数据孤岛定义为由一个部门或业务单元控制的数据存储库,因此其他部门无法完全或轻易访问这些数据。虽然它们看似无害,但数据孤岛往往会导致不必要的信息壁垒的产生,从而稀释整体数据质量和数据治理标准。

  • 缺乏或不充分采用公司范围内的数据产品创建和部署指南。这既可能是文化问题,也可能是技术问题,我们将在本文稍后讨论文化方面,但在技术方面,这通常发生在技术基础不允许数据及其生产基础设施的灵活性和民主化时。这不仅会导致开发周期变慢和数据解决方案质量差,还会严重阻碍组织的数据成熟过程。

人员与文化

“文化比战略更重要” 彼得·德鲁克。

奥地利裔美国管理顾问和作者的名言在组织的数据战略方面特别适用。数据在现代组织中扮演着核心角色;这种中心性不仅仅是一个比喻,因为数据团队通常介于传统 IT 和不同业务职能之间。因此,数据从业人员被期望管理和应对不同背景和沟通风格的利益相关者。这可能导致以下一种或多种情况:

  • IT 与业务之间缺乏对齐。业务和 IT 团队在组织中有着根本不同的目的和责任。然而,他们都朝着相同的目标努力:提高整体业务表现,降低成本,实现可持续增长。

  • IT 与数据管理功能之间缺乏对齐。对于大多数组织来说,数据实践相对于软件来说是新兴的。虽然将数据管理视为一个独立实体是有意义的,但完全与 IT 和工程分隔开并不是一个明智的选择。完全分离可能会损害数据生产周期,并导致许多低效,更不用说对知识转移的负面影响了。

  • 数据生产者和数据消费者在数据服务级别协议(SLAs)方面缺乏对齐。我们称他们为消费者,但他们真的被这样对待吗?

  • 数据质量的所有权不明确。是数据生产者的责任吗?是消费者的责任吗?还是数据产品经理的责任?越来越多的角色正在崛起以解决这个问题,例如数据质量分析师、数据运营、数据治理战略家等。然而,大多数组织仍然难以确定像数据资产质量这样基本问题的责任归属。

  • 数据被视为二等公民。每家公司都希望成为数据驱动型,但即使在今天的环境下,数据驱动决策对许多人来说仍然是一个难以捉摸的理念。在启用和促进强大的数据驱动文化时,购买软件无疑是最简单的部分。

  • 数据工程师将超过 50%的时间用于处理数据事件。让数据工程师专注于创造收入的活动而不是解决数据管道问题,可以带来很多价值。这样做不仅减少了他们在重复任务上花费的时间,还使他们能够关注更重要的事情。

数据质量或其缺乏

  • 数据质量事件频繁,涉及的影响和严重程度各异。在2021 Gartner 报告中,不良数据每年给企业带来的成本约为 1300 万美元。但数据质量问题到底是什么样的?有各种指标可以衡量数据质量并评估数据偏离预期的程度。更多信息可以在这篇博客中找到。

  • 看似相同的事物有多个版本。什么是单一的真实来源?这尤其受到孤岛现象和团队之间缺乏协调的加剧。因此,试图根据数据做出决策的利益相关者之间的混乱和挫折不断升级,导致他们质疑数据团队生产的所有内容,有时甚至质疑其存在。

  • 数据生产者和数据消费者之间存在持续冲突,通常关于谁应该对数据的可靠性负责以及在管道的哪个阶段。虽然在相对较小的组织中可能会得到控制,但在更大规模时,这种冲突就变得难以克服。

那么你如何在数据平台中减少熵?

虽然热力学第二定律指出熵要么随着时间增加,要么保持不变,绝不会减少,但幸运的是,对我们数据从业者来说,这一规则并不适用。以下是一些减少数据平台中熵的技巧:

人员与文化

  • 数据驱动的文化始于(非常)高层。为了创建和培养数据驱动的文化,公司高层管理和 C 级管理者需要灌输和培养基于事实的决策思维。当高层管理者普遍期望每个业务案例和每个决策都需要以事实为依据,并以身作则时,操作人员自然会跟随。关于这一点的精彩阅读在这里

  • 数据领导者也是业务领导者。作为数据领导者,你在设计上是一个混合角色,因为数据需要服务于特定的业务目的。没有公司是为了分析而进行分析的。你不仅凭借出色的技术领导力赢得了席位,还可以在整体业务表现中发挥重要作用。为此,你需要像业务领导者一样思考,这意味着要对数据和数据项目采取务实的以结果为导向的方法。

  • 要有宏大的目标,但也要庆祝小胜利。数据成熟没有捷径,虽然你应该有雄心壮志并承担大项目,但知道何时退一步庆祝小成就对于保持团队的积极性和参与感至关重要。在这里同样需要采取基于事实的方法,因为这会增加数据驱动的倾向,并为强大的数据文化奠定良好的基础。

流程

“如果你不能将你所做的事情描述为一个过程,那你就不知道你在做什么。” ——W. Edwards Deming

  • 复杂性是不可避免的。学会接受它。随着数据平台的扩展,它会变得越来越复杂。重要的是要记住,复杂性并不是坏事,它是不可避免的。关键在于管理复杂性,使系统更容易理解和使用。

  • 放下那些不再适用于你的东西。数据平台复杂性的一个挑战是可能会导致大量的“技术债务”。这种情况发生在系统中存在一些不再发挥作用但由于过于复杂或耗时而被保留的部分。定期审查数据平台并剔除不必要的部分对保持其精简和高效至关重要。

  • 务实,经常审查流程并在必要时进行更改。随着数据平台的发展,你的流程也应相应变化。定期审查运营并根据需要进行调整至关重要,以保持其更新和相关性。

技术

  • 软件投资回报率。在数据平台技术领域,确保获得良好的投资回报率至关重要。这意味着选择适合你目的、能够满足现在和未来需求的软件至关重要。在做出决策时,还必须考虑诸如扩展成本、维护费用和培训等因素。

  • 何时构建,何时购买。市场上有大量优秀的数据平台软件可供选择,在大多数情况下,购买现成的解决方案比自行构建更具成本效益。在权衡每种方法的利弊时,需要考虑的关键因素包括:成本、时间、质量和可扩展性。虽然在某些情况下构建自定义解决方案以解决特定问题可能是有意义的,但这通常会导致技术债务的累积,并且无法实现可扩展性,更不用说构建和维护解决方案所带来的机会成本。

  • 数据可观测性。数据可观测性指的是理解和衡量数据平台中不同数据资产及其组件健康状态的能力。由于数据熵代表了数据平台内部的混乱和混沌,数据可观测性解决方案应运而生,旨在解决这类问题。

    这篇博文中所解释的,数据可观测性是一个源自软件及控制理论的概念。软件可观测性的概念——比如 Datadog 和 New Relic 这样的公司——旨在帮助软件工程师更好地了解其应用程序内部的运行情况并监控其健康状态。现在这一概念可以应用到数据领域。因此,可观测性可以被定义为组织获得关于其数据健康状态的可操作见解的能力。正如我在这篇博文中所解释的,数据可观测性有四大支柱:

  • 指标:衡量数据质量

  • 元数据:访问和监控数据的外部特征

  • 谱系:映射数据资产之间的依赖关系

  • 日志:数据与“外部世界”互动的方式

    理解数据可观察性的最佳方式是将其视为现代数据基础设施的一个监督层,旨在使数据从业者能够洞察和理解构成现代企业数据平台的日益复杂的数据资产网络。通过连接到数据堆栈的每个部分,从数据摄取、ETL/ELT、建模、数据仓库,一直到 BI/分析、反向 ETL、机器学习等,数据可观察性解决方案应能够提供对数据资产在管道各阶段健康状态的可操作见解。

图片由作者提供

结论

数据熵是昂贵的。根据IBM的研究,数据质量差每年给美国经济造成高达 3.1 万亿美元的损失,更不用说它对组织竞争地位和声誉的影响了。数据中的熵是否不可避免?它是否是规模和扩展的直接结果?是否应该接受它?是的,是的,还是。面对不可避免的复杂性增加,领先的组织正在寻求切实的解决方案,以帮助他们应对数据熵,确保他们昂贵的软件和数据平台投资能够获得最佳的投资回报。数据可观察性作为应对数据熵的解决方案,通过提供数据资产健康状态的全面视图以及它们如何与平台内的数据交互,帮助数据从业者最终将时间投入到推动业务前进的有价值的任务中。

Julia 中的数据过滤:你需要知道的一切

原文:towardsdatascience.com/data-filtering-in-julia-everything-you-need-to-know-d0bf08a00328

当涉及到在 Julia 中进行数据过滤时,你需要知道的一切

Emma BoudreauTowards Data Science Emma Boudreau

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

--

图片由 Najib Kalil 提供,来源于 Unsplash

在数据科学中,进行假设检验、机器学习甚至分析时,获得结果的最重要因素是拥有良好的数据。数据通常需要满足多种不同的要求。数据过滤是一种非常常见且在数据领域中经常使用的技术。数据过滤可以是移除不属于的数据组件的过程,也可以是获取符合某些参数或多个参数的样本的过程。

我们可以说,当我们从数据中移除不属于该数据的内容时,比如从数据中去除缺失值,这就是一种数据过滤。这是数据科学过程中的一个重要步骤,通常通过过滤技术来完成。一个符合某些设定参数的样本的例子是,如果我们试图测试身高和撞头之间的统计显著性。我们会过滤掉所有身材矮小的数据,以便只留下高个子的数据进行测试。

这种技术有许多应用。过滤可能对于执行一些常见的数据科学任务也很重要,因此确实需要注意。幸运的是,在 Julia 中进行过滤相对简单。如果你想尝试本文中的代码,这里有一个以笔记本格式呈现的概述链接:

[## Emmetts-DS-NoteBooks/Julia/data filtering in julia.ipynb 在 master 分支 · emmettgb/Emmetts-DS-NoteBooks

各种项目的随机笔记本。通过创建帐户,参与 emmettgb/Emmetts-DS-NoteBooks 的开发……

github.com](https://github.com/emmettgb/Emmetts-DS-NoteBooks/blob/master/Julia/data filtering in julia.ipynb?source=post_page-----d0bf08a00328--------------------------------)

要真正理解 Julia 中的过滤,我们需要了解一些不同的内容。匿名函数 通常作为参数提供,尽管包括 do 语法在内的任何形式的函数作为参数都是可能的。在某些情况下,我们也可能使用一种叫做 **BitArray**Vector 类型。

BitArray

BitArray 是一个只包含 Bool 类型值的 Vector。在 Julia 中,Array 只是 Vector 的别名。通常在数学中,我们认为 Vector 特指一维数组……通常是一列特征。在 BitArray 的情况下,我们应该注意到它也可能被称为 BitVector。这是大多数按位布尔操作符在使用 . 进行广播时返回的结果,例如 .==

julia> y = [5, 10] .== [5, 10]
2-element BitVector:
 1
 1

julia> BitArray
BitArray

julia> BitVector
BitVector (alias for BitArray{1})

尽管这个类型与 Vector{Bool} 非常相似,但重要的是要记住,这种类型确实存在,因为它将是从操作符中返回的结果,例如。

匿名函数

在 Julia 中,匿名函数起初可能看起来有些难以理解,但一旦掌握了语法,它们相对简单。如果你想要阅读有关 Julia 中匿名函数的更深入概述,这里有一篇我写的文章:

## 什么是匿名函数?

揭示 Julia 中匿名函数和类型的方方面面

towardsdatascience.com

匿名函数与普通函数类似,只是函数没有在某个类型下定义。让我通过进一步解释 Julia 函数来使这个概念更清晰。每当我们在 Julia 中正常创建一个函数时,这个函数会被定义在一个名称下,并具有一个类型。这是一个单例,这个类型拥有与该方法类型(typeof)注册的方法。

function myfunc()

end
typeof(myfunc)

typeof(myfunc) (singleton type of function myfunc, subtype of Function)

我们可以使用 methods 方法来访问新函数的方法。

methods(myfunc)

# 1 method for generic function myfunc from [35mMain[39m:myfunc() in Main at In[4]:1

每当我们创建一个匿名函数时,函数名称会变得未定义——出于这个原因,我们只能为匿名函数拥有一个方法。如果我们只想让一个函数快速完成某事,例如过滤,这一点非常有用。要编写匿名函数,我们使用逻辑右操作符。在很多方面,这就像我们将正常函数声明中的 = 替换了一样。

f = x -> x += 5

f2(x) = x += 5

匿名函数的参数放在操作符的左侧,函数本身放在右侧。我们可以使用注释来使这一点更加清晰:

x::Int64 -> 5 +  x

Julia 的一个伟大之处在于语言的可扩展性。借助 Julia,所有模块都可以利用 Base 模块提供的函数来添加新方法。换句话说,模块可以无缝地与 Base 结合,并且通常像 Base 类型一样被处理。这意味着如果我们学习了这些方法如何与 Base 一起工作,我们可能会将很多这方面的知识带到其他模块中。今天我们将通过从 Base 开始,然后扩展到来自 DataFramesDataFrame 过滤不同结构,来演示这一点。

过滤基本类型

有几种不同的技术可以用来过滤简单的 Vector。我认为 Julia 中相对较新的一个特性是能够提供条件掩码作为索引。我不确定这个功能在 Base 中已经存在多久了,但这无疑是一个很棒的特性,因为我喜欢条件掩码。要创建条件掩码,我们需要制作一个之前提到的 BitArrays。在这种情况下,我们将再次广播一个比较操作符。在这里,我们将过滤出 x 中大于 14 的任何值:

x = [5, 10, 15, 20]
xmask = x .< 14
x[xmask]
2-element Vector{Int64}:
  5
 10

另外,我们可以利用 filter 方法。这些方法有 filterfilter!。这两个方法做完全相同的事情,唯一的区别是 filter! 是一个变异方法。这正是函数名中的 ! 所代表的含义。我觉得这是一个非常酷的标准,因为它确实使我们更容易区分何时数据被变异,何时没有。我认为这是一个非常值得了解的事情,特别是在数据科学方面。filter 方法提供了一个 Function 作为第一个位置参数,然后是我们的 Vector 作为第二个位置参数。如果类型不是 Vector,这种情况可能会有所变化,所以请记住这一点。

filter(x::Int64 -> x < 14, x)

2-element Vector{Int64}:
  5
 10

由于我们在这里使用的是 filter 而不是 filter!,我们需要将 x 设置为返回值以强制执行这些更改。我们还可以使用这种技术过滤字典。与其提供 Vector 中每个元素的类型,不如使用 Pair

mydict = Dict(:A => [5, 10], :B => [4, 10])

filter(k::Pair{Symbol, Vector{Int64}} -> k[2][1] != 5, mydict)

Dict{Symbol, Vector{Int64}} with 1 entry:
  :B => [4, 10]

因为函数是第一个位置参数,这也开启了利用 do 语法的能力,所以一定要记住这一点。

x = [5, 10, nothing, nothing, 40]

filter!(x) do number
    ~(isnothing(number))
end

3-element Vector{Union{Nothing, Int64}}:
  5
 10
 40

过滤数据框

另一种可能需要过滤的常见结构是DataFrame。这有点不同,因为它是一个依赖项和模块,而不仅仅是Base的一部分。

using DataFrames

df = DataFrame(:X => [1, 2, 3, 4], :Y => [1, 2, 3, 4])

当在DataFrame上使用filter方法时,它会向函数提供一个DataFrameRow。这是一个很酷的类型,我们可以很容易地对其进行索引,这使得过滤变得轻而易举。

filter!(df) do row
    if row[:X] > 3
        return(false)
    end
    true
end

这确实就是全部内容,结合Base中的现有知识,可能很难找到无法用这种技术过滤的内容!

总结

数据过滤是处理数据过程中的一个重要步骤。过滤技术使我们能够移除可能妨碍任何实际建模分析的观察数据。幸运的是,作为科学计算语言的 Julia 考虑了这一点,语言提供的Base工具对此效果很好!此外,Julia 的构建方式允许在Base中包含的函数添加额外的方法以增加功能。

这创造了我最喜欢的 Julia 特点之一。在编程世界中,熟悉某个生态系统或一组包是很常见的。例如,知道如何使用 NumPy 并不意味着知道如何使用 Dask。在 Julia 中情况略有不同,因为完全可能 Dask 或 NumPy 的等效方法会使用相同的方法。我们在DataFrames上的filter应用中可以看到这一点。这有助于节省单例的成本,也帮助包的用户重用旧知识。不用多说,Julia 是一个处理数据的出色语言——数据过滤的应用也不例外。

数据治理,但要将其作为团队运动

原文:towardsdatascience.com/data-governance-but-make-it-a-team-sport-30dc0164fb7c?source=collection_archive---------2-----------------------#2023-01-20

数据治理中没有 🙃

Maggie HaysTowards Data Science Maggie Hays

·

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

--

众所周知,我们正处于现代数据堆栈的黄金时代。现在接入和使用高度灵活、可扩展的数据工具从未如此快速和简单。我们敏捷!我们以数据驱动!我们正在实现自助服务、数据民主化的梦想!

图片由 Campaign Creators 提供,来源于 Unsplash

但每个优势背后都伴随有劣势:数据在不断增长的系统集合中越容易生成和消费,我们亲爱的现代数据堆栈就越快变成错综复杂的依赖网络。我们变得不那么灵活,反而花费越来越多的时间来处理不断增长的破损管道、转换逻辑、仪表板等积压问题。自助资源开始产生冲突的结果,挫败了我们的核心利益相关者,并在我们做出数据驱动决策的能力上播下了怀疑的种子。

不可避免地,这会转变为有关数据所有权、文档、标签、分类等的讨论;数据治理成为了可以解决一切的灵丹妙药!但我们应该从哪里开始?谁负责,负责什么?

听起来令人畏惧,对吧?其实不必如此!让我们通过一个迭代框架来引入数据治理。

步骤 1. 确定要解决的问题

首先,花时间清楚定义你希望通过数据治理实践解决的问题。例如:

  • 遵守不断变化的合规/监管要求需要耗时且冗余的手动审计数据源。我们没有标准的数据敏感性定义,因此结果会因审计人员的不同而有所变化。

  • 我们每月的数据存储和处理成本失控了。我们需要尽快削减成本,但不知道谁负责什么,也不知道它在组织中的使用情况。

  • 我们团队花费 20%的开发时间来修复由于数据质量不可靠而导致的损坏管道。数据质量问题应该在源头被发现,并由拥有数据源的团队解决。

让我们在深入框架时聚焦于第三个问题:

作者提供的图片

步骤 2. 设定明确的目标

现在你已经确定了要解决的目标问题,并有了如何解决的想法,是时候设定一些具体的目标了。

花时间深入思考当前的问题:其他人可以采取什么措施来解决这个问题?你可以为其他人设定什么目标?如果一切顺利,这项努力会带来什么可衡量的影响?

让我们继续以数据质量不可靠为例,我们希望数据质量问题在源头被发现,并由适当的团队解决。这意味着我们要设定两个目标:1)分配数据所有权,2)为所有资产建立数据质量测试覆盖。因此,我们期望看到解决破损数据管道的时间减少。

作者提供的图片

很简单,对吧?我们知道要解决什么问题,怎么解决它,以及如何衡量影响。让我们继续前进。

步骤 3. 从小处着手,然后再扩大

你还记得我们在第 2 步刚想到的解决方案吗?抑制一下将其一次性推广到所有数据资产的冲动。这一点尤其重要,当你从数千(或数十万!)资源开始,并将其委派给几十或上百个人时。

🤦🏼‍♀️提醒:将 180k 列名称直接放入 Google 表格,并期待整个工程团队因为你礼貌请求就同意添加文档,这 绝对是个糟糕的主意 。他们会嘲笑你,你每次想到这事时都会感到尴尬。 🙃🙃

相反,从高影响、低复杂性的数据资源子集和高度投入的利益相关者开始,与他们合作——这就是我们开始将数据治理变成团队运动的地方!

回到我们的数据质量问题例子:我们设定了一个非常高的目标,即将所有数据实体的所有权和数据质量测试分配给 100%。我们将避免一下子实现这个大目标,而是从一个狭窄的资源子集开始,这些资源是:

  • 由高度投入的利益相关者拥有

  • 频繁查询

  • 应用于业务关键的管道、模型、报告等

作者提供的图片

通过保持数据实体的狭窄范围,你将能够更专注于设计可重复的、跨团队的数据治理工作流程。

第 4 步:推动渐进行动

你已经完成了定义解决什么问题、目标结果以及处理哪些资源的工作;现在是时候启用其他人采取行动了。这是与初步利益相关者组紧密合作的机会,以测试这一举措,为未来的大规模推广提供信息。

在你启用其他人采取行动时,请记住以下一些问题:

  • 这明显吗? “为什么”应该对他们来说是显而易见的;应清楚为什么这项工作是有价值和有影响的

  • 这简单吗? “做什么”应该明确定义,以便他们可以专注于执行

  • 这是协作的吗? 不要过度规定“如何做”;专注于共同的最终目标,鼓励他们建立适合现有工作流程的过程/解决方案

最后但同样重要的是,创建快速反馈循环,并主动征求反馈。你的利益相关者在哪些方面缺乏背景?哪些行动对他们来说很困难?他们需要什么类型的持续支持?

回到我们不可靠的数据质量例子,我们已经要求高度投入的利益相关者正式承担所有权,并为他们最常查询的 25%业务关键数据资产添加数据质量测试。在这一步中,我们应专注于确保他们:

  • 理解数据质量测试的缺乏对下游资源有很大影响

  • 理解我们对数据所有者的期望

  • 能够轻松创建和监控范围内资源的数据质量测试

作者提供的图片

第 5 步:衡量进展并学习

既然你已经启动了跨团队的数据治理工作流,现在是时候衡量进展并确定哪些有效哪些无效了:

  • 你是否在朝着你的目标取得进展?

  • 目标仍然是正确的吗?

  • 你的利益相关者是否支持这一倡议?

  • 在向其他利益相关者推出之前,你需要改变/调整什么?

作者提供的图片

第 6 步。迭代!

你已经完成了最后一步!现在是时候从头开始,应用你所学到的知识,并在过程中完善这些工作流。

一些告别的想法

不要让完美成为善的敌人——即使你不能完美地治理你堆栈中的每一个数据实体,也不意味着治理一个子集没有价值。关注影响,而不是完美。

要习惯重新评估你的目标/预期结果,并根据需要帮助你的利益相关者调整方向。听取他们的反馈。记住,你是在让他们承担额外的工作,所以尽力让这份工作值得他们的时间(而不是因为我们曾经设定了一个目标,所以我们也许应该去做它。)

我很想知道你在组织中如何处理数据治理——让我们一起对这个框架进行迭代!你可以随时通过DataHubSlack与我联系——我迫不及待地想听到你的消息!

时间序列数据库的数据集成策略

原文:towardsdatascience.com/data-integration-strategies-for-time-series-databases-f96cab274820

探讨用于时间序列数据库(TSDB)的流行数据集成策略,包括 ETL、ELT 和 CDC

Yitaek HwangTowards Data Science Yitaek Hwang

·发表于Towards Data Science ·6 分钟阅读·2023 年 1 月 31 日

--

图片来源:fabioUnsplash

随着数字化转型扩展到更多行业,生成的数据点数量正呈指数增长。因此,如何从不同来源以各种格式和结构收集如此大量的数据成为数据工程团队的主要关注点。传统的数据集成方法主要集中在将高度结构化的数据整理到数据仓库中,这些方法难以应对新数据集的体量和异质性。

时间序列数据增加了额外的复杂性。由于时间的推移,每个时间序列数据点的价值会减少,因为数据的粒度在数据陈旧时会失去相关性。因此,团队必须仔细规划数据集成策略到时间序列数据库(TSDB)中,以确保分析能够反映接近实时的趋势和情况。

在这篇文章中,我们将审视一些最流行的时间序列数据库数据集成方法:

  • ETL(提取、转换、加载)

  • 提取、加载、转换(ELT)

  • 数据流处理与 CDC(变更数据捕捉)

鉴于时间序列数据对实时洞察的需求,现代事件驱动架构通常采用 CDC 进行数据流处理。为了展示其实际操作,我们将通过QuestDB(一个快速的 TSDB)来展示 CDC 如何灵活地满足时间序列数据源的需求。

提取、转换、加载(ETL)

ETL 是一种传统且流行的数据集成策略,它首先将数据转换成预定结构,然后再将数据加载到目标系统(通常是数据仓库)中。

作者提供的图片

ETL 的主要优势之一是它提供了最高程度的自定义。由于数据首先被提取到一个临时区域,在那里被转换为干净、标准化的格式,ETL 系统可以处理各种格式和结构。此外,一旦数据加载到数据仓库中,数据科学团队可以运行高效的查询和分析。最后,鉴于 ETL 生态系统的成熟,选择企业级工具的选项非常丰富。

另一方面,ETL 维护起来既耗时又费资源。数据清理和转换的逻辑可能复杂且计算开销大。这也是为什么大多数 ETL 系统通常是批处理导向的,只在周期性地将数据加载到仓库中。随着数据量和数据源的增加,这可能会成为瓶颈。

鉴于这些特性,ETL 系统通常用于需要在分析之前进行复杂转换逻辑的数据集。它也适合那些不需要实时洞察并可以长期存储用于趋势分析的数据集。

提取、加载、转换(ELT

正如名称所示,ELT 首先将数据加载到目标系统(通常是数据湖)中,并在系统内部进行转换。由于目标系统负责处理快速加载和转换,ELT 流水线通常利用现代的、基于云的数据湖,以应对处理需求。

作者提供的图片

ETL 流水线相比,ELT 系统可以提供更多实时的数据分析,因为原始数据会被实时摄取和转换。大多数基于云的数据湖提供 SDK 或端点,以高效摄取微批量数据并提供几乎无限的可扩展性。然而,ELT 并非没有缺点。由于转换由目标系统完成,这些操作受限于数据湖支持的功能。如果需要更复杂的转换逻辑,可能需要额外的步骤来重新提取数据并以更友好的格式存储。

对于大多数使用场景而言,ELT 是比 ETL 更高效的数据集成策略。如果你的应用程序可以利用基于云的工具并且不需要复杂的处理,ELT 可以是处理大量数据的极佳选择,接近实时完成处理。

更改数据捕获(CDC

对于新项目,团队可以计划将 TSDB 作为 ETL 或 ELT 管道中的目标系统之一。然而,对于现有项目,无论是迁移到 TSDB 还是将 TSDB 添加到系统中都可能面临挑战。一方面,可能需要修改或使用新的驱动程序/SDK 来将数据流式传输到 TSDB。即使支持相同的驱动程序,数据格式也可能需要更改以充分利用 TSDB 的功能。CDC 工具可以有效地弥合这一差距。

CDC 在原则上很简单:CDC 工具如 Debezium 持续监控源系统中的变化,并在发生变化时通知你的数据管道。引起变化的应用程序通常甚至不知道有 CDC 进程在监听变化。这使得 CDC 非常适合将新的实时数据管道集成到现有架构中,因为它对现有应用程序的更改很少或没有。因此,CDC 可以与 ETL 或 ELT 管道一起使用。例如,源系统可以是 SQL RDBMS(例如 MySQL、PostgreSQL 等)或 NoSQL 数据库(例如 MongoDB、Cassandra),而目标系统之一可以是 TSDB 以及其他数据湖或数据仓库。

图片来源:作者

使用 CDC(Change Data Capture)进行数据集成的主要优势在于它提供了实时数据复制。与传统的批量处理 ETL 和 ELT 系统不同,CDC 会将源系统的变化持续流式传输到一个或多个目标系统。这对于在多个数据库之间近实时地复制部分数据或全部数据非常有用。目标数据库甚至可能位于不同的地理区域或服务于不同的目的(即长期存储与实时分析)。对于时间序列数据,随着时间变化的数值通常最为有用,CDC 能有效捕捉这些增量数据,以便提供实时洞察。

CDC 的参考实现

为了更具体地说明 CDC 如何工作,我们可以看一下我最近写的参考实现,它将股票价格流式传输到 QuestDB。

图片来源:作者

在高层次上,一个 Java Spring 应用将股票价格信息发布到 PostgreSQL。然后 Debezium Connector 从 PostgreSQL 中读取这些变化,并将其发布到 Kafka。另一方面,QuestDB 的 Kafka Connector 从 Kafka 主题中读取数据,并将其流式传输到 QuestDB。

要深入了解,请参阅:

[## 使用 QuestDB 和 Debezium 进行变更数据捕获 | QuestDB:时间序列数据库]

在本教程中,我们的社区贡献者 Yitaek Hwang 向我们展示了如何使用变更数据流将数据传输到 QuestDB。

questdb.io

在这个参考架构中,Java Spring 应用程序在将数据复制到 TSDB 进行进一步分析之前,将数据转换并加载到 PostgreSQL 中。如果需要更类似 ELT 的管道,可以将来自其他提供商的原始数据直接加载到 PostgreSQL 中,并在 QuestDB 中进行转换。

这个架构中需要注意的关键点是,CDC 可以无缝集成到现有系统中。从应用程序的角度来看,它可以保留 PostgreSQL 的事务保证,同时在管道中添加一个新的 TSDB 组件。

结论

数据集成在使用 TSDB 来存储和分析时间序列数据的组织中扮演着重要角色。在本文中,我们探讨了使用 ETL 或 ELT 的一些优缺点。我们还研究了如何将 CDC 与这些管道结合使用,以实现对 TSDB 的实时复制。

考虑到时间序列数据的特殊性质,使用 TSDB 来正确存储和分析这些数据非常重要。如果你是从零开始,考虑建立一个 ELT 管道,将数据流入 TSDB。要与现有系统集成,可以利用 CDC 工具以减少对当前架构的干扰。

数据是语言模型的基础

原文:towardsdatascience.com/data-is-the-foundation-of-language-models-52e9f48c07f5

高质量的数据如何影响 LLM 训练流程的每个方面…

Cameron R. Wolfe, Ph.D.Towards Data Science Cameron R. Wolfe, Ph.D.

·发表于 Towards Data Science ·阅读时间 16 分钟·2023 年 10 月 29 日

--

(照片由 Joshua Sortino 提供,来源于 Unsplash

大型语言模型(LLMs)已经存在了一段时间,但直到最近,它们令人印象深刻的表现才引起了更广泛的人工智能社区的显著关注。考虑到这一点,我们可能开始质疑当前 LLM 运动的起源。究竟是什么使得近期模型相比于前辈如此令人印象深刻? 尽管有些人可能会提出多种不同因素,但一个尤其重要的进步是执行对齐的能力。换句话说,我们找到了训练 LLMs 的方法,不仅输出最可能的下一个词,而且输出能满足人类目标的文本,无论是通过遵循指令还是检索重要信息。

“我们假设对齐可以是一个简单的过程,其中模型学习与用户互动的风格或格式,以揭示在预训练期间已经获得的知识和能力”— 来自 [1]

本概述将研究对齐的作用和影响,以及对齐与预训练之间的相互作用。有趣的是,这些观点由最近的 LIMA 模型 [1] 探讨,该模型通过仅对 1,000 个高质量响应示例的半手动策划语料库进行预训练的 LLM 的微调来执行对齐。我们将了解到,对齐过程虽然关键,但主要教会 LLM 可操控性和正确的行为或风格,而大多数知识是在预训练期间获得的。因此,即使数据训练量极少,对齐也可以成功执行。然而,我们将看到数据质量和多样性对对齐以及 LLM 训练的其他方面(例如,预训练、微调等)的影响是巨大的。

LLM 训练流程

“LLMs 的训练分为两个阶段:(1)从原始文本中进行无监督预训练,以学习通用表示,和(2)大规模的指令调整和强化学习,以更好地对齐最终任务和用户偏好。” — 摘自 [1]

尽管最近几个月从各种不同的角度研究了语言模型,但这些模型的创建往往遵循标准化的过程,并具有一些共同的组件;见下文。

创建和完善 LLM 的多步骤过程(摘自 [11, 12])

这个过程的第一步——预训练——是通过语言建模目标在大规模的未标记文本语料库上训练模型,通常是最昂贵的步骤。之后,模型会经过一个对齐过程,包括监督微调(SFT)和/或来自人类反馈的强化学习(RLHF)。在模型训练完成后,可以将其部署到下游应用中,在那里可以利用进一步的微调或上下文学习来提高性能。在本节中,我们将概述这些组件,以更好地理解它们对大型语言模型(LLM)行为的影响。

语言模型预训练

(作者创建)

上述的预训练过程是创建大型语言模型(LLM)中计算最昂贵的一步。在此过程中,语言模型会接触到未标记的文本数据集,并使用标准的语言建模目标进行训练。简而言之,这意味着我们通过 i) 从数据集中采样一些文本以及 ii) 训练模型以预测下一个单词来训练模型。这一预训练过程是一种自监督学习形式,因为正确的“下一个”单词可以通过简单地查看数据集中的下一个单词来确定。考虑到数据集庞大(例如,~0.5–2 万亿标记 [13])且模型必须从头开始训练,预训练过程是广泛的。

对齐过程

(摘自 [11])

在预训练完成后,我们拥有一个“基础模型”,即一个尚未具备任何专门能力的通用 LLM。为了赋予这个模型进行有趣对话、遵循指令等能力,我们必须对齐这个模型,或者训练它以复制人类用户期望的行为。在大多数情况下,对齐过程基于两种主要技术:

  • 监督微调(SFT)

  • 人类反馈中的强化学习(RLHF)

这些技术可以单独使用,也可以通过依次执行将它们组合在一起,正如 InstructGPT [11](即 ChatGPT 的前身)最初提议的那样。

SFT 是一种简单的对齐方法——我们只需获取期望行为的示例,并直接在这些数据上对 LLM 进行微调(使用语言建模目标)。例如,如果我们想教一个基础 LLM 遵循指令,我们可以获取许多对基于指令的提示做出准确回应的示例,然后在这些示例上训练基础模型。我们将在本概述中重点讨论这一技术,它既简单又强大。然而,正如我们将看到的,使用 SFT 获得良好结果依赖于精心策划的数据集

(来自 [11])

RLHF 使我们能够根据人类提供的反馈来优化 LLM 的参数。从一组提示开始,我们首先使用 LLM 为每个提示生成多个潜在输出。根据这些输出,我们可以要求人工标注员对这些回应的质量进行排名(即,哪个输出是“最佳”),然后使用这些排名来训练奖励模型——这只是一个较小的 LLM,它根据模型的回应预测人类的偏好1;见上文。我们可以使用奖励模型的输出作为标量奖励,并通过 PPO 算法优化 LLM 以最大化这一奖励;见下文。

(来自 [11])

上述 RLHF 过程的美妙之处在于我们直接基于人类偏好来优化模型,但“偏好”可以捕捉各种不同的属性!例如,也许我们希望模型更好地遵循指令,输出更有趣的内容,甚至停止幻想(即,编造虚假信息)。所有这些行为都可以通过使用 RLHF 进行优化,使其成为一种非常强大的对齐工具;见下文。

(来自 [11])

对于那些不熟悉强化学习(RL)的人来说,没有额外背景阅读,RLHF 可能是一个难以理解的框架。为了获得更好的 RL 背景,我建议查看以下资源。

  • 强化学习基础 [link]

  • 近端策略优化 [link]

  • RLHF 概述 [link]

“模型的能力主要来自预训练过程——RLHF 并不会提升考试表现(如果没有积极的努力,它实际上会降低表现)。但模型的引导来自于后训练过程——基础模型需要提示工程才能知道它应该回答问题。” — 引自 GPT-4 博客

对齐的目的是什么? 对齐是一个极其活跃的研究领域。目前,研究社区正在讨论更好地理解对齐的角色/目的。在 GPT-4 的分析中,我们看到对齐技术如 RLHF 的作用是使 LLM 更易于引导和有趣,而不是教给模型新的信息。事实上,模型所拥有的大部分知识似乎来自预训练。在 [1] 中也提出了类似的观点,我们看到通过一个小型的精心策划的数据集进行 SFT 可以实现高质量的对齐。考虑到在对齐流行的专有模型如 GPT-4 上投入的大量人力和计算资源,这一结果尤其有趣。

应用 LLM

与 LLM 的不同学习类型(由作者创建)

一旦一个 LLM 完成了预训练和对齐,它就(或多或少)准备好用于下游应用。然而,我们必须采取一些措施来确保 LLM 能够准确解决特定任务。通常,这可以通过 i) 进一步微调模型或 ii) 使用上下文学习来完成;见上文。

领域特定的微调。 如果我们将 LLM 部署到一个专业领域(例如医疗、法律、软件等),那么进一步微调模型以适应该领域的数据类型可能是有意义的。这个过程非常简单。我们继续使用语言建模目标训练模型,但使用领域特定的语料库(即与所需应用中将看到的数据类似的数据)来代替预训练数据集。

上下文学习。 一旦我们准备好部署模型(无论是否经过领域特定的微调),我们应利用上下文学习,这种方法使用文本提示来指导/引导模型朝向期望的行为,以更准确地解决下游任务。这些提示可能包括正确解决方案的示例(即少量示例),但这些数据仅作为生成输出时的上下文使用(即,我们不用于训练)。我们在之前的综述中已经广泛探讨了提示方法。

  • 实用提示工程 [link]

  • 高级提示工程 [link]

LIMA: 对齐中的少即是多

在 [1] 中,作者通过训练 LIMA(一种 LLaMA-65B [2] 的衍生模型),对比了预训练与对齐的相对重要性。LIMA 经过了针对策划对齐数据集的 SFT(没有 RLHF)。特别是,LIMA 使用的微调数据集是由 1,000 个精心策划的提示和回答示例组成,这些示例具有相似的输出风格和多样化的输入;见下文。

(来自 [1])

当在这些示例上训练时,我们看到 LIMA 的表现非常好,甚至接近于最先进的专有模型,如 GPT-4 和 Claude。这样的结果表明,通过少量精心选择的示例,语言模型可以被有效对齐,从而强调了数据质量和多样性在训练和对齐强大语言模型中的作用

“一个模型的知识和能力几乎完全是在预训练期间学到的,而对齐则教会它在与用户交互时应该使用哪些格式的子分布。” — 来自 [1]

肤浅对齐假说。 在这方面,[1] 中的作者提出了肤浅对齐假说(SAH),其总结见上述引文。大多数大型语言模型(LLM)的核心知识是在预训练期间学习的,而对齐则寻找展示这些知识的适当格式或风格。SAH 简单地表示,给定一组具有足够质量和多样性的示例,对齐可以以数据高效的方式学习。

数据策划以实现对齐

[1] 中用于对齐的数据集是由社区问答论坛(例如 StackExchange、wikiHow 和 Reddit)与手动编写的示例结合构建的。与近期试图自动化 SFT 数据策划的工作不同(例如,Self-Instruct),我们在 [1] 中看到,这两种来源的数据都经过了仔细(且通常是手动)筛选,以确保质量和多样性。尽管手动策划需要时间,但它提升了最终数据集的质量,而这种质量被发现非常有益。

(来自 [1])

数据来源。 LIMA 的训练数据的详细信息见上表。在训练集中,750 个样本来自社区问答论坛。为了确保数据质量,这些样本要么经过人工筛选,要么通过“点赞”指标进行筛选。剩余的 250 个样本由作者手动编写——其中 200 个是从零开始创建的,50 个则来自SuperNaturalInstructions。在手动创建数据时,作者通过确保以下几点来最大化数据的多样性和一致性:

  • 回应的风格与有用的 AI 代理的行为保持一致

  • 提示尽可能多样化

简而言之,我们希望尽量减少对齐数据集中噪声的数量(即,确保风格、语气、格式等的一致性),同时确保 LLM 观察到的数据具有尽可能多的多样性和覆盖范围。值得注意的是,[1]中的作者甚至在对齐数据中包括了一些恶意提示,以展示如何避免潜在的有害命令。

我们能自动化吗? 在关于开源 LLM 的模仿学习的近期工作中,我们通常会看到用于微调的数据是自动策划的。例如,SFT 的数据可以从在线来源(例如,ShareGPT)下载或直接从 LLM API 获取(如 ChatGPT 或 GPT-4)。与手动策展相比,这种方法效率极高,并且(在某些情况下)表现良好;例如,Orca [3] 在从 ChatGPT/GPT-4 获得的大量对话中进行训练,表现相当好(甚至与顶级模型相比)。然而,在[4]中我们看到,以这种方式训练的模型通常存在局限性,并在经过广泛分析时表现较差。

“手动创建多样化的提示并以一致的风格撰写丰富的回应是劳动密集型的。虽然一些近期工作通过蒸馏和其他自动化手段避免了手动劳动,优化数量而非质量,但这项工作探索了投资于多样性和质量的效果。” — 来自[1]

在[1]中,我们研究了一种对齐的替代方法,即投入到高质量数据的策展中。与其自动获取大量数据,不如手动筛选和选择较少的示例。这种较小规模(但劳动密集型)的选择过程使数据的多样性和质量得以控制,这说明了数据规模和质量对 LLM 对齐的影响

对齐实际上是肤浅的吗?

(来自[1])

LIMA 的表现与各种不同语言模型的表现进行了比较。特别地,我们将 LIMA 的表现与 Alpaca-65B [6]、DaVinci003(即经过 RLHF 调优的 GPT-3.5 版本)、Bard(即基于 PaLM [7])、Claude(即通过 AI 反馈训练的 52B 参数 LLM [8])和 GPT-4 进行了比较。评估使用了众包工作者和来自 GPT-4 的自动反馈,如上图所示。有趣的是,LIMA 的表现始终优于 Alpaca(尽管其在52X少量数据上进行了微调!),甚至在相当多的情况下与 Claude 和 GPT-4 的质量相当或超越。考虑到这些其他模型在数百万用户提示和反馈下进行训练,LIMA 的竞争力表现令人印象深刻。

绝对性能。 除了上面进行的模型比较试验外,[1]中的作者还手动评估了 LIMA 生成的 50 个随机响应的质量。有趣的是,我们发现 LIMA 仅在 50 个提示中的六个中未能作答,并且在 50%的试验中产生了优秀的响应;见下文。

(来自 [1])

当这种手动评估仅在分布外的提示上重复进行(即那些与微调集中包含的例子差异较大的提示),结果差异不大——20% 的响应失败,35% 的响应合格,45% 的响应优秀。这种结果表明 LIMA 实际上具有较好的泛化能力,而不仅仅是记忆或过拟合于策划的微调数据集3

(来自 [1])

例如,LIMA 在多轮对话方面表现得相对较好(但不出色),尽管在其微调数据集中没有这样的例子。当模型仅接触到 30 个多轮对话例子时,我们看到 LIMA 能够快速学习如何从这些例子中维持对话;见上文。

(来自 [1])

数据的有用属性。 除了上述主要结果外,我们在 [1] 的消融实验中看到,用于对齐的样本的多样性和质量非常重要;见上文。值得注意的是,仅仅增加微调数据集的大小并不总是能提高 LLM 的性能。因此,我们从 [1] 中了解到,高质量数据的精心策划对于对齐是非常有价值的。

更大的图景

在最近的开源语言模型研究中,我们看到了一系列不同的 LLM(例如,Alpaca [6]、Vicuna [9]、Koala [10] 等)采用了自动化的数据策划方法进行 SFT。特别是,这些模型使用了一种模仿方法,其中 i) 从其他 LLM 收集大量对话,ii) 对这些数据进行监督微调。虽然这些模型最初表现得相当不错,但我们在更有针对性的评估中看到它们的对齐质量较差。考虑到这一点,我们可以合理地问:是什么使得 LIMA 的方法更有效?

(来自 [4])

质量 > 数量。 即使在模仿模型的研究中,我们也发现仅仅增加微调集中的数据量对模型性能的提升影响有限;见上文。我们在 [1] 中看到类似的结果对于 LIMA 也是如此。鉴于仅增加数据量没有好处,我们有几个不同的选项来提升 LLM 的性能:

  1. 创建一个更强大的基础模型

  2. 改进对齐数据集

虽然一些工作(例如 MPT 和 Falcon)已经探讨了更好的基础模型的创建,但 LIMA 研究了如何创建更好的对齐数据集4。简单来说,我们在 [1] 中看到,创建一个既多样化又高质量的对齐数据集(即使它很小!)是极其有效的。LLMs 可以根据最少的数据准确地学习模拟某些行为,这支持了 SAH。

“为了对齐的目的,扩大输入多样性和输出质量具有可测量的积极效果,而单独扩大数量可能没有。” — 来自 [1]

但这些结果并不完美! 之前的模仿模型最初被认为表现非常出色,甚至可以与像 ChatGPT 这样的顶级专有模型相媲美。然而,我们后来发现这些结论是由于人为错误所致。这些模型模仿了专有 LLMS 的风格,但缺乏其真实性,并且在训练集之外的泛化表现较差,这使得在评估这些模型时,人类更难以推断。鉴于 LIMA 也是主要通过众包工人进行评估的,[1]中的结果也存在类似的局限性。然而,我们看到 LIMA 往往具有良好的泛化能力,并且常常优于像 Alpaca 这样的模仿模型,这表明高质量的对齐数据仍然对 LLM 性能非常有益。

数据对齐之外的影响

在 [1] 中,我们看到数据的质量对于有效地对齐语言模型极其重要。然而,数据质量和多样性的重要性不仅仅局限于对齐 —— 数据的类型和质量会影响 LLM 训练流程的各个方面。让我们来看几个参考例子。

预训练。 在各种不同的模型中,我们看到用于预训练的数据质量至关重要。例如,在 Galactica [14] 中,作者发现使用较小的、高度策划的高质量科学信息数据集进行训练可以获得最佳性能。类似地,BioMedLM 模型是在较小的、策划过的技术内容语料库上进行预训练的。最后,Falcon-40B —— 目前开源语言模型的最先进水平 —— 给予了预训练数据质量显著的重视,我们看到作者投入了大量精力来开发一个新颖且高效的管道,从网络中提取高质量的预训练数据。

对齐。 除了[1]中探讨的方法外,最近提出的 Orca 模型[3]重点研究了数据质量在解决对齐问题中的作用。然而,采用了一种略有不同的方法。即,作者使用模仿方法训练模型,但将用于 SFT 的数据(即与其他 LLM 的对话示例)与模型关于每个问题如何解决的详细信息进行增强。包括这些额外细节在对齐数据集中被发现能够生成比像 Alpaca[6]或 Vicuna[9]这样的模型更为强健的模仿模型。

上下文学习。 除了对 LLM 进行训练和微调外,用于上下文/少量示例学习的数据可以极大地影响性能。特别是,最近关于少量示例学习的研究表明,示例的排序、分布或格式等因素都可能影响模型的表现。进一步而言,我们看到数据的多样性是极其重要的,使用多样化的少量示例提示模型通常表现得更好。

结束语

“这些结果强烈表明,大型语言模型中的几乎所有知识都在预训练期间学习,只有有限的指令调优数据才有必要教会模型生成高质量的输出。” — 来自[1]

本概述中涉及的主要结论有两点:

  1. 表面对齐假设:LLMs 在预训练期间学习他们的知识,而对齐则教会它们如何与用户进行适当的互动。

  2. 数据的质量和多样性对对齐过程至关重要(比数据规模更为重要)。

在[1]中,我们在创建 LIMA 的过程中观察到了这些主要结论,其中高质量的对齐可以通过较小的、精心策划的语料库来实现。如果数据的质量足够高,即输入提示多样且响应具有标准化的结构和语气,则对齐(使用 SFT)所需的数据量不多。然而,高质量数据的积极影响远远超出了对齐——所有 LLM 训练的方面都从使用高质量数据中受益。无论是在预训练还是上下文学习中,语言模型仍然基本遵循与其他所有机器学习模型相同的基本规则:“垃圾进,垃圾出”。

与我联系!

非常感谢您阅读这篇文章。我是 Cameron R. WolfeRebuy 的 AI 总监。我研究深度学习的经验和理论基础。如果您喜欢这个概述,可以订阅我的 Deep (Learning) Focus 新闻通讯,我会通过从基础开始的相关主题概述来帮助读者理解 AI 研究。您还可以在 XLinkedIn 上关注我,或查看我在 medium 上的 其他著作

参考文献

[1] Zhou, Chunting 等人. “Lima: 对齐问题中的简约主义。” arXiv 预印本 arXiv:2305.11206 (2023)。

[2] Touvron, Hugo 等人. “Llama: 开放和高效的基础语言模型。” arXiv 预印本 arXiv:2302.13971 (2023)。

[3] Mukherjee, Subhabrata 等人. “Orca: 通过 GPT-4 的复杂解释轨迹进行渐进学习。” arXiv 预印本 arXiv:2306.02707 (2023)。

[4] Gudibande, Arnav 等人. “模仿专有语言模型的虚假承诺。” arXiv 预印本 arXiv:2305.15717 (2023)。

[5] Wang, Yizhong 等人. “Super-naturalinstructions: 通过声明性指令在 1600+ NLP 任务中进行泛化。” arXiv 预印本 arXiv:2204.07705 (2022)。

[6] Taori, Rohan 等人. “Stanford Alpaca: 一个跟随指令的 LLaMA 模型。” (2023)。

[7] Chowdhery, Aakanksha 等人. “Palm: 通过路径扩展语言建模。” arXiv 预印本 arXiv:2204.02311 (2022)。

[8] Bai, Yuntao 等人. “Constitutional ai: 从 AI 反馈中获得的无害性。” arXiv 预印本 arXiv:2212.08073 (2022)。

[9] Chiang, Wei-Lin 等人. “Vicuna: 一个开源聊天机器人,以 90%* ChatGPT 质量打动 GPT-4。” (2023)。

[10] Geng, Xinyang 等人. “Koala: 一个用于学术研究的对话模型。” (2023)。

[11] Ouyang, Long 等人. “训练语言模型以通过人类反馈遵循指令。” 神经信息处理系统进展 35 (2022): 27730–27744。

[12] Glaese, Amelia 等人. “通过针对性的人类评判改善对话代理的对齐。” arXiv 预印本 arXiv:2209.14375 (2022)。

[13] Hoffmann, Jordan 等人. “训练计算最优的大型语言模型。” arXiv 预印本 arXiv:2203.15556 (2022)。

[14] Taylor, Ross 等人. “Galactica: 一个用于科学的大型语言模型。” arXiv 预印本 arXiv:2211.09085 (2022)。

解读 2023 年数据职位市场:数据是否表明过度饱和还是机会?

原文:towardsdatascience.com/data-job-market-outlook-oversaturated-good-career-now-60a111388897

从我听到的一切来看,数据职位市场的情况非常严峻。为了了解目前数据专业人员的就业前景,我进行了对数据职位市场趋势、挑战和前景的深入分析。

Ken JeeTowards Data Science Ken Jee

·发表在 Towards Data Science ·8 分钟阅读·2023 年 11 月 18 日

--

图片由 作者 提供

目录

• FAANG 与非 FAANG

• 当前职位形势

• • • 职位发布按角色

• • • 按资历发布的职位

• • • 全球职位发布

• • • 全职与非全职

• 现在谁在招聘?

• 这意味着什么?

• 参考资料

我到处看到,似乎人们很难找到数据职位。在 Reddit 和我的 YouTube 评论区,我看到从初学者到资深专家的每个人都在为找到合适的职位而挣扎。

图片由作者提供

图片由作者提供

这引出了一个问题:数据职位市场有多糟糕?在这个领域找到工作是否绝望,还是比许多人所认为的更有希望?

图片由作者提供

让我们深入数据中看看。我将研究市场中的总体趋势。雇主现在招聘哪些类型的数据职位,并且这些职位是否适合目前的职业生涯?甚至最后哪些公司在招聘。因此,你会想要继续关注。

与数据职位市场相关的数据非常难以找到。所以,请耐心等待。我尽我所能从不同的资源中搜集数据。我找到的第一个职位市场数据来自一个 Interview Query 博客,下面的参考文献中有链接。虽然它只涵盖到今年三月,但它仍然有助于描绘数据角色的年同比市场状况。

FAANG 与非 FAANG

从 2022 年 3 月到 2023 年 3 月,我们看到大科技公司(即 FAANG 或 MANGA 缩写)发布的职位减少了约 90%。另一方面,其他公司实际上增加了招聘,改善了大约 7%。

市场上总的职位发布数量与去年大致相同。这是因为非 FAANG 公司占总职位的比例远远大于 FAANG 公司的比例(即,数以万计对比数以百计)。

当前职位市场

我相信你在想,“Ken,这些是旧数据。现在怎么样?我敢打赌,自三月以来市场已经完全崩溃了。”这时我从 Luke Barousse 那里获得的一些优秀数据就派上用场了。如果你想自己玩这些数据,这里是他的工具 datanerd.tech

我们可以查看 Interview Query 数据的终点,看看市场自那时以来如何变化。从 Luke 的数据来看,自三月以来,职位市场确实经历了一段下滑,直到五月。然而,现在看起来它开始恢复。

作者提供的图片

如果我们批判性地看待这一点,我们可以看到,市场上的负面情绪可能是五月大幅低点的遗留影响。

作者提供的图片

对于仍在市场上的人来说,这可能是个好兆头,因为它似乎正在试图纠正。

作者提供的图片

我想指出,Luke 和 Interview Query 的数据来自不同来源,因此这些数据之间显然存在差异。

然而,这仍然是我能找到的最佳质量数据。现在让我们根据各种特征来查看这些数据。

按角色发布的职位

这可能令人惊讶,但数据工程职位的机会远多于数据科学家或数据分析师职位。这些角色似乎都在经历一个与四月或五月低点之后相同的上升趋势。

作者提供的图片

就像许多公司似乎永远不够数据工程师一样,我们对 Medium 算法的点赞也永远不够。因此,如果你觉得这很有用,请随意点一个。

按资历发布的职位

让我们也来探讨一下招聘职位的资历要求。不知道这是否令人惊讶,但市场上招聘的非高级职位显著增多。这适用于数据工程师、数据分析师和数据科学家。

作者提供的图片

不幸的是,这些数据没有显示市场另一侧的需求。因此,如果你申请的是初级职位且感觉市场饱和,那很可能是因为入门级人才的供应远大于高级人才。

快速在 Google Trends 上搜索入门级数据职位,传闻显示这些职位的兴趣供应自年初以来可能在增加。

作者提供的图片

这可能解释了在获得入门级数据职位时的一些困难,特别是在今年的 5 月。

作者提供的图片

全球职位发布

我认为很多人可能会说,“这对美国来说是正确的,但对我所在的地方不一定如此。我所在地区的就业市场仍然很糟糕。”

那么,我们来看一下其他一些常见的数据职位地点。诚然,这些数据来自全球职位发布网站,而非本地网站,所以可能并不完全具有代表性。

这是自去年 12 月以来,发布职位最多的前 10 个国家的职位列表。

作者提供的图片

显然,美国及前 10 名之外的所有其他国家,总体上承担了大量的职位,但我们来探讨一下这些其他国家的数据职位市场。

再次看起来,5 月确实是几乎所有国家的底部点,大多数国家的情况都有相当好的反弹。

作者提供的图片

全职与非全职

现在,我还想探讨一个小假设。所以,让我们来看一下非全职职位,因为我知道你们中的许多人正在申请实习和兼职工作。

作者提供的图片

尽管合同职位在 7 月确实出现了下滑,但这很可能与六个月合同职位的填补或移除有关,影响了下半年的情况。

我发现看到合同或兼职工作的稳定性在最大的经济低谷期间很有趣,因为许多人认为合同工作比全职工作不那么可靠。

如果你想要一篇关于我对现代承包商重新定义的看法的文章,请在评论区告诉我。

现在,谁在招聘?

我们从 Interview Query 数据中看到,大型科技公司已经大幅降低了他们的招聘信息。现在,哪些公司对职位的需求最大?目前,这些是招聘职位最多的前 20 家公司。看来这些主要是大型公司,许多位于咨询和金融服务领域。

图片由作者提供

你会注意到,FAANG 公司并不在前 20 名之内。实际上,唯一进入前 100 名的是苹果和亚马逊,分别位列第 33 位和第 95 位。

这到底意味着什么?

尽管我们总是希望有更高质量的数据,但我认为这些招聘信息确实讲述了一个有趣的故事。

五月份的就业市场确实变得相当糟糕,但我看到的所有迹象表明,它几乎在各个领域都在回暖。

图片由作者提供

对我来说,这表明这可能是一个周期性的下行,而不是结构性的(至少,目前我没有任何关于角色需求的结构性变化的证据)。这预示着未来可能会有复苏。

相较于大型科技公司,行业、金融和咨询领域对数据人才的总体需求似乎更大,同时数据领域内不同角色的需求存在差异,这更倾向于数据工程。

如果你正在找工作,并且对未来感到压力重重,你应该准备好适应机会更丰富的领域,通过获得核心技能之外的技能,并考虑多样化的行业来拓宽你的工作前景。此外,建立一个强大的网络可以在困难时期提供见解、职位线索和支持。

希望这篇文章能更全面、数据驱动地描绘发生的情况。根据我从市场数据中看到的情况,我对这个领域许多情况的改善感到更加乐观。

下次再见,祝你在数据科学的旅程中好运。

参考资料

如果你喜欢这篇文章,记得在 Medium 上关注我以获取更多类似内容,或通过邮件订阅我。你还可以将这篇文章分享并推荐给你网络中对数据科学感兴趣的人!

如果你喜欢关于数据科学、机器学习和人工智能的有趣且富有信息的视频,请查看我的 YouTube 频道,在那里我提供评论、教程和其他教育视频。

要获取每周的内容更新以及有关数据科学行业的额外学习资源,请注册我的新闻通讯,Data Dribble!

数据泄漏:它是什么,为什么会导致我们的预测系统失败

原文:towardsdatascience.com/data-leakage-what-it-is-and-why-it-causes-our-predictive-systems-to-fail-4e5ed008489b

数据泄漏与过拟合/欠拟合一起,代表了机器学习项目在生产中失败的主要原因

Andrea D'AgostinoTowards Data Science Andrea D'Agostino

·发表于 Towards Data Science ·阅读时间 5 分钟·2023 年 8 月 4 日

--

图片由 Grianghraf 提供,来源于 Unsplash

数据泄漏无疑是一个威胁,困扰着数据科学家,无论其资历如何。

这是一个可能影响每个人的现象——即使是拥有多年经验的专业人士也会受到影响。

与过拟合/欠拟合一起,它代表了机器学习项目在生产中失败的主要原因。

数据泄漏发生在训练集中的信息泄漏到评估集(无论是验证集还是测试集)时。

但为什么数据泄漏会造成如此多的受害者?

因为即使经过了许多实验和评估阶段,我们的模型在生产环境中仍可能会失败得很惨。

避免数据泄漏并不容易。希望通过这篇文章,你能理解为什么以及如何在你的项目中避免它!

数据泄漏的示例

这里有一个示例,可能对你理解数据泄漏有帮助。

想象一下,我们是应用 AI 的开发者,受雇于一家大规模生产儿童玩具的公司。

我们的任务是创建一个机器学习模型,以识别一个玩具是否会在销售后的 3 天内收到退款请求。

我们从工厂接收数据,以图像的形式捕捉玩具在罐装前的状态。

图片由 Jerry Wang 提供,来源于 Unsplash

我们使用这些图像来训练我们的模型,该模型在交叉验证和测试集上表现非常好。

我们交付了模型,并且在第一个月客户报告只有 5%的玩具退款请求是由于缺陷。

在第二个月,我们准备对模型进行重新训练。工厂向我们发送了更多照片,我们用这些照片来扩充初始训练数据集。

再次,模型在交叉验证和测试中表现良好。

然而,这次我们收到的通信表示客户提出了请求,并且 90%的这些请求涉及到一个有缺陷的玩具。

我们查看这些照片……并注意到客户在最后一批中发送的照片显示了第一月内退货的玩具。

工厂提供的新照片不经意间包含了玩具销售后三天内的退款请求信息。

基本上,客户在申请退款后向我们发送了精选的图像,因此这些图像捕捉了已退货玩具的特定特征。这可能包括客户发现的可见损坏或明显缺陷,并导致了退款请求。

因此,我们理解模型在识别特定缺陷方面非常出色,从而在开发中表现良好,但在生产中表现不佳。

我们必须联系客户,解释情况,并可能需要清理训练数据集。

尽管其重要性,数据泄漏的概念与过拟合相比鲜有提及。这使得它对新兴的数据科学家更具危险性。

数据泄漏的常见原因

现在,让我们看看导致数据泄漏的一些最常见原因。

1. 时间线随机拆分,而不是按时间拆分

我们通常被教导随机拆分训练、验证和测试数据集是正确的选择。

然而,我们的数据很可能在时间上相关,这意味着时间变量会影响我们标签的分布。

比如说一个来自股市的时间序列。

如果我们随机拆分数据集,数据泄漏的概率将达到 100%。

这是因为接下来的数据会随机混入训练数据集中。我们的模型将接触到“正确”的标签,而无需学习它们。

这有点像是学校里的孩子拥有了测试的正确答案。表现优秀,但知识水平很低。

为了避免问题,基于时间拆分数据是有用的:例如,如果我们有一个月的数据,我们在前 20 天上训练模型,在剩余的 10 天上进行测试,按顺序进行。

2. 在拆分之前转换数据

这是数据科学新手中最常见的原因之一。

这种错误是如何表现的

from sklearn import model_selection
from sklearn import preprocessing

# ...

scaler = preprocessing.StandardScaler()
data = scaler.fit_transform(data)

train, test = model_selection.train_test_split(
  data, 
  test_size=0.2, 
  random_state=42
)

# ...

这里的错误是我们在将数据拆分成训练集和测试集之前对所有数据应用了缩放。

在这种情况下,缩放器对象需要知道它所应用的数据集的均值和标准差。

通过提供整个数据集,它也会存储和使用测试集中的信息,这将改变均值和标准差。

为了解决这个问题,总是在应用诸如缩放之类的转换之前先划分数据。

3. 用测试集中的信息填补缺失数据

有点类似于上面的观点,但从另一个角度看。

插补缺失数据的一种常见方法是用列中所有数据的均值或中位数填充单元格。

如果均值也基于测试集中的值进行计算,我们就在训练集中生成了泄漏。

再次提醒,我们在应用插补之前需要先划分数据。

4. 未能移除重复项

如果我们在数据集中有重复记录,那么这些记录有可能同时出现在训练集和测试集中。

因此,数据泄漏。我们的模型将显然能很好地预测这些重复项的值,显著降低预测误差(但这并不正确)。

为了避免这个错误,你必须在进行训练-测试划分之前移除重复项

如果我们进行过采样,即创建人工重复项以在不平衡的数据集上训练模型,那么我们需要在划分数据后再进行过采样。

5. 错误的数据生成过程

就像上面的玩具工厂例子一样。

有时,泄漏来自于我们需要训练模型的数据是如何生成和交付的

这种情况是不可避免的。我们只能保持警惕,提出问题,不要掉以轻心,尤其是在我们无法控制数据生成过程的情况下,就像玩具工厂的例子一样。

一般建议是,如果数据不是由数据科学团队处理/交付的,应该总是检查数据。

结论

感谢你读到这里。

你已经了解了数据泄漏是什么,以及为什么即使对于经验丰富的数据科学家来说,它也是如此难以管理。

在这种情况下,问题出在细节上,尤其是对于新程序员来说,这些细节在编码时很容易被忽视。

我给你展示的例子应该可以帮助你评估你的项目是否遇到了数据泄漏。

通常,如果你在开发中发现性能非常高,一定要检查是否存在数据泄漏。

我们绝不希望我们的模型在生产环境中失败!

下次见,

安德鲁

《使用 Python 和 SQL 掌握数据:通过 4 个战略应用案例释放效率和安全性》

原文:towardsdatascience.com/data-mastery-with-python-and-sql-unleashing-efficiency-and-security-through-4-strategic-use-cases-eb8afb5019a0

利用技术提取标签、抑制警告,并使用参数化查询进行强大的数据分析

Subha GanapathiTowards Data Science Subha Ganapathi

·发表于 Towards Data Science ·阅读时长 9 分钟·2023 年 5 月 23 日

--

照片由 Kevin Canlas 提供,来源于 Unsplash

介绍

数据分析和管理是任何现代企业运营的重要组成部分。为了有效利用数据的力量,专业人员依赖于编程语言和工具的组合,以实现高效的数据处理、操作和分析。本文探讨了 Python 和 SQL 的惊人能力,这两种基础语言被数据分析师和科学家广泛应用于有效决策。

众所周知,Python 提供了大量的库和框架来应对各种数据相关的挑战。Python 和 SQL 组合成了一个强大的双雄,使数据专业人员能够充分挖掘数据的潜力,更有效地挖掘数据库。

在本文中,我们深入探讨了四个独特的应用案例,展示了 Python 和 SQL 在应对各种数据挑战中的有效性和协同作用。每个应用案例代表了一个独特的场景,展现了 Python 灵活性与 SQL 查询能力的结合效果。

让我们深入了解!

应用案例

应用案例 1:提升在 Python 中编写的 SQL 查询的可读性

想象一下你正在使用 Web API 连接到云数据库,比如 GridDB,并通过执行 SQL 查询来检索数据。在构建 HTTP 请求体时,特别是对于接受 JSON 负载的 API 端点,SQL 查询需要被包含在请求体中。但真正的挑战在于实际的查询。

实际的 SQL 查询往往变得更加复杂,通常很难维持适当的缩进,添加换行符,以及格式化代码以提高可读性。此外,当被限制在 Python 笔记本(如 VScode 或 Jupyter)中编写单行查询时,很难添加有用的注释行来解释代码的功能。这样的查询的笔记本在长期维护和调试时会变得困难。

以下解决方案允许你在多行中编写 SQL 查询,这样可以提高代码的可读性和可维护性。通过使用适当的换行和缩进,你可以轻松组织和理解复杂的查询,而不会牺牲清晰度。

这里有一种更好的方法来编写 Python 中的 SQL 查询。

sql_query1 = (f"""
-- Query to classify sales data by region, category, and sales classification

SELECT region,
    CASE WHEN lower(category) LIKE 'a%' THEN 'Category A'
         WHEN lower(category) LIKE 'b%' THEN 'Category B'
         WHEN lower(category) LIKE 'c%' THEN 'Category C'
         ELSE 'Other Categories'
    END AS category_classification,
    CASE WHEN subquery.total_sales BETWEEN 1 AND 1000 THEN 'Low Sales' 
         WHEN subquery.total_sales BETWEEN 1001 AND 5000 THEN 'Medium Sales'
         WHEN subquery.total_sales > 5000 THEN 'High Sales'
    END AS sales_classification
FROM Sales_Dataset
JOIN (
    SELECT region, SUM(sales) AS total_sales
    FROM Sales_Dataset
    GROUP BY region
) AS subquery
ON Sales_Dataset.region = subquery.region
GROUP BY 1, 2
ORDER BY 3 DESC
""")

这可以通过 Python 的 JSON 库传递给请求体,如下所示 -

import json
# Construct the request body
request_body = json.dumps([
    {
        "type": "sql-select",
        "stmt": sql_query1
    }
])

# Validate the constructed request body
print(request_body)

它创建了一个包含单个项目的 JSON 对象,该项目包含操作类型(“sql-select”)和 SQL 查询语句(sql_query1)。使用 json.dumps() 函数将 Python 字典转换为 JSON 字符串表示。

然后你可以使用 requests 库继续发出请求。

data_req1 = requests.post(url, data=request_body, headers=header_obj)

用例 2: 从文本列中提取标签(hashtags)

在分析 TikTok 和 Instagram 等社交媒体平台时,通常会通过 API 提取数据并将其存储在像 Azure 或 Redshift 这样的数据库中。然而,API 响应通常包括作为字符串的内容,标签分散在视频标题中。为了解决这个问题,可以使用以下查询从文本列(如视频标题)中提取标签。

select * from 
(SELECT distinct TRIM(SPLIT_PART(title, '#', num)) AS hashtag
FROM social_media_video_info
CROSS JOIN (
    SELECT 1 AS num UNION ALL
    SELECT 2 AS num UNION ALL
    SELECT 3 AS num UNION ALL
    SELECT 4 AS num UNION ALL
    SELECT 5 AS num UNION ALL
    SELECT 6 AS num UNION ALL
    SELECT 7 AS num UNION ALL
    SELECT 8 AS num UNION ALL
    SELECT 9 AS num UNION ALL
    SELECT 10 AS num
) AS nums
WHERE num <= LENGTH(title) - LENGTH(REPLACE(title, '#', '')) + 1
  AND TRIM(SPLIT_PART(title, '#', num)) <> ''
) 
where hashtag not like '% %'

子查询执行以下步骤:

  • 它使用‘#’字符作为分隔符,分割“social_media_video_info”表的“title”列。

  • 函数 SPLIT_PART(title, ‘#’, num) 提取“title”列中由‘#’分隔的在指定“num”位置的部分。

  • TRIM() 函数去除提取部分的任何前导或尾随空格。

  • DISTINCT 关键字确保仅选择唯一的标签。

  • 与子查询 “nums” 的 CROSS JOIN 生成一个临时结果集,包含从 1 到 10 的数字。

  • 条件 num <= LENGTH(title) — LENGTH(REPLACE(title, ‘#’, ‘’)) + 1 确保分割操作做到“title”列中的最大标签数量。

  • 条件 TRIM(SPLIT_PART(title, ‘#’, num)) <> ‘’ 过滤掉任何空标签。

  • 总结来说,查询通过使用 ‘#’ 作为分隔符,提取“social_media_video_info”表中的“title”列中的标签。它确保仅选择唯一的非空标签,并排除包含空格的标签。请注意,此查询仅考虑 每个标题最多 10 个标签

用例 3:在 Python 中抑制未来和弃用警告

这些代码行的目的是在程序执行期间抑制未来警告和弃用警告。

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
warnings.simplefilter(action='ignore', category=DeprecationWarning) 

这在你确信你使用的代码与当前版本的库兼容,并且你不想被警告潜在问题或弃用功能的情况下会很有用。

警告通常提供有关潜在问题或库未来版本变化的有价值信息。通常建议解决导致警告的根本问题,而不是完全忽视它们。‘warnings’ 模块还提供了 simplefilter() 选项,可用于控制如何在 Python 代码中处理警告。

用例 4:在可能的情况下使用参数化查询

当使用 Python 执行 SQL 查询时,建议使用参数化查询或预处理语句,而不是直接在 SQL 语句中嵌入值。这有助于防止 SQL 注入攻击,并确保数据类型的正确处理。

假设你的应用程序或脚本使用以下选择查询 -

SELECT * FROM 
TABLE 
WHERE 
COLUMN1 IN ('abcd')

如果攻击者想在你的数据库中插入恶意值,他们可以利用这个查询作为漏洞来做到这一点。以下是攻击者如何将插入语句添加到‘选择查询’中并注入不必要的值到数据库的基本示例。下面的灰色部分是攻击者提供的恶意输入,这导致执行了两个查询而不是一个——1. 选择和 2. 插入。

SQL 注入攻击 — 场景亮点(作者提供的图片)

请注意,这不限于插入表。攻击者可以运行其他选择、更新或删除操作,甚至执行 drop table。想象一下‘DROP TABLE’会有多么灾难!

SQL 注入可以通过净化输入和参数化来防止。让我们详细看看这两者。

参数化

参数化可以手动完成,也可以通过 Python 中的可用包完成。设想你使用一个调查应用程序,向用户发送调查以填写。用户被要求在提供的两个文本框中输入详细信息。假设每次用户在两个文本框中输入详细信息时,后台都会运行如下的插入 SQL 查询 -

theVal1 = 来自调查文本框 1

theVal2 = 来自调查文本框 2

以下是后台运行的应用代码 -

 sql = "INSERT INTO TABLE VALUES ('" + theVal1 + "','" + theVal2 + "')"

假设第一个用户在文本框 1 中输入A3,并在文本框 2 中输入A4。后台运行的查询如下 -

INSERT INTO TABLE VALUES ('A3','A4')

假设第二个用户是恶意用户。这个用户了解你的表结构和后台查询。如果他们有一些恶意企图,他们可以尝试使用这些来恶意插入额外的记录。

假设用户在文本框 1 中输入了值 A1,并在文本框 2 中输入了下面的值 -

A2 ');INSERT INTO TABLE VALUES ('B1','B2

本质上发生的情况是,这个值被附加到后台查询中,变成了下面的内容 -

INSERT INTO TABLE VALUES ('A1','A2');INSERT INTO TABLE VALUES ('B1','B2')

因此,这个恶意用户将插入 2 条记录。

你的表将有三个值,第一个用户插入了 1 个,第二个用户插入了 2 个 -

后台表(作者提供的图片)

输入清理

数据清理可以通过转义输入中的特殊字符来完成。这涉及到用其转义字符替换或编码在目标上下文(例如 SQL 查询)中具有特殊意义的字符。例如,在 SQL 中,单引号字符(‘)通常通过将其加倍(‘’)来转义,即用两个单引号替换字符串中的单引号。换句话说,我们可以在将输入值放入查询之前手动转义这些值。我们可以使用 str.replace 来实现这一点。

虽然应用代码保持不变,我们添加了几条字符串替换语句,如下所示 -

theVal1 = 来自调查文本框 1

theVal2 = 来自调查文本框 2

escapedVal1 = theVal1.replace("'", "''")
escapedVal2 = theVal2.replace("'", "''")
sql = "INSERT INTO TABLE VALUES ('" + escapedVal1 + "','" + escapedVal2 + "')"

结果是,当恶意用户尝试插入恶意记录时,它会连同用户的插入语句一起被插入。它看起来像下面这样 -

INSERT INTO TABLE VALUES ('A1','A2'');INSERT INTO TABLE VALUES (''B1'',''B2')

插入到你的表中的值如下 -

转义引号后的后台表(作者提供的图片)

因此,你会在后台表中看到其中一个用户尝试运行插入语句。你通过仅仅转义输入变量,有效地阻止了 SQL 注入的发生。

更优化的方法是使用诸如 psycopg2、pyodbc、sqlite3 或 SQLAlchemy 等 Python 库。这些是 SQL 适配器,内置支持参数化查询等功能。

结论

在这篇文章中,我们探索了四个实际用例,以提高你在处理 SQL 查询时的 Python 编程技能。我们从用例 1 开始,重点提高了用 Python 编写的 SQL 查询的可读性。通过利用查询格式化和缩进等技术,我们可以使代码更有条理,更易于理解。

继续用例 2,我们深入探讨了如何从文本列中提取标签。通过利用 SQL 及其字符串处理函数,我们学会了如何有效提取相关标签,并增强数据分析过程。

在用例 3 中,我们讨论了在 Python 中抑制未来和弃用警告的重要性。通过利用warnings模块,我们可以确保 Python 输出更清晰,代码执行无错误,避免不必要的干扰和潜在的兼容性问题。

最后,在用例 4 中,我们强调了使用经过清理的代码和参数化查询的重要性,以增强安全性、提高性能并防止 SQL 注入攻击。

通过理解和实施这些用例,Python 开发者和数据分析师可以提升 SQL 查询执行和优化技能,进而编写出更健壮和高效的代码。我相信,将这些技术应用于实际场景将有助于创建更清晰的工作流程,并使你能够高效而安全地提取可操作的见解。

数据建模的初学者,第一部分:什么是数据建模?

原文:towardsdatascience.com/data-modeling-for-mere-mortals-part-1-what-is-data-modeling-103eb184930e

数据建模有时可能看起来令人畏惧(那些复杂的图表!),但其实并不一定如此

Nikola IlicTowards Data Science Nikola Ilic

·发表于 Towards Data Science ·12 min 阅读·2023 年 8 月 2 日

--

由 Alina Grubnyak 拍摄,来自 Unsplash

近年来,我进行了数十次关于各种数据平台主题的培训,面对各种观众。在教授各种数据平台概念和技术时,我发现其中一个概念对许多业务分析师特别令人畏惧,尤其是那些刚刚开始他们旅程的人。那就是数据建模的概念

为什么数据建模有时令人感到畏惧?

也许是因为当你看到这些看起来如此复杂和复杂的图表时你感到迷茫……

但是,数据建模不是关于图表的。它是关于建立信任,即业务与数据专业人士之间的共同理解,最终目标是通过数据提供增加的业务价值。

如果我们同意数据建模是关于建立信任的,我相信我们也可以同意信任不是轻易建立的——必须考虑到一定的时间和精力。而且,时间和精力不是你可以理所当然地拥有的——这是你需要投入的!

所以,我们可以正确地认为数据建模是一种投资。一种应该为你的业务带来更多稳定性和适应性的投资。

把它想象成投资建造一座房子。

由 Pixabay 拍摄,来自 Pexels

显然,你可以选择快速简便的方法,将建筑块直接放在地面上,这在某段时间内可能会有效,直到出现新的情况——例如地震或雷暴。你的房子可能会受到损坏。但不仅仅是房子会受损——你的信任也会受到损害:与你同住的人、你的邻居、朋友会意识到你没有投入足够的时间和精力来预防这种不良情况。

现在,假设你决定采取另一条路径——在一开始更具挑战性,这将需要你投入更多的时间和精力。你为房子奠定了一个良好的基础,固定了地下的东西,然后在上面建造了房子。现在,你的房子会更稳固,可以适应未来的挑战。

照片由 Valeria Miller 拍摄,来源于 Pexels

既然我们已经解释了为什么投入时间和精力构建数据模型至关重要,现在让我们审视各种类型的数据模型以及它们如何融入大数据建模的全貌。

概念数据模型

通常,起点是创建一个概念数据模型。这是一个高层次的视角,可以说是从 10,000 英尺的高度来看待数据的业务需求。由于我们讨论的是高层次的视角,概念数据模型的主要目标是简化日常业务流程和关键实体。

在这个阶段,我们在编制一个大图景:在我们的业务流程中,关键实体是什么?它们之间是如何关联的?概念数据模型的关键特征是它应以易于理解的术语进行沟通。简而言之,利用商业用户和非技术人员容易理解的通用语言。

我知道我告诉过你数据建模不是关于图表的,但我们仍然需要可视化创建数据模型的过程。我会首先给你一个概念数据模型的基本示例。

作者提供的图像

在这个插图中,你可以识别出各种实体。体育场、事件、客户、观众和票务。你还可以注意到这些实体之间是如何互相连接的。这种高层次的概述提供了组织内业务流程的简化图景。

现在,让我们继续用通俗的语言解释一下这张插图中所展示的内容。

我们的第一个实体是体育场。体育场有一个名称,并且位于一个特定的国家和城市,这些信息唯一地标识了该体育场。体育场可能会举办许多活动,可能会有很多观众来参加这些活动。

接下来,我们有一个事件。一个特定的事件不能存在于其计划举办的体育场之外。一个事件可以由一个参与者出席,并且一个事件可以有许多参与者。

参与者是参加活动的实体。他们也可以是体育场实体的顾客,比如他们曾经访问过体育场商店或类似场所。顾客参与者的主要区别在于,顾客不一定需要参加体育场的特定活动。

顾客可能与体育场有关,例如,通过参观体育场博物馆或在体育场球迷商店购物,但这并不意味着他们是活动的参与者。

最后,票务是一个实体,代表确认参与者将参加特定的活动。每张票都有一个唯一的标识符,因为如果两个或更多参与者得到相同编号的票就会很尴尬。尽管票务有唯一的标识,单个参与者可以购买多张票。

我们为什么需要概念数据模型?

既然我们已经解释了概念数据建模的核心组件,你可能会想:这为什么重要?为什么有人要花时间和精力描述所有实体及其之间的关系?

还记得我们讨论过如何在业务和数据角色之间建立信任吗?这正是概念数据模型的全部内容。确保业务相关者能够获得他们所需的信息,以通用语言解释,以便他们能够轻松理解整个工作流程。建立概念数据模型还为业务相关者提供了识别一系列需要在构建物理数据模型之前回答的业务问题的可能性。

业务可能会问的一些问题包括:顾客和参与者是同一个实体吗(如果不是,为什么)?一个参与者可以购买多张票吗?什么唯一标识一个特定事件?当然还有更多问题……

此外,概念数据模型有时以更易于理解的方式展示非常复杂的业务流程。与其翻阅一页又一页的书面文档,不如查看实体和关系的图示,所有内容都以用户友好的方式解释,从而快速理解业务流程的核心要素。

逻辑数据模型

一旦业务和数据团队就概念数据模型达成一致,数据建模过程的下一步是设计逻辑数据模型。在这一阶段,我们在前一步的基础上进行,确定实体的具体结构,并提供有关这些实体之间关系的更多细节。在这一阶段,你应该识别特定实体的所有相关属性,以及关系的基数。

请注意,与概念数据建模阶段一样,我们仍然不讨论具体的平台或解决方案。与之前的阶段一样,我们的重点是理解业务需求以及如何将这些需求有效地转化为数据模型。

为确保概念数据模型成功演变为逻辑数据模型,需要执行几个步骤。

  • 识别实体属性

  • 识别候选键 — 找出哪个属性或属性集可以唯一标识特定实体

  • 选择主键 — 根据上一阶段的发现,设置实体的主键

  • 对数据模型应用规范化/反规范化 — 我们将在接下来的文章中更详细地讨论规范化和反规范化。此时,请记住,规范化应消除数据冗余,通常用于 OLTP 系统。另一方面,反规范化主要用于分析系统或 OLAP,以减少 JOIN 操作的影响并加快数据读取速度

  • 设置实体间关系 — 验证各种实体如何互联,如果需要,通过将一个实体拆分为多个实体来减少多个关系依赖的复杂性

  • 识别关系基数 — 另一个极其重要的步骤。关系基数定义了一个实体的多少实例与另一个实体的实例相关联。有三种主要的基数类型:一对一(1:1)、一对多(1:M)和多对多(M:M)

  • 迭代和优化 — 在现实中,几乎不可能立刻找到适合所有人需求并满足所有要求的数据模型。因此,始终从业务相关者那里征求反馈至关重要,并根据收到的反馈优化逻辑数据模型,然后再将其转化为物理形式

图片由作者提供

为什么我们需要逻辑数据模型?

与概念数据模型不同,虽然在构建概念数据模型时投资时间和精力的好处并不明显,但我认为对于逻辑数据模型,其潜在收益更加明显。首先,逻辑数据模型作为最优质的质量保证测试,因为它可以发现业务流程中的差距和问题,从而节省大量时间和精力。在确定特定平台并在其上构建低效的物理数据模型之前,在此阶段修复这些问题要容易得多且成本更低。

正如我们之前提到的,一个好的逻辑数据模型的关键特征之一是迭代和微调是一个持续的过程。因此,构建逻辑数据模型可以被视为敏捷数据建模周期的一部分,这确保了更强健、可扩展和面向未来的模型。

逻辑数据模型的终极好处在于,它作为业务逻辑最终实现的蓝图,通过物理数据模型得以实现。依赖于精心设计的逻辑数据模型,数据库工程师和数据架构师能够创建更高效的物理数据库系统。

物理数据模型

物理数据模型代表了最终的细节 — 数据模型将在特定的数据库中如何实现。与概念数据模型和逻辑数据模型不同,后者是平台和解决方案无关的,物理实现需要定义可能特定于某些数据库提供商的低级细节。

从逻辑数据模型过渡到物理数据模型需要对逻辑数据模型中定义的实体和关系进行更多的迭代和微调。

作者提供的图片

与逻辑数据模型一样,成功实现数据模型的过程需要完成一整套必要的步骤,因此,让我们关注其中最重要的一些:

  1. 选择平台 — 这是一个无法跳过的步骤,因为这个决策将塑造你未来的设计原则。

  2. 将逻辑实体转换为物理表 — 逻辑实体只是逻辑上的存在,存在于更抽象的层次上。由于实际数据库不支持这种抽象层次,我们需要将该实体转换为物理单元。这意味着我们需要向底层平台提供如何存储数据的信息 — 简单来说,就是定义每个实体属性的数据类型 — 无论是整数、十进制数还是普通文本。此外,每个物理表应依赖于键来确保数据完整性 — 在物理数据建模过程中,你的任务是设置主键、外键和唯一键。

  3. 建立关系 — 基于关键列,物理数据建模工作流中的下一步是创建表之间的关系。

  4. 应用规范化/非规范化 — 类似于我们在逻辑数据建模阶段已经检查过的,检查并确认表格是否根据你设计的是事务系统还是分析系统,规范化或非规范化到确保特定系统工作负载最有效的程度 — 不要忘记,在 OLTP 系统中,表格应规范化(在大多数情况下)到第三范式,以减少数据冗余,并高效支持写入操作(插入、更新),而在 OLAP 系统中,数据可能被非规范化以消除连接的数量,使读取操作更具性能。

  5. 定义表约束 — 你可以应用许多约束以确保数据完整性。这不仅仅是关于我们之前提到的键,还有其他逻辑检查。我给你一个简单的例子:假设你的表中有一个列存储关于学生大学成绩的数据。学生可以获得 100 分吗?或者 1000 分?或者 1 分?当然不行!有一个有限的可能成绩列表,在 5 到 10 之间(或者在其他系统中是 A 到 F)。那么,为什么不在列上定义这个约束,从而防止插入不合逻辑的值 — 无论是由于错误,还是让最终用户有可能输入他们想要的内容?

  6. 创建索引和/或分区 — 索引和分区是特殊的物理数据结构,它们“存在”于表中,其主要目标是提高数据模型的效率。解释索引表分区超出了本文的范围,但请记住,这两种功能可能会显著影响数据模型的性能 — 既有利也有弊!可以公平地说,尤其是对于索引,实现高效的索引策略本身就是一种精通。表分区是一种将一个大表拆分为多个较小的“子表”的技术,从而帮助数据库引擎在查询执行期间减少扫描时间。一个好的例子是按日历周期进行分区,比如按年份进行分区。与其将所有记录保存在一个庞大的、数百万行或数十亿行的表中,不如拆分这个表并创建多个包含特定年份记录的较小“子表”。当我说“子表”时不要感到困惑 — 这些并不是你数据模型中的“真实”新表 — 一切都发生在幕后,这只是数据在底层物理结构的方式。

  7. 扩展程序化对象 — 几乎每个数据库管理系统都允许你创建各种程序化对象,如存储过程、函数或触发器。解释这些对象中的每一个超出了本文的范围,它们各自有不同的用途,并帮助你高效地完成各种任务。然而,它们的实现几乎是每个数据平台解决方案中的“事实”标准,很难找到不依赖于这些对象的 OLTP 或 OLAP 系统。

为什么我们需要物理数据模型?

结合之前提到的所有要点,拥有一个物理数据模型的主要好处是确保效率、最佳性能和可扩展性。当我们谈论效率时,我们显然是在考虑两个最珍贵的业务资产 — 时间和金钱。除非你认为时间=金钱,否则我们只有一个资产需要考虑…

由 Karolina Grabowska 提供的照片,来自 Pexels

简而言之,你的数据模型越高效,它可以服务的用户就越多,服务他们的速度也会更快,这通常最终会带来更多的商业利润。

并排比较

这是每种数据模型类型的关键特征的简要概述:

图片由作者提供

结论

我希望你记住有关数据建模的三件关键事项:

  • 千万不要低估数据建模过程和设计合适数据模型的重要性!你可以“偷偷”绕过它吗?当然可以……但是,就像我们举的房子例子一样,没有坚实的基础,迟早你会开始挠头了。

  • 节省时间和精力以避免构建高效数据模型将会产生技术债务,这会导致在后期需要投入更多的时间和资源来处理它。

  • 始终以业务为导向来建模数据!为了建一个数据模型而设计数据模型,或使用预定义的“通用数据建模”模板,如果那个模型不能回答关键的业务问题,那是没有意义的。

在本系列的下一部分,我们将探讨维度数据建模的奇妙世界……

感谢阅读!

成为会员,支持 Medium 上的数千名作者!

数据建模入门 — 第二部分:维度建模基础

原文:towardsdatascience.com/data-modeling-for-mere-mortals-part-2-dimensional-modeling-fundamentals-ae2f53622321

介绍四步维度建模设计方法

Nikola IlicTowards Data Science Nikola Ilic

·发表于Towards Data Science ·阅读时间 9 分钟·2023 年 9 月 1 日

--

你知道在商业智能领域中可能是最好的数据模型是什么吗?想了解 4 步维度设计过程吗?这篇文章将为你提供维度建模的所有基础知识。

徐 at Pexels 的照片

上一篇文章中,你了解了数据建模的一般原则。现在,是时候缩小我们的学习范围,掌握专门适用于商业智能场景的数据建模概念和技术了。而每当我们谈论商业智能的数据建模时,维度建模无疑是最重要的概念。

历史回顾 — Ralph Kimball

在我们解释为什么称之为“维度建模”之前,先来简要回顾一下历史。在 1996 年,一位名叫Ralph Kimball的人出版了一本书,《数据仓库工具包》,这本书至今仍被视为维度建模的“圣经”。在他的书中,Kimball 介绍了一种全新的数据建模方法,即所谓的“自下而上”方法。重点是识别组织内的关键业务流程,并首先对这些流程进行建模,然后再引入其他业务流程。

4 步维度设计过程

Kimball 的方法相当简单,由 4 个步骤组成,也称为“4 步维度设计过程”。每个步骤都基于一个决策。

  1. 这从选择业务过程开始。为了保持一致,让我们使用与上一篇文章相同的例子,假设销售事件的票是我们感兴趣的业务过程。

作者插图

第二步是声明粒度。粒度?粒度到底是什么?简单来说,就是业务过程捕获的最低详细级别。在我们的例子中,最低的详细级别是单个票据销售。稍后我们会解释为什么选择正确的粒度在维度建模中至关重要。目前,只需要记住,粒度指的是业务过程捕获的最低详细级别。

第三步,一旦你声明了粒度,接下来的步骤是确定维度。让我们暂时抛开这个名称维度……尝试解释一下“维度”真正的含义……它是一种特殊类型的表,我喜欢把它当作查找表来思考。你知道,如果你需要关于某个对象的更多描述性信息——这就是你在查找表中会找到的内容。哎呀,我的意思是,在维度表中。想想一个人及其描述。你会如何描述一个特定的人?当然是按名字。然后是性别、年龄、身体特征、电子邮件地址,可能还有电话号码。或者产品呢?产品可能有名字,属于特定类别,还有特定的颜色或尺寸。维度表通常回答以“W”开头的问题:谁、什么、何时、哪里、为什么。回到我们的例子:我们什么时候卖出了票?我们在哪里卖出了票?我们卖了什么类型的票?等等……

第四步是识别事实。 如果我们将维度视为查找表,那么事实表就是一个存储事件数据的表——这些事件是业务过程的结果。大多数情况下,但并非总是,这些事件用数字值来表示——我们卖出了多少张票?我们赚了多少收入?

为什么选择维度建模?

在我们继续解释维度建模的不同物理实现之前,让我们快速重申一下为商业智能场景构建维度模型的关键好处:

  • 用户友好的数据导航 — 作为用户,我们往往更容易将业务过程视作过程中的主题。哪个事件在去年第二季度售出的票最多?女性顾客为冠军联赛决赛购买了多少张票?哪位美国员工在 2022 年超级碗中售出了最多的 VIP 票?当然,还有很多其他问题……

  • 性能——正如你已经了解的那样,OLAP 系统被设计用来支持快速和高效的数据读取操作,这通常意味着表之间的连接更少。这正是维度建模通过星型模式设计提供的——我们将在下一部分中详细介绍。

  • 灵活性——这是维度模型的关键优势之一。你的客户更改了地址?你的员工在组织中调动了职位?没关系,因为所有这些挑战都可以通过一种称为“慢变维度”的技术在模型中轻松处理。不过,更多细节将在本文后续部分介绍。

数据建模师还是占星师?理解星型与雪花型模式

如果你发现自己被经验丰富的数据建模师包围,你可能会听到他们谈论星型和雪花型模式。别担心,他们突然变成了占星师或自然研究者。星型和雪花型模式可能是维度建模领域中最具影响力的概念。

让我们首先尝试解释星型模式。别忘了,根据 Kimball 的说法,每一条数据应该被分类为“什么、何时、在哪里、谁、为什么”,或“多少”或“多少”。因此,在一个设计良好的维度模型中,你应该有一个包含所有测量和事件的中央表——这就是事实表——被查找表围绕,我们称之为维度表。事实表和维度表通过维度表的主键和事实表的外键建立的关系连接在一起。如果你查看下图,你将理解为什么这个数据建模设计模式被称为星型模式。

插图来自作者

星型模式是企业数据仓库系统中数据建模的事实标准,因为它能够高效地查询大量数据,快速汇总并直观地过滤数据。

尽管由于“年代问题”存在许多关于星型模式在现代数据平台解决方案中相关性的争论,但可以公平地说,这一概念在设计高效且可扩展的商业智能系统时仍然绝对相关,并且是最广泛采用的。

雪花型模式与星型模式非常相似! 从概念上讲,星型和雪花型没有区别——在这两种情况下,你会将“谁、什么、何时、在哪里和为什么”放入维度表中,同时将“多少”和“多少”保留在事实表中。唯一的区别是,在雪花型模式中,维度被规范化并分解成子维度,正如你在下图中看到的那样,这就是它类似雪花的原因。

插图来自作者

规范化维度的主要动机是消除维度表中的数据冗余。尽管这听起来像是一种理想的方法,但规范化维度带来了一些严重的考虑:

  • 整体数据模型结构变得更复杂

  • 性能可能会受到影响,因为规范化维度表之间的连接

当然,存在特定的用例,其中维度规范化可能是更可行的选择,尤其是在减少数据模型大小方面。然而,请记住,雪花架构在为商业智能工作负载建模时应当是例外而非规则。

星型架构应始终作为默认选择!

为了总结关于星型架构相关性的这个话题,我喜欢用一个由 Carlsberg Brewery 创建的市场营销信息的类比,它说:

“Carlsberg — 可能是世界上最好的啤酒…”

翻译到数据建模世界:

星型架构可能是商业智能世界中最好的数据模型…

事实表的 3 个要点

有很多精彩的书籍(如前述的《数据仓库工具包》,以及Christopher Adamson 的《星型架构 — 完整参考》),我强烈建议你阅读并了解关于维度建模和星型架构的所有细节。

因此,认为阅读这篇文章或任何其他单一文章会使你成为“维度建模大师”将是绝对错误和自大的。然而,我想强调 3 个与维度模型中的事实表相关的关键概念。

免责声明: 这些是我个人认为最重要的 3 个概念,这并不意味着你不应该考虑其他概念更为相关。

典型的事实表结构 — 作者提供的图像

  1. 事实表结构 — 在大多数情况下,事实表将包含通过测量现实世界事件产生的数值,以及一堆相关维度表的外键。在最低粒度下,事实表的一行对应于一个现实世界事件。因此,事实表的设计完全基于这个事件,而不受未来可能生成的报告的影响。事实表通常是窄而长的(行数多,列数少)

  2. 加法、半加法和非加法度量 — 我们在事实表中存储的数值并不总是具有相同的“性质”。加法度量是最灵活的,可以在与事实表相关的任何维度上进行汇总。半加法度量可以在某些维度上进行汇总,但不是全部。一个典型的例子是余额金额,它在除时间之外的所有维度上都是加法的。最后,一些度量完全是非加法的,例如比率。

  3. 事实表中的 NULLs——这里需要记住的关键点是:并非每个 NULL 值在事实表中表现良好!虽然测量值为 NULL 是完全可以接受的,但在事实表中使用 NULL 作为外键应该尽量避免,因为这会导致参照完整性违反。这可以通过在维度表中引入一个用于“未知”或“未定义”记录的特殊默认行来解决

关于维度表的 3 个要点

典型的维度表结构——作者提供的图像

  1. 维度表结构——维度表应始终包含一个主键列,该列在相关的事实表中用作外键。维度表通常较宽而短(列多,行数少,尽管有些维度表有多百万行)。维度表是非规范化的,通常包含文本属性和描述

  2. 慢变化维度 (SCD)——这是处理数据随时间变化的关键概念之一。不要被“慢”这个词搞混:)… 这并不是关于“慢”或“快”,重点是“变化”这个词。处理维度属性修改的技术有多种(考虑更改个人的实际地址或电话号码),Kimball 列出了不少于 8 种不同的 SCD 类型!然而,最常用的是类型 2,你会在维度表中添加一行新数据并更新行的有效性属性

  3. 角色扮演维度——我已经写了一篇关于这个主题的完整文章,并解释了如何在 Microsoft Power BI 中应用这个概念

如我所提到的,这些只是与维度表相关的一些概念,我强烈建议阅读 Kimball 或 Adamson 的书,以了解其他重要的概念和技术。

结论

通过了解各种数据模型类型(概念模型、逻辑模型和物理模型),我们建立了理解数据建模过程重要性的坚实基础。尽管维度建模仅是数据建模的一个子集,但它在实现高效且可扩展的实际商业智能解决方案中具有关键重要性。

总结一下,维度模型最大的优势是其灵活性和适应性。以下是可以在不违反任何现有商业智能查询或应用的情况下应用的更改列表:

  • 如果你想向现有事实表添加新事实,只需创建一个新列(假设“新”事实与现有事实的粒度相同)

  • 如果你需要向现有事实表添加新的查找属性,只需添加一个新的列,将外键指向一个新的维度表即可。

  • 每当你需要用新属性扩展现有维度表时,只需像向相应维度表添加新列一样简单。

感谢阅读!

成为会员,支持 Medium 上的成千上万位作者!

数据建模技术用于数据仓库

原文:towardsdatascience.com/data-modeling-techniques-for-data-warehouse-3edcb541e34e?source=collection_archive---------0-----------------------#2023-06-19

Mariusz KujawskiTowards Data Science Mariusz Kujawski

·

关注 发布于 Towards Data Science ·11 分钟阅读·2023 年 6 月 19 日

--

图片来源:Zdeněk MacháčekUnsplash

数据建模是创建数据及其在组织或系统中关系的概念表示的过程。维度建模是一种先进的技术,旨在以任何用户都能直观理解的方式呈现数据。它还允许高性能的访问、灵活性和可扩展性,以适应业务需求的变化。

在这篇文章中,我将深入概述数据建模,特别关注 Kimball 的方法。此外,我还将介绍其他用于以用户友好和直观的方式呈现数据的技术。一个特别有趣的技术是将数据存储在一个宽表中,尽管这种方法可能不适用于所有查询引擎。我将介绍在数据仓库、数据湖、数据湖屋等中使用的技术。然而,选择适合特定用例和查询引擎的方法论非常重要。

什么是维度建模?

每个维度模型由一个或多个具有多部分键的表组成,称为事实表,以及一组称为维度表的表。每个维度表都有一个主键,与事实表中多部分键的一个组件精确对应。这种独特的结构通常被称为星型模式。在某些情况下,可以使用一种更复杂的结构称为雪花模式,其中维度表与更小的维度表相连接。

维度建模的好处:

维度建模提供了一种实用而高效的数据组织和分析方法,带来了以下好处:

  • 为业务用户提供简单易懂的体验。

  • 改进查询性能以加快数据检索速度。

  • 灵活性和可扩展性以适应不断变化的业务需求。

  • 确保多个来源的数据一致性和集成。

  • 增强用户采用和自助分析。

现在我们已经讨论了什么是维度建模及其为组织带来的价值,让我们探讨如何有效利用它。

数据和维度建模方法

虽然我打算主要关注 Kimball 的方法,但在深入研究之前,让我们简要了解几种其他流行的技术。

Inmon 方法

Inmon 建议在数据仓库中使用规范化的数据模型。这种方法支持数据集市的创建。这些数据集市是数据仓库的较小、专门的子集,服务于特定的业务领域或用户组。它们旨在为特定的业务功能或部门提供更量身定制和高效的数据访问体验。

数据保险库

数据保险库是一种建模方法,重点关注可扩展性、灵活性和可追溯性。它由三个核心组件组成:中心、链接和卫星。

中心

中心是所有独特实体的集合。例如,一个账户中心将包括账户、account_ID、load_date 和 src_name。这使我们能够追踪记录最初的来源,并确定是否需要从业务键生成替代键。

链接

链接建立中心之间的关系,并捕获不同实体之间的关联。它们包含相关中心的外键,支持多对多关系的创建。

卫星表

卫星表存储关于中心的描述性信息,提供额外的上下文和属性。它们包括历史数据、审计信息以及与特定时间点相关的其他属性。

Data Vault 的设计允许灵活且可扩展的数据仓库架构。它促进数据的可追溯性、可审计性和历史跟踪。这使得它适用于数据集成和敏捷性至关重要的场景,如高度受监管的行业或快速变化的商业环境。

单一大表(OBT)

OBT 将数据存储在一个宽表中。使用一个大表或非规范化表可以简化查询、提高性能,并简化数据分析。这消除了复杂连接的需求,简化了数据集成,在某些场景中可能有益。然而,它可能导致冗余、数据完整性挑战和维护复杂性增加。在选择单个大表之前,请考虑具体要求。

作者提供的图片

作者提供的图片

WITH transactions AS (
  SELECT 1000001 AS order_id, TIMESTAMP('2017-12-18 15:02:00') AS order_time,
          STRUCT(65401 AS id, 'John Doe' AS name, 'Norway' AS location) AS customer,
          [
            STRUCT('xxx123456' AS sku, 3 AS quantity, 1.3 AS price),
            STRUCT('xxx535522' AS sku, 6 AS quantity, 500.4 AS price),
            STRUCT('xxx762222' AS sku, 4 AS quantity, 123.6 AS price)
          ] AS orders
  UNION ALL
  SELECT 1000002, TIMESTAMP('2017-12-16 11:34:00'),
        STRUCT(74682, 'Jane Smith', 'Poland') AS customer,
        [
            STRUCT('xxx635354',   4,      345.7),
            STRUCT('xxx828822', 2,      9.5)
        ] AS orders
)

select *

from
transactions

对于一个宽表,我们不需要连接多个表。我们可以使用一个表来聚合数据和进行分析。这种方法提高了 BigQuery 的性能。

作者提供的图片

select customer.name, sum(a.quantity)

from
transactions t, UNNEST(t.orders) as a
group by  customer.name

Kimball 方法论

Kimball 方法论强调创建一个称为数据仓库的集中数据存储库。这个数据仓库作为唯一的真实数据源,以一致且结构化的方式整合和存储来自各种操作系统的数据。

这种方法提供了一套全面的指导方针和最佳实践,用于设计、开发和实施数据仓库系统。它强调创建维度数据模型,并优先考虑简洁性、灵活性和易用性。现在,让我们深入探讨 Kimball 方法论的关键原则和组件。

实体模型到维度模型

在我们的数据仓库中,数据源通常存在于实体模型中,这些模型被规范化为多个表,包含应用程序的业务逻辑。在这种情况下,理解表之间的依赖关系和基础业务逻辑可能很具挑战性。创建分析报告或生成统计数据通常需要连接多个表。

作者提供的图片

要创建维度模型,数据需要经过提取、转换和加载(ETL)过程,将其反规范化为星型模式或雪花模式。这个过程的关键活动包括识别事实表和维度表,并定义粒度。粒度决定了事实表中存储的详细程度。例如,交易可以按小时或按天汇总。

图片由作者提供

假设我们有一家销售自行车和自行车配件的公司。在这种情况下,我们有以下信息:

  • 交易

  • 商店

  • 客户

  • 产品

根据我们的业务知识,我们知道需要收集关于销售量、随时间变化的数量,以及按地区、客户和产品细分的信息。根据这些信息,我们可以设计我们的数据模型。交易表将作为我们的事实表,而商店、客户和产品表将作为维度表。

事实表

事实表通常代表一个业务事件或交易,并包括与该事件相关的指标或度量。这些指标可以包含各种数据点,如销售金额、销售数量、客户互动、网站点击或任何其他提供业务绩效见解的可衡量数据。事实表还包括建立与维度表关系的外键列。

图片由作者提供

事实表设计的最佳实践是将所有外键放在表的顶部,然后再进行度量。

事实表类型

  1. 交易事实表提供的粒度在其最低级别为一行代表来自交易系统的一条记录。数据会每日或实时刷新。

  2. 定期快照事实表捕捉某一时间点的事实表快照,例如月末。

  3. 累积快照事实表总结了在过程开始和结束之间的可预测步骤中发生的测量事件。

  4. 无事实表记录没有任何度量或指标的事件信息。

维度表

维度表是维度建模中的一种表,包含描述性属性,例如关于产品、其类别和类型的信息。维度表为存储在事实表中的定量数据提供了背景和视角。

维度表包含一个唯一键,用于识别表中的每条记录,称为代理键。表中可以包含一个来自源系统的业务键。一个好的做法是生成代理键,而不是使用业务键。

创建代理键有几种方法:

    • 哈希:可以使用类似 MD5、SHA256 的哈希函数生成代理键(例如 md5(key_1, key_2, key_3))。
    • 增量生成:通过使用总是递增的数字(例如 row_number()、identity)生成的替代键
    • 连接生成:通过连接唯一键列(例如 concat(key_1, key_2, key_3))生成的替代键
    • 唯一生成的:通过使用生成唯一标识符的函数(例如 GENERATE_UUID())生成的替代键

你选择的方法取决于你用于处理和存储数据的引擎。这可能会影响数据查询的性能。

维度表通常包含层级结构。

a) 例如,父子层级可以用来表示员工与其经理之间的关系。

作者提供的图像

属性之间的层级关系。例如,一个时间维度可能具有如年份、季度、月份和日期等属性,形成一个层级结构。

作者提供的图像

维度表的类型

一致维度:

一致维度是可以被多个事实表使用的维度。例如,一个区域表可以被不同的事实表利用。

退化维度:

当属性存储在事实表中而不是维度表中时,就会发生退化维度。例如,交易编号可以在事实表中找到。

垃圾维度:

这包含在现有维度表中不适合的无意义属性,或表示各种状态组合的标志和二进制值的组合。

角色扮演维度:

相同的维度键在事实表中包含多个外键。例如,日期维度可以在事实表中引用不同的日期,如创建日期、订单日期和交货日期。

静态维度:

静态维度是一个通常不会变化的维度。它可以从参考数据中加载而无需更新。一个例子可能是公司的分支机构列表。

桥表:

当事实表与维度表之间存在一对多关系时,使用桥表。

缓慢变化维度

缓慢变化维度(SCD)是维度建模中的一个概念。它处理维度表中维度属性随时间的变化。SCD 提供了一种机制,用于在业务实体发展及其属性变化时维护维度表中的历史数据和当前数据。SCD 有六种类型,但最受欢迎的三种是:

  • SCD 类型 0:在这种类型中,仅导入新记录到维度表中,不进行任何更新。

  • SCD 类型 1:在这种类型中,新记录被导入到维度表中,现有记录被更新。

  • SCD 类型 2:在这种类型中,新记录被导入,并为更改的属性创建新记录和新值。

例如,当 John Smith 搬到另一个城市时,我们使用 SCD 类型 2 来保留与伦敦相关的事务信息。在这种情况下,我们创建一个新记录并更新之前的记录。结果是,历史报告将保留他在伦敦进行购买的信息。

图片由作者提供

图片由作者提供

MERGE INTO client AS tgt
USING (
    SELECT 
Client_id,
Name,       
Surname,
City
      GETDATE() AS ValidFrom
    ‘20199-01-01’ AS ValidTo
from client_stg
) AS src
ON (tgt.Clinet_id = src.Clinet_id AND tgt.iscurrent = 1)
WHEN MATCHED THEN
    UPDATE SET tgt.iscurrent = 0, ValidTo = GETDATE()
WHEN NOT MATCHED THEN
    INSERT (Client_id, name, Surname, City, ValidFrom, ValidTo, iscurrent)
    VALUES (Client_id, name, Surname, City, ValidFrom, ValidTo,1);

这是 SCD 3 的样子,当我们将新值和旧值保存在不同列时。

星型模式与雪花模式

设计数据仓库时最流行的方法是使用星型模式或雪花模式。星型模式包含与事实表相关的事实表和维度表。在星型模式中,事实表和维度表直接与事实表相关。另一方面,雪花模式由一个事实表、与事实表相关的维度表以及与这些维度表相关的附加维度组成。

图片由作者提供

这两种设计之间的主要区别在于它们的规范化方法。星型模式保持数据非规范化,而雪花模式确保规范化。星型模式旨在提供更好的查询性能。雪花模式专门设计用来处理大型维度的更新。如果你在更新大规模维度表时遇到挑战,可以考虑转换为雪花模式。

数据加载策略

在我们的数据仓库、数据湖和数据湖屋中,我们可以有各种加载策略,例如:

全量加载(Full Load): 全量加载策略涉及从源系统中加载所有数据到数据仓库中。当出现性能问题或缺乏可以指示行修改的列时,通常会使用这种策略。

增量加载(Incremental Load): 增量加载策略涉及仅加载自上次数据加载以来的新数据。如果源系统中的行不能更改,我们可以基于唯一标识符或创建日期仅加载新记录。我们需要定义一个“水印”,以便用于选择新行。

增量加载(Delta Load): 增量加载策略专注于加载自上次加载以来发生变化或新增的记录。它与增量加载的不同之处在于,它专门针对增量变化,而不是所有记录。当处理大量数据变化时,增量加载策略可以提高效率,并显著减少处理时间和所需资源。

加载数据的最常见策略是首先填充维度表,然后填充事实表。这里的顺序很重要,因为我们需要在事实表中使用维度表的主键来创建表之间的关系。有一个例外情况。当我们需要在维度表之前加载事实表时,这种技术被称为“迟到维度”(late arriving dimensions)。

在这种技术中,我们可以在维度表中创建代理键,并在填充事实表后通过 ETL 过程更新它。

总结

在详细阅读本文后,如果你有任何问题或想进一步讨论数据建模和有效的维度模型,请随时通过LinkedIn与我联系。实施数据建模可以释放数据的潜力,为明智决策提供有价值的见解,同时获得方法和最佳实践的知识。

数据工程师的数据建模

原文:towardsdatascience.com/data-modelling-for-data-engineers-93d058efa302

初学者的终极指南

💡Mike ShakhomirovTowards Data Science 💡Mike Shakhomirov

·发布于 Towards Data Science ·阅读时间 9 分钟·2023 年 12 月 3 日

--

图片来源:Sebastian SvensonUnsplash

数据建模是数据工程的重要组成部分。在这个故事中,我想讨论不同的数据模型、SQL 在数据转换中的作用以及数据增强过程。SQL 是一个强大的工具,帮助操作数据。通过数据转换管道,我们可以转换和增强加载到数据平台中的数据。我们将讨论各种数据操作方法、调度和增量表更新。为了使这个过程高效,我们需要首先了解一些关于数据建模的基本知识。

什么是数据建模?

数据模型旨在组织数据元素并标准化数据元素之间的关系。

数据模型确保数据的质量、语义配置和命名一致性。它有助于从概念上设计数据库,并在数据元素之间建立逻辑连接,例如主键和外键、表格等。

如果你需要最可靠和具有成本效益的数据转换,良好而全面的数据模型设计是至关重要的。它保证数据处理没有延迟和不必要的步骤。

公司使用一种称为维度数据建模的程序来处理数据。来源生产分析级别在模式(数据集)之间的划分实现了有效的数据治理,并确保我们的数据为业务智能和机器学习做好准备。

任何可衡量的信息都存储在事实表中,即事务、会话、请求等

外键 用于事实表中,并且它们与维度表相连接。维度表 拥有与事实表相关的描述性数据,例如 品牌、产品类型/代码、国家等。

根据业务需求,维度和事实 被整合到 模式 中。

最受欢迎的两种模式类型是 星型模式和雪花模式。并且这些也是数据工程师面试中最常见的问题之一 [1]。

[## 数据工程面试问题]

准备工作面试的提示

towardsdatascience.com

  • 星型模式:星型模式是一个具有高度非规范化结构的模式。这在现代数据仓库中非常常见,数据会在多个表中重复。每个维度代表一个维度表。维度表具有与事实表连接的外键,以获取结果。数据冗余较高,但由于模式设计的简单性,它可以提供性能上的好处。它更为非规范化;这意味着表之间的连接较少,因此它在查询时表现更优。

星型模式示例。作者提供的图片。

  • 雪花模式:雪花模式扩展了星型模式,具有更多规范化的维度表。此模式使用较少的磁盘空间,因为所有表都经过规范化,并拆分成进一步的维度表。添加维度到此模式中很容易,而且由于复杂的模式设计,数据冗余也较少。

雪花模式示例。作者提供的图片。

  • 星系模式:类似于上述模式,星系模式具有来自 多个维度模型 的多个事实表。

关系型、维度型和多维数据建模

纯粹的 关系型数据建模 通常出现在 OLTP 数据管道和向应用程序提供数据的关系(事务)数据库中。在这种情况下,数据事务速度和大小至关重要。在这种情况下,我们希望应用高度规范化的数据模式设计。数据总是最新的,但由于在各种维度表上的连接次数较多,运行报告可能成为一个具有挑战性的任务。

维度建模 作为一个过程,旨在确保最有效的数据存储结构以及更快的数据转换,以处理大量数据。我们希望这样做的原因很简单——提供一个更高效的数据平台,便于管理和数据治理。例如,雪花模式有助于减少数据冗余,这与星型模式相反,更适合用于 OLAP 分析和报告(无需执行昂贵的连接操作)。

多维数据模型立方体形式表示数据。

三维数据立方体中的数据点(值)。作者提供的图片。

它增加了复杂性,但也使数据结构对复杂报告更加高效。数据存储在聚合立方体(表)中,这为用户提供了更多执行强大 OLAP 数据转换的能力,即透视、钻取功能和同时处理多个维度的高级仪表板。通常,不需要对数据进行聚合和保留。我们可以使用 SQL 和 ROLLUP 将数据模型定义为立方体[2]。

ROLLUP 函数用于在多个级别上执行聚合。这在处理维度图时非常有用。

作者提供的图片

请参考下面的 SQL 代码。它提供了输入数据和所需的 SQL 转换,以创建一个多维数据模型。

with data as (
    select
    current_timestamp() as ts           
    ,'stage'            as context_type 
    ,1                  as user_id      
    ,100                as credit_value 
    , true              as is_gift
union all
    select
    timestamp_sub(current_timestamp(), interval 24 hour) as ts           
    ,'user'             as context_type 
    ,1                  as user_id      
    ,200                as credit_value 
    ,false              as is_gift
union all
    select
    timestamp_sub(current_timestamp(), interval 24*2 hour) as ts           
    ,'user'             as context_type 
    ,3                  as user_id      
    ,300                as credit_value 
    ,true               as is_gift

)
, results as (
select 
     date(ts) as date 
    ,context_type
    ,sum(credit_value)/100 as daily_credits_spend
from data    

group by rollup(1, context_type)
order by 1
)

select
  date
  ,if(context_type is null, 'total', context_type) as context_type
  ,daily_credits_spend
from results
order by date
; 

我们创建的任何数据集作为自定义 SQL 查询都可以被视为维度模型。

高级 SQL 技术入门

在 1 到 10 的评分中,你的数据仓库技能有多好?

高级 SQL 技术入门

维度和多维模型在数据仓库解决方案中非常常见。维度数据模型使模式对业务用户更加简单和清晰。

如何创建一个维度模型?

为了将维度模型元素引入我们的数据平台设计中,我们创建维度模型。它可以是一个模式、一个数据库,甚至是数据湖中我们数据平台的一部分,能够描述由许多事实表定义的各种业务流程。让我们考虑一个包含两个事实表的单一业务流程。

首先,我们需要识别一个业务流程,并将其转换到我们的数据模型中,定义每个事实表的粒度。让我们假设这是一个应用内购买,支付交易的事实表行数据如下:

create table if not exists production.payment_transaction_raw (
  src string
)
partition by date(_partitiontime)
;
insert production.payment_transaction_raw (src)
select
'{"transaction_id":1,"user_id":1,"reputation":99,"total_cost:9.99,"product_id":1001,"product_type":"bot", "currency":"USD","created_at":"2021-01-01 01:00:01 UTC"}' union all select
'{"transaction_id":2,"user_id":2,"reputation":99,"total_cost:4.99,"product_id":2001,"product_type":"charm", "currency":"USD","created_at":"2022-01-01 01:00:01 UTC"}' union all select
'{"transaction_id":3,"user_id":1,"reputation":99,"total_cost:6.99,"product_id":3001,"product_type":"bot", "currency":"USD","created_at":"2023-01-01 01:00:01 UTC"}' union all select
;

粒度指的是我们表中存储的信息级别。例如,在我们的例子中,应用内支付按每个交易记录;因此,粒度是交易级别,可以很容易地转换为每日。重要的是,我们的维度模型中的事实表必须与指定的粒度一致。这意味着我们希望将原始数据加载到相同的维度结构中。

通常,我们希望转换和增量更新数据,如下所示:

create table if not exists production.payment_transaction (
  transaction_id   int64   
 ,user_id          int64   
 ,reputation       int64   
 ,total_cost       float64 
 ,product_id       int64
 ,product_type     string
 ,currency         string  
 ,created_at       timestamp
)
partition by date(created_at)
;
insert production.payment_transaction (
  transaction_id  
 ,user_id         
 ,reputation      
 ,total_cost      
 ,product_id      
 ,product_type
 ,currency        
 ,created_at      
)

-- insert new batch and avoid duplicates:
select
  raw.transaction_id  
 ,raw.user_id         
 ,raw.reputation         
 ,raw.total_cost      
 ,raw.product_id          
 ,raw.product_type        
 ,raw.currency        
 ,raw.created_at      
from (
   select
     cast(json_extract(src,'$.transaction_id') as int64)              as transaction_id  
    ,cast(json_extract(src,'$.user_id') as int64)                     as user_id         
    ,cast(json_extract(src,'$.reputation') as int64)                  as reputation         
    ,cast(json_extract(src,'$.total_cost') as float64)                as total_cost      
    ,cast(json_extract(src,'$.product_id') as int64)                  as product_id      
    ,cast(json_extract(src,'$.product_type') as string)               as product_type      
    ,cast(json_extract(src,'$.currency') as string)                   as currency        
    ,cast(json_extract_scalar(src, '$.created_at') as timestamp)      as created_at      
   from
    production.payment_transaction_raw
) raw
left join production.payment_transaction self on
   raw.transaction_id = self.transaction_id
where
   self.transaction_id is null -- insert only new records that don't exit.
;

接下来,我们需要为每个事实表定义维度。在我们的案例中,支付交易作为事实。这些事实可以包含有用的数据,例如销售的总数量、货币兑换率、总收入、税收等。进一步的维度将为我们的应用内购买数据提供背景,即时间、产品和用户维度数据。每个维度都可以帮助回答重要的业务问题。例如,我们可能想知道哪个是最畅销的产品,或者用户的平均花费是多少。

每个应用内支付交易将拥有描述事实与维度关系的外键,并指向相关的产品、购买时间或用户资料。

接下来,我们希望定义层级。层级有助于业务用户理解数据集的不同粒度级别。它还帮助导航和高级报告功能,即钻取。例如,产品维度可能具有一个层级,从类别类型再到ID子 ID

我们希望在模型中使用一些称为度量的计算。度量是我们使用事实表计算以生成数据洞察的内容。可以是总收入净收入税额等。

使用这个多维模型,企业可以回答一些关键问题:

  • 去年或上个月我们每个产品的总收入是多少?

  • 具有何种声誉级别的用户花费更多?

  • 哪个产品是最畅销的?

  • 今年的收入如何与去年同期相比?

最后,我们希望通过用户验证我们的数据模型。

维度建模的好处

灵活性。 一个维度建模框架扩展了数据仓库的过程。该设计高度适应以适应新的业务需求或更改中央仓库。为了表示更新后的业务流程,可以向模型中添加新实体或调整现有实体的布局。维度模型可以轻松处理这些变化。可以向维度表中添加更多列,而不会干扰使用原始表的当前商业智能应用程序。

多维分析: 正如前面的示例所示,我们可以使用额外的维度来创建更好的图表和更复杂的图形。层级结构有助于理解数据,并同时进行分析,报告仪表板中还增加了钻取功能。它针对商业智能(BI)需求进行了优化,并且易于理解。现代 BI 工具内置了原生设计的 AI 功能,以理解维度结构和数据[3]。

## 分析中的人工智能

AI 驱动的商业智能。是炒作还是现实?

towardsdatascience.com

改进的查询速度: 多维表通常是非规范化的,并带来星型架构设计的所有性能优势。与雪花架构相对,它通过减少join操作实现更快的数据检索

维度数据建模的局限性

尽管维度建模是 OLAP 用途的强大技术,但它确实有一定的限制,在某些情况下,可能不是最佳选择。

数据经常变化: 由于维度数据集通常是非规范化的,这里的数据冗余较高。非规范化的数据集可能在实时修改的需求下表现不佳。OLTP 方法可能更适合这种情况。

非结构化数据: 维度数据建模在稀疏和非结构化数据上表现不佳。对于有限的事实数据,这些数据可以与维度关联,可能会低效,例如文档和文本数据。在这种情况下,面向文档的数据库解决方案可能是更好的选择。

存储: 非规范化的数据集需要比 OLTP 模式更多的存储空间。

结论

由于其提供的好处,维度建模仍然是开发业务数据仓库时最常用的数据建模方法。它通过多维分析提供更好的报告能力,通过减少联接优化查询性能并加快数据检索。维度建模使数据仓库设计过程更灵活,更具适应性。它也有一定的局限性,使其与非结构化数据集和 OLTP 管道不兼容,因为后者对数据冗余的要求较高。总体而言,多维建模是一个出色的 OLAP 和报告技术,能够提供更好的查询性能。

推荐阅读

[1] medium.com/towards-data-science/data-engineering-interview-questions-fdef62e46505

[2] medium.com/towards-data-science/advanced-sql-techniques-for-beginners-211851a28488

[3] medium.com/towards-data-science/artificial-intelligence-in-analytics-f11d2deafdf0

[4] towardsdatascience.com/modern-data-engineering-e202776fb9a9

[5] towardsdatascience.com/how-to-become-a-data-engineer-c0319cb226c2

数据可观察性:分析与机器学习团队的应用

原文:towardsdatascience.com/data-observability-for-analytics-and-ml-teams-421f6089d905?source=collection_archive---------6-----------------------#2023-04-06

确保高质量数据流的原则、实践和示例

Sam StoneTowards Data Science Sam Stone

·

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

--

来源: DreamStudio(由作者生成)

几乎 100% 的公司今天依赖数据来推动商业机会,76% 的公司将数据作为形成商业策略的重要组成部分。在当今数字业务时代,越来越多的公司在提供客户体验、建立信任和塑造商业策略时,都从准确的数据开始。数据质量差不仅会让公司难以了解客户的需求,还可能使决策变成一种不必要的猜测。数据质量对于提供良好的客户体验至关重要。

数据可观察性是一组可以在工具中实现的原则,以确保数据准确、最新和完整。如果你希望提高组织的数据质量,这里是数据可观察性可能是你答案的原因以及如何实施。

如何判断你是否需要数据可观察性

数据可观察性变得越来越必要,特别是传统的软件监控方法在处理高容量、高多样性的数据时显得不足。单元测试,评估小块代码在离散、确定性任务上的性能,被现实世界数据可能采取的多样化形状和数值所压倒。例如,一个单元测试可以验证一个预期为布尔值的列确实是布尔值,但如果该列中“真”的百分比在一天到另一天之间变化很大呢?甚至只是略微变化。另一方面,端到端测试,评估一个横跨代码库和服务的完整系统,被跨团队的动态数据管道复杂性所压倒。单元测试和端到端测试是必要的,但不足以确保在有复杂数据需求和复杂表格的组织中保持高数据质量。

你的组织需要数据可观察性的三个主要迹象——这不仅仅与机器学习有关:

  • 上游数据的变化经常破坏下游应用程序,尽管上游团队进行了预防性措施

  • 数据问题经常由客户(内部或外部)发现,而不是由拥有相关表格的团队发现

  • 你正在朝着一个集中的数据团队迈进

我在Opendoor工作了四年,该平台是一个用于住宅房地产交易的电子商务平台,并且是一个大型的买卖房屋平台——我们用来评估房屋价值的数据丰富但常常自相矛盾。我们使用数百个数据源,并维护成千上万的表——包括公共数据、第三方数据和专有数据——这些数据常常相互矛盾。例如,一套房子可能在最近的 MLS 列表和公共税务评估中提供了不同的平方英尺数据。房主在出售房屋时可能会声明最高的平方英尺,但在处理税务部门时则声明最低的面积。获取“真实情况”并不总是容易,但我们通过合成多个来源的数据来提高数据准确性——这就是数据可观察性派上用场的时候。

家庭数据示例,突显源系统不一致之处:来源:Opendoor,经许可

定义健康表格

数据可观察性简单来说就是应用能够量化动态表健康状况的框架。要检查表中的行和列是否符合预期,请考虑以下因素和问题:

  • 新鲜度——数据最后一次更新是什么时候?

  • 数据量——最近添加或更新了多少行?

  • 重复——是否有任何行是冗余的?

列:

  • 模式——你预期的所有列都存在吗(还有一些你不期望的列吗)

  • 分布——描述数据的统计信息发生了怎样的变化?

新鲜度、数据量、重复和模式检查都相对容易实现(即,如果你期望数据的形状随着时间保持稳定)。

或者你可以使用简单的时间序列模型来评估这些指标,如果你的数据形状在以渐进和可预测的方式变化,可以调整确定性检查参数。例如,如果你客户量增长了 X%,你可以设置行数据量检查,以便有一个随着时间推移而逐步增加的可接受窗口。在 Opendoor,我们知道房地产交易在节假日时非常少,因此我们能够设置规则,在这些日子调整警报窗口。

列分布检查通常是复杂性和重点所在。它们往往最难做到准确,但做好了则回报最高。列分布检查的类型包括以下内容:

  • 数值——均值、中位数、第 X 百分位数,……

  • 类别——列基数、最常见值、第 2 常见值,……

  • 百分比缺失值

当你的表格健康时,分析和产品团队可以确信下游用途和数据驱动的洞察是可靠的,并且他们是在建立在一个可靠的基础上。当表格不健康时,所有下游应用程序都需要仔细检查。

异常检测

拥有数据健康框架是一个有用的第一步,但能够将该框架转化为可靠运行的代码、生成有用的警报并且易于配置和维护是至关重要的。以下是从数据质量抽象到启动实时异常检测系统时需要考虑的几个事项:

  • 检测逻辑:如果可以提前定义什么构成行级或列级违规,那么专注于确定性检查的系统(由开发人员手动编写这些规则)可能是最好的。如果你看到异常就能识别(但不能通过确定性规则提前描述),那么专注于概率检测的系统可能更好。如果需要检查的关键表格数量如此庞大,以至于手动编写逻辑不可行,那么同样适用。

  • 集成:你的系统应该与你已经拥有的核心系统集成,包括数据库、警报系统(例如 PagerDuty),以及——如果你有的话——数据目录(例如 SelectStar)。

  • 成本:如果你有一个小型的工程团队但预算没有限制,则倾向于选择第三方解决方案。如果你的预算有限但工程团队很大,并且有非常独特的需求,则倾向于选择内部构建的第一方解决方案。

  • 数据类型:异常检测的方式取决于数据是结构化、半结构化还是非结构化的,因此了解你正在处理的数据类型非常重要。

当涉及到检测非结构化数据(例如文本、图像、视频、音频)中的异常时,很难计算有意义的列级描述统计信息。非结构化数据是高维的——例如,一个小的 100x100 像素图像可能有 30,000 个值(10,000 个像素 x 三种颜色)。与其检查数据库中 10,000 列的图像类型的变化,不如将图像转换为少量维度,并对这些维度应用列级检查。这一降维过程称为数据嵌入,适用于任何非结构化数据格式。

这是我们在 Opendoor 遇到的一个例子:我们在第一天接收到 100,000 张图像,其中 20%标记为“is_kitchen_image=True”。第二天,我们接收到 100,000 张图像,其中 50%标记为“is_kitchen_image=False”。这可能是正确的——但分布变化的规模肯定应该引发异常警报!

如果你的团队专注于非结构化数据,请考虑具有内置嵌入支持的异常检测。

自动化数据目录

自动化你的数据目录使得数据对开发人员、分析师和非技术团队成员更易于访问,从而促进更好的数据驱动决策。在构建数据目录时,以下是几个关键问题:

表格文档

  • 每一行代表什么?

  • 每一列代表什么?

  • 表格所有权——当表格出现问题时,我应该联系组织中的谁?

表格血缘(代码关系)

  • 上游有哪些表格?它们是如何被查询或转换的?

  • 下游有哪些表格、仪表板或报告?

现实世界中的应用

  • 这个表格的受欢迎程度如何?

  • 这个表格和/或列在查询中通常如何使用?

  • 我们组织中的谁使用这个表格?

在 Opendoor,我们发现表格文档化很难自动化,成功的关键在于我们的工程和分析团队在一个明确的地方填写这些定义的责任划分。另一方面,我们发现自动检测表格血统和现实世界中的应用(通过解析 SQL 代码,包括检入到 Github 的代码和更多“即席”SQL 支持仪表板)为这些元数据提供了很高的覆盖率和准确性,无需手动元数据注释。

结果是,人们知道在哪里找到数据,使用什么数据(以及不使用什么数据),并且更好地理解他们所使用的数据。

特定于机器学习的策略

机器学习数据在数据可观察性方面有所不同,主要有两个原因。首先,机器学习代码路径通常容易出现细微的错误。机器学习系统通常有两个代码路径,执行类似但略有不同的操作:模型训练,侧重于并行计算并容忍高延迟,以及模型服务,侧重于低延迟计算且通常是顺序进行的。这些双重代码路径为错误进入服务系统提供了机会,特别是当测试仅集中在训练路径上时。这个挑战可以通过两种策略来解决:

  • 使用“黄金集”(或“生产测试”)评估服务推断。首先,收集一组已知正确输出的输入,或者至少在合理的范围内已知(例如,Opendoor 对销售价格有很高信心的家庭价格集合)。接下来,查询生产系统中的这些输入,并将产品系统输出与“实际情况”进行比较。

  • 对服务输入应用分布检查。假设 Opendoor 使用数据训练我们的模型,其中家庭平方英尺的分布在第 25 百分位为 1,000 平方英尺,第 50 百分位为 2,000 平方英尺,第 75 百分位为 3,000 平方英尺。我们会根据这个分布建立界限——例如,第 25 百分位应为 1,000 平方英尺 +/- 10%——并收集对服务系统的调用并对每批次运行检查。

来源:作者提供的图像

机器学习数据在数据可观察性方面的另一个不同之处是“正确”的输出并不总是显而易见的。用户通常不知道什么是错误,或者可能没有动力报告错误。为了解决这个问题,分析和机器学习团队可以征求用户反馈,汇总并分析外部用户和内部用户/领域专家的趋势。

无论是专注于 ML 数据还是整个代码库,数据可观察性都能让你的生活变得更加轻松。它帮助分析和 ML 团队洞察系统性能和健康状况,提高跨离散工具的端到端可见性和监控,并快速识别问题,无论问题源自何处。随着数字业务的持续发展、增长和转型,建立这一健康基础将带来巨大的变化。

Python 中的数据导向编程

原文:towardsdatascience.com/data-oriented-programming-with-python-ef478c43a874

Yehonathan Sharvit 关于数据导向编程的回顾,但以 Python 示例(而非 JavaScript 和 Java)来说明

Tam D Tran-TheTowards Data Science Tam D Tran-The

·发表于 Towards Data Science ·12 分钟阅读·2023 年 5 月 12 日

--

图片由 AltumCode 提供,来源于 Unsplash

数据导向编程 是 Yehonathan Sharvit 编写的一本很棒的书,为数据导向编程(DOP)概念提供了温和的介绍,作为传统面向对象编程(OOP)的替代方案。Sharvit 解构了在 OOP 中有时看似不可避免的复杂元素,并总结了 DOP 的主要原则,帮助我们使系统更加可管理。

正如其名,数据导向编程(DOP)将数据置于首位。这可以通过遵循四个主要原则来实现。这些原则与语言无关。它们可以在面向对象编程(OOP)语言(如 Java、C++ 等)、函数式编程(FP)语言(如 Clojure 等)或通用编程语言(如 Python、JavaScript)中表示。虽然作者用 JavaScript 和 Java 说明了他的示例,但本文尝试用 Python 展示这些思想。

跟随本文,你会发现简单的 Python 代码片段,展示了如何遵守或违背每个原则。Sharvit 还阐明了每个原则的好处和成本——其中许多在 Python 中相关,而有些则不相关。

请注意,所有提到的原则、相应的优缺点均归功于 Yehonathan Sharvit,而关于这些陈述在 Python 中的适用性以及与 Python 代码示例相关的观点则是我个人的看法。

原则 #1: 将代码与数据分离

“以一种代码位于不依赖于函数上下文中封装数据的函数的方式将代码与数据分离。” — Yehonathan Sharvit

在 Python 中,遵循这一原则的自然方式是使用顶层函数(用于代码)和仅包含字段的数据类(用于数据)。虽然 Sharvit 在他的书中分别说明了如何在 OOP 和 FP(函数式编程)中遵循这一原则,但我在 Python 中的示例是 OOP 和 FP 的混合体。

请参考下面的代码片段作为代码(行为)与数据(事实/信息)分离的示例。

from dataclasses import dataclass

@dataclass
class AuthorData:
    """Class for keeping track of an author in the system"""

    first_name: str
    last_name: str
    n_books: int

def calculate_name(first_name: str, last_name: str):
    return f"{first_name} {last_name}"

def is_prolific(n_books: int):
    return n_books > 100

author_data = AuthorData("Isaac", "Asimov", 500)
calculate_name(author_data.first_name, author_data.last_name)
# 'Isaac Asimov'

好处 # 1: “代码可以在不同的上下文中重用” — Yehonathan Sharvit

从上面的示例可以看出,calculate_name() 不仅可以用于作者,还可以用于用户、图书馆员或任何具有名字和姓氏字段的人。处理全名计算的代码与处理作者数据创建的代码是分开的。

@dataclass
class UserData:
    """Class for keeping track of a user in the system"""

    first_name: str
    last_name: str
    email: str

user_data = UserData("John", "Doe", "john.doe@gmail.com")
calculate_name(user_data.first_name, user_data.last_name)
# 'John Doe'

好处 # 2: “代码可以在隔离的环境中进行测试” — Yehonathan Sharvit

以下是一个不符合原则 #1 的示例。

class Address:
    def __init__(self, street_num: int, street_name: str,
                 city: str, state: str, zip_code: int):
        self.street_num = street_num
        self.street_name = street_name
        self.city = city
        self.state = state
        self.zip_code = zip_code

class Author:
    def __init__(self, first_name: str, last_name: str, n_books: int, 
                 address: Address):
        self.first_name = first_name
        self.last_name = last_name
        self.n_books = n_books
        self.address = address

    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"

    @property
    def is_prolific(self):
        return self.n_books > 100

address = Address(651, "Essex Street", "Brooklyn", "NY", 11208) 
author = Author("Issac", "Asimov", 500, address)
assert author.full_name == "Issac Asimov"

为了测试 Author 类中的 full_name() 属性,我们需要实例化 Author 对象,这要求我们为所有属性提供值,包括那些与我们正在测试的行为无关的属性(如 n_booksaddress 自定义类)。这是为了测试单个方法而进行的不必要的复杂和繁琐的设置。

另一方面,在 DOP 版本中,为了测试 calculate_name() 代码,我们可以创建数据以隔离地传递给函数。

assert calculate_name("Issac", "Asimov") == "Issac Asimov"

成本 # 1: “无法控制哪些代码可以访问哪些数据” — Yehonathan Sharvit

“…在 OOP 中,数据封装在一个对象中,这保证了数据只能通过对象的方法访问。在 DOP 中,由于数据独立存在,它可以被任何代码访问……这本质上是不安全的。” — Yehonathan Sharvit

这个声明在 Python 中不适用。

在 Python 中,类持有的数据仍然可以被任何引用对象的代码访问。例如:

class Author:
    def __init__(self, first_name: str, last_name: str, n_books: int):
        self.first_name = first_name
        self.last_name = last_name
        self.n_books = n_books

    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"

    @property
    def is_prolific(self):
        return self.n_books > 100

author = Author("Issac", "Asimov", 500, address)
author.full_name
# 'Issac Asimov'

同样,除非我们将数据存储在全局变量中,否则我们仍然可以使用作用域(函数、循环等)来控制谁可以访问/更改 Python 中的数据。

成本 #2: “没有封装” — Yehonathan Sharvit

“在 DOP 中,操控数据的代码可以在任何地方。这可能使开发者难以发现 [某个特定函数] 的存在,从而导致浪费时间和代码重复。” — Yehonathan Sharvit

这在我们上面的 Python 示例中是正确的。例如,我们的 AuthorData 数据类可能在一个文件中,而 calculate_name() 函数可能在另一个文件中。

原则 #2: 使用通用数据结构表示数据

“在 DOP 中,数据使用通用数据结构表示,例如映射(或字典)和数组(或列表)。” — Yehonathan Sharvit

在 Python 中,我们的通用数据结构选项包括 dictlisttuple

在本文中,我使用 Python 的 dataclass,它可以被视为“带有默认值的可变命名元组”。请注意,这不是 Sharvit 所说的“通用数据结构”。Python 的 dataclass 是一种更接近面向对象编程(OOP)而非面向过程编程(DOP)的混合体。然而,与字典和元组相比,这种替代方案不易出现拼写错误,通过类型提示更具描述性,有助于以更清晰、更简洁的方式表示嵌套复杂结构等。此外,如果需要,也可以很容易地将其转换为字典或元组。

from dataclasses import dataclass, asdict

@dataclass
class AuthorData:
    """Class for keeping track of an author in the system"""

    first_name: str
    last_name: str
    n_books: int

author_data = AuthorData("Isaac", "Asimov", 500)
asdict(author_data)
# {'first_name': 'Isaac', 'last_name': 'Asimov', 'n_books': 500}

好处 #1: “能够使用不局限于我们特定用例的通用函数” — Yehonathan Sharvit

通过通用结构,我们可以利用丰富的内置 Python 函数集来操作 dictlisttuple 等上的数据。

以下是一些可以用来操作存储在 dict 中的数据的通用函数示例。

author = {"first_name": "Issac", "last_name": "Asimov", "n_books": 500}

# Access dict values
author.get("first_name")

# Add new field to dict
author["alive"] = False

# Update existing field
author["n_books"] = 703

这意味着我们不必学习和记住每个人类的自定义方法。此外,如果我们更改一些库版本,通用函数不会崩溃。它们只有在 Python 语言更改时才会崩溃(这几乎从未发生)。

好处 #2: “**灵活的数据模型” — Yehonathan Sharvit

“使用通用数据结构时,数据可以创建为没有预定义形状的形式,并且可以随意修改其形状。” — Yehonathan Sharvit

在下面的示例中,列表中的字典并不是所有的都有相同的键。额外的键可以存在于第二个字典中,只要必要的字段存在即可。

names = []
names.append({"first_name": "Isaac", "last_name": "Asimov"})
names.append({"first_name": "Jane", "last_name": "Doe", 
              "suffix": "III", "age": 70})

成本 #1: “性能损失” — Yehonathan Sharvit

这在 Python 中并不完全适用。

在 Python 中,检索类成员的值与检索字典中与键相关的值之间的性能差异不大。与 Java 不同,Python 没有编译步骤,这意味着在访问类成员时没有编译器优化。

然而,并不是所有的通用数据结构都是相同的。setdict 的查找时间比 listtuple 更高效,因为集合和字典使用哈希函数来立即确定特定数据,而无需进行搜索。

成本 #2: “没有数据模式” — Yehonathan Sharvit

“当数据从一个类实例化时,数据形状的信息就在类定义中。类级别的数据模式存在使得发现预期的数据形状变得容易。当数据使用通用数据结构表示时,数据模式不是数据表示的一部分。” — Yehonathan Sharvit

例如,我们可以轻松地识别下方作为类对象实例化的 FullName 的数据形状。

class FullName:
    def __init__(self, first_name, last_name, suffix):
        self.first_name = first_name
        self.last_name = last_name
        self.suffix = suffix

成本 #3: “没有编译时检查数据是否有效” — Yehonathan Sharvit

这在 Python 中并不完全适用。

在 Python 中没有像 Java 那样的编译步骤。Python 唯一的编译时检查就是运行像 mypy 这样的工具。

然而,夏维特关于如何通过通用数据结构中的数据形状错误可能会遗漏的例子在 Python 中仍然可以部分演示,如下所示。

当数据传递到 FullName 类时,如果数据不符合它所期望的形状,就会在运行时发生错误。例如,如果我们拼错了存储名字的字段(fist_name 而不是 first_name),我们会得到 TypeError: __init__() got an unexpected keyword argument 'fist_name'

class FullName:
    def __init__(self, first_name, last_name, suffix):
        self.first_name = first_name
        self.last_name = last_name
        self.suffix = suffix

FullName(fist_name="Jane", last_name="Doe", suffix="II")

然而,使用通用数据结构时,拼错字段可能不会导致错误或异常。相反,名字会神秘地从结果中省略。

names = []
names.append({"first_name": "Jane", "last_name": "Doe", "suffix": "III"})
names.append({"first_name": "Isaac", "last_name": "Asimov"})
names.append({"fist_name": "John", "last_name": "Smith"})

print(f"{names[2].get('first_name')} {names[2].get('last_name')}")
# None Smith

成本 #4: “需要显式的类型转换” — 叶洪·夏维特

这不能转化为 Python。

Python 是一种动态类型语言。它不需要显式的类型转换。

原则 #3: 数据是不可变的

“根据 DOP,数据不应更改!而是创建数据的新版本。” — 叶洪·夏维特

为了遵循这一原则,我们将 dataclass 设置为冻结(即不可变)。

@dataclass(frozen=True)
class AuthorData:
    """Class for keeping track of an author in the system"""

    first_name: str
    last_name: str
    n_books: int

Python 内置的不可变数据类型有 intfloatdecimalboolstringtuplerange。注意 dictlistset 是可变的。

好处 #1: “对所有人都能自信地访问数据” — 叶洪·夏维特

“当数据是可变的时,我们在将数据作为参数传递给函数时必须小心,因为它可能会被修改或克隆。” — 叶洪·夏维特

在下面的示例中,我们最初将一个空列表作为默认参数传递给函数。由于列表是一个可变对象,每次调用函数时,列表都会被修改,并且在后续调用中使用不同的默认值。

def append_to_list(el, ls=[]):
    ls.append(el)
    return ls

append_to_list(1)
# [1]
append_to_list(2)
# [1, 2]
append_to_list(3)
# [1, 2, 3]

要修复上述用例,我们可以这样做:

def append_to_list(el, ls=None):
    if ls is None:
        ls = []    
    ls.append(el)
    return ls

append_to_list(1)
# [1]
append_to_list(2)
# [2]

这段代码按预期工作,因为 None 是不可变的。

“当数据是不可变的时,可以自信地将其传递给任何函数,因为数据永远不会改变。” — 叶洪·夏维特

好处 #2: “可预测的代码行为” — 叶洪·夏维特

这里是一个不可预测的代码示例:

from datetime import date

dummy = {"age": 30}

if date.today().day % 2 == 0:
    dummy["age"] = 40

dummy 字典中的 age 值是不可预测的。它取决于你是在偶数天还是奇数天运行代码。

然而,使用不可变的数据可以保证数据永远不会改变。

author_data = AuthorData("Issac", "Asimov", 500)

if date.today().day % 2 == 0:
    author_data.n_books = 100
# dataclasses.FrozenInstanceError: cannot assign to field "n_books"

上面的代码会出错,显示 dataclasses.FrozenInstanceError: cannot assign to field "n_books"。使用冻结的数据类,无论是偶数天还是奇数天,author_data.n_books 始终是 500。

好处 #3: “快速的相等性检查” — 叶洪·夏维特

Python 有两个类似的运算符用于检查两个对象是否相等:is==is 通过比较内存地址的整数相等性来检查对象的身份。== 通过检查实际存储的内容来检查值的相等性。

# String is immutable
x = "abc"

# Note that the identity of `x` and `abc` is the same
print(id(x))
# 140676188882480
print(id("abc"))
# 140676188882480

print(x == "abc")
# True
print(x is "abc")
# True

# List is mutable
y = [1, 2, 3]

# Note that the identity of `y` and `[1, 2, 3]` is different
print(id(y))
# 140676283875904
print(id([1, 2, 3])
# 140676283875584

print(y == [1, 2, 3])
# True
print(y is [1, 2, 3])
# Fasle

如上所示,is== 对于 x(即不可变数据类型)表现相同,但对 y(即可变数据类型)表现不同。使用不可变数据对象时,is 的行为更可预测。而且,is 通常比 == 更快,因为比较对象地址比比较所有字段要快。因此,不可变数据通过引用比较数据实现了快速的相等性检查。

优点 #4:免费的并发安全性” — Yehonathan Sharvit

当数据在多线程环境中是可变的时,可能会发生竞态条件失败。例如,假设两个线程尝试通过加/减 10 来访问和修改 x 的值:

竞态条件失败示例。图片由作者提供。

可能的三种答案是: x=90x=100,和 x=110。根据执行顺序,程序的行为每次运行时都会变化,这样不安全且容易损坏。为了确保并发安全,数据应该处于不可变状态。

成本 #1: “性能损失” — Yehonathan Sharvit

由于 list 是可变的,而 tuple 是不可变的,当我们扩展这两种对象时,list 的身份保持不变,而一个全新的 tuple 被创建并具有不同的身份。

list1 = [1, 2, 3]
tuple1 = (1, 2, 3)

print(id(list1))
# 140218642718848
print(id(tuple1))
# 140218642722496

list1 += [4, 5]
tuple1 += (4, 5)

print(id(list1))
# 140218642718848
print(id(tuple1))
# 140218642772352

每次修改不可变对象时需要将内容复制到一个新对象中,这需要额外的内存,并增加了 CPU 的负担,特别是对于非常大的集合。

成本 #2: “对不可变数据结构的库要求” — Yehonathan Sharvit

这不能转译到 Python。

frozensettuple 是 Python 中一些基本的内置不可变数据结构。我们并不总是需要包含第三方库来遵循数据不可变性原则。

原则 #4: 将数据模式与数据表示分开

“在 DOP 中,数据的期望形状表示为(元)数据,这些数据与主要的数据表示分开保存。” — Yehonathan Sharvit

以下是一个基本的 JSON 模式(本质上是一个字典),它描述了数据的格式,这些数据也以字典形式表示。该模式定义了哪些字段是必需的以及字段的数据类型,而数据则按原则 #3 由通用数据结构表示。

schema = {
    "required": ["first_name", "last_name"],
    "properties": {
        "first_name": {"type": str},
        "last_name": {"type": str},
        "books": {"type": int},
    }
}

data = {
    "valid": {
        "first_name": "Isaac",
        "last_name": "Asimov",
        "books": 500
    },
    "invalid1": {
        "fist_name": "Isaac",
        "last_name": "Asimov",
    },
    "invalid2": {
        "first_name": "Isaac",
        "last_name": "Asimov",
        "books": "five hundred"
    }
}

数据验证函数(或库)可用于检查数据是否符合数据模式。

def validate(data):
    assert set(schema["required"]).issubset(set(data.keys())), \
        f"Data must have following fields: {schema['required']}"

    for k in data:
        if k in schema["properties"].keys():
            assert type(data[k]) == schema["properties"][k]["type"], \
                f"Field {k} must be of type {str(schema['properties'][k]['type'])}"

validate 函数在数据有效时通过,或者在数据无效时以人类可读的格式返回详细错误。

validate(data["valid"]))
# No error

validate(data["invalid1"])
# AssertionError: Data must have following fields: ['first_name', 'last_name']

validate(data["invalid2"])
# AssertionError: Field books must be of type <class 'int'>

优点 #1: “可选字段” — Yehonathan Sharvit

“在 OOP 中,允许类成员为可选项并不容易。在 DOP 中,在映射中声明字段为可选是很自然的。” — Yehonathan Sharvit

这不能转译到 Python。

在 Python 中,即使使用 OOP,允许类成员为可选项也不难。因此,这一好处在 Python 的背景下并不强烈。例如,下面我们可以将n_books的默认参数设置为None,以表示该字段是可选的。

class Author:
    def __init__(self, first_name: str, last_name: str, n_books: int = None):
        self.first_name = first_name
        self.last_name = last_name
        self.n_books = n_books

    @property
    def fullname(self):
        return f"{self.first_name} {self.last_name}"

    @property
    def is_prolific(self):
        if self.n_books:
            return self.n_books > 100

author = Author("Issac", "Asimov")

好处 #2: “高级数据验证条件” — Yehonathan Sharvit

“在 DOP 中,数据验证发生在运行时。这允许定义超出字段类型的数据验证条件。” — Yehonathan Sharvit

与上面定义的最小模式相比,以下模式可以扩展以包含每个字段的更多属性。

schema = {
    "required": ["first_name", "last_name"],
    "properties": {
        "first_name": {
            "type": str,
            "max_length": 100,
        },
        "last_name": {
            "type": str,
            "max_length": 100
        },
        "books": {
            "type": int,
            "min": 0,
            "max": 10000,
        },
    }
}

尽管 Sharvit 提到的 DOP 原则的所有优缺点并不直接适用于 Python,但基本原则仍然稳健。这种方法促进了更容易推理、测试和维护的代码。通过接受 DOP 的原则和技术,Python 程序员可以创建更具可维护性和可扩展性的代码,并释放数据的全部潜力。

特别感谢 Eddie Pantridge 对本文改进所作出的深思熟虑的评论和努力。

数据管道设计模式

原文:towardsdatascience.com/data-pipeline-design-patterns-100afa4b93e3

选择合适的架构及其示例

💡Mike ShakhomirovTowards Data Science 💡Mike Shakhomirov

·发表于 Towards Data Science ·9 分钟阅读·2023 年 1 月 2 日

--

照片由 israel palacioUnsplash 提供

通常数据在多个步骤中被处理、提取和转换。因此,一系列数据处理阶段可以称为数据管道

选择哪个设计模式?

需要考虑许多因素,即使用哪个数据栈?考虑哪些工具?如何从概念上设计数据管道?ETL 还是 ELT?也许是 ETLT?什么是变更数据捕获?

我将在这里尝试涵盖这些问题。

一个数据管道

所以它是一个数据处理步骤的序列。由于这些阶段之间的逻辑数据流连接,每个阶段都会生成一个输出,作为下一个阶段的输入

每当在 A 点和 B 点之间进行数据处理时,就会有一个数据管道。

数据管道的三个主要部分是源、处理步骤或步骤、以及目标。从外部 API(源)提取的数据可以加载到数据仓库(目标)中。这是一个最常见的数据管道的例子,其中源和目标是不同的。然而,这并非总是如此,因为目标到目标的管道也存在。

例如,数据可以最初作为数据仓库中的reference表格存在,然后经过一些数据转换后,可以落地到一个新的模式中,例如,在analytics中用于报告解决方案。

每当在源和目标之间处理数据时,总会有一个数据管道。

数据管道。图片由作者提供

由后端仅一个source创建的事件数据,通过使用 Kinesis Firehose 或 Kafka 流构建的事件流,可以提供给多个不同的destinations

考虑来自Google Analytics用户参与数据,因为它作为事件流流动,可以用于用户活动的分析仪表板以及用于流失预测的机器学习(ML)管道中。

尽管使用相同的数据源,但两个管道独立操作,必须成功完成后用户才能看到结果。

或者,可以将来自两个或更多source位置的数据汇总到一个destination中。例如,来自不同支付商提供商的数据可以转换为业务智能(BI)仪表板的收入报告。

数据质量检查、数据清洗、转换、丰富、过滤、分组、聚合以及对数据应用算法是数据管道中的常见步骤。

架构类型和示例

数据管道架构这一术语根据情况可能有多种含义。一般来说,它可以分为概念性(逻辑)和平台级别或架构类型。

概念性逻辑部分描述了数据集从采集到服务的处理和转换方式,而平台架构则专注于特定场景中使用的一组工具和框架,以及它们各自的功能。

这是数据仓库管道的逻辑结构

概念性数据管道设计。图片由作者提供

在这篇文章中,我找到了一种从 Firebase/Google Analytics 4 中提取实时数据并将其加载到 BigQuery 的方法:

## 如何从 Google Analytics 4 和 Firebase 中提取实时的日内数据并加载到 BigQuery

并且始终为您的自定义报告保持最新的数据

towardsdatascience.com

这是一个概念性数据湖管道示例:

概念性数据管道设计。图片由作者提供

例如,在之前的帖子中,我写了如何从 MySQL 数据库中提取数据,将其保存到 Cloud Storage,以便稍后进行分析:

## MySQL 数据连接器用于您的数据仓库解决方案

如何构建一个并分块导出数百万行、流式传输、捕获实时数据变化或提取数据并保存……

towardsdatascience.com

这是一个平台级架构示例:

平台级数据管道设计。图片由作者提供

这是许多湖仓架构解决方案中非常常见的模式。在这篇博客文章中,我创建了一个定制的数据摄取管理器,它在 Cloud Storage 中创建新对象事件时被触发:

## 如何使用无服务器摄取管理器和 Node.js 处理 BigQuery 中的数据加载

文件格式、yaml 管道定义以及用于简单可靠的数据摄取的转换和事件触发器…

towardsdatascience.com

流处理

应用程序可以通过流处理对新数据事件做出即时响应。

流处理是企业数据的“必备”解决方案。

流处理会在数据生成时收集和处理数据,而不是像批处理那样在预定的频率下进行汇总。常见的使用案例包括异常检测和欺诈预防实时个性化和营销以及物联网。数据和事件通常由“发布者”或“源”生成,并传输到“流处理应用程序”,在这里数据被立即处理,然后发送到“订阅者”。通常,作为,你可以遇到使用Hadoop、Apache Kafka、Amazon Kinesis等构建的流应用程序。“发布者/订阅者”模式通常被称为pub/sub

在这个例子中,我们可以设置一个ELT 流式数据管道到AWS Redshift。AWS Firehose 传输流可以提供这种无缝集成,当流式数据直接上传到数据仓库表时。然后,数据将被转化为报告,使用AWS Quicksight作为 BI 工具。

批处理

批处理是一种模型,其中数据根据预定的阈值或频率被收集,无论是微批处理还是传统批处理,然后进行处理。历史上,数据环境中的工作负载主要是批处理导向的。然而,现代应用程序持续生成大量数据,企业倾向于微批处理和流处理,在这种处理方式中,数据被立即处理以保持竞争优势。微批处理加载的技术包括Apache Spark Streaming、Fluentd 和 Logstash,这与传统批处理非常相似,在传统批处理中,事件按计划或小组处理。

当数据的准确性此时并不重要时,这是一个很好的选择。

这种数据管道设计模式在需要持续处理的小型数据集上效果更好,因为 Athena 是根据扫描的数据量收费的。

假设你不想在 MySQL 数据库实例上使用 *change log* 。这将是理想的情况,因为支付数据集并不庞大。

Lambda/Kappa 架构

这种架构结合了批处理和流处理方法。它结合了两者的优点,并建议必须保留原始数据,例如,保存在数据湖中,以便在你想要再次使用它来构建新管道或调查故障时使用。它具有批处理流处理(速度)层,这有助于快速响应变化的业务和市场条件。Lambda 架构有时可能会非常复杂,需要维护多个代码库。

Lambda 数据管道设计。图像由作者提供

先转换然后加载?

ETL 被认为是一种传统的方法,并且历史上最广泛使用。随着数据仓库的兴起,ELT 变得越来越流行。

确实,如果我们可以在数据仓库中集中处理,为什么我们需要先进行转换呢?

虚拟化 是另一种数据仓库的流行方法,我们在数据上创建视图而不是物化表。对业务敏捷性的新增要求将成本效益置于次要位置,数据用户可以查询视图而不是

变更数据捕获 是另一种在变化发生时即时更新数据的方法。当通常使用延迟数据管道时,CDC 技术可以识别数据变化并提供有关这些变化的信息。变化通常会被推送到消息队列或作为流提供。

如何选择数据管道架构?

近年来,数据架构组件如数据管道已经发展,以支持大量数据。术语“大数据”可以描述为具有三个特征:体量、种类和速度。大数据可以在各种用例中开启新的机会,包括预测分析、实时报告和警报。由于数据量、种类和速度的显著增加,架构师和开发者不得不适应新的“大数据”需求。新的数据处理框架不断出现。由于现代数据流的高速,我们可能需要使用流式数据管道。数据可以实时收集和分析,从而允许立即采取行动。

然而,流式数据管道设计模式并不总是最具成本效益的。

例如,在大多数数据仓库解决方案中,批量数据摄取是免费的。然而,流式处理可能会带来费用。同样的说法适用于数据处理。在大多数情况下,流式处理是处理数据的最昂贵方式。

大数据volumes要求数据管道能够同时处理事件,因为这些事件通常是同时发送的。数据解决方案必须具备可扩展性。

variety意味着数据可能以不同的格式通过管道传输,通常是非结构化或半结构化的。

架构类型取决于各种因素,即destination类型和数据最终要到达的位置、成本考虑以及你团队的开发栈和你与同事们已经具备的某些数据处理技能。

你的数据管道是否必须是可管理且基于云的,还是更愿意将其部署在本地?

实际上,有许多变量组合可以帮助选择最佳的数据平台架构。那个管道中的速度或数据流率会是多少?你是否需要实时分析,还是近实时的分析就足够了?这将解决你是否需要“流”管道的问题。

例如,有些服务可以创建和运行批处理数据管道,即Google Dataflow。那么它与数据仓库解决方案中的其他管道有何不同?选择取决于现有的基础设施。例如,如果你有一些现有的Hadoop工作负载,那么 GCP DataFlow 将是一个错误的选择,因为它不允许你重用代码(它使用的是 Apache Beam)。在这种情况下,你会希望使用GCP Dataproc,它可以处理Hadoop/Spark代码。

一个经验法则是,如果处理依赖于 Hadoop 生态系统中的任何工具,则应使用 Dataproc。

它基本上是一个 Hadoop 扩展服务。

另一方面,如果你不受现有代码的限制,并且希望可靠地处理不断增加的流数据,那么Dataflow是推荐的选择。如果你喜欢用JAVA做事,可以查看这些Dataflow 模板

这里有一个来自 Google 的系统设计指南

结论

大数据提出了对数据架构的新挑战要求。不断增加的数据格式和数据源的多样性提高了数据集成的重要性,而不影响生产应用。企业越来越倾向于自动化数据集成程序、实时处理流数据,并简化数据湖和数据仓库的生命周期。考虑到过去十年数据量、速度和数据格式的多样性不断增加,这确实成为了一项挑战。现在,数据管道设计必须既稳健又灵活,以简化和自动化创建新的数据管道。日益增加的使用 data mesh/ data mart 平台的趋势要求创建数据目录。为了创建受控的、企业级的数据,并为数据消费者提供便捷的方法来查找、检查和自助提供数据,这个过程也应尽可能自动化。因此,选择合适的数据管道架构可以有效解决这些问题。

最初发布于 https://mydataschool.com

推荐阅读:

[## 使用数据管道 | Cloud Dataflow | Google Cloud

你可以在 Note: google-data-pipelines-feedback 报告 Dataflow 数据管道问题并请求新功能。你…

cloud.google.com [## 云架构指导和拓扑 | 云架构中心 | Google Cloud

发现参考架构、指导和最佳实践,以帮助你在 Google 上构建或迁移工作负载…

cloud.google.com [## GitHub - GoogleCloudPlatform/DataflowTemplates: Google 提供的 Cloud Dataflow 模板管道…

这些 Dataflow 模板旨在解决简单但大规模的云端数据任务,包括数据…

github.com [## 教程

教程 - AWS 数据管道 以下教程将逐步引导你创建和使用…

docs.aws.amazon.com

AWS 数据管道文档 [docs.aws.amazon.com/data-pipeline/]

数据管道编排

原文:towardsdatascience.com/data-pipeline-orchestration-9887e1b5eb7a

正确的数据管道管理简化了部署,增加了数据分析的可用性和可及性。

💡Mike ShakhomirovTowards Data Science 💡Mike Shakhomirov

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

--

图片由 Manuel Nägeli 提供,来源于 Unsplash

什么是数据编排?

DataOps 团队使用数据管道编排作为集中管理和监督端到端数据管道的解决方案。

自动化数据管道的过程被称为数据编排。

正确管理数据管道很重要,因为它几乎影响一切,即数据质量、过程速度和数据治理。

什么是有效的数据管道管理?

  • 透明性和可见性。 让团队中的每个人都知道数据是如何转换的,它来自哪里,以及数据转换过程的结束地点是很重要的。

  • 更快的部署。 能够持续重现数据管道的各个元素至关重要。将这些元素视为数据管道的构建模块。因此,当需要创建新的数据管道时,重要的是能够轻松复制这些构建模块,而不是从头开始创建。

  • 高效的数据治理。 通过创建结构化的数据流,相关团队能够管理和源控流程,数据易于访问和管理。

处理数据工作流时的常见问题

ETL 工作流复杂性

我的经验表明,管理数据管道时最典型的问题是ETL 过程的复杂性。数据管道通常由一系列数据转换步骤定义,这些步骤将数据从源头转移到目的地。有许多工具,甚至是框架、方法和技术可以做到这一点,我之前在这里写过:

## 数据管道设计模式

选择正确的架构示例

towardsdatascience.com

因此,对数据管道及其包含的步骤进行源代码控制是有意义的。记录所有内容非常重要,以便团队其他成员准确了解数据解决方案的功能。

有一个数据管道的可视化表示总是很有帮助的。

如果你使用 Airflow、DBT、Dataform、Jinja 或 AWS Step Functions 部署管道,那很好,通常这些工具提供了很好的依赖关系图功能。

示例依赖关系图。图像由作者提供。

难以复制和部署更改

数据管道通常很复杂,可能需要大量时间来更改相关资源并进行部署。换句话说,这可能成为数据和机器学习工程师非常耗时的任务。

还必须涵盖数据管道的所有部分,而不仅仅是数据转换部分。例如,Airflow 是一个很好的工具来协调数据管道,我们可能想在这里使用 S3 桶连接器来连接数据湖,但你可能还需要描述 S3 资源并将其保存在 Github 上。

这就是基础设施即代码(IAC)变得有用的地方。

使用像 Terraform 和 AWS CloudFormation 这样的 IAC 工具,我们可以描述我们数据管道可能需要的所有资源,而不仅仅是实际执行数据转换的资源。例如,我们可以描述不仅是转换数据的管道资源,即 AWS Lambda 函数和其他服务,还可以描述数据存储资源、通知设置和这些微服务的警报。一些 IAC 解决方案是平台无关的(Terraform),一些则不是(AWS CloudFormation),它们都有其优缺点根据手头的数据堆栈和 DataOps 团队的技能而定。

[## 初学者的基础设施即代码

使用这些模板像专业人士一样部署数据管道

levelup.gitconnected.com](https://levelup.gitconnected.com/infrastructure-as-code-for-beginners-a4e36c805316?source=post_page-----9887e1b5eb7a--------------------------------)

可以在不同环境中持续重复和部署的数据管道解决方案很棒,因为它们是源代码控制的,并具有很好的CI/CD 特性。所有这些有助于避免潜在的人为错误,并减少数据工程时间和成本

现代的数据管道管理和协调方法是减少上述所有潜在问题的,即

为了减少人为错误,能够轻松复制数据管道资源,可视化依赖关系并提高数据质量。

正确的数据协调可以增加数据的可用性和分析的可及性。

需要管理的典型数据流

通常,数据管道指的是一组数据相关的资源,帮助我们将数据从 A 点传送到 B 点并进行转换。

资源指的是工具,在概念层面上,我们有三个主要的数据处理任务,必须能够有效执行:

- 数据存储

这可以是 Google Cloud Storage 中的数据湖、AWS S3 数据湖等,或者任何关系型和非关系型数据库,甚至是通过 API 提供的第三方资源。它们的共同目的是存储数据。在大多数情况下,这就是我们数据管道设计图中数据的来源。

- 数据加载或摄取。

这些可以是管理工具(如 Fivetran、Stitch 等),也可以是定制的无服务器微服务和各种 AWS Lambda 或 GCP Cloud Functions。它们的共同目的是执行 ETL 并将数据加载到目标中,例如数据仓库或根据我们的数据平台架构类型的其他数据湖。有时使用能够良好扩展的工具(例如 Spark)可能更高效。

## 数据平台架构类型

它在多大程度上满足了你的业务需求?选择的困境。

towardsdatascience.com

- 数据转换工具

历史上,最自然的数据转换方式是 SQL。这是一种被所有团队、商业智能(BI)和数据分析师、软件工程师及数据科学家认可的常用数据操作语言。因此,目前有多种工具可以提供可靠的源控数据转换,例如 Jinja、DBT、Dataform、AWS Step Functions 等。

- 商业智能

这些工具帮助提供分析和见解。其中一些是免费的社区工具,如 Looker Studio,其他的是仅限订阅的付费工具。每种工具都有优缺点,选择时可能需要根据公司规模来决定。例如,中小企业通常不需要为 BI OLAP 立方体功能支付额外费用,这些功能用于额外分析和转换数据。他们只需通过电子邮件发送包含主要 KPI 的每日仪表盘。

工具列表庞大,一些出色的 BI 解决方案也提供免费功能。

不是详尽的列表:

  • AWS Quicksight

  • Mode

  • Sisense

  • Looker

  • Metabase

  • PowerBI

问题在于没有完全集成功能的数据工具。

我们如何将数据服务连接在一起?

将我们的数据处理过程整合到一个解决方案中也可以通过一点编程和基础设施即代码技术来实现。

从概念上讲,我们可能希望像这样部署数据管道:

图片由作者提供。

我们如何使这个解决方案既强大又具有成本效益?

毋庸置疑,最具成本效益的方法是使用无服务器架构。例如,考虑下面的数据管道。它可以通过 IAC(AWS Cloudformation 或 Terraform)进行部署,并且所有主要部分都集成在一个完整的数据解决方案中。

我们有 AWS Lambda 函数、AWS Step 函数、一个关系型数据库、数据湖存储桶和一个数据湖房解决方案(AWS Athena)。

示例 DAG。图片由作者提供。

考虑这个由 AWS Lambda 函数组成的数据管道示例。作为一个独立的微服务,它只是一个 lambda 函数,但我们可以用它做更多的事情,例如从 API 提取数据、从关系型数据库导出数据、调用和触发其他服务等。

我们可以通过一个 AWS CLI 命令使用基础设施即代码来部署管道:

部署结果。图片由作者提供。

有了这个,我们应该能够创建一个简单的 Step Function,并可以将其用作模板来扩展和改进我们的 ETL 管道。

如果我们选择平台无关,我们可以使用Terraform来部署完整的管道,并在不同的云平台上使用各种数据服务。我经常使用这种数据管道设计模式与 AWS Lambda 函数结合使用,以在我的 BigQuery 数据仓库解决方案中运行 SQL 查询或执行其他 ETL 任务,非常有用。

结论

将我们的数据处理过程打包成一个解决方案可能会很具挑战性,并且需要一些想象力

基础设施即代码听起来是正确的选择。确实,它简化了数据解决方案的部署和复制,消除了人为错误,并有助于更快地执行数据工程和 MLOps 任务。尽管它可能看起来复杂和精致,但这都需要经验,并且需要时间来学习。不要犹豫,投入一点时间来学习它。这是一项非常有价值的技能。

它可以提供多种数据管道编排选项。

只需一点编码和了解 API 的工作方式,数据管道设计和管理的机会就变得无穷无尽。

推荐阅读

## 使用 AWS Cloudformation 创建 MySQL 和 Postgres 实例

数据库从业者的基础设施即代码

[towardsdatascience.com [## 作为代码的基础设施 - AWS CloudFormation - AWS

AWS CloudFormation 通过 AWS CloudFormation,您可以在全球范围内扩展基础设施,并管理所有 AWS 账户和区域中的资源…

aws.amazon.com [## 无服务器工作流编排 - AWS Step Functions - 亚马逊网络服务

AWS Step Functions 每月支持 4,000 次状态转换 使用代码按需处理数据,进行大规模并行处理…

aws.amazon.com [## HashiCorp 的 Terraform

Terraform 是一款开源的基础设施即代码软件工具,能够帮助您安全、可预测地创建…

www.terraform.io

数据管道与 Airflow 和 AWS 工具(S3、Lambda 和 Glue)

原文:towardsdatascience.com/data-pipeline-with-airflow-and-aws-tools-s3-lambda-glue-18585d269761?source=collection_archive---------1-----------------------#2023-04-06

了解这些工具及其集成方式

João PedroTowards Data Science João Pedro

·

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

--

图片由Nolan Krattinger拍摄,发布在Unsplash

介绍

几周前,当我在思考新的文章创意时,我想:嗯,我需要更多地了解(和讨论)云计算和这些相关内容。我在本地环境中已经练习了很多,使用了开源工具,远离了专有解决方案……但世界是云的,我认为这种情况不会很快改变……

然后我写了一篇关于创建 使用本地 Spark 和 GCP 的数据管道 的文章,这是我第一次使用云基础设施。今天的文章遵循相同的理念:将本地和云端的组件组合在一起,构建数据管道。但这次,我们将使用 AWS,而不是 GCP。

AWS 是迄今为止最受欢迎的云计算平台,它拥有大量的产品来解决你想象中的每种特定问题。至于数据工程解决方案,它也不例外:它们有数据库、ETL 工具、流媒体平台等等 — 一整套工具,让我们的生活更轻松(只要你为它们付费)。

所以,请跟随我在这篇文章中从零开始开发一个完整的数据管道,使用 AWS 工具集中的一些组件。

未赞助。

工具 — TLDR

Lambda functions 是 AWS 最著名的无服务器计算解决方案。“无服务器”意味着应用程序不依附于特定的服务器。相反,每当发出请求时,会快速启动一个新的计算实例,应用程序响应后,该实例将被终止。因此,这些应用程序应该是小型的、无状态的。

Glue 是 AWS 的一种简单的无服务器 ETL 解决方案。使用可视化界面、代码编辑器或 Jupyter notebooks 创建 Python 或 Spark 处理作业。按需运行作业,只为执行时间付费。

S3 是 AWS 的 blob 存储。这个概念很简单:创建一个存储桶并在其中存储文件。稍后通过它们的“路径”读取这些文件。文件夹是虚假的,对象是不可变的。

Airflow 是一个“工作流协调器”。它是一个开发、组织、排序、调度和监控任务的工具,使用一种称为 DAG 的结构 — 有向无环图,用 Python 代码定义。

数据

为了充分探索这些工具的功能,我选择使用来自巴西 ENEM(国家高中考试)的数据。这个考试每年举行,是大多数巴西公立和私立大学的主要入学门槛;它在四个主要知识领域评估学生:人文科学、自然科学、数学和语言(每个领域 45 道题目)。

ENEM 2010,人文科学及其技术。图片来源:作者。

我们的任务是从实际的考试中提取这些问题,这些考试以 PDF 形式在 MEC(教育部)网站上提供 [CC BY-ND 3.0]。

从 PDF 中提取问题。图片来源:作者。

实现过程

在阅读了关于 AWS 可用的数据处理工具的一两行内容后,我决定用 Lambda 和 Glue 作为数据处理组件,S3 作为存储,和本地 Airflow 来协调一切,来构建一个数据管道。

简单的想法,对吧?

嗯,可以说是这样。

正如你在这篇帖子中会注意到的那样,问题在于有很多配置、授权、角色、用户、连接和密钥需要创建,以使这些工具能够顺利协作。

我保证会尽量覆盖大部分步骤,但为了缩短帖子,我需要省略一些细节。

说到这里,让我们看看每个工具的功能,见下图。

提议的管道。图像由作者提供。

本地 Airflow 实例将负责协调所有操作,从 MEC 网站下载 PDF 并将其上传到 S3。此过程应该会自动触发 Lambda 函数执行,该函数将读取 PDF,提取其文本,并将结果保存到 S3 的“另一个地方”。然后,Airflow 应触发一个 Glue 作业,该作业将读取这些文本,提取问题,并将结果以 CSV 格式保存到 S3。

步骤:

  1. (Airflow) 下载 PDF 并上传到 S3

  2. (Lambda) 从 PDF 中提取文本,将结果以 JSON 格式写入 S3

  3. (Airflow->Glue) 读取文本,拆分问题,添加适当的元数据,并将结果保存为 CSV

0. 设置环境

本项目中使用的所有代码都可以在这个 GitHub 仓库中找到。

第一步是配置本地环境。

你需要在本地机器上安装 Docker 来创建 Airflow 集群。Docker 镜像已经配置好,可以自动从头创建一个新环境,因此我们可以更多地关注实现部分。

docker-compose.yaml 文件的相同文件夹中,使用以下命令启动环境:

docker compose up

在初始配置后,airflow Web 服务应该在 localhost:8080 启动。默认的用户名和密码都是 ‘airflow’。

如果在启动 Airflow 时遇到问题,请尝试为新创建的卷赋予读写权限,例如:chmod 777

接下来进入云环境。

你需要一个 AWS 账户,这里有一个警告——注意账单。S3 存储和 Lambda 函数的使用将会在免费配额范围内(如果你还没有用完),但 Glue 执行会收取一些美元美分。记得在工作完成后关闭所有服务。

一旦你创建了账户,请按照以下步骤操作:

  1. 在 S3 中创建一个名为 enem-bucket 的新 Bucket。

  2. 创建一个新的 IAM 用户,授权读取和写入 S3 并运行 Glue 作业,存储生成的访问密钥对。

  3. 在 airflow UI(localhost:8080)中,点击 admin->connections 标签,创建一个新的 AWS 连接,命名为 AWSConnection,使用之前创建的访问密钥对

创建 AWS 连接。图像由作者提供。

可能还需要一些其他小调整,AWS 是一个疯狂的地方,但上面的列表应该涵盖了整体过程。

曾经有一个人吃了一整架飞机。秘密在于这个过程持续了 2 年,他一块一块地吃掉了它。请将这种哲学带到本文中。接下来的部分将详细介绍每个管道的实现,一步一步地构建完整的项目。

1. 使用 Airflow 上传文件到 AWS

首先,在/dags文件夹中创建一个 Python 文件,我将其命名为process_enem_pdf.py。这是 Airflow 默认搜索 dags 定义的文件夹。在脚本中,导入以下依赖项:

# import airflow dependencies
from airflow import DAG
from airflow.models import Variable
from airflow.operators.python_operator import PythonOperator
from airflow.providers.amazon.aws.hooks.s3 import S3Hook
from airflow.providers.amazon.aws.hooks.base_aws import AwsGenericHook

from datetime import datetime
import requests

在实际场景中,网络抓取应用程序会在 MEC 页面上搜索 PDF 的下载链接,但为了简化,我手动收集了这些链接(数量不多)并将它们硬编码在一个字典中。

LINKS_ENEM = {
    "2010_1":'https://download.inep.gov.br/educacao_basica/enem/provas/2010/dia1_caderno1_azul_com_gab.pdf',
    "2010_2":'https://download.inep.gov.br/educacao_basica/enem/provas/2010/dia2_caderno7_azul_com_gab.pdf',
    "2010_3":'https://download.inep.gov.br/educacao_basica/enem/provas/2010/AZUL_quarta-feira_GAB.pdf',
    "2010_4":'https://download.inep.gov.br/educacao_basica/enem/provas/2010/AZUL_quinta-feira_GAB.pdf',

    "2011_1":'https://download.inep.gov.br/educacao_basica/enem/provas/2011/01_AZUL_GAB.pdf',
    "2011_2":'https://download.inep.gov.br/educacao_basica/enem/provas/2011/07_AZUL_GAB.pdf',
    "2011_3":'https://download.inep.gov.br/educacao_basica/enem/ppl/2011/PPL_ENEM_2011_03_BRANCO.pdf',
    # OMITTED TO MAKE THIS CODE BLOCK SMALLER
    # ...
}

规划创建网络抓取器时要始终负责:检查网站的使用条款和托管内容的版权。

为了更好地模拟抓取应用程序的行为,我还在 Airflow UI 中创建了一个“年”变量(admin->variables)。这个变量模拟了抓取脚本应执行的“年份”,从 2010 年开始,并在任务执行结束时自动递增(+1)。这样,每次任务运行将仅处理一年的数据。

变量列表。图像由作者提供。

Airflow 变量和连接在代码中通过其 ID(名称)引用。我通常将它们的名称作为常量:

# Connections & Variables
AWS_CONN_ID = "AWSConnection"
YEAR_VARIABLE = "year"

在 Airflow DAGs 中执行 Python 代码最常见的方式是使用 PythonOperator,它基于 Python 函数创建任务。

因此,下载 PDF 并将其上传到 S3 桶的过程需要封装在一个函数中。见下文。

AWS_CONN_ID = "AWSConnection"
YEAR_VARIABLE = "year"

def download_pdfs_from_year(
        year_variable,
        bucket
    ):

    # Create a S3 connection using the AWS Connection defined in the UI
    conn = S3Hook(aws_conn_id=AWS_CONN_ID)
    client = conn.get_conn()

    year = Variable.get(year_variable)
    year_keys = [key for key in LINKS_ENEM.keys() if year in key]

    for key in year_keys:
        print(f"Downloading {key}")
        url = LINKS_ENEM[key]
        r = requests.get(
            url, 
            allow_redirects=True,
            verify=False
        )

        client.put_object(
            Body=r.content,
            Key=f"pdf_{key}.pdf",
            Bucket=bucket,
        )

    # increase the year
    year = str(int(year)+1)
    Variable.set(year_variable, year)

现在,只需实例化 DAG 对象本身:

# Some airflow boilerplate and blah blah blah
default_args = {
    'owner': 'ENEM_PDF',
    'depends_on_past': False,
    'start_date': datetime(2021, 1, 1),
}

dag = DAG(
    'process_enem_pdf_aws',
    default_args=default_args,
    description='Process ENEM PDFs using AWS',
    tags=['enem'],
    catchup=False,
)

书写任务:

with dag:
    download_pdf_upload_s3 = PythonOperator(
        task_id='download_pdf_upload_s3',
        python_callable=download_pdfs_from_year,
        op_kwargs={
            'year_variable': YEAR_VARIABLE ,
            'bucket': 'enem-bucket',
        },
    )

DAG 将在 Airflow UI 中可见,我们可以激活它并触发执行:

DAG 列表。图像由作者提供。

这是(第一次)关键时刻,触发 dag 并查看 S3 桶。如果一切顺利,PDF 应出现在 S3 桶中。

上传 PDF 的 S3 桶。图像由作者提供。

如果没有(这很可能,因为技术领域的事情往往会出错),开始调试 DAG 日志并搜索配置错误。

DAG 运行中的错误。图像由作者提供。

2. 使用 Lambda Functions 提取 PDF 文本

PDF 文件已经上传到 S3,现在是下一步:提取它们的文本。

这是使用 AWS Lambda Functions 实现的完美任务:一个无状态、小型且快速的过程。

简单回顾一下无服务器技术的工作原理。在常规“服务器”应用程序中,我们购买一个特定的服务器(机器),具有合适的 IP 地址,将我们的应用程序安装在其中,并保持 24/7 运行(或类似的状态)以满足我们的需求。

使用这种方法来处理像这样简单的文本提取预处理任务的问题在于,我们需要从头构建一个完整的健壮服务器,这需要时间,并且从长远来看可能不够成本效益。无服务器技术的到来是为了解决这个问题。

在无服务器环境中,每当发出请求时,都会快速启动一个新的小型服务器实例,应用程序响应后,该实例会被终止。

就像租车vs叫 Uber 去进行一次小的 5 分钟行程。

让我们回到编码。

在你的 AWS 账户中搜索 Lambda,并创建一个新的 lambda 函数,与之前使用的 S3 桶在同一区域,否则它将无法使用触发器与之互动(更多细节稍后说明)。

搜索 AWS Lambda。图片由作者提供。

从头创建一个新函数,将其命名为process-enem-pdf,选择 Python 3.9 运行时,就可以开始了。AWS 可能会指导你创建一个新的 IAM 角色用于 Lambda 函数,确保这个角色在enem-bucket S3 桶中拥有读写权限。

你可能还需要将函数的最大执行时间增加到大约 3 分钟,默认值是 3 秒(或接近的值),这对于我们的目的来说是不够的。

AWS 中的 Python Lambda 函数呈现为一个名为lambda_function.py的简单 Python 文件,其中包含一个lambda_handler(event, context)函数,其中‘event’是一个 JSON 对象,表示触发执行的事件。

你可以直接在 AWS 内置 IDE 中编辑 Python 文件,或使用压缩的 zip 文件上传本地文件。

Lambda 函数代码编辑器中的示例代码。图片由作者提供。

这时事情变得有点棘手。

要从 PDF 中提取文本,我们将使用 PyPDF2 包。然而,在 AWS Lambda 函数环境中安装这个依赖项并不像运行‘pip install’那么简单。

我们需要本地安装这些包,并将代码和依赖项压缩(zip)一起发送。

为此,按照以下步骤操作:

  1. 创建一个 Python 虚拟环境,使用venv: python3 -m venv pdfextractor

  2. 激活环境并安装依赖项。

source pdfextractor/bin/activate
pip3 install pypdf2 typing_extensions

创建一个本地lambda_function.py文件,并包含lambda_handler函数。

import boto3
from PyPDF2 import PdfReader    
import io
import json

def lambda_handler(event, context):
  # The code goes here blah blah blah
  # Detailed latter
  # ...

lambda_function.py复制到pdfextractor/lib/python3/site-packages/路径下。

pdfextractor/lib/python3/site-packages/文件夹的内容压缩成一个.zip 文件。

将这个文件上传到 Lambda 函数 UI 中。

现在你(可能)已经了解了这个过程,我们可以继续开发代码本身。

想法很简单:每当向 S3 桶添加一个新的 PDF 对象时,Lambda 函数应该被触发,提取其文本,并将结果写入 S3。

幸运的是,我们不需要手动编写这个触发规则,因为 AWS 提供了与其基础设施不同部分交互的内置触发器。在process-enem-pdf页面,点击添加触发器

添加触发器。作者提供的图片。

现在,基于 S3 配置一个新规则……

配置触发器。作者提供的图片。

:enem-bucket;事件类型:所有对象创建事件;后缀:.pdf

正确添加后缀是非常重要的。 我们将使用这个功能将新文件写入相同的桶中,如果后缀过滤器配置不正确,可能会导致无限递归循环,从而消耗无限的资金

现在,每当 S3 桶中创建一个新对象时,它将触发一次新的执行。参数event将存储一个 JSON,描述这个新创建的对象,其格式大致如下:

{
  "Records": [
    {
      # blah blah blah blah blah
      "s3": {
        # blah blah blah blah blah
        "bucket": {
          "name": "enem-bucket",
          "ownerIdentity": {
            # blah blah blah
          },
          "arn": "arn:aws:s3:::enem-bucket"
        },
        "object": {
          "key": "pdf_2010_1.pdf",
          "size": 1024,
        }
      }
      # blah blah blah
    }
  ]
}

利用这些信息,函数可以从 S3 读取 PDF,提取其文本,并保存结果。请参见下面的代码。

import boto3
from PyPDF2 import PdfReader
import io
import json

def lambda_handler(event, context):
    object_key = event["Records"][0]["s3"]["object"]["key"]
    bucket = event["Records"][0]["s3"]["bucket"]["name"]

    object_uri = f"s3://{bucket}/{object_key}"

    if not object_uri.endswith(".pdf"):
        # Just to make sure that this function will not
        # cause a recursive loop
        return "Object is not a PDF"

    # Create a S3 client
    # Remember to configure the Lambda role used
    # with read and write permissions to the bucket
    client = boto3.client("s3")

    try:
        pdf_file = client.get_object(Bucket=bucket, Key=object_key)
        pdf_file = io.BytesIO(pdf_file["Body"].read())
    except Exception as e:
        print(e)
        print(f"Error. Lambda was not able to get object from bucket {bucket}")
        raise e

    try:
        pdf = PdfReader(pdf_file)
        text = ""
        for page in pdf.pages:
            text += page.extract_text()

    except Exception as e:
        print(e)
        print(f"Error. Lambda was not able to parse PDF {object_uri}")
        raise e

    try:
        # Save the results as JSON
        text_object = {
            "content": text,
            "original_uri": object_uri
        }

        client.put_object(
            Body=json.dumps(text_object).encode("utf-8"),
            Bucket=bucket,
            Key=f"content/{object_key[:-4]}.json" ,
        )
    except Exception as e:
        print(e)
        print(f"Error. Lambda was not able to put object in bucket {bucket}")
        raise e

创建这个功能并重复之前解释的部署步骤(venv、zip 和上传),一切应该运行良好(可能)。一旦我们的 airflow 管道将新的 PDF 保存到桶中,其文本应该被提取并作为 JSON 保存到/content “文件夹”(记住,文件夹是虚假的)。

提取的文本 JSON。作者提供的图片。

3. 使用 Glue 处理文本

最后,我们完成了管道的最后一部分。文本已经被提取并以大多数数据处理引擎可以轻松处理的格式(JSON)存储。

最后的任务是处理这些文本以孤立出单个问题,这就是 AWS Glue 的作用。

Glue 是一对解决方案:一个数据目录,带有爬虫来查找和编目数据以及映射模式,还有无服务器 ETL 引擎,负责数据处理。

在 AWS 控制台中搜索 Glue 并选择它。

搜索 Glue。作者提供的图片。

在编写作业之前,我们将使用爬虫数据目录中创建一个新的数据集。我知道新概念太多了,但过程很简单。在 Glue 的主页面,转到左侧菜单中的爬虫。

AWS Glue 侧边栏。作者提供的图片。

创建一个新的爬虫,在步骤 1 中给它命名,然后进入步骤 2。在这里,添加一个新的数据源,指向s3://enem-bucket/content,这是我们存储所有文本的“文件夹”。

配置爬虫。作者提供的图片。

进入步骤 3,如果需要,创建一个新的IAM角色。步骤 4 将要求你选择一个数据库,点击添加数据库并创建一个名为enem_pdf_project的新数据库。在步骤 5 中查看信息并保存爬虫。

你将被重定向到爬虫页面。现在进入危险区域(这会花费你几分钱 ;-;),点击运行爬虫,它将开始在指定的源(s3://enem-bucket/content)中映射数据。几秒钟后,处理完成,如果一切顺利,enem_pdf_project 数据库中应该会出现一个名为content的新表。

现在,Glue 作业将能够读取引用目录中此表的 S3 JSON 文件。

我认为这实际上是不必要的,因为你可以直接查询 S3,但这个教训仍然适用。

现在,我们准备好编码我们的作业了。

在 Jobs 任务中,你可以选择多种方式来开发新作业:可视化连接块,使用交互式 pyspark 笔记本会话,或直接在脚本编辑器中编写代码。

Glue 作业界面。图片来源:作者。

我建议你自己探索这些选项(注意笔记本会话,你需要为它们付费)。无论你选择什么,都将创建的作业命名为Spark_EnemExtractQuestionsJSON。我选择使用 Spark,因为我对它更熟悉。见下面的代码。

from awsglue.transforms import *
from pyspark.context import SparkContext
import pyspark.sql.functions as F
from awsglue.context import GlueContext
from awsglue.job import Job

sc = SparkContext.getOrCreate()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)

# Reading the table content from the Data Catalog
dyf = glueContext.create_dynamic_frame.from_catalog(
    database="enem_pdf_project", table_name="content"
)
dyf.printSchema()

# Just pyspark script below
df = dyf.toDF()

# Create a new column with the year
df = df.withColumn(
    "year", F.regexp_extract(F.col("original_uri"), ".+pdf_([0-9]{4})", 1)
)

# Split the text using the 'questão XX' regex
# and explode the resultant list
# resulting in one row per question
df = (
    df.withColumn("text", F.lower(F.col("content")))
    .withColumn(
        "text",
        F.regexp_replace(
            F.col("text"), "(questão [0-9]+)", "<QUESTION_START_MARKER>$1"
        ),
    )
    .withColumn("text", F.split(F.col("text"), "<QUESTION_START_MARKER>"))
    .withColumn("question", F.explode(F.col("text")))
    .withColumn(
        "question_number", F.regexp_extract(F.col("question"), "questão ([0-9]+)", 1)
    )
    .drop("content", "text")
)

# Save the result in CSV to S3
df.write.csv("s3://enem-bucket/processed/", mode="overwrite", header=True)
job.commit()

除了与 AWS 基础设施(读取和写入)交互所需的一些额外代码外,所有处理逻辑都是使用标准 pyspark 操作编写的。如果你对了解更多关于 Spark 的内容感兴趣,可以查看我之前的一篇文章

默认情况下,Glue 作业配置为按需运行,这意味着我们必须手动触发其执行,使用 AWS 界面或通过 API 调用。

因此,我们只需要在 Airflow DAG 中添加一个新任务来触发作业并完成管道。

幸运的是,所需的代码非常简单,所以让我们回到process_enem_pdf.py 文件中创建一个新函数。

def trigger_process_enem_pdf_glue_job(
    job_name
):
    session = AwsGenericHook(aws_conn_id=AWS_CONN_ID)

    # Get a client in the same region as the Glue job
    boto3_session = session.get_session(
        region_name='us-east-1',
    )

    # Trigger the job using its name
    client = boto3_session.client('glue')
    client.start_job_run(
        JobName=job_name,
    )

并将此函数作为任务添加到 DAG 中…

with dag:
    download_pdf_upload_s3 = PythonOperator(
        task_id='download_pdf_upload_s3',
        python_callable=download_pdfs_from_year,
        op_kwargs={
            'year_variable': 'year',
            'bucket': 'enem-bucket',
        },
    )

    trigger_glue_job = PythonOperator(
        task_id='trigger_glue_job',
        python_callable=trigger_process_enem_pdf_glue_job,
        op_kwargs={
            'job_name': 'Spark_EnemExtractQuestionsJSON'
        },
    )

    download_pdf_upload_s3 >> trigger_glue_job

而且,,管道完成了。

DAG 的图形表示。图片来源:作者。

现在,每次运行时,管道应该会下载最新的 PDF 文件,并将它们上传到 S3,这会触发一个 Lambda 函数,该函数提取文本并将其保存到/content 路径。这个路径是由爬虫映射的,并在数据目录中可用。当管道触发 Glue 作业时,它读取这些文本,提取每个问题,并将结果保存为 CSV 文件在/processed 路径中。

‘processed’ 路径在 S3 中。图片来源:作者。

请查看下面的结果…

在 S3 中创建的 CSV 文件。图片来源:作者。

结论

这是一场漫长的冒险。

在这篇文章中,我们从头开始构建了一个完整的数据管道,混合了多种著名的数据工具的强大功能,包括 AWS 云(Lambda、Glue 和 S3)和本地环境(Airflow+Docker)。

我们探讨了 Lambda 和 Glue 在数据处理中的功能,讨论了它们的优点和使用案例。我们还学到了一些关于 Airflow 的知识,Airflow 是数据管道中最著名的编排工具。

这些工具每一个都是一个独立的世界。我试图将项目开发期间学到的所有信息压缩成尽可能小的文章,因此不可避免地,一些信息被遗漏了。如果你遇到问题或有疑问,请在评论中告诉我。

我知道所提议的数据管道可能不是最优的,尤其是在成本效率方面,但我认为这篇文章的主要观点(对我来说,我希望对你也是如此)是学习使用所涉及工具开发数据产品的整体过程。

此外,如今大多数可用数据,特别是在互联网上,都是所谓的非结构化格式,如 PDF、视频、图像等。处理这种数据是一项关键技能,涉及到知道超出常见的 Pandas/Spark/SQL 工具集的更广泛工具。我们今天构建的管道正是解决这一问题,通过将存储在网站上的原始 PDF 转化为存储在我们云基础设施中的半结构化 CSV 文件。

对我来说,这个管道的一个亮点是使用 AWS Lambda 部署的文本提取步骤,因为仅仅依靠 Spark 实施这一任务可能是不可能或非常困难的(据我所知)。

我希望你从这篇文章中得到的主要信息是:构建良好的数据基础设施不仅需要对数据架构、数据建模或流媒体的理论知识,还需要对可以帮助实现你愿景的可用工具有良好的理解。

和往常一样,我并不是所讨论主题的专家,我强烈推荐进一步阅读和讨论(请参见下面的一些参考资料)。

这让我花费了 36 美分 + 税费 ;-;

感谢你的阅读! 😉

参考文献

所有代码均可在这个 GitHub 仓库中找到。

使用的数据 — ENEM PDF,[CC BY-ND 3.0],巴西教育部。

[1] 亚马逊网络服务拉丁美洲. (2021 年 12 月 6 日). 使用 AWS Glue 转化和目录化数据第一部分 — 葡萄牙语 [视频]. YouTube. 链接

[2] Bakshi, U. (2023 年 2 月 9 日). 如何使用 Python AWS Lambda 将文件上传到 S3 — 极客文化 — Medium. Medium. 链接

[3] Cairocoders. (2020 年 3 月 5 日). 如何在 AWS Lambda 函数中导入自定义 Python 包 [视频]. YouTube. 链接

[4] 如何使用 AWS Glue 提取、转换和加载数据以进行分析处理(第二部分) | 亚马逊网络服务。(2022 年 4 月 4 日)。亚马逊网络服务。 Link.

[5] 如何使用 boto3 向 S3 对象写入文件或数据。(无日期)。Stack Overflow。 Link.

[6] 教程:使用 Amazon S3 触发器调用 Lambda 函数 — AWS Lambda。(无日期)。 Link.

[7] Um Inventor Qualquer。(2022 年 1 月 10 日)。在这个免费的实用课程中学习 AWS Lambda!| 课程 17 — #70 [视频]。YouTube。 Link.

[8] Chambers, B., & Zaharia, M.(2018 年)。Spark: 权威指南:大数据处理变得简单。 “O’Reilly Media, Inc.”。

使用 Polars 构建的数据管道:逐步指南

原文:towardsdatascience.com/data-pipelines-with-polars-step-by-step-guide-f5474accacc4

使用 Polars 构建可扩展且快速的数据管道

Antons Tocilins-RubertsTowards Data Science Antons Tocilins-Ruberts

·发表在 Towards Data Science ·阅读时间 14 分钟·2023 年 7 月 24 日

--

图片由 Filippo Vicini 提供,来源于 Unsplash

介绍

本文的目的是解释并展示如何使用 Polars 构建数据管道。它整合并使用了你从本系列前两部分获得的所有知识,因此如果你还没有阅读这些内容,我强烈建议你先去阅读,然后再回来这里。

## 使用 Polars 进行 EDA:Pandas 用户的逐步指南(第一部分)

提升你在 Polars 上的数据分析技能

towardsdatascience.com ## 使用 Polars 进行 EDA:汇总和分析函数的逐步指南(第二部分)

使用 Polars 以闪电般的速度进行高级聚合和滚动平均

towardsdatascience.com

设置

你可以在这个 repository 中找到所有的代码,所以不要忘记克隆/拉取并给它加星。特别是,我们将探索这个 file,这意味着我们最终将从笔记本走向实际应用!

本项目中使用的数据可以从 Kaggle 下载(CC0: Public Domain)。这与前两部分中使用的 YouTube 趋势数据集相同。我假设你已经安装了 Polars,因此只需确保通过 pip install -U polars 更新到最新版本。

数据管道

简单来说,数据管道是一个自动化的步骤序列,它从一个或多个位置提取数据,应用处理步骤,并将处理后的数据保存到其他地方,使其可以用于进一步的使用。

Polars 中的管道

Polars 处理数据的方式非常适合构建可扩展的数据管道。首先,我们可以如此轻松地链式调用方法,这使得一些相当复杂的管道可以优雅地编写。

例如,假设我们想找出 2018 年每个月中哪个趋势视频的观看次数最多。下面你可以看到一个完整的管道,用于计算这个指标并将其保存为 parquet 文件。

import polars as pl
csv_path = "./youtube/GBvideos.csv"

pl.read_csv(csv_path).with_columns(
    # Original date is in string format like 17.01.01
    pl.col("trending_date").str.to_date(format="%y.%d.%m")
).filter(pl.col("trending_date").dt.year() == 2018).with_columns(
    pl.col("views")
    .rank(descending=True)
    # Rank is calculated over a month 
    .over(pl.col("trending_date").dt.month())
    .alias("monthly_rank")
).filter(
    pl.col("monthly_rank") == 1
).select(
    pl.col("trending_date"), pl.col("title"), pl.col("channel_title"), pl.col("views")
).write_parquet(
    "top_monthly_videos.parquet"
)

相当不错,对吧?如果你了解 SQL,这很容易阅读和理解。但我们可以做得更好吗?当然可以,使用 Polars 的 .pipe() 方法。这种方法为我们提供了一种有结构的方式来将顺序函数应用于 DataFrame。为了使其有效,让我们将上述代码重构成函数。

def process_date(df, date_column, format):
    result = df.with_columns(pl.col(date_column).str.to_date(format))
    return result

def filter_year(df, date_column, year):
    result = df.filter(pl.col(date_column).dt.year() == year)
    return result

def get_first_by_month(df, date_column, metric):
    result = df.with_columns(
        pl.col(metric)
        .rank(method="ordinal", descending=True)
        .over(pl.col(date_column).dt.month())
        .alias("rank")
    ).filter(pl.col("rank") == 1)

    return result

def select_data_to_write(df, columns):
    result = df.select([pl.col(c) for c in columns])
    return result

注意这些函数接受一个 Polars DataFrame 作为输入(以及一些其他参数),并输出已经修改的 Polars DataFrame。通过 .pipe() 方法将这些方法链在一起是一件轻而易举的事。

(
    pl.read_csv(csv_path)
    .pipe(process_date, date_column="trending_date", format="%y.%d.%m")
    .pipe(filter_year, date_column="trending_date", year=2018)
    .pipe(get_first_by_month, date_column="trending_date", metric="views")
    .pipe(
        select_data_to_write,
        columns=["trending_date", "title", "channel_title", "views"],
    )
).write_parquet("top_monthly_videos.parquet")

首先,重格式化后的代码更容易理解。其次,关注点分离通常是一个很好的编程原则,因为它可以更容易地调试和保持代码整洁。对于这个简单的示例,将管道模块化可能有些过于复杂,但你会看到它在下一个部分的更大示例中的有用之处。现在,让我们使用 懒模式 来加快整体速度。

懒模式允许我们编写查询和管道,将它们全部组合在一起,然后让后台引擎进行优化。例如,上述代码显然不是最优的。我把列选择作为最后一步,这意味着处理的数据量不必要地大。幸运的是,Polars 足够聪明,能够识别这一点,因此它会优化代码。此外,我们只需在代码中做两个小的更改即可获得速度提升,这点非常不可思议。首先,我们将 pl.read_csv 更改为 pl.scan_csv 以在懒模式下读取数据。然后,在查询的末尾添加 .collect(),以告知 Polars 我们希望执行优化后的查询。

(
    pl.scan_csv(csv_path).pipe(process_date, date_column="trending_date", format="%y.%d.%m")
    .pipe(filter_year, date_column="trending_date", year=2018)
    .pipe(get_first_by_month, date_column="trending_date", metric="views")
    .pipe(
        select_data_to_write,
        columns=["trending_date", "title", "channel_title", "views"],
    )
).collect().write_parquet("top_monthly_videos.parquet")

在我的机器上,我得到了约 3 倍的速度提升,考虑到我们仅做了两个非常简单的编辑,这点非常令人印象深刻。现在你已经掌握了管道和延迟评估的概念,让我们进入一个更复杂的示例。

机器学习特征的数据管道

警告:有很多文本和代码!各部分应该按顺序跟随,因为它们构建了管道。

根据我们手头的数据集(YouTube Trending Videos),我们来构建用于预测视频在流行中持续时间的特征。虽然这听起来很简单,但创建这些特征的过程将会相当复杂。数据集的最终格式应该是每个视频 ID 一行,视频进入流行时可用的特征,以及视频在流行中停留的实际天数(目标)。

模拟最终数据集格式。作者创建。

在我们的预测任务中可能有用的特征包括:

  • 视频特征(例如,类别)

  • 进入流行时的观看次数、点赞数、评论数等

  • 频道在流行中的过去表现(例如,过去 7 天的流行视频数量)

  • 一般流行特征(例如,过去 30 天所有视频的平均流行时间)

下面你可以看到创建该数据集所需的管道的图示表示(确保放大查看)。

数据管道流程。作者创建。

我知道这信息量很大,所以我们一口一口地消化它。下面你可以找到每个管道步骤的描述和代码。此外,这个管道将使用 YAML 配置文件进行参数化,所以你也会找到每个步骤的配置参数。这是如何读取名为 pipe_config.yaml 的 YAML 文件的示例,你可以在仓库中找到。

import yaml

# Read config
with open("pipe_config.yaml", "r") as file:
    pipe_config = yaml.safe_load(file)

因此,对于管道的每一步,你会发现:

  • 步骤描述

  • 相关功能

  • 相关配置参数

  • 运行到此步骤的管道代码

这样,我们将逐步建立完整的管道,你将对发生的事情有深入了解,并学会如何为自己的数据创建类似的内容。

读取数据

这一步的目标不言自明——读取数据集以进行进一步处理。我们有两个输入——一个包含主要数据的 csv 文件和一个包含类别映射数据的 json 文件。此步骤的参数如下:

data_path: "./youtube/GBvideos.csv"
category_map_path: "./youtube/GB_category_id.json"

不需要编写读取 csv 数据的函数(因为在 Polars 中已经存在),所以我们只编写了读取类别映射的函数。

def read_category_mappings(path: str) -> Dict[int, str]:
    with open(path, "r") as f:
        categories = json.load(f)

    id_to_category = {}
    for c in categories["items"]:
        id_to_category[int(c["id"])] = c["snippet"]["title"]

    return id_to_category

使用此函数,读取所需文件的代码非常简单。

# Create mapping
id_to_category = read_category_mappings(pipe_config["category_map_path"])
col_mappings = {"category_id": id_to_category}

# Pipeline
output_data = pl.scan_csv(pipe_config["data_path"]).collect()

现在,让我们进入一个非常不令人兴奋但至关重要的步骤——数据清理。

数据清理

这个数据集已经相当干净,但我们需要对日期和类别列做一些额外的预处理。

  • trending_datepublish_time 需要格式化为 pl.datetime

  • category_id 需要从 ID 映射到实际的类别名称

Polars 需要知道日期将以何种格式提供,因此最好在 pipe_config.yaml 文件中对数据格式进行编码,并在相应的日期列中进行说明,以使其清晰且易于更改。

# Pre-processing config
date_column_format:
  trending_date: "%y.%d.%m"
  publish_time: "%Y-%m-%dT%H:%M:%S%.fZ"

由于我们希望使用 Polars 管道来模块化代码,我们需要创建两个函数——parse_datesmap_dict_columns,它们将执行两个所需的转换。然而,有个问题——将这些操作分成两个步骤会使代码变得更慢,因为 Polars 无法有效地使用并行化。你可以通过计时这两个 Polars 表达式的执行来自己测试一下。

slow = df.with_columns(
    # Process dates
    pl.col("trending_date").str.to_date("%y.%d.%m"),
    pl.col("publish_time").str.to_date("%Y-%m-%dT%H:%M:%S%.fZ"),
).with_columns(
    # Then process category
    pl.col("category_id").map_dict(id_to_category)
)

fast = df.with_columns(
    # Process all together
    pl.col("trending_date").str.to_date("%y.%d.%m"),
    pl.col("publish_time").str.to_date("%Y-%m-%dT%H:%M:%S%.fZ"),
    pl.col("category_id").map_dict(id_to_category)
)

对我来说,第一个表达式慢了 ~2 倍,这非常显著。那么我们该怎么办呢?好吧,这里有个秘密:

我们应该在将表达式传递给 .with_columns 方法之前构建它们。

因此,函数 parse_datesmap_dict_columns 应该返回表达式列表,而不是转换后的数据帧。这些表达式可以在最终清理函数中组合并应用,我们将称之为 clean_data

def parse_dates(date_cols: Dict[str, str]) -> List[pl.Expr]:
    expressions = []
    for date_col, fmt in date_cols.items():
        expressions.append(pl.col(date_col).str.to_date(format=fmt))

    return expressions

def map_dict_columns(
    mapping_cols: Dict[str, Dict[str | int, str | int]]
) -> List[pl.Expr]:
    expressions = []
    for col, mapping in mapping_cols.items():
        expressions.append(pl.col(col).map_dict(mapping))
    return expressions

def clean_data(
    df: pl.DataFrame,
    date_cols_config: Dict[str, str],
    mapping_cols_config: Dict[str, Dict[str | int, str | int]],
) -> pl.DataFrame:
    parse_dates_expressions = parse_dates(date_cols=date_cols_config)
    mapping_expressions = map_dict_columns(mapping_cols_config)

    df = df.with_columns(parse_dates_expressions + mapping_expressions)
    return df

如你所见,我们现在只有一个 .with_columns 操作,这使得代码更优化。请注意,所有函数的参数都作为字典提供。这是因为 YAML 被读取为字典。现在,让我们将清理步骤添加到管道中。

# Create mapping
id_to_category = read_category_mappings(pipe_config["category_map_path"])
col_mappings = {"category_id": id_to_category}

# Read in configs
date_column_format = pipe_config["date_column_format"]

# Pipeline
output_data = pl.scan_csv(pipe_config["data_path"]).pipe(
    clean_data, date_column_format, col_mappings
).collect()

干净、模块化、快速——还有什么不喜欢的?让我们继续下一步。

基础特征工程

这一步在清理的数据上做一些基本的特征工程,即:

  • 计算比率特征——点赞与踩的比率、点赞与观看的比率以及评论与观看的比率

  • 计算发布和趋势之间的天数差异

  • trending_date 列中提取工作日

让我们在配置文件中对这些特征的计算进行参数化。我们要指定一个要创建的特征的名称以及数据集中用于计算的相应列。

# Feature engineering config
ratio_features:
  # feature name
  likes_to_dislikes: 
    # features used in calculation 
    - likes           
    - dislikes
  likes_to_views:
    - likes
    - views
  comments_to_views:
    - comment_count
    - views

difference_features:
  days_to_trending:
    - trending_date
    - publish_time

date_features:
  trending_date:
    - weekday

函数的逻辑仍然是一样的——构建表达式并将其传递给 .with_columns 方法。因此,函数 ratio_featuresdiff_featuresdate_features 都在名为 basic_feature_engineering 的主函数中调用。

def ratio_features(features_config: Dict[str, List[str]]) -> List[pl.Expr]:
    expressions = []
    for name, cols in features_config.items():
        expressions.append((pl.col(cols[0]) / pl.col(cols[1])).alias(name))

    return expressions

def diff_features(features_config: Dict[str, List[str]]) -> List[pl.Expr]:
    expressions = []
    for name, cols in features_config.items():
        expressions.append((pl.col(cols[0]) - pl.col(cols[1])).alias(name))

    return expressions

def date_features(features_config: Dict[str, List[str]]) -> List[pl.Expr]:
    expressions = []
    for col, features in features_config.items():
        if "weekday" in features:
            expressions.append(pl.col(col).dt.weekday().alias(f"{col}_weekday"))
        if "month" in features:
            expressions.append(pl.col(col).dt.month().alias(f"{col}_month"))
        if "year" in features:
            expressions.append(pl.col(col).dt.year().alias(f"{col}_year"))

    return expressions

def basic_feature_engineering(
    data: pl.DataFrame,
    ratios_config: Dict[str, List[str]],
    diffs_config: Dict[str, List[str]],
    dates_config: Dict[str, List[str]],
) -> pl.DataFrame:
    ratio_expressions = ratio_features(ratios_config)
    date_diff_expressions = diff_features(diffs_config)
    date_expressions = date_features(dates_config)

    data = data.with_columns(
        ratio_expressions + date_diff_expressions + date_expressions
    )
    return data

类似于前一步,我们只需将主函数传递给 pipe 并将所有所需的配置作为参数提供。

# Create mapping
id_to_category = read_category_mappings(pipe_config["category_map_path"])
col_mappings = {"category_id": id_to_category}

# Read in configs
date_column_format = pipe_config["date_column_format"]
ratios_config = pipe_config["ratio_features"]
diffs_config = pipe_config["difference_features"]
dates_config = pipe_config["date_features"]

# Pipeline
output_data = (
    pl.scan_csv(pipe_config["data_path"])
    .pipe(clean_data, date_column_format, col_mappings)
    .pipe(basic_feature_engineering, ratios_config, diffs_config, dates_config)
).collect()

很好,我们已经完成了一半的管道!现在,让我们将数据集转换为正确的格式,并最终计算我们的目标——趋势中的天数。

数据转换

提醒一下,原始数据集中每个视频有多个条目,因为它详细记录了每一天的趋势。如果一个视频在趋势中停留了五天,这个视频在数据集中会出现五次。我们的目标是得到一个每个视频只有一个条目的数据集(请参见下图)。

数据转换步骤示例。作者创建。

我们可以通过 .groupby.agg 方法的组合来实现这一点。这里唯一需要配置的参数是过滤那些由于视频花费太长时间才进入流行榜的视频,因为这些视频是前一部分中识别出的离群点。在得到包含 video_ids 和相应目标(流行天数)的表后,我们还需要记得从原始数据集中联接特征,因为这些特征不会在 groupby 操作中传递。因此,我们还需要指定要联接的特征和作为联接键的列。

# Filter videos
max_time_to_trending: 60

# Features to join to the transformed data
base_columns:
  - views
  - likes
  - dislikes
  - comment_count
  - comments_disabled
  - ratings_disabled
  - video_error_or_removed
  - likes_to_dislikes
  - likes_to_views
  - comments_to_views
  - trending_date_weekday
  - channel_title
  - tags
  - description
  - category_id

# Use these columns to join transformed data with original
join_columns:
  - video_id
  - trending_date

为了执行所需的步骤,我们将设计两个函数——join_original_featurescreate_target_df

def join_original_features(
    main: pl.DataFrame,
    original: pl.DataFrame,
    main_join_cols: List[str],
    original_join_cols: List[str],
    other_cols: List[str],
) -> pl.DataFrame:
    original_features = original.select(original_join_cols + other_cols).unique(
        original_join_cols
    )  # unique ensures one row per video + date
    main = main.join(
        original_features,
        left_on=main_join_cols,
        right_on=original_join_cols,
        how="left",
    )

    return main

def create_target_df(
    df: pl.DataFrame,
    time_to_trending_thr: int,
    original_join_cols: List[str],
    other_cols: List[str],
) -> pl.DataFrame:
    # Create a DF with video ID per row and corresponding days to trending and days in trending (target)
    target = (
        df.groupby(["video_id"])
        .agg(
            pl.col("days_to_trending").min().dt.days(),
            pl.col("trending_date").min().dt.date().alias("first_day_in_trending"),
            pl.col("trending_date").max().dt.date().alias("last_day_in_trending"),
            # our TARGET
            (pl.col("trending_date").max() - pl.col("trending_date").min()).dt.days().alias("days_in_trending"),
        )
        .filter(pl.col("days_to_trending") <= time_to_trending_thr)
    )

    # Join features to the aggregates
    target = join_original_features(
        main=target,
        original=df,
        main_join_cols=["video_id", "first_day_in_trending"],
        original_join_cols=original_join_cols,
        other_cols=other_cols,
    )

    return target

注意,在 create_target_df 函数中,groupby 操作以创建目标和 join_original_features 函数都是运行的,因为它们都使用原始数据集作为输入。这意味着即使我们有一个中间输出(target 变量),我们仍然可以在 pipe 方法中运行此函数,而不会出现问题。

# Create mapping
id_to_category = read_category_mappings(pipe_config["category_map_path"])
col_mappings = {"category_id": id_to_category}

# Read in configs
date_column_format = pipe_config["date_column_format"]
ratios_config = pipe_config["ratio_features"]
diffs_config = pipe_config["difference_features"]
dates_config = pipe_config["date_features"]

# Pipeline
output_data = (
    pl.scan_csv(pipe_config["data_path"])
    .pipe(clean_data, date_column_format, col_mappings)
    .pipe(basic_feature_engineering, ratios_config, diffs_config, dates_config)
    .pipe(
        create_target_df,
        time_to_trending_thr=pipe_config["max_time_to_trending"],
        original_join_cols=join_cols,
        other_cols=base_features,
    )
).collect()

对于最后一步,让我们使用动态和滚动聚合生成更高级的特征(在上一篇文章中详细介绍)。

高级聚合

这一步负责生成基于时间的聚合。我们需要提供的唯一配置是聚合的窗口期。

aggregate_windows:
  - 7
  - 30
  - 180

滚动聚合

让我们从滚动特征开始。下方是一个示例,展示了一个 abc 频道在两天窗口期内的两个滞后滚动特征。

滚动特征示例。图片由作者提供。

在 Polars 中,滚动特征非常简单,你只需要 .groupby_rolling() 方法和 .agg() 命名空间中的一些聚合。可能有用的聚合包括:

  • 先前流行视频的数量

  • 先前流行视频的平均流行天数

  • 先前流行视频的最大流行天数

鉴于此,让我们构建一个名为 build_channel_rolling 的函数,该函数可以将所需的周期作为输入,这样我们就可以轻松创建任何我们想要的滚动特征,并输出这些所需的聚合。by 参数应设置为 channel_title,因为我们希望按频道创建聚合,而索引列应为 first_day_in_trending,因为这是我们的主要日期列。这两列也将用于将这些滚动聚合联接到原始数据框中。

def build_channel_rolling(df: pl.DataFrame, date_col: str, period: int) -> pl.DataFrame:
    channel_aggs = (
        df.sort(date_col)
        .groupby_rolling(
            index_column=date_col,
            period=f"{period}d",
            by="channel_title",
            closed="left",  # only left to not include the actual day
        )
        .agg(
            pl.col("video_id").n_unique().alias(f"channel_num_trending_videos_last_{period}_days"),
            pl.col("days_in_trending").max().alias(f"channel_max_days_in_trending_{period}_days"),
            pl.col("days_in_trending").mean().alias(f"channel_avg_days_in_trending_{period}_days"),
        )
        .fill_null(0)
    )

    return channel_aggs

def add_rolling_features(
    df: pl.DataFrame, date_col: str, periods: List[int]
) -> pl.DataFrame:
    for period in periods:
        rolling_features = build_channel_rolling(df, date_col, period)
        df = df.join(rolling_features, on=["channel_title", "first_day_in_trending"])

    return df

add_rolling_features 是一个包装函数,可以传递到我们的管道中。它生成并联接配置中指定的周期的聚合。现在,让我们进入最终的特征生成步骤。

周期聚合

这些聚合类似于滚动聚合,但它们旨在衡量“流行”标签中的一般行为。

周期特征示例。图片由作者提供。

如果滚动聚合旨在捕捉频道的过去行为,这些聚合将捕捉一般趋势。这可能是有用的,因为决定谁进入流行和停留多长时间的算法不断变化。因此,我们想要创建的聚合是:

  • 最近一段时间内流行的视频数量

  • 最近一段时间内的平均流行天数

  • 最近一段时间内的最大流行天数

函数的逻辑是相同的 — 我们将创建一个函数来构建这些聚合,以及一个包装函数来构建并连接所有时期的聚合。请注意,我们不指定by参数,因为我们想要计算每天所有视频的这些特征。还要注意,我们需要在聚合上使用shift,因为我们希望使用最后一段时间的特征,而不是当前的。

def build_period_features(df: pl.DataFrame, date_col: str, period: int) -> pl.DataFrame:
    general_aggs = (
        df.sort(date_col)
        .groupby_dynamic(
            index_column=date_col,
            every="1d",
            period=f"{period}d",
            closed="left",
        )
        .agg(
            pl.col("video_id").n_unique().alias(f"general_num_trending_videos_last_{period}_days"),
            pl.col("days_in_trending").max().alias(f"general_max_days_in_trending_{period}_days"),
            pl.col("days_in_trending").mean().alias(f"general_avg_days_in_trending_{period}_days"),
        )
        .with_columns(
            # shift match values with previous period
            pl.col(f"general_num_trending_videos_last_{period}_days").shift(period),
            pl.col(f"general_max_days_in_trending_{period}_days").shift(period),
            pl.col(f"general_avg_days_in_trending_{period}_days").shift(period),
        )
        .fill_null(0)
    )

    return general_aggs

def add_period_features(
    df: pl.DataFrame, date_col: str, periods: List[int]
) -> pl.DataFrame:
    for period in periods:
        rolling_features = build_period_features(df, date_col, period)
        df = df.join(rolling_features, on=["first_day_in_trending"])

    return df

最后,让我们将这些都整合到我们的管道中吧!

# Create mapping
id_to_category = read_category_mappings(pipe_config["category_map_path"])
col_mappings = {"category_id": id_to_category}

# Read in configs
date_column_format = pipe_config["date_column_format"]
ratios_config = pipe_config["ratio_features"]
diffs_config = pipe_config["difference_features"]
dates_config = pipe_config["date_features"]

output_data = (
        pl.scan_csv(pipe_config["data_path"])
        .pipe(clean_data, date_column_format, col_mappings)
        .pipe(basic_feature_engineering, ratios_config, diffs_config, dates_config)
        .pipe(
            create_target_df,
            time_to_trending_thr=pipe_config["max_time_to_trending"],
            original_join_cols=pipe_config["join_columns"],
            other_cols=pipe_config["base_columns"],
        )
        .pipe(
            add_rolling_features,
            "first_day_in_trending",
            pipe_config["aggregate_windows"],
        )
        .pipe(
            add_period_features,
            "first_day_in_trending",
            pipe_config["aggregate_windows"],
        )
    ).collect()

我希望你和我一样激动,因为我们快到了!最后一步 — 写出数据。

写数据

保存转换后的数据非常简单,因为我们可以在collect()操作之后直接使用.save_parquet()。下面你可以看到文件data_preparation_pipeline.py中包含的完整代码。

 def pipeline():
    """Pipeline that reads, cleans, and transofrms data into
    the format we need for modelling
    """
    # Read and unwrap the config
    with open("pipe_config.yaml", "r") as file:
        pipe_config = yaml.safe_load(file)

    date_column_format = pipe_config["date_column_format"]
    ratios_config = pipe_config["ratio_features"]
    diffs_config = pipe_config["difference_features"]
    dates_config = pipe_config["date_features"]

    id_to_category = read_category_mappings(pipe_config["category_map_path"])
    col_mappings = {"category_id": id_to_category}

    output_data = (
        pl.scan_csv(pipe_config["data_path"])
        .pipe(clean_data, date_column_format, col_mappings)
        .pipe(basic_feature_engineering, ratios_config, diffs_config, dates_config)
        .pipe(
            create_target_df,
            time_to_trending_thr=pipe_config["max_time_to_trending"],
            original_join_cols=pipe_config["join_columns"],
            other_cols=pipe_config["base_columns"],
        )
        .pipe(
            add_rolling_features,
            "first_day_in_trending",
            pipe_config["aggregate_windows"],
        )
        .pipe(
            add_period_features,
            "first_day_in_trending",
            pipe_config["aggregate_windows"],
        )
    ).collect()

    return output_data

if __name__ == "__main__":
    t0 = time.time()
    output = pipeline()
    t1 = time.time()
    print("Pipeline took", t1 - t0, "seconds")
    print("Output shape", output.shape)
    print("Output columns:", output.columns)
    output.write_parquet("./data/modelling_data.parquet")

我们可以像运行其他 Python 文件一样运行这个管道。

python data_preparation_pipeline.py
Pipeline took 0.3374309539794922 seconds
Output shape (3196, 38)
Output columns: [
 'video_id', 'days_to_trending', 'first_day_in_trending',
 'last_day_in_trending', 'days_in_trending', 'views', 'likes', 'dislikes', 
 'comment_count', 'comments_disabled', 'ratings_disabled', 
 'video_error_or_removed', 'likes_to_dislikes', 'likes_to_views',
 'comments_to_views', 'trending_date_weekday', 'channel_title', 
 'tags', 'description', 'category_id', 'channel_num_trending_videos_last_7_days',
 'channel_max_days_in_trending_7_days', 'channel_avg_days_in_trending_7_days',
 'channel_num_trending_videos_last_30_days', 'channel_max_days_in_trending_30_days', 
 'channel_avg_days_in_trending_30_days', 'channel_num_trending_videos_last_180_days',
 'channel_max_days_in_trending_180_days', 'channel_avg_days_in_trending_180_days', 
 'general_num_trending_videos_last_7_days', 'general_max_days_in_trending_7_days', 
 'general_avg_days_in_trending_7_days', 'general_num_trending_videos_last_30_days', 
 'general_max_days_in_trending_30_days', 'general_avg_days_in_trending_30_days',
 'general_num_trending_videos_last_180_days', 'general_max_days_in_trending_180_days',
 'general_avg_days_in_trending_180_days'
]

在我的笔记本电脑上,这些步骤不到半秒钟完成,这很令人印象深刻,考虑到我们链在一起的操作数量和生成的特征数量。最重要的是,管道看起来很干净,非常容易调试,并且可以在很短时间内扩展/更改/裁剪。我们做得很棒!

结论

如果你按照这些步骤走到了这里 — 做得好!以下是你应该在这篇文章中学到的内容的简要总结:

  • 如何将多个操作链在一起形成管道

  • 如何使这个管道高效

  • 如何结构化你的管道项目并使用 YAML 文件对其进行参数化

确保将这些学习应用到你自己的数据中。我建议从小处着手(2-3 步),然后随着需求的增长扩展管道。确保保持模块化,惰性,并将尽可能多的操作组合到.with_columns()中,以确保适当的并行处理。

还不是 Medium 会员?

[## 使用我的推荐链接加入 Medium — Antons Tocilins-Ruberts

阅读 Antons Tocilins-Ruberts 的每个故事(以及 Medium 上成千上万其他作家的故事)。你的会员费直接…

medium.com](https://medium.com/@antonsruberts/membership?source=post_page-----f5474accacc4--------------------------------)

数据平台架构类型

原文:towardsdatascience.com/data-platform-architecture-types-f255ac6e0b7

它在多大程度上满足你的业务需求?选择的困境。

💡Mike ShakhomirovTowards Data Science 💡Mike Shakhomirov

·发表于 Towards Data Science ·9 分钟阅读·2023 年 2 月 20 日

--

图片来源:Brooke LarkUnsplash

在当前市场上,数据工具琳琅满目,很容易迷失。互联网充斥着关于使用哪些数据工具和如何使我们的数据堆栈 在今年特别现代化的意见故事(通常是推测性的)。哪些数据工具是最好的?谁是领导者?如何选择合适的工具? 这个故事是为那些在“领域”中并且正在建立世界上最佳数据平台的人准备的。

那么,“现代数据堆栈”是什么,它有多现代呢?

简而言之,它是一个工具集合,用于处理数据。根据我们打算如何处理数据,这些工具可能包括以下内容:

  • 管理的 ETL/ELT 数据管道服务

  • 基于云的管理数据仓库/数据湖,作为数据的目的地

  • 数据转换工具

  • 商业智能或数据可视化平台

  • 机器学习和数据科学能力

有时,现代化的程度并不重要。

确实,如果我们的 BI 工具非常现代,具备定制的 OLAP 数据建模和 git 集成,但无法将报告渲染到电子邮件中,这也是不重要的。

这些小细节往往至关重要。业务需求和数据管道要求是最重要的。

在下图中,我们可以看到数据流转过程以及在数据管道的每个步骤中可以使用的一些相关工具。

数据流动和工具。图片来源:作者

Redshift、Postgres、Google BigQuery、Snowflake、Databricks、Hadoop、Dataproc、Spark,还是 Elastic Map Reduce?

为你的数据平台选择哪个产品?

这取决于你计划用数据执行的日常 任务数据处理和数据存储架构,哪个最适合这些任务。

数据平台架构类型

我记得几年前,互联网充斥着“Hasdoop 已死”这类的故事。数据仓库架构的趋势明显发生了变化。到 2023 年,每个人似乎都对实时数据流和可扩展性着迷,暗示 Spark 和 Kafka 很快会成为公共基准的领导者。

那么哪一种是最好的?谁是领导者,选择哪些数据工具?如何选择?

我理解的是这些基准判断非常主观,应当以一种保留的态度来看待。真正重要的是这些工具与我们的业务需求对齐的程度,以便我们构建一个数据平台。

数据仓库

一种无服务器、分布式的 SQL 引擎(BigQuery、Snowflake、Redshift、Microsoft Azure Synapse、Teradata)。这是一个以 SQL 为优先数据架构,你的数据存储在一个数据仓库中,你可以充分利用去规范化星型模式数据集的所有优势。当然,我们能做到这一点,因为大多数现代数据仓库都是分布式的,扩展性良好,这意味着你不需要担心表的键和索引。它非常适合进行与大数据相关的临时分析。

大多数现代数据仓库解决方案可以处理结构化非结构化数据,如果你的用户主要是数据分析师且具备良好的 SQL 技能,这些解决方案确实非常方便。现代数据仓库能够轻松与业务智能解决方案如Looker、Tableau、Sisense 或 Mode集成,这些解决方案也大量依赖于ANSI-SQL。它不设计用于存储图像、视频或文档。然而,使用 SQL 你几乎可以做任何事情,甚至在一些供应商解决方案中训练机器学习模型。

## 针对初学者的高级 SQL 技术

在 1 到 10 的范围内,你的数据仓库技能有多好?

towardsdatascience.com

数据湖(Databricks、Dataproc、EMR)

一种架构类型是你的数据存储在云存储中,即 AWS S3、Google Cloud Storage、ABS。当然,也自然可以用于图像、视频或文档以及任何其他文件类型(JSON、CSV、PARQUET、AVRO 等),但要分析这些数据,你的用户需要编写一些代码

最常用的编程语言Python,有许多可用的库。JAVA、Scala 或 PySpark 也是此任务的另一种流行选择。

令人惊叹的好处伴随代码而来。

这是数据处理的最高灵活性水平。我们的用户只需要知道如何做到这一点。

湖屋

这是数据仓库和数据湖架构的结合,兼具两者的优点,服务于程序员和普通业务用户,如数据分析师。它使业务能够运行交互式 SQL 查询,同时在定制方面保持很大的灵活性。现代数据仓库解决方案大多能够对存储在数据湖中的数据运行交互式查询,即外部表。例如,一个数据管道可能如下所示:

湖屋管道示例。图像来源于作者

[## 用这个机器学习管道提升你的数据工程技能

数据建模、Python、DAGs、大数据文件格式、成本……它涵盖了所有内容

pub.towardsai.net](https://pub.towardsai.net/supercharge-your-data-engineering-skills-with-this-machine-learning-pipeline-b69d159780b7?source=post_page-----f255ac6e0b7--------------------------------)

数据网格

数据网格架构是一种去中心化的方法,使公司能够自主管理数据、进行跨团队/跨领域的数据分析并共享数据。

每个业务单元可能具有不同的编程技能组合,即SQL 或 Python,以及各种数据工作负载需求(灵活的数据处理与交互式 SQL 查询)。尽管如此,每个业务单元可以自由选择自己的数据仓库/数据湖解决方案,但仍能够与其他单元共享数据而无需数据移动。

## 数据管道设计模式

选择合适的架构及其示例

towardsdatascience.com

关系型和非关系型数据库管理系统

关系型数据库管理系统(RDS)将数据存储在一个以行作为单位的表中,列连接相关的数据元素。它旨在记录和优化以快速获取当前数据。流行的关系型数据库有PostgreSQL, MySQL, Microsoft SQL Server 和 OracleNoSQL 数据库不仅支持简单事务,而关系型数据库还支持复杂的事务和联接。NoSQL 数据库用于处理高速流入的数据。流行的 NoSQL 数据库有:

  • 文档数据库:MongoDB 和 CouchDB

  • 键值数据库:Redis 和 DynamoDB

数据仓库具有类似的列式结构,与 RDS 一样,它是关系型的。数据也被组织成表格、行和列。然而,它与数据库的主要不同在于数据组织和存储的方式,数据库数据按行存储,而数据仓库数据按列存储,以便进行在线分析处理(OLAP),而数据库则使用在线事务处理(OLTP)。例如,AWS Redshift支持数据仓库和数据湖方法,使其能够访问和分析大量数据。

数据仓库设计用于数据分析,包括大量历史数据。使用数据仓库要求用户提前创建预定义的固定模式,这大大有助于数据分析。表格必须简单(去规范化),以便计算大量数据。

RDS 数据库表和连接较为复杂,因为它们是标准化的。因此,传统数据库与数据仓库之间的主要区别在于,传统数据库是为记录数据而设计和优化的,而数据仓库是为响应分析而设计和优化的。当你运行一个应用程序并需要快速获取当前数据时,你会使用数据库。RDS 存储了运行应用程序所需的当前数据。

你需要决定哪一个适合你。

商业智能堆栈

现代数据堆栈应包括帮助数据建模和可视化的 BI 工具。以下是一些高层次的概述。当然这不是一个详尽的列表,但这些是 2023 年市场上最受欢迎的 BI 工具:

Looker Data Studio(Google Looker Studio)

主要特点:

  • 以前称为 Google Data Studio 的免费版本。这是一个出色的免费 BI 工具,具有社区支持。

  • 丰富的小部件和图表

  • 丰富的社区数据连接器

  • 免费的电子邮件调度和投递。完美地将报告渲染到电子邮件中。

  • 免费的数据治理功能

  • 由于这是一个免费的社区工具,它的 API 还稍显不完善

Looker(付费版)

主要特点:

  • 强大的数据建模功能和自助服务能力。适合中型和大型公司。

  • API 功能

Tableau

主要特点:

  • 出色的可视化效果

  • 合理的定价

  • 专利 VizQL 引擎驱动其直观的分析体验

  • 与许多数据源的连接,如 HADOOP、SAP 和 DB 技术,提高数据分析质量。

  • 与 Slack、Salesforce 及其他众多工具的集成。

AWS Quicksight

主要特点:

  • 定制品牌的电子邮件报告

  • 无服务器且易于管理

  • 强大的 API

  • 无服务器自动扩展

  • 按需付费定价

Power BI

主要特点:

  • Excel 集成

  • 强大的数据摄取和连接能力

  • 从 Excel 数据中轻松创建共享仪表板

  • 丰富的视觉效果和图形

Sisense(前身为 Periscope)

Sisense 是一个端到端的数据分析平台,通过可嵌入的、可扩展的架构,使客户和员工都可以进行数据发现和分析。

关键功能:

  • 提供几乎每个主要服务和数据源的数据连接器

  • 为非技术用户提供无代码体验,尽管平台也支持 Python、R 和 SQL。

  • Git 集成和自定义数据集

  • 可能会有点贵,因为它基于按许可证按用户计费的模式

  • 一些功能仍在建设中,即报告电子邮件发送和报告渲染

ThoughtSpot

关键功能:

  • 查询的自然语言

Mode

关键功能:

  • 仪表板的 CSS 设计

  • 协作功能允许在承诺高级计划之前进行快速原型制作

  • 笔记本支持

  • Git 支持

Metabase

关键功能:

  • 适合初学者,非常灵活

  • 有 docker 镜像,因此我们可以立即运行它

  • 自助分析

Redash

关键功能:

  • API

  • 用自然语法编写查询并探索模式

  • 使用查询结果作为数据源来连接不同的数据库

这些工具中的一些有免费版本。例如,Looker Data Studio 提供了基本的仪表板功能,如电子邮件,即拖放小部件构建器,以及良好的图表选择。其他工具则提供付费功能,即数据建模、警报、笔记本和 git 集成。

这些都是很棒的工具,各有利弊。有些工具更用户友好,有些则提供更强大的 API、CI/CD 功能和 git 集成。对于一些工具,这些功能仅在付费版本中提供。

结论

现代数据驱动的应用程序将需要一个数据库来存储当前的应用程序数据。所以如果你有一个应用程序要运行,请考虑 OLTP 和 RDS 架构。

数据湖、数据仓库、湖屋和数据库各有其优点,并且各司其职。

希望对历史数据执行复杂 SQL 查询的大数据分析公司可能会选择用数据仓库(或湖屋)来补充其数据库。这使得数据堆栈更灵活、更现代。

一般来说,答案总是一样的:

选择最便宜的或与您的开发堆栈兼容性最好的工具

试试看,你会发现关系型数据库可以很容易地集成到数据平台中。无论是数据湖还是数据仓库,各种数据连接器将使数据提取变得简单流畅。

但是,有几个问题需要考虑。

这里的关键是尝试数据工具,看看它们能多好地与我们的业务需求对接。

例如,一些 BI 工具只提供按用户付费的定价方式,如果我们需要与外部用户共享仪表板,这将不太适合。

如果有任何节省成本的好处,最好将数据工具与开发堆栈所在的同一云供应商保持一致。

我们可能需要检查工具之间是否存在功能重叠,例如,当我们已经在数据仓库中进行数据建模时,我们是否真的需要一个在其自身的 OLAP 立方体中进行数据建模的 BI 解决方案?

数据建模很重要

确实,它定义了我们处理数据的频率,这将不可避免地反映在处理成本上。

向数据湖或数据仓库的转变主要取决于用户的技能水平。数据仓库解决方案将实现更多的互动,并将我们的选择范围缩小到以 SQL 为首的产品(如 Snowflake、BigQuery 等)。

数据湖适用于具有编程技能的用户,我们会选择以 Python 为首的产品,如 Databricks、Galaxy、Dataproc、EMR。

推荐阅读

  1. www.mckinsey.com/capabilities/mckinsey-digital/our-insights/how-to-build-a-data-architecture-to-drive-innovation-today-and-tomorrow

  2. aws.amazon.com/emr/

  3. cloud.google.com/learn/what-is-a-data-lake

  4. medium.com/towards-data-science/data-pipeline-design-patterns-100afa4b93e3

  5. www.snowflake.com/trending/data-architecture-principles

机器翻译的数据预处理

原文:towardsdatascience.com/data-preprocessing-for-machine-translation-fcbedef0e26a?source=collection_archive---------5-----------------------#2023-02-25

清洗、归一化和分词

本杰明·玛丽Towards Data Science 本杰明·玛丽

·

关注 发表在 Towards Data Science · 14 min read · 2023 年 2 月 25 日

--

图片来自 Pixabay

数据预处理是任何机器学习任务中的关键步骤。数据必须是正确的、清洁的,并且符合预期的格式。

在这篇博客文章中,我解释了预处理用于训练、验证和评估机器翻译系统的数据所需的所有步骤。

我通过示例和代码片段解释每一步预处理步骤,以便你可以自行重现。

在本文的预处理示例中,我使用了西班牙语-英语(Es→En)ParaCrawl v9语料库的前 100,000 个段落(CC0)。我直接提供了这个数据集(大小:9Mb)。

如果你想自己制作这个语料库,请按照这些步骤操作(耐心点,原始数据集已压缩,但仍重达 24Gb):

#Download the corpus
wget https://opus.nlpl.eu/download.php?f=ParaCrawl/v9/moses/en-es.txt.zip
#Uncompress it
unzip en-es.txt.zip
#Keep only the first 100,000 lines
head -n 100000 ParaCrawlV9.es.txt > train.es
head -n 100000 ParaCrawlV9.en.txt > train.en
#Discard the original files
rm en-es.txt.zip

在我之前的文章中,我介绍了用于训练、验证和评估的机器翻译数据集的所有主要特征:

## 训练、验证和评估机器翻译的数据集

选择、检查和拆分

[towardsdatascience.com

数据格式:TXT、TSV 和 TMX

在寻找机器翻译数据集时,你通常会发现它们以不同的格式出现,这些格式试图最好地处理其多语言性质。

无论原始格式是什么,大多数用于训练机器翻译系统的框架只接受原始文本格式的数据。

因此,如果数据集不是文本文件,你可能需要对其进行转换。

你可能会发现的最常见格式有:

  • 平行文本(.txt):这是理想的格式。我们不需要进行任何转换。源语言段落在一个文本文件中,目标语言段落在另一个文本文件中。大多数接下来的预处理步骤将并行应用于这两个文件。在介绍部分,我们下载了这种格式的 ParaCrawl 数据。

  • 制表符分隔值(.tsv):这是一个单文件,每对源语言和目标语言段落在同一行中由制表符分隔。用“cut”命令将其转换为文本文件是直接的:

#The source segments
cut -f1 data.tsv > train.es
#The target segments
cut -f2 data.tsv > train.en
  • 翻译记忆交换(.tmx):这是一种 XML 格式,专业翻译人员经常使用。这是一种非常详细的格式。这就是为什么它很少用于大型语料库。处理 TMX 稍微困难一些。我们可以先去除 XML 标签。为此,我使用了来自 Moses 项目的脚本(LGPL 许可):
strip-xml.perl < data.tmx > data.txt
#Then we have to remove the empty lines:
sed -i '/^$/d' data.txt
#Finally, we have to separate source and target segments into two files. We can use "sed" to do this:
sed -n 'n;p' data.txt > train.en
sed -n 'p;n' data.txt > train.es

不要修改评估数据集的目标端

在深入之前,预处理机器翻译数据集时有一个非常重要的规则:

绝不要对评估数据的目标端进行预处理!

这些被称为“参考翻译”。由于它们是“参考”,我们不应对它们进行修改。

这样做有几个原因。其中最主要的原因是评估数据的目标端应该与您希望系统生成的数据相似。

例如,在某些预处理步骤中,我们将移除空行并对段落进行分词。

你可能希望系统返回空行,例如在翻译空文本时,当然你不希望返回标记化文本作为机器翻译系统的最终输出。

如果你从参考中删除空行,你将无法直接评估系统在需要时生成空行的能力。而如果你对参考进行标记化,你只能知道系统生成标记化文本的效果。如我们将看到的,标记化文本不是你希望系统生成的内容。

此外,参考翻译用于计算自动度量分数以评估机器翻译质量。如果我们修改这些翻译,就会修改分数。这样,分数将不再与其他已发布的参考翻译分数可比,因为我们修改了这些参考。

因此,保留原始参考翻译对于确保可重复性和可比性至关重要。

如果在预处理的某个点,评估数据的目标端与原始数据不同,说明出现了问题。

步骤 1:清理和过滤

步骤适用于:训练和验证数据的源端和目标端。

出于各种原因,公开的平行数据可能需要一些清理。

如果数据是从网络上抓取的文本自动创建的,这一点尤其正确。

清理通常意味着从平行数据中删除以下段落(或句子):

  • 空白或大多包含不可打印字符

  • 带有无效 UTF8,即未正确编码的。

  • 包含极长的标记(或“单词”),因为它们通常无法翻译,例如 DNA 序列、数字序列、无意义的内容等。

  • 有时,重复,即如果一个段或一对段在平行数据中出现多于一次,我们只保留一个实例。

这不是必需的,但我通常会删除训练平行数据中的段对重复项,原因有多个:

  • 它们在训练中很少有用。

  • 它们在训练数据中对某个特定翻译赋予更多权重,且没有充分理由。

  • 它们通常是获取数据过程中出现的缺陷产品(例如爬取),换句话说,这些重复本不应该存在。

所有这些过滤规则的应用是为了只保留对训练神经模型有用的内容。它也删除了可能在后续预处理步骤中引发错误的段落。

它们也略微减少了平行数据的大小。

请记住,我们在清理/过滤平行数据。每条过滤规则应同时应用于数据的两侧。例如,如果源段为空且应删除,则目标段也应删除,以保持数据的平行性。

除了我上面提到的规则外,我们还应该过滤掉包含以下内容的片段对:

  • 非常长的片段

  • 非常短的片段

  • 高繁殖率,即当一个片段比其对应的片段出现得不成比例地更长或更短时

这些规则继承自统计机器翻译时代,在那个时代,这些片段显著增加了计算成本,而对于训练翻译模型没有用处。

由于今天使用的神经算法进行训练,这些规则已经不再必要。然而,这些片段在训练翻译模型时仍大多无用,因此可以安全地从训练数据中移除,以进一步减少其大小。

有许多公共工具可以执行此清理操作。

预处理LGPL 许可)是一个高效的框架,可以执行许多过滤操作。它被机器翻译研讨会用于准备主要国际机器翻译竞赛的数据。

我通常会用自制脚本和额外的框架来补充,比如Moses 脚本LGPL 许可)。

在接下来的段落中,我将逐步描述我通常应用于原始平行数据的整个清理和过滤过程。

实践

我们希望清理我们的 Paracrawl 西班牙语-英语平行数据(见本文介绍部分)。

在内存方面,最昂贵的步骤之一是删除重复项(即“去重”)。在去重之前,我们应该尽可能地移除更多的片段对。

我们可以通过应用clean-n-corpus.perl来开始(这个脚本不需要安装 Moses),如下所示:注意:此步骤假设源语言和目标语言中都存在空格。如果其中一种语言(大多)不使用空格,如日语或中文,你必须首先对源文本和目标文本文件进行分词。如果适用于你的用例,直接跳到“步骤 2 和 3”,然后在完成后再回来这里。

要了解如何使用 clean-n-corpus.perl,调用脚本而不带任何参数。它应返回:

syntax: clean-corpus-n.perl [-ratio n] corpus l1 l2 clean-corpus min max [lines retained file]

参数如下:

  • 比例:这是繁殖率。默认情况下,它设置为 9。我们通常不需要修改它,因此不使用此参数。

  • 语料库:这是清理数据集的路径,不包含扩展名。脚本假设你将源文件和目标文件命名为相同,使用语言 ISO 代码作为扩展名,例如,在我们的例子中是 train.es 和 train.en。如果你采用了我为 ParaCrawl 使用的相同文件名约定,你只需在那里输入:“train”(假设你在包含数据的目录中)。

  • l1: 其中一个平行文件的扩展名,例如“es”。

  • l2: 其他平行文件的扩展名,例如“en”。

  • clean-corpus: 清理后的文件名。例如,如果输入“train.clean”,脚本将把过滤后的平行数据保存到“train.clean.es”和“train.clean.en”。

  • min: 应丢弃的段落的最小标记数。

  • max: 应丢弃的段落的最大标记数。

  • max-word-length(此处未显示):一个标记中的最大字符数。如果一个段落对包含的标记长度超过 max-word-length,则会被移除。

要清理我们的 ParaCrawl 语料库,运行的完整命令是:

clean-corpus-n.perl -max-word-length 50 train es en train.clean 0 150

这个命令从 train.es 和 train.en 中移除带有以下条件的段落:

  • 空段落

  • 超过 150 个单词(或标记)的段落

  • 高度的繁殖力

  • 单词(或标记)包含超过 50 个字符的段落

并将结果保存到“train.clean.es”和“train.clean.en”。

脚本会显示移除的段落数量。如果你做的和我一样,数据中应该剩下 99976 个段落:

clean-corpus.perl: processing train.es & .en to train.clean, cutoff 0–150, ratio 9
……….(100000)
Input sentences: 100000 Output sentences: 99976

注意,每次移除一个段落时,其平行段落也会被移除。train.clean.es 和 train.clean.en 应该有相同数量的行。你可以用以下命令检查:

wc -l train.clean.es train.clean.en

接下来,我们用 preprocess 移除段落:

我们需要先编译它(需要 cmake):

git clone https://github.com/kpu/preprocess.git
cd preprocess
mkdir build
cd build
cmake ..
make -j4

然后,我们可以使用 preprocess 来移除包含以下内容的行:

  • 无效的 UTF-8

  • 控制字符(除了制表符和换行符)

  • 过多的常见和继承的 Unicode 脚本字符(如数字)

  • 标点符号过多或过少

  • 期望脚本中的内容过少(例如移除英文数据中的中文句子)

我们使用“simple_cleaning”二进制文件来处理平行数据:

preprocess/build/bin/simple_cleaning -p train.clean.es train.clean.en train.clean.pp.es train.clean.pp.en

过滤后的数据保存在两个新文件中,我将其命名为“train.clean.pp.es”和“train.clean.pp.en”。

并且应该打印:

Kept 85127 / 99976 = 0.851474

最后,我们可以用“dedupe”来去除重复项:

preprocess/build/bin/dedupe -p train.clean.pp.es train.clean.pp.en train.clean.pp.dedup.es train.clean.pp.dedup.en

并且应该打印:

Kept 84838 / 85127 = 0.996605

我们已经完成了数据清理。

我们几乎移除了 15%的段落。这意味着神经机器翻译的每个训练周期将快 15%(大约)。

第 2 步:标准化

适用步骤:所有数据集的源侧,可能还包括训练和验证数据集的目标侧。

标准化的目标是确保在所有数据集中使用相同的符号,如标点符号、数字和空格,并且具有相同的 UTF8 编码。

实际上,这一步也可以减少词汇量(不同标记类型的数量),通过将具有类似作用或意义的符号映射到相同的符号。

例如,这一步可以将这些不同的引号标准化为相同的引号,如下所示:

  • « → “

  • → “

此步骤还可以确保您的系统不会生成具有不同标点风格的翻译,如果你将其应用于训练数据的目标侧。

当然,如果我们还规范化训练数据的目标侧,我们必须确保映射到所需的字符。

例如,如果你偏好使用“《”,因为你的目标语言使用这种类型的引号标记,那么你应该做不同的映射,如下所示:

  • « →

  • “ →

由于在准备机器翻译数据时通常会执行此步骤,因此有多种工具可以完成它。

我使用了sacremoses(MIT 许可)。它是Moses 项目normalize-punctuation.perl的 Python 实现。

实践

sacremoses 规范化工具 映射了来自多种语言的几十种符号。这些规则可以轻松编辑,以更好地符合你的期望。

sacremoses 可以通过 pip 安装(需要 Python 3):

pip install sacremoses

然后你可以使用 CLI 来规范化数据:

sacremoses normalize < train.clean.pp.dedup.es > train.clean.pp.dedup.norm.es
sacremoses normalize < train.clean.pp.dedup.en > train.clean.pp.dedup.norm.en

差异示例(使用命令“diff”获得):注意:我选择截图而不是复制粘贴这些句子,因为博客编辑器会自动应用其自身的规范化规则。

ParaCrawl V9 (CC0) 的句子在规范化前后的对比。截图由作者提供。

你可以为此命令传递多个选项,例如,如果你想规范化数字,可以添加选项“-n”。要查看所有选项,请运行:

sacremoses normalize –help

第三步:分词

适用步骤:所有数据集的源侧,以及训练和验证数据集的目标侧。

传统上,机器翻译的数据集使用基于规则的分词器进行分词。它们通常仅使用空格来分隔词元,并附加规则来处理特殊情况。

让我们以以下英文句子为例:

However, if you deactivate the cookies, you may not be able to access the full range of functions on this website.
Thirty-four retreatants gathered for a Memorial Day weekend of inspiring teachings by Ven.
Facebook: Brisbane City Council - Personal Safety
Tráiler de "The Imaginarium of Doctor Parnassus"
Smoke safety pressure gauge - UNICAL: 04953D
You can also book a holiday rental directly with the property owner or manager.

使用 sacremoses 分词器进行分词后,我们得到:

However , if you deactivate the cookies , you may not be able to access the full range of functions on this website .
Thirty-four retreatants gathered for a Memorial Day weekend of inspiring teachings by Ven .
Facebook : Brisbane City Council - Personal Safety
Tráiler de &quot; The Imaginarium of Doctor Parnassus &quot;
Smoke safety pressure gauge - UNICAL : 04953D
You can also book a holiday rental directly with the property owner or manager .

分词前后的差异很难察觉。如果你看不到它们,可以注意标点符号附近的空格。

出于几个原因,这些基于规则的分词器对神经机器翻译并不实用。例如,它们生成了过多的稀有词元,这些词元无法被神经模型正确建模。

数据必须“子分词”。例如,传统分词器生成的词元被拆分为更小的词元。这就是字节对编码方法 (BPE)的作用。

更简单的是,SentencePiece 方法 甚至不需要传统的分词。因此,我们少了一个工具(传统分词器),从而减少了预处理中的潜在错误/问题来源。

SentencePiece 可以直接应用于任何字符序列。这对于像日语、中文和泰语这样空格稀少的语言尤其实用。

实际上,SentencePiece 目前是大语言模型(如T5FLAN 系列)中最常用的标记化算法之一。

让我们通过使用相同的英文句子来看一下它是如何工作的。我们将获得:

▁However , ▁if ▁you ▁de ac tiva te ▁the ▁cookies , ▁you ▁may ▁not ▁be ▁able ▁to ▁access ▁the ▁full ▁range ▁of ▁function s ▁on ▁this ▁website .
▁Th ir ty - fo ur ▁re tre at ants ▁gather ed ▁for ▁a ▁Me mor ial ▁Day ▁weekend ▁of ▁in spi ring ▁te ach ing s ▁by ▁Ven .
▁Facebook : ▁B ris ban e ▁City ▁Council ▁- ▁Personal ▁Safety
▁T rá il er ▁de ▁" The ▁I ma gin ar ium ▁of ▁Do ctor ▁Par nas s us "
▁S mo ke ▁safety ▁pressure ▁ga u ge ▁- ▁UN ICA L : ▁ 04 95 3 D
▁You ▁can ▁also ▁book ▁a ▁holiday ▁rental ▁directly ▁with ▁the ▁property ▁owner ▁or ▁manage r .

这些句子对人类来说更难阅读,标记化也不直观。然而,对于神经模型,这种方法效果很好。

为了获得这个结果,你首先需要训练一个 SentencePiece 模型。然后使用该模型对数据进行标记化,以及对所有将发送到我们的机器翻译系统的新输入进行标记化。

实践

要训练这个模型,我们首先需要通过以下方式安装SentencePiece

pip install sentencepiece

然后,将并行数据的源侧和目标侧合并到一个单独的文本文件中。这将允许我们训练一个双语标记化模型,而不是为源语言和目标语言训练不同的模型。

cat train.clean.pp.dedup.norm.es train.clean.pp.dedup.norm.en > train.clean.pp.dedup.norm.es-en

在进入训练之前,我们必须决定词汇表的大小。

这个选择是一个困难但重要的决策。我们通常使用一个经验法则:8,000 到 16,000 之间的值适用于大多数用例。如果你有非常大的并行数据,可以选择更高的值;如果你的训练数据较小,例如少于 100,000 个片段对,则可以选择较低的值。

原因是如果你设置的词汇表大小过高,你的词汇表将包含更稀有的标记。如果你的训练并行数据中没有足够这些标记的实例,它们的嵌入会被估计得很差。

相反,如果你将值设置得过低,神经模型将不得不处理更小的标记,这些标记需要在其嵌入中携带更多信息。在这种情况下,模型可能难以生成良好的翻译。

由于我们的并行数据量较小,我随意选择了 8,000 作为词汇表的大小。

要开始训练:

spm_train -input=train.clean.pp.dedup.norm.es-en -model_prefix=es-en.8kspm -vocab_size=8000

这应该很快(少于 2 分钟)。

参数如下:

  • input:用于训练 SentencePiece 模型的数据。

  • model_prefix:SentencePiece 模型的名称。

  • vocab_size:词汇表的大小。

然后,你必须将模型应用于所有数据,除了测试集的目标侧(记住:我们从不接触这一部分)。

对于我们的 ParaCrawl 语料库,我们做:

spm_encode -model=es-en.8kspm.model < train.clean.pp.dedup.norm.es > train.clean.pp.dedup.norm.spm8k.es
spm_encode -model=es-en.8kspm.model < train.clean.pp.dedup.norm.en > train.clean.pp.dedup.norm.spm8k.en

就这样!我们的数据集都已预处理完毕。我们现在可以开始训练机器翻译系统了。

可选步骤:truecasing 和打乱

还有两个步骤,你可能会在一些机器翻译的预处理管道中看到:truecasing 和打乱。

Truecasing 正在被淘汰,但可能会略微提高翻译质量。这个预处理步骤将仅因其在句子中的位置而大写的字符转换为小写。

例如:

He will go to Canada and I will go to England.
I am not sure why.

以 truecased 形式表示:

he will go to Canada and I will go to England.
I am not sure why.

“He”中的“h”被小写化,因为它只因在句中的位置而被大写。这是唯一的区别。

这一步骤略微减少了词汇表的大小。

sacremoses 实现了 truecasing。

至于打乱段落对,它可能已经集成在你用来训练机器翻译系统的框架中。

训练数据通常会在每个训练周期自动重新打乱。

结论

过滤和归一化是可以显著降低训练神经机器翻译计算成本的步骤。

这些步骤也可能提高翻译质量,特别是当训练数据非常嘈杂时。

我在本文中建议的过滤和归一化规则并不适用于所有用例。它们对于大多数语言对都能很好地工作,但你可能需要根据你的语言进行调整,例如,当处理日语等亚洲语言时,你可能需要更改大多数归一化规则,以避免在日语文本中生成英文标点符号。

分词尤为关键。幸运的是,这也是最直接的步骤。大多数机器翻译的预处理管道对分词的处理是一样的,只是超参数不同。

在下一篇文章中,我将解释你需要知道的一切,以使用你刚刚预处理的数据来训练机器翻译系统:框架、神经网络结构和超参数。

我所有的文章都发布在《The Kaitchup》这本通讯中。订阅以接收每周的新闻、技巧和教程,以便在你的计算机上运行大型语言模型和机器翻译系统。

## The Kaitchup - AI on a Budget | Benjamin Marie, PhD | Substack

订阅每周的 AI 新闻、技巧和有关微调、运行和服务大型语言模型的教程……

kaitchup.substack.com

实用数据质量审计:综合指南

原文:towardsdatascience.com/data-quality-auditing-a-comprehensive-guide-66b7bfe2aa1a

探索如何利用 Python 生态系统进行数据质量审计

Mohamed A. WarsameTowards Data Science Mohamed A. Warsame

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

--

图像作者提供。

你无法管理你无法测量的东西 —— 彼得·德鲁克

介绍

数据质量审计是在我们快速发展的、人工智能赋能的世界中不可或缺的技能。正如原油需要精炼,数据也需要清理和处理才能发挥作用。古老的格言“垃圾进,垃圾出”在今天仍然与计算机早期时代一样相关。

在本文中,我们将探讨 Python 如何帮助我们确保数据集符合成功项目的质量标准。我们将深入探讨 Python 库、代码片段和示例,供你在自己的工作流程中使用。

目录

  1. 理解数据质量及其维度

  2. 使用 Pydantic 和 pandas_dq 验证数据

  3. 比较 Pydantic 和 pandas_dq

  4. 探索准确性和一致性

  5. 使用 pandas_dq 进行数据质量审计

  6. 结论

数据质量审计

在深入探讨工具和技术之前,让我们首先回顾数据质量的概念。根据广泛接受的行业定义,数据质量指的是数据集在准确性、完整性、时效性、有效性、唯一标识属性以及一致性方面的程度。

数据质量维度。图像作者提供。

完整性

数据质量的完整性涵盖了完成特定目标所需的所有关键数据元素。例如,针对营销目的的客户数据库,如果缺少某些客户的电话或电子邮件等关键信息,则被视为不完整。

为确保数据的完整性,组织可以使用数据分析技术。

数据分析是系统地检查和评估数据集,以发现模式、不一致性和异常。

通过仔细审查数据,可以识别出差距、特异性或缺失值,从而采取纠正措施,例如获取缺失的信息或实施强健的数据验证流程。结果是一个更可靠、更完整和更具可操作性的数据集,能够支持更好的决策制定、优化的营销工作,并最终推动业务成功。

但在进行全面的数据分析之前,任何数据质量审核的第一步都是审查数据字典:一个简明的描述性参考,定义数据集中数据元素的结构、属性和关系,作为理解和解释数据含义及目的的指南。

数据字典示例。图片由作者提供。

在手中拥有全面的审查或创建的数据字典后,当你利用如 Sweetviz、Missingno 或 Pandas_DQ 等低代码库的强大功能时,评估完整性变得轻而易举。

import missingno as msno
import sweetviz as sv
from pandas_dq import dq_report

# completeness check
msno.matrix(df)

# data profiling
Report = sv.analyze(df)

Report.show_notebook()

就个人而言,我倾向于使用 Pandas-Matplotlib-Seaborn 组合,因为它提供了我对输出的完全控制。这使我可以制作出引人入胜且视觉上吸引人的分析。

# check for missing values
import seaborn as sns
import matplotlib.pyplot as plt

def plot_missing_values(df: pd.DataFrame, 
                        title="Missing Values Plot"):
    plt.figure(figsize=(10, 6))

    sns.displot(
        data=df.isna().melt(value_name="missing"),
        y="variable",
        hue="missing",
        multiple="fill",
        aspect=1.25
    )
    plt.title(title)
    plt.show()

plot_missing_values(df)

缺失值图。图片由作者提供。

唯一性

唯一性是一个数据质量维度强调在具有唯一性约束的列中不存在重复数据。每条记录应代表一个唯一的实体,没有冗余。例如,用户列表应为每个注册用户提供唯一的 ID;多个具有相同 ID 的记录表示缺乏唯一性。

在下面的示例中,我模拟了合并两个结构相同的数据集的数据集成步骤。如果唯一性被违反,Pandas concat 函数的参数 verify_integrity 将抛出错误:

# verify integrity check
df_loans = pd.concat([df, df_pdf], verify_integrity=True)

# check duplicated ids
df_loans[df_loans.duplicated(keep=False)].sort_index()

唯一性违反。图片由作者提供。

理想情况下,你应将检查重复的存在作为数据质量审核的一部分。

def check_duplicates(df, col):
    '''
    Check how many duplicates are in col.
    '''
    # first step set index
    df_check = df.set_index(col)
    count = df_check.index.duplicated().sum()
    del df_check
    print("There are {} duplicates in {}".format(count, col))

时效性

时效性是数据质量的一个方面,关注数据的可用性和频率。最新和随时可用的数据对准确的分析和决策至关重要。例如,及时的销售报告应包含尽可能最新的数据,而不仅仅是几个月前的数据。到目前为止我们用于示例的数据集没有时间维度,因此我们无法更深入地探索频率。

时效性示例。图片由作者提供。

有效性

当我们过渡到有效性的概念时,应该认识到其在确保数据遵守既定规则、格式和标准方面的作用。有效性保证数据符合为数据集指定的模式、约束和数据类型。我们可以使用强大的 Python 库 Pydantic 来实现这一点:

# data validation on the data dictionary
from pydantic import BaseModel, Field, conint, condecimal, constr

class LoanApplication(BaseModel):
    Loan_ID: int
    Gender: conint(ge=1, le=2)
    Married: conint(ge=0, le=1)
    Dependents: conint(ge=0, le=3)
    Graduate: conint(ge=0, le=1)
    Self_Employed: conint(ge=0, le=1)
    ApplicantIncome: condecimal(ge=0)
    CoapplicantIncome: condecimal(ge=0)
    LoanAmount: condecimal(ge=0)
    Loan_Amount_Term: condecimal(ge=0)
    Credit_History: conint(ge=0, le=1)
    Property_Area: conint(ge=1, le=3)
    Loan_Status: constr(regex="^[YN]$")

# Sample loan application data
loan_application_data = {
    "Loan_ID": 123456,
    "Gender": 1,
    "Married": 1,
    "Dependents": 2,
    "Graduate": 1,
    "Self_Employed": 0,
    "ApplicantIncome": 5000,
    "CoapplicantIncome": 2000,
    "LoanAmount": 100000,
    "Loan_Amount_Term": 360,
    "Credit_History": 1,
    "Property_Area": 2,
    "Loan_Status": "Y"
}

# Validate the data using the LoanApplication Pydantic model
loan_application = LoanApplication(**loan_application_data)

一旦用示例测试过后,我们可以通过验证检查运行整个数据集,如果成功,应该会打印“没有数据验证问题”:

# data validation on the data dictionary
from pydantic import ValidationError
from typing import List

# Function to validate DataFrame and return a list of failed LoanApplication objects
def validate_loan_applications(df: pd.DataFrame) -> List[LoanApplication]:
    failed_applications = []

    for index, row in df.iterrows():
        row_dict = row.to_dict()

        try:
            loan_application = LoanApplication(**row_dict)
        except ValidationError as e:
            print(f"Validation failed for row {index}: {e}")
            failed_applications.append(row_dict)

    return failed_applications

# Validate the entire DataFrame
failed_applications = validate_loan_applications(df_loans.reset_index())

# Print the failed loan applications or "No data quality issues" 
if not failed_applications:
    print("No data validation issues")
else:
    for application in failed_applications:
        print(f"Failed application: {application}")

我们可以用 pandas_dq 完成相同的操作,代码量要少得多:

from pandas_dq import DataSchemaChecker

schema = {
    'Loan_ID': 'int64',
    'Gender': 'int64',
    'Married': 'int64',
    'Dependents': 'int64',
    'Graduate': 'int64',
    'Self_Employed': 'int64',
    'ApplicantIncome': 'float64',
    'CoapplicantIncome': 'float64',
    'LoanAmount': 'float64',
    'Loan_Amount_Term': 'float64',
    'Credit_History': 'int64',
    'Property_Area': 'int64',
    'Loan_Status': 'object'
}

checker = DataSchemaChecker(schema)

checker.fit(df_loans.reset_index())

这返回了一个易于阅读的 Pandas dataframe 风格的报告,详细说明了遇到的任何验证问题。我提供了一个不正确的 schema,其中 int64 变量被报告为 float64 变量。该库已正确识别这些:

DataSchemaChecker 输出。图片由作者提供。

数据类型不匹配通过使用从 DataSchemaChecker 类创建的检查器对象的一行代码得到纠正:

# fix issues
df_fixed = checker.transform(df_loans.reset_index())

DataSchemaChecker 转换输出。图片由作者提供。

Pydantic 还是 pandas_dq?

Pydantic 和 pandas_dq 之间有一些差异:

  1. 声明式语法:可以说,Pydantic 允许你使用更简洁和易读的语法定义数据 schema 和验证规则。这可以使理解和维护代码变得更容易。我发现能够定义可能值的范围而不仅仅是数据类型非常有帮助。

  2. 内置验证函数:Pydantic 提供了各种强大的内置验证函数,如 conintcondecimalconstr,这些函数允许你对数据施加约束,而无需编写自定义验证函数。

  3. 全面的错误处理:使用 Pydantic 时,如果输入数据不符合定义的 schema,它会抛出一个 ValidationError,并提供关于错误的详细信息。这可以帮助你轻松识别数据问题并采取必要的措施。

  4. 序列化和反序列化:Pydantic 自动处理数据的序列化和反序列化,使得处理不同的数据格式(如 JSON)及其之间的转换变得非常方便。

总之,Pydantic 提供了比 pandas_dq 的 DataSchemaChecker 类更简洁、更具功能丰富性且用户友好的数据验证方法。

Pydantic 可能是生产环境中验证数据 schema 的更好选择。但如果你只是想快速启动一个原型,可能会更喜欢 DataSchemaChecker 的低代码特性。

准确性与一致性

还有两个数据质量维度我们尚未探讨:

  • 准确性 是一个数据质量维度,涉及数据的正确性,确保它能无误地表示现实世界的情况。例如,一个准确的客户数据库应该包含所有客户的正确和最新地址。

  • 一致性 涉及数据在不同来源或数据集中的统一性。数据在格式、单位和数值上应该保持一致。例如,一个跨国公司应以单一货币报告收入数据,以保持在不同国家办公室间的一致性。

你可以使用dq_report函数检查数据集中存在的所有数据质量问题:

from pandas_dq import dq_report

dq_report(df_loans.reset_index(), target=None, verbose=1)

它检测以下数据质量问题:

  • 强关联变量(多重共线性)

  • 无差异的列(冗余特征)

  • 不对称的数据分布(异常值、离群点等)

  • 不频繁出现的类别

来自 pandas_dq 库的 DQ 报告。图片由作者提供。

结论

执行数据质量审计对维护高质量的数据集至关重要,这反过来又推动了更好的决策和业务成功。Python 提供了丰富的库和工具,使审计过程更为便捷和高效。

通过理解和应用本文讨论的概念和技术,你将能确保你的数据集符合项目所需的质量标准。

完整代码链接: github.com/mohwarsame273/Medium-Articles/blob/main/DataQualityAudit.ipynb

参考文献

[1] Pydantic(2023):文档 docs.pydantic.dev/(访问于 2023 年 4 月 24 日)

[2] Pandas_dq(2023):文档 github.com/AutoViML/pandas_dq(访问于 2023 年 4 月 24 日)

[3] 数据质量维度(企业数据管理委员会 EDM):cdn.ymaws.com/edmcouncil.org/resource/resmgr/featured_documents/BP_DQ_Dimensions_Oct17.pdf(访问于 2023 年 4 月 24 日)

[4] Batini, C., Cappiello, C., Francalanci, C. 和 Maurino, A.(2009)。数据质量评估和改进的方法论。ACM 计算调查(CSUR)41(3),第 1–52 页。

[5] Günther, L.C., Colangelo, E., Wiendahl, H.H. 和 Bauer, C.(2019)。改进决策制定的数据质量评估:中小型企业的方法论。Procedia Manufacturing29,第 583–591 页。

杀死你机器学习模型的数据质量问题

原文:towardsdatascience.com/data-quality-issues-that-kill-your-machine-learning-models-961591340b40?source=collection_archive---------4-----------------------#2023-01-19

数据质量纪实

应对不完美数据的复杂性

Miriam SantosTowards Data Science Miriam Santos

·

关注 发表在Towards Data Science ·8 min read·2023 年 1 月 19 日

--

这是一个专注于数据科学数据质量的专栏系列。这是第一篇文章,重点讨论数据不平衡、数据代表性不足以及数据重叠问题。

图片由Sergey Sokolov提供,来源于Unsplash

垃圾进,垃圾出。 这就是从数据中学习的诅咒。 在这篇文章中,我将讨论为你的机器学习模型提供高质量数据的重要性,并介绍一些致命的数据质量问题,如果不加以控制,可能会彻底影响你的数据科学项目。

机器学习已经成为现实世界领域中的一种无处不在的工具

从社会到医疗应用,机器学习已深深融入我们的日常生活。

机器学习在实际应用中的情况:有了强大的能力,就有了巨大的责任。这提到了面部识别技术的“编码凝视”和 Joy Buolamwini 在算法正义联盟上开展的工作。图片由engin akyurt拍摄,来源于Unsplash

也许你今天早上 7:45 醒来,因为一个算法已经分析了你的睡眠模式,并确定这是你开始新一天而不会感到昏昏欲睡的最佳时间。然后,你可能会沿着另一个算法推荐的路线开车去工作,以避免交通拥堵。

当你打开笔记本电脑时,你的电子邮件已经被分门别类到这些所谓的“智能”文件夹中,垃圾邮件也被自动过滤(通过另一个算法!),这样你可以专注于重要的消息。

在这漫长的一天结束时,也许你会有一个盲目约会,对方的个人资料在成千上万的可能性中被另一个算法挑选(好吧,脚本挑选?)。再次由另一个算法完成。

当技术变得如此普遍,如机器学习目前的状态,我们明智的做法是 专注于这些模型和它们的学习方式,因为尽管 AI 具有 服务社会的巨大潜力,它也具有 巨大的破坏和不平等的力量*。

那为什么会这样呢?

不完美数据:在不完美的世界中觅食

原因在于 算法从我们教给它们的内容中学习*。

它们从我们提供的数据中学习,并期望这些数据在多个属性上是“表现良好”的。

理想情况下,情况应该是这样的。但我们的世界不完美,我们不完美,我们产生的数据自然带有这些不完美。

数据(或大数据,一个我们在过去几年中经常听到的词汇)并不等同于优质数据,混淆这两者可能导致开发出有偏见和不公平的模型,而不是准确可靠的模型

数据与优质数据。图像由作者提供。

传统上,机器学习算法依赖于几个假设,例如训练数据的假设:

  • 现有类别的代表性是均等的;

  • 现有的数据子概念也是均等代表的;

  • 来自不同类别的实例占据输入空间的不同区域;

  • 数据中有足够多的训练实例来学习潜在的概念;

  • 特征值一致,实例被正确标记;

  • 特征对最终任务是有信息量和相关性的;

  • 训练和测试数据遵循相同的分布;

  • 所有特征值对所有实例都是可用的。

自然地,在现实世界中,缺陷总是潜伏的,这些假设往往被打破

当它们被破坏时,它们表现为数据缺陷,具体包括:

  • 不平衡数据;

  • 代表性不足的数据或小型分离群体;

  • 类别重叠;

  • 小数据、缺乏密度或数据不足;

  • 不一致数据;

  • 无关数据;

  • 冗余数据;

  • 嘈杂数据;

  • 数据集偏移;

  • 缺失数据。

如果不加以处理,这些缺陷可能会危害标准机器学习模型的性能,对商业应用和人们的生活产生不良后果。

一次错误的信用卡欺诈警报导致了重要投资的损失。一项失败的肿瘤检测转变为痛苦的治疗方案或临终决定之间的艰难选择。一个对相似面部结构的错误判断错误地判处一个人面临法律,而另一个人则被释放。

缺陷可能让我们付出金钱、自由和生命的代价。

在我深入讨论这些数据缺陷之前,我想澄清一下不完美数据的概念。

我的研究中,我将这个词作为一个总括性术语,描述任何数据特性、特有性或容易导致分类器行为和性能偏差的问题(其他作者将其描述为数据内在特征数据难度因素数据不规则性)。

这意味着某些“缺陷”不应字面理解(这可能在某种程度上转化为有缺陷的数据)。

当然,一些缺陷可能是由于数据获取、传输和收集过程中的错误引起的,但其他缺陷是领域内在性质的自然产物。 它们自然地出现,不论数据获取、传输或收集过程有多么完善。

这里涵盖的三种数据缺陷——不平衡数据、代表性不足的数据和重叠数据——就是一个很好的例子。它们往往来源于领域本身的性质,而非数据收集或存储过程中的错误。

不平衡数据:概念不均、预测不等

不平衡数据通常指的是数据集中每个类别样本数量的不均衡

换句话说,类在领域中没有得到平等表示,这使分类器的学习过程偏向于表现良好的概念,可能忽视或忽略其余的。这是一个问题,因为在大多数应用中,少数类通常是感兴趣的类。

那么我们在哪里可以找到它们?

一些示例包括疾病诊断信用卡欺诈情感分析客户流失预测

一个有趣的转折:类不均衡本身可能并不是问题!

确实,即使在高度不均衡的领域中,标准分类器也可能在分类问题复杂度较低(例如,考虑一个线性可分的领域)时取得令人满意的结果。

存在于单独的不均衡数据类重叠结合的数据。两个领域包含相同数量的点(500)和不均衡比(8:1)。图像由作者提供。

尽管单独解决类不均衡可能比较容易,但在训练机器学习模型时必须始终考虑这一点,特别是在设计合适的交叉验证方法和选择无偏分类性能度量方面。

数据不均衡:小离群点的问题

数据不均衡是另一种形式的不均衡数据

在之前的案例中,我们提到的是类间不均衡,而数据不均衡与类内不均衡现象相关,并以小离群点的形式出现。

小离群点是数据中小而未充分代表的子概念,理解为类概念中的小簇。

数据不均衡的特点是数据中出现小的子簇。图像由作者提供。

类间不均衡类似,小离群点问题在于分类器通常通过为表现良好的概念生成规则来学习,即较大的离群点。因此,它们容易对这些子概念进行过拟合,这导致新示例的分类性能较差。

那么在哪里可以找到它们?

小离群点的出现非常普遍于医疗数据,这与一些疾病(如癌症)的异质性和患者之间的生物多样性有关。其他示例包括面部和情感识别

当前研究中的一个开放挑战是区分核心概念(即使它们在数据空间中表现为簇)、代表性不足的子概念或小的分离点以及噪声实例。这本身并不是一个简单的问题,如果数据中存在其他问题(而且通常会存在),则变得更为复杂。

类别重叠:离开我的(输入)空间!

类别重叠发生在不同类别的实例共存于数据空间的同一区域。

随着不同概念的代表填充相同的区域,机器学习分类器很难区分它们,这导致了分类性能差(特别是影响这些区域中较少表示的概念)。

类别重叠的典型示例:领域中存在越来越多的重叠示例。图片由作者提供。

多年来,研究人员处理这个问题的方式包括仅从非重叠区域学习(在一定程度上忽视了这个问题)、将重叠数据视为新类别为重叠和非重叠区域构建单独的分类器

其他作者尝试区分散布在整个输入空间中的示例与集中在概念之间决策边界上的示例,应用量身定制的策略来处理每种类型的不同。

当前研究正转向类别重叠是一个异质概念,包含多种复杂来源的观点。在初步工作中,我特别将其区分为 4 种主要的重叠表示,将其分为特征重叠、实例重叠、结构重叠和多分辨率重叠,每种类型与不同的复杂性概念相关联。

我们在哪里可以找到它?

字符识别软件缺陷预测蛋白质及药物发现来看,类别重叠也是现实世界领域中的一种常见数据特征。

结论:我们从这里出发去哪里?

虽然过去几十年 AI 研究致力于生产更好的模型——我们称之为模型中心 AI——但目前的关注点已从模型优化和超参数调整转向系统识别和缓解数据质量问题——这一范式最近被称为数据中心 AI

在我们当前生活的“人工智能巴别塔”中,真正理解数据并指向重要内容将比拥有大量的“信息”更具变革性。这种指向是新数据中心人工智能范式的基础。照片由 Killian CartigniesUnsplash 提供

这种新方法包含了对数据进行系统化和持续迭代的循环,从不完美数据转变为智能且可操作数据。这自然需要对数据缺陷有深入的理解,包括其识别与特征描述,以及其综合效应和高效的缓解策略。

数据质量纪实系列介绍了数据科学中的数据质量话题,从现实世界中常见的 3 种数据质量问题开始:不平衡数据不足数据重叠数据。系列的后续部分将致力于对其他数据质量问题进行特征描述,深入探讨每一个问题,并向读者介绍有效的工具和策略,以便在处理现实世界数据集时有效识别和衡量这些问题。

关于我

博士,机器学习研究员,教育者,数据倡导者,以及全能型人才。在 Medium 上,我撰写关于数据中心人工智能和数据质量的文章,教育数据科学与机器学习社区如何从不完美数据过渡到智能数据。

数据中心人工智能社区 | GitHub | Google Scholar | LinkedIn

参考文献

  1. B. Krawczyk, 从不平衡数据中学习:开放挑战与未来方向(2016),人工智能进展,5(4),221–232。

  2. S. Das, S. Datta, B. Chaudhuri, 处理分类中的数据不规则性:基础、趋势与未来挑战(2018),模式识别 81, 674–693。

  3. A. Fernández, S. García, M. Galar, M., R. Prati, B. Krawczyk, F. Herrera, 数据内在特征(2018),Springer International Publishing,页 253–277。

  4. I. Triguero, D. García-Gil, J. Maillo, J. Luengo, S. García, F. Herrera, 将大数据转化为智能数据:关于使用 k-最近邻算法获取高质量数据的见解(2019),Wiley Interdisciplinary Reviews:数据挖掘与知识发现 9, e1289。

  5. M. Santos, P. Abreu, N. Japkowicz, A. Fernández, J. Santos, 关于类重叠和不平衡的统一视角:关键概念、多视角全景以及研究的新领域(2023 年),信息融合 89, 228–253。

数据科学最佳实践,第一部分——测试你的查询

原文:towardsdatascience.com/data-science-better-practices-part-1-test-your-queries-629ad5209f28?source=collection_archive---------5-----------------------#2023-09-07

如何确保我们的查询按预期工作——以及其他未来的好处。

Shachaf PoranTowards Data Science Shachaf Poran

·

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

--

由 Midjourney 生成

数据科学领域根植于数学和统计学以及计算机科学。尽管在过去几十年中它有了显著的发展,但只有在过去的 10 到 15 年里,它才成为组织中的一个成熟角色,并且作为科技行业中的一个独立领域而崛起。

作为一个相对年轻的职业,数据科学中的最佳实践还没有足够的时间来凝聚,也没有得到很好的记录。这与软件工程这一相对成熟的领域形成对比,后者充满了经验丰富的指南、结构和方法论,经过时间的考验被证明是有益的。

从逻辑上讲,数据科学家应从与软件工程师的重叠和紧密合作中受益,特别是在实践方面。不幸的是,情况往往并非如此,因为许多数据科学家要么对这些方法论不了解,要么不愿意学习它们,声称这些方法论不相关或不在他们的职责范围内。

在这个博客系列中,我想分享一些数据科学家在工作中可以使用的技巧、窍门和系统性方法,旨在提高我们代码的正确性和稳定性,更好地管理我们的模型,并改善团队合作。

前提是

我们从每个处理大数据的人都在某个时刻面对的场景开始,有些人可能甚至每天都面临这个问题:

你正在使用PySpark并希望从一个大表中提取一些信息。你无法在内存中保持大量的相关数据,因此你被迫在查询语言中完成所有的转换、聚合、连接等操作。

你开始编写查询,并且对它感到满意,因为PySpark使得即使查询太复杂而难以解释给其他人,它也能轻松使用具有 Python 风格的优雅 API。即使你决定使用 SQL 接口——你仍然愉快地输入着。

然后,你意识到在groupBy调用中遗漏了一个关键列,于是回去修复它。

然后,你意识到一个窗口函数缺少orderBy子句。

然后,你决定将第四行中使用的这个神奇数字改为 1.25,而不是 1.2。

你最终会反复地查看这些 20 到 50 行的查询,来回调整 20 到 30 分钟,稍微修改一下,同时构建最终的查询结构。

然后……你运行查询,它失败了。

你再次遍历你刚刚生成的代码行,努力找出遗漏的逻辑需求,并逐一修复它们。

最终,查询运行并返回了一些结果。

但是……

谁能保证这些结果确实反映了你一直以来想要的内容,并且与当前你头脑中的过程相匹配?

这时测试会为我们提供帮助。

测试?

是的。我们做的是:

  1. 手动制作一个小数据集。

  2. 手动计算我们希望通过查询获得的结果。

  3. 在那个小数据集上应用我们编写的查询。

  4. 将查询结果与我们自己的计算结果进行匹配。

如果我们遇到不匹配的情况,我们必须修复一些东西——要么我们的手动计算错误,要么查询没有按预期执行。另一方面,如果结果匹配——我们可以继续到下一步。

现在,我将逐步向你展示我在编写这些测试时使用的结构。

设置环境

让我们从创建我们需要与PySpark一起工作的环境(即固定装置)开始。我们每次运行可能会测试许多用例,因此我们在模块级别设置PySpark会话。否则,我们可能需要为每个测试启动和停止会话,这会产生不可忽略的开销。

我使用 Python 内置的*unittest*,但如果你或你的团队其他成员使用*pytest**nose*或任何其他测试框架,我相信你会找到执行这些操作的方法。

*unittest*有两个钩子*setUpModule**tearDownModule*,分别在测试之前和之后运行。我们将使用这些钩子来启动和停止我们的PySpark会话。

test_pyspark.py

import pyspark
import unittest

spark: pyspark.sql.SparkSession | None = None

def setUpModule():
    global spark
    spark = get_spark_session('local')

def tearDownModule():
    global spark
    if spark is None:
        return
    try:
        spark.stop()
    finally:
        spark = None 

我喜欢我的会话创建函数是可重用的,所以这里是它(我会在适当的时候填写非本地选项):

query_pyspark.py

import pyspark

def get_spark_session(scope='local'):
    if scope == 'local':
        return (
            pyspark.sql.SparkSession.builder
            .appName('unit-tests')
            .master('local[4]')
        ).getOrCreate()
    else:
        ...  # TODO

如果项目变得更大,我会将这个函数放在PySpark专用的工具文件中,但目前我们将项目保持扁平和小巧。

我们的第一个测试

我现在要测试的第一件事是运行这个测试时是否实际获得了会话。以下是测试:

test_pyspark.py

class TestPysparkQueries(unittest.TestCase):
    def test_session_created(self):
        self.assertIsNotNone(spark)

你瞧,我运行了测试(PyCharm 允许你直接从代码中运行,你应该会看到每个测试旁边有一个绿色的“播放”按钮),并且收到了 OK 消息:

创建测试我们的数据

现在我们可以开始讨论数据了。你应该手头有一个小的数据集,涵盖你可能遇到的不同情况,并且仍然可以手动处理。就实际大小而言,我通常建议 20–50 行,具体取决于领域和查询的复杂性。如果涉及分组,请选择 5–10 个不同的组。

出于教学目的,我创建了一个包含名字和出生日期的数据集。为了简单起见,我假设所有具有相同姓氏的个体是兄弟姐妹。我还引入了行顺序的随机性,以防止顺序敏感的查询在不直接处理顺序的情况下获得正确的答案。数据如下所示:

现在是时候将数据加载到我们的PySpark会话中了。但首先,让我们为它创建一个基本的测试。顺便说一下,创建测试然后编写使测试通过的代码是测试驱动开发(TDD)方法的一部分,但我不对数据科学家传授这个方法,只关注测试部分。

对于一个理智的测试,我们可以测试列名,测试数据集大小,或者两者都测试,或者我们可以提出更深层次的测试。甚至,我们可以编写一个测试,逐行匹配 CSV 文件和DataFrame

我们在编写测试时越严格,后来对代码正确性的确信就越强,但这也会使未来的更改更加困难,例如,如果我们想在数据集中添加/更改一行以测试特定边界情况怎么办?

在我们的工作中,平衡这些速度和正确性因素更像是一门艺术,而不是科学,这随着时间和实践会变得更加自然。

test_pyspark.py

 def test_load_data(self):
        df = get_family_data()
        self.assertEqual(25, df.count())

接下来,我们编写加载数据的函数:

query_pyspark.py

def get_family_data():
    return (
        get_spark_session(scope='local')
        .read.csv(os.path.join(os.path.dirname(__file__), 
                  '../assets/data_sample.csv'))
    )

当我运行测试时……失败了?这怎么可能?

在重新计算行数并确保为 25 后,我最终在代码中添加了*header=True*,测试通过了(不用担心,我会在接下来的示例中省略虚假的戏剧性):

query_pyspark.py

def get_family_data():
    return (
        get_spark_session(scope='local')
        .read.csv(os.path.join(os.path.dirname(__file__), 
                  '../assets/data_sample.csv'), header=True)
    )

测试我们的查询

现在是查询特定测试的时间。假设我想从每个家庭中获取最年长的孩子。我查看数据集(或使用排序的电子表格)来找出我期望获得的确切名字集合,并将其硬编码到我的测试中:

test_pyspark.py

 def test_elder_child_query(self):
        df = get_elder_child(get_family_data())
        elders = {_.elder_child for _ in df.toLocalIterator()}
        self.assertEqual(elders, {'Gus', 'Rita', 'Sam', 'Trent', 'Ursula'})

使测试通过的代码如下:

query_pyspark.py

def get_elder_child(family_df: pyspark.sql.DataFrame):
    return (
        family_df
        .orderBy(f.col('date_born').desc())
        .groupby('last_name')
        .agg(f.first('first_name').alias('elder_child'))
    )

尽管我省去了戏剧性的描述,我还是要告诉你,我不得不多次修复查询才能使测试通过。例如,我按*first_name*分组,聚合了*last_name*的值,并且忘记了进行降序排序。

在我的工作中,测试让我多次避免了丢脸

完成了吗?绝对没有。

我们应该考虑边界情况,比如如果有双胞胎呢?是否有没有孩子的家庭?如果数据不可靠,那么空值怎么办?

对于这些选项,我们将转到我们的数据集,将其更改为产生这样的案例,然后更新我们的测试和代码。

如果我们通过后来出现的错误遇到这些特殊情况(即使我们不是自己提出的),我们也会这样做——更改数据集以反映这些情况,然后从那里继续。

我们还应该为其他查询编写测试,并且我们会遇到不同类型的测试。在上面的测试中,我们关心的是结果集,但如果我们想测试简单的 1:1 转换, f(row) = y,我们需要考虑 Spark 在行顺序上的非确定性。

例如,假设我们想要获取数据集中名字的首字母。

一种选择是对*DataFrame*进行排序,并在使用我们手工制作的列表进行相等断言时相信这个顺序:

query_pyspark.py

def get_initials_col():
    return (
        f.concat(
            f.substring('first_name', 0, 1),
            f.lit('. '),
            f.substring('last_name', 0, 1),
            f.lit('.'),
        )

    ).alias('initials')

test_pyspark.py

 def test_get_initials_col_1_by_1(self):
        df = (
            get_family_data()
            .withColumn('initials', get_initials_col())
            .orderBy('date_born')
        )
        expected_list = ['V. A.', 'W. W.', 'X. M.', 'Y. T.', 'Z. C.', 'I. M.', 'J. T.', 'K. C.', 'L. A.', 'M. W.',
                         'N. M.', 'O. T.', 'P. C.', 'Q. A.', 'A. A.', 'B. W.', 'C. M.', 'E. T.', 'F. C.', 'G. A.',
                         'H. W.', 'R. W.', 'S. M.', 'T. T.', 'U. C.']
        for expected, actual in zip(expected_list, [_.initials for _ in df.toLocalIterator()]):
            self.assertEqual(expected, actual)

另一种选择是编写一个本地函数来完成相同的工作,并进行充分测试。然后,我们可以在将结果加载到内存后应用它,并编写测试以逐行断言相等。以下是一个示例:

query_pyspark.py

def get_initials(first_name, last_name):
    return f'{first_name[:1]}. {last_name[:1]}.'

test_pyspark.py

 def test_get_initials(self):
        self.assertEqual('B. H.', get_initials('Bob', 'Hope'))
        self.assertEqual('C. C.', get_initials('Charlie', 'Chaplin'))
        self.assertEqual('J. L.', get_initials('Jonathan', 'Livingstone'))

    def test_get_initials_col_support_function(self):
        df = (
            get_family_data()
            .withColumn('initials', get_initials_col())
        )
        for row in df.toLocalIterator():
            self.assertEqual(get_initials(row.first_name, row.last_name), row.initials)

在这两个选项中,我肯定会选择后者,因为它更灵活,因为它不直接依赖数据,而是通过支持函数的代理——这些函数在不与数据集耦合的情况下进行测试。

当然,如果函数对你的查询负载不重,你可以选择将其用作 UDF,保持代码复杂性低。

等等,还有更多内容吗?

当然。有许多不同的情况,比如联接和窗口函数的结果,但我相信上述示例足以说明测试是一种重要工具,是编写查询时的有效方法,即使对像我这样的数据科学家也是如此。

请注意,我选择展示如何在使用PySpark时工作,因为它是一个常见的大数据工具,但这种模式不仅限于PySpark或大数据数据库。实际上,它应该适用于任何数据库。你也可以使用这种方法与内存数据库类似的工具,如*pandas*

只要你能够:

  1. 连接到数据源。

  2. 加载/模拟数据源中的数据。

  3. 执行查询。

  4. 检索并处理查询结果。

一切准备就绪。如果你使用的工具无法执行这些步骤中的任何一个,你可能需要重新考虑使用这个工具。

而且你会发现,测试你的代码还有一个隐藏的好处。假设你发现你的某个函数在运行时间或内存消耗方面表现不佳,并决定尝试优化或重构它。现在你可以使用现有的测试来保证你的新代码给出的输出与之前的相同。没有这些测试,我个人会害怕改动哪怕一行代码,担心会破坏某些下游依赖。

总结

测试是一种强大且重要的方法,可以验证代码的正确性,并使任何重构易于管理。

在未来的帖子中,我将提供更多关于数据科学中良好实践的示例。我们将涉及如何在不相互干扰的情况下协同工作在同一个模型上,如何管理数据集版本,如何观察我们代码在生产环境中的性能,以及更多内容。

敬请关注。

常见问题解答

问:等等,什么?

答:欢迎在这里或其他地方就本博客系列中的概念展开讨论。

问:如果我的查询需要成千上万行来测试呢?

答:编写查询的参数化版本,例如*def get_n_smalles_children(family_df, n): …*并使参数足够小。另一个选项是通过编程方式模拟数据,但这也会带来新的问题和挑战。

问:如果我不断更改查询,这是否意味着我也需要更改测试?

A: 理想情况下,你不会随时间改变查询,但我知道我们领域的探索性。因此答案是“是的”。这也是为什么你可能会感觉编写测试时速度变慢的原因之一。然而,速度是与准确性/正确性权衡的。你可能会在后期过程中编写测试,当查询结构更加稳定时。

Q: 如果我不使用 PyCharm,怎么运行测试?

A: 在测试文件的末尾添加以下魔法行,然后使用*python test_pyspark.py*运行它。别忘了确保代码根目录被包含在PYTHONPATH中,以便导入能够正常工作(PyCharm 会自动处理这个问题)。

if __name__ == '__main__':
    unittest.main()

Q: 如果我不想(或者无法)将数据保存在.csv 文件中怎么办?

A: 任何对你有效的数据存储和加载方式都可以,只要尽量保持整洁即可。对于非常小的数据集,我使用了 dict-to-DataFrame(或者如果你愿意,可以使用 json-to-DataFrame),对于较大的数据集,我使用了永久存储在 Hadoop 上的表。

Q: 你给出的示例函数不是非常简单吗?

A: 是的。这就是我教授的方式——通过给出简单的例子,并逐渐使其变得更加复杂。不幸的是,这篇文章的边距太小,无法容纳后面的部分。

Q: 你有没有将上述代码存储在某个仓库中,以便我可以用作参考?

A: 是的,我确实这样做了。

数据科学职业路径、技能与特别项目:我们 2023 年的最佳阅读

原文:towardsdatascience.com/data-science-career-paths-skills-and-special-projects-our-best-reads-of-2023-d3ed88b6db41?source=collection_archive---------1-----------------------#2023-12-21

TDS EditorsTowards Data Science TDS Editors

·

关注 发表在 Towards Data Science · 作为 Newsletter · 5 分钟阅读 · 2023 年 12 月 21 日

--

2023 年可能是大型语言模型(LLM)的年份——我们上周突出了我们关于 ChatGPT 的最受欢迎的文章以及相关主题——但数据科学和机器学习的领域太广泛,难以将其缩减为单一现象(尽管这可能是不可避免的)。

每天,TDS 作者都会发布涵盖各种主题的优秀作品,从最新的工具到职业见解和项目演示。为了今年的最后一期 Variable,我们决定突出一些最令人难忘和广泛阅读的帖子,围绕三个主题:数据科学家的编程、职业发展,以及创意项目和观点文章。它们出色地展示了这个领域及我们的社区是多么充满活力、多样性和动态。

我们希望你喜欢我们的精选内容,并再次感谢你在过去一年中的支持。

数据科学家的编程

  • 编程很难,直到我学会了这两件事

    如何从“有抱负的开发者”成为一名受雇的开发者?Natassha Selvaraj 的病毒式热门文章提供了切实可行的建议和大量的灵感。

  • Pandas 2.0:数据科学家的游戏规则改变者? 新的 Python 库每天都在出现,但 Pandas 仍然是数据科学家的必备工具。Miriam Santos 的有用概述带我们了解了该库 2.0 版本的显著特点。

图片由 Sneha Chekuri 拍摄,发布在 Unsplash

  • 12 个将你的代码提升到新水平的 Python 装饰器

    如果你想扩展你的 Python 知识,为什么不探索装饰器的世界呢?Ahmed Besbes 介绍了 12 种你可以尝试的装饰器,以便编写更高效的代码。

  • 我在几乎所有数据科学项目中使用的 5 个 Python 装饰器 想要更多装饰器吗?通过跟随 Thuwarakesh Murallie 的清晰简明指南,给你的编程工具包额外加点油。

  • 5 个区分高级开发者与初级开发者的 Python 技巧

    解决问题的方式不止一种,但这并不意味着所有策略都等同。Tomer Gabay 展示了经验丰富的开发者在代码中引入的小调整如何产生显著的影响。

职业发展

  • 6 个正在毁灭你数据科学生产力的坏习惯从过度依赖笔记本到跳过数据版本管理,数据科学家常常因不良习惯而陷入困境。多纳托·里奇奥提供了切实可行的建议,作为强有力的解药。

  • 数据科学家角色的原型鉴于职位描述的广泛差异和招聘广告的模糊不清,选择数据科学领域中的正确路径可能会很棘手。为了帮助你完成这一过程,斯蒂芬妮·基尔默列出了你可能会考虑的主要角色类型。

  • 在开始数据科学之旅之前,不要错过这 5 个必做步骤对于求职者来说,这段时期充满挑战,但没有理由感到绝望。正如胡劳德·艾拉米在她受欢迎的帖子中所强调的,早期职业数据科学家有能力为自己创造成功的机会。

  • 让我获得数据科学家工作的作品集通过详细讲解如何创建一个引人注目的数据科学作品集,马特·查普曼的指南引起了大量从业者的共鸣,他们希望在竞争激烈的领域中脱颖而出。

  • 使用 OpenAI 和 Python 来提升你的简历:一步一步的指南为什么不利用新工具来简化简历制作过程?皮耶罗·派亚隆加将 ChatGPT 和一些 Python 代码结合起来,将这一常常枯燥的任务变得更加愉快。

  • 如何制定有效的自学计划,成功学习数据科学

    如果你为即将到来的一年设定了一些雄心勃勃的目标,你需要一个能够帮助你度过挑战时刻的计划。麦迪逊·亨特详细解释了如何制定一个既有效又可持续的自学计划。

  • 我在数据科学总监职位的第一年学到了什么 每个人当然都想在职业生涯中有所进步,但成为高级数据科学家也面临一系列挑战。CJ Sullivan 讲述了她在担任总监级高管的第一年中需要克服的一些挑战。

创意项目和观点文章

  • 我如何使用 AI 生成的曲目构建了一个 Lo-fi 音乐网络播放器 在围绕 AI 的许多炒作中被忽视的是其激发一些真正创新项目的能力。举个例子:Aleksandra Ma的有趣(且聪明)的 lo-fi 音乐播放器。

  • 监管 AI:基于机制的方法案例 试图对 AI 发展进行规制的努力一直笨拙而棘手,众多利益相关者争夺影响力。Viggy Balagopalakrishnan 分享了如何使这一过程变得更有效的主观见解。

  • 走向负责任的 AI 生成内容的途径 在 AI 的广泛领域中,生成工具占据了社会和伦理问题的相当大一部分,这些问题仍需解决。Lingjuan Lyu的深刻文章绘制了一张通向更负责任使用这一强大技术的路线图。

  • 仪表板已死:三年后 我们过多地关注如何生成和呈现数据,以至于其在其他领域同事中的实际使用有时感觉像是事后的想法。Taylor Brownlow重新审视了仪表板的概念,以揭示协作和沟通的长期挑战。

  • 被禁书页:美国书籍禁令的数据分析 数据在告知和塑造重要对话方面的力量得到了充分展示,Yennie Jun深入探讨了美国书籍禁令的令人担忧的趋势,“LGBTQ+社区、穆斯林家庭、科学女性、种族主义、性别歧视,甚至是关于牛的图画书的故事。”

  • 摆脱局限:在 MoMA 验证机器幻觉 我们很高兴阅读了Christian Burke关于他在纽约现代艺术博物馆的协作人类/人工智能装置工作的介绍。随着人工智能在我们生活中的影响越来越大,这为各种创作者打开了新的方向,这是一次令人着迷的探索。

再次感谢你在 2023 年对我们作者工作的支持!如果你喜欢在 TDS 上阅读的文章,可以考虑成为 Medium 会员:这是一个新的会员等级,为你喜爱的作者提供更大的奖励,以回报他们的高水平写作。

《The Variable》将在假期期间短暂休息,但我们迫不及待想在新的一年里再见到你。

TDS 编辑部

数据科学专业知识以多种形式和形状出现

原文:towardsdatascience.com/data-science-expertise-comes-in-many-shapes-and-forms-6a14f665f325?source=collection_archive---------7-----------------------#2023-05-25

TDS EditorsTowards Data Science TDS Editors

·

关注 发布于 Towards Data Science · 作为 Newsletter 发送 · 3 分钟阅读 · 2023 年 5 月 25 日

--

很少有事情比发布作者的第一篇 TDS 文章更让我们感到高兴。这部分是因为发现一个新声音分享他们独特的经历和知识的兴奋感。另一个原因是一次又一次地意识到,即使我们处理的是常见的问题和挑战,总有一个新角度值得探索。

我们将本期《Variable》致力于最近由我们最新一批作者所作的贡献,并且有几个原因。首先,我们想庆祝他们用清晰、精准和独特的个人视角传达复杂思想的能力。

其次,也许更自私一点——我们突出这些文章,希望我们社区中的其他成员也能受到启发,与 TDS 的观众分享他们的专业知识。我们涵盖了非常广泛的主题——从 AI 伦理和气候变化数据到 ML 管道和产品管理。真正重要的是你对自己的主题充满热情,并认为其他人也应该关心它。

祝阅读愉快!

  • “世界需要另一个GPT 家族的介绍吗?”这是一个值得提出的问题。在阅读了Beatriz Stollnitz对这些无处不在的语言模型背后核心概念的详细解释后,我们相当确定你的回答会是响亮的“是!”

  • 关系型数据库已经存在了几十年,但它们的持久性并不是忽视它们强大功能和经得起时间考验的可靠性的理由——相反!Stephanie Lo 解释了为什么数据科学家应该更深入地了解数据库的工作原理并学习如何有效使用它们。

  • 作为一名从事 NLP 研究的实践者,Francisco Caio Lima PaivaChatGPT 在情感分析任务中的表现如何感到好奇,因此他决定测试这款聊天机器人的能力,并与特定领域的模型进行对比。

Viviane OkuboUnsplash上拍摄的照片

  • 你是否偶尔会觉得编写自定义函数的想法让人感到畏惧?那就不要错过Vivian Peng的新文章:它涵盖了使用自定义函数在 R 中生成多个图形的完整过程****。 作为额外的好处,Vivian 描述的主要步骤可以在各种工作流程中普遍适用。

  • “[处理包含日期和时间的数据可能会让人感到不知所措,”Andreas Lukita说;我们猜测你们中的许多人对这句话点头如捣蒜。Andreas 将提供帮助,Python 的 datetime 模块的全面、一站式资源

  • 管理 ML 和 AI 团队有其自身的奖励和挑战。Przemek Pospieszny 分享了他在研发和产品领导方面的丰富经验,并强调了敏捷性、规划以及在创新与务实之间找到正确平衡的重要性

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

直到下一个 Variable,

TDS 编辑

数据科学造福社会:超越利润,迈向更美好的世界

原文:towardsdatascience.com/data-science-for-good-beyond-profits-towards-a-better-world-2d161e2d2b28

利用数据分析的力量来推动公司内部的积极变革,同时提高盈利能力。

Samir SaciTowards Data Science Samir Saci

·发表于Towards Data Science ·阅读时长 11 分钟·2023 年 8 月 25 日

--

数据科学造福社会 — (作者提供的图片)

数据科学可以支持远超财务收益的业务转型。

这可以改善工作条件,减少不平等,并促进包容性的工作环境。

在我作为高级供应链工程师的经验中,我主要使用分析工具来提高运营绩效并降低成本。

然而,工程师的职责不仅仅是最大化利润;她还可以帮助让世界变得更美好。

作为数据科学家,您如何改善物流操作员的工作条件?

在这篇文章中,我将分享使用数据科学来改善物流操作员的工作条件(和奖金)的例子。

💌 免费订阅最新文章: 新闻通讯

📘 您的完整供应链分析指南:分析备忘单

I. 生产力与盈利能力

物流公司的盈利能力

作为一名前供应链解决方案设计师,我职业生涯的早期帮助物流公司优化生产力以提高利润。

我亲眼目睹了我们的客户 (零售、时尚、奢侈品、化妆品) 面临的巨大压力,他们需要按时交付货物,同时最小化成本

3PL 零售分销 — (作者提供的图片)

作为第三方物流提供商,这种压力持续向我们施加,我们时刻担心合同无法续签。

在这种压力巨大的环境下,削减成本和采用激进管理策略的诱惑始终存在。

让我们找到更聪明的工作方式吧!

通过利用数据科学的力量来改进流程,我们可以避免将这种压力转移到操作人员身上,并推动积极的变化。

过程生产力定义

让我们以一个假设的场景来讨论,一家主要的国际时尚零售商正在与物流公司建立其配送网络

配送网络以交付 100 家门店 — (图像来源:作者)

I&N 正在寻求外包其仓储和运输操作,以交付其在上海的门店。

物流团队组织招标,也称为请求提案(RFP),邀请全球物流公司提交他们的解决方案。

请求提案 — (图像来源:作者)

作为 RFP 的一部分,I&N 的物流团队提供

  • 数据和流程要求

  • 一份报价单概述了不同服务价格,如存储、接收、箱子拣选、单件拣选、装载和退货管理。

作为解决方案设计经理,我负责提出解决方案并计算每项服务的价格。

价格通过考虑设备和劳动力成本来确定,这些成本由操作人员的生产力和销售利润率驱动。

计算处理服务的价格 — (图像来源:作者)

附注:销售利润率定义为营业额中代表利润的百分比。

仓库和运输团队随后将进行为期三年的操作,关键目标是保持这一利润率。

这可以通过提高价格(几乎不可能)或降低成本来实现。

损益平衡 — (图像来源:作者)

后者的选择通常归结为一个关键因素:提高生产力

但是你如何提高生产力呢?

这可以通过使用高个人生产力目标、工资削减和非法劳动实践的激进管理策略来实现。

这通常会导致员工压力和不满增加,这可能会影响贵公司的 ESG 评分。

❓ 想了解更多关于 ESG 评分的内容吗?

## 什么是 ESG 报告?

利用数据分析进行全面和有效的环境、社会及治理(ESG)报告

towardsdatascience.com

第二种方法是优化流程、布局和货物流动,帮助操作人员在相同的努力下变得更高效

这种方法不仅有利于公司,而且改善了工作条件

为了说明我的观点,我将分享我在零售、快时尚和奢侈品行业的职业生涯中实施的示例。

💡 关注我在 Medium 上的文章,获取更多与🏭供应链分析、🌳可持续发展和🕜生产力相关的内容。

II. 通过优化提高生产力

数据科学可以成为改善操作员工作条件和工资的重要工具。

提高拣货效率

让我们从一个来自再工程项目的例子开始,我使用分析来帮助操作员提高拣货生产力。

仓库拣货可以定义为从库存中提取产品以准备订单(电子商务、门店配送)的任务。

仓库中的拣货路线——(作者提供的图片)

配送中心(DC)中,从一个位置到另一个位置的行走时间在picking route中可以占据操作员工作时间的60%到 70%

计算拣货的价格——(作者提供的图片)

减少行走时间可以对生产力产生显著影响。

假设操作员拣选的是完整的箱子。

  • 生产力将以(箱数/小时)来衡量。

  • 价格将以($/小时)来衡量。

仓库操作员有生产力目标,他们需要达到这些目标才能获得奖金。

对于零售和快速消费品(FMCG)操作,操作员面临巨大的压力,因为他们占据了固定和变动成本的主要部分。

改善操作员生产力的最佳方法是什么?

如果您最大化每米行走拣选的箱数,可以在最小化操作员的努力的同时提高他们的生产力。

提高每米行走拣选的箱子数量的 3 种方法——(作者提供的图片)

在一系列文章中,我提出了几种数据驱动的方法来减少行走距离并最大化操作员的生产力。

  • 订单批处理以增加每条路线拣选的订单行数。

  • 拣货地点的聚类以按区域分组订单。

  • 使用 Google-OR 库进行高级路径规划以优化拣货路线。

这些不同的优化方法已经通过实际的订单行和仓库布局进行了测试。

更多的箱子被拣选 + 更少的距离 = 更高的生产力。

结果显示,在拣选相同订单范围时,行走距离大幅减少。

通过在您的仓库管理系统中实施这些算法,您可以在不改变流程的情况下提升操作员的生产力。

💡 欲了解更多详细信息,您可以查看这些文章,

[## 使用订单分批处理提高仓库生产力

设计一个仿真模型,以估算几种单拣货员路线问题策略在拣货中的影响…

向数据科学进发 [## 使用空间聚类提高仓库生产力

通过使用拣货位置空间聚类,将订单分批处理来提高仓库拣货生产力

向数据科学进发 [## 使用路径规划算法提高仓库生产力

基于旅行推销员问题设计的路径规划算法,采用 Google AI 线性优化…

向数据科学进发

使用排队理论设计的包裹打包过程

现在让我们转到电子商务操作。

在线零售的兴起给履行中心带来了巨大的压力,以无与伦比的速度准备和发货订单。

我们的仓库每天可以发货多少订单?

在这个例子中,一名持续改进工程师的主要问题是运输能力

仓库中的打包区域 — (图像由作者提供)

拣货之后,订单等待过长时间被打包并装上卡车。

根据现场观察和生产力分析,我们的工程师了解到打包过程是瓶颈

为了减轻打包操作员的压力,她希望重新设计布局并优化流程。

现场经理决定投资第二个打包站

因此,我们的工程师希望使用排队理论找到最佳布局。

解决方案 1:保留一条带有两个平行工作站的单线

解决方案 1 — (图像由作者提供)

解决方案 2:增加一条带有专用工作站的第二条线

解决方案 1 — (图像作者提供)

最佳解决方案是什么,以最小化排队时间并减少瓶颈?

运用排队理论的概念,我们可以估计考虑输入流量变异的两种布局的表现。

两种解决方案的模拟结果 — (图像作者提供)

分析显示,第二种解决方案鲁棒性较差,可能会在面对容量变异时降低包装站的能力。

假设我们有 1.5 的变异性,

  • 打包线队列时间减少-25%(秒)

  • 在相同的包装速度下更高的整体生产力

按解决方案计算的平均生产力 — (图像作者提供)

多亏了这个简单的建模,她设计了一个可以让操作员在没有额外努力的情况下提高生产力的布局。

💡 有关更多详细信息,请查看这篇文章

## 使用路径寻找算法和 Python 提升仓库生产力

实施基于旅行商问题的路径寻找算法,由 Google AI 线性优化设计…

towardsdatascience.com

结论

使用先进的分析工具,您可以重新设计流程,以帮助操作员提高效率。

  • 提高挑选和包装操作员的奖金(👩‍🏭)

  • 更少的努力就能快速行走或打包,无需经理的压力(🧘)

  • 为公司带来更好的盈利能力(💰)

在下一部分,我们将探索其他基于数据的改进机会,重点关注资源分配

💡 在 Medium 上关注我,获取更多关于🏭供应链分析、🌳可持续性和🕜生产力的文章。

II. 智慧地投资于您的员工

前一部分专注于设计一种最佳过程,以最大化操作员的生产力。

然而,过程设计执行之间存在一个差距,这个差距可以通过优化工具来弥补。

入库管理的劳动力规划

对于我们的快时尚客户I&N,我们必须计划劳动力以应对商店需求的波动。

电子商务仓库的日常波动示例 — (图片由作者提供)

I&N 提供体积预测,如上所示,管理者正在规划所需的员工人数以满足需求。

基于体积预测的劳动力规划 — (图片由作者提供)

他们必须在确保员工保留和遵守当地法规的同时,最小化临时工的数量。

在我们的例子中,我们希望帮助入库经理

他的团队职责包括

  • 卸货 从卡车上卸下托盘

  • 扫描 每个托盘并记录在仓库管理系统(WMS)中收到的数量

  • 将这些 托盘放置在库存区域

仓库卸货过程 — (图片由作者提供)

团队有两种生产力目标

  • 每小时卸货的托盘数量 每个工人 (I)

  • 每小时卸货的托盘数量 为整个团队支付(II)

如何根据体积和生产力来确定劳动力规模?

如果经理招聘了过多的操作员总体生产力(II)可能会大幅下降。

根据体积预测,他可以估算每天所需的资源。

每日入库资源需求预测 — (图片由作者提供)

确保员工保留,你需要保证每周 5 个连续工作日的最低要求。

根据上述约束进行轮班规划 — (图片由作者提供)

为了帮助我们的入库经理平衡这些约束和目标,我们可以使用Python 的线性规划

最终结果 — (图片由作者提供)

这个优化工具的结果令人满意,

  • 除了星期五和星期六,我们没有多余的资源

  • 供应每天与需求相匹配

除了这两天外,总体生产力不会受到计划问题的影响。

如果操作员达成目标,他们将获得全额奖金,而不会影响运营利润。

💡 要了解更多细节,你可以查看这篇文章

## 使用线性规划和 Python 优化劳动力规划

你需要雇佣多少临时工来吸收每周的工作量,同时确保……

towardsdatascience.com

仓库操作员的最佳激励政策

在这个最后的例子中,让我们假设你正在帮助一个拥有22 个仓库的物流公司的区域主任处理她的损益表。

配送中心的拣货、增值服务和包装 — (作者提供的 CAD 模型)

目标仍然是最大化操作员的拣货生产力

她希望使用财务激励来激励操作员提高每小时的产出。

当前的激励计划为达到目标的操作员提供每天 5 欧元(日薪:62 欧元)

然而,这并不高效,因为只有20%的操作员能够达成他们的目标。

达到 75%目标所需的最低每日奖金是多少?

这个想法是进行一个数据驱动的实验

  1. 随机选择你在22 个仓库中的操作员

  2. 实施每日激励金额在1 到 20 欧元之间

  3. 验证操作员是否达到了目标

使用逻辑回归,我们可以得到一个概率图,帮助估算每个每日激励值下达到目标的概率。

你的样本数据的拟合线图 — (作者提供的图片)

根据趋势,以15 欧元的每日激励,我们有 75%的概率达到目标。

这一结果,通过统计工具的支持,为我们的主管提供了有价值的见解,以便调整她的激励政策。

💡 更多详细信息,请查看这篇文章

## 使用 Python 进行精益六西格玛 — 逻辑回归

用 Python 代替 Minitab 执行逻辑回归,以估算达到 75%目标所需的最低奖金。

towardsdatascience.com

结论

我们发现了一些优化和统计工具,帮助公司改善资源分配以实现关键运营目标。

  • 提高整体团队生产力和改善利润率(👩‍🏭)

  • 为操作员提供更多激励(💰)

  • 减少管理层的压力(🧘)

💡 关注我在 Medium 上的更多文章,内容涉及🏭供应链分析、🌳可持续发展和🕜生产力。

III. 下一步

希望这些现实世界的应用能够激发你设计和部署支持财务收益并改善操作条件的工具。

随着利益相关者对企业社会责任(CSR)和 ESG 评分的要求越来越高,工作条件和公平工资将成为任何业务转型的关键参数。

ESG 支柱演示——(作者提供的图片)

环境、社会和治理(ESG)是一种用于披露公司环境足迹、社会影响和治理结构的报告方法。

查看 ESG 评分中包含的指标,你会发现利用数据科学带来积极变化的额外机会。

有关 ESG 评分的更多细节

什么是 ESG 报告?

利用数据分析进行全面有效的公司环境、社会和治理报告

towardsdatascience.com

在本系列的下一篇文章中,我将专注于高级分析以减少消耗品使用和废物生成

敬请关注!

🌍 对全球可持续未来的路线图感到好奇吗?

深入了解我最近关于联合国可持续发展目标如何通过数据支持的见解

## 数据科学支持可持续发展目标(SDGs)——数据为善

将全球可持续发展倡议与公司供应链数字化转型通过数据科学相结合

s-saci95.medium.com

关于我

LinkedinTwitter上与我联系,我是一名使用数据分析改善物流操作和降低成本的供应链工程师

如果你对数据分析和供应链感兴趣,可以看看我的网站。

## Samir Saci | 数据科学与生产力

一个关注数据科学、个人生产力、自动化、运筹学和可持续性的技术博客……

samirsaci.com

💡 在 Medium 上关注我,获取更多有关🏭供应链分析、🌳可持续性和🕜生产力的文章。

参考资料

拉曼光谱的数据科学:一个实际示例

原文:towardsdatascience.com/data-science-for-raman-spectroscopy-a-practical-example-e81c56cf25f?source=collection_archive---------5-----------------------#2023-01-16

光谱预处理和建模的实际示例

Nicolas Coca, PhDTowards Data Science Nicolas Coca, PhD

·

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

--

拉曼光谱的数据科学。从光谱预处理到建模:峰值检测与去除、基线扣除、平滑处理以及经典最小二乘法应用于成分量化。[作者提供的图片]。

介绍

拉曼光谱学提供了分子振动模式的信息,以拉曼光谱的形式展现。这些光谱可以像结构指纹一样,用于识别和表征分子和材料。从中我们可以了解材料的组成、温度、材料内部的应力、是否有施加的电磁场等。然而,为了提取这些信息,我们需要先清洗和处理数据,然后应用化学计量学或机器学习模型。在本文草稿中,我将解释数据科学管道如何在拉曼光谱数据上工作,并给出定量光谱分析的示例化学计量学。

本文是《Python 拉曼光谱数据科学系列》的一个部分。本篇将帮助总结之前的主题,并将其用于完整的处理和建模周期。请查看之前在Towards Data Science发布的文章:

一个例子

本文的目的是展示拉曼光谱数据分析和建模的典型工作流程。为此,我们生成了一个基于三个不同组件的合成拉曼光谱。为了增加真实感,添加了不同类型的噪声。应用了最常见的预处理步骤以恢复干净的光谱。最后,使用经典的最小二乘法来估计三个不同组件的浓度。

本笔记本因此分为三部分:

  1. 合成光谱生成

  2. 光谱的预处理

  3. 应用经典最小二乘法计算光谱中不同组件的量

让我们首先加载将要使用的库。

# Loading the required packages:
import numpy as np
import matplotlib.pyplot as plt
from scipy import sparse
from scipy.sparse.linalg import spsolve
from scipy.signal import savgol_filter, general_gaussian
import sklearn.linear_model as linear_model

合成数据生成

对于这个实际例子,我们将模拟一些拉曼光谱,以便知道预期结果。

  1. 我们将生成三个组件,并按给定比例混合。

  2. 为了让数据更真实,我们将添加噪声:随机噪声、尖峰和基线。

生成由三个组件组成的混合光谱

即使这个话题更复杂,为了这个例子,我们假设信号由高斯峰组成。为此,我们定义一个高斯函数为

def Gauss(x, mu, sigma, A = 1):
    # This def returns the Gaussian function of x
    # x is an array
    # mu is the expected value
    # sigma is the square root of the variance
    # A is a multiplication factor

    gaussian = A/(sigma * np.sqrt(2*np.pi)) * np.exp(-0.5*((x-mu)/sigma)**2)

    return gaussian

我们首先生成这三个组件:

# X-axis (Wavelengths)
x_range =  np.linspace(650, 783, 1024)

# Let's create three different components

# Component A
mu_a1 = 663
sigma_a1 = 1
intensity_a1 = 1

mu_a2 = 735
sigma_a2 = 1
intensity_a2 = 0.2

mu_a3 = 771
sigma_a3 = 1
intensity_a3 = 0.3

gauss_a =  Gauss(x_range, mu_a1, sigma_a1, intensity_a1) + Gauss(x_range, mu_a2, sigma_a2, intensity_a2) + Gauss(x_range, mu_a3, sigma_a3, intensity_a3)

# Component B
mu_b = 700
sigma_b = 1
intensity_b = 0.2

mu_b1 = 690
sigma_b1 = 2
intensity_b1 = 0.5

mu_b2 = 710
sigma_b2 = 1
intensity_b2 = 0.75

mu_b3 = 774
sigma_b3 = 1.5
intensity_b3 = 0.25

gauss_b = Gauss(x_range, mu_b, sigma_b, intensity_b) + Gauss(x_range, mu_b1, sigma_b1, intensity_b1) + Gauss(x_range, mu_b2, sigma_b2, intensity_b2) + Gauss(x_range, mu_b3, sigma_b3, intensity_b3)

# Component C
mu_c1 = 660
sigma_c1 = 1
intensity_c1 = 0.05

mu_c2 = 712
sigma_c2 = 4
intensity_c2 = 0.7

gauss_c = Gauss(x_range, mu_c1, sigma_c1, intensity_c1) + Gauss(x_range, mu_c2, sigma_c2, intensity_c2)

# Component normalization
component_a = gauss_a/np.max(gauss_a)
component_b = gauss_b/np.max(gauss_b)
component_c = gauss_c/np.max(gauss_c)

# How do they look?
plt.plot(x_range, component_a, label = 'Component 1')
plt.plot(x_range, component_b, label = 'Component 2')
plt.plot(x_range, component_c, label = 'Component 3')
plt.title('Known components in our mixture', fontsize = 15)
plt.xlabel('Wavelength', fontsize = 15)
plt.ylabel('Normalized intensity', fontsize = 15)
plt.legend()
plt.show()

三个(已知的)合成组件光谱。[作者提供的图片]。

我们可以基于这三个不同的组件生成一个混合光谱:

# What concentrations we want these components to have in our mixture:
c_a = 0.5
c_b = 0.3
c_c = 0.2

comps = np.array([c_a, c_b, c_c])

# Let's build the spectrum to be studied: The mixture spectrum
mix_spectrum = c_a * component_a + c_b * component_b + c_c *component_c

# How does it look?
plt.plot(x_range, mix_spectrum, color = 'black', label = 'Mixture spectrum with noise')
plt.title('Mixture spectrum', fontsize = 15)
plt.xlabel('Wavelength', fontsize = 15)
plt.ylabel('Intensity',  fontsize = 15)
plt.show()

合成光谱。 [作者提供的图片]。

为了让我们的混合物更加真实,现在我们添加一些噪声:

# Let's add some noise for a bit of realism:

# Random noise:
mix_spectrum = mix_spectrum +  np.random.normal(0, 0.02, len(x_range))

# Spikes: 
mix_spectrum[800] = mix_spectrum[800] + 1
mix_spectrum[300] = mix_spectrum[300] + 0.3

# Baseline as a polynomial background:
poly = 0.2 * np.ones(len(x_range)) + 0.0001 * x_range + 0.000051 * (x_range - 680)**2 
mix_spectrum = mix_spectrum + poly

# How does it look now?
plt.plot(x_range, mix_spectrum, color = 'black', label = 'Mixture spectrum with noise')
plt.title('Mixture spectrum', fontsize = 15)
plt.xlabel('Wavelength', fontsize = 15)
plt.ylabel('Intensity',  fontsize = 15)
plt.show()

添加噪声的合成光谱。 [作者提供的图片]。

我们现在有了一个合成的测量光谱。

光谱的预处理

在我们进行任何建模之前,需要对光谱进行预处理。这些是我们在实际测量光谱时会做的一些工作:清除尖峰、平滑噪声和减去基线。为此,我们将使用一些简单的算法,如下所述。

关于拉曼光谱处理步骤的更完整列表,建议读者查阅 B. Barton 等人的文章(《应用光谱学》76 (9), 1021–1041)或 O. Ryabchykov 等人的文章 (doi.org/10.1515/psr-2017-0043)。

i) 去尖峰光谱

第一步是定位并修正尖峰。为此,我们使用了基于修改后的 z-score 的算法。修改后的 z-score 计算如下:

z(i) = 0.6745 (x(i)-M) / MAD

其中 MAD = median(|x-M|),|…| 表示绝对值,x 是差分光谱的值。

更多信息请参见我之前的 文章 或 这个 jupyter 笔记本

# The next function calculates the modified z-scores of a diferentiated spectrum

def modified_z_score(ys):
    ysb = np.diff(ys) # Differentiated intensity values
    median_y = np.median(ysb) # Median of the intensity values
    median_absolute_deviation_y = np.median([np.abs(y - median_y) for y in ysb]) # median_absolute_deviation of the differentiated intensity values
    modified_z_scores = [0.6745 * (y - median_y) / median_absolute_deviation_y for y in ysb] # median_absolute_deviationmodified z scores
    return modified_z_scores

# The next function calculates the average values around the point to be replaced.
def fixer(y,ma):
    threshold = 7 # binarization threshold
    spikes = abs(np.array(modified_z_score(y))) > threshold
    y_out = y.copy()
    for i in np.arange(len(spikes)):
        if spikes[i] != 0:
            w = np.arange(i-ma,i+1+ma)
            we = w[spikes[w] == 0]
            y_out[i] = np.mean(y[we])
    return y_out

接下来,我们应用去尖峰算法:

despiked_spectrum = fixer(mix_spectrum,ma=10)

并与原始混合光谱进行比较:

尖峰检测。 [作者提供的图片]。

ii) 基线分离

为了计算基线,我们使用了一种基于非对称最小二乘法的基线估计算法,如 Eilers 和 Boelens 在 2005 年论文中所述。

# Baseline stimation with asymmetric least squares
# According to paper: "Baseline Correction with Asymmetric Least Squares Smoothing" 
# by Paul H. C. Eilers and Hans F.M. Boelens. October 21, 2005

# We need the following packages here:
from scipy import sparse
from scipy.sparse.linalg import spsolve

# Baseline stimation function:
def baseline_als(y, lam, p, niter=100):
    L = len(y)
    D = sparse.diags([1,-2,1],[0,-1,-2], shape=(L,L-2))
    w = np.ones(L)
    for i in range(niter):
        W = sparse.spdiags(w, 0, L, L)
        Z = W + lam * D.dot(D.transpose())
        z = spsolve(Z, w*y)
        w = p * (y > z) + (1-p) * (y < z)
    return z

# For more info see the paper and https://stackoverflow.com/questions/29156532/python-baseline-correction-library

基线减法参数:

正如他们在论文中所说,“有两个参数:p 表示非对称性l 表示平滑度。这两个参数都需要根据手头的数据进行调整。我们发现通常 0.001 < p < 0.1 是一个不错的选择(对于有正峰的信号),10² < l < 10⁹。” 参见 Eilers 和 Boelens,2005。

# Parameters for this case:
l = 10000000 # smoothness
p = 0.05 # asymmetry

借助这个算法,我们可以估计基线并将其从测量光谱中减去。

# Estimation of the baseline:
estimated_baselined = baseline_als(despiked_spectrum, l, p)

# Baseline subtraction:
baselined_spectrum = despiked_spectrum - estimated_baselined

# How does it look like?
fig, (ax1, ax2) = plt.subplots(1,2, figsize=(16,4))

# We compared the original mix spectrum and the estimated baseline:
ax1.plot(x_range, despiked_spectrum, color = 'black', label = 'Mix spectrum with noise' )
ax1.plot(x_range, estimated_baselined, color = 'red', label = 'Estimated baseline')
ax1.set_title('Baseline estimation', fontsize = 15)
ax1.set_xlabel('Wavelength', fontsize = 15)
ax1.set_ylabel('Intensity',  fontsize = 15)
ax1.legend()

# We plot the mix spectrum after baseline subtraction
ax2.plot(x_range, baselined_spectrum, color = 'black', label = 'Baselined spectrum with noise' )
ax2.set_title('Baselined spectrum', fontsize = 15)
ax2.set_xlabel('Wavelength', fontsize = 15)
ax2.set_ylabel('Intensity',  fontsize = 15)
plt.show()

基线减法。 [作者提供的图片]。

iii) 平滑

为了平滑光谱,我们使用了在 SciPy 库中实现的 Savitzky-Golay 滤波器。窗口参数(点数)w 和多项式阶数 p 可以针对每组光谱进行优化。

from scipy.signal import savgol_filter, general_gaussian

# Parameters:
w = 9 # window (number of points)
p = 2 # polynomial order

smoothed_spectrum = savgol_filter(baselined_spectrum, w, polyorder = p, deriv=0)

# Some more information on the implementation of this method can be found here:
# https://nirpyresearch.com/savitzky-golay-smoothing-method/

# We plot the mix spectrum after baseline subtraction
plt.plot(x_range, baselined_spectrum, color = 'black', label = 'Baselined spectrum with noise' )
plt.plot(x_range, smoothed_spectrum, color = 'red', label = 'Smoothed spectrum' )
plt.title('Smoothed spectrum', fontsize = 15)
plt.xlabel('Wavelength', fontsize = 15)
plt.ylabel('Intensity',  fontsize = 15)
plt.legend()
plt.show()

噪声平滑。 [作者提供的图片]。

我们最终得到了一个处理过的光谱,可以在其上应用模型。

经典最小二乘(CLS)方法用于定量光谱分析

现在我们已经预处理了我们的混合光谱,可以应用 CLS 来量化其组成。有关 CLS 的更多信息,你可以查看我之前的帖子 这里

query_spectrum = smoothed_spectrum # Let's just rename it

我们首先生成成分矩阵 K

# Generate the components matrix or K matrix
components = np.array([component_a, component_b, component_c]) 

我们可以利用 Scikit-learn 实现 CLS,因此我们导入了该库。

import sklearn.linear_model as linear_model

并应用 CLS 计算浓度

cs = linear_model.LinearRegression().fit(components.T, query_spectrum).coef_
#We print the result:
print('The expected concentrations for components A, B and C are: ' + str(cs)) 

让我们从图形上来看一下:

# Does the result match the original data?

# Plot the original data:
plt.plot(x_range, query_spectrum, color = 'black', label = 'Mix spectrum' )

# Plot the separate components times its calculated concentration:
for i in np.arange(len(cs)):
    plt.plot(x_range, cs[i]*components[i], label = 'c' + str(i)+ ' = ' + str(np.round(cs[i], 3)))

# Plot the result: the sum of separate components times its calculated concentration:
plt.plot(x_range, np.dot(cs,components), color = 'red', linewidth = 2, label = 'Calculation')

plt.title('Mixture spectrum and calculated components', fontsize = 15)
plt.xlabel('Wavelength', fontsize = 15)
plt.ylabel('Intensity', fontsize = 15)
plt.legend()
plt.ylim(ymin = -0.1)
plt.show()

光谱拟合。[作者提供的图片]。

图例中显示的拟合浓度与合成光谱的设定值完全一致。

总结一下,我们已经看到了如何预处理光谱,并应用了一个简单的最小二乘模型来恢复不同的成分及其浓度。这些简单而强大的工具可能会帮助你最大程度地利用你的光谱数据。重要的是,这种方法的不同步骤不仅可以应用于拉曼光谱,还可以应用于任何类型的光谱数据,例如红外光谱、X 射线衍射光谱等。

现在轮到你了,合成不同的光谱,调整不同算法的参数,或将其应用于你自己测量的光谱。

… 如果你有任何问题、评论或建议,请随时通过消息或我的 LinkedIn 账号 联系我nicolascocalopez

Jupyter notebook:

查看完整的 jupyter notebook 这里

参考文献

你可以在我之前的帖子 这里 阅读更多关于去尖峰光谱的内容,以及在 这里 阅读关于经典最小二乘法的内容,或者在我的 GitHub 或 Medium 账户中查看:

原始文献:

  • 修改过的 z 分数去尖峰算法

    Whitaker 等人,《化学计量学与智能实验室系统》第 179 卷,2018 年 8 月 15 日。

  • 使用不对称最小二乘法进行基线校正。

    “基线修正与不对称最小二乘平滑”由 Paul H. C. Eilers 和 Hans F.M. Boelens 著。2005 年 10 月 21 日。

关于如何处理和应用化学计量学于拉曼光谱的完整指南,请参见:

  • Barton, B., Thomson, J., Diz, E. L., & Portela, R. (2022). 拉曼光谱学化学计量学的协调. 应用光谱学, 76(9), 1021–1041.

  • Ryabchykov, O., Guo, S., & Bocklitz, T. (2019). 拉曼光谱数据的分析. 物理科学评论, 4(2).

小公司与大公司中的数据科学

原文:towardsdatascience.com/data-science-in-small-and-big-companies-5cb32be1491a

数据科学家角色的公司规模综合比较

Thomas A DorferTowards Data Science Thomas A Dorfer

·发表于 Towards Data Science ·8 分钟阅读·2023 年 2 月 24 日

--

图片由 Annie SprattChuttersnap 提供,编辑者为作者。

数据科学已成为企业不可或缺的一部分,并渗透到几乎每一个领域,从电子商务到自动驾驶和医疗保健。在每个领域内,数据科学团队都在努力产生有价值的数据驱动洞察,这些洞察可以用来做出明智的业务决策。然而,得出这些洞察的基本过程可能会有很大差异。

在考虑数据科学时,通常会想到一些狭窄的概念,如数据探索、实验、模型训练和数据可视化。然而,数据科学家的角色可以非常多样化,这取决于组织的规模。

本文通过评估一系列标准,包括任务和职责、影响和创新、学习与技能发展、可用资源、指导机会和晋升空间,提供了在小公司与大公司中担任数据科学家的情况概述。

应当注意的是,这里讨论的标准会因公司而异,也会依赖于公司内数据科学部门的成熟度。

任务和职责

在较小的公司中,数据科学家的任务性质和职责范围可能会因当前数据科学部门的成熟程度而有很大差异。

如果公司刚刚成立或仅由少数人组成,你可能会发现自己作为一个全能型人才,负责建立包括数据收集、处理、清理、探索、模型训练、部署、监控等在内的整个基础设施。在某些情况下,职责还可能包括积极参与并实施公司的数据驱动策略,比如识别新的商业机会或提高运营效率。

从积极的一面来看,在较小的组织中,个人工作的可见性通常更高,数据科学家往往享有更大的自主权和项目所有权。然而,这也带来了更多的责任和问责。

当我在一家由大约 30 名员工组成的小型生物技术初创公司工作时,我直接向公司的 CTO 汇报工作。这当然是一个让我的工作得到关注并因成功和及时交付功能而获得认可的绝佳方式。然而,C 级高管的期望往往很高,这增加了数据科学家在紧张时间表下交付结果时所面临的压力。

Desola Lanre-Ologun拍摄,Unsplash

相比之下,在大型组织中,数据科学家的角色往往更加狭窄和专业化。例如,如果你加入了苹果公司的 Siri 团队,你很可能会在自然语言处理领域内的一个小众领域工作。同样,加入一个专注于微软 Teams 的数据科学部门可能会要求你成为噪声抑制或异常检测等领域的专家。

在大公司工作的一大优势是你能够获得来自协作和领域知识的支持。大型公司通常拥有更多经验丰富和多样化的团队,能够提供宝贵的支持和专业知识。从更广泛的范围获取信息可以帮助数据科学家对业务的基本面有更全面的理解,这不仅提供了绝佳的学习机会,还帮助识别数据驱动的解决方案,以解决公司内部的低效问题。

Dylan Nolte拍摄,Unsplash

影响与创新

在较小的公司中,数据科学家通常对业务的整体成功有更显著的影响,因为他们的工作可以直接影响决策和战略。然而,由于缺乏现成的资源,如计算能力或领域知识,数据科学家可能需要更具创造性和高效地工作以产生有影响力的解决方案,因此需要展现出更大的多样性和适应能力。

较小公司的一个显著优势是更快的决策过程。如果你有一个新的创新想法,由于组织层级较小,获得领导层的批准和执行通常会发生得更快。

另一方面,在大公司中,由于组织的规模和复杂性,数据科学家可能对业务结果的直接影响较小。然而,他们可能会有更多的资源和基础设施来存储、处理和分析数据,这可以使他们能够处理更复杂和雄心勃勃的项目。此外,大公司中的领域知识和专业技术的增加可以创造更多的合作和创新机会,使数据科学家能够与其他专家一起工作,开发先进的解决方案。

学习与技能发展

在较小的公司中,数据科学家需要熟悉整个数据科学工作流程,这提供了一个发展广泛且全面技能的绝佳机会。这种对数据科学周期各个方面的接触进一步使他们能够确定哪些领域特别与他们产生共鸣,从而可以利用这些信息做出更明智的职业发展决策。

图片由肯尼·艾利亚森拍摄,发布在Unsplash

相比之下,大公司中数据科学家通常专注于特定的细分领域,这可能会限制他们技能的广度,但会在其专注领域内积累相当深入的知识和专业技能。他们还可能从更多的公司资源中受益,例如专门的培训计划或免费的在线学习平台,如 LinkedIn Learning、Coursera 或 Pluralsight。例如,在微软,我们还有一个名为 MLADS(机器学习、人工智能和数据科学)的半年内部会议,其中分享了广泛领域的最前沿技术。

可用资源

虽然所有规模的公司通常都会面临资源限制,但在小型组织中这些限制往往更为明显。这些限制可能包括较小的数据集、有限的计算能力、较少的专用工具和软件,以及较少的学习和成长机会,表现为有限的培训项目、学术会议资金和公司内部的领域知识来源。这使得数据科学家进行大规模分析和处理需要大量领域知识、数据和计算能力的复杂项目变得更加困难。

例如,在我的创业公司任职期间,我们只有两个 GPU,这大大限制了模型的容量,增加了训练时间,并限制了我们进行大规模实验的能力。因此,我们不得不投入大量时间和精力寻找更高效的解决方案,有时也会推迟特性交付的初始日期。

大型公司的情况通常相反。他们往往为数据科学家提供访问大型数据集、更多计算能力以及各种工具和软件,这些资源使他们能够以多种不同方式实现解决方案。效率仍然重要,因为它可以减少延迟并带来成本节省;然而,与小公司相比,它通常不是首要关注点。此外,大型公司往往拥有更多的资金用于通过内部培训项目、训练营、在线学习资源或学术会议来支持学习和职业成长。

导师机会

导师指导是职业发展的关键方面,能够帮助你朝正确方向发展,并在决定下一步职业时做出更明智的决策。

由于较小公司的数据科学家和高级员工数量有限,他们可能无法始终提供导师机会,尤其是技术方面的。例如,我在创业公司工作期间的数据科学部门只有三个人,这意味着为了获取技术信息,我们不得不查阅科学文献,并想办法将论文中的新方法转化为代码。然而,另一方面,你可能会发现自己有更多直接接触高级主管甚至创始人的机会,他们仍然可以在业务运营和战略方面提供宝贵且更高层次的建议。

照片由Monica Melton拍摄,发布在Unsplash

与此相比,许多大型公司拥有完善的指导计划,将数据科学家与经验丰富的导师配对,并提供技术技能发展、职业成长和领导力方面的指导。例如,在微软,员工可以选择多种受到高度欢迎的指导计划。我自己也一直在接受积极的指导,这是一段极具回报的经历,不仅帮助我解决了技术问题,还让我学到了更多关于数据科学家职业发展的知识,并参与了关于该领域最新趋势的激动人心的讨论。

然而,根据我自己和同事们的经验,找到一位在特定领域拥有相关专业知识的合适导师仍然具有挑战性,特别是在数据科学家数量不断增长的背景下。

向上流动性

在小型公司中,向上流动性高度依赖于高级职位的可用性以及数据科学部门的成熟度。如果后者仍处于起步阶段,那么凭借合适的技能和经验,更快地被分配到高级职位的可能性可能会增加。这样,你有更多机会承担更大的项目所有权和责任,这可以直接影响公司方向的塑造。

图片由Hunters Race拍摄,来源于Unsplash

相反,大型公司往往为数据科学家提供更明确的职业发展路径,概述了晋升到下一级所需满足的具体标准。虽然它们可能提供更广泛的机会,让你在数据科学的特定领域专注或承担更多责任,但大型公司的等级结构可能使得对公司方向产生显著影响变得更加困难,晋升可能更多地依赖于满足一些预定义的标准,而不是承担更广泛的职责。

结论

我们已经看到,小型组织通常要求数据科学家对整个数据科学周期有基本的理解,而在大型公司中,角色通常更专注于特定的细分领域。尽管所有公司都面临资源限制,无论其规模大小,这种限制在小型公司中往往更为明显,可能会限制对大型数据集、计算资源、软件和职业发展计划的访问。与高级管理人员的直接接触在小型公司中无疑是一个优点;然而,这可能会以技术指导机会的减少为代价,因为相关专业知识的员工较少。

作为数据科学家,决定加入小公司还是大公司最终是一个个人决定,这应该基于你的职业目标、优先事项和偏好。我希望这篇文章能为你提供一些指导,并让你对在小公司和大公司担任数据科学家的情况有一个总体了解。

数据科学不是科学

原文:towardsdatascience.com/data-science-is-not-science-bb95d783697a?source=collection_archive---------7-----------------------#2023-01-17

如何在数据科学分析中融入科学过程

Conor O'SullivanTowards Data Science Conor O'Sullivan

·

关注Towards Data Science上发布 · 8 分钟阅读 · 2023 年 1 月 17 日

--

图片由Greg Rakozy提供,发布于Unsplash

商业科学、运动科学、食品科学……我们喜欢在其他词汇后面加上“科学”这个词。这让这些领域听起来,更加科学。然而,如果我们不小心,它们会与伪科学有更多的共同点。

数据科学也没有什么不同。

表面上看,它可能接近科学。两者都是寻求知识的过程。从本质上讲,机器学习也是一个重复的过程。我们不断收集新数据并尝试改进之前的结果。然而,在许多情况下,数据科学偏离了科学过程。

了解为什么我们讨论:

  • 科学过程

  • 数据科学与科学不同的方面

  • 我们如何使数据科学更接近科学

科学是我们理解自然世界的最佳工具。它可能在不同的条件下操作于数据科学,但我们仍然可以采用其过程。这样做将导致更可靠的分析。

在你继续阅读之前,你可能对这个话题的概述感兴趣:

什么是科学?

科学涵盖了大量的人类活动。大致来说,它既包括我们已经知道的东西,也包括我们知道这些东西的方法。后者被称为科学过程。这是一种获取新知识的系统方法论。该系统中最重要的部分是假设。

科学可以被看作既是知识的体系,又是获取新知识的过程(通过观察和实验——测试和假设)。

— 澳大利亚科学院

假设

假设是一种关于事物运作方式的有根据的假设或猜测。例如,你可能睡得不好。一个好的假设是:“我睡前喝的双倍浓缩咖啡让我无法入睡。”这是一个好的假设,因为它既可测试又有证据支持。

可测试性

事实上,假设必须是可测试的。这意味着我们能够设计一个实验来提供数据。对这些数据的分析将支持或反驳假设。我们上面的假设是可测试的。你可以简单地在睡前停止喝咖啡,然后观察你的睡眠情况。

证据

在科学中,我们永远不能以 100%的确定性宣称一个假设是真的。我们可以通过更多来自多个独立实验的证据逐渐接近这一目标。要宣称咖啡对睡眠有负面影响,我们需要比一次实验更严格。这需要多个人、不同的咖啡因水平、双盲测试等……

这就是科学过程——做观察,制定假设,设计实验,收集证据,得出结论并重复!这是我们获取新知识的最佳工具。然而,它不是唯一的工具。

为什么数据科学不是科学

数据科学也寻求获取新知识。正如我们所提到的,它以偏离上述过程的方式进行。在我担任数据科学家期间,我遇到过这方面的例子。

数据讲述

首先是数据讲述。实际上,这就是以一种不会让非技术观众感到乏味的方式重新框定分析。我们使用漂亮的图表和相关的故事来说服他们我们的结论是正确的。

数据讲述的概念是基于复杂的数据和分析构建一个引人入胜的叙述,帮助讲述你的故事并影响和告知特定的观众。

— 微软(Power BI)

数据讲述的一个关键部分是建立对模型的信任。我们需要说明为何使用某些特征或这些特征的参数。换句话说,我们需要解释模型捕捉的潜在关系。

问题是,这些理由是在模型构建后才被想到的。我们从数百,甚至数千个特征开始。经过多次迭代,我们将这些特征缩减到最终集。只有到那时,我们才会为将这些特征纳入模型提供令人信服的商业理由。这正好与假设检验相反。

黑箱模型

在某些情况下,我们甚至不关心模型是如何做出预测的。我们只关心它们做出预测的效果如何。算法被视为黑箱,我们没有产生新的知识。这与科学的最终目标相悖。

机器学习的回音室

更糟糕的是,这种方法可能会从行业的数据科学转到学术界。也就是说,机器学习研究者往往会创造自己的生态圈。研究的目标甚至整个社区的目标是提升在基准数据集上的表现。性能改进的递减被视为科学贡献,原始的研究问题则成为次要考虑。

一个例子来自我自己的研究。我的硕士论文研究了使用机器学习和自然语言处理技术预测法律案件的结果。这旨在帮助欧洲人权法院(ECHR)提高判决速度。

我的唯一目标是提高现有方法的准确度。回顾过去,这实际上对 ECHR 并没有帮助。法官会相信模型的结果吗?

与其挤出额外的 1%准确度,我本可以解释模型或提供更深入的结果分析。这样法院可以用来辅助决策,而不是完全取代决策。

快速决策

最后的原因与环境差异有关。学术界有重新测试、收集新数据集或甚至以全新的方式测试假设的特权。工业界则没有这种特权。你需要立即做出决策,基于你所拥有的任何证据。

这引出了一个问题,数据科学是否应该更像科学?通常答案是否定的。用与科学假设相同的严谨度来处理所有商业问题会适得其反。在做出决定所需的时间内根本无法创造出多条证据链。

数据科学如何更像科学

然而,与此同时,科学仍然是我们揭示真相的最佳方法。将数据科学更接近科学可以使我们的结果更可靠。那么,我们可以通过哪些方式来实现这一目标呢?

融入领域知识

与非技术同事提前讨论期望是很重要的。这可以包括数据中的趋势或你期望哪些模型特征是重要的。这些可以作为非正式假设。

你的分析将提供支持或反对这些期望的证据。如果你发现相反的证据,现在你不仅仅需要对观察到的趋势提供令人信服的理由。你还需要解释为何现有的期望是错误的。这将增加你理由的负担,使其更具可靠性。

使用多个指标和可视化

在提供证据时,不要仅仅依赖一个指标或可视化,因为它们都有自己的局限性。数据科学家们已经这样做了。在评估模型性能时,我们不仅仅看准确性。我们使用精确度、召回率,甚至通过 ROC 曲线来可视化性能。

进行分析时也应采取相同的方法。例如,假设我们想评估某人的财务状况。我们可以计算他们前一年的总收入。这会告诉我们很多,但一个数字不足以完全捕捉他们的状况。我们还需要查看开支和现有债务。除了总数外,我们还可以查看月度变化。

这类似于为一个结论提供多条证据。尽管这并不等同于收集新数据或重新运行实验。我们仍然使用一个基础数据集。所有指标都将受到该数据集中的偏差的影响。我们只是去除了由指标本身引入的偏差。

预先定义成功指标

更进一步,我们可以正式定义指标及其截断值。也就是说,我们将在分析中使用哪些指标,以及哪些值将被视为成功。这是为了防止你挑选支持先入为主观念的指标。

一个例子来自下面关于算法公平性的文章。这里我们提供了公平性的不同定义。一种定义,平等机会,通过比较特权组和非特权组的假阳性率(FPR)来工作。如果 FPR 的差异在某个截断值之内,则模型被认为是公平的。

## 机器学习中的公平性分析(使用 Python)

进行探索性公平性分析,并使用平等机会、平等化概率和差异化...

towardsdatascience.com

假设我们没有定义截断值或选择公平性的定义。在计算 FPR 差异后,我们可以争论一个更高的截断值。如果差异显著,我们甚至可以争论使用完全不同的定义。面对几个月的建模工作,这可能是诱人的。

可解释的机器学习

我们还可以努力不仅仅使用这些评估指标。这就是 IML 的作用。它旨在构建可以被人类理解的模型。像 SHAP、LIME、PDPs 和 ICE 图这样的 IML 方法允许你窥视黑箱,了解它的工作原理。

使用这些方法使我们更接近科学的目标——理解我们的自然世界。我们从了解模型预测的效果到了解它们如何做出这些预测。在这个过程中,我们可以学到一些关于我们数据的新知识。

[## 从黑箱模型中可以学到什么

使用非线性模型进行数据探索和知识生成

了解我们可以从黑箱模型中学到什么

对你的结论保持自信

直到现在,我们讨论了与数据科学相关的问题。然而,许多问题实际上是由于其最佳实践的曲解造成的。数据科学家往往不是做出决策的人。你是负责提供基于数据的信息,以支持决策的人。你应该努力提供尽可能可靠的信息。

在组织中工作时,这说起来容易做起来难。你会处理多个利益相关者,他们可能对决策有不同的偏好。无论是想让模型上线的经理,还是希望销售更多保险的业务主管,都会对你的结论施加压力。因此,为了避免从 DS 变成 BS,你需要坚定立场,依靠数据所告诉你的信息。

数据科学可以通过许多方式变得更像科学。我们没有涉及的一点是科学的协作性质。我认为数据科学家已经很擅长这一点。无论是分享 git 代码、Kaggle 上的数据集还是 Medium 上的文章,我们都喜欢分享我们的想法。也许我们可以对这种做法稍微更具科学性。

希望你喜欢这篇文章!你可以通过成为我的推荐会员来支持我😃

## 使用我的推荐链接加入 Medium — Conor O’Sullivan

作为 Medium 会员,你的一部分会员费用会分给你阅读过的作者,你将可以完全访问每一个故事……

加入 Medium,使用我的推荐链接 — Conor O’Sullivan

| Twitter | YouTube | Newsletter — 免费注册以获得 Python SHAP 课程

参考资料

伯克利,科学理解 101 undsci.berkeley.edu/understanding-science-101/

科学委员会,我们对科学的定义sciencecouncil.org/about-science/our-definition-of-science/

澳大利亚科学院,什么是科学? www.science.org.au/curious/people-medicine/what-science

可汗学院,科学方法 www.khanacademy.org/science/biology/intro-to-biology/science-of-biology/a/the-science-of-biology

微软 Power BI,什么是数据讲述? powerbi.microsoft.com/en-us/data-storytelling/

数据科学项目通过 KPI 达成精通

原文:towardsdatascience.com/data-science-project-mastery-through-kpis-adfdfe8fde1d

在数据科学项目控制中使用关键绩效指标

Janik 和 Patrick TinzTowards Data Science Janik 和 Patrick Tinz

·发表于 Towards Data Science ·10 分钟阅读·2023 年 5 月 17 日

--

照片由 Alexandr Podvalny 提供,来源于 Unsplash

项目控制 在数据科学项目中至关重要,因为所需的时间和成本往往难以估算。客户希望通过机器学习方法高效、成功地实施他们的用例。通过有意义的 关键绩效指标 (KPIs),你可以积极影响一致的项目控制。根据一些 研究,85% 的数据科学项目失败,这需要早期识别障碍。

在传统的 IT 项目中,存在许多用于项目控制的 KPI,但这些 KPI 对数据科学项目来说是不够的。因此,本文介绍了 IT 项目的基本 KPI,并针对数据科学项目进行分析。结果表明,特定的 KPI 可以使数据科学项目的项目控制更加透明。

本文首先处理项目控制的基础知识,并介绍了一些来自软件行业的 KPI。接着,我们使用这些 KPI 并根据数据科学项目使用 CRISP-DM(CRoss-Industry Standard Process for Data Mining)过程模型进行分析。分析还包括来自数据科学项目的个人项目经验。

如果你想了解更多关于 CRISP-DM 的内容,我们推荐阅读我们的 CRISP-DM 文章

项目控制

项目控制是项目管理的核心组成部分,用于支持项目决策。其目标如下(参见[1],第 334 页):

  • 项目目标协调

  • 支持项目管理,识别计划中的偏差及其原因

  • 风险评估和采取措施以控制或降低风险。

项目控制人员使用各种工具来控制项目,包括仪表盘。IT 项目通常根据项目订单进行管理。项目描述通常定义了项目的范围以及客户方期望的项目目标。然而,项目描述往往只是粗略的描述,因此需要进一步细化。最后,我们需要推导出具有里程碑的路线图以及资源和成本规划。里程碑在项目控制中至关重要,因为它们决定了审查的时间,从而有助于对整体项目的工作状态进行分类。

支持项目控制的 KPI 是本文的主要焦点。经验丰富的项目经理通常依赖于明确且易于解释的 KPI。

在数据科学项目中的项目控制时,了解机器学习的基本知识是很重要的。

在下一节中,我们将介绍一个概念,用于推导 IT 项目的有意义的 KPI。

平衡计分卡概念

概念平衡计分卡(简称:BSC)在实践中已被证明是一种全面的 KPI 开发方法。BSC 是一个平衡的 KPI 系统。该概念的核心是对成功现象的具体化以及对非货币指标的整合。(参见[1],第 374 页)。该概念考虑了以下四个视角(参见[1],第 375 页):

  • 财务: 我们需要考虑哪些财务方面?需要哪些关键指标?

  • 客户: 客户对项目的期望和态度是什么?

  • 内部过程: 内部过程在成本、时间和质量方面的效率如何?

  • 学习和发展: 员工的部署是否有针对性?

这四个视角构成了平衡计分卡的框架,但它们也可以扩展,例如,通过项目成果。(参见[1],第 374 页)

首先,我们需要为项目推导出视角。然后,我们可以最终定义项目目标和需求。随后,我们为平衡计分卡的每个视角推导出以下协议(参见[1],第 376 页):

  • 项目在各自视角下追求的目标的定义。

  • 关键指标的分配

  • 每个关键指标的目标定义

  • 偏差情况下的措施

最后,我们可以根据关键成功因素和战略目标,为每个视角推导出关键指标。

从财务角度来看,关键成功因素包括项目预算、项目成本和项目收益(IT 项目的 ROI)。从客户角度来看,关键成功因素包括客户满意度、利益相关者对项目工作的接受度以及客户对软件解决方案的使用情况。从内部流程角度来看,关键成功因素包括项目进展(里程碑)、活动的生产力和流程的创新支持。从员工角度来看,关键成功因素可以包括项目员工的满意度、项目员工的资格和团队发展。

关键绩效指标

在本节中,我们为每个角度定义了一些重要的 KPI。

财务:

KPI:预算合规性(参见[1],第 378 页)

  • 目标:偏差低于 10%

  • 单位:百分比

  • 公式:实际预算/目标预算

KPI:投资回报率(项目的 ROI)(参见[1],第 381 页)

  • 目标:透明地呈现和沟通 IT 项目的好处。

  • 单位:绝对数

  • 公式:根据效用价值分析(标准 x 权重)确定

客户:

KPI:客户满意度指数(参见[1],第 381 页)

  • 目标:满意度指数至少为 98%

  • 单位:百分比

  • 公式:客户调查结果

内部流程:

KPI:项目中的截止日期遵守情况(参见[1],第 382 页)

  • 目标:遵守 99%的约定截止日期

  • 单位:百分比

  • 公式:实际项目持续时间/预测项目持续时间

KPI:项目计划偏差(参见[2])

  • 目标:按计划完成项目

  • 单位:绝对数

  • 公式:实际值 - 计划值

KPI:实现值(参见[2])

  • 目标:实现积极的实现值

  • 单位:绝对数

  • 公式:计划小时数 - 实际支付小时数

学习和发展:

KPI:员工满意度指数(参见[1],第 381 页)

  • 目标:确保项目员工的高满意度

  • 单位:百分比

  • 公式:员工调查结果

在本节中,我们讨论了一些经典 IT 项目中的 KPI。这个列表并不完整,因为每个 IT 项目都有不同的 KPI。接下来,我们将分析数据科学项目中的 KPI 并引入新的 KPI。

分析

在本节中,我们分析了在数据科学项目中与经典 IT 项目相比可以使用哪些 KPI。此外,我们还介绍了数据科学项目的额外 KPI。数据科学项目通常按照 CRISP-DM 过程模型进行。因此,我们利用该过程模型分析在数据科学过程中哪些 KPI 可以在什么阶段使用。

CRISP-DM 过程模型的阶段(见图)包括业务理解、数据理解、数据准备、建模/评估和部署。

CRISP-DM 过程模型(见[3])

业务理解

在数据科学项目的开始阶段,通常不清楚数据分析对客户的潜在价值。第一步是找到合适的使用案例。在实践中,项目开始时通常会举行初步研讨会,以识别具有高商业潜力的使用案例。还应该注意,问题和数据必须相匹配。在这种情况下,数据科学家应当提前参与,因为他们可以基于可能的数据来源更好地评估潜在价值。一旦找到使用案例,就可以确定用于衡量项目成功的 KPI。在这个过程中,必须考虑不仅是经典的控制 KPI,还包括实际业务价值(ROI)的测量,例如将机器停机时间减少 30%。在这个阶段,应明确我们希望实现的效用价值。在此阶段,我们定义了使用案例及相关目标。目标通过 KPI 的定义变得可衡量。因此,目标也应与要开发的机器学习模型相关联。您可以使用各种指标(如准确率、精确率召回率、F1 分数、AUC 分数、混淆矩阵或提升因子)来控制预期的模型质量。在这个阶段,您可以设定一个最低要求。本文中将这些 KPI 称为数据科学 KPI,并可以将其与经典 KPI 一起使用。

数据理解

数据基础为成功的数据科学项目提供了基础。在这一步中,数据科学家必须获得对数据的概览并评估其质量。但什么是数据质量?数据质量描述了数据与使用案例的适配程度。数据科学家通过正确性、相关性、完整性、一致性和可用性等标准来检查数据质量。数据科学家的重点应该始终放在数据质量上,而不仅仅是数据数量。在数据科学家进行初步评估后,通常会进行工作量和预算规划的估算。

数据准备

在数据准备阶段,我们从原始数据中创建一个用于分析的最终数据集。数据准备占据了整个项目持续时间的约 50–70%。因此,调整项目控制以应对此是非常重要的。在数据准备期间,与客户的紧密沟通是必要的,以便在数据准备中考虑领域特定的方面。必须明确如何处理缺失值或异常值。在此阶段,应特别关注 KPI 的实现值,因为这一阶段的工作量通常非常高。应确保花费的时间不会超过总小时数的某个上限,例如 60%。然而,您必须为项目具体选择这个上限。此外,您还应确保遵守项目计划(KPI 项目计划偏差)。

建模 / 评估

在建模阶段,选择并实施适当的分析方法。分析方法可以是简单的统计程序或复杂的机器学习程序。随后,评估分析结果并与目标进行比较。这些阶段是迭代的,因为你可以随时返回建模阶段以根据评估指标调整模型。这两个阶段作为可行性研究。在这一点上,可以明确数据科学项目是否实现了设定的目标。在此阶段,你应关注业务理解中定义的数据科学 KPI。

部署

在这个阶段,当公司具有运营附加值时,开发的模型会部署在 IT 基础设施中。部署是长期有效利用数据的关键!

CRISP-DM 过程模型显示,数据科学项目中存在其他多个 KPI。在数据科学项目中,模型评估结果对项目的成功至关重要。

讨论分析结果

在本节中,我们根据分析结果和个人经验讨论以下论点:

“传统的 IT 环境中的关键绩效指标在数据科学项目中是不够的。”

目前,媒体将数据科学描述为一种可以解决许多问题的“灵丹妙药”。这也是许多服务公司希望在这一领域进一步发展的原因。服务提供商宣传他们在数据科学领域的能力,并与潜在客户展开谈判。首先,服务提供商进行潜力分析。潜力分析显示项目是否为客户带来附加值。如果潜力分析成功,数据科学项目将启动。根据我们的经验,许多数据科学项目使用 CRISP-DM 过程模型。经验丰富的 IT 项目经理通常会领导这些项目。项目经理将项目作为传统 IT 项目进行管理。项目经理使用 KPI 进行项目控制,并且在这些方面有良好的经验。

根据我们的经验,这些项目可能会成功,也可能会失败。原因在于项目经理通常对特定的数据科学 KPI 缺乏理解。我们认为,特定的数据科学 KPI 可以使项目对所有利益相关者更加透明。定义可量化的目标至关重要,以便能够衡量数据科学项目的成功。根据我们的经验,如果项目经理已经对数据科学有所了解,这也是有帮助的。如果是这样,沟通和控制将更加透明。此外,将数据科学项目分阶段进行也是有用的。你可以使用 CRISP 过程模型,这样可以清楚地知道你在项目中的位置。

分析显示,数据科学项目中应使用 IT 环境中的 KPI。例如,预算合规性和项目效益(ROI)。定义 ROI 是至关重要的,这样你才能衡量项目的实际收益。根据我们的经验,快速实现一个原型通常会非常有帮助,这个原型能够令人印象深刻地展示 ROI。这个原型让客户更加放松,所有方面的优化动力也随之增加。此外,它创造了一个更好的工作氛围,提高了员工满意度指数。然而,我们也从以往的项目中学到,不应将数据科学项目管理得像经典的 IT 项目一样。其他因素也起着作用。你必须将数据科学 KPI 融入到控制流程中。数据科学 KPI 确保了开发过程的更大透明度。此外,项目经理应该熟悉数据科学 KPI,以确保数据科学项目的成功。我们同意这个观点,因为我们在数据科学项目中必须使用特定的数据科学 KPI。

结论

本文通过 CRISP-DM 过程模型说明了在数据科学项目中使用 KPI。KPI 的使用在数据科学和 IT 项目中都是至关重要的。此外,对于数据科学项目来说,如果项目经理具有数据科学背景则非常有帮助。在数据科学中,尤其是与模型质量相关的新 KPI 也出现了。

最后,值得注意的是,数据科学项目中的项目经理仍处于寻找最佳 KPI 的阶段。项目经理的经验和成功的项目实施将确保出现更多可以有助于数据科学项目成功控制的 KPI。

👉🏽 加入我们的免费每周 Magic AI 通讯,获取最新 AI 更新!

👉🏽 你可以在我们的数字产品页面找到所有免费的资源!

免费订阅 以获取我们发布新故事的通知:

[## 订阅以获取 Janik 和 Patrick Tinz 发布的最新邮件。

订阅以获取 Janik 和 Patrick Tinz 发布的最新邮件。通过注册,如果你还没有 Medium 账户,将创建一个账户…

tinztwinspro.medium.com

在我们的关于页面了解更多关于我们的信息。不要忘记在X上关注我们。非常感谢阅读。如果你喜欢这篇文章,欢迎分享。祝你有美好的一天!

使用我们的链接注册 Medium 会员,阅读无限制的 Medium 故事。

参考文献

  • [1] Ernst Tiemeyer. 《IT 项目管理手册:方法模型、管理工具、最佳实践》。Carl Hanser Verlag GmbH Co KG,2018 年。

  • [2] Oliver Brüggen. 3 个项目管理 KPI,您应该测量的指标。2018 年。

  • [3] Wirth, R. 和 Hipp, J., 2000 年 4 月. CRISP-DM: 朝着数据挖掘标准过程模型迈进。见于第四届国际知识发现与数据挖掘应用大会论文集(第 1 卷,第 29–39 页)。

数据科学团队的顶层设计

原文:towardsdatascience.com/data-science-team-topologies-b6844d4e2fa4?source=collection_archive---------10-----------------------#2023-02-03

数据产品开发如何与软件开发不同

Mark DerdzinskiTowards Data Science Mark Derdzinski

·

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

--

图片来源:Alina Grubnyak via Unsplash

今天,我们将探讨数据科学团队的顶层设计以及它们如何与典型的软件团队不同。我们的论点有多准确?跟随我,我们来深入探讨这个问题。

构建数据科学团队与传统的软件开发团队有几个关键的不同。从团队成员的角色和职责到所使用的工具和流程,数据科学团队需要独特的方法以确保成功。我们将探讨在构建数据科学团队时需要考虑的一些因素,包括跨职能协作的重要性、专业技能以及数据科学过程中的实验和迭代。如果我们到最后能够说服自己,我们将发现一些宝贵的见解和最佳实践,帮助你应对构建数据科学团队的复杂性。

数据驱动,而非代码驱动

在数据科学中,问题很少围绕特定的技术或编程语言,更多的是围绕数据本身。因此,数据科学团队的组成可能与传统的软件团队大相径庭。多样化的技能和观点在处理数据时是一个优势。这就是为什么跨学科团队,拥有各种背景和领域专长的专家,往往会取得成功。此外,一组能够以新视角解决问题的通才,可以带来宝贵的见解并推动创新。

图片由 Luke Chesser 提供,来自 Unsplash

在分析追求和数据产品设计中,数据科学团队也往往采取更具探索性和迭代性的解决问题的方法。因此,数据产品和分析项目通常具有不确定性和对明确解决方案的需求。数据科学家必须不断地筛选大量数据以识别模式和见解,并且可能需要尝试多种不同的方法才能得出最终解决方案。这种迭代过程可能非常耗时,并且使得科学家或领导者很难准确预测团队何时完成某些项目。因此,数据科学团队可能需要比传统的软件团队更多的灵活性和自主性。

知道就是知道自己一无所知

数据科学团队的拓扑结构与典型的软件团队拓扑结构从根本上有所不同:它们基于实验和探索。与某些软件团队可能拥有明确的需求和定义的路线图不同,数据科学家通常负责在大量数据中发现见解和识别新机会。这种模型需要不同的团队合作方式,强调灵活性、适应性和愿意尝试新事物。

在一个高效运作的数据科学团队中,个人贡献者和领导层应该对不确定性和模糊性感到舒适。他们应该能够在实验没有产生预期结果时迅速调整,并愿意冒险尝试新的方法。与传统的功能开发团队(其目标可能包括在特定时间表上交付特定产品)不同,数据科学团队专注于发现洞察和进行发现,工作时间表不可预测。成功的数据科学团队的一个重要方面是能够以迭代和探索的方式工作,愿意进行实验和尝试新事物,同时持续在数据中寻找新的机会和洞察。

地图不是领域

数据科学团队的拓扑结构通常与典型的软件团队不同,因为它们需要对其所处领域有深入的了解。这种专业知识对构建具有影响力的数据产品至关重要,这些产品能够真正解决问题并为最终用户提供价值。有了领域专长,数据科学家可以避免陷入优化虚荣指标或构建会加剧数据偏见的模型的陷阱。

照片来源于 Marjan Blan | @marjanblan 在 Unsplash

对问题领域的全面理解意味着不仅要了解问题的技术方面,还要考虑到业务背景、用户需求和伦理影响。通过使用全面的方法,数据科学团队可以将其工作与组织的整体目标对齐,并优化所期望的结果。这种方法不仅有助于减少数据产品中的偏见,还可以避免在优化实验结果或模型性能时的狭隘视角,以实现长期的业务目标或用户行为。

为了通过数据产品产生更大的影响和创造价值,数据科学团队(特别是那些开发面向用户的数据产品的团队)应该是“流对齐”的,并对其工作的最终结果或用户影响负责。这些团队对问题领域和期望的结果有深入的了解,与最终用户紧密联系,并对其产品的影响和表现负责。此外,他们还与支持团队和产品利益相关者合作,负责确保他们的数据产品随着最终用户需求的变化而不断发展。通过采用全面的数据科学方法并专注于结果(而不仅仅是交付数据或模型),数据科学团队可以更好地响应用户需求,发展更强的目标感,并创造适应市场变化和用户需求演变的产品。

不同的操作模型

传统 DevOps 功能所需的结构和技能与数据科学组织所需的 DataOps 或 MLOps 技能不同。虽然经典的 DevOps 团队专注于部署和维护软件所需的基础设施和操作,但 DataOps 和 MLOps 团队还必须考虑管理和部署机器学习模型的独特需求。这种细微差别通常强调数据科学家、工程师和运维专业人员之间的协作,以确保模型得到部署并持续监控、测试和更新,以满足用户(不断变化)的需求。支持生产数据产品的运维还需要了解问题领域,以确保所部署的产品以正确的准确性和公平性运行,同时仍能解决它们旨在解决的业务问题。

数据产品的康威定律

任何设计系统(广义上定义)的组织都会产生一个设计,其结构是组织沟通结构的副本。——梅尔文·E·康威

创建一个新的数据产品不仅仅涉及数据科学家,还包括来自组织其他部分的利益相关者。这种协作意味着数据科学团队的沟通架构必须设计成能够容纳这些不同的利益相关者及其不同的观点。这可能与某些软件团队的沟通架构大相径庭,后者的重点可能是团队内部的沟通。数据科学家还必须能够将他们工作的结果传达给非技术利益相关者,如业务领导,并帮助他们理解开发的数据产品的影响。要求数据科学家深入理解问题领域并具备强大的沟通技能,最终会影响如何将数据科学家或团队嵌入到组织中。

面向服务的结构

本着健壮性的精神,让我们承认生活是复杂的,尝试反驳我们自己的观点。然后,退一步,问自己:“数据科学团队结构何时最接近典型的软件开发?”

图片由 Markus Spiske 拍摄于 Unsplash

一个例子是,当一个团队将数据科学功能作为服务提供时。例如,用于实验的平台或被多个下游产品使用的模型可能需要类似于典型软件开发团队的结构和方法。在这种情况下,团队可能会专注于构建和维护支持实验和模型开发过程的基础设施、工具和流程。他们还可能负责确保模型的质量和性能,以及为使用平台或模型的其他团队提供文档和支持。

然而,虽然在这些情况下,开发或维护的结构和方法可能相似,但所需的技能集可能会有所不同。提供功能即服务的数据科学团队仍需要对特定领域有深入的理解,通常还需要具备强大的协作和沟通能力,以便有效地与下游团队和利益相关者合作。支持即服务的数据产品需要提供指导和支持,以确保开发的数据产品与预期结果一致,以免度量标准或模型被误解或滥用。

一种不同的团队拓扑

我们学到了什么?在数据科学中,产品的架构(例如 ML 模型)通常只有在数据科学家有机会进行实验和探索后才会被完全理解。因此,数据科学团队的结构需要灵活和适应,能够随着团队对问题和潜在解决方案的理解的演变而调整。此外,这意味着数据科学团队与组织其他部分之间的沟通架构需要反映这种适应性,并处理更高程度的不确定性。这种对协作、探索和端到端责任的过度关注,特征化了高影响力数据科学功能的拓扑结构。

你怎么看——我们说服了自己吗?这个论点在哪些方面显得薄弱,又在哪些方面产生了共鸣?

文中的观点仅代表我个人的意见,并不代表任何组织、其附属机构或员工的意见。

数据科学技术以提升营销活动

原文:towardsdatascience.com/data-science-techniques-to-improve-marketing-campaigns-f949573712d9

数据科学 | 人工智能 | 营销

一份利用数据科学增强现有营销活动的指南,将您的业务提升到一个新的水平

David FarrugiaTowards Data Science David Farrugia

·发表于 Towards Data Science ·5 分钟阅读·2023 年 5 月 10 日

--

图片由 Melanie Deziel 提供,来源于 Unsplash

说实话,在今天的世界里,营销是任何业务的命脉。营销(尤其是数字营销)有潜力产生成千上万的潜在客户。在当今竞争激烈的市场中,任何企业的成功都与其营销活动的有效性紧密相关。

有效的营销活动必须针对一个(或多个)群体。

有效的营销活动必须充分了解其目标受众。了解他们喜欢什么和不喜欢什么。

数据科学与营销的结合赢得如此强烈的关系,这一点并不奇怪。

数据科学彻底改变了企业处理营销的方式。最有效的营销活动完全依赖数据驱动。企业分析其流量,识别趋势,并不断渴望更好地理解其客户。

本文的目的是提供关于一些在数据科学中最强大的技术的高级讨论,这些技术用于构建高效的营销活动。

1. 客户细分:识别目标受众

假设您是一个服装店的自豪老板。您的库存涵盖从最新流行的青少年时尚到高档商务套装。显然,向所有客户发送一条关于商务套装的促销信息不会带来最大的投资回报。

相反,您决定使用 k-Means 聚类按年龄、性别和购物习惯对客户群体进行细分。

然后你会创建一个营销活动,更有效地向正确的客户群体推广你的库存。

你将从更具针对性和焦点的促销信息中受益,这些信息旨在带来更高的回报。

2. 情感分析:理解客户意见

情感分析是一种技术,通过分析文本来判断该文本是否具有积极或消极的意图。

情感分析对企业评估客户对其产品的接受程度非常有价值。你的客户对购买感到满意吗?他们会推荐给朋友吗?

通过分析社交媒体平台上的文本甚至客户评论,你的业务可以确定客户对你业务的总体情感。这将揭示关于你营销策略的宝贵见解。例如,你可能会选择加大对受欢迎的产品线的投入,或者决定停产那些收到负面情感的产品线。你也可以采取必要措施来改变或更新你的产品,以更好地满足客户需求。

3. 预测分析:预测客户行为

预测分析是一个非常重要且极具多样性和动态性的领域。根据业务和行业的不同,预测分析有不同的应用案例。

简而言之,预测分析是一种使用历史数据通过机器学习和统计方法预测未来事件的技术。

一些示例可能包括预测客户行为、预测趋势以及按季节性销售等。

此外,还有一些其他高效的应用案例,包括客户生命周期价值(CLV)、流失预测和欺诈检测。

4. A/B 测试:实验并找到最佳方法

考虑这个。

你拥有一个在线商店。你目前的着陆页是红色背景和蓝色按钮。

你已经对这个布局感到厌倦,并且根据你的情感分析,客户似乎也不喜欢这个布局。

你决定将按钮的颜色从蓝色改为白色。

那个改变是好的?还是使情况更糟?

A/B 测试帮助我们回答这个问题。A/B 测试是一种相对简单但极其有效的技术,用于衡量变更带来的影响。例如,在这种情况下,我们可以使用 A/B 测试来确定新的布局更改是否改善了客户的情感评分,或是否与其之前的版本相比带来了更多销售。

A/B 测试对优化营销至关重要,因为它还可以帮助我们衡量哪个营销活动产生了更好的结果。

5. 社交网络分析:揭示关键连接和关系

毫无疑问,如今每个人及他们的祖母至少都在使用一个社交媒体平台。

另外,我们都有那个在社交媒体上过度分享的家庭成员或朋友。

潜意识中,我们每个人都不可避免地让别人知道我们喜欢什么、不喜欢什么。

企业利用这些信息深入了解他们的客户。社会网络分析是一种技术,通过检查社会结构和模式来确定它们在网络中的关系。

在促销的背景下,社会网络分析可以跟踪信息的传播,揭示隐藏的关系,并提供目标受众可能感兴趣的其他信息。

也许这是利用数据进行营销时最具争议的方面,但这肯定正在我们周围发生。

社会网络分析的另一种方法是识别关键客户(即“意见领袖”),这些客户可能会将您的产品分享给他们的网络,并尽可能多地转化为付费客户。最终目标是让您的产品“病毒式传播”。

6. 推荐系统:个性化客户体验

顾名思义,推荐系统就是一个推荐东西的系统。

它分析用户行为和偏好,以向合适的受众推荐合适的内容。其好处有两方面:1) 定制化营销活动,2) 提高品牌参与度。更有针对性和集中的营销活动。

这里的典型例子包括亚马逊的“其他人也买了这个”或著名的 Netflix 的“因为你观看了”。

整合一切:数据驱动的营销成功故事

假设一个小型在线商店销售环保产品的实际例子。

以下是强大而有效的营销的总结性步骤指南。

  • 客户细分以根据购买历史、人口统计数据和浏览行为来识别重点群体。这导致了更个性化的营销内容,与每个细分群体产生共鸣,从而提高了参与度。

  • 情感分析用于分析客户评论,了解他们在社交媒体上的意见。这意味着他们能够解决问题和客户反馈,从而改善品牌形象。

  • 预测分析用于预测客户的需求和偏好。这导致了更有针对性的营销活动,并减少了客户的流失率。

  • A/B 测试使他们能够微调广告设计、优惠结构和信息传递风格,从而大幅提高点击率。

  • 社会网络分析被用来识别关键的有影响力客户,以推广他们的产品并帮助提升品牌形象和曝光度。

结论

数据科学是一个有趣的领域,提供了大量营销机会。利用数据的力量,使我们能够创建更有针对性、最终更有效的营销活动,与您的目标受众产生共鸣。

您喜欢这篇文章吗?每月$5,您可以成为会员,解锁对 Medium 的无限访问权限。您将直接支持我和您在 Medium 上最喜欢的其他作家。非常感谢!

## 通过我的推荐链接加入 Medium - David Farrugia

获得对我所有⚡优质⚡内容的独家访问权,无限制地浏览 Medium。通过给我买杯咖啡来支持我的工作…

david-farrugia.medium.com

想要联系我吗?

我很想听听你对这个话题的看法,或者对任何关于 AI 和数据的意见。

如果你希望联系我,可以发邮件到davidfarrugia53@gmail.com

Linkedin

数据科学:现代经济学的支柱

原文:towardsdatascience.com/data-science-the-modern-day-pillar-of-economics-f5e9e6d3cb44

Petru van der Walt FélixTowards Data Science Petru van der Walt Félix

·发布于Towards Data Science ·6 分钟阅读·2023 年 11 月 20 日

--

图片:Shutterstock,授权(1928239373)

大致框架

随着近年来特别是新千年以来的技术进步,数据科学已成为一个独立的学科,区别于计算机科学,更加贴近统计学。它开辟了一个独特的领域,在这个领域中,数据科学家致力于解决依赖于数据的获取、处理和最终解释的商业问题。

这需要特定的技能组合,例如对编程语言的良好理解,例如 Python 和 R,以帮助简化访问大型离散数据集所需的分析工作流。数据科学家的技能组合与经济学家的技能结合,为那些希望在现代经济学中脱颖而出的个人提供了成功的公式。

事实与数据

上述发现得到了声望卓著的伦敦经济学院[1]在近年来扩展其课程的支持,增加了一项名为 BSc 数据科学与商业分析的本科课程,课程标语承诺学习者将“学习分析数据以解决现实世界问题”,这些现实世界的问题自然基于经济和商业关系。

另一个积极的指标是,世界银行前首席经济学家[2]和 2018 年诺贝尔经济学奖共同获得者保罗·罗默,是 Jupyter Notebook 的支持者。Jupyter Notebook 是一个开源的网络应用程序,允许用户创建和分享包含实时代码、方程式和可视化的文档,以支持跨多种编程语言的互动计算。Jupyter 的最终备注是其核心,Jupyter 的名称是一个首字母缩略词,代表 Julia、Python 和 R,这三种都是编程语言。

一个经济学领域的巨头公开支持数据科学工具意义重大——无意冒犯——这明显表明了发展方向。正如罗默在 2018 年的一篇博客中所指出的:“Jupyter 奖励透明性;Mathematica 理性化保密。Jupyter 鼓励个人诚信;Mathematica 让个人躲在企业规避后面。”[3] 在这里,他将 Jupyter 与竞争平台 Mathematica 进行比较,然而如果我们超越这些描述,可以明显看出罗默强烈认同数据科学带来的好处和可能性。

实时案例研究

为了说明上述主题,我们来看一个当前的现实问题,英国正接近其脱欧后欧盟-英国关系的三周年纪念,按照新实施的贸易规定进行运作。

在 2016 年 6 月 23 日英国公投决定脱离欧盟及单一市场之后,经过四年的过渡期,直到 2021 年 12 月 31 日,才实施了商定的变更。

快进到现在,即 2023 年 11 月 20 日,除了英国脱欧成为影响英国经济成败的关键因素外,还有其他几个因素加入了这个复杂的局面,使得形势更加扑朔迷离。

其中一个晚到的因素当然是新冠疫情。纯粹从金融角度来看,新冠疫情导致消费者支出的前所未有的停滞,进而增加了家庭的储蓄,这主要是因为办公人员转为在家工作,所有的社交和休闲活动,包括旅行,都被无限期推迟。

不用说,这无疑加剧了财政上的巨大失衡,在金融系统已经因 0%利率而过热的情况下,进一步限制了货币政策的灵活性,从而使经济复苏变得困难。

这涵盖了两个因素,脱欧和新冠疫情。如果这还不够,紧接着出现了由乌克兰-俄罗斯冲突引发的能源危机,这场冲突始于 2022 年 2 月 24 日俄罗斯入侵乌克兰。再一次,纯粹从经济角度来看,这场冲突导致了能源需求超过供应,进而导致所有受影响的消费品价格飙升。

总结来说,这三种因素汇聚在一起,形成了完美的经济风暴,这将要求各方共同努力,帮助我们度过前方的波涛汹涌。

数据科学和经济学都建立在统计学之上,关键的区别在于,数据科学往往更侧重于预测变量之间的未来互动,而经济学则更侧重于历史互动,即前瞻性与回顾性,预测与因果关系。

戴上经济学家的帽子,我审查了 G7 国家在 2023 年之前的贸易对 GDP 比率,数据和图表由国家统计局提供[4]。这清楚地表明,尽管所有 G7 成员国在 2020-2022 年的疫情期间贸易暴跌,但它们都恢复到了疫情前的水平,强劲反弹,唯一的例外是英国仍然落后于其他成员国,如下图所示:

图片:国家统计局 [4]

这表明,与其他六个 G7 国家相比,贸易在英国 GDP 中的比重较低,这是截至 2022 年第二季度的有效声明。与 Brexiteers 在公投前主张的促进与欧盟以外的贸易关系不同,看来英国的贸易开放度,即其贸易能力,实际上在减少,这一点得到了数据科学的支持。

健康经济体的另一个关键组成部分当然是通货膨胀的平衡,目标是将其控制在 2%以下。截至 2023 年 10 月,英国的通货膨胀率为 4.6%。[5]

如果我们回顾从 1960 年代到现在的数据,特别是跟踪通货膨胀情况,也能提供一些有趣的见解。以下数据和图表来源于世界银行集团。[6]

图片:世界银行集团 [6]

我们在 2008 年看到的峰值是全球通货膨胀跳升至 8.9%,这是对 2008-2011 年全球金融危机的反应。

这一点也可以在下面的英国通货膨胀等效图中看到:

图片:世界银行集团 [6]

尽管 2008 年的峰值在全球通货膨胀尺度上并不显著,但却跃升至 3.5%,比前一年上涨了 1.1%,比 2004 年稳定水平 1.4 上涨了 2.1%。[6]

结论

随着全球和英国通货膨胀水平显著上升,这支持了关于 2024-2025 年经济衰退的预期,如果历史趋势和数据是可信的话。

基于上述发现和我作为投资管理数据业务分析师的个人经验,可以明显看出数据科学和经济学学科紧密相连,数据科学实际上作为经济学及其驱动和支持的决策的一个关键支柱。

我认为可以公平地得出结论,经济学已经通过利用市场上可用的新技术和数据分析方法获得了显著回报,一切迹象表明这仍是一个处于形成阶段的趋势。

参考文献:

[1] 伦敦经济学院,提供BSc 数据科学与商业分析学位作为其课程的一部分

[2] 保罗·罗默,维基百科

[3] P. Romer,Jupyter、Mathematica 和研究论文的未来(2018)

[4] 国家统计办公室,2022 年 10 月 10 日发布,ONS 网站,G7 经济体国际贸易流动的近期趋势

[5] 国家统计办公室,2023 年 10 月发布,ONS 网站,通货膨胀和价格指数

[6] 通货膨胀数据和视觉图表,世界数据集团

数据科学家面试指南

原文:towardsdatascience.com/data-scientist-technical-interview-guide-part-1-84e286fd4bae

第一部分:统计学和概率

Daniel FoleyTowards Data Science Daniel Foley

·发布于Towards Data Science ·阅读时间 18 分钟·2023 年 2 月 9 日

--

照片由Maranda Vandergriff拍摄,来自Unsplash

介绍

现在有很多关于数据科学面试的博客文章,但由于我最近又开始面试了,我觉得分享一下我以前和现在的经历,并希望能为正在经历这段旅程的你们提供一些指导,会是很有意义的。毫无疑问,数据科学面试是非常困难的。部分挑战在于你可能被问到的主题范围非常广泛。你几乎需要将软件工程师、数据分析师和产品经理的角色合为一身。

好消息是,一旦你提醒自己这些基础知识,你会发现经常被问到相同类型的问题。在这份技术面试指南的第一部分,我想提供一些概率和统计问题的题目和深入解决方案,你在技术面试中很可能会被考到这些问题。所以不再赘述,让我们开始吧。

统计学和概率

统计学和概率学是任何数据科学家必备的基础,复习这些基本概念对于在技术面试中表现出色至关重要。有很多主题经常出现,特别是下面列出的这些。

  • 概率:期望值、组合和排列、递归关系

  • 统计学: p 值、假设检验、效能、置信区间、概率与似然、中心极限定理、大数法则

  • 分布: 均匀分布、二项分布和正态分布是最重要的

在我看来,更难的面试将专注于解决涉及这些概念中的一个或多个问题,这是这篇文章的主要重点。然而,能够以非技术性的方式解释这些概念也极其重要,因此我将涵盖一些我认为有用的主题。我希望,通过展示我在解决这些问题时的思维过程,能够为那些准备技术面试的人提供有用的指南。

这里有一本参考书,我发现对回忆基本概念很有帮助。

中心极限定理

假设我想测量人们的收入。如果我们取一个收入样本并绘制这些数据点,我们很可能会看到一个偏斜的分布,其中一些人有很高的收入,而大多数人的收入较小且更为集中。假设我们基于这个样本计算平均值。如果我们继续抽取收入样本并计算平均值并绘制数据,我们将得到样本均值的抽样分布(如下图所示)。由于中心极限定理,这个分布看起来像正态分布。至关重要的是,基础的总体分布不必是正态的。下面的图示说明了这一点。

来源:作者的指数分布

来源:作者的正态分布

这很重要,因为它使我们能够基于单个样本进行统计推断。例如,在假设检验和回归分析中,我们可以使用 CLT 定理对我们的测试统计量/系数的行为进行推断。因为我们知道它是正态分布的,所以我们可以计算置信区间和 p 值,并做出可靠的推断。

大数法则

LLN 表明,随着样本大小的增加,样本均值会收敛于总体均值。可以将总体和样本视为两个集合,其中样本是总体集合的一个子集。随着样本大小的增加,样本集合的大小会越来越大,最终会与总体相同。这就是为什么样本均值会收敛于总体均值的原因。

LLN 和 CLT 之间的关键区别在于,LLN 讲述的是样本均值的情况,而 CLT 则与样本均值的分布相关。

二项分布

二项分布是一个极其重要的分布,并且在面试中经常出现。用直观的高层次方式从基本原理推导分布是展示你对其理解的好方法。

假设我们网站上有 10 个用户,我们想预测他们中有多少人会购买某物。我们可以将其视为重复的独立试验,每个用户要么购买要么不购买。设 p 为购买的概率,1-p 为不购买的概率,因为概率需要加起来为 1。考虑没有人购买的情况:(1-p)(1-p)… = (1-p)¹⁰。

如果一个人购买了呢?我们有 p¹(1-p)⁹。即 1 次成功和 9 次失败。我们不知道是哪位用户进行了购买。它可能是用户 1、用户 2、…或用户 10。为了解决这个问题,我们有 10 种可能性,并且我们要选择其中 1 种。将这些信息综合起来,就得到了下面的通用二项式公式。

二项式公式

p 值

p 值是一个相当有争议的话题,我认为即使是数据科学家也不容易直观理解,更不用说非技术人员了。但让我试着用一个例子来解释它们是如何使用的。

我们使用 p 值来确定假设检验是否显著。通常我们选择 p 值为 0.05 或 5%。例如,如果我们进行了一次假设检验,并获得了 0.03 的 p 值,这意味着假设原假设为真,我们观察到的检验统计量与此一样极端或更极端的概率是 3%。换句话说,如果原假设为真,我们观察到这种效果的可能性非常小,因此它很可能是错误的。我们也可以这样考虑 p 值:如果我们进行 100 次实验,我们期望观察到至少与此一样大的检验统计量 5 次。最后,另一种看待 p 值的方法是,它是一个条件概率。在这种情况下,它是原假设为真的情况下观察到的检验统计量与此一样极端或更极端的概率。

在接下来的这些问题中,我将展示我们如何在实践中使用一些这些主题来解决技术面试中的问题集风格的问题。

解决问题

以下问题来源于我以前在面试中被问到的问题或在准备面试时遇到的各种书籍和互联网资源(例如 glassdoor)。这些问题的选择基于反映技术面试中最重要和最常见的概念。我还突出了一些我认为在每个问题中需要认识的关键概念。

问题 1

你和一个朋友轮流掷一个公平的六面骰子,第一个掷出 6 的人获胜。第一个掷骰子的人获胜的概率是多少?

关键概念:递归关系,概率树,独立性,均匀分布

如果我们在开始之前稍微思考一下问题陈述,直观上看来,先行似乎会给我们带来优势,所以我们期望这个概率大于 1/2。我们还知道关于掷骰子的什么?好吧,我们知道掷骰子的结果是均匀分布的。每个结果的可能性是一样的,得到特定数字的概率是 1/6。

对于这些类型的问题,我喜欢画一个概率树。至少对我来说,这简化了问题,并给了我一个可以用来解决不立即显而易见的问题的一般方法。那么从我们的角度来看,我们来做这个。有两种可能的情况

  • 我们赢:将获胜的概率定义为 x。

  • 我们输:概率为 1-x。

在第一次掷骰子时,我们有以下情况,

  • 我们要么掷出 6 的概率是 1/6,然后我们以 1 的概率获胜。

  • 或者我们掷其他数字的概率是 5/6。

第二种情况很有趣,因为现在是我们朋友的回合。为了使我们获胜,我们需要他们掷出 6,这种情况的概率为 5/6。请注意,当这种情况发生时,我们实际上是重新开始游戏。即再轮到我们掷骰子,我们的获胜概率是 x。

让我们用一个方程使这一点更具体。为了得到我们的概率,我们只需要解方程 x。

因此,如果我们先行,获胜的概率是 6/11,而我们朋友在第二个回合获胜的概率是 5/11(1–6/11)。请参见下面的树状图。

问题 2

你从一个 86 的网格底部开始。你只能向上或向右移动。你通过中心的概率是多少?*

关键概念:组合(顺序无关),计数的乘法规则

对于这些类型的问题,通常将概率定义为频率,即(相关事件的数量)/(总事件的数量)是有帮助的。这样做可以帮助我们将问题分解,并尝试求解分子和分母。

让我们首先尝试找出分母,即可能事件的总数。在这种情况下,我们可以从起点(0,0)移动到终点(8,6)的总方式数。我们需要做多少步才能到达这一点?我们需要做 8 次向上移动和 6 次向右移动,总共 14 步。如果我们将向上移动定义为 U,向右移动定义为 R,那么一种移动方式是

UUUUUUUURRRRRR,即 8 次向上移动后跟 6 次向右移动。

这会将我们带到目的地。然而,我们也可以这样做。

RRRRRRUUUUUUUU,即 6 次向右移动后跟 8 次向上移动。

我们可以继续列举所有可能性,但可能有更简单的方法。总的来说,我们从(0,0)到(8,6)有 14!种移动方式,(第一次移动有 14 种方式,然后第二次有 13 种方式,依此类推)。

然而,这会导致重复计算,因为我们可以重新排列初始的 6 个 R,而不会改变结果,即顺序无关紧要。因此,我们必须将总数除以排列这 6 个 R 的方式数,即 6!。同样,我们也将总数除以排列 8 个 U 的方式数(8!)。这给我们:

14! / (8!6!) = 3003。

你们中的一些人可能会认识到这是组合公式。没错。因为顺序无关紧要,这与 14 选 8 是一样的。好的,我们知道了分母的值,但分子呢?记住,我们需要找到通过网格中心点(即点(4, 3))到达点(8, 6)的所有路径。解决这个问题的最简单方法是将旅程分成两部分。

首先,我们需要找到从(0, 0)到(4, 3)的移动方式数,然后找到从(4, 3)到(8, 6)的移动方式数。然后将这些值相乘以得到分子的总数。

我们可以使用与分母相同的思维过程。在这里,我们必须总共进行 4 次右移和 3 次上移,或者 4+3 = 7 次总移动。一个可能的路径是:RRRRUUU。再次,排列这些字母有 7!种方法,我们需要除以排列 4 个 R 的方式数(4!)和排列 3 个 U 的方式数(3!)。

(0, 0) -> (4, 3):7!/(4!3!) = 35

我们再次应用相同的原则来计算从(4, 3)到(8, 6)的路径数。这与第一部分完全相同,即 7 次总移动,4 次右移和 3 次上移。

(4, 3) -> (8, 6):7!/(4!3!) = 35

因此,我们的答案是(3535) / 3003 = 1225 / 3003 = 175 / 429.*

问题 3

给两个骑手发放了一张折扣券。使用优惠券的概率为 P。给定至少一个骑手使用了优惠券,两者都使用优惠券的概率是多少?

关键概念:贝叶斯定理,二项分布,独立性

解决这个问题的方法不止一种,我将在这里讨论两种。第一种方法是使用贝叶斯定理,我们通常可以通过一些条件语句识别这些类型的问题。在这种情况下,给定至少一个骑手使用了优惠券的情况下,两者都使用优惠券的概率是多少?我们可以将这些事件定义如下:

  • 至少一个骑手使用了优惠券(A)

  • 两名骑手都使用了优惠券(B)。

我们想找到给定 A 的情况下 B 的概率,我们可以使用下面的贝叶斯定理来计算。

贝叶斯定理

剩下的就是计算每个组件并将它们代入方程。让我们从 P(A|B) 开始,即在两名骑手都使用优惠券的情况下,至少有一名骑手使用优惠券的概率。这显然是 1,因为如果两名骑手都使用优惠券,至少有一名骑手会使用。P(B) 是什么?我们知道使用优惠券的概率是 P,并且假设每名骑手是独立的,我们可以说两名骑手都使用优惠券的概率是 P*P=P²(乘法规则)。

  • P(B) = 骑手 1 使用优惠券的概率 骑手 2 使用优惠券的概率 = P*P = P²

计算 A 的概率(P(A))会有点复杂。至少有 1 个骑手使用优惠券的概率等于 1 个骑手使用优惠券 2 个骑手使用优惠券的概率。由于每个骑手使用或不使用优惠券的概率是相同的,我们可以使用二项分布来描述这个问题。

二项分布

要获得总概率,我们只需将这些值相加(加法规则)。将这些值代入公式中,我们得到下面的答案。

P(A)

解决方案

替代解决方案

如上所述,还有一种更简单的解决方案。我们可以列出所有可能性。如果我们让 X 代表骑手使用优惠券,Y 代表骑手不使用优惠券,我们有:

  • XX: 两名骑手都使用了优惠券

  • XY: 骑手 1 使用优惠券而骑手 2 不使用优惠券

  • YX: 骑手 1 不使用优惠券而骑手 2 使用优惠券

  • YY: 两名骑手都没有使用优惠券

现在,如果我们条件化在至少一名骑手使用了优惠券的情况下,我们只剩下前 3 种可能性。现在的概率就是我们想要的事件除以所有可能事件的总数。

代入 P 作为骑手使用优惠券的概率,我们得到与第一个解决方案相同的结果。

替代解决方案

问题 4

假设你 观察了 3 个连续的 5 次抛硬币序列。至少有一个序列全是正面。这枚硬币有偏吗?*

关键概念:假设检验,二项分布,独立性

第二个问题是一个有趣的问题,我在面试中遇到过,可能会有点棘手。在我看来,解决这个问题的最简单方法是尝试将问题分解成几个步骤。首先,我们定义硬币有偏的含义。一个硬币有偏的定义是得到正面的概率不是 1/2。接下来,我们需要考虑如何测试这个问题。为了评估硬币是否有偏,我们基本上需要进行假设检验。

为了制定我们的假设检验,我们首先假设硬币是公平的,即我们的原假设是 p = 0.5(即掷出正面的概率),我们的备择假设是 p ≠ 0.5。然后我们在这个假设下计算观察到这个事件序列的概率。如果概率非常低(低于某个阈值,即 p < 0.05),我们拒绝我们的假设并得出结论:硬币有偏。我们如何计算观察到这个事件序列的概率?

记住,得到至少一个全正面的序列的概率与得到 0 个全正面序列的概率的 1 减去相同。或者,我们可以计算得到正好 1 个全正面序列 + 2 个全正面序列 + 3 个全正面序列的概率。

为了找到这个概率,我们需要计算以下内容

  • 5 次翻转中得到 5 个正面的概率:即 HHHHH

  • 得到恰好 0 个全正面序列中的 3 个序列的概率。

  • 我们需要从 1 中减去这个概率,以得到至少一个全正面序列。

这个问题由两个二项分布组成两个部分。第一个部分是 5 次翻转中得到 5 个正面。然后我们可以将这个概率作为输入用于我们的第二个二项分布,3 个 5 次翻转的序列,其中 p 是第一个部分的答案。

考虑第一次序列包含 5 次翻转。我们试图计算什么?我们想知道在 5 次翻转中得到 5 个正面的概率。如果我们定义成功的概率为得到一个正面,我们有

好的,现在我们知道 5 次翻转中得到 5 个正面的概率。对于第二部分,我们可以再次应用二项式公式,因为每个序列具有相同的概率且都是独立的。我们有 3 次试验,我们希望其中 0 次是 HHHHH。

现在我们有 n = 3,k = 0,p = 0.03124 和 1-p = 0.96875。

记住,这是看到所有零个正面序列的概率,因此得到至少一个全正面1–0.909 = 0.09

这本质上是我们的假设检验的 p 值。这个值没有低于我们 0.05 的阈值,因此我们没有足够的证据来拒绝硬币是公平的假设。这也应该有直观意义,因为你通常需要更多的硬币翻转来确定它是否有偏。

问题 5

你每天从一个正态分布的随机变量 X — N(0,1)中抽取一次。你获得大于 2 的值的预期天数是多少?

关键概念:期望值,正态分布,经验法则,几何分布

要解决这个问题,我们需要认识到我们正在计算某个事件发生之前的期望时间。我们也可以将其框架化为第一次成功之前的试验次数,你可能会认识到这是一个几何分布。换句话说,我们需要找到几何分布的期望值。几何分布的均值是 1/p。这个问题与我们期望在掷骰子之前看到 4 的次数是完全一样的。答案是 1/(1/6) = 6 次掷骰子。

解决这个问题的关键是找到正确的 p 值。我们知道我们的分布是均值为 0,标准差为 1 的正态分布。如果我们从这个分布中抽样,得到值为 2 的可能性有多大?

好吧,2 是距离均值 2 个标准差。根据经验法则,我们知道曲线> 2 个标准差的面积大约是 0.025。因此,我们在任何给定的一天观察到值> 2 的概率是 0.025。这就是我们的 p 值。

现在,我们只需计算 1/0.025 = 40。换句话说,我们期望等到 40 天后才能看到一个 2。

问题 6

你掷 100 枚公平的硬币。如果 60 枚或更多正面朝上,你赢得$10,其他所有结果都不赢。你应该为$1 玩这个游戏吗?

关键概念:期望值,二项分布,中心极限定理,经验法则

你应该将这个问题识别为一个期望值问题。我们想知道玩这个游戏获得的期望收益是否大于玩游戏的期望成本。

我们可以如下定义游戏的期望值:即,玩这个游戏的成本是$1,如果我们得到 60 次或更多的正面,我们将赢得$10,如果得到少于 60 次正面,我们将赢得$0。

好的,这个问题归结为寻找 P(X≥60)。经过一点思考,我们可以认识到,在 100 次掷币中获得 60 次或更多正面应该是二项分布,N = 100,p = 0.5(假设是公平的硬币)。现在,我们只需要使用二项分布找到获得 60 次或更多正面的概率。做这个的长方法是逐一计算获得 60 + 61 + 62 + … + 100 次正面的概率。这似乎有点低效,那么有没有其他方法可以使这个计算更容易呢?

请注意,我们的样本量是 100 次掷币。希望你能认识到中心极限定理(CLT)可以在这里帮助我们,因为 N > 30。我承认,这并不是世界上最显而易见的洞见,但你练习这些问题越多,就越容易发现像这样的情况。为什么 CLT 在这里有用?我们知道如果应用 CLT,那么采样分布将大致是正态分布。由于这是一个表现良好的分布,我们只需要知道均值和标准差即可定义整个分布。如果我们知道分布,我们就可以轻松计算所需的概率。

我们首先需要计算硬币投掷的均值和标准差。由于它们遵循二项分布,我们有:

因此,我们的均值为 50,标准差为 5。关于正态分布我们还知道什么?如果你记得经验规则,它指出大约 95% 的分布在均值的 2 个标准差范围内,我们可以利用这一点计算所需的概率。60 距离均值 2 个标准差,我们知道大约 2.5% 的分布在这一点之上。

因此,P(X≥60) 大约是 0.025。现在我们可以直接将其代入上述期望值方程中。

这个游戏的期望值为负,因此我们不应该玩它。我非常喜欢这个问题,因为它需要应用概率和统计的几个方面才能解决。

问题 7

在不盈利的时期,公司有时会暂停支付股息。假设在支付股息后,下一个股息支付的概率为 0.9,而在暂停股息后,下一个股息也会以 0.6 的概率暂停。长期来看,将支付的股息的比例是多少?

关键概念:马尔可夫链,平稳分布

这个问题可以被表述为具有 2 个状态的马尔可夫链。

  • 支付股息

  • 支付股息 暂停

为什么可以表示为马尔可夫链?因为这个问题具有马尔可夫性质(无记忆性)。在时间 t+1 进入任何状态的概率仅取决于你在时间 t 时的状态。即我们是在支付股息状态还是暂停股息状态?时间 t-1、t-2 等的状态不会影响我们下一个状态的概率。

问题给出了这两个状态之间的转移概率。如果我们处于支付股息状态,我们将在该状态下保持的概率为 0.9,因此以概率 0.1 转移到暂停状态。如果我们处于暂停股息状态,我们将以 0.6 的概率保持在该状态,并以 0.4 的概率转移到另一个状态。我们可以将其表示为以下转移矩阵:

转移矩阵

问题要求我们解决马尔可夫链的平稳分布,即长期均衡状态。在这种情况下,随着时间的推移,概率分布保持不变。即如果我将当前状态与转移矩阵相乘,我会得到相同的值。这种情况在以下方程中描述。

平稳分布

平稳分布

我们在这里要做的就是解这组方程以求出 pi1 和 pi2。由于概率必须加起来等于 1,因此 pi1+pi2 = 1。

方程

如果我们解这个方程组,我们会得到如下结果

解答

因此股息将会支付 长期来看大约 85%的股息将会支付。

结论

下面是一些额外的参考资料,我认为它们将有助于你的准备,并深入探讨我在这里没有涵盖的话题。

参考文献

[1] Joseph K. Blitzstein 和 Jessica Hwang 的《概率论导论》

[2] Zenefits Glassdoor 面试问题

[2] www.stat.berkeley.edu/~aldous/150/takis_exercises.pdf

[3] Lyft Glassdoor 面试问题

[4] 递归方法

[5] 中心极限定理

[6] 二项分布

你可能会对我的其他帖子感兴趣

## 构建流数据管道

Apache Beam 和 DataFlow 实时数据管道

towardsdatascience.com ## 使用 Snowflake 和 Dask 构建机器学习管道

引言

towardsdatascience.com ## 贝叶斯时间序列预测方法

今天我们将从零开始在 R 中实现贝叶斯线性回归,并用它来预测美国 GDP 增长…

towardsdatascience.com

数据科学家:首先是问题解决者,其次是算法魔术师

原文:towardsdatascience.com/data-scientists-problem-solvers-first-algorithm-wizards-second-93daa031d131

观点

拿着锤子,看到的每样东西都是钉子

Ilro LeeTowards Data Science Ilro Lee

·发表于Towards Data Science ·阅读时间 5 分钟·2023 年 3 月 10 日

--

图片由谢尔盖·秋伊科拍摄,刊登于Canva

最近,我有机会与澳大利亚一家大型政府机构的数据科学团队会面。从我所了解的情况来看,该团队由高度熟练的数据科学和机器学习专家组成。然而,在我们的交谈中,我不禁注意到数据科学家们有一个共同的主题。他们似乎更愿意讨论预测人类行为的最新算法,而不是讨论和定义他们试图解决的核心问题。

这个经历突显了我在数据科学领域看到的一个常见问题:把一切都视为数据科学问题的倾向。这就像是拿着锤子,看到的每样东西都是钉子。尽管算法和高级技术可以是强大的工具,但数据科学家应该首先是问题解决者,这一点至关重要。

在这篇文章中,我打算阐明问题解决技能在数据科学中的重要性,以及为什么它们应该优先于算法技术。我还将讨论在提出数据科学解决方案时理解组织准备情况的重要性,以及数据科学家如何引领组织走向更加数据驱动的未来。

1. 成为问题解决者的的重要性

数据科学有潜力提供有价值的见解并解决复杂的问题。然而,当数据科学家把一切都看作数据科学问题时,他们可能会过于关注数据和算法,而忽视了他们试图解决的现实世界问题。这可能导致解决方案不完整或无效。

为了避免这种陷阱,数据科学家应该优先考虑解决问题。他们应首先了解他们试图解决的问题、相关的利益相关者以及业务背景。只有这样,他们才能确定最佳的数据科学应用方法,以有效地解决问题。

例如,假设一家公司正在经历高客户流失率。数据科学家应首先调查流失的根本原因,而不是立即应用机器学习算法来识别最可能流失的客户。他们可能会进行客户访谈,分析反馈调查,并检查社交媒体数据以识别常见主题。掌握这些信息后,他们可以提出一个解决方案,解决流失的根本原因,同时利用数据科学预测哪些客户最可能流失,以及哪些留存策略最有效。

通过首先成为问题解决者,数据科学家可以创建为组织提供实际价值的解决方案。他们可以解决问题的根本原因,提供可行的见解,并对公司成功产生有意义的影响。

你是小型组织中唯一的数据科学家吗?

具有资源 fulness、利益相关者管理和持续学习

towardsdatascience.com

2. 理解组织对高级数据科学解决方案的准备程度

尽管数据科学具有巨大的潜力,但重要的是要理解并非所有组织都准备好采用最先进的数据科学算法或解决方案。在某些情况下,像数据透视表这样的简单解决方案可能更为合适。

在提出数据科学解决方案时,考虑组织的准备情况至关重要。这涉及到理解组织的文化、基础设施和资源。例如,假设组织缺乏支持先进算法的必要基础设施或没有数据驱动的文化。在这种情况下,它可能还没有准备好采用最先进的数据科学解决方案。

在这些情况下,重要的是从简单的解决方案开始,并逐步过渡到更高级的解决方案。例如,数据科学家可以建议一个简单的数据可视化或报告工具,以帮助组织更好地理解其数据,然后再过渡到更高级的机器学习算法。

通过逐步推进的方法,我们可以与利益相关者建立信任,并以符合组织需求和能力的方式展示数据科学的价值。这有助于为未来更高级的数据科学解决方案创造一个更具接受度的环境。

## 数据科学家:如何推销你的项目和你自己

遵循这一公式来制定完美的电梯陈述

towardsdatascience.com

3. 引领数据驱动组织的旅程

当我们加入一个组织,尤其是当组织处于数据转型的早期阶段时,我们有机会引导我们的组织变得更加数据驱动。这涉及到了解采用数据科学解决方案的障碍,并努力克服这些障碍。

数据驱动决策最显著的障碍之一是对数据科学潜力的缺乏理解。许多利益相关者可能不清楚数据科学如何帮助他们解决问题和做出更好的决策。我们的工作是教育利益相关者数据科学的潜力,以及它如何帮助他们实现目标。

另一个障碍是组织内缺乏数据素养。这可以通过培训计划和教育举措来解决,帮助利益相关者理解如何使用数据来做出决策。

最后,可能存在数据驱动决策的文化障碍,比如对数据缺乏信任或偏好基于直觉的决策。这些障碍可以通过数据讲故事和其他技术来解决,帮助利益相关者看到数据驱动决策的实际价值。

通过在解决这些障碍中担任领导角色,数据科学家可以帮助他们的组织变得更加数据驱动,取得更多成功。正如我们之前讨论的,这可能涉及从简单的解决方案开始,逐步构建到更先进的数据科学解决方案。最终目标是创造一种文化,使数据被视为宝贵的资产,数据驱动的决策成为常态。

## 数据科学项目中有效利益相关者管理的 3 个关键方面

不能忘记我们仍然需要与人合作

towardsdatascience.com

尽管数据科学是一种强大的工具,但在提出解决方案时,重要的是以解决问题的心态来对待它,并且在建议方案时要考虑组织的准备情况。作为数据科学家,我们有责任教育利益相关者,并引导我们的组织朝着更加数据驱动的方向发展。这包括解决数据驱动决策中的文化和组织障碍,从简单的解决方案开始,逐步构建更先进的数据科学解决方案。通过这样做,我们可以创建真正为组织提供价值的解决方案,并对其成功产生有意义的影响。

[## 通过我的推荐链接加入 Medium - Ilro Lee

阅读 Ilro Lee 的每一篇故事(以及 Medium 上成千上万其他作者的故事)。您的会员费直接支持 Ilro…

ilro.medium.com](https://ilro.medium.com/membership?source=post_page-----93daa031d131--------------------------------)

数据共享挑战:隐私和安全问题

原文:towardsdatascience.com/data-sharing-challenges-privacy-and-security-concerns-ca436543a323?source=collection_archive---------15-----------------------#2023-02-01

实施数据共享时的隐私和安全挑战

Louise de LeyritzTowards Data Science Louise de Leyritz

·

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

--

隐私与安全:数据共享的最大挑战 — 图片来自 Castor

数据共享可以为公司带来 许多好处,但也伴随着一系列问题。公司常常面临的两个主要问题是隐私安全。我们将在这一系列关于数据共享的第三篇文章中讨论这些概念。

没有人真正喜欢讨论这些话题。我会首先承认,这些话题并不是最令人兴奋的事情。但相信我,花几分钟时间关注它们是值得的。这可以帮助你的公司避免数百万美元的罚款

尽管数据共享带来了巨大的好处,但它可能与隐私和安全原则相矛盾。

数据共享是关于给业务团队提供访问数据的权限,以帮助他们做出基于数据的决策。让我们回顾一下数据共享的原则:

  • 每个人都应该能够访问他们所需的数据,而不仅仅是某些角色或职位。

  • 应该没有障碍阻止人们获取他们需要的数据。

  • 数据应该以一种组织和结构化的方式进行整理,使任何人都能访问、理解和使用它。

因此,认为隐私和安全与这些原则相冲突是自然的。隐私是关于赋予个人对其个人信息的控制权。安全则是保护信息不被未经授权的访问。这两个概念可能在数据共享时看似相矛盾。

如果你认为自己不受隐私和安全规则的影响,再想想吧。所有处理个人数据的公司都会受到影响。

更重要的是,这些规则通过严格的法规得以执行,例如欧洲的通用数据保护条例 (GDPR)和美国的加州消费者隐私法案(CCPA)

根据像 GDPR 和 CCPA 这样的法律,仅仅遵守是不够的,你还必须能够证明这一点!这就是问责原则。如果你不能展示你遵守规则,你将被认为是不合规的。而且,我们知道,不合规会带来巨额的代价。

本文探讨了在避免隐私和安全相关风险的同时实施数据共享。文章分为三个部分:

  1. 处理隐私风险及其管理方式。

  2. 安全问题以及如何缓解这些问题。

  3. 演示合规性和问责原则。

‍1. 隐私

当我们分享个人身份信息(PII)时,想要保持隐私是很自然的。隐私就是对信息共享、共享对象以及原因的控制。

个人信息包括我们的姓名、社会安全号码、电子邮件、邮寄地址和 IP 地址。保护这些数据对于防止从不便(如垃圾广告)到真正的威胁(如身份盗窃)的侵扰是非常重要的。

GDPR 和 CCPA 中最广泛认可的隐私规则被称为目的限制。根据这一规则,你应该仅为了特定、明确且合法的目的处理 PII。你必须在收集数据之前向数据主体说明这些目的。

这一原则确保收集的数据始终用于其指定目的。

假设你是一家零售商,收集客户地址用于产品配送。根据目的限制原则,你只能将这些数据用于产品配送。你没有权利将其用于其他目的,如营销活动。

当涉及数据共享时,我们如何确保数据仅用于其预期目的?当数据对更广泛的受众开放时,跟踪其使用情况可能会变得困难。利益相关者往往不知道数据收集的具体原因。没有这些知识,遵守规则可能会很困难。

此外,数据共享增加了 PII 数据潜在暴露点的数量。这为潜在的隐私侵犯(如身份盗窃)和对个人信息的控制丧失打开了大门。你对数据的开放程度越高,利益相关者利用数据进行恶意目的的机会就越多。

解决方案:文档与数据共享协议

在深入解决方案之前,重要的是要注意数据共享并不意味着对 PII 数据的无限制访问。

PII 数据应仅暴露给需要查看的人。

我们将在稍后的访问控制管理部分讨论这些内容。

本节讨论隐私问题和确保有访问权限的人将数据用于预期目的。

这里有两个重要步骤可以帮助你避免组织内的数据滥用。

1. 管理数据访问

数据访问可能导致潜在的滥用,因为员工和分包商可能会获取他们不应拥有的机密或敏感信息。

有不同的方法来管理组织中的数据访问:

  • 实施二次认证措施:验证单个用户的身份至关重要,以便准确知道是谁试图登录系统。在员工使用共享账户(如管理员或 root)时,这变得更加复杂。在这些情况下,实施二次认证方法非常重要。

  • 引入双因素认证:凭证盗窃仍然是安全漏洞的一个普遍原因。双因素认证通过要求用户不仅提供他们知道的东西(如凭证),还需要他们拥有的东西(如智能手机)或是(如生物识别数据)来改善用户身份验证。

  • 分配特定的用户角色或访问属性到每个账户: 一旦用户身份经过验证,可以通过分配特定的用户角色或访问属性到每个用户账户来实现细粒度的访问管理。

2. 教育你的员工

不要忽视员工教育在防止数据滥用中的影响。

教育员工的最佳方式是将数据安全信息纳入整体公司政策中。一项全面的政策作为内部程序和标准的可靠信息来源,包括网络安全。这是一种有效的方法,可以教育新员工了解他们可以和不可以如何处理公司数据。

另一种解决方案是数据文档化。对 PII 数据进行适当的文档记录是确保数据以道德和合法方式处理的重要步骤。

文档化涉及识别 PII 数据并在数据库中标记。你应该指定数据收集的目的,以及你将使用数据的具体用途。

为每个 PII 字段提供正确的背景信息可以确保每个人都知道它的用途。这样,各团队在访问数据时可以在目的限制原则下合法使用。

假设你在数据集中有一个标记为“电子邮件地址”的列。对于这个列,重要的是要包含数据使用的详细说明。这可能包括如下声明:“电子邮件地址,仅用于产品配送”

这确保了利益相关者使用数据的目的符合预期,而不是用于任何未经授权的活动。

一旦你的业务团队已经设置好可以轻松访问文档化数据,保持系统运作的另一种方法是设立一个数据共享协议 (DSA)。正如Piethein Strengholt所述,DSA 是一个具有法律约束力的合同,列出了数据共享和使用的所有条款和条件。

它概述了将共享哪些类型的数据、共享的原因以及如何保护这些数据。它还列出了每个人的责任,包括数据使用的任何限制,以及如果事情没有按计划进行会发生什么。这些协议在研究、商业和政府中被广泛使用。它们是确保每个人都遵守规则并将数据用于预定目的的绝佳方式。

2. 安全

安全是关于实施保护个人信息的措施。

PII 数据需要防止未经授权的访问、使用、披露、干扰、修改或销毁。

GDPR 中的一个重要安全规则是完整性原则。它规定,个人数据必须受到保护,防止未经授权的访问、更改或销毁。

实施数据共享就像是打开了洪水闸门,带来了多种潜在威胁,如黑客攻击或恶意软件。访问数据的人越多,未经授权方获取数据的机会就越多。而且,当数据被共享时,它可能还会存储在多个位置,使得监控变得更加困难。

即使公司的 IT 系统像金库一样坚固,数据共享仍可能带来安全风险。这是因为尽管强大的 IT 系统可能能够抵御外部威胁,但它可能无法防止内部威胁,例如内部泄露。

数据共享可能是一项棘手的业务。访问数据的人越多,你公司系统中的潜在弱点就越多。但这并不意味着一切都要悲观。即使数据受到更多人的关注,仍然可以在确保数据安全和合规的同时保护数据。这只是需要采取正确措施以保护数据。

解决方案:访问控制和数据最小化

在数据共享时,关键是找到访问与安全之间的正确平衡。一方面,你要确保正确的人可以访问他们完成工作所需的信息,另一方面,又不想让任何人都能随意进入。这就是访问控制数据最小化发挥作用的地方。

  • 访问控制旨在确保只有合适的人才能访问数据。这个话题在前面章节中已经讨论过。

  • 数据最小化是另一个关键部分。其核心在于将共享的数据量保持在最低限度。与其分享所有数据,不如退一步思考哪些信息对各团队执行任务是真正必要的。一般来说,你可以在数据集中移除或遮蔽 PII 列,而不影响利益相关者。只分享必要的数据,可以将流动的 PII 信息量保持在最低水平。

访问控制数据最小化一起使用时,它们可以帮助你与更多人共享数据,同时保持数据的安全性和符合安全法规的要求。它们可以共同确保你的数据安全,同时向需要的人提供访问。

实践这一点的最佳方法是使用数据共享平台。可以把它看作是一个虚拟的“文件柜”,你可以在其中存储和分享你的数据给合适的人。这些平台通常配备了内置的访问控制,因此你可以确保只有那些应有访问权限的人才能看到数据。此外,它们通常还具有强大的安全措施,以保护你的数据免受错误人员的侵害。

通过使用数据共享平台来管理访问控制和数据最小化,你可以与更多人共享数据,同时保持其安全并符合安全法规。这就像一个组合锁,既能保护数据的安全,又能使数据对需要的人可用。这是一个双赢的局面。

3. 责任:如何证明合规性?

如前所述,如果你不能证明你遵守了规则,那么你会被视为违反规则。这是数据法规如 GDPR 和 CCPA 中责任原则的基本理念。负责任意味着能够证明你遵循所有法规并确保个人数据安全。

责任原则规定,公司必须能够展示他们已采取适当的技术和组织措施,以满足法规下的义务。

想象一下,你是一个组织,监管机构正在进行审计以检查谁访问了敏感数据以及他们对这些数据做了什么。如果没有适当的流程,你将陷入困惑,试图弄清楚数据的使用情况。

为了证明符合通用数据保护条例(GDPR),你可以采取以下措施:

  1. 进行数据保护影响评估(DPIA) — DPIA 是一种风险评估,帮助你识别、分析和减轻数据处理活动的隐私风险。它表明你已采取必要措施,以确保你的数据处理符合 GDPR。

  2. 保留处理活动记录 — 你必须保留所有数据处理活动的记录,包括处理的数据类别、处理目的、数据保留期限等。查看你的数据谱系也可以帮助你证明符合规定。数据谱系,也称为数据家谱,是追踪数据在整个生命周期中的起源和流动的能力。它让你能够看到数据的来源、去向以及去过的地方。这意味着你可以轻松追踪谁访问了这些敏感数据,何时访问的,以及他们对数据做了什么。

  3. 实施适当的技术和组织措施 — 您必须实施适当的技术和组织措施来确保个人数据的安全,例如加密、访问控制和防火墙。这些措施帮助您证明您已采取必要步骤来保护个人数据。

  4. 任命数据保护官(DPO) — 如果您的组织处理大量个人数据或进行定期和系统的数据主体监控,您必须任命一名 DPO。DPO 可以通过提供专家建议、培训和支持,帮助您证明您对 GDPR 的合规性。

  5. 定期进行隐私审计 — 定期进行隐私审计有助于评估您对 GDPR 的合规性,并识别需要改进的领域。它们还表明您致力于保护个人数据并保持对 GDPR 的合规性。

数据血缘示例 — 图片由 Castor 提供

结论

虽然数据共享可以为公司带来显著的好处,但它也带来了一系列问题,特别是在隐私和安全方面。

隐私是关于控制信息的共享、共享对象及其原因,而安全性则是指保护数据免受未经授权的访问、更改或破坏。

管理隐私和安全风险可以通过实施一些关键策略来实现。为了保护个人信息,重要的是投资于适当的文档并建立明确的数据共享协议。此外,实施访问控制和遵循数据最小化实践可以帮助减少安全风险并确保敏感信息的安全。我们在下面的图片中总结了这些信息。

处理隐私与安全 — 图片由 Castor 提供

公司必须遵守严格的法规,如 GDPR 和 CCPA,以保持数据的安全和保护。问责原则还规定,公司必须能够证明其合规性。

然而,借助适当的工具和实践,公司可以在尊重安全性和隐私规则的同时有效管理数据共享。

关于我们

我们介绍了利用数据资产时涉及的所有过程:从 现代数据堆栈 到数据团队组成,再到数据治理。我们的 博客 涵盖了从数据中创造实际价值的技术和非技术方面。

在 Castor,我们正在为 Notion、Figma、Slack 一代构建数据文档工具。

想了解更多?联系我们 我们将为您展示一个演示。

最初发布于 https://www.castordoc.com.

数据讲故事基础:数据科学家和 AI 从业者的核心策略

原文:towardsdatascience.com/data-storytelling-101-essential-strategies-for-data-scientists-and-ai-practitioners-b6843891ec8d

向同事和客户展示枯燥无味的数据的时代已经结束!

Richmond AlakeTowards Data Science Richmond Alake

·发布在 Towards Data Science ·13 分钟阅读·2023 年 1 月 10 日

--

发掘数据讲故事的力量,将你的职业生涯提升到新的高度!在这篇文章中,你将学习如何利用数据讲故事来吸引观众,让你的信息深入人心,并从竞争中脱颖而出。

无论你是经验丰富的专家还是刚刚起步,这些技巧将帮助你将数据转化为引人入胜的叙事,从而推动结果。通过阅读本文,你将获得一个框架,将数据讲故事的关键要素融入你的下一次演示、提案或提议中。

但你为什么需要关心呢?

作为一名 AI 或数据从业者,你将技术概念和数据见解转化为易于理解的术语的能力对于赢得利益相关者的支持和推动项目成功至关重要。

交叉口上的一个人 - 图片由作者在(Canva Text-to-Image)创建

你正站在两条道路的交汇处;道路#1 尽是从数据中提取的见解和智慧,而道路#2 则是项目利益相关者、业务经理以及那些决定你命运的人(有点戏剧化,但你明白了)。

为了确保利益相关者理解技术需求、附加值以及数据科学团队工作的影响,数据科学家、数据工程师和机器学习(ML)工程师必须有效沟通。你本质上控制着这两条道路交汇处的流量。

本文旨在:

  • 在数据科学和机器学习的背景下引入讲故事技巧

  • 提供培养讲故事技能的技巧

  • 强调有效讲故事在数据科学中的好处

  • 提供一个框架,你可以采用这个框架在各种场景中融入讲故事的元素,例如向非技术性利益相关者展示数据集。

数据驱动的讲故事

我们首先提出问题:

卓越讲故事的要素是什么?

照片由Product School提供,来源于Unsplash

为了理解数据讲故事的力量,让我们看一个案例研究,展示一个讲述良好的数据故事的关键元素。通过分析一个实际的例子,你将更好地理解有效的数据讲故事的样子,以及如何在你的工作中使用这些技巧。

作为背景,这篇文章的作者使用数字、数据和信息来阐述全球普遍问题的影响以及缺乏行动的负面后果。在这种情况下,利用数据讲述特定的故事,使所呈现的问题变得真实,同时也使提出的解决方案变得既切实又可实现。

在这篇文章中,作者利用数据将全球问题生动呈现,并使提出的解决方案变得切实可行。通过使用数字、数据和信息来说明这些问题的影响及不作为的后果,作者有效地讲述了一个故事,使所呈现的问题变得真实,解决方案也变得可以实现。

解剖一个用数据讲述的好故事

图片来自 Canva

随着全球人口继续增长,特别是在非洲,我们看到了一系列后果,包括经济繁荣、收入差距扩大和财富分配不均。人口规模的增加对一个国家、地区或大陆可能产生各种影响。

Ashley Kirk,曾是《电讯报》的数据记者,在文章非洲在 100 年后的模样中讨论了人口规模的增加对非洲经济的意义。

Ashley 从拉各斯、达喀尔和开罗等主要城市的视角,利用数据和动态可视化将非洲的持续变革生动呈现。讲故事的方式不仅在战略上构建了任何优秀数据驱动叙述的基本结构,还将事实信息、轶事、数据、图表和图形无缝结合,创造出引人入胜且信息丰富的主题叙述。

文章的开头部分描述了情况的背景和要点:

“非洲能否将其庞大的人口增长转化为经济发展和生活质量的提升?”

开篇部分提供了背景并为主要观点奠定了基础,而事实信息和关键日期、数字以及第一手陈述则使叙事扎根于现实中。图表和图形帮助可视化非洲主要城市的转变,突出显示人口增长、教育水平和预期寿命等关键数据点。

文章的中段深入探讨了这些城市面临的冲突和问题,利用数据、轶事和个人经历来描绘挑战和机遇的复杂图景。最终,这篇文章展示了数据实践者如何利用数据讲述一个强有力的人性化故事,与读者产生共鸣。

数据科学中的讲故事不仅仅是呈现事实和数字——它是关于创建一个与观众产生共鸣的相关且卓越的叙事。这就是为什么 Ashley 的文章包含了来自焦点城市中个人的亲身经历和轶事,为数据驱动的分析增添了人性化的色彩。

在整篇文章中,Ashley 还结合了专家访谈,以提供额外的背景和理解这些变化如何影响人们的生活。

Ashley 的文章提出了许多问题,但通过讨论已经实施或正在提议的解决方案,最终达到了高潮。机构采取行动解决教育、气候和冲突等问题的存在,增强了 Ashley 对读者的叙事。

在下一节中,我们将为你提供一个逐步框架,帮助你构建自己的数据驱动故事,以便在下一个演示、文章或视频中有效地向利益相关者展示你的发现。

数据科学讲故事的有效框架

故事讲述的成功可以归结为三个关键要素:背景、叙事和数据。将这些元素结合起来,可以创建一个引人入胜的故事,打动你的观众并推动你的议程。无论你讲述的是事实还是虚构的故事,这些组件都是构建有意义、数据驱动的叙事的必需要素。

有效的数据故事讲述依赖于三个关键要素:背景争议解决方案。这些元素在下面的图像中进行了说明,并为构建引人入胜的故事奠定了基础。在本节中,我们将探讨这些关键组件,并了解如何使用它们来提升你的数据故事讲述技巧。考虑到这些组件作为基本要素,显而易见的是,实践者如何将它们与其他方法结合起来,以创建一个用数据讲述的引人入胜的故事,并提升他们的数据故事讲述技能。

讲故事组件的示意图:背景、争议和解决方案 — 作者图

#1. 背景

有效的数据讲述的第一步是设定背景。没有背景信息,数据可能会令人困惑和误导,导致项目取消和商业损失。

这就是为什么为你的数据提供背景如此重要——它有助于赋予数字意义,并使你的观众更好地理解当前的问题或主题。记住,仅凭数据不足以提供可操作的见解或有意义的解决方案。必须包括其他支持性元素,使你的数据真正具有意义和影响力。

故事讲述中的背景信息是为了强化、支持和揭示从数据样本中提取的关键发现,从而提供视角。 这可以通过多种方式实现,包括使用演员、轶事、可视化、数据标签、图表等。目标是让观众更好地理解数据收集的背景及其与大局的关系。通过包括这些强化材料,你可以帮助你的观众理解数据,并看到你的发现的重要性。

“2021 年,英国销售了 305,300 辆插电式电动车,同比增长约 140%。” — Statista, acea.auto

上述材料很吸引人,但这些数据在整体上意味着什么?没有背景或个人经历,理解这一数字的意义可能很困难。这就是故事讲述的作用。通过使用轶事、可视化和其他支持材料,我们可以赋予数据意义,并帮助观众更深入地与数据连接。让我们用一个名叫詹姆斯的演员来说明这一点。

作者说明:请注意,以下故事是虚构的,用于说明本文所要表达的观点。

“2020 年 2 月,詹姆斯在 M24 高速公路上通勤时,收听了他的早间广播节目《UK today》。节目中的讨论集中在激进分子对石油公司抗议的不断增加,以及向公众普及气候变化的意识。詹姆斯印象深刻的一句话是:“英国的碳足迹是全球平均水平的两倍,这意味着在个人层面上,我们在英国排放的碳气体比地球上大多数人都要多。”

这个说法让詹姆斯记忆犹新,他决定采取行动,或者至少做出自己的贡献。詹姆斯回忆起曾听同事谈论电动车,尽管当时因为缺乏兴趣而没有在意。但现在,詹姆斯的兴趣激增,经过对购买大众 ID.3 所需定金的调查,他做出了购买的决定。于是他购买了一辆。到 2021 年中,詹姆斯拥有了一辆电动车。詹姆斯并不是唯一一个决定采取行动的人;还有数百万名英国居民也采取了行动。无论是通过增加回收努力、减少电力和天然气使用还是购买电动车。英国公众对气候变化影响的意识促进了 2021 年电动车销售的增长。因此,2021 年销售的电动车数量超过了 300,000 辆,比去年增长了一倍多。而詹姆斯就是新电动车车主之一。如今,英国在电动车销售数量方面已成为领先国家之一。”

“未来主义风格的男人给电动车充电” — 图片由作者提供于 MidJourney

增加的背景信息使用了角色和轶事,使初始数据点更具生命力。图表和图示也是提供背景的重要工具,如下图所示。

图表显示了 2021 年选定欧洲国家的插电式电动车销售量。来源:欧洲;Statista;各类来源(国家汽车制造商协会);ACEA;2021(CC BY-ND 4.0)

#2. 争议

讲故事中的争议是你故事的反派,而解决方案则是迎接挑战的英雄。理解争议为需要解决的问题、冲突或难题会更容易。这是你故事的推动力,也是为什么需要解决方案的原因。没有争议,就不需要解决方案,你的故事就会变得平淡。因此,在构建基于数据的叙事时,明确识别争议以及你的解决方案如何解决它。这将帮助你创建一个引人入胜、富有影响力的故事,与观众产生共鸣。

让我们继续创建一个争议,以有效讲述一个故事,并有效地推动初始数据点的重点:

2021 年,英国销售了 305,300 辆插电式电动车,相比去年增长了约 140%。”

“英国是一个化石燃料的净进口国,这些燃料用于能源和电力生产。化石燃料驱动我们的交通、电子和技术服务,甚至是依赖化石燃料能源输出的家庭用品。问题在于,英国决心在 2050 年前显著减少对化石燃料的依赖。因此,问题是英国如何减少化石燃料消耗并转向低碳能源来源。除此之外,化石燃料是气候变化的重大贡献者,导致全球气温升高和极端天气。”

未来主义的图像描绘了化石燃料使用和依赖对世界产生负面影响的场景 — 图片由作者提供于 MidJourney

#3. 解决方案

解决方案是你基于数据讲述故事的核心——它是数据存在的目的,也是背景和争议的原因。在 Ashley Kirk 的文章中,解决方案结合了旨在改善非洲经济的因素的倡议和行动。它包括基础设施发展、改善安全、现代化和国际援助。

解决方案可以有多种形式,例如重新配置现有系统、实施新方法、提高对教育材料的认知。

在数据驱动的故事中展示解决方案时,重要的是要直接、明显和难忘。模糊的解决方案可能会引发更多问题,并削弱你的信心和呈现效果。清晰、可操作的解决方案允许后续步骤,并帮助你的计划在竞争激烈的领域中脱颖而出。是时候不再把自己当作个体,而是将自己视为通过每个项目完成销售自己及其服务的公司。

让我们根据我们正在构建的初步数据点和解决方案将要解决的争议来完成解决方案的制定。以下是初步数据点和争议的提醒。

数据点:

“2021 年,英国售出了 305,300 辆插电式电动车,同比增长约 140%。”

争议:

“英国是一个化石燃料的净进口国,这些燃料用于能源和电力生产。化石燃料驱动我们的交通、电子和技术服务,甚至是依赖化石燃料能源输出的家庭用品。问题在于,英国决心在 2050 年前显著减少对化石燃料的依赖。因此,问题是英国如何减少化石燃料消耗并转向低碳能源来源。除此之外,化石燃料是气候变化的重大贡献者,导致全球气温升高和极端天气。”

以下是提议的解决方案:

“詹姆斯减少碳足迹的旅程始于意识。公众对化石燃料依赖问题及其对环境的损害的认识是实现英国到 2050 年减少化石燃料依赖的国家目标的第一步。为了覆盖像詹姆斯这样的更多人群,我们提议扩展 WWF 碳足迹应用程序,加入 AI 驱动的功能,实现基于历史数据和预测的能源需求的家庭能源消耗预测。这个扩展计划将需要 1 亿英镑的资金,并将在项目批准后一年向公众发布。”

再次说明,我提出的解决方案是虚构的,仅用于说明目的。为了明确,提出的解决方案引用了故事,使其更易记忆。我包括了有关项目成本和时间表的信息,以显示它的直接性。如果我们愿意,我们可以轻易发现我快速整理的这个虚构解决方案中的缺陷,但你应该从中得到的是构成一个理想解决方案的元素:难忘和简单明了。

“数据讲述既是一门艺术,也是一门科学,作为 AI/数据从业者,我们可以将其拆解成易于遵循的步骤。正如广泛讨论的,一个引人注目的数据驱动故事应包括以下组件:

  1. 背景 — 为你的叙述奠定基础并提供一些背景信息。

  2. 争议 — 讨论与背景相关的问题。

  3. 解决方案 — 最终,解释和讨论能够解决或缓解所识别问题的解决方案。

将这些部分整合在一起

图片由 Mourizal Zativa 提供,来源于 Unsplash

总结一下,这是你在编写数据驱动故事时可以遵循的框架:

  1. 开头部分:以一个突显争议影响或解决方案价值的事实陈述开头。用几段文字扩展,介绍和解释背景。

  2. 中间部分:在背景中介绍和扩展争议。使用轶事、事实、数据、图表和图解来说明问题。然后,介绍并扩展有关解决方案的争议。使用轶事、事实、数据、图表和图解展示提出的解决方案的影响和附加价值。

  3. 结束部分:总结时,通过对比现实情况来展示没有解决方案的负面后果和拥有解决方案的好处。包括一个行动呼吁作为下一步,概括用数据讲述的故事的期望结果。

结合有效的数据驱动故事所需的关键组件、元素和考虑因素,你将得到下方所示的图表。

讲故事的组件、元素和考虑因素的完整图示 — 图片由作者提供

当我们在争议和解决方案的旁边提供背景时,我们创建了一个强大的三位一体,帮助个人理解问题、感到有动力去解决问题,并知道如何采取行动。

通过将数据进行情境化并用背景信息支持关键消息,我们可以创造出一个更具信息性和参与感的公众。无论是通过轶事、可视化还是数据标签,目标是提供增强数据驱动故事影响力和理解的背景。

摘要

随着公司、社会和世界变得越来越数据驱动,我们将看到对人工智能可解释性和数据简化的需求上升。所有层次的 AI 和数据行业从业者和专业人士需要培养数据讲故事的技能,以弥合技术领域、数据集和技术与非技术利益相关者、客户以及广泛社会之间的理解差距。

你的数据讲故事之旅并不会在这里结束。还有许多其他方面的技能培养未在本文中涉及,如通过数据可视化、设计考虑、文档等来提升数据讲故事的演示和提案。然而,本文中的三个步骤将为你提供一个坚实的基础,帮助你开始建立数据讲故事的技能。

下一步是将本文中的学习应用到你的下一个项目、实践或组织中。我将在接下来的文章中深入探讨增强数据讲故事演示材料的内容。

希望你觉得这篇文章有用。

要与我联系或找到更多类似于本文的内容,请执行以下操作:

  1. 支持我的写作 通过成为推荐的 Medium 会员

  2. 订阅我的YouTube 频道

  3. 订阅我的播客 Apple Podcasts | Spotify | Audible

  4. 订阅我的电子邮件列表 以获取我的新闻通讯

接下来阅读什么 👇🏾

## Ken Jee 的 数据科学职业建议

关于在人工智能和数据领域内学术及职业路径的建议

towardsdatascience.com

本文的一个版本已发布在 Nvidia 数据科学博客

数据讲故事与动态词云

原文:towardsdatascience.com/data-storytelling-with-animated-word-clouds-1889fdeb97b8

动态词云将经典词云转换为动态可视化。了解更多关于在 Python 中讲述数据故事的信息。

Petr KorabTowards Data Science Petr Korab

·发表于 Towards Data Science ·阅读时间 5 分钟·2023 年 11 月 22 日

--

来源:AnimatedWordCloud 库。图片由作者提供。

引言

动态词云展示了 n-gram(连续文本样本项的序列)随时间变化的绝对频率,以视频文件中的一系列图像呈现。 它给予在源文本中出现频率较高的词语更多的重要性。n-gram 的显示越大越粗体,它在文本中出现的频率就越高。它基于经典词云的直观逻辑,并为可视化添加了时间视角。

由于如今许多文本数据集作为多个时期的文本观察被收集,如何可视化数据随时间的变化成为一个特别的挑战。与其为多个不同的时期制作汇总表或图表,不如准备一个 MP4 视频,讲述故事,吸引观众,并为展示增添“哇”的效果。

本文将描述如何在 Python 中从文本数据生成动态词云。以下是AnimatedWordCloud 库的一些独特功能:

  • 提供所有拉丁字母语言的 n-gram 频率可视化

  • 从标点符号、数字和 NLTK 停用词列表 中清理文本数据集

  • 生成每年或每月的 n-gram 频率。

图片 1. 经济学研究趋势的动态词云

如何使用

要使用该库,请遵循以下步骤:

1. 安装

为项目创建一个新的 Python 3.8 虚拟环境,以避免任何依赖冲突。AnimatedWordCloud 依赖 Python 3.8,因为其可视化 要求。要使用 pip 安装,请使用:

pip install AnimatedWordCloud

在 Pycharm 社区版中进行了测试。建议使用此 IDE,并在 .py 文件中运行代码,而不是在 jupyter notebook 中。

2. 生成帧

我们将关注欧洲中央银行(ECB)的沟通,并了解银行委员会成员在 1997–2023 年期间讨论的概念。数据集来自 ECB 网站,发布时具有灵活的 许可

数据包含 2846 行,并具有 NaN 值,AnimatedWordCloud 可以有效处理。数据如下所示:

图片 2. ECB 演讲数据集

让我们导入数据。

import pandas as pd

data = pd.read_csv('dataset.csv')

然后导入 animated_word_cloud 方法。

编辑 2023 年 12 月:AnimatedWordCloud 已不断更新新参数。查看 PiPy 以获取当前版本。

它提供以美国 (MM/DD/YYYY) 或欧洲风格 (DD/MM/YYYY) 日期和时间格式读取数据。它在输入时自动清理数据中的标点符号和数字。它还可以移除 NLTK 语料库中语言的标准停用词列表。

from AnimatedWordCloud import animated_word_cloud

animated_word_cloud(text=data['contents'],  # Read text column
          time=data['date'],                # Read date column
          date_format = 'us',               # Read dates in US date format
          ngram=1,                          # Show individual word frequencies
          freq='Y',                         # Calculate yearly frequencies
          stopwords=['english', 'french',   # Clean from English, French,
                     'german', 'spanish'])  # German and Spanish stop words

该代码每个周期生成 90 个 PNG 帧,并在工作目录中创建一个 .postprocessing/frames 文件夹来存储这些图像。

3. 从图像创建视频

最后一步是将单独的帧制作成视频文件。此步骤将在未来版本中自动化,但目前:

这里 下载 ffmpeg 文件夹和 frames2video.bat 文件,并将其放入 postprocessing 文件夹中。接下来,运行 frames2video.bat,这将生成一个 wordSwarmOut.mp4 文件,这是所需的输出。

图片 3. 欧洲中央银行家演讲的动画词云

让我们通过中央银行家的视角讲述欧元区的故事:

  • 1999 — 2002 年期间,关键主题是欧元引入 (“加入”、 “欧元”)

  • 2003 — 2006 年间,银行家们主要讨论了标准货币政策实施问题 (“货币”、 “金融”、 “市场”、 “政策”)

  • 在即将到来的金融危机中,2008 — 2012 年的关键主题是 “流动性”、 “危机” 和 “银行”

  • 重要时期出现在 2021 年,COVID-19 经济影响 (“大流行”) 和乌克兰战争导致通货膨胀成为主要话题。

这些发展对任何对欧元历史感兴趣的人来说都是显而易见的。但呈现这些信息是一项具有挑战性的任务。例如,我们可以使用(1)关键词的 n-gram 频率分析,并生成多个关键词频率表,或(2)显示一个热图(或一个矩阵图),x 轴上为时间段,y 轴上为单词,矩阵中的项为单词频率。另一种选择(3)是为每个时期生成多个词云。这些方法都不适合较大的数据集,而动态词云为传递信息提供了另一种选择。

实际应用

该库主要用于演示教学。文本挖掘现在与经济学、政治学或商业等其他学科相融合,教师、分析师和学生现在可以使用一种不同、更具吸引力的方式来呈现事实。

  • 你是一位对科学历史感兴趣的历史学家吗?那么,尝试从像Constellate这样的平台下载文本数据集,如文章标题或期刊摘要,准备一个视频文件,并告诉你的学生 AI 在已发表研究中的历史。从 1900 到 2018 年的经济数据研究趋势分析项目可能会为你提供一些灵感,我们在这篇文章中有描述。

  • 你想展示你的营销团队客户对你产品的看法吗?那就使用来自外部平台(如 Amazon)的产品评论,解释客户提到的词汇。是“快速”“交付”,还是“好”“棒”?还是频繁出现的词汇是“差”“坏”“质量”?讲述一个故事以传达信息,并观察其随时间的变化。

我们与 Jarko Fidrmuc 和 David Štrba 合作的早期 TDS 文章概述了词云如何用于总结探索性文本数据分析。以动态形式展示文本数据集的结构,可以更好、更易理解地呈现时间序列数据。

其他有趣的应用案例,如使用 X-tweets 对COVID-19 相关讨论美国总统辩论进行建模,可能在学士论文或研讨会论文中也很值得检查。

技术说明

WordsSwarm项目中,Michael Kane开发了用于库中词频动画的核心框架。AnimatedWordCloud是我创建的一个Apache-2.0 许可证项目,使代码能够高效地处理各种拉丁字母语言的文本数据集。

它使用了我早期的一个项目,𝐀𝐫𝐚𝐛𝐢𝐜𝐚库,用于处理(文本清理和词频汇总)。它依赖于一些比较古老的可视化需求,这些需求非常适合这个项目。PyBox2D用于群体中单词的物理和碰撞检测。Pyglet(跨平台窗口和多媒体库)以及PyGame用于创建动画。

它显示了绝对词频,但会对数据进行缩放,以便在不同大小的数据集上显示词云。通过这种方式,词频较大的数据集会被一个常数缩放,从而使词云仍然可以在视频中显示。它处理缺失值,并且使用ftfy来处理乱码错误。

另一方面,AnimatedWordCloud可能在 Jupyter Notebook 和其他 IDE(如 Pycharm)中运行时会遇到困难。我将在下一个版本中查看这些不足之处。在未来的版本中,AnimatedWordCloud还将提供双字频率,以便更详细地查看人们在文本中讨论的叙述和主题。同时,我还将扩展库的文档。

PS: 让我知道它在你的项目中效果如何!😃

如果你喜欢我的工作,你可以 请我喝咖啡 并支持我的写作。你也可以订阅我的 邮件列表 以获得我新文章的通知。谢谢!

数据流处理令人兴奋:在跳入之前你需要了解的事项

原文:towardsdatascience.com/data-streaming-is-exciting-what-you-need-to-know-before-jumping-in-e233e192337b

数据流处理适合你的业务吗?需要考虑的关键事实

Chengzhi ZhaoTowards Data Science Chengzhi Zhao

·发表于 Towards Data Science ·阅读时间 5 分钟·2023 年 2 月 27 日

--

照片由 Stephen LeonardiUnsplash 上提供

流数据是数据领域中一个令人兴奋的领域,近年来受到了极大的关注。由于这种兴奋,开源项目的领域变得非常拥挤。许多技术使流数据处理变得比以往更简单:Kafka、Flink、Spark、Storm、Beam 等技术已经在市场上存在多年,并建立了坚实的用户基础。

“让我们做流处理。”

对于数据专业人士来说,这是一个不可避免的话题。然而,在有人告诉你关于流数据之前,我们应该退一步,用一个简单的问题与自己再次确认:我在这个用例中需要流数据吗? 在深入了解之前,让我们面对这个故事中的流数据事实。

流数据

在我们查看流数据的事实之前,让我们先看看什么是流数据。Hadoop 为处理大规模数据集奠定了基础,并赋予了数据专业人士设计更复杂的数据处理工具的能力。

Tyler Akidau 于 2013 年发表的关于 MillWheel: Fault-Tolerant Stream Processing at Internet Scale 的论文为现代流数据奠定了基础,并激发了像 Apache Flink 这样的流处理框架。

泰勒著名的 Streaming 101Streaming 102 以及他的书籍 Streaming System 提供了流处理数据的广泛背景。

当我使用“流处理”这个术语时,你可以安全地假设我指的是一个为无限数据集设计的执行引擎,仅此而已。 — 泰勒·阿基道

让我们遵循泰勒的确切定义,并在整个故事中关注无限数据。

Kappa 与 Lambda 架构

我们都熟悉 Lambda 架构 — 我们使用两个独立的数据处理系统:批处理和流处理,重复编写类似的逻辑并处理相同的数据。流处理用于推测,批处理用于准确性和完整性。

另一方面,我们有 Kappa 架构。我们有一个单一的管道运行,无需重复代码,并利用 Kafka 实现可重放的操作,以便在需要准确性和完整性时使用。

总的来说,Kappa 是一个设计良好的系统的好想法。然而,这样的系统需要将数据处理视为首要任务。

流处理数据并非灵丹妙药。

不久前,对数据处理有一种印象,即“流处理数据是灵丹妙药”,我们都将转向流处理数据。批处理被视为过时的。

一段时间后,人们意识到流处理数据并不是解决问题的灵丹妙药,反而可能会使情况更糟:

  • 流处理不足以生成完整的数据分析数据集。由于数据的迟到或处理错误,仍然需要批处理来弥补这一缺口。

  • 流处理和批处理通常讲的是不同的语言。流处理通常使用 Java、Scala 和 Go 运行,配合 Apache Flink / Kafka Stream 等框架。批处理则通常使用 Python、SQL 和 R 运行,配合 Apache Spark / SQL 引擎等框架。为批处理和流处理重复相同的逻辑是一个麻烦。这是运行 Lambda 架构时最具挑战性的问题之一。

流处理数据的权衡

数据自然是以流处理的方式出现的。最初,批处理似乎不适合解决数据问题,但批处理因其几十年来的声誉而有其存在的理由。批处理是一种简化的哲学,用于解决复杂的问题。

批处理和流处理之间存在显著的权衡。

完整性与推测

许多数据源不可避免地会产生延迟;主要是你的数据分析包括多个数据源。批处理在处理数据完整性方面表现出色,可以在一切准备好后延迟处理。

另一方面,数据流可以通过等待额外的时间,即在内存中保持数据几个小时或一天,来实现这一目标,这样做是昂贵的。流处理也可以提供完整的数据集,但需要上游数据生成器的配合,以解决数据整合和额外延迟的问题。

你的用例的准确 SLA

你需要你的流处理系统处理数据的速度有多快?你的用例可以接受多少延迟?许多 ETL 批处理管道是每天处理的。这对你的业务来说是否太慢了?许多用例受服务水平协议(SLA)的限制。与广告或日间交易不同,几个小时的延迟不会阻止公司正常运营。

流数据不容易维护

迟到的数据

对于任何数据处理系统来说,一个不可避免的事实是:数据到达迟缓。一个设计良好的系统有时可以规避这个问题,但只是偶尔。

在批处理过程中,迟到的数据不是一个大问题,因为数据处理的时间远晚于事件日期,并且 SLA 的要求不严格到分钟或小时。那些从事批处理的人对数据将在 24 小时或更长时间内到达的期望较低。

流处理不是“万无一失”的解决方案——像水印这样的概念为我们处理迟到的数据提供了额外的缓冲。然而,水印是另一种将数据保存在内存中的方式。内存不是免费的:在进一步的点上,水印必须推进,你决定丢弃记录或将其发送到死队列,以便另一个过程重新处理——批处理。

24/7 维护

维护流处理应用程序是很有挑战性的。与批处理不同,你在停机窗口中可以放松,决定是否修复一个错误或喝杯咖啡。

在流数据处理过程中,需要 24/7 的监控和最小的停机时间。你的值班团队必须监控并修复潜在的数据问题,以保持管道的运行。流处理可能听起来很令人兴奋,但作为维护流处理管道的数据工程师,值班需要付出很多努力。

连接数据的复杂性要高得多。

在流处理中,与多个流的数据连接并不是简单的。在批处理时,通过将一组标准键与两个有界表连接,连接非常简单。在流处理中,必须仔细考虑两个无界数据集如何连接。

一个更大的问题出现了:我们如何知道是否还有需要考虑的传入记录?Tyler 的Streaming 102提供了一个很好的示例来演示这一点。简而言之,在不同流之间连接数据比批处理复杂得多。

最后思考

在采用流处理应用程序之前,了解你的用例是否适合流处理是至关重要的。

以流式方式处理数据是令人兴奋和吸引人的。

然而,激动的背后是成本。批处理更为直接,并且已被历史认可数十年。在盲目进入数据流处理之前,理解其优缺点应当被仔细评估。

我希望这个故事对你有所帮助。本文是我的工程与数据科学故事系列中的一部分,目前包括以下内容:

Chengzhi Zhao

Chengzhi Zhao

数据工程与数据科学故事

查看列表53 个故事

你也可以订阅我的新文章或成为推荐的 Medium 会员,享受 Medium 上所有故事的无限访问。

如果有问题或评论,请随时在本故事的评论区留言,或通过LinkedinTwitter与我直接联系。

数据,优化:如何构建更好的产品、工作流程和团队

原文:如何优化数据:构建更好的产品、工作流程和团队

TDS 编辑Towards Data Science TDS 编辑

·

关注 发表在 Towards Data Science · 发送至 通讯 · 3 分钟阅读 · 2023 年 8 月 24 日

--

尽管有许多公司的出现和工具的普及,这些公司的唯一目的是帮助数据从业者实现他们职业的承诺,但可用数据与有用数据之间的差距证明是非常难以弥合的。

事情是怎么发展的呢?可能的罪魁祸首有很多——从过时的基础设施到沟通障碍以及利益相关者的不一致——事情走向岔路的方式也很多。幸运的是,还有一些基本原则可以帮助数据团队变得更加高效:明确的、可衡量的目标以及简化的默认设置是我们发布的数据管理文章中的共同主题。

为了帮助你逐步进入这个偶尔棘手的话题,我们精选了一些优秀的近期贡献,这些作者根据自己的宝贵经验分享了见解和建议。有些文章解决了个体贡献者层面的问题,而其他文章则处理了跨组织的数据操作简化挑战。它们共同的特点是对使团队和项目更顺利运行持有冷静、务实的态度。让我们深入探讨一下吧。

  • 云数据服务的出现对无数公司来说是一个游戏规则改变者。然而,Barr Moses指出,向云端基础设施的迁移并不总是伴随必要的思维转变以适应新的更好的工作流程——但对于那些寻求“现代工具、顶尖人才和最佳实践之间的对齐”的组织而言,改变是触手可及的。

  • 这可能听起来有些违反直觉,但Robert Yi提供了一个有力的案例避免让团队过于僵化地以数据驱动。根据他在 COVID-19 大流行初期的混乱经验,Robert 认为我们应当始终“考虑不同的决策情境”,并根据我们所处的具体环境来利用数据(或不利用)。

图片由Amelia Bartlett提供,来自Unsplash

  • 设计数据项目可能是一项令人生畏的任务:这通常涉及与可能对数据不如你熟悉的合作伙伴打交道,繁琐的工具集成,以及相互竞争的优先事项。Radmila M.提倡采用定制的“问题陈述工作表(PSW)方法”以保持事情的整洁和专注。

  • 从混乱的数据沼泽到过于复杂且难以驾驭的技术栈,公司总能找到各种方式来降低数据的价值。Michał Szudejko 综述了 当前组织挑战的现状,并概述了应对这些挑战的广泛策略:“没有一种正确的方法,但开始这个旅程是值得的。”

从深思熟虑的解释到引人入胜的副项目,在 TDS 上总有许多杰出的文章等你发现;以下是我们作者的一些精选文章:

  • Hennie de Harder 分享了一篇开眼界的(且易于理解的)文章,介绍了 时间复杂性和 NP-困难性的数学基础。

  • 想要详细而耐心的技术教程吗?通过跟随 Cesar Flores 的最新文章,了解 如何构建一个带有记忆微服务的对话代理。

  • 对于 分类问题的初学者友好介绍 和 CatBoost 梯度提升决策树,不要错过 Caroline Arnold 的新文章。

  • 我们可以从个人数据中学到什么? Jeff Braun 的深入探讨提供了从积累数据的公司那里请求数据的技巧,以及生成专业和个人见解的思路。

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

直到下一个 Variable,

TDS 编辑部

数据团队作为支持团队

原文:towardsdatascience.com/data-teams-as-support-teams-2bb1f1ed31b?source=collection_archive---------11-----------------------#2023-01-05

改善沟通、减少摩擦并帮助企业获胜

Chad IsenbergTowards Data Science Chad Isenberg

·

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

--

图片由 Annie Spratt 提供,来源于 Unsplash

在最近的一次部门会议中,我向我们的副总裁提到了我们数据团队的一个痛点。我们的客户似乎不知道或不在乎他们的请求给我们带来了多大的负担。大多数从事数据工作的人都曾与利益相关者讨论过,要求的工作量是几天甚至几周的工作,但期望却认为这是微不足道的。此外,在付出了大量努力后,我们发现最初的请求要么是不必要的,要么与客户的实际问题无关。我的论点是,数据团队与客户之间存在激励不匹配的问题。

我的副总裁有不同的观点。他建议我们的业务伙伴应该专注于他们最擅长的领域,当他们需要数据来执行他们的愿景时,他们应该向我们提出请求。他们不应该决定他们需要的东西是否“过于昂贵”或“过于耗时”。

我的副总裁说得也有道理。我怀疑任何使用过内部服务组织如人力资源、法律、IT 或采购的我们,都没有考虑过我们请求的复杂性或成本。申请贷款的收入证明可能需要多长时间?替换我坏掉的笔记本电脑需要什么?这些问题完全由各自的组织吸收,企业整体运行也因此更高效。

尽管考虑了这一切,我仍然无法摆脱数据是不同的这种感觉。我并不是说数据作为一个领域通常比其他支持功能更困难、更有价值或更复杂;恰恰相反,我认为由于这个领域相对不成熟,很多过程和概念仍然缺失或半成品。总体而言,我认为数据团队往往有弱的操作模型,支持可疑的价值主张。在核心上,存在两个缺陷:数据模型和分析价值主张。

数据建模

我非常喜欢Chad Sanderson,他不断推动企业优先考虑数据建模,这是我核心信念之一。需要明确的是,数据建模总是存在的;无论是共享的(在数据仓库或数据湖中完成)还是个人的(在单个报告或仪表板中)都是根本问题。缺乏核心数据模型导致对业务有多个理解,这种情况会导致团队之间的错位。

此外,缺乏明确定义的语义,使得数据与其支撑的业务过程之间的关系变得极其难以理解。即使是经验丰富的分析师也可能花费大量时间尝试理顺过程、数据和分析输出之间的错位。当大多数公司处理数十个或数百个来源,其中许多是外部维护的,并且所有这些都有独立(操作)的数据模型时,我们经常无法描绘出业务的连贯图景,难道不奇怪吗?

合理的数据建模是产品(适用时)、运营和数据团队之间的复杂工作。大多数企业对这一关键要素投资不足,甚至完全忽视。我们没有在前期做出协作决定,而是在后期做出个别决定;工程师和分析师的时间被用于为每个资产、报告和仪表板重新发明轮子。作为数据专业人员,我们感受到这种痛苦,而我们的客户也因为糟糕(或不存在)的时间价值体验到这种痛苦。

分析的价值主张

依我经验,许多客户不知道数据能回答什么问题,更重要的是,他们不知道如何将业务成果与数据资产联系起来。XY 问题¹ 在与数据团队交流时成了商业团队的共通语言。

在一定程度上,核心数据模型的缺失加剧了我们客户的困惑。他们对数据看起来“必须”是什么样子(通常但并不总是很合理的假设),因为他们不是生活在表格和管道中的人,所以他们经常是错的。即使我们的客户知道存在哪些数据资产,他们也不一定有专业知识去理解它们是否可以应用于他们的具体用例。

作为一个行业,我们面临的更棘手的问题是,我们对于分析输出的价值有着非常脆弱的理解,而且我们对于任何特定输出所产生的价值又了解更少。我们“知道”将数据提供给企业是有价值的(看看 FAANG+的成功),而且有研究表明利用数据的公司比其同行更有效²,但具体的机制却知之甚少。

基本上,我们的客户从来没有真正有机会提出好的要求,至少没有持续地这样做。他们也不应该(也不需要)了解他们组织的数据资产的细节,和专家一样,他们对于他们想要的报告是否真正带来了实际的商业价值一无所知。他们可以整天要求报告,但不会有人知道数据团队的时间是否合理利用。这种缺乏反馈循环令数据团队感到沮丧,也会让客户觉得他们没有得到他们所要求的东西:不是因为交付物与规范不符合,而是因为所要求的交付物本身其实从一开始就无法创造价值

数据不是信息

所有这些痛苦的核心是非常简单的事情。数据不是信息。数据不是洞察力。我见过很多商业人士花费大量宝贵的时间尝试做他们自己的数据分析。盯着电子表格不停地进行数据透视和过滤并不能产生价值。

Robert Yi 有一篇关于对齐的精彩文章³,比我能做的更好地覆盖了这个话题,但我还是想尝试添加一些想法。数据资产深深陷入了只有专家才能成功导航的背景中。数字从来不仅仅是数字,这就是为什么分析工作具有欺骗性难度。就像试图跟随一张地图,却发现有些地方的道路未被标出,而有些地方则有错误的道路。“哦,对了。旧主楼两年前已经退役了,但我们决定将其保留在地图上,以便你理解为什么主街会有这样的走向。”旅行者将会陷入困境。

我们最终会得到这些模糊的地图,因为(混合比喻)制作过程特别混乱。分析是一个非常复杂的过程,其中 1)业务获取信息(预订、账单、购买)2)这些信息通过产品和企业系统编码为数据(账户对象上的记录,库存表中的记录)3)这些数据最终提取到分析数据存储中,并重新组装为信息。最后一步总是更多的是艺术而非科学。

这是一场我们做得相当糟糕的电话游戏。语义在过程中丢失了。当我们尝试将不同来源的对象模型结合起来时,我们未能理解它们之间的重要区别。人类会随着时间调整业务流程中的细微偏差,但我们的分析系统却不会。

但我们可以做一些事情,我对未来充满乐观。特别是,我认为我们应该关注以下几点:

  1. 改进我们对哪些分析活动能驱动价值的理解。研究机构是这一部分的一部分,但在此期间我们可以进行大量的实地工作。正如产品公司进行实验以将功能与价值(减少流失、扩展)联系起来一样,数据团队也应该开始投资于实验框架。我们需要知道我们的数据产品是受欢迎的(来自客户反馈)和有价值的(来自指标)。

  2. 改进我们的数据模型。通过将我们的核心数据资产与业务更紧密地对齐,我们可以更敏捷地回答客户的问题。由于分析通常依赖时间,能够在几天内而非几周内提供答案是提供大量价值和没有任何价值之间的区别。

  3. 改善我们的客户关系。我们需要提醒自己,客户在寻求帮助时不应期望理解我们极其复杂的系统。他们不能因为分析尚未解决而受到责备。与此同时,他们需要更加参与我们的工作及其挑战。我们不能仅仅翻白眼说,“这是一大堆工作,”他们也不能在我们详细说明时心不在焉:“因为你要求的领域只存在于我们四个账单系统中的一个。”

  4. 改进我们的操作模式。我们需要开发系统以便在客户请求的纷扰中识别有价值的数据工作,有效地沟通需求,并在交付中增加问责制和反馈。我们需要赋予客户提出正确问题的能力,确保我们正在处理重要的事情,并确保我们交付的内容提供了价值,如果没有,能够辨别出流程中出现了什么问题:仅仅是一个“坏”请求?需求不充分?需求理解错误?工作质量差?

结论 — 团队中没有“我”(也没有数据中的“我”)

尤其在这些困难时期,当我们看到我们的同行失败时,很容易指责并感到沮丧。归根结底,我们都是一体的。数据团队希望我们的公司成功,这意味着我们需要我们的业务伙伴成功。同样,他们也关心我们的成功。我们都需要提醒自己,我们在同一个团队中,面临相同的挑战,并在克服这些挑战中各自扮演角色。

参考文献

[1] 维基百科贡献者,XY 问题(2022 年 12 月 11 日),维基百科,自由百科全书

[2] Weill, Peter 和 Woerner, Stephanie L., 仪表板的回报(2022 年 1 月),MIT CISR 研究简报第 XXII-1 号

[3] Yi, Robert,沟通是关于对齐的(2022 年 12 月),Win with Data

数据告诉我们“是什么”,而我们总是寻求“为什么”

原文:towardsdatascience.com/data-tells-us-what-and-we-always-seek-for-why-66da7dc3f24d

照片由凯莉·西克马Unsplash上拍摄

《为什么的书》第一章和第二章,“与我共读”系列

朱子静,博士Towards Data Science 朱子静,博士

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

--

在我的上一篇文章中,我启动了“与我共读”书友会,探索犹太·珀尔的《为什么的书》。我想感谢每一位表现出兴趣并报名参加俱乐部的朋友。我希望我们能通过一起阅读和分享见解,深入理解因果关系。两周后,正如承诺的,我将分享我从前两章中获得的一些关键点。

在这两章中,犹太·珀尔首先解释了因果性阶梯,并回顾了因果理论的发展历史。我们将进一步深入探讨这三个层级。

因果性阶梯源自于犹太·珀尔

第一层级:关联

回到 1800 年,从 Galton 到 Pearson,当他们试图理解人类如何继承遗传特征时,他们发现相关性在科学上是足够的。毕竟,“数据即科学的一切”。对他们来说,因果关系只是相关性的一种特殊情况,永远无法证明。另一方面,相关性足够强大,可以解释为什么较高父亲的儿子比人口平均值更高。基于相关性的预测模型通过识别最具预测性的变量来对目标进行预测,即使在许多情况下这可能没有意义。例如,一个国家的人均巧克力消费量与其诺贝尔奖获奖者人数之间有强烈的相关性。显然,吃更多巧克力不会增加你获得诺贝尔奖的机会,而一个国家的财富更可能是这里的混杂因素。我们可以找到很多类似的例子,这些例子并未提供有意义和科学的信息。当面对这些发现时,Pearson 将其视为“虚假的”相关性。

除了“虚假的”相关性外,还常常发现总体中的相关性在子群体中发生反转。例如,在测量颅骨长度和宽度之间的相关性时,单独在男性和女性群体中测量时,相关性微不足道。然而,合并性别组时,这种相关性是显著的。这种情况在存在混杂因素时很常见,我们称之为辛普森悖论。

[## 被称为“辛普森悖论”

我们因为失去而变得更好

zzhu17.medium.com](https://zzhu17.medium.com/the-so-called-simpsons-paradox-6d0efdca6fdc?source=post_page-----66da7dc3f24d--------------------------------)

尽管存在这些不足,基于相关性的预测模型在静态设置中仍能取得不错的准确度。统计学家们开发了许多复杂的模型来从数据中提取洞见。随着数据样本的增加和算法的复杂化,我们可以构建出不错的模型来预测销售、客户流失/保留等。然而,当面对新情况时,基于相关性的模型由于缺乏历史数据,无法做出可信的预测。为了保持良好的性能,模型开发者必须不断地向模型提供越来越多的数据,明确涵盖所有现有情况,这总是滞后于新的设置。正如 Pearl 所写:

任何在因果关系阶梯的第一级运作的系统都不可避免地缺乏灵活性和适应性。

第 2 层:干预

超越“看到它是什么”,我们正在向“改变它是什么”迈进。如果我提高这个产品的价格,这个产品的销售会有什么变化?如果我停止给这位客户优惠,他还会继续订阅吗?第 2 层的问题不能再通过基于相关性的模型来回答。例如,当我们使用观察数据找出价格和销售之间的过去相关性时,这可能会受到宏观经济情况、其他产品的销售和价格等因素的偏倚。观察数据显示的是许多因素对销售的组合影响,而不是价格对销售的纯粹影响。这些因素中的一些可以被观察和量化,但有些很难量化,这使我们无法获得纯粹的影响。

第一个到达第 2 层的研究者是塞沃尔·赖特,他绘制了第一个路径图,展示了导致豚鼠毛色的因素。该图不仅列出了所有因素,还指导了如何估计每个因素的强度。要回答第 2 层的问题,除了第 1 层收集的数据,我们还需要因果假设,如赖特的路径图。下图展示了赖特提出的因果图,用于估计豚鼠的出生体重。

作者提供的图像,参见《为什么的书》

因果图的好处在于我们可以隔离每个因素的影响。例如,孕期对出生体重的纯粹影响是什么?即使有些因素我们无法量化或未被观察到,我们可以提出与不可量化因素密切相关的其他因素,并在因果图中适当地估计它们的影响。工具变量的概念源于此。

作者提供的图像

然而,尽管赖特在 1920 年代提出了这种因果图的想法,但这个理论从未被主流接受。朱迪亚将这种情况与伽利略声称地球围绕太阳旋转的情况进行了比较。反对意见来自于“数据在所有情况下优于意见和解释”这一规范。毕竟,数据是客观的,而意见是主观的。然而,正如朱迪亚所写:

关于因果关系,一点智慧的主观性比任何量的客观性更能揭示现实世界。

事情直到 1960 年代才开始发生变化,当时经济学家和社会学家开始使用结构方程模型(SEM)。这些模型将因果关系嵌入线性代数中。在结构方程中,有些变量被指定为内生变量,这些变量与目标因果相关,而其他变量被称为外生变量。包括内生变量有助于解决绘制因果结论时的混淆问题。唯一的缺点是这些模型中存在的因果关系是非方向性的。因此,它仍然无法解决像“是否 y 导致 x 而不是 x 导致 y”这样的问题。

图片由马库斯·乌尔本茨提供,来自Unsplash

在突破数据主导的理论中的另一步进展是贝叶斯理论的出现,它提供了一种将观察数据与主观先验知识相结合的客观方法。贝叶斯统计以主观的先验信念开始,例如硬币不公平,然后通过收集新证据——多次抛掷硬币——不断修正信念。如果我们看到正面大约出现一半的时间,则修改信念认为硬币应该是公平的。由于主观性的包含,因果理论从贝叶斯概率开始,然后绕了一大圈,这个故事将在接下来的章节中分享。

第 3 层:反事实

信不信由你,回到我们的智人祖先,他们在计划猛犸象狩猎时,已经在心中有一个关于狩猎成功率的心理因果模型:

作者所指的图片参见《为什么的书》。

心理模型展示了成功率的不同原因,这就是想象力发生的地方。它帮助我们的祖先通过对模型进行局部调整来尝试不同的情境。例如,如果我们增加猎人的数量,成功率能提高多少?如果猛犸象的体型比平常大,我们需要增加多少猎人以保持相同的成功率?想象力的能力使我们达到了因果关系的最高层级,即第 3 层。

尤瓦尔·赫拉利在他的《人类简史》中定义了认知革命,它表现为描绘虚构生物的能力。斯塔德尔洞穴的狮子人雕像由考古学家发现,展示了智人新发展出的能力,想象一个不存在的“半人半狮”的生物。想象能力使人类与其他生物区别开来,就像制造反事实将人类智力与动物和人工智能区分开来一样。

图片来源于 Mohamed NohassiUnsplash

反事实与数据之间的关系尤其复杂,因为数据是事实,而反事实是想象。数据无法告诉我们在一个某些已观察事实被简单否定的虚拟世界中会发生什么。另一方面,因果模型可以通过帮助我们推理可能发生的事情来引导我们。在商业世界中,这些“如果”代表了最具盈利性的问题——如果我提高了价格,产品销售会发生什么?正确回答这些问题将指导我们自信地做出任何决策。

数据告诉我们“什么”,而我们找出“为什么”。我们大多数人都听说过《创世纪》中提到的伊甸园。当上帝发现亚当藏在园中时,发生了以下对话:

上帝: 你吃了我禁戒的那棵树上的果子吗?

亚当: 你给我做伴侣的女人,她给了我树上的果子,我就吃了。

上帝(对夏娃): 你做了什么?

夏娃: 蛇欺骗了我,我就吃了。

上帝问 什么,但他们回答 为什么。上帝要求 事实,但他们回答 解释。 他们的直觉是,命名原因会缓解他们行为的后果。数据是客观的事实,人类主观地解释这些数据以得出结论并指导进一步的行动。寻找“为什么”在“什么”背后的意义 是人类智慧的本能,而这正是人工智能所缺乏的。没有因果结构——即人类主观思维过程的紧凑表示——人工智能无法具备人类水平的智慧。没有这些,人工智能模型仅仅是在获得类似动物的能力,就像我在我的 上一篇文章 中给出的猫的例子。

这就是我想分享的《为何的书》第一个和第二个章节的内容。第一章和第二章通过介绍因果性阶梯和整个理论的发展历史奠定了书的基础。我预见到后续章节将包含更多技术细节和行业应用。别忘了 订阅我的邮件列表 来参与后续讨论:

我分享的内容还有很多细节。像往常一样,我非常鼓励你阅读、思考并分享你在这里或在你的个人博客文章中的主要收获。

感谢阅读。如果你喜欢这篇文章,请不要忘记:

参考

《为什么的书》 由朱迪亚·珀尔撰写

因果性阶梯照片:

[1] 机器人照片由 Rock’n Roll Monkey 提供, Unsplash

[2] 猫照片由 Raoul Droog 提供, Unsplash

[3] 干预照片由 British Library 提供, Unsplash

[4] 人类下棋照片由 JESHOOTS.COM 提供, Unsplash

你是数据票务处理者还是决策制定者?

原文:towardsdatascience.com/data-ticket-takers-vs-decision-makers-a6cf957b507a?source=collection_archive---------1-----------------------#2023-06-02

反应型与主动型数据团队的特点与价值

Barr MosesTowards Data Science Barr Moses

·

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

--

图片由作者提供。

从根本上说,世界上有两种不同类型的数据团队。一个是对组织的需求作出反应的团队,另一个是主动引导组织满足其需求的团队。

前者有帮助,但只是一个成本中心。后者是价值创造者。在当前经济条件下,你希望成为哪一种?

这并不是说数据团队不应该解决票务问题或处理临时请求。我们彼此都要负责,健康的组织需要相互配合。

但尽管有其缺陷,现代数据堆栈以其速度和规模为数据团队提供了前所未有的机会来设定议程和塑造组织的命运。

这里有四种方式,我见过数据领导者抓住机会并从后台过渡到前台。

喧闹的轮子 vs. 组织优先级

我还没有遇到过一个数据团队的能力超出其利益相关者需求。工作的性质要求无情的优先排序。

一些数据领导者会陷入根据谁最吵或哪个领域请求最多来集中时间和资源的陷阱。虽然这稍微偏离了字面上的“处理请求”,但让“市场决定”有其合理的理由。

毕竟,推理是,那些最频繁和密集地请求数据的人,是否会真正善加利用,并从数据团队投入的时间中获得最大价值?

不对。

首先,这种方法将所有数据价值创造的责任交给那些非数据专家的人。其次,最重要的利益相关者,你的组织的客户,无法直接通过 Slack 向你的分析师“快速提取一些数据。”

在理想情况下,数据团队会评估和衡量每个主要数据用例的价值。他们会根据任务的投资回报率做出纯粹理性的决策。

在现实世界中,这种黑白分明的数学在试图计算仪表盘和数据素养倡议的价值时会很快崩溃。我对数据领导者的建议是紧密关注收入、客户和组织目标。

这就是网络安全公司 Drata 的数据副总裁Lior Solomon在他曾经的 Vimeo 数据领导角色以及目前角色中所做的。

在 Vimeo,他通过密切跟踪和确保业务指标数据的质量,能够优先考虑数据资源并合理分配预算。由于 Vimeo 即将上市,准确监控业务指标对公司增长和成功至关重要。

在 Drata 的要求不同。他在构建数据堆栈时是从零开始,数据不像在 Vimeo 那样面向客户。在这种情况下,他将注意力集中在快速、切实的成果上,例如与市场部门合作提高广告活动绩效。

他还紧密围绕 Drata 的使命,帮助客户尽快达到完全合规,通过构建系统来分析和监控哪些控制措施阻碍了不同客户群体实现完全合规。

跟踪指标 vs. 设定指标

数据票据处理员由他们的消费者告知关键指标。他们被要求开发跟踪这些指标变化的报告。团队的职责是创建和维护仪表盘和视图。

数据决策者将仪表盘的创建和维护视为必要的门槛,然后暂时放置,以腾出空间进行价值生成活动。

“我的座右铭是,我不希望通过我生成的报告数量来衡量我的价值,因为那样我就会变成 IT 的一个延伸,”Lior 说。

“我们的数据使命声明与 Drata 的使命相一致:‘我们的使命是建立网络信任’,”他强调道。“我们致力于将数据作为一种强大的工具,用于在我们的组织和整个数字领域中培养信任和可靠性。通过利用数据的潜力,我们努力促进透明度、安全性和问责制,最终为我们的客户和合作伙伴建立一个值得信赖的环境。”

为了实现这一目标,Alex Tverdohleb(福克斯网络数据服务副总裁)等数据领导者优先建立数据自服务基础设施。几年前,我的团队与 Alex 坐下来讨论了他自己在建立数据团队方面的经验

“如果你考虑一个集中化的数据报告结构,过去你是提交一个请求,等待你的轮次,到你得到答案的时候,往往已经太晚了,”Alex 说。“我们提供数据源,并保证其可信赖性……所以请随意使用它。”

主动的数据领导者不是建立报告指标的团队,而是建立发现和实验业务新增长驱动因素(指标)的团队。他们从“发生了什么?” 转变为 “为什么会发生?” 和 “如果我们这样做,我们认为会发生什么?”

数据领导者可以帮助他们的团队开始这一过渡的一个方法是将每一个临时请求视为增加价值的机会,这一点John Steinmetz(医疗保健调度和资质管理公司 Shiftkey 的数据与分析副总裁)深知其要义。我去年有幸与 John 交谈过,讨论了他在建立更积极的数据团队方面的经历

“我也给我的团队一个任务:不要仅仅满足于给利益相关者他们要求的内容。总是要给他们更多,”他说。“我告诉我的团队,当他们收到临时请求时,不要只是满足于给利益相关者他们要求的内容——要搞清楚他们为什么需要这些数据,给他们他们要求的内容,并提供一条你认为能更好地解决他们问题的信息。随着时间的推移,他们会把你看作不仅仅是一个列表的提供者。”

报警问题与警示利益相关者

这是我经常看到的场景。糟糕的数据传递给公司领导,甚至更糟,传递给客户。请求从上而下传达:“这些数字为什么看起来有点奇怪?”

火警演练时间到!团队中最优秀的数据工程师和系统专家被抽调出他们的项目来排查和分析根本原因。几天之后,仍未找出具体原因,大家又回到各自的项目中,等待下周的火警演练。

我还听到数据领导者讲述他们的收件箱经常被数据消费者的问题填满。实际上,那些消费者已经充当了手动数据监控者,并向数据团队中的每一个人发出警报,这些警报现在变成了工单。

随着工程资源越来越分散,团队在数据测试上花费的时间也越来越少,这个故事变得越来越常见。

主动的数据领导者不仅会优先处理,而且还会衡量他们的团队在多大程度上是第一个知道任何数据质量问题的人。他们认识到两点:1) 数据信任是他们最重要的货币,每一个未被标记的问题都是从他们的账户中提取的一笔钱,以及 2) 这可能是数据团队被视为问题还是解决方案之间的区别。

一位财富 500 强公司的数据领导者这样向我描述了从反应性到主动的数据质量过渡:

“我们每天发送报告,以更新高管关于业务各个方面的情况。如果这些报告中的数据有误,我会在早上第一时间听到这件事,这是一种最糟糕的开局方式…[最近],我收到了一条量警报,并迅速给[业务利益相关者]发送了电子邮件,仅仅说,‘我们现在有问题,但我们正在解决中。请暂时不要发送你的日报。’她对我们积极主动的举措非常感激。这是一个[业务利益相关者]知道我们支持她的时刻。感觉很好——我觉得自己是在提供服务,而不仅仅是解决她的问题。”

这一过渡需要一些前提条件。数据团队需要有一个数据质量监控系统;一种理解事件将如何影响组织内的内容和人员的方法;以及通知这些利益相关者的过程。快速的 Slack 提醒可以完成这项工作,但我见过的最好的解决方案是通过数据可观测性/数据目录集成或定制构建的解决方案在 BI 仪表板上放置通知。

顶尖的数据团队会在‘首个知情’的速率之上,与利益相关者合作提前定义所需的可靠性水平(什么实际构成事件)并在数据 SLA中明确这些期望。

成本与投资

被动的数据团队报告他们的输出,通常用技术术语框定。例如,“我们已经建立了 X 条数据管道,”或者“我们已将延迟减少了 25%。”他们以相同的方式提出预算请求。“我们需要 X 以便做 Y。”

结果,团队被视为成本中心,他们的预算请求(现在甚至包括人员需求)被视为需要最小化的非必要项。在首席财务官的压力下,“如果他们真的需要这个”,他们要么放弃这个计划,要么转向拼凑各种零散的开源解决方案,如同弗兰肯斯坦的怪物。

这可能迅速变成一个恶性循环,因为被动的数据团队花费更多时间——实际上超过 50%——来保持他们的弗兰肯斯坦基础设施的功能性,而不是增加价值。他们变成了数据水管工,而不是数据工程师。

根据Adam Woods,数字广告平台 Choozle 的首席执行官,数据团队在尝试采取更主动的方式时,应考虑他们的技术决策如何影响团队生产力——最重要的是,如何推动业务增长。

“我理解转向开源的本能,但实际上我在使用[某些现代数据堆栈工具]时拥有更低的拥有成本,因为管理负担非常小,而且生态系统配合得很好,”Adam 说道。“我们能够将开发人员和数据库分析师原本花在担心更新和基础设施上的时间重新投入到构建卓越的客户体验中。”

主动的数据领导者没有成本,他们只有投资。 两者之间的区别在于,投资是有可追溯性的,并且有回报。虽然对于更广泛的平台或基础设施投资,两者都可能难以计算,但主动的数据领导者会在实施前后花时间建立商业案例。

对于许多公司而言,预算不是给予的,而是赢得的。 因此,这些数据团队几乎总是有足够的资源,因为 CFO 理解这一项目项的背景,“通过使我们的面向客户的机器学习模型更准确来提高 5%的收入。”

积极的数据团队还通过关注业务价值来确保这些投资获得回报。更多时候,这意味着通过向客户展示数据来实现数据的货币化。

数据团队应该与其合作,而不是为其建设。

请不要把这些例子误认为是对你的业务利益相关者下达命令的许可。 了解何时领导,何时跟随是有帮助的——与业务团队合作并共同引领前进更为出色。

如果你觉得这篇文章有帮助,并希望我进一步探讨孤岛与平台、使能与强制、囤积与策划以及其他那些刚刚错过的积极数据团队的例子,请告诉我。

在此之前,我给你留一句话,这反映了我对数据领导者的最常见建议:对客户影响要有执着。前 Gitlab 数据与分析高级总监 Rob Parker 对此了解颇深。事实上,Gitlab 将其公司手册开源,以确保客户对他们的愿景、业务优先级和 KPI 有全面的了解。

Rob 说:“作为一个嵌入在业务中、没有直接影响或与客户互动的团队,很容易忽视我们工作的意义。所以我们尽量考虑我们客户的客户。我们已经能够从典型的接单者转变为在为业务构建可扩展和可靠的解决方案的过程中成为值得信赖的业务合作伙伴。”

那么,你会选择哪条路?

联系 Barr 在 LinkedIn 上 **以表达你的挫败感、发送数据搞笑图或分享你在应对这一危机过程中的经历。总是很高兴联系

现代数据科学家的数据版本控制:你不能忽视的 7 个 DVC 概念

原文:towardsdatascience.com/data-version-control-for-the-modern-data-scientist-7-dvc-concepts-you-cant-ignore-bb2433ccec88

数据科学中一种必备实践的详细插图指南

Bex T.Towards Data Science Bex T.

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

--

图片由我使用 Midjourney 制作。

2020 年 5 月 31 日

多么美好的一天啊!我正坐在车里听着Data Beats FM,这时一个广告引起了我的注意。

数据科学家们对软件工程师羡慕已久。虽然纯粹的软件工程师——我们戏称他们为邪恶的编程巫师——在可信赖的 Git 下轻松处理代码提交和分支,我们却常常不得不与巨大的数据集和 ML 模型搏斗,让 Git 无法招架。但不用担心,DVC 以闪亮的铠甲出现了…

我把音量调高,脸上带着好奇的表情。

DVC 以其卓越的能力,解放了成千上万的数据科学家摆脱 Git 在处理那些似乎无论如何都无法有效版本控制的大文件时的挣扎(Git-LFS 简直糟糕透了)。

我此时面带微笑。

现在是告别疯狂删除和重新上传数据以规避 Git 大小限制的日子的时候了。有了 DVC,我们终于可以恢复理智,自信地推进我们的数据驱动工作。

你本可以数清我的所有牙齿。

免费查看请访问…

我进入了隧道,信号丢失。几乎喜极而泣,我在隧道出口处立即停车,拿出旅行中的 MacBook。我在搜索框中输入 DVC 并…

这段文字摘自一位虚构数据科学家的虚构日记。日记中详细描述了使用 DVC 进行数据版本控制的乐趣,以及它如何巧妙地解决了 Git 在处理大文件时的难题。

我决定跳过那部分,展示其余内容,其中我们的虚拟朋友解释了八个最重要的 DVC 概念,没有自尊的数据科学家是无法忽视的。我必须说,这些解释非常精彩 😃

让我们继续阅读。

0. DVC 作为版本控制系统

当你开始使用 DVC 时,它并不是一个版本控制系统。相反,它模仿 Git 对大文件的功能,同时作为一个与 Git 并行运行的元数据管理工具。

DVC 并不是直接处理大量数据集和 ML 模型(大型资产),而是为它们生成元数据文件占位符。这些轻量级文件允许你在开始用 Git 跟踪它们时进行伪跟踪。

比如,假设你刚刚编写了一个脚本来应用图像增强,从而增加了图像数据集中的样本数量。

由于 DVC 已经为包含你的图像的文件夹创建了一个占位符文件,它会及时检测到操作并相应地更新元数据文件。随后,由于 Git 正在跟踪那个元数据文件,它也会识别到这个变化。

通过这种方式,大文件的修改可以成为你的 Git 提交和历史的一部分,而不会导致 Git 崩溃。

稍后会详细讲解。

1. 跟踪和未跟踪文件

当在一个 Git 跟踪的仓库中初始化 DVC时,文件可以被分类为三类。

类别 0 包含仅由 Git 使用 git add filename 命令跟踪的文件。DVC 与这些文件无关。通常,它们包括源代码文件(.py.jl.R 等)、配置文件(.ini.cfg.yaml.json)、文档文件(Markdown、README 和其他文本格式)或测试文件。

图片由我提供

类别 1 包括 .gitignore 文件中列出的文件。这些是 Git 永远不会跟踪的文件。

在类别 2 中,我们有 DVC 跟踪的文件,这些文件是使用 dvc add asset 命令的结果。

作为一个例子,我们考虑一个名为 tuned.joblib 的模型文件和存储在 images 文件夹中的图像数据集。一旦你执行 dvc add images tuned.joblib,该文件和文件夹都会被 DVC 监控,并添加到 .gitignore 文件中,以防止意外的 Git 跟踪。

此外,还会生成两个 .dvc 扩展名的新文件:images.dvctuned.joblib.dvc

这些轻量级的元数据文件属于类别 0,这意味着你应该使用 git add images.dvc tuned.joblib.dvc 命令跟踪它们。

类别 2 中典型的文件包括笔记本(可能包含视觉和表格,导致文件较大)、数据集(如图像、视频、音频、CSV、parquets、数据库等)和 ML 模型。

最后,在类别 3 中,我们有可选的 .dvcignore 文件。类似于 .gitignore 文件,你可以使用它们来指定 DVC 永远不应该跟踪的文件。

2. .dvc 文件

.dvc文件使用 YAML 1.2文件格式,这是一种对所有编程语言友好的数据序列化格式。

正如我之前提到的,DVC 为每个用 DVC 跟踪的文件或文件夹创建一个轻量级的.dvc文件。

当你查看images.dvc的内容时,你将看到以下条目:

图片由我提供

最有趣的部分是md5。MD5 是一种流行的哈希函数。它接受任意大小的文件,并使用其内容生成一个固定长度的字符字符串(在我们的例子中是 32 个字符)。

这些字符可能看起来随机,但如果你多次重新运行哈希函数在文件上,它们将始终保持相同。但是,即使文件中的一个比特发生变化,生成的哈希值也会完全不同。

DVC 使用这些哈希(也称为校验和)来区分两个文件是否相同、完全不同或是同一文件的不同版本。

例如,如果我将一个新的假图片添加到images文件夹中,images.dvc中的 MD5 哈希值将会不同:

图片由我提供

如前所述,你应该用 Git 跟踪所有.dvc文件,这样对大型资产的修改将成为你 Git 提交和历史记录的一部分。

$ git add images.dvc

了解更多关于.dvc文件如何工作的内容,请参考DVC 用户指南此页面

3. DVC 缓存

当你在一个大型资产上调用dvc add时,它会被复制到一个特殊的目录中,称为 DVC 缓存,位于.dvc/cache下。

缓存是 DVC 在不同版本下保持数据和模型原始记录的地方。当前工作目录中的.dvc文件可能显示最新的或其他版本的大型资产,但缓存将包含自你开始用 DVC 跟踪它们以来,所有资产的不同状态。

例如,假设你将一个 1 GB 的data.csv文件添加到 DVC 中。默认情况下,该文件将同时出现在你的工作区和.dvc/cache文件夹中,占用两倍的空间(2 GB)。

图片由我提供

任何后续通过dvc add data.csv跟踪的更改都会在.dvc/cache中创建一个新版本的data.csv,并占用另外一个 GB 的内存。

所以,你可能会问——这不是效率非常低吗?答案是肯定的!至少对于单个文件来说是如此,但我们将在下一部分看到缓解这一问题的方法。

对于文件夹,情况有点不同。

当你使用dvc add dirname跟踪不同版本的文件夹时,DVC 足够智能,只检测目录内发生变化的文件。这意味着,除非你更新目录中的每一个文件,否则 DVC 将只缓存变化文件的版本,这不会占用太多空间。

总之,可以将 DVC 缓存视为Git 的暂存区的对应物。

了解更多关于缓存和内部 DVC 文件的信息,请访问本用户指南部分

4. DVC 中的缓存策略

DVC 中有三种缓存策略。

我们已经看过策略 0,它简单地将跟踪的文件复制到缓存中。这是最安全且始终可用的选项,即使它很慢且内存效率低。但它允许你在原地编辑跟踪的文件,因为它们在你的工作区中物理存在。

在策略 1 中,DVC 使用硬链接将缓存中的大型资产链接到你的工作区,而无需重复它们。可以将硬链接视为跟踪文件的双胞胎。

这就像是同一个房间有多个门,但它们仍然是同一个空间。每扇门(或链接)提供对房间(或文件)的访问,而无需重复其内容。

第二种策略,创建软链接,实际上和硬链接类似。不过,如果你的仓库和 DVC 缓存位于不同的文件系统或驱动器上,软链接效果更好,而硬链接则在它们位于相同分区或存储设备上时表现最佳。

硬链接和软链接的缺点是你不能在原地编辑它们,因为这会导致缓存损坏。为了保护你不进行编辑,DVC 将这些文件的权限限制为只读。

最后一种第三策略是 reflinks。它们是最好的、最有效的,但仅在最新的文件系统中可用。例如,它在我的WSL2中甚至不可用,因此 DVC 从不单独使用这种策略。

但当它可用时,reflinks 带来了其他所有策略的好处。被跟踪的文件实际上在缓存中,但它们像是副本一样链接到工作区。如果你想在原地编辑这些文件,文件系统会将原始文件从缓存中复制到工作区,直到你完成编辑。

图片来自 DVC 文档。Apache-2.0 许可证。

默认策略是copy,当你初始化 DVC 时。要更改它,你可以运行:

$ dvc config cache.type hardlink[reflink, symlink, copy]

了解更多关于缓存策略之间的差异以及如何禁用硬/软链接的不可编辑缺点,请访问这里这里

5. DVC 工作流程

数据版本控制的总体思想是将对大型文件所做的更改包含在你的 Git 提交和分支中,而不触及 Git 的大小限制。我们提到 DVC 通过创建小的 .dvc 占位符来实现这一点,这些占位符在你更新 DVC 跟踪的大型文件时会发生变化。

由我制作

所以,基本工作流程如下:

  1. 你可以通过 dvc add large_assets 开始跟踪大型资产。

  2. DVC 将资产添加到 .gitignore 中,因此 Git 不会看到它,同时创建 large_assets.dvc 文件。

  3. 你可以使用 git add large_assets.dvclarge_assets.dvc 文件添加到 Git 中。

由我制作

  1. 如果你更改了其他文件,比如 train.py,你也需要添加该更改:git add train.py,或者你可以简单地使用 git add --all 来捕捉 Git 可见的所有更改。

  2. 你使用 git commit -m "Message" 进行提交。

这样,你可以在 Git 历史中记录你的代码更改和大型资产更改。如果你使用 git checkout branch_name/commit_hash 切换分支或提交,DVC 必须显示这些特定提交或分支的资产版本。为此,你必须在 git checkout 后调用 dvc checkout

$ git checkout branch_name
$ dvc checkout

该命令将使用 .dvc 文件和其中的 MD5 哈希从缓存中获取 DVC 资产的版本。

6. DVC 远程存储

Git 使用像 GitHub 这样的托管平台在线存储你的代码仓库,以便其他人可以协作你的项目。但就像 Git 不能处理你的大型资产一样,GitHub 也不能。因此,DVC 通过拥有自己的远程存储来解决这个问题。

由我制作

这个远程存储与缓存分开,可以在与你的仓库同一台机器上,也可以在任何云服务提供商上。DVC 支持所有主要的云服务提供商,包括 AWS、GCP 和 Azure。类似于你经常将 Git 提交推送到 GitHub,你应该使用 dvc push 将 DVC 缓存中的资产发送到远程,以保持其最新。

你选择的远程存储将在你的 DVC 配置文件 .dvc/config 中声明。当某人克隆或分叉你的 GitHub 仓库时,他们将获得此配置文件以及 .dvc 占位符文件,但他们的 DVC 缓存将为空。因此,他们将使用 dvc pull 从远程下载所有大型资产及其版本到他们的缓存和工作区。

这就是协作 DVC 启用仓库的工作流程。

DVC 用户指南中的远程存储展示了设置代码。

7. DVC 流水线

DVC 最棒的功能之一是智能流水线。一个典型的机器学习项目包括许多步骤,如收集数据、清洗和转换数据、选择模型、调整选择的模型,最后部署它。每个步骤都可能消耗和生成不同的资产,如数据集、模型、指标、图表、docker 镜像等。

DVC 管道的目标是通过单一命令dvc repro(即DVC 重现)来依次运行所有这些步骤,让 DVC 在后台处理所有资产的组合。这听起来很棒,对吧?

我不会详细讲解如何设置这些管道,因为过去某位作者已经做过这件事:

[## 如何创建高度组织化的 ML 项目,任何人都可以使用 DVC 管道重现

编辑描述

pub.towardsai.net](https://pub.towardsai.net/how-to-create-highly-organized-ml-projects-anyone-can-reproduce-with-dvc-pipelines-fc3ac7867d16?source=post_page-----bb2433ccec88--------------------------------)

但我认为只要你知道当你调用dvc repro时,DVC 就会省去每次运行管道时调用dvc add的需要,这就足以引起你的兴趣。我强烈推荐阅读这篇文章。

或者你也可以阅读关于管道的优秀用户指南

结论

我要用对这个库的巨大希望来结束这篇日记。现在是 2020 年,库的版本是 1.0.0,但我预测在 2023 年某个随机的年份,它的v3.0.0将会带来许多新功能和改进。

我猜他们也会在不久的将来(可能是今年 11 月)添加一个功能来跟踪机器学习实验,但这只是痴心妄想。

何等美好的一天。

喜欢这篇文章吗?让我们面对现实,它的写作风格确实很奇特。想象一下,有几十篇类似的文章都由一个聪明、迷人、机智的作者(顺便说一下,就是我)所写。

仅需 4.99 美元的会员费,你将不仅能访问我的故事,还能获得来自 Medium 上最杰出思想者的知识宝库。如果你使用我的推荐链接,你将获得我超级新星的感谢和一个虚拟的击掌,以支持我的工作。

[## 使用我的推荐链接加入 Medium — Bex T.

获取对我所有⚡高级⚡内容的独家访问权限,在 Medium 上无限制地浏览。通过给我买一杯…

ibexorigin.medium.com](https://ibexorigin.medium.com/membership?source=post_page-----bb2433ccec88--------------------------------)

图片由我提供。通过 Midjourney 制作。

参考文献:

[1] RealPython, 使用 Python 和 DVC 的数据版本控制, 2020, realpython.com

KNIME 的数据可视化

原文:towardsdatascience.com/data-visualization-with-knime-2b86548736d7

一步一步的全面指南

Michal SzudejkoTowards Data Science Michal Szudejko

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

--

来源:作者在 ChatGPT 和 DALL-E 3 中生成的图像。

2007 年,诺贝尔和平奖授予了阿尔·戈尔和政府间气候变化专门委员会(IPCC),以表彰他们提高了对气候变化危害的认识。由戴维斯·古根 heim执导的纪录片《难以忽视的真相》在将气候变化问题置于公众意识的前沿方面发挥了重要作用。该片展示了戈尔多次向全球观众展示的幻灯片¹。

值得特别注意的是阿尔·戈尔在那个演讲中使用的图表。尽管有一些批评,这些可视化在向广大观众传达气候危机的紧迫性方面起到了关键作用。 戈尔巧妙地使用了引人入胜的视觉效果,有效提高了意识,并强调了应对气候变化的必要性。这些可视化将抽象概念转化为清晰、易于理解的信息,在讨论复杂问题时证明了其价值²。

我推荐给任何对 数据可视化 感兴趣的人, 也许更重要的是,气候变化。

那么,这篇文章讲了什么呢?

惊讶,惊讶,这讲的是可视化。 更准确地说,它涉及使用最近引起我注意的工具 KNIME 来制作可视化、报告和仪表板。如果你读过我之前的一些文章,你会知道我已经深入探索了替代可视化的世界,使用各种免费的实用工具进行创建

其中一个工具 KNIME 值得单独写一篇文章。至少在我谦逊的观点中是这样。所以,这里就有它。

对许多人来说,KNIME 可能是一个熟悉的名字。在准备这篇文章时,我遇到了大量描述这个工具能力和功能的文章。然而,我得到的印象是,大多数关于 KNIME 和可视化的文章处理的是碎片化的功能(例如制作单一图表),而缺乏全面的视角。我希望能填补这个空白。

当我开始使用 KNIME 时,我对这个工具一无所知。 然而,由于其易用性,学习它并没有特别的挑战。希望我能证明,这趟旅程绝对值得。

这篇文章是针对那些熟悉数据可视化但对 KNIME 新手的人。我希望这将是任何希望快速了解 KNIME 在可视化方面能力的人的完美起点。

什么是 KNIME?

KNIME 是一个开源的数据分析和可视化平台。它特别适合数据操作,使得排序、合并和分析数据变得简单,无需编码。它还提供了广泛的统计工具。

Knime 在 Gartner Peer Insights 上获得了 4.6 的评分,并且新评论仍在不断涌入。根据 6sense 的数据,KNIME 拥有 2038 名客户,占分析平台市场份额的大约 1%(市场排名第 12 位)。因此,它是一个知名品牌,但不是主要玩家。它能提供什么好处?

易用性

KNIME 的基于工作流的方法使其易于使用,即使对于数据技能有限的人群也是如此。这种方法通过将复杂的数据过程分解为可管理的步骤来简化复杂的数据处理过程。它是一种比纯 Python 编程更易于访问的替代方案。

机器学习和预测建模

KNIME 是一个强大的机器学习和预测建模工具。它可以高效地处理异常检测和回归等任务,还可以连接各种数据源并自动化工作流程,从而简化数据工程任务。

可视化

KNIME 的可视化功能由 JavaScript 库提供支持,提供了一个灵活的数据展示工具箱。这使得用户能够创建清晰且富有洞察力的数据可视化。我希望在稍后展示它。

可重用性

KNIME 工作流的一个主要好处是它们的可重用性。这意味着工作流可以轻松地安排并重复运行,新的数据集可以根据需要上传。这些工作流还可以集成到报告和仪表板中。

使用 KNIME 进行可视化:一步一步来

首先,我将向你展示如何制作下面的可视化。这是一个展示前 10 种产品收入的柱状图。虽然最终产品可能看起来并不特别,但必须遵循几个步骤才能实现这个结果。

顺便提一下,阿尔·戈尔使用的大多数图表都相当简单,其中许多是折线图或面积图。

作者生成的图表。

第 1 步. KNIME 安装

第一步是下载并启动 KNIME。你可以从他们的网站www.knime.com/download下载必要的文件集。有时下载时会遇到困难,例如在 Windows 11 系统上。如果你遇到问题,建议查看www.knime.com/installation-problems-on-windows上的信息。KNIME 不需要传统的安装方式。你只需将文件解压到选择的目录中并运行程序。

第 2 步. 数据准备

我为这个练习准备了一个数据集,可以从这里下载。它是一个包含了 2018 年至 2022 年间一家快消品公司收入和成本的合成数据。

一旦我们获得数据,就可以在 KNIME 中输入和处理它。

首先,我们需要创建一个新的工作空间。选择“文件”,然后选择“新建...”,最后选择“新建 KNIME 工作流”。

我们首先上传数据。用鼠标左键单击你保存的数据集文件,并将其拖放到 KNIME 的工作流区域中。“Excel 读取器”节点将自动创建。

我们需要在工作流中使用以下节点:

  1. “列筛选器”: 在这里,我们将选择第一个可视化所需的列。

  2. “日期和时间基于的行筛选器”:在这一步,我们将选择合适的时间范围(我选择了 2022 年)。

  3. “数学公式”: 我们将用这个来重新计算收入度量,以千为单位,四舍五入到一个小数位。

添加所有这些节点后,我们必须连接它们。用鼠标左键选择两个节点,然后选择“连接所选节点”。你也可以为节点分配特征名称。双击默认名称并输入你自己的名称。

数据导入和处理是我们工作流的一部分。 来源:作者提供的图片。

在“Excel 读取器”节点中,我们配置将文件导入到工作流中。在这一节点中,我没有做其他操作。

在“列筛选器”节点中,我取消选择了“产品 ID”、“类别”、“COGS”、“市场支出”、“其他费用”和“<任何未知的新列>”。

在“日期和时间基于的行筛选器”中,我选择了一个包括从 2022 年 1 月 1 日到 2022 年 12 月 31 日的日期范围。

最后,在“数学公式”节点中,我添加了以下公式:round($Revenue$/1000,1)。我想将其写入一个新列,因此选择了“附加列”选项,并将新列命名为“Revenue_k”。不过,如果你愿意,也可以覆盖现有的列。

第 3 步. 设计可视化

附注: 如果您不确定选择哪个可视化方式,可以寻求 ChatGPT(或其他大型语言模型)的帮助。只需在提示中描述数据和您的目标,您将获得有用的建议。

向 ChatGPT 请求图表建议。 来源:作者截屏。

ChatGPT 生成的图表创意。 来源:作者截屏。

在我们的情况下,我们已经知道要制作什么可视化。作为提醒,我们将创建一个显示前 10 个收入生成产品的图表。

工作流的视觉部分设计。 来源:作者截屏。

为此,我们需要向我们的工作流中添加三个额外的节点:

  1. ‘GroupBy’: 我们将根据产品名称对收入进行分组。

  2. ‘Top K Selector’: 这允许我们从给定的数据集中选择前 ‘x’ 项。

  3. ‘条形图’: 用于绘制实际的图表。

让我们稍微关注一下‘GroupBy’ 节点。要应用分组,我们首先需要指定分组列。在我们的情况下,它将是产品。

来源:作者截屏。

接下来,我们添加聚合。在我们的情况下,选择的聚合方法是‘求和’。

来源:作者截屏。

要设置条形图,请按照以下简单步骤操作:

  1. 进入‘选项’选项卡。选择‘生成图像’,并指定‘产品名称’作为‘类别列’。对于‘聚合方法’,选择‘求和’。

  2. 在‘常规选项’选项卡中,为您的图表输入标题。我建议选择‘绘制水平条形图’。

条形图配置窗口。 来源:作者截屏。

抓住机会深入探讨其他配置设置。实验这些选项可以帮助您发现一系列潜在的结果和可能性。

步骤 4. 发布结果

要完成这个过程,请配置导出设置。一种有效的方法是使用‘Image Writer (Port)’ 节点。此步骤将完成您的工作,并为展示或进一步使用做好准备。

完整工作流。 来源:作者截屏。

我将‘Image Writer’配置为将文件保存为 *.svg。另一个选项 *.png 在我的电脑上无法使用。我从 KNIME 相关论坛了解到,这个问题相当普遍。

来源:作者截屏。

最后,我们可以通过点击顶部菜单中的‘执行所有可执行节点’来运行我们的工作流。

来源:作者截屏。

如果一切顺利,我们应该会看到节点下方的绿色指示灯。如果对 KNIME 初步生成的结果不满意,你可以随时在 KNIME Hub 中探索更多选项。一个值得考虑的替代方案是KNIME JFreeChart,如下所示。

相同的图表但使用 JFreeCHarts 生成。 来源:作者生成的图像。

另外,你可以深入探索社区选项,比如这个。最后一个选项的额外好处是相较于我上面展示的基本图表,提供了更多对 y 轴的控制。

自定义范围条形图。 来源:作者提供的图像。

总结本节内容,我们使用 KNIME 创建了一个简单的可视化效果。当然,这个效果还有很大的提升空间,但要注意,过程可能会变得复杂,从而迅速提高入门门槛。

使用 KNIME 的可视化:替代可视化方案

旭日图

我们刚刚在 KNIME 中创建的第一个视觉效果并不惊艳。现在,让我们探讨是否可以创建一些更高级的东西。

在我最近的帖子中,我探讨了替代可视化的世界,重点关注除了标准条形图、折线图和饼图之外的视觉效果。让我们调查一下 KNIME 是否也能在这一领域提供有趣的选项。

## 数据驱动故事讲述的替代可视化

选择它们的原因,以及如何创建它们

towardsdatascience.com

为此,我们将再次使用之前的文件作为起点。这一次,我们将创建一个旭日图

在 KNIME 中生成的旭日图。 来源:作者提供的动画。

旭日图是一种用于显示分层数据的可视化图表,数据以同心圆环的形式呈现。图表中的每个环代表一个层级,分段显示每个组成部分的比例。这种布局有助于直观理解数据中的部分与整体关系³。

为了创建旭日图,我制定了以下工作流程。我在‘列过滤器’节点中选择了‘类别’和‘产品名称’列。

旭日图工作流程。 来源:作者提供的截图。

在此步骤中,检查列顺序是否与我们打算在图表中展示的内容一致是至关重要的。具体来说,如果“类别”列要呈现在旭日图的内环中,则必须在列列表中排在“产品名称”之上。我们可以在“Excel Reader”节点中轻松调整,如下图所示。

在 Excel 读取节点中更改列顺序。 来源:作者截屏。

互动表格

接下来我将展示的是一个互动表格。下面的动画图片显示了最终产品。

在 KNIME 中制作的互动表格。 来源:作者提供的图片。

这是创建上述表格的工作流。在此过程中,“提取日期和时间字段”节点是一个新元素。

在 KNIME 中创建表格的工作流。 来源:作者截屏。

我们需要这个新节点从“日期”列中提取年份。然后,使用“GroupBy”节点,我们可以利用该数字对值进行分组。如果没有这一步,表格中将显示每一天的数据,这会使其毫无用处。

从“日期”列中提取年份。 来源:作者截屏。

折线图

好的,这不是一种替代可视化。然而,我想评估一下 KNIME 在趋势分析中的表现。不幸的是,我必须说这很令人失望。看看下面显示的图表。我最大的抱怨是控制图表轴的难度,特别是在决定最低和最高点时。在制作这个图表时,我还没找到一个简单的方法来做到这一点。但我会继续寻找!

在 KNIME 中制作的折线图。 来源:作者生成的图片。

用于生成折线图的工作流。 来源:作者截屏。

在图表中,我展示了月平均价格。为了计算这一点,我创建了一个“数学公式”节点,并输入了以下方程式:Price = Revenue / Units Sold

为了在图表中生成多条线,我首先从“日期”列中提取了年份和月份值,然后应用了透视。在透视过程中,我将“月份(数字)”放在行中(参见“组”标签下的第 1 个截图),将“年份”放在列中(参见“透视”标签下的第 2 个截图)。

透视表,步骤 1。 来源:作者截屏。

透视表,步骤 2。 来源:作者截屏。

最后一步是为“价格”创建聚合规则。我选择了“均值”作为聚合方法。

数据透视表,第 2 步。 来源:作者打印屏幕截图。

如果您希望图表从 y 轴的零点开始缩放,请记得在“线图”节点的配置设置中勾选“始终显示原点”选项。

来源:作者打印屏幕截图。

另一种方法是使用小多重图表。在此示例中,我展示了 2022 年不同产品的平均价格。用于创建此图表的节点从 KNIME Hub 下载。

小多重可视化。 来源:作者打印屏幕截图。

使用 KNIME 进行可视化:使用报告设计器⁴

我在 KNIME 中制作的报告的第 1 页。 来源:作者打印屏幕截图。

第 1 步:报告设计器的安装

在这一点上,我将向您展示如何使用我们之前创建的可视化来设计报告。如果我们还没有添加“报告扩展”,我们必须添加。首先,从文件菜单中选择“安装 KNIME 扩展……”,然后在搜索框中输入“report”,并选择“KNIME Report Designer”。

报告设计器的安装。 来源:作者打印屏幕截图。

第 2 步:将报告节点添加到您的可视化中

我们需要添加报告节点以获取来自不同工作流的结果。

将报告节点添加到工作流中。 来源:作者打印屏幕截图。

在这个阶段需要注意的一个关键方面是使用报告节点中的自定义图像缩放选项。否则,您的可视化可能无法正确显示——它们可能会很小且分辨率低。我选择了高度和宽度设置为 400,并选择了 *.png 作为导出格式。

第 3 步:设计报告

您可以在 这里 找到我生成的示例报告。要查看它,请首先打开报告编辑器。为此,点击顶部菜单中的图标。

启动报告编辑器。 来源:作者打印屏幕截图。

制作报告的第一步是将视图设置为“横向”,以在“母版页”布局中为我的可视化留出更多垂直空间。

报告方向配置。 来源:作者打印屏幕截图。

然后,我添加并格式化了标题标签,并排列了网格以定位我的可视化。

我使用了“网格”进行整体布局。 来源:作者打印屏幕截图。

让我们添加第一个可视化——条形图。为此,拖放‘Image’图标,选择‘BAR CHART TO REPORT’数据源,然后选择‘Image’。

将图表添加到报告中。 来源:作者的打印屏幕。

你可以通过调整网格属性来控制图表的对齐。

在网格单元格中控制对齐。 来源:作者的打印屏幕。

然后,我类似地添加了 Sunburst 图表,并点击了顶部菜单中的‘View Report’按钮。我在下面的图像中展示了结果。

首次添加到报告中的图表。 来源:作者的打印屏幕。

接下来,我想在一个工作流中添加我们创建的表(‘TABLE TO REPORT’节点)。我决定在‘Cross Tab’中进行操作。为了使这样的表正常工作,我们必须首先构建一个立方体。这个过程很简单。首先,将‘Cross Tab’拖放到网格中。然后,从数据源中拖动任何维度或度量。这将启动‘Cross Tab Cube Builder’。你必须在构建器中指示组(维度)和汇总字段(度量)。你可以为它们分配新名称,以便以后更容易找到。

Cross tab 立方体构建器。 来源:作者的打印屏幕。

你可以在‘Data Explorer’中查看立方体。从那里,你可以将字段拖放到表格中。你可以将度量(‘Revenue’)显示为图表。

在交叉表中设置图表视图。 来源:作者的打印屏幕。

第 4 步。发布报告

发布报告简单易行。从‘View Report’菜单中选择一个选项即可。

报告发布选项。 来源:作者的打印屏幕。

使用 KNIME 进行可视化:构建交互式仪表板

我在 KNIME 中创建的交互式仪表板。 来源:作者生成的图像。

我从一个稍作修改的可视化教程工作流开始,其中我移除了报告节点和表格工作流。我只留下了每个节点的一个可视化。如果你希望保留之前生成的工作空间,我建议你制作一个副本。 开发仪表板的第一步是选择所有节点,然后点击‘Create component’⁵。

在 KNIME 中创建组件。 来源:作者的打印屏幕。

首先打开组件以修改你的工作流和/或设计仪表板布局。

修改仪表板布局步骤 1。 来源:作者的打印屏幕。

修改仪表板布局步骤 2。 来源:作者的打印屏幕。

我对 KNIME 的评估

我喜欢什么?

在使用 KNIME 几天后,我必须说我看到了这个应用程序的许多优点。首先,它是相对易于使用的。正如你所见,创建可视化或甚至互动仪表板非常简单。我很欣赏你可以在本地使用 KNIME 并将其作为自动化工具,例如定期更新的仪表板。安装简便、开源基础和社区支持无疑是巨大的优势

我不喜欢什么?

然而,也存在一些缺点。一些基本功能不可用或复杂,即使有社区帮助。例如,修改图表轴、扩展图表的颜色选项或实现互动切片器⁶可能会很有挑战性。此外,可视化效果可能更具视觉吸引力。尽管大多数问题是可以解决的,但时间和学习曲线可能迅速变得陡峭

摘要

在这篇文章中,我探讨了在 KNIME 中制作可视化的过程。我描述了创建有效视觉效果、制作详细报告和构建互动仪表板所需的步骤。 KNIME 的用户友好界面,以拖放功能和广泛的配置选项为特征,使其成为数据可视化的多功能且强大的工具。此外,它的开源性质和活跃的社区使我们在遇到问题时能够不断改进和获得指导。

尽管有这些优点,KNIME 在某些功能上偶尔会表现出局限性。毫无疑问,还有进一步改进的空间。

总体而言,KNIME 证明是一个有价值的数据可视化工具,提供了一种简单直观的方法来从数据中提取有意义的洞察。我打算继续探索 KNIME 的分析能力,并在未来的文章中分享我的发现。

你喜欢这篇文章吗?

考虑订阅以获取我的新故事通知,关注我,或留下 👏。

参考文献

[1]. 维基百科不便的真相

[2]. 罗伯特·科萨拉图表的诺贝尔奖

[3]. FusionCharts旭日图

[4]. KNIME 报告设计师用户指南

[5]. KNIME 组件指南

[6]. 带过滤功能的条形图

posted @ 2024-10-12 19:55  绝不原创的飞龙  阅读(354)  评论(0)    收藏  举报